diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 9087dea090bfca..ba0f3d9084908d 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21602.2", + "version": "1.0.0-prerelease.21609.1", "commands": [ "xharness" ] diff --git a/.gitattributes b/.gitattributes index b4e3bcd0745655..a25b1f94d6e5a4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -75,3 +75,4 @@ src/tests/JIT/Performance/CodeQuality/BenchmarksGame/reverse-complement/revcomp- src/tests/JIT/Performance/CodeQuality/BenchmarksGame/reverse-complement/revcomp-input25000.txt text eol=lf src/tests/JIT/Performance/CodeQuality/BenchmarksGame/k-nucleotide/knucleotide-input.txt text eol=lf src/tests/JIT/Performance/CodeQuality/BenchmarksGame/k-nucleotide/knucleotide-input-big.txt text eol=lf +src/mono/wasm/runtime/dotnet.d.ts text eol=lf diff --git a/.github/fabricbot.json b/.github/fabricbot.json new file mode 100644 index 00000000000000..836d070c0f65c4 --- /dev/null +++ b/.github/fabricbot.json @@ -0,0 +1,6966 @@ +[ + { + "taskType": "scheduledAndTrigger", + "capabilityId": "IssueRouting", + "subCapability": "@Mention", + "version": "1.0", + "config": { + "taskName": "Area-owners", + "labelsAndMentions": [ + { + "labels": [ + "area-System.Security" + ], + "mentionees": [ + "dotnet/area-system-security", + "vcsjones", + "krwq" + ] + }, + { + "labels": [ + "area-System.Diagnostics.Tracing" + ], + "mentionees": [ + "tarekgh", + "tommcdon", + "pjanotti", + "safern" + ] + }, + { + "labels": [ + "area-System.Linq.Parallel" + ], + "mentionees": [ + "dotnet/area-system-linq-parallel", + "tarekgh" + ] + }, + { + "labels": [ + "area-System.Text.Encoding" + ], + "mentionees": [ + "dotnet/area-system-text-encoding", + "tarekgh" + ] + }, + { + "labels": [ + "area-System.Text.Encodings.Web" + ], + "mentionees": [ + "dotnet/area-system-text-encodings-web", + "tarekgh" + ] + }, + { + "labels": [ + "area-System.Threading.Channels" + ], + "mentionees": [ + "dotnet/area-system-threading-channels" + ] + }, + { + "labels": [ + "area-System.Threading.Tasks" + ], + "mentionees": [ + "dotnet/area-system-threading-tasks" + ] + }, + { + "labels": [ + "area-System.Text.RegularExpressions" + ], + "mentionees": [ + "dotnet/area-system-text-regularexpressions" + ] + }, + { + "labels": [ + "area-GC-mono" + ], + "mentionees": [ + "brzvlad" + ] + }, + { + "labels": [ + "area-Codegen-Interpreter-mono" + ], + "mentionees": [ + "brzvlad" + ] + }, + { + "labels": [ + "area-Infrastructure-mono" + ], + "mentionees": [ + "directhex" + ] + }, + { + "labels": [ + "area-AssemblyLoader-mono" + ], + "mentionees": [] + }, + { + "labels": [ + "area-Debugger-mono" + ], + "mentionees": [ + "thaystg" + ] + }, + { + "labels": [ + "area-System.Collections" + ], + "mentionees": [ + "dotnet/area-system-collections" + ] + }, + { + "labels": [ + "area-System.Linq" + ], + "mentionees": [ + "dotnet/area-system-linq" + ] + }, + { + "labels": [ + "area-System.Numerics.Tensors" + ], + "mentionees": [ + "dotnet/area-system-numerics-tensors" + ] + }, + { + "labels": [ + "area-Host" + ], + "mentionees": [ + "vitek-karas", + "agocke", + "vsadov" + ] + }, + { + "labels": [ + "area-HostModel" + ], + "mentionees": [ + "vitek-karas", + "agocke" + ] + }, + { + "labels": [ + "area-Single-File" + ], + "mentionees": [ + "agocke", + "vitek-karas", + "vsadov" + ] + }, + { + "labels": [ + "area-System.Buffers" + ], + "mentionees": [ + "dotnet/area-system-buffers", + "GrabYourPitchforks" + ] + }, + { + "labels": [ + "area-System.Numerics" + ], + "mentionees": [ + "dotnet/area-system-numerics" + ] + }, + { + "labels": [ + "area-System.Runtime.Intrinsics" + ], + "mentionees": [ + "dotnet/area-system-runtime-intrinsics" + ] + }, + { + "labels": [ + "area-System.CodeDom" + ], + "mentionees": [ + "dotnet/area-system-codedom" + ] + }, + { + "labels": [ + "area-System.Xml" + ], + "mentionees": [ + "dotnet/area-system-xml" + ] + }, + { + "labels": [ + "area-AssemblyLoader-coreclr" + ], + "mentionees": [ + "vitek-karas", + "agocke", + "vsadov" + ] + }, + { + "labels": [ + "area-System.Dynamic.Runtime" + ], + "mentionees": [ + "cston" + ] + }, + { + "labels": [ + "area-System.Linq.Expressions" + ], + "mentionees": [ + "cston" + ] + }, + { + "labels": [ + "area-Microsoft.CSharp" + ], + "mentionees": [ + "cston" + ] + }, + { + "labels": [ + "area-Microsoft.VisualBasic" + ], + "mentionees": [ + "cston" + ] + }, + { + "labels": [ + "area-Infrastructure-libraries" + ], + "mentionees": [ + "dotnet/area-infrastructure-libraries" + ] + }, + { + "labels": [ + "area-Infrastructure" + ], + "mentionees": [ + "dotnet/runtime-infrastructure" + ] + }, + { + "labels": [ + "area-GC-coreclr" + ], + "mentionees": [ + "dotnet/gc" + ] + }, + { + "labels": [ + "area-System.Net" + ], + "mentionees": [ + "dotnet/ncl" + ] + }, + { + "labels": [ + "area-System.Net.Http" + ], + "mentionees": [ + "dotnet/ncl" + ] + }, + { + "labels": [ + "area-System.Net.Security" + ], + "mentionees": [ + "dotnet/ncl", + "vcsjones" + ] + }, + { + "labels": [ + "area-System.Net.Sockets" + ], + "mentionees": [ + "dotnet/ncl" + ] + }, + { + "labels": [ + "area-Diagnostics-coreclr" + ], + "mentionees": [ + "tommcdon" + ] + }, + { + "labels": [ + "area-System.Diagnostics" + ], + "mentionees": [ + "tommcdon" + ] + }, + { + "labels": [ + "area-System.Data" + ], + "mentionees": [ + "roji", + "ajcvickers" + ] + }, + { + "labels": [ + "area-System.Data.OleDB" + ], + "mentionees": [ + "roji", + "ajcvickers" + ] + }, + { + "labels": [ + "area-System.Data.Odbc" + ], + "mentionees": [ + "roji", + "ajcvickers" + ] + }, + { + "labels": [ + "area-System.Data.SqlClient" + ], + "mentionees": [ + "cheenamalhotra", + "david-engel" + ] + }, + { + "labels": [ + "area-System.ComponentModel.DataAnnotations" + ], + "mentionees": [ + "ajcvickers", + "bricelam", + "roji" + ] + }, + { + "labels": [ + "area-Extensions-FileSystem" + ], + "mentionees": [ + "dotnet/area-extensions-filesystem", + "maryamariyan" + ] + }, + { + "labels": [ + "area-Extensions-HttpClientFactory" + ], + "mentionees": [ + "dotnet/ncl" + ] + }, + { + "labels": [ + "area-System.Net.Quic" + ], + "mentionees": [ + "dotnet/ncl" + ] + }, + { + "labels": [ + "area-System.Formats.Cbor" + ], + "mentionees": [ + "dotnet/area-system-formats-cbor" + ] + }, + { + "labels": [ + "area-System.Formats.Asn1" + ], + "mentionees": [ + "dotnet/area-system-formats-asn1" + ] + }, + { + "labels": [ + "area-Codegen-JIT-Mono" + ], + "mentionees": [ + "SamMonoRT", + "vargaz" + ] + }, + { + "labels": [ + "area-CodeGen-LLVM-Mono" + ], + "mentionees": [ + "SamMonoRT", + "vargaz", + "imhameed" + ] + }, + { + "labels": [ + "area-CodeGen-meta-Mono" + ], + "mentionees": [ + "SamMonoRT", + "vargaz", + "lambdageek" + ] + }, + { + "labels": [ + "area-System.Text.Json" + ], + "mentionees": [ + "dotnet/area-system-text-json" + ] + }, + { + "labels": [ + "area-System.Memory" + ], + "mentionees": [ + "dotnet/area-system-memory" + ] + }, + { + "labels": [ + "area-Infrastructure-coreclr" + ], + "mentionees": [ + "hoyosjs" + ] + }, + { + "labels": [ + "area-System.IO" + ], + "mentionees": [ + "dotnet/area-system-io" + ] + }, + { + "labels": [ + "area-System.IO.Compression" + ], + "mentionees": [ + "dotnet/area-system-io-compression" + ] + }, + { + "labels": [ + "area-System.Diagnostics.Process" + ], + "mentionees": [ + "dotnet/area-system-diagnostics-process" + ] + }, + { + "labels": [ + "area-System.Console" + ], + "mentionees": [ + "dotnet/area-system-console" + ] + }, + { + "labels": [ + "area-System.Runtime" + ], + "mentionees": [ + "dotnet/area-system-runtime" + ] + }, + { + "labels": [ + "area-System.Threading" + ], + "mentionees": [ + "mangod9" + ] + }, + { + "labels": [ + "area-vm-coreclr" + ], + "mentionees": [ + "mangod9" + ] + }, + { + "labels": [ + "area-CodeGen-coreclr" + ], + "mentionees": [ + "JulieLeeMSFT" + ] + }, + { + "labels": [ + "area-ILTools-coreclr" + ], + "mentionees": [ + "JulieLeeMSFT" + ] + }, + { + "labels": [ + "area-ILVerification" + ], + "mentionees": [ + "JulieLeeMSFT" + ] + }, + { + "labels": [ + "area-System.DirectoryServices" + ], + "mentionees": [ + "dotnet/area-system-directoryservices", + "jay98014" + ] + }, + { + "labels": [ + "area-System.Speech" + ], + "mentionees": [ + "danmoseley" + ] + }, + { + "labels": [ + "area-Meta" + ], + "mentionees": [ + "dotnet/area-meta" + ] + }, + { + "labels": [ + "area-DependencyModel" + ], + "mentionees": [ + "dotnet/area-dependencymodel" + ] + }, + { + "labels": [ + "area-Extensions-Caching" + ], + "mentionees": [ + "dotnet/area-extensions-caching" + ] + }, + { + "labels": [ + "area-Extensions-Configuration" + ], + "mentionees": [ + "dotnet/area-extensions-configuration" + ] + }, + { + "labels": [ + "area-Extensions-DependencyInjection" + ], + "mentionees": [ + "dotnet/area-extensions-dependencyinjection" + ] + }, + { + "labels": [ + "area-Extensions-Hosting" + ], + "mentionees": [ + "dotnet/area-extensions-hosting" + ] + }, + { + "labels": [ + "area-Extensions-Logging" + ], + "mentionees": [ + "dotnet/area-extensions-logging" + ] + }, + { + "labels": [ + "area-Extensions-Options" + ], + "mentionees": [ + "dotnet/area-extensions-options" + ] + }, + { + "labels": [ + "area-Extensions-Primitives" + ], + "mentionees": [ + "dotnet/area-extensions-primitives" + ] + }, + { + "labels": [ + "area-Microsoft.Extensions" + ], + "mentionees": [ + "dotnet/area-microsoft-extensions" + ] + }, + { + "labels": [ + "area-System.ComponentModel" + ], + "mentionees": [ + "dotnet/area-system-componentmodel" + ] + }, + { + "labels": [ + "area-System.ComponentModel.Composition" + ], + "mentionees": [ + "dotnet/area-system-componentmodel-composition" + ] + }, + { + "labels": [ + "area-System.Composition" + ], + "mentionees": [ + "dotnet/area-system-composition" + ] + }, + { + "labels": [ + "area-System.Diagnostics.Activity" + ], + "mentionees": [ + "dotnet/area-system-diagnostics-activity" + ] + }, + { + "labels": [ + "area-System.Globalization" + ], + "mentionees": [ + "dotnet/area-system-globalization" + ] + }, + { + "labels": [ + "area-Microsoft.Win32" + ], + "mentionees": [ + "dotnet/area-microsoft-win32" + ] + }, + { + "labels": [ + "area-System.Diagnostics.EventLog" + ], + "mentionees": [ + "dotnet/area-system-diagnostics-eventlog" + ] + }, + { + "labels": [ + "area-System.Diagnostics.PerformanceCounter" + ], + "mentionees": [ + "dotnet/area-system-diagnostics-performancecounter" + ] + }, + { + "labels": [ + "area-System.Diagnostics.TraceSource" + ], + "mentionees": [ + "dotnet/area-system-diagnostics-tracesource" + ] + }, + { + "labels": [ + "area-System.Drawing" + ], + "mentionees": [ + "dotnet/area-system-drawing" + ] + }, + { + "labels": [ + "area-System.Management" + ], + "mentionees": [ + "dotnet/area-system-management" + ] + }, + { + "labels": [ + "area-System.ServiceProcess" + ], + "mentionees": [ + "dotnet/area-system-serviceprocess" + ] + }, + { + "labels": [ + "area-System.Configuration" + ], + "mentionees": [ + "dotnet/area-system-configuration" + ] + }, + { + "labels": [ + "area-System.Reflection" + ], + "mentionees": [ + "dotnet/area-system-reflection" + ] + }, + { + "labels": [ + "area-System.Reflection.Emit" + ], + "mentionees": [ + "dotnet/area-system-reflection-emit" + ] + }, + { + "labels": [ + "area-System.Reflection.Metadata" + ], + "mentionees": [ + "dotnet/area-system-reflection-metadata" + ] + }, + { + "labels": [ + "area-System.Resources" + ], + "mentionees": [ + "dotnet/area-system-resources", + "tarekgh" + ] + }, + { + "labels": [ + "area-System.Runtime.CompilerServices" + ], + "mentionees": [ + "dotnet/area-system-runtime-compilerservices" + ] + } + ], + "replyTemplate": "Tagging subscribers to this area: ${mentionees}\nSee info in [area-owners.md](https://github.com/dotnet/runtime/blob/main/docs/area-owners.md) if you want to be subscribed.", + "enableForPullRequests": true + }, + "disabled": false + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "breaking-change" + } + }, + { + "operator": "and", + "operands": [ + { + "name": "", + "parameters": {} + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "actions": [ + { + "name": "addLabel", + "parameters": { + "label": "needs-breaking-change-doc-created" + } + }, + { + "name": "addReply", + "parameters": { + "comment": "Added `needs-breaking-change-doc-created` label because this issue has the `breaking-change` label. \n\n1. [ ] Create and link to this issue a matching issue in the dotnet/docs repo using the [breaking change documentation template](https://github.com/dotnet/docs/issues/new?assignees=gewarren&labels=breaking-change%2CPri1%2Cdoc-idea&template=breaking-change.yml&title=%5BBreaking+change%5D%3A+), then remove this `needs-breaking-change-doc-created` label.\n\nTagging @dotnet/compat for awareness of the breaking change." + } + } + ], + "taskName": "Add breaking change doc label to issue" + }, + "disabled": false + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "breaking-change" + } + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "actions": [ + { + "name": "addLabel", + "parameters": { + "label": "needs-breaking-change-doc-created" + } + }, + { + "name": "addReply", + "parameters": { + "comment": "Added `needs-breaking-change-doc-created` label because this PR has the `breaking-change` label. \n\nWhen you commit this breaking change:\n\n1. [ ] Create and link to this PR and the issue a matching issue in the dotnet/docs repo using the [breaking change documentation template](https://github.com/dotnet/docs/issues/new?assignees=gewarren&labels=breaking-change%2CPri1%2Cdoc-idea&template=breaking-change.yml&title=%5BBreaking+change%5D%3A+), then remove this `needs-breaking-change-doc-created` label.\n2. [ ] Ask a committer to mail the `.NET Breaking Change Notification` DL.\n\nTagging @dotnet/compat for awareness of the breaking change." + } + } + ], + "taskName": "Add breaking change doc label to PR" + }, + "disabled": false + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "frequency": [ + { + "weekDay": 0, + "hours": [ + 1, + 7, + 13, + 19 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 1, + "hours": [ + 1, + 7, + 13, + 19 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 2, + "hours": [ + 1, + 7, + 13, + 19 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 3, + "hours": [ + 1, + 7, + 13, + 19 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 4, + "hours": [ + 1, + 7, + 13, + 19 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 5, + "hours": [ + 1, + 7, + 13, + 19 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 6, + "hours": [ + 1, + 7, + 13, + 19 + ], + "timezoneOffset": 0 + } + ], + "searchTerms": [ + { + "name": "isClosed", + "parameters": {} + }, + { + "name": "noActivitySince", + "parameters": { + "days": 30 + } + }, + { + "name": "isUnlocked", + "parameters": {} + } + ], + "actions": [ + { + "name": "lockIssue", + "parameters": { + "reason": "resolved", + "label": "will_lock_this" + } + } + ], + "taskName": "Lock stale issues and PR's" + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssueCommentResponder", + "version": "1.0", + "config": { + "taskName": "Replace `needs more info` label with `needs further triage` label when the author comments on an issue", + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "created" + } + }, + { + "name": "isActivitySender", + "parameters": { + "user": { + "type": "author" + } + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "needs more info" + } + }, + { + "name": "isOpen", + "parameters": {} + } + ] + }, + "actions": [ + { + "name": "addLabel", + "parameters": { + "label": "needs further triage" + } + }, + { + "name": "removeLabel", + "parameters": { + "label": "needs more info" + } + } + ], + "eventType": "issue", + "eventNames": [ + "issue_comment" + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "taskName": "Remove `no recent activity` label from issues when issue is modified", + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "closed" + } + } + ] + }, + { + "name": "hasLabel", + "parameters": { + "label": "no recent activity" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "no recent activity" + } + } + ] + } + ] + }, + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "no recent activity" + } + } + ], + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssueCommentResponder", + "version": "1.0", + "config": { + "taskName": "Remove `no recent activity` label when an issue is commented on", + "conditions": { + "operator": "and", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "no recent activity" + } + } + ] + }, + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "no recent activity" + } + } + ], + "eventType": "issue", + "eventNames": [ + "issue_comment" + ] + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "taskName": "Close issues with no recent activity", + "frequency": [ + { + "weekDay": 0, + "hours": [ + 0, + 6, + 12, + 18 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 1, + "hours": [ + 0, + 6, + 12, + 18 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 2, + "hours": [ + 0, + 6, + 12, + 18 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 3, + "hours": [ + 0, + 6, + 12, + 18 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 4, + "hours": [ + 0, + 6, + 12, + 18 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 5, + "hours": [ + 0, + 6, + 12, + 18 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 6, + "hours": [ + 0, + 6, + 12, + 18 + ], + "timezoneOffset": 0 + } + ], + "searchTerms": [ + { + "name": "isIssue", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "hasLabel", + "parameters": { + "label": "no recent activity" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 14 + } + } + ], + "actions": [ + { + "name": "addReply", + "parameters": { + "comment": "This issue will now be closed since it had been marked `no recent activity` but received no further activity in the past 14 days. It is still possible to reopen or comment on the issue, but please note that the issue will be locked if it remains inactive for another 30 days." + } + }, + { + "name": "closeIssue", + "parameters": {} + } + ] + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "taskName": "Add no recent activity label to issues", + "frequency": [ + { + "weekDay": 0, + "hours": [ + 4, + 10, + 16, + 22 + ], + "timezoneOffset": 1 + }, + { + "weekDay": 1, + "hours": [ + 4, + 10, + 16, + 22 + ], + "timezoneOffset": 1 + }, + { + "weekDay": 2, + "hours": [ + 4, + 10, + 16, + 22 + ], + "timezoneOffset": 1 + }, + { + "weekDay": 3, + "hours": [ + 4, + 10, + 16, + 22 + ], + "timezoneOffset": 1 + }, + { + "weekDay": 4, + "hours": [ + 4, + 10, + 16, + 22 + ], + "timezoneOffset": 1 + }, + { + "weekDay": 5, + "hours": [ + 4, + 10, + 16, + 22 + ], + "timezoneOffset": 1 + }, + { + "weekDay": 6, + "hours": [ + 4, + 10, + 16, + 22 + ], + "timezoneOffset": 1 + } + ], + "searchTerms": [ + { + "name": "isIssue", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "hasLabel", + "parameters": { + "label": "needs more info" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 14 + } + }, + { + "name": "noLabel", + "parameters": { + "label": "no recent activity" + } + } + ], + "actions": [ + { + "name": "addLabel", + "parameters": { + "label": "no recent activity" + } + }, + { + "name": "addReply", + "parameters": { + "comment": "This issue has been automatically marked `no recent activity` because it has not had any activity for 14 days. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will remove `no recent activity`." + } + } + ] + }, + "disabled": false + }, + { + "taskType": "trigger", + "capabilityId": "InPrLabel", + "subCapability": "InPrLabel", + "version": "1.0", + "config": { + "taskName": "Add 'In-PR' label on issue when an open pull request is targeting it", + "inPrLabelText": "Status: In PR", + "fixedLabelText": "Status: Fixed", + "fixedLabelEnabled": false, + "label_inPr": "in pr" + } + }, + { + "taskType": "scheduledAndTrigger", + "capabilityId": "IssueRouting", + "subCapability": "@Mention", + "version": "1.0", + "config": { + "taskName": "@Mention for linkable-framework", + "labelsAndMentions": [ + { + "labels": [ + "linkable-framework" + ], + "mentionees": [ + "eerhardt", + "vitek-karas", + "LakshanF", + "sbomer", + "joperezr" + ] + } + ], + "replyTemplate": "Tagging subscribers to 'linkable-framework': ${mentionees}\nSee info in area-owners.md if you want to be subscribed.", + "enableForPullRequests": true + } + }, + { + "taskType": "scheduledAndTrigger", + "capabilityId": "IssueRouting", + "subCapability": "@Mention", + "version": "1.0", + "config": { + "taskName": "@Mention for size-reduction", + "replyTemplate": "Tagging subscribers to 'size-reduction': ${mentionees}\nSee info in area-owners.md if you want to be subscribed.", + "labelsAndMentions": [ + { + "labels": [ + "size-reduction" + ], + "mentionees": [ + "eerhardt", + "SamMonoRT", + "marek-safar" + ] + } + ], + "enableForPullRequests": true + } + }, + { + "taskType": "scheduledAndTrigger", + "capabilityId": "IssueRouting", + "subCapability": "@Mention", + "version": "1.0", + "config": { + "taskName": "@Mention for wasm", + "labelsAndMentions": [ + { + "labels": [ + "arch-wasm" + ], + "mentionees": [ + "lewing" + ] + } + ], + "replyTemplate": "Tagging subscribers to 'arch-wasm': ${mentionees}\nSee info in area-owners.md if you want to be subscribed.", + "enableForPullRequests": true + } + }, + { + "taskType": "scheduledAndTrigger", + "capabilityId": "IssueRouting", + "subCapability": "@Mention", + "version": "1.0", + "config": { + "taskName": "@Mention for ios", + "labelsAndMentions": [ + { + "labels": [ + "os-ios" + ], + "mentionees": [ + "steveisok", + "akoeplinger" + ] + } + ], + "enableForPullRequests": true, + "replyTemplate": "Tagging subscribers to 'os-ios': ${mentionees}\nSee info in area-owners.md if you want to be subscribed." + } + }, + { + "taskType": "scheduledAndTrigger", + "capabilityId": "IssueRouting", + "subCapability": "@Mention", + "version": "1.0", + "config": { + "taskName": "@Mention for android", + "labelsAndMentions": [ + { + "labels": [ + "os-android" + ], + "mentionees": [ + "steveisok", + "akoeplinger" + ] + } + ], + "enableForPullRequests": true, + "replyTemplate": "Tagging subscribers to 'arch-android': ${mentionees}\nSee info in area-owners.md if you want to be subscribed." + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "frequency": [ + { + "weekDay": 0, + "hours": [ + 5, + 11, + 17, + 23 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 1, + "hours": [ + 5, + 11, + 17, + 23 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 2, + "hours": [ + 5, + 11, + 17, + 23 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 3, + "hours": [ + 5, + 11, + 17, + 23 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 4, + "hours": [ + 5, + 11, + 17, + 23 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 5, + "hours": [ + 5, + 11, + 17, + 23 + ], + "timezoneOffset": 0 + }, + { + "weekDay": 6, + "hours": [ + 5, + 11, + 17, + 23 + ], + "timezoneOffset": 0 + } + ], + "searchTerms": [ + { + "name": "isDraftPr", + "parameters": { + "value": "true" + } + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "noActivitySince", + "parameters": { + "days": 30 + } + } + ], + "taskName": "Close inactive Draft PRs", + "actions": [ + { + "name": "closeIssue", + "parameters": {} + }, + { + "name": "addReply", + "parameters": { + "comment": "Draft Pull Request was automatically closed for inactivity. Please [let us know](https://github.com/dotnet/runtime/blob/main/docs/area-owners.md) if you'd like to reopen it." + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "label": "area-System.DirectoryServices", + "projectName": "Triage POD for Meta, Reflection, etc" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "area-Meta" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.CodeDom" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.ComponentModel.Composition" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Composition" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Configuration" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Reflection" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Reflection.Emit" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Reflection.Metadata" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Resources" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Runtime.CompilerServices" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.DirectoryServices" + } + } + ] + }, + { + "name": "isOpen", + "parameters": {} + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[joperezr Pod Triage] Move issue to our pod project when a label from our area is added.", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Triage POD for Meta, Reflection, etc", + "columnName": "Needs triage" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD." + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "area-DependencyModel" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-Caching" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-Configuration" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-DependencyInjection" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-Hosting" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-Logging" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-Options" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-Primitives" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.ComponentModel" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Drawing" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Globalization" + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[safern/maryamariyan Pod triage] Add issues to POD project", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD.", + "columnName": "Untriaged" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD.", + "columnName": "Untriaged" + } + }, + { + "operator": "or", + "operands": [ + { + "operator": "and", + "operands": [ + { + "name": "addedToMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "untriaged" + } + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "needs further triage" + } + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[safern/maryamariyan Pod triage] Move issues to 6.0.0 column", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD.", + "columnName": "6.0.0" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD." + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD.", + "columnName": "Active PRs" + } + } + ] + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "area-DependencyModel" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-Caching" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-Configuration" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-DependencyInjection" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-Hosting" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-Logging" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-Options" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Extensions-Primitives" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Drawing" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.ComponentModel" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Globalization" + } + } + ] + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "[safern/maryamariyan Pod triage] Add PRs to project board", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD.", + "columnName": "Active PRs" + } + }, + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD.", + "columnName": "Active PRs" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD." + } + }, + { + "operator": "or", + "operands": [ + { + "operator": "and", + "operands": [ + { + "name": "addedToMilestone", + "parameters": { + "milestoneName": "Future" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "untriaged" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "Future" + } + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "needs further triage" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "Future" + } + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[safern/maryamariyan Pod triage] Move issues to future milestone column", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD.", + "columnName": "Future" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD." + } + }, + { + "operator": "not", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD.", + "columnName": "Untriaged" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "untriaged" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "needs further triage" + } + }, + { + "name": "removedFromMilestone", + "parameters": { + "milestoneName": "" + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[safern/maryamariyan Pod triage] Move issues to untriaged column", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD.", + "columnName": "Untriaged" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD." + } + }, + { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "area-DependencyModel" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Extensions-Caching" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Extensions-Configuration" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Extensions-DependencyInjection" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Extensions-Hosting" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Extensions-Logging" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Extensions-Options" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Extensions-Primitives" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-System.ComponentModel" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-System.Drawing" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-System.Globalization" + } + } + ] + }, + { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-DependencyModel" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Extensions-Caching" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Extensions-Configuration" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Extensions-DependencyInjection" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Extensions-Hosting" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Extensions-Logging" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Extensions-Options" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Extensions-Primitives" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.ComponentModel" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Drawing" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Globalization" + } + } + ] + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[safern/maryamariyan Pod triage] Remove from project", + "actions": [ + { + "name": "removeFromProject", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD." + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssueCommentResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Triage POD for Meta, Reflection, etc", + "columnName": "Future" + } + }, + { + "name": "isOpen", + "parameters": {} + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issue_comment" + ], + "taskName": "[joperezr Pod Triage] Move issues from Future column to Needs triage when commented on.", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "Triage POD for Meta, Reflection, etc", + "columnName": "Needs triage" + } + }, + { + "name": "addLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Triage POD for Meta, Reflection, etc", + "columnName": "Future" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "closed" + } + } + ] + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "operator": "not", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "labeled" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "unlabeled" + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[joperezr Pod Triage] Move issues from Future column to Needs triage when issue is changed.", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "Triage POD for Meta, Reflection, etc", + "columnName": "Needs triage" + } + }, + { + "name": "addLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Infrastructure Backlog" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "area-Infrastructure" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Infrastructure-coreclr" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Infrastructure-installer" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Infrastructure-libraries" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Infrastructure-mono" + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Viktor/safern] Add issues to infra POD project", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Infrastructure Backlog", + "columnName": "Untriaged" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Infrastructure Backlog" + } + }, + { + "operator": "or", + "operands": [ + { + "operator": "and", + "operands": [ + { + "name": "addedToMilestone", + "parameters": { + "milestoneName": "Future" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "untriaged" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "Future" + } + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "needs further triage" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "Future" + } + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Viktor/safern] Add infrastructure issues to future column in project board", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "Infrastructure Backlog", + "columnName": "Future" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Infrastructure Backlog" + } + }, + { + "operator": "or", + "operands": [ + { + "operator": "and", + "operands": [ + { + "name": "addedToMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "untriaged" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "needs further triage" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Viktor/safern] Add to 6.0.0 column in infrastructure project board", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "Infrastructure Backlog", + "columnName": "6.0.0" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Infrastructure Backlog" + } + }, + { + "operator": "or", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "untriaged" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "needs further triage" + } + }, + { + "name": "addedToMilestone", + "parameters": {} + } + ] + }, + { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "Future" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + } + ] + }, + { + "name": "isInMilestone", + "parameters": {} + }, + { + "operator": "not", + "operands": [ + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "7.0.0" + } + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Viktor/safern] Move to servicing column in infrastructure project", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "Infrastructure Backlog", + "columnName": "Servicing" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Infrastructure Backlog" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "area-Infrastructure" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Infrastructure-coreclr" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Infrastructure-installer" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Infrastructure-libraries" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-Infrastructure-mono" + } + } + ] + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "[Viktor/safern] Add infrastructure PRs to Active project column", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Infrastructure Backlog", + "columnName": "In Progress" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Infrastructure Backlog" + } + }, + { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "area-Infrastructure" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Infrastructure-coreclr" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Infrastructure-installer" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Infrastructure-libraries" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Infrastructure-mono" + } + } + ] + }, + { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Infrastructure" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Infrastructure-coreclr" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Infrastructure-installer" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Infrastructure-libraries" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Infrastructure-mono" + } + } + ] + } + ] + } + ] + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "[Viktor/safern] Remove PRs from Infrastructure project", + "actions": [ + { + "name": "removeFromProject", + "parameters": { + "projectName": "Infrastructure Backlog" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Infrastructure Backlog" + } + }, + { + "operator": "or", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "area-Infrastructure" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Infrastructure-coreclr" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Infrastructure-installer" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Infrastructure-libraries" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-Infrastructure-mono" + } + } + ] + }, + { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Infrastructure" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Infrastructure-coreclr" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Infrastructure-installer" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Infrastructure-libraries" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Infrastructure-mono" + } + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Viktor/safern] Remove issues from infrastructure project board", + "actions": [ + { + "name": "removeFromProject", + "parameters": { + "projectName": "Infrastructure Backlog" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Infrastructure Backlog" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Infrastructure Backlog", + "columnName": "Untriaged" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "untriaged" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "needs further triage" + } + }, + { + "name": "removedFromMilestone", + "parameters": {} + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Viktor/safern] Move issues to Untriaged column in infrastructure project", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "Infrastructure Backlog", + "columnName": "Untriaged" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "area-Microsoft.Win32" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Diagnostics.EventLog" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Diagnostics.PerformanceCounter" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Diagnostics.Tracing" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Management" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.ServiceProcess" + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Anirudh\\Viktor Pod Triage] Add untriaged issues to the pod", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI", + "columnName": "Untriaged" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI" + } + }, + { + "operator": "or", + "operands": [ + { + "operator": "and", + "operands": [ + { + "name": "addedToMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + ] + }, + { + "operator": "and", + "operands": [] + }, + { + "name": "labelRemoved", + "parameters": { + "label": "untriaged" + } + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "needs further triage" + } + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Anirudh/Viktor Pod triage] Move issues to 6.0.0 milestone column", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI", + "columnName": "6.0.0" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "columnName": "Active Prs", + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "area-Microsoft.Win32" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Diagnostics.EventLog" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Diagnostics.PerformanceCounter" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Diagnostics.Tracing" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Management" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.ServiceProcess" + } + } + ] + } + ] + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "[Anirudh/Viktor Pod triage] Add PRs to project board", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI", + "columnName": "Active PRs" + } + }, + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI", + "columnName": "Active PRs" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI", + "columnName": "Untriaged" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "needs further triage" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "untriaged" + } + }, + { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "removedFromMilestone", + "parameters": { + "milestoneName": "Future" + } + }, + { + "name": "removedFromMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "Future" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + } + ] + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Anirudh/Viktor Pod triage] Move issues to untriaged column", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI", + "columnName": "Untriaged" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI" + } + }, + { + "operator": "or", + "operands": [ + { + "operator": "and", + "operands": [ + { + "name": "addedToMilestone", + "parameters": { + "milestoneName": "Future" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "untriaged" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "Future" + } + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "needs further triage" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "Future" + } + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Anirudh/Viktor Pod triage] Move issues to future milestone column", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI", + "columnName": "Future" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI" + } + }, + { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-System.Diagnostics.EventLog" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-System.Diagnostics.PerformanceCounter" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-System.Diagnostics.Tracing" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-System.Management" + } + }, + { + "name": "labelRemoved", + "parameters": { + "label": "area-System.ServiceProcess" + } + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Microsoft.Win32" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Diagnostics.EventLog" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Diagnostics.PerformanceCounter" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Diagnostics.Tracing" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Management" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.ServiceProcess" + } + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Anirudh/ViktorPod triage] Remove from project", + "actions": [ + { + "name": "removeFromProject", + "parameters": { + "projectName": "Issue Triage Pod for EventLog, PerformanceCounter , Tracing, ServiceProcess and WMI" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Triage POD for Meta, Reflection, etc" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "area-Meta" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.CodeDom" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.ComponentModel.Composition" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Composition" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Configuration" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Reflection" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Reflection.Emit" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Reflection.Metadata" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Resources" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Runtime.CompilerServices" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.DirectoryServices" + } + } + ] + }, + { + "name": "isOpen", + "parameters": {} + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "[joperezr Pod Triage] Move PRs to our pod project when a label from our area is added", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Triage POD for Meta, Reflection, etc", + "columnName": "Needs triage" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "prMatchesPattern", + "parameters": { + "matchRegex": ".*ILLink.*" + } + }, + { + "name": "prMatchesPattern", + "parameters": { + "matchRegex": ".*illink.*" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "linkable-framework" + } + } + ] + }, + { + "name": "isOpen", + "parameters": {} + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "[Linkable-framework workgroup] Add linkable-framework label to new Prs that touch files with *ILLink* that not have it already", + "actions": [ + { + "name": "addLabel", + "parameters": { + "label": "linkable-framework" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "prMatchesPattern", + "parameters": { + "matchRegex": ".*ILLink.*" + } + }, + { + "name": "prMatchesPattern", + "parameters": { + "matchRegex": ".*illink.*" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "linkable-framework" + } + } + ] + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "isAction", + "parameters": { + "action": "synchronize" + } + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "[Linkable-framework workgroup] Add linkable-framework label to Prs that get changes pushed where they touch *ILLInk* files", + "actions": [ + { + "name": "addLabel", + "parameters": { + "label": "linkable-framework" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": "dotnet-maestro[bot]" + } + }, + { + "name": "isAction", + "parameters": { + "action": "opened" + } + }, + { + "name": "titleContains", + "parameters": { + "titlePattern": "dotnet-optimization" + } + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "Auto-approve maestro PRs", + "actions": [ + { + "name": "approvePullRequest", + "parameters": { + "comment": "Auto-approve dotnet-optimization PR" + } + } + ] + }, + "disabled": true + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "opened" + } + }, + { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "activitySenderHasPermissions", + "parameters": { + "association": "OWNER", + "permissions": "admin" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "activitySenderHasPermissions", + "parameters": { + "association": "MEMBER", + "permissions": "write" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": "github-actions[bot]" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": "dotnet-maestro[bot]" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": "dotnet-maestro-bot[bot]" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": "dotnet-maestro-bot" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": "dotnet-maestro" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": "github-actions" + } + } + ] + } + ] + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "Label community PRs", + "actions": [ + { + "name": "addLabel", + "parameters": { + "label": "community-contribution" + } + } + ] + }, + "disabled": false + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": ".NET Core Diagnostics ", + "isOrgProject": true + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Diagnostics" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-Diagnostics-coreclr" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-Tracing-coreclr" + } + } + ] + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "6.0.0" + } + }, + { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "User Story" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "enhancement" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "feature request" + } + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[hoyosjs/tommcdon] Add diagnostics 6.0 issues to project", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": ".NET Core Diagnostics", + "columnName": "6.0.0", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD." + } + }, + { + "operator": "or", + "operands": [ + { + "operator": "and", + "operands": [ + { + "name": "addedToMilestone", + "parameters": { + "milestoneName": "7.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "untriaged" + } + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "7.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "untriaged" + } + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "7.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[safern/maryamariyan Pod Triage] Move issues to 7.0.0 column", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "ML, Extensions, Globalization, etc, POD.", + "columnName": "7.0.0" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Infrastructure Backlog" + } + }, + { + "operator": "or", + "operands": [ + { + "operator": "and", + "operands": [ + { + "name": "addedToMilestone", + "parameters": { + "milestoneName": "7.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "untriaged" + } + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "7.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "needs further triage" + } + } + ] + } + ] + }, + { + "operator": "and", + "operands": [ + { + "name": "labelRemoved", + "parameters": { + "label": "needs further triage" + } + }, + { + "name": "isInMilestone", + "parameters": { + "milestoneName": "7.0.0" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "untriaged" + } + } + ] + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Viktor/safern] Add infrastructure issue to 7.0.0 column", + "actions": [ + { + "name": "moveToProjectColumn", + "parameters": { + "projectName": "Infrastructure Backlog", + "columnName": "7.0.0" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "no recent activity" + } + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "Issue cleanup notification", + "actions": [ + { + "name": "addReply", + "parameters": { + "comment": "Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.\n\nThis process is part of the experimental [issue cleanup initiative](https://github.com/dotnet/runtime/issues/60288) we are currently trialing in a limited number of areas. Please share any feedback you might have in the linked issue." + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "needs more info" + } + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "Needs more info notification", + "actions": [ + { + "name": "addReply", + "parameters": { + "comment": "This issue has been marked `needs more info` since it may be missing important information. Please refer to our [contribution guidelines](https://github.com/dotnet/runtime/blob/main/CONTRIBUTING.md#writing-a-good-bug-report) for tips on how to report issues effectively." + } + } + ] + } + }, + { + "taskType": "scheduledAndTrigger", + "capabilityId": "IssueRouting", + "subCapability": "@Mention", + "version": "1.0", + "config": { + "taskName": "@Mention for tvos", + "labelsAndMentions": [ + { + "labels": [ + "os-tvos" + ], + "mentionees": [ + "steveisok", + "akoeplinger" + ] + } + ], + "enableForPullRequests": true, + "replyTemplate": "Tagging subscribers to 'os-tvos': ${mentionees}\nSee info in area-owners.md if you want to be subscribed." + } + }, + { + "taskType": "scheduledAndTrigger", + "capabilityId": "IssueRouting", + "subCapability": "@Mention", + "version": "1.0", + "config": { + "labelsAndMentions": [ + { + "labels": [ + "os-maccatalyst" + ], + "mentionees": [ + "steveisok", + "akoeplinger" + ] + } + ], + "replyTemplate": "Tagging subscribers to 'os-maccatalyst': ${mentionees}\nSee info in area-owners.md if you want to be subscribed.", + "enableForPullRequests": true, + "taskName": "@Mention for maccatalyst" + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestReviewResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "activitySenderHasPermissions", + "parameters": { + "state": "changes_requested", + "permissions": "write" + } + }, + { + "name": "isAction", + "parameters": { + "action": "submitted" + } + }, + { + "name": "isReviewState", + "parameters": { + "state": "changes_requested" + } + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request_review" + ], + "taskName": "PR reviews with \"changes requested\" applies the needs-author-action label", + "actions": [ + { + "name": "addLabel", + "parameters": { + "label": "needs-author-action" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "synchronize" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "needs-author-action" + } + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "Pushing changes to PR branch removes the needs-author-action label", + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "needs-author-action" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestCommentResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": { + "type": "author" + } + } + }, + { + "name": "isAction", + "parameters": { + "action": "created" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "needs-author-action" + } + }, + { + "name": "isOpen", + "parameters": {} + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "issue_comment" + ], + "taskName": "Author commenting in PR removes the needs-author-action label", + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "needs-author-action" + } + } + ] + }, + "disabled": false + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestReviewResponder", + "version": "1.0", + "config": { + "taskName": "Author responding to a pull request review comment removes the needs-author-action label", + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": { + "type": "author" + } + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "needs-author-action" + } + }, + { + "name": "isAction", + "parameters": { + "action": "submitted" + } + }, + { + "name": "isOpen", + "parameters": {} + } + ] + }, + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "needs-author-action" + } + } + ], + "eventType": "pull_request", + "eventNames": [ + "pull_request_review" + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Meta" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "reopened" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "isInMilestone", + "parameters": {} + } + ] + } + ] + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "area-Meta" + } + } + ] + } + ] + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "operator": "or", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - Issue Triage", + "isOrgProject": true + } + } + ] + }, + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - Issue Triage", + "isOrgProject": true, + "columnName": "Triaged" + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Area Pod: Eric / Jeff - Issue Triage] Add new issue to Board", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - Issue Triage", + "columnName": "Needs Triage", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssueCommentResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Meta" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isCloseAndComment", + "parameters": {} + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "activitySenderHasPermissions", + "parameters": { + "permissions": "write" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - Issue Triage", + "isOrgProject": true + } + } + ] + }, + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - Issue Triage", + "columnName": "Triaged", + "isOrgProject": true + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issue_comment" + ], + "taskName": "[Area Pod: Eric / Jeff - Issue Triage] Needs Further Triage", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - Issue Triage", + "columnName": "Needs Triage", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - Issue Triage", + "columnName": "Needs Triage", + "isOrgProject": true + } + }, + { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Meta" + } + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Area Pod: Eric / Jeff - Issue Triage] Remove relabeled issues", + "actions": [ + { + "name": "removeFromProject", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - Issue Triage", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - Issue Triage", + "isOrgProject": true + } + }, + { + "operator": "not", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - Issue Triage", + "columnName": "Triaged" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "addedToMilestone", + "parameters": {} + }, + { + "name": "labelAdded", + "parameters": { + "label": "needs more info" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "api-ready-for-review" + } + }, + { + "name": "isAction", + "parameters": { + "action": "closed" + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Area Pod: Eric / Jeff - Issue Triage] Move to Triaged Column", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - Issue Triage", + "columnName": "Triaged", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Collections" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Linq" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Text.Json" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Xml" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - PRs", + "isOrgProject": true + } + } + ] + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "[Area Pod: Eirik / Krzysztof / Layomi - PRs] Add new PR to Board", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - PRs", + "columnName": "Needs Champion", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - PRs", + "columnName": "Needs Champion", + "isOrgProject": true + } + }, + { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-Meta" + } + } + ] + } + ] + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "[Area Pod: Eric / Jeff - PRs] Remove relabeled PRs", + "actions": [ + { + "name": "removeFromProject", + "parameters": { + "projectName": "Area Pod: Eric / Jeff - PRs", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.CodeDom" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Configuration" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Reflection" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Reflection.Emit" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Reflection.Metadata" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Resources" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Runtime.CompilerServices" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Text.RegularExpressions" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Threading.Channels" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Threading.Tasks" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.DirectoryServices" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "reopened" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "isInMilestone", + "parameters": {} + } + ] + } + ] + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "area-System.CodeDom" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Configuration" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Reflection" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Reflection.Emit" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Reflection.Metadata" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Resources" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Runtime.CompilerServices" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Text.RegularExpressions" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Threading.Channels" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Threading.Tasks" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.DirectoryServices" + } + } + ] + } + ] + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "operator": "or", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - Issue Triage", + "isOrgProject": true + } + } + ] + }, + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - Issue Triage", + "isOrgProject": true, + "columnName": "Triaged" + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Area Pod: Buyaa / Jose / Steve - Issue Triage] Add new issue to Board", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - Issue Triage", + "columnName": "Needs Triage", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssueCommentResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.CodeDom" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Configuration" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Reflection" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Reflection.Emit" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Reflection.Metadata" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Resources" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Runtime.CompilerServices" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Text.RegularExpressions" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Threading.Channels" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Threading.Tasks" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.DirectoryServices" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isCloseAndComment", + "parameters": {} + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "activitySenderHasPermissions", + "parameters": { + "permissions": "write" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - Issue Triage", + "isOrgProject": true + } + } + ] + }, + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - Issue Triage", + "columnName": "Triaged", + "isOrgProject": true + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issue_comment" + ], + "taskName": "[Area Pod: Buyaa / Jose / Steve - Issue Triage] Needs Further Triage", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - Issue Triage", + "columnName": "Needs Triage", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - Issue Triage", + "columnName": "Needs Triage", + "isOrgProject": true + } + }, + { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.CodeDom" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Configuration" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Reflection" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Reflection.Emit" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Reflection.Metadata" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Resources" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Runtime.CompilerServices" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Text.RegularExpressions" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Threading.Channels" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Threading.Tasks" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.DirectoryServices" + } + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Area Pod: Buyaa / Jose / Steve - Issue Triage] Remove relabeled issues", + "actions": [ + { + "name": "removeFromProject", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - Issue Triage", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - Issue Triage", + "isOrgProject": true + } + }, + { + "operator": "not", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - Issue Triage", + "columnName": "Triaged" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "addedToMilestone", + "parameters": {} + }, + { + "name": "labelAdded", + "parameters": { + "label": "needs more info" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "api-ready-for-review" + } + }, + { + "name": "isAction", + "parameters": { + "action": "closed" + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Area Pod: Buyaa / Jose / Steve - Issue Triage] Move to Triaged Column", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - Issue Triage", + "columnName": "Triaged", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - PRs", + "columnName": "Needs Champion", + "isOrgProject": true + } + }, + { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.CodeDom" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Configuration" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Reflection" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Reflection.Emit" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Reflection.Metadata" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Resources" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Runtime.CompilerServices" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Text.RegularExpressions" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Threading.Channels" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Threading.Tasks" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.DirectoryServices" + } + } + ] + } + ] + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "[Area Pod: Buyaa / Jose / Steve - PRs] Remove relabeled PRs", + "actions": [ + { + "name": "removeFromProject", + "parameters": { + "projectName": "Area Pod: Buyaa / Jose / Steve - PRs", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Collections" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Linq" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Text.Json" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Xml" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "reopened" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "isInMilestone", + "parameters": {} + } + ] + } + ] + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Collections" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Linq" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Text.Json" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "area-System.Xml" + } + } + ] + } + ] + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "operator": "or", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - Issue Triage", + "isOrgProject": true + } + } + ] + }, + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - Issue Triage", + "isOrgProject": true, + "columnName": "Triaged" + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Area Pod: Eirik / Krzysztof / Layomi - Issue Triage] Add new issue to Board", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - Issue Triage", + "columnName": "Needs Triage", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssueCommentResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "or", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Collections" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Linq" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Text.Json" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Xml" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "isCloseAndComment", + "parameters": {} + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "activitySenderHasPermissions", + "parameters": { + "permissions": "write" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - Issue Triage", + "isOrgProject": true + } + } + ] + }, + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - Issue Triage", + "columnName": "Triaged", + "isOrgProject": true + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issue_comment" + ], + "taskName": "[Area Pod: Eirik / Krzysztof / Layomi - Issue Triage] Needs Further Triage", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - Issue Triage", + "columnName": "Needs Triage", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - Issue Triage", + "columnName": "Needs Triage", + "isOrgProject": true + } + }, + { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Collections" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Linq" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Text.Json" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Xml" + } + } + ] + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Area Pod: Eirik / Krzysztof / Layomi - Issue Triage] Remove relabeled issues", + "actions": [ + { + "name": "removeFromProject", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - Issue Triage", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProject", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - Issue Triage", + "isOrgProject": true + } + }, + { + "operator": "not", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - Issue Triage", + "columnName": "Triaged" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "addedToMilestone", + "parameters": {} + }, + { + "name": "labelAdded", + "parameters": { + "label": "needs more info" + } + }, + { + "name": "labelAdded", + "parameters": { + "label": "api-ready-for-review" + } + }, + { + "name": "isAction", + "parameters": { + "action": "closed" + } + } + ] + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "[Area Pod: Eirik / Krzysztof / Layomi - Issue Triage] Move to Triaged Column", + "actions": [ + { + "name": "addToProject", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - Issue Triage", + "columnName": "Triaged", + "isOrgProject": true + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isInProjectColumn", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - PRs", + "columnName": "Needs Champion", + "isOrgProject": true + } + }, + { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Collections" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Linq" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Text.Json" + } + } + ] + }, + { + "operator": "not", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "area-System.Xml" + } + } + ] + } + ] + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "taskName": "[Area Pod: Eirik / Krzysztof / Layomi - PRs] Remove relabeled PRs", + "actions": [ + { + "name": "removeFromProject", + "parameters": { + "projectName": "Area Pod: Eirik / Krzysztof / Layomi - PRs", + "isOrgProject": true + } + } + ] + } + } +] \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index 10a7c114340a5d..6276483bfde877 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -54,7 +54,7 @@ net$(NetCoreAppToolCurrentVersion) $(NetCoreAppCurrentIdentifier),Version=v$(NetCoreAppToolCurrentVersion) - 5.0 + 6.0 $(NetCoreAppLatestStableVersion).0 net$(NetCoreAppLatestStableVersion) @@ -278,6 +278,8 @@ preview + + preview latest strict;nullablePublicOnly diff --git a/docs/area-owners.md b/docs/area-owners.md index c1d570ca21d314..70cc2e4e82fdd9 100644 --- a/docs/area-owners.md +++ b/docs/area-owners.md @@ -29,7 +29,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | area-Extensions-Caching | @ericstj | @eerhardt @maryamariyan @tarekgh | Consultants: @adamsitnik | | area-Extensions-Configuration | @ericstj | @eerhardt @maryamariyan @tarekgh | | | area-Extensions-DependencyInjection | @ericstj | @eerhardt @maryamariyan @tarekgh | | -| area-Extensions-FileSystem | @jeffhandley | @adamsitnik @carlossanlop @jozkee | Consultants: @eerhardt | +| area-Extensions-FileSystem | @jeffhandley | @adamsitnik @jozkee | Consultants: @eerhardt | | area-Extensions-Hosting | @ericstj | @eerhardt @maryamariyan @tarekgh | | | area-Extensions-HttpClientFactory | @karelz | @dotnet/ncl | | | area-Extensions-Logging | @ericstj | @eerhardt @maryamariyan @tarekgh | | @@ -44,7 +44,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | area-Infrastructure | @agocke | @jeffschwMSFT @dleeapho | | | area-Infrastructure-coreclr | @agocke | @jeffschwMSFT @trylek | | | area-Infrastructure-installer | @dleeapho | @dleeapho @NikolaMilosavljevic | | -| area-Infrastructure-libraries | @ericstj | @safern | Covers:
| +| area-Infrastructure-libraries | @ericstj | @carlossanlop @safern | Covers:
| | area-Infrastructure-mono | @steveisok | @directhex | | | area-Interop-coreclr | @jeffschwMSFT | @jeffschwMSFT @AaronRobinsonMSFT | | | area-Interop-mono | @marek-safar | @lambdageek | | @@ -52,7 +52,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | area-Microsoft.CSharp | @jaredpar | @cston @333fred | Archived component - limited churn/contributions (see [#27790](https://github.com/dotnet/runtime/issues/27790)) | | area-Microsoft.Extensions | @ericstj | @eerhardt @maryamariyan @tarekgh | | | area-Microsoft.VisualBasic | @jaredpar | @cston @333fred | Archived component - limited churn/contributions (see [#27790](https://github.com/dotnet/runtime/issues/27790)) | -| area-Microsoft.Win32 | @ericstj | @safern | Including System.Windows.Extensions | +| area-Microsoft.Win32 | @ericstj | @carlossanlop @safern | Including System.Windows.Extensions | | area-NativeAOT-coreclr | @agocke | @MichalStrehovsky @jkotas | | | area-PAL-coreclr | @mangod9 | @janvorli | | | area-Performance-mono | @SamMonoRT | @SamMonoRT | | @@ -66,39 +66,39 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | area-System.CodeDom | @ericstj | @buyaa-n @joperezr @steveharter | | | area-System.Collections | @jeffhandley | @eiriktsarpalis @krwq @layomia | Excluded: Consultants: @steveharter @GrabYourPitchForks | | area-System.ComponentModel | @ericstj | @eerhardt @maryamariyan @tarekgh | | -| area-System.ComponentModel.Composition | @jeffhandley | @eerhardt @maryamariyan @tarekgh | | +| area-System.ComponentModel.Composition | @ericstj | @eerhardt @maryamariyan @tarekgh | | | area-System.ComponentModel.DataAnnotations | @ajcvickers | @lajones @ajcvickers | Included: | -| area-System.Composition | @jeffhandley | @eerhardt @maryamariyan @tarekgh | | +| area-System.Composition | @ericstj | @eerhardt @maryamariyan @tarekgh | | | area-System.Configuration | @ericstj | @buyaa-n @joperezr @steveharter | | -| area-System.Console | @jeffhandley | @adamsitnik @carlossanlop @jozkee | Consultants: @GrabYourPitchForks | -| area-System.Data | @ajcvickers | @ajcvickers @davoudeshtehari @david-engel | | +| area-System.Console | @jeffhandley | @adamsitnik @jozkee | Consultants: @GrabYourPitchForks | +| area-System.Data | @ajcvickers | @ajcvickers @davoudeshtehari @david-engel | | | area-System.Data.Odbc | @ajcvickers | @ajcvickers | | | area-System.Data.OleDB | @ajcvickers | @ajcvickers | | -| area-System.Data.SqlClient | @David-Engel | @davoudeshtehari @david-engel @jrahnama | Archived component - limited churn/contributions (see https://devblogs.microsoft.com/dotnet/introducing-the-new-microsoftdatasqlclient/) | +| area-System.Data.SqlClient | @David-Engel | @davoudeshtehari @david-engel @jrahnama | Archived component - limited churn/contributions (see https://devblogs.microsoft.com/dotnet/introducing-the-new-microsoftdatasqlclient/) | | area-System.Diagnostics | @tommcdon | @tommcdon | | | area-System.Diagnostics-coreclr | @tommcdon | @tommcdon | | | area-System.Diagnostics-mono | @lewing | @thaystg @radical | | | area-System.Diagnostics.Activity | @tommcdon | @eerhardt @maryamariyan @tarekgh | | -| area-System.Diagnostics.EventLog | @ericstj | @safern | | +| area-System.Diagnostics.EventLog | @ericstj | @carlossanlop @safern | | | area-System.Diagnostics.Metric | @tommcdon | @noahfalk | | -| area-System.Diagnostics.PerformanceCounter | @ericstj | @safern | | -| area-System.Diagnostics.Process | @jeffhandley | @adamsitnik @carlossanlop @jozkee | | +| area-System.Diagnostics.PerformanceCounter | @ericstj | @carlossanlop @safern | | +| area-System.Diagnostics.Process | @jeffhandley | @adamsitnik @jozkee | | | area-System.Diagnostics.Tracing | @tommcdon | @noahfalk @tommcdon @safern @tarekgh | Included: | -| area-System.Diagnostics.TraceSource | @ericstj | @safern | | +| area-System.Diagnostics.TraceSource | @ericstj | @carlossanlop @safern | | | area-System.DirectoryServices | @ericstj | @buyaa-n @joperezr @steveharter | Consultants: @BRDPM @grubioe @jay98014 | -| area-System.Drawing | @ericstj | @safern | | +| area-System.Drawing | @ericstj | @carlossanlop @safern | | | area-System.Dynamic.Runtime | @jaredpar | @cston @333fred | Archived component - limited churn/contributions (see [#27790](https://github.com/dotnet/runtime/issues/27790)) | | area-System.Formats.Asn1 | @jeffhandley | @bartonjs @GrabYourPitchforks | | | area-System.Formats.Cbor | @jeffhandley | @bartonjs @GrabYourPitchforks | Consultants: @eiriktsarpalis | | area-System.Globalization | @ericstj | @eerhardt @maryamariyan @tarekgh | | -| area-System.IO | @jeffhandley | @adamsitnik @carlossanlop @jozkee | | -| area-System.IO.Compression | @jeffhandley | @adamsitnik @carlossanlop @jozkee | | +| area-System.IO | @jeffhandley | @adamsitnik @jozkee | Consultants: @carlossanlop | +| area-System.IO.Compression | @jeffhandley | @adamsitnik @jozkee | Consultants: @carlossanlop | | area-System.IO.Pipelines | @kevinpi | @davidfowl @halter73 @jkotalik | | | area-System.Linq | @jeffhandley | @eiriktsarpalis @krwq @layomia | | | area-System.Linq.Expressions | @jaredpar | @cston @333fred | Archived component - limited churn/contributions (see [#27790](https://github.com/dotnet/runtime/issues/27790)) | -| area-System.Linq.Parallel | @jeffhandley | @adamsitnik @carlossanlop @jozkee | Consultants: @stephentoub @kouvel | -| area-System.Management | @ericstj | @safern | WMI | -| area-System.Memory | @jeffhandley | @adamsitnik @carlossanlop @jozkee | Consultants: @GrabYourPitchforks | +| area-System.Linq.Parallel | @jeffhandley | @adamsitnik @jozkee | Consultants: @stephentoub @kouvel | +| area-System.Management | @ericstj | @carlossanlop @safern | WMI | +| area-System.Memory | @jeffhandley | @adamsitnik @jozkee | Consultants: @GrabYourPitchforks | | area-System.Net | @karelz | @dotnet/ncl | Included: | | area-System.Net.Http | @karelz | @dotnet/ncl | | | area-System.Net.Quic | @karelz | @dotnet/ncl | | @@ -120,7 +120,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | area-System.Security | @jeffhandley | @bartonjs @GrabYourPitchforks | | | area-System.ServiceModel | @HongGit | @HongGit @mconnew | Repo: https://github.com/dotnet/WCF
Packages: | | area-System.ServiceModel.Syndication | @HongGit | @StephenMolloy @HongGit | | -| area-System.ServiceProcess | @ericstj | @safern | | +| area-System.ServiceProcess | @ericstj | @carlossanlop @safern | | | area-System.Speech | @danmoseley | @danmoseley | | | area-System.Text.Encoding | @jeffhandley | @bartonjs @GrabYourPitchforks | Consultants: @tarekgh | | area-System.Text.Encodings.Web | @jeffhandley | @bartonjs @GrabYourPitchforks | | diff --git a/docs/design/coreclr/botr/clr-abi.md b/docs/design/coreclr/botr/clr-abi.md index 1f65ca71bc9ce4..f7b01352de9224 100644 --- a/docs/design/coreclr/botr/clr-abi.md +++ b/docs/design/coreclr/botr/clr-abi.md @@ -109,7 +109,7 @@ ARM64-only: When a method returns a structure that is larger than 16 bytes the c *Calli Pinvoke* - The VM wants the address of the PInvoke in (AMD64) `R10` / (ARM) `R12` / (ARM64) `R14` (In the JIT: `REG_PINVOKE_TARGET_PARAM`), and the signature (the pinvoke cookie) in (AMD64) `R11` / (ARM) `R4` / (ARM64) `R15` (in the JIT: `REG_PINVOKE_COOKIE_PARAM`). -*Normal PInvoke* - The VM shares IL stubs based on signatures, but wants the right method to show up in call stack and exceptions, so the MethodDesc for the exact PInvoke is passed in the (x86) `EAX` / (AMD64) `R10` / (ARM, ARM64) `R12` (in the JIT: `REG_SECRET_STUB_PARAM`). Then in the IL stub, when the JIT gets `CORJIT_FLG_PUBLISH_SECRET_PARAM`, it must move the register into a compiler temp. The value is returned for the intrinsic `CORINFO_INTRINSIC_StubHelpers_GetStubContext`, and the address of that location is returned for `CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr`. +*Normal PInvoke* - The VM shares IL stubs based on signatures, but wants the right method to show up in call stack and exceptions, so the MethodDesc for the exact PInvoke is passed in the (x86) `EAX` / (AMD64) `R10` / (ARM, ARM64) `R12` (in the JIT: `REG_SECRET_STUB_PARAM`). Then in the IL stub, when the JIT gets `CORJIT_FLG_PUBLISH_SECRET_PARAM`, it must move the register into a compiler temp. The value is returned for the intrinsic `NI_System_StubHelpers_GetStubContext`. # PInvokes diff --git a/docs/infra/automation.md b/docs/infra/automation.md new file mode 100644 index 00000000000000..23af9d9e4a2088 --- /dev/null +++ b/docs/infra/automation.md @@ -0,0 +1,7 @@ +## Automation + +### Fabric Bot + +This repository uses Fabric Bot to automate issue and pull request management. All automation rules are defined in the [`.github/fabricbot.json`](.github/fabricbot.json) file. + +At this moment Fabric Bot has not published a JSON schema for the configuration format. In order to make changes to the configuration file, you should use the [`Fabric Bot portal`](https://portal.fabricbot.ms/bot/) to load the configuration file via the "Import Configuration" option and make changes using the editor. You need to be signed out from the portal in order for this option to appear. diff --git a/docs/workflow/building/mono/README.md b/docs/workflow/building/mono/README.md index 0b5c4701d20601..56c95551f5eba8 100644 --- a/docs/workflow/building/mono/README.md +++ b/docs/workflow/building/mono/README.md @@ -33,6 +33,15 @@ build.cmd mono ``` When the build completes, product binaries will be dropped in the `artifacts\bin\mono\..` folder. +If you want to skip restoring nuget packages, when only making change to mono, you want to use this command: +```bash +./build.sh mono --build +``` +or on Windows, +```cmd +build.cmd mono --build +``` + ### Useful Build Arguments Here are a list of build arguments that may be of use: diff --git a/docs/workflow/requirements/windows-requirements.md b/docs/workflow/requirements/windows-requirements.md index 402610a6253448..6e32c5f1d45a6d 100644 --- a/docs/workflow/requirements/windows-requirements.md +++ b/docs/workflow/requirements/windows-requirements.md @@ -37,7 +37,7 @@ Visual Studio 2022 installation process: A `.vsconfig` file is included in the root of the dotnet/runtime repository that includes all components needed to build the dotnet/runtime repository. You can [import `.vsconfig` in your Visual Studio installer](https://docs.microsoft.com/en-us/visualstudio/install/import-export-installation-configurations?view=vs-2022#import-a-configuration) to install all necessary components. You may get a message saying 'Microsoft.Net.Component.4.5.2.TargetingPack has no matching workload or component found'. This is not an issue as long as you have a newer targeting pack installed. -Visual Studio 2022 Preview 4 or later is required. +Visual Studio 2022 or later is required. ## Build Tools diff --git a/docs/workflow/testing/libraries/testing-apple.md b/docs/workflow/testing/libraries/testing-apple.md index c303fc737362d2..0ce42742e9c710 100644 --- a/docs/workflow/testing/libraries/testing-apple.md +++ b/docs/workflow/testing/libraries/testing-apple.md @@ -108,6 +108,10 @@ To build for AOT only mode, add `/p:RunAOTCompilation=true /p:MonoEnableInterpre To build for AOT-LLVM mode, add `/p:RunAOTCompilation=true /p:MonoEnableInterpreter=false /p:MonoEnableLLVM=true` to a build command. +4. App Sandbox + +To build the test app bundle with the App Sandbox entitlement, add `/p:EnableAppSandbox=true` to a build command. + ### Test App Design iOS/tvOS `*.app` (or `*.ipa`) is basically a simple [ObjC app](https://github.com/dotnet/runtime/blob/main/src/tasks/AppleAppBuilder/Templates/main-console.m) that inits the Mono Runtime. This Mono Runtime starts a simple xunit test runner called XHarness.TestRunner (see https://github.com/dotnet/xharness) which runs tests for all `*.Tests.dll` libs in the bundle. There is also XHarness.CLI tool to deploy `*.app` and `*.ipa` to a target (device or simulator) and listens for logs via network sockets. diff --git a/docs/workflow/testing/mono/testing.md b/docs/workflow/testing/mono/testing.md index 14d141e529a3ca..7d34ee1a7b0380 100644 --- a/docs/workflow/testing/mono/testing.md +++ b/docs/workflow/testing/mono/testing.md @@ -7,10 +7,10 @@ Before running tests, [build Mono](../../building/mono/README.md) using the desi To build the runtime tests for Mono JIT or interpreter: -1. Build CoreCLR - the `clr.native` subset is enough but you can build the whole thing, optionally. From the `$(REPO_ROOT)`: +1. Build test host (corerun) - From the `$(REPO_ROOT)`: ``` -./build.sh clr.native -c +./build.sh clr.hosts -c ``` 2. Build the tests (in `$(REPO_ROOT)/src/tests`) @@ -26,25 +26,23 @@ For example: `./build.sh excludemonofailures release -test:JIT/opt/InstructionCo Run individual test: ``` -cd src/mono -make run-tests-coreclr CoreClrTest="bash ../../artifacts/tests/coreclr/OSX.x64.Release/JIT/opt/InstructionCombining/DivToMul/DivToMul.sh" +bash ./artifacts/tests/coreclr/OSX.x64.Release/JIT/opt/InstructionCombining/DivToMul/DivToMul.sh -coreroot=`pwd`/artifacts/tests/coreclr/OSX.x64.Release/Tests/Core_Root ``` -Run all tests: +Run all built tests: ``` -cd src/mono -make run-tests-coreclr-all +./run.sh ``` To debug a single test with `lldb`: -1. Run the test at least once normally (or manually run the `mono.proj` `PatchCoreClrCoreRoot` target) -2. Run the shell script for the test case manually: +1. Run the shell script for the test case manually with the `-debug` option: ``` bash ./artifacts/tests/coreclr/OSX.x64.Release/JIT/opt/InstructionCombining/DivToMul/DivToMul.sh -coreroot=`pwd`/artifacts/tests/coreclr/OSX.x64.Release/Tests/Core_Root -debug=/usr/bin/lldb ``` -3. In LLDB add the debug symbols for mono: `add-dsym /libcoreclr.dylib.dwarf` -4. Run/debug the test +2. In LLDB add the debug symbols for mono: `add-dsym /libcoreclr.dylib.dwarf` +3. Run/debug the test + ### WebAssembly: Build the runtime tests for WebAssembly diff --git a/eng/ApiCompatExcludeAttributes.txt b/eng/ApiCompatExcludeAttributes.txt index 16db9ad94cda54..19475f50297f8a 100644 --- a/eng/ApiCompatExcludeAttributes.txt +++ b/eng/ApiCompatExcludeAttributes.txt @@ -1,2 +1,3 @@ T:System.CLSCompliantAttribute T:System.Diagnostics.CodeAnalysis.MemberNotNullAttribute +T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute diff --git a/eng/Subsets.props b/eng/Subsets.props index 65da9d8861bd68..b76d849cf4cc26 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -100,6 +100,7 @@ + @@ -164,6 +165,10 @@ + + $(ClrRuntimeBuildSubsets);ClrHostsSubset=true + + $(ClrRuntimeBuildSubsets);ClrRuntimeSubset=true diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index d4afac7bd540bf..a677399625acac 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/icu - c454e00c63464ff6b599f23448bcc0a0c9fbac5f + c2e633bfc491d2b9dc5e49d2ffe3de9b4b5359d0 https://github.com/dotnet/msquic @@ -18,153 +18,157 @@ - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - 6003aa6296ad2bebdf8147de94afbea9b5f16264 + 658e482c4af9a16cbe9ea0fae4c6e4281f1521b6 - + https://github.com/dotnet/runtime-assets - 6003aa6296ad2bebdf8147de94afbea9b5f16264 + 658e482c4af9a16cbe9ea0fae4c6e4281f1521b6 - + https://github.com/dotnet/runtime-assets - 6003aa6296ad2bebdf8147de94afbea9b5f16264 + 658e482c4af9a16cbe9ea0fae4c6e4281f1521b6 - + https://github.com/dotnet/runtime-assets - 6003aa6296ad2bebdf8147de94afbea9b5f16264 + 658e482c4af9a16cbe9ea0fae4c6e4281f1521b6 - + https://github.com/dotnet/runtime-assets - 6003aa6296ad2bebdf8147de94afbea9b5f16264 + 658e482c4af9a16cbe9ea0fae4c6e4281f1521b6 - + https://github.com/dotnet/runtime-assets - 6003aa6296ad2bebdf8147de94afbea9b5f16264 + 658e482c4af9a16cbe9ea0fae4c6e4281f1521b6 - + https://github.com/dotnet/runtime-assets - 6003aa6296ad2bebdf8147de94afbea9b5f16264 + 658e482c4af9a16cbe9ea0fae4c6e4281f1521b6 - + https://github.com/dotnet/runtime-assets - 6003aa6296ad2bebdf8147de94afbea9b5f16264 + 658e482c4af9a16cbe9ea0fae4c6e4281f1521b6 - + https://github.com/dotnet/runtime-assets - 6003aa6296ad2bebdf8147de94afbea9b5f16264 + 658e482c4af9a16cbe9ea0fae4c6e4281f1521b6 - + https://github.com/dotnet/runtime-assets - 6003aa6296ad2bebdf8147de94afbea9b5f16264 + 658e482c4af9a16cbe9ea0fae4c6e4281f1521b6 - + + https://github.com/dotnet/runtime-assets + 658e482c4af9a16cbe9ea0fae4c6e4281f1521b6 + + https://github.com/dotnet/llvm-project - 2e69189f031e16ffb36167d89a658e14cbdba099 + 45004b720a726e3283dc241136384198a823312d - + https://github.com/dotnet/llvm-project - 2e69189f031e16ffb36167d89a658e14cbdba099 + 45004b720a726e3283dc241136384198a823312d - + https://github.com/dotnet/llvm-project - 2e69189f031e16ffb36167d89a658e14cbdba099 + 45004b720a726e3283dc241136384198a823312d - + https://github.com/dotnet/llvm-project - 2e69189f031e16ffb36167d89a658e14cbdba099 + 45004b720a726e3283dc241136384198a823312d - + https://github.com/dotnet/llvm-project - 2e69189f031e16ffb36167d89a658e14cbdba099 + 45004b720a726e3283dc241136384198a823312d - + https://github.com/dotnet/llvm-project - 2e69189f031e16ffb36167d89a658e14cbdba099 + 45004b720a726e3283dc241136384198a823312d - + https://github.com/dotnet/llvm-project - 2e69189f031e16ffb36167d89a658e14cbdba099 + 45004b720a726e3283dc241136384198a823312d - + https://github.com/dotnet/llvm-project - 2e69189f031e16ffb36167d89a658e14cbdba099 + 45004b720a726e3283dc241136384198a823312d https://github.com/dotnet/runtime @@ -202,17 +206,17 @@ https://github.com/dotnet/linker 4e77a943d360a5afea00de7b8a2d0ba2c721223f - + https://github.com/dotnet/xharness - d27acf37630aa2a03824b698aa51356e47f902c0 + 5898930d87e4acca8fbcea5f38f08c967a506f79 - + https://github.com/dotnet/xharness - d27acf37630aa2a03824b698aa51356e47f902c0 + 5898930d87e4acca8fbcea5f38f08c967a506f79 - + https://github.com/dotnet/arcade - 59775387deb609d7c62f9e713d133c34ba28ffcd + 05a63c6bae31f97583d35f5a16e1bd8f41a1d094 https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -230,13 +234,13 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization 91d6b3c1f51888d166701510189505f35714665c - + https://github.com/dotnet/hotreload-utils - 49922f7ec53df036084899e597f3c94627d709eb + 5add30ce9477193614865f98c811dc4001134308 - + https://github.com/dotnet/runtime-assets - 6003aa6296ad2bebdf8147de94afbea9b5f16264 + 658e482c4af9a16cbe9ea0fae4c6e4281f1521b6 https://github.com/dotnet/roslyn-analyzers diff --git a/eng/Versions.props b/eng/Versions.props index 6af39feabfeb67..cb1a36cd3a9131 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -54,21 +54,21 @@ 2.0.0-alpha.1.21525.11 - 7.0.0-beta.21602.3 - 7.0.0-beta.21602.3 - 7.0.0-beta.21602.3 - 7.0.0-beta.21602.3 - 7.0.0-beta.21602.3 - 7.0.0-beta.21602.3 - 2.5.1-beta.21602.3 - 7.0.0-beta.21602.3 - 7.0.0-beta.21602.3 - 7.0.0-beta.21602.3 - 7.0.0-beta.21602.3 - 7.0.0-beta.21602.3 - 7.0.0-beta.21602.3 - 7.0.0-beta.21602.3 - 7.0.0-beta.21602.3 + 7.0.0-beta.21609.2 + 7.0.0-beta.21609.2 + 7.0.0-beta.21609.2 + 7.0.0-beta.21609.2 + 7.0.0-beta.21609.2 + 7.0.0-beta.21609.2 + 2.5.1-beta.21609.2 + 7.0.0-beta.21609.2 + 7.0.0-beta.21609.2 + 7.0.0-beta.21609.2 + 7.0.0-beta.21609.2 + 7.0.0-beta.21609.2 + 7.0.0-beta.21609.2 + 7.0.0-beta.21609.2 + 7.0.0-beta.21609.2 6.0.0-preview.1.102 @@ -118,17 +118,18 @@ 4.5.0 7.0.0-alpha.1.21576.4 - 7.0.0-beta.21602.1 - 7.0.0-beta.21602.1 - 7.0.0-beta.21602.1 - 7.0.0-beta.21602.1 - 7.0.0-beta.21602.1 - 7.0.0-beta.21602.1 - 7.0.0-beta.21602.1 - 7.0.0-beta.21602.1 - 7.0.0-beta.21602.1 - 7.0.0-beta.21602.1 - 7.0.0-beta.21602.1 + 7.0.0-beta.21603.1 + 7.0.0-beta.21603.1 + 7.0.0-beta.21603.1 + 7.0.0-beta.21603.1 + 7.0.0-beta.21603.1 + 7.0.0-beta.21603.1 + 7.0.0-beta.21603.1 + 7.0.0-beta.21603.1 + 7.0.0-beta.21603.1 + 7.0.0-beta.21603.1 + 7.0.0-beta.21603.1 + 7.0.0-beta.21603.1 1.0.0-prerelease.21577.2 1.0.0-prerelease.21577.2 @@ -151,9 +152,9 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21602.2 - 1.0.0-prerelease.21602.2 - 1.0.2-alpha.0.21579.1 + 1.0.0-prerelease.21609.1 + 1.0.0-prerelease.21609.1 + 1.0.2-alpha.0.21606.2 2.4.2-pre.9 2.4.2 1.3.0 @@ -169,18 +170,18 @@ 7.0.100-1.21601.5 $(MicrosoftNETILLinkTasksVersion) - 7.0.0-alpha.1.21579.1 + 7.0.0-alpha.1.21606.1 7.0.0-alpha.1.21529.3 - 11.1.0-alpha.1.21579.1 - 11.1.0-alpha.1.21579.1 - 11.1.0-alpha.1.21579.1 - 11.1.0-alpha.1.21579.1 - 11.1.0-alpha.1.21579.1 - 11.1.0-alpha.1.21579.1 - 11.1.0-alpha.1.21579.1 - 11.1.0-alpha.1.21579.1 + 11.1.0-alpha.1.21606.1 + 11.1.0-alpha.1.21606.1 + 11.1.0-alpha.1.21606.1 + 11.1.0-alpha.1.21606.1 + 11.1.0-alpha.1.21606.1 + 11.1.0-alpha.1.21606.1 + 11.1.0-alpha.1.21606.1 + 11.1.0-alpha.1.21606.1 7.0.0-alpha.1.21601.1 $(MicrosoftNETWorkloadEmscriptenManifest70100Version) diff --git a/eng/common/cross/armv6/sources.list.buster b/eng/common/cross/armv6/sources.list.buster new file mode 100644 index 00000000000000..f27fc4fb346b6c --- /dev/null +++ b/eng/common/cross/armv6/sources.list.buster @@ -0,0 +1,2 @@ +deb http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi +deb-src http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index 6fa2c8aa5511dd..5102245b7b5e3f 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -99,6 +99,15 @@ while :; do __AlpineArch=armv7 __QEMUArch=arm ;; + armv6) + __BuildArch=armv6 + __UbuntuArch=armhf + __QEMUArch=arm + __UbuntuRepo="http://raspbian.raspberrypi.org/raspbian/" + __CodeName=buster + __LLDB_Package="liblldb-6.0-dev" + __Keyring="/usr/share/keyrings/raspbian-archive-keyring.gpg" + ;; arm64) __BuildArch=arm64 __UbuntuArch=arm64 @@ -236,6 +245,12 @@ while :; do shift done +if [ -e "$__Keyring" ]; then + __Keyring="--keyring=$__Keyring" +else + __Keyring="" +fi + if [ "$__BuildArch" == "armel" ]; then __LLDB_Package="lldb-3.5-dev" fi @@ -337,7 +352,7 @@ elif [[ "$__CodeName" == "illumos" ]]; then wget -P "$__RootfsDir"/usr/include/netpacket https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/inet/sockmods/netpacket/packet.h wget -P "$__RootfsDir"/usr/include/sys https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/sys/sdt.h elif [[ -n $__CodeName ]]; then - qemu-debootstrap --arch $__UbuntuArch $__CodeName $__RootfsDir $__UbuntuRepo + qemu-debootstrap $__Keyring --arch $__UbuntuArch $__CodeName $__RootfsDir $__UbuntuRepo cp $__CrossDir/$__BuildArch/sources.list.$__CodeName $__RootfsDir/etc/apt/sources.list chroot $__RootfsDir apt-get update chroot $__RootfsDir apt-get -f -y install diff --git a/eng/common/templates/job/execute-sdl.yml b/eng/common/templates/job/execute-sdl.yml index 8128f2c357052c..8cf772b3cbf812 100644 --- a/eng/common/templates/job/execute-sdl.yml +++ b/eng/common/templates/job/execute-sdl.yml @@ -51,14 +51,9 @@ jobs: value: ${{ parameters.AzDOPipelineId }} - name: AzDOBuildId value: ${{ parameters.AzDOBuildId }} - # The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in - # sync with the packages.config file. - - name: DefaultGuardianVersion - value: 0.109.0 + - template: /eng/common/templates/variables/sdl-variables.yml - name: GuardianVersion value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} - - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config pool: vmImage: windows-2019 steps: @@ -125,57 +120,11 @@ jobs: displayName: Extract Archive Artifacts continueOnError: ${{ parameters.sdlContinueOnError }} - - ${{ if ne(parameters.overrideGuardianVersion, '') }}: - - powershell: | - $content = Get-Content $(GuardianPackagesConfigFile) - - Write-Host "packages.config content was:`n$content" - - $content = $content.Replace('$(DefaultGuardianVersion)', '$(GuardianVersion)') - $content | Set-Content $(GuardianPackagesConfigFile) - - Write-Host "packages.config content updated to:`n$content" - displayName: Use overridden Guardian version ${{ parameters.overrideGuardianVersion }} - - - task: NuGetToolInstaller@1 - displayName: 'Install NuGet.exe' - - task: NuGetCommand@2 - displayName: 'Install Guardian' - inputs: - restoreSolution: $(Build.SourcesDirectory)\eng\common\sdl\packages.config - feedsToUse: config - nugetConfigPath: $(Build.SourcesDirectory)\eng\common\sdl\NuGet.config - externalFeedCredentials: GuardianConnect - restoreDirectory: $(Build.SourcesDirectory)\.packages - - - ${{ if ne(parameters.overrideParameters, '') }}: - - powershell: ${{ parameters.executeAllSdlToolsScript }} ${{ parameters.overrideParameters }} - displayName: Execute SDL - continueOnError: ${{ parameters.sdlContinueOnError }} - - ${{ if eq(parameters.overrideParameters, '') }}: - - powershell: ${{ parameters.executeAllSdlToolsScript }} - -GuardianPackageName Microsoft.Guardian.Cli.$(GuardianVersion) - -NugetPackageDirectory $(Build.SourcesDirectory)\.packages - -AzureDevOpsAccessToken $(dn-bot-dotnet-build-rw-code-rw) - ${{ parameters.additionalParameters }} - displayName: Execute SDL - continueOnError: ${{ parameters.sdlContinueOnError }} - - - ${{ if ne(parameters.publishGuardianDirectoryToPipeline, 'false') }}: - # We want to publish the Guardian results and configuration for easy diagnosis. However, the - # '.gdn' dir is a mix of configuration, results, extracted dependencies, and Guardian default - # tooling files. Some of these files are large and aren't useful during an investigation, so - # exclude them by simply deleting them before publishing. (As of writing, there is no documented - # way to selectively exclude a dir from the pipeline artifact publish task.) - - task: DeleteFiles@1 - displayName: Delete Guardian dependencies to avoid uploading - inputs: - SourceFolder: $(Agent.BuildDirectory)/.gdn - Contents: | - c - i - condition: succeededOrFailed() - - publish: $(Agent.BuildDirectory)/.gdn - artifact: GuardianConfiguration - displayName: Publish GuardianConfiguration - condition: succeededOrFailed() + - template: /eng/common/templates/steps/execute-sdl.yml + parameters: + overrideGuardianVersion: ${{ parameters.overrideGuardianVersion }} + executeAllSdlToolsScript: ${{ parameters.executeAllSdlToolsScript }} + overrideParameters: ${{ parameters.overrideParameters }} + additionalParameters: ${{ parameters.additionalParameters }} + publishGuardianDirectoryToPipeline: ${{ parameters.publishGuardianDirectoryToPipeline }} + sdlContinueOnError: ${{ parameters.sdlContinueOnError }} diff --git a/eng/common/templates/steps/execute-sdl.yml b/eng/common/templates/steps/execute-sdl.yml new file mode 100644 index 00000000000000..7b8ee18a28d7e7 --- /dev/null +++ b/eng/common/templates/steps/execute-sdl.yml @@ -0,0 +1,68 @@ +parameters: + overrideGuardianVersion: '' + executeAllSdlToolsScript: '' + overrideParameters: '' + additionalParameters: '' + publishGuardianDirectoryToPipeline: false + sdlContinueOnError: false + condition: '' + +steps: +- ${{ if ne(parameters.overrideGuardianVersion, '') }}: + - powershell: | + $content = Get-Content $(GuardianPackagesConfigFile) + + Write-Host "packages.config content was:`n$content" + + $content = $content.Replace('$(DefaultGuardianVersion)', '$(GuardianVersion)') + $content | Set-Content $(GuardianPackagesConfigFile) + + Write-Host "packages.config content updated to:`n$content" + displayName: Use overridden Guardian version ${{ parameters.overrideGuardianVersion }} + +- task: NuGetToolInstaller@1 + displayName: 'Install NuGet.exe' + +- task: NuGetCommand@2 + displayName: 'Install Guardian' + inputs: + restoreSolution: $(Build.SourcesDirectory)\eng\common\sdl\packages.config + feedsToUse: config + nugetConfigPath: $(Build.SourcesDirectory)\eng\common\sdl\NuGet.config + externalFeedCredentials: GuardianConnect + restoreDirectory: $(Build.SourcesDirectory)\.packages + +- ${{ if ne(parameters.overrideParameters, '') }}: + - powershell: ${{ parameters.executeAllSdlToolsScript }} ${{ parameters.overrideParameters }} + displayName: Execute SDL + continueOnError: ${{ parameters.sdlContinueOnError }} + condition: ${{ parameters.condition }} + +- ${{ if eq(parameters.overrideParameters, '') }}: + - powershell: ${{ parameters.executeAllSdlToolsScript }} + -GuardianPackageName Microsoft.Guardian.Cli.$(GuardianVersion) + -NugetPackageDirectory $(Build.SourcesDirectory)\.packages + -AzureDevOpsAccessToken $(dn-bot-dotnet-build-rw-code-rw) + ${{ parameters.additionalParameters }} + displayName: Execute SDL + continueOnError: ${{ parameters.sdlContinueOnError }} + condition: ${{ parameters.condition }} + +- ${{ if ne(parameters.publishGuardianDirectoryToPipeline, 'false') }}: + # We want to publish the Guardian results and configuration for easy diagnosis. However, the + # '.gdn' dir is a mix of configuration, results, extracted dependencies, and Guardian default + # tooling files. Some of these files are large and aren't useful during an investigation, so + # exclude them by simply deleting them before publishing. (As of writing, there is no documented + # way to selectively exclude a dir from the pipeline artifact publish task.) + - task: DeleteFiles@1 + displayName: Delete Guardian dependencies to avoid uploading + inputs: + SourceFolder: $(Agent.BuildDirectory)/.gdn + Contents: | + c + i + condition: succeededOrFailed() + - publish: $(Agent.BuildDirectory)/.gdn + artifact: GuardianConfiguration + displayName: Publish GuardianConfiguration + condition: succeededOrFailed() \ No newline at end of file diff --git a/eng/common/templates/variables/sdl-variables.yml b/eng/common/templates/variables/sdl-variables.yml new file mode 100644 index 00000000000000..dbdd66d4a4b3a0 --- /dev/null +++ b/eng/common/templates/variables/sdl-variables.yml @@ -0,0 +1,7 @@ +variables: +# The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in +# sync with the packages.config file. +- name: DefaultGuardianVersion + value: 0.109.0 +- name: GuardianPackagesConfigFile + value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config \ No newline at end of file diff --git a/eng/generators.targets b/eng/generators.targets index 13926ce90bd7ed..4c32554b5e2195 100644 --- a/eng/generators.targets +++ b/eng/generators.targets @@ -20,13 +20,15 @@ ('@(Reference)' != '' and @(Reference->AnyHaveMetadataValue('Identity', 'System.Runtime.InteropServices'))) or ('@(ProjectReference)' != '' - and @(ProjectReference->AnyHaveMetadataValue('Identity', '$(CoreLibProject)'))))" /> + and @(ProjectReference->AnyHaveMetadataValue('Identity', '$(CoreLibProject)'))) + or ('$(NetCoreAppCurrentTargetFrameworkMoniker)' == '$(TargetFrameworkMoniker)' + and '$(DisableImplicitAssemblyReferences)' == 'false'))" /> + and ('$(TargetFrameworkIdentifier)' == '.NETStandard' or '$(TargetFrameworkIdentifier)' == '.NETFramework' or ('$(TargetFrameworkIdentifier)' == '.NETCoreApp' and $([MSBuild]::VersionLessThan($(TargetFrameworkVersion), '$(NetCoreAppCurrentVersion)'))))" /> diff --git a/eng/packaging.targets b/eng/packaging.targets index 7f9707ab8d8148..6d2bbce10da346 100644 --- a/eng/packaging.targets +++ b/eng/packaging.targets @@ -22,19 +22,26 @@ '$(IsRIDSpecificProject)' == 'true') and '$(PreReleaseVersionLabel)' != 'servicing' and '$(GitHubRepositoryName)' != 'runtimelab'">true + false $(XmlDocFileRoot)1033\$(AssemblyName).xml true
- + + + 0 + - $(MajorVersion).$(MinorVersion).$(ServicingVersion) + $(MajorVersion).$(MinorVersion).$(ServicingVersion) + $(Version)-$(VersionSuffix) <_IsWindowsDesktopApp Condition="$(WindowsDesktopCoreAppLibrary.Contains('$(AssemblyName);'))">true <_IsAspNetCoreApp Condition="$(AspNetCoreAppLibrary.Contains('$(AssemblyName);'))">true - <_AssemblyInTargetingPack Condition="'$(IsNETCoreAppSrc)' == 'true' or '$(_IsAspNetCoreApp)' == 'true' or '$(_IsWindowsDesktopApp)' == 'true'">true + <_AssemblyInTargetingPack Condition="('$(IsNETCoreAppSrc)' == 'true' or '$(_IsAspNetCoreApp)' == 'true' or '$(_IsWindowsDesktopApp)' == 'true') and '$(TargetFrameworkIdentifier)' != '.NETFramework'">true - $(MajorVersion).$(MinorVersion).0.$(ServicingVersion) + $(MajorVersion).$(MinorVersion).0.$(ServicingVersion) @@ -246,9 +253,15 @@ + + + + diff --git a/eng/pipelines/common/xplat-setup.yml b/eng/pipelines/common/xplat-setup.yml index 1c7d6490df5f6b..05a6bc7c4a63c4 100644 --- a/eng/pipelines/common/xplat-setup.yml +++ b/eng/pipelines/common/xplat-setup.yml @@ -128,7 +128,7 @@ jobs: # OSX Build Pool (we don't have on-prem OSX BuildPool ${{ if in(parameters.osGroup, 'OSX', 'MacCatalyst', 'iOS', 'iOSSimulator', 'tvOS', 'tvOSSimulator') }}: - vmImage: 'macOS-10.15' + vmImage: 'macOS-11' # Official Build Windows Pool ${{ if and(eq(parameters.osGroup, 'windows'), ne(variables['System.TeamProject'], 'public')) }}: diff --git a/eng/pipelines/coreclr/perf.yml b/eng/pipelines/coreclr/perf.yml index 0e274b0d899cab..ec233f348b0ecb 100644 --- a/eng/pipelines/coreclr/perf.yml +++ b/eng/pipelines/coreclr/perf.yml @@ -239,7 +239,7 @@ jobs: runJobTemplate: /eng/pipelines/coreclr/templates/run-scenarios-job.yml logicalmachine: 'perfpixel4a' - # run mono and maui iOS scenarios + # run mono iOS scenarios and maui iOS scenarios - template: /eng/pipelines/common/platform-matrix.yml parameters: jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml @@ -382,7 +382,7 @@ jobs: runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml logicalmachine: 'perftiger' -# run coreclr perftiger microbenchmarks pgo perf jobs + # run coreclr perftiger microbenchmarks pgo perf jobs - template: /eng/pipelines/common/platform-matrix.yml parameters: jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml diff --git a/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml b/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml index 3d5d83b6dadc88..6e8ace4e7935c2 100644 --- a/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml +++ b/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml @@ -23,6 +23,32 @@ steps: - script: make run MONO_ARCH=arm64 DEPLOY_AND_RUN=false workingDirectory: $(Build.SourcesDirectory)/src/mono/sample/Android displayName: Build HelloAndroid sample app + - template: /eng/pipelines/common/upload-artifact-step.yml + parameters: + rootFolder: $(Build.SourcesDirectory)/artifacts/bin/AndroidSampleApp/arm64/Release/android-arm64/publish/apk/bin/HelloAndroid.apk + includeRootFolder: true + displayName: Android Mono Artifacts + artifactName: AndroidMonoarm64 + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz + - script: rm -r -f $(Build.SourcesDirectory)/artifacts/bin/AndroidSampleApp + workingDirectory: $(Build.SourcesDirectory)/artifacts/bin + displayName: clean bindir + - script: make run MONO_ARCH=arm64 DEPLOY_AND_RUN=false RUNTIME_COMPONENTS=diagnostics_tracing + workingDirectory: $(Build.SourcesDirectory)/src/mono/sample/Android + displayName: Build HelloAndroid sample app + - script: mv $(Build.SourcesDirectory)/artifacts/bin/AndroidSampleApp/arm64/Release/android-arm64/publish/apk/bin/HelloAndroid.apk $(Build.SourcesDirectory)/artifacts/bin/AndroidSampleApp/arm64/Release/android-arm64/publish/apk/bin/HelloAndroidWithDiag.apk + - template: /eng/pipelines/common/upload-artifact-step.yml + parameters: + rootFolder: $(Build.SourcesDirectory)/artifacts/bin/AndroidSampleApp/arm64/Release/android-arm64/publish/apk/bin/HelloAndroidWithDiag.apk + includeRootFolder: true + displayName: Android Mono Artifacts With Diag + artifactName: AndroidMonoWithDiagarm64 + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz + - ${{ if eq(parameters.osGroup, 'iOS') }}: - script: make build-appbundle TARGET=iOS MONO_ARCH=arm64 MONO_CONFIG=Release AOT=True USE_LLVM=False DEPLOY_AND_RUN=false env: @@ -37,13 +63,13 @@ steps: artifactName: ${{ parameters.artifactName }} - template: /eng/pipelines/common/upload-artifact-step.yml parameters: - rootFolder: $(Build.SourcesDirectory)/src/mono/sample/iOS/bin/ios-arm64/publish/app/HelloiOS/Release-iphoneos/HelloiOS.app - includeRootFolder: true - displayName: iOS Sample App NoLLVM - artifactName: iOSSampleAppNoLLVM - archiveExtension: '.tar.gz' - archiveType: tar - tarCompression: gz + rootFolder: $(Build.SourcesDirectory)/src/mono/sample/iOS/bin/ios-arm64/publish/app/HelloiOS/Release-iphoneos/HelloiOS.app + includeRootFolder: true + displayName: iOS Sample App NoLLVM + artifactName: iOSSampleAppNoLLVM + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz - script: rm -r -f $(Build.SourcesDirectory)/src/mono/sample/iOS/bin workingDirectory: $(Build.SourcesDirectory)/src/mono/sample/iOS displayName: Clean bindir @@ -60,31 +86,11 @@ steps: artifactName: ${{ parameters.artifactName }} - template: /eng/pipelines/common/upload-artifact-step.yml parameters: - rootFolder: $(Build.SourcesDirectory)/src/mono/sample/iOS/bin/ios-arm64/publish/app/HelloiOS/Release-iphoneos/HelloiOS.app - includeRootFolder: true - displayName: iOS Sample App LLVM - artifactName: iOSSampleAppLLVM - archiveExtension: '.tar.gz' - archiveType: tar - tarCompression: gz + rootFolder: $(Build.SourcesDirectory)/src/mono/sample/iOS/bin/ios-arm64/publish/app/HelloiOS/Release-iphoneos/HelloiOS.app + includeRootFolder: true + displayName: iOS Sample App LLVM + artifactName: iOSSampleAppLLVM + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz - - template: /eng/pipelines/common/upload-artifact-step.yml - parameters: - osGroup: ${{ parameters.osGroup }} - osSubgroup: ${{ parameters.osSubgroup }} - archType: ${{ parameters.archType }} - buildConfig: ${{ parameters.buildConfig }} - runtimeFlavor: ${{ parameters.runtimeFlavor }} - helixQueues: ${{ parameters.helixQueues }} - targetRid: ${{ parameters.targetRid }} - nameSuffix: ${{ parameters.nameSuffix }} - platform: ${{ parameters.platform }} - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - rootFolder: ${{ parameters.rootFolder }} - includeRootFolder: ${{ parameters.includeRootFolder }} - displayName: ${{ parameters.displayName }} - artifactName: ${{ parameters.artifactName }} - archiveExtension: ${{ parameters.archiveExtension }} - archiveType: ${{ parameters.archiveType }} - tarCompression: ${{ parameters.tarCompression }} - diff --git a/eng/pipelines/coreclr/templates/perf-job.yml b/eng/pipelines/coreclr/templates/perf-job.yml index 50941e60f1c5f5..dd1ff0ebd3b242 100644 --- a/eng/pipelines/coreclr/templates/perf-job.yml +++ b/eng/pipelines/coreclr/templates/perf-job.yml @@ -154,11 +154,11 @@ jobs: - ${{ if eq(parameters.runtimeType, 'AndroidMono')}}: - template: /eng/pipelines/common/download-artifact-step.yml parameters: - unpackFolder: $(Build.SourcesDirectory) + unpackFolder: $(Build.SourcesDirectory)/androidHelloWorld cleanUnpackFolder: false artifactFileName: 'AndroidMonoarm64.tar.gz' artifactName: 'AndroidMonoarm64' - displayName: 'Mono Android runtime' + displayName: 'Mono Android HelloWorld' - template: /eng/pipelines/common/download-artifact-step.yml parameters: unpackFolder: $(Build.SourcesDirectory) @@ -166,8 +166,14 @@ jobs: artifactFileName: 'MauiAndroidApp.tar.gz' artifactName: 'MauiAndroidApp' displayName: 'Maui Android App' - - + - template: /eng/pipelines/common/download-artifact-step.yml + parameters: + unpackFolder: $(Build.SourcesDirectory)/androidHelloWorldWithDiag + cleanUnpackFolder: false + artifactFileName: 'AndroidMonoWithDiagarm64.tar.gz' + artifactName: 'AndroidMonoWithDiagarm64' + displayName: 'Mono Android Diagnostic Helloworld' + # Download iOSMono tests and MauiiOS/MacCatalyst - ${{ if eq(parameters.runtimeType, 'iOSMono') }}: - template: /eng/pipelines/common/download-artifact-step.yml diff --git a/eng/pipelines/coreclr/templates/run-scenarios-job.yml b/eng/pipelines/coreclr/templates/run-scenarios-job.yml index edaf1f485fc955..ab713b3c02175b 100644 --- a/eng/pipelines/coreclr/templates/run-scenarios-job.yml +++ b/eng/pipelines/coreclr/templates/run-scenarios-job.yml @@ -118,26 +118,26 @@ jobs: displayName: Copy scenario support files (Linux) condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) # build Startup - - script: $(PayloadDirectory)\dotnet\dotnet.exe publish -c Release -o $(WorkItemDirectory)\Startup -f netcoreapp3.1 -r win-$(Architecture) $(PerformanceDirectory)\src\tools\ScenarioMeasurement\Startup\Startup.csproj + - script: $(PayloadDirectory)\dotnet\dotnet.exe publish -c Release -o $(WorkItemDirectory)\Startup -f net6.0 -r win-$(Architecture) $(PerformanceDirectory)\src\tools\ScenarioMeasurement\Startup\Startup.csproj displayName: Build Startup tool (Windows) env: - PERFLAB_TARGET_FRAMEWORKS: netcoreapp3.1 + PERFLAB_TARGET_FRAMEWORKS: net6.0 condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT')) - - script: $(PayloadDirectory)/dotnet/dotnet publish -c Release -o $(WorkItemDirectory)/startup -f netcoreapp3.1 -r linux-$(Architecture) $(PerformanceDirectory)/src/tools/ScenarioMeasurement/Startup/Startup.csproj + - script: $(PayloadDirectory)/dotnet/dotnet publish -c Release -o $(WorkItemDirectory)/startup -f net6.0 -r linux-$(Architecture) $(PerformanceDirectory)/src/tools/ScenarioMeasurement/Startup/Startup.csproj displayName: Build Startup tool (Linux) env: - PERFLAB_TARGET_FRAMEWORKS: netcoreapp3.1 + PERFLAB_TARGET_FRAMEWORKS: net6.0 condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) # build SizeOnDisk - - script: $(PayloadDirectory)\dotnet\dotnet.exe publish -c Release -o $(WorkItemDirectory)\SOD -f netcoreapp3.1 -r win-$(Architecture) $(PerformanceDirectory)\src\tools\ScenarioMeasurement\SizeOnDisk\SizeOnDisk.csproj + - script: $(PayloadDirectory)\dotnet\dotnet.exe publish -c Release -o $(WorkItemDirectory)\SOD -f net6.0 -r win-$(Architecture) $(PerformanceDirectory)\src\tools\ScenarioMeasurement\SizeOnDisk\SizeOnDisk.csproj displayName: Build SizeOnDisk tool (Windows) env: - PERFLAB_TARGET_FRAMEWORKS: netcoreapp3.1 + PERFLAB_TARGET_FRAMEWORKS: net6.0 condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT')) - - script: $(PayloadDirectory)/dotnet/dotnet publish -c Release -o $(WorkItemDirectory)/SOD -f netcoreapp3.1 -r linux-$(Architecture) $(PerformanceDirectory)/src/tools/ScenarioMeasurement/SizeOnDisk/SizeOnDisk.csproj + - script: $(PayloadDirectory)/dotnet/dotnet publish -c Release -o $(WorkItemDirectory)/SOD -f net6.0 -r linux-$(Architecture) $(PerformanceDirectory)/src/tools/ScenarioMeasurement/SizeOnDisk/SizeOnDisk.csproj displayName: Build SizeOnDisk tool (Linux) env: - PERFLAB_TARGET_FRAMEWORKS: netcoreapp3.1 + PERFLAB_TARGET_FRAMEWORKS: net6.0 condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) # run perf testing in helix diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index 73432c9c408b1a..a57c1ab59fd589 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -36,6 +36,7 @@ jobs: - ${{ if eq(parameters.platform, 'Linux_arm64') }}: - ${{ if eq(parameters.jobParameters.isFullMatrix, true) }}: - (Ubuntu.1804.ArmArch.Open)Ubuntu.1804.ArmArch.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-16.04-helix-arm64v8-20210106155927-56c6673 + - (Ubuntu.2104.Arm64.Open)Ubuntu.1804.ArmArch.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-21.04-helix-arm64v8-20210922170819-34a2d72 - ${{ if eq(parameters.jobParameters.isFullMatrix, false) }}: - (Ubuntu.1804.ArmArch.Open)Ubuntu.1804.ArmArch.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-16.04-helix-arm64v8-20210106155927-56c6673 @@ -58,7 +59,7 @@ jobs: - RedHat.7.Amd64.Open - SLES.15.Amd64.Open - (Fedora.34.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-34-helix-20210913123654-4f64125 - - (Ubuntu.1910.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-19.10-helix-amd64-cfcfd50-20191030180623 + - (Ubuntu.2104.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-21.04-helix-amd64-20210922170909-34a2d72 - (Debian.10.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-bfcd90a-20200121150006 - ${{ if or(ne(parameters.jobParameters.testScope, 'outerloop'), ne(parameters.jobParameters.runtimeFlavor, 'mono')) }}: - ${{ if eq(parameters.jobParameters.isFullMatrix, true) }}: @@ -69,10 +70,11 @@ jobs: - SLES.12.Amd64.Open - SLES.15.Amd64.Open - (Fedora.34.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-34-helix-20210913123654-4f64125 - - (Ubuntu.1910.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-19.10-helix-amd64-cfcfd50-20191030180623 + - (Ubuntu.2104.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-21.04-helix-amd64-20210922170909-34a2d72 - (Debian.10.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-20210304164434-56c6673 - (Debian.11.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-11-helix-amd64-20210304164428-5a7c380 - (Mariner.1.0.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-1.0-helix-20210528192219-92bf620 + - (openSUSE.15.2.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:opensuse-15.2-helix-amd64-20211018152525-9cc02fe - ${{ if eq(parameters.jobParameters.isFullMatrix, false) }}: - (Centos.7.Amd64.Open)Ubuntu.1604.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-mlnet-helix-20210714125435-dde38af - RedHat.7.Amd64.Open diff --git a/eng/pipelines/runtime-manual.yml b/eng/pipelines/runtime-manual.yml index 83f9e891dab573..3be3efb85c9e2c 100644 --- a/eng/pipelines/runtime-manual.yml +++ b/eng/pipelines/runtime-manual.yml @@ -58,6 +58,34 @@ jobs: interpreter: true testRunNamePrefixSuffix: Mono_$(_BuildConfig) +# +# MacCatalyst interp - requires AOT Compilation and Interp flags +# Build the whole product using Mono and run libraries tests +# The test app is built with the App Sandbox entitlement +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: Release + runtimeFlavor: mono + platforms: + - MacCatalyst_x64 + # don't run tests on arm64 PRs until we can get significantly more devices + - ${{ if eq(variables['isFullMatrix'], true) }}: + - MacCatalyst_arm64 + jobParameters: + testGroup: innerloop + nameSuffix: AllSubsets_Mono_AppSandbox + buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=adhoc /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildDarwinFrameworks=true /p:EnableAppSandbox=true + timeoutInMinutes: 180 + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/libraries/helix.yml + extraStepsParameters: + creator: dotnet-bot + interpreter: true + testRunNamePrefixSuffix: Mono_$(_BuildConfig) + # # iOS/tvOS devices - Full AOT + AggressiveTrimming to reduce size # Build the whole product using Mono and run libraries tests @@ -332,47 +360,46 @@ jobs: eq(variables['isManualOrIsNotPR'], true), eq(variables['isFullMatrix'], true)) -# Disabled due to https://github.com/dotnet/runtime/issues/61721 -#- template: /eng/pipelines/common/platform-matrix.yml - #parameters: - #jobTemplate: /eng/pipelines/common/global-build-job.yml - #helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml - #buildConfig: release - #runtimeFlavor: mono - #platforms: - #- Browser_wasm_win - #variables: - ## map dependencies variables to local variables - #- name: librariesContainsChange - #value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ] - #- name: monoContainsChange - #value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ] - #jobParameters: - #testGroup: innerloop - #nameSuffix: Windows_wasm_AOT - #buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=windows /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true $(_runSmokeTestsOnlyArg) - #timeoutInMinutes: 180 - #condition: >- - #or( - #eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), - #eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), - #eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true), - #eq(variables['isManualOrIsNotPR'], true), - #eq(variables['isFullMatrix'], true)) - ## extra steps, run tests - #extraStepsTemplate: /eng/pipelines/libraries/helix.yml - #extraStepsParameters: - #creator: dotnet-bot - #testRunNamePrefixSuffix: Mono_$(_BuildConfig) - #extraHelixArguments: /p:BrowserHost=windows /p:NeedsToBuildWasmAppsOnHelix=true $(_runSmokeTestsOnlyArg) - #scenarios: - #- normal - #condition: >- - #or( - #eq(variables['librariesContainsChange'], true), - #eq(variables['monoContainsChange'], true), - #eq(variables['isManualOrIsNotPR'], true), - #eq(variables['isFullMatrix'], true)) +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Browser_wasm_win + variables: + # map dependencies variables to local variables + - name: librariesContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ] + - name: monoContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ] + jobParameters: + testGroup: innerloop + nameSuffix: Windows_wasm_AOT + buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=windows /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true $(_runSmokeTestsOnlyArg) + timeoutInMinutes: 180 + condition: >- + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true), + eq(variables['isManualOrIsNotPR'], true), + eq(variables['isFullMatrix'], true)) + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/libraries/helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: Mono_$(_BuildConfig) + extraHelixArguments: /p:BrowserHost=windows /p:NeedsToBuildWasmAppsOnHelix=true $(_runSmokeTestsOnlyArg) + scenarios: + - normal + condition: >- + or( + eq(variables['librariesContainsChange'], true), + eq(variables['monoContainsChange'], true), + eq(variables['isManualOrIsNotPR'], true), + eq(variables['isFullMatrix'], true)) # # Build for Browser/wasm, with EnableAggressiveTrimming=true diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index a38deea1927a5c..eb39d2e2c53a90 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -154,6 +154,51 @@ jobs: eq(variables['isManualOrIsNotPR'], true), eq(variables['isFullMatrix'], true)) +# +# MacCatalyst interp - requires AOT Compilation and Interp flags +# Build the whole product using Mono and run libraries tests +# The test app is built with the App Sandbox entitlement +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: Release + runtimeFlavor: mono + platforms: + - MacCatalyst_x64 + # don't run tests on arm64 PRs until we can get significantly more devices + - ${{ if eq(variables['isFullMatrix'], true) }}: + - MacCatalyst_arm64 + variables: + # map dependencies variables to local variables + - name: librariesContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ] + - name: monoContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ] + jobParameters: + testGroup: innerloop + nameSuffix: AllSubsets_Mono_AppSandbox + buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true $(_runSmokeTestsOnlyArg) /p:DevTeamProvisioning=adhoc /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildDarwinFrameworks=true /p:EnableAppSandbox=true + timeoutInMinutes: 180 + condition: >- + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true), + eq(variables['isFullMatrix'], true)) + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/libraries/helix.yml + extraStepsParameters: + creator: dotnet-bot + interpreter: true + testRunNamePrefixSuffix: Mono_$(_BuildConfig) + condition: >- + or( + eq(variables['librariesContainsChange'], true), + eq(variables['monoContainsChange'], true), + eq(variables['isFullMatrix'], true)) + # # iOS/tvOS devices - Full AOT + AggressiveTrimming to reduce size # Build the whole product using Mono and run libraries tests @@ -493,47 +538,44 @@ jobs: # Build Browser_wasm, on windows, and run tests with AOT # # Disabled due to https://github.com/dotnet/runtime/issues/61721 -#- template: /eng/pipelines/common/platform-matrix.yml - #parameters: - #jobTemplate: /eng/pipelines/common/global-build-job.yml - #helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml - #buildConfig: release - #runtimeFlavor: mono - #platforms: - #- Browser_wasm_win - #variables: - ## map dependencies variables to local variables - #- name: librariesContainsChange - #value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ] - #- name: monoContainsChange - #value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ] - #jobParameters: - #testGroup: innerloop - #nameSuffix: Windows_wasm_AOT - #buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=windows /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true $(_runSmokeTestsOnlyArg) - #timeoutInMinutes: 180 - #condition: >- - #or( - #eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), - #eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), - #eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true), - #eq(variables['isManualOrIsNotPR'], true), - #eq(variables['isFullMatrix'], true)) - ## extra steps, run tests - #extraStepsTemplate: /eng/pipelines/libraries/helix.yml - #extraStepsParameters: - #creator: dotnet-bot - #testRunNamePrefixSuffix: Mono_$(_BuildConfig) - #extraHelixArguments: /p:BrowserHost=windows /p:NeedsToBuildWasmAppsOnHelix=true $(_runSmokeTestsOnlyArg) - #scenarios: - #- normal - #condition: >- - #or( - #eq(variables['librariesContainsChange'], true), - #eq(variables['monoContainsChange'], true), - #eq(variables['isManualOrIsNotPR'], true), - #eq(variables['isFullMatrix'], true)) - +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Browser_wasm_win + variables: + # map dependencies variables to local variables + - name: librariesContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ] + - name: monoContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ] + jobParameters: + testGroup: innerloop + nameSuffix: Windows_wasm_AOT + buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=windows /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true $(_runSmokeTestsOnlyArg) + timeoutInMinutes: 180 + condition: >- + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true), + eq(variables['isFullMatrix'], true)) + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/libraries/helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: Mono_$(_BuildConfig) + extraHelixArguments: /p:BrowserHost=windows /p:NeedsToBuildWasmAppsOnHelix=true $(_runSmokeTestsOnlyArg) + scenarios: + - normal + condition: >- + or( + eq(variables['librariesContainsChange'], true), + eq(variables['monoContainsChange'], true), + eq(variables['isFullMatrix'], true)) # # Build Browser_wasm, on windows, and run Wasm.Build.Tests diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index af8b9c116dd80c..04fca6cc817093 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -1026,7 +1026,8 @@ jobs: runtimeFlavor: mono platforms: - OSX_x64 - - Linux_arm64 + - ${{ if eq(variables['isFullMatrix'], true) }}: + - Linux_arm64 helixQueueGroup: pr helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml jobParameters: @@ -1035,7 +1036,10 @@ jobs: liveRuntimeBuildConfig: release runtimeVariant: minijit condition: >- - eq(variables['isFullMatrix'], true) + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true), + eq(variables['isFullMatrix'], true)) # # Mono CoreCLR runtime Test executions using live libraries in interpreter mode @@ -1047,7 +1051,8 @@ jobs: runtimeFlavor: mono platforms: - OSX_x64 - - Linux_arm64 + - ${{ if eq(variables['isFullMatrix'], true) }}: + - Linux_arm64 helixQueueGroup: pr helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml jobParameters: @@ -1056,7 +1061,10 @@ jobs: liveRuntimeBuildConfig: release runtimeVariant: monointerpreter condition: >- - eq(variables['isFullMatrix'], true) + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true), + eq(variables['isFullMatrix'], true)) # # Mono CoreCLR runtime Test executions using live libraries and LLVM AOT # Only when Mono is changed @@ -1078,7 +1086,10 @@ jobs: liveRuntimeBuildConfig: release runtimeVariant: llvmaot condition: >- - eq(variables['isFullMatrix'], true) + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true), + eq(variables['isFullMatrix'], true)) # # Mono CoreCLR runtime Test executions using live libraries and LLVM Full AOT @@ -1101,7 +1112,10 @@ jobs: liveRuntimeBuildConfig: release runtimeVariant: llvmfullaot condition: >- - eq(variables['isFullMatrix'], true) + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true), + eq(variables['isFullMatrix'], true)) # # Libraries Release Test Execution against a release mono runtime. diff --git a/eng/testing/performance/android_scenarios.proj b/eng/testing/performance/android_scenarios.proj index 338a696150fb3d..52af8fa1678dc8 100644 --- a/eng/testing/performance/android_scenarios.proj +++ b/eng/testing/performance/android_scenarios.proj @@ -1,4 +1,9 @@ + + true + 1.0.0-prerelease.21566.2 + %HELIX_CORRELATION_PAYLOAD%\microsoft.dotnet.xharness.cli\$(MicrosoftDotNetXHarnessCLIVersion)\tools\net6.0\any\Microsoft.DotNet.XHarness.CLI.dll + python3 $(HelixPreCommands);chmod +x $HELIX_WORKITEM_PAYLOAD/SOD/SizeOnDisk @@ -42,5 +47,11 @@ $(Python) test.py sod --scenario-name "%(Identity)" $(Python) post.py + + $(WorkItemDirectory) + echo on;set XHARNESSPATH=$(XharnessPath);cd $(ScenarioDirectory)helloandroid;copy %HELIX_CORRELATION_PAYLOAD%\HelloAndroidWithDiag.apk .;$(Python) pre.py + $(Python) test.py devicestartup --device-type android --package-path pub\HelloAndroidWithDiag.apk --package-name net.dot.HelloAndroid --exit-code 42 --scenario-name "%(Identity)" + $(Python) post.py + - \ No newline at end of file + diff --git a/eng/testing/performance/performance-setup.ps1 b/eng/testing/performance/performance-setup.ps1 index 5e2837f802ad7e..d85325ee1349d5 100644 --- a/eng/testing/performance/performance-setup.ps1 +++ b/eng/testing/performance/performance-setup.ps1 @@ -148,7 +148,13 @@ if ($AndroidMono) { { mkdir $WorkItemDirectory } - Copy-Item -path "$SourceDirectory\artifacts\bin\AndroidSampleApp\arm64\Release\android-arm64\publish\apk\bin\HelloAndroid.apk" $PayloadDirectory + if(Test-Path "$SourceDirectory\androidHelloWorld\HelloAndroid.apk") { + Copy-Item -path "$SourceDirectory\androidHelloWorld\HelloAndroid.apk" $PayloadDirectory -Verbose + } + + if(Test-Path "$SourceDirectory\androidHelloWorldWithDiag\HelloAndroidWithDiag.apk") { + Copy-Item -path "$SourceDirectory\androidHelloWorldWithDiag\HelloAndroidWithDiag.apk" $PayloadDirectory -Verbose + } Copy-Item -path "$SourceDirectory\MauiAndroidDefault.apk" $PayloadDirectory $SetupArguments = $SetupArguments -replace $Architecture, 'arm64' } diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index 17783b7ed5ec43..192363c4b170a1 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -6,7 +6,7 @@ $(AppBundleRoot)runonly\$(AssemblyName) $([MSBuild]::NormalizeDirectory('$(PublishDir)', 'AppBundle')) $([MSBuild]::NormalizePath('$(BundleDir)', '$(RunScriptOutputName)')) - + true BundleTestAndroidApp Publish @@ -36,7 +36,6 @@ - $(Configuration) $(AdditionalXHarnessArguments) --expected-exit-code $(ExpectedExitCode) @@ -55,9 +54,9 @@ - - @@ -184,7 +183,7 @@ - + <_ShellCommandSeparator Condition="'$(OS)' == 'Windows_NT'">&& <_ShellCommandSeparator Condition="'$(OS)' != 'Windows_NT'">&& + false @@ -28,7 +29,7 @@ <_XHarnessArgs Condition="'$(OS)' != 'Windows_NT'">wasm $XHARNESS_COMMAND --app=. --output-directory=$XHARNESS_OUT <_XHarnessArgs Condition="'$(OS)' == 'Windows_NT'">wasm %XHARNESS_COMMAND% --app=. --output-directory=%XHARNESS_OUT% - <_XHarnessArgs Condition="'$(Scenario)' != 'WasmTestOnBrowser' and '$(Scenario)' != 'BuildWasmApps'">$(_XHarnessArgs) --engine=$(JSEngine) $(JSEngineArgs) --js-file=main.js + <_XHarnessArgs Condition="'$(Scenario)' != 'WasmTestOnBrowser' and '$(Scenario)' != 'BuildWasmApps'">$(_XHarnessArgs) --engine=$(JSEngine) $(JSEngineArgs) --js-file=test-main.js <_XHarnessArgs Condition="'$(BrowserHost)' == 'windows'">$(_XHarnessArgs) --browser=chrome --browser-path=%HELIX_CORRELATION_PAYLOAD%\chrome-win\chrome.exe <_XHarnessArgs Condition="'$(IsFunctionalTest)' == 'true'" >$(_XHarnessArgs) --expected-exit-code=$(ExpectedExitCode) <_XHarnessArgs Condition="'$(WasmXHarnessArgs)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgs) diff --git a/global.json b/global.json index ae4b4d3af4b13a..d70a6fc7b84469 100644 --- a/global.json +++ b/global.json @@ -12,10 +12,10 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "7.0.0-beta.21602.3", - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.21602.3", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.21602.3", - "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.21602.3", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "7.0.0-beta.21609.2", + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.21609.2", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.21609.2", + "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.21609.2", "Microsoft.Build.NoTargets": "3.1.0", "Microsoft.Build.Traversal": "3.0.23", "Microsoft.NET.Sdk.IL": "7.0.0-alpha.1.21576.4" diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs index e5c1db9ce14dbd..332dd6301cde09 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs @@ -12,13 +12,21 @@ public static partial class AssemblyExtensions [return: MarshalAs(UnmanagedType.Bool)] private static unsafe partial bool InternalTryGetRawMetadata(QCallAssembly assembly, ref byte* blob, ref int length); - // Retrieves the metadata section of the assembly, for use with System.Reflection.Metadata.MetadataReader. - // - Returns false upon failure. Metadata might not be available for some assemblies, such as AssemblyBuilder, .NET - // native images, etc. - // - Callers should not write to the metadata blob - // - The metadata blob pointer will remain valid as long as the AssemblyLoadContext with which the assembly is - // associated, is alive. The caller is responsible for keeping the assembly object alive while accessing the - // metadata blob. + /// + /// Retrieves the metadata section of the assembly, for use with . + /// + /// The assembly from which to retrieve the metadata. + /// When this method returns, contains the pointer to the metadata section blob. + /// When this method returns, contains the length of the metadata section blob. + /// + /// if the metadata is retrieved successfully; upon failure. + /// The metadata might not be available for some assemblies, such as , AOT images, etc. + /// + /// + /// Callers should not write to the metadata blob. + /// The metadata blob pointer will remain valid as long as the assembly is alive. + /// The caller is responsible for keeping the assembly object alive while accessing the metadata blob. + /// [CLSCompliant(false)] // out byte* blob public static unsafe bool TryGetRawMetadata(this Assembly assembly, out byte* blob, out int length) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs index 41a85755b01d0f..41735099fb2e79 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs @@ -1181,7 +1181,7 @@ internal static class StubHelpers internal static extern IntPtr GetNDirectTarget(IntPtr pMD); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr GetDelegateTarget(Delegate pThis, ref IntPtr pStubArg); + internal static extern IntPtr GetDelegateTarget(Delegate pThis); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void ClearLastError(); @@ -1348,11 +1348,6 @@ internal static void CheckStringLength(uint length) [MethodImpl(MethodImplOptions.InternalCall)] internal static extern IntPtr GetStubContext(); -#if TARGET_64BIT - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr GetStubContextAddr(); -#endif // TARGET_64BIT - #if FEATURE_ARRAYSTUB_AS_IL [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void ArrayTypeCheck(object o, object[] arr); @@ -1363,6 +1358,7 @@ internal static void CheckStringLength(uint length) internal static extern void MulticastDebuggerTraceHelper(object o, int count); #endif + [Intrinsic] [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern IntPtr NextCallReturnAddress(); } // class StubHelpers diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index fc62daa4406ef3..9ed65b7a5d1e06 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -4551,8 +4551,10 @@ void MethodContext::dmpGetFunctionFixedEntryPoint(DWORDLONG key, const Agnostic_ printf("GetFunctionFixedEntryPoint key ftn-%016llX, value %s", key, SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value).c_str()); } -void MethodContext::repGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP* pResult) +void MethodContext::repGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, bool isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP* pResult) { + // The isUnsafeFunctionPointer has no impact on the resulting value. + // It helps produce side-effects in the runtime only. DWORDLONG key = CastHandle(ftn); AssertMapAndKeyExist(GetFunctionFixedEntryPoint, key, ": key %016llX", key); Agnostic_CORINFO_CONST_LOOKUP value = GetFunctionFixedEntryPoint->Get(key); diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h index 6e7c910f16d586..2e695c6d221772 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h @@ -569,7 +569,7 @@ class MethodContext void recGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP* pResult); void dmpGetFunctionFixedEntryPoint(DWORDLONG key, const Agnostic_CORINFO_CONST_LOOKUP& value); - void repGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP* pResult); + void repGetFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, bool isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP* pResult); void recGetFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num, CORINFO_FIELD_HANDLE result); void dmpGetFieldInClass(DLD key, DWORDLONG value); @@ -1067,7 +1067,7 @@ enum mcPackets Packet_GetMethodNameFromMetadata = 161, Packet_GetDefaultEqualityComparerClass = 162, Packet_CompareTypesForCast = 163, - Packet_CompareTypesForEquality = 164, + Packet_CompareTypesForEquality = 164, Packet_GetUnboxedEntry = 165, Packet_GetClassNameFromMetadata = 166, Packet_GetTypeInstantiationArgument = 167, diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp index ef1afc408afff3..50fed27bb63bc2 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1439,10 +1439,13 @@ void interceptor_ICJI::getFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn, /* I // return a directly callable address. This can be used similarly to the // value returned by getFunctionEntryPoint() except that it is // guaranteed to be multi callable entrypoint. -void interceptor_ICJI::getFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP* pResult) +void interceptor_ICJI::getFunctionFixedEntryPoint( + CORINFO_METHOD_HANDLE ftn, + bool isUnsafeFunctionPointer, + CORINFO_CONST_LOOKUP* pResult) { mc->cr->AddCall("getFunctionFixedEntryPoint"); - original_ICorJitInfo->getFunctionFixedEntryPoint(ftn, pResult); + original_ICorJitInfo->getFunctionFixedEntryPoint(ftn, isUnsafeFunctionPointer, pResult); mc->recGetFunctionFixedEntryPoint(ftn, pResult); } diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp index 60ba9a998d5544..0b0507ddb121d9 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -985,10 +985,11 @@ void interceptor_ICJI::getFunctionEntryPoint( void interceptor_ICJI::getFunctionFixedEntryPoint( CORINFO_METHOD_HANDLE ftn, + bool isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP* pResult) { mcs->AddCall("getFunctionFixedEntryPoint"); - original_ICorJitInfo->getFunctionFixedEntryPoint(ftn, pResult); + original_ICorJitInfo->getFunctionFixedEntryPoint(ftn, isUnsafeFunctionPointer, pResult); } void* interceptor_ICJI::getMethodSync( diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp index 0c01d60937d6ab..6e1d86a176271c 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -862,9 +862,10 @@ void interceptor_ICJI::getFunctionEntryPoint( void interceptor_ICJI::getFunctionFixedEntryPoint( CORINFO_METHOD_HANDLE ftn, + bool isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP* pResult) { - original_ICorJitInfo->getFunctionFixedEntryPoint(ftn, pResult); + original_ICorJitInfo->getFunctionFixedEntryPoint(ftn, isUnsafeFunctionPointer, pResult); } void* interceptor_ICJI::getMethodSync( diff --git a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp index ab34fb4623da37..350931cf3aaf9c 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp @@ -1272,10 +1272,13 @@ void MyICJI::getFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn, /* IN */ // return a directly callable address. This can be used similarly to the // value returned by getFunctionEntryPoint() except that it is // guaranteed to be multi callable entrypoint. -void MyICJI::getFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP* pResult) +void MyICJI::getFunctionFixedEntryPoint( + CORINFO_METHOD_HANDLE ftn, + bool isUnsafeFunctionPointer, + CORINFO_CONST_LOOKUP* pResult) { jitInstance->mc->cr->AddCall("getFunctionFixedEntryPoint"); - jitInstance->mc->repGetFunctionFixedEntryPoint(ftn, pResult); + jitInstance->mc->repGetFunctionFixedEntryPoint(ftn, isUnsafeFunctionPointer, pResult); } // get the synchronization handle that is passed to monXstatic function diff --git a/src/coreclr/build-runtime.cmd b/src/coreclr/build-runtime.cmd index dc17395f486319..d14d06111dabae 100644 --- a/src/coreclr/build-runtime.cmd +++ b/src/coreclr/build-runtime.cmd @@ -324,6 +324,9 @@ if NOT DEFINED PYTHON ( set __CMakeTarget= for /f "delims=" %%a in ("-%__RequestedBuildComponents%-") do ( set "string=%%a" + if not "!string:-hosts-=!"=="!string!" ( + set __CMakeTarget=!__CMakeTarget! hosts + ) if not "!string:-jit-=!"=="!string!" ( set __CMakeTarget=!__CMakeTarget! jit ) @@ -739,7 +742,7 @@ echo -all: Builds all configurations and platforms. echo Build architecture: one of -x64, -x86, -arm, -arm64 ^(default: -x64^). echo Build type: one of -Debug, -Checked, -Release ^(default: -Debug^). echo -component ^ : specify this option one or more times to limit components built to those specified. -echo Allowed ^: jit alljits runtime paltests iltools +echo Allowed ^: hosts jit alljits runtime paltests iltools echo -enforcepgo: verify after the build that PGO was used for key DLLs, and fail the build if not echo -pgoinstrument: generate instrumented code for profile guided optimization enabled binaries. echo -cmakeargs: user-settable additional arguments passed to CMake. diff --git a/src/coreclr/build-runtime.sh b/src/coreclr/build-runtime.sh index 7adfb63f21743d..cbf18f67b2dd0d 100755 --- a/src/coreclr/build-runtime.sh +++ b/src/coreclr/build-runtime.sh @@ -22,7 +22,7 @@ usage_list+=("-pgodatapath: path to profile guided optimization data.") usage_list+=("-pgoinstrument: generate instrumented code for profile guided optimization enabled binaries.") usage_list+=("-skipcrossarchnative: Skip building cross-architecture native binaries.") usage_list+=("-staticanalyzer: use scan_build static analyzer.") -usage_list+=("-component: Build individual components instead of the full project. Available options are 'jit', 'runtime', 'paltests', 'alljits', and 'iltools'. Can be specified multiple times.") +usage_list+=("-component: Build individual components instead of the full project. Available options are 'hosts', 'jit', 'runtime', 'paltests', 'alljits', and 'iltools'. Can be specified multiple times.") setup_dirs_local() { diff --git a/src/coreclr/components.cmake b/src/coreclr/components.cmake index 01d3db0c87d210..81ec07a3f42d59 100644 --- a/src/coreclr/components.cmake +++ b/src/coreclr/components.cmake @@ -1,6 +1,7 @@ # Define all the individually buildable components of the CoreCLR build and their respective targets add_component(jit) add_component(alljits) +add_component(hosts) add_component(runtime) add_component(paltests paltests_install) add_component(iltools) @@ -16,5 +17,7 @@ add_dependencies(runtime coreclr_misc) # The runtime build requires the clrjit and iltools builds add_dependencies(runtime jit iltools) +add_dependencies(runtime hosts) + # The cross-components build is separate, so we don't need to add a dependency on coreclr_misc add_component(crosscomponents) diff --git a/src/coreclr/debug/dbgutil/machoreader.cpp b/src/coreclr/debug/dbgutil/machoreader.cpp index 48fea72682f205..d2801547cb9ba9 100644 --- a/src/coreclr/debug/dbgutil/machoreader.cpp +++ b/src/coreclr/debug/dbgutil/machoreader.cpp @@ -126,19 +126,41 @@ MachOModule::TryLookupSymbol(const char* symbolName, uint64_t* symbolValue) _ASSERTE(m_nlists != nullptr); _ASSERTE(m_strtabAddress != 0); - for (int i = 0; i < m_dysymtabCommand->nextdefsym; i++) + // First, search just the "external" export symbols + if (TryLookupSymbol(m_dysymtabCommand->iextdefsym, m_dysymtabCommand->nextdefsym, symbolName, symbolValue)) { - std::string name = GetSymbolName(i); - // Skip the leading underscores to match Linux externs - if (name[0] == '_') - { - name.erase(0, 1); - } - if (strcmp(name.c_str(), symbolName) == 0) - { - *symbolValue = m_loadBias + m_nlists[i].n_value; - return true; - } + m_reader.Trace("SYM: Found '%s' in external symbols\n", symbolName); + return true; + } + m_reader.Trace("SYM: Missed '%s' in external symbols\n", symbolName); + + // If not found in external symbols, search all of them + if (TryLookupSymbol(0, m_symtabCommand->nsyms, symbolName, symbolValue)) + { + m_reader.Trace("SYM: Found '%s' in all symbols\n", symbolName); + return true; + } + m_reader.Trace("SYM: Missed '%s' in all symbols\n", symbolName); + } + *symbolValue = 0; + return false; +} + +bool +MachOModule::TryLookupSymbol(int start, int nsyms, const char* symbolName, uint64_t* symbolValue) +{ + for (int i = 0; i < nsyms; i++) + { + std::string name = GetSymbolName(start + i); + + // Skip the leading underscores to match Linux externs + const char* currentName = name.length() > 0 && name[0] == '_' ? name.c_str() + 1 : name.c_str(); + + // Does this symbol match? + if (strcmp(currentName, symbolName) == 0) + { + *symbolValue = m_loadBias + m_nlists[start + i].n_value; + return true; } } *symbolValue = 0; @@ -263,26 +285,30 @@ MachOModule::ReadSymbolTable() _ASSERTE(m_symtabCommand != nullptr); _ASSERTE(m_strtabAddress == 0); - m_reader.TraceVerbose("SYM: symoff %08x nsyms %d stroff %08x strsize %d iext %d next %d\n", + m_reader.TraceVerbose("SYM: symoff %08x nsyms %d stroff %08x strsize %d iext %d next %d iundef %d nundef %d extref %d nextref %d\n", m_symtabCommand->symoff, m_symtabCommand->nsyms, m_symtabCommand->stroff, m_symtabCommand->strsize, m_dysymtabCommand->iextdefsym, - m_dysymtabCommand->nextdefsym); - - // Read the external symbol part of symbol table. An array of "nlist" structs. - void* extSymbolTableAddress = (void*)(GetAddressFromFileOffset(m_symtabCommand->symoff) + (m_dysymtabCommand->iextdefsym * sizeof(nlist_64))); - size_t symtabSize = sizeof(nlist_64) * m_dysymtabCommand->nextdefsym; + m_dysymtabCommand->nextdefsym, + m_dysymtabCommand->iundefsym, + m_dysymtabCommand->nundefsym, + m_dysymtabCommand->extrefsymoff, + m_dysymtabCommand->nextrefsyms); + + // Read the entire symbol part of symbol table. An array of "nlist" structs. + void* symbolTableAddress = (void*)GetAddressFromFileOffset(m_symtabCommand->symoff); + size_t symtabSize = sizeof(nlist_64) * m_symtabCommand->nsyms; m_nlists = (nlist_64*)malloc(symtabSize); if (m_nlists == nullptr) { - m_reader.Trace("ERROR: Failed to allocate %zu byte external symbol table\n", symtabSize); + m_reader.Trace("ERROR: Failed to allocate %zu byte symtab\n", symtabSize); return false; } - if (!m_reader.ReadMemory(extSymbolTableAddress, m_nlists, symtabSize)) + if (!m_reader.ReadMemory(symbolTableAddress, m_nlists, symtabSize)) { - m_reader.Trace("ERROR: Failed to read external symtab at %p of %zu\n", extSymbolTableAddress, symtabSize); + m_reader.Trace("ERROR: Failed to read symtab at %p of %zu\n", symbolTableAddress, symtabSize); return false; } diff --git a/src/coreclr/debug/dbgutil/machoreader.h b/src/coreclr/debug/dbgutil/machoreader.h index bb9ffe0fdd7ceb..a5f458b17ff2d5 100644 --- a/src/coreclr/debug/dbgutil/machoreader.h +++ b/src/coreclr/debug/dbgutil/machoreader.h @@ -37,6 +37,7 @@ class MachOModule bool ReadHeader(); bool TryLookupSymbol(const char* symbolName, uint64_t* symbolValue); + bool TryLookupSymbol(int start, int nsyms, const char* symbolName, uint64_t* symbolValue); bool EnumerateSegments(); private: diff --git a/src/coreclr/dlls/mscordac/mscordac_unixexports.src b/src/coreclr/dlls/mscordac/mscordac_unixexports.src index 4fc0af3d682340..2a529b2f4a779d 100644 --- a/src/coreclr/dlls/mscordac/mscordac_unixexports.src +++ b/src/coreclr/dlls/mscordac/mscordac_unixexports.src @@ -23,6 +23,7 @@ nativeStringResourceTable_mscorrc #PAL_CatchHardwareExceptionHolderEnter #PAL_CatchHardwareExceptionHolderExit #PAL_bsearch +#PAL_CopyModuleData #PAL_errno #PAL_fflush #PAL__flushall @@ -142,6 +143,7 @@ nativeStringResourceTable_mscorrc #LocalAlloc #LocalFree #MapViewOfFile +#MapViewOfFileEx #MoveFileExW #MultiByteToWideChar #OpenProcess diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 939830dc32ddc3..ace66eac266709 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -2532,13 +2532,15 @@ uint8_t** gc_heap::background_mark_stack_array = 0; size_t gc_heap::background_mark_stack_array_length = 0; +BOOL gc_heap::processed_eph_overflow_p = FALSE; + +#ifdef USE_REGIONS +BOOL gc_heap::background_overflow_p = FALSE; +#else //USE_REGIONS uint8_t* gc_heap::background_min_overflow_address =0; uint8_t* gc_heap::background_max_overflow_address =0; -BOOL gc_heap::processed_eph_overflow_p = FALSE; - -#ifndef USE_REGIONS uint8_t* gc_heap::background_min_soh_overflow_address =0; uint8_t* gc_heap::background_max_soh_overflow_address =0; @@ -2548,7 +2550,7 @@ heap_segment* gc_heap::saved_overflow_ephemeral_seg = 0; heap_segment* gc_heap::saved_sweep_ephemeral_seg = 0; uint8_t* gc_heap::saved_sweep_ephemeral_start = 0; -#endif //!USE_REGIONS +#endif //USE_REGIONS Thread* gc_heap::bgc_thread = 0; @@ -23429,6 +23431,21 @@ void gc_heap::mark_object (uint8_t* o THREAD_NUMBER_DCL) #ifdef BACKGROUND_GC +#ifdef USE_REGIONS +void gc_heap::set_background_overflow_p (uint8_t* oo) +{ + heap_segment* overflow_region = get_region_info_for_address (oo); + overflow_region->flags |= heap_segment_flags_overflow; + dprintf (3,("setting overflow flag for region %p", heap_segment_mem (overflow_region))); +#ifdef MULTIPLE_HEAPS + gc_heap* overflow_heap = heap_segment_heap (overflow_region); +#else + gc_heap* overflow_heap = nullptr; +#endif + overflow_heap->background_overflow_p = TRUE; +} +#endif //USE_REGIONS + void gc_heap::background_mark_simple1 (uint8_t* oo THREAD_NUMBER_DCL) { uint8_t** mark_stack_limit = &background_mark_stack_array[background_mark_stack_array_length]; @@ -23495,9 +23512,13 @@ void gc_heap::background_mark_simple1 (uint8_t* oo THREAD_NUMBER_DCL) } else { - dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo)); + dprintf (3,("background mark stack overflow for object %Ix ", (size_t)oo)); +#ifdef USE_REGIONS + set_background_overflow_p (oo); +#else //USE_REGIONS background_min_overflow_address = min (background_min_overflow_address, oo); background_max_overflow_address = max (background_max_overflow_address, oo); +#endif //USE_REGIONS } } else @@ -23609,9 +23630,13 @@ void gc_heap::background_mark_simple1 (uint8_t* oo THREAD_NUMBER_DCL) } else { - dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo)); + dprintf (3,("background mark stack overflow for object %Ix ", (size_t)oo)); +#ifdef USE_REGIONS + set_background_overflow_p (oo); +#else //USE_REGIONS background_min_overflow_address = min (background_min_overflow_address, oo); background_max_overflow_address = max (background_max_overflow_address, oo); +#endif //USE_REGIONS } } } @@ -23887,13 +23912,13 @@ uint8_t* gc_heap::background_first_overflow (uint8_t* min_add, BOOL concurrent_p, BOOL small_object_p) { +#ifdef USE_REGIONS + return heap_segment_mem (seg); +#else uint8_t* o = 0; if (small_object_p) { -#ifdef USE_REGIONS - return find_first_object (min_add, heap_segment_mem (seg)); -#else if (in_range_for_segment (min_add, seg)) { // min_add was the beginning of gen1 when we did the concurrent @@ -23919,11 +23944,11 @@ uint8_t* gc_heap::background_first_overflow (uint8_t* min_add, } } } -#endif //USE_REGIONS } o = max (heap_segment_mem (seg), min_add); return o; +#endif //USE_REGIONS } void gc_heap::background_process_mark_overflow_internal (uint8_t* min_add, uint8_t* max_add, @@ -23948,7 +23973,9 @@ void gc_heap::background_process_mark_overflow_internal (uint8_t* min_add, uint8 exclusive_sync* loh_alloc_lock = 0; +#ifndef USE_REGIONS dprintf (2,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add)); +#endif #ifdef MULTIPLE_HEAPS // We don't have each heap scan all heaps concurrently because we are worried about // multiple threads calling things like find_first_object. @@ -23981,9 +24008,14 @@ void gc_heap::background_process_mark_overflow_internal (uint8_t* min_add, uint8 #ifdef USE_REGIONS if (heap_segment_overflow_p (seg)) { - assert (!concurrent_p); - current_min_add = max (heap_segment_mem (seg), min_add); - current_max_add = min (heap_segment_allocated (seg), max_add); + seg->flags &= ~heap_segment_flags_overflow; + current_min_add = heap_segment_mem (seg); + current_max_add = heap_segment_allocated (seg); + dprintf (2,("Processing Mark overflow [%Ix %Ix]", (size_t)current_min_add, (size_t)current_max_add)); + } + else + { + current_min_add = current_max_add = 0; } #endif //USE_REGIONS uint8_t* o = hp->background_first_overflow (current_min_add, seg, concurrent_p, small_object_segments); @@ -24034,15 +24066,19 @@ void gc_heap::background_process_mark_overflow_internal (uint8_t* min_add, uint8 } } - dprintf (2, ("went through overflow objects in segment %Ix (%d) (so far %Id marked)", - heap_segment_mem (seg), (small_object_segments ? 0 : 1), total_marked_objects)); - +#ifdef USE_REGIONS + if (current_max_add != 0) +#endif //USE_REGIONS + { + dprintf (2, ("went through overflow objects in segment %Ix (%d) (so far %Id marked)", + heap_segment_mem (seg), (small_object_segments ? 0 : 1), total_marked_objects)); + } #ifndef USE_REGIONS if (concurrent_p && (seg == hp->saved_overflow_ephemeral_seg)) { break; } -#endif //USE_REGIONS +#endif //!USE_REGIONS seg = heap_segment_next_in_range (seg); } @@ -24071,36 +24107,18 @@ BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p) if (concurrent_p) { assert (!processed_eph_overflow_p); - +#ifndef USE_REGIONS if ((background_max_overflow_address != 0) && (background_min_overflow_address != MAX_PTR)) { -#ifdef USE_REGIONS - // We don't want to step into the ephemeral regions so remember these regions and - // be sure to process them later. An FGC cannot happen while we are going through - // the region lists. - for (int i = 0; i < max_generation; i++) - { - heap_segment* region = generation_start_segment (generation_of (i)); - while (region) - { - if ((heap_segment_mem (region) <= background_max_overflow_address) && - (heap_segment_allocated (region) >= background_min_overflow_address)) - { - region->flags |= heap_segment_flags_overflow; - } - region = heap_segment_next (region); - } - } -#else //USE_REGIONS // We have overflow to process but we know we can't process the ephemeral generations // now (we actually could process till the current gen1 start but since we are going to // make overflow per segment, for now I'll just stop at the saved gen1 start. saved_overflow_ephemeral_seg = ephemeral_heap_segment; background_max_soh_overflow_address = heap_segment_reserved (saved_overflow_ephemeral_seg); background_min_soh_overflow_address = generation_allocation_start (generation_of (max_generation - 1)); -#endif //USE_REGIONS } +#endif //!USE_REGIONS } else { @@ -24114,12 +24132,18 @@ BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p) { // if there was no more overflow we just need to process what we didn't process // on the saved ephemeral segment. +#ifdef USE_REGIONS + if (!background_overflow_p) +#else if ((background_max_overflow_address == 0) && (background_min_overflow_address == MAX_PTR)) +#endif //USE_REGIONS { dprintf (2, ("final processing mark overflow - no more overflow since last time")); grow_mark_array_p = FALSE; } -#ifndef USE_REGIONS +#ifdef USE_REGIONS + background_overflow_p = TRUE; +#else background_min_overflow_address = min (background_min_overflow_address, background_min_soh_overflow_address); background_max_overflow_address = max (background_max_overflow_address, @@ -24131,8 +24155,12 @@ BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p) BOOL overflow_p = FALSE; recheck: +#ifdef USE_REGIONS + if (background_overflow_p) +#else if ((! ((background_max_overflow_address == 0)) || ! ((background_min_overflow_address == MAX_PTR)))) +#endif { overflow_p = TRUE; @@ -24155,11 +24183,17 @@ BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p) grow_mark_array_p = TRUE; } +#ifdef USE_REGIONS + uint8_t* min_add = 0; + uint8_t* max_add = 0; + background_overflow_p = FALSE; +#else uint8_t* min_add = background_min_overflow_address; uint8_t* max_add = background_max_overflow_address; background_max_overflow_address = 0; background_min_overflow_address = MAX_PTR; +#endif background_process_mark_overflow_internal (min_add, max_add, concurrent_p); if (!concurrent_p) @@ -32375,6 +32409,7 @@ void gc_heap::background_scan_dependent_handles (ScanContext *sc) if (!s_fScanRequired) { +#ifndef USE_REGIONS uint8_t* all_heaps_max = 0; uint8_t* all_heaps_min = MAX_PTR; int i; @@ -32390,6 +32425,7 @@ void gc_heap::background_scan_dependent_handles (ScanContext *sc) g_heaps[i]->background_max_overflow_address = all_heaps_max; g_heaps[i]->background_min_overflow_address = all_heaps_min; } +#endif //!USE_REGIONS } dprintf(2, ("Starting all gc thread mark stack overflow processing")); @@ -32920,12 +32956,14 @@ void gc_heap::background_mark_phase () bpromoted_bytes (heap_number) = 0; static uint32_t num_sizedrefs = 0; +#ifdef USE_REGIONS + background_overflow_p = FALSE; +#else background_min_overflow_address = MAX_PTR; background_max_overflow_address = 0; -#ifndef USE_REGIONS background_min_soh_overflow_address = MAX_PTR; background_max_soh_overflow_address = 0; -#endif //!USE_REGIONS +#endif //USE_REGIONS processed_eph_overflow_p = FALSE; //set up the mark lists from g_mark_list @@ -33125,7 +33163,7 @@ void gc_heap::background_mark_phase () enable_preemptive (); -#ifdef MULTIPLE_HEAPS +#if defined(MULTIPLE_HEAPS) && !defined(USE_REGIONS) bgc_t_join.join(this, gc_join_concurrent_overflow); if (bgc_t_join.joined()) { @@ -33142,6 +33180,7 @@ void gc_heap::background_mark_phase () all_heaps_max = g_heaps[i]->background_max_overflow_address; if (all_heaps_min > g_heaps[i]->background_min_overflow_address) all_heaps_min = g_heaps[i]->background_min_overflow_address; + } for (i = 0; i < n_heaps; i++) { @@ -33151,7 +33190,7 @@ void gc_heap::background_mark_phase () dprintf(3, ("Starting all bgc threads after updating the overflow info")); bgc_t_join.restart(); } -#endif //MULTIPLE_HEAPS +#endif //MULTIPLE_HEAPS && !USE_REGIONS disable_preemptive (true); diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index 136db1ae8c1b95..081b9a539c073c 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -2466,6 +2466,10 @@ class gc_heap void verify_mark_bits_cleared (uint8_t* obj, size_t s); PER_HEAP void clear_all_mark_array(); +#ifdef USE_REGIONS + PER_HEAP + void set_background_overflow_p (uint8_t* oo); +#endif #ifdef BGC_SERVO_TUNING @@ -4282,18 +4286,21 @@ class gc_heap PER_HEAP size_t background_mark_stack_array_length; + // We can't process the ephemeral range concurrently so we + // wait till final mark to process it. PER_HEAP - uint8_t* background_min_overflow_address; + BOOL processed_eph_overflow_p; +#ifdef USE_REGIONS PER_HEAP - uint8_t* background_max_overflow_address; + BOOL background_overflow_p; +#else + PER_HEAP + uint8_t* background_min_overflow_address; - // We can't process the ephemeral range concurrently so we - // wait till final mark to process it. PER_HEAP - BOOL processed_eph_overflow_p; + uint8_t* background_max_overflow_address; -#ifndef USE_REGIONS PER_HEAP uint8_t* background_min_soh_overflow_address; diff --git a/src/coreclr/hosts/corerun/CMakeLists.txt b/src/coreclr/hosts/corerun/CMakeLists.txt index ef20f57e0bb8d8..b95f4c87bd878a 100644 --- a/src/coreclr/hosts/corerun/CMakeLists.txt +++ b/src/coreclr/hosts/corerun/CMakeLists.txt @@ -32,4 +32,4 @@ else(CLR_CMAKE_HOST_WIN32) endif() endif(CLR_CMAKE_HOST_WIN32) -install_clr(TARGETS corerun DESTINATIONS . COMPONENT runtime) +install_clr(TARGETS corerun DESTINATIONS . COMPONENT hosts) diff --git a/src/coreclr/hosts/coreshim/CMakeLists.txt b/src/coreclr/hosts/coreshim/CMakeLists.txt index 5c6a2fd28899d8..e25caf7dfa5d44 100644 --- a/src/coreclr/hosts/coreshim/CMakeLists.txt +++ b/src/coreclr/hosts/coreshim/CMakeLists.txt @@ -22,4 +22,4 @@ target_link_libraries(CoreShim ${STATIC_MT_VCRT_LIB} ) -install_clr(TARGETS CoreShim DESTINATIONS . COMPONENT runtime) +install_clr(TARGETS CoreShim DESTINATIONS . COMPONENT hosts) diff --git a/src/coreclr/inc/cor.h b/src/coreclr/inc/cor.h index ee82e433695ebe..8ccc151cc0378e 100644 --- a/src/coreclr/inc/cor.h +++ b/src/coreclr/inc/cor.h @@ -1942,7 +1942,7 @@ inline ULONG CorSigUncompressData( // return number of bytes of that compre ULONG dwSizeOfData = 0; // We don't know how big the signature is, so we'll just say that it's big enough - if (FAILED(CorSigUncompressData(pData, 0xff, pDataOut, &dwSizeOfData))) + if (FAILED(CorSigUncompressData(pData, 0xff, reinterpret_cast(pDataOut), reinterpret_cast(&dwSizeOfData)))) { *pDataOut = 0; return (ULONG)-1; diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 4cc0c44b83c1df..ae24d18c2c9667 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -885,11 +885,6 @@ enum CorInfoIntrinsics CORINFO_INTRINSIC_Array_Get, // Get the value of an element in an array CORINFO_INTRINSIC_Array_Address, // Get the address of an element in an array CORINFO_INTRINSIC_Array_Set, // Set the value of an element in an array - CORINFO_INTRINSIC_RTH_GetValueInternal, - CORINFO_INTRINSIC_Object_GetType, - CORINFO_INTRINSIC_StubHelpers_GetStubContext, - CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr, - CORINFO_INTRINSIC_StubHelpers_NextCallReturnAddress, CORINFO_INTRINSIC_ByReference_Ctor, CORINFO_INTRINSIC_ByReference_Value, @@ -2929,6 +2924,7 @@ class ICorDynamicInfo : public ICorStaticInfo // guaranteed to be multi callable entrypoint. virtual void getFunctionFixedEntryPoint( CORINFO_METHOD_HANDLE ftn, + bool isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP * pResult) = 0; // get the synchronization handle that is passed to monXstatic function diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index da724e75da6073..d912b90b8612b1 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -502,6 +502,7 @@ void getFunctionEntryPoint( void getFunctionFixedEntryPoint( CORINFO_METHOD_HANDLE ftn, + bool isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP* pResult) override; void* getMethodSync( diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index b51f56b2df5c78..21679993f73a60 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 0c6f2d8d-f1b7-4c28-bbe8-36c8f6b35fbf */ - 0xc6f2d8d, - 0xf1b7, - 0x4c28, - { 0xbb, 0xe8, 0x36, 0xc8, 0xf6, 0xb3, 0x5f, 0xbf} +constexpr GUID JITEEVersionIdentifier = { /* 3d9496d4-03f7-4eb0-bf7a-a88794e74537 */ + 0x3d9496d4, + 0x03f7, + 0x4eb0, + {0xbf, 0x7a, 0xa8, 0x87, 0x94, 0xe7, 0x45, 0x37} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/inc/stresslog.h b/src/coreclr/inc/stresslog.h index 508bc91edb72a0..4ee3acaeb8bb6c 100644 --- a/src/coreclr/inc/stresslog.h +++ b/src/coreclr/inc/stresslog.h @@ -45,6 +45,8 @@ #ifndef _ASSERTE #define _ASSERTE(expr) #endif +#else +#include // offsetof #endif // STRESS_LOG_ANALYZER /* The STRESS_LOG* macros work like printf. In fact the use printf in their implementation @@ -322,11 +324,39 @@ class StressLog { static const size_t MAX_MODULES = 5; ModuleDesc modules[MAX_MODULES]; // descriptor of the modules images -#if defined(HOST_WINDOWS) && defined(HOST_64BIT) +#if defined(HOST_64BIT) #define MEMORY_MAPPED_STRESSLOG +#ifdef HOST_WINDOWS +#define MEMORY_MAPPED_STRESSLOG_BASE_ADDRESS (void*)0x400000000000 +#else +#define MEMORY_MAPPED_STRESSLOG_BASE_ADDRESS nullptr +#endif +#endif + +#ifdef STRESS_LOG_ANALYZER + static size_t writing_base_address; + static size_t reading_base_address; + + template + static T* TranslateMemoryMappedPointer(T* input) + { + if (input == nullptr) + { + return nullptr; + } + + return ((T*)(((uint8_t*)input) - writing_base_address + reading_base_address)); + } +#else + template + static T* TranslateMemoryMappedPointer(T* input) + { + return input; + } #endif #ifdef MEMORY_MAPPED_STRESSLOG + MapViewHolder hMapView; static void* AllocMemoryMapped(size_t n); @@ -547,8 +577,14 @@ struct StressLogChunk DWORD dwSig2; #if !defined(STRESS_LOG_READONLY) + +#ifdef MEMORY_MAPPED_STRESSLOG + static bool s_memoryMapped; +#endif //MEMORY_MAPPED_STRESSLOG + #ifdef HOST_WINDOWS static HANDLE s_LogChunkHeap; +#endif //HOST_WINDOWS void * operator new (size_t size) throw() { @@ -556,43 +592,32 @@ struct StressLogChunk { return NULL; } - - if (s_LogChunkHeap != NULL) - { - //no need to zero memory because we could handle garbage contents - return HeapAlloc(s_LogChunkHeap, 0, size); - } - else - { #ifdef MEMORY_MAPPED_STRESSLOG + if (s_memoryMapped) return StressLog::AllocMemoryMapped(size); -#else - return nullptr; #endif //MEMORY_MAPPED_STRESSLOG - } - } - - void operator delete (void * chunk) - { - if (s_LogChunkHeap != NULL) - HeapFree (s_LogChunkHeap, 0, chunk); - } +#ifdef HOST_WINDOWS + _ASSERTE(s_LogChunkHeap); + return HeapAlloc(s_LogChunkHeap, 0, size); #else - void* operator new (size_t size) throw() - { - if (IsInCantAllocStressLogRegion()) - { - return NULL; - } - return malloc(size); +#endif //HOST_WINDOWS } - void operator delete (void* chunk) + void operator delete (void * chunk) { +#ifdef MEMORY_MAPPED_STRESSLOG + if (s_memoryMapped) + return; +#endif //MEMORY_MAPPED_STRESSLOG +#ifdef HOST_WINDOWS + _ASSERTE(s_LogChunkHeap); + HeapFree (s_LogChunkHeap, 0, chunk); +#else free(chunk); +#endif //HOST_WINDOWS } -#endif + #endif //!STRESS_LOG_READONLY StressLogChunk (StressLogChunk * p = NULL, StressLogChunk * n = NULL) @@ -772,7 +797,7 @@ class ThreadStressLog { BOOL IsValid () const { - return chunkListHead != NULL && (!curWriteChunk || curWriteChunk->IsValid ()); + return chunkListHead != NULL && (!curWriteChunk || StressLog::TranslateMemoryMappedPointer(curWriteChunk)->IsValid ()); } #ifdef STRESS_LOG_READONLY diff --git a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp index 9f51c43b765ee0..031d2ba75b505f 100644 --- a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp +++ b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp @@ -1205,10 +1205,11 @@ void WrapICorJitInfo::getFunctionEntryPoint( void WrapICorJitInfo::getFunctionFixedEntryPoint( CORINFO_METHOD_HANDLE ftn, + bool isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP* pResult) { API_ENTER(getFunctionFixedEntryPoint); - wrapHnd->getFunctionFixedEntryPoint(ftn, pResult); + wrapHnd->getFunctionFixedEntryPoint(ftn, isUnsafeFunctionPointer, pResult); API_LEAVE(getFunctionFixedEntryPoint); } diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 87eb4923ff743a..78c76a805ab3cd 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -1562,6 +1562,10 @@ class BasicBlockSimpleList // and must be non-null. E.g., // for (BasicBlock* const block : BasicBlockRangeList(startBlock, endBlock)) ... // +// Note that endBlock->bbNext is captured at the beginning of the iteration. Thus, any blocks +// inserted before that will continue the iteration. In particular, inserting blocks between endBlock +// and endBlock->bbNext will yield unexpected results, as the iteration will continue longer than desired. +// class BasicBlockRangeList { BasicBlock* m_begin; diff --git a/src/coreclr/jit/clrjit.natvis b/src/coreclr/jit/clrjit.natvis index 1a62108f08267a..aa5fb650c28de4 100644 --- a/src/coreclr/jit/clrjit.natvis +++ b/src/coreclr/jit/clrjit.natvis @@ -25,6 +25,12 @@ Documentation for VS debugger format specifiers: https://docs.microsoft.com/en-u BB{bbNum,d}; {bbJumpKind,en} + + REMOVED + BB{lpTop->bbNum,d}-BB{lpBottom->bbNum,d} pre-h:BB{lpHead->bbNum,d} e:BB{lpEntry->bbNum,d} {lpFlags,en} + BB{lpTop->bbNum,d}-BB{lpBottom->bbNum,d} h:BB{lpHead->bbNum,d} e:BB{lpEntry->bbNum,d} {lpFlags,en} + + type={ebdHandlerType} diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 785ae4f96647be..19a24869146225 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -3838,7 +3838,7 @@ void CodeGen::genPushCalleeSavedRegisters() // The amount to subtract from SP before starting to store the callee-saved registers. It might be folded into the // first save instruction as a "predecrement" amount, if possible. - int calleeSaveSPDelta = 0; + int calleeSaveSpDelta = 0; if (isFramePointerUsed()) { @@ -3920,7 +3920,7 @@ void CodeGen::genPushCalleeSavedRegisters() // separate SUB instruction or the SP adjustment might be folded in to the first STP if there is // no outgoing argument space AND no local frame space, that is, if the only thing the frame does // is save callee-saved registers (and possibly varargs argument registers). - calleeSaveSPDelta = totalFrameSize; + calleeSaveSpDelta = totalFrameSize; offset = (int)compiler->compLclFrameSize; } @@ -4015,7 +4015,7 @@ void CodeGen::genPushCalleeSavedRegisters() // slots. In fact, we are not; any empty alignment slots were calculated in // Compiler::lvaAssignFrameOffsets() and its callees. - int calleeSaveSPDeltaUnaligned = totalFrameSize - compiler->compLclFrameSize; + int calleeSaveSpDeltaUnaligned = totalFrameSize - compiler->compLclFrameSize; if (genSaveFpLrWithAllCalleeSavedRegisters) { JITDUMP("Frame type 5 (save FP/LR at top). #outsz=%d; #framesz=%d; LclFrameSize=%d\n", @@ -4035,19 +4035,19 @@ void CodeGen::genPushCalleeSavedRegisters() frameType = 3; - calleeSaveSPDeltaUnaligned -= 2 * REGSIZE_BYTES; // 2 for FP, LR which we'll save later. + calleeSaveSpDeltaUnaligned -= 2 * REGSIZE_BYTES; // 2 for FP, LR which we'll save later. // We'll take care of these later, but callee-saved regs code shouldn't see them. maskSaveRegsInt &= ~(RBM_FP | RBM_LR); } - assert(calleeSaveSPDeltaUnaligned >= 0); - assert((calleeSaveSPDeltaUnaligned % 8) == 0); // It better at least be 8 byte aligned. - calleeSaveSPDelta = AlignUp((UINT)calleeSaveSPDeltaUnaligned, STACK_ALIGN); + assert(calleeSaveSpDeltaUnaligned >= 0); + assert((calleeSaveSpDeltaUnaligned % 8) == 0); // It better at least be 8 byte aligned. + calleeSaveSpDelta = AlignUp((UINT)calleeSaveSpDeltaUnaligned, STACK_ALIGN); - offset = calleeSaveSPDelta - calleeSaveSPDeltaUnaligned; + offset = calleeSaveSpDelta - calleeSaveSpDeltaUnaligned; - JITDUMP(" calleeSaveSPDelta=%d, offset=%d\n", calleeSaveSPDelta, offset); + JITDUMP(" calleeSaveSpDelta=%d, offset=%d\n", calleeSaveSpDelta, offset); // At most one alignment slot between SP and where we store the callee-saved registers. assert((offset == 0) || (offset == REGSIZE_BYTES)); @@ -4068,8 +4068,10 @@ void CodeGen::genPushCalleeSavedRegisters() assert(frameType != 0); - JITDUMP(" offset=%d, calleeSaveSPDelta=%d\n", offset, calleeSaveSPDelta); - genSaveCalleeSavedRegistersHelp(maskSaveRegsInt | maskSaveRegsFloat, offset, -calleeSaveSPDelta); + const int calleeSaveSpOffset = offset; + + JITDUMP(" offset=%d, calleeSaveSpDelta=%d\n", offset, calleeSaveSpDelta); + genSaveCalleeSavedRegistersHelp(maskSaveRegsInt | maskSaveRegsFloat, offset, -calleeSaveSpDelta); offset += genCountBits(maskSaveRegsInt | maskSaveRegsFloat) * REGSIZE_BYTES; @@ -4114,10 +4116,10 @@ void CodeGen::genPushCalleeSavedRegisters() { assert(!genSaveFpLrWithAllCalleeSavedRegisters); - int remainingFrameSz = totalFrameSize - calleeSaveSPDelta; + int remainingFrameSz = totalFrameSize - calleeSaveSpDelta; assert(remainingFrameSz > 0); assert((remainingFrameSz % 16) == 0); // this is guaranteed to be 16-byte aligned because each component -- - // totalFrameSize and calleeSaveSPDelta -- is 16-byte aligned. + // totalFrameSize and calleeSaveSpDelta -- is 16-byte aligned. if (compiler->lvaOutgoingArgSpaceSize > 504) { @@ -4166,14 +4168,14 @@ void CodeGen::genPushCalleeSavedRegisters() else if (frameType == 4) { assert(genSaveFpLrWithAllCalleeSavedRegisters); - offsetSpToSavedFp = calleeSaveSPDelta - (compiler->info.compIsVarArgs ? MAX_REG_ARG * REGSIZE_BYTES : 0) - + offsetSpToSavedFp = calleeSaveSpDelta - (compiler->info.compIsVarArgs ? MAX_REG_ARG * REGSIZE_BYTES : 0) - 2 * REGSIZE_BYTES; // -2 for FP, LR } else if (frameType == 5) { assert(genSaveFpLrWithAllCalleeSavedRegisters); - offsetSpToSavedFp = calleeSaveSPDelta - (compiler->info.compIsVarArgs ? MAX_REG_ARG * REGSIZE_BYTES : 0) - + offsetSpToSavedFp = calleeSaveSpDelta - (compiler->info.compIsVarArgs ? MAX_REG_ARG * REGSIZE_BYTES : 0) - 2 * REGSIZE_BYTES; // -2 for FP, LR JITDUMP(" offsetSpToSavedFp=%d\n", offsetSpToSavedFp); genEstablishFramePointer(offsetSpToSavedFp, /* reportUnwindData */ true); @@ -4181,10 +4183,10 @@ void CodeGen::genPushCalleeSavedRegisters() // We just established the frame pointer chain; don't do it again. establishFramePointer = false; - int remainingFrameSz = totalFrameSize - calleeSaveSPDelta; + int remainingFrameSz = totalFrameSize - calleeSaveSpDelta; assert(remainingFrameSz > 0); assert((remainingFrameSz % 16) == 0); // this is guaranteed to be 16-byte aligned because each component -- - // totalFrameSize and calleeSaveSPDelta -- is 16-byte aligned. + // totalFrameSize and calleeSaveSpDelta -- is 16-byte aligned. JITDUMP(" remainingFrameSz=%d\n", remainingFrameSz); @@ -4204,6 +4206,13 @@ void CodeGen::genPushCalleeSavedRegisters() } assert(offset == totalFrameSize); + + // Save off information about the frame for later use + // + compiler->compFrameInfo.frameType = frameType; + compiler->compFrameInfo.calleeSaveSpOffset = calleeSaveSpOffset; + compiler->compFrameInfo.calleeSaveSpDelta = calleeSaveSpDelta; + compiler->compFrameInfo.offsetSpToSavedFp = offsetSpToSavedFp; #endif // TARGET_ARM64 } diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index ef0c34ec5a01d2..237a22c6782f0f 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -5194,22 +5194,22 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog) regMaskTP regsToRestoreMask = rsRestoreRegs; - int totalFrameSize = genTotalFrameSize(); + const int totalFrameSize = genTotalFrameSize(); - int calleeSaveSPOffset = 0; // This will be the starting place for restoring the callee-saved registers, in - // decreasing order. - int frameType = 0; // An indicator of what type of frame we are popping. - int calleeSaveSPDelta = 0; // Amount to add to SP after callee-saved registers have been restored. + // Fetch info about the frame we saved when creating the prolog. + // + const int frameType = compiler->compFrameInfo.frameType; + const int calleeSaveSpOffset = compiler->compFrameInfo.calleeSaveSpOffset; + const int calleeSaveSpDelta = compiler->compFrameInfo.calleeSaveSpDelta; + const int offsetSpToSavedFp = compiler->compFrameInfo.offsetSpToSavedFp; - if (isFramePointerUsed()) + switch (frameType) { - if ((compiler->lvaOutgoingArgSpaceSize == 0) && (totalFrameSize <= 504) && - !genSaveFpLrWithAllCalleeSavedRegisters) + case 1: { JITDUMP("Frame type 1. #outsz=0; #framesz=%d; localloc? %s\n", totalFrameSize, dspBool(compiler->compLocallocUsed)); - frameType = 1; if (compiler->compLocallocUsed) { // Restore sp from fp @@ -5219,13 +5219,16 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog) } regsToRestoreMask &= ~(RBM_FP | RBM_LR); // We'll restore FP/LR at the end, and post-index SP. - - // Compute callee save SP offset which is at the top of local frame while the FP/LR is saved at the - // bottom of stack. - calleeSaveSPOffset = compiler->compLclFrameSize + 2 * REGSIZE_BYTES; + break; } - else if (totalFrameSize <= 512) + + case 2: { + JITDUMP("Frame type 2 (save FP/LR at bottom). #outsz=%d; #framesz=%d; localloc? %s\n", + unsigned(compiler->lvaOutgoingArgSpaceSize), totalFrameSize, dspBool(compiler->compLocallocUsed)); + + assert(!genSaveFpLrWithAllCalleeSavedRegisters); + if (compiler->compLocallocUsed) { // Restore sp from fp @@ -5235,52 +5238,22 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog) compiler->unwindSetFrameReg(REG_FPBASE, SPtoFPdelta); } - if (genSaveFpLrWithAllCalleeSavedRegisters) - { - JITDUMP("Frame type 4 (save FP/LR at top). #outsz=%d; #framesz=%d; localloc? %s\n", - unsigned(compiler->lvaOutgoingArgSpaceSize), totalFrameSize, - dspBool(compiler->compLocallocUsed)); - - frameType = 4; - - calleeSaveSPOffset = compiler->compLclFrameSize; - - // Remove the frame after we're done restoring the callee-saved registers. - calleeSaveSPDelta = totalFrameSize; - } - else - { - JITDUMP("Frame type 2 (save FP/LR at bottom). #outsz=%d; #framesz=%d; localloc? %s\n", - unsigned(compiler->lvaOutgoingArgSpaceSize), totalFrameSize, - dspBool(compiler->compLocallocUsed)); - - frameType = 2; - - regsToRestoreMask &= ~(RBM_FP | RBM_LR); // We'll restore FP/LR at the end, and post-index SP. - - // Compute callee save SP offset which is at the top of local frame while the FP/LR is saved at the - // bottom of stack. - calleeSaveSPOffset = compiler->compLclFrameSize + 2 * REGSIZE_BYTES; - } + regsToRestoreMask &= ~(RBM_FP | RBM_LR); // We'll restore FP/LR at the end, and post-index SP. + break; } - else if (!genSaveFpLrWithAllCalleeSavedRegisters) + + case 3: { JITDUMP("Frame type 3 (save FP/LR at bottom). #outsz=%d; #framesz=%d; localloc? %s\n", unsigned(compiler->lvaOutgoingArgSpaceSize), totalFrameSize, dspBool(compiler->compLocallocUsed)); - frameType = 3; + assert(!genSaveFpLrWithAllCalleeSavedRegisters); - int calleeSaveSPDeltaUnaligned = totalFrameSize - compiler->compLclFrameSize - - 2 * REGSIZE_BYTES; // 2 for FP, LR which we'll restore later. - assert(calleeSaveSPDeltaUnaligned >= 0); - assert((calleeSaveSPDeltaUnaligned % 8) == 0); // It better at least be 8 byte aligned. - calleeSaveSPDelta = AlignUp((UINT)calleeSaveSPDeltaUnaligned, STACK_ALIGN); - - JITDUMP(" calleeSaveSPDelta=%d\n", calleeSaveSPDelta); + JITDUMP(" calleeSaveSpDelta=%d\n", calleeSaveSpDelta); regsToRestoreMask &= ~(RBM_FP | RBM_LR); // We'll restore FP/LR at the end, and (hopefully) post-index SP. - int remainingFrameSz = totalFrameSize - calleeSaveSPDelta; + int remainingFrameSz = totalFrameSize - calleeSaveSpDelta; assert(remainingFrameSz > 0); if (compiler->lvaOutgoingArgSpaceSize > 504) @@ -5332,83 +5305,91 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog) // Unlike frameType=1 or frameType=2 that restore SP at the end, // frameType=3 already adjusted SP above to delete local frame. // There is at most one alignment slot between SP and where we store the callee-saved registers. - calleeSaveSPOffset = calleeSaveSPDelta - calleeSaveSPDeltaUnaligned; - assert((calleeSaveSPOffset == 0) || (calleeSaveSPOffset == REGSIZE_BYTES)); + assert((calleeSaveSpOffset == 0) || (calleeSaveSpOffset == REGSIZE_BYTES)); + + break; } - else + + case 4: { - JITDUMP("Frame type 5 (save FP/LR at top). #outsz=%d; #framesz=%d; localloc? %s\n", + JITDUMP("Frame type 4 (save FP/LR at top). #outsz=%d; #framesz=%d; localloc? %s\n", unsigned(compiler->lvaOutgoingArgSpaceSize), totalFrameSize, dspBool(compiler->compLocallocUsed)); - frameType = 5; + assert(genSaveFpLrWithAllCalleeSavedRegisters); + + if (compiler->compLocallocUsed) + { + // Restore sp from fp + // sub sp, fp, #outsz // Uses #outsz if FP/LR stored at bottom + int SPtoFPdelta = genSPtoFPdelta(); + GetEmitter()->emitIns_R_R_I(INS_sub, EA_PTRSIZE, REG_SPBASE, REG_FPBASE, SPtoFPdelta); + compiler->unwindSetFrameReg(REG_FPBASE, SPtoFPdelta); + } + break; + } - int calleeSaveSPDeltaUnaligned = totalFrameSize - compiler->compLclFrameSize; - assert(calleeSaveSPDeltaUnaligned >= 0); - assert((calleeSaveSPDeltaUnaligned % 8) == 0); // It better at least be 8 byte aligned. - calleeSaveSPDelta = AlignUp((UINT)calleeSaveSPDeltaUnaligned, STACK_ALIGN); + case 5: + { + JITDUMP("Frame type 5 (save FP/LR at top). #outsz=%d; #framesz=%d; localloc? %s\n", + unsigned(compiler->lvaOutgoingArgSpaceSize), totalFrameSize, dspBool(compiler->compLocallocUsed)); - calleeSaveSPOffset = calleeSaveSPDelta - calleeSaveSPDeltaUnaligned; - assert((calleeSaveSPOffset == 0) || (calleeSaveSPOffset == REGSIZE_BYTES)); + assert((calleeSaveSpOffset == 0) || (calleeSaveSpOffset == REGSIZE_BYTES)); // Restore sp from fp: // sub sp, fp, #sp-to-fp-delta // This is the same whether there is localloc or not. Note that we don't need to do anything to remove the // "remainingFrameSz" to reverse the SUB of that amount in the prolog. - - int offsetSpToSavedFp = calleeSaveSPDelta - - (compiler->info.compIsVarArgs ? MAX_REG_ARG * REGSIZE_BYTES : 0) - - 2 * REGSIZE_BYTES; // -2 for FP, LR GetEmitter()->emitIns_R_R_I(INS_sub, EA_PTRSIZE, REG_SPBASE, REG_FPBASE, offsetSpToSavedFp); compiler->unwindSetFrameReg(REG_FPBASE, offsetSpToSavedFp); + break; } - } - else - { - // No frame pointer (no chaining). - NYI("Frame without frame pointer"); - calleeSaveSPOffset = 0; + + default: + unreached(); } - JITDUMP(" calleeSaveSPOffset=%d, calleeSaveSPDelta=%d\n", calleeSaveSPOffset, calleeSaveSPDelta); - genRestoreCalleeSavedRegistersHelp(regsToRestoreMask, calleeSaveSPOffset, calleeSaveSPDelta); + JITDUMP(" calleeSaveSpOffset=%d, calleeSaveSpDelta=%d\n", calleeSaveSpOffset, calleeSaveSpDelta); + genRestoreCalleeSavedRegistersHelp(regsToRestoreMask, calleeSaveSpOffset, calleeSaveSpDelta); - if (frameType == 1) + switch (frameType) { - // Generate: - // ldp fp,lr,[sp],#framesz + case 1: + { + // Generate: + // ldp fp,lr,[sp],#framesz - GetEmitter()->emitIns_R_R_R_I(INS_ldp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, totalFrameSize, - INS_OPTS_POST_INDEX); - compiler->unwindSaveRegPairPreindexed(REG_FP, REG_LR, -totalFrameSize); - } - else if (frameType == 2) - { - // Generate: - // ldr fp,lr,[sp,#outsz] - // add sp,sp,#framesz + GetEmitter()->emitIns_R_R_R_I(INS_ldp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, totalFrameSize, + INS_OPTS_POST_INDEX); + compiler->unwindSaveRegPairPreindexed(REG_FP, REG_LR, -totalFrameSize); + break; + } - GetEmitter()->emitIns_R_R_R_I(INS_ldp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, - compiler->lvaOutgoingArgSpaceSize); - compiler->unwindSaveRegPair(REG_FP, REG_LR, compiler->lvaOutgoingArgSpaceSize); + case 2: + { + // Generate: + // ldr fp,lr,[sp,#outsz] + // add sp,sp,#framesz - GetEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, totalFrameSize); - compiler->unwindAllocStack(totalFrameSize); - } - else if (frameType == 3) - { - // Nothing to do after restoring callee-saved registers. - } - else if (frameType == 4) - { - // Nothing to do after restoring callee-saved registers. - } - else if (frameType == 5) - { - // Nothing to do after restoring callee-saved registers. - } - else - { - unreached(); + GetEmitter()->emitIns_R_R_R_I(INS_ldp, EA_PTRSIZE, REG_FP, REG_LR, REG_SPBASE, + compiler->lvaOutgoingArgSpaceSize); + compiler->unwindSaveRegPair(REG_FP, REG_LR, compiler->lvaOutgoingArgSpaceSize); + + GetEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, totalFrameSize); + compiler->unwindAllocStack(totalFrameSize); + break; + } + case 3: + case 4: + case 5: + { + // Nothing to do after restoring callee-saved registers. + break; + } + + default: + { + unreached(); + } } } diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index e56c96b026c0b1..fcb6f12e3d7fe1 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -5245,26 +5245,42 @@ void Compiler::placeLoopAlignInstructions() JITDUMP("Inside placeLoopAlignInstructions for %d loops.\n", loopAlignCandidates); // Add align only if there were any loops that needed alignment - weight_t minBlockSoFar = BB_MAX_WEIGHT; - BasicBlock* bbHavingAlign = nullptr; + weight_t minBlockSoFar = BB_MAX_WEIGHT; + BasicBlock* bbHavingAlign = nullptr; + BasicBlock::loopNumber currentAlignedLoopNum = BasicBlock::NOT_IN_LOOP; + + if ((fgFirstBB != nullptr) && fgFirstBB->isLoopAlign()) + { + // Adding align instruction in prolog is not supported + // hence just remove that loop from our list. + loopsToProcess--; + } + for (BasicBlock* const block : Blocks()) { - if ((block == fgFirstBB) && block->isLoopAlign()) + if (currentAlignedLoopNum != BasicBlock::NOT_IN_LOOP) { - // Adding align instruction in prolog is not supported - // hence skip the align block if it is the first block. - loopsToProcess--; - continue; + // We've been processing blocks within an aligned loop. Are we out of that loop now? + if (currentAlignedLoopNum != block->bbNatLoopNum) + { + currentAlignedLoopNum = BasicBlock::NOT_IN_LOOP; + } } - // If there is a unconditional jump - if (opts.compJitHideAlignBehindJmp && (block->bbJumpKind == BBJ_ALWAYS)) + // If there is a unconditional jump (which is not part of callf/always pair) + if (opts.compJitHideAlignBehindJmp && (block->bbJumpKind == BBJ_ALWAYS) && !block->isBBCallAlwaysPairTail()) { + // Track the lower weight blocks if (block->bbWeight < minBlockSoFar) { - minBlockSoFar = block->bbWeight; - bbHavingAlign = block; - JITDUMP(FMT_BB ", bbWeight=" FMT_WT " ends with unconditional 'jmp' \n", block->bbNum, block->bbWeight); + if (currentAlignedLoopNum == BasicBlock::NOT_IN_LOOP) + { + // Ok to insert align instruction in this block because it is not part of any aligned loop. + minBlockSoFar = block->bbWeight; + bbHavingAlign = block; + JITDUMP(FMT_BB ", bbWeight=" FMT_WT " ends with unconditional 'jmp' \n", block->bbNum, + block->bbWeight); + } } } @@ -5285,8 +5301,9 @@ void Compiler::placeLoopAlignInstructions() } bbHavingAlign->bbFlags |= BBF_HAS_ALIGN; - minBlockSoFar = BB_MAX_WEIGHT; - bbHavingAlign = nullptr; + minBlockSoFar = BB_MAX_WEIGHT; + bbHavingAlign = nullptr; + currentAlignedLoopNum = block->bbNext->bbNatLoopNum; if (--loopsToProcess == 0) { @@ -5567,6 +5584,10 @@ int Compiler::compCompile(CORINFO_MODULE_HANDLE classPtr, assert(info.compPatchpointInfo != nullptr); } +#if defined(TARGET_ARM64) + compFrameInfo = {0}; +#endif + virtualStubParamInfo = new (this, CMK_Unknown) VirtualStubParamInfo(IsTargetAbi(CORINFO_CORERT_ABI)); // compMatchedVM is set to true if both CPU/ABI and OS are matching the execution engine requirements @@ -8828,29 +8849,32 @@ void cLiveness(Compiler* comp) void cCVarSet(Compiler* comp, VARSET_VALARG_TP vars) { static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called - printf("===================================================================== dCVarSet %u\n", sequenceNumber++); + printf("===================================================================== *CVarSet %u\n", sequenceNumber++); dumpConvertedVarSet(comp, vars); printf("\n"); // dumpConvertedVarSet() doesn't emit a trailing newline } -void cLoop(Compiler* comp, Compiler::LoopDsc* loop) +void cLoop(Compiler* comp, unsigned loopNum) { static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called - printf("===================================================================== Loop %u\n", sequenceNumber++); - printf("HEAD " FMT_BB "\n", loop->lpHead->bbNum); - printf("TOP " FMT_BB "\n", loop->lpTop->bbNum); - printf("ENTRY " FMT_BB "\n", loop->lpEntry->bbNum); - if (loop->lpExitCnt == 1) - { - printf("EXIT " FMT_BB "\n", loop->lpExit->bbNum); - } - else - { - printf("EXITS %u\n", loop->lpExitCnt); - } - printf("BOTTOM " FMT_BB "\n", loop->lpBottom->bbNum); + printf("===================================================================== *Loop %u\n", sequenceNumber++); + comp->optPrintLoopInfo(loopNum, /* verbose */ true); + printf("\n"); +} - comp->fgDispBasicBlocks(loop->lpHead, loop->lpBottom, true); +void cLoopPtr(Compiler* comp, const Compiler::LoopDsc* loop) +{ + static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called + printf("===================================================================== *LoopPtr %u\n", sequenceNumber++); + comp->optPrintLoopInfo(loop, /* verbose */ true); + printf("\n"); +} + +void cLoops(Compiler* comp) +{ + static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called + printf("===================================================================== *Loops %u\n", sequenceNumber++); + comp->optPrintLoopTable(); } void dBlock(BasicBlock* block) @@ -8948,9 +8972,19 @@ void dCVarSet(VARSET_VALARG_TP vars) cCVarSet(JitTls::GetCompiler(), vars); } -void dLoop(Compiler::LoopDsc* loop) +void dLoop(unsigned loopNum) { - cLoop(JitTls::GetCompiler(), loop); + cLoop(JitTls::GetCompiler(), loopNum); +} + +void dLoopPtr(const Compiler::LoopDsc* loop) +{ + cLoopPtr(JitTls::GetCompiler(), loop); +} + +void dLoops() +{ + cLoops(JitTls::GetCompiler()); } void dRegMask(regMaskTP mask) @@ -9383,6 +9417,11 @@ void cTreeFlags(Compiler* comp, GenTree* tree) chars += printf("[ICON_BBC_PTR]"); break; + case GTF_ICON_STATIC_BOX_PTR: + + chars += printf("[GTF_ICON_STATIC_BOX_PTR]"); + break; + case GTF_ICON_FIELD_OFF: chars += printf("[ICON_FIELD_OFF]"); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 1a8d4040d5ecf4..17878f92806bf3 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3466,7 +3466,7 @@ class Compiler GenTree* gtReverseCond(GenTree* tree); - bool gtHasRef(GenTree* tree, ssize_t lclNum, bool defOnly); + static bool gtHasRef(GenTree* tree, ssize_t lclNum); bool gtHasLocalsWithAddrOp(GenTree* tree); @@ -4411,6 +4411,7 @@ class Compiler CORINFO_RESOLVED_TOKEN* pContstrainedResolvedToken, CORINFO_THIS_TRANSFORM constraintCallThisTransform, CorInfoIntrinsics* pIntrinsicID, + NamedIntrinsic* pIntrinsicName, bool* isSpecialIntrinsic = nullptr); GenTree* impMathIntrinsic(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, @@ -6645,7 +6646,7 @@ class Compiler }; // Do hoisting for loop "lnum" (an index into the optLoopTable), and all loops nested within it. - // Tracks the expressions that have been hoisted by containing loops by temporary recording their + // Tracks the expressions that have been hoisted by containing loops by temporarily recording their // value numbers in "m_hoistedInParentLoops". This set is not modified by the call. void optHoistLoopNest(unsigned lnum, LoopHoistContext* hoistCtxt); @@ -6655,7 +6656,7 @@ class Compiler // void optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt); - // Hoist all expressions in "blk" that are invariant in loop "lnum" (an index into the optLoopTable) + // Hoist all expressions in "blocks" that are invariant in loop "loopNum" (an index into the optLoopTable) // outside of that loop. Exempt expressions whose value number is in "m_hoistedInParentLoops"; add VN's of hoisted // expressions to "hoistInLoop". void optHoistLoopBlocks(unsigned loopNum, ArrayStack* blocks, LoopHoistContext* hoistContext); @@ -6679,6 +6680,13 @@ class Compiler // written to, and SZ-array element type equivalence classes updated. void optComputeLoopSideEffects(); +#ifdef DEBUG + bool optAnyChildNotRemoved(unsigned loopNum); +#endif // DEBUG + + // Mark a loop as removed. + void optMarkLoopRemoved(unsigned loopNum); + private: // Requires "lnum" to be the index of an outermost loop in the loop table. Traverses the body of that loop, // including all nested loops, and records the set of "side effects" of the loop: fields (object instance and @@ -6765,6 +6773,8 @@ class Compiler VARSET_TP lpVarInOut; // The set of variables that are IN or OUT during the execution of this loop VARSET_TP lpVarUseDef; // The set of variables that are USE or DEF during the execution of this loop + // The following counts are used for hoisting profitability checks. + int lpHoistedExprCount; // The register count for the non-FP expressions from inside this loop that have been // hoisted int lpLoopVarCount; // The register count for the non-FP LclVars that are read/written inside this loop @@ -6811,7 +6821,7 @@ class Compiler // : Valid if LPFLG_VAR_INIT }; - // The following is for LPFLG_ITER loops only (i.e. the loop condition is "i RELOP const or var" + // The following is for LPFLG_ITER loops only (i.e. the loop condition is "i RELOP const or var") GenTree* lpTestTree; // pointer to the node containing the loop test genTreeOps lpTestOper() const; // the type of the comparison between the iterator and the limit (GT_LE, GT_GE, @@ -6839,44 +6849,46 @@ class Compiler { return lpTop->bbNum <= blk->bbNum && blk->bbNum <= lpBottom->bbNum; } - // Returns "true" iff "*this" (properly) contains the range [first, bottom] (allowing firsts + + // Returns "true" iff "*this" (properly) contains the range [top, bottom] (allowing tops // to be equal, but requiring bottoms to be different.) - bool lpContains(BasicBlock* first, BasicBlock* bottom) const + bool lpContains(BasicBlock* top, BasicBlock* bottom) const { - return lpTop->bbNum <= first->bbNum && bottom->bbNum < lpBottom->bbNum; + return lpTop->bbNum <= top->bbNum && bottom->bbNum < lpBottom->bbNum; } - // Returns "true" iff "*this" (properly) contains "lp2" (allowing firsts to be equal, but requiring + // Returns "true" iff "*this" (properly) contains "lp2" (allowing tops to be equal, but requiring // bottoms to be different.) bool lpContains(const LoopDsc& lp2) const { return lpContains(lp2.lpTop, lp2.lpBottom); } - // Returns "true" iff "*this" is (properly) contained by the range [first, bottom] - // (allowing firsts to be equal, but requiring bottoms to be different.) - bool lpContainedBy(BasicBlock* first, BasicBlock* bottom) const + // Returns "true" iff "*this" is (properly) contained by the range [top, bottom] + // (allowing tops to be equal, but requiring bottoms to be different.) + bool lpContainedBy(BasicBlock* top, BasicBlock* bottom) const { - return first->bbNum <= lpTop->bbNum && lpBottom->bbNum < bottom->bbNum; + return top->bbNum <= lpTop->bbNum && lpBottom->bbNum < bottom->bbNum; } // Returns "true" iff "*this" is (properly) contained by "lp2" - // (allowing firsts to be equal, but requiring bottoms to be different.) + // (allowing tops to be equal, but requiring bottoms to be different.) bool lpContainedBy(const LoopDsc& lp2) const { - return lpContains(lp2.lpTop, lp2.lpBottom); + return lpContainedBy(lp2.lpTop, lp2.lpBottom); } // Returns "true" iff "*this" is disjoint from the range [top, bottom]. - bool lpDisjoint(BasicBlock* first, BasicBlock* bottom) const + bool lpDisjoint(BasicBlock* top, BasicBlock* bottom) const { - return bottom->bbNum < lpTop->bbNum || lpBottom->bbNum < first->bbNum; + return bottom->bbNum < lpTop->bbNum || lpBottom->bbNum < top->bbNum; } // Returns "true" iff "*this" is disjoint from "lp2". bool lpDisjoint(const LoopDsc& lp2) const { return lpDisjoint(lp2.lpTop, lp2.lpBottom); } + // Returns "true" iff the loop is well-formed (see code for defn). bool lpWellFormed() const { @@ -6917,6 +6929,12 @@ class Compiler BasicBlock* exit, unsigned char exitCnt); +#ifdef DEBUG + void optPrintLoopInfo(unsigned lnum, bool printVerbose = false); + void optPrintLoopInfo(const LoopDsc* loop, bool printVerbose = false); + void optPrintLoopTable(); +#endif + protected: unsigned optCallCount; // number of calls made in the method unsigned optIndirectCallCount; // number of virtual, interface and indirect calls made in the method @@ -6924,17 +6942,6 @@ class Compiler unsigned optLoopsCloned; // number of loops cloned in the current method. #ifdef DEBUG - void optPrintLoopInfo(unsigned loopNum, - BasicBlock* lpHead, - BasicBlock* lpTop, - BasicBlock* lpEntry, - BasicBlock* lpBottom, - unsigned char lpExitCnt, - BasicBlock* lpExit, - unsigned parentLoop = BasicBlock::NOT_IN_LOOP) const; - void optPrintLoopInfo(unsigned lnum) const; - void optPrintLoopRecording(unsigned lnum) const; - void optCheckPreds(); #endif @@ -6969,10 +6976,11 @@ class Compiler // unshared with any other loop. Returns "true" iff the flowgraph has been modified bool optCanonicalizeLoop(unsigned char loopInd); - // Requires "l1" to be a valid loop table index, and not "BasicBlock::NOT_IN_LOOP". Requires "l2" to be - // a valid loop table index, or else "BasicBlock::NOT_IN_LOOP". Returns true - // iff "l2" is not NOT_IN_LOOP, and "l1" contains "l2". - bool optLoopContains(unsigned l1, unsigned l2); + // Requires "l1" to be a valid loop table index, and not "BasicBlock::NOT_IN_LOOP". + // Requires "l2" to be a valid loop table index, or else "BasicBlock::NOT_IN_LOOP". + // Returns true iff "l2" is not NOT_IN_LOOP, and "l1" contains "l2". + // A loop contains itself. + bool optLoopContains(unsigned l1, unsigned l2) const; // Updates the loop table by changing loop "loopInd", whose head is required // to be "from", to be "to". Also performs this transformation for any @@ -10330,6 +10338,24 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX unsigned compMapILvarNum(unsigned ILvarNum); // map accounting for hidden args unsigned compMap2ILvarNum(unsigned varNum) const; // map accounting for hidden args +#if defined(TARGET_ARM64) + struct FrameInfo + { + // Frame type (1-5) + int frameType; + + // Distance from established (method body) SP to base of callee save area + int calleeSaveSpOffset; + + // Amount to subtract from SP before saving (prolog) OR + // to add to SP after restoring (epilog) callee saves + int calleeSaveSpDelta; + + // Distance from established SP to where caller's FP was saved + int offsetSpToSavedFp; + } compFrameInfo; +#endif + //------------------------------------------------------------------------- static void compStartup(); // One-time initialization @@ -11426,11 +11452,6 @@ class GenTreeVisitor GenTree** op1Use = &dynBlock->gtOp1; GenTree** op2Use = &dynBlock->gtDynamicSize; - if (TVisitor::UseExecutionOrder && dynBlock->gtEvalSizeFirst) - { - std::swap(op1Use, op2Use); - } - result = WalkTree(op1Use, dynBlock); if (result == fgWalkResult::WALK_ABORT) { @@ -11458,11 +11479,6 @@ class GenTreeVisitor { std::swap(op1Use, op2Use); } - if (dynBlock->gtEvalSizeFirst) - { - std::swap(op3Use, op2Use); - std::swap(op2Use, op1Use); - } } result = WalkTree(op1Use, dynBlock); diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index f20baa57a74ef1..e763e2390930f8 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -894,12 +894,10 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed const bool isForceInline = (info.compFlags & CORINFO_FLG_FORCEINLINE) != 0; const bool makeInlineObservations = (compInlineResult != nullptr); const bool isInlining = compIsForInlining(); - const bool isPreJit = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT); - const bool isTier1 = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER1); unsigned retBlocks = 0; int prefixFlags = 0; bool preciseScan = makeInlineObservations && compInlineResult->GetPolicy()->RequiresPreciseScan(); - const bool resolveTokens = preciseScan && (isPreJit || isTier1); + const bool resolveTokens = preciseScan; // Track offsets where IL instructions begin in DEBUG builds. Used to // validate debug info generated by the JIT. @@ -4703,7 +4701,7 @@ void Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable) { loopAlignCandidates++; succBlock->bbFlags |= BBF_LOOP_ALIGN; - JITDUMP("Propagating LOOP_ALIGN flag from " FMT_BB " to " FMT_BB " for loop# %d.", block->bbNum, + JITDUMP("Propagating LOOP_ALIGN flag from " FMT_BB " to " FMT_BB " for loop# %d.\n", block->bbNum, succBlock->bbNum, block->bbNatLoopNum); } diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 8fa10e2f9f18f5..ce76dc79896192 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -734,12 +734,14 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos) const bool includeLoops = (JitConfig.JitDumpFgLoops() != 0) && !compIsForInlining() && (phase < PHASE_RATIONALIZE); const bool constrained = JitConfig.JitDumpFgConstrained() != 0; const bool useBlockId = JitConfig.JitDumpFgBlockID() != 0; + const bool displayBlockFlags = JitConfig.JitDumpFgBlockFlags() != 0; #else // !DEBUG - const bool createDotFile = true; - const bool includeEH = false; - const bool includeLoops = false; - const bool constrained = true; - const bool useBlockId = false; + const bool createDotFile = true; + const bool includeEH = false; + const bool includeLoops = false; + const bool constrained = true; + const bool useBlockId = false; + const bool displayBlockFlags = false; #endif // !DEBUG FILE* fgxFile = fgOpenFlowGraphFile(&dontClose, phase, pos, createDotFile ? W("dot") : W("fgx")); @@ -866,6 +868,43 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos) fprintf(fgxFile, FMT_BB, block->bbNum); } + if (displayBlockFlags) + { + // Don't display the `[` `]` unless we're going to display something. + const BasicBlockFlags allDisplayedBlockFlags = BBF_TRY_BEG | BBF_FUNCLET_BEG | BBF_RUN_RARELY | + BBF_LOOP_HEAD | BBF_LOOP_PREHEADER | BBF_LOOP_ALIGN; + if (block->bbFlags & allDisplayedBlockFlags) + { + // Display a very few, useful, block flags + fprintf(fgxFile, " ["); + if (block->bbFlags & BBF_TRY_BEG) + { + fprintf(fgxFile, "T"); + } + if (block->bbFlags & BBF_FUNCLET_BEG) + { + fprintf(fgxFile, "F"); + } + if (block->bbFlags & BBF_RUN_RARELY) + { + fprintf(fgxFile, "R"); + } + if (block->bbFlags & BBF_LOOP_HEAD) + { + fprintf(fgxFile, "L"); + } + if (block->bbFlags & BBF_LOOP_PREHEADER) + { + fprintf(fgxFile, "P"); + } + if (block->bbFlags & BBF_LOOP_ALIGN) + { + fprintf(fgxFile, "A"); + } + fprintf(fgxFile, "]"); + } + } + if (block->bbJumpKind == BBJ_COND) { fprintf(fgxFile, "\\n"); @@ -1636,6 +1675,12 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos) if (includeLoops) { +#ifdef DEBUG + const bool displayLoopFlags = JitConfig.JitDumpFgLoopFlags() != 0; +#else // !DEBUG + const bool displayLoopFlags = false; +#endif // !DEBUG + char name[30]; for (unsigned loopNum = 0; loopNum < optLoopCount; loopNum++) { @@ -1644,7 +1689,24 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos) { continue; } + sprintf_s(name, sizeof(name), FMT_LP, loopNum); + + if (displayLoopFlags) + { + // Display a very few, useful, loop flags + strcat_s(name, sizeof(name), " ["); + if (loop.lpFlags & LoopFlags::LPFLG_ITER) + { + strcat_s(name, sizeof(name), "I"); + } + if (loop.lpFlags & LoopFlags::LPFLG_HAS_PREHEAD) + { + strcat_s(name, sizeof(name), "P"); + } + strcat_s(name, sizeof(name), "]"); + } + rgnGraph.Insert(name, RegionGraph::RegionType::Loop, loop.lpTop, loop.lpBottom); } } @@ -2804,7 +2866,7 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef #ifndef JIT32_GCENCODER copiedForGenericsCtxt = ((info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_THIS) != 0); #else // JIT32_GCENCODER - copiedForGenericsCtxt = false; + copiedForGenericsCtxt = false; #endif // JIT32_GCENCODER // This if only in support of the noway_asserts it contains. diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index cf73fa123e6fd9..17d13b362d04cf 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -1905,7 +1905,7 @@ void Compiler::fgInlineAppendStatements(InlineInfo* inlineInfo, BasicBlock* bloc GenTree* retExpr = inlineInfo->retExpr; if (retExpr != nullptr) { - const bool interferesWithReturn = gtHasRef(inlineInfo->retExpr, tmpNum, false); + const bool interferesWithReturn = gtHasRef(inlineInfo->retExpr, tmpNum); noway_assert(!interferesWithReturn); } diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index e0fdac1da935ca..859ea08aed822e 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -1084,7 +1084,9 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, GenTree* qmarkNode = nullptr; if (oper == GT_FTN_ADDR) { - targetMethodHnd = targetMethod->AsFptrVal()->gtFptrMethod; + GenTreeFptrVal* fptrValTree = targetMethod->AsFptrVal(); + fptrValTree->gtFptrDelegateTarget = true; + targetMethodHnd = fptrValTree->gtFptrMethod; } else if (oper == GT_CALL && targetMethod->AsCall()->gtCallMethHnd == eeFindHelper(CORINFO_HELP_VIRTUAL_FUNC_PTR)) { @@ -3933,11 +3935,7 @@ void Compiler::fgSetTreeSeqHelper(GenTree* tree, bool isLIR) GenTree* sizeNode = dynBlk->gtDynamicSize; GenTree* dstAddr = dynBlk->Addr(); GenTree* src = dynBlk->Data(); - bool isReverse = ((dynBlk->gtFlags & GTF_REVERSE_OPS) != 0); - if (dynBlk->gtEvalSizeFirst) - { - fgSetTreeSeqHelper(sizeNode, isLIR); - } + bool isReverse = dynBlk->IsReverseOp(); // We either have a DYN_BLK or a STORE_DYN_BLK. If the latter, we have a // src (the Data to be stored), and isReverse tells us whether to evaluate @@ -3951,10 +3949,8 @@ void Compiler::fgSetTreeSeqHelper(GenTree* tree, bool isLIR) { fgSetTreeSeqHelper(src, isLIR); } - if (!dynBlk->gtEvalSizeFirst) - { - fgSetTreeSeqHelper(sizeNode, isLIR); - } + fgSetTreeSeqHelper(sizeNode, isLIR); + fgSetTreeSeqFinish(dynBlk, isLIR); return; } diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 00c58d70bae1d2..7c0f79288f5226 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -1634,262 +1634,70 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK) return false; } -/***************************************************************************** - * - * Returns non-zero if the given tree contains a use of a local #lclNum. - */ - -// TODO-List-Cleanup: rewrite with a general visitor. -bool Compiler::gtHasRef(GenTree* tree, ssize_t lclNum, bool defOnly) +//------------------------------------------------------------------------ +// gtHasRef: Find out whether the given tree contains a local/field. +// +// Arguments: +// tree - tree to find the local in +// lclNum - the local's number, *or* the handle for the field +// +// Return Value: +// Whether "tree" has any LCL_VAR/LCL_FLD nodes that refer to the +// local, LHS or RHS, or FIELD nodes with the specified handle. +// +// Notes: +// Does not pay attention to local address nodes. +// +/* static */ bool Compiler::gtHasRef(GenTree* tree, ssize_t lclNum) { - genTreeOps oper; - unsigned kind; - -AGAIN: - - assert(tree); - - oper = tree->OperGet(); - kind = tree->OperKind(); - - /* Is this a constant node? */ - - if (kind & GTK_CONST) + if (tree == nullptr) { return false; } - /* Is this a leaf node? */ - - if (kind & GTK_LEAF) + if (tree->OperIsLeaf()) { - if (oper == GT_LCL_VAR) + if (tree->OperIs(GT_LCL_VAR, GT_LCL_FLD) && (tree->AsLclVarCommon()->GetLclNum() == (unsigned)lclNum)) { - if (tree->AsLclVarCommon()->GetLclNum() == (unsigned)lclNum) - { - if (!defOnly) - { - return true; - } - } + return true; } - else if (oper == GT_RET_EXPR) + if (tree->OperIs(GT_RET_EXPR)) { - return gtHasRef(tree->AsRetExpr()->gtInlineCandidate, lclNum, defOnly); + return gtHasRef(tree->AsRetExpr()->gtInlineCandidate, lclNum); } return false; } - /* Is it a 'simple' unary/binary operator? */ - - if (kind & GTK_SMPOP) + if (tree->OperIsUnary()) { // Code in importation (see CEE_STFLD in impImportBlockCode), when // spilling, can pass us "lclNum" that is actually a field handle... - if (tree->OperIs(GT_FIELD) && (lclNum == (ssize_t)tree->AsField()->gtFldHnd) && !defOnly) + if (tree->OperIs(GT_FIELD) && (lclNum == (ssize_t)tree->AsField()->gtFldHnd)) { return true; } - if (tree->gtGetOp2IfPresent()) - { - if (gtHasRef(tree->AsOp()->gtOp1, lclNum, defOnly)) - { - return true; - } - - tree = tree->AsOp()->gtOp2; - goto AGAIN; - } - else - { - tree = tree->AsOp()->gtOp1; - - if (!tree) - { - return false; - } - - if (oper == GT_ASG) - { - // 'tree' is the gtOp1 of an assignment node. So we can handle - // the case where defOnly is either true or false. - - if (tree->gtOper == GT_LCL_VAR && tree->AsLclVarCommon()->GetLclNum() == (unsigned)lclNum) - { - return true; - } - else if (tree->gtOper == GT_FIELD && lclNum == (ssize_t)tree->AsField()->gtFldHnd) - { - return true; - } - } - - goto AGAIN; - } + return gtHasRef(tree->AsUnOp()->gtGetOp1(), lclNum); } - /* See what kind of a special operator we have here */ - - switch (oper) + if (tree->OperIsBinary()) { - case GT_CALL: - if (tree->AsCall()->gtCallThisArg != nullptr) - { - if (gtHasRef(tree->AsCall()->gtCallThisArg->GetNode(), lclNum, defOnly)) - { - return true; - } - } - - for (GenTreeCall::Use& use : tree->AsCall()->Args()) - { - if (gtHasRef(use.GetNode(), lclNum, defOnly)) - { - return true; - } - } - - for (GenTreeCall::Use& use : tree->AsCall()->LateArgs()) - { - if (gtHasRef(use.GetNode(), lclNum, defOnly)) - { - return true; - } - } - - if (tree->AsCall()->gtControlExpr) - { - if (gtHasRef(tree->AsCall()->gtControlExpr, lclNum, defOnly)) - { - return true; - } - } - - if (tree->AsCall()->gtCallType == CT_INDIRECT) - { - // pinvoke-calli cookie is a constant, or constant indirection - assert(tree->AsCall()->gtCallCookie == nullptr || tree->AsCall()->gtCallCookie->gtOper == GT_CNS_INT || - tree->AsCall()->gtCallCookie->gtOper == GT_IND); - - tree = tree->AsCall()->gtCallAddr; - } - else - { - tree = nullptr; - } - - if (tree) - { - goto AGAIN; - } - - break; - -#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS) -#if defined(FEATURE_SIMD) - case GT_SIMD: -#endif -#if defined(FEATURE_HW_INTRINSICS) - case GT_HWINTRINSIC: -#endif - for (GenTree* operand : tree->AsMultiOp()->Operands()) - { - if (gtHasRef(operand, lclNum, defOnly)) - { - return true; - } - } - break; -#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS) - - case GT_ARR_ELEM: - if (gtHasRef(tree->AsArrElem()->gtArrObj, lclNum, defOnly)) - { - return true; - } - - unsigned dim; - for (dim = 0; dim < tree->AsArrElem()->gtArrRank; dim++) - { - if (gtHasRef(tree->AsArrElem()->gtArrInds[dim], lclNum, defOnly)) - { - return true; - } - } - - break; - - case GT_ARR_OFFSET: - if (gtHasRef(tree->AsArrOffs()->gtOffset, lclNum, defOnly) || - gtHasRef(tree->AsArrOffs()->gtIndex, lclNum, defOnly) || - gtHasRef(tree->AsArrOffs()->gtArrObj, lclNum, defOnly)) - { - return true; - } - break; - - case GT_PHI: - for (GenTreePhi::Use& use : tree->AsPhi()->Uses()) - { - if (gtHasRef(use.GetNode(), lclNum, defOnly)) - { - return true; - } - } - break; - - case GT_FIELD_LIST: - for (GenTreeFieldList::Use& use : tree->AsFieldList()->Uses()) - { - if (gtHasRef(use.GetNode(), lclNum, defOnly)) - { - return true; - } - } - break; - - case GT_CMPXCHG: - if (gtHasRef(tree->AsCmpXchg()->gtOpLocation, lclNum, defOnly)) - { - return true; - } - if (gtHasRef(tree->AsCmpXchg()->gtOpValue, lclNum, defOnly)) - { - return true; - } - if (gtHasRef(tree->AsCmpXchg()->gtOpComparand, lclNum, defOnly)) - { - return true; - } - break; + return gtHasRef(tree->AsOp()->gtGetOp1(), lclNum) || gtHasRef(tree->AsOp()->gtGetOp2(), lclNum); + } - case GT_STORE_DYN_BLK: - if (gtHasRef(tree->AsDynBlk()->Data(), lclNum, defOnly)) - { - return true; - } - FALLTHROUGH; - case GT_DYN_BLK: - if (gtHasRef(tree->AsDynBlk()->Addr(), lclNum, defOnly)) - { - return true; - } - if (gtHasRef(tree->AsDynBlk()->gtDynamicSize, lclNum, defOnly)) - { - return true; - } - break; + bool result = false; + tree->VisitOperands([lclNum, &result](GenTree* operand) -> GenTree::VisitResult { + if (gtHasRef(operand, lclNum)) + { + result = true; + return GenTree::VisitResult::Abort; + } - default: -#ifdef DEBUG - gtDispTree(tree); -#endif - assert(!"unexpected operator"); - } + return GenTree::VisitResult::Continue; + }); - return false; + return result; } struct AddrTakenDsc @@ -2620,29 +2428,58 @@ unsigned Compiler::gtSetMultiOpOrder(GenTreeMultiOp* multiOp) unsigned level = 0; unsigned lvl2 = 0; -#if defined(FEATURE_HW_INTRINSICS) && defined(TARGET_XARCH) - if (multiOp->OperIs(GT_HWINTRINSIC) && (multiOp->GetOperandCount() == 1) && - multiOp->AsHWIntrinsic()->OperIsMemoryLoadOrStore()) +#if defined(FEATURE_HW_INTRINSICS) + if (multiOp->OperIs(GT_HWINTRINSIC)) { - costEx = IND_COST_EX; - costSz = 2; + GenTreeHWIntrinsic* hwTree = multiOp->AsHWIntrinsic(); +#if defined(TARGET_XARCH) + if ((hwTree->GetOperandCount() == 1) && hwTree->OperIsMemoryLoadOrStore()) + { + costEx = IND_COST_EX; + costSz = 2; - GenTree* addr = multiOp->Op(1)->gtEffectiveVal(); - level = gtSetEvalOrder(addr); + GenTree* addr = hwTree->Op(1)->gtEffectiveVal(); + level = gtSetEvalOrder(addr); - // See if we can form a complex addressing mode. - if (addr->OperIs(GT_ADD) && gtMarkAddrMode(addr, &costEx, &costSz, multiOp->TypeGet())) - { - // Nothing to do, costs have been set. + // See if we can form a complex addressing mode. + if (addr->OperIs(GT_ADD) && gtMarkAddrMode(addr, &costEx, &costSz, hwTree->TypeGet())) + { + // Nothing to do, costs have been set. + } + else + { + costEx += addr->GetCostEx(); + costSz += addr->GetCostSz(); + } + + hwTree->SetCosts(costEx, costSz); + return level; } - else +#endif + switch (hwTree->GetHWIntrinsicId()) { - costEx += addr->GetCostEx(); - costSz += addr->GetCostSz(); +#if defined(TARGET_XARCH) + case NI_Vector128_Create: + case NI_Vector256_Create: +#elif defined(TARGET_ARM64) + case NI_Vector64_Create: + case NI_Vector128_Create: +#endif + { + if ((hwTree->GetOperandCount() == 1) && hwTree->Op(1)->OperIsConst()) + { + // Vector.Create(cns) is cheap but not that cheap to be (1,1) + costEx = IND_COST_EX; + costSz = 2; + level = gtSetEvalOrder(hwTree->Op(1)); + hwTree->SetCosts(costEx, costSz); + return level; + } + break; + } + default: + break; } - - multiOp->SetCosts(costEx, costSz); - return level; } #endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS) @@ -3947,21 +3784,16 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) } break; } + + case NI_System_Object_GetType: + // Giving intrinsics a large fixed execution cost is because we'd like to CSE + // them, even if they are implemented by calls. This is different from modeling + // user calls since we never CSE user calls. + costEx = 36; + costSz = 4; + break; } } - else - { - // old style intrinsic (only Object_GetType is expected) - assert(intrinsic->gtIntrinsicName == NI_Illegal); - assert(intrinsic->gtIntrinsicId == CORINFO_INTRINSIC_Object_GetType); - - // Giving intrinsics a large fixed execution cost is because we'd like to CSE - // them, even if they are implemented by calls. This is different from modeling - // user calls since we never CSE user calls. - costEx = 36; - costSz = 4; - break; - } level++; break; @@ -4841,7 +4673,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) case GT_STORE_DYN_BLK: case GT_DYN_BLK: - { level = gtSetEvalOrder(tree->AsDynBlk()->Addr()); costEx = tree->AsDynBlk()->Addr()->GetCostEx(); costSz = tree->AsDynBlk()->Addr()->GetCostSz(); @@ -4854,45 +4685,11 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) costSz += tree->AsDynBlk()->Data()->GetCostSz(); } - unsigned sizeLevel = gtSetEvalOrder(tree->AsDynBlk()->gtDynamicSize); - - // Determine whether the size node should be evaluated first. - // We would like to do this if the sizeLevel is larger than the current level, - // but we have to ensure that we obey ordering constraints. - if (tree->AsDynBlk()->gtEvalSizeFirst != (level < sizeLevel)) - { - bool canChange = true; - - GenTree* sizeNode = tree->AsDynBlk()->gtDynamicSize; - GenTree* dst = tree->AsDynBlk()->Addr(); - GenTree* src = tree->AsDynBlk()->Data(); - - if (tree->AsDynBlk()->gtEvalSizeFirst) - { - canChange = gtCanSwapOrder(sizeNode, dst); - if (canChange && (src != nullptr)) - { - canChange = gtCanSwapOrder(sizeNode, src); - } - } - else - { - canChange = gtCanSwapOrder(dst, sizeNode); - if (canChange && (src != nullptr)) - { - gtCanSwapOrder(src, sizeNode); - } - } - if (canChange) - { - tree->AsDynBlk()->gtEvalSizeFirst = (level < sizeLevel); - } - } - level = max(level, sizeLevel); + lvl2 = gtSetEvalOrder(tree->AsDynBlk()->gtDynamicSize); + level = max(level, lvl2); costEx += tree->AsDynBlk()->gtDynamicSize->GetCostEx(); costSz += tree->AsDynBlk()->gtDynamicSize->GetCostSz(); - } - break; + break; default: JITDUMP("unexpected operator in this tree:\n"); @@ -8605,25 +8402,15 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) return; case GT_DYN_BLK: - { - GenTreeDynBlk* const dynBlock = m_node->AsDynBlk(); - m_edge = dynBlock->gtEvalSizeFirst ? &dynBlock->gtDynamicSize : &dynBlock->gtOp1; + m_edge = &m_node->AsDynBlk()->Addr(); assert(*m_edge != nullptr); m_advance = &GenTreeUseEdgeIterator::AdvanceDynBlk; - } return; case GT_STORE_DYN_BLK: { GenTreeDynBlk* const dynBlock = m_node->AsDynBlk(); - if (dynBlock->gtEvalSizeFirst) - { - m_edge = &dynBlock->gtDynamicSize; - } - else - { - m_edge = dynBlock->IsReverseOp() ? &dynBlock->gtOp2 : &dynBlock->gtOp1; - } + m_edge = dynBlock->IsReverseOp() ? &dynBlock->gtOp2 : &dynBlock->gtOp1; assert(*m_edge != nullptr); m_advance = &GenTreeUseEdgeIterator::AdvanceStoreDynBlk; @@ -8712,7 +8499,7 @@ void GenTreeUseEdgeIterator::AdvanceDynBlk() { GenTreeDynBlk* const dynBlock = m_node->AsDynBlk(); - m_edge = dynBlock->gtEvalSizeFirst ? &dynBlock->gtOp1 : &dynBlock->gtDynamicSize; + m_edge = &dynBlock->gtDynamicSize; assert(*m_edge != nullptr); m_advance = &GenTreeUseEdgeIterator::Terminate; } @@ -8720,43 +8507,21 @@ void GenTreeUseEdgeIterator::AdvanceDynBlk() //------------------------------------------------------------------------ // GenTreeUseEdgeIterator::AdvanceStoreDynBlk: produces the next operand of a StoreDynBlk node and advances the state. // -// These nodes are moderately complicated but rare enough that templating this function is probably not -// worth the extra complexity. -// void GenTreeUseEdgeIterator::AdvanceStoreDynBlk() { GenTreeDynBlk* const dynBlock = m_node->AsDynBlk(); - if (dynBlock->gtEvalSizeFirst) - { - switch (m_state) - { - case 0: - m_edge = dynBlock->IsReverseOp() ? &dynBlock->gtOp2 : &dynBlock->gtOp1; - m_state = 1; - break; - case 1: - m_edge = dynBlock->IsReverseOp() ? &dynBlock->gtOp1 : &dynBlock->gtOp2; - m_advance = &GenTreeUseEdgeIterator::Terminate; - break; - default: - unreached(); - } - } - else + switch (m_state) { - switch (m_state) - { - case 0: - m_edge = dynBlock->IsReverseOp() ? &dynBlock->gtOp1 : &dynBlock->gtOp2; - m_state = 1; - break; - case 1: - m_edge = &dynBlock->gtDynamicSize; - m_advance = &GenTreeUseEdgeIterator::Terminate; - break; - default: - unreached(); - } + case 0: + m_edge = dynBlock->IsReverseOp() ? &dynBlock->gtOp1 : &dynBlock->gtOp2; + m_state = 1; + break; + case 1: + m_edge = &dynBlock->gtDynamicSize; + m_advance = &GenTreeUseEdgeIterator::Terminate; + break; + default: + unreached(); } assert(*m_edge != nullptr); @@ -10454,6 +10219,9 @@ void Compiler::gtDispConst(GenTree* tree) case GTF_ICON_BBC_PTR: printf(" bbc"); break; + case GTF_ICON_STATIC_BOX_PTR: + printf(" static box ptr"); + break; default: printf(" UNKNOWN"); break; @@ -11018,101 +10786,96 @@ void Compiler::gtDispTree(GenTree* tree, { GenTreeIntrinsic* intrinsic = tree->AsIntrinsic(); - if (intrinsic->gtIntrinsicId == CORINFO_INTRINSIC_Object_GetType) - { - assert(intrinsic->gtIntrinsicName == NI_Illegal); - printf(" objGetType"); - } - else + assert(intrinsic->gtIntrinsicId == CORINFO_INTRINSIC_Illegal); + switch (intrinsic->gtIntrinsicName) { - assert(intrinsic->gtIntrinsicId == CORINFO_INTRINSIC_Illegal); - switch (intrinsic->gtIntrinsicName) - { - case NI_System_Math_Abs: - printf(" abs"); - break; - case NI_System_Math_Acos: - printf(" acos"); - break; - case NI_System_Math_Acosh: - printf(" acosh"); - break; - case NI_System_Math_Asin: - printf(" asin"); - break; - case NI_System_Math_Asinh: - printf(" asinh"); - break; - case NI_System_Math_Atan: - printf(" atan"); - break; - case NI_System_Math_Atanh: - printf(" atanh"); - break; - case NI_System_Math_Atan2: - printf(" atan2"); - break; - case NI_System_Math_Cbrt: - printf(" cbrt"); - break; - case NI_System_Math_Ceiling: - printf(" ceiling"); - break; - case NI_System_Math_Cos: - printf(" cos"); - break; - case NI_System_Math_Cosh: - printf(" cosh"); - break; - case NI_System_Math_Exp: - printf(" exp"); - break; - case NI_System_Math_Floor: - printf(" floor"); - break; - case NI_System_Math_FMod: - printf(" fmod"); - break; - case NI_System_Math_FusedMultiplyAdd: - printf(" fma"); - break; - case NI_System_Math_ILogB: - printf(" ilogb"); - break; - case NI_System_Math_Log: - printf(" log"); - break; - case NI_System_Math_Log2: - printf(" log2"); - break; - case NI_System_Math_Log10: - printf(" log10"); - break; - case NI_System_Math_Pow: - printf(" pow"); - break; - case NI_System_Math_Round: - printf(" round"); - break; - case NI_System_Math_Sin: - printf(" sin"); - break; - case NI_System_Math_Sinh: - printf(" sinh"); - break; - case NI_System_Math_Sqrt: - printf(" sqrt"); - break; - case NI_System_Math_Tan: - printf(" tan"); - break; - case NI_System_Math_Tanh: - printf(" tanh"); - break; + case NI_System_Math_Abs: + printf(" abs"); + break; + case NI_System_Math_Acos: + printf(" acos"); + break; + case NI_System_Math_Acosh: + printf(" acosh"); + break; + case NI_System_Math_Asin: + printf(" asin"); + break; + case NI_System_Math_Asinh: + printf(" asinh"); + break; + case NI_System_Math_Atan: + printf(" atan"); + break; + case NI_System_Math_Atanh: + printf(" atanh"); + break; + case NI_System_Math_Atan2: + printf(" atan2"); + break; + case NI_System_Math_Cbrt: + printf(" cbrt"); + break; + case NI_System_Math_Ceiling: + printf(" ceiling"); + break; + case NI_System_Math_Cos: + printf(" cos"); + break; + case NI_System_Math_Cosh: + printf(" cosh"); + break; + case NI_System_Math_Exp: + printf(" exp"); + break; + case NI_System_Math_Floor: + printf(" floor"); + break; + case NI_System_Math_FMod: + printf(" fmod"); + break; + case NI_System_Math_FusedMultiplyAdd: + printf(" fma"); + break; + case NI_System_Math_ILogB: + printf(" ilogb"); + break; + case NI_System_Math_Log: + printf(" log"); + break; + case NI_System_Math_Log2: + printf(" log2"); + break; + case NI_System_Math_Log10: + printf(" log10"); + break; + case NI_System_Math_Pow: + printf(" pow"); + break; + case NI_System_Math_Round: + printf(" round"); + break; + case NI_System_Math_Sin: + printf(" sin"); + break; + case NI_System_Math_Sinh: + printf(" sinh"); + break; + case NI_System_Math_Sqrt: + printf(" sqrt"); + break; + case NI_System_Math_Tan: + printf(" tan"); + break; + case NI_System_Math_Tanh: + printf(" tanh"); + break; + case NI_System_Object_GetType: + printf(" objGetType"); + break; - default: - unreached(); - } + default: + unreached(); } } @@ -15373,13 +15136,13 @@ Compiler::TypeProducerKind Compiler::gtGetTypeProducerKind(GenTree* tree) } else if (tree->AsCall()->gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC) { - if (info.compCompHnd->getIntrinsicID(tree->AsCall()->gtCallMethHnd) == CORINFO_INTRINSIC_Object_GetType) + if (lookupNamedIntrinsic(tree->AsCall()->gtCallMethHnd) == NI_System_Object_GetType) { return TPK_GetType; } } } - else if ((tree->gtOper == GT_INTRINSIC) && (tree->AsIntrinsic()->gtIntrinsicId == CORINFO_INTRINSIC_Object_GetType)) + else if ((tree->gtOper == GT_INTRINSIC) && (tree->AsIntrinsic()->gtIntrinsicName == NI_System_Object_GetType)) { return TPK_GetType; } @@ -16964,7 +16727,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b { GenTreeIntrinsic* intrinsic = obj->AsIntrinsic(); - if (intrinsic->gtIntrinsicId == CORINFO_INTRINSIC_Object_GetType) + if (intrinsic->gtIntrinsicName == NI_System_Object_GetType) { CORINFO_CLASS_HANDLE runtimeType = info.compCompHnd->getBuiltinClass(CLASSID_RUNTIME_TYPE); assert(runtimeType != NO_CLASS_HANDLE); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 1a0865073982dc..1cad150ac5e35c 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -542,27 +542,28 @@ enum GenTreeFlags : unsigned int GTF_BOX_VALUE = 0x80000000, // GT_BOX -- "box" is on a value type - GTF_ICON_HDL_MASK = 0xF0000000, // Bits used by handle types below - GTF_ICON_SCOPE_HDL = 0x10000000, // GT_CNS_INT -- constant is a scope handle - GTF_ICON_CLASS_HDL = 0x20000000, // GT_CNS_INT -- constant is a class handle - GTF_ICON_METHOD_HDL = 0x30000000, // GT_CNS_INT -- constant is a method handle - GTF_ICON_FIELD_HDL = 0x40000000, // GT_CNS_INT -- constant is a field handle - GTF_ICON_STATIC_HDL = 0x50000000, // GT_CNS_INT -- constant is a handle to static data - GTF_ICON_STR_HDL = 0x60000000, // GT_CNS_INT -- constant is a string handle - GTF_ICON_CONST_PTR = 0x70000000, // GT_CNS_INT -- constant is a pointer to immutable data, (e.g. IAT_PPVALUE) - GTF_ICON_GLOBAL_PTR = 0x80000000, // GT_CNS_INT -- constant is a pointer to mutable data (e.g. from the VM state) - GTF_ICON_VARG_HDL = 0x90000000, // GT_CNS_INT -- constant is a var arg cookie handle - GTF_ICON_PINVKI_HDL = 0xA0000000, // GT_CNS_INT -- constant is a pinvoke calli handle - GTF_ICON_TOKEN_HDL = 0xB0000000, // GT_CNS_INT -- constant is a token handle (other than class, method or field) - GTF_ICON_TLS_HDL = 0xC0000000, // GT_CNS_INT -- constant is a TLS ref with offset - GTF_ICON_FTN_ADDR = 0xD0000000, // GT_CNS_INT -- constant is a function address - GTF_ICON_CIDMID_HDL = 0xE0000000, // GT_CNS_INT -- constant is a class ID or a module ID - GTF_ICON_BBC_PTR = 0xF0000000, // GT_CNS_INT -- constant is a basic block count pointer - - GTF_ICON_FIELD_OFF = 0x08000000, // GT_CNS_INT -- constant is a field offset - GTF_ICON_SIMD_COUNT = 0x04000000, // GT_CNS_INT -- constant is Vector.Count - - GTF_ICON_INITCLASS = 0x02000000, // GT_CNS_INT -- Constant is used to access a static that requires preceding + GTF_ICON_HDL_MASK = 0xFF000000, // Bits used by handle types below + GTF_ICON_SCOPE_HDL = 0x01000000, // GT_CNS_INT -- constant is a scope handle + GTF_ICON_CLASS_HDL = 0x02000000, // GT_CNS_INT -- constant is a class handle + GTF_ICON_METHOD_HDL = 0x03000000, // GT_CNS_INT -- constant is a method handle + GTF_ICON_FIELD_HDL = 0x04000000, // GT_CNS_INT -- constant is a field handle + GTF_ICON_STATIC_HDL = 0x05000000, // GT_CNS_INT -- constant is a handle to static data + GTF_ICON_STR_HDL = 0x06000000, // GT_CNS_INT -- constant is a string handle + GTF_ICON_CONST_PTR = 0x07000000, // GT_CNS_INT -- constant is a pointer to immutable data, (e.g. IAT_PPVALUE) + GTF_ICON_GLOBAL_PTR = 0x08000000, // GT_CNS_INT -- constant is a pointer to mutable data (e.g. from the VM state) + GTF_ICON_VARG_HDL = 0x09000000, // GT_CNS_INT -- constant is a var arg cookie handle + GTF_ICON_PINVKI_HDL = 0x0A000000, // GT_CNS_INT -- constant is a pinvoke calli handle + GTF_ICON_TOKEN_HDL = 0x0B000000, // GT_CNS_INT -- constant is a token handle (other than class, method or field) + GTF_ICON_TLS_HDL = 0x0C000000, // GT_CNS_INT -- constant is a TLS ref with offset + GTF_ICON_FTN_ADDR = 0x0D000000, // GT_CNS_INT -- constant is a function address + GTF_ICON_CIDMID_HDL = 0x0E000000, // GT_CNS_INT -- constant is a class ID or a module ID + GTF_ICON_BBC_PTR = 0x0F000000, // GT_CNS_INT -- constant is a basic block count pointer + GTF_ICON_STATIC_BOX_PTR = 0x10000000, // GT_CNS_INT -- constant is an address of the box for a STATIC_IN_HEAP field + + // GTF_ICON_REUSE_REG_VAL = 0x00800000 // GT_CNS_INT -- GTF_REUSE_REG_VAL, defined above + GTF_ICON_FIELD_OFF = 0x00400000, // GT_CNS_INT -- constant is a field offset + GTF_ICON_SIMD_COUNT = 0x00200000, // GT_CNS_INT -- constant is Vector.Count + GTF_ICON_INITCLASS = 0x00100000, // GT_CNS_INT -- Constant is used to access a static that requires preceding // class/static init helper. In some cases, the constant is // the address of the static field itself, and in other cases // there's an extra layer of indirection and it is the address @@ -2271,9 +2272,8 @@ struct GenTree // hot code, as it affords better opportunities for inlining and acheives shorter dynamic path lengths when // deciding how operands need to be accessed. // - // Note that this function does not respect `GTF_REVERSE_OPS` and `gtEvalSizeFirst`. This is always safe in LIR, - // but may be dangerous in HIR if for some reason you need to visit operands in the order in which they will - // execute. + // Note that this function does not respect `GTF_REVERSE_OPS`. This is always safe in LIR, but may be dangerous + // in HIR if for some reason you need to visit operands in the order in which they will execute. template void VisitOperands(TVisitor visitor); @@ -4965,11 +4965,14 @@ struct GenTreeFptrVal : public GenTree { CORINFO_METHOD_HANDLE gtFptrMethod; + bool gtFptrDelegateTarget; + #ifdef FEATURE_READYTORUN CORINFO_CONST_LOOKUP gtEntryPoint; #endif - GenTreeFptrVal(var_types type, CORINFO_METHOD_HANDLE meth) : GenTree(GT_FTN_ADDR, type), gtFptrMethod(meth) + GenTreeFptrVal(var_types type, CORINFO_METHOD_HANDLE meth) + : GenTree(GT_FTN_ADDR, type), gtFptrMethod(meth), gtFptrDelegateTarget(false) { #ifdef FEATURE_READYTORUN gtEntryPoint.addr = nullptr; @@ -6254,10 +6257,9 @@ struct GenTreeDynBlk : public GenTreeBlk { public: GenTree* gtDynamicSize; - bool gtEvalSizeFirst; GenTreeDynBlk(GenTree* addr, GenTree* dynamicSize) - : GenTreeBlk(GT_DYN_BLK, TYP_STRUCT, addr, nullptr), gtDynamicSize(dynamicSize), gtEvalSizeFirst(false) + : GenTreeBlk(GT_DYN_BLK, TYP_STRUCT, addr, nullptr), gtDynamicSize(dynamicSize) { // Conservatively the 'addr' could be null or point into the global heap. gtFlags |= GTF_EXCEPT | GTF_GLOB_REF; @@ -6624,7 +6626,7 @@ struct Statement // The statement nodes are doubly-linked. The first statement node in a block points // to the last node in the block via its `m_prev` link. Note that the last statement node - // does not point to the first: it's `m_next == nullptr`; that is, the list is not fully circular. + // does not point to the first: it has `m_next == nullptr`; that is, the list is not fully circular. Statement* m_next; Statement* m_prev; diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index ad45f810ad85d7..6f6462d6743bd0 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -292,7 +292,7 @@ GTNODE(JMPTABLE , GenTree ,0, (GTK_LEAF|GTK_NOCONTAIN)) // Ge GTNODE(SWITCH_TABLE , GenTreeOp ,0, (GTK_BINOP|GTK_NOVALUE)) // Jump Table based switch construct #ifdef TARGET_ARM64 GTNODE(ADDEX, GenTreeOp ,0, GTK_BINOP) // Add with sign/zero extension -GTNODE(BFIZ , GenTreeOp ,0, GTK_BINOP) // Bitfield Insert in Zero +GTNODE(BFIZ , GenTreeOp ,0, GTK_BINOP) // Bitfield Insert in Zero #endif //----------------------------------------------------------------------------- diff --git a/src/coreclr/jit/hwintrinsicarm64.cpp b/src/coreclr/jit/hwintrinsicarm64.cpp index 7fea81a34e2c12..1a08a26f78dc9e 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -308,8 +308,22 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, var_types retType, unsigned simdSize) { - HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsic); - int numArgs = sig->numArgs; + const HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsic); + const int numArgs = sig->numArgs; + + // The vast majority of "special" intrinsics are Vector64/Vector128 methods. + // The only exception is ArmBase.Yield which should be treated differently. + if (intrinsic == NI_ArmBase_Yield) + { + assert(sig->numArgs == 0); + assert(JITtype2varType(sig->retType) == TYP_VOID); + assert(simdSize == 0); + + return gtNewScalarHWIntrinsicNode(TYP_VOID, intrinsic); + } + + assert(category != HW_Category_Scalar); + assert(!HWIntrinsicInfo::isScalarIsa(HWIntrinsicInfo::lookupIsa(intrinsic))); if (!featureSIMD || !IsBaselineSimdIsaSupported()) { @@ -318,13 +332,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(numArgs >= 0); - var_types simdBaseType = TYP_UNKNOWN; - - if (intrinsic != NI_ArmBase_Yield) - { - simdBaseType = JitType2PreciseVarType(simdBaseJitType); - assert(varTypeIsArithmetic(simdBaseType)); - } + const var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType); + assert(varTypeIsArithmetic(simdBaseType)); GenTree* retNode = nullptr; GenTree* op1 = nullptr; @@ -333,16 +342,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, switch (intrinsic) { - case NI_ArmBase_Yield: - { - assert(sig->numArgs == 0); - assert(JITtype2varType(sig->retType) == TYP_VOID); - assert(simdSize == 0); - - retNode = gtNewScalarHWIntrinsicNode(TYP_VOID, intrinsic); - break; - } - case NI_Vector64_Abs: case NI_Vector128_Abs: { diff --git a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp index bb6a6daa815d8b..2bbe2835816fcf 100644 --- a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp @@ -2034,9 +2034,9 @@ void CodeGen::genFMAIntrinsic(GenTreeHWIntrinsic* node) NamedIntrinsic intrinsicId = node->GetHWIntrinsicId(); var_types baseType = node->GetSimdBaseType(); emitAttr attr = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->GetSimdSize())); - instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType); // 213 form - instruction _132form = (instruction)(ins - 1); - instruction _231form = (instruction)(ins + 1); + instruction _213form = HWIntrinsicInfo::lookupIns(intrinsicId, baseType); // 213 form + instruction _132form = (instruction)(_213form - 1); + instruction _231form = (instruction)(_213form + 1); GenTree* op1 = node->Op(1); GenTree* op2 = node->Op(2); GenTree* op3 = node->Op(3); @@ -2058,57 +2058,82 @@ void CodeGen::genFMAIntrinsic(GenTreeHWIntrinsic* node) // Intrinsics with CopyUpperBits semantics cannot have op1 be contained assert(!copiesUpperBits || !op1->isContained()); + // We need to keep this in sync with lsraxarch.cpp + // Ideally we'd actually swap the operands in lsra and simplify codegen + // but its a bit more complicated to do so for many operands as well + // as being complicated to tell codegen how to pick the right instruction + + instruction ins = INS_invalid; + if (op1->isContained() || op1->isUsedFromSpillTemp()) { + // targetReg == op3NodeReg or targetReg == ? + // op3 = ([op1] * op2) + op3 + // 231 form: XMM1 = (XMM2 * [XMM3]) + XMM1 + ins = _231form; + std::swap(emitOp1, emitOp3); + if (targetReg == op2NodeReg) { - std::swap(emitOp1, emitOp2); // op2 = ([op1] * op2) + op3 // 132 form: XMM1 = (XMM1 * [XMM3]) + XMM2 ins = _132form; - std::swap(emitOp2, emitOp3); + std::swap(emitOp1, emitOp2); } - else + } + else if (op3->isContained() || op3->isUsedFromSpillTemp()) + { + // targetReg could be op1NodeReg, op2NodeReg, or not equal to any op + // op1 = (op1 * op2) + [op3] or op2 = (op1 * op2) + [op3] + // ? = (op1 * op2) + [op3] or ? = (op1 * op2) + op3 + // 213 form: XMM1 = (XMM2 * XMM1) + [XMM3] + ins = _213form; + + if (!copiesUpperBits && (targetReg == op2NodeReg)) { - // targetReg == op3NodeReg or targetReg == ? - // op3 = ([op1] * op2) + op3 - // 231 form: XMM1 = (XMM2 * [XMM3]) + XMM1 - ins = _231form; - std::swap(emitOp1, emitOp3); + // op2 = (op1 * op2) + [op3] + // 213 form: XMM1 = (XMM2 * XMM1) + [XMM3] + std::swap(emitOp1, emitOp2); } } else if (op2->isContained() || op2->isUsedFromSpillTemp()) { + // targetReg == op1NodeReg or targetReg == ? + // op1 = (op1 * [op2]) + op3 + // 132 form: XMM1 = (XMM1 * [XMM3]) + XMM2 + ins = _132form; + std::swap(emitOp2, emitOp3); + if (!copiesUpperBits && (targetReg == op3NodeReg)) { // op3 = (op1 * [op2]) + op3 // 231 form: XMM1 = (XMM2 * [XMM3]) + XMM1 ins = _231form; - std::swap(emitOp1, emitOp3); - } - else - { - // targetReg == op1NodeReg or targetReg == ? - // op1 = (op1 * [op2]) + op3 - // 132 form: XMM1 = (XMM1 * [XMM3]) + XMM2 - ins = _132form; + std::swap(emitOp1, emitOp2); } - std::swap(emitOp2, emitOp3); } else { - // targetReg could be op1NodeReg, op2NodeReg, or not equal to any op - // op1 = (op1 * op2) + [op3] or op2 = (op1 * op2) + [op3] - // ? = (op1 * op2) + [op3] or ? = (op1 * op2) + op3 - // 213 form: XMM1 = (XMM2 * XMM1) + [XMM3] - if (!copiesUpperBits && (targetReg == op2NodeReg)) + // When we don't have a contained operand we still want to + // preference based on the target register if possible. + + if (targetReg == op2NodeReg) { - // op2 = (op1 * op2) + [op3] - // 213 form: XMM1 = (XMM2 * XMM1) + [XMM3] + ins = _213form; std::swap(emitOp1, emitOp2); } + else if (targetReg == op3NodeReg) + { + ins = _231form; + std::swap(emitOp1, emitOp3); + } + else + { + ins = _213form; + } } + assert(ins != INS_invalid); genHWIntrinsic_R_R_R_RM(ins, attr, targetReg, emitOp1->GetRegNum(), emitOp2->GetRegNum(), emitOp3); genProduceReg(node); } diff --git a/src/coreclr/jit/hwintrinsiclistarm64.h b/src/coreclr/jit/hwintrinsiclistarm64.h index c7e49b91a05be8..65879aff4024c3 100644 --- a/src/coreclr/jit/hwintrinsiclistarm64.h +++ b/src/coreclr/jit/hwintrinsiclistarm64.h @@ -627,7 +627,7 @@ HARDWARE_INTRINSIC(Aes, PolynomialMultiplyWideningUpper, // Base Intrinsics HARDWARE_INTRINSIC(ArmBase, LeadingZeroCount, 0, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_clz, INS_clz, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoFloatingPointUsed) HARDWARE_INTRINSIC(ArmBase, ReverseElementBits, 0, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_rbit, INS_rbit, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_NoFloatingPointUsed) -HARDWARE_INTRINSIC(ArmBase, Yield, 0, 0, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport) +HARDWARE_INTRINSIC(ArmBase, Yield, 0, 0, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_NoFloatingPointUsed|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport) // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // ISA Function name SIMD size Number of arguments Instructions Category Flags diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 70a5cf39b544a2..dc4f56eb4b469f 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -508,7 +508,7 @@ inline void Compiler::impAppendStmtCheck(Statement* stmt, unsigned chkLevel) unsigned lclNum = tree->AsOp()->gtOp1->AsLclVarCommon()->GetLclNum(); for (unsigned level = 0; level < chkLevel; level++) { - assert(!gtHasRef(verCurrentState.esStack[level].val, lclNum, false)); + assert(!gtHasRef(verCurrentState.esStack[level].val, lclNum)); assert(!lvaTable[lclNum].IsAddressExposed() || (verCurrentState.esStack[level].val->gtFlags & GTF_SIDE_EFFECT) == 0); } @@ -2757,7 +2757,7 @@ void Compiler::impSpillLclRefs(ssize_t lclNum) /* Skip the tree if it doesn't have an affected reference, unless xcptnCaught */ - if (xcptnCaught || gtHasRef(tree, lclNum, false)) + if (xcptnCaught || gtHasRef(tree, lclNum)) { impSpillStackEntry(level, BAD_VAR_NUM DEBUGARG(false) DEBUGARG("impSpillLclRefs")); } @@ -3648,17 +3648,8 @@ GenTree* Compiler::impInitializeArrayIntrinsic(CORINFO_SIG_INFO* sig) const char* Compiler::impGetIntrinsicName(CorInfoIntrinsics intrinsicID) { static const char* const intrinsicNameMap[CORINFO_INTRINSIC_Count] = { - "CORINFO_INTRINSIC_Array_Get", - "CORINFO_INTRINSIC_Array_Address", - "CORINFO_INTRINSIC_Array_Set", - "CORINFO_INTRINSIC_RTH_GetValueInternal", - "CORINFO_INTRINSIC_Object_GetType", - "CORINFO_INTRINSIC_StubHelpers_GetStubContext", - "CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr", - "CORINFO_INTRINSIC_StubHelpers_NextCallReturnAddress", - "CORINFO_INTRINSIC_ByReference_Ctor", - "CORINFO_INTRINSIC_ByReference_Value", - "CORINFO_INTRINSIC_GetRawHandle", + "CORINFO_INTRINSIC_Array_Get", "CORINFO_INTRINSIC_Array_Address", "CORINFO_INTRINSIC_Array_Set", + "CORINFO_INTRINSIC_ByReference_Ctor", "CORINFO_INTRINSIC_ByReference_Value", "CORINFO_INTRINSIC_GetRawHandle", }; if ((0 <= intrinsicID) && (intrinsicID < CORINFO_INTRINSIC_Count)) @@ -3844,6 +3835,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_THIS_TRANSFORM constraintCallThisTransform, CorInfoIntrinsics* pIntrinsicID, + NamedIntrinsic* pIntrinsicName, bool* isSpecialIntrinsic) { assert((methodFlags & (CORINFO_FLG_INTRINSIC | CORINFO_FLG_JIT_INTRINSIC)) != 0); @@ -3919,24 +3911,16 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, } } - *pIntrinsicID = intrinsicID; + *pIntrinsicID = intrinsicID; + *pIntrinsicName = ni; - if (intrinsicID == CORINFO_INTRINSIC_StubHelpers_GetStubContext) + if (ni == NI_System_StubHelpers_GetStubContext) { // must be done regardless of DbgCode and MinOpts return gtNewLclvNode(lvaStubArgumentVar, TYP_I_IMPL); } -#ifdef TARGET_64BIT - if (intrinsicID == CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr) - { - // must be done regardless of DbgCode and MinOpts - return gtNewOperNode(GT_ADDR, TYP_I_IMPL, gtNewLclvNode(lvaStubArgumentVar, TYP_I_IMPL)); - } -#else - assert(intrinsicID != CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr); -#endif - if (intrinsicID == CORINFO_INTRINSIC_StubHelpers_NextCallReturnAddress) + if (ni == NI_System_StubHelpers_NextCallReturnAddress) { // For now we just avoid inlining anything into these methods since // this intrinsic is only rarely used. We could do this better if we @@ -3982,127 +3966,6 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, retNode = impArrayAccessIntrinsic(clsHnd, sig, memberRef, readonlyCall, intrinsicID); break; - case CORINFO_INTRINSIC_RTH_GetValueInternal: - op1 = impStackTop(0).val; - if (op1->gtOper == GT_CALL && (op1->AsCall()->gtCallType == CT_HELPER) && - gtIsTypeHandleToRuntimeTypeHandleHelper(op1->AsCall())) - { - // Old tree - // Helper-RuntimeTypeHandle -> TreeToGetNativeTypeHandle - // - // New tree - // TreeToGetNativeTypeHandle - - // Remove call to helper and return the native TypeHandle pointer that was the parameter - // to that helper. - - op1 = impPopStack().val; - - // Get native TypeHandle argument to old helper - GenTreeCall::Use* arg = op1->AsCall()->gtCallArgs; - assert(arg->GetNext() == nullptr); - op1 = arg->GetNode(); - retNode = op1; - } - // Call the regular function. - break; - - case CORINFO_INTRINSIC_Object_GetType: - { - JITDUMP("\n impIntrinsic: call to Object.GetType\n"); - op1 = impStackTop(0).val; - - // If we're calling GetType on a boxed value, just get the type directly. - if (op1->IsBoxedValue()) - { - JITDUMP("Attempting to optimize box(...).getType() to direct type construction\n"); - - // Try and clean up the box. Obtain the handle we - // were going to pass to the newobj. - GenTree* boxTypeHandle = gtTryRemoveBoxUpstreamEffects(op1, BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE); - - if (boxTypeHandle != nullptr) - { - // Note we don't need to play the TYP_STRUCT games here like - // do for LDTOKEN since the return value of this operator is Type, - // not RuntimeTypeHandle. - impPopStack(); - GenTreeCall::Use* helperArgs = gtNewCallArgs(boxTypeHandle); - GenTree* runtimeType = - gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE, TYP_REF, helperArgs); - retNode = runtimeType; - } - } - - // If we have a constrained callvirt with a "box this" transform - // we know we have a value class and hence an exact type. - // - // If so, instead of boxing and then extracting the type, just - // construct the type directly. - if ((retNode == nullptr) && (pConstrainedResolvedToken != nullptr) && - (constraintCallThisTransform == CORINFO_BOX_THIS)) - { - // Ensure this is one of the is simple box cases (in particular, rule out nullables). - const CorInfoHelpFunc boxHelper = info.compCompHnd->getBoxHelper(pConstrainedResolvedToken->hClass); - const bool isSafeToOptimize = (boxHelper == CORINFO_HELP_BOX); - - if (isSafeToOptimize) - { - JITDUMP("Optimizing constrained box-this obj.getType() to direct type construction\n"); - impPopStack(); - GenTree* typeHandleOp = - impTokenToHandle(pConstrainedResolvedToken, nullptr, true /* mustRestoreHandle */); - if (typeHandleOp == nullptr) - { - assert(compDonotInline()); - return nullptr; - } - GenTreeCall::Use* helperArgs = gtNewCallArgs(typeHandleOp); - GenTree* runtimeType = - gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE, TYP_REF, helperArgs); - retNode = runtimeType; - } - } - -#ifdef DEBUG - if (retNode != nullptr) - { - JITDUMP("Optimized result for call to GetType is\n"); - if (verbose) - { - gtDispTree(retNode); - } - } -#endif - - // Else expand as an intrinsic, unless the call is constrained, - // in which case we defer expansion to allow impImportCall do the - // special constraint processing. - if ((retNode == nullptr) && (pConstrainedResolvedToken == nullptr)) - { - JITDUMP("Expanding as special intrinsic\n"); - impPopStack(); - op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, intrinsicID, ni, method); - - // Set the CALL flag to indicate that the operator is implemented by a call. - // Set also the EXCEPTION flag because the native implementation of - // CORINFO_INTRINSIC_Object_GetType intrinsic can throw NullReferenceException. - op1->gtFlags |= (GTF_CALL | GTF_EXCEPT); - retNode = op1; - // Might be further optimizable, so arrange to leave a mark behind - isSpecial = true; - } - - if (retNode == nullptr) - { - JITDUMP("Leaving as normal call\n"); - // Might be further optimizable, so arrange to leave a mark behind - isSpecial = true; - } - - break; - } - // Implement ByReference Ctor. This wraps the assignment of the ref into a byref-like field // in a value type. The canonical example of this is Span. In effect this is just a // substitution. The parameter byref will be assigned into the newly allocated object. @@ -4318,6 +4181,33 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, break; } + case NI_System_RuntimeTypeHandle_GetValueInternal: + { + GenTree* op1 = impStackTop(0).val; + if (op1->gtOper == GT_CALL && (op1->AsCall()->gtCallType == CT_HELPER) && + gtIsTypeHandleToRuntimeTypeHandleHelper(op1->AsCall())) + { + // Old tree + // Helper-RuntimeTypeHandle -> TreeToGetNativeTypeHandle + // + // New tree + // TreeToGetNativeTypeHandle + + // Remove call to helper and return the native TypeHandle pointer that was the parameter + // to that helper. + + op1 = impPopStack().val; + + // Get native TypeHandle argument to old helper + GenTreeCall::Use* arg = op1->AsCall()->gtCallArgs; + assert(arg->GetNext() == nullptr); + op1 = arg->GetNode(); + retNode = op1; + } + // Call the regular function. + break; + } + case NI_System_Type_GetTypeFromHandle: { GenTree* op1 = impStackTop(0).val; @@ -4678,6 +4568,103 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, break; } + case NI_System_Object_GetType: + { + JITDUMP("\n impIntrinsic: call to Object.GetType\n"); + GenTree* op1 = impStackTop(0).val; + + // If we're calling GetType on a boxed value, just get the type directly. + if (op1->IsBoxedValue()) + { + JITDUMP("Attempting to optimize box(...).getType() to direct type construction\n"); + + // Try and clean up the box. Obtain the handle we + // were going to pass to the newobj. + GenTree* boxTypeHandle = gtTryRemoveBoxUpstreamEffects(op1, BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE); + + if (boxTypeHandle != nullptr) + { + // Note we don't need to play the TYP_STRUCT games here like + // do for LDTOKEN since the return value of this operator is Type, + // not RuntimeTypeHandle. + impPopStack(); + GenTreeCall::Use* helperArgs = gtNewCallArgs(boxTypeHandle); + GenTree* runtimeType = + gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE, TYP_REF, helperArgs); + retNode = runtimeType; + } + } + + // If we have a constrained callvirt with a "box this" transform + // we know we have a value class and hence an exact type. + // + // If so, instead of boxing and then extracting the type, just + // construct the type directly. + if ((retNode == nullptr) && (pConstrainedResolvedToken != nullptr) && + (constraintCallThisTransform == CORINFO_BOX_THIS)) + { + // Ensure this is one of the is simple box cases (in particular, rule out nullables). + const CorInfoHelpFunc boxHelper = info.compCompHnd->getBoxHelper(pConstrainedResolvedToken->hClass); + const bool isSafeToOptimize = (boxHelper == CORINFO_HELP_BOX); + + if (isSafeToOptimize) + { + JITDUMP("Optimizing constrained box-this obj.getType() to direct type construction\n"); + impPopStack(); + GenTree* typeHandleOp = + impTokenToHandle(pConstrainedResolvedToken, nullptr, true /* mustRestoreHandle */); + if (typeHandleOp == nullptr) + { + assert(compDonotInline()); + return nullptr; + } + GenTreeCall::Use* helperArgs = gtNewCallArgs(typeHandleOp); + GenTree* runtimeType = + gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE, TYP_REF, helperArgs); + retNode = runtimeType; + } + } + +#ifdef DEBUG + if (retNode != nullptr) + { + JITDUMP("Optimized result for call to GetType is\n"); + if (verbose) + { + gtDispTree(retNode); + } + } +#endif + + // Else expand as an intrinsic, unless the call is constrained, + // in which case we defer expansion to allow impImportCall do the + // special constraint processing. + if ((retNode == nullptr) && (pConstrainedResolvedToken == nullptr)) + { + JITDUMP("Expanding as special intrinsic\n"); + impPopStack(); + op1 = new (this, GT_INTRINSIC) + GenTreeIntrinsic(genActualType(callType), op1, intrinsicID, ni, method); + + // Set the CALL flag to indicate that the operator is implemented by a call. + // Set also the EXCEPTION flag because the native implementation of + // NI_System_Object_GetType intrinsic can throw NullReferenceException. + op1->gtFlags |= (GTF_CALL | GTF_EXCEPT); + retNode = op1; + // Might be further optimizable, so arrange to leave a mark behind + isSpecial = true; + } + + if (retNode == nullptr) + { + JITDUMP("Leaving as normal call\n"); + // Might be further optimizable, so arrange to leave a mark behind + isSpecial = true; + } + + break; + } + case NI_System_Array_GetLength: case NI_System_Array_GetLowerBound: case NI_System_Array_GetUpperBound: @@ -5206,6 +5193,17 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_System_Object_MemberwiseClone; } + else if (strcmp(methodName, "GetType") == 0) + { + result = NI_System_Object_GetType; + } + } + else if (strcmp(className, "RuntimeTypeHandle") == 0) + { + if (strcmp(methodName, "GetValueInternal") == 0) + { + result = NI_System_RuntimeTypeHandle_GetValueInternal; + } } else if (strcmp(className, "Type") == 0) { @@ -5420,6 +5418,20 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) } } } + else if (strcmp(namespaceName, "System.StubHelpers") == 0) + { + if (strcmp(className, "StubHelpers") == 0) + { + if (strcmp(methodName, "GetStubContext") == 0) + { + result = NI_System_StubHelpers_GetStubContext; + } + else if (strcmp(methodName, "NextCallReturnAddress") == 0) + { + result = NI_System_StubHelpers_NextCallReturnAddress; + } + } + } if (result == NI_Illegal) { @@ -8279,15 +8291,23 @@ GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedT void** pFldAddr = nullptr; void* fldAddr = info.compCompHnd->getFieldAddress(pResolvedToken->hField, (void**)&pFldAddr); - // We should always be able to access this static's address directly - // + // We should always be able to access this static's address directly. assert(pFldAddr == nullptr); + GenTreeFlags handleKind; + if ((pFieldInfo->fieldFlags & CORINFO_FLG_FIELD_STATIC_IN_HEAP) != 0) + { + handleKind = GTF_ICON_STATIC_BOX_PTR; + } + else + { + handleKind = GTF_ICON_STATIC_HDL; + } + FieldSeqNode* fldSeq = GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField); - /* Create the data member node */ - op1 = gtNewIconHandleNode(pFldAddr == nullptr ? (size_t)fldAddr : (size_t)pFldAddr, GTF_ICON_STATIC_HDL, - fldSeq); + // Create the address node. + op1 = gtNewIconHandleNode((size_t)fldAddr, handleKind, fldSeq); #ifdef DEBUG op1->AsIntCon()->gtTargetHandle = op1->AsIntCon()->gtIconVal; #endif @@ -8338,6 +8358,7 @@ GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedT if (pFieldInfo->fieldFlags & CORINFO_FLG_FIELD_STATIC_IN_HEAP) { op1 = gtNewOperNode(GT_IND, TYP_REF, op1); + op1->gtFlags |= (GTF_IND_INVARIANT | GTF_IND_NONFAULTING | GTF_IND_NONNULL); FieldSeqNode* fldSeq = GetFieldSeqStore()->CreateSingleton(FieldSeqStore::FirstElemPseudoField); @@ -8761,6 +8782,7 @@ var_types Compiler::impImportCall(OPCODE opcode, else // (opcode != CEE_CALLI) { CorInfoIntrinsics intrinsicID = CORINFO_INTRINSIC_Count; + NamedIntrinsic ni = NI_Illegal; // Passing CORINFO_CALLINFO_ALLOWINSTPARAM indicates that this JIT is prepared to // supply the instantiation parameters necessary to make direct calls to underlying @@ -8848,7 +8870,7 @@ var_types Compiler::impImportCall(OPCODE opcode, const bool isTailCall = canTailCall && (tailCallFlags != 0); call = impIntrinsic(newobjThis, clsHnd, methHnd, sig, mflags, pResolvedToken->token, isReadonlyCall, - isTailCall, pConstrainedResolvedToken, callInfo->thisTransform, &intrinsicID, + isTailCall, pConstrainedResolvedToken, callInfo->thisTransform, &intrinsicID, &ni, &isSpecialIntrinsic); if (compDonotInline()) @@ -9076,7 +9098,7 @@ var_types Compiler::impImportCall(OPCODE opcode, // TODO-CQ: JIT64 does not introduce the null check for many more helper calls // and intrinsics. if (callInfo->nullInstanceCheck && - !((mflags & CORINFO_FLG_INTRINSIC) != 0 && (intrinsicID == CORINFO_INTRINSIC_Object_GetType))) + !((mflags & CORINFO_FLG_INTRINSIC) != 0 && (ni == NI_System_Object_GetType))) { call->gtFlags |= GTF_CALL_NULLCHECK; } @@ -18547,7 +18569,7 @@ void Compiler::impImportBlock(BasicBlock* block) are spilling to the temps already used by a previous block), we need to spill addStmt */ - if (addStmt != nullptr && !newTemps && gtHasRef(addStmt->GetRootNode(), tempNum, false)) + if (addStmt != nullptr && !newTemps && gtHasRef(addStmt->GetRootNode(), tempNum)) { GenTree* addTree = addStmt->GetRootNode(); @@ -18558,7 +18580,7 @@ void Compiler::impImportBlock(BasicBlock* block) var_types type = genActualType(relOp->AsOp()->gtOp1->TypeGet()); - if (gtHasRef(relOp->AsOp()->gtOp1, tempNum, false)) + if (gtHasRef(relOp->AsOp()->gtOp1, tempNum)) { unsigned temp = lvaGrabTemp(true DEBUGARG("spill addStmt JTRUE ref Op1")); impAssignTempGen(temp, relOp->AsOp()->gtOp1, level); @@ -18566,7 +18588,7 @@ void Compiler::impImportBlock(BasicBlock* block) relOp->AsOp()->gtOp1 = gtNewLclvNode(temp, type); } - if (gtHasRef(relOp->AsOp()->gtOp2, tempNum, false)) + if (gtHasRef(relOp->AsOp()->gtOp2, tempNum)) { unsigned temp = lvaGrabTemp(true DEBUGARG("spill addStmt JTRUE ref Op2")); impAssignTempGen(temp, relOp->AsOp()->gtOp2, level); diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 06211a597c8ed6..5f9fa62008b304 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -231,6 +231,8 @@ CONFIG_INTEGER(JitDumpFgConstrained, W("JitDumpFgConstrained"), 1) // 0 == don't // linear layout CONFIG_INTEGER(JitDumpFgBlockID, W("JitDumpFgBlockID"), 0) // 0 == display block with bbNum; 1 == display with both // bbNum and bbID +CONFIG_INTEGER(JitDumpFgBlockFlags, W("JitDumpFgBlockFlags"), 0) // 0 == don't display block flags; 1 == display flags +CONFIG_INTEGER(JitDumpFgLoopFlags, W("JitDumpFgLoopFlags"), 0) // 0 == don't display loop flags; 1 == display flags CONFIG_STRING(JitDumpPreciseDebugInfoFile, W("JitDumpPreciseDebugInfoFile")) CONFIG_INTEGER(JitDisasmWithDebugInfo, W("JitDisasmWithDebugInfo"), 0) diff --git a/src/coreclr/jit/jiteh.cpp b/src/coreclr/jit/jiteh.cpp index c0fd2bf9581ce3..10e7652a6caa0c 100644 --- a/src/coreclr/jit/jiteh.cpp +++ b/src/coreclr/jit/jiteh.cpp @@ -4433,8 +4433,8 @@ void Compiler::fgExtendEHRegionBefore(BasicBlock* block) bPrev->bbRefs++; // If this is a handler for a filter, the last block of the filter will end with - // a BBJ_EJFILTERRET block that has a bbJumpDest that jumps to the first block of - // it's handler. So we need to update it to keep things in sync. + // a BBJ_EHFILTERRET block that has a bbJumpDest that jumps to the first block of + // its handler. So we need to update it to keep things in sync. // if (HBtab->HasFilter()) { diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index a1c71f932f5a65..09f42b6e6f6659 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -1604,7 +1604,7 @@ bool Compiler::optIsLoopClonable(unsigned loopInd) if (end->bbJumpDest != beg) { - JITDUMP("Loop cloning: rejecting loop " FMT_LP ". Branch at loop 'end' not looping to 'begin'.\n", loopInd); + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". Branch at loop 'bottom' not looping to 'top'.\n", loopInd); return false; } diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 1c9febeb131440..2467a00accdaa7 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -1257,7 +1257,12 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, fgArgTabEntry* inf } else if (!arg->OperIs(GT_FIELD_LIST)) { +#ifdef TARGET_ARM + assert((info->GetStackSlotsNumber() == 1) || + ((arg->TypeGet() == TYP_DOUBLE) && (info->GetStackSlotsNumber() == 2))); +#else assert(varTypeIsSIMD(arg) || (info->GetStackSlotsNumber() == 1)); +#endif } } #endif // FEATURE_PUT_STRUCT_ARG_STK diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index ca169600e83f83..4705ee6798595e 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -2305,6 +2305,11 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree) // Intrinsics with CopyUpperBits semantics must have op1 as target assert(containedOpNum != 1 || !copiesUpperBits); + // We need to keep this in sync with hwintrinsiccodegenxarch.cpp + // Ideally we'd actually swap the operands here and simplify codegen + // but its a bit more complicated to do so for many operands as well + // as being complicated to tell codegen how to pick the right instruction + if (containedOpNum == 1) { // https://github.com/dotnet/runtime/issues/62215 @@ -2316,7 +2321,7 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree) if (resultOpNum == 2) { // op2 = ([op1] * op2) + op3 - std::swap(emitOp2, emitOp3); + std::swap(emitOp1, emitOp2); } } else if (containedOpNum == 3) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index f8019ed88eec81..288f7c16f4d9b6 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -6272,13 +6272,25 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac) // The address is not directly addressible, so force it into a // constant, so we handle it properly - GenTree* addr = gtNewIconHandleNode((size_t)fldAddr, GTF_ICON_STATIC_HDL); - addr->gtType = TYP_I_IMPL; - FieldSeqNode* fieldSeq = - fieldMayOverlap ? FieldSeqStore::NotAField() : GetFieldSeqStore()->CreateSingleton(symHnd); - addr->AsIntCon()->gtFieldSeq = fieldSeq; - // Translate GTF_FLD_INITCLASS to GTF_ICON_INITCLASS - if ((tree->gtFlags & GTF_FLD_INITCLASS) != 0) + bool isBoxedStatic = gtIsStaticFieldPtrToBoxedStruct(tree->TypeGet(), symHnd); + GenTreeFlags handleKind = GTF_EMPTY; + if (isBoxedStatic) + { + handleKind = GTF_ICON_STATIC_BOX_PTR; + } + else if (isStaticReadOnlyInited) + { + handleKind = GTF_ICON_CONST_PTR; + } + else + { + handleKind = GTF_ICON_STATIC_HDL; + } + FieldSeqNode* fieldSeq = GetFieldSeqStore()->CreateSingleton(symHnd); + GenTree* addr = gtNewIconHandleNode((size_t)fldAddr, handleKind, fieldSeq); + + // Translate GTF_FLD_INITCLASS to GTF_ICON_INITCLASS, if we need to. + if (((tree->gtFlags & GTF_FLD_INITCLASS) != 0) && !isStaticReadOnlyInited) { tree->gtFlags &= ~GTF_FLD_INITCLASS; addr->gtFlags |= GTF_ICON_INITCLASS; @@ -6287,14 +6299,18 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac) tree->SetOper(GT_IND); tree->AsOp()->gtOp1 = addr; - if (isStaticReadOnlyInited) + if (isBoxedStatic) + { + // The box for the static cannot be null, and is logically invariant, since it + // represents (a base for) the static's address. + tree->gtFlags |= (GTF_IND_INVARIANT | GTF_IND_NONFAULTING | GTF_IND_NONNULL); + } + else if (isStaticReadOnlyInited) { JITDUMP("Marking initialized static read-only field '%s' as invariant.\n", eeGetFieldName(symHnd)); // Static readonly field is not null at this point (see getStaticFieldCurrentClass impl). tree->gtFlags |= (GTF_IND_INVARIANT | GTF_IND_NONFAULTING | GTF_IND_NONNULL); - tree->gtFlags &= ~GTF_ICON_INITCLASS; - addr->gtFlags = GTF_ICON_CONST_PTR; } return fgMorphSmpOp(tree); @@ -9555,17 +9571,23 @@ GenTree* Compiler::fgMorphLeaf(GenTree* tree) } else if (tree->gtOper == GT_FTN_ADDR) { + GenTreeFptrVal* fptrValTree = tree->AsFptrVal(); + + // A function pointer address is being used. Let the VM know if this is the + // target of a Delegate or a raw function pointer. + bool isUnsafeFunctionPointer = !fptrValTree->gtFptrDelegateTarget; + CORINFO_CONST_LOOKUP addrInfo; #ifdef FEATURE_READYTORUN - if (tree->AsFptrVal()->gtEntryPoint.addr != nullptr) + if (fptrValTree->gtEntryPoint.addr != nullptr) { - addrInfo = tree->AsFptrVal()->gtEntryPoint; + addrInfo = fptrValTree->gtEntryPoint; } else #endif { - info.compCompHnd->getFunctionFixedEntryPoint(tree->AsFptrVal()->gtFptrMethod, &addrInfo); + info.compCompHnd->getFunctionFixedEntryPoint(fptrValTree->gtFptrMethod, isUnsafeFunctionPointer, &addrInfo); } GenTree* indNode = nullptr; @@ -11447,6 +11469,37 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) { return fgMorphTree(optimizedTree); } + + // Pattern-matching optimization: + // (a % c) ==/!= 0 + // for power-of-2 constant `c` + // => + // a & (c - 1) ==/!= 0 + // For integer `a`, even if negative. + if (opts.OptimizationEnabled() && !optValnumCSE_phase) + { + assert(tree->OperIs(GT_EQ, GT_NE)); + if (op1->OperIs(GT_MOD) && varTypeIsIntegral(op1) && op2->IsIntegralConst(0)) + { + GenTree* op1op2 = op1->AsOp()->gtOp2; + if (op1op2->IsCnsIntOrI()) + { + const ssize_t modValue = op1op2->AsIntCon()->IconValue(); + if (isPow2(modValue)) + { + JITDUMP("\nTransforming:\n"); + DISPTREE(tree); + + op1->SetOper(GT_AND); // Change % => & + op1op2->AsIntConCommon()->SetIconValue(modValue - 1); // Change c => c - 1 + fgUpdateConstTreeValueNumber(op1op2); + + JITDUMP("\ninto:\n"); + DISPTREE(tree); + } + } + } + } } FALLTHROUGH; @@ -13355,30 +13408,6 @@ GenTree* Compiler::fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp) GenTree* op1 = cmp->gtGetOp1(); GenTreeIntConCommon* op2 = cmp->gtGetOp2()->AsIntConCommon(); - // Pattern-matching optimization: - // (a % c) ==/!= 0 - // for power-of-2 constant `c` - // => - // a & (c - 1) ==/!= 0 - // For integer `a`, even if negative. - if (opts.OptimizationEnabled()) - { - if (op1->OperIs(GT_MOD) && varTypeIsIntegral(op1) && op2->IsIntegralConst(0)) - { - GenTree* op1op2 = op1->AsOp()->gtOp2; - if (op1op2->IsCnsIntOrI()) - { - ssize_t modValue = op1op2->AsIntCon()->IconValue(); - if (isPow2(modValue)) - { - op1->SetOper(GT_AND); // Change % => & - op1op2->AsIntConCommon()->SetIconValue(modValue - 1); // Change c => c - 1 - fgUpdateConstTreeValueNumber(op1op2); - } - } - } - } - // Check for "(expr +/- icon1) ==/!= (non-zero-icon2)". if (op2->IsCnsIntOrI() && (op2->IconValue() != 0)) { diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index 62ebb80c089994..5d07e88b576ae7 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -60,6 +60,10 @@ enum NamedIntrinsic : unsigned short NI_System_Array_GetLowerBound, NI_System_Array_GetUpperBound, NI_System_Object_MemberwiseClone, + NI_System_Object_GetType, + NI_System_RuntimeTypeHandle_GetValueInternal, + NI_System_StubHelpers_GetStubContext, + NI_System_StubHelpers_NextCallReturnAddress, NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan, NI_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray, diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 39d2328c2e8d62..43e9616b2192fc 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -413,19 +413,42 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar continue; } - if (block == loop.lpEntry || block == loop.lpBottom) - { - loop.lpFlags |= LPFLG_REMOVED; - continue; - } + // Avoid printing to the JitDump unless we're actually going to change something. + // If we call reportBefore, then we're going to change the loop table, and we should print the + // `reportAfter` info as well. Only print the `reportBefore` info once, if multiple changes to + // the table are made. + INDEBUG(bool reportedBefore = false); + auto reportBefore = [&]() { #ifdef DEBUG - if (verbose) + if (verbose && !reportedBefore) + { + printf("optUpdateLoopsBeforeRemoveBlock " FMT_BB " Before: ", block->bbNum); + optPrintLoopInfo(loopNum); + printf("\n"); + reportedBefore = true; + } +#endif // DEBUG + }; + + auto reportAfter = [&]() { +#ifdef DEBUG + if (verbose && reportedBefore) + { + printf("optUpdateLoopsBeforeRemoveBlock " FMT_BB " After: ", block->bbNum); + optPrintLoopInfo(loopNum); + printf("\n"); + } +#endif // DEBUG + }; + + if (block == loop.lpEntry || block == loop.lpBottom) { - printf("\nUpdateLoopsBeforeRemoveBlock Before: "); - optPrintLoopInfo(loopNum); + reportBefore(); + optMarkLoopRemoved(loopNum); + reportAfter(); + continue; } -#endif /* If the loop is still in the table * any block in the loop must be reachable !!! */ @@ -435,6 +458,7 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar if (loop.lpExit == block) { + reportBefore(); loop.lpExit = nullptr; loop.lpFlags &= ~LPFLG_ONE_EXIT; } @@ -537,23 +561,18 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar if (removeLoop) { - loop.lpFlags |= LPFLG_REMOVED; + reportBefore(); + optMarkLoopRemoved(loopNum); } } else if (loop.lpHead == block) { + reportBefore(); /* The loop has a new head - Just update the loop table */ loop.lpHead = block->bbPrev; } -#ifdef DEBUG - if (verbose) - { - printf("\nUpdateLoopsBeforeRemoveBlock After: "); - optPrintLoopInfo(loopNum); - printf("\n"); - } -#endif + reportAfter(); } if ((skipUnmarkLoop == false) && // @@ -575,43 +594,156 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar * Print loop info in an uniform way. */ -void Compiler::optPrintLoopInfo(unsigned loopInd, - BasicBlock* lpHead, - BasicBlock* lpTop, - BasicBlock* lpEntry, - BasicBlock* lpBottom, - unsigned char lpExitCnt, - BasicBlock* lpExit, - unsigned parentLoop) const +void Compiler::optPrintLoopInfo(const LoopDsc* loop, bool printVerbose /* = false */) { - printf(FMT_LP ", from " FMT_BB " to " FMT_BB " (Head=" FMT_BB ", Entry=" FMT_BB ", ExitCnt=%d", loopInd, - lpTop->bbNum, lpBottom->bbNum, lpHead->bbNum, lpEntry->bbNum, lpExitCnt); + assert(optLoopTable != nullptr); + assert((&optLoopTable[0] <= loop) && (loop < &optLoopTable[optLoopCount])); + + unsigned lnum = (unsigned)(loop - optLoopTable); + assert(lnum < optLoopCount); + assert(&optLoopTable[lnum] == loop); - if (lpExitCnt == 1) + if (loop->lpFlags & LPFLG_REMOVED) { - printf(" at " FMT_BB, lpExit->bbNum); + // If a loop has been removed, it might be dangerous to print its fields (e.g., loop unrolling + // nulls out the lpHead field). + printf(FMT_LP " REMOVED", lnum); + return; } - if (parentLoop != BasicBlock::NOT_IN_LOOP) + printf(FMT_LP ", from " FMT_BB " to " FMT_BB " (Head=" FMT_BB ", Entry=" FMT_BB, lnum, loop->lpTop->bbNum, + loop->lpBottom->bbNum, loop->lpHead->bbNum, loop->lpEntry->bbNum); + + if (loop->lpExitCnt == 1) { - printf(", parent loop = " FMT_LP, parentLoop); + printf(", Exit=" FMT_BB, loop->lpExit->bbNum); + } + else + { + printf(", ExitCnt=%d", loop->lpExitCnt); + } + + if (loop->lpParent != BasicBlock::NOT_IN_LOOP) + { + printf(", parent=" FMT_LP, loop->lpParent); } printf(")"); -} -/***************************************************************************** - * - * Print loop information given the index of the loop in the loop table. - */ + if (printVerbose) + { + if (loop->lpChild != BasicBlock::NOT_IN_LOOP) + { + printf(", child loop = " FMT_LP, loop->lpChild); + } + if (loop->lpSibling != BasicBlock::NOT_IN_LOOP) + { + printf(", sibling loop = " FMT_LP, loop->lpSibling); + } + + // If an iterator loop print the iterator and the initialization. + if (loop->lpFlags & LPFLG_ITER) + { + printf(" [over V%02u", loop->lpIterVar()); + printf(" ("); + printf(GenTree::OpName(loop->lpIterOper())); + printf(" %d)", loop->lpIterConst()); + + if (loop->lpFlags & LPFLG_CONST_INIT) + { + printf(" from %d", loop->lpConstInit); + } + if (loop->lpFlags & LPFLG_VAR_INIT) + { + printf(" from V%02u", loop->lpVarInit); + } + + // If a simple test condition print operator and the limits */ + printf(" %s", GenTree::OpName(loop->lpTestOper())); + + if (loop->lpFlags & LPFLG_CONST_LIMIT) + { + printf(" %d", loop->lpConstLimit()); + if (loop->lpFlags & LPFLG_SIMD_LIMIT) + { + printf(" (simd)"); + } + } + if (loop->lpFlags & LPFLG_VAR_LIMIT) + { + printf(" V%02u", loop->lpVarLimit()); + } + if (loop->lpFlags & LPFLG_ARRLEN_LIMIT) + { + ArrIndex* index = new (getAllocator(CMK_DebugOnly)) ArrIndex(getAllocator(CMK_DebugOnly)); + if (loop->lpArrLenLimit(this, index)) + { + printf(" "); + index->Print(); + printf(".Length"); + } + else + { + printf(" ???.Length"); + } + } + + printf("]"); + } + + // Print the flags + + if (loop->lpContainsCall) + { + printf(" call"); + } + if (loop->lpFlags & LPFLG_HAS_PREHEAD) + { + printf(" prehead"); + } + if (loop->lpFlags & LPFLG_DONT_UNROLL) + { + printf(" !unroll"); + } + if (loop->lpFlags & LPFLG_ASGVARS_YES) + { + printf(" avyes"); + } + if (loop->lpFlags & LPFLG_ASGVARS_INC) + { + printf(" avinc"); + } + } +} -void Compiler::optPrintLoopInfo(unsigned lnum) const +void Compiler::optPrintLoopInfo(unsigned lnum, bool printVerbose /* = false */) { assert(lnum < optLoopCount); - const LoopDsc* ldsc = &optLoopTable[lnum]; + const LoopDsc& loop = optLoopTable[lnum]; + optPrintLoopInfo(&loop, printVerbose); +} + +//------------------------------------------------------------------------ +// optPrintLoopTable: Print the loop table +// +void Compiler::optPrintLoopTable() +{ + printf("\n*************** Natural loop table\n"); + + if (optLoopCount == 0) + { + printf("No loops\n"); + } + else + { + for (unsigned loopInd = 0; loopInd < optLoopCount; loopInd++) + { + optPrintLoopInfo(loopInd, /* verbose */ true); + printf("\n"); + } + } - optPrintLoopInfo(lnum, ldsc->lpHead, ldsc->lpTop, ldsc->lpEntry, ldsc->lpBottom, ldsc->lpExitCnt, ldsc->lpExit, - ldsc->lpParent); + printf("\n"); } #endif // DEBUG @@ -1111,6 +1243,7 @@ bool Compiler::optRecordLoop( { optLoopTable[loopInd].lpLoopHasMemoryHavoc[memoryKind] = false; } + optLoopTable[loopInd].lpContainsCall = false; optLoopTable[loopInd].lpFieldsModified = nullptr; optLoopTable[loopInd].lpArrayElemTypesModified = nullptr; @@ -1229,63 +1362,23 @@ bool Compiler::optRecordLoop( } DONE_LOOP: - DBEXEC(verbose, optPrintLoopRecording(loopInd)); + + bool loopInsertedAtEnd = (loopInd == optLoopCount); optLoopCount++; - return true; -} #ifdef DEBUG -//------------------------------------------------------------------------ -// optPrintLoopRecording: Print a recording of the loop. -// -// Arguments: -// loopInd - loop index. -// -void Compiler::optPrintLoopRecording(unsigned loopInd) const -{ - const LoopDsc& loop = optLoopTable[loopInd]; - - printf("Recorded loop %s", (loopInd != optLoopCount ? "(extended) " : "")); - optPrintLoopInfo(optLoopCount, // Not necessarily the loop index, but the number of loops that have been added. - loop.lpHead, loop.lpTop, loop.lpEntry, loop.lpBottom, loop.lpExitCnt, loop.lpExit); - - // If an iterator loop print the iterator and the initialization. - if (loop.lpFlags & LPFLG_ITER) + if (verbose) { - printf(" [over V%02u", loop.lpIterVar()); - printf(" ("); - printf(GenTree::OpName(loop.lpIterOper())); - printf(" "); - printf("%d )", loop.lpIterConst()); - - if (loop.lpFlags & LPFLG_CONST_INIT) - { - printf(" from %d", loop.lpConstInit); - } - if (loop.lpFlags & LPFLG_VAR_INIT) - { - printf(" from V%02u", loop.lpVarInit); - } - - // If a simple test condition print operator and the limits */ - printf(GenTree::OpName(loop.lpTestOper())); - - if (loop.lpFlags & LPFLG_CONST_LIMIT) - { - printf("%d ", loop.lpConstLimit()); - } - - if (loop.lpFlags & LPFLG_VAR_LIMIT) - { - printf("V%02u ", loop.lpVarLimit()); - } - - printf("]"); + printf("Recorded loop %s", loopInsertedAtEnd ? "" : "(extended) "); + optPrintLoopInfo(loopInd, /* verbose */ true); + printf("\n"); } +#endif // DEBUG - printf("\n"); + return true; } +#ifdef DEBUG void Compiler::optCheckPreds() { for (BasicBlock* const block : Blocks()) @@ -2476,12 +2569,7 @@ void Compiler::optFindNaturalLoops() #ifdef DEBUG if (verbose && (optLoopCount > 0)) { - printf("\nFinal natural loop table:\n"); - for (unsigned loopInd = 0; loopInd < optLoopCount; loopInd++) - { - optPrintLoopInfo(loopInd); - printf("\n"); - } + optPrintLoopTable(); } #endif // DEBUG } @@ -2909,7 +2997,7 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd) // Notes: // A loop contains itself. // -bool Compiler::optLoopContains(unsigned l1, unsigned l2) +bool Compiler::optLoopContains(unsigned l1, unsigned l2) const { assert(l1 < optLoopCount); assert((l2 < optLoopCount) || (l2 == BasicBlock::NOT_IN_LOOP)); @@ -3501,6 +3589,8 @@ PhaseStatus Compiler::optUnrollLoops() /* Look for loop unrolling candidates */ bool change = false; + INDEBUG(int unrollCount = 0); // count of loops unrolled + INDEBUG(int unrollFailures = 0); // count of loops attempted to be unrolled, but failed static const unsigned ITER_LIMIT[COUNT_OPT_CODE + 1] = { 10, // BLENDED_CODE @@ -3568,6 +3658,7 @@ PhaseStatus Compiler::optUnrollLoops() if ((loopFlags & requiredFlags) != requiredFlags) { + // Don't print to the JitDump about this common case. continue; } @@ -3575,6 +3666,7 @@ PhaseStatus Compiler::optUnrollLoops() if (loopFlags & (LPFLG_DONT_UNROLL | LPFLG_REMOVED)) { + // Don't print to the JitDump about this common case. continue; } @@ -3605,11 +3697,13 @@ PhaseStatus Compiler::optUnrollLoops() if (lvaTable[lvar].IsAddressExposed()) { // If the loop iteration variable is address-exposed then bail + JITDUMP("Failed to unroll loop " FMT_LP ": V%02u is address exposed\n", lnum, lvar); continue; } if (lvaTable[lvar].lvIsStructField) { // If the loop iteration variable is a promoted field from a struct then bail + JITDUMP("Failed to unroll loop " FMT_LP ": V%02u is a promoted struct field\n", lnum, lvar); continue; } @@ -3641,6 +3735,7 @@ PhaseStatus Compiler::optUnrollLoops() if (!optComputeLoopRep(lbeg, llim, iterInc, iterOper, iterOperType, testOper, unsTest, dupCond, &totalIter)) { + JITDUMP("Failed to unroll loop " FMT_LP ": not a constant iteration count\n", lnum); continue; } @@ -3648,6 +3743,8 @@ PhaseStatus Compiler::optUnrollLoops() if (totalIter > iterLimit) { + JITDUMP("Failed to unroll loop " FMT_LP ": too many iterations (%d > %d) (heuristic)\n", lnum, totalIter, + iterLimit); continue; } @@ -3668,6 +3765,7 @@ PhaseStatus Compiler::optUnrollLoops() { // Otherwise unroll only if limit is Vector_.Length // (as a heuristic, not for correctness/structural reasons) + JITDUMP("Failed to unroll loop " FMT_LP ": constant limit isn't Vector.Length (heuristic)\n", lnum); continue; } @@ -3676,6 +3774,8 @@ PhaseStatus Compiler::optUnrollLoops() // Don't unroll loops we don't understand. if (incr->gtOper != GT_ASG) { + JITDUMP("Failed to unroll loop " FMT_LP ": unknown increment op (%s)\n", lnum, + GenTree::OpName(incr->gtOper)); continue; } incr = incr->AsOp()->gtOp2; @@ -3717,6 +3817,7 @@ PhaseStatus Compiler::optUnrollLoops() if (block->bbTryIndex != tryIndex) { // Unrolling would require cloning EH regions + JITDUMP("Failed to unroll loop " FMT_LP ": EH constraint\n", lnum); goto DONE_LOOP; } @@ -3741,6 +3842,7 @@ PhaseStatus Compiler::optUnrollLoops() if (fgReturnCount + loopRetCount * (totalIter - 1) > SET_EPILOGCNT_MAX) { // Jit32 GC encoder can't report more than SET_EPILOGCNT_MAX epilogs. + JITDUMP("Failed to unroll loop " FMT_LP ": GC encoder max epilog constraint\n", lnum); goto DONE_LOOP; } #endif // !JIT32_GCENCODER @@ -3756,6 +3858,8 @@ PhaseStatus Compiler::optUnrollLoops() if (unrollCostSz.IsOverflow() || (unrollCostSz.Value() > unrollLimitSz)) { + JITDUMP("Failed to unroll loop " FMT_LP ": size constraint (%d > %d) (heuristic)\n", lnum, + unrollCostSz.Value(), unrollLimitSz); goto DONE_LOOP; } @@ -3765,11 +3869,8 @@ PhaseStatus Compiler::optUnrollLoops() #ifdef DEBUG if (verbose) { - printf("\nUnrolling loop " FMT_BB, head->bbNext->bbNum); - if (head->bbNext->bbNum != bottom->bbNum) - { - printf(".." FMT_BB, bottom->bbNum); - } + printf("\nUnrolling loop "); + optPrintLoopInfo(&optLoopTable[lnum]); printf(" over V%02u from %u to %u unrollCostSz = %d\n\n", lvar, lbeg, llim, unrollCostSz); } #endif @@ -3807,6 +3908,9 @@ PhaseStatus Compiler::optUnrollLoops() bottom->bbNext = oldBottomNext; oldBottomNext->bbPrev = bottom; optLoopTable[lnum].lpFlags |= LPFLG_DONT_UNROLL; + INDEBUG(++unrollFailures); + JITDUMP("Failed to unroll loop " FMT_LP ": block cloning failed on " FMT_BB "\n", lnum, + block->bbNum); goto DONE_LOOP; } @@ -3941,6 +4045,8 @@ PhaseStatus Compiler::optUnrollLoops() // Note if we created new BBJ_RETURNs fgReturnCount += loopRetCount * (totalIter - 1); + + INDEBUG(++unrollCount); } DONE_LOOP:; @@ -3948,8 +4054,33 @@ PhaseStatus Compiler::optUnrollLoops() if (change) { +#ifdef DEBUG + if (verbose) + { + printf("\nFinished unrolling %d loops", unrollCount); + if (unrollFailures > 0) + { + printf(", %d failures due to block cloning", unrollFailures); + } + printf("\n"); + } +#endif // DEBUG + constexpr bool computePreds = true; fgUpdateChangedFlowGraph(computePreds); + + DBEXEC(verbose, fgDispBasicBlocks()); + } + else + { +#ifdef DEBUG + assert(unrollCount == 0); + + if (unrollFailures > 0) + { + printf("\nFinished loop unrolling, %d failures due to block cloning\n", unrollFailures); + } +#endif // DEBUG } #ifdef DEBUG @@ -4629,6 +4760,8 @@ void Compiler::optMarkLoopHeads() { printf("*************** In optMarkLoopHeads()\n"); } + + int loopHeadsMarked = 0; #endif bool hasLoops = false; @@ -4653,12 +4786,14 @@ void Compiler::optMarkLoopHeads() { hasLoops = true; block->bbFlags |= BBF_LOOP_HEAD; + INDEBUG(++loopHeadsMarked); break; // No need to look at more `block` predecessors } } } } + JITDUMP("%d loop heads marked\n", loopHeadsMarked); fgHasLoops = hasLoops; } @@ -5510,7 +5645,8 @@ void Compiler::optPerformHoistExpr(GenTree* origExpr, BasicBlock* exprBb, unsign { printf("\nHoisting a copy of "); printTreeID(origExpr); - printf(" into PreHeader for loop " FMT_LP " <" FMT_BB ".." FMT_BB ">:\n", lnum, optLoopTable[lnum].lpTop->bbNum, + printf(" from " FMT_BB " into PreHeader " FMT_BB " for loop " FMT_LP " <" FMT_BB ".." FMT_BB ">:\n", + exprBb->bbNum, optLoopTable[lnum].lpHead->bbNum, lnum, optLoopTable[lnum].lpTop->bbNum, optLoopTable[lnum].lpBottom->bbNum); gtDispTree(origExpr); printf("\n"); @@ -5708,7 +5844,8 @@ void Compiler::optHoistLoopCode() printf("\n*************** In optHoistLoopCode()\n"); printf("Blocks/Trees before phase\n"); fgDispBasicBlocks(true); - printf(""); + fgDispHandlerTab(); + optPrintLoopTable(); } #endif @@ -5847,7 +5984,7 @@ void Compiler::optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt) // We must have a do-while loop if ((pLoopDsc->lpFlags & LPFLG_DO_WHILE) == 0) { - JITDUMP(" ... not hoisting " FMT_LP ": not do-while\n", lnum); + JITDUMP(" ... not hoisting " FMT_LP ": not top entry\n", lnum); return; } @@ -5885,7 +6022,7 @@ void Compiler::optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt) #ifdef DEBUG if (verbose) { - printf("optHoistLoopCode for loop " FMT_LP " <" FMT_BB ".." FMT_BB ">:\n", lnum, begn, endn); + printf("optHoistThisLoop for loop " FMT_LP " <" FMT_BB ".." FMT_BB ">:\n", lnum, begn, endn); printf(" Loop body %s a call\n", pLoopDsc->lpContainsCall ? "contains" : "does not contain"); printf(" Loop has %s\n", (pLoopDsc->lpFlags & LPFLG_ONE_EXIT) ? "single exit" : "multiple exits"); } @@ -6408,7 +6545,7 @@ void Compiler::optHoistLoopBlocks(unsigned loopNum, ArrayStack* blo m_valueStack.Reset(); } - // Only uncondtionally executed blocks in the loop are visited (see optHoistThisLoop) + // Only unconditionally executed blocks in the loop are visited (see optHoistThisLoop) // so after we're done visiting the first block we need to assume the worst, that the // blocks that are not visisted have side effects. m_beforeSideEffect = false; @@ -6778,10 +6915,8 @@ void Compiler::optHoistLoopBlocks(unsigned loopNum, ArrayStack* blo BasicBlock* block = blocks->Pop(); weight_t blockWeight = block->getBBWeight(this); - JITDUMP("\n optHoistLoopBlocks " FMT_BB " (weight=%6s) of loop " FMT_LP " <" FMT_BB ".." FMT_BB - ">, firstBlock is %s\n", - block->bbNum, refCntWtd2str(blockWeight), loopNum, loopDsc->lpTop->bbNum, loopDsc->lpBottom->bbNum, - dspBool(block == loopDsc->lpEntry)); + JITDUMP("\n optHoistLoopBlocks " FMT_BB " (weight=%6s) of loop " FMT_LP " <" FMT_BB ".." FMT_BB ">\n", + block->bbNum, refCntWtd2str(blockWeight), loopNum, loopDsc->lpTop->bbNum, loopDsc->lpBottom->bbNum); if (blockWeight < (BB_UNITY_WEIGHT / 10)) { @@ -7222,6 +7357,16 @@ void Compiler::fgCreateLoopPreHeader(unsigned lnum) } } } + +#ifdef DEBUG + if (verbose) + { + JITDUMP("*************** After fgCreateLoopPreHeader for " FMT_LP "\n", lnum); + fgDispBasicBlocks(); + fgDispHandlerTab(); + optPrintLoopTable(); + } +#endif } bool Compiler::optBlockIsLoopEntry(BasicBlock* blk, unsigned* pLnum) @@ -7290,8 +7435,8 @@ void Compiler::optComputeLoopSideEffects() void Compiler::optComputeLoopNestSideEffects(unsigned lnum) { + JITDUMP("optComputeLoopNestSideEffects for " FMT_LP "\n", lnum); assert(optLoopTable[lnum].lpParent == BasicBlock::NOT_IN_LOOP); // Requires: lnum is outermost. - JITDUMP("optComputeLoopSideEffects lnum is %d\n", lnum); for (BasicBlock* const bbInLoop : optLoopTable[lnum].LoopBlocks()) { if (!optComputeLoopSideEffectsOfBlock(bbInLoop)) @@ -9122,3 +9267,67 @@ void Compiler::optRemoveRedundantZeroInits() block->bbFlags &= ~BBF_MARKED; } } + +#ifdef DEBUG + +//------------------------------------------------------------------------ +// optAnyChildNotRemoved: Recursively check the child loops of a loop to see if any of them +// are still live (that is, not marked as LPFLG_REMOVED). This check is done when we are +// removing a parent, just to notify that there is something odd about leaving a live child. +// +// Arguments: +// loopNum - the loop number to check +// +bool Compiler::optAnyChildNotRemoved(unsigned loopNum) +{ + assert(loopNum < optLoopCount); + + // Now recursively mark the children. + for (BasicBlock::loopNumber l = optLoopTable[loopNum].lpChild; // + l != BasicBlock::NOT_IN_LOOP; // + l = optLoopTable[l].lpSibling) + { + if ((optLoopTable[l].lpFlags & LPFLG_REMOVED) == 0) + { + return true; + } + + if (optAnyChildNotRemoved(l)) + { + return true; + } + } + + // All children were removed + return false; +} + +#endif // DEBUG + +//------------------------------------------------------------------------ +// optMarkLoopRemoved: Mark the specified loop as removed (some optimization, such as unrolling, has made the +// loop no longer exist). Note that only the given loop is marked as being removed; if it has any children, +// they are not touched (but a warning message is output to the JitDump). +// +// Arguments: +// loopNum - the loop number to remove +// +void Compiler::optMarkLoopRemoved(unsigned loopNum) +{ + JITDUMP("Marking loop " FMT_LP " removed\n", loopNum); + + assert(loopNum < optLoopCount); + LoopDsc& loop = optLoopTable[loopNum]; + loop.lpFlags |= LPFLG_REMOVED; + +#ifdef DEBUG + if (optAnyChildNotRemoved(loopNum)) + { + JITDUMP("Removed loop " FMT_LP " has one or more live children\n", loopNum); + } + +// Note: we can't call `fgDebugCheckLoopTable()` here because if there are live children, it will assert. +// Assume the caller is going to fix up the table and `bbNatLoopNum` block annotations before the next time +// `fgDebugCheckLoopTable()` is called. +#endif // DEBUG +} diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 4b957a720adc34..8590ecdd81c9f1 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -336,18 +336,20 @@ void RangeCheck::Widen(BasicBlock* block, GenTree* tree, Range* pRange) bool RangeCheck::IsBinOpMonotonicallyIncreasing(GenTreeOp* binop) { - assert(binop->OperIs(GT_ADD)); + assert(binop->OperIs(GT_ADD, GT_MUL, GT_LSH)); GenTree* op1 = binop->gtGetOp1(); GenTree* op2 = binop->gtGetOp2(); JITDUMP("[RangeCheck::IsBinOpMonotonicallyIncreasing] [%06d], [%06d]\n", Compiler::dspTreeID(op1), Compiler::dspTreeID(op2)); - // Check if we have a var + const. - if (op2->OperGet() == GT_LCL_VAR) + + // Check if we have a var + const or var * const. + if (binop->OperIs(GT_ADD, GT_MUL) && op2->OperGet() == GT_LCL_VAR) { std::swap(op1, op2); } + if (op1->OperGet() != GT_LCL_VAR) { JITDUMP("Not monotonically increasing because op1 is not lclVar.\n"); @@ -356,7 +358,8 @@ bool RangeCheck::IsBinOpMonotonicallyIncreasing(GenTreeOp* binop) switch (op2->OperGet()) { case GT_LCL_VAR: - // When adding two local variables, we also must ensure that any constant is non-negative. + // When adding/multiplying/shifting two local variables, we also must ensure that any constant is + // non-negative. return IsMonotonicallyIncreasing(op1, true) && IsMonotonicallyIncreasing(op2, true); case GT_CNS_INT: @@ -417,7 +420,7 @@ bool RangeCheck::IsMonotonicallyIncreasing(GenTree* expr, bool rejectNegativeCon return (ssaDef != nullptr) && IsMonotonicallyIncreasing(ssaDef->GetAssignment()->gtGetOp2(), rejectNegativeConst); } - else if (expr->OperGet() == GT_ADD) + else if (expr->OperIs(GT_ADD, GT_MUL, GT_LSH)) { return IsBinOpMonotonicallyIncreasing(expr->AsOp()); } @@ -894,11 +897,12 @@ void RangeCheck::MergeAssertion(BasicBlock* block, GenTree* op, Range* pRange DE // Compute the range for a binary operation. Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool monIncreasing DEBUGARG(int indent)) { - assert(binop->OperIs(GT_ADD, GT_AND, GT_RSH, GT_LSH, GT_UMOD)); + assert(binop->OperIs(GT_ADD, GT_AND, GT_RSH, GT_LSH, GT_UMOD, GT_MUL)); GenTree* op1 = binop->gtGetOp1(); GenTree* op2 = binop->gtGetOp2(); + // Special cases for binops where op2 is a constant if (binop->OperIs(GT_AND, GT_RSH, GT_LSH, GT_UMOD)) { if (!op2->IsIntCnsFitsInI32()) @@ -929,17 +933,21 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool } } - if (icon < 0) + if (icon >= 0) + { + Range range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, icon)); + JITDUMP("Limit range to %s\n", range.ToString(m_pCompiler->getAllocatorDebugOnly())); + return range; + } + // Generalized range computation not implemented for these operators + else if (binop->OperIs(GT_AND, GT_UMOD, GT_RSH)) { return Range(Limit::keUnknown); } - Range range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, icon)); - JITDUMP("Limit range to %s\n", range.ToString(m_pCompiler->getAllocatorDebugOnly())); - return range; } // other operators are expected to be handled above. - assert(binop->OperIs(GT_ADD)); + assert(binop->OperIs(GT_ADD, GT_MUL, GT_LSH)); Range* op1RangeCached = nullptr; Range op1Range = Limit(Limit::keUndef); @@ -985,9 +993,30 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool op2Range = *op2RangeCached; } - Range r = RangeOps::Add(op1Range, op2Range); - JITDUMP("BinOp add ranges %s %s = %s\n", op1Range.ToString(m_pCompiler->getAllocatorDebugOnly()), - op2Range.ToString(m_pCompiler->getAllocatorDebugOnly()), r.ToString(m_pCompiler->getAllocatorDebugOnly())); + Range r = Range(Limit::keUnknown); + if (binop->OperIs(GT_ADD)) + { + r = RangeOps::Add(op1Range, op2Range); + JITDUMP("BinOp add ranges %s %s = %s\n", op1Range.ToString(m_pCompiler->getAllocatorDebugOnly()), + op2Range.ToString(m_pCompiler->getAllocatorDebugOnly()), + r.ToString(m_pCompiler->getAllocatorDebugOnly())); + } + else if (binop->OperIs(GT_MUL)) + { + r = RangeOps::Multiply(op1Range, op2Range); + JITDUMP("BinOp multiply ranges %s %s = %s\n", op1Range.ToString(m_pCompiler->getAllocatorDebugOnly()), + op2Range.ToString(m_pCompiler->getAllocatorDebugOnly()), + r.ToString(m_pCompiler->getAllocatorDebugOnly())); + } + else if (binop->OperIs(GT_LSH)) + { + // help the next step a bit, convert the LSH rhs to a multiply + Range convertedOp2Range = RangeOps::ConvertShiftToMultiply(op2Range); + r = RangeOps::Multiply(op1Range, convertedOp2Range); + JITDUMP("BinOp multiply ranges %s %s = %s\n", op1Range.ToString(m_pCompiler->getAllocatorDebugOnly()), + convertedOp2Range.ToString(m_pCompiler->getAllocatorDebugOnly()), + r.ToString(m_pCompiler->getAllocatorDebugOnly())); + } return r; } @@ -1077,6 +1106,24 @@ bool RangeCheck::AddOverflows(Limit& limit1, Limit& limit2) return IntAddOverflows(max1, max2); } +// Check if the arithmetic overflows. +bool RangeCheck::MultiplyOverflows(Limit& limit1, Limit& limit2) +{ + int max1; + if (!GetLimitMax(limit1, &max1)) + { + return true; + } + + int max2; + if (!GetLimitMax(limit2, &max2)) + { + return true; + } + + return CheckedOps::MulOverflows(max1, max2, CheckedOps::Signed); +} + // Does the bin operation overflow. bool RangeCheck::DoesBinOpOverflow(BasicBlock* block, GenTreeOp* binop) { @@ -1109,10 +1156,15 @@ bool RangeCheck::DoesBinOpOverflow(BasicBlock* block, GenTreeOp* binop) JITDUMP("Checking bin op overflow %s %s\n", op1Range->ToString(m_pCompiler->getAllocatorDebugOnly()), op2Range->ToString(m_pCompiler->getAllocatorDebugOnly())); - if (!AddOverflows(op1Range->UpperLimit(), op2Range->UpperLimit())) + if (binop->OperIs(GT_ADD)) { - return false; + return AddOverflows(op1Range->UpperLimit(), op2Range->UpperLimit()); } + else if (binop->OperIs(GT_MUL)) + { + return MultiplyOverflows(op1Range->UpperLimit(), op2Range->UpperLimit()); + } + return true; } @@ -1180,7 +1232,7 @@ bool RangeCheck::ComputeDoesOverflow(BasicBlock* block, GenTree* expr) overflows = DoesVarDefOverflow(expr->AsLclVarCommon()); } // Check if add overflows. - else if (expr->OperGet() == GT_ADD) + else if (expr->OperGet() == GT_ADD || expr->OperGet() == GT_MUL) { overflows = DoesBinOpOverflow(block, expr->AsOp()); } @@ -1276,7 +1328,7 @@ Range RangeCheck::ComputeRange(BasicBlock* block, GenTree* expr, bool monIncreas MergeAssertion(block, expr, &range DEBUGARG(indent + 1)); } // compute the range for binary operation - else if (expr->OperIs(GT_ADD, GT_AND, GT_RSH, GT_LSH, GT_UMOD)) + else if (expr->OperIs(GT_ADD, GT_AND, GT_RSH, GT_LSH, GT_UMOD, GT_MUL)) { range = ComputeRangeForBinOp(block, expr->AsOp(), monIncreasing DEBUGARG(indent + 1)); } diff --git a/src/coreclr/jit/rangecheck.h b/src/coreclr/jit/rangecheck.h index 751b243740c408..7161aa2d162c4e 100644 --- a/src/coreclr/jit/rangecheck.h +++ b/src/coreclr/jit/rangecheck.h @@ -147,6 +147,28 @@ struct Limit return false; } + bool MultiplyConstant(int i) + { + switch (type) + { + case keDependent: + return true; + case keBinOpArray: + case keConstant: + if (CheckedOps::MulOverflows(cns, i, CheckedOps::Signed)) + { + return false; + } + cns *= i; + return true; + case keUndef: + case keUnknown: + // For these values of 'type', conservatively return false + break; + } + + return false; + } bool Equals(Limit& l) { @@ -250,6 +272,21 @@ struct RangeOps } } + // Given a constant limit in "l1", multiply it to l2 and mutate "l2". + static Limit MultiplyConstantLimit(Limit& l1, Limit& l2) + { + assert(l1.IsConstant()); + Limit l = l2; + if (l.MultiplyConstant(l1.GetConstant())) + { + return l; + } + else + { + return Limit(Limit::keUnknown); + } + } + // Given two ranges "r1" and "r2", perform an add operation on the // ranges. static Range Add(Range& r1, Range& r2) @@ -291,6 +328,47 @@ struct RangeOps return result; } + // Given two ranges "r1" and "r2", perform an multiply operation on the + // ranges. + static Range Multiply(Range& r1, Range& r2) + { + Limit& r1lo = r1.LowerLimit(); + Limit& r1hi = r1.UpperLimit(); + Limit& r2lo = r2.LowerLimit(); + Limit& r2hi = r2.UpperLimit(); + + Range result = Limit(Limit::keUnknown); + + // Check lo ranges if they are dependent and not unknown. + if ((r1lo.IsDependent() && !r1lo.IsUnknown()) || (r2lo.IsDependent() && !r2lo.IsUnknown())) + { + result.lLimit = Limit(Limit::keDependent); + } + // Check hi ranges if they are dependent and not unknown. + if ((r1hi.IsDependent() && !r1hi.IsUnknown()) || (r2hi.IsDependent() && !r2hi.IsUnknown())) + { + result.uLimit = Limit(Limit::keDependent); + } + + if (r1lo.IsConstant()) + { + result.lLimit = MultiplyConstantLimit(r1lo, r2lo); + } + if (r2lo.IsConstant()) + { + result.lLimit = MultiplyConstantLimit(r2lo, r1lo); + } + if (r1hi.IsConstant()) + { + result.uLimit = MultiplyConstantLimit(r1hi, r2hi); + } + if (r2hi.IsConstant()) + { + result.uLimit = MultiplyConstantLimit(r2hi, r1hi); + } + return result; + } + // Given two ranges "r1" and "r2", do a Phi merge. If "monIncreasing" is true, // then ignore the dependent variables for the lower bound but not for the upper bound. static Range Merge(Range& r1, Range& r2, bool monIncreasing) @@ -378,6 +456,32 @@ struct RangeOps } return result; } + + // Given a Range C from an op (x << C), convert it to be used as + // (x * C'), where C' is a power of 2. + static Range ConvertShiftToMultiply(Range& r1) + { + Limit& r1lo = r1.LowerLimit(); + Limit& r1hi = r1.UpperLimit(); + + if (!r1lo.IsConstant() || !r1hi.IsConstant()) + { + return Limit(Limit::keUnknown); + } + + // Keep it simple for now, check if 0 <= C < 31 + int r1loConstant = r1lo.GetConstant(); + int r1hiConstant = r1hi.GetConstant(); + if (r1loConstant <= 0 || r1loConstant > 31 || r1hiConstant <= 0 || r1hiConstant > 31) + { + return Limit(Limit::keUnknown); + } + + Range result = Limit(Limit::keConstant); + result.lLimit = Limit(Limit::keConstant, 1 << r1loConstant); + result.uLimit = Limit(Limit::keConstant, 1 << r1hiConstant); + return result; + } }; class RangeCheck @@ -484,6 +588,9 @@ class RangeCheck // Does the addition of the two limits overflow? bool AddOverflows(Limit& limit1, Limit& limit2); + // Does the multiplication of the two limits overflow? + bool MultiplyOverflows(Limit& limit1, Limit& limit2); + // Does the binary operation between the operands overflow? Check recursively. bool DoesBinOpOverflow(BasicBlock* block, GenTreeOp* binop); diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp index aae9f1bca07ace..5e54ca8bd94d46 100644 --- a/src/coreclr/jit/rationalize.cpp +++ b/src/coreclr/jit/rationalize.cpp @@ -482,8 +482,7 @@ void Rationalizer::RewriteAssignment(LIR::Use& use) storeOper = GT_STORE_OBJ; break; case GT_DYN_BLK: - storeOper = GT_STORE_DYN_BLK; - storeBlk->AsDynBlk()->gtEvalSizeFirst = false; + storeOper = GT_STORE_DYN_BLK; break; default: unreached(); diff --git a/src/coreclr/jit/redundantbranchopts.cpp b/src/coreclr/jit/redundantbranchopts.cpp index d8e4f97e68cf82..5417308a9bd00f 100644 --- a/src/coreclr/jit/redundantbranchopts.cpp +++ b/src/coreclr/jit/redundantbranchopts.cpp @@ -1027,7 +1027,7 @@ bool Compiler::optRedundantRelop(BasicBlock* const block) for (unsigned int i = 0; i < definedLocalsCount; i++) { - if (gtHasRef(prevTreeRHS, definedLocals[i], /*def only*/ false)) + if (gtHasRef(prevTreeRHS, definedLocals[i])) { JITDUMP(" -- prev tree ref to V%02u interferes\n", definedLocals[i]); interferes = true; diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index e4d18a9a0c9f91..af8d3752199176 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -8827,6 +8827,7 @@ void Compiler::fgValueNumberTree(GenTree* tree) vnStore->VNPUnpackExc(addr->gtVNPair, &addrNvnp, &addrXvnp); // Is the dereference immutable? If so, model it as referencing the read-only heap. + // TODO-VNTypes: this code needs to encode the types of the indirections. if (tree->gtFlags & GTF_IND_INVARIANT) { assert(!isVolatile); // We don't expect both volatile and invariant @@ -8849,22 +8850,13 @@ void Compiler::fgValueNumberTree(GenTree* tree) if (!wasNewobj) { - // Is this invariant indirect expected to always return a non-null value? + // TODO-VNTypes: non-null indirects should only be used for TYP_REFs. if ((tree->gtFlags & GTF_IND_NONNULL) != 0) { assert(tree->gtFlags & GTF_IND_NONFAULTING); tree->gtVNPair = vnStore->VNPairForFunc(tree->TypeGet(), VNF_NonNullIndirect, addrNvnp); - if (addr->IsCnsIntOrI()) - { - assert(addrXvnp.BothEqual() && - (addrXvnp.GetLiberal() == ValueNumStore::VNForEmptyExcSet())); - } - else - { - assert(false && "it's not expected to be hit at the moment, but can be allowed."); - // tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp); - } + tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp); } else { @@ -9452,7 +9444,7 @@ void Compiler::fgValueNumberIntrinsic(GenTree* tree) } else { - assert(intrinsic->gtIntrinsicId == CORINFO_INTRINSIC_Object_GetType); + assert(intrinsic->gtIntrinsicName == NI_System_Object_GetType); intrinsic->gtVNPair = vnStore->VNPWithExc(vnStore->VNPairForFunc(intrinsic->TypeGet(), VNF_ObjGetType, arg0VNP), arg0VNPx); } diff --git a/src/coreclr/nativeaot/Bootstrap/CMakeLists.txt b/src/coreclr/nativeaot/Bootstrap/CMakeLists.txt new file mode 100644 index 00000000000000..02fb30aad9f0c3 --- /dev/null +++ b/src/coreclr/nativeaot/Bootstrap/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(base) +add_subdirectory(dll) diff --git a/src/coreclr/nativeaot/Bootstrap/base/CMakeLists.txt b/src/coreclr/nativeaot/Bootstrap/base/CMakeLists.txt new file mode 100644 index 00000000000000..2366cd267bff56 --- /dev/null +++ b/src/coreclr/nativeaot/Bootstrap/base/CMakeLists.txt @@ -0,0 +1,15 @@ +project(bootstrapper) + +set(SOURCES + ../main.cpp +) + +add_library(bootstrapper STATIC ${SOURCES}) + +install_static_library(bootstrapper aotsdk nativeaot) + +if (CLR_CMAKE_TARGET_WIN32) + add_library(bootstrapper.GuardCF STATIC ${SOURCES}) + install_static_library(bootstrapper.GuardCF aotsdk nativeaot) + target_compile_options(bootstrapper.GuardCF PRIVATE $<$,$>:/guard:cf>) +endif() diff --git a/src/coreclr/nativeaot/Bootstrap/dll/CMakeLists.txt b/src/coreclr/nativeaot/Bootstrap/dll/CMakeLists.txt new file mode 100644 index 00000000000000..02fe16cb2825ba --- /dev/null +++ b/src/coreclr/nativeaot/Bootstrap/dll/CMakeLists.txt @@ -0,0 +1,17 @@ +project(bootstrapperdll) + +add_definitions(-DCORERT_DLL) + +set(SOURCES + ../main.cpp +) + +add_library(bootstrapperdll STATIC ${SOURCES}) + +install_static_library(bootstrapperdll aotsdk nativeaot) + +if (CLR_CMAKE_TARGET_WIN32) + add_library(bootstrapperdll.GuardCF STATIC ${SOURCES}) + install_static_library(bootstrapperdll.GuardCF aotsdk nativeaot) + target_compile_options(bootstrapperdll.GuardCF PRIVATE $<$,$>:/guard:cf>) +endif() diff --git a/src/coreclr/nativeaot/Bootstrap/main.cpp b/src/coreclr/nativeaot/Bootstrap/main.cpp new file mode 100644 index 00000000000000..798c4db7481c9a --- /dev/null +++ b/src/coreclr/nativeaot/Bootstrap/main.cpp @@ -0,0 +1,228 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +// +// This is the mechanism whereby multiple linked modules contribute their global data for initialization at +// startup of the application. +// +// ILC creates sections in the output obj file to mark the beginning and end of merged global data. +// It defines sentinel symbols that are used to get the addresses of the start and end of global data +// at runtime. The section names are platform-specific to match platform-specific linker conventions. +// +#if defined(_MSC_VER) + +#pragma section(".modules$A", read) +#pragma section(".modules$Z", read) +extern "C" __declspec(allocate(".modules$A")) void * __modules_a[]; +extern "C" __declspec(allocate(".modules$Z")) void * __modules_z[]; + +__declspec(allocate(".modules$A")) void * __modules_a[] = { nullptr }; +__declspec(allocate(".modules$Z")) void * __modules_z[] = { nullptr }; + +// +// Each obj file compiled from managed code has a .modules$I section containing a pointer to its ReadyToRun +// data (which points at eager class constructors, frozen strings, etc). +// +// The #pragma ... /merge directive folds the book-end sections and all .modules$I sections from all input +// obj files into .rdata in alphabetical order. +// +#pragma comment(linker, "/merge:.modules=.rdata") + +// +// Unboxing stubs need to be merged, folded and sorted. They are delimited by two special sections (.unbox$A +// and .unbox$Z). All unboxing stubs are in .unbox$M sections. +// +#pragma comment(linker, "/merge:.unbox=.text") + +char _bookend_a; +char _bookend_z; + +// +// Generate bookends for the managed code section. +// We give them unique bodies to prevent folding. +// + +#pragma code_seg(".managedcode$A") +void* __managedcode_a() { return &_bookend_a; } +#pragma code_seg(".managedcode$Z") +void* __managedcode_z() { return &_bookend_z; } +#pragma code_seg() + +// +// Generate bookends for the unboxing stub section. +// We give them unique bodies to prevent folding. +// + +#pragma code_seg(".unbox$A") +void* __unbox_a() { return &_bookend_a; } +#pragma code_seg(".unbox$Z") +void* __unbox_z() { return &_bookend_z; } +#pragma code_seg() + +#else // _MSC_VER + +#if defined(__APPLE__) + +extern void * __modules_a[] __asm("section$start$__DATA$__modules"); +extern void * __modules_z[] __asm("section$end$__DATA$__modules"); +extern char __managedcode_a __asm("section$start$__TEXT$__managedcode"); +extern char __managedcode_z __asm("section$end$__TEXT$__managedcode"); +extern char __unbox_a __asm("section$start$__TEXT$__unbox"); +extern char __unbox_z __asm("section$end$__TEXT$__unbox"); + +#else // __APPLE__ + +extern "C" void * __start___modules[]; +extern "C" void * __stop___modules[]; +static void * (&__modules_a)[] = __start___modules; +static void * (&__modules_z)[] = __stop___modules; + +extern "C" char __start___managedcode; +extern "C" char __stop___managedcode; +static char& __managedcode_a = __start___managedcode; +static char& __managedcode_z = __stop___managedcode; + +extern "C" char __start___unbox; +extern "C" char __stop___unbox; +static char& __unbox_a = __start___unbox; +static char& __unbox_z = __stop___unbox; + +#endif // __APPLE__ + +#endif // _MSC_VER + +extern "C" bool RhInitialize(); +extern "C" void RhpEnableConservativeStackReporting(); +extern "C" void RhpShutdown(); +extern "C" void RhSetRuntimeInitializationCallback(int (*fPtr)()); + +extern "C" bool RhRegisterOSModule(void * pModule, + void * pvManagedCodeStartRange, uint32_t cbManagedCodeRange, + void * pvUnboxingStubsStartRange, uint32_t cbUnboxingStubsRange, + void ** pClasslibFunctions, uint32_t nClasslibFunctions); + +extern "C" void* PalGetModuleHandleFromPointer(void* pointer); + +extern "C" void GetRuntimeException(); +extern "C" void FailFast(); +extern "C" void AppendExceptionStackFrame(); +extern "C" void GetSystemArrayEEType(); +extern "C" void OnFirstChanceException(); +extern "C" void OnUnhandledException(); +extern "C" void IDynamicCastableIsInterfaceImplemented(); +extern "C" void IDynamicCastableGetInterfaceImplementation(); + +typedef void(*pfn)(); + +static const pfn c_classlibFunctions[] = { + &GetRuntimeException, + &FailFast, + nullptr, // &UnhandledExceptionHandler, + &AppendExceptionStackFrame, + nullptr, // &CheckStaticClassConstruction, + &GetSystemArrayEEType, + &OnFirstChanceException, + &OnUnhandledException, + &IDynamicCastableIsInterfaceImplemented, + &IDynamicCastableGetInterfaceImplementation, +}; + +#ifndef _countof +#define _countof(_array) (sizeof(_array)/sizeof(_array[0])) +#endif + +extern "C" void InitializeModules(void* osModule, void ** modules, int count, void ** pClasslibFunctions, int nClasslibFunctions); + +#ifndef CORERT_DLL +#define CORERT_ENTRYPOINT __managed__Main +#if defined(_WIN32) +extern "C" int __managed__Main(int argc, wchar_t* argv[]); +#else +extern "C" int __managed__Main(int argc, char* argv[]); +#endif +#else +#define CORERT_ENTRYPOINT __managed__Startup +extern "C" void __managed__Startup(); +#endif // !CORERT_DLL + +static int InitializeRuntime() +{ + if (!RhInitialize()) + return -1; + + // RhpEnableConservativeStackReporting(); + + void * osModule = PalGetModuleHandleFromPointer((void*)&CORERT_ENTRYPOINT); + + // TODO: pass struct with parameters instead of the large signature of RhRegisterOSModule + if (!RhRegisterOSModule( + osModule, + (void*)&__managedcode_a, (uint32_t)((char *)&__managedcode_z - (char*)&__managedcode_a), + (void*)&__unbox_a, (uint32_t)((char *)&__unbox_z - (char*)&__unbox_a), + (void **)&c_classlibFunctions, _countof(c_classlibFunctions))) + { + return -1; + } + + InitializeModules(osModule, __modules_a, (int)((__modules_z - __modules_a)), (void **)&c_classlibFunctions, _countof(c_classlibFunctions)); + +#ifdef CORERT_DLL + // Run startup method immediately for a native library + __managed__Startup(); +#endif // CORERT_DLL + + return 0; +} + +#ifndef CORERT_DLL + +#ifdef ENSURE_PRIMARY_STACK_SIZE +__attribute__((noinline, optnone)) +static void EnsureStackSize(int stackSize) +{ + volatile char* s = (char*)_alloca(stackSize); + *s = 0; +} +#endif // ENSURE_PRIMARY_STACK_SIZE + +#if defined(_WIN32) +int __cdecl wmain(int argc, wchar_t* argv[]) +#else +int main(int argc, char* argv[]) +#endif +{ +#ifdef ENSURE_PRIMARY_STACK_SIZE + // TODO: https://github.com/dotnet/runtimelab/issues/791 + EnsureStackSize(1536 * 1024); +#endif + + int initval = InitializeRuntime(); + if (initval != 0) + return initval; + + int retval = __managed__Main(argc, argv); + + RhpShutdown(); + + return retval; +} +#endif // !CORERT_DLL + +#ifdef CORERT_DLL +static struct InitializeRuntimePointerHelper +{ + InitializeRuntimePointerHelper() + { + RhSetRuntimeInitializationCallback(&InitializeRuntime); + } +} initializeRuntimePointerHelper; + +extern "C" void* CoreRT_StaticInitialization(); + +void* CoreRT_StaticInitialization() +{ + return &initializeRuntimePointerHelper; +} +#endif // CORERT_DLL diff --git a/src/coreclr/nativeaot/BuildIntegration/BuildFrameworkNativeObjects.proj b/src/coreclr/nativeaot/BuildIntegration/BuildFrameworkNativeObjects.proj new file mode 100644 index 00000000000000..6469964c0170a1 --- /dev/null +++ b/src/coreclr/nativeaot/BuildIntegration/BuildFrameworkNativeObjects.proj @@ -0,0 +1,47 @@ + + + + ComputeIlcCompileInputs;BuildOneFrameworkLibrary;SetupOSSpecificProps + BuildAllFrameworkLibrariesAsSingleLib + true + $(FrameworkObjPath)\ + true + true + true + + + + + + + + + + LibraryToCompile=%(DefaultFrameworkAssemblies.Identity) + + + + + + + + + + + + + + + + true + + + + + + + + diff --git a/src/coreclr/nativeaot/BuildIntegration/BuildIntegration.proj b/src/coreclr/nativeaot/BuildIntegration/BuildIntegration.proj new file mode 100644 index 00000000000000..624760868cf2de --- /dev/null +++ b/src/coreclr/nativeaot/BuildIntegration/BuildIntegration.proj @@ -0,0 +1,17 @@ + + + + + $(RuntimeBinDir)/build/ + + + + + + + + + + + + diff --git a/src/coreclr/nativeaot/BuildIntegration/CoreRTNatVis.natvis b/src/coreclr/nativeaot/BuildIntegration/CoreRTNatVis.natvis new file mode 100644 index 00000000000000..bd5ebc5f660d9d --- /dev/null +++ b/src/coreclr/nativeaot/BuildIntegration/CoreRTNatVis.natvis @@ -0,0 +1,80 @@ + + + + {&(_firstChar),su} + &(_firstChar),su + + + {{count = {_numComponents}}} + + + _numComponents + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + + + + + + + + + + Null + {*Get()} + + Get() + + + + + + + + diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.targets new file mode 100644 index 00000000000000..f1bb8ce6e9610a --- /dev/null +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.targets @@ -0,0 +1,55 @@ + + + + + + win + osx + linux-musl + linux + + + $(RuntimeIdentifier)- + x86 + x64 + arm + arm64 + unknown + + runtime.$(OSIdentifier)-$(TargetArchitecture).Microsoft.DotNet.ILCompiler + + $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant) + + + arm64 + + $(OSHostArch) + runtime.$(OSIdentifier)-$(IlcHostArch).Microsoft.DotNet.ILCompiler + true + + + + + + + RunResolvePackageDependencies + ImportRuntimeIlcPackageTarget + SetupProperties + + + + + + + + + + %(PackageDefinitions.ResolvedPath) + %(PackageDefinitions.ResolvedPath) + + + + + + diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets new file mode 100644 index 00000000000000..67efba61efde98 --- /dev/null +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + <_ResolvedCopyLocalPublishAssets Remove="@(_AssembliesToSkipPublish)" /> + <_ResolvedCopyLocalPublishAssets Include="@(_LinkedResolvedAssemblies)" /> + + + + <_NativeIntermediateAssembly Include="@(IntermediateAssembly->'$(NativeOutputPath)%(Filename)$(NativeBinaryExt)')" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.props b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.props new file mode 100644 index 00000000000000..e486925e1eeba2 --- /dev/null +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.props @@ -0,0 +1,110 @@ + + + + clang + $(CppCompilerAndLinker) + $(CppCompilerAndLinker) + ar + + + + + + libRuntime.WorkstationGC + libRuntime.ServerGC + + + $(RuntimeIdentifier) + + + x86_64 + aarch64 + + + $(CrossCompileArch)-linux-gnu + $(CrossCompileArch)-alpine-linux-musl + + + + + + + + + + + + + + + + + + + + + + $(IlcPath)/framework/lib%(Identity).a + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.props b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.props new file mode 100644 index 00000000000000..32cb494f28fa1e --- /dev/null +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.props @@ -0,0 +1,118 @@ + + + + cl + link + lib + .lib + .GuardCF.lib + Runtime.WorkstationGC + Runtime.ServerGC + bootstrapper + bootstrapperdll + wmainCRTStartup + WINDOWS + CONSOLE + + + + + + + + + + + + + + + + + + + $(IlcPath)\sdk\%(Identity).Aot$(LibrarySuffix) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_CppToolsDirectory>$(_FindVCVarsallOutput.Split(`#`)[0]) + "$(_CppToolsDirectory)cl.exe" + "$(_CppToolsDirectory)link.exe" + "$(_CppToolsDirectory)lib.exe" + + + + + diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets new file mode 100644 index 00000000000000..2d353a4dc2e90b --- /dev/null +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -0,0 +1,384 @@ + + + + + + $(IntermediateOutputPath)native\ + $(OutputPath)native\ + true + $(MSBuildThisFileDirectory)..\tools\netstandard\ILCompiler.Build.Tasks.dll + $(IlcPath) + windows + OSX + $(OS) + true + + false + true + + + + + <_BuildingInCompatibleMode Condition="$(TrimmerDefaultAction) == '' and $(IlcGenerateStackTraceData) == '' and $(IlcDisableReflection) == ''">true + + true + true + true + copyused + + + + + false + true + false + + + + .obj + .o + + .lib + .a + + $(NativeObjectExt) + + true + + .exe + + .dll + .dylib + .so + .lib + .a + + .def + .exports + + $(NativeIntermediateOutputPath)$(TargetName)$(NativeObjectExt) + $(NativeOutputPath)$(TargetName)$(NativeBinaryExt) + $(NativeIntermediateOutputPath)$(TargetName)$(ExportsFileExt) + + $(NativeObject) + + IlcCompile + + $(NativeOutputPath) + $(NativeIntermediateOutputPath) + + $(FrameworkLibPath)\Framework$(LibFileExt) + $(FrameworkLibPath)\libframework$(LibFileExt) + SetupProperties + + + + Compile;ComputeIlcCompileInputs + $(IlcCompileDependsOn);BuildFrameworkLib + $(IlcCompileDependsOn);SetupOSSpecificProps + $(IlcCompileDependsOn);PrepareForILLink + + + + + + + + + + + + + <_ExcludedPrivateSdkAssemblies Include="$(IlcPath)\sdk\System.Private.Reflection.Core.dll" Condition="$(IlcDisableReflection) == 'true'" /> + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + $(IlcHostPackagePath) + $(RuntimePackagePath) + + + + + <_ExcludedPrivateSdkAssemblies Include="$(IlcPath)\sdk\System.Private.Reflection.Core.dll" Condition="$(IlcDisableReflection) == 'true'" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + IntermediateOutputPath=$(IntermediateOutputPath); + FrameworkLibPath=$(FrameworkLibPath); + FrameworkObjPath=$(FrameworkObjPath); + RuntimePackagePath=$(RuntimePackagePath); + IlcHostPackagePath=$(IlcHostPackagePath); + TargetArchitecture=$(TargetArchitecture); + + + + + + + + + + + true + + + + + + + <__SingleWarnIntermediateAssembly Include="@(ManagedAssemblyToLink)" /> + <__SingleWarnIntermediateAssembly Remove="@(IntermediateAssembly)" /> + + <_SingleWarnIntermediateAssembly Include="@(ManagedAssemblyToLink)" /> + <_SingleWarnIntermediateAssembly Remove="@(__SingleWarnIntermediateAssembly)" /> + + <_SingleWarnIntermediateAssembly> + false + + + + + + + + false + + + + + <_IlcRootedAssemblies Include="@(TrimmerRootAssembly)" /> + <_IlcRootedAssemblies Include="@(ManagedAssemblyToLink)" Condition="%(ManagedAssemblyToLink.TrimMode) == 'copy'" /> + <_IlcConditionallyRootedAssemblies Include="@(ManagedAssemblyToLink)" Condition="%(ManagedAssemblyToLink.TrimMode) == 'copyused'" /> + <_IlcTrimmedAssemblies Include="@(ManagedAssemblyToLink)" Condition="%(ManagedAssemblyToLink.TrimMode) == 'link'" /> + <_IlcNoSingleWarnAssemblies Include="@(ManagedAssemblyToLink)" Condition="%(ManagedAssemblyToLink.TrimmerSingleWarn) == 'false'" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_Win32ResFile>$(NativeIntermediateOutputPath)$(TargetName).res + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_IgnoreLinkerWarnings>false + <_IgnoreLinkerWarnings Condition="'$(TargetOS)' == 'OSX'">true + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreclr/nativeaot/BuildIntegration/WindowsAPIs.txt b/src/coreclr/nativeaot/BuildIntegration/WindowsAPIs.txt new file mode 100644 index 00000000000000..845a90ca278443 --- /dev/null +++ b/src/coreclr/nativeaot/BuildIntegration/WindowsAPIs.txt @@ -0,0 +1,2336 @@ +# List of ubiquitous Windows APIs that are safe to emit direct calls for +# +# This list was seeded from mincore.lib in Windows SDK. Rarely used .libs, and APIs not available +# on all Windows editions (including Windows 7 and Windows Nano) were removed. +# + +advapi32!AbortSystemShutdownW +advapi32!AccessCheck +advapi32!AccessCheckAndAuditAlarmW +advapi32!AccessCheckByType +advapi32!AccessCheckByTypeAndAuditAlarmW +advapi32!AccessCheckByTypeResultList +advapi32!AccessCheckByTypeResultListAndAuditAlarmByHandleW +advapi32!AccessCheckByTypeResultListAndAuditAlarmW +advapi32!AddAccessAllowedAce +advapi32!AddAccessAllowedAceEx +advapi32!AddAccessAllowedObjectAce +advapi32!AddAccessDeniedAce +advapi32!AddAccessDeniedAceEx +advapi32!AddAccessDeniedObjectAce +advapi32!AddAce +advapi32!AddAuditAccessAce +advapi32!AddAuditAccessAceEx +advapi32!AddAuditAccessObjectAce +advapi32!AddMandatoryAce +advapi32!AdjustTokenGroups +advapi32!AdjustTokenPrivileges +advapi32!AllocateAndInitializeSid +advapi32!AllocateLocallyUniqueId +advapi32!AreAllAccessesGranted +advapi32!AreAnyAccessesGranted +advapi32!ChangeServiceConfig2A +advapi32!ChangeServiceConfig2W +advapi32!ChangeServiceConfigA +advapi32!ChangeServiceConfigW +advapi32!CheckTokenMembership +advapi32!CloseServiceHandle +advapi32!CloseTrace +advapi32!ControlService +advapi32!ControlServiceExA +advapi32!ControlServiceExW +advapi32!ControlTraceW +advapi32!ConvertSecurityDescriptorToStringSecurityDescriptorW +advapi32!ConvertSidToStringSidW +advapi32!ConvertStringSecurityDescriptorToSecurityDescriptorW +advapi32!ConvertStringSidToSidW +advapi32!ConvertToAutoInheritPrivateObjectSecurity +advapi32!CopySid +advapi32!CreatePrivateObjectSecurity +advapi32!CreatePrivateObjectSecurityEx +advapi32!CreatePrivateObjectSecurityWithMultipleInheritance +advapi32!CreateProcessAsUserA +advapi32!CreateProcessAsUserW +advapi32!CreateRestrictedToken +advapi32!CreateServiceA +advapi32!CreateServiceW +advapi32!CreateWellKnownSid +advapi32!CredDeleteA +advapi32!CredDeleteW +advapi32!CredEnumerateA +advapi32!CredEnumerateW +advapi32!CredFindBestCredentialA +advapi32!CredFindBestCredentialW +advapi32!CredFree +advapi32!CredGetSessionTypes +advapi32!CredGetTargetInfoA +advapi32!CredGetTargetInfoW +advapi32!CredIsMarshaledCredentialW +advapi32!CredIsProtectedA +advapi32!CredIsProtectedW +advapi32!CredMarshalCredentialA +advapi32!CredMarshalCredentialW +advapi32!CredProtectA +advapi32!CredProtectW +advapi32!CredReadA +advapi32!CredReadDomainCredentialsA +advapi32!CredReadDomainCredentialsW +advapi32!CredReadW +advapi32!CredUnmarshalCredentialA +advapi32!CredUnmarshalCredentialW +advapi32!CredUnprotectA +advapi32!CredUnprotectW +advapi32!CredWriteA +advapi32!CredWriteDomainCredentialsA +advapi32!CredWriteDomainCredentialsW +advapi32!CredWriteW +advapi32!DeleteAce +advapi32!DeleteService +advapi32!DestroyPrivateObjectSecurity +advapi32!DuplicateToken +advapi32!DuplicateTokenEx +advapi32!EnableTraceEx2 +advapi32!EnumDependentServicesW +advapi32!EnumerateTraceGuidsEx +advapi32!EnumServicesStatusExW +advapi32!EqualDomainSid +advapi32!EqualPrefixSid +advapi32!EqualSid +advapi32!EventAccessControl +advapi32!EventAccessQuery +advapi32!EventAccessRemove +advapi32!EventActivityIdControl +advapi32!EventEnabled +advapi32!EventProviderEnabled +advapi32!EventRegister +advapi32!EventSetInformation +advapi32!EventUnregister +advapi32!EventWrite +advapi32!EventWriteEx +advapi32!EventWriteString +advapi32!EventWriteTransfer +advapi32!FindFirstFreeAce +advapi32!FreeSid +advapi32!GetAce +advapi32!GetAclInformation +advapi32!GetFileSecurityW +advapi32!GetKernelObjectSecurity +advapi32!GetLengthSid +advapi32!GetPrivateObjectSecurity +advapi32!GetSecurityDescriptorControl +advapi32!GetSecurityDescriptorDacl +advapi32!GetSecurityDescriptorGroup +advapi32!GetSecurityDescriptorLength +advapi32!GetSecurityDescriptorOwner +advapi32!GetSecurityDescriptorRMControl +advapi32!GetSecurityDescriptorSacl +advapi32!GetServiceDisplayNameW +advapi32!GetServiceKeyNameW +advapi32!GetSidIdentifierAuthority +advapi32!GetSidLengthRequired +advapi32!GetSidSubAuthority +advapi32!GetSidSubAuthorityCount +advapi32!GetTokenInformation +advapi32!GetTraceEnableFlags +advapi32!GetTraceEnableLevel +advapi32!GetTraceLoggerHandle +advapi32!GetWindowsAccountDomainSid +advapi32!ImpersonateAnonymousToken +advapi32!ImpersonateLoggedOnUser +advapi32!ImpersonateNamedPipeClient +advapi32!ImpersonateSelf +advapi32!InitializeAcl +advapi32!InitializeSecurityDescriptor +advapi32!InitializeSid +advapi32!InitiateShutdownW +advapi32!InitiateSystemShutdownExW +advapi32!IsTokenRestricted +advapi32!IsValidAcl +advapi32!IsValidSecurityDescriptor +advapi32!IsValidSid +advapi32!IsWellKnownSid +advapi32!LogonUserExExW +advapi32!LookupAccountNameW +advapi32!LookupAccountSidW +advapi32!LookupPrivilegeDisplayNameW +advapi32!LookupPrivilegeNameW +advapi32!LookupPrivilegeValueW +advapi32!LsaEnumerateTrustedDomains +advapi32!LsaManageSidNameMapping +advapi32!MakeAbsoluteSD +advapi32!MakeSelfRelativeSD +advapi32!MapGenericMask +advapi32!NotifyServiceStatusChangeA +advapi32!NotifyServiceStatusChangeW +advapi32!ObjectCloseAuditAlarmW +advapi32!ObjectDeleteAuditAlarmW +advapi32!ObjectOpenAuditAlarmW +advapi32!ObjectPrivilegeAuditAlarmW +advapi32!OpenProcessToken +advapi32!OpenSCManagerA +advapi32!OpenSCManagerW +advapi32!OpenServiceA +advapi32!OpenServiceW +advapi32!OpenThreadToken +advapi32!OpenTraceW +advapi32!PrivilegeCheck +advapi32!PrivilegedServiceAuditAlarmW +advapi32!ProcessTrace +advapi32!QueryAllTracesW +advapi32!QuerySecurityAccessMask +advapi32!QueryServiceConfig2A +advapi32!QueryServiceConfig2W +advapi32!QueryServiceConfigA +advapi32!QueryServiceConfigW +advapi32!QueryServiceObjectSecurity +advapi32!QueryServiceStatus +advapi32!QueryServiceStatusEx +advapi32!RegCloseKey +advapi32!RegCopyTreeW +advapi32!RegCreateKeyExA +advapi32!RegCreateKeyExW +advapi32!RegDeleteKeyExA +advapi32!RegDeleteKeyExW +advapi32!RegDeleteKeyValueA +advapi32!RegDeleteKeyValueW +advapi32!RegDeleteTreeA +advapi32!RegDeleteTreeW +advapi32!RegDeleteValueA +advapi32!RegDeleteValueW +advapi32!RegDisablePredefinedCacheEx +advapi32!RegEnumKeyExA +advapi32!RegEnumKeyExW +advapi32!RegEnumValueA +advapi32!RegEnumValueW +advapi32!RegFlushKey +advapi32!RegGetKeySecurity +advapi32!RegGetValueA +advapi32!RegGetValueW +advapi32!RegisterServiceCtrlHandlerA +advapi32!RegisterServiceCtrlHandlerExA +advapi32!RegisterServiceCtrlHandlerExW +advapi32!RegisterServiceCtrlHandlerW +advapi32!RegisterTraceGuidsW +advapi32!RegLoadAppKeyA +advapi32!RegLoadAppKeyW +advapi32!RegLoadKeyA +advapi32!RegLoadKeyW +advapi32!RegLoadMUIStringA +advapi32!RegLoadMUIStringW +advapi32!RegNotifyChangeKeyValue +advapi32!RegOpenCurrentUser +advapi32!RegOpenKeyExA +advapi32!RegOpenKeyExW +advapi32!RegOpenUserClassesRoot +advapi32!RegQueryInfoKeyA +advapi32!RegQueryInfoKeyW +advapi32!RegQueryMultipleValuesA +advapi32!RegQueryMultipleValuesW +advapi32!RegQueryValueExA +advapi32!RegQueryValueExW +advapi32!RegRestoreKeyA +advapi32!RegRestoreKeyW +advapi32!RegSaveKeyExA +advapi32!RegSaveKeyExW +advapi32!RegSetKeySecurity +advapi32!RegSetKeyValueA +advapi32!RegSetKeyValueW +advapi32!RegSetValueExA +advapi32!RegSetValueExW +advapi32!RegUnLoadKeyA +advapi32!RegUnLoadKeyW +advapi32!RevertToSelf +advapi32!SetAclInformation +advapi32!SetFileSecurityW +advapi32!SetKernelObjectSecurity +advapi32!SetPrivateObjectSecurity +advapi32!SetPrivateObjectSecurityEx +advapi32!SetSecurityAccessMask +advapi32!SetSecurityDescriptorControl +advapi32!SetSecurityDescriptorDacl +advapi32!SetSecurityDescriptorGroup +advapi32!SetSecurityDescriptorOwner +advapi32!SetSecurityDescriptorRMControl +advapi32!SetSecurityDescriptorSacl +advapi32!SetServiceObjectSecurity +advapi32!SetServiceStatus +advapi32!SetThreadToken +advapi32!SetTokenInformation +advapi32!StartServiceA +advapi32!StartServiceCtrlDispatcherA +advapi32!StartServiceCtrlDispatcherW +advapi32!StartServiceW +advapi32!StartTraceW +advapi32!StopTraceW +advapi32!SystemFunction001 +advapi32!SystemFunction002 +advapi32!SystemFunction003 +advapi32!SystemFunction004 +advapi32!SystemFunction005 +advapi32!SystemFunction028 +advapi32!SystemFunction029 +advapi32!SystemFunction034 +advapi32!SystemFunction036 +advapi32!SystemFunction040 +advapi32!SystemFunction041 +advapi32!TraceEvent +advapi32!TraceMessage +advapi32!TraceMessageVa +advapi32!TraceSetInformation +advapi32!UnregisterTraceGuids +bcrypt!BCryptAddContextFunction +bcrypt!BCryptCloseAlgorithmProvider +bcrypt!BCryptConfigureContext +bcrypt!BCryptConfigureContextFunction +bcrypt!BCryptCreateContext +bcrypt!BCryptCreateHash +bcrypt!BCryptDecrypt +bcrypt!BCryptDeleteContext +bcrypt!BCryptDeriveKey +bcrypt!BCryptDeriveKeyCapi +bcrypt!BCryptDeriveKeyPBKDF2 +bcrypt!BCryptDestroyHash +bcrypt!BCryptDestroyKey +bcrypt!BCryptDestroySecret +bcrypt!BCryptDuplicateHash +bcrypt!BCryptDuplicateKey +bcrypt!BCryptEncrypt +bcrypt!BCryptEnumAlgorithms +bcrypt!BCryptEnumContextFunctionProviders +bcrypt!BCryptEnumContextFunctions +bcrypt!BCryptEnumContexts +bcrypt!BCryptEnumProviders +bcrypt!BCryptEnumRegisteredProviders +bcrypt!BCryptExportKey +bcrypt!BCryptFinalizeKeyPair +bcrypt!BCryptFinishHash +bcrypt!BCryptFreeBuffer +bcrypt!BCryptGenerateKeyPair +bcrypt!BCryptGenerateSymmetricKey +bcrypt!BCryptGenRandom +bcrypt!BCryptGetFipsAlgorithmMode +bcrypt!BCryptGetProperty +bcrypt!BCryptHashData +bcrypt!BCryptImportKey +bcrypt!BCryptImportKeyPair +bcrypt!BCryptOpenAlgorithmProvider +bcrypt!BCryptQueryContextConfiguration +bcrypt!BCryptQueryContextFunctionConfiguration +bcrypt!BCryptQueryContextFunctionProperty +bcrypt!BCryptQueryProviderRegistration +bcrypt!BCryptRegisterConfigChangeNotify +bcrypt!BCryptRemoveContextFunction +bcrypt!BCryptResolveProviders +bcrypt!BCryptSecretAgreement +bcrypt!BCryptSetAuditingInterface +bcrypt!BCryptSetContextFunctionProperty +bcrypt!BCryptSetProperty +bcrypt!BCryptSignHash +bcrypt!BCryptUnregisterConfigChangeNotify +bcrypt!BCryptVerifySignature +crypt32!CertAddCertificateContextToStore +crypt32!CertAddCertificateLinkToStore +crypt32!CertAddCRLContextToStore +crypt32!CertAddCRLLinkToStore +crypt32!CertAddCTLContextToStore +crypt32!CertAddCTLLinkToStore +crypt32!CertAddEncodedCertificateToStore +crypt32!CertAddEncodedCertificateToSystemStoreA +crypt32!CertAddEncodedCertificateToSystemStoreW +crypt32!CertAddEncodedCRLToStore +crypt32!CertAddEncodedCTLToStore +crypt32!CertAddEnhancedKeyUsageIdentifier +crypt32!CertAddRefServerOcspResponse +crypt32!CertAddRefServerOcspResponseContext +crypt32!CertAddSerializedElementToStore +crypt32!CertAddStoreToCollection +crypt32!CertAlgIdToOID +crypt32!CertCloseServerOcspResponse +crypt32!CertCloseStore +crypt32!CertCompareCertificate +crypt32!CertCompareCertificateName +crypt32!CertCompareIntegerBlob +crypt32!CertComparePublicKeyInfo +crypt32!CertControlStore +crypt32!CertCreateCertificateChainEngine +crypt32!CertCreateCertificateContext +crypt32!CertCreateContext +crypt32!CertCreateCRLContext +crypt32!CertCreateCTLContext +crypt32!CertCreateCTLEntryFromCertificateContextProperties +crypt32!CertCreateSelfSignCertificate +crypt32!CertDeleteCertificateFromStore +crypt32!CertDeleteCRLFromStore +crypt32!CertDeleteCTLFromStore +crypt32!CertDuplicateCertificateChain +crypt32!CertDuplicateCertificateContext +crypt32!CertDuplicateCRLContext +crypt32!CertDuplicateCTLContext +crypt32!CertDuplicateStore +crypt32!CertEnumCertificateContextProperties +crypt32!CertEnumCertificatesInStore +crypt32!CertEnumCRLContextProperties +crypt32!CertEnumCRLsInStore +crypt32!CertEnumCTLContextProperties +crypt32!CertEnumCTLsInStore +crypt32!CertEnumPhysicalStore +crypt32!CertEnumSubjectInSortedCTL +crypt32!CertEnumSystemStore +crypt32!CertEnumSystemStoreLocation +crypt32!CertFindAttribute +crypt32!CertFindCertificateInCRL +crypt32!CertFindCertificateInStore +crypt32!CertFindChainInStore +crypt32!CertFindCRLInStore +crypt32!CertFindCTLInStore +crypt32!CertFindExtension +crypt32!CertFindRDNAttr +crypt32!CertFindSubjectInCTL +crypt32!CertFindSubjectInSortedCTL +crypt32!CertFreeCertificateChain +crypt32!CertFreeCertificateChainEngine +crypt32!CertFreeCertificateChainList +crypt32!CertFreeCertificateContext +crypt32!CertFreeCRLContext +crypt32!CertFreeCTLContext +crypt32!CertFreeServerOcspResponseContext +crypt32!CertGetCertificateChain +crypt32!CertGetCertificateContextProperty +crypt32!CertGetCRLContextProperty +crypt32!CertGetCRLFromStore +crypt32!CertGetCTLContextProperty +crypt32!CertGetEnhancedKeyUsage +crypt32!CertGetIntendedKeyUsage +crypt32!CertGetIssuerCertificateFromStore +crypt32!CertGetNameStringA +crypt32!CertGetNameStringW +crypt32!CertGetPublicKeyLength +crypt32!CertGetServerOcspResponseContext +crypt32!CertGetStoreProperty +crypt32!CertGetSubjectCertificateFromStore +crypt32!CertGetValidUsages +crypt32!CertIsRDNAttrsInCertificateName +crypt32!CertIsStrongHashToSign +crypt32!CertIsValidCRLForCertificate +crypt32!CertNameToStrA +crypt32!CertNameToStrW +crypt32!CertOIDToAlgId +crypt32!CertOpenServerOcspResponse +crypt32!CertOpenStore +crypt32!CertOpenSystemStoreA +crypt32!CertOpenSystemStoreW +crypt32!CertRDNValueToStrA +crypt32!CertRDNValueToStrW +crypt32!CertRegisterPhysicalStore +crypt32!CertRegisterSystemStore +crypt32!CertRemoveEnhancedKeyUsageIdentifier +crypt32!CertRemoveStoreFromCollection +crypt32!CertResyncCertificateChainEngine +crypt32!CertRetrieveLogoOrBiometricInfo +crypt32!CertSaveStore +crypt32!CertSelectCertificateChains +crypt32!CertSerializeCertificateStoreElement +crypt32!CertSerializeCRLStoreElement +crypt32!CertSerializeCTLStoreElement +crypt32!CertSetCertificateContextPropertiesFromCTLEntry +crypt32!CertSetCertificateContextProperty +crypt32!CertSetCRLContextProperty +crypt32!CertSetCTLContextProperty +crypt32!CertSetEnhancedKeyUsage +crypt32!CertSetStoreProperty +crypt32!CertStrToNameA +crypt32!CertStrToNameW +crypt32!CertUnregisterPhysicalStore +crypt32!CertUnregisterSystemStore +crypt32!CertVerifyCertificateChainPolicy +crypt32!CertVerifyCRLRevocation +crypt32!CertVerifyCRLTimeValidity +crypt32!CertVerifyCTLUsage +crypt32!CertVerifyRevocation +crypt32!CertVerifySubjectCertificateContext +crypt32!CertVerifyTimeValidity +crypt32!CertVerifyValidityNesting +crypt32!CryptAcquireCertificatePrivateKey +crypt32!CryptBinaryToStringA +crypt32!CryptBinaryToStringW +crypt32!CryptCloseAsyncHandle +crypt32!CryptCreateAsyncHandle +crypt32!CryptCreateKeyIdentifierFromCSP +crypt32!CryptDecodeMessage +crypt32!CryptDecodeObject +crypt32!CryptDecodeObjectEx +crypt32!CryptDecryptAndVerifyMessageSignature +crypt32!CryptDecryptMessage +crypt32!CryptEncodeObject +crypt32!CryptEncodeObjectEx +crypt32!CryptEncryptMessage +crypt32!CryptEnumKeyIdentifierProperties +crypt32!CryptEnumOIDFunction +crypt32!CryptEnumOIDInfo +crypt32!CryptExportPKCS8 +crypt32!CryptExportPublicKeyInfo +crypt32!CryptExportPublicKeyInfoEx +crypt32!CryptExportPublicKeyInfoFromBCryptKeyHandle +crypt32!CryptFindCertificateKeyProvInfo +crypt32!CryptFindLocalizedName +crypt32!CryptFindOIDInfo +crypt32!CryptFormatObject +crypt32!CryptFreeOIDFunctionAddress +crypt32!CryptGetAsyncParam +crypt32!CryptGetDefaultOIDDllList +crypt32!CryptGetDefaultOIDFunctionAddress +crypt32!CryptGetKeyIdentifierProperty +crypt32!CryptGetMessageCertificates +crypt32!CryptGetMessageSignerCount +crypt32!CryptGetOIDFunctionAddress +crypt32!CryptGetOIDFunctionValue +crypt32!CryptHashCertificate +crypt32!CryptHashCertificate2 +crypt32!CryptHashMessage +crypt32!CryptHashPublicKeyInfo +crypt32!CryptHashToBeSigned +crypt32!CryptImportPKCS8 +crypt32!CryptImportPublicKeyInfo +crypt32!CryptImportPublicKeyInfoEx +crypt32!CryptImportPublicKeyInfoEx2 +crypt32!CryptInitOIDFunctionSet +crypt32!CryptInstallDefaultContext +crypt32!CryptInstallOIDFunctionAddress +crypt32!CryptLoadSip +crypt32!CryptMemAlloc +crypt32!CryptMemFree +crypt32!CryptMemRealloc +crypt32!CryptMsgCalculateEncodedLength +crypt32!CryptMsgClose +crypt32!CryptMsgControl +crypt32!CryptMsgCountersign +crypt32!CryptMsgCountersignEncoded +crypt32!CryptMsgDuplicate +crypt32!CryptMsgEncodeAndSignCTL +crypt32!CryptMsgGetAndVerifySigner +crypt32!CryptMsgGetParam +crypt32!CryptMsgOpenToDecode +crypt32!CryptMsgOpenToEncode +crypt32!CryptMsgSignCTL +crypt32!CryptMsgUpdate +crypt32!CryptMsgVerifyCountersignatureEncoded +crypt32!CryptMsgVerifyCountersignatureEncodedEx +crypt32!CryptProtectData +crypt32!CryptProtectMemory +crypt32!CryptQueryObject +crypt32!CryptRegisterDefaultOIDFunction +crypt32!CryptRegisterOIDFunction +crypt32!CryptRegisterOIDInfo +crypt32!CryptRetrieveTimeStamp +crypt32!CryptSetAsyncParam +crypt32!CryptSetKeyIdentifierProperty +crypt32!CryptSetOIDFunctionValue +crypt32!CryptSignAndEncodeCertificate +crypt32!CryptSignAndEncryptMessage +crypt32!CryptSignCertificate +crypt32!CryptSignMessage +crypt32!CryptSignMessageWithKey +crypt32!CryptSIPAddProvider +crypt32!CryptSIPCreateIndirectData +crypt32!CryptSIPGetCaps +crypt32!CryptSIPGetSignedDataMsg +crypt32!CryptSIPLoad +crypt32!CryptSIPPutSignedDataMsg +crypt32!CryptSIPRemoveProvider +crypt32!CryptSIPRemoveSignedDataMsg +crypt32!CryptSIPRetrieveSubjectGuid +crypt32!CryptSIPRetrieveSubjectGuidForCatalogFile +crypt32!CryptSIPVerifyIndirectData +crypt32!CryptStringToBinaryA +crypt32!CryptStringToBinaryW +crypt32!CryptUninstallDefaultContext +crypt32!CryptUnprotectData +crypt32!CryptUnprotectMemory +crypt32!CryptUnregisterDefaultOIDFunction +crypt32!CryptUnregisterOIDFunction +crypt32!CryptUnregisterOIDInfo +crypt32!CryptUpdateProtectedState +crypt32!CryptVerifyCertificateSignature +crypt32!CryptVerifyCertificateSignatureEx +crypt32!CryptVerifyDetachedMessageHash +crypt32!CryptVerifyDetachedMessageSignature +crypt32!CryptVerifyMessageHash +crypt32!CryptVerifyMessageSignature +crypt32!CryptVerifyMessageSignatureWithKey +crypt32!CryptVerifyTimeStampSignature +crypt32!PFXExportCertStore +crypt32!PFXExportCertStore2 +crypt32!PFXExportCertStoreEx +crypt32!PFXImportCertStore +crypt32!PFXIsPFXBlob +crypt32!PFXVerifyPassword +iphlpapi!AddIPAddress +iphlpapi!AllocateAndGetInterfaceInfoFromStack +iphlpapi!AllocateAndGetIpAddrTableFromStack +iphlpapi!CancelIPChangeNotify +iphlpapi!CancelMibChangeNotify2 +iphlpapi!ConvertGuidToStringA +iphlpapi!ConvertGuidToStringW +iphlpapi!ConvertInterfaceAliasToLuid +iphlpapi!ConvertInterfaceGuidToLuid +iphlpapi!ConvertInterfaceIndexToLuid +iphlpapi!ConvertInterfaceLuidToAlias +iphlpapi!ConvertInterfaceLuidToGuid +iphlpapi!ConvertInterfaceLuidToIndex +iphlpapi!ConvertInterfaceLuidToNameA +iphlpapi!ConvertInterfaceLuidToNameW +iphlpapi!ConvertInterfaceNameToLuidA +iphlpapi!ConvertInterfaceNameToLuidW +iphlpapi!ConvertInterfacePhysicalAddressToLuid +iphlpapi!ConvertIpv4MaskToLength +iphlpapi!ConvertLengthToIpv4Mask +iphlpapi!ConvertRemoteInterfaceAliasToLuid +iphlpapi!ConvertRemoteInterfaceGuidToLuid +iphlpapi!ConvertRemoteInterfaceIndexToLuid +iphlpapi!ConvertRemoteInterfaceLuidToAlias +iphlpapi!ConvertRemoteInterfaceLuidToGuid +iphlpapi!ConvertRemoteInterfaceLuidToIndex +iphlpapi!ConvertStringToGuidA +iphlpapi!ConvertStringToGuidW +iphlpapi!ConvertStringToInterfacePhysicalAddress +iphlpapi!CreateAnycastIpAddressEntry +iphlpapi!CreateIpForwardEntry +iphlpapi!CreateIpForwardEntry2 +iphlpapi!CreateIpNetEntry +iphlpapi!CreateIpNetEntry2 +iphlpapi!CreatePersistentTcpPortReservation +iphlpapi!CreatePersistentUdpPortReservation +iphlpapi!CreateProxyArpEntry +iphlpapi!CreateSortedAddressPairs +iphlpapi!CreateUnicastIpAddressEntry +iphlpapi!DeleteAnycastIpAddressEntry +iphlpapi!DeleteIPAddress +iphlpapi!DeleteIpForwardEntry +iphlpapi!DeleteIpForwardEntry2 +iphlpapi!DeleteIpNetEntry +iphlpapi!DeleteIpNetEntry2 +iphlpapi!DeletePersistentTcpPortReservation +iphlpapi!DeletePersistentUdpPortReservation +iphlpapi!DeleteProxyArpEntry +iphlpapi!DeleteUnicastIpAddressEntry +iphlpapi!DisableMediaSense +iphlpapi!EnableRouter +iphlpapi!FlushIpNetTable +iphlpapi!FlushIpNetTable2 +iphlpapi!FlushIpPathTable +iphlpapi!FreeMibTable +iphlpapi!GetAdapterIndex +iphlpapi!GetAdapterOrderMap +iphlpapi!GetAdaptersAddresses +iphlpapi!GetAdaptersInfo +iphlpapi!GetAnycastIpAddressEntry +iphlpapi!GetAnycastIpAddressTable +iphlpapi!GetBestInterface +iphlpapi!GetBestInterfaceEx +iphlpapi!GetBestRoute +iphlpapi!GetBestRoute2 +iphlpapi!GetCurrentThreadCompartmentId +iphlpapi!GetExtendedTcpTable +iphlpapi!GetExtendedUdpTable +iphlpapi!GetFriendlyIfIndex +iphlpapi!GetIcmpStatistics +iphlpapi!GetIcmpStatisticsEx +iphlpapi!GetIfEntry +iphlpapi!GetIfEntry2 +iphlpapi!GetIfStackTable +iphlpapi!GetIfTable +iphlpapi!GetIfTable2 +iphlpapi!GetIfTable2Ex +iphlpapi!GetInterfaceInfo +iphlpapi!GetInvertedIfStackTable +iphlpapi!GetIpAddrTable +iphlpapi!GetIpErrorString +iphlpapi!GetIpForwardEntry2 +iphlpapi!GetIpForwardTable +iphlpapi!GetIpForwardTable2 +iphlpapi!GetIpInterfaceEntry +iphlpapi!GetIpInterfaceTable +iphlpapi!GetIpNetEntry2 +iphlpapi!GetIpNetTable +iphlpapi!GetIpNetTable2 +iphlpapi!GetIpPathEntry +iphlpapi!GetIpPathTable +iphlpapi!GetIpStatistics +iphlpapi!GetIpStatisticsEx +iphlpapi!GetMulticastIpAddressEntry +iphlpapi!GetMulticastIpAddressTable +iphlpapi!GetNetworkInformation +iphlpapi!GetNetworkParams +iphlpapi!GetNumberOfInterfaces +iphlpapi!GetOwnerModuleFromPidAndInfo +iphlpapi!GetOwnerModuleFromTcp6Entry +iphlpapi!GetOwnerModuleFromTcpEntry +iphlpapi!GetOwnerModuleFromUdp6Entry +iphlpapi!GetOwnerModuleFromUdpEntry +iphlpapi!GetPerAdapterInfo +iphlpapi!GetPerTcp6ConnectionEStats +iphlpapi!GetPerTcp6ConnectionStats +iphlpapi!GetPerTcpConnectionEStats +iphlpapi!GetPerTcpConnectionStats +iphlpapi!GetRTTAndHopCount +iphlpapi!GetSessionCompartmentId +iphlpapi!GetTcp6Table +iphlpapi!GetTcp6Table2 +iphlpapi!GetTcpStatistics +iphlpapi!GetTcpStatisticsEx +iphlpapi!GetTcpTable +iphlpapi!GetTcpTable2 +iphlpapi!GetTeredoPort +iphlpapi!GetUdp6Table +iphlpapi!GetUdpStatistics +iphlpapi!GetUdpStatisticsEx +iphlpapi!GetUdpTable +iphlpapi!GetUnicastIpAddressEntry +iphlpapi!GetUnicastIpAddressTable +iphlpapi!GetUniDirectionalAdapterInfo +iphlpapi!Icmp6CreateFile +iphlpapi!Icmp6ParseReplies +iphlpapi!Icmp6SendEcho2 +iphlpapi!IcmpCloseHandle +iphlpapi!IcmpCreateFile +iphlpapi!IcmpParseReplies +iphlpapi!IcmpSendEcho +iphlpapi!IcmpSendEcho2 +iphlpapi!IcmpSendEcho2Ex +iphlpapi!if_indextoname +iphlpapi!if_nametoindex +iphlpapi!InitializeIpForwardEntry +iphlpapi!InitializeIpInterfaceEntry +iphlpapi!InitializeUnicastIpAddressEntry +iphlpapi!InternalCleanupPersistentStore +iphlpapi!InternalCreateAnycastIpAddressEntry +iphlpapi!InternalCreateIpForwardEntry +iphlpapi!InternalCreateIpForwardEntry2 +iphlpapi!InternalCreateIpNetEntry +iphlpapi!InternalCreateIpNetEntry2 +iphlpapi!InternalCreateUnicastIpAddressEntry +iphlpapi!InternalDeleteAnycastIpAddressEntry +iphlpapi!InternalDeleteIpForwardEntry +iphlpapi!InternalDeleteIpForwardEntry2 +iphlpapi!InternalDeleteIpNetEntry +iphlpapi!InternalDeleteIpNetEntry2 +iphlpapi!InternalDeleteUnicastIpAddressEntry +iphlpapi!InternalFindInterfaceByAddress +iphlpapi!InternalGetAnycastIpAddressEntry +iphlpapi!InternalGetAnycastIpAddressTable +iphlpapi!InternalGetForwardIpTable2 +iphlpapi!InternalGetIfEntry2 +iphlpapi!InternalGetIfTable +iphlpapi!InternalGetIfTable2 +iphlpapi!InternalGetIpAddrTable +iphlpapi!InternalGetIpForwardEntry2 +iphlpapi!InternalGetIpForwardTable +iphlpapi!InternalGetIpInterfaceEntry +iphlpapi!InternalGetIpInterfaceTable +iphlpapi!InternalGetIpNetEntry2 +iphlpapi!InternalGetIpNetTable +iphlpapi!InternalGetIpNetTable2 +iphlpapi!InternalGetMulticastIpAddressEntry +iphlpapi!InternalGetMulticastIpAddressTable +iphlpapi!InternalGetTcp6Table2 +iphlpapi!InternalGetTcp6TableWithOwnerModule +iphlpapi!InternalGetTcp6TableWithOwnerPid +iphlpapi!InternalGetTcpTable +iphlpapi!InternalGetTcpTable2 +iphlpapi!InternalGetTcpTableEx +iphlpapi!InternalGetTcpTableWithOwnerModule +iphlpapi!InternalGetTcpTableWithOwnerPid +iphlpapi!InternalGetTunnelPhysicalAdapter +iphlpapi!InternalGetUdp6TableWithOwnerModule +iphlpapi!InternalGetUdp6TableWithOwnerPid +iphlpapi!InternalGetUdpTable +iphlpapi!InternalGetUdpTableEx +iphlpapi!InternalGetUdpTableWithOwnerModule +iphlpapi!InternalGetUdpTableWithOwnerPid +iphlpapi!InternalGetUnicastIpAddressEntry +iphlpapi!InternalGetUnicastIpAddressTable +iphlpapi!InternalSetIfEntry +iphlpapi!InternalSetIpForwardEntry +iphlpapi!InternalSetIpForwardEntry2 +iphlpapi!InternalSetIpInterfaceEntry +iphlpapi!InternalSetIpNetEntry +iphlpapi!InternalSetIpNetEntry2 +iphlpapi!InternalSetIpStats +iphlpapi!InternalSetTcpEntry +iphlpapi!InternalSetTeredoPort +iphlpapi!InternalSetUnicastIpAddressEntry +iphlpapi!IpReleaseAddress +iphlpapi!IpRenewAddress +iphlpapi!LookupPersistentTcpPortReservation +iphlpapi!LookupPersistentUdpPortReservation +iphlpapi!NhGetGuidFromInterfaceName +iphlpapi!NhGetInterfaceDescriptionFromGuid +iphlpapi!NhGetInterfaceNameFromDeviceGuid +iphlpapi!NhGetInterfaceNameFromGuid +iphlpapi!NhpAllocateAndGetInterfaceInfoFromStack +iphlpapi!NotifyAddrChange +iphlpapi!NotifyIpInterfaceChange +iphlpapi!NotifyRouteChange +iphlpapi!NotifyRouteChange2 +iphlpapi!NotifyStableUnicastIpAddressTable +iphlpapi!NotifyTeredoPortChange +iphlpapi!NotifyUnicastIpAddressChange +iphlpapi!NTPTimeToNTFileTime +iphlpapi!NTTimeToNTPTime +iphlpapi!ParseNetworkString +iphlpapi!ResolveIpNetEntry2 +iphlpapi!ResolveNeighbor +iphlpapi!RestoreMediaSense +iphlpapi!SendARP +iphlpapi!SetAdapterIpAddress +iphlpapi!SetCurrentThreadCompartmentId +iphlpapi!SetIfEntry +iphlpapi!SetIpForwardEntry +iphlpapi!SetIpForwardEntry2 +iphlpapi!SetIpInterfaceEntry +iphlpapi!SetIpNetEntry +iphlpapi!SetIpNetEntry2 +iphlpapi!SetIpStatistics +iphlpapi!SetIpStatisticsEx +iphlpapi!SetIpTTL +iphlpapi!SetNetworkInformation +iphlpapi!SetPerTcp6ConnectionEStats +iphlpapi!SetPerTcp6ConnectionStats +iphlpapi!SetPerTcpConnectionEStats +iphlpapi!SetPerTcpConnectionStats +iphlpapi!SetSessionCompartmentId +iphlpapi!SetTcpEntry +iphlpapi!SetUnicastIpAddressEntry +iphlpapi!UnenableRouter +kernel32!AcquireSRWLockExclusive +kernel32!AcquireSRWLockShared +kernel32!AddConsoleAliasA +kernel32!AddConsoleAliasW +kernel32!AddDllDirectory +kernel32!AddSIDToBoundaryDescriptor +kernel32!AddVectoredContinueHandler +kernel32!AddVectoredExceptionHandler +kernel32!AllocateUserPhysicalPages +kernel32!AllocateUserPhysicalPagesNuma +kernel32!AllocConsole +kernel32!AreFileApisANSI +kernel32!AttachConsole +kernel32!Beep +kernel32!CallbackMayRunLong +kernel32!CallNamedPipeW +kernel32!CancelIo +kernel32!CancelIoEx +kernel32!CancelSynchronousIo +kernel32!CancelThreadpoolIo +kernel32!CancelWaitableTimer +kernel32!CheckRemoteDebuggerPresent +kernel32!ClearCommBreak +kernel32!ClearCommError +kernel32!CloseHandle +kernel32!ClosePrivateNamespace +kernel32!CloseThreadpool +kernel32!CloseThreadpoolCleanupGroup +kernel32!CloseThreadpoolCleanupGroupMembers +kernel32!CloseThreadpoolIo +kernel32!CloseThreadpoolTimer +kernel32!CloseThreadpoolWait +kernel32!CloseThreadpoolWork +kernel32!CompareFileTime +kernel32!CompareStringEx +kernel32!CompareStringOrdinal +kernel32!CompareStringW +kernel32!ConnectNamedPipe +kernel32!ContinueDebugEvent +kernel32!ConvertDefaultLocale +kernel32!CopyContext +kernel32!CopyFileExW +kernel32!CopyFileW +kernel32!CreateBoundaryDescriptorW +kernel32!CreateConsoleScreenBuffer +kernel32!CreateDirectoryA +kernel32!CreateDirectoryExW +kernel32!CreateDirectoryW +kernel32!CreateEventA +kernel32!CreateEventExA +kernel32!CreateEventExW +kernel32!CreateEventW +kernel32!CreateFileA +kernel32!CreateFileMappingNumaW +kernel32!CreateFileMappingW +kernel32!CreateFileW +kernel32!CreateHardLinkA +kernel32!CreateHardLinkW +kernel32!CreateIoCompletionPort +kernel32!CreateMemoryResourceNotification +kernel32!CreateMutexA +kernel32!CreateMutexExA +kernel32!CreateMutexExW +kernel32!CreateMutexW +kernel32!CreateNamedPipeW +kernel32!CreatePipe +kernel32!CreatePrivateNamespaceW +kernel32!CreateProcessA +kernel32!CreateProcessW +kernel32!CreateRemoteThread +kernel32!CreateRemoteThreadEx +kernel32!CreateSemaphoreExW +kernel32!CreateSemaphoreW +kernel32!CreateSymbolicLinkW +kernel32!CreateThread +kernel32!CreateThreadpool +kernel32!CreateThreadpoolCleanupGroup +kernel32!CreateThreadpoolIo +kernel32!CreateThreadpoolTimer +kernel32!CreateThreadpoolWait +kernel32!CreateThreadpoolWork +kernel32!CreateWaitableTimerExW +kernel32!CreateWaitableTimerW +kernel32!DebugActiveProcess +kernel32!DebugActiveProcessStop +kernel32!DebugBreak +kernel32!DecodePointer +kernel32!DecodeSystemPointer +kernel32!DefineDosDeviceW +kernel32!DelayLoadFailureHook +kernel32!DeleteBoundaryDescriptor +kernel32!DeleteCriticalSection +kernel32!DeleteFileA +kernel32!DeleteFileW +kernel32!DeleteProcThreadAttributeList +kernel32!DeleteVolumeMountPointW +kernel32!DeviceIoControl +kernel32!DisableThreadLibraryCalls +kernel32!DisassociateCurrentThreadFromCallback +kernel32!DisconnectNamedPipe +kernel32!DuplicateHandle +kernel32!EncodePointer +kernel32!EncodeSystemPointer +kernel32!EnterCriticalSection +kernel32!EnumCalendarInfoExEx +kernel32!EnumCalendarInfoExW +kernel32!EnumCalendarInfoW +kernel32!EnumDateFormatsExEx +kernel32!EnumDateFormatsExW +kernel32!EnumDateFormatsW +kernel32!EnumResourceLanguagesExA +kernel32!EnumResourceLanguagesExW +kernel32!EnumResourceNamesExA +kernel32!EnumResourceNamesExW +kernel32!EnumResourceNamesW +kernel32!EnumResourceTypesExA +kernel32!EnumResourceTypesExW +kernel32!EnumSystemCodePagesW +kernel32!EnumSystemFirmwareTables +kernel32!EnumSystemGeoID +kernel32!EnumSystemLocalesA +kernel32!EnumSystemLocalesEx +kernel32!EnumSystemLocalesW +kernel32!EnumTimeFormatsEx +kernel32!EnumTimeFormatsW +kernel32!EscapeCommFunction +kernel32!ExitProcess +kernel32!ExitThread +kernel32!ExpandEnvironmentStringsA +kernel32!ExpandEnvironmentStringsW +kernel32!FatalAppExitA +kernel32!FatalAppExitW +kernel32!FileTimeToLocalFileTime +kernel32!FileTimeToSystemTime +kernel32!FillConsoleOutputAttribute +kernel32!FillConsoleOutputCharacterA +kernel32!FillConsoleOutputCharacterW +kernel32!FindClose +kernel32!FindCloseChangeNotification +kernel32!FindFirstChangeNotificationA +kernel32!FindFirstChangeNotificationW +kernel32!FindFirstFileA +kernel32!FindFirstFileExA +kernel32!FindFirstFileExW +kernel32!FindFirstFileNameW +kernel32!FindFirstFileW +kernel32!FindFirstStreamW +kernel32!FindFirstVolumeW +kernel32!FindNextChangeNotification +kernel32!FindNextFileA +kernel32!FindNextFileNameW +kernel32!FindNextFileW +kernel32!FindNextStreamW +kernel32!FindNextVolumeW +kernel32!FindNLSString +kernel32!FindNLSStringEx +kernel32!FindResourceExW +kernel32!FindResourceW +kernel32!FindStringOrdinal +kernel32!FindVolumeClose +kernel32!FlsAlloc +kernel32!FlsFree +kernel32!FlsGetValue +kernel32!FlsSetValue +kernel32!FlushConsoleInputBuffer +kernel32!FlushFileBuffers +kernel32!FlushInstructionCache +kernel32!FlushProcessWriteBuffers +kernel32!FlushViewOfFile +kernel32!FoldStringW +kernel32!FormatMessageA +kernel32!FormatMessageW +kernel32!FreeConsole +kernel32!FreeEnvironmentStringsA +kernel32!FreeEnvironmentStringsW +kernel32!FreeLibrary +kernel32!FreeLibraryAndExitThread +kernel32!FreeLibraryWhenCallbackReturns +kernel32!FreeResource +kernel32!FreeUserPhysicalPages +kernel32!GenerateConsoleCtrlEvent +kernel32!GetACP +kernel32!GetCalendarInfoEx +kernel32!GetCalendarInfoW +kernel32!GetCommandLineA +kernel32!GetCommandLineW +kernel32!GetCommConfig +kernel32!GetCommMask +kernel32!GetCommModemStatus +kernel32!GetCommProperties +kernel32!GetCommState +kernel32!GetCommTimeouts +kernel32!GetCompressedFileSizeA +kernel32!GetCompressedFileSizeW +kernel32!GetComputerNameExA +kernel32!GetComputerNameExW +kernel32!GetConsoleAliasA +kernel32!GetConsoleAliasesA +kernel32!GetConsoleAliasesLengthA +kernel32!GetConsoleAliasesLengthW +kernel32!GetConsoleAliasesW +kernel32!GetConsoleAliasExesA +kernel32!GetConsoleAliasExesLengthA +kernel32!GetConsoleAliasExesLengthW +kernel32!GetConsoleAliasExesW +kernel32!GetConsoleAliasW +kernel32!GetConsoleCP +kernel32!GetConsoleCursorInfo +kernel32!GetConsoleDisplayMode +kernel32!GetConsoleFontSize +kernel32!GetConsoleHistoryInfo +kernel32!GetConsoleMode +kernel32!GetConsoleOriginalTitleA +kernel32!GetConsoleOriginalTitleW +kernel32!GetConsoleOutputCP +kernel32!GetConsoleProcessList +kernel32!GetConsoleScreenBufferInfo +kernel32!GetConsoleScreenBufferInfoEx +kernel32!GetConsoleSelectionInfo +kernel32!GetConsoleTitleA +kernel32!GetConsoleTitleW +kernel32!GetConsoleWindow +kernel32!GetCPInfo +kernel32!GetCPInfoExW +kernel32!GetCurrencyFormatEx +kernel32!GetCurrencyFormatW +kernel32!GetCurrentConsoleFont +kernel32!GetCurrentConsoleFontEx +kernel32!GetCurrentDirectoryA +kernel32!GetCurrentDirectoryW +kernel32!GetCurrentProcess +kernel32!GetCurrentProcessId +kernel32!GetCurrentProcessorNumber +kernel32!GetCurrentProcessorNumberEx +kernel32!GetCurrentThread +kernel32!GetCurrentThreadId +kernel32!GetDateFormatA +kernel32!GetDateFormatEx +kernel32!GetDateFormatW +kernel32!GetDiskFreeSpaceA +kernel32!GetDiskFreeSpaceExA +kernel32!GetDiskFreeSpaceExW +kernel32!GetDiskFreeSpaceW +kernel32!GetDriveTypeA +kernel32!GetDriveTypeW +kernel32!GetDurationFormatEx +kernel32!GetDynamicTimeZoneInformation +kernel32!GetEnvironmentStrings +kernel32!GetEnvironmentStringsW +kernel32!GetEnvironmentVariableA +kernel32!GetEnvironmentVariableW +kernel32!GetErrorMode +kernel32!GetExitCodeProcess +kernel32!GetExitCodeThread +kernel32!GetFileAttributesA +kernel32!GetFileAttributesExA +kernel32!GetFileAttributesExW +kernel32!GetFileAttributesW +kernel32!GetFileInformationByHandle +kernel32!GetFileInformationByHandleEx +kernel32!GetFileMUIInfo +kernel32!GetFileMUIPath +kernel32!GetFileSize +kernel32!GetFileSizeEx +kernel32!GetFileTime +kernel32!GetFileType +kernel32!GetFinalPathNameByHandleA +kernel32!GetFinalPathNameByHandleW +kernel32!GetFirmwareEnvironmentVariableA +kernel32!GetFirmwareEnvironmentVariableW +kernel32!GetFullPathNameA +kernel32!GetFullPathNameW +kernel32!GetGeoInfoW +kernel32!GetHandleInformation +kernel32!GetLargePageMinimum +kernel32!GetLargestConsoleWindowSize +kernel32!GetLastError +kernel32!GetLocaleInfoA +kernel32!GetLocaleInfoEx +kernel32!GetLocaleInfoW +kernel32!GetLocalTime +kernel32!GetLogicalDrives +kernel32!GetLogicalDriveStringsW +kernel32!GetLogicalProcessorInformation +kernel32!GetLogicalProcessorInformationEx +kernel32!GetLongPathNameA +kernel32!GetLongPathNameW +kernel32!GetModuleFileNameA +kernel32!GetModuleFileNameW +kernel32!GetModuleHandleA +kernel32!GetModuleHandleExA +kernel32!GetModuleHandleExW +kernel32!GetModuleHandleW +kernel32!GetNamedPipeClientComputerNameW +kernel32!GetNamedPipeHandleStateW +kernel32!GetNamedPipeInfo +kernel32!GetNativeSystemInfo +kernel32!GetNLSVersion +kernel32!GetNLSVersionEx +kernel32!GetNumaHighestNodeNumber +kernel32!GetNumaNodeProcessorMaskEx +kernel32!GetNumaProximityNodeEx +kernel32!GetNumberFormatEx +kernel32!GetNumberOfConsoleInputEvents +kernel32!GetNumberOfConsoleMouseButtons +kernel32!GetOEMCP +kernel32!GetOverlappedResult +kernel32!GetPhysicallyInstalledSystemMemory +kernel32!GetPriorityClass +kernel32!GetProcAddress +kernel32!GetProcessGroupAffinity +kernel32!GetProcessHandleCount +kernel32!GetProcessHeap +kernel32!GetProcessHeaps +kernel32!GetProcessId +kernel32!GetProcessIdOfThread +kernel32!GetProcessorSystemCycleTime +kernel32!GetProcessPreferredUILanguages +kernel32!GetProcessPriorityBoost +kernel32!GetProcessShutdownParameters +kernel32!GetProcessTimes +kernel32!GetProcessVersion +kernel32!GetProcessWorkingSetSizeEx +kernel32!GetProductInfo +kernel32!GetQueuedCompletionStatus +kernel32!GetQueuedCompletionStatusEx +kernel32!GetShortPathNameW +kernel32!GetStartupInfoW +kernel32!GetStdHandle +kernel32!GetStringTypeExW +kernel32!GetStringTypeW +kernel32!GetSystemDefaultLangID +kernel32!GetSystemDefaultLCID +kernel32!GetSystemDefaultLocaleName +kernel32!GetSystemDirectoryA +kernel32!GetSystemDirectoryW +kernel32!GetSystemFileCacheSize +kernel32!GetSystemFirmwareTable +kernel32!GetSystemInfo +kernel32!GetSystemPreferredUILanguages +kernel32!GetSystemTime +kernel32!GetSystemTimeAdjustment +kernel32!GetSystemTimeAsFileTime +kernel32!GetSystemTimes +kernel32!GetSystemWindowsDirectoryA +kernel32!GetSystemWindowsDirectoryW +kernel32!GetSystemWow64DirectoryA +kernel32!GetSystemWow64DirectoryW +kernel32!GetTempFileNameA +kernel32!GetTempFileNameW +kernel32!GetTempPathA +kernel32!GetTempPathW +kernel32!GetThreadContext +kernel32!GetThreadErrorMode +kernel32!GetThreadGroupAffinity +kernel32!GetThreadId +kernel32!GetThreadIdealProcessorEx +kernel32!GetThreadIOPendingFlag +kernel32!GetThreadLocale +kernel32!GetThreadPreferredUILanguages +kernel32!GetThreadPriority +kernel32!GetThreadPriorityBoost +kernel32!GetThreadTimes +kernel32!GetThreadUILanguage +kernel32!GetTickCount +kernel32!GetTickCount64 +kernel32!GetTimeFormatA +kernel32!GetTimeFormatEx +kernel32!GetTimeFormatW +kernel32!GetTimeZoneInformation +kernel32!GetTimeZoneInformationForYear +kernel32!GetUILanguageInfo +kernel32!GetUserDefaultLangID +kernel32!GetUserDefaultLCID +kernel32!GetUserDefaultLocaleName +kernel32!GetUserGeoID +kernel32!GetUserPreferredUILanguages +kernel32!GetVersion +kernel32!GetVersionExA +kernel32!GetVersionExW +kernel32!GetVolumeInformationA +kernel32!GetVolumeInformationByHandleW +kernel32!GetVolumeInformationW +kernel32!GetVolumeNameForVolumeMountPointW +kernel32!GetVolumePathNamesForVolumeNameW +kernel32!GetVolumePathNameW +kernel32!GetWindowsDirectoryA +kernel32!GetWindowsDirectoryW +kernel32!GetWriteWatch +kernel32!GlobalAlloc +kernel32!GlobalFree +kernel32!GlobalMemoryStatusEx +kernel32!HeapAlloc +kernel32!HeapCompact +kernel32!HeapCreate +kernel32!HeapDestroy +kernel32!HeapFree +kernel32!HeapLock +kernel32!HeapQueryInformation +kernel32!HeapReAlloc +kernel32!HeapSetInformation +kernel32!HeapSize +kernel32!HeapUnlock +kernel32!HeapValidate +kernel32!HeapWalk +kernel32!IdnToAscii +kernel32!IdnToUnicode +kernel32!InitializeConditionVariable +kernel32!InitializeContext +kernel32!InitializeCriticalSection +kernel32!InitializeCriticalSectionAndSpinCount +kernel32!InitializeCriticalSectionEx +kernel32!InitializeProcThreadAttributeList +kernel32!InitializeSListHead +kernel32!InitializeSRWLock +kernel32!InitOnceBeginInitialize +kernel32!InitOnceComplete +kernel32!InitOnceExecuteOnce +kernel32!InitOnceInitialize +kernel32!InterlockedFlushSList +kernel32!InterlockedPopEntrySList +kernel32!InterlockedPushEntrySList +kernel32!IsDBCSLeadByte +kernel32!IsDBCSLeadByteEx +kernel32!IsDebuggerPresent +kernel32!IsNLSDefinedString +kernel32!IsProcessInJob +kernel32!IsProcessorFeaturePresent +kernel32!IsThreadAFiber +kernel32!IsThreadpoolTimerSet +kernel32!IsValidCodePage +kernel32!IsValidLanguageGroup +kernel32!IsValidLocale +kernel32!IsValidLocaleName +kernel32!IsWow64Process +kernel32!K32EmptyWorkingSet +kernel32!K32EnumDeviceDrivers +kernel32!K32EnumPageFilesW +kernel32!K32EnumProcesses +kernel32!K32EnumProcessModules +kernel32!K32EnumProcessModulesEx +kernel32!K32GetDeviceDriverBaseNameW +kernel32!K32GetDeviceDriverFileNameW +kernel32!K32GetMappedFileNameW +kernel32!K32GetModuleBaseNameW +kernel32!K32GetModuleFileNameExW +kernel32!K32GetModuleInformation +kernel32!K32GetPerformanceInfo +kernel32!K32GetProcessImageFileNameW +kernel32!K32GetProcessMemoryInfo +kernel32!K32GetWsChanges +kernel32!K32GetWsChangesEx +kernel32!K32InitializeProcessForWsWatch +kernel32!K32QueryWorkingSet +kernel32!K32QueryWorkingSetEx +kernel32!LCIDToLocaleName +kernel32!LCMapStringA +kernel32!LCMapStringEx +kernel32!LCMapStringW +kernel32!LeaveCriticalSection +kernel32!LeaveCriticalSectionWhenCallbackReturns +kernel32!LoadLibraryA +kernel32!LoadLibraryExA +kernel32!LoadLibraryExW +kernel32!LoadLibraryW +kernel32!LoadResource +kernel32!LocalAlloc +kernel32!LocaleNameToLCID +kernel32!LocalFileTimeToFileTime +kernel32!LocalFree +kernel32!LocalLock +kernel32!LocalReAlloc +kernel32!LocalUnlock +kernel32!LockFile +kernel32!LockFileEx +kernel32!LockResource +kernel32!MapUserPhysicalPages +kernel32!MapViewOfFile +kernel32!MapViewOfFileEx +kernel32!MoveFileExW +kernel32!MoveFileWithProgressW +kernel32!MultiByteToWideChar +kernel32!NeedCurrentDirectoryForExePathA +kernel32!NeedCurrentDirectoryForExePathW +kernel32!OpenEventA +kernel32!OpenEventW +kernel32!OpenFileById +kernel32!OpenFileMappingW +kernel32!OpenMutexW +kernel32!OpenPrivateNamespaceW +kernel32!OpenProcess +kernel32!OpenSemaphoreW +kernel32!OpenThread +kernel32!OpenWaitableTimerW +kernel32!OutputDebugStringA +kernel32!OutputDebugStringW +kernel32!PeekConsoleInputA +kernel32!PeekConsoleInputW +kernel32!PeekNamedPipe +kernel32!PostQueuedCompletionStatus +kernel32!ProcessIdToSessionId +kernel32!PurgeComm +kernel32!QueryDepthSList +kernel32!QueryDosDeviceW +kernel32!QueryFullProcessImageNameW +kernel32!QueryIdleProcessorCycleTime +kernel32!QueryIdleProcessorCycleTimeEx +kernel32!QueryMemoryResourceNotification +kernel32!QueryPerformanceCounter +kernel32!QueryPerformanceFrequency +kernel32!QueryProcessAffinityUpdateMode +kernel32!QueryProcessCycleTime +kernel32!QueryThreadCycleTime +kernel32!QueryThreadpoolStackInformation +kernel32!QueryUnbiasedInterruptTime +kernel32!QueueUserAPC +kernel32!RaiseException +kernel32!RaiseFailFastException +kernel32!ReadConsoleA +kernel32!ReadConsoleInputA +kernel32!ReadConsoleInputW +kernel32!ReadConsoleOutputA +kernel32!ReadConsoleOutputAttribute +kernel32!ReadConsoleOutputCharacterA +kernel32!ReadConsoleOutputCharacterW +kernel32!ReadConsoleOutputW +kernel32!ReadConsoleW +kernel32!ReadDirectoryChangesW +kernel32!ReadFile +kernel32!ReadFileEx +kernel32!ReadFileScatter +kernel32!ReadProcessMemory +kernel32!ReleaseMutex +kernel32!ReleaseMutexWhenCallbackReturns +kernel32!ReleaseSemaphore +kernel32!ReleaseSemaphoreWhenCallbackReturns +kernel32!ReleaseSRWLockExclusive +kernel32!ReleaseSRWLockShared +kernel32!RemoveDirectoryA +kernel32!RemoveDirectoryW +kernel32!RemoveDllDirectory +kernel32!RemoveVectoredContinueHandler +kernel32!RemoveVectoredExceptionHandler +kernel32!ReOpenFile +kernel32!ReplaceFileW +kernel32!ResetEvent +kernel32!ResetWriteWatch +kernel32!ResolveLocaleName +kernel32!RestoreLastError +kernel32!ResumeThread +kernel32!RtlCaptureContext +kernel32!RtlCaptureStackBackTrace +kernel32!RtlUnwind +kernel32!ScrollConsoleScreenBufferA +kernel32!ScrollConsoleScreenBufferW +kernel32!SearchPathA +kernel32!SearchPathW +kernel32!SetCalendarInfoW +kernel32!SetCommBreak +kernel32!SetCommConfig +kernel32!SetCommMask +kernel32!SetCommState +kernel32!SetCommTimeouts +kernel32!SetComputerNameA +kernel32!SetComputerNameExA +kernel32!SetComputerNameExW +kernel32!SetComputerNameW +kernel32!SetConsoleActiveScreenBuffer +kernel32!SetConsoleCP +kernel32!SetConsoleCtrlHandler +kernel32!SetConsoleCursorInfo +kernel32!SetConsoleCursorPosition +kernel32!SetConsoleDisplayMode +kernel32!SetConsoleHistoryInfo +kernel32!SetConsoleMode +kernel32!SetConsoleOutputCP +kernel32!SetConsoleScreenBufferInfoEx +kernel32!SetConsoleScreenBufferSize +kernel32!SetConsoleTextAttribute +kernel32!SetConsoleTitleA +kernel32!SetConsoleTitleW +kernel32!SetConsoleWindowInfo +kernel32!SetCriticalSectionSpinCount +kernel32!SetCurrentConsoleFontEx +kernel32!SetCurrentDirectoryA +kernel32!SetCurrentDirectoryW +kernel32!SetDefaultDllDirectories +kernel32!SetDynamicTimeZoneInformation +kernel32!SetEndOfFile +kernel32!SetEnvironmentStringsW +kernel32!SetEnvironmentVariableA +kernel32!SetEnvironmentVariableW +kernel32!SetErrorMode +kernel32!SetEvent +kernel32!SetEventWhenCallbackReturns +kernel32!SetFileApisToANSI +kernel32!SetFileApisToOEM +kernel32!SetFileAttributesA +kernel32!SetFileAttributesW +kernel32!SetFileInformationByHandle +kernel32!SetFileIoOverlappedRange +kernel32!SetFilePointer +kernel32!SetFilePointerEx +kernel32!SetFileTime +kernel32!SetFileValidData +kernel32!SetFirmwareEnvironmentVariableA +kernel32!SetFirmwareEnvironmentVariableW +kernel32!SetHandleInformation +kernel32!SetLastError +kernel32!SetLocaleInfoW +kernel32!SetLocalTime +kernel32!SetNamedPipeHandleState +kernel32!SetPriorityClass +kernel32!SetProcessAffinityUpdateMode +kernel32!SetProcessPreferredUILanguages +kernel32!SetProcessPriorityBoost +kernel32!SetProcessShutdownParameters +kernel32!SetProcessWorkingSetSizeEx +kernel32!SetStdHandle +kernel32!SetStdHandleEx +kernel32!SetSystemFileCacheSize +kernel32!SetSystemTime +kernel32!SetSystemTimeAdjustment +kernel32!SetThreadContext +kernel32!SetThreadErrorMode +kernel32!SetThreadGroupAffinity +kernel32!SetThreadIdealProcessor +kernel32!SetThreadIdealProcessorEx +kernel32!SetThreadLocale +kernel32!SetThreadpoolStackInformation +kernel32!SetThreadpoolThreadMaximum +kernel32!SetThreadpoolThreadMinimum +kernel32!SetThreadpoolTimer +kernel32!SetThreadpoolWait +kernel32!SetThreadPreferredUILanguages +kernel32!SetThreadPriority +kernel32!SetThreadPriorityBoost +kernel32!SetThreadStackGuarantee +kernel32!SetThreadUILanguage +kernel32!SetTimeZoneInformation +kernel32!SetUnhandledExceptionFilter +kernel32!SetupComm +kernel32!SetUserGeoID +kernel32!SetWaitableTimer +kernel32!SetWaitableTimerEx +kernel32!SignalObjectAndWait +kernel32!SizeofResource +kernel32!Sleep +kernel32!SleepConditionVariableCS +kernel32!SleepConditionVariableSRW +kernel32!SleepEx +kernel32!StartThreadpoolIo +kernel32!SubmitThreadpoolWork +kernel32!SuspendThread +kernel32!SwitchToThread +kernel32!SystemTimeToFileTime +kernel32!SystemTimeToTzSpecificLocalTime +kernel32!SystemTimeToTzSpecificLocalTimeEx +kernel32!TerminateProcess +kernel32!TerminateThread +kernel32!TlsAlloc +kernel32!TlsFree +kernel32!TlsGetValue +kernel32!TlsSetValue +kernel32!TransactNamedPipe +kernel32!TransmitCommChar +kernel32!TryAcquireSRWLockExclusive +kernel32!TryAcquireSRWLockShared +kernel32!TryEnterCriticalSection +kernel32!TrySubmitThreadpoolCallback +kernel32!TzSpecificLocalTimeToSystemTime +kernel32!TzSpecificLocalTimeToSystemTimeEx +kernel32!UnhandledExceptionFilter +kernel32!UnlockFile +kernel32!UnlockFileEx +kernel32!UnmapViewOfFile +kernel32!UpdateProcThreadAttribute +kernel32!VerSetConditionMask +kernel32!VirtualAlloc +kernel32!VirtualAllocEx +kernel32!VirtualAllocExNuma +kernel32!VirtualFree +kernel32!VirtualFreeEx +kernel32!VirtualLock +kernel32!VirtualProtect +kernel32!VirtualProtectEx +kernel32!VirtualQuery +kernel32!VirtualQueryEx +kernel32!VirtualUnlock +kernel32!WaitCommEvent +kernel32!WaitForDebugEvent +kernel32!WaitForMultipleObjects +kernel32!WaitForMultipleObjectsEx +kernel32!WaitForSingleObject +kernel32!WaitForSingleObjectEx +kernel32!WaitForThreadpoolIoCallbacks +kernel32!WaitForThreadpoolTimerCallbacks +kernel32!WaitForThreadpoolWaitCallbacks +kernel32!WaitForThreadpoolWorkCallbacks +kernel32!WaitNamedPipeW +kernel32!WakeAllConditionVariable +kernel32!WakeConditionVariable +kernel32!WideCharToMultiByte +kernel32!Wow64DisableWow64FsRedirection +kernel32!Wow64RevertWow64FsRedirection +kernel32!WriteConsoleA +kernel32!WriteConsoleInputA +kernel32!WriteConsoleInputW +kernel32!WriteConsoleOutputA +kernel32!WriteConsoleOutputAttribute +kernel32!WriteConsoleOutputCharacterA +kernel32!WriteConsoleOutputCharacterW +kernel32!WriteConsoleOutputW +kernel32!WriteConsoleW +kernel32!WriteFile +kernel32!WriteFileEx +kernel32!WriteFileGather +kernel32!WriteProcessMemory +mswsock!AcceptEx +mswsock!dn_expand +mswsock!EnumProtocolsA +mswsock!EnumProtocolsW +mswsock!GetAcceptExSockaddrs +mswsock!GetAddressByNameA +mswsock!GetAddressByNameW +mswsock!GetNameByTypeA +mswsock!GetNameByTypeW +mswsock!getnetbyname +mswsock!GetServiceA +mswsock!GetServiceW +mswsock!GetTypeByNameA +mswsock!GetTypeByNameW +mswsock!inet_network +mswsock!MigrateWinsockConfiguration +mswsock!NPLoadNameSpaces +mswsock!rcmd +mswsock!rexec +mswsock!rresvport +mswsock!s_perror +mswsock!sethostname +mswsock!SetServiceA +mswsock!SetServiceW +mswsock!TransmitFile +mswsock!WSARecvEx +ncrypt!GetIsolationServerInterface +ncrypt!GetKeyStorageInterface +ncrypt!GetSChannelInterface +ncrypt!NCryptCreatePersistedKey +ncrypt!NCryptDecrypt +ncrypt!NCryptDeleteKey +ncrypt!NCryptDeriveKey +ncrypt!NCryptEncrypt +ncrypt!NCryptEnumAlgorithms +ncrypt!NCryptEnumKeys +ncrypt!NCryptEnumStorageProviders +ncrypt!NCryptExportKey +ncrypt!NCryptFinalizeKey +ncrypt!NCryptFreeBuffer +ncrypt!NCryptFreeObject +ncrypt!NCryptGetProperty +ncrypt!NCryptImportKey +ncrypt!NCryptIsAlgSupported +ncrypt!NCryptIsKeyHandle +ncrypt!NCryptNotifyChangeKey +ncrypt!NCryptOpenKey +ncrypt!NCryptOpenStorageProvider +ncrypt!NCryptSecretAgreement +ncrypt!NCryptSetAuditingInterface +ncrypt!NCryptSetProperty +ncrypt!NCryptSignHash +ncrypt!NCryptTranslateHandle +ncrypt!NCryptVerifySignature +ncrypt!SslChangeNotify +ncrypt!SslComputeClientAuthHash +ncrypt!SslComputeEapKeyBlock +ncrypt!SslComputeFinishedHash +ncrypt!SslComputeSessionHash +ncrypt!SslCreateClientAuthHash +ncrypt!SslCreateEphemeralKey +ncrypt!SslCreateHandshakeHash +ncrypt!SslDecrementProviderReferenceCount +ncrypt!SslDecryptPacket +ncrypt!SslEncryptPacket +ncrypt!SslEnumCipherSuites +ncrypt!SslEnumProtocolProviders +ncrypt!SslExportKey +ncrypt!SslFreeBuffer +ncrypt!SslFreeObject +ncrypt!SslGenerateMasterKey +ncrypt!SslGeneratePreMasterKey +ncrypt!SslGenerateSessionKeys +ncrypt!SslGetCipherSuitePRFHashAlgorithm +ncrypt!SslGetKeyProperty +ncrypt!SslGetProviderProperty +ncrypt!SslHashHandshake +ncrypt!SslImportKey +ncrypt!SslImportMasterKey +ncrypt!SslIncrementProviderReferenceCount +ncrypt!SslLookupCipherLengths +ncrypt!SslLookupCipherSuiteInfo +ncrypt!SslOpenPrivateKey +ncrypt!SslOpenProvider +ncrypt!SslSignHash +ncrypt!SslVerifySignature +ntdll!RtlCopyContext +ntdll!RtlCopyExtendedContext +ntdll!RtlGetEnabledExtendedFeatures +ntdll!RtlGetExtendedContextLength +ntdll!RtlGetExtendedFeaturesMask +ntdll!RtlInitializeExtendedContext +ntdll!RtlLocateExtendedFeature +ntdll!RtlLocateLegacyContext +ntdll!RtlSetExtendedFeaturesMask +ole32!CLSIDFromProgID +ole32!CLSIDFromProgIDEx +ole32!CLSIDFromString +ole32!CoAddRefServerProcess +ole32!CoCancelCall +ole32!CoCopyProxy +ole32!CoCreateFreeThreadedMarshaler +ole32!CoCreateGuid +ole32!CoCreateInstance +ole32!CoCreateInstanceEx +ole32!CoDisableCallCancellation +ole32!CoDisconnectContext +ole32!CoDisconnectObject +ole32!CoEnableCallCancellation +ole32!CoFileTimeNow +ole32!CoFreeUnusedLibraries +ole32!CoFreeUnusedLibrariesEx +ole32!CoGetApartmentType +ole32!CoGetCallContext +ole32!CoGetCallerTID +ole32!CoGetCancelObject +ole32!CoGetClassObject +ole32!CoGetContextToken +ole32!CoGetCurrentLogicalThreadId +ole32!CoGetCurrentProcess +ole32!CoGetDefaultContext +ole32!CoGetInterfaceAndReleaseStream +ole32!CoGetMalloc +ole32!CoGetMarshalSizeMax +ole32!CoGetObjectContext +ole32!CoGetPSClsid +ole32!CoGetStandardMarshal +ole32!CoGetStdMarshalEx +ole32!CoGetTreatAsClass +ole32!CoImpersonateClient +ole32!CoInitializeEx +ole32!CoInitializeSecurity +ole32!CoInvalidateRemoteMachineBindings +ole32!CoIsHandlerConnected +ole32!CoLockObjectExternal +ole32!CoMarshalHresult +ole32!CoMarshalInterface +ole32!CoMarshalInterThreadInterfaceInStream +ole32!CoQueryAuthenticationServices +ole32!CoQueryClientBlanket +ole32!CoQueryProxyBlanket +ole32!CoRegisterActivationFilter +ole32!CoRegisterClassObject +ole32!CoRegisterPSClsid +ole32!CoRegisterSurrogate +ole32!CoReleaseMarshalData +ole32!CoReleaseServerProcess +ole32!CoResumeClassObjects +ole32!CoRevertToSelf +ole32!CoRevokeClassObject +ole32!CoSetCancelObject +ole32!CoSetProxyBlanket +ole32!CoSuspendClassObjects +ole32!CoSwitchCallContext +ole32!CoTaskMemAlloc +ole32!CoTaskMemFree +ole32!CoTaskMemRealloc +ole32!CoTestCancel +ole32!CoUninitialize +ole32!CoUnmarshalHresult +ole32!CoUnmarshalInterface +ole32!CoWaitForMultipleHandles +ole32!CreateStreamOnHGlobal +ole32!FreePropVariantArray +ole32!GetHGlobalFromStream +ole32!IIDFromString +ole32!ProgIDFromCLSID +ole32!PropVariantClear +ole32!PropVariantCopy +ole32!StringFromCLSID +ole32!StringFromGUID2 +ole32!StringFromIID +oleaut32!BSTR_UserFree +oleaut32!BSTR_UserMarshal +oleaut32!BSTR_UserSize +oleaut32!BSTR_UserUnmarshal +oleaut32!BstrFromVector +oleaut32!ClearCustData +oleaut32!CreateDispTypeInfo +oleaut32!CreateErrorInfo +oleaut32!CreateStdDispatch +oleaut32!CreateTypeLib +oleaut32!CreateTypeLib2 +oleaut32!DispCallFunc +oleaut32!DispGetIDsOfNames +oleaut32!DispGetParam +oleaut32!DispInvoke +oleaut32!DosDateTimeToVariantTime +oleaut32!GetActiveObject +oleaut32!GetAltMonthNames +oleaut32!GetErrorInfo +oleaut32!GetRecordInfoFromGuids +oleaut32!GetRecordInfoFromTypeInfo +oleaut32!GetVarConversionLocaleSetting +oleaut32!LHashValOfNameSys +oleaut32!LHashValOfNameSysA +oleaut32!LoadRegTypeLib +oleaut32!LoadTypeLib +oleaut32!LoadTypeLibEx +oleaut32!LPSAFEARRAY_Marshal +oleaut32!LPSAFEARRAY_Size +oleaut32!LPSAFEARRAY_Unmarshal +oleaut32!LPSAFEARRAY_UserFree +oleaut32!LPSAFEARRAY_UserMarshal +oleaut32!LPSAFEARRAY_UserSize +oleaut32!LPSAFEARRAY_UserUnmarshal +oleaut32!OaBuildVersion +oleaut32!OACreateTypeLib2 +oleaut32!OaEnablePerUserTLibRegistration +oleaut32!OleCreateFontIndirect +oleaut32!OleCreatePictureIndirect +oleaut32!OleCreatePropertyFrame +oleaut32!OleCreatePropertyFrameIndirect +oleaut32!OleIconToCursor +oleaut32!OleLoadPicture +oleaut32!OleLoadPictureEx +oleaut32!OleLoadPictureFile +oleaut32!OleLoadPictureFileEx +oleaut32!OleLoadPicturePath +oleaut32!OleSavePictureFile +oleaut32!OleTranslateColor +oleaut32!QueryPathOfRegTypeLib +oleaut32!RegisterActiveObject +oleaut32!RegisterTypeLib +oleaut32!RegisterTypeLibForUser +oleaut32!RevokeActiveObject +oleaut32!SafeArrayAccessData +oleaut32!SafeArrayAddRef +oleaut32!SafeArrayAllocData +oleaut32!SafeArrayAllocDescriptor +oleaut32!SafeArrayAllocDescriptorEx +oleaut32!SafeArrayCopy +oleaut32!SafeArrayCopyData +oleaut32!SafeArrayCreate +oleaut32!SafeArrayCreateEx +oleaut32!SafeArrayCreateVector +oleaut32!SafeArrayCreateVectorEx +oleaut32!SafeArrayDestroy +oleaut32!SafeArrayDestroyData +oleaut32!SafeArrayDestroyDescriptor +oleaut32!SafeArrayGetDim +oleaut32!SafeArrayGetElement +oleaut32!SafeArrayGetElemsize +oleaut32!SafeArrayGetIID +oleaut32!SafeArrayGetLBound +oleaut32!SafeArrayGetRecordInfo +oleaut32!SafeArrayGetUBound +oleaut32!SafeArrayGetVartype +oleaut32!SafeArrayLock +oleaut32!SafeArrayPtrOfIndex +oleaut32!SafeArrayPutElement +oleaut32!SafeArrayRedim +oleaut32!SafeArrayReleaseData +oleaut32!SafeArrayReleaseDescriptor +oleaut32!SafeArraySetIID +oleaut32!SafeArraySetRecordInfo +oleaut32!SafeArrayUnaccessData +oleaut32!SafeArrayUnlock +oleaut32!SetErrorInfo +oleaut32!SetOaNoCache +oleaut32!SetVarConversionLocaleSetting +oleaut32!SysAddRefString +oleaut32!SysAllocString +oleaut32!SysAllocStringByteLen +oleaut32!SysAllocStringLen +oleaut32!SysFreeString +oleaut32!SysReAllocString +oleaut32!SysReAllocStringLen +oleaut32!SysReleaseString +oleaut32!SysStringByteLen +oleaut32!SysStringLen +oleaut32!SystemTimeToVariantTime +oleaut32!UnRegisterTypeLib +oleaut32!UnRegisterTypeLibForUser +oleaut32!VarAbs +oleaut32!VarAdd +oleaut32!VarAnd +oleaut32!VarBoolFromCy +oleaut32!VarBoolFromDate +oleaut32!VarBoolFromDec +oleaut32!VarBoolFromDisp +oleaut32!VarBoolFromI1 +oleaut32!VarBoolFromI2 +oleaut32!VarBoolFromI4 +oleaut32!VarBoolFromI8 +oleaut32!VarBoolFromR4 +oleaut32!VarBoolFromR8 +oleaut32!VarBoolFromStr +oleaut32!VarBoolFromUI1 +oleaut32!VarBoolFromUI2 +oleaut32!VarBoolFromUI4 +oleaut32!VarBoolFromUI8 +oleaut32!VarBstrCat +oleaut32!VarBstrCmp +oleaut32!VarBstrFromBool +oleaut32!VarBstrFromCy +oleaut32!VarBstrFromDate +oleaut32!VarBstrFromDec +oleaut32!VarBstrFromDisp +oleaut32!VarBstrFromI1 +oleaut32!VarBstrFromI2 +oleaut32!VarBstrFromI4 +oleaut32!VarBstrFromI8 +oleaut32!VarBstrFromR4 +oleaut32!VarBstrFromR8 +oleaut32!VarBstrFromUI1 +oleaut32!VarBstrFromUI2 +oleaut32!VarBstrFromUI4 +oleaut32!VarBstrFromUI8 +oleaut32!VarCat +oleaut32!VarCmp +oleaut32!VarCyAbs +oleaut32!VarCyAdd +oleaut32!VarCyCmp +oleaut32!VarCyCmpR8 +oleaut32!VarCyFix +oleaut32!VarCyFromBool +oleaut32!VarCyFromDate +oleaut32!VarCyFromDec +oleaut32!VarCyFromDisp +oleaut32!VarCyFromI1 +oleaut32!VarCyFromI2 +oleaut32!VarCyFromI4 +oleaut32!VarCyFromI8 +oleaut32!VarCyFromR4 +oleaut32!VarCyFromR8 +oleaut32!VarCyFromStr +oleaut32!VarCyFromUI1 +oleaut32!VarCyFromUI2 +oleaut32!VarCyFromUI4 +oleaut32!VarCyFromUI8 +oleaut32!VarCyInt +oleaut32!VarCyMul +oleaut32!VarCyMulI4 +oleaut32!VarCyMulI8 +oleaut32!VarCyNeg +oleaut32!VarCyRound +oleaut32!VarCySub +oleaut32!VarDateFromBool +oleaut32!VarDateFromCy +oleaut32!VarDateFromDec +oleaut32!VarDateFromDisp +oleaut32!VarDateFromI1 +oleaut32!VarDateFromI2 +oleaut32!VarDateFromI4 +oleaut32!VarDateFromI8 +oleaut32!VarDateFromR4 +oleaut32!VarDateFromR8 +oleaut32!VarDateFromStr +oleaut32!VarDateFromUdate +oleaut32!VarDateFromUdateEx +oleaut32!VarDateFromUI1 +oleaut32!VarDateFromUI2 +oleaut32!VarDateFromUI4 +oleaut32!VarDateFromUI8 +oleaut32!VarDecAbs +oleaut32!VarDecAdd +oleaut32!VarDecCmp +oleaut32!VarDecCmpR8 +oleaut32!VarDecDiv +oleaut32!VarDecFix +oleaut32!VarDecFromBool +oleaut32!VarDecFromCy +oleaut32!VarDecFromDate +oleaut32!VarDecFromDisp +oleaut32!VarDecFromI1 +oleaut32!VarDecFromI2 +oleaut32!VarDecFromI4 +oleaut32!VarDecFromI8 +oleaut32!VarDecFromR4 +oleaut32!VarDecFromR8 +oleaut32!VarDecFromStr +oleaut32!VarDecFromUI1 +oleaut32!VarDecFromUI2 +oleaut32!VarDecFromUI4 +oleaut32!VarDecFromUI8 +oleaut32!VarDecInt +oleaut32!VarDecMul +oleaut32!VarDecNeg +oleaut32!VarDecRound +oleaut32!VarDecSub +oleaut32!VarDiv +oleaut32!VarEqv +oleaut32!VarFix +oleaut32!VarFormat +oleaut32!VarFormatCurrency +oleaut32!VarFormatDateTime +oleaut32!VarFormatFromTokens +oleaut32!VarFormatNumber +oleaut32!VarFormatPercent +oleaut32!VarI1FromBool +oleaut32!VarI1FromCy +oleaut32!VarI1FromDate +oleaut32!VarI1FromDec +oleaut32!VarI1FromDisp +oleaut32!VarI1FromI2 +oleaut32!VarI1FromI4 +oleaut32!VarI1FromI8 +oleaut32!VarI1FromR4 +oleaut32!VarI1FromR8 +oleaut32!VarI1FromStr +oleaut32!VarI1FromUI1 +oleaut32!VarI1FromUI2 +oleaut32!VarI1FromUI4 +oleaut32!VarI1FromUI8 +oleaut32!VarI2FromBool +oleaut32!VarI2FromCy +oleaut32!VarI2FromDate +oleaut32!VarI2FromDec +oleaut32!VarI2FromDisp +oleaut32!VarI2FromI1 +oleaut32!VarI2FromI4 +oleaut32!VarI2FromI8 +oleaut32!VarI2FromR4 +oleaut32!VarI2FromR8 +oleaut32!VarI2FromStr +oleaut32!VarI2FromUI1 +oleaut32!VarI2FromUI2 +oleaut32!VarI2FromUI4 +oleaut32!VarI2FromUI8 +oleaut32!VarI4FromBool +oleaut32!VarI4FromCy +oleaut32!VarI4FromDate +oleaut32!VarI4FromDec +oleaut32!VarI4FromDisp +oleaut32!VarI4FromI1 +oleaut32!VarI4FromI2 +oleaut32!VarI4FromI8 +oleaut32!VarI4FromR4 +oleaut32!VarI4FromR8 +oleaut32!VarI4FromStr +oleaut32!VarI4FromUI1 +oleaut32!VarI4FromUI2 +oleaut32!VarI4FromUI4 +oleaut32!VarI4FromUI8 +oleaut32!VarI8FromBool +oleaut32!VarI8FromCy +oleaut32!VarI8FromDate +oleaut32!VarI8FromDec +oleaut32!VarI8FromDisp +oleaut32!VarI8FromI1 +oleaut32!VarI8FromI2 +oleaut32!VarI8FromR4 +oleaut32!VarI8FromR8 +oleaut32!VarI8FromStr +oleaut32!VarI8FromUI1 +oleaut32!VarI8FromUI2 +oleaut32!VarI8FromUI4 +oleaut32!VarI8FromUI8 +oleaut32!VARIANT_UserFree +oleaut32!VARIANT_UserMarshal +oleaut32!VARIANT_UserSize +oleaut32!VARIANT_UserUnmarshal +oleaut32!VariantChangeType +oleaut32!VariantChangeTypeEx +oleaut32!VariantClear +oleaut32!VariantCopy +oleaut32!VariantCopyInd +oleaut32!VariantInit +oleaut32!VariantTimeToDosDateTime +oleaut32!VariantTimeToSystemTime +oleaut32!VarIdiv +oleaut32!VarImp +oleaut32!VarInt +oleaut32!VarMod +oleaut32!VarMonthName +oleaut32!VarMul +oleaut32!VarNeg +oleaut32!VarNot +oleaut32!VarNumFromParseNum +oleaut32!VarOr +oleaut32!VarParseNumFromStr +oleaut32!VarPow +oleaut32!VarR4CmpR8 +oleaut32!VarR4FromBool +oleaut32!VarR4FromCy +oleaut32!VarR4FromDate +oleaut32!VarR4FromDec +oleaut32!VarR4FromDisp +oleaut32!VarR4FromI1 +oleaut32!VarR4FromI2 +oleaut32!VarR4FromI4 +oleaut32!VarR4FromI8 +oleaut32!VarR4FromR8 +oleaut32!VarR4FromStr +oleaut32!VarR4FromUI1 +oleaut32!VarR4FromUI2 +oleaut32!VarR4FromUI4 +oleaut32!VarR4FromUI8 +oleaut32!VarR8FromBool +oleaut32!VarR8FromCy +oleaut32!VarR8FromDate +oleaut32!VarR8FromDec +oleaut32!VarR8FromDisp +oleaut32!VarR8FromI1 +oleaut32!VarR8FromI2 +oleaut32!VarR8FromI4 +oleaut32!VarR8FromI8 +oleaut32!VarR8FromR4 +oleaut32!VarR8FromStr +oleaut32!VarR8FromUI1 +oleaut32!VarR8FromUI2 +oleaut32!VarR8FromUI4 +oleaut32!VarR8FromUI8 +oleaut32!VarR8Pow +oleaut32!VarR8Round +oleaut32!VarRound +oleaut32!VarSub +oleaut32!VarTokenizeFormatString +oleaut32!VarUdateFromDate +oleaut32!VarUI1FromBool +oleaut32!VarUI1FromCy +oleaut32!VarUI1FromDate +oleaut32!VarUI1FromDec +oleaut32!VarUI1FromDisp +oleaut32!VarUI1FromI1 +oleaut32!VarUI1FromI2 +oleaut32!VarUI1FromI4 +oleaut32!VarUI1FromI8 +oleaut32!VarUI1FromR4 +oleaut32!VarUI1FromR8 +oleaut32!VarUI1FromStr +oleaut32!VarUI1FromUI2 +oleaut32!VarUI1FromUI4 +oleaut32!VarUI1FromUI8 +oleaut32!VarUI2FromBool +oleaut32!VarUI2FromCy +oleaut32!VarUI2FromDate +oleaut32!VarUI2FromDec +oleaut32!VarUI2FromDisp +oleaut32!VarUI2FromI1 +oleaut32!VarUI2FromI2 +oleaut32!VarUI2FromI4 +oleaut32!VarUI2FromI8 +oleaut32!VarUI2FromR4 +oleaut32!VarUI2FromR8 +oleaut32!VarUI2FromStr +oleaut32!VarUI2FromUI1 +oleaut32!VarUI2FromUI4 +oleaut32!VarUI2FromUI8 +oleaut32!VarUI4FromBool +oleaut32!VarUI4FromCy +oleaut32!VarUI4FromDate +oleaut32!VarUI4FromDec +oleaut32!VarUI4FromDisp +oleaut32!VarUI4FromI1 +oleaut32!VarUI4FromI2 +oleaut32!VarUI4FromI4 +oleaut32!VarUI4FromI8 +oleaut32!VarUI4FromR4 +oleaut32!VarUI4FromR8 +oleaut32!VarUI4FromStr +oleaut32!VarUI4FromUI1 +oleaut32!VarUI4FromUI2 +oleaut32!VarUI4FromUI8 +oleaut32!VarUI8FromBool +oleaut32!VarUI8FromCy +oleaut32!VarUI8FromDate +oleaut32!VarUI8FromDec +oleaut32!VarUI8FromDisp +oleaut32!VarUI8FromI1 +oleaut32!VarUI8FromI2 +oleaut32!VarUI8FromI8 +oleaut32!VarUI8FromR4 +oleaut32!VarUI8FromR8 +oleaut32!VarUI8FromStr +oleaut32!VarUI8FromUI1 +oleaut32!VarUI8FromUI2 +oleaut32!VarUI8FromUI4 +oleaut32!VarWeekdayName +oleaut32!VarXor +oleaut32!VectorFromBstr +secur32!AcceptSecurityContext +secur32!AcquireCredentialsHandleA +secur32!AcquireCredentialsHandleW +secur32!AddCredentialsA +secur32!AddCredentialsW +secur32!AddSecurityPackageA +secur32!AddSecurityPackageW +secur32!ApplyControlToken +secur32!ChangeAccountPasswordA +secur32!ChangeAccountPasswordW +secur32!CompleteAuthToken +secur32!DecryptMessage +secur32!DeleteSecurityContext +secur32!DeleteSecurityPackageA +secur32!DeleteSecurityPackageW +secur32!EncryptMessage +secur32!EnumerateSecurityPackagesA +secur32!EnumerateSecurityPackagesW +secur32!ExportSecurityContext +secur32!FreeContextBuffer +secur32!FreeCredentialsHandle +secur32!GetUserNameExA +secur32!GetUserNameExW +secur32!ImpersonateSecurityContext +secur32!ImportSecurityContextA +secur32!ImportSecurityContextW +secur32!InitializeSecurityContextA +secur32!InitializeSecurityContextW +secur32!InitSecurityInterfaceA +secur32!InitSecurityInterfaceW +secur32!LsaCallAuthenticationPackage +secur32!LsaConnectUntrusted +secur32!LsaDeregisterLogonProcess +secur32!LsaEnumerateLogonSessions +secur32!LsaFreeReturnBuffer +secur32!LsaGetLogonSessionData +secur32!LsaLogonUser +secur32!LsaLookupAuthenticationPackage +secur32!LsaRegisterLogonProcess +secur32!LsaRegisterPolicyChangeNotification +secur32!LsaUnregisterPolicyChangeNotification +secur32!MakeSignature +secur32!QueryContextAttributesA +secur32!QueryContextAttributesW +secur32!QueryCredentialsAttributesA +secur32!QueryCredentialsAttributesW +secur32!QuerySecurityContextToken +secur32!QuerySecurityPackageInfoA +secur32!QuerySecurityPackageInfoW +secur32!RevertSecurityContext +secur32!SaslAcceptSecurityContext +secur32!SaslEnumerateProfilesA +secur32!SaslEnumerateProfilesW +secur32!SaslGetContextOption +secur32!SaslGetProfilePackageA +secur32!SaslGetProfilePackageW +secur32!SaslIdentifyPackageA +secur32!SaslIdentifyPackageW +secur32!SaslInitializeSecurityContextA +secur32!SaslInitializeSecurityContextW +secur32!SaslSetContextOption +secur32!SealMessage +secur32!SetContextAttributesA +secur32!SetContextAttributesW +secur32!SetCredentialsAttributesA +secur32!SetCredentialsAttributesW +secur32!SspiCompareAuthIdentities +secur32!SspiCopyAuthIdentity +secur32!SspiDecryptAuthIdentity +secur32!SspiEncodeAuthIdentityAsStrings +secur32!SspiEncodeStringsAsAuthIdentity +secur32!SspiEncryptAuthIdentity +secur32!SspiExcludePackage +secur32!SspiFreeAuthIdentity +secur32!SspiGetTargetHostName +secur32!SspiIsAuthIdentityEncrypted +secur32!SspiLocalFree +secur32!SspiMarshalAuthIdentity +secur32!SspiPrepareForCredRead +secur32!SspiPrepareForCredWrite +secur32!SspiUnmarshalAuthIdentity +secur32!SspiValidateAuthIdentity +secur32!SspiZeroAuthIdentity +secur32!UnsealMessage +secur32!VerifySignature +user32!CharLowerBuffW +user32!CharLowerW +user32!CharNextW +user32!CharPrevW +user32!CharUpperBuffW +user32!CharUpperW +user32!IsCharAlphaNumericW +user32!IsCharAlphaW +user32!IsCharLowerW +user32!IsCharUpperW +user32!LoadStringA +user32!LoadStringW +version!GetFileVersionInfoExW +version!GetFileVersionInfoSizeExW +version!GetFileVersionInfoSizeW +version!GetFileVersionInfoW +version!VerFindFileW +version!VerQueryValueW +ws2_32!accept +ws2_32!bind +ws2_32!closesocket +ws2_32!connect +ws2_32!freeaddrinfo +ws2_32!FreeAddrInfoEx +ws2_32!FreeAddrInfoExW +ws2_32!FreeAddrInfoW +ws2_32!getaddrinfo +ws2_32!GetAddrInfoExA +ws2_32!GetAddrInfoExW +ws2_32!GetAddrInfoW +ws2_32!gethostbyaddr +ws2_32!gethostbyname +ws2_32!gethostname +ws2_32!getnameinfo +ws2_32!GetNameInfoW +ws2_32!getpeername +ws2_32!getprotobyname +ws2_32!getprotobynumber +ws2_32!getservbyname +ws2_32!getservbyport +ws2_32!getsockname +ws2_32!getsockopt +ws2_32!htonl +ws2_32!htons +ws2_32!inet_addr +ws2_32!inet_ntoa +ws2_32!inet_ntop +ws2_32!inet_pton +ws2_32!InetNtopW +ws2_32!InetPtonW +ws2_32!ioctlsocket +ws2_32!listen +ws2_32!ntohl +ws2_32!ntohs +ws2_32!recv +ws2_32!recvfrom +ws2_32!select +ws2_32!send +ws2_32!sendto +ws2_32!SetAddrInfoExA +ws2_32!SetAddrInfoExW +ws2_32!setsockopt +ws2_32!shutdown +ws2_32!socket +ws2_32!WPUCompleteOverlappedRequest +ws2_32!WSAAccept +ws2_32!WSAAddressToStringA +ws2_32!WSAAddressToStringW +ws2_32!WSAAdvertiseProvider +ws2_32!WSAAsyncGetHostByAddr +ws2_32!WSAAsyncGetHostByName +ws2_32!WSAAsyncGetProtoByName +ws2_32!WSAAsyncGetProtoByNumber +ws2_32!WSAAsyncGetServByName +ws2_32!WSAAsyncGetServByPort +ws2_32!WSAAsyncSelect +ws2_32!WSACancelAsyncRequest +ws2_32!WSACancelBlockingCall +ws2_32!WSACleanup +ws2_32!WSACloseEvent +ws2_32!WSAConnect +ws2_32!WSAConnectByList +ws2_32!WSAConnectByNameA +ws2_32!WSAConnectByNameW +ws2_32!WSACreateEvent +ws2_32!WSADuplicateSocketA +ws2_32!WSADuplicateSocketW +ws2_32!WSAEnumNameSpaceProvidersA +ws2_32!WSAEnumNameSpaceProvidersExA +ws2_32!WSAEnumNameSpaceProvidersExW +ws2_32!WSAEnumNameSpaceProvidersW +ws2_32!WSAEnumNetworkEvents +ws2_32!WSAEnumProtocolsA +ws2_32!WSAEnumProtocolsW +ws2_32!WSAEventSelect +ws2_32!WSAGetLastError +ws2_32!WSAGetOverlappedResult +ws2_32!WSAGetQOSByName +ws2_32!WSAGetServiceClassInfoA +ws2_32!WSAGetServiceClassInfoW +ws2_32!WSAGetServiceClassNameByClassIdA +ws2_32!WSAGetServiceClassNameByClassIdW +ws2_32!WSAHtonl +ws2_32!WSAHtons +ws2_32!WSAInstallServiceClassA +ws2_32!WSAInstallServiceClassW +ws2_32!WSAIoctl +ws2_32!WSAIsBlocking +ws2_32!WSAJoinLeaf +ws2_32!WSALookupServiceBeginA +ws2_32!WSALookupServiceBeginW +ws2_32!WSALookupServiceEnd +ws2_32!WSALookupServiceNextA +ws2_32!WSALookupServiceNextW +ws2_32!WSANSPIoctl +ws2_32!WSANtohl +ws2_32!WSANtohs +ws2_32!WSAPoll +ws2_32!WSAProviderCompleteAsyncCall +ws2_32!WSAProviderConfigChange +ws2_32!WSARecv +ws2_32!WSARecvDisconnect +ws2_32!WSARecvFrom +ws2_32!WSARemoveServiceClass +ws2_32!WSAResetEvent +ws2_32!WSASend +ws2_32!WSASendDisconnect +ws2_32!WSASendMsg +ws2_32!WSASendTo +ws2_32!WSASetBlockingHook +ws2_32!WSASetEvent +ws2_32!WSASetLastError +ws2_32!WSASetServiceA +ws2_32!WSASetServiceW +ws2_32!WSASocketA +ws2_32!WSASocketW +ws2_32!WSAStartup +ws2_32!WSAStringToAddressA +ws2_32!WSAStringToAddressW +ws2_32!WSAUnadvertiseProvider +ws2_32!WSAUnhookBlockingHook +ws2_32!WSAWaitForMultipleEvents +ws2_32!WSCDeinstallProvider +ws2_32!WSCEnableNSProvider +ws2_32!WSCEnumProtocols +ws2_32!WSCGetApplicationCategory +ws2_32!WSCGetProviderInfo +ws2_32!WSCGetProviderPath +ws2_32!WSCInstallNameSpace +ws2_32!WSCInstallNameSpaceEx +ws2_32!WSCInstallProvider +ws2_32!WSCSetApplicationCategory +ws2_32!WSCSetProviderInfo +ws2_32!WSCUnInstallNameSpace +ws2_32!WSCUpdateProvider +ws2_32!WSCWriteNameSpaceOrder +ws2_32!WSCWriteProviderOrder diff --git a/src/coreclr/nativeaot/BuildIntegration/findvcvarsall.bat b/src/coreclr/nativeaot/BuildIntegration/findvcvarsall.bat new file mode 100644 index 00000000000000..efee6316785f66 --- /dev/null +++ b/src/coreclr/nativeaot/BuildIntegration/findvcvarsall.bat @@ -0,0 +1,55 @@ +@ECHO OFF +SETLOCAL + +IF "%~1"=="" ( + ECHO Usage: %~nx0 ^ + GOTO :ERROR +) + +SET vswherePath=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe +IF NOT EXIST "%vswherePath%" GOTO :ERROR + +SET toolsSuffix=x86.x64 +IF /I "%~1"=="arm64" SET toolsSuffix=ARM64 + +FOR /F "tokens=*" %%i IN ( + '"%vswherePath%" -latest -prerelease -products * ^ + -requires Microsoft.VisualStudio.Component.VC.Tools.%toolsSuffix% ^ + -version [16^,18^) ^ + -property installationPath' +) DO SET vsBase=%%i + +IF "%vsBase%"=="" GOTO :ERROR + +SET procArch=%PROCESSOR_ARCHITEW6432% +IF "%procArch%"=="" SET procArch=%PROCESSOR_ARCHITECTURE% + +SET vcEnvironment=%~1 +IF /I "%~1"=="x64" ( + SET vcEnvironment=x86_amd64 + IF /I "%procArch%"=="AMD64" SET vcEnvironment=amd64 +) +IF /I "%~1"=="arm64" ( + SET vcEnvironment=x86_arm64 + IF /I "%procArch%"=="AMD64" SET vcEnvironment=amd64_arm64 +) + +CALL "%vsBase%\vc\Auxiliary\Build\vcvarsall.bat" %vcEnvironment% > NUL + +FOR /F "delims=" %%W IN ('where link') DO ( + FOR %%A IN ("%%W") DO ECHO %%~dpA# + GOTO :CAPTURE_LIB_PATHS +) + +GOTO :ERROR + +:CAPTURE_LIB_PATHS +IF "%LIB%"=="" GOTO :ERROR +ECHO %LIB% + +ENDLOCAL + +EXIT /B 0 + +:ERROR +EXIT /B 1 diff --git a/src/coreclr/nativeaot/CMakeLists.txt b/src/coreclr/nativeaot/CMakeLists.txt new file mode 100644 index 00000000000000..05b92b3534fbbb --- /dev/null +++ b/src/coreclr/nativeaot/CMakeLists.txt @@ -0,0 +1,50 @@ +if(WIN32) + add_definitions(-DUNICODE=1) +endif (WIN32) + +if(MSVC) + add_compile_options($<$:/EHa->) # Native AOT runtime does not use C++ exception handling + add_compile_options($<$:/EHs->) + + # CFG runtime checks in C++ code are unnecessary overhead unless Native AOT compiler produces CFG compliant code as well + # and CFG is enabled in the linker + if(CMAKE_CXX_FLAGS MATCHES "/guard:cf") + string(REPLACE "/guard:cf" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + endif() + if(CMAKE_C_FLAGS MATCHES "/guard:cf") + string(REPLACE "/guard:cf" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + endif() + + # The code generated by the Native AOT compiler doesn't work with Link Time Code Generation + add_compile_options($<$:/GL->) + + # Sets the options that create the fastest code in the majority of cases + add_compile_options($<$,$>:/O2>) +endif (MSVC) + +if(CLR_CMAKE_HOST_UNIX) + add_compile_options(-fno-rtti) # Native AOT runtime doesn't use RTTI + add_compile_options(-fno-exceptions) # Native AOT runtime doesn't use C++ exception handling + + if(CLR_CMAKE_TARGET_OSX) + add_definitions(-D_XOPEN_SOURCE) + endif(CLR_CMAKE_TARGET_OSX) + + if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386) + # Allow 16 byte compare-exchange + add_compile_options(-mcx16) + endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386) +endif (CLR_CMAKE_HOST_UNIX) + +if(CLR_CMAKE_HOST_ALPINE_LINUX) + # Fix up the main thread stack size for MUSL to more reasonable size. + # TODO: https://github.com/dotnet/runtimelab/issues/791 + add_definitions(-DENSURE_PRIMARY_STACK_SIZE) +endif() + +if(CLR_CMAKE_TARGET_ANDROID) + add_definitions(-DFEATURE_EMULATED_TLS) +endif() + +add_subdirectory(Bootstrap) +add_subdirectory(Runtime) diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs new file mode 100644 index 00000000000000..6d2506c01d7e06 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs @@ -0,0 +1,268 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Internal.Runtime.CompilerServices; + +using Debug = Internal.Runtime.CompilerHelpers.StartupDebug; + +namespace Internal.Runtime.CompilerHelpers +{ + public static partial class StartupCodeHelpers + { + /// + /// Initial module array allocation used when adding modules dynamically. + /// + private const int InitialModuleCount = 8; + + /// + /// Table of logical modules. Only the first s_moduleCount elements of the array are in use. + /// + private static TypeManagerHandle[] s_modules; + + /// + /// Number of valid elements in the logical module table. + /// + private static int s_moduleCount; + + /// + /// GC handle of an array with s_moduleCount elements, each representing and array of GC static bases of the types in the module. + /// + private static IntPtr s_moduleGCStaticsSpines; + + [UnmanagedCallersOnly(EntryPoint = "InitializeModules", CallConvs = new Type[] { typeof(CallConvCdecl) })] + internal static unsafe void InitializeModules(IntPtr osModule, IntPtr* pModuleHeaders, int count, IntPtr* pClasslibFunctions, int nClasslibFunctions) + { + RuntimeImports.RhpRegisterOsModule(osModule); + TypeManagerHandle[] modules = CreateTypeManagers(osModule, pModuleHeaders, count, pClasslibFunctions, nClasslibFunctions); + object[] gcStaticBaseSpines = new object[count]; + + for (int i = 0; i < modules.Length; i++) + { + InitializeGlobalTablesForModule(modules[i], i, gcStaticBaseSpines); + } + + s_moduleGCStaticsSpines = RuntimeImports.RhHandleAlloc(gcStaticBaseSpines, GCHandleType.Normal); + + // We are now at a stage where we can use GC statics - publish the list of modules + // so that the eager constructors can access it. + if (s_modules != null) + { + for (int i = 0; i < modules.Length; i++) + { + AddModule(modules[i]); + } + } + else + { + s_modules = modules; + s_moduleCount = modules.Length; + } + + // These two loops look funny but it's important to initialize the global tables before running + // the first class constructor to prevent them calling into another uninitialized module + for (int i = 0; i < modules.Length; i++) + { + RunInitializers(modules[i], ReadyToRunSectionType.EagerCctor); + } + } + + /// + /// Return the number of registered logical modules; optionally copy them into an array. + /// + /// Array to copy logical modules to, null = only return logical module count + internal static int GetLoadedModules(TypeManagerHandle[] outputModules) + { + if (outputModules != null) + { + int copyLimit = (s_moduleCount < outputModules.Length ? s_moduleCount : outputModules.Length); + for (int copyIndex = 0; copyIndex < copyLimit; copyIndex++) + { + outputModules[copyIndex] = s_modules[copyIndex]; + } + } + return s_moduleCount; + } + + private static void AddModule(TypeManagerHandle newModuleHandle) + { + if (s_modules == null || s_moduleCount >= s_modules.Length) + { + // Reallocate logical module array + int newModuleLength = 2 * s_moduleCount; + if (newModuleLength < InitialModuleCount) + { + newModuleLength = InitialModuleCount; + } + + TypeManagerHandle[] newModules = new TypeManagerHandle[newModuleLength]; + for (int copyIndex = 0; copyIndex < s_moduleCount; copyIndex++) + { + newModules[copyIndex] = s_modules[copyIndex]; + } + s_modules = newModules; + } + + s_modules[s_moduleCount] = newModuleHandle; + s_moduleCount++; + } + + private static unsafe TypeManagerHandle[] CreateTypeManagers(IntPtr osModule, IntPtr* pModuleHeaders, int count, IntPtr* pClasslibFunctions, int nClasslibFunctions) + { + // Count the number of modules so we can allocate an array to hold the TypeManager objects. + // At this stage of startup, complex collection classes will not work. + int moduleCount = 0; + for (int i = 0; i < count; i++) + { + // The null pointers are sentinel values and padding inserted as side-effect of + // the section merging. (The global static constructors section used by C++ has + // them too.) + if (pModuleHeaders[i] != IntPtr.Zero) + moduleCount++; + } + + TypeManagerHandle[] modules = new TypeManagerHandle[moduleCount]; + int moduleIndex = 0; + for (int i = 0; i < count; i++) + { + if (pModuleHeaders[i] != IntPtr.Zero) + { + modules[moduleIndex] = RuntimeImports.RhpCreateTypeManager(osModule, pModuleHeaders[i], pClasslibFunctions, nClasslibFunctions); + moduleIndex++; + } + } + + return modules; + } + + /// + /// Each managed module linked into the final binary may have its own global tables for strings, + /// statics, etc that need initializing. InitializeGlobalTables walks through the modules + /// and offers each a chance to initialize its global tables. + /// + private static unsafe void InitializeGlobalTablesForModule(TypeManagerHandle typeManager, int moduleIndex, object[] gcStaticBaseSpines) + { + // Configure the module indirection cell with the newly created TypeManager. This allows EETypes to find + // their interface dispatch map tables. + int length; + TypeManagerSlot* section = (TypeManagerSlot*)RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.TypeManagerIndirection, out length); + section->TypeManager = typeManager; + section->ModuleIndex = moduleIndex; + + // Initialize statics if any are present + IntPtr staticsSection = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.GCStaticRegion, out length); + if (staticsSection != IntPtr.Zero) + { + Debug.Assert(length % IntPtr.Size == 0); + + object[] spine = InitializeStatics(staticsSection, length); + + // Call write barrier directly. Assigning object reference does a type check. + Debug.Assert((uint)moduleIndex < (uint)gcStaticBaseSpines.Length); + ref object rawSpineIndexData = ref Unsafe.As(ref Unsafe.As(gcStaticBaseSpines).Data); + InternalCalls.RhpAssignRef(ref Unsafe.Add(ref rawSpineIndexData, moduleIndex), spine); + } + + // Initialize frozen object segment for the module with GC present + IntPtr frozenObjectSection = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.FrozenObjectRegion, out length); + if (frozenObjectSection != IntPtr.Zero) + { + Debug.Assert(length % IntPtr.Size == 0); + InitializeModuleFrozenObjectSegment(frozenObjectSection, length); + } + } + + private static unsafe void InitializeModuleFrozenObjectSegment(IntPtr segmentStart, int length) + { + if (RuntimeImports.RhpRegisterFrozenSegment(segmentStart, (IntPtr)length) == IntPtr.Zero) + { + // This should only happen if we ran out of memory. + RuntimeExceptionHelpers.FailFast("Failed to register frozen object segment for the module."); + } + } + + internal static void RunModuleInitializers() + { + for (int i = 0; i < s_moduleCount; i++) + { + RunInitializers(s_modules[i], ReadyToRunSectionType.ModuleInitializerList); + } + } + + private static unsafe void RunInitializers(TypeManagerHandle typeManager, ReadyToRunSectionType section) + { + var initializers = (delegate**)RuntimeImports.RhGetModuleSection(typeManager, section, out int length); + Debug.Assert(length % IntPtr.Size == 0); + int count = length / IntPtr.Size; + for (int i = 0; i < count; i++) + { + initializers[i](); + } + } + + private static unsafe object[] InitializeStatics(IntPtr gcStaticRegionStart, int length) + { + IntPtr gcStaticRegionEnd = (IntPtr)((byte*)gcStaticRegionStart + length); + + object[] spine = new object[length / IntPtr.Size]; + + ref object rawSpineData = ref Unsafe.As(ref Unsafe.As(spine).Data); + + int currentBase = 0; + for (IntPtr* block = (IntPtr*)gcStaticRegionStart; block < (IntPtr*)gcStaticRegionEnd; block++) + { + // Gc Static regions can be shared by modules linked together during compilation. To ensure each + // is initialized once, the static region pointer is stored with lowest bit set in the image. + // The first time we initialize the static region its pointer is replaced with an object reference + // whose lowest bit is no longer set. + IntPtr* pBlock = (IntPtr*)*block; + nint blockAddr = *pBlock; + if ((blockAddr & GCStaticRegionConstants.Uninitialized) == GCStaticRegionConstants.Uninitialized) + { + object? obj = null; + RuntimeImports.RhAllocateNewObject( + new IntPtr(blockAddr & ~GCStaticRegionConstants.Mask), + (uint)GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP, + Unsafe.AsPointer(ref obj)); + if (obj == null) + { + RuntimeExceptionHelpers.FailFast("Failed allocating GC static bases"); + } + + + if ((blockAddr & GCStaticRegionConstants.HasPreInitializedData) == GCStaticRegionConstants.HasPreInitializedData) + { + // The next pointer is preinitialized data blob that contains preinitialized static GC fields, + // which are pointer relocs to GC objects in frozen segment. + // It actually has all GC fields including non-preinitialized fields and we simply copy over the + // entire blob to this object, overwriting everything. + IntPtr pPreInitDataAddr = *(pBlock + 1); + RuntimeImports.RhBulkMoveWithWriteBarrier(ref obj.GetRawData(), ref *(byte *)pPreInitDataAddr, obj.GetRawDataSize()); + } + + // Call write barrier directly. Assigning object reference does a type check. + Debug.Assert(currentBase < spine.Length); + InternalCalls.RhpAssignRef(ref Unsafe.Add(ref rawSpineData, currentBase), obj); + + // Update the base pointer to point to the pinned object + *pBlock = *(IntPtr*)Unsafe.AsPointer(ref obj); + } + + currentBase++; + } + + return spine; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct TypeManagerSlot + { + public TypeManagerHandle TypeManager; + public int ModuleIndex; + } +} diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupDebug.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupDebug.cs new file mode 100644 index 00000000000000..b69af16e839717 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupDebug.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// replacement for the startup path. + /// It's not safe to use the full-blown Debug class during startup because big chunks + /// of the managed execution environment are not initialized yet. + /// + internal static class StartupDebug + { + [Conditional("DEBUG")] + public static void Assert(bool condition) + { + if (!condition) + unsafe { *(int*)0 = 0; } + } + } +} diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/LowLevelStringConverter.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/LowLevelStringConverter.cs new file mode 100644 index 00000000000000..99487b5c8e36f0 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/LowLevelStringConverter.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; + +namespace Internal.Runtime +{ + /// + /// Extension methods that provide low level ToString() equivalents for some of the core types. + /// Calling regular ToString() on these types goes through a lot of the CultureInfo machinery + /// which is not low level enough to be used everywhere. + /// + internal static class LowLevelStringConverter + { + private const string HexDigits = "0123456789ABCDEF"; + + // TODO: Rename to ToHexString() + public static string LowLevelToString(this int arg) + { + return ((uint)arg).LowLevelToString(); + } + + // TODO: Rename to ToHexString() + public static string LowLevelToString(this uint arg) + { + StringBuilder sb = new StringBuilder(8); + int shift = 4 * 8; + while (shift > 0) + { + shift -= 4; + int digit = (int)((arg >> shift) & 0xF); + sb.Append(HexDigits[digit]); + } + + return sb.ToString(); + } + + public static string LowLevelToString(this IntPtr arg) + { + StringBuilder sb = new StringBuilder(IntPtr.Size * 4); + ulong num = (ulong)arg; + + int shift = IntPtr.Size * 8; + while (shift > 0) + { + shift -= 4; + int digit = (int)((num >> shift) & 0xF); + sb.Append(HexDigits[digit]); + } + + return sb.ToString(); + } + } +} diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs new file mode 100644 index 00000000000000..007e940bec2795 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs @@ -0,0 +1,1662 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Internal.NativeFormat; +using Internal.Runtime.CompilerServices; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime +{ + [StructLayout(LayoutKind.Sequential)] + internal struct ObjHeader + { + // Contents of the object header + private IntPtr _objHeaderContents; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct EEInterfaceInfo + { + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InterfaceTypeUnion + { + [FieldOffset(0)] + public MethodTable* _pInterfaceEEType; + [FieldOffset(0)] + public MethodTable** _ppInterfaceEETypeViaIAT; + } + + private InterfaceTypeUnion _interfaceType; + + internal MethodTable* InterfaceType + { + get + { + if ((unchecked((uint)_interfaceType._pInterfaceEEType) & IndirectionConstants.IndirectionCellPointer) != 0) + { +#if TARGET_64BIT + MethodTable** ppInterfaceEETypeViaIAT = (MethodTable**)(((ulong)_interfaceType._ppInterfaceEETypeViaIAT) - IndirectionConstants.IndirectionCellPointer); +#else + MethodTable** ppInterfaceEETypeViaIAT = (MethodTable**)(((uint)_interfaceType._ppInterfaceEETypeViaIAT) - IndirectionConstants.IndirectionCellPointer); +#endif + return *ppInterfaceEETypeViaIAT; + } + + return _interfaceType._pInterfaceEEType; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _interfaceType._pInterfaceEEType = value; + } +#endif + } + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct DispatchMap + { + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct DispatchMapEntry + { + internal ushort _usInterfaceIndex; + internal ushort _usInterfaceMethodSlot; + internal ushort _usImplMethodSlot; + } + + private ushort _standardEntryCount; // Implementations on the class + private ushort _defaultEntryCount; // Default implementations + private DispatchMapEntry _dispatchMap; // at least one entry if any interfaces defined + + public uint NumStandardEntries + { + get + { + return _standardEntryCount; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _standardEntryCount = checked((ushort)value); + } +#endif + } + + public uint NumDefaultEntries + { + get + { + return _defaultEntryCount; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _defaultEntryCount = checked((ushort)value); + } +#endif + } + + public int Size + { + get + { + return sizeof(ushort) + sizeof(ushort) + sizeof(DispatchMapEntry) * ((int)_standardEntryCount + (int)_defaultEntryCount); + } + } + + public DispatchMapEntry* this[int index] + { + get + { + return (DispatchMapEntry*)Unsafe.AsPointer(ref Unsafe.Add(ref _dispatchMap, index)); + } + } + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe partial struct MethodTable + { +#if TARGET_64BIT + private const int POINTER_SIZE = 8; + private const int PADDING = 1; // _numComponents is padded by one Int32 to make the first element pointer-aligned +#else + private const int POINTER_SIZE = 4; + private const int PADDING = 0; +#endif + internal const int SZARRAY_BASE_SIZE = POINTER_SIZE + POINTER_SIZE + (1 + PADDING) * 4; + + [StructLayout(LayoutKind.Explicit)] + private unsafe struct RelatedTypeUnion + { + // Kinds.CanonicalEEType + [FieldOffset(0)] + public MethodTable* _pBaseType; + [FieldOffset(0)] + public MethodTable** _ppBaseTypeViaIAT; + + // Kinds.ClonedEEType + [FieldOffset(0)] + public MethodTable* _pCanonicalType; + [FieldOffset(0)] + public MethodTable** _ppCanonicalTypeViaIAT; + + // Kinds.ArrayEEType + [FieldOffset(0)] + public MethodTable* _pRelatedParameterType; + [FieldOffset(0)] + public MethodTable** _ppRelatedParameterTypeViaIAT; + } + + private static unsafe class OptionalFieldsReader + { + internal static uint GetInlineField(byte* pFields, EETypeOptionalFieldTag eTag, uint uiDefaultValue) + { + if (pFields == null) + return uiDefaultValue; + + bool isLastField = false; + while (!isLastField) + { + byte fieldHeader = NativePrimitiveDecoder.ReadUInt8(ref pFields); + isLastField = (fieldHeader & 0x80) != 0; + EETypeOptionalFieldTag eCurrentTag = (EETypeOptionalFieldTag)(fieldHeader & 0x7f); + uint uiCurrentValue = NativePrimitiveDecoder.DecodeUnsigned(ref pFields); + + // If we found a tag match return the current value. + if (eCurrentTag == eTag) + return uiCurrentValue; + } + + // Reached end of stream without getting a match. Field is not present so return default value. + return uiDefaultValue; + } + } + + /// + /// Gets a value indicating whether the statically generated data structures use relative pointers. + /// + internal static bool SupportsRelativePointers + { + [Intrinsic] + get + { + throw new NotImplementedException(); + } + } + + /// + /// Gets a value indicating whether writable data is supported. + /// + internal static bool SupportsWritableData + { + get + { + // For now just key this off of SupportsRelativePointer to avoid this on both CppCodegen and WASM. + return SupportsRelativePointers; + } + } + + private ushort _usComponentSize; + private ushort _usFlags; + private uint _uBaseSize; + private RelatedTypeUnion _relatedType; + private ushort _usNumVtableSlots; + private ushort _usNumInterfaces; + private uint _uHashCode; + + // vtable follows + + // These masks and paddings have been chosen so that the ValueTypePadding field can always fit in a byte of data. + // if the alignment is 8 bytes or less. If the alignment is higher then there may be a need for more bits to hold + // the rest of the padding data. + // If paddings of greater than 7 bytes are necessary, then the high bits of the field represent that padding + private const uint ValueTypePaddingLowMask = 0x7; + private const uint ValueTypePaddingHighMask = 0xFFFFFF00; + private const uint ValueTypePaddingMax = 0x07FFFFFF; + private const int ValueTypePaddingHighShift = 8; + private const uint ValueTypePaddingAlignmentMask = 0xF8; + private const int ValueTypePaddingAlignmentShift = 3; + + internal ushort ComponentSize + { + get + { + return _usComponentSize; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _usComponentSize = value; + } +#endif + } + + internal ushort GenericArgumentCount + { + get + { + Debug.Assert(IsGenericTypeDefinition); + return _usComponentSize; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert(IsGenericTypeDefinition); + _usComponentSize = value; + } +#endif + } + + internal ushort Flags + { + get + { + return _usFlags; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _usFlags = value; + } +#endif + } + + internal uint BaseSize + { + get + { + return _uBaseSize; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _uBaseSize = value; + } +#endif + } + + internal ushort NumVtableSlots + { + get + { + return _usNumVtableSlots; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _usNumVtableSlots = value; + } +#endif + } + + internal ushort NumInterfaces + { + get + { + return _usNumInterfaces; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _usNumInterfaces = value; + } +#endif + } + + internal uint HashCode + { + get + { + return _uHashCode; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _uHashCode = value; + } +#endif + } + + private EETypeKind Kind + { + get + { + return (EETypeKind)(_usFlags & (ushort)EETypeFlags.EETypeKindMask); + } + } + + internal bool HasOptionalFields + { + get + { + return ((_usFlags & (ushort)EETypeFlags.OptionalFieldsFlag) != 0); + } + } + + // Mark or determine that a type is generic and one or more of it's type parameters is co- or + // contra-variant. This only applies to interface and delegate types. + internal bool HasGenericVariance + { + get + { + return ((_usFlags & (ushort)EETypeFlags.GenericVarianceFlag) != 0); + } + } + + internal bool IsFinalizable + { + get + { + return ((_usFlags & (ushort)EETypeFlags.HasFinalizerFlag) != 0); + } + } + + internal bool IsNullable + { + get + { + return ElementType == EETypeElementType.Nullable; + } + } + + internal bool IsCloned + { + get + { + return Kind == EETypeKind.ClonedEEType; + } + } + + internal bool IsCanonical + { + get + { + return Kind == EETypeKind.CanonicalEEType; + } + } + + internal bool IsString + { + get + { + // String is currently the only non-array type with a non-zero component size. + return ComponentSize == StringComponentSize.Value && !IsArray && !IsGenericTypeDefinition; + } + } + + internal bool IsArray + { + get + { + EETypeElementType elementType = ElementType; + return elementType == EETypeElementType.Array || elementType == EETypeElementType.SzArray; + } + } + + + internal int ArrayRank + { + get + { + Debug.Assert(this.IsArray); + + int boundsSize = (int)this.ParameterizedTypeShape - SZARRAY_BASE_SIZE; + if (boundsSize > 0) + { + // Multidim array case: Base size includes space for two Int32s + // (upper and lower bound) per each dimension of the array. + return boundsSize / (2 * sizeof(int)); + } + return 1; + } + } + + internal bool IsSzArray + { + get + { + return ElementType == EETypeElementType.SzArray; + } + } + + internal bool IsGeneric + { + get + { + return ((_usFlags & (ushort)EETypeFlags.IsGenericFlag) != 0); + } + } + + internal bool IsGenericTypeDefinition + { + get + { + return Kind == EETypeKind.GenericTypeDefEEType; + } + } + + internal MethodTable* GenericDefinition + { + get + { + Debug.Assert(IsGeneric); + if (IsDynamicType || !SupportsRelativePointers) + return GetField>(EETypeField.ETF_GenericDefinition).Value; + + return GetField>(EETypeField.ETF_GenericDefinition).Value; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert(IsGeneric && IsDynamicType); + uint cbOffset = GetFieldOffset(EETypeField.ETF_GenericDefinition); + fixed (MethodTable* pThis = &this) + { + *((MethodTable**)((byte*)pThis + cbOffset)) = value; + } + } +#endif + } + + [StructLayout(LayoutKind.Sequential)] + private readonly struct GenericComposition + { + public readonly ushort Arity; + + private readonly EETypeRef _genericArgument1; + public EETypeRef* GenericArguments + { + get + { + return (EETypeRef*)Unsafe.AsPointer(ref Unsafe.AsRef(in _genericArgument1)); + } + } + + public GenericVariance* GenericVariance + { + get + { + // Generic variance directly follows the last generic argument + return (GenericVariance*)(GenericArguments + Arity); + } + } + } + +#if TYPE_LOADER_IMPLEMENTATION + internal static int GetGenericCompositionSize(int numArguments, bool hasVariance) + { + return IntPtr.Size + + numArguments * IntPtr.Size + + (hasVariance ? numArguments * sizeof(GenericVariance) : 0); + } + + internal void SetGenericComposition(IntPtr data) + { + Debug.Assert(IsGeneric && IsDynamicType); + uint cbOffset = GetFieldOffset(EETypeField.ETF_GenericComposition); + fixed (MethodTable* pThis = &this) + { + *((IntPtr*)((byte*)pThis + cbOffset)) = data; + } + } +#endif + + internal uint GenericArity + { + get + { + Debug.Assert(IsGeneric); + if (IsDynamicType || !SupportsRelativePointers) + return GetField>(EETypeField.ETF_GenericComposition).Value->Arity; + + return GetField>(EETypeField.ETF_GenericComposition).Value->Arity; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert(IsDynamicType); + // GenericComposition is a readonly struct, so we just blit the bytes over. Asserts guard changes to the layout. + *((ushort*)GetField>(EETypeField.ETF_GenericComposition).Value) = checked((ushort)value); + Debug.Assert(GenericArity == (ushort)value); + } +#endif + } + + internal EETypeRef* GenericArguments + { + get + { + Debug.Assert(IsGeneric); + if (IsDynamicType || !SupportsRelativePointers) + return GetField>(EETypeField.ETF_GenericComposition).Value->GenericArguments; + + return GetField>(EETypeField.ETF_GenericComposition).Value->GenericArguments; + } + } + + internal GenericVariance* GenericVariance + { + get + { + Debug.Assert(IsGeneric); + + if (!HasGenericVariance) + return null; + + if (IsDynamicType || !SupportsRelativePointers) + return GetField>(EETypeField.ETF_GenericComposition).Value->GenericVariance; + + return GetField>(EETypeField.ETF_GenericComposition).Value->GenericVariance; + } + } + + internal bool IsPointerType + { + get + { + return ElementType == EETypeElementType.Pointer; + } + } + + internal bool IsByRefType + { + get + { + return ElementType == EETypeElementType.ByRef; + } + } + + internal bool IsInterface + { + get + { + return ElementType == EETypeElementType.Interface; + } + } + + internal bool IsAbstract + { + get + { + return IsInterface || (RareFlags & EETypeRareFlags.IsAbstractClassFlag) != 0; + } + } + + internal bool IsByRefLike + { + get + { + return (RareFlags & EETypeRareFlags.IsByRefLikeFlag) != 0; + } + } + + internal bool IsDynamicType + { + get + { + return (_usFlags & (ushort)EETypeFlags.IsDynamicTypeFlag) != 0; + } + } + + internal bool HasDynamicallyAllocatedDispatchMap + { + get + { + return (RareFlags & EETypeRareFlags.HasDynamicallyAllocatedDispatchMapFlag) != 0; + } + } + + internal bool IsParameterizedType + { + get + { + return Kind == EETypeKind.ParameterizedEEType; + } + } + + // The parameterized type shape defines the particular form of parameterized type that + // is being represented. + // Currently, the meaning is a shape of 0 indicates that this is a Pointer, + // shape of 1 indicates a ByRef, and >=SZARRAY_BASE_SIZE indicates that this is an array. + // Two types are not equivalent if their shapes do not exactly match. + internal uint ParameterizedTypeShape + { + get + { + return _uBaseSize; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _uBaseSize = value; + } +#endif + } + + internal bool IsRelatedTypeViaIAT + { + get + { + return ((_usFlags & (ushort)EETypeFlags.RelatedTypeViaIATFlag) != 0); + } + } + + internal bool RequiresAlign8 + { + get + { + return (RareFlags & EETypeRareFlags.RequiresAlign8Flag) != 0; + } + } + + internal bool IsIDynamicInterfaceCastable + { + get + { + return ((_usFlags & (ushort)EETypeFlags.IDynamicInterfaceCastableFlag) != 0); + } + } + + internal bool IsValueType + { + get + { + return ElementType < EETypeElementType.Class; + } + } + + // Warning! UNLIKE the similarly named Reflection api, this method also returns "true" for Enums. + internal bool IsPrimitive + { + get + { + return ElementType < EETypeElementType.ValueType; + } + } + + internal bool HasGCPointers + { + get + { + return ((_usFlags & (ushort)EETypeFlags.HasPointersFlag) != 0); + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + if (value) + { + _usFlags |= (ushort)EETypeFlags.HasPointersFlag; + } + else + { + _usFlags &= (ushort)~EETypeFlags.HasPointersFlag; + } + } +#endif + } + + internal bool IsHFA + { + get + { + return (RareFlags & EETypeRareFlags.IsHFAFlag) != 0; + } + } + + internal uint ValueTypeFieldPadding + { + get + { + byte* optionalFields = OptionalFieldsPtr; + + // If there are no optional fields then the padding must have been the default, 0. + if (optionalFields == null) + return 0; + + // Get the value from the optional fields. The default is zero if that particular field was not included. + // The low bits of this field is the ValueType field padding, the rest of the byte is the alignment if present + uint ValueTypeFieldPaddingData = OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.ValueTypeFieldPadding, 0); + uint padding = ValueTypeFieldPaddingData & ValueTypePaddingLowMask; + // If there is additional padding, the other bits have that data + padding |= (ValueTypeFieldPaddingData & ValueTypePaddingHighMask) >> (ValueTypePaddingHighShift - ValueTypePaddingAlignmentShift); + return padding; + } + } + + internal uint ValueTypeSize + { + get + { + Debug.Assert(IsValueType); + // get_BaseSize returns the GC size including space for the sync block index field, the MethodTable* and + // padding for GC heap alignment. Must subtract all of these to get the size used for locals, array + // elements or fields of another type. + return BaseSize - ((uint)sizeof(ObjHeader) + (uint)sizeof(MethodTable*) + ValueTypeFieldPadding); + } + } + + internal uint FieldByteCountNonGCAligned + { + get + { + // This api is designed to return correct results for EETypes which can be derived from + // And results indistinguishable from correct for DefTypes which cannot be derived from (sealed classes) + // (For sealed classes, this should always return BaseSize-((uint)sizeof(ObjHeader)); + Debug.Assert(!IsInterface && !IsParameterizedType); + + // get_BaseSize returns the GC size including space for the sync block index field, the MethodTable* and + // padding for GC heap alignment. Must subtract all of these to get the size used for the fields of + // the type (where the fields of the type includes the MethodTable*) + return BaseSize - ((uint)sizeof(ObjHeader) + ValueTypeFieldPadding); + } + } + + internal EEInterfaceInfo* InterfaceMap + { + get + { + fixed (MethodTable* start = &this) + { + // interface info table starts after the vtable and has _usNumInterfaces entries + return (EEInterfaceInfo*)((byte*)start + sizeof(MethodTable) + sizeof(void*) * _usNumVtableSlots); + } + } + } + + internal bool HasDispatchMap + { + get + { + if (NumInterfaces == 0) + return false; + byte* optionalFields = OptionalFieldsPtr; + if (optionalFields == null) + return false; + uint idxDispatchMap = OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.DispatchMap, 0xffffffff); + if (idxDispatchMap == 0xffffffff) + { + if (HasDynamicallyAllocatedDispatchMap) + return true; + else if (IsDynamicType) + return DynamicTemplateType->HasDispatchMap; + return false; + } + return true; + } + } + + internal DispatchMap* DispatchMap + { + get + { + if (NumInterfaces == 0) + return null; + byte* optionalFields = OptionalFieldsPtr; + if (optionalFields == null) + return null; + uint idxDispatchMap = OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.DispatchMap, 0xffffffff); + if (idxDispatchMap == 0xffffffff && IsDynamicType) + { + if (HasDynamicallyAllocatedDispatchMap) + { + fixed (MethodTable* pThis = &this) + return *(DispatchMap**)((byte*)pThis + GetFieldOffset(EETypeField.ETF_DynamicDispatchMap)); + } + else + return DynamicTemplateType->DispatchMap; + } + + return ((DispatchMap**)TypeManager.DispatchMap)[idxDispatchMap]; + } + } + + // Get the address of the finalizer method for finalizable types. + internal IntPtr FinalizerCode + { + get + { + Debug.Assert(IsFinalizable); + + if (IsDynamicType || !SupportsRelativePointers) + return GetField(EETypeField.ETF_Finalizer).Value; + + return GetField(EETypeField.ETF_Finalizer).Value; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert(IsDynamicType && IsFinalizable); + + fixed (MethodTable* pThis = &this) + *(IntPtr*)((byte*)pThis + GetFieldOffset(EETypeField.ETF_Finalizer)) = value; + } +#endif + } + + internal MethodTable* BaseType + { + get + { + if (IsCloned) + { + return CanonicalEEType->BaseType; + } + + if (IsParameterizedType) + { + if (IsArray) + return GetArrayEEType(); + else + return null; + } + + Debug.Assert(IsCanonical); + + if (IsRelatedTypeViaIAT) + return *_relatedType._ppBaseTypeViaIAT; + else + return _relatedType._pBaseType; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert(IsDynamicType); + Debug.Assert(!IsParameterizedType); + Debug.Assert(!IsCloned); + Debug.Assert(IsCanonical); + _usFlags &= (ushort)~EETypeFlags.RelatedTypeViaIATFlag; + _relatedType._pBaseType = value; + } +#endif + } + + internal MethodTable* NonArrayBaseType + { + get + { + Debug.Assert(!IsArray, "array type not supported in BaseType"); + + if (IsCloned) + { + // Assuming that since this is not an Array, the CanonicalEEType is also not an array + return CanonicalEEType->NonArrayBaseType; + } + + Debug.Assert(IsCanonical, "we expect canonical types here"); + + if (IsRelatedTypeViaIAT) + { + return *_relatedType._ppBaseTypeViaIAT; + } + + return _relatedType._pBaseType; + } + } + + internal MethodTable* NonClonedNonArrayBaseType + { + get + { + Debug.Assert(!IsArray, "array type not supported in NonArrayBaseType"); + Debug.Assert(!IsCloned, "cloned type not supported in NonClonedNonArrayBaseType"); + Debug.Assert(IsCanonical || IsGenericTypeDefinition, "we expect canonical types here"); + + if (IsRelatedTypeViaIAT) + { + return *_relatedType._ppBaseTypeViaIAT; + } + + return _relatedType._pBaseType; + } + } + + internal MethodTable* RawBaseType + { + get + { + Debug.Assert(!IsParameterizedType, "array type not supported in NonArrayBaseType"); + Debug.Assert(!IsCloned, "cloned type not supported in NonClonedNonArrayBaseType"); + Debug.Assert(IsCanonical, "we expect canonical types here"); + Debug.Assert(!IsRelatedTypeViaIAT, "Non IAT"); + + return _relatedType._pBaseType; + } + } + + internal MethodTable* CanonicalEEType + { + get + { + // cloned EETypes must always refer to types in other modules + Debug.Assert(IsCloned); + if (IsRelatedTypeViaIAT) + return *_relatedType._ppCanonicalTypeViaIAT; + else + return _relatedType._pCanonicalType; + } + } + + internal MethodTable* NullableType + { + get + { + Debug.Assert(IsNullable); + Debug.Assert(GenericArity == 1); + return GenericArguments[0].Value; + } + } + + /// + /// Gets the offset of the value embedded in a Nullable<T>. + /// + internal byte NullableValueOffset + { + get + { + Debug.Assert(IsNullable); + + // Grab optional fields. If there aren't any then the offset was the default of 1 (immediately after the + // Nullable's boolean flag). + byte* optionalFields = OptionalFieldsPtr; + if (optionalFields == null) + return 1; + + // The offset is never zero (Nullable has a boolean there indicating whether the value is valid). So the + // offset is encoded - 1 to save space. The zero below is the default value if the field wasn't encoded at + // all. + return (byte)(OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.NullableValueOffset, 0) + 1); + } + } + + internal MethodTable* RelatedParameterType + { + get + { + Debug.Assert(IsParameterizedType); + + if (IsRelatedTypeViaIAT) + return *_relatedType._ppRelatedParameterTypeViaIAT; + else + return _relatedType._pRelatedParameterType; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert(IsDynamicType && IsParameterizedType); + _usFlags &= ((ushort)~EETypeFlags.RelatedTypeViaIATFlag); + _relatedType._pRelatedParameterType = value; + } +#endif + } + + internal unsafe IntPtr* GetVTableStartAddress() + { + byte* pResult; + + // EETypes are always in unmanaged memory, so 'leaking' the 'fixed pointer' is safe. + fixed (MethodTable* pThis = &this) + pResult = (byte*)pThis; + + pResult += sizeof(MethodTable); + return (IntPtr*)pResult; + } + + private static IntPtr FollowRelativePointer(int* pDist) + { + int dist = *pDist; + IntPtr result = (IntPtr)((byte*)pDist + dist); + return result; + } + + internal IntPtr GetSealedVirtualSlot(ushort slotNumber) + { + Debug.Assert((RareFlags & EETypeRareFlags.HasSealedVTableEntriesFlag) != 0); + + fixed (MethodTable* pThis = &this) + { + if (IsDynamicType || !SupportsRelativePointers) + { + uint cbSealedVirtualSlotsTypeOffset = GetFieldOffset(EETypeField.ETF_SealedVirtualSlots); + IntPtr* pSealedVirtualsSlotTable = *(IntPtr**)((byte*)pThis + cbSealedVirtualSlotsTypeOffset); + return pSealedVirtualsSlotTable[slotNumber]; + } + else + { + uint cbSealedVirtualSlotsTypeOffset = GetFieldOffset(EETypeField.ETF_SealedVirtualSlots); + int* pSealedVirtualsSlotTable = (int*)FollowRelativePointer((int*)((byte*)pThis + cbSealedVirtualSlotsTypeOffset)); + IntPtr result = FollowRelativePointer(&pSealedVirtualsSlotTable[slotNumber]); + return result; + } + } + } + +#if TYPE_LOADER_IMPLEMENTATION + internal void SetSealedVirtualSlot(IntPtr value, ushort slotNumber) + { + Debug.Assert(IsDynamicType); + + fixed (MethodTable* pThis = &this) + { + uint cbSealedVirtualSlotsTypeOffset = GetFieldOffset(EETypeField.ETF_SealedVirtualSlots); + IntPtr* pSealedVirtualsSlotTable = *(IntPtr**)((byte*)pThis + cbSealedVirtualSlotsTypeOffset); + pSealedVirtualsSlotTable[slotNumber] = value; + } + } +#endif + + internal byte* OptionalFieldsPtr + { + get + { + if (!HasOptionalFields) + return null; + + if (IsDynamicType || !SupportsRelativePointers) + return GetField>(EETypeField.ETF_OptionalFieldsPtr).Value; + + return GetField>(EETypeField.ETF_OptionalFieldsPtr).Value; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert(IsDynamicType); + + _usFlags |= (ushort)EETypeFlags.OptionalFieldsFlag; + + uint cbOptionalFieldsOffset = GetFieldOffset(EETypeField.ETF_OptionalFieldsPtr); + fixed (MethodTable* pThis = &this) + { + *(byte**)((byte*)pThis + cbOptionalFieldsOffset) = value; + } + } +#endif + } + + internal MethodTable* DynamicTemplateType + { + get + { + Debug.Assert(IsDynamicType); + uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicTemplateType); + fixed (MethodTable* pThis = &this) + { + return *(MethodTable**)((byte*)pThis + cbOffset); + } + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert(IsDynamicType); + uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicTemplateType); + fixed (MethodTable* pThis = &this) + { + *(MethodTable**)((byte*)pThis + cbOffset) = value; + } + } +#endif + } + + internal IntPtr DynamicGcStaticsData + { + get + { + Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0); + uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicGcStatics); + fixed (MethodTable* pThis = &this) + { + return *(IntPtr*)((byte*)pThis + cbOffset); + } + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0); + uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicGcStatics); + fixed (MethodTable* pThis = &this) + { + *(IntPtr*)((byte*)pThis + cbOffset) = value; + } + } +#endif + } + + internal IntPtr DynamicNonGcStaticsData + { + get + { + Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0); + uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicNonGcStatics); + fixed (MethodTable* pThis = &this) + { + return *(IntPtr*)((byte*)pThis + cbOffset); + } + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0); + uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicNonGcStatics); + fixed (MethodTable* pThis = &this) + { + *(IntPtr*)((byte*)pThis + cbOffset) = value; + } + } +#endif + } + + internal DynamicModule* DynamicModule + { + get + { + if ((RareFlags & EETypeRareFlags.HasDynamicModuleFlag) != 0) + { + uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicModule); + fixed (MethodTable* pThis = &this) + { + return *(DynamicModule**)((byte*)pThis + cbOffset); + } + } + else + { + return null; + } + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert(RareFlags.HasFlag(EETypeRareFlags.HasDynamicModuleFlag)); + uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicModule); + fixed (MethodTable* pThis = &this) + { + *(DynamicModule**)((byte*)pThis + cbOffset) = value; + } + } +#endif + } + + internal TypeManagerHandle TypeManager + { + get + { + IntPtr typeManagerIndirection; + if (IsDynamicType || !SupportsRelativePointers) + typeManagerIndirection = GetField(EETypeField.ETF_TypeManagerIndirection).Value; + else + typeManagerIndirection = GetField(EETypeField.ETF_TypeManagerIndirection).Value; + + return *(TypeManagerHandle*)typeManagerIndirection; + } + } +#if TYPE_LOADER_IMPLEMENTATION + internal IntPtr PointerToTypeManager + { + get + { + uint cbOffset = GetFieldOffset(EETypeField.ETF_TypeManagerIndirection); + // This is always a pointer to a pointer to a type manager + return (IntPtr)(*(TypeManagerHandle**)((byte*)Unsafe.AsPointer(ref this) + cbOffset)); + } + set + { + uint cbOffset = GetFieldOffset(EETypeField.ETF_TypeManagerIndirection); + // This is always a pointer to a pointer to a type manager + *(TypeManagerHandle**)((byte*)Unsafe.AsPointer(ref this) + cbOffset) = (TypeManagerHandle*)value; + } + } +#endif + + /// + /// Gets a pointer to a segment of writable memory associated with this MethodTable. + /// The purpose of the segment is controlled by the class library. The runtime doesn't + /// use this memory for any purpose. + /// + internal IntPtr WritableData + { + get + { + Debug.Assert(SupportsWritableData); + + uint offset = GetFieldOffset(EETypeField.ETF_WritableData); + + if (!IsDynamicType) + return GetField(offset).Value; + else + return GetField(offset).Value; + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert(IsDynamicType && SupportsWritableData); + + uint cbOffset = GetFieldOffset(EETypeField.ETF_WritableData); + *(IntPtr*)((byte*)Unsafe.AsPointer(ref this) + cbOffset) = value; + } +#endif + } + + internal unsafe EETypeRareFlags RareFlags + { + get + { + // If there are no optional fields then none of the rare flags have been set. + // Get the flags from the optional fields. The default is zero if that particular field was not included. + return HasOptionalFields ? (EETypeRareFlags)OptionalFieldsReader.GetInlineField(OptionalFieldsPtr, EETypeOptionalFieldTag.RareFlags, 0) : 0; + } + } + + internal int FieldAlignmentRequirement + { + get + { + byte* optionalFields = OptionalFieldsPtr; + + // If there are no optional fields then the alignment must have been the default, IntPtr.Size. + // (This happens for all reference types, and for valuetypes with default alignment and no padding) + if (optionalFields == null) + return IntPtr.Size; + + // Get the value from the optional fields. The default is zero if that particular field was not included. + // The low bits of this field is the ValueType field padding, the rest of the value is the alignment if present + uint alignmentValue = (OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.ValueTypeFieldPadding, 0) & ValueTypePaddingAlignmentMask) >> ValueTypePaddingAlignmentShift; + + // Alignment is stored as 1 + the log base 2 of the alignment, except a 0 indicates standard pointer alignment. + if (alignmentValue == 0) + return IntPtr.Size; + else + return 1 << ((int)alignmentValue - 1); + } + } + + internal EETypeElementType ElementType + { + get + { + return (EETypeElementType)((_usFlags & (ushort)EETypeFlags.ElementTypeMask) >> (ushort)EETypeFlags.ElementTypeShift); + } + } + + public bool HasCctor + { + get + { + return (RareFlags & EETypeRareFlags.HasCctorFlag) != 0; + } + } + + public uint GetFieldOffset(EETypeField eField) + { + // First part of MethodTable consists of the fixed portion followed by the vtable. + uint cbOffset = (uint)(sizeof(MethodTable) + (IntPtr.Size * _usNumVtableSlots)); + + // Then we have the interface map. + if (eField == EETypeField.ETF_InterfaceMap) + { + Debug.Assert(NumInterfaces > 0); + return cbOffset; + } + cbOffset += (uint)(sizeof(EEInterfaceInfo) * NumInterfaces); + + uint relativeOrFullPointerOffset = (IsDynamicType || !SupportsRelativePointers ? (uint)IntPtr.Size : 4); + + // Followed by the type manager indirection cell. + if (eField == EETypeField.ETF_TypeManagerIndirection) + { + return cbOffset; + } + cbOffset += relativeOrFullPointerOffset; + + // Followed by writable data. + if (SupportsWritableData) + { + if (eField == EETypeField.ETF_WritableData) + { + return cbOffset; + } + cbOffset += relativeOrFullPointerOffset; + } + + // Followed by the pointer to the finalizer method. + if (eField == EETypeField.ETF_Finalizer) + { + Debug.Assert(IsFinalizable); + return cbOffset; + } + if (IsFinalizable) + cbOffset += relativeOrFullPointerOffset; + + // Followed by the pointer to the optional fields. + if (eField == EETypeField.ETF_OptionalFieldsPtr) + { + Debug.Assert(HasOptionalFields); + return cbOffset; + } + if (HasOptionalFields) + cbOffset += relativeOrFullPointerOffset; + + // Followed by the pointer to the sealed virtual slots + if (eField == EETypeField.ETF_SealedVirtualSlots) + return cbOffset; + + EETypeRareFlags rareFlags = RareFlags; + + // in the case of sealed vtable entries on static types, we have a UInt sized relative pointer + if ((rareFlags & EETypeRareFlags.HasSealedVTableEntriesFlag) != 0) + cbOffset += relativeOrFullPointerOffset; + + if (eField == EETypeField.ETF_DynamicDispatchMap) + { + Debug.Assert(IsDynamicType); + return cbOffset; + } + if ((rareFlags & EETypeRareFlags.HasDynamicallyAllocatedDispatchMapFlag) != 0) + cbOffset += (uint)IntPtr.Size; + + if (eField == EETypeField.ETF_GenericDefinition) + { + Debug.Assert(IsGeneric); + return cbOffset; + } + if (IsGeneric) + { + cbOffset += relativeOrFullPointerOffset; + } + + if (eField == EETypeField.ETF_GenericComposition) + { + Debug.Assert(IsGeneric); + return cbOffset; + } + if (IsGeneric) + { + cbOffset += relativeOrFullPointerOffset; + } + + if (eField == EETypeField.ETF_DynamicModule) + { + return cbOffset; + } + + if ((rareFlags & EETypeRareFlags.HasDynamicModuleFlag) != 0) + cbOffset += (uint)IntPtr.Size; + + if (eField == EETypeField.ETF_DynamicTemplateType) + { + Debug.Assert(IsDynamicType); + return cbOffset; + } + if (IsDynamicType) + cbOffset += (uint)IntPtr.Size; + + if (eField == EETypeField.ETF_DynamicGcStatics) + { + Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0); + return cbOffset; + } + if ((rareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0) + cbOffset += (uint)IntPtr.Size; + + if (eField == EETypeField.ETF_DynamicNonGcStatics) + { + Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0); + return cbOffset; + } + if ((rareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0) + cbOffset += (uint)IntPtr.Size; + + if (eField == EETypeField.ETF_DynamicThreadStaticOffset) + { + Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0); + return cbOffset; + } + if ((rareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0) + cbOffset += 4; + + Debug.Assert(false, "Unknown MethodTable field type"); + return 0; + } + + public ref T GetField(EETypeField eField) + { + return ref Unsafe.As(ref *((byte*)Unsafe.AsPointer(ref this) + GetFieldOffset(eField))); + } + + public ref T GetField(uint offset) + { + return ref Unsafe.As(ref *((byte*)Unsafe.AsPointer(ref this) + offset)); + } + +#if TYPE_LOADER_IMPLEMENTATION + internal static uint GetSizeofEEType( + ushort cVirtuals, + ushort cInterfaces, + bool fHasFinalizer, + bool fRequiresOptionalFields, + bool fHasSealedVirtuals, + bool fHasGenericInfo, + bool fHasNonGcStatics, + bool fHasGcStatics, + bool fHasThreadStatics) + { + return (uint)(sizeof(MethodTable) + + (IntPtr.Size * cVirtuals) + + (sizeof(EEInterfaceInfo) * cInterfaces) + + sizeof(IntPtr) + // TypeManager + (SupportsWritableData ? sizeof(IntPtr) : 0) + // WritableData + (fHasFinalizer ? sizeof(UIntPtr) : 0) + + (fRequiresOptionalFields ? sizeof(IntPtr) : 0) + + (fHasSealedVirtuals ? sizeof(IntPtr) : 0) + + (fHasGenericInfo ? sizeof(IntPtr)*2 : 0) + // pointers to GenericDefinition and GenericComposition + (fHasNonGcStatics ? sizeof(IntPtr) : 0) + // pointer to data + (fHasGcStatics ? sizeof(IntPtr) : 0) + // pointer to data + (fHasThreadStatics ? sizeof(uint) : 0)); // tls offset + } +#endif + } + + // Wrapper around MethodTable pointers that may be indirected through the IAT if their low bit is set. + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct EETypeRef + { + private byte* _value; + + public MethodTable* Value + { + get + { + if (((int)_value & IndirectionConstants.IndirectionCellPointer) == 0) + return (MethodTable*)_value; + return *(MethodTable**)(_value - IndirectionConstants.IndirectionCellPointer); + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _value = (byte*)value; + } +#endif + } + } + + // Wrapper around pointers + [StructLayout(LayoutKind.Sequential)] + internal readonly struct Pointer + { + private readonly IntPtr _value; + + public IntPtr Value + { + get + { + return _value; + } + } + } + + // Wrapper around pointers + [StructLayout(LayoutKind.Sequential)] + internal unsafe readonly struct Pointer where T : unmanaged + { + private readonly T* _value; + + public T* Value + { + get + { + return _value; + } + } + } + + // Wrapper around pointers that might be indirected through IAT + [StructLayout(LayoutKind.Sequential)] + internal unsafe readonly struct IatAwarePointer where T : unmanaged + { + private readonly T* _value; + + public T* Value + { + get + { + if (((int)_value & IndirectionConstants.IndirectionCellPointer) == 0) + return _value; + return *(T**)((byte*)_value - IndirectionConstants.IndirectionCellPointer); + } + } + } + + // Wrapper around relative pointers + [StructLayout(LayoutKind.Sequential)] + internal readonly struct RelativePointer + { + private readonly int _value; + + public unsafe IntPtr Value + { + get + { + return (IntPtr)((byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in _value)) + _value); + } + } + } + + // Wrapper around relative pointers + [StructLayout(LayoutKind.Sequential)] + internal unsafe readonly struct RelativePointer where T : unmanaged + { + private readonly int _value; + + public T* Value + { + get + { + return (T*)((byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in _value)) + _value); + } + } + } + + // Wrapper around relative pointers that might be indirected through IAT + [StructLayout(LayoutKind.Sequential)] + internal unsafe readonly struct IatAwareRelativePointer where T : unmanaged + { + private readonly int _value; + + public T* Value + { + get + { + if ((_value & IndirectionConstants.IndirectionCellPointer) == 0) + { + return (T*)((byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in _value)) + _value); + } + else + { + return *(T**)((byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in _value)) + (_value & ~IndirectionConstants.IndirectionCellPointer)); + } + } + } + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct DynamicModule + { + // Size field used to indicate the number of bytes of this structure that are defined in Runtime Known ways + // This is used to drive versioning of this field + private int _cbSize; + + // Pointer to interface dispatch resolver that works off of a type/slot pair + // This is a function pointer with the following signature IntPtr()(MethodTable* targetType, MethodTable* interfaceType, ushort slot) + private delegate* _dynamicTypeSlotDispatchResolve; + + // Starting address for the the binary module corresponding to this dynamic module. + private delegate* _getRuntimeException; + +#if TYPE_LOADER_IMPLEMENTATION + public int CbSize + { + get + { + return _cbSize; + } + set + { + _cbSize = value; + } + } +#endif + + public delegate* DynamicTypeSlotDispatchResolve + { + get + { + if (_cbSize >= sizeof(IntPtr) * 2) + { + return _dynamicTypeSlotDispatchResolve; + } + else + { + return null; + } + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _dynamicTypeSlotDispatchResolve = value; + } +#endif + } + + public delegate* GetRuntimeException + { + get + { + if (_cbSize >= sizeof(IntPtr) * 3) + { + return _getRuntimeException; + } + else + { + return null; + } + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + _getRuntimeException = value; + } +#endif + } + + /////////////////////// END OF FIELDS KNOWN TO THE MRT RUNTIME //////////////////////// +#if TYPE_LOADER_IMPLEMENTATION + public static readonly int DynamicModuleSize = IntPtr.Size * 3; // We have three fields here. + + // We can put non-low level runtime fields that are module level, that need quick access from a type here + // For instance, we may choose to put a pointer to the metadata reader or the like here in the future. +#endif + } +} diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/TransitionBlock.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/TransitionBlock.cs new file mode 100644 index 00000000000000..909514e070b122 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/TransitionBlock.cs @@ -0,0 +1,468 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This file is a line by line port of callingconvention.h from the CLR with the intention that we may wish to merge +// changes from the CLR in at a later time. As such, the normal coding conventions are ignored. +// + +// +#if TARGET_ARM +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define FEATURE_HFA +#elif TARGET_ARM64 +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define ENREGISTERED_PARAMTYPE_MAXSIZE +#define FEATURE_HFA +#elif TARGET_X86 +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#elif TARGET_AMD64 +#if TARGET_UNIX +#define UNIX_AMD64_ABI +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#else +#endif +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define ENREGISTERED_PARAMTYPE_MAXSIZE +#elif TARGET_WASM +#else +#error Unknown architecture! +#endif + +// Provides an abstraction over platform specific calling conventions (specifically, the calling convention +// utilized by the JIT on that platform). The caller enumerates each argument of a signature in turn, and is +// provided with information mapping that argument into registers and/or stack locations. + +using System; + +namespace Internal.Runtime +{ +#if TARGET_AMD64 +#pragma warning disable 0169 +#if UNIX_AMD64_ABI + struct ReturnBlock + { + IntPtr returnValue; + IntPtr returnValue2; + } + + struct ArgumentRegisters + { + IntPtr rdi; + IntPtr rsi; + IntPtr rdx; + IntPtr rcx; + IntPtr r8; + IntPtr r9; + } +#else // UNIX_AMD64_ABI + struct ReturnBlock + { + IntPtr returnValue; + } + + struct ArgumentRegisters + { + IntPtr rdx; + IntPtr rcx; + IntPtr r8; + IntPtr r9; + } +#endif // UNIX_AMD64_ABI +#pragma warning restore 0169 + +#pragma warning disable 0169 + struct M128A + { + IntPtr a; + IntPtr b; + } + struct FloatArgumentRegisters + { + M128A d0; + M128A d1; + M128A d2; + M128A d3; +#if UNIX_AMD64_ABI + M128A d4; + M128A d5; + M128A d6; + M128A d7; +#endif + } +#pragma warning restore 0169 + + struct ArchitectureConstants + { + // To avoid corner case bugs, limit maximum size of the arguments with sufficient margin + public const int MAX_ARG_SIZE = 0xFFFFFF; + +#if UNIX_AMD64_ABI + public const int NUM_ARGUMENT_REGISTERS = 6; +#else + public const int NUM_ARGUMENT_REGISTERS = 4; +#endif + public const int ARGUMENTREGISTERS_SIZE = NUM_ARGUMENT_REGISTERS * 8; + public const int ENREGISTERED_RETURNTYPE_MAXSIZE = 8; + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE = 8; + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE = 8; + public const int ENREGISTERED_PARAMTYPE_MAXSIZE = 8; + public const int STACK_ELEM_SIZE = 8; + public static int StackElemSize(int size) { return (((size) + STACK_ELEM_SIZE - 1) & ~(STACK_ELEM_SIZE - 1)); } + } +#elif TARGET_ARM64 +#pragma warning disable 0169 + struct ReturnBlock + { + IntPtr returnValue; + IntPtr returnValue2; + IntPtr returnValue3; + IntPtr returnValue4; + } + + struct ArgumentRegisters + { + IntPtr x0; + IntPtr x1; + IntPtr x2; + IntPtr x3; + IntPtr x4; + IntPtr x5; + IntPtr x6; + IntPtr x7; + IntPtr x8; + public static unsafe int GetOffsetOfx8() + { + return sizeof(IntPtr) * 8; + } + } +#pragma warning restore 0169 + +#pragma warning disable 0169 + struct FloatArgumentRegisters + { + double d0; + double d1; + double d2; + double d3; + double d4; + double d5; + double d6; + double d7; + } +#pragma warning restore 0169 + + struct ArchitectureConstants + { + // To avoid corner case bugs, limit maximum size of the arguments with sufficient margin + public const int MAX_ARG_SIZE = 0xFFFFFF; + + public const int NUM_ARGUMENT_REGISTERS = 8; + public const int ARGUMENTREGISTERS_SIZE = NUM_ARGUMENT_REGISTERS * 8; + public const int ENREGISTERED_RETURNTYPE_MAXSIZE = 32; // bytes (four FP registers: d0,d1,d2 and d3) + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE = 16; // bytes (two int registers: x0 and x1) + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE = 8; + public const int ENREGISTERED_PARAMTYPE_MAXSIZE = 16; // bytes (max value type size that can be passed by value) + public const int STACK_ELEM_SIZE = 8; + public static int StackElemSize(int size) { return (((size) + STACK_ELEM_SIZE - 1) & ~(STACK_ELEM_SIZE - 1)); } + } +#elif TARGET_X86 +#pragma warning disable 0169, 0649 + struct ReturnBlock + { + public IntPtr returnValue; + public IntPtr returnValue2; + } + + struct ArgumentRegisters + { + public IntPtr edx; + public static unsafe int GetOffsetOfEdx() + { + return 0; + } + public IntPtr ecx; + public static unsafe int GetOffsetOfEcx() + { + return sizeof(IntPtr); + } + } + // This struct isn't used by x86, but exists for compatibility with the definition of the CallDescrData struct + struct FloatArgumentRegisters + { + } +#pragma warning restore 0169, 0649 + + struct ArchitectureConstants + { + // To avoid corner case bugs, limit maximum size of the arguments with sufficient margin + public const int MAX_ARG_SIZE = 0xFFFFFF; + + public const int NUM_ARGUMENT_REGISTERS = 2; + public const int ARGUMENTREGISTERS_SIZE = NUM_ARGUMENT_REGISTERS * 4; + public const int ENREGISTERED_RETURNTYPE_MAXSIZE = 8; + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE = 4; + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE = 4; + public const int STACK_ELEM_SIZE = 4; + public static int StackElemSize(int size) { return (((size) + STACK_ELEM_SIZE - 1) & ~(STACK_ELEM_SIZE - 1)); } + } +#elif TARGET_ARM +#pragma warning disable 0169 + struct ReturnBlock + { + IntPtr returnValue; + IntPtr returnValue2; + IntPtr returnValue3; + IntPtr returnValue4; + IntPtr returnValue5; + IntPtr returnValue6; + IntPtr returnValue7; + IntPtr returnValue8; + } + + struct ArgumentRegisters + { + IntPtr r0; + IntPtr r1; + IntPtr r2; + IntPtr r3; + } + + struct FloatArgumentRegisters + { + double d0; + double d1; + double d2; + double d3; + double d4; + double d5; + double d6; + double d7; + } +#pragma warning restore 0169 + + struct ArchitectureConstants + { + // To avoid corner case bugs, limit maximum size of the arguments with sufficient margin + public const int MAX_ARG_SIZE = 0xFFFFFF; + + public const int NUM_ARGUMENT_REGISTERS = 4; + public const int ARGUMENTREGISTERS_SIZE = NUM_ARGUMENT_REGISTERS * 4; + public const int ENREGISTERED_RETURNTYPE_MAXSIZE = 32; + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE = 4; + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE = 8; + public const int STACK_ELEM_SIZE = 4; + public static int StackElemSize(int size) { return (((size) + STACK_ELEM_SIZE - 1) & ~(STACK_ELEM_SIZE - 1)); } + } + +#elif TARGET_WASM +#pragma warning disable 0169 + struct ReturnBlock + { + IntPtr returnValue; + } + + struct ArgumentRegisters + { + // No registers on WASM + } + + struct FloatArgumentRegisters + { + // No registers on WASM + } +#pragma warning restore 0169 + + struct ArchitectureConstants + { + // To avoid corner case bugs, limit maximum size of the arguments with sufficient margin + public const int MAX_ARG_SIZE = 0xFFFFFF; + + public const int NUM_ARGUMENT_REGISTERS = 0; + public const int ARGUMENTREGISTERS_SIZE = NUM_ARGUMENT_REGISTERS * 4; + public const int ENREGISTERED_RETURNTYPE_MAXSIZE = 32; + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE = 4; + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE = 8; + public const int STACK_ELEM_SIZE = 4; + public static int StackElemSize(int size) { return (((size) + STACK_ELEM_SIZE - 1) & ~(STACK_ELEM_SIZE - 1)); } + } +#endif + + // + // TransitionBlock is layout of stack frame of method call, saved argument registers and saved callee saved registers. Even though not + // all fields are used all the time, we use uniform form for simplicity. + // + internal struct TransitionBlock + { +#pragma warning disable 0169,0649 + +#if TARGET_X86 + public ArgumentRegisters m_argumentRegisters; + public static unsafe int GetOffsetOfArgumentRegisters() + { + return 0; + } + public ReturnBlock m_returnBlock; + public static unsafe int GetOffsetOfReturnValuesBlock() + { + return sizeof(ArgumentRegisters); + } + IntPtr m_ebp; + IntPtr m_ReturnAddress; +#elif TARGET_AMD64 + +#if UNIX_AMD64_ABI + public ReturnBlock m_returnBlock; + public static unsafe int GetOffsetOfReturnValuesBlock() + { + return 0; + } + + public ArgumentRegisters m_argumentRegisters; + public static unsafe int GetOffsetOfArgumentRegisters() + { + return sizeof(ReturnBlock); + } + + IntPtr m_alignmentPadding; + IntPtr m_ReturnAddress; +#else // UNIX_AMD64_ABI + IntPtr m_returnBlockPadding; + ReturnBlock m_returnBlock; + public static unsafe int GetOffsetOfReturnValuesBlock() + { + return sizeof(IntPtr); + } + IntPtr m_alignmentPadding; + IntPtr m_ReturnAddress; + public static unsafe int GetOffsetOfArgumentRegisters() + { + return sizeof(TransitionBlock); + } +#endif // UNIX_AMD64_ABI + +#elif TARGET_ARM + public ReturnBlock m_returnBlock; + public static unsafe int GetOffsetOfReturnValuesBlock() + { + return 0; + } + + public ArgumentRegisters m_argumentRegisters; + public static unsafe int GetOffsetOfArgumentRegisters() + { + return sizeof(ReturnBlock); + } +#elif TARGET_ARM64 + public ReturnBlock m_returnBlock; + public static unsafe int GetOffsetOfReturnValuesBlock() + { + return 0; + } + + public ArgumentRegisters m_argumentRegisters; + public static unsafe int GetOffsetOfArgumentRegisters() + { + return sizeof(ReturnBlock); + } + + public IntPtr m_alignmentPad; +#elif TARGET_WASM + public ReturnBlock m_returnBlock; + public static unsafe int GetOffsetOfReturnValuesBlock() + { + return 0; + } + + public ArgumentRegisters m_argumentRegisters; + public static unsafe int GetOffsetOfArgumentRegisters() + { + return sizeof(ReturnBlock); + } +#else +#error Portability problem +#endif +#pragma warning restore 0169, 0649 + + // The transition block should define everything pushed by callee. The code assumes in number of places that + // end of the transition block is caller's stack pointer. + + public static unsafe byte GetOffsetOfArgs() + { + return (byte)sizeof(TransitionBlock); + } + + + public static bool IsStackArgumentOffset(int offset) + { + int ofsArgRegs = GetOffsetOfArgumentRegisters(); + + return offset >= (int)(ofsArgRegs + ArchitectureConstants.ARGUMENTREGISTERS_SIZE); + } + + public static bool IsArgumentRegisterOffset(int offset) + { + int ofsArgRegs = GetOffsetOfArgumentRegisters(); + + return offset >= ofsArgRegs && offset < (int)(ofsArgRegs + ArchitectureConstants.ARGUMENTREGISTERS_SIZE); + } + +#if !TARGET_X86 + public static unsafe int GetArgumentIndexFromOffset(int offset) + { + return ((offset - GetOffsetOfArgumentRegisters()) / IntPtr.Size); + } + + public static int GetStackArgumentIndexFromOffset(int offset) + { + return (offset - GetOffsetOfArgs()) / ArchitectureConstants.STACK_ELEM_SIZE; + } +#endif + +#if CALLDESCR_FPARGREGS + public static bool IsFloatArgumentRegisterOffset(int offset) + { + return offset < 0; + } + + public static int GetOffsetOfFloatArgumentRegisters() + { + return -GetNegSpaceSize(); + } +#endif + + public static unsafe int GetNegSpaceSize() + { + int negSpaceSize = 0; +#if CALLDESCR_FPARGREGS + negSpaceSize += sizeof(FloatArgumentRegisters); +#endif + return negSpaceSize; + } + + public static int GetThisOffset() + { + // This pointer is in the first argument register by default + int ret = TransitionBlock.GetOffsetOfArgumentRegisters(); + +#if TARGET_X86 + // x86 is special as always + ret += ArgumentRegisters.GetOffsetOfEcx(); +#endif + + return ret; + } + + public const int InvalidOffset = -1; + }; +} diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/TypeManagerHandle.Equality.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/TypeManagerHandle.Equality.cs new file mode 100644 index 00000000000000..39b6afdebd107c --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/TypeManagerHandle.Equality.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Internal.Runtime +{ + public unsafe partial struct TypeManagerHandle : IEquatable + { + public static bool operator ==(TypeManagerHandle left, TypeManagerHandle right) + { + return left._handleValue == right._handleValue; + } + + public static bool operator !=(TypeManagerHandle left, TypeManagerHandle right) + { + return left._handleValue != right._handleValue; + } + + public override int GetHashCode() + { + return ((IntPtr)_handleValue).GetHashCode(); + } + + public override bool Equals(object o) + { + if (!(o is TypeManagerHandle)) + return false; + + return _handleValue == ((TypeManagerHandle)o)._handleValue; + } + + public bool Equals(TypeManagerHandle other) + { + return _handleValue == other._handleValue; + } + + public string LowLevelToString() + { + return ((IntPtr)_handleValue).LowLevelToString(); + } + } +} diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/TypeManagerHandle.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/TypeManagerHandle.cs new file mode 100644 index 00000000000000..9780acd39f72a9 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/TypeManagerHandle.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +namespace Internal.Runtime +{ + /// + /// TypeManagerHandle represents an AOT module in MRT based runtimes. + /// These handles are a pointer to a TypeManager + /// + public unsafe partial struct TypeManagerHandle + { + private TypeManager* _handleValue; + + // This is a partial definition of the TypeManager struct which is defined in TypeManager.h + [StructLayout(LayoutKind.Sequential)] + private struct TypeManager + { + public IntPtr OsHandle; + public IntPtr ReadyToRunHeader; + public IntPtr DispatchMap; + } + + public TypeManagerHandle(IntPtr handleValue) + { + _handleValue = (TypeManager*)handleValue; + } + + public IntPtr GetIntPtrUNSAFE() + { + return (IntPtr)_handleValue; + } + + public bool IsNull + { + get + { + return _handleValue == null; + } + } + + public unsafe IntPtr OsModuleBase + { + get + { + return _handleValue->OsHandle; + } + } + + public unsafe IntPtr DispatchMap + { + get + { + return _handleValue->DispatchMap; + } + } + } +} diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Concurrent/ConcurrentUnifier.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Concurrent/ConcurrentUnifier.cs new file mode 100644 index 00000000000000..888522ccbea484 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Collections/Concurrent/ConcurrentUnifier.cs @@ -0,0 +1,310 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; + +namespace System.Collections.Concurrent +{ + // Abstract base for a thread-safe dictionary mapping a set of keys (K) to values (V). + // + // To create an actual dictionary, subclass this type and override the protected Factory method + // to instantiate values (V) for the "Add" case. + // + // The key must be of a type that implements IEquatable. The unifier calls IEquality.Equals() + // and Object.GetHashCode() on the keys. + // + // Deadlock risks: + // - Keys may be tested for equality and asked to compute their hashcode while the unifier + // holds its lock. Thus these operations must be written carefully to avoid deadlocks and + // reentrancy in to the table. + // + // - The Factory method will never be called inside the unifier lock. If two threads race to + // enter a value for the same key, the Factory() may get invoked twice for the same key - one + // of them will "win" the race and its result entered into the dictionary - other gets thrown away. + // + // Notes: + // - This class is used to look up types when GetType() or typeof() is invoked. + // That means that this class itself cannot do or call anything that does these + // things. + // + // - For this reason, it chooses not to mimic the official ConcurrentDictionary class + // (I don't even want to risk using delegates.) Even the LowLevel versions of these + // general utility classes may not be low-level enough for this class's purpose. + // + // Thread safety guarantees: + // + // ConcurrentUnifier is fully thread-safe and requires no + // additional locking to be done by callers. + // + // Performance characteristics: + // + // ConcurrentUnifier will not block a reader, even while + // the table is being written. Only one writer is allowed at a time; + // ConcurrentUnifier handles the synchronization that ensures this. + // + // Safety for concurrent readers is ensured as follows: + // + // Each hash bucket is maintained as a stack. Inserts are done under + // a lock; the entry is filled out completely, then "published" by a + // single write to the top of the bucket. This ensures that a reader + // will see a valid snapshot of the bucket, once it has read the head. + // + // On resize, we allocate an entirely new table, rather than resizing + // in place. We fill in the new table completely, under the lock, + // then "publish" it with a single write. Any reader that races with + // this will either see the old table or the new one; each will contain + // the same data. + // + internal abstract class ConcurrentUnifier + where K : IEquatable + where V : class + { + protected ConcurrentUnifier() + { + _lock = new Lock(); + _container = new Container(this); + } + + // + // Retrieve the *unique* value for a given key. If the key was previously not entered into the dictionary, + // this method invokes the overridable Factory() method to create the new value. The Factory() method is + // invoked outside of any locks. If two threads race to enter a value for the same key, the Factory() + // may get invoked twice for the same key - one of them will "win" the race and its result entered into the + // dictionary - other gets thrown away. + // + public V GetOrAdd(K key) + { + Debug.Assert(key != null); + Debug.Assert(!_lock.IsAcquired, "GetOrAdd called while lock already acquired. A possible cause of this is an Equals or GetHashCode method that causes reentrancy in the table."); + + int hashCode = key.GetHashCode(); + V value; + bool found = _container.TryGetValue(key, hashCode, out value); +#if DEBUG + { + V checkedValue; + bool checkedFound; + // In debug builds, always exercise a locked TryGet (this is a good way to detect deadlock/reentrancy through Equals/GetHashCode()). + using (LockHolder.Hold(_lock)) + { + _container.VerifyUnifierConsistency(); + int h = key.GetHashCode(); + checkedFound = _container.TryGetValue(key, h, out checkedValue); + } + + if (found) + { + // State of a key must never go from found to not found, and only one value may exist per key. + Debug.Assert(checkedFound); + if (default(V) == null) // No good way to do the "only one value" check for value types. + Debug.Assert(object.ReferenceEquals(checkedValue, value)); + } + } +#endif //DEBUG + if (found) + return value; + + value = this.Factory(key); + + using (LockHolder.Hold(_lock)) + { + V heyIWasHereFirst; + if (_container.TryGetValue(key, hashCode, out heyIWasHereFirst)) + return heyIWasHereFirst; + if (!_container.HasCapacity) + _container.Resize(); // This overwrites the _container field. + _container.Add(key, hashCode, value); + return value; + } + } + + protected abstract V Factory(K key); + + private volatile Container _container; + private readonly Lock _lock; + + private sealed class Container + { + public Container(ConcurrentUnifier owner) + { + // Note: This could be done by calling Resize()'s logic but we cannot safely do that as this code path is reached + // during class construction time and Resize() pulls in enough stuff that we get cyclic cctor warnings from the build. + _buckets = new int[_initialCapacity]; + for (int i = 0; i < _initialCapacity; i++) + _buckets[i] = -1; + _entries = new Entry[_initialCapacity]; + _nextFreeEntry = 0; + _owner = owner; + } + + private Container(ConcurrentUnifier owner, int[] buckets, Entry[] entries, int nextFreeEntry) + { + _buckets = buckets; + _entries = entries; + _nextFreeEntry = nextFreeEntry; + _owner = owner; + } + + public bool TryGetValue(K key, int hashCode, out V value) + { + // Lock acquistion NOT required (but lock inacquisition NOT guaranteed either.) + + int bucket = ComputeBucket(hashCode, _buckets.Length); + int i = Volatile.Read(ref _buckets[bucket]); + while (i != -1) + { + if (key.Equals(_entries[i]._key)) + { + value = _entries[i]._value; + return true; + } + i = _entries[i]._next; + } + + value = default(V); + return false; + } + + public void Add(K key, int hashCode, V value) + { + Debug.Assert(_owner._lock.IsAcquired); + + int bucket = ComputeBucket(hashCode, _buckets.Length); + + int newEntryIdx = _nextFreeEntry; + _entries[newEntryIdx]._key = key; + _entries[newEntryIdx]._value = value; + _entries[newEntryIdx]._hashCode = hashCode; + _entries[newEntryIdx]._next = _buckets[bucket]; + + _nextFreeEntry++; + + // The line that atomically adds the new key/value pair. If the thread is killed before this line executes but after + // we've incremented _nextFreeEntry, this entry is harmlessly leaked until the next resize. + Volatile.Write(ref _buckets[bucket], newEntryIdx); + + VerifyUnifierConsistency(); + } + + public bool HasCapacity + { + get + { + Debug.Assert(_owner._lock.IsAcquired); + return _nextFreeEntry != _entries.Length; + } + } + + public void Resize() + { + Debug.Assert(_owner._lock.IsAcquired); + + int newSize = HashHelpers.GetPrime(_buckets.Length * 2); +#if DEBUG + newSize = _buckets.Length + 3; +#endif + if (newSize <= _nextFreeEntry) + throw new OutOfMemoryException(); + + Entry[] newEntries = new Entry[newSize]; + int[] newBuckets = new int[newSize]; + for (int i = 0; i < newSize; i++) + newBuckets[i] = -1; + + // Note that we walk the bucket chains rather than iterating over _entries. This is because we allow for the possibility + // of abandoned entries (with undefined contents) if a thread is killed between allocating an entry and linking it onto the + // bucket chain. + int newNextFreeEntry = 0; + for (int bucket = 0; bucket < _buckets.Length; bucket++) + { + for (int entry = _buckets[bucket]; entry != -1; entry = _entries[entry]._next) + { + newEntries[newNextFreeEntry]._key = _entries[entry]._key; + newEntries[newNextFreeEntry]._value = _entries[entry]._value; + newEntries[newNextFreeEntry]._hashCode = _entries[entry]._hashCode; + int newBucket = ComputeBucket(newEntries[newNextFreeEntry]._hashCode, newSize); + newEntries[newNextFreeEntry]._next = newBuckets[newBucket]; + newBuckets[newBucket] = newNextFreeEntry; + newNextFreeEntry++; + } + } + + // The assertion is "<=" rather than "==" because we allow an entry to "leak" until the next resize if + // a thread died between the time between we allocated the entry and the time we link it into the bucket stack. + Debug.Assert(newNextFreeEntry <= _nextFreeEntry); + + // The line that atomically installs the resize. If this thread is killed before this point, + // the table remains full and the next guy attempting an add will have to redo the resize. + _owner._container = new Container(_owner, newBuckets, newEntries, newNextFreeEntry); + + _owner._container.VerifyUnifierConsistency(); + } + + private static int ComputeBucket(int hashCode, int numBuckets) + { + int bucket = (hashCode & 0x7fffffff) % numBuckets; + return bucket; + } + + [Conditional("DEBUG")] + public void VerifyUnifierConsistency() + { +#if DEBUG + // There's a point at which this check becomes gluttonous, even by checked build standards... + if (_nextFreeEntry >= 5000 && (0 != (_nextFreeEntry % 100))) + return; + + Debug.Assert(_owner._lock.IsAcquired); + Debug.Assert(_nextFreeEntry >= 0 && _nextFreeEntry <= _entries.Length); + int numEntriesEncountered = 0; + for (int bucket = 0; bucket < _buckets.Length; bucket++) + { + int walk1 = _buckets[bucket]; + int walk2 = _buckets[bucket]; // walk2 advances two elements at a time - if walk1 ever meets walk2, we've detected a cycle. + while (walk1 != -1) + { + numEntriesEncountered++; + Debug.Assert(walk1 >= 0 && walk1 < _nextFreeEntry); + Debug.Assert(walk2 >= -1 && walk2 < _nextFreeEntry); + Debug.Assert(_entries[walk1]._key != null); + int hashCode = _entries[walk1]._key.GetHashCode(); + Debug.Assert(hashCode == _entries[walk1]._hashCode); + int storedBucket = ComputeBucket(_entries[walk1]._hashCode, _buckets.Length); + Debug.Assert(storedBucket == bucket); + walk1 = _entries[walk1]._next; + if (walk2 != -1) + walk2 = _entries[walk2]._next; + if (walk2 != -1) + walk2 = _entries[walk2]._next; + if (walk1 == walk2 && walk2 != -1) + Debug.Fail("Bucket " + bucket + " has a cycle in its linked list."); + } + } + // The assertion is "<=" rather than "==" because we allow an entry to "leak" until the next resize if + // a thread died between the time between we allocated the entry and the time we link it into the bucket stack. + Debug.Assert(numEntriesEncountered <= _nextFreeEntry); +#endif //DEBUG + } + + private readonly int[] _buckets; + private readonly Entry[] _entries; + private int _nextFreeEntry; + + private readonly ConcurrentUnifier _owner; + + private const int _initialCapacity = 5; + } + + private struct Entry + { + public K _key; + public V _value; + public int _hashCode; + public int _next; + } + } +} diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Concurrent/ConcurrentUnifierW.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Concurrent/ConcurrentUnifierW.cs new file mode 100644 index 00000000000000..1d805e0081bc13 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Collections/Concurrent/ConcurrentUnifierW.cs @@ -0,0 +1,396 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; + +namespace System.Collections.Concurrent +{ + // Abstract base for a thread-safe dictionary mapping a set of keys (K) to values (V). + // + // Value immortality is guaranteed. Once entered into the dictionary, the value never expires + // in an observable way as long as values don't have finalizers. + // + // To create an actual dictionary, subclass this type and override the protected Factory method + // to instantiate values (V) for the "Add" case. + // + // The key must be of a type that implements IEquatable. The unifier calls IEquality.Equals() + // and Object.GetHashCode() on the keys. + // + // Deadlock risks: + // - Keys may be tested for equality and asked to compute their hashcode while the unifier + // holds its lock. Thus these operations must be written carefully to avoid deadlocks and + // reentrancy in to the table. + // + // - The Factory method will never be called inside the unifier lock. If two threads race to + // enter a value for the same key, the Factory() may get invoked twice for the same key - one + // of them will "win" the race and its result entered into the dictionary - other gets thrown away. + // + // Notes: + // - This class is used to look up types when GetType() or typeof() is invoked. + // That means that this class itself cannot do or call anything that does these + // things. + // + // - For this reason, it chooses not to mimic the official ConcurrentDictionary class + // (I don't even want to risk using delegates.) Even the LowLevel versions of these + // general utility classes may not be low-level enough for this class's purpose. + // + // Thread safety guarantees: + // + // ConcurrentUnifierW is fully thread-safe and requires no + // additional locking to be done by callers. + // + // Performance characteristics: + // + // ConcurrentUnifierW will not block a reader, even while + // the table is being written. Only one writer is allowed at a time; + // ConcurrentUnifierW handles the synchronization that ensures this. + // + // Safety for concurrent readers is ensured as follows: + // + // Each hash bucket is maintained as a stack. Inserts are done under + // a lock in one of two ways: + // + // - The entry is filled out completely, then "published" by a + // single write to the top of the bucket. This ensures that a reader + // will see a valid snapshot of the bucket, once it has read the head. + // + // - An expired WeakReference inside an existing entry is replaced atomically + // by a new WeakReference. A reader will either see the old expired WeakReference + // (if so, he'll wait for the current lock to be released then do the locked retry) + // or the new WeakReference (which is fine for him to see.)) + // + // On resize, we allocate an entirely new table, rather than resizing + // in place. We fill in the new table completely, under the lock, + // then "publish" it with a single write. Any reader that races with + // this will either see the old table or the new one; each will contain + // the same data. + // + internal abstract class ConcurrentUnifierW + where K : IEquatable + where V : class + { + protected ConcurrentUnifierW() + { + _lock = new Lock(); + _container = new Container(this); + } + + // + // Retrieve the *unique* value for a given key. If the key was previously not entered into the dictionary, + // this method invokes the overridable Factory() method to create the new value. The Factory() method is + // invoked outside of any locks. If two threads race to enter a value for the same key, the Factory() + // may get invoked twice for the same key - one of them will "win" the race and its result entered into the + // dictionary - other gets thrown away. + // + public V GetOrAdd(K key) + { + Debug.Assert(key != null); + Debug.Assert(!_lock.IsAcquired, "GetOrAdd called while lock already acquired. A possible cause of this is an Equals or GetHashCode method that causes reentrancy in the table."); + + int hashCode = key.GetHashCode(); + V? value; + bool found = _container.TryGetValue(key, hashCode, out value); +#if DEBUG + { + V? checkedValue; + bool checkedFound; + // In debug builds, always exercise a locked TryGet (this is a good way to detect deadlock/reentrancy through Equals/GetHashCode()). + using (LockHolder.Hold(_lock)) + { + _container.VerifyUnifierConsistency(); + int h = key.GetHashCode(); + checkedFound = _container.TryGetValue(key, h, out checkedValue); + } + + if (found) + { + // Since this DEBUG code is holding a strong reference to "value", state of a key must never go from found to not found, + // and only one value may exist per key. + Debug.Assert(checkedFound); + Debug.Assert(object.ReferenceEquals(checkedValue, value)); + GC.KeepAlive(value); + } + } +#endif //DEBUG + if (found) + return value; + + value = this.Factory(key); + + // This doesn't catch every object that has a finalizer, but the old saying about half a loaf... + Debug.Assert(!(value is IDisposable), + "Values placed in this table should not have finalizers. ConcurrentUnifierW guarantees observational immortality only " + + "in the absence of finalizers. Or to speak more plainly, we can use WeakReferences to guarantee observational immortality " + + "without paying the cost of storage immortality."); + + if (value == null) + { + // There's no point in caching null's in the dictionary as a WeakReference of null will always show up as expired + // and force a re-add every time. Just return the null value without storing it. This does mean that repeated look ups + // for this case will be very slow - this generally corresponds to scenarios like looking for a type member that doesn't + // exist so hopefully, it's better to have awful throughput for such cases rather than polluting the dictionary with + // "null entries" that have to be special-cased for everyone. + return null; + } + + using (LockHolder.Hold(_lock)) + { + V? heyIWasHereFirst; + if (_container.TryGetValue(key, hashCode, out heyIWasHereFirst)) + return heyIWasHereFirst; + if (!_container.HasCapacity) + _container.Resize(); // This overwrites the _container field. + _container.Add(key, hashCode, value); + return value; + } + } + + protected abstract V Factory(K key); + + private volatile Container _container; + private readonly Lock _lock; + + private sealed class Container + { + public Container(ConcurrentUnifierW owner) + { + // Note: This could be done by calling Resize()'s logic but we cannot safely do that as this code path is reached + // during class construction time and Resize() pulls in enough stuff that we get cyclic cctor warnings from the build. + _buckets = new int[_initialCapacity]; + for (int i = 0; i < _initialCapacity; i++) + _buckets[i] = -1; + _entries = new Entry[_initialCapacity]; + _nextFreeEntry = 0; + _owner = owner; + } + + private Container(ConcurrentUnifierW owner, int[] buckets, Entry[] entries, int nextFreeEntry) + { + _buckets = buckets; + _entries = entries; + _nextFreeEntry = nextFreeEntry; + _owner = owner; + } + + public bool TryGetValue(K key, int hashCode, out V? value) + { + // Lock acquistion NOT required. + + int bucket = ComputeBucket(hashCode, _buckets.Length); + int i = Volatile.Read(ref _buckets[bucket]); + while (i != -1) + { + if (key.Equals(_entries[i]._key)) + { + // We found the entry for the key but the weak reference may have expired. If it did expire, do NOT + // try to refill it here. To maintain the appearance of value immortality, ONLY the entry belonging + // to the most up to date chain can be refilled in this way and we can only be sure of doing that we're + // inside the lock. + return _entries[i]._weakValue.TryGetTarget(out value); + } + i = _entries[i]._next; + } + + value = default(V); + return false; + } + + public void Add(K key, int hashCode, V value) + { + Debug.Assert(_owner._lock.IsAcquired); + + int bucket = ComputeBucket(hashCode, _buckets.Length); + + // We must first determine whether we're adding because the weak reference expired or whether no entry for the key. + // exists at all. If the former, we must replacing the existing WeakReference as creating a duplicate entry for the key + // in the same Container violates the invariants of this class. + // + // Note that other threads may be doing Gets on this chain without taking locks. This is why _weakValue is marked volatile. + int idx = _buckets[bucket]; + while (idx != -1) + { + if (_entries[idx]._key.Equals(key)) + { +#if DEBUG + { + V? heyYoureSupposedToBeDead; + if (_entries[idx]._weakValue.TryGetTarget(out heyYoureSupposedToBeDead)) + Debug.Fail("Add: You were supposed to verify inside the lock that this entry's weak reference had already expired!"); + } +#endif //DEBUG + _entries[idx]._weakValue = new WeakReference(value, trackResurrection: false); + return; + } + idx = _entries[idx]._next; + } + + + // If we got here, there is no entry for this particular key. Create a new one and link it atomically + // to the head of the bucket chain. + + int newEntryIdx = _nextFreeEntry; + _entries[newEntryIdx]._key = key; + _entries[newEntryIdx]._weakValue = new WeakReference(value, trackResurrection: false); + _entries[newEntryIdx]._hashCode = hashCode; + _entries[newEntryIdx]._next = _buckets[bucket]; + + _nextFreeEntry++; + + // The line that atomically adds the new key/value pair. If the thread is killed before this line executes but after + // we've incremented _nextFreeEntry, this entry is harmlessly leaked until the next resize. + Volatile.Write(ref _buckets[bucket], newEntryIdx); + + VerifyUnifierConsistency(); + } + + public bool HasCapacity + { + get + { + Debug.Assert(_owner._lock.IsAcquired); + return _nextFreeEntry != _entries.Length; + } + } + + public void Resize() + { + Debug.Assert(_owner._lock.IsAcquired); + + // Before we actually grow the size of the table, figure out how much we can recover just by dropping entries with + // expired weak references. + int estimatedNumLiveEntries = 0; + for (int bucket = 0; bucket < _buckets.Length; bucket++) + { + for (int entry = _buckets[bucket]; entry != -1; entry = _entries[entry]._next) + { + // Check if the weakreference has expired. + V? value; + if (_entries[entry]._weakValue.TryGetTarget(out value)) + estimatedNumLiveEntries++; + } + } + double estimatedLivePercentage = ((double)estimatedNumLiveEntries) / ((double)(_entries.Length)); + int newSize; + if (estimatedLivePercentage < _growThreshold && (_entries.Length - estimatedNumLiveEntries) > _initialCapacity) + { + newSize = _buckets.Length; + } + else + { + newSize = HashHelpers.GetPrime(_buckets.Length * 2); +#if DEBUG + newSize = _buckets.Length + 3; +#endif + if (newSize <= _nextFreeEntry) + throw new OutOfMemoryException(); + } + Entry[] newEntries = new Entry[newSize]; + int[] newBuckets = new int[newSize]; + for (int i = 0; i < newSize; i++) + newBuckets[i] = -1; + + // Note that we walk the bucket chains rather than iterating over _entries. This is because we allow for the possibility + // of abandoned entries (with undefined contents) if a thread is killed between allocating an entry and linking it onto the + // bucket chain. + int newNextFreeEntry = 0; + for (int bucket = 0; bucket < _buckets.Length; bucket++) + { + for (int entry = _buckets[bucket]; entry != -1; entry = _entries[entry]._next) + { + // Check if the weakreference has expired. If so, this is where we drop the entry altogether. + V? value; + if (_entries[entry]._weakValue.TryGetTarget(out value)) + { + newEntries[newNextFreeEntry]._key = _entries[entry]._key; + newEntries[newNextFreeEntry]._weakValue = _entries[entry]._weakValue; + newEntries[newNextFreeEntry]._hashCode = _entries[entry]._hashCode; + int newBucket = ComputeBucket(newEntries[newNextFreeEntry]._hashCode, newSize); + newEntries[newNextFreeEntry]._next = newBuckets[newBucket]; + newBuckets[newBucket] = newNextFreeEntry; + newNextFreeEntry++; + } + } + } + + // The assertion is "<=" rather than "==" because we allow an entry to "leak" until the next resize if + // a thread died between the time between we allocated the entry and the time we link it into the bucket stack. + // In addition, we don't bother copying entries where the weak reference has expired. + Debug.Assert(newNextFreeEntry <= _nextFreeEntry); + + // The line that atomically installs the resize. If this thread is killed before this point, + // the table remains full and the next guy attempting an add will have to redo the resize. + _owner._container = new Container(_owner, newBuckets, newEntries, newNextFreeEntry); + + _owner._container.VerifyUnifierConsistency(); + } + + private static int ComputeBucket(int hashCode, int numBuckets) + { + int bucket = (hashCode & 0x7fffffff) % numBuckets; + return bucket; + } + + [Conditional("DEBUG")] + public void VerifyUnifierConsistency() + { +#if DEBUG + // There's a point at which this check becomes gluttonous, even by checked build standards... + if (_nextFreeEntry >= 5000 || (0 != (_nextFreeEntry % 100))) + return; + + Debug.Assert(_owner._lock.IsAcquired); + Debug.Assert(_nextFreeEntry >= 0 && _nextFreeEntry <= _entries.Length); + int numEntriesEncountered = 0; + for (int bucket = 0; bucket < _buckets.Length; bucket++) + { + int walk1 = _buckets[bucket]; + int walk2 = _buckets[bucket]; // walk2 advances two elements at a time - if walk1 ever meets walk2, we've detected a cycle. + while (walk1 != -1) + { + numEntriesEncountered++; + Debug.Assert(walk1 >= 0 && walk1 < _nextFreeEntry); + Debug.Assert(walk2 >= -1 && walk2 < _nextFreeEntry); + Debug.Assert(_entries[walk1]._key != null); + int hashCode = _entries[walk1]._key.GetHashCode(); + Debug.Assert(hashCode == _entries[walk1]._hashCode); + Debug.Assert(_entries[walk1]._weakValue != null); + int storedBucket = ComputeBucket(_entries[walk1]._hashCode, _buckets.Length); + Debug.Assert(storedBucket == bucket); + walk1 = _entries[walk1]._next; + if (walk2 != -1) + walk2 = _entries[walk2]._next; + if (walk2 != -1) + walk2 = _entries[walk2]._next; + if (walk1 == walk2 && walk2 != -1) + Debug.Fail("Bucket " + bucket + " has a cycle in its linked list."); + } + } + // The assertion is "<=" rather than "==" because we allow an entry to "leak" until the next resize if + // a thread died between the time between we allocated the entry and the time we link it into the bucket stack. + Debug.Assert(numEntriesEncountered <= _nextFreeEntry); +#endif //DEBUG + } + + private readonly int[] _buckets; + private readonly Entry[] _entries; + private int _nextFreeEntry; + + private readonly ConcurrentUnifierW _owner; + + private const int _initialCapacity = 5; + private const double _growThreshold = 0.75; + } + + private struct Entry + { + public K _key; + public volatile WeakReference _weakValue; + public int _hashCode; + public int _next; + } + } +} diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Concurrent/ConcurrentUnifierWKeyed.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Concurrent/ConcurrentUnifierWKeyed.cs new file mode 100644 index 00000000000000..c85567e76c4075 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Collections/Concurrent/ConcurrentUnifierWKeyed.cs @@ -0,0 +1,390 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; + +namespace System.Collections.Concurrent +{ + // Abstract base for a thread-safe dictionary mapping a set of keys (K) to values (V). + // + // This flavor of ConcurrentUnifier holds values using weak references. It does not store the keys directly. Instead, values are + // required to contain the key and expose it via IKeyedItem. This flavor should be used in situations where the keys themselves + // could store direct or indirect references to the value (thus, preventing the value's from being GC'd if the table were to + // store the keys directly.) + // + // Value immortality is guaranteed. Once entered into the dictionary, the value never expires + // in an observable way as long as values don't have finalizers. + // + // To create an actual dictionary, subclass this type and override the protected Factory method + // to instantiate values (V) for the "Add" case. + // + // The key must be of a type that implements IEquatable. The unifier calls IEquality.Equals() + // and Object.GetHashCode() on the keys. + // + // The value must be a reference type that implements IKeyedItem. The unifier invokes the + // IKeyedItem.PrepareKey() method (outside the lock) on any value returned by the factory. This gives the value + // a chance to do any lazy evaluation of the keys while it's safe to do so. + // + // Deadlock risks: + // - Keys may be tested for equality and asked to compute their hashcode while the unifier + // holds its lock. Thus these operations must be written carefully to avoid deadlocks and + // reentrancy in to the table. + // + // - Values may get their IKeyedItem.Key property called while the unifier holds its lock. + // Values that need to do lazy evaluation to compute their keys should do that in the PrepareKey() + // method which the unifier promises to call outside the lock prior to entering the value into the table. + // + // - The Factory method will never be called inside the unifier lock. If two threads race to + // enter a value for the same key, the Factory() may get invoked twice for the same key - one + // of them will "win" the race and its result entered into the dictionary - other gets thrown away. + // + // Notes: + // - This class is used to look up types when GetType() or typeof() is invoked. + // That means that this class itself cannot do or call anything that does these + // things. + // + // - For this reason, it chooses not to mimic the official ConcurrentDictionary class + // (I don't even want to risk using delegates.) Even the LowLevel versions of these + // general utility classes may not be low-level enough for this class's purpose. + // + // Thread safety guarantees: + // + // ConcurrentUnifier is fully thread-safe and requires no + // additional locking to be done by callers. + // + // Performance characteristics: + // + // ConcurrentUnifier will not block a reader, even while + // the table is being written. Only one writer is allowed at a time; + // ConcurrentUnifier handles the synchronization that ensures this. + // + // Safety for concurrent readers is ensured as follows: + // + // Each hash bucket is maintained as a stack. Inserts are done under + // a lock in one of two ways: + // + // - The entry is filled out completely, then "published" by a + // single write to the top of the bucket. This ensures that a reader + // will see a valid snapshot of the bucket, once it has read the head. + // + // - An expired WeakReference inside an existing entry is replaced atomically + // by a new WeakReference. A reader will either see the old expired WeakReference + // (if so, he'll wait for the current lock to be released then do the locked retry) + // or the new WeakReference (which is fine for him to see.)) + // + // On resize, we allocate an entirely new table, rather than resizing + // in place. We fill in the new table completely, under the lock, + // then "publish" it with a single write. Any reader that races with + // this will either see the old table or the new one; each will contain + // the same data. + // + internal abstract class ConcurrentUnifierWKeyed + where K : IEquatable + where V : class, IKeyedItem + { + protected ConcurrentUnifierWKeyed() + { + _lock = new Lock(); + _container = new Container(this); + } + + // + // Retrieve the *unique* value for a given key. If the key was previously not entered into the dictionary, + // this method invokes the overridable Factory() method to create the new value. The Factory() method is + // invoked outside of any locks. If two threads race to enter a value for the same key, the Factory() + // may get invoked twice for the same key - one of them will "win" the race and its result entered into the + // dictionary - other gets thrown away. + // + public V GetOrAdd(K key) + { + Debug.Assert(key != null); + Debug.Assert(!_lock.IsAcquired, "GetOrAdd called while lock already acquired. A possible cause of this is an Equals or GetHashCode method that causes reentrancy in the table."); + + int hashCode = key.GetHashCode(); + V value; + bool found = _container.TryGetValue(key, hashCode, out value); +#if DEBUG + { + V checkedValue; + bool checkedFound; + // In debug builds, always exercise a locked TryGet (this is a good way to detect deadlock/reentrancy through Equals/GetHashCode()). + using (LockHolder.Hold(_lock)) + { + _container.VerifyUnifierConsistency(); + int h = key.GetHashCode(); + checkedFound = _container.TryGetValue(key, h, out checkedValue); + } + + if (found) + { + // Since this DEBUG code is holding a strong reference to "value", state of a key must never go from found to not found, + // and only one value may exist per key. + Debug.Assert(checkedFound); + Debug.Assert(object.ReferenceEquals(checkedValue, value)); + GC.KeepAlive(value); + } + } +#endif //DEBUG + if (found) + return value; + + value = this.Factory(key); + + // This doesn't catch every object that has a finalizer, but the old saying about half a loaf... + Debug.Assert(!(value is IDisposable), + "Values placed in this table should not have finalizers. ConcurrentUnifiers guarantee observational immortality only " + + "in the absence of finalizers. Or to speak more plainly, we can use WeakReferences to guarantee observational immortality " + + "without paying the cost of storage immortality."); + + if (value == null) + { + // There's no point in caching null's in the dictionary as a WeakReference of null will always show up as expired + // and force a re-add every time. Just return the null value without storing it. This does mean that repeated look ups + // for this case will be very slow - this generally corresponds to scenarios like looking for a type member that doesn't + // exist so hopefully, it's better to have awful throughput for such cases rather than polluting the dictionary with + // "null entries" that have to be special-cased for everyone. + return null; + } + + // While still outside the lock, invoke the value's PrepareKey method to give the chance to do any lazy evaluation + // it needs to produce the key quickly and in a deadlock-free manner once we're inside the lock. + value.PrepareKey(); + + using (LockHolder.Hold(_lock)) + { + V heyIWasHereFirst; + if (_container.TryGetValue(key, hashCode, out heyIWasHereFirst)) + return heyIWasHereFirst; + if (!_container.HasCapacity) + _container.Resize(); // This overwrites the _container field. + _container.Add(key, hashCode, value); + return value; + } + } + + protected abstract V Factory(K key); + + private volatile Container _container; + private readonly Lock _lock; + + private sealed class Container + { + public Container(ConcurrentUnifierWKeyed owner) + { + // Note: This could be done by calling Resize()'s logic but we cannot safely do that as this code path is reached + // during class construction time and Resize() pulls in enough stuff that we get cyclic cctor warnings from the build. + _buckets = new int[_initialCapacity]; + for (int i = 0; i < _initialCapacity; i++) + _buckets[i] = -1; + _entries = new Entry[_initialCapacity]; + _nextFreeEntry = 0; + _owner = owner; + } + + private Container(ConcurrentUnifierWKeyed owner, int[] buckets, Entry[] entries, int nextFreeEntry) + { + _buckets = buckets; + _entries = entries; + _nextFreeEntry = nextFreeEntry; + _owner = owner; + } + + public bool TryGetValue(K key, int hashCode, out V value) + { + // Lock acquistion NOT required. + + int bucket = ComputeBucket(hashCode, _buckets.Length); + int i = Volatile.Read(ref _buckets[bucket]); + while (i != -1) + { + V? actualValue; + if (hashCode == _entries[i]._hashCode && _entries[i]._weakValue.TryGetTarget(out actualValue)) + { + K actualKey = actualValue.Key; + if (key.Equals(actualKey)) + { + value = actualValue; + return true; + } + } + i = _entries[i]._next; + } + + value = default(V); + return false; + } + + public void Add(K key, int hashCode, V value) + { + Debug.Assert(_owner._lock.IsAcquired); + + int bucket = ComputeBucket(hashCode, _buckets.Length); + int newEntryIdx = _nextFreeEntry; + _entries[newEntryIdx]._weakValue = new WeakReference(value, trackResurrection: false); + _entries[newEntryIdx]._hashCode = hashCode; + _entries[newEntryIdx]._next = _buckets[bucket]; + + _nextFreeEntry++; + + // The line that atomically adds the new key/value pair. If the thread is killed before this line executes but after + // we've incremented _nextFreeEntry, this entry is harmlessly leaked until the next resize. + Volatile.Write(ref _buckets[bucket], newEntryIdx); + + VerifyUnifierConsistency(); + } + + public bool HasCapacity + { + get + { + Debug.Assert(_owner._lock.IsAcquired); + return _nextFreeEntry != _entries.Length; + } + } + + public void Resize() + { + Debug.Assert(_owner._lock.IsAcquired); + + // Before we actually grow the size of the table, figure out how much we can recover just by dropping entries with + // expired weak references. + int estimatedNumLiveEntries = 0; + for (int bucket = 0; bucket < _buckets.Length; bucket++) + { + for (int entry = _buckets[bucket]; entry != -1; entry = _entries[entry]._next) + { + // Check if the weakreference has expired. + V? value; + if (_entries[entry]._weakValue.TryGetTarget(out value)) + estimatedNumLiveEntries++; + } + } + double estimatedLivePercentage = ((double)estimatedNumLiveEntries) / ((double)(_entries.Length)); + int newSize; + if (estimatedLivePercentage < _growThreshold && (_entries.Length - estimatedNumLiveEntries) > _initialCapacity) + { + newSize = _buckets.Length; + } + else + { + newSize = HashHelpers.GetPrime(_buckets.Length * 2); +#if DEBUG + newSize = _buckets.Length + 3; +#endif + if (newSize <= _nextFreeEntry) + throw new OutOfMemoryException(); + } + Entry[] newEntries = new Entry[newSize]; + int[] newBuckets = new int[newSize]; + for (int i = 0; i < newSize; i++) + newBuckets[i] = -1; + + // Note that we walk the bucket chains rather than iterating over _entries. This is because we allow for the possibility + // of abandoned entries (with undefined contents) if a thread is killed between allocating an entry and linking it onto the + // bucket chain. + int newNextFreeEntry = 0; + for (int bucket = 0; bucket < _buckets.Length; bucket++) + { + for (int entry = _buckets[bucket]; entry != -1; entry = _entries[entry]._next) + { + // Check if the weakreference has expired. If so, this is where we drop the entry altogether. + V? value; + if (_entries[entry]._weakValue.TryGetTarget(out value)) + { + newEntries[newNextFreeEntry]._weakValue = _entries[entry]._weakValue; + newEntries[newNextFreeEntry]._hashCode = _entries[entry]._hashCode; + int newBucket = ComputeBucket(newEntries[newNextFreeEntry]._hashCode, newSize); + newEntries[newNextFreeEntry]._next = newBuckets[newBucket]; + newBuckets[newBucket] = newNextFreeEntry; + newNextFreeEntry++; + } + } + } + + // The assertion is "<=" rather than "==" because we allow an entry to "leak" until the next resize if + // a thread died between the time between we allocated the entry and the time we link it into the bucket stack. + // In addition, we don't bother copying entries where the weak reference has expired. + Debug.Assert(newNextFreeEntry <= _nextFreeEntry); + + // The line that atomically installs the resize. If this thread is killed before this point, + // the table remains full and the next guy attempting an add will have to redo the resize. + _owner._container = new Container(_owner, newBuckets, newEntries, newNextFreeEntry); + + _owner._container.VerifyUnifierConsistency(); + } + + private static int ComputeBucket(int hashCode, int numBuckets) + { + int bucket = (hashCode & 0x7fffffff) % numBuckets; + return bucket; + } + + [Conditional("DEBUG")] + public void VerifyUnifierConsistency() + { +#if DEBUG + // There's a point at which this check becomes gluttonous, even by checked build standards... + if (_nextFreeEntry >= 5000 && (0 != (_nextFreeEntry % 100))) + return; + + Debug.Assert(_owner._lock.IsAcquired); + Debug.Assert(_nextFreeEntry >= 0 && _nextFreeEntry <= _entries.Length); + int numEntriesEncountered = 0; + for (int bucket = 0; bucket < _buckets.Length; bucket++) + { + int walk1 = _buckets[bucket]; + int walk2 = _buckets[bucket]; // walk2 advances two elements at a time - if walk1 ever meets walk2, we've detected a cycle. + while (walk1 != -1) + { + numEntriesEncountered++; + Debug.Assert(walk1 >= 0 && walk1 < _nextFreeEntry); + Debug.Assert(walk2 >= -1 && walk2 < _nextFreeEntry); + Debug.Assert(_entries[walk1]._weakValue != null); + V? value; + if (_entries[walk1]._weakValue.TryGetTarget(out value)) + { + K key = value.Key; + Debug.Assert(key != null); + int hashCode = key.GetHashCode(); + Debug.Assert(hashCode == _entries[walk1]._hashCode); + } + + int storedBucket = ComputeBucket(_entries[walk1]._hashCode, _buckets.Length); + Debug.Assert(storedBucket == bucket); + walk1 = _entries[walk1]._next; + if (walk2 != -1) + walk2 = _entries[walk2]._next; + if (walk2 != -1) + walk2 = _entries[walk2]._next; + if (walk1 == walk2 && walk2 != -1) + Debug.Fail("Bucket " + bucket + " has a cycle in its linked list."); + } + } + // The assertion is "<=" rather than "==" because we allow an entry to "leak" until the next resize if + // a thread died between the time between we allocated the entry and the time we link it into the bucket stack. + Debug.Assert(numEntriesEncountered <= _nextFreeEntry); +#endif //DEBUG + } + + private readonly int[] _buckets; + private readonly Entry[] _entries; + private int _nextFreeEntry; + + private readonly ConcurrentUnifierWKeyed _owner; + + private const int _initialCapacity = 5; + private const double _growThreshold = 0.75; + } + + private struct Entry + { + public WeakReference _weakValue; + public int _hashCode; + public int _next; + } + } +} diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Concurrent/IKeyedItem.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Concurrent/IKeyedItem.cs new file mode 100644 index 00000000000000..ea43941b6f2ec5 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Collections/Concurrent/IKeyedItem.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; + +namespace System.Collections.Concurrent +{ + // + // Objects that want to be used as values in a keyed ConcurrentUnifier need to implement this interface. + // Keyed items are values that contain their own keys and can produce them on demand. + // + internal interface IKeyedItem where K : IEquatable + { + // + // This method is the keyed item's chance to do any lazy evaluation needed to produce the key quickly. + // Concurrent unifiers are guaranteed to invoke this method at least once and wait for it + // to complete before invoking the Key property. The unifier lock is NOT held across the call. + // + // PrepareKey() must be idempodent and thread-safe. It may be invoked multiple times and concurrently. + // + void PrepareKey(); + + // + // Produce the key. This is a high-traffic property and is called while the hash table's lock is held. Thus, it should + // return a precomputed stored value and refrain from invoking other methods. If the keyed item wishes to + // do lazy evaluation of the key, it should do so in the PrepareKey() method. + // + K Key { get; } + } +} diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Generic/Empty.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/Empty.cs new file mode 100644 index 00000000000000..bef0bfde53d334 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/Empty.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; + +namespace System.Collections.Generic +{ + // + // Helper class to store reusable empty IEnumerables. + // + internal static class Empty + { + // + // Returns a reusable empty IEnumerable (that does not secretly implement more advanced collection interfaces.) + // + public static IEnumerable Enumerable + { + get + { + return _enumerable; + } + } + + private sealed class EmptyEnumImpl : IEnumerable, IEnumerator + { + public IEnumerator GetEnumerator() + { + return this; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this; + } + + public T Current + { + get { throw new InvalidOperationException(); } + } + + object IEnumerator.Current + { + get { throw new InvalidOperationException(); } + } + + public bool MoveNext() + { + return false; + } + + public void Reset() + { + } + + public void Dispose() + { + } + } + + private static IEnumerable _enumerable = new EmptyEnumImpl(); + } +} diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Generic/EnumerableExtensions.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/EnumerableExtensions.cs new file mode 100644 index 00000000000000..9e8fb9790b3b6d --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/EnumerableExtensions.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +namespace System.Collections.Generic +{ + internal static class EnumerableExtensions + { + // Used to prevent returning values out of IEnumerable<>-typed properties + // that an untrusted caller could cast back to array or List. + public static IEnumerable AsNothingButIEnumerable(this IEnumerable en) + { + foreach (T t in en) + yield return t; + } + } +} diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelDictionary.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelDictionary.cs new file mode 100644 index 00000000000000..cd98bef0d7d8bc --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelDictionary.cs @@ -0,0 +1,316 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace System.Collections.Generic +{ + /*============================================================ + ** + ** Class: LowLevelDictionary + ** + ** Private version of Dictionary<> for internal System.Private.CoreLib use. This + ** permits sharing more source between BCL and System.Private.CoreLib (as well as the + ** fact that Dictionary<> is just a useful class in general.) + ** + ** This does not strive to implement the full api surface area + ** (but any portion it does implement should match the real Dictionary<>'s + ** behavior.) + ** + ===========================================================*/ +#if TYPE_LOADER_IMPLEMENTATION + [System.Runtime.CompilerServices.ForceDictionaryLookups] +#endif + internal class LowLevelDictionary where TKey : IEquatable + { + private const int DefaultSize = 17; + + public LowLevelDictionary() + : this(DefaultSize) + { + } + + public LowLevelDictionary(int capacity) + { + Clear(capacity); + } + + public int Count + { + get + { + return _numEntries; + } + } + + public TValue this[TKey key] + { + get + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + Entry entry = Find(key); + if (entry == null) + throw new KeyNotFoundException(); + return entry.m_value; + } + set + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _version++; + Entry entry = Find(key); + if (entry != null) + entry.m_value = value; + else + UncheckedAdd(key, value); + } + } + + public bool TryGetValue(TKey key, out TValue? value) + { + value = default(TValue); + if (key == null) + throw new ArgumentNullException(nameof(key)); + Entry entry = Find(key); + if (entry != null) + { + value = entry.m_value; + return true; + } + return false; + } + + public void Add(TKey key, TValue value) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + Entry entry = Find(key); + if (entry != null) + throw new ArgumentException(SR.Format(SR.Argument_AddingDuplicate, key)); + _version++; + UncheckedAdd(key, value); + } + + public void Clear(int capacity = DefaultSize) + { + _version++; + _buckets = new Entry[capacity]; + _numEntries = 0; + } + + public bool Remove(TKey key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + int bucket = GetBucket(key); + Entry? prev = null; + Entry? entry = _buckets[bucket]; + while (entry != null) + { + if (key.Equals(entry.m_key)) + { + if (prev == null) + { + _buckets[bucket] = entry.m_next; + } + else + { + prev.m_next = entry.m_next; + } + _version++; + _numEntries--; + return true; + } + + prev = entry; + entry = entry.m_next; + } + return false; + } + + internal TValue LookupOrAdd(TKey key, TValue value) + { + Entry entry = Find(key); + if (entry != null) + return entry.m_value; + UncheckedAdd(key, value); + return value; + } + + private Entry Find(TKey key) + { + int bucket = GetBucket(key); + Entry? entry = _buckets[bucket]; + while (entry != null) + { + if (key.Equals(entry.m_key)) + return entry; + + entry = entry.m_next; + } + return null; + } + + private Entry UncheckedAdd(TKey key, TValue value) + { + Entry entry = new Entry(); + entry.m_key = key; + entry.m_value = value; + + int bucket = GetBucket(key); + entry.m_next = _buckets[bucket]; + _buckets[bucket] = entry; + + _numEntries++; + if (_numEntries > (_buckets.Length * 2)) + ExpandBuckets(); + + return entry; + } + + + private void ExpandBuckets() + { + try + { + int newNumBuckets = _buckets.Length * 2 + 1; + Entry[] newBuckets = new Entry[newNumBuckets]; + for (int i = 0; i < _buckets.Length; i++) + { + Entry? entry = _buckets[i]; + while (entry != null) + { + Entry? nextEntry = entry.m_next; + + int bucket = GetBucket(entry.m_key, newNumBuckets); + entry.m_next = newBuckets[bucket]; + newBuckets[bucket] = entry; + + entry = nextEntry; + } + } + _buckets = newBuckets; + } + catch (OutOfMemoryException) + { + } + } + + private int GetBucket(TKey key, int numBuckets = 0) + { + int h = key.GetHashCode(); + h &= 0x7fffffff; + return (h % (numBuckets == 0 ? _buckets.Length : numBuckets)); + } + + +#if TYPE_LOADER_IMPLEMENTATION + [System.Runtime.CompilerServices.ForceDictionaryLookups] +#endif + private sealed class Entry + { + public TKey m_key; + public TValue m_value; + public Entry? m_next; + } + + private Entry?[] _buckets; + private int _numEntries; + private int _version; + +#if TYPE_LOADER_IMPLEMENTATION + [System.Runtime.CompilerServices.ForceDictionaryLookups] +#endif + protected sealed class LowLevelDictEnumerator : IEnumerator> + { + public LowLevelDictEnumerator(LowLevelDictionary dict) + { + _dict = dict; + _version = _dict._version; + Entry[] entries = new Entry[_dict._numEntries]; + int dst = 0; + for (int bucket = 0; bucket < _dict._buckets.Length; bucket++) + { + Entry? entry = _dict._buckets[bucket]; + while (entry != null) + { + entries[dst++] = entry; + entry = entry.m_next; + } + } + _entries = entries; + Reset(); + } + + public KeyValuePair Current + { + get + { + if (_version != _dict._version) + throw new InvalidOperationException("InvalidOperation_EnumFailedVersion"); + if (_curPosition == -1 || _curPosition == _entries.Length) + throw new InvalidOperationException("InvalidOperation_EnumOpCantHappen"); + Entry entry = _entries[_curPosition]; + return new KeyValuePair(entry.m_key, entry.m_value); + } + } + + public void Dispose() + { + } + + public bool MoveNext() + { + if (_version != _dict._version) + throw new InvalidOperationException("InvalidOperation_EnumFailedVersion"); + if (_curPosition != _entries.Length) + _curPosition++; + bool anyMore = (_curPosition != _entries.Length); + return anyMore; + } + + object IEnumerator.Current + { + get + { + KeyValuePair kv = Current; + return kv; + } + } + + public void Reset() + { + if (_version != _dict._version) + throw new InvalidOperationException("InvalidOperation_EnumFailedVersion"); + _curPosition = -1; + } + + private LowLevelDictionary _dict; + private Entry[] _entries; + private int _curPosition; + private int _version; + } + } + + /// + /// LowLevelDictionary when enumeration is needed + /// + internal sealed class LowLevelDictionaryWithIEnumerable : LowLevelDictionary, IEnumerable> where TKey : IEquatable + { + public IEnumerator> GetEnumerator() + { + return new LowLevelDictEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + IEnumerator> ie = GetEnumerator(); + return ie; + } + } +} diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs new file mode 100644 index 00000000000000..9909556b183305 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs @@ -0,0 +1,577 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================ +** +** +** Private version of List for internal System.Private.CoreLib use. This +** permits sharing more source between BCL and System.Private.CoreLib (as well as the +** fact that List is just a useful class in general.) +** +** This does not strive to implement the full api surface area +** (but any portion it does implement should match the real List's +** behavior.) +** +** This file is a subset of System.Collections\System\Collections\Generics\List.cs +** and should be kept in sync with that file. +** +===========================================================*/ + +using System; +using System.Diagnostics; + +namespace System.Collections.Generic +{ + // Implements a variable-size List that uses an array of objects to store the + // elements. A List has a capacity, which is the allocated length + // of the internal array. As elements are added to a List, the capacity + // of the List is automatically increased as required by reallocating the + // internal array. + // + // LowLevelList with no interface implementation minimizes both code and data size. + // Data size is smaller because there will be minimal virtual function table. + // Code size is smaller because only functions called will be in the binary. + // Use LowLevelListWithIList for IList support + [DebuggerDisplay("Count = {Count}")] +#if TYPE_LOADER_IMPLEMENTATION + [System.Runtime.CompilerServices.ForceDictionaryLookups] +#endif + internal class LowLevelList + { + private const int _defaultCapacity = 4; + + protected T?[] _items; + protected int _size; + protected int _version; + +#pragma warning disable CA1825 // avoid the extra generic instantiation for Array.Empty() + private static readonly T[] s_emptyArray = new T[0]; +#pragma warning restore CA1825 + + // Constructs a List. The list is initially empty and has a capacity + // of zero. Upon adding the first element to the list the capacity is + // increased to 4, and then increased in multiples of two as required. + public LowLevelList() + { + _items = s_emptyArray; + } + + // Constructs a List with a given initial capacity. The list is + // initially empty, but will have room for the given number of elements + // before any reallocations are required. + // + public LowLevelList(int capacity) + { + if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); + + if (capacity == 0) + _items = s_emptyArray; + else + _items = new T[capacity]; + } + + // Constructs a List, copying the contents of the given collection. The + // size and capacity of the new list will both be equal to the size of the + // given collection. + // + public LowLevelList(IEnumerable collection) + { + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + + ICollection? c = collection as ICollection; + if (c != null) + { + int count = c.Count; + if (count == 0) + { + _items = s_emptyArray; + } + else + { + _items = new T[count]; + c.CopyTo(_items, 0); + _size = count; + } + } + else + { + _size = 0; + _items = s_emptyArray; + // This enumerable could be empty. Let Add allocate a new array, if needed. + // Note it will also go to _defaultCapacity first, not 1, then 2, etc. + + using (IEnumerator en = collection.GetEnumerator()) + { + while (en.MoveNext()) + { + Add(en.Current); + } + } + } + } + + // Gets and sets the capacity of this list. The capacity is the size of + // the internal array used to hold items. When set, the internal + // array of the list is reallocated to the given capacity. + // + public int Capacity + { + get + { + return _items.Length; + } + set + { + if (value < _size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + if (value != _items.Length) + { + if (value > 0) + { + T[] newItems = new T[value]; + Array.Copy(_items, 0, newItems, 0, _size); + _items = newItems; + } + else + { + _items = s_emptyArray; + } + } + } + } + + // Read-only property describing how many elements are in the List. + public int Count + { + get + { + return _size; + } + } + + // Sets or Gets the element at the given index. + // + public T this[int index] + { + get + { + // Following trick can reduce the range check by one + if ((uint)index >= (uint)_size) + { + throw new ArgumentOutOfRangeException(); + } + return _items[index]; + } + + set + { + if ((uint)index >= (uint)_size) + { + throw new ArgumentOutOfRangeException(); + } + _items[index] = value; + _version++; + } + } + + + // Adds the given object to the end of this list. The size of the list is + // increased by one. If required, the capacity of the list is doubled + // before adding the new element. + // + public void Add(T item) + { + if (_size == _items.Length) EnsureCapacity(_size + 1); + _items[_size++] = item; + _version++; + } + + // Ensures that the capacity of this list is at least the given minimum + // value. If the currect capacity of the list is less than min, the + // capacity is increased to twice the current capacity or to min, + // whichever is larger. + private void EnsureCapacity(int min) + { + if (_items.Length < min) + { + int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Length * 2; + // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + //if ((uint)newCapacity > Array.MaxLength) newCapacity = Array.MaxLength; + if (newCapacity < min) newCapacity = min; + Capacity = newCapacity; + } + } + +#if !TYPE_LOADER_IMPLEMENTATION + // Adds the elements of the given collection to the end of this list. If + // required, the capacity of the list is increased to twice the previous + // capacity or the new size, whichever is larger. + // + public void AddRange(IEnumerable collection) + { + + InsertRange(_size, collection); + } + + // Clears the contents of List. + public void Clear() + { + if (_size > 0) + { + Array.Clear(_items, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references. + _size = 0; + } + _version++; + } + + // Contains returns true if the specified element is in the List. + // It does a linear, O(n) search. Equality is determined by calling + // item.Equals(). + // + public bool Contains(T item) + { + if ((object?)item == null) + { + for (int i = 0; i < _size; i++) + if ((object?)_items[i] == null) + return true; + return false; + } + else + { + int index = IndexOf(item); + if (index >= 0) + return true; + return false; + } + } + + + // Copies a section of this list to the given array at the given index. + // + // The method uses the Array.Copy method to copy the elements. + // + public void CopyTo(int index, T[] array, int arrayIndex, int count) + { + if (_size - index < count) + { + throw new ArgumentException(); + } + + // Delegate rest of error checking to Array.Copy. + Array.Copy(_items, index, array, arrayIndex, count); + } + + public void CopyTo(T[] array, int arrayIndex) + { + // Delegate rest of error checking to Array.Copy. + Array.Copy(_items, 0, array, arrayIndex, _size); + } + + // Returns the index of the first occurrence of a given value in a range of + // this list. The list is searched forwards from beginning to end. + // The elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.IndexOf method to perform the + // search. + // + public int IndexOf(T item) + { + return Array.IndexOf(_items, item, 0, _size); + } + + + // Returns the index of the first occurrence of a given value in a range of + // this list. The list is searched forwards, starting at index + // index and ending at count number of elements. The + // elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.IndexOf method to perform the + // search. + // + public int IndexOf(T item, int index) + { + if (index > _size) + throw new ArgumentOutOfRangeException(nameof(index)); + return Array.IndexOf(_items, item, index, _size - index); + } + + // Inserts an element into this list at a given index. The size of the list + // is increased by one. If required, the capacity of the list is doubled + // before inserting the new element. + // + public void Insert(int index, T item) + { + // Note that insertions at the end are legal. + if ((uint)index > (uint)_size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + if (_size == _items.Length) EnsureCapacity(_size + 1); + if (index < _size) + { + Array.Copy(_items, index, _items, index + 1, _size - index); + } + _items[index] = item; + _size++; + _version++; + } + + // Inserts the elements of the given collection at a given index. If + // required, the capacity of the list is increased to twice the previous + // capacity or the new size, whichever is larger. Ranges may be added + // to the end of the list by setting index to the List's size. + // + public void InsertRange(int index, IEnumerable collection) + { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + + if ((uint)index > (uint)_size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + ICollection? c = collection as ICollection; + if (c != null) + { // if collection is ICollection + int count = c.Count; + if (count > 0) + { + EnsureCapacity(_size + count); + if (index < _size) + { + Array.Copy(_items, index, _items, index + count, _size - index); + } + + // If we're inserting a List into itself, we want to be able to deal with that. + if (this == c) + { + // Copy first part of _items to insert location + Array.Copy(_items, 0, _items, index, index); + // Copy last part of _items back to inserted location + Array.Copy(_items, index + count, _items, index * 2, _size - index); + } + else + { + T[] itemsToInsert = new T[count]; + c.CopyTo(itemsToInsert, 0); + Array.Copy(itemsToInsert, 0, _items, index, count); + } + _size += count; + } + } + else + { + using (IEnumerator en = collection.GetEnumerator()) + { + while (en.MoveNext()) + { + Insert(index++, en.Current); + } + } + } + _version++; + } + + // Removes the element at the given index. The size of the list is + // decreased by one. + // + public bool Remove(T item) + { + int index = IndexOf(item); + if (index >= 0) + { + RemoveAt(index); + return true; + } + + return false; + } + + // This method removes all items which matches the predicate. + // The complexity is O(n). + public int RemoveAll(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + + int freeIndex = 0; // the first free slot in items array + + // Find the first item which needs to be removed. + while (freeIndex < _size && !match(_items[freeIndex]!)) freeIndex++; + if (freeIndex >= _size) return 0; + + int current = freeIndex + 1; + while (current < _size) + { + // Find the first item which needs to be kept. + while (current < _size && match(_items[current]!)) current++; + + if (current < _size) + { + // copy item to the free slot. + _items[freeIndex++] = _items[current++]; + } + } + + Array.Clear(_items, freeIndex, _size - freeIndex); + int result = _size - freeIndex; + _size = freeIndex; + _version++; + return result; + } + + // Removes the element at the given index. The size of the list is + // decreased by one. + // + public void RemoveAt(int index) + { + if ((uint)index >= (uint)_size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + _size--; + if (index < _size) + { + Array.Copy(_items, index + 1, _items, index, _size - index); + } + _items[_size] = default(T); + _version++; + } + + // ToArray returns a new Object array containing the contents of the List. + // This requires copying the List, which is an O(n) operation. + public T[] ToArray() + { + T[] array = new T[_size]; + Array.Copy(_items, 0, array, 0, _size); + return array; + } +#endif + } + +#if !TYPE_LOADER_IMPLEMENTATION + // LowLevelList with full IList implementation + internal sealed class LowLevelListWithIList : LowLevelList, IList + { + public LowLevelListWithIList() + { + } + + public LowLevelListWithIList(int capacity) + : base(capacity) + { + } + + public LowLevelListWithIList(IEnumerable collection) + : base(collection) + { + } + + // Is this List read-only? + bool ICollection.IsReadOnly + { + get { return false; } + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + + private struct Enumerator : IEnumerator, System.Collections.IEnumerator + { + private LowLevelListWithIList _list; + private int _index; + private int _version; + private T? _current; + + internal Enumerator(LowLevelListWithIList list) + { + _list = list; + _index = 0; + _version = list._version; + _current = default(T); + } + + public void Dispose() + { + } + + public bool MoveNext() + { + LowLevelListWithIList localList = _list; + + if (_version == localList._version && ((uint)_index < (uint)localList._size)) + { + _current = localList._items[_index]; + _index++; + return true; + } + return MoveNextRare(); + } + + private bool MoveNextRare() + { + if (_version != _list._version) + { + throw new InvalidOperationException(); + } + + _index = _list._size + 1; + _current = default(T); + return false; + } + + public T Current + { + get + { + return _current!; + } + } + + object System.Collections.IEnumerator.Current + { + get + { + if (_index == 0 || _index == _list._size + 1) + { + throw new InvalidOperationException(); + } + return Current; + } + } + + void System.Collections.IEnumerator.Reset() + { + if (_version != _list._version) + { + throw new InvalidOperationException(); + } + + _index = 0; + _current = default(T); + } + } + } +#endif // !TYPE_LOADER_IMPLEMENTATION +} diff --git a/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceModeOnlyAttribute.cs b/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceModeOnlyAttribute.cs new file mode 100644 index 00000000000000..f7d6a1d315f848 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceModeOnlyAttribute.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace System.Runtime.CompilerServices +{ + // + // Attach to classes that contain code only used in ILC /BuildType:chk builds. + // + // Any class attributed with this must have the following properties: + // + // - Class must be declared "static" + // + // - All public/internal methods must have a return type of: + // + // void + // bool + // any non-value type + // + // - All fields must be private. + // + // - Class constructor must not have externally visible side effects. + // + // + // On /BuildType:ret builds, ILC will run a special transform that + // turns all of the public and internal method bodies into + // the equivalent of: + // + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // T Foo() + // { + // return default(T); + // } + // + // It also removes all fields and private methods (including the class constructor.) + // + // The method semantics must be defined so that ret builds have + // the desired behavior with these implementations. + // + // + [AttributeUsage(AttributeTargets.Class)] + internal sealed class DeveloperExperienceModeOnlyAttribute : Attribute + { + } +} diff --git a/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceState.cs b/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceState.cs new file mode 100644 index 00000000000000..f2e0d260b5d12a --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/DeveloperExperienceState.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace System.Runtime.CompilerServices +{ + [DeveloperExperienceModeOnly] + internal static class DeveloperExperienceState + { + public static bool DeveloperExperienceModeEnabled + { + get + { + return true; // ILC will rewrite to this "return false" if run with "/buildType:ret" + } + } + } +} diff --git a/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/__BlockAllReflectionAttribute.cs b/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/__BlockAllReflectionAttribute.cs new file mode 100644 index 00000000000000..7e560e3f5a21c4 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/__BlockAllReflectionAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/* + Providing a definition for __BlockAllReflectionAttribute in an assembly is a signal to the .NET Native toolchain + to remove the metadata for all APIs. This both reduces size and disables all reflection on those + APIs in libraries that include this. +*/ + +using System; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.All)] + internal class __BlockAllReflectionAttribute : Attribute { } +} diff --git a/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/__BlockReflectionAttribute.cs b/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/__BlockReflectionAttribute.cs new file mode 100644 index 00000000000000..aba2c806f7171f --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Runtime/CompilerServices/__BlockReflectionAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/* + Providing a definition for __BlockReflectionAttribute in an assembly is a signal to the .NET Native toolchain + to remove the metadata for all non-public APIs. This both reduces size and disables private reflection on those + APIs in libraries that include this. +*/ + +using System; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.All)] + internal class __BlockReflectionAttribute : Attribute { } +} diff --git a/src/coreclr/nativeaot/Directory.Build.props b/src/coreclr/nativeaot/Directory.Build.props new file mode 100644 index 00000000000000..8807dca0c847e7 --- /dev/null +++ b/src/coreclr/nativeaot/Directory.Build.props @@ -0,0 +1,120 @@ + + + + + false + + + + false + true + false + false + $(NetCoreAppCurrent) + + Portable + true + + $(RuntimeBinDir)/aotsdk/ + Debug;Release;Checked + x64;x86;arm;arm64 + + $(TargetArchitecture) + arm + true + + false + + true + v4.0.30319 + true + $(NoWarn),0419,0649,CA2249,CA1830 + + + $(NoWarn);CS8600;CS8601;CS8602;CS8603;CS8604;CS8610;CS8618;CS8620;CS8625;CS8632;CS8765 + + + $(NoWarn);CA1810;CA1823;CA1825;CA2208;SA1129;SA1205;SA1400;SA1517 + + + $(NoWarn);CS3016 + + + CORERT;NETCOREAPP + + true + + $(OutputPath)$(MSBuildProjectName).xml + + + $(IntermediateOutputPath)$(MSBuildProjectName).deps.json + + + + false + true + + + FEATURE_COMINTEROP;$(DefineConstants) + + + false + true + + + + + x64 + false + TARGET_64BIT;TARGET_AMD64;$(DefineConstants) + + + x86 + TARGET_32BIT;$(DefineConstants) + + + arm + TARGET_32BIT;TARGET_ARM;$(DefineConstants) + + + AnyCPU + TARGET_64BIT;TARGET_ARM64;$(DefineConstants) + + + + TARGET_WINDOWS;$(DefineConstants) + TARGET_UNIX;$(DefineConstants) + + + + + false + true + _LOGGING;DEBUG;$(DefineConstants) + + + true + + + + + $(AssemblyName) + + + + + true + SilverlightPlatform + + + + + $([MSBuild]::NormalizeDirectory('$(LibrariesProjectRoot)', 'Common', 'src')) + $(MSBuildThisFileDirectory)Common\src\ + $(MSBuildThisFileDirectory)..\tools\Common\ + + + + diff --git a/src/coreclr/nativeaot/Directory.Build.targets b/src/coreclr/nativeaot/Directory.Build.targets new file mode 100644 index 00000000000000..0572326e695aec --- /dev/null +++ b/src/coreclr/nativeaot/Directory.Build.targets @@ -0,0 +1,13 @@ + + + + + + + + false + + + + diff --git a/src/coreclr/nativeaot/Runtime.Base/src/AsmOffsets.cspp b/src/coreclr/nativeaot/Runtime.Base/src/AsmOffsets.cspp new file mode 100644 index 00000000000000..27d0035ae47d86 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/AsmOffsets.cspp @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#define PLAT_ASM_OFFSET(offset, cls, member) internal const int OFFSETOF__##cls##__##member = 0x##offset; +#define PLAT_ASM_SIZEOF(offset, cls) internal const int SIZEOF__##cls = 0x##offset; +#define PLAT_ASM_CONST(constant, expr) internal const int expr = 0x##constant; + +static class AsmOffsets +{ +#include "../../Runtime/AsmOffsets.h" +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/Internal/Runtime/CompilerServices/Unsafe.cs b/src/coreclr/nativeaot/Runtime.Base/src/Internal/Runtime/CompilerServices/Unsafe.cs new file mode 100644 index 00000000000000..a66ee4ce7c0da7 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/Internal/Runtime/CompilerServices/Unsafe.cs @@ -0,0 +1,104 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +namespace Internal.Runtime.CompilerServices +{ + // + // Subsetted clone of System.Runtime.CompilerServices.Unsafe for internal runtime use. + // Keep in sync with https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafe. + // + + /// + /// Contains generic, low-level functionality for manipulating pointers. + /// + public static unsafe class Unsafe + { + /// + /// Returns a pointer to the given by-ref parameter. + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* AsPointer(ref T value) + { + throw new PlatformNotSupportedException(); + + // ldarg.0 + // conv.u + // ret + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SizeOf() + { + // This method is implemented by the toolchain + throw new PlatformNotSupportedException(); + + // sizeof !!0 + // ret + } + + /// + /// Casts the given object to the specified type, performs no dynamic type checking. + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T As(object value) where T : class + { + // This method is implemented by the toolchain + throw new PlatformNotSupportedException(); + + // ldarg.0 + // ret + } + + /// + /// Reinterprets the given reference as a reference to a value of type . + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TTo As(ref TFrom source) + { + // This method is implemented by the toolchain + throw new PlatformNotSupportedException(); + + // ldarg.0 + // ret + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T AddByteOffset(ref T source, IntPtr byteOffset) + { + // This method is implemented by the toolchain + throw new PlatformNotSupportedException(); + + // ldarg.0 + // ldarg.1 + // add + // ret + } + + /// + /// Adds an element offset to the given reference. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T Add(ref T source, int elementOffset) + { + return ref AddByteOffset(ref source, (IntPtr)(elementOffset * (nint)SizeOf())); + } + + /// + /// Reinterprets the given location as a reference to a value of type . + /// + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T AsRef(in T source) + { + throw new PlatformNotSupportedException(); + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/RhBaseName.cs b/src/coreclr/nativeaot/Runtime.Base/src/RhBaseName.cs new file mode 100644 index 00000000000000..2c2189c7d8bf49 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/RhBaseName.cs @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static class Redhawk { public const string BaseName = "*"; } diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Array.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Array.cs new file mode 100644 index 00000000000000..e6f4b12ba86b7e --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Array.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +using Internal.Runtime.CompilerServices; + +namespace System +{ + // CONTRACT with Runtime + // The Array type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type int + + public partial class Array + { + // CS0169: The field 'Array._numComponents' is never used +#pragma warning disable 0169 + // This field should be the first field in Array as the runtime/compilers depend on it + private int _numComponents; +#pragma warning restore + + public int Length => (int)Unsafe.As(this).Length; + } + + // To accommodate class libraries that wish to implement generic interfaces on arrays, all class libraries + // are now required to provide an Array class that derives from Array. + internal class Array : Array + { + } + + [StructLayout(LayoutKind.Sequential)] + internal class RawArrayData + { + public uint Length; // Array._numComponents padded to IntPtr +#if BIT64 + public uint Padding; +#endif + public byte Data; + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Attribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Attribute.cs new file mode 100644 index 00000000000000..0ab0cf95225f0d --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Attribute.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System +{ + [AttributeUsageAttribute(AttributeTargets.All, Inherited = true, AllowMultiple = false)] + public abstract class Attribute + { + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/AttributeTargets.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/AttributeTargets.cs new file mode 100644 index 00000000000000..37f5d151709e85 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/AttributeTargets.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System +{ + // Enum used to indicate all the elements of the + // VOS it is valid to attach this element to. + + internal enum AttributeTargets + { + Assembly = 0x0001, + Module = 0x0002, + Class = 0x0004, + Struct = 0x0008, + Enum = 0x0010, + Constructor = 0x0020, + Method = 0x0040, + Property = 0x0080, + Field = 0x0100, + Event = 0x0200, + Interface = 0x0400, + Parameter = 0x0800, + Delegate = 0x1000, + ReturnValue = 0x2000, + GenericParameter = 0x4000, + + All = Assembly | Module | Class | Struct | Enum | Constructor | + Method | Property | Field | Event | Interface | Parameter | + Delegate | ReturnValue | GenericParameter + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/AttributeUsageAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/AttributeUsageAttribute.cs new file mode 100644 index 00000000000000..bd9ef45df75207 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/AttributeUsageAttribute.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================ +** +** +** +** Purpose: The class denotes how to specify the usage of an attribute +** +** +===========================================================*/ + +namespace System +{ + /* By default, attributes are inherited and multiple attributes are not allowed */ + [AttributeUsage(AttributeTargets.Class, Inherited = true)] + internal sealed class AttributeUsageAttribute : Attribute + { + //Constructors + public AttributeUsageAttribute(AttributeTargets validOn) + { + } + + public AttributeUsageAttribute(AttributeTargets validOn, bool allowMultiple, bool inherited) + { + } + + //Properties. + // Allowing the set properties as it allows a more readable syntax in the specifiers (and are commonly used) + // The get properties will be needed only if these attributes are used at Runtime, however, the compiler + // is getting an internal error if the gets are not defined. + + public bool AllowMultiple + { + get { return false; } + set { } + } + + public bool Inherited + { + get { return false; } + set { } + } + } +} diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/pch.cpp b/src/coreclr/nativeaot/Runtime.Base/src/System/Delegate.cs similarity index 51% rename from src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/pch.cpp rename to src/coreclr/nativeaot/Runtime.Base/src/System/Delegate.cs index 997542f41025a8..bf63352b06e235 100644 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/pch.cpp +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Delegate.cs @@ -1,6 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#include "pch.h" +using System.Runtime; +using System.Runtime.InteropServices; -// Bei der Verwendung vorkompilierter Header ist diese Quelldatei für eine erfolgreiche Kompilierung erforderlich. +namespace System +{ + public class Delegate + { + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Diagnostics/ConditionalAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Diagnostics/ConditionalAttribute.cs new file mode 100644 index 00000000000000..de79c774c57b54 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Diagnostics/ConditionalAttribute.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] + internal sealed class ConditionalAttribute : Attribute + { + public ConditionalAttribute(string conditionString) + { + _conditionString = conditionString; + } + + public string ConditionString + { + get + { + return _conditionString; + } + } + + private string _conditionString; + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Diagnostics/Debug.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Diagnostics/Debug.cs new file mode 100644 index 00000000000000..0221b8e7c98951 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Diagnostics/Debug.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System.Diagnostics +{ + internal static class Debug + { + [System.Diagnostics.Conditional("DEBUG")] + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Assert(bool condition, string message) + { + if (!condition) + { + EH.FallbackFailFast(RhFailFastReason.InternalError, null); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Assert(bool condition) + { + if (!condition) + { + EH.FallbackFailFast(RhFailFastReason.InternalError, null); + } + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Exception.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Exception.cs new file mode 100644 index 00000000000000..f3851d38d7833f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Exception.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace System +{ + public abstract class Exception + { + private string _exceptionString; + + public Exception() { } + + public Exception(string str) + { + _exceptionString = str; + } + } + + internal sealed class NullReferenceException : Exception + { + public NullReferenceException() { } + } + + internal sealed class InvalidOperationException : Exception + { + public InvalidOperationException() { } + } + + internal sealed class ArgumentOutOfRangeException : Exception + { + public ArgumentOutOfRangeException() { } + } + + internal sealed class IndexOutOfRangeException : Exception + { + public IndexOutOfRangeException() { } + } + + internal sealed class ArgumentNullException : Exception + { + public ArgumentNullException() { } + } + + internal sealed class NotImplementedException : Exception + { + public NotImplementedException() { } + } + + internal sealed class NotSupportedException : Exception + { + public NotSupportedException() { } + } + + internal sealed class PlatformNotSupportedException : Exception + { + public PlatformNotSupportedException() { } + } + + internal sealed class InvalidCastException : Exception + { + public InvalidCastException() { } + } + + internal sealed class ArrayTypeMismatchException : Exception + { + public ArrayTypeMismatchException() { } + } + + internal sealed class OverflowException : Exception + { + public OverflowException() { } + } + + internal sealed class ArithmeticException : Exception + { + public ArithmeticException() { } + } + + internal sealed class DivideByZeroException : Exception + { + public DivideByZeroException() { } + } + + internal class OutOfMemoryException : Exception + { + public OutOfMemoryException() { } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/FlagsAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/FlagsAttribute.cs new file mode 100644 index 00000000000000..fd33dcf19f81a8 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/FlagsAttribute.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System +{ + // Custom attribute to indicate that the enum + // should be treated as a bitfield (or set of flags). + // An IDE may use this information to provide a richer + // development experience. + [AttributeUsage(AttributeTargets.Enum, Inherited = false)] + public class FlagsAttribute : Attribute + { + public FlagsAttribute() + { + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/GC.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/GC.cs new file mode 100644 index 00000000000000..fea06c6123be6e --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/GC.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Exposes features of the Garbage Collector to managed code. +// +// This is an extremely simple initial version that only exposes a small fraction of the API that the CLR +// version does. +// + +namespace System +{ + // !!!!!!!!!!!!!!!!!!!!!!! + // Make sure you change the def in gc.h if you change this! + public enum InternalGCCollectionMode + { + NonBlocking = 0x00000001, + Blocking = 0x00000002, + Optimized = 0x00000004, + } + + public enum GCLatencyMode + { + Batch = 0, + Interactive = 1, + LowLatency = 2, + SustainedLowLatency = 3 + } +} diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/pch.h b/src/coreclr/nativeaot/Runtime.Base/src/System/MulticastDelegate.cs similarity index 56% rename from src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/pch.h rename to src/coreclr/nativeaot/Runtime.Base/src/System/MulticastDelegate.cs index 8ea9a9f7b04182..89ca1daa25c0a7 100644 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/pch.h +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/MulticastDelegate.cs @@ -1,8 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#ifndef PCH_H -#define PCH_H +using System.Runtime; -// Fügen Sie hier Header hinzu, die vorkompiliert werden sollen. -#endif //PCH_H +namespace System +{ + public class MulticastDelegate : Delegate + { + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Nullable.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Nullable.cs new file mode 100644 index 00000000000000..2f43b9b7515eb9 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Nullable.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace System +{ + internal struct Nullable where T : struct + { +#pragma warning disable 169 // The field 'blah' is never used + private readonly bool _hasValue; + private T _value; +#pragma warning restore 0169 + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Object.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Object.cs new file mode 100644 index 00000000000000..c64cd5000fe8dc --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Object.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +using Internal.Runtime; +using Internal.Runtime.CompilerServices; + +namespace System +{ + // CONTRACT with Runtime + // The Object type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type MethodTable* + // VTable Contract: The first vtable slot should be the finalizer for object => The first virtual method in the object class should be the Finalizer + + public unsafe class Object + { + // CS0649: Field '{blah}' is never assigned to, and will always have its default value +#pragma warning disable 649 + private MethodTable* m_pEEType; +#pragma warning restore + + // Creates a new instance of an Object. + public Object() + { + } + + // Allow an object to free resources before the object is reclaimed by the GC. + // CONTRACT with runtime: This method's virtual slot number is hardcoded in the binder. It is an + // implementation detail where it winds up at runtime. + // **** Do not add any virtual methods in this class ahead of this **** + + ~Object() + { + } + + internal MethodTable* MethodTable + { + get + { + // NOTE: if managed code can be run when the GC has objects marked, then this method is + // unsafe. But, generically, we don't expect managed code such as this to be allowed + // to run while the GC is running. + return m_pEEType; + } + } + + [Runtime.CompilerServices.Intrinsic] + internal static extern MethodTable* MethodTableOf(); + + internal EETypePtr EETypePtr + { + get + { + return new EETypePtr(new IntPtr(m_pEEType)); + } + } + + [StructLayout(LayoutKind.Sequential)] + private class RawData + { + public byte Data; + } + + /// + /// Return beginning of all data (excluding ObjHeader and MethodTable*) within this object. + /// Note that for strings/arrays this would include the Length as well. + /// + internal ref byte GetRawData() + { + return ref Unsafe.As(this).Data; + } + + /// + /// Return size of all data (excluding ObjHeader and MethodTable*). + /// Note that for strings/arrays this would include the Length as well. + /// + internal uint GetRawDataSize() + { + return EETypePtr.BaseSize - (uint)sizeof(ObjHeader) - (uint)sizeof(MethodTable*); + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/ParamArrayAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/ParamArrayAttribute.cs new file mode 100644 index 00000000000000..3bc2f7847effec --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/ParamArrayAttribute.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System +{ + // Attribute to indicate array of arguments for variable number of args. + + [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] + internal class ParamArrayAttribute : Attribute + { + public ParamArrayAttribute() + { + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Primitives.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Primitives.cs new file mode 100644 index 00000000000000..52dffcfdb8c59e --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Primitives.cs @@ -0,0 +1,534 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This file contains the basic primitive type definitions (int etc) +// These types are well known to the compiler and the runtime and are basic interchange types that do not change + +// CONTRACT with Runtime +// Each of the data types has a data contract with the runtime. See the contract in the type definition +// + +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System +{ + // CONTRACT with Runtime + // Place holder type for type hierarchy, Compiler/Runtime requires this class + public abstract class ValueType + { + } + + // CONTRACT with Runtime, Compiler/Runtime requires this class + // Place holder type for type hierarchy + public abstract class Enum : ValueType + { + } + + /*============================================================ + ** + ** Class: Boolean + ** + ** + ** Purpose: The boolean class serves as a wrapper for the primitive + ** type boolean. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Boolean type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type bool + + public struct Boolean + { + // Disable compile warning about unused _value field +#pragma warning disable 0169 + private bool _value; +#pragma warning restore 0169 + } + + + /*============================================================ + ** + ** Class: Char + ** + ** + ** Purpose: This is the value class representing a Unicode character + ** + ** + ===========================================================*/ + + + // CONTRACT with Runtime + // The Char type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type char + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Char + { + private char _value; + + public const char MaxValue = (char)0xFFFF; + public const char MinValue = (char)0x00; + } + + + /*============================================================ + ** + ** Class: SByte + ** + ** + ** Purpose: A representation of a 8 bit 2's complement integer. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The SByte type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type sbyte + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct SByte + { + private sbyte _value; + + public const sbyte MaxValue = (sbyte)0x7F; + public const sbyte MinValue = unchecked((sbyte)0x80); + } + + + /*============================================================ + ** + ** Class: Byte + ** + ** + ** Purpose: A representation of a 8 bit integer (byte) + ** + ** + ===========================================================*/ + + + // CONTRACT with Runtime + // The Byte type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type bool + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Byte + { + private byte _value; + + public const byte MaxValue = (byte)0xFF; + public const byte MinValue = 0; + } + + + /*============================================================ + ** + ** Class: Int16 + ** + ** + ** Purpose: A representation of a 16 bit 2's complement integer. + ** + ** + ===========================================================*/ + + + // CONTRACT with Runtime + // The Int16 type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type short + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Int16 + { + private short _value; + + public const short MaxValue = (short)0x7FFF; + public const short MinValue = unchecked((short)0x8000); + } + + /*============================================================ + ** + ** Class: UInt16 + ** + ** + ** Purpose: A representation of a short (unsigned 16-bit) integer. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Uint16 type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type ushort + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct UInt16 + { + private ushort _value; + + public const ushort MaxValue = (ushort)0xffff; + public const ushort MinValue = 0; + } + + /*============================================================ + ** + ** Class: Int32 + ** + ** + ** Purpose: A representation of a 32 bit 2's complement integer. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Int32 type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type int + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Int32 + { + private int _value; + + public const int MaxValue = 0x7fffffff; + public const int MinValue = unchecked((int)0x80000000); + } + + + /*============================================================ + ** + ** Class: UInt32 + ** + ** + ** Purpose: A representation of a 32 bit unsigned integer. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Uint32 type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type uint + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct UInt32 + { + private uint _value; + + public const uint MaxValue = (uint)0xffffffff; + public const uint MinValue = 0; + } + + + /*============================================================ + ** + ** Class: Int64 + ** + ** + ** Purpose: A representation of a 64 bit 2's complement integer. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Int64 type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type long + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Int64 + { + private long _value; + + public const long MaxValue = 0x7fffffffffffffffL; + public const long MinValue = unchecked((long)0x8000000000000000L); + } + + + /*============================================================ + ** + ** Class: UInt64 + ** + ** + ** Purpose: A representation of a 64 bit unsigned integer. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The UInt64 type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type ulong + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct UInt64 + { + private ulong _value; + + public const ulong MaxValue = (ulong)0xffffffffffffffffL; + public const ulong MinValue = 0; + } + + + /*============================================================ + ** + ** Class: Single + ** + ** + ** Purpose: A wrapper class for the primitive type float. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Single type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type float + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Single + { + private float _value; + } + + + /*============================================================ + ** + ** Class: Double + ** + ** + ** Purpose: A representation of an IEEE double precision + ** floating point number. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Double type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type double + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Double + { + private double _value; + } + + + /*============================================================ + ** + ** Class: IntPtr + ** + ** + ** Purpose: Platform independent integer + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The IntPtr type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type void * + + // This type implements == without overriding GetHashCode, Equals, disable compiler warning +#pragma warning disable 0660, 0661 + public struct IntPtr + { + private unsafe void* _value; // The compiler treats void* closest to uint hence explicit casts are required to preserve int behavior + + [Intrinsic] + public static readonly IntPtr Zero; + + public static unsafe int Size + { + [Intrinsic] + get + { +#if TARGET_64BIT + return 8; +#else + return 4; +#endif + } + } + + [Intrinsic] + public unsafe IntPtr(void* value) + { + _value = value; + } + + [Intrinsic] + public unsafe IntPtr(int value) + { + _value = (void*)value; + } + + [Intrinsic] + public unsafe IntPtr(long value) + { + _value = (void*)value; + } + + [Intrinsic] + public unsafe long ToInt64() + { +#if TARGET_64BIT + return (long)_value; +#else + return (long)(int)_value; +#endif + } + + [Intrinsic] + public static unsafe explicit operator IntPtr(int value) + { + return new IntPtr(value); + } + + [Intrinsic] + public static unsafe explicit operator IntPtr(long value) + { + return new IntPtr(value); + } + + [Intrinsic] + public static unsafe explicit operator IntPtr(void* value) + { + return new IntPtr(value); + } + + [Intrinsic] + public static unsafe explicit operator void* (IntPtr value) + { + return value._value; + } + + [Intrinsic] + public static unsafe explicit operator int(IntPtr value) + { + return unchecked((int)value._value); + } + + [Intrinsic] + public static unsafe explicit operator long(IntPtr value) + { + return unchecked((long)value._value); + } + + [Intrinsic] + public static unsafe bool operator ==(IntPtr value1, IntPtr value2) + { + return value1._value == value2._value; + } + + [Intrinsic] + public static unsafe bool operator !=(IntPtr value1, IntPtr value2) + { + return value1._value != value2._value; + } + + [Intrinsic] + public static unsafe IntPtr operator +(IntPtr pointer, int offset) + { +#if TARGET_64BIT + return new IntPtr((long)pointer._value + offset); +#else + return new IntPtr((int)pointer._value + offset); +#endif + } + } +#pragma warning restore 0660, 0661 + + + /*============================================================ + ** + ** Class: UIntPtr + ** + ** + ** Purpose: Platform independent integer + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The UIntPtr type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type void * + + // This type implements == without overriding GetHashCode, Equals, disable compiler warning +#pragma warning disable 0660, 0661 + public struct UIntPtr + { + private unsafe void* _value; + + [Intrinsic] + public static readonly UIntPtr Zero; + + [Intrinsic] + public unsafe UIntPtr(uint value) + { + _value = (void*)value; + } + + [Intrinsic] + public unsafe UIntPtr(ulong value) + { +#if TARGET_64BIT + _value = (void*)value; +#else + _value = (void*)checked((uint)value); +#endif + } + + [Intrinsic] + public unsafe UIntPtr(void* value) + { + _value = value; + } + + [Intrinsic] + public static unsafe explicit operator UIntPtr(void* value) + { + return new UIntPtr(value); + } + + [Intrinsic] + public static unsafe explicit operator void* (UIntPtr value) + { + return value._value; + } + + [Intrinsic] + public static unsafe explicit operator uint (UIntPtr value) + { +#if TARGET_64BIT + return checked((uint)value._value); +#else + return (uint)value._value; +#endif + } + + [Intrinsic] + public static unsafe explicit operator ulong (UIntPtr value) + { + return (ulong)value._value; + } + + [Intrinsic] + public static unsafe bool operator ==(UIntPtr value1, UIntPtr value2) + { + return value1._value == value2._value; + } + + [Intrinsic] + public static unsafe bool operator !=(UIntPtr value1, UIntPtr value2) + { + return value1._value != value2._value; + } + } +#pragma warning restore 0660, 0661 +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs new file mode 100644 index 00000000000000..6946733ebdf9f0 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Runtime.CompilerServices; + +using Internal.Runtime; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime +{ + internal static unsafe class CachedInterfaceDispatch + { + [RuntimeExport("RhpCidResolve")] + private static unsafe IntPtr RhpCidResolve(IntPtr callerTransitionBlockParam, IntPtr pCell) + { + IntPtr locationOfThisPointer = callerTransitionBlockParam + TransitionBlock.GetThisOffset(); + object pObject = Unsafe.As(ref *(IntPtr*)locationOfThisPointer); + IntPtr dispatchResolveTarget = RhpCidResolve_Worker(pObject, pCell); + return dispatchResolveTarget; + } + + private static IntPtr RhpCidResolve_Worker(object pObject, IntPtr pCell) + { + DispatchCellInfo cellInfo; + + InternalCalls.RhpGetDispatchCellInfo(pCell, out cellInfo); + IntPtr pTargetCode = RhResolveDispatchWorker(pObject, (void*)pCell, ref cellInfo); + if (pTargetCode != IntPtr.Zero) + { + // We don't update the dispatch cell cache if this is IDynamicInterfaceCastable because this + // scenario is by-design dynamic. There is no guarantee that another instance with the same MethodTable + // as the one we just resolved would do the resolution the same way. We will need to ask again. + if (!pObject.MethodTable->IsIDynamicInterfaceCastable) + { + return InternalCalls.RhpUpdateDispatchCellCache(pCell, pTargetCode, pObject.MethodTable, ref cellInfo); + } + else + { + return pTargetCode; + } + } + + // "Valid method implementation was not found." + EH.FallbackFailFast(RhFailFastReason.InternalError, null); + return IntPtr.Zero; + } + + [RuntimeExport("RhpResolveInterfaceMethod")] + private static IntPtr RhpResolveInterfaceMethod(object pObject, IntPtr pCell) + { + if (pObject == null) + { + // ProjectN Optimizer may perform code motion on dispatch such that it occurs independant of + // null check on "this" pointer. Allow for this case by returning back an invalid pointer. + return IntPtr.Zero; + } + + MethodTable* pInstanceType = pObject.MethodTable; + + // This method is used for the implementation of LOAD_VIRT_FUNCTION and in that case the mapping we want + // may already be in the cache. + IntPtr pTargetCode = InternalCalls.RhpSearchDispatchCellCache(pCell, pInstanceType); + if (pTargetCode == IntPtr.Zero) + { + // Otherwise call the version of this method that knows how to resolve the method manually. + pTargetCode = RhpCidResolve_Worker(pObject, pCell); + } + + return pTargetCode; + } + + [RuntimeExport("RhResolveDispatch")] + private static IntPtr RhResolveDispatch(object pObject, EETypePtr interfaceType, ushort slot) + { + DispatchCellInfo cellInfo = default; + cellInfo.CellType = DispatchCellType.InterfaceAndSlot; + cellInfo.InterfaceType = interfaceType; + cellInfo.InterfaceSlot = slot; + + return RhResolveDispatchWorker(pObject, null, ref cellInfo); + } + + [RuntimeExport("RhResolveDispatchOnType")] + private static IntPtr RhResolveDispatchOnType(EETypePtr instanceType, EETypePtr interfaceType, ushort slot) + { + // Type of object we're dispatching on. + MethodTable* pInstanceType = instanceType.ToPointer(); + + // Type of interface + MethodTable* pInterfaceType = interfaceType.ToPointer(); + + return DispatchResolve.FindInterfaceMethodImplementationTarget(pInstanceType, + pInterfaceType, + slot); + } + + private static unsafe IntPtr RhResolveDispatchWorker(object pObject, void* cell, ref DispatchCellInfo cellInfo) + { + // Type of object we're dispatching on. + MethodTable* pInstanceType = pObject.MethodTable; + + if (cellInfo.CellType == DispatchCellType.InterfaceAndSlot) + { + // Type whose DispatchMap is used. Usually the same as the above but for types which implement IDynamicInterfaceCastable + // we may repeat this process with an alternate type. + MethodTable* pResolvingInstanceType = pInstanceType; + + IntPtr pTargetCode = DispatchResolve.FindInterfaceMethodImplementationTarget(pResolvingInstanceType, + cellInfo.InterfaceType.ToPointer(), + cellInfo.InterfaceSlot); + if (pTargetCode == IntPtr.Zero && pInstanceType->IsIDynamicInterfaceCastable) + { + // Dispatch not resolved through normal dispatch map, try using the IDynamicInterfaceCastable + // This will either give us the appropriate result, or throw. + var pfnGetInterfaceImplementation = (delegate*) + pInstanceType->GetClasslibFunction(ClassLibFunctionId.IDynamicCastableGetInterfaceImplementation); + pTargetCode = pfnGetInterfaceImplementation(pObject, cellInfo.InterfaceType.ToPointer(), cellInfo.InterfaceSlot); + Diagnostics.Debug.Assert(pTargetCode != IntPtr.Zero); + } + return pTargetCode; + } + else if (cellInfo.CellType == DispatchCellType.VTableOffset) + { + // Dereference VTable + return *(IntPtr*)(((byte*)pInstanceType) + cellInfo.VTableOffset); + } + else + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING_AND_SUPPORTS_TOKEN_BASED_DISPATCH_CELLS + // Attempt to convert dispatch cell to non-metadata form if we haven't acquired a cache for this cell yet + if (cellInfo.HasCache == 0) + { + cellInfo = InternalTypeLoaderCalls.ConvertMetadataTokenDispatch(InternalCalls.RhGetModuleFromPointer(cell), cellInfo); + if (cellInfo.CellType != DispatchCellType.MetadataToken) + { + return RhResolveDispatchWorker(pObject, cell, ref cellInfo); + } + } + + // If that failed, go down the metadata resolution path + return InternalTypeLoaderCalls.ResolveMetadataTokenDispatch(InternalCalls.RhGetModuleFromPointer(cell), (int)cellInfo.MetadataToken, new IntPtr(pInstanceType)); +#else + EH.FallbackFailFast(RhFailFastReason.InternalError, null); + return IntPtr.Zero; +#endif + } + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/EagerStaticClassConstructionAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/EagerStaticClassConstructionAttribute.cs new file mode 100644 index 00000000000000..a0d83b3ce28d8f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/EagerStaticClassConstructionAttribute.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + // When applied to a type this custom attribute will cause any static class constructor to be run eagerly + // at module load time rather than deferred till just before the class is used. + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] + public class EagerStaticClassConstructionAttribute : Attribute + { + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/FixedBufferAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/FixedBufferAttribute.cs new file mode 100644 index 00000000000000..cbc56b256d169f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/FixedBufferAttribute.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + internal unsafe sealed class FixedBufferAttribute : Attribute + { + public FixedBufferAttribute(Type elementType, int length) + { + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/IntrinsicAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/IntrinsicAttribute.cs new file mode 100644 index 00000000000000..410f621935ac90 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/IntrinsicAttribute.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + // This attribute is only for use in a Class Library + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, Inherited = false)] + internal sealed class IntrinsicAttribute : Attribute + { + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/IsByRefLikeAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/IsByRefLikeAttribute.cs new file mode 100644 index 00000000000000..7e158afed715c6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/IsByRefLikeAttribute.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Struct)] + internal sealed class IsByRefLikeAttribute : Attribute + { + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/IsVolatile.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/IsVolatile.cs new file mode 100644 index 00000000000000..d069ca2bd3d98a --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/IsVolatile.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + public static class IsVolatile + { + // no instantiation, please! + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/MethodImplAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/MethodImplAttribute.cs new file mode 100644 index 00000000000000..012c7444fc32a4 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/MethodImplAttribute.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace System.Runtime.CompilerServices +{ + // This Enum matchs the miImpl flags defined in corhdr.h. It is used to specify + // certain method properties. + public enum MethodImplOptions + { + Unmanaged = 0x0004, + NoInlining = 0x0008, + NoOptimization = 0x0040, + AggressiveInlining = 0x0100, + AggressiveOptimization = 0x200, + InternalCall = 0x1000, + } + + // Custom attribute to specify additional method properties. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] + public sealed class MethodImplAttribute : Attribute + { + internal MethodImplOptions _val; + + public MethodImplAttribute(MethodImplOptions methodImplOptions) + { + _val = methodImplOptions; + } + + public MethodImplAttribute(short value) + { + _val = (MethodImplOptions)value; + } + + public MethodImplAttribute() + { + } + + public MethodImplOptions Value { get { return _val; } } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/RuntimeHelpers.cs new file mode 100644 index 00000000000000..b06f2f3d5eb30c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + internal static class RuntimeHelpers + { + public static int OffsetToStringData + { + get + { + // Number of bytes from the address pointed to by a reference to + // a String to the first 16-bit character in the String. + // This property allows C#'s fixed statement to work on Strings. + return String.FIRST_CHAR_OFFSET; + } + } + + [Intrinsic] + public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs new file mode 100644 index 00000000000000..97ef702e3dc2a5 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + internal class UnsafeValueTypeAttribute : Attribute { } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/DispatchResolve.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/DispatchResolve.cs new file mode 100644 index 00000000000000..85adf27fe5469b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/DispatchResolve.cs @@ -0,0 +1,237 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +using Internal.Runtime; + +namespace System.Runtime +{ + internal static unsafe class DispatchResolve + { + public static IntPtr FindInterfaceMethodImplementationTarget(MethodTable* pTgtType, + MethodTable* pItfType, + ushort itfSlotNumber) + { + DynamicModule* dynamicModule = pTgtType->DynamicModule; + + // Use the dynamic module resolver if it's present + if (dynamicModule != null) + { + delegate* resolver = dynamicModule->DynamicTypeSlotDispatchResolve; + if (resolver != null) + return resolver(pTgtType, pItfType, itfSlotNumber); + } + + // Start at the current type and work up the inheritance chain + MethodTable* pCur = pTgtType; + + if (pItfType->IsCloned) + pItfType = pItfType->CanonicalEEType; + + while (pCur != null) + { + ushort implSlotNumber; + if (FindImplSlotForCurrentType( + pCur, pItfType, itfSlotNumber, &implSlotNumber)) + { + IntPtr targetMethod; + if (implSlotNumber < pCur->NumVtableSlots) + { + // true virtual - need to get the slot from the target type in case it got overridden + targetMethod = pTgtType->GetVTableStartAddress()[implSlotNumber]; + } + else if (implSlotNumber == SpecialDispatchMapSlot.Reabstraction) + { + throw pTgtType->GetClasslibException(ExceptionIDs.EntrypointNotFound); + } + else if (implSlotNumber == SpecialDispatchMapSlot.Diamond) + { + throw pTgtType->GetClasslibException(ExceptionIDs.AmbiguousImplementation); + } + else + { + // sealed virtual - need to get the slot form the implementing type, because + // it's not present on the target type + targetMethod = pCur->GetSealedVirtualSlot((ushort)(implSlotNumber - pCur->NumVtableSlots)); + } + return targetMethod; + } + if (pCur->IsArray) + pCur = pCur->GetArrayEEType(); + else + pCur = pCur->NonArrayBaseType; + } + return IntPtr.Zero; + } + + + private static bool FindImplSlotForCurrentType(MethodTable* pTgtType, + MethodTable* pItfType, + ushort itfSlotNumber, + ushort* pImplSlotNumber) + { + bool fRes = false; + + // If making a call and doing virtual resolution don't look into the dispatch map, + // take the slot number directly. + if (!pItfType->IsInterface) + { + *pImplSlotNumber = itfSlotNumber; + + // Only notice matches if the target type and search types are the same + // This will make dispatch to sealed slots work correctly + return pTgtType == pItfType; + } + + if (pTgtType->HasDispatchMap) + { + // We first look at non-default implementation. Default implementations are only considered + // if the "old algorithm" didn't come up with an answer. + + bool fDoDefaultImplementationLookup = false; + + // For variant interface dispatch, the algorithm is to walk the parent hierarchy, and at each level + // attempt to dispatch exactly first, and then if that fails attempt to dispatch variantly. This can + // result in interesting behavior such as a derived type only overriding one particular instantiation + // and funneling all the dispatches to it, but its the algorithm. + + again: + bool fDoVariantLookup = false; // do not check variance for first scan of dispatch map + + fRes = FindImplSlotInSimpleMap( + pTgtType, pItfType, itfSlotNumber, pImplSlotNumber, fDoVariantLookup, fDoDefaultImplementationLookup); + + if (!fRes) + { + fDoVariantLookup = true; // check variance for second scan of dispatch map + fRes = FindImplSlotInSimpleMap( + pTgtType, pItfType, itfSlotNumber, pImplSlotNumber, fDoVariantLookup, fDoDefaultImplementationLookup); + } + + // If we haven't found anything and haven't looked at the default implementations yet, look now + if (!fRes && !fDoDefaultImplementationLookup) + { + fDoDefaultImplementationLookup = true; + goto again; + } + } + + return fRes; + } + + private static bool FindImplSlotInSimpleMap(MethodTable* pTgtType, + MethodTable* pItfType, + uint itfSlotNumber, + ushort* pImplSlotNumber, + bool actuallyCheckVariance, + bool checkDefaultImplementations) + { + Debug.Assert(pTgtType->HasDispatchMap, "Missing dispatch map"); + + MethodTable* pItfOpenGenericType = null; + EETypeRef* pItfInstantiation = null; + int itfArity = 0; + GenericVariance* pItfVarianceInfo = null; + + bool fCheckVariance = false; + bool fArrayCovariance = false; + + if (actuallyCheckVariance) + { + fCheckVariance = pItfType->HasGenericVariance; + fArrayCovariance = pTgtType->IsArray; + + // Non-arrays can follow array variance rules iff + // 1. They have one generic parameter + // 2. That generic parameter is array covariant. + // + // This special case is to allow array enumerators to work + if (!fArrayCovariance && pTgtType->HasGenericVariance) + { + int tgtEntryArity = (int)pTgtType->GenericArity; + GenericVariance* pTgtVarianceInfo = pTgtType->GenericVariance; + + if ((tgtEntryArity == 1) && pTgtVarianceInfo[0] == GenericVariance.ArrayCovariant) + { + fArrayCovariance = true; + } + } + + // Arrays are covariant even though you can both get and set elements (type safety is maintained by + // runtime type checks during set operations). This extends to generic interfaces implemented on those + // arrays. We handle this by forcing all generic interfaces on arrays to behave as though they were + // covariant (over their one type parameter corresponding to the array element type). + if (fArrayCovariance && pItfType->IsGeneric) + fCheckVariance = true; + + // If there is no variance checking, there is no operation to perform. (The non-variance check loop + // has already completed) + if (!fCheckVariance) + { + return false; + } + } + + DispatchMap* pMap = pTgtType->DispatchMap; + DispatchMap.DispatchMapEntry* i = (*pMap)[checkDefaultImplementations ? (int)pMap->NumStandardEntries : 0]; + DispatchMap.DispatchMapEntry* iEnd = (*pMap)[checkDefaultImplementations ? (int)(pMap->NumStandardEntries + pMap->NumDefaultEntries) : (int)pMap->NumStandardEntries]; + for (; i != iEnd; ++i) + { + if (i->_usInterfaceMethodSlot == itfSlotNumber) + { + MethodTable* pCurEntryType = + pTgtType->InterfaceMap[i->_usInterfaceIndex].InterfaceType; + + if (pCurEntryType->IsCloned) + pCurEntryType = pCurEntryType->CanonicalEEType; + + if (pCurEntryType == pItfType) + { + *pImplSlotNumber = i->_usImplMethodSlot; + return true; + } + else if (fCheckVariance && ((fArrayCovariance && pCurEntryType->IsGeneric) || pCurEntryType->HasGenericVariance)) + { + // Interface types don't match exactly but both the target interface and the current interface + // in the map are marked as being generic with at least one co- or contra- variant type + // parameter. So we might still have a compatible match. + + // Retrieve the unified generic instance for the callsite interface if we haven't already (we + // lazily get this then cache the result since the lookup isn't necessarily cheap). + if (pItfOpenGenericType == null) + { + pItfOpenGenericType = pItfType->GenericDefinition; + itfArity = (int)pItfType->GenericArity; + pItfInstantiation = pItfType->GenericArguments; + pItfVarianceInfo = pItfType->GenericVariance; + } + + // Retrieve the unified generic instance for the interface we're looking at in the map. + MethodTable* pCurEntryGenericType = pCurEntryType->GenericDefinition; + + // If the generic types aren't the same then the types aren't compatible. + if (pItfOpenGenericType != pCurEntryGenericType) + continue; + + // Grab instantiation details for the candidate interface. + EETypeRef* pCurEntryInstantiation = pCurEntryType->GenericArguments; + + // The types represent different instantiations of the same generic type. The + // arity of both had better be the same. + Debug.Assert(itfArity == (int)pCurEntryType->GenericArity, "arity mismatch betweeen generic instantiations"); + + if (TypeCast.TypeParametersAreCompatible(itfArity, pCurEntryInstantiation, pItfInstantiation, pItfVarianceInfo, fArrayCovariance, null)) + { + *pImplSlotNumber = i->_usImplMethodSlot; + return true; + } + } + } + } + + return false; + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/EETypePtr.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/EETypePtr.cs new file mode 100644 index 00000000000000..4252397ed0811f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/EETypePtr.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +/*============================================================ +** +** Class: EETypePtr +** +** +** Purpose: Pointer Type to a MethodTable in the runtime. +** +** +===========================================================*/ + +namespace System +{ + // This type does not implement GetHashCode but implements Equals +#pragma warning disable 0659 + + [StructLayout(LayoutKind.Sequential)] + public struct EETypePtr + { + private IntPtr _value; + + internal EETypePtr(IntPtr value) + { + _value = value; + } + + internal bool Equals(EETypePtr p) + { + return (_value == p._value); + } + + internal unsafe Internal.Runtime.MethodTable* ToPointer() + { + return (Internal.Runtime.MethodTable*)(void*)_value; + } + + [Intrinsic] + internal static EETypePtr EETypePtrOf() + { + throw new NotImplementedException(); + } + + internal unsafe uint BaseSize + { + get + { + return ToPointer()->BaseSize; + } + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs new file mode 100644 index 00000000000000..a0c392325267fe --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -0,0 +1,938 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Internal.Runtime; + +// Disable: Filter expression is a constant. We know. We just can't do an unfiltered catch. +#pragma warning disable 7095 + +namespace System.Runtime +{ + public enum RhFailFastReason + { + Unknown = 0, + InternalError = 1, // "Runtime internal error" + UnhandledException_ExceptionDispatchNotAllowed = 2, // "Unhandled exception: no handler found before escaping a finally clause or other fail-fast scope." + UnhandledException_CallerDidNotHandle = 3, // "Unhandled exception: no handler found in calling method." + ClassLibDidNotTranslateExceptionID = 4, // "Unable to translate failure into a classlib-specific exception object." + IllegalUnmanagedCallersOnlyEntry = 5, // "Invalid Program: attempted to call a UnmanagedCallersOnly method from runtime-typesafe code." + + PN_UnhandledException = 6, // ProjectN: "unhandled exception" + PN_UnhandledExceptionFromPInvoke = 7, // ProjectN: "Unhandled exception: an unmanaged exception was thrown out of a managed-to-native transition." + Max + } + + internal static unsafe partial class EH + { + internal static UIntPtr MaxSP + { + get + { + return (UIntPtr)(void*)(-1); + } + } + + private enum RhEHClauseKind + { + RH_EH_CLAUSE_TYPED = 0, + RH_EH_CLAUSE_FAULT = 1, + RH_EH_CLAUSE_FILTER = 2, + RH_EH_CLAUSE_UNUSED = 3, + } + + private struct RhEHClause + { + internal RhEHClauseKind _clauseKind; + internal uint _tryStartOffset; + internal uint _tryEndOffset; + internal byte* _filterAddress; + internal byte* _handlerAddress; + internal void* _pTargetType; + + /// + /// We expect the stackwalker to adjust return addresses to point at 'return address - 1' so that we + /// can use an interval here that is closed at the start and open at the end. When a hardware fault + /// occurs, the IP is pointing at the start of the instruction and will not be adjusted by the + /// stackwalker. Therefore, it will naturally work with an interval that has a closed start and open + /// end. + /// + public bool ContainsCodeOffset(uint codeOffset) + { + return ((codeOffset >= _tryStartOffset) && + (codeOffset < _tryEndOffset)); + } + } + + [StructLayout(LayoutKind.Explicit, Size = AsmOffsets.SIZEOF__EHEnum)] + private struct EHEnum + { + [FieldOffset(0)] + private IntPtr _dummy; // For alignment + } + + // This is a fail-fast function used by the runtime as a last resort that will terminate the process with + // as little effort as possible. No guarantee is made about the semantics of this fail-fast. + internal static void FallbackFailFast(RhFailFastReason reason, object unhandledException) + { + InternalCalls.RhpFallbackFailFast(); + } + + // Given an address pointing somewhere into a managed module, get the classlib-defined fail-fast + // function and invoke it. Any failure to find and invoke the function, or if it returns, results in + // MRT-defined fail-fast behavior. + internal static void FailFastViaClasslib(RhFailFastReason reason, object unhandledException, + IntPtr classlibAddress) + { + // Find the classlib function that will fail fast. This is a RuntimeExport function from the + // classlib module, and is therefore managed-callable. + IntPtr pFailFastFunction = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromCodeAddress(classlibAddress, + ClassLibFunctionId.FailFast); + + if (pFailFastFunction == IntPtr.Zero) + { + // The classlib didn't provide a function, so we fail our way... + FallbackFailFast(reason, unhandledException); + } + + try + { + // Invoke the classlib fail fast function. + ((delegate*)pFailFastFunction) + (reason, unhandledException, IntPtr.Zero, IntPtr.Zero); + } + catch when (true) + { + // disallow all exceptions leaking out of callbacks + } + + // The classlib's function should never return and should not throw. If it does, then we fail our way... + FallbackFailFast(reason, unhandledException); + } + +#if TARGET_AMD64 + [StructLayout(LayoutKind.Explicit, Size = 0x4d0)] +#elif TARGET_ARM + [StructLayout(LayoutKind.Explicit, Size = 0x1a0)] +#elif TARGET_X86 + [StructLayout(LayoutKind.Explicit, Size = 0x2cc)] +#elif TARGET_ARM64 + [StructLayout(LayoutKind.Explicit, Size = 0x390)] +#else + [StructLayout(LayoutKind.Explicit, Size = 0x10)] // this is small enough that it should trip an assert in RhpCopyContextFromExInfo +#endif + private struct OSCONTEXT + { + } + + internal static unsafe void* PointerAlign(void* ptr, int alignmentInBytes) + { + int alignMask = alignmentInBytes - 1; +#if TARGET_64BIT + return (void*)((((long)ptr) + alignMask) & ~alignMask); +#else + return (void*)((((int)ptr) + alignMask) & ~alignMask); +#endif + } + + private static void OnFirstChanceExceptionViaClassLib(object exception) + { + IntPtr pOnFirstChanceFunction = + (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType(exception.MethodTable, ClassLibFunctionId.OnFirstChance); + + if (pOnFirstChanceFunction == IntPtr.Zero) + { + return; + } + + try + { + ((delegate*)pOnFirstChanceFunction)(exception); + } + catch when (true) + { + // disallow all exceptions leaking out of callbacks + } + } + + private static void OnUnhandledExceptionViaClassLib(object exception) + { + IntPtr pOnUnhandledExceptionFunction = + (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType(exception.MethodTable, ClassLibFunctionId.OnUnhandledException); + + if (pOnUnhandledExceptionFunction == IntPtr.Zero) + { + return; + } + + try + { + ((delegate*)pOnUnhandledExceptionFunction)(exception); + } + catch when (true) + { + // disallow all exceptions leaking out of callbacks + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static unsafe void UnhandledExceptionFailFastViaClasslib( + RhFailFastReason reason, object unhandledException, IntPtr classlibAddress, ref ExInfo exInfo) + { + IntPtr pFailFastFunction = + (IntPtr)InternalCalls.RhpGetClasslibFunctionFromCodeAddress(classlibAddress, ClassLibFunctionId.FailFast); + + if (pFailFastFunction == IntPtr.Zero) + { + FailFastViaClasslib( + reason, + unhandledException, + classlibAddress); + } + + // 16-byte align the context. This is overkill on x86 and ARM, but simplifies things slightly. + const int contextAlignment = 16; + byte* pbBuffer = stackalloc byte[sizeof(OSCONTEXT) + contextAlignment]; + void* pContext = PointerAlign(pbBuffer, contextAlignment); + + InternalCalls.RhpCopyContextFromExInfo(pContext, sizeof(OSCONTEXT), exInfo._pExContext); + + try + { + ((delegate*)pFailFastFunction) + (reason, unhandledException, exInfo._pExContext->IP, pContext); + } + catch when (true) + { + // disallow all exceptions leaking out of callbacks + } + + // The classlib's funciton should never return and should not throw. If it does, then we fail our way... + FallbackFailFast(reason, unhandledException); + } + + private enum RhEHFrameType + { + RH_EH_FIRST_FRAME = 1, + RH_EH_FIRST_RETHROW_FRAME = 2, + } + + private static void AppendExceptionStackFrameViaClasslib(object exception, IntPtr ip, + ref bool isFirstRethrowFrame, ref bool isFirstFrame) + { + IntPtr pAppendStackFrame = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromCodeAddress(ip, + ClassLibFunctionId.AppendExceptionStackFrame); + + if (pAppendStackFrame != IntPtr.Zero) + { + int flags = (isFirstFrame ? (int)RhEHFrameType.RH_EH_FIRST_FRAME : 0) | + (isFirstRethrowFrame ? (int)RhEHFrameType.RH_EH_FIRST_RETHROW_FRAME : 0); + + try + { + ((delegate*)pAppendStackFrame)(exception, ip, flags); + } + catch when (true) + { + // disallow all exceptions leaking out of callbacks + } + + // Clear flags only if we called the function + isFirstRethrowFrame = false; + isFirstFrame = false; + } + } + + // Given an ExceptionID and an address pointing somewhere into a managed module, get + // an exception object of a type that the module containing the given address will understand. + // This finds the classlib-defined GetRuntimeException function and asks it for the exception object. + internal static Exception GetClasslibException(ExceptionIDs id, IntPtr address) + { + // Find the classlib function that will give us the exception object we want to throw. This + // is a RuntimeExport function from the classlib module, and is therefore managed-callable. + IntPtr pGetRuntimeExceptionFunction = + (IntPtr)InternalCalls.RhpGetClasslibFunctionFromCodeAddress(address, ClassLibFunctionId.GetRuntimeException); + + // Return the exception object we get from the classlib. + Exception? e = null; + try + { + e = ((delegate*)pGetRuntimeExceptionFunction)(id); + } + catch when (true) + { + // disallow all exceptions leaking out of callbacks + } + + // If the helper fails to yield an object, then we fail-fast. + if (e == null) + { + FailFastViaClasslib( + RhFailFastReason.ClassLibDidNotTranslateExceptionID, + null, + address); + } + + return e; + } + + // Given an ExceptionID and an MethodTable address, get an exception object of a type that the module containing + // the given address will understand. This finds the classlib-defined GetRuntimeException function and asks + // it for the exception object. + internal static Exception GetClasslibExceptionFromEEType(ExceptionIDs id, MethodTable* pEEType) + { + // Find the classlib function that will give us the exception object we want to throw. This + // is a RuntimeExport function from the classlib module, and is therefore managed-callable. + IntPtr pGetRuntimeExceptionFunction = IntPtr.Zero; + if (pEEType != null) + { + pGetRuntimeExceptionFunction = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType(pEEType, ClassLibFunctionId.GetRuntimeException); + } + + // Return the exception object we get from the classlib. + Exception? e = null; + try + { + e = ((delegate*)pGetRuntimeExceptionFunction)(id); + } + catch when (true) + { + // disallow all exceptions leaking out of callbacks + } + + // If the helper fails to yield an object, then we fail-fast. + if (e == null) + { + FailFastViaClasslib( + RhFailFastReason.ClassLibDidNotTranslateExceptionID, + null, + (IntPtr)pEEType); + } + + return e; + } + + // RhExceptionHandling_ functions are used to throw exceptions out of our asm helpers. We tail-call from + // the asm helpers to these functions, which performs the throw. The tail-call is important: it ensures that + // the stack is crawlable from within these functions. + [RuntimeExport("RhExceptionHandling_ThrowClasslibOverflowException")] + public static void ThrowClasslibOverflowException(IntPtr address) + { + // Throw the overflow exception defined by the classlib, using the return address of the asm helper + // to find the correct classlib. + + throw GetClasslibException(ExceptionIDs.Overflow, address); + } + + [RuntimeExport("RhExceptionHandling_ThrowClasslibDivideByZeroException")] + public static void ThrowClasslibDivideByZeroException(IntPtr address) + { + // Throw the divide by zero exception defined by the classlib, using the return address of the asm helper + // to find the correct classlib. + + throw GetClasslibException(ExceptionIDs.DivideByZero, address); + } + + [RuntimeExport("RhExceptionHandling_FailedAllocation")] + public static void FailedAllocation(EETypePtr pEEType, bool fIsOverflow) + { + ExceptionIDs exID = fIsOverflow ? ExceptionIDs.Overflow : ExceptionIDs.OutOfMemory; + + // Throw the out of memory exception defined by the classlib, using the input MethodTable* + // to find the correct classlib. + + throw pEEType.ToPointer()->GetClasslibException(exID); + } + +#if !INPLACE_RUNTIME + private static OutOfMemoryException s_theOOMException = new OutOfMemoryException(); + + // MRT exports GetRuntimeException for the few cases where we have a helper that throws an exception + // and may be called by either MRT or other classlibs and that helper needs to throw an exception. + // There are only a few cases where this happens now (the fast allocation helpers), so we limit the + // exception types that MRT will return. + [RuntimeExport("GetRuntimeException")] + public static Exception GetRuntimeException(ExceptionIDs id) + { + switch (id) + { + case ExceptionIDs.OutOfMemory: + // Throw a preallocated exception to avoid infinite recursion. + return s_theOOMException; + + case ExceptionIDs.Overflow: + return new OverflowException(); + + case ExceptionIDs.InvalidCast: + return new InvalidCastException(); + + default: + Debug.Assert(false, "unexpected ExceptionID"); + FallbackFailFast(RhFailFastReason.InternalError, null); + return null; + } + } +#endif + + private enum HwExceptionCode : uint + { + STATUS_REDHAWK_NULL_REFERENCE = 0x00000000u, + STATUS_REDHAWK_UNMANAGED_HELPER_NULL_REFERENCE = 0x00000042u, + STATUS_REDHAWK_THREAD_ABORT = 0x00000043u, + + STATUS_DATATYPE_MISALIGNMENT = 0x80000002u, + STATUS_ACCESS_VIOLATION = 0xC0000005u, + STATUS_INTEGER_DIVIDE_BY_ZERO = 0xC0000094u, + STATUS_INTEGER_OVERFLOW = 0xC0000095u, + } + + [StructLayout(LayoutKind.Explicit, Size = AsmOffsets.SIZEOF__PAL_LIMITED_CONTEXT)] + public struct PAL_LIMITED_CONTEXT + { + [FieldOffset(AsmOffsets.OFFSETOF__PAL_LIMITED_CONTEXT__IP)] + internal IntPtr IP; + // the rest of the struct is left unspecified. + } + + // N.B. -- These values are burned into the throw helper assembly code and are also known the the + // StackFrameIterator code. + [Flags] + internal enum ExKind : byte + { + None = 0, + Throw = 1, + HardwareFault = 2, + KindMask = 3, + + RethrowFlag = 4, + + SupersededFlag = 8, + + InstructionFaultFlag = 0x10 + } + + [StructLayout(LayoutKind.Explicit)] + public ref struct ExInfo + { + internal void Init(object exceptionObj, bool instructionFault = false) + { + // _pPrevExInfo -- set by asm helper + // _pExContext -- set by asm helper + // _passNumber -- set by asm helper + // _kind -- set by asm helper + // _idxCurClause -- set by asm helper + // _frameIter -- initialized explicitly during dispatch + + _exception = exceptionObj; + if (instructionFault) + _kind |= ExKind.InstructionFaultFlag; + _notifyDebuggerSP = UIntPtr.Zero; + } + + internal void Init(object exceptionObj, ref ExInfo rethrownExInfo) + { + // _pPrevExInfo -- set by asm helper + // _pExContext -- set by asm helper + // _passNumber -- set by asm helper + // _idxCurClause -- set by asm helper + // _frameIter -- initialized explicitly during dispatch + + _exception = exceptionObj; + _kind = rethrownExInfo._kind | ExKind.RethrowFlag; + _notifyDebuggerSP = UIntPtr.Zero; + } + + internal object ThrownException + { + get + { + return _exception; + } + } + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_pPrevExInfo)] + internal void* _pPrevExInfo; + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_pExContext)] + internal PAL_LIMITED_CONTEXT* _pExContext; + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_exception)] + private object _exception; // actual object reference, specially reported by GcScanRootsWorker + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_kind)] + internal ExKind _kind; + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_passNumber)] + internal byte _passNumber; + + // BEWARE: This field is used by the stackwalker to know if the dispatch code has reached the + // point at which a handler is called. In other words, it serves as an "is a handler + // active" state where '_idxCurClause == MaxTryRegionIdx' means 'no'. + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_idxCurClause)] + internal uint _idxCurClause; + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_frameIter)] + internal StackFrameIterator _frameIter; + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_notifyDebuggerSP)] + internal volatile UIntPtr _notifyDebuggerSP; + } + + // + // Called by RhpThrowHwEx + // + [RuntimeExport("RhThrowHwEx")] + public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) + { + // trigger a GC (only if gcstress) to ensure we can stackwalk at this point + GCStress.TriggerGC(); + + InternalCalls.RhpValidateExInfoStack(); + + IntPtr faultingCodeAddress = exInfo._pExContext->IP; + bool instructionFault = true; + ExceptionIDs exceptionId = default(ExceptionIDs); + Exception? exceptionToThrow = null; + + switch (exceptionCode) + { + case (uint)HwExceptionCode.STATUS_REDHAWK_NULL_REFERENCE: + exceptionId = ExceptionIDs.NullReference; + break; + + case (uint)HwExceptionCode.STATUS_REDHAWK_UNMANAGED_HELPER_NULL_REFERENCE: + // The write barrier where the actual fault happened has been unwound already. + // The IP of this fault needs to be treated as return address, not as IP of + // faulting instruction. + instructionFault = false; + exceptionId = ExceptionIDs.NullReference; + break; + + case (uint)HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT: + exceptionToThrow = InternalCalls.RhpGetThreadAbortException(); + break; + + case (uint)HwExceptionCode.STATUS_DATATYPE_MISALIGNMENT: + exceptionId = ExceptionIDs.DataMisaligned; + break; + + // N.B. -- AVs that have a read/write address lower than 64k are already transformed to + // HwExceptionCode.REDHAWK_NULL_REFERENCE prior to calling this routine. + case (uint)HwExceptionCode.STATUS_ACCESS_VIOLATION: + exceptionId = ExceptionIDs.AccessViolation; + break; + + case (uint)HwExceptionCode.STATUS_INTEGER_DIVIDE_BY_ZERO: + exceptionId = ExceptionIDs.DivideByZero; + break; + + case (uint)HwExceptionCode.STATUS_INTEGER_OVERFLOW: + exceptionId = ExceptionIDs.Overflow; + break; + + default: + // We don't wrap SEH exceptions from foreign code like CLR does, so we believe that we + // know the complete set of HW faults generated by managed code and do not need to handle + // this case. + FailFastViaClasslib(RhFailFastReason.InternalError, null, faultingCodeAddress); + break; + } + + if (exceptionId != default(ExceptionIDs)) + { + exceptionToThrow = GetClasslibException(exceptionId, faultingCodeAddress); + } + + exInfo.Init(exceptionToThrow!, instructionFault); + DispatchEx(ref exInfo._frameIter, ref exInfo, MaxTryRegionIdx); + FallbackFailFast(RhFailFastReason.InternalError, null); + } + + private const uint MaxTryRegionIdx = 0xFFFFFFFFu; + + [RuntimeExport("RhThrowEx")] + public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) + { + // trigger a GC (only if gcstress) to ensure we can stackwalk at this point + GCStress.TriggerGC(); + + InternalCalls.RhpValidateExInfoStack(); + + // Transform attempted throws of null to a throw of NullReferenceException. + if (exceptionObj == null) + { + IntPtr faultingCodeAddress = exInfo._pExContext->IP; + exceptionObj = GetClasslibException(ExceptionIDs.NullReference, faultingCodeAddress); + } + + exInfo.Init(exceptionObj); + DispatchEx(ref exInfo._frameIter, ref exInfo, MaxTryRegionIdx); + FallbackFailFast(RhFailFastReason.InternalError, null); + } + + [RuntimeExport("RhRethrow")] + public static void RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) + { + // trigger a GC (only if gcstress) to ensure we can stackwalk at this point + GCStress.TriggerGC(); + + InternalCalls.RhpValidateExInfoStack(); + + // We need to copy the exception object to this stack location because collided unwinds + // will cause the original stack location to go dead. + object rethrownException = activeExInfo.ThrownException; + + exInfo.Init(rethrownException, ref activeExInfo); + DispatchEx(ref exInfo._frameIter, ref exInfo, activeExInfo._idxCurClause); + FallbackFailFast(RhFailFastReason.InternalError, null); + } + + private static void DispatchEx(ref StackFrameIterator frameIter, ref ExInfo exInfo, uint startIdx) + { + Debug.Assert(exInfo._passNumber == 1, "expected asm throw routine to set the pass"); + object exceptionObj = exInfo.ThrownException; + + // ------------------------------------------------ + // + // First pass + // + // ------------------------------------------------ + UIntPtr handlingFrameSP = MaxSP; + byte* pCatchHandler = null; + uint catchingTryRegionIdx = MaxTryRegionIdx; + + bool isFirstRethrowFrame = (startIdx != MaxTryRegionIdx); + bool isFirstFrame = true; + + byte* prevControlPC = null; + byte* prevOriginalPC = null; + UIntPtr prevFramePtr = UIntPtr.Zero; + bool unwoundReversePInvoke = false; + + bool isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0); + Debug.Assert(isValid, "RhThrowEx called with an unexpected context"); + + OnFirstChanceExceptionViaClassLib(exceptionObj); + + for (; isValid; isValid = frameIter.Next(&startIdx, &unwoundReversePInvoke)) + { + // For GC stackwalking, we'll happily walk across native code blocks, but for EH dispatch, we + // disallow dispatching exceptions across native code. + if (unwoundReversePInvoke) + break; + + prevControlPC = frameIter.ControlPC; + prevOriginalPC = frameIter.OriginalControlPC; + + DebugScanCallFrame(exInfo._passNumber, frameIter.ControlPC, frameIter.SP); + + UpdateStackTrace(exceptionObj, exInfo._frameIter.FramePointer, (IntPtr)frameIter.OriginalControlPC, ref isFirstRethrowFrame, ref prevFramePtr, ref isFirstFrame); + + byte* pHandler; + if (FindFirstPassHandler(exceptionObj, startIdx, ref frameIter, + out catchingTryRegionIdx, out pHandler)) + { + handlingFrameSP = frameIter.SP; + pCatchHandler = pHandler; + + DebugVerifyHandlingFrame(handlingFrameSP); + break; + } + } + + if (pCatchHandler == null) + { + OnUnhandledExceptionViaClassLib(exceptionObj); + + UnhandledExceptionFailFastViaClasslib( + RhFailFastReason.PN_UnhandledException, + exceptionObj, + (IntPtr)prevOriginalPC, // IP of the last frame that did not handle the exception + ref exInfo); + } + + // We FailFast above if the exception goes unhandled. Therefore, we cannot run the second pass + // without a catch handler. + Debug.Assert(pCatchHandler != null, "We should have a handler if we're starting the second pass"); + + // ------------------------------------------------ + // + // Second pass + // + // ------------------------------------------------ + + // Due to the stackwalker logic, we cannot tolerate triggering a GC from the dispatch code once we + // are in the 2nd pass. This is because the stackwalker applies a particular unwind semantic to + // 'collapse' funclets which gets confused when we walk out of the dispatch code and encounter the + // 'main body' without first encountering the funclet. The thunks used to invoke 2nd-pass + // funclets will always toggle this mode off before invoking them. + InternalCalls.RhpSetThreadDoNotTriggerGC(); + + exInfo._passNumber = 2; + startIdx = MaxTryRegionIdx; + isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0); + for (; isValid && ((byte*)frameIter.SP <= (byte*)handlingFrameSP); isValid = frameIter.Next(&startIdx)) + { + Debug.Assert(isValid, "second-pass EH unwind failed unexpectedly"); + DebugScanCallFrame(exInfo._passNumber, frameIter.ControlPC, frameIter.SP); + + if ((frameIter.SP == handlingFrameSP) +#if TARGET_ARM64 + && (frameIter.ControlPC == prevControlPC) +#endif + ) + { + // invoke only a partial second-pass here... + InvokeSecondPass(ref exInfo, startIdx, catchingTryRegionIdx); + break; + } + + InvokeSecondPass(ref exInfo, startIdx); + } + + // ------------------------------------------------ + // + // Call the handler and resume execution + // + // ------------------------------------------------ + exInfo._idxCurClause = catchingTryRegionIdx; + InternalCalls.RhpCallCatchFunclet( + exceptionObj, pCatchHandler, frameIter.RegisterSet, ref exInfo); + // currently, RhpCallCatchFunclet will resume after the catch + Debug.Assert(false, "unreachable"); + FallbackFailFast(RhFailFastReason.InternalError, null); + } + + [System.Diagnostics.Conditional("DEBUG")] + private static void DebugScanCallFrame(int passNumber, byte* ip, UIntPtr sp) + { + Debug.Assert(ip != null, "IP address must not be null"); + } + + [System.Diagnostics.Conditional("DEBUG")] + private static void DebugVerifyHandlingFrame(UIntPtr handlingFrameSP) + { + Debug.Assert(handlingFrameSP != MaxSP, "Handling frame must have an SP value"); + Debug.Assert(((UIntPtr*)handlingFrameSP) > &handlingFrameSP, + "Handling frame must have a valid stack frame pointer"); + } + + private static void UpdateStackTrace(object exceptionObj, UIntPtr curFramePtr, IntPtr ip, + ref bool isFirstRethrowFrame, ref UIntPtr prevFramePtr, ref bool isFirstFrame) + { + // We use the fact that all funclet stack frames belonging to the same logical method activation + // will have the same FramePointer value. Additionally, the stackwalker will return a sequence of + // callbacks for all the funclet stack frames, one right after the other. The classlib doesn't + // want to know about funclets, so we strip them out by only reporting the first frame of a + // sequence of funclets. This is correct because the leafmost funclet is first in the sequence + // and corresponds to the current 'IP state' of the method. + if ((prevFramePtr == UIntPtr.Zero) || (curFramePtr != prevFramePtr)) + { + AppendExceptionStackFrameViaClasslib(exceptionObj, ip, + ref isFirstRethrowFrame, ref isFirstFrame); + } + prevFramePtr = curFramePtr; + } + + private static bool FindFirstPassHandler(object exception, uint idxStart, + ref StackFrameIterator frameIter, out uint tryRegionIdx, out byte* pHandler) + { + pHandler = null; + tryRegionIdx = MaxTryRegionIdx; + + EHEnum ehEnum; + byte* pbMethodStartAddress; + if (!InternalCalls.RhpEHEnumInitFromStackFrameIterator(ref frameIter, &pbMethodStartAddress, &ehEnum)) + return false; + + byte* pbControlPC = frameIter.ControlPC; + + uint codeOffset = (uint)(pbControlPC - pbMethodStartAddress); + + uint lastTryStart = 0, lastTryEnd = 0; + + // Search the clauses for one that contains the current offset. + RhEHClause ehClause; + for (uint curIdx = 0; InternalCalls.RhpEHEnumNext(&ehEnum, &ehClause); curIdx++) + { + // + // Skip to the starting try region. This is used by collided unwinds and rethrows to pickup where + // the previous dispatch left off. + // + if (idxStart != MaxTryRegionIdx) + { + if (curIdx <= idxStart) + { + lastTryStart = ehClause._tryStartOffset; lastTryEnd = ehClause._tryEndOffset; + continue; + } + + // Now, we continue skipping while the try region is identical to the one that invoked the + // previous dispatch. + if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd)) + continue; + + // We are done skipping. This is required to handle empty finally block markers that are used + // to separate runs of different try blocks with same native code offsets. + idxStart = MaxTryRegionIdx; + } + + RhEHClauseKind clauseKind = ehClause._clauseKind; + + if (((clauseKind != RhEHClauseKind.RH_EH_CLAUSE_TYPED) && + (clauseKind != RhEHClauseKind.RH_EH_CLAUSE_FILTER)) + || !ehClause.ContainsCodeOffset(codeOffset)) + { + continue; + } + + // Found a containing clause. Because of the order of the clauses, we know this is the + // most containing. + if (clauseKind == RhEHClauseKind.RH_EH_CLAUSE_TYPED) + { + if (ShouldTypedClauseCatchThisException(exception, (MethodTable*)ehClause._pTargetType)) + { + pHandler = ehClause._handlerAddress; + tryRegionIdx = curIdx; + return true; + } + } + else + { + byte* pFilterFunclet = ehClause._filterAddress; + bool shouldInvokeHandler = + InternalCalls.RhpCallFilterFunclet(exception, pFilterFunclet, frameIter.RegisterSet); + + if (shouldInvokeHandler) + { + pHandler = ehClause._handlerAddress; + tryRegionIdx = curIdx; + return true; + } + } + } + + return false; + } + +#if DEBUG && !INPLACE_RUNTIME + private static MethodTable* s_pLowLevelObjectType; + private static void AssertNotRuntimeObject(MethodTable* pClauseType) + { + // + // The C# try { } catch { } clause expands into a typed catch of System.Object. + // Since runtime has its own definition of System.Object, try { } catch { } might not do what + // was intended (catch all exceptions). + // + // This assertion is making sure we don't use try { } catch { } within the runtime. + // The runtime codebase should either use try { } catch (Exception) { } for exception types + // from the runtime or a try { } catch when (true) { } to catch all exceptions. + // + + if (s_pLowLevelObjectType == null) + { + // Allocating might fail, but since this is just a debug assert, it's probably fine. + s_pLowLevelObjectType = new System.Object().MethodTable; + } + + Debug.Assert(!pClauseType->IsEquivalentTo(s_pLowLevelObjectType)); + } +#endif // DEBUG && !INPLACE_RUNTIME + + + private static bool ShouldTypedClauseCatchThisException(object exception, MethodTable* pClauseType) + { +#if DEBUG && !INPLACE_RUNTIME + AssertNotRuntimeObject(pClauseType); +#endif + + return TypeCast.IsInstanceOfClass(pClauseType, exception) != null; + } + + private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart) + { + InvokeSecondPass(ref exInfo, idxStart, MaxTryRegionIdx); + } + private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart, uint idxLimit) + { + EHEnum ehEnum; + byte* pbMethodStartAddress; + if (!InternalCalls.RhpEHEnumInitFromStackFrameIterator(ref exInfo._frameIter, &pbMethodStartAddress, &ehEnum)) + return; + + byte* pbControlPC = exInfo._frameIter.ControlPC; + + uint codeOffset = (uint)(pbControlPC - pbMethodStartAddress); + + uint lastTryStart = 0, lastTryEnd = 0; + + // Search the clauses for one that contains the current offset. + RhEHClause ehClause; + for (uint curIdx = 0; InternalCalls.RhpEHEnumNext(&ehEnum, &ehClause) && curIdx < idxLimit; curIdx++) + { + // + // Skip to the starting try region. This is used by collided unwinds and rethrows to pickup where + // the previous dispatch left off. + // + if (idxStart != MaxTryRegionIdx) + { + if (curIdx <= idxStart) + { + lastTryStart = ehClause._tryStartOffset; lastTryEnd = ehClause._tryEndOffset; + continue; + } + + // Now, we continue skipping while the try region is identical to the one that invoked the + // previous dispatch. + if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd)) + continue; + + // We are done skipping. This is required to handle empty finally block markers that are used + // to separate runs of different try blocks with same native code offsets. + idxStart = MaxTryRegionIdx; + } + + RhEHClauseKind clauseKind = ehClause._clauseKind; + + if ((clauseKind != RhEHClauseKind.RH_EH_CLAUSE_FAULT) + || !ehClause.ContainsCodeOffset(codeOffset)) + { + continue; + } + + // Found a containing clause. Because of the order of the clauses, we know this is the + // most containing. + + // N.B. -- We need to suppress GC "in-between" calls to finallys in this loop because we do + // not have the correct next-execution point live on the stack and, therefore, may cause a GC + // hole if we allow a GC between invocation of finally funclets (i.e. after one has returned + // here to the dispatcher, but before the next one is invoked). Once they are running, it's + // fine for them to trigger a GC, obviously. + // + // As a result, RhpCallFinallyFunclet will set this state in the runtime upon return from the + // funclet, and we need to reset it if/when we fall out of the loop and we know that the + // method will no longer get any more GC callbacks. + + byte* pFinallyHandler = ehClause._handlerAddress; + exInfo._idxCurClause = curIdx; + InternalCalls.RhpCallFinallyFunclet(pFinallyHandler, exInfo._frameIter.RegisterSet); + exInfo._idxCurClause = MaxTryRegionIdx; + } + } + + [UnmanagedCallersOnly(EntryPoint = "RhpFailFastForPInvokeExceptionPreemp", CallConvs = new Type[] { typeof(CallConvCdecl) })] + public static void RhpFailFastForPInvokeExceptionPreemp(IntPtr PInvokeCallsiteReturnAddr, void* pExceptionRecord, void* pContextRecord) + { + FailFastViaClasslib(RhFailFastReason.PN_UnhandledExceptionFromPInvoke, null, PInvokeCallsiteReturnAddr); + } + [RuntimeExport("RhpFailFastForPInvokeExceptionCoop")] + public static void RhpFailFastForPInvokeExceptionCoop(IntPtr classlibBreadcrumb, void* pExceptionRecord, void* pContextRecord) + { + FailFastViaClasslib(RhFailFastReason.PN_UnhandledExceptionFromPInvoke, null, classlibBreadcrumb); + } + } // static class EH +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionIDs.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionIDs.cs new file mode 100644 index 00000000000000..022a964020c182 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionIDs.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime +{ + public enum ExceptionIDs + { + OutOfMemory = 1, + Arithmetic = 2, + ArrayTypeMismatch = 3, + DivideByZero = 4, + IndexOutOfRange = 5, + InvalidCast = 6, + Overflow = 7, + NullReference = 8, + AccessViolation = 9, + DataMisaligned = 10, + EntrypointNotFound = 11, + AmbiguousImplementation = 12, + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/GCStress.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/GCStress.cs new file mode 100644 index 00000000000000..3478cf3353309d --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/GCStress.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Runtime +{ + internal class GCStress + { + [UnmanagedCallersOnly(EntryPoint = "RhGcStress_Initialize", CallConvs = new Type[] { typeof(CallConvCdecl) })] + public static void Initialize() + { +#if FEATURE_GC_STRESS + // This method is called via binder-injected code in a module's DllMain. The OS guarantees that + // only one thread at a time is in any DllMain, so we should be thread-safe as a result. + if (Initialized) + return; + + Initialized = true; + + Head = new GCStress(); + Tail = Head; + + int size = 10; + for (int i = 0; i < size; i++) + { + Tail.Next = new GCStress(); + Tail = Tail.Next; + } + + // drop the first element + Head = Head.Next; + + // notify redhawku.dll + InternalCalls.RhpInitializeGcStress(); +#endif // FEATURE_GC_STRESS + } + + [System.Diagnostics.Conditional("FEATURE_GC_STRESS")] + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void TriggerGC() + { +#if FEATURE_GC_STRESS + if (GCStress.Initialized) + InternalCalls.RhCollect(-1, InternalGCCollectionMode.Blocking); +#endif + } + + ~GCStress() + { + // drop the first element + Head = Head.Next; + + // create and link a new element at the end of the list + Tail.Next = new GCStress(); + Tail = Tail.Next; + } + +#if FEATURE_GC_STRESS + internal static bool Initialized { get; private set; } +#endif // FEATURE_GC_STRESS + + private static GCStress Head; + private static GCStress Tail; + + private GCStress Next; + + private GCStress() + { + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs new file mode 100644 index 00000000000000..364994447f474f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs @@ -0,0 +1,322 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This is where we group together all the internal calls. +// + +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Internal.Runtime; + +namespace System.Runtime +{ + internal enum DispatchCellType + { + InterfaceAndSlot = 0x0, + MetadataToken = 0x1, + VTableOffset = 0x2, + } + + internal struct DispatchCellInfo + { + public DispatchCellType CellType; + public EETypePtr InterfaceType; + public ushort InterfaceSlot; + public byte HasCache; + public uint MetadataToken; + public uint VTableOffset; + } + + // Constants used with RhpGetClasslibFunction, to indicate which classlib function + // we are interested in. + // Note: make sure you change the def in ICodeManager.h if you change this! + internal enum ClassLibFunctionId + { + GetRuntimeException = 0, + FailFast = 1, + // UnhandledExceptionHandler = 2, // unused + AppendExceptionStackFrame = 3, + // unused = 4, + GetSystemArrayEEType = 5, + OnFirstChance = 6, + OnUnhandledException = 7, + IDynamicCastableIsInterfaceImplemented = 8, + IDynamicCastableGetInterfaceImplementation = 9, + } + + internal static class InternalCalls + { + // + // internalcalls for System.GC. + // + + // Force a garbage collection. + [RuntimeExport("RhCollect")] + internal static void RhCollect(int generation, InternalGCCollectionMode mode) + { + RhpCollect(generation, mode); + } + + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + private static extern void RhpCollect(int generation, InternalGCCollectionMode mode); + + [RuntimeExport("RhGetGcTotalMemory")] + internal static long RhGetGcTotalMemory() + { + return RhpGetGcTotalMemory(); + } + + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + private static extern long RhpGetGcTotalMemory(); + + [RuntimeExport("RhStartNoGCRegion")] + internal static int RhStartNoGCRegion(long totalSize, bool hasLohSize, long lohSize, bool disallowFullBlockingGC) + { + return RhpStartNoGCRegion(totalSize, hasLohSize, lohSize, disallowFullBlockingGC); + } + + [RuntimeExport("RhEndNoGCRegion")] + internal static int RhEndNoGCRegion() + { + return RhpEndNoGCRegion(); + } + + // + // internalcalls for System.Runtime.__Finalizer. + // + + // Fetch next object which needs finalization or return null if we've reached the end of the list. + [RuntimeImport(Redhawk.BaseName, "RhpGetNextFinalizableObject")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern object RhpGetNextFinalizableObject(); + + // + // internalcalls for System.Runtime.InteropServices.GCHandle. + // + + // Allocate handle. + [RuntimeImport(Redhawk.BaseName, "RhpHandleAlloc")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern IntPtr RhpHandleAlloc(object value, GCHandleType type); + + [RuntimeImport(Redhawk.BaseName, "RhHandleGet")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern object RhHandleGet(IntPtr handle); + + [RuntimeImport(Redhawk.BaseName, "RhHandleSet")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern IntPtr RhHandleSet(IntPtr handle, object value); + + // + // internal calls for allocation + // + [RuntimeImport(Redhawk.BaseName, "RhpNewFast")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe object RhpNewFast(MethodTable* pEEType); // BEWARE: not for finalizable objects! + + [RuntimeImport(Redhawk.BaseName, "RhpNewFinalizable")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe object RhpNewFinalizable(MethodTable* pEEType); + + [RuntimeImport(Redhawk.BaseName, "RhpNewArray")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe object RhpNewArray(MethodTable* pEEType, int length); + +#if FEATURE_64BIT_ALIGNMENT + [RuntimeImport(Redhawk.BaseName, "RhpNewFastAlign8")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe object RhpNewFastAlign8(MethodTable * pEEType); // BEWARE: not for finalizable objects! + + [RuntimeImport(Redhawk.BaseName, "RhpNewFinalizableAlign8")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe object RhpNewFinalizableAlign8(MethodTable* pEEType); + + [RuntimeImport(Redhawk.BaseName, "RhpNewArrayAlign8")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe object RhpNewArrayAlign8(MethodTable* pEEType, int length); + + [RuntimeImport(Redhawk.BaseName, "RhpNewFastMisalign")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe object RhpNewFastMisalign(MethodTable * pEEType); +#endif // FEATURE_64BIT_ALIGNMENT + + [RuntimeImport(Redhawk.BaseName, "RhpCopyObjectContents")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe void RhpCopyObjectContents(object objDest, object objSrc); + + [RuntimeImport(Redhawk.BaseName, "RhpAssignRef")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe void RhpAssignRef(ref object address, object obj); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(Redhawk.BaseName, "RhpInitMultibyte")] + internal static extern unsafe ref byte RhpInitMultibyte(ref byte dmem, int c, nuint size); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(Redhawk.BaseName, "memmove")] + internal static extern unsafe void* memmove(byte* dmem, byte* smem, nuint size); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(Redhawk.BaseName, "RhBulkMoveWithWriteBarrier")] + internal static extern unsafe void RhBulkMoveWithWriteBarrier(ref byte dmem, ref byte smem, nuint size); + +#if FEATURE_GC_STRESS + // + // internal calls for GC stress + // + [RuntimeImport(Redhawk.BaseName, "RhpInitializeGcStress")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe void RhpInitializeGcStress(); +#endif // FEATURE_GC_STRESS + + [RuntimeImport(Redhawk.BaseName, "RhpEHEnumInitFromStackFrameIterator")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe bool RhpEHEnumInitFromStackFrameIterator(ref StackFrameIterator pFrameIter, byte** pMethodStartAddress, void* pEHEnum); + + [RuntimeImport(Redhawk.BaseName, "RhpEHEnumNext")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe bool RhpEHEnumNext(void* pEHEnum, void* pEHClause); + + [RuntimeImport(Redhawk.BaseName, "RhpGetDispatchCellInfo")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe void RhpGetDispatchCellInfo(IntPtr pCell, out DispatchCellInfo newCellInfo); + + [RuntimeImport(Redhawk.BaseName, "RhpSearchDispatchCellCache")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe IntPtr RhpSearchDispatchCellCache(IntPtr pCell, MethodTable* pInstanceType); + + [RuntimeImport(Redhawk.BaseName, "RhpUpdateDispatchCellCache")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe IntPtr RhpUpdateDispatchCellCache(IntPtr pCell, IntPtr pTargetCode, MethodTable* pInstanceType, ref DispatchCellInfo newCellInfo); + + [RuntimeImport(Redhawk.BaseName, "RhpGetClasslibFunctionFromCodeAddress")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe void* RhpGetClasslibFunctionFromCodeAddress(IntPtr address, ClassLibFunctionId id); + + [RuntimeImport(Redhawk.BaseName, "RhpGetClasslibFunctionFromEEType")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe void* RhpGetClasslibFunctionFromEEType(MethodTable* pEEType, ClassLibFunctionId id); + + // + // StackFrameIterator + // + + [RuntimeImport(Redhawk.BaseName, "RhpSfiInit")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe bool RhpSfiInit(ref StackFrameIterator pThis, void* pStackwalkCtx, bool instructionFault); + + [RuntimeImport(Redhawk.BaseName, "RhpSfiNext")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe bool RhpSfiNext(ref StackFrameIterator pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke); + + // + // Miscellaneous helpers. + // + + [RuntimeImport(Redhawk.BaseName, "RhpCallCatchFunclet")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe IntPtr RhpCallCatchFunclet( + object exceptionObj, byte* pHandlerIP, void* pvRegDisplay, ref EH.ExInfo exInfo); + + [RuntimeImport(Redhawk.BaseName, "RhpCallFinallyFunclet")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe void RhpCallFinallyFunclet(byte* pHandlerIP, void* pvRegDisplay); + + [RuntimeImport(Redhawk.BaseName, "RhpCallFilterFunclet")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe bool RhpCallFilterFunclet( + object exceptionObj, byte* pFilterIP, void* pvRegDisplay); + + [RuntimeImport(Redhawk.BaseName, "RhpFallbackFailFast")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe void RhpFallbackFailFast(); + + [RuntimeImport(Redhawk.BaseName, "RhpSetThreadDoNotTriggerGC")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void RhpSetThreadDoNotTriggerGC(); + + [System.Diagnostics.Conditional("DEBUG")] + [RuntimeImport(Redhawk.BaseName, "RhpValidateExInfoStack")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void RhpValidateExInfoStack(); + + [RuntimeImport(Redhawk.BaseName, "RhpCopyContextFromExInfo")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe void RhpCopyContextFromExInfo(void* pOSContext, int cbOSContext, EH.PAL_LIMITED_CONTEXT* pPalContext); + + [RuntimeImport(Redhawk.BaseName, "RhpGetNumThunkBlocksPerMapping")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern int RhpGetNumThunkBlocksPerMapping(); + + [RuntimeImport(Redhawk.BaseName, "RhpGetNumThunksPerBlock")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern int RhpGetNumThunksPerBlock(); + + [RuntimeImport(Redhawk.BaseName, "RhpGetThunkSize")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern int RhpGetThunkSize(); + + [RuntimeImport(Redhawk.BaseName, "RhpGetThunkDataBlockAddress")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern IntPtr RhpGetThunkDataBlockAddress(IntPtr thunkStubAddress); + + [RuntimeImport(Redhawk.BaseName, "RhpGetThunkStubsBlockAddress")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern IntPtr RhpGetThunkStubsBlockAddress(IntPtr thunkDataAddress); + + [RuntimeImport(Redhawk.BaseName, "RhpGetThunkBlockSize")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern int RhpGetThunkBlockSize(); + + [RuntimeImport(Redhawk.BaseName, "RhpGetThreadAbortException")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern Exception RhpGetThreadAbortException(); + + //------------------------------------------------------------------------------------------------------------ + // PInvoke-based internal calls + // + // These either do not need to be called in cooperative mode or, in some cases, MUST be called in preemptive + // mode. Note that they must use the Cdecl calling convention due to a limitation in our .obj file linking + // support. + //------------------------------------------------------------------------------------------------------------ + + // Block the current thread until at least one object needs to be finalized (returns true) or + // memory is low (returns false and the finalizer thread should initiate a garbage collection). + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint RhpWaitForFinalizerRequest(); + + // Indicate that the current round of finalizations is complete. + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + internal static extern void RhpSignalFinalizationComplete(); + + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + internal static extern void RhpAcquireCastCacheLock(); + + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + internal static extern void RhpReleaseCastCacheLock(); + + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + internal static extern ulong PalGetTickCount64(); + + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + internal static extern void RhpAcquireThunkPoolLock(); + + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + internal static extern void RhpReleaseThunkPoolLock(); + + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr RhAllocateThunksMapping(); + + // Enters a no GC region, possibly doing a blocking GC if there is not enough + // memory available to satisfy the caller's request. + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + internal static extern int RhpStartNoGCRegion(long totalSize, bool hasLohSize, long lohSize, bool disallowFullBlockingGC); + + // Exits a no GC region, possibly doing a GC to clean up the garbage that + // the caller allocated. + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + internal static extern int RhpEndNoGCRegion(); + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/CallingConvention.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/CallingConvention.cs new file mode 100644 index 00000000000000..05e0036eee9d36 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/CallingConvention.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + // Used for the CallingConvention named argument to the DllImport and UnmanagedCallersOnly attribute + public enum CallingConvention + { + Winapi = 1, + Cdecl = 2, + StdCall = 3, + ThisCall = 4, + FastCall = 5, + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/CharSet.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/CharSet.cs new file mode 100644 index 00000000000000..d1cb6d08769675 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/CharSet.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + // Use this in P/Direct function prototypes to specify + // which character set to use when marshalling Strings. + // Using Ansi will marshal the strings as 1 byte char*'s. + // Using Unicode will marshal the strings as 2 byte wchar*'s. + // Generally you probably want to use Auto, which does the + // right thing 99% of the time. + + public enum CharSet + { + None = 1, // User didn't specify how to marshal strings. + Ansi = 2, // Strings should be marshalled as ANSI 1 byte chars. + Unicode = 3, // Strings should be marshalled as Unicode 2 byte chars. + Auto = 4, // Marshal Strings in the right way for the target system. + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs new file mode 100644 index 00000000000000..2f5df61b3cc10f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Method)] + public sealed class DllImportAttribute : Attribute + { + public CallingConvention CallingConvention; + + public string EntryPoint; + + public bool ExactSpelling; + + public DllImportAttribute(string dllName) + { + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/FieldOffsetAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/FieldOffsetAttribute.cs new file mode 100644 index 00000000000000..59833e979f910c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/FieldOffsetAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + public sealed class FieldOffsetAttribute : Attribute + { + public FieldOffsetAttribute(int offset) + { + } + public int Value { get { return 0; } } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/GCHandleType.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/GCHandleType.cs new file mode 100644 index 00000000000000..e93d611aef2490 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/GCHandleType.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + public enum GCHandleType + { + Weak = 0, + WeakTrackResurrection = 1, + Normal = 2, + Pinned = 3 // NOTE: not implemented here + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/LayoutKind.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/LayoutKind.cs new file mode 100644 index 00000000000000..faa81e2c990d35 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/LayoutKind.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + // Used in the StructLayoutAttribute class + public enum LayoutKind + { + Sequential = 0, // 0x00000008, + Explicit = 2, // 0x00000010, + Auto = 3, // 0x00000000, + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/OutAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/OutAttribute.cs new file mode 100644 index 00000000000000..97838bea3476c1 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/OutAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + // Not used in Redhawk. Only here as C# compiler requires it + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class OutAttribute : Attribute + { + public OutAttribute() + { + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/StructLayoutAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/StructLayoutAttribute.cs new file mode 100644 index 00000000000000..5545b377bf741a --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/StructLayoutAttribute.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] + public sealed class StructLayoutAttribute : Attribute + { + public StructLayoutAttribute(LayoutKind layoutKind) + { + } + + // These fields are expected by C# compiler, + // so just disable the 'unused' warning +#pragma warning disable 649 + public LayoutKind Value; + public int Pack; + public int Size; + public CharSet CharSet; + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedCallersOnlyAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedCallersOnlyAttribute.cs new file mode 100644 index 00000000000000..790d2f0fe219a5 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedCallersOnlyAttribute.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + /// + /// Any method marked with can be directly called from + /// native code. The function token can be loaded to a local variable using the address-of operator + /// in C# and passed as a callback to a native method. + /// + /// + /// Methods marked with this attribute have the following restrictions: + /// * Method must be marked "static". + /// * Must not be called from managed code. + /// * Must only have blittable arguments. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class UnmanagedCallersOnlyAttribute : Attribute + { + public UnmanagedCallersOnlyAttribute() + { + } + + /// + /// Optional. If omitted, the runtime will use the default platform calling convention. + /// + /// + /// Supplied types must be from the official "System.Runtime.CompilerServices" namespace and + /// be of the form "CallConvXXX". + /// + public Type[]? CallConvs; + + /// + /// Optional. If omitted, no named export is emitted during compilation. + /// + public string? EntryPoint; + } +} + +namespace System.Runtime.CompilerServices +{ + public class CallConvCdecl + { + public CallConvCdecl() { } + } + public class CallConvFastcall + { + public CallConvFastcall() { } + } + public class CallConvStdcall + { + public CallConvStdcall() { } + } + public class CallConvThiscall + { + public CallConvThiscall() { } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedType.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedType.cs new file mode 100644 index 00000000000000..980cb229622cdc --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnmanagedType.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + public class UnmanagedType { } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnsafeGCHandle.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnsafeGCHandle.cs new file mode 100644 index 00000000000000..e19452064ea4bd --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/UnsafeGCHandle.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Diagnostics; + +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// + /// Limited implementation of GCHandle. Only implements as much as is currently used. + /// + [StructLayout(LayoutKind.Sequential)] + public struct UnsafeGCHandle + { + // The actual integer handle value that the EE uses internally. + private IntPtr _handle; + + // Allocate a handle storing the object and the type. + private UnsafeGCHandle(object value, GCHandleType type) + { + Debug.Assert((uint)type <= (uint)GCHandleType.Normal, "unexpected handle type"); + + _handle = InternalCalls.RhpHandleAlloc(value, type); + if (_handle == IntPtr.Zero) + throw new OutOfMemoryException(); + } + + public static UnsafeGCHandle Alloc(object value, GCHandleType type) + { + return new UnsafeGCHandle(value, type); + } + + // Target property - allows getting / updating of the handle's referent. + public unsafe object Target + { + get + { + Debug.Assert(IsAllocated, "handle isn't initialized"); +#if DEBUG + // The runtime performs additional checks in debug builds + return InternalCalls.RhHandleGet(_handle); +#else + return Unsafe.As(ref *(IntPtr*)_handle); +#endif + } + + set + { + Debug.Assert(IsAllocated, "handle isn't initialized"); + InternalCalls.RhHandleSet(_handle, value); + } + } + + // Determine whether this handle has been allocated or not. + public bool IsAllocated + { + get + { + return _handle != default(IntPtr); + } + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/MethodTable.Runtime.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/MethodTable.Runtime.cs new file mode 100644 index 00000000000000..8bf93cb40c4442 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/MethodTable.Runtime.cs @@ -0,0 +1,162 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime; +using Internal.Runtime.CompilerServices; + +namespace Internal.Runtime +{ + // Extensions to MethodTable that are specific to the use in Runtime.Base. + internal unsafe partial struct MethodTable + { + internal MethodTable* GetArrayEEType() + { +#if INPLACE_RUNTIME + return EETypePtr.EETypePtrOf().ToPointer(); +#else + fixed (MethodTable* pThis = &this) + { + void* pGetArrayEEType = InternalCalls.RhpGetClasslibFunctionFromEEType(new IntPtr(pThis), ClassLibFunctionId.GetSystemArrayEEType); + return ((delegate* )pGetArrayEEType)(); + } +#endif + } + + internal Exception GetClasslibException(ExceptionIDs id) + { +#if INPLACE_RUNTIME + return RuntimeExceptionHelpers.GetRuntimeException(id); +#else + DynamicModule* dynamicModule = this.DynamicModule; + if (dynamicModule != null) + { + delegate* getRuntimeException = dynamicModule->GetRuntimeException; + if (getRuntimeException != null) + { + return getRuntimeException(id); + } + } + if (IsParameterizedType) + { + return RelatedParameterType->GetClasslibException(id); + } + + return EH.GetClasslibExceptionFromEEType(id, GetAssociatedModuleAddress()); +#endif + } + + internal IntPtr GetClasslibFunction(ClassLibFunctionId id) + { + return (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType((MethodTable*)Unsafe.AsPointer(ref this), id); + } + + internal void SetToCloneOf(MethodTable* pOrigType) + { + Debug.Assert((_usFlags & (ushort)EETypeFlags.EETypeKindMask) == 0, "should be a canonical type"); + _usFlags |= (ushort)EETypeKind.ClonedEEType; + _relatedType._pCanonicalType = pOrigType; + } + + // Returns an address in the module most closely associated with this MethodTable that can be handed to + // EH.GetClasslibException and use to locate the compute the correct exception type. In most cases + // this is just the MethodTable pointer itself, but when this type represents a generic that has been + // unified at runtime (and thus the MethodTable pointer resides in the process heap rather than a specific + // module) we need to do some work. + internal unsafe MethodTable* GetAssociatedModuleAddress() + { + fixed (MethodTable* pThis = &this) + { + if (!IsDynamicType) + return pThis; + + // There are currently four types of runtime allocated EETypes, arrays, pointers, byrefs, and generic types. + // Arrays/Pointers/ByRefs can be handled by looking at their element type. + if (IsParameterizedType) + return pThis->RelatedParameterType->GetAssociatedModuleAddress(); + + if (!IsGeneric) + { + // No way to resolve module information for a non-generic dynamic type. + return null; + } + + // Generic types are trickier. Often we could look at the parent type (since eventually it + // would derive from the class library's System.Object which is definitely not runtime + // allocated). But this breaks down for generic interfaces. Instead we fetch the generic + // instantiation information and use the generic type definition, which will always be module + // local. We know this lookup will succeed since we're dealing with a unified generic type + // and the unification process requires this metadata. + MethodTable* pGenericType = pThis->GenericDefinition; + + Debug.Assert(pGenericType != null, "Generic type expected"); + + return pGenericType; + } + } + + /// + /// Return true if type is good for simple casting : canonical, no related type via IAT, no generic variance + /// + internal bool SimpleCasting() + { + return (_usFlags & (ushort)EETypeFlags.ComplexCastingMask) == (ushort)EETypeKind.CanonicalEEType; + } + + /// + /// Return true if both types are good for simple casting: canonical, no related type via IAT, no generic variance + /// + internal static bool BothSimpleCasting(MethodTable* pThis, MethodTable* pOther) + { + return ((pThis->_usFlags | pOther->_usFlags) & (ushort)EETypeFlags.ComplexCastingMask) == (ushort)EETypeKind.CanonicalEEType; + } + + internal bool IsEquivalentTo(MethodTable* pOtherEEType) + { + fixed (MethodTable* pThis = &this) + { + if (pThis == pOtherEEType) + return true; + + MethodTable* pThisEEType = pThis; + + if (pThisEEType->IsCloned) + pThisEEType = pThisEEType->CanonicalEEType; + + if (pOtherEEType->IsCloned) + pOtherEEType = pOtherEEType->CanonicalEEType; + + if (pThisEEType == pOtherEEType) + return true; + + if (pThisEEType->IsParameterizedType && pOtherEEType->IsParameterizedType) + { + return pThisEEType->RelatedParameterType->IsEquivalentTo(pOtherEEType->RelatedParameterType) && + pThisEEType->ParameterizedTypeShape == pOtherEEType->ParameterizedTypeShape; + } + } + + return false; + } + } + + internal static class WellKnownEETypes + { + // Returns true if the passed in MethodTable is the MethodTable for System.Object + // This is recognized by the fact that System.Object and interfaces are the only ones without a base type + internal static unsafe bool IsSystemObject(MethodTable* pEEType) + { + if (pEEType->IsArray) + return false; + return (pEEType->NonArrayBaseType == null) && !pEEType->IsInterface; + } + + // Returns true if the passed in MethodTable is the MethodTable for System.Array. + // The binder sets a special CorElementType for this well known type + internal static unsafe bool IsSystemArray(MethodTable* pEEType) + { + return (pEEType->ElementType == EETypeElementType.SystemArray); + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExceptionHelpers.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExceptionHelpers.cs new file mode 100644 index 00000000000000..65ee7803c99feb --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExceptionHelpers.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; + +namespace System +{ + public class RuntimeExceptionHelpers + { + public static void FailFast(String message) + { + InternalCalls.RhpFallbackFailFast(); + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExportAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExportAttribute.cs new file mode 100644 index 00000000000000..874f7d2f3f5673 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExportAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class RuntimeExportAttribute : Attribute + { + public string EntryPoint; + + public RuntimeExportAttribute(string entry) + { + EntryPoint = entry; + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs new file mode 100644 index 00000000000000..91acbb4edbd377 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs @@ -0,0 +1,511 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This is where we group together all the runtime export calls. +// + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Internal.Runtime; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime +{ + internal static class RuntimeExports + { + // + // internal calls for allocation + // + [RuntimeExport("RhNewObject")] + public static unsafe object RhNewObject(MethodTable* pEEType) + { + // This is structured in a funny way because at the present state of things in CoreRT, the Debug.Assert + // below will call into the assert defined in the class library (and not the MRT version of it). The one + // in the class library is not low level enough to be callable when GC statics are not initialized yet. + // Feel free to restructure once that's not a problem. +#if DEBUG + bool isValid = !pEEType->IsGenericTypeDefinition && + !pEEType->IsInterface && + !pEEType->IsArray && + !pEEType->IsString && + !pEEType->IsByRefLike; + if (!isValid) + Debug.Assert(false); +#endif + +#if FEATURE_64BIT_ALIGNMENT + if (pEEType->RequiresAlign8) + { + if (pEEType->IsValueType) + return InternalCalls.RhpNewFastMisalign(pEEType); + if (pEEType->IsFinalizable) + return InternalCalls.RhpNewFinalizableAlign8(pEEType); + return InternalCalls.RhpNewFastAlign8(pEEType); + } + else +#endif // FEATURE_64BIT_ALIGNMENT + { + if (pEEType->IsFinalizable) + return InternalCalls.RhpNewFinalizable(pEEType); + return InternalCalls.RhpNewFast(pEEType); + } + } + + [RuntimeExport("RhNewArray")] + public static unsafe object RhNewArray(MethodTable* pEEType, int length) + { + Debug.Assert(pEEType->IsArray || pEEType->IsString); + +#if FEATURE_64BIT_ALIGNMENT + if (pEEType->RequiresAlign8) + { + return InternalCalls.RhpNewArrayAlign8(pEEType, length); + } + else +#endif // FEATURE_64BIT_ALIGNMENT + { + return InternalCalls.RhpNewArray(pEEType, length); + } + } + + [RuntimeExport("RhBox")] + public static unsafe object RhBox(MethodTable* pEEType, ref byte data) + { + ref byte dataAdjustedForNullable = ref data; + + // Can box value types only (which also implies no finalizers). + Debug.Assert(pEEType->IsValueType && !pEEType->IsFinalizable); + + // If we're boxing a Nullable then either box the underlying T or return null (if the + // nullable's value is empty). + if (pEEType->IsNullable) + { + // The boolean which indicates whether the value is null comes first in the Nullable struct. + if (data == 0) + return null; + + // Switch type we're going to box to the Nullable target type and advance the data pointer + // to the value embedded within the nullable. + dataAdjustedForNullable = ref Unsafe.Add(ref data, pEEType->NullableValueOffset); + pEEType = pEEType->NullableType; + } + + object result; +#if FEATURE_64BIT_ALIGNMENT + if (pEEType->RequiresAlign8) + { + result = InternalCalls.RhpNewFastMisalign(pEEType); + } + else +#endif // FEATURE_64BIT_ALIGNMENT + { + result = InternalCalls.RhpNewFast(pEEType); + } + + // Copy the unboxed value type data into the new object. + // Perform any write barriers necessary for embedded reference fields. + if (pEEType->HasGCPointers) + { + InternalCalls.RhBulkMoveWithWriteBarrier(ref result.GetRawData(), ref dataAdjustedForNullable, pEEType->ValueTypeSize); + } + else + { + fixed (byte* pFields = &result.GetRawData()) + fixed (byte* pData = &dataAdjustedForNullable) + InternalCalls.memmove(pFields, pData, pEEType->ValueTypeSize); + } + + return result; + } + + [RuntimeExport("RhBoxAny")] + public static unsafe object RhBoxAny(ref byte data, MethodTable* pEEType) + { + if (pEEType->IsValueType) + { + return RhBox(pEEType, ref data); + } + else + { + return Unsafe.As(ref data); + } + } + + private static unsafe bool UnboxAnyTypeCompare(MethodTable* pEEType, MethodTable* ptrUnboxToEEType) + { + if (TypeCast.AreTypesEquivalent(pEEType, ptrUnboxToEEType)) + return true; + + if (pEEType->ElementType == ptrUnboxToEEType->ElementType) + { + // Enum's and primitive types should pass the UnboxAny exception cases + // if they have an exactly matching cor element type. + switch (ptrUnboxToEEType->ElementType) + { + case EETypeElementType.Byte: + case EETypeElementType.SByte: + case EETypeElementType.Int16: + case EETypeElementType.UInt16: + case EETypeElementType.Int32: + case EETypeElementType.UInt32: + case EETypeElementType.Int64: + case EETypeElementType.UInt64: + case EETypeElementType.IntPtr: + case EETypeElementType.UIntPtr: + return true; + } + } + + return false; + } + + [RuntimeExport("RhUnboxAny")] + public static unsafe void RhUnboxAny(object? o, ref byte data, EETypePtr pUnboxToEEType) + { + MethodTable* ptrUnboxToEEType = (MethodTable*)pUnboxToEEType.ToPointer(); + if (ptrUnboxToEEType->IsValueType) + { + bool isValid = false; + + if (ptrUnboxToEEType->IsNullable) + { + isValid = (o == null) || TypeCast.AreTypesEquivalent(o.MethodTable, ptrUnboxToEEType->NullableType); + } + else + { + isValid = (o != null) && UnboxAnyTypeCompare(o.MethodTable, ptrUnboxToEEType); + } + + if (!isValid) + { + // Throw the invalid cast exception defined by the classlib, using the input unbox MethodTable* + // to find the correct classlib. + + ExceptionIDs exID = o == null ? ExceptionIDs.NullReference : ExceptionIDs.InvalidCast; + + throw ptrUnboxToEEType->GetClasslibException(exID); + } + + RhUnbox(o, ref data, ptrUnboxToEEType); + } + else + { + if (o != null && (TypeCast.IsInstanceOf(ptrUnboxToEEType, o) == null)) + { + throw ptrUnboxToEEType->GetClasslibException(ExceptionIDs.InvalidCast); + } + + Unsafe.As(ref data) = o; + } + } + + // + // Unbox helpers with RyuJIT conventions + // + [RuntimeExport("RhUnbox2")] + public static unsafe ref byte RhUnbox2(MethodTable* pUnboxToEEType, object obj) + { + if ((obj == null) || !UnboxAnyTypeCompare(obj.MethodTable, pUnboxToEEType)) + { + ExceptionIDs exID = obj == null ? ExceptionIDs.NullReference : ExceptionIDs.InvalidCast; + throw pUnboxToEEType->GetClasslibException(exID); + } + return ref obj.GetRawData(); + } + + [RuntimeExport("RhUnboxNullable")] + public static unsafe void RhUnboxNullable(ref byte data, MethodTable* pUnboxToEEType, object obj) + { + if ((obj != null) && !TypeCast.AreTypesEquivalent(obj.MethodTable, pUnboxToEEType->NullableType)) + { + throw pUnboxToEEType->GetClasslibException(ExceptionIDs.InvalidCast); + } + RhUnbox(obj, ref data, pUnboxToEEType); + } + + [RuntimeExport("RhUnbox")] + public static unsafe void RhUnbox(object? obj, ref byte data, MethodTable* pUnboxToEEType) + { + // When unboxing to a Nullable the input object may be null. + if (obj == null) + { + Debug.Assert(pUnboxToEEType != null && pUnboxToEEType->IsNullable); + + // Set HasValue to false and clear the value (in case there were GC references we wish to stop reporting). + InternalCalls.RhpInitMultibyte( + ref data, + 0, + pUnboxToEEType->ValueTypeSize); + + return; + } + + MethodTable* pEEType = obj.MethodTable; + + // Can unbox value types only. + Debug.Assert(pEEType->IsValueType); + + // A special case is that we can unbox a value type T into a Nullable. It's the only case where + // pUnboxToEEType is useful. + Debug.Assert((pUnboxToEEType == null) || UnboxAnyTypeCompare(pEEType, pUnboxToEEType) || pUnboxToEEType->IsNullable); + if (pUnboxToEEType != null && pUnboxToEEType->IsNullable) + { + Debug.Assert(pUnboxToEEType->NullableType->IsEquivalentTo(pEEType)); + + // Set the first field of the Nullable to true to indicate the value is present. + Unsafe.As(ref data) = true; + + // Adjust the data pointer so that it points at the value field in the Nullable. + data = ref Unsafe.Add(ref data, pUnboxToEEType->NullableValueOffset); + } + + ref byte fields = ref obj.GetRawData(); + + if (pEEType->HasGCPointers) + { + // Copy the boxed fields into the new location in a GC safe manner + InternalCalls.RhBulkMoveWithWriteBarrier(ref data, ref fields, pEEType->ValueTypeSize); + } + else + { + // Copy the boxed fields into the new location. + fixed (byte *pData = &data) + fixed (byte* pFields = &fields) + InternalCalls.memmove(pData, pFields, pEEType->ValueTypeSize); + } + } + + [RuntimeExport("RhMemberwiseClone")] + public static unsafe object RhMemberwiseClone(object src) + { + object objClone; + + if (src.MethodTable->IsArray) + objClone = RhNewArray(src.MethodTable, Unsafe.As(src).Length); + else + objClone = RhNewObject(src.MethodTable); + + InternalCalls.RhpCopyObjectContents(objClone, src); + + return objClone; + } + + [RuntimeExport("RhpReversePInvokeBadTransition")] + public static void RhpReversePInvokeBadTransition(IntPtr returnAddress) + { + EH.FailFastViaClasslib( + RhFailFastReason.IllegalUnmanagedCallersOnlyEntry, + null, + returnAddress); + } + + [RuntimeExport("RhGetCurrentThreadStackTrace")] + [MethodImpl(MethodImplOptions.NoInlining)] // Ensures that the RhGetCurrentThreadStackTrace frame is always present + public static unsafe int RhGetCurrentThreadStackTrace(IntPtr[] outputBuffer) + { + fixed (IntPtr* pOutputBuffer = outputBuffer) + return RhpGetCurrentThreadStackTrace(pOutputBuffer, (uint)((outputBuffer != null) ? outputBuffer.Length : 0), new UIntPtr(&pOutputBuffer)); + } + + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + private static extern unsafe int RhpGetCurrentThreadStackTrace(IntPtr* pOutputBuffer, uint outputBufferLength, UIntPtr addressInCurrentFrame); + + // Worker for RhGetCurrentThreadStackTrace. RhGetCurrentThreadStackTrace just allocates a transition + // frame that will be used to seed the stack trace and this method does all the real work. + // + // Input: outputBuffer may be null or non-null + // Return value: positive: number of entries written to outputBuffer + // negative: number of required entries in outputBuffer in case it's too small (or null) + // Output: outputBuffer is filled in with return address IPs, starting with placing the this + // method's return address into index 0 + // + // NOTE: We don't want to allocate the array on behalf of the caller because we don't know which class + // library's objects the caller understands (we support multiple class libraries with multiple root + // System.Object types). + [UnmanagedCallersOnly(EntryPoint = "RhpCalculateStackTraceWorker", CallConvs = new Type[] { typeof(CallConvCdecl) })] + private static unsafe int RhpCalculateStackTraceWorker(IntPtr* pOutputBuffer, uint outputBufferLength, UIntPtr addressInCurrentFrame) + { + uint nFrames = 0; + bool success = true; + + StackFrameIterator frameIter = default; + + bool isValid = frameIter.Init(null); + Debug.Assert(isValid, "Missing RhGetCurrentThreadStackTrace frame"); + + // Note that the while loop will skip RhGetCurrentThreadStackTrace frame + while (frameIter.Next()) + { + if ((void*)frameIter.SP < (void*)addressInCurrentFrame) + continue; + + if (nFrames < outputBufferLength) + pOutputBuffer[nFrames] = new IntPtr(frameIter.ControlPC); + else + success = false; + + nFrames++; + } + + return success ? (int)nFrames : -(int)nFrames; + } + + // The GC conservative reporting descriptor is a special structure of data that the GC + // parses to determine whether there are specific regions of memory that it should not + // collect or move around. + // During garbage collection, the GC will inspect the data in this structure, and verify that: + // 1) _magic is set to the magic number (also hard coded on the GC side) + // 2) The reported region is valid (checks alignments, size, within bounds of the thread memory, etc...) + // 3) The ConservativelyReportedRegionDesc pointer must be reported by a frame which does not make a pinvoke transition. + // 4) The value of the _hash field is the computed hash of _regionPointerLow with _regionPointerHigh + // 5) The region must be IntPtr aligned, and have a size which is also IntPtr aligned + // If all conditions are satisfied, the region of memory starting at _regionPointerLow and ending at + // _regionPointerHigh will be conservatively reported. + // This can only be used to report memory regions on the current stack and the structure must itself + // be located on the stack. + public struct ConservativelyReportedRegionDesc + { + internal const ulong MagicNumber64 = 0x87DF7A104F09E0A9UL; + internal const uint MagicNumber32 = 0x4F09E0A9; + + internal UIntPtr _magic; + internal UIntPtr _regionPointerLow; + internal UIntPtr _regionPointerHigh; + internal UIntPtr _hash; + } + + [RuntimeExport("RhInitializeConservativeReportingRegion")] + public static unsafe void RhInitializeConservativeReportingRegion(ConservativelyReportedRegionDesc* regionDesc, void* bufferBegin, int cbBuffer) + { + Debug.Assert((((int)bufferBegin) & (sizeof(IntPtr) - 1)) == 0, "Buffer not IntPtr aligned"); + Debug.Assert((cbBuffer & (sizeof(IntPtr) - 1)) == 0, "Size of buffer not IntPtr aligned"); + + UIntPtr regionPointerLow = (UIntPtr)bufferBegin; + UIntPtr regionPointerHigh = (UIntPtr)(((byte*)bufferBegin) + cbBuffer); + + // Setup pointers to start and end of region + regionDesc->_regionPointerLow = regionPointerLow; + regionDesc->_regionPointerHigh = regionPointerHigh; + + // Activate the region for processing +#if TARGET_64BIT + ulong hash = ConservativelyReportedRegionDesc.MagicNumber64; + hash = ((hash << 13) ^ hash) ^ (ulong)regionPointerLow; + hash = ((hash << 13) ^ hash) ^ (ulong)regionPointerHigh; + + regionDesc->_hash = new UIntPtr(hash); + regionDesc->_magic = new UIntPtr(ConservativelyReportedRegionDesc.MagicNumber64); +#else + uint hash = ConservativelyReportedRegionDesc.MagicNumber32; + hash = ((hash << 13) ^ hash) ^ (uint)regionPointerLow; + hash = ((hash << 13) ^ hash) ^ (uint)regionPointerHigh; + + regionDesc->_hash = new UIntPtr(hash); + regionDesc->_magic = new UIntPtr(ConservativelyReportedRegionDesc.MagicNumber32); +#endif + } + + // Disable conservative reporting + [RuntimeExport("RhDisableConservativeReportingRegion")] + public static unsafe void RhDisableConservativeReportingRegion(ConservativelyReportedRegionDesc* regionDesc) + { + regionDesc->_magic = default(UIntPtr); + } + + [RuntimeExport("RhCreateThunksHeap")] + public static object RhCreateThunksHeap(IntPtr commonStubAddress) + { + return ThunksHeap.CreateThunksHeap(commonStubAddress); + } + + [RuntimeExport("RhAllocateThunk")] + public static IntPtr RhAllocateThunk(object thunksHeap) + { + return ((ThunksHeap)thunksHeap).AllocateThunk(); + } + + [RuntimeExport("RhFreeThunk")] + public static void RhFreeThunk(object thunksHeap, IntPtr thunkAddress) + { + ((ThunksHeap)thunksHeap).FreeThunk(thunkAddress); + } + + [RuntimeExport("RhSetThunkData")] + public static void RhSetThunkData(object thunksHeap, IntPtr thunkAddress, IntPtr context, IntPtr target) + { + ((ThunksHeap)thunksHeap).SetThunkData(thunkAddress, context, target); + } + + [RuntimeExport("RhTryGetThunkData")] + public static bool RhTryGetThunkData(object thunksHeap, IntPtr thunkAddress, out IntPtr context, out IntPtr target) + { + return ((ThunksHeap)thunksHeap).TryGetThunkData(thunkAddress, out context, out target); + } + + [RuntimeExport("RhGetThunkSize")] + public static int RhGetThunkSize() + { + return InternalCalls.RhpGetThunkSize(); + } + + [RuntimeExport("RhGetRuntimeHelperForType")] + internal static unsafe IntPtr RhGetRuntimeHelperForType(MethodTable* pEEType, RuntimeHelperKind kind) + { + switch (kind) + { + case RuntimeHelperKind.AllocateObject: +#if FEATURE_64BIT_ALIGNMENT + if (pEEType->RequiresAlign8) + { + if (pEEType->IsFinalizable) + return (IntPtr)(delegate*)&InternalCalls.RhpNewFinalizableAlign8; + else if (pEEType->IsValueType) // returns true for enum types as well + return (IntPtr)(delegate*)&InternalCalls.RhpNewFastMisalign; + else + return (IntPtr)(delegate*)&InternalCalls.RhpNewFastAlign8; + } +#endif // FEATURE_64BIT_ALIGNMENT + + if (pEEType->IsFinalizable) + return (IntPtr)(delegate*)&InternalCalls.RhpNewFinalizable; + else + return (IntPtr)(delegate*)&InternalCalls.RhpNewFast; + + case RuntimeHelperKind.IsInst: + if (pEEType->IsArray) + return (IntPtr)(delegate*)&TypeCast.IsInstanceOfArray; + else if (pEEType->IsInterface) + return (IntPtr)(delegate*)&TypeCast.IsInstanceOfInterface; + else if (pEEType->IsParameterizedType) + return (IntPtr)(delegate*)&TypeCast.IsInstanceOf; // Array handled above; pointers and byrefs handled here + else + return (IntPtr)(delegate*)&TypeCast.IsInstanceOfClass; + + case RuntimeHelperKind.CastClass: + if (pEEType->IsArray) + return (IntPtr)(delegate*)&TypeCast.CheckCastArray; + else if (pEEType->IsInterface) + return (IntPtr)(delegate*)&TypeCast.CheckCastInterface; + else if (pEEType->IsParameterizedType) + return (IntPtr)(delegate*)&TypeCast.CheckCast; // Array handled above; pointers and byrefs handled here + else + return (IntPtr)(delegate*)&TypeCast.CheckCastClass; + + case RuntimeHelperKind.AllocateArray: +#if FEATURE_64BIT_ALIGNMENT + if (pEEType->RequiresAlign8) + return (IntPtr)(delegate*)&InternalCalls.RhpNewArrayAlign8; +#endif // FEATURE_64BIT_ALIGNMENT + + return (IntPtr)(delegate*)&InternalCalls.RhpNewArray; + + default: + Debug.Assert(false, "Unknown RuntimeHelperKind"); + return IntPtr.Zero; + } + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImportAttribute.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImportAttribute.cs new file mode 100644 index 00000000000000..c9025f97e861d5 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImportAttribute.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] + internal sealed class RuntimeImportAttribute : Attribute + { + public string DllName { get; } + public string EntryPoint { get; } + + public RuntimeImportAttribute(string entry) + { + EntryPoint = entry; + } + + public RuntimeImportAttribute(string dllName, string entry) + { + EntryPoint = entry; + DllName = dllName; + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImports.cs new file mode 100644 index 00000000000000..2d7aac8213b14d --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImports.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Internal.Runtime; + +namespace System.Runtime +{ + internal static class RuntimeImports + { + private const string RuntimeLibrary = "*"; + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpRegisterFrozenSegment")] + internal static extern IntPtr RhpRegisterFrozenSegment(IntPtr pSegmentStart, IntPtr length); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpUnregisterFrozenSegment")] + internal static extern void RhpUnregisterFrozenSegment(IntPtr pSegmentHandle); + + [RuntimeImport(RuntimeLibrary, "RhpGetModuleSection")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern IntPtr RhGetModuleSection(ref TypeManagerHandle module, ReadyToRunSectionType section, out int length); + + internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSectionType section, out int length) + { + return RhGetModuleSection(ref module, section, out length); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpCreateTypeManager")] + internal static extern unsafe TypeManagerHandle RhpCreateTypeManager(IntPtr osModule, IntPtr moduleHeader, IntPtr* pClasslibFunctions, int nClasslibFunctions); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpRegisterOsModule")] + internal static extern unsafe IntPtr RhpRegisterOsModule(IntPtr osModule); + + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhNewObject")] + internal static extern object RhNewObject(EETypePtr pEEType); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhBulkMoveWithWriteBarrier")] + internal static extern unsafe void RhBulkMoveWithWriteBarrier(ref byte dmem, ref byte smem, uint size); + + + // Allocate handle. + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpHandleAlloc")] + private static extern IntPtr RhpHandleAlloc(Object value, GCHandleType type); + + internal static IntPtr RhHandleAlloc(Object value, GCHandleType type) + { + IntPtr h = RhpHandleAlloc(value, type); + if (h == IntPtr.Zero) + throw new OutOfMemoryException(); + return h; + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.cs new file mode 100644 index 00000000000000..da2dffdc5a566c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Runtime +{ + [StructLayout(LayoutKind.Explicit, Size = AsmOffsets.SIZEOF__REGDISPLAY)] + internal unsafe struct REGDISPLAY + { + [FieldOffset(AsmOffsets.OFFSETOF__REGDISPLAY__SP)] + internal UIntPtr SP; + } + + [StructLayout(LayoutKind.Explicit, Size = AsmOffsets.SIZEOF__StackFrameIterator)] + internal unsafe struct StackFrameIterator + { + [FieldOffset(AsmOffsets.OFFSETOF__StackFrameIterator__m_FramePointer)] + private UIntPtr _framePointer; + [FieldOffset(AsmOffsets.OFFSETOF__StackFrameIterator__m_ControlPC)] + private IntPtr _controlPC; + [FieldOffset(AsmOffsets.OFFSETOF__StackFrameIterator__m_RegDisplay)] + private REGDISPLAY _regDisplay; + [FieldOffset(AsmOffsets.OFFSETOF__StackFrameIterator__m_OriginalControlPC)] + private IntPtr _originalControlPC; + + internal byte* ControlPC { get { return (byte*)_controlPC; } } + internal byte* OriginalControlPC { get { return (byte*)_originalControlPC; } } + internal void* RegisterSet { get { fixed (void* pRegDisplay = &_regDisplay) { return pRegDisplay; } } } + internal UIntPtr SP { get { return _regDisplay.SP; } } + internal UIntPtr FramePointer { get { return _framePointer; } } + + internal bool Init(EH.PAL_LIMITED_CONTEXT* pStackwalkCtx, bool instructionFault = false) + { + return InternalCalls.RhpSfiInit(ref this, pStackwalkCtx, instructionFault); + } + + internal bool Next() + { + return Next(null, null); + } + + internal bool Next(uint* uExCollideClauseIdx) + { + return Next(uExCollideClauseIdx, null); + } + + internal bool Next(uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke) + { + return InternalCalls.RhpSfiNext(ref this, uExCollideClauseIdx, fUnwoundReversePInvoke); + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ThunkPool.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ThunkPool.cs new file mode 100644 index 00000000000000..de50016e469b5b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ThunkPool.cs @@ -0,0 +1,417 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This is an implementation of a general purpose thunk pool manager. Each thunk consists of: +// 1- A thunk stub, typically consisting of a lea + jmp instructions (slightly different +// on ARM, but semantically equivalent) +// 2- A thunk common stub: the implementation of the common stub depends on +// the usage scenario of the thunk +// 3- Thunk data: each thunk has two pointer-sized data values that can be stored. +// The first data value is called the thunk's 'context', and the second value is +// the thunk's jump target typically. +// +// Without FEATURE_RX_THUNKS, thunks are allocated by mapping a thunks template into memory. The template +// consists of a number of pairs of sections called thunk blocks (typically 8 pairs per mapping). Each pair +// has 2 page-long sections (4096 bytes): +// 1- The first section has RX permissions, and contains the thunk stubs (lea's + jmp's), +// and the thunk common stubs. +// 2- The second section has RW permissions and contains the thunks data (context + target). +// The last pointer-sized block in this section is special: it stores the address of +// the common stub that each thunk stub will jump to (the jump instruction in each thunk +// jumps to the address stored in that block). Therefore, whenever a new thunks template +// gets mapped into memory, the value of that last pointer cell in the data section is updated +// to the common stub address passed in by the caller +// +// With FEATURE_RX_THUNKS, thunks are created by allocating new virtual memory space, where the first half of +// that space is filled with thunk stubs, and gets RX permissions, and the second half is for the thunks data, +// and gets RW permissions. The thunk stubs and data blocks are not in groupped in pairs like in ProjectN: all +// the thunk stubs blocks are groupped at the begining of the allocated virtual memory space, and all the +// thunk data blocks are groupped in the second half of the virtual space. +// +// Available thunks are tracked using a linked list. The first cell in the data block of each thunk is +// used as the nodes of the linked list. The cell will point to the data block of the next available thunk, +// if one is available, or point to null. When thunks are freed, they are added to the begining of the list. +// + +using System.Diagnostics; + +namespace System.Runtime +{ + internal static class Constants + { + public const uint PageSize = 0x1000; // 4k + public const uint AllocationGranularity = 0x10000; // 64k + public const nuint PageSizeMask = 0xFFF; + public const nuint AllocationGranularityMask = 0xFFFF; + + public static readonly int ThunkDataSize = 2 * IntPtr.Size; + public static readonly int ThunkCodeSize = InternalCalls.RhpGetThunkSize(); + public static readonly int NumThunksPerBlock = InternalCalls.RhpGetNumThunksPerBlock(); + public static readonly int NumThunkBlocksPerMapping = InternalCalls.RhpGetNumThunkBlocksPerMapping(); + } + + internal class ThunksHeap + { + private class AllocatedBlock + { + internal IntPtr _blockBaseAddress; + internal AllocatedBlock _nextBlock; + } + + private IntPtr _commonStubAddress; + private IntPtr _nextAvailableThunkPtr; + private IntPtr _lastThunkPtr; + + private AllocatedBlock _allocatedBlocks; + + // Helper functions to set/clear the lowest bit for ARM instruction pointers + private static IntPtr ClearThumbBit(IntPtr value) + { +#if TARGET_ARM + Debug.Assert(((nint)value & 1) == 1); + value = (IntPtr)((nint)value - 1); +#endif + return value; + } + private static IntPtr SetThumbBit(IntPtr value) + { +#if TARGET_ARM + Debug.Assert(((nint)value & 1) == 0); + value = (IntPtr)((nint)value + 1); +#endif + return value; + } + + private unsafe ThunksHeap(IntPtr commonStubAddress) + { + _commonStubAddress = commonStubAddress; + + _allocatedBlocks = new AllocatedBlock(); + + InternalCalls.RhpAcquireThunkPoolLock(); + + IntPtr thunkStubsBlock = ThunkBlocks.GetNewThunksBlock(); + + InternalCalls.RhpReleaseThunkPoolLock(); + + if (thunkStubsBlock != IntPtr.Zero) + { + IntPtr thunkDataBlock = InternalCalls.RhpGetThunkDataBlockAddress(thunkStubsBlock); + + // Address of the first thunk data cell should be at the begining of the thunks data block (page-aligned) + Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.PageSize) == 0); + + // Update the last pointer value in the thunks data section with the value of the common stub address + *(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) = commonStubAddress; + Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) == commonStubAddress); + + // Set the head and end of the linked list + _nextAvailableThunkPtr = thunkDataBlock; + _lastThunkPtr = _nextAvailableThunkPtr + Constants.ThunkDataSize * (Constants.NumThunksPerBlock - 1); + + _allocatedBlocks._blockBaseAddress = thunkStubsBlock; + } + } + + public static unsafe ThunksHeap? CreateThunksHeap(IntPtr commonStubAddress) + { + try + { + ThunksHeap newHeap = new ThunksHeap(commonStubAddress); + + if (newHeap._nextAvailableThunkPtr != IntPtr.Zero) + return newHeap; + } + catch (Exception) { } + + return null; + } + + // TODO: Feature + // public static ThunksHeap DestroyThunksHeap(ThunksHeap heapToDestroy) + // { + // } + + // + // Note: Expected to be called under lock + // + private unsafe bool ExpandHeap() + { + AllocatedBlock newBlockInfo; + + try + { + newBlockInfo = new AllocatedBlock(); + } + catch (Exception) + { + return false; + } + + IntPtr thunkStubsBlock = ThunkBlocks.GetNewThunksBlock(); + + if (thunkStubsBlock != IntPtr.Zero) + { + IntPtr thunkDataBlock = InternalCalls.RhpGetThunkDataBlockAddress(thunkStubsBlock); + + // Address of the first thunk data cell should be at the begining of the thunks data block (page-aligned) + Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.PageSize) == 0); + + // Update the last pointer value in the thunks data section with the value of the common stub address + *(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) = _commonStubAddress; + Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) == _commonStubAddress); + + // Link the last entry in the old list to the first entry in the new list + *((IntPtr*)_lastThunkPtr) = thunkDataBlock; + + // Update the pointer to the last entry in the list + _lastThunkPtr = *((IntPtr*)_lastThunkPtr) + Constants.ThunkDataSize * (Constants.NumThunksPerBlock - 1); + + newBlockInfo._blockBaseAddress = thunkStubsBlock; + newBlockInfo._nextBlock = _allocatedBlocks; + + _allocatedBlocks = newBlockInfo; + + return true; + } + + return false; + } + + public unsafe IntPtr AllocateThunk() + { + // TODO: optimize the implementation and make it lock-free + // or at least change it to a per-heap lock instead of a global lock. + + Debug.Assert(_nextAvailableThunkPtr != IntPtr.Zero); + + InternalCalls.RhpAcquireThunkPoolLock(); + + IntPtr nextAvailableThunkPtr = _nextAvailableThunkPtr; + IntPtr nextNextAvailableThunkPtr = *((IntPtr*)(nextAvailableThunkPtr)); + + if (nextNextAvailableThunkPtr == IntPtr.Zero) + { + if (!ExpandHeap()) + { + InternalCalls.RhpReleaseThunkPoolLock(); + return IntPtr.Zero; + } + + nextAvailableThunkPtr = _nextAvailableThunkPtr; + nextNextAvailableThunkPtr = *((IntPtr*)(nextAvailableThunkPtr)); + Debug.Assert(nextNextAvailableThunkPtr != IntPtr.Zero); + } + + _nextAvailableThunkPtr = nextNextAvailableThunkPtr; + + InternalCalls.RhpReleaseThunkPoolLock(); + + Debug.Assert(nextAvailableThunkPtr != IntPtr.Zero); + +#if DEBUG + // Reset debug flag indicating the thunk is now in use + *((IntPtr*)(nextAvailableThunkPtr + IntPtr.Size)) = IntPtr.Zero; +#endif + + int thunkIndex = (int)(((nuint)(nint)nextAvailableThunkPtr) - ((nuint)(nint)nextAvailableThunkPtr & ~Constants.PageSizeMask)); + Debug.Assert((thunkIndex % Constants.ThunkDataSize) == 0); + thunkIndex = thunkIndex / Constants.ThunkDataSize; + + IntPtr thunkAddress = InternalCalls.RhpGetThunkStubsBlockAddress(nextAvailableThunkPtr) + thunkIndex * Constants.ThunkCodeSize; + + return SetThumbBit(thunkAddress); + } + + public unsafe void FreeThunk(IntPtr thunkAddress) + { + // TODO: optimize the implementation and make it lock-free + // or at least change it to a per-heap lock instead of a global lock. + + IntPtr dataAddress = TryGetThunkDataAddress(thunkAddress); + if (dataAddress == IntPtr.Zero) + EH.FallbackFailFast(RhFailFastReason.InternalError, null); + +#if DEBUG + if (!IsThunkInHeap(thunkAddress)) + EH.FallbackFailFast(RhFailFastReason.InternalError, null); + + // Debug flag indicating the thunk is no longer used + *((IntPtr*)(dataAddress + IntPtr.Size)) = new IntPtr(-1); +#endif + + InternalCalls.RhpAcquireThunkPoolLock(); + + *((IntPtr*)(dataAddress)) = _nextAvailableThunkPtr; + _nextAvailableThunkPtr = dataAddress; + + InternalCalls.RhpReleaseThunkPoolLock(); + } + + private bool IsThunkInHeap(IntPtr thunkAddress) + { + nuint thunkAddressValue = (nuint)(nint)ClearThumbBit(thunkAddress); + + AllocatedBlock currentBlock = _allocatedBlocks; + + while (currentBlock != null) + { + if (thunkAddressValue >= (nuint)(nint)currentBlock._blockBaseAddress && + thunkAddressValue < (nuint)(nint)currentBlock._blockBaseAddress + (nuint)(Constants.NumThunksPerBlock * Constants.ThunkCodeSize)) + { + return true; + } + + currentBlock = currentBlock._nextBlock; + } + + return false; + } + + private IntPtr TryGetThunkDataAddress(IntPtr thunkAddress) + { + nuint thunkAddressValue = (nuint)(nint)ClearThumbBit(thunkAddress); + + // Compute the base address of the thunk's mapping + nuint currentThunksBlockAddress = thunkAddressValue & ~Constants.PageSizeMask; + + // Make sure the thunk address is valid by checking alignment + if ((thunkAddressValue - currentThunksBlockAddress) % (nuint)Constants.ThunkCodeSize != 0) + return IntPtr.Zero; + + // Compute the thunk's index + int thunkIndex = (int)((thunkAddressValue - currentThunksBlockAddress) / (nuint)Constants.ThunkCodeSize); + + // Compute the address of the data block that corresponds to the current thunk + IntPtr thunkDataBlockAddress = InternalCalls.RhpGetThunkDataBlockAddress((IntPtr)((nint)thunkAddressValue)); + + return thunkDataBlockAddress + thunkIndex * Constants.ThunkDataSize; + } + + /// + /// This method retrieves the two data fields for a thunk. + /// Caution: No checks are made to verify that the thunk address is that of a + /// valid thunk in use. The caller of this API is responsible for providing a valid + /// address of a thunk that was not previously freed. + /// + /// True if the thunk's data was successfully retrieved. + public unsafe bool TryGetThunkData(IntPtr thunkAddress, out IntPtr context, out IntPtr target) + { + context = IntPtr.Zero; + target = IntPtr.Zero; + + IntPtr dataAddress = TryGetThunkDataAddress(thunkAddress); + if (dataAddress == IntPtr.Zero) + return false; + + if (!IsThunkInHeap(thunkAddress)) + return false; + + // Update the data that will be used by the thunk that was allocated + context = *((IntPtr*)(dataAddress)); + target = *((IntPtr*)(dataAddress + IntPtr.Size)); + + return true; + } + + /// + /// This method sets the two data fields for a thunk. + /// Caution: No checks are made to verify that the thunk address is that of a + /// valid thunk in use. The caller of this API is responsible for providing a valid + /// address of a thunk that was not previously freed. + /// + public unsafe void SetThunkData(IntPtr thunkAddress, IntPtr context, IntPtr target) + { + IntPtr dataAddress = TryGetThunkDataAddress(thunkAddress); + if (dataAddress == IntPtr.Zero) + EH.FallbackFailFast(RhFailFastReason.InternalError, null); + +#if DEBUG + if (!IsThunkInHeap(thunkAddress)) + EH.FallbackFailFast(RhFailFastReason.InternalError, null); +#endif + + // Update the data that will be used by the thunk that was allocated + *((IntPtr*)(dataAddress)) = context; + *((IntPtr*)(dataAddress + IntPtr.Size)) = target; + } + } + + internal static class ThunkBlocks + { + private static IntPtr[] s_currentlyMappedThunkBlocks = new IntPtr[Constants.NumThunkBlocksPerMapping]; + private static int s_currentlyMappedThunkBlocksIndex = Constants.NumThunkBlocksPerMapping; + + public static unsafe IntPtr GetNewThunksBlock() + { + IntPtr nextThunksBlock; + + // Check the most recently mapped thunks block. Each mapping consists of multiple + // thunk stubs pages, and multiple thunk data pages (typically 8 pages of each in a single mapping) + if (s_currentlyMappedThunkBlocksIndex < Constants.NumThunkBlocksPerMapping) + { + nextThunksBlock = s_currentlyMappedThunkBlocks[s_currentlyMappedThunkBlocksIndex++]; +#if DEBUG + s_currentlyMappedThunkBlocks[s_currentlyMappedThunkBlocksIndex - 1] = IntPtr.Zero; + Debug.Assert(nextThunksBlock != IntPtr.Zero); +#endif + } + else + { + nextThunksBlock = InternalCalls.RhAllocateThunksMapping(); + + if (nextThunksBlock == IntPtr.Zero) + { + // We either ran out of memory and can't do anymore mappings of the thunks templates sections, + // or we are using the managed runtime services fallback, which doesn't provide the + // file mapping feature (ex: older version of mrt100.dll, or no mrt100.dll at all). + + // The only option is for the caller to attempt and recycle unused thunks to be able to + // find some free entries. + + return IntPtr.Zero; + } + + // Each mapping consists of multiple blocks of thunk stubs/data pairs. Keep track of those + // so that we do not create a new mapping until all blocks in the sections we just mapped are consumed + IntPtr currentThunksBlock = nextThunksBlock; + int thunkBlockSize = InternalCalls.RhpGetThunkBlockSize(); + for (int i = 0; i < Constants.NumThunkBlocksPerMapping; i++) + { + s_currentlyMappedThunkBlocks[i] = currentThunksBlock; + currentThunksBlock += thunkBlockSize; + } + s_currentlyMappedThunkBlocksIndex = 1; + } + + Debug.Assert(nextThunksBlock != IntPtr.Zero); + + // Setup the thunks in the new block as a linked list of thunks. + // Use the first data field of the thunk to build the linked list. + IntPtr dataAddress = InternalCalls.RhpGetThunkDataBlockAddress(nextThunksBlock); + + for (int i = 0; i < Constants.NumThunksPerBlock; i++) + { + if (i == (Constants.NumThunksPerBlock - 1)) + *((IntPtr*)(dataAddress)) = IntPtr.Zero; + else + *((IntPtr*)(dataAddress)) = dataAddress + Constants.ThunkDataSize; + +#if DEBUG + // Debug flag in the second data cell indicating the thunk is not used + *((IntPtr*)(dataAddress + IntPtr.Size)) = new IntPtr(-1); +#endif + + dataAddress += Constants.ThunkDataSize; + } + + return nextThunksBlock; + } + + // TODO: [Feature] Keep track of mapped sections and free them if we need to. + // public static unsafe void FreeThunksBlock() + // { + // } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs new file mode 100644 index 00000000000000..228004e51a822f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs @@ -0,0 +1,1281 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Internal.Runtime; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime +{ + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // + // **** WARNING **** + // + // A large portion of the logic present in this file is duplicated + // in src\System.Private.Reflection.Execution\Internal\Reflection\Execution\TypeLoader\TypeCast.cs + // (for dynamic type builder). If you make changes here make sure they are reflected there. + // + // **** WARNING **** + // + ///////////////////////////////////////////////////////////////////////////////////////////////////// + + internal static class TypeCast + { + [Flags] + internal enum AssignmentVariation + { + Normal = 0, + + /// + /// Assume the source type is boxed so that value types and enums are compatible with Object, ValueType + /// and Enum (if applicable) + /// + BoxedSource = 1, + + /// + /// Allow identically sized integral types and enums to be considered equivalent (currently used only for + /// array element types) + /// + AllowSizeEquivalence = 2, + } + + [RuntimeExport("RhTypeCast_IsInstanceOfClass")] + public static unsafe object IsInstanceOfClass(MethodTable* pTargetType, object obj) + { + if (obj == null || obj.MethodTable == pTargetType) + { + return obj; + } + + MethodTable* pObjType = obj.MethodTable; + + Debug.Assert(!pTargetType->IsParameterizedType, "IsInstanceOfClass called with parameterized MethodTable"); + Debug.Assert(!pTargetType->IsInterface, "IsInstanceOfClass called with interface MethodTable"); + + // Quick check if both types are good for simple casting: canonical, no related type via IAT, no generic variance + if (Internal.Runtime.MethodTable.BothSimpleCasting(pObjType, pTargetType)) + { + // walk the type hierarchy looking for a match + do + { + pObjType = pObjType->RawBaseType; + + if (pObjType == null) + { + return null; + } + + if (pObjType == pTargetType) + { + return obj; + } + } + while (pObjType->SimpleCasting()); + } + + return IsInstanceOfClass_Helper(pTargetType, obj, pObjType); + } + + private static unsafe object IsInstanceOfClass_Helper(MethodTable* pTargetType, object obj, MethodTable* pObjType) + { + if (pTargetType->IsCloned) + { + pTargetType = pTargetType->CanonicalEEType; + } + + if (pObjType->IsCloned) + { + pObjType = pObjType->CanonicalEEType; + } + + // if the EETypes pointers match, we're done + if (pObjType == pTargetType) + { + return obj; + } + + if (pTargetType->HasGenericVariance && pObjType->HasGenericVariance) + { + // Only generic interfaces and delegates can have generic variance and we shouldn't see + // interfaces for either input here. So if the canonical types are marked as having variance + // we know we've hit the delegate case. We've dealt with the identical case just above. And + // the regular path below will handle casting to Object, Delegate and MulticastDelegate. Since + // we don't support deriving from user delegate classes any further all we have to check here + // is that the uninstantiated generic delegate definitions are the same and the type + // parameters are compatible. + + // NOTE: using general assignable path for the cache because of the cost of the variance checks + if (CastCache.AreTypesAssignableInternal(pObjType, pTargetType, AssignmentVariation.BoxedSource, null)) + return obj; + return null; + } + + if (pObjType->IsArray) + { + // arrays can be cast to System.Object + if (WellKnownEETypes.IsSystemObject(pTargetType)) + { + return obj; + } + + // arrays can be cast to System.Array + if (WellKnownEETypes.IsSystemArray(pTargetType)) + { + return obj; + } + + return null; + } + + + // walk the type hierarchy looking for a match + while (true) + { + pObjType = pObjType->NonClonedNonArrayBaseType; + if (pObjType == null) + { + return null; + } + + if (pObjType->IsCloned) + pObjType = pObjType->CanonicalEEType; + + if (pObjType == pTargetType) + { + return obj; + } + } + } + + [RuntimeExport("RhTypeCast_CheckCastClass")] + public static unsafe object CheckCastClass(MethodTable* pTargetEEType, object obj) + { + // a null value can be cast to anything + if (obj == null) + return null; + + object result = IsInstanceOfClass(pTargetEEType, obj); + + if (result == null) + { + // Throw the invalid cast exception defined by the classlib, using the input MethodTable* + // to find the correct classlib. + + throw pTargetEEType->GetClasslibException(ExceptionIDs.InvalidCast); + } + + return result; + } + + [RuntimeExport("RhTypeCast_IsInstanceOfArray")] + public static unsafe object IsInstanceOfArray(MethodTable* pTargetType, object obj) + { + if (obj == null) + { + return null; + } + + MethodTable* pObjType = obj.MethodTable; + + Debug.Assert(pTargetType->IsArray, "IsInstanceOfArray called with non-array MethodTable"); + Debug.Assert(!pTargetType->IsCloned, "cloned array types are disallowed"); + + // if the types match, we are done + if (pObjType == pTargetType) + { + return obj; + } + + // if the object is not an array, we're done + if (!pObjType->IsArray) + { + return null; + } + + Debug.Assert(!pObjType->IsCloned, "cloned array types are disallowed"); + + // compare the array types structurally + + if (pObjType->ParameterizedTypeShape != pTargetType->ParameterizedTypeShape) + { + // If the shapes are different, there's one more case to check for: Casting SzArray to MdArray rank 1. + if (!pObjType->IsSzArray || pTargetType->ArrayRank != 1) + { + return null; + } + } + + if (CastCache.AreTypesAssignableInternal(pObjType->RelatedParameterType, pTargetType->RelatedParameterType, + AssignmentVariation.AllowSizeEquivalence, null)) + { + return obj; + } + + return null; + } + + [RuntimeExport("RhTypeCast_CheckCastArray")] + public static unsafe object CheckCastArray(MethodTable* pTargetEEType, object obj) + { + // a null value can be cast to anything + if (obj == null) + return null; + + object result = IsInstanceOfArray(pTargetEEType, obj); + + if (result == null) + { + // Throw the invalid cast exception defined by the classlib, using the input MethodTable* + // to find the correct classlib. + + throw pTargetEEType->GetClasslibException(ExceptionIDs.InvalidCast); + } + + return result; + } + + [RuntimeExport("RhTypeCast_IsInstanceOfInterface")] + public static unsafe object IsInstanceOfInterface(MethodTable* pTargetType, object obj) + { + if (obj == null) + { + return null; + } + + MethodTable* pObjType = obj.MethodTable; + + if (CastCache.AreTypesAssignableInternal_SourceNotTarget_BoxedSource(pObjType, pTargetType, null)) + return obj; + + // If object type implements IDynamicInterfaceCastable then there's one more way to check whether it implements + // the interface. + if (pObjType->IsIDynamicInterfaceCastable && IsInstanceOfInterfaceViaIDynamicInterfaceCastable(pTargetType, obj, throwing: false)) + return obj; + + return null; + } + + private static unsafe bool IsInstanceOfInterfaceViaIDynamicInterfaceCastable(MethodTable* pTargetType, object obj, bool throwing) + { + var pfnIsInterfaceImplemented = (delegate*) + pTargetType->GetClasslibFunction(ClassLibFunctionId.IDynamicCastableIsInterfaceImplemented); + return pfnIsInterfaceImplemented(obj, pTargetType, throwing); + } + + internal static unsafe bool ImplementsInterface(MethodTable* pObjType, MethodTable* pTargetType, EETypePairList* pVisited) + { + Debug.Assert(!pTargetType->IsParameterizedType, "did not expect paramterized type"); + Debug.Assert(pTargetType->IsInterface, "IsInstanceOfInterface called with non-interface MethodTable"); + + // This can happen with generic interface types + // Debug.Assert(!pTargetType->IsCloned, "cloned interface types are disallowed"); + + // canonicalize target type + if (pTargetType->IsCloned) + pTargetType = pTargetType->CanonicalEEType; + + int numInterfaces = pObjType->NumInterfaces; + EEInterfaceInfo* interfaceMap = pObjType->InterfaceMap; + for (int i = 0; i < numInterfaces; i++) + { + MethodTable* pInterfaceType = interfaceMap[i].InterfaceType; + + // canonicalize the interface type + if (pInterfaceType->IsCloned) + pInterfaceType = pInterfaceType->CanonicalEEType; + + if (pInterfaceType == pTargetType) + { + return true; + } + } + + // We did not find the interface type in the list of supported interfaces. There's still one + // chance left: if the target interface is generic and one or more of its type parameters is co or + // contra variant then the object can still match if it implements a different instantiation of + // the interface with type compatible generic arguments. + // + // Interfaces which are only variant for arrays have the HasGenericVariance flag set even if they + // are not variant. + bool fArrayCovariance = pObjType->IsArray; + if (pTargetType->HasGenericVariance) + { + // Grab details about the instantiation of the target generic interface. + MethodTable* pTargetGenericType = pTargetType->GenericDefinition; + EETypeRef* pTargetInstantiation = pTargetType->GenericArguments; + int targetArity = (int)pTargetType->GenericArity; + GenericVariance* pTargetVarianceInfo = pTargetType->GenericVariance; + + Debug.Assert(pTargetVarianceInfo != null, "did not expect empty variance info"); + + + for (int i = 0; i < numInterfaces; i++) + { + MethodTable* pInterfaceType = interfaceMap[i].InterfaceType; + + // We can ignore interfaces which are not also marked as having generic variance + // unless we're dealing with array covariance. + // + // Interfaces which are only variant for arrays have the HasGenericVariance flag set even if they + // are not variant. + if (pInterfaceType->HasGenericVariance) + { + MethodTable* pInterfaceGenericType = pInterfaceType->GenericDefinition; + + // If the generic types aren't the same then the types aren't compatible. + if (pInterfaceGenericType != pTargetGenericType) + continue; + + // Grab instantiation details for the candidate interface. + EETypeRef* pInterfaceInstantiation = pInterfaceType->GenericArguments; + int interfaceArity = (int)pInterfaceType->GenericArity; + GenericVariance* pInterfaceVarianceInfo = pInterfaceType->GenericVariance; + + Debug.Assert(pInterfaceVarianceInfo != null, "did not expect empty variance info"); + + // The types represent different instantiations of the same generic type. The + // arity of both had better be the same. + Debug.Assert(targetArity == interfaceArity, "arity mismatch betweeen generic instantiations"); + + // Compare the instantiations to see if they're compatible taking variance into account. + if (TypeParametersAreCompatible(targetArity, + pInterfaceInstantiation, + pTargetInstantiation, + pTargetVarianceInfo, + fArrayCovariance, + pVisited)) + return true; + } + } + } + + return false; + } + + // Compare two types to see if they are compatible via generic variance. + private static unsafe bool TypesAreCompatibleViaGenericVariance(MethodTable* pSourceType, MethodTable* pTargetType, EETypePairList* pVisited) + { + MethodTable* pTargetGenericType = pTargetType->GenericDefinition; + MethodTable* pSourceGenericType = pSourceType->GenericDefinition; + + // If the generic types aren't the same then the types aren't compatible. + if (pSourceGenericType == pTargetGenericType) + { + // Get generic instantiation metadata for both types. + + EETypeRef* pTargetInstantiation = pTargetType->GenericArguments; + int targetArity = (int)pTargetType->GenericArity; + GenericVariance* pTargetVarianceInfo = pTargetType->GenericVariance; + + Debug.Assert(pTargetVarianceInfo != null, "did not expect empty variance info"); + + EETypeRef* pSourceInstantiation = pSourceType->GenericArguments; + int sourceArity = (int)pSourceType->GenericArity; + GenericVariance* pSourceVarianceInfo = pSourceType->GenericVariance; + + Debug.Assert(pSourceVarianceInfo != null, "did not expect empty variance info"); + + // The types represent different instantiations of the same generic type. The + // arity of both had better be the same. + Debug.Assert(targetArity == sourceArity, "arity mismatch betweeen generic instantiations"); + + // Compare the instantiations to see if they're compatible taking variance into account. + if (TypeParametersAreCompatible(targetArity, + pSourceInstantiation, + pTargetInstantiation, + pTargetVarianceInfo, + false, + pVisited)) + { + return true; + } + } + + return false; + } + + // Compare two sets of generic type parameters to see if they're assignment compatible taking generic + // variance into account. It's assumed they've already had their type definition matched (which + // implies their arities are the same as well). The fForceCovariance argument tells the method to + // override the defined variance of each parameter and instead assume it is covariant. This is used to + // implement covariant array interfaces. + internal static unsafe bool TypeParametersAreCompatible(int arity, + EETypeRef* pSourceInstantiation, + EETypeRef* pTargetInstantiation, + GenericVariance* pVarianceInfo, + bool fForceCovariance, + EETypePairList* pVisited) + { + // Walk through the instantiations comparing the cast compatibility of each pair + // of type args. + for (int i = 0; i < arity; i++) + { + MethodTable* pTargetArgType = pTargetInstantiation[i].Value; + MethodTable* pSourceArgType = pSourceInstantiation[i].Value; + + GenericVariance varType; + if (fForceCovariance) + varType = GenericVariance.ArrayCovariant; + else + varType = pVarianceInfo[i]; + + switch (varType) + { + case GenericVariance.NonVariant: + // Non-variant type params need to be identical. + + if (!AreTypesEquivalent(pSourceArgType, pTargetArgType)) + return false; + + break; + + case GenericVariance.Covariant: + // For covariance (or out type params in C#) the object must implement an + // interface with a more derived type arg than the target interface. Or + // the object interface can have a type arg that is an interface + // implemented by the target type arg. + // For instance: + // class Foo : ICovariant is ICovariant + // class Foo : ICovariant is ICovariant + // class Foo : ICovariant is ICovariant + + if (!CastCache.AreTypesAssignableInternal(pSourceArgType, pTargetArgType, AssignmentVariation.Normal, pVisited)) + return false; + + break; + + case GenericVariance.ArrayCovariant: + // For array covariance the object must be an array with a type arg + // that is more derived than that the target interface, or be a primitive + // (or enum) with the same size. + // For instance: + // string[,,] is object[,,] + // int[,,] is uint[,,] + + // This call is just like the call for Covariance above except true is passed + // to the fAllowSizeEquivalence parameter to allow the int/uint matching to work + if (!CastCache.AreTypesAssignableInternal(pSourceArgType, pTargetArgType, AssignmentVariation.AllowSizeEquivalence, pVisited)) + return false; + + break; + + case GenericVariance.Contravariant: + // For contravariance (or in type params in C#) the object must implement + // an interface with a less derived type arg than the target interface. Or + // the object interface can have a type arg that is a class implementing + // the interface that is the target type arg. + // For instance: + // class Foo : IContravariant is IContravariant + // class Foo : IContravariant is IContravariant + // class Foo : IContravariant is IContravariant + + if (!CastCache.AreTypesAssignableInternal(pTargetArgType, pSourceArgType, AssignmentVariation.Normal, pVisited)) + return false; + + break; + + default: + Debug.Assert(false, "unknown generic variance type"); + break; + } + } + + return true; + } + + // + // Determines if a value of the source type can be assigned to a location of the target type. + // It does not handle IDynamicInterfaceCastable, and cannot since we do not have an actual object instance here. + // This routine assumes that the source type is boxed, i.e. a value type source is presumed to be + // compatible with Object and ValueType and an enum source is additionally compatible with Enum. + // + [RuntimeExport("RhTypeCast_AreTypesAssignable")] + public static unsafe bool AreTypesAssignable(MethodTable* pSourceType, MethodTable* pTargetType) + { + // Special case: Generic Type definitions are not assignable in a mrt sense + // in any way. Assignability of those types is handled by reflection logic. + // Call this case out first and here so that these only somewhat filled in + // types do not leak into the rest of the type casting logic. + if (pTargetType->IsGenericTypeDefinition || pSourceType->IsGenericTypeDefinition) + { + return false; + } + + // Special case: T can be cast to Nullable (where T is a value type). Call this case out here + // since this is only applicable if T is boxed, which is not true for any other callers of + // AreTypesAssignableInternal, so no sense making all the other paths pay the cost of the check. + if (pTargetType->IsNullable && pSourceType->IsValueType && !pSourceType->IsNullable) + { + MethodTable* pNullableType = pTargetType->NullableType; + + return AreTypesEquivalent(pSourceType, pNullableType); + } + + return CastCache.AreTypesAssignableInternal(pSourceType, pTargetType, AssignmentVariation.BoxedSource, null); + } + + // Internally callable version of the export method above. Has two additional flags: + // fBoxedSource : assume the source type is boxed so that value types and enums are + // compatible with Object, ValueType and Enum (if applicable) + // fAllowSizeEquivalence : allow identically sized integral types and enums to be considered + // equivalent (currently used only for array element types) + internal static unsafe bool AreTypesAssignableInternal(MethodTable* pSourceType, MethodTable* pTargetType, AssignmentVariation variation, EETypePairList* pVisited) + { + bool fBoxedSource = ((variation & AssignmentVariation.BoxedSource) == AssignmentVariation.BoxedSource); + bool fAllowSizeEquivalence = ((variation & AssignmentVariation.AllowSizeEquivalence) == AssignmentVariation.AllowSizeEquivalence); + + // + // Are the types identical? + // + if (AreTypesEquivalent(pSourceType, pTargetType)) + return true; + + // + // Handle cast to interface cases. + // + if (pTargetType->IsInterface) + { + // Value types can only be cast to interfaces if they're boxed. + if (!fBoxedSource && pSourceType->IsValueType) + return false; + + if (ImplementsInterface(pSourceType, pTargetType, pVisited)) + return true; + + // Are the types compatible due to generic variance? + if (pTargetType->HasGenericVariance && pSourceType->HasGenericVariance) + return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType, pVisited); + + return false; + } + if (pSourceType->IsInterface) + { + // The only non-interface type an interface can be cast to is Object. + return WellKnownEETypes.IsSystemObject(pTargetType); + } + + // + // Handle cast to array or pointer cases. + // + if (pTargetType->IsParameterizedType) + { + if (pSourceType->IsParameterizedType + && (pTargetType->ParameterizedTypeShape == pSourceType->ParameterizedTypeShape)) + { + // Source type is also a parameterized type. Are the parameter types compatible? + if (pSourceType->RelatedParameterType->IsPointerType) + { + // If the parameter types are pointers, then only exact matches are correct. + // As we've already called AreTypesEquivalent at the start of this function, + // return false as the exact match case has already been handled. + // int** is not compatible with uint**, nor is int*[] oompatible with uint*[]. + return false; + } + else if (pSourceType->RelatedParameterType->IsByRefType) + { + // Only allow exact matches for ByRef types - same as pointers above. This should + // be unreachable and it's only a defense in depth. ByRefs can't be parameters + // of any parameterized type. + return false; + } + else + { + // Note that using AreTypesAssignableInternal with AssignmentVariation.AllowSizeEquivalence + // here handles array covariance as well as IFoo[] -> Foo[] etc. We are not using + // AssignmentVariation.BoxedSource because int[] is not assignable to object[]. + return CastCache.AreTypesAssignableInternal(pSourceType->RelatedParameterType, + pTargetType->RelatedParameterType, AssignmentVariation.AllowSizeEquivalence, pVisited); + } + } + + // Can't cast a non-parameter type to a parameter type or a parameter type of different shape to a parameter type + return false; + } + if (pSourceType->IsArray) + { + // Target type is not an array. But we can still cast arrays to Object or System.Array. + return WellKnownEETypes.IsSystemObject(pTargetType) || WellKnownEETypes.IsSystemArray(pTargetType); + } + else if (pSourceType->IsParameterizedType) + { + return false; + } + + // + // Handle cast to other (non-interface, non-array) cases. + // + + if (pSourceType->IsValueType) + { + // Certain value types of the same size are treated as equivalent when the comparison is + // between array element types (indicated by fAllowSizeEquivalence). These are integer types + // of the same size (e.g. int and uint) and the base type of enums vs all integer types of the + // same size. + if (fAllowSizeEquivalence && pTargetType->IsPrimitive) + { + if (GetNormalizedIntegralArrayElementType(pSourceType) == GetNormalizedIntegralArrayElementType(pTargetType)) + return true; + + // Non-identical value types aren't equivalent in any other case (since value types are + // sealed). + return false; + } + + // If the source type is a value type but it's not boxed then we've run out of options: the types + // are not identical, the target type isn't an interface and we're not allowed to check whether + // the target type is a parent of this one since value types are sealed and thus the only matches + // would be against Object, ValueType or Enum, all of which are reference types and not compatible + // with non-boxed value types. + if (!fBoxedSource) + return false; + } + + // Sub case of casting between two instantiations of the same delegate type where one or more of + // the type parameters have variance. Only interfaces and delegate types can have variance over + // their type parameters and we know that neither type is an interface due to checks above. + if (pTargetType->HasGenericVariance && pSourceType->HasGenericVariance) + { + // We've dealt with the identical case at the start of this method. And the regular path below + // will handle casting to Object, Delegate and MulticastDelegate. Since we don't support + // deriving from user delegate classes any further all we have to check here is that the + // uninstantiated generic delegate definitions are the same and the type parameters are + // compatible. + return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType, pVisited); + } + + // Is the source type derived from the target type? + if (IsDerived(pSourceType, pTargetType)) + return true; + + return false; + } + + [RuntimeExport("RhTypeCast_CheckCastInterface")] + public static unsafe object CheckCastInterface(MethodTable* pTargetType, object obj) + { + // a null value can be cast to anything + if (obj == null) + { + return null; + } + + MethodTable* pObjType = obj.MethodTable; + + if (CastCache.AreTypesAssignableInternal_SourceNotTarget_BoxedSource(pObjType, pTargetType, null)) + return obj; + + // If object type implements IDynamicInterfaceCastable then there's one more way to check whether it implements + // the interface. + if (pObjType->IsIDynamicInterfaceCastable + && IsInstanceOfInterfaceViaIDynamicInterfaceCastable(pTargetType, obj, throwing: true)) + { + return obj; + } + + // Throw the invalid cast exception defined by the classlib, using the input MethodTable* to find the + // correct classlib. + throw pTargetType->GetClasslibException(ExceptionIDs.InvalidCast); + } + + [RuntimeExport("RhTypeCast_CheckArrayStore")] + public static unsafe void CheckArrayStore(object array, object obj) + { + if (array == null || obj == null) + { + return; + } + + Debug.Assert(array.MethodTable->IsArray, "first argument must be an array"); + + MethodTable* arrayElemType = array.MethodTable->RelatedParameterType; + if (CastCache.AreTypesAssignableInternal(obj.MethodTable, arrayElemType, AssignmentVariation.BoxedSource, null)) + return; + + // If object type implements IDynamicInterfaceCastable then there's one more way to check whether it implements + // the interface. + if (obj.MethodTable->IsIDynamicInterfaceCastable && IsInstanceOfInterfaceViaIDynamicInterfaceCastable(arrayElemType, obj, throwing: false)) + return; + + // Throw the array type mismatch exception defined by the classlib, using the input array's MethodTable* + // to find the correct classlib. + + throw array.MethodTable->GetClasslibException(ExceptionIDs.ArrayTypeMismatch); + } + + [RuntimeExport("RhTypeCast_CheckVectorElemAddr")] + public static unsafe void CheckVectorElemAddr(MethodTable* elemType, object array) + { + if (array == null) + { + return; + } + + Debug.Assert(array.MethodTable->IsArray, "second argument must be an array"); + + MethodTable* arrayElemType = array.MethodTable->RelatedParameterType; + + if (!AreTypesEquivalent(elemType, arrayElemType) + // In addition to the exactness check, add another check to allow non-exact matches through + // if the element type is a ValueType. The issue here is Universal Generics. The Universal + // Generic codegen will generate a call to this helper for all ldelema opcodes if the exact + // type is not known, and this can include ValueTypes. For ValueTypes, the exact check is not + // desireable as enum's are allowed to pass through this code if they are size matched. + // While this check is overly broad and allows non-enum valuetypes to also skip the check + // that is OK, because in the non-enum case the casting operations are sufficient to ensure + // type safety. + && !elemType->IsValueType) + { + // Throw the array type mismatch exception defined by the classlib, using the input array's MethodTable* + // to find the correct classlib. + + throw array.MethodTable->GetClasslibException(ExceptionIDs.ArrayTypeMismatch); + } + } + + internal struct ArrayElement + { + public object Value; + } + + // + // Array stelem/ldelema helpers with RyuJIT conventions + // + [RuntimeExport("RhpStelemRef")] + public static unsafe void StelemRef(Array array, int index, object obj) + { + // This is supported only on arrays + Debug.Assert(array.MethodTable->IsArray, "first argument must be an array"); + +#if INPLACE_RUNTIME + // this will throw appropriate exceptions if array is null or access is out of range. + ref object element = ref Unsafe.As(array)[index].Value; +#else + if (array is null) + { + // TODO: If both array and obj are null, we're likely going to throw Redhawk's NullReferenceException. + // This should blame the caller. + throw obj.MethodTable->GetClasslibException(ExceptionIDs.NullReference); + } + if ((uint)index >= (uint)array.Length) + { + throw array.MethodTable->GetClasslibException(ExceptionIDs.IndexOutOfRange); + } + ref object rawData = ref Unsafe.As(ref Unsafe.As(array).Data); + ref object element = ref Unsafe.Add(ref rawData, index); +#endif + + MethodTable* elementType = array.MethodTable->RelatedParameterType; + + if (obj == null) + goto assigningNull; + + if (elementType != obj.MethodTable) + goto notExactMatch; + +doWrite: + InternalCalls.RhpAssignRef(ref element, obj); + return; + +assigningNull: + element = null; + return; + + notExactMatch: +#if INPLACE_RUNTIME + // This optimization only makes sense for inplace runtime where there's only one System.Object. + if (array.MethodTable == MethodTableOf()) + goto doWrite; +#endif + + StelemRef_Helper(ref element, elementType, obj); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe void StelemRef_Helper(ref object element, MethodTable* elementType, object obj) + { + if (CastCache.AreTypesAssignableInternal(obj.MethodTable, elementType, AssignmentVariation.BoxedSource, null)) + { + InternalCalls.RhpAssignRef(ref element, obj); + } + else + { + // If object type implements IDynamicInterfaceCastable then there's one more way to check whether it implements + // the interface. + if (!obj.MethodTable->IsIDynamicInterfaceCastable || !IsInstanceOfInterfaceViaIDynamicInterfaceCastable(elementType, obj, throwing: false)) + { + // Throw the array type mismatch exception defined by the classlib, using the input array's + // MethodTable* to find the correct classlib. + throw elementType->GetClasslibException(ExceptionIDs.ArrayTypeMismatch); + } + InternalCalls.RhpAssignRef(ref element, obj); + } + } + + [RuntimeExport("RhpLdelemaRef")] + public static unsafe ref object LdelemaRef(Array array, int index, IntPtr elementType) + { + Debug.Assert(array.MethodTable->IsArray, "first argument must be an array"); + + MethodTable* elemType = (MethodTable*)elementType; + MethodTable* arrayElemType = array.MethodTable->RelatedParameterType; + + if (!AreTypesEquivalent(elemType, arrayElemType)) + { + // Throw the array type mismatch exception defined by the classlib, using the input array's MethodTable* + // to find the correct classlib. + + throw array.MethodTable->GetClasslibException(ExceptionIDs.ArrayTypeMismatch); + } + + ref object rawData = ref Unsafe.As(ref Unsafe.As(array).Data); + return ref Unsafe.Add(ref rawData, index); + } + + internal static unsafe bool IsDerived(MethodTable* pDerivedType, MethodTable* pBaseType) + { + Debug.Assert(!pDerivedType->IsArray, "did not expect array type"); + Debug.Assert(!pDerivedType->IsParameterizedType, "did not expect parameterType"); + Debug.Assert(!pBaseType->IsArray, "did not expect array type"); + Debug.Assert(!pBaseType->IsInterface, "did not expect interface type"); + Debug.Assert(!pBaseType->IsParameterizedType, "did not expect parameterType"); + Debug.Assert(pBaseType->IsCanonical || pBaseType->IsCloned || pBaseType->IsGenericTypeDefinition, "unexpected MethodTable"); + Debug.Assert(pDerivedType->IsCanonical || pDerivedType->IsCloned || pDerivedType->IsGenericTypeDefinition, "unexpected MethodTable"); + + // If a generic type definition reaches this function, then the function should return false unless the types are equivalent. + // This works as the NonClonedNonArrayBaseType of a GenericTypeDefinition is always null. + + if (pBaseType->IsCloned) + pBaseType = pBaseType->CanonicalEEType; + + do + { + if (pDerivedType->IsCloned) + pDerivedType = pDerivedType->CanonicalEEType; + + if (pDerivedType == pBaseType) + return true; + + pDerivedType = pDerivedType->NonClonedNonArrayBaseType; + } + while (pDerivedType != null); + + return false; + } + + // Method to compare two types pointers for type equality + // We cannot just compare the pointers as there can be duplicate type instances + // for cloned and constructed types. + // There are three separate cases here + // 1. The pointers are Equal => true + // 2. Either one or both the types are CLONED, follow to the canonical MethodTable and check + // 3. For Arrays/Pointers, we have to further check for rank and element type equality + [RuntimeExport("RhTypeCast_AreTypesEquivalent")] + public static unsafe bool AreTypesEquivalent(MethodTable* pType1, MethodTable* pType2) + { + if (pType1 == pType2) + return true; + + if (pType1->IsCloned) + pType1 = pType1->CanonicalEEType; + + if (pType2->IsCloned) + pType2 = pType2->CanonicalEEType; + + if (pType1 == pType2) + return true; + + if (pType1->IsParameterizedType && pType2->IsParameterizedType) + return AreTypesEquivalent(pType1->RelatedParameterType, pType2->RelatedParameterType) && pType1->ParameterizedTypeShape == pType2->ParameterizedTypeShape; + + return false; + } + + // this is necessary for shared generic code - Foo may be executing + // for T being an interface, an array or a class + [RuntimeExport("RhTypeCast_IsInstanceOf")] + public static unsafe object IsInstanceOf(MethodTable* pTargetType, object obj) + { + // @TODO: consider using the cache directly, but beware of IDynamicInterfaceCastable in the interface case + if (pTargetType->IsArray) + return IsInstanceOfArray(pTargetType, obj); + else if (pTargetType->IsInterface) + return IsInstanceOfInterface(pTargetType, obj); + else if (pTargetType->IsParameterizedType) + return null; // We handled arrays above so this is for pointers and byrefs only. + else + return IsInstanceOfClass(pTargetType, obj); + } + + [RuntimeExport("RhTypeCast_CheckCast")] + public static unsafe object CheckCast(MethodTable* pTargetType, object obj) + { + // @TODO: consider using the cache directly, but beware of IDynamicInterfaceCastable in the interface case + if (pTargetType->IsArray) + return CheckCastArray(pTargetType, obj); + else if (pTargetType->IsInterface) + return CheckCastInterface(pTargetType, obj); + else if (pTargetType->IsParameterizedType) + return CheckCastNonArrayParameterizedType(pTargetType, obj); + else + return CheckCastClass(pTargetType, obj); + } + + private static unsafe object CheckCastNonArrayParameterizedType(MethodTable* pTargetType, object obj) + { + // a null value can be cast to anything + if (obj == null) + { + return null; + } + + // Parameterized types are not boxable, so nothing can be an instance of these. + throw pTargetType->GetClasslibException(ExceptionIDs.InvalidCast); + } + + private static unsafe EETypeElementType GetNormalizedIntegralArrayElementType(MethodTable* type) + { + EETypeElementType elementType = type->ElementType; + switch (elementType) + { + case EETypeElementType.Byte: + case EETypeElementType.UInt16: + case EETypeElementType.UInt32: + case EETypeElementType.UInt64: + case EETypeElementType.UIntPtr: + return elementType - 1; + } + + return elementType; + } + + internal unsafe struct EETypePairList + { + private MethodTable* _eetype1; + private MethodTable* _eetype2; + private EETypePairList* _next; + + public EETypePairList(MethodTable* pEEType1, MethodTable* pEEType2, EETypePairList* pNext) + { + _eetype1 = pEEType1; + _eetype2 = pEEType2; + _next = pNext; + } + + public static bool Exists(EETypePairList* pList, MethodTable* pEEType1, MethodTable* pEEType2) + { + while (pList != null) + { + if (pList->_eetype1 == pEEType1 && pList->_eetype2 == pEEType2) + return true; + if (pList->_eetype1 == pEEType2 && pList->_eetype2 == pEEType1) + return true; + pList = pList->_next; + } + return false; + } + } + + // source type + target type + assignment variation -> true/false + [System.Runtime.CompilerServices.EagerStaticClassConstructionAttribute] + private static class CastCache + { + // + // Cache size parameters + // + + // Start with small cache size so that the cache entries used by startup one-time only initialization + // will get flushed soon + private const int InitialCacheSize = 128; // MUST BE A POWER OF TWO + private const int DefaultCacheSize = 1024; + private const int MaximumCacheSize = 128 * 1024; + + // + // Cache state + // + private static Entry[] s_cache = new Entry[InitialCacheSize]; // Initialize the cache eagerly to avoid null checks. + private static UnsafeGCHandle s_previousCache; + private static ulong s_tickCountOfLastOverflow = InternalCalls.PalGetTickCount64(); + private static int s_entries; + private static bool s_roundRobinFlushing; + + + private sealed class Entry + { + public Entry Next; + public Key Key; + public bool Result; // @TODO: consider storing this bit in the Key -- there is room + } + + private unsafe struct Key + { + private IntPtr _sourceTypeAndVariation; + private IntPtr _targetType; + + public Key(MethodTable* pSourceType, MethodTable* pTargetType, AssignmentVariation variation) + { + Debug.Assert((((long)pSourceType) & 3) == 0, "misaligned MethodTable!"); + Debug.Assert(((uint)variation) <= 3, "variation enum has an unexpectedly large value!"); + + _sourceTypeAndVariation = (IntPtr)(((byte*)pSourceType) + ((int)variation)); + _targetType = (IntPtr)pTargetType; + } + + private static int GetHashCode(IntPtr intptr) + { + return unchecked((int)((long)intptr)); + } + + public int CalculateHashCode() + { + return ((GetHashCode(_targetType) >> 4) ^ GetHashCode(_sourceTypeAndVariation)); + } + + public bool Equals(ref Key other) + { + return (_sourceTypeAndVariation == other._sourceTypeAndVariation) && (_targetType == other._targetType); + } + + public AssignmentVariation Variation + { + get { return (AssignmentVariation)(unchecked((int)(long)_sourceTypeAndVariation) & 3); } + } + + public MethodTable* SourceType { get { return (MethodTable*)(((long)_sourceTypeAndVariation) & ~3L); } } + public MethodTable* TargetType { get { return (MethodTable*)_targetType; } } + } + + public static unsafe bool AreTypesAssignableInternal(MethodTable* pSourceType, MethodTable* pTargetType, AssignmentVariation variation, EETypePairList* pVisited) + { + // Important special case -- it breaks infinite recursion in CastCache itself! + if (pSourceType == pTargetType) + return true; + + Key key = new Key(pSourceType, pTargetType, variation); + Entry entry = LookupInCache(s_cache, ref key); + if (entry == null) + return CacheMiss(ref key, pVisited); + + return entry.Result; + } + + // This method is an optimized and customized version of AreTypesAssignable that achieves better performance + // than AreTypesAssignableInternal through 2 significant changes + // 1. Removal of sourceType to targetType check (This propery must be known before calling this function. At time + // of writing, this is true as its is only used if sourceType is from an object, and targetType is an interface.) + // 2. Force inlining (This particular variant is only used in a small number of dispatch scenarios that are particularly + // high in performance impact.) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe bool AreTypesAssignableInternal_SourceNotTarget_BoxedSource(MethodTable* pSourceType, MethodTable* pTargetType, EETypePairList* pVisited) + { + Debug.Assert(pSourceType != pTargetType, "target is source"); + Key key = new Key(pSourceType, pTargetType, AssignmentVariation.BoxedSource); + Entry entry = LookupInCache(s_cache, ref key); + if (entry == null) + return CacheMiss(ref key, pVisited); + + return entry.Result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Entry? LookupInCache(Entry[] cache, ref Key key) + { + int entryIndex = key.CalculateHashCode() & (cache.Length - 1); + Entry entry = cache[entryIndex]; + while (entry != null) + { + if (entry.Key.Equals(ref key)) + break; + entry = entry.Next; + } + return entry; + } + + private static unsafe bool CacheMiss(ref Key key, EETypePairList* pVisited) + { + // + // First, check if we previously visited the input types pair, to avoid infinite recursions + // + if (EETypePairList.Exists(pVisited, key.SourceType, key.TargetType)) + return false; + + bool result = false; + bool previouslyCached = false; + + // + // Try to find the entry in the previous version of the cache that is kept alive by weak reference + // + if (s_previousCache.IsAllocated) + { + // Unchecked cast to avoid recursive dependency on array casting + Entry[] previousCache = Unsafe.As(s_previousCache.Target); + if (previousCache != null) + { + Entry previousEntry = LookupInCache(previousCache, ref key); + if (previousEntry != null) + { + result = previousEntry.Result; + previouslyCached = true; + } + } + } + + // + // Call into the type cast code to calculate the result + // + if (!previouslyCached) + { + EETypePairList newList = new EETypePairList(key.SourceType, key.TargetType, pVisited); + result = TypeCast.AreTypesAssignableInternal(key.SourceType, key.TargetType, key.Variation, &newList); + } + + // + // Update the cache under the lock + // + InternalCalls.RhpAcquireCastCacheLock(); + try + { + try + { + // Avoid duplicate entries + Entry existingEntry = LookupInCache(s_cache, ref key); + if (existingEntry != null) + return existingEntry.Result; + + // Resize cache as necessary + Entry[] cache = ResizeCacheForNewEntryAsNecessary(); + + int entryIndex = key.CalculateHashCode() & (cache.Length - 1); + + Entry newEntry = new Entry() { Key = key, Result = result, Next = cache[entryIndex] }; + + // BEWARE: Array store check can lead to infinite recursion. We avoid this by making certain + // that the cache trivially answers the case of equivalent types without triggering the cache + // miss path. (See CastCache.AreTypesAssignableInternal) + cache[entryIndex] = newEntry; + return newEntry.Result; + } + catch (OutOfMemoryException) + { + // Entry allocation failed -- but we can still return the correct cast result. + return result; + } + } + finally + { + InternalCalls.RhpReleaseCastCacheLock(); + } + } + + private static Entry[] ResizeCacheForNewEntryAsNecessary() + { + Entry[] cache = s_cache; + + int entries = s_entries++; + + // If the cache has spare space, we are done + if (2 * entries < cache.Length) + { + if (s_roundRobinFlushing) + { + cache[2 * entries] = null; + cache[2 * entries + 1] = null; + } + return cache; + } + + // + // Now, we have cache that is overflowing with results. We need to decide whether to resize it or start + // flushing the old entries instead + // + + // Start over counting the entries + s_entries = 0; + + // See how long it has been since the last time the cache was overflowing + ulong tickCount = InternalCalls.PalGetTickCount64(); + int tickCountSinceLastOverflow = (int)(tickCount - s_tickCountOfLastOverflow); + s_tickCountOfLastOverflow = tickCount; + + bool shrinkCache = false; + bool growCache = false; + + if (cache.Length < DefaultCacheSize) + { + // If the cache have not reached the default size, just grow it without thinking about it much + growCache = true; + } + else + { + if (tickCountSinceLastOverflow < cache.Length) + { + // We 'overflow' when 2*entries == cache.Length, so we have cache.Length / 2 entries that were + // filled in tickCountSinceLastOverflow ms, which is 2ms/entry + + // If the fill rate of the cache is faster than ~2ms per entry, grow it + if (cache.Length < MaximumCacheSize) + growCache = true; + } + else + if (tickCountSinceLastOverflow > cache.Length * 16) + { + // We 'overflow' when 2*entries == cache.Length, so we have ((cache.Length*16) / 2) entries that + // were filled in tickCountSinceLastOverflow ms, which is 32ms/entry + + // If the fill rate of the cache is slower than 32ms per entry, shrink it + if (cache.Length > DefaultCacheSize) + shrinkCache = true; + } + // Otherwise, keep the current size and just keep flushing the entries round robin + } + + Entry[]? newCache = null; + if (growCache || shrinkCache) + { + try + { + newCache = new Entry[shrinkCache ? (cache.Length / 2) : (cache.Length * 2)]; + } + catch (OutOfMemoryException) + { + // Failed to allocate a bigger/smaller cache. That is fine, keep the old one. + } + } + + if (newCache != null) + { + s_roundRobinFlushing = false; + + // Keep the reference to the old cache in a weak handle. We will try to use it to avoid hitting the + // cache miss path until the GC collects it. + if (s_previousCache.IsAllocated) + { + s_previousCache.Target = cache; + } + else + { + try + { + s_previousCache = UnsafeGCHandle.Alloc(cache, GCHandleType.Weak); + } + catch (OutOfMemoryException) + { + // Failed to allocate the handle to utilize the old cache, that is fine, we will just miss + // out on repopulating the new cache from the old cache. + s_previousCache = default(UnsafeGCHandle); + } + } + + return s_cache = newCache; + } + else + { + s_roundRobinFlushing = true; + return cache; + } + } + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/__Finalizer.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/__Finalizer.cs new file mode 100644 index 00000000000000..7a054439378efd --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/__Finalizer.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +// +// Implements the single finalizer thread for a Redhawk instance. Essentially waits for an event to fire +// indicating finalization is necessary then drains the queue of pending finalizable objects, calling the +// finalize method for each one. +// + +namespace System.Runtime +{ + // We choose this name to avoid clashing with any future public class with the name Finalizer. + internal static class __Finalizer + { + [UnmanagedCallersOnly(EntryPoint = "ProcessFinalizers", CallConvs = new Type[] { typeof(CallConvCdecl) })] + public static void ProcessFinalizers() + { +#if INPLACE_RUNTIME + System.Runtime.FinalizerInitRunner.DoInitialize(); +#endif + + while (true) + { + // Wait until there's some work to be done. If true is returned we should finalize objects, + // otherwise memory is low and we should initiate a collection. + if (InternalCalls.RhpWaitForFinalizerRequest() != 0) + { + DrainQueue(); + + // Tell anybody that's interested that the finalization pass is complete (there is a race condition here + // where we might immediately signal a new request as complete, but this is acceptable). + InternalCalls.RhpSignalFinalizationComplete(); + } + else + { + // RhpWaitForFinalizerRequest() returned false and indicated that memory is low. We help + // out by initiating a garbage collection and then go back to waiting for another request. + InternalCalls.RhCollect(-1, InternalGCCollectionMode.Blocking); + } + } + } + + // Do not inline this method -- we do not want to accidentally have any temps in ProcessFinalizers which contain + // objects that came off of the finalizer queue. If such temps were reported across the duration of the + // finalizer thread wait operation, it could cause unpredictable behavior with weak handles. + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe void DrainQueue() + { + // Drain the queue of finalizable objects. + while (true) + { + object target = InternalCalls.RhpGetNextFinalizableObject(); + if (target == null) + return; + + // Call the finalizer on the current target object. If the finalizer throws we'll fail + // fast via normal Redhawk exception semantics (since we don't attempt to catch + // anything). + ((delegate*)target.MethodTable->FinalizerCode)(target); + } + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/RuntimeHandles.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/RuntimeHandles.cs new file mode 100644 index 00000000000000..d94592c83ce697 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/RuntimeHandles.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// The following type is present only because the C# compiler assumes this type always exists +// + +namespace System +{ + internal struct RuntimeFieldHandle + { + } + + internal struct RuntimeMethodHandle + { + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/RuntimeTypeHandle.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/RuntimeTypeHandle.cs new file mode 100644 index 00000000000000..85f880035e904b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/RuntimeTypeHandle.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// System.Type and System.RuntimeTypeHandle are defined here as the C# compiler requires them +// In the redhawk runtime these are not used. In the class library there is an implementation that support typeof + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public class Type + { + public RuntimeTypeHandle TypeHandle { get { return default(RuntimeTypeHandle); } } + } + + [StructLayout(LayoutKind.Sequential)] + public struct RuntimeTypeHandle + { + private EETypePtr _pEEType; + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/String.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/String.cs new file mode 100644 index 00000000000000..52ef0ec05214b0 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/String.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System +{ + // CONTRACT with Runtime + // The String type is one of the primitives understood by the compilers and runtime + // Data Contract: One int field, m_stringLength, the number of 2-byte wide chars that are valid + + // STRING LAYOUT + // ------------- + // Strings are null-terminated for easy interop with native, but the value returned by String.Length + // does NOT include this null character in its count. As a result, there's some trickiness here in the + // layout and allocation of strings that needs explanation... + // + // String is allocated like any other array, using the RhNewArray API. It is essentially a very special + // char[] object. In order to be an array, the String MethodTable must have an 'array element size' of 2, + // which is setup by a special case in the binder. Strings must also have a typical array instance + // layout, which means that the first field after the m_pEEType field is the 'number of array elements' + // field. However, here, it is called m_stringLength because it contains the number of characters in the + // string (NOT including the terminating null element) and, thus, directly represents both the array + // length and String.Length. + // + // As with all arrays, the GC calculates the size of an object using the following formula: + // + // obj_size = align(base_size + (num_elements * element_size), sizeof(void*)) + // + // The values 'base_size' and 'element_size' are both stored in the MethodTable for String and 'num_elements' + // is m_stringLength. + // + // Our base_size is the size of the fixed portion of the string defined below. It, therefore, contains + // the size of the m_firstChar field in it. This means that, since our string data actually starts + // inside the fixed 'base_size' area, and our num_elements is equal to String.Length, we end up with one + // extra character at the end. This is how we get our extra null terminator which allows us to pass a + // pinned string out to native code as a null-terminated string. This is also why we don't increment the + // requested string length by one before passing it to RhNewArray. There is no need to allocate an extra + // array element, it is already allocated here in the fixed layout of the String. + // + // Typically, the base_size of an array type is aligned up to the nearest pointer size multiple (so that + // array elements start out aligned in case they need alignment themselves), but we don't want to do that + // with String because we are allocating String.Length components with RhNewArray and the overall object + // size will then need another alignment, resulting in wasted space. So the binder specially shrinks the + // base_size of String, leaving it unaligned in order to allow the use of that otherwise wasted space. + // + // One more note on base_size -- on 64-bit, the base_size ends up being 22 bytes, which is less than the + // min_obj_size of (3 * sizeof(void*)). This is OK because our array allocator will still align up the + // overall object size, so a 0-length string will end up with an object size of 24 bytes, which meets the + // min_obj_size requirement. + // + + // This type does not override GetHashCode, Equals +#pragma warning disable 0661, 0660 + [StructLayout(LayoutKind.Sequential)] + public class String + { +#if TARGET_64BIT + private const int POINTER_SIZE = 8; +#else + private const int POINTER_SIZE = 4; +#endif + // m_pEEType + m_stringLength + internal const int FIRST_CHAR_OFFSET = POINTER_SIZE + sizeof(int); + + // CS0169: The private field '{blah}' is never used + // CS0649: Field '{blah}' is never assigned to, and will always have its default value +#pragma warning disable 169, 649 + private int _stringLength; + private char _firstChar; + +#pragma warning restore + + public int Length + { + get + { + return _stringLength; + } + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/ThrowHelpers.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/ThrowHelpers.cs new file mode 100644 index 00000000000000..e52c8a598ace00 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/ThrowHelpers.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// These methods are used to throw exceptions from generated code. + /// + internal static class ThrowHelpers + { + private static void ThrowOverflowException() + { + throw new OverflowException(); + } + + private static void ThrowIndexOutOfRangeException() + { + throw new IndexOutOfRangeException(); + } + + private static void ThrowNullReferenceException() + { + throw new NullReferenceException(); + } + + private static void ThrowDivideByZeroException() + { + throw new DivideByZeroException(); + } + + private static void ThrowArrayTypeMismatchException() + { + throw new ArrayTypeMismatchException(); + } + + private static void ThrowPlatformNotSupportedException() + { + throw new PlatformNotSupportedException(); + } + + private static void ThrowTypeLoadException() + { + // exception doesn't exist in MRT: throw PlatformNotSupportedException() instead + throw new PlatformNotSupportedException(); + } + + private static void ThrowArgumentException() + { + // exception doesn't exist in MRT: throw PlatformNotSupportedException() instead + throw new PlatformNotSupportedException(); + } + + private static void ThrowArgumentOutOfRangeException() + { + // exception doesn't exist in MRT: throw PlatformNotSupportedException() instead + throw new PlatformNotSupportedException(); + } + } +} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Void.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Void.cs new file mode 100644 index 00000000000000..c6bb6c3168b54a --- /dev/null +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Void.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System +{ + // This class represents the void return type + public struct Void + { + } +} diff --git a/src/coreclr/nativeaot/Runtime/AsmOffsets.h b/src/coreclr/nativeaot/Runtime/AsmOffsets.h new file mode 100644 index 00000000000000..0bc72e01abf695 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/AsmOffsets.h @@ -0,0 +1,121 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This file is used by AsmOffsets.cpp to validate that our +// assembly-code offsets always match their C++ counterparts. + +// You must #define PLAT_ASM_OFFSET and PLAT_ASM_SIZEOF before you #include this file + +#ifdef HOST_64BIT +#define ASM_OFFSET(offset32, offset64, cls, member) PLAT_ASM_OFFSET(offset64, cls, member) +#define ASM_SIZEOF(sizeof32, sizeof64, cls ) PLAT_ASM_SIZEOF(sizeof64, cls) +#define ASM_CONST(const32, const64, expr) PLAT_ASM_CONST(const64, expr) +#else +#define ASM_OFFSET(offset32, offset64, cls, member) PLAT_ASM_OFFSET(offset32, cls, member) +#define ASM_SIZEOF(sizeof32, sizeof64, cls ) PLAT_ASM_SIZEOF(sizeof32, cls) +#define ASM_CONST(const32, const64, expr) PLAT_ASM_CONST(const32, expr) +#endif + +// NOTE: the values MUST be in hex notation WITHOUT the 0x prefix + +// 32-bit,64-bit, constant symbol +ASM_CONST( 400, 800, CLUMP_SIZE) +ASM_CONST( a, b, LOG2_CLUMP_SIZE) + +// 32-bit,64-bit, class, member +ASM_OFFSET( 0, 0, Object, m_pEEType) + +ASM_OFFSET( 4, 8, Array, m_Length) + +ASM_OFFSET( 4, 8, String, m_Length) +ASM_OFFSET( 8, C, String, m_FirstChar) +ASM_CONST( 2, 2, STRING_COMPONENT_SIZE) +ASM_CONST( E, 16, STRING_BASE_SIZE) +ASM_CONST(3FFFFFDF,3FFFFFDF,MAX_STRING_LENGTH) + +ASM_OFFSET( 0, 0, MethodTable, m_usComponentSize) +ASM_OFFSET( 2, 2, MethodTable, m_usFlags) +ASM_OFFSET( 4, 4, MethodTable, m_uBaseSize) +ASM_OFFSET( 14, 18, MethodTable, m_VTable) + +ASM_OFFSET( 0, 0, Thread, m_rgbAllocContextBuffer) +ASM_OFFSET( 28, 38, Thread, m_ThreadStateFlags) +ASM_OFFSET( 2c, 40, Thread, m_pTransitionFrame) +ASM_OFFSET( 30, 48, Thread, m_pHackPInvokeTunnel) +ASM_OFFSET( 40, 68, Thread, m_ppvHijackedReturnAddressLocation) +ASM_OFFSET( 44, 70, Thread, m_pvHijackedReturnAddress) +#ifdef HOST_64BIT +ASM_OFFSET( 0, 78, Thread, m_uHijackedReturnValueFlags) +#endif +ASM_OFFSET( 48, 80, Thread, m_pExInfoStackHead) +ASM_OFFSET( 4c, 88, Thread, m_threadAbortException) + +ASM_OFFSET( 50, 90, Thread, m_pThreadLocalModuleStatics) +ASM_OFFSET( 54, 98, Thread, m_numThreadLocalModuleStatics) + +ASM_SIZEOF( 14, 20, EHEnum) + +ASM_OFFSET( 0, 0, gc_alloc_context, alloc_ptr) +ASM_OFFSET( 4, 8, gc_alloc_context, alloc_limit) + +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH +ASM_OFFSET( 4, 8, InterfaceDispatchCell, m_pCache) +#ifndef HOST_64BIT +ASM_OFFSET( 8, 0, InterfaceDispatchCache, m_pCell) +#endif +ASM_OFFSET( 10, 20, InterfaceDispatchCache, m_rgEntries) +ASM_SIZEOF( 8, 10, InterfaceDispatchCacheEntry) +#endif + +#ifdef FEATURE_DYNAMIC_CODE +ASM_OFFSET( 0, 0, CallDescrData, pSrc) +ASM_OFFSET( 4, 8, CallDescrData, numStackSlots) +ASM_OFFSET( 8, C, CallDescrData, fpReturnSize) +ASM_OFFSET( C, 10, CallDescrData, pArgumentRegisters) +ASM_OFFSET( 10, 18, CallDescrData, pFloatArgumentRegisters) +ASM_OFFSET( 14, 20, CallDescrData, pTarget) +ASM_OFFSET( 18, 28, CallDescrData, pReturnBuffer) +#endif + +// Undefine macros that are only used in this header for convenience. +#undef ASM_OFFSET +#undef ASM_SIZEOF +#undef ASM_CONST + +// Define platform specific offsets +#include "AsmOffsetsCpu.h" + +//#define USE_COMPILE_TIME_CONSTANT_FINDER // Uncomment this line to use the constant finder +#if defined(__cplusplus) && defined(USE_COMPILE_TIME_CONSTANT_FINDER) +// This class causes the compiler to emit an error with the constant we're interested in +// in the error message. This is useful if a size or offset changes. To use, comment out +// the compile-time assert that is firing, enable the constant finder, add the appropriate +// constant to find to BogusFunction(), and build. +// +// Here's a sample compiler error: +// In file included from corert/src/Native/Runtime/AsmOffsetsVerify.cpp:38: +// corert/src/Native/Runtime/Full/../AsmOffsets.h:117:61: error: calling a private constructor of class +// 'AsmOffsets::FindCompileTimeConstant<25>' +// FindCompileTimeConstant bogus_variable; +// ^ +// corert/src/Native/Runtime/Full/../AsmOffsets.h:111:5: note: declared private here +// FindCompileTimeConstant(); +// ^ +template +class FindCompileTimeConstant +{ +private: + FindCompileTimeConstant(); +}; + +void BogusFunction() +{ + // Sample usage to generate the error + FindCompileTimeConstant bogus_variable; + FindCompileTimeConstant bogus_variable2; + FindCompileTimeConstant bogus_variable3; + FindCompileTimeConstant bogus_variable4; + FindCompileTimeConstant bogus_variable5; +} +#endif // defined(__cplusplus) && defined(USE_COMPILE_TIME_CONSTANT_FINDER) diff --git a/src/coreclr/nativeaot/Runtime/AsmOffsetsVerify.cpp b/src/coreclr/nativeaot/Runtime/AsmOffsetsVerify.cpp new file mode 100644 index 00000000000000..6f4b87104e8d23 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/AsmOffsetsVerify.cpp @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "gcenv.h" +#include "gcheaputilities.h" +#include "rhassert.h" +#include "RedhawkWarnings.h" +#include "slist.h" +#include "gcrhinterface.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "TargetPtrs.h" +#include "rhbinder.h" +#include "RWLock.h" +#include "RuntimeInstance.h" +#include "CachedInterfaceDispatch.h" +#include "shash.h" +#include "CallDescr.h" + +class AsmOffsets +{ + static_assert(sizeof(Thread::m_rgbAllocContextBuffer) >= sizeof(gc_alloc_context), "Thread::m_rgbAllocContextBuffer is not big enough to hold a gc_alloc_context"); + + // Some assembly helpers for arrays and strings are shared and use the fact that arrays and strings have similar layouts) + static_assert(offsetof(Array, m_Length) == offsetof(String, m_Length), "The length field of String and Array have different offsets"); + static_assert(sizeof(((Array*)0)->m_Length) == sizeof(((String*)0)->m_Length), "The length field of String and Array have different sizes"); + +#define PLAT_ASM_OFFSET(offset, cls, member) \ + static_assert((offsetof(cls, member) == 0x##offset) || (offsetof(cls, member) > 0x##offset), "Bad asm offset for '" #cls "." #member "', the actual offset is smaller than 0x" #offset "."); \ + static_assert((offsetof(cls, member) == 0x##offset) || (offsetof(cls, member) < 0x##offset), "Bad asm offset for '" #cls "." #member "', the actual offset is larger than 0x" #offset "."); + +#define PLAT_ASM_SIZEOF(size, cls ) \ + static_assert((sizeof(cls) == 0x##size) || (sizeof(cls) > 0x##size), "Bad asm size for '" #cls "', the actual size is smaller than 0x" #size "."); \ + static_assert((sizeof(cls) == 0x##size) || (sizeof(cls) < 0x##size), "Bad asm size for '" #cls "', the actual size is larger than 0x" #size "."); + +#define PLAT_ASM_CONST(constant, expr) \ + static_assert(((expr) == 0x##constant) || ((expr) > 0x##constant), "Bad asm constant for '" #expr "', the actual value is smaller than 0x" #constant "."); \ + static_assert(((expr) == 0x##constant) || ((expr) < 0x##constant), "Bad asm constant for '" #expr "', the actual value is larger than 0x" #constant "."); + +#include "AsmOffsets.h" + +}; + +#ifdef _MSC_VER +namespace { char WorkaroundLNK4221Warning; }; +#endif diff --git a/src/coreclr/nativeaot/Runtime/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/CMakeLists.txt new file mode 100644 index 00000000000000..dffaed5dbe2725 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/CMakeLists.txt @@ -0,0 +1,282 @@ +set(GC_DIR ../../gc) + +set(COMMON_RUNTIME_SOURCES + allocheap.cpp + rhassert.cpp + CachedInterfaceDispatch.cpp + Crst.cpp + DebugHeader.cpp + MethodTable.cpp + EHHelpers.cpp + event.cpp + FinalizerHelpers.cpp + GCHelpers.cpp + gctoclreventsink.cpp + gcheaputilities.cpp + GCMemoryHelpers.cpp + gcrhenv.cpp + gcrhscan.cpp + GcStressControl.cpp + HandleTableHelpers.cpp + MathHelpers.cpp + MiscHelpers.cpp + TypeManager.cpp + ObjectLayout.cpp + portable.cpp + profheapwalkhelper.cpp + RestrictedCallouts.cpp + RhConfig.cpp + RuntimeInstance.cpp + RWLock.cpp + StackFrameIterator.cpp + startup.cpp + stressLog.cpp + SyncClean.cpp + thread.cpp + threadstore.cpp + UniversalTransitionHelpers.cpp + yieldprocessornormalized.cpp + + ${GC_DIR}/gceventstatus.cpp + ${GC_DIR}/gcload.cpp + ${GC_DIR}/gcconfig.cpp + ${GC_DIR}/gchandletable.cpp + ${GC_DIR}/gccommon.cpp + ${GC_DIR}/gceewks.cpp + ${GC_DIR}/gcwks.cpp + ${GC_DIR}/gcscan.cpp + ${GC_DIR}/handletable.cpp + ${GC_DIR}/handletablecache.cpp + ${GC_DIR}/handletablecore.cpp + ${GC_DIR}/handletablescan.cpp + ${GC_DIR}/objecthandle.cpp +) + +set(SERVER_GC_SOURCES + ${GC_DIR}/gceesvr.cpp + ${GC_DIR}/gcsvr.cpp +) + +set(FULL_RUNTIME_SOURCES + AsmOffsetsVerify.cpp + ThunksMapping.cpp +) + +set(RUNTIME_SOURCES_ARCH_ASM +) + +set(PORTABLE_RUNTIME_SOURCES +) + +include_directories(inc) + +include_directories(.) +include_directories(${GC_DIR}) +include_directories(${GC_DIR}/env) + +if (WIN32) + set(GC_HEADERS + ${GC_DIR}/env/common.h + ${GC_DIR}/env/etmdummy.h + ${GC_DIR}/env/gcenv.base.h + ${GC_DIR}/env/gcenv.ee.h + ${GC_DIR}/env/gcenv.h + ${GC_DIR}/env/gcenv.interlocked.h + ${GC_DIR}/env/gcenv.interlocked.inl + ${GC_DIR}/env/gcenv.object.h + ${GC_DIR}/env/gcenv.os.h + ${GC_DIR}/env/gcenv.structs.h + ${GC_DIR}/env/gcenv.sync.h + ${GC_DIR}/env/gcenv.windows.inl + ${GC_DIR}/env/volatile.h + ${GC_DIR}/gc.h + ${GC_DIR}/gcconfig.h + ${GC_DIR}/gcdesc.h + ${GC_DIR}/gcenv.ee.standalone.inl + ${GC_DIR}/gcenv.inl + ${GC_DIR}/gcevent_serializers.h + ${GC_DIR}/gcevents.h + ${GC_DIR}/gceventstatus.h + ${GC_DIR}/gchandletableimpl.h + ${GC_DIR}/gcimpl.h + ${GC_DIR}/gcinterface.dac.h + ${GC_DIR}/gcinterface.ee.h + ${GC_DIR}/gcinterface.h + ${GC_DIR}/gcpriv.h + ${GC_DIR}/gcrecord.h + ${GC_DIR}/gcscan.h + ${GC_DIR}/handletable.h + ${GC_DIR}/handletable.inl + ${GC_DIR}/handletablepriv.h + ${GC_DIR}/objecthandle.h + ${GC_DIR}/softwarewritewatch.h) +endif(WIN32) + +if(WIN32) + + include_directories(windows) + + list(APPEND COMMON_RUNTIME_SOURCES + windows/PalRedhawkCommon.cpp + windows/PalRedhawkMinWin.cpp + ${GC_DIR}/windows/gcenv.windows.cpp + eventtrace.cpp + rheventtrace.cpp + ) + + list(APPEND FULL_RUNTIME_SOURCES + windows/CoffNativeCodeManager.cpp + ) + + set(ASM_SUFFIX asm) + + if (CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_AMD64) + list(APPEND RUNTIME_SOURCES_ARCH_ASM + ${ARCH_SOURCES_DIR}/GC.${ASM_SUFFIX} + ) + endif() + +else() + + include_directories(unix) + + # sal.h, pshpack/poppack.h + add_definitions(-DPAL_STDCPP_COMPAT) + include_directories(../../pal/inc/rt) + + include(CheckIncludeFiles) + include(CheckLibraryExists) + + include(${GC_DIR}/unix/configure.cmake) + + if(NOT CLR_CMAKE_TARGET_ARCH_WASM) + include_directories(../libunwind/include) + else() + include_directories($ENV{EMSCRIPTEN/system/lib/libcxxabi/include}) + endif() + + include_directories(../libunwind/include) + include_directories(../libunwind) + + # Disable building _Unwind_XXX style APIs of libunwind, since we don't use them. + add_definitions(-D_LIBUNWIND_DISABLE_ZERO_COST_APIS=1) + + # Compile unwinding only for the current compilation target architecture + add_definitions(-D_LIBUNWIND_IS_NATIVE_ONLY) + + list(APPEND COMMON_RUNTIME_SOURCES + unix/PalRedhawkUnix.cpp + ${GC_DIR}/unix/gcenv.unix.cpp + ${GC_DIR}/unix/events.cpp + ${GC_DIR}/unix/cgroup.cpp + ) + + list(APPEND FULL_RUNTIME_SOURCES + unix/cgroup.cpp + unix/HardwareExceptions.cpp + unix/UnixContext.cpp + unix/UnwindHelpers.cpp + unix/UnixNativeCodeManager.cpp + ../libunwind/src/Unwind-EHABI.cpp + ../libunwind/src/libunwind.cpp + ) + + if(CLR_CMAKE_TARGET_OSX) + list(APPEND FULL_RUNTIME_SOURCES + ../libunwind/src/Unwind_AppleExtras.cpp + ) + endif() + + set(ASM_SUFFIX S) + + list(APPEND RUNTIME_SOURCES_ARCH_ASM + ../libunwind/src/UnwindRegistersRestore.S + ../libunwind/src/UnwindRegistersSave.S + ) +endif() + +if (CLR_CMAKE_TARGET_ARCH_AMD64 AND CLR_CMAKE_TARGET_WIN32) + list(APPEND COMMON_RUNTIME_SOURCES + ${GC_DIR}/vxsort/isa_detection.cpp + ${GC_DIR}/vxsort/do_vxsort_avx2.cpp + ${GC_DIR}/vxsort/do_vxsort_avx512.cpp + ${GC_DIR}/vxsort/machine_traits.avx2.cpp + ${GC_DIR}/vxsort/smallsort/bitonic_sort.AVX2.int64_t.generated.cpp + ${GC_DIR}/vxsort/smallsort/bitonic_sort.AVX2.int32_t.generated.cpp + ${GC_DIR}/vxsort/smallsort/bitonic_sort.AVX512.int64_t.generated.cpp + ${GC_DIR}/vxsort/smallsort/bitonic_sort.AVX512.int32_t.generated.cpp + ${GC_DIR}/vxsort/smallsort/avx2_load_mask_tables.cpp +) +endif (CLR_CMAKE_TARGET_ARCH_AMD64 AND CLR_CMAKE_TARGET_WIN32) + +list(APPEND RUNTIME_SOURCES_ARCH_ASM + ${ARCH_SOURCES_DIR}/AllocFast.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/CallDescrWorker.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/CallingConventionConverterHelpers.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/ExceptionHandling.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/GcProbe.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/Interlocked.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/MiscStubs.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/PInvoke.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/InteropThunksHelpers.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/StubDispatch.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/UniversalTransition.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/WriteBarriers.${ASM_SUFFIX} +) + +# Add architecture specific folder for looking up headers. +convert_to_absolute_path(ARCH_SOURCES_DIR ${ARCH_SOURCES_DIR}) +include_directories(${ARCH_SOURCES_DIR}) + +add_definitions(-DFEATURE_BASICFREEZE) +add_definitions(-DFEATURE_CONSERVATIVE_GC) +add_definitions(-DFEATURE_CUSTOM_IMPORTS) +add_definitions(-DFEATURE_DYNAMIC_CODE) +add_compile_definitions($<$,$>:FEATURE_GC_STRESS>) +add_definitions(-DFEATURE_REDHAWK) +add_definitions(-DVERIFY_HEAP) +add_definitions(-DCORERT) +add_definitions(-DFEATURE_CACHED_INTERFACE_DISPATCH) +add_definitions(-D_LIB) + +# there is a problem with undefined symbols when this is set +# add_definitions(-DSTRESS_HEAP) + +if(WIN32) + add_definitions(-DFEATURE_ETW) + add_definitions(-DFEATURE_EVENT_TRACE) +else() + add_definitions(-DNO_UI_ASSERT) + include(unix/configure.cmake) + include_directories(${CMAKE_CURRENT_BINARY_DIR}) +endif() + +if (CLR_CMAKE_TARGET_UNIX) + + if (CLR_CMAKE_TARGET_ARCH_AMD64) + add_definitions(-DUNIX_AMD64_ABI) + elseif (CLR_CMAKE_TARGET_ARCH_ARM) + add_definitions(-DUNIX_ARM_ABI) + elseif (CLR_CMAKE_TARGET_ARCH_I386) + add_definitions(-DUNIX_X86_ABI) + endif() + +endif(CLR_CMAKE_TARGET_UNIX) + +set(RUNTIME_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +list(APPEND COMMON_RUNTIME_SOURCES ${GC_HEADERS}) + +convert_to_absolute_path(COMMON_RUNTIME_SOURCES ${COMMON_RUNTIME_SOURCES}) + +convert_to_absolute_path(FULL_RUNTIME_SOURCES ${FULL_RUNTIME_SOURCES}) +convert_to_absolute_path(PORTABLE_RUNTIME_SOURCES ${PORTABLE_RUNTIME_SOURCES}) +convert_to_absolute_path(SERVER_GC_SOURCES ${SERVER_GC_SOURCES}) + +convert_to_absolute_path(RUNTIME_SOURCES_ARCH_ASM ${RUNTIME_SOURCES_ARCH_ASM}) + +if(NOT CLR_CMAKE_TARGET_ARCH_WASM) + add_subdirectory(Full) +endif() + +add_subdirectory(Portable) diff --git a/src/coreclr/nativeaot/Runtime/CachedInterfaceDispatch.cpp b/src/coreclr/nativeaot/Runtime/CachedInterfaceDispatch.cpp new file mode 100644 index 00000000000000..872cd0590e94f5 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/CachedInterfaceDispatch.cpp @@ -0,0 +1,544 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ==--== +// +// Shared (non-architecture specific) portions of a mechanism to perform interface dispatch using an alternate +// mechanism to VSD that does not require runtime generation of code. +// +// ============================================================================ +#include "common.h" +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH + +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "DebugMacrosExt.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "holder.h" +#include "Crst.h" +#include "RedhawkWarnings.h" +#include "TargetPtrs.h" +#include "MethodTable.h" +#include "Range.h" +#include "allocheap.h" +#include "rhbinder.h" +#include "ObjectLayout.h" +#include "gcrhinterface.h" +#include "shash.h" +#include "RWLock.h" +#include "TypeManager.h" +#include "RuntimeInstance.h" +#include "MethodTable.inl" +#include "CommonMacros.inl" + +#include "CachedInterfaceDispatch.h" + +// We always allocate cache sizes with a power of 2 number of entries. We have a maximum size we support, +// defined below. +#define CID_MAX_CACHE_SIZE_LOG2 6 +#define CID_MAX_CACHE_SIZE (1 << CID_MAX_CACHE_SIZE_LOG2) + +//#define FEATURE_CID_STATS 1 + +#ifdef FEATURE_CID_STATS + +// Some counters used for debugging and profiling the algorithms. +extern "C" +{ + uint32_t CID_g_cLoadVirtFunc = 0; + uint32_t CID_g_cCacheMisses = 0; + uint32_t CID_g_cCacheSizeOverflows = 0; + uint32_t CID_g_cCacheOutOfMemory = 0; + uint32_t CID_g_cCacheReallocates = 0; + uint32_t CID_g_cCacheAllocates = 0; + uint32_t CID_g_cCacheDiscards = 0; + uint32_t CID_g_cInterfaceDispatches = 0; + uint32_t CID_g_cbMemoryAllocated = 0; + uint32_t CID_g_rgAllocatesBySize[CID_MAX_CACHE_SIZE_LOG2 + 1] = { 0 }; +}; + +#define CID_COUNTER_INC(_counter_name) CID_g_c##_counter_name++ + +#else + +#define CID_COUNTER_INC(_counter_name) + +#endif // FEATURE_CID_STATS + +// Helper function for updating two adjacent pointers (which are aligned on a double pointer-sized boundary) +// atomically. +// +// This is used to update interface dispatch cache entries and also the stub/cache pair in +// interface dispatch indirection cells. The cases have slightly different semantics: cache entry updates +// (fFailOnNonNull == true) require that the existing values in the location are both NULL whereas indirection +// cell updates have no such restriction. In both cases we'll try the update once; on failure we'll return the +// new value of the second pointer and on success we'll the old value of the second pointer. +// +// This suits the semantics of both callers. For indirection cell updates the caller needs to know the address +// of the cache that can now be scheduled for release and the cache pointer is the second one in the pair. For +// cache entry updates the caller only needs a success/failure indication: on success the return value will be +// NULL and on failure non-NULL. +static void * UpdatePointerPairAtomically(void * pPairLocation, + void * pFirstPointer, + void * pSecondPointer, + bool fFailOnNonNull) +{ +#if defined(HOST_64BIT) + // The same comments apply to the AMD64 version. The CompareExchange looks a little different since the + // API was refactored in terms of int64_t to avoid creating a 128-bit integer type. + + int64_t rgComparand[2] = { 0 , 0 }; + if (!fFailOnNonNull) + { + rgComparand[0] = *(int64_t volatile *)pPairLocation; + rgComparand[1] = *((int64_t volatile *)pPairLocation + 1); + } + + uint8_t bResult = PalInterlockedCompareExchange128((int64_t*)pPairLocation, (int64_t)pSecondPointer, (int64_t)pFirstPointer, rgComparand); + if (bResult == 1) + { + // Success, return old value of second pointer (rgComparand is updated by + // PalInterlockedCompareExchange128 with the old pointer values in this case). + return (void*)rgComparand[1]; + } + + // Failure, return the new second pointer value. + return pSecondPointer; +#else + // Stuff the two pointers into a 64-bit value as the proposed new value for the CompareExchange64 below. + int64_t iNewValue = (int64_t)((uint64_t)(uintptr_t)pFirstPointer | ((uint64_t)(uintptr_t)pSecondPointer << 32)); + + // Read the old value in the location. If fFailOnNonNull is set we just assume this was zero and we'll + // fail below if that's not the case. + int64_t iOldValue = fFailOnNonNull ? 0 : *(int64_t volatile *)pPairLocation; + + int64_t iUpdatedOldValue = PalInterlockedCompareExchange64((int64_t*)pPairLocation, iNewValue, iOldValue); + if (iUpdatedOldValue == iOldValue) + { + // Successful update. Return the previous value of the second pointer. For cache entry updates + // (fFailOnNonNull == true) this is guaranteed to be NULL in this case and the result being being + // NULL in the success case is all the caller cares about. For indirection cell updates the second + // pointer represents the old cache and the caller needs this data so they can schedule the cache + // for deletion once it becomes safe to do so. + return (void*)(uint32_t)(iOldValue >> 32); + } + + // The update failed due to a racing update to the same location. Return the new value of the second + // pointer (either a new cache that lost the race or a non-NULL pointer in the cache entry update case). + return pSecondPointer; +#endif // HOST_64BIT +} + +// Helper method for updating an interface dispatch cache entry atomically. See comments by the usage of +// this method for the details of why we need this. If a racing update is detected false is returned and the +// update abandoned. This is necessary since it's not safe to update a valid cache entry (one with a non-NULL +// m_pInstanceType field) outside of a GC. +static bool UpdateCacheEntryAtomically(InterfaceDispatchCacheEntry *pEntry, + MethodTable * pInstanceType, + void * pTargetCode) +{ + C_ASSERT(sizeof(InterfaceDispatchCacheEntry) == (sizeof(void*) * 2)); + C_ASSERT(offsetof(InterfaceDispatchCacheEntry, m_pInstanceType) < offsetof(InterfaceDispatchCacheEntry, m_pTargetCode)); + + return UpdatePointerPairAtomically(pEntry, pInstanceType, pTargetCode, true) == NULL; +} + +// Helper method for updating an interface dispatch indirection cell's stub and cache pointer atomically. +// Returns the value of the cache pointer that is not referenced by the cell after this operation. This can be +// NULL on the initial cell update, the value of the old cache pointer or the value of the new cache pointer +// supplied (in the case where another thread raced with us for the update and won). In any case, if the +// returned pointer is non-NULL it represents a cache that should be scheduled for release. +static InterfaceDispatchCache * UpdateCellStubAndCache(InterfaceDispatchCell * pCell, + void * pStub, + uintptr_t newCacheValue) +{ + C_ASSERT(offsetof(InterfaceDispatchCell, m_pStub) == 0); + C_ASSERT(offsetof(InterfaceDispatchCell, m_pCache) == sizeof(void*)); + + uintptr_t oldCacheValue = (uintptr_t)UpdatePointerPairAtomically(pCell, pStub, (void*)newCacheValue, false); + + if (InterfaceDispatchCell::IsCache(oldCacheValue)) + { + return (InterfaceDispatchCache *)oldCacheValue; + } + else + { + return nullptr; + } +} + +// +// Cache allocation logic. +// +// We use the existing AllocHeap mechanism as our base allocator for cache blocks. This is because it can +// provide the required 16-byte alignment with no padding or heap header costs. The downside is that there is +// no deallocation support (which would be hard to implement without implementing a cache block compaction +// scheme, which is certainly possible but not necessarily needed at this point). +// +// Instead, much like the original VSD algorithm, we keep discarded cache blocks and use them to satisfy new +// allocation requests before falling back on AllocHeap. +// +// We can't re-use discarded cache blocks immediately since there may be code that is still using them. +// Instead we link them into a global list and then at the next GC (when no code can hold a reference to these +// any more) we can place them on one of several free lists based on their size. +// + +#if defined(HOST_AMD64) || defined(HOST_ARM64) + +// Head of the list of discarded cache blocks that can't be re-used just yet. +InterfaceDispatchCache * g_pDiscardedCacheList; // for AMD64 and ARM64, m_pCell is not used and we can link the discarded blocks themselves + +#else // defined(HOST_AMD64) || defined(HOST_ARM64) + +struct DiscardedCacheBlock +{ + DiscardedCacheBlock * m_pNext; // for x86 and ARM, we are short of registers, thus need the m_pCell back pointers + InterfaceDispatchCache * m_pCache; // and thus need this auxiliary list +}; + +// Head of the list of discarded cache blocks that can't be re-used just yet. +static DiscardedCacheBlock * g_pDiscardedCacheList = NULL; + +// Free list of DiscardedCacheBlock items +static DiscardedCacheBlock * g_pDiscardedCacheFree = NULL; + +#endif // defined(HOST_AMD64) || defined(HOST_ARM64) + +// Free lists for each cache size up to the maximum. We allocate from these in preference to new memory. +static InterfaceDispatchCache * g_rgFreeLists[CID_MAX_CACHE_SIZE_LOG2 + 1]; + +// Lock protecting both g_pDiscardedCacheList and g_rgFreeLists. We don't use the OS SLIST support here since +// it imposes too much space overhead on list entries on 64-bit (each is actually 16 bytes). +static CrstStatic g_sListLock; + +// The base memory allocator. +static AllocHeap * g_pAllocHeap = NULL; + +// Each cache size has an associated stub used to perform lookup over that cache. +extern "C" void RhpInterfaceDispatch1(); +extern "C" void RhpInterfaceDispatch2(); +extern "C" void RhpInterfaceDispatch4(); +extern "C" void RhpInterfaceDispatch8(); +extern "C" void RhpInterfaceDispatch16(); +extern "C" void RhpInterfaceDispatch32(); +extern "C" void RhpInterfaceDispatch64(); + +extern "C" void RhpVTableOffsetDispatch(); + +typedef void (*InterfaceDispatchStub)(); + +static void * g_rgDispatchStubs[CID_MAX_CACHE_SIZE_LOG2 + 1] = { + (void *)&RhpInterfaceDispatch1, + (void *)&RhpInterfaceDispatch2, + (void *)&RhpInterfaceDispatch4, + (void *)&RhpInterfaceDispatch8, + (void *)&RhpInterfaceDispatch16, + (void *)&RhpInterfaceDispatch32, + (void *)&RhpInterfaceDispatch64, +}; + +// Map a cache size into a linear index. +static uint32_t CacheSizeToIndex(uint32_t cCacheEntries) +{ + switch (cCacheEntries) + { + case 1: + return 0; + case 2: + return 1; + case 4: + return 2; + case 8: + return 3; + case 16: + return 4; + case 32: + return 5; + case 64: + return 6; + default: + UNREACHABLE(); + } +} + +// Allocates and initializes new cache of the given size. If given a previous version of the cache (guaranteed +// to be smaller) it will also pre-populate the new cache with the contents of the old. Additionally the +// address of the interface dispatch stub associated with this size of cache is returned. +static uintptr_t AllocateCache(uint32_t cCacheEntries, InterfaceDispatchCache * pExistingCache, const DispatchCellInfo *pNewCellInfo, void ** ppStub) +{ + if (pNewCellInfo->CellType == DispatchCellType::VTableOffset) + { + ASSERT(pNewCellInfo->VTableOffset < InterfaceDispatchCell::IDC_MaxVTableOffsetPlusOne); + *ppStub = (void *)&RhpVTableOffsetDispatch; + ASSERT(!InterfaceDispatchCell::IsCache(pNewCellInfo->VTableOffset)); + return pNewCellInfo->VTableOffset; + } + + ASSERT((cCacheEntries >= 1) && (cCacheEntries <= CID_MAX_CACHE_SIZE)); + ASSERT((pExistingCache == NULL) || (pExistingCache->m_cEntries < cCacheEntries)); + + InterfaceDispatchCache * pCache = NULL; + + // Transform cache size back into a linear index. + uint32_t idxCacheSize = CacheSizeToIndex(cCacheEntries); + + // Attempt to allocate the head of the free list of the correct cache size. + if (g_rgFreeLists[idxCacheSize] != NULL) + { + CrstHolder lh(&g_sListLock); + + pCache = g_rgFreeLists[idxCacheSize]; + if (pCache != NULL) + { + g_rgFreeLists[idxCacheSize] = pCache->m_pNextFree; + CID_COUNTER_INC(CacheReallocates); + } + } + + if (pCache == NULL) + { + // No luck with the free list, allocate the cache from via the AllocHeap. + pCache = (InterfaceDispatchCache*)g_pAllocHeap->AllocAligned(sizeof(InterfaceDispatchCache) + + (sizeof(InterfaceDispatchCacheEntry) * cCacheEntries), + sizeof(void*) * 2); + if (pCache == NULL) + return NULL; + + CID_COUNTER_INC(CacheAllocates); +#ifdef FEATURE_CID_STATS + CID_g_cbMemoryAllocated += sizeof(InterfaceDispatchCacheEntry) * cCacheEntries; + CID_g_rgAllocatesBySize[idxCacheSize]++; +#endif + } + + // We have a cache block, now initialize it. + pCache->m_pNextFree = NULL; + pCache->m_cEntries = cCacheEntries; + pCache->m_cacheHeader.Initialize(pNewCellInfo); + + // Copy over entries from previous version of the cache (if any) and zero the rest. + if (pExistingCache) + { + memcpy(pCache->m_rgEntries, + pExistingCache->m_rgEntries, + sizeof(InterfaceDispatchCacheEntry) * pExistingCache->m_cEntries); + memset(&pCache->m_rgEntries[pExistingCache->m_cEntries], + 0, + (cCacheEntries - pExistingCache->m_cEntries) * sizeof(InterfaceDispatchCacheEntry)); + } + else + { + memset(pCache->m_rgEntries, + 0, + cCacheEntries * sizeof(InterfaceDispatchCacheEntry)); + } + + // Pass back the stub the corresponds to this cache size. + *ppStub = g_rgDispatchStubs[idxCacheSize]; + + return (uintptr_t)pCache; +} + +// Discards a cache by adding it to a list of caches that may still be in use but will be made available for +// re-allocation at the next GC. +static void DiscardCache(InterfaceDispatchCache * pCache) +{ + CID_COUNTER_INC(CacheDiscards); + + CrstHolder lh(&g_sListLock); + +#if defined(HOST_AMD64) || defined(HOST_ARM64) + + // on AMD64 and ARM64, we can thread the list through the blocks directly + pCache->m_pNextFree = g_pDiscardedCacheList; + g_pDiscardedCacheList = pCache; + +#else // defined(HOST_AMD64) || defined(HOST_ARM64) + + // on other architectures, we cannot overwrite pCache->m_pNextFree yet + // because it shares storage with m_pCell which may still be used as a back + // pointer to the dispatch cell. + + // instead, allocate an auxiliary node (with its own auxiliary free list) + DiscardedCacheBlock * pDiscardedCacheBlock = g_pDiscardedCacheFree; + if (pDiscardedCacheBlock != NULL) + g_pDiscardedCacheFree = pDiscardedCacheBlock->m_pNext; + else + pDiscardedCacheBlock = (DiscardedCacheBlock *)g_pAllocHeap->Alloc(sizeof(DiscardedCacheBlock)); + + if (pDiscardedCacheBlock != NULL) // if we did NOT get the memory, we leak the discarded block + { + pDiscardedCacheBlock->m_pNext = g_pDiscardedCacheList; + pDiscardedCacheBlock->m_pCache = pCache; + + g_pDiscardedCacheList = pDiscardedCacheBlock; + } +#endif // defined(HOST_AMD64) || defined(HOST_ARM64) +} + +// Called during a GC to empty the list of discarded caches (which we can now guarantee aren't being accessed) +// and sort the results into the free lists we maintain for each cache size. +void ReclaimUnusedInterfaceDispatchCaches() +{ + // No need for any locks, we're not racing with any other threads any more. + + // Walk the list of discarded caches. +#if defined(HOST_AMD64) || defined(HOST_ARM64) + + // on AMD64, this is threaded directly through the cache blocks + InterfaceDispatchCache * pCache = g_pDiscardedCacheList; + while (pCache) + { + InterfaceDispatchCache * pNextCache = pCache->m_pNextFree; + + // Transform cache size back into a linear index. + uint32_t idxCacheSize = CacheSizeToIndex(pCache->m_cEntries); + + // Insert the cache onto the head of the correct free list. + pCache->m_pNextFree = g_rgFreeLists[idxCacheSize]; + g_rgFreeLists[idxCacheSize] = pCache; + + pCache = pNextCache; + } + +#else // defined(HOST_AMD64) || defined(HOST_ARM64) + + // on other architectures, we use an auxiliary list instead + DiscardedCacheBlock * pDiscardedCacheBlock = g_pDiscardedCacheList; + while (pDiscardedCacheBlock) + { + InterfaceDispatchCache * pCache = pDiscardedCacheBlock->m_pCache; + + // Transform cache size back into a linear index. + uint32_t idxCacheSize = CacheSizeToIndex(pCache->m_cEntries); + + // Insert the cache onto the head of the correct free list. + pCache->m_pNextFree = g_rgFreeLists[idxCacheSize]; + g_rgFreeLists[idxCacheSize] = pCache; + + // Insert the container to its own free list + DiscardedCacheBlock * pNextDiscardedCacheBlock = pDiscardedCacheBlock->m_pNext; + pDiscardedCacheBlock->m_pNext = g_pDiscardedCacheFree; + g_pDiscardedCacheFree = pDiscardedCacheBlock; + pDiscardedCacheBlock = pNextDiscardedCacheBlock; + } + +#endif // defined(HOST_AMD64) || defined(HOST_ARM64) + + // We processed all the discarded entries, so we can simply NULL the list head. + g_pDiscardedCacheList = NULL; +} + +// One time initialization of interface dispatch. +bool InitializeInterfaceDispatch() +{ + g_pAllocHeap = new (nothrow) AllocHeap(); + if (g_pAllocHeap == NULL) + return false; + + if (!g_pAllocHeap->Init()) + return false; + + g_sListLock.Init(CrstInterfaceDispatchGlobalLists, CRST_DEFAULT); + + return true; +} + +COOP_PINVOKE_HELPER(PTR_Code, RhpUpdateDispatchCellCache, (InterfaceDispatchCell * pCell, PTR_Code pTargetCode, MethodTable* pInstanceType, DispatchCellInfo *pNewCellInfo)) +{ + // Attempt to update the cache with this new mapping (if we have any cache at all, the initial state + // is none). + InterfaceDispatchCache * pCache = (InterfaceDispatchCache*)pCell->GetCache(); + uint32_t cOldCacheEntries = 0; + if (pCache != NULL) + { + InterfaceDispatchCacheEntry * pCacheEntry = pCache->m_rgEntries; + for (uint32_t i = 0; i < pCache->m_cEntries; i++, pCacheEntry++) + { + if (pCacheEntry->m_pInstanceType == NULL) + { + if (UpdateCacheEntryAtomically(pCacheEntry, pInstanceType, pTargetCode)) + return (PTR_Code)pTargetCode; + } + } + + cOldCacheEntries = pCache->m_cEntries; + } + + // Failed to update an existing cache, we need to allocate a new cache. The old one, if any, might + // still be in use so we can't simply reclaim it. Instead we keep it around until the next GC at which + // point we know no code is holding a reference to it. Particular cache sizes are associated with a + // (globally shared) stub which implicitly knows the size of the cache. + + if (cOldCacheEntries == CID_MAX_CACHE_SIZE) + { + // We already reached the maximum cache size we wish to allocate. For now don't attempt to cache + // the mapping we just did: there's no safe way to update the existing cache right now if it + // doesn't have an empty entries. There are schemes that would let us do this at the next GC point + // but it's not clear whether we should do this or re-tune the cache max size, we need to measure + // this. + CID_COUNTER_INC(CacheSizeOverflows); + return (PTR_Code)pTargetCode; + } + + uint32_t cNewCacheEntries = cOldCacheEntries ? cOldCacheEntries * 2 : 1; + void *pStub; + uintptr_t newCacheValue = AllocateCache(cNewCacheEntries, pCache, pNewCellInfo, &pStub); + if (newCacheValue == 0) + { + CID_COUNTER_INC(CacheOutOfMemory); + return (PTR_Code)pTargetCode; + } + + if (InterfaceDispatchCell::IsCache(newCacheValue)) + { + pCache = (InterfaceDispatchCache*)newCacheValue; +#if !defined(HOST_AMD64) && !defined(HOST_ARM64) + // Set back pointer to interface dispatch cell for non-AMD64 and non-ARM64 + // for AMD64 and ARM64, we have enough registers to make this trick unnecessary + pCache->m_pCell = pCell; +#endif // !defined(HOST_AMD64) && !defined(HOST_ARM64) + + // Add entry to the first unused slot. + InterfaceDispatchCacheEntry * pCacheEntry = &pCache->m_rgEntries[cOldCacheEntries]; + pCacheEntry->m_pInstanceType = pInstanceType; + pCacheEntry->m_pTargetCode = pTargetCode; + } + + // Publish the new cache by atomically updating both the cache and stub pointers in the indirection + // cell. This returns us a cache to discard which may be NULL (no previous cache), the previous cache + // value or the cache we just allocated (another thread performed an update first). + InterfaceDispatchCache * pDiscardedCache = UpdateCellStubAndCache(pCell, pStub, newCacheValue); + if (pDiscardedCache) + DiscardCache(pDiscardedCache); + + return (PTR_Code)pTargetCode; +} + +COOP_PINVOKE_HELPER(PTR_Code, RhpSearchDispatchCellCache, (InterfaceDispatchCell * pCell, MethodTable* pInstanceType)) +{ + // This function must be implemented in native code so that we do not take a GC while walking the cache + InterfaceDispatchCache * pCache = (InterfaceDispatchCache*)pCell->GetCache(); + if (pCache != NULL) + { + InterfaceDispatchCacheEntry * pCacheEntry = pCache->m_rgEntries; + for (uint32_t i = 0; i < pCache->m_cEntries; i++, pCacheEntry++) + if (pCacheEntry->m_pInstanceType == pInstanceType) + return (PTR_Code)pCacheEntry->m_pTargetCode; + } + + return nullptr; +} + +// Given a dispatch cell, get the type and slot associated with it. This function MUST be implemented +// in cooperative native code, as the m_pCache field on the cell is unsafe to access from managed +// code due to its use of the GC state as a lock, and as lifetime control +COOP_PINVOKE_HELPER(void, RhpGetDispatchCellInfo, (InterfaceDispatchCell * pCell, DispatchCellInfo* pDispatchCellInfo)) +{ + *pDispatchCellInfo = pCell->GetDispatchCellInfo(); +} + +#endif // FEATURE_CACHED_INTERFACE_DISPATCH diff --git a/src/coreclr/nativeaot/Runtime/CachedInterfaceDispatch.h b/src/coreclr/nativeaot/Runtime/CachedInterfaceDispatch.h new file mode 100644 index 00000000000000..135c09ec41f3f1 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/CachedInterfaceDispatch.h @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ==--== +// +// Shared (non-architecture specific) portions of a mechanism to perform interface dispatch using an alternate +// mechanism to VSD that does not require runtime generation of code. +// +// ============================================================================ + +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH + +bool InitializeInterfaceDispatch(); +void ReclaimUnusedInterfaceDispatchCaches(); + +// Interface dispatch caches contain an array of these entries. An instance of a cache is paired with a stub +// that implicitly knows how many entries are contained. These entries must be aligned to twice the alignment +// of a pointer due to the synchonization mechanism used to update them at runtime. +struct InterfaceDispatchCacheEntry +{ + MethodTable * m_pInstanceType; // Potential type of the object instance being dispatched on + void * m_pTargetCode; // Method to dispatch to if the actual instance type matches the above +}; + +// The interface dispatch cache itself. As well as the entries we include the cache size (since logic such as +// cache miss processing needs to determine this value in a synchronized manner, so it can't be contained in +// the owning interface dispatch indirection cell) and a list entry used to link the caches in one of a couple +// of lists related to cache reclamation. +#pragma warning(push) +#pragma warning(disable:4200) // nonstandard extension used: zero-sized array in struct/union +struct InterfaceDispatchCell; +struct InterfaceDispatchCache +{ + InterfaceDispatchCacheHeader m_cacheHeader; + union + { + InterfaceDispatchCache * m_pNextFree; // next in free list +#ifndef HOST_AMD64 + InterfaceDispatchCell * m_pCell; // pointer back to interface dispatch cell - not used for AMD64 +#endif + }; + uint32_t m_cEntries; + InterfaceDispatchCacheEntry m_rgEntries[]; +}; +#pragma warning(pop) + +#endif // FEATURE_CACHED_INTERFACE_DISPATCH diff --git a/src/coreclr/nativeaot/Runtime/CallDescr.h b/src/coreclr/nativeaot/Runtime/CallDescr.h new file mode 100644 index 00000000000000..946b96d2c8e7c9 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/CallDescr.h @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +struct CallDescrData +{ + uint8_t* pSrc; + int numStackSlots; + int fpReturnSize; + uint8_t* pArgumentRegisters; + uint8_t* pFloatArgumentRegisters; + void* pTarget; + void* pReturnBuffer; +}; diff --git a/src/coreclr/nativeaot/Runtime/CommonMacros.h b/src/coreclr/nativeaot/Runtime/CommonMacros.h new file mode 100644 index 00000000000000..836c6852f5b4ac --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/CommonMacros.h @@ -0,0 +1,246 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __COMMONMACROS_H__ +#define __COMMONMACROS_H__ + +#include "rhassert.h" + +#define EXTERN_C extern "C" + +#if defined(HOST_X86) && !defined(HOST_UNIX) +#define FASTCALL __fastcall +#define STDCALL __stdcall +#else +#define FASTCALL +#define STDCALL +#endif + +#define REDHAWK_API +#define REDHAWK_CALLCONV FASTCALL + +#ifdef _MSC_VER + +#define MSVC_SAVE_WARNING_STATE() __pragma(warning(push)) +#define MSVC_DISABLE_WARNING(warn_num) __pragma(warning(disable: warn_num)) +#define MSVC_RESTORE_WARNING_STATE() __pragma(warning(pop)) + +#else + +#define MSVC_SAVE_WARNING_STATE() +#define MSVC_DISABLE_WARNING(warn_num) +#define MSVC_RESTORE_WARNING_STATE() + +#endif // _MSC_VER + +#ifndef COUNTOF +template +char (*COUNTOF_helper(_CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray]; +#define COUNTOF(_Array) sizeof(*COUNTOF_helper(_Array)) +#endif // COUNTOF + +#ifndef offsetof +#define offsetof(s,m) (uintptr_t)( (intptr_t)&reinterpret_cast((((s *)0)->m)) ) +#endif // offsetof + +#ifndef FORCEINLINE +#define FORCEINLINE __forceinline +#endif + +#ifndef NOINLINE +#ifdef _MSC_VER +#define NOINLINE __declspec(noinline) +#else +#define NOINLINE __attribute__((noinline)) +#endif +#endif + +#ifndef __GCENV_BASE_INCLUDED__ + +// +// This macro returns val rounded up as necessary to be a multiple of alignment; alignment must be a power of 2 +// +inline uintptr_t ALIGN_UP(uintptr_t val, uintptr_t alignment); +template +inline T* ALIGN_UP(T* val, uintptr_t alignment); + +inline uintptr_t ALIGN_DOWN(uintptr_t val, uintptr_t alignment); +template +inline T* ALIGN_DOWN(T* val, uintptr_t alignment); + +#endif // !__GCENV_BASE_INCLUDED__ + +inline bool IS_ALIGNED(uintptr_t val, uintptr_t alignment); +template +inline bool IS_ALIGNED(T* val, uintptr_t alignment); + +#ifndef DACCESS_COMPILE + +#ifndef ZeroMemory +#define ZeroMemory(_dst, _size) memset((_dst), 0, (_size)) +#endif + +//------------------------------------------------------------------------------------------------- +// min/max + +#ifndef min +#define min(_a, _b) ((_a) < (_b) ? (_a) : (_b)) +#endif +#ifndef max +#define max(_a, _b) ((_a) < (_b) ? (_b) : (_a)) +#endif + +#endif // !DACCESS_COMPILE + +//------------------------------------------------------------------------------------------------- +// Platform-specific defines + +#if defined(HOST_AMD64) + +#define LOG2_PTRSIZE 3 +#define POINTER_SIZE 8 + +#elif defined(HOST_X86) + +#define LOG2_PTRSIZE 2 +#define POINTER_SIZE 4 + +#elif defined(HOST_ARM) + +#define LOG2_PTRSIZE 2 +#define POINTER_SIZE 4 + +#elif defined(HOST_ARM64) + +#define LOG2_PTRSIZE 3 +#define POINTER_SIZE 8 + +#elif defined (HOST_WASM) + +#define LOG2_PTRSIZE 2 +#define POINTER_SIZE 4 + +#else +#error Unsupported target architecture +#endif + +#ifndef __GCENV_BASE_INCLUDED__ +#if defined(HOST_AMD64) + +#define DATA_ALIGNMENT 8 +#define OS_PAGE_SIZE 0x1000 + +#elif defined(HOST_X86) + +#define DATA_ALIGNMENT 4 +#ifndef OS_PAGE_SIZE +#define OS_PAGE_SIZE 0x1000 +#endif + +#elif defined(HOST_ARM) + +#define DATA_ALIGNMENT 4 +#ifndef OS_PAGE_SIZE +#define OS_PAGE_SIZE 0x1000 +#endif + +#elif defined(HOST_ARM64) + +#define DATA_ALIGNMENT 8 +#ifndef OS_PAGE_SIZE +#define OS_PAGE_SIZE 0x1000 +#endif + +#elif defined(HOST_WASM) + +#define DATA_ALIGNMENT 4 +#ifndef OS_PAGE_SIZE +#define OS_PAGE_SIZE 0x4 +#endif + +#else +#error Unsupported target architecture +#endif +#endif // __GCENV_BASE_INCLUDED__ + +#if defined(TARGET_ARM) +#define THUMB_CODE 1 +#endif + +// +// Define an unmanaged function called from managed code that needs to execute in co-operative GC mode. (There +// should be very few of these, most such functions will be simply p/invoked). +// +#define COOP_PINVOKE_HELPER(_rettype, _method, _args) EXTERN_C REDHAWK_API _rettype REDHAWK_CALLCONV _method _args +#ifdef HOST_X86 +// We have helpers that act like memcpy and memset from the CRT, so they need to be __cdecl. +#define COOP_PINVOKE_CDECL_HELPER(_rettype, _method, _args) EXTERN_C REDHAWK_API _rettype __cdecl _method _args +#else +#define COOP_PINVOKE_CDECL_HELPER COOP_PINVOKE_HELPER +#endif + +typedef bool CLR_BOOL; + +#if defined(TARGET_X86) || defined(TARGET_AMD64) +// The return value is artifically widened on x86 and amd64 +typedef int32_t FC_BOOL_RET; +#else +typedef bool FC_BOOL_RET; +#endif + +#define FC_RETURN_BOOL(x) do { return !!(x); } while(0) + +#ifndef DACCESS_COMPILE +#define IN_DAC(x) +#define NOT_IN_DAC(x) x +#else +#define IN_DAC(x) x +#define NOT_IN_DAC(x) +#endif + +#define INLINE inline + +enum STARTUP_TIMELINE_EVENT_ID +{ + PROCESS_ATTACH_BEGIN = 0, + NONGC_INIT_COMPLETE, + GC_INIT_COMPLETE, + PROCESS_ATTACH_COMPLETE, + + NUM_STARTUP_TIMELINE_EVENTS +}; + +#ifdef PROFILE_STARTUP +extern unsigned __int64 g_startupTimelineEvents[NUM_STARTUP_TIMELINE_EVENTS]; +#define STARTUP_TIMELINE_EVENT(eventid) PalQueryPerformanceCounter((LARGE_INTEGER*)&g_startupTimelineEvents[eventid]); +#else // PROFILE_STARTUP +#define STARTUP_TIMELINE_EVENT(eventid) +#endif // PROFILE_STARTUP + +#ifndef C_ASSERT +#define C_ASSERT(e) static_assert(e, #e) +#endif // C_ASSERT + +#ifdef __llvm__ +#define DECLSPEC_THREAD __thread +#else // __llvm__ +#define DECLSPEC_THREAD __declspec(thread) +#endif // !__llvm__ + +#ifndef __GCENV_BASE_INCLUDED__ +#if !defined(_INC_WINDOWS) +#ifdef _WIN32 +// this must exactly match the typedef used by windows.h +typedef long HRESULT; +#else +typedef int32_t HRESULT; +#endif + +#define S_OK 0x0 +#define E_FAIL 0x80004005 + +#define UNREFERENCED_PARAMETER(P) (void)(P) +#endif // !defined(_INC_WINDOWS) +#endif // __GCENV_BASE_INCLUDED__ + +#endif // __COMMONMACROS_H__ diff --git a/src/coreclr/nativeaot/Runtime/CommonMacros.inl b/src/coreclr/nativeaot/Runtime/CommonMacros.inl new file mode 100644 index 00000000000000..a9ad0b2b6764e0 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/CommonMacros.inl @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __GCENV_BASE_INCLUDED__ + +// +// This macro returns val rounded up as necessary to be a multiple of alignment; alignment must be a power of 2 +// +inline uintptr_t ALIGN_UP( uintptr_t val, uintptr_t alignment ) +{ + // alignment must be a power of 2 for this implementation to work (need modulo otherwise) + ASSERT( 0 == (alignment & (alignment - 1)) ); + uintptr_t result = (val + (alignment - 1)) & ~(alignment - 1); + ASSERT( result >= val ); // check for overflow + + return result; +} + +template +inline T* ALIGN_UP(T* val, uintptr_t alignment) +{ + return reinterpret_cast(ALIGN_UP(reinterpret_cast(val), alignment)); +} + +inline uintptr_t ALIGN_DOWN( uintptr_t val, uintptr_t alignment ) +{ + // alignment must be a power of 2 for this implementation to work (need modulo otherwise) + ASSERT( 0 == (alignment & (alignment - 1)) ); + uintptr_t result = val & ~(alignment - 1); + return result; +} + +template +inline T* ALIGN_DOWN(T* val, uintptr_t alignment) +{ + return reinterpret_cast(ALIGN_DOWN(reinterpret_cast(val), alignment)); +} + +#endif // !__GCENV_BASE_INCLUDED__ + +inline bool IS_ALIGNED(uintptr_t val, uintptr_t alignment) +{ + ASSERT(0 == (alignment & (alignment - 1))); + return 0 == (val & (alignment - 1)); +} + +template +inline bool IS_ALIGNED(T* val, uintptr_t alignment) +{ + ASSERT(0 == (alignment & (alignment - 1))); + return IS_ALIGNED(reinterpret_cast(val), alignment); +} + +// Convert from a PCODE to the corresponding PINSTR. On many architectures this will be the identity function; +// on ARM, this will mask off the THUMB bit. +inline TADDR PCODEToPINSTR(PCODE pc) +{ +#ifdef TARGET_ARM + return dac_cast(pc & ~THUMB_CODE); +#else + return dac_cast(pc); +#endif +} + +// Convert from a PINSTR to the corresponding PCODE. On many architectures this will be the identity function; +// on ARM, this will raise the THUMB bit. +inline PCODE PINSTRToPCODE(TADDR addr) +{ +#ifdef TARGET_ARM + return dac_cast(addr | THUMB_CODE); +#else + return dac_cast(addr); +#endif +} diff --git a/src/coreclr/nativeaot/Runtime/Crst.cpp b/src/coreclr/nativeaot/Runtime/Crst.cpp new file mode 100644 index 00000000000000..48a3ee7fde0312 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/Crst.cpp @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "holder.h" +#include "Crst.h" + +void CrstStatic::Init(CrstType eType, CrstFlags eFlags) +{ + UNREFERENCED_PARAMETER(eType); + UNREFERENCED_PARAMETER(eFlags); +#ifndef DACCESS_COMPILE +#if defined(_DEBUG) + m_uiOwnerId.Clear(); +#endif // _DEBUG + PalInitializeCriticalSectionEx(&m_sCritSec, 0, 0); +#endif // !DACCESS_COMPILE +} + +void CrstStatic::Destroy() +{ +#ifndef DACCESS_COMPILE + PalDeleteCriticalSection(&m_sCritSec); +#endif // !DACCESS_COMPILE +} + +// static +void CrstStatic::Enter(CrstStatic *pCrst) +{ +#ifndef DACCESS_COMPILE + PalEnterCriticalSection(&pCrst->m_sCritSec); +#if defined(_DEBUG) + pCrst->m_uiOwnerId.SetToCurrentThread(); +#endif // _DEBUG +#else + UNREFERENCED_PARAMETER(pCrst); +#endif // !DACCESS_COMPILE +} + +// static +void CrstStatic::Leave(CrstStatic *pCrst) +{ +#ifndef DACCESS_COMPILE +#if defined(_DEBUG) + pCrst->m_uiOwnerId.Clear(); +#endif // _DEBUG + PalLeaveCriticalSection(&pCrst->m_sCritSec); +#else + UNREFERENCED_PARAMETER(pCrst); +#endif // !DACCESS_COMPILE +} + +#if defined(_DEBUG) +bool CrstStatic::OwnedByCurrentThread() +{ +#ifndef DACCESS_COMPILE + return m_uiOwnerId.IsCurrentThread(); +#else + return false; +#endif +} + +EEThreadId CrstStatic::GetHolderThreadId() +{ + return m_uiOwnerId; +} +#endif // _DEBUG diff --git a/src/coreclr/nativeaot/Runtime/Crst.h b/src/coreclr/nativeaot/Runtime/Crst.h new file mode 100644 index 00000000000000..658b0186429d02 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/Crst.h @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// ----------------------------------------------------------------------------------------------------------- +// +// Minimal Crst implementation based on CRITICAL_SECTION. Doesn't support much except for the basic locking +// functionality (in particular there is no rank violation checking). +// + +enum CrstType +{ + CrstHandleTable, + CrstDispatchCache, + CrstAllocHeap, + CrstGenericInstHashtab, + CrstMemAccessMgr, + CrstInterfaceDispatchGlobalLists, + CrstStressLog, + CrstRestrictedCallouts, + CrstGcStressControl, + CrstSuspendEE, + CrstCastCache, + CrstYieldProcessorNormalized, +}; + +enum CrstFlags +{ + CRST_DEFAULT = 0x0, + CRST_REENTRANCY = 0x0, + CRST_UNSAFE_SAMELEVEL = 0x0, + CRST_UNSAFE_ANYMODE = 0x0, + CRST_DEBUGGER_THREAD = 0x0, +}; + +// Static version of Crst with no default constructor (user must call Init() before use). +class CrstStatic +{ +public: + void Init(CrstType eType, CrstFlags eFlags = CRST_DEFAULT); + bool InitNoThrow(CrstType eType, CrstFlags eFlags = CRST_DEFAULT) { Init(eType, eFlags); return true; } + void Destroy(); + void Enter() { CrstStatic::Enter(this); } + void Leave() { CrstStatic::Leave(this); } + static void Enter(CrstStatic *pCrst); + static void Leave(CrstStatic *pCrst); +#if defined(_DEBUG) + bool OwnedByCurrentThread(); + EEThreadId GetHolderThreadId(); +#endif // _DEBUG + +private: + CRITICAL_SECTION m_sCritSec; +#if defined(_DEBUG) + EEThreadId m_uiOwnerId; +#endif // _DEBUG +}; + +// Non-static version that will initialize itself during construction. +class Crst : public CrstStatic +{ +public: + Crst(CrstType eType, CrstFlags eFlags = CRST_DEFAULT) + : CrstStatic() + { Init(eType, eFlags); } +}; + +// Holder for a Crst instance. +class CrstHolder +{ + CrstStatic * m_pLock; + +public: + CrstHolder(CrstStatic * pLock) + : m_pLock(pLock) + { + m_pLock->Enter(); + } + + ~CrstHolder() + { + m_pLock->Leave(); + } +}; + +class CrstHolderWithState +{ + CrstStatic * m_pLock; + bool m_fAcquired; + +public: + CrstHolderWithState(CrstStatic * pLock, bool fAcquire = true) + : m_pLock(pLock), m_fAcquired(fAcquire) + { + if (fAcquire) + m_pLock->Enter(); + } + + ~CrstHolderWithState() + { + if (m_fAcquired) + m_pLock->Leave(); + } + + void Acquire() + { + if (!m_fAcquired) + { + m_pLock->Enter(); + m_fAcquired = true; + } + } + + void Release() + { + if (m_fAcquired) + { + m_pLock->Leave(); + m_fAcquired = false; + } + } + + CrstStatic * GetValue() + { + return m_pLock; + } +}; diff --git a/src/coreclr/nativeaot/Runtime/DebugHeader.cpp b/src/coreclr/nativeaot/Runtime/DebugHeader.cpp new file mode 100644 index 00000000000000..e1025ce27e664c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/DebugHeader.cpp @@ -0,0 +1,279 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" +#include "gcenv.h" +#include "gcheaputilities.h" +#include "gcinterface.dac.h" +#include "rhassert.h" +#include "TargetPtrs.h" +#include "varint.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "holder.h" +#include "RuntimeInstance.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "threadstore.h" + +GPTR_DECL(MethodTable, g_pFreeObjectEEType); + +struct DebugTypeEntry +{ + const char *TypeName; + const char *FieldName; + uint32_t FieldOffset; + uint32_t ResevedPadding; +}; + +struct GlobalValueEntry +{ + const char *Name; + const void *Address; +}; + +// This size should be one bigger than the number of entries since a null entry +// signifies the end of the array. +static constexpr size_t DebugTypeEntriesArraySize = 96; +static DebugTypeEntry s_DebugEntries[DebugTypeEntriesArraySize]; + +// This size should be one bigger than the number of entries since a null entry +// signifies the end of the array. +static constexpr size_t GlobalEntriesArraySize = 6; +static GlobalValueEntry s_GlobalEntries[GlobalEntriesArraySize]; + +// This structure is part of a in-memory serialization format that is used by diagnostic tools to +// reason about the runtime. As a contract with our diagnostic tools it must be kept up-to-date +// by changing the MajorVersion when breaking changes occur. If you are changing the runtime then +// you are responsible for understanding what changes are breaking changes. +// +// If you do want to make a breaking change please coordinate with diagnostics team as breaking changes +// require debugger side components to be updated, and then the new versions will need to be distributed +// to customers. Ideally you will check in updates to the runtime components, the debugger parser +// components, and the format specification at the same time. +// +// Although not guaranteed to be exhaustive, at a glance these are some potential breaking changes: +// - Removing a field from this structure +// - Reordering fields in the structure +// - Changing the data type of a field in this structure +// - Changing the data type of a field in another structure that is being refered to here with +// the offsetof() operator +// - Changing the data type of a global whose address is recorded in this structure +// - Changing the meaning of a field or global refered to in this structure so that it can no longer +// be used in the manner the format specification describes. +struct DotNetRuntimeDebugHeader +{ + // The cookie serves as a sanity check against process corruption or being requested + // to treat some other non-.Net module as though it did contain the coreRT runtime. + // It can also be changed if we want to make a breaking change so drastic that + // earlier debuggers should treat the module as if it had no .Net runtime at all. + // If the cookie is valid a debugger is safe to assume the Major/Minor version fields + // will follow, but any contents beyond that depends on the version values. + // The cookie value is currently set to 0x4E 0x41 0x44 0x48 (DNDH in ascii) + const uint8_t Cookie[4] = { 0x44, 0x4E, 0x44, 0x48 }; + + // This counter can be incremented to indicate breaking changes + // This field must be encoded little endian, regardless of the typical endianess of + // the machine + const uint16_t MajorVersion = 1; + + // This counter can be incremented to indicate back-compatible changes + // This field must be encoded little endian, regardless of the typical endianess of + // the machine + const uint16_t MinorVersion = 0; + + // These flags must be encoded little endian, regardless of the typical endianess of + // the machine. Ie Bit 0 is the least significant bit of the first byte. + // Bit 0 - Set if the pointer size is 8 bytes, otherwise pointer size is 4 bytes + // Bit 1 - Set if the machine is big endian + // The high 30 bits are reserved. Changes to these bits will be considered a + // back-compatible change. + const uint32_t Flags = sizeof(void*) == 8 ? 0x1 : 0x0; + + // Reserved - Currently it only serves as alignment padding for the pointers which + // follow but future usage will be considered a back-compatible change. + const uint32_t ReservedPadding1 = 0; + + // Header pointers below here are encoded using the defined pointer size and endianess + // specified in the Flags field. The data within the contracts they point to also uses + // the same pointer size and endianess encoding unless otherwise specified. + + // A pointer to an array describing important types and their offsets + DebugTypeEntry (* volatile DebugTypeEntries)[DebugTypeEntriesArraySize] = nullptr; + + // A pointer to an array that contains pointers to important globals + GlobalValueEntry (* volatile GlobalEntries)[GlobalEntriesArraySize] = nullptr; +}; + +#ifdef TARGET_WINDOWS +#pragma comment (linker, "/EXPORT:DotNetRuntimeDebugHeader,DATA") +#endif +extern "C" DotNetRuntimeDebugHeader DotNetRuntimeDebugHeader = {}; + +#define MAKE_DEBUG_ENTRY(TypeName, FieldName, Value) \ + do \ + { \ + s_DebugEntries[currentDebugPos] = { #TypeName, #FieldName, Value, 0 }; \ + ++currentDebugPos; \ + ASSERT(currentDebugPos <= DebugTypeEntriesArraySize); \ + } while(0) + +#define MAKE_DEBUG_FIELD_ENTRY(TypeName, FieldName) MAKE_DEBUG_ENTRY(TypeName, FieldName, offsetof(TypeName, FieldName)) + +#define MAKE_DEFINE_ENTRY(Name, Value) MAKE_DEBUG_ENTRY(Globals, Name, Value) + +#define MAKE_SIZE_ENTRY(TypeName) MAKE_DEBUG_ENTRY(TypeName, SIZEOF, sizeof(TypeName)) + +#define MAKE_GLOBAL_ENTRY(Name) \ + do \ + { \ + s_GlobalEntries[currentGlobalPos] = { #Name, Name }; \ + ++currentGlobalPos; \ + ASSERT(currentGlobalPos <= GlobalEntriesArraySize) \ + } while(0) \ + +extern "C" void PopulateDebugHeaders() +{ + size_t currentDebugPos = 0; + size_t currentGlobalPos = 0; + + ZeroMemory(s_DebugEntries, DebugTypeEntriesArraySize); + ZeroMemory(s_GlobalEntries, GlobalEntriesArraySize); + + MAKE_SIZE_ENTRY(GcDacVars); + MAKE_DEBUG_FIELD_ENTRY(GcDacVars, major_version_number); + MAKE_DEBUG_FIELD_ENTRY(GcDacVars, minor_version_number); + MAKE_DEBUG_FIELD_ENTRY(GcDacVars, generation_size); + MAKE_DEBUG_FIELD_ENTRY(GcDacVars, total_generation_count); + MAKE_DEBUG_FIELD_ENTRY(GcDacVars, built_with_svr); + MAKE_DEBUG_FIELD_ENTRY(GcDacVars, finalize_queue); + MAKE_DEBUG_FIELD_ENTRY(GcDacVars, generation_table); + MAKE_DEBUG_FIELD_ENTRY(GcDacVars, ephemeral_heap_segment); + MAKE_DEBUG_FIELD_ENTRY(GcDacVars, alloc_allocated); + MAKE_DEBUG_FIELD_ENTRY(GcDacVars, n_heaps); + MAKE_DEBUG_FIELD_ENTRY(GcDacVars, g_heaps); + + MAKE_SIZE_ENTRY(dac_gc_heap); + MAKE_DEBUG_FIELD_ENTRY(dac_gc_heap, alloc_allocated); + MAKE_DEBUG_FIELD_ENTRY(dac_gc_heap, ephemeral_heap_segment); + MAKE_DEBUG_FIELD_ENTRY(dac_gc_heap, finalize_queue); + MAKE_DEBUG_FIELD_ENTRY(dac_gc_heap, generation_table); + + MAKE_SIZE_ENTRY(gc_alloc_context); + MAKE_DEBUG_FIELD_ENTRY(gc_alloc_context, alloc_ptr); + MAKE_DEBUG_FIELD_ENTRY(gc_alloc_context, alloc_limit); + MAKE_DEBUG_FIELD_ENTRY(gc_alloc_context, alloc_bytes); + MAKE_DEBUG_FIELD_ENTRY(gc_alloc_context, alloc_bytes_uoh); + MAKE_DEBUG_FIELD_ENTRY(gc_alloc_context, alloc_count); + + MAKE_SIZE_ENTRY(dac_generation); + MAKE_DEBUG_FIELD_ENTRY(dac_generation, allocation_context); + MAKE_DEBUG_FIELD_ENTRY(dac_generation, start_segment); + MAKE_DEBUG_FIELD_ENTRY(dac_generation, allocation_start); + + MAKE_SIZE_ENTRY(dac_heap_segment); + MAKE_DEBUG_FIELD_ENTRY(dac_heap_segment, allocated); + MAKE_DEBUG_FIELD_ENTRY(dac_heap_segment, committed); + MAKE_DEBUG_FIELD_ENTRY(dac_heap_segment, reserved); + MAKE_DEBUG_FIELD_ENTRY(dac_heap_segment, used); + MAKE_DEBUG_FIELD_ENTRY(dac_heap_segment, mem); + MAKE_DEBUG_FIELD_ENTRY(dac_heap_segment, flags); + MAKE_DEBUG_FIELD_ENTRY(dac_heap_segment, next); + MAKE_DEBUG_FIELD_ENTRY(dac_heap_segment, background_allocated); + MAKE_DEBUG_FIELD_ENTRY(dac_heap_segment, heap); + + MAKE_DEFINE_ENTRY(FinalizeExtraSegCount, dac_finalize_queue::ExtraSegCount); + MAKE_DEFINE_ENTRY(MinObjectSize, MIN_OBJECT_SIZE); + + MAKE_SIZE_ENTRY(ThreadStore); + MAKE_DEBUG_FIELD_ENTRY(ThreadStore, m_ThreadList); + + MAKE_SIZE_ENTRY(ThreadBuffer); + MAKE_DEBUG_FIELD_ENTRY(ThreadBuffer, m_pNext); + MAKE_DEBUG_FIELD_ENTRY(ThreadBuffer, m_rgbAllocContextBuffer); + MAKE_DEBUG_FIELD_ENTRY(ThreadBuffer, m_threadId); + MAKE_DEBUG_FIELD_ENTRY(ThreadBuffer, m_pThreadStressLog); + + MAKE_SIZE_ENTRY(MethodTable); + MAKE_DEBUG_FIELD_ENTRY(MethodTable, m_uBaseSize); + MAKE_DEBUG_FIELD_ENTRY(MethodTable, m_usComponentSize); + MAKE_DEBUG_FIELD_ENTRY(MethodTable, m_usFlags); + MAKE_DEBUG_ENTRY(MethodTable, m_pBaseType, offsetof(MethodTable, m_RelatedType) + offsetof(MethodTable::RelatedTypeUnion, m_pBaseType)); + MAKE_DEBUG_ENTRY(MethodTable, m_ppBaseTypeViaIAT, offsetof(MethodTable, m_RelatedType) + offsetof(MethodTable::RelatedTypeUnion, m_ppBaseTypeViaIAT)); + MAKE_DEBUG_ENTRY(MethodTable, m_pCanonicalType, offsetof(MethodTable, m_RelatedType) + offsetof(MethodTable::RelatedTypeUnion, m_pCanonicalType)); + MAKE_DEBUG_ENTRY(MethodTable, m_ppCanonicalTypeViaIAT, offsetof(MethodTable, m_RelatedType) + offsetof(MethodTable::RelatedTypeUnion, m_ppCanonicalTypeViaIAT)); + MAKE_DEBUG_ENTRY(MethodTable, m_pRelatedParameterType, offsetof(MethodTable, m_RelatedType) + offsetof(MethodTable::RelatedTypeUnion, m_pRelatedParameterType)); + MAKE_DEBUG_ENTRY(MethodTable, m_ppRelatedParameterTypeViaIAT, offsetof(MethodTable, m_RelatedType) + offsetof(MethodTable::RelatedTypeUnion, m_ppRelatedParameterTypeViaIAT)); + MAKE_DEBUG_FIELD_ENTRY(MethodTable, m_VTable); + + MAKE_SIZE_ENTRY(StressLog); + MAKE_DEBUG_FIELD_ENTRY(StressLog, facilitiesToLog); + MAKE_DEBUG_FIELD_ENTRY(StressLog, levelToLog); + MAKE_DEBUG_FIELD_ENTRY(StressLog, totalChunk); + MAKE_DEBUG_FIELD_ENTRY(StressLog, logs); + MAKE_DEBUG_FIELD_ENTRY(StressLog, tickFrequency); + MAKE_DEBUG_FIELD_ENTRY(StressLog, startTimeStamp); + MAKE_DEBUG_FIELD_ENTRY(StressLog, startTime); + MAKE_DEBUG_FIELD_ENTRY(StressLog, moduleOffset); + + MAKE_SIZE_ENTRY(ThreadStressLog); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, next); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, threadId); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, isDead); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, readHasWrapped); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, writeHasWrapped); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, curPtr); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, readPtr); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, chunkListHead); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, chunkListTail); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, curReadChunk); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, curWriteChunk); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, chunkListLength); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, pThread); + MAKE_DEBUG_FIELD_ENTRY(ThreadStressLog, origCurPtr); + + MAKE_SIZE_ENTRY(StressLogChunk); + MAKE_DEFINE_ENTRY(StressLogChunk_ChunkSize, STRESSLOG_CHUNK_SIZE); + MAKE_DEBUG_FIELD_ENTRY(StressLogChunk, prev); + MAKE_DEBUG_FIELD_ENTRY(StressLogChunk, next); + MAKE_DEBUG_FIELD_ENTRY(StressLogChunk, buf); + MAKE_DEBUG_FIELD_ENTRY(StressLogChunk, dwSig1); + MAKE_DEBUG_FIELD_ENTRY(StressLogChunk, dwSig2); + + MAKE_SIZE_ENTRY(StressMsg); + MAKE_DEBUG_FIELD_ENTRY(StressMsg, fmtOffsCArgs); + MAKE_DEBUG_FIELD_ENTRY(StressMsg, facility); + MAKE_DEBUG_FIELD_ENTRY(StressMsg, timeStamp); + MAKE_DEBUG_FIELD_ENTRY(StressMsg, args); + + MAKE_SIZE_ENTRY(RuntimeInstance); + MAKE_DEBUG_FIELD_ENTRY(RuntimeInstance, m_pThreadStore); + + RuntimeInstance *g_pTheRuntimeInstance = GetRuntimeInstance(); + MAKE_GLOBAL_ENTRY(g_pTheRuntimeInstance); + + MAKE_GLOBAL_ENTRY(g_gcDacGlobals); + + MAKE_GLOBAL_ENTRY(g_pFreeObjectEEType); + + void *g_stressLog = &StressLog::theLog; + MAKE_GLOBAL_ENTRY(g_stressLog); + + // Some DAC functions need to know the module base address, easiest way is with + // the HANDLE to our module which is the base address. + HANDLE moduleBaseAddress = PalGetModuleHandleFromPointer((void *)&PopulateDebugHeaders); + MAKE_GLOBAL_ENTRY(moduleBaseAddress); + + DotNetRuntimeDebugHeader.DebugTypeEntries = &s_DebugEntries; + DotNetRuntimeDebugHeader.GlobalEntries = &s_GlobalEntries; + + static_assert(MethodTable::Flags::EETypeKindMask == 0x0003, "The debugging data contract has a hard coded dependency on this value of MethodTable::Flags. If you change this value you must bump major_version_number."); + static_assert(MethodTable::Flags::RelatedTypeViaIATFlag == 0x0004, "The debugging data contract has a hard coded dependency on this value of MethodTable::Flags. If you change this value you must bump major_version_number."); + static_assert(MethodTable::Flags::HasFinalizerFlag == 0x0010, "The debugging data contract has a hard coded dependency on this value of MethodTable::Flags. If you change this value you must bump major_version_number."); + static_assert(MethodTable::Flags::HasPointersFlag == 0x0020, "The debugging data contract has a hard coded dependency on this value of MethodTable::Flags. If you change this value you must bump major_version_number."); + static_assert(MethodTable::Flags::GenericVarianceFlag == 0x0080, "The debugging data contract has a hard coded dependency on this value of MethodTable::Flags. If you change this value you must bump major_version_number."); + static_assert(MethodTable::Flags::IsGenericFlag == 0x0400, "The debugging data contract has a hard coded dependency on this value of MethodTable::Flags. If you change this value you must bump major_version_number."); + static_assert(MethodTable::Flags::ElementTypeMask == 0xf800, "The debugging data contract has a hard coded dependency on this value of MethodTable::Flags. If you change this value you must bump major_version_number."); + static_assert(MethodTable::Flags::ElementTypeShift == 11, "The debugging data contract has a hard coded dependency on this value of MethodTable::Flags. If you change this value you must bump major_version_number."); +} diff --git a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp new file mode 100644 index 00000000000000..b8108dfa379f96 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp @@ -0,0 +1,575 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#ifndef DACCESS_COMPILE +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "rhassert.h" +#include "slist.h" +#include "gcrhinterface.h" +#include "shash.h" +#include "RWLock.h" +#include "TypeManager.h" +#include "varint.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "holder.h" +#include "Crst.h" +#include "RuntimeInstance.h" +#include "event.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "stressLog.h" +#include "rhbinder.h" +#include "MethodTable.h" +#include "MethodTable.inl" + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpEHEnumInitFromStackFrameIterator, ( + StackFrameIterator* pFrameIter, void ** pMethodStartAddressOut, EHEnum* pEHEnum)) +{ + ICodeManager * pCodeManager = pFrameIter->GetCodeManager(); + pEHEnum->m_pCodeManager = pCodeManager; + + FC_RETURN_BOOL(pCodeManager->EHEnumInit(pFrameIter->GetMethodInfo(), pMethodStartAddressOut, &pEHEnum->m_state)); +} + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpEHEnumNext, (EHEnum* pEHEnum, EHClause* pEHClause)) +{ + FC_RETURN_BOOL(pEHEnum->m_pCodeManager->EHEnumNext(&pEHEnum->m_state, pEHClause)); +} + +// Unmanaged helper to locate one of two classlib-provided functions that the runtime needs to +// implement throwing of exceptions out of Rtm, and fail-fast. This may return NULL if the classlib +// found via the provided address does not have the necessary exports. +COOP_PINVOKE_HELPER(void *, RhpGetClasslibFunctionFromCodeAddress, (void * address, ClasslibFunctionId functionId)) +{ + return GetRuntimeInstance()->GetClasslibFunctionFromCodeAddress(address, functionId); +} + +// Unmanaged helper to locate one of two classlib-provided functions that the runtime needs to +// implement throwing of exceptions out of Rtm, and fail-fast. This may return NULL if the classlib +// found via the provided address does not have the necessary exports. +COOP_PINVOKE_HELPER(void *, RhpGetClasslibFunctionFromEEType, (MethodTable * pEEType, ClasslibFunctionId functionId)) +{ + return pEEType->GetTypeManagerPtr()->AsTypeManager()->GetClasslibFunction(functionId); +} + +COOP_PINVOKE_HELPER(void, RhpValidateExInfoStack, ()) +{ + Thread * pThisThread = ThreadStore::GetCurrentThread(); + pThisThread->ValidateExInfoStack(); +} + +COOP_PINVOKE_HELPER(void, RhpClearThreadDoNotTriggerGC, ()) +{ + Thread * pThisThread = ThreadStore::GetCurrentThread(); + + if (!pThisThread->IsDoNotTriggerGcSet()) + RhFailFast(); + + pThisThread->ClearDoNotTriggerGc(); +} + +COOP_PINVOKE_HELPER(void, RhpSetThreadDoNotTriggerGC, ()) +{ + Thread * pThisThread = ThreadStore::GetCurrentThread(); + + if (pThisThread->IsDoNotTriggerGcSet()) + RhFailFast(); + + pThisThread->SetDoNotTriggerGc(); +} + +COOP_PINVOKE_HELPER(int32_t, RhGetModuleFileName, (HANDLE moduleHandle, _Out_ const TCHAR** pModuleNameOut)) +{ + return PalGetModuleFileName(pModuleNameOut, moduleHandle); +} + +COOP_PINVOKE_HELPER(void, RhpCopyContextFromExInfo, (void * pOSContext, int32_t cbOSContext, PAL_LIMITED_CONTEXT * pPalContext)) +{ + UNREFERENCED_PARAMETER(cbOSContext); + ASSERT(cbOSContext >= sizeof(CONTEXT)); + CONTEXT* pContext = (CONTEXT *)pOSContext; +#if defined(UNIX_AMD64_ABI) + pContext->Rip = pPalContext->IP; + pContext->Rsp = pPalContext->Rsp; + pContext->Rbp = pPalContext->Rbp; + pContext->Rdx = pPalContext->Rdx; + pContext->Rax = pPalContext->Rax; + pContext->Rbx = pPalContext->Rbx; + pContext->R12 = pPalContext->R12; + pContext->R13 = pPalContext->R13; + pContext->R14 = pPalContext->R14; + pContext->R15 = pPalContext->R15; +#elif defined(HOST_AMD64) + pContext->Rip = pPalContext->IP; + pContext->Rsp = pPalContext->Rsp; + pContext->Rbp = pPalContext->Rbp; + pContext->Rdi = pPalContext->Rdi; + pContext->Rsi = pPalContext->Rsi; + pContext->Rax = pPalContext->Rax; + pContext->Rbx = pPalContext->Rbx; + pContext->R12 = pPalContext->R12; + pContext->R13 = pPalContext->R13; + pContext->R14 = pPalContext->R14; + pContext->R15 = pPalContext->R15; +#elif defined(HOST_X86) + pContext->Eip = pPalContext->IP; + pContext->Esp = pPalContext->Rsp; + pContext->Ebp = pPalContext->Rbp; + pContext->Edi = pPalContext->Rdi; + pContext->Esi = pPalContext->Rsi; + pContext->Eax = pPalContext->Rax; + pContext->Ebx = pPalContext->Rbx; +#elif defined(HOST_ARM) + pContext->R0 = pPalContext->R0; + pContext->R4 = pPalContext->R4; + pContext->R5 = pPalContext->R5; + pContext->R6 = pPalContext->R6; + pContext->R7 = pPalContext->R7; + pContext->R8 = pPalContext->R8; + pContext->R9 = pPalContext->R9; + pContext->R10 = pPalContext->R10; + pContext->R11 = pPalContext->R11; + pContext->Sp = pPalContext->SP; + pContext->Lr = pPalContext->LR; + pContext->Pc = pPalContext->IP; +#elif defined(HOST_ARM64) + pContext->X0 = pPalContext->X0; + pContext->X1 = pPalContext->X1; + // TODO: Copy registers X2-X7 when we start supporting HVA's + pContext->X19 = pPalContext->X19; + pContext->X20 = pPalContext->X20; + pContext->X21 = pPalContext->X21; + pContext->X22 = pPalContext->X22; + pContext->X23 = pPalContext->X23; + pContext->X24 = pPalContext->X24; + pContext->X25 = pPalContext->X25; + pContext->X26 = pPalContext->X26; + pContext->X27 = pPalContext->X27; + pContext->X28 = pPalContext->X28; + pContext->Fp = pPalContext->FP; + pContext->Sp = pPalContext->SP; + pContext->Lr = pPalContext->LR; + pContext->Pc = pPalContext->IP; +#elif defined(HOST_WASM) + // No registers, no work to do yet +#else +#error Not Implemented for this architecture -- RhpCopyContextFromExInfo +#endif +} + +#if defined(HOST_AMD64) || defined(HOST_ARM) || defined(HOST_X86) || defined(HOST_ARM64) +struct DISPATCHER_CONTEXT +{ + uintptr_t ControlPc; + // N.B. There is more here (so this struct isn't the right size), but we ignore everything else +}; + +#ifdef HOST_X86 +struct EXCEPTION_REGISTRATION_RECORD +{ + uintptr_t Next; + uintptr_t Handler; +}; +#endif // HOST_X86 + +EXTERN_C void __cdecl RhpFailFastForPInvokeExceptionPreemp(intptr_t PInvokeCallsiteReturnAddr, + void* pExceptionRecord, void* pContextRecord); +EXTERN_C void REDHAWK_CALLCONV RhpFailFastForPInvokeExceptionCoop(intptr_t PInvokeCallsiteReturnAddr, + void* pExceptionRecord, void* pContextRecord); +int32_t __stdcall RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs); + +EXTERN_C int32_t __stdcall RhpPInvokeExceptionGuard(PEXCEPTION_RECORD pExceptionRecord, + uintptr_t EstablisherFrame, + PCONTEXT pContextRecord, + DISPATCHER_CONTEXT * pDispatcherContext) +{ + UNREFERENCED_PARAMETER(EstablisherFrame); + + Thread * pThread = ThreadStore::GetCurrentThread(); + + // If the thread is currently in the "do not trigger GC" mode, we must not allocate, we must not reverse pinvoke, or + // return from a pinvoke. All of these things will deadlock with the GC and they all become increasingly likely as + // exception dispatch kicks off. So we just nip this in the bud as early as possible with a FailFast. The most + // likely case where this occurs is in our GC-callouts for Jupiter lifetime management -- in that case, we have + // managed code that calls to native code (without pinvoking) which might have a bug that causes an AV. + if (pThread->IsDoNotTriggerGcSet()) + RhFailFast(); + + // We promote exceptions that were not converted to managed exceptions to a FailFast. However, we have to + // be careful because we got here via OS SEH infrastructure and, therefore, don't know what GC mode we're + // currently in. As a result, since we're calling back into managed code to handle the FailFast, we must + // correctly call either a UnmanagedCallersOnly or a RuntimeExport version of the same method. + if (pThread->IsCurrentThreadInCooperativeMode()) + { + // Cooperative mode -- Typically, RhpVectoredExceptionHandler will handle this because the faulting IP will be + // in managed code. But sometimes we AV on a bad call indirect or something similar. In that situation, we can + // use the dispatcher context or exception registration record to find the relevant classlib. +#ifdef HOST_X86 + intptr_t classlibBreadcrumb = ((EXCEPTION_REGISTRATION_RECORD*)EstablisherFrame)->Handler; +#else + intptr_t classlibBreadcrumb = pDispatcherContext->ControlPc; +#endif + RhpFailFastForPInvokeExceptionCoop(classlibBreadcrumb, pExceptionRecord, pContextRecord); + } + else + { + // Preemptive mode -- the classlib associated with the last pinvoke owns the fail fast behavior. + intptr_t pinvokeCallsiteReturnAddr = (intptr_t)pThread->GetCurrentThreadPInvokeReturnAddress(); + RhpFailFastForPInvokeExceptionPreemp(pinvokeCallsiteReturnAddr, pExceptionRecord, pContextRecord); + } + + return 0; +} +#else +EXTERN_C int32_t RhpPInvokeExceptionGuard() +{ + ASSERT_UNCONDITIONALLY("RhpPInvokeExceptionGuard NYI for this architecture!"); + RhFailFast(); + return 0; +} +#endif + +#if defined(HOST_AMD64) || defined(HOST_ARM) || defined(HOST_X86) || defined(HOST_ARM64) || defined(HOST_WASM) +EXTERN_C REDHAWK_API void REDHAWK_CALLCONV RhpThrowHwEx(); +#else +COOP_PINVOKE_HELPER(void, RhpThrowHwEx, ()) +{ + ASSERT_UNCONDITIONALLY("RhpThrowHwEx NYI for this architecture!"); +} +COOP_PINVOKE_HELPER(void, RhpThrowEx, ()) +{ + ASSERT_UNCONDITIONALLY("RhpThrowEx NYI for this architecture!"); +} +COOP_PINVOKE_HELPER(void, RhpCallCatchFunclet, ()) +{ + ASSERT_UNCONDITIONALLY("RhpCallCatchFunclet NYI for this architecture!"); +} +COOP_PINVOKE_HELPER(void, RhpCallFinallyFunclet, ()) +{ + ASSERT_UNCONDITIONALLY("RhpCallFinallyFunclet NYI for this architecture!"); +} +COOP_PINVOKE_HELPER(void, RhpCallFilterFunclet, ()) +{ + ASSERT_UNCONDITIONALLY("RhpCallFilterFunclet NYI for this architecture!"); +} +COOP_PINVOKE_HELPER(void, RhpRethrow, ()) +{ + ASSERT_UNCONDITIONALLY("RhpRethrow NYI for this architecture!"); +} + +EXTERN_C void* RhpCallCatchFunclet2 = NULL; +EXTERN_C void* RhpCallFinallyFunclet2 = NULL; +EXTERN_C void* RhpCallFilterFunclet2 = NULL; +EXTERN_C void* RhpThrowEx2 = NULL; +EXTERN_C void* RhpThrowHwEx2 = NULL; +EXTERN_C void* RhpRethrow2 = NULL; +#endif + +EXTERN_C void * RhpAssignRefAVLocation; +EXTERN_C void * RhpCheckedAssignRefAVLocation; +EXTERN_C void * RhpCheckedLockCmpXchgAVLocation; +EXTERN_C void * RhpCheckedXchgAVLocation; +EXTERN_C void * RhpLockCmpXchg32AVLocation; +EXTERN_C void * RhpLockCmpXchg64AVLocation; +EXTERN_C void * RhpCopyMultibyteDestAVLocation; +EXTERN_C void * RhpCopyMultibyteSrcAVLocation; +EXTERN_C void * RhpCopyMultibyteNoGCRefsDestAVLocation; +EXTERN_C void * RhpCopyMultibyteNoGCRefsSrcAVLocation; +EXTERN_C void * RhpCopyMultibyteWithWriteBarrierDestAVLocation; +EXTERN_C void * RhpCopyMultibyteWithWriteBarrierSrcAVLocation; +EXTERN_C void * RhpCopyAnyWithWriteBarrierDestAVLocation; +EXTERN_C void * RhpCopyAnyWithWriteBarrierSrcAVLocation; + +static bool InWriteBarrierHelper(uintptr_t faultingIP) +{ +#ifndef USE_PORTABLE_HELPERS + static uintptr_t writeBarrierAVLocations[] = + { + (uintptr_t)&RhpAssignRefAVLocation, + (uintptr_t)&RhpCheckedAssignRefAVLocation, + (uintptr_t)&RhpCheckedLockCmpXchgAVLocation, + (uintptr_t)&RhpCheckedXchgAVLocation, + (uintptr_t)&RhpLockCmpXchg32AVLocation, + (uintptr_t)&RhpLockCmpXchg64AVLocation, + }; + + // compare the IP against the list of known possible AV locations in the write barrier helpers + for (size_t i = 0; i < sizeof(writeBarrierAVLocations)/sizeof(writeBarrierAVLocations[0]); i++) + { +#if defined(HOST_AMD64) || defined(HOST_X86) + // Verify that the runtime is not linked with incremental linking enabled. Incremental linking + // wraps every method symbol with a jump stub that breaks the following check. + ASSERT(*(uint8_t*)writeBarrierAVLocations[i] != 0xE9); // jmp XXXXXXXX +#endif + + if (writeBarrierAVLocations[i] == faultingIP) + return true; + } +#endif // USE_PORTABLE_HELPERS + + return false; +} + +EXTERN_C void* RhpInitialInterfaceDispatch; +EXTERN_C void* RhpInterfaceDispatchAVLocation1; +EXTERN_C void* RhpInterfaceDispatchAVLocation2; +EXTERN_C void* RhpInterfaceDispatchAVLocation4; +EXTERN_C void* RhpInterfaceDispatchAVLocation8; +EXTERN_C void* RhpInterfaceDispatchAVLocation16; +EXTERN_C void* RhpInterfaceDispatchAVLocation32; +EXTERN_C void* RhpInterfaceDispatchAVLocation64; + +static bool InInterfaceDispatchHelper(uintptr_t faultingIP) +{ +#ifndef USE_PORTABLE_HELPERS + static uintptr_t interfaceDispatchAVLocations[] = + { + (uintptr_t)&RhpInitialInterfaceDispatch, + (uintptr_t)&RhpInterfaceDispatchAVLocation1, + (uintptr_t)&RhpInterfaceDispatchAVLocation2, + (uintptr_t)&RhpInterfaceDispatchAVLocation4, + (uintptr_t)&RhpInterfaceDispatchAVLocation8, + (uintptr_t)&RhpInterfaceDispatchAVLocation16, + (uintptr_t)&RhpInterfaceDispatchAVLocation32, + (uintptr_t)&RhpInterfaceDispatchAVLocation64, + }; + + // compare the IP against the list of known possible AV locations in the interface dispatch helpers + for (size_t i = 0; i < sizeof(interfaceDispatchAVLocations) / sizeof(interfaceDispatchAVLocations[0]); i++) + { +#if defined(HOST_AMD64) || defined(HOST_X86) + // Verify that the runtime is not linked with incremental linking enabled. Incremental linking + // wraps every method symbol with a jump stub that breaks the following check. + ASSERT(*(uint8_t*)interfaceDispatchAVLocations[i] != 0xE9); // jmp XXXXXXXX +#endif + + if (interfaceDispatchAVLocations[i] == faultingIP) + return true; + } +#endif // USE_PORTABLE_HELPERS + + return false; +} + +static uintptr_t UnwindSimpleHelperToCaller( +#ifdef TARGET_UNIX + PAL_LIMITED_CONTEXT * pContext +#else + _CONTEXT * pContext +#endif + ) +{ +#if defined(_DEBUG) + uintptr_t faultingIP = pContext->GetIp(); + ASSERT(InWriteBarrierHelper(faultingIP) || InInterfaceDispatchHelper(faultingIP)); +#endif +#if defined(HOST_AMD64) || defined(HOST_X86) + // simulate a ret instruction + uintptr_t sp = pContext->GetSp(); + uintptr_t adjustedFaultingIP = *(uintptr_t *)sp; + pContext->SetSp(sp+sizeof(uintptr_t)); // pop the stack +#elif defined(HOST_ARM) || defined(HOST_ARM64) + uintptr_t adjustedFaultingIP = pContext->GetLr(); +#else + uintptr_t adjustedFaultingIP = 0; // initializing to make the compiler happy + PORTABILITY_ASSERT("UnwindSimpleHelperToCaller"); +#endif + return adjustedFaultingIP; +} + +#ifdef TARGET_UNIX + +int32_t __stdcall RhpHardwareExceptionHandler(uintptr_t faultCode, uintptr_t faultAddress, + PAL_LIMITED_CONTEXT* palContext, uintptr_t* arg0Reg, uintptr_t* arg1Reg) +{ + uintptr_t faultingIP = palContext->GetIp(); + + ICodeManager * pCodeManager = GetRuntimeInstance()->FindCodeManagerByAddress((PTR_VOID)faultingIP); + bool translateToManagedException = false; + if (pCodeManager != NULL) + { + // Make sure that the OS does not use our internal fault codes + ASSERT(faultCode != STATUS_REDHAWK_NULL_REFERENCE && faultCode != STATUS_REDHAWK_UNMANAGED_HELPER_NULL_REFERENCE); + + if (faultCode == STATUS_ACCESS_VIOLATION) + { + if (faultAddress < NULL_AREA_SIZE) + { + faultCode = STATUS_REDHAWK_NULL_REFERENCE; + } + } + else if (faultCode == STATUS_STACK_OVERFLOW) + { + // Do not use ASSERT_UNCONDITIONALLY here. It will crash because of it consumes too much stack. + + PalPrintFatalError("\nProcess is terminating due to StackOverflowException.\n"); + RhFailFast(); + } + + translateToManagedException = true; + } + else if (faultCode == STATUS_ACCESS_VIOLATION) + { + // If this was an AV and code manager is null, this was an AV in unmanaged code. + // Could still be an AV in one of our assembly helpers that we know how to handle. + bool inWriteBarrierHelper = InWriteBarrierHelper(faultingIP); + bool inInterfaceDispatchHelper = InInterfaceDispatchHelper(faultingIP); + + if (inWriteBarrierHelper || inInterfaceDispatchHelper) + { + if (faultAddress < NULL_AREA_SIZE) + { + faultCode = STATUS_REDHAWK_UNMANAGED_HELPER_NULL_REFERENCE; + } + + // we were AV-ing in a helper - unwind our way to our caller + faultingIP = UnwindSimpleHelperToCaller(palContext); + + translateToManagedException = true; + } + } + + if (translateToManagedException) + { + *arg0Reg = faultCode; + *arg1Reg = faultingIP; + palContext->SetIp((uintptr_t)&RhpThrowHwEx); + + return EXCEPTION_CONTINUE_EXECUTION; + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +#else // TARGET_UNIX + +static bool g_ContinueOnFatalErrors = false; + +// Set the runtime to continue search when encountering an unhandled runtime exception. Once done it is forever. +// Continuing the search allows any vectored exception handlers or SEH installed by the client to take effect. +// Any client that does so is expected to handle stack overflows. +EXTERN_C void RhpContinueOnFatalErrors() +{ + g_ContinueOnFatalErrors = true; +} + +int32_t __stdcall RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs) +{ + uintptr_t faultCode = pExPtrs->ExceptionRecord->ExceptionCode; + + // Do not interfere with debugger exceptions + if (faultCode == STATUS_BREAKPOINT || faultCode == STATUS_SINGLE_STEP) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + uintptr_t faultingIP = pExPtrs->ContextRecord->GetIp(); + + ICodeManager * pCodeManager = GetRuntimeInstance()->FindCodeManagerByAddress((PTR_VOID)faultingIP); + bool translateToManagedException = false; + if (pCodeManager != NULL) + { + // Make sure that the OS does not use our internal fault codes + ASSERT(faultCode != STATUS_REDHAWK_NULL_REFERENCE && faultCode != STATUS_REDHAWK_UNMANAGED_HELPER_NULL_REFERENCE); + + if (faultCode == STATUS_ACCESS_VIOLATION) + { + if (pExPtrs->ExceptionRecord->ExceptionInformation[1] < NULL_AREA_SIZE) + { + faultCode = STATUS_REDHAWK_NULL_REFERENCE; + } + } + else if (faultCode == STATUS_STACK_OVERFLOW) + { + if (g_ContinueOnFatalErrors) + { + // The client is responsible for the handling. + return EXCEPTION_CONTINUE_SEARCH; + } + + // Do not use ASSERT_UNCONDITIONALLY here. It will crash because of it consumes too much stack. + PalPrintFatalError("\nProcess is terminating due to StackOverflowException.\n"); + PalRaiseFailFastException(pExPtrs->ExceptionRecord, pExPtrs->ContextRecord, 0); + } + + translateToManagedException = true; + } + else if (faultCode == STATUS_ACCESS_VIOLATION) + { + // If this was an AV and code manager is null, this was an AV in unmanaged code. + // Could still be an AV in one of our assembly helpers that we know how to handle. + bool inWriteBarrierHelper = InWriteBarrierHelper(faultingIP); + bool inInterfaceDispatchHelper = InInterfaceDispatchHelper(faultingIP); + + if (inWriteBarrierHelper || inInterfaceDispatchHelper) + { + if (pExPtrs->ExceptionRecord->ExceptionInformation[1] < NULL_AREA_SIZE) + { + faultCode = STATUS_REDHAWK_UNMANAGED_HELPER_NULL_REFERENCE; + } + + // we were AV-ing in a helper - unwind our way to our caller + faultingIP = UnwindSimpleHelperToCaller(pExPtrs->ContextRecord); + + translateToManagedException = true; + } + } + + if (translateToManagedException) + { + pExPtrs->ContextRecord->SetIp((uintptr_t)&RhpThrowHwEx); + pExPtrs->ContextRecord->SetArg0Reg(faultCode); + pExPtrs->ContextRecord->SetArg1Reg(faultingIP); + + return EXCEPTION_CONTINUE_EXECUTION; + } + + // The client may have told us to continue to search for custom handlers, + // but in general we consider any form of hardware exception within the runtime itself a fatal error. + // Note this includes the managed code within the runtime. + if (!g_ContinueOnFatalErrors) + { + static uint8_t *s_pbRuntimeModuleLower = NULL; + static uint8_t *s_pbRuntimeModuleUpper = NULL; + + // If this is the first time through this path then calculate the upper and lower bounds of the + // runtime module. Note we could be racing to calculate this but it doesn't matter since the results + // should always agree. + if ((s_pbRuntimeModuleLower == NULL) || (s_pbRuntimeModuleUpper == NULL)) + { + // Get the module handle for this runtime. Do this by passing an address definitely within the + // module (the address of this function) to GetModuleHandleEx with the "from address" flag. + HANDLE hRuntimeModule = PalGetModuleHandleFromPointer(reinterpret_cast(RhpVectoredExceptionHandler)); + if (!hRuntimeModule) + { + ASSERT_UNCONDITIONALLY("Failed to locate our own module handle"); + RhFailFast(); + } + + PalGetModuleBounds(hRuntimeModule, &s_pbRuntimeModuleLower, &s_pbRuntimeModuleUpper); + } + + if (((uint8_t*)faultingIP >= s_pbRuntimeModuleLower) && ((uint8_t*)faultingIP < s_pbRuntimeModuleUpper)) + { + ASSERT_UNCONDITIONALLY("Hardware exception raised inside the runtime."); + PalRaiseFailFastException(pExPtrs->ExceptionRecord, pExPtrs->ContextRecord, 0); + } + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +#endif // TARGET_UNIX + +COOP_PINVOKE_HELPER(void, RhpFallbackFailFast, ()) +{ + RhFailFast(); +} + +#endif // !DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/EtwEvents.h b/src/coreclr/nativeaot/Runtime/EtwEvents.h new file mode 100644 index 00000000000000..2ec3334ef9472b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/EtwEvents.h @@ -0,0 +1,904 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// **** This file is auto-generated. Do not edit by hand. **** +// +// Instead ensure this file and EtwEvents.man are checked-out from source code control, locate the PUCLR ETW +// manifest file (it should be in puclr\ndp\clr\src\VM\ClrEtwAll.man), copy it into the rh\src\rtetw +// directory and run the following command from an rhenv window: +// perl EtwImportClrEvents.pl +// +// This script consults EtwEventFilter.txt to determine which events to extract from the CLR manifest. It then +// merges any additional Redhawk-specific events from EtwRedhawkEvents.xml. The result is an updated version +// of this header file plus EtwEvents.man, a new ETW manifest file describing the final Redhawk events which +// can be registered with the system via the following command: +// wevtutil im EtwEvents.man +// + +#ifndef __RH_ETW_DEFS_INCLUDED +#define __RH_ETW_DEFS_INCLUDED + +#if defined(FEATURE_ETW) && !defined(DACCESS_COMPILE) + +#ifndef RH_ETW_INLINE +#define RH_ETW_INLINE __declspec(noinline) __inline +#endif + +struct RH_ETW_CONTEXT +{ + TRACEHANDLE RegistrationHandle; + TRACEHANDLE Logger; + uint64_t MatchAnyKeyword; + uint64_t MatchAllKeyword; + EVENT_FILTER_DESCRIPTOR * FilterData; + uint32_t Flags; + uint32_t IsEnabled; + uint8_t Level; + uint8_t Reserve; +}; + +uint32_t EtwCallback(uint32_t IsEnabled, RH_ETW_CONTEXT * CallbackContext); + +__declspec(noinline) __inline void __stdcall +RhEtwControlCallback(GUID * /*SourceId*/, uint32_t IsEnabled, uint8_t Level, uint64_t MatchAnyKeyword, uint64_t MatchAllKeyword, EVENT_FILTER_DESCRIPTOR * FilterData, void * CallbackContext) +{ + RH_ETW_CONTEXT * Ctx = (RH_ETW_CONTEXT*)CallbackContext; + if (Ctx == NULL) + return; + Ctx->Level = Level; + Ctx->MatchAnyKeyword = MatchAnyKeyword; + Ctx->MatchAllKeyword = MatchAllKeyword; + Ctx->FilterData = FilterData; + Ctx->IsEnabled = IsEnabled; + EtwCallback(IsEnabled, (RH_ETW_CONTEXT*)CallbackContext); +} + +__declspec(noinline) __inline bool __stdcall + RhEventTracingEnabled(RH_ETW_CONTEXT * EnableInfo, + const EVENT_DESCRIPTOR * EventDescriptor) +{ + if (!EnableInfo) + return false; + if ((EventDescriptor->Level <= EnableInfo->Level) || (EnableInfo->Level == 0)) + { + if ((EventDescriptor->Keyword == (ULONGLONG)0) || + ((EventDescriptor->Keyword & EnableInfo->MatchAnyKeyword) && + ((EventDescriptor->Keyword & EnableInfo->MatchAllKeyword) == EnableInfo->MatchAllKeyword))) + return true; + } + return false; +} + +#define ETW_EVENT_ENABLED(Context, EventDescriptor) (Context.IsEnabled && RhEventTracingEnabled(&Context, &EventDescriptor)) + +extern "C" __declspec(selectany) const GUID MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER = {0x1095638c, 0x8748, 0x4c7a, {0xb3, 0x9e, 0xba, 0xea, 0x27, 0xb9, 0xc5, 0x89}}; + +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGC1stConEnd = {0xd, 0x0, 0x10, 0x4, 0x1b, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGC1stNonConEnd = {0xc, 0x0, 0x10, 0x4, 0x1a, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGC2ndConBegin = {0x10, 0x0, 0x10, 0x4, 0x1e, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGC2ndConEnd = {0x11, 0x0, 0x10, 0x4, 0x1f, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGC2ndNonConBegin = {0xe, 0x0, 0x10, 0x4, 0x1c, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGC2ndNonConEnd = {0xf, 0x0, 0x10, 0x4, 0x1d, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGCAllocWaitBegin = {0x17, 0x0, 0x10, 0x4, 0x25, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGCAllocWaitEnd = {0x18, 0x0, 0x10, 0x4, 0x26, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGCBegin = {0xb, 0x0, 0x10, 0x4, 0x19, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGCDrainMark = {0x14, 0x0, 0x10, 0x4, 0x22, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGCOverflow = {0x16, 0x0, 0x10, 0x4, 0x24, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGCPlanEnd = {0x12, 0x0, 0x10, 0x4, 0x20, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGCRevisit = {0x15, 0x0, 0x10, 0x4, 0x23, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BGCSweepEnd = {0x13, 0x0, 0x10, 0x4, 0x21, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCFullNotify_V1 = {0x19, 0x1, 0x10, 0x4, 0x13, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCGlobalHeapHistory_V1 = {0x5, 0x1, 0x10, 0x4, 0x12, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCJoin_V1 = {0x6, 0x1, 0x10, 0x5, 0x14, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCOptimized_V1 = {0x3, 0x1, 0x10, 0x5, 0x10, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCPerHeapHistory = {0x4, 0x2, 0x10, 0x4, 0x11, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCSettings = {0x2, 0x0, 0x10, 0x4, 0xe, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR PinPlugAtGCTime = {0xc7, 0x0, 0x10, 0x5, 0x2c, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR PrvDestroyGCHandle = {0xc3, 0x0, 0x10, 0x5, 0x2b, 0x1, 0x8000000000004000}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR PrvGCMarkCards_V1 = {0xa, 0x1, 0x10, 0x4, 0x18, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR PrvGCMarkFinalizeQueueRoots_V1 = {0x8, 0x1, 0x10, 0x4, 0x16, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR PrvGCMarkHandles_V1 = {0x9, 0x1, 0x10, 0x4, 0x17, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR PrvGCMarkStackRoots_V1 = {0x7, 0x1, 0x10, 0x4, 0x15, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR PrvSetGCHandle = {0xc2, 0x0, 0x10, 0x5, 0x2a, 0x1, 0x8000000000004000}; + +extern "C" __declspec(selectany) REGHANDLE Microsoft_Windows_Redhawk_GC_PrivateHandle; +extern "C" __declspec(selectany) RH_ETW_CONTEXT MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context; + +#define RH_ETW_REGISTER_Microsoft_Windows_Redhawk_GC_Private() do { PalEventRegister(&MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER, RhEtwControlCallback, &MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context, &Microsoft_Windows_Redhawk_GC_PrivateHandle); } while (false) +#define RH_ETW_UNREGISTER_Microsoft_Windows_Redhawk_GC_Private() do { PalEventUnregister(Microsoft_Windows_Redhawk_GC_PrivateHandle); } while (false) + +#define FireEtwBGC1stConEnd(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGC1stConEnd)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCNoUserData(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGC1stConEnd, ClrInstanceID) : 0 + +#define FireEtwBGC1stNonConEnd(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGC1stNonConEnd)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCNoUserData(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGC1stNonConEnd, ClrInstanceID) : 0 + +#define FireEtwBGC2ndConBegin(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGC2ndConBegin)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCNoUserData(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGC2ndConBegin, ClrInstanceID) : 0 + +#define FireEtwBGC2ndConEnd(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGC2ndConEnd)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCNoUserData(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGC2ndConEnd, ClrInstanceID) : 0 + +#define FireEtwBGC2ndNonConBegin(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGC2ndNonConBegin)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCNoUserData(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGC2ndNonConBegin, ClrInstanceID) : 0 + +#define FireEtwBGC2ndNonConEnd(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGC2ndNonConEnd)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCNoUserData(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGC2ndNonConEnd, ClrInstanceID) : 0 + +#define FireEtwBGCAllocWaitBegin(Reason, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCAllocWaitBegin)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_BGCAllocWait(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCAllocWaitBegin, Reason, ClrInstanceID) : 0 + +#define FireEtwBGCAllocWaitEnd(Reason, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCAllocWaitEnd)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_BGCAllocWait(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCAllocWaitEnd, Reason, ClrInstanceID) : 0 + +#define FireEtwBGCBegin(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCBegin)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCNoUserData(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCBegin, ClrInstanceID) : 0 + +#define FireEtwBGCDrainMark(Objects, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCDrainMark)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_BGCDrainMark(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCDrainMark, Objects, ClrInstanceID) : 0 + +#define FireEtwBGCOverflow(Min, Max, Objects, IsLarge, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCOverflow)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_BGCOverflow(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCOverflow, Min, Max, Objects, IsLarge, ClrInstanceID) : 0 + +#define FireEtwBGCPlanEnd(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCPlanEnd)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCNoUserData(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCPlanEnd, ClrInstanceID) : 0 + +#define FireEtwBGCRevisit(Pages, Objects, IsLarge, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCRevisit)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_BGCRevisit(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCRevisit, Pages, Objects, IsLarge, ClrInstanceID) : 0 + +#define FireEtwBGCSweepEnd(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCSweepEnd)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCNoUserData(Microsoft_Windows_Redhawk_GC_PrivateHandle, &BGCSweepEnd, ClrInstanceID) : 0 + +#define FireEtwGCFullNotify_V1(GenNumber, IsAlloc, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCFullNotify_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCFullNotify_V1(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCFullNotify_V1, GenNumber, IsAlloc, ClrInstanceID) : 0 + +#define FireEtwGCGlobalHeapHistory_V1(FinalYoungestDesired, NumHeaps, CondemnedGeneration, Gen0ReductionCount, Reason, GlobalMechanisms, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCGlobalHeapHistory_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCGlobalHeap_V1(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCGlobalHeapHistory_V1, FinalYoungestDesired, NumHeaps, CondemnedGeneration, Gen0ReductionCount, Reason, GlobalMechanisms, ClrInstanceID) : 0 + +#define FireEtwGCJoin_V1(Heap, JoinTime, JoinType, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCJoin_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCJoin_V1(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCJoin_V1, Heap, JoinTime, JoinType, ClrInstanceID) : 0 + +#define FireEtwGCOptimized_V1(DesiredAllocation, NewAllocation, GenerationNumber, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCOptimized_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCOptimized_V1(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCOptimized_V1, DesiredAllocation, NewAllocation, GenerationNumber, ClrInstanceID) : 0 + +#define FireEtwGCPerHeapHistory() (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCPerHeapHistory)) ? TemplateEventDescriptor(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCPerHeapHistory) : 0 + +#define FireEtwGCSettings(SegmentSize, LargeObjectSegmentSize, ServerGC) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCSettings)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCSettings(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCSettings, SegmentSize, LargeObjectSegmentSize, ServerGC) : 0 + +#define FireEtwPinPlugAtGCTime(PlugStart, PlugEnd, GapBeforeSize, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PinPlugAtGCTime)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_PinPlugAtGCTime(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PinPlugAtGCTime, PlugStart, PlugEnd, GapBeforeSize, ClrInstanceID) : 0 + +#define FireEtwPrvDestroyGCHandle(HandleID, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PrvDestroyGCHandle)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_PrvDestroyGCHandle(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PrvDestroyGCHandle, HandleID, ClrInstanceID) : 0 + +#define FireEtwPrvGCMarkCards_V1(HeapNum, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PrvGCMarkCards_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_PrvGCMark_V1(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PrvGCMarkCards_V1, HeapNum, ClrInstanceID) : 0 + +#define FireEtwPrvGCMarkFinalizeQueueRoots_V1(HeapNum, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PrvGCMarkFinalizeQueueRoots_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_PrvGCMark_V1(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PrvGCMarkFinalizeQueueRoots_V1, HeapNum, ClrInstanceID) : 0 + +#define FireEtwPrvGCMarkHandles_V1(HeapNum, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PrvGCMarkHandles_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_PrvGCMark_V1(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PrvGCMarkHandles_V1, HeapNum, ClrInstanceID) : 0 + +#define FireEtwPrvGCMarkStackRoots_V1(HeapNum, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PrvGCMarkStackRoots_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_PrvGCMark_V1(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PrvGCMarkStackRoots_V1, HeapNum, ClrInstanceID) : 0 + +#define FireEtwPrvSetGCHandle(HandleID, ObjectID, Kind, Generation, AppDomainID, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PrvSetGCHandle)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_PrvSetGCHandle(Microsoft_Windows_Redhawk_GC_PrivateHandle, &PrvSetGCHandle, HandleID, ObjectID, Kind, Generation, AppDomainID, ClrInstanceID) : 0 + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_BGCAllocWait(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Reason, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[2]; + EventDataDescCreate(&EventData[0], &Reason, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 2, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_BGCDrainMark(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint64_t Objects, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[2]; + EventDataDescCreate(&EventData[0], &Objects, sizeof(uint64_t)); + EventDataDescCreate(&EventData[1], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 2, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_BGCOverflow(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint64_t Min, uint64_t Max, uint64_t Objects, uint32_t IsLarge, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[5]; + EventDataDescCreate(&EventData[0], &Min, sizeof(uint64_t)); + EventDataDescCreate(&EventData[1], &Max, sizeof(uint64_t)); + EventDataDescCreate(&EventData[2], &Objects, sizeof(uint64_t)); + EventDataDescCreate(&EventData[3], &IsLarge, sizeof(uint32_t)); + EventDataDescCreate(&EventData[4], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 5, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_BGCRevisit(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint64_t Pages, uint64_t Objects, uint32_t IsLarge, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[4]; + EventDataDescCreate(&EventData[0], &Pages, sizeof(uint64_t)); + EventDataDescCreate(&EventData[1], &Objects, sizeof(uint64_t)); + EventDataDescCreate(&EventData[2], &IsLarge, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 4, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCFullNotify_V1(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t GenNumber, uint32_t IsAlloc, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[3]; + EventDataDescCreate(&EventData[0], &GenNumber, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &IsAlloc, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 3, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCGlobalHeap_V1(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint64_t FinalYoungestDesired, int32_t NumHeaps, uint32_t CondemnedGeneration, uint32_t Gen0ReductionCount, uint32_t Reason, uint32_t GlobalMechanisms, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[7]; + EventDataDescCreate(&EventData[0], &FinalYoungestDesired, sizeof(uint64_t)); + EventDataDescCreate(&EventData[1], &NumHeaps, sizeof(int32_t)); + EventDataDescCreate(&EventData[2], &CondemnedGeneration, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &Gen0ReductionCount, sizeof(uint32_t)); + EventDataDescCreate(&EventData[4], &Reason, sizeof(uint32_t)); + EventDataDescCreate(&EventData[5], &GlobalMechanisms, sizeof(uint32_t)); + EventDataDescCreate(&EventData[6], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 7, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCJoin_V1(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Heap, uint32_t JoinTime, uint32_t JoinType, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[4]; + EventDataDescCreate(&EventData[0], &Heap, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &JoinTime, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &JoinType, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 4, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCNoUserData(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[1]; + EventDataDescCreate(&EventData[0], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 1, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCOptimized_V1(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint64_t DesiredAllocation, uint64_t NewAllocation, uint32_t GenerationNumber, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[4]; + EventDataDescCreate(&EventData[0], &DesiredAllocation, sizeof(uint64_t)); + EventDataDescCreate(&EventData[1], &NewAllocation, sizeof(uint64_t)); + EventDataDescCreate(&EventData[2], &GenerationNumber, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 4, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_GCSettings(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint64_t SegmentSize, uint64_t LargeObjectSegmentSize, UInt32_BOOL ServerGC) +{ + EVENT_DATA_DESCRIPTOR EventData[3]; + EventDataDescCreate(&EventData[0], &SegmentSize, sizeof(uint64_t)); + EventDataDescCreate(&EventData[1], &LargeObjectSegmentSize, sizeof(uint64_t)); + EventDataDescCreate(&EventData[2], &ServerGC, sizeof(UInt32_BOOL)); + return PalEventWrite(RegHandle, Descriptor, 3, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_PinPlugAtGCTime(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, void* PlugStart, void* PlugEnd, void* GapBeforeSize, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[4]; + EventDataDescCreate(&EventData[0], &PlugStart, sizeof(void*)); + EventDataDescCreate(&EventData[1], &PlugEnd, sizeof(void*)); + EventDataDescCreate(&EventData[2], &GapBeforeSize, sizeof(void*)); + EventDataDescCreate(&EventData[3], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 4, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_PrvDestroyGCHandle(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, void* HandleID, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[2]; + EventDataDescCreate(&EventData[0], &HandleID, sizeof(void*)); + EventDataDescCreate(&EventData[1], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 2, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_PrvGCMark_V1(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t HeapNum, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[2]; + EventDataDescCreate(&EventData[0], &HeapNum, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 2, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_PrvSetGCHandle(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, void* HandleID, void* ObjectID, uint32_t Kind, uint32_t Generation, uint64_t AppDomainID, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[6]; + EventDataDescCreate(&EventData[0], &HandleID, sizeof(void*)); + EventDataDescCreate(&EventData[1], &ObjectID, sizeof(void*)); + EventDataDescCreate(&EventData[2], &Kind, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &Generation, sizeof(uint32_t)); + EventDataDescCreate(&EventData[4], &AppDomainID, sizeof(uint64_t)); + EventDataDescCreate(&EventData[5], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 6, EventData); +} + +extern "C" __declspec(selectany) const GUID MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER = {0x47c3ba0c, 0x77f1, 0x4eb0, {0x8d, 0x4d, 0xae, 0xf4, 0x47, 0xf1, 0x6a, 0x85}}; + +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR BulkType = {0xf, 0x0, 0x10, 0x4, 0xa, 0x15, 0x8000000000080000}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR DestroyGCHandle = {0x1f, 0x0, 0x10, 0x4, 0x22, 0x1, 0x8000000000000002}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR ExceptionThrown_V1 = {0x50, 0x1, 0x10, 0x2, 0x1, 0x7, 0x8000000200008000}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCAllocationTick_V1 = {0xa, 0x1, 0x10, 0x5, 0xb, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCAllocationTick_V2 = {0xa, 0x2, 0x10, 0x5, 0xb, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCAllocationTick_V3 = {0xa, 0x3, 0x10, 0x5, 0xb, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCBulkEdge = {0x13, 0x0, 0x10, 0x4, 0x17, 0x1, 0x8000000000100000}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCBulkMovedObjectRanges = {0x16, 0x0, 0x10, 0x4, 0x1a, 0x1, 0x8000000000400000}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCBulkNode = {0x12, 0x0, 0x10, 0x4, 0x16, 0x1, 0x8000000000100000}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCBulkRCW = {0x25, 0x0, 0x10, 0x4, 0x27, 0x1, 0x8000000000100000}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCBulkRootCCW = {0x24, 0x0, 0x10, 0x4, 0x26, 0x1, 0x8000000000100000}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCBulkRootConditionalWeakTableElementEdge = {0x11, 0x0, 0x10, 0x4, 0x15, 0x1, 0x8000000000100000}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCBulkRootEdge = {0x10, 0x0, 0x10, 0x4, 0x14, 0x1, 0x8000000000100000}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCBulkSurvivingObjectRanges = {0x15, 0x0, 0x10, 0x4, 0x19, 0x1, 0x8000000000400000}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCCreateConcurrentThread_V1 = {0xb, 0x1, 0x10, 0x4, 0xc, 0x1, 0x8000000000010001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCCreateSegment_V1 = {0x5, 0x1, 0x10, 0x4, 0x86, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCEnd_V1 = {0x2, 0x1, 0x10, 0x4, 0x2, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCFreeSegment_V1 = {0x6, 0x1, 0x10, 0x4, 0x87, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCGenerationRange = {0x17, 0x0, 0x10, 0x4, 0x1b, 0x1, 0x8000000000400000}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCGlobalHeapHistory_V2 = {0xcd, 0x2, 0x10, 0x4, 0xcd, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCHeapStats_V1 = {0x4, 0x1, 0x10, 0x4, 0x85, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCJoin_V2 = {0xcb, 0x2, 0x10, 0x5, 0xcb, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCMarkFinalizeQueueRoots = {0x1a, 0x0, 0x10, 0x4, 0x1d, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCMarkHandles = {0x1b, 0x0, 0x10, 0x4, 0x1e, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCMarkOlderGenerationRoots = {0x1c, 0x0, 0x10, 0x4, 0x1f, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCMarkStackRoots = {0x19, 0x0, 0x10, 0x4, 0x1c, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCMarkWithType = {0xca, 0x0, 0x10, 0x4, 0xca, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCPerHeapHistory_V3 = {0xcc, 0x3, 0x10, 0x4, 0xcc, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCRestartEEBegin_V1 = {0x7, 0x1, 0x10, 0x4, 0x88, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCRestartEEEnd_V1 = {0x3, 0x1, 0x10, 0x4, 0x84, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCStart_V1 = {0x1, 0x1, 0x10, 0x4, 0x1, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCStart_V2 = {0x1, 0x2, 0x10, 0x4, 0x1, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCSuspendEEBegin_V1 = {0x9, 0x1, 0x10, 0x4, 0xa, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCSuspendEEEnd_V1 = {0x8, 0x1, 0x10, 0x4, 0x89, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCTerminateConcurrentThread_V1 = {0xc, 0x1, 0x10, 0x4, 0xd, 0x1, 0x8000000000010001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR GCTriggered = {0x23, 0x0, 0x10, 0x4, 0x23, 0x1, 0x8000000000000001}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR ModuleLoad_V2 = {0x98, 0x2, 0x10, 0x4, 0x21, 0xa, 0x8000000020000008}; +extern "C" __declspec(selectany) const EVENT_DESCRIPTOR SetGCHandle = {0x1e, 0x0, 0x10, 0x4, 0x21, 0x1, 0x8000000000000002}; + +extern "C" __declspec(selectany) REGHANDLE Microsoft_Windows_Redhawk_GC_PublicHandle; +extern "C" __declspec(selectany) RH_ETW_CONTEXT MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context; + +#define RH_ETW_REGISTER_Microsoft_Windows_Redhawk_GC_Public() do { PalEventRegister(&MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER, RhEtwControlCallback, &MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context, &Microsoft_Windows_Redhawk_GC_PublicHandle); } while (false) +#define RH_ETW_UNREGISTER_Microsoft_Windows_Redhawk_GC_Public() do { PalEventUnregister(Microsoft_Windows_Redhawk_GC_PublicHandle); } while (false) + +#define FireEtwBulkType(Count, ClrInstanceID, Values_Len_, Values) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &BulkType)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_BulkType(Microsoft_Windows_Redhawk_GC_PublicHandle, &BulkType, Count, ClrInstanceID, Values_Len_, Values) : 0 + +#define FireEtwDestroyGCHandle(HandleID, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &DestroyGCHandle)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_DestroyGCHandle(Microsoft_Windows_Redhawk_GC_PublicHandle, &DestroyGCHandle, HandleID, ClrInstanceID) : 0 + +#define FireEtwExceptionThrown_V1(ExceptionType, ExceptionMessage, ExceptionEIP, ExceptionHRESULT, ExceptionFlags, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &ExceptionThrown_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Exception(Microsoft_Windows_Redhawk_GC_PublicHandle, &ExceptionThrown_V1, ExceptionType, ExceptionMessage, ExceptionEIP, ExceptionHRESULT, ExceptionFlags, ClrInstanceID) : 0 + +#define FireEtwGCAllocationTick_V1(AllocationAmount, AllocationKind, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCAllocationTick_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCAllocationTick_V1(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCAllocationTick_V1, AllocationAmount, AllocationKind, ClrInstanceID) : 0 + +#define FireEtwGCAllocationTick_V2(AllocationAmount, AllocationKind, ClrInstanceID, AllocationAmount64, TypeID, TypeName, HeapIndex) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCAllocationTick_V2)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCAllocationTick_V2(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCAllocationTick_V2, AllocationAmount, AllocationKind, ClrInstanceID, AllocationAmount64, TypeID, TypeName, HeapIndex) : 0 + +#define FireEtwGCAllocationTick_V3(AllocationAmount, AllocationKind, ClrInstanceID, AllocationAmount64, TypeID, TypeName, HeapIndex, Address) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCAllocationTick_V3)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCAllocationTick_V3(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCAllocationTick_V3, AllocationAmount, AllocationKind, ClrInstanceID, AllocationAmount64, TypeID, TypeName, HeapIndex, Address) : 0 + +#define FireEtwGCBulkEdge(Index, Count, ClrInstanceID, Values_Len_, Values) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkEdge)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkEdge(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkEdge, Index, Count, ClrInstanceID, Values_Len_, Values) : 0 + +#define FireEtwGCBulkMovedObjectRanges(Index, Count, ClrInstanceID, Values_Len_, Values) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkMovedObjectRanges)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkMovedObjectRanges(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkMovedObjectRanges, Index, Count, ClrInstanceID, Values_Len_, Values) : 0 + +#define FireEtwGCBulkNode(Index, Count, ClrInstanceID, Values_Len_, Values) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkNode)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkNode(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkNode, Index, Count, ClrInstanceID, Values_Len_, Values) : 0 + +#define FireEtwGCBulkRCW(Count, ClrInstanceID, Values_Len_, Values) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkRCW)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkRCW(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkRCW, Count, ClrInstanceID, Values_Len_, Values) : 0 + +#define FireEtwGCBulkRootCCW(Count, ClrInstanceID, Values_Len_, Values) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkRootCCW)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkRootCCW(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkRootCCW, Count, ClrInstanceID, Values_Len_, Values) : 0 + +#define FireEtwGCBulkRootConditionalWeakTableElementEdge(Index, Count, ClrInstanceID, Values_Len_, Values) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkRootConditionalWeakTableElementEdge)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkRootConditionalWeakTableElementEdge(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkRootConditionalWeakTableElementEdge, Index, Count, ClrInstanceID, Values_Len_, Values) : 0 + +#define FireEtwGCBulkRootEdge(Index, Count, ClrInstanceID, Values_Len_, Values) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkRootEdge)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkRootEdge(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkRootEdge, Index, Count, ClrInstanceID, Values_Len_, Values) : 0 + +#define FireEtwGCBulkSurvivingObjectRanges(Index, Count, ClrInstanceID, Values_Len_, Values) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkSurvivingObjectRanges)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkSurvivingObjectRanges(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCBulkSurvivingObjectRanges, Index, Count, ClrInstanceID, Values_Len_, Values) : 0 + +#define FireEtwGCCreateConcurrentThread_V1(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCCreateConcurrentThread_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCCreateConcurrentThread(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCCreateConcurrentThread_V1, ClrInstanceID) : 0 + +#define FireEtwGCCreateSegment_V1(Address, Size, Type, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCCreateSegment_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCCreateSegment_V1(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCCreateSegment_V1, Address, Size, Type, ClrInstanceID) : 0 + +#define FireEtwGCEnd_V1(Count, Depth, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCEnd_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCEnd_V1(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCEnd_V1, Count, Depth, ClrInstanceID) : 0 + +#define FireEtwGCFreeSegment_V1(Address, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCFreeSegment_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCFreeSegment_V1(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCFreeSegment_V1, Address, ClrInstanceID) : 0 + +#define FireEtwGCGenerationRange(Generation, RangeStart, RangeUsedLength, RangeReservedLength, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCGenerationRange)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCGenerationRange(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCGenerationRange, Generation, RangeStart, RangeUsedLength, RangeReservedLength, ClrInstanceID) : 0 + +#define FireEtwGCGlobalHeapHistory_V2(FinalYoungestDesired, NumHeaps, CondemnedGeneration, Gen0ReductionCount, Reason, GlobalMechanisms, ClrInstanceID, PauseMode, MemoryPressure) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCGlobalHeapHistory_V2)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCGlobalHeap_V2(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCGlobalHeapHistory_V2, FinalYoungestDesired, NumHeaps, CondemnedGeneration, Gen0ReductionCount, Reason, GlobalMechanisms, ClrInstanceID, PauseMode, MemoryPressure) : 0 + +#define FireEtwGCHeapStats_V1(GenerationSize0, TotalPromotedSize0, GenerationSize1, TotalPromotedSize1, GenerationSize2, TotalPromotedSize2, GenerationSize3, TotalPromotedSize3, FinalizationPromotedSize, FinalizationPromotedCount, PinnedObjectCount, SinkBlockCount, GCHandleCount, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCHeapStats_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCHeapStats_V1(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCHeapStats_V1, GenerationSize0, TotalPromotedSize0, GenerationSize1, TotalPromotedSize1, GenerationSize2, TotalPromotedSize2, GenerationSize3, TotalPromotedSize3, FinalizationPromotedSize, FinalizationPromotedCount, PinnedObjectCount, SinkBlockCount, GCHandleCount, ClrInstanceID) : 0 + +#define FireEtwGCJoin_V2(Heap, JoinTime, JoinType, ClrInstanceID, JoinID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCJoin_V2)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCJoin_V2(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCJoin_V2, Heap, JoinTime, JoinType, ClrInstanceID, JoinID) : 0 + +#define FireEtwGCMarkFinalizeQueueRoots(HeapNum, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCMarkFinalizeQueueRoots)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCMark(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCMarkFinalizeQueueRoots, HeapNum, ClrInstanceID) : 0 + +#define FireEtwGCMarkHandles(HeapNum, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCMarkHandles)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCMark(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCMarkHandles, HeapNum, ClrInstanceID) : 0 + +#define FireEtwGCMarkOlderGenerationRoots(HeapNum, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCMarkOlderGenerationRoots)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCMark(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCMarkOlderGenerationRoots, HeapNum, ClrInstanceID) : 0 + +#define FireEtwGCMarkStackRoots(HeapNum, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCMarkStackRoots)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCMark(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCMarkStackRoots, HeapNum, ClrInstanceID) : 0 + +#define FireEtwGCMarkWithType(HeapNum, ClrInstanceID, Type, Bytes) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCMarkWithType)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCMarkWithType(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCMarkWithType, HeapNum, ClrInstanceID, Type, Bytes) : 0 + +#define FireEtwGCPerHeapHistory_V3(ClrInstanceID, FreeListAllocated, FreeListRejected, EndOfSegAllocated, CondemnedAllocated, PinnedAllocated, PinnedAllocatedAdvance, RunningFreeListEfficiency, CondemnReasons0, CondemnReasons1, CompactMechanisms, ExpandMechanisms, HeapIndex, ExtraGen0Commit, Count, Values_Len_, Values) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCPerHeapHistory_V3)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCPerHeapHistory_V3(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCPerHeapHistory_V3, ClrInstanceID, FreeListAllocated, FreeListRejected, EndOfSegAllocated, CondemnedAllocated, PinnedAllocated, PinnedAllocatedAdvance, RunningFreeListEfficiency, CondemnReasons0, CondemnReasons1, CompactMechanisms, ExpandMechanisms, HeapIndex, ExtraGen0Commit, Count, Values_Len_, Values) : 0 + +#define FireEtwGCRestartEEBegin_V1(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCRestartEEBegin_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCNoUserData(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCRestartEEBegin_V1, ClrInstanceID) : 0 + +#define FireEtwGCRestartEEEnd_V1(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCRestartEEEnd_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCNoUserData(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCRestartEEEnd_V1, ClrInstanceID) : 0 + +#define FireEtwGCStart_V1(Count, Depth, Reason, Type, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCStart_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCStart_V1(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCStart_V1, Count, Depth, Reason, Type, ClrInstanceID) : 0 + +#define FireEtwGCStart_V2(Count, Depth, Reason, Type, ClrInstanceID, ClientSequenceNumber) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCStart_V2)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCStart_V2(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCStart_V2, Count, Depth, Reason, Type, ClrInstanceID, ClientSequenceNumber) : 0 + +#define FireEtwGCSuspendEEBegin_V1(Reason, Count, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCSuspendEEBegin_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCSuspendEE_V1(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCSuspendEEBegin_V1, Reason, Count, ClrInstanceID) : 0 + +#define FireEtwGCSuspendEEEnd_V1(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCSuspendEEEnd_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCNoUserData(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCSuspendEEEnd_V1, ClrInstanceID) : 0 + +#define FireEtwGCTerminateConcurrentThread_V1(ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCTerminateConcurrentThread_V1)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCTerminateConcurrentThread(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCTerminateConcurrentThread_V1, ClrInstanceID) : 0 + +#define FireEtwGCTriggered(Reason, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCTriggered)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCTriggered(Microsoft_Windows_Redhawk_GC_PublicHandle, &GCTriggered, Reason, ClrInstanceID) : 0 + +#define FireEtwModuleLoad_V2(ModuleID, AssemblyID, ModuleFlags, Reserved1, ModuleILPath, ModuleNativePath, ClrInstanceID, ManagedPdbSignature, ManagedPdbAge, ManagedPdbBuildPath, NativePdbSignature, NativePdbAge, NativePdbBuildPath) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &ModuleLoad_V2)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_ModuleLoadUnload_V2(Microsoft_Windows_Redhawk_GC_PublicHandle, &ModuleLoad_V2, ModuleID, AssemblyID, ModuleFlags, Reserved1, ModuleILPath, ModuleNativePath, ClrInstanceID, ManagedPdbSignature, ManagedPdbAge, ManagedPdbBuildPath, NativePdbSignature, NativePdbAge, NativePdbBuildPath) : 0 + +#define FireEtwSetGCHandle(HandleID, ObjectID, Kind, Generation, AppDomainID, ClrInstanceID) (MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PublicHandle, &SetGCHandle)) ? Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_SetGCHandle(Microsoft_Windows_Redhawk_GC_PublicHandle, &SetGCHandle, HandleID, ObjectID, Kind, Generation, AppDomainID, ClrInstanceID) : 0 + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_BulkType(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Count, uint16_t ClrInstanceID, ULONG Values_Len_, const PVOID Values) +{ + EVENT_DATA_DESCRIPTOR EventData[11]; + EventDataDescCreate(&EventData[0], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[2], Values, Count * Values_Len_); + return PalEventWrite(RegHandle, Descriptor, 3, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_DestroyGCHandle(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, void* HandleID, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[2]; + EventDataDescCreate(&EventData[0], &HandleID, sizeof(void*)); + EventDataDescCreate(&EventData[1], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 2, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Exception(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, LPCWSTR ExceptionType, LPCWSTR ExceptionMessage, void* ExceptionEIP, uint32_t ExceptionHRESULT, uint16_t ExceptionFlags, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[6]; + EventDataDescCreate(&EventData[0], (ExceptionType != NULL) ? ExceptionType : L"", (ExceptionType != NULL) ? (ULONG)((wcslen(ExceptionType) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[1], (ExceptionMessage != NULL) ? ExceptionMessage : L"", (ExceptionMessage != NULL) ? (ULONG)((wcslen(ExceptionMessage) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], &ExceptionEIP, sizeof(void*)); + EventDataDescCreate(&EventData[3], &ExceptionHRESULT, sizeof(uint32_t)); + EventDataDescCreate(&EventData[4], &ExceptionFlags, sizeof(uint16_t)); + EventDataDescCreate(&EventData[5], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 6, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCAllocationTick_V1(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t AllocationAmount, uint32_t AllocationKind, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[3]; + EventDataDescCreate(&EventData[0], &AllocationAmount, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &AllocationKind, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 3, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCAllocationTick_V2(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t AllocationAmount, uint32_t AllocationKind, uint16_t ClrInstanceID, uint64_t AllocationAmount64, void* TypeID, LPCWSTR TypeName, uint32_t HeapIndex) +{ + EVENT_DATA_DESCRIPTOR EventData[7]; + EventDataDescCreate(&EventData[0], &AllocationAmount, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &AllocationKind, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[3], &AllocationAmount64, sizeof(uint64_t)); + EventDataDescCreate(&EventData[4], &TypeID, sizeof(void*)); + EventDataDescCreate(&EventData[5], (TypeName != NULL) ? TypeName : L"", (TypeName != NULL) ? (ULONG)((wcslen(TypeName) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[6], &HeapIndex, sizeof(uint32_t)); + return PalEventWrite(RegHandle, Descriptor, 7, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCAllocationTick_V3(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t AllocationAmount, uint32_t AllocationKind, uint16_t ClrInstanceID, uint64_t AllocationAmount64, void* TypeID, LPCWSTR TypeName, uint32_t HeapIndex, void* Address) +{ + EVENT_DATA_DESCRIPTOR EventData[8]; + EventDataDescCreate(&EventData[0], &AllocationAmount, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &AllocationKind, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[3], &AllocationAmount64, sizeof(uint64_t)); + EventDataDescCreate(&EventData[4], &TypeID, sizeof(void*)); + EventDataDescCreate(&EventData[5], (TypeName != NULL) ? TypeName : L"", (TypeName != NULL) ? (ULONG)((wcslen(TypeName) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[6], &HeapIndex, sizeof(uint32_t)); + EventDataDescCreate(&EventData[7], &Address, sizeof(void*)); + return PalEventWrite(RegHandle, Descriptor, 8, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkEdge(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Index, uint32_t Count, uint16_t ClrInstanceID, ULONG Values_Len_, const PVOID Values) +{ + EVENT_DATA_DESCRIPTOR EventData[6]; + EventDataDescCreate(&EventData[0], &Index, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[3], Values, Count * Values_Len_); + return PalEventWrite(RegHandle, Descriptor, 4, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkMovedObjectRanges(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Index, uint32_t Count, uint16_t ClrInstanceID, ULONG Values_Len_, const PVOID Values) +{ + EVENT_DATA_DESCRIPTOR EventData[7]; + EventDataDescCreate(&EventData[0], &Index, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[3], Values, Count * Values_Len_); + return PalEventWrite(RegHandle, Descriptor, 4, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkNode(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Index, uint32_t Count, uint16_t ClrInstanceID, ULONG Values_Len_, const PVOID Values) +{ + EVENT_DATA_DESCRIPTOR EventData[8]; + EventDataDescCreate(&EventData[0], &Index, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[3], Values, Count * Values_Len_); + return PalEventWrite(RegHandle, Descriptor, 4, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkRCW(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Count, uint16_t ClrInstanceID, ULONG Values_Len_, const PVOID Values) +{ + EVENT_DATA_DESCRIPTOR EventData[9]; + EventDataDescCreate(&EventData[0], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[2], Values, Count * Values_Len_); + return PalEventWrite(RegHandle, Descriptor, 3, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkRootCCW(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Count, uint16_t ClrInstanceID, ULONG Values_Len_, const PVOID Values) +{ + EVENT_DATA_DESCRIPTOR EventData[10]; + EventDataDescCreate(&EventData[0], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[2], Values, Count * Values_Len_); + return PalEventWrite(RegHandle, Descriptor, 3, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkRootConditionalWeakTableElementEdge(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Index, uint32_t Count, uint16_t ClrInstanceID, ULONG Values_Len_, const PVOID Values) +{ + EVENT_DATA_DESCRIPTOR EventData[7]; + EventDataDescCreate(&EventData[0], &Index, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[3], Values, Count * Values_Len_); + return PalEventWrite(RegHandle, Descriptor, 4, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkRootEdge(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Index, uint32_t Count, uint16_t ClrInstanceID, ULONG Values_Len_, const PVOID Values) +{ + EVENT_DATA_DESCRIPTOR EventData[8]; + EventDataDescCreate(&EventData[0], &Index, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[3], Values, Count * Values_Len_); + return PalEventWrite(RegHandle, Descriptor, 4, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCBulkSurvivingObjectRanges(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Index, uint32_t Count, uint16_t ClrInstanceID, ULONG Values_Len_, const PVOID Values) +{ + EVENT_DATA_DESCRIPTOR EventData[6]; + EventDataDescCreate(&EventData[0], &Index, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[3], Values, Count * Values_Len_); + return PalEventWrite(RegHandle, Descriptor, 4, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCCreateConcurrentThread(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[1]; + EventDataDescCreate(&EventData[0], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 1, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCCreateSegment_V1(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint64_t Address, uint64_t Size, uint32_t Type, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[4]; + EventDataDescCreate(&EventData[0], &Address, sizeof(uint64_t)); + EventDataDescCreate(&EventData[1], &Size, sizeof(uint64_t)); + EventDataDescCreate(&EventData[2], &Type, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 4, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCEnd_V1(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Count, uint32_t Depth, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[3]; + EventDataDescCreate(&EventData[0], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &Depth, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 3, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCFreeSegment_V1(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint64_t Address, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[2]; + EventDataDescCreate(&EventData[0], &Address, sizeof(uint64_t)); + EventDataDescCreate(&EventData[1], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 2, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCGenerationRange(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint8_t Generation, void* RangeStart, uint64_t RangeUsedLength, uint64_t RangeReservedLength, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[5]; + EventDataDescCreate(&EventData[0], &Generation, sizeof(uint8_t)); + EventDataDescCreate(&EventData[1], &RangeStart, sizeof(void*)); + EventDataDescCreate(&EventData[2], &RangeUsedLength, sizeof(uint64_t)); + EventDataDescCreate(&EventData[3], &RangeReservedLength, sizeof(uint64_t)); + EventDataDescCreate(&EventData[4], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 5, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCGlobalHeap_V2(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint64_t FinalYoungestDesired, int32_t NumHeaps, uint32_t CondemnedGeneration, uint32_t Gen0ReductionCount, uint32_t Reason, uint32_t GlobalMechanisms, uint16_t ClrInstanceID, uint32_t PauseMode, uint32_t MemoryPressure) +{ + EVENT_DATA_DESCRIPTOR EventData[9]; + EventDataDescCreate(&EventData[0], &FinalYoungestDesired, sizeof(uint64_t)); + EventDataDescCreate(&EventData[1], &NumHeaps, sizeof(int32_t)); + EventDataDescCreate(&EventData[2], &CondemnedGeneration, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &Gen0ReductionCount, sizeof(uint32_t)); + EventDataDescCreate(&EventData[4], &Reason, sizeof(uint32_t)); + EventDataDescCreate(&EventData[5], &GlobalMechanisms, sizeof(uint32_t)); + EventDataDescCreate(&EventData[6], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[7], &PauseMode, sizeof(uint32_t)); + EventDataDescCreate(&EventData[8], &MemoryPressure, sizeof(uint32_t)); + return PalEventWrite(RegHandle, Descriptor, 9, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCHeapStats_V1(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint64_t GenerationSize0, uint64_t TotalPromotedSize0, uint64_t GenerationSize1, uint64_t TotalPromotedSize1, uint64_t GenerationSize2, uint64_t TotalPromotedSize2, uint64_t GenerationSize3, uint64_t TotalPromotedSize3, uint64_t FinalizationPromotedSize, uint64_t FinalizationPromotedCount, uint32_t PinnedObjectCount, uint32_t SinkBlockCount, uint32_t GCHandleCount, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[14]; + EventDataDescCreate(&EventData[0], &GenerationSize0, sizeof(uint64_t)); + EventDataDescCreate(&EventData[1], &TotalPromotedSize0, sizeof(uint64_t)); + EventDataDescCreate(&EventData[2], &GenerationSize1, sizeof(uint64_t)); + EventDataDescCreate(&EventData[3], &TotalPromotedSize1, sizeof(uint64_t)); + EventDataDescCreate(&EventData[4], &GenerationSize2, sizeof(uint64_t)); + EventDataDescCreate(&EventData[5], &TotalPromotedSize2, sizeof(uint64_t)); + EventDataDescCreate(&EventData[6], &GenerationSize3, sizeof(uint64_t)); + EventDataDescCreate(&EventData[7], &TotalPromotedSize3, sizeof(uint64_t)); + EventDataDescCreate(&EventData[8], &FinalizationPromotedSize, sizeof(uint64_t)); + EventDataDescCreate(&EventData[9], &FinalizationPromotedCount, sizeof(uint64_t)); + EventDataDescCreate(&EventData[10], &PinnedObjectCount, sizeof(uint32_t)); + EventDataDescCreate(&EventData[11], &SinkBlockCount, sizeof(uint32_t)); + EventDataDescCreate(&EventData[12], &GCHandleCount, sizeof(uint32_t)); + EventDataDescCreate(&EventData[13], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 14, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCJoin_V2(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Heap, uint32_t JoinTime, uint32_t JoinType, uint16_t ClrInstanceID, uint32_t JoinID) +{ + EVENT_DATA_DESCRIPTOR EventData[5]; + EventDataDescCreate(&EventData[0], &Heap, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &JoinTime, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &JoinType, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[4], &JoinID, sizeof(uint32_t)); + return PalEventWrite(RegHandle, Descriptor, 5, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCMark(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t HeapNum, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[2]; + EventDataDescCreate(&EventData[0], &HeapNum, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 2, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCMarkWithType(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t HeapNum, uint16_t ClrInstanceID, uint32_t Type, uint64_t Bytes) +{ + EVENT_DATA_DESCRIPTOR EventData[4]; + EventDataDescCreate(&EventData[0], &HeapNum, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[2], &Type, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &Bytes, sizeof(uint64_t)); + return PalEventWrite(RegHandle, Descriptor, 4, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCNoUserData(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[1]; + EventDataDescCreate(&EventData[0], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 1, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCPerHeapHistory_V3(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint16_t ClrInstanceID, void* FreeListAllocated, void* FreeListRejected, void* EndOfSegAllocated, void* CondemnedAllocated, void* PinnedAllocated, void* PinnedAllocatedAdvance, uint32_t RunningFreeListEfficiency, uint32_t CondemnReasons0, uint32_t CondemnReasons1, uint32_t CompactMechanisms, uint32_t ExpandMechanisms, uint32_t HeapIndex, void* ExtraGen0Commit, uint32_t Count, ULONG Values_Len_, const PVOID Values) +{ + EVENT_DATA_DESCRIPTOR EventData[26]; + EventDataDescCreate(&EventData[0], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[1], &FreeListAllocated, sizeof(void*)); + EventDataDescCreate(&EventData[2], &FreeListRejected, sizeof(void*)); + EventDataDescCreate(&EventData[3], &EndOfSegAllocated, sizeof(void*)); + EventDataDescCreate(&EventData[4], &CondemnedAllocated, sizeof(void*)); + EventDataDescCreate(&EventData[5], &PinnedAllocated, sizeof(void*)); + EventDataDescCreate(&EventData[6], &PinnedAllocatedAdvance, sizeof(void*)); + EventDataDescCreate(&EventData[7], &RunningFreeListEfficiency, sizeof(uint32_t)); + EventDataDescCreate(&EventData[8], &CondemnReasons0, sizeof(uint32_t)); + EventDataDescCreate(&EventData[9], &CondemnReasons1, sizeof(uint32_t)); + EventDataDescCreate(&EventData[10], &CompactMechanisms, sizeof(uint32_t)); + EventDataDescCreate(&EventData[11], &ExpandMechanisms, sizeof(uint32_t)); + EventDataDescCreate(&EventData[12], &HeapIndex, sizeof(uint32_t)); + EventDataDescCreate(&EventData[13], &ExtraGen0Commit, sizeof(void*)); + EventDataDescCreate(&EventData[14], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[15], Values, Count * Values_Len_); + return PalEventWrite(RegHandle, Descriptor, 16, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCStart_V1(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Count, uint32_t Depth, uint32_t Reason, uint32_t Type, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[5]; + EventDataDescCreate(&EventData[0], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &Depth, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &Reason, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &Type, sizeof(uint32_t)); + EventDataDescCreate(&EventData[4], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 5, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCStart_V2(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Count, uint32_t Depth, uint32_t Reason, uint32_t Type, uint16_t ClrInstanceID, uint64_t ClientSequenceNumber) +{ + EVENT_DATA_DESCRIPTOR EventData[6]; + EventDataDescCreate(&EventData[0], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &Depth, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &Reason, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &Type, sizeof(uint32_t)); + EventDataDescCreate(&EventData[4], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[5], &ClientSequenceNumber, sizeof(uint64_t)); + return PalEventWrite(RegHandle, Descriptor, 6, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCSuspendEE_V1(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Reason, uint32_t Count, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[3]; + EventDataDescCreate(&EventData[0], &Reason, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &Count, sizeof(uint32_t)); + EventDataDescCreate(&EventData[2], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 3, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCTerminateConcurrentThread(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[1]; + EventDataDescCreate(&EventData[0], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 1, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_GCTriggered(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint32_t Reason, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[2]; + EventDataDescCreate(&EventData[0], &Reason, sizeof(uint32_t)); + EventDataDescCreate(&EventData[1], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 2, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_ModuleLoadUnload_V2(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, uint64_t ModuleID, uint64_t AssemblyID, uint32_t ModuleFlags, uint32_t Reserved1, LPCWSTR ModuleILPath, LPCWSTR ModuleNativePath, uint16_t ClrInstanceID, const GUID* ManagedPdbSignature, uint32_t ManagedPdbAge, LPCWSTR ManagedPdbBuildPath, const GUID* NativePdbSignature, uint32_t NativePdbAge, LPCWSTR NativePdbBuildPath) +{ + EVENT_DATA_DESCRIPTOR EventData[13]; + EventDataDescCreate(&EventData[0], &ModuleID, sizeof(uint64_t)); + EventDataDescCreate(&EventData[1], &AssemblyID, sizeof(uint64_t)); + EventDataDescCreate(&EventData[2], &ModuleFlags, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &Reserved1, sizeof(uint32_t)); + EventDataDescCreate(&EventData[4], (ModuleILPath != NULL) ? ModuleILPath : L"", (ModuleILPath != NULL) ? (ULONG)((wcslen(ModuleILPath) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[5], (ModuleNativePath != NULL) ? ModuleNativePath : L"", (ModuleNativePath != NULL) ? (ULONG)((wcslen(ModuleNativePath) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[6], &ClrInstanceID, sizeof(uint16_t)); + EventDataDescCreate(&EventData[7], ManagedPdbSignature, sizeof(*(ManagedPdbSignature))); + EventDataDescCreate(&EventData[8], &ManagedPdbAge, sizeof(uint32_t)); + EventDataDescCreate(&EventData[9], (ManagedPdbBuildPath != NULL) ? ManagedPdbBuildPath : L"", (ManagedPdbBuildPath != NULL) ? (ULONG)((wcslen(ManagedPdbBuildPath) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[10], NativePdbSignature, sizeof(*(NativePdbSignature))); + EventDataDescCreate(&EventData[11], &NativePdbAge, sizeof(uint32_t)); + EventDataDescCreate(&EventData[12], (NativePdbBuildPath != NULL) ? NativePdbBuildPath : L"", (NativePdbBuildPath != NULL) ? (ULONG)((wcslen(NativePdbBuildPath) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + return PalEventWrite(RegHandle, Descriptor, 13, EventData); +} + +RH_ETW_INLINE uint32_t +Template_MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_SetGCHandle(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor, void* HandleID, void* ObjectID, uint32_t Kind, uint32_t Generation, uint64_t AppDomainID, uint16_t ClrInstanceID) +{ + EVENT_DATA_DESCRIPTOR EventData[6]; + EventDataDescCreate(&EventData[0], &HandleID, sizeof(void*)); + EventDataDescCreate(&EventData[1], &ObjectID, sizeof(void*)); + EventDataDescCreate(&EventData[2], &Kind, sizeof(uint32_t)); + EventDataDescCreate(&EventData[3], &Generation, sizeof(uint32_t)); + EventDataDescCreate(&EventData[4], &AppDomainID, sizeof(uint64_t)); + EventDataDescCreate(&EventData[5], &ClrInstanceID, sizeof(uint16_t)); + return PalEventWrite(RegHandle, Descriptor, 6, EventData); +} + +RH_ETW_INLINE uint32_t +TemplateEventDescriptor(REGHANDLE RegHandle, const EVENT_DESCRIPTOR * Descriptor) +{ + return PalEventWrite(RegHandle, Descriptor, 0, NULL); +} + +#else // FEATURE_ETW + +#define ETW_EVENT_ENABLED(Context, EventDescriptor) false + +#define FireEtwBGC1stConEnd(ClrInstanceID) +#define FireEtwBGC1stNonConEnd(ClrInstanceID) +#define FireEtwBGC2ndConBegin(ClrInstanceID) +#define FireEtwBGC2ndConEnd(ClrInstanceID) +#define FireEtwBGC2ndNonConBegin(ClrInstanceID) +#define FireEtwBGC2ndNonConEnd(ClrInstanceID) +#define FireEtwBGCAllocWaitBegin(Reason, ClrInstanceID) +#define FireEtwBGCAllocWaitEnd(Reason, ClrInstanceID) +#define FireEtwBGCBegin(ClrInstanceID) +#define FireEtwBGCDrainMark(Objects, ClrInstanceID) +#define FireEtwBGCOverflow(Min, Max, Objects, IsLarge, ClrInstanceID) +#define FireEtwBGCPlanEnd(ClrInstanceID) +#define FireEtwBGCRevisit(Pages, Objects, IsLarge, ClrInstanceID) +#define FireEtwBGCSweepEnd(ClrInstanceID) +#define FireEtwGCFullNotify_V1(GenNumber, IsAlloc, ClrInstanceID) +#define FireEtwGCGlobalHeapHistory_V1(FinalYoungestDesired, NumHeaps, CondemnedGeneration, Gen0ReductionCount, Reason, GlobalMechanisms, ClrInstanceID) +#define FireEtwGCJoin_V1(Heap, JoinTime, JoinType, ClrInstanceID) +#define FireEtwGCOptimized_V1(DesiredAllocation, NewAllocation, GenerationNumber, ClrInstanceID) +#define FireEtwGCPerHeapHistory() +#define FireEtwGCSettings(SegmentSize, LargeObjectSegmentSize, ServerGC) +#define FireEtwPinPlugAtGCTime(PlugStart, PlugEnd, GapBeforeSize, ClrInstanceID) +#define FireEtwPrvDestroyGCHandle(HandleID, ClrInstanceID) +#define FireEtwPrvGCMarkCards_V1(HeapNum, ClrInstanceID) +#define FireEtwPrvGCMarkFinalizeQueueRoots_V1(HeapNum, ClrInstanceID) +#define FireEtwPrvGCMarkHandles_V1(HeapNum, ClrInstanceID) +#define FireEtwPrvGCMarkStackRoots_V1(HeapNum, ClrInstanceID) +#define FireEtwPrvSetGCHandle(HandleID, ObjectID, Kind, Generation, AppDomainID, ClrInstanceID) + +#define FireEtwBulkType(Count, ClrInstanceID, Values_Len_, Values) +#define FireEtwDestroyGCHandle(HandleID, ClrInstanceID) +#define FireEtwExceptionThrown_V1(ExceptionType, ExceptionMessage, ExceptionEIP, ExceptionHRESULT, ExceptionFlags, ClrInstanceID) +#define FireEtwGCAllocationTick_V1(AllocationAmount, AllocationKind, ClrInstanceID) +#define FireEtwGCAllocationTick_V2(AllocationAmount, AllocationKind, ClrInstanceID, AllocationAmount64, TypeID, TypeName, HeapIndex) +#define FireEtwGCAllocationTick_V3(AllocationAmount, AllocationKind, ClrInstanceID, AllocationAmount64, TypeID, TypeName, HeapIndex, Address) +#define FireEtwGCBulkEdge(Index, Count, ClrInstanceID, Values_Len_, Values) +#define FireEtwGCBulkMovedObjectRanges(Index, Count, ClrInstanceID, Values_Len_, Values) +#define FireEtwGCBulkNode(Index, Count, ClrInstanceID, Values_Len_, Values) +#define FireEtwGCBulkRCW(Count, ClrInstanceID, Values_Len_, Values) +#define FireEtwGCBulkRootCCW(Count, ClrInstanceID, Values_Len_, Values) +#define FireEtwGCBulkRootConditionalWeakTableElementEdge(Index, Count, ClrInstanceID, Values_Len_, Values) +#define FireEtwGCBulkRootEdge(Index, Count, ClrInstanceID, Values_Len_, Values) +#define FireEtwGCBulkSurvivingObjectRanges(Index, Count, ClrInstanceID, Values_Len_, Values) +#define FireEtwGCCreateConcurrentThread_V1(ClrInstanceID) +#define FireEtwGCCreateSegment_V1(Address, Size, Type, ClrInstanceID) +#define FireEtwGCEnd_V1(Count, Depth, ClrInstanceID) +#define FireEtwGCFreeSegment_V1(Address, ClrInstanceID) +#define FireEtwGCGenerationRange(Generation, RangeStart, RangeUsedLength, RangeReservedLength, ClrInstanceID) +#define FireEtwGCGlobalHeapHistory_V2(FinalYoungestDesired, NumHeaps, CondemnedGeneration, Gen0ReductionCount, Reason, GlobalMechanisms, ClrInstanceID, PauseMode, MemoryPressure) +#define FireEtwGCHeapStats_V1(GenerationSize0, TotalPromotedSize0, GenerationSize1, TotalPromotedSize1, GenerationSize2, TotalPromotedSize2, GenerationSize3, TotalPromotedSize3, FinalizationPromotedSize, FinalizationPromotedCount, PinnedObjectCount, SinkBlockCount, GCHandleCount, ClrInstanceID) +#define FireEtwGCJoin_V2(Heap, JoinTime, JoinType, ClrInstanceID, JoinID) +#define FireEtwGCMarkFinalizeQueueRoots(HeapNum, ClrInstanceID) +#define FireEtwGCMarkHandles(HeapNum, ClrInstanceID) +#define FireEtwGCMarkOlderGenerationRoots(HeapNum, ClrInstanceID) +#define FireEtwGCMarkStackRoots(HeapNum, ClrInstanceID) +#define FireEtwGCMarkWithType(HeapNum, ClrInstanceID, Type, Bytes) +#define FireEtwGCPerHeapHistory_V3(ClrInstanceID, FreeListAllocated, FreeListRejected, EndOfSegAllocated, CondemnedAllocated, PinnedAllocated, PinnedAllocatedAdvance, RunningFreeListEfficiency, CondemnReasons0, CondemnReasons1, CompactMechanisms, ExpandMechanisms, HeapIndex, ExtraGen0Commit, Count, Values_Len_, Values) +#define FireEtwGCRestartEEBegin_V1(ClrInstanceID) +#define FireEtwGCRestartEEEnd_V1(ClrInstanceID) +#define FireEtwGCStart_V1(Count, Depth, Reason, Type, ClrInstanceID) +#define FireEtwGCStart_V2(Count, Depth, Reason, Type, ClrInstanceID, ClientSequenceNumber) +#define FireEtwGCSuspendEEBegin_V1(Reason, Count, ClrInstanceID) +#define FireEtwGCSuspendEEEnd_V1(ClrInstanceID) +#define FireEtwGCTerminateConcurrentThread_V1(ClrInstanceID) +#define FireEtwGCTriggered(Reason, ClrInstanceID) +#define FireEtwModuleLoad_V2(ModuleID, AssemblyID, ModuleFlags, Reserved1, ModuleILPath, ModuleNativePath, ClrInstanceID, ManagedPdbSignature, ManagedPdbAge, ManagedPdbBuildPath, NativePdbSignature, NativePdbAge, NativePdbBuildPath) +#define FireEtwSetGCHandle(HandleID, ObjectID, Kind, Generation, AppDomainID, ClrInstanceID) + +#endif // FEATURE_ETW + +#endif // !__RH_ETW_DEFS_INCLUDED diff --git a/src/coreclr/nativeaot/Runtime/FinalizerHelpers.cpp b/src/coreclr/nativeaot/Runtime/FinalizerHelpers.cpp new file mode 100644 index 00000000000000..0f2aff5182b7fb --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/FinalizerHelpers.cpp @@ -0,0 +1,211 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Unmanaged helpers called by the managed finalizer thread. +// +#include "common.h" +#include "gcenv.h" +#include "gcheaputilities.h" + +#include "slist.h" +#include "gcrhinterface.h" +#include "RWLock.h" +#include "RuntimeInstance.h" +#include "shash.h" + +#include "regdisplay.h" +#include "StackFrameIterator.h" + +#include "thread.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "thread.inl" + +#include "yieldprocessornormalized.h" + +GPTR_DECL(Thread, g_pFinalizerThread); + +CLREventStatic g_FinalizerEvent; +CLREventStatic g_FinalizerDoneEvent; + +// Finalizer method implemented by redhawkm. +extern "C" void __cdecl ProcessFinalizers(); + +// Unmanaged front-end to the finalizer thread. We require this because at the point the GC creates the +// finalizer thread we're still executing the DllMain for RedhawkU. At that point we can't run managed code +// successfully (in particular module initialization code has not run for RedhawkM). Instead this method waits +// for the first finalization request (by which time everything must be up and running) and kicks off the +// managed portion of the thread at that point. +uint32_t WINAPI FinalizerStart(void* pContext) +{ + HANDLE hFinalizerEvent = (HANDLE)pContext; + + ThreadStore::AttachCurrentThread(); + Thread * pThread = ThreadStore::GetCurrentThread(); + + // Disallow gcstress on this thread to work around the current implementation's limitation that it will + // get into an infinite loop if performed on the finalizer thread. + pThread->SetSuppressGcStress(); + + g_pFinalizerThread = PTR_Thread(pThread); + + // We have some time until the first finalization request - use the time to calibrate normalized waits. + EnsureYieldProcessorNormalizedInitialized(); + + // Wait for a finalization request. + uint32_t uResult = PalWaitForSingleObjectEx(hFinalizerEvent, INFINITE, FALSE); + ASSERT(uResult == WAIT_OBJECT_0); + + // Since we just consumed the request (and the event is auto-reset) we must set the event again so the + // managed finalizer code will immediately start processing the queue when we run it. + UInt32_BOOL fResult = PalSetEvent(hFinalizerEvent); + ASSERT(fResult); + + // Run the managed portion of the finalizer. Until we implement (non-process) shutdown this call will + // never return. + + ProcessFinalizers(); + + ASSERT(!"Finalizer thread should never return"); + return 0; +} + +bool RhInitializeFinalization() +{ + // Allocate the events the GC expects the finalizer thread to have. The g_FinalizerEvent event is signalled + // by the GC whenever it completes a collection where it found otherwise unreachable finalizable objects. + // The g_FinalizerDoneEvent is set by the finalizer thread every time it wakes up and drains the + // queue of finalizable objects. It's mainly used by GC.WaitForPendingFinalizers(). + if (!g_FinalizerEvent.CreateAutoEventNoThrow(false)) + return false; + if (!g_FinalizerDoneEvent.CreateManualEventNoThrow(false)) + return false; + + // Create the finalizer thread itself. + if (!PalStartFinalizerThread(FinalizerStart, (void*)g_FinalizerEvent.GetOSEvent())) + return false; + + return true; +} + +void RhEnableFinalization() +{ + g_FinalizerEvent.Set(); +} + +EXTERN_C REDHAWK_API void __cdecl RhInitializeFinalizerThread() +{ + g_FinalizerEvent.Set(); +} + +EXTERN_C REDHAWK_API void __cdecl RhWaitForPendingFinalizers(UInt32_BOOL allowReentrantWait) +{ + // This must be called via p/invoke rather than RuntimeImport since it blocks and could starve the GC if + // called in cooperative mode. + ASSERT(!ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode()); + + // Can't call this from the finalizer thread itself. + if (ThreadStore::GetCurrentThread() != g_pFinalizerThread) + { + // Clear any current indication that a finalization pass is finished and wake the finalizer thread up + // (if there's no work to do it'll set the done event immediately). + g_FinalizerDoneEvent.Reset(); + g_FinalizerEvent.Set(); + + // Wait for the finalizer thread to get back to us. + g_FinalizerDoneEvent.Wait(INFINITE, false, allowReentrantWait); + } +} + +// Block the current thread until at least one object needs to be finalized (returns true) or memory is low +// (returns false and the finalizer thread should initiate a garbage collection). +EXTERN_C REDHAWK_API UInt32_BOOL __cdecl RhpWaitForFinalizerRequest() +{ + // We can wait for two events; finalization queue has been populated and low memory resource notification. + // But if the latter is signalled we shouldn't wait on it again immediately -- if the garbage collection + // the finalizer thread initiates as a result is not sufficient to remove the low memory condition the + // event will still be signalled and we'll end up looping doing cpu intensive collections, which won't + // help the situation at all and could make it worse. So we remember whether the last event we reported + // was low memory and if so we'll wait at least two seconds (the CLR value) on just a finalization + // request. + static bool fLastEventWasLowMemory = false; + + IGCHeap * pHeap = GCHeapUtilities::GetGCHeap(); + + // Wait in a loop because we may have to retry if we decide to only wait for finalization events but the + // two second timeout expires. + do + { + HANDLE lowMemEvent = NULL; +#if 0 // TODO: hook up low memory notification + lowMemEvent = pHeap->GetLowMemoryNotificationEvent(); + HANDLE rgWaitHandles[] = { g_FinalizerEvent.GetOSEvent(), lowMemEvent }; + uint32_t cWaitHandles = (fLastEventWasLowMemory || (lowMemEvent == NULL)) ? 1 : 2; + uint32_t uTimeout = fLastEventWasLowMemory ? 2000 : INFINITE; + + uint32_t uResult = PalWaitForMultipleObjectsEx(cWaitHandles, rgWaitHandles, FALSE, uTimeout, FALSE); +#else + uint32_t uResult = PalWaitForSingleObjectEx(g_FinalizerEvent.GetOSEvent(), INFINITE, FALSE); +#endif + + switch (uResult) + { + case WAIT_OBJECT_0: + // At least one object is ready for finalization. + return TRUE; + + case WAIT_OBJECT_0 + 1: + // Memory is low, tell the finalizer thread to garbage collect. + ASSERT(!fLastEventWasLowMemory); + fLastEventWasLowMemory = true; + return FALSE; + + case WAIT_TIMEOUT: + // We were waiting only for finalization events but didn't get one within the timeout period. Go + // back to waiting for any event. + ASSERT(fLastEventWasLowMemory); + fLastEventWasLowMemory = false; + break; + + default: + ASSERT(!"Unexpected PalWaitForMultipleObjectsEx() result"); + return FALSE; + } + } while (true); +} + +// Indicate that the current round of finalizations is complete. +EXTERN_C REDHAWK_API void __cdecl RhpSignalFinalizationComplete() +{ + g_FinalizerDoneEvent.Set(); +} + +// +// The following helpers are special in that they interact with internal GC state or directly manipulate +// managed references so they're called with a special co-operative p/invoke. +// + +// Fetch next object which needs finalization or return null if we've reached the end of the list. +COOP_PINVOKE_HELPER(OBJECTREF, RhpGetNextFinalizableObject, ()) +{ + while (true) + { + // Get the next finalizable object. If we get back NULL we've reached the end of the list. + OBJECTREF refNext = GCHeapUtilities::GetGCHeap()->GetNextFinalizable(); + if (refNext == NULL) + return NULL; + + // The queue may contain objects which have been marked as finalized already (via GC.SuppressFinalize() + // for instance). Skip finalization for these but reset the flag so that the object can be put back on + // the list with RegisterForFinalization(). + if (refNext->GetHeader()->GetBits() & BIT_SBLK_FINALIZER_RUN) + { + refNext->GetHeader()->ClrBit(BIT_SBLK_FINALIZER_RUN); + continue; + } + + // We've found the first finalizable object, return it to the caller. + return refNext; + } +} diff --git a/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt new file mode 100644 index 00000000000000..22af5f44eba20c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt @@ -0,0 +1,83 @@ +project(Runtime) + +# Full version of the runtime is required by the JIT CodeGen. +# The low-level helpers can be implemented in assembly code. + +# Include auto-generated files on include path +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +add_definitions(-DFEATURE_RX_THUNKS) + +if (CLR_CMAKE_TARGET_WIN32) + if (CLR_CMAKE_HOST_ARCH_ARM OR CLR_CMAKE_HOST_ARCH_ARM64) + # Needed to include AsmOffsets.inc + include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}) + preprocess_files(RUNTIME_SOURCES_ARCH_ASM ${RUNTIME_SOURCES_ARCH_ASM}) + if (CMAKE_GENERATOR MATCHES "Visual Studio") + # Replaces .asm files in RUNTIME_SOURCES_ARCH_ASM with the corresponding .obj files + compile_asm(TARGET Runtime.WorkstationGC ASM_FILES ${RUNTIME_SOURCES_ARCH_ASM} OUTPUT_OBJECTS RUNTIME_ARCH_ASM_OBJECTS) + endif() + endif() +endif (CLR_CMAKE_TARGET_WIN32) + +add_library(Runtime.WorkstationGC STATIC ${COMMON_RUNTIME_SOURCES} ${FULL_RUNTIME_SOURCES} ${RUNTIME_SOURCES_ARCH_ASM} ${RUNTIME_ARCH_ASM_OBJECTS}) + +add_library(Runtime.ServerGC STATIC ${COMMON_RUNTIME_SOURCES} ${FULL_RUNTIME_SOURCES} ${RUNTIME_SOURCES_ARCH_ASM} ${SERVER_GC_SOURCES} ${RUNTIME_ARCH_ASM_OBJECTS}) + +target_compile_definitions(Runtime.ServerGC PRIVATE -DFEATURE_SVR_GC) + +if (CLR_CMAKE_TARGET_WIN32) + add_library(Runtime.ServerGC.GuardCF STATIC ${COMMON_RUNTIME_SOURCES} ${FULL_RUNTIME_SOURCES} ${RUNTIME_SOURCES_ARCH_ASM} ${SERVER_GC_SOURCES} ${RUNTIME_ARCH_ASM_OBJECTS}) + target_compile_definitions(Runtime.ServerGC.GuardCF PRIVATE -DFEATURE_SVR_GC) + target_compile_options(Runtime.ServerGC.GuardCF PRIVATE $<$,$>:/guard:cf>) +endif (CLR_CMAKE_TARGET_WIN32) + +# Get the current list of definitions +get_compile_definitions(DEFINITIONS) + +set(ASM_OFFSETS_CSPP ${RUNTIME_DIR}/../Runtime.Base/src/AsmOffsets.cspp) + +if(WIN32) + set(COMPILER_LANGUAGE "") + set(PREPROCESSOR_FLAGS -EP) + set(ASM_OFFSETS_CPP ${RUNTIME_DIR}/windows/AsmOffsets.cpp) +else() + set(COMPILER_LANGUAGE -x c++) + set(PREPROCESSOR_FLAGS -E -P) + set(ASM_OFFSETS_CPP ${RUNTIME_DIR}/unix/AsmOffsets.cpp) +endif() + +add_custom_command( + # The AsmOffsets.cs is consumed later by the managed build + TARGET Runtime.WorkstationGC + COMMAND ${CMAKE_CXX_COMPILER} ${COMPILER_LANGUAGE} ${DEFINITIONS} ${PREPROCESSOR_FLAGS} -I"${ARCH_SOURCES_DIR}" "${ASM_OFFSETS_CSPP}" >"${CMAKE_CURRENT_BINARY_DIR}/AsmOffsets.cs" + DEPENDS "${RUNTIME_DIR}/AsmOffsets.cpp" "${RUNTIME_DIR}/AsmOffsets.h" +) + +add_custom_command( + COMMAND ${CMAKE_CXX_COMPILER} ${DEFINITIONS} ${PREPROCESSOR_FLAGS} -I"${RUNTIME_DIR}" -I"${ARCH_SOURCES_DIR}" "${ASM_OFFSETS_CPP}" >"${CMAKE_CURRENT_BINARY_DIR}/AsmOffsets.inc" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/AsmOffsets.inc" + DEPENDS "${ASM_OFFSETS_CPP}" "${RUNTIME_DIR}/AsmOffsets.h" + COMMENT "Generating AsmOffsets.inc" +) + +set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/AsmOffsets.inc" PROPERTIES GENERATED TRUE) + +# Runtime.WorkstationGC and Runtime.ServerGC share AsmOffsets.inc and assembler helpers (for Windows ARM/ARM64). +# Avoid a race condition by adding this target as a dependency for both libraries. +add_custom_target( + RuntimeAsmHelpers + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/AsmOffsets.inc" "${RUNTIME_ARCH_ASM_OBJECTS}" +) + +add_dependencies(Runtime.WorkstationGC RuntimeAsmHelpers) +add_dependencies(Runtime.ServerGC RuntimeAsmHelpers) +if (CLR_CMAKE_TARGET_WIN32) + add_dependencies(Runtime.ServerGC.GuardCF RuntimeAsmHelpers) +endif (CLR_CMAKE_TARGET_WIN32) + +install_static_library(Runtime.WorkstationGC aotsdk nativeaot) +install_static_library(Runtime.ServerGC aotsdk nativeaot) +if (CLR_CMAKE_TARGET_WIN32) + install_static_library(Runtime.ServerGC.GuardCF aotsdk nativeaot) +endif (CLR_CMAKE_TARGET_WIN32) diff --git a/src/coreclr/nativeaot/Runtime/GCHelpers.cpp b/src/coreclr/nativeaot/Runtime/GCHelpers.cpp new file mode 100644 index 00000000000000..9457e46d6f7269 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/GCHelpers.cpp @@ -0,0 +1,341 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Unmanaged helpers exposed by the System.GC managed class. +// + +#include "common.h" +#include "gcenv.h" +#include "gcenv.ee.h" +#include "gcheaputilities.h" +#include "RestrictedCallouts.h" + +#include "gcrhinterface.h" + +#include "PalRedhawkCommon.h" +#include "slist.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" + +#include "thread.h" +#include "RWLock.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "thread.inl" + +EXTERN_C REDHAWK_API void __cdecl RhpCollect(uint32_t uGeneration, uint32_t uMode) +{ + // This must be called via p/invoke rather than RuntimeImport to make the stack crawlable. + + Thread * pCurThread = ThreadStore::GetCurrentThread(); + + pCurThread->SetupHackPInvokeTunnel(); + pCurThread->DisablePreemptiveMode(); + + ASSERT(!pCurThread->IsDoNotTriggerGcSet()); + GCHeapUtilities::GetGCHeap()->GarbageCollect(uGeneration, FALSE, uMode); + + pCurThread->EnablePreemptiveMode(); +} + +EXTERN_C REDHAWK_API int64_t __cdecl RhpGetGcTotalMemory() +{ + // This must be called via p/invoke rather than RuntimeImport to make the stack crawlable. + + Thread * pCurThread = ThreadStore::GetCurrentThread(); + + pCurThread->SetupHackPInvokeTunnel(); + pCurThread->DisablePreemptiveMode(); + + int64_t ret = GCHeapUtilities::GetGCHeap()->GetTotalBytesInUse(); + + pCurThread->EnablePreemptiveMode(); + + return ret; +} + +EXTERN_C REDHAWK_API int32_t __cdecl RhpStartNoGCRegion(int64_t totalSize, UInt32_BOOL hasLohSize, int64_t lohSize, UInt32_BOOL disallowFullBlockingGC) +{ + Thread *pCurThread = ThreadStore::GetCurrentThread(); + ASSERT(!pCurThread->IsCurrentThreadInCooperativeMode()); + + pCurThread->SetupHackPInvokeTunnel(); + pCurThread->DisablePreemptiveMode(); + + int result = GCHeapUtilities::GetGCHeap()->StartNoGCRegion(totalSize, hasLohSize, lohSize, disallowFullBlockingGC); + + pCurThread->EnablePreemptiveMode(); + + return result; +} + +EXTERN_C REDHAWK_API int32_t __cdecl RhpEndNoGCRegion() +{ + ASSERT(!ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode()); + + return GCHeapUtilities::GetGCHeap()->EndNoGCRegion(); +} + +COOP_PINVOKE_HELPER(void, RhSuppressFinalize, (OBJECTREF refObj)) +{ + if (!refObj->get_EEType()->HasFinalizer()) + return; + GCHeapUtilities::GetGCHeap()->SetFinalizationRun(refObj); +} + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhReRegisterForFinalize, (OBJECTREF refObj)) +{ + if (!refObj->get_EEType()->HasFinalizer()) + FC_RETURN_BOOL(true); + FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->RegisterForFinalization(-1, refObj)); +} + +COOP_PINVOKE_HELPER(int32_t, RhGetMaxGcGeneration, ()) +{ + return GCHeapUtilities::GetGCHeap()->GetMaxGeneration(); +} + +COOP_PINVOKE_HELPER(int32_t, RhGetGcCollectionCount, (int32_t generation, CLR_BOOL getSpecialGCCount)) +{ + return GCHeapUtilities::GetGCHeap()->CollectionCount(generation, getSpecialGCCount); +} + +COOP_PINVOKE_HELPER(int32_t, RhGetGeneration, (OBJECTREF obj)) +{ + return GCHeapUtilities::GetGCHeap()->WhichGeneration(obj); +} + +COOP_PINVOKE_HELPER(int32_t, RhGetGcLatencyMode, ()) +{ + return GCHeapUtilities::GetGCHeap()->GetGcLatencyMode(); +} + +COOP_PINVOKE_HELPER(int32_t, RhSetGcLatencyMode, (int32_t newLatencyMode)) +{ + return GCHeapUtilities::GetGCHeap()->SetGcLatencyMode(newLatencyMode); +} + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhIsServerGc, ()) +{ + FC_RETURN_BOOL(GCHeapUtilities::IsServerHeap()); +} + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhRegisterGcCallout, (GcRestrictedCalloutKind eKind, void * pCallout)) +{ + FC_RETURN_BOOL(RestrictedCallouts::RegisterGcCallout(eKind, pCallout)); +} + +COOP_PINVOKE_HELPER(void, RhUnregisterGcCallout, (GcRestrictedCalloutKind eKind, void * pCallout)) +{ + RestrictedCallouts::UnregisterGcCallout(eKind, pCallout); +} + +COOP_PINVOKE_HELPER(int32_t, RhGetLohCompactionMode, ()) +{ + return GCHeapUtilities::GetGCHeap()->GetLOHCompactionMode(); +} + +COOP_PINVOKE_HELPER(void, RhSetLohCompactionMode, (int32_t newLohCompactionMode)) +{ + GCHeapUtilities::GetGCHeap()->SetLOHCompactionMode(newLohCompactionMode); +} + +COOP_PINVOKE_HELPER(int64_t, RhGetCurrentObjSize, ()) +{ + return GCHeapUtilities::GetGCHeap()->GetCurrentObjSize(); +} + +COOP_PINVOKE_HELPER(int64_t, RhGetGCNow, ()) +{ + return GCHeapUtilities::GetGCHeap()->GetNow(); +} + +COOP_PINVOKE_HELPER(int64_t, RhGetLastGCStartTime, (int32_t generation)) +{ + return GCHeapUtilities::GetGCHeap()->GetLastGCStartTime(generation); +} + +COOP_PINVOKE_HELPER(int64_t, RhGetLastGCDuration, (int32_t generation)) +{ + return GCHeapUtilities::GetGCHeap()->GetLastGCDuration(generation); +} + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhRegisterForFullGCNotification, (int32_t maxGenerationThreshold, int32_t largeObjectHeapThreshold)) +{ + ASSERT(maxGenerationThreshold >= 1 && maxGenerationThreshold <= 99); + ASSERT(largeObjectHeapThreshold >= 1 && largeObjectHeapThreshold <= 99); + FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->RegisterForFullGCNotification(maxGenerationThreshold, largeObjectHeapThreshold)); +} + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhCancelFullGCNotification, ()) +{ + FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->CancelFullGCNotification()); +} + +COOP_PINVOKE_HELPER(int32_t, RhWaitForFullGCApproach, (int32_t millisecondsTimeout)) +{ + ASSERT(millisecondsTimeout >= -1); + ASSERT(ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode()); + + int timeout = millisecondsTimeout == -1 ? INFINITE : millisecondsTimeout; + return GCHeapUtilities::GetGCHeap()->WaitForFullGCApproach(millisecondsTimeout); +} + +COOP_PINVOKE_HELPER(int32_t, RhWaitForFullGCComplete, (int32_t millisecondsTimeout)) +{ + ASSERT(millisecondsTimeout >= -1); + ASSERT(ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode()); + + int timeout = millisecondsTimeout == -1 ? INFINITE : millisecondsTimeout; + return GCHeapUtilities::GetGCHeap()->WaitForFullGCComplete(millisecondsTimeout); +} + +COOP_PINVOKE_HELPER(int64_t, RhGetGCSegmentSize, ()) +{ + size_t first = GCHeapUtilities::GetGCHeap()->GetValidSegmentSize(true); + size_t second = GCHeapUtilities::GetGCHeap()->GetValidSegmentSize(false); + + return (first > second) ? first : second; +} + +COOP_PINVOKE_HELPER(int64_t, RhGetAllocatedBytesForCurrentThread, ()) +{ + Thread *pThread = ThreadStore::GetCurrentThread(); + gc_alloc_context *ac = pThread->GetAllocContext(); + int64_t currentAllocated = ac->alloc_bytes + ac->alloc_bytes_uoh - (ac->alloc_limit - ac->alloc_ptr); + return currentAllocated; +} + +struct RH_GC_GENERATION_INFO +{ + uint64_t sizeBefore; + uint64_t fragmentationBefore; + uint64_t sizeAfter; + uint64_t fragmentationAfter; +}; + +struct RH_GH_MEMORY_INFO +{ +public: + uint64_t highMemLoadThresholdBytes; + uint64_t totalAvailableMemoryBytes; + uint64_t lastRecordedMemLoadBytes; + uint64_t lastRecordedHeapSizeBytes; + uint64_t lastRecordedFragmentationBytes; + uint64_t totalCommittedBytes; + uint64_t promotedBytes; + uint64_t pinnedObjectCount; + uint64_t finalizationPendingCount; + uint64_t index; + uint32_t generation; + uint32_t pauseTimePercent; + uint8_t isCompaction; + uint8_t isConcurrent; + RH_GC_GENERATION_INFO generationInfo0; + RH_GC_GENERATION_INFO generationInfo1; + RH_GC_GENERATION_INFO generationInfo2; + RH_GC_GENERATION_INFO generationInfo3; + RH_GC_GENERATION_INFO generationInfo4; + uint64_t pauseDuration0; + uint64_t pauseDuration1; +}; + +COOP_PINVOKE_HELPER(void, RhGetMemoryInfo, (RH_GH_MEMORY_INFO* pData, int kind)) +{ + uint64_t* genInfoRaw = (uint64_t*)&(pData->generationInfo0); + uint64_t* pauseInfoRaw = (uint64_t*)&(pData->pauseDuration0); + + return GCHeapUtilities::GetGCHeap()->GetMemoryInfo( + &(pData->highMemLoadThresholdBytes), + &(pData->totalAvailableMemoryBytes), + &(pData->lastRecordedMemLoadBytes), + &(pData->lastRecordedHeapSizeBytes), + &(pData->lastRecordedFragmentationBytes), + &(pData->totalCommittedBytes), + &(pData->promotedBytes), + &(pData->pinnedObjectCount), + &(pData->finalizationPendingCount), + &(pData->index), + &(pData->generation), + &(pData->pauseTimePercent), + (bool*)&(pData->isCompaction), + (bool*)&(pData->isConcurrent), + genInfoRaw, + pauseInfoRaw, + kind); +} + +COOP_PINVOKE_HELPER(int64_t, RhGetTotalAllocatedBytes, ()) +{ + uint64_t allocated_bytes = GCHeapUtilities::GetGCHeap()->GetTotalAllocatedBytes() - RedhawkGCInterface::GetDeadThreadsNonAllocBytes(); + + // highest reported allocated_bytes. We do not want to report a value less than that even if unused_bytes has increased. + static uint64_t high_watermark; + + uint64_t current_high = high_watermark; + while (allocated_bytes > current_high) + { + uint64_t orig = PalInterlockedCompareExchange64((int64_t*)&high_watermark, allocated_bytes, current_high); + if (orig == current_high) + return allocated_bytes; + + current_high = orig; + } + + return current_high; +} + +EXTERN_C REDHAWK_API int64_t __cdecl RhGetTotalAllocatedBytesPrecise() +{ + int64_t allocated; + + // We need to suspend/restart the EE to get each thread's + // non-allocated memory from their allocation contexts + + GCToEEInterface::SuspendEE(SUSPEND_REASON::SUSPEND_FOR_GC); + + allocated = GCHeapUtilities::GetGCHeap()->GetTotalAllocatedBytes() - RedhawkGCInterface::GetDeadThreadsNonAllocBytes(); + + FOREACH_THREAD(pThread) + { + gc_alloc_context* ac = pThread->GetAllocContext(); + allocated -= ac->alloc_limit - ac->alloc_ptr; + } + END_FOREACH_THREAD + + GCToEEInterface::RestartEE(true); + + return allocated; +} + +extern Object* GcAllocInternal(MethodTable* pEEType, uint32_t uFlags, uintptr_t cbSize, Thread* pThread); + +EXTERN_C REDHAWK_API void RhAllocateNewArray(MethodTable* pArrayEEType, uint32_t numElements, uint32_t flags, Array** pResult) +{ + Thread* pThread = ThreadStore::GetCurrentThread(); + + pThread->SetupHackPInvokeTunnel(); + pThread->DisablePreemptiveMode(); + + ASSERT(!pThread->IsDoNotTriggerGcSet()); + + *pResult = (Array*)GcAllocInternal(pArrayEEType, flags, numElements, pThread); + + pThread->EnablePreemptiveMode(); +} + +EXTERN_C REDHAWK_API void RhAllocateNewObject(MethodTable* pEEType, uint32_t flags, Object** pResult) +{ + Thread* pThread = ThreadStore::GetCurrentThread(); + + pThread->SetupHackPInvokeTunnel(); + pThread->DisablePreemptiveMode(); + + ASSERT(!pThread->IsDoNotTriggerGcSet()); + + *pResult = GcAllocInternal(pEEType, flags, 0, pThread); + + pThread->EnablePreemptiveMode(); +} diff --git a/src/coreclr/nativeaot/Runtime/GCMemoryHelpers.cpp b/src/coreclr/nativeaot/Runtime/GCMemoryHelpers.cpp new file mode 100644 index 00000000000000..15dc03a9bbcebe --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/GCMemoryHelpers.cpp @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Unmanaged GC memory helpers +// + +#include "common.h" +#include "gcenv.h" +#include "PalRedhawkCommon.h" +#include "CommonMacros.inl" + +#include "GCMemoryHelpers.h" +#include "GCMemoryHelpers.inl" + +// This function clears a piece of memory in a GC safe way. It makes the guarantee that it will clear memory in at +// least pointer sized chunks whenever possible. Unaligned memory at the beginning and remaining bytes at the end are +// written bytewise. We must make this guarantee whenever we clear memory in the GC heap that could contain object +// references. The GC or other user threads can read object references at any time, clearing them bytewise can result +// in a read on another thread getting incorrect data. +// +// USAGE: The caller is responsible for hoisting any null reference exceptions to a place where the hardware exception +// can be properly translated to a managed exception. +COOP_PINVOKE_CDECL_HELPER(void *, RhpInitMultibyte, (void * mem, int c, size_t size)) +{ + // The caller must do the null-check because we cannot take an AV in the runtime and translate it to managed. + ASSERT(mem != nullptr); + + uintptr_t bv = (uint8_t)c; + uintptr_t pv = 0; + + if (bv != 0) + { + pv = +#if (POINTER_SIZE == 8) + bv << 7*8 | bv << 6*8 | bv << 5*8 | bv << 4*8 | +#endif + bv << 3*8 | bv << 2*8 | bv << 1*8 | bv; + } + + InlineGCSafeFillMemory(mem, size, pv); + + // memset returns the destination buffer + return mem; +} + + +// This is a GC-safe variant of memcpy. It guarantees that the object references in the GC heap are updated atomically. +// This is required for type safety and proper operation of the background GC. +// +// USAGE: 1) The caller is responsible for performing the appropriate bulk write barrier. +// 2) The caller is responsible for hoisting any null reference exceptions to a place where the hardware +// exception can be properly translated to a managed exception. This is handled by RhpCopyMultibyte. +// 3) The caller must ensure that all three parameters are pointer-size-aligned. This should be the case for +// value types which contain GC refs anyway, so if you want to copy structs without GC refs which might be +// unaligned, then you must use RhpCopyMultibyteNoGCRefs. +COOP_PINVOKE_CDECL_HELPER(void *, memcpyGCRefs, (void * dest, const void *src, size_t len)) +{ + // null pointers are not allowed (they are checked by RhpCopyMultibyte) + ASSERT(dest != nullptr); + ASSERT(src != nullptr); + + InlineForwardGCSafeCopy(dest, src, len); + + // memcpy returns the destination buffer + return dest; +} + +// This is a GC-safe variant of memcpy. It guarantees that the object references in the GC heap are updated atomically. +// This is required for type safety and proper operation of the background GC. +// Writebarrier is included. +// +// USAGE: +// 1) The caller is responsible for hoisting any null reference exceptions to a place where the hardware +// exception can be properly translated to a managed exception. This is handled by RhpCopyMultibyte. +// 2) The caller must ensure that all three parameters are pointer-size-aligned. This should be the case for +// value types which contain GC refs anyway, so if you want to copy structs without GC refs which might be +// unaligned, then you must use RhpCopyMultibyteNoGCRefs. +COOP_PINVOKE_CDECL_HELPER(void *, memcpyGCRefsWithWriteBarrier, (void * dest, const void *src, size_t len)) +{ + // null pointers are not allowed (they are checked by RhpCopyMultibyteWithWriteBarrier) + ASSERT(dest != nullptr); + ASSERT(src != nullptr); + + InlineForwardGCSafeCopy(dest, src, len); + InlinedBulkWriteBarrier(dest, len); + + // memcpy returns the destination buffer + return dest; +} + +// Same as memcpyGCRefsWithWriteBarrier, except it checks if memory might contain GC pointers +// and if so dispatches to memcpyGCRefsWithWriteBarrier and if not uses traditional memcpy +COOP_PINVOKE_CDECL_HELPER(void *, memcpyAnyWithWriteBarrier, (void * dest, const void *src, size_t len)) +{ + // null pointers are not allowed (they are checked by RhpCopyMultibyteWithWriteBarrier) + ASSERT(dest != nullptr); + ASSERT(src != nullptr); + + // Use GC safe copy whenever there might be GC pointers + if (IS_ALIGNED(dest, sizeof(size_t)) && IS_ALIGNED(src, sizeof(size_t)) && IS_ALIGNED(len, sizeof(size_t))) + { + return memcpyGCRefsWithWriteBarrier(dest, src, len); + } + + return memcpy(dest, src, len); +} + +// Move memory, in a way that is compatible with a move onto the heap, but +// does not require the destination pointer to be on the heap. + +COOP_PINVOKE_HELPER(void, RhBulkMoveWithWriteBarrier, (uint8_t* pDest, uint8_t* pSrc, size_t cbDest)) +{ + if (pDest <= pSrc || pSrc + cbDest <= pDest) + InlineForwardGCSafeCopy(pDest, pSrc, cbDest); + else + InlineBackwardGCSafeCopy(pDest, pSrc, cbDest); + + InlinedBulkWriteBarrier(pDest, cbDest); +} + +void GCSafeCopyMemoryWithWriteBarrier(void * dest, const void *src, size_t len) +{ + InlineForwardGCSafeCopy(dest, src, len); + InlinedBulkWriteBarrier(dest, len); +} + +void REDHAWK_CALLCONV RhpBulkWriteBarrier(void* pMemStart, uint32_t cbMemSize) +{ + InlinedBulkWriteBarrier(pMemStart, cbMemSize); +} diff --git a/src/coreclr/nativeaot/Runtime/GCMemoryHelpers.h b/src/coreclr/nativeaot/Runtime/GCMemoryHelpers.h new file mode 100644 index 00000000000000..8f1d9cf8c23c50 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/GCMemoryHelpers.h @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Unmanaged GC memory helpers +// + +void GCSafeCopyMemoryWithWriteBarrier(void * dest, const void *src, size_t len); + +EXTERN_C void REDHAWK_CALLCONV RhpBulkWriteBarrier(void* pMemStart, uint32_t cbMemSize); diff --git a/src/coreclr/nativeaot/Runtime/GCMemoryHelpers.inl b/src/coreclr/nativeaot/Runtime/GCMemoryHelpers.inl new file mode 100644 index 00000000000000..afd601b74d35fe --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/GCMemoryHelpers.inl @@ -0,0 +1,325 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "volatile.h" + +// +// Unmanaged GC memory helpers +// + +#if defined(HOST_64BIT) +static const int card_byte_shift = 11; +static const int card_bundle_byte_shift = 21; +#else +static const int card_byte_shift = 10; + +#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES +#error Manually managed card bundles are currently only implemented for 64 bit hosts. +#endif +#endif + + +// This function fills a piece of memory in a GC safe way. It makes the guarantee +// that it will fill memory in at least pointer sized chunks whenever possible. +// Unaligned memory at the beginning and remaining bytes at the end are written bytewise. +// We must make this guarantee whenever we clear memory in the GC heap that could contain +// object references. The GC or other user threads can read object references at any time, +// clearing them bytewise can result in a read on another thread getting incorrect data. +FORCEINLINE void InlineGCSafeFillMemory(void * mem, size_t size, size_t pv) +{ + uint8_t * memBytes = (uint8_t *)mem; + uint8_t * endBytes = &memBytes[size]; + + // handle unaligned bytes at the beginning + while (!IS_ALIGNED(memBytes, sizeof(void *)) && (memBytes < endBytes)) + *memBytes++ = (uint8_t)pv; + + // now write pointer sized pieces + // volatile ensures that this doesn't get optimized back into a memset call + size_t nPtrs = (endBytes - memBytes) / sizeof(void *); + volatile uintptr_t* memPtr = (uintptr_t*)memBytes; + for (size_t i = 0; i < nPtrs; i++) + *memPtr++ = pv; + + // handle remaining bytes at the end + memBytes = (uint8_t*)memPtr; + while (memBytes < endBytes) + *memBytes++ = (uint8_t)pv; +} + +// These functions copy memory in a GC safe way. They makes the guarantee +// that the memory is copies in at least pointer sized chunks. + +FORCEINLINE void InlineForwardGCSafeCopy(void * dest, const void *src, size_t len) +{ + // All parameters must be pointer-size-aligned + ASSERT(IS_ALIGNED(dest, sizeof(size_t))); + ASSERT(IS_ALIGNED(src, sizeof(size_t))); + ASSERT(IS_ALIGNED(len, sizeof(size_t))); + + size_t size = len; + uint8_t * dmem = (uint8_t *)dest; + uint8_t * smem = (uint8_t *)src; + + // regions must be non-overlapping + ASSERT(dmem <= smem || smem + size <= dmem); + + // copy 4 pointers at a time + while (size >= 4 * sizeof(size_t)) + { + size -= 4 * sizeof(size_t); + ((size_t *)dmem)[0] = ((size_t *)smem)[0]; + ((size_t *)dmem)[1] = ((size_t *)smem)[1]; + ((size_t *)dmem)[2] = ((size_t *)smem)[2]; + ((size_t *)dmem)[3] = ((size_t *)smem)[3]; + smem += 4 * sizeof(size_t); + dmem += 4 * sizeof(size_t); + } + + // copy 2 trailing pointers, if needed + if ((size & (2 * sizeof(size_t))) != 0) + { + ((size_t *)dmem)[0] = ((size_t *)smem)[0]; + ((size_t *)dmem)[1] = ((size_t *)smem)[1]; + smem += 2 * sizeof(size_t); + dmem += 2 * sizeof(size_t); + } + + // finish with one pointer, if needed + if ((size & sizeof(size_t)) != 0) + { + ((size_t *)dmem)[0] = ((size_t *)smem)[0]; + } +} + +FORCEINLINE void InlineBackwardGCSafeCopy(void * dest, const void *src, size_t len) +{ + // All parameters must be pointer-size-aligned + ASSERT(IS_ALIGNED(dest, sizeof(size_t))); + ASSERT(IS_ALIGNED(src, sizeof(size_t))); + ASSERT(IS_ALIGNED(len, sizeof(size_t))); + + size_t size = len; + uint8_t * dmem = (uint8_t *)dest + len; + uint8_t * smem = (uint8_t *)src + len; + + // regions must be non-overlapping + ASSERT(smem <= dmem || dmem + size <= smem); + + // copy 4 pointers at a time + while (size >= 4 * sizeof(size_t)) + { + size -= 4 * sizeof(size_t); + smem -= 4 * sizeof(size_t); + dmem -= 4 * sizeof(size_t); + ((size_t *)dmem)[3] = ((size_t *)smem)[3]; + ((size_t *)dmem)[2] = ((size_t *)smem)[2]; + ((size_t *)dmem)[1] = ((size_t *)smem)[1]; + ((size_t *)dmem)[0] = ((size_t *)smem)[0]; + } + + // copy 2 trailing pointers, if needed + if ((size & (2 * sizeof(size_t))) != 0) + { + smem -= 2 * sizeof(size_t); + dmem -= 2 * sizeof(size_t); + ((size_t *)dmem)[1] = ((size_t *)smem)[1]; + ((size_t *)dmem)[0] = ((size_t *)smem)[0]; + } + + // finish with one pointer, if needed + if ((size & sizeof(size_t)) != 0) + { + smem -= sizeof(size_t); + dmem -= sizeof(size_t); + ((size_t *)dmem)[0] = ((size_t *)smem)[0]; + } +} + + +#ifndef DACCESS_COMPILE +#ifdef WRITE_BARRIER_CHECK +extern uint8_t* g_GCShadow; +extern uint8_t* g_GCShadowEnd; +typedef DPTR(uint8_t) PTR_uint8_t; +extern "C" { + GPTR_DECL(uint8_t, g_lowest_address); + GPTR_DECL(uint8_t, g_highest_address); +} +#endif + +typedef DPTR(uint32_t) PTR_uint32_t; +extern "C" { + GPTR_DECL(uint32_t, g_card_table); +} +static const uint32_t INVALIDGCVALUE = 0xcccccccd; + +FORCEINLINE void InlineWriteBarrier(void * dst, void * ref) +{ + if (((uint8_t*)ref >= g_ephemeral_low) && ((uint8_t*)ref < g_ephemeral_high)) + { + // volatile is used here to prevent fetch of g_card_table from being reordered + // with g_lowest/highest_address check above. See comment in code:gc_heap::grow_brick_card_tables. + uint8_t* pCardByte = (uint8_t *)VolatileLoadWithoutBarrier(&g_card_table) + ((size_t)dst >> LOG2_CLUMP_SIZE); + if (*pCardByte != 0xFF) + *pCardByte = 0xFF; + } +} + +#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES +extern "C" uint32_t* g_card_bundle_table; +#endif // FEATURE_MANUALLY_MANAGED_CARD_BUNDLES + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +extern "C" bool g_sw_ww_enabled_for_gc_heap; +extern "C" uint8_t* g_write_watch_table; + +static const int SoftwareWriteWatchAddressToTableByteIndexShift = 12; + +// In accordance with the SoftwareWriteWatch scheme, marks a range of addresses +// as dirty, starting at the given address and with the given length. +inline static void SoftwareWriteWatchSetDirtyRegion(void* address, size_t length) +{ + // We presumably have just memcopied something to this address, so it can't be null. + assert(address != nullptr); + + // The "base index" is the first index in the SWW table that covers the target + // region of memory. + size_t base_index = reinterpret_cast(address) >> SoftwareWriteWatchAddressToTableByteIndexShift; + + // The "end_index" is the last index in the SWW table that covers the target + // region of memory. + uint8_t* end_pointer = reinterpret_cast(address) + length - 1; + size_t end_index = reinterpret_cast(end_pointer) >> SoftwareWriteWatchAddressToTableByteIndexShift; + + // We'll mark the entire region of memory as dirty by memseting all entries in + // the SWW table between the start and end indexes. + memset(&g_write_watch_table[base_index], ~0, end_index - base_index + 1); +} +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + +FORCEINLINE void InlineCheckedWriteBarrier(void * dst, void * ref) +{ + // if the dst is outside of the heap (unboxed value classes) then we + // simply exit + if (((uint8_t*)dst < g_lowest_address) || ((uint8_t*)dst >= g_highest_address)) + return; + + InlineWriteBarrier(dst, ref); +} + +FORCEINLINE void InlinedBulkWriteBarrier(void* pMemStart, size_t cbMemSize) +{ + // Check whether the writes were even into the heap. If not there's no card update required. + // Also if the size is smaller than a pointer, no write barrier is required. + // This case can occur with universal shared generic code where the size + // is not known at compile time. + if (pMemStart < g_lowest_address || (pMemStart >= g_highest_address) || (cbMemSize < sizeof(uintptr_t))) + { + return; + } + +#ifdef WRITE_BARRIER_CHECK + // Perform shadow heap updates corresponding to the gc heap updates that immediately preceded this helper + // call. + + // If g_GCShadow is 0, don't perform the check. + if (g_GCShadow != NULL) + { + // Compute the shadow heap address corresponding to the beginning of the range of heap addresses modified + // and in the process range check it to make sure we have the shadow version allocated. + uintptr_t* shadowSlot = (uintptr_t*)(g_GCShadow + ((uint8_t*)pMemStart - g_lowest_address)); + if (shadowSlot <= (uintptr_t*)g_GCShadowEnd) + { + // Iterate over every pointer sized slot in the range, copying data from the real heap to the shadow heap. + // As we perform each copy we need to recheck the real heap contents with an ordered read to ensure we're + // not racing with another heap updater. If we discover a race we invalidate the corresponding shadow heap + // slot using a special well-known value so that this location will not be tested during the next shadow + // heap validation. + + uintptr_t* realSlot = (uintptr_t*)pMemStart; + uintptr_t slotCount = cbMemSize / sizeof(uintptr_t); + do + { + // Update shadow slot from real slot. + uintptr_t realValue = *realSlot; + *shadowSlot = realValue; + // Memory barrier to ensure the next read is ordered wrt to the shadow heap write we just made. + PalMemoryBarrier(); + + // Read the real slot contents again. If they don't agree with what we just wrote then someone just raced + // with us and updated the heap again. In such cases we invalidate the shadow slot. + if (*realSlot != realValue) + { + *shadowSlot = INVALIDGCVALUE; + } + + realSlot++; + shadowSlot++; + slotCount--; + } + while (slotCount > 0); + } + } + +#endif // WRITE_BARRIER_CHECK + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + if (g_sw_ww_enabled_for_gc_heap) + { + SoftwareWriteWatchSetDirtyRegion(pMemStart, cbMemSize); + } +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + + // Compute the starting card address and the number of bytes to write (groups of 8 cards). We could try + // for further optimization here using aligned 32-bit writes but there's some overhead in setup required + // and additional complexity. It's not clear this is warranted given that a single byte of card table + // update already covers 1K of object space (2K on 64-bit platforms). It's also not worth probing that + // 1K/2K range to see if any of the pointers appear to be non-ephemeral GC references. Given the size of + // the area the chances are high that at least one interesting GC refenence is present. + + size_t startAddress = (size_t)pMemStart; + size_t endAddress = startAddress + cbMemSize; + size_t startingClump = startAddress >> LOG2_CLUMP_SIZE; + size_t endingClump = (endAddress + CLUMP_SIZE - 1) >> LOG2_CLUMP_SIZE; + + // calculate the number of clumps to mark (round_up(end) - start) + size_t clumpCount = endingClump - startingClump; + // VolatileLoadWithoutBarrier() is used here to prevent fetch of g_card_table from being reordered + // with g_lowest/highest_address check at the beginning of this function. + uint8_t* card = ((uint8_t*)VolatileLoadWithoutBarrier(&g_card_table)) + startingClump; + + // Fill the cards. To avoid cache line thrashing we check whether the cards have already been set before + // writing. + do + { + if (*card != 0xff) + { + *card = 0xff; + } + + card++; + clumpCount--; + } + while (clumpCount != 0); + +#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES + size_t startBundleByte = startAddress >> card_bundle_byte_shift; + size_t endBundleByte = (endAddress + (1 << card_bundle_byte_shift) - 1) >> card_bundle_byte_shift; + size_t bundleByteCount = endBundleByte - startBundleByte; + + uint8_t* pBundleByte = ((uint8_t*)VolatileLoadWithoutBarrier(&g_card_bundle_table)) + startBundleByte; + + do + { + if (*pBundleByte != 0xFF) + { + *pBundleByte = 0xFF; + } + + pBundleByte++; + bundleByteCount--; + } while (bundleByteCount != 0); +#endif +} +#endif // DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/GcStressControl.cpp b/src/coreclr/nativeaot/Runtime/GcStressControl.cpp new file mode 100644 index 00000000000000..e93594fd908b4e --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/GcStressControl.cpp @@ -0,0 +1,182 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" + +#if defined(FEATURE_GC_STRESS) & !defined(DACCESS_COMPILE) + + +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "holder.h" +#include "Crst.h" +#include "RhConfig.h" +#include "gcrhinterface.h" +#include "slist.h" +#include "varint.h" +#include "regdisplay.h" +#include "forward_declarations.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "event.h" +#include "RWLock.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "shash.h" +#include "shash.inl" +#include "GcStressControl.h" + + +class GcStressControl +{ +public: + static bool ShouldHijack(uintptr_t CallsiteIP, HijackType ht) + { + if (s_initState != isInited) + Initialize(); + + // don't hijack for GC stress if we're in a "no GC stress" region + Thread * pCurrentThread = ThreadStore::GetCurrentThread(); + if (pCurrentThread->IsSuppressGcStressSet()) + return false; + + if (g_pRhConfig->GetGcStressThrottleMode() == 0) + { + return true; + } + if (g_pRhConfig->GetGcStressThrottleMode() & gcstm_TriggerRandom) + { + if (GcStressTriggerRandom(CallsiteIP, ht, pCurrentThread)) + return true; + } + if (g_pRhConfig->GetGcStressThrottleMode() & gcstm_TriggerOnFirstHit) + { + if (GcStressTriggerFirstHit(CallsiteIP, ht)) + return true; + } + return false; + } + +private: + enum InitState { isNotInited, isIniting, isInited }; + + static void Initialize() + { + volatile InitState is = (InitState) PalInterlockedCompareExchange((volatile int32_t*)(&s_initState), isIniting, isNotInited); + if (is == isNotInited) + { + s_lock.InitNoThrow(CrstGcStressControl); + + if (g_pRhConfig->GetGcStressSeed()) + s_lGcStressRNGSeed = g_pRhConfig->GetGcStressSeed(); + else + s_lGcStressRNGSeed = (uint32_t)PalGetTickCount64(); + + if (g_pRhConfig->GetGcStressFreqDenom()) + s_lGcStressFreqDenom = g_pRhConfig->GetGcStressFreqDenom(); + else + s_lGcStressFreqDenom = 10000; + + s_initState = isInited; + } + else + { + while (s_initState != isInited) + ; + } + } + + // returns true if no entry was found for CallsiteIP, false otherwise + static bool GcStressTrackAtIP(uintptr_t CallsiteIP, HijackType ht, bool bForceGC) + { + // do this under a lock, as the underlying SHash might be "grown" by + // operations on other threads + + CrstHolder lh(&s_lock); + + const CallsiteCountEntry * pEntry = s_callsites.LookupPtr(CallsiteIP); + size_t hits; + + if (pEntry == NULL) + { + hits = 1; + CallsiteCountEntry e = {CallsiteIP, 1, 1, ht}; + s_callsites.AddOrReplace(e); + } + else + { + hits = ++(const_cast(pEntry)->countHit); + if (bForceGC) + { + ++(const_cast(pEntry)->countForced); + } + } + + return pEntry == NULL; + } + + static bool GcStressTriggerFirstHit(uintptr_t CallsiteIP, HijackType ht) + { + return GcStressTrackAtIP(CallsiteIP, ht, false); + } + + static uint32_t GcStressRNG(uint32_t uMaxValue, Thread *pCurrentThread) + { + if (!pCurrentThread->IsRandInited()) + { + pCurrentThread->SetRandomSeed(s_lGcStressRNGSeed); + } + + return pCurrentThread->NextRand() % uMaxValue; + } + + static bool GcStressTriggerRandom(uintptr_t CallsiteIP, HijackType ht, Thread *pCurrentThread) + { + bool bRes = false; + if (ht == htLoop) + { + bRes = GcStressRNG(s_lGcStressFreqDenom , pCurrentThread) < g_pRhConfig->GetGcStressFreqLoop(); + } + else if (ht == htCallsite) + { + bRes = GcStressRNG(s_lGcStressFreqDenom , pCurrentThread) < g_pRhConfig->GetGcStressFreqCallsite(); + } + if (bRes) + { + // if we're about to trigger a GC, track this in s_callsites + GcStressTrackAtIP(CallsiteIP, ht, true); + } + return bRes; + } + +private: + static CrstStatic s_lock; + static uint32_t s_lGcStressRNGSeed; + static uint32_t s_lGcStressFreqDenom; + static volatile InitState s_initState; + +public: + static CallsiteCountSHash s_callsites; // exposed to the DAC +}; + +// public interface: + +CallsiteCountSHash GcStressControl::s_callsites; +CrstStatic GcStressControl::s_lock; +uint32_t GcStressControl::s_lGcStressRNGSeed = 0; +uint32_t GcStressControl::s_lGcStressFreqDenom = 0; +volatile GcStressControl::InitState GcStressControl::s_initState = GcStressControl::isNotInited; + +GPTR_IMPL_INIT(CallsiteCountSHash, g_pCallsites, &GcStressControl::s_callsites); + +bool ShouldHijackForGcStress(uintptr_t CallsiteIP, HijackType ht) +{ + return GcStressControl::ShouldHijack(CallsiteIP, ht); +} + +#endif // FEATURE_GC_STRESS & !DACCESS_COMPILE + + diff --git a/src/coreclr/nativeaot/Runtime/GcStressControl.h b/src/coreclr/nativeaot/Runtime/GcStressControl.h new file mode 100644 index 00000000000000..d80bdbeefa1853 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/GcStressControl.h @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#ifndef __GcStressControl_h__ +#define __GcStressControl_h__ + + +enum HijackType { htLoop, htCallsite }; +bool ShouldHijackForGcStress(uintptr_t CallsiteIP, HijackType ht); + + +enum GcStressThrottleMode { + gcstm_TriggerAlways = 0x0000, // trigger a GC every time we hit a GC safe point + gcstm_TriggerOnFirstHit = 0x0001, // trigger a GC the first time a GC safe point is hit + gcstm_TriggerRandom = 0x0002, // trigger a GC randomly, as defined by GcStressFreqCallsite/GcStressFreqLoop/GcStressSeed +}; + +struct CallsiteCountEntry +{ + uintptr_t callsiteIP; + uintptr_t countHit; + uintptr_t countForced; + HijackType ht; +}; + +typedef DPTR(CallsiteCountEntry) PTR_CallsiteCountEntry; + +class CallsiteCountTraits: public NoRemoveSHashTraits< DefaultSHashTraits < CallsiteCountEntry > > +{ +public: + typedef uintptr_t key_t; + + static uintptr_t GetKey(const CallsiteCountEntry & e) { return e.callsiteIP; } + + static count_t Hash(uintptr_t k) + { return (count_t) k; } + + static bool Equals(uintptr_t k1, uintptr_t k2) + { return k1 == k2; } + + static CallsiteCountEntry Null() + { CallsiteCountEntry e; e.callsiteIP = 0; return e; } + + static bool IsNull(const CallsiteCountEntry & e) + { return e.callsiteIP == 0; } +}; + +typedef SHash < CallsiteCountTraits > CallsiteCountSHash; +typedef DPTR(CallsiteCountSHash) PTR_CallsiteCountSHash; + + +#endif // __GcStressControl_h__ diff --git a/src/coreclr/nativeaot/Runtime/HandleTableHelpers.cpp b/src/coreclr/nativeaot/Runtime/HandleTableHelpers.cpp new file mode 100644 index 00000000000000..3f4b88e527159a --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/HandleTableHelpers.cpp @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Helper functions that are p/invoked from redhawkm in order to expose handle table functionality to managed +// code. These p/invokes are special in that the handle table code requires we remain in co-operative mode +// (since these routines mutate the handle tables which are also accessed during garbage collections). The +// binder has special knowledge of these methods and doesn't generate the normal code to transition out of the +// runtime prior to the call. +// +#include "common.h" +#include "gcenv.h" +#include "objecthandle.h" +#include "RestrictedCallouts.h" +#include "gchandleutilities.h" + + +COOP_PINVOKE_HELPER(OBJECTHANDLE, RhpHandleAlloc, (Object *pObject, int type)) +{ + return GCHandleUtilities::GetGCHandleManager()->GetGlobalHandleStore()->CreateHandleOfType(pObject, (HandleType)type); +} + +COOP_PINVOKE_HELPER(OBJECTHANDLE, RhpHandleAllocDependent, (Object *pPrimary, Object *pSecondary)) +{ + return GCHandleUtilities::GetGCHandleManager()->GetGlobalHandleStore()->CreateDependentHandle(pPrimary, pSecondary); +} + +COOP_PINVOKE_HELPER(void, RhHandleFree, (OBJECTHANDLE handle)) +{ + GCHandleUtilities::GetGCHandleManager()->DestroyHandleOfUnknownType(handle); +} + +COOP_PINVOKE_HELPER(Object *, RhHandleGet, (OBJECTHANDLE handle)) +{ + return ObjectFromHandle(handle); +} + +COOP_PINVOKE_HELPER(Object *, RhHandleGetDependent, (OBJECTHANDLE handle, Object **ppSecondary)) +{ + Object *pPrimary = ObjectFromHandle(handle); + *ppSecondary = (pPrimary != NULL) ? GetDependentHandleSecondary(handle) : NULL; + return pPrimary; +} + +COOP_PINVOKE_HELPER(void, RhHandleSetDependentSecondary, (OBJECTHANDLE handle, Object *pSecondary)) +{ + SetDependentHandleSecondary(handle, pSecondary); +} + +COOP_PINVOKE_HELPER(void, RhHandleSet, (OBJECTHANDLE handle, Object *pObject)) +{ + GCHandleUtilities::GetGCHandleManager()->StoreObjectInHandle(handle, pObject); +} + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhRegisterRefCountedHandleCallback, (void * pCallout, MethodTable * pTypeFilter)) +{ + FC_RETURN_BOOL(RestrictedCallouts::RegisterRefCountedHandleCallback(pCallout, pTypeFilter)); +} + +COOP_PINVOKE_HELPER(void, RhUnregisterRefCountedHandleCallback, (void * pCallout, MethodTable * pTypeFilter)) +{ + RestrictedCallouts::UnregisterRefCountedHandleCallback(pCallout, pTypeFilter); +} diff --git a/src/coreclr/nativeaot/Runtime/ICodeManager.h b/src/coreclr/nativeaot/Runtime/ICodeManager.h new file mode 100644 index 00000000000000..ee5fb474b9bdbe --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/ICodeManager.h @@ -0,0 +1,166 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#pragma once + +#define ICODEMANAGER_INCLUDED + +// TODO: Debugger/DAC support (look for TODO: JIT) + +struct REGDISPLAY; + +#define GC_CALL_INTERIOR 0x1 +#define GC_CALL_PINNED 0x2 +#define GC_CALL_CHECK_APP_DOMAIN 0x4 +#define GC_CALL_STATIC 0x8 + +typedef void (*GCEnumCallback)( + void * hCallback, // callback data + PTR_PTR_VOID pObject, // address of object-reference we are reporting + uint32_t flags // is this a pinned and/or interior pointer +); + +struct GCEnumContext +{ + GCEnumCallback pCallback; +}; + +// All values but GCRK_Unknown must correspond to MethodReturnKind enumeration in gcinfo.h +enum GCRefKind : unsigned char +{ + GCRK_Scalar = 0x00, + GCRK_Object = 0x01, + GCRK_Byref = 0x02, +#ifdef TARGET_ARM64 + // Composite return kinds for value types returned in two registers (encoded with two bits per register) + GCRK_Scalar_Obj = (GCRK_Object << 2) | GCRK_Scalar, + GCRK_Obj_Obj = (GCRK_Object << 2) | GCRK_Object, + GCRK_Byref_Obj = (GCRK_Object << 2) | GCRK_Byref, + GCRK_Scalar_Byref = (GCRK_Byref << 2) | GCRK_Scalar, + GCRK_Obj_Byref = (GCRK_Byref << 2) | GCRK_Object, + GCRK_Byref_Byref = (GCRK_Byref << 2) | GCRK_Byref, + + GCRK_LastValid = GCRK_Byref_Byref, +#else // TARGET_ARM64 + GCRK_LastValid = GCRK_Byref, +#endif // TARGET_ARM64 + GCRK_Unknown = 0xFF, +}; + +#ifdef TARGET_ARM64 +// Extract individual GCRefKind components from a composite return kind +inline GCRefKind ExtractReg0ReturnKind(GCRefKind returnKind) +{ + ASSERT(returnKind <= GCRK_LastValid); + return (GCRefKind)(returnKind & (GCRK_Object | GCRK_Byref)); +} + +inline GCRefKind ExtractReg1ReturnKind(GCRefKind returnKind) +{ + ASSERT(returnKind <= GCRK_LastValid); + return (GCRefKind)(returnKind >> 2); +} +#endif // TARGET_ARM64 + +// +// MethodInfo is placeholder type used to allocate space for MethodInfo. Maximum size +// of the actual method should be less or equal to the placeholder size. +// It avoids memory allocation during stackwalk. +// +class MethodInfo +{ + TADDR dummyPtrs[5]; + int32_t dummyInts[8]; +}; + +class EHEnumState +{ + TADDR dummyPtrs[2]; + int32_t dummyInts[2]; +}; + +enum EHClauseKind +{ + EH_CLAUSE_TYPED = 0, + EH_CLAUSE_FAULT = 1, + EH_CLAUSE_FILTER = 2, + EH_CLAUSE_UNUSED = 3, +}; + +struct EHClause +{ + EHClauseKind m_clauseKind; + uint32_t m_tryStartOffset; + uint32_t m_tryEndOffset; + uint8_t* m_filterAddress; + uint8_t* m_handlerAddress; + void* m_pTargetType; +}; + +// Note: make sure you change the def in System\Runtime\InternalCalls.cs if you change this! +enum class ClasslibFunctionId +{ + GetRuntimeException = 0, + FailFast = 1, + UnhandledExceptionHandler = 2, + AppendExceptionStackFrame = 3, + // unused = 4, + GetSystemArrayEEType = 5, + OnFirstChanceException = 6, + OnUnhandledException = 7, + IDynamicCastableIsInterfaceImplemented = 8, + IDynamicCastableGetInterfaceImplementation = 9, +}; + +enum class AssociatedDataFlags : unsigned char +{ + None = 0, + HasUnboxingStubTarget = 1, +}; + +class ICodeManager +{ +public: + virtual bool FindMethodInfo(PTR_VOID ControlPC, + MethodInfo * pMethodInfoOut) = 0; + + virtual bool IsFunclet(MethodInfo * pMethodInfo) = 0; + + virtual PTR_VOID GetFramePointer(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet) = 0; + + virtual void EnumGcRefs(MethodInfo * pMethodInfo, + PTR_VOID safePointAddress, + REGDISPLAY * pRegisterSet, + GCEnumContext * hCallback) = 0; + + virtual bool UnwindStackFrame(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet, // in/out + PTR_VOID * ppPreviousTransitionFrame) = 0; // out + + virtual uintptr_t GetConservativeUpperBoundForOutgoingArgs(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet) = 0; + + virtual bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet, // in + PTR_PTR_VOID * ppvRetAddrLocation, // out + GCRefKind * pRetValueKind) = 0; // out + + virtual void UnsynchronizedHijackMethodLoops(MethodInfo * pMethodInfo) = 0; + + virtual PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC) = 0; + + virtual bool EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMethodStartAddress, EHEnumState * pEHEnumState) = 0; + + virtual bool EHEnumNext(EHEnumState * pEHEnumState, EHClause * pEHClause) = 0; + + virtual PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo) = 0; + + virtual PTR_VOID GetOsModuleHandle() = 0; + + virtual void * GetClasslibFunction(ClasslibFunctionId functionId) = 0; + + // Returns any custom data attached to the method. Format: + // AssociatedDataFlags // 1 byte. Flags describing the data stored + // Data (stream of bytes) // Variable size (depending on flags). Custom data associated with method + virtual PTR_VOID GetAssociatedData(PTR_VOID ControlPC) = 0; +}; diff --git a/src/coreclr/nativeaot/Runtime/IntrinsicConstants.h b/src/coreclr/nativeaot/Runtime/IntrinsicConstants.h new file mode 100644 index 00000000000000..d3ea7a17bc0423 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/IntrinsicConstants.h @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef INTRINSICCONSTANTS_INCLUDED +#define INTRINSICCONSTANTS_INCLUDED + +// Should match the constants defined in the compiler in HardwareIntrinsicHelpers.Aot.cs + +#if defined(HOST_X86) || defined(HOST_AMD64) +enum XArchIntrinsicConstants +{ + XArchIntrinsicConstants_Aes = 0x0001, + XArchIntrinsicConstants_Pclmulqdq = 0x0002, + XArchIntrinsicConstants_Sse3 = 0x0004, + XArchIntrinsicConstants_Ssse3 = 0x0008, + XArchIntrinsicConstants_Sse41 = 0x0010, + XArchIntrinsicConstants_Sse42 = 0x0020, + XArchIntrinsicConstants_Popcnt = 0x0040, + XArchIntrinsicConstants_Avx = 0x0080, + XArchIntrinsicConstants_Fma = 0x0100, + XArchIntrinsicConstants_Avx2 = 0x0200, + XArchIntrinsicConstants_Bmi1 = 0x0400, + XArchIntrinsicConstants_Bmi2 = 0x0800, + XArchIntrinsicConstants_Lzcnt = 0x1000, +}; +#endif //HOST_X86 || HOST_AMD64 + +#if defined(HOST_ARM64) +enum ARM64IntrinsicConstants +{ + ARM64IntrinsicConstants_ArmBase = 0x0001, + ARM64IntrinsicConstants_ArmBase_Arm64 = 0x0002, + ARM64IntrinsicConstants_AdvSimd = 0x0004, + ARM64IntrinsicConstants_AdvSimd_Arm64 = 0x0008, + ARM64IntrinsicConstants_Aes = 0x0010, + ARM64IntrinsicConstants_Crc32 = 0x0020, + ARM64IntrinsicConstants_Crc32_Arm64 = 0x0040, + ARM64IntrinsicConstants_Sha1 = 0x0080, + ARM64IntrinsicConstants_Sha256 = 0x0100, + ARM64IntrinsicConstants_Atomics = 0x0200, + ARM64IntrinsicConstants_Vector64 = 0x0400, + ARM64IntrinsicConstants_Vector128 = 0x0800 +}; +#endif //HOST_ARM64 + +#endif //!INTRINSICCONSTANTS_INCLUDED diff --git a/src/coreclr/nativeaot/Runtime/MathHelpers.cpp b/src/coreclr/nativeaot/Runtime/MathHelpers.cpp new file mode 100644 index 00000000000000..dfcd1c194b8576 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/MathHelpers.cpp @@ -0,0 +1,179 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "rhassert.h" + +// +// Floating point and 64-bit integer math helpers. +// + +EXTERN_C REDHAWK_API uint64_t REDHAWK_CALLCONV RhpDbl2ULng(double val) +{ + return((uint64_t)val); +} + +#undef min +#undef max +#include + +EXTERN_C REDHAWK_API float REDHAWK_CALLCONV RhpFltRem(float dividend, float divisor) +{ + // + // From the ECMA standard: + // + // If [divisor] is zero or [dividend] is infinity + // the result is NaN. + // If [divisor] is infinity, + // the result is [dividend] (negated for -infinity***). + // + // ***"negated for -infinity" has been removed from the spec + // + + if (divisor==0 || !std::isfinite(dividend)) + { + return -nanf(0); + } + else if (!std::isfinite(divisor) && !std::isnan(divisor)) + { + return dividend; + } + // else... + return fmodf(dividend,divisor); +} + +EXTERN_C REDHAWK_API double REDHAWK_CALLCONV RhpDblRem(double dividend, double divisor) +{ + // + // From the ECMA standard: + // + // If [divisor] is zero or [dividend] is infinity + // the result is NaN. + // If [divisor] is infinity, + // the result is [dividend] (negated for -infinity***). + // + // ***"negated for -infinity" has been removed from the spec + // + if (divisor==0 || !std::isfinite(dividend)) + { + return -nan(0); + } + else if (!std::isfinite(divisor) && !std::isnan(divisor)) + { + return dividend; + } + // else... + return(fmod(dividend,divisor)); +} + +EXTERN_C REDHAWK_API double REDHAWK_CALLCONV RhpDblRound(double value) +{ + return round(value); +} + +EXTERN_C REDHAWK_API float REDHAWK_CALLCONV RhpFltRound(float value) +{ + return roundf(value); +} + +#ifdef HOST_ARM +EXTERN_C REDHAWK_API int32_t REDHAWK_CALLCONV RhpIDiv(int32_t i, int32_t j) +{ + ASSERT(j && "Divide by zero!"); + return i / j; +} + +EXTERN_C REDHAWK_API uint32_t REDHAWK_CALLCONV RhpUDiv(uint32_t i, uint32_t j) +{ + ASSERT(j && "Divide by zero!"); + return i / j; +} + +EXTERN_C REDHAWK_API int64_t REDHAWK_CALLCONV RhpLDiv(int64_t i, int64_t j) +{ + ASSERT(j && "Divide by zero!"); + return i / j; +} + +EXTERN_C REDHAWK_API uint64_t REDHAWK_CALLCONV RhpULDiv(uint64_t i, uint64_t j) +{ + ASSERT(j && "Divide by zero!"); + return i / j; +} + +EXTERN_C REDHAWK_API int32_t REDHAWK_CALLCONV RhpIMod(int32_t i, int32_t j) +{ + ASSERT(j && "Divide by zero!"); + return i % j; +} + +EXTERN_C REDHAWK_API uint32_t REDHAWK_CALLCONV RhpUMod(uint32_t i, uint32_t j) +{ + ASSERT(j && "Divide by zero!"); + return i % j; +} + +EXTERN_C REDHAWK_API int64_t REDHAWK_CALLCONV RhpLMod(int64_t i, int64_t j) +{ + ASSERT(j && "Divide by zero!"); + return i % j; +} + +EXTERN_C REDHAWK_API uint64_t REDHAWK_CALLCONV RhpULMod(uint64_t i, uint64_t j) +{ + ASSERT(j && "Divide by zero!"); + return i % j; +} + +EXTERN_C REDHAWK_API int64_t REDHAWK_CALLCONV RhpLMul(int64_t i, int64_t j) +{ + return i * j; +} + +EXTERN_C REDHAWK_API uint64_t REDHAWK_CALLCONV RhpULMul(uint64_t i, uint64_t j) +{ + return i * j; +} + +EXTERN_C REDHAWK_API uint64_t REDHAWK_CALLCONV RhpLRsz(uint64_t i, int32_t j) +{ + return i >> j; +} + +EXTERN_C REDHAWK_API int64_t REDHAWK_CALLCONV RhpLRsh(int64_t i, int32_t j) +{ + return i >> j; +} + +EXTERN_C REDHAWK_API int64_t REDHAWK_CALLCONV RhpLLsh(int64_t i, int32_t j) +{ + return i << j; +} + +EXTERN_C REDHAWK_API int64_t REDHAWK_CALLCONV RhpDbl2Lng(double val) +{ + return (int64_t)val; +} + +EXTERN_C REDHAWK_API int32_t REDHAWK_CALLCONV RhpDbl2Int(double val) +{ + return (int32_t)val; +} + +EXTERN_C REDHAWK_API uint32_t REDHAWK_CALLCONV RhpDbl2UInt(double val) +{ + return (uint32_t)val; +} + +EXTERN_C REDHAWK_API double REDHAWK_CALLCONV RhpLng2Dbl(int64_t val) +{ + return (double)val; +} + +EXTERN_C REDHAWK_API double REDHAWK_CALLCONV RhpULng2Dbl(uint64_t val) +{ + return (double)val; +} + +#endif // HOST_ARM diff --git a/src/coreclr/nativeaot/Runtime/MethodTable.cpp b/src/coreclr/nativeaot/Runtime/MethodTable.cpp new file mode 100644 index 00000000000000..4197cd8ba36237 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/MethodTable.cpp @@ -0,0 +1,167 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "rhassert.h" +#include "rhbinder.h" +#include "MethodTable.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" + +#include "CommonMacros.inl" +#include "MethodTable.inl" + +#pragma warning(disable:4127) // C4127: conditional expression is constant + +// Validate an MethodTable extracted from an object. +bool MethodTable::Validate(bool assertOnFail /* default: true */) +{ +#define REPORT_FAILURE() do { if (assertOnFail) { ASSERT_UNCONDITIONALLY("MethodTable::Validate check failed"); } return false; } while (false) + + // Deal with the most common case of a bad pointer without an exception. + if (this == NULL) + REPORT_FAILURE(); + + // MethodTable structures should be at least pointer aligned. + if (dac_cast(this) & (sizeof(TADDR)-1)) + REPORT_FAILURE(); + + // Verify object size is bigger than min_obj_size + size_t minObjSize = get_BaseSize(); + if (get_ComponentSize() != 0) + { + // If it is an array, we will align the size to the nearest pointer alignment, even if there are + // zero elements. Our strings take advantage of this. + minObjSize = (size_t)ALIGN_UP(minObjSize, sizeof(TADDR)); + } + if (minObjSize < (3 * sizeof(TADDR))) + REPORT_FAILURE(); + + switch (get_Kind()) + { + case CanonicalEEType: + { + // If the parent type is NULL this had better look like Object. + if (!IsInterface() && (m_RelatedType.m_pBaseType == NULL)) + { + if (IsRelatedTypeViaIAT() || + get_IsValueType() || + HasFinalizer() || + HasReferenceFields() || + HasGenericVariance()) + { + REPORT_FAILURE(); + } + } + break; + } + + case ClonedEEType: + { + // Cloned types must have a related type. + if (m_RelatedType.m_ppCanonicalTypeViaIAT == NULL) + REPORT_FAILURE(); + + // Either we're dealing with a clone of String or a generic type. We can tell the difference based + // on the component size. + switch (get_ComponentSize()) + { + case 0: + { + // Cloned generic type. + if (!IsRelatedTypeViaIAT()) + { + REPORT_FAILURE(); + } + break; + } + + case 2: + { + // Cloned string. + if (get_IsValueType() || + HasFinalizer() || + HasReferenceFields() || + HasGenericVariance()) + { + REPORT_FAILURE(); + } + + break; + } + + default: + // Apart from cloned strings we don't expected cloned types to have a component size. + REPORT_FAILURE(); + } + break; + } + + case ParameterizedEEType: + { + // The only parameter EETypes that can exist on the heap are arrays + + // Array types must have a related type. + if (m_RelatedType.m_pRelatedParameterType == NULL) + REPORT_FAILURE(); + + // Component size cannot be zero in this case. + if (get_ComponentSize() == 0) + REPORT_FAILURE(); + + if (get_IsValueType() || + HasFinalizer() || + HasGenericVariance()) + { + REPORT_FAILURE(); + } + + break; + } + + case GenericTypeDefEEType: + { + // We should never see uninstantiated generic type definitions here + // since we should never construct an object instance around them. + REPORT_FAILURE(); + } + + default: + // Should be unreachable. + REPORT_FAILURE(); + } + +#undef REPORT_FAILURE + + return true; +} + +//----------------------------------------------------------------------------------------------------------- +MethodTable::Kinds MethodTable::get_Kind() +{ + return (Kinds)(m_usFlags & (uint16_t)EETypeKindMask); +} + +//----------------------------------------------------------------------------------------------------------- +MethodTable * MethodTable::get_CanonicalEEType() +{ + // cloned EETypes must always refer to types in other modules + ASSERT(IsCloned()); + if (IsRelatedTypeViaIAT()) + return *PTR_PTR_EEType(reinterpret_cast(m_RelatedType.m_ppCanonicalTypeViaIAT)); + else + return PTR_EEType(reinterpret_cast(m_RelatedType.m_pCanonicalType)); // in the R2R case, the link is direct rather than indirect via the IAT +} + +//----------------------------------------------------------------------------------------------------------- +MethodTable * MethodTable::get_RelatedParameterType() +{ + ASSERT(IsParameterizedType()); + + if (IsRelatedTypeViaIAT()) + return *PTR_PTR_EEType(reinterpret_cast(m_RelatedType.m_ppRelatedParameterTypeViaIAT)); + else + return PTR_EEType(reinterpret_cast(m_RelatedType.m_pRelatedParameterType)); +} diff --git a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp new file mode 100644 index 00000000000000..7d602f7b37b259 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp @@ -0,0 +1,434 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Miscellaneous unmanaged helpers called by managed code. +// + +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "holder.h" +#include "Crst.h" +#include "rhbinder.h" +#include "RWLock.h" +#include "RuntimeInstance.h" +#include "regdisplay.h" +#include "gcrhinterface.h" +#include "varint.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "event.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "thread.inl" +#include "gcrhinterface.h" +#include "shash.h" +#include "TypeManager.h" +#include "MethodTable.h" +#include "ObjectLayout.h" +#include "slist.inl" +#include "MethodTable.inl" +#include "CommonMacros.inl" +#include "volatile.h" +#include "GCMemoryHelpers.h" +#include "GCMemoryHelpers.inl" +#include "yieldprocessornormalized.h" + +COOP_PINVOKE_HELPER(void, RhDebugBreak, ()) +{ + PalDebugBreak(); +} + +// Busy spin for the given number of iterations. +EXTERN_C REDHAWK_API void __cdecl RhSpinWait(int32_t iterations) +{ + YieldProcessorNormalizationInfo normalizationInfo; + YieldProcessorNormalizedForPreSkylakeCount(normalizationInfo, iterations); +} + +// Yield the cpu to another thread ready to process, if one is available. +EXTERN_C REDHAWK_API UInt32_BOOL __cdecl RhYield() +{ + // This must be called via p/invoke -- it's a wait operation and we don't want to block thread suspension on this. + ASSERT_MSG(!ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode(), + "You must p/invoke to RhYield"); + + return PalSwitchToThread(); +} + +EXTERN_C REDHAWK_API void __cdecl RhFlushProcessWriteBuffers() +{ + // This must be called via p/invoke -- it's a wait operation and we don't want to block thread suspension on this. + ASSERT_MSG(!ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode(), + "You must p/invoke to RhFlushProcessWriteBuffers"); + + PalFlushProcessWriteBuffers(); +} + +// Get the list of currently loaded Redhawk modules (as OS HMODULE handles). The caller provides a reference +// to an array of pointer-sized elements and we return the total number of modules currently loaded (whether +// that is less than, equal to or greater than the number of elements in the array). If there are more modules +// loaded than the array will hold then the array is filled to capacity and the caller can tell further +// modules are available based on the return count. It is also possible to call this method without an array, +// in which case just the module count is returned (note that it's still possible for the module count to +// increase between calls to this method). +COOP_PINVOKE_HELPER(uint32_t, RhGetLoadedOSModules, (Array * pResultArray)) +{ + // Note that we depend on the fact that this is a COOP helper to make writing into an unpinned array safe. + + // If a result array is passed then it should be an array type with pointer-sized components that are not + // GC-references. + ASSERT(!pResultArray || pResultArray->get_EEType()->IsArray()); + ASSERT(!pResultArray || !pResultArray->get_EEType()->HasReferenceFields()); + ASSERT(!pResultArray || pResultArray->get_EEType()->get_ComponentSize() == sizeof(void*)); + + uint32_t cResultArrayElements = pResultArray ? pResultArray->GetArrayLength() : 0; + HANDLE * pResultElements = pResultArray ? (HANDLE*)(pResultArray + 1) : NULL; + + uint32_t cModules = 0; + + ReaderWriterLock::ReadHolder read(&GetRuntimeInstance()->GetTypeManagerLock()); + + RuntimeInstance::OsModuleList *osModules = GetRuntimeInstance()->GetOsModuleList(); + + for (RuntimeInstance::OsModuleList::Iterator iter = osModules->Begin(); iter != osModules->End(); iter++) + { + if (pResultArray && (cModules < cResultArrayElements)) + pResultElements[cModules] = iter->m_osModule; + cModules++; + } + + return cModules; +} + +COOP_PINVOKE_HELPER(HANDLE, RhGetOSModuleFromPointer, (PTR_VOID pPointerVal)) +{ + ICodeManager * pCodeManager = GetRuntimeInstance()->FindCodeManagerByAddress(pPointerVal); + + if (pCodeManager != NULL) + return (HANDLE)pCodeManager->GetOsModuleHandle(); + + return NULL; +} + +COOP_PINVOKE_HELPER(HANDLE, RhGetOSModuleFromEEType, (MethodTable * pEEType)) +{ + return pEEType->GetTypeManagerPtr()->AsTypeManager()->GetOsModuleHandle(); +} + +COOP_PINVOKE_HELPER(TypeManagerHandle, RhGetModuleFromEEType, (MethodTable * pEEType)) +{ + return *pEEType->GetTypeManagerPtr(); +} + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhFindBlob, (TypeManagerHandle *pTypeManagerHandle, uint32_t blobId, uint8_t ** ppbBlob, uint32_t * pcbBlob)) +{ + TypeManagerHandle typeManagerHandle = *pTypeManagerHandle; + + ReadyToRunSectionType section = + (ReadyToRunSectionType)((uint32_t)ReadyToRunSectionType::ReadonlyBlobRegionStart + blobId); + ASSERT(section <= ReadyToRunSectionType::ReadonlyBlobRegionEnd); + + TypeManager* pModule = typeManagerHandle.AsTypeManager(); + + int length; + void* pBlob; + pBlob = pModule->GetModuleSection(section, &length); + + *ppbBlob = (uint8_t*)pBlob; + *pcbBlob = (uint32_t)length; + + FC_RETURN_BOOL(pBlob != NULL); +} + +COOP_PINVOKE_HELPER(void *, RhGetTargetOfUnboxingAndInstantiatingStub, (void * pUnboxStub)) +{ + return GetRuntimeInstance()->GetTargetOfUnboxingAndInstantiatingStub(pUnboxStub); +} + +#if TARGET_ARM +//***************************************************************************** +// Extract the 16-bit immediate from ARM Thumb2 Instruction (format T2_N) +//***************************************************************************** +static FORCEINLINE uint16_t GetThumb2Imm16(uint16_t * p) +{ + return ((p[0] << 12) & 0xf000) | + ((p[0] << 1) & 0x0800) | + ((p[1] >> 4) & 0x0700) | + ((p[1] >> 0) & 0x00ff); +} + +//***************************************************************************** +// Extract the 32-bit immediate from movw/movt sequence +//***************************************************************************** +inline uint32_t GetThumb2Mov32(uint16_t * p) +{ + // Make sure we are decoding movw/movt sequence + ASSERT((*(p + 0) & 0xFBF0) == 0xF240); + ASSERT((*(p + 2) & 0xFBF0) == 0xF2C0); + + return (uint32_t)GetThumb2Imm16(p) + ((uint32_t)GetThumb2Imm16(p + 2) << 16); +} + +//***************************************************************************** +// Extract the 24-bit distance from a B/BL instruction +//***************************************************************************** +inline int32_t GetThumb2BlRel24(uint16_t * p) +{ + uint16_t Opcode0 = p[0]; + uint16_t Opcode1 = p[1]; + + uint32_t S = Opcode0 >> 10; + uint32_t J2 = Opcode1 >> 11; + uint32_t J1 = Opcode1 >> 13; + + int32_t ret = + ((S << 24) & 0x1000000) | + (((J1 ^ S ^ 1) << 23) & 0x0800000) | + (((J2 ^ S ^ 1) << 22) & 0x0400000) | + ((Opcode0 << 12) & 0x03FF000) | + ((Opcode1 << 1) & 0x0000FFE); + + // Sign-extend and return + return (ret << 7) >> 7; +} +#endif // TARGET_ARM + +// Given a pointer to code, find out if this points to an import stub +// or unboxing stub, and if so, return the address that stub jumps to +COOP_PINVOKE_HELPER(uint8_t *, RhGetCodeTarget, (uint8_t * pCodeOrg)) +{ + bool unboxingStub = false; + + // First, check the unboxing stubs regions known by the runtime (if any exist) + if (!GetRuntimeInstance()->IsUnboxingStub(pCodeOrg)) + { + return pCodeOrg; + } + +#ifdef TARGET_AMD64 + uint8_t * pCode = pCodeOrg; + + // is this "add rcx/rdi,8"? + if (pCode[0] == 0x48 && + pCode[1] == 0x83 && +#ifdef UNIX_AMD64_ABI + pCode[2] == 0xc7 && +#else + pCode[2] == 0xc1 && +#endif + pCode[3] == 0x08) + { + // unboxing sequence + unboxingStub = true; + pCode += 4; + } + // is this an indirect jump? + if (pCode[0] == 0xff && pCode[1] == 0x25) + { + // normal import stub - dist to IAT cell is relative to the point *after* the instruction + int32_t distToIatCell = *(int32_t *)&pCode[2]; + uint8_t ** pIatCell = (uint8_t **)(pCode + 6 + distToIatCell); + return *pIatCell; + } + // is this an unboxing stub followed by a relative jump? + else if (unboxingStub && pCode[0] == 0xe9) + { + // relative jump - dist is relative to the point *after* the instruction + int32_t distToTarget = *(int32_t *)&pCode[1]; + uint8_t * target = pCode + 5 + distToTarget; + return target; + } + +#elif TARGET_X86 + uint8_t * pCode = pCodeOrg; + + // is this "add ecx,4"? + if (pCode[0] == 0x83 && pCode[1] == 0xc1 && pCode[2] == 0x04) + { + // unboxing sequence + unboxingStub = true; + pCode += 3; + } + // is this an indirect jump? + if (pCode[0] == 0xff && pCode[1] == 0x25) + { + // normal import stub - address of IAT follows + uint8_t **pIatCell = *(uint8_t ***)&pCode[2]; + return *pIatCell; + } + // is this an unboxing stub followed by a relative jump? + else if (unboxingStub && pCode[0] == 0xe9) + { + // relative jump - dist is relative to the point *after* the instruction + int32_t distToTarget = *(int32_t *)&pCode[1]; + uint8_t * pTarget = pCode + 5 + distToTarget; + return pTarget; + } + +#elif TARGET_ARM + uint16_t * pCode = (uint16_t *)((size_t)pCodeOrg & ~THUMB_CODE); + // is this "adds r0,4"? + if (pCode[0] == 0x3004) + { + // unboxing sequence + unboxingStub = true; + pCode += 1; + } + // is this movw r12,#imm16; movt r12,#imm16; ldr pc,[r12] + // or movw r12,#imm16; movt r12,#imm16; bx r12 + if ((pCode[0] & 0xfbf0) == 0xf240 && (pCode[1] & 0x0f00) == 0x0c00 + && (pCode[2] & 0xfbf0) == 0xf2c0 && (pCode[3] & 0x0f00) == 0x0c00 + && ((pCode[4] == 0xf8dc && pCode[5] == 0xf000) || pCode[4] == 0x4760)) + { + if (pCode[4] == 0xf8dc && pCode[5] == 0xf000) + { + // ldr pc,[r12] + uint8_t **pIatCell = (uint8_t **)GetThumb2Mov32(pCode); + return *pIatCell; + } + else if (pCode[4] == 0x4760) + { + // bx r12 + return (uint8_t *)GetThumb2Mov32(pCode); + } + } + // is this an unboxing stub followed by a relative jump? + else if (unboxingStub && (pCode[0] & 0xf800) == 0xf000 && (pCode[1] & 0xd000) == 0x9000) + { + int32_t distToTarget = GetThumb2BlRel24(pCode); + uint8_t * pTarget = (uint8_t *)(pCode + 2) + distToTarget + THUMB_CODE; + return (uint8_t *)pTarget; + } + +#elif TARGET_ARM64 + uint32_t * pCode = (uint32_t *)pCodeOrg; + // is this "add x0,x0,#8"? + if (pCode[0] == 0x91002000) + { + // unboxing sequence + unboxingStub = true; + pCode++; + } + // is this an indirect jump? + // adrp xip0,#imm21; ldr xip0,[xip0,#imm12]; br xip0 + if ((pCode[0] & 0x9f00001f) == 0x90000010 && + (pCode[1] & 0xffc003ff) == 0xf9400210 && + pCode[2] == 0xd61f0200) + { + // normal import stub - dist to IAT cell is relative to (PC & ~0xfff) + // adrp: imm = SignExtend(immhi:immlo:Zeros(12), 64); + int64_t distToIatCell = (((((int64_t)pCode[0] & ~0x1f) << 40) >> 31) | ((pCode[0] >> 17) & 0x3000)); + // ldr: offset = LSL(ZeroExtend(imm12, 64), 3); + distToIatCell += (pCode[1] >> 7) & 0x7ff8; + uint8_t ** pIatCell = (uint8_t **)(((int64_t)pCode & ~0xfff) + distToIatCell); + return *pIatCell; + } + // is this an unboxing stub followed by a relative jump? + else if (unboxingStub && (pCode[0] >> 26) == 0x5) + { + // relative jump - dist is relative to the instruction + // offset = SignExtend(imm26:'00', 64); + int64_t distToTarget = ((int64_t)pCode[0] << 38) >> 36; + return (uint8_t *)pCode + distToTarget; + } +#else + UNREFERENCED_PARAMETER(unboxingStub); + PORTABILITY_ASSERT("RhGetCodeTarget"); +#endif + + return pCodeOrg; +} + +// Get the universal transition thunk. If the universal transition stub is called through +// the normal PE static linkage model, a jump stub would be used which may interfere with +// the custom calling convention of the universal transition thunk. So instead, a special +// api just for getting the thunk address is needed. +// TODO: On ARM this may still result in a jump stub that trashes R12. Determine if anything +// needs to be done about that when we implement the stub for ARM. +extern "C" void RhpUniversalTransition(); +COOP_PINVOKE_HELPER(void*, RhGetUniversalTransitionThunk, ()) +{ + return (void*)RhpUniversalTransition; +} + +extern CrstStatic g_CastCacheLock; + +EXTERN_C REDHAWK_API void __cdecl RhpAcquireCastCacheLock() +{ + g_CastCacheLock.Enter(); +} + +EXTERN_C REDHAWK_API void __cdecl RhpReleaseCastCacheLock() +{ + g_CastCacheLock.Leave(); +} + +extern CrstStatic g_ThunkPoolLock; + +EXTERN_C REDHAWK_API void __cdecl RhpAcquireThunkPoolLock() +{ + g_ThunkPoolLock.Enter(); +} + +EXTERN_C REDHAWK_API void __cdecl RhpReleaseThunkPoolLock() +{ + g_ThunkPoolLock.Leave(); +} + +EXTERN_C int32_t __cdecl RhpCalculateStackTraceWorker(void* pOutputBuffer, uint32_t outputBufferLength, void* pAddressInCurrentFrame); + +EXTERN_C REDHAWK_API int32_t __cdecl RhpGetCurrentThreadStackTrace(void* pOutputBuffer, uint32_t outputBufferLength, void* pAddressInCurrentFrame) +{ + // This must be called via p/invoke rather than RuntimeImport to make the stack crawlable. + + ThreadStore::GetCurrentThread()->SetupHackPInvokeTunnel(); + + return RhpCalculateStackTraceWorker(pOutputBuffer, outputBufferLength, pAddressInCurrentFrame); +} + +COOP_PINVOKE_HELPER(void*, RhpRegisterFrozenSegment, (void* pSegmentStart, size_t length)) +{ + return RedhawkGCInterface::RegisterFrozenSegment(pSegmentStart, length); +} + +COOP_PINVOKE_HELPER(void, RhpUnregisterFrozenSegment, (void* pSegmentHandle)) +{ + RedhawkGCInterface::UnregisterFrozenSegment((GcSegmentHandle)pSegmentHandle); +} + +COOP_PINVOKE_HELPER(void*, RhpGetModuleSection, (TypeManagerHandle *pModule, int32_t headerId, int32_t* length)) +{ + return pModule->AsTypeManager()->GetModuleSection((ReadyToRunSectionType)headerId, length); +} + +COOP_PINVOKE_HELPER(void, RhGetCurrentThreadStackBounds, (PTR_VOID * ppStackLow, PTR_VOID * ppStackHigh)) +{ + ThreadStore::GetCurrentThread()->GetStackBounds(ppStackLow, ppStackHigh); +} + +// Function to call when a thread is detached from the runtime +ThreadExitCallback g_threadExitCallback; + +COOP_PINVOKE_HELPER(void, RhSetThreadExitCallback, (void * pCallback)) +{ + g_threadExitCallback = (ThreadExitCallback)pCallback; +} + +COOP_PINVOKE_HELPER(int32_t, RhGetProcessCpuCount, ()) +{ + return PalGetProcessCpuCount(); +} + +#if defined(TARGET_X86) || defined(TARGET_AMD64) +EXTERN_C REDHAWK_API void __cdecl RhCpuIdEx(int* cpuInfo, int functionId, int subFunctionId) +{ + __cpuidex(cpuInfo, functionId, subFunctionId); +} +#endif diff --git a/src/coreclr/nativeaot/Runtime/ObjectLayout.cpp b/src/coreclr/nativeaot/Runtime/ObjectLayout.cpp new file mode 100644 index 00000000000000..748104c050f609 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/ObjectLayout.cpp @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Implementations of functions dealing with object layout related types. +// +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "rhassert.h" +#include "RedhawkWarnings.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "TargetPtrs.h" +#include "MethodTable.h" +#include "ObjectLayout.h" +#include "MethodTable.inl" + +#ifndef DACCESS_COMPILE +void Object::InitEEType(MethodTable * pEEType) +{ + ASSERT(NULL == m_pEEType); + m_pEEType = pEEType; +} +#endif + +uint32_t Array::GetArrayLength() +{ + return m_Length; +} + +void* Array::GetArrayData() +{ + uint8_t* pData = (uint8_t*)this; + pData += (get_EEType()->get_BaseSize() - sizeof(ObjHeader)); + return pData; +} + +#ifndef DACCESS_COMPILE +void Array::InitArrayLength(uint32_t length) +{ + m_Length = length; +} + +void ObjHeader::SetBit(uint32_t uBit) +{ + PalInterlockedOr(&m_uSyncBlockValue, uBit); +} + +void ObjHeader::ClrBit(uint32_t uBit) +{ + PalInterlockedAnd(&m_uSyncBlockValue, ~uBit); +} + +size_t Object::GetSize() +{ + MethodTable * pEEType = get_EEType(); + + // strings have component size2, all other non-arrays should have 0 + ASSERT(( pEEType->get_ComponentSize() <= 2) || pEEType->IsArray()); + + size_t s = pEEType->get_BaseSize(); + uint16_t componentSize = pEEType->get_ComponentSize(); + if (componentSize > 0) + s += ((Array*)this)->GetArrayLength() * componentSize; + return s; +} + +#endif diff --git a/src/coreclr/nativeaot/Runtime/ObjectLayout.h b/src/coreclr/nativeaot/Runtime/ObjectLayout.h new file mode 100644 index 00000000000000..eb9fdc0ad2eece --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/ObjectLayout.h @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Low-level types describing GC object layouts. +// + +// Bits stolen from the sync block index that the GC/HandleTable knows about (currently these are at the same +// positions as the mainline runtime but we can change this below when it becomes apparent how Redhawk will +// handle sync blocks). +#define BIT_SBLK_GC_RESERVE 0x20000000 +#define BIT_SBLK_FINALIZER_RUN 0x40000000 + +// The sync block index header (small structure that immediately precedes every object in the GC heap). Only +// the GC uses this so far, and only to store a couple of bits of information. +class ObjHeader +{ +private: +#if defined(HOST_64BIT) + uint32_t m_uAlignpad; +#endif // HOST_64BIT + uint32_t m_uSyncBlockValue; + +public: + uint32_t GetBits() { return m_uSyncBlockValue; } + void SetBit(uint32_t uBit); + void ClrBit(uint32_t uBit); + void SetGCBit() { m_uSyncBlockValue |= BIT_SBLK_GC_RESERVE; } + void ClrGCBit() { m_uSyncBlockValue &= ~BIT_SBLK_GC_RESERVE; } +}; + +//------------------------------------------------------------------------------------------------- +static uintptr_t const SYNC_BLOCK_SKEW = sizeof(void *); + +class MethodTable; +typedef DPTR(class MethodTable) PTR_EEType; +class MethodTable; + +//------------------------------------------------------------------------------------------------- +class Object +{ + friend class AsmOffsets; + + PTR_EEType m_pEEType; +public: + MethodTable * get_EEType() const + { return m_pEEType; } + MethodTable * get_SafeEEType() const +#ifdef TARGET_64BIT + { return dac_cast((dac_cast(m_pEEType)) & ~((uintptr_t)7)); } +#else + { return dac_cast((dac_cast(m_pEEType)) & ~((uintptr_t)3)); } +#endif + ObjHeader * GetHeader() { return dac_cast(dac_cast(this) - SYNC_BLOCK_SKEW); } +#ifndef DACCESS_COMPILE + void set_EEType(MethodTable * pEEType) + { m_pEEType = pEEType; } + void InitEEType(MethodTable * pEEType); + + size_t GetSize(); +#endif + + // + // Adapter methods for GC code so that GC and runtime code can use the same type. + // These methods are deprecated -- only use from existing GC code. + // + MethodTable * RawGetMethodTable() const + { + return (MethodTable*)get_EEType(); + } + MethodTable * GetGCSafeMethodTable() const + { + return (MethodTable *)get_SafeEEType(); + } + void RawSetMethodTable(MethodTable * pMT) + { + m_pEEType = PTR_EEType((MethodTable *)pMT); + } + ////// End adaptor methods +}; +typedef DPTR(Object) PTR_Object; +typedef DPTR(PTR_Object) PTR_PTR_Object; + +//------------------------------------------------------------------------------------------------- +static uintptr_t const MIN_OBJECT_SIZE = (2 * sizeof(void*)) + sizeof(ObjHeader); + +//------------------------------------------------------------------------------------------------- +static uintptr_t const REFERENCE_SIZE = sizeof(Object *); + +//------------------------------------------------------------------------------------------------- +class Array : public Object +{ + friend class ArrayBase; + friend class AsmOffsets; + + uint32_t m_Length; +#if defined(HOST_64BIT) + uint32_t m_uAlignpad; +#endif // HOST_64BIT +public: + uint32_t GetArrayLength(); + void InitArrayLength(uint32_t length); + void* GetArrayData(); +}; +typedef DPTR(Array) PTR_Array; + +//------------------------------------------------------------------------------------------------- +class String : public Object +{ + friend class AsmOffsets; + friend class StringConstants; + + uint32_t m_Length; + uint16_t m_FirstChar; +}; +typedef DPTR(String) PTR_String; + +//------------------------------------------------------------------------------------------------- +class StringConstants +{ +public: + static uintptr_t const ComponentSize = sizeof(((String*)0)->m_FirstChar); + static uintptr_t const BaseSize = sizeof(ObjHeader) + offsetof(String, m_FirstChar) + ComponentSize; +}; + +//------------------------------------------------------------------------------------------------- +static uintptr_t const STRING_COMPONENT_SIZE = StringConstants::ComponentSize; + +//------------------------------------------------------------------------------------------------- +static uintptr_t const STRING_BASE_SIZE = StringConstants::BaseSize; + +//------------------------------------------------------------------------------------------------- +static uintptr_t const MAX_STRING_LENGTH = 0x3FFFFFDF; diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawk.h b/src/coreclr/nativeaot/Runtime/PalRedhawk.h new file mode 100644 index 00000000000000..951177d372fddc --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/PalRedhawk.h @@ -0,0 +1,754 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Provides declarations for external resources consumed by Redhawk. This comprises functionality +// normally exported from Win32 libraries such as KERNEL32 and MSVCRT. When hosted on Win32 calls to these +// functions become simple pass throughs to the native implementation via export forwarding entries in a PAL +// (Platform Abstraction Layer) library. On other platforms the PAL library has actual code to emulate the +// functionality of these same APIs. +// +// In order to make it both obvious and intentional where Redhawk consumes an external API, such functions are +// decorated with an 'Pal' prefix. Ideally the associated supporting types, constants etc. would be +// similarly isolated from their concrete Win32 definitions, making the extent of platform dependence within +// the core explicit. For now that is too big a work item and we'll settle for manually restricting the use of +// external header files to within this header. +// + +#include +#include +#include "gcenv.structs.h" +#include "IntrinsicConstants.h" + +#ifndef PAL_REDHAWK_INCLUDED +#define PAL_REDHAWK_INCLUDED + +/* Adapted from intrin.h - For compatibility with , some intrinsics are __cdecl except on x64 */ +#if defined (_M_X64) +#define __PN__MACHINECALL_CDECL_OR_DEFAULT +#else +#define __PN__MACHINECALL_CDECL_OR_DEFAULT __cdecl +#endif + +#ifndef _MSC_VER + +// Note: Win32-hosted GCC predefines __stdcall and __cdecl, but Unix- +// hosted GCC does not. + +#ifdef __i386__ + +#if !defined(__cdecl) +#define __cdecl __attribute__((cdecl)) +#endif + +#else // !defined(__i386__) + +#define __cdecl + +#endif // !defined(__i386__) + +#endif // !_MSC_VER + +#ifndef _INC_WINDOWS +//#ifndef DACCESS_COMPILE + +// There are some fairly primitive type definitions below but don't pull them into the rest of Redhawk unless +// we have to (in which case these definitions will move to CommonTypes.h). +typedef WCHAR * LPWSTR; +typedef const WCHAR * LPCWSTR; +typedef char * LPSTR; +typedef const char * LPCSTR; +typedef void * HINSTANCE; + +typedef void * LPSECURITY_ATTRIBUTES; +typedef void * LPOVERLAPPED; + +#ifdef TARGET_UNIX +#define __stdcall +#endif + +#ifndef __GCENV_BASE_INCLUDED__ +#define CALLBACK __stdcall +#define WINAPI __stdcall +#define WINBASEAPI __declspec(dllimport) +#endif //!__GCENV_BASE_INCLUDED__ + +#ifdef TARGET_UNIX +#define DIRECTORY_SEPARATOR_CHAR '/' +#else // TARGET_UNIX +#define DIRECTORY_SEPARATOR_CHAR '\\' +#endif // TARGET_UNIX + +typedef union _LARGE_INTEGER { + struct { +#if BIGENDIAN + int32_t HighPart; + uint32_t LowPart; +#else + uint32_t LowPart; + int32_t HighPart; +#endif + } u; + int64_t QuadPart; +} LARGE_INTEGER, *PLARGE_INTEGER; + +typedef struct _GUID { + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +} GUID; + +#define DECLARE_HANDLE(_name) typedef HANDLE _name + +// defined in gcrhenv.cpp +bool __SwitchToThread(uint32_t dwSleepMSec, uint32_t dwSwitchCount); + +struct FILETIME +{ + uint32_t dwLowDateTime; + uint32_t dwHighDateTime; +}; + +#ifdef HOST_AMD64 + +typedef struct DECLSPEC_ALIGN(16) _XSAVE_FORMAT { + uint16_t ControlWord; + uint16_t StatusWord; + uint8_t TagWord; + uint8_t Reserved1; + uint16_t ErrorOpcode; + uint32_t ErrorOffset; + uint16_t ErrorSelector; + uint16_t Reserved2; + uint32_t DataOffset; + uint16_t DataSelector; + uint16_t Reserved3; + uint32_t MxCsr; + uint32_t MxCsr_Mask; + Fp128 FloatRegisters[8]; +#if defined(HOST_64BIT) + Fp128 XmmRegisters[16]; + uint8_t Reserved4[96]; +#else + Fp128 XmmRegisters[8]; + uint8_t Reserved4[220]; + uint32_t Cr0NpxState; +#endif +} XSAVE_FORMAT, *PXSAVE_FORMAT; + + +typedef XSAVE_FORMAT XMM_SAVE_AREA32, *PXMM_SAVE_AREA32; + +typedef struct DECLSPEC_ALIGN(16) _CONTEXT { + uint64_t P1Home; + uint64_t P2Home; + uint64_t P3Home; + uint64_t P4Home; + uint64_t P5Home; + uint64_t P6Home; + uint32_t ContextFlags; + uint32_t MxCsr; + uint16_t SegCs; + uint16_t SegDs; + uint16_t SegEs; + uint16_t SegFs; + uint16_t SegGs; + uint16_t SegSs; + uint32_t EFlags; + uint64_t Dr0; + uint64_t Dr1; + uint64_t Dr2; + uint64_t Dr3; + uint64_t Dr6; + uint64_t Dr7; + uint64_t Rax; + uint64_t Rcx; + uint64_t Rdx; + uint64_t Rbx; + uint64_t Rsp; + uint64_t Rbp; + uint64_t Rsi; + uint64_t Rdi; + uint64_t R8; + uint64_t R9; + uint64_t R10; + uint64_t R11; + uint64_t R12; + uint64_t R13; + uint64_t R14; + uint64_t R15; + uint64_t Rip; + union { + XMM_SAVE_AREA32 FltSave; + struct { + Fp128 Header[2]; + Fp128 Legacy[8]; + Fp128 Xmm0; + Fp128 Xmm1; + Fp128 Xmm2; + Fp128 Xmm3; + Fp128 Xmm4; + Fp128 Xmm5; + Fp128 Xmm6; + Fp128 Xmm7; + Fp128 Xmm8; + Fp128 Xmm9; + Fp128 Xmm10; + Fp128 Xmm11; + Fp128 Xmm12; + Fp128 Xmm13; + Fp128 Xmm14; + Fp128 Xmm15; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + Fp128 VectorRegister[26]; + uint64_t VectorControl; + uint64_t DebugControl; + uint64_t LastBranchToRip; + uint64_t LastBranchFromRip; + uint64_t LastExceptionToRip; + uint64_t LastExceptionFromRip; + + void SetIp(uintptr_t ip) { Rip = ip; } + void SetSp(uintptr_t sp) { Rsp = sp; } +#ifdef UNIX_AMD64_ABI + void SetArg0Reg(uintptr_t val) { Rdi = val; } + void SetArg1Reg(uintptr_t val) { Rsi = val; } +#else // UNIX_AMD64_ABI + void SetArg0Reg(uintptr_t val) { Rcx = val; } + void SetArg1Reg(uintptr_t val) { Rdx = val; } +#endif // UNIX_AMD64_ABI + uintptr_t GetIp() { return Rip; } + uintptr_t GetSp() { return Rsp; } +} CONTEXT, *PCONTEXT; +#elif defined(HOST_ARM) + +#define ARM_MAX_BREAKPOINTS 8 +#define ARM_MAX_WATCHPOINTS 1 + +typedef struct DECLSPEC_ALIGN(8) _CONTEXT { + uint32_t ContextFlags; + uint32_t R0; + uint32_t R1; + uint32_t R2; + uint32_t R3; + uint32_t R4; + uint32_t R5; + uint32_t R6; + uint32_t R7; + uint32_t R8; + uint32_t R9; + uint32_t R10; + uint32_t R11; + uint32_t R12; + uint32_t Sp; // R13 + uint32_t Lr; // R14 + uint32_t Pc; // R15 + uint32_t Cpsr; + uint32_t Fpscr; + uint32_t Padding; + union { + Fp128 Q[16]; + uint64_t D[32]; + uint32_t S[32]; + } DUMMYUNIONNAME; + uint32_t Bvr[ARM_MAX_BREAKPOINTS]; + uint32_t Bcr[ARM_MAX_BREAKPOINTS]; + uint32_t Wvr[ARM_MAX_WATCHPOINTS]; + uint32_t Wcr[ARM_MAX_WATCHPOINTS]; + uint32_t Padding2[2]; + + void SetIp(uintptr_t ip) { Pc = ip; } + void SetArg0Reg(uintptr_t val) { R0 = val; } + void SetArg1Reg(uintptr_t val) { R1 = val; } + uintptr_t GetIp() { return Pc; } + uintptr_t GetLr() { return Lr; } +} CONTEXT, *PCONTEXT; + +#elif defined(HOST_X86) +#define SIZE_OF_80387_REGISTERS 80 +#define MAXIMUM_SUPPORTED_EXTENSION 512 + +typedef struct _FLOATING_SAVE_AREA { + uint32_t ControlWord; + uint32_t StatusWord; + uint32_t TagWord; + uint32_t ErrorOffset; + uint32_t ErrorSelector; + uint32_t DataOffset; + uint32_t DataSelector; + uint8_t RegisterArea[SIZE_OF_80387_REGISTERS]; + uint32_t Cr0NpxState; +} FLOATING_SAVE_AREA; + +#include "pshpack4.h" +typedef struct _CONTEXT { + uint32_t ContextFlags; + uint32_t Dr0; + uint32_t Dr1; + uint32_t Dr2; + uint32_t Dr3; + uint32_t Dr6; + uint32_t Dr7; + FLOATING_SAVE_AREA FloatSave; + uint32_t SegGs; + uint32_t SegFs; + uint32_t SegEs; + uint32_t SegDs; + uint32_t Edi; + uint32_t Esi; + uint32_t Ebx; + uint32_t Edx; + uint32_t Ecx; + uint32_t Eax; + uint32_t Ebp; + uint32_t Eip; + uint32_t SegCs; + uint32_t EFlags; + uint32_t Esp; + uint32_t SegSs; + uint8_t ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; + + void SetIp(uintptr_t ip) { Eip = ip; } + void SetSp(uintptr_t sp) { Esp = sp; } + void SetArg0Reg(uintptr_t val) { Ecx = val; } + void SetArg1Reg(uintptr_t val) { Edx = val; } + uintptr_t GetIp() { return Eip; } + uintptr_t GetSp() { return Esp; } +} CONTEXT, *PCONTEXT; +#include "poppack.h" + +#elif defined(HOST_ARM64) + +// Specify the number of breakpoints and watchpoints that the OS +// will track. Architecturally, ARM64 supports up to 16. In practice, +// however, almost no one implements more than 4 of each. + +#define ARM64_MAX_BREAKPOINTS 8 +#define ARM64_MAX_WATCHPOINTS 2 + +typedef struct _NEON128 { + uint64_t Low; + int64_t High; +} NEON128, *PNEON128; + +typedef struct DECLSPEC_ALIGN(16) _CONTEXT { + // + // Control flags. + // + uint32_t ContextFlags; + + // + // Integer registers + // + uint32_t Cpsr; // NZVF + DAIF + CurrentEL + SPSel + union { + struct { + uint64_t X0; + uint64_t X1; + uint64_t X2; + uint64_t X3; + uint64_t X4; + uint64_t X5; + uint64_t X6; + uint64_t X7; + uint64_t X8; + uint64_t X9; + uint64_t X10; + uint64_t X11; + uint64_t X12; + uint64_t X13; + uint64_t X14; + uint64_t X15; + uint64_t X16; + uint64_t X17; + uint64_t X18; + uint64_t X19; + uint64_t X20; + uint64_t X21; + uint64_t X22; + uint64_t X23; + uint64_t X24; + uint64_t X25; + uint64_t X26; + uint64_t X27; + uint64_t X28; +#pragma warning(push) +#pragma warning(disable:4201) // nameless struct + }; + uint64_t X[29]; + }; +#pragma warning(pop) + uint64_t Fp; // X29 + uint64_t Lr; // X30 + uint64_t Sp; + uint64_t Pc; + + // + // Floating Point/NEON Registers + // + NEON128 V[32]; + uint32_t Fpcr; + uint32_t Fpsr; + + // + // Debug registers + // + uint32_t Bcr[ARM64_MAX_BREAKPOINTS]; + uint64_t Bvr[ARM64_MAX_BREAKPOINTS]; + uint32_t Wcr[ARM64_MAX_WATCHPOINTS]; + uint64_t Wvr[ARM64_MAX_WATCHPOINTS]; + + void SetIp(uintptr_t ip) { Pc = ip; } + void SetArg0Reg(uintptr_t val) { X0 = val; } + void SetArg1Reg(uintptr_t val) { X1 = val; } + uintptr_t GetIp() { return Pc; } + uintptr_t GetLr() { return Lr; } +} CONTEXT, *PCONTEXT; + +#elif defined(HOST_WASM) + +typedef struct DECLSPEC_ALIGN(8) _CONTEXT { + // TODO: Figure out if WebAssembly has a meaningful context available + void SetIp(uintptr_t ip) { } + void SetArg0Reg(uintptr_t val) { } + void SetArg1Reg(uintptr_t val) { } + uintptr_t GetIp() { return 0; } +} CONTEXT, *PCONTEXT; +#endif + +#define EXCEPTION_MAXIMUM_PARAMETERS 15 // maximum number of exception parameters + +typedef struct _EXCEPTION_RECORD32 { + uint32_t ExceptionCode; + uint32_t ExceptionFlags; + uintptr_t ExceptionRecord; + uintptr_t ExceptionAddress; + uint32_t NumberParameters; + uintptr_t ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; +} EXCEPTION_RECORD, *PEXCEPTION_RECORD; + +typedef struct _EXCEPTION_POINTERS { + PEXCEPTION_RECORD ExceptionRecord; + PCONTEXT ContextRecord; +} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS; + +typedef int32_t (__stdcall *PVECTORED_EXCEPTION_HANDLER)( + PEXCEPTION_POINTERS ExceptionInfo + ); + +#define EXCEPTION_CONTINUE_EXECUTION (-1) +#define EXCEPTION_CONTINUE_SEARCH (0) +#define EXCEPTION_EXECUTE_HANDLER (1) + +typedef enum _EXCEPTION_DISPOSITION { + ExceptionContinueExecution, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind +} EXCEPTION_DISPOSITION; + +#define STATUS_BREAKPOINT ((uint32_t )0x80000003L) +#define STATUS_SINGLE_STEP ((uint32_t )0x80000004L) +#define STATUS_ACCESS_VIOLATION ((uint32_t )0xC0000005L) +#define STATUS_STACK_OVERFLOW ((uint32_t )0xC00000FDL) +#define STATUS_REDHAWK_NULL_REFERENCE ((uint32_t )0x00000000L) +#define STATUS_REDHAWK_UNMANAGED_HELPER_NULL_REFERENCE ((uint32_t )0x00000042L) + +#ifdef TARGET_UNIX +#define NULL_AREA_SIZE (4*1024) +#else +#define NULL_AREA_SIZE (64*1024) +#endif + +//#endif // !DACCESS_COMPILE +#endif // !_INC_WINDOWS + + + +#ifndef DACCESS_COMPILE +#ifndef _INC_WINDOWS + +#ifndef __GCENV_BASE_INCLUDED__ +#define TRUE 1 +#define FALSE 0 +#endif // !__GCENV_BASE_INCLUDED__ + +#define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1) + +#define DLL_PROCESS_ATTACH 1 +#define DLL_THREAD_ATTACH 2 +#define DLL_THREAD_DETACH 3 +#define DLL_PROCESS_DETACH 0 + +#define INFINITE 0xFFFFFFFF + +#define DUPLICATE_CLOSE_SOURCE 0x00000001 +#define DUPLICATE_SAME_ACCESS 0x00000002 + +#define PAGE_NOACCESS 0x01 +#define PAGE_READONLY 0x02 +#define PAGE_READWRITE 0x04 +#define PAGE_WRITECOPY 0x08 +#define PAGE_EXECUTE 0x10 +#define PAGE_EXECUTE_READ 0x20 +#define PAGE_EXECUTE_READWRITE 0x40 +#define PAGE_EXECUTE_WRITECOPY 0x80 +#define PAGE_GUARD 0x100 +#define PAGE_NOCACHE 0x200 +#define PAGE_WRITECOMBINE 0x400 +#define MEM_COMMIT 0x1000 +#define MEM_RESERVE 0x2000 +#define MEM_DECOMMIT 0x4000 +#define MEM_RELEASE 0x8000 +#define MEM_FREE 0x10000 +#define MEM_PRIVATE 0x20000 +#define MEM_MAPPED 0x40000 +#define MEM_RESET 0x80000 +#define MEM_TOP_DOWN 0x100000 +#define MEM_WRITE_WATCH 0x200000 +#define MEM_PHYSICAL 0x400000 +#define MEM_LARGE_PAGES 0x20000000 +#define MEM_4MB_PAGES 0x80000000 + +#define WAIT_OBJECT_0 0 +#define WAIT_TIMEOUT 258 +#define WAIT_FAILED 0xFFFFFFFF + +#endif // !_INC_WINDOWS +#endif // !DACCESS_COMPILE + +typedef uint64_t REGHANDLE; +typedef uint64_t TRACEHANDLE; + +#ifndef _EVNTPROV_H_ +struct EVENT_DATA_DESCRIPTOR +{ + uint64_t Ptr; + uint32_t Size; + uint32_t Reserved; +}; + +struct EVENT_DESCRIPTOR +{ + uint16_t Id; + uint8_t Version; + uint8_t Channel; + uint8_t Level; + uint8_t Opcode; + uint16_t Task; + uint64_t Keyword; + +}; + +struct EVENT_FILTER_DESCRIPTOR +{ + uint64_t Ptr; + uint32_t Size; + uint32_t Type; +}; + +__forceinline +void +EventDataDescCreate(_Out_ EVENT_DATA_DESCRIPTOR * EventDataDescriptor, _In_opt_ const void * DataPtr, uint32_t DataSize) +{ + EventDataDescriptor->Ptr = (uint64_t)DataPtr; + EventDataDescriptor->Size = DataSize; + EventDataDescriptor->Reserved = 0; +} +#endif // _EVNTPROV_H_ + +extern uint32_t g_RhNumberOfProcessors; + +#ifdef TARGET_UNIX +#define REDHAWK_PALIMPORT extern "C" +#define REDHAWK_PALEXPORT extern "C" +#define REDHAWK_PALAPI +#else +#define REDHAWK_PALIMPORT EXTERN_C +#define REDHAWK_PALAPI __stdcall +#endif // TARGET_UNIX + +#ifndef DACCESS_COMPILE + +#ifdef _DEBUG +#define CaptureStackBackTrace RtlCaptureStackBackTrace +#endif + +#ifndef _INC_WINDOWS +// Include the list of external functions we wish to access. If we do our job 100% then it will be +// possible to link without any direct reference to any Win32 library. +#include "PalRedhawkFunctions.h" +#endif // !_INC_WINDOWS +#endif // !DACCESS_COMPILE + +// The Redhawk PAL must be initialized before any of its exports can be called. Returns true for a successful +// initialization and false on failure. +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalInit(); + +// Given the OS handle of a loaded module, compute the upper and lower virtual address bounds (inclusive). +REDHAWK_PALIMPORT void REDHAWK_PALAPI PalGetModuleBounds(HANDLE hOsHandle, _Out_ uint8_t ** ppLowerBound, _Out_ uint8_t ** ppUpperBound); + +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalGetThreadContext(HANDLE hThread, _Out_ PAL_LIMITED_CONTEXT * pCtx); + +REDHAWK_PALIMPORT int32_t REDHAWK_PALAPI PalGetProcessCpuCount(); + +// Retrieves the entire range of memory dedicated to the calling thread's stack. This does +// not get the current dynamic bounds of the stack, which can be significantly smaller than +// the maximum bounds. +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalGetMaximumStackBounds(_Out_ void** ppStackLowOut, _Out_ void** ppStackHighOut); + +// Return value: number of characters in name string +REDHAWK_PALIMPORT int32_t PalGetModuleFileName(_Out_ const TCHAR** pModuleNameOut, HANDLE moduleBase); + +#if _WIN32 + +// Various intrinsic declarations needed for the PalGetCurrentTEB implementation below. +#if defined(HOST_X86) +EXTERN_C unsigned long __readfsdword(unsigned long Offset); +#pragma intrinsic(__readfsdword) +#elif defined(HOST_AMD64) +EXTERN_C unsigned __int64 __readgsqword(unsigned long Offset); +#pragma intrinsic(__readgsqword) +#elif defined(HOST_ARM) +EXTERN_C unsigned int _MoveFromCoprocessor(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); +#pragma intrinsic(_MoveFromCoprocessor) +#elif defined(HOST_ARM64) +EXTERN_C unsigned __int64 __getReg(int); +#pragma intrinsic(__getReg) +#else +#error Unsupported architecture +#endif + +// Retrieves the OS TEB for the current thread. +inline uint8_t * PalNtCurrentTeb() +{ +#if defined(HOST_X86) + return (uint8_t*)__readfsdword(0x18); +#elif defined(HOST_AMD64) + return (uint8_t*)__readgsqword(0x30); +#elif defined(HOST_ARM) + return (uint8_t*)_MoveFromCoprocessor(15, 0, 13, 0, 2); +#elif defined(HOST_ARM64) + return (uint8_t*)__getReg(18); +#else +#error Unsupported architecture +#endif +} + +// Offsets of ThreadLocalStoragePointer in the TEB. +#if defined(HOST_64BIT) +#define OFFSETOF__TEB__ThreadLocalStoragePointer 0x58 +#else +#define OFFSETOF__TEB__ThreadLocalStoragePointer 0x2c +#endif + +#else // _WIN32 + +inline uint8_t * PalNtCurrentTeb() +{ + // UNIXTODO: Implement PalNtCurrentTeb + return NULL; +} + +#define OFFSETOF__TEB__ThreadLocalStoragePointer 0 + +#endif // _WIN32 + +// +// Compiler intrinsic definitions. In the interest of performance the PAL doesn't provide exports of these +// (that would defeat the purpose of having an intrinsic in the first place). Instead we place the necessary +// compiler linkage directly inline in this header. As a result this section may have platform specific +// conditional compilation (upto and including defining an export of functionality that isn't a supported +// intrinsic on that platform). +// + +EXTERN_C void * __cdecl _alloca(size_t); +#pragma intrinsic(_alloca) + +REDHAWK_PALIMPORT _Ret_maybenull_ _Post_writable_byte_size_(size) void* REDHAWK_PALAPI PalVirtualAlloc(_In_opt_ void* pAddress, uintptr_t size, uint32_t allocationType, uint32_t protect); +REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalVirtualFree(_In_ void* pAddress, uintptr_t size, uint32_t freeType); +REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalVirtualProtect(_In_ void* pAddress, uintptr_t size, uint32_t protect); +REDHAWK_PALIMPORT void REDHAWK_PALAPI PalSleep(uint32_t milliseconds); +REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalSwitchToThread(); +REDHAWK_PALIMPORT HANDLE REDHAWK_PALAPI PalCreateEventW(_In_opt_ LPSECURITY_ATTRIBUTES pEventAttributes, UInt32_BOOL manualReset, UInt32_BOOL initialState, _In_opt_z_ LPCWSTR pName); +REDHAWK_PALIMPORT uint64_t REDHAWK_PALAPI PalGetTickCount64(); +REDHAWK_PALIMPORT void REDHAWK_PALAPI PalTerminateCurrentProcess(uint32_t exitCode); +REDHAWK_PALIMPORT HANDLE REDHAWK_PALAPI PalGetModuleHandleFromPointer(_In_ void* pointer); + +#ifdef TARGET_UNIX +REDHAWK_PALIMPORT void REDHAWK_PALAPI PalSetHardwareExceptionHandler(PHARDWARE_EXCEPTION_HANDLER handler); +#else +REDHAWK_PALIMPORT void* REDHAWK_PALAPI PalAddVectoredExceptionHandler(uint32_t firstHandler, _In_ PVECTORED_EXCEPTION_HANDLER vectoredHandler); +#endif + +typedef uint32_t (__stdcall *BackgroundCallback)(_In_opt_ void* pCallbackContext); +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalStartBackgroundGCThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext); +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalStartFinalizerThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext); + +typedef UInt32_BOOL (*PalHijackCallback)(HANDLE hThread, _In_ PAL_LIMITED_CONTEXT* pThreadContext, _In_opt_ void* pCallbackContext); +REDHAWK_PALIMPORT uint32_t REDHAWK_PALAPI PalHijack(HANDLE hThread, _In_ PalHijackCallback callback, _In_opt_ void* pCallbackContext); + +#ifdef FEATURE_ETW +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalEventEnabled(REGHANDLE regHandle, _In_ const EVENT_DESCRIPTOR* eventDescriptor); +#endif + +REDHAWK_PALIMPORT _Ret_maybenull_ void* REDHAWK_PALAPI PalSetWerDataBuffer(_In_ void* pNewBuffer); + +REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalAllocateThunksFromTemplate(_In_ HANDLE hTemplateModule, uint32_t templateRva, size_t templateSize, _Outptr_result_bytebuffer_(templateSize) void** newThunksOut); +REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalFreeThunksFromTemplate(_In_ void *pBaseAddress); + +REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalMarkThunksAsValidCallTargets( + void *virtualAddress, + int thunkSize, + int thunksPerBlock, + int thunkBlockSize, + int thunkBlocksPerMapping); + +REDHAWK_PALIMPORT uint32_t REDHAWK_PALAPI PalCompatibleWaitAny(UInt32_BOOL alertable, uint32_t timeout, uint32_t count, HANDLE* pHandles, UInt32_BOOL allowReentrantWait); + +REDHAWK_PALIMPORT void REDHAWK_PALAPI PalAttachThread(void* thread); +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalDetachThread(void* thread); + +REDHAWK_PALIMPORT uint64_t PalGetCurrentThreadIdForLogging(); + +REDHAWK_PALIMPORT void PalPrintFatalError(const char* message); + +#ifdef TARGET_UNIX +REDHAWK_PALIMPORT int32_t __cdecl _stricmp(const char *string1, const char *string2); +#endif // TARGET_UNIX + +#ifdef UNICODE +#define _tcsicmp _wcsicmp +#define _tcscat wcscat +#define _tcslen wcslen +#else +#define _tcsicmp _stricmp +#define _tcscat strcat +#define _tcslen strlen +#endif + +#if defined(HOST_X86) || defined(HOST_AMD64) + +#ifdef TARGET_UNIX +// MSVC directly defines intrinsics for __cpuid and __cpuidex matching the below signatures +// We define matching signatures for use on Unix platforms. +REDHAWK_PALIMPORT void __cpuid(int cpuInfo[4], int function_id); +REDHAWK_PALIMPORT void __cpuidex(int cpuInfo[4], int function_id, int subFunction_id); +#else +#include +#endif + +REDHAWK_PALIMPORT uint32_t REDHAWK_PALAPI xmmYmmStateSupport(); +REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalIsAvxEnabled(); + +#endif // defined(HOST_X86) || defined(HOST_AMD64) + +#if defined(HOST_ARM64) +REDHAWK_PALIMPORT void REDHAWK_PALAPI PAL_GetCpuCapabilityFlags(int* flags); +#endif //defined(HOST_ARM64) + +#include "PalRedhawkInline.h" + +#endif // !PAL_REDHAWK_INCLUDED diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawkCommon.h b/src/coreclr/nativeaot/Runtime/PalRedhawkCommon.h new file mode 100644 index 00000000000000..29896b7f53ba8e --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/PalRedhawkCommon.h @@ -0,0 +1,164 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Provide common definitions between the Redhawk and the Redhawk PAL implementation. This header file is used +// (rather than PalRedhawk.h) since the PAL implementation is built in a different environment than Redhawk +// code. For instance both environments may provide a definition of various common macros such as NULL. +// +// This header contains only environment neutral definitions (i.e. using only base C++ types and compositions +// of those types) and can thus be included from either environment without issue. +// + +#ifndef __PAL_REDHAWK_COMMON_INCLUDED +#define __PAL_REDHAWK_COMMON_INCLUDED + +#include "rhassert.h" + +#ifndef DECLSPEC_ALIGN +#ifdef _MSC_VER +#define DECLSPEC_ALIGN(x) __declspec(align(x)) +#else +#define DECLSPEC_ALIGN(x) __attribute__((aligned(x))) +#endif +#endif // DECLSPEC_ALIGN + +#ifdef HOST_AMD64 +#define AMD64_ALIGN_16 DECLSPEC_ALIGN(16) +#else // HOST_AMD64 +#define AMD64_ALIGN_16 +#endif // HOST_AMD64 + +struct AMD64_ALIGN_16 Fp128 { + uint64_t Low; + int64_t High; +}; + + +struct PAL_LIMITED_CONTEXT +{ + // Includes special registers, callee saved registers and general purpose registers used to return values from functions (not floating point return registers) +#ifdef TARGET_ARM + uintptr_t R0; + uintptr_t R4; + uintptr_t R5; + uintptr_t R6; + uintptr_t R7; + uintptr_t R8; + uintptr_t R9; + uintptr_t R10; + uintptr_t R11; + + uintptr_t IP; + uintptr_t SP; + uintptr_t LR; + + uint64_t D[16-8]; // D8 .. D15 registers (D16 .. D31 are volatile according to the ABI spec) + + uintptr_t GetIp() const { return IP; } + uintptr_t GetSp() const { return SP; } + uintptr_t GetFp() const { return R7; } + uintptr_t GetLr() const { return LR; } + void SetIp(uintptr_t ip) { IP = ip; } + void SetSp(uintptr_t sp) { SP = sp; } +#elif defined(TARGET_ARM64) + uintptr_t FP; + uintptr_t LR; + + uintptr_t X0; + uintptr_t X1; + uintptr_t X19; + uintptr_t X20; + uintptr_t X21; + uintptr_t X22; + uintptr_t X23; + uintptr_t X24; + uintptr_t X25; + uintptr_t X26; + uintptr_t X27; + uintptr_t X28; + + uintptr_t SP; + uintptr_t IP; + + uint64_t D[16 - 8]; // Only the bottom 64-bit value of the V registers V8..V15 needs to be preserved + // (V0-V7 and V16-V31 are not preserved according to the ABI spec). + + + uintptr_t GetIp() const { return IP; } + uintptr_t GetSp() const { return SP; } + uintptr_t GetFp() const { return FP; } + uintptr_t GetLr() const { return LR; } + void SetIp(uintptr_t ip) { IP = ip; } + void SetSp(uintptr_t sp) { SP = sp; } +#elif defined(UNIX_AMD64_ABI) + // Param regs: rdi, rsi, rdx, rcx, r8, r9, scratch: rax, rdx (both return val), preserved: rbp, rbx, r12-r15 + uintptr_t IP; + uintptr_t Rsp; + uintptr_t Rbp; + uintptr_t Rax; + uintptr_t Rbx; + uintptr_t Rdx; + uintptr_t R12; + uintptr_t R13; + uintptr_t R14; + uintptr_t R15; + + uintptr_t GetIp() const { return IP; } + uintptr_t GetSp() const { return Rsp; } + void SetIp(uintptr_t ip) { IP = ip; } + void SetSp(uintptr_t sp) { Rsp = sp; } + uintptr_t GetFp() const { return Rbp; } +#elif defined(TARGET_X86) || defined(TARGET_AMD64) + uintptr_t IP; + uintptr_t Rsp; + uintptr_t Rbp; + uintptr_t Rdi; + uintptr_t Rsi; + uintptr_t Rax; + uintptr_t Rbx; +#ifdef TARGET_AMD64 + uintptr_t R12; + uintptr_t R13; + uintptr_t R14; + uintptr_t R15; + uintptr_t __explicit_padding__; + Fp128 Xmm6; + Fp128 Xmm7; + Fp128 Xmm8; + Fp128 Xmm9; + Fp128 Xmm10; + Fp128 Xmm11; + Fp128 Xmm12; + Fp128 Xmm13; + Fp128 Xmm14; + Fp128 Xmm15; +#endif // TARGET_AMD64 + + uintptr_t GetIp() const { return IP; } + uintptr_t GetSp() const { return Rsp; } + uintptr_t GetFp() const { return Rbp; } + void SetIp(uintptr_t ip) { IP = ip; } + void SetSp(uintptr_t sp) { Rsp = sp; } +#else // TARGET_ARM + uintptr_t IP; + + uintptr_t GetIp() const { PORTABILITY_ASSERT("GetIp"); return 0; } + uintptr_t GetSp() const { PORTABILITY_ASSERT("GetSp"); return 0; } + uintptr_t GetFp() const { PORTABILITY_ASSERT("GetFp"); return 0; } + void SetIp(uintptr_t ip) { PORTABILITY_ASSERT("SetIp"); } + void SetSp(uintptr_t sp) { PORTABILITY_ASSERT("GetSp"); } +#endif // TARGET_ARM +}; + +void RuntimeThreadShutdown(void* thread); + +typedef void (*ThreadExitCallback)(); + +extern ThreadExitCallback g_threadExitCallback; + +#ifdef TARGET_UNIX +typedef int32_t (*PHARDWARE_EXCEPTION_HANDLER)(uintptr_t faultCode, uintptr_t faultAddress, PAL_LIMITED_CONTEXT* palContext, uintptr_t* arg0Reg, uintptr_t* arg1Reg); +#endif + +#endif // __PAL_REDHAWK_COMMON_INCLUDED diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawkFunctions.h b/src/coreclr/nativeaot/Runtime/PalRedhawkFunctions.h new file mode 100644 index 00000000000000..ff58a8ea0dc08d --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/PalRedhawkFunctions.h @@ -0,0 +1,174 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +extern "C" uint16_t __stdcall CaptureStackBackTrace(uint32_t, uint32_t, void*, uint32_t*); +inline uint16_t PalCaptureStackBackTrace(uint32_t arg1, uint32_t arg2, void* arg3, uint32_t* arg4) +{ + return CaptureStackBackTrace(arg1, arg2, arg3, arg4); +} + +extern "C" UInt32_BOOL __stdcall CloseHandle(HANDLE); +inline UInt32_BOOL PalCloseHandle(HANDLE arg1) +{ + return CloseHandle(arg1); +} + +extern "C" UInt32_BOOL __stdcall CreateDirectoryW(LPCWSTR, LPSECURITY_ATTRIBUTES); +inline UInt32_BOOL PalCreateDirectoryW(LPCWSTR arg1, LPSECURITY_ATTRIBUTES arg2) +{ + return CreateDirectoryW(arg1, arg2); +} + +extern "C" void __stdcall DeleteCriticalSection(CRITICAL_SECTION *); +inline void PalDeleteCriticalSection(CRITICAL_SECTION * arg1) +{ + DeleteCriticalSection(arg1); +} + +extern "C" UInt32_BOOL __stdcall DuplicateHandle(HANDLE, HANDLE, HANDLE, HANDLE *, uint32_t, UInt32_BOOL, uint32_t); +inline UInt32_BOOL PalDuplicateHandle(HANDLE arg1, HANDLE arg2, HANDLE arg3, HANDLE * arg4, uint32_t arg5, UInt32_BOOL arg6, uint32_t arg7) +{ + return DuplicateHandle(arg1, arg2, arg3, arg4, arg5, arg6, arg7); +} + +extern "C" void __stdcall EnterCriticalSection(CRITICAL_SECTION *); +inline void PalEnterCriticalSection(CRITICAL_SECTION * arg1) +{ + EnterCriticalSection(arg1); +} + +extern "C" uint32_t __stdcall EventRegister(const GUID *, void *, void *, REGHANDLE *); +inline uint32_t PalEventRegister(const GUID * arg1, void * arg2, void * arg3, REGHANDLE * arg4) +{ + return EventRegister(arg1, arg2, arg3, arg4); +} + +extern "C" uint32_t __stdcall EventUnregister(REGHANDLE); +inline uint32_t PalEventUnregister(REGHANDLE arg1) +{ + return EventUnregister(arg1); +} + +extern "C" uint32_t __stdcall EventWrite(REGHANDLE, const EVENT_DESCRIPTOR *, uint32_t, EVENT_DATA_DESCRIPTOR *); +inline uint32_t PalEventWrite(REGHANDLE arg1, const EVENT_DESCRIPTOR * arg2, uint32_t arg3, EVENT_DATA_DESCRIPTOR * arg4) +{ + return EventWrite(arg1, arg2, arg3, arg4); +} + +extern "C" void __stdcall FlushProcessWriteBuffers(); +inline void PalFlushProcessWriteBuffers() +{ + FlushProcessWriteBuffers(); +} + +extern "C" HANDLE __stdcall GetCurrentProcess(); +inline HANDLE PalGetCurrentProcess() +{ + return GetCurrentProcess(); +} + +extern "C" uint32_t __stdcall GetCurrentProcessId(); +inline uint32_t PalGetCurrentProcessId() +{ + return GetCurrentProcessId(); +} + +extern "C" HANDLE __stdcall GetCurrentThread(); +inline HANDLE PalGetCurrentThread() +{ + return GetCurrentThread(); +} + +#ifdef UNICODE +extern "C" uint32_t __stdcall GetEnvironmentVariableW(__in_z_opt LPCWSTR, __out_z_opt LPWSTR, uint32_t); +inline uint32_t PalGetEnvironmentVariable(__in_z_opt LPCWSTR arg1, __out_z_opt LPWSTR arg2, uint32_t arg3) +{ + return GetEnvironmentVariableW(arg1, arg2, arg3); +} +#else +extern "C" uint32_t __stdcall GetEnvironmentVariableA(__in_z_opt LPCSTR, __out_z_opt LPSTR, uint32_t); +inline uint32_t PalGetEnvironmentVariable(__in_z_opt LPCSTR arg1, __out_z_opt LPSTR arg2, uint32_t arg3) +{ + return GetEnvironmentVariableA(arg1, arg2, arg3); +} +#endif + +extern "C" void * __stdcall GetProcAddress(HANDLE, const char *); +inline void * PalGetProcAddress(HANDLE arg1, const char * arg2) +{ + return GetProcAddress(arg1, arg2); +} + +extern "C" UInt32_BOOL __stdcall InitializeCriticalSectionEx(CRITICAL_SECTION *, uint32_t, uint32_t); +inline UInt32_BOOL PalInitializeCriticalSectionEx(CRITICAL_SECTION * arg1, uint32_t arg2, uint32_t arg3) +{ + return InitializeCriticalSectionEx(arg1, arg2, arg3); +} + +extern "C" UInt32_BOOL __stdcall IsDebuggerPresent(); +inline UInt32_BOOL PalIsDebuggerPresent() +{ + return IsDebuggerPresent(); +} + +extern "C" void __stdcall LeaveCriticalSection(CRITICAL_SECTION *); +inline void PalLeaveCriticalSection(CRITICAL_SECTION * arg1) +{ + LeaveCriticalSection(arg1); +} + +extern "C" HANDLE __stdcall LoadLibraryExW(const WCHAR *, HANDLE, uint32_t); +inline HANDLE PalLoadLibraryExW(const WCHAR * arg1, HANDLE arg2, uint32_t arg3) +{ + return LoadLibraryExW(arg1, arg2, arg3); +} + +extern "C" UInt32_BOOL __stdcall QueryPerformanceCounter(LARGE_INTEGER *); +inline UInt32_BOOL PalQueryPerformanceCounter(LARGE_INTEGER * arg1) +{ + return QueryPerformanceCounter(arg1); +} + +extern "C" UInt32_BOOL __stdcall QueryPerformanceFrequency(LARGE_INTEGER *); +inline UInt32_BOOL PalQueryPerformanceFrequency(LARGE_INTEGER * arg1) +{ + return QueryPerformanceFrequency(arg1); +} + +extern "C" void __stdcall RaiseException(uint32_t, uint32_t, uint32_t, const uint32_t *); +inline void PalRaiseException(uint32_t arg1, uint32_t arg2, uint32_t arg3, const uint32_t * arg4) +{ + RaiseException(arg1, arg2, arg3, arg4); +} + +extern "C" UInt32_BOOL __stdcall ResetEvent(HANDLE); +inline UInt32_BOOL PalResetEvent(HANDLE arg1) +{ + return ResetEvent(arg1); +} + +extern "C" UInt32_BOOL __stdcall SetEvent(HANDLE); +inline UInt32_BOOL PalSetEvent(HANDLE arg1) +{ + return SetEvent(arg1); +} + +extern "C" uint32_t __stdcall WaitForSingleObjectEx(HANDLE, uint32_t, UInt32_BOOL); +inline uint32_t PalWaitForSingleObjectEx(HANDLE arg1, uint32_t arg2, UInt32_BOOL arg3) +{ + return WaitForSingleObjectEx(arg1, arg2, arg3); +} + +#ifdef PAL_REDHAWK_INCLUDED +extern "C" void __stdcall GetSystemTimeAsFileTime(FILETIME *); +inline void PalGetSystemTimeAsFileTime(FILETIME * arg1) +{ + GetSystemTimeAsFileTime(arg1); +} + +extern "C" void __stdcall RaiseFailFastException(PEXCEPTION_RECORD, PCONTEXT, uint32_t); +inline void PalRaiseFailFastException(PEXCEPTION_RECORD arg1, PCONTEXT arg2, uint32_t arg3) +{ + RaiseFailFastException(arg1, arg2, arg3); +} +#endif diff --git a/src/coreclr/nativeaot/Runtime/Portable/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/Portable/CMakeLists.txt new file mode 100644 index 00000000000000..ea7b5a348eb233 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/Portable/CMakeLists.txt @@ -0,0 +1,31 @@ +project(PortableRuntime) + +# Portable version of the runtime is designed to be used with CppCodeGen or WASM. +# It should be written in pure C/C++, with no assembly code. + +add_definitions(-DUSE_PORTABLE_HELPERS) + +add_library(PortableRuntime STATIC ${COMMON_RUNTIME_SOURCES} ${PORTABLE_RUNTIME_SOURCES}) + +# Get the current list of definitions +get_compile_definitions(DEFINITIONS) +set(ASM_OFFSETS_CSPP ${RUNTIME_DIR}/../Runtime.Base/src/AsmOffsets.cspp) + +if(WIN32) + set(COMPILER_LANGUAGE "") + set(PREPROCESSOR_FLAGS -EP) + set(ASM_OFFSETS_CPP ${RUNTIME_DIR}/windows/AsmOffsets.cpp) +else() + set(COMPILER_LANGUAGE -x c++) + set(PREPROCESSOR_FLAGS -E -P) + set(ASM_OFFSETS_CPP ${RUNTIME_DIR}/unix/AsmOffsets.cpp) +endif() + +add_custom_command( + # The AsmOffsetsPortable.cs is consumed later by the managed build + TARGET PortableRuntime + COMMAND ${CMAKE_CXX_COMPILER} ${COMPILER_LANGUAGE} ${DEFINITIONS} ${PREPROCESSOR_FLAGS} -I"${ARCH_SOURCES_DIR}" "${ASM_OFFSETS_CSPP}" >"${CMAKE_CURRENT_BINARY_DIR}/AsmOffsetsPortable.cs" + DEPENDS "${RUNTIME_DIR}/AsmOffsets.cpp" "${RUNTIME_DIR}/AsmOffsets.h" +) + +install_static_library(PortableRuntime aotsdk nativeaot) diff --git a/src/coreclr/nativeaot/Runtime/RWLock.cpp b/src/coreclr/nativeaot/Runtime/RWLock.cpp new file mode 100644 index 00000000000000..0ae19e96ebd276 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/RWLock.cpp @@ -0,0 +1,267 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// RWLock.cpp -- adapted from CLR SimpleRWLock.cpp +// +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "gcrhinterface.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "holder.h" +#include "Crst.h" +#include "event.h" +#include "RWLock.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "RuntimeInstance.h" +#include "yieldprocessornormalized.h" + +// Configurable constants used across our spin locks +// Initialization here is necessary so that we have meaningful values before the runtime is started +// These initial values were selected to match the defaults, but anything reasonable is close enough +struct SpinConstants +{ + uint32_t uInitialDuration; + uint32_t uMaximumDuration; + uint32_t uBackoffFactor; + uint32_t uRepetitions; +} g_SpinConstants = { + 50, // dwInitialDuration + 40000, // dwMaximumDuration - ideally (20000 * max(2, numProc)) + 3, // dwBackoffFactor + 10 // dwRepetitions +}; + +ReaderWriterLock::ReadHolder::ReadHolder(ReaderWriterLock * pLock, bool fAcquireLock) : + m_pLock(pLock) +{ +#ifndef DACCESS_COMPILE + m_fLockAcquired = fAcquireLock; + if (fAcquireLock) + m_pLock->AcquireReadLock(); +#else + UNREFERENCED_PARAMETER(fAcquireLock); +#endif // !DACCESS_COMPILE +} + +ReaderWriterLock::ReadHolder::~ReadHolder() +{ +#ifndef DACCESS_COMPILE + if (m_fLockAcquired) + m_pLock->ReleaseReadLock(); +#endif // !DACCESS_COMPILE +} + +ReaderWriterLock::WriteHolder::WriteHolder(ReaderWriterLock * pLock, bool fAcquireLock) : + m_pLock(pLock) +{ +#ifndef DACCESS_COMPILE + m_fLockAcquired = fAcquireLock; + if (fAcquireLock) + m_pLock->AcquireWriteLock(); +#else + UNREFERENCED_PARAMETER(fAcquireLock); +#endif // !DACCESS_COMPILE +} + +ReaderWriterLock::WriteHolder::~WriteHolder() +{ +#ifndef DACCESS_COMPILE + if (m_fLockAcquired) + m_pLock->ReleaseWriteLock(); +#endif // !DACCESS_COMPILE +} + +ReaderWriterLock::ReaderWriterLock(bool fBlockOnGc) : + m_RWLock(0) +#if 0 + , m_WriterWaiting(false) +#endif +{ + m_spinCount = ( +#ifndef DACCESS_COMPILE + (PalGetProcessCpuCount() == 1) ? 0 : +#endif + 4000); + m_fBlockOnGc = fBlockOnGc; +} + + +#ifndef DACCESS_COMPILE + +// Attempt to take the read lock, but do not wait if a writer has the lock. +// Release the lock if successfully acquired. Returns true if the lock was +// taken and released. Returns false if a writer had the lock. +// +// BEWARE: Because this method returns after releasing the lock, you can't +// infer the state of the lock based on the return value. This is currently +// only used to detect if a suspended thread owns the write lock to prevent +// deadlock with the Hijack logic during GC suspension. +// +bool ReaderWriterLock::DangerousTryPulseReadLock() +{ + if (TryAcquireReadLock()) + { + ReleaseReadLock(); + return true; + } + return false; +} + +bool ReaderWriterLock::TryAcquireReadLock() +{ + int32_t RWLock; + + do + { + RWLock = m_RWLock; + if (RWLock == -1) + return false; + ASSERT(RWLock >= 0); + } + while (RWLock != PalInterlockedCompareExchange(&m_RWLock, RWLock+1, RWLock)); + + return true; +} + +void ReaderWriterLock::AcquireReadLock() +{ + if (TryAcquireReadLock()) + return; + + AcquireReadLockWorker(); +} + +void ReaderWriterLock::AcquireReadLockWorker() +{ + uint32_t uSwitchCount = 0; + + for (;;) + { +#if 0 + // @TODO: Validate that we never re-enter the reader lock from a thread that + // already holds it. This scenario will deadlock if there are outstanding + // writers. + + // prevent writers from being starved. This assumes that writers are rare and + // dont hold the lock for a long time. + while (m_WriterWaiting) + { + int32_t spinCount = m_spinCount; + while (spinCount > 0) { + spinCount--; + PalYieldProcessor(); + } + __SwitchToThread(0, ++uSwitchCount); + } +#endif + + if (TryAcquireReadLock()) + return; + + uint32_t uDelay = g_SpinConstants.uInitialDuration; + do + { + if (TryAcquireReadLock()) + return; + + if (g_RhNumberOfProcessors <= 1) + break; + + // Delay by approximately 2*i clock cycles (Pentium III). + YieldProcessorNormalizedForPreSkylakeCount(uDelay); + + // exponential backoff: wait a factor longer in the next iteration + uDelay *= g_SpinConstants.uBackoffFactor; + } + while (uDelay < g_SpinConstants.uMaximumDuration); + + __SwitchToThread(0, ++uSwitchCount); + } +} + +void ReaderWriterLock::ReleaseReadLock() +{ + int32_t RWLock; + RWLock = PalInterlockedDecrement(&m_RWLock); + ASSERT(RWLock >= 0); +} + + +bool ReaderWriterLock::TryAcquireWriteLock() +{ + int32_t RWLock = PalInterlockedCompareExchange(&m_RWLock, -1, 0); + + ASSERT(RWLock >= 0 || RWLock == -1); + + if (RWLock) + return false; + +#if 0 + m_WriterWaiting = false; +#endif + + return true; +} + +void ReaderWriterLock::AcquireWriteLock() +{ + uint32_t uSwitchCount = 0; + + for (;;) + { + if (TryAcquireWriteLock()) + return; + +#if 0 + // Set the writer waiting word, if not already set, to notify potential readers to wait. + m_WriterWaiting = true; +#endif + + uint32_t uDelay = g_SpinConstants.uInitialDuration; + do + { + if (TryAcquireWriteLock()) + return; + + // Do not spin if GC is in progress because the lock will not + // be released until GC is finished. + if (m_fBlockOnGc && ThreadStore::IsTrapThreadsRequested()) + { + RedhawkGCInterface::WaitForGCCompletion(); + } + + if (g_RhNumberOfProcessors <= 1) + { + break; + } + + // Delay by approximately 2*i clock cycles (Pentium III). + YieldProcessorNormalizedForPreSkylakeCount(uDelay); + + // exponential backoff: wait a factor longer in the next iteration + uDelay *= g_SpinConstants.uBackoffFactor; + } + while (uDelay < g_SpinConstants.uMaximumDuration); + + __SwitchToThread(0, ++uSwitchCount); + } +} + +void ReaderWriterLock::ReleaseWriteLock() +{ + int32_t RWLock; + RWLock = PalInterlockedExchange(&m_RWLock, 0); + ASSERT(RWLock == -1); +} +#endif // DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/RWLock.h b/src/coreclr/nativeaot/Runtime/RWLock.h new file mode 100644 index 00000000000000..9115b03388c412 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/RWLock.h @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __RWLock_h__ +#define __RWLock_h__ + +class ReaderWriterLock +{ + volatile int32_t m_RWLock; // lock used for R/W synchronization + int32_t m_spinCount; // spin count for a reader waiting for a writer to release the lock + bool m_fBlockOnGc; // True if the spinning writers should block when GC is in progress + + +#if 0 + // used to prevent writers from being starved by readers + // we currently do not prevent writers from starving readers since writers + // are supposed to be rare. + bool m_WriterWaiting; +#endif + + bool TryAcquireReadLock(); + bool TryAcquireWriteLock(); + +public: + class ReadHolder + { + ReaderWriterLock * m_pLock; + bool m_fLockAcquired; + public: + ReadHolder(ReaderWriterLock * pLock, bool fAcquireLock = true); + ~ReadHolder(); + }; + + class WriteHolder + { + ReaderWriterLock * m_pLock; + bool m_fLockAcquired; + public: + WriteHolder(ReaderWriterLock * pLock, bool fAcquireLock = true); + ~WriteHolder(); + }; + + ReaderWriterLock(bool fBlockOnGc = false); + + void AcquireReadLock(); + void ReleaseReadLock(); + + bool DangerousTryPulseReadLock(); + +protected: + void AcquireWriteLock(); + void ReleaseWriteLock(); + + void AcquireReadLockWorker(); + +}; + +#endif // __RWLock_h__ diff --git a/src/coreclr/nativeaot/Runtime/Range.h b/src/coreclr/nativeaot/Runtime/Range.h new file mode 100644 index 00000000000000..0ced61e9e190ab --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/Range.h @@ -0,0 +1,137 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#pragma once + +namespace rh { namespace util +{ + //--------------------------------------------------------------------------------------------- + // Represents value range [a,b), and provides various convenience methods. + + template + class Range + { + typedef Range THIS_T; + + public: + //----------------------------------------------------------------------------------------- + // Construction + + Range() + : m_start(0), + m_end(0) + {} + + Range(Range const & range) + : m_start(range.m_start), + m_end(range.m_end) + {} + + static Range CreateWithEndpoint(VALUE_TYPE start, + VALUE_TYPE end) + { return Range(start, end); } + + static Range CreateWithLength(VALUE_TYPE start, LENGTH_TYPE len) + { return Range(start, start + len); } + + //----------------------------------------------------------------------------------------- + // Operations + + THIS_T& operator=(THIS_T const & range) + { m_start = range.m_start; m_end = range.m_end; return *this; } + + bool Equals(THIS_T const & range) const + { return GetStart() == range.GetStart() && GetEnd() == range.GetEnd(); } + + bool operator==(THIS_T const & range) const + { return Equals(range); } + + bool operator!=(THIS_T const & range) const + { return !Equals(range); } + + VALUE_TYPE GetStart() const + { return m_start; } + + VALUE_TYPE GetEnd() const + { return m_end; } + + LENGTH_TYPE GetLength() const + { return m_end - m_start; } + + bool IntersectsWith(THIS_T const &range) const + { return range.GetStart() < GetEnd() && range.GetEnd() > GetStart(); } + + bool IntersectsWith(VALUE_TYPE start, + VALUE_TYPE end) const + { return IntersectsWith(THIS_T(start, end)); } + + bool Contains(THIS_T const &range) const + { return GetStart() <= range.GetStart() && range.GetEnd() <= GetEnd(); } + + bool IsAdjacentTo(THIS_T const &range) const + { return GetEnd() == range.GetStart() || range.GetEnd() == GetStart(); } + + protected: + Range(VALUE_TYPE start, VALUE_TYPE end) + : m_start(start), + m_end(end) + { ASSERT(start <= end); } + + VALUE_TYPE m_start; + VALUE_TYPE m_end; + }; + + //--------------------------------------------------------------------------------------------- + // Represents address range [a,b), and provides various convenience methods. + + class MemRange : public Range + { + typedef Range BASE_T; + + public: + //----------------------------------------------------------------------------------------- + // Construction + + MemRange() + : BASE_T() + {} + + MemRange(void* pvMemStart, + uintptr_t cbMemLen) + : BASE_T(reinterpret_cast(pvMemStart), reinterpret_cast(pvMemStart) + cbMemLen) + {} + + MemRange(void* pvMemStart, + void* pvMemEnd) + : BASE_T(reinterpret_cast(pvMemStart), reinterpret_cast(pvMemEnd)) + {} + + MemRange(MemRange const & range) + : BASE_T(range) + { } + + //----------------------------------------------------------------------------------------- + // Operations + + MemRange& operator=(MemRange const & range) + { BASE_T::operator=(range); return *this; } + + uintptr_t GetPageCount() const + { + uint8_t *pCurPage = ALIGN_DOWN(GetStart(), OS_PAGE_SIZE); + uint8_t *pEndPage = ALIGN_UP(GetEnd(), OS_PAGE_SIZE); + return (pEndPage - pCurPage) / OS_PAGE_SIZE; + } + + uint8_t* GetStartPage() const + { return ALIGN_DOWN(GetStart(), OS_PAGE_SIZE); } + + // The page immediately following the last page contained by this range. + uint8_t* GetEndPage() const + { return ALIGN_UP(GetEnd(), OS_PAGE_SIZE); } + + MemRange GetPageRange() const + { return MemRange(GetStartPage(), GetEndPage()); } + }; +}// namespace util +}// namespace rh + diff --git a/src/coreclr/nativeaot/Runtime/RedhawkWarnings.h b/src/coreclr/nativeaot/Runtime/RedhawkWarnings.h new file mode 100644 index 00000000000000..e3cc1118b5d8d3 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/RedhawkWarnings.h @@ -0,0 +1,8 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Disable some commonly ignored warnings +// + +MSVC_DISABLE_WARNING(4200) // nonstandard extension used : zero-sized array in struct/union diff --git a/src/coreclr/nativeaot/Runtime/RestrictedCallouts.cpp b/src/coreclr/nativeaot/Runtime/RestrictedCallouts.cpp new file mode 100644 index 00000000000000..bd661670d33c17 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/RestrictedCallouts.cpp @@ -0,0 +1,249 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Callouts from the unmanaged portion of the runtime to C# helpers made during garbage collections. See +// RestrictedCallouts.h for more detail. +// + +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "holder.h" +#include "gcrhinterface.h" +#include "shash.h" +#include "RWLock.h" +#include "rhbinder.h" +#include "Crst.h" +#include "RuntimeInstance.h" +#include "MethodTable.h" +#include "ObjectLayout.h" +#include "event.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "RestrictedCallouts.h" +#include "MethodTable.inl" + +// The head of the chains of GC callouts, one per callout type. +RestrictedCallouts::GcRestrictedCallout * RestrictedCallouts::s_rgGcRestrictedCallouts[GCRC_Count] = { 0 }; + +// The head of the chain of HandleTable callouts. +RestrictedCallouts::HandleTableRestrictedCallout * RestrictedCallouts::s_pHandleTableRestrictedCallouts = NULL; + +// Lock protecting access to s_rgGcRestrictedCallouts and s_pHandleTableRestrictedCallouts during registration +// and unregistration (not used during actual callbacks since everything is single threaded then). +CrstStatic RestrictedCallouts::s_sLock; + +// One time startup initialization. +bool RestrictedCallouts::Initialize() +{ + s_sLock.Init(CrstRestrictedCallouts, CRST_DEFAULT); + + return true; +} + +// Register callback of the given type to the method with the given address. The most recently registered +// callbacks are called first. Returns true on success, false if insufficient memory was available for the +// registration. +bool RestrictedCallouts::RegisterGcCallout(GcRestrictedCalloutKind eKind, void * pCalloutMethod) +{ + // Validate callout kind. + if (eKind >= GCRC_Count) + { + ASSERT_UNCONDITIONALLY("Invalid GC restricted callout kind."); + RhFailFast(); + } + + GcRestrictedCallout * pCallout = new (nothrow) GcRestrictedCallout(); + if (pCallout == NULL) + return false; + + pCallout->m_pCalloutMethod = pCalloutMethod; + + CrstHolder lh(&s_sLock); + + // Link new callout to head of the chain according to its type. + pCallout->m_pNext = s_rgGcRestrictedCallouts[eKind]; + s_rgGcRestrictedCallouts[eKind] = pCallout; + + return true; +} + +// Unregister a previously registered callout. Removes the first registration that matches on both callout +// kind and address. Causes a fail fast if the registration doesn't exist. +void RestrictedCallouts::UnregisterGcCallout(GcRestrictedCalloutKind eKind, void * pCalloutMethod) +{ + // Validate callout kind. + if (eKind >= GCRC_Count) + { + ASSERT_UNCONDITIONALLY("Invalid GC restricted callout kind."); + RhFailFast(); + } + + CrstHolder lh(&s_sLock); + + GcRestrictedCallout * pCurrCallout = s_rgGcRestrictedCallouts[eKind]; + GcRestrictedCallout * pPrevCallout = NULL; + + while (pCurrCallout) + { + if (pCurrCallout->m_pCalloutMethod == pCalloutMethod) + { + // Found a matching entry, remove it from the chain. + if (pPrevCallout) + pPrevCallout->m_pNext = pCurrCallout->m_pNext; + else + s_rgGcRestrictedCallouts[eKind] = pCurrCallout->m_pNext; + + delete pCurrCallout; + + return; + } + + pPrevCallout = pCurrCallout; + pCurrCallout = pCurrCallout->m_pNext; + } + + // If we get here we didn't find a matching registration, indicating a bug on the part of the caller. + ASSERT_UNCONDITIONALLY("Attempted to unregister restricted callout that wasn't registered."); + RhFailFast(); +} + +// Register callback for the "is alive" property of ref counted handles with objects of the given type (the +// type match must be exact). The most recently registered callbacks are called first. Returns true on +// success, false if insufficient memory was available for the registration. +bool RestrictedCallouts::RegisterRefCountedHandleCallback(void * pCalloutMethod, MethodTable * pTypeFilter) +{ + HandleTableRestrictedCallout * pCallout = new (nothrow) HandleTableRestrictedCallout(); + if (pCallout == NULL) + return false; + + pCallout->m_pCalloutMethod = pCalloutMethod; + pCallout->m_pTypeFilter = pTypeFilter; + + CrstHolder lh(&s_sLock); + + // Link new callout to head of the chain. + pCallout->m_pNext = s_pHandleTableRestrictedCallouts; + s_pHandleTableRestrictedCallouts = pCallout; + + return true; +} + +// Unregister a previously registered callout. Removes the first registration that matches on both callout +// address and filter type. Causes a fail fast if the registration doesn't exist. +void RestrictedCallouts::UnregisterRefCountedHandleCallback(void * pCalloutMethod, MethodTable * pTypeFilter) +{ + CrstHolder lh(&s_sLock); + + HandleTableRestrictedCallout * pCurrCallout = s_pHandleTableRestrictedCallouts; + HandleTableRestrictedCallout * pPrevCallout = NULL; + + while (pCurrCallout) + { + if ((pCurrCallout->m_pCalloutMethod == pCalloutMethod) && + (pCurrCallout->m_pTypeFilter == pTypeFilter)) + { + // Found a matching entry, remove it from the chain. + if (pPrevCallout) + pPrevCallout->m_pNext = pCurrCallout->m_pNext; + else + s_pHandleTableRestrictedCallouts = pCurrCallout->m_pNext; + + delete pCurrCallout; + + return; + } + + pPrevCallout = pCurrCallout; + pCurrCallout = pCurrCallout->m_pNext; + } + + // If we get here we didn't find a matching registration, indicating a bug on the part of the caller. + ASSERT_UNCONDITIONALLY("Attempted to unregister restricted callout that wasn't registered."); + RhFailFast(); +} + +// Invoke all the registered GC callouts of the given kind. The condemned generation of the current collection +// is passed along to the callouts. +void RestrictedCallouts::InvokeGcCallouts(GcRestrictedCalloutKind eKind, uint32_t uiCondemnedGeneration) +{ + ASSERT(eKind < GCRC_Count); + + // It is illegal for any of the callouts to trigger a GC. + Thread * pThread = ThreadStore::GetCurrentThread(); + pThread->SetDoNotTriggerGc(); + + // Due to the above we have better suppress GC stress. + bool fGcStressWasSuppressed = pThread->IsSuppressGcStressSet(); + if (!fGcStressWasSuppressed) + pThread->SetSuppressGcStress(); + + GcRestrictedCallout * pCurrCallout = s_rgGcRestrictedCallouts[eKind]; + while (pCurrCallout) + { + // Make the callout. + ((GcRestrictedCallbackFunction)pCurrCallout->m_pCalloutMethod)(uiCondemnedGeneration); + + pCurrCallout = pCurrCallout->m_pNext; + } + + // Revert GC stress mode if we changed it. + if (!fGcStressWasSuppressed) + pThread->ClearSuppressGcStress(); + + pThread->ClearDoNotTriggerGc(); +} + +// Invoke all the registered ref counted handle callouts for the given object extracted from the handle. The +// result is the union of the results for all the handlers that matched the object type (i.e. if one of them +// returned true the overall result is true otherwise false is returned (which includes the case where no +// handlers matched)). Since there should be no other side-effects of the callout, the invocations cease as +// soon as a handler returns true. +bool RestrictedCallouts::InvokeRefCountedHandleCallbacks(Object * pObject) +{ + bool fResult = false; + + // It is illegal for any of the callouts to trigger a GC. + Thread * pThread = ThreadStore::GetCurrentThread(); + pThread->SetDoNotTriggerGc(); + + // Due to the above we have better suppress GC stress. + bool fGcStressWasSuppressed = pThread->IsSuppressGcStressSet(); + if (!fGcStressWasSuppressed) + pThread->SetSuppressGcStress(); + + HandleTableRestrictedCallout * pCurrCallout = s_pHandleTableRestrictedCallouts; + while (pCurrCallout) + { + if (pObject->get_SafeEEType() == pCurrCallout->m_pTypeFilter) + { + // Make the callout. Return true to our caller as soon as we see a true result here. + if (((HandleTableRestrictedCallbackFunction)pCurrCallout->m_pCalloutMethod)(pObject)) + { + fResult = true; + goto Done; + } + } + + pCurrCallout = pCurrCallout->m_pNext; + } + + Done: + // Revert GC stress mode if we changed it. + if (!fGcStressWasSuppressed) + pThread->ClearSuppressGcStress(); + + pThread->ClearDoNotTriggerGc(); + + return fResult; +} diff --git a/src/coreclr/nativeaot/Runtime/RestrictedCallouts.h b/src/coreclr/nativeaot/Runtime/RestrictedCallouts.h new file mode 100644 index 00000000000000..3a70c7352960f4 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/RestrictedCallouts.h @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Restricted callouts refer to calls to classlib defined code written in C# made from the runtime during a +// garbage collection. As such these C# methods are constrained in what they can do and must be written very +// carefully. The most obvious restriction is that they cannot trigger a GC (by attempting to allocate memory +// for example) since that would lead to an immediate deadlock. +// +// Known constraints: +// * No triggering of GCs (new, boxing value types, foreach over a type that allocates for its IEnumerator, +// calling GC.Collect etc.). +// * No exceptions can leak out of the callout. +// * No blocking (or expensive) operations that could starve the GC or potentially lead to deadlocks. +// * No use of runtime facilities that check whether a GC is in progress, these will deadlock. The big +// example we know about so far is making a p/invoke call. +// * For the AfterMarkPhase callout special attention must be paid to avoid any action that reads the MethodTable* +// from an object header (e.g. casting). At this point the GC may have mark bits set in the the pointer. +// + +class MethodTable; + +// Enum for the various GC callouts available. The values and their meanings are a contract with the classlib +// so be careful altering these. +enum GcRestrictedCalloutKind +{ + GCRC_StartCollection = 0, // Collection is about to begin + GCRC_EndCollection = 1, // Collection has completed + GCRC_AfterMarkPhase = 2, // All live objects are marked (not including ready for finalization + // objects), no handles have been cleared + GCRC_Count // Maximum number of callout types +}; + +class RestrictedCallouts +{ +public: + // One time startup initialization. + static bool Initialize(); + + // Register callback of the given type to the method with the given address. The most recently registered + // callbacks are called first. Returns true on success, false if insufficient memory was available for the + // registration. + static bool RegisterGcCallout(GcRestrictedCalloutKind eKind, void * pCalloutMethod); + + // Unregister a previously registered callout. Removes the first registration that matches on both callout + // kind and address. Causes a fail fast if the registration doesn't exist. + static void UnregisterGcCallout(GcRestrictedCalloutKind eKind, void * pCalloutMethod); + + // Register callback for the "is alive" property of ref counted handles with objects of the given type + // (the type match must be exact). The most recently registered callbacks are called first. Returns true + // on success, false if insufficient memory was available for the registration. + static bool RegisterRefCountedHandleCallback(void * pCalloutMethod, MethodTable * pTypeFilter); + + // Unregister a previously registered callout. Removes the first registration that matches on both callout + // address and filter type. Causes a fail fast if the registration doesn't exist. + static void UnregisterRefCountedHandleCallback(void * pCalloutMethod, MethodTable * pTypeFilter); + + // Invoke all the registered GC callouts of the given kind. The condemned generation of the current + // collection is passed along to the callouts. + static void InvokeGcCallouts(GcRestrictedCalloutKind eKind, uint32_t uiCondemnedGeneration); + + // Invoke all the registered ref counted handle callouts for the given object extracted from the handle. + // The result is the union of the results for all the handlers that matched the object type (i.e. if one + // of them returned true the overall result is true otherwise false is returned (which includes the case + // where no handlers matched)). Since there should be no other side-effects of the callout, the + // invocations cease as soon as a handler returns true. + static bool InvokeRefCountedHandleCallbacks(Object * pObject); + +private: + // Context struct used to record which GC callbacks are registered to be made (we allow multiple + // registrations). + struct GcRestrictedCallout + { + GcRestrictedCallout * m_pNext; // Next callout to make or NULL + void * m_pCalloutMethod; // Address of code to call + }; + + // The head of the chains of GC callouts, one per callout type. + static GcRestrictedCallout * s_rgGcRestrictedCallouts[GCRC_Count]; + + // The handle table only has one callout type, for ref-counted handles. But it allows the client to + // specify a type filter: i.e. only handles with an object of the exact type specified will have the + // callout invoked. + struct HandleTableRestrictedCallout + { + HandleTableRestrictedCallout * m_pNext; // Next callout to make or NULL + void * m_pCalloutMethod; // Address of code to call + MethodTable * m_pTypeFilter; // Type of object for which callout will be made + }; + + // The head of the chain of HandleTable callouts. + static HandleTableRestrictedCallout * s_pHandleTableRestrictedCallouts; + + // Lock protecting access to s_rgGcRestrictedCallouts and s_pHandleTableRestrictedCallouts during + // registration and unregistration (not used during actual callbacks since everything is single threaded + // then). + static CrstStatic s_sLock; + + // Prototypes for the callouts. + typedef void (REDHAWK_CALLCONV * GcRestrictedCallbackFunction)(uint32_t uiCondemnedGeneration); + typedef CLR_BOOL (REDHAWK_CALLCONV * HandleTableRestrictedCallbackFunction)(Object * pObject); +}; diff --git a/src/coreclr/nativeaot/Runtime/RhConfig.cpp b/src/coreclr/nativeaot/Runtime/RhConfig.cpp new file mode 100644 index 00000000000000..da618a85930e08 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/RhConfig.cpp @@ -0,0 +1,259 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#ifndef DACCESS_COMPILE +#include "CommonTypes.h" +#include "daccess.h" +#include "CommonMacros.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "gcrhinterface.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "holder.h" +#include "Crst.h" +#include "event.h" +#include "RWLock.h" +#include "threadstore.h" +#include "RuntimeInstance.h" +#include "shash.h" +#include "RhConfig.h" + +#include + +bool RhConfig::ReadConfigValue(_In_z_ const TCHAR *wszName, uint32_t* pValue, bool decimal) +{ + TCHAR wszBuffer[CONFIG_VAL_MAXLEN + 1]; // 8 hex digits plus a nul terminator. + const uint32_t cchBuffer = sizeof(wszBuffer) / sizeof(wszBuffer[0]); + + uint32_t cchResult = 0; + +#ifdef FEATURE_ENVIRONMENT_VARIABLE_CONFIG + TCHAR wszVariableName[64] = _T("DOTNET_"); + assert(_tcslen(wszVariableName) + _tcslen(wszName) < sizeof(wszVariableName) / sizeof(wszVariableName[0])); + _tcscat(wszVariableName, wszName); + cchResult = PalGetEnvironmentVariable(wszVariableName, wszBuffer, cchBuffer); +#endif // FEATURE_ENVIRONMENT_VARIABLE_CONFIG + +#ifdef FEATURE_EMBEDDED_CONFIG + // if the config key wasn't found in the ini file + if ((cchResult == 0) || (cchResult >= cchBuffer)) + cchResult = GetEmbeddedVariable(wszName, wszBuffer, cchBuffer); +#endif // FEATURE_EMBEDDED_CONFIG + + if ((cchResult == 0) || (cchResult >= cchBuffer)) + return false; // not found + + uint32_t uiResult = 0; + + for (uint32_t i = 0; i < cchResult; i++) + { + TCHAR ch = wszBuffer[i]; + + if (decimal) + { + uiResult *= 10; + + if ((ch >= _T('0')) && (ch <= _T('9'))) + uiResult += ch - _T('0'); + else + return false; // parse error + } + else + { + uiResult *= 16; + + if ((ch >= _T('0')) && (ch <= _T('9'))) + uiResult += ch - _T('0'); + else if ((ch >= _T('a')) && (ch <= _T('f'))) + uiResult += (ch - _T('a')) + 10; + else if ((ch >= _T('A')) && (ch <= _T('F'))) + uiResult += (ch - _T('A')) + 10; + else + return false; // parse error + } + } + + *pValue = uiResult; + return true; +} + +#ifdef FEATURE_EMBEDDED_CONFIG +uint32_t RhConfig::GetEmbeddedVariable(_In_z_ const TCHAR* configName, _Out_writes_all_(cchOutputBuffer) TCHAR* outputBuffer, _In_ uint32_t cchOutputBuffer) +{ + //the buffer needs to be big enough to read the value buffer + null terminator + if (cchOutputBuffer < CONFIG_VAL_MAXLEN + 1) + { + return 0; + } + + //if we haven't read the config yet try to read + if (g_embeddedSettings == NULL) + { + ReadEmbeddedSettings(); + } + + //if the config wasn't read or reading failed return 0 immediately + if (g_embeddedSettings == CONFIG_INI_NOT_AVAIL) + { + return 0; + } + + return GetConfigVariable(configName, (ConfigPair*)g_embeddedSettings, outputBuffer, cchOutputBuffer); +} +#endif // FEATURE_EMBEDDED_CONFIG + +uint32_t RhConfig::GetConfigVariable(_In_z_ const TCHAR* configName, const ConfigPair* configPairs, _Out_writes_all_(cchOutputBuffer) TCHAR* outputBuffer, _In_ uint32_t cchOutputBuffer) +{ + //find the first name which matches (case insensitive to be compat with environment variable counterpart) + for (int iSettings = 0; iSettings < RCV_Count; iSettings++) + { + if (_tcsicmp(configName, configPairs[iSettings].Key) == 0) + { + bool nullTerm = FALSE; + + uint32_t iValue; + + for (iValue = 0; (iValue < CONFIG_VAL_MAXLEN + 1) && (iValue < (int32_t)cchOutputBuffer); iValue++) + { + outputBuffer[iValue] = configPairs[iSettings].Value[iValue]; + + if (outputBuffer[iValue] == '\0') + { + nullTerm = true; + break; + } + } + + //return the length of the config value if null terminated else return zero + return nullTerm ? iValue : 0; + } + } + + //if the config key was not found return 0 + return 0; +} + +#ifdef FEATURE_EMBEDDED_CONFIG +struct CompilerEmbeddedSettingsBlob +{ + uint32_t Size; + char Data[1]; +}; + +extern "C" CompilerEmbeddedSettingsBlob g_compilerEmbeddedSettingsBlob; + +void RhConfig::ReadEmbeddedSettings() +{ + if (g_embeddedSettings == NULL) + { + //if reading the file contents failed set g_embeddedSettings to CONFIG_INI_NOT_AVAIL + if (g_compilerEmbeddedSettingsBlob.Size == 0) + { + //only set if another thread hasn't initialized the buffer yet, otherwise ignore and let the first setter win + PalInterlockedCompareExchangePointer(&g_embeddedSettings, CONFIG_INI_NOT_AVAIL, NULL); + + return; + } + + ConfigPair* iniBuff = new (nothrow) ConfigPair[RCV_Count]; + if (iniBuff == NULL) + { + //only set if another thread hasn't initialized the buffer yet, otherwise ignore and let the first setter win + PalInterlockedCompareExchangePointer(&g_embeddedSettings, CONFIG_INI_NOT_AVAIL, NULL); + + return; + } + + uint32_t iBuff = 0; + uint32_t iIniBuff = 0; + char* currLine; + + //while we haven't reached the max number of config pairs, or the end of the file, read the next line + while (iIniBuff < RCV_Count && iBuff < g_compilerEmbeddedSettingsBlob.Size) + { + currLine = &g_compilerEmbeddedSettingsBlob.Data[iBuff]; + + //find the end of the line + while ((g_compilerEmbeddedSettingsBlob.Data[iBuff] != '\0') && (iBuff < g_compilerEmbeddedSettingsBlob.Size)) + iBuff++; + + //parse the line + //only increment iIniBuff if the parsing succeeded otherwise reuse the config struct + if (ParseConfigLine(&iniBuff[iIniBuff], currLine)) + { + iIniBuff++; + } + + //advance to the next line; + iBuff++; + } + + //initialize the remaining config pairs to "\0" + while (iIniBuff < RCV_Count) + { + iniBuff[iIniBuff].Key[0] = '\0'; + iniBuff[iIniBuff].Value[0] = '\0'; + iIniBuff++; + } + + //if another thread initialized first let the first setter win + //delete the iniBuff to avoid leaking memory + if (PalInterlockedCompareExchangePointer(&g_embeddedSettings, iniBuff, NULL) != NULL) + { + delete[] iniBuff; + } + } + + return; +} +#endif // FEATURE_EMBEDDED_CONFIG + +//Parses one line of config and populates values in the passed in configPair +//returns: true if the parsing was successful, false if the parsing failed. +//NOTE: if the method fails configPair is left in an unitialized state +bool RhConfig::ParseConfigLine(_Out_ ConfigPair* configPair, _In_z_ const char * line) +{ + uint32_t iLine = 0; + uint32_t iKey = 0; + uint32_t iVal = 0; + + //while we haven't reached the end of the key signalled by '=', or the end of the line, or the key maxlen + while (line[iLine] != '=' && line[iLine] != '\0' && iKey < CONFIG_KEY_MAXLEN) + { + configPair->Key[iKey++] = line[iLine++]; + } + + //if the current char is not '=' we reached the key maxlen, or the line ended return false + if (line[iLine] != '=') + { + return FALSE; + } + + configPair->Key[iKey] = '\0'; + + //increment to start of the value + iLine++; + + //while we haven't reached the end of the line, or val maxlen + while (line[iLine] != '\0' && iVal < CONFIG_VAL_MAXLEN) + { + configPair->Value[iVal++] = line[iLine++]; + } + + //if the current char is not '\0' we didn't reach the end of the line return false + if (line[iLine] != '\0') + { + return FALSE; + } + + configPair->Value[iVal] = '\0'; + + return TRUE; +} + +#endif diff --git a/src/coreclr/nativeaot/Runtime/RhConfig.h b/src/coreclr/nativeaot/Runtime/RhConfig.h new file mode 100644 index 00000000000000..faa2ec158bf397 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/RhConfig.h @@ -0,0 +1,119 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Provides simple configuration support through environment variables. Each variable is lazily inspected on +// first query and the resulting value cached for future use. To keep things simple we support reading only +// 32-bit hex quantities and a zero value is considered equivalent to the environment variable not being +// defined. We can get more sophisticated if needs be, but the hope is that very few configuration values are +// exposed in this manner. +// +// Values can also be embedded in the compiled binary. +// + + +#ifndef DACCESS_COMPILE + +#define FEATURE_EMBEDDED_CONFIG +#define FEATURE_ENVIRONMENT_VARIABLE_CONFIG + +class RhConfig +{ + +#define CONFIG_INI_NOT_AVAIL (void*)0x1 //signal for ini file failed to load +#define CONFIG_KEY_MAXLEN 50 //arbitrary max length of config keys increase if needed +#define CONFIG_VAL_MAXLEN 8 //32 bit uint in hex + +private: + struct ConfigPair + { + public: + TCHAR Key[CONFIG_KEY_MAXLEN + 1]; //maxlen + null terminator + TCHAR Value[CONFIG_VAL_MAXLEN + 1]; //maxlen + null terminator + }; + +#ifdef FEATURE_EMBEDDED_CONFIG + // g_embeddedSettings is a buffer of ConfigPair structs embedded in the compiled binary. + // + //NOTE: g_embeddedSettings is only set in ReadEmbeddedSettings and must be set atomically only once + // using PalInterlockedCompareExchangePointer to avoid races when initializing + void* volatile g_embeddedSettings = NULL; +#endif // FEATURE_EMBEDDED_CONFIG + +public: + + bool ReadConfigValue(_In_z_ const TCHAR* wszName, uint32_t* pValue, bool decimal = false); + +#define DEFINE_VALUE_ACCESSOR(_name, defaultVal) \ + uint32_t Get##_name() \ + { \ + if (m_uiConfigValuesRead & (1 << RCV_##_name)) \ + return m_uiConfigValues[RCV_##_name]; \ + uint32_t uiValue; \ + m_uiConfigValues[RCV_##_name] = ReadConfigValue(_T(#_name), &uiValue) ? uiValue : defaultVal; \ + m_uiConfigValuesRead |= 1 << RCV_##_name; \ + return m_uiConfigValues[RCV_##_name]; \ + } + + +#ifdef _DEBUG +#define DEBUG_CONFIG_VALUE(_name) DEFINE_VALUE_ACCESSOR(_name, 0) +#define DEBUG_CONFIG_VALUE_WITH_DEFAULT(_name, defaultVal) DEFINE_VALUE_ACCESSOR(_name, defaultVal) +#else +#define DEBUG_CONFIG_VALUE(_name) +#define DEBUG_CONFIG_VALUE_WITH_DEFAULT(_name, defaultVal) +#endif +#define RETAIL_CONFIG_VALUE(_name) DEFINE_VALUE_ACCESSOR(_name, 0) +#define RETAIL_CONFIG_VALUE_WITH_DEFAULT(_name, defaultVal) DEFINE_VALUE_ACCESSOR(_name, defaultVal) +#include "RhConfigValues.h" +#undef DEBUG_CONFIG_VALUE +#undef RETAIL_CONFIG_VALUE +#undef DEBUG_CONFIG_VALUE_WITH_DEFAULT +#undef RETAIL_CONFIG_VALUE_WITH_DEFAULT + +private: + + enum RhConfigValue + { +#define DEBUG_CONFIG_VALUE(_name) RCV_##_name, +#define RETAIL_CONFIG_VALUE(_name) RCV_##_name, +#define DEBUG_CONFIG_VALUE_WITH_DEFAULT(_name, defaultVal) RCV_##_name, +#define RETAIL_CONFIG_VALUE_WITH_DEFAULT(_name, defaultVal) RCV_##_name, +#include "RhConfigValues.h" +#undef DEBUG_CONFIG_VALUE +#undef RETAIL_CONFIG_VALUE +#undef DEBUG_CONFIG_VALUE_WITH_DEFAULT +#undef RETAIL_CONFIG_VALUE_WITH_DEFAULT + RCV_Count + }; + +//accomidate for the maximum number of config values plus sizable buffer for whitespace 2K +#define CONFIG_FILE_MAXLEN RCV_Count * sizeof(ConfigPair) + 2000 + +private: + //Parses one line of config and populates values in the passed in configPair + //returns: true if the parsing was successful, false if the parsing failed. + //NOTE: if the method fails configPair is left in an unitialized state + bool ParseConfigLine(_Out_ ConfigPair* configPair, _In_z_ const char * line); + +#ifdef FEATURE_EMBEDDED_CONFIG + void ReadEmbeddedSettings(); + + uint32_t GetEmbeddedVariable(_In_z_ const TCHAR* configName, _Out_writes_all_(cchOutputBuffer) TCHAR* outputBuffer, _In_ uint32_t cchOutputBuffer); +#endif // FEATURE_EMBEDDED_CONFIG + + uint32_t GetConfigVariable(_In_z_ const TCHAR* configName, const ConfigPair* configPairs, _Out_writes_all_(cchOutputBuffer) TCHAR* outputBuffer, _In_ uint32_t cchOutputBuffer); + + static bool priv_isspace(char c) + { + return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'); + } + + + uint32_t m_uiConfigValuesRead; + uint32_t m_uiConfigValues[RCV_Count]; +}; + +extern RhConfig * g_pRhConfig; + +#endif //!DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/RhConfigValues.h b/src/coreclr/nativeaot/Runtime/RhConfigValues.h new file mode 100644 index 00000000000000..36ceafaeba06cf --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/RhConfigValues.h @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Definitions of each configuration value used by the RhConfig class. +// +// Each variable is lazily inspected on first query and the resulting value cached for future use. To keep +// things simple we support reading only 32-bit hex quantities and a zero value is considered equivalent to +// the environment variable not being defined. We can get more sophisticated if needs be, but the hope is that +// very few configuration values are exposed in this manner. +// + +// By default, print assert to console and break in the debugger, if attached. Set to 0 for a pop-up dialog on assert. +DEBUG_CONFIG_VALUE_WITH_DEFAULT(BreakOnAssert, 1) + +RETAIL_CONFIG_VALUE(StressLogLevel) +RETAIL_CONFIG_VALUE(TotalStressLogSize) +RETAIL_CONFIG_VALUE(gcServer) +DEBUG_CONFIG_VALUE(GcStressThrottleMode) // gcstm_TriggerAlways / gcstm_TriggerOnFirstHit / gcstm_TriggerRandom +DEBUG_CONFIG_VALUE(GcStressFreqCallsite) // Number of times to force GC out of GcStressFreqDenom (for GCSTM_RANDOM) +DEBUG_CONFIG_VALUE(GcStressFreqLoop) // Number of times to force GC out of GcStressFreqDenom (for GCSTM_RANDOM) +DEBUG_CONFIG_VALUE(GcStressFreqDenom) // Denominator defining frequencies above, 10,000 used when left unspecified (for GCSTM_RANDOM) +DEBUG_CONFIG_VALUE(GcStressSeed) // Specify Seed for random generator (for GCSTM_RANDOM) diff --git a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp new file mode 100644 index 00000000000000..8ce73e02c5bdff --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp @@ -0,0 +1,427 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "holder.h" +#include "Crst.h" +#include "rhbinder.h" +#include "RWLock.h" +#include "RuntimeInstance.h" +#include "event.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "gcrhinterface.h" +#include "shash.h" +#include "TypeManager.h" +#include "MethodTable.h" +#include "varint.h" + +#include "CommonMacros.inl" +#include "slist.inl" +#include "MethodTable.inl" + +#ifdef FEATURE_GC_STRESS +enum HijackType { htLoop, htCallsite }; +bool ShouldHijackForGcStress(uintptr_t CallsiteIP, HijackType ht); +#endif // FEATURE_GC_STRESS + +#include "shash.inl" + +#ifndef DACCESS_COMPILE +COOP_PINVOKE_HELPER(uint8_t *, RhSetErrorInfoBuffer, (uint8_t * pNewBuffer)) +{ + return (uint8_t *) PalSetWerDataBuffer(pNewBuffer); +} +#endif // DACCESS_COMPILE + + +ThreadStore * RuntimeInstance::GetThreadStore() +{ + return m_pThreadStore; +} + +COOP_PINVOKE_HELPER(uint8_t *, RhFindMethodStartAddress, (void * codeAddr)) +{ + return dac_cast(GetRuntimeInstance()->FindMethodStartAddress(dac_cast(codeAddr))); +} + +PTR_UInt8 RuntimeInstance::FindMethodStartAddress(PTR_VOID ControlPC) +{ + ICodeManager * pCodeManager = FindCodeManagerByAddress(ControlPC); + MethodInfo methodInfo; + if (pCodeManager != NULL && pCodeManager->FindMethodInfo(ControlPC, &methodInfo)) + { + return (PTR_UInt8)pCodeManager->GetMethodStartAddress(&methodInfo); + } + + return NULL; +} + +ICodeManager * RuntimeInstance::FindCodeManagerByAddress(PTR_VOID pvAddress) +{ + ReaderWriterLock::ReadHolder read(&m_ModuleListLock); + + // TODO: ICodeManager support in DAC +#ifndef DACCESS_COMPILE + for (CodeManagerEntry * pEntry = m_CodeManagerList.GetHead(); pEntry != NULL; pEntry = pEntry->m_pNext) + { + if (dac_cast(pvAddress) - dac_cast(pEntry->m_pvStartRange) < pEntry->m_cbRange) + return pEntry->m_pCodeManager; + } +#endif + + return NULL; +} + +#ifndef DACCESS_COMPILE + +// Find the code manager containing the given address, which might be a return address from a managed function. The +// address may be to another managed function, or it may be to an unmanaged function. The address may also refer to +// an MethodTable. +ICodeManager * RuntimeInstance::FindCodeManagerForClasslibFunction(PTR_VOID address) +{ + // Try looking up the code manager assuming the address is for code first. This is expected to be most common. + ICodeManager * pCodeManager = FindCodeManagerByAddress(address); + if (pCodeManager != NULL) + return pCodeManager; + + ASSERT_MSG(!Thread::IsHijackTarget(address), "not expected to be called with hijacked return address"); + + return NULL; +} + +void * RuntimeInstance::GetClasslibFunctionFromCodeAddress(PTR_VOID address, ClasslibFunctionId functionId) +{ + // Find the code manager for the given address, which is an address into some managed module. It could + // be code, or it could be an MethodTable. No matter what, it's an address into a managed module in some non-Rtm + // type system. + ICodeManager * pCodeManager = FindCodeManagerForClasslibFunction(address); + + // If the address isn't in a managed module then we have no classlib function. + if (pCodeManager == NULL) + { + return NULL; + } + + return pCodeManager->GetClasslibFunction(functionId); +} + +#endif // DACCESS_COMPILE + +PTR_UInt8 RuntimeInstance::GetTargetOfUnboxingAndInstantiatingStub(PTR_VOID ControlPC) +{ + ICodeManager * pCodeManager = FindCodeManagerByAddress(ControlPC); + if (pCodeManager != NULL) + { + PTR_UInt8 pData = (PTR_UInt8)pCodeManager->GetAssociatedData(ControlPC); + if (pData != NULL) + { + uint8_t flags = *pData++; + + if ((flags & (uint8_t)AssociatedDataFlags::HasUnboxingStubTarget) != 0) + return pData + *dac_cast(pData); + } + } + + return NULL; +} + +GPTR_IMPL_INIT(RuntimeInstance, g_pTheRuntimeInstance, NULL); + +PTR_RuntimeInstance GetRuntimeInstance() +{ + return g_pTheRuntimeInstance; +} + +void RuntimeInstance::EnumAllStaticGCRefs(void * pfnCallback, void * pvCallbackData) +{ + for (TypeManagerList::Iterator iter = m_TypeManagerList.Begin(); iter != m_TypeManagerList.End(); iter++) + { + iter->m_pTypeManager->EnumStaticGCRefs(pfnCallback, pvCallbackData); + } +} + +void RuntimeInstance::SetLoopHijackFlags(uint32_t flag) +{ + for (TypeManagerList::Iterator iter = m_TypeManagerList.Begin(); iter != m_TypeManagerList.End(); iter++) + { + iter->m_pTypeManager->SetLoopHijackFlag(flag); + } +} + +RuntimeInstance::OsModuleList* RuntimeInstance::GetOsModuleList() +{ + return dac_cast(dac_cast(this) + offsetof(RuntimeInstance, m_OsModuleList)); +} + +ReaderWriterLock& RuntimeInstance::GetTypeManagerLock() +{ + return m_ModuleListLock; +} + +#ifndef DACCESS_COMPILE + +RuntimeInstance::RuntimeInstance() : + m_pThreadStore(NULL), + m_conservativeStackReportingEnabled(false), + m_pUnboxingStubsRegion(NULL) +{ +} + +RuntimeInstance::~RuntimeInstance() +{ + if (NULL != m_pThreadStore) + { + delete m_pThreadStore; + m_pThreadStore = NULL; + } +} + +HANDLE RuntimeInstance::GetPalInstance() +{ + return m_hPalInstance; +} + +void RuntimeInstance::EnableConservativeStackReporting() +{ + m_conservativeStackReportingEnabled = true; +} + +bool RuntimeInstance::RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, uint32_t cbRange) +{ + CodeManagerEntry * pEntry = new (nothrow) CodeManagerEntry(); + if (NULL == pEntry) + return false; + + pEntry->m_pvStartRange = pvStartRange; + pEntry->m_cbRange = cbRange; + pEntry->m_pCodeManager = pCodeManager; + + { + ReaderWriterLock::WriteHolder write(&m_ModuleListLock); + + m_CodeManagerList.PushHead(pEntry); + } + + return true; +} + +void RuntimeInstance::UnregisterCodeManager(ICodeManager * pCodeManager) +{ + CodeManagerEntry * pEntry = NULL; + + { + ReaderWriterLock::WriteHolder write(&m_ModuleListLock); + + for (CodeManagerList::Iterator i = m_CodeManagerList.Begin(), end = m_CodeManagerList.End(); i != end; i++) + { + if (i->m_pCodeManager == pCodeManager) + { + pEntry = *i; + + m_CodeManagerList.Remove(i); + break; + } + } + } + + ASSERT(pEntry != NULL); + delete pEntry; +} + +extern "C" bool __stdcall RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, uint32_t cbRange) +{ + return GetRuntimeInstance()->RegisterCodeManager(pCodeManager, pvStartRange, cbRange); +} + +extern "C" void __stdcall UnregisterCodeManager(ICodeManager * pCodeManager) +{ + return GetRuntimeInstance()->UnregisterCodeManager(pCodeManager); +} + +bool RuntimeInstance::RegisterUnboxingStubs(PTR_VOID pvStartRange, uint32_t cbRange) +{ + ASSERT(pvStartRange != NULL && cbRange > 0); + + UnboxingStubsRegion * pEntry = new (nothrow) UnboxingStubsRegion(); + if (NULL == pEntry) + return false; + + pEntry->m_pRegionStart = pvStartRange; + pEntry->m_cbRegion = cbRange; + + do + { + pEntry->m_pNextRegion = m_pUnboxingStubsRegion; + } + while (PalInterlockedCompareExchangePointer((void *volatile *)&m_pUnboxingStubsRegion, pEntry, pEntry->m_pNextRegion) != pEntry->m_pNextRegion); + + return true; +} + +bool RuntimeInstance::IsUnboxingStub(uint8_t* pCode) +{ + UnboxingStubsRegion * pCurrent = m_pUnboxingStubsRegion; + while (pCurrent != NULL) + { + uint8_t* pUnboxingStubsRegion = dac_cast(pCurrent->m_pRegionStart); + if (pCode >= pUnboxingStubsRegion && pCode < (pUnboxingStubsRegion + pCurrent->m_cbRegion)) + return true; + + pCurrent = pCurrent->m_pNextRegion; + } + + return false; +} + +extern "C" bool __stdcall RegisterUnboxingStubs(PTR_VOID pvStartRange, uint32_t cbRange) +{ + return GetRuntimeInstance()->RegisterUnboxingStubs(pvStartRange, cbRange); +} + +bool RuntimeInstance::RegisterTypeManager(TypeManager * pTypeManager) +{ + TypeManagerEntry * pEntry = new (nothrow) TypeManagerEntry(); + if (NULL == pEntry) + return false; + + pEntry->m_pTypeManager = pTypeManager; + + { + ReaderWriterLock::WriteHolder write(&m_ModuleListLock); + + m_TypeManagerList.PushHead(pEntry); + } + + return true; +} + +COOP_PINVOKE_HELPER(TypeManagerHandle, RhpCreateTypeManager, (HANDLE osModule, void* pModuleHeader, PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)) +{ + TypeManager * typeManager = TypeManager::Create(osModule, pModuleHeader, pClasslibFunctions, nClasslibFunctions); + GetRuntimeInstance()->RegisterTypeManager(typeManager); + + return TypeManagerHandle::Create(typeManager); +} + +COOP_PINVOKE_HELPER(void*, RhpRegisterOsModule, (HANDLE hOsModule)) +{ + RuntimeInstance::OsModuleEntry * pEntry = new (nothrow) RuntimeInstance::OsModuleEntry(); + if (NULL == pEntry) + return nullptr; // Return null on failure. + + pEntry->m_osModule = hOsModule; + + { + RuntimeInstance *pRuntimeInstance = GetRuntimeInstance(); + ReaderWriterLock::WriteHolder write(&pRuntimeInstance->GetTypeManagerLock()); + + pRuntimeInstance->GetOsModuleList()->PushHead(pEntry); + } + + return hOsModule; // Return non-null on success +} + +RuntimeInstance::TypeManagerList& RuntimeInstance::GetTypeManagerList() +{ + return m_TypeManagerList; +} + +// static +bool RuntimeInstance::Initialize(HANDLE hPalInstance) +{ + NewHolder pRuntimeInstance = new (nothrow) RuntimeInstance(); + if (NULL == pRuntimeInstance) + return false; + + CreateHolder pThreadStore = ThreadStore::Create(pRuntimeInstance); + if (NULL == pThreadStore) + return false; + + pThreadStore.SuppressRelease(); + pRuntimeInstance.SuppressRelease(); + + pRuntimeInstance->m_pThreadStore = pThreadStore; + pRuntimeInstance->m_hPalInstance = hPalInstance; + + ASSERT_MSG(g_pTheRuntimeInstance == NULL, "multi-instances are not supported"); + g_pTheRuntimeInstance = pRuntimeInstance; + + return true; +} + +void RuntimeInstance::Destroy() +{ + delete this; +} + +bool RuntimeInstance::ShouldHijackLoopForGcStress(uintptr_t CallsiteIP) +{ +#ifdef FEATURE_GC_STRESS + return ShouldHijackForGcStress(CallsiteIP, htLoop); +#else // FEATURE_GC_STRESS + UNREFERENCED_PARAMETER(CallsiteIP); + return false; +#endif // FEATURE_GC_STRESS +} + +bool RuntimeInstance::ShouldHijackCallsiteForGcStress(uintptr_t CallsiteIP) +{ +#ifdef FEATURE_GC_STRESS + return ShouldHijackForGcStress(CallsiteIP, htCallsite); +#else // FEATURE_GC_STRESS + UNREFERENCED_PARAMETER(CallsiteIP); + return false; +#endif // FEATURE_GC_STRESS +} + +COOP_PINVOKE_HELPER(uint32_t, RhGetGCDescSize, (MethodTable* pEEType)) +{ + return RedhawkGCInterface::GetGCDescSize(pEEType); +} + +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH +EXTERN_C void RhpInitialDynamicInterfaceDispatch(); + +COOP_PINVOKE_HELPER(void *, RhNewInterfaceDispatchCell, (MethodTable * pInterface, int32_t slotNumber)) +{ + InterfaceDispatchCell * pCell = new (nothrow) InterfaceDispatchCell[2]; + if (pCell == NULL) + return NULL; + + // Due to the synchronization mechanism used to update this indirection cell we must ensure the cell's alignment is twice that of a pointer. + // Fortunately, Windows heap guarantees this alignment. + ASSERT(IS_ALIGNED(pCell, 2 * POINTER_SIZE)); + ASSERT(IS_ALIGNED(pInterface, (InterfaceDispatchCell::IDC_CachePointerMask + 1))); + + pCell[0].m_pStub = (uintptr_t)&RhpInitialDynamicInterfaceDispatch; + pCell[0].m_pCache = ((uintptr_t)pInterface) | InterfaceDispatchCell::IDC_CachePointerIsInterfacePointerOrMetadataToken; + pCell[1].m_pStub = 0; + pCell[1].m_pCache = (uintptr_t)slotNumber; + + return pCell; +} +#endif // FEATURE_CACHED_INTERFACE_DISPATCH + +COOP_PINVOKE_HELPER(PTR_UInt8, RhGetThreadLocalStorageForDynamicType, (uint32_t uOffset, uint32_t tlsStorageSize, uint32_t numTlsCells)) +{ + Thread * pCurrentThread = ThreadStore::GetCurrentThread(); + + PTR_UInt8 pResult = pCurrentThread->GetThreadLocalStorageForDynamicType(uOffset); + if (pResult != NULL || tlsStorageSize == 0 || numTlsCells == 0) + return pResult; + + ASSERT(tlsStorageSize > 0 && numTlsCells > 0); + return pCurrentThread->AllocateThreadLocalStorageForDynamicType(uOffset, tlsStorageSize, numTlsCells); +} + +#endif // DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/RuntimeInstance.h b/src/coreclr/nativeaot/Runtime/RuntimeInstance.h new file mode 100644 index 00000000000000..6b74f7799a8d3a --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/RuntimeInstance.h @@ -0,0 +1,130 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __RuntimeInstance_h__ +#define __RuntimeInstance_h__ + +class ThreadStore; +typedef DPTR(ThreadStore) PTR_ThreadStore; +class ICodeManager; +struct StaticGcDesc; +typedef SPTR(StaticGcDesc) PTR_StaticGcDesc; +class TypeManager; +enum GenericVarianceType : uint8_t; + +#include "ICodeManager.h" + +extern "C" void PopulateDebugHeaders(); + +class RuntimeInstance +{ + friend class AsmOffsets; + friend struct DefaultSListTraits; + friend class Thread; + friend void PopulateDebugHeaders(); + + PTR_ThreadStore m_pThreadStore; + HANDLE m_hPalInstance; // this is the HANDLE passed into DllMain + ReaderWriterLock m_ModuleListLock; + +public: + struct OsModuleEntry; + typedef DPTR(OsModuleEntry) PTR_OsModuleEntry; + struct OsModuleEntry + { + PTR_OsModuleEntry m_pNext; + HANDLE m_osModule; + }; + + typedef SList OsModuleList; +private: + OsModuleList m_OsModuleList; + + struct CodeManagerEntry; + typedef DPTR(CodeManagerEntry) PTR_CodeManagerEntry; + + struct CodeManagerEntry + { + PTR_CodeManagerEntry m_pNext; + PTR_VOID m_pvStartRange; + uint32_t m_cbRange; + ICodeManager * m_pCodeManager; + }; + + typedef SList CodeManagerList; + CodeManagerList m_CodeManagerList; + +public: + struct TypeManagerEntry + { + TypeManagerEntry* m_pNext; + TypeManager* m_pTypeManager; + }; + + typedef SList TypeManagerList; + +private: + TypeManagerList m_TypeManagerList; + + bool m_conservativeStackReportingEnabled; + + struct UnboxingStubsRegion + { + PTR_VOID m_pRegionStart; + uint32_t m_cbRegion; + UnboxingStubsRegion* m_pNextRegion; + + UnboxingStubsRegion() : m_pRegionStart(0), m_cbRegion(0), m_pNextRegion(NULL) { } + }; + + UnboxingStubsRegion* m_pUnboxingStubsRegion; + + RuntimeInstance(); + + SList* GetModuleList(); + + SList* GetModuleManagerList(); + + bool BuildGenericTypeHashTable(); + + ICodeManager * FindCodeManagerForClasslibFunction(PTR_VOID address); + +public: + ~RuntimeInstance(); + ThreadStore * GetThreadStore(); + HANDLE GetPalInstance(); + + PTR_UInt8 FindMethodStartAddress(PTR_VOID ControlPC); + PTR_UInt8 GetTargetOfUnboxingAndInstantiatingStub(PTR_VOID ControlPC); + void EnableConservativeStackReporting(); + bool IsConservativeStackReportingEnabled() { return m_conservativeStackReportingEnabled; } + + bool RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, uint32_t cbRange); + void UnregisterCodeManager(ICodeManager * pCodeManager); + + ICodeManager * FindCodeManagerByAddress(PTR_VOID ControlPC); + PTR_VOID GetClasslibFunctionFromCodeAddress(PTR_VOID address, ClasslibFunctionId functionId); + + bool RegisterTypeManager(TypeManager * pTypeManager); + TypeManagerList& GetTypeManagerList(); + OsModuleList* GetOsModuleList(); + ReaderWriterLock& GetTypeManagerLock(); + + bool RegisterUnboxingStubs(PTR_VOID pvStartRange, uint32_t cbRange); + bool IsUnboxingStub(uint8_t* pCode); + + static bool Initialize(HANDLE hPalInstance); + void Destroy(); + + void EnumAllStaticGCRefs(void * pfnCallback, void * pvCallbackData); + + bool ShouldHijackCallsiteForGcStress(uintptr_t CallsiteIP); + bool ShouldHijackLoopForGcStress(uintptr_t CallsiteIP); + void SetLoopHijackFlags(uint32_t flag); +}; +typedef DPTR(RuntimeInstance) PTR_RuntimeInstance; + + +PTR_RuntimeInstance GetRuntimeInstance(); + +#endif // __RuntimeInstance_h__ diff --git a/src/coreclr/nativeaot/Runtime/SpinLock.h b/src/coreclr/nativeaot/Runtime/SpinLock.h new file mode 100644 index 00000000000000..a343a1881f057c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/SpinLock.h @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#ifndef __SPINLOCK_H__ +#define __SPINLOCK_H__ + +// #SwitchToThreadSpinning +// +// If you call __SwitchToThread in a loop waiting for a condition to be met, +// it is critical that you insert periodic sleeps. This is because the thread +// you are waiting for to set that condition may need your CPU, and simply +// calling __SwitchToThread(0) will NOT guarantee that it gets a chance to run. +// If there are other runnable threads of higher priority, or even if there +// aren't and it is in another processor's queue, you will be spinning a very +// long time. +// +// To force all callers to consider this issue and to avoid each having to +// duplicate the same backoff code, __SwitchToThread takes a required second +// parameter. If you want it to handle backoff for you, this parameter should +// be the number of successive calls you have made to __SwitchToThread (a loop +// count). If you want to take care of backing off yourself, you can pass +// CALLER_LIMITS_SPINNING. There are three valid cases for doing this: +// +// - You count iterations and induce a sleep periodically +// - The number of consecutive __SwitchToThreads is limited +// - Your call to __SwitchToThread includes a non-zero sleep duration +// +// Lastly, to simplify this requirement for the following common coding pattern: +// +// while (!condition) +// SwitchToThread +// +// you can use the YIELD_WHILE macro. + +#define YIELD_WHILE(condition) \ + { \ + uint32_t __dwSwitchCount = 0; \ + while (condition) \ + { \ + __SwitchToThread(0, ++__dwSwitchCount); \ + } \ + } + +class SpinLock +{ +private: + enum LOCK_STATE + { + UNLOCKED = 0, + LOCKED = 1 + }; + + volatile int32_t m_lock; + + static void Lock(SpinLock& lock) + { YIELD_WHILE (PalInterlockedExchange(&lock.m_lock, LOCKED) == LOCKED); } + + static void Unlock(SpinLock& lock) + { PalInterlockedExchange(&lock.m_lock, UNLOCKED); } + +public: + SpinLock() + : m_lock(UNLOCKED) { } + + typedef HolderNoDefaultValue + Holder; +}; + +#endif + diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp new file mode 100644 index 00000000000000..335dd71fb4327b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -0,0 +1,1870 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "RedhawkWarnings.h" +#include "rhassert.h" +#include "slist.h" +#include "gcrhinterface.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "holder.h" +#include "Crst.h" +#include "RWLock.h" +#include "event.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "thread.inl" +#include "stressLog.h" + +#include "shash.h" +#include "RuntimeInstance.h" +#include "rhbinder.h" + +// warning C4061: enumerator '{blah}' in switch of enum '{blarg}' is not explicitly handled by a case label +#pragma warning(disable:4061) + +#if !defined(USE_PORTABLE_HELPERS) // @TODO: CORERT: these are (currently) only implemented in assembly helpers + +#if defined(FEATURE_DYNAMIC_CODE) +EXTERN_C void * RhpUniversalTransition(); +GPTR_IMPL_INIT(PTR_VOID, g_RhpUniversalTransitionAddr, (void**)&RhpUniversalTransition); + +EXTERN_C PTR_VOID PointerToReturnFromUniversalTransition; +GVAL_IMPL_INIT(PTR_VOID, g_ReturnFromUniversalTransitionAddr, PointerToReturnFromUniversalTransition); + +EXTERN_C PTR_VOID PointerToReturnFromUniversalTransition_DebugStepTailCall; +GVAL_IMPL_INIT(PTR_VOID, g_ReturnFromUniversalTransition_DebugStepTailCallAddr, PointerToReturnFromUniversalTransition_DebugStepTailCall); + +EXTERN_C PTR_VOID PointerToReturnFromCallDescrThunk; +GVAL_IMPL_INIT(PTR_VOID, g_ReturnFromCallDescrThunkAddr, PointerToReturnFromCallDescrThunk); +#endif + +#ifdef TARGET_X86 +EXTERN_C void * PointerToRhpCallFunclet2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpCallFunclet2Addr, PointerToRhpCallFunclet2); +#endif +EXTERN_C void * PointerToRhpCallCatchFunclet2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpCallCatchFunclet2Addr, PointerToRhpCallCatchFunclet2); +EXTERN_C void * PointerToRhpCallFinallyFunclet2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpCallFinallyFunclet2Addr, PointerToRhpCallFinallyFunclet2); +EXTERN_C void * PointerToRhpCallFilterFunclet2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpCallFilterFunclet2Addr, PointerToRhpCallFilterFunclet2); +EXTERN_C void * PointerToRhpThrowEx2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpThrowEx2Addr, PointerToRhpThrowEx2); +EXTERN_C void * PointerToRhpThrowHwEx2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpThrowHwEx2Addr, PointerToRhpThrowHwEx2); +EXTERN_C void * PointerToRhpRethrow2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpRethrow2Addr, PointerToRhpRethrow2); +#endif // !defined(USE_PORTABLE_HELPERS) + +// Addresses of functions in the DAC won't match their runtime counterparts so we +// assign them to globals. However it is more performant in the runtime to compare +// against immediates than to fetch the global. This macro hides the difference. +// +// We use a special code path for the return address from thunks as +// having the return address public confuses today DIA stackwalker. Before we can +// ingest the updated DIA, we're instead exposing a global void * variable +// holding the return address. +#ifdef DACCESS_COMPILE +#define EQUALS_RETURN_ADDRESS(x, func_name) ((x) == g_ ## func_name ## Addr) +#else +#define EQUALS_RETURN_ADDRESS(x, func_name) (((x)) == (PointerTo ## func_name)) +#endif + +#ifdef DACCESS_COMPILE +#define FAILFAST_OR_DAC_FAIL(x) if(!(x)) { DacError(E_FAIL); } +#define FAILFAST_OR_DAC_FAIL_MSG(x, msg) if(!(x)) { DacError(E_FAIL); } +#define FAILFAST_OR_DAC_FAIL_UNCONDITIONALLY(msg) DacError(E_FAIL) +#else +#define FAILFAST_OR_DAC_FAIL(x) if(!(x)) { ASSERT_UNCONDITIONALLY(#x); RhFailFast(); } +#define FAILFAST_OR_DAC_FAIL_MSG(x, msg) if(!(x)) { ASSERT_MSG((x), msg); ASSERT_UNCONDITIONALLY(#x); RhFailFast(); } +#define FAILFAST_OR_DAC_FAIL_UNCONDITIONALLY(msg) { ASSERT_UNCONDITIONALLY(msg); RhFailFast(); } +#endif + +PTR_PInvokeTransitionFrame GetPInvokeTransitionFrame(PTR_VOID pTransitionFrame) +{ + return static_cast(pTransitionFrame); +} + +StackFrameIterator::StackFrameIterator(Thread * pThreadToWalk, PTR_VOID pInitialTransitionFrame) +{ + STRESS_LOG0(LF_STACKWALK, LL_INFO10000, "----Init---- [ GC ]\n"); + ASSERT(!pThreadToWalk->DangerousCrossThreadIsHijacked()); + InternalInit(pThreadToWalk, GetPInvokeTransitionFrame(pInitialTransitionFrame), GcStackWalkFlags); + PrepareToYieldFrame(); +} + +StackFrameIterator::StackFrameIterator(Thread * pThreadToWalk, PTR_PAL_LIMITED_CONTEXT pCtx) +{ + STRESS_LOG0(LF_STACKWALK, LL_INFO10000, "----Init---- [ hijack ]\n"); + InternalInit(pThreadToWalk, pCtx, 0); + PrepareToYieldFrame(); +} + +void StackFrameIterator::ResetNextExInfoForSP(uintptr_t SP) +{ + while (m_pNextExInfo && (SP > (uintptr_t)dac_cast(m_pNextExInfo))) + m_pNextExInfo = m_pNextExInfo->m_pPrevExInfo; +} + +void StackFrameIterator::EnterInitialInvalidState(Thread * pThreadToWalk) +{ + m_pThread = pThreadToWalk; + m_pInstance = GetRuntimeInstance(); + m_pCodeManager = NULL; + m_pHijackedReturnValue = NULL; + m_HijackedReturnValueKind = GCRK_Unknown; + m_pConservativeStackRangeLowerBound = NULL; + m_pConservativeStackRangeUpperBound = NULL; + m_ShouldSkipRegularGcReporting = false; + m_pendingFuncletFramePointer = NULL; + m_pNextExInfo = pThreadToWalk->GetCurExInfo(); + SetControlPC(0); +} + +// Prepare to start a stack walk from the context listed in the supplied PInvokeTransitionFrame. +// The supplied frame can be TOP_OF_STACK_MARKER to indicate that there are no more managed +// frames on the stack. Otherwise, the context in the frame always describes a callsite +// where control transitioned from managed to unmanaged code. +// NOTE: When a return address hijack is executed, the PC in the generated PInvokeTransitionFrame +// matches the hijacked return address. This PC is not guaranteed to be in managed code +// since the hijacked return address may refer to a location where an assembly thunk called +// into managed code. +// NOTE: When the PC is in an assembly thunk, this function will unwind to the next managed +// frame and may publish a conservative stack range (if and only if any of the unwound +// thunks report a conservative range). +void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PInvokeTransitionFrame pFrame, uint32_t dwFlags) +{ + // EH stackwalks are always required to unwind non-volatile floating point state. This + // state is never carried by PInvokeTransitionFrames, implying that they can never be used + // as the initial state for an EH stackwalk. + ASSERT_MSG(!(dwFlags & ApplyReturnAddressAdjustment), + "PInvokeTransitionFrame content is not sufficient to seed an EH stackwalk"); + + EnterInitialInvalidState(pThreadToWalk); + + if (pFrame == TOP_OF_STACK_MARKER) + { + // There are no managed frames on the stack. Leave the iterator in its initial invalid state. + return; + } + + m_dwFlags = dwFlags; + + // We need to walk the ExInfo chain in parallel with the stackwalk so that we know when we cross over + // exception throw points. So we must find our initial point in the ExInfo chain here so that we can + // properly walk it in parallel. + ResetNextExInfoForSP((uintptr_t)dac_cast(pFrame)); + +#if !defined(USE_PORTABLE_HELPERS) // @TODO: CORERT: no portable version of regdisplay + memset(&m_RegDisplay, 0, sizeof(m_RegDisplay)); + m_RegDisplay.SetIP((PCODE)pFrame->m_RIP); + m_RegDisplay.SetAddrOfIP((PTR_PCODE)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_RIP)); + SetControlPC(dac_cast(*(m_RegDisplay.pIP))); + + PTR_UIntNative pPreservedRegsCursor = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_PreservedRegs); + +#ifdef TARGET_ARM + m_RegDisplay.pLR = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_RIP); + m_RegDisplay.pR11 = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_ChainPointer); + + if (pFrame->m_Flags & PTFF_SAVE_R4) { m_RegDisplay.pR4 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R5) { m_RegDisplay.pR5 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R6) { m_RegDisplay.pR6 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R7) { m_RegDisplay.pR7 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R8) { m_RegDisplay.pR8 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R9) { m_RegDisplay.pR9 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R10) { m_RegDisplay.pR10 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_SP) { m_RegDisplay.SP = *pPreservedRegsCursor++; } + m_RegDisplay.pR11 = (PTR_UIntNative) PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_FramePointer); + if (pFrame->m_Flags & PTFF_SAVE_R0) { m_RegDisplay.pR0 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R1) { m_RegDisplay.pR1 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R2) { m_RegDisplay.pR2 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R3) { m_RegDisplay.pR3 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_LR) { m_RegDisplay.pLR = pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_R0_IS_GCREF) + { + m_pHijackedReturnValue = (PTR_RtuObjectRef) m_RegDisplay.pR0; + m_HijackedReturnValueKind = GCRK_Object; + } + if (pFrame->m_Flags & PTFF_R0_IS_BYREF) + { + m_pHijackedReturnValue = (PTR_RtuObjectRef) m_RegDisplay.pR0; + m_HijackedReturnValueKind = GCRK_Byref; + } + +#elif defined(TARGET_ARM64) + m_RegDisplay.pFP = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_FramePointer); + m_RegDisplay.pLR = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_RIP); + + ASSERT(!(pFrame->m_Flags & PTFF_SAVE_FP)); // FP should never contain a GC ref because we require + // a frame pointer for methods with pinvokes + + if (pFrame->m_Flags & PTFF_SAVE_X19) { m_RegDisplay.pX19 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X20) { m_RegDisplay.pX20 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X21) { m_RegDisplay.pX21 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X22) { m_RegDisplay.pX22 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X23) { m_RegDisplay.pX23 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X24) { m_RegDisplay.pX24 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X25) { m_RegDisplay.pX25 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X26) { m_RegDisplay.pX26 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X27) { m_RegDisplay.pX27 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X28) { m_RegDisplay.pX28 = pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_SP) { m_RegDisplay.SP = *pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_X0) { m_RegDisplay.pX0 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X1) { m_RegDisplay.pX1 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X2) { m_RegDisplay.pX2 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X3) { m_RegDisplay.pX3 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X4) { m_RegDisplay.pX4 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X5) { m_RegDisplay.pX5 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X6) { m_RegDisplay.pX6 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X7) { m_RegDisplay.pX7 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X8) { m_RegDisplay.pX8 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X9) { m_RegDisplay.pX9 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X10) { m_RegDisplay.pX10 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X11) { m_RegDisplay.pX11 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X12) { m_RegDisplay.pX12 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X13) { m_RegDisplay.pX13 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X14) { m_RegDisplay.pX14 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X15) { m_RegDisplay.pX15 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X16) { m_RegDisplay.pX16 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X17) { m_RegDisplay.pX17 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X18) { m_RegDisplay.pX18 = pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_LR) { m_RegDisplay.pLR = pPreservedRegsCursor++; } + + GCRefKind retValueKind = TransitionFrameFlagsToReturnKind(pFrame->m_Flags); + if (retValueKind != GCRK_Scalar) + { + m_pHijackedReturnValue = (PTR_RtuObjectRef)m_RegDisplay.pX0; + m_HijackedReturnValueKind = retValueKind; + } + +#else // TARGET_ARM + if (pFrame->m_Flags & PTFF_SAVE_RBX) { m_RegDisplay.pRbx = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_RSI) { m_RegDisplay.pRsi = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_RDI) { m_RegDisplay.pRdi = pPreservedRegsCursor++; } + ASSERT(!(pFrame->m_Flags & PTFF_SAVE_RBP)); // RBP should never contain a GC ref because we require + // a frame pointer for methods with pinvokes +#ifdef TARGET_AMD64 + if (pFrame->m_Flags & PTFF_SAVE_R12) { m_RegDisplay.pR12 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R13) { m_RegDisplay.pR13 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R14) { m_RegDisplay.pR14 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R15) { m_RegDisplay.pR15 = pPreservedRegsCursor++; } +#endif // TARGET_AMD64 + + m_RegDisplay.pRbp = (PTR_UIntNative) PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_FramePointer); + + if (pFrame->m_Flags & PTFF_SAVE_RSP) { m_RegDisplay.SP = *pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_RAX) { m_RegDisplay.pRax = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_RCX) { m_RegDisplay.pRcx = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_RDX) { m_RegDisplay.pRdx = pPreservedRegsCursor++; } +#ifdef TARGET_AMD64 + if (pFrame->m_Flags & PTFF_SAVE_R8 ) { m_RegDisplay.pR8 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R9 ) { m_RegDisplay.pR9 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R10) { m_RegDisplay.pR10 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R11) { m_RegDisplay.pR11 = pPreservedRegsCursor++; } +#endif // TARGET_AMD64 + + if (pFrame->m_Flags & PTFF_RAX_IS_GCREF) + { + m_pHijackedReturnValue = (PTR_RtuObjectRef) m_RegDisplay.pRax; + m_HijackedReturnValueKind = GCRK_Object; + } + if (pFrame->m_Flags & PTFF_RAX_IS_BYREF) + { + m_pHijackedReturnValue = (PTR_RtuObjectRef) m_RegDisplay.pRax; + m_HijackedReturnValueKind = GCRK_Byref; + } + +#endif // TARGET_ARM + +#endif // defined(USE_PORTABLE_HELPERS) + + // @TODO: currently, we always save all registers -- how do we handle the onese we don't save once we + // start only saving those that weren't already saved? + + // This function guarantees that the final initialized context will refer to a managed + // frame. In the rare case where the PC does not refer to managed code (and refers to an + // assembly thunk instead), unwind through the thunk sequence to find the nearest managed + // frame. + // NOTE: When thunks are present, the thunk sequence may report a conservative GC reporting + // lower bound that must be applied when processing the managed frame. + + ReturnAddressCategory category = CategorizeUnadjustedReturnAddress(m_ControlPC); + + if (category == InManagedCode) + { + ASSERT(m_pInstance->FindCodeManagerByAddress(m_ControlPC)); + } + else if (IsNonEHThunk(category)) + { + UnwindNonEHThunkSequence(); + ASSERT(m_pInstance->FindCodeManagerByAddress(m_ControlPC)); + } + else + { + FAILFAST_OR_DAC_FAIL_UNCONDITIONALLY("PInvokeTransitionFrame PC points to an unexpected assembly thunk kind."); + } + + STRESS_LOG1(LF_STACKWALK, LL_INFO10000, " %p\n", m_ControlPC); +} + +#ifndef DACCESS_COMPILE + +void StackFrameIterator::InternalInitForEH(Thread * pThreadToWalk, PAL_LIMITED_CONTEXT * pCtx, bool instructionFault) +{ + STRESS_LOG0(LF_STACKWALK, LL_INFO10000, "----Init---- [ EH ]\n"); + InternalInit(pThreadToWalk, pCtx, EHStackWalkFlags); + + if (instructionFault) + { + // We treat the IP as a return-address and adjust backward when doing EH-related things. The faulting + // instruction IP here will be the start of the faulting instruction and so we have the right IP for + // EH-related things already. + m_dwFlags &= ~ApplyReturnAddressAdjustment; + PrepareToYieldFrame(); + m_dwFlags |= ApplyReturnAddressAdjustment; + } + else + { + PrepareToYieldFrame(); + } + + STRESS_LOG1(LF_STACKWALK, LL_INFO10000, " %p\n", m_ControlPC); +} + +void StackFrameIterator::InternalInitForStackTrace() +{ + STRESS_LOG0(LF_STACKWALK, LL_INFO10000, "----Init---- [ StackTrace ]\n"); + Thread * pThreadToWalk = ThreadStore::GetCurrentThread(); + PTR_VOID pFrame = pThreadToWalk->GetTransitionFrameForStackTrace(); + InternalInit(pThreadToWalk, GetPInvokeTransitionFrame(pFrame), StackTraceStackWalkFlags); + PrepareToYieldFrame(); +} + +#endif //!DACCESS_COMPILE + +// Prepare to start a stack walk from the context listed in the supplied PAL_LIMITED_CONTEXT. +// The supplied context can describe a location in either managed or unmanaged code. In the +// latter case the iterator is left in an invalid state when this function returns. +void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PAL_LIMITED_CONTEXT pCtx, uint32_t dwFlags) +{ + ASSERT((dwFlags & MethodStateCalculated) == 0); + + EnterInitialInvalidState(pThreadToWalk); + + m_dwFlags = dwFlags; + + // We need to walk the ExInfo chain in parallel with the stackwalk so that we know when we cross over + // exception throw points. So we must find our initial point in the ExInfo chain here so that we can + // properly walk it in parallel. + ResetNextExInfoForSP(pCtx->GetSp()); + + // This codepath is used by the hijack stackwalk and we can get arbitrary ControlPCs from there. If this + // context has a non-managed control PC, then we're done. + if (!m_pInstance->FindCodeManagerByAddress(dac_cast(pCtx->GetIp()))) + return; + + // + // control state + // + SetControlPC(dac_cast(pCtx->GetIp())); + m_RegDisplay.SP = pCtx->GetSp(); + m_RegDisplay.IP = pCtx->GetIp(); + m_RegDisplay.pIP = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, IP); + +#ifdef TARGET_ARM + // + // preserved regs + // + m_RegDisplay.pR4 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R4); + m_RegDisplay.pR5 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R5); + m_RegDisplay.pR6 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R6); + m_RegDisplay.pR7 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R7); + m_RegDisplay.pR8 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R8); + m_RegDisplay.pR9 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R9); + m_RegDisplay.pR10 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R10); + m_RegDisplay.pR11 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R11); + m_RegDisplay.pLR = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, LR); + + // + // preserved vfp regs + // + for (int32_t i = 0; i < 16 - 8; i++) + { + m_RegDisplay.D[i] = pCtx->D[i]; + } + // + // scratch regs + // + m_RegDisplay.pR0 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R0); + +#elif defined(TARGET_ARM64) + // + // preserved regs + // + m_RegDisplay.pX19 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X19); + m_RegDisplay.pX20 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X20); + m_RegDisplay.pX21 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X21); + m_RegDisplay.pX22 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X22); + m_RegDisplay.pX23 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X23); + m_RegDisplay.pX24 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X24); + m_RegDisplay.pX25 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X25); + m_RegDisplay.pX26 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X26); + m_RegDisplay.pX27 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X27); + m_RegDisplay.pX28 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X28); + m_RegDisplay.pFP = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, FP); + m_RegDisplay.pLR = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, LR); + + // + // preserved vfp regs + // + for (int32_t i = 0; i < 16 - 8; i++) + { + m_RegDisplay.D[i] = pCtx->D[i]; + } + // + // scratch regs + // + m_RegDisplay.pX0 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X0); + m_RegDisplay.pX1 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X1); + // TODO: Copy X2-X7 when we start supporting HVA's + +#elif defined(UNIX_AMD64_ABI) + // + // preserved regs + // + m_RegDisplay.pRbp = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rbp); + m_RegDisplay.pRbx = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rbx); + m_RegDisplay.pR12 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R12); + m_RegDisplay.pR13 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R13); + m_RegDisplay.pR14 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R14); + m_RegDisplay.pR15 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R15); + + // + // scratch regs + // + m_RegDisplay.pRax = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rax); + m_RegDisplay.pRcx = NULL; + m_RegDisplay.pRdx = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rdx); + m_RegDisplay.pRsi = NULL; + m_RegDisplay.pRdi = NULL; + m_RegDisplay.pR8 = NULL; + m_RegDisplay.pR9 = NULL; + m_RegDisplay.pR10 = NULL; + m_RegDisplay.pR11 = NULL; + +#elif defined(TARGET_X86) || defined(TARGET_AMD64) + // + // preserved regs + // + m_RegDisplay.pRbp = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rbp); + m_RegDisplay.pRsi = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rsi); + m_RegDisplay.pRdi = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rdi); + m_RegDisplay.pRbx = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rbx); +#ifdef TARGET_AMD64 + m_RegDisplay.pR12 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R12); + m_RegDisplay.pR13 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R13); + m_RegDisplay.pR14 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R14); + m_RegDisplay.pR15 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R15); + // + // preserved xmm regs + // + memcpy(m_RegDisplay.Xmm, &pCtx->Xmm6, sizeof(m_RegDisplay.Xmm)); +#endif // TARGET_AMD64 + + // + // scratch regs + // + m_RegDisplay.pRax = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rax); + m_RegDisplay.pRcx = NULL; + m_RegDisplay.pRdx = NULL; +#ifdef TARGET_AMD64 + m_RegDisplay.pR8 = NULL; + m_RegDisplay.pR9 = NULL; + m_RegDisplay.pR10 = NULL; + m_RegDisplay.pR11 = NULL; +#endif // TARGET_AMD64 +#else + PORTABILITY_ASSERT("StackFrameIterator::InternalInit"); +#endif // TARGET_ARM +} + +PTR_VOID StackFrameIterator::HandleExCollide(PTR_ExInfo pExInfo) +{ + STRESS_LOG3(LF_STACKWALK, LL_INFO10000, " [ ex collide ] kind = %d, pass = %d, idxCurClause = %d\n", + pExInfo->m_kind, pExInfo->m_passNumber, pExInfo->m_idxCurClause); + + PTR_VOID collapsingTargetFrame = NULL; + uint32_t curFlags = m_dwFlags; + + // Capture and clear the pending funclet frame pointer (if any). This field is only set + // when stack walks collide with active exception dispatch, and only exists to save the + // funclet frame pointer until the next ExInfo collision (which has now occurred). + PTR_VOID activeFuncletFramePointer = m_pendingFuncletFramePointer; + m_pendingFuncletFramePointer = NULL; + + // If we aren't invoking a funclet (i.e. idxCurClause == -1), and we're doing a GC stackwalk, we don't + // want the 2nd-pass collided behavior because that behavior assumes that the previous frame was a + // funclet, which isn't the case when taking a GC at some points in the EH dispatch code. So we treat it + // as if the 2nd pass hasn't actually started yet. + if ((pExInfo->m_passNumber == 1) || + (pExInfo->m_idxCurClause == 0xFFFFFFFF)) + { + FAILFAST_OR_DAC_FAIL_MSG(!(curFlags & ApplyReturnAddressAdjustment), + "did not expect to collide with a 1st-pass ExInfo during a EH stackwalk"); + InternalInit(m_pThread, pExInfo->m_pExContext, curFlags); + m_pNextExInfo = pExInfo->m_pPrevExInfo; + CalculateCurrentMethodState(); + ASSERT(IsValid()); + + if ((pExInfo->m_kind & EK_HardwareFault) && (curFlags & RemapHardwareFaultsToSafePoint)) + m_effectiveSafePointAddress = GetCodeManager()->RemapHardwareFaultToGCSafePoint(&m_methodInfo, m_ControlPC); + } + else + { + ASSERT_MSG(activeFuncletFramePointer != NULL, + "collided with an active funclet invoke but the funclet frame pointer is unknown"); + + // + // Copy our state from the previous StackFrameIterator + // + this->UpdateFromExceptionDispatch((PTR_StackFrameIterator)&pExInfo->m_frameIter); + + // Sync our 'current' ExInfo with the updated state (we may have skipped other dispatches) + ResetNextExInfoForSP(m_RegDisplay.GetSP()); + + // In case m_ControlPC is pre-adjusted, counteract here, since the caller of this routine + // will apply the adjustment again once we return. If the m_ControlPC is not pre-adjusted, + // this is simply an no-op. + m_ControlPC = m_OriginalControlPC; + + m_dwFlags = curFlags; + + // The iterator has been moved to the "owner frame" (either a parent funclet or the main + // code body) of the funclet being invoked by this ExInfo. As a result, both the active + // funclet and the current frame must be "part of the same function" and therefore must + // have identical frame pointer values. + + CalculateCurrentMethodState(); + ASSERT(IsValid()); + ASSERT(m_FramePointer == activeFuncletFramePointer); + + if ((m_ControlPC != 0) && // the dispatch in ExInfo could have gone unhandled + (m_dwFlags & CollapseFunclets)) + { + // GC stack walks must skip the owner frame since GC information for the entire function + // has already been reported by the leafmost active funclet. In general, the GC stack walk + // must skip all parent frames that are "part of the same function" (i.e., have the same + // frame pointer). + collapsingTargetFrame = activeFuncletFramePointer; + } + } + return collapsingTargetFrame; +} + +void StackFrameIterator::UpdateFromExceptionDispatch(PTR_StackFrameIterator pSourceIterator) +{ + ASSERT(m_pendingFuncletFramePointer == NULL); + PreservedRegPtrs thisFuncletPtrs = this->m_funcletPtrs; + + // Blast over 'this' with everything from the 'source'. + *this = *pSourceIterator; + + // Clear the funclet frame pointer (if any) that was loaded from the previous iterator. + // This field does not relate to the transferrable state of the previous iterator (it + // instead tracks the frame-by-frame progression of a particular iterator instance) and + // therefore has no meaning in the context of the current stack walk. + m_pendingFuncletFramePointer = NULL; + + // Then, put back the pointers to the funclet's preserved registers (since those are the correct values + // until the funclet completes, at which point the values will be copied back to the ExInfo's REGDISPLAY). + +#ifdef TARGET_ARM + m_RegDisplay.pR4 = thisFuncletPtrs.pR4 ; + m_RegDisplay.pR5 = thisFuncletPtrs.pR5 ; + m_RegDisplay.pR6 = thisFuncletPtrs.pR6 ; + m_RegDisplay.pR7 = thisFuncletPtrs.pR7 ; + m_RegDisplay.pR8 = thisFuncletPtrs.pR8 ; + m_RegDisplay.pR9 = thisFuncletPtrs.pR9 ; + m_RegDisplay.pR10 = thisFuncletPtrs.pR10; + m_RegDisplay.pR11 = thisFuncletPtrs.pR11; + +#elif defined(TARGET_ARM64) + m_RegDisplay.pX19 = thisFuncletPtrs.pX19; + m_RegDisplay.pX20 = thisFuncletPtrs.pX20; + m_RegDisplay.pX21 = thisFuncletPtrs.pX21; + m_RegDisplay.pX22 = thisFuncletPtrs.pX22; + m_RegDisplay.pX23 = thisFuncletPtrs.pX23; + m_RegDisplay.pX24 = thisFuncletPtrs.pX24; + m_RegDisplay.pX25 = thisFuncletPtrs.pX25; + m_RegDisplay.pX26 = thisFuncletPtrs.pX26; + m_RegDisplay.pX27 = thisFuncletPtrs.pX27; + m_RegDisplay.pX28 = thisFuncletPtrs.pX28; + m_RegDisplay.pFP = thisFuncletPtrs.pFP; + +#elif defined(UNIX_AMD64_ABI) + // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. + m_RegDisplay.pRbp = thisFuncletPtrs.pRbp; + m_RegDisplay.pRbx = thisFuncletPtrs.pRbx; + m_RegDisplay.pR12 = thisFuncletPtrs.pR12; + m_RegDisplay.pR13 = thisFuncletPtrs.pR13; + m_RegDisplay.pR14 = thisFuncletPtrs.pR14; + m_RegDisplay.pR15 = thisFuncletPtrs.pR15; + +#elif defined(TARGET_X86) || defined(TARGET_AMD64) + // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. + m_RegDisplay.pRbp = thisFuncletPtrs.pRbp; + m_RegDisplay.pRdi = thisFuncletPtrs.pRdi; + m_RegDisplay.pRsi = thisFuncletPtrs.pRsi; + m_RegDisplay.pRbx = thisFuncletPtrs.pRbx; +#ifdef TARGET_AMD64 + m_RegDisplay.pR12 = thisFuncletPtrs.pR12; + m_RegDisplay.pR13 = thisFuncletPtrs.pR13; + m_RegDisplay.pR14 = thisFuncletPtrs.pR14; + m_RegDisplay.pR15 = thisFuncletPtrs.pR15; +#endif // TARGET_AMD64 +#else + PORTABILITY_ASSERT("StackFrameIterator::UpdateFromExceptionDispatch"); +#endif +} + +#ifdef TARGET_AMD64 +typedef DPTR(Fp128) PTR_Fp128; +#endif + +// The invoke of a funclet is a bit special and requires an assembly thunk, but we don't want to break the +// stackwalk due to this. So this routine will unwind through the assembly thunks used to invoke funclets. +// It's also used to disambiguate exceptionally- and non-exceptionally-invoked funclets. +void StackFrameIterator::UnwindFuncletInvokeThunk() +{ + ASSERT((m_dwFlags & MethodStateCalculated) == 0); + +#if defined(USE_PORTABLE_HELPERS) // @TODO: CORERT: Currently no funclet invoke defined in a portable way + return; +#else // defined(USE_PORTABLE_HELPERS) + ASSERT(CategorizeUnadjustedReturnAddress(m_ControlPC) == InFuncletInvokeThunk); + + PTR_UIntNative SP; + +#ifdef TARGET_X86 + // First, unwind RhpCallFunclet + SP = (PTR_UIntNative)(m_RegDisplay.SP + 0x4); // skip the saved assembly-routine-EBP + m_RegDisplay.SetAddrOfIP(SP); + m_RegDisplay.SetIP(*SP++); + m_RegDisplay.SetSP((uintptr_t)dac_cast(SP)); + SetControlPC(dac_cast(*(m_RegDisplay.pIP))); + + ASSERT( + EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2) || + EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallFinallyFunclet2) || + EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallFilterFunclet2) + ); +#endif + + bool isFilterInvoke = EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallFilterFunclet2); + +#if defined(UNIX_AMD64_ABI) + SP = (PTR_UIntNative)(m_RegDisplay.SP); + + if (isFilterInvoke) + { + SP++; // stack alignment + } + else + { + // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. + m_funcletPtrs.pRbp = m_RegDisplay.pRbp; + m_funcletPtrs.pRbx = m_RegDisplay.pRbx; + m_funcletPtrs.pR12 = m_RegDisplay.pR12; + m_funcletPtrs.pR13 = m_RegDisplay.pR13; + m_funcletPtrs.pR14 = m_RegDisplay.pR14; + m_funcletPtrs.pR15 = m_RegDisplay.pR15; + + if (EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2)) + { + SP += 6 + 1; // 6 locals and stack alignment + } + else + { + SP += 3; // 3 locals + } + } + + m_RegDisplay.pRbp = SP++; + m_RegDisplay.pRbx = SP++; + m_RegDisplay.pR12 = SP++; + m_RegDisplay.pR13 = SP++; + m_RegDisplay.pR14 = SP++; + m_RegDisplay.pR15 = SP++; +#elif defined(TARGET_AMD64) + static const int ArgumentsScratchAreaSize = 4 * 8; + + PTR_Fp128 xmm = (PTR_Fp128)(m_RegDisplay.SP + ArgumentsScratchAreaSize); + + for (int i = 0; i < 10; i++) + { + m_RegDisplay.Xmm[i] = *xmm++; + } + + SP = (PTR_UIntNative)xmm; + + if (isFilterInvoke) + { + SP++; // stack alignment + } + else + { + // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. + m_funcletPtrs.pRbp = m_RegDisplay.pRbp; + m_funcletPtrs.pRdi = m_RegDisplay.pRdi; + m_funcletPtrs.pRsi = m_RegDisplay.pRsi; + m_funcletPtrs.pRbx = m_RegDisplay.pRbx; + m_funcletPtrs.pR12 = m_RegDisplay.pR12; + m_funcletPtrs.pR13 = m_RegDisplay.pR13; + m_funcletPtrs.pR14 = m_RegDisplay.pR14; + m_funcletPtrs.pR15 = m_RegDisplay.pR15; + + if (EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2)) + { + SP += 3; // 3 locals + } + else + { + SP++; // 1 local + } + } + + m_RegDisplay.pRbp = SP++; + m_RegDisplay.pRdi = SP++; + m_RegDisplay.pRsi = SP++; + m_RegDisplay.pRbx = SP++; + m_RegDisplay.pR12 = SP++; + m_RegDisplay.pR13 = SP++; + m_RegDisplay.pR14 = SP++; + m_RegDisplay.pR15 = SP++; + +#elif defined(TARGET_X86) + SP = (PTR_UIntNative)(m_RegDisplay.SP); + + if (!isFilterInvoke) + { + // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. + m_funcletPtrs.pRbp = m_RegDisplay.pRbp; + m_funcletPtrs.pRdi = m_RegDisplay.pRdi; + m_funcletPtrs.pRsi = m_RegDisplay.pRsi; + m_funcletPtrs.pRbx = m_RegDisplay.pRbx; + } + + if (EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2)) + { + SP += 2; // 2 locals + } + else + { + SP++; // 1 local + } + m_RegDisplay.pRdi = SP++; + m_RegDisplay.pRsi = SP++; + m_RegDisplay.pRbx = SP++; + m_RegDisplay.pRbp = SP++; +#elif defined(TARGET_ARM) + + PTR_UInt64 d = (PTR_UInt64)(m_RegDisplay.SP); + + for (int i = 0; i < 8; i++) + { + m_RegDisplay.D[i] = *d++; + } + + SP = (PTR_UIntNative)d; + + if (!isFilterInvoke) + { + // RhpCallCatchFunclet puts a couple of extra things on the stack that aren't put there by the other two + // thunks, but we don't need to know what they are here, so we just skip them. + SP += EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2) ? 3 : 1; + + // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. + m_funcletPtrs.pR4 = m_RegDisplay.pR4; + m_funcletPtrs.pR5 = m_RegDisplay.pR5; + m_funcletPtrs.pR6 = m_RegDisplay.pR6; + m_funcletPtrs.pR7 = m_RegDisplay.pR7; + m_funcletPtrs.pR8 = m_RegDisplay.pR8; + m_funcletPtrs.pR9 = m_RegDisplay.pR9; + m_funcletPtrs.pR10 = m_RegDisplay.pR10; + m_funcletPtrs.pR11 = m_RegDisplay.pR11; + } + + m_RegDisplay.pR4 = SP++; + m_RegDisplay.pR5 = SP++; + m_RegDisplay.pR6 = SP++; + m_RegDisplay.pR7 = SP++; + m_RegDisplay.pR8 = SP++; + m_RegDisplay.pR9 = SP++; + m_RegDisplay.pR10 = SP++; + m_RegDisplay.pR11 = SP++; + +#elif defined(TARGET_ARM64) + PTR_UInt64 d = (PTR_UInt64)(m_RegDisplay.SP); + + for (int i = 0; i < 8; i++) + { + m_RegDisplay.D[i] = *d++; + } + + SP = (PTR_UIntNative)d; + + if (!isFilterInvoke) + { + // RhpCallCatchFunclet puts a couple of extra things on the stack that aren't put there by the other two + // thunks, but we don't need to know what they are here, so we just skip them. + SP += EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2) ? 6 : 4; + + // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. + m_funcletPtrs.pX19 = m_RegDisplay.pX19; + m_funcletPtrs.pX20 = m_RegDisplay.pX20; + m_funcletPtrs.pX21 = m_RegDisplay.pX21; + m_funcletPtrs.pX22 = m_RegDisplay.pX22; + m_funcletPtrs.pX23 = m_RegDisplay.pX23; + m_funcletPtrs.pX24 = m_RegDisplay.pX24; + m_funcletPtrs.pX25 = m_RegDisplay.pX25; + m_funcletPtrs.pX26 = m_RegDisplay.pX26; + m_funcletPtrs.pX27 = m_RegDisplay.pX27; + m_funcletPtrs.pX28 = m_RegDisplay.pX28; + m_funcletPtrs.pFP = m_RegDisplay.pFP; + } + + m_RegDisplay.pFP = SP++; + + m_RegDisplay.SetAddrOfIP((PTR_PCODE)SP); + m_RegDisplay.SetIP(*SP++); + + m_RegDisplay.pX19 = SP++; + m_RegDisplay.pX20 = SP++; + m_RegDisplay.pX21 = SP++; + m_RegDisplay.pX22 = SP++; + m_RegDisplay.pX23 = SP++; + m_RegDisplay.pX24 = SP++; + m_RegDisplay.pX25 = SP++; + m_RegDisplay.pX26 = SP++; + m_RegDisplay.pX27 = SP++; + m_RegDisplay.pX28 = SP++; + +#else + SP = (PTR_UIntNative)(m_RegDisplay.SP); + ASSERT_UNCONDITIONALLY("NYI for this arch"); +#endif + +#if !defined(TARGET_ARM64) + m_RegDisplay.SetAddrOfIP((PTR_PCODE)SP); + m_RegDisplay.SetIP(*SP++); +#endif + + m_RegDisplay.SetSP((uintptr_t)dac_cast(SP)); + SetControlPC(dac_cast(*(m_RegDisplay.pIP))); + + // We expect to be called by the runtime's C# EH implementation, and since this function's notion of how + // to unwind through the stub is brittle relative to the stub itself, we want to check as soon as we can. + ASSERT(m_pInstance->FindCodeManagerByAddress(m_ControlPC) && "unwind from funclet invoke stub failed"); +#endif // defined(USE_PORTABLE_HELPERS) +} + +// For a given target architecture, the layout of this structure must precisely match the +// stack frame layout used by the associated architecture-specific RhpUniversalTransition +// implementation. +struct UniversalTransitionStackFrame +{ + +// In DAC builds, the "this" pointer refers to an object in the DAC host. +#define GET_POINTER_TO_FIELD(_FieldName) \ + (PTR_UIntNative)PTR_HOST_MEMBER(UniversalTransitionStackFrame, this, _FieldName) + +#if defined(UNIX_AMD64_ABI) + + // Conservative GC reporting must be applied to everything between the base of the + // ReturnBlock and the top of the StackPassedArgs. +private: + Fp128 m_fpArgRegs[8]; // ChildSP+000 CallerSP-0D0 (0x80 bytes) (xmm0-xmm7) + uintptr_t m_returnBlock[2]; // ChildSP+080 CallerSP-050 (0x10 bytes) + uintptr_t m_intArgRegs[6]; // ChildSP+090 CallerSP-040 (0x30 bytes) (rdi,rsi,rcx,rdx,r8,r9) + uintptr_t m_alignmentPad; // ChildSP+0C0 CallerSP-010 (0x8 bytes) + uintptr_t m_callerRetaddr; // ChildSP+0C8 CallerSP-008 (0x8 bytes) + uintptr_t m_stackPassedArgs[1]; // ChildSP+0D0 CallerSP+000 (unknown size) + +public: + PTR_UIntNative get_CallerSP() { return GET_POINTER_TO_FIELD(m_stackPassedArgs[0]); } + PTR_UIntNative get_AddressOfPushedCallerIP() { return GET_POINTER_TO_FIELD(m_callerRetaddr); } + PTR_UIntNative get_LowerBoundForConservativeReporting() { return GET_POINTER_TO_FIELD(m_returnBlock[0]); } + + void UnwindNonVolatileRegisters(REGDISPLAY * pRegisterSet) + { + // RhpUniversalTransition does not touch any non-volatile state on amd64. + UNREFERENCED_PARAMETER(pRegisterSet); + } + +#elif defined(TARGET_AMD64) + + // Conservative GC reporting must be applied to everything between the base of the + // ReturnBlock and the top of the StackPassedArgs. +private: + uintptr_t m_calleeArgumentHomes[4]; // ChildSP+000 CallerSP-080 (0x20 bytes) + Fp128 m_fpArgRegs[4]; // ChildSP+020 CallerSP-060 (0x40 bytes) (xmm0-xmm3) + uintptr_t m_returnBlock[2]; // ChildSP+060 CallerSP-020 (0x10 bytes) + uintptr_t m_alignmentPad; // ChildSP+070 CallerSP-010 (0x8 bytes) + uintptr_t m_callerRetaddr; // ChildSP+078 CallerSP-008 (0x8 bytes) + uintptr_t m_intArgRegs[4]; // ChildSP+080 CallerSP+000 (0x20 bytes) (rcx,rdx,r8,r9) + uintptr_t m_stackPassedArgs[1]; // ChildSP+0a0 CallerSP+020 (unknown size) + +public: + PTR_UIntNative get_CallerSP() { return GET_POINTER_TO_FIELD(m_intArgRegs[0]); } + PTR_UIntNative get_AddressOfPushedCallerIP() { return GET_POINTER_TO_FIELD(m_callerRetaddr); } + PTR_UIntNative get_LowerBoundForConservativeReporting() { return GET_POINTER_TO_FIELD(m_returnBlock[0]); } + + void UnwindNonVolatileRegisters(REGDISPLAY * pRegisterSet) + { + // RhpUniversalTransition does not touch any non-volatile state on amd64. + UNREFERENCED_PARAMETER(pRegisterSet); + } + +#elif defined(TARGET_ARM) + + // Conservative GC reporting must be applied to everything between the base of the + // ReturnBlock and the top of the StackPassedArgs. +private: + uintptr_t m_pushedR11; // ChildSP+000 CallerSP-078 (0x4 bytes) (r11) + uintptr_t m_pushedLR; // ChildSP+004 CallerSP-074 (0x4 bytes) (lr) + uint64_t m_fpArgRegs[8]; // ChildSP+008 CallerSP-070 (0x40 bytes) (d0-d7) + uint64_t m_returnBlock[4]; // ChildSP+048 CallerSP-030 (0x20 bytes) + uintptr_t m_intArgRegs[4]; // ChildSP+068 CallerSP-010 (0x10 bytes) (r0-r3) + uintptr_t m_stackPassedArgs[1]; // ChildSP+078 CallerSP+000 (unknown size) + +public: + PTR_UIntNative get_CallerSP() { return GET_POINTER_TO_FIELD(m_stackPassedArgs[0]); } + PTR_UIntNative get_AddressOfPushedCallerIP() { return GET_POINTER_TO_FIELD(m_pushedLR); } + PTR_UIntNative get_LowerBoundForConservativeReporting() { return GET_POINTER_TO_FIELD(m_returnBlock[0]); } + + void UnwindNonVolatileRegisters(REGDISPLAY * pRegisterSet) + { + pRegisterSet->pR11 = GET_POINTER_TO_FIELD(m_pushedR11); + } + +#elif defined(TARGET_X86) + + // Conservative GC reporting must be applied to everything between the base of the + // IntArgRegs and the top of the StackPassedArgs. +private: + uintptr_t m_intArgRegs[2]; // ChildSP+000 CallerSP-018 (0x8 bytes) (edx,ecx) + uintptr_t m_returnBlock[2]; // ChildSP+008 CallerSP-010 (0x8 bytes) + uintptr_t m_pushedEBP; // ChildSP+010 CallerSP-008 (0x4 bytes) + uintptr_t m_callerRetaddr; // ChildSP+014 CallerSP-004 (0x4 bytes) + uintptr_t m_stackPassedArgs[1]; // ChildSP+018 CallerSP+000 (unknown size) + +public: + PTR_UIntNative get_CallerSP() { return GET_POINTER_TO_FIELD(m_stackPassedArgs[0]); } + PTR_UIntNative get_AddressOfPushedCallerIP() { return GET_POINTER_TO_FIELD(m_callerRetaddr); } + PTR_UIntNative get_LowerBoundForConservativeReporting() { return GET_POINTER_TO_FIELD(m_intArgRegs[0]); } + + void UnwindNonVolatileRegisters(REGDISPLAY * pRegisterSet) + { + pRegisterSet->pRbp = GET_POINTER_TO_FIELD(m_pushedEBP); + } + +#elif defined(TARGET_ARM64) + + // Conservative GC reporting must be applied to everything between the base of the + // ReturnBlock and the top of the StackPassedArgs. +private: + uintptr_t m_pushedFP; // ChildSP+000 CallerSP-0C0 (0x08 bytes) (fp) + uintptr_t m_pushedLR; // ChildSP+008 CallerSP-0B8 (0x08 bytes) (lr) + uint64_t m_fpArgRegs[8]; // ChildSP+010 CallerSP-0B0 (0x40 bytes) (d0-d7) + uintptr_t m_returnBlock[4]; // ChildSP+050 CallerSP-070 (0x40 bytes) + uintptr_t m_intArgRegs[9]; // ChildSP+070 CallerSP-050 (0x48 bytes) (x0-x8) + uintptr_t m_alignmentPad; // ChildSP+0B8 CallerSP-008 (0x08 bytes) + uintptr_t m_stackPassedArgs[1]; // ChildSP+0C0 CallerSP+000 (unknown size) + +public: + PTR_UIntNative get_CallerSP() { return GET_POINTER_TO_FIELD(m_stackPassedArgs[0]); } + PTR_UIntNative get_AddressOfPushedCallerIP() { return GET_POINTER_TO_FIELD(m_pushedLR); } + PTR_UIntNative get_LowerBoundForConservativeReporting() { return GET_POINTER_TO_FIELD(m_returnBlock[0]); } + + void UnwindNonVolatileRegisters(REGDISPLAY * pRegisterSet) + { + pRegisterSet->pFP = GET_POINTER_TO_FIELD(m_pushedFP); + } +#elif defined(TARGET_WASM) +private: + // WASMTODO: #error NYI for this arch + uintptr_t m_stackPassedArgs[1]; // Placeholder +public: + PTR_UIntNative get_CallerSP() { PORTABILITY_ASSERT("@TODO: FIXME:WASM"); return NULL; } + PTR_UIntNative get_AddressOfPushedCallerIP() { PORTABILITY_ASSERT("@TODO: FIXME:WASM"); return NULL; } + PTR_UIntNative get_LowerBoundForConservativeReporting() { PORTABILITY_ASSERT("@TODO: FIXME:WASM"); return NULL; } + + void UnwindNonVolatileRegisters(REGDISPLAY * pRegisterSet) + { + UNREFERENCED_PARAMETER(pRegisterSet); + PORTABILITY_ASSERT("@TODO: FIXME:WASM"); + } +#else +#error NYI for this arch +#endif + +#undef GET_POINTER_TO_FIELD + +}; + +typedef DPTR(UniversalTransitionStackFrame) PTR_UniversalTransitionStackFrame; + +// NOTE: This function always publishes a non-NULL conservative stack range lower bound. +// +// NOTE: In x86 cases, the unwound callsite often uses a calling convention that expects some amount +// of stack-passed argument space to be callee-popped before control returns (or unwinds) to the +// callsite. Since the callsite signature (and thus the amount of callee-popped space) is unknown, +// the recovered SP does not account for the callee-popped space is therefore "wrong" for the +// purposes of unwind. This implies that any x86 function which calls into RhpUniversalTransition +// must have a frame pointer to ensure that the incorrect SP value is ignored and does not break the +// unwind. +void StackFrameIterator::UnwindUniversalTransitionThunk() +{ + ASSERT((m_dwFlags & MethodStateCalculated) == 0); + +#if defined(USE_PORTABLE_HELPERS) // @TODO: CORERT: Corresponding helper code is only defined in assembly code + return; +#else // defined(USE_PORTABLE_HELPERS) + ASSERT(CategorizeUnadjustedReturnAddress(m_ControlPC) == InUniversalTransitionThunk); + + // The current PC is within RhpUniversalTransition, so establish a view of the surrounding stack frame. + // NOTE: In DAC builds, the pointer will refer to a newly constructed object in the DAC host. + UniversalTransitionStackFrame * stackFrame = (PTR_UniversalTransitionStackFrame)m_RegDisplay.SP; + + stackFrame->UnwindNonVolatileRegisters(&m_RegDisplay); + + PTR_UIntNative addressOfPushedCallerIP = stackFrame->get_AddressOfPushedCallerIP(); + m_RegDisplay.SetAddrOfIP((PTR_PCODE)addressOfPushedCallerIP); + m_RegDisplay.SetIP(*addressOfPushedCallerIP); + m_RegDisplay.SetSP((uintptr_t)dac_cast(stackFrame->get_CallerSP())); + SetControlPC(dac_cast(*(m_RegDisplay.pIP))); + + // All universal transition cases rely on conservative GC reporting being applied to the + // full argument set that flowed into the call. Report the lower bound of this range (the + // caller will compute the upper bound). + PTR_UIntNative pLowerBound = stackFrame->get_LowerBoundForConservativeReporting(); + ASSERT(pLowerBound != NULL); + ASSERT(m_pConservativeStackRangeLowerBound == NULL); + m_pConservativeStackRangeLowerBound = pLowerBound; +#endif // defined(USE_PORTABLE_HELPERS) +} + +#ifdef TARGET_AMD64 +#define STACK_ALIGN_SIZE 16 +#elif defined(TARGET_ARM) +#define STACK_ALIGN_SIZE 8 +#elif defined(TARGET_ARM64) +#define STACK_ALIGN_SIZE 16 +#elif defined(TARGET_X86) +#define STACK_ALIGN_SIZE 4 +#elif defined(TARGET_WASM) +#define STACK_ALIGN_SIZE 4 +#endif + +#ifdef TARGET_AMD64 +struct CALL_DESCR_CONTEXT +{ + uintptr_t Rbp; + uintptr_t Rsi; + uintptr_t Rbx; + uintptr_t IP; +}; +#elif defined(TARGET_ARM) +struct CALL_DESCR_CONTEXT +{ + uintptr_t R4; + uintptr_t R5; + uintptr_t R7; + uintptr_t IP; +}; +#elif defined(TARGET_ARM64) +struct CALL_DESCR_CONTEXT +{ + uintptr_t FP; + uintptr_t IP; + uintptr_t X19; + uintptr_t X20; +}; +#elif defined(TARGET_X86) +struct CALL_DESCR_CONTEXT +{ + uintptr_t Rbx; + uintptr_t Rbp; + uintptr_t IP; +}; +#elif defined (TARGET_WASM) +struct CALL_DESCR_CONTEXT +{ + uintptr_t IP; +}; +#else +#error NYI - For this arch +#endif + +typedef DPTR(CALL_DESCR_CONTEXT) PTR_CALL_DESCR_CONTEXT; + +void StackFrameIterator::UnwindCallDescrThunk() +{ + ASSERT((m_dwFlags & MethodStateCalculated) == 0); + +#if defined(USE_PORTABLE_HELPERS) // @TODO: CORERT: Corresponding helper code is only defined in assembly code + return; +#else // defined(USE_PORTABLE_HELPERS) + ASSERT(CategorizeUnadjustedReturnAddress(m_ControlPC) == InCallDescrThunk); + + uintptr_t newSP; +#ifdef TARGET_AMD64 + // RBP points to the SP that we want to capture. (This arrangement allows for + // the arguments from this function to be loaded into memory with an adjustment + // to SP, like an alloca + newSP = *(PTR_UIntNative)m_RegDisplay.pRbp; + + PTR_CALL_DESCR_CONTEXT pContext = (PTR_CALL_DESCR_CONTEXT)newSP; + + m_RegDisplay.pRbp = PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, Rbp); + m_RegDisplay.pRsi = PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, Rsi); + m_RegDisplay.pRbx = PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, Rbx); + + // And adjust SP to be the state that it should be in just after returning from + // the CallDescrFunction + newSP += sizeof(CALL_DESCR_CONTEXT); +#elif defined(TARGET_ARM) + // R7 points to the SP that we want to capture. (This arrangement allows for + // the arguments from this function to be loaded into memory with an adjustment + // to SP, like an alloca + newSP = *(PTR_UIntNative)m_RegDisplay.pR7; + PTR_CALL_DESCR_CONTEXT pContext = (PTR_CALL_DESCR_CONTEXT)newSP; + + m_RegDisplay.pR4 = PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, R4); + m_RegDisplay.pR5 = PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, R5); + m_RegDisplay.pR7 = PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, R7); + + // And adjust SP to be the state that it should be in just after returning from + // the CallDescrFunction + newSP += sizeof(CALL_DESCR_CONTEXT); + +#elif defined(TARGET_ARM64) + // pFP points to the SP that we want to capture. (This arrangement allows for + // the arguments from this function to be loaded into memory with an adjustment + // to SP, like an alloca + newSP = *(PTR_UIntNative)m_RegDisplay.pFP; + PTR_CALL_DESCR_CONTEXT pContext = (PTR_CALL_DESCR_CONTEXT)newSP; + + m_RegDisplay.pX19 = PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, X19); + m_RegDisplay.pX20 = PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, X20); + + // And adjust SP to be the state that it should be in just after returning from + // the CallDescrFunction + newSP += sizeof(CALL_DESCR_CONTEXT); + +#elif defined(TARGET_X86) + // RBP points to the SP that we want to capture. (This arrangement allows for + // the arguments from this function to be loaded into memory with an adjustment + // to SP, like an alloca + newSP = *(PTR_UIntNative)m_RegDisplay.pRbp; + + PTR_CALL_DESCR_CONTEXT pContext = (PTR_CALL_DESCR_CONTEXT)(newSP - offsetof(CALL_DESCR_CONTEXT, Rbp)); + + m_RegDisplay.pRbp = PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, Rbp); + m_RegDisplay.pRbx = PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, Rbx); + + // And adjust SP to be the state that it should be in just after returning from + // the CallDescrFunction + newSP += sizeof(CALL_DESCR_CONTEXT) - offsetof(CALL_DESCR_CONTEXT, Rbp); + +#else + PORTABILITY_ASSERT("UnwindCallDescrThunk"); + PTR_CALL_DESCR_CONTEXT pContext = NULL; +#endif + + m_RegDisplay.SetAddrOfIP(PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, IP)); + m_RegDisplay.SetIP(pContext->IP); + m_RegDisplay.SetSP(newSP); + SetControlPC(dac_cast(pContext->IP)); + +#endif // defined(USE_PORTABLE_HELPERS) +} + +void StackFrameIterator::UnwindThrowSiteThunk() +{ + ASSERT((m_dwFlags & MethodStateCalculated) == 0); + +#if defined(USE_PORTABLE_HELPERS) // @TODO: CORERT: no portable version of throw helpers + return; +#else // defined(USE_PORTABLE_HELPERS) + ASSERT(CategorizeUnadjustedReturnAddress(m_ControlPC) == InThrowSiteThunk); + + const uintptr_t STACKSIZEOF_ExInfo = ((sizeof(ExInfo) + (STACK_ALIGN_SIZE-1)) & ~(STACK_ALIGN_SIZE-1)); +#if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI) + const uintptr_t SIZEOF_OutgoingScratch = 0x20; +#else + const uintptr_t SIZEOF_OutgoingScratch = 0; +#endif + + PTR_PAL_LIMITED_CONTEXT pContext = (PTR_PAL_LIMITED_CONTEXT) + (m_RegDisplay.SP + SIZEOF_OutgoingScratch + STACKSIZEOF_ExInfo); + +#if defined(UNIX_AMD64_ABI) + m_RegDisplay.pRbp = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, Rbp); + m_RegDisplay.pRbx = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, Rbx); + m_RegDisplay.pR12 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R12); + m_RegDisplay.pR13 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R13); + m_RegDisplay.pR14 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R14); + m_RegDisplay.pR15 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R15); +#elif defined(TARGET_AMD64) + m_RegDisplay.pRbp = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, Rbp); + m_RegDisplay.pRdi = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, Rdi); + m_RegDisplay.pRsi = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, Rsi); + m_RegDisplay.pRbx = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, Rbx); + m_RegDisplay.pR12 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R12); + m_RegDisplay.pR13 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R13); + m_RegDisplay.pR14 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R14); + m_RegDisplay.pR15 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R15); +#elif defined(TARGET_ARM) + m_RegDisplay.pR4 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R4); + m_RegDisplay.pR5 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R5); + m_RegDisplay.pR6 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R6); + m_RegDisplay.pR7 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R7); + m_RegDisplay.pR8 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R8); + m_RegDisplay.pR9 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R9); + m_RegDisplay.pR10 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R10); + m_RegDisplay.pR11 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R11); +#elif defined(TARGET_ARM64) + m_RegDisplay.pX19 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X19); + m_RegDisplay.pX20 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X20); + m_RegDisplay.pX21 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X21); + m_RegDisplay.pX22 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X22); + m_RegDisplay.pX23 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X23); + m_RegDisplay.pX24 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X24); + m_RegDisplay.pX25 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X25); + m_RegDisplay.pX26 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X26); + m_RegDisplay.pX27 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X27); + m_RegDisplay.pX28 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X28); + m_RegDisplay.pFP = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, FP); +#elif defined(TARGET_X86) + m_RegDisplay.pRbp = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, Rbp); + m_RegDisplay.pRdi = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, Rdi); + m_RegDisplay.pRsi = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, Rsi); + m_RegDisplay.pRbx = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, Rbx); +#else + ASSERT_UNCONDITIONALLY("NYI for this arch"); +#endif + + m_RegDisplay.SetAddrOfIP(PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, IP)); + m_RegDisplay.SetIP(pContext->IP); + m_RegDisplay.SetSP(pContext->GetSp()); + SetControlPC(dac_cast(pContext->IP)); + + // We expect the throw site to be in managed code, and since this function's notion of how to unwind + // through the stub is brittle relative to the stub itself, we want to check as soon as we can. + ASSERT(m_pInstance->FindCodeManagerByAddress(m_ControlPC) && "unwind from throw site stub failed"); +#endif // defined(USE_PORTABLE_HELPERS) +} + +bool StackFrameIterator::IsValid() +{ + return (m_ControlPC != 0); +} + +void StackFrameIterator::Next() +{ + NextInternal(); + STRESS_LOG1(LF_STACKWALK, LL_INFO10000, " %p\n", m_ControlPC); +} + +void StackFrameIterator::NextInternal() +{ +UnwindOutOfCurrentManagedFrame: + ASSERT(m_dwFlags & MethodStateCalculated); + m_dwFlags &= ~(ExCollide|MethodStateCalculated|UnwoundReversePInvoke); + ASSERT(IsValid()); + + m_pHijackedReturnValue = NULL; + m_HijackedReturnValueKind = GCRK_Unknown; + +#ifdef _DEBUG + SetControlPC(dac_cast((void*)666)); +#endif // _DEBUG + + // Clear any preceding published conservative range. The current unwind will compute a new range + // from scratch if one is needed. + m_pConservativeStackRangeLowerBound = NULL; + m_pConservativeStackRangeUpperBound = NULL; + +#if defined(_DEBUG) && !defined(DACCESS_COMPILE) + uintptr_t DEBUG_preUnwindSP = m_RegDisplay.GetSP(); +#endif + + PTR_VOID pPreviousTransitionFrame; + FAILFAST_OR_DAC_FAIL(GetCodeManager()->UnwindStackFrame(&m_methodInfo, &m_RegDisplay, &pPreviousTransitionFrame)); + + bool doingFuncletUnwind = GetCodeManager()->IsFunclet(&m_methodInfo); + + if (pPreviousTransitionFrame != NULL) + { + ASSERT(!doingFuncletUnwind); + + if (pPreviousTransitionFrame == TOP_OF_STACK_MARKER) + { + SetControlPC(0); + } + else + { + // NOTE: If this is an EH stack walk, then reinitializing the iterator using the GC stack + // walk flags is incorrect. That said, this is OK because the exception dispatcher will + // immediately trigger a failfast when it sees the UnwoundReversePInvoke flag. + // NOTE: This can generate a conservative stack range if the recovered PInvoke callsite + // resides in an assembly thunk and not in normal managed code. In this case InternalInit + // will unwind through the thunk and back to the nearest managed frame, and therefore may + // see a conservative range reported by one of the thunks encountered during this "nested" + // unwind. + InternalInit(m_pThread, GetPInvokeTransitionFrame(pPreviousTransitionFrame), GcStackWalkFlags); + ASSERT(m_pInstance->FindCodeManagerByAddress(m_ControlPC)); + } + m_dwFlags |= UnwoundReversePInvoke; + } + else + { + // if the thread is safe to walk, it better not have a hijack in place. + ASSERT((ThreadStore::GetCurrentThread() == m_pThread) || !m_pThread->DangerousCrossThreadIsHijacked()); + + SetControlPC(dac_cast(*(m_RegDisplay.GetAddrOfIP()))); + + PTR_VOID collapsingTargetFrame = NULL; + + // Starting from the unwound return address, unwind further (if needed) until reaching + // either the next managed frame (i.e., the next frame that should be yielded from the + // stack frame iterator) or a collision point that requires complex handling. + + bool exCollide = false; + ReturnAddressCategory category = CategorizeUnadjustedReturnAddress(m_ControlPC); + + if (doingFuncletUnwind) + { + ASSERT(m_pendingFuncletFramePointer == NULL); + ASSERT(m_FramePointer != NULL); + + if (category == InFuncletInvokeThunk) + { + // The iterator is unwinding out of an exceptionally invoked funclet. Before proceeding, + // record the funclet frame pointer so that the iterator can verify that the remainder of + // the stack walk encounters "owner frames" (i.e., parent funclets or the main code body) + // in the expected order. + // NOTE: m_pendingFuncletFramePointer will be cleared by HandleExCollide the stack walk + // collides with the ExInfo that invoked this funclet. + m_pendingFuncletFramePointer = m_FramePointer; + + // Unwind through the funclet invoke assembly thunk to reach the topmost managed frame in + // the exception dispatch code. All non-GC stack walks collide at this point (whereas GC + // stack walks collide at the throw site which is reached after processing all of the + // exception dispatch frames). + UnwindFuncletInvokeThunk(); + if (!(m_dwFlags & CollapseFunclets)) + { + exCollide = true; + } + } + else if (category == InManagedCode) + { + // Non-exceptionally invoked funclet case. The caller is processed as a normal managed + // frame, with the caveat that funclet collapsing must be applied in GC stack walks (since + // the caller is either a parent funclet or the main code body and the leafmost funclet + // already provided GC information for the entire function). + if (m_dwFlags & CollapseFunclets) + { + collapsingTargetFrame = m_FramePointer; + } + } + else + { + FAILFAST_OR_DAC_FAIL_UNCONDITIONALLY("Unexpected thunk encountered when unwinding out of a funclet."); + } + } + else if (category != InManagedCode) + { + // Unwinding the current (non-funclet) managed frame revealed that its caller is one of the + // well-known assembly thunks. Unwind through the thunk to find the next managed frame + // that should be yielded from the stack frame iterator. + // NOTE: It is generally possible for a sequence of multiple thunks to appear "on top of + // each other" on the stack (e.g., the CallDescrThunk can be used to invoke the + // UniversalTransitionThunk), but EH thunks can never appear in such sequences. + + if (IsNonEHThunk(category)) + { + // Unwind the current sequence of one or more thunks until the next managed frame is reached. + // NOTE: This can generate a conservative stack range if one or more of the thunks in the + // sequence report a conservative lower bound. + UnwindNonEHThunkSequence(); + } + else if (category == InThrowSiteThunk) + { + // EH stack walks collide at the funclet invoke thunk and are never expected to encounter + // throw sites (except in illegal cases such as exceptions escaping from the managed + // exception dispatch code itself). + FAILFAST_OR_DAC_FAIL_MSG(!(m_dwFlags & ApplyReturnAddressAdjustment), + "EH stack walk is attempting to propagate an exception across a throw site."); + + UnwindThrowSiteThunk(); + + if (m_dwFlags & CollapseFunclets) + { + uintptr_t postUnwindSP = m_RegDisplay.SP; + + if (m_pNextExInfo && (postUnwindSP > ((uintptr_t)dac_cast(m_pNextExInfo)))) + { + // This GC stack walk has processed all managed exception frames associated with the + // current throw site, meaning it has now collided with the associated ExInfo. + exCollide = true; + } + } + } + else + { + FAILFAST_OR_DAC_FAIL_UNCONDITIONALLY("Unexpected thunk encountered when unwinding out of a non-funclet."); + } + } + + if (exCollide) + { + // OK, so we just hit (collided with) an exception throw point. We continue by consulting the + // ExInfo. + + // In the GC stackwalk, this means walking all the way off the end of the managed exception + // dispatch code to the throw site. In the EH stackwalk, this means hitting the special funclet + // invoke ASM thunks. + + // Double-check that the ExInfo that is being consulted is at or below the 'current' stack pointer + ASSERT(DEBUG_preUnwindSP <= (uintptr_t)m_pNextExInfo); + + ASSERT(collapsingTargetFrame == NULL); + + collapsingTargetFrame = HandleExCollide(m_pNextExInfo); + } + + // Now that all assembly thunks and ExInfo collisions have been processed, it is guaranteed + // that the next managed frame has been located. The located frame must now be yielded + // from the iterator with the one and only exception being cases where a managed frame must + // be skipped due to funclet collapsing. + + ASSERT(m_pInstance->FindCodeManagerByAddress(m_ControlPC)); + + if (collapsingTargetFrame != NULL) + { + // The iterator is positioned on a parent funclet or main code body in a function where GC + // information has already been reported by the leafmost funclet, implying that the current + // frame needs to be skipped by the GC stack walk. In general, the GC stack walk must skip + // all parent frames that are "part of the same function" (i.e., have the same frame + // pointer). + ASSERT(m_dwFlags & CollapseFunclets); + CalculateCurrentMethodState(); + ASSERT(IsValid()); + FAILFAST_OR_DAC_FAIL(m_FramePointer == collapsingTargetFrame); + + // Fail if the skipped frame has no associated conservative stack range (since any + // attached stack range is about to be dropped without ever being reported to the GC). + // This should never happen since funclet collapsing cases and only triggered when + // unwinding out of managed frames and never when unwinding out of the thunks that report + // conservative ranges. + FAILFAST_OR_DAC_FAIL(m_pConservativeStackRangeLowerBound == NULL); + + STRESS_LOG0(LF_STACKWALK, LL_INFO10000, "[ KeepUnwinding ]\n"); + goto UnwindOutOfCurrentManagedFrame; + } + + // Before yielding this frame, indicate that it was located via an ExInfo collision as + // opposed to normal unwind. + if (exCollide) + m_dwFlags |= ExCollide; + } + + // At this point, the iterator is in an invalid state if there are no more managed frames + // on the current stack, and is otherwise positioned on the next managed frame to yield to + // the caller. + PrepareToYieldFrame(); +} + +// NOTE: This function will publish a non-NULL conservative stack range lower bound if and +// only if one or more of the thunks in the sequence report conservative stack ranges. +void StackFrameIterator::UnwindNonEHThunkSequence() +{ + ReturnAddressCategory category = CategorizeUnadjustedReturnAddress(m_ControlPC); + ASSERT(IsNonEHThunk(category)); + + // Unwind the current sequence of thunks until the next managed frame is reached, being + // careful to detect and aggregate any conservative stack ranges reported by the thunks. + PTR_UIntNative pLowestLowerBound = NULL; + PTR_UIntNative pPrecedingLowerBound = NULL; + while (category != InManagedCode) + { + ASSERT(m_pConservativeStackRangeLowerBound == NULL); + + if (category == InCallDescrThunk) + { + UnwindCallDescrThunk(); + } + else if (category == InUniversalTransitionThunk) + { + UnwindUniversalTransitionThunk(); + ASSERT(m_pConservativeStackRangeLowerBound != NULL); + } + else + { + FAILFAST_OR_DAC_FAIL_UNCONDITIONALLY("Unexpected thunk encountered when unwinding a non-EH thunk sequence."); + } + + if (m_pConservativeStackRangeLowerBound != NULL) + { + // The newly unwound thunk reported a conservative stack range lower bound. The thunk + // sequence being unwound needs to generate a single conservative range that will be + // reported along with the managed frame eventually yielded by the iterator. To ensure + // sufficient reporting, this range always extends from the first (i.e., lowest) lower + // bound all the way to the top of the outgoing arguments area in the next managed frame. + // This aggregate range therefore covers all intervening thunk frames (if any), and also + // covers all necessary conservative ranges in the pathological case where a sequence of + // thunks contains multiple frames which report distinct conservative lower bound values. + // + // Capture the initial lower bound, and assert that the lower bound values are compatible + // with the "aggregate range" approach described above (i.e., that they never exceed the + // unwound thunk's stack frame and are always larger than all previously encountered lower + // bound values). + + if (pLowestLowerBound == NULL) + pLowestLowerBound = m_pConservativeStackRangeLowerBound; + + FAILFAST_OR_DAC_FAIL(m_pConservativeStackRangeLowerBound < (PTR_UIntNative)m_RegDisplay.SP); + FAILFAST_OR_DAC_FAIL(m_pConservativeStackRangeLowerBound > pPrecedingLowerBound); + pPrecedingLowerBound = m_pConservativeStackRangeLowerBound; + m_pConservativeStackRangeLowerBound = NULL; + } + + category = CategorizeUnadjustedReturnAddress(m_ControlPC); + } + + // The iterator has reached the next managed frame. Publish the computed lower bound value. + ASSERT(m_pConservativeStackRangeLowerBound == NULL); + m_pConservativeStackRangeLowerBound = pLowestLowerBound; +} + +// This function is called immediately before a given frame is yielded from the iterator +// (i.e., before a given frame is exposed outside of the iterator). At yield points, +// iterator must either be invalid (indicating that all managed frames have been processed) +// or must describe a valid managed frame. In the latter case, some common postprocessing +// steps must always be applied before the frame is exposed outside of the iterator. +void StackFrameIterator::PrepareToYieldFrame() +{ + if (!IsValid()) + return; + + ASSERT(m_pInstance->FindCodeManagerByAddress(m_ControlPC)); + + if (m_dwFlags & ApplyReturnAddressAdjustment) + { + m_ControlPC = AdjustReturnAddressBackward(m_ControlPC); + } + + m_ShouldSkipRegularGcReporting = false; + + // Each time a managed frame is yielded, configure the iterator to explicitly indicate + // whether or not unwinding to the current frame has revealed a stack range that must be + // conservatively reported by the GC. + if ((m_pConservativeStackRangeLowerBound != NULL) && (m_dwFlags & CollapseFunclets)) + { + // Conservatively reported stack ranges always correspond to the full extent of the + // argument set (including stack-passed arguments and spilled argument registers) that + // flowed into a managed callsite which called into the runtime. The runtime has no + // knowledge of the callsite signature in these cases, and unwind through these callsites + // is only possible via the associated assembly thunk (e.g., the ManagedCalloutThunk or + // UniversalTransitionThunk). + // + // The iterator is currently positioned on the managed frame which contains the callsite of + // interest. The lower bound of the argument set was already computed while unwinding + // through the assembly thunk. The upper bound of the argument set is always at or below + // the top of the outgoing arguments area in the current managed frame (i.e., in the + // managed frame which contains the callsite). + // + // Compute a conservative upper bound and then publish the total range so that it can be + // observed by the current GC stack walk (via HasStackRangeToReportConservatively). Note + // that the upper bound computation never mutates m_RegDisplay. + CalculateCurrentMethodState(); + ASSERT(IsValid()); + + uintptr_t rawUpperBound = GetCodeManager()->GetConservativeUpperBoundForOutgoingArgs(&m_methodInfo, &m_RegDisplay); + m_pConservativeStackRangeUpperBound = (PTR_UIntNative)rawUpperBound; + + ASSERT(m_pConservativeStackRangeLowerBound != NULL); + ASSERT(m_pConservativeStackRangeUpperBound != NULL); + ASSERT(m_pConservativeStackRangeUpperBound > m_pConservativeStackRangeLowerBound); + } + else + { + m_pConservativeStackRangeLowerBound = NULL; + m_pConservativeStackRangeUpperBound = NULL; + } +} + +REGDISPLAY * StackFrameIterator::GetRegisterSet() +{ + ASSERT(IsValid()); + return &m_RegDisplay; +} + +PTR_VOID StackFrameIterator::GetEffectiveSafePointAddress() +{ + ASSERT(IsValid()); + return m_effectiveSafePointAddress; +} + +PTR_ICodeManager StackFrameIterator::GetCodeManager() +{ + ASSERT(IsValid()); + return m_pCodeManager; +} + +MethodInfo * StackFrameIterator::GetMethodInfo() +{ + ASSERT(IsValid()); + return &m_methodInfo; +} + +#ifdef DACCESS_COMPILE +#define FAILFAST_OR_DAC_RETURN_FALSE(x) if(!(x)) return false; +#else +#define FAILFAST_OR_DAC_RETURN_FALSE(x) if(!(x)) { ASSERT_UNCONDITIONALLY(#x); RhFailFast(); } +#endif + +void StackFrameIterator::CalculateCurrentMethodState() +{ + if (m_dwFlags & MethodStateCalculated) + return; + + // Assume that the caller is likely to be in the same module + if (m_pCodeManager == NULL || !m_pCodeManager->FindMethodInfo(m_ControlPC, &m_methodInfo)) + { + m_pCodeManager = dac_cast(m_pInstance->FindCodeManagerByAddress(m_ControlPC)); + FAILFAST_OR_DAC_FAIL(m_pCodeManager); + + FAILFAST_OR_DAC_FAIL(m_pCodeManager->FindMethodInfo(m_ControlPC, &m_methodInfo)); + } + + m_effectiveSafePointAddress = m_ControlPC; + m_FramePointer = GetCodeManager()->GetFramePointer(&m_methodInfo, &m_RegDisplay); + + m_dwFlags |= MethodStateCalculated; +} + +bool StackFrameIterator::GetHijackedReturnValueLocation(PTR_RtuObjectRef * pLocation, GCRefKind * pKind) +{ + if (GCRK_Unknown == m_HijackedReturnValueKind) + return false; + + ASSERT((GCRK_Scalar < m_HijackedReturnValueKind) && (m_HijackedReturnValueKind <= GCRK_LastValid)); + + *pLocation = m_pHijackedReturnValue; + *pKind = m_HijackedReturnValueKind; + return true; +} + +void StackFrameIterator::SetControlPC(PTR_VOID controlPC) +{ + m_OriginalControlPC = m_ControlPC = controlPC; +} + +bool StackFrameIterator::IsNonEHThunk(ReturnAddressCategory category) +{ + switch (category) + { + default: + return false; + case InUniversalTransitionThunk: + case InCallDescrThunk: + return true; + } +} + +bool StackFrameIterator::IsValidReturnAddress(PTR_VOID pvAddress) +{ + // These are return addresses into functions that call into managed (non-funclet) code, so we might see + // them as hijacked return addresses. + ReturnAddressCategory category = CategorizeUnadjustedReturnAddress(pvAddress); + + // All non-EH thunks call out to normal managed code, implying that return addresses into + // them can be hijacked. + if (IsNonEHThunk(category)) + return true; + + // Throw site thunks call out to managed code, but control never returns from the managed + // callee. As a result, return addresses into these thunks can be hijacked, but the + // hijacks will never execute. + if (category == InThrowSiteThunk) + return true; + + return (NULL != GetRuntimeInstance()->FindCodeManagerByAddress(pvAddress)); +} + +// Support for conservatively reporting GC references in a stack range. This is used when managed methods with +// an unknown signature potentially including GC references call into the runtime and we need to let a GC +// proceed (typically because we call out into managed code again). Instead of storing signature metadata for +// every possible managed method that might make such a call we identify a small range of the stack that might +// contain outgoing arguments. We then report every pointer that looks like it might refer to the GC heap as a +// fixed interior reference. + +bool StackFrameIterator::HasStackRangeToReportConservatively() +{ + // When there's no range to report both the lower and upper bounds will be NULL. + return IsValid() && (m_pConservativeStackRangeUpperBound != NULL); +} + +void StackFrameIterator::GetStackRangeToReportConservatively(PTR_RtuObjectRef * ppLowerBound, PTR_RtuObjectRef * ppUpperBound) +{ + ASSERT(HasStackRangeToReportConservatively()); + *ppLowerBound = (PTR_RtuObjectRef)m_pConservativeStackRangeLowerBound; + *ppUpperBound = (PTR_RtuObjectRef)m_pConservativeStackRangeUpperBound; +} + +PTR_VOID StackFrameIterator::AdjustReturnAddressBackward(PTR_VOID controlPC) +{ +#ifdef TARGET_ARM + return (PTR_VOID)(((PTR_UInt8)controlPC) - 2); +#elif defined(TARGET_ARM64) + return (PTR_VOID)(((PTR_UInt8)controlPC) - 4); +#else + return (PTR_VOID)(((PTR_UInt8)controlPC) - 1); +#endif +} + +// Given a return address, determine the category of function where it resides. In +// general, return addresses encountered by the stack walker are required to reside in +// managed code unless they reside in one of the well-known assembly thunks. + +// static +StackFrameIterator::ReturnAddressCategory StackFrameIterator::CategorizeUnadjustedReturnAddress(PTR_VOID returnAddress) +{ +#if defined(USE_PORTABLE_HELPERS) // @TODO: CORERT: no portable thunks are defined + + return InManagedCode; + +#else // defined(USE_PORTABLE_HELPERS) + +#if defined(FEATURE_DYNAMIC_CODE) + if (EQUALS_RETURN_ADDRESS(returnAddress, ReturnFromCallDescrThunk)) + { + return InCallDescrThunk; + } + else if (EQUALS_RETURN_ADDRESS(returnAddress, ReturnFromUniversalTransition) || + EQUALS_RETURN_ADDRESS(returnAddress, ReturnFromUniversalTransition_DebugStepTailCall)) + { + return InUniversalTransitionThunk; + } +#endif + + if (EQUALS_RETURN_ADDRESS(returnAddress, RhpThrowEx2) || + EQUALS_RETURN_ADDRESS(returnAddress, RhpThrowHwEx2) || + EQUALS_RETURN_ADDRESS(returnAddress, RhpRethrow2)) + { + return InThrowSiteThunk; + } + + if ( +#ifdef TARGET_X86 + EQUALS_RETURN_ADDRESS(returnAddress, RhpCallFunclet2) +#else + EQUALS_RETURN_ADDRESS(returnAddress, RhpCallCatchFunclet2) || + EQUALS_RETURN_ADDRESS(returnAddress, RhpCallFinallyFunclet2) || + EQUALS_RETURN_ADDRESS(returnAddress, RhpCallFilterFunclet2) +#endif + ) + { + return InFuncletInvokeThunk; + } + + return InManagedCode; +#endif // defined(USE_PORTABLE_HELPERS) +} + +bool StackFrameIterator::ShouldSkipRegularGcReporting() +{ + return m_ShouldSkipRegularGcReporting; +} + +#ifndef DACCESS_COMPILE + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpSfiInit, (StackFrameIterator* pThis, PAL_LIMITED_CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault)) +{ + Thread * pCurThread = ThreadStore::GetCurrentThread(); + + // The stackwalker is intolerant to hijacked threads, as it is largely expecting to be called from C++ + // where the hijack state of the thread is invariant. Because we've exposed the iterator out to C#, we + // need to unhijack every time we callback into C++ because the thread could have been hijacked during our + // time executing C#. + pCurThread->Unhijack(); + + // Passing NULL is a special-case to request a standard managed stack trace for the current thread. + if (pStackwalkCtx == NULL) + pThis->InternalInitForStackTrace(); + else + pThis->InternalInitForEH(pCurThread, pStackwalkCtx, instructionFault); + + bool isValid = pThis->IsValid(); + if (isValid) + pThis->CalculateCurrentMethodState(); + FC_RETURN_BOOL(isValid); +} + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpSfiNext, (StackFrameIterator* pThis, uint32_t* puExCollideClauseIdx, CLR_BOOL* pfUnwoundReversePInvoke)) +{ + // The stackwalker is intolerant to hijacked threads, as it is largely expecting to be called from C++ + // where the hijack state of the thread is invariant. Because we've exposed the iterator out to C#, we + // need to unhijack every time we callback into C++ because the thread could have been hijacked during our + // time executing C#. + ThreadStore::GetCurrentThread()->Unhijack(); + + const uint32_t MaxTryRegionIdx = 0xFFFFFFFF; + + ExInfo * pCurExInfo = pThis->m_pNextExInfo; + pThis->Next(); + bool isValid = pThis->IsValid(); + if (isValid) + pThis->CalculateCurrentMethodState(); + + if (puExCollideClauseIdx != NULL) + { + if (pThis->m_dwFlags & StackFrameIterator::ExCollide) + { + ASSERT(pCurExInfo->m_idxCurClause != MaxTryRegionIdx); + *puExCollideClauseIdx = pCurExInfo->m_idxCurClause; + pCurExInfo->m_kind = (ExKind)(pCurExInfo->m_kind | EK_SupersededFlag); + } + else + { + *puExCollideClauseIdx = MaxTryRegionIdx; + } + } + + if (pfUnwoundReversePInvoke != NULL) + { + *pfUnwoundReversePInvoke = (pThis->m_dwFlags & StackFrameIterator::UnwoundReversePInvoke) != 0; + } + + FC_RETURN_BOOL(isValid); +} + +#endif // !DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h new file mode 100644 index 00000000000000..a5c826f4e00449 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h @@ -0,0 +1,211 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "ICodeManager.h" + +struct ExInfo; +typedef DPTR(ExInfo) PTR_ExInfo; +typedef VPTR(ICodeManager) PTR_ICodeManager; + +enum ExKind : uint8_t +{ + EK_HardwareFault = 2, + EK_SupersededFlag = 8, +}; + +struct EHEnum +{ + ICodeManager * m_pCodeManager; + EHEnumState m_state; +}; + +EXTERN_C FC_BOOL_RET FASTCALL RhpSfiInit(StackFrameIterator* pThis, PAL_LIMITED_CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault); +EXTERN_C FC_BOOL_RET FASTCALL RhpSfiNext(StackFrameIterator* pThis, uint32_t* puExCollideClauseIdx, CLR_BOOL* pfUnwoundReversePInvoke); + +struct PInvokeTransitionFrame; +typedef DPTR(PInvokeTransitionFrame) PTR_PInvokeTransitionFrame; +typedef DPTR(PAL_LIMITED_CONTEXT) PTR_PAL_LIMITED_CONTEXT; + +class StackFrameIterator +{ + friend class AsmOffsets; + friend FC_BOOL_RET FASTCALL RhpSfiInit(StackFrameIterator* pThis, PAL_LIMITED_CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault); + friend FC_BOOL_RET FASTCALL RhpSfiNext(StackFrameIterator* pThis, uint32_t* puExCollideClauseIdx, CLR_BOOL* pfUnwoundReversePInvoke); + +public: + StackFrameIterator() {} + StackFrameIterator(Thread * pThreadToWalk, PTR_VOID pInitialTransitionFrame); + StackFrameIterator(Thread * pThreadToWalk, PTR_PAL_LIMITED_CONTEXT pCtx); + + + bool IsValid(); + void CalculateCurrentMethodState(); + void Next(); + PTR_VOID GetEffectiveSafePointAddress(); + REGDISPLAY * GetRegisterSet(); + PTR_ICodeManager GetCodeManager(); + MethodInfo * GetMethodInfo(); + bool GetHijackedReturnValueLocation(PTR_RtuObjectRef * pLocation, GCRefKind * pKind); + void SetControlPC(PTR_VOID controlPC); + + static bool IsValidReturnAddress(PTR_VOID pvAddress); + + // Support for conservatively reporting GC references in a stack range. This is used when managed methods + // with an unknown signature potentially including GC references call into the runtime and we need to let + // a GC proceed (typically because we call out into managed code again). Instead of storing signature + // metadata for every possible managed method that might make such a call we identify a small range of the + // stack that might contain outgoing arguments. We then report every pointer that looks like it might + // refer to the GC heap as a fixed interior reference. + bool HasStackRangeToReportConservatively(); + void GetStackRangeToReportConservatively(PTR_RtuObjectRef * ppLowerBound, PTR_RtuObjectRef * ppUpperBound); + + // Debugger Hijacked frame looks very much like a usual managed frame except when the + // frame must be reported conservatively, and when that happens, regular GC reporting should be skipped + bool ShouldSkipRegularGcReporting(); + +private: + // The invoke of a funclet is a bit special and requires an assembly thunk, but we don't want to break the + // stackwalk due to this. So this routine will unwind through the assembly thunks used to invoke funclets. + // It's also used to disambiguate exceptionally- and non-exceptionally-invoked funclets. + void UnwindFuncletInvokeThunk(); + void UnwindThrowSiteThunk(); + + // If our control PC indicates that we're in the universal transition thunk that we use to generically + // dispatch arbitrary managed calls, then handle the stack walk specially. + // NOTE: This function always publishes a non-NULL conservative stack range lower bound. + void UnwindUniversalTransitionThunk(); + + // If our control PC indicates that we're in the call descr thunk that we use to call an arbitrary managed + // function with an arbitrary signature from a normal managed function handle the stack walk specially. + void UnwindCallDescrThunk(); + + void EnterInitialInvalidState(Thread * pThreadToWalk); + + void InternalInit(Thread * pThreadToWalk, PTR_PInvokeTransitionFrame pFrame, uint32_t dwFlags); // GC stackwalk + void InternalInit(Thread * pThreadToWalk, PTR_PAL_LIMITED_CONTEXT pCtx, uint32_t dwFlags); // EH and hijack stackwalk, and collided unwind + void InternalInitForEH(Thread * pThreadToWalk, PAL_LIMITED_CONTEXT * pCtx, bool instructionFault); // EH stackwalk + void InternalInitForStackTrace(); // Environment.StackTrace + + PTR_VOID HandleExCollide(PTR_ExInfo pExInfo); + void NextInternal(); + + // This will walk m_pNextExInfo from its current value until it finds the next ExInfo at a higher address + // than the SP reference value passed in. This is useful when 'restarting' the stackwalk from a + // particular PInvokeTransitionFrame or after we have a 'collided unwind' that may skip over ExInfos. + void ResetNextExInfoForSP(uintptr_t SP); + + void UpdateFromExceptionDispatch(PTR_StackFrameIterator pSourceIterator); + + // helpers to ApplyReturnAddressAdjustment + PTR_VOID AdjustReturnAddressForward(PTR_VOID controlPC); + PTR_VOID AdjustReturnAddressBackward(PTR_VOID controlPC); + + void UnwindNonEHThunkSequence(); + void PrepareToYieldFrame(); + + enum ReturnAddressCategory + { + InManagedCode, + InThrowSiteThunk, + InFuncletInvokeThunk, + InCallDescrThunk, + InUniversalTransitionThunk, + }; + + static ReturnAddressCategory CategorizeUnadjustedReturnAddress(PTR_VOID returnAddress); + static bool IsNonEHThunk(ReturnAddressCategory category); + + enum Flags + { + // If this flag is set, each unwind will apply a -1 to the ControlPC. This is used by EH to ensure + // that the ControlPC of a callsite stays within the containing try region. + ApplyReturnAddressAdjustment = 1, + + // Used by the GC stackwalk, this flag will ensure that multiple funclet frames for a given method + // activation will be given only one callback. The one callback is given for the most nested physical + // stack frame of a given activation of a method. (i.e. the leafmost funclet) + CollapseFunclets = 2, + + // This is a state returned by Next() which indicates that we just crossed an ExInfo in our unwind. + ExCollide = 4, + + // If a hardware fault frame is encountered, report its control PC at the binder-inserted GC safe + // point immediately after the prolog of the most nested enclosing try-region's handler. + RemapHardwareFaultsToSafePoint = 8, + + MethodStateCalculated = 0x10, + + // This is a state returned by Next() which indicates that we just unwound a reverse pinvoke method + UnwoundReversePInvoke = 0x20, + + GcStackWalkFlags = (CollapseFunclets | RemapHardwareFaultsToSafePoint), + EHStackWalkFlags = ApplyReturnAddressAdjustment, + StackTraceStackWalkFlags = GcStackWalkFlags + }; + + struct PreservedRegPtrs + { +#ifdef TARGET_ARM + PTR_UIntNative pR4; + PTR_UIntNative pR5; + PTR_UIntNative pR6; + PTR_UIntNative pR7; + PTR_UIntNative pR8; + PTR_UIntNative pR9; + PTR_UIntNative pR10; + PTR_UIntNative pR11; +#elif defined(TARGET_ARM64) + PTR_UIntNative pX19; + PTR_UIntNative pX20; + PTR_UIntNative pX21; + PTR_UIntNative pX22; + PTR_UIntNative pX23; + PTR_UIntNative pX24; + PTR_UIntNative pX25; + PTR_UIntNative pX26; + PTR_UIntNative pX27; + PTR_UIntNative pX28; + PTR_UIntNative pFP; +#elif defined(UNIX_AMD64_ABI) + PTR_UIntNative pRbp; + PTR_UIntNative pRbx; + PTR_UIntNative pR12; + PTR_UIntNative pR13; + PTR_UIntNative pR14; + PTR_UIntNative pR15; +#else // TARGET_ARM + PTR_UIntNative pRbp; + PTR_UIntNative pRdi; + PTR_UIntNative pRsi; + PTR_UIntNative pRbx; +#ifdef TARGET_AMD64 + PTR_UIntNative pR12; + PTR_UIntNative pR13; + PTR_UIntNative pR14; + PTR_UIntNative pR15; +#endif // TARGET_AMD64 +#endif // TARGET_ARM + }; + +protected: + Thread * m_pThread; + RuntimeInstance * m_pInstance; + PTR_VOID m_FramePointer; + PTR_VOID m_ControlPC; + REGDISPLAY m_RegDisplay; + PTR_ICodeManager m_pCodeManager; + MethodInfo m_methodInfo; + PTR_VOID m_effectiveSafePointAddress; + PTR_RtuObjectRef m_pHijackedReturnValue; + GCRefKind m_HijackedReturnValueKind; + PTR_UIntNative m_pConservativeStackRangeLowerBound; + PTR_UIntNative m_pConservativeStackRangeUpperBound; + uint32_t m_dwFlags; + PTR_ExInfo m_pNextExInfo; + PTR_VOID m_pendingFuncletFramePointer; + PreservedRegPtrs m_funcletPtrs; // @TODO: Placing the 'scratch space' in the StackFrameIterator is not + // preferred because not all StackFrameIterators require this storage + // space. However, the implementation simpler by doing it this way. + bool m_ShouldSkipRegularGcReporting; + PTR_VOID m_OriginalControlPC; +}; + diff --git a/src/coreclr/nativeaot/Runtime/SyncClean.cpp b/src/coreclr/nativeaot/Runtime/SyncClean.cpp new file mode 100644 index 00000000000000..927d3f5bf307ae --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/SyncClean.cpp @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "holder.h" +#include "SpinLock.h" +#include "rhbinder.h" +#include "CachedInterfaceDispatch.h" + +#include "SyncClean.hpp" + +void SyncClean::Terminate() +{ + CleanUp(); +} + +void SyncClean::CleanUp () +{ +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH + // Update any interface dispatch caches that were unsafe to modify outside of this GC. + ReclaimUnusedInterfaceDispatchCaches(); +#endif +} diff --git a/src/coreclr/nativeaot/Runtime/SyncClean.hpp b/src/coreclr/nativeaot/Runtime/SyncClean.hpp new file mode 100644 index 00000000000000..c9ea16263075e5 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/SyncClean.hpp @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _SYNCCLEAN_HPP_ +#define _SYNCCLEAN_HPP_ + +// We keep a list of memory blocks to be freed at the end of GC, but before we resume EE. +// To make this work, we need to make sure that these data are accessed in cooperative GC +// mode. + +class SyncClean { +public: + static void Terminate (); + static void CleanUp (); +}; + +#endif diff --git a/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp b/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp new file mode 100644 index 00000000000000..656521b66e706b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp @@ -0,0 +1,346 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" + +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "CommonMacros.inl" +#include "volatile.h" +#include "PalRedhawk.h" +#include "rhassert.h" + + +#ifdef FEATURE_RX_THUNKS + +#ifdef TARGET_AMD64 +#define THUNK_SIZE 20 +#elif TARGET_X86 +#define THUNK_SIZE 12 +#elif TARGET_ARM +#define THUNK_SIZE 20 +#elif TARGET_ARM64 +#define THUNK_SIZE 16 +#else +#define THUNK_SIZE (2 * OS_PAGE_SIZE) // This will cause RhpGetNumThunksPerBlock to return 0 +#endif + +static_assert((THUNK_SIZE % 4) == 0, "Thunk stubs size not aligned correctly. This will cause runtime failures."); + +#define THUNKS_MAP_SIZE 0x8000 // 32 K + +#ifdef TARGET_ARM +//***************************************************************************** +// Encode a 16-bit immediate mov/movt in ARM Thumb2 Instruction (format T2_N) +//***************************************************************************** +void EncodeThumb2Mov16(uint16_t * pCode, uint16_t value, uint8_t rDestination, bool topWord) +{ + pCode[0] = ((topWord ? 0xf2c0 : 0xf240) | + ((value >> 12) & 0x000f) | + ((value >> 1) & 0x0400)); + pCode[1] = (((value << 4) & 0x7000) | + (value & 0x00ff) | + (rDestination << 8)); +} + +//***************************************************************************** +// Encode a 32-bit immediate mov in ARM Thumb2 Instruction (format T2_N) +//***************************************************************************** +void EncodeThumb2Mov32(uint16_t * pCode, uint32_t value, uint8_t rDestination) +{ + EncodeThumb2Mov16(pCode, (uint16_t)(value & 0x0000ffff), rDestination, false); + EncodeThumb2Mov16(pCode + 2, (uint16_t)(value >> 16), rDestination, true); +} +#endif + +COOP_PINVOKE_HELPER(int, RhpGetNumThunkBlocksPerMapping, ()) +{ + static_assert((THUNKS_MAP_SIZE % OS_PAGE_SIZE) == 0, "Thunks map size should be in multiples of pages"); + + return THUNKS_MAP_SIZE / OS_PAGE_SIZE; +} + +COOP_PINVOKE_HELPER(int, RhpGetNumThunksPerBlock, ()) +{ + return min( + OS_PAGE_SIZE / THUNK_SIZE, // Number of thunks that can fit in a page + (OS_PAGE_SIZE - POINTER_SIZE) / (POINTER_SIZE * 2) // Number of pointer pairs, minus the jump stub cell, that can fit in a page + ); +} + +COOP_PINVOKE_HELPER(int, RhpGetThunkSize, ()) +{ + return THUNK_SIZE; +} + +COOP_PINVOKE_HELPER(void*, RhpGetThunkDataBlockAddress, (void* pThunkStubAddress)) +{ + return (void*)(((uintptr_t)pThunkStubAddress & ~(OS_PAGE_SIZE - 1)) + THUNKS_MAP_SIZE); +} + +COOP_PINVOKE_HELPER(void*, RhpGetThunkStubsBlockAddress, (void* pThunkDataAddress)) +{ + return (void*)(((uintptr_t)pThunkDataAddress & ~(OS_PAGE_SIZE - 1)) - THUNKS_MAP_SIZE); +} + +COOP_PINVOKE_HELPER(int, RhpGetThunkBlockSize, ()) +{ + return OS_PAGE_SIZE; +} + +EXTERN_C REDHAWK_API void* __cdecl RhAllocateThunksMapping() +{ +#ifdef WIN32 + + void * pNewMapping = PalVirtualAlloc(NULL, THUNKS_MAP_SIZE * 2, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (pNewMapping == NULL) + return NULL; + + void * pThunksSection = pNewMapping; + void * pDataSection = (uint8_t*)pNewMapping + THUNKS_MAP_SIZE; + +#else + + // Note: On secure linux systems, we can't add execute permissions to a mapped virtual memory if it was not created + // with execute permissions in the first place. This is why we create the virtual section with RX permissions, then + // reduce it to RW for the data section. For the stubs section we need to increase to RWX to generate the stubs + // instructions. After this we go back to RX for the stubs section before the stubs are used and should not be + // changed anymore. + void * pNewMapping = PalVirtualAlloc(NULL, THUNKS_MAP_SIZE * 2, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READ); + if (pNewMapping == NULL) + return NULL; + + void * pThunksSection = pNewMapping; + void * pDataSection = (uint8_t*)pNewMapping + THUNKS_MAP_SIZE; + + if (!PalVirtualProtect(pDataSection, THUNKS_MAP_SIZE, PAGE_READWRITE) || + !PalVirtualProtect(pThunksSection, THUNKS_MAP_SIZE, PAGE_EXECUTE_READWRITE)) + { + PalVirtualFree(pNewMapping, 0, MEM_RELEASE); + return NULL; + } + +#endif + + int numBlocksPerMap = RhpGetNumThunkBlocksPerMapping(); + int numThunksPerBlock = RhpGetNumThunksPerBlock(); + + for (int m = 0; m < numBlocksPerMap; m++) + { + uint8_t* pDataBlockAddress = (uint8_t*)pDataSection + m * OS_PAGE_SIZE; + uint8_t* pThunkBlockAddress = (uint8_t*)pThunksSection + m * OS_PAGE_SIZE; + + for (int i = 0; i < numThunksPerBlock; i++) + { + uint8_t* pCurrentThunkAddress = pThunkBlockAddress + THUNK_SIZE * i; + uint8_t* pCurrentDataAddress = pDataBlockAddress + i * POINTER_SIZE * 2; + +#ifdef TARGET_AMD64 + + // mov r10, + // jmp [r10 + + // jmp [eax + + // str r12,[sp,#-4] + // ldr r12,[r12, + //ldr xip1, [xip0, ] + //br xip1 + //brk 0xf000 //Stubs need to be 16 byte aligned therefore we fill with a break here + + int delta = (int)(pCurrentDataAddress - pCurrentThunkAddress); + *((uint32_t*)pCurrentThunkAddress) = 0x10000010 | (((delta & 0x03) << 29) | (((delta & 0x1FFFFC) >> 2) << 5)); + pCurrentThunkAddress += 4; + + *((uint32_t*)pCurrentThunkAddress) = 0xF9400211 | (((OS_PAGE_SIZE - POINTER_SIZE - (i * POINTER_SIZE * 2)) / 8) << 10); + pCurrentThunkAddress += 4; + + *((uint32_t*)pCurrentThunkAddress) = 0xD61F0220; + pCurrentThunkAddress += 4; + + *((uint32_t*)pCurrentThunkAddress) = 0xD43E0000; + pCurrentThunkAddress += 4; +#else + UNREFERENCED_PARAMETER(pCurrentDataAddress); + UNREFERENCED_PARAMETER(pCurrentThunkAddress); + PORTABILITY_ASSERT("RhAllocateThunksMapping"); +#endif + } + } + + if (!PalVirtualProtect(pThunksSection, THUNKS_MAP_SIZE, PAGE_EXECUTE_READ)) + { + PalVirtualFree(pNewMapping, 0, MEM_RELEASE); + return NULL; + } + + return pThunksSection; +} + +// FEATURE_RX_THUNKS +#elif FEATURE_FIXED_POOL_THUNKS +// This is used by the thunk code to find the stub data for the called thunk slot +extern "C" uintptr_t g_pThunkStubData; +uintptr_t g_pThunkStubData = NULL; + +COOP_PINVOKE_HELPER(int, RhpGetThunkBlockCount, ()); +COOP_PINVOKE_HELPER(int, RhpGetNumThunkBlocksPerMapping, ()); +COOP_PINVOKE_HELPER(int, RhpGetThunkBlockSize, ()); +COOP_PINVOKE_HELPER(void*, RhpGetThunkDataBlockAddress, (void* addr)); +COOP_PINVOKE_HELPER(void*, RhpGetThunkStubsBlockAddress, (void* addr)); + +EXTERN_C REDHAWK_API void* __cdecl RhAllocateThunksMapping() +{ + static int nextThunkDataMapping = 0; + + int thunkBlocksPerMapping = RhpGetNumThunkBlocksPerMapping(); + int thunkBlockSize = RhpGetThunkBlockSize(); + int blockCount = RhpGetThunkBlockCount(); + + ASSERT(blockCount % thunkBlocksPerMapping == 0) + + int thunkDataMappingSize = thunkBlocksPerMapping * thunkBlockSize; + int thunkDataMappingCount = blockCount / thunkBlocksPerMapping; + + if (nextThunkDataMapping == thunkDataMappingCount) + { + return NULL; + } + + if (g_pThunkStubData == NULL) + { + int thunkDataSize = thunkDataMappingSize * thunkDataMappingCount; + + g_pThunkStubData = (uintptr_t)PalVirtualAlloc(NULL, thunkDataSize, MEM_RESERVE, PAGE_READWRITE); + + if (g_pThunkStubData == NULL) + { + return NULL; + } + } + + void* pThunkDataBlock = (int8_t*)g_pThunkStubData + nextThunkDataMapping * thunkDataMappingSize; + + if (PalVirtualAlloc(pThunkDataBlock, thunkDataMappingSize, MEM_COMMIT, PAGE_READWRITE) == NULL) + { + return NULL; + } + + nextThunkDataMapping++; + + void* pThunks = RhpGetThunkStubsBlockAddress(pThunkDataBlock); + ASSERT(RhpGetThunkDataBlockAddress(pThunks) == pThunkDataBlock); + + return pThunks; +} + +#else // FEATURE_FIXED_POOL_THUNKS + +COOP_PINVOKE_HELPER(void*, RhpGetThunksBase, ()); +COOP_PINVOKE_HELPER(int, RhpGetNumThunkBlocksPerMapping, ()); +COOP_PINVOKE_HELPER(int, RhpGetNumThunksPerBlock, ()); +COOP_PINVOKE_HELPER(int, RhpGetThunkSize, ()); +COOP_PINVOKE_HELPER(int, RhpGetThunkBlockSize, ()); + +EXTERN_C REDHAWK_API void* __cdecl RhAllocateThunksMapping() +{ + static void* pThunksTemplateAddress = NULL; + + void *pThunkMap = NULL; + + int thunkBlocksPerMapping = RhpGetNumThunkBlocksPerMapping(); + int thunkBlockSize = RhpGetThunkBlockSize(); + int templateSize = thunkBlocksPerMapping * thunkBlockSize; + + if (pThunksTemplateAddress == NULL) + { + // First, we use the thunks directly from the thunks template sections in the module until all + // thunks in that template are used up. + pThunksTemplateAddress = RhpGetThunksBase(); + pThunkMap = pThunksTemplateAddress; + } + else + { + // We've already used the thunks template in the module for some previous thunks, and we + // cannot reuse it here. Now we need to create a new mapping of the thunks section in order to have + // more thunks + + uint8_t* pModuleBase = (uint8_t*)PalGetModuleHandleFromPointer(pThunksTemplateAddress); + int templateRva = (int)((uint8_t*)RhpGetThunksBase() - pModuleBase); + + if (!PalAllocateThunksFromTemplate((HANDLE)pModuleBase, templateRva, templateSize, &pThunkMap)) + return NULL; + } + + if (!PalMarkThunksAsValidCallTargets( + pThunkMap, + RhpGetThunkSize(), + RhpGetNumThunksPerBlock(), + thunkBlockSize, + thunkBlocksPerMapping)) + { + if (pThunkMap != pThunksTemplateAddress) + PalFreeThunksFromTemplate(pThunkMap); + + return NULL; + } + + return pThunkMap; +} + +#endif // FEATURE_RX_THUNKS diff --git a/src/coreclr/nativeaot/Runtime/TypeManager.cpp b/src/coreclr/nativeaot/Runtime/TypeManager.cpp new file mode 100644 index 00000000000000..3e5203fdf9d290 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/TypeManager.cpp @@ -0,0 +1,171 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "holder.h" +#include "rhassert.h" +#include "slist.h" +#include "gcrhinterface.h" +#include "shash.h" +#include "RWLock.h" +#include "varint.h" +#include "rhbinder.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "event.h" +#include "threadstore.h" +#include "TypeManager.h" + +/* static */ +TypeManager * TypeManager::Create(HANDLE osModule, void * pModuleHeader, void** pClasslibFunctions, uint32_t nClasslibFunctions) +{ + ReadyToRunHeader * pReadyToRunHeader = (ReadyToRunHeader *)pModuleHeader; + + // Sanity check the signature magic + ASSERT(pReadyToRunHeader->Signature == ReadyToRunHeaderConstants::Signature); + if (pReadyToRunHeader->Signature != ReadyToRunHeaderConstants::Signature) + return nullptr; + + // Only the current major version is supported currently + ASSERT(pReadyToRunHeader->MajorVersion == ReadyToRunHeaderConstants::CurrentMajorVersion); + if (pReadyToRunHeader->MajorVersion != ReadyToRunHeaderConstants::CurrentMajorVersion) + return nullptr; + + return new (nothrow) TypeManager(osModule, pReadyToRunHeader, pClasslibFunctions, nClasslibFunctions); +} + +TypeManager::TypeManager(HANDLE osModule, ReadyToRunHeader * pHeader, void** pClasslibFunctions, uint32_t nClasslibFunctions) + : m_osModule(osModule), m_pHeader(pHeader), + m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions) +{ + int length; + m_pStaticsGCDataSection = (uint8_t*)GetModuleSection(ReadyToRunSectionType::GCStaticRegion, &length); + m_pStaticsGCInfo = (StaticGcDesc*)GetModuleSection(ReadyToRunSectionType::GCStaticDesc, &length); + m_pThreadStaticsDataSection = (uint8_t*)GetModuleSection(ReadyToRunSectionType::ThreadStaticRegion, &length); + m_pThreadStaticsGCInfo = (StaticGcDesc*)GetModuleSection(ReadyToRunSectionType::ThreadStaticGCDescRegion, &length); + m_pTlsIndex = (uint32_t*)GetModuleSection(ReadyToRunSectionType::ThreadStaticIndex, &length); + m_pLoopHijackFlag = (uint32_t*)GetModuleSection(ReadyToRunSectionType::LoopHijackFlag, &length); + m_pDispatchMapTable = (DispatchMap **)GetModuleSection(ReadyToRunSectionType::InterfaceDispatchTable, &length); +} + +void * TypeManager::GetModuleSection(ReadyToRunSectionType sectionId, int * length) +{ + ModuleInfoRow * pModuleInfoRows = (ModuleInfoRow *)(m_pHeader + 1); + + ASSERT(m_pHeader->EntrySize == sizeof(ModuleInfoRow)); + + // TODO: Binary search + for (int i = 0; i < m_pHeader->NumberOfSections; i++) + { + ModuleInfoRow * pCurrent = pModuleInfoRows + i; + if ((int32_t)sectionId == pCurrent->SectionId) + { + *length = pCurrent->GetLength(); + return pCurrent->Start; + } + } + + *length = 0; + return nullptr; +} + +void * TypeManager::GetClasslibFunction(ClasslibFunctionId functionId) +{ + uint32_t id = (uint32_t)functionId; + + if (id >= m_nClasslibFunctions) + return nullptr; + + return m_pClasslibFunctions[id]; +} + +bool TypeManager::ModuleInfoRow::HasEndPointer() +{ + return Flags & (int32_t)ModuleInfoFlags::HasEndPointer; +} + +int TypeManager::ModuleInfoRow::GetLength() +{ + if (HasEndPointer()) + { + return (int)((uint8_t*)End - (uint8_t*)Start); + } + else + { + return sizeof(void*); + } +} + +void TypeManager::EnumStaticGCRefsBlock(void * pfnCallback, void * pvCallbackData, StaticGcDesc* pStaticGcInfo) +{ + if (pStaticGcInfo == NULL) + return; + + for (uint32_t idxSeries = 0; idxSeries < pStaticGcInfo->m_numSeries; idxSeries++) + { + PTR_StaticGcDescGCSeries pSeries = dac_cast(dac_cast(pStaticGcInfo) + + offsetof(StaticGcDesc, m_series) + (idxSeries * sizeof(StaticGcDesc::GCSeries))); + + // The m_startOffset field is really 32-bit relocation (IMAGE_REL_BASED_RELPTR32) to the GC static base of the type + // the GCSeries is describing for. This makes it tolerable to the symbol sorting that the linker conducts. + PTR_RtuObjectRef pRefLocation = dac_cast(dac_cast(&pSeries->m_startOffset) + (int32_t)pSeries->m_startOffset); + uint32_t numObjects = pSeries->m_size; + + RedhawkGCInterface::BulkEnumGcObjRef(pRefLocation, numObjects, pfnCallback, pvCallbackData); + } +} + +void TypeManager::EnumThreadStaticGCRefsBlock(void * pfnCallback, void * pvCallbackData, StaticGcDesc* pStaticGcInfo, uint8_t* pbThreadStaticData) +{ + if (pStaticGcInfo == NULL) + return; + + for (uint32_t idxSeries = 0; idxSeries < pStaticGcInfo->m_numSeries; idxSeries++) + { + PTR_StaticGcDescGCSeries pSeries = dac_cast(dac_cast(pStaticGcInfo) + + offsetof(StaticGcDesc, m_series) + (idxSeries * sizeof(StaticGcDesc::GCSeries))); + + // The m_startOffset field is really a 32-bit relocation (IMAGE_REL_SECREL) to the TLS section. + uint8_t* pTlsObject = pbThreadStaticData + pSeries->m_startOffset; + PTR_RtuObjectRef pRefLocation = dac_cast(pTlsObject); + uint32_t numObjects = pSeries->m_size; + + RedhawkGCInterface::BulkEnumGcObjRef(pRefLocation, numObjects, pfnCallback, pvCallbackData); + } +} + +void TypeManager::EnumStaticGCRefs(void * pfnCallback, void * pvCallbackData) +{ + // Regular statics. + EnumStaticGCRefsBlock(pfnCallback, pvCallbackData, m_pStaticsGCInfo); + + // Thread local statics. + if (m_pThreadStaticsGCInfo != NULL) + { + FOREACH_THREAD(pThread) + { + // To calculate the address of the data for each thread's TLS fields we need two values: + // 1) The TLS slot index allocated for this module by the OS loader. We keep a pointer to this + // value in the module header. + // 2) The offset into the TLS block at which managed data begins. + EnumThreadStaticGCRefsBlock(pfnCallback, pvCallbackData, m_pThreadStaticsGCInfo, + dac_cast(pThread->GetThreadLocalStorage(*m_pTlsIndex, 0))); + } + END_FOREACH_THREAD + } +} + +HANDLE TypeManager::GetOsModuleHandle() +{ + return m_osModule; +} + +TypeManager* TypeManagerHandle::AsTypeManager() +{ + return (TypeManager*)_value; +} diff --git a/src/coreclr/nativeaot/Runtime/TypeManager.h b/src/coreclr/nativeaot/Runtime/TypeManager.h new file mode 100644 index 00000000000000..bd34a64dd148ab --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/TypeManager.h @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#pragma once +#include "ModuleHeaders.h" +#include "ICodeManager.h" + +struct StaticGcDesc; +class DispatchMap; + +class TypeManager +{ + // NOTE: Part of this layout is a contract with the managed side in TypeManagerHandle.cs + HANDLE m_osModule; + ReadyToRunHeader * m_pHeader; + DispatchMap** m_pDispatchMapTable; + StaticGcDesc* m_pStaticsGCInfo; + StaticGcDesc* m_pThreadStaticsGCInfo; + uint8_t* m_pStaticsGCDataSection; + uint8_t* m_pThreadStaticsDataSection; + uint32_t* m_pTlsIndex; // Pointer to TLS index if this module uses thread statics + void** m_pClasslibFunctions; + uint32_t m_nClasslibFunctions; + uint32_t* m_pLoopHijackFlag; + + TypeManager(HANDLE osModule, ReadyToRunHeader * pHeader, void** pClasslibFunctions, uint32_t nClasslibFunctions); + +public: + static TypeManager * Create(HANDLE osModule, void * pModuleHeader, void** pClasslibFunctions, uint32_t nClasslibFunctions); + void * GetModuleSection(ReadyToRunSectionType sectionId, int * length); + void EnumStaticGCRefs(void * pfnCallback, void * pvCallbackData); + HANDLE GetOsModuleHandle(); + void* GetClasslibFunction(ClasslibFunctionId functionId); + uint32_t* GetPointerToTlsIndex() { return m_pTlsIndex; } + void SetLoopHijackFlag(uint32_t flag) { if (m_pLoopHijackFlag != nullptr) *m_pLoopHijackFlag = flag; } + +private: + + struct ModuleInfoRow + { + int32_t SectionId; + int32_t Flags; + void * Start; + void * End; + + bool HasEndPointer(); + int GetLength(); + }; + + void EnumStaticGCRefsBlock(void * pfnCallback, void * pvCallbackData, StaticGcDesc* pStaticGcInfo); + void EnumThreadStaticGCRefsBlock(void * pfnCallback, void * pvCallbackData, StaticGcDesc* pStaticGcInfo, uint8_t* pbThreadStaticData); +}; + +// TypeManagerHandle represents an AOT module in MRT based runtimes. +// These handles are a pointer to a TypeManager. +struct TypeManagerHandle +{ + static TypeManagerHandle Null() + { + TypeManagerHandle handle; + handle._value = nullptr; + return handle; + } + + static TypeManagerHandle Create(TypeManager * value) + { + TypeManagerHandle handle; + handle._value = value; + return handle; + } + + void *_value; + + TypeManager* AsTypeManager(); +}; + diff --git a/src/coreclr/nativeaot/Runtime/UniversalTransitionHelpers.cpp b/src/coreclr/nativeaot/Runtime/UniversalTransitionHelpers.cpp new file mode 100644 index 00000000000000..30a1ff269290ab --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/UniversalTransitionHelpers.cpp @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" + +#ifdef _DEBUG +#define TRASH_SAVED_ARGUMENT_REGISTERS +#endif + +#ifdef TRASH_SAVED_ARGUMENT_REGISTERS + +// +// Define tables of predictable distinguished values that RhpUniversalTransition can use to +// trash argument registers after they have been saved into the transition frame. +// +// Trashing these registers is a testability aid that makes it easier to detect bugs where +// the transition frame content is not correctly propagated to the eventual callee. +// +// In the absence of trashing, such bugs can become undetectable if the code that +// dispatches the call happens to never touch the impacted argument register (e.g., xmm3 on +// amd64 or d5 on arm32). In such a case, the original enregistered argument will flow +// unmodified into the eventual callee, obscuring the fact that the dispatcher failed to +// propagate the transition frame copy of this register. +// +// These tables are manually aligned as a conservative safeguard to ensure that the +// consumers can use arbitrary access widths without ever needing to worry about alignment. +// The comments in each table show the %d/%f renderings of each 32-bit value, plus the +// %I64d/%f rendering of the combined 64-bit value of each aligned pair of 32-bit values. +// + +#define TRASH_VALUE_ALIGNMENT 16 + +EXTERN_C +DECLSPEC_ALIGN(TRASH_VALUE_ALIGNMENT) +const uint32_t RhpIntegerTrashValues[] = { + // Lo32 Hi32 Lo32 Hi32 Hi32:Lo32 + // ----------- ----------- --------- --------- ------------------ + 0x07801001U, 0x07802002U, // (125833217, 125837314) (540467148372316161) + 0x07803003U, 0x07804004U, // (125841411, 125845508) (540502341334347779) + 0x07805005U, 0x07806006U, // (125849605, 125853702) (540537534296379397) + 0x07807007U, 0x07808008U, // (125857799, 125861896) (540572727258411015) + 0x07809009U, 0x0780a00aU, // (125865993, 125870090) (540607920220442633) + 0x0780b00bU, 0x0780c00cU, // (125874187, 125878284) (540643113182474251) + 0x0780d00dU, 0x0780e00eU, // (125882381, 125886478) (540678306144505869) + 0x0780f00fU, 0x07810010U, // (125890575, 125894672) (540713499106537487) +}; + +EXTERN_C +DECLSPEC_ALIGN(TRASH_VALUE_ALIGNMENT) +const uint32_t RhpFpTrashValues[] = { + // Lo32 Hi32 Lo32 Hi32 Hi32:Lo32 + // ----------- ----------- ------------------- ------------------- ------------------- + 0x42001001U, 0x42002002U, // (32.0156288146972660, 32.0312576293945310) (8657061952.00781440) + 0x42003003U, 0x42004004U, // (32.0468864440917970, 32.0625152587890630) (8724187200.02344320) + 0x42005005U, 0x42006006U, // (32.0781440734863280, 32.0937728881835940) (8791312448.03907200) + 0x42007007U, 0x42008008U, // (32.1094017028808590, 32.1250305175781250) (8858437696.05470090) + 0x42009009U, 0x4200a00aU, // (32.1406593322753910, 32.1562881469726560) (8925562944.07032970) + 0x4200b00bU, 0x4200c00cU, // (32.1719169616699220, 32.1875457763671880) (8992688192.08595850) + 0x4200d00dU, 0x4200e00eU, // (32.2031745910644530, 32.2188034057617190) (9059813440.10158730) + 0x4200f00fU, 0x42010010U, // (32.2344322204589840, 32.2500610351562500) (9126938688.11721610) +}; + +#endif // TRASH_SAVED_ARGUMENT_REGISTERS + diff --git a/src/coreclr/nativeaot/Runtime/allocheap.cpp b/src/coreclr/nativeaot/Runtime/allocheap.cpp new file mode 100644 index 00000000000000..00fb40914d65bf --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/allocheap.cpp @@ -0,0 +1,372 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "DebugMacrosExt.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "holder.h" +#include "Crst.h" +#include "Range.h" +#ifdef FEATURE_RWX_MEMORY +#include "memaccessmgr.h" +#endif +#include "allocheap.h" + +#include "CommonMacros.inl" +#include "slist.inl" + +using namespace rh::util; + +//------------------------------------------------------------------------------------------------- +AllocHeap::AllocHeap() + : m_blockList(), + m_rwProtectType(PAGE_READWRITE), + m_roProtectType(PAGE_READWRITE), +#ifdef FEATURE_RWX_MEMORY + m_pAccessMgr(NULL), + m_hCurPageRW(), +#endif // FEATURE_RWX_MEMORY + m_pNextFree(NULL), + m_pFreeCommitEnd(NULL), + m_pFreeReserveEnd(NULL), + m_pbInitialMem(NULL), + m_fShouldFreeInitialMem(false), + m_lock(CrstAllocHeap) + COMMA_INDEBUG(m_fIsInit(false)) +{ + ASSERT(!_UseAccessManager()); +} + +#ifdef FEATURE_RWX_MEMORY +//------------------------------------------------------------------------------------------------- +AllocHeap::AllocHeap( + uint32_t rwProtectType, + uint32_t roProtectType, + MemAccessMgr* pAccessMgr) + : m_blockList(), + m_rwProtectType(rwProtectType), + m_roProtectType(roProtectType == 0 ? rwProtectType : roProtectType), + m_pAccessMgr(pAccessMgr), + m_hCurPageRW(), + m_pNextFree(NULL), + m_pFreeCommitEnd(NULL), + m_pFreeReserveEnd(NULL), + m_pbInitialMem(NULL), + m_fShouldFreeInitialMem(false), + m_lock(CrstAllocHeap) + COMMA_INDEBUG(m_fIsInit(false)) +{ + ASSERT(!_UseAccessManager() || (m_rwProtectType != m_roProtectType && m_pAccessMgr != NULL)); +} +#endif // FEATURE_RWX_MEMORY + +//------------------------------------------------------------------------------------------------- +bool AllocHeap::Init() +{ + ASSERT(!m_fIsInit); + INDEBUG(m_fIsInit = true;) + + return true; +} + +//------------------------------------------------------------------------------------------------- +// This is for using pre-allocated memory on heap construction. +// Should never use this more than once, and should always follow construction of heap. + +bool AllocHeap::Init( + uint8_t * pbInitialMem, + uintptr_t cbInitialMemCommit, + uintptr_t cbInitialMemReserve, + bool fShouldFreeInitialMem) +{ + ASSERT(!m_fIsInit); + +#ifdef FEATURE_RWX_MEMORY + // Manage the committed portion of memory + if (_UseAccessManager()) + { + m_pAccessMgr->ManageMemoryRange(MemRange(pbInitialMem, cbInitialMemCommit), true); + } +#endif // FEATURE_RWX_MEMORY + + BlockListElem *pBlock = new (nothrow) BlockListElem(pbInitialMem, cbInitialMemReserve); + if (pBlock == NULL) + return false; + m_blockList.PushHead(pBlock); + + if (!_UpdateMemPtrs(pbInitialMem, + pbInitialMem + cbInitialMemCommit, + pbInitialMem + cbInitialMemReserve)) + { + return false; + } + + m_pbInitialMem = pbInitialMem; + m_fShouldFreeInitialMem = fShouldFreeInitialMem; + + INDEBUG(m_fIsInit = true;) + return true; +} + +//------------------------------------------------------------------------------------------------- +AllocHeap::~AllocHeap() +{ + while (!m_blockList.IsEmpty()) + { + BlockListElem *pCur = m_blockList.PopHead(); + if (pCur->GetStart() != m_pbInitialMem || m_fShouldFreeInitialMem) + PalVirtualFree(pCur->GetStart(), pCur->GetLength(), MEM_RELEASE); + delete pCur; + } +} + +//------------------------------------------------------------------------------------------------- +uint8_t * AllocHeap::_Alloc( + uintptr_t cbMem, + uintptr_t alignment + WRITE_ACCESS_HOLDER_ARG + ) +{ +#ifndef FEATURE_RWX_MEMORY + const void* pRWAccessHolder = NULL; +#endif // FEATURE_RWX_MEMORY + + ASSERT((alignment & (alignment - 1)) == 0); // Power of 2 only. + ASSERT(alignment <= OS_PAGE_SIZE); // Can't handle this right now. + ASSERT((m_rwProtectType == m_roProtectType) == (pRWAccessHolder == NULL)); + ASSERT(!_UseAccessManager() || pRWAccessHolder != NULL); + + if (_UseAccessManager() && pRWAccessHolder == NULL) + return NULL; + + CrstHolder lock(&m_lock); + + uint8_t * pbMem = _AllocFromCurBlock(cbMem, alignment PASS_WRITE_ACCESS_HOLDER_ARG); + if (pbMem != NULL) + return pbMem; + + // Must allocate new block + if (!_AllocNewBlock(cbMem)) + return NULL; + + pbMem = _AllocFromCurBlock(cbMem, alignment PASS_WRITE_ACCESS_HOLDER_ARG); + ASSERT_MSG(pbMem != NULL, "AllocHeap::Alloc: failed to alloc mem after new block alloc"); + + return pbMem; +} + +//------------------------------------------------------------------------------------------------- +uint8_t * AllocHeap::Alloc( + uintptr_t cbMem + WRITE_ACCESS_HOLDER_ARG) +{ + return _Alloc(cbMem, 1 PASS_WRITE_ACCESS_HOLDER_ARG); +} + +//------------------------------------------------------------------------------------------------- +uint8_t * AllocHeap::AllocAligned( + uintptr_t cbMem, + uintptr_t alignment + WRITE_ACCESS_HOLDER_ARG) +{ + return _Alloc(cbMem, alignment PASS_WRITE_ACCESS_HOLDER_ARG); +} + +//------------------------------------------------------------------------------------------------- +bool AllocHeap::Contains(void* pvMem, uintptr_t cbMem) +{ + MemRange range(pvMem, cbMem); + for (BlockList::Iterator it = m_blockList.Begin(); it != m_blockList.End(); ++it) + { + if (it->Contains(range)) + { + return true; + } + } + return false; +} + +#ifdef FEATURE_RWX_MEMORY +//------------------------------------------------------------------------------------------------- +bool AllocHeap::_AcquireWriteAccess( + uint8_t* pvMem, + uintptr_t cbMem, + WriteAccessHolder* pHolder) +{ + ASSERT(!_UseAccessManager() || m_pAccessMgr != NULL); + + if (_UseAccessManager()) + return m_pAccessMgr->AcquireWriteAccess(MemRange(pvMem, cbMem), m_hCurPageRW, pHolder); + else + return true; +} + +//------------------------------------------------------------------------------------------------- +bool AllocHeap::AcquireWriteAccess( + void* pvMem, + uintptr_t cbMem, + WriteAccessHolder* pHolder) +{ + return _AcquireWriteAccess(static_cast(pvMem), cbMem, pHolder); +} +#endif // FEATURE_RWX_MEMORY + +//------------------------------------------------------------------------------------------------- +bool AllocHeap::_UpdateMemPtrs(uint8_t* pNextFree, uint8_t* pFreeCommitEnd, uint8_t* pFreeReserveEnd) +{ + ASSERT(MemRange(pNextFree, pFreeReserveEnd).Contains(MemRange(pNextFree, pFreeCommitEnd))); + ASSERT(ALIGN_DOWN(pFreeCommitEnd, OS_PAGE_SIZE) == pFreeCommitEnd); + ASSERT(ALIGN_DOWN(pFreeReserveEnd, OS_PAGE_SIZE) == pFreeReserveEnd); + +#ifdef FEATURE_RWX_MEMORY + // See if we need to update current allocation holder or protect committed pages. + if (_UseAccessManager()) + { + if (pFreeCommitEnd - pNextFree > 0) + { +#ifndef STRESS_MEMACCESSMGR + // Create or update the alloc cache, used to speed up new allocations. + // If there is available commited memory and either m_pNextFree is + // being updated past a page boundary or the current cache is empty, + // then update the cache. + if (ALIGN_DOWN(m_pNextFree, OS_PAGE_SIZE) != ALIGN_DOWN(pNextFree, OS_PAGE_SIZE) || + m_hCurPageRW.GetRange().GetLength() == 0) + { + // Update current alloc page write access holder. + if (!_AcquireWriteAccess(ALIGN_DOWN(pNextFree, OS_PAGE_SIZE), + OS_PAGE_SIZE, + &m_hCurPageRW)) + { + return false; + } + } +#endif // STRESS_MEMACCESSMGR + + } + else + { // No available committed memory. Release the cache. + m_hCurPageRW.Release(); + } + } +#endif // FEATURE_RWX_MEMORY + + m_pNextFree = pNextFree; + m_pFreeCommitEnd = pFreeCommitEnd; + m_pFreeReserveEnd = pFreeReserveEnd; + return true; +} + +//------------------------------------------------------------------------------------------------- +bool AllocHeap::_UpdateMemPtrs(uint8_t* pNextFree, uint8_t* pFreeCommitEnd) +{ + return _UpdateMemPtrs(pNextFree, pFreeCommitEnd, m_pFreeReserveEnd); +} + +//------------------------------------------------------------------------------------------------- +bool AllocHeap::_UpdateMemPtrs(uint8_t* pNextFree) +{ + return _UpdateMemPtrs(pNextFree, m_pFreeCommitEnd); +} + +//------------------------------------------------------------------------------------------------- +bool AllocHeap::_AllocNewBlock(uintptr_t cbMem) +{ + cbMem = ALIGN_UP(max(cbMem, s_minBlockSize), OS_PAGE_SIZE);; + + uint8_t * pbMem = reinterpret_cast + (PalVirtualAlloc(NULL, cbMem, MEM_COMMIT, m_roProtectType)); + + if (pbMem == NULL) + return false; + + BlockListElem *pBlockListElem = new (nothrow) BlockListElem(pbMem, cbMem); + if (pBlockListElem == NULL) + { + PalVirtualFree(pbMem, 0, MEM_RELEASE); + return false; + } + + // Add to the list. While there is no race for writers (we hold the lock) we have the + // possibility of simultaneous readers, and using the interlocked version creates a + // memory barrier to make sure any reader sees a consistent list. + m_blockList.PushHeadInterlocked(pBlockListElem); + + return _UpdateMemPtrs(pbMem, pbMem + cbMem, pbMem + cbMem); +} + +//------------------------------------------------------------------------------------------------- +uint8_t * AllocHeap::_AllocFromCurBlock( + uintptr_t cbMem, + uintptr_t alignment + WRITE_ACCESS_HOLDER_ARG) +{ + uint8_t * pbMem = NULL; + + cbMem += (uint8_t *)ALIGN_UP(m_pNextFree, alignment) - m_pNextFree; + + if (m_pNextFree + cbMem <= m_pFreeCommitEnd || + _CommitFromCurBlock(cbMem)) + { + ASSERT(cbMem + m_pNextFree <= m_pFreeCommitEnd); +#ifdef FEATURE_RWX_MEMORY + if (pRWAccessHolder != NULL) + { + if (!_AcquireWriteAccess(m_pNextFree, cbMem, pRWAccessHolder)) + return NULL; + } +#endif // FEATURE_RWX_MEMORY + pbMem = ALIGN_UP(m_pNextFree, alignment); + + if (!_UpdateMemPtrs(m_pNextFree + cbMem)) + return NULL; + } + + return pbMem; +} + +//------------------------------------------------------------------------------------------------- +bool AllocHeap::_CommitFromCurBlock(uintptr_t cbMem) +{ + ASSERT(m_pFreeCommitEnd < m_pNextFree + cbMem); + + if (m_pNextFree + cbMem <= m_pFreeReserveEnd) + { + uintptr_t cbMemToCommit = ALIGN_UP(cbMem, OS_PAGE_SIZE); + +#ifdef FEATURE_RWX_MEMORY + if (_UseAccessManager()) + { + if (!m_pAccessMgr->ManageMemoryRange(MemRange(m_pFreeCommitEnd, cbMemToCommit), false)) + return false; + } + else + { + uint32_t oldProtectType; + if (!PalVirtualProtect(m_pFreeCommitEnd, cbMemToCommit, m_roProtectType, &oldProtectType)) + return false; + } +#endif // FEATURE_RWX_MEMORY + + return _UpdateMemPtrs(m_pNextFree, m_pFreeCommitEnd + cbMemToCommit); + } + + return false; +} + +//------------------------------------------------------------------------------------------------- +void * __cdecl operator new(size_t n, AllocHeap * alloc) +{ + return alloc->Alloc(n); +} + +//------------------------------------------------------------------------------------------------- +void * __cdecl operator new[](size_t n, AllocHeap * alloc) +{ + return alloc->Alloc(n); +} + diff --git a/src/coreclr/nativeaot/Runtime/allocheap.h b/src/coreclr/nativeaot/Runtime/allocheap.h new file mode 100644 index 00000000000000..08f244fd638420 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/allocheap.h @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "forward_declarations.h" + +#ifdef FEATURE_RWX_MEMORY +#define WRITE_ACCESS_HOLDER_ARG , rh::util::WriteAccessHolder *pRWAccessHolder +#define WRITE_ACCESS_HOLDER_ARG_NULL_DEFAULT , rh::util::WriteAccessHolder *pRWAccessHolder = NULL +#define PASS_WRITE_ACCESS_HOLDER_ARG , pRWAccessHolder +#else // FEATURE_RWX_MEMORY +#define WRITE_ACCESS_HOLDER_ARG +#define WRITE_ACCESS_HOLDER_ARG_NULL_DEFAULT +#define PASS_WRITE_ACCESS_HOLDER_ARG +#endif // FEATURE_RWX_MEMORY + +class AllocHeap +{ + public: + AllocHeap(); + +#ifdef FEATURE_RWX_MEMORY + // If pAccessMgr is non-NULL, it will be used to manage R/W access to the memory allocated. + AllocHeap(uint32_t rwProtectType = PAGE_READWRITE, + uint32_t roProtectType = 0, // 0 indicates "same as rwProtectType" + rh::util::MemAccessMgr* pAccessMgr = NULL); +#endif // FEATURE_RWX_MEMORY + + bool Init(); + + bool Init(uint8_t * pbInitialMem, + uintptr_t cbInitialMemCommit, + uintptr_t cbInitialMemReserve, + bool fShouldFreeInitialMem); + + ~AllocHeap(); + + // If AllocHeap was created with a MemAccessMgr, pRWAccessHolder must be non-NULL. + // On return, the holder will permit R/W access to the allocated memory until it + // is destructed. + uint8_t * Alloc(uintptr_t cbMem WRITE_ACCESS_HOLDER_ARG_NULL_DEFAULT); + + // If AllocHeap was created with a MemAccessMgr, pRWAccessHolder must be non-NULL. + // On return, the holder will permit R/W access to the allocated memory until it + // is destructed. + uint8_t * AllocAligned(uintptr_t cbMem, + uintptr_t alignment + WRITE_ACCESS_HOLDER_ARG_NULL_DEFAULT); + + // Returns true if this AllocHeap owns the memory range [pvMem, pvMem+cbMem) + bool Contains(void * pvMem, + uintptr_t cbMem); + +#ifdef FEATURE_RWX_MEMORY + // Used with previously-allocated memory for which RW access is needed again. + // Returns true on success. R/W access will be granted until the holder is + // destructed. + bool AcquireWriteAccess(void* pvMem, + uintptr_t cbMem, + rh::util::WriteAccessHolder* pHolder); +#endif // FEATURE_RWX_MEMORY + + private: + // Allocation Helpers + uint8_t* _Alloc(uintptr_t cbMem, uintptr_t alignment WRITE_ACCESS_HOLDER_ARG); + bool _AllocNewBlock(uintptr_t cbMem); + uint8_t* _AllocFromCurBlock(uintptr_t cbMem, uintptr_t alignment WRITE_ACCESS_HOLDER_ARG); + bool _CommitFromCurBlock(uintptr_t cbMem); + + // Access protection helpers +#ifdef FEATURE_RWX_MEMORY + bool _AcquireWriteAccess(uint8_t* pvMem, uintptr_t cbMem, rh::util::WriteAccessHolder* pHolder); +#endif // FEATURE_RWX_MEMORY + bool _UpdateMemPtrs(uint8_t* pNextFree, uint8_t* pFreeCommitEnd, uint8_t* pFreeReserveEnd); + bool _UpdateMemPtrs(uint8_t* pNextFree, uint8_t* pFreeCommitEnd); + bool _UpdateMemPtrs(uint8_t* pNextFree); + bool _UseAccessManager() { return m_rwProtectType != m_roProtectType; } + + static const uintptr_t s_minBlockSize = OS_PAGE_SIZE; + + typedef rh::util::MemRange Block; + typedef DPTR(Block) PTR_Block; + struct BlockListElem : public Block + { + BlockListElem(Block const & block) + : Block(block) + {} + + BlockListElem(uint8_t * pbMem, uintptr_t cbMem) + : Block(pbMem, cbMem) + {} + + Block m_block; + PTR_Block m_pNext; + }; + + typedef SList BlockList; + BlockList m_blockList; + + uint32_t m_rwProtectType; // READ/WRITE/EXECUTE/etc + uint32_t m_roProtectType; // What to do with fully allocated and initialized pages. + +#ifdef FEATURE_RWX_MEMORY + rh::util::MemAccessMgr* m_pAccessMgr; + rh::util::WriteAccessHolder m_hCurPageRW; // Used to hold RW access to the current allocation page + // Passed as pHint to MemAccessMgr::AcquireWriteAccess. +#endif // FEATURE_RWX_MEMORY + uint8_t * m_pNextFree; + uint8_t * m_pFreeCommitEnd; + uint8_t * m_pFreeReserveEnd; + + uint8_t * m_pbInitialMem; + bool m_fShouldFreeInitialMem; + + Crst m_lock; + + INDEBUG(bool m_fIsInit;) +}; +typedef DPTR(AllocHeap) PTR_AllocHeap; + +//------------------------------------------------------------------------------------------------- +void * __cdecl operator new(size_t n, AllocHeap * alloc); +void * __cdecl operator new[](size_t n, AllocHeap * alloc); + diff --git a/src/coreclr/nativeaot/Runtime/amd64/AllocFast.S b/src/coreclr/nativeaot/Runtime/amd64/AllocFast.S new file mode 100644 index 00000000000000..a42bfbcb19fb2f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/AllocFast.S @@ -0,0 +1,312 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include // generated by the build from AsmOffsets.cpp +#include + +// Allocate non-array, non-finalizable object. If the allocation doesn't fit into the current thread's +// allocation context then automatically fallback to the slow allocation path. +// RDI == MethodTable +NESTED_ENTRY RhpNewFast, _TEXT, NoHandler + push_nonvol_reg rbx + mov rbx, rdi + + // rax = GetThread() + INLINE_GETTHREAD + + // + // rbx contains MethodTable pointer + // + mov edx, [rbx + OFFSETOF__MethodTable__m_uBaseSize] + + // + // rax: Thread pointer + // rbx: MethodTable pointer + // rdx: base size + // + + mov rsi, [rax + OFFSETOF__Thread__m_alloc_context__alloc_ptr] + add rdx, rsi + cmp rdx, [rax + OFFSETOF__Thread__m_alloc_context__alloc_limit] + ja LOCAL_LABEL(RhpNewFast_RarePath) + + // set the new alloc pointer + mov [rax + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rdx + + mov rax, rsi + + // set the new object's MethodTable pointer + mov [rsi], rbx + + .cfi_remember_state + pop_nonvol_reg rbx + ret + + .cfi_restore_state + .cfi_def_cfa_offset 16 // workaround cfi_restore_state bug +LOCAL_LABEL(RhpNewFast_RarePath): + mov rdi, rbx // restore MethodTable + xor esi, esi + pop_nonvol_reg rbx + jmp C_FUNC(RhpNewObject) + +NESTED_END RhpNewFast, _TEXT + + + +// Allocate non-array object with finalizer +// RDI == MethodTable +LEAF_ENTRY RhpNewFinalizable, _TEXT + mov esi, GC_ALLOC_FINALIZE + jmp C_FUNC(RhpNewObject) +LEAF_END RhpNewFinalizable, _TEXT + + + +// Allocate non-array object +// RDI == MethodTable +// ESI == alloc flags +NESTED_ENTRY RhpNewObject, _TEXT, NoHandler + + PUSH_COOP_PINVOKE_FRAME rcx + END_PROLOGUE + + // RCX: transition frame + + // Preserve the MethodTable in RBX + mov rbx, rdi + + xor edx, edx // numElements + + // Call the rest of the allocation helper. + // void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + call C_FUNC(RhpGcAlloc) + + test rax, rax + jz LOCAL_LABEL(NewOutOfMemory) + + .cfi_remember_state + POP_COOP_PINVOKE_FRAME + ret + + .cfi_restore_state + .cfi_def_cfa_offset 96 // workaround cfi_restore_state bug +LOCAL_LABEL(NewOutOfMemory): + // This is the OOM failure path. We're going to tail-call to a managed helper that will throw + // an out of memory exception that the caller of this allocator understands. + + mov rdi, rbx // MethodTable pointer + xor esi, esi // Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + + jmp EXTERNAL_C_FUNC(RhExceptionHandling_FailedAllocation) +NESTED_END RhpNewObject, _TEXT + + +// Allocate a string. +// RDI == MethodTable +// ESI == character/element count +NESTED_ENTRY RhNewString, _TEXT, NoHandler + // we want to limit the element count to the non-negative 32-bit int range + cmp rsi, MAX_STRING_LENGTH + ja LOCAL_LABEL(StringSizeOverflow) + + push_nonvol_reg rbx + push_nonvol_reg r12 + push_register rcx // padding + + mov rbx, rdi // save MethodTable + mov r12, rsi // save element count + + // rax = GetThread() + INLINE_GETTHREAD + + mov rcx, rax // rcx = Thread* + + // Compute overall allocation size (align(base size + (element size * elements), 8)). + lea rax, [r12 * STRING_COMPONENT_SIZE + STRING_BASE_SIZE + 7] + and rax, -8 + + // rax == string size + // rbx == MethodTable + // rcx == Thread* + // r12 == element count + + mov rdx, rax + add rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr] + jc LOCAL_LABEL(RhNewString_RarePath) + + // rax == new alloc ptr + // rbx == MethodTable + // rcx == Thread* + // rdx == string size + // r12 == element count + cmp rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_limit] + ja LOCAL_LABEL(RhNewString_RarePath) + + mov [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax + + // calc the new object pointer + sub rax, rdx + + mov [rax + OFFSETOF__Object__m_pEEType], rbx + mov [rax + OFFSETOF__String__m_Length], r12d + + .cfi_remember_state + pop_register rcx // padding + pop_nonvol_reg r12 + pop_nonvol_reg rbx + ret + + .cfi_restore_state + .cfi_def_cfa_offset 32 // workaround cfi_restore_state bug +LOCAL_LABEL(RhNewString_RarePath): + mov rdi, rbx // restore MethodTable + mov rsi, r12 // restore element count + // passing string size in rdx + + pop_register rcx // padding + pop_nonvol_reg r12 + pop_nonvol_reg rbx + jmp C_FUNC(RhpNewArrayRare) + +LOCAL_LABEL(StringSizeOverflow): + // We get here if the size of the final string object can't be represented as an unsigned + // 32-bit value. We're going to tail-call to a managed helper that will throw + // an OOM exception that the caller of this allocator understands. + + // rdi holds MethodTable pointer already + xor esi, esi // Indicate that we should throw OOM. + jmp EXTERNAL_C_FUNC(RhExceptionHandling_FailedAllocation) + +NESTED_END RhNewString, _TEXT + + +// Allocate one dimensional, zero based array (SZARRAY). +// RDI == MethodTable +// ESI == element count +NESTED_ENTRY RhpNewArray, _TEXT, NoHandler + // we want to limit the element count to the non-negative 32-bit int range + cmp rsi, 0x07fffffff + ja LOCAL_LABEL(ArraySizeOverflow) + + push_nonvol_reg rbx + push_nonvol_reg r12 + push_register rcx // padding + + mov rbx, rdi // save MethodTable + mov r12, rsi // save element count + + // rax = GetThread() + INLINE_GETTHREAD + + mov rcx, rax // rcx = Thread* + + // Compute overall allocation size (align(base size + (element size * elements), 8)). + movzx eax, word ptr [rbx + OFFSETOF__MethodTable__m_usComponentSize] + mul r12 + mov edx, [rbx + OFFSETOF__MethodTable__m_uBaseSize] + add rax, rdx + add rax, 7 + and rax, -8 + + // rax == array size + // rbx == MethodTable + // rcx == Thread* + // r12 == element count + + mov rdx, rax + add rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr] + jc LOCAL_LABEL(RhpNewArray_RarePath) + + // rax == new alloc ptr + // rbx == MethodTable + // rcx == Thread* + // rdx == array size + // r12 == element count + cmp rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_limit] + ja LOCAL_LABEL(RhpNewArray_RarePath) + + mov [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax + + // calc the new object pointer + sub rax, rdx + + mov [rax + OFFSETOF__Object__m_pEEType], rbx + mov [rax + OFFSETOF__Array__m_Length], r12d + + .cfi_remember_state + pop_register rcx // padding + pop_nonvol_reg r12 + pop_nonvol_reg rbx + ret + + .cfi_restore_state + .cfi_def_cfa_offset 32 // workaround cfi_restore_state bug +LOCAL_LABEL(RhpNewArray_RarePath): + mov rdi, rbx // restore MethodTable + mov rsi, r12 // restore element count + // passing array size in rdx + + pop_register rcx // padding + pop_nonvol_reg r12 + pop_nonvol_reg rbx + jmp C_FUNC(RhpNewArrayRare) + +LOCAL_LABEL(ArraySizeOverflow): + // We get here if the size of the final array object can't be represented as an unsigned + // 32-bit value. We're going to tail-call to a managed helper that will throw + // an overflow exception that the caller of this allocator understands. + + // rdi holds MethodTable pointer already + mov esi, 1 // Indicate that we should throw OverflowException + jmp EXTERNAL_C_FUNC(RhExceptionHandling_FailedAllocation) + +NESTED_END RhpNewArray, _TEXT + +NESTED_ENTRY RhpNewArrayRare, _TEXT, NoHandler + + // rdi == MethodTable + // rsi == element count + + PUSH_COOP_PINVOKE_FRAME rcx + END_PROLOGUE + + // rcx: transition frame + + // Preserve the MethodTable in RBX + mov rbx, rdi + + mov rdx, rsi // numElements + + // passing MethodTable in rdi + xor rsi, rsi // uFlags + // pasing pTransitionFrame in rcx + + // Call the rest of the allocation helper. + // void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + call C_FUNC(RhpGcAlloc) + + test rax, rax + jz LOCAL_LABEL(ArrayOutOfMemory) + + .cfi_remember_state + POP_COOP_PINVOKE_FRAME + ret + + .cfi_restore_state + .cfi_def_cfa_offset 96 // workaround cfi_restore_state bug +LOCAL_LABEL(ArrayOutOfMemory): + // This is the OOM failure path. We're going to tail-call to a managed helper that will throw + // an out of memory exception that the caller of this allocator understands. + + mov rdi, rbx // MethodTable pointer + xor esi, esi // Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + + jmp EXTERNAL_C_FUNC(RhExceptionHandling_FailedAllocation) + +NESTED_END RhpNewArrayRare, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/AllocFast.asm b/src/coreclr/nativeaot/Runtime/amd64/AllocFast.asm new file mode 100644 index 00000000000000..005be9e1a74d17 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/AllocFast.asm @@ -0,0 +1,247 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include asmmacros.inc + + +;; Allocate non-array, non-finalizable object. If the allocation doesn't fit into the current thread's +;; allocation context then automatically fallback to the slow allocation path. +;; RCX == MethodTable +LEAF_ENTRY RhpNewFast, _TEXT + + ;; rdx = GetThread(), TRASHES rax + INLINE_GETTHREAD rdx, rax + + ;; + ;; rcx contains MethodTable pointer + ;; + mov r8d, [rcx + OFFSETOF__MethodTable__m_uBaseSize] + + ;; + ;; eax: base size + ;; rcx: MethodTable pointer + ;; rdx: Thread pointer + ;; + + mov rax, [rdx + OFFSETOF__Thread__m_alloc_context__alloc_ptr] + add r8, rax + cmp r8, [rdx + OFFSETOF__Thread__m_alloc_context__alloc_limit] + ja RhpNewFast_RarePath + + ;; set the new alloc pointer + mov [rdx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], r8 + + ;; set the new object's MethodTable pointer + mov [rax], rcx + ret + +RhpNewFast_RarePath: + xor edx, edx + jmp RhpNewObject + +LEAF_END RhpNewFast, _TEXT + + + +;; Allocate non-array object with finalizer +;; RCX == MethodTable +LEAF_ENTRY RhpNewFinalizable, _TEXT + mov edx, GC_ALLOC_FINALIZE + jmp RhpNewObject +LEAF_END RhpNewFinalizable, _TEXT + + + +;; Allocate non-array object +;; RCX == MethodTable +;; EDX == alloc flags +NESTED_ENTRY RhpNewObject, _TEXT + + PUSH_COOP_PINVOKE_FRAME r9 + END_PROLOGUE + + ; R9: transition frame + + ;; Preserve the MethodTable in RSI + mov rsi, rcx + + xor r8d, r8d ; numElements + + ;; Call the rest of the allocation helper. + ;; void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + call RhpGcAlloc + + test rax, rax + jz NewOutOfMemory + + POP_COOP_PINVOKE_FRAME + ret + +NewOutOfMemory: + ;; This is the OOM failure path. We're going to tail-call to a managed helper that will throw + ;; an out of memory exception that the caller of this allocator understands. + + mov rcx, rsi ; MethodTable pointer + xor edx, edx ; Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + + jmp RhExceptionHandling_FailedAllocation +NESTED_END RhpNewObject, _TEXT + + +;; Allocate a string. +;; RCX == MethodTable +;; EDX == character/element count +LEAF_ENTRY RhNewString, _TEXT + + ; we want to limit the element count to the non-negative 32-bit int range + cmp rdx, MAX_STRING_LENGTH + ja StringSizeOverflow + + ; Compute overall allocation size (align(base size + (element size * elements), 8)). + lea rax, [(rdx * STRING_COMPONENT_SIZE) + (STRING_BASE_SIZE + 7)] + and rax, -8 + + ; rax == string size + ; rcx == MethodTable + ; rdx == element count + + INLINE_GETTHREAD r10, r8 + + mov r8, rax + add rax, [r10 + OFFSETOF__Thread__m_alloc_context__alloc_ptr] + jc RhpNewArrayRare + + ; rax == new alloc ptr + ; rcx == MethodTable + ; rdx == element count + ; r8 == array size + ; r10 == thread + cmp rax, [r10 + OFFSETOF__Thread__m_alloc_context__alloc_limit] + ja RhpNewArrayRare + + mov [r10 + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax + + ; calc the new object pointer + sub rax, r8 + + mov [rax + OFFSETOF__Object__m_pEEType], rcx + mov [rax + OFFSETOF__String__m_Length], edx + + ret + +StringSizeOverflow: + ; We get here if the size of the final string object can't be represented as an unsigned + ; 32-bit value. We're going to tail-call to a managed helper that will throw + ; an OOM exception that the caller of this allocator understands. + + ; rcx holds MethodTable pointer already + xor edx, edx ; Indicate that we should throw OOM. + jmp RhExceptionHandling_FailedAllocation +LEAF_END RhNewString, _TEXT + + +;; Allocate one dimensional, zero based array (SZARRAY). +;; RCX == MethodTable +;; EDX == element count +LEAF_ENTRY RhpNewArray, _TEXT + + ; we want to limit the element count to the non-negative 32-bit int range + cmp rdx, 07fffffffh + ja ArraySizeOverflow + + ; save element count + mov r8, rdx + + ; Compute overall allocation size (align(base size + (element size * elements), 8)). + movzx eax, word ptr [rcx + OFFSETOF__MethodTable__m_usComponentSize] + mul rdx + mov edx, [rcx + OFFSETOF__MethodTable__m_uBaseSize] + add rax, rdx + add rax, 7 + and rax, -8 + + mov rdx, r8 + + ; rax == array size + ; rcx == MethodTable + ; rdx == element count + + INLINE_GETTHREAD r10, r8 + + mov r8, rax + add rax, [r10 + OFFSETOF__Thread__m_alloc_context__alloc_ptr] + jc RhpNewArrayRare + + ; rax == new alloc ptr + ; rcx == MethodTable + ; rdx == element count + ; r8 == array size + ; r10 == thread + cmp rax, [r10 + OFFSETOF__Thread__m_alloc_context__alloc_limit] + ja RhpNewArrayRare + + mov [r10 + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax + + ; calc the new object pointer + sub rax, r8 + + mov [rax + OFFSETOF__Object__m_pEEType], rcx + mov [rax + OFFSETOF__Array__m_Length], edx + + ret + +ArraySizeOverflow: + ; We get here if the size of the final array object can't be represented as an unsigned + ; 32-bit value. We're going to tail-call to a managed helper that will throw + ; an overflow exception that the caller of this allocator understands. + + ; rcx holds MethodTable pointer already + mov edx, 1 ; Indicate that we should throw OverflowException + jmp RhExceptionHandling_FailedAllocation +LEAF_END RhpNewArray, _TEXT + +NESTED_ENTRY RhpNewArrayRare, _TEXT + + ; rcx == MethodTable + ; rdx == element count + + PUSH_COOP_PINVOKE_FRAME r9 + END_PROLOGUE + + ; r9: transition frame + + ; Preserve the MethodTable in RSI + mov rsi, rcx + + ; passing MethodTable in rcx + mov r8, rdx ; numElements + xor rdx, rdx ; uFlags + ; pasing pTransitionFrame in r9 + + ; Call the rest of the allocation helper. + ; void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + call RhpGcAlloc + + test rax, rax + jz ArrayOutOfMemory + + POP_COOP_PINVOKE_FRAME + ret + +ArrayOutOfMemory: + ;; This is the OOM failure path. We're going to tail-call to a managed helper that will throw + ;; an out of memory exception that the caller of this allocator understands. + + mov rcx, rsi ; MethodTable pointer + xor edx, edx ; Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + + jmp RhExceptionHandling_FailedAllocation + +NESTED_END RhpNewArrayRare, _TEXT + + + END diff --git a/src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc b/src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc new file mode 100644 index 00000000000000..a00e94f3165c2a --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc @@ -0,0 +1,417 @@ +;; +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. +;; + +include AsmOffsets.inc ; generated by the build from AsmOffsets.cpp + +;; +;; MACROS +;; + +; +; Define macros to build unwind data for prologues. +; + +push_nonvol_reg macro Reg + + .errnz ___STACK_ADJUSTMENT_FORBIDDEN, + + push Reg + .pushreg Reg + + endm + +push_vol_reg macro Reg + + .errnz ___STACK_ADJUSTMENT_FORBIDDEN, push_vol_reg cannot be used after save_reg_postrsp + + push Reg + .allocstack 8 + + endm + +push_imm macro imm + + .errnz ___STACK_ADJUSTMENT_FORBIDDEN, push_vol_reg cannot be used after save_reg_postrsp + + push imm + .allocstack 8 + + endm + +push_eflags macro + + .errnz ___STACK_ADJUSTMENT_FORBIDDEN, push_eflags cannot be used after save_reg_postrsp + + pushfq + .allocstack 8 + + endm + +alloc_stack macro Size + + .errnz ___STACK_ADJUSTMENT_FORBIDDEN, alloc_stack cannot be used after save_reg_postrsp + + sub rsp, Size + .allocstack Size + + endm + +save_reg_frame macro Reg, FrameReg, Offset + + .erre ___FRAME_REG_SET, save_reg_frame cannot be used before set_frame + + mov Offset[FrameReg], Reg + .savereg Reg, Offset + + endm + +save_reg_postrsp macro Reg, Offset + + .errnz ___FRAME_REG_SET, save_reg_postrsp cannot be used after set_frame + + mov Offset[rsp], Reg + .savereg Reg, Offset + + ___STACK_ADJUSTMENT_FORBIDDEN = 1 + + endm + +save_xmm128_frame macro Reg, FrameReg, Offset + + .erre ___FRAME_REG_SET, save_xmm128_frame cannot be used before set_frame + + movdqa Offset[FrameReg], Reg + .savexmm128 Reg, Offset + + endm + +save_xmm128_postrsp macro Reg, Offset + + .errnz ___FRAME_REG_SET, save_reg_postrsp cannot be used after set_frame + + movdqa Offset[rsp], Reg + .savexmm128 Reg, Offset + + ___STACK_ADJUSTMENT_FORBIDDEN = 1 + + endm + +set_frame macro Reg, Offset + + .errnz ___FRAME_REG_SET, set_frame cannot be used more than once + +if Offset + + lea Reg, Offset[rsp] + +else + + mov reg, rsp + +endif + + .setframe Reg, Offset + ___FRAME_REG_SET = 1 + + endm + +END_PROLOGUE macro + + .endprolog + + endm + +; +; Define function entry/end macros. +; + +LEAF_ENTRY macro Name, Section + +Section segment para 'CODE' + + align 16 + + public Name +Name proc + + endm + +LEAF_END macro Name, section + +Name endp + +Section ends + + endm + +LEAF_END_MARKED macro Name, section + public Name&_End +Name&_End label qword + ; this nop is important to keep the label in + ; the right place in the face of BBT + nop + +Name endp + +Section ends + + endm + + +NESTED_ENTRY macro Name, Section, Handler + +Section segment para 'CODE' + + align 16 + + public Name + +ifb + +Name proc frame + +else + +Name proc frame:Handler + +endif + + ___FRAME_REG_SET = 0 + ___STACK_ADJUSTMENT_FORBIDDEN = 0 + + endm + +NESTED_END macro Name, section + +Name endp + +Section ends + + endm + +NESTED_END_MARKED macro Name, section + public Name&_End +Name&_End label qword + +Name endp + +Section ends + + endm + + +ALTERNATE_ENTRY macro Name + +Name label proc +PUBLIC Name + endm + +LABELED_RETURN_ADDRESS macro Name + +Name label proc +PUBLIC Name + endm + +EXPORT_POINTER_TO_ADDRESS macro Name + + local AddressToExport + +AddressToExport label proc + + .const + + align 8 + +Name dq offset AddressToExport + + public Name + + .code + + endm + +_tls_array equ 58h ;; offsetof(TEB, ThreadLocalStoragePointer) + +;; +;; __declspec(thread) version +;; +INLINE_GETTHREAD macro destReg, trashReg + EXTERN _tls_index : DWORD + EXTERN tls_CurrentThread:DWORD + +;; +;; construct 'eax' from 'rax' so that the register size and data size match +;; +;; BEWARE: currently only r10 is allowed as destReg from the r8-r15 set. +;; +ifidni , +destRegDWORD EQU r10d +else +destRegDWORD TEXTEQU @CatStr( e, @SubStr( destReg, 2, 2 ) ) +endif + + mov destRegDWORD, [_tls_index] + mov trashReg, gs:[_tls_array] + mov trashReg, [trashReg + destReg * 8] + mov destRegDWORD, SECTIONREL tls_CurrentThread + add destReg, trashReg + +endm + +INLINE_THREAD_UNHIJACK macro threadReg, trashReg1, trashReg2 + ;; + ;; Thread::Unhijack() + ;; + mov trashReg1, [threadReg + OFFSETOF__Thread__m_pvHijackedReturnAddress] + cmp trashReg1, 0 + je @F + + mov trashReg2, [threadReg + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + mov [trashReg2], trashReg1 + mov qword ptr [threadReg + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], 0 + mov qword ptr [threadReg + OFFSETOF__Thread__m_pvHijackedReturnAddress], 0 + +@@: +endm + +DEFAULT_FRAME_SAVE_FLAGS equ PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_RSP + +;; +;; Macro used from unmanaged helpers called from managed code where the helper does not transition immediately +;; into pre-emptive mode but may cause a GC and thus requires the stack is crawlable. This is typically the +;; case for helpers that meddle in GC state (e.g. allocation helpers) where the code must remain in +;; cooperative mode since it handles object references and internal GC state directly but a garbage collection +;; may be inevitable. In these cases we need to be able to transition to pre-meptive mode deep within the +;; unmanaged code but still be able to initialize the stack iterator at the first stack frame which may hold +;; interesting GC references. In all our helper cases this corresponds to the most recent managed frame (e.g. +;; the helper's caller). +;; +;; This macro builds a frame describing the current state of managed code. +;; +;; INVARIANTS +;; - The macro assumes it is called from a prolog, prior to a frame pointer being setup. +;; - All preserved registers remain unchanged from their values in managed code. +;; +PUSH_COOP_PINVOKE_FRAME macro trashReg + lea trashReg, [rsp + 8h] + push_vol_reg trashReg ; save caller's RSP + push_nonvol_reg r15 ; save preserved registers + push_nonvol_reg r14 ; .. + push_nonvol_reg r13 ; .. + push_nonvol_reg r12 ; .. + push_nonvol_reg rdi ; .. + push_nonvol_reg rsi ; .. + push_nonvol_reg rbx ; .. + push_imm DEFAULT_FRAME_SAVE_FLAGS ; save the register bitmask + push_vol_reg trashReg ; Thread * (unused by stackwalker) + push_nonvol_reg rbp ; save caller's RBP + mov trashReg, [rsp + 11*8] ; Find the return address + push_vol_reg trashReg ; save m_RIP + lea trashReg, [rsp + 0] ; trashReg == address of frame + + ;; allocate scratch space and any required alignment + alloc_stack 28h +endm + +;; +;; Pop the frame and restore register state preserved by PUSH_COOP_PINVOKE_FRAME +;; +POP_COOP_PINVOKE_FRAME macro + add rsp, 30h + pop rbp ; restore RBP + pop r10 ; discard thread + pop r10 ; discard bitmask + pop rbx + pop rsi + pop rdi + pop r12 + pop r13 + pop r14 + pop r15 + pop r10 ; discard caller RSP +endm + +; - TAILCALL_RAX: ("jmp rax") should be used for tailcalls, this emits an instruction +; sequence which is recognized by the unwinder as a valid epilogue terminator +TAILJMP_RAX TEXTEQU + +;; +;; CONSTANTS -- INTEGER +;; +TSF_Attached equ 01h +TSF_SuppressGcStress equ 08h +TSF_DoNotTriggerGc equ 10h + +;; +;; Rename fields of nested structs +;; +OFFSETOF__Thread__m_alloc_context__alloc_ptr equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr +OFFSETOF__Thread__m_alloc_context__alloc_limit equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit + + + +;; GC type flags +GC_ALLOC_FINALIZE equ 1 + +;; Note: these must match the defs in PInvokeTransitionFrameFlags +PTFF_SAVE_RBX equ 00000001h +PTFF_SAVE_RSI equ 00000002h +PTFF_SAVE_RDI equ 00000004h +PTFF_SAVE_R12 equ 00000010h +PTFF_SAVE_R13 equ 00000020h +PTFF_SAVE_R14 equ 00000040h +PTFF_SAVE_R15 equ 00000080h +PTFF_SAVE_ALL_PRESERVED equ 000000F7h ;; NOTE: RBP is not included in this set! +PTFF_SAVE_RSP equ 00008000h +PTFF_SAVE_RAX equ 00000100h ;; RAX is saved if it contains a GC ref and we're in hijack handler +PTFF_SAVE_ALL_SCRATCH equ 00007F00h +PTFF_RAX_IS_GCREF equ 00010000h ;; iff PTFF_SAVE_RAX: set -> eax is Object, clear -> eax is scalar +PTFF_RAX_IS_BYREF equ 00020000h ;; iff PTFF_SAVE_RAX: set -> eax is ByRef, clear -> eax is Object or scalar +PTFF_THREAD_ABORT equ 00040000h ;; indicates that ThreadAbortException should be thrown when returning from the transition + +;; These must match the TrapThreadsFlags enum +TrapThreadsFlags_None equ 0 +TrapThreadsFlags_AbortInProgress equ 1 +TrapThreadsFlags_TrapThreads equ 2 + +;; This must match HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT +STATUS_REDHAWK_THREAD_ABORT equ 43h + +;; +;; CONSTANTS -- SYMBOLS +;; + +ifdef FEATURE_GC_STRESS +REDHAWKGCINTERFACE__STRESSGC equ ?StressGc@RedhawkGCInterface@@SAXXZ +THREAD__HIJACKFORGCSTRESS equ ?HijackForGcStress@Thread@@SAXPEAUPAL_LIMITED_CONTEXT@@@Z +endif ;; FEATURE_GC_STRESS + +;; +;; IMPORTS +;; + +EXTERN RhpGcAlloc : PROC +EXTERN RhpValidateExInfoPop : PROC +EXTERN RhDebugBreak : PROC +EXTERN RhpWaitForSuspend2 : PROC +EXTERN RhpWaitForGC2 : PROC +EXTERN RhpReversePInvokeAttachOrTrapThread2 : PROC +EXTERN RhExceptionHandling_FailedAllocation : PROC +EXTERN RhThrowHwEx : PROC +EXTERN RhThrowEx : PROC +EXTERN RhRethrow : PROC +EXTERN RhpGcPoll2 : PROC +ifdef FEATURE_GC_STRESS +EXTERN REDHAWKGCINTERFACE__STRESSGC : PROC +EXTERN THREAD__HIJACKFORGCSTRESS : PROC +endif ;; FEATURE_GC_STRESS + +EXTERN g_lowest_address : QWORD +EXTERN g_highest_address : QWORD +EXTERN g_ephemeral_low : QWORD +EXTERN g_ephemeral_high : QWORD +EXTERN g_card_table : QWORD +EXTERN RhpTrapThreads : DWORD + diff --git a/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h new file mode 100644 index 00000000000000..2239433993f082 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h @@ -0,0 +1,121 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This file is used by AsmOffsets.h to validate that our +// assembly-code offsets always match their C++ counterparts. +// +// NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix + +#ifndef UNIX_AMD64_ABI +PLAT_ASM_SIZEOF(260, ExInfo) +PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) +PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) +PLAT_ASM_OFFSET(10, ExInfo, m_exception) +PLAT_ASM_OFFSET(18, ExInfo, m_kind) +PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) +PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) +PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) +PLAT_ASM_OFFSET(250, ExInfo, m_notifyDebuggerSP) + +PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP) +PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) +PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) +PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) +PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) + +PLAT_ASM_SIZEOF(230, StackFrameIterator) +PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) +PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) +PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) +PLAT_ASM_OFFSET(228, StackFrameIterator, m_OriginalControlPC) + +PLAT_ASM_SIZEOF(100, PAL_LIMITED_CONTEXT) +PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, IP) + +PLAT_ASM_OFFSET(8, PAL_LIMITED_CONTEXT, Rsp) +PLAT_ASM_OFFSET(10, PAL_LIMITED_CONTEXT, Rbp) +PLAT_ASM_OFFSET(18, PAL_LIMITED_CONTEXT, Rdi) +PLAT_ASM_OFFSET(20, PAL_LIMITED_CONTEXT, Rsi) +PLAT_ASM_OFFSET(28, PAL_LIMITED_CONTEXT, Rax) +PLAT_ASM_OFFSET(30, PAL_LIMITED_CONTEXT, Rbx) + +PLAT_ASM_OFFSET(38, PAL_LIMITED_CONTEXT, R12) +PLAT_ASM_OFFSET(40, PAL_LIMITED_CONTEXT, R13) +PLAT_ASM_OFFSET(48, PAL_LIMITED_CONTEXT, R14) +PLAT_ASM_OFFSET(50, PAL_LIMITED_CONTEXT, R15) +PLAT_ASM_OFFSET(60, PAL_LIMITED_CONTEXT, Xmm6) +PLAT_ASM_OFFSET(70, PAL_LIMITED_CONTEXT, Xmm7) +PLAT_ASM_OFFSET(80, PAL_LIMITED_CONTEXT, Xmm8) +PLAT_ASM_OFFSET(90, PAL_LIMITED_CONTEXT, Xmm9) +PLAT_ASM_OFFSET(0a0, PAL_LIMITED_CONTEXT, Xmm10) +PLAT_ASM_OFFSET(0b0, PAL_LIMITED_CONTEXT, Xmm11) +PLAT_ASM_OFFSET(0c0, PAL_LIMITED_CONTEXT, Xmm12) +PLAT_ASM_OFFSET(0d0, PAL_LIMITED_CONTEXT, Xmm13) +PLAT_ASM_OFFSET(0e0, PAL_LIMITED_CONTEXT, Xmm14) +PLAT_ASM_OFFSET(0f0, PAL_LIMITED_CONTEXT, Xmm15) + +PLAT_ASM_SIZEOF(130, REGDISPLAY) +PLAT_ASM_OFFSET(78, REGDISPLAY, SP) + +PLAT_ASM_OFFSET(18, REGDISPLAY, pRbx) +PLAT_ASM_OFFSET(20, REGDISPLAY, pRbp) +PLAT_ASM_OFFSET(28, REGDISPLAY, pRsi) +PLAT_ASM_OFFSET(30, REGDISPLAY, pRdi) +PLAT_ASM_OFFSET(58, REGDISPLAY, pR12) +PLAT_ASM_OFFSET(60, REGDISPLAY, pR13) +PLAT_ASM_OFFSET(68, REGDISPLAY, pR14) +PLAT_ASM_OFFSET(70, REGDISPLAY, pR15) +PLAT_ASM_OFFSET(90, REGDISPLAY, Xmm) + +#else // !UNIX_AMD64_ABI + +PLAT_ASM_SIZEOF(1a8, ExInfo) +PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) +PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) +PLAT_ASM_OFFSET(10, ExInfo, m_exception) +PLAT_ASM_OFFSET(18, ExInfo, m_kind) +PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) +PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) +PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) +PLAT_ASM_OFFSET(1a0, ExInfo, m_notifyDebuggerSP) + +PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP) +PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) +PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) +PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) +PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) + +PLAT_ASM_SIZEOF(180, StackFrameIterator) +PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) +PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) +PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) +PLAT_ASM_OFFSET(178, StackFrameIterator, m_OriginalControlPC) + +PLAT_ASM_SIZEOF(50, PAL_LIMITED_CONTEXT) +PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, IP) + +PLAT_ASM_OFFSET(8, PAL_LIMITED_CONTEXT, Rsp) +PLAT_ASM_OFFSET(10, PAL_LIMITED_CONTEXT, Rbp) +PLAT_ASM_OFFSET(18, PAL_LIMITED_CONTEXT, Rax) +PLAT_ASM_OFFSET(20, PAL_LIMITED_CONTEXT, Rbx) +PLAT_ASM_OFFSET(28, PAL_LIMITED_CONTEXT, Rdx) + +PLAT_ASM_OFFSET(30, PAL_LIMITED_CONTEXT, R12) +PLAT_ASM_OFFSET(38, PAL_LIMITED_CONTEXT, R13) +PLAT_ASM_OFFSET(40, PAL_LIMITED_CONTEXT, R14) +PLAT_ASM_OFFSET(48, PAL_LIMITED_CONTEXT, R15) + +PLAT_ASM_SIZEOF(90, REGDISPLAY) +PLAT_ASM_OFFSET(78, REGDISPLAY, SP) + +PLAT_ASM_OFFSET(18, REGDISPLAY, pRbx) +PLAT_ASM_OFFSET(20, REGDISPLAY, pRbp) +PLAT_ASM_OFFSET(28, REGDISPLAY, pRsi) +PLAT_ASM_OFFSET(30, REGDISPLAY, pRdi) +PLAT_ASM_OFFSET(58, REGDISPLAY, pR12) +PLAT_ASM_OFFSET(60, REGDISPLAY, pR13) +PLAT_ASM_OFFSET(68, REGDISPLAY, pR14) +PLAT_ASM_OFFSET(70, REGDISPLAY, pR15) + +#endif // !UNIX_AMD64_ABI diff --git a/src/coreclr/nativeaot/Runtime/amd64/CallDescrWorker.S b/src/coreclr/nativeaot/Runtime/amd64/CallDescrWorker.S new file mode 100644 index 00000000000000..483d4b5f9ac8cc --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/CallDescrWorker.S @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include + +NESTED_ENTRY RhCallDescrWorker, _TEXT, NoHandler + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFromCallDescrThunk + + // UNIXTODO: Implement this function + int 3 +NESTED_END RhCallDescrWorker, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/CallDescrWorker.asm b/src/coreclr/nativeaot/Runtime/amd64/CallDescrWorker.asm new file mode 100644 index 00000000000000..3f4ded05d9918e --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/CallDescrWorker.asm @@ -0,0 +1,105 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include AsmMacros.inc + + +;;;;;;;;;;;;;;;;;;;;;;; CallingConventionConverter Thunks Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;extern "C" void RhCallDescrWorker(CallDescrData * pCallDescrData); + + NESTED_ENTRY RhCallDescrWorker, _TEXT + + push_nonvol_reg rbx ; save nonvolatile registers + push_nonvol_reg rsi ; + push_nonvol_reg rbp ; + set_frame rbp, 0 ; set frame pointer + + END_PROLOGUE + + mov rbx, rcx ; save pCallDescrData in rbx + + mov ecx, dword ptr [rbx + OFFSETOF__CallDescrData__numStackSlots] + + test ecx, 1 + jz StackAligned + push rax +StackAligned: + + mov rsi, [rbx + OFFSETOF__CallDescrData__pSrc] ; set source argument list address + lea rsi, [rsi + 8 * rcx] + +StackCopyLoop: ; copy the arguments to stack top-down to carefully probe for sufficient + ; stack space + sub rsi, 8 + push qword ptr [rsi] + dec ecx + jnz StackCopyLoop + + ; + ; N.B. All four argument registers are loaded regardless of the actual number + ; of arguments. + ; + + mov rax, [rbx + OFFSETOF__CallDescrData__pFloatArgumentRegisters] ; get floating pointer arg registers pointer + + mov rcx, 0[rsp] ; load first four argument registers + mov rdx, 8[rsp] ; + mov r8, 10h[rsp] ; + mov r9, 18h[rsp] ; + test rax, rax ; + jz DoCall ; + movdqa xmm0, [rax + 00h] ; load floating point registers if they are used + movdqa xmm1, [rax + 10h] ; + movdqa xmm2, [rax + 20h] ; + movdqa xmm3, [rax + 30h] ; +DoCall: + call qword ptr [rbx + OFFSETOF__CallDescrData__pTarget] ; call target function + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFromCallDescrThunk + + ; Symbol used to identify thunk call to managed function so the special + ; case unwinder can unwind through this function. Sadly we cannot directly + ; export this symbol right now because it confuses DIA unwinder to believe + ; it's the beginning of a new method, therefore we export the address + ; of an auxiliary variable holding the address instead. + + ; Save FP return value + + mov ecx, dword ptr [rbx + OFFSETOF__CallDescrData__fpReturnSize] + test ecx, ecx + jz ReturnsInt + + cmp ecx, 4 + je ReturnsFloat + cmp ecx, 8 + je ReturnsDouble + ; unexpected + jmp Epilog + +ReturnsInt: + mov rbx, [rbx + OFFSETOF__CallDescrData__pReturnBuffer] + mov [rbx], rax + +Epilog: + lea rsp, 0[rbp] ; deallocate argument list + pop rbp ; restore nonvolatile register + pop rsi ; + pop rbx ; + ret + +ReturnsFloat: +; Unlike desktop returnValue is a pointer to a return buffer, not the buffer itself + mov rbx, [rbx + OFFSETOF__CallDescrData__pReturnBuffer] + movss real4 ptr [rbx], xmm0 + jmp Epilog + +ReturnsDouble: +; Unlike desktop returnValue is a pointer to a return buffer, not the buffer itself + mov rbx, [rbx + OFFSETOF__CallDescrData__pReturnBuffer] + movsd real8 ptr [rbx], xmm0 + jmp Epilog + + NESTED_END RhCallDescrWorker, _TEXT + +end diff --git a/src/coreclr/nativeaot/Runtime/amd64/CallingConventionConverterHelpers.S b/src/coreclr/nativeaot/Runtime/amd64/CallingConventionConverterHelpers.S new file mode 100644 index 00000000000000..6ce126a42e78bc --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/CallingConventionConverterHelpers.S @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include + +// +// void CallingConventionConverter_ReturnVoidReturnThunk() +// +LEAF_ENTRY CallingConventionConverter_ReturnVoidReturnThunk, _TEXT + ret +LEAF_END CallingConventionConverter_ReturnVoidReturnThunk, _TEXT + +// +// int CallingConventionConverter_ReturnIntegerReturnThunk(int) +// +LEAF_ENTRY CallingConventionConverter_ReturnIntegerReturnThunk, _TEXT + // UNIXTODO: Implement this function + int 3 +LEAF_END CallingConventionConverter_ReturnIntegerReturnThunk, _TEXT + +// +// Note: The "__jmpstub__" prefix is used to indicate to debugger +// that it must step-through this stub when it encounters it while +// stepping. +// + +// __jmpstub__CallingConventionConverter_CommonCallingStub +// +// +// struct CallingConventionConverter_CommonCallingStub_PointerData +// { +// void *ManagedCallConverterThunk; +// void *UniversalThunk; +// } +// +// struct CommonCallingStubInputData +// { +// ULONG_PTR CallingConventionId; +// CallingConventionConverter_CommonCallingStub_PointerData *commonData; +// } +// +// r10 - Points at CommonCallingStubInputData +// +// +LEAF_ENTRY __jmpstub__CallingConventionConverter_CommonCallingStub, _TEXT + // UNIXTODO: Implement this function + int 3 +LEAF_END __jmpstub__CallingConventionConverter_CommonCallingStub, _TEXT + +// +// void CallingConventionConverter_GetStubs(IntPtr *returnVoidStub, IntPtr *returnIntegerStub, IntPtr *commonStub) +// +LEAF_ENTRY CallingConventionConverter_GetStubs, _TEXT + // UNIXTODO: Implement this function + int 3 +LEAF_END CallingConventionConverter_GetStubs, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/CallingConventionConverterHelpers.asm b/src/coreclr/nativeaot/Runtime/amd64/CallingConventionConverterHelpers.asm new file mode 100644 index 00000000000000..b4525cbf4cd9aa --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/CallingConventionConverterHelpers.asm @@ -0,0 +1,85 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +;; ----------------------------------------------------------------------------------------------------------- +;; #include "asmmacros.inc" +;; ----------------------------------------------------------------------------------------------------------- + +LEAF_ENTRY macro Name, Section + Section segment para 'CODE' + align 16 + public Name + Name proc +endm + +LEAF_END macro Name, Section + Name endp + Section ends +endm + +; - TAILCALL_RAX: ("jmp rax") should be used for tailcalls, this emits an instruction +; sequence which is recognized by the unwinder as a valid epilogue terminator +TAILJMP_RAX TEXTEQU +POINTER_SIZE equ 08h + +;; +;; void CallingConventionConverter_ReturnVoidReturnThunk() +;; +LEAF_ENTRY CallingConventionConverter_ReturnVoidReturnThunk, _TEXT + ret +LEAF_END CallingConventionConverter_ReturnVoidReturnThunk, _TEXT + +;; +;; int CallingConventionConverter_ReturnIntegerReturnThunk(int) +;; +LEAF_ENTRY CallingConventionConverter_ReturnIntegerReturnThunk, _TEXT + mov rax, rcx + ret +LEAF_END CallingConventionConverter_ReturnIntegerReturnThunk, _TEXT + +;; +;; Note: The "__jmpstub__" prefix is used to indicate to debugger +;; that it must step-through this stub when it encounters it while +;; stepping. +;; + +;; __jmpstub__CallingConventionConverter_CommonCallingStub +;; +;; +;; struct CallingConventionConverter_CommonCallingStub_PointerData +;; { +;; void *ManagedCallConverterThunk; +;; void *UniversalThunk; +;; } +;; +;; struct CommonCallingStubInputData +;; { +;; ULONG_PTR CallingConventionId; +;; CallingConventionConverter_CommonCallingStub_PointerData *commonData; +;; } +;; +;; r10 - Points at CommonCallingStubInputData +;; +;; +LEAF_ENTRY __jmpstub__CallingConventionConverter_CommonCallingStub, _TEXT + mov r11, [r10] ; put CallingConventionId into r11 as "parameter" to universal transition thunk + mov r10, [r10 + POINTER_SIZE] ; get pointer to CallingConventionConverter_CommonCallingStub_PointerData into r10 + mov rax, [r10 + POINTER_SIZE] ; get address of UniversalTransitionThunk + mov r10, [r10] ; get address of ManagedCallConverterThunk + TAILJMP_RAX +LEAF_END __jmpstub__CallingConventionConverter_CommonCallingStub, _TEXT + +;; +;; void CallingConventionConverter_GetStubs(IntPtr *returnVoidStub, IntPtr *returnIntegerStub, IntPtr *commonStub) +;; +LEAF_ENTRY CallingConventionConverter_GetStubs, _TEXT + lea rax, [CallingConventionConverter_ReturnVoidReturnThunk] + mov [rcx], rax + lea rax, [CallingConventionConverter_ReturnIntegerReturnThunk] + mov [rdx], rax + lea rax, [__jmpstub__CallingConventionConverter_CommonCallingStub] + mov [r8], rax + ret +LEAF_END CallingConventionConverter_GetStubs, _TEXT + +end diff --git a/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.S new file mode 100644 index 00000000000000..fd348e7466a968 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.S @@ -0,0 +1,534 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include // generated by the build from AsmOffsets.cpp +#include + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpThrowHwEx +// +// INPUT: RDI: exception code of fault +// RSI: faulting RIP +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpThrowHwEx, _TEXT, NoHandler + + STACKSIZEOF_ExInfo = ((SIZEOF__ExInfo + 15) & (~15)) + rsp_offsetof_Context = STACKSIZEOF_ExInfo + + mov rax, rsp // save the faulting RSP + + // Align the stack towards zero + and rsp, -16 + + xor rdx, rdx + +// struct PAL_LIMITED_CONTEXT +// { + push_nonvol_reg r15 + push_nonvol_reg r14 + push_nonvol_reg r13 + push_nonvol_reg r12 + push_register rdx // rdx set to 0 + push_nonvol_reg rbx + push_register rdx // rax set to 0 + push_nonvol_reg rbp + push_register rax // faulting RSP + push_register rsi // faulting IP +// } + // allocate outgoing args area and space for the ExInfo + alloc_stack STACKSIZEOF_ExInfo + + END_PROLOGUE + + mov rbx, rdi + INLINE_GETTHREAD + mov rdi, rbx + + mov rsi, rsp // rsi <- ExInfo* + + xor rdx, rdx + mov [rsi + OFFSETOF__ExInfo__m_exception], rdx // init the exception object to null + mov byte ptr [rsi + OFFSETOF__ExInfo__m_passNumber], 1 // init to the first pass + mov dword ptr [rsi + OFFSETOF__ExInfo__m_idxCurClause], 0xFFFFFFFF + mov byte ptr [rsi + OFFSETOF__ExInfo__m_kind], 2 // ExKind.HardwareFault + + // link the ExInfo into the thread's ExInfo chain + mov rdx, [rax + OFFSETOF__Thread__m_pExInfoStackHead] + mov [rsi + OFFSETOF__ExInfo__m_pPrevExInfo], rdx // pExInfo->m_pPrevExInfo = m_pExInfoStackHead + mov [rax + OFFSETOF__Thread__m_pExInfoStackHead], rsi // m_pExInfoStackHead = pExInfo + + // set the exception context field on the ExInfo + lea rdx, [rsp + rsp_offsetof_Context] // rdx <- PAL_LIMITED_CONTEXT* + mov [rsi + OFFSETOF__ExInfo__m_pExContext], rdx // init ExInfo.m_pExContext + + // rdi still contains the exception code + // rsi contains the address of the ExInfo + call EXTERNAL_C_FUNC(RhThrowHwEx) + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowHwEx2 + + // no return + int 3 + +NESTED_END RhpThrowHwEx, _TEXT + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpThrowEx +// +// INPUT: RDI: exception object +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler + + STACKSIZEOF_ExInfo = ((SIZEOF__ExInfo + 15) & (~ 15)) + rsp_offsetof_Context = STACKSIZEOF_ExInfo + + lea rax, [rsp+8] // save the RSP of the throw site + mov rsi, [rsp] // get return address + + xor rdx, rdx + push_register rdx // padding + +// struct PAL_LIMITED_CONTEXT +// { + push_nonvol_reg r15 + push_nonvol_reg r14 + push_nonvol_reg r13 + push_nonvol_reg r12 + push_register rdx // rdx set to 0 + push_nonvol_reg rbx + push_register rdx // rax set to 0 + push_nonvol_reg rbp + push_register rax // 'faulting' RSP + push_register rsi // 'faulting' IP +// } + + // allocate space for the ExInfo + alloc_stack STACKSIZEOF_ExInfo + + END_PROLOGUE + + mov rbx, rdi + INLINE_GETTHREAD + mov rdi, rbx + + lea rbx, [rsp + rsp_offsetof_Context + SIZEOF__PAL_LIMITED_CONTEXT + 0x8] // rbx <- addr of return address + + // There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return + // address could have been hijacked when we were in that C# code and we must remove the hijack and + // reflect the correct return address in our exception context record. The other throw helpers don't + // need this because they cannot be tail-called from C#. + INLINE_THREAD_UNHIJACK rax, rcx, rsi // trashes RCX, RSI + mov rsi, [rbx] // rdx <- return address + mov [rsp + rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP], rsi // set 'faulting' IP after unhijack + + mov rsi, rsp // rsi <- ExInfo* + + mov [rsi + OFFSETOF__ExInfo__m_exception], rdx // init the exception object to null + mov byte ptr [rsi + OFFSETOF__ExInfo__m_passNumber], 1 // init to the first pass + mov dword ptr [rsi + OFFSETOF__ExInfo__m_idxCurClause], 0xFFFFFFFF + mov byte ptr [rsi + OFFSETOF__ExInfo__m_kind], 1 // ExKind.Throw + + // link the ExInfo into the thread's ExInfo chain + mov rdx, [rax + OFFSETOF__Thread__m_pExInfoStackHead] + mov [rsi + OFFSETOF__ExInfo__m_pPrevExInfo], rdx // pExInfo->m_pPrevExInfo = m_pExInfoStackHead + mov [rax + OFFSETOF__Thread__m_pExInfoStackHead], rsi // m_pExInfoStackHead = pExInfo + + // set the exception context field on the ExInfo + lea rdx, [rsp + rsp_offsetof_Context] // rdx <- PAL_LIMITED_CONTEXT* + mov [rsi + OFFSETOF__ExInfo__m_pExContext], rdx // init ExInfo.m_pExContext + + // rdi still contains the exception object + // rsi contains the address of the ExInfo + call EXTERNAL_C_FUNC(RhThrowEx) + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowEx2 + + // no return + int 3 + +NESTED_END RhpThrowEx, _TEXT + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// void FASTCALL RhpRethrow() +// +// SUMMARY: Similar to RhpThrowEx, except that it passes along the currently active ExInfo +// +// INPUT: +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpRethrow, _TEXT, NoHandler + + STACKSIZEOF_ExInfo = ((SIZEOF__ExInfo + 15) & (~ 15)) + rsp_offsetof_Context = STACKSIZEOF_ExInfo + + lea rax, [rsp+8] // save the RSP of the throw site + mov rsi, [rsp] // get return address + + xor rdx, rdx + push_register rdx // padding + +// struct PAL_LIMITED_CONTEXT +// { + push_nonvol_reg r15 + push_nonvol_reg r14 + push_nonvol_reg r13 + push_nonvol_reg r12 + push_register rdx // rdx set to 0 + push_nonvol_reg rbx + push_register rdx // rax set to 0 + push_nonvol_reg rbp + push_register rax // 'faulting' RSP + push_register rsi // 'faulting' IP +// } + + // allocate space for the ExInfo + alloc_stack STACKSIZEOF_ExInfo + + END_PROLOGUE + + INLINE_GETTHREAD + + mov rsi, rsp // rsi <- ExInfo* + + mov [rsi + OFFSETOF__ExInfo__m_exception], rdx // init the exception object to null + mov byte ptr [rsi + OFFSETOF__ExInfo__m_passNumber], 1 // init to the first pass + mov dword ptr [rsi + OFFSETOF__ExInfo__m_idxCurClause], 0xFFFFFFFF + mov byte ptr [rsi + OFFSETOF__ExInfo__m_kind], 0 // init to a deterministic value (ExKind.None) + + + // link the ExInfo into the thread's ExInfo chain + mov rdi, [rax + OFFSETOF__Thread__m_pExInfoStackHead] // rdi <- currently active ExInfo + mov [rsi + OFFSETOF__ExInfo__m_pPrevExInfo], rdi // pExInfo->m_pPrevExInfo = m_pExInfoStackHead + mov [rax + OFFSETOF__Thread__m_pExInfoStackHead], rsi // m_pExInfoStackHead = pExInfo + + // set the exception context field on the ExInfo + lea rdx, [rsp + rsp_offsetof_Context] // rdx <- PAL_LIMITED_CONTEXT* + mov [rsi + OFFSETOF__ExInfo__m_pExContext], rdx // init ExInfo.m_pExContext + + // rdi contains the currently active ExInfo + // rsi contains the address of the new ExInfo + call EXTERNAL_C_FUNC(RhRethrow) + + EXPORT_POINTER_TO_ADDRESS PointerToRhpRethrow2 + + // no return + int 3 + +NESTED_END RhpRethrow, _TEXT + +// +// Prologue of all funclet calling helpers (RhpCallXXXXFunclet) +// +.macro FUNCLET_CALL_PROLOGUE localsCount, alignStack + + push_nonvol_reg r15 // save preserved regs for OS stackwalker + push_nonvol_reg r14 // ... + push_nonvol_reg r13 // ... + push_nonvol_reg r12 // ... + push_nonvol_reg rbx // ... + push_nonvol_reg rbp // ... + + stack_alloc_size = \localsCount * 8 + \alignStack * 8 + + alloc_stack stack_alloc_size + + END_PROLOGUE +.endm + +// +// Epilogue of all funclet calling helpers (RhpCallXXXXFunclet) +// +.macro FUNCLET_CALL_EPILOGUE + free_stack stack_alloc_size + + pop_nonvol_reg rbp + pop_nonvol_reg rbx + pop_nonvol_reg r12 + pop_nonvol_reg r13 + pop_nonvol_reg r14 + pop_nonvol_reg r15 +.endm + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// void* FASTCALL RhpCallCatchFunclet(RtuObjectRef exceptionObj, void* pHandlerIP, REGDISPLAY* pRegDisplay, +// ExInfo* pExInfo) +// +// INPUT: RDI: exception object +// RSI: handler funclet address +// RDX: REGDISPLAY* +// RCX: ExInfo* +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpCallCatchFunclet, _TEXT, NoHandler + + FUNCLET_CALL_PROLOGUE 6, 1 + + locThread = 0 + locResumeIp = 8 + locArg0 = 0x10 + locArg1 = 0x18 + locArg2 = 0x20 + locArg3 = 0x28 + + mov [rsp + locArg0], rdi // save arguments for later + mov [rsp + locArg1], rsi + mov [rsp + locArg2], rdx + mov [rsp + locArg3], rcx + + mov rbx, rdx + INLINE_GETTHREAD + mov rdx, rbx + + mov [rsp + locThread], rax // save Thread* for later + + // Clear the DoNotTriggerGc state before calling out to our managed catch funclet. + lock and dword ptr [rax + OFFSETOF__Thread__m_ThreadStateFlags], ~TSF_DoNotTriggerGc + + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbx] + mov rbx, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbp] + mov rbp, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR12] + mov r12, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR13] + mov r13, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR14] + mov r14, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR15] + mov r15, [rax] + +#if 0 // _DEBUG // @TODO: temporarily removed because trashing RBP breaks the debugger + // trash the values at the old homes to make sure nobody uses them + mov rcx, 0xbaaddeed + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbx] + mov [rax], rcx + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbp] + mov [rax], rcx + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR12] + mov [rax], rcx + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR13] + mov [rax], rcx + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR14] + mov [rax], rcx + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR15] + mov [rax], rcx +#endif + + mov rdi, [rdx + OFFSETOF__REGDISPLAY__SP] // rdi <- establisher frame + mov rsi, [rsp + locArg0] // rsi <- exception object + call qword ptr [rsp + locArg1] // call handler funclet + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallCatchFunclet2 + + mov rdx, [rsp + locArg2] // rdx <- dispatch context + +#ifdef _DEBUG + // Call into some C++ code to validate the pop of the ExInfo. We only do this in debug because we + // have to spill all the preserved registers and then refill them after the call. + + mov [rsp + locResumeIp], rax // save resume IP for later + + mov rdi, [rdx + OFFSETOF__REGDISPLAY__pRbx] + mov [rdi] , rbx + mov rdi, [rdx + OFFSETOF__REGDISPLAY__pRbp] + mov [rdi] , rbp + mov rdi, [rdx + OFFSETOF__REGDISPLAY__pR12] + mov [rdi] , r12 + mov rdi, [rdx + OFFSETOF__REGDISPLAY__pR13] + mov [rdi] , r13 + mov rdi, [rdx + OFFSETOF__REGDISPLAY__pR14] + mov [rdi] , r14 + mov rdi, [rdx + OFFSETOF__REGDISPLAY__pR15] + mov [rdi] , r15 + + mov rdi, [rsp] // rdi <- Thread* + mov rsi, [rsp + locArg3] // rsi <- current ExInfo * + mov rdx, [rdx + OFFSETOF__REGDISPLAY__SP] // rdx <- resume SP value + call C_FUNC(RhpValidateExInfoPop) + + mov rdx, [rsp + locArg2] // rdx <- dispatch context + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbx] + mov rbx, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbp] + mov rbp, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR12] + mov r12, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR13] + mov r13, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR14] + mov r14, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR15] + mov r15, [rax] + + mov rax, [rsp + locResumeIp] // reload resume IP +#endif + mov rsi, [rsp + locThread] // rsi <- Thread* + + // We must unhijack the thread at this point because the section of stack where the hijack is applied + // may go dead. If it does, then the next time we try to unhijack the thread, it will corrupt the stack. + INLINE_THREAD_UNHIJACK rsi, rdi, rcx // Thread in rsi, trashes rdi and rcx + + mov rdi, [rsp + locArg3] // rdi <- current ExInfo * + mov rdx, [rdx + OFFSETOF__REGDISPLAY__SP] // rdx <- resume SP value + xor ecx, ecx // rcx <- 0 + +LOCAL_LABEL(ExInfoLoop): + mov rdi, [rdi + OFFSETOF__ExInfo__m_pPrevExInfo] // rdi <- next ExInfo + cmp rdi, rcx + je LOCAL_LABEL(ExInfoLoopDone) // we're done if it's null + cmp rdi, rdx + jl LOCAL_LABEL(ExInfoLoop) // keep looping if it's lower than the new SP + +LOCAL_LABEL(ExInfoLoopDone): + mov [rsi + OFFSETOF__Thread__m_pExInfoStackHead], rdi // store the new head on the Thread + + // reset RSP and jump to the continuation address + mov rsp, rdx // reset the SP + jmp rax + +NESTED_END RhpCallCatchFunclet, _TEXT + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// void FASTCALL RhpCallFinallyFunclet(void* pHandlerIP, REGDISPLAY* pRegDisplay) +// +// INPUT: RDI: handler funclet address +// RSI: REGDISPLAY* +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpCallFinallyFunclet, _TEXT, NoHandler + + FUNCLET_CALL_PROLOGUE 3, 0 + + locThread = 0 + locArg0 = 8 + locArg1 = 0x10 + + mov [rsp + locArg0], rdi // save arguments for later + mov [rsp + locArg1], rsi + + mov rbx, rsi + INLINE_GETTHREAD + mov rsi, rbx + + mov [rsp + locThread], rax // save Thread* for later + + // + // We want to suppress hijacking between invocations of subsequent finallys. We do this because we + // cannot tolerate a GC after one finally has run (and possibly side-effected the GC state of the + // method) and then been popped off the stack, leaving behind no trace of its effect. + // + // So we clear the state before and set it after invocation of the handler. + // + lock and dword ptr [rax + OFFSETOF__Thread__m_ThreadStateFlags], ~TSF_DoNotTriggerGc + + mov rax, [rsi + OFFSETOF__REGDISPLAY__pRbx] + mov rbx, [rax] + mov rax, [rsi + OFFSETOF__REGDISPLAY__pRbp] + mov rbp, [rax] + mov rax, [rsi + OFFSETOF__REGDISPLAY__pR12] + mov r12, [rax] + mov rax, [rsi + OFFSETOF__REGDISPLAY__pR13] + mov r13, [rax] + mov rax, [rsi + OFFSETOF__REGDISPLAY__pR14] + mov r14, [rax] + mov rax, [rsi + OFFSETOF__REGDISPLAY__pR15] + mov r15, [rax] + +#if 0 // _DEBUG // @TODO: temporarily removed because trashing RBP breaks the debugger + // trash the values at the old homes to make sure nobody uses them + mov rcx, 0xbaaddeed + mov rax, [rsi + OFFSETOF__REGDISPLAY__pRbx] + mov [rax], rcx + mov rax, [rsi + OFFSETOF__REGDISPLAY__pRbp] + mov [rax], rcx + mov rax, [rsi + OFFSETOF__REGDISPLAY__pR12] + mov [rax], rcx + mov rax, [rsi + OFFSETOF__REGDISPLAY__pR13] + mov [rax], rcx + mov rax, [rsi + OFFSETOF__REGDISPLAY__pR14] + mov [rax], rcx + mov rax, [rsi + OFFSETOF__REGDISPLAY__pR15] + mov [rax], rcx +#endif + + mov rdi, [rsi + OFFSETOF__REGDISPLAY__SP] // rdi <- establisher frame + call qword ptr [rsp + locArg0] // handler funclet address + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFinallyFunclet2 + + mov rsi, [rsp + locArg1] // rsi <- regdisplay + + mov rax, [rsi + OFFSETOF__REGDISPLAY__pRbx] + mov [rax] , rbx + mov rax, [rsi + OFFSETOF__REGDISPLAY__pRbp] + mov [rax] , rbp + mov rax, [rsi + OFFSETOF__REGDISPLAY__pR12] + mov [rax] , r12 + mov rax, [rsi + OFFSETOF__REGDISPLAY__pR13] + mov [rax] , r13 + mov rax, [rsi + OFFSETOF__REGDISPLAY__pR14] + mov [rax] , r14 + mov rax, [rsi + OFFSETOF__REGDISPLAY__pR15] + mov [rax] , r15 + + mov rax, [rsp + locThread] // rax <- Thread* + lock or dword ptr [rax + OFFSETOF__Thread__m_ThreadStateFlags], TSF_DoNotTriggerGc + + FUNCLET_CALL_EPILOGUE + + ret + +NESTED_END RhpCallFinallyFunclet, _TEXT + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// void* FASTCALL RhpCallFilterFunclet(RtuObjectRef exceptionObj, void* pFilterIP, REGDISPLAY* pRegDisplay) +// +// INPUT: RDI: exception object +// RSI: filter funclet address +// RDX: REGDISPLAY* +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpCallFilterFunclet, _TEXT, NoHandler + + FUNCLET_CALL_PROLOGUE 0, 1 + + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbp] + mov rbp, [rax] + + mov rax, rsi // rax <- handler funclet address + mov rsi, rdi // rsi <- exception object + mov rdi, [rdx + OFFSETOF__REGDISPLAY__SP] // rdi <- establisher frame + call rax + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2 + + // RAX contains the result of the filter execution + + FUNCLET_CALL_EPILOGUE + + ret + +NESTED_END RhpCallFilterFunclet, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.asm b/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.asm new file mode 100644 index 00000000000000..a5a48de02a42ec --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.asm @@ -0,0 +1,679 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include asmmacros.inc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowHwEx +;; +;; INPUT: RCX: exception code of fault +;; RDX: faulting RIP +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NESTED_ENTRY RhpThrowHwEx, _TEXT + + SIZEOF_XmmSaves equ SIZEOF__PAL_LIMITED_CONTEXT - OFFSETOF__PAL_LIMITED_CONTEXT__Xmm6 + STACKSIZEOF_ExInfo equ ((SIZEOF__ExInfo + 15) AND (NOT 15)) + + SIZEOF_OutgoingScratch equ 20h + rsp_offsetof_ExInfo equ SIZEOF_OutgoingScratch + rsp_offsetof_Context equ SIZEOF_OutgoingScratch + STACKSIZEOF_ExInfo + + mov rax, rsp ;; save the faulting RSP + + ;; Align the stack towards zero + and rsp, -16 + + ;; Push the expected "machine frame" for the unwinder to see. All that it looks at is the faulting + ;; RSP and RIP, so we push zero for the others. + xor r8, r8 + push r8 ;; SS + push rax ;; faulting RSP + pushfq ;; EFLAGS + push r8 ;; CS + push rdx ;; faulting RIP + + ; Tell the unwinder that the frame is there now + .pushframe + + alloc_stack SIZEOF_XmmSaves + 8h ;; reserve stack for the xmm saves (+8h to realign stack) + push_vol_reg r8 ;; padding + push_nonvol_reg r15 + push_nonvol_reg r14 + push_nonvol_reg r13 + push_nonvol_reg r12 + push_nonvol_reg rbx + push_vol_reg r8 + push_nonvol_reg rsi + push_nonvol_reg rdi + push_nonvol_reg rbp + push_vol_reg rax ;; faulting RSP + push_vol_reg rdx ;; faulting IP + + ;; allocate outgoing args area and space for the ExInfo + alloc_stack SIZEOF_OutgoingScratch + STACKSIZEOF_ExInfo + + save_xmm128_postrsp Xmm6 , rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm6 + save_xmm128_postrsp Xmm7 , rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm7 + save_xmm128_postrsp Xmm8 , rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm8 + save_xmm128_postrsp Xmm9 , rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm9 + save_xmm128_postrsp Xmm10, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm10 + save_xmm128_postrsp Xmm11, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm11 + save_xmm128_postrsp Xmm12, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm12 + save_xmm128_postrsp Xmm13, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm13 + save_xmm128_postrsp Xmm14, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm14 + save_xmm128_postrsp Xmm15, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm15 + + END_PROLOGUE + + INLINE_GETTHREAD rax, rbx ;; rax <- Thread*, rbx is trashed + + lea rdx, [rsp + rsp_offsetof_ExInfo] ;; rdx <- ExInfo* + + xor r8, r8 + mov [rdx + OFFSETOF__ExInfo__m_exception], r8 ;; init the exception object to null + mov byte ptr [rdx + OFFSETOF__ExInfo__m_passNumber], 1 ;; init to the first pass + mov dword ptr [rdx + OFFSETOF__ExInfo__m_idxCurClause], 0FFFFFFFFh + mov byte ptr [rdx + OFFSETOF__ExInfo__m_kind], 2 ;; ExKind.HardwareFault + + ;; link the ExInfo into the thread's ExInfo chain + mov r8, [rax + OFFSETOF__Thread__m_pExInfoStackHead] + mov [rdx + OFFSETOF__ExInfo__m_pPrevExInfo], r8 ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + mov [rax + OFFSETOF__Thread__m_pExInfoStackHead], rdx ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + lea r8, [rsp + rsp_offsetof_Context] ;; r8 <- PAL_LIMITED_CONTEXT* + mov [rdx + OFFSETOF__ExInfo__m_pExContext], r8 ;; init ExInfo.m_pExContext + + ;; rcx still contains the exception code + ;; rdx contains the address of the ExInfo + call RhThrowHwEx + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowHwEx2 + + ;; no return + int 3 + +NESTED_END RhpThrowHwEx, _TEXT + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowEx +;; +;; INPUT: RCX: exception object +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NESTED_ENTRY RhpThrowEx, _TEXT + + SIZEOF_XmmSaves equ SIZEOF__PAL_LIMITED_CONTEXT - OFFSETOF__PAL_LIMITED_CONTEXT__Xmm6 + STACKSIZEOF_ExInfo equ ((SIZEOF__ExInfo + 15) AND (NOT 15)) + + SIZEOF_OutgoingScratch equ 20h + rsp_offsetof_ExInfo equ SIZEOF_OutgoingScratch + rsp_offsetof_Context equ SIZEOF_OutgoingScratch + STACKSIZEOF_ExInfo + + lea rax, [rsp+8] ;; save the RSP of the throw site + mov rdx, [rsp] ;; get return address + + xor r8, r8 + + alloc_stack SIZEOF_XmmSaves + 8h ;; reserve stack for the xmm saves (+8h to realign stack) + push_vol_reg r8 ;; padding + push_nonvol_reg r15 + push_nonvol_reg r14 + push_nonvol_reg r13 + push_nonvol_reg r12 + push_nonvol_reg rbx + push_vol_reg r8 + push_nonvol_reg rsi + push_nonvol_reg rdi + push_nonvol_reg rbp + push_vol_reg rax ;; 'faulting' RSP + push_vol_reg rdx ;; 'faulting' IP + + ;; allocate outgoing args area and space for the ExInfo + alloc_stack SIZEOF_OutgoingScratch + STACKSIZEOF_ExInfo + + save_xmm128_postrsp Xmm6 , rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm6 + save_xmm128_postrsp Xmm7 , rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm7 + save_xmm128_postrsp Xmm8 , rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm8 + save_xmm128_postrsp Xmm9 , rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm9 + save_xmm128_postrsp Xmm10, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm10 + save_xmm128_postrsp Xmm11, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm11 + save_xmm128_postrsp Xmm12, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm12 + save_xmm128_postrsp Xmm13, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm13 + save_xmm128_postrsp Xmm14, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm14 + save_xmm128_postrsp Xmm15, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm15 + + END_PROLOGUE + + INLINE_GETTHREAD rax, rbx ;; rax <- Thread*, rbx is trashed + + lea rbx, [rsp + rsp_offsetof_Context + SIZEOF__PAL_LIMITED_CONTEXT + 8h] ;; rbx <- addr of return address + + ;; There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return + ;; address could have been hijacked when we were in that C# code and we must remove the hijack and + ;; reflect the correct return address in our exception context record. The other throw helpers don't + ;; need this because they cannot be tail-called from C#. + INLINE_THREAD_UNHIJACK rax, r9, rdx ;; trashes R9, RDX + mov rdx, [rbx] ;; rdx <- return address + mov [rsp + rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP], rdx ;; set 'faulting' IP after unhijack + + lea rdx, [rsp + rsp_offsetof_ExInfo] ;; rdx <- ExInfo* + + mov [rdx + OFFSETOF__ExInfo__m_exception], r8 ;; init the exception object to null + mov byte ptr [rdx + OFFSETOF__ExInfo__m_passNumber], 1 ;; init to the first pass + mov dword ptr [rdx + OFFSETOF__ExInfo__m_idxCurClause], 0FFFFFFFFh + mov byte ptr [rdx + OFFSETOF__ExInfo__m_kind], 1 ;; ExKind.Throw + + ;; link the ExInfo into the thread's ExInfo chain + mov r8, [rax + OFFSETOF__Thread__m_pExInfoStackHead] + mov [rdx + OFFSETOF__ExInfo__m_pPrevExInfo], r8 ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + mov [rax + OFFSETOF__Thread__m_pExInfoStackHead], rdx ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + lea r8, [rsp + rsp_offsetof_Context] ;; r8 <- PAL_LIMITED_CONTEXT* + mov [rdx + OFFSETOF__ExInfo__m_pExContext], r8 ;; init ExInfo.m_pExContext + + ;; rcx still contains the exception object + ;; rdx contains the address of the ExInfo + call RhThrowEx + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowEx2 + + ;; no return + int 3 + +NESTED_END RhpThrowEx, _TEXT + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void FASTCALL RhpRethrow() +;; +;; SUMMARY: Similar to RhpThrowEx, except that it passes along the currently active ExInfo +;; +;; INPUT: +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NESTED_ENTRY RhpRethrow, _TEXT + + SIZEOF_XmmSaves equ SIZEOF__PAL_LIMITED_CONTEXT - OFFSETOF__PAL_LIMITED_CONTEXT__Xmm6 + STACKSIZEOF_ExInfo equ ((SIZEOF__ExInfo + 15) AND (NOT 15)) + + SIZEOF_OutgoingScratch equ 20h + rsp_offsetof_ExInfo equ SIZEOF_OutgoingScratch + rsp_offsetof_Context equ SIZEOF_OutgoingScratch + STACKSIZEOF_ExInfo + + lea rax, [rsp+8] ;; save the RSP of the throw site + mov rdx, [rsp] ;; get return address + + xor r8, r8 + + alloc_stack SIZEOF_XmmSaves + 8h ;; reserve stack for the xmm saves (+8h to realign stack) + push_vol_reg r8 ;; padding + push_nonvol_reg r15 + push_nonvol_reg r14 + push_nonvol_reg r13 + push_nonvol_reg r12 + push_nonvol_reg rbx + push_vol_reg r8 + push_nonvol_reg rsi + push_nonvol_reg rdi + push_nonvol_reg rbp + push_vol_reg rax ;; 'faulting' RSP + push_vol_reg rdx ;; 'faulting' IP + + ;; allocate outgoing args area and space for the ExInfo + alloc_stack SIZEOF_OutgoingScratch + STACKSIZEOF_ExInfo + + save_xmm128_postrsp Xmm6 , rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm6 + save_xmm128_postrsp Xmm7 , rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm7 + save_xmm128_postrsp Xmm8 , rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm8 + save_xmm128_postrsp Xmm9 , rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm9 + save_xmm128_postrsp Xmm10, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm10 + save_xmm128_postrsp Xmm11, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm11 + save_xmm128_postrsp Xmm12, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm12 + save_xmm128_postrsp Xmm13, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm13 + save_xmm128_postrsp Xmm14, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm14 + save_xmm128_postrsp Xmm15, rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__Xmm15 + + END_PROLOGUE + + INLINE_GETTHREAD rax, rbx ;; rax <- Thread*, rbx is trashed + + lea rdx, [rsp + rsp_offsetof_ExInfo] ;; rdx <- ExInfo* + + mov [rdx + OFFSETOF__ExInfo__m_exception], r8 ;; init the exception object to null + mov byte ptr [rdx + OFFSETOF__ExInfo__m_passNumber], 1 ;; init to the first pass + mov dword ptr [rdx + OFFSETOF__ExInfo__m_idxCurClause], 0FFFFFFFFh + mov byte ptr [rdx + OFFSETOF__ExInfo__m_kind], 0 ;; init to a deterministic value (ExKind.None) + + + ;; link the ExInfo into the thread's ExInfo chain + mov rcx, [rax + OFFSETOF__Thread__m_pExInfoStackHead] ;; rcx <- currently active ExInfo + mov [rdx + OFFSETOF__ExInfo__m_pPrevExInfo], rcx ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + mov [rax + OFFSETOF__Thread__m_pExInfoStackHead], rdx ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + lea r8, [rsp + rsp_offsetof_Context] ;; r8 <- PAL_LIMITED_CONTEXT* + mov [rdx + OFFSETOF__ExInfo__m_pExContext], r8 ;; init ExInfo.m_pExContext + + ;; rcx contains the currently active ExInfo + ;; rdx contains the address of the new ExInfo + call RhRethrow + + EXPORT_POINTER_TO_ADDRESS PointerToRhpRethrow2 + + ;; no return + int 3 + +NESTED_END RhpRethrow, _TEXT + +;; +;; Prologue of all funclet calling helpers (RhpCallXXXXFunclet) +;; +FUNCLET_CALL_PROLOGUE macro localsCount, alignStack + + push_nonvol_reg r15 ;; save preserved regs for OS stackwalker + push_nonvol_reg r14 ;; ... + push_nonvol_reg r13 ;; ... + push_nonvol_reg r12 ;; ... + push_nonvol_reg rbx ;; ... + push_nonvol_reg rsi ;; ... + push_nonvol_reg rdi ;; ... + push_nonvol_reg rbp ;; ... + + arguments_scratch_area_size = 20h + xmm_save_area_size = 10 * 10h ;; xmm6..xmm15 save area + stack_alloc_size = arguments_scratch_area_size + localsCount * 8 + alignStack * 8 + xmm_save_area_size + rsp_offsetof_arguments = stack_alloc_size + 8*8h + 8h + rsp_offsetof_locals = arguments_scratch_area_size + xmm_save_area_size + + alloc_stack stack_alloc_size + + save_xmm128_postrsp xmm6, (arguments_scratch_area_size + 0 * 10h) + save_xmm128_postrsp xmm7, (arguments_scratch_area_size + 1 * 10h) + save_xmm128_postrsp xmm8, (arguments_scratch_area_size + 2 * 10h) + save_xmm128_postrsp xmm9, (arguments_scratch_area_size + 3 * 10h) + save_xmm128_postrsp xmm10, (arguments_scratch_area_size + 4 * 10h) + save_xmm128_postrsp xmm11, (arguments_scratch_area_size + 5 * 10h) + save_xmm128_postrsp xmm12, (arguments_scratch_area_size + 6 * 10h) + save_xmm128_postrsp xmm13, (arguments_scratch_area_size + 7 * 10h) + save_xmm128_postrsp xmm14, (arguments_scratch_area_size + 8 * 10h) + save_xmm128_postrsp xmm15, (arguments_scratch_area_size + 9 * 10h) + + END_PROLOGUE +endm + +;; +;; Epilogue of all funclet calling helpers (RhpCallXXXXFunclet) +;; +FUNCLET_CALL_EPILOGUE macro + movdqa xmm6, [rsp + arguments_scratch_area_size + 0 * 10h] + movdqa xmm7, [rsp + arguments_scratch_area_size + 1 * 10h] + movdqa xmm8, [rsp + arguments_scratch_area_size + 2 * 10h] + movdqa xmm9, [rsp + arguments_scratch_area_size + 3 * 10h] + movdqa xmm10, [rsp + arguments_scratch_area_size + 4 * 10h] + movdqa xmm11, [rsp + arguments_scratch_area_size + 5 * 10h] + movdqa xmm12, [rsp + arguments_scratch_area_size + 6 * 10h] + movdqa xmm13, [rsp + arguments_scratch_area_size + 7 * 10h] + movdqa xmm14, [rsp + arguments_scratch_area_size + 8 * 10h] + movdqa xmm15, [rsp + arguments_scratch_area_size + 9 * 10h] + + add rsp, stack_alloc_size + pop rbp + pop rdi + pop rsi + pop rbx + pop r12 + pop r13 + pop r14 + pop r15 +endm + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* FASTCALL RhpCallCatchFunclet(RtuObjectRef exceptionObj, void* pHandlerIP, REGDISPLAY* pRegDisplay, +;; ExInfo* pExInfo) +;; +;; INPUT: RCX: exception object +;; RDX: handler funclet address +;; R8: REGDISPLAY* +;; R9: ExInfo* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NESTED_ENTRY RhpCallCatchFunclet, _TEXT + + FUNCLET_CALL_PROLOGUE 3, 0 + + ;; locals + rsp_offsetof_thread = rsp_offsetof_locals + rsp_offsetof_resume_ip = rsp_offsetof_locals + 8; + rsp_offsetof_is_handling_thread_abort = rsp_offsetof_locals + 16; + + mov [rsp + rsp_offsetof_arguments + 0h], rcx ;; save arguments for later + mov [rsp + rsp_offsetof_arguments + 8h], rdx + mov [rsp + rsp_offsetof_arguments + 10h], r8 + mov [rsp + rsp_offsetof_arguments + 18h], r9 + + INLINE_GETTHREAD rax, rbx ;; rax <- Thread*, rbx is trashed + mov [rsp + rsp_offsetof_thread], rax ;; save Thread* for later + + cmp rcx, [rax + OFFSETOF__Thread__m_threadAbortException] + setz byte ptr [rsp + rsp_offsetof_is_handling_thread_abort] + + ;; Clear the DoNotTriggerGc state before calling out to our managed catch funclet. + lock and dword ptr [rax + OFFSETOF__Thread__m_ThreadStateFlags], NOT TSF_DoNotTriggerGc + + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRbx] + mov rbx, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRbp] + mov rbp, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRsi] + mov rsi, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRdi] + mov rdi, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pR12] + mov r12, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pR13] + mov r13, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pR14] + mov r14, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pR15] + mov r15, [rax] + +if 0 ;; _DEBUG ;; @TODO: temporarily removed because trashing RBP breaks the debugger + ;; trash the values at the old homes to make sure nobody uses them + mov r9, 0baaddeedh + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRbx] + mov [rax], r9 + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRbp] + mov [rax], r9 + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRsi] + mov [rax], r9 + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRdi] + mov [rax], r9 + mov rax, [r8 + OFFSETOF__REGDISPLAY__pR12] + mov [rax], r9 + mov rax, [r8 + OFFSETOF__REGDISPLAY__pR13] + mov [rax], r9 + mov rax, [r8 + OFFSETOF__REGDISPLAY__pR14] + mov [rax], r9 + mov rax, [r8 + OFFSETOF__REGDISPLAY__pR15] + mov [rax], r9 +endif + + movdqa xmm6, [r8 + OFFSETOF__REGDISPLAY__Xmm + 0*10h] + movdqa xmm7, [r8 + OFFSETOF__REGDISPLAY__Xmm + 1*10h] + movdqa xmm8, [r8 + OFFSETOF__REGDISPLAY__Xmm + 2*10h] + movdqa xmm9, [r8 + OFFSETOF__REGDISPLAY__Xmm + 3*10h] + movdqa xmm10,[r8 + OFFSETOF__REGDISPLAY__Xmm + 4*10h] + + movdqa xmm11,[r8 + OFFSETOF__REGDISPLAY__Xmm + 5*10h] + movdqa xmm12,[r8 + OFFSETOF__REGDISPLAY__Xmm + 6*10h] + movdqa xmm13,[r8 + OFFSETOF__REGDISPLAY__Xmm + 7*10h] + movdqa xmm14,[r8 + OFFSETOF__REGDISPLAY__Xmm + 8*10h] + movdqa xmm15,[r8 + OFFSETOF__REGDISPLAY__Xmm + 9*10h] + + mov rcx, [r8 + OFFSETOF__REGDISPLAY__SP] ;; rcx <- establisher frame + mov rdx, [rsp + rsp_offsetof_arguments + 0h] ;; rdx <- exception object + call qword ptr [rsp + rsp_offsetof_arguments + 8h] ;; call handler funclet + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallCatchFunclet2 + + mov r8, [rsp + rsp_offsetof_arguments + 10h] ;; r8 <- dispatch context + +ifdef _DEBUG + ;; Call into some C++ code to validate the pop of the ExInfo. We only do this in debug because we + ;; have to spill all the preserved registers and then refill them after the call. + mov [rsp + rsp_offsetof_resume_ip], rax ;; save resume IP for later + + mov rcx, [r8 + OFFSETOF__REGDISPLAY__pRbx] + mov [rcx] , rbx + mov rcx, [r8 + OFFSETOF__REGDISPLAY__pRbp] + mov [rcx] , rbp + mov rcx, [r8 + OFFSETOF__REGDISPLAY__pRsi] + mov [rcx] , rsi + mov rcx, [r8 + OFFSETOF__REGDISPLAY__pRdi] + mov [rcx] , rdi + mov rcx, [r8 + OFFSETOF__REGDISPLAY__pR12] + mov [rcx] , r12 + mov rcx, [r8 + OFFSETOF__REGDISPLAY__pR13] + mov [rcx] , r13 + mov rcx, [r8 + OFFSETOF__REGDISPLAY__pR14] + mov [rcx] , r14 + mov rcx, [r8 + OFFSETOF__REGDISPLAY__pR15] + mov [rcx] , r15 + + mov rcx, [rsp + rsp_offsetof_thread] ;; rcx <- Thread* + mov rdx, [rsp + rsp_offsetof_arguments + 18h] ;; rdx <- current ExInfo * + mov r8, [r8 + OFFSETOF__REGDISPLAY__SP] ;; r8 <- resume SP value + call RhpValidateExInfoPop + + mov r8, [rsp + rsp_offsetof_arguments + 10h] ;; r8 <- dispatch context + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRbx] + mov rbx, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRbp] + mov rbp, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRsi] + mov rsi, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRdi] + mov rdi, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pR12] + mov r12, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pR13] + mov r13, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pR14] + mov r14, [rax] + mov rax, [r8 + OFFSETOF__REGDISPLAY__pR15] + mov r15, [rax] + + mov rax, [rsp + rsp_offsetof_resume_ip] ;; reload resume IP +endif + mov rdx, [rsp + rsp_offsetof_thread] ;; rdx <- Thread* + + ;; We must unhijack the thread at this point because the section of stack where the hijack is applied + ;; may go dead. If it does, then the next time we try to unhijack the thread, it will corrupt the stack. + INLINE_THREAD_UNHIJACK rdx, rcx, r9 ;; Thread in rdx, trashes rcx and r9 + + mov rcx, [rsp + rsp_offsetof_arguments + 18h] ;; rcx <- current ExInfo * + mov r8, [r8 + OFFSETOF__REGDISPLAY__SP] ;; r8 <- resume SP value + xor r9d, r9d ;; r9 <- 0 + + @@: mov rcx, [rcx + OFFSETOF__ExInfo__m_pPrevExInfo] ;; rcx <- next ExInfo + cmp rcx, r9 + je @F ;; we're done if it's null + cmp rcx, r8 + jl @B ;; keep looping if it's lower than the new SP + + @@: mov [rdx + OFFSETOF__Thread__m_pExInfoStackHead], rcx ;; store the new head on the Thread + + test [RhpTrapThreads], TrapThreadsFlags_AbortInProgress + jz @f + + ;; test if the exception handled by the catch was the ThreadAbortException + cmp byte ptr [rsp + rsp_offsetof_is_handling_thread_abort], 0 + je @f + + ;; It was the ThreadAbortException, so rethrow it + mov rcx, STATUS_REDHAWK_THREAD_ABORT + mov rdx, rax ;; rdx <- continuation address as exception RIP + mov rsp, r8 ;; reset the SP to resume SP value + jmp RhpThrowHwEx ;; Throw the ThreadAbortException as a special kind of hardware exception + + ;; reset RSP and jump to the continuation address + @@: mov rsp, r8 ;; reset the SP to resume SP value + jmp rax + + +NESTED_END RhpCallCatchFunclet, _TEXT + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void FASTCALL RhpCallFinallyFunclet(void* pHandlerIP, REGDISPLAY* pRegDisplay) +;; +;; INPUT: RCX: handler funclet address +;; RDX: REGDISPLAY* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NESTED_ENTRY RhpCallFinallyFunclet, _TEXT + + FUNCLET_CALL_PROLOGUE 1, 0 + + mov [rsp + rsp_offsetof_arguments + 0h], rcx ;; save arguments for later + mov [rsp + rsp_offsetof_arguments + 8h], rdx + + rsp_offsetof_thread = rsp_offsetof_locals + + INLINE_GETTHREAD rax, rbx ;; rax <- Thread*, rbx is trashed + mov [rsp + rsp_offsetof_thread], rax ;; save Thread* for later + + ;; + ;; We want to suppress hijacking between invocations of subsequent finallys. We do this because we + ;; cannot tolerate a GC after one finally has run (and possibly side-effected the GC state of the + ;; method) and then been popped off the stack, leaving behind no trace of its effect. + ;; + ;; So we clear the state before and set it after invocation of the handler. + ;; + lock and dword ptr [rax + OFFSETOF__Thread__m_ThreadStateFlags], NOT TSF_DoNotTriggerGc + + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbx] + mov rbx, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbp] + mov rbp, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRsi] + mov rsi, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRdi] + mov rdi, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR12] + mov r12, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR13] + mov r13, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR14] + mov r14, [rax] + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR15] + mov r15, [rax] + + movdqa xmm6, [rdx + OFFSETOF__REGDISPLAY__Xmm + 0*10h] + movdqa xmm7, [rdx + OFFSETOF__REGDISPLAY__Xmm + 1*10h] + movdqa xmm8, [rdx + OFFSETOF__REGDISPLAY__Xmm + 2*10h] + movdqa xmm9, [rdx + OFFSETOF__REGDISPLAY__Xmm + 3*10h] + movdqa xmm10,[rdx + OFFSETOF__REGDISPLAY__Xmm + 4*10h] + + movdqa xmm11,[rdx + OFFSETOF__REGDISPLAY__Xmm + 5*10h] + movdqa xmm12,[rdx + OFFSETOF__REGDISPLAY__Xmm + 6*10h] + movdqa xmm13,[rdx + OFFSETOF__REGDISPLAY__Xmm + 7*10h] + movdqa xmm14,[rdx + OFFSETOF__REGDISPLAY__Xmm + 8*10h] + movdqa xmm15,[rdx + OFFSETOF__REGDISPLAY__Xmm + 9*10h] + +if 0 ;; _DEBUG ;; @TODO: temporarily removed because trashing RBP breaks the debugger + ;; trash the values at the old homes to make sure nobody uses them + mov r9, 0baaddeedh + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbx] + mov [rax], r9 + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbp] + mov [rax], r9 + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRsi] + mov [rax], r9 + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRdi] + mov [rax], r9 + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR12] + mov [rax], r9 + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR13] + mov [rax], r9 + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR14] + mov [rax], r9 + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR15] + mov [rax], r9 +endif + + mov rcx, [rdx + OFFSETOF__REGDISPLAY__SP] ;; rcx <- establisher frame + call qword ptr [rsp + rsp_offsetof_arguments + 0h] ;; handler funclet address + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFinallyFunclet2 + + mov rdx, [rsp + rsp_offsetof_arguments + 8h] ;; rdx <- regdisplay + + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbx] + mov [rax] , rbx + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRbp] + mov [rax] , rbp + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRsi] + mov [rax] , rsi + mov rax, [rdx + OFFSETOF__REGDISPLAY__pRdi] + mov [rax] , rdi + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR12] + mov [rax] , r12 + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR13] + mov [rax] , r13 + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR14] + mov [rax] , r14 + mov rax, [rdx + OFFSETOF__REGDISPLAY__pR15] + mov [rax] , r15 + + movdqa [rdx + OFFSETOF__REGDISPLAY__Xmm + 0*10h], xmm6 + movdqa [rdx + OFFSETOF__REGDISPLAY__Xmm + 1*10h], xmm7 + movdqa [rdx + OFFSETOF__REGDISPLAY__Xmm + 2*10h], xmm8 + movdqa [rdx + OFFSETOF__REGDISPLAY__Xmm + 3*10h], xmm9 + movdqa [rdx + OFFSETOF__REGDISPLAY__Xmm + 4*10h], xmm10 + + movdqa [rdx + OFFSETOF__REGDISPLAY__Xmm + 5*10h], xmm11 + movdqa [rdx + OFFSETOF__REGDISPLAY__Xmm + 6*10h], xmm12 + movdqa [rdx + OFFSETOF__REGDISPLAY__Xmm + 7*10h], xmm13 + movdqa [rdx + OFFSETOF__REGDISPLAY__Xmm + 8*10h], xmm14 + movdqa [rdx + OFFSETOF__REGDISPLAY__Xmm + 9*10h], xmm15 + + mov rax, [rsp + rsp_offsetof_thread] ;; rax <- Thread* + lock or dword ptr [rax + OFFSETOF__Thread__m_ThreadStateFlags], TSF_DoNotTriggerGc + + FUNCLET_CALL_EPILOGUE + + ret + +NESTED_END RhpCallFinallyFunclet, _TEXT + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* FASTCALL RhpCallFilterFunclet(RtuObjectRef exceptionObj, void* pFilterIP, REGDISPLAY* pRegDisplay) +;; +;; INPUT: RCX: exception object +;; RDX: filter funclet address +;; R8: REGDISPLAY* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NESTED_ENTRY RhpCallFilterFunclet, _TEXT + + FUNCLET_CALL_PROLOGUE 0, 1 + + mov rax, [r8 + OFFSETOF__REGDISPLAY__pRbp] + mov rbp, [rax] + + mov rax, rdx ;; rax <- handler funclet address + mov rdx, rcx ;; rdx <- exception object + mov rcx, [r8 + OFFSETOF__REGDISPLAY__SP] ;; rcx <- establisher frame + call rax + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2 + + ;; RAX contains the result of the filter execution + + FUNCLET_CALL_EPILOGUE + + ret + +NESTED_END RhpCallFilterFunclet, _TEXT + + end diff --git a/src/coreclr/nativeaot/Runtime/amd64/FloatingPoint.asm b/src/coreclr/nativeaot/Runtime/amd64/FloatingPoint.asm new file mode 100644 index 00000000000000..9a843d0530dfeb --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/FloatingPoint.asm @@ -0,0 +1,57 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include asmmacros.inc + +LEAF_ENTRY RhpFltRemRev, _TEXT + + sub rsp, 18h + + movss dword ptr [rsp + 10h], xmm1 ; divisor + movss dword ptr [rsp + 8h], xmm0 ; dividend + + fld dword ptr [rsp + 10h] ; divisor + fld dword ptr [rsp + 8h] ; dividend + +fremloop: + fprem + fstsw ax + test ax, 0400h + jnz fremloop + + fstp dword ptr [rsp] + movlps xmm0,qword ptr [rsp] + + fstp st(0) + add rsp,18h + ret + +LEAF_END RhpFltRemRev, _TEXT + + +LEAF_ENTRY RhpDblRemRev, _TEXT + + sub rsp, 18h + + movsd qword ptr [rsp + 10h], xmm1 ; divisor + movsd qword ptr [rsp + 8h], xmm0 ; dividend + + fld qword ptr [rsp + 10h] ; divisor + fld qword ptr [rsp + 8h] ; dividend + +fremloopd: + fprem + fstsw ax + test ax, 0400h + jnz fremloopd + + fstp qword ptr [rsp] + movlpd xmm0,qword ptr [rsp] + + fstp st(0) + add rsp,18h + ret + +LEAF_END RhpDblRemRev, _TEXT + + END diff --git a/src/coreclr/nativeaot/Runtime/amd64/GC.asm b/src/coreclr/nativeaot/Runtime/amd64/GC.asm new file mode 100644 index 00000000000000..1dc8a851253712 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/GC.asm @@ -0,0 +1,21 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include AsmMacros.inc + +;; extern "C" DWORD __stdcall xmmYmmStateSupport(); +LEAF_ENTRY xmmYmmStateSupport, _TEXT + mov ecx, 0 ; Specify xcr0 + xgetbv ; result in EDX:EAX + and eax, 06H + cmp eax, 06H ; check OS has enabled both XMM and YMM state support + jne not_supported + mov eax, 1 + jmp done + not_supported: + mov eax, 0 + done: + ret +LEAF_END xmmYmmStateSupport, _TEXT + + end diff --git a/src/coreclr/nativeaot/Runtime/amd64/GcProbe.S b/src/coreclr/nativeaot/Runtime/amd64/GcProbe.S new file mode 100644 index 00000000000000..04f0294ec1fa59 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/GcProbe.S @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include + +LEAF_ENTRY RhpGcPoll, _TEXT + cmp dword ptr [C_VAR(RhpTrapThreads)], TrapThreadsFlags_None + jne LOCAL_LABEL(RhpGcPoll_RarePath) + ret +LOCAL_LABEL(RhpGcPoll_RarePath): + jmp C_FUNC(RhpGcPollRare) + +LEAF_END RhpGcPoll, _TEXT + +NESTED_ENTRY RhpGcPollRare, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME rdi + END_PROLOGUE + call C_FUNC(RhpGcPoll2) + POP_COOP_PINVOKE_FRAME + ret +NESTED_END RhpGcPollRare, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm b/src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm new file mode 100644 index 00000000000000..4426c42009a02c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm @@ -0,0 +1,833 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include AsmMacros.inc + +PROBE_SAVE_FLAGS_EVERYTHING equ DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_ALL_SCRATCH +PROBE_SAVE_FLAGS_RAX_IS_GCREF equ DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_RAX_IS_GCREF + +;; +;; See PUSH_COOP_PINVOKE_FRAME, this macro is very similar, but also saves RAX and accepts the register +;; bitmask in RCX +;; +;; On entry: +;; - BITMASK: bitmask describing pushes, may be volatile register or constant value +;; - RAX: managed function return value, may be an object or byref +;; - preserved regs: need to stay preserved, may contain objects or byrefs +;; - extraStack bytes of stack have already been allocated +;; +;; INVARIANTS +;; - The macro assumes it is called from a prolog, prior to a frame pointer being setup. +;; - All preserved registers remain unchanged from their values in managed code. +;; +PUSH_PROBE_FRAME macro threadReg, trashReg, extraStack, BITMASK + + push_vol_reg rax ; save RAX, it might contain an objectref + lea trashReg, [rsp + 10h + extraStack] + push_vol_reg trashReg ; save caller's RSP + push_nonvol_reg r15 ; save preserved registers + push_nonvol_reg r14 ; .. + push_nonvol_reg r13 ; .. + push_nonvol_reg r12 ; .. + push_nonvol_reg rdi ; .. + push_nonvol_reg rsi ; .. + push_nonvol_reg rbx ; .. + push_vol_reg BITMASK ; save the register bitmask passed in by caller + push_vol_reg threadReg ; Thread * (unused by stackwalker) + push_nonvol_reg rbp ; save caller's RBP + mov trashReg, [rsp + 12*8 + extraStack] ; Find the return address + push_vol_reg trashReg ; save m_RIP + lea trashReg, [rsp + 0] ; trashReg == address of frame + + ;; allocate scratch space and any required alignment + alloc_stack 20h + 10h + (extraStack AND (10h-1)) + + ;; save xmm0 in case it's being used as a return value + movdqa [rsp + 20h], xmm0 + + ; link the frame into the Thread + mov [threadReg + OFFSETOF__Thread__m_pHackPInvokeTunnel], trashReg +endm + +;; +;; Remove the frame from a previous call to PUSH_PROBE_FRAME from the top of the stack and restore preserved +;; registers and return value to their values from before the probe was called (while also updating any +;; object refs or byrefs). +;; +;; NOTE: does NOT deallocate the 'extraStack' portion of the stack, the user of this macro must do that. +;; +POP_PROBE_FRAME macro extraStack + movdqa xmm0, [rsp + 20h] + add rsp, 20h + 10h + (extraStack AND (10h-1)) + 8 + pop rbp + pop rax ; discard Thread* + pop rax ; discard BITMASK + pop rbx + pop rsi + pop rdi + pop r12 + pop r13 + pop r14 + pop r15 + pop rax ; discard caller RSP + pop rax +endm + +;; +;; Macro to clear the hijack state. This is safe to do because the suspension code will not Unhijack this +;; thread if it finds it at an IP that isn't managed code. +;; +;; Register state on entry: +;; RDX: thread pointer +;; +;; Register state on exit: +;; RCX: trashed +;; +ClearHijackState macro + xor ecx, ecx + mov [rdx + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], rcx + mov [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress], rcx +endm + + +;; +;; The prolog for all GC suspension hijacks (normal and stress). Fixes up the hijacked return address, and +;; clears the hijack state. +;; +;; Register state on entry: +;; All registers correct for return to the original return address. +;; +;; Register state on exit: +;; RCX: trashed +;; RDX: thread pointer +;; +FixupHijackedCallstack macro + + ;; rdx <- GetThread(), TRASHES rcx + INLINE_GETTHREAD rdx, rcx + + ;; + ;; Fix the stack by pushing the original return address + ;; + mov rcx, [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress] + push rcx + + ClearHijackState +endm + +;; +;; Set the Thread state and wait for a GC to complete. +;; +;; Register state on entry: +;; RBX: thread pointer +;; +;; Register state on exit: +;; RBX: thread pointer +;; All other registers trashed +;; + +EXTERN RhpWaitForGCNoAbort : PROC + +WaitForGCCompletion macro + test dword ptr [rbx + OFFSETOF__Thread__m_ThreadStateFlags], TSF_SuppressGcStress + TSF_DoNotTriggerGc + jnz @F + + mov rcx, [rbx + OFFSETOF__Thread__m_pHackPInvokeTunnel] + call RhpWaitForGCNoAbort +@@: + +endm + + +EXTERN RhpPInvokeExceptionGuard : PROC + +;; +;; +;; +;; GC Probe Hijack targets +;; +;; +NESTED_ENTRY RhpGcProbeHijackScalar, _TEXT, RhpPInvokeExceptionGuard + END_PROLOGUE + FixupHijackedCallstack + mov ecx, DEFAULT_FRAME_SAVE_FLAGS + jmp RhpGcProbe +NESTED_END RhpGcProbeHijackScalar, _TEXT + +NESTED_ENTRY RhpGcProbeHijackObject, _TEXT, RhpPInvokeExceptionGuard + END_PROLOGUE + FixupHijackedCallstack + mov ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_RAX_IS_GCREF + jmp RhpGcProbe +NESTED_END RhpGcProbeHijackObject, _TEXT + +NESTED_ENTRY RhpGcProbeHijackByref, _TEXT, RhpPInvokeExceptionGuard + END_PROLOGUE + FixupHijackedCallstack + mov ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_RAX_IS_BYREF + jmp RhpGcProbe +NESTED_END RhpGcProbeHijackByref, _TEXT + +ifdef FEATURE_GC_STRESS +;; +;; +;; GC Stress Hijack targets +;; +;; +LEAF_ENTRY RhpGcStressHijackScalar, _TEXT + FixupHijackedCallstack + mov ecx, DEFAULT_FRAME_SAVE_FLAGS + jmp RhpGcStressProbe +LEAF_END RhpGcStressHijackScalar, _TEXT + +LEAF_ENTRY RhpGcStressHijackObject, _TEXT + FixupHijackedCallstack + mov ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_RAX_IS_GCREF + jmp RhpGcStressProbe +LEAF_END RhpGcStressHijackObject, _TEXT + +LEAF_ENTRY RhpGcStressHijackByref, _TEXT + FixupHijackedCallstack + mov ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_RAX_IS_BYREF + jmp RhpGcStressProbe +LEAF_END RhpGcStressHijackByref, _TEXT + +;; +;; Worker for our GC stress probes. Do not call directly!! +;; Instead, go through RhpGcStressHijack{Scalar|Object|Byref}. +;; This worker performs the GC Stress work and returns to the original return address. +;; +;; Register state on entry: +;; RDX: thread pointer +;; RCX: register bitmask +;; +;; Register state on exit: +;; Scratch registers, except for RAX, have been trashed +;; All other registers restored as they were when the hijack was first reached. +;; +NESTED_ENTRY RhpGcStressProbe, _TEXT + PUSH_PROBE_FRAME rdx, rax, 0, rcx + END_PROLOGUE + + call REDHAWKGCINTERFACE__STRESSGC + + POP_PROBE_FRAME 0 + ret +NESTED_END RhpGcStressProbe, _TEXT + +endif ;; FEATURE_GC_STRESS + +EXTERN RhpThrowHwEx : PROC + +NESTED_ENTRY RhpGcProbe, _TEXT + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jnz @f + ret +@@: + PUSH_PROBE_FRAME rdx, rax, 0, rcx + END_PROLOGUE + + mov rbx, rdx + WaitForGCCompletion + + mov rax, [rbx + OFFSETOF__Thread__m_pHackPInvokeTunnel] + test dword ptr [rax + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_THREAD_ABORT + jnz Abort + POP_PROBE_FRAME 0 + ret +Abort: + POP_PROBE_FRAME 0 + mov rcx, STATUS_REDHAWK_THREAD_ABORT + pop rdx ;; return address as exception RIP + jmp RhpThrowHwEx ;; Throw the ThreadAbortException as a special kind of hardware exception + +NESTED_END RhpGcProbe, _TEXT + + +ifdef FEATURE_GC_STRESS +;; PAL_LIMITED_CONTEXT, 6 xmm regs to save, 2 scratch regs to save, plus 20h bytes for scratch space +RhpHijackForGcStress_FrameSize equ SIZEOF__PAL_LIMITED_CONTEXT + 6*10h + 2*8h + 20h + +; ----------------------------------------------------------------------------------------------------------- +; RhpHijackForGcStress +; +; Called at the beginning of the epilog when a method is bound with /gcstress +; +; N.B. -- Leaf frames may not have aligned the stack or reserved any scratch space on the stack. Also, in +; order to have a resonable stacktrace in the debugger, we must use the .pushframe unwind directive. +; +; N.B. #2 -- The "EH jump epilog" codegen depends on rcx/rdx being preserved across this call. We currently +; will trash R8-R11, but we can do better, if necessary. +; +NESTED_ENTRY RhpHijackForGcStress, _TEXT + + lea r10, [rsp+8] ;; save the original RSP (prior to call) + mov r11, [rsp] ;; get the return address + + ;; Align the stack + and rsp, -16 + + ;; Push the expected "machine frame" for the unwinder to see. All that it looks at is the RSP and + ;; RIP, so we push zero for the others. + xor r8, r8 + push r8 ;; just aligning the stack + push r8 ;; SS + push r10 ;; original RSP + push r8 ;; EFLAGS + push r8 ;; CS + push r11 ;; return address + + ; Tell the unwinder that the frame is there now + .pushframe + + alloc_stack RhpHijackForGcStress_FrameSize + END_PROLOGUE + + ;; Save xmm scratch regs -- this is probably overkill, only the return value reg is + ;; likely to be interesting at this point, but it's a bit ambiguous. + movdqa [rsp + 20h + 0*10h], xmm0 + movdqa [rsp + 20h + 1*10h], xmm1 + movdqa [rsp + 20h + 2*10h], xmm2 + movdqa [rsp + 20h + 3*10h], xmm3 + movdqa [rsp + 20h + 4*10h], xmm4 + movdqa [rsp + 20h + 5*10h], xmm5 + + mov [rsp + 20h + 6*10h + 0*8h], rcx + mov [rsp + 20h + 6*10h + 1*8h], rdx + + ;; + ;; Setup a PAL_LIMITED_CONTEXT that looks like what you'd get if you had suspended this thread at the + ;; IP after the call to this helper. + ;; + ;; This is very likely overkill since the calculation of the return address should only need RSP and + ;; RBP, but this is test code, so I'm not too worried about efficiency. + ;; + mov [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__IP], r11 ; rip at callsite + mov [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__Rsp], r10 ; rsp at callsite + mov [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__Rbp], rbp + mov [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__Rdi], rdi + mov [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__Rsi], rsi + mov [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__Rax], rax + mov [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__Rbx], rbx + + mov [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__R12], r12 + mov [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__R13], r13 + mov [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__R14], r14 + mov [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__R15], r15 + + lea rcx, [rsp + 20h + 6*10h + 2*8h] ;; address of PAL_LIMITED_CONTEXT + call THREAD__HIJACKFORGCSTRESS + + ;; Note: we only restore the scratch registers here. No GC has occured, so restoring + ;; the callee saved ones is unnecessary. + mov rax, [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__Rax] + mov rcx, [rsp + 20h + 6*10h + 0*8h] + mov rdx, [rsp + 20h + 6*10h + 1*8h] + + ;; Restore xmm scratch regs + movdqa xmm0, [rsp + 20h + 0*10h] + movdqa xmm1, [rsp + 20h + 1*10h] + movdqa xmm2, [rsp + 20h + 2*10h] + movdqa xmm3, [rsp + 20h + 3*10h] + movdqa xmm4, [rsp + 20h + 4*10h] + movdqa xmm5, [rsp + 20h + 5*10h] + + ;; epilog + mov r10, [rsp + 20h + 6*10h + 2*8h + OFFSETOF__PAL_LIMITED_CONTEXT__Rsp] + lea rsp, [r10 - 8] ;; adjust RSP to point back at the return address + ret +NESTED_END RhpHijackForGcStress, _TEXT + +endif ;; FEATURE_GC_STRESS + + +;; +;; The following functions are _jumped_ to when we need to transfer control from one method to another for EH +;; dispatch. These are needed to properly coordinate with the GC hijacking logic. We are essentially replacing +;; the return from the throwing method with a jump to the handler in the caller, but we need to be aware of +;; any return address hijack that may be in place for GC suspension. These routines use a quick test of the +;; return address against a specific GC hijack routine, and then fixup the stack pointer to what it would be +;; after a real return from the throwing method. Then, if we are not hijacked we can simply jump to the +;; handler in the caller. +;; +;; If we are hijacked, then we jump to a routine that will unhijack appropriatley and wait for the GC to +;; complete. There are also variants for GC stress. +;; +;; Note that at this point we are eiher hijacked or we are not, and this will not change until we return to +;; managed code. It is an invariant of the system that a thread will only attempt to hijack or unhijack +;; another thread while the target thread is suspended in managed code, and this is _not_ managed code. +;; +;; Register state on entry: +;; RAX: pointer to this function (i.e., trash) +;; RCX: reference to the exception object. +;; RDX: handler address we want to jump to. +;; RBX, RSI, RDI, RBP, and R12-R15 are all already correct for return to the caller. +;; The stack still contains the return address. +;; +;; Register state on exit: +;; RSP: what it would be after a complete return to the caler. +;; RDX: TRASHED +;; +RTU_EH_JUMP_HELPER macro funcName, hijackFuncName, isStress, stressFuncName +LEAF_ENTRY funcName, _TEXT + lea rax, [hijackFuncName] + cmp [rsp], rax + je RhpGCProbeForEHJump + +IF isStress EQ 1 + lea rax, [stressFuncName] + cmp [rsp], rax + je RhpGCStressProbeForEHJump +ENDIF + + ;; We are not hijacked, so we can return to the handler. + ;; We return to keep the call/return prediction balanced. + mov [rsp], rdx ; Update the return address + ret + +LEAF_END funcName, _TEXT +endm + +;; We need an instance of the helper for each possible hijack function. The binder has enough +;; information to determine which one we need to use for any function. +RTU_EH_JUMP_HELPER RhpEHJumpScalar, RhpGcProbeHijackScalar, 0, 0 +RTU_EH_JUMP_HELPER RhpEHJumpObject, RhpGcProbeHijackObject, 0, 0 +RTU_EH_JUMP_HELPER RhpEHJumpByref, RhpGcProbeHijackByref, 0, 0 +ifdef FEATURE_GC_STRESS +RTU_EH_JUMP_HELPER RhpEHJumpScalarGCStress, RhpGcProbeHijackScalar, 1, RhpGcStressHijackScalar +RTU_EH_JUMP_HELPER RhpEHJumpObjectGCStress, RhpGcProbeHijackObject, 1, RhpGcStressHijackObject +RTU_EH_JUMP_HELPER RhpEHJumpByrefGCStress, RhpGcProbeHijackByref, 1, RhpGcStressHijackByref +endif + +;; +;; Macro to setup our frame and adjust the location of the EH object reference for EH jump probe funcs. +;; +;; Register state on entry: +;; RAX: scratch +;; RCX: reference to the exception object. +;; RDX: handler address we want to jump to. +;; RBX, RSI, RDI, RBP, and R12-R15 are all already correct for return to the caller. +;; The stack is as if we are just about to returned from the call +;; +;; Register state on exit: +;; RAX: reference to the exception object +;; RCX: scratch +;; RDX: thread pointer +;; +EHJumpProbeProlog_extraStack = 1*8 +EHJumpProbeProlog macro + push_nonvol_reg rdx ; save the handler address so we can jump to it later + mov rax, rcx ; move the ex object reference into rax so we can report it + + ;; rdx <- GetThread(), TRASHES rcx + INLINE_GETTHREAD rdx, rcx + + ;; Fix the stack by patching the original return address + mov rcx, [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress] + mov [rsp + EHJumpProbeProlog_extraStack], rcx + + ClearHijackState + + ; TRASHES r10 + PUSH_PROBE_FRAME rdx, r10, EHJumpProbeProlog_extraStack, PROBE_SAVE_FLAGS_RAX_IS_GCREF + + END_PROLOGUE +endm + +;; +;; Macro to re-adjust the location of the EH object reference, cleanup the frame, and make the +;; final jump to the handler for EH jump probe funcs. +;; +;; Register state on entry: +;; RAX: reference to the exception object +;; RCX: scratch +;; RDX: scratch +;; +;; Register state on exit: +;; RSP: correct for return to the caller +;; RCX: reference to the exception object +;; RDX: trashed +;; +EHJumpProbeEpilog macro + POP_PROBE_FRAME EHJumpProbeProlog_extraStack + mov rcx, rax ; Put the EX obj ref back into rcx for the handler. + + pop rax ; Recover the handler address. + mov [rsp], rax ; Update the return address + ret +endm + +;; +;; We are hijacked for a normal GC (not GC stress), so we need to unhijcak and wait for the GC to complete. +;; +;; Register state on entry: +;; RAX: scratch +;; RCX: reference to the exception object. +;; RDX: handler address we want to jump to. +;; RBX, RSI, RDI, RBP, and R12-R15 are all already correct for return to the caller. +;; The stack is as if we have tail called to this function (rsp points to return address). +;; +;; Register state on exit: +;; RSP: correct for return to the caller +;; RBP: previous ebp frame +;; RCX: reference to the exception object +;; +NESTED_ENTRY RhpGCProbeForEHJump, _TEXT + EHJumpProbeProlog + +ifdef _DEBUG + ;; + ;; If we get here, then we have been hijacked for a real GC, and our SyncState must + ;; reflect that we've been requested to synchronize. + + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jnz @F + + call RhDebugBreak +@@: +endif ;; _DEBUG + + mov rbx, rdx + WaitForGCCompletion + + EHJumpProbeEpilog + +NESTED_END RhpGCProbeForEHJump, _TEXT + +ifdef FEATURE_GC_STRESS +;; +;; We are hijacked for GC Stress (not a normal GC) so we need to invoke the GC stress helper. +;; +;; Register state on entry: +;; RAX: scratch +;; RCX: reference to the exception object. +;; RDX: handler address we want to jump to. +;; RBX, RSI, RDI, RBP, and R12-R15 are all already correct for return to the caller. +;; The stack is as if we have tail called to this function (rsp points to return address). +;; +;; Register state on exit: +;; RSP: correct for return to the caller +;; RBP: previous ebp frame +;; RCX: reference to the exception object +;; +NESTED_ENTRY RhpGCStressProbeForEHJump, _TEXT + EHJumpProbeProlog + + call REDHAWKGCINTERFACE__STRESSGC + + EHJumpProbeEpilog + +NESTED_END RhpGCStressProbeForEHJump, _TEXT + +g_pTheRuntimeInstance equ ?g_pTheRuntimeInstance@@3PEAVRuntimeInstance@@EA +EXTERN g_pTheRuntimeInstance : QWORD +RuntimeInstance__ShouldHijackLoopForGcStress equ ?ShouldHijackLoopForGcStress@RuntimeInstance@@QEAA_N_K@Z +EXTERN RuntimeInstance__ShouldHijackLoopForGcStress : PROC + +endif ;; FEATURE_GC_STRESS + +EXTERN g_fGcStressStarted : DWORD +EXTERN g_fHasFastFxsave : BYTE + +FXSAVE_SIZE equ 512 + +;; Trap to GC. +;; Set up the P/Invoke transition frame with the return address as the safe point. +;; All registers, both volatile and non-volatile, are preserved. +;; The function should be called not jumped because it's expecting the return address +NESTED_ENTRY RhpTrapToGC, _TEXT + + sizeof_OutgoingScratchSpace equ 20h + sizeof_PInvokeFrame equ OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs + 15*8 + sizeof_XmmAlignPad equ 8 + sizeof_XmmSave equ FXSAVE_SIZE + sizeof_MachineFrame equ 6*8 + sizeof_InitialPushedArgs equ 2*8 ;; eflags, return value + sizeof_FixedFrame equ sizeof_OutgoingScratchSpace + sizeof_PInvokeFrame + sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + + ;; On the stack on entry: + ;; [rsp ] -> Return address + + ;; save eflags before we trash them + pushfq + + ;; What we want to get to: + ;; + ;; [rsp ] -> outgoing scratch area + ;; + ;; [rsp + 20] -> m_RIP -------| + ;; [rsp + 28] -> m_FramePointer | + ;; [rsp + 30] -> m_pThread | + ;; [rsp + 38] -> m_Flags / m_dwAlignPad2 | + ;; [rsp + 40] -> rbx save | + ;; [rsp + 48] -> rsi save | + ;; [rsp + 50] -> rdi save | + ;; [rsp + 58] -> r12 save | + ;; [rsp + 60] -> r13 save | + ;; [rsp + 68] -> r14 save | PInvokeTransitionFrame + ;; [rsp + 70] -> r15 save | + ;; [rsp + 78] -> rsp save | + ;; [rsp + 80] -> rax save | + ;; [rsp + 88] -> rcx save | + ;; [rsp + 90] -> rdx save | + ;; [rsp + 98] -> r8 save | + ;; [rsp + a0] -> r9 save | + ;; [rsp + a8] -> r10 save | + ;; [rsp + b0] -> r11 save -------| + ;; + ;; [rsp + b8] -> [XmmAlignPad] + ;; + ;; [rsp + c0] -> FXSAVE area + ;; + ;; [rsp +2c0] | RIP | + ;; [rsp +2c8] | CS | + ;; [rsp +2d0] | EFLAGS | <-- 'machine frame' + ;; [rsp +2d8] | RSP | + ;; [rsp +2e0] | SS | + ;; [rsp +2e8] | padding | + ;; + ;; [rsp +2f0] [PSP] + ;; [rsp +2f8] [optional stack alignment] + ;; + ;; [PSP - 10] -> eflags save + ;; [PSP - 8] -> Return address + ;; [PSP] -> caller's frame + + test rsp, 0Fh + jz AlreadyAligned + + sub rsp, sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + 8 ; +8 to save PSP, + push r11 ; save incoming R11 into save location + lea r11, [rsp + 8 + sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + 8 + sizeof_InitialPushedArgs] + jmp PspCalculated + + AlreadyAligned: + + sub rsp, sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + 16 ; +8 to save RSP, +8 to re-align PSP, + push r11 ; save incoming R11 into save location + lea r11, [rsp + 8 + sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + 16 + sizeof_InitialPushedArgs] + + PspCalculated: + push r10 ; save incoming R10 into save location + xor r10d, r10d + + ;; + ;; Populate the 'machine frame' in the diagram above. We have only pushed up to the 'r10 save', so we have not + ;; yet pushed 0xA8 bytes of that diagram. + ;; + ;; [rsp + {offset-in-target-frame-layout-diagram} - {as-yet-unpushed-stack-size}] + mov [rsp + 2c0h - 0a8h], r10 ; init RIP to zero + mov [rsp + 2c8h - 0a8h], r10 ; init CS to zero + mov [rsp + 2d0h - 0a8h], r10 ; init EFLAGS to zero + mov [rsp + 2d8h - 0a8h], r11 ; save PSP in the 'machine frame' + mov [rsp + 2e0h - 0a8h], r10 ; init SS to zero + mov [rsp + 2f0h - 0a8h], r11 ; save PSP + + .pushframe + .allocstack sizeof_XmmAlignPad + sizeof_XmmSave + 2*8 ;; only 2 of the regs from the PInvokeTransitionFrame are on the stack + + push_vol_reg r9 + push_vol_reg r8 + push_vol_reg rdx + push_vol_reg rcx + push_vol_reg rax + push_vol_reg r11 ; PSP gets saved into the PInvokeTransitionFrame + push_nonvol_reg r15 + push_nonvol_reg r14 + push_nonvol_reg r13 + push_nonvol_reg r12 + push_nonvol_reg rdi + push_nonvol_reg rsi + push_nonvol_reg rbx + push_vol_reg PROBE_SAVE_FLAGS_EVERYTHING ; m_Flags / m_dwAlignPad2 + + ;; rdx <- GetThread(), TRASHES rcx + INLINE_GETTHREAD rdx, rcx + + push_vol_reg rdx ; m_pThread + push_nonvol_reg rbp ; m_FramePointer + push_vol_reg r10 ; m_RIP + + alloc_stack sizeof_OutgoingScratchSpace + END_PROLOGUE + + mov rbx, r11 ; put PSP into RBX + mov rsi, rdx ; put Thread* into RSI + + ; RBX is PSP + ; RSI is Thread* + + fxsave [rsp + 0c0h] + + cmp [g_fHasFastFxsave], 0 ; fast fxsave won't save the xmm registers, so we must do it + jz DontSaveXmmAgain + + ;; 0C0h -> offset of FXSAVE area + ;; 0A0h -> offset of xmm0 save area within the FXSAVE area + movdqa [rsp + 0c0h + 0a0h + 0*10h], xmm0 + movdqa [rsp + 0c0h + 0a0h + 1*10h], xmm1 + movdqa [rsp + 0c0h + 0a0h + 2*10h], xmm2 + movdqa [rsp + 0c0h + 0a0h + 3*10h], xmm3 + movdqa [rsp + 0c0h + 0a0h + 4*10h], xmm4 + movdqa [rsp + 0c0h + 0a0h + 5*10h], xmm5 + movdqa [rsp + 0c0h + 0a0h + 6*10h], xmm6 + movdqa [rsp + 0c0h + 0a0h + 7*10h], xmm7 + movdqa [rsp + 0c0h + 0a0h + 8*10h], xmm8 + movdqa [rsp + 0c0h + 0a0h + 9*10h], xmm9 + movdqa [rsp + 0c0h + 0a0h + 10*10h], xmm10 + movdqa [rsp + 0c0h + 0a0h + 11*10h], xmm11 + movdqa [rsp + 0c0h + 0a0h + 12*10h], xmm12 + movdqa [rsp + 0c0h + 0a0h + 13*10h], xmm13 + movdqa [rsp + 0c0h + 0a0h + 14*10h], xmm14 + movdqa [rsp + 0c0h + 0a0h + 15*10h], xmm15 + +DontSaveXmmAgain: + mov rax, [rbx - 8] + mov [rsp + 2c0h], rax ; save return address into 'machine frame' + mov [rsp + 20h], rax ; save return address into PInvokeTransitionFrame + + ; Early out if GC stress is currently suppressed. Do this after we have computed the real address to + ; return to but before we link the transition frame onto m_pHackPInvokeTunnel (because hitting this + ; condition implies we're running restricted callouts during a GC itself and we could end up + ; overwriting a co-op frame set by the code that caused the GC in the first place, e.g. a GC.Collect + ; call). + test dword ptr [rsi + OFFSETOF__Thread__m_ThreadStateFlags], TSF_SuppressGcStress + TSF_DoNotTriggerGc + jnz DoneWaitingForGc + + ; link the frame into the Thread + lea rcx, [rsp + sizeof_OutgoingScratchSpace] ; rcx <- PInvokeTransitionFrame* + mov [rsi + OFFSETOF__Thread__m_pHackPInvokeTunnel], rcx + + ;; + ;; Unhijack this thread, if necessary. + ;; + INLINE_THREAD_UNHIJACK rsi, rax, rcx ;; trashes RAX, RCX + +ifdef FEATURE_GC_STRESS + xor eax, eax + cmp [g_fGcStressStarted], eax + jz @F + + mov rdx, [rsp + 2c0h] + mov rcx, [g_pTheRuntimeInstance] + call RuntimeInstance__ShouldHijackLoopForGcStress + cmp al, 0 + je @F + + call REDHAWKGCINTERFACE__STRESSGC +@@: +endif ;; FEATURE_GC_STRESS + + lea rcx, [rsp + sizeof_OutgoingScratchSpace] ; calculate PInvokeTransitionFrame pointer + call RhpWaitForGCNoAbort + + DoneWaitingForGc: + + fxrstor [rsp + 0c0h] + + cmp [g_fHasFastFxsave], 0 + jz DontRestoreXmmAgain + + movdqa xmm0 , [rsp + 0c0h + 0a0h + 0*10h] + movdqa xmm1 , [rsp + 0c0h + 0a0h + 1*10h] + movdqa xmm2 , [rsp + 0c0h + 0a0h + 2*10h] + movdqa xmm3 , [rsp + 0c0h + 0a0h + 3*10h] + movdqa xmm4 , [rsp + 0c0h + 0a0h + 4*10h] + movdqa xmm5 , [rsp + 0c0h + 0a0h + 5*10h] + movdqa xmm6 , [rsp + 0c0h + 0a0h + 6*10h] + movdqa xmm7 , [rsp + 0c0h + 0a0h + 7*10h] + movdqa xmm8 , [rsp + 0c0h + 0a0h + 8*10h] + movdqa xmm9 , [rsp + 0c0h + 0a0h + 9*10h] + movdqa xmm10, [rsp + 0c0h + 0a0h + 10*10h] + movdqa xmm11, [rsp + 0c0h + 0a0h + 11*10h] + movdqa xmm12, [rsp + 0c0h + 0a0h + 12*10h] + movdqa xmm13, [rsp + 0c0h + 0a0h + 13*10h] + movdqa xmm14, [rsp + 0c0h + 0a0h + 14*10h] + movdqa xmm15, [rsp + 0c0h + 0a0h + 15*10h] + +DontRestoreXmmAgain: + add rsp, sizeof_OutgoingScratchSpace + mov eax, [rsp + OFFSETOF__PInvokeTransitionFrame__m_Flags] + test eax, PTFF_THREAD_ABORT + pop rax ; m_RIP + pop rbp ; m_FramePointer + pop rax ; m_pThread + pop rax ; m_Flags / m_dwAlign2 + pop rbx + pop rsi + pop rdi + pop r12 + pop r13 + pop r14 + pop r15 + pop rax ; RSP + pop rax ; RAX save + pop rcx + pop rdx + pop r8 + pop r9 + pop r10 + pop r11 + + ;; restore PSP + ;; 2F0h -> offset of the PSP area + ;; 0B8h -> offset of the end of the integer register area which is already popped + mov rsp, [rsp + 2f0h - 0b8h] + + ;; RSP is PSP at this point and the stack looks like this: + ;; [PSP - 10] -> eflags save + ;; [PSP - 8] -> return address + ;; [PSP] -> caller's frame + ;; + ;; The final step is to restore eflags and return + + lea rsp, [rsp - 10h] + jz @f ;; result of the test instruction before the pops above + popfq ;; restore flags + mov rcx, STATUS_REDHAWK_THREAD_ABORT + pop rdx ;; return address as exception RIP + jmp RhpThrowHwEx ;; Throw the ThreadAbortException as a special kind of hardware exception + +@@: + popfq ;; restore flags + ret + +NESTED_END RhpTrapToGC, _TEXT + +ifdef FEATURE_GC_STRESS +;; +;; INVARIANT: Don't trash the argument registers, the binder codegen depends on this. +;; +LEAF_ENTRY RhpSuppressGcStress, _TEXT + + INLINE_GETTHREAD rax, r10 + lock or dword ptr [rax + OFFSETOF__Thread__m_ThreadStateFlags], TSF_SuppressGcStress + ret + +LEAF_END RhpSuppressGcStress, _TEXT +endif ;; FEATURE_GC_STRESS + +LEAF_ENTRY RhpGcPoll, _TEXT + + cmp [RhpTrapThreads], TrapThreadsFlags_None + jne @F ; forward branch - predicted not taken + ret +@@: + jmp RhpGcPollRare + +LEAF_END RhpGcPoll, _TEXT + +NESTED_ENTRY RhpGcPollRare, _TEXT + + PUSH_COOP_PINVOKE_FRAME rcx + END_PROLOGUE + + call RhpGcPoll2 + + POP_COOP_PINVOKE_FRAME + + ret + +NESTED_END RhpGcPollRare, _TEXT + + end diff --git a/src/coreclr/nativeaot/Runtime/amd64/GetThread.asm b/src/coreclr/nativeaot/Runtime/amd64/GetThread.asm new file mode 100644 index 00000000000000..806f8db2c72298 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/GetThread.asm @@ -0,0 +1,27 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include asmmacros.inc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpGetThread +;; +;; +;; INPUT: +;; +;; OUTPUT: RAX: Thread pointer +;; +;; TRASHES: R10 +;; +;; MUST PRESERVE ARGUMENT REGISTERS +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +LEAF_ENTRY RhpGetThread, _TEXT + ;; rax = GetThread(), TRASHES r10 + INLINE_GETTHREAD rax, r10 + ret +LEAF_END RhpGetThread, _TEXT + + + end diff --git a/src/coreclr/nativeaot/Runtime/amd64/Interlocked.S b/src/coreclr/nativeaot/Runtime/amd64/Interlocked.S new file mode 100644 index 00000000000000..9fb387054ede4c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/Interlocked.S @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include + +// WARNING: Code in EHHelpers.cpp makes assumptions about this helper, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpLockCmpXchg32AVLocation +// - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpLockCmpXchg32, _TEXT + mov rax, rdx +ALTERNATE_ENTRY RhpLockCmpXchg32AVLocation + lock cmpxchg [rdi], esi + ret +LEAF_END RhpLockCmpXchg32, _TEXT + +// WARNING: Code in EHHelpers.cpp makes assumptions about this helper, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpLockCmpXchg64AVLocation +// - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpLockCmpXchg64, _TEXT + mov rax, rdx +ALTERNATE_ENTRY RhpLockCmpXchg64AVLocation + lock cmpxchg [rdi], rsi + ret +LEAF_END RhpLockCmpXchg64, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/Interlocked.asm b/src/coreclr/nativeaot/Runtime/amd64/Interlocked.asm new file mode 100644 index 00000000000000..ce87417104ea72 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/Interlocked.asm @@ -0,0 +1,26 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include AsmMacros.inc + +;; WARNING: Code in EHHelpers.cpp makes assumptions about this helper, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpLockCmpXchg32AVLocation +;; - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpLockCmpXchg32, _TEXT + mov rax, r8 +ALTERNATE_ENTRY RhpLockCmpXchg32AVLocation + lock cmpxchg [rcx], edx + ret +LEAF_END RhpLockCmpXchg32, _TEXT + +;; WARNING: Code in EHHelpers.cpp makes assumptions about this helper, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpLockCmpXchg64AVLocation +;; - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpLockCmpXchg64, _TEXT + mov rax, r8 +ALTERNATE_ENTRY RhpLockCmpXchg64AVLocation + lock cmpxchg [rcx], rdx + ret +LEAF_END RhpLockCmpXchg64, _TEXT + + end diff --git a/src/coreclr/nativeaot/Runtime/amd64/InteropThunksHelpers.S b/src/coreclr/nativeaot/Runtime/amd64/InteropThunksHelpers.S new file mode 100644 index 00000000000000..5c0b6f631ef539 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/InteropThunksHelpers.S @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include // generated by the build from AsmOffsets.cpp +#include +#define POINTER_SIZE 8 + +LEAF_ENTRY RhCommonStub, _TEXT + + PUSH_ARGUMENT_REGISTERS + push_register r10 + + alloc_stack SIZEOF_FP_REGS + SAVE_FLOAT_ARGUMENT_REGISTERS 0 + + INLINE_GET_TLS_VAR tls_thunkData + + RESTORE_FLOAT_ARGUMENT_REGISTERS 0 + free_stack SIZEOF_FP_REGS + + pop_register r10 + POP_ARGUMENT_REGISTERS + + mov r11, [r10] + mov qword ptr [rax], r11 + + mov rax, [r10 + POINTER_SIZE] + jmp rax +LEAF_END RhCommonStub, _TEXT + + +LEAF_ENTRY RhGetCommonStubAddress, _TEXT + lea rax, [rip + C_FUNC(RhCommonStub)] + ret +LEAF_END RhGetCommonStubAddress, _TEXT + + +LEAF_ENTRY RhGetCurrentThunkContext, _TEXT + + INLINE_GET_TLS_VAR tls_thunkData + + mov rax, qword ptr [rax] + ret +LEAF_END RhGetCurrentThunkContext, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/InteropThunksHelpers.asm b/src/coreclr/nativeaot/Runtime/amd64/InteropThunksHelpers.asm new file mode 100644 index 00000000000000..b5dc7b26a70ee8 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/InteropThunksHelpers.asm @@ -0,0 +1,97 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + +;; ----------------------------------------------------------------------------------------------------------- +;;#include "asmmacros.inc" +;; ----------------------------------------------------------------------------------------------------------- + +LEAF_ENTRY macro Name, Section + Section segment para 'CODE' + align 16 + public Name + Name proc +endm + +LEAF_END macro Name, Section + Name endp + Section ends +endm + +; - TAILCALL_RAX: ("jmp rax") should be used for tailcalls, this emits an instruction +; sequence which is recognized by the unwinder as a valid epilogue terminator +TAILJMP_RAX TEXTEQU + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +_tls_array equ 58h ;; offsetof(TEB, ThreadLocalStoragePointer) + +POINTER_SIZE equ 08h + +;; TLS variables +_TLS SEGMENT ALIAS(".tls$") + ThunkParamSlot DQ 0000000000000000H +_TLS ENDS + +EXTRN _tls_index:DWORD + + +;;;;;;;;;;;;;;;;;;;;;;; Interop Thunks Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; +;; RhCommonStub +;; +LEAF_ENTRY RhCommonStub, _TEXT + ;; There are arbitrary callers passing arguments with arbitrary signatures. + ;; Custom calling convention: + ;; r10: pointer to the current thunk's data block (data contains 2 pointer values: context + target pointers) + + ;; Save context data into the ThunkParamSlot thread-local variable + ;; A pointer to the delegate and function pointer for open static delegate should have been saved in the thunk's context cell during thunk allocation + mov [rsp + 8], rcx ;; Save rcx in a home scratch location. Pushing the + ;; register on the stack will break callstack unwind + mov ecx, [_tls_index] + mov r11, gs:[_tls_array] + mov rax, [r11 + rcx * POINTER_SIZE] + + ;; rax = base address of TLS data + ;; r10 = address of context cell in thunk's data + ;; r11 = trashed + + ;; store thunk address in thread static + mov r11, [r10] + mov ecx, SECTIONREL ThunkParamSlot + mov [rax + rcx], r11 ;; ThunkParamSlot <- context slot data + + mov rcx, [rsp + 8] ;; Restore rcx + + ;; jump to the target + mov rax, [r10 + POINTER_SIZE] + TAILJMP_RAX +LEAF_END RhCommonStub, _TEXT + + +;; +;; IntPtr RhGetCommonStubAddress() +;; +LEAF_ENTRY RhGetCommonStubAddress, _TEXT + lea rax, [RhCommonStub] + ret +LEAF_END RhGetCommonStubAddress, _TEXT + + +;; +;; IntPtr RhGetCurrentThunkContext() +;; +LEAF_ENTRY RhGetCurrentThunkContext, _TEXT + mov r10d, [_tls_index] + mov r11, gs:[_tls_array] + mov r10, [r11 + r10 * POINTER_SIZE] + mov r8d, SECTIONREL ThunkParamSlot + mov rax, [r10 + r8] ;; rax <- ThunkParamSlot + ret +LEAF_END RhGetCurrentThunkContext, _TEXT + + +end diff --git a/src/coreclr/nativeaot/Runtime/amd64/MemClrForGC.asm b/src/coreclr/nativeaot/Runtime/amd64/MemClrForGC.asm new file mode 100644 index 00000000000000..de5476cad45550 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/MemClrForGC.asm @@ -0,0 +1,99 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include AsmMacros.inc + + +LEAF_ENTRY memclr_for_gc, _TEXT + +; x64 version + +; we get the following parameters +; rcx = destination address +; rdx = size to clear + + ; save rdi - this should be faster than a push + mov r11,rdi + + xor eax, eax + + ; check alignment of destination + test cl,7 + jnz alignDest +alignDone: + ; now destination is qword aligned + ; move it to rdi for rep stos + mov rdi,rcx + + ; compute number of bytes to clear non-temporally + ; we wish to clear the first 8k or so with rep stos, + ; anything above that non-temporally + + xor r8,r8 + cmp rdx,8*1024 + jbe noNonTempClear + + ; compute the number of bytes above 8k + ; and round down to a multiple of 64 + mov r8,rdx + sub r8,8*1024 + and r8,not 63 + + ; compute remaining size to clear temporally + sub rdx,r8 + +noNonTempClear: + + ; do the temporal clear + mov rcx,rdx + shr rcx,3 + rep stosq + + ; do the non-temporal clear + test r8,r8 + jne nonTempClearLoop + +nonTempClearDone: + + ; clear any remaining bytes + mov rcx,rdx + and rcx,7 + rep stosb + + ; restore rdi + mov rdi,r11 + + ret + + ; this is the infrequent case, hence out of line +nonTempClearLoop: + movnti [rdi+ 0],rax + movnti [rdi+ 8],rax + movnti [rdi+16],rax + movnti [rdi+24],rax + + movnti [rdi+32],rax + movnti [rdi+40],rax + movnti [rdi+48],rax + movnti [rdi+56],rax + + add rdi,64 + sub r8,64 + ja nonTempClearLoop + jmp nonTempClearDone + +alignDest: + test rdx,rdx + je alignDone +alignLoop: + mov [rcx],al + add rcx,1 + sub rdx,1 + jz alignDone + test cl,7 + jnz alignLoop + jmp alignDone + +LEAF_END memclr_for_gc, _TEXT + + end diff --git a/src/coreclr/nativeaot/Runtime/amd64/MiscStubs.S b/src/coreclr/nativeaot/Runtime/amd64/MiscStubs.S new file mode 100644 index 00000000000000..7d88ec5ac2b208 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/MiscStubs.S @@ -0,0 +1,95 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include // generated by the build from AsmOffsets.cpp +#include + +// The following helper will access ("probe") a word on each page of the stack +// starting with the page right beneath rsp down to the one pointed to by r11. +// The procedure is needed to make sure that the "guard" page is pushed down below the allocated stack frame. +// The call to the helper will be emitted by JIT in the function/funclet prolog when large (larger than 0x3000 bytes) stack frame is required. +// +// NOTE: On Linux we must advance the stack pointer as we probe - it is not allowed to access 65535 bytes below rsp. +// Since this helper will modify a value of rsp - it must establish the frame pointer. +// +// See also https://github.com/dotnet/runtime/issues/9899 for more information. + +#define PAGE_SIZE 0x1000 + +NESTED_ENTRY RhpStackProbe, _TEXT, NoHandler + // On entry: + // r11 - points to the lowest address on the stack frame being allocated (i.e. [InitialSp - FrameSize]) + // rsp - points to some byte on the last probed page + // On exit: + // r11 - is preserved + // + // NOTE: this helper will probe at least one page below the one pointed by rsp. + + push_nonvol_reg rbp + mov rbp, rsp + set_cfa_register rbp, 16 + + END_PROLOGUE + + and rsp, -PAGE_SIZE // rsp points to the **lowest address** on the last probed page + // This is done to make the following loop end condition simpler. + +LOCAL_LABEL(ProbeLoop): + sub rsp, PAGE_SIZE // rsp points to the lowest address of the **next page** to probe + test dword ptr [rsp], eax // rsp points to the lowest address on the **last probed** page + cmp rsp, r11 + jg LOCAL_LABEL(ProbeLoop) // if (rsp > r11), then we need to probe at least one more page. + + RESET_FRAME_WITH_RBP + ret +NESTED_END RhpStackProbe, _TEXT + +NESTED_ENTRY RhpGetThreadStaticBaseForType, _TEXT, NoHandler + // On entry: + // rdi - TypeManagerSlot* + // rsi - type index + // On exit: + // rax - the thread static base for the given type + + push_nonvol_reg rbx + push_nonvol_reg r12 + + mov rbx, rdi // Save TypeManagerSlot* + mov r12, rsi // Save type index + + // rax = GetThread() + INLINE_GETTHREAD + + mov r8d, [rbx + 8] // Get ModuleIndex out of the TypeManagerSlot + cmp r8d, [rax + OFFSETOF__Thread__m_numThreadLocalModuleStatics] + jae LOCAL_LABEL(RhpGetThreadStaticBaseForType_RarePath) + + mov r9, [rax + OFFSETOF__Thread__m_pThreadLocalModuleStatics] + mov rax, [r9 + r8 * 8] // Index into the array of modules + test rax, rax + jz LOCAL_LABEL(RhpGetThreadStaticBaseForType_RarePath) + + mov r8, [rax] // Get the managed array from the handle + cmp r12d, [r8 + OFFSETOF__Array__m_Length] + jae LOCAL_LABEL(RhpGetThreadStaticBaseForType_RarePath) + mov rax, [r8 + r12 * 8 + 0x10] + + test rax, rax + jz LOCAL_LABEL(RhpGetThreadStaticBaseForType_RarePath) + + .cfi_remember_state + pop_nonvol_reg r12 + pop_nonvol_reg rbx + ret + + .cfi_restore_state + .cfi_def_cfa_offset 24 // workaround cfi_restore_state bug +LOCAL_LABEL(RhpGetThreadStaticBaseForType_RarePath): + mov rdi, rbx // restore TypeManagerSlot* + mov rsi, r12 // restore type index + + pop_nonvol_reg r12 + pop_nonvol_reg rbx + jmp C_FUNC(RhpGetThreadStaticBaseForTypeSlow) +NESTED_END RhpGetThreadStaticBaseForType, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/MiscStubs.asm b/src/coreclr/nativeaot/Runtime/amd64/MiscStubs.asm new file mode 100644 index 00000000000000..7331a258b94e9b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/MiscStubs.asm @@ -0,0 +1,220 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include AsmMacros.inc + +EXTERN memcpy : PROC +EXTERN memcpyGCRefs : PROC +EXTERN memcpyGCRefsWithWriteBarrier : PROC +EXTERN memcpyAnyWithWriteBarrier : PROC +EXTERN RhpGetThreadStaticBaseForTypeSlow : PROC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyMultibyteNoGCRefs(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; +LEAF_ENTRY RhpCopyMultibyteNoGCRefs, _TEXT + + ; rcx dest + ; rdx src + ; r8 count + + test r8, r8 ; check for a zero-length copy + jz NothingToCopy + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. +ALTERNATE_ENTRY RhpCopyMultibyteNoGCRefsDestAVLocation + cmp byte ptr [rcx], 0 +ALTERNATE_ENTRY RhpCopyMultibyteNoGCRefsSrcAVLocation + cmp byte ptr [rdx], 0 + + ; tail-call to plain-old-memcpy + jmp memcpy + +NothingToCopy: + mov rax, rcx ; return dest + ret + +LEAF_END RhpCopyMultibyteNoGCRefs, _TEXT + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyMultibyte(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; +LEAF_ENTRY RhpCopyMultibyte, _TEXT + + ; rcx dest + ; rdx src + ; r8 count + + test r8, r8 ; check for a zero-length copy + jz NothingToCopy + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. +ALTERNATE_ENTRY RhpCopyMultibyteDestAVLocation + cmp byte ptr [rcx], 0 +ALTERNATE_ENTRY RhpCopyMultibyteSrcAVLocation + cmp byte ptr [rdx], 0 + + ; tail-call to the GC-safe memcpy implementation + jmp memcpyGCRefs + +NothingToCopy: + mov rax, rcx ; return dest + ret + +LEAF_END RhpCopyMultibyte, _TEXT + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyMultibyteWithWriteBarrier(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; Runs a card table update via RhpBulkWriteBarrier after the copy +;; +LEAF_ENTRY RhpCopyMultibyteWithWriteBarrier, _TEXT + + ; rcx dest + ; rdx src + ; r8 count + + test r8, r8 ; check for a zero-length copy + jz NothingToCopy + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. +ALTERNATE_ENTRY RhpCopyMultibyteWithWriteBarrierDestAVLocation + cmp byte ptr [rcx], 0 +ALTERNATE_ENTRY RhpCopyMultibyteWithWriteBarrierSrcAVLocation + cmp byte ptr [rdx], 0 + + ; tail-call to the GC-safe memcpy implementation + jmp memcpyGCRefsWithWriteBarrier + +NothingToCopy: + mov rax, rcx ; return dest + ret + +LEAF_END RhpCopyMultibyteWithWriteBarrier, _TEXT + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyAnyWithWriteBarrier(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; Runs a card table update via RhpBulkWriteBarrier after the copy if the copy may contain GC pointers +;; +LEAF_ENTRY RhpCopyAnyWithWriteBarrier, _TEXT + + ; rcx dest + ; rdx src + ; r8 count + + test r8, r8 ; check for a zero-length copy + jz NothingToCopy + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. +ALTERNATE_ENTRY RhpCopyAnyWithWriteBarrierDestAVLocation + cmp byte ptr [rcx], 0 +ALTERNATE_ENTRY RhpCopyAnyWithWriteBarrierSrcAVLocation + cmp byte ptr [rdx], 0 + + ; tail-call to the GC-safe memcpy implementation + jmp memcpyAnyWithWriteBarrier + +NothingToCopy: + mov rax, rcx ; return dest + ret + +LEAF_END RhpCopyAnyWithWriteBarrier, _TEXT + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; The following helper will access ("probe") a word on each page of the stack +; starting with the page right beneath rsp down to the one pointed to by r11. +; The procedure is needed to make sure that the "guard" page is pushed down below the allocated stack frame. +; The call to the helper will be emitted by JIT in the function/funclet prolog when large (larger than 0x3000 bytes) stack frame is required. +; +; NOTE: this helper will NOT modify a value of rsp and can be defined as a leaf function. + +PAGE_SIZE equ 1000h + +LEAF_ENTRY RhpStackProbe, _TEXT + ; On entry: + ; r11 - points to the lowest address on the stack frame being allocated (i.e. [InitialSp - FrameSize]) + ; rsp - points to some byte on the last probed page + ; On exit: + ; rax - is not preserved + ; r11 - is preserved + ; + ; NOTE: this helper will probe at least one page below the one pointed by rsp. + + mov rax, rsp ; rax points to some byte on the last probed page + and rax, -PAGE_SIZE ; rax points to the **lowest address** on the last probed page + ; This is done to make the following loop end condition simpler. + +ProbeLoop: + sub rax, PAGE_SIZE ; rax points to the lowest address of the **next page** to probe + test dword ptr [rax], eax ; rax points to the lowest address on the **last probed** page + cmp rax, r11 + jg ProbeLoop ; If (rax > r11), then we need to probe at least one more page. + + ret + +LEAF_END RhpStackProbe, _TEXT + +LEAF_ENTRY RhpGetThreadStaticBaseForType, _TEXT + ; On entry and thorough the procedure: + ; rcx - TypeManagerSlot* + ; rdx - type index + ; On exit: + ; rax - the thread static base for the given type + + ;; rax = GetThread(), TRASHES r8 + INLINE_GETTHREAD rax, r8 + + mov r8d, [rcx + 8] ; Get ModuleIndex out of the TypeManagerSlot + cmp r8d, [rax + OFFSETOF__Thread__m_numThreadLocalModuleStatics] + jae RhpGetThreadStaticBaseForType_RarePath + + mov r9, [rax + OFFSETOF__Thread__m_pThreadLocalModuleStatics] + mov rax, [r9 + r8 * 8] ; Index into the array of modules + test rax, rax + jz RhpGetThreadStaticBaseForType_RarePath + + mov r8, [rax] ; Get the managed array from the handle + cmp edx, [r8 + OFFSETOF__Array__m_Length] + jae RhpGetThreadStaticBaseForType_RarePath + mov rax, [r8 + rdx * 8 + 10h] + + test rax, rax + jz RhpGetThreadStaticBaseForType_RarePath + + ret + +RhpGetThreadStaticBaseForType_RarePath: + ;; We kept the arguments in their appropriate registers + ;; and we can tailcall right away. + jmp RhpGetThreadStaticBaseForTypeSlow + +LEAF_END RhpGetThreadStaticBaseForType, _TEXT + +end diff --git a/src/coreclr/nativeaot/Runtime/amd64/PInvoke.S b/src/coreclr/nativeaot/Runtime/amd64/PInvoke.S new file mode 100644 index 00000000000000..00481192145082 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/PInvoke.S @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include // generated by the build from AsmOffsets.cpp +#include + +// +// RhpPInvoke +// +// IN: RDI: address of pinvoke frame +// +// This helper assumes that its callsite is as good to start the stackwalk as the actual PInvoke callsite. +// The codegenerator must treat the callsite of this helper as GC triggering and generate the GC info for it. +// Also, the codegenerator must ensure that there are no live GC references in callee saved registers. +// +NESTED_ENTRY RhpPInvoke, _TEXT, NoHandler + push_nonvol_reg rbx + mov rbx, rdi + + // RAX = GetThread() + INLINE_GETTHREAD + + mov r11, [rsp + 0x8] // r11 <- return address + mov qword ptr [rbx + OFFSETOF__PInvokeTransitionFrame__m_pThread], rax + mov qword ptr [rbx + OFFSETOF__PInvokeTransitionFrame__m_FramePointer], rbp + mov qword ptr [rbx + OFFSETOF__PInvokeTransitionFrame__m_RIP], r11 + + lea r11, [rsp + 0x10] // r11 <- caller SP + mov dword ptr [rbx + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_SAVE_RSP + mov qword ptr [rbx + OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs], r11 + + mov qword ptr [rax + OFFSETOF__Thread__m_pTransitionFrame], rbx + + test dword ptr [C_VAR(RhpTrapThreads)], TrapThreadsFlags_TrapThreads + pop_nonvol_reg rbx + jnz 0f // forward branch - predicted not taken + ret +0: + jmp C_FUNC(RhpWaitForSuspend2) +NESTED_END RhpPInvoke, _TEXT + + +// +// RhpPInvokeReturn +// +// IN: RDI: address of pinvoke frame +// +LEAF_ENTRY RhpPInvokeReturn, _TEXT + mov rsi, [rdi + OFFSETOF__PInvokeTransitionFrame__m_pThread] + mov qword ptr [rsi + OFFSETOF__Thread__m_pTransitionFrame], 0 + cmp dword ptr [C_VAR(RhpTrapThreads)], TrapThreadsFlags_None + jne 0f // forward branch - predicted not taken + ret +0: + // passing transition frame pointer in rdi + jmp C_FUNC(RhpWaitForGC2) +LEAF_END RhpPInvokeReturn, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/PInvoke.asm b/src/coreclr/nativeaot/Runtime/amd64/PInvoke.asm new file mode 100644 index 00000000000000..973dc8da2f06cf --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/PInvoke.asm @@ -0,0 +1,236 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include asmmacros.inc + +extern RhpReversePInvokeBadTransition : proc + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForSuspend -- rare path for RhpPInvoke and RhpReversePInvokeReturn +;; +;; +;; INPUT: none +;; +;; TRASHES: none +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NESTED_ENTRY RhpWaitForSuspend, _TEXT + push_vol_reg rax + alloc_stack 60h + + ; save the arg regs in the caller's scratch space + save_reg_postrsp rcx, 70h + save_reg_postrsp rdx, 78h + save_reg_postrsp r8, 80h + save_reg_postrsp r9, 88h + + ; save the FP arg regs in our stack frame + save_xmm128_postrsp xmm0, (20h + 0*10h) + save_xmm128_postrsp xmm1, (20h + 1*10h) + save_xmm128_postrsp xmm2, (20h + 2*10h) + save_xmm128_postrsp xmm3, (20h + 3*10h) + + END_PROLOGUE + + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jz NoWait + + call RhpWaitForSuspend2 + +NoWait: + movdqa xmm0, [rsp + 20h + 0*10h] + movdqa xmm1, [rsp + 20h + 1*10h] + movdqa xmm2, [rsp + 20h + 2*10h] + movdqa xmm3, [rsp + 20h + 3*10h] + + mov rcx, [rsp + 70h] + mov rdx, [rsp + 78h] + mov r8, [rsp + 80h] + mov r9, [rsp + 88h] + + add rsp, 60h + pop rax + ret + +NESTED_END RhpWaitForSuspend, _TEXT + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGCNoAbort -- rare path for RhpPInvokeReturn +;; +;; +;; INPUT: RCX: transition frame +;; +;; TRASHES: RCX, RDX, R8, R9, R10, R11 +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NESTED_ENTRY RhpWaitForGCNoAbort, _TEXT + push_vol_reg rax ; don't trash the integer return value + alloc_stack 30h + movdqa [rsp + 20h], xmm0 ; don't trash the FP return value + END_PROLOGUE + + mov rdx, [rcx + OFFSETOF__PInvokeTransitionFrame__m_pThread] + + test dword ptr [rdx + OFFSETOF__Thread__m_ThreadStateFlags], TSF_DoNotTriggerGc + jnz Done + + ; passing transition frame pointer in rcx + call RhpWaitForGC2 + +Done: + movdqa xmm0, [rsp + 20h] + add rsp, 30h + pop rax + ret + +NESTED_END RhpWaitForGCNoAbort, _TEXT + +EXTERN RhpThrowHwEx : PROC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGC -- rare path for RhpPInvokeReturn +;; +;; +;; INPUT: RCX: transition frame +;; +;; TRASHES: RCX, RDX, R8, R9, R10, R11 +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NESTED_ENTRY RhpWaitForGC, _TEXT + push_nonvol_reg rbx + END_PROLOGUE + + mov rbx, rcx + + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jz NoWait + + call RhpWaitForGCNoAbort +NoWait: + test [RhpTrapThreads], TrapThreadsFlags_AbortInProgress + jz Done + test dword ptr [rbx + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_THREAD_ABORT + jz Done + + mov rcx, STATUS_REDHAWK_THREAD_ABORT + pop rbx + pop rdx ; return address as exception RIP + jmp RhpThrowHwEx ; Throw the ThreadAbortException as a special kind of hardware exception + +Done: + pop rbx + ret + +NESTED_END RhpWaitForGC, _TEXT + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpReversePInvokeAttachOrTrapThread +;; +;; +;; INCOMING: RAX -- address of reverse pinvoke frame +;; +;; PRESERVES: RCX, RDX, R8, R9 -- need to preserve these because the caller assumes they aren't trashed +;; +;; TRASHES: RAX, R10, R11 +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NESTED_ENTRY RhpReversePInvokeAttachOrTrapThread, _TEXT + alloc_stack 88h ; alloc scratch area and frame + + ; save the integer arg regs + save_reg_postrsp rcx, (20h + 0*8) + save_reg_postrsp rdx, (20h + 1*8) + save_reg_postrsp r8, (20h + 2*8) + save_reg_postrsp r9, (20h + 3*8) + + ; save the FP arg regs + save_xmm128_postrsp xmm0, (20h + 4*8 + 0*10h) + save_xmm128_postrsp xmm1, (20h + 4*8 + 1*10h) + save_xmm128_postrsp xmm2, (20h + 4*8 + 2*10h) + save_xmm128_postrsp xmm3, (20h + 4*8 + 3*10h) + + END_PROLOGUE + + mov rcx, rax ; rcx <- reverse pinvoke frame + call RhpReversePInvokeAttachOrTrapThread2 + + movdqa xmm0, [rsp + (20h + 4*8 + 0*10h)] + movdqa xmm1, [rsp + (20h + 4*8 + 1*10h)] + movdqa xmm2, [rsp + (20h + 4*8 + 2*10h)] + movdqa xmm3, [rsp + (20h + 4*8 + 3*10h)] + + mov rcx, [rsp + (20h + 0*8)] + mov rdx, [rsp + (20h + 1*8)] + mov r8, [rsp + (20h + 2*8)] + mov r9, [rsp + (20h + 3*8)] + + ;; epilog + add rsp, 88h + ret + +NESTED_END RhpReversePInvokeAttachOrTrapThread, _TEXT + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpPInvoke +;; +;; IN: RCX: address of pinvoke frame +;; +;; TRASHES: R10, R11 +;; +;; This helper assumes that its callsite is as good to start the stackwalk as the actual PInvoke callsite. +;; The codegenerator must treat the callsite of this helper as GC triggering and generate the GC info for it. +;; Also, the codegenerator must ensure that there are no live GC references in callee saved registers. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +LEAF_ENTRY RhpPInvoke, _TEXT + ;; R10 = GetThread(), TRASHES R11 + INLINE_GETTHREAD r10, r11 + + mov r11, [rsp] ; r11 <- return address + mov qword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_pThread], r10 + mov qword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_FramePointer], rbp + mov qword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_RIP], r11 + + lea r11, [rsp + 8] ; r11 <- caller SP + mov dword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_SAVE_RSP + mov qword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs], r11 + + mov qword ptr [r10 + OFFSETOF__Thread__m_pTransitionFrame], rcx + + cmp [RhpTrapThreads], TrapThreadsFlags_None + jne @F ; forward branch - predicted not taken + ret +@@: + jmp RhpWaitForSuspend +LEAF_END RhpPInvoke, _TEXT + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpPInvokeReturn +;; +;; IN: RCX: address of pinvoke frame +;; +;; TRASHES: RCX, RDX, R8, R9, R10, R11 +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +LEAF_ENTRY RhpPInvokeReturn, _TEXT + mov rdx, [rcx + OFFSETOF__PInvokeTransitionFrame__m_pThread] + mov qword ptr [rdx + OFFSETOF__Thread__m_pTransitionFrame], 0 + cmp [RhpTrapThreads], TrapThreadsFlags_None + jne @F ; forward branch - predicted not taken + ret +@@: + ; passing transition frame pointer in rcx + jmp RhpWaitForGC +LEAF_END RhpPInvokeReturn, _TEXT + + +END diff --git a/src/coreclr/nativeaot/Runtime/amd64/StubDispatch.S b/src/coreclr/nativeaot/Runtime/amd64/StubDispatch.S new file mode 100644 index 00000000000000..2f400c76e4e71d --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/StubDispatch.S @@ -0,0 +1,92 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include // generated by the build from AsmOffsets.cpp +#include + +// trick to avoid PLT relocation at runtime which corrupts registers +#define REL_C_FUNC(name) C_FUNC(name)@gotpcrel + + +// Macro that generates a stub consuming a cache with the given number of entries. +.macro DEFINE_INTERFACE_DISPATCH_STUB entries + +LEAF_ENTRY RhpInterfaceDispatch\entries, _TEXT + + // r10 currently contains the indirection cell address. + // load r11 to point to the cache block. + mov r11, [r10 + OFFSETOF__InterfaceDispatchCell__m_pCache] + + // Load the MethodTable from the object instance in rdi. + ALTERNATE_ENTRY RhpInterfaceDispatchAVLocation\entries + mov rax, [rdi] + + CurrentOffset = OFFSETOF__InterfaceDispatchCache__m_rgEntries + + // For each entry in the cache, see if its MethodTable type matches the MethodTable in rax. + // If so, call the second cache entry. If not, skip the InterfaceDispatchCacheEntry. + .rept \entries + cmp rax, [r11 + CurrentOffset] + jne 0f + jmp [r11 + CurrentOffset + 8] + 0: + CurrentOffset = CurrentOffset + 16 + .endr + + // r10 still contains the the indirection cell address. + + jmp C_FUNC(RhpInterfaceDispatchSlow) +LEAF_END RhpInterfaceDispatch\entries, _TEXT + +.endm // DEFINE_INTERFACE_DISPATCH_STUB + + + +// Define all the stub routines we currently need. +// +// The mrt100dbi requires these be exported to identify mrt100 code that dispatches back into managed. +// If you change or add any new dispatch stubs, please also change slr.def and dbi\process.cpp CordbProcess::GetExportStepInfo +// +// If you change or add any new dispatch stubs, exception handling might need to be aware because it refers to the +// *AVLocation symbols defined by the dispatch stubs to be able to unwind and blame user code if a NullRef happens +// during the interface dispatch. +// +DEFINE_INTERFACE_DISPATCH_STUB 1 +DEFINE_INTERFACE_DISPATCH_STUB 2 +DEFINE_INTERFACE_DISPATCH_STUB 4 +DEFINE_INTERFACE_DISPATCH_STUB 8 +DEFINE_INTERFACE_DISPATCH_STUB 16 +DEFINE_INTERFACE_DISPATCH_STUB 32 +DEFINE_INTERFACE_DISPATCH_STUB 64 + +// Stub dispatch routine for dispatch to a vtable slot +LEAF_ENTRY RhpVTableOffsetDispatch, _TEXT + // UNIXTODO: Implement this function + int 3 +LEAF_END RhpVTableOffsetDispatch, _TEXT + +// Initial dispatch on an interface when we don't have a cache yet. +LEAF_ENTRY RhpInitialInterfaceDispatch, _TEXT +ALTERNATE_ENTRY RhpInitialDynamicInterfaceDispatch + // Trigger an AV if we're dispatching on a null this. + // The exception handling infrastructure is aware of the fact that this is the first + // instruction of RhpInitialInterfaceDispatch and uses it to translate an AV here + // to a NullReferenceException at the callsite. + cmp byte ptr [rdi], 0 + + // Just tail call to the cache miss helper. + jmp C_FUNC(RhpInterfaceDispatchSlow) + +LEAF_END RhpInitialInterfaceDispatch, _TEXT + +// Cache miss case, call the runtime to resolve the target and update the cache. +// Use universal transition helper to allow an exception to flow out of resolution +LEAF_ENTRY RhpInterfaceDispatchSlow, _TEXT + // r10 contains indirection cell address, move to r11 where it will be passed by + // the universal transition thunk as an argument to RhpCidResolve + mov r11, r10 + mov r10, [rip + REL_C_FUNC(RhpCidResolve)] + jmp qword ptr [rip + REL_C_FUNC(RhpUniversalTransition_DebugStepTailCall)] + +LEAF_END RhpInterfaceDispatchSlow, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/StubDispatch.asm b/src/coreclr/nativeaot/Runtime/amd64/StubDispatch.asm new file mode 100644 index 00000000000000..7a30b63a95a4ff --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/StubDispatch.asm @@ -0,0 +1,119 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include AsmMacros.inc + + +ifdef FEATURE_CACHED_INTERFACE_DISPATCH + + +EXTERN RhpCidResolve : PROC +EXTERN RhpUniversalTransition_DebugStepTailCall : PROC + +;; Macro that generates code to check a single cache entry. +CHECK_CACHE_ENTRY macro entry +NextLabel textequ @CatStr( Attempt, %entry+1 ) + cmp rax, [r11 + OFFSETOF__InterfaceDispatchCache__m_rgEntries + (entry * 16)] + jne NextLabel + jmp qword ptr [r11 + OFFSETOF__InterfaceDispatchCache__m_rgEntries + (entry * 16) + 8] +NextLabel: +endm + + +;; Macro that generates a stub consuming a cache with the given number of entries. +DEFINE_INTERFACE_DISPATCH_STUB macro entries + +StubName textequ @CatStr( RhpInterfaceDispatch, entries ) +StubAVLocation textequ @CatStr( RhpInterfaceDispatchAVLocation, entries ) + +LEAF_ENTRY StubName, _TEXT + +;EXTERN CID_g_cInterfaceDispatches : DWORD + ;inc [CID_g_cInterfaceDispatches] + + ;; r10 currently contains the indirection cell address. + ;; load r11 to point to the cache block. + mov r11, [r10 + OFFSETOF__InterfaceDispatchCell__m_pCache] + + ;; Load the MethodTable from the object instance in rcx. + ALTERNATE_ENTRY StubAVLocation + mov rax, [rcx] + +CurrentEntry = 0 + while CurrentEntry lt entries + CHECK_CACHE_ENTRY %CurrentEntry +CurrentEntry = CurrentEntry + 1 + endm + + ;; r10 still contains the the indirection cell address. + + jmp RhpInterfaceDispatchSlow + +LEAF_END StubName, _TEXT + + endm ;; DEFINE_INTERFACE_DISPATCH_STUB + + +;; Define all the stub routines we currently need. +;; +;; The mrt100dbi requires these be exported to identify mrt100 code that dispatches back into managed. +;; If you change or add any new dispatch stubs, please also change slr.def and dbi\process.cpp CordbProcess::GetExportStepInfo +;; +;; If you change or add any new dispatch stubs, exception handling might need to be aware because it refers to the +;; *AVLocation symbols defined by the dispatch stubs to be able to unwind and blame user code if a NullRef happens +;; during the interface dispatch. +;; +DEFINE_INTERFACE_DISPATCH_STUB 1 +DEFINE_INTERFACE_DISPATCH_STUB 2 +DEFINE_INTERFACE_DISPATCH_STUB 4 +DEFINE_INTERFACE_DISPATCH_STUB 8 +DEFINE_INTERFACE_DISPATCH_STUB 16 +DEFINE_INTERFACE_DISPATCH_STUB 32 +DEFINE_INTERFACE_DISPATCH_STUB 64 + +;; Stub dispatch routine for dispatch to a vtable slot +LEAF_ENTRY RhpVTableOffsetDispatch, _TEXT + ;; r10 currently contains the indirection cell address. + ;; load rax to point to the vtable offset (which is stored in the m_pCache field). + mov rax, [r10 + OFFSETOF__InterfaceDispatchCell__m_pCache] + + ;; Load the MethodTable from the object instance in rcx, and add it to the vtable offset + ;; to get the address in the vtable of what we want to dereference + add rax, [rcx] + + ;; Load the target address of the vtable into rax + mov rax, [rax] + + TAILJMP_RAX +LEAF_END RhpVTableOffsetDispatch, _TEXT + + +;; Initial dispatch on an interface when we don't have a cache yet. +LEAF_ENTRY RhpInitialInterfaceDispatch, _TEXT +ALTERNATE_ENTRY RhpInitialDynamicInterfaceDispatch + ;; Trigger an AV if we're dispatching on a null this. + ;; The exception handling infrastructure is aware of the fact that this is the first + ;; instruction of RhpInitialInterfaceDispatch and uses it to translate an AV here + ;; to a NullReferenceException at the callsite. + cmp byte ptr [rcx], 0 + + ;; Just tail call to the cache miss helper. + jmp RhpInterfaceDispatchSlow + +LEAF_END RhpInitialInterfaceDispatch, _TEXT + +;; Cache miss case, call the runtime to resolve the target and update the cache. +;; Use universal transition helper to allow an exception to flow out of resolution +LEAF_ENTRY RhpInterfaceDispatchSlow, _TEXT + ;; r10 contains indirection cell address, move to r11 where it will be passed by + ;; the universal transition thunk as an argument to RhpCidResolve + mov r11, r10 + lea r10, RhpCidResolve + jmp RhpUniversalTransition_DebugStepTailCall + +LEAF_END RhpInterfaceDispatchSlow, _TEXT + + +endif ;; FEATURE_CACHED_INTERFACE_DISPATCH + +end diff --git a/src/coreclr/nativeaot/Runtime/amd64/ThunkPoolThunks.asm b/src/coreclr/nativeaot/Runtime/amd64/ThunkPoolThunks.asm new file mode 100644 index 00000000000000..2169f6e220d981 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/ThunkPoolThunks.asm @@ -0,0 +1,291 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +;; ----------------------------------------------------------------------------------------------------------- +;;#include "asmmacros.inc" +;; ----------------------------------------------------------------------------------------------------------- + +LEAF_ENTRY macro Name, Section + Section segment para 'CODE' + align 16 + public Name + Name proc +endm + +NAMED_LEAF_ENTRY macro Name, Section, SectionAlias + Section segment para alias(SectionAlias) 'CODE' + align 16 + public Name + Name proc +endm + +LEAF_END macro Name, Section + Name endp + Section ends +endm + +NAMED_READONLY_DATA_SECTION macro Section, SectionAlias + Section segment alias(SectionAlias) read 'DATA' + align 16 + DQ 0 + Section ends +endm + +NAMED_READWRITE_DATA_SECTION macro Section, SectionAlias + Section segment alias(SectionAlias) read write 'DATA' + align 16 + DQ 0 + Section ends +endm + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; STUBS & DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +THUNK_CODESIZE equ 10h ;; 7-byte lea, 6-byte jmp, 3 bytes of nops +THUNK_DATASIZE equ 010h ;; 2 qwords + +THUNK_POOL_NUM_THUNKS_PER_PAGE equ 0FAh ;; 250 thunks per page + +PAGE_SIZE equ 01000h ;; 4K +POINTER_SIZE equ 08h + + +LOAD_DATA_ADDRESS macro groupIndex, index, thunkPool + ALIGN 10h ;; make sure we align to 16-byte boundary for CFG table + + ;; set r10 to begining of data page : r10 <- [thunkPool] + PAGE_SIZE + ;; fix offset of the data : r10 <- r10 + (THUNK_DATASIZE * current thunk's index) + lea r10, [thunkPool + PAGE_SIZE + (groupIndex * THUNK_DATASIZE * 10 + THUNK_DATASIZE * index)] +endm + +JUMP_TO_COMMON macro groupIndex, index, thunkPool + ;; jump to the location pointed at by the last qword in the data page + jmp qword ptr[thunkPool + PAGE_SIZE + PAGE_SIZE - POINTER_SIZE] +endm + +TenThunks macro groupIndex, thunkPool + ;; Each thunk will load the address of its corresponding data (from the page that immediately follows) + ;; and call a common stub. The address of the common stub is setup by the caller (first qword + ;; in the thunks data section, hence the +8's below) depending on the 'kind' of thunks needed (interop, + ;; fat function pointers, etc...) + + ;; Each data block used by a thunk consists of two qword values: + ;; - Context: some value given to the thunk as context (passed in r10). Example for fat-fptrs: context = generic dictionary + ;; - Target : target code that the thunk eventually jumps to. + + LOAD_DATA_ADDRESS groupIndex,0,thunkPool + JUMP_TO_COMMON groupIndex,0,thunkPool + + LOAD_DATA_ADDRESS groupIndex,1,thunkPool + JUMP_TO_COMMON groupIndex,1,thunkPool + + LOAD_DATA_ADDRESS groupIndex,2,thunkPool + JUMP_TO_COMMON groupIndex,2,thunkPool + + LOAD_DATA_ADDRESS groupIndex,3,thunkPool + JUMP_TO_COMMON groupIndex,3,thunkPool + + LOAD_DATA_ADDRESS groupIndex,4,thunkPool + JUMP_TO_COMMON groupIndex,4,thunkPool + + LOAD_DATA_ADDRESS groupIndex,5,thunkPool + JUMP_TO_COMMON groupIndex,5,thunkPool + + LOAD_DATA_ADDRESS groupIndex,6,thunkPool + JUMP_TO_COMMON groupIndex,6,thunkPool + + LOAD_DATA_ADDRESS groupIndex,7,thunkPool + JUMP_TO_COMMON groupIndex,7,thunkPool + + LOAD_DATA_ADDRESS groupIndex,8,thunkPool + JUMP_TO_COMMON groupIndex,8,thunkPool + + LOAD_DATA_ADDRESS groupIndex,9,thunkPool + JUMP_TO_COMMON groupIndex,9,thunkPool +endm + +THUNKS_PAGE_BLOCK macro thunkPool + TenThunks 0,thunkPool + TenThunks 1,thunkPool + TenThunks 2,thunkPool + TenThunks 3,thunkPool + TenThunks 4,thunkPool + TenThunks 5,thunkPool + TenThunks 6,thunkPool + TenThunks 7,thunkPool + TenThunks 8,thunkPool + TenThunks 9,thunkPool + TenThunks 10,thunkPool + TenThunks 11,thunkPool + TenThunks 12,thunkPool + TenThunks 13,thunkPool + TenThunks 14,thunkPool + TenThunks 15,thunkPool + TenThunks 16,thunkPool + TenThunks 17,thunkPool + TenThunks 18,thunkPool + TenThunks 19,thunkPool + TenThunks 20,thunkPool + TenThunks 21,thunkPool + TenThunks 22,thunkPool + TenThunks 23,thunkPool + TenThunks 24,thunkPool +endm + +;; +;; The first thunks section should be 64K aligned because it can get +;; mapped multiple times in memory, and mapping works on allocation +;; granularity boundaries (we don't want to map more than what we need) +;; +;; The easiest way to do so is by having the thunks section at the +;; first 64K aligned virtual address in the binary. We provide a section +;; layout file to the linker to tell it how to layout the thunks sections +;; that we care about. (ndp\rh\src\runtime\DLLs\app\mrt100_app_sectionlayout.txt) +;; +;; The PE spec says images cannot have gaps between sections (other +;; than what is required by the section alignment value in the header), +;; therefore we need a couple of padding data sections (otherwise the +;; OS will not load the image). +;; + +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment0, ".pad0" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment1, ".pad1" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment2, ".pad2" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment3, ".pad3" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment4, ".pad4" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment5, ".pad5" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment6, ".pad6" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment7, ".pad7" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment8, ".pad8" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment9, ".pad9" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment10, ".pad10" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment11, ".pad11" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment12, ".pad12" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment13, ".pad13" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment14, ".pad14" + +;; +;; Thunk Stubs +;; NOTE: Keep number of blocks in sync with macro/constant named 'NUM_THUNK_BLOCKS' in: +;; - ndp\FxCore\src\System.Private.CoreLib\System\Runtime\InteropServices\ThunkPool.cs +;; - ndp\rh\src\tools\rhbind\zapimage.h +;; +NAMED_LEAF_ENTRY ThunkPool, TKS0, ".tks0" + THUNKS_PAGE_BLOCK ThunkPool +LEAF_END ThunkPool, TKS0 + +NAMED_READWRITE_DATA_SECTION ThunkData0, ".tkd0" + +NAMED_LEAF_ENTRY ThunkPool1, TKS1, ".tks1" + THUNKS_PAGE_BLOCK ThunkPool1 +LEAF_END ThunkPool1, TKS1 + +NAMED_READWRITE_DATA_SECTION ThunkData1, ".tkd1" + +NAMED_LEAF_ENTRY ThunkPool2, TKS2, ".tks2" + THUNKS_PAGE_BLOCK ThunkPool2 +LEAF_END ThunkPool2, TKS2 + +NAMED_READWRITE_DATA_SECTION ThunkData2, ".tkd2" + +NAMED_LEAF_ENTRY ThunkPool3, TKS3, ".tks3" + THUNKS_PAGE_BLOCK ThunkPool3 +LEAF_END ThunkPool3, TKS3 + +NAMED_READWRITE_DATA_SECTION ThunkData3, ".tkd3" + +NAMED_LEAF_ENTRY ThunkPool4, TKS4, ".tks4" + THUNKS_PAGE_BLOCK ThunkPool4 +LEAF_END ThunkPool4, TKS4 + +NAMED_READWRITE_DATA_SECTION ThunkData4, ".tkd4" + +NAMED_LEAF_ENTRY ThunkPool5, TKS5, ".tks5" + THUNKS_PAGE_BLOCK ThunkPool5 +LEAF_END ThunkPool5, TKS5 + +NAMED_READWRITE_DATA_SECTION ThunkData5, ".tkd5" + +NAMED_LEAF_ENTRY ThunkPool6, TKS6, ".tks6" + THUNKS_PAGE_BLOCK ThunkPool6 +LEAF_END ThunkPool6, TKS6 + +NAMED_READWRITE_DATA_SECTION ThunkData6, ".tkd6" + +NAMED_LEAF_ENTRY ThunkPool7, TKS7, ".tks7" + THUNKS_PAGE_BLOCK ThunkPool7 +LEAF_END ThunkPool7, TKS7 + +NAMED_READWRITE_DATA_SECTION ThunkData7, ".tkd7" + +;; +;; IntPtr RhpGetThunksBase() +;; +LEAF_ENTRY RhpGetThunksBase, _TEXT + ;; Return the address of the first thunk pool to the caller (this is really the base address) + lea rax, [ThunkPool] + ret +LEAF_END RhpGetThunksBase, _TEXT + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; General Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; +;; int RhpGetNumThunksPerBlock() +;; +LEAF_ENTRY RhpGetNumThunksPerBlock, _TEXT + mov rax, THUNK_POOL_NUM_THUNKS_PER_PAGE + ret +LEAF_END RhpGetNumThunksPerBlock, _TEXT + +;; +;; int RhpGetThunkSize() +;; +LEAF_ENTRY RhpGetThunkSize, _TEXT + mov rax, THUNK_CODESIZE + ret +LEAF_END RhpGetThunkSize, _TEXT + +;; +;; int RhpGetNumThunkBlocksPerMapping() +;; +LEAF_ENTRY RhpGetNumThunkBlocksPerMapping, _TEXT + mov rax, 8 + ret +LEAF_END RhpGetNumThunkBlocksPerMapping, _TEXT + +;; +;; int RhpGetThunkBlockSize +;; +LEAF_ENTRY RhpGetThunkBlockSize, _TEXT + mov rax, PAGE_SIZE * 2 + ret +LEAF_END RhpGetThunkBlockSize, _TEXT + +;; +;; IntPtr RhpGetThunkDataBlockAddress(IntPtr thunkStubAddress) +;; +LEAF_ENTRY RhpGetThunkDataBlockAddress, _TEXT + mov rax, rcx + mov rcx, PAGE_SIZE - 1 + not rcx + and rax, rcx + add rax, PAGE_SIZE + ret +LEAF_END RhpGetThunkDataBlockAddress, _TEXT + +;; +;; IntPtr RhpGetThunkStubsBlockAddress(IntPtr thunkDataAddress) +;; +LEAF_ENTRY RhpGetThunkStubsBlockAddress, _TEXT + mov rax, rcx + mov rcx, PAGE_SIZE - 1 + not rcx + and rax, rcx + sub rax, PAGE_SIZE + ret +LEAF_END RhpGetThunkStubsBlockAddress, _TEXT + + +end diff --git a/src/coreclr/nativeaot/Runtime/amd64/UniversalTransition.S b/src/coreclr/nativeaot/Runtime/amd64/UniversalTransition.S new file mode 100644 index 00000000000000..d5a1cf34df31ca --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/UniversalTransition.S @@ -0,0 +1,162 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include + +#ifdef FEATURE_DYNAMIC_CODE + +#ifdef _DEBUG +#define TRASH_SAVED_ARGUMENT_REGISTERS 1 +#endif + +#define SIZEOF_RETADDR 8 + +#define SIZEOF_RETURN_BLOCK 0x10 // for 16 bytes of conservatively reported space that the callee can + // use to manage the return value that the call eventually generates + +#define SIZEOF_ARGUMENT_REGISTERS 0x30 // Callee register spill + +// +// From CallerSP to ChildSP, the stack frame is composed of the following adjacent regions: +// +// SIZEOF_RETADDR +// SIZEOF_ARGUMENT_REGISTERS +// SIZEOF_RETURN_BLOCK +// SIZEOF_FP_REGS +// + +#define DISTANCE_FROM_CHILDSP_TO_FP_REGS 0 + +#define DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK SIZEOF_FP_REGS + +#define DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS (SIZEOF_FP_REGS + SIZEOF_RETURN_BLOCK) + +#define DISTANCE_FROM_CHILDSP_TO_RETADDR (SIZEOF_FP_REGS + SIZEOF_RETURN_BLOCK + SIZEOF_ARGUMENT_REGISTERS + 8) + +// +// Defines an assembly thunk used to make a transition from managed code to a callee, +// then (based on the return value from the callee), either returning or jumping to +// a new location while preserving the input arguments. The usage of this thunk also +// ensures arguments passed are properly reported. +// +// TODO: This code currently only tailcalls, and does not return. +// +// Inputs: +// rdi, esi, rcx, rdx, r8, r9, stack space: arguments as normal +// r10: The location of the target code the UniversalTransition thunk will call +// r11: The only parameter to the target function (passed in rdx to callee) +// + +// +// Frame layout is: +// +// {StackPassedArgs} ChildSP+0D0 CallerSP+000 +// {CallerRetaddr} ChildSP+0C8 CallerSP-008 +// {AlignmentPad (0x8 bytes)} ChildSP+0C0 CallerSP-010 +// {IntArgRegs (0x30 bytes)} ChildSP+090 CallerSP-040 +// {ReturnBlock (0x10 bytes)} ChildSP+080 CallerSP-050 +// {FpArgRegs (xmm0-xmm7) (0x80 bytes)} ChildSP+000 CallerSP-0D0 +// {CalleeRetaddr} ChildSP-008 CallerSP-0D8 +// +// NOTE: If the frame layout ever changes, the C++ UniversalTransitionStackFrame structure +// must be updated as well. +// +// NOTE: The callee receives a pointer to the base of the ReturnBlock, and the callee has +// knowledge of the exact layout of all pieces of the frame that lie at or above the pushed +// FpArgRegs. +// +// NOTE: The stack walker guarantees that conservative GC reporting will be applied to +// everything between the base of the ReturnBlock and the top of the StackPassedArgs. +// + +.macro UNIVERSAL_TRANSITION FunctionName + +NESTED_ENTRY Rhp\FunctionName, _TEXT, NoHandler + + alloc_stack DISTANCE_FROM_CHILDSP_TO_RETADDR + + // save integer argument registers + mov [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x00], rdi + mov [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x08], rsi + mov [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x10], rcx + mov [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x18], rdx + mov [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x20], r8 + mov [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x28], r9 + + // save fp argument registers + movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x00], xmm0 + movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x10], xmm1 + movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x20], xmm2 + movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x30], xmm3 + movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x40], xmm4 + movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x50], xmm5 + movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x60], xmm6 + movdqa [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x70], xmm7 + +#ifdef TRASH_SAVED_ARGUMENT_REGISTERS + + // Before calling out, trash all of the argument registers except the ones (rdi, rsi) that + // hold outgoing arguments. All of these registers have been saved to the transition + // frame, and the code at the call target is required to use only the transition frame + // copies when dispatching this call to the eventual callee. + + movsd xmm0, [C_VAR(RhpFpTrashValues) + 0x0] + movsd xmm1, [C_VAR(RhpFpTrashValues) + 0x8] + movsd xmm2, [C_VAR(RhpFpTrashValues) + 0x10] + movsd xmm3, [C_VAR(RhpFpTrashValues) + 0x18] + movsd xmm4, [C_VAR(RhpFpTrashValues) + 0x20] + movsd xmm5, [C_VAR(RhpFpTrashValues) + 0x28] + movsd xmm6, [C_VAR(RhpFpTrashValues) + 0x30] + movsd xmm7, [C_VAR(RhpFpTrashValues) + 0x38] + + mov rcx, qword ptr [C_VAR(RhpIntegerTrashValues) + 0x10] + mov rdx, qword ptr [C_VAR(RhpIntegerTrashValues) + 0x18] + mov r8, qword ptr [C_VAR(RhpIntegerTrashValues) + 0x20] + mov r9, qword ptr [C_VAR(RhpIntegerTrashValues) + 0x28] + +#endif // TRASH_SAVED_ARGUMENT_REGISTERS + + // + // Call out to the target, while storing and reporting arguments to the GC. + // + mov rsi, r11 + lea rdi, [rsp + DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK] + call r10 + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom\FunctionName + + // restore fp argument registers + movdqa xmm0, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x00] + movdqa xmm1, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x10] + movdqa xmm2, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x20] + movdqa xmm3, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x30] + movdqa xmm4, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x40] + movdqa xmm5, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x50] + movdqa xmm6, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x60] + movdqa xmm7, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x70] + + // restore integer argument registers + mov rdi, [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x00] + mov rsi, [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x08] + mov rcx, [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x10] + mov rdx, [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x18] + mov r8, [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x20] + mov r9, [rsp + DISTANCE_FROM_CHILDSP_TO_ARGUMENT_REGISTERS + 0x28] + + // Pop the space that was allocated between the ChildSP and the caller return address. + free_stack DISTANCE_FROM_CHILDSP_TO_RETADDR + + jmp rax + +NESTED_END Rhp\FunctionName, _TEXT + +.endm // UNIVERSAL_TRANSITION + + // To enable proper step-in behavior in the debugger, we need to have two instances + // of the thunk. For the first one, the debugger steps into the call in the function, + // for the other, it steps over it. + UNIVERSAL_TRANSITION UniversalTransition + UNIVERSAL_TRANSITION UniversalTransition_DebugStepTailCall + +#endif // FEATURE_DYNAMIC_CODE diff --git a/src/coreclr/nativeaot/Runtime/amd64/UniversalTransition.asm b/src/coreclr/nativeaot/Runtime/amd64/UniversalTransition.asm new file mode 100644 index 00000000000000..676ebe396c7e63 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/UniversalTransition.asm @@ -0,0 +1,167 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include AsmMacros.inc + +ifdef FEATURE_DYNAMIC_CODE + +ifdef _DEBUG +TRASH_SAVED_ARGUMENT_REGISTERS equ 1 +else +TRASH_SAVED_ARGUMENT_REGISTERS equ 0 +endif + +if TRASH_SAVED_ARGUMENT_REGISTERS ne 0 +EXTERN RhpIntegerTrashValues : QWORD +EXTERN RhpFpTrashValues : QWORD +endif ;; TRASH_SAVED_ARGUMENT_REGISTERS + +SIZEOF_RETADDR equ 8h + +SIZEOF_ALIGNMENT_PADDING equ 8h + +SIZEOF_RETURN_BLOCK equ 10h ; for 16 bytes of conservatively reported space that the callee can + ; use to manage the return value that the call eventually generates + +SIZEOF_FP_REGS equ 40h ; xmm0-3 + +SIZEOF_OUT_REG_HOMES equ 20h ; Callee register spill + +; +; From CallerSP to ChildSP, the stack frame is composed of the following adjacent regions: +; +; SIZEOF_RETADDR +; SIZEOF_ALIGNMENT_PADDING +; SIZEOF_RETURN_BLOCK +; SIZEOF_FP_REGS +; SIZEOF_OUT_REG_HOMES +; + +DISTANCE_FROM_CHILDSP_TO_FP_REGS equ SIZEOF_OUT_REG_HOMES + +DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK equ DISTANCE_FROM_CHILDSP_TO_FP_REGS + SIZEOF_FP_REGS + +DISTANCE_FROM_CHILDSP_TO_RETADDR equ DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK + SIZEOF_RETURN_BLOCK + SIZEOF_ALIGNMENT_PADDING + +DISTANCE_FROM_CHILDSP_TO_CALLERSP equ DISTANCE_FROM_CHILDSP_TO_RETADDR + SIZEOF_RETADDR + +.errnz DISTANCE_FROM_CHILDSP_TO_CALLERSP mod 16 + +;; +;; Defines an assembly thunk used to make a transition from managed code to a callee, +;; then (based on the return value from the callee), either returning or jumping to +;; a new location while preserving the input arguments. The usage of this thunk also +;; ensures arguments passed are properly reported. +;; +;; TODO: This code currently only tailcalls, and does not return. +;; +;; Inputs: +;; rcx, rdx, r8, r9, stack space: arguments as normal +;; r10: The location of the target code the UniversalTransition thunk will call +;; r11: The only parameter to the target function (passed in rdx to callee) +;; + +; +; Frame layout is: +; +; {StackPassedArgs} ChildSP+0a0 CallerSP+020 +; {IntArgRegs (rcx,rdx,r8,r9) (0x20 bytes)} ChildSP+080 CallerSP+000 +; {CallerRetaddr} ChildSP+078 CallerSP-008 +; {AlignmentPad (0x8 bytes)} ChildSP+070 CallerSP-010 +; {ReturnBlock (0x10 bytes)} ChildSP+060 CallerSP-020 +; {FpArgRegs (xmm0-xmm3) (0x40 bytes)} ChildSP+020 CallerSP-060 +; {CalleeArgumentHomes (0x20 bytes)} ChildSP+000 CallerSP-080 +; {CalleeRetaddr} ChildSP-008 CallerSP-088 +; +; NOTE: If the frame layout ever changes, the C++ UniversalTransitionStackFrame structure +; must be updated as well. +; +; NOTE: The callee receives a pointer to the base of the ReturnBlock, and the callee has +; knowledge of the exact layout of all pieces of the frame that lie at or above the pushed +; FpArgRegs. +; +; NOTE: The stack walker guarantees that conservative GC reporting will be applied to +; everything between the base of the ReturnBlock and the top of the StackPassedArgs. +; + +UNIVERSAL_TRANSITION macro FunctionName + +NESTED_ENTRY Rhp&FunctionName, _TEXT + + alloc_stack DISTANCE_FROM_CHILDSP_TO_RETADDR + + save_reg_postrsp rcx, 0h + DISTANCE_FROM_CHILDSP_TO_CALLERSP + save_reg_postrsp rdx, 8h + DISTANCE_FROM_CHILDSP_TO_CALLERSP + save_reg_postrsp r8, 10h + DISTANCE_FROM_CHILDSP_TO_CALLERSP + save_reg_postrsp r9, 18h + DISTANCE_FROM_CHILDSP_TO_CALLERSP + + save_xmm128_postrsp xmm0, DISTANCE_FROM_CHILDSP_TO_FP_REGS + save_xmm128_postrsp xmm1, DISTANCE_FROM_CHILDSP_TO_FP_REGS + 10h + save_xmm128_postrsp xmm2, DISTANCE_FROM_CHILDSP_TO_FP_REGS + 20h + save_xmm128_postrsp xmm3, DISTANCE_FROM_CHILDSP_TO_FP_REGS + 30h + + END_PROLOGUE + +if TRASH_SAVED_ARGUMENT_REGISTERS ne 0 + + ; Before calling out, trash all of the argument registers except the ones (rcx, rdx) that + ; hold outgoing arguments. All of these registers have been saved to the transition + ; frame, and the code at the call target is required to use only the transition frame + ; copies when dispatching this call to the eventual callee. + + movsd xmm0, mmword ptr [RhpFpTrashValues + 0h] + movsd xmm1, mmword ptr [RhpFpTrashValues + 8h] + movsd xmm2, mmword ptr [RhpFpTrashValues + 10h] + movsd xmm3, mmword ptr [RhpFpTrashValues + 18h] + + mov r8, qword ptr [RhpIntegerTrashValues + 10h] + mov r9, qword ptr [RhpIntegerTrashValues + 18h] + +endif ; TRASH_SAVED_ARGUMENT_REGISTERS + + ; + ; Call out to the target, while storing and reporting arguments to the GC. + ; + mov rdx, r11 + lea rcx, [rsp + DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK] + call r10 + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom&FunctionName + + ; We cannot make the label public as that tricks DIA stackwalker into thinking + ; it's the beginning of a method. For this reason we export the address + ; by means of an auxiliary variable. + + ; restore fp argument registers + movdqa xmm0, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS ] + movdqa xmm1, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 10h] + movdqa xmm2, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 20h] + movdqa xmm3, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 30h] + + ; restore integer argument registers + mov rcx, [rsp + 0h + DISTANCE_FROM_CHILDSP_TO_CALLERSP] + mov rdx, [rsp + 8h + DISTANCE_FROM_CHILDSP_TO_CALLERSP] + mov r8, [rsp + 10h + DISTANCE_FROM_CHILDSP_TO_CALLERSP] + mov r9, [rsp + 18h + DISTANCE_FROM_CHILDSP_TO_CALLERSP] + + ; epilog + nop + + ; Pop the space that was allocated between the ChildSP and the caller return address. + add rsp, DISTANCE_FROM_CHILDSP_TO_RETADDR + + TAILJMP_RAX + +NESTED_END Rhp&FunctionName, _TEXT + + endm + + ; To enable proper step-in behavior in the debugger, we need to have two instances + ; of the thunk. For the first one, the debugger steps into the call in the function, + ; for the other, it steps over it. + UNIVERSAL_TRANSITION UniversalTransition + UNIVERSAL_TRANSITION UniversalTransition_DebugStepTailCall + +endif + +end diff --git a/src/coreclr/nativeaot/Runtime/amd64/WriteBarriers.S b/src/coreclr/nativeaot/Runtime/amd64/WriteBarriers.S new file mode 100644 index 00000000000000..ac4c4b496252a4 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/WriteBarriers.S @@ -0,0 +1,287 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include + +#ifdef WRITE_BARRIER_CHECK + +.macro UPDATE_GC_SHADOW BASENAME, REFREG, DESTREG + + // If g_GCShadow is 0, don't perform the check. + cmp qword ptr [C_VAR(g_GCShadow)], 0 + je LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Done_\REFREG) + + // Save DESTREG since we're about to modify it (and we need the original value both within the macro and + // once we exit the macro). Note that this is naughty since we're altering the stack pointer outside of + // the prolog inside a method without a frame. But given that this is only debug code and generally we + // shouldn't be walking the stack at this point it seems preferable to recoding the all the barrier + // variants to set up frames. The compiler knows exactly which registers are trashed in the simple write + // barrier case, so we don't have any more scratch registers to play with (and doing so would only make + // things harder if at a later stage we want to allow multiple barrier versions based on the input + // registers). + push \DESTREG + + // Transform DESTREG into the equivalent address in the shadow heap. + sub \DESTREG, [C_VAR(g_lowest_address)] + jb LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_PopThenDone_\REFREG) + add \DESTREG, [C_VAR(g_GCShadow)] + cmp \DESTREG, [C_VAR(g_GCShadowEnd)] + ja LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_PopThenDone_\REFREG) + + // Update the shadow heap. + mov [\DESTREG], \REFREG + + // Now check that the real heap location still contains the value we just wrote into the shadow heap. This + // read must be strongly ordered wrt to the previous write to prevent race conditions. We also need to + // recover the old value of DESTREG for the comparison so use an xchg instruction (which has an implicit lock + // prefix). + xchg [rsp], \DESTREG + cmp [\DESTREG], \REFREG + jne LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Invalidate_\REFREG) + + // The original DESTREG value is now restored but the stack has a value (the shadow version of the + // location) pushed. Need to discard this push before we are done. + add rsp, 8 + jmp LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Done_\REFREG) + +LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Invalidate_\REFREG): + // Someone went and updated the real heap. We need to invalidate the shadow location since we can't + // guarantee whose shadow update won. + + // Retrieve shadow location from the stack and restore original DESTREG to the stack. This is an + // additional memory barrier we don't require but it's on the rare path and x86 doesn't have an xchg + // variant that doesn't implicitly specify the lock prefix. Note that INVALIDGCVALUE is a 64-bit + // immediate and therefore must be moved into a register before it can be written to the shadow + // location. + xchg [rsp], \DESTREG + push \REFREG + movabs \REFREG, INVALIDGCVALUE + mov qword ptr [\DESTREG], \REFREG + pop \REFREG + +LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_PopThenDone_\REFREG): + // Restore original DESTREG value from the stack. + pop \DESTREG + +LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Done_\REFREG): +.endm + +#else // WRITE_BARRIER_CHECK + +.macro UPDATE_GC_SHADOW BASENAME, REFREG, DESTREG +.endm + +#endif // WRITE_BARRIER_CHECK + +// There are several different helpers used depending on which register holds the object reference. Since all +// the helpers have identical structure we use a macro to define this structure. Two arguments are taken, the +// name of the register that points to the location to be updated and the name of the register that holds the +// object reference (this should be in upper case as it's used in the definition of the name of the helper). +.macro DEFINE_UNCHECKED_WRITE_BARRIER_CORE BASENAME, REFREG + + // Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + // we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW \BASENAME, \REFREG, rdi + + // If the reference is to an object that's not in an ephemeral generation we have no need to track it + // (since the object won't be collected or moved by an ephemeral collection). + cmp \REFREG, [C_VAR(g_ephemeral_low)] + jb LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG) + cmp \REFREG, [C_VAR(g_ephemeral_high)] + jae LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG) + + // We have a location on the GC heap being updated with a reference to an ephemeral object so we must + // track this write. The location address is translated into an offset in the card table bitmap. We set + // an entire byte in the card table since it's quicker than messing around with bitmasks and we only write + // the byte if it hasn't already been done since writes are expensive and impact scaling. + shr rdi, 11 + add rdi, [C_VAR(g_card_table)] + cmp byte ptr [rdi], 0x0FF + jne LOCAL_LABEL(\BASENAME\()_UpdateCardTable_\REFREG) + +LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG): + ret + +// We get here if it's necessary to update the card table. +LOCAL_LABEL(\BASENAME\()_UpdateCardTable_\REFREG): + mov byte ptr [rdi], 0x0FF + ret + +.endm + +// There are several different helpers used depending on which register holds the object reference. Since all +// the helpers have identical structure we use a macro to define this structure. One argument is taken, the +// name of the register that will hold the object reference (this should be in upper case as it's used in the +// definition of the name of the helper). +.macro DEFINE_UNCHECKED_WRITE_BARRIER REFREG, EXPORT_REG_NAME + +// Define a helper with a name of the form RhpAssignRefEAX etc. (along with suitable calling standard +// decoration). The location to be updated is in DESTREG. The object reference that will be assigned into that +// location is in one of the other general registers determined by the value of REFREG. + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen on the first instruction +// - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpAssignRef\EXPORT_REG_NAME, _TEXT + + // Export the canonical write barrier under unqualified name as well + .ifc \REFREG, RSI + ALTERNATE_ENTRY RhpAssignRef + ALTERNATE_ENTRY RhpAssignRefAVLocation + .endif + + // Write the reference into the location. Note that we rely on the fact that no GC can occur between here + // and the card table update we may perform below. + mov qword ptr [rdi], \REFREG + + DEFINE_UNCHECKED_WRITE_BARRIER_CORE RhpAssignRef, \REFREG + +LEAF_END RhpAssignRef\EXPORT_REG_NAME, _TEXT +.endm + +// One day we might have write barriers for all the possible argument registers but for now we have +// just one write barrier that assumes the input register is RSI. +DEFINE_UNCHECKED_WRITE_BARRIER RSI, ESI + +// +// Define the helpers used to implement the write barrier required when writing an object reference into a +// location residing on the GC heap. Such write barriers allow the GC to optimize which objects in +// non-ephemeral generations need to be scanned for references to ephemeral objects during an ephemeral +// collection. +// + +.macro DEFINE_CHECKED_WRITE_BARRIER_CORE BASENAME, REFREG + + // The location being updated might not even lie in the GC heap (a handle or stack location for instance), + // in which case no write barrier is required. + cmp rdi, [C_VAR(g_lowest_address)] + jb LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG) + cmp rdi, [C_VAR(g_highest_address)] + jae LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG) + + DEFINE_UNCHECKED_WRITE_BARRIER_CORE \BASENAME, \REFREG + +.endm + +// There are several different helpers used depending on which register holds the object reference. Since all +// the helpers have identical structure we use a macro to define this structure. One argument is taken, the +// name of the register that will hold the object reference (this should be in upper case as it's used in the +// definition of the name of the helper). +.macro DEFINE_CHECKED_WRITE_BARRIER REFREG, EXPORT_REG_NAME + +// Define a helper with a name of the form RhpCheckedAssignRefEAX etc. (along with suitable calling standard +// decoration). The location to be updated is always in RDI. The object reference that will be assigned into +// that location is in one of the other general registers determined by the value of REFREG. + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen on the first instruction +// - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpCheckedAssignRef\EXPORT_REG_NAME, _TEXT + + // Export the canonical write barrier under unqualified name as well + .ifc \REFREG, RSI + ALTERNATE_ENTRY RhpCheckedAssignRef + ALTERNATE_ENTRY RhpCheckedAssignRefAVLocation + .endif + + // Write the reference into the location. Note that we rely on the fact that no GC can occur between here + // and the card table update we may perform below. + mov qword ptr [rdi], \REFREG + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedAssignRef, \REFREG + +LEAF_END RhpCheckedAssignRef\EXPORT_REG_NAME, _TEXT +.endm + +// One day we might have write barriers for all the possible argument registers but for now we have +// just one write barrier that assumes the input register is RSI. +DEFINE_CHECKED_WRITE_BARRIER RSI, ESI + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedLockCmpXchgAVLocation +// - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpCheckedLockCmpXchg, _TEXT + mov rax, rdx +ALTERNATE_ENTRY RhpCheckedLockCmpXchgAVLocation + lock cmpxchg [rdi], rsi + jne LOCAL_LABEL(RhpCheckedLockCmpXchg_NoBarrierRequired_RSI) + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedLockCmpXchg, RSI + +LEAF_END RhpCheckedLockCmpXchg, _TEXT + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedXchgAVLocation +// - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpCheckedXchg, _TEXT + + // Setup rax with the new object for the exchange, that way it will automatically hold the correct result + // afterwards and we can leave rdx unaltered ready for the GC write barrier below. + mov rax, rsi +ALTERNATE_ENTRY RhpCheckedXchgAVLocation + xchg [rdi], rax + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedXchg, RSI + +LEAF_END RhpCheckedXchg, _TEXT + +// +// RhpByRefAssignRef simulates movs instruction for object references. +// +// On entry: +// rdi: address of ref-field (assigned to) +// rsi: address of the data (source) +// rcx: be trashed +// +// On exit: +// rdi, rsi are incremented by 8, +// rcx: trashed +// +LEAF_ENTRY RhpByRefAssignRef, _TEXT + mov rcx, [rsi] + mov [rdi], rcx + + // Check whether the writes were even into the heap. If not there's no card update required. + cmp rdi, [C_VAR(g_lowest_address)] + jb LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) + cmp rdi, [C_VAR(g_highest_address)] + jae LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) + + // Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + // we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW BASENAME, rcx, rdi + + // If the reference is to an object that's not in an ephemeral generation we have no need to track it + // (since the object won't be collected or moved by an ephemeral collection). + cmp rcx, [C_VAR(g_ephemeral_low)] + jb LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) + cmp rcx, [C_VAR(g_ephemeral_high)] + jae LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) + + // move current rdi value into rcx and then increment the pointers + mov rcx, rdi + add rsi, 0x8 + add rdi, 0x8 + + // We have a location on the GC heap being updated with a reference to an ephemeral object so we must + // track this write. The location address is translated into an offset in the card table bitmap. We set + // an entire byte in the card table since it's quicker than messing around with bitmasks and we only write + // the byte if it hasn't already been done since writes are expensive and impact scaling. + shr rcx, 11 + add rcx, [C_VAR(g_card_table)] + cmp byte ptr [rcx], 0x0FF + jne LOCAL_LABEL(RhpByRefAssignRef_UpdateCardTable) + ret + +// We get here if it's necessary to update the card table. +LOCAL_LABEL(RhpByRefAssignRef_UpdateCardTable): + mov byte ptr [rcx], 0x0FF + ret + +LOCAL_LABEL(RhpByRefAssignRef_NotInHeap): + // Increment the pointers before leaving + add rdi, 0x8 + add rsi, 0x8 + ret +LEAF_END RhpByRefAssignRef, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/WriteBarriers.asm b/src/coreclr/nativeaot/Runtime/amd64/WriteBarriers.asm new file mode 100644 index 00000000000000..a249f0f043d29b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/amd64/WriteBarriers.asm @@ -0,0 +1,305 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include AsmMacros.inc + +;; Macro used to copy contents of newly updated GC heap locations to a shadow copy of the heap. This is used +;; during garbage collections to verify that object references where never written to the heap without using a +;; write barrier. Note that we're potentially racing to update the shadow heap while other threads are writing +;; new references to the real heap. Since this can't be solved perfectly without critical sections around the +;; entire update process, we instead update the shadow location and then re-check the real location (as two +;; ordered operations) and if there is a disparity we'll re-write the shadow location with a special value +;; (INVALIDGCVALUE) which disables the check for that location. Since the shadow heap is only validated at GC +;; time and these write barrier operations are atomic wrt to GCs this is sufficient to guarantee that the +;; shadow heap contains only valid copies of real heap values or INVALIDGCVALUE. +ifdef WRITE_BARRIER_CHECK + +g_GCShadow TEXTEQU +g_GCShadowEnd TEXTEQU +INVALIDGCVALUE EQU 0CCCCCCCDh + +EXTERN g_GCShadow : QWORD +EXTERN g_GCShadowEnd : QWORD + +UPDATE_GC_SHADOW macro BASENAME, REFREG, DESTREG + + ;; If g_GCShadow is 0, don't perform the check. + cmp g_GCShadow, 0 + je &BASENAME&_UpdateShadowHeap_Done_&REFREG& + + ;; Save DESTREG since we're about to modify it (and we need the original value both within the macro and + ;; once we exit the macro). Note that this is naughty since we're altering the stack pointer outside of + ;; the prolog inside a method without a frame. But given that this is only debug code and generally we + ;; shouldn't be walking the stack at this point it seems preferable to recoding the all the barrier + ;; variants to set up frames. Unlike RhpBulkWriteBarrier below which is treated as a helper call using the + ;; usual calling convention, the compiler knows exactly which registers are trashed in the simple write + ;; barrier case, so we don't have any more scratch registers to play with (and doing so would only make + ;; things harder if at a later stage we want to allow multiple barrier versions based on the input + ;; registers). + push DESTREG + + ;; Transform DESTREG into the equivalent address in the shadow heap. + sub DESTREG, g_lowest_address + jb &BASENAME&_UpdateShadowHeap_PopThenDone_&REFREG& + add DESTREG, [g_GCShadow] + cmp DESTREG, [g_GCShadowEnd] + ja &BASENAME&_UpdateShadowHeap_PopThenDone_&REFREG& + + ;; Update the shadow heap. + mov [DESTREG], REFREG + + ;; Now check that the real heap location still contains the value we just wrote into the shadow heap. This + ;; read must be strongly ordered wrt to the previous write to prevent race conditions. We also need to + ;; recover the old value of DESTREG for the comparison so use an xchg instruction (which has an implicit lock + ;; prefix). + xchg [rsp], DESTREG + cmp [DESTREG], REFREG + jne &BASENAME&_UpdateShadowHeap_Invalidate_&REFREG& + + ;; The original DESTREG value is now restored but the stack has a value (the shadow version of the + ;; location) pushed. Need to discard this push before we are done. + add rsp, 8 + jmp &BASENAME&_UpdateShadowHeap_Done_&REFREG& + +&BASENAME&_UpdateShadowHeap_Invalidate_&REFREG&: + ;; Someone went and updated the real heap. We need to invalidate the shadow location since we can't + ;; guarantee whose shadow update won. + + ;; Retrieve shadow location from the stack and restore original DESTREG to the stack. This is an + ;; additional memory barrier we don't require but it's on the rare path and x86 doesn't have an xchg + ;; variant that doesn't implicitly specify the lock prefix. Note that INVALIDGCVALUE is a 64-bit + ;; immediate and therefore must be moved into a register before it can be written to the shadow + ;; location. + xchg [rsp], DESTREG + push REFREG + mov REFREG, INVALIDGCVALUE + mov qword ptr [DESTREG], REFREG + pop REFREG + +&BASENAME&_UpdateShadowHeap_PopThenDone_&REFREG&: + ;; Restore original DESTREG value from the stack. + pop DESTREG + +&BASENAME&_UpdateShadowHeap_Done_&REFREG&: +endm + +else ; WRITE_BARRIER_CHECK + +UPDATE_GC_SHADOW macro BASENAME, REFREG, DESTREG +endm + +endif ; WRITE_BARRIER_CHECK + +;; There are several different helpers used depending on which register holds the object reference. Since all +;; the helpers have identical structure we use a macro to define this structure. Two arguments are taken, the +;; name of the register that points to the location to be updated and the name of the register that holds the +;; object reference (this should be in upper case as it's used in the definition of the name of the helper). +DEFINE_UNCHECKED_WRITE_BARRIER_CORE macro BASENAME, REFREG + + ;; Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + ;; we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW BASENAME, REFREG, rcx + + ;; If the reference is to an object that's not in an ephemeral generation we have no need to track it + ;; (since the object won't be collected or moved by an ephemeral collection). + cmp REFREG, [g_ephemeral_low] + jb &BASENAME&_NoBarrierRequired_&REFREG& + cmp REFREG, [g_ephemeral_high] + jae &BASENAME&_NoBarrierRequired_&REFREG& + + ;; We have a location on the GC heap being updated with a reference to an ephemeral object so we must + ;; track this write. The location address is translated into an offset in the card table bitmap. We set + ;; an entire byte in the card table since it's quicker than messing around with bitmasks and we only write + ;; the byte if it hasn't already been done since writes are expensive and impact scaling. + shr rcx, 11 + add rcx, [g_card_table] + cmp byte ptr [rcx], 0FFh + jne &BASENAME&_UpdateCardTable_&REFREG& + +&BASENAME&_NoBarrierRequired_&REFREG&: + ret + +;; We get here if it's necessary to update the card table. +&BASENAME&_UpdateCardTable_&REFREG&: + mov byte ptr [rcx], 0FFh + ret + +endm + +;; There are several different helpers used depending on which register holds the object reference. Since all +;; the helpers have identical structure we use a macro to define this structure. One argument is taken, the +;; name of the register that will hold the object reference (this should be in upper case as it's used in the +;; definition of the name of the helper). +DEFINE_UNCHECKED_WRITE_BARRIER macro REFREG, EXPORT_REG_NAME + +;; Define a helper with a name of the form RhpAssignRefEAX etc. (along with suitable calling standard +;; decoration). The location to be updated is in DESTREG. The object reference that will be assigned into that +;; location is in one of the other general registers determined by the value of REFREG. + +;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen on the first instruction +;; - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpAssignRef&EXPORT_REG_NAME&, _TEXT + + ;; Export the canonical write barrier under unqualified name as well + ifidni , + ALTERNATE_ENTRY RhpAssignRef + ALTERNATE_ENTRY RhpAssignRefAVLocation + endif + + ;; Write the reference into the location. Note that we rely on the fact that no GC can occur between here + ;; and the card table update we may perform below. + mov qword ptr [rcx], REFREG + + DEFINE_UNCHECKED_WRITE_BARRIER_CORE RhpAssignRef, REFREG + +LEAF_END RhpAssignRef&EXPORT_REG_NAME&, _TEXT +endm + +;; One day we might have write barriers for all the possible argument registers but for now we have +;; just one write barrier that assumes the input register is RDX. +DEFINE_UNCHECKED_WRITE_BARRIER RDX, EDX + +;; +;; Define the helpers used to implement the write barrier required when writing an object reference into a +;; location residing on the GC heap. Such write barriers allow the GC to optimize which objects in +;; non-ephemeral generations need to be scanned for references to ephemeral objects during an ephemeral +;; collection. +;; + +DEFINE_CHECKED_WRITE_BARRIER_CORE macro BASENAME, REFREG + + ;; The location being updated might not even lie in the GC heap (a handle or stack location for instance), + ;; in which case no write barrier is required. + cmp rcx, [g_lowest_address] + jb &BASENAME&_NoBarrierRequired_&REFREG& + cmp rcx, [g_highest_address] + jae &BASENAME&_NoBarrierRequired_&REFREG& + + DEFINE_UNCHECKED_WRITE_BARRIER_CORE BASENAME, REFREG + +endm + +;; There are several different helpers used depending on which register holds the object reference. Since all +;; the helpers have identical structure we use a macro to define this structure. One argument is taken, the +;; name of the register that will hold the object reference (this should be in upper case as it's used in the +;; definition of the name of the helper). +DEFINE_CHECKED_WRITE_BARRIER macro REFREG, EXPORT_REG_NAME + +;; Define a helper with a name of the form RhpCheckedAssignRefEAX etc. (along with suitable calling standard +;; decoration). The location to be updated is always in RCX. The object reference that will be assigned into +;; that location is in one of the other general registers determined by the value of REFREG. + +;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen on the first instruction +;; - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpCheckedAssignRef&EXPORT_REG_NAME&, _TEXT + + ;; Export the canonical write barrier under unqualified name as well + ifidni , + ALTERNATE_ENTRY RhpCheckedAssignRef + ALTERNATE_ENTRY RhpCheckedAssignRefAVLocation + endif + + ;; Write the reference into the location. Note that we rely on the fact that no GC can occur between here + ;; and the card table update we may perform below. + mov qword ptr [rcx], REFREG + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedAssignRef, REFREG + +LEAF_END RhpCheckedAssignRef&EXPORT_REG_NAME&, _TEXT +endm + +;; One day we might have write barriers for all the possible argument registers but for now we have +;; just one write barrier that assumes the input register is RDX. +DEFINE_CHECKED_WRITE_BARRIER RDX, EDX + +;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedLockCmpXchgAVLocation +;; - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpCheckedLockCmpXchg, _TEXT + mov rax, r8 +ALTERNATE_ENTRY RhpCheckedLockCmpXchgAVLocation + lock cmpxchg [rcx], rdx + jne RhpCheckedLockCmpXchg_NoBarrierRequired_RDX + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedLockCmpXchg, RDX + +LEAF_END RhpCheckedLockCmpXchg, _TEXT + +;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedXchgAVLocation +;; - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpCheckedXchg, _TEXT + + ;; Setup rax with the new object for the exchange, that way it will automatically hold the correct result + ;; afterwards and we can leave rdx unaltered ready for the GC write barrier below. + mov rax, rdx +ALTERNATE_ENTRY RhpCheckedXchgAVLocation + xchg [rcx], rax + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedXchg, RDX + +LEAF_END RhpCheckedXchg, _TEXT + +;; +;; RhpByRefAssignRef simulates movs instruction for object references. +;; +;; On entry: +;; rdi: address of ref-field (assigned to) +;; rsi: address of the data (source) +;; rcx: be trashed +;; +;; On exit: +;; rdi, rsi are incremented by 8, +;; rcx: trashed +;; +LEAF_ENTRY RhpByRefAssignRef, _TEXT + mov rcx, [rsi] + mov [rdi], rcx + + ;; Check whether the writes were even into the heap. If not there's no card update required. + cmp rdi, [g_lowest_address] + jb RhpByRefAssignRef_NotInHeap + cmp rdi, [g_highest_address] + jae RhpByRefAssignRef_NotInHeap + + ;; Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + ;; we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW BASENAME, rcx, rdi + + ;; If the reference is to an object that's not in an ephemeral generation we have no need to track it + ;; (since the object won't be collected or moved by an ephemeral collection). + cmp rcx, [g_ephemeral_low] + jb RhpByRefAssignRef_NotInHeap + cmp rcx, [g_ephemeral_high] + jae RhpByRefAssignRef_NotInHeap + + ;; move current rdi value into rcx and then increment the pointers + mov rcx, rdi + add rsi, 8h + add rdi, 8h + + ;; We have a location on the GC heap being updated with a reference to an ephemeral object so we must + ;; track this write. The location address is translated into an offset in the card table bitmap. We set + ;; an entire byte in the card table since it's quicker than messing around with bitmasks and we only write + ;; the byte if it hasn't already been done since writes are expensive and impact scaling. + shr rcx, 11 + add rcx, [g_card_table] + cmp byte ptr [rcx], 0FFh + jne RhpByRefAssignRef_UpdateCardTable + ret + +;; We get here if it's necessary to update the card table. +RhpByRefAssignRef_UpdateCardTable: + mov byte ptr [rcx], 0FFh + ret + +RhpByRefAssignRef_NotInHeap: + ; Increment the pointers before leaving + add rdi, 8h + add rsi, 8h + ret +LEAF_END RhpByRefAssignRef, _TEXT + + end diff --git a/src/coreclr/nativeaot/Runtime/arm/AllocFast.S b/src/coreclr/nativeaot/Runtime/arm/AllocFast.S new file mode 100644 index 00000000000000..31b54d1bca313a --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/AllocFast.S @@ -0,0 +1,502 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp +#include + +// Allocate non-array, non-finalizable object. If the allocation doesn't fit into the current thread's +// allocation context then automatically fallback to the slow allocation path. +// r0 == MethodTable +LEAF_ENTRY RhpNewFast, _TEXT + PROLOG_PUSH "{r4,lr}" + mov r4, r0 // save MethodTable + + // r0 = GetThread() + INLINE_GETTHREAD + + // r4 contains MethodTable pointer + ldr r2, [r4, #OFFSETOF__MethodTable__m_uBaseSize] + + // r0: Thread pointer + // r4: MethodTable pointer + // r2: base size + + ldr r3, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + add r2, r3 + ldr r1, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r1 + bhi LOCAL_LABEL(RhpNewFast_RarePath) + + // set the new alloc pointer + str r2, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new object's MethodTable pointer + str r4, [r3, #OFFSETOF__Object__m_pEEType] + + mov r0, r3 + + EPILOG_POP "{r4,pc}" + +LOCAL_LABEL(RhpNewFast_RarePath): + mov r0, r4 // restore MethodTable + mov r1, #0 + EPILOG_POP "{r4,lr}" + b C_FUNC(RhpNewObject) + +LEAF_END RhpNewFast, _TEXT + +// Allocate non-array object with finalizer. +// r0 == MethodTable +// +LEAF_ENTRY RhpNewFinalizable, _TEXT + mov r1, #GC_ALLOC_FINALIZE + b C_FUNC(RhpNewObject) +LEAF_END RhpNewFinalizable, _TEXT + + +// Allocate non-array object. +// r0 == MethodTable +// r1 == alloc flags +NESTED_ENTRY RhpNewObject, _TEXT, NoHandler + + PUSH_COOP_PINVOKE_FRAME r3 + + // r0: MethodTable + // r1: alloc flags + // r3: transition frame + + // Preserve the MethodTable in r5. + mov r5, r0 + + mov r2, #0 // numElements + + // void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + blx C_FUNC(RhpGcAlloc) + + cbz r0, LOCAL_LABEL(NewOutOfMemory) + + POP_COOP_PINVOKE_FRAME + bx lr + +LOCAL_LABEL(NewOutOfMemory): + // This is the OOM failure path. We're going to tail-call to a managed helper that will throw + // an out of memory exception that the caller of this allocator understands. + + mov r0, r5 // MethodTable pointer + mov r1, #0 // Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + + b C_FUNC(RhExceptionHandling_FailedAllocation) + +NESTED_END RhpNewObject, _TEXT + + +// Allocate a string. +// r0 == MethodTable +// r1 == element/character count +LEAF_ENTRY RhNewString, _TEXT + PROLOG_PUSH "{r4-r6,lr}" + // Make sure computing the overall allocation size won't overflow + MOV32 r12, MAX_STRING_LENGTH + cmp r1, r12 + bhi LOCAL_LABEL(StringSizeOverflow) + + // Compute overall allocation size (align(base size + (element size * elements), 4)). + mov r2, #(STRING_BASE_SIZE + 3) +#if STRING_COMPONENT_SIZE == 2 + add r2, r2, r1, lsl #1 // r2 += characters * 2 +#else + NotImplementedComponentSize +#endif + bic r2, r2, #3 + + mov r4, r0 // Save MethodTable + mov r5, r1 // Save element count + mov r6, r2 // Save string size + // r0 = GetThread() + INLINE_GETTHREAD + // r4 == MethodTable + // r5 == element count + // r6 == string size + // r0 == Thread* + + // Load potential new object address into r12. + ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Determine whether the end of the object would lie outside of the current allocation context. If so, + // we abandon the attempt to allocate the object directly and fall back to the slow helper. + adds r6, r12 + bcs LOCAL_LABEL(RhNewString_RarePath) // if we get a carry here, the string is too large to fit below 4 GB + + ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r6, r12 + bhi LOCAL_LABEL(RhNewString_RarePath) + + // Reload new object address into r12. + ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Update the alloc pointer to account for the allocation. + str r6, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new object's MethodTable pointer and element count. + str r4, [r12, #OFFSETOF__Object__m_pEEType] + str r5, [r12, #OFFSETOF__String__m_Length] + + // Return the object allocated in r0. + mov r0, r12 + EPILOG_POP "{r4-r6,pc}" + +LOCAL_LABEL(StringSizeOverflow): + // We get here if the size of the final string object can't be represented as an unsigned + // 32-bit value. We're going to tail-call to a managed helper that will throw + // an OOM exception that the caller of this allocator understands. + + // MethodTable is in r0 already + mov r1, 0 // Indicate that we should throw OOM + EPILOG_POP "{r4-r6,lr}" + b C_FUNC(RhExceptionHandling_FailedAllocation) + +LOCAL_LABEL(RhNewString_RarePath): + mov r3, r0 + mov r0, r4 + mov r1, r5 + mov r2, r6 + // r0 == MethodTable + // r1 == element count + // r2 == string size + Thread::m_alloc_context::alloc_ptr + // r3 == Thread + EPILOG_POP "{r4-r6,lr}" + b C_FUNC(RhpNewArrayRare) + +LEAF_END RhNewString, _TEXT + + +// Allocate one dimensional, zero based array (SZARRAY). +// r0 == MethodTable +// r1 == element count +LEAF_ENTRY RhpNewArray, _TEXT + PROLOG_PUSH "{r4-r6,lr}" + + // Compute overall allocation size (align(base size + (element size * elements), 4)). + // if the element count is <= 0x10000, no overflow is possible because the component + // size is <= 0xffff (it's an unsigned 16-bit value) and thus the product is <= 0xffff0000 + // and the base size for the worst case (32 dimensional MdArray) is less than 0xffff. + ldrh r2, [r0, #OFFSETOF__MethodTable__m_usComponentSize] + cmp r1, #0x10000 + bhi LOCAL_LABEL(ArraySizeBig) + umull r2, r3, r2, r1 + ldr r3, [r0, #OFFSETOF__MethodTable__m_uBaseSize] + adds r2, r3 + adds r2, #3 +LOCAL_LABEL(ArrayAlignSize): + bic r2, r2, #3 + + mov r4, r0 // Save MethodTable + mov r5, r1 // Save element count + mov r6, r2 // Save array size + // r0 = GetThread() + INLINE_GETTHREAD + // r4 == MethodTable + // r5 == element count + // r6 == array size + // r0 == Thread* + + // Load potential new object address into r12. + ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Determine whether the end of the object would lie outside of the current allocation context. If so, + // we abandon the attempt to allocate the object directly and fall back to the slow helper. + adds r6, r12 + bcs LOCAL_LABEL(RhpNewArray_RarePath) // if we get a carry here, the array is too large to fit below 4 GB + + ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r6, r12 + bhi LOCAL_LABEL(RhpNewArray_RarePath) + + // Reload new object address into r12. + ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Update the alloc pointer to account for the allocation. + str r6, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new object's MethodTable pointer and element count. + str r4, [r12, #OFFSETOF__Object__m_pEEType] + str r5, [r12, #OFFSETOF__Array__m_Length] + + // Return the object allocated in r0. + mov r0, r12 + EPILOG_POP "{r4-r6,pc}" + +LOCAL_LABEL(ArraySizeBig): + // if the element count is negative, it's an overflow error + cmp r1, #0 + blt LOCAL_LABEL(ArraySizeOverflow) + + // now we know the element count is in the signed int range [0..0x7fffffff] + // overflow in computing the total size of the array size gives an out of memory exception, + // NOT an overflow exception + // we already have the component size in r2 + umull r2, r3, r2, r1 + cbnz r3, LOCAL_LABEL(ArrayOutOfMemoryFinal) + ldr r3, [r0, #OFFSETOF__MethodTable__m_uBaseSize] + adds r2, r3 + bcs LOCAL_LABEL(ArrayOutOfMemoryFinal) + adds r2, #3 + bcs LOCAL_LABEL(ArrayOutOfMemoryFinal) + b LOCAL_LABEL(ArrayAlignSize) + +LOCAL_LABEL(ArrayOutOfMemoryFinal): + + // MethodTable is in r0 already + mov r1, #0 // Indicate that we should throw OOM. + EPILOG_POP "{r4-r6,lr}" + b C_FUNC(RhExceptionHandling_FailedAllocation) + +LOCAL_LABEL(ArraySizeOverflow): + // We get here if the size of the final array object can't be represented as an unsigned + // 32-bit value. We're going to tail-call to a managed helper that will throw + // an overflow exception that the caller of this allocator understands. + + // MethodTable is in r0 already + mov r1, #1 // Indicate that we should throw OverflowException + EPILOG_POP "{r4-r6,lr}" + b C_FUNC(RhExceptionHandling_FailedAllocation) + +LOCAL_LABEL(RhpNewArray_RarePath): + mov r3, r0 + mov r0, r4 + mov r1, r5 + mov r2, r6 + // r0 == MethodTable + // r1 == element count + // r2 == array size + Thread::m_alloc_context::alloc_ptr + // r3 == Thread + EPILOG_POP "{r4-r6,lr}" + b C_FUNC(RhpNewArrayRare) + +LEAF_END RhpNewArray, _TEXT + + +// Allocate one dimensional, zero based array (SZARRAY) using the slow path that calls a runtime helper. +// r0 == MethodTable +// r1 == element count +// r2 == array size + Thread::m_alloc_context::alloc_ptr +// r3 == Thread +NESTED_ENTRY RhpNewArrayRare, _TEXT, NoHandler + + // Recover array size by subtracting the alloc_ptr from r2. + ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + sub r2, r12 + + PUSH_COOP_PINVOKE_FRAME r3 + + // Preserve the MethodTable in r5. + mov r5, r0 + + mov r2, r1 // numElements + mov r1, #0 // uFlags + + // void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + blx C_FUNC(RhpGcAlloc) + + // Test for failure (NULL return). + cbz r0, LOCAL_LABEL(ArrayOutOfMemory) + + POP_COOP_PINVOKE_FRAME + bx lr + +LOCAL_LABEL(ArrayOutOfMemory): + + mov r0, r5 // MethodTable + mov r1, #0 // Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + + b C_FUNC(RhExceptionHandling_FailedAllocation) + +NESTED_END RhpNewArrayRare, _TEXT + +// Allocate simple object (not finalizable, array or value type) on an 8 byte boundary. +// r0 == MethodTable +LEAF_ENTRY RhpNewFastAlign8, _TEXT + PROLOG_PUSH "{r4,lr}" + + mov r4, r0 // save MethodTable + + // r0 = GetThread() + INLINE_GETTHREAD + + // Fetch object size into r2. + ldr r2, [r4, #OFFSETOF__MethodTable__m_uBaseSize] + + // r4: MethodTable pointer + // r0: Thread pointer + // r2: base size + + // Load potential new object address into r3. Cache this result in r12 as well for the common case + // where the allocation succeeds (r3 will be overwritten in the following bounds check). + ldr r3, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + mov r12, r3 + + // Check whether the current allocation context is already aligned for us. + tst r3, #0x7 + bne LOCAL_LABEL(Alloc8Failed) + + // Determine whether the end of the object would lie outside of the current allocation context. If so, + // we abandon the attempt to allocate the object directly and fall back to the slow helper. + add r2, r3 + ldr r3, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r3 + bhi LOCAL_LABEL(Alloc8Failed) + + // Update the alloc pointer to account for the allocation. + str r2, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new object's MethodTable pointer. + str r4, [r12, #OFFSETOF__Object__m_pEEType] + + // Return the object allocated in r0. + mov r0, r12 + + EPILOG_POP "{r4,pc}" + +LOCAL_LABEL(Alloc8Failed): + // Fast allocation failed. Call slow helper with flags set to indicate an 8-byte alignment and no + // finalization. + mov r0, r4 // restore MethodTable + mov r1, #GC_ALLOC_ALIGN8 + EPILOG_POP "{r4,lr}" + b C_FUNC(RhpNewObject) + +LEAF_END RhpNewFastAlign8, _TEXT + +// Allocate a finalizable object (by definition not an array or value type) on an 8 byte boundary. +// r0 == MethodTable +LEAF_ENTRY RhpNewFinalizableAlign8, _TEXT + mov r1, #(GC_ALLOC_FINALIZE | GC_ALLOC_ALIGN8) + b C_FUNC(RhpNewObject) +LEAF_END RhpNewFinalizableAlign8, _TEXT + +// Allocate a value type object (i.e. box it) on an 8 byte boundary + 4 (so that the value type payload +// itself is 8 byte aligned). +// r0 == MethodTable +LEAF_ENTRY RhpNewFastMisalign, _TEXT + PROLOG_PUSH "{r4,lr}" + + mov r4, r0 // save MethodTable + + // r0 = GetThread() + INLINE_GETTHREAD + + // Fetch object size into r2. + ldr r2, [r4, #OFFSETOF__MethodTable__m_uBaseSize] + + // r4: MethodTable pointer + // r0: Thread pointer + // r2: base size + + // Load potential new object address into r3. Cache this result in r12 as well for the common case + // where the allocation succeeds (r3 will be overwritten in the following bounds check). + ldr r3, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + mov r12, r3 + + // Check whether the current allocation context is already aligned for us (for boxing that means the + // address % 8 == 4, so the value type payload following the MethodTable* is actually 8-byte aligned). + tst r3, #0x7 + beq LOCAL_LABEL(BoxAlloc8Failed) + + // Determine whether the end of the object would lie outside of the current allocation context. If so, + // we abandon the attempt to allocate the object directly and fall back to the slow helper. + add r2, r3 + ldr r3, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r3 + bhi LOCAL_LABEL(BoxAlloc8Failed) + + // Update the alloc pointer to account for the allocation. + str r2, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new object's MethodTable pointer. + str r4, [r12, #OFFSETOF__Object__m_pEEType] + + // Return the object allocated in r0. + mov r0, r12 + + EPILOG_POP "{r4,pc}" + +LOCAL_LABEL(BoxAlloc8Failed): + // Fast allocation failed. Call slow helper with flags set to indicate an 8+4 byte alignment and no + // finalization. + mov r0, r4 // restore MethodTable + mov r1, #(GC_ALLOC_ALIGN8 | GC_ALLOC_ALIGN8_BIAS) + EPILOG_POP "{r4,lr}" + b C_FUNC(RhpNewObject) + +LEAF_END RhpNewFastMisalign, _TEXT + +// Allocate an array on an 8 byte boundary. +// r0 == MethodTable +// r1 == element count +NESTED_ENTRY RhpNewArrayAlign8, _TEXT, NoHandler + + PUSH_COOP_PINVOKE_FRAME r3 + + // Compute overall allocation size (base size + align((element size * elements), 4)). + ldrh r2, [r0, #OFFSETOF__MethodTable__m_usComponentSize] + umull r2, r4, r2, r1 + cbnz r4, LOCAL_LABEL(Array8SizeOverflow) + adds r2, #3 + bcs LOCAL_LABEL(Array8SizeOverflow) + bic r2, r2, #3 + ldr r4, [r0, #OFFSETOF__MethodTable__m_uBaseSize] + adds r2, r4 + bcs LOCAL_LABEL(Array8SizeOverflow) + + // Preserve the MethodTable in r5. + mov r5, r0 + + mov r2, r1 // numElements + mov r1, #GC_ALLOC_ALIGN8 // uFlags + + // void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + blx C_FUNC(RhpGcAlloc) + + // Test for failure (NULL return). + cbz r0, LOCAL_LABEL(Array8OutOfMemory) + + POP_COOP_PINVOKE_FRAME + + bx lr + +LOCAL_LABEL(Array8SizeOverflow): + // We get here if the size of the final array object can't be represented as an unsigned + // 32-bit value. We're going to tail-call to a managed helper that will throw + // an OOM or overflow exception that the caller of this allocator understands. + + // if the element count is non-negative, it's an OOM error + cmp r1, #0 + bge LOCAL_LABEL(Array8OutOfMemory1) + + // r0 holds MethodTable pointer already + mov r1, #1 // Indicate that we should throw OverflowException + + POP_COOP_PINVOKE_FRAME + b C_FUNC(RhExceptionHandling_FailedAllocation) + +LOCAL_LABEL(Array8OutOfMemory): + // This is the OOM failure path. We're going to tail-call to a managed helper that will throw + // an out of memory exception that the caller of this allocator understands. + + mov r0, r5 // MethodTable pointer + +LOCAL_LABEL(Array8OutOfMemory1): + + mov r1, #0 // Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + b C_FUNC(RhExceptionHandling_FailedAllocation) + +NESTED_END RhpNewArrayAlign8, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm/AllocFast.asm b/src/coreclr/nativeaot/Runtime/arm/AllocFast.asm new file mode 100644 index 00000000000000..cc637768795a18 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/AllocFast.asm @@ -0,0 +1,531 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + +;; Allocate non-array, non-finalizable object. If the allocation doesn't fit into the current thread's +;; allocation context then automatically fallback to the slow allocation path. +;; r0 == MethodTable + LEAF_ENTRY RhpNewFast + + ;; r1 = GetThread(), TRASHES r2 + INLINE_GETTHREAD r1, r2 + + ;; Fetch object size into r2. + ldr r2, [r0, #OFFSETOF__MethodTable__m_uBaseSize] + + ;; + ;; r0: MethodTable pointer + ;; r1: Thread pointer + ;; r2: base size + ;; + + ;; Load potential new object address into r3. Cache this result in r12 as well for the common case + ;; where the allocation succeeds (r3 will be overwritten in the following bounds check). + ldr r3, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + mov r12, r3 + + ;; Determine whether the end of the object would lie outside of the current allocation context. If so, + ;; we abandon the attempt to allocate the object directly and fall back to the slow helper. + add r2, r3 + ldr r3, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r3 + bhi AllocFailed + + ;; Update the alloc pointer to account for the allocation. + str r2, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the new object's MethodTable pointer. + str r0, [r12, #OFFSETOF__Object__m_pEEType] + + ;; Return the object allocated in r0. + mov r0, r12 + + bx lr + +AllocFailed + ;; Fast allocation failed. Call slow helper with flags set to zero (this isn't a finalizable object). + mov r1, #0 + b RhpNewObject + + LEAF_END RhpNewFast + + INLINE_GETTHREAD_CONSTANT_POOL + + +;; Allocate non-array object with finalizer. +;; r0 == MethodTable + LEAF_ENTRY RhpNewFinalizable + mov r1, #GC_ALLOC_FINALIZE + b RhpNewObject + LEAF_END RhpNewFinalizable + +;; Allocate non-array object. +;; r0 == MethodTable +;; r1 == alloc flags + NESTED_ENTRY RhpNewObject + + PUSH_COOP_PINVOKE_FRAME r3 + + ; r0: MethodTable + ; r1: alloc flags + ; r3: transition frame + + ;; Preserve the MethodTable in r5. + mov r5, r0 + + mov r2, #0 ; numElements + + ;; void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + blx RhpGcAlloc + + cbz r0, NewOutOfMemory + + POP_COOP_PINVOKE_FRAME + EPILOG_RETURN + +NewOutOfMemory + ;; This is the OOM failure path. We're going to tail-call to a managed helper that will throw + ;; an out of memory exception that the caller of this allocator understands. + + mov r0, r5 ; MethodTable pointer + mov r1, #0 ; Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + EPILOG_BRANCH RhExceptionHandling_FailedAllocation + + NESTED_END RhpNewObject + + +;; Allocate a string. +;; r0 == MethodTable +;; r1 == element/character count + LEAF_ENTRY RhNewString + + ; Make sure computing the overall allocation size won't overflow + MOV32 r2, MAX_STRING_LENGTH + cmp r1, r2 + bhs StringSizeOverflow + + ; Compute overall allocation size (align(base size + (element size * elements), 4)). + mov r2, #(STRING_BASE_SIZE + 3) +#if STRING_COMPONENT_SIZE == 2 + add r2, r2, r1, lsl #1 ; r2 += characters * 2 +#else + NotImplementedComponentSize +#endif + bic r2, r2, #3 + + ; r0 == MethodTable + ; r1 == element count + ; r2 == string size + + INLINE_GETTHREAD r3, r12 + + ;; Load potential new object address into r12. + ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Determine whether the end of the object would lie outside of the current allocation context. If so, + ;; we abandon the attempt to allocate the object directly and fall back to the slow helper. + adds r2, r12 + bcs RhpNewArrayRare ; if we get a carry here, the array is too large to fit below 4 GB + ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r12 + bhi RhpNewArrayRare + + ;; Reload new object address into r12. + ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Update the alloc pointer to account for the allocation. + str r2, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the new object's MethodTable pointer and element count. + str r0, [r12, #OFFSETOF__Object__m_pEEType] + str r1, [r12, #OFFSETOF__String__m_Length] + + ;; Return the object allocated in r0. + mov r0, r12 + + bx lr + +StringSizeOverflow + ; We get here if the size of the final string object can't be represented as an unsigned + ; 32-bit value. We're going to tail-call to a managed helper that will throw + ; an OOM exception that the caller of this allocator understands. + + ; r0 holds MethodTable pointer already + mov r1, #0 ; Indicate that we should throw OOM. + b RhExceptionHandling_FailedAllocation + + LEAF_END RhNewString + + INLINE_GETTHREAD_CONSTANT_POOL + + +;; Allocate one dimensional, zero based array (SZARRAY). +;; r0 == MethodTable +;; r1 == element count + LEAF_ENTRY RhpNewArray + + ; Compute overall allocation size (align(base size + (element size * elements), 4)). + ; if the element count is <= 0x10000, no overflow is possible because the component + ; size is <= 0xffff (it's an unsigned 16-bit value) and thus the product is <= 0xffff0000 + ; and the base size for the worst case (32 dimensional MdArray) is less than 0xffff. + ldrh r2, [r0, #OFFSETOF__MethodTable__m_usComponentSize] + cmp r1, #0x10000 + bhi ArraySizeBig + umull r2, r3, r2, r1 + ldr r3, [r0, #OFFSETOF__MethodTable__m_uBaseSize] + adds r2, r3 + adds r2, #3 +ArrayAlignSize + bic r2, r2, #3 + + ; r0 == MethodTable + ; r1 == element count + ; r2 == array size + + INLINE_GETTHREAD r3, r12 + + ;; Load potential new object address into r12. + ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Determine whether the end of the object would lie outside of the current allocation context. If so, + ;; we abandon the attempt to allocate the object directly and fall back to the slow helper. + adds r2, r12 + bcs RhpNewArrayRare ; if we get a carry here, the array is too large to fit below 4 GB + ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r12 + bhi RhpNewArrayRare + + ;; Reload new object address into r12. + ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Update the alloc pointer to account for the allocation. + str r2, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the new object's MethodTable pointer and element count. + str r0, [r12, #OFFSETOF__Object__m_pEEType] + str r1, [r12, #OFFSETOF__Array__m_Length] + + ;; Return the object allocated in r0. + mov r0, r12 + + bx lr + +ArraySizeOverflow + ; We get here if the size of the final array object can't be represented as an unsigned + ; 32-bit value. We're going to tail-call to a managed helper that will throw + ; an overflow exception that the caller of this allocator understands. + + ; r0 holds MethodTable pointer already + mov r1, #1 ; Indicate that we should throw OverflowException + b RhExceptionHandling_FailedAllocation + +ArraySizeBig + ; if the element count is negative, it's an overflow error + cmp r1, #0 + blt ArraySizeOverflow + ; now we know the element count is in the signed int range [0..0x7fffffff] + ; overflow in computing the total size of the array size gives an out of memory exception, + ; NOT an overflow exception + ; we already have the component size in r2 + umull r2, r3, r2, r1 + cbnz r3, ArrayOutOfMemoryFinal + ldr r3, [r0, #OFFSETOF__MethodTable__m_uBaseSize] + adds r2, r3 + bcs ArrayOutOfMemoryFinal + adds r2, #3 + bcs ArrayOutOfMemoryFinal + b ArrayAlignSize + +ArrayOutOfMemoryFinal + ; r0 holds MethodTable pointer already + mov r1, #0 ; Indicate that we should throw OOM. + b RhExceptionHandling_FailedAllocation + + LEAF_END RhpNewArray + + INLINE_GETTHREAD_CONSTANT_POOL + + +;; Allocate one dimensional, zero based array (SZARRAY) using the slow path that calls a runtime helper. +;; r0 == MethodTable +;; r1 == element count +;; r3 == Thread + NESTED_ENTRY RhpNewArrayRare + + ; Recover array size by subtracting the alloc_ptr from r2. + PROLOG_NOP ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + PROLOG_NOP sub r2, r12 + + PUSH_COOP_PINVOKE_FRAME r3 + + ; Preserve the MethodTable in r5. + mov r5, r0 + + mov r2, r1 ; numElements + mov r1, #0 ; uFlags + + ; void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + blx RhpGcAlloc + + ; Test for failure (NULL return). + cbz r0, ArrayOutOfMemory + + POP_COOP_PINVOKE_FRAME + EPILOG_RETURN + +ArrayOutOfMemory + ;; This is the OOM failure path. We're going to tail-call to a managed helper that will throw + ;; an out of memory exception that the caller of this allocator understands. + + mov r0, r5 ;; MethodTable pointer + mov r1, #0 ;; Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + EPILOG_BRANCH RhExceptionHandling_FailedAllocation + + NESTED_END RhpNewArrayRare + +;; Allocate simple object (not finalizable, array or value type) on an 8 byte boundary. +;; r0 == MethodTable + LEAF_ENTRY RhpNewFastAlign8 + + ;; r1 = GetThread(), TRASHES r2 + INLINE_GETTHREAD r1, r2 + + ;; Fetch object size into r2. + ldr r2, [r0, #OFFSETOF__MethodTable__m_uBaseSize] + + ;; + ;; r0: MethodTable pointer + ;; r1: Thread pointer + ;; r2: base size + ;; + + ;; Load potential new object address into r3. Cache this result in r12 as well for the common case + ;; where the allocation succeeds (r3 will be overwritten in the following bounds check). + ldr r3, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + mov r12, r3 + + ;; Check whether the current allocation context is already aligned for us. + tst r3, #0x7 + bne ContextMisaligned + + ;; Determine whether the end of the object would lie outside of the current allocation context. If so, + ;; we abandon the attempt to allocate the object directly and fall back to the slow helper. + add r2, r3 + ldr r3, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r3 + bhi Alloc8Failed + + ;; Update the alloc pointer to account for the allocation. + str r2, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the new object's MethodTable pointer. + str r0, [r12, #OFFSETOF__Object__m_pEEType] + + ;; Return the object allocated in r0. + mov r0, r12 + + bx lr + +ContextMisaligned + ;; Allocation context is currently misaligned. We attempt to fix this by allocating a minimum sized + ;; free object (which is sized such that it "flips" the alignment to a good value). + + ;; Determine whether the end of both objects would lie outside of the current allocation context. If + ;; so, we abandon the attempt to allocate the object directly and fall back to the slow helper. + add r2, r3 + add r2, #SIZEOF__MinObject + ldr r3, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r3 + bhi Alloc8Failed + + ;; Update the alloc pointer to account for the allocation. + str r2, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the free object's MethodTable pointer (it's the only field we need to set, a component count of zero + ;; is what we want). + ldr r2, =$G_FREE_OBJECT_EETYPE + ldr r2, [r2] + str r2, [r12, #OFFSETOF__Object__m_pEEType] + + ;; Set the new object's MethodTable pointer. + str r0, [r12, #(SIZEOF__MinObject + OFFSETOF__Object__m_pEEType)] + + ;; Return the object allocated in r0. + add r0, r12, #SIZEOF__MinObject + + bx lr + +Alloc8Failed + ;; Fast allocation failed. Call slow helper with flags set to indicate an 8-byte alignment and no + ;; finalization. + mov r1, #GC_ALLOC_ALIGN8 + b RhpNewObject + + LEAF_END RhpNewFastAlign8 + + INLINE_GETTHREAD_CONSTANT_POOL + + +;; Allocate a finalizable object (by definition not an array or value type) on an 8 byte boundary. +;; r0 == MethodTable + LEAF_ENTRY RhpNewFinalizableAlign8 + + mov r1, #(GC_ALLOC_FINALIZE | GC_ALLOC_ALIGN8) + b RhpNewObject + + LEAF_END RhpNewFinalizableAlign8 + +;; Allocate a value type object (i.e. box it) on an 8 byte boundary + 4 (so that the value type payload +;; itself is 8 byte aligned). +;; r0 == MethodTable + LEAF_ENTRY RhpNewFastMisalign + + ;; r1 = GetThread(), TRASHES r2 + INLINE_GETTHREAD r1, r2 + + ;; Fetch object size into r2. + ldr r2, [r0, #OFFSETOF__MethodTable__m_uBaseSize] + + ;; + ;; r0: MethodTable pointer + ;; r1: Thread pointer + ;; r2: base size + ;; + + ;; Load potential new object address into r3. Cache this result in r12 as well for the common case + ;; where the allocation succeeds (r3 will be overwritten in the following bounds check). + ldr r3, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + mov r12, r3 + + ;; Check whether the current allocation context is already aligned for us (for boxing that means the + ;; address % 8 == 4, so the value type payload following the MethodTable* is actually 8-byte aligned). + tst r3, #0x7 + beq BoxContextMisaligned + + ;; Determine whether the end of the object would lie outside of the current allocation context. If so, + ;; we abandon the attempt to allocate the object directly and fall back to the slow helper. + add r2, r3 + ldr r3, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r3 + bhi BoxAlloc8Failed + + ;; Update the alloc pointer to account for the allocation. + str r2, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the new object's MethodTable pointer. + str r0, [r12, #OFFSETOF__Object__m_pEEType] + + ;; Return the object allocated in r0. + mov r0, r12 + + bx lr + +BoxContextMisaligned + ;; Allocation context is currently misaligned. We attempt to fix this by allocating a minimum sized + ;; free object (which is sized such that it "flips" the alignment to a good value). + + ;; Determine whether the end of both objects would lie outside of the current allocation context. If + ;; so, we abandon the attempt to allocate the object directly and fall back to the slow helper. + add r2, r3 + add r2, #SIZEOF__MinObject + ldr r3, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r3 + bhi BoxAlloc8Failed + + ;; Update the alloc pointer to account for the allocation. + str r2, [r1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the free object's MethodTable pointer (it's the only field we need to set, a component count of zero + ;; is what we want). + ldr r2, =$G_FREE_OBJECT_EETYPE + ldr r2, [r2] + str r2, [r12, #OFFSETOF__Object__m_pEEType] + + ;; Set the new object's MethodTable pointer. + str r0, [r12, #(SIZEOF__MinObject + OFFSETOF__Object__m_pEEType)] + + ;; Return the object allocated in r0. + add r0, r12, #SIZEOF__MinObject + + bx lr + +BoxAlloc8Failed + ;; Fast allocation failed. Call slow helper with flags set to indicate an 8+4 byte alignment and no + ;; finalization. + mov r1, #(GC_ALLOC_ALIGN8 | GC_ALLOC_ALIGN8_BIAS) + b RhpNewObject + + LEAF_END RhpNewFastMisalign + + INLINE_GETTHREAD_CONSTANT_POOL + + +;; Allocate an array on an 8 byte boundary. +;; r0 == MethodTable +;; r1 == element count + NESTED_ENTRY RhpNewArrayAlign8 + + PUSH_COOP_PINVOKE_FRAME r3 + + ; Compute overall allocation size (base size + align((element size * elements), 4)). + ldrh r2, [r0, #OFFSETOF__MethodTable__m_usComponentSize] + umull r2, r4, r2, r1 + cbnz r4, Array8SizeOverflow + adds r2, #3 + bcs Array8SizeOverflow + bic r2, r2, #3 + ldr r4, [r0, #OFFSETOF__MethodTable__m_uBaseSize] + adds r2, r4 + bcs Array8SizeOverflow + + ; Preserve the MethodTable in r5. + mov r5, r0 + + mov r2, r1 ; numElements + mov r1, #GC_ALLOC_ALIGN8 ; uFlags + + ; void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + blx RhpGcAlloc + + ; Test for failure (NULL return). + cbz r0, Array8OutOfMemory + + POP_COOP_PINVOKE_FRAME + EPILOG_RETURN + +Array8SizeOverflow + ; We get here if the size of the final array object can't be represented as an unsigned + ; 32-bit value. We're going to tail-call to a managed helper that will throw + ; an OOM or overflow exception that the caller of this allocator understands. + + ; if the element count is non-negative, it's an OOM error + cmp r1, #0 + bge Array8OutOfMemory1 + + ; r0 holds MethodTable pointer already + mov r1, #1 ;; Indicate that we should throw OverflowException + + POP_COOP_PINVOKE_FRAME + EPILOG_BRANCH RhExceptionHandling_FailedAllocation + +Array8OutOfMemory + ; This is the OOM failure path. We're going to tail-call to a managed helper that will throw + ; an out of memory exception that the caller of this allocator understands. + + mov r0, r5 ;; MethodTable pointer +Array8OutOfMemory1 + mov r1, #0 ;; Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + EPILOG_BRANCH RhExceptionHandling_FailedAllocation + + NESTED_END RhpNewArrayAlign8 + + END diff --git a/src/coreclr/nativeaot/Runtime/arm/AsmMacros.h b/src/coreclr/nativeaot/Runtime/arm/AsmMacros.h new file mode 100644 index 00000000000000..c99ee5fd4ba87d --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/AsmMacros.h @@ -0,0 +1,286 @@ +;; Licensed to the.NET Foundation under one or more agreements. +;; The.NET Foundation licenses this file to you under the MIT license. + +;; OS provided macros +#include +;; generated by the build from AsmOffsets.cpp +#include "AsmOffsets.inc" + +;; +;; CONSTANTS -- INTEGER +;; +TSF_Attached equ 0x01 +TSF_SuppressGcStress equ 0x08 +TSF_DoNotTriggerGc equ 0x10 +TSF_SuppressGcStress__OR__TSF_DoNotTriggerGC equ 0x18 + +;; GC type flags +GC_ALLOC_FINALIZE equ 1 +GC_ALLOC_ALIGN8_BIAS equ 4 +GC_ALLOC_ALIGN8 equ 8 + +;; GC minimal sized object. We use this to switch between 4 and 8 byte alignment in the GC heap (see AllocFast.asm). +SIZEOF__MinObject equ 12 + ASSERT (SIZEOF__MinObject :MOD: 8) == 4 + +;; Note: these must match the defs in PInvokeTransitionFrameFlags +PTFF_SAVE_R4 equ 0x00000001 +PTFF_SAVE_R5 equ 0x00000002 +PTFF_SAVE_R6 equ 0x00000004 +PTFF_SAVE_R7 equ 0x00000008 +PTFF_SAVE_R8 equ 0x00000010 +PTFF_SAVE_R9 equ 0x00000020 +PTFF_SAVE_R10 equ 0x00000040 +PTFF_SAVE_ALL_PRESERVED equ 0x00000077 ;; NOTE: FP is not included in this set! +PTFF_SAVE_SP equ 0x00000100 +PTFF_SAVE_R0 equ 0x00000200 ;; R0 is saved if it contains a GC ref and we're in hijack handler +PTFF_SAVE_ALL_SCRATCH equ 0x00003e00 ;; R0-R3,LR (R12 is trashed by the helpers anyway, but LR is relevant for loop hijacking +PTFF_R0_IS_GCREF equ 0x00004000 ;; iff PTFF_SAVE_R0: set -> r0 is Object, clear -> r0 is scalar +PTFF_R0_IS_BYREF equ 0x00008000 ;; iff PTFF_SAVE_R0: set -> r0 is ByRef, clear -> r0 is Object or scalar +PTFF_THREAD_ABORT equ 0x00010000 ;; indicates that ThreadAbortException should be thrown when returning from the transition + +;; These must match the TrapThreadsFlags enum +TrapThreadsFlags_None equ 0 +TrapThreadsFlags_AbortInProgress equ 1 +TrapThreadsFlags_TrapThreads equ 2 + +;; This must match HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT +STATUS_REDHAWK_THREAD_ABORT equ 0x43 + +;; +;; Rename fields of nested structs +;; +OFFSETOF__Thread__m_alloc_context__alloc_ptr equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr +OFFSETOF__Thread__m_alloc_context__alloc_limit equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit + + +__tls_array equ 0x2C ;; offsetof(TEB, ThreadLocalStoragePointer) + +;; +;; MACROS +;; + + GBLS __SECTIONREL_tls_CurrentThread +__SECTIONREL_tls_CurrentThread SETS "SECTIONREL_tls_CurrentThread" + + MACRO + INLINE_GETTHREAD $destReg, $trashReg + EXTERN _tls_index + + ldr $destReg, =_tls_index + ldr $destReg, [$destReg] + mrc p15, 0, $trashReg, c13, c0, 2 + ldr $trashReg, [$trashReg, #__tls_array] + ldr $destReg, [$trashReg, $destReg, lsl #2] + ldr $trashReg, $__SECTIONREL_tls_CurrentThread + add $destReg, $trashReg + MEND + + ;; INLINE_GETTHREAD_CONSTANT_POOL macro has to be used after the last function in the .asm file that used + ;; INLINE_GETTHREAD. Optionally, it can be also used after any function that used INLINE_GETTHREAD + ;; to improve density, or to reduce distance betweeen the constant pool and its use. + MACRO + INLINE_GETTHREAD_CONSTANT_POOL + EXTERN tls_CurrentThread + +$__SECTIONREL_tls_CurrentThread + DCD tls_CurrentThread + RELOC 15 ;; SECREL + +__SECTIONREL_tls_CurrentThread SETS "$__SECTIONREL_tls_CurrentThread":CC:"_" + + MEND + + MACRO + INLINE_THREAD_UNHIJACK $threadReg, $trashReg1, $trashReg2 + ;; + ;; Thread::Unhijack() + ;; + ldr $trashReg1, [$threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + cbz $trashReg1, %ft0 + + ldr $trashReg2, [$threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str $trashReg1, [$trashReg2] + mov $trashReg1, #0 + str $trashReg1, [$threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str $trashReg1, [$threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] +0 + MEND + +DEFAULT_FRAME_SAVE_FLAGS equ PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_SP + +;; +;; Macro used from unmanaged helpers called from managed code where the helper does not transition immediately +;; into pre-emptive mode but may cause a GC and thus requires the stack is crawlable. This is typically the +;; case for helpers that meddle in GC state (e.g. allocation helpers) where the code must remain in +;; cooperative mode since it handles object references and internal GC state directly but a garbage collection +;; may be inevitable. In these cases we need to be able to transition to pre-meptive mode deep within the +;; unmanaged code but still be able to initialize the stack iterator at the first stack frame which may hold +;; interesting GC references. In all our helper cases this corresponds to the most recent managed frame (e.g. +;; the helper's caller). +;; +;; This macro builds a frame describing the current state of managed code. +;; +;; INVARIANTS +;; - The macro assumes it defines the method prolog, it should typically be the first code in a method and +;; certainly appear before any attempt to alter the stack pointer. +;; - This macro uses trashReg (after its initial value has been saved in the frame) and upon exit trashReg +;; will contain the address of transition frame. +;; + MACRO + PUSH_COOP_PINVOKE_FRAME $trashReg + + PROLOG_STACK_ALLOC 4 ; Save space for caller's SP + PROLOG_PUSH {r4-r6,r8-r10} ; Save preserved registers + PROLOG_STACK_ALLOC 8 ; Save space for flags and Thread* + PROLOG_PUSH {r7} ; Save caller's FP + PROLOG_PUSH {r11,lr} ; Save caller's frame-chain pointer and PC + + ; Compute SP value at entry to this method and save it in the last slot of the frame (slot #11). + add $trashReg, sp, #(12 * 4) + str $trashReg, [sp, #(11 * 4)] + + ; Record the bitmask of saved registers in the frame (slot #4). + mov $trashReg, #DEFAULT_FRAME_SAVE_FLAGS + str $trashReg, [sp, #(4 * 4)] + + mov $trashReg, sp + MEND + +;; Pop the frame and restore register state preserved by PUSH_COOP_PINVOKE_FRAME + MACRO + POP_COOP_PINVOKE_FRAME + EPILOG_POP {r11,lr} ; Restore caller's frame-chain pointer and PC (return address) + EPILOG_POP {r7} ; Restore caller's FP + EPILOG_STACK_FREE 8 ; Discard flags and Thread* + EPILOG_POP {r4-r6,r8-r10} ; Restore preserved registers + EPILOG_STACK_FREE 4 ; Discard caller's SP + MEND + + +; Macro used to assign an alternate name to a symbol containing characters normally disallowed in a symbol +; name (e.g. C++ decorated names). + MACRO + SETALIAS $name, $symbol + GBLS $name +$name SETS "|$symbol|" + MEND + + + ; + ; Helper macro: create a global label for the given name, + ; decorate it, and export it for external consumption. + ; + + MACRO + __ExportLabelName $FuncName + + LCLS Name +Name SETS "|$FuncName|" + EXPORT $Name +$Name + MEND + + ; + ; Macro for indicating an alternate entry point into a function. + ; + + MACRO + LABELED_RETURN_ADDRESS $ReturnAddressName + + ; export the return address name, but do not perturb the code by forcing alignment + __ExportLabelName $ReturnAddressName + + ; flush any pending literal pool stuff + ROUT + + MEND + + MACRO + EXPORT_POINTER_TO_ADDRESS $Name + +1 + + AREA |.rdata|, ALIGN=4, DATA, READONLY + +$Name + + DCD %BT1 + + EXPORT $Name + + TEXTAREA + + ROUT + + MEND + +;----------------------------------------------------------------------------- +; Macro used to check (in debug builds only) whether the stack is 64-bit aligned (a requirement before calling +; out into C++/OS code). Invoke this directly after your prolog (if the stack frame size is fixed) or directly +; before a call (if you have a frame pointer and a dynamic stack). A breakpoint will be invoked if the stack +; is misaligned. +; + MACRO + CHECK_STACK_ALIGNMENT + +#ifdef _DEBUG + push {r0} + add r0, sp, #4 + tst r0, #7 + pop {r0} + beq %F0 + EMIT_BREAKPOINT +0 +#endif + MEND + +;; Loads a 32bit constant into destination register + MACRO + MOV32 $destReg, $constant + + movw $destReg, #(($constant) & 0xFFFF) + movt $destReg, #(($constant) >> 16) + MEND + +;; +;; CONSTANTS -- SYMBOLS +;; + + SETALIAS G_LOWEST_ADDRESS, g_lowest_address + SETALIAS G_HIGHEST_ADDRESS, g_highest_address + SETALIAS G_EPHEMERAL_LOW, g_ephemeral_low + SETALIAS G_EPHEMERAL_HIGH, g_ephemeral_high + SETALIAS G_CARD_TABLE, g_card_table + SETALIAS G_FREE_OBJECT_EETYPE, ?g_pFreeObjectEEType@@3PAVEEType@@A +#ifdef FEATURE_GC_STRESS + SETALIAS THREAD__HIJACKFORGCSTRESS, ?HijackForGcStress@Thread@@SAXPAUPAL_LIMITED_CONTEXT@@@Z + SETALIAS REDHAWKGCINTERFACE__STRESSGC, ?StressGc@RedhawkGCInterface@@SAXXZ +#endif ;; FEATURE_GC_STRESS +;; +;; IMPORTS +;; + EXTERN RhpGcAlloc + EXTERN RhDebugBreak + EXTERN RhpWaitForSuspend2 + EXTERN RhpWaitForGC2 + EXTERN RhpReversePInvokeAttachOrTrapThread2 + EXTERN RhExceptionHandling_FailedAllocation + + + EXTERN $G_LOWEST_ADDRESS + EXTERN $G_HIGHEST_ADDRESS + EXTERN $G_EPHEMERAL_LOW + EXTERN $G_EPHEMERAL_HIGH + EXTERN $G_CARD_TABLE + EXTERN RhpTrapThreads + EXTERN $G_FREE_OBJECT_EETYPE + + EXTERN RhThrowHwEx + EXTERN RhThrowEx + EXTERN RhRethrow + +#ifdef FEATURE_GC_STRESS + EXTERN $REDHAWKGCINTERFACE__STRESSGC + EXTERN $THREAD__HIJACKFORGCSTRESS +#endif ;; FEATURE_GC_STRESS diff --git a/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h new file mode 100644 index 00000000000000..a8b3b9465a9f71 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This file is used by AsmOffsets.h to validate that our +// assembly-code offsets always match their C++ counterparts. +// +// NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix + +PLAT_ASM_SIZEOF(138, ExInfo) +PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) +PLAT_ASM_OFFSET(4, ExInfo, m_pExContext) +PLAT_ASM_OFFSET(8, ExInfo, m_exception) +PLAT_ASM_OFFSET(0c, ExInfo, m_kind) +PLAT_ASM_OFFSET(0d, ExInfo, m_passNumber) +PLAT_ASM_OFFSET(10, ExInfo, m_idxCurClause) +PLAT_ASM_OFFSET(18, ExInfo, m_frameIter) +PLAT_ASM_OFFSET(130, ExInfo, m_notifyDebuggerSP) + +PLAT_ASM_OFFSET(4, PInvokeTransitionFrame, m_RIP) +PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) +PLAT_ASM_OFFSET(0c, PInvokeTransitionFrame, m_pThread) +PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_Flags) +PLAT_ASM_OFFSET(14, PInvokeTransitionFrame, m_PreservedRegs) + +PLAT_ASM_SIZEOF(118, StackFrameIterator) +PLAT_ASM_OFFSET(08, StackFrameIterator, m_FramePointer) +PLAT_ASM_OFFSET(0c, StackFrameIterator, m_ControlPC) +PLAT_ASM_OFFSET(10, StackFrameIterator, m_RegDisplay) +PLAT_ASM_OFFSET(114, StackFrameIterator, m_OriginalControlPC) + +PLAT_ASM_SIZEOF(70, PAL_LIMITED_CONTEXT) +PLAT_ASM_OFFSET(24, PAL_LIMITED_CONTEXT, IP) + +PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, R0) +PLAT_ASM_OFFSET(4, PAL_LIMITED_CONTEXT, R4) +PLAT_ASM_OFFSET(8, PAL_LIMITED_CONTEXT, R5) +PLAT_ASM_OFFSET(0c, PAL_LIMITED_CONTEXT, R6) +PLAT_ASM_OFFSET(10, PAL_LIMITED_CONTEXT, R7) +PLAT_ASM_OFFSET(14, PAL_LIMITED_CONTEXT, R8) +PLAT_ASM_OFFSET(18, PAL_LIMITED_CONTEXT, R9) +PLAT_ASM_OFFSET(1c, PAL_LIMITED_CONTEXT, R10) +PLAT_ASM_OFFSET(20, PAL_LIMITED_CONTEXT, R11) +PLAT_ASM_OFFSET(28, PAL_LIMITED_CONTEXT, SP) +PLAT_ASM_OFFSET(2c, PAL_LIMITED_CONTEXT, LR) + +PLAT_ASM_SIZEOF(88, REGDISPLAY) +PLAT_ASM_OFFSET(38, REGDISPLAY, SP) + +PLAT_ASM_OFFSET(10, REGDISPLAY, pR4) +PLAT_ASM_OFFSET(14, REGDISPLAY, pR5) +PLAT_ASM_OFFSET(18, REGDISPLAY, pR6) +PLAT_ASM_OFFSET(1c, REGDISPLAY, pR7) +PLAT_ASM_OFFSET(20, REGDISPLAY, pR8) +PLAT_ASM_OFFSET(24, REGDISPLAY, pR9) +PLAT_ASM_OFFSET(28, REGDISPLAY, pR10) +PLAT_ASM_OFFSET(2c, REGDISPLAY, pR11) +PLAT_ASM_OFFSET(48, REGDISPLAY, D) diff --git a/src/coreclr/nativeaot/Runtime/arm/CallDescrWorker.S b/src/coreclr/nativeaot/Runtime/arm/CallDescrWorker.S new file mode 100644 index 00000000000000..53184d9b28b2a3 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/CallDescrWorker.S @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp +#include + +// TODO: Implement Arm support + +NESTED_ENTRY RhCallDescrWorker, _TEXT, NoHandler +LOCAL_LABEL(ReturnFromCallDescrThunk): + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFromCallDescrThunk + + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +NESTED_END RhCallDescrWorker, _TEXT + diff --git a/src/coreclr/nativeaot/Runtime/arm/CallDescrWorker.asm b/src/coreclr/nativeaot/Runtime/arm/CallDescrWorker.asm new file mode 100644 index 00000000000000..7ee66320cb3325 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/CallDescrWorker.asm @@ -0,0 +1,128 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + +;;----------------------------------------------------------------------------- +;; This helper routine enregisters the appropriate arguments and makes the +;; actual call. +;;----------------------------------------------------------------------------- +;;void RhCallDescrWorker(CallDescrData * pCallDescrData); + NESTED_ENTRY RhCallDescrWorker + PROLOG_PUSH {r4,r5,r7,lr} + PROLOG_STACK_SAVE r7 + + mov r5,r0 ; save pCallDescrData in r5 + + ldr r1, [r5,#OFFSETOF__CallDescrData__numStackSlots] + cbz r1, Ldonestack + + ;; Add frame padding to ensure frame size is a multiple of 8 (a requirement of the OS ABI). + ;; We push four registers (above) and numStackSlots arguments (below). If this comes to an odd number + ;; of slots we must pad with another. This simplifies to "if the low bit of numStackSlots is set, + ;; extend the stack another four bytes". + lsls r2, r1, #2 + and r3, r2, #4 + sub sp, sp, r3 + + ;; This loop copies numStackSlots words + ;; from [pSrcEnd-4,pSrcEnd-8,...] to [sp-4,sp-8,...] + ldr r0, [r5,#OFFSETOF__CallDescrData__pSrc] + add r0,r0,r2 +Lstackloop + ldr r2, [r0,#-4]! + str r2, [sp,#-4]! + subs r1, r1, #1 + bne Lstackloop +Ldonestack + + ;; If FP arguments are supplied in registers (r3 != NULL) then initialize all of them from the pointer + ;; given in r3. Do not use "it" since it faults in floating point even when the instruction is not executed. + ldr r3, [r5,#OFFSETOF__CallDescrData__pFloatArgumentRegisters] + cbz r3, LNoFloatingPoint + vldm r3, {s0-s15} +LNoFloatingPoint + + ;; Copy [pArgumentRegisters, ..., pArgumentRegisters + 12] + ;; into r0, ..., r3 + + ldr r4, [r5,#OFFSETOF__CallDescrData__pArgumentRegisters] + ldm r4, {r0-r3} + + CHECK_STACK_ALIGNMENT + + ;; call pTarget + ;; Note that remoting expect target in r4. + ldr r4, [r5,#OFFSETOF__CallDescrData__pTarget] + blx r4 + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFromCallDescrThunk + + ;; Symbol used to identify thunk call to managed function so the special + ;; case unwinder can unwind through this function. Sadly we cannot directly + ;; export this symbol right now because it confuses DIA unwinder to believe + ;; it's the beginning of a new method, therefore we export the address + ;; of an auxiliary variable holding the address instead. + + ldr r3, [r5,#OFFSETOF__CallDescrData__fpReturnSize] + + ;; Save FP return value if appropriate + cbz r3, LFloatingPointReturnDone + + ;; Float return case + ;; Do not use "it" since it faults in floating point even when the instruction is not executed. + cmp r3, #4 + bne LNoFloatReturn + vmov r0, s0 + b LFloatingPointReturnDone +LNoFloatReturn + + ;; Double return case + ;; Do not use "it" since it faults in floating point even when the instruction is not executed. + cmp r3, #8 + bne LNoDoubleReturn + vmov r0, r1, s0, s1 + b LFloatingPointReturnDone +LNoDoubleReturn +; Unlike desktop returnValue is a pointer to a return buffer, not the buffer itself + ldr r2, [r5, #OFFSETOF__CallDescrData__pReturnBuffer] + + cmp r3, #16 + bne LNoFloatHFAReturn + vstm r2, {s0-s3} + b LReturnDone +LNoFloatHFAReturn + + cmp r3, #32 + bne LNoDoubleHFAReturn + vstm r2, {d0-d3} + b LReturnDone +LNoDoubleHFAReturn + + EMIT_BREAKPOINT ; Unreachable + +LFloatingPointReturnDone + +; Unlike desktop returnValue is a pointer to a return buffer, not the buffer itself + ldr r5, [r5, #OFFSETOF__CallDescrData__pReturnBuffer] + + ;; Save return value into retbuf + str r0, [r5, #(0)] + str r1, [r5, #(4)] + +LReturnDone + +#ifdef _DEBUG + ;; trash the floating point registers to ensure that the HFA return values + ;; won't survive by accident + vldm sp, {d0-d3} +#endif + + EPILOG_STACK_RESTORE r7 + EPILOG_POP {r4,r5,r7,pc} + + NESTED_END RhCallDescrWorker + + END diff --git a/src/coreclr/nativeaot/Runtime/arm/CallingConventionConverterHelpers.S b/src/coreclr/nativeaot/Runtime/arm/CallingConventionConverterHelpers.S new file mode 100644 index 00000000000000..3e216602b0c6b5 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/CallingConventionConverterHelpers.S @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// TODO: Implement Arm support +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp +#include + +// +// void CallingConventionConverter_ReturnVoidReturnThunk() +// +LEAF_ENTRY CallingConventionConverter_ReturnVoidReturnThunk, _TEXT + bx lr +LEAF_END CallingConventionConverter_ReturnVoidReturnThunk, _TEXT + +// +// int CallingConventionConverter_ReturnIntegerReturnThunk(int) +// +LEAF_ENTRY CallingConventionConverter_ReturnIntegerReturnThunk, _TEXT + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +LEAF_END CallingConventionConverter_ReturnIntegerReturnThunk, _TEXT + +// +// __jmpstub__CallingConventionConverter_CommonCallingStub +// +// struct CallingConventionConverter_CommonCallingStub_PointerData +// { +// void *ManagedCallConverterThunk; +// void *UniversalThunk; +// } +// +// struct CommonCallingStubInputData +// { +// ULONG_PTR CallingConventionId; +// CallingConventionConverter_CommonCallingStub_PointerData *commonData; // Only the ManagedCallConverterThunk field is used +// // However, it is specified just like other platforms, so the behavior of the common +// // calling stub is easier to debug +// } +// +// sp-4 - Points at CommonCallingStubInputData +// +// +LEAF_ENTRY __jmpstub__CallingConventionConverter_CommonCallingStub + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +LEAF_END __jmpstub__CallingConventionConverter_CommonCallingStub + +// +// void CallingConventionConverter_GetStubs(IntPtr *returnVoidStub, IntPtr *returnIntegerStub, IntPtr *commonCallingStub) +// +LEAF_ENTRY CallingConventionConverter_GetStubs, _TEXT + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +LEAF_END CallingConventionConverter_GetStubs, _TEXT + +// +// void CallingConventionConverter_SpecifyCommonStubData(CallingConventionConverter_CommonCallingStub_PointerData *commonData); +// +LEAF_ENTRY CallingConventionConverter_SpecifyCommonStubData + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +LEAF_END CallingConventionConverter_SpecifyCommonStubData diff --git a/src/coreclr/nativeaot/Runtime/arm/CallingConventionConverterHelpers.asm b/src/coreclr/nativeaot/Runtime/arm/CallingConventionConverterHelpers.asm new file mode 100644 index 00000000000000..6a65987b7cef59 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/CallingConventionConverterHelpers.asm @@ -0,0 +1,88 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "kxarm.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + DATAAREA +UniversalThunkPointer % 4 + TEXTAREA + +OFFSETOF_CallingConventionId EQU 0 +OFFSETOF_commonData EQU 4 +OFFSETOF_ManagedCallConverterThunk EQU 0 +OFFSETOF_UniversalThunk EQU 4 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CallingConventionCoverter Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; +;; Note: The "__jmpstub__" prefix is used to indicate to debugger +;; that it must step-through this stub when it encounters it while +;; stepping. +;; + + ;; + ;; void CallingConventionConverter_ReturnThunk() + ;; + LEAF_ENTRY CallingConventionConverter_ReturnThunk + bx lr + LEAF_END CallingConventionConverter_ReturnThunk + + ;; + ;; __jmpstub__CallingConventionConverter_CommonCallingStub + ;; + ;; struct CallingConventionConverter_CommonCallingStub_PointerData + ;; { + ;; void *ManagedCallConverterThunk; + ;; void *UniversalThunk; + ;; } + ;; + ;; struct CommonCallingStubInputData + ;; { + ;; ULONG_PTR CallingConventionId; + ;; CallingConventionConverter_CommonCallingStub_PointerData *commonData; // Only the ManagedCallConverterThunk field is used + ;; // However, it is specified just like other platforms, so the behavior of the common + ;; // calling stub is easier to debug + ;; } + ;; + ;; sp-4 - Points at CommonCallingStubInputData + ;; + ;; + LEAF_ENTRY __jmpstub__CallingConventionConverter_CommonCallingStub + ldr r12, [sp, #-4] + ldr r12, [r12, #OFFSETOF_CallingConventionId] ; Get CallingConventionId into r12 + str r12, [sp, #-8] ; Put calling convention id into red zone + ldr r12, [sp, #-4] + ldr r12, [r12, #OFFSETOF_commonData] ; Get pointer to common data + ldr r12, [r12, #OFFSETOF_ManagedCallConverterThunk] ; Get pointer to managed call converter thunk + str r12, [sp, #-4] ; Put managed calling convention thunk pointer into red zone (overwrites pointer to CommonCallingStubInputData) + ldr r12, =UniversalThunkPointer + ldr r12, [r12] + bx r12 + LEAF_END __jmpstub__CallingConventionConverter_CommonCallingStub + + ;; + ;; void CallingConventionConverter_SpecifyCommonStubData(CallingConventionConverter_CommonCallingStub_PointerData *commonData); + ;; + LEAF_ENTRY CallingConventionConverter_SpecifyCommonStubData + ldr r1, [r0, #OFFSETOF_ManagedCallConverterThunk] ; Load ManagedCallConverterThunk into r1 {r1 = (CallingConventionConverter_CommonCallingStub_PointerData*)r0->ManagedCallConverterThunk } + ldr r2, [r0, #OFFSETOF_UniversalThunk] ; Load UniversalThunk into r2 {r2 = (CallingConventionConverter_CommonCallingStub_PointerData*)r0->UniversalThunk } + ldr r12, =UniversalThunkPointer + str r2, [r12] + bx lr + LEAF_END CallingConventionConverter_SpecifyCommonStubData + + ;; + ;; void CallingConventionConverter_GetStubs(IntPtr *returnVoidStub, IntPtr *returnIntegerStub, IntPtr *commonCallingStub) + ;; + LEAF_ENTRY CallingConventionConverter_GetStubs + ldr r12, =CallingConventionConverter_ReturnThunk + str r12, [r0] ;; ARM doesn't need different return thunks. + str r12, [r1] + ldr r12, =__jmpstub__CallingConventionConverter_CommonCallingStub + str r12, [r2] + bx lr + LEAF_END CallingConventionConverter_GetStubs + + END diff --git a/src/coreclr/nativeaot/Runtime/arm/Dummies.asm b/src/coreclr/nativeaot/Runtime/arm/Dummies.asm new file mode 100644 index 00000000000000..ea6c21fc810d09 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/Dummies.asm @@ -0,0 +1,18 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + + LEAF_ENTRY RhpLMod + DCW 0xdefe + bx lr + LEAF_END RhpLMod + + LEAF_ENTRY RhpLMul + DCW 0xdefe + bx lr + LEAF_END RhpLMul + + END diff --git a/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.S new file mode 100644 index 00000000000000..82a1d89d8df174 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.S @@ -0,0 +1,500 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp +#include + +#define STACKSIZEOF_ExInfo ((SIZEOF__ExInfo + 7)&(~7)) + +#define rsp_offsetof_ExInfo 0 +#define rsp_offsetof_Context STACKSIZEOF_ExInfo + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpThrowHwEx +// +// INPUT: R0: exception code of fault +// R1: faulting RIP +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpThrowHwEx, _TEXT, NoHandler + + mov r2, r0 // save exception code into r2 + mov r0, sp // get SP of fault site + + mov lr, r1 // set IP of fault site + + // Setup a PAL_LIMITED_CONTEXT on the stack { + PROLOG_VPUSH {d8-d15} + PROLOG_PUSH "{r0,lr}" // push {sp, pc} of fault site + PROLOG_PUSH "{r0,r4-r11,lr}" + // } end PAL_LIMITED_CONTEXT + + PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo + + // r0: SP of fault site + // r1: IP of fault site + // r2: exception code of fault + // lr: IP of fault site (as a 'return address') + mov r4, r2 // save exception code of fault + + // r0 = GetThread() + INLINE_GETTHREAD + + // r1 <- ExInfo* + add r1, sp, #rsp_offsetof_ExInfo + mov r3, #0 + str r3, [r1, #OFFSETOF__ExInfo__m_exception] // pExInfo->m_exception = null + mov r3, #1 + strb r3, [r1, #OFFSETOF__ExInfo__m_passNumber] // pExInfo->m_passNumber = 1 + mov r3, #0xFFFFFFFF + str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] // pExInfo->m_idxCurClause = MaxTryRegionIdx + mov r3, #2 + strb r3, [r1, #OFFSETOF__ExInfo__m_kind] // pExInfo->m_kind = ExKind.HardwareFault + + // link the ExInfo into the thread's ExInfo chain + ldr r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] + str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str r1, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo + + mov r0, r4 // restore the exception code + // r0 contains the exception code + // r1 contains the address of the ExInfo + bl C_FUNC(RhThrowHwEx) + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowHwEx2 + + // no return + EMIT_BREAKPOINT + +NESTED_END RhpThrowHwEx + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpThrowEx +// +// INPUT: R0: exception object +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler + + // Setup a PAL_LIMITED_CONTEXT on the stack { + PROLOG_VPUSH {d8-d15} + PROLOG_PUSH "{r0,lr}" // Reserve space for SP and store LR + PROLOG_PUSH "{r0,r4-r11,lr}" + // } end PAL_LIMITED_CONTEXT + + PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo + + // Calculate SP at callsite and save into the PAL_LIMITED_CONTEXT + add r4, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) + str r4, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__SP)] + + mov r4, r0 // Save exception object + // r0 = GetThread() + INLINE_GETTHREAD + + add r2, sp, #(rsp_offsetof_Context + SIZEOF__PAL_LIMITED_CONTEXT + 0x8) // r2 <- addr of return address + + // There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return + // address could have been hijacked when we were in that C# code and we must remove the hijack and + // reflect the correct return address in our exception context record. The other throw helpers don't + // need this because they cannot be tail-called from C#. + // NOTE: we cannot use INLINE_THREAD_UNHIJACK because it will write into the stack at the location + // where the tail-calling thread had saved LR, which may not match where we have saved LR. + + ldr r1, [r0, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + cbz r1, LOCAL_LABEL(NotHiJacked) + + ldr r3, [r0, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + + // r4: exception object + // r1: hijacked return address + // r0: pThread + // r3: hijacked return address location + + add r12, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) // re-compute SP at callsite + cmp r3, r12 // if (m_ppvHijackedReturnAddressLocation < SP at callsite) + blo LOCAL_LABEL(TailCallWasHijacked) + + // normal case where a valid return address location is hijacked + str r1, [r3] + b LOCAL_LABEL(ClearThreadState) + +LOCAL_LABEL(TailCallWasHijacked): + + // Abnormal case where the return address location is now invalid because we ended up here via a tail + // call. In this case, our hijacked return address should be the correct caller of this method. + // + + // stick the previous return address in LR as well as in the right spots in our PAL_LIMITED_CONTEXT. + mov lr, r1 + str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] + str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] + +LOCAL_LABEL(ClearThreadState): + + // clear the Thread's hijack state + mov r3, #0 + str r3, [r0, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str r3, [r0, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + +LOCAL_LABEL(NotHiJacked): + + add r1, sp, #rsp_offsetof_ExInfo // r1 <- ExInfo* + mov r3, #0 + str r3, [r1, #OFFSETOF__ExInfo__m_exception] // init the exception object to null + mov r3, #1 + strb r3, [r1, #OFFSETOF__ExInfo__m_passNumber] // init to the first pass + strb r3, [r1, #OFFSETOF__ExInfo__m_kind] + mov r3, #0xFFFFFFFF + str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] // ExKind.Throw + + // link the ExInfo into the thread's ExInfo chain + ldr r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] + str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str r1, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo + + // set the exception context field on the ExInfo + add r3, sp, #rsp_offsetof_Context // r3 <- PAL_LIMITED_CONTEXT* + str r3, [r1, #OFFSETOF__ExInfo__m_pExContext] // init ExInfo.m_pExContext + + mov r0, r4 // Restore exception object + // r0 contains the exception object + // r1 contains the address of the new ExInfo + bl C_FUNC(RhThrowEx) + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowEx2 + + // no return + EMIT_BREAKPOINT + +NESTED_END RhpThrowEx, _TEXT + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// void FASTCALL RhpRethrow() +// +// SUMMARY: Similar to RhpThrowEx, except that it passes along the currently active ExInfo +// +// INPUT: +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpRethrow, _TEXT, NoHandler + + // Setup a PAL_LIMITED_CONTEXT on the stack { + PROLOG_VPUSH {d8-d15} + PROLOG_PUSH "{r0,lr}" // Reserve space for SP and store LR + PROLOG_PUSH "{r0,r4-r11,lr}" + // } end PAL_LIMITED_CONTEXT + + PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo + + // Compute and save SP at callsite. + add r1, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) + str r1, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__SP)] + + // r0 = GetThread(); + INLINE_GETTHREAD + + // r1 <- ExInfo* + add r1, sp, #rsp_offsetof_ExInfo + + mov r3, #0 + str r3, [r1, #OFFSETOF__ExInfo__m_exception] // init the exception object to null + strb r3, [r1, #OFFSETOF__ExInfo__m_kind] // init to a deterministic value (ExKind.None) + mov r3, #1 + strb r3, [r1, #OFFSETOF__ExInfo__m_passNumber] // pExInfo->m_passNumber = 1 + mov r3, #0xFFFFFFFF + str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] + + // link the ExInfo into the thread's ExInfo chain + ldr r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // r3 <- currently active ExInfo + str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str r1, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo + + // set the exception context field on the ExInfo + add r2, sp, #rsp_offsetof_Context // r2 <- PAL_LIMITED_CONTEXT* + str r2, [r1, #OFFSETOF__ExInfo__m_pExContext] // init ExInfo.m_pExContext + + mov r0, r3 + // r0 contains the currently active ExInfo + // r1 contains the address of the new ExInfo + blx C_FUNC(RhRethrow) + + EXPORT_POINTER_TO_ADDRESS PointerToRhpRethrow2 + + // no return + EMIT_BREAKPOINT + +NESTED_END RhpRethrow, _TEXT + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// void* FASTCALL RhpCallCatchFunclet(RtuObjectRef exceptionObj, +// void* pHandlerIP, +// REGDISPLAY* pRegDisplay, +// ExInfo* pExInfo) +// +// INPUT: R0: exception object +// R1: handler funclet address +// R2: REGDISPLAY* +// R3: ExInfo* +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpCallCatchFunclet, _TEXT, NoHandler + + PROLOG_PUSH "{r0,r2-r11,lr}" // r0, r2 & r3 are saved so we have the exception object, + // REGDISPLAY and ExInfo later + PROLOG_VPUSH {d8-d15} + +#define rsp_offset_r2 (8 * 8) + 4 +#define rsp_offset_r3 (8 * 8) + 8 + + mov r4, r0 // Save exception object + mov r5, r1 // Save handler funclet address + mov r6, r2 // Save REGDISPLAY* + + // Clear the DoNotTriggerGc state before calling out to our managed catch funclet, + // trashes r0-r2. + // r0 = GetThread() + INLINE_GETTHREAD + +LOCAL_LABEL(ClearRetry_Catch): + ldrex r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + bics r1, #TSF_DoNotTriggerGc + strex r2, r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + cbz r2, LOCAL_LABEL(ClearSuccess_Catch) + b LOCAL_LABEL(ClearRetry_Catch) +LOCAL_LABEL(ClearSuccess_Catch): + + mov r0, r4 // Reload exception object + mov r3, r5 // Reload handler funclet address + mov r2, r6 // Reload REGDISPLAY pointer + + // + // set preserved regs to the values expected by the funclet + // + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR4] + ldr r4, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR5] + ldr r5, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR6] + ldr r6, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR7] + ldr r7, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR8] + ldr r8, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR9] + ldr r9, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR10] + ldr r10, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR11] + ldr r11, [r12] + + // + // load vfp preserved regs + // + add r12, r2, #OFFSETOF__REGDISPLAY__D + vldm r12!, {d8-d15} + + // r0 <- exception object + blx r3 // call handler funclet + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallCatchFunclet2 + + mov r4, r0 // Save the result + + INLINE_GETTHREAD // r0 <- Thread* + // We must unhijack the thread at this point because the section of stack where the + // hijack is applied may go dead. If it does, then the next time we try to unhijack + // the thread, it will corrupt the stack. + INLINE_THREAD_UNHIJACK r0, r3, r12 // Thread in r0, trashes r3 and r1 + ldr r2, [sp, #rsp_offset_r2] // r2 <- REGDISPLAY* + ldr r3, [sp, #rsp_offset_r3] // r3 <- current ExInfo* + ldr r2, [r2, #OFFSETOF__REGDISPLAY__SP] // r2 <- resume SP value + +LOCAL_LABEL(PopExInfoLoop): + ldr r3, [r3, #OFFSETOF__ExInfo__m_pPrevExInfo] // r3 <- next ExInfo + cbz r3, LOCAL_LABEL(DonePopping) // if (pExInfo == null) { we're done } + cmp r3, r2 + blt LOCAL_LABEL(PopExInfoLoop) // if (pExInfo < resume SP} { keep going } +LOCAL_LABEL(DonePopping): + + str r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // store the new head on the Thread + + // reset RSP and jump to the continuation address + mov sp, r2 + bx r4 + +NESTED_END RhpCallCatchFunclet, _TEXT + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// void FASTCALL RhpCallFinallyFunclet(void* pHandlerIP, REGDISPLAY* pRegDisplay) +// +// INPUT: R0: handler funclet address +// R1: REGDISPLAY* +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpCallFinallyFunclet, _TEXT, NoHandler + + PROLOG_PUSH "{r1,r4-r11,lr}" // r1 is saved so we have the REGDISPLAY later + PROLOG_VPUSH {d8-d15} +#define rsp_offset_r1 8 * 8 + + // + // We want to suppress hijacking between invocations of subsequent finallys. We do + // this because we cannot tolerate a GC after one finally has run (and possibly + // side-effected the GC state of the method) and then been popped off the stack, + // leaving behind no trace of its effect. + // + // So we clear the state before and set it after invocation of the handler. + // + + mov r4, r0 // Save handler funclet address + mov r5, r1 // Save REGDISPLAY* + // + // clear the DoNotTriggerGc flag, trashes r0-r2 + // + INLINE_GETTHREAD // r0 <- Thread* + +LOCAL_LABEL(ClearRetry): + ldrex r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + bics r1, #TSF_DoNotTriggerGc + strex r2, r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + cbz r2, LOCAL_LABEL(ClearSuccess) + b LOCAL_LABEL(ClearRetry) +LOCAL_LABEL(ClearSuccess): + + mov r2, r4 // reload handler funclet address + mov r1, r5 // reload REGDISPLAY pointer + + // + // set preserved regs to the values expected by the funclet + // + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR4] + ldr r4, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR5] + ldr r5, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR6] + ldr r6, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR7] + ldr r7, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR8] + ldr r8, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR9] + ldr r9, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR10] + ldr r10, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR11] + ldr r11, [r12] + + // + // load vfp preserved regs + // + add r12, r1, #OFFSETOF__REGDISPLAY__D + vldm r12!, {d8-d15} + + blx r2 // handler funclet address + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFinallyFunclet2 + + ldr r1, [sp, #rsp_offset_r1] // reload REGDISPLAY pointer + + // + // save new values of preserved regs into REGDISPLAY + // + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR4] + str r4, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR5] + str r5, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR6] + str r6, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR7] + str r7, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR8] + str r8, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR9] + str r9, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR10] + str r10, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR11] + str r11, [r12] + + // + // store vfp preserved regs + // + add r12, r1, #OFFSETOF__REGDISPLAY__D + vstm r12!, {d8-d15} + + // + // set the DoNotTriggerGc flag, trashes r0-r2 + // + INLINE_GETTHREAD // r0 <- Thread* +LOCAL_LABEL(SetRetry): + ldrex r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + orrs r1, #TSF_DoNotTriggerGc + strex r2, r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + cbz r2, LOCAL_LABEL(SetSuccess) + b LOCAL_LABEL(SetRetry) +LOCAL_LABEL(SetSuccess): + + EPILOG_VPOP {d8-d15} + EPILOG_POP "{r1,r4-r11,pc}" + +NESTED_END RhpCallFinallyFunclet, _TEXT + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// void* FASTCALL RhpCallFilterFunclet(RtuObjectRef exceptionObj, void* pFilterIP, REGDISPLAY* pRegDisplay) +// +// INPUT: R0: exception object +// R1: filter funclet address +// R2: REGDISPLAY* +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpCallFilterFunclet, _TEXT, NoHandler + + PROLOG_PUSH "{r2,r4-r11,lr}" + PROLOG_VPUSH {d8-d15} + + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR11] + ldr r11, [r12] + + mov r12, r1 // r12 <- handler funclet address + // r0 still contains the exception object + ldr r1, [r2, #OFFSETOF__REGDISPLAY__SP] // r1 <- establisher frame + + // + // call the funclet + // r0 = exception object + // r1 = establisher frame + blx r12 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2 + + // R0 contains the result of the filter execution + + EPILOG_VPOP {d8-d15} + EPILOG_POP "{r2,r4-r11,pc}" + +NESTED_END RhpCallFilterFunclet, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.asm b/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.asm new file mode 100644 index 00000000000000..36a7ddfd99f472 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.asm @@ -0,0 +1,555 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowHwEx +;; +;; INPUT: R0: exception code of fault +;; R1: faulting IP +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpThrowHwEx + +#define STACKSIZEOF_ExInfo ((SIZEOF__ExInfo + 7)&(~7)) + +#define rsp_offsetof_ExInfo 0 +#define rsp_offsetof_Context STACKSIZEOF_ExInfo + + PROLOG_NOP mov r2, r0 ;; save exception code into r2 + PROLOG_NOP mov r0, sp ;; get SP of fault site + + PROLOG_NOP mov lr, r1 ;; set IP of fault site + + ;; Setup a PAL_LIMITED_CONTEXT on the stack { + PROLOG_NOP vpush {d8-d15} + PROLOG_NOP push {r0,lr} ;; push {sp, pc} of fault site + PROLOG_PUSH_MACHINE_FRAME ;; unwind code only + PROLOG_PUSH {r0,r4-r11,lr} + ;; } end PAL_LIMITED_CONTEXT + + PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo + + ; r0: SP of fault site + ; r1: IP of fault site + ; r2: exception code of fault + ; lr: IP of fault site (as a 'return address') + + mov r0, r2 ;; r0 <- exception code of fault + + ;; r2 = GetThread(), TRASHES r1 + INLINE_GETTHREAD r2, r1 + + add r1, sp, #rsp_offsetof_ExInfo ;; r1 <- ExInfo* + mov r3, #0 + str r3, [r1, #OFFSETOF__ExInfo__m_exception] ;; pExInfo->m_exception = null + mov r3, #1 + strb r3, [r1, #OFFSETOF__ExInfo__m_passNumber] ;; pExInfo->m_passNumber = 1 + mov r3, #0xFFFFFFFF + str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] ;; pExInfo->m_idxCurClause = MaxTryRegionIdx + mov r3, #2 + strb r3, [r1, #OFFSETOF__ExInfo__m_kind] ;; pExInfo->m_kind = ExKind.HardwareFault + + + ;; link the ExInfo into the thread's ExInfo chain + ldr r3, [r2, #OFFSETOF__Thread__m_pExInfoStackHead] + str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str r1, [r2, #OFFSETOF__Thread__m_pExInfoStackHead] ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + add r2, sp, #rsp_offsetof_Context ;; r2 <- PAL_LIMITED_CONTEXT* + str r2, [r1, #OFFSETOF__ExInfo__m_pExContext] ;; pExInfo->m_pExContext = pContext + + ;; r0: exception code + ;; r1: ExInfo* + bl RhThrowHwEx + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowHwEx2 + + ;; no return + __debugbreak + + NESTED_END RhpThrowHwEx + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowEx +;; +;; INPUT: R0: exception object +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpThrowEx + + ;; Setup a PAL_LIMITED_CONTEXT on the stack { + PROLOG_VPUSH {d8-d15} + PROLOG_PUSH {r0,lr} ;; Reserve space for SP and store LR + PROLOG_PUSH {r0,r4-r11,lr} + ;; } end PAL_LIMITED_CONTEXT + + PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo + + ;; Compute and save SP at callsite. + add r1, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) + str r1, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__SP)] + + ;; r2 = GetThread(), TRASHES r1 + INLINE_GETTHREAD r2, r1 + + ;; There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return + ;; address could have been hijacked when we were in that C# code and we must remove the hijack and + ;; reflect the correct return address in our exception context record. The other throw helpers don't + ;; need this because they cannot be tail-called from C#. + + ;; NOTE: we cannot use INLINE_THREAD_UNHIJACK because it will write into the stack at the location + ;; where the tail-calling thread had saved LR, which may not match where we have saved LR. + + ldr r1, [r2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + cbz r1, NotHijacked + + ldr r3, [r2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + + ;; r0: exception object + ;; r1: hijacked return address + ;; r2: pThread + ;; r3: hijacked return address location + + add r12, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) ;; re-compute SP at callsite + cmp r3, r12 ;; if (m_ppvHijackedReturnAddressLocation < SP at callsite) + blo TailCallWasHijacked + + ;; normal case where a valid return address location is hijacked + str r1, [r3] + b ClearThreadState + +TailCallWasHijacked + + ;; Abnormal case where the return address location is now invalid because we ended up here via a tail + ;; call. In this case, our hijacked return address should be the correct caller of this method. + ;; + + ;; stick the previous return address in LR as well as in the right spots in our PAL_LIMITED_CONTEXT. + mov lr, r1 + str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] + str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] + +ClearThreadState + + ;; clear the Thread's hijack state + mov r3, #0 + str r3, [r2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str r3, [r2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + +NotHijacked + + add r1, sp, #rsp_offsetof_ExInfo ;; r1 <- ExInfo* + mov r3, #0 + str r3, [r1, #OFFSETOF__ExInfo__m_exception] ;; pExInfo->m_exception = null + mov r3, #1 + strb r3, [r1, #OFFSETOF__ExInfo__m_passNumber] ;; pExInfo->m_passNumber = 1 + mov r3, #0xFFFFFFFF + str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] ;; pExInfo->m_idxCurClause = MaxTryRegionIdx + mov r3, #1 + strb r3, [r1, #OFFSETOF__ExInfo__m_kind] ;; pExInfo->m_kind = ExKind.Throw + + ;; link the ExInfo into the thread's ExInfo chain + ldr r3, [r2, #OFFSETOF__Thread__m_pExInfoStackHead] + str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str r1, [r2, #OFFSETOF__Thread__m_pExInfoStackHead] ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + add r2, sp, #rsp_offsetof_Context ;; r2 <- PAL_LIMITED_CONTEXT* + str r2, [r1, #OFFSETOF__ExInfo__m_pExContext] ;; pExInfo->m_pExContext = pContext + + ;; r0: exception object + ;; r1: ExInfo* + bl RhThrowEx + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowEx2 + + ;; no return + __debugbreak + + NESTED_END RhpThrowEx + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void FASTCALL RhpRethrow() +;; +;; SUMMARY: Similar to RhpThrowEx, except that it passes along the currently active ExInfo +;; +;; INPUT: +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpRethrow + + ;; Setup a PAL_LIMITED_CONTEXT on the stack { + PROLOG_VPUSH {d8-d15} + PROLOG_PUSH {r0,lr} ;; Reserve space for SP and store LR + PROLOG_PUSH {r0,r4-r11,lr} + ;; } end PAL_LIMITED_CONTEXT + + PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo + + ;; Compute and save SP at callsite. + add r1, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) + str r1, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__SP)] + + ;; r2 = GetThread(), TRASHES r1 + INLINE_GETTHREAD r2, r1 + + add r1, sp, #rsp_offsetof_ExInfo ;; r1 <- ExInfo* + mov r3, #0 + str r3, [r1, #OFFSETOF__ExInfo__m_exception] ;; pExInfo->m_exception = null + strb r3, [r1, #OFFSETOF__ExInfo__m_kind] ;; init to a deterministic value (ExKind.None) + mov r3, #1 + strb r3, [r1, #OFFSETOF__ExInfo__m_passNumber] ;; pExInfo->m_passNumber = 1 + mov r3, #0xFFFFFFFF + str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] ;; pExInfo->m_idxCurClause = MaxTryRegionIdx + + ;; link the ExInfo into the thread's ExInfo chain + ldr r3, [r2, #OFFSETOF__Thread__m_pExInfoStackHead] + mov r0, r3 ;; r0 <- current ExInfo + str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str r1, [r2, #OFFSETOF__Thread__m_pExInfoStackHead] ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + add r2, sp, #rsp_offsetof_Context ;; r2 <- PAL_LIMITED_CONTEXT* + str r2, [r1, #OFFSETOF__ExInfo__m_pExContext] ;; pExInfo->m_pExContext = pContext + + ;; r0 contains the currently active ExInfo + ;; r1 contains the address of the new ExInfo + bl RhRethrow + + EXPORT_POINTER_TO_ADDRESS PointerToRhpRethrow2 + + ;; no return + __debugbreak + + NESTED_END RhpRethrow + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* FASTCALL RhpCallCatchFunclet(RtuObjectRef exceptionObj, void* pHandlerIP, REGDISPLAY* pRegDisplay, +;; ExInfo* pExInfo) +;; +;; INPUT: R0: exception object +;; R1: handler funclet address +;; R2: REGDISPLAY* +;; R3: ExInfo* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpCallCatchFunclet + + PROLOG_PUSH {r0,r2-r11,lr} ;; r0, r2 & r3 are saved so we have the exception object, + ;; REGDISPLAY and ExInfo later + PROLOG_VPUSH {d8-d15} + +#define rsp_offset_is_not_handling_thread_abort (8 * 8) + 0 +#define rsp_offset_r2 (8 * 8) + 4 +#define rsp_offset_r3 (8 * 8) + 8 + + ;; + ;; clear the DoNotTriggerGc flag, trashes r4-r6 + ;; + INLINE_GETTHREAD r5, r6 ;; r5 <- Thread*, r6 <- trashed + + ldr r4, [r5, #OFFSETOF__Thread__m_threadAbortException] + sub r4, r0 + str r4, [sp, #rsp_offset_is_not_handling_thread_abort] ;; Non-zero if the exception is not ThreadAbortException + +ClearRetry_Catch + ldrex r4, [r5, #OFFSETOF__Thread__m_ThreadStateFlags] + bic r4, #TSF_DoNotTriggerGc + strex r6, r4, [r5, #OFFSETOF__Thread__m_ThreadStateFlags] + cbz r6, ClearSuccess_Catch + b ClearRetry_Catch +ClearSuccess_Catch + + ;; + ;; set preserved regs to the values expected by the funclet + ;; + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR4] + ldr r4, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR5] + ldr r5, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR6] + ldr r6, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR7] + ldr r7, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR8] + ldr r8, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR9] + ldr r9, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR10] + ldr r10, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR11] + ldr r11, [r12] + +#if 0 // def _DEBUG ;; @TODO: temporarily removed because trashing the frame pointer breaks the debugger + ;; trash the values at the old homes to make sure nobody uses them + movw r3, #0xdeed + movt r3, #0xbaad + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR4] + str r3, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR5] + str r3, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR6] + str r3, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR7] + str r3, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR8] + str r3, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR9] + str r3, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR10] + str r3, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR11] + str r3, [r12] +#endif // _DEBUG + + ;; + ;; load vfp preserved regs + ;; + add r12, r2, #OFFSETOF__REGDISPLAY__D + vldm r12!, {d8-d15} + + ;; + ;; call the funclet + ;; + ;; r0 still contains the exception object + blx r1 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallCatchFunclet2 + + ;; r0 contains resume IP + + ldr r2, [sp, #rsp_offset_r2] ;; r2 <- REGDISPLAY* + +;; @TODO: add debug-only validation code for ExInfo pop + + INLINE_GETTHREAD r1, r3 ;; r1 <- Thread*, r3 <- trashed + + ;; We must unhijack the thread at this point because the section of stack where the hijack is applied + ;; may go dead. If it does, then the next time we try to unhijack the thread, it will corrupt the stack. + INLINE_THREAD_UNHIJACK r1, r3, r12 ;; Thread in r1, trashes r3 and r12 + + ldr r3, [sp, #rsp_offset_r3] ;; r3 <- current ExInfo* + ldr r2, [r2, #OFFSETOF__REGDISPLAY__SP] ;; r2 <- resume SP value + +PopExInfoLoop + ldr r3, [r3, #OFFSETOF__ExInfo__m_pPrevExInfo] ;; r3 <- next ExInfo + cbz r3, DonePopping ;; if (pExInfo == null) { we're done } + cmp r3, r2 + blt PopExInfoLoop ;; if (pExInfo < resume SP} { keep going } + +DonePopping + str r3, [r1, #OFFSETOF__Thread__m_pExInfoStackHead] ;; store the new head on the Thread + + ldr r3, =RhpTrapThreads + ldr r3, [r3] + tst r3, #TrapThreadsFlags_AbortInProgress + beq NoAbort + + ldr r3, [sp, #rsp_offset_is_not_handling_thread_abort] + cmp r3, #0 + bne NoAbort + + ;; It was the ThreadAbortException, so rethrow it + ;; reset SP + mov r1, r0 ;; r1 <- continuation address as exception PC + mov r0, #STATUS_REDHAWK_THREAD_ABORT + mov sp, r2 + b RhpThrowHwEx + +NoAbort + ;; reset SP and jump to continuation address + mov sp, r2 + bx r0 + + NESTED_END RhpCallCatchFunclet + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void FASTCALL RhpCallFinallyFunclet(void* pHandlerIP, REGDISPLAY* pRegDisplay) +;; +;; INPUT: R0: handler funclet address +;; R1: REGDISPLAY* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpCallFinallyFunclet + + PROLOG_PUSH {r1,r4-r11,lr} ;; r1 is saved so we have the REGDISPLAY later + PROLOG_VPUSH {d8-d15} +#define rsp_offset_r1 8 * 8 + + ;; + ;; We want to suppress hijacking between invocations of subsequent finallys. We do this because we + ;; cannot tolerate a GC after one finally has run (and possibly side-effected the GC state of the + ;; method) and then been popped off the stack, leaving behind no trace of its effect. + ;; + ;; So we clear the state before and set it after invocation of the handler. + ;; + + ;; + ;; clear the DoNotTriggerGc flag, trashes r1-r3 + ;; + INLINE_GETTHREAD r2, r3 ;; r2 <- Thread*, r3 <- trashed +ClearRetry + ldrex r1, [r2, #OFFSETOF__Thread__m_ThreadStateFlags] + bic r1, #TSF_DoNotTriggerGc + strex r3, r1, [r2, #OFFSETOF__Thread__m_ThreadStateFlags] + cbz r3, ClearSuccess + b ClearRetry +ClearSuccess + + ldr r1, [sp, #rsp_offset_r1] ;; reload REGDISPLAY pointer + + ;; + ;; set preserved regs to the values expected by the funclet + ;; + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR4] + ldr r4, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR5] + ldr r5, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR6] + ldr r6, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR7] + ldr r7, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR8] + ldr r8, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR9] + ldr r9, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR10] + ldr r10, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR11] + ldr r11, [r12] + +#if 0 // def _DEBUG ;; @TODO: temporarily removed because trashing the frame pointer breaks the debugger + ;; trash the values at the old homes to make sure nobody uses them + movw r3, #0xdeed + movt r3, #0xbaad + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR4] + str r3, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR5] + str r3, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR6] + str r3, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR7] + str r3, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR8] + str r3, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR9] + str r3, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR10] + str r3, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR11] + str r3, [r12] +#endif // _DEBUG + + ;; + ;; load vfp preserved regs + ;; + add r12, r1, #OFFSETOF__REGDISPLAY__D + vldm r12!, {d8-d15} + + ;; + ;; call the funclet + ;; + blx r0 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFinallyFunclet2 + + ldr r1, [sp, #rsp_offset_r1] ;; reload REGDISPLAY pointer + + ;; + ;; save new values of preserved regs into REGDISPLAY + ;; + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR4] + str r4, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR5] + str r5, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR6] + str r6, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR7] + str r7, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR8] + str r8, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR9] + str r9, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR10] + str r10, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR11] + str r11, [r12] + + ;; + ;; store vfp preserved regs + ;; + add r12, r1, #OFFSETOF__REGDISPLAY__D + vstm r12!, {d8-d15} + + ;; + ;; set the DoNotTriggerGc flag, trashes r1-r3 + ;; + INLINE_GETTHREAD r2, r3 ;; r2 <- Thread*, r3 <- trashed +SetRetry + ldrex r1, [r2, #OFFSETOF__Thread__m_ThreadStateFlags] + orr r1, #TSF_DoNotTriggerGc + strex r3, r1, [r2, #OFFSETOF__Thread__m_ThreadStateFlags] + cbz r3, SetSuccess + b SetRetry +SetSuccess + + EPILOG_VPOP {d8-d15} + EPILOG_POP {r1,r4-r11,pc} + + NESTED_END RhpCallFinallyFunclet + + INLINE_GETTHREAD_CONSTANT_POOL + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* FASTCALL RhpCallFilterFunclet(RtuObjectRef exceptionObj, void* pFilterIP, REGDISPLAY* pRegDisplay) +;; +;; INPUT: R0: exception object +;; R1: filter funclet address +;; R2: REGDISPLAY* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpCallFilterFunclet + + PROLOG_PUSH {r2,r4-r11,lr} + PROLOG_VPUSH {d8-d15} + + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR7] + ldr r7, [r12] + + ;; + ;; call the funclet + ;; + ;; r0 still contains the exception object + blx r1 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2 + + EPILOG_VPOP {d8-d15} + EPILOG_POP {r2,r4-r11,pc} + + NESTED_END RhpCallFilterFunclet + + end diff --git a/src/coreclr/nativeaot/Runtime/arm/FloatingPoint.asm b/src/coreclr/nativeaot/Runtime/arm/FloatingPoint.asm new file mode 100644 index 00000000000000..9d872fecb6576b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/FloatingPoint.asm @@ -0,0 +1,38 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + + IMPORT fmod + + NESTED_ENTRY RhpFltRemRev + + PROLOG_PUSH {r4,lr} ; Save return address (and r4 for stack alignment) + + ;; The CRT only exports the double form of fmod, so we need to convert our input registers (s0, s1) to + ;; doubles (d0, d1). Unfortunately these registers overlap (d0 == s0/s1) so we need to move our inputs + ;; elsewhere first. In this case we can move them into s4/s5, which are also volatile and don't need + ;; to be preserved. + vmov.f32 s4, s0 + vmov.f32 s5, s1 + + ;; Convert s4 and s5 into d0 and d1. + vcvt.f64.f32 d0, s4 + vcvt.f64.f32 d1, s5 + + ;; Call the CRT's fmod to calculate the remainder into d0. + ldr r12, =fmod + blx r12 + + ;; Convert double result back to single. As far as I can see it's legal to do this directly even + ;; though d0 overlaps s0. + vcvt.f32.f64 s0, d0 + + EPILOG_POP {r4,lr} + EPILOG_RETURN + + NESTED_END RhpFltRemRev + + end diff --git a/src/coreclr/nativeaot/Runtime/arm/GcProbe.S b/src/coreclr/nativeaot/Runtime/arm/GcProbe.S new file mode 100644 index 00000000000000..c9ce1c6adaf443 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/GcProbe.S @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include "AsmOffsets.inc" + + .global RhpGcPoll2 + + LEAF_ENTRY RhpGcPoll + ldr r0, =RhpTrapThreads + ldr r0, [r0] + cmp r0, #TrapThreadsFlags_None + bne RhpGcPollRare + bx lr + LEAF_END RhpGcPoll + + NESTED_ENTRY RhpGcPollRare, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME r0 + bl RhpGcPoll2 + POP_COOP_PINVOKE_FRAME + bx lr + NESTED_END RhpGcPollRare diff --git a/src/coreclr/nativeaot/Runtime/arm/GcProbe.asm b/src/coreclr/nativeaot/Runtime/arm/GcProbe.asm new file mode 100644 index 00000000000000..82976b92b90d7c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/GcProbe.asm @@ -0,0 +1,613 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + + ;; ARM64TODO: do same fix here as on Arm64? + SETALIAS g_fGcStressStarted, ?g_GCShadow@@3PAEA + + EXTERN $g_fGcStressStarted + +PROBE_SAVE_FLAGS_EVERYTHING equ DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_ALL_SCRATCH +PROBE_SAVE_FLAGS_R0_IS_GCREF equ DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R0 + PTFF_R0_IS_GCREF + + + ;; Build a map of symbols representing offsets into a transition frame (see PInvokeTransitionFrame in + ;; rhbinder.h and keep these two in sync. + map 0 +m_ChainPointer field 4 ; r11 - OS frame chain used for quick stackwalks +m_RIP field 4 ; lr +m_FramePointer field 4 ; r7 +m_pThread field 4 +m_Flags field 4 ; bitmask of saved registers +m_PreservedRegs field (4 * 6) ; r4-r6,r8-r10 +m_CallersSP field 4 ; sp at routine entry +m_SavedR0 field 4 ; r0 +m_VolatileRegs field (4 * 4) ; r1-r3,lr +m_ReturnVfpRegs field (8 * 4) ; d0-d3, not really part of the struct +m_SavedAPSR field 4 ; saved condition codes +PROBE_FRAME_SIZE field 0 + + ;; Support for setting up a transition frame when performing a GC probe. In many respects this is very + ;; similar to the logic in PUSH_COOP_PINVOKE_FRAME in AsmMacros.h. In most cases setting up the + ;; transition frame comprises the entirety of the caller's prolog (and initial non-prolog code) and + ;; similarly for the epilog. Those cases can be dealt with using PROLOG_PROBE_FRAME and EPILOG_PROBE_FRAME + ;; defined below. For the special cases where additional work has to be done in the prolog we also provide + ;; the lower level macros ALLOC_PROBE_FRAME, FREE_PROBE_FRAME and INIT_PROBE_FRAME that allow more control + ;; to be asserted. + ;; + ;; Note that we currently employ a significant simplification of frame setup: we always allocate a + ;; maximally-sized PInvokeTransitionFrame and save all of the registers. Depending on the caller this can + ;; lead to upto five additional register saves (r0-r3,r12) or 20 bytes of stack space. I have done no + ;; analysis to see whether any of the worst cases occur on performance sensitive paths and whether the + ;; additional saves will show any measurable degradation. + + ;; Perform the parts of setting up a probe frame that can occur during the prolog (and indeed this macro + ;; can only be called from within the prolog). + MACRO + ALLOC_PROBE_FRAME + + PROLOG_STACK_ALLOC 4 ; Space for saved APSR + PROLOG_VPUSH {d0-d3} ; Save floating point return registers + PROLOG_PUSH {r0-r3,lr} ; Save volatile registers + PROLOG_STACK_ALLOC 4 ; Space for caller's SP + PROLOG_PUSH {r4-r6,r8-r10} ; Save non-volatile registers + PROLOG_STACK_ALLOC 8 ; Space for flags and Thread* + PROLOG_PUSH {r7} ; Save caller's frame pointer + PROLOG_PUSH {r11,lr} ; Save frame-chain pointer and return address + MEND + + ;; Undo the effects of an ALLOC_PROBE_FRAME. This may only be called within an epilog. Note that all + ;; registers are restored (apart for sp and pc), even volatiles. + MACRO + FREE_PROBE_FRAME + + EPILOG_POP {r11,lr} ; Restore frame-chain pointer and return address + EPILOG_POP {r7} ; Restore caller's frame pointer + EPILOG_STACK_FREE 8 ; Discard flags and Thread* + EPILOG_POP {r4-r6,r8-r10} ; Restore non-volatile registers + EPILOG_STACK_FREE 4 ; Discard caller's SP + EPILOG_POP {r0-r3,lr} ; Restore volatile registers + EPILOG_VPOP {d0-d3} ; Restore floating point return registers + EPILOG_STACK_FREE 4 ; Space for saved APSR + MEND + + ;; Complete the setup of a probe frame allocated with ALLOC_PROBE_FRAME with the initialization that can + ;; occur only outside the prolog (includes linking the frame to the current Thread). This macro assumes SP + ;; is invariant outside of the prolog. + ;; + ;; $threadReg : register containing the Thread* (this will be preserved) + ;; $trashReg : register that can be trashed by this macro + ;; $BITMASK : value to initialize m_Flags field with (register or #constant) + ;; $frameSize : total size of the method's stack frame (including probe frame size) + MACRO + INIT_PROBE_FRAME $threadReg, $trashReg, $BITMASK, $frameSize + + str $threadReg, [sp, #m_pThread] ; Thread * + mov $trashReg, $BITMASK ; Bitmask of preserved registers + str $trashReg, [sp, #m_Flags] + add $trashReg, sp, #$frameSize + str $trashReg, [sp, #m_CallersSP] + MEND + + ;; Simple macro to use when setting up the probe frame can comprise the entire prolog. Call this macro + ;; first in the method (no further prolog instructions can be added after this). + ;; + ;; $threadReg : register containing the Thread* (this will be preserved). If defaulted (specify |) then + ;; the current thread will be calculated inline into r2 ($trashReg must not equal r2 in + ;; this case) + ;; $trashReg : register that can be trashed by this macro + ;; $BITMASK : value to initialize m_Flags field with (register or #constant) + MACRO + PROLOG_PROBE_FRAME $threadReg, $trashReg, $BITMASK + + ; Local string tracking the name of the register in which the Thread* is kept. Defaults to the value + ; of $threadReg. + LCLS __PPF_ThreadReg +__PPF_ThreadReg SETS "$threadReg" + + ; Define the method prolog, allocating enough stack space for the PInvokeTransitionFrame and saving + ; incoming register values into it. + ALLOC_PROBE_FRAME + + ; If the caller didn't provide a value for $threadReg then generate code to fetch the Thread* into r2. + ; Record that r2 holds the Thread* in our local variable. + IF "$threadReg" == "" + ASSERT "$trashReg" != "r2" +__PPF_ThreadReg SETS "r2" + INLINE_GETTHREAD $__PPF_ThreadReg, $trashReg + ENDIF + + ; Perform the rest of the PInvokeTransitionFrame initialization. + INIT_PROBE_FRAME $__PPF_ThreadReg, $trashReg, $BITMASK, PROBE_FRAME_SIZE + str sp, [$__PPF_ThreadReg, #OFFSETOF__Thread__m_pHackPInvokeTunnel] + MEND + + ; Simple macro to use when PROLOG_PROBE_FRAME was used to set up and initialize the prolog and + ; PInvokeTransitionFrame. This will define the epilog including a return via the restored LR. + MACRO + EPILOG_PROBE_FRAME + + FREE_PROBE_FRAME + EPILOG_RETURN + MEND + + +;; +;; Macro to clear the hijack state. This is safe to do because the suspension code will not Unhijack this +;; thread if it finds it at an IP that isn't managed code. +;; +;; Register state on entry: +;; r2: thread pointer +;; +;; Register state on exit: +;; r12: trashed +;; + MACRO + ClearHijackState + + mov r12, #0 + str r12, [r2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str r12, [r2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + MEND + + +;; +;; The prolog for all GC suspension hijacks (normal and stress). Fixes up the hijacked return address, and +;; clears the hijack state. +;; +;; Register state on entry: +;; All registers correct for return to the original return address. +;; +;; Register state on exit: +;; r2: thread pointer +;; r3: trashed +;; r12: trashed +;; + MACRO + FixupHijackedCallstack + + ;; r2 <- GetThread(), TRASHES r3 + INLINE_GETTHREAD r2, r3 + + ;; + ;; Fix the stack by restoring the original return address + ;; + ldr lr, [r2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + + ClearHijackState + MEND + +;; +;; Set the Thread state and wait for a GC to complete. +;; +;; Register state on entry: +;; r4: thread pointer +;; +;; Register state on exit: +;; r4: thread pointer +;; All other registers trashed +;; + + EXTERN RhpWaitForGCNoAbort + + MACRO + WaitForGCCompletion + + ldr r2, [r4, #OFFSETOF__Thread__m_ThreadStateFlags] + tst r2, #TSF_SuppressGcStress__OR__TSF_DoNotTriggerGC + bne %ft0 + + ldr r2, [r4, #OFFSETOF__Thread__m_pHackPInvokeTunnel] + bl RhpWaitForGCNoAbort +0 + MEND + + + MACRO + HijackTargetFakeProlog + + ;; This is a fake entrypoint for the method that 'tricks' the OS into calling our personality routine. + ;; The code here should never be executed, and the unwind info is bogus, but we don't mind since the + ;; stack is broken by the hijack anyway until after we fix it below. + PROLOG_PUSH {lr} + nop ; We also need a nop here to simulate the implied bl instruction. Without + ; this, an OS-applied -2 will back up into the method prolog and the unwind + ; will not be applied as desired. + + MEND + + +;; +;; +;; +;; GC Probe Hijack targets +;; +;; + EXTERN RhpPInvokeExceptionGuard + + + NESTED_ENTRY RhpGcProbeHijackScalarWrapper, .text, RhpPInvokeExceptionGuard + + HijackTargetFakeProlog + + LABELED_RETURN_ADDRESS RhpGcProbeHijackScalar + + FixupHijackedCallstack + mov r12, #DEFAULT_FRAME_SAVE_FLAGS + b RhpGcProbe + NESTED_END RhpGcProbeHijackScalarWrapper + + NESTED_ENTRY RhpGcProbeHijackObjectWrapper, .text, RhpPInvokeExceptionGuard + + HijackTargetFakeProlog + + LABELED_RETURN_ADDRESS RhpGcProbeHijackObject + + FixupHijackedCallstack + mov r12, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R0 + PTFF_R0_IS_GCREF) + b RhpGcProbe + NESTED_END RhpGcProbeHijackObjectWrapper + + NESTED_ENTRY RhpGcProbeHijackByrefWrapper, .text, RhpPInvokeExceptionGuard + + HijackTargetFakeProlog + + LABELED_RETURN_ADDRESS RhpGcProbeHijackByref + + FixupHijackedCallstack + mov r12, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R0 + PTFF_R0_IS_BYREF) + b RhpGcProbe + NESTED_END RhpGcProbeHijackByrefWrapper + +#ifdef FEATURE_GC_STRESS +;; +;; +;; GC Stress Hijack targets +;; +;; + LEAF_ENTRY RhpGcStressHijackScalar + FixupHijackedCallstack + mov r12, #DEFAULT_FRAME_SAVE_FLAGS + b RhpGcStressProbe + LEAF_END RhpGcStressHijackScalar + + LEAF_ENTRY RhpGcStressHijackObject + FixupHijackedCallstack + mov r12, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R0 + PTFF_R0_IS_GCREF) + b RhpGcStressProbe + LEAF_END RhpGcStressHijackObject + + LEAF_ENTRY RhpGcStressHijackByref + FixupHijackedCallstack + mov r12, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R0 + PTFF_R0_IS_BYREF) + b RhpGcStressProbe + LEAF_END RhpGcStressHijackByref + + +;; +;; Worker for our GC stress probes. Do not call directly!! +;; Instead, go through RhpGcStressHijack{Scalar|Object|Byref}. +;; This worker performs the GC Stress work and returns to the original return address. +;; +;; Register state on entry: +;; r0: hijacked function return value +;; r1: hijacked function return value +;; r2: thread pointer +;; r12: register bitmask +;; +;; Register state on exit: +;; Scratch registers, except for r0, have been trashed +;; All other registers restored as they were when the hijack was first reached. +;; + NESTED_ENTRY RhpGcStressProbe + PROLOG_PROBE_FRAME r2, r3, r12 + + bl $REDHAWKGCINTERFACE__STRESSGC + + EPILOG_PROBE_FRAME + NESTED_END RhpGcStressProbe +#endif ;; FEATURE_GC_STRESS + + EXTERN RhpThrowHwEx + + LEAF_ENTRY RhpGcProbe + ldr r3, =RhpTrapThreads + ldr r3, [r3] + tst r3, #TrapThreadsFlags_TrapThreads + bne %0 + bx lr +0 + b RhpGcProbeRare + LEAF_END RhpGcProbe + + NESTED_ENTRY RhpGcProbeRare + PROLOG_PROBE_FRAME r2, r3, r12 + + mov r4, r2 + WaitForGCCompletion + + ldr r2, [sp, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + tst r2, #PTFF_THREAD_ABORT + bne %1 + + EPILOG_PROBE_FRAME + +1 + FREE_PROBE_FRAME + EPILOG_NOP mov r0, #STATUS_REDHAWK_THREAD_ABORT + EPILOG_NOP mov r1, lr ;; return address as exception PC + EPILOG_BRANCH RhpThrowHwEx + + NESTED_END RhpGcProbe + + LEAF_ENTRY RhpGcPoll + ldr r0, =RhpTrapThreads + ldr r0, [r0] + tst r0, #TrapThreadsFlags_TrapThreads + bne RhpGcPollRare + bx lr + LEAF_END RhpGcPoll + + NESTED_ENTRY RhpGcPollRare + PROLOG_PROBE_FRAME |, r3, #PROBE_SAVE_FLAGS_EVERYTHING + + ; Unhijack this thread, if necessary. + INLINE_THREAD_UNHIJACK r2, r0, r1 ;; trashes r0, r1 + + mov r4, r2 + WaitForGCCompletion + + EPILOG_PROBE_FRAME + NESTED_END RhpGcPollRare + + LEAF_ENTRY RhpGcPollStress + ; + ; loop hijacking is used instead + ; + __debugbreak + + LEAF_END RhpGcPollStress + + +#ifdef FEATURE_GC_STRESS + NESTED_ENTRY RhpHijackForGcStress + PROLOG_PUSH {r0,r1} ; Save return value + PROLOG_VPUSH {d0-d3} ; Save VFP return value + + ;; + ;; Setup a PAL_LIMITED_CONTEXT that looks like what you'd get if you had suspended this thread at the + ;; IP after the call to this helper. + ;; + ;; This is very likely overkill since the calculation of the return address should only need SP and + ;; LR, but this is test code, so I'm not too worried about efficiency. + ;; + ;; Setup a PAL_LIMITED_CONTEXT on the stack { + ;; we'll need to reserve the size of the D registers in the context + ;; compute in the funny way below to include any padding between LR and D +DREG_SZ equ (SIZEOF__PAL_LIMITED_CONTEXT - (OFFSETOF__PAL_LIMITED_CONTEXT__LR + 4)) + + PROLOG_STACK_ALLOC DREG_SZ ;; Reserve space for d8-d15 + PROLOG_PUSH {r0,lr} ;; Reserve space for SP and store LR + PROLOG_PUSH {r0,r4-r11,lr} + ;; } end PAL_LIMITED_CONTEXT + + ;; Compute and save SP at callsite. + add r0, sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x20 + 8) ;; +0x20 for vpush {d0-d3}, +8 for push {r0,r1} + str r0, [sp, #OFFSETOF__PAL_LIMITED_CONTEXT__SP] + + mov r0, sp ; Address of PAL_LIMITED_CONTEXT + bl $THREAD__HIJACKFORGCSTRESS + + ;; epilog + EPILOG_POP {r0,r4-r11,lr} + EPILOG_STACK_FREE DREG_SZ + 8 ; Discard saved SP and LR and space for d8-d15 + EPILOG_VPOP {d0-d3} ; Restore VFP return value + EPILOG_POP {r0,r1} ; Restore return value + bx lr + NESTED_END RhpHijackForGcStress +#endif ;; FEATURE_GC_STRESS + + +;; +;; The following functions are _jumped_ to when we need to transfer control from one method to another for EH +;; dispatch. These are needed to properly coordinate with the GC hijacking logic. We are essentially replacing +;; the return from the throwing method with a jump to the handler in the caller, but we need to be aware of +;; any return address hijack that may be in place for GC suspension. These routines use a quick test of the +;; return address against a specific GC hijack routine, and then fixup the stack pointer to what it would be +;; after a real return from the throwing method. Then, if we are not hijacked we can simply jump to the +;; handler in the caller. +;; +;; If we are hijacked, then we jump to a routine that will unhijack appropriatley and wait for the GC to +;; complete. There are also variants for GC stress. +;; +;; Note that at this point we are eiher hijacked or we are not, and this will not change until we return to +;; managed code. It is an invariant of the system that a thread will only attempt to hijack or unhijack +;; another thread while the target thread is suspended in managed code, and this is _not_ managed code. +;; +;; Register state on entry: +;; r0: pointer to this function (i.e., trash) +;; r1: reference to the exception object. +;; r2: handler address we want to jump to. +;; Non-volatile registers are all already correct for return to the caller. +;; LR still contains the return address. +;; +;; Register state on exit: +;; All registers except r0 and lr unchanged +;; + MACRO + RTU_EH_JUMP_HELPER $funcName, $hijackFuncName, $isStress, $stressFuncName + + LEAF_ENTRY $funcName + ; Currently the EH epilog won't pop the return address back into LR, + ; so we have to have a funny load from [sp-4] here to retrieve it. + + ldr r0, =$hijackFuncName + cmp r0, lr + beq RhpGCProbeForEHJump + + IF $isStress + ldr r0, =$stressFuncName + cmp r0, lr + beq RhpGCStressProbeForEHJump + ENDIF + + ;; We are not hijacked, so we can return to the handler. + ;; We return to keep the call/return prediction balanced. + mov lr, r2 ; Update the return address + bx lr + LEAF_END $funcName + MEND + +;; We need an instance of the helper for each possible hijack function. The binder has enough +;; information to determine which one we need to use for any function. + RTU_EH_JUMP_HELPER RhpEHJumpScalar, RhpGcProbeHijackScalar, {false}, 0 + RTU_EH_JUMP_HELPER RhpEHJumpObject, RhpGcProbeHijackObject, {false}, 0 + RTU_EH_JUMP_HELPER RhpEHJumpByref, RhpGcProbeHijackByref, {false}, 0 +#ifdef FEATURE_GC_STRESS + RTU_EH_JUMP_HELPER RhpEHJumpScalarGCStress, RhpGcProbeHijackScalar, {true}, RhpGcStressHijackScalar + RTU_EH_JUMP_HELPER RhpEHJumpObjectGCStress, RhpGcProbeHijackObject, {true}, RhpGcStressHijackObject + RTU_EH_JUMP_HELPER RhpEHJumpByrefGCStress, RhpGcProbeHijackByref, {true}, RhpGcStressHijackByref +#endif + +;; +;; Macro to setup our frame and adjust the location of the EH object reference for EH jump probe funcs. +;; +;; Register state on entry: +;; r0: scratch +;; r1: reference to the exception object. +;; r2: handler address we want to jump to. +;; Non-volatile registers are all already correct for return to the caller. +;; The stack is as if we are just about to returned from the call +;; +;; Register state on exit: +;; r0: reference to the exception object +;; r2: thread pointer +;; + MACRO + EHJumpProbeProlog + + PROLOG_PUSH {r1,r2} ; save the handler address so we can jump to it later (save r1 just for alignment) + PROLOG_NOP mov r0, r1 ; move the ex object reference into r0 so we can report it + ALLOC_PROBE_FRAME + + ;; r2 <- GetThread(), TRASHES r1 + INLINE_GETTHREAD r2, r1 + + ;; Recover the original return address and update the frame + ldr lr, [r2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + str lr, [sp, #OFFSETOF__PInvokeTransitionFrame__m_RIP] + + ;; ClearHijackState expects thread in r2 (trashes r12). + ClearHijackState + + ; TRASHES r1 + INIT_PROBE_FRAME r2, r1, #PROBE_SAVE_FLAGS_R0_IS_GCREF, (PROBE_FRAME_SIZE + 8) + str sp, [r2, #OFFSETOF__Thread__m_pHackPInvokeTunnel] + MEND + +;; +;; Macro to re-adjust the location of the EH object reference, cleanup the frame, and make the +;; final jump to the handler for EH jump probe funcs. +;; +;; Register state on entry: +;; r0: reference to the exception object +;; r1-r3: scratch +;; +;; Register state on exit: +;; sp: correct for return to the caller +;; r1: reference to the exception object +;; + MACRO + EHJumpProbeEpilog + + FREE_PROBE_FRAME ; This restores exception object back into r0 + EPILOG_NOP mov r1, r0 ; Move the Exception object back into r1 where the catch handler expects it + EPILOG_POP {r0,pc} ; Recover the handler address and jump to it + MEND + +;; +;; We are hijacked for a normal GC (not GC stress), so we need to unhijack and wait for the GC to complete. +;; +;; Register state on entry: +;; r0: reference to the exception object. +;; r2: thread +;; Non-volatile registers are all already correct for return to the caller. +;; The stack is as if we have tail called to this function (lr points to return address). +;; +;; Register state on exit: +;; r7: previous frame pointer +;; r0: reference to the exception object +;; + NESTED_ENTRY RhpGCProbeForEHJump + EHJumpProbeProlog + +#ifdef _DEBUG + ;; + ;; If we get here, then we have been hijacked for a real GC, and our SyncState must + ;; reflect that we've been requested to synchronize. + + ldr r1, =RhpTrapThreads + ldr r1, [r1] + tst r1, #TrapThreadsFlags_TrapThreads + bne %0 + + bl RhDebugBreak +0 +#endif ;; _DEBUG + + mov r4, r2 + WaitForGCCompletion + + EHJumpProbeEpilog + NESTED_END RhpGCProbeForEHJump + +#ifdef FEATURE_GC_STRESS +;; +;; We are hijacked for GC Stress (not a normal GC) so we need to invoke the GC stress helper. +;; +;; Register state on entry: +;; r1: reference to the exception object. +;; r2: thread +;; Non-volatile registers are all already correct for return to the caller. +;; The stack is as if we have tail called to this function (lr points to return address). +;; +;; Register state on exit: +;; r7: previous frame pointer +;; r0: reference to the exception object +;; + NESTED_ENTRY RhpGCStressProbeForEHJump + EHJumpProbeProlog + + bl $REDHAWKGCINTERFACE__STRESSGC + + EHJumpProbeEpilog + NESTED_END RhpGCStressProbeForEHJump + +;; +;; INVARIANT: Don't trash the argument registers, the binder codegen depends on this. +;; + LEAF_ENTRY RhpSuppressGcStress + + push {r0-r2} + INLINE_GETTHREAD r0, r1 + +Retry + ldrex r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + orr r1, #TSF_SuppressGcStress + strex r2, r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + cbz r2, Success + b Retry + +Success + pop {r0-r2} + bx lr + + LEAF_END RhpSuppressGcStress +#endif ;; FEATURE_GC_STRESS + + INLINE_GETTHREAD_CONSTANT_POOL + + end diff --git a/src/coreclr/nativeaot/Runtime/arm/GetThread.asm b/src/coreclr/nativeaot/Runtime/arm/GetThread.asm new file mode 100644 index 00000000000000..b78319f8f15c24 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/GetThread.asm @@ -0,0 +1,32 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpGetThread +;; +;; +;; INPUT: none +;; +;; OUTPUT: r0: Thread pointer +;; +;; MUST PRESERVE ARGUMENT REGISTERS +;; @todo check the actual requirements here, r0 is both return and argument register +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + LEAF_ENTRY RhpGetThread + + ;; r0 = GetThread(), TRASHES r12 + INLINE_GETTHREAD r0, r12 + bx lr + + LEAF_END +FASTCALL_ENDFUNC + + INLINE_GETTHREAD_CONSTANT_POOL + + end diff --git a/src/coreclr/nativeaot/Runtime/arm/Interlocked.S b/src/coreclr/nativeaot/Runtime/arm/Interlocked.S new file mode 100644 index 00000000000000..baf4e4b02216a1 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/Interlocked.S @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp +#include + +// WARNING: Code in EHHelpers.cpp makes assumptions about this helper, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpLockCmpXchg32AVLocation +// - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address +// r0 = destination address +// r1 = value +// r2 = comparand +LEAF_ENTRY RhpLockCmpXchg32, _TEXT + dmb +ALTERNATE_ENTRY RhpLockCmpXchg32AVLocation +LOCAL_LABEL(CmpXchg32Retry): + ldrex r3, [r0] + cmp r2, r3 + bne LOCAL_LABEL(CmpXchg32Exit) + strex r12, r1, [r0] + cmp r12, #0 + bne LOCAL_LABEL(CmpXchg32Retry) +LOCAL_LABEL(CmpXchg32Exit): + mov r0, r3 + dmb + bx lr +LEAF_END RhpLockCmpXchg32, _TEXT + +// WARNING: Code in EHHelpers.cpp makes assumptions about this helper, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpLockCmpXchg64AVLocation +// - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address +// r0 = destination address +// {r2,r3} = value +// sp[0+8] = comparand +LEAF_ENTRY RhpLockCmpXchg64, _TEXT +ALTERNATE_ENTRY RhpLockCmpXchg64AVLocation + ldr r12, [r0] // dummy read for null check + PROLOG_PUSH "{r4-r6,lr}" + dmb + ldrd r4, r5, [sp,#0x10] +LOCAL_LABEL(CmpXchg64Retry): + ldrexd r6, r1, [r0] + cmp r6, r4 + bne LOCAL_LABEL(CmpXchg64Exit) + cmp r1, r5 + bne LOCAL_LABEL(CmpXchg64Exit) + strexd r12, r2, r3, [r0] + cmp r12, #0 + bne LOCAL_LABEL(CmpXchg64Retry) +LOCAL_LABEL(CmpXchg64Exit): + mov r0, r6 + dmb + EPILOG_POP "{r4-r6,pc}" +LEAF_END RhpLockCmpXchg64, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm/InteropThunksHelpers.S b/src/coreclr/nativeaot/Runtime/arm/InteropThunksHelpers.S new file mode 100644 index 00000000000000..d8012f088a6ec1 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/InteropThunksHelpers.S @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp +#include + +#define POINTER_SIZE 4 + +// +// RhCommonStub +// +NESTED_ENTRY RhCommonStub, _TEXT, NoHandler + // Custom calling convention: + // red zone has pointer to the current thunk's data block (data contains 2 pointer values: context + target pointers) + // Copy red zone value into r12 so that the PROLOG_PUSH doesn't destroy it + ldr r12, [sp, #-4] + PROLOG_PUSH "{r0-r4, lr}" + PROLOG_VPUSH {d0-d7} // Capture the floating point argument registers + + mov r4, r12 + + INLINE_GET_TLS_VAR tls_thunkData + + // r0 = base address of TLS data + // r4 = address of context cell in thunk's data + + ldr r12, [r4] + str r12, [r0] + + // Now load the target address and jump to it. + ldr r12, [r4, #POINTER_SIZE] + EPILOG_VPOP {d0-d7} + EPILOG_POP "{r0-r4, lr}" + bx r12 + +NESTED_END RhCommonStub, _TEXT + +// +// IntPtr RhGetCommonStubAddress() +// +LEAF_ENTRY RhGetCommonStubAddress, _TEXT + ldr r0, =C_FUNC(RhCommonStub) + bx lr +LEAF_END RhGetCommonStubAddress, _TEXT + +// +// IntPtr RhGetCurrentThunkContext() +// +LEAF_ENTRY RhGetCurrentThunkContext, _TEXT + + PROLOG_PUSH "{r12, lr}" + + INLINE_GET_TLS_VAR tls_thunkData + + ldr r0, [r0] + EPILOG_POP "{r12, pc}" +LEAF_END RhGetCurrentThunkContext, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm/InteropThunksHelpers.asm b/src/coreclr/nativeaot/Runtime/arm/InteropThunksHelpers.asm new file mode 100644 index 00000000000000..68b1675981b748 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/InteropThunksHelpers.asm @@ -0,0 +1,83 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + +#include "kxarm.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +__tls_array equ 0x2C ;; offsetof(TEB, ThreadLocalStoragePointer) + +POINTER_SIZE equ 0x04 + +;; TLS variables + AREA |.tls$|, DATA +ThunkParamSlot % 0x4 + + TEXTAREA + + EXTERN _tls_index + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interop Thunks Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;; + ;; RhCommonStub + ;; + NESTED_ENTRY RhCommonStub + ;; Custom calling convention: + ;; red zone has pointer to the current thunk's data block (data contains 2 pointer values: context + target pointers) + ;; Copy red zone value into r12 so that the PROLOG_PUSH doesn't destroy it + PROLOG_NOP ldr r12, [sp, #-4] + PROLOG_PUSH {r0-r3} + + ;; Save context data into the ThunkParamSlot thread-local variable + ;; A pointer to the delegate and function pointer for open static delegate should have been saved in the thunk's context cell during thunk allocation + ldr r3, =_tls_index + ldr r2, [r3] + mrc p15, #0, r3, c13, c0, #2 + ldr r3, [r3, #__tls_array] + ldr r2, [r3, r2, lsl #2] ;; r2 <- our TLS base + + ;; r2 = base address of TLS data + ;; r12 = address of context cell in thunk's data + + ;; store thunk address in thread static + ldr r1, [r12] + ldr r3, =ThunkParamSlot + str r1, [r2, r3] ;; ThunkParamSlot <- context slot data + + ;; Now load the target address and jump to it. + ldr r12, [r12, #POINTER_SIZE] + EPILOG_POP {r0-r3} + bx r12 + NESTED_END RhCommonStub + + + ;; + ;; IntPtr RhGetCommonStubAddress() + ;; + LEAF_ENTRY RhGetCommonStubAddress + ldr r0, =RhCommonStub + bx lr + LEAF_END RhGetCommonStubAddress + + + ;; + ;; IntPtr RhGetCurrentThunkContext() + ;; + LEAF_ENTRY RhGetCurrentThunkContext + + ldr r3, =_tls_index + ldr r2, [r3] + mrc p15, #0, r3, c13, c0, #2 + ldr r3, [r3, #__tls_array] + ldr r2, [r3, r2, lsl #2] ;; r2 <- our TLS base + + ldr r3, =ThunkParamSlot + ldr r0, [r2, r3] ;; r0 <- ThunkParamSlot + + bx lr + LEAF_END RhGetCurrentThunkContext + + END diff --git a/src/coreclr/nativeaot/Runtime/arm/MiscStubs.S b/src/coreclr/nativeaot/Runtime/arm/MiscStubs.S new file mode 100644 index 00000000000000..65b3d72c980265 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/MiscStubs.S @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp +#include + +// ------------------------------------------------------------------ +// The following helper will access ("probe") a word on each page of the stack +// starting with the page right beneath sp down to the one pointed to by r4. +// The procedure is needed to make sure that the "guard" page is pushed down below the allocated stack frame. +// The call to the helper will be emitted by JIT in the function/funclet prolog when stack frame is larger than an OS page. +// On entry: +// r4 - points to the lowest address on the stack frame being allocated (i.e. [InitialSp - FrameSize]) +// sp - points to some byte on the last probed page +// On exit: +// r4 - is preserved +// r5 - is not preserved +// +// NOTE: this helper will probe at least one page below the one pointed to by sp. +#define PROBE_PAGE_SIZE 4096 +#define PROBE_PAGE_SIZE_LOG2 12 + +LEAF_ENTRY RhpStackProbe, _TEXT + PROLOG_PUSH "{r7}" + PROLOG_STACK_SAVE r7 + + mov r5, sp // r5 points to some byte on the last probed page + bfc r5, #0, #PROBE_PAGE_SIZE_LOG2 // r5 points to the **lowest address** on the last probed page + mov sp, r5 + +ProbeLoop: + // Immediate operand for the following instruction can not be greater than 4095. + sub sp, #(PROBE_PAGE_SIZE - 4) // sp points to the **fourth** byte on the **next page** to probe + ldr r5, [sp, #-4]! // sp points to the lowest address on the **last probed** page + cmp sp, r4 + bhi ProbeLoop // If (sp > r4), then we need to probe at least one more page. + + EPILOG_STACK_RESTORE r7 + EPILOG_POP "{r7}" + EPILOG_BRANCH_REG lr +LEAF_END RhpStackProbe, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm/MiscStubs.asm b/src/coreclr/nativeaot/Runtime/arm/MiscStubs.asm new file mode 100644 index 00000000000000..9585ff2b7de942 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/MiscStubs.asm @@ -0,0 +1,156 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + EXTERN memcpy + EXTERN memcpyGCRefs + EXTERN memcpyGCRefsWithWriteBarrier + EXTERN memcpyAnyWithWriteBarrier + + TEXTAREA + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyMultibyteNoGCRefs(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; + + LEAF_ENTRY RhpCopyMultibyteNoGCRefs + + ; r0 dest + ; r1 src + ; r2 count + + cmp r2, #0 ; check for a zero-length copy + beq NothingToCopy_NoGCRefs + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyMultibyteNoGCRefsDestAVLocation + ldrb r3, [r0] + ALTERNATE_ENTRY RhpCopyMultibyteNoGCRefsSrcAVLocation + ldrb r3, [r1] + + ; tail-call to plain-old-memcpy + b memcpy + +NothingToCopy_NoGCRefs + ; dest is already still in r0 + bx lr + + LEAF_END + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyMultibyte(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; + + LEAF_ENTRY RhpCopyMultibyte + + ; r0 dest + ; r1 src + ; r2 count + + cmp r2, #0 ; check for a zero-length copy + beq NothingToCopy_RhpCopyMultibyte + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyMultibyteDestAVLocation + ldrb r3, [r0] + ALTERNATE_ENTRY RhpCopyMultibyteSrcAVLocation + ldrb r3, [r1] + + ; tail-call to the GC-safe memcpy implementation + b memcpyGCRefs + +NothingToCopy_RhpCopyMultibyte + ; dest is already still in r0 + bx lr + + LEAF_END + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyMultibyteWithWriteBarrier(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; Runs a card table update via RhpBulkWriteBarrier after the copy +;; + + LEAF_ENTRY RhpCopyMultibyteWithWriteBarrier + + ; r0 dest + ; r1 src + ; r2 count + + cmp r2, #0 ; check for a zero-length copy + beq NothingToCopy_RhpCopyMultibyteWithWriteBarrier + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyMultibyteWithWriteBarrierDestAVLocation + ldrb r3, [r0] + ALTERNATE_ENTRY RhpCopyMultibyteWithWriteBarrierSrcAVLocation + ldrb r3, [r1] + + ; tail-call to the GC-safe memcpy implementation + b memcpyGCRefsWithWriteBarrier + +NothingToCopy_RhpCopyMultibyteWithWriteBarrier + ; dest is already still in r0 + bx lr + + LEAF_END + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyAnyWithWriteBarrier(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; Runs a card table update via RhpBulkWriteBarrier after the copy if it contained GC pointers +;; + + LEAF_ENTRY RhpCopyAnyWithWriteBarrier + + ; r0 dest + ; r1 src + ; r2 count + + cmp r2, #0 ; check for a zero-length copy + beq NothingToCopy_RhpCopyAnyWithWriteBarrier + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyAnyWithWriteBarrierDestAVLocation + ldrb r3, [r0] + ALTERNATE_ENTRY RhpCopyAnyWithWriteBarrierSrcAVLocation + ldrb r3, [r1] + + ; tail-call to the GC-safe memcpy implementation + b memcpyAnyWithWriteBarrier + +NothingToCopy_RhpCopyAnyWithWriteBarrier + ; dest is already still in r0 + bx lr + + LEAF_END + + end diff --git a/src/coreclr/nativeaot/Runtime/arm/PInvoke.S b/src/coreclr/nativeaot/Runtime/arm/PInvoke.S new file mode 100644 index 00000000000000..6be1544876c7b8 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/PInvoke.S @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include + +.syntax unified +.thumb + +// +// RhpPInvoke +// +// IN: R0: address of pinvoke frame +// +// This helper assumes that its callsite is as good to start the stackwalk as the actual PInvoke callsite. +// The codegenerator must treat the callsite of this helper as GC triggering and generate the GC info for it. +// Also, the codegenerator must ensure that there are no live GC references in callee saved registers. +// + +NESTED_ENTRY RhpPInvoke, _TEXT, NoHandler + str lr, [r0, #OFFSETOF__PInvokeTransitionFrame__m_RIP] + str r11, [r0, #OFFSETOF__PInvokeTransitionFrame__m_FramePointer] + str sp, [r0, #OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs] + mov r3, #PTFF_SAVE_SP + str r3, [r0, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + + PROLOG_PUSH "{r5,lr}" + + mov r5, r0 + // get TLS global variable address + // r0 = GetThread() + INLINE_GETTHREAD + str r0, [r5, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + str r5, [r0, #OFFSETOF__Thread__m_pTransitionFrame] + + ldr r3, =C_FUNC(RhpTrapThreads) + ldr r3, [r3] + cbnz r3, LOCAL_LABEL(InvokeRareTrapThread) // TrapThreadsFlags_None = 0 + + EPILOG_POP "{r5,pc}" + +LOCAL_LABEL(InvokeRareTrapThread): + EPILOG_POP "{r5,lr}" + b C_FUNC(RhpWaitForSuspend2) +NESTED_END RhpPInvoke, _TEXT + + +// +// RhpPInvokeReturn +// +// IN: R0: address of pinvoke frame +// +LEAF_ENTRY RhpPInvokeReturn, _TEXT + ldr r3, [r0, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + + mov r2, #0 + str r2, [r3, #OFFSETOF__Thread__m_pTransitionFrame] + + ldr r3, =C_FUNC(RhpTrapThreads) + ldr r3, [r3] + cbnz r3, LOCAL_LABEL(ReturnRareTrapThread) // TrapThreadsFlags_None = 0 + + bx lr +LOCAL_LABEL(ReturnRareTrapThread): + // passing transition frame pointer in r0 + b C_FUNC(RhpWaitForGC2) +LEAF_END RhpPInvokeReturn, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm/PInvoke.asm b/src/coreclr/nativeaot/Runtime/arm/PInvoke.asm new file mode 100644 index 00000000000000..95c116b0a5132a --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/PInvoke.asm @@ -0,0 +1,123 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + + IMPORT RhpReversePInvokeBadTransition + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForSuspend -- rare path for RhpPInvoke and RhpReversePInvokeReturn +;; +;; +;; INPUT: none +;; +;; TRASHES: none +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpWaitForSuspend + + PROLOG_PUSH {r0-r4,lr} ; Need to save argument registers r0-r3 and lr, r4 is just for alignment + PROLOG_VPUSH {d0-d7} ; Save float argument registers as well since they're volatile + + bl RhpWaitForSuspend2 + + EPILOG_VPOP {d0-d7} + EPILOG_POP {r0-r4,pc} + + NESTED_END RhpWaitForSuspend + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGCNoAbort +;; +;; +;; INPUT: r2: transition frame +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpWaitForGCNoAbort + + PROLOG_PUSH {r0-r6,lr} ; Even number of registers to maintain 8-byte stack alignment + PROLOG_VPUSH {d0-d3} ; Save float return value registers as well + + ldr r5, [r2, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + + ldr r0, [r5, #OFFSETOF__Thread__m_ThreadStateFlags] + tst r0, #TSF_DoNotTriggerGc + bne Done + + mov r0, r2 ; passing transition frame in r0 + bl RhpWaitForGC2 + +Done + EPILOG_VPOP {d0-d3} + EPILOG_POP {r0-r6,pc} + + NESTED_END RhpWaitForGCNoAbort + + EXTERN RhpThrowHwEx + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGC +;; +;; +;; INPUT: r2: transition frame +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpWaitForGC + PROLOG_PUSH {r0,lr} + + ldr r0, =RhpTrapThreads + ldr r0, [r0] + tst r0, #TrapThreadsFlags_TrapThreads + beq NoWait + bl RhpWaitForGCNoAbort +NoWait + tst r0, #TrapThreadsFlags_AbortInProgress + beq NoAbort + ldr r0, [r2, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + tst r0, #PTFF_THREAD_ABORT + beq NoAbort + EPILOG_POP {r0,r1} ; hijack target address as exception PC + EPILOG_NOP mov r0, #STATUS_REDHAWK_THREAD_ABORT + EPILOG_BRANCH RhpThrowHwEx +NoAbort + EPILOG_POP {r0,pc} + NESTED_END RhpWaitForGC + + INLINE_GETTHREAD_CONSTANT_POOL + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpReversePInvokeAttachOrTrapThread -- rare path for RhpPInvoke +;; +;; +;; INPUT: r4: address of reverse pinvoke frame +;; +;; TRASHES: none +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpReversePInvokeAttachOrTrapThread + + PROLOG_PUSH {r0-r4,lr} ; Need to save argument registers r0-r3 and lr, r4 is just for alignment + PROLOG_VPUSH {d0-d7} ; Save float argument registers as well since they're volatile + + mov r0, r4 ; passing reverse pinvoke frame pointer in r0 + bl RhpReversePInvokeAttachOrTrapThread2 + + EPILOG_VPOP {d0-d7} + EPILOG_POP {r0-r4,pc} + + NESTED_END RhpReversePInvokeTrapThread + + + end diff --git a/src/coreclr/nativeaot/Runtime/arm/StubDispatch.S b/src/coreclr/nativeaot/Runtime/arm/StubDispatch.S new file mode 100644 index 00000000000000..c6eacd71d99caf --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/StubDispatch.S @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp +#include + +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH + +// Macro that generates a stub consuming a cache with the given number of entries. +.macro DEFINE_INTERFACE_DISPATCH_STUB entries + +NESTED_ENTRY RhpInterfaceDispatch\entries, _TEXT, NoHandler + // r12 currently contains the indirection cell address. But we need more scratch registers and + // we may A/V on a null this. Store r1 and r2 in red zone. + str r1, [sp, #-8] + str r2, [sp, #-4] + + // r12 currently holds the indirection cell address. We need to get the cache structure instead. + ldr r2, [r12, #OFFSETOF__InterfaceDispatchCell__m_pCache] + + // Load the MethodTable from the object instance in r0. + ALTERNATE_ENTRY RhpInterfaceDispatchAVLocation\entries + ldr r1, [r0] + + CurrentOffset = OFFSETOF__InterfaceDispatchCache__m_rgEntries + // For each entry in the cache, see if its MethodTable type matches the MethodTable in r1. + // If so, call the second cache entry. If not, skip the InterfaceDispatchCacheEntry. + // R1 : Instance MethodTable* + // R2: Cache data structure + // R12 : Trashed. On succesful check, set to the target address to jump to. + .rept \entries + ldr r12, [r2, #CurrentOffset] + cmp r1, r12 + bne 0f + ldr r12, [r2, #(CurrentOffset + 4)] + b LOCAL_LABEL(99_\entries) + 0: + CurrentOffset = CurrentOffset + 8 + .endr + + // Point r12 to the indirection cell using the back pointer in the cache block + ldr r12, [r2, #OFFSETOF__InterfaceDispatchCache__m_pCell] + + ldr r1, [sp, #-8] + ldr r2, [sp, #-4] + b C_FUNC(RhpInterfaceDispatchSlow) + + // Common epilog for cache hits. Have to out of line it here due to limitation on the number of + // epilogs imposed by the unwind code macros. +LOCAL_LABEL(99_\entries): + // R2 contains address of the cache block. We store it in the red zone in case the target we jump + // to needs it. + // R12 contains the target address to jump to + ldr r1, [sp, #-8] + // We have to store R2 with address of the cache block into red zone before restoring original r2. + str r2, [sp, #-8] + ldr r2, [sp, #-4] + EPILOG_BRANCH_REG r12 + +NESTED_END RhpInterfaceDispatch\entries, _TEXT + +.endm // DEFINE_INTERFACE_DISPATCH_STUB + +// Define all the stub routines we currently need. +// +// The mrt100dbi requires these be exported to identify mrt100 code that dispatches back into managed. +// If you change or add any new dispatch stubs, please also change slr.def and dbi\process.cpp CordbProcess::GetExportStepInfo +// +DEFINE_INTERFACE_DISPATCH_STUB 1 +DEFINE_INTERFACE_DISPATCH_STUB 2 +DEFINE_INTERFACE_DISPATCH_STUB 4 +DEFINE_INTERFACE_DISPATCH_STUB 8 +DEFINE_INTERFACE_DISPATCH_STUB 16 +DEFINE_INTERFACE_DISPATCH_STUB 32 +DEFINE_INTERFACE_DISPATCH_STUB 64 + +// Stub dispatch routine for dispatch to a vtable slot +LEAF_ENTRY RhpVTableOffsetDispatch, _TEXT + // On input we have the indirection cell data structure in r12. But we need more scratch registers and + // we may A/V on a null this. Both of these suggest we need a real prolog and epilog. + PROLOG_PUSH {r1} + + // r12 currently holds the indirection cell address. We need to update it to point to the vtable + // offset instead. + ldr r12, [r12, #OFFSETOF__InterfaceDispatchCell__m_pCache] + + // Load the MethodTable from the object instance in r0. + ldr r1, [r0] + + // add the vtable offset to the MethodTable pointer + add r12, r1, r12 + + // Load the target address of the vtable into r12 + ldr r12, [r12] + + EPILOG_POP {r1} + EPILOG_BRANCH_REG r12 +LEAF_END RhpVTableOffsetDispatch, _TEXT + +// Initial dispatch on an interface when we don't have a cache yet. +LEAF_ENTRY RhpInitialInterfaceDispatch, _TEXT + // The stub that jumped here pushed r12, which contains the interface dispatch cell + // we need to pop it here + pop { r12 } + + // Just tail call to the cache miss helper. + b C_FUNC(RhpInterfaceDispatchSlow) +LEAF_END RhpInitialInterfaceDispatch, _TEXT + +// No as alternate entry due to missed thumb bit in this case +// See https://github.com/dotnet/runtime/issues/8608 +LEAF_ENTRY RhpInitialDynamicInterfaceDispatch, _TEXT + // Just tail call to the cache miss helper. + b C_FUNC(RhpInterfaceDispatchSlow) +LEAF_END RhpInitialDynamicInterfaceDispatch, _TEXT + +// Cache miss case, call the runtime to resolve the target and update the cache. +// Use universal transition helper to allow an exception to flow out of resolution +LEAF_ENTRY RhpInterfaceDispatchSlow, _TEXT + // r12 has the interface dispatch cell address in it. + // The calling convention of the universal thunk is that the parameter + // for the universal thunk target is to be placed in sp-8 + // and the universal thunk target address is to be placed in sp-4 + str r12, [sp, #-8] + ldr r12, =C_FUNC(RhpCidResolve) + str r12, [sp, #-4] + + // jump to universal transition thunk + b C_FUNC(RhpUniversalTransition_DebugStepTailCall) +LEAF_END RhpInterfaceDispatchSlow, _TEXT + +#endif // FEATURE_CACHED_INTERFACE_DISPATCH diff --git a/src/coreclr/nativeaot/Runtime/arm/StubDispatch.asm b/src/coreclr/nativeaot/Runtime/arm/StubDispatch.asm new file mode 100644 index 00000000000000..cc24abdbf788b7 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/StubDispatch.asm @@ -0,0 +1,141 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + + +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH + + EXTERN RhpCidResolve + EXTERN RhpUniversalTransition_DebugStepTailCall + + ;; Macro that generates code to check a single cache entry. + MACRO + CHECK_CACHE_ENTRY $entry + ;; Check a single entry in the cache. + ;; R1 : Instance MethodTable* + ;; R2: Cache data structure + ;; R12 : Trashed. On succesful check, set to the target address to jump to. + + ldr r12, [r2, #(OFFSETOF__InterfaceDispatchCache__m_rgEntries + ($entry * 8))] + cmp r1, r12 + bne %ft0 + ldr r12, [r2, #(OFFSETOF__InterfaceDispatchCache__m_rgEntries + ($entry * 8) + 4)] + b %fa99 +0 + MEND + + +;; Macro that generates a stub consuming a cache with the given number of entries. + GBLS StubName + + MACRO + DEFINE_INTERFACE_DISPATCH_STUB $entries + +StubName SETS "RhpInterfaceDispatch$entries" + + NESTED_ENTRY $StubName + ;; On input we have the indirection cell data structure in r12. But we need more scratch registers and + ;; we may A/V on a null this. Both of these suggest we need a real prolog and epilog. + PROLOG_PUSH {r1-r2} + + ;; r12 currently holds the indirection cell address. We need to get the cache structure instead. + ldr r2, [r12, #OFFSETOF__InterfaceDispatchCell__m_pCache] + + ;; Load the MethodTable from the object instance in r0. + ldr r1, [r0] + + GBLA CurrentEntry +CurrentEntry SETA 0 + WHILE CurrentEntry < $entries + CHECK_CACHE_ENTRY CurrentEntry +CurrentEntry SETA CurrentEntry + 1 + WEND + + ;; Point r12 to the indirection cell using the back pointer in the cache block + ldr r12, [r2, #OFFSETOF__InterfaceDispatchCache__m_pCell] + + EPILOG_POP {r1-r2} + EPILOG_BRANCH RhpInterfaceDispatchSlow + + ;; Common epilog for cache hits. Have to out of line it here due to limitation on the number of + ;; epilogs imposed by the unwind code macros. +99 + ;; R2 contains address of the cache block. We store it in the red zone in case the target we jump + ;; to needs it. + ;; R12 contains the target address to jump to + EPILOG_POP r1 + ;; The red zone is only 8 bytes long, so we have to store r2 into it between the pops. + EPILOG_NOP str r2, [sp, #-4] + EPILOG_POP r2 + EPILOG_BRANCH_REG r12 + + NESTED_END $StubName + + MEND + +;; Define all the stub routines we currently need. + DEFINE_INTERFACE_DISPATCH_STUB 1 + DEFINE_INTERFACE_DISPATCH_STUB 2 + DEFINE_INTERFACE_DISPATCH_STUB 4 + DEFINE_INTERFACE_DISPATCH_STUB 8 + DEFINE_INTERFACE_DISPATCH_STUB 16 + DEFINE_INTERFACE_DISPATCH_STUB 32 + DEFINE_INTERFACE_DISPATCH_STUB 64 + + +;; Initial dispatch on an interface when we don't have a cache yet. + LEAF_ENTRY RhpInitialInterfaceDispatch + + ;; The stub that jumped here pushed r12, which contains the interface dispatch cell + ;; we need to pop it here + pop { r12 } + + ;; Simply tail call the slow dispatch helper. + b RhpInterfaceDispatchSlow + + LEAF_END RhpInitialInterfaceDispatch + + LEAF_ENTRY RhpVTableOffsetDispatch + ;; On input we have the indirection cell data structure in r12. But we need more scratch registers and + ;; we may A/V on a null this. Both of these suggest we need a real prolog and epilog. + PROLOG_PUSH {r1} + + ;; r12 currently holds the indirection cell address. We need to update it to point to the vtable + ;; offset instead. + ldr r12, [r12, #OFFSETOF__InterfaceDispatchCell__m_pCache] + + ;; Load the MethodTable from the object instance in r0. + ldr r1, [r0] + + ;; add the vtable offset to the MethodTable pointer + add r12, r1, r12 + + ;; Load the target address of the vtable into r12 + ldr r12, [r12] + + EPILOG_POP {r1} + EPILOG_BRANCH_REG r12 + LEAF_END RhpVTableOffsetDispatch + +;; Cache miss case, call the runtime to resolve the target and update the cache. + LEAF_ENTRY RhpInterfaceDispatchSlow + ALTERNATE_ENTRY RhpInitialDynamicInterfaceDispatch + ;; r12 has the interface dispatch cell address in it. + ;; The calling convention of the universal thunk is that the parameter + ;; for the universal thunk target is to be placed in sp-8 + ;; and the universal thunk target address is to be placed in sp-4 + str r12, [sp, #-8] + ldr r12, =RhpCidResolve + str r12, [sp, #-4] + + ;; jump to universal transition thunk + b RhpUniversalTransition_DebugStepTailCall + LEAF_END RhpInterfaceDispatchSlow + + +#endif // FEATURE_CACHED_INTERFACE_DISPATCH + + end diff --git a/src/coreclr/nativeaot/Runtime/arm/ThunkPoolThunks.asm b/src/coreclr/nativeaot/Runtime/arm/ThunkPoolThunks.asm new file mode 100644 index 00000000000000..3566b3bc60d4cf --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/ThunkPoolThunks.asm @@ -0,0 +1,273 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "kxarm.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; STUBS & DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +THUNK_CODESIZE equ 0x10 ;; 4-byte mov, 2-byte add, 4-byte str, 4-byte ldr, 2-byte branch +THUNK_DATASIZE equ 0x08 ;; 2 dwords + +THUNK_POOL_NUM_THUNKS_PER_PAGE equ 0xFA ;; 250 thunks per page + +PAGE_SIZE equ 0x1000 ;; 4K +POINTER_SIZE equ 0x04 + + MACRO + NAMED_READONLY_DATA_SECTION $name, $areaAlias + AREA $areaAlias,DATA,READONLY +RO$name % 4 + MEND + + MACRO + NAMED_READWRITE_DATA_SECTION $name, $areaAlias + AREA $areaAlias,DATA +RW$name % 4 + MEND + + MACRO + LOAD_DATA_ADDRESS $groupIndex, $index + ALIGN 0x10 ;; make sure we align to 16-byte boundary for CFG table + + ;; set r12 to begining of data page : r12 <- pc - (THUNK_CODESIZE * current thunk's index - sizeof(mov+add instructions)) + PAGE_SIZE + ;; fix offset of the data : r12 <- r12 + (THUNK_DATASIZE * current thunk's index) + mov.w r12, PAGE_SIZE + ($groupIndex * THUNK_DATASIZE * 10 + THUNK_DATASIZE * $index) - (8 + $groupIndex * THUNK_CODESIZE * 10 + THUNK_CODESIZE * $index) + add.n r12, r12, pc + MEND + + MACRO + JUMP_TO_COMMON $groupIndex, $index + ;; start : r12 points to the current thunks first data cell in the data page + ;; put r12 into the red zone : r12 isn't changed + ;; set r12 to begining of data page : r12 <- r12 - (THUNK_DATASIZE * current thunk's index) + ;; fix offset to point to last DWROD in page : r12 <- r11 + PAGE_SIZE - POINTER_SIZE + ;; jump to the location pointed at by the last dword in the data page + str.w r12, [sp, #-4] + ldr.w r12, [r12, #(PAGE_SIZE - POINTER_SIZE - ($groupIndex * THUNK_DATASIZE * 10 + THUNK_DATASIZE * $index))] + bx.n r12 + MEND + + MACRO + TenThunks $groupIndex + ;; Each thunk will load the address of its corresponding data (from the page that immediately follows) + ;; and call a common stub. The address of the common stub is setup by the caller (last dword + ;; in the thunks data section) depending on the 'kind' of thunks needed (interop, fat function pointers, etc...) + + ;; Each data block used by a thunk consists of two dword values: + ;; - Context: some value given to the thunk as context (passed in eax). Example for fat-fptrs: context = generic dictionary + ;; - Target : target code that the thunk eventually jumps to. + + LOAD_DATA_ADDRESS $groupIndex,0 + JUMP_TO_COMMON $groupIndex,0 + + LOAD_DATA_ADDRESS $groupIndex,1 + JUMP_TO_COMMON $groupIndex,1 + + LOAD_DATA_ADDRESS $groupIndex,2 + JUMP_TO_COMMON $groupIndex,2 + + LOAD_DATA_ADDRESS $groupIndex,3 + JUMP_TO_COMMON $groupIndex,3 + + LOAD_DATA_ADDRESS $groupIndex,4 + JUMP_TO_COMMON $groupIndex,4 + + LOAD_DATA_ADDRESS $groupIndex,5 + JUMP_TO_COMMON $groupIndex,5 + + LOAD_DATA_ADDRESS $groupIndex,6 + JUMP_TO_COMMON $groupIndex,6 + + LOAD_DATA_ADDRESS $groupIndex,7 + JUMP_TO_COMMON $groupIndex,7 + + LOAD_DATA_ADDRESS $groupIndex,8 + JUMP_TO_COMMON $groupIndex,8 + + LOAD_DATA_ADDRESS $groupIndex,9 + JUMP_TO_COMMON $groupIndex,9 + MEND + + MACRO + THUNKS_PAGE_BLOCK + + TenThunks 0 + TenThunks 1 + TenThunks 2 + TenThunks 3 + TenThunks 4 + TenThunks 5 + TenThunks 6 + TenThunks 7 + TenThunks 8 + TenThunks 9 + TenThunks 10 + TenThunks 11 + TenThunks 12 + TenThunks 13 + TenThunks 14 + TenThunks 15 + TenThunks 16 + TenThunks 17 + TenThunks 18 + TenThunks 19 + TenThunks 20 + TenThunks 21 + TenThunks 22 + TenThunks 23 + TenThunks 24 + MEND + + ;; + ;; The first thunks section should be 64K aligned because it can get + ;; mapped multiple times in memory, and mapping works on allocation + ;; granularity boundaries (we don't want to map more than what we need) + ;; + ;; The easiest way to do so is by having the thunks section at the + ;; first 64K aligned virtual address in the binary. We provide a section + ;; layout file to the linker to tell it how to layout the thunks sections + ;; that we care about. (ndp\rh\src\runtime\DLLs\app\mrt100_app_sectionlayout.txt) + ;; + ;; The PE spec says images cannot have gaps between sections (other + ;; than what is required by the section alignment value in the header), + ;; therefore we need a couple of padding data sections (otherwise the + ;; OS will not load the image). + ;; + + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment0, "|.pad0|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment1, "|.pad1|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment2, "|.pad2|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment3, "|.pad3|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment4, "|.pad4|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment5, "|.pad5|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment6, "|.pad6|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment7, "|.pad7|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment8, "|.pad8|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment9, "|.pad9|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment10, "|.pad10|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment11, "|.pad11|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment12, "|.pad12|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment13, "|.pad13|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment14, "|.pad14|" + + ;; + ;; Thunk Stubs + ;; NOTE: Keep number of blocks in sync with macro/constant named 'NUM_THUNK_BLOCKS' in: + ;; - ndp\FxCore\src\System.Private.CoreLib\System\Runtime\InteropServices\ThunkPool.cs + ;; - ndp\rh\src\tools\rhbind\zapimage.h + ;; + LEAF_ENTRY ThunkPool, "|.tks0|" + THUNKS_PAGE_BLOCK + LEAF_END ThunkPool + + NAMED_READWRITE_DATA_SECTION ThunkData0, "|.tkd0|" + + LEAF_ENTRY ThunkPool1, "|.tks1|" + THUNKS_PAGE_BLOCK + LEAF_END ThunkPool1 + + NAMED_READWRITE_DATA_SECTION ThunkData1, "|.tkd1|" + + LEAF_ENTRY ThunkPool2, "|.tks2|" + THUNKS_PAGE_BLOCK + LEAF_END ThunkPool2 + + NAMED_READWRITE_DATA_SECTION ThunkData2, "|.tkd2|" + + LEAF_ENTRY ThunkPool3, "|.tks3|" + THUNKS_PAGE_BLOCK + LEAF_END ThunkPool3 + + NAMED_READWRITE_DATA_SECTION ThunkData3, "|.tkd3|" + + LEAF_ENTRY ThunkPool4, "|.tks4|" + THUNKS_PAGE_BLOCK + LEAF_END ThunkPool4 + + NAMED_READWRITE_DATA_SECTION ThunkData4, "|.tkd4|" + + LEAF_ENTRY ThunkPool5, "|.tks5|" + THUNKS_PAGE_BLOCK + LEAF_END ThunkPool5 + + NAMED_READWRITE_DATA_SECTION ThunkData5, "|.tkd5|" + + LEAF_ENTRY ThunkPool6, "|.tks6|" + THUNKS_PAGE_BLOCK + LEAF_END ThunkPool6 + + NAMED_READWRITE_DATA_SECTION ThunkData6, "|.tkd6|" + + LEAF_ENTRY ThunkPool7, "|.tks7|" + THUNKS_PAGE_BLOCK + LEAF_END ThunkPool7 + + NAMED_READWRITE_DATA_SECTION ThunkData7, "|.tkd7|" + + + ;; + ;; IntPtr RhpGetThunksBase() + ;; + LEAF_ENTRY RhpGetThunksBase + ;; Return the address of the first thunk pool to the caller (this is really the base address) + ldr r0, =ThunkPool + sub r0, r0, #1 + bx lr + LEAF_END RhpGetThunksBase + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; General Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;; + ;; int RhpGetNumThunksPerBlock() + ;; + LEAF_ENTRY RhpGetNumThunksPerBlock + mov r0, THUNK_POOL_NUM_THUNKS_PER_PAGE + bx lr + LEAF_END RhpGetNumThunksPerBlock + + ;; + ;; int RhpGetThunkSize() + ;; + LEAF_ENTRY RhpGetThunkSize + mov r0, THUNK_CODESIZE + bx lr + LEAF_END RhpGetThunkSize + + ;; + ;; int RhpGetNumThunkBlocksPerMapping() + ;; + LEAF_ENTRY RhpGetNumThunkBlocksPerMapping + mov r0, 8 + bx lr + LEAF_END RhpGetNumThunkBlocksPerMapping + + ;; + ;; int RhpGetThunkBlockSize + ;; + LEAF_ENTRY RhpGetThunkBlockSize + mov r0, PAGE_SIZE * 2 + bx lr + LEAF_END RhpGetThunkBlockSize + + ;; + ;; IntPtr RhpGetThunkDataBlockAddress(IntPtr thunkStubAddress) + ;; + LEAF_ENTRY RhpGetThunkDataBlockAddress + mov r12, PAGE_SIZE - 1 + bic r0, r0, r12 + add r0, PAGE_SIZE + bx lr + LEAF_END RhpGetThunkDataBlockAddress + + ;; + ;; IntPtr RhpGetThunkStubsBlockAddress(IntPtr thunkDataAddress) + ;; + LEAF_ENTRY RhpGetThunkStubsBlockAddress + mov r12, PAGE_SIZE - 1 + bic r0, r0, r12 + sub r0, PAGE_SIZE + bx lr + LEAF_END RhpGetThunkStubsBlockAddress + + END diff --git a/src/coreclr/nativeaot/Runtime/arm/UniversalTransition.S b/src/coreclr/nativeaot/Runtime/arm/UniversalTransition.S new file mode 100644 index 00000000000000..46a3929b9301a2 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/UniversalTransition.S @@ -0,0 +1,157 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp +#include + +#ifdef FEATURE_DYNAMIC_CODE + +#ifdef _DEBUG +#define TRASH_SAVED_ARGUMENT_REGISTERS +#endif + +#define COUNT_ARG_REGISTERS (4) +#define INTEGER_REGISTER_SIZE (4) +#define ARGUMENT_REGISTERS_SIZE (COUNT_ARG_REGISTERS * INTEGER_REGISTER_SIZE) + +// Largest return block is 4 doubles +#define RETURN_BLOCK_SIZE (32) + +#define COUNT_FLOAT_ARG_REGISTERS (8) +#define FLOAT_REGISTER_SIZE (8) +#define FLOAT_ARG_REGISTERS_SIZE (COUNT_FLOAT_ARG_REGISTERS * FLOAT_REGISTER_SIZE) + +#define PUSHED_LR_SIZE (4) +#define PUSHED_R11_SIZE (4) + +// +// From CallerSP to ChildSP, the stack frame is composed of the following adjacent regions: +// +// ARGUMENT_REGISTERS_SIZE +// RETURN_BLOCK_SIZE +// FLOAT_ARG_REGISTERS_SIZE +// PUSHED_LR +// PUSHED_R11 + + +#define DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK (PUSHED_R11_SIZE + PUSHED_LR_SIZE + FLOAT_ARG_REGISTERS_SIZE) + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpUniversalTransition +// +// At input to this function, r0-3, d0-7 and the stack may contain any number of arguments. +// +// In addition, there are 2 extra arguments passed in the RED ZONE (8 byte negative space +// off of sp). +// sp-4 will contain the managed function that is to be called by this transition function +// sp-8 will contain the pointer sized extra argument to the managed function +// +// When invoking the callee: +// +// r0 shall contain a pointer to the TransitionBlock +// r1 shall contain the value that was in sp-8 at entry to this function +// +// Frame layout is: +// +// {StackPassedArgs} ChildSP+078 CallerSP+000 +// {IntArgRegs (r0-r3) (0x10 bytes)} ChildSP+068 CallerSP-010 +// {ReturnBlock (0x20 bytes)} ChildSP+048 CallerSP-030 +// -- The base address of the Return block is the TransitionBlock pointer, the floating point args are +// in the neg space of the TransitionBlock pointer. Note that the callee has knowledge of the exact +// layout of all pieces of the frame that lie at or above the pushed floating point registers. +// {FpArgRegs (d0-d7) (0x40 bytes)} ChildSP+008 CallerSP-070 +// {PushedLR} ChildSP+004 CallerSP-074 +// {PushedR11} ChildSP+000 CallerSP-078 +// +// NOTE: If the frame layout ever changes, the C++ UniversalTransitionStackFrame structure +// must be updated as well. +// +// NOTE: The callee receives a pointer to the base of the ReturnBlock, and the callee has +// knowledge of the exact layout of all pieces of the frame that lie at or above the pushed +// FpArgRegs. +// +// NOTE: The stack walker guarantees that conservative GC reporting will be applied to +// everything between the base of the ReturnBlock and the top of the StackPassedArgs. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +.macro UNIVERSAL_TRANSITION FunctionName + +NESTED_ENTRY Rhp\FunctionName, _TEXT, NoHandler + // Save argument registers (including floating point) and the return address. + // NOTE: While we do that, capture the two arguments in the red zone into r12 and r3. + ldr r12, [sp, #-4] // Capture first argument from red zone into r12 + PROLOG_PUSH "{r3}" // Push r3 + ldr r3, [sp, #-4] // Capture second argument from red zone into r3 + PROLOG_PUSH "{r0-r2}" // Push the rest of the registers + PROLOG_STACK_ALLOC RETURN_BLOCK_SIZE // Save space a buffer to be used to hold return buffer data. + PROLOG_VPUSH {d0-d7} // Capture the floating point argument registers + PROLOG_PUSH "{r11,lr}" // Save caller's frame chain pointer and PC + + // Setup the arguments to the transition thunk. + mov r1, r3 + +#ifdef TRASH_SAVED_ARGUMENT_REGISTERS + + // Before calling out, trash all of the argument registers except the ones (r0, r1) that + // hold outgoing arguments. All of these registers have been saved to the transition + // frame, and the code at the call target is required to use only the transition frame + // copies when dispatching this call to the eventual callee. + + ldr r3, =C_FUNC(RhpFpTrashValues) + vldr d0, [r3, #(0 * 8)] + vldr d1, [r3, #(1 * 8)] + vldr d2, [r3, #(2 * 8)] + vldr d3, [r3, #(3 * 8)] + vldr d4, [r3, #(4 * 8)] + vldr d5, [r3, #(5 * 8)] + vldr d6, [r3, #(6 * 8)] + vldr d7, [r3, #(7 * 8)] + + ldr r3, =C_FUNC(RhpIntegerTrashValues) + ldr r2, [r3, #(2 * 4)] + ldr r3, [r3, #(3 * 4)] + +#endif // TRASH_SAVED_ARGUMENT_REGISTERS + + // Make the ReturnFromUniversalTransition alternate entry 4 byte aligned + .balign 4 + add r0, sp, #DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK // First parameter to target function is a pointer to the return block + blx r12 + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom\FunctionName + + // We cannot make the label public as that tricks DIA stackwalker into thinking + // it's the beginning of a method. For this reason we export an auxiliary variable + // holding the address instead. + + // Move the result (the target address) to r12 so it doesn't get overridden when we restore the + // argument registers. Additionally make sure the thumb2 bit is set. + orr r12, r0, #1 + + // Restore caller's frame chain pointer and PC. + EPILOG_POP "{r11,lr}" + + // Restore the argument registers. + EPILOG_VPOP {d0-d7} + EPILOG_STACK_FREE RETURN_BLOCK_SIZE // pop return block conservatively reported area + EPILOG_POP "{r0-r3}" + + // Tailcall to the target address. + EPILOG_BRANCH_REG r12 + +NESTED_END Rhp\FunctionName, _TEXT + +.endm + +// To enable proper step-in behavior in the debugger, we need to have two instances +// of the thunk. For the first one, the debugger steps into the call in the function, +// for the other, it steps over it. +UNIVERSAL_TRANSITION UniversalTransition +UNIVERSAL_TRANSITION UniversalTransition_DebugStepTailCall + +#endif // FEATURE_DYNAMIC_CODE diff --git a/src/coreclr/nativeaot/Runtime/arm/UniversalTransition.asm b/src/coreclr/nativeaot/Runtime/arm/UniversalTransition.asm new file mode 100644 index 00000000000000..818e48b57fda10 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/UniversalTransition.asm @@ -0,0 +1,157 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + +#ifdef _DEBUG +#define TRASH_SAVED_ARGUMENT_REGISTERS +#endif + +#ifdef TRASH_SAVED_ARGUMENT_REGISTERS + EXTERN RhpIntegerTrashValues + EXTERN RhpFpTrashValues +#endif ;; TRASH_SAVED_ARGUMENT_REGISTERS + +#define COUNT_ARG_REGISTERS (4) +#define INTEGER_REGISTER_SIZE (4) +#define ARGUMENT_REGISTERS_SIZE (COUNT_ARG_REGISTERS * INTEGER_REGISTER_SIZE) + +;; Largest return block is 4 doubles +#define RETURN_BLOCK_SIZE (32) + +#define COUNT_FLOAT_ARG_REGISTERS (8) +#define FLOAT_REGISTER_SIZE (8) +#define FLOAT_ARG_REGISTERS_SIZE (COUNT_FLOAT_ARG_REGISTERS * FLOAT_REGISTER_SIZE) + +#define PUSHED_LR_SIZE (4) +#define PUSHED_R11_SIZE (4) + +;; +;; From CallerSP to ChildSP, the stack frame is composed of the following adjacent regions: +;; +;; ARGUMENT_REGISTERS_SIZE +;; RETURN_BLOCK_SIZE +;; FLOAT_ARG_REGISTERS_SIZE +;; PUSHED_LR +;; PUSHED_R11 +;; + +#define DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK (PUSHED_R11_SIZE + PUSHED_LR_SIZE + FLOAT_ARG_REGISTERS_SIZE) + + TEXTAREA + +;; +;; RhpUniversalTransition +;; +;; At input to this function, r0-3, d0-7 and the stack may contain any number of arguments. +;; +;; In addition, there are 2 extra arguments passed in the RED ZONE (8 byte negative space +;; off of sp). +;; sp-4 will contain the managed function that is to be called by this transition function +;; sp-8 will contain the pointer sized extra argument to the managed function +;; +;; When invoking the callee: +;; +;; r0 shall contain a pointer to the TransitionBlock +;; r1 shall contain the value that was in sp-8 at entry to this function +;; +;; Frame layout is: +;; +;; {StackPassedArgs} ChildSP+078 CallerSP+000 +;; {IntArgRegs (r0-r3) (0x10 bytes)} ChildSP+068 CallerSP-010 +;; {ReturnBlock (0x20 bytes)} ChildSP+048 CallerSP-030 +;; -- The base address of the Return block is the TransitionBlock pointer, the floating point args are +;; in the neg space of the TransitionBlock pointer. Note that the callee has knowledge of the exact +;; layout of all pieces of the frame that lie at or above the pushed floating point registers. +;; {FpArgRegs (d0-d7) (0x40 bytes)} ChildSP+008 CallerSP-070 +;; {PushedLR} ChildSP+004 CallerSP-074 +;; {PushedR11} ChildSP+000 CallerSP-078 +;; +;; NOTE: If the frame layout ever changes, the C++ UniversalTransitionStackFrame structure +;; must be updated as well. +;; +;; NOTE: The callee receives a pointer to the base of the ReturnBlock, and the callee has +;; knowledge of the exact layout of all pieces of the frame that lie at or above the pushed +;; FpArgRegs. +;; +;; NOTE: The stack walker guarantees that conservative GC reporting will be applied to +;; everything between the base of the ReturnBlock and the top of the StackPassedArgs. +;; + + MACRO + UNIVERSAL_TRANSITION $FunctionName + + NESTED_ENTRY Rhp$FunctionName + ;; Save argument registers (including floating point) and the return address. + ;; NOTE: While we do that, capture the two arguments in the red zone into r12 and r3. + PROLOG_NOP ldr r12, [sp, #-4] ; Capture first argument from red zone into r12 + PROLOG_PUSH {r3} ; Push r3 + PROLOG_NOP ldr r3, [sp, #-4] ; Capture second argument from red zone into r3 + PROLOG_PUSH {r0-r2} ; Push the rest of the registers + PROLOG_STACK_ALLOC RETURN_BLOCK_SIZE ; Save space a buffer to be used to hold return buffer data. + PROLOG_VPUSH {d0-d7} ; Capture the floating point argument registers + PROLOG_PUSH {r11,lr} ; Save caller's frame chain pointer and PC + + ;; Setup the arguments to the transition thunk. + mov r1, r3 + +#ifdef TRASH_SAVED_ARGUMENT_REGISTERS + + ;; Before calling out, trash all of the argument registers except the ones (r0, r1) that + ;; hold outgoing arguments. All of these registers have been saved to the transition + ;; frame, and the code at the call target is required to use only the transition frame + ;; copies when dispatching this call to the eventual callee. + + ldr r3, =RhpFpTrashValues + vldr d0, [r3, #(0 * 8)] + vldr d1, [r3, #(1 * 8)] + vldr d2, [r3, #(2 * 8)] + vldr d3, [r3, #(3 * 8)] + vldr d4, [r3, #(4 * 8)] + vldr d5, [r3, #(5 * 8)] + vldr d6, [r3, #(6 * 8)] + vldr d7, [r3, #(7 * 8)] + + ldr r3, =RhpIntegerTrashValues + ldr r2, [r3, #(2 * 4)] + ldr r3, [r3, #(3 * 4)] + +#endif // TRASH_SAVED_ARGUMENT_REGISTERS + + ;; Make the ReturnFromUniversalTransition alternate entry 4 byte aligned + ALIGN 4 + add r0, sp, #DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK ;; First parameter to target function is a pointer to the return block + blx r12 + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom$FunctionName + + ; We cannot make the label public as that tricks DIA stackwalker into thinking + ; it's the beginning of a method. For this reason we export an auxiliary variable + ; holding the address instead. + + ;; Move the result (the target address) to r12 so it doesn't get overridden when we restore the + ;; argument registers. Additionally make sure the thumb2 bit is set. + orr r12, r0, #1 + + ;; Restore caller's frame chain pointer and PC. + EPILOG_POP {r11,lr} + + ;; Restore the argument registers. + EPILOG_VPOP {d0-d7} + EPILOG_STACK_FREE RETURN_BLOCK_SIZE ; pop return block conservatively reported area + EPILOG_POP {r0-r3} + + ;; Tailcall to the target address. + EPILOG_BRANCH_REG r12 + + NESTED_END Rhp$FunctionName + + MEND + + ; To enable proper step-in behavior in the debugger, we need to have two instances + ; of the thunk. For the first one, the debugger steps into the call in the function, + ; for the other, it steps over it. + UNIVERSAL_TRANSITION UniversalTransition + UNIVERSAL_TRANSITION UniversalTransition_DebugStepTailCall + + END diff --git a/src/coreclr/nativeaot/Runtime/arm/WriteBarriers.S b/src/coreclr/nativeaot/Runtime/arm/WriteBarriers.S new file mode 100644 index 00000000000000..478561f5ee4743 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/WriteBarriers.S @@ -0,0 +1,382 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp +#include + +#ifdef WRITE_BARRIER_CHECK + +.macro UPDATE_GC_SHADOW BASENAME, REFREG, DESTREG + + // If g_GCShadow is 0, don't perform the check. + ldr r12, =C_FUNC(g_GCShadow) + ldr r12, [r12] + cbz r12, LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Done_\REFREG) + + // Save DESTREG since we're about to modify it (and we need the original value both within the macro and + // once we exit the macro). Note that this is naughty since we're altering the stack pointer outside of + // the prolog inside a method without a frame. But given that this is only debug code and generally we + // shouldn't be walking the stack at this point it seems preferable to recoding the all the barrier + // variants to set up frames. The compiler knows exactly which registers are trashed in the simple write + // barrier case, so we don't have any more scratch registers to play with (and doing so would only make + // things harder if at a later stage we want to allow multiple barrier versions based on the input + // registers). + push \DESTREG + + // Transform DESTREG into the equivalent address in the shadow heap. + ldr r12, =C_FUNC(g_lowest_address) + ldr r12, [r12] + sub \DESTREG, r12 + cmp \DESTREG, #0 + blo LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_PopThenDone_\REFREG) + ldr r12, =C_FUNC(g_GCShadow) + ldr r12, [r12] + add \DESTREG, r12 + ldr r12, =C_FUNC(g_GCShadowEnd) + ldr r12, [r12] + cmp \DESTREG, r12 + jhi LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_PopThenDone_\REFREG) + + // Update the shadow heap. + str \REFREG, [\DESTREG] + + // The following read must be strongly ordered wrt to the write we've just performed in order to + // prevent race conditions. + dmb + + // Now check that the real heap location still contains the value we just wrote into the shadow heap. + mov r12, \DESTREG + ldr \DESTREG, [sp] + str r12, [sp] + ldr r12, [\DESTREG] + cmp r12, \REFREG + bne LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Invalidate_\REFREG) + + // The original DESTREG value is now restored but the stack has a value (the shadow version of the + // location) pushed. Need to discard this push before we are done. + add sp, #4 + b LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Done_\REFREG) + +LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Invalidate_\REFREG): + // Someone went and updated the real heap. We need to invalidate the shadow location since we can't + // guarantee whose shadow update won. + + // Retrieve shadow location from the stack and restore original DESTREG to the stack. This is an + // additional memory barrier we don't require but it's on the rare path and x86 doesn't have an xchg + // variant that doesn't implicitly specify the lock prefix. Note that INVALIDGCVALUE is a 32-bit + // immediate and therefore must be moved into a register before it can be written to the shadow + // location. + mov r12, \DESTREG + ldr \DESTREG, [sp] + str r12, [sp] + push \REFREG + movw \REFREG, #(INVALIDGCVALUE & 0xFFFF) + movt \REFREG, #(INVALIDGCVALUE >> 16) + str \REFREG, [\DESTREG] + pop \REFREG + +LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_PopThenDone_\REFREG): + // Restore original DESTREG value from the stack. + pop \DESTREG + +LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Done_\REFREG): + +.endm + +#else // WRITE_BARRIER_CHECK + +.macro UPDATE_GC_SHADOW BASENAME, REFREG, DESTREG +.endm + +#endif // WRITE_BARRIER_CHECK + +// There are several different helpers used depending on which register holds the object reference. Since all +// the helpers have identical structure we use a macro to define this structure. Two arguments are taken, the +// name of the register that points to the location to be updated and the name of the register that holds the +// object reference (this should be in upper case as it's used in the definition of the name of the helper). +.macro DEFINE_UNCHECKED_WRITE_BARRIER_CORE BASENAME, REFREG + + // Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + // we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW \BASENAME, \REFREG, r0 + + // If the reference is to an object that's not in an ephemeral generation we have no need to track it + // (since the object won't be collected or moved by an ephemeral collection). + ldr r12, =C_FUNC(g_ephemeral_low) + ldr r12, [r12] + cmp \REFREG, r12 + blo LOCAL_LABEL(\BASENAME\()_EXIT_\REFREG) + + ldr r12, =C_FUNC(g_ephemeral_high) + ldr r12, [r12] + cmp \REFREG, r12 + bhi LOCAL_LABEL(\BASENAME\()_EXIT_\REFREG) + + // We have a location on the GC heap being updated with a reference to an ephemeral object so we must + // track this write. The location address is translated into an offset in the card table bitmap. We set + // an entire byte in the card table since it's quicker than messing around with bitmasks and we only write + // the byte if it hasn't already been done since writes are expensive and impact scaling. + ldr r12, =C_FUNC(g_card_table) + ldr r12, [r12] + add r0, r12, r0, lsr #LOG2_CLUMP_SIZE + ldrb r12, [r0] + cmp r12, #0x0FF + bne LOCAL_LABEL(\BASENAME\()_UpdateCardTable_\REFREG) + +LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG): + b LOCAL_LABEL(\BASENAME\()_EXIT_\REFREG) + +// We get here if it's necessary to update the card table. +LOCAL_LABEL(\BASENAME\()_UpdateCardTable_\REFREG): + mov r12, #0x0FF + strb r12, [r0] + +LOCAL_LABEL(\BASENAME\()_EXIT_\REFREG): + +.endm + +// There are several different helpers used depending on which register holds the object reference. Since all +// the helpers have identical structure we use a macro to define this structure. One argument is taken, the +// name of the register that will hold the object reference (this should be in upper case as it's used in the +// definition of the name of the helper). +.macro DEFINE_UNCHECKED_WRITE_BARRIER REFREG, EXPORT_REG_NAME + +// Define a helper with a name of the form RhpAssignRefEAX etc. (along with suitable calling standard +// decoration). The location to be updated is in DESTREG. The object reference that will be assigned into that +// location is in one of the other general registers determined by the value of REFREG. + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at WriteBarrierFunctionAvLOC +// - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address +LEAF_ENTRY RhpAssignRef\EXPORT_REG_NAME, _TEXT + +// Export the canonical write barrier under unqualified name as well +.ifc \REFREG, r1 +ALTERNATE_ENTRY RhpAssignRef +.endif + + // Use the GC write barrier as a convenient place to implement the managed memory model for ARM. The + // intent is that writes to the target object ($REFREG) will be visible across all CPUs before the + // write to the destination ($DESTREG). This covers most of the common scenarios where the programmer + // might assume strongly ordered accessess, namely where the preceding writes are used to initialize + // the object and the final write, made by this barrier in the instruction following the DMB, + // publishes that object for other threads/cpus to see. + // + // Note that none of this is relevant for single cpu machines. We may choose to implement a + // uniprocessor specific version of this barrier if uni-proc becomes a significant scenario again. + dmb + + // Write the reference into the location. Note that we rely on the fact that no GC can occur between here + // and the card table update we may perform below. +ALTERNATE_ENTRY "RhpAssignRefAvLocation"\EXPORT_REG_NAME // WriteBarrierFunctionAvLocation +.ifc \REFREG, r1 +ALTERNATE_ENTRY RhpAssignRefAVLocation +.endif + str \REFREG, [r0] + + DEFINE_UNCHECKED_WRITE_BARRIER_CORE RhpAssignRef, \REFREG + + bx lr +LEAF_END RhpAssignRef\EXPORT_REG_NAME, _TEXT +.endm + +// One day we might have write barriers for all the possible argument registers but for now we have +// just one write barrier that assumes the input register is RSI. +DEFINE_UNCHECKED_WRITE_BARRIER r1, r1 + +// +// Define the helpers used to implement the write barrier required when writing an object reference into a +// location residing on the GC heap. Such write barriers allow the GC to optimize which objects in +// non-ephemeral generations need to be scanned for references to ephemeral objects during an ephemeral +// collection. +// + +.macro DEFINE_CHECKED_WRITE_BARRIER_CORE BASENAME, REFREG + + // The location being updated might not even lie in the GC heap (a handle or stack location for instance), + // in which case no write barrier is required. + ldr r12, =C_FUNC(g_lowest_address) + ldr r12, [r12] + cmp r0, r12 + blo LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG) + ldr r12, =C_FUNC(g_highest_address) + ldr r12, [r12] + cmp r0, r12 + bhi LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG) + + DEFINE_UNCHECKED_WRITE_BARRIER_CORE \BASENAME, \REFREG + +.endm + +// There are several different helpers used depending on which register holds the object reference. Since all +// the helpers have identical structure we use a macro to define this structure. One argument is taken, the +// name of the register that will hold the object reference (this should be in upper case as it's used in the +// definition of the name of the helper). +.macro DEFINE_CHECKED_WRITE_BARRIER REFREG, EXPORT_REG_NAME + +// Define a helper with a name of the form RhpCheckedAssignRefEAX etc. (along with suitable calling standard +// decoration). The location to be updated is always in R0. The object reference that will be assigned into +// that location is in one of the other general registers determined by the value of REFREG. + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen on the first instruction +// - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address +LEAF_ENTRY RhpCheckedAssignRef\EXPORT_REG_NAME, _TEXT + +// Export the canonical write barrier under unqualified name as well +.ifc \REFREG, r1 +ALTERNATE_ENTRY RhpCheckedAssignRef +.endif + + // Use the GC write barrier as a convenient place to implement the managed memory model for ARM. The + // intent is that writes to the target object ($REFREG) will be visible across all CPUs before the + // write to the destination ($DESTREG). This covers most of the common scenarios where the programmer + // might assume strongly ordered accessess, namely where the preceding writes are used to initialize + // the object and the final write, made by this barrier in the instruction following the DMB, + // publishes that object for other threads/cpus to see. + // + // Note that none of this is relevant for single cpu machines. We may choose to implement a + // uniprocessor specific version of this barrier if uni-proc becomes a significant scenario again. + dmb + // Write the reference into the location. Note that we rely on the fact that no GC can occur between here + // and the card table update we may perform below. +ALTERNATE_ENTRY "RhpCheckedAssignRefAvLocation"\EXPORT_REG_NAME // WriteBarrierFunctionAvLocation +.ifc \REFREG, r1 +ALTERNATE_ENTRY RhpCheckedAssignRefAVLocation +.endif + str \REFREG, [r0] + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedAssignRef, \REFREG + + bx lr +LEAF_END RhpCheckedAssignRef\EXPORT_REG_NAME, _TEXT +.endm + +// One day we might have write barriers for all the possible argument registers but for now we have +// just one write barrier that assumes the input register is RSI. +DEFINE_CHECKED_WRITE_BARRIER r1, r1 + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedLockCmpXchgAVLocation +// - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address +// r0 = destination address +// r1 = value +// r2 = comparand +LEAF_ENTRY RhpCheckedLockCmpXchg, _TEXT + // To implement our chosen memory model for ARM we insert a memory barrier at GC write brriers. This + // barrier must occur before the object reference update, so we have to do it unconditionally even + // though the update may fail below. + dmb +ALTERNATE_ENTRY RhpCheckedLockCmpXchgAVLocation +LOCAL_LABEL(RhpCheckedLockCmpXchgRetry): + ldrex r3, [r0] + cmp r2, r3 + bne LOCAL_LABEL(RhpCheckedLockCmpXchg_NoBarrierRequired_r1) + strex r3, r1, [r0] + cmp r3, #0 + bne LOCAL_LABEL(RhpCheckedLockCmpXchgRetry) + mov r3, r2 + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedLockCmpXchg, r1 + + mov r0, r3 + bx lr +LEAF_END RhpCheckedLockCmpXchg, _TEXT + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedXchgAVLocation +// - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address +// r0 = destination address +// r1 = value +LEAF_ENTRY RhpCheckedXchg, _TEXT + // To implement our chosen memory model for ARM we insert a memory barrier at GC write barriers. This + // barrier must occur before the object reference update. + dmb +ALTERNATE_ENTRY RhpCheckedXchgAVLocation +LOCAL_LABEL(RhpCheckedXchgRetry): + ldrex r2, [r0] + strex r3, r1, [r0] + cmp r3, #0 + bne LOCAL_LABEL(RhpCheckedXchgRetry) + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedXchg, r1 + + // The original value is currently in r2. We need to return it in r0. + mov r0, r2 + + bx lr +LEAF_END RhpCheckedXchg, _TEXT + +// +// RhpByRefAssignRef simulates movs instruction for object references. +// +// On entry: +// r0: address of ref-field (assigned to) +// r1: address of the data (source) +// r2, r3: be trashed +// +// On exit: +// r0, r1 are incremented by 4, +// r2, r3: trashed +// +LEAF_ENTRY RhpByRefAssignRef, _TEXT + ldr r2, [r1] + str r2, [r0] + + // Check whether the writes were even into the heap. If not there's no card update required. + ldr r3, =C_FUNC(g_lowest_address) + ldr r3, [r3] + cmp r0, r3 + blo LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) + ldr r3, =C_FUNC(g_highest_address) + ldr r3, [r3] + cmp r0, r3 + bhi LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) + + // Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + // we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW BASENAME, r2, r0 + + // If the reference is to an object that's not in an ephemeral generation we have no need to track it + // (since the object won't be collected or moved by an ephemeral collection). + ldr r3, =C_FUNC(g_ephemeral_low) + ldr r3, [r3] + cmp r2, r3 + blo LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) + ldr r3, =C_FUNC(g_ephemeral_high) + ldr r3, [r3] + cmp r2, r3 + bhi LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) + + // move current r0 value into r2 and then increment the pointers + mov r2, r0 + add r1, #4 + add r0, #4 + + // We have a location on the GC heap being updated with a reference to an ephemeral object so we must + // track this write. The location address is translated into an offset in the card table bitmap. We set + // an entire byte in the card table since it's quicker than messing around with bitmasks and we only write + // the byte if it hasn't already been done since writes are expensive and impact scaling. + ldr r3, =C_FUNC(g_card_table) + ldr r3, [r3] + add r2, r3, r2, lsr #LOG2_CLUMP_SIZE + ldrb r3, [r2] + cmp r3, #0x0FF + bne LOCAL_LABEL(RhpByRefAssignRef_UpdateCardTable) + bx lr + +// We get here if it's necessary to update the card table. +LOCAL_LABEL(RhpByRefAssignRef_UpdateCardTable): + mov r3, #0x0FF + strb r3, [r2] + bx lr + +LOCAL_LABEL(RhpByRefAssignRef_NotInHeap): + // Increment the pointers before leaving + add r0, #4 + add r1, #4 + bx lr +LEAF_END RhpByRefAssignRef, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm/WriteBarriers.asm b/src/coreclr/nativeaot/Runtime/arm/WriteBarriers.asm new file mode 100644 index 00000000000000..f2a663e750a5c2 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm/WriteBarriers.asm @@ -0,0 +1,421 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +;; +;; Define the helpers used to implement the write barrier required when writing an object reference into a +;; location residing on the GC heap. Such write barriers allow the GC to optimize which objects in +;; non-ephemeral generations need to be scanned for references to ephemeral objects during an ephemeral +;; collection. +;; + +#include "AsmMacros.h" + + TEXTAREA + +;; Macro used to copy contents of newly updated GC heap locations to a shadow copy of the heap. This is used +;; during garbage collections to verify that object references where never written to the heap without using a +;; write barrier. Note that we're potentially racing to update the shadow heap while other threads are writing +;; new references to the real heap. Since this can't be solved perfectly without critical sections around the +;; entire update process, we instead update the shadow location and then re-check the real location (as two +;; ordered operations) and if there is a disparity we'll re-write the shadow location with a special value +;; (INVALIDGCVALUE) which disables the check for that location. Since the shadow heap is only validated at GC +;; time and these write barrier operations are atomic wrt to GCs this is sufficient to guarantee that the +;; shadow heap contains only valid copies of real heap values or INVALIDGCVALUE. +#ifdef WRITE_BARRIER_CHECK + + SETALIAS g_GCShadow, ?g_GCShadow@@3PAEA + SETALIAS g_GCShadowEnd, ?g_GCShadowEnd@@3PAEA + + EXTERN $g_GCShadow + EXTERN $g_GCShadowEnd + + MACRO + ;; On entry: + ;; $DESTREG: location to be updated + ;; $REFREG: objectref to be stored + ;; + ;; On exit: + ;; r12: trashed + ;; other registers are preserved + ;; + UPDATE_GC_SHADOW $DESTREG, $REFREG + + ;; If g_GCShadow is 0, don't perform the check. + ldr r12, =$g_GCShadow + ldr r12, [r12] + cmp r12, 0 + beq %ft1 + + ;; Save $DESTREG since we're about to modify it (and we need the original value both within the macro and + ;; once we exit the macro). Note that this is naughty since we're altering the stack pointer outside of + ;; the prolog inside a method without a frame. But given that this is only debug code and generally we + ;; shouldn't be walking the stack at this point it seems preferable to recoding the all the barrier + ;; variants to set up frames. The compiler knows exactly which registers are trashed in the simple write + ;; barrier case, so we don't have any more scratch registers to play with (and doing so would only make + ;; things harder if at a later stage we want to allow multiple barrier versions based on the input + ;; registers). + push $DESTREG + + ;; Transform $DESTREG into the equivalent address in the shadow heap. + ldr r12, =$G_LOWEST_ADDRESS + ldr r12, [r12] + subs $DESTREG, r12 + blo %ft0 + + ldr r12, =$g_GCShadow + ldr r12, [r12] + add $DESTREG, r12 + + ldr r12, =$g_GCShadowEnd + ldr r12, [r12] + cmp $DESTREG, r12 + bhs %ft0 + + ;; Update the shadow heap. + str $REFREG, [$DESTREG] + + ;; The following read must be strongly ordered wrt to the write we've just performed in order to + ;; prevent race conditions. + dmb + + ;; Now check that the real heap location still contains the value we just wrote into the shadow heap. + ldr r12, [sp] + ldr r12, [r12] + cmp r12, $REFREG + beq %ft0 + + ;; Someone went and updated the real heap. We need to invalidate the shadow location since we can't + ;; guarantee whose shadow update won. + movw r12, #0xcccd + movt r12, #0xcccc + str r12, [$DESTREG] + +0 + ;; Restore original $DESTREG value from the stack. + pop $DESTREG + +1 + MEND + +#else // WRITE_BARRIER_CHECK + + MACRO + UPDATE_GC_SHADOW $DESTREG, $REFREG + MEND + +#endif // WRITE_BARRIER_CHECK + +;; There are several different helpers used depending on which register holds the object reference. Since all +;; the helpers have identical structure we use a macro to define this structure. Two arguments are taken, the +;; name of the register that points to the location to be updated and the name of the register that holds the +;; object reference (this should be in upper case as it's used in the definition of the name of the helper). + +;; Define a sub-macro first that expands to the majority of the barrier implementation. This is used below for +;; some interlocked helpers that need an inline barrier. + MACRO + ;; On entry: + ;; $DESTREG: location to be updated + ;; $REFREG: objectref to be stored + ;; + ;; On exit: + ;; $DESTREG, r12: trashed + ;; other registers are preserved + ;; + INSERT_CHECKED_WRITE_BARRIER_CORE $DESTREG, $REFREG + + ;; The location being updated might not even lie in the GC heap (a handle or stack location for + ;; instance), in which case no write barrier is required. + ldr r12, =$G_LOWEST_ADDRESS + ldr r12, [r12] + cmp $DESTREG, r12 + blo %ft0 + ldr r12, =$G_HIGHEST_ADDRESS + ldr r12, [r12] + cmp $DESTREG, r12 + bhs %ft0 + + ;; Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + ;; we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW $DESTREG, $REFREG + + ;; If the reference is to an object that's not in an ephemeral generation we have no need to track it + ;; (since the object won't be collected or moved by an ephemeral collection). + ldr r12, =$G_EPHEMERAL_LOW + ldr r12, [r12] + cmp $REFREG, r12 + blo %ft0 + ldr r12, =$G_EPHEMERAL_HIGH + ldr r12, [r12] + cmp $REFREG, r12 + bhs %ft0 + + ;; All tests pass, so update the card table. + ldr r12, =$G_CARD_TABLE + ldr r12, [r12] + add r12, r12, $DESTREG, lsr #10 + + ;; Check that this card hasn't already been written. Avoiding useless writes is a big win on + ;; multi-proc systems since it avoids cache thrashing. + ldrb $DESTREG, [r12] + cmp $DESTREG, #0xFF + beq %ft0 + mov $DESTREG, #0xFF + strb $DESTREG, [r12] + +0 + + MEND + +;; There are several different helpers used depending on which register holds the object reference. Since all +;; the helpers have identical structure we use a macro to define this structure. Two arguments are taken, the +;; name of the register that points to the location to be updated and the name of the register that holds the +;; object reference (this should be in upper case as it's used in the definition of the name of the helper). + +;; Define a sub-macro first that expands to the majority of the barrier implementation. This is used below for +;; some interlocked helpers that need an inline barrier. + MACRO + ;; On entry: + ;; $DESTREG: location to be updated + ;; $REFREG: objectref to be stored + ;; + ;; On exit: + ;; $DESTREG, r12: trashed + ;; other registers are preserved + ;; + INSERT_UNCHECKED_WRITE_BARRIER_CORE $DESTREG, $REFREG + + ;; Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + ;; we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW $DESTREG, $REFREG + + ;; If the reference is to an object that's not in an ephemeral generation we have no need to track it + ;; (since the object won't be collected or moved by an ephemeral collection). + ldr r12, =$G_EPHEMERAL_LOW + ldr r12, [r12] + cmp $REFREG, r12 + blo %ft0 + ldr r12, =$G_EPHEMERAL_HIGH + ldr r12, [r12] + cmp $REFREG, r12 + bhs %ft0 + + ;; All tests pass, so update the card table. + ldr r12, =$G_CARD_TABLE + ldr r12, [r12] + add r12, r12, $DESTREG, lsr #10 + + ;; Check that this card hasn't already been written. Avoiding useless writes is a big win on + ;; multi-proc systems since it avoids cache thrashing. + ldrb $DESTREG, [r12] + cmp $DESTREG, #0xFF + beq %ft0 + mov $DESTREG, #0xFF + strb $DESTREG, [r12] + +0 + + MEND + + MACRO + ;; Define a helper with a name of the form RhpCheckedAssignRefR0 etc. The location to be updated is in + ;; $DESTREG. The object reference that will be assigned into that location is in one of the other + ;; general registers determined by the value of $REFREG. R12 is used as a scratch register. + + ;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: + ;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at WriteBarrierFunctionAvLocation + ;; - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address + + DEFINE_CHECKED_WRITE_BARRIER $DESTREG, $REFREG + + gbls WriteBarrierFunction + gbls WriteBarrierFunctionAvLocation +WriteBarrierFunction SETS "RhpCheckedAssignRef":cc:"$REFREG" +WriteBarrierFunctionAvLocation SETS "RhpCheckedAssignRefAvLocation":cc:"$REFREG" + + EXPORT $WriteBarrierFunction +$WriteBarrierFunction + + ;; Export the canonical write barrier under unqualified name as well + IF "$REFREG" == "R1" + ALTERNATE_ENTRY RhpCheckedAssignRef + ENDIF + + ;; Use the GC write barrier as a convenient place to implement the managed memory model for ARM. The + ;; intent is that writes to the target object ($REFREG) will be visible across all CPUs before the + ;; write to the destination ($DESTREG). This covers most of the common scenarios where the programmer + ;; might assume strongly ordered accessess, namely where the preceding writes are used to initialize + ;; the object and the final write, made by this barrier in the instruction following the DMB, + ;; publishes that object for other threads/cpus to see. + ;; + ;; Note that none of this is relevant for single cpu machines. We may choose to implement a + ;; uniprocessor specific version of this barrier if uni-proc becomes a significant scenario again. + dmb + + ;; Write the reference into the location. Note that we rely on the fact that no GC can occur between + ;; here and the card table update we may perform below. + ALTERNATE_ENTRY $WriteBarrierFunctionAvLocation + IF "$REFREG" == "R1" + ALTERNATE_ENTRY RhpCheckedAssignRefAVLocation + ENDIF + str $REFREG, [$DESTREG] + + INSERT_CHECKED_WRITE_BARRIER_CORE $DESTREG, $REFREG + + bx lr + + MEND + + + MACRO + ;; Define a helper with a name of the form RhpAssignRefR0 etc. The location to be updated is in + ;; $DESTREG. The object reference that will be assigned into that location is in one of the other + ;; general registers determined by the value of $REFREG. R12 is used as a scratch register. + + ;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: + ;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at WriteBarrierFunctionAvLocation + ;; - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address + + DEFINE_UNCHECKED_WRITE_BARRIER $DESTREG, $REFREG + + gbls WriteBarrierFunction + gbls WriteBarrierFunctionAvLocation +WriteBarrierFunction SETS "RhpAssignRef":cc:"$REFREG" +WriteBarrierFunctionAvLocation SETS "RhpAssignRefAvLocation":cc:"$REFREG" + + ;; Export the canonical write barrier under unqualified name as well + IF "$REFREG" == "R1" + ALTERNATE_ENTRY RhpAssignRef + ENDIF + + EXPORT $WriteBarrierFunction +$WriteBarrierFunction + + ;; Use the GC write barrier as a convenient place to implement the managed memory model for ARM. The + ;; intent is that writes to the target object ($REFREG) will be visible across all CPUs before the + ;; write to the destination ($DESTREG). This covers most of the common scenarios where the programmer + ;; might assume strongly ordered accessess, namely where the preceding writes are used to initialize + ;; the object and the final write, made by this barrier in the instruction following the DMB, + ;; publishes that object for other threads/cpus to see. + ;; + ;; Note that none of this is relevant for single cpu machines. We may choose to implement a + ;; uniprocessor specific version of this barrier if uni-proc becomes a significant scenario again. + dmb + + ;; Write the reference into the location. Note that we rely on the fact that no GC can occur between + ;; here and the card table update we may perform below. + ALTERNATE_ENTRY $WriteBarrierFunctionAvLocation + IF "$REFREG" == "R1" + ALTERNATE_ENTRY RhpAssignRefAVLocation + ENDIF + str $REFREG, [$DESTREG] + + INSERT_UNCHECKED_WRITE_BARRIER_CORE $DESTREG, $REFREG + + bx lr + + MEND + +;; One day we might have write barriers for all the possible argument registers but for now we have +;; just one write barrier that assumes the input register is R1. + DEFINE_CHECKED_WRITE_BARRIER R0, R1 + + DEFINE_UNCHECKED_WRITE_BARRIER R0, R1 + +;; Interlocked operation helpers where the location is an objectref, thus requiring a GC write barrier upon +;; successful updates. + +;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedLockCmpXchgAVLocation +;; - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address + + ;; Interlocked compare exchange on objectref. + ;; + ;; On entry: + ;; r0: pointer to objectref + ;; r1: exchange value + ;; r2: comparand + ;; + ;; On exit: + ;; r0: original value of objectref + ;; r1,r2,r3,r12: trashed + ;; + LEAF_ENTRY RhpCheckedLockCmpXchg + + ;; To implement our chosen memory model for ARM we insert a memory barrier at GC write barriers. This + ;; barrier must occur before the object reference update, so we have to do it unconditionally even + ;; though the update may fail below. + dmb + +CX_Retry + ;; Check location value is what we expect. + ALTERNATE_ENTRY RhpCheckedLockCmpXchgAVLocation + ldrex r3, [r0] + cmp r2, r3 + bne CX_NoUpdate + + ;; Current value matches comparand, attempt to update with the new value. + strex r3, r1, [r0] + cmp r3, #0 + bne CX_Retry ; Retry the operation if another write beat us there + + ;; We've successfully updated the value of the objectref so now we need a GC write barrier. + ;; The following barrier code takes the destination in r0 and the value in r1 so the arguments are + ;; already correctly set up. + + INSERT_CHECKED_WRITE_BARRIER_CORE r0, r1 + + ;; The original value was equal to the comparand which is still in r2 so we can return that. + mov r0, r2 + bx lr + +CX_NoUpdate + ;; Location value didn't match comparand, return that value. + mov r0, r3 + bx lr + + LEAF_END RhpCheckedLockCmpXchg + + ;; Interlocked exchange on objectref. + ;; + ;; On entry: + ;; r0: pointer to objectref + ;; r1: exchange value + ;; + ;; On exit: + ;; r0: original value of objectref + ;; r1,r2,r3,r12: trashed + ;; + + ;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: + ;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen within at RhpCheckedXchgAVLocation + ;; - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address + + LEAF_ENTRY RhpCheckedXchg + + ;; To implement our chosen memory model for ARM we insert a memory barrier at GC write barriers. This + ;; barrier must occur before the object reference update. + dmb + +X_Retry + ALTERNATE_ENTRY RhpCheckedXchgAVLocation + ;; Read the original contents of the location. + ldrex r2, [r0] + + ;; Attempt to update with the new value. + strex r3, r1, [r0] + cmp r3, #0 + bne X_Retry ; Retry the operation if another write beat us there + + ;; We've successfully updated the value of the objectref so now we need a GC write barrier. + ;; The following barrier code takes the destination in r0 and the value in r1 so the arguments are + ;; already correctly set up. + + INSERT_CHECKED_WRITE_BARRIER_CORE r0, r1 + + ;; The original value is currently in r2. We need to return it in r0. + mov r0, r2 + bx lr + + LEAF_END RhpCheckedXchg + + end diff --git a/src/coreclr/nativeaot/Runtime/arm64/AllocFast.S b/src/coreclr/nativeaot/Runtime/arm64/AllocFast.S new file mode 100644 index 00000000000000..b2ab3df2c8d1f1 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/AllocFast.S @@ -0,0 +1,272 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include "AsmOffsets.inc" + +// GC type flags +GC_ALLOC_FINALIZE = 1 + +// +// Rename fields of nested structs +// +OFFSETOF__Thread__m_alloc_context__alloc_ptr = OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr +OFFSETOF__Thread__m_alloc_context__alloc_limit = OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit + + + +// Allocate non-array, non-finalizable object. If the allocation doesn't fit into the current thread's +// allocation context then automatically fallback to the slow allocation path. +// x0 == MethodTable + LEAF_ENTRY RhpNewFast, _TEXT + + // x1 = GetThread() +#ifdef FEATURE_EMULATED_TLS + GETTHREAD_ETLS_1 +#else + INLINE_GETTHREAD x1 +#endif + + // + // x0 contains MethodTable pointer + // + ldr w2, [x0, #OFFSETOF__MethodTable__m_uBaseSize] + + // + // x0: MethodTable pointer + // x1: Thread pointer + // x2: base size + // + + // Load potential new object address into x12. + ldr x12, [x1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Determine whether the end of the object would lie outside of the current allocation context. If so, + // we abandon the attempt to allocate the object directly and fall back to the slow helper. + add x2, x2, x12 + ldr x13, [x1, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp x2, x13 + bhi RhpNewFast_RarePath + + // Update the alloc pointer to account for the allocation. + str x2, [x1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new objects MethodTable pointer + str x0, [x12, #OFFSETOF__Object__m_pEEType] + + mov x0, x12 + ret + +RhpNewFast_RarePath: + mov x1, #0 + b RhpNewObject + LEAF_END RhpNewFast, _TEXT + +// Allocate non-array object with finalizer. +// x0 == MethodTable + LEAF_ENTRY RhpNewFinalizable, _TEXT + mov x1, #GC_ALLOC_FINALIZE + b RhpNewObject + LEAF_END RhpNewFinalizable, _TEXT + +// Allocate non-array object. +// x0 == MethodTable +// x1 == alloc flags + NESTED_ENTRY RhpNewObject, _TEXT, NoHandler + + PUSH_COOP_PINVOKE_FRAME x3 + + // x3: transition frame + + // Preserve the MethodTable in x19 + mov x19, x0 + + mov w2, 0 // numElements + + // Call the rest of the allocation helper. + // void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + bl RhpGcAlloc + + // Set the new objects MethodTable pointer on success. + cbz x0, NewOutOfMemory + + POP_COOP_PINVOKE_FRAME + EPILOG_RETURN + +NewOutOfMemory: + // This is the OOM failure path. We are going to tail-call to a managed helper that will throw + // an out of memory exception that the caller of this allocator understands. + + mov x0, x19 // MethodTable pointer + mov x1, 0 // Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + b RhExceptionHandling_FailedAllocation + + NESTED_END RhpNewObject, _TEXT + +// Allocate a string. +// x0 == MethodTable +// x1 == element/character count + LEAF_ENTRY RhNewString, _TEXT + // Make sure computing the overall allocation size wont overflow + movz x2, MAX_STRING_LENGTH & 0xFFFF + movk x2, MAX_STRING_LENGTH >> 16, lsl 16 + cmp x1, x2 + bhi StringSizeOverflow + + // Compute overall allocation size (align(base size + (element size * elements), 8)). + mov w2, #STRING_COMPONENT_SIZE + mov x3, #(STRING_BASE_SIZE + 7) + umaddl x2, w1, w2, x3 // x2 = w1 * w2 + x3 + and x2, x2, #-8 + + // x0 == MethodTable + // x1 == element count + // x2 == string size + +#ifdef FEATURE_EMULATED_TLS + GETTHREAD_ETLS_3 +#else + INLINE_GETTHREAD x3 +#endif + + // Load potential new object address into x12. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Determine whether the end of the object would lie outside of the current allocation context. If so, + // we abandon the attempt to allocate the object directly and fall back to the slow helper. + add x2, x2, x12 + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp x2, x12 + bhi RhpNewArrayRare + + // Reload new object address into r12. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Update the alloc pointer to account for the allocation. + str x2, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new objects MethodTable pointer and element count. + str x0, [x12, #OFFSETOF__Object__m_pEEType] + str x1, [x12, #OFFSETOF__Array__m_Length] + + // Return the object allocated in x0. + mov x0, x12 + + ret + +StringSizeOverflow: + // We get here if the length of the final string object can not be represented as an unsigned + // 32-bit value. We are going to tail-call to a managed helper that will throw + // an OOM exception that the caller of this allocator understands. + + // x0 holds MethodTable pointer already + mov x1, #1 // Indicate that we should throw OverflowException + b RhExceptionHandling_FailedAllocation + LEAF_END RhNewString, _Text + +// Allocate one dimensional, zero based array (SZARRAY). +// x0 == MethodTable +// x1 == element count + LEAF_ENTRY RhpNewArray, _Text + + // We want to limit the element count to the non-negative 32-bit int range. + // If the element count is <= 0x7FFFFFFF, no overflow is possible because the component + // size is <= 0xffff (it is an unsigned 16-bit value), and the base size for the worst + // case (32 dimensional MdArray) is less than 0xffff, and thus the product fits in 64 bits. + mov x2, #0x7FFFFFFF + cmp x1, x2 + bhi ArraySizeOverflow + + ldrh w2, [x0, #OFFSETOF__MethodTable__m_usComponentSize] + umull x2, w1, w2 + ldr w3, [x0, #OFFSETOF__MethodTable__m_uBaseSize] + add x2, x2, x3 + add x2, x2, #7 + and x2, x2, #-8 + + // x0 == MethodTable + // x1 == element count + // x2 == array size + +#ifdef FEATURE_EMULATED_TLS + GETTHREAD_ETLS_3 +#else + INLINE_GETTHREAD x3 +#endif + + // Load potential new object address into x12. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Determine whether the end of the object would lie outside of the current allocation context. If so, + // we abandon the attempt to allocate the object directly and fall back to the slow helper. + add x2, x2, x12 + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp x2, x12 + bhi RhpNewArrayRare + + // Reload new object address into x12. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Update the alloc pointer to account for the allocation. + str x2, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new objects MethodTable pointer and element count. + str x0, [x12, #OFFSETOF__Object__m_pEEType] + str x1, [x12, #OFFSETOF__Array__m_Length] + + // Return the object allocated in r0. + mov x0, x12 + + ret + +ArraySizeOverflow: + // We get here if the size of the final array object can not be represented as an unsigned + // 32-bit value. We are going to tail-call to a managed helper that will throw + // an overflow exception that the caller of this allocator understands. + + // x0 holds MethodTable pointer already + mov x1, #1 // Indicate that we should throw OverflowException + b RhExceptionHandling_FailedAllocation + LEAF_END RhpNewArray, _TEXT + +// Allocate one dimensional, zero based array (SZARRAY) using the slow path that calls a runtime helper. +// x0 == MethodTable +// x1 == element count +// x2 == array size + Thread::m_alloc_context::alloc_ptr +// x3 == Thread + NESTED_ENTRY RhpNewArrayRare, _TEXT, NoHandler + + // Recover array size by subtracting the alloc_ptr from x2. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + sub x2, x2, x12 + + PUSH_COOP_PINVOKE_FRAME x3 + + // Preserve data we will need later into the callee saved registers + mov x19, x0 // Preserve MethodTable + + mov x2, x1 // numElements + mov x1, #0 // uFlags + + // void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + bl RhpGcAlloc + + // Set the new objects MethodTable pointer and length on success. + cbz x0, ArrayOutOfMemory + + POP_COOP_PINVOKE_FRAME + EPILOG_RETURN + +ArrayOutOfMemory: + // This is the OOM failure path. We are going to tail-call to a managed helper that will throw + // an out of memory exception that the caller of this allocator understands. + + mov x0, x19 // MethodTable Pointer + mov x1, 0 // Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + b RhExceptionHandling_FailedAllocation + + NESTED_END RhpNewArrayRare, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm64/AllocFast.asm b/src/coreclr/nativeaot/Runtime/arm64/AllocFast.asm new file mode 100644 index 00000000000000..560c6dcbad52d8 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/AllocFast.asm @@ -0,0 +1,257 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + +;; Allocate non-array, non-finalizable object. If the allocation doesn't fit into the current thread's +;; allocation context then automatically fallback to the slow allocation path. +;; x0 == MethodTable + LEAF_ENTRY RhpNewFast + + ;; x1 = GetThread(), TRASHES x2 + INLINE_GETTHREAD x1, x2 + + ;; + ;; x0 contains MethodTable pointer + ;; + ldr w2, [x0, #OFFSETOF__MethodTable__m_uBaseSize] + + ;; + ;; x0: MethodTable pointer + ;; x1: Thread pointer + ;; x2: base size + ;; + + ;; Load potential new object address into x12. + ldr x12, [x1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Determine whether the end of the object would lie outside of the current allocation context. If so, + ;; we abandon the attempt to allocate the object directly and fall back to the slow helper. + add x2, x2, x12 + ldr x13, [x1, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp x2, x13 + bhi RhpNewFast_RarePath + + ;; Update the alloc pointer to account for the allocation. + str x2, [x1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the new object's MethodTable pointer + str x0, [x12, #OFFSETOF__Object__m_pEEType] + + mov x0, x12 + ret + +RhpNewFast_RarePath + mov x1, #0 + b RhpNewObject + LEAF_END RhpNewFast + + INLINE_GETTHREAD_CONSTANT_POOL + +;; Allocate non-array object with finalizer. +;; x0 == MethodTable + LEAF_ENTRY RhpNewFinalizable + mov x1, #GC_ALLOC_FINALIZE + b RhpNewObject + LEAF_END RhpNewFinalizable + +;; Allocate non-array object. +;; x0 == MethodTable +;; x1 == alloc flags + NESTED_ENTRY RhpNewObject + + PUSH_COOP_PINVOKE_FRAME x3 + + ;; x3: transition frame + + ;; Preserve the MethodTable in x19 + mov x19, x0 + + mov w2, #0 ; numElements + + ;; Call the rest of the allocation helper. + ;; void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + bl RhpGcAlloc + + cbz x0, NewOutOfMemory + + POP_COOP_PINVOKE_FRAME + EPILOG_RETURN + +NewOutOfMemory + ;; This is the OOM failure path. We're going to tail-call to a managed helper that will throw + ;; an out of memory exception that the caller of this allocator understands. + + mov x0, x19 ; MethodTable pointer + mov x1, #0 ; Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + EPILOG_NOP b RhExceptionHandling_FailedAllocation + + NESTED_END RhpNewObject + +;; Allocate a string. +;; x0 == MethodTable +;; x1 == element/character count + LEAF_ENTRY RhNewString + ;; Make sure computing the overall allocation size won't overflow + movz x2, #(MAX_STRING_LENGTH & 0xFFFF) + movk x2, #(MAX_STRING_LENGTH >> 16), lsl #16 + cmp x1, x2 + bhi StringSizeOverflow + + ;; Compute overall allocation size (align(base size + (element size * elements), 8)). + mov w2, #STRING_COMPONENT_SIZE + mov x3, #(STRING_BASE_SIZE + 7) + umaddl x2, w1, w2, x3 ; x2 = w1 * w2 + x3 + and x2, x2, #-8 + + ; x0 == MethodTable + ; x1 == element count + ; x2 == string size + + INLINE_GETTHREAD x3, x5 + + ;; Load potential new object address into x12. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Determine whether the end of the object would lie outside of the current allocation context. If so, + ;; we abandon the attempt to allocate the object directly and fall back to the slow helper. + add x2, x2, x12 + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp x2, x12 + bhi RhpNewArrayRare + + ;; Reload new object address into r12. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Update the alloc pointer to account for the allocation. + str x2, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the new object's MethodTable pointer and element count. + str x0, [x12, #OFFSETOF__Object__m_pEEType] + str x1, [x12, #OFFSETOF__Array__m_Length] + + ;; Return the object allocated in x0. + mov x0, x12 + + ret + +StringSizeOverflow + ; We get here if the length of the final string object can't be represented as an unsigned + ; 32-bit value. We're going to tail-call to a managed helper that will throw + ; an OOM exception that the caller of this allocator understands. + + ; x0 holds MethodTable pointer already + mov x1, #1 ; Indicate that we should throw OverflowException + b RhExceptionHandling_FailedAllocation + LEAF_END RhNewString + + INLINE_GETTHREAD_CONSTANT_POOL + + +;; Allocate one dimensional, zero based array (SZARRAY). +;; x0 == MethodTable +;; x1 == element count + LEAF_ENTRY RhpNewArray + + ;; We want to limit the element count to the non-negative 32-bit int range. + ;; If the element count is <= 0x7FFFFFFF, no overflow is possible because the component + ;; size is <= 0xffff (it's an unsigned 16-bit value), and the base size for the worst + ;; case (32 dimensional MdArray) is less than 0xffff, and thus the product fits in 64 bits. + mov x2, #0x7FFFFFFF + cmp x1, x2 + bhi ArraySizeOverflow + + ldrh w2, [x0, #OFFSETOF__MethodTable__m_usComponentSize] + umull x2, w1, w2 + ldr w3, [x0, #OFFSETOF__MethodTable__m_uBaseSize] + add x2, x2, x3 + add x2, x2, #7 + and x2, x2, #-8 + + ; x0 == MethodTable + ; x1 == element count + ; x2 == array size + + INLINE_GETTHREAD x3, x5 + + ;; Load potential new object address into x12. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Determine whether the end of the object would lie outside of the current allocation context. If so, + ;; we abandon the attempt to allocate the object directly and fall back to the slow helper. + add x2, x2, x12 + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp x2, x12 + bhi RhpNewArrayRare + + ;; Reload new object address into x12. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Update the alloc pointer to account for the allocation. + str x2, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the new object's MethodTable pointer and element count. + str x0, [x12, #OFFSETOF__Object__m_pEEType] + str x1, [x12, #OFFSETOF__Array__m_Length] + + ;; Return the object allocated in r0. + mov x0, x12 + + ret + +ArraySizeOverflow + ; We get here if the size of the final array object can't be represented as an unsigned + ; 32-bit value. We're going to tail-call to a managed helper that will throw + ; an overflow exception that the caller of this allocator understands. + + ; x0 holds MethodTable pointer already + mov x1, #1 ; Indicate that we should throw OverflowException + b RhExceptionHandling_FailedAllocation + LEAF_END RhpNewArray + + INLINE_GETTHREAD_CONSTANT_POOL + +;; Allocate one dimensional, zero based array (SZARRAY) using the slow path that calls a runtime helper. +;; x0 == MethodTable +;; x1 == element count +;; x2 == array size + Thread::m_alloc_context::alloc_ptr +;; x3 == Thread + NESTED_ENTRY RhpNewArrayRare + + ; Recover array size by subtracting the alloc_ptr from x2. + PROLOG_NOP ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + PROLOG_NOP sub x2, x2, x12 + + PUSH_COOP_PINVOKE_FRAME x3 + + ; Preserve data we'll need later into the callee saved registers + mov x19, x0 ; Preserve MethodTable + + mov x2, x1 ; numElements + mov x1, #0 ; uFlags + + ;; void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + bl RhpGcAlloc + + cbz x0, ArrayOutOfMemory + + POP_COOP_PINVOKE_FRAME + EPILOG_RETURN + +ArrayOutOfMemory + ;; This is the OOM failure path. We're going to tail-call to a managed helper that will throw + ;; an out of memory exception that the caller of this allocator understands. + + mov x0, x19 ; MethodTable Pointer + mov x1, #0 ; Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + EPILOG_NOP b RhExceptionHandling_FailedAllocation + + NESTED_END RhpNewArrayRare + + END diff --git a/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h b/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h new file mode 100644 index 00000000000000..2682ca0e5d85e8 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h @@ -0,0 +1,325 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +;; OS provided macros +#include +;; generated by the build from AsmOffsets.cpp +#include "AsmOffsets.inc" + +;; +;; CONSTANTS -- INTEGER +;; +TSF_Attached equ 0x01 +TSF_SuppressGcStress equ 0x08 +TSF_DoNotTriggerGc equ 0x10 +TSF_SuppressGcStress__OR__TSF_DoNotTriggerGC equ 0x18 + +;; Bit position for the flags above, to be used with tbz/tbnz instructions +TSF_Attached_Bit equ 0 +TSF_SuppressGcStress_Bit equ 3 +TSF_DoNotTriggerGc_Bit equ 4 + +;; GC type flags +GC_ALLOC_FINALIZE equ 1 +GC_ALLOC_ALIGN8_BIAS equ 4 +GC_ALLOC_ALIGN8 equ 8 + +;; Note: these must match the defs in PInvokeTransitionFrameFlags defined in rhbinder.h +PTFF_SAVE_X19 equ 0x00000001 +PTFF_SAVE_X20 equ 0x00000002 +PTFF_SAVE_X21 equ 0x00000004 +PTFF_SAVE_X22 equ 0x00000008 +PTFF_SAVE_X23 equ 0x00000010 +PTFF_SAVE_X24 equ 0x00000020 +PTFF_SAVE_X25 equ 0x00000040 +PTFF_SAVE_X26 equ 0x00000080 +PTFF_SAVE_X27 equ 0x00000100 +PTFF_SAVE_X28 equ 0x00000200 +PTFF_SAVE_SP equ 0x00000400 +PTFF_SAVE_ALL_PRESERVED equ 0x000003FF ;; NOTE: x19-x28 +PTFF_SAVE_X0 equ 0x00000800 +PTFF_SAVE_X1 equ 0x00001000 +PTFF_SAVE_X2 equ 0x00002000 +PTFF_SAVE_X3 equ 0x00004000 +PTFF_SAVE_X4 equ 0x00008000 +PTFF_SAVE_X5 equ 0x00010000 +PTFF_SAVE_X6 equ 0x00020000 +PTFF_SAVE_X7 equ 0x00040000 +PTFF_SAVE_X8 equ 0x00080000 +PTFF_SAVE_X9 equ 0x00100000 +PTFF_SAVE_X10 equ 0x00200000 +PTFF_SAVE_X11 equ 0x00400000 +PTFF_SAVE_X12 equ 0x00800000 +PTFF_SAVE_X13 equ 0x01000000 +PTFF_SAVE_X14 equ 0x02000000 +PTFF_SAVE_X15 equ 0x04000000 +PTFF_SAVE_X16 equ 0x08000000 +PTFF_SAVE_X17 equ 0x10000000 +PTFF_SAVE_X18 equ 0x20000000 +PTFF_SAVE_ALL_SCRATCH equ 0x3FFFF800 ;; NOTE: X0-X18 +PTFF_SAVE_FP equ 0x40000000 +PTFF_SAVE_LR equ 0x80000000 + +;; NOTE: The following flags represent the upper 32 bits of the PInvokeTransitionFrameFlags. +;; Since the assembler doesn't support 64 bit constants in any way, we need to define just +;; the upper bits here +PTFF_X0_IS_GCREF_HI equ 0x00000001 ;; iff PTFF_SAVE_X0 : set->x0 is Object, clear->x0 is scalar +PTFF_X0_IS_BYREF_HI equ 0x00000002 ;; iff PTFF_SAVE_X0 : set->x0 is ByRef, clear->x0 is Object or scalar +PTFF_X1_IS_GCREF_HI equ 0x00000004 ;; iff PTFF_SAVE_X1 : set->x1 is Object, clear->x1 is scalar +PTFF_X1_IS_BYREF_HI equ 0x00000008 ;; iff PTFF_SAVE_X1 : set->x1 is ByRef, clear->x1 is Object or scalar +PTFF_THREAD_ABORT_HI equ 0x00000010 ;; indicates that ThreadAbortException should be thrown when returning from the transition + +;; Bit position for the flags above, to be used with tbz / tbnz instructions +PTFF_THREAD_ABORT_BIT equ 36 + +;; These must match the TrapThreadsFlags enum +TrapThreadsFlags_None equ 0 +TrapThreadsFlags_AbortInProgress equ 1 +TrapThreadsFlags_TrapThreads equ 2 + +;; Bit position for the flags above, to be used with tbz / tbnz instructions +TrapThreadsFlags_AbortInProgress_Bit equ 0 +TrapThreadsFlags_TrapThreads_Bit equ 1 + +;; This must match HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT +STATUS_REDHAWK_THREAD_ABORT equ 0x43 + +;; +;; Rename fields of nested structs +;; +OFFSETOF__Thread__m_alloc_context__alloc_ptr equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr +OFFSETOF__Thread__m_alloc_context__alloc_limit equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit + +;; +;; IMPORTS +;; + EXTERN RhpGcAlloc + EXTERN RhExceptionHandling_FailedAllocation + EXTERN RhDebugBreak + EXTERN RhpWaitForSuspend2 + EXTERN RhpWaitForGC2 + EXTERN RhpReversePInvokeAttachOrTrapThread2 + EXTERN RhThrowHwEx + EXTERN RhThrowEx + EXTERN RhRethrow + + EXTERN RhpTrapThreads + EXTERN g_lowest_address + EXTERN g_highest_address + EXTERN g_ephemeral_low + EXTERN g_ephemeral_high + EXTERN g_card_table + + +;; ----------------------------------------------------------------------------- +;; Macro used to assign an alternate name to a symbol containing characters normally disallowed in a symbol +;; name (e.g. C++ decorated names). + MACRO + SETALIAS $name, $symbol + GBLS $name +$name SETS "|$symbol|" + MEND + +;;----------------------------------------------------------------------------- +;; Macro for loading a 64-bit constant by a minimal number of instructions +;; Since the asssembles doesn't support 64 bit arithmetics in expressions, +;; the value is passed in as lo, hi pair. + MACRO + MOVL64 $Reg, $ConstantLo, $ConstantHi + + LCLS MovInstr +MovInstr SETS "movz" + + IF ((($ConstantHi):SHR:16):AND:0xffff) != 0 + $MovInstr $Reg, #((($Constant):SHR:16):AND:0xffff), lsl #48 +MovInstr SETS "movk" + ENDIF + + IF (($ConstantHi):AND:0xffff) != 0 + $MovInstr $Reg, #(($ConstantHi):AND:0xffff), lsl #32 +MovInstr SETS "movk" + ENDIF + + IF ((($ConstantLo):SHR:16):AND:0xffff) != 0 + $MovInstr $Reg, #((($ConstantLo):SHR:16):AND:0xffff), lsl #16 +MovInstr SETS "movk" + ENDIF + + $MovInstr $Reg, #(($ConstantLo):AND:0xffff) + MEND + +;; ----------------------------------------------------------------------------- +;; +;; Macro to export a pointer to an address inside a stub as a 64-bit variable +;; + MACRO + EXPORT_POINTER_TO_ADDRESS $Name + LCLS CodeLbl +CodeLbl SETS "$Name":CC:"Lbl" +$CodeLbl + AREA | .rdata | , ALIGN = 8, DATA, READONLY +$Name + DCQ $CodeLbl + EXPORT $Name + TEXTAREA + ROUT + + MEND + +;; ----------------------------------------------------------------------------- +;; +;; Macro for indicating an alternate entry point into a function. +;; + + MACRO + LABELED_RETURN_ADDRESS $ReturnAddressName + + ; export the return address name, but do not perturb the code by forcing alignment +$ReturnAddressName + EXPORT $ReturnAddressName + + ; flush any pending literal pool stuff + ROUT + + MEND + +;; ----------------------------------------------------------------------------- +;; +;; Macro to get a pointer to the Thread* object for the currently executing thread +;; + +__tls_array equ 0x58 ;; offsetof(TEB, ThreadLocalStoragePointer) + + EXTERN _tls_index + + GBLS __SECTIONREL_tls_CurrentThread +__SECTIONREL_tls_CurrentThread SETS "SECTIONREL_tls_CurrentThread" + + MACRO + INLINE_GETTHREAD $destReg, $trashReg + + ;; The following macro variables are just some assembler magic to get the name of the 32-bit version + ;; of $trashReg. It does it by string manipulation. Replaces something like x3 with w3. + LCLS TrashRegister32Bit +TrashRegister32Bit SETS "$trashReg" +TrashRegister32Bit SETS "w":CC:("$TrashRegister32Bit":RIGHT:((:LEN:TrashRegister32Bit) - 1)) + + ldr $trashReg, =_tls_index + ldr $TrashRegister32Bit, [$trashReg] + ldr $destReg, [xpr, #__tls_array] + ldr $destReg, [$destReg, $trashReg lsl #3] + ldr $trashReg, =$__SECTIONREL_tls_CurrentThread + ldr $trashReg, [$trashReg] + add $destReg, $destReg, $trashReg + MEND + + ;; INLINE_GETTHREAD_CONSTANT_POOL macro has to be used after the last function in the .asm file that used + ;; INLINE_GETTHREAD. Optionally, it can be also used after any function that used INLINE_GETTHREAD + ;; to improve density, or to reduce distance betweeen the constant pool and its use. + MACRO + INLINE_GETTHREAD_CONSTANT_POOL + EXTERN tls_CurrentThread + + ;; Section relocs are 32 bits. Using an extra DCD initialized to zero for 8-byte alignment. +$__SECTIONREL_tls_CurrentThread + DCD tls_CurrentThread + RELOC 8, tls_CurrentThread ;; SECREL + DCD 0 + +__SECTIONREL_tls_CurrentThread SETS "$__SECTIONREL_tls_CurrentThread":CC:"_" + + MEND + + MACRO + INLINE_THREAD_UNHIJACK $threadReg, $trashReg1, $trashReg2 + ;; + ;; Thread::Unhijack() + ;; + ldr $trashReg1, [$threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + cbz $trashReg1, %ft0 + + ldr $trashReg2, [$threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str $trashReg1, [$trashReg2] + str xzr, [$threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str xzr, [$threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] +0 + MEND + +;; ---------------------------------------------------------------------------- - +;; +;; Macro to add a memory barrier. Equal to __sync_synchronize(). +;; + + MACRO + ArmInterlockedOperationBarrier + + dmb ish + MEND + +;; ----------------------------------------------------------------------------- +;; +;; Macro used from unmanaged helpers called from managed code where the helper does not transition immediately +;; into pre-emptive mode but may cause a GC and thus requires the stack is crawlable. This is typically the +;; case for helpers that meddle in GC state (e.g. allocation helpers) where the code must remain in +;; cooperative mode since it handles object references and internal GC state directly but a garbage collection +;; may be inevitable. In these cases we need to be able to transition to pre-meptive mode deep within the +;; unmanaged code but still be able to initialize the stack iterator at the first stack frame which may hold +;; interesting GC references. In all our helper cases this corresponds to the most recent managed frame (e.g. +;; the helper's caller). +;; +;; This macro builds a frame describing the current state of managed code. +;; +;; INVARIANTS +;; - The macro assumes it defines the method prolog, it should typically be the first code in a method and +;; certainly appear before any attempt to alter the stack pointer. +;; - This macro uses trashReg (after its initial value has been saved in the frame) and upon exit trashReg +;; will contain the address of transition frame. +;; + +DEFAULT_FRAME_SAVE_FLAGS equ PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_SP + + MACRO + PUSH_COOP_PINVOKE_FRAME $trashReg + + PROLOG_SAVE_REG_PAIR fp, lr, #-0x80! ;; Push down stack pointer and store FP and LR + + ;; 0x10 bytes reserved for Thread* and flags + + ;; Save callee saved registers + PROLOG_SAVE_REG_PAIR x19, x20, #0x20 + PROLOG_SAVE_REG_PAIR x21, x22, #0x30 + PROLOG_SAVE_REG_PAIR x23, x24, #0x40 + PROLOG_SAVE_REG_PAIR x25, x26, #0x50 + PROLOG_SAVE_REG_PAIR x27, x28, #0x60 + + ;; Save the value of SP before stack allocation to the last slot in the frame (slot #15) + add $trashReg, sp, #0x80 + str $trashReg, [sp, #0x70] + + ;; Record the bitmask of saved registers in the frame (slot #3) + mov $trashReg, #DEFAULT_FRAME_SAVE_FLAGS + str $trashReg, [sp, #0x18] + + mov $trashReg, sp + MEND + +;; Pop the frame and restore register state preserved by PUSH_COOP_PINVOKE_FRAME + MACRO + POP_COOP_PINVOKE_FRAME + + EPILOG_RESTORE_REG_PAIR x19, x20, #0x20 + EPILOG_RESTORE_REG_PAIR x21, x22, #0x30 + EPILOG_RESTORE_REG_PAIR x23, x24, #0x40 + EPILOG_RESTORE_REG_PAIR x25, x26, #0x50 + EPILOG_RESTORE_REG_PAIR x27, x28, #0x60 + EPILOG_RESTORE_REG_PAIR fp, lr, #0x80! + MEND + + +#ifdef FEATURE_GC_STRESS + SETALIAS THREAD__HIJACKFORGCSTRESS, ?HijackForGcStress@Thread@@SAXPEAUPAL_LIMITED_CONTEXT@@@Z + SETALIAS REDHAWKGCINTERFACE__STRESSGC, ?StressGc@RedhawkGCInterface@@SAXXZ + + EXTERN $REDHAWKGCINTERFACE__STRESSGC + EXTERN $THREAD__HIJACKFORGCSTRESS +#endif ;; FEATURE_GC_STRESS diff --git a/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h new file mode 100644 index 00000000000000..6e59ade597ad47 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This file is used by AsmOffsets.h to validate that our +// assembly-code offsets always match their C++ counterparts. +// +// NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix + +PLAT_ASM_SIZEOF(290, ExInfo) +PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) +PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) +PLAT_ASM_OFFSET(10, ExInfo, m_exception) +PLAT_ASM_OFFSET(18, ExInfo, m_kind) +PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) +PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) +PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) +PLAT_ASM_OFFSET(288, ExInfo, m_notifyDebuggerSP) + +PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_FramePointer) +PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_RIP) +PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) +PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) +PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) + +PLAT_ASM_SIZEOF(268, StackFrameIterator) +PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) +PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) +PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) +PLAT_ASM_OFFSET(260, StackFrameIterator, m_OriginalControlPC) + +PLAT_ASM_SIZEOF(C0, PAL_LIMITED_CONTEXT) + +PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, FP) +PLAT_ASM_OFFSET(8, PAL_LIMITED_CONTEXT, LR) +PLAT_ASM_OFFSET(10, PAL_LIMITED_CONTEXT, X0) +PLAT_ASM_OFFSET(18, PAL_LIMITED_CONTEXT, X1) +PLAT_ASM_OFFSET(20, PAL_LIMITED_CONTEXT, X19) +PLAT_ASM_OFFSET(28, PAL_LIMITED_CONTEXT, X20) +PLAT_ASM_OFFSET(30, PAL_LIMITED_CONTEXT, X21) +PLAT_ASM_OFFSET(38, PAL_LIMITED_CONTEXT, X22) +PLAT_ASM_OFFSET(40, PAL_LIMITED_CONTEXT, X23) +PLAT_ASM_OFFSET(48, PAL_LIMITED_CONTEXT, X24) +PLAT_ASM_OFFSET(50, PAL_LIMITED_CONTEXT, X25) +PLAT_ASM_OFFSET(58, PAL_LIMITED_CONTEXT, X26) +PLAT_ASM_OFFSET(60, PAL_LIMITED_CONTEXT, X27) +PLAT_ASM_OFFSET(68, PAL_LIMITED_CONTEXT, X28) +PLAT_ASM_OFFSET(70, PAL_LIMITED_CONTEXT, SP) +PLAT_ASM_OFFSET(78, PAL_LIMITED_CONTEXT, IP) + +PLAT_ASM_SIZEOF(150, REGDISPLAY) +PLAT_ASM_OFFSET(f8, REGDISPLAY, SP) + +PLAT_ASM_OFFSET(98, REGDISPLAY, pX19) +PLAT_ASM_OFFSET(a0, REGDISPLAY, pX20) +PLAT_ASM_OFFSET(a8, REGDISPLAY, pX21) +PLAT_ASM_OFFSET(b0, REGDISPLAY, pX22) +PLAT_ASM_OFFSET(b8, REGDISPLAY, pX23) +PLAT_ASM_OFFSET(c0, REGDISPLAY, pX24) +PLAT_ASM_OFFSET(c8, REGDISPLAY, pX25) +PLAT_ASM_OFFSET(d0, REGDISPLAY, pX26) +PLAT_ASM_OFFSET(d8, REGDISPLAY, pX27) +PLAT_ASM_OFFSET(e0, REGDISPLAY, pX28) +PLAT_ASM_OFFSET(e8, REGDISPLAY, pFP) +PLAT_ASM_OFFSET(110, REGDISPLAY, D) diff --git a/src/coreclr/nativeaot/Runtime/arm64/CallDescrWorker.S b/src/coreclr/nativeaot/Runtime/arm64/CallDescrWorker.S new file mode 100644 index 00000000000000..fa24f81393ac81 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/CallDescrWorker.S @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include "AsmOffsets.inc" + +//----------------------------------------------------------------------------- +// This helper routine enregisters the appropriate arguments and makes the +// actual call. +// +// INPUT: x0: pointer to CallDescrData struct +// +//----------------------------------------------------------------------------- +//void RhCallDescrWorker(CallDescrData * pCallDescrData); + NESTED_ENTRY RhCallDescrWorker, _TEXT, NoHandler + + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32 + PROLOG_SAVE_REG_PAIR x19, x20, 16 + + // Save the value of SP before we start pushing any arguments + mov x20, sp + + mov x19, x0 // save pCallDescrData in x19 + + ldr w1, [x19, #OFFSETOF__CallDescrData__numStackSlots] + cbz w1, Ldonestack + + // Add frame padding to ensure frame size is a multiple of 16 (a requirement of the OS ABI). + // We push two registers (above) and numStackSlots arguments (below). If this comes to an odd number + // of slots we must pad with another. This simplifies to "if the low bit of numStackSlots is set, + // extend the stack another eight bytes". + ldr x0, [x19, #OFFSETOF__CallDescrData__pSrc] + add x0, x0, x1, lsl #3 // pSrcEnd=pSrc+8*numStackSlots + ands x2, x1, #1 + beq Lstackloop + + // This loop copies numStackSlots words + // from [pSrcEnd-8,pSrcEnd-16,...] to [sp-8,sp-16,...] + + // Pad and store one stack slot as number of slots are odd + ldr x4, [x0,#-8]! + str x4, [sp,#-16]! + subs x1, x1, #1 + beq Ldonestack +Lstackloop: + ldp x2, x4, [x0,#-16]! + stp x2, x4, [sp,#-16]! + subs x1, x1, #2 + bne Lstackloop +Ldonestack: + + // If FP arguments are supplied in registers (x9 != NULL) then initialize all of them from the pointer + // given in x9. + ldr x9, [x19, #OFFSETOF__CallDescrData__pFloatArgumentRegisters] + cbz x9, LNoFloatingPoint + ldp d0, d1, [x9] + ldp d2, d3, [x9, #16] + ldp d4, d5, [x9, #32] + ldp d6, d7, [x9, #48] +LNoFloatingPoint: + + // Copy [pArgumentRegisters, ..., pArgumentRegisters + 64] + // into x0, ..., x7, x8 + + ldr x9, [x19, #OFFSETOF__CallDescrData__pArgumentRegisters] + ldp x0, x1, [x9] + ldp x2, x3, [x9, #16] + ldp x4, x5, [x9, #32] + ldp x6, x7, [x9, #48] + ldr x8, [x9, #64] + + // call pTarget + ldr x9, [x19, #OFFSETOF__CallDescrData__pTarget] + blr x9 + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFromCallDescrThunk + + // Symbol used to identify thunk call to managed function so the special + // case unwinder can unwind through this function. Sadly we cannot directly + // export this symbol right now because it confuses DIA unwinder to believe + // it's the beginning of a new method, therefore we export the address + // of an auxiliary variable holding the address instead. + + ldr w3, [x19, #OFFSETOF__CallDescrData__fpReturnSize] + + // Unlike desktop returnValue is a pointer to a return buffer, not the buffer itself + ldr x19, [x19, #OFFSETOF__CallDescrData__pReturnBuffer] + + // Int return case + cbz w3, LIntReturn + + // Float return case + cmp w3, #4 + beq LFloatOrDoubleReturn + + // Double return case + cmp w3, #8 + bne LCheckHFAReturn + +LFloatOrDoubleReturn: + str d0, [x19] + b LReturnDone + +LCheckHFAReturn: + cmp w3, #16 + beq LFloatOrDoubleHFAReturn + cmp w3, #32 + beq LFloatOrDoubleHFAReturn + b LNoHFAReturn + +LFloatOrDoubleHFAReturn: + //Single/Double HFAReturn return case + stp d0, d1, [x19, #00] + stp d2, d3, [x19, #16] + b LReturnDone + +LNoHFAReturn: + + EMIT_BREAKPOINT // Unreachable + +LIntReturn: + // Save return value(s) into retbuf for int + stp x0, x1, [x19] + +LReturnDone: + +#ifdef _DEBUG + // Trash the floating point registers to ensure that the HFA return values + // won't survive by accident + ldp d0, d1, [sp] + ldp d2, d3, [sp, #16] +#endif + // Restore the value of SP + mov sp, x20 + + EPILOG_RESTORE_REG_PAIR x19, x20, 16 + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 + EPILOG_RETURN + + NESTED_END RhCallDescrWorker diff --git a/src/coreclr/nativeaot/Runtime/arm64/CallDescrWorker.asm b/src/coreclr/nativeaot/Runtime/arm64/CallDescrWorker.asm new file mode 100644 index 00000000000000..09eef6d781ebf4 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/CallDescrWorker.asm @@ -0,0 +1,143 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + +;;----------------------------------------------------------------------------- +;; This helper routine enregisters the appropriate arguments and makes the +;; actual call. +;; +;; INPUT: x0: pointer to CallDescrData struct +;; +;;----------------------------------------------------------------------------- +;;void RhCallDescrWorker(CallDescrData * pCallDescrData); + NESTED_ENTRY RhCallDescrWorker + + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + PROLOG_SAVE_REG_PAIR x19, x20, #16 + + ;; Save the value of SP before we start pushing any arguments + mov x20, sp + + mov x19, x0 ; save pCallDescrData in x19 + + ldr w1, [x19, #OFFSETOF__CallDescrData__numStackSlots] + cbz w1, Ldonestack + + ;; Add frame padding to ensure frame size is a multiple of 16 (a requirement of the OS ABI). + ;; We push two registers (above) and numStackSlots arguments (below). If this comes to an odd number + ;; of slots we must pad with another. This simplifies to "if the low bit of numStackSlots is set, + ;; extend the stack another eight bytes". + ldr x0, [x19, #OFFSETOF__CallDescrData__pSrc] + add x0, x0, x1 lsl #3 ; pSrcEnd=pSrc+8*numStackSlots + ands x2, x1, #1 + beq Lstackloop + + ;; This loop copies numStackSlots words + ;; from [pSrcEnd-8,pSrcEnd-16,...] to [sp-8,sp-16,...] + + ;; Pad and store one stack slot as number of slots are odd + ldr x4, [x0,#-8]! + str x4, [sp,#-16]! + subs x1, x1, #1 + beq Ldonestack +Lstackloop + ldp x2, x4, [x0,#-16]! + stp x2, x4, [sp,#-16]! + subs x1, x1, #2 + bne Lstackloop +Ldonestack + + ;; If FP arguments are supplied in registers (x9 != NULL) then initialize all of them from the pointer + ;; given in x9. + ldr x9, [x19, #OFFSETOF__CallDescrData__pFloatArgumentRegisters] + cbz x9, LNoFloatingPoint + ldp d0, d1, [x9] + ldp d2, d3, [x9, #16] + ldp d4, d5, [x9, #32] + ldp d6, d7, [x9, #48] +LNoFloatingPoint + + ;; Copy [pArgumentRegisters, ..., pArgumentRegisters + 64] + ;; into x0, ..., x7, x8 + + ldr x9, [x19, #OFFSETOF__CallDescrData__pArgumentRegisters] + ldp x0, x1, [x9] + ldp x2, x3, [x9, #16] + ldp x4, x5, [x9, #32] + ldp x6, x7, [x9, #48] + ldr x8, [x9, #64] + + ;; call pTarget + ldr x9, [x19, #OFFSETOF__CallDescrData__pTarget] + blr x9 + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFromCallDescrThunk + + ;; Symbol used to identify thunk call to managed function so the special + ;; case unwinder can unwind through this function. Sadly we cannot directly + ;; export this symbol right now because it confuses DIA unwinder to believe + ;; it's the beginning of a new method, therefore we export the address + ;; of an auxiliary variable holding the address instead. + + ldr w3, [x19, #OFFSETOF__CallDescrData__fpReturnSize] + + ;; Unlike desktop returnValue is a pointer to a return buffer, not the buffer itself + ldr x19, [x19, #OFFSETOF__CallDescrData__pReturnBuffer] + + ;; Int return case + cbz w3, LIntReturn + + ;; Float return case + cmp w3, #4 + beq LFloatOrDoubleReturn + + ;; Double return case + cmp w3, #8 + bne LCheckHFAReturn + +LFloatOrDoubleReturn + str d0, [x19] + b LReturnDone + +LCheckHFAReturn + cmp w3, #16 + beq LFloatOrDoubleHFAReturn + cmp w3, #32 + beq LFloatOrDoubleHFAReturn + b LNoHFAReturn + +LFloatOrDoubleHFAReturn + ;;Single/Double HFAReturn return case + stp d0, d1, [x19, #00] + stp d2, d3, [x19, #16] + b LReturnDone + +LNoHFAReturn + + EMIT_BREAKPOINT ; Unreachable + +LIntReturn + ;; Save return value(s) into retbuf for int + stp x0, x1, [x19] + +LReturnDone + +#ifdef _DEBUG + ;; Trash the floating point registers to ensure that the HFA return values + ;; won't survive by accident + ldp d0, d1, [sp] + ldp d2, d3, [sp, #16] +#endif + ;; Restore the value of SP + mov sp, x20 + + EPILOG_RESTORE_REG_PAIR x19, x20, #16 + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + + NESTED_END RhCallDescrWorker + + END diff --git a/src/coreclr/nativeaot/Runtime/arm64/CallingConventionConverterHelpers.S b/src/coreclr/nativeaot/Runtime/arm64/CallingConventionConverterHelpers.S new file mode 100644 index 00000000000000..67cd6fdd952dd6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/CallingConventionConverterHelpers.S @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include "AsmOffsets.inc" + +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CallingConventionCoverter Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +POINTER_SIZE = 0x08 + +// Note: The "__jmpstub__" prefix is used to indicate to debugger +// that it must step-through this stub when it encounters it while +// stepping. + + + // + // void CallingConventionConverter_ReturnThunk() + // + LEAF_ENTRY CallingConventionConverter_ReturnThunk, _TEXT + ret + LEAF_END CallingConventionConverter_ReturnThunk, _TEXT + + // + // __jmpstub__CallingConventionConverter_CommonCallingStub + // + // struct CallingConventionConverter_CommonCallingStub_PointerData + // { + // void *ManagedCallConverterThunk; + // void *UniversalThunk; + // } + // + // struct CommonCallingStubInputData + // { + // ULONG_PTR CallingConventionId; + // CallingConventionConverter_CommonCallingStub_PointerData *commonData; // Only the ManagedCallConverterThunk field is used + // // However, it is specified just like other platforms, so the behavior of the common + // // calling stub is easier to debug + // } + // + // xip0 - Points at CommonCallingStubInputData + // + // + LEAF_ENTRY __jmpstub__CallingConventionConverter_CommonCallingStub, _TEXT + ldr xip1, [xip0] // put CallingConventionId into xip1 as "parameter" to universal transition thunk + ldr xip0, [xip0, #POINTER_SIZE] // get pointer to CallingConventionConverter_CommonCallingStub_PointerData into xip0 + ldr x12, [xip0, #POINTER_SIZE] // get address of UniversalTransitionThunk (which we'll tailcall to later) + ldr xip0, [xip0] // get address of ManagedCallConverterThunk (target for universal thunk to call) + br x12 + LEAF_END __jmpstub__CallingConventionConverter_CommonCallingStub, _TEXT + + // + // void CallingConventionConverter_GetStubs(IntPtr *returnVoidStub, IntPtr *returnIntegerStub, IntPtr *commonCallingStub) + // + LEAF_ENTRY CallingConventionConverter_GetStubs, _TEXT + PREPARE_EXTERNAL_VAR CallingConventionConverter_ReturnThunk, x12 + str x12, [x0] // ARM doesn't need different return thunks. + str x12, [x1] + PREPARE_EXTERNAL_VAR __jmpstub__CallingConventionConverter_CommonCallingStub, x12 + str x12, [x2] + ret + LEAF_END CallingConventionConverter_GetStubs, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm64/CallingConventionConverterHelpers.asm b/src/coreclr/nativeaot/Runtime/arm64/CallingConventionConverterHelpers.asm new file mode 100644 index 00000000000000..0d6ea3ec0a0bda --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/CallingConventionConverterHelpers.asm @@ -0,0 +1,63 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "ksarm64.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CallingConventionCoverter Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +POINTER_SIZE equ 0x08 + +;; +;; Note: The "__jmpstub__" prefix is used to indicate to debugger +;; that it must step-through this stub when it encounters it while +;; stepping. +;; + + ;; + ;; void CallingConventionConverter_ReturnThunk() + ;; + LEAF_ENTRY CallingConventionConverter_ReturnThunk + ret + LEAF_END CallingConventionConverter_ReturnThunk + + ;; + ;; __jmpstub__CallingConventionConverter_CommonCallingStub + ;; + ;; struct CallingConventionConverter_CommonCallingStub_PointerData + ;; { + ;; void *ManagedCallConverterThunk; + ;; void *UniversalThunk; + ;; } + ;; + ;; struct CommonCallingStubInputData + ;; { + ;; ULONG_PTR CallingConventionId; + ;; CallingConventionConverter_CommonCallingStub_PointerData *commonData; // Only the ManagedCallConverterThunk field is used + ;; // However, it is specified just like other platforms, so the behavior of the common + ;; // calling stub is easier to debug + ;; } + ;; + ;; xip0 - Points at CommonCallingStubInputData + ;; + ;; + LEAF_ENTRY __jmpstub__CallingConventionConverter_CommonCallingStub + ldr xip1, [xip0] ; put CallingConventionId into xip1 as "parameter" to universal transition thunk + ldr xip0, [xip0, #POINTER_SIZE] ; get pointer to CallingConventionConverter_CommonCallingStub_PointerData into xip0 + ldr x12, [xip0, #POINTER_SIZE] ; get address of UniversalTransitionThunk (which we'll tailcall to later) + ldr xip0, [xip0] ; get address of ManagedCallConverterThunk (target for universal thunk to call) + br x12 + LEAF_END __jmpstub__CallingConventionConverter_CommonCallingStub + + ;; + ;; void CallingConventionConverter_GetStubs(IntPtr *returnVoidStub, IntPtr *returnIntegerStub, IntPtr *commonCallingStub) + ;; + LEAF_ENTRY CallingConventionConverter_GetStubs + ldr x12, =CallingConventionConverter_ReturnThunk + str x12, [x0] ;; ARM doesn't need different return thunks. + str x12, [x1] + ldr x12, =__jmpstub__CallingConventionConverter_CommonCallingStub + str x12, [x2] + ret + LEAF_END CallingConventionConverter_GetStubs + + END diff --git a/src/coreclr/nativeaot/Runtime/arm64/Dummies.asm b/src/coreclr/nativeaot/Runtime/arm64/Dummies.asm new file mode 100644 index 00000000000000..ea6c21fc810d09 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/Dummies.asm @@ -0,0 +1,18 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + + LEAF_ENTRY RhpLMod + DCW 0xdefe + bx lr + LEAF_END RhpLMod + + LEAF_ENTRY RhpLMul + DCW 0xdefe + bx lr + LEAF_END RhpLMul + + END diff --git a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S new file mode 100644 index 00000000000000..debaed60e91571 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S @@ -0,0 +1,655 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include "AsmOffsets.inc" + +#define STACKSIZEOF_ExInfo ((SIZEOF__ExInfo + 15)&(~15)) + +#define HARDWARE_EXCEPTION 1 +#define SOFTWARE_EXCEPTION 0 + +.global RhpTrapThreads + +// ----------------------------------------------------------------------------- +// Macro used to create frame of exception throwing helpers (RhpThrowEx, RhpThrowHwEx) + .macro ALLOC_THROW_FRAME exceptionType + + mov x3, sp + + // Setup a PAL_LIMITED_CONTEXT on the stack { + .if \exceptionType == HARDWARE_EXCEPTION + sub sp,sp,#0x50 + .cfi_adjust_cfa_offset 0x50 + stp x3, x1, [sp] // x3 is the SP and x1 is the IP of the fault site + .else + PROLOG_STACK_ALLOC 0x50 + .cfi_adjust_cfa_offset 0x50 + stp x3, lr, [sp] // x3 is the SP and lr is the IP of the fault site + .endif + stp d8, d9, [sp, #0x10] + stp d10, d11, [sp, #0x20] + stp d12, d13, [sp, #0x30] + stp d14, d15, [sp, #0x40] + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -0x70 + stp xzr, xzr, [sp, #0x10] // locations reserved for return value, not used for exception handling + PROLOG_SAVE_REG_PAIR x19, x20, 0x20 + PROLOG_SAVE_REG_PAIR x21, x22, 0x30 + PROLOG_SAVE_REG_PAIR x23, x24, 0x40 + PROLOG_SAVE_REG_PAIR x25, x26, 0x50 + PROLOG_SAVE_REG_PAIR x27, x28, 0x60 + // } end PAL_LIMITED_CONTEXT + + PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo + .endm + +// ----------------------------------------------------------------------------- +// Macro used to create frame of funclet calling helpers (RhpCallXXXXFunclet) +// extraStackSize - extra stack space that the user of the macro can use to +// store additional registers + .macro ALLOC_CALL_FUNCLET_FRAME extraStackSize + + // Using below prolog instead of PROLOG_SAVE_REG_PAIR fp,lr, #-60! + // is intentional. Above statement would also emit instruction to save + // sp in fp. If sp is saved in fp in prolog then it is not expected that fp can change in the body + // of method. However, this method needs to be able to change fp before calling funclet. + // This is required to access locals in funclet. + PROLOG_SAVE_REG_PAIR_NO_FP_INDEXED fp,lr, -0x60 + PROLOG_SAVE_REG_PAIR x19, x20, 0x10 + PROLOG_SAVE_REG_PAIR x21, x22, 0x20 + PROLOG_SAVE_REG_PAIR x23, x24, 0x30 + PROLOG_SAVE_REG_PAIR x25, x26, 0x40 + PROLOG_SAVE_REG_PAIR x27, x28, 0x50 + mov fp, sp + .cfi_def_cfa_register fp + + .if \extraStackSize != 0 + PROLOG_STACK_ALLOC \extraStackSize + .endif + .endm + +// ----------------------------------------------------------------------------- +// Macro used to free frame of funclet calling helpers (RhpCallXXXXFunclet) +// extraStackSize - extra stack space that the user of the macro can use to +// store additional registers. +// It needs to match the value passed to the corresponding +// ALLOC_CALL_FUNCLET_FRAME. + .macro FREE_CALL_FUNCLET_FRAME extraStackSize + + .if \extraStackSize != 0 + EPILOG_STACK_FREE \extraStackSize + .endif + + EPILOG_RESTORE_REG_PAIR x19, x20, 0x10 + EPILOG_RESTORE_REG_PAIR x21, x22, 0x20 + EPILOG_RESTORE_REG_PAIR x23, x24, 0x30 + EPILOG_RESTORE_REG_PAIR x25, x26, 0x40 + EPILOG_RESTORE_REG_PAIR x27, x28, 0x50 + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 0x60 + .endm + + +// ----------------------------------------------------------------------------- +// Macro used to restore preserved general purpose and FP registers from REGDISPLAY +// regdisplayReg - register pointing to the REGDISPLAY structure + .macro RESTORE_PRESERVED_REGISTERS regdisplayReg + + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX19] + ldr x19, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX20] + ldr x20, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX21] + ldr x21, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX22] + ldr x22, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX23] + ldr x23, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX24] + ldr x24, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX25] + ldr x25, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX26] + ldr x26, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX27] + ldr x27, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX28] + ldr x28, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pFP] + ldr fp, [x12] + // + // load FP preserved regs + // + add x12, \regdisplayReg, #OFFSETOF__REGDISPLAY__D + ldp d8, d9, [x12, #0x00] + ldp d10, d11, [x12, #0x10] + ldp d12, d13, [x12, #0x20] + ldp d14, d15, [x12, #0x30] + .endm + +// ----------------------------------------------------------------------------- +// Macro used to save preserved general purpose and FP registers to REGDISPLAY +// regdisplayReg - register pointing to the REGDISPLAY structure + .macro SAVE_PRESERVED_REGISTERS regdisplayReg + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX19] + str x19, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX20] + str x20, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX21] + str x21, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX22] + str x22, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX23] + str x23, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX24] + str x24, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX25] + str x25, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX26] + str x26, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX27] + str x27, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX28] + str x28, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pFP] + str fp, [x12] + // + // store vfp preserved regs + // + add x12, \regdisplayReg, #OFFSETOF__REGDISPLAY__D + stp d8, d9, [x12, #0x00] + stp d10, d11, [x12, #0x10] + stp d12, d13, [x12, #0x20] + stp d14, d15, [x12, #0x30] + .endm + + +// ----------------------------------------------------------------------------- +// Macro used to thrash preserved general purpose registers in REGDISPLAY +// to make sure nobody uses them +// regdisplayReg - register pointing to the REGDISPLAY structure + .macro TRASH_PRESERVED_REGISTERS_STORAGE regdisplayReg + +#if _DEBUG + movz x3, #0xbaad, LSL #48 + movk x3, #0xdeed, LSL #32 + movk x3, #0xbaad, LSL #16 + movk x3, #0xdeed + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX19] + str x3, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX20] + str x3, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX21] + str x3, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX22] + str x3, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX23] + str x3, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX24] + str x3, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX25] + str x3, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX26] + str x3, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX27] + str x3, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pX28] + str x3, [x12] + ldr x12, [\regdisplayReg, #OFFSETOF__REGDISPLAY__pFP] + str x3, [x12] +#endif // _DEBUG + .endm + +.macro GetThreadX2 + stp x0, x1, [sp, -16]! + bl RhpGetThread + mov x2, x0 + ldp x0, x1, [sp], 16 +.endm + +#define rsp_offsetof_ExInfo 0 +#define rsp_offsetof_Context STACKSIZEOF_ExInfo + +// +// RhpThrowHwEx +// +// INPUT: W0: exception code of fault +// X1: faulting IP +// +// OUTPUT: +// + NESTED_ENTRY RhpThrowHwEx, _TEXT, NoHandler + + ALLOC_THROW_FRAME HARDWARE_EXCEPTION + + GetThreadX2 + + add x1, sp, #rsp_offsetof_ExInfo // x1 <- ExInfo* + str xzr, [x1, #OFFSETOF__ExInfo__m_exception] // pExInfo->m_exception = null + mov w3, #1 + strb w3, [x1, #OFFSETOF__ExInfo__m_passNumber] // pExInfo->m_passNumber = 1 + mov w3, #0xFFFFFFFF + str w3, [x1, #OFFSETOF__ExInfo__m_idxCurClause] // pExInfo->m_idxCurClause = MaxTryRegionIdx + mov w3, #2 + strb w3, [x1, #OFFSETOF__ExInfo__m_kind] // pExInfo->m_kind = ExKind.HardwareFault + + // link the ExInfo into the thread's ExInfo chain + ldr x3, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] + str x3, [x1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str x1, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo + + // set the exception context field on the ExInfo + add x2, sp, #rsp_offsetof_Context // x2 <- PAL_LIMITED_CONTEXT* + str x2, [x1, #OFFSETOF__ExInfo__m_pExContext] // pExInfo->m_pExContext = pContext + + // w0: exception code + // x1: ExInfo* + bl RhThrowHwEx + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowHwEx2 + + // no return + EMIT_BREAKPOINT + + NESTED_END RhpThrowHwEx, _TEXT + +// +// RhpThrowEx +// +// INPUT: X0: exception object +// +// OUTPUT: +// + + NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler + + ALLOC_THROW_FRAME SOFTWARE_EXCEPTION + + GetThreadX2 + + // There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return + // address could have been hijacked when we were in that C# code and we must remove the hijack and + // reflect the correct return address in our exception context record. The other throw helpers don't + // need this because they cannot be tail-called from C#. + + // NOTE: we cannot use INLINE_THREAD_UNHIJACK because it will write into the stack at the location + // where the tail-calling thread had saved LR, which may not match where we have saved LR. + + ldr x1, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + cbz x1, NotHijacked + + ldr x3, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + + // x0: exception object + // x1: hijacked return address + // x2: pThread + // x3: hijacked return address location + + add x12, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) // re-compute SP at callsite + cmp x3, x12 // if (m_ppvHijackedReturnAddressLocation < SP at callsite) + blo TailCallWasHijacked + + // normal case where a valid return address location is hijacked + str x1, [x3] + b ClearThreadState + +TailCallWasHijacked: + + // Abnormal case where the return address location is now invalid because we ended up here via a tail + // call. In this case, our hijacked return address should be the correct caller of this method. + + // stick the previous return address in LR as well as in the right spots in our PAL_LIMITED_CONTEXT. + mov lr, x1 + str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] + str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] + +ClearThreadState: + + // clear the Thread's hijack state + str xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str xzr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + +NotHijacked: + + add x1, sp, #rsp_offsetof_ExInfo // x1 <- ExInfo* + str xzr, [x1, #OFFSETOF__ExInfo__m_exception] // pExInfo->m_exception = null + mov w3, #1 + strb w3, [x1, #OFFSETOF__ExInfo__m_passNumber] // pExInfo->m_passNumber = 1 + mov w3, #0xFFFFFFFF + str w3, [x1, #OFFSETOF__ExInfo__m_idxCurClause] // pExInfo->m_idxCurClause = MaxTryRegionIdx + mov w3, #1 + strb w3, [x1, #OFFSETOF__ExInfo__m_kind] // pExInfo->m_kind = ExKind.Throw + + // link the ExInfo into the thread's ExInfo chain + ldr x3, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] + str x3, [x1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str x1, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo + + // set the exception context field on the ExInfo + add x2, sp, #rsp_offsetof_Context // x2 <- PAL_LIMITED_CONTEXT* + str x2, [x1, #OFFSETOF__ExInfo__m_pExContext] // pExInfo->m_pExContext = pContext + + // x0: exception object + // x1: ExInfo* + bl RhThrowEx + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowEx2 + + // no return + EMIT_BREAKPOINT + NESTED_END RhpThrowEx, _TEXT + + +// +// void FASTCALL RhpRethrow() +// +// SUMMARY: Similar to RhpThrowEx, except that it passes along the currently active ExInfo +// +// INPUT: +// +// OUTPUT: +// + + NESTED_ENTRY RhpRethrow, _TEXT, NoHandler + + ALLOC_THROW_FRAME SOFTWARE_EXCEPTION + + GetThreadX2 + + add x1, sp, #rsp_offsetof_ExInfo // x1 <- ExInfo* + str xzr, [x1, #OFFSETOF__ExInfo__m_exception] // pExInfo->m_exception = null + strb wzr, [x1, #OFFSETOF__ExInfo__m_kind] // init to a deterministic value (ExKind.None) + mov w3, #1 + strb w3, [x1, #OFFSETOF__ExInfo__m_passNumber] // pExInfo->m_passNumber = 1 + mov w3, #0xFFFFFFFF + str w3, [x1, #OFFSETOF__ExInfo__m_idxCurClause] // pExInfo->m_idxCurClause = MaxTryRegionIdx + + // link the ExInfo into the thread's ExInfo chain + ldr x3, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] + mov x0, x3 // x0 <- current ExInfo + str x3, [x1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str x1, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo + + // set the exception context field on the ExInfo + add x2, sp, #rsp_offsetof_Context // x2 <- PAL_LIMITED_CONTEXT* + str x2, [x1, #OFFSETOF__ExInfo__m_pExContext] // pExInfo->m_pExContext = pContext + + // x0 contains the currently active ExInfo + // x1 contains the address of the new ExInfo + bl RhRethrow + + EXPORT_POINTER_TO_ADDRESS PointerToRhpRethrow2 + + // no return + EMIT_BREAKPOINT + NESTED_END RhpRethrow, _TEXT + +// +// void* FASTCALL RhpCallCatchFunclet(RtuObjectRef exceptionObj, void* pHandlerIP, REGDISPLAY* pRegDisplay, +// ExInfo* pExInfo) +// +// INPUT: X0: exception object +// X1: handler funclet address +// X2: REGDISPLAY* +// X3: ExInfo* +// +// OUTPUT: +// + + NESTED_ENTRY RhpCallCatchFunclet, _TEXT, NoHandler + + ALLOC_CALL_FUNCLET_FRAME 0x70 // Size needs to be equal with ExceptionHandling.asm variant of this function + stp d8, d9, [sp, #0x00] + stp d10, d11, [sp, #0x10] + stp d12, d13, [sp, #0x20] + stp d14, d15, [sp, #0x30] + stp x0, x1, [sp, #0x40] // x0 to x3 are stored to restore them anytime + stp x2, x3, [sp, #0x50] + str xzr, [sp, #0x60] // xzr makes space for the local "is_not_handling_thread_abort"; last qword will store the thread obj + +#define rsp_offset_is_not_handling_thread_abort 0x60 +#define rsp_offset_x0 0x40 +#define rsp_offset_x1 0x48 +#define rsp_offset_x2 0x50 +#define rsp_offset_x3 0x58 +#define rsp_CatchFunclet_offset_thread 0x68 + + // + // clear the DoNotTriggerGc flag, trashes x4-x6 + // + + bl RhpGetThread + str x0, [sp, rsp_CatchFunclet_offset_thread] + mov x5,x0 + ldp x0, x1, [sp, #0x40] + ldp x2, x3, [sp, #0x50] + + ldr x4, [x5, #OFFSETOF__Thread__m_threadAbortException] + sub x4, x4, x0 + str x4, [sp, #rsp_offset_is_not_handling_thread_abort] // Non-zero if the exception is not ThreadAbortException + + add x12, x5, #OFFSETOF__Thread__m_ThreadStateFlags + +ClearRetry_Catch: + ldxr w4, [x12] + bic w4, w4, #TSF_DoNotTriggerGc + stxr w6, w4, [x12] + cbz w6, ClearSuccess_Catch + b ClearRetry_Catch +ClearSuccess_Catch: + + // + // set preserved regs to the values expected by the funclet + // + RESTORE_PRESERVED_REGISTERS x2 + // + // trash the values at the old homes to make sure nobody uses them + // + TRASH_PRESERVED_REGISTERS_STORAGE x2 + + // + // call the funclet + // + // x0 still contains the exception object + blr x1 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallCatchFunclet2 + + // x0 contains resume IP + + ldr x2, [sp, #rsp_offset_x2] // x2 <- REGDISPLAY* + +#ifdef _DEBUG + // Call into some C++ code to validate the pop of the ExInfo. We only do this in debug because we + // have to spill all the preserved registers and then refill them after the call. + + str x0, [sp, rsp_offset_x0] + + SAVE_PRESERVED_REGISTERS x2 + + ldr x0, [sp, rsp_CatchFunclet_offset_thread] // x0 <- Thread* + ldr x1, [sp, #rsp_offset_x3] // x1 <- current ExInfo* + ldr x2, [x2, #OFFSETOF__REGDISPLAY__SP] // x2 <- resume SP value + bl RhpValidateExInfoPop + + ldr x2, [sp, rsp_offset_x2] // x2 <- REGDISPLAY* + + RESTORE_PRESERVED_REGISTERS x2 + + ldr x0, [sp, rsp_offset_x0] // reload resume IP +#endif + + ldr x1, [sp, rsp_CatchFunclet_offset_thread] + + ldr x1, [sp, rsp_CatchFunclet_offset_thread] + + // We must unhijack the thread at this point because the section of stack where the hijack is applied + // may go dead. If it does, then the next time we try to unhijack the thread, it will corrupt the stack. + INLINE_THREAD_UNHIJACK x1, x3, x12 // Thread in x1, trashes x3 and x12 + + ldr x3, [sp, #rsp_offset_x3] // x3 <- current ExInfo* + ldr x2, [x2, #OFFSETOF__REGDISPLAY__SP] // x2 <- resume SP value + +PopExInfoLoop: + ldr x3, [x3, #OFFSETOF__ExInfo__m_pPrevExInfo] // x3 <- next ExInfo + cbz x3, DonePopping // if (pExInfo == null) { we're done } + cmp x3, x2 + blt PopExInfoLoop // if (pExInfo < resume SP} { keep going } + +DonePopping: + str x3, [x1, #OFFSETOF__Thread__m_pExInfoStackHead] // store the new head on the Thread + + PREPARE_EXTERNAL_VAR_INDIRECT_W RhpTrapThreads, 3 + + tbz x3, #TrapThreadsFlags_AbortInProgress_Bit, NoAbort + + ldr x3, [sp, #rsp_offset_is_not_handling_thread_abort] + cbnz x3, NoAbort + + // It was the ThreadAbortException, so rethrow it + // reset SP + mov x1, x0 // x1 <- continuation address as exception PC + mov w0, #STATUS_REDHAWK_THREAD_ABORT + mov sp, x2 + b RhpThrowHwEx + +NoAbort: + // reset SP and jump to continuation address + mov sp, x2 + br x0 + + NESTED_END RhpCallCatchFunclet, _Text + +// +// void FASTCALL RhpCallFinallyFunclet(void* pHandlerIP, REGDISPLAY* pRegDisplay) +// +// INPUT: X0: handler funclet address +// X1: REGDISPLAY* +// +// OUTPUT: +// + + NESTED_ENTRY RhpCallFinallyFunclet, _TEXT, NoHandler + + ALLOC_CALL_FUNCLET_FRAME 0x60 // Size needs to be equal with ExceptionHandling.asm variant of this function + stp d8, d9, [sp, #0x00] + stp d10, d11, [sp, #0x10] + stp d12, d13, [sp, #0x20] + stp d14, d15, [sp, #0x30] + stp x0, x1, [sp, #0x40] // x0 and x1 are saved so we have them later + +#define rsp_offset_x1 0x48 +#define rsp_FinallyFunclet_offset_thread 0x50 + + + // We want to suppress hijacking between invocations of subsequent finallys. We do this because we + // cannot tolerate a GC after one finally has run (and possibly side-effected the GC state of the + // method) and then been popped off the stack, leaving behind no trace of its effect. + // + // So we clear the state before and set it after invocation of the handler. + // + + // + // clear the DoNotTriggerGc flag, trashes x2-x4 + // + + bl RhpGetThread + str x0, [sp, rsp_FinallyFunclet_offset_thread] + mov x2, x0 + ldp x0, x1, [sp, #0x40] + + add x12, x2, #OFFSETOF__Thread__m_ThreadStateFlags + +ClearRetry: + ldxr w4, [x12] + bic w4, w4, #TSF_DoNotTriggerGc + stxr w3, w4, [x12] + cbz w3, ClearSuccess + b ClearRetry +ClearSuccess: + + // + // set preserved regs to the values expected by the funclet + // + RESTORE_PRESERVED_REGISTERS x1 + // + // trash the values at the old homes to make sure nobody uses them + // + TRASH_PRESERVED_REGISTERS_STORAGE x1 + + // + // call the funclet + // + blr x0 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFinallyFunclet2 + + ldr x1, [sp, #rsp_offset_x1] // reload REGDISPLAY pointer + + // + // save new values of preserved regs into REGDISPLAY + // + SAVE_PRESERVED_REGISTERS x1 + + // + // set the DoNotTriggerGc flag, trashes x1-x3 + // + + ldr x2, [sp, rsp_FinallyFunclet_offset_thread] + + add x12, x2, #OFFSETOF__Thread__m_ThreadStateFlags +SetRetry: + ldxr w1, [x12] + orr w1, w1, #TSF_DoNotTriggerGc + stxr w3, w1, [x12] + cbz w3, SetSuccess + b SetRetry +SetSuccess: + + ldp d8, d9, [sp, #0x00] + ldp d10, d11, [sp, #0x10] + ldp d12, d13, [sp, #0x20] + ldp d14, d15, [sp, #0x30] + + FREE_CALL_FUNCLET_FRAME 0x60 + EPILOG_RETURN + + NESTED_END RhpCallFinallyFunclet, _Text + + +// +// void* FASTCALL RhpCallFilterFunclet(RtuObjectRef exceptionObj, void* pFilterIP, REGDISPLAY* pRegDisplay) +// +// INPUT: X0: exception object +// X1: filter funclet address +// X2: REGDISPLAY* +// +// OUTPUT: +// + + NESTED_ENTRY RhpCallFilterFunclet, _TEXT, NoHandler + ALLOC_CALL_FUNCLET_FRAME 0x40 + stp d8, d9, [sp, #0x00] + stp d10, d11, [sp, #0x10] + stp d12, d13, [sp, #0x20] + stp d14, d15, [sp, #0x30] + + ldr x12, [x2, #OFFSETOF__REGDISPLAY__pFP] + ldr fp, [x12] + + // + // call the funclet + // + // x0 still contains the exception object + blr x1 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2 + + ldp d8, d9, [sp, #0x00] + ldp d10, d11, [sp, #0x10] + ldp d12, d13, [sp, #0x20] + ldp d14, d15, [sp, #0x30] + + FREE_CALL_FUNCLET_FRAME 0x40 + EPILOG_RETURN + + NESTED_END RhpCallFilterFunclet, Text diff --git a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm new file mode 100644 index 00000000000000..3db1d648c4deec --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm @@ -0,0 +1,629 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + +#define STACKSIZEOF_ExInfo ((SIZEOF__ExInfo + 15)&(~15)) + +#define HARDWARE_EXCEPTION 1 +#define SOFTWARE_EXCEPTION 0 + +;; ----------------------------------------------------------------------------- +;; Macro used to create frame of exception throwing helpers (RhpThrowEx, RhpThrowHwEx) + MACRO + ALLOC_THROW_FRAME $exceptionType + + PROLOG_NOP mov x3, sp + + ;; Setup a PAL_LIMITED_CONTEXT on the stack { + IF $exceptionType == HARDWARE_EXCEPTION + PROLOG_NOP sub sp,sp,#0x50 + PROLOG_NOP stp x3, x1, [sp] ; x3 is the SP and x1 is the IP of the fault site + PROLOG_PUSH_MACHINE_FRAME + ELSE + PROLOG_STACK_ALLOC 0x50 + PROLOG_NOP stp x3, lr, [sp] ; x3 is the SP and lr is the IP of the fault site + ENDIF + PROLOG_NOP stp d8, d9, [sp, #0x10] + PROLOG_NOP stp d10, d11, [sp, #0x20] + PROLOG_NOP stp d12, d13, [sp, #0x30] + PROLOG_NOP stp d14, d15, [sp, #0x40] + PROLOG_SAVE_REG_PAIR fp, lr, #-0x70! + PROLOG_NOP stp xzr, xzr, [sp, #0x10] ; locations reserved for return value, not used for exception handling + PROLOG_SAVE_REG_PAIR x19, x20, #0x20 + PROLOG_SAVE_REG_PAIR x21, x22, #0x30 + PROLOG_SAVE_REG_PAIR x23, x24, #0x40 + PROLOG_SAVE_REG_PAIR x25, x26, #0x50 + PROLOG_SAVE_REG_PAIR x27, x28, #0x60 + ;; } end PAL_LIMITED_CONTEXT + + PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo + MEND + +;; ----------------------------------------------------------------------------- +;; Macro used to create frame of funclet calling helpers (RhpCallXXXXFunclet) +;; $extraStackSize - extra stack space that the user of the macro can use to +;; store additional registers + MACRO + ALLOC_CALL_FUNCLET_FRAME $extraStackSize + + ; Using below prolog instead of PROLOG_SAVE_REG_PAIR fp,lr, #-60! + ; is intentional. Above statement would also emit instruction to save + ; sp in fp. If sp is saved in fp in prolog then it is not expected that fp can change in the body + ; of method. However, this method needs to be able to change fp before calling funclet. + ; This is required to access locals in funclet. + PROLOG_SAVE_REG_PAIR_NO_FP fp,lr, #-0x60! + PROLOG_SAVE_REG_PAIR x19, x20, #0x10 + PROLOG_SAVE_REG_PAIR x21, x22, #0x20 + PROLOG_SAVE_REG_PAIR x23, x24, #0x30 + PROLOG_SAVE_REG_PAIR x25, x26, #0x40 + PROLOG_SAVE_REG_PAIR x27, x28, #0x50 + PROLOG_NOP mov fp, sp + + IF $extraStackSize != 0 + PROLOG_STACK_ALLOC $extraStackSize + ENDIF + MEND + +;; ----------------------------------------------------------------------------- +;; Macro used to free frame of funclet calling helpers (RhpCallXXXXFunclet) +;; $extraStackSize - extra stack space that the user of the macro can use to +;; store additional registers. +;; It needs to match the value passed to the corresponding +;; ALLOC_CALL_FUNCLET_FRAME. + MACRO + FREE_CALL_FUNCLET_FRAME $extraStackSize + + IF $extraStackSize != 0 + EPILOG_STACK_FREE $extraStackSize + ENDIF + + EPILOG_RESTORE_REG_PAIR x19, x20, #0x10 + EPILOG_RESTORE_REG_PAIR x21, x22, #0x20 + EPILOG_RESTORE_REG_PAIR x23, x24, #0x30 + EPILOG_RESTORE_REG_PAIR x25, x26, #0x40 + EPILOG_RESTORE_REG_PAIR x27, x28, #0x50 + EPILOG_RESTORE_REG_PAIR fp, lr, #0x60! + MEND + +;; ----------------------------------------------------------------------------- +;; Macro used to restore preserved general purpose and FP registers from REGDISPLAY +;; $regdisplayReg - register pointing to the REGDISPLAY structure + MACRO + RESTORE_PRESERVED_REGISTERS $regdisplayReg + + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX19] + ldr x19, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX20] + ldr x20, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX21] + ldr x21, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX22] + ldr x22, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX23] + ldr x23, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX24] + ldr x24, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX25] + ldr x25, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX26] + ldr x26, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX27] + ldr x27, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX28] + ldr x28, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pFP] + ldr fp, [x12] + ;; + ;; load FP preserved regs + ;; + add x12, $regdisplayReg, #OFFSETOF__REGDISPLAY__D + ldp d8, d9, [x12, #0x00] + ldp d10, d11, [x12, #0x10] + ldp d12, d13, [x12, #0x20] + ldp d14, d15, [x12, #0x30] + MEND + +;; ----------------------------------------------------------------------------- +;; Macro used to save preserved general purpose and FP registers to REGDISPLAY +;; $regdisplayReg - register pointing to the REGDISPLAY structure + MACRO + SAVE_PRESERVED_REGISTERS $regdisplayReg + + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX19] + str x19, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX20] + str x20, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX21] + str x21, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX22] + str x22, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX23] + str x23, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX24] + str x24, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX25] + str x25, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX26] + str x26, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX27] + str x27, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX28] + str x28, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pFP] + str fp, [x12] + ;; + ;; store vfp preserved regs + ;; + add x12, $regdisplayReg, #OFFSETOF__REGDISPLAY__D + stp d8, d9, [x12, #0x00] + stp d10, d11, [x12, #0x10] + stp d12, d13, [x12, #0x20] + stp d14, d15, [x12, #0x30] + MEND + +;; ----------------------------------------------------------------------------- +;; Macro used to thrash preserved general purpose registers in REGDISPLAY +;; to make sure nobody uses them +;; $regdisplayReg - register pointing to the REGDISPLAY structure + MACRO + TRASH_PRESERVED_REGISTERS_STORAGE $regdisplayReg + +#if 0 // def _DEBUG ;; @TODO: temporarily removed because trashing the frame pointer breaks the debugger + movz x3, #0xbaad, LSL #48 + movk x3, #0xdeed, LSL #32 + movk x3, #0xbaad, LSL #16 + movk x3, #0xdeed + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX19] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX20] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX21] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX22] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX23] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX24] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX25] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX26] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX27] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX28] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pFP] + str x3, [x12] +#endif // _DEBUG + MEND + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowHwEx +;; +;; INPUT: W0: exception code of fault +;; X1: faulting IP +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpThrowHwEx + +#define rsp_offsetof_ExInfo 0 +#define rsp_offsetof_Context STACKSIZEOF_ExInfo + + ALLOC_THROW_FRAME HARDWARE_EXCEPTION + + ;; x2 = GetThread(), TRASHES x1 + INLINE_GETTHREAD x2, x1 + + add x1, sp, #rsp_offsetof_ExInfo ;; x1 <- ExInfo* + str xzr, [x1, #OFFSETOF__ExInfo__m_exception] ;; pExInfo->m_exception = null + mov w3, #1 + strb w3, [x1, #OFFSETOF__ExInfo__m_passNumber] ;; pExInfo->m_passNumber = 1 + mov w3, #0xFFFFFFFF + str w3, [x1, #OFFSETOF__ExInfo__m_idxCurClause] ;; pExInfo->m_idxCurClause = MaxTryRegionIdx + mov w3, #2 + strb w3, [x1, #OFFSETOF__ExInfo__m_kind] ;; pExInfo->m_kind = ExKind.HardwareFault + + ;; link the ExInfo into the thread's ExInfo chain + ldr x3, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] + str x3, [x1, #OFFSETOF__ExInfo__m_pPrevExInfo] ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str x1, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + add x2, sp, #rsp_offsetof_Context ;; x2 <- PAL_LIMITED_CONTEXT* + str x2, [x1, #OFFSETOF__ExInfo__m_pExContext] ;; pExInfo->m_pExContext = pContext + + ;; w0: exception code + ;; x1: ExInfo* + bl RhThrowHwEx + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowHwEx2 + + ;; no return + EMIT_BREAKPOINT + + NESTED_END RhpThrowHwEx + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowEx +;; +;; INPUT: X0: exception object +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpThrowEx + + ALLOC_THROW_FRAME SOFTWARE_EXCEPTION + + ;; x2 = GetThread(), TRASHES x1 + INLINE_GETTHREAD x2, x1 + + ;; There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return + ;; address could have been hijacked when we were in that C# code and we must remove the hijack and + ;; reflect the correct return address in our exception context record. The other throw helpers don't + ;; need this because they cannot be tail-called from C#. + + ;; NOTE: we cannot use INLINE_THREAD_UNHIJACK because it will write into the stack at the location + ;; where the tail-calling thread had saved LR, which may not match where we have saved LR. + + ldr x1, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + cbz x1, NotHijacked + + ldr x3, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + + ;; x0: exception object + ;; x1: hijacked return address + ;; x2: pThread + ;; x3: hijacked return address location + + add x12, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) ;; re-compute SP at callsite + cmp x3, x12 ;; if (m_ppvHijackedReturnAddressLocation < SP at callsite) + blo TailCallWasHijacked + + ;; normal case where a valid return address location is hijacked + str x1, [x3] + b ClearThreadState + +TailCallWasHijacked + + ;; Abnormal case where the return address location is now invalid because we ended up here via a tail + ;; call. In this case, our hijacked return address should be the correct caller of this method. + ;; + + ;; stick the previous return address in LR as well as in the right spots in our PAL_LIMITED_CONTEXT. + mov lr, x1 + str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] + str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] + +ClearThreadState + + ;; clear the Thread's hijack state + str xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str xzr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + +NotHijacked + + add x1, sp, #rsp_offsetof_ExInfo ;; x1 <- ExInfo* + str xzr, [x1, #OFFSETOF__ExInfo__m_exception] ;; pExInfo->m_exception = null + mov w3, #1 + strb w3, [x1, #OFFSETOF__ExInfo__m_passNumber] ;; pExInfo->m_passNumber = 1 + mov w3, #0xFFFFFFFF + str w3, [x1, #OFFSETOF__ExInfo__m_idxCurClause] ;; pExInfo->m_idxCurClause = MaxTryRegionIdx + mov w3, #1 + strb w3, [x1, #OFFSETOF__ExInfo__m_kind] ;; pExInfo->m_kind = ExKind.Throw + + ;; link the ExInfo into the thread's ExInfo chain + ldr x3, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] + str x3, [x1, #OFFSETOF__ExInfo__m_pPrevExInfo] ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str x1, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + add x2, sp, #rsp_offsetof_Context ;; x2 <- PAL_LIMITED_CONTEXT* + str x2, [x1, #OFFSETOF__ExInfo__m_pExContext] ;; pExInfo->m_pExContext = pContext + + ;; x0: exception object + ;; x1: ExInfo* + bl RhThrowEx + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowEx2 + + ;; no return + EMIT_BREAKPOINT + NESTED_END RhpThrowEx + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void FASTCALL RhpRethrow() +;; +;; SUMMARY: Similar to RhpThrowEx, except that it passes along the currently active ExInfo +;; +;; INPUT: +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpRethrow + + ALLOC_THROW_FRAME SOFTWARE_EXCEPTION + + ;; x2 = GetThread(), TRASHES x1 + INLINE_GETTHREAD x2, x1 + + add x1, sp, #rsp_offsetof_ExInfo ;; x1 <- ExInfo* + str xzr, [x1, #OFFSETOF__ExInfo__m_exception] ;; pExInfo->m_exception = null + strb wzr, [x1, #OFFSETOF__ExInfo__m_kind] ;; init to a deterministic value (ExKind.None) + mov w3, #1 + strb w3, [x1, #OFFSETOF__ExInfo__m_passNumber] ;; pExInfo->m_passNumber = 1 + mov w3, #0xFFFFFFFF + str w3, [x1, #OFFSETOF__ExInfo__m_idxCurClause] ;; pExInfo->m_idxCurClause = MaxTryRegionIdx + + ;; link the ExInfo into the thread's ExInfo chain + ldr x3, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] + mov x0, x3 ;; x0 <- current ExInfo + str x3, [x1, #OFFSETOF__ExInfo__m_pPrevExInfo] ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str x1, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + add x2, sp, #rsp_offsetof_Context ;; x2 <- PAL_LIMITED_CONTEXT* + str x2, [x1, #OFFSETOF__ExInfo__m_pExContext] ;; pExInfo->m_pExContext = pContext + + ;; x0 contains the currently active ExInfo + ;; x1 contains the address of the new ExInfo + bl RhRethrow + + EXPORT_POINTER_TO_ADDRESS PointerToRhpRethrow2 + + ;; no return + EMIT_BREAKPOINT + NESTED_END RhpRethrow + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* FASTCALL RhpCallCatchFunclet(RtuObjectRef exceptionObj, void* pHandlerIP, REGDISPLAY* pRegDisplay, +;; ExInfo* pExInfo) +;; +;; INPUT: X0: exception object +;; X1: handler funclet address +;; X2: REGDISPLAY* +;; X3: ExInfo* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpCallCatchFunclet + + ALLOC_CALL_FUNCLET_FRAME 0x70 // Size needs to be equal with ExceptionHandling.S variant of this function + stp d8, d9, [sp, #0x00] + stp d10, d11, [sp, #0x10] + stp d12, d13, [sp, #0x20] + stp d14, d15, [sp, #0x30] + stp x0, x2, [sp, #0x40] ;; x0, x2 & x3 are saved so we have the exception object, REGDISPLAY and + stp x3, xzr, [sp, #0x50] ;; ExInfo later, xzr makes space for the local "is_not_handling_thread_abort" + +#define rsp_offset_is_not_handling_thread_abort 0x58 +#define rsp_offset_x2 0x48 +#define rsp_offset_x3 0x50 + + ;; + ;; clear the DoNotTriggerGc flag, trashes x4-x6 + ;; + INLINE_GETTHREAD x5, x6 ;; x5 <- Thread*, x6 <- trashed + + ldr x4, [x5, #OFFSETOF__Thread__m_threadAbortException] + sub x4, x4, x0 + str x4, [sp, #rsp_offset_is_not_handling_thread_abort] ;; Non-zero if the exception is not ThreadAbortException + + add x12, x5, #OFFSETOF__Thread__m_ThreadStateFlags + +ClearRetry_Catch + ldxr w4, [x12] + bic w4, w4, #TSF_DoNotTriggerGc + stxr w6, w4, [x12] + cbz w6, ClearSuccess_Catch + b ClearRetry_Catch +ClearSuccess_Catch + + ;; + ;; set preserved regs to the values expected by the funclet + ;; + RESTORE_PRESERVED_REGISTERS x2 + ;; + ;; trash the values at the old homes to make sure nobody uses them + ;; + TRASH_PRESERVED_REGISTERS_STORAGE x2 + + ;; + ;; call the funclet + ;; + ;; x0 still contains the exception object + blr x1 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallCatchFunclet2 + + ;; x0 contains resume IP + + ldr x2, [sp, #rsp_offset_x2] ;; x2 <- REGDISPLAY* + +;; @TODO: add debug-only validation code for ExInfo pop + + INLINE_GETTHREAD x1, x3 ;; x1 <- Thread*, x3 <- trashed + + ;; We must unhijack the thread at this point because the section of stack where the hijack is applied + ;; may go dead. If it does, then the next time we try to unhijack the thread, it will corrupt the stack. + INLINE_THREAD_UNHIJACK x1, x3, x12 ;; Thread in x1, trashes x3 and x12 + + ldr x3, [sp, #rsp_offset_x3] ;; x3 <- current ExInfo* + ldr x2, [x2, #OFFSETOF__REGDISPLAY__SP] ;; x2 <- resume SP value + +PopExInfoLoop + ldr x3, [x3, #OFFSETOF__ExInfo__m_pPrevExInfo] ;; x3 <- next ExInfo + cbz x3, DonePopping ;; if (pExInfo == null) { we're done } + cmp x3, x2 + blt PopExInfoLoop ;; if (pExInfo < resume SP} { keep going } + +DonePopping + str x3, [x1, #OFFSETOF__Thread__m_pExInfoStackHead] ;; store the new head on the Thread + + ldr x3, =RhpTrapThreads + ldr w3, [x3] + tbz x3, #TrapThreadsFlags_AbortInProgress_Bit, NoAbort + + ldr x3, [sp, #rsp_offset_is_not_handling_thread_abort] + cbnz x3, NoAbort + + ;; It was the ThreadAbortException, so rethrow it + ;; reset SP + mov x1, x0 ;; x1 <- continuation address as exception PC + mov w0, #STATUS_REDHAWK_THREAD_ABORT + mov sp, x2 + b RhpThrowHwEx + +NoAbort + ;; reset SP and jump to continuation address + mov sp, x2 + br x0 + + NESTED_END RhpCallCatchFunclet + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void FASTCALL RhpCallFinallyFunclet(void* pHandlerIP, REGDISPLAY* pRegDisplay) +;; +;; INPUT: X0: handler funclet address +;; X1: REGDISPLAY* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpCallFinallyFunclet + + ALLOC_CALL_FUNCLET_FRAME 0x60 // Size needs to be equal with ExceptionHandling.S variant of this function + stp d8, d9, [sp, #0x00] + stp d10, d11, [sp, #0x10] + stp d12, d13, [sp, #0x20] + stp d14, d15, [sp, #0x30] + stp x0, x1, [sp, #0x40] ;; x1 is saved so we have the REGDISPLAY later, x0 is just alignment padding + +#define rsp_offset_x1 0x48 + + ;; + ;; We want to suppress hijacking between invocations of subsequent finallys. We do this because we + ;; cannot tolerate a GC after one finally has run (and possibly side-effected the GC state of the + ;; method) and then been popped off the stack, leaving behind no trace of its effect. + ;; + ;; So we clear the state before and set it after invocation of the handler. + ;; + + ;; + ;; clear the DoNotTriggerGc flag, trashes x2-x4 + ;; + INLINE_GETTHREAD x2, x3 ;; x2 <- Thread*, x3 <- trashed + + add x12, x2, #OFFSETOF__Thread__m_ThreadStateFlags + +ClearRetry + ldxr w4, [x12] + bic w4, w4, #TSF_DoNotTriggerGc + stxr w3, w4, [x12] + cbz w3, ClearSuccess + b ClearRetry +ClearSuccess + + ;; + ;; set preserved regs to the values expected by the funclet + ;; + RESTORE_PRESERVED_REGISTERS x1 + ;; + ;; trash the values at the old homes to make sure nobody uses them + ;; + TRASH_PRESERVED_REGISTERS_STORAGE x1 + + ;; + ;; call the funclet + ;; + blr x0 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFinallyFunclet2 + + ldr x1, [sp, #rsp_offset_x1] ;; reload REGDISPLAY pointer + + ;; + ;; save new values of preserved regs into REGDISPLAY + ;; + SAVE_PRESERVED_REGISTERS x1 + + ;; + ;; set the DoNotTriggerGc flag, trashes x1-x3 + ;; + INLINE_GETTHREAD x2, x3 ;; x2 <- Thread*, x3 <- trashed + + add x12, x2, #OFFSETOF__Thread__m_ThreadStateFlags +SetRetry + ldxr w1, [x12] + orr w1, w1, #TSF_DoNotTriggerGc + stxr w3, w1, [x12] + cbz w3, SetSuccess + b SetRetry +SetSuccess + + ldp d8, d9, [sp, #0x00] + ldp d10, d11, [sp, #0x10] + ldp d12, d13, [sp, #0x20] + ldp d14, d15, [sp, #0x30] + + FREE_CALL_FUNCLET_FRAME 0x60 + EPILOG_RETURN + + NESTED_END RhpCallFinallyFunclet + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* FASTCALL RhpCallFilterFunclet(RtuObjectRef exceptionObj, void* pFilterIP, REGDISPLAY* pRegDisplay) +;; +;; INPUT: X0: exception object +;; X1: filter funclet address +;; X2: REGDISPLAY* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpCallFilterFunclet + ALLOC_CALL_FUNCLET_FRAME 0x40 + stp d8, d9, [sp, #0x00] + stp d10, d11, [sp, #0x10] + stp d12, d13, [sp, #0x20] + stp d14, d15, [sp, #0x30] + + ldr x12, [x2, #OFFSETOF__REGDISPLAY__pFP] + ldr fp, [x12] + + ;; + ;; call the funclet + ;; + ;; x0 still contains the exception object + blr x1 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2 + + ldp d8, d9, [sp, #0x00] + ldp d10, d11, [sp, #0x10] + ldp d12, d13, [sp, #0x20] + ldp d14, d15, [sp, #0x30] + + FREE_CALL_FUNCLET_FRAME 0x40 + EPILOG_RETURN + + NESTED_END RhpCallFilterFunclet + + INLINE_GETTHREAD_CONSTANT_POOL + + end diff --git a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S new file mode 100644 index 00000000000000..57ea4412832dea --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include "AsmOffsets.inc" + + .global RhpGcPoll2 + + LEAF_ENTRY RhpGcPoll + PREPARE_EXTERNAL_VAR_INDIRECT_W RhpTrapThreads, 0 + cbnz w0, RhpGcPollRare // TrapThreadsFlags_None = 0 + ret + LEAF_END RhpGcPoll + + NESTED_ENTRY RhpGcPollRare, _TEXT, NoHandler + PUSH_COOP_PINVOKE_FRAME x0 + bl RhpGcPoll2 + POP_COOP_PINVOKE_FRAME + ret + NESTED_END RhpGcPollRare diff --git a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm new file mode 100644 index 00000000000000..59af51ace18fb9 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm @@ -0,0 +1,562 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + + EXTERN RhpGcPoll2 + EXTERN g_fGcStressStarted + +PROBE_SAVE_FLAGS_EVERYTHING equ DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_ALL_SCRATCH + + ;; Build a map of symbols representing offsets into the transition frame (see PInvokeTransitionFrame in + ;; rhbinder.h) and keep these two in sync. + map 0 + field OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs + field 10 * 8 ; x19..x28 +m_CallersSP field 8 ; SP at routine entry + field 19 * 8 ; x0..x18 + field 8 ; lr +m_SavedNZCV field 8 ; Saved condition flags + field 4 * 8 ; d0..d3 +PROBE_FRAME_SIZE field 0 + + ;; Support for setting up a transition frame when performing a GC probe. In many respects this is very + ;; similar to the logic in PUSH_COOP_PINVOKE_FRAME in AsmMacros.h. In most cases setting up the + ;; transition frame comprises the entirety of the caller's prolog (and initial non-prolog code) and + ;; similarly for the epilog. Those cases can be dealt with using PROLOG_PROBE_FRAME and EPILOG_PROBE_FRAME + ;; defined below. For the special cases where additional work has to be done in the prolog we also provide + ;; the lower level macros ALLOC_PROBE_FRAME, FREE_PROBE_FRAME and INIT_PROBE_FRAME that allow more control + ;; to be asserted. + ;; + ;; Note that we currently employ a significant simplification of frame setup: we always allocate a + ;; maximally-sized PInvokeTransitionFrame and save all of the registers. Depending on the caller this can + ;; lead to up to 20 additional register saves (x0-x18, lr) or 160 bytes of stack space. I have done no + ;; analysis to see whether any of the worst cases occur on performance sensitive paths and whether the + ;; additional saves will show any measurable degradation. + + ;; Perform the parts of setting up a probe frame that can occur during the prolog (and indeed this macro + ;; can only be called from within the prolog). + MACRO + ALLOC_PROBE_FRAME $extraStackSpace, $saveFPRegisters + + ;; First create PInvokeTransitionFrame + PROLOG_SAVE_REG_PAIR fp, lr, #-(PROBE_FRAME_SIZE + $extraStackSpace)! ;; Push down stack pointer and store FP and LR + + ;; Slot at [sp, #0x10] is reserved for Thread * + ;; Slot at [sp, #0x18] is reserved for bitmask of saved registers + + ;; Save callee saved registers + PROLOG_SAVE_REG_PAIR x19, x20, #0x20 + PROLOG_SAVE_REG_PAIR x21, x22, #0x30 + PROLOG_SAVE_REG_PAIR x23, x24, #0x40 + PROLOG_SAVE_REG_PAIR x25, x26, #0x50 + PROLOG_SAVE_REG_PAIR x27, x28, #0x60 + + ;; Slot at [sp, #0x70] is reserved for caller sp + + ;; Save the scratch registers + PROLOG_NOP str x0, [sp, #0x78] + PROLOG_NOP stp x1, x2, [sp, #0x80] + PROLOG_NOP stp x3, x4, [sp, #0x90] + PROLOG_NOP stp x5, x6, [sp, #0xA0] + PROLOG_NOP stp x7, x8, [sp, #0xB0] + PROLOG_NOP stp x9, x10, [sp, #0xC0] + PROLOG_NOP stp x11, x12, [sp, #0xD0] + PROLOG_NOP stp x13, x14, [sp, #0xE0] + PROLOG_NOP stp x15, x16, [sp, #0xF0] + PROLOG_NOP stp x17, x18, [sp, #0x100] + PROLOG_NOP str lr, [sp, #0x110] + + ;; Slot at [sp, #0x118] is reserved for NZCV + + ;; Save the floating return registers + IF $saveFPRegisters + PROLOG_NOP stp d0, d1, [sp, #0x120] + PROLOG_NOP stp d2, d3, [sp, #0x130] + ENDIF + + MEND + + ;; Undo the effects of an ALLOC_PROBE_FRAME. This may only be called within an epilog. Note that all + ;; registers are restored (apart for sp and pc), even volatiles. + MACRO + FREE_PROBE_FRAME $extraStackSpace, $restoreFPRegisters + + ;; Restore the scratch registers + PROLOG_NOP ldr x0, [sp, #0x78] + PROLOG_NOP ldp x1, x2, [sp, #0x80] + PROLOG_NOP ldp x3, x4, [sp, #0x90] + PROLOG_NOP ldp x5, x6, [sp, #0xA0] + PROLOG_NOP ldp x7, x8, [sp, #0xB0] + PROLOG_NOP ldp x9, x10, [sp, #0xC0] + PROLOG_NOP ldp x11, x12, [sp, #0xD0] + PROLOG_NOP ldp x13, x14, [sp, #0xE0] + PROLOG_NOP ldp x15, x16, [sp, #0xF0] + PROLOG_NOP ldp x17, x18, [sp, #0x100] + PROLOG_NOP ldr lr, [sp, #0x110] + + ; Restore the floating return registers + IF $restoreFPRegisters + EPILOG_NOP ldp d0, d1, [sp, #0x120] + EPILOG_NOP ldp d2, d3, [sp, #0x130] + ENDIF + + ;; Restore callee saved registers + EPILOG_RESTORE_REG_PAIR x19, x20, #0x20 + EPILOG_RESTORE_REG_PAIR x21, x22, #0x30 + EPILOG_RESTORE_REG_PAIR x23, x24, #0x40 + EPILOG_RESTORE_REG_PAIR x25, x26, #0x50 + EPILOG_RESTORE_REG_PAIR x27, x28, #0x60 + + EPILOG_RESTORE_REG_PAIR fp, lr, #(PROBE_FRAME_SIZE + $extraStackSpace)! + MEND + + ;; Complete the setup of a probe frame allocated with ALLOC_PROBE_FRAME with the initialization that can + ;; occur only outside the prolog (includes linking the frame to the current Thread). This macro assumes SP + ;; is invariant outside of the prolog. + ;; + ;; $threadReg : register containing the Thread* (this will be preserved) + ;; $trashReg : register that can be trashed by this macro + ;; $savedRegsMask : value to initialize m_Flags field with (register or #constant) + ;; $gcFlags : value of gcref / gcbyref flags for saved registers, used only if $savedRegsMask is constant + ;; $frameSize : total size of the method's stack frame (including probe frame size) + MACRO + INIT_PROBE_FRAME $threadReg, $trashReg, $savedRegsMask, $gcFlags, $frameSize + + LCLS BitmaskStr +BitmaskStr SETS "$savedRegsMask" + + str $threadReg, [sp, #OFFSETOF__PInvokeTransitionFrame__m_pThread] ; Thread * + IF BitmaskStr:LEFT:1 == "#" + ;; The savedRegsMask is a constant, remove the leading "#" since the MOVL64 doesn't expect it +BitmaskStr SETS BitmaskStr:RIGHT:(:LEN:BitmaskStr - 1) + MOVL64 $trashReg, $BitmaskStr, $gcFlags + ELSE + ASSERT "$gcFlags" == "" + ;; The savedRegsMask is a register + mov $trashReg, $savedRegsMask + ENDIF + str $trashReg, [sp, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + add $trashReg, sp, #$frameSize + str $trashReg, [sp, #m_CallersSP] + MEND + + ;; Simple macro to use when setting up the probe frame can comprise the entire prolog. Call this macro + ;; first in the method (no further prolog instructions can be added after this). + ;; + ;; $threadReg : register containing the Thread* (this will be preserved). If defaulted (specify |) then + ;; the current thread will be calculated inline into r2 ($trashReg must not equal r2 in + ;; this case) + ;; $trashReg : register that can be trashed by this macro + ;; $savedRegsMask : value to initialize m_dwFlags field with (register or #constant) + ;; $gcFlags : value of gcref / gcbyref flags for saved registers, used only if $savedRegsMask is constant + MACRO + PROLOG_PROBE_FRAME $threadReg, $trashReg, $savedRegsMask, $gcFlags + + ; Local string tracking the name of the register in which the Thread* is kept. Defaults to the value + ; of $threadReg. + LCLS __PPF_ThreadReg +__PPF_ThreadReg SETS "$threadReg" + + ; Define the method prolog, allocating enough stack space for the PInvokeTransitionFrame and saving + ; incoming register values into it. + ALLOC_PROBE_FRAME 0, {true} + + ; If the caller didn't provide a value for $threadReg then generate code to fetch the Thread* into x2. + ; Record that x2 holds the Thread* in our local variable. + IF "$threadReg" == "" + ASSERT "$trashReg" != "x2" +__PPF_ThreadReg SETS "x2" + INLINE_GETTHREAD $__PPF_ThreadReg, $trashReg + ENDIF + + ; Perform the rest of the PInvokeTransitionFrame initialization. + INIT_PROBE_FRAME $__PPF_ThreadReg, $trashReg, $savedRegsMask, $gcFlags, PROBE_FRAME_SIZE + mov $trashReg, sp + str $trashReg, [$__PPF_ThreadReg, #OFFSETOF__Thread__m_pHackPInvokeTunnel] + MEND + + ; Simple macro to use when PROLOG_PROBE_FRAME was used to set up and initialize the prolog and + ; PInvokeTransitionFrame. This will define the epilog including a return via the restored LR. + MACRO + EPILOG_PROBE_FRAME + + FREE_PROBE_FRAME 0, {true} + EPILOG_RETURN + MEND + +;; In order to avoid trashing VFP registers across the loop hijack we must save all user registers, so that +;; registers used by the loop being hijacked will not be affected. Unlike ARM32 where neon registers (NQ0, ..., NQ15) +;; are fully covered by the floating point registers D0 ... D31, we have 32 neon registers Q0, ... Q31 on ARM64 +;; which are not fully covered by the register D0 ... D31. Therefore we must explicitly save all Q registers. +EXTRA_SAVE_SIZE equ (32*16) + + MACRO + ALLOC_LOOP_HIJACK_FRAME + + PROLOG_STACK_ALLOC EXTRA_SAVE_SIZE + + ;; Save all neon registers + PROLOG_NOP stp q0, q1, [sp] + PROLOG_NOP stp q2, q3, [sp, #0x20] + PROLOG_NOP stp q4, q5, [sp, #0x40] + PROLOG_NOP stp q6, q7, [sp, #0x60] + PROLOG_NOP stp q8, q9, [sp, #0x80] + PROLOG_NOP stp q10, q11, [sp, #0xA0] + PROLOG_NOP stp q12, q13, [sp, #0xC0] + PROLOG_NOP stp q14, q15, [sp, #0xE0] + PROLOG_NOP stp q16, q17, [sp, #0x100] + PROLOG_NOP stp q18, q19, [sp, #0x120] + PROLOG_NOP stp q20, q21, [sp, #0x140] + PROLOG_NOP stp q22, q23, [sp, #0x160] + PROLOG_NOP stp q24, q25, [sp, #0x180] + PROLOG_NOP stp q26, q27, [sp, #0x1A0] + PROLOG_NOP stp q28, q29, [sp, #0x1C0] + PROLOG_NOP stp q30, q31, [sp, #0x1E0] + + ALLOC_PROBE_FRAME 0, {false} + MEND + + MACRO + FREE_LOOP_HIJACK_FRAME + + FREE_PROBE_FRAME 0, {false} + + ;; restore all neon registers + PROLOG_NOP ldp q0, q1, [sp] + PROLOG_NOP ldp q2, q3, [sp, #0x20] + PROLOG_NOP ldp q4, q5, [sp, #0x40] + PROLOG_NOP ldp q6, q7, [sp, #0x60] + PROLOG_NOP ldp q8, q9, [sp, #0x80] + PROLOG_NOP ldp q10, q11, [sp, #0xA0] + PROLOG_NOP ldp q12, q13, [sp, #0xC0] + PROLOG_NOP ldp q14, q15, [sp, #0xE0] + PROLOG_NOP ldp q16, q17, [sp, #0x100] + PROLOG_NOP ldp q18, q19, [sp, #0x120] + PROLOG_NOP ldp q20, q21, [sp, #0x140] + PROLOG_NOP ldp q22, q23, [sp, #0x160] + PROLOG_NOP ldp q24, q25, [sp, #0x180] + PROLOG_NOP ldp q26, q27, [sp, #0x1A0] + PROLOG_NOP ldp q28, q29, [sp, #0x1C0] + PROLOG_NOP ldp q30, q31, [sp, #0x1E0] + + EPILOG_STACK_FREE EXTRA_SAVE_SIZE + MEND + +;; +;; Macro to clear the hijack state. This is safe to do because the suspension code will not Unhijack this +;; thread if it finds it at an IP that isn't managed code. +;; +;; Register state on entry: +;; x2: thread pointer +;; +;; Register state on exit: +;; + MACRO + ClearHijackState + + ASSERT OFFSETOF__Thread__m_pvHijackedReturnAddress == (OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation + 8) + ;; Clear m_ppvHijackedReturnAddressLocation and m_pvHijackedReturnAddress + stp xzr, xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + ;; Clear m_uHijackedReturnValueFlags + str xzr, [x2, #OFFSETOF__Thread__m_uHijackedReturnValueFlags] + MEND + +;; +;; The prolog for all GC suspension hijacks (normal and stress). Fixes up the hijacked return address, and +;; clears the hijack state. +;; +;; Register state on entry: +;; All registers correct for return to the original return address. +;; +;; Register state on exit: +;; x2: thread pointer +;; x3: trashed +;; x12: transition frame flags for the return registers x0 and x1 +;; + MACRO + FixupHijackedCallstack + + ;; x2 <- GetThread(), TRASHES x3 + INLINE_GETTHREAD x2, x3 + + ;; + ;; Fix the stack by restoring the original return address + ;; + ASSERT OFFSETOF__Thread__m_uHijackedReturnValueFlags == (OFFSETOF__Thread__m_pvHijackedReturnAddress + 8) + ;; Load m_pvHijackedReturnAddress and m_uHijackedReturnValueFlags + ldp lr, x12, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + + ClearHijackState + MEND + +;; +;; Set the Thread state and wait for a GC to complete. +;; +;; Register state on entry: +;; x4: thread pointer +;; +;; Register state on exit: +;; x4: thread pointer +;; All other registers trashed +;; + + EXTERN RhpWaitForGCNoAbort + + MACRO + WaitForGCCompletion + + ldr w2, [x4, #OFFSETOF__Thread__m_ThreadStateFlags] + tst w2, #TSF_SuppressGcStress__OR__TSF_DoNotTriggerGC + bne %ft0 + + ldr x9, [x4, #OFFSETOF__Thread__m_pHackPInvokeTunnel] + bl RhpWaitForGCNoAbort +0 + MEND + + MACRO + HijackTargetFakeProlog + + ;; This is a fake entrypoint for the method that 'tricks' the OS into calling our personality routine. + ;; The code here should never be executed, and the unwind info is bogus, but we don't mind since the + ;; stack is broken by the hijack anyway until after we fix it below. + PROLOG_SAVE_REG_PAIR fp, lr, #-0x10! + nop ; We also need a nop here to simulate the implied bl instruction. Without + ; this, an OS-applied -4 will back up into the method prolog and the unwind + ; will not be applied as desired. + + MEND + +;; +;; +;; +;; GC Probe Hijack targets +;; +;; + EXTERN RhpPInvokeExceptionGuard + + NESTED_ENTRY RhpGcProbeHijackWrapper, .text, RhpPInvokeExceptionGuard + HijackTargetFakeProlog + + LABELED_RETURN_ADDRESS RhpGcProbeHijack + + FixupHijackedCallstack + orr x12, x12, #DEFAULT_FRAME_SAVE_FLAGS + b RhpGcProbe + NESTED_END RhpGcProbeHijackWrapper + +#ifdef FEATURE_GC_STRESS +;; +;; +;; GC Stress Hijack targets +;; +;; + LEAF_ENTRY RhpGcStressHijack + FixupHijackedCallstack + orr x12, x12, #DEFAULT_FRAME_SAVE_FLAGS + b RhpGcStressProbe + LEAF_END RhpGcStressHijack +;; +;; Worker for our GC stress probes. Do not call directly!! +;; Instead, go through RhpGcStressHijack{Scalar|Object|Byref}. +;; This worker performs the GC Stress work and returns to the original return address. +;; +;; Register state on entry: +;; x0: hijacked function return value +;; x1: hijacked function return value +;; x2: thread pointer +;; w12: register bitmask +;; +;; Register state on exit: +;; Scratch registers, except for x0, have been trashed +;; All other registers restored as they were when the hijack was first reached. +;; + NESTED_ENTRY RhpGcStressProbe + PROLOG_PROBE_FRAME x2, x3, x12, + + bl $REDHAWKGCINTERFACE__STRESSGC + + EPILOG_PROBE_FRAME + NESTED_END RhpGcStressProbe +#endif ;; FEATURE_GC_STRESS + + LEAF_ENTRY RhpGcProbe + ldr x3, =RhpTrapThreads + ldr w3, [x3] + tbnz x3, #TrapThreadsFlags_TrapThreads_Bit, RhpGcProbeRare + ret + LEAF_END RhpGcProbe + + EXTERN RhpThrowHwEx + + NESTED_ENTRY RhpGcProbeRare + PROLOG_PROBE_FRAME x2, x3, x12, + + mov x4, x2 + WaitForGCCompletion + + ldr x2, [sp, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + tbnz x2, #PTFF_THREAD_ABORT_BIT, %F1 + + EPILOG_PROBE_FRAME + +1 + FREE_PROBE_FRAME 0, {true} + EPILOG_NOP mov w0, #STATUS_REDHAWK_THREAD_ABORT + EPILOG_NOP mov x1, lr ;; return address as exception PC + EPILOG_NOP b RhpThrowHwEx + NESTED_END RhpGcProbeRare + + LEAF_ENTRY RhpGcPoll + ldr x0, =RhpTrapThreads + ldr w0, [x0] + cbnz w0, RhpGcPollRare ;; TrapThreadsFlags_None = 0 + ret + LEAF_END RhpGcPoll + + NESTED_ENTRY RhpGcPollRare + PUSH_COOP_PINVOKE_FRAME x0 + bl RhpGcPoll2 + POP_COOP_PINVOKE_FRAME + ret + NESTED_END RhpGcPollRare + +#ifdef FEATURE_GC_STRESS + NESTED_ENTRY RhpHijackForGcStress + ;; This function should be called from right before epilog + + ;; Push FP and LR, and allocate stack to hold PAL_LIMITED_CONTEXT structure and VFP return value registers + PROLOG_SAVE_REG_PAIR fp, lr, #-(SIZEOF__PAL_LIMITED_CONTEXT + 0x20)! + + ;; + ;; Setup a PAL_LIMITED_CONTEXT that looks like what you'd get if you had suspended this thread at the + ;; IP after the call to this helper. + ;; + ;; This is very likely overkill since the calculation of the return address should only need SP and + ;; LR, but this is test code, so I'm not too worried about efficiency. + ;; + ;; Setup a PAL_LIMITED_CONTEXT on the stack + ;; { + ;; FP and LR already pushed. + PROLOG_NOP stp x0, x1, [sp, #0x10] + PROLOG_SAVE_REG_PAIR x19, x20, #0x20 + PROLOG_SAVE_REG_PAIR x21, x22, #0x30 + PROLOG_SAVE_REG_PAIR x23, x24, #0x40 + PROLOG_SAVE_REG_PAIR x25, x26, #0x50 + PROLOG_SAVE_REG_PAIR x27, x28, #0x60 + PROLOG_SAVE_REG lr, #0x78 + + ;; } end PAL_LIMITED_CONTEXT + + ;; Save VFP return value + stp d0, d1, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x00)] + stp d2, d3, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x10)] + + ;; Compute and save SP at callsite. + add x0, sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x20) ;; +0x20 for the pushes right before the context struct + str x0, [sp, #OFFSETOF__PAL_LIMITED_CONTEXT__SP] + + mov x0, sp ; Address of PAL_LIMITED_CONTEXT + bl $THREAD__HIJACKFORGCSTRESS + + ;; Restore return value registers (saved in PAL_LIMITED_CONTEXT structure) + ldp x0, x1, [sp, #0x10] + + ;; Restore VFP return value + ldp d0, d1, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x00)] + ldp d2, d3, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x10)] + + ;; Epilog + EPILOG_RESTORE_REG_PAIR x19, x20, #0x20 + EPILOG_RESTORE_REG_PAIR x21, x22, #0x30 + EPILOG_RESTORE_REG_PAIR x23, x24, #0x40 + EPILOG_RESTORE_REG_PAIR x25, x26, #0x50 + EPILOG_RESTORE_REG_PAIR x27, x28, #0x60 + EPILOG_RESTORE_REG_PAIR fp, lr, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x20)! + EPILOG_RETURN + + NESTED_END RhpHijackForGcStress + + NESTED_ENTRY RhpHijackForGcStressLeaf + ;; This should be jumped to, right before epilog + ;; x9 has the return address (we don't care about trashing scratch regs at this point) + + ;; Push FP and LR, and allocate stack to hold PAL_LIMITED_CONTEXT structure and VFP return value registers + PROLOG_SAVE_REG_PAIR fp, lr, #-(SIZEOF__PAL_LIMITED_CONTEXT + 0x20)! + + ;; + ;; Setup a PAL_LIMITED_CONTEXT that looks like what you'd get if you had suspended this thread at the + ;; IP after the call to this helper. + ;; + ;; This is very likely overkill since the calculation of the return address should only need SP and + ;; LR, but this is test code, so I'm not too worried about efficiency. + ;; + ;; Setup a PAL_LIMITED_CONTEXT on the stack + ;; { + ;; FP and LR already pushed. + PROLOG_NOP stp x0, x1, [sp, #0x10] + PROLOG_SAVE_REG_PAIR x19, x20, #0x20 + PROLOG_SAVE_REG_PAIR x21, x22, #0x30 + PROLOG_SAVE_REG_PAIR x23, x24, #0x40 + PROLOG_SAVE_REG_PAIR x25, x26, #0x50 + PROLOG_SAVE_REG_PAIR x27, x28, #0x60 + ; PROLOG_SAVE_REG macro doesn't let to use scratch reg: + PROLOG_NOP str x9, [sp, #0x78] ; this is return address from RhpHijackForGcStress; lr is return address for it's caller + + ;; } end PAL_LIMITED_CONTEXT + + ;; Save VFP return value + stp d0, d1, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x00)] + stp d2, d3, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x10)] + + ;; Compute and save SP at callsite. + add x0, sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x20) ;; +0x20 for the pushes right before the context struct + str x0, [sp, #OFFSETOF__PAL_LIMITED_CONTEXT__SP] + + mov x0, sp ; Address of PAL_LIMITED_CONTEXT + bl $THREAD__HIJACKFORGCSTRESS + + ;; Restore return value registers (saved in PAL_LIMITED_CONTEXT structure) + ldp x0, x1, [sp, #0x10] + + ;; Restore VFP return value + ldp d0, d1, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x00)] + ldp d2, d3, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x10)] + + ;; Epilog + EPILOG_RESTORE_REG_PAIR x19, x20, #0x20 + EPILOG_RESTORE_REG_PAIR x21, x22, #0x30 + EPILOG_RESTORE_REG_PAIR x23, x24, #0x40 + EPILOG_RESTORE_REG_PAIR x25, x26, #0x50 + EPILOG_RESTORE_REG_PAIR x27, x28, #0x60 + EPILOG_NOP ldr x9, [sp, #0x78] + EPILOG_RESTORE_REG_PAIR fp, lr, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x20)! + EPILOG_NOP br x9 + + NESTED_END RhpHijackForGcStressLeaf + +;; +;; INVARIANT: Don't trash the argument registers, the binder codegen depends on this. +;; + LEAF_ENTRY RhpSuppressGcStress + INLINE_GETTHREAD x9, x10 + add x9, x9, #OFFSETOF__Thread__m_ThreadStateFlags +Retry + ldxr w10, [x9] + orr w10, w10, #TSF_SuppressGcStress + stxr w11, w10, [x9] + cbz w11, Success + b Retry + +Success + ret + LEAF_END RhpSuppressGcStress +#endif ;; FEATURE_GC_STRESS + + INLINE_GETTHREAD_CONSTANT_POOL + + end + diff --git a/src/coreclr/nativeaot/Runtime/arm64/GetThread.asm b/src/coreclr/nativeaot/Runtime/arm64/GetThread.asm new file mode 100644 index 00000000000000..7c01e66453385c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/GetThread.asm @@ -0,0 +1,29 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpGetThread +;; +;; +;; INPUT: none +;; +;; OUTPUT: x9: Thread pointer +;; +;; MUST PRESERVE ARGUMENT REGISTERS +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + LEAF_ENTRY RhpGetThread + ;; x9 = GetThread(), TRASHES xip0 (which can be used as an intra-procedure-call scratch register) + INLINE_GETTHREAD x9, xip0 + ret + LEAF_END +FASTCALL_ENDFUNC + + INLINE_GETTHREAD_CONSTANT_POOL + + end diff --git a/src/coreclr/nativeaot/Runtime/arm64/Interlocked.S b/src/coreclr/nativeaot/Runtime/arm64/Interlocked.S new file mode 100644 index 00000000000000..4b5aa6f56e2ed8 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/Interlocked.S @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +// WARNING: Code in EHHelpers.cpp makes assumptions about this helper, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpLockCmpXchg32AVLocation +// - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address +// x0 = destination address +// w1 = value +// w2 = comparand +LEAF_ENTRY RhpLockCmpXchg32, _TEXT + mov x8, x0 // Save value of x0 into x8 as x0 is used for the return value +ALTERNATE_ENTRY RhpLockCmpXchg32AVLocation +1: // loop + ldaxr w0, [x8] // w0 = *x8 + cmp w0, w2 + bne 2f // if (w0 != w2) goto exit + stlxr w9, w1, [x8] // if (w0 == w2) { try *x8 = w1 and goto loop if failed or goto exit } + cbnz w9, 1b + +2: // exit + ArmInterlockedOperationBarrier + ret +LEAF_END RhpLockCmpXchg32, _TEXT + +// WARNING: Code in EHHelpers.cpp makes assumptions about this helper, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpLockCmpXchg64AVLocation +// - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address +// x0 = destination address +// x1 = value +// x2 = comparand +LEAF_ENTRY RhpLockCmpXchg64, _TEXT + mov x8, x0 // Save value of x0 into x8 as x0 is used for the return value +ALTERNATE_ENTRY RhpLockCmpXchg64AVLocation +1: // loop + ldaxr x0, [x8] // x0 = *x8 + cmp x0, x2 + bne 2f // if (x0 != x2) goto exit + stlxr w9, x1, [x8] // if (x0 == x2) { try *x8 = x1 and goto loop if failed or goto exit } + cbnz w9, 1b +2: // exit + ArmInterlockedOperationBarrier + ret +LEAF_END RhpLockCmpXchg64, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm64/Interlocked.asm b/src/coreclr/nativeaot/Runtime/arm64/Interlocked.asm new file mode 100644 index 00000000000000..19ba1454dff49f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/Interlocked.asm @@ -0,0 +1,50 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + + ;; WARNING: Code in EHHelpers.cpp makes assumptions about this helper, in particular: + ;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpLockCmpXchg32AVLocation + ;; - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address + ;; x0 = destination address + ;; w1 = value + ;; w2 = comparand + LEAF_ENTRY RhpLockCmpXchg32, _TEXT + mov x8, x0 ;; Save value of x0 into x8 as x0 is used for the return value + ALTERNATE_ENTRY RhpLockCmpXchg32AVLocation +1 ;; loop + ldaxr w0, [x8] ;; w0 = *x8 + cmp w0, w2 + bne %ft2 ;; if (w0 != w2) goto exit + stlxr w9, w1, [x8] ;; if (w0 == w2) { try *x8 = w1 and goto loop if failed or goto exit } + cbnz w9, %bt1 + +2 ;; exit + ArmInterlockedOperationBarrier + ret + LEAF_END RhpLockCmpXchg32 + + ;; WARNING: Code in EHHelpers.cpp makes assumptions about this helper, in particular: + ;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpLockCmpXchg64AVLocation + ;; - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address + ;; x0 = destination address + ;; x1 = value + ;; x2 = comparand + LEAF_ENTRY RhpLockCmpXchg64, _TEXT + mov x8, x0 ;; Save value of x0 into x8 as x0 is used for the return value + ALTERNATE_ENTRY RhpLockCmpXchg64AVLocation +1 ;; loop + ldaxr x0, [x8] ;; x0 = *x8 + cmp x0, x2 + bne %ft2 ;; if (x0 != x2) goto exit + stlxr w9, x1, [x8] ;; if (x0 == x2) { try *x8 = x1 and goto loop if failed or goto exit } + cbnz w9, %bt1 + +2 ;; exit + ArmInterlockedOperationBarrier + ret + LEAF_END RhpLockCmpXchg64 + + end diff --git a/src/coreclr/nativeaot/Runtime/arm64/InteropThunksHelpers.S b/src/coreclr/nativeaot/Runtime/arm64/InteropThunksHelpers.S new file mode 100644 index 00000000000000..f883120a2b5f3e --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/InteropThunksHelpers.S @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +POINTER_SIZE = 0x08 + +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interop Thunks Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + // + // RhCommonStub + // + // INPUT: xip0: thunk's data block + // + // TRASHES: x9, x10, xip0 + // + LEAF_ENTRY RhCommonStub, _TEXT + // There are arbitrary callers passing arguments with arbitrary signatures. + // Custom calling convention: + // xip0 pointer to the current thunk's data block (data contains 2 pointer values: context + target pointers) + +#ifdef FEATURE_EMULATED_TLS + // This doesn't save and restore the floating point argument registers. If we encounter a + // target system that uses TLS emulation and modify these registers during this call we + // need to save and restore them, too + GETTHUNKDATA_ETLS_9 +#else + INLINE_GET_TLS_VAR x9, tls_thunkData +#endif + + // x9 = base address of TLS data + // xip0 = address of context cell in thunk's data + + // store thunk address in thread static + ldr x10, [xip0] + str x10, [x9] + + // Now load the target address and jump to it. + ldr xip0, [xip0, #POINTER_SIZE] + br xip0 + + LEAF_END RhCommonStub, _TEXT + + // + // IntPtr RhGetCommonStubAddress() + // + LEAF_ENTRY RhGetCommonStubAddress, _TEXT + PREPARE_EXTERNAL_VAR RhCommonStub, x0 + ret + LEAF_END RhGetCommonStubAddress, _TEXT + + +#ifndef FEATURE_EMULATED_TLS + // + // IntPtr RhGetCurrentThunkContext() + // + LEAF_ENTRY RhGetCurrentThunkContext, _TEXT + + INLINE_GET_TLS_VAR x1, tls_thunkData + + ldr x0, [x1] + + ret + + LEAF_END RhGetCurrentThunkContext, _TEXT +#endif //FEATURE_EMULATED_TLS diff --git a/src/coreclr/nativeaot/Runtime/arm64/InteropThunksHelpers.asm b/src/coreclr/nativeaot/Runtime/arm64/InteropThunksHelpers.asm new file mode 100644 index 00000000000000..7b9cc54413e348 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/InteropThunksHelpers.asm @@ -0,0 +1,91 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + +#include "ksarm64.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +__tls_array equ 0x58 ;; offsetof(TEB, ThreadLocalStoragePointer) + +POINTER_SIZE equ 0x08 + +;; TLS variables + AREA |.tls$|, DATA +ThunkParamSlot % 0x8 + + TEXTAREA + + EXTERN _tls_index + + ;; Section relocs are 32 bits. Using an extra DCD initialized to zero for 8-byte alignment. +__SECTIONREL_ThunkParamSlot + DCD ThunkParamSlot + RELOC 8, ThunkParamSlot ;; SECREL + DCD 0 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interop Thunks Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;; + ;; RhCommonStub + ;; + ;; INPUT: xip0: thunk's data block + ;; + ;; TRASHES: x9, x10, x11, xip0 + ;; + LEAF_ENTRY RhCommonStub + ;; There are arbitrary callers passing arguments with arbitrary signatures. + ;; Custom calling convention: + ;; xip0 pointer to the current thunk's data block (data contains 2 pointer values: context + target pointers) + + ;; Save context data into the ThunkParamSlot thread-local variable + ;; A pointer to the delegate and function pointer for open static delegate should have been saved in the thunk's context cell during thunk allocation + ldr x10, =_tls_index + ldr w10, [x10] + ldr x9, [xpr, #__tls_array] + ldr x9, [x9, x10 lsl #3] ;; x9 <- our TLS base + + ;; x9 = base address of TLS data + ;; x10 = trashed + ;; xip0 = address of context cell in thunk's data + + ;; store thunk address in thread static + ldr x10, [xip0] + ldr x11, =__SECTIONREL_ThunkParamSlot + ldr x11, [x11] + str x10, [x9, x11] ;; ThunkParamSlot <- context slot data + + ;; Now load the target address and jump to it. + ldr xip0, [xip0, #POINTER_SIZE] + br xip0 + + LEAF_END RhCommonStub + + ;; + ;; IntPtr RhGetCommonStubAddress() + ;; + LEAF_ENTRY RhGetCommonStubAddress + ldr x0, =RhCommonStub + ret + LEAF_END RhGetCommonStubAddress + + + ;; + ;; IntPtr RhGetCurrentThunkContext() + ;; + LEAF_ENTRY RhGetCurrentThunkContext + + ldr x1, =_tls_index + ldr w1, [x1] + ldr x0, [xpr, #__tls_array] + ldr x0, [x0, x1 lsl #3] ;; x0 <- our TLS base + + ldr x1, =__SECTIONREL_ThunkParamSlot + ldr x1, [x1] + ldr x0, [x0, x1] ;; x0 <- ThunkParamSlot + + ret + + LEAF_END RhGetCurrentThunkContext + + END diff --git a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S new file mode 100644 index 00000000000000..61ae4e4046c6b0 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include "AsmOffsets.inc" + + .global memcpy + .global memcpyGCRefs + .global memcpyGCRefsWithWriteBarrier + .global memcpyAnyWithWriteBarrier + +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +// +// void* RhpCopyMultibyteNoGCRefs(void*, void*, size_t) +// +// The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +// the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +// it to managed code. +// + + LEAF_ENTRY RhpCopyMultibyteNoGCRefs, _TEXT + + // x0 dest + // x1 src + // x2 count + + cbz x2, NothingToCopy_NoGCRefs // check for a zero-length copy + + // Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + // unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + // translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyMultibyteNoGCRefsDestAVLocation + ldrb wzr, [x0] + ALTERNATE_ENTRY RhpCopyMultibyteNoGCRefsSrcAVLocation + ldrb wzr, [x1] + + // tail-call to plain-old-memcpy + b memcpy + +NothingToCopy_NoGCRefs: + // dest is already in x0 + ret + + LEAF_END RhpCopyMultibyteNoGCRefs, _TEXT + + +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +// +// void* RhpCopyMultibyte(void*, void*, size_t) +// +// The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +// the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +// it to managed code. +// + + LEAF_ENTRY RhpCopyMultibyte, _TEXT + + // x0 dest + // x1 src + // x2 count + + // check for a zero-length copy + cbz x2, NothingToCopy_RhpCopyMultibyte + + // Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + // unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + // translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyMultibyteDestAVLocation + ldrb wzr, [x0] + ALTERNATE_ENTRY RhpCopyMultibyteSrcAVLocation + ldrb wzr, [x1] + + // tail-call to the GC-safe memcpy implementation + b memcpyGCRefs + +NothingToCopy_RhpCopyMultibyte: + // dest is already still in x0 + ret + + LEAF_END RhpCopyMultibyte, _TEXT + +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +// +// void* RhpCopyMultibyteWithWriteBarrier(void*, void*, size_t) +// +// The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +// the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +// it to managed code. +// Runs a card table update via RhpBulkWriteBarrier after the copy +// + + LEAF_ENTRY RhpCopyMultibyteWithWriteBarrier, _TEXT + + // x0 dest + // x1 src + // x2 count + + // check for a zero-length copy + cbz x2, NothingToCopy_RhpCopyMultibyteWithWriteBarrier + + // Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + // unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + // translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyMultibyteWithWriteBarrierDestAVLocation + ldrb wzr, [x0] + ALTERNATE_ENTRY RhpCopyMultibyteWithWriteBarrierSrcAVLocation + ldrb wzr, [x1] + + // tail-call to the GC-safe memcpy implementation + b memcpyGCRefsWithWriteBarrier + +NothingToCopy_RhpCopyMultibyteWithWriteBarrier: + // dest is already still in x0 + ret + LEAF_END RhpCopyMultibyteWithWriteBarrier, _TEXT + +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +// +// void* RhpCopyAnyWithWriteBarrier(void*, void*, size_t) +// +// The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +// the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +// it to managed code. +// Runs a card table update via RhpBulkWriteBarrier after the copy if it contained GC pointers +// + + LEAF_ENTRY RhpCopyAnyWithWriteBarrier, _TEXT + + // x0 dest + // x1 src + // x2 count + + // check for a zero-length copy + cbz x2, NothingToCopy_RhpCopyAnyWithWriteBarrier + + // Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + // unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + // translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyAnyWithWriteBarrierDestAVLocation + ldrb wzr, [x0] + ALTERNATE_ENTRY RhpCopyAnyWithWriteBarrierSrcAVLocation + ldrb wzr, [x1] + + // tail-call to the GC-safe memcpy implementation + b memcpyAnyWithWriteBarrier + +NothingToCopy_RhpCopyAnyWithWriteBarrier: + // dest is already still in x0 + ret + + LEAF_END RhpCopyAnyWithWriteBarrier, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm new file mode 100644 index 00000000000000..b845854cdb2653 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm @@ -0,0 +1,154 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + EXTERN memcpy + EXTERN memcpyGCRefs + EXTERN memcpyGCRefsWithWriteBarrier + EXTERN memcpyAnyWithWriteBarrier + + TEXTAREA + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyMultibyteNoGCRefs(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; + + LEAF_ENTRY RhpCopyMultibyteNoGCRefs + + ; x0 dest + ; x1 src + ; x2 count + + cbz x2, NothingToCopy_NoGCRefs ; check for a zero-length copy + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyMultibyteNoGCRefsDestAVLocation + ldrb wzr, [x0] + ALTERNATE_ENTRY RhpCopyMultibyteNoGCRefsSrcAVLocation + ldrb wzr, [x1] + + ; tail-call to plain-old-memcpy + b memcpy + +NothingToCopy_NoGCRefs + ; dest is already in x0 + ret + + LEAF_END + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyMultibyte(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; + + LEAF_ENTRY RhpCopyMultibyte + + ; x0 dest + ; x1 src + ; x2 count + + ; check for a zero-length copy + cbz x2, NothingToCopy_RhpCopyMultibyte + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyMultibyteDestAVLocation + ldrb wzr, [x0] + ALTERNATE_ENTRY RhpCopyMultibyteSrcAVLocation + ldrb wzr, [x1] + + ; tail-call to the GC-safe memcpy implementation + b memcpyGCRefs + +NothingToCopy_RhpCopyMultibyte + ; dest is already still in x0 + ret + + LEAF_END + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyMultibyteWithWriteBarrier(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; Runs a card table update via RhpBulkWriteBarrier after the copy +;; + + LEAF_ENTRY RhpCopyMultibyteWithWriteBarrier + + ; x0 dest + ; x1 src + ; x2 count + + ; check for a zero-length copy + cbz x2, NothingToCopy_RhpCopyMultibyteWithWriteBarrier + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyMultibyteWithWriteBarrierDestAVLocation + ldrb wzr, [x0] + ALTERNATE_ENTRY RhpCopyMultibyteWithWriteBarrierSrcAVLocation + ldrb wzr, [x1] + + ; tail-call to the GC-safe memcpy implementation + b memcpyGCRefsWithWriteBarrier + +NothingToCopy_RhpCopyMultibyteWithWriteBarrier + ; dest is already still in x0 + ret + LEAF_END + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyAnyWithWriteBarrier(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; Runs a card table update via RhpBulkWriteBarrier after the copy if it contained GC pointers +;; + + LEAF_ENTRY RhpCopyAnyWithWriteBarrier + + ; x0 dest + ; x1 src + ; x2 count + + ; check for a zero-length copy + cbz x2, NothingToCopy_RhpCopyAnyWithWriteBarrier + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyAnyWithWriteBarrierDestAVLocation + ldrb wzr, [x0] + ALTERNATE_ENTRY RhpCopyAnyWithWriteBarrierSrcAVLocation + ldrb wzr, [x1] + + ; tail-call to the GC-safe memcpy implementation + b memcpyAnyWithWriteBarrier + +NothingToCopy_RhpCopyAnyWithWriteBarrier + ; dest is already still in x0 + ret + + LEAF_END + + end diff --git a/src/coreclr/nativeaot/Runtime/arm64/PInvoke.S b/src/coreclr/nativeaot/Runtime/arm64/PInvoke.S new file mode 100644 index 00000000000000..f66851461f278f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/PInvoke.S @@ -0,0 +1,245 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +#include +#include "AsmOffsets.inc" + +.global RhpTrapThreads + +// Note: these must match the defs in PInvokeTransitionFrameFlags defined in rhbinder.h +PTFF_SAVE_SP = 0x00000400 + +// Bit position for the flags above, to be used with tbz / tbnz instructions +PTFF_THREAD_ABORT_BIT = 36 + +// Bit position for the flags above, to be used with tbz/tbnz instructions +TSF_Attached_Bit = 0 +TSF_SuppressGcStress_Bit = 3 +TSF_DoNotTriggerGc_Bit = 4 + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpWaitForSuspend -- rare path for RhpPInvoke and RhpReversePInvokeReturn +// +// +// INPUT: none +// +// TRASHES: none +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + NESTED_ENTRY RhpWaitForSuspend, _TEXT, NoHandler + + // FP and LR registers + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -0xA0 // Push down stack pointer and store FP and LR + + // Need to save argument registers x0-x7 and the return buffer register x8 + // Also save x9 which may be used for saving indirect call target + stp x0, x1, [sp, #0x10] + stp x2, x3, [sp, #0x20] + stp x4, x5, [sp, #0x30] + stp x6, x7, [sp, #0x40] + stp x8, x9, [sp, #0x50] + + // Save float argument registers as well since they are volatile + stp d0, d1, [sp, #0x60] + stp d2, d3, [sp, #0x70] + stp d4, d5, [sp, #0x80] + stp d6, d7, [sp, #0x90] + + bl RhpWaitForSuspend2 + + // Restore floating point registers + ldp d0, d1, [sp, #0x60] + ldp d2, d3, [sp, #0x70] + ldp d4, d5, [sp, #0x80] + ldp d6, d7, [sp, #0x90] + + // Restore the argument registers + ldp x0, x1, [sp, #0x10] + ldp x2, x3, [sp, #0x20] + ldp x4, x5, [sp, #0x30] + ldp x6, x7, [sp, #0x40] + ldp x8, x9, [sp, #0x50] + + // Restore FP and LR registers, and free the allocated stack block + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 0xA0 + EPILOG_RETURN + + NESTED_END RhpWaitForSuspend, _TEXT + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpWaitForGCNoAbort +// +// +// INPUT: x9: transition frame +// +// TRASHES: None +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + NESTED_ENTRY RhpWaitForGCNoAbort, _TEXT, NoHandler + + // FP and LR registers + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -0x40 // Push down stack pointer and store FP and LR + + // Save the integer return registers, as well as the floating return registers + stp x0, x1, [sp, #0x10] + stp d0, d1, [sp, #0x20] + stp d2, d3, [sp, #0x30] + + ldr x0, [x9, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + ldr w0, [x0, #OFFSETOF__Thread__m_ThreadStateFlags] + tbnz x0, #TSF_DoNotTriggerGc_Bit, Done + + mov x0, x9 // passing transition frame in x0 + bl RhpWaitForGC2 + +Done: + ldp x0, x1, [sp, #0x10] + ldp d0, d1, [sp, #0x20] + ldp d2, d3, [sp, #0x30] + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 0x40 + EPILOG_RETURN + + NESTED_END RhpWaitForGCNoAbort + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpWaitForGC +// +// +// INPUT: x9: transition frame +// +// TRASHES: x0, x1, x10 +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + NESTED_ENTRY RhpWaitForGC, _TEXT, NoHandler + + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -0x10 + + PREPARE_EXTERNAL_VAR_INDIRECT_W RhpTrapThreads, 10 + + tbz x10, #TrapThreadsFlags_TrapThreads_Bit, NoWait + bl RhpWaitForGCNoAbort +NoWait: + tbz x10, #TrapThreadsFlags_AbortInProgress_Bit, NoAbort + ldr x10, [x9, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + tbz x10, #PTFF_THREAD_ABORT_BIT, NoAbort + + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 0x10 + mov w0, #STATUS_REDHAWK_THREAD_ABORT + mov x1, lr // hijack target address as exception PC + b RhpThrowHwEx + +NoAbort: + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 0x10 + EPILOG_RETURN + + NESTED_END RhpWaitForGC, _TEXT + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpReversePInvokeAttachOrTrapThread -- rare path for RhpPInvoke +// +// +// INPUT: x9: address of reverse pinvoke frame +// +// PRESERVES: x0-x8 -- need to preserve these because the caller assumes they are not trashed +// +// TRASHES: none +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + NESTED_ENTRY RhpReversePInvokeAttachOrTrapThread, _TEXT, NoHandler + + // FP and LR registers + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -0xA0 // Push down stack pointer and store FP and LR + + // Need to save argument registers x0-x7 and the return buffer register x8 (twice for 16B alignment) + stp x0, x1, [sp, #0x10] + stp x2, x3, [sp, #0x20] + stp x4, x5, [sp, #0x30] + stp x6, x7, [sp, #0x40] + stp x8, x8, [sp, #0x50] + + // Save float argument registers as well since they are volatile + stp d0, d1, [sp, #0x60] + stp d2, d3, [sp, #0x70] + stp d4, d5, [sp, #0x80] + stp d6, d7, [sp, #0x90] + + mov x0, x9 // passing reverse pinvoke frame pointer in x0 + bl RhpReversePInvokeAttachOrTrapThread2 + + // Restore floating point registers + ldp d0, d1, [sp, #0x60] + ldp d2, d3, [sp, #0x70] + ldp d4, d5, [sp, #0x80] + ldp d6, d7, [sp, #0x90] + + // Restore the argument registers + ldp x0, x1, [sp, #0x10] + ldp x2, x3, [sp, #0x20] + ldp x4, x5, [sp, #0x30] + ldp x6, x7, [sp, #0x40] + ldr x8, [sp, #0x50] + + // Restore FP and LR registers, and free the allocated stack block + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 0xA0 + EPILOG_RETURN + + NESTED_END RhpReversePInvokeTrapThread + +// +// RhpPInvoke +// +// IN: X0: address of pinvoke frame +// +// This helper assumes that its callsite is as good to start the stackwalk as the actual PInvoke callsite. +// The codegenerator must treat the callsite of this helper as GC triggering and generate the GC info for it. +// Also, the codegenerator must ensure that there are no live GC references in callee saved registers. +// + +NESTED_ENTRY RhpPInvoke, _TEXT, NoHandler + str fp, [x0, #OFFSETOF__PInvokeTransitionFrame__m_FramePointer] + str lr, [x0, #OFFSETOF__PInvokeTransitionFrame__m_RIP] + mov x9, SP + str x9, [x0, #OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs] + mov x9, #PTFF_SAVE_SP + str x9, [x0, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + + // get TLS global variable address + +#ifdef FEATURE_EMULATED_TLS + GETTHREAD_ETLS_1 +#else + INLINE_GETTHREAD x1 +#endif + + str x1, [x0, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + str x0, [x1, #OFFSETOF__Thread__m_pTransitionFrame] + + PREPARE_EXTERNAL_VAR_INDIRECT_W RhpTrapThreads, 9 + + cbnz w9, InvokeRareTrapThread // TrapThreadsFlags_None = 0 + ret + +InvokeRareTrapThread: + b C_FUNC(RhpWaitForSuspend2) +NESTED_END RhpPInvoke, _TEXT + + +LEAF_ENTRY RhpPInvokeReturn, _TEXT + ldr x9, [x0, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + mov x10, 0 + str x10, [x9, #OFFSETOF__Thread__m_pTransitionFrame] + + PREPARE_EXTERNAL_VAR_INDIRECT_W RhpTrapThreads, 9 + + cbnz w9, 0f // TrapThreadsFlags_None = 0 + ret +0: + // passing transition frame pointer in x0 + b C_FUNC(RhpWaitForGC2) +LEAF_END RhpPInvokeReturn, _TEXT + diff --git a/src/coreclr/nativeaot/Runtime/arm64/PInvoke.asm b/src/coreclr/nativeaot/Runtime/arm64/PInvoke.asm new file mode 100644 index 00000000000000..d8ee2bb056d872 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/PInvoke.asm @@ -0,0 +1,250 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + + IMPORT RhpReversePInvokeBadTransition + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForSuspend -- rare path for RhpPInvoke and RhpReversePInvokeReturn +;; +;; +;; INPUT: none +;; +;; TRASHES: none +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpWaitForSuspend + + ;; FP and LR registers + PROLOG_SAVE_REG_PAIR fp, lr, #-0xA0! ;; Push down stack pointer and store FP and LR + + ;; Need to save argument registers x0-x7 and the return buffer register x8 + ;; Also save x9 which may be used for saving indirect call target + stp x0, x1, [sp, #0x10] + stp x2, x3, [sp, #0x20] + stp x4, x5, [sp, #0x30] + stp x6, x7, [sp, #0x40] + stp x8, x9, [sp, #0x50] + + ;; Save float argument registers as well since they're volatile + stp d0, d1, [sp, #0x60] + stp d2, d3, [sp, #0x70] + stp d4, d5, [sp, #0x80] + stp d6, d7, [sp, #0x90] + + bl RhpWaitForSuspend2 + + ;; Restore floating point registers + ldp d0, d1, [sp, #0x60] + ldp d2, d3, [sp, #0x70] + ldp d4, d5, [sp, #0x80] + ldp d6, d7, [sp, #0x90] + + ;; Restore the argument registers + ldp x0, x1, [sp, #0x10] + ldp x2, x3, [sp, #0x20] + ldp x4, x5, [sp, #0x30] + ldp x6, x7, [sp, #0x40] + ldp x8, x9, [sp, #0x50] + + ;; Restore FP and LR registers, and free the allocated stack block + EPILOG_RESTORE_REG_PAIR fp, lr, #0xA0! + EPILOG_RETURN + + NESTED_END RhpWaitForSuspend + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGCNoAbort +;; +;; +;; INPUT: x9: transition frame +;; +;; TRASHES: None +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpWaitForGCNoAbort + + ;; FP and LR registers + PROLOG_SAVE_REG_PAIR fp, lr, #-0x40! ;; Push down stack pointer and store FP and LR + + ;; Save the integer return registers, as well as the floating return registers + stp x0, x1, [sp, #0x10] + stp d0, d1, [sp, #0x20] + stp d2, d3, [sp, #0x30] + + ldr x0, [x9, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + ldr w0, [x0, #OFFSETOF__Thread__m_ThreadStateFlags] + tbnz x0, #TSF_DoNotTriggerGc_Bit, Done + + mov x0, x9 ; passing transition frame in x0 + bl RhpWaitForGC2 + +Done + ldp x0, x1, [sp, #0x10] + ldp d0, d1, [sp, #0x20] + ldp d2, d3, [sp, #0x30] + EPILOG_RESTORE_REG_PAIR fp, lr, #0x40! + EPILOG_RETURN + + NESTED_END RhpWaitForGCNoAbort + + EXTERN RhpThrowHwEx + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGC +;; +;; +;; INPUT: x9: transition frame +;; +;; TRASHES: x0, x1, x10 +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpWaitForGC + + PROLOG_SAVE_REG_PAIR fp, lr, #-0x10! + + ldr x10, =RhpTrapThreads + ldr w10, [x10] + tbz x10, #TrapThreadsFlags_TrapThreads_Bit, NoWait + bl RhpWaitForGCNoAbort +NoWait + tbz x10, #TrapThreadsFlags_AbortInProgress_Bit, NoAbort + ldr x10, [x9, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + tbz x10, #PTFF_THREAD_ABORT_BIT, NoAbort + + EPILOG_RESTORE_REG_PAIR fp, lr, #0x10! + EPILOG_NOP mov w0, #STATUS_REDHAWK_THREAD_ABORT + EPILOG_NOP mov x1, lr ; hijack target address as exception PC + EPILOG_NOP b RhpThrowHwEx + +NoAbort + EPILOG_RESTORE_REG_PAIR fp, lr, #0x10! + EPILOG_RETURN + + NESTED_END RhpWaitForGC + + INLINE_GETTHREAD_CONSTANT_POOL + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpReversePInvokeAttachOrTrapThread -- rare path for RhpPInvoke +;; +;; +;; INPUT: x9: address of reverse pinvoke frame +;; +;; PRESERVES: x0-x8 -- need to preserve these because the caller assumes they aren't trashed +;; +;; TRASHES: none +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpReversePInvokeAttachOrTrapThread + + ;; FP and LR registers + PROLOG_SAVE_REG_PAIR fp, lr, #-0xA0! ;; Push down stack pointer and store FP and LR + + ;; Need to save argument registers x0-x7 and the return buffer register x8 (twice for 16B alignment) + stp x0, x1, [sp, #0x10] + stp x2, x3, [sp, #0x20] + stp x4, x5, [sp, #0x30] + stp x6, x7, [sp, #0x40] + stp x8, x8, [sp, #0x50] + + ;; Save float argument registers as well since they're volatile + stp d0, d1, [sp, #0x60] + stp d2, d3, [sp, #0x70] + stp d4, d5, [sp, #0x80] + stp d6, d7, [sp, #0x90] + + mov x0, x9 ; passing reverse pinvoke frame pointer in x0 + bl RhpReversePInvokeAttachOrTrapThread2 + + ;; Restore floating point registers + ldp d0, d1, [sp, #0x60] + ldp d2, d3, [sp, #0x70] + ldp d4, d5, [sp, #0x80] + ldp d6, d7, [sp, #0x90] + + ;; Restore the argument registers + ldp x0, x1, [sp, #0x10] + ldp x2, x3, [sp, #0x20] + ldp x4, x5, [sp, #0x30] + ldp x6, x7, [sp, #0x40] + ldr x8, [sp, #0x50] + + ;; Restore FP and LR registers, and free the allocated stack block + EPILOG_RESTORE_REG_PAIR fp, lr, #0xA0! + EPILOG_RETURN + + NESTED_END RhpReversePInvokeTrapThread + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpPInvoke +;; +;; IN: x0: address of pinvoke frame +;; +;; TRASHES: x9 +;; +;; This helper assumes that its callsite is as good to start the stackwalk as the actual PInvoke callsite. +;; The codegenerator must treat the callsite of this helper as GC triggering and generate the GC info for it. +;; Also, the codegenerator must ensure that there are no live GC references in callee saved registers. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpPInvoke, _TEXT + + str fp, [x0, #OFFSETOF__PInvokeTransitionFrame__m_FramePointer] + str lr, [x0, #OFFSETOF__PInvokeTransitionFrame__m_RIP] + mov x9, sp + str x9, [x0, #OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs] + mov x9, #PTFF_SAVE_SP + str x9, [x0, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + + INLINE_GETTHREAD x1, x9 + + str x1, [x0, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + str x0, [x1, #OFFSETOF__Thread__m_pTransitionFrame] + + ldr x9, =RhpTrapThreads + ldr w9, [x9] + cbnz w9, InvokeRareTrapThread ;; TrapThreadsFlags_None = 0 + ret + +InvokeRareTrapThread + b RhpWaitForSuspend2 + NESTED_END RhpPInvoke + + INLINE_GETTHREAD_CONSTANT_POOL + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpPInvokeReturn +;; +;; IN: x0: address of pinvoke frame +;; +;; TRASHES: x9, x10 +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + LEAF_ENTRY RhpPInvokeReturn, _TEXT + ldr x9, [x0, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + mov x10, 0 + str x10, [x9, #OFFSETOF__Thread__m_pTransitionFrame] + + ldr x9, =RhpTrapThreads + ldr w9, [x9] + cbnz w9, %ft0 ;; TrapThreadsFlags_None = 0 + ret +0 + ;; passing transition frame pointer in x0 + b RhpWaitForGC2 + LEAF_END RhpPInvokeReturn + + end diff --git a/src/coreclr/nativeaot/Runtime/arm64/StubDispatch.S b/src/coreclr/nativeaot/Runtime/arm64/StubDispatch.S new file mode 100644 index 00000000000000..6073d32bb53c57 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/StubDispatch.S @@ -0,0 +1,119 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include "AsmOffsets.inc" + +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH + + .extern RhpCidResolve + .extern RhpUniversalTransition_DebugStepTailCall + + // Macro that generates code to check a single cache entry. + .macro CHECK_CACHE_ENTRY entry + // Check a single entry in the cache. + // x9 : Cache data structure. Also used for target address jump. + // x10 : Instance MethodTable* + // x11 : Indirection cell address, preserved + // x12 : Trashed + ldr x12, [x9, #(OFFSETOF__InterfaceDispatchCache__m_rgEntries + (\entry * 16))] + cmp x10, x12 + bne 0f + ldr x9, [x9, #(OFFSETOF__InterfaceDispatchCache__m_rgEntries + (\entry * 16) + 8)] + br x9 +0: + .endm + +// +// Macro that generates a stub consuming a cache with the given number of entries. +// + .macro DEFINE_INTERFACE_DISPATCH_STUB entries + + NESTED_ENTRY "RhpInterfaceDispatch\entries", _TEXT, NoHandler + + // x11 holds the indirection cell address. Load the cache pointer. + ldr x9, [x11, #OFFSETOF__InterfaceDispatchCell__m_pCache] + + // Load the MethodTable from the object instance in x0. + ALTERNATE_ENTRY RhpInterfaceDispatchAVLocation\entries + ldr x10, [x0] + + .global CurrentEntry + .set CurrentEntry, 0 + + .rept \entries + CHECK_CACHE_ENTRY CurrentEntry + .set CurrentEntry, CurrentEntry + 1 + .endr + + // x11 still contains the indirection cell address. + b RhpInterfaceDispatchSlow + + NESTED_END "RhpInterfaceDispatch\entries", _TEXT + + .endm + +// +// Define all the stub routines we currently need. +// +// If you change or add any new dispatch stubs, exception handling might need to be aware because it refers to the +// *AVLocation symbols defined by the dispatch stubs to be able to unwind and blame user code if a NullRef happens +// during the interface dispatch. +// + DEFINE_INTERFACE_DISPATCH_STUB 1 + DEFINE_INTERFACE_DISPATCH_STUB 2 + DEFINE_INTERFACE_DISPATCH_STUB 4 + DEFINE_INTERFACE_DISPATCH_STUB 8 + DEFINE_INTERFACE_DISPATCH_STUB 16 + DEFINE_INTERFACE_DISPATCH_STUB 32 + DEFINE_INTERFACE_DISPATCH_STUB 64 + +// +// Initial dispatch on an interface when we don't have a cache yet. +// + LEAF_ENTRY RhpInitialInterfaceDispatch, _TEXT + ALTERNATE_ENTRY RhpInitialDynamicInterfaceDispatch + // Trigger an AV if we're dispatching on a null this. + // The exception handling infrastructure is aware of the fact that this is the first + // instruction of RhpInitialInterfaceDispatch and uses it to translate an AV here + // to a NullReferenceException at the callsite. + ldr xzr, [x0] + + // Just tail call to the cache miss helper. + b RhpInterfaceDispatchSlow + LEAF_END RhpInitialInterfaceDispatch, _TEXT + +// +// Stub dispatch routine for dispatch to a vtable slot +// + LEAF_ENTRY RhpVTableOffsetDispatch, _TEXT + // x11 contains the interface dispatch cell address. + // load x12 to point to the vtable offset (which is stored in the m_pCache field). + ldr x12, [x11, #OFFSETOF__InterfaceDispatchCell__m_pCache] + + // Load the MethodTable from the object instance in x0, and add it to the vtable offset + // to get the address in the vtable of what we want to dereference + ldr x13, [x0] + add x12, x12, x13 + + // Load the target address of the vtable into x12 + ldr x12, [x12] + + br x12 + LEAF_END RhpVTableOffsetDispatch, _TEXT + +// +// Cache miss case, call the runtime to resolve the target and update the cache. +// Use universal transition helper to allow an exception to flow out of resolution. +// + LEAF_ENTRY RhpInterfaceDispatchSlow, _TEXT + // x11 contains the interface dispatch cell address. + // Calling convention of the universal thunk is: + // xip0: target address for the thunk to call + // xip1: parameter of the thunk's target + PREPARE_EXTERNAL_VAR RhpCidResolve, xip0 + mov xip1, x11 + b RhpUniversalTransition_DebugStepTailCall + LEAF_END RhpInterfaceDispatchSlow, _TEXT + +#endif // FEATURE_CACHED_INTERFACE_DISPATCH diff --git a/src/coreclr/nativeaot/Runtime/arm64/StubDispatch.asm b/src/coreclr/nativeaot/Runtime/arm64/StubDispatch.asm new file mode 100644 index 00000000000000..93e6038f1047a0 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/StubDispatch.asm @@ -0,0 +1,126 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + + TEXTAREA + +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH + + EXTERN RhpCidResolve + EXTERN RhpUniversalTransition_DebugStepTailCall + + ;; Macro that generates code to check a single cache entry. + MACRO + CHECK_CACHE_ENTRY $entry + ;; Check a single entry in the cache. + ;; x9 : Cache data structure. Also used for target address jump. + ;; x10 : Instance MethodTable* + ;; x11 : Indirection cell address, preserved + ;; x12 : Trashed + ldr x12, [x9, #(OFFSETOF__InterfaceDispatchCache__m_rgEntries + ($entry * 16))] + cmp x10, x12 + bne %ft0 + ldr x9, [x9, #(OFFSETOF__InterfaceDispatchCache__m_rgEntries + ($entry * 16) + 8)] + br x9 +0 + MEND + + +;; +;; Macro that generates a stub consuming a cache with the given number of entries. +;; + MACRO + DEFINE_INTERFACE_DISPATCH_STUB $entries + + NESTED_ENTRY RhpInterfaceDispatch$entries + + ;; x11 holds the indirection cell address. Load the cache pointer. + ldr x9, [x11, #OFFSETOF__InterfaceDispatchCell__m_pCache] + + ;; Load the MethodTable from the object instance in x0. + ALTERNATE_ENTRY RhpInterfaceDispatchAVLocation$entries + ldr x10, [x0] + + GBLA CurrentEntry +CurrentEntry SETA 0 + + WHILE CurrentEntry < $entries + CHECK_CACHE_ENTRY CurrentEntry +CurrentEntry SETA CurrentEntry + 1 + WEND + + ;; x11 still contains the indirection cell address. + b RhpInterfaceDispatchSlow + + NESTED_END RhpInterfaceDispatch$entries + + MEND + +;; +;; Define all the stub routines we currently need. +;; +;; If you change or add any new dispatch stubs, exception handling might need to be aware because it refers to the +;; *AVLocation symbols defined by the dispatch stubs to be able to unwind and blame user code if a NullRef happens +;; during the interface dispatch. +;; + DEFINE_INTERFACE_DISPATCH_STUB 1 + DEFINE_INTERFACE_DISPATCH_STUB 2 + DEFINE_INTERFACE_DISPATCH_STUB 4 + DEFINE_INTERFACE_DISPATCH_STUB 8 + DEFINE_INTERFACE_DISPATCH_STUB 16 + DEFINE_INTERFACE_DISPATCH_STUB 32 + DEFINE_INTERFACE_DISPATCH_STUB 64 + + +;; +;; Initial dispatch on an interface when we don't have a cache yet. +;; + LEAF_ENTRY RhpInitialInterfaceDispatch + ALTERNATE_ENTRY RhpInitialDynamicInterfaceDispatch + ;; Trigger an AV if we're dispatching on a null this. + ;; The exception handling infrastructure is aware of the fact that this is the first + ;; instruction of RhpInitialInterfaceDispatch and uses it to translate an AV here + ;; to a NullReferenceException at the callsite. + ldr xzr, [x0] + + ;; Just tail call to the cache miss helper. + b RhpInterfaceDispatchSlow + LEAF_END RhpInitialInterfaceDispatch + +;; +;; Stub dispatch routine for dispatch to a vtable slot +;; + LEAF_ENTRY RhpVTableOffsetDispatch + ;; x11 contains the interface dispatch cell address. + ;; load x12 to point to the vtable offset (which is stored in the m_pCache field). + ldr x12, [x11, #OFFSETOF__InterfaceDispatchCell__m_pCache] + + ;; Load the MethodTable from the object instance in x0, and add it to the vtable offset + ;; to get the address in the vtable of what we want to dereference + ldr x13, [x0] + add x12, x12, x13 + + ;; Load the target address of the vtable into x12 + ldr x12, [x12] + + br x12 + LEAF_END RhpVTableOffsetDispatch + +;; +;; Cache miss case, call the runtime to resolve the target and update the cache. +;; Use universal transition helper to allow an exception to flow out of resolution. +;; + LEAF_ENTRY RhpInterfaceDispatchSlow + ;; x11 contains the interface dispatch cell address. + ;; Calling convention of the universal thunk is: + ;; xip0: target address for the thunk to call + ;; xip1: parameter of the thunk's target + ldr xip0, =RhpCidResolve + mov xip1, x11 + b RhpUniversalTransition_DebugStepTailCall + LEAF_END RhpInterfaceDispatchSlow + +#endif // FEATURE_CACHED_INTERFACE_DISPATCH + + END diff --git a/src/coreclr/nativeaot/Runtime/arm64/ThunkPoolThunks.asm b/src/coreclr/nativeaot/Runtime/arm64/ThunkPoolThunks.asm new file mode 100644 index 00000000000000..9ef87b02cb87fe --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/ThunkPoolThunks.asm @@ -0,0 +1,334 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "ksarm64.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; STUBS & DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +THUNK_CODESIZE equ 0x10 ;; 3 instructions, 4 bytes each (and we also have 4 bytes of padding) +THUNK_DATASIZE equ 0x10 ;; 2 qwords + +THUNK_POOL_NUM_THUNKS_PER_PAGE equ 0xFA ;; 250 thunks per page + +POINTER_SIZE equ 0x08 + + MACRO + NAMED_READONLY_DATA_SECTION $name, $areaAlias + AREA $areaAlias,DATA,READONLY +RO$name % 8 + MEND + + ;; This macro is used to declare the thunks data blocks. Unlike the macro above (which is just used for padding), + ;; this macro needs to assign labels to each data block, so we can address them using PC-relative addresses. + MACRO + NAMED_READWRITE_DATA_SECTION $name, $areaAlias, $pageIndex + AREA $areaAlias,DATA + THUNKS_DATA_PAGE_BLOCK $pageIndex + MEND + + MACRO + LOAD_DATA_ADDRESS $groupIndex, $index, $pageIndex + + ;; Set xip0 to the address of the current thunk's data block. This is done using labels. + adr xip0, label_$groupIndex_$index_P$pageIndex + MEND + + MACRO + JUMP_TO_COMMON $groupIndex, $index + ;; start : xip0 points to the current thunks first data cell in the data page + ;; set xip0 to begining of data page : xip0 <- xip0 - (THUNK_DATASIZE * current thunk's index) + ;; fix offset to point to last QWROD in page : xip1 <- [xip0 + PAGE_SIZE - POINTER_SIZE] + ;; tailcall to the location pointed at by the last qword in the data page + ldr xip1, [xip0, #(PAGE_SIZE - POINTER_SIZE - ($groupIndex * THUNK_DATASIZE * 10 + THUNK_DATASIZE * $index))] + br xip1 + + brk 0xf000 ;; Stubs need to be 16-byte aligned for CFG table. Filling padding with a + ;; deterministic brk instruction, instead of having it just filled with zeros. + MEND + + MACRO + THUNK_LABELED_DATA_BLOCK $groupIndex, $index, $pageIndex + + ;; Each data block contains 2 qword cells. The data block is also labeled so it can be addressed + ;; using PC relative instructions +label_$groupIndex_$index_P$pageIndex + DCQ 0 + DCQ 0 + MEND + + MACRO + TenThunks $groupIndex, $pageIndex + + ;; Each thunk will load the address of its corresponding data (from the page that immediately follows) + ;; and call a common stub. The address of the common stub is setup by the caller (last qword + ;; in the thunks data section) depending on the 'kind' of thunks needed (interop, fat function pointers, etc...) + + ;; Each data block used by a thunk consists of two qword values: + ;; - Context: some value given to the thunk as context. Example for fat-fptrs: context = generic dictionary + ;; - Target : target code that the thunk eventually jumps to. + + LOAD_DATA_ADDRESS $groupIndex,0,$pageIndex + JUMP_TO_COMMON $groupIndex,0 + + LOAD_DATA_ADDRESS $groupIndex,1,$pageIndex + JUMP_TO_COMMON $groupIndex,1 + + LOAD_DATA_ADDRESS $groupIndex,2,$pageIndex + JUMP_TO_COMMON $groupIndex,2 + + LOAD_DATA_ADDRESS $groupIndex,3,$pageIndex + JUMP_TO_COMMON $groupIndex,3 + + LOAD_DATA_ADDRESS $groupIndex,4,$pageIndex + JUMP_TO_COMMON $groupIndex,4 + + LOAD_DATA_ADDRESS $groupIndex,5,$pageIndex + JUMP_TO_COMMON $groupIndex,5 + + LOAD_DATA_ADDRESS $groupIndex,6,$pageIndex + JUMP_TO_COMMON $groupIndex,6 + + LOAD_DATA_ADDRESS $groupIndex,7,$pageIndex + JUMP_TO_COMMON $groupIndex,7 + + LOAD_DATA_ADDRESS $groupIndex,8,$pageIndex + JUMP_TO_COMMON $groupIndex,8 + + LOAD_DATA_ADDRESS $groupIndex,9,$pageIndex + JUMP_TO_COMMON $groupIndex,9 + MEND + + MACRO + TenThunkDataBlocks $groupIndex, $pageIndex + + ;; Similar to the thunks stubs block, we declare the thunks data blocks here + + THUNK_LABELED_DATA_BLOCK $groupIndex, 0, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 1, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 2, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 3, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 4, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 5, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 6, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 7, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 8, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 9, $pageIndex + MEND + + MACRO + THUNKS_PAGE_BLOCK $pageIndex + + TenThunks 0, $pageIndex + TenThunks 1, $pageIndex + TenThunks 2, $pageIndex + TenThunks 3, $pageIndex + TenThunks 4, $pageIndex + TenThunks 5, $pageIndex + TenThunks 6, $pageIndex + TenThunks 7, $pageIndex + TenThunks 8, $pageIndex + TenThunks 9, $pageIndex + TenThunks 10, $pageIndex + TenThunks 11, $pageIndex + TenThunks 12, $pageIndex + TenThunks 13, $pageIndex + TenThunks 14, $pageIndex + TenThunks 15, $pageIndex + TenThunks 16, $pageIndex + TenThunks 17, $pageIndex + TenThunks 18, $pageIndex + TenThunks 19, $pageIndex + TenThunks 20, $pageIndex + TenThunks 21, $pageIndex + TenThunks 22, $pageIndex + TenThunks 23, $pageIndex + TenThunks 24, $pageIndex + MEND + + MACRO + THUNKS_DATA_PAGE_BLOCK $pageIndex + + TenThunkDataBlocks 0, $pageIndex + TenThunkDataBlocks 1, $pageIndex + TenThunkDataBlocks 2, $pageIndex + TenThunkDataBlocks 3, $pageIndex + TenThunkDataBlocks 4, $pageIndex + TenThunkDataBlocks 5, $pageIndex + TenThunkDataBlocks 6, $pageIndex + TenThunkDataBlocks 7, $pageIndex + TenThunkDataBlocks 8, $pageIndex + TenThunkDataBlocks 9, $pageIndex + TenThunkDataBlocks 10, $pageIndex + TenThunkDataBlocks 11, $pageIndex + TenThunkDataBlocks 12, $pageIndex + TenThunkDataBlocks 13, $pageIndex + TenThunkDataBlocks 14, $pageIndex + TenThunkDataBlocks 15, $pageIndex + TenThunkDataBlocks 16, $pageIndex + TenThunkDataBlocks 17, $pageIndex + TenThunkDataBlocks 18, $pageIndex + TenThunkDataBlocks 19, $pageIndex + TenThunkDataBlocks 20, $pageIndex + TenThunkDataBlocks 21, $pageIndex + TenThunkDataBlocks 22, $pageIndex + TenThunkDataBlocks 23, $pageIndex + TenThunkDataBlocks 24, $pageIndex + MEND + + + ;; + ;; The first thunks section should be 64K aligned because it can get + ;; mapped multiple times in memory, and mapping works on allocation + ;; granularity boundaries (we don't want to map more than what we need) + ;; + ;; The easiest way to do so is by having the thunks section at the + ;; first 64K aligned virtual address in the binary. We provide a section + ;; layout file to the linker to tell it how to layout the thunks sections + ;; that we care about. (ndp\rh\src\runtime\DLLs\app\mrt100_app_sectionlayout.txt) + ;; + ;; The PE spec says images cannot have gaps between sections (other + ;; than what is required by the section alignment value in the header), + ;; therefore we need a couple of padding data sections (otherwise the + ;; OS will not load the image). + ;; + + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment0, "|.pad0|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment1, "|.pad1|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment2, "|.pad2|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment3, "|.pad3|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment4, "|.pad4|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment5, "|.pad5|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment6, "|.pad6|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment7, "|.pad7|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment8, "|.pad8|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment9, "|.pad9|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment10, "|.pad10|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment11, "|.pad11|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment12, "|.pad12|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment13, "|.pad13|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment14, "|.pad14|" + + ;; + ;; Declaring all the data section first since they have labels referenced by the stubs sections, to prevent + ;; compilation errors ("undefined symbols"). The stubs/data sections will be correctly laid out in the image + ;; using using the explicit layout configurations (ndp\rh\src\runtime\DLLs\mrt100_sectionlayout.txt) + ;; + NAMED_READWRITE_DATA_SECTION ThunkData0, "|.tkd0|", 0 + NAMED_READWRITE_DATA_SECTION ThunkData1, "|.tkd1|", 1 + NAMED_READWRITE_DATA_SECTION ThunkData2, "|.tkd2|", 2 + NAMED_READWRITE_DATA_SECTION ThunkData3, "|.tkd3|", 3 + NAMED_READWRITE_DATA_SECTION ThunkData4, "|.tkd4|", 4 + NAMED_READWRITE_DATA_SECTION ThunkData5, "|.tkd5|", 5 + NAMED_READWRITE_DATA_SECTION ThunkData6, "|.tkd6|", 6 + NAMED_READWRITE_DATA_SECTION ThunkData7, "|.tkd7|", 7 + + ;; + ;; Thunk Stubs + ;; NOTE: Keep number of blocks in sync with macro/constant named 'NUM_THUNK_BLOCKS' in: + ;; - ndp\FxCore\src\System.Private.CoreLib\System\Runtime\InteropServices\ThunkPool.cs + ;; - ndp\rh\src\tools\rhbind\zapimage.h + ;; + + LEAF_ENTRY ThunkPool, "|.tks0|" + THUNKS_PAGE_BLOCK 0 + LEAF_END ThunkPool + + LEAF_ENTRY ThunkPool1, "|.tks1|" + THUNKS_PAGE_BLOCK 1 + LEAF_END ThunkPool1 + + LEAF_ENTRY ThunkPool2, "|.tks2|" + THUNKS_PAGE_BLOCK 2 + LEAF_END ThunkPool2 + + LEAF_ENTRY ThunkPool3, "|.tks3|" + THUNKS_PAGE_BLOCK 3 + LEAF_END ThunkPool3 + + LEAF_ENTRY ThunkPool4, "|.tks4|" + THUNKS_PAGE_BLOCK 4 + LEAF_END ThunkPool4 + + LEAF_ENTRY ThunkPool5, "|.tks5|" + THUNKS_PAGE_BLOCK 5 + LEAF_END ThunkPool5 + + LEAF_ENTRY ThunkPool6, "|.tks6|" + THUNKS_PAGE_BLOCK 6 + LEAF_END ThunkPool6 + + LEAF_ENTRY ThunkPool7, "|.tks7|" + THUNKS_PAGE_BLOCK 7 + LEAF_END ThunkPool7 + + + ;; + ;; IntPtr RhpGetThunksBase() + ;; + ;; ARM64TODO: There is a bug in the arm64 assembler which ends up with mis-sorted Pdata entries + ;; for the functions in this file. As a work around, don't generate pdata for these small stubs. + ;; All the "No_PDATA" variants need to be removed after MASM bug 516396 is fixed. + LEAF_ENTRY_NO_PDATA RhpGetThunksBase + ;; Return the address of the first thunk pool to the caller (this is really the base address) + ldr x0, =ThunkPool + ret + LEAF_END_NO_PDATA RhpGetThunksBase + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; General Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;; + ;; int RhpGetNumThunksPerBlock() + ;; + LEAF_ENTRY_NO_PDATA RhpGetNumThunksPerBlock + mov x0, THUNK_POOL_NUM_THUNKS_PER_PAGE + ret + LEAF_END_NO_PDATA RhpGetNumThunksPerBlock + + ;; + ;; int RhpGetThunkSize() + ;; + LEAF_ENTRY_NO_PDATA RhpGetThunkSize + mov x0, THUNK_CODESIZE + ret + LEAF_END_NO_PDATA RhpGetThunkSize + + ;; + ;; int RhpGetNumThunkBlocksPerMapping() + ;; + LEAF_ENTRY_NO_PDATA RhpGetNumThunkBlocksPerMapping + mov x0, 8 + ret + LEAF_END_NO_PDATA RhpGetNumThunkBlocksPerMapping + + ;; + ;; int RhpGetThunkBlockSize + ;; + LEAF_ENTRY_NO_PDATA RhpGetThunkBlockSize + mov x0, PAGE_SIZE * 2 + ret + LEAF_END_NO_PDATA RhpGetThunkBlockSize + + ;; + ;; IntPtr RhpGetThunkDataBlockAddress(IntPtr thunkStubAddress) + ;; + LEAF_ENTRY_NO_PDATA RhpGetThunkDataBlockAddress + mov x12, PAGE_SIZE - 1 + bic x0, x0, x12 + mov x12, PAGE_SIZE + add x0, x0, x12 + ret + LEAF_END_NO_PDATA RhpGetThunkDataBlockAddress + + ;; + ;; IntPtr RhpGetThunkStubsBlockAddress(IntPtr thunkDataAddress) + ;; + LEAF_ENTRY_NO_PDATA RhpGetThunkStubsBlockAddress + mov x12, PAGE_SIZE - 1 + bic x0, x0, x12 + mov x12, PAGE_SIZE + sub x0, x0, x12 + ret + LEAF_END_NO_PDATA RhpGetThunkStubsBlockAddress + + END diff --git a/src/coreclr/nativeaot/Runtime/arm64/UniversalTransition.S b/src/coreclr/nativeaot/Runtime/arm64/UniversalTransition.S new file mode 100644 index 00000000000000..12fa42365f4c2e --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/UniversalTransition.S @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +#ifdef _DEBUG +#define TRASH_SAVED_ARGUMENT_REGISTERS +#endif + +#ifdef TRASH_SAVED_ARGUMENT_REGISTERS + .global RhpIntegerTrashValues + .global RhpFpTrashValues +#endif // TRASH_SAVED_ARGUMENT_REGISTERS + +// Padding to account for the odd number of saved integer registers +#define ALIGNMENT_PADDING_SIZE (8) + +#define COUNT_ARG_REGISTERS (9) +#define INTEGER_REGISTER_SIZE (8) +#define ARGUMENT_REGISTERS_SIZE (COUNT_ARG_REGISTERS * INTEGER_REGISTER_SIZE) + +// Largest return block is 4 doubles +#define RETURN_BLOCK_SIZE (32) + +#define COUNT_FLOAT_ARG_REGISTERS (8) +#define FLOAT_REGISTER_SIZE (8) +#define FLOAT_ARG_REGISTERS_SIZE (COUNT_FLOAT_ARG_REGISTERS * FLOAT_REGISTER_SIZE) + +#define PUSHED_LR_SIZE (8) +#define PUSHED_FP_SIZE (8) + +// +// From CallerSP to ChildSP, the stack frame is composed of the following adjacent regions: +// +// ALIGNMENT_PADDING_SIZE +// ARGUMENT_REGISTERS_SIZE +// RETURN_BLOCK_SIZE +// FLOAT_ARG_REGISTERS_SIZE +// PUSHED_LR_SIZE +// PUSHED_FP_SIZE +// + +#define DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK (PUSHED_FP_SIZE + PUSHED_LR_SIZE + FLOAT_ARG_REGISTERS_SIZE) + +#define STACK_SIZE (ALIGNMENT_PADDING_SIZE + ARGUMENT_REGISTERS_SIZE + RETURN_BLOCK_SIZE + FLOAT_ARG_REGISTERS_SIZE + PUSHED_LR_SIZE + PUSHED_FP_SIZE) + +#define FLOAT_ARG_OFFSET (PUSHED_FP_SIZE + PUSHED_LR_SIZE) +#define ARGUMENT_REGISTERS_OFFSET (FLOAT_ARG_OFFSET + FLOAT_ARG_REGISTERS_SIZE + RETURN_BLOCK_SIZE) + +// +// RhpUniversalTransition +// +// At input to this function, x0-8, d0-7 and the stack may contain any number of arguments. +// +// In addition, there are 2 extra arguments passed in the intra-procedure-call scratch register: +// xip0 will contain the managed function that is to be called by this transition function +// xip1 will contain the pointer sized extra argument to the managed function +// +// When invoking the callee: +// +// x0 shall contain a pointer to the TransitionBlock +// x1 shall contain the value that was in xip1 at entry to this function +// +// Frame layout is: +// +// {StackPassedArgs} ChildSP+0C0 CallerSP+000 +// {AlignmentPad (0x8 bytes)} ChildSP+0B8 CallerSP-008 +// {IntArgRegs (x0-x8) (0x48 bytes)} ChildSP+070 CallerSP-050 +// {ReturnBlock (0x20 bytes)} ChildSP+050 CallerSP-070 +// -- The base address of the Return block is the TransitionBlock pointer, the floating point args are +// in the neg space of the TransitionBlock pointer. Note that the callee has knowledge of the exact +// layout of all pieces of the frame that lie at or above the pushed floating point registers. +// {FpArgRegs (d0-d7) (0x40 bytes)} ChildSP+010 CallerSP-0B0 +// {PushedLR} ChildSP+008 CallerSP-0B8 +// {PushedFP} ChildSP+000 CallerSP-0C0 +// +// NOTE: If the frame layout ever changes, the C++ UniversalTransitionStackFrame structure +// must be updated as well. +// +// NOTE: The callee receives a pointer to the base of the ReturnBlock, and the callee has +// knowledge of the exact layout of all pieces of the frame that lie at or above the pushed +// FpArgRegs. +// +// NOTE: The stack walker guarantees that conservative GC reporting will be applied to +// everything between the base of the ReturnBlock and the top of the StackPassedArgs. +// + + .text + + .macro UNIVERSAL_TRANSITION FunctionName + + NESTED_ENTRY Rhp\FunctionName, _TEXT, NoHandler + + // FP and LR registers + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -STACK_SIZE // ;; Push down stack pointer and store FP and LR + + // Floating point registers + stp d0, d1, [sp, #(FLOAT_ARG_OFFSET )] + stp d2, d3, [sp, #(FLOAT_ARG_OFFSET + 0x10)] + stp d4, d5, [sp, #(FLOAT_ARG_OFFSET + 0x20)] + stp d6, d7, [sp, #(FLOAT_ARG_OFFSET + 0x30)] + + // Space for return buffer data (0x40 bytes) + + // Save argument registers + stp x0, x1, [sp, #(ARGUMENT_REGISTERS_OFFSET )] + stp x2, x3, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x10)] + stp x4, x5, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x20)] + stp x6, x7, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x30)] + stp x8, xzr, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x40)] + +#ifdef TRASH_SAVED_ARGUMENT_REGISTERS + PREPARE_EXTERNAL_VAR RhpFpTrashValues, x1 + + ldp d0,d1, [x1, 0x0] + ldp d2,d3, [x1, 0x10] + ldp d4,d5, [x1, 0x20] + ldp d6,d7, [x1, 0x30] + + PREPARE_EXTERNAL_VAR RhpIntegerTrashValues, x1 + + ldp x2,x3, [x1, 0x10] + ldp x4,x5, [x1, 0x20] + ldp x6,x7, [x1, 0x30] +#endif // TRASH_SAVED_ARGUMENT_REGISTERS + + add x0, sp, #DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK // First parameter to target function is a pointer to the return block + mov x8, x0 // Arm64 calling convention: Address of return block shall be passed in x8 + mov x1, xip1 // Second parameter to target function + blr xip0 + + // We cannot make the label public as that tricks DIA stackwalker into thinking + // it's the beginning of a method. For this reason we export an auxiliary variable + // holding the address instead. + EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom\FunctionName + + // Move the result (the target address) to x12 so it doesn't get overridden when we restore the + // argument registers. + mov x12, x0 + + // Restore floating point registers + ldp d0, d1, [sp, #(FLOAT_ARG_OFFSET )] + ldp d2, d3, [sp, #(FLOAT_ARG_OFFSET + 0x10)] + ldp d4, d5, [sp, #(FLOAT_ARG_OFFSET + 0x20)] + ldp d6, d7, [sp, #(FLOAT_ARG_OFFSET + 0x30)] + + // Restore the argument registers + ldp x0, x1, [sp, #(ARGUMENT_REGISTERS_OFFSET )] + ldp x2, x3, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x10)] + ldp x4, x5, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x20)] + ldp x6, x7, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x30)] + ldr x8, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x40)] + + // Restore FP and LR registers, and free the allocated stack block + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, STACK_SIZE + + // Tailcall to the target address. + br x12 + + NESTED_END Rhp\FunctionName, _TEXT + + .endm + + // To enable proper step-in behavior in the debugger, we need to have two instances + // of the thunk. For the first one, the debugger steps into the call in the function, + // for the other, it steps over it. + UNIVERSAL_TRANSITION UniversalTransition + UNIVERSAL_TRANSITION UniversalTransition_DebugStepTailCall + diff --git a/src/coreclr/nativeaot/Runtime/arm64/UniversalTransition.asm b/src/coreclr/nativeaot/Runtime/arm64/UniversalTransition.asm new file mode 100644 index 00000000000000..6f1fc0953cd985 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/UniversalTransition.asm @@ -0,0 +1,161 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmMacros.h" + +#ifdef _DEBUG +#define TRASH_SAVED_ARGUMENT_REGISTERS +#endif + +#ifdef TRASH_SAVED_ARGUMENT_REGISTERS + EXTERN RhpIntegerTrashValues + EXTERN RhpFpTrashValues +#endif ;; TRASH_SAVED_ARGUMENT_REGISTERS + +;; Padding to account for the odd number of saved integer registers +#define ALIGNMENT_PADDING_SIZE (8) + +#define COUNT_ARG_REGISTERS (9) +#define INTEGER_REGISTER_SIZE (8) +#define ARGUMENT_REGISTERS_SIZE (COUNT_ARG_REGISTERS * INTEGER_REGISTER_SIZE) + +;; Largest return block is 4 doubles +#define RETURN_BLOCK_SIZE (32) + +#define COUNT_FLOAT_ARG_REGISTERS (8) +#define FLOAT_REGISTER_SIZE (8) +#define FLOAT_ARG_REGISTERS_SIZE (COUNT_FLOAT_ARG_REGISTERS * FLOAT_REGISTER_SIZE) + +#define PUSHED_LR_SIZE (8) +#define PUSHED_FP_SIZE (8) + +;; +;; From CallerSP to ChildSP, the stack frame is composed of the following adjacent regions: +;; +;; ALIGNMENT_PADDING_SIZE +;; ARGUMENT_REGISTERS_SIZE +;; RETURN_BLOCK_SIZE +;; FLOAT_ARG_REGISTERS_SIZE +;; PUSHED_LR_SIZE +;; PUSHED_FP_SIZE +;; + +#define DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK (PUSHED_FP_SIZE + PUSHED_LR_SIZE + FLOAT_ARG_REGISTERS_SIZE) + +#define STACK_SIZE (ALIGNMENT_PADDING_SIZE + ARGUMENT_REGISTERS_SIZE + RETURN_BLOCK_SIZE + FLOAT_ARG_REGISTERS_SIZE + \ + PUSHED_LR_SIZE + PUSHED_FP_SIZE) + +#define FLOAT_ARG_OFFSET (PUSHED_FP_SIZE + PUSHED_LR_SIZE) +#define ARGUMENT_REGISTERS_OFFSET (FLOAT_ARG_OFFSET + FLOAT_ARG_REGISTERS_SIZE + RETURN_BLOCK_SIZE) + +;; +;; RhpUniversalTransition +;; +;; At input to this function, x0-8, d0-7 and the stack may contain any number of arguments. +;; +;; In addition, there are 2 extra arguments passed in the intra-procedure-call scratch register: +;; xip0 will contain the managed function that is to be called by this transition function +;; xip1 will contain the pointer sized extra argument to the managed function +;; +;; When invoking the callee: +;; +;; x0 shall contain a pointer to the TransitionBlock +;; x1 shall contain the value that was in xip1 at entry to this function +;; +;; Frame layout is: +;; +;; {StackPassedArgs} ChildSP+0C0 CallerSP+000 +;; {AlignmentPad (0x8 bytes)} ChildSP+0B8 CallerSP-008 +;; {IntArgRegs (x0-x8) (0x48 bytes)} ChildSP+070 CallerSP-050 +;; {ReturnBlock (0x20 bytes)} ChildSP+050 CallerSP-070 +;; -- The base address of the Return block is the TransitionBlock pointer, the floating point args are +;; in the neg space of the TransitionBlock pointer. Note that the callee has knowledge of the exact +;; layout of all pieces of the frame that lie at or above the pushed floating point registers. +;; {FpArgRegs (d0-d7) (0x40 bytes)} ChildSP+010 CallerSP-0B0 +;; {PushedLR} ChildSP+008 CallerSP-0B8 +;; {PushedFP} ChildSP+000 CallerSP-0C0 +;; +;; NOTE: If the frame layout ever changes, the C++ UniversalTransitionStackFrame structure +;; must be updated as well. +;; +;; NOTE: The callee receives a pointer to the base of the ReturnBlock, and the callee has +;; knowledge of the exact layout of all pieces of the frame that lie at or above the pushed +;; FpArgRegs. +;; +;; NOTE: The stack walker guarantees that conservative GC reporting will be applied to +;; everything between the base of the ReturnBlock and the top of the StackPassedArgs. +;; + + TEXTAREA + + MACRO + UNIVERSAL_TRANSITION $FunctionName + + NESTED_ENTRY Rhp$FunctionName + + ;; FP and LR registers + PROLOG_SAVE_REG_PAIR fp, lr, #-STACK_SIZE! ;; Push down stack pointer and store FP and LR + + ;; Floating point registers + stp d0, d1, [sp, #(FLOAT_ARG_OFFSET )] + stp d2, d3, [sp, #(FLOAT_ARG_OFFSET + 0x10)] + stp d4, d5, [sp, #(FLOAT_ARG_OFFSET + 0x20)] + stp d6, d7, [sp, #(FLOAT_ARG_OFFSET + 0x30)] + + ;; Space for return buffer data (0x40 bytes) + + ;; Save argument registers + stp x0, x1, [sp, #(ARGUMENT_REGISTERS_OFFSET )] + stp x2, x3, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x10)] + stp x4, x5, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x20)] + stp x6, x7, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x30)] + stp x8, xzr, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x40)] + +#ifdef TRASH_SAVED_ARGUMENT_REGISTERS + ;; ARM64TODO +#endif // TRASH_SAVED_ARGUMENT_REGISTERS + + add x0, sp, #DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK ;; First parameter to target function is a pointer to the return block + mov x8, x0 ;; Arm64 calling convention: Address of return block shall be passed in x8 + mov x1, xip1 ;; Second parameter to target function + blr xip0 + + ;; We cannot make the label public as that tricks DIA stackwalker into thinking + ;; it's the beginning of a method. For this reason we export an auxiliary variable + ;; holding the address instead. + EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom$FunctionName + + ;; Move the result (the target address) to x12 so it doesn't get overridden when we restore the + ;; argument registers. + mov x12, x0 + + ;; Restore floating point registers + ldp d0, d1, [sp, #(FLOAT_ARG_OFFSET )] + ldp d2, d3, [sp, #(FLOAT_ARG_OFFSET + 0x10)] + ldp d4, d5, [sp, #(FLOAT_ARG_OFFSET + 0x20)] + ldp d6, d7, [sp, #(FLOAT_ARG_OFFSET + 0x30)] + + ;; Restore the argument registers + ldp x0, x1, [sp, #(ARGUMENT_REGISTERS_OFFSET )] + ldp x2, x3, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x10)] + ldp x4, x5, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x20)] + ldp x6, x7, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x30)] + ldr x8, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x40)] + + ;; Restore FP and LR registers, and free the allocated stack block + EPILOG_RESTORE_REG_PAIR fp, lr, #STACK_SIZE! + + ;; Tailcall to the target address. + EPILOG_NOP br x12 + + NESTED_END Rhp$FunctionName + + MEND + + ; To enable proper step-in behavior in the debugger, we need to have two instances + ; of the thunk. For the first one, the debugger steps into the call in the function, + ; for the other, it steps over it. + UNIVERSAL_TRANSITION UniversalTransition + UNIVERSAL_TRANSITION UniversalTransition_DebugStepTailCall + + END diff --git a/src/coreclr/nativeaot/Runtime/arm64/WriteBarriers.S b/src/coreclr/nativeaot/Runtime/arm64/WriteBarriers.S new file mode 100644 index 00000000000000..de8a7aa1a4361c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/WriteBarriers.S @@ -0,0 +1,373 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +// Macro used to copy contents of newly updated GC heap locations to a shadow copy of the heap. This is used +// during garbage collections to verify that object references where never written to the heap without using a +// write barrier. Note that we are potentially racing to update the shadow heap while other threads are writing +// new references to the real heap. Since this can not be solved perfectly without critical sections around the +// entire update process, we instead update the shadow location and then re-check the real location (as two +// ordered operations) and if there is a disparity we will re-write the shadow location with a special value +// (INVALIDGCVALUE) which disables the check for that location. Since the shadow heap is only validated at GC +// time and these write barrier operations are atomic wrt to GCs this is sufficient to guarantee that the +// shadow heap contains only valid copies of real heap values or INVALIDGCVALUE. +#ifdef WRITE_BARRIER_CHECK + + .global $g_GCShadow + .global $g_GCShadowEnd + + // On entry: + // $destReg: location to be updated + // $refReg: objectref to be stored + // + // On exit: + // x9,x10: trashed + // other registers are preserved + // + .macro UPDATE_GC_SHADOW destReg, refReg + + // If g_GCShadow is 0, don't perform the check. + PREPARE_EXTERNAL_VAR_INDIRECT g_GCShadow, X9 + cbz x9, 1f + + // Save destReg since we're about to modify it (and we need the original value both within the macro and + // once we exit the macro). + mov x10, \destReg + + // Transform destReg into the equivalent address in the shadow heap. + PREPARE_EXTERNAL_VAR_INDIRECT g_lowest_address, X9 + subs \destReg, \destReg, x9 + blt 0f + + PREPARE_EXTERNAL_VAR_INDIRECT g_GCShadow, X9 + add \destReg, \destReg, x9 + + PREPARE_EXTERNAL_VAR_INDIRECT g_GCShadowEnd, X9 + cmp \destReg, x9 + bgt 0f + + // Update the shadow heap. + str \refReg, [\destReg] + + // The following read must be strongly ordered wrt to the write we have just performed in order to + // prevent race conditions. + dmb ish + + // Now check that the real heap location still contains the value we just wrote into the shadow heap. + mov x9, x10 + ldr x9, [x9] + cmp x9, \refReg + beq 0f + + // Someone went and updated the real heap. We need to invalidate INVALIDGCVALUE the shadow location since we can not + // guarantee whose shadow update won. + movz x9, (INVALIDGCVALUE & 0xFFFF) // #0xcccd + movk x9, ((INVALIDGCVALUE >> 16) & 0xFFFF), LSL #16 + str x9, [\destReg] + +0: + // Restore original destReg value + mov \destReg, x10 + +1: + .endm + +#else // WRITE_BARRIER_CHECK + + .macro UPDATE_GC_SHADOW destReg, refReg + .endm + +#endif // WRITE_BARRIER_CHECK + +// There are several different helpers used depending on which register holds the object reference. Since all +// the helpers have identical structure we use a macro to define this structure. Two arguments are taken, the +// name of the register that points to the location to be updated and the name of the register that holds the +// object reference (this should be in upper case as it is used in the definition of the name of the helper). + +// Define a sub-macro first that expands to the majority of the barrier implementation. This is used below for +// some interlocked helpers that need an inline barrier. + + // On entry: + // destReg: location to be updated + // refReg: objectref to be stored + // trash: register nr than can be trashed + // trash2: register than can be trashed + // + // On exit: + // destReg: trashed + // + .macro INSERT_UNCHECKED_WRITE_BARRIER_CORE destReg, refReg, trash, trash2 + + // Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + // we are in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW \destReg, \refReg + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + // Update the write watch table if necessary + PREPARE_EXTERNAL_VAR_INDIRECT g_write_watch_table, x\trash + + cbz x\trash, 2f + add x\trash, x\trash, \destReg, lsr #0xc // SoftwareWriteWatch::AddressToTableByteIndexShift + ldrb w17, [x\trash] + cbnz x17, 2f + mov w17, #0xFF + strb w17, [x\trash] +#endif + +2: + // We can skip the card table write if the reference is to + // an object not on the epehemeral segment. + PREPARE_EXTERNAL_VAR_INDIRECT g_ephemeral_low, x\trash + cmp \refReg, x\trash + blt 0f + + PREPARE_EXTERNAL_VAR_INDIRECT g_ephemeral_high, x\trash + cmp \refReg, x\trash + bge 0f + + // Set this objects card, if it has not already been set. + + PREPARE_EXTERNAL_VAR_INDIRECT g_card_table, x\trash + add \trash2, x\trash, \destReg, lsr #11 + + // Check that this card has not already been written. Avoiding useless writes is a big win on + // multi-proc systems since it avoids cache thrashing. + ldrb w\trash, [\trash2] + cmp x\trash, 0xFF + beq 0f + + mov x\trash, 0xFF + strb w\trash, [\trash2] + +#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES + // Check if we need to update the card bundle table + PREPARE_EXTERNAL_VAR_INDIRECT g_card_bundle_table, x\trash + add \trash2, x\trash, \destReg, lsr #21 + ldrb w\trash, [\trash2] + cmp x\trash, 0xFF + beq 0f + + mov x\trash, 0xFF + strb w\trash, [\trash2] +#endif + +0: + // Exit label + .endm + + // On entry: + // destReg: location to be updated + // refReg: objectref to be stored + // trash: register nr than can be trashed + // trash2: register than can be trashed + // + // On exit: + // destReg: trashed + // + .macro INSERT_CHECKED_WRITE_BARRIER_CORE destReg, refReg, trash, trash2 + + // The "check" of this checked write barrier - is destReg + // within the heap? if no, early out. + + PREPARE_EXTERNAL_VAR_INDIRECT g_lowest_address, x\trash + cmp \destReg, x\trash + blt 0f + + PREPARE_EXTERNAL_VAR_INDIRECT g_highest_address, x\trash + cmp \destReg, x\trash + bgt 0f + + INSERT_UNCHECKED_WRITE_BARRIER_CORE \destReg, \refReg, \trash, \trash2 + +0: + // Exit label + .endm + +// RhpCheckedAssignRef(Object** dst, Object* src) +// +// Write barrier for writes to objects that may reside +// on the managed heap. +// +// On entry: +// x0 : the destination address (LHS of the assignment). +// May not be an object reference (hence the checked). +// x1 : the object reference (RHS of the assignment). +// On exit: +// x1 : trashed +// x9 : trashed + LEAF_ENTRY RhpCheckedAssignRef, _TEXT + ALTERNATE_ENTRY RhpCheckedAssignRefX1 + + mov x14, x0 ; x14 = dst + mov x15, x1 ; x15 = val + b RhpCheckedAssignRefArm64 + +LEAF_END RhpCheckedAssignRef, _TEXT + +// RhpAssignRef(Object** dst, Object* src) +// +// Write barrier for writes to objects that are known to +// reside on the managed heap. +// +// On entry: +// x0 : the destination address (LHS of the assignment). +// x1 : the object reference (RHS of the assignment). +// On exit: +// x1 : trashed +// x9 : trashed +LEAF_ENTRY RhpAssignRef, _TEXT + ALTERNATE_ENTRY RhpAssignRefX1 + + mov x14, x0 ; x14 = dst + mov x15, x1 ; x15 = val + b RhpAssignRefArm64 + +LEAF_END RhpAssignRef, _TEXT + +// Interlocked operation helpers where the location is an objectref, thus requiring a GC write barrier upon +// successful updates. + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedLockCmpXchgAVLocation +// - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address + +// RhpCheckedLockCmpXchg(Object** dest, Object* value, Object* comparand) +// +// Interlocked compare exchange on objectref. +// +// On entry: +// x0: pointer to objectref +// x1: exchange value +// x2: comparand +// +// On exit: +// x0: original value of objectref +// x9: trashed +// x10: trashed +// + LEAF_ENTRY RhpCheckedLockCmpXchg + ALTERNATE_ENTRY RhpCheckedLockCmpXchgAVLocation + +CmpXchgRetry: + // Check location value is what we expect. + ldaxr x10, [x0] + cmp x10, x2 + bne CmpXchgNoUpdate + + // Current value matches comparand, attempt to update with the new value. + stlxr w9, x1, [x0] + cbnz w9, CmpXchgRetry + + // We have successfully updated the value of the objectref so now we need a GC write barrier. + // The following barrier code takes the destination in x0 and the value in x1 so the arguments are + // already correctly set up. + + INSERT_CHECKED_WRITE_BARRIER_CORE x0, x1, 9, x0 + +CmpXchgNoUpdate: + // x10 still contains the original value. + mov x0, x10 + ArmInterlockedOperationBarrier + ret lr + + LEAF_END RhpCheckedLockCmpXchg, _TEXT + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen within at RhpCheckedXchgAVLocation +// - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address + +// RhpCheckedXchg(Object** destination, Object* value) +// +// Interlocked exchange on objectref. +// +// On entry: +// x0: pointer to objectref +// x1: exchange value +// +// On exit: +// x0: original value of objectref +// x9: trashed +// x10: trashed +// + LEAF_ENTRY RhpCheckedXchg, _TEXT + ALTERNATE_ENTRY RhpCheckedXchgAVLocation + +ExchangeRetry: + // Read the existing memory location. + ldaxr x10, [x0] + + // Attempt to update with the new value. + stlxr w9, x1, [x0] + cbnz w9, ExchangeRetry + + // We have successfully updated the value of the objectref so now we need a GC write barrier. + // The following barrier code takes the destination in x0 and the value in x1 so the arguments are + // already correctly set up. + + INSERT_CHECKED_WRITE_BARRIER_CORE x0, x1, 9, x0 + + // x10 still contains the original value. + mov x0, x10 + ArmInterlockedOperationBarrier + ret + + LEAF_END RhpCheckedXchg, _TEXT + +LEAF_ENTRY RhpAssignRefArm64, _TEXT + ALTERNATE_ENTRY RhpAssignRefAVLocation + ALTERNATE_ENTRY RhpAssignRefX1AVLocation + stlr x15, [x14] + + INSERT_UNCHECKED_WRITE_BARRIER_CORE x14, x15, 12, X14 + + ret +LEAF_END RhpAssignRefArm64, _TEXT + +// void JIT_CheckedWriteBarrier(Object** dst, Object* src) +// On entry: +// x14 : the destination address (LHS of the assignment) +// x15 : the object reference (RHS of the assignment) +// +// On exit: +// x12 : trashed +// x14 : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract) +// x15 : trashed +// x17 : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +// +LEAF_ENTRY RhpCheckedAssignRefArm64, _TEXT + ALTERNATE_ENTRY RhpCheckedAssignRefAVLocation + ALTERNATE_ENTRY RhpCheckedAssignRefX1AVLocation + + stlr x15, [x14] + + INSERT_CHECKED_WRITE_BARRIER_CORE x14, x15, 12, X15 + + add x14, x14, #8 + + ret +LEAF_END RhpCheckedAssignRefArm64, _TEXT + +// void JIT_ByRefWriteBarrier +// On entry: +// x13 : the source address (points to object reference to write) +// x14 : the destination address (object reference written here) +// +// On exit: +// x12 : trashed +// x13 : incremented by 8 +// x14 : incremented by 8 +// x15 : trashed +// x17 : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +// +LEAF_ENTRY RhpByRefAssignRefArm64, _TEXT + ldr x15, [x13] + str x15, [x14] + + INSERT_CHECKED_WRITE_BARRIER_CORE x14, x15, 12, X15 + + add X13, x13, #8 + add x14, x14, #8 + + ret +LEAF_END RhpByRefAssignRefArm64, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm64/WriteBarriers.asm b/src/coreclr/nativeaot/Runtime/arm64/WriteBarriers.asm new file mode 100644 index 00000000000000..9aff215ffc57be --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/arm64/WriteBarriers.asm @@ -0,0 +1,393 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +;; +;; Define the helpers used to implement the write barrier required when writing an object reference into a +;; location residing on the GC heap. Such write barriers allow the GC to optimize which objects in +;; non-ephemeral generations need to be scanned for references to ephemeral objects during an ephemeral +;; collection. +;; + +#include "AsmMacros.h" + + TEXTAREA + +;; Macro used to copy contents of newly updated GC heap locations to a shadow copy of the heap. This is used +;; during garbage collections to verify that object references where never written to the heap without using a +;; write barrier. Note that we're potentially racing to update the shadow heap while other threads are writing +;; new references to the real heap. Since this can't be solved perfectly without critical sections around the +;; entire update process, we instead update the shadow location and then re-check the real location (as two +;; ordered operations) and if there is a disparity we'll re-write the shadow location with a special value +;; (INVALIDGCVALUE) which disables the check for that location. Since the shadow heap is only validated at GC +;; time and these write barrier operations are atomic wrt to GCs this is sufficient to guarantee that the +;; shadow heap contains only valid copies of real heap values or INVALIDGCVALUE. +#ifdef WRITE_BARRIER_CHECK + + SETALIAS g_GCShadow, ?g_GCShadow@@3PEAEEA + SETALIAS g_GCShadowEnd, ?g_GCShadowEnd@@3PEAEEA + EXTERN $g_GCShadow + EXTERN $g_GCShadowEnd + +INVALIDGCVALUE EQU 0xCCCCCCCD + + MACRO + ;; On entry: + ;; $destReg: location to be updated (cannot be x12,x17) + ;; $refReg: objectref to be stored (cannot be x12,x17) + ;; + ;; On exit: + ;; x12,x17: trashed + ;; other registers are preserved + ;; + UPDATE_GC_SHADOW $destReg, $refReg + + ;; If g_GCShadow is 0, don't perform the check. + adrp x12, $g_GCShadow + ldr x12, [x12, $g_GCShadow] + cbz x12, %ft1 + + ;; Save $destReg since we're about to modify it (and we need the original value both within the macro and + ;; once we exit the macro). + mov x17, $destReg + + ;; Transform $destReg into the equivalent address in the shadow heap. + adrp x12, g_lowest_address + ldr x12, [x12, g_lowest_address] + subs $destReg, $destReg, x12 + blt %ft0 + + adrp x12, $g_GCShadow + ldr x12, [x12, $g_GCShadow] + add $destReg, $destReg, x12 + + adrp x12, $g_GCShadowEnd + ldr x12, [x12, $g_GCShadowEnd] + cmp $destReg, x12 + bgt %ft0 + + ;; Update the shadow heap. + str $refReg, [$destReg] + + ;; The following read must be strongly ordered wrt to the write we've just performed in order to + ;; prevent race conditions. + dmb ish + + ;; Now check that the real heap location still contains the value we just wrote into the shadow heap. + mov x12, x17 + ldr x12, [x12] + cmp x12, $refReg + beq %ft0 + + ;; Someone went and updated the real heap. We need to invalidate the shadow location since we can't + ;; guarantee whose shadow update won. + MOVL64 x12, INVALIDGCVALUE, 0 + str x12, [$destReg] + +0 + ;; Restore original $destReg value + mov $destReg, x17 + +1 + MEND + +#else // WRITE_BARRIER_CHECK + + MACRO + UPDATE_GC_SHADOW $destReg, $refReg + MEND + +#endif // WRITE_BARRIER_CHECK + +;; There are several different helpers used depending on which register holds the object reference. Since all +;; the helpers have identical structure we use a macro to define this structure. Two arguments are taken, the +;; name of the register that points to the location to be updated and the name of the register that holds the +;; object reference (this should be in upper case as it's used in the definition of the name of the helper). + +;; Define a sub-macro first that expands to the majority of the barrier implementation. This is used below for +;; some interlocked helpers that need an inline barrier. + MACRO + ;; On entry: + ;; $destReg: location to be updated (cannot be x12,x17) + ;; $refReg: objectref to be stored (cannot be x12,x17) + ;; $trashReg: register that can be trashed (can be $destReg or $refReg) + ;; + ;; On exit: + ;; $trashReg: trashed + ;; x12: trashed + ;; x17: trashed if WRITE_BARRIER_CHECK + ;; + INSERT_UNCHECKED_WRITE_BARRIER_CORE $destReg, $refReg, $trashReg + + ;; Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + ;; we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW $destReg, $refReg + + ;; We can skip the card table write if the reference is to + ;; an object not on the epehemeral segment. + adrp x12, g_ephemeral_low + ldr x12, [x12, g_ephemeral_low] + cmp $refReg, x12 + blt %ft0 + + adrp x12, g_ephemeral_high + ldr x12, [x12, g_ephemeral_high] + cmp $refReg, x12 + bge %ft0 + + ;; Set this object's card, if it hasn't already been set. + adrp x12, g_card_table + ldr x12, [x12, g_card_table] + add $trashReg, x12, $destReg lsr #11 + + ;; Check that this card hasn't already been written. Avoiding useless writes is a big win on + ;; multi-proc systems since it avoids cache trashing. + ldrb w12, [$trashReg] + cmp x12, 0xFF + beq %ft0 + + mov x12, 0xFF + strb w12, [$trashReg] + +0 + ;; Exit label + MEND + + MACRO + ;; On entry: + ;; $destReg: location to be updated (cannot be x12,x17) + ;; $refReg: objectref to be stored (cannot be x12,x17) + ;; $trashReg: register that can be trashed (can be $destReg or $refReg) + ;; + ;; On exit: + ;; $trashReg: trashed + ;; x12: trashed + ;; x17: trashed if WRITE_BARRIER_CHECK + ;; + INSERT_CHECKED_WRITE_BARRIER_CORE $destReg, $refReg, $trashReg + + ;; The "check" of this checked write barrier - is $destReg + ;; within the heap? if no, early out. + adrp x12, g_lowest_address + ldr x12, [x12, g_lowest_address] + cmp $destReg, x12 + blt %ft0 + + adrp x12, g_highest_address + ldr x12, [x12, g_highest_address] + cmp $destReg, x12 + bgt %ft0 + + INSERT_UNCHECKED_WRITE_BARRIER_CORE $destReg, $refReg, $trashReg + +0 + ;; Exit label + MEND + +;; RhpCheckedAssignRef(Object** dst, Object* src) +;; +;; Write barrier for writes to objects that may reside +;; on the managed heap. +;; +;; On entry: +;; x0 : the destination address (LHS of the assignment). +;; May not be an object reference (hence the checked). +;; x1 : the object reference (RHS of the assignment) +;; +;; On exit: +;; x12 : trashed +;; x14 : trashed +;; x15 : trashed +;; x17 : trashed if WRITE_BARRIER_CHECK + LEAF_ENTRY RhpCheckedAssignRef + ALTERNATE_ENTRY RhpCheckedAssignRefX1 + + mov x14, x0 ; x14 = dst + mov x15, x1 ; x15 = val + b RhpCheckedAssignRefArm64 + + LEAF_END RhpCheckedAssignRef + +;; RhpAssignRef(Object** dst, Object* src) +;; +;; Write barrier for writes to objects that are known to +;; reside on the managed heap. +;; +;; On entry: +;; x0 : the destination address (LHS of the assignment) +;; x1 : the object reference (RHS of the assignment) +;; +;; On exit: +;; x12 : trashed +;; x14 : trashed +;; x15 : trashed +;; x17 : trashed if WRITE_BARRIER_CHECK + LEAF_ENTRY RhpAssignRef + ALTERNATE_ENTRY RhpAssignRefX1 + + mov x14, x0 ; x14 = dst + mov x15, x1 ; x15 = val + b RhpAssignRefArm64 + + LEAF_END RhpAssignRef + +;; Interlocked operation helpers where the location is an objectref, thus requiring a GC write barrier upon +;; successful updates. + +;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedLockCmpXchgAVLocation +;; - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address + +;; RhpCheckedLockCmpXchg(Object** dest, Object* value, Object* comparand) +;; +;; Interlocked compare exchange on objectref. +;; +;; On entry: +;; x0 : pointer to objectref +;; x1 : exchange value +;; x2 : comparand +;; +;; On exit: +;; x0 : original value of objectref +;; x9 : trashed +;; x10 : trashed +;; x12 : trashed +;; x17 : trashed if WRITE_BARRIER_CHECK +;; + LEAF_ENTRY RhpCheckedLockCmpXchg + ALTERNATE_ENTRY RhpCheckedLockCmpXchgAVLocation + +CmpXchgRetry + ;; Check location value is what we expect. + ldaxr x10, [x0] + cmp x10, x2 + bne CmpXchgNoUpdate + + ;; Current value matches comparand, attempt to update with the new value. + stlxr w9, x1, [x0] + cbnz w9, CmpXchgRetry + + ;; We've successfully updated the value of the objectref so now we need a GC write barrier. + ;; The following barrier code takes the destination in x0 and the value in x1 so the arguments are + ;; already correctly set up. + + INSERT_CHECKED_WRITE_BARRIER_CORE x0, x1, x0 + +CmpXchgNoUpdate + ;; x10 still contains the original value. + mov x0, x10 + ArmInterlockedOperationBarrier + ret lr + + LEAF_END RhpCheckedLockCmpXchg + +;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen within at RhpCheckedXchgAVLocation +;; - Function "UnwindSimpleHelperToCaller" assumes no registers were pushed and LR contains the return address + +;; RhpCheckedXchg(Object** destination, Object* value) +;; +;; Interlocked exchange on objectref. +;; +;; On entry: +;; x0 : pointer to objectref +;; x1 : exchange value +;; +;; On exit: +;; x0 : original value of objectref +;; x9 : trashed +;; x10 : trashed +;; x12 : trashed +;; x17 : trashed if WRITE_BARRIER_CHECK +;; + LEAF_ENTRY RhpCheckedXchg + ALTERNATE_ENTRY RhpCheckedXchgAVLocation + +ExchangeRetry + ;; Read the existing memory location. + ldaxr x10, [x0] + + ;; Attempt to update with the new value. + stlxr w9, x1, [x0] + cbnz w9, ExchangeRetry + + ;; We've successfully updated the value of the objectref so now we need a GC write barrier. + ;; The following barrier code takes the destination in x0 and the value in x1 so the arguments are + ;; already correctly set up. + + INSERT_CHECKED_WRITE_BARRIER_CORE x0, x1, x0 + + ;; x10 still contains the original value. + mov x0, x10 + ArmInterlockedOperationBarrier + ret + + LEAF_END RhpCheckedXchg + +;; RhpAssignRefArm64(Object** dst, Object* src) +;; +;; On entry: +;; x14 : the destination address (LHS of the assignment). +;; x15 : the object reference (RHS of the assignment). +;; +;; On exit: +;; x12 : trashed +;; x15 : trashed +;; x17 : trashed if WRITE_BARRIER_CHECK +;; + LEAF_ENTRY RhpAssignRefArm64, _TEXT + ALTERNATE_ENTRY RhpAssignRefAVLocation + ALTERNATE_ENTRY RhpAssignRefX1AVLocation + + stlr x15, [x14] + + INSERT_UNCHECKED_WRITE_BARRIER_CORE x14, x15, x15 + + ret + LEAF_END RhpAssignRefArm64 + +;; void JIT_CheckedWriteBarrier(Object** dst, Object* src) +;; On entry: +;; x14 : the destination address (LHS of the assignment) +;; x15 : the object reference (RHS of the assignment) +;; +;; On exit: +;; x12 : trashed +;; x15 : trashed +;; x17 : trashed if WRITE_BARRIER_CHECK +;; + LEAF_ENTRY RhpCheckedAssignRefArm64, _TEXT + ALTERNATE_ENTRY RhpCheckedAssignRefAVLocation + ALTERNATE_ENTRY RhpCheckedAssignRefX1AVLocation + + stlr x15, [x14] + + INSERT_CHECKED_WRITE_BARRIER_CORE x14, x15, x15 + + ret + LEAF_END RhpCheckedAssignRefArm64 + +;; void JIT_ByRefWriteBarrier +;; On entry: +;; x13 : the source address (points to object reference to write) +;; x14 : the destination address (object reference written here) +;; +;; On exit: +;; x12 : trashed +;; x13 : incremented by 8 +;; x14 : incremented by 8 +;; x15 : trashed +;; x17 : trashed if WRITE_BARRIER_CHECK +;; + LEAF_ENTRY RhpByRefAssignRefArm64, _TEXT + ldr x15, [x13] + stlr x15, [x14] + + INSERT_CHECKED_WRITE_BARRIER_CORE x14, x15, x15 + + add x13, x13, #8 + add x14, x14, #8 + + ret + LEAF_END RhpByRefAssignRefArm64 + + end diff --git a/src/coreclr/nativeaot/Runtime/event.cpp b/src/coreclr/nativeaot/Runtime/event.cpp new file mode 100644 index 00000000000000..d91fa39d906dfc --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/event.cpp @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "event.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "gcrhinterface.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "holder.h" +#include "Crst.h" +#include "RWLock.h" +#include "threadstore.h" +#include "threadstore.inl" + +// +// ----------------------------------------------------------------------------------------------------------- +// +// CLR wrapper around events. This version directly uses Win32 events (there's no support for host +// interception). +// + +bool CLREventStatic::CreateManualEventNoThrow(bool bInitialState) +{ + m_hEvent = PalCreateEventW(NULL, TRUE, bInitialState, NULL); + m_fInitialized = true; + return IsValid(); +} + +bool CLREventStatic::CreateAutoEventNoThrow(bool bInitialState) +{ + m_hEvent = PalCreateEventW(NULL, FALSE, bInitialState, NULL); + m_fInitialized = true; + return IsValid(); +} + +bool CLREventStatic::CreateOSManualEventNoThrow(bool bInitialState) +{ + m_hEvent = PalCreateEventW(NULL, TRUE, bInitialState, NULL); + m_fInitialized = true; + return IsValid(); +} + +bool CLREventStatic::CreateOSAutoEventNoThrow(bool bInitialState) +{ + m_hEvent = PalCreateEventW(NULL, FALSE, bInitialState, NULL); + m_fInitialized = true; + return IsValid(); +} + +void CLREventStatic::CloseEvent() +{ + if (m_fInitialized && m_hEvent != INVALID_HANDLE_VALUE) + { + PalCloseHandle(m_hEvent); + m_hEvent = INVALID_HANDLE_VALUE; + } +} + +bool CLREventStatic::IsValid() const +{ + return m_fInitialized && m_hEvent != INVALID_HANDLE_VALUE; +} + +bool CLREventStatic::Set() +{ + if (!m_fInitialized) + return false; + return PalSetEvent(m_hEvent); +} + +bool CLREventStatic::Reset() +{ + if (!m_fInitialized) + return false; + return PalResetEvent(m_hEvent); +} + +uint32_t CLREventStatic::Wait(uint32_t dwMilliseconds, bool bAlertable, bool bAllowReentrantWait) +{ + uint32_t result = WAIT_FAILED; + + if (m_fInitialized) + { + bool disablePreemptive = false; + Thread * pCurThread = ThreadStore::GetCurrentThreadIfAvailable(); + + if (NULL != pCurThread) + { + if (pCurThread->IsCurrentThreadInCooperativeMode()) + { + pCurThread->EnablePreemptiveMode(); + disablePreemptive = true; + } + } + + result = PalCompatibleWaitAny(bAlertable, dwMilliseconds, 1, &m_hEvent, bAllowReentrantWait); + + if (disablePreemptive) + { + pCurThread->DisablePreemptiveMode(); + } + } + + return result; +} + +HANDLE CLREventStatic::GetOSEvent() +{ + if (!m_fInitialized) + return INVALID_HANDLE_VALUE; + return m_hEvent; +} diff --git a/src/coreclr/nativeaot/Runtime/event.h b/src/coreclr/nativeaot/Runtime/event.h new file mode 100644 index 00000000000000..b46b9e538207cb --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/event.h @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +class CLREventStatic +{ +public: + bool CreateManualEventNoThrow(bool bInitialState); + bool CreateAutoEventNoThrow(bool bInitialState); + bool CreateOSManualEventNoThrow(bool bInitialState); + bool CreateOSAutoEventNoThrow(bool bInitialState); + void CloseEvent(); + bool IsValid() const; + bool Set(); + bool Reset(); + uint32_t Wait(uint32_t dwMilliseconds, bool bAlertable, bool bAllowReentrantWait = false); + HANDLE GetOSEvent(); + +private: + HANDLE m_hEvent; + bool m_fInitialized; +}; diff --git a/src/coreclr/nativeaot/Runtime/eventtrace.cpp b/src/coreclr/nativeaot/Runtime/eventtrace.cpp new file mode 100644 index 00000000000000..9d115b2f264b74 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/eventtrace.cpp @@ -0,0 +1,6503 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: eventtrace.cpp +// Abstract: This module implements Event Tracing support +// +// ============================================================================ + +#include "common.h" + +#ifdef FEATURE_REDHAWK +#include "gcenv.h" +#include "gcheaputilities.h" + +#include "daccess.h" + +#include "slist.h" +#include "varint.h" +#include "regdisplay.h" +#include "stackframeiterator.h" +#include "thread.h" +#include "rwlock.h" +#include "threadstore.h" +#include "threadstore.inl" +//#include "PalRedhawk.h" + +#define Win32EventWrite PalEventWrite +#else // !FEATURE_REDHAWK + +#include "eventtrace.h" +#include "winbase.h" +#include "contract.h" +#include "ex.h" +#include "dbginterface.h" +#define Win32EventWrite EventWrite + +// Flags used to store some runtime information for Event Tracing +BOOL g_fEEOtherStartup=FALSE; +BOOL g_fEEComActivatedStartup=FALSE; +LPCGUID g_fEEComObjectGuid=&GUID_NULL; + +BOOL g_fEEHostedStartup = FALSE; + +#endif // FEATURE_REDHAWK + +#include "eventtracepriv.h" + +#ifdef FEATURE_REDHAWK +volatile LONGLONG ETW::GCLog::s_l64LastClientSequenceNumber = 0; +#else // FEATURE_REDHAWK +Volatile ETW::GCLog::s_l64LastClientSequenceNumber = 0; +#endif // FEATURE_REDHAWK + +#ifndef FEATURE_REDHAWK + +//--------------------------------------------------------------------------------------- +// Helper macros to determine which version of the Method events to use +// +// The V2 versions of these events include the ReJITID, the V1 versions do not. +// Historically, when we version events, we'd just stop sending the old version and only +// send the new one. However, now that we have xperf in heavy use internally and soon to be +// used externally, we need to be a bit careful. In particular, we'd like to allow +// current xperf to continue working without knowledge of ReJITIDs, and allow future +// xperf to decode symbols in ReJITted functions. Thus, +// * During a first-JIT, only issue the existing V1 MethodLoad, etc. events (NOT v0, +// NOT v2). This event does not include a ReJITID, and can thus continue to be +// parsed by older decoders. +// * During a rejit, only issue the new V2 events (NOT v0 or v1), which will include a +// nonzero ReJITID. Thus, your unique key for a method extent would be MethodID + +// ReJITID + extent (hot/cold). These events will be ignored by older decoders +// (including current xperf) because of the version number, but xperf will be +// updated to decode these in the future. + +#define FireEtwMethodLoadVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID) \ +{ \ + if (rejitID == 0) \ + { FireEtwMethodLoadVerbose_V1(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID); } \ + else \ + { FireEtwMethodLoadVerbose_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID); } \ +} + +#define FireEtwMethodLoad_V1_or_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, clrInstanceID, rejitID) \ +{ \ + if (rejitID == 0) \ + { FireEtwMethodLoad_V1(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, clrInstanceID); } \ + else \ + { FireEtwMethodLoad_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, clrInstanceID, rejitID); } \ +} + +#define FireEtwMethodUnloadVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID) \ +{ \ + if (rejitID == 0) \ + { FireEtwMethodUnloadVerbose_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID); } \ + else \ + { FireEtwMethodUnloadVerbose_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID); } \ +} + +#define FireEtwMethodUnload_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID) \ +{ \ + if (rejitID == 0) \ + { FireEtwMethodUnload_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID); } \ + else \ + { FireEtwMethodUnload_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID); } \ +} + +#define FireEtwMethodDCStartVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID) \ +{ \ + if (rejitID == 0) \ + { FireEtwMethodDCStartVerbose_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID); } \ + else \ + { FireEtwMethodDCStartVerbose_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID); } \ +} + +#define FireEtwMethodDCStart_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID) \ +{ \ + if (rejitID == 0) \ + { FireEtwMethodDCStart_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID); } \ + else \ + { FireEtwMethodDCStart_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID); } \ +} + +#define FireEtwMethodDCEndVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID) \ +{ \ + if (rejitID == 0) \ + { FireEtwMethodDCEndVerbose_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID); } \ + else \ + { FireEtwMethodDCEndVerbose_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID); } \ +} + +#define FireEtwMethodDCEnd_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID) \ +{ \ + if (rejitID == 0) \ + { FireEtwMethodDCEnd_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID); } \ + else \ + { FireEtwMethodDCEnd_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID); } \ +} + +// Module load / unload events: +// There is no precedent here for using GUIDs in Mac events, and it's doubtful any +// of the new PDB fields for the V2 Module events are at all useful on the Mac anyway. So +// stick with V1 module events on the Mac. + +#ifdef FEATURE_DTRACE +#define FireEtwModuleLoad_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) \ + FireEtwModuleLoad_V1(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId) +#define FireEtwModuleUnload_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) \ + FireEtwModuleUnload_V1(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId) +#define FireEtwModuleDCStart_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) \ + FireEtwModuleDCStart_V1(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId) +#define FireEtwModuleDCEnd_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) \ + FireEtwModuleDCEnd_V1(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId) +#else // FEATURE_DTRACE +#define FireEtwModuleLoad_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) \ + FireEtwModuleLoad_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) +#define FireEtwModuleUnload_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) \ + FireEtwModuleUnload_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) +#define FireEtwModuleDCStart_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) \ + FireEtwModuleDCStart_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) +#define FireEtwModuleDCEnd_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) \ + FireEtwModuleDCEnd_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) +#endif // FEATURE_DTRACE + + +//--------------------------------------------------------------------------------------- +// +// Rather than checking the NGEN keyword on the runtime provider directly, use this +// helper that checks that the NGEN runtime provider keyword is enabled AND the +// OverrideAndSuppressNGenEvents keyword on the runtime provider is NOT enabled. +// +// OverrideAndSuppressNGenEvents allows controllers to set the expensive NGEN keyword for +// older runtimes (< 4.0) where NGEN PDB info is NOT available, while suppressing those +// expensive events on newer runtimes (>= 4.5) where NGEN PDB info IS available. Note +// that 4.0 has NGEN PDBS but unfortunately not the OverrideAndSuppressNGenEvents +// keyword, b/c NGEN PDBs were made publicly only after 4.0 shipped. So tools that need +// to consume both <4.0 and 4.0 events would neeed to enable the expensive NGEN events to +// deal properly with 3.5, even though those events aren't necessary on 4.0. +// +// On CoreCLR, this keyword is a no-op, because coregen PDBs don't exist (and thus we'll +// need the NGEN rundown to still work on Silverligth). +// +// Return Value: +// nonzero iff NGenKeyword is enabled on the runtime provider and +// OverrideAndSuppressNGenEventsKeyword is not enabled on the runtime provider. +// + +BOOL IsRuntimeNgenKeywordEnabledAndNotSuppressed() +{ + LIMITED_METHOD_CONTRACT; + + return + ( + ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_NGEN_KEYWORD) + && ! ( ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_OVERRIDEANDSUPPRESSNGENEVENTS_KEYWORD) ) + ); +} + +// Same as above, but for the rundown provider +BOOL IsRundownNgenKeywordEnabledAndNotSuppressed() +{ + LIMITED_METHOD_CONTRACT; + + return + ( + ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNNGEN_KEYWORD) + && ! ( ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNOVERRIDEANDSUPPRESSNGENEVENTS_KEYWORD) ) + ); +} + +/*******************************************************/ +/* Fast assembly function to get the topmost EBP frame */ +/*******************************************************/ +#if defined(TARGET_X86) +extern "C" +{ + CallStackFrame* GetEbp() + { + CallStackFrame *frame=NULL; + __asm + { + mov frame, ebp + } + return frame; + } +} +#endif //TARGET_X86 + +#ifndef FEATURE_PAL + +/*************************************/ +/* Function to append a frame to an existing stack */ +/*************************************/ +void ETW::SamplingLog::Append(SIZE_T currentFrame) +{ + LIMITED_METHOD_CONTRACT; + if(m_FrameCount < (ETW::SamplingLog::s_MaxStackSize-1) && + currentFrame != 0) + { + m_EBPStack[m_FrameCount] = currentFrame; + m_FrameCount++; + } +}; + +/********************************************************/ +/* Function to get the callstack on the current thread */ +/********************************************************/ +ETW::SamplingLog::EtwStackWalkStatus ETW::SamplingLog::GetCurrentThreadsCallStack(UINT32 *frameCount, PVOID **Stack) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_TOLERANT; + } + CONTRACTL_END; + + // The stack walk performed below can cause allocations (thus entering the host). But + // this is acceptable, since we're not supporting the use of SQL/F1 profiling and + // full-blown ETW CLR stacks (which would be redundant). + PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonUnsupportedForSQLF1Profiling); + + m_FrameCount = 0; + ETW::SamplingLog::EtwStackWalkStatus stackwalkStatus = SaveCurrentStack(); + + _ASSERTE(m_FrameCount < ETW::SamplingLog::s_MaxStackSize); + + // this not really needed, but let's do it + // because we use the framecount while dumping the stack event + for(int i=m_FrameCount; im_State & Thread::TS_Hijacked) { + return ETW::SamplingLog::UnInitialized; + } + + if (pThread->IsEtwStackWalkInProgress()) + { + return ETW::SamplingLog::InProgress; + } + pThread->MarkEtwStackWalkInProgress(); + EX_TRY + { +#ifdef TARGET_X86 + CallStackFrame *currentEBP = GetEbp(); + CallStackFrame *lastEBP = NULL; + while(currentEBP) + { + lastEBP = currentEBP; + currentEBP = currentEBP->m_Next; + + // Skip the top N frames + if(skipTopNFrames) { + skipTopNFrames--; + continue; + } + + // Save the Return Address for symbol decoding + Append(lastEBP->m_ReturnAddress); + + // Check for stack limits + if((SIZE_T)currentEBP < (SIZE_T)Thread::GetStackLowerBound() || (SIZE_T)currentEBP > (SIZE_T)Thread::GetStackUpperBound()) + { + break; + } + + // If we have a too small address, we are probably bad + if((SIZE_T)currentEBP < (SIZE_T)0x10000) + break; + + if((SIZE_T)currentEBP < (SIZE_T)lastEBP) + { + break; + } + } +#else + CONTEXT ctx; + ClrCaptureContext(&ctx); + UINT_PTR ControlPc = 0; + UINT_PTR CurrentSP = 0, PrevSP = 0; + + while(1) + { + // Unwind to the caller + ControlPc = Thread::VirtualUnwindCallFrame(&ctx); + + // This is to take care of recursion + CurrentSP = (UINT_PTR)GetSP(&ctx); + + // when to break from this loop + if ( ControlPc == 0 || ( PrevSP == CurrentSP ) ) + { + break; + } + + // Skip the top N frames + if ( skipTopNFrames ) { + skipTopNFrames--; + continue; + } + + // Add the stack frame to the list + Append(ControlPc); + + PrevSP = CurrentSP; + } +#endif //TARGET_X86 + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); + pThread->MarkEtwStackWalkCompleted(); +#endif //!DACCESS_COMPILE + + return ETW::SamplingLog::Completed; +} +#endif //!FEATURE_PAL + +#endif // !FEATURE_REDHAWK + + +#if defined(FEATURE_REDHAWK) || !defined(FEATURE_PAL) || defined(FEATURE_DTRACE) + +/****************************************************************************/ +/* Methods that are called from the runtime */ +/****************************************************************************/ + +#ifndef FEATURE_DTRACE +/****************************************************************************/ +/* Methods for rundown events */ +/* Since DTRACe does not support passing a method pointer as a callback when*/ +/* enable a events, rundown events are not supported on Mac */ +/****************************************************************************/ + +/***************************************************************************/ +/* This function should be called from the event tracing callback routine + when the private CLR provider is enabled */ +/***************************************************************************/ + +#ifndef FEATURE_REDHAWK + +void ETW::GCLog::GCSettingsEvent() +{ + if (GCHeapUtilities::IsGCHeapInitialized()) + { + if (ETW_TRACING_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, + GCSettings)) + { + ETW::GCLog::ETW_GC_INFO Info; + + Info.GCSettings.ServerGC = GCHeapUtilities::IsServerHeap (); + Info.GCSettings.SegmentSize = GCHeapUtilities::GetGCHeap()->GetValidSegmentSize (FALSE); + Info.GCSettings.LargeObjectSegmentSize = GCHeapUtilities::GetGCHeap()->GetValidSegmentSize (TRUE); + FireEtwGCSettings_V1(Info.GCSettings.SegmentSize, Info.GCSettings.LargeObjectSegmentSize, Info.GCSettings.ServerGC, GetClrInstanceId()); + } + GCHeapUtilities::GetGCHeap()->TraceGCSegments(); + } +}; + +#endif // !FEATURE_REDHAWK + + +//--------------------------------------------------------------------------------------- +// Code for sending GC heap object events is generally the same for both FEATURE_REDHAWK +// and !FEATURE_REDHAWK builds +//--------------------------------------------------------------------------------------- + + +// Simple helpers called by the GC to decide whether it needs to do a walk of heap +// objects and / or roots. + +BOOL ETW::GCLog::ShouldWalkHeapObjectsForEtw() +{ + LIMITED_METHOD_CONTRACT; + return ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_GCHEAPDUMP_KEYWORD); +} + +BOOL ETW::GCLog::ShouldWalkHeapRootsForEtw() +{ + LIMITED_METHOD_CONTRACT; + return ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_GCHEAPDUMP_KEYWORD); +} + +BOOL ETW::GCLog::ShouldTrackMovementForEtw() +{ + LIMITED_METHOD_CONTRACT; + return ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_GCHEAPSURVIVALANDMOVEMENT_KEYWORD); +} + +BOOL ETW::GCLog::ShouldWalkStaticsAndCOMForEtw() +{ + // @TODO: + return FALSE; +} + +void ETW::GCLog::WalkStaticsAndCOMForETW() +{ + // @TODO: +} + + +// Batches the list of moved/surviving references for the GCBulkMovedObjectRanges / +// GCBulkSurvivingObjectRanges events +struct EtwGcMovementContext +{ +public: + // An instance of EtwGcMovementContext is dynamically allocated and stored + // inside of MovedReferenceContextForEtwAndProfapi, which in turn is dynamically + // allocated and pointed to by a profiling_context pointer created by the GC on the stack. + // This is used to batch and send GCBulkSurvivingObjectRanges events and + // GCBulkMovedObjectRanges events. This method is passed a pointer to + // MovedReferenceContextForEtwAndProfapi::pctxEtw; if non-NULL it gets returned; + // else, a new EtwGcMovementContext is allocated, stored in that pointer, and + // then returned. Callers should test for NULL, which can be returned if out of + // memory + static EtwGcMovementContext * GetOrCreateInGCContext(EtwGcMovementContext ** ppContext) + { + LIMITED_METHOD_CONTRACT; + + _ASSERTE(ppContext != NULL); + + EtwGcMovementContext * pContext = *ppContext; + if (pContext == NULL) + { + pContext = new (nothrow) EtwGcMovementContext; + *ppContext = pContext; + } + return pContext; + } + + EtwGcMovementContext() : + iCurBulkSurvivingObjectRanges(0), + iCurBulkMovedObjectRanges(0) + { + LIMITED_METHOD_CONTRACT; + Clear(); + } + + // Resets structure for reuse on construction, and after each flush. + // (Intentionally leave iCurBulk* as is, since they persist across flushes within a GC.) + void Clear() + { + LIMITED_METHOD_CONTRACT; + cBulkSurvivingObjectRanges = 0; + cBulkMovedObjectRanges = 0; + ZeroMemory(rgGCBulkSurvivingObjectRanges, sizeof(rgGCBulkSurvivingObjectRanges)); + ZeroMemory(rgGCBulkMovedObjectRanges, sizeof(rgGCBulkMovedObjectRanges)); + } + + //--------------------------------------------------------------------------------------- + // GCBulkSurvivingObjectRanges + //--------------------------------------------------------------------------------------- + + // Sequence number for each GCBulkSurvivingObjectRanges event + UINT iCurBulkSurvivingObjectRanges; + + // Number of surviving object ranges currently filled out in rgGCBulkSurvivingObjectRanges array + UINT cBulkSurvivingObjectRanges; + + // Struct array containing the primary data for each GCBulkSurvivingObjectRanges + // event. Fix the size so the total event stays well below the 64K limit (leaving + // lots of room for non-struct fields that come before the values data) + EventStructGCBulkSurvivingObjectRangesValue rgGCBulkSurvivingObjectRanges[ + (cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkSurvivingObjectRangesValue)]; + + //--------------------------------------------------------------------------------------- + // GCBulkMovedObjectRanges + //--------------------------------------------------------------------------------------- + + // Sequence number for each GCBulkMovedObjectRanges event + UINT iCurBulkMovedObjectRanges; + + // Number of Moved object ranges currently filled out in rgGCBulkMovedObjectRanges array + UINT cBulkMovedObjectRanges; + + // Struct array containing the primary data for each GCBulkMovedObjectRanges + // event. Fix the size so the total event stays well below the 64K limit (leaving + // lots of room for non-struct fields that come before the values data) + EventStructGCBulkMovedObjectRangesValue rgGCBulkMovedObjectRanges[ + (cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkMovedObjectRangesValue)]; +}; + +// Contains above struct for ETW, plus extra info (opaque to us) used by the profiling +// API to track its own information. +struct MovedReferenceContextForEtwAndProfapi +{ + // An instance of MovedReferenceContextForEtwAndProfapi is dynamically allocated and + // pointed to by a profiling_context pointer created by the GC on the stack. This is used to + // batch and send GCBulkSurvivingObjectRanges events and GCBulkMovedObjectRanges + // events and the corresponding callbacks for profapi profilers. This method is + // passed a pointer to a MovedReferenceContextForEtwAndProfapi; if non-NULL it gets + // returned; else, a new MovedReferenceContextForEtwAndProfapi is allocated, stored + // in that pointer, and then returned. Callers should test for NULL, which can be + // returned if out of memory + static MovedReferenceContextForEtwAndProfapi * CreateInGCContext(LPVOID pvContext) + { + LIMITED_METHOD_CONTRACT; + + _ASSERTE(pvContext != NULL); + + MovedReferenceContextForEtwAndProfapi * pContext = *(MovedReferenceContextForEtwAndProfapi **) pvContext; + + // Shouldn't be called if the context was already created. Perhaps someone made + // one too many BeginMovedReferences calls, or didn't have an EndMovedReferences + // in between? + _ASSERTE(pContext == NULL); + + pContext = new (nothrow) MovedReferenceContextForEtwAndProfapi; + *(MovedReferenceContextForEtwAndProfapi **) pvContext = pContext; + + return pContext; + } + + + MovedReferenceContextForEtwAndProfapi() : + pctxProfAPI(NULL), + pctxEtw(NULL) + + { + LIMITED_METHOD_CONTRACT; + } + + LPVOID pctxProfAPI; + EtwGcMovementContext * pctxEtw; +}; + + +//--------------------------------------------------------------------------------------- +// +// Called by the GC for each moved or surviving reference that it encounters. This +// batches the info into our context's buffer, and flushes that buffer to ETW as it fills +// up. +// +// Arguments: +// * pbMemBlockStart - Start of moved/surviving block +// * pbMemBlockEnd - Next pointer after end of moved/surviving block +// * cbRelocDistance - How far did the block move? (0 for non-compacted / surviving +// references; negative if moved to earlier addresses) +// * profilingContext - Where our context is stored +// * fCompacting - Is this a compacting GC? Used to decide whether to send the moved +// or surviving event +// + +// static +void ETW::GCLog::MovedReference( + BYTE * pbMemBlockStart, + BYTE * pbMemBlockEnd, + ptrdiff_t cbRelocDistance, + size_t profilingContext, + BOOL fCompacting, + BOOL /*fAllowProfApiNotification*/) // @TODO: unused param from newer implementation +{ +#ifndef WINXP_AND_WIN2K3_BUILD_SUPPORT + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + CAN_TAKE_LOCK; // EEToProfInterfaceImpl::AllocateMovedReferencesData takes lock + } + CONTRACTL_END; + + MovedReferenceContextForEtwAndProfapi * pCtxForEtwAndProfapi = + (MovedReferenceContextForEtwAndProfapi *) profilingContext; + if (pCtxForEtwAndProfapi == NULL) + { + _ASSERTE(!"MovedReference() encountered a NULL profilingContext"); + return; + } + +#ifdef PROFILING_SUPPORTED + // ProfAPI + { + BEGIN_PIN_PROFILER(CORProfilerTrackGC()); + g_profControlBlock.pProfInterface->MovedReference(pbMemBlockStart, + pbMemBlockEnd, + cbRelocDistance, + &(pCtxForEtwAndProfapi->pctxProfAPI), + fCompacting); + END_PIN_PROFILER(); + } +#endif // PROFILING_SUPPORTED + + // ETW + + if (!ShouldTrackMovementForEtw()) + return; + + EtwGcMovementContext * pContext = + EtwGcMovementContext::GetOrCreateInGCContext(&pCtxForEtwAndProfapi->pctxEtw); + if (pContext == NULL) + return; + + if (fCompacting) + { + // Moved references + + _ASSERTE(pContext->cBulkMovedObjectRanges < _countof(pContext->rgGCBulkMovedObjectRanges)); + EventStructGCBulkMovedObjectRangesValue * pValue = + &pContext->rgGCBulkMovedObjectRanges[pContext->cBulkMovedObjectRanges]; + pValue->OldRangeBase = pbMemBlockStart; + pValue->NewRangeBase = pbMemBlockStart + cbRelocDistance; + pValue->RangeLength = pbMemBlockEnd - pbMemBlockStart; + pContext->cBulkMovedObjectRanges++; + + // If buffer is now full, empty it into ETW + if (pContext->cBulkMovedObjectRanges == _countof(pContext->rgGCBulkMovedObjectRanges)) + { + FireEtwGCBulkMovedObjectRanges( + pContext->iCurBulkMovedObjectRanges, + pContext->cBulkMovedObjectRanges, + GetClrInstanceId(), + sizeof(pContext->rgGCBulkMovedObjectRanges[0]), + &pContext->rgGCBulkMovedObjectRanges[0]); + + pContext->iCurBulkMovedObjectRanges++; + pContext->Clear(); + } + } + else + { + // Surviving references + + _ASSERTE(pContext->cBulkSurvivingObjectRanges < _countof(pContext->rgGCBulkSurvivingObjectRanges)); + EventStructGCBulkSurvivingObjectRangesValue * pValue = + &pContext->rgGCBulkSurvivingObjectRanges[pContext->cBulkSurvivingObjectRanges]; + pValue->RangeBase = pbMemBlockStart; + pValue->RangeLength = pbMemBlockEnd - pbMemBlockStart; + pContext->cBulkSurvivingObjectRanges++; + + // If buffer is now full, empty it into ETW + if (pContext->cBulkSurvivingObjectRanges == _countof(pContext->rgGCBulkSurvivingObjectRanges)) + { + FireEtwGCBulkSurvivingObjectRanges( + pContext->iCurBulkSurvivingObjectRanges, + pContext->cBulkSurvivingObjectRanges, + GetClrInstanceId(), + sizeof(pContext->rgGCBulkSurvivingObjectRanges[0]), + &pContext->rgGCBulkSurvivingObjectRanges[0]); + + pContext->iCurBulkSurvivingObjectRanges++; + pContext->Clear(); + } + } +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT +} + + +//--------------------------------------------------------------------------------------- +// +// Called by the GC just before it begins enumerating plugs. Gives us a chance to +// allocate our context structure, to allow us to batch plugs before firing events +// for them +// +// Arguments: +// * pProfilingContext - Points to location on stack (in GC function) where we can +// store a pointer to the context we allocate +// + +// static +void ETW::GCLog::BeginMovedReferences(size_t * pProfilingContext) +{ + LIMITED_METHOD_CONTRACT; + + MovedReferenceContextForEtwAndProfapi::CreateInGCContext(LPVOID(pProfilingContext)); +} + + +//--------------------------------------------------------------------------------------- +// +// Called by the GC at the end of a heap walk to give us a place to flush any remaining +// buffers of data to ETW or the profapi profiler +// +// Arguments: +// profilingContext - Our context we built up during the heap walk +// + +// static +void ETW::GCLog::EndMovedReferences(size_t profilingContext, + BOOL /*fAllowProfApiNotification*/) // @TODO: unused param from newer implementation +{ +#ifndef WINXP_AND_WIN2K3_BUILD_SUPPORT + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + CAN_TAKE_LOCK; + } + CONTRACTL_END; + + MovedReferenceContextForEtwAndProfapi * pCtxForEtwAndProfapi = (MovedReferenceContextForEtwAndProfapi *) profilingContext; + if (pCtxForEtwAndProfapi == NULL) + { + _ASSERTE(!"EndMovedReferences() encountered a NULL profilingContext"); + return; + } + +#ifdef PROFILING_SUPPORTED + // ProfAPI + { + BEGIN_PIN_PROFILER(CORProfilerTrackGC()); + g_profControlBlock.pProfInterface->EndMovedReferences(&(pCtxForEtwAndProfapi->pctxProfAPI)); + END_PIN_PROFILER(); + } +#endif //PROFILING_SUPPORTED + + // ETW + + if (!ShouldTrackMovementForEtw()) + return; + + // If context isn't already set up for us, then we haven't been collecting any data + // for ETW events. + EtwGcMovementContext * pContext = pCtxForEtwAndProfapi->pctxEtw; + if (pContext == NULL) + return; + + // Flush any remaining moved or surviving range data + + if (pContext->cBulkMovedObjectRanges > 0) + { + FireEtwGCBulkMovedObjectRanges( + pContext->iCurBulkMovedObjectRanges, + pContext->cBulkMovedObjectRanges, + GetClrInstanceId(), + sizeof(pContext->rgGCBulkMovedObjectRanges[0]), + &pContext->rgGCBulkMovedObjectRanges[0]); + } + + if (pContext->cBulkSurvivingObjectRanges > 0) + { + FireEtwGCBulkSurvivingObjectRanges( + pContext->iCurBulkSurvivingObjectRanges, + pContext->cBulkSurvivingObjectRanges, + GetClrInstanceId(), + sizeof(pContext->rgGCBulkSurvivingObjectRanges[0]), + &pContext->rgGCBulkSurvivingObjectRanges[0]); + } + + pCtxForEtwAndProfapi->pctxEtw = NULL; + delete pContext; +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT +} + +/***************************************************************************/ +/* This implements the public runtime provider's GCHeapCollectKeyword. It + performs a full, gen-2, blocking GC. +/***************************************************************************/ +void ETW::GCLog::ForceGC(LONGLONG l64ClientSequenceNumber) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + +#ifdef FEATURE_REDHAWK + if (!GCHeapUtilities::IsGCHeapInitialized()) + return; + + // No InterlockedExchange64 on Redhawk, even though there is one for + // InterlockedCompareExchange64. Technically, there's a race here by using + // InterlockedCompareExchange64, but it's not worth addressing. The race would be + // between two ETW controllers trying to trigger GCs simultaneously, in which case + // one will win and get its sequence number to appear in the GCStart event, while the + // other will lose. Rare, uninteresting, and low-impact. + PalInterlockedCompareExchange64(&s_l64LastClientSequenceNumber, l64ClientSequenceNumber, s_l64LastClientSequenceNumber); +#else // !FEATURE_REDHAWK + if (!IsGarbageCollectorFullyInitialized()) + return; + + InterlockedExchange64(&s_l64LastClientSequenceNumber, l64ClientSequenceNumber); +#endif // FEATURE_REDHAWK + + ForceGCForDiagnostics(); +} + +//--------------------------------------------------------------------------------------- +// +// Helper to fire the GCStart event. Figures out which version of GCStart to fire, and +// includes the client sequence number, if available. +// +// Arguments: +// pGcInfo - ETW_GC_INFO containing details from GC about this collection +// + +// static +void ETW::GCLog::FireGcStart(ETW_GC_INFO * pGcInfo) +{ + LIMITED_METHOD_CONTRACT; + +#if !defined(FEATURE_PAL) || defined(FEATURE_DTRACE) + + if (ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_GC_KEYWORD)) + { +#if !defined(FEATURE_PAL) + // If the controller specified a client sequence number for us to log with this + // GCStart, then retrieve it + LONGLONG l64ClientSequenceNumberToLog = 0; + if ((s_l64LastClientSequenceNumber != 0) && + (pGcInfo->GCStart.Depth == GCHeapUtilities::GetGCHeap()->GetMaxGeneration()) && + (pGcInfo->GCStart.Reason == ETW_GC_INFO::GC_INDUCED)) + { +#ifdef FEATURE_REDHAWK + // No InterlockedExchange64 on Redhawk (presumably b/c there is no compiler + // intrinsic for this on x86, even though there is one for InterlockedCompareExchange64) + l64ClientSequenceNumberToLog = PalInterlockedCompareExchange64(&s_l64LastClientSequenceNumber, 0, s_l64LastClientSequenceNumber); +#else + l64ClientSequenceNumberToLog = InterlockedExchange64(&s_l64LastClientSequenceNumber, 0); +#endif + } + + FireEtwGCStart_V2(pGcInfo->GCStart.Count, pGcInfo->GCStart.Depth, pGcInfo->GCStart.Reason, pGcInfo->GCStart.Type, GetClrInstanceId(), l64ClientSequenceNumberToLog); + +#elif defined(FEATURE_DTRACE) + FireEtwGCStart(pGcInfo->GCStart.Count,pGcInfo->GCStart.Reason); +#endif + } + +#endif // defined(FEATURE_PAL) || defined(FEATURE_DTRACE) +} + +//--------------------------------------------------------------------------------------- +// +// Contains code common to profapi and ETW scenarios where the profiler wants to force +// the CLR to perform a GC. The important work here is to create a managed thread for +// the current thread BEFORE the GC begins. On both ETW and profapi threads, there may +// not yet be a managed thread object. But some scenarios require a managed thread +// object be present (notably if we need to call into Jupiter during the GC). +// +// Return Value: +// HRESULT indicating success or failure +// +// Assumptions: +// Caller should ensure that the EE has fully started up and that the GC heap is +// initialized enough to actually perform a GC +// + +// static +HRESULT ETW::GCLog::ForceGCForDiagnostics() +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr = E_FAIL; + +#ifndef FEATURE_REDHAWK + // Caller should ensure we're past startup. + _ASSERTE(IsGarbageCollectorFullyInitialized()); + + // In immersive apps the GarbageCollect() call below will call into Jupiter, + // which will call back into the runtime to track references. This call + // chain would cause a Thread object to be created for this thread while code + // higher on the stack owns the ThreadStoreLock. This will lead to asserts + // since the ThreadStoreLock is non-reentrant. To avoid this we'll create + // the Thread object here instead. + if (GetThreadNULLOk() == NULL) + { + HRESULT hr = E_FAIL; + SetupThreadNoThrow(&hr); + if (FAILED(hr)) + return hr; + } + + ASSERT_NO_EE_LOCKS_HELD(); + + EX_TRY + { + // Need to switch to cooperative mode as the thread will access managed + // references (through Jupiter callbacks). + GCX_COOP(); + +#else // FEATURE_REDHAWK + _ASSERTE(GCHeapUtilities::IsGCHeapInitialized()); + + ThreadStore::AttachCurrentThread(); + Thread * pThread = ThreadStore::GetCurrentThread(); + + // Doing this prevents the GC from trying to walk this thread's stack for roots. + pThread->SetGCSpecial(true); + + // While doing the GC, much code assumes & asserts the thread doing the GC is in + // cooperative mode. + pThread->DisablePreemptiveMode(); +#endif // FEATURE_REDHAWK + + hr = GCHeapUtilities::GetGCHeap()->GarbageCollect( + -1, // all generations should be collected + FALSE, // low_memory_p + collection_blocking); + +#ifdef FEATURE_REDHAWK + // In case this thread (generated by the ETW OS APIs) hangs around a while, + // better stick it back into preemptive mode, so it doesn't block any other GCs + pThread->EnablePreemptiveMode(); +#else // !FEATURE_REDHAWK + } + EX_CATCH { } + EX_END_CATCH(RethrowCorruptingExceptions); +#endif // FEATURE_REDHAWK + + return hr; +} + + +//--------------------------------------------------------------------------------------- +// BulkTypeValue / BulkTypeEventLogger: These take care of batching up types so they can +// be logged via ETW in bulk +//--------------------------------------------------------------------------------------- + +BulkTypeValue::BulkTypeValue() : cTypeParameters(0), rgTypeParameters() +#ifdef FEATURE_REDHAWK +, ullSingleTypeParameter(0) +#else // FEATURE_REDHAWK +, sName() +#endif // FEATURE_REDHAWK +{ + LIMITED_METHOD_CONTRACT; + ZeroMemory(&fixedSizedData, sizeof(fixedSizedData)); +} + +//--------------------------------------------------------------------------------------- +// +// Clears a BulkTypeValue so it can be reused after the buffer is flushed to ETW +// + +void BulkTypeValue::Clear() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + ZeroMemory(&fixedSizedData, sizeof(fixedSizedData)); + cTypeParameters = 0; +#ifdef FEATURE_REDHAWK + ullSingleTypeParameter = 0; + rgTypeParameters.Release(); +#else // FEATURE_REDHAWK + sName.Clear(); + rgTypeParameters.Clear(); +#endif // FEATURE_REDHAWK +} + +//--------------------------------------------------------------------------------------- +// +// Fire an ETW event for all the types we batched so far, and then reset our state +// so we can start batching new types at the beginning of the array. +// +// + +void BulkTypeEventLogger::FireBulkTypeEvent() +{ +#ifndef WINXP_AND_WIN2K3_BUILD_SUPPORT + LIMITED_METHOD_CONTRACT; + + if (m_nBulkTypeValueCount == 0) + { + // No types were batched up, so nothing to send + return; + } + + // Normally, we'd use the MC-generated FireEtwBulkType for all this gunk, but + // it's insufficient as the bulk type event is too complex (arrays of structs of + // varying size). So we directly log the event via EventDataDescCreate and + // EventWrite + + // We use one descriptor for the count + one for the ClrInstanceID + 4 + // per batched type (to include fixed-size data + name + param count + param + // array). But the system limit of 128 descriptors per event kicks in way + // before the 64K event size limit, and we already limit our batch size + // (m_nBulkTypeValueCount) to stay within the 128 descriptor limit. + EVENT_DATA_DESCRIPTOR EventData[128]; + UINT16 nClrInstanceID = GetClrInstanceId(); + + UINT iDesc = 0; + + _ASSERTE(iDesc < _countof(EventData)); + EventDataDescCreate(&EventData[iDesc++], &m_nBulkTypeValueCount, sizeof(m_nBulkTypeValueCount)); + + _ASSERTE(iDesc < _countof(EventData)); + EventDataDescCreate(&EventData[iDesc++], &nClrInstanceID, sizeof(nClrInstanceID)); + + for (int iTypeData = 0; iTypeData < m_nBulkTypeValueCount; iTypeData++) + { + // Do fixed-size data as one bulk copy + _ASSERTE(iDesc < _countof(EventData)); + EventDataDescCreate( + &EventData[iDesc++], + &(m_rgBulkTypeValues[iTypeData].fixedSizedData), + sizeof(m_rgBulkTypeValues[iTypeData].fixedSizedData)); + + // Do var-sized data individually per field + + // Type name (nonexistent and thus empty on FEATURE_REDHAWK) + _ASSERTE(iDesc < _countof(EventData)); +#ifdef FEATURE_REDHAWK + EventDataDescCreate(&EventData[iDesc++], L"", sizeof(WCHAR)); +#else // FEATURE_REDHAWK + LPCWSTR wszName = m_rgBulkTypeValues[iTypeData].sName.GetUnicode(); + EventDataDescCreate( + &EventData[iDesc++], + (wszName == NULL) ? L"" : wszName, + (wszName == NULL) ? sizeof(WCHAR) : (m_rgBulkTypeValues[iTypeData].sName.GetCount() + 1) * sizeof(WCHAR)); +#endif // FEATURE_REDHAWK + + // Type parameter count +#ifndef FEATURE_REDHAWK + m_rgBulkTypeValues[iTypeData].cTypeParameters = m_rgBulkTypeValues[iTypeData].rgTypeParameters.GetCount(); +#endif // FEATURE_REDHAWK + _ASSERTE(iDesc < _countof(EventData)); + EventDataDescCreate( + &EventData[iDesc++], + &(m_rgBulkTypeValues[iTypeData].cTypeParameters), + sizeof(m_rgBulkTypeValues[iTypeData].cTypeParameters)); + + // Type parameter array + if (m_rgBulkTypeValues[iTypeData].cTypeParameters > 0) + { + _ASSERTE(iDesc < _countof(EventData)); + EventDataDescCreate( + &EventData[iDesc++], +#ifdef FEATURE_REDHAWK + ((m_rgBulkTypeValues[iTypeData].cTypeParameters == 1) ? + &(m_rgBulkTypeValues[iTypeData].ullSingleTypeParameter) : + (ULONGLONG *) (m_rgBulkTypeValues[iTypeData].rgTypeParameters)), +#else + m_rgBulkTypeValues[iTypeData].rgTypeParameters.GetElements(), +#endif + sizeof(ULONGLONG) * m_rgBulkTypeValues[iTypeData].cTypeParameters); + } + } + + Win32EventWrite(Microsoft_Windows_DotNETRuntimeHandle, &BulkType, iDesc, EventData); + + // Reset state + m_nBulkTypeValueCount = 0; + m_nBulkTypeValueByteCount = 0; +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT +} + +#ifndef FEATURE_REDHAWK + +//--------------------------------------------------------------------------------------- +// +// Batches a single type into the array, flushing the array to ETW if it fills up. Most +// interaction with the type system (to analyze the type) is done here. This does not +// recursively batch up any parameter types (for arrays or generics), but does add their +// TypeHandles to the rgTypeParameters array. LogTypeAndParameters is responsible for +// initiating any recursive calls to deal with type parameters. +// +// Arguments: +// th - TypeHandle to batch +// +// Return Value: +// Index into array of where this type got batched. -1 if there was a failure. +// + +int BulkTypeEventLogger::LogSingleType(TypeHandle th) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + CAN_TAKE_LOCK; // some of the type system stuff can take locks + } + CONTRACTL_END; + + // If there's no room for another type, flush what we've got + if (m_nBulkTypeValueCount == _countof(m_rgBulkTypeValues)) + { + FireBulkTypeEvent(); + } + + _ASSERTE(m_nBulkTypeValueCount < _countof(m_rgBulkTypeValues)); + + if (!th.IsTypeDesc() && th.GetMethodTable()->IsArray()) + { + _ASSERTE(!"BulkTypeEventLogger::LogSingleType called with MethodTable array"); + return -1; + } + + BulkTypeValue * pVal = &m_rgBulkTypeValues[m_nBulkTypeValueCount]; + + // Clear out pVal before filling it out (array elements can get reused if there + // are enough types that we need to flush to multiple events). Clearing the + // contained SBuffer can throw, so deal with exceptions + BOOL fSucceeded = FALSE; + EX_TRY + { + pVal->Clear(); + fSucceeded = TRUE; + } + EX_CATCH + { + fSucceeded = FALSE; + } + EX_END_CATCH(RethrowCorruptingExceptions); + if (!fSucceeded) + return -1; + + pVal->fixedSizedData.TypeID = (ULONGLONG) th.AsTAddr(); + pVal->fixedSizedData.ModuleID = (ULONGLONG) (TADDR) th.GetModule(); + pVal->fixedSizedData.TypeNameID = (th.GetMethodTable() == NULL) ? 0 : th.GetCl(); + pVal->fixedSizedData.Flags = 0; + pVal->fixedSizedData.CorElementType = (BYTE) th.GetInternalCorElementType(); + + if (th.IsArray()) + { + // Normal typedesc array + pVal->fixedSizedData.Flags |= kEtwTypeFlagsArray; + + // Fetch TypeHandle of array elements + fSucceeded = FALSE; + EX_TRY + { + pVal->rgTypeParameters.Append((ULONGLONG) th.AsArray()->GetArrayElementTypeHandle().AsTAddr()); + fSucceeded = TRUE; + } + EX_CATCH + { + fSucceeded = FALSE; + } + EX_END_CATCH(RethrowCorruptingExceptions); + if (!fSucceeded) + return -1; + } + else if (th.IsTypeDesc()) + { + // Non-array Typedescs + PTR_TypeDesc pTypeDesc = th.AsTypeDesc(); + if (pTypeDesc->HasTypeParam()) + { + fSucceeded = FALSE; + EX_TRY + { + pVal->rgTypeParameters.Append((ULONGLONG) pTypeDesc->GetTypeParam().AsTAddr()); + fSucceeded = TRUE; + } + EX_CATCH + { + fSucceeded = FALSE; + } + EX_END_CATCH(RethrowCorruptingExceptions); + if (!fSucceeded) + return -1; + } + } + else + { + // Non-array MethodTable + + PTR_MethodTable pMT = th.AsMethodTable(); + + // Make CorElementType more specific if this is a string MT + if (pMT->IsString()) + { + pVal->fixedSizedData.CorElementType = ELEMENT_TYPE_STRING; + } + else if (pMT->IsObjectClass()) + { + pVal->fixedSizedData.CorElementType = ELEMENT_TYPE_OBJECT; + } + + // Generic arguments + DWORD cTypeParameters = pMT->GetNumGenericArgs(); + if (cTypeParameters > 0) + { + Instantiation inst = pMT->GetInstantiation(); + fSucceeded = FALSE; + EX_TRY + { + for (DWORD i=0; i < cTypeParameters; i++) + { + pVal->rgTypeParameters.Append((ULONGLONG) inst[i].AsTAddr()); + } + fSucceeded = TRUE; + } + EX_CATCH + { + fSucceeded = FALSE; + } + EX_END_CATCH(RethrowCorruptingExceptions); + if (!fSucceeded) + return -1; + } + + if (pMT->HasFinalizer()) + { + pVal->fixedSizedData.Flags |= kEtwTypeFlagsFinalizable; + } + if (pMT->IsDelegate()) + { + pVal->fixedSizedData.Flags |= kEtwTypeFlagsDelegate; + } + if (pMT->IsComObjectType()) + { + pVal->fixedSizedData.Flags |= kEtwTypeFlagsExternallyImplementedCOMObject; + } + } + + // If the profiler wants it, construct a name. Always normalize the string (even if + // type names are not requested) so that calls to sName.GetCount() can't throw + EX_TRY + { + if (ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_GCHEAPANDTYPENAMES_KEYWORD)) + { + th.GetName(pVal->sName); + } + pVal->sName.Normalize(); + } + EX_CATCH + { + // If this failed, the name remains empty, which is ok; the event just + // won't have a name in it. + pVal->sName.Clear(); + } + EX_END_CATCH(RethrowCorruptingExceptions); + + // Now that we know the full size of this type's data, see if it fits in our + // batch or whether we need to flush + + int cbVal = pVal->GetByteCountInEvent(); + if (cbVal > kMaxBytesTypeValues) + { + // This type is apparently so huge, it's too big to squeeze into an event, even + // if it were the only type batched in the whole event. Bail + _ASSERTE(!"Type too big to log via ETW"); + return -1; + } + + if (m_nBulkTypeValueByteCount + cbVal > kMaxBytesTypeValues) + { + // Although this type fits into the array, its size is so big that the entire + // array can't be logged via ETW. So flush the array, and start over by + // calling ourselves--this refetches the type info and puts it at the + // beginning of the array. Since we know this type is small enough to be + // batched into an event on its own, this recursive call will not try to + // call itself again. + FireBulkTypeEvent(); + return LogSingleType(th); + } + + // The type fits into the batch, so update our state + m_nBulkTypeValueCount++; + m_nBulkTypeValueByteCount += cbVal; + return m_nBulkTypeValueCount - 1; // Index of type we just added +} + +void BulkTypeEventLogger::Cleanup() {} + +//--------------------------------------------------------------------------------------- +// +// High-level method to batch a type and (recursively) its type parameters, flushing to +// ETW as needed. This is called by (static) +// ETW::TypeSystemLog::LogTypeAndParametersIfNecessary, which is what clients use to log +// type events +// +// Arguments: +// * thAsAddr - Type to batch +// * typeLogBehavior - Reminder of whether the type system log lock is held +// (useful if we need to recurively call back into TypeSystemLog), and whether +// we even care to check if the type was already logged +// + +void BulkTypeEventLogger::LogTypeAndParameters(ULONGLONG thAsAddr, ETW::TypeSystemLog::TypeLogBehavior typeLogBehavior) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + CAN_TAKE_LOCK; // LogSingleType can take locks + } + CONTRACTL_END; + + TypeHandle th = TypeHandle::FromTAddr((TADDR) thAsAddr); + + // Batch up this type. This grabs useful info about the type, including any + // type parameters it may have, and sticks it in m_rgBulkTypeValues + int iBulkTypeEventData = LogSingleType(th); + if (iBulkTypeEventData == -1) + { + // There was a failure trying to log the type, so don't bother with its type + // parameters + return; + } + + // Look at the type info we just batched, so we can get the type parameters + BulkTypeValue * pVal = &m_rgBulkTypeValues[iBulkTypeEventData]; + + // We're about to recursively call ourselves for the type parameters, so make a + // local copy of their type handles first (else, as we log them we could flush + // and clear out m_rgBulkTypeValues, thus trashing pVal) + + StackSArray rgTypeParameters; + DWORD cParams = pVal->rgTypeParameters.GetCount(); + + BOOL fSucceeded = FALSE; + EX_TRY + { + for (COUNT_T i = 0; i < cParams; i++) + { + rgTypeParameters.Append(pVal->rgTypeParameters[i]); + } + fSucceeded = TRUE; + } + EX_CATCH + { + fSucceeded = FALSE; + } + EX_END_CATCH(RethrowCorruptingExceptions); + if (!fSucceeded) + return; + + // Before we recurse, adjust the special-cased type-log behavior that allows a + // top-level type to be logged without lookup, but still requires lookups to avoid + // dupes of type parameters + if (typeLogBehavior == ETW::TypeSystemLog::kTypeLogBehaviorAssumeLockAndAlwaysLogTopLevelType) + typeLogBehavior = ETW::TypeSystemLog::kTypeLogBehaviorAssumeLockAndLogIfFirstTime; + + // Recursively log any referenced parameter types + for (COUNT_T i=0; i < cParams; i++) + { + ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(this, rgTypeParameters[i], typeLogBehavior); + } +} + +#endif // FEATURE_REDHAWK + +// Holds state that batches of roots, nodes, edges, and types as the GC walks the heap +// at the end of a collection. +class EtwGcHeapDumpContext +{ +public: + // An instance of EtwGcHeapDumpContext is dynamically allocated and stored inside of + // ProfilingScanContext and ProfilerWalkHeapContext, which are context structures + // that the GC heap walker sends back to the callbacks. This method is passed a + // pointer to ProfilingScanContext::pvEtwContext or + // ProfilerWalkHeapContext::pvEtwContext; if non-NULL it gets returned; else, a new + // EtwGcHeapDumpContext is allocated, stored in that pointer, and then returned. + // Callers should test for NULL, which can be returned if out of memory + static EtwGcHeapDumpContext * GetOrCreateInGCContext(LPVOID * ppvEtwContext) + { + LIMITED_METHOD_CONTRACT; + + _ASSERTE(ppvEtwContext != NULL); + + EtwGcHeapDumpContext * pContext = (EtwGcHeapDumpContext *) *ppvEtwContext; + if (pContext == NULL) + { + pContext = new (nothrow) EtwGcHeapDumpContext; + *ppvEtwContext = pContext; + } + return pContext; + } + + EtwGcHeapDumpContext() : + iCurBulkRootEdge(0), + iCurBulkRootConditionalWeakTableElementEdge(0), + iCurBulkNodeEvent(0), + iCurBulkEdgeEvent(0), + bulkTypeEventLogger() + { + LIMITED_METHOD_CONTRACT; + ClearRootEdges(); + ClearRootConditionalWeakTableElementEdges(); + ClearNodes(); + ClearEdges(); + } + + // These helpers clear the individual buffers, for use after a flush and on + // construction. They intentionally leave the indices (iCur*) alone, since they + // persist across flushes within a GC + + void ClearRootEdges() + { + LIMITED_METHOD_CONTRACT; + cGcBulkRootEdges = 0; + ZeroMemory(rgGcBulkRootEdges, sizeof(rgGcBulkRootEdges)); + } + + void ClearRootConditionalWeakTableElementEdges() + { + LIMITED_METHOD_CONTRACT; + cGCBulkRootConditionalWeakTableElementEdges = 0; + ZeroMemory(rgGCBulkRootConditionalWeakTableElementEdges, sizeof(rgGCBulkRootConditionalWeakTableElementEdges)); + } + + void ClearNodes() + { + LIMITED_METHOD_CONTRACT; + cGcBulkNodeValues = 0; + ZeroMemory(rgGcBulkNodeValues, sizeof(rgGcBulkNodeValues)); + } + + void ClearEdges() + { + LIMITED_METHOD_CONTRACT; + cGcBulkEdgeValues = 0; + ZeroMemory(rgGcBulkEdgeValues, sizeof(rgGcBulkEdgeValues)); + } + + //--------------------------------------------------------------------------------------- + // GCBulkRootEdge + // + // A "root edge" is the relationship between a source "GCRootID" (i.e., stack + // variable, handle, static, etc.) and the target "RootedNodeAddress" (the managed + // object that gets rooted). + // + //--------------------------------------------------------------------------------------- + + // Sequence number for each GCBulkRootEdge event + UINT iCurBulkRootEdge; + + // Number of root edges currently filled out in rgGcBulkRootEdges array + UINT cGcBulkRootEdges; + + // Struct array containing the primary data for each GCBulkRootEdge event. Fix the size so + // the total event stays well below the 64K + // limit (leaving lots of room for non-struct fields that come before the root edge data) + EventStructGCBulkRootEdgeValue rgGcBulkRootEdges[(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkRootEdgeValue)]; + + + //--------------------------------------------------------------------------------------- + // GCBulkRootConditionalWeakTableElementEdge + // + // These describe dependent handles, which simulate an edge connecting a key NodeID + // to a value NodeID. + // + //--------------------------------------------------------------------------------------- + + // Sequence number for each GCBulkRootConditionalWeakTableElementEdge event + UINT iCurBulkRootConditionalWeakTableElementEdge; + + // Number of root edges currently filled out in rgGCBulkRootConditionalWeakTableElementEdges array + UINT cGCBulkRootConditionalWeakTableElementEdges; + + // Struct array containing the primary data for each GCBulkRootConditionalWeakTableElementEdge event. Fix the size so + // the total event stays well below the 64K + // limit (leaving lots of room for non-struct fields that come before the root edge data) + EventStructGCBulkRootConditionalWeakTableElementEdgeValue rgGCBulkRootConditionalWeakTableElementEdges + [(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkRootConditionalWeakTableElementEdgeValue)]; + + //--------------------------------------------------------------------------------------- + // GCBulkNode + // + // A "node" is ANY managed object sitting on the heap, including RootedNodeAddresses + // as well as leaf nodes. + // + //--------------------------------------------------------------------------------------- + + // Sequence number for each GCBulkNode event + UINT iCurBulkNodeEvent; + + // Number of nodes currently filled out in rgGcBulkNodeValues array + UINT cGcBulkNodeValues; + + // Struct array containing the primary data for each GCBulkNode event. Fix the size so + // the total event stays well below the 64K + // limit (leaving lots of room for non-struct fields that come before the node data) + EventStructGCBulkNodeValue rgGcBulkNodeValues[(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkNodeValue)]; + + //--------------------------------------------------------------------------------------- + // GCBulkEdge + // + // An "edge" is the relationship between a source node and its referenced target + // node. Edges are reported in bulk, separately from Nodes, but it is expected that + // the consumer read the Node and Edge streams together. One takes the first node + // from the Node stream, and then reads EdgeCount entries in the Edge stream, telling + // you all of that Node's targets. Then, one takes the next node in the Node stream, + // and reads the next entries in the Edge stream (using this Node's EdgeCount to + // determine how many) to find all of its targets. This continues on until the Node + // and Edge streams have been fully read. + // + // GCBulkRootEdges are not duplicated in the GCBulkEdge events. GCBulkEdge events + // begin at the GCBulkRootEdge.RootedNodeAddress and move forward. + // + //--------------------------------------------------------------------------------------- + + // Sequence number for each GCBulkEdge event + UINT iCurBulkEdgeEvent; + + // Number of nodes currently filled out in rgGcBulkEdgeValues array + UINT cGcBulkEdgeValues; + + // Struct array containing the primary data for each GCBulkEdge event. Fix the size so + // the total event stays well below the 64K + // limit (leaving lots of room for non-struct fields that come before the edge data) + EventStructGCBulkEdgeValue rgGcBulkEdgeValues[(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkEdgeValue)]; + + + //--------------------------------------------------------------------------------------- + // BulkType + // + // Types are a bit more complicated to batch up, since their data is of varying + // size. BulkTypeEventLogger takes care of the pesky details for us + //--------------------------------------------------------------------------------------- + + BulkTypeEventLogger bulkTypeEventLogger; +}; + + + +//--------------------------------------------------------------------------------------- +// +// Called during a heap walk for each root reference encountered. Batches up the root in +// the ETW context +// +// Arguments: +// * pvHandle - If the root is a handle, this points to the handle +// * pRootedNode - Points to object that is rooted +// * pSecondaryNodeForDependentHandle - For dependent handles, this is the +// secondary object +// * fDependentHandle - nonzero iff this is for a dependent handle +// * profilingScanContext - The shared profapi/etw context built up during the heap walk. +// * dwGCFlags - Bitmask of "GC_"-style flags set by GC +// * rootFlags - Bitmask of EtwGCRootFlags describing the root +// + +// static +void ETW::GCLog::RootReference( + LPVOID pvHandle, + Object * pRootedNode, + Object * pSecondaryNodeForDependentHandle, + BOOL fDependentHandle, + ProfilingScanContext * profilingScanContext, + DWORD dwGCFlags, + DWORD rootFlags) +{ +#ifndef WINXP_AND_WIN2K3_BUILD_SUPPORT + LIMITED_METHOD_CONTRACT; + + if (pRootedNode == NULL) + return; + + EtwGcHeapDumpContext * pContext = + EtwGcHeapDumpContext::GetOrCreateInGCContext(&profilingScanContext->pvEtwContext); + if (pContext == NULL) + return; + + // Determine root kind, root ID, and handle-specific flags + LPVOID pvRootID = NULL; + BYTE nRootKind = (BYTE) profilingScanContext->dwEtwRootKind; + switch (nRootKind) + { + case kEtwGCRootKindStack: +#ifndef FEATURE_REDHAWK + pvRootID = profilingScanContext->pMD; +#endif // !FEATURE_REDHAWK + break; + + case kEtwGCRootKindHandle: + pvRootID = pvHandle; + break; + + case kEtwGCRootKindFinalizer: + _ASSERTE(pvRootID == NULL); + break; + + case kEtwGCRootKindOther: + default: + _ASSERTE(nRootKind == kEtwGCRootKindOther); + _ASSERTE(pvRootID == NULL); + break; + } + + // Convert GC root flags to ETW root flags + if (dwGCFlags & GC_CALL_INTERIOR) + rootFlags |= kEtwGCRootFlagsInterior; + if (dwGCFlags & GC_CALL_PINNED) + rootFlags |= kEtwGCRootFlagsPinning; + + // Add root edge to appropriate buffer + if (fDependentHandle) + { + _ASSERTE(pContext->cGCBulkRootConditionalWeakTableElementEdges < + _countof(pContext->rgGCBulkRootConditionalWeakTableElementEdges)); + EventStructGCBulkRootConditionalWeakTableElementEdgeValue * pRCWTEEdgeValue = + &pContext->rgGCBulkRootConditionalWeakTableElementEdges[pContext->cGCBulkRootConditionalWeakTableElementEdges]; + pRCWTEEdgeValue->GCKeyNodeID = pRootedNode; + pRCWTEEdgeValue->GCValueNodeID = pSecondaryNodeForDependentHandle; + pRCWTEEdgeValue->GCRootID = pvRootID; + pContext->cGCBulkRootConditionalWeakTableElementEdges++; + + // If RCWTE edge buffer is now full, empty it into ETW + if (pContext->cGCBulkRootConditionalWeakTableElementEdges == + _countof(pContext->rgGCBulkRootConditionalWeakTableElementEdges)) + { + FireEtwGCBulkRootConditionalWeakTableElementEdge( + pContext->iCurBulkRootConditionalWeakTableElementEdge, + pContext->cGCBulkRootConditionalWeakTableElementEdges, + GetClrInstanceId(), + sizeof(pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]), + &pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]); + + pContext->iCurBulkRootConditionalWeakTableElementEdge++; + pContext->ClearRootConditionalWeakTableElementEdges(); + } + } + else + { + _ASSERTE(pContext->cGcBulkRootEdges < _countof(pContext->rgGcBulkRootEdges)); + EventStructGCBulkRootEdgeValue * pBulkRootEdgeValue = &pContext->rgGcBulkRootEdges[pContext->cGcBulkRootEdges]; + pBulkRootEdgeValue->RootedNodeAddress = pRootedNode; + pBulkRootEdgeValue->GCRootKind = nRootKind; + pBulkRootEdgeValue->GCRootFlag = rootFlags; + pBulkRootEdgeValue->GCRootID = pvRootID; + pContext->cGcBulkRootEdges++; + + // If root edge buffer is now full, empty it into ETW + if (pContext->cGcBulkRootEdges == _countof(pContext->rgGcBulkRootEdges)) + { + FireEtwGCBulkRootEdge( + pContext->iCurBulkRootEdge, + pContext->cGcBulkRootEdges, + GetClrInstanceId(), + sizeof(pContext->rgGcBulkRootEdges[0]), + &pContext->rgGcBulkRootEdges[0]); + + pContext->iCurBulkRootEdge++; + pContext->ClearRootEdges(); + } + } +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT +} + + +//--------------------------------------------------------------------------------------- +// +// Called during a heap walk for each object reference encountered. Batches up the +// corresponding node, edges, and type data for the ETW events. +// +// Arguments: +// * profilerWalkHeapContext - The shared profapi/etw context built up during the heap walk. +// * pObjReferenceSource - Object doing the pointing +// * typeID - Type of pObjReferenceSource +// * fDependentHandle - nonzero iff this is for a dependent handle +// * cRefs - Count of objects being pointed to +// * rgObjReferenceTargets - Array of objects being pointed to +// + +// static +void ETW::GCLog::ObjectReference( + ProfilerWalkHeapContext * profilerWalkHeapContext, + Object * pObjReferenceSource, + ULONGLONG typeID, + ULONGLONG cRefs, + Object ** rgObjReferenceTargets) +{ +#ifndef WINXP_AND_WIN2K3_BUILD_SUPPORT + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + + // LogTypeAndParametersIfNecessary can take a lock + CAN_TAKE_LOCK; + } + CONTRACTL_END; + + EtwGcHeapDumpContext * pContext = + EtwGcHeapDumpContext::GetOrCreateInGCContext(&profilerWalkHeapContext->pvEtwContext); + if (pContext == NULL) + return; + + //--------------------------------------------------------------------------------------- + // GCBulkNode events + //--------------------------------------------------------------------------------------- + + // Add Node (pObjReferenceSource) to buffer + _ASSERTE(pContext->cGcBulkNodeValues < _countof(pContext->rgGcBulkNodeValues)); + EventStructGCBulkNodeValue * pBulkNodeValue = &pContext->rgGcBulkNodeValues[pContext->cGcBulkNodeValues]; + pBulkNodeValue->Address = pObjReferenceSource; + pBulkNodeValue->Size = pObjReferenceSource->GetSize(); + pBulkNodeValue->TypeID = typeID; + pBulkNodeValue->EdgeCount = cRefs; + pContext->cGcBulkNodeValues++; + + // If Node buffer is now full, empty it into ETW + if (pContext->cGcBulkNodeValues == _countof(pContext->rgGcBulkNodeValues)) + { + FireEtwGCBulkNode( + pContext->iCurBulkNodeEvent, + pContext->cGcBulkNodeValues, + GetClrInstanceId(), + sizeof(pContext->rgGcBulkNodeValues[0]), + &pContext->rgGcBulkNodeValues[0]); + + pContext->iCurBulkNodeEvent++; + pContext->ClearNodes(); + } + + //--------------------------------------------------------------------------------------- + // BulkType events + //--------------------------------------------------------------------------------------- + + // We send type information as necessary--only for nodes, and only for nodes that we + // haven't already sent type info for + if (typeID != 0) + { + ETW::TypeSystemLog::LogTypeAndParametersIfNecessary( + &pContext->bulkTypeEventLogger, // Batch up this type with others to minimize events + typeID, + + // During heap walk, GC holds the lock for us, so we can directly enter the + // hash to see if the type has already been logged + ETW::TypeSystemLog::kTypeLogBehaviorAssumeLockAndLogIfFirstTime + ); + } + + //--------------------------------------------------------------------------------------- + // GCBulkEdge events + //--------------------------------------------------------------------------------------- + + // Add Edges (rgObjReferenceTargets) to buffer. Buffer could fill up before all edges + // are added (it could even fill up multiple times during this one call if there are + // a lot of edges), so empty Edge buffer into ETW as we go along, as many times as we + // need. + + for (ULONGLONG i=0; i < cRefs; i++) + { + _ASSERTE(pContext->cGcBulkEdgeValues < _countof(pContext->rgGcBulkEdgeValues)); + EventStructGCBulkEdgeValue * pBulkEdgeValue = &pContext->rgGcBulkEdgeValues[pContext->cGcBulkEdgeValues]; + pBulkEdgeValue->Value = rgObjReferenceTargets[i]; + // FUTURE: ReferencingFieldID + pBulkEdgeValue->ReferencingFieldID = 0; + pContext->cGcBulkEdgeValues++; + + // If Edge buffer is now full, empty it into ETW + if (pContext->cGcBulkEdgeValues == _countof(pContext->rgGcBulkEdgeValues)) + { + FireEtwGCBulkEdge( + pContext->iCurBulkEdgeEvent, + pContext->cGcBulkEdgeValues, + GetClrInstanceId(), + sizeof(pContext->rgGcBulkEdgeValues[0]), + &pContext->rgGcBulkEdgeValues[0]); + + pContext->iCurBulkEdgeEvent++; + pContext->ClearEdges(); + } + } +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT +} + +//--------------------------------------------------------------------------------------- +// +// Called by GC at end of heap dump to give us a convenient time to flush any remaining +// buffers of data to ETW +// +// Arguments: +// profilerWalkHeapContext - Context containing data we've batched up +// + +// static +void ETW::GCLog::EndHeapDump(ProfilerWalkHeapContext * profilerWalkHeapContext) +{ +#ifndef WINXP_AND_WIN2K3_BUILD_SUPPORT + LIMITED_METHOD_CONTRACT; + + // If context isn't already set up for us, then we haven't been collecting any data + // for ETW events. + EtwGcHeapDumpContext * pContext = (EtwGcHeapDumpContext *) profilerWalkHeapContext->pvEtwContext; + if (pContext == NULL) + return; + + // If the GC events are enabled, flush any remaining root, node, and / or edge data + if (ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_GCHEAPDUMP_KEYWORD)) + { + if (pContext->cGcBulkRootEdges > 0) + { + FireEtwGCBulkRootEdge( + pContext->iCurBulkRootEdge, + pContext->cGcBulkRootEdges, + GetClrInstanceId(), + sizeof(pContext->rgGcBulkRootEdges[0]), + &pContext->rgGcBulkRootEdges[0]); + } + + if (pContext->cGCBulkRootConditionalWeakTableElementEdges > 0) + { + FireEtwGCBulkRootConditionalWeakTableElementEdge( + pContext->iCurBulkRootConditionalWeakTableElementEdge, + pContext->cGCBulkRootConditionalWeakTableElementEdges, + GetClrInstanceId(), + sizeof(pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]), + &pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]); + } + + if (pContext->cGcBulkNodeValues > 0) + { + FireEtwGCBulkNode( + pContext->iCurBulkNodeEvent, + pContext->cGcBulkNodeValues, + GetClrInstanceId(), + sizeof(pContext->rgGcBulkNodeValues[0]), + &pContext->rgGcBulkNodeValues[0]); + } + + if (pContext->cGcBulkEdgeValues > 0) + { + FireEtwGCBulkEdge( + pContext->iCurBulkEdgeEvent, + pContext->cGcBulkEdgeValues, + GetClrInstanceId(), + sizeof(pContext->rgGcBulkEdgeValues[0]), + &pContext->rgGcBulkEdgeValues[0]); + } + } + + // Ditto for type events + if (ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_TYPE_KEYWORD)) + { + pContext->bulkTypeEventLogger.FireBulkTypeEvent(); + pContext->bulkTypeEventLogger.Cleanup(); + } + + // Delete any GC state built up in the context + profilerWalkHeapContext->pvEtwContext = NULL; + delete pContext; +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT +} + + +#ifndef FEATURE_REDHAWK + +//--------------------------------------------------------------------------------------- +// +// Helper to send public finalize object & type events, and private finalize object +// event. If Type events are enabled, this will send the Type event for the finalized +// objects. It will not be batched with other types (except type parameters, if any), +// and will not check if the Type has already been logged (may thus result in dupe +// logging of the Type). +// +// Arguments: +// pMT - MT of object getting finalized +// pObj - object getting finalized +// + +// static +void ETW::GCLog::SendFinalizeObjectEvent(MethodTable * pMT, Object * pObj) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + + // LogTypeAndParameters locks, and we take our own lock if typeLogBehavior says to + CAN_TAKE_LOCK; + } + CONTRACTL_END; + + // Send public finalize object event, if it's enabled + if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, FinalizeObject)) + { + FireEtwFinalizeObject(pMT, pObj, GetClrInstanceId()); + + // This function checks if type events are enabled; if so, it sends event for + // finalized object's type (and parameter types, if any) + ETW::TypeSystemLog::LogTypeAndParametersIfNecessary( + NULL, // Not batching this type with others + (TADDR) pMT, + + // Don't spend the time entering the lock and checking the hash table to see + // if we've already logged the type; just log it (if type events are enabled). + ETW::TypeSystemLog::kTypeLogBehaviorAlwaysLog + ); + } + + // Send private finalize object event, if it's enabled + if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, PrvFinalizeObject)) + { + EX_TRY + { + DefineFullyQualifiedNameForClassWOnStack(); + FireEtwPrvFinalizeObject(pMT, pObj, GetClrInstanceId(), GetFullyQualifiedNameForClassNestedAwareW(pMT)); + } + EX_CATCH + { + } + EX_END_CATCH(RethrowCorruptingExceptions); + } +} + +DWORD ETW::ThreadLog::GetEtwThreadFlags(Thread * pThread) +{ + LIMITED_METHOD_CONTRACT; + + DWORD dwEtwThreadFlags = 0; + + if (pThread->IsThreadPoolThread()) + { + dwEtwThreadFlags |= kEtwThreadFlagThreadPoolWorker; + } + if (pThread->IsGCSpecial()) + { + dwEtwThreadFlags |= kEtwThreadFlagGCSpecial; + } + if (IsGarbageCollectorFullyInitialized() && + (pThread == GCHeapUtilities::GetGCHeap()->GetFinalizerThread())) + { + dwEtwThreadFlags |= kEtwThreadFlagFinalizer; + } + + return dwEtwThreadFlags; +} + +void ETW::ThreadLog::FireThreadCreated(Thread * pThread) +{ + LIMITED_METHOD_CONTRACT; + + FireEtwThreadCreated( + (ULONGLONG)pThread, + (ULONGLONG)pThread->GetDomain(), + GetEtwThreadFlags(pThread), + pThread->GetThreadId(), + pThread->GetOSThreadId(), + GetClrInstanceId()); +} + +void ETW::ThreadLog::FireThreadDC(Thread * pThread) +{ + LIMITED_METHOD_CONTRACT; + + FireEtwThreadDC( + (ULONGLONG)pThread, + (ULONGLONG)pThread->GetDomain(), + GetEtwThreadFlags(pThread), + pThread->GetThreadId(), + pThread->GetOSThreadId(), + GetClrInstanceId()); +} + + + +// TypeSystemLog implementation +// +// We keep track of which TypeHandles have been logged, and stats on instances of these +// TypeHandles that have been allocated, by a hash table of hash tables. The outer hash +// table maps Module*'s to an inner hash table that contains all the TypeLoggingInfos for that +// Module*. Arranging things this way makes it easy to deal with Module unloads, as we +// can simply remove the corresponding inner hash table from the outer hash table. + +// The following help define the "inner" hash table: a hash table of TypeLoggingInfos +// from a particular Module (key = TypeHandle, value = TypeLoggingInfo. + +class LoggedTypesFromModuleTraits : public NoRemoveSHashTraits< DefaultSHashTraits > +{ +public: + + // explicitly declare local typedefs for these traits types, otherwise + // the compiler may get confused + typedef NoRemoveSHashTraits< DefaultSHashTraits > PARENT; + typedef PARENT::element_t element_t; + typedef PARENT::count_t count_t; + + typedef TypeHandle key_t; + + static key_t GetKey(const element_t &e) + { + LIMITED_METHOD_CONTRACT; + return e.th; + } + + static BOOL Equals(key_t k1, key_t k2) + { + LIMITED_METHOD_CONTRACT; + return (k1 == k2); + } + + static count_t Hash(key_t k) + { + LIMITED_METHOD_CONTRACT; + return (count_t) k.AsTAddr(); + } + + static bool IsNull(const element_t &e) + { + LIMITED_METHOD_CONTRACT; + return (e.th.AsTAddr() == NULL); + } + + static const element_t Null() + { + LIMITED_METHOD_CONTRACT; + return ETW::TypeLoggingInfo(NULL); + } +}; +typedef SHash LoggedTypesFromModuleHash; + +// The inner hash table is housed inside this class, which acts as an entry in the outer +// hash table. +class ETW::LoggedTypesFromModule +{ +public: + Module * pModule; + LoggedTypesFromModuleHash loggedTypesFromModuleHash; + + // These are used by the outer hash table (mapping Module*'s to instances of + // LoggedTypesFromModule). + static COUNT_T Hash(Module * pModule) + { + LIMITED_METHOD_CONTRACT; + return (COUNT_T) (SIZE_T) pModule; + } + Module * GetKey() + { + LIMITED_METHOD_CONTRACT; + return pModule; + } + + LoggedTypesFromModule(Module * pModuleParam) : loggedTypesFromModuleHash() + { + LIMITED_METHOD_CONTRACT; + pModule = pModuleParam; + } + + ~LoggedTypesFromModule() + { + LIMITED_METHOD_CONTRACT; + } +}; + +// The following define the outer hash table (mapping Module*'s to instances of +// LoggedTypesFromModule). + +class AllLoggedTypesTraits : public DefaultSHashTraits +{ +public: + + // explicitly declare local typedefs for these traits types, otherwise + // the compiler may get confused + typedef DefaultSHashTraits PARENT; + typedef PARENT::element_t element_t; + typedef PARENT::count_t count_t; + + typedef Module * key_t; + + static key_t GetKey(const element_t &e) + { + LIMITED_METHOD_CONTRACT; + return e->pModule; + } + + static BOOL Equals(key_t k1, key_t k2) + { + LIMITED_METHOD_CONTRACT; + return (k1 == k2); + } + + static count_t Hash(key_t k) + { + LIMITED_METHOD_CONTRACT; + return (count_t) (size_t) k; + } + + static bool IsNull(const element_t &e) + { + LIMITED_METHOD_CONTRACT; + return (e == NULL); + } + + static const element_t Null() + { + LIMITED_METHOD_CONTRACT; + return NULL; + } +}; + +typedef SHash AllLoggedTypesHash; + +// The outer hash table (mapping Module*'s to instances of LoggedTypesFromModule) is +// housed in this struct, which is dynamically allocated the first time we decide we need +// it. +struct AllLoggedTypes +{ +public: + // This Crst protects the entire outer & inner hash tables. On a GC heap walk, it + // is entered once for the duration of the walk, so that we can freely access the + // hash tables during the walk. On each object allocation, this Crst must be + // entered individually each time. + static CrstStatic s_cs; + + // The outer hash table (mapping Module*'s to instances of LoggedTypesFromModule) + AllLoggedTypesHash allLoggedTypesHash; +}; + + +CrstStatic AllLoggedTypes::s_cs; +AllLoggedTypes * ETW::TypeSystemLog::s_pAllLoggedTypes = NULL; +BOOL ETW::TypeSystemLog::s_fHeapAllocEventEnabledOnStartup = FALSE; +BOOL ETW::TypeSystemLog::s_fHeapAllocHighEventEnabledNow = FALSE; +BOOL ETW::TypeSystemLog::s_fHeapAllocLowEventEnabledNow = FALSE; +int ETW::TypeSystemLog::s_nCustomMsBetweenEvents = 0; + + +//--------------------------------------------------------------------------------------- +// +// Initializes TypeSystemLog (specifically its crst). Called just before ETW providers +// are registered with the OS +// +// Return Value: +// HRESULT indicating success or failure +// + +// static +HRESULT ETW::TypeSystemLog::PreRegistrationInit() +{ + LIMITED_METHOD_CONTRACT; + + if (!AllLoggedTypes::s_cs.InitNoThrow( + CrstEtwTypeLogHash, + CRST_UNSAFE_ANYMODE)) // This lock is taken during a GC while walking the heap + { + return E_FAIL; + } + + return S_OK; +} + +//--------------------------------------------------------------------------------------- +// +// Initializes TypeSystemLog (specifically its crst). Called just after ETW providers +// are registered with the OS +// +// Return Value: +// HRESULT indicating success or failure +// + +// static +void ETW::TypeSystemLog::PostRegistrationInit() +{ + LIMITED_METHOD_CONTRACT; + + // Initialize our "current state" BOOLs that remember if low or high allocation + // sampling is turned on + BOOL s_fHeapAllocLowEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCLOW_KEYWORD); + BOOL s_fHeapAllocHighEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCHIGH_KEYWORD); + + // Snapshot the current state of the object allocated keyword (on startup), and rely + // on this snapshot for the rest of the process run. Since these events require the + // slow alloc JIT helper to be enabled, and that can only be done on startup, we + // remember in this BOOL that we did so, so that we can prevent the object allocated + // event from being fired if the fast allocation helper were enabled but had to + // degrade down to the slow helper (e.g., thread ran over its allocation limit). This + // keeps things consistent. + s_fHeapAllocEventEnabledOnStartup = (s_fHeapAllocLowEventEnabledNow || s_fHeapAllocHighEventEnabledNow); + + if (s_fHeapAllocEventEnabledOnStartup) + { + // Determine if a COMPLUS env var is overriding the frequency for the sampled + // object allocated events + + // Config value intentionally typed as string, b/c DWORD intepretation is hard-coded + // to hex, which is not what the user would expect. This way I can force the + // conversion to use decimal. + NewArrayHolder wszCustomObjectAllocationEventsPerTypePerSec(NULL); + if (FAILED(CLRConfig::GetConfigValue( + CLRConfig::UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec, + &wszCustomObjectAllocationEventsPerTypePerSec)) || + (wszCustomObjectAllocationEventsPerTypePerSec == NULL)) + { + return; + } + LPWSTR endPtr; + DWORD dwCustomObjectAllocationEventsPerTypePerSec = wcstoul( + wszCustomObjectAllocationEventsPerTypePerSec, + &endPtr, + 10 // Base 10 conversion + ); + + if (dwCustomObjectAllocationEventsPerTypePerSec == ULONG_MAX) + dwCustomObjectAllocationEventsPerTypePerSec = 0; + if (dwCustomObjectAllocationEventsPerTypePerSec != 0) + { + // MsBetweenEvents = (1000 ms/sec) / (custom desired events/sec) + s_nCustomMsBetweenEvents = 1000 / dwCustomObjectAllocationEventsPerTypePerSec; + } + } +} + + +//--------------------------------------------------------------------------------------- +// +// Update object allocation sampling frequency and / or Type hash table contents based +// on what keywords were changed. +// + +// static +void ETW::TypeSystemLog::OnKeywordsChanged() +{ + LIMITED_METHOD_CONTRACT; + + // If the desired frequencey for the GCSampledObjectAllocation events has changed, + // update our state. + s_fHeapAllocLowEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCLOW_KEYWORD); + s_fHeapAllocHighEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCHIGH_KEYWORD); + + // FUTURE: Would be nice here to log an error event if (s_fHeapAllocLowEventEnabledNow || + // s_fHeapAllocHighEventEnabledNow), but !s_fHeapAllocEventEnabledOnStartup + + // If the type events should be turned off, eliminate the hash tables that tracked + // which types were logged. (If type events are turned back on later, we'll re-log + // them all as we encounter them.) Note that all we can really test for is that the + // Types keyword on the runtime provider is off. Not necessarily that it was on and + // was just turned off with this request. But either way, TypeSystemLog can handle it + // because it is extremely smart. + if (!ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_TYPE_KEYWORD)) + OnTypesKeywordTurnedOff(); +} + + +//--------------------------------------------------------------------------------------- +// +// Based on keywords alone, determine the what the default sampling rate should be for +// object allocation events. (This function does not consider any COMPLUS overrides for +// the sampling rate.) +// + +// static +int ETW::TypeSystemLog::GetDefaultMsBetweenEvents() +{ + LIMITED_METHOD_CONTRACT; + + // We should only get here if the allocation event is enabled. In spirit, this assert + // is correct, but a race could cause the assert to fire (if someone toggled the + // event off after we decided that the event was on and we started down the path of + // calculating statistics to fire the event). In such a case we'll end up returning + // k_nDefaultMsBetweenEventsLow below, but next time we won't get here as we'll know + // early enough not to fire the event. + //_ASSERTE(IsHeapAllocEventEnabled()); + + // MsBetweenEvents = (1000 ms/sec) / (desired events/sec) + const int k_nDefaultMsBetweenEventsHigh = 1000 / 100; // 100 events per type per sec + const int k_nDefaultMsBetweenEventsLow = 1000 / 5; // 5 events per type per sec + + // If both are set, High takes precedence + if (s_fHeapAllocHighEventEnabledNow) + { + return k_nDefaultMsBetweenEventsHigh; + } + return k_nDefaultMsBetweenEventsLow; +} + +//--------------------------------------------------------------------------------------- +// +// Use this to decide whether to fire the object allocation event +// +// Return Value: +// nonzero iff we should fire the event. +// + +// static +BOOL ETW::TypeSystemLog::IsHeapAllocEventEnabled() +{ + LIMITED_METHOD_CONTRACT; + + return + // Only fire the event if it was enabled at startup (and thus the slow-JIT new + // helper is used in all cases) + s_fHeapAllocEventEnabledOnStartup && + + // AND a keyword is still enabled. (Thus people can turn off the event + // whenever they want; but they cannot turn it on unless it was also on at startup.) + (s_fHeapAllocHighEventEnabledNow || s_fHeapAllocLowEventEnabledNow); +} + +//--------------------------------------------------------------------------------------- +// +// Helper that adds (or updates) the TypeLoggingInfo inside the inner hash table passed +// in. +// +// Arguments: +// * pLoggedTypesFromModule - Inner hash table to update +// * pTypeLoggingInfo - TypeLoggingInfo to store +// +// Return Value: +// nonzero iff the add/replace was successful. +// +// Assumptions: +// Caller must be holding the hash crst +// + +// static +BOOL ETW::TypeSystemLog::AddOrReplaceTypeLoggingInfo(ETW::LoggedTypesFromModule * pLoggedTypesFromModule, const ETW::TypeLoggingInfo * pTypeLoggingInfo) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE(GetHashCrst()->OwnedByCurrentThread()); + _ASSERTE(pLoggedTypesFromModule != NULL); + + BOOL fSucceeded = FALSE; + EX_TRY + { + pLoggedTypesFromModule->loggedTypesFromModuleHash.AddOrReplace(*pTypeLoggingInfo); + fSucceeded = TRUE; + } + EX_CATCH + { + fSucceeded = FALSE; + } + EX_END_CATCH(RethrowCorruptingExceptions); + + return fSucceeded; +} + +//--------------------------------------------------------------------------------------- +// +// Records stats about the object's allocation, and determines based on those stats whether +// to fires the high / low frequency GCSampledObjectAllocation ETW event +// +// Arguments: +// * pObject - Allocated object to log +// * th - TypeHandle for the object +// + +// static +void ETW::TypeSystemLog::SendObjectAllocatedEvent(Object * pObject) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + // No-op if the appropriate keywords were not enabled on startup (or we're not yet + // started up) + if (!s_fHeapAllocEventEnabledOnStartup || !g_fEEStarted) + return; + + TypeHandle th = pObject->GetTypeHandle(); + + SIZE_T size = pObject->GetSize(); + if(size < MIN_OBJECT_SIZE) + { + size = PtrAlign(size); + } + + SIZE_T nTotalSizeForTypeSample = size; + DWORD dwTickNow = GetTickCount(); + DWORD dwObjectCountForTypeSample = 0; + + // BLOCK: Hold the crst around the type stats hash table while we read and update + // the type's stats + { + CrstHolder _crst(GetHashCrst()); + + // Get stats for type + TypeLoggingInfo typeLoggingInfo(NULL); + LoggedTypesFromModule * pLoggedTypesFromModule = NULL; + BOOL fCreatedNew = FALSE; + typeLoggingInfo = LookupOrCreateTypeLoggingInfo(th, &fCreatedNew, &pLoggedTypesFromModule); + if (typeLoggingInfo.th.IsNull()) + return; + + // Update stats with current allocation + typeLoggingInfo.dwAllocsSkippedForSample++; + typeLoggingInfo.cbIgnoredSizeForSample += size; + + // This is our filter. If we should ignore this alloc, then record our updated + // our stats, and bail without sending the event. Note that we always log objects + // over 10K in size. + if (size < 10000 && typeLoggingInfo.dwAllocsSkippedForSample < typeLoggingInfo.dwAllocsToSkipPerSample) + { + // Update hash table's copy of type logging info with these values. Sucks that + // we're doing another hash table lookup here. Could instead have used LookupPtr() + // if it gave us back a non-const pointer, and then we could have updated in-place + AddOrReplaceTypeLoggingInfo(pLoggedTypesFromModule, &typeLoggingInfo); + if (fCreatedNew) + { + // Although we're skipping logging the allocation, we still need to log + // the type (so it's available for resolving future allocation events to + // their types). + // + // (See other call to LogTypeAndParametersIfNecessary further down for + // more comments.) + LogTypeAndParametersIfNecessary( + NULL, + th.AsTAddr(), + kTypeLogBehaviorAssumeLockAndAlwaysLogTopLevelType); + } + return; + } + + // Based on observed allocation stats, adjust our sampling rate for this type + + typeLoggingInfo.dwAllocCountInCurrentBucket += typeLoggingInfo.dwAllocsSkippedForSample; + int delta = (dwTickNow - typeLoggingInfo.dwTickOfCurrentTimeBucket) & 0x7FFFFFFF; // make wrap around work. + + int nMinAllocPerMSec = typeLoggingInfo.dwAllocCountInCurrentBucket / 16; // This is an underestimation of the true rate. + if (delta >= 16 || (nMinAllocPerMSec > 2 && nMinAllocPerMSec > typeLoggingInfo.flAllocPerMSec * 1.5F)) + { + float flNewAllocPerMSec = 0; + if (delta >= 16) + { + // This is the normal case, our allocation rate is under control with the current throttling. + flNewAllocPerMSec = ((float) typeLoggingInfo.dwAllocCountInCurrentBucket) / delta; + // Do a exponential decay window that is 5 * max(16, AllocationInterval) + typeLoggingInfo.flAllocPerMSec = 0.8F * typeLoggingInfo.flAllocPerMSec + 0.2F * flNewAllocPerMSec; + typeLoggingInfo.dwTickOfCurrentTimeBucket = dwTickNow; + typeLoggingInfo.dwAllocCountInCurrentBucket = 0; + } + else + { + flNewAllocPerMSec = (float) nMinAllocPerMSec; + // This means the second clause above is true, which means our sampling rate is too low + // so we need to throttle quickly. + typeLoggingInfo.flAllocPerMSec = flNewAllocPerMSec; + } + + + // Obey the desired sampling rate, but don't ignore > 1000 allocations per second + // per type + int nDesiredMsBetweenEvents = (s_nCustomMsBetweenEvents == 0) ? GetDefaultMsBetweenEvents() : s_nCustomMsBetweenEvents; + typeLoggingInfo.dwAllocsToSkipPerSample = min((int) (typeLoggingInfo.flAllocPerMSec * nDesiredMsBetweenEvents), 1000); + if (typeLoggingInfo.dwAllocsToSkipPerSample == 1) + typeLoggingInfo.dwAllocsToSkipPerSample = 0; + } + + // We're logging this sample, so save the values we need into locals, and reset + // our counts for the next sample. + nTotalSizeForTypeSample = typeLoggingInfo.cbIgnoredSizeForSample; + dwObjectCountForTypeSample = typeLoggingInfo.dwAllocsSkippedForSample; + typeLoggingInfo.cbIgnoredSizeForSample = 0; + typeLoggingInfo.dwAllocsSkippedForSample = 0; + + // Save updated stats into hash table + if (!AddOrReplaceTypeLoggingInfo(pLoggedTypesFromModule, &typeLoggingInfo)) + { + return; + } + + // While we're still holding the crst, optionally log any relevant Types now (we may need + // to reconsult the hash in here if there are any type parameters, though we can + // optimize and NOT consult the hash for th itself). + if (fCreatedNew) + { + // We were the ones to add the Type to the hash. So it wasn't there before, + // which means it hasn't been logged yet. + LogTypeAndParametersIfNecessary( + + // No BulkTypeEventLogger, as we're not batching during a GC heap walk + NULL, + + th.AsTAddr(), + + // We've determined the type is not yet logged, so no need to check + kTypeLogBehaviorAssumeLockAndAlwaysLogTopLevelType); + } + } // RELEASE: CrstHolder _crst(GetHashCrst()); + + // Now log the allocation + if (s_fHeapAllocHighEventEnabledNow) + { + FireEtwGCSampledObjectAllocationHigh(pObject, (LPVOID) th.AsTAddr(), dwObjectCountForTypeSample, nTotalSizeForTypeSample, GetClrInstanceId()); + } + else + { + FireEtwGCSampledObjectAllocationLow(pObject, (LPVOID) th.AsTAddr(), dwObjectCountForTypeSample, nTotalSizeForTypeSample, GetClrInstanceId()); + } +} + +//--------------------------------------------------------------------------------------- +// +// Accessor for hash table crst +// +// Return Value: +// hash table crst +// + +// static +CrstBase * ETW::TypeSystemLog::GetHashCrst() +{ + LIMITED_METHOD_CONTRACT; + return &AllLoggedTypes::s_cs; +} + +//--------------------------------------------------------------------------------------- +// +// Outermost level of ETW-type-logging. Clients outside eventtrace.cpp call this to log +// a TypeHandle and (recursively) its type parameters when present. This guy then calls +// into the appropriate BulkTypeEventLogger to do the batching and logging +// +// Arguments: +// * pBulkTypeEventLogger - If our caller is keeping track of batched types, it +// passes this to us so we can use it to batch the current type (GC heap walk +// does this). If this is NULL, no batching is going on (e.g., we're called on +// object allocation, not a GC heal walk), in which case we create our own +// temporary BulkTypeEventLogger. +// * thAsAddr - TypeHandle to batch +// * typeLogBehavior - Optimization to tell us we don't need to enter the +// TypeSystemLog's crst, as the TypeSystemLog's hash table is already protected +// by a prior acquisition of the crst by our caller. (Or that we don't even +// need to check the hash in the first place.) +// + +// static +void ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(BulkTypeEventLogger * pLogger, ULONGLONG thAsAddr, TypeLogBehavior typeLogBehavior) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + + // LogTypeAndParameters locks, and we take our own lock if typeLogBehavior says to + CAN_TAKE_LOCK; + } + CONTRACTL_END; + + if (!ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_TYPE_KEYWORD)) + { + return; + } + + TypeHandle th = TypeHandle::FromTAddr((TADDR) thAsAddr); + if (!th.IsRestored()) + { + return; + } + + // Check to see if we've already logged this type. If so, bail immediately. + // Otherwise, mark that it's getting logged (by adding it to the hash), and fall + // through to the logging code below. If caller doesn't care, then don't even + // check; just log the type + BOOL fShouldLogType = ((typeLogBehavior == kTypeLogBehaviorAlwaysLog) || + (typeLogBehavior == kTypeLogBehaviorAssumeLockAndAlwaysLogTopLevelType)) ? + TRUE : + ((typeLogBehavior == kTypeLogBehaviorTakeLockAndLogIfFirstTime) ? + ShouldLogType(th) : + ShouldLogTypeNoLock(th)); + if (!fShouldLogType) + return; + + if (pLogger == NULL) + { + // We're not batching this type against previous types (e.g., we're being called + // on object allocate instead of a GC heap walk). So create a temporary logger + // on the stack. If there are generic parameters that need to be logged, then + // at least they'll get batched together with the type + BulkTypeEventLogger logger; + logger.LogTypeAndParameters(thAsAddr, typeLogBehavior); + + // Since this logger isn't being used to batch anything else, flush what we have + logger.FireBulkTypeEvent(); + } + else + { + // We are batching this type with others (e.g., we're being called at the end of + // a GC on a heap walk). So use the logger our caller set up for us. + pLogger->LogTypeAndParameters(thAsAddr, typeLogBehavior); + } +} + + +//--------------------------------------------------------------------------------------- +// +// Same as code:ETW::TypeSystemLog::ShouldLogTypeNoLock but acquires the lock first. + +// static +BOOL ETW::TypeSystemLog::ShouldLogType(TypeHandle th) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + CAN_TAKE_LOCK; + } + CONTRACTL_END; + + CrstHolder _crst(GetHashCrst()); + return ShouldLogTypeNoLock(th); +} + + +//--------------------------------------------------------------------------------------- +// +// Ask hash table if we've already logged the type, without first acquiring the lock +// (our caller already did this). As a side-effect, a TypeLoggingInfo will be created +// for this type (so future calls to this function will return FALSE to avoid dupe type +// logging). +// +// Arguments: +// pth - TypeHandle to query +// +// Return Value: +// nonzero iff type should be logged (i.e., not previously logged) +// +// Assumptions: +// Caller must own the hash table's crst +// + +// static +BOOL ETW::TypeSystemLog::ShouldLogTypeNoLock(TypeHandle th) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE(GetHashCrst()->OwnedByCurrentThread()); + + // Check to see if TypeLoggingInfo exists yet for *pth. If not, creates one and + // adds it to the hash. + BOOL fCreatedNew = FALSE; + LookupOrCreateTypeLoggingInfo(th, &fCreatedNew); + + // Return whether we had to create the TypeLoggingInfo (indicating it was not yet in + // the hash, and thus that we hadn't yet logged the type). + return fCreatedNew; +} + + +//--------------------------------------------------------------------------------------- +// +// Helper that returns (creating if necessary) the TypeLoggingInfo in the hash table +// corresponding with the specified TypeHandle +// +// Arguments: +// * th - Key to lookup the TypeLoggingInfo +// * pfCreatedNew - [out] Points to nonzero iff a new TypeLoggingInfo was created +// (i.e., none existed yet in the hash for th). +// * ppLoggedTypesFromModule - [out] Points to the inner hash that was used to do +// the lookup. (An otpimization so the caller doesn't have to find this again, +// if it needs to do further operations on it.) +// +// Return Value: +// TypeLoggingInfo found or created. +// +// Assumptions: +// Hash crst must be held by caller +// + +// static +ETW::TypeLoggingInfo ETW::TypeSystemLog::LookupOrCreateTypeLoggingInfo(TypeHandle th, BOOL * pfCreatedNew, LoggedTypesFromModule ** ppLoggedTypesFromModule /* = NULL */) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE(pfCreatedNew != NULL); + _ASSERTE(GetHashCrst()->OwnedByCurrentThread()); + + if (ppLoggedTypesFromModule != NULL) + { + *ppLoggedTypesFromModule = NULL; + } + + BOOL fSucceeded = FALSE; + + if (s_pAllLoggedTypes == NULL) + { + s_pAllLoggedTypes = new (nothrow) AllLoggedTypes; + if (s_pAllLoggedTypes == NULL) + { + // out of memory. Bail on ETW stuff + *pfCreatedNew = FALSE; + return TypeLoggingInfo(NULL); + } + } + + // Step 1: go from LoaderModule to hash of types. + + Module * pLoaderModule = th.GetLoaderModule(); + _ASSERTE(pLoaderModule != NULL); + LoggedTypesFromModule * pLoggedTypesFromModule = s_pAllLoggedTypes->allLoggedTypesHash.Lookup(pLoaderModule); + if (pLoggedTypesFromModule == NULL) + { + pLoggedTypesFromModule = new (nothrow) LoggedTypesFromModule(pLoaderModule); + if (pLoggedTypesFromModule == NULL) + { + // out of memory. Bail on ETW stuff + *pfCreatedNew = FALSE; + return TypeLoggingInfo(NULL); + } + + fSucceeded = FALSE; + EX_TRY + { + s_pAllLoggedTypes->allLoggedTypesHash.Add(pLoggedTypesFromModule); + fSucceeded = TRUE; + } + EX_CATCH + { + fSucceeded = FALSE; + } + EX_END_CATCH(RethrowCorruptingExceptions); + if (!fSucceeded) + { + *pfCreatedNew = FALSE; + return TypeLoggingInfo(NULL); + } + } + + if (ppLoggedTypesFromModule != NULL) + { + *ppLoggedTypesFromModule = pLoggedTypesFromModule; + } + + // Step 2: From hash of types, see if our TypeHandle is there already + TypeLoggingInfo typeLoggingInfoPreexisting = pLoggedTypesFromModule->loggedTypesFromModuleHash.Lookup(th); + if (!typeLoggingInfoPreexisting.th.IsNull()) + { + // Type is already hashed, so it's already logged, so we don't need to + // log it again. + *pfCreatedNew = FALSE; + return typeLoggingInfoPreexisting; + } + + // We haven't logged this type, so we need to continue with this function to + // log it below. Add it to the hash table first so any recursive calls will + // see that this type is already being taken care of + fSucceeded = FALSE; + TypeLoggingInfo typeLoggingInfoNew(th); + EX_TRY + { + pLoggedTypesFromModule->loggedTypesFromModuleHash.Add(typeLoggingInfoNew); + fSucceeded = TRUE; + } + EX_CATCH + { + fSucceeded = FALSE; + } + EX_END_CATCH(RethrowCorruptingExceptions); + if (!fSucceeded) + { + *pfCreatedNew = FALSE; + return TypeLoggingInfo(NULL); + } + + *pfCreatedNew = TRUE; + return typeLoggingInfoNew; +} + + +//--------------------------------------------------------------------------------------- +// +// Called when we determine if a module was unloaded, so we can clear out that module's +// set of types from our hash table +// +// Arguments: +// pModule - Module getting unloaded +// + +// static +void ETW::TypeSystemLog::OnModuleUnload(Module * pModule) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + CAN_TAKE_LOCK; + } + CONTRACTL_END; + + if (!ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_TYPE_KEYWORD)) + { + return; + } + + LoggedTypesFromModule * pLoggedTypesFromModule = NULL; + + { + CrstHolder _crst(GetHashCrst()); + + if (s_pAllLoggedTypes == NULL) + return; + + // Is there a TypesHash for this module? + pLoggedTypesFromModule = s_pAllLoggedTypes->allLoggedTypesHash.Lookup(pModule); + if (pLoggedTypesFromModule == NULL) + return; + + // Remove TypesHash from master hash mapping modules to their TypesHash + s_pAllLoggedTypes->allLoggedTypesHash.Remove(pModule); + } + + // Destruct this TypesHash we just removed + delete pLoggedTypesFromModule; + pLoggedTypesFromModule = NULL; +} + +//--------------------------------------------------------------------------------------- +// +// Whenever we detect that the Types keyword is off, this gets called. This eliminates the +// hash tables that tracked which types were logged (if the hash tables had been created +// previously). If type events are turned back on later, we'll re-log them all as we +// encounter them. +// + +// static +void ETW::TypeSystemLog::OnTypesKeywordTurnedOff() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + CAN_TAKE_LOCK; + } + CONTRACTL_END; + + CrstHolder _crst(GetHashCrst()); + + if (s_pAllLoggedTypes == NULL) + return; + + // Destruct each of the per-module TypesHashes + AllLoggedTypesHash * pLoggedTypesHash = &s_pAllLoggedTypes->allLoggedTypesHash; + for (AllLoggedTypesHash::Iterator iter = pLoggedTypesHash->Begin(); + iter != pLoggedTypesHash->End(); + ++iter) + { + LoggedTypesFromModule * pLoggedTypesFromModule = *iter; + delete pLoggedTypesFromModule; + } + + // This causes the default ~AllLoggedTypes() to be called, and thus + // ~AllLoggedTypesHash() to be called + delete s_pAllLoggedTypes; + s_pAllLoggedTypes = NULL; +} + + +/****************************************************************************/ +/* Called when ETW is turned ON on an existing process and ModuleRange events are to + be fired */ +/****************************************************************************/ +void ETW::EnumerationLog::ModuleRangeRundown() +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + } CONTRACTL_END; + + EX_TRY + { + if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_PERFTRACK_PRIVATE_KEYWORD)) + { + ETW::EnumerationLog::EnumerationHelper(NULL, NULL, ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate); + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/****************************************************************************/ +/* Called when ETW is turned ON on an existing process */ +/****************************************************************************/ +void ETW::EnumerationLog::StartRundown() +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + } CONTRACTL_END; + + EX_TRY + { + BOOL bIsArmRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNAPPDOMAINRESOURCEMANAGEMENT_KEYWORD); + BOOL bIsPerfTrackRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNPERFTRACK_KEYWORD); + BOOL bIsThreadingRundownEnabled = ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNTHREADING_KEYWORD); + + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNJIT_KEYWORD) + || + ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNLOADER_KEYWORD) + || + IsRundownNgenKeywordEnabledAndNotSuppressed() + || + ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD) + || + bIsArmRundownEnabled + || + bIsPerfTrackRundownEnabled + || + bIsThreadingRundownEnabled) + { + // begin marker event will go to the rundown provider + FireEtwDCStartInit_V1(GetClrInstanceId()); + + // The rundown flag is expected to be checked in the caller, so no need to check here again + DWORD enumerationOptions=ETW::EnumerationLog::EnumerationStructs::None; + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNLOADER_KEYWORD)) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart; + } + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNJIT_KEYWORD)) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart; + } + if(IsRundownNgenKeywordEnabledAndNotSuppressed()) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart; + } + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD)) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::MethodDCStartILToNativeMap; + } + if(bIsPerfTrackRundownEnabled) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart; + } + + ETW::EnumerationLog::EnumerationHelper(NULL, NULL, enumerationOptions); + + if (bIsArmRundownEnabled) + { + // When an ETW event consumer asks for ARM rundown, that not only enables + // the ETW events, but also causes some minor behavioral changes in the + // CLR, such as gathering CPU usage baselines for each thread right now, + // and also gathering resource usage information later on (keyed off of + // g_fEnableARM, which we'll set right now). + EnableARM(); + } + + if (bIsArmRundownEnabled || bIsThreadingRundownEnabled) + { + SendThreadRundownEvent(); + } + + // end marker event will go to the rundown provider + FireEtwDCStartComplete_V1(GetClrInstanceId()); + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +//--------------------------------------------------------------------------------------- +// +// Simple helper to convert the currently active keywords on the runtime provider into a +// bitmask of enumeration options as defined in ETW::EnumerationLog::EnumerationStructs +// +// Return Value: +// ETW::EnumerationLog::EnumerationStructs bitmask corresponding to the currently +// active keywords on the runtime provider +// + +// static +DWORD ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords() +{ + LIMITED_METHOD_CONTRACT; + + DWORD enumerationOptions=ETW::EnumerationLog::EnumerationStructs::None; + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_LOADER_KEYWORD)) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload; + } + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_JIT_KEYWORD) && + ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_ENDENUMERATION_KEYWORD)) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::JitMethodUnload; + } + if(IsRuntimeNgenKeywordEnabledAndNotSuppressed() && + ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_ENDENUMERATION_KEYWORD)) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload; + } + + return enumerationOptions; +} + +//--------------------------------------------------------------------------------------- +// +// Executes a flavor of rundown initiated by a CAPTURE_STATE request to +// code:#EtwCallback. CAPTURE_STATE is the "ETW-sanctioned" way of performing a +// rundown, whereas the CLR's rundown provider was *our* version of this, implemented +// before CAPTURE_STATE was standardized. +// +// When doing a CAPTURE_STATE, the CLR rundown provider is completely unused. Instead, +// we pay attention to the runtime keywords active at the time the CAPTURE_STATE was +// requested, and enumerate through the appropriate objects (AppDomains, assemblies, +// modules, types, methods, threads) and send runtime events for each of them. +// +// CAPTURE_STATE is intended to be used primarily by PerfTrack. Implementing this form +// of rundown allows PerfTrack to be blissfully unaware of the CLR's rundown provider. +// + +// static +void ETW::EnumerationLog::EnumerateForCaptureState() +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + } + CONTRACTL_END; + + EX_TRY + { + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, KEYWORDZERO)) + { + DWORD enumerationOptions = GetEnumerationOptionsFromRuntimeKeywords(); + + // Send unload events for all remaining domains, including shared domain and + // default domain. + ETW::EnumerationLog::EnumerationHelper(NULL /* module filter */, NULL /* domain filter */, enumerationOptions); + + // Send thread created events for all currently active threads, if requested + if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_THREADING_KEYWORD)) + { + SendThreadRundownEvent(); + } + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/**************************************************************************************/ +/* Called when ETW is turned OFF on an existing process .Will be used by the controller for end rundown*/ +/**************************************************************************************/ +void ETW::EnumerationLog::EndRundown() +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + } CONTRACTL_END; + + EX_TRY + { + BOOL bIsPerfTrackRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNPERFTRACK_KEYWORD); + BOOL bIsThreadingRundownEnabled = ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNTHREADING_KEYWORD); + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNJIT_KEYWORD) + || + ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNLOADER_KEYWORD) + || + IsRundownNgenKeywordEnabledAndNotSuppressed() + || + ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD) + || + bIsPerfTrackRundownEnabled + || + bIsThreadingRundownEnabled + ) + { + // begin marker event will go to the rundown provider + FireEtwDCEndInit_V1(GetClrInstanceId()); + + // The rundown flag is expected to be checked in the caller, so no need to check here again + DWORD enumerationOptions=ETW::EnumerationLog::EnumerationStructs::None; + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNLOADER_KEYWORD)) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd; + } + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNJIT_KEYWORD)) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd; + } + if(IsRundownNgenKeywordEnabledAndNotSuppressed()) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd; + } + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD)) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::MethodDCEndILToNativeMap; + } + if(bIsPerfTrackRundownEnabled) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd; + } + + ETW::EnumerationLog::EnumerationHelper(NULL, NULL, enumerationOptions); + + if (bIsThreadingRundownEnabled) + { + SendThreadRundownEvent(); + } + + // end marker event will go to the rundown provider + FireEtwDCEndComplete_V1(GetClrInstanceId()); + } + } EX_CATCH { + STRESS_LOG1(LF_ALWAYS, LL_ERROR, "Exception during Rundown Enumeration, EIP of last AV = %p", g_LastAccessViolationEIP); + } EX_END_CATCH(SwallowAllExceptions); +} + +// #Registration +/*++ + +Routine Description: + + Registers provider with ETW tracing framework. + This function should not be called more than once, on + Dll Process attach only. + Not thread safe. + +Arguments: + none + +Return Value: + Returns the return value from RegisterTraceGuids or EventRegister. + +--*/ + +void InitializeEventTracing() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + // Do startup-only initialization of any state required by the ETW classes before + // events can be fired + HRESULT hr = ETW::TypeSystemLog::PreRegistrationInit(); + if (FAILED(hr)) + return; + + // Register CLR providers with the OS + if (g_pEtwTracer == NULL) + { + NewHolder tempEtwTracer (new (nothrow) ETW::CEtwTracer()); + if (tempEtwTracer != NULL && tempEtwTracer->Register () == ERROR_SUCCESS) + g_pEtwTracer = tempEtwTracer.Extract (); + } + + g_nClrInstanceId = GetRuntimeId() & 0x0000FFFF; // This will give us duplicate ClrInstanceId after UINT16_MAX + + // Any classes that need some initialization to happen after we've registered the + // providers can do so now + ETW::TypeSystemLog::PostRegistrationInit(); +} + +HRESULT ETW::CEtwTracer::Register() +{ + WRAPPER_NO_CONTRACT; + + OSVERSIONINFO osVer; + osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + if (GetOSVersion(&osVer) == FALSE) { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + else if (osVer.dwMajorVersion < ETW_SUPPORTED_MAJORVER) { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + // if running on OS < Longhorn, skip registration unless reg key is set + // since ETW reg is expensive (in both time and working set) on older OSes + if (osVer.dwMajorVersion < ETW_ENABLED_MAJORVER && !g_fEnableETW && !CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PreVistaETWEnabled)) + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + // If running on OS >= Longhorn, skip registration if ETW is not enabled + if (osVer.dwMajorVersion >= ETW_ENABLED_MAJORVER && !g_fEnableETW && !CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_VistaAndAboveETWEnabled)) + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + EventRegisterMicrosoft_Windows_DotNETRuntime(); + EventRegisterMicrosoft_Windows_DotNETRuntimePrivate(); + EventRegisterMicrosoft_Windows_DotNETRuntimeRundown(); + + // Stress Log ETW events are available only on the desktop version of the runtime +#ifndef FEATURE_CORECLR + EventRegisterMicrosoft_Windows_DotNETRuntimeStress(); +#endif // !FEATURE_CORECLR + + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimeHandle; + MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimePrivateHandle; + MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimeRundownHandle; +#ifndef FEATURE_CORECLR + MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimeStressHandle; +#endif // !FEATURE_CORECLR + + return S_OK; +} + +// #Unregistration +/*++ + +Routine Description: + Unregisters the provider from ETW. This function + should only be called once from DllMain Detach process. + Not thread safe. + +Arguments: + none + +Return Value: + Returns ERROR_SUCCESS + +--*/ +HRESULT ETW::CEtwTracer::UnRegister() +{ + LIMITED_METHOD_CONTRACT; + + EventUnregisterMicrosoft_Windows_DotNETRuntime(); + EventUnregisterMicrosoft_Windows_DotNETRuntimePrivate(); + EventUnregisterMicrosoft_Windows_DotNETRuntimeRundown(); +#ifndef FEATURE_CORECLR + EventUnregisterMicrosoft_Windows_DotNETRuntimeStress(); +#endif // !FEATURE_CORECLR + return S_OK; +} + +extern "C" +{ + ETW_INLINE + void EtwCallout(REGHANDLE RegHandle, + PCEVENT_DESCRIPTOR Descriptor, + ULONG ArgumentCount, + PEVENT_DATA_DESCRIPTOR EventData) + { + WRAPPER_NO_CONTRACT; + UINT8 providerIndex = 0; + if(RegHandle == Microsoft_Windows_DotNETRuntimeHandle) { + providerIndex = 0; + } else if(RegHandle == Microsoft_Windows_DotNETRuntimeRundownHandle) { + providerIndex = 1; + } else if(RegHandle == Microsoft_Windows_DotNETRuntimeStressHandle) { + providerIndex = 2; + } else if(RegHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) { + providerIndex = 3; + } else { + _ASSERTE(!"Provider not one of Runtime, Rundown, Private and Stress"); + return; + } + + // stacks are supposed to be fired for only the events with a bit set in the etwStackSupportedEvents bitmap + if(((etwStackSupportedEvents[providerIndex][Descriptor->Id/8]) & + (1<<(Descriptor->Id%8))) != 0) + { + if(RegHandle == Microsoft_Windows_DotNETRuntimeHandle) { + ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, &CLRStackWalk, &CLRStackId); + } else if(RegHandle == Microsoft_Windows_DotNETRuntimeRundownHandle) { + ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, &CLRStackWalkDCStart, &CLRStackRundownId); + } else if(RegHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) { + ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, &CLRStackWalkPrivate, &CLRStackPrivateId); + } else if(RegHandle == Microsoft_Windows_DotNETRuntimeStressHandle) { + ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_Context, &CLRStackWalkStress, &CLRStackStressId); + } + } + } +} + +extern "C" +{ + + // #EtwCallback: + // During the build, MC generates the code to register our provider, and to register + // our ETW callback. (This is buried under Intermediates, in a path like + // Intermediate\clr\corguids.nativeproj_1723354836\obj1c\x86\ClrEtwAll.h.) The ETW + // callback is also generated for us by MC. But we can hook into this generated + // callback by #defining MCGEN_PRIVATE_ENABLE_CALLBACK_V2 to be a call to this + // function (EtwCallback), thus causing EtwCallback to get called after the + // MC-generated code executes. + // + // This callback function is called whenever an ETW session is enabled or disabled. A + // callback function needs to be specified when the provider is registered. C style + // callback wrappers are needed during event registration. To handle the callback + // action in this class, we pass "this" during provider registration and modify the + // context to the relevant context in the C callback later. + ETW_INLINE + void EtwCallback( + _In_ LPCGUID SourceId, + _In_ ULONG ControlCode, + _In_ UCHAR Level, + _In_ ULONGLONG MatchAnyKeyword, + _In_ ULONGLONG MatchAllKeyword, + _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData, + _Inout_opt_ PVOID CallbackContext) + { + CONTRACTL { + NOTHROW; + if(g_fEEStarted) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}; + MODE_ANY; + CAN_TAKE_LOCK; + STATIC_CONTRACT_FAULT; + SO_NOT_MAINLINE; + } CONTRACTL_END; + + // Mark that we are the special ETWRundown thread. Currently all this does + // is insure that AVs thrown in this thread are treated as normal exceptions. + // This allows us to catch and swallow them. We can do this because we have + // a reasonably strong belief that doing ETW Rundown does not change runtime state + // and thus if an AV happens it is better to simply give up logging ETW and + // instead of terminating the process (which is what we would do normally) + ClrFlsThreadTypeSwitch etwRundownThreadHolder(ThreadType_ETWRundownThread); + PMCGEN_TRACE_CONTEXT context = (PMCGEN_TRACE_CONTEXT)CallbackContext; + + BOOLEAN bIsPublicTraceHandle = +#ifdef WINXP_AND_WIN2K3_BUILD_SUPPORT + McGenPreVista ? ((ULONGLONG)Microsoft_Windows_DotNETRuntimeHandle==(ULONGLONG)context) : +#endif + (context->RegistrationHandle==Microsoft_Windows_DotNETRuntimeHandle); + + BOOLEAN bIsPrivateTraceHandle = +#ifdef WINXP_AND_WIN2K3_BUILD_SUPPORT + McGenPreVista ? ((ULONGLONG)Microsoft_Windows_DotNETRuntimePrivateHandle==(ULONGLONG)context) : +#endif + (context->RegistrationHandle==Microsoft_Windows_DotNETRuntimePrivateHandle); + + BOOLEAN bIsRundownTraceHandle = +#ifdef WINXP_AND_WIN2K3_BUILD_SUPPORT + McGenPreVista ? ((ULONGLONG)Microsoft_Windows_DotNETRuntimeRundownHandle==(ULONGLONG)context) : +#endif + (context->RegistrationHandle==Microsoft_Windows_DotNETRuntimeRundownHandle); + + + // A manifest based provider can be enabled to multiple event tracing sessions + // As long as there is atleast 1 enabled session, IsEnabled will be TRUE + // Since classic providers can be enabled to only a single session, + // IsEnabled will be TRUE when it is enabled and FALSE when disabled + BOOL bEnabled = + ((ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER) || + (ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE)); + if(bEnabled) + { + // TypeSystemLog needs a notification when certain keywords are modified, so + // give it a hook here. + if (g_fEEStarted && !g_fEEShutDown && bIsPublicTraceHandle) + { + ETW::TypeSystemLog::OnKeywordsChanged(); + } + + if (bIsPrivateTraceHandle) + { + ETW::GCLog::GCSettingsEvent(); + if(g_fEEStarted && !g_fEEShutDown) + { + ETW::EnumerationLog::ModuleRangeRundown(); + } + } + +#ifdef _WIN64 // We only do this on 64 bit (NOT ARM, because ARM uses frame based stack crawling) + // If we have turned on the JIT keyword to the VERBOSE setting (needed to get JIT names) then + // we assume that we also want good stack traces so we need to publish unwind information so + // ETW can get at it + if(bIsPublicTraceHandle && ETW_CATEGORY_ENABLED((*context), TRACE_LEVEL_VERBOSE, CLR_RUNDOWNJIT_KEYWORD)) + UnwindInfoTable::PublishUnwindInfo(g_fEEStarted != FALSE); +#endif + if(g_fEEStarted && !g_fEEShutDown && bIsRundownTraceHandle) + { + // Fire the runtime information event + ETW::InfoLog::RuntimeInformation(ETW::InfoLog::InfoStructs::Callback); + + // Start and End Method/Module Rundowns + // Used to fire events that we missed since we started the controller after the process started + // flags for immediate start rundown + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNSTART_KEYWORD)) + ETW::EnumerationLog::StartRundown(); + + // flags delayed end rundown + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_RUNDOWNEND_KEYWORD)) + ETW::EnumerationLog::EndRundown(); + } + + if (g_fEEStarted && !g_fEEShutDown && (ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE)) + { + ETW::EnumerationLog::EnumerateForCaptureState(); + } + + // Special check for the runtime provider's GCHeapCollectKeyword. Profilers + // flick this to force a full GC. + if (g_fEEStarted && !g_fEEShutDown && bIsPublicTraceHandle && + ((MatchAnyKeyword & CLR_GCHEAPCOLLECT_KEYWORD) != 0)) + { + // Profilers may (optionally) specify extra data in the filter parameter + // to log with the GCStart event. + LONGLONG l64ClientSequenceNumber = 0; + if ((FilterData != NULL) && + (FilterData->Type == 1) && + (FilterData->Size == sizeof(l64ClientSequenceNumber))) + { + l64ClientSequenceNumber = *(LONGLONG *) (FilterData->Ptr); + } + ETW::GCLog::ForceGC(l64ClientSequenceNumber); + } + } +#ifdef FEATURE_COMINTEROP + if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, CCWRefCountChange)) + g_pConfig->SetLogCCWRefCountChangeEnabled(bEnabled != 0); +#endif // FEATURE_COMINTEROP + + } +} + +#endif // FEATURE_REDHAWK +#else // !FEATURE_DTRACE + +/**************************************************************************************/ +/* Helper data structure for supporting string in Dtrace probes. Since Dtrace does not support Unicode */ +/* in its printf API, we cast the unicode string to UFT8 string and then output them. */ +/**************************************************************************************/ +#define DTRACE_OUTPUT_STRING_LEN 512 +const CHAR szDtraceOutputNULL[]="NULL"; +INT32 WideCharToMultiByte(LPCWSTR wszSrcStr, LPSTR szDstStr); + +#include + +// The possible value of COMPlus_ETWEnabled should be '0' or '1' +#define SIZE_ETWEnabled 2 +// The possible value of COMPlus_EventInfo should be a string in the following format: +// GUID:HexNumfer:Level +// GUID: For example e13c0d23-ccbc-4e12-931b-d9cc2eee27e4 (36 bytes) +// HewNumber: 0xffffffff (10 bytes) +// Level: 0~9 (1 bytes) +// Therefore the length of it should be 36 + 1 + 10 + 1 + 1 + 1 = 50 +#define SIZE_EventInfo 50 + +ULONG ETW::CEtwTracer::Register() +{ + // Get Env Var COMPlus_ETWEnabled + char szETWEnabled[SIZE_ETWEnabled]; + DWORD newLen = GetEnvironmentVariableA("COMPlus_ETWEnabled", szETWEnabled, SIZE_ETWEnabled); + if (newLen == 0 || newLen >= SIZE_ETWEnabled || strcmp(szETWEnabled, "1") != 0) + return 0; + + // Get Env Var COMPlus_EventInfo + char szEventInfo[SIZE_EventInfo]; + newLen = GetEnvironmentVariableA("COMPlus_EventInfo", szEventInfo, SIZE_EventInfo); + if (newLen == 0 || newLen >= SIZE_EventInfo || strchr(szEventInfo, ' ') != NULL) + return 0; + + // Get Env Var COMPlus_EventLogFileName + char szEventLogFN[_MAX_FNAME]; + newLen = GetEnvironmentVariableA("COMPlus_EventLogFileName", szEventLogFN, _MAX_FNAME); + if (newLen == 0 || newLen >= _MAX_FNAME || strchr(szEventLogFN, '|') != NULL) + return 0; + char szEventLogFullPath[_MAX_PATH]; + newLen = GetFullPathNameA(szEventLogFN, _MAX_PATH, szEventLogFullPath, NULL); + if (newLen == 0 || newLen > _MAX_PATH || strchr(szEventLogFN, '|') != NULL) + return 0; + + // Get the process id which is ued in dtrace to fire the probes of the process + int nProcessId = GetCurrentProcessId(); + + // Start the log (By calling an PAL API to connect to a Unix Domain Server) + PAL_StartLog(szEventInfo, szEventLogFullPath, nProcessId); + + return 0; +} + +INT32 WideCharToMultiByte(LPCWSTR wszSrcStr, LPSTR szDstStr) +{ + INT32 nSize = WideCharToMultiByte(CP_UTF8, 0, wszSrcStr, -1, NULL, 0, NULL, NULL); + if (0 == nSize) + { + return 0; + } + if (nSize > DTRACE_OUTPUT_STRING_LEN-1) + { + nSize = DTRACE_OUTPUT_STRING_LEN-1; + } + INT32 nSize2 = WideCharToMultiByte(CP_UTF8, 0, wszSrcStr, -1, szDstStr, nSize, NULL, NULL); + if(nSize2 != nSize || nSize2 <=0 ) + { + return 0; + } + return nSize; +} + +void EEConfigSetup_V1() +{ + FireEtwEEConfigSetup_V1(GetClrInstanceId()); +} + +void EEConfigSetupEnd_V1() +{ + FireEtwEEConfigSetupEnd_V1(GetClrInstanceId()); +} + +void LdSysBases_V1() +{ + FireEtwLdSysBases_V1(GetClrInstanceId()); +} + +void LdSysBasesEnd_V1() +{ + FireEtwLdSysBasesEnd_V1(GetClrInstanceId()); +} + +void ExecExe_V1() +{ + FireEtwExecExe_V1(GetClrInstanceId()); +} + +void ExecExeEnd_V1() +{ + FireEtwExecExeEnd_V1(GetClrInstanceId()); +} + +void Main_V1() +{ + FireEtwMain_V1(GetClrInstanceId()); +} + +void MainEnd_V1() +{ + FireEtwMainEnd_V1(GetClrInstanceId()); +} + + +void ApplyPolicyStart_V1() +{ + FireEtwApplyPolicyStart_V1(GetClrInstanceId()); +} + +void ApplyPolicyEnd_V1() +{ + FireEtwApplyPolicyEnd_V1(GetClrInstanceId()); +} + +void PrestubWorker_V1() +{ + FireEtwPrestubWorker_V1(GetClrInstanceId()); +} + +void PrestubWorkerEnd_V1() +{ + FireEtwPrestubWorkerEnd_V1(GetClrInstanceId()); +} + +void ExplicitBindStart_V1() +{ + FireEtwExplicitBindStart_V1(GetClrInstanceId()); +} + +void ExplicitBindEnd_V1() +{ + FireEtwExplicitBindEnd_V1(GetClrInstanceId()); +} + +void ParseXml_V1() +{ + FireEtwParseXml_V1(GetClrInstanceId()); +} + +void ParseXmlEnd_V1() +{ + FireEtwParseXmlEnd_V1(GetClrInstanceId()); +} + +void InitDefaultDomain_V1() +{ + FireEtwInitDefaultDomain_V1(GetClrInstanceId()); +} + +void InitDefaultDomainEnd_V1() +{ + FireEtwInitDefaultDomainEnd_V1(GetClrInstanceId()); +} +void AllowBindingRedirs_V1() +{ + FireEtwAllowBindingRedirs_V1(GetClrInstanceId()); +} + +void AllowBindingRedirsEnd_V1() +{ + FireEtwAllowBindingRedirsEnd_V1(GetClrInstanceId()); +} + +void EEConfigSync_V1() +{ + FireEtwEEConfigSync_V1(GetClrInstanceId()); +} + +void EEConfigSyncEnd_V1() +{ + FireEtwEEConfigSyncEnd_V1(GetClrInstanceId()); +} + +void FusionBinding_V1() +{ + FireEtwFusionBinding_V1(GetClrInstanceId()); +} + +void FusionBindingEnd_V1() +{ + FireEtwFusionBindingEnd_V1(GetClrInstanceId()); +} + +void LoaderCatchCall_V1() +{ + FireEtwLoaderCatchCall_V1(GetClrInstanceId()); +} + +void LoaderCatchCallEnd_V1() +{ + FireEtwLoaderCatchCallEnd_V1(GetClrInstanceId()); +} + +void FusionInit_V1() +{ + FireEtwFusionInit_V1(GetClrInstanceId()); +} + +void FusionInitEnd_V1() +{ + FireEtwFusionInitEnd_V1(GetClrInstanceId()); +} + +void FusionAppCtx_V1() +{ + FireEtwFusionAppCtx_V1(GetClrInstanceId()); +} + +void FusionAppCtxEnd_V1() +{ + FireEtwFusionAppCtxEnd_V1(GetClrInstanceId()); +} + +void SecurityCatchCall_V1() +{ + FireEtwSecurityCatchCall_V1(GetClrInstanceId()); +} + +void SecurityCatchCallEnd_V1() +{ + FireEtwSecurityCatchCallEnd_V1(GetClrInstanceId()); +} + + +#endif // !FEATURE_DTRACE + +#ifndef FEATURE_REDHAWK + +/****************************************************************************/ +/* This is called by the runtime when an exception is thrown */ +/****************************************************************************/ +void ETW::ExceptionLog::ExceptionThrown(CrawlFrame *pCf, BOOL bIsReThrownException, BOOL bIsNewException) +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThread()->GetThrowable() != NULL); + } CONTRACTL_END; + + if(!(bIsReThrownException || bIsNewException)) + { + return; + } + if(!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionThrown_V1)) + { + return; + } + EX_TRY + { + SString exceptionType(L""); + LPWSTR exceptionMessage = NULL; + BOOL bIsCLSCompliant=FALSE, bIsCSE=FALSE, bIsNestedException=FALSE, bHasInnerException=FALSE; + UINT16 exceptionFlags=0; + PVOID exceptionEIP=0; + + Thread *pThread = GetThread(); + + struct + { + OBJECTREF exceptionObj; + OBJECTREF innerExceptionObj; + STRINGREF exceptionMessageRef; + } gc; + ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + + gc.exceptionObj = pThread->GetThrowable(); + gc.innerExceptionObj = ((EXCEPTIONREF)gc.exceptionObj)->GetInnerException(); + + ThreadExceptionState *pExState = pThread->GetExceptionState(); +#ifndef WIN64EXCEPTIONS + PTR_ExInfo pExInfo = NULL; +#else + PTR_ExceptionTracker pExInfo = NULL; +#endif //!WIN64EXCEPTIONS + pExInfo = pExState->GetCurrentExceptionTracker(); + _ASSERTE(pExInfo != NULL); + bIsNestedException = (pExInfo->GetPreviousExceptionTracker() != NULL); + bIsCSE = (pExInfo->GetCorruptionSeverity() == ProcessCorrupting); + bIsCLSCompliant = IsException((gc.exceptionObj)->GetMethodTable()) && + ((gc.exceptionObj)->GetMethodTable() != MscorlibBinder::GetException(kRuntimeWrappedException)); + + // A rethrown exception is also a nested exception + // but since we have a separate flag for it, lets unset the nested flag + if(bIsReThrownException) + { + bIsNestedException = FALSE; + } + bHasInnerException = (gc.innerExceptionObj) != NULL; + + exceptionFlags = ((bHasInnerException ? ETW::ExceptionLog::ExceptionStructs::HasInnerException : 0) | + (bIsNestedException ? ETW::ExceptionLog::ExceptionStructs::IsNestedException : 0) | + (bIsReThrownException ? ETW::ExceptionLog::ExceptionStructs::IsReThrownException : 0) | + (bIsCSE ? ETW::ExceptionLog::ExceptionStructs::IsCSE : 0) | + (bIsCLSCompliant ? ETW::ExceptionLog::ExceptionStructs::IsCLSCompliant : 0)); + + if (pCf->IsFrameless()) + { +#ifndef _WIN64 + exceptionEIP = (PVOID)pCf->GetRegisterSet()->ControlPC; +#else + exceptionEIP = (PVOID)GetIP(pCf->GetRegisterSet()->pContext); +#endif //!_WIN64 + } + else + { + exceptionEIP = (PVOID)(pCf->GetFrame()->GetIP()); + } + + // On platforms other than IA64, we are at the instruction after the faulting instruction + // This check has been copied from StackTraceInfo::AppendElement + if (!(pCf->HasFaulted() || pCf->IsIPadjusted()) && exceptionEIP != 0) + { + exceptionEIP = (PVOID)((UINT_PTR)exceptionEIP - 1); + } + + gc.exceptionMessageRef = ((EXCEPTIONREF)gc.exceptionObj)->GetMessage(); + TypeHandle exceptionTypeHandle = (gc.exceptionObj)->GetTypeHandle(); + exceptionTypeHandle.GetName(exceptionType); + WCHAR *exceptionTypeName = (WCHAR *)exceptionType.GetUnicode(); + + if(gc.exceptionMessageRef != NULL) + { + exceptionMessage = (gc.exceptionMessageRef)->GetBuffer(); + } + + HRESULT exceptionHRESULT = ((EXCEPTIONREF)gc.exceptionObj)->GetHResult(); + + FireEtwExceptionThrown_V1(exceptionTypeName, + exceptionMessage, + exceptionEIP, + exceptionHRESULT, + exceptionFlags, + GetClrInstanceId()); + GCPROTECT_END(); + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/****************************************************************************/ +/* This is called by the runtime when a domain is loaded */ +/****************************************************************************/ +void ETW::LoaderLog::DomainLoadReal(BaseDomain *pDomain, __in_opt LPWSTR wszFriendlyName) +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + } CONTRACTL_END; + + EX_TRY + { + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_LOADER_KEYWORD)) + { + DWORD dwEventOptions = ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad; + ETW::LoaderLog::SendDomainEvent(pDomain, dwEventOptions, wszFriendlyName); + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/****************************************************************************/ +/* This is called by the runtime when an AppDomain is unloaded */ +/****************************************************************************/ +void ETW::LoaderLog::DomainUnload(AppDomain *pDomain) +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + } CONTRACTL_END; + + EX_TRY + { + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + KEYWORDZERO)) + { + if(!pDomain->NoAccessToHandleTable()) + { + DWORD enumerationOptions = ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords(); + + // Domain unload also causes type unload events + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_TYPE_KEYWORD)) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::TypeUnload; + } + + ETW::EnumerationLog::EnumerationHelper(NULL, pDomain, enumerationOptions); + } + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/****************************************************************************/ +/* This is called by the runtime when a LoaderAllocator is unloaded */ +/****************************************************************************/ +void ETW::LoaderLog::CollectibleLoaderAllocatorUnload(AssemblyLoaderAllocator *pLoaderAllocator) +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + } CONTRACTL_END; + + EX_TRY + { + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + KEYWORDZERO)) + { + DWORD enumerationOptions = ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords(); + + // Collectible Loader Allocator unload also causes type unload events + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_TYPE_KEYWORD)) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::TypeUnload; + } + + ETW::EnumerationLog::IterateCollectibleLoaderAllocator(pLoaderAllocator, enumerationOptions); + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/****************************************************************************/ +/* This is called by the runtime when the runtime is loaded + Function gets called by both the Callback mechanism and regular ETW events. + Type is used to differentiate whether its a callback or a normal call*/ +/****************************************************************************/ +void ETW::InfoLog::RuntimeInformation(INT32 type) +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + } CONTRACTL_END; + + EX_TRY { + if((type == ETW::InfoLog::InfoStructs::Normal && ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, RuntimeInformationStart)) +#ifndef FEATURE_PAL + || + (type == ETW::InfoLog::InfoStructs::Callback && ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, RuntimeInformationDCStart)) +#endif //!FEATURE_PAL + ) + { +#ifndef FEATURE_DTRACE + PCWSTR szDtraceOutput1=L"",szDtraceOutput2=L""; +#else + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + CHAR szDtraceOutput2[DTRACE_OUTPUT_STRING_LEN]; +#endif // !FEATURE_DTRACE + UINT8 startupMode = 0; + UINT startupFlags = 0; + WCHAR dllPath[MAX_PATH+1] = {0}; + UINT8 Sku = 0; + _ASSERTE(g_fEEManagedEXEStartup || //CLR started due to a managed exe + g_fEEIJWStartup || //CLR started as a mixed mode Assembly + CLRHosted() || g_fEEHostedStartup || //CLR started through one of the Hosting API CLRHosted() returns true if CLR started through the V2 Interface while + // g_fEEHostedStartup is true if CLR is hosted through the V1 API. + g_fEEComActivatedStartup || //CLR started as a COM object + g_fEEOtherStartup ); //In case none of the 4 above mentioned cases are true for example ngen, ildasm then we asssume its a "other" startup + +#ifdef FEATURE_CORECLR + Sku = ETW::InfoLog::InfoStructs::CoreCLR; +#else + Sku = ETW::InfoLog::InfoStructs::DesktopCLR; +#endif //FEATURE_CORECLR + + //version info for clr.dll + USHORT vmMajorVersion = VER_MAJORVERSION; + USHORT vmMinorVersion = VER_MINORVERSION; + USHORT vmBuildVersion = VER_PRODUCTBUILD; + USHORT vmQfeVersion = VER_PRODUCTBUILD_QFE; + + //version info for mscorlib.dll + USHORT bclMajorVersion = VER_ASSEMBLYMAJORVERSION; + USHORT bclMinorVersion = VER_ASSEMBLYMINORVERSION; + USHORT bclBuildVersion = VER_ASSEMBLYBUILD; + USHORT bclQfeVersion = VER_ASSEMBLYBUILD_QFE; + +#ifndef FEATURE_PAL + LPCGUID comGUID=g_fEEComObjectGuid; +#else + unsigned int comGUID=0; +#endif //!FEATURE_PAL + +#ifndef FEATURE_DTRACE + LPWSTR lpwszCommandLine = L""; + LPWSTR lpwszRuntimeDllPath = (LPWSTR)dllPath; +#else + SIZE_T lpwszCommandLine = (SIZE_T)szDtraceOutput1; + SIZE_T lpwszRuntimeDllPath = (SIZE_T)szDtraceOutput2; +#endif //!FEATURE_DTRACE + +#ifndef FEATURE_CORECLR + startupFlags = CorHost2::GetStartupFlags(); +#endif //!FEATURE_CORECLR + + // Determine the startupmode + if(g_fEEIJWStartup) + { + //IJW Mode + startupMode = ETW::InfoLog::InfoStructs::IJW; + } + else if(g_fEEManagedEXEStartup) + { + //managed exe + startupMode = ETW::InfoLog::InfoStructs::ManagedExe; +#ifndef FEATURE_DTRACE + lpwszCommandLine = WszGetCommandLine(); +#else + INT32 nSize = WideCharToMultiByte(WszGetCommandLine(), szDtraceOutput1); + if(nSize > 0) { + lpwszCommandLine = (SIZE_T)szDtraceOutput1; + } +#endif //!FEATURE_DTRACE + } + else if (CLRHosted() || g_fEEHostedStartup) + { + //Hosted CLR + startupMode = ETW::InfoLog::InfoStructs::HostedCLR; + } + else if(g_fEEComActivatedStartup) + { + //com activated + startupMode = ETW::InfoLog::InfoStructs::COMActivated; + } + else if(g_fEEOtherStartup) + { + //startup type is other + startupMode = ETW::InfoLog::InfoStructs::Other; + } + + _ASSERTE (NumItems(dllPath) > MAX_PATH); + // if WszGetModuleFileName fails, we return an empty string + if (!WszGetModuleFileName(GetCLRModule(), dllPath, MAX_PATH)) { + dllPath[0] = 0; + } + dllPath[MAX_PATH] = 0; +#ifdef FEATURE_DTRACE + _ASSERTE (NumItems(szDtraceOutput2) >= NumItems(dllPath)); + INT32 nSize = WideCharToMultiByte(dllPath, szDtraceOutput2); + if(nSize > 0) { + lpwszRuntimeDllPath = (SIZE_T)szDtraceOutput2; + } +#endif // FEATURE_DTRACE + + if(type == ETW::InfoLog::InfoStructs::Callback) + { + FireEtwRuntimeInformationDCStart( GetClrInstanceId(), + Sku, + bclMajorVersion, + bclMinorVersion, + bclBuildVersion, + bclQfeVersion, + vmMajorVersion, + vmMinorVersion, + vmBuildVersion, + vmQfeVersion, + startupFlags, + startupMode, + lpwszCommandLine, + comGUID, + lpwszRuntimeDllPath ); + } + else + { + FireEtwRuntimeInformationStart( GetClrInstanceId(), + Sku, + bclMajorVersion, + bclMinorVersion, + bclBuildVersion, + bclQfeVersion, + vmMajorVersion, + vmMinorVersion, + vmBuildVersion, + vmQfeVersion, + startupFlags, + startupMode, + lpwszCommandLine, + comGUID, + lpwszRuntimeDllPath ); + } + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/*******************************************************/ +/* This is called by the runtime when a method is jitted completely */ +/*******************************************************/ +void ETW::MethodLog::MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature, SIZE_T pCode, ReJITID rejitID) +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + } CONTRACTL_END; + + EX_TRY + { + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_JIT_KEYWORD)) + { + ETW::MethodLog::SendMethodEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::JitMethodLoad, TRUE, namespaceOrClassName, methodName, methodSignature, pCode, rejitID); + } +#ifndef WINXP_AND_WIN2K3_BUILD_SUPPORT + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_JITTEDMETHODILTONATIVEMAP_KEYWORD)) + { + // The call to SendMethodILToNativeMapEvent assumes that the debugger's lazy + // data has already been initialized. + + // g_pDebugInterface is initialized on startup on desktop CLR, regardless of whether a debugger + // or profiler is loaded. So it should always be available. + _ASSERTE(g_pDebugInterface != NULL); + g_pDebugInterface->InitializeLazyDataIfNecessary(); + + ETW::MethodLog::SendMethodILToNativeMapEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::JitMethodILToNativeMap, rejitID); + } +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/*************************************************/ +/* This is called by the runtime when method jitting started */ +/*************************************************/ +void ETW::MethodLog::MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature) +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + PRECONDITION(pMethodDesc != NULL); + } CONTRACTL_END; + + EX_TRY + { + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_VERBOSE, + CLR_JIT_KEYWORD)) + { + pMethodDesc->GetMethodInfo(*namespaceOrClassName, *methodName, *methodSignature); + ETW::MethodLog::SendMethodJitStartEvent(pMethodDesc, namespaceOrClassName, methodName, methodSignature); + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/**********************************************************************/ +/* This is called by the runtime when a single jit helper method with stub is initialized */ +/**********************************************************************/ +void ETW::MethodLog::StubInitialized(ULONGLONG ullHelperStartAddress, LPCWSTR pHelperName) +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + PRECONDITION(ullHelperStartAddress != 0); + } CONTRACTL_END; + + EX_TRY + { + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_JIT_KEYWORD)) + { + DWORD dwHelperSize=0; + Stub::RecoverStubAndSize((TADDR)ullHelperStartAddress, &dwHelperSize); + ETW::MethodLog::SendHelperEvent(ullHelperStartAddress, dwHelperSize, pHelperName); + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/**********************************************************/ +/* This is called by the runtime when helpers with stubs are initialized */ +/**********************************************************/ +void ETW::MethodLog::StubsInitialized(PVOID *pHelperStartAddresss, PVOID *pHelperNames, LONG lNoOfHelpers) +{ + WRAPPER_NO_CONTRACT; + + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_JIT_KEYWORD)) + { + for(int i=0; iIsThunking()) +#endif + { + MethodTable::MethodIterator iter(pMethodTable); + for (; iter.IsValid(); iter.Next()) + { + MethodDesc *pMD = (MethodDesc *)(iter.GetMethodDesc()); + if(pMD && pMD->IsRestored() && pMD->GetMethodTable_NoLogging() == pMethodTable) + ETW::MethodLog::SendMethodEvent(pMD, ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad, FALSE); + } + } + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + + +/****************************************************************************/ +/* This is called by the runtime when a Strong Name Verification Starts */ +/****************************************************************************/ +void ETW::SecurityLog::StrongNameVerificationStart(DWORD dwInFlags, __in LPWSTR strFullyQualifiedAssemblyName) +{ + WRAPPER_NO_CONTRACT; +#ifndef FEATURE_CORECLR +#ifndef FEATURE_DTRACE + FireEtwStrongNameVerificationStart_V1(dwInFlags, 0, strFullyQualifiedAssemblyName, GetClrInstanceId()); +#else + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + // since DTrace do not support UNICODE string, they need to be converted to ANSI string + INT32 nSize = WideCharToMultiByte(strFullyQualifiedAssemblyName, szDtraceOutput1); + if (nSize != 0) + FireEtwStrongNameVerificationStart_V1(dwInFlags, 0, szDtraceOutput1, GetClrInstanceId()); +#endif +#endif // !FEATURE_CORECLR +} + + +/****************************************************************************/ +/* This is called by the runtime when a Strong Name Verification Ends */ +/****************************************************************************/ +void ETW::SecurityLog::StrongNameVerificationStop(DWORD dwInFlags,ULONG result, __in LPWSTR strFullyQualifiedAssemblyName) +{ + WRAPPER_NO_CONTRACT; +#ifndef FEATURE_CORECLR +#ifndef FEATURE_DTRACE + FireEtwStrongNameVerificationStop_V1(dwInFlags, result, strFullyQualifiedAssemblyName, GetClrInstanceId()); +#else + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + // since DTrace do not support UNICODE string, they need to be converted to ANSI string + INT32 nSize = WideCharToMultiByte(strFullyQualifiedAssemblyName, szDtraceOutput1); + if (nSize != 0) + FireEtwStrongNameVerificationStop_V1(dwInFlags, result, szDtraceOutput1, GetClrInstanceId()); +#endif +#endif // !FEATURE_CORECLR +} + +/****************************************************************************/ +/* This is called by the runtime when field transparency calculations begin */ +/****************************************************************************/ +void ETW::SecurityLog::FireFieldTransparencyComputationStart(LPCWSTR wszFieldName, + LPCWSTR wszModuleName, + DWORD dwAppDomain) +{ + WRAPPER_NO_CONTRACT; +#ifndef FEATURE_DTRACE + FireEtwFieldTransparencyComputationStart(wszFieldName, wszModuleName, dwAppDomain, GetClrInstanceId()); +#else // FEATURE_DTRACE + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + CHAR szDtraceOutput2[DTRACE_OUTPUT_STRING_LEN]; + // since DTrace does not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeField = WideCharToMultiByte(wszFieldName, szDtraceOutput1); + INT32 nSizeModule = WideCharToMultiByte(wszModuleName, szDtraceOutput2); + + if (nSizeField != 0 && nSizeModule != 0) + FireEtwFieldTransparencyComputationStart(szDtraceOutput1, szDtraceOutput2, dwAppDomain, GetClrInstanceId()); +#endif // !FEATURE_DTRACE +} + +/****************************************************************************/ +/* This is called by the runtime when field transparency calculations end */ +/****************************************************************************/ +void ETW::SecurityLog::FireFieldTransparencyComputationEnd(LPCWSTR wszFieldName, + LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsCritical, + BOOL fIsTreatAsSafe) +{ + WRAPPER_NO_CONTRACT; +#ifndef FEATURE_DTRACE + FireEtwFieldTransparencyComputationEnd(wszFieldName, wszModuleName, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId()); +#else // FEATURE_DTRACE + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + CHAR szDtraceOutput2[DTRACE_OUTPUT_STRING_LEN]; + // since DTrace does not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeField = WideCharToMultiByte(wszFieldName, szDtraceOutput1); + INT32 nSizeModule = WideCharToMultiByte(wszModuleName, szDtraceOutput2); + + if (nSizeField != 0 && nSizeModule != 0) + FireEtwFieldTransparencyComputationEnd(szDtraceOutput1, szDtraceOutput2, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId()); +#endif // !FEATURE_DTRACE +} + +/*****************************************************************************/ +/* This is called by the runtime when method transparency calculations begin */ +/*****************************************************************************/ +void ETW::SecurityLog::FireMethodTransparencyComputationStart(LPCWSTR wszMethodName, + LPCWSTR wszModuleName, + DWORD dwAppDomain) +{ + WRAPPER_NO_CONTRACT; +#ifndef FEATURE_DTRACE + FireEtwMethodTransparencyComputationStart(wszMethodName, wszModuleName, dwAppDomain, GetClrInstanceId()); +#else // FEATURE_DTRACE + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + CHAR szDtraceOutput2[DTRACE_OUTPUT_STRING_LEN]; + // since DTrace does not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeMethod = WideCharToMultiByte(wszMethodName, szDtraceOutput1); + INT32 nSizeModule = WideCharToMultiByte(wszModuleName, szDtraceOutput2); + + if (nSizeMethod != 0 && nSizeModule != 0) + FireEtwMethodTransparencyComputationStart(szDtraceOutput1, szDtraceOutput2, dwAppDomain, GetClrInstanceId()); +#endif // !FEATURE_DTRACE +} + +/*****************************************************************************/ +/* This is called by the runtime when method transparency calculations end */ +/********************************************(********************************/ +void ETW::SecurityLog::FireMethodTransparencyComputationEnd(LPCWSTR wszMethodName, + LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsCritical, + BOOL fIsTreatAsSafe) +{ + WRAPPER_NO_CONTRACT; +#ifndef FEATURE_DTRACE + FireEtwMethodTransparencyComputationEnd(wszMethodName, wszModuleName, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId()); +#else // FEATURE_DTRACE + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + CHAR szDtraceOutput2[DTRACE_OUTPUT_STRING_LEN]; + // since DTrace does not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeMethod = WideCharToMultiByte(wszMethodName, szDtraceOutput1); + INT32 nSizeModule = WideCharToMultiByte(wszModuleName, szDtraceOutput2); + + if (nSizeMethod != 0 && nSizeModule != 0) + FireEtwMethodTransparencyComputationEnd(szDtraceOutput1, szDtraceOutput2, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId()); +#endif // !FEATURE_DTRACE +} + +/*****************************************************************************/ +/* This is called by the runtime when module transparency calculations begin */ +/*****************************************************************************/ +void ETW::SecurityLog::FireModuleTransparencyComputationStart(LPCWSTR wszModuleName, + DWORD dwAppDomain) +{ + WRAPPER_NO_CONTRACT; +#ifndef FEATURE_DTRACE + FireEtwModuleTransparencyComputationStart(wszModuleName, dwAppDomain, GetClrInstanceId()); +#else // FEATURE_DTRACE + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + // since DTrace does not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeModule = WideCharToMultiByte(wszModuleName, szDtraceOutput1); + + if (nSizeModule != 0) + FireEtwModuleTransparencyComputationStart(szDtraceOutput1, dwAppDomain, GetClrInstanceId()); +#endif // !FEATURE_DTRACE +} + +/****************************************************************************/ +/* This is called by the runtime when module transparency calculations end */ +/****************************************************************************/ +void ETW::SecurityLog::FireModuleTransparencyComputationEnd(LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsAllCritical, + BOOL fIsAllTransparent, + BOOL fIsTreatAsSafe, + BOOL fIsOpportunisticallyCritical, + DWORD dwSecurityRuleSet) +{ + WRAPPER_NO_CONTRACT; +#ifndef FEATURE_DTRACE + FireEtwModuleTransparencyComputationEnd(wszModuleName, dwAppDomain, fIsAllCritical, fIsAllTransparent, fIsTreatAsSafe, fIsOpportunisticallyCritical, dwSecurityRuleSet, GetClrInstanceId()); +#else // FEATURE_DTRACE + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + // since DTrace does not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeModule = WideCharToMultiByte(wszModuleName, szDtraceOutput1); + + if (nSizeModule != 0) + FireEtwModuleTransparencyComputationEnd(szDtraceOutput1, dwAppDomain, fIsAllCritical, fIsAllTransparent, fIsTreatAsSafe, fIsOpportunisticallyCritical, dwSecurityRuleSet, GetClrInstanceId()); +#endif // !FEATURE_DTRACE +} + +/****************************************************************************/ +/* This is called by the runtime when token transparency calculations begin */ +/****************************************************************************/ +void ETW::SecurityLog::FireTokenTransparencyComputationStart(DWORD dwToken, + LPCWSTR wszModuleName, + DWORD dwAppDomain) +{ + WRAPPER_NO_CONTRACT; +#ifndef FEATURE_DTRACE + FireEtwTokenTransparencyComputationStart(dwToken, wszModuleName, dwAppDomain, GetClrInstanceId()); +#else // FEATURE_DTRACE + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + // since DTrace does not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeModule = WideCharToMultiByte(wszModuleName, szDtraceOutput1); + + if (nSizeModule != 0) + FireEtwTokenTransparencyComputationStart(dwToken, szDtraceOutput1, dwAppDomain, GetClrInstanceId()); +#endif // !FEATURE_DTRACE +} + +/****************************************************************************/ +/* This is called by the runtime when token transparency calculations end */ +/****************************************************************************/ +void ETW::SecurityLog::FireTokenTransparencyComputationEnd(DWORD dwToken, + LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsCritical, + BOOL fIsTreatAsSafe) +{ + WRAPPER_NO_CONTRACT; +#ifndef FEATURE_DTRACE + FireEtwTokenTransparencyComputationEnd(dwToken, wszModuleName, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId()); +#else // FEATURE_DTRACE + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + // since DTrace does not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeModule = WideCharToMultiByte(wszModuleName, szDtraceOutput1); + + if (nSizeModule != 0) + FireEtwTokenTransparencyComputationEnd(dwToken, szDtraceOutput1, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId()); +#endif // !FEATURE_DTRACE +} + +/*****************************************************************************/ +/* This is called by the runtime when type transparency calculations begin */ +/*****************************************************************************/ +void ETW::SecurityLog::FireTypeTransparencyComputationStart(LPCWSTR wszTypeName, + LPCWSTR wszModuleName, + DWORD dwAppDomain) +{ + WRAPPER_NO_CONTRACT; +#ifndef FEATURE_DTRACE + FireEtwTypeTransparencyComputationStart(wszTypeName, wszModuleName, dwAppDomain, GetClrInstanceId()); +#else // FEATURE_DTRACE + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + CHAR szDtraceOutput2[DTRACE_OUTPUT_STRING_LEN]; + // since DTrace does not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeType = WideCharToMultiByte(wszTypeName, szDtraceOutput1); + INT32 nSizeModule = WideCharToMultiByte(wszModuleName, szDtraceOutput2); + + if (nSizeType != 0 && nSizeModule != 0) + FireEtwTypeTransparencyComputationStart(szDtraceOutput1, szDtraceOutput2, dwAppDomain, GetClrInstanceId()); +#endif // !FEATURE_DTRACE +} + +/****************************************************************************/ +/* This is called by the runtime when type transparency calculations end */ +/****************************************************************************/ +void ETW::SecurityLog::FireTypeTransparencyComputationEnd(LPCWSTR wszTypeName, + LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsAllCritical, + BOOL fIsAllTransparent, + BOOL fIsCritical, + BOOL fIsTreatAsSafe) +{ + WRAPPER_NO_CONTRACT; +#ifndef FEATURE_DTRACE + FireEtwTypeTransparencyComputationEnd(wszTypeName, wszModuleName, dwAppDomain, fIsAllCritical, fIsAllTransparent, fIsCritical, fIsTreatAsSafe, GetClrInstanceId()); +#else // FEATURE_DTRACE + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + CHAR szDtraceOutput2[DTRACE_OUTPUT_STRING_LEN]; + // since DTrace does not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeType = WideCharToMultiByte(wszTypeName, szDtraceOutput1); + INT32 nSizeModule = WideCharToMultiByte(wszModuleName, szDtraceOutput2); + + if (nSizeType != 0 && nSizeModule != 0) + FireEtwTypeTransparencyComputationEnd(szDtraceOutput1, szDtraceOutput2, dwAppDomain, fIsAllCritical, fIsAllTransparent, fIsCritical, fIsTreatAsSafe, GetClrInstanceId()); +#endif // !FEATURE_DTRACE +} + +/**********************************************************************************/ +/* This is called by the runtime when a module is loaded */ +/* liReportedSharedModule will be 0 when this module is reported for the 1st time */ +/**********************************************************************************/ +void ETW::LoaderLog::ModuleLoad(Module *pModule, LONG liReportedSharedModule) +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + } CONTRACTL_END; + + EX_TRY + { + DWORD enumerationOptions = ETW::EnumerationLog::EnumerationStructs::None; + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + KEYWORDZERO)) + { + BOOL bTraceFlagLoaderSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_LOADER_KEYWORD); + BOOL bTraceFlagNgenMethodSet = IsRuntimeNgenKeywordEnabledAndNotSuppressed(); + BOOL bTraceFlagStartRundownSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_STARTENUMERATION_KEYWORD); + BOOL bTraceFlagPerfTrackSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_PERFTRACK_KEYWORD); + + if(liReportedSharedModule == 0) + { + + if(bTraceFlagLoaderSet) + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad; + if (bTraceFlagPerfTrackSet) + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad; + if(bTraceFlagNgenMethodSet && bTraceFlagStartRundownSet) + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad; + + if(pModule->IsManifest() && bTraceFlagLoaderSet) + ETW::LoaderLog::SendAssemblyEvent(pModule->GetAssembly(), enumerationOptions); + + if(bTraceFlagLoaderSet || bTraceFlagPerfTrackSet) + ETW::LoaderLog::SendModuleEvent(pModule, ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad | ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad); + + ETW::EnumerationLog::EnumerationHelper(pModule, NULL, enumerationOptions); + } + + // we want to report domainmodule events whenever they are loaded in any AppDomain + if(bTraceFlagLoaderSet) + ETW::LoaderLog::SendModuleEvent(pModule, ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad, TRUE); + } + +#if !defined(FEATURE_PAL) + { + BOOL bTraceFlagPerfTrackPrivateSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_PERFTRACK_PRIVATE_KEYWORD); + if (liReportedSharedModule == 0 && bTraceFlagPerfTrackPrivateSet) + { + enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate; + ETW::LoaderLog::SendModuleRange(pModule, enumerationOptions); + } + } +#endif + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/****************************************************************************/ +/* This is called by the runtime when the process is being shutdown */ +/****************************************************************************/ +void ETW::EnumerationLog::ProcessShutdown() +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + } CONTRACTL_END; + + EX_TRY + { + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, KEYWORDZERO)) + { + DWORD enumerationOptions = GetEnumerationOptionsFromRuntimeKeywords(); + + // Send unload events for all remaining domains, including shared domain and + // default domain. + ETW::EnumerationLog::EnumerationHelper(NULL /* module filter */, NULL /* domain filter */, enumerationOptions); + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/****************************************************************************/ +/****************************************************************************/ +/* Begining of helper functions */ +/****************************************************************************/ +/****************************************************************************/ + +/****************************************************************************/ +/* This routine is used to send a domain load/unload or rundown event */ +/****************************************************************************/ +void ETW::LoaderLog::SendDomainEvent(BaseDomain *pBaseDomain, DWORD dwEventOptions, LPCWSTR wszFriendlyName) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + } CONTRACTL_END; + + if(!pBaseDomain) + return; + +#ifndef FEATURE_DTRACE + PCWSTR szDtraceOutput1=L""; +#else + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; +#endif // !FEATURE_DTRACE + BOOL bIsDefaultDomain = pBaseDomain->IsDefaultDomain(); + BOOL bIsAppDomain = pBaseDomain->IsAppDomain(); + BOOL bIsExecutable = bIsAppDomain ? !(pBaseDomain->AsAppDomain()->IsPassiveDomain()) : FALSE; + BOOL bIsSharedDomain = pBaseDomain->IsSharedDomain(); + UINT32 uSharingPolicy = bIsAppDomain?(pBaseDomain->AsAppDomain()->GetSharePolicy()):0; + + ULONGLONG ullDomainId = (ULONGLONG)pBaseDomain; + ULONG ulDomainFlags = ((bIsDefaultDomain ? ETW::LoaderLog::LoaderStructs::DefaultDomain : 0) | + (bIsExecutable ? ETW::LoaderLog::LoaderStructs::ExecutableDomain : 0) | + (bIsSharedDomain ? ETW::LoaderLog::LoaderStructs::SharedDomain : 0) | + (uSharingPolicy<<28)); + + LPCWSTR wsEmptyString = L""; + LPCWSTR wsSharedString = L"SharedDomain"; + + LPWSTR lpswzDomainName = (LPWSTR)wsEmptyString; + + if(bIsAppDomain) + { + if(wszFriendlyName) + lpswzDomainName = (PWCHAR)wszFriendlyName; + else + lpswzDomainName = (PWCHAR)pBaseDomain->AsAppDomain()->GetFriendlyName(); + } + else + lpswzDomainName = (LPWSTR)wsSharedString; + + /* prepare events args for ETW and ETM */ +#ifndef FEATURE_DTRACE + szDtraceOutput1 = (PCWSTR)lpswzDomainName; +#else // !FEATURE_DTRACE + // since DTrace do not support UNICODE string, they need to be converted to ANSI string + INT32 nSize = WideCharToMultiByte(lpswzDomainName, szDtraceOutput1); + if (nSize == 0) + return; +#endif // !FEATURE_DTRACE + + if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) + { + FireEtwAppDomainLoad_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, pBaseDomain->GetId().m_dwId, GetClrInstanceId()); + } + else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) + { + FireEtwAppDomainUnload_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, pBaseDomain->GetId().m_dwId, GetClrInstanceId()); + } + else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) + { + FireEtwAppDomainDCStart_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, pBaseDomain->GetId().m_dwId, GetClrInstanceId()); + } + else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) + { + FireEtwAppDomainDCEnd_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, pBaseDomain->GetId().m_dwId, GetClrInstanceId()); + } + else + { + _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)); + } +} + +/********************************************************/ +/* This routine is used to send thread rundown events when ARM is enabled */ +/********************************************************/ +void ETW::EnumerationLog::SendThreadRundownEvent() +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + } CONTRACTL_END; + +#ifndef DACCESS_COMPILE + Thread *pThread = NULL; + + // Take the thread store lock while we enumerate threads. + ThreadStoreLockHolder tsl; + while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL) + { + if (pThread->IsUnstarted() || pThread->IsDead()) + continue; + + // Send thread rundown provider events and thread created runtime provider + // events (depending on which are enabled) + ThreadLog::FireThreadDC(pThread); + ThreadLog::FireThreadCreated(pThread); + } +#endif // !DACCESS_COMPILE +} + +/****************************************************************************/ +/* This routine is used to send an assembly load/unload or rundown event ****/ +/****************************************************************************/ +void ETW::LoaderLog::SendAssemblyEvent(Assembly *pAssembly, DWORD dwEventOptions) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + } CONTRACTL_END; + + if(!pAssembly) + return; + +#ifndef FEATURE_DTRACE + PCWSTR szDtraceOutput1=L""; +#else + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; +#endif // !FEATURE_DTRACE + BOOL bIsDynamicAssembly = pAssembly->IsDynamic(); + BOOL bIsCollectibleAssembly = pAssembly->IsCollectible(); + BOOL bIsDomainNeutral = pAssembly->IsDomainNeutral() ; + BOOL bHasNativeImage = pAssembly->GetManifestFile()->HasNativeImage(); + + ULONGLONG ullAssemblyId = (ULONGLONG)pAssembly; + ULONGLONG ullDomainId = (ULONGLONG)pAssembly->GetDomain(); + ULONGLONG ullBindingID = 0; +#if (defined FEATURE_PREJIT) && (defined FEATURE_FUSION_DEPRECATE) + ullBindingID = pAssembly->GetManifestFile()->GetBindingID(); +#endif + ULONG ulAssemblyFlags = ((bIsDomainNeutral ? ETW::LoaderLog::LoaderStructs::DomainNeutralAssembly : 0) | + (bIsDynamicAssembly ? ETW::LoaderLog::LoaderStructs::DynamicAssembly : 0) | + (bHasNativeImage ? ETW::LoaderLog::LoaderStructs::NativeAssembly : 0) | + (bIsCollectibleAssembly ? ETW::LoaderLog::LoaderStructs::CollectibleAssembly : 0)); + + SString sAssemblyPath; + pAssembly->GetDisplayName(sAssemblyPath); + LPWSTR lpszAssemblyPath = (LPWSTR)sAssemblyPath.GetUnicode(); + +/* prepare events args for ETW and ETM */ +#ifndef FEATURE_DTRACE + szDtraceOutput1 = (PCWSTR)lpszAssemblyPath; +#else // !FEATURE_DTRACE + // since DTrace do not support UNICODE string, they need to be converted to ANSI string + INT32 nSize = WideCharToMultiByte(lpszAssemblyPath, szDtraceOutput1); + if (nSize == 0) + return; +#endif // !FEATURE_DTRACE + + if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) + { + FireEtwAssemblyLoad_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId()); + } + else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) + { + FireEtwAssemblyUnload_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId()); + } + else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) + { + FireEtwAssemblyDCStart_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId()); + } + else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) + { + FireEtwAssemblyDCEnd_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId()); + } + else + { + _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)); + } +} + +#if !defined(FEATURE_PAL) +ETW_INLINE + ULONG + ETW::LoaderLog::SendModuleRange( + __in Module *pModule, + __in DWORD dwEventOptions) + +{ + ULONG Result = ERROR_SUCCESS; + + + // do not fire the ETW event when: + // 1. We did not load the native image + // 2. We do not have IBC data for the native image + if( !pModule || !pModule->HasNativeImage() || !pModule->IsIbcOptimized() ) + { + return Result; + } + + // get information about the hot sections from the native image that has been loaded + COUNT_T cbSizeOfSectionTable; + CORCOMPILE_VIRTUAL_SECTION_INFO* pVirtualSectionsTable = (CORCOMPILE_VIRTUAL_SECTION_INFO* )pModule->GetNativeImage()->GetVirtualSectionsTable(&cbSizeOfSectionTable); + + COUNT_T RangeCount = cbSizeOfSectionTable/sizeof(CORCOMPILE_VIRTUAL_SECTION_INFO); + + // if we do not have any hot ranges, we do not fire the ETW event + + // Figure out the rest of the event data + UINT16 ClrInstanceId = GetClrInstanceId(); + UINT64 ModuleID = (ULONGLONG)(TADDR) pModule; + + for (COUNT_T i = 0; i < RangeCount; ++i) + { + DWORD rangeBegin = pVirtualSectionsTable[i].VirtualAddress; + DWORD rangeSize = pVirtualSectionsTable[i].Size; + DWORD sectionType = pVirtualSectionsTable[i].SectionType; + + UINT8 ibcType = VirtualSectionData::IBCType(sectionType); + UINT8 rangeType = VirtualSectionData::RangeType(sectionType); + UINT16 virtualSectionType = VirtualSectionData::VirtualSectionType(sectionType); + BOOL isIBCProfiledColdSection = VirtualSectionData::IsIBCProfiledColdSection(sectionType); + if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad) + { + if (isIBCProfiledColdSection) + Result &= FireEtwModuleRangeLoad(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType); + } + else if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart) + { + if (isIBCProfiledColdSection) + Result &= FireEtwModuleRangeDCStart(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType); + } + else if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd) + { + if (isIBCProfiledColdSection) + Result &= FireEtwModuleRangeDCEnd(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType); + } + // Fire private events if they are requested. + if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate) + { + Result &= FireEtwModuleRangeLoadPrivate(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType, ibcType, virtualSectionType); + } + } + return Result; +} +#endif // !FEATURE_PAL + +#ifndef FEATURE_DTRACE +//--------------------------------------------------------------------------------------- +// +// Helper that takes a module, and returns the managed and native PDB information +// corresponding to that module. Used by the routine that fires the module load / unload +// events. +// +// Arguments: +// * pModule - Module to examine +// * pCvInfoIL - [out] CV_INFO_PDB70 corresponding to managed PDB for this module +// (the last debug directory entry in the PE File), if it exists. If it doesn't +// exist, this is zeroed out. +// * pCvInfoNative - [out] CV_INFO_PDB70 corresponding to native NGEN PDB for this +// module (the next-to-last debug directory entry in the PE File), if it exists. +// If it doesn't exist, this is zeroed out. +// +// Notes: +// * This method only understands the CV_INFO_PDB70 / RSDS format. If the format +// changes, this function will act as if there are no debug directory entries. +// Module load / unload events will still be fired, but all PDB info will be +// zeroed out. +// * The raw data in the PE file's debug directory entries are assumed to be +// untrusted, and reported sizes of buffers are verified against their data. +// + +static void GetCodeViewInfo(Module * pModule, CV_INFO_PDB70 * pCvInfoIL, CV_INFO_PDB70 * pCvInfoNative) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE (pModule != NULL); + _ASSERTE (pCvInfoIL != NULL); + _ASSERTE (pCvInfoNative != NULL); + + ZeroMemory(pCvInfoIL, sizeof(*pCvInfoIL)); + ZeroMemory(pCvInfoNative, sizeof(*pCvInfoNative)); + + PTR_PEFile pPEFile = pModule->GetFile(); + _ASSERTE(pPEFile != NULL); + + PTR_PEImageLayout pLayout = NULL; + if (pPEFile->HasNativeImage()) + { + pLayout = pPEFile->GetLoadedNative(); + } + else if (pPEFile->HasOpenedILimage()) + { + pLayout = pPEFile->GetLoadedIL(); + } + + if (pLayout == NULL) + { + // This can happen for reflection-loaded modules + return; + } + + if (!pLayout->HasNTHeaders()) + { + // Without NT headers, we'll have a tough time finding the debug directory + // entries. This can happen for nlp files. + return; + } + + if (!pLayout->HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG)) + return; + + COUNT_T cbDebugEntries; + IMAGE_DEBUG_DIRECTORY * rgDebugEntries = + (IMAGE_DEBUG_DIRECTORY *) pLayout->GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_DEBUG, &cbDebugEntries); + + if (cbDebugEntries < sizeof(IMAGE_DEBUG_DIRECTORY)) + return; + + // Since rgDebugEntries is an array of IMAGE_DEBUG_DIRECTORYs, cbDebugEntries + // should be a multiple of sizeof(IMAGE_DEBUG_DIRECTORY). + if (cbDebugEntries % sizeof(IMAGE_DEBUG_DIRECTORY) != 0) + return; + + // Temporary storage for a CV_INFO_PDB70 and its size (which could be less than + // sizeof(CV_INFO_PDB70); see below). + struct PdbInfo + { + CV_INFO_PDB70 * m_pPdb70; + ULONG m_cbPdb70; + }; + + // Iterate through all debug directory entries. The very last one will be the + // managed PDB entry. The next to last one (if it exists) will be the (native) NGEN + // PDB entry. Treat raw bytes we read as untrusted. + PdbInfo pdbInfoLast = {0}; + PdbInfo pdbInfoNextToLast = {0}; + int cEntries = cbDebugEntries / sizeof(IMAGE_DEBUG_DIRECTORY); + for (int i = 0; i < cEntries; i++) + { + if (rgDebugEntries[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW) + continue; + + // Get raw data pointed to by this IMAGE_DEBUG_DIRECTORY + + // Some compilers set PointerToRawData but not AddressOfRawData as they put the + // data at the end of the file in an unmapped part of the file + RVA rvaOfRawData = (rgDebugEntries[i].AddressOfRawData != NULL) ? + rgDebugEntries[i].AddressOfRawData : + pLayout->OffsetToRva(rgDebugEntries[i].PointerToRawData); + + ULONG cbDebugData = rgDebugEntries[i].SizeOfData; + if (cbDebugData < (offsetof(CV_INFO_PDB70, magic) + sizeof(((CV_INFO_PDB70*)0)->magic))) + { + // raw data too small to contain magic number at expected spot, so its format + // is not recognizeable. Skip + continue; + } + + if (!pLayout->CheckRva(rvaOfRawData, cbDebugData)) + { + // Memory claimed to belong to the raw data does not fit. + // IMAGE_DEBUG_DIRECTORY is outright corrupt. Do not include PDB info in + // event at all. + return; + } + + // Verify the magic number is as expected + CV_INFO_PDB70 * pPdb70 = (CV_INFO_PDB70 *) pLayout->GetRvaData(rvaOfRawData); + if (pPdb70->magic != CV_SIGNATURE_RSDS) + { + // Unrecognized magic number. Skip + continue; + } + + // From this point forward, the format should adhere to the expected layout of + // CV_INFO_PDB70. If we find otherwise, then assume the IMAGE_DEBUG_DIRECTORY is + // outright corrupt, and do not include PDB info in event at all. The caller will + // still fire the module event, but have zeroed-out / empty PDB fields. + + // Verify sane size of raw data + if (cbDebugData > sizeof(CV_INFO_PDB70)) + return; + + // cbDebugData actually can be < sizeof(CV_INFO_PDB70), since the "path" field + // can be truncated to its actual data length (i.e., fewer than MAX_PATH chars + // may be present in the PE file). In some cases, though, cbDebugData will + // include all MAX_PATH chars even though path gets null-terminated well before + // the MAX_PATH limit. + + // Gotta have at least one byte of the path + if (cbDebugData < offsetof(CV_INFO_PDB70, path) + sizeof(char)) + return; + + // How much space is available for the path? + size_t cchPathMaxIncludingNullTerminator = (cbDebugData - offsetof(CV_INFO_PDB70, path)) / sizeof(char); + _ASSERTE(cchPathMaxIncludingNullTerminator >= 1); // Guaranteed above + + // Verify path string fits inside the declared size + size_t cchPathActualExcludingNullTerminator = strnlen(pPdb70->path, cchPathMaxIncludingNullTerminator); + if (cchPathActualExcludingNullTerminator == cchPathMaxIncludingNullTerminator) + { + // This is how strnlen indicates failure--it couldn't find the null + // terminator within the buffer size specified + return; + } + + // Looks valid. Remember it. + pdbInfoNextToLast = pdbInfoLast; + pdbInfoLast.m_pPdb70 = pPdb70; + pdbInfoLast.m_cbPdb70 = cbDebugData; + } + + // Return whatever we found + + if (pdbInfoLast.m_pPdb70 != NULL) + { + // The last guy is the IL (managed) PDB info + _ASSERTE(pdbInfoLast.m_cbPdb70 <= sizeof(*pCvInfoIL)); // Guaranteed by checks above + memcpy(pCvInfoIL, pdbInfoLast.m_pPdb70, pdbInfoLast.m_cbPdb70); + } + + if (pdbInfoNextToLast.m_pPdb70 != NULL) + { + // The next-to-last guy is the NGEN (native) PDB info + _ASSERTE(pdbInfoNextToLast.m_cbPdb70 <= sizeof(*pCvInfoNative)); // Guaranteed by checks above + memcpy(pCvInfoNative, pdbInfoNextToLast.m_pPdb70, pdbInfoNextToLast.m_cbPdb70); + } +} +#endif // FEATURE_DTRACE + + + +//--------------------------------------------------------------------------------------- +// +// send a module load/unload or rundown event and domainmodule load and rundown event +// +// Arguments: +// * pModule - Module loading or unloading +// * dwEventOptions - Bitmask of which events to fire +// * bFireDomainModuleEvents - nonzero if we are to fire DomainModule events; zero +// if we are to fire Module events +// +void ETW::LoaderLog::SendModuleEvent(Module *pModule, DWORD dwEventOptions, BOOL bFireDomainModuleEvents) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + } CONTRACTL_END; + + if(!pModule) + return; + +#ifndef FEATURE_DTRACE + PCWSTR szDtraceOutput1=L"",szDtraceOutput2=L""; +#else + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + CHAR szDtraceOutput2[DTRACE_OUTPUT_STRING_LEN]; +#endif // !FEATURE_DTRACE + BOOL bIsDynamicAssembly = pModule->GetAssembly()->IsDynamic(); + BOOL bHasNativeImage = FALSE; +#ifdef FEATURE_PREJIT + bHasNativeImage = pModule->HasNativeImage(); +#endif // FEATURE_PREJIT + BOOL bIsManifestModule = pModule->IsManifest(); + ULONGLONG ullAppDomainId = 0; // This is used only with DomainModule events + ULONGLONG ullModuleId = (ULONGLONG)(TADDR) pModule; + ULONGLONG ullAssemblyId = (ULONGLONG)pModule->GetAssembly(); + BOOL bIsDomainNeutral = pModule->GetAssembly()->IsDomainNeutral(); + BOOL bIsIbcOptimized = FALSE; + if(bHasNativeImage) + { + bIsIbcOptimized = pModule->IsIbcOptimized(); + } + ULONG ulReservedFlags = 0; + ULONG ulFlags = ((bIsDomainNeutral ? ETW::LoaderLog::LoaderStructs::DomainNeutralModule : 0) | + (bHasNativeImage ? ETW::LoaderLog::LoaderStructs::NativeModule : 0) | + (bIsDynamicAssembly ? ETW::LoaderLog::LoaderStructs::DynamicModule : 0) | + (bIsManifestModule ? ETW::LoaderLog::LoaderStructs::ManifestModule : 0) | + (bIsIbcOptimized ? ETW::LoaderLog::LoaderStructs::IbcOptimized : 0)); + +#ifndef FEATURE_DTRACE + // Grab PDB path, guid, and age for managed PDB and native (NGEN) PDB when + // available. Any failures are not fatal. The corresponding PDB info will remain + // zeroed out, and that's what we'll include in the event. + CV_INFO_PDB70 cvInfoIL = {0}; + CV_INFO_PDB70 cvInfoNative = {0}; + GetCodeViewInfo(pModule, &cvInfoIL, &cvInfoNative); +#endif // FEATURE_DTRACE + + PWCHAR ModuleILPath=L"", ModuleNativePath=L""; + + if(bFireDomainModuleEvents) + { + if(pModule->GetDomain()->IsSharedDomain()) // for shared domains, we do not fire domainmodule event + return; + ullAppDomainId = (ULONGLONG)pModule->FindDomainAssembly(pModule->GetDomain()->AsAppDomain())->GetAppDomain(); + } + + LPCWSTR pEmptyString = L""; +#ifndef FEATURE_PAL + SString moduleName = L""; +#else // !FEATURE_PAL + SString moduleName; +#endif // !FEATURE_PAL + if(!bIsDynamicAssembly) + { + ModuleILPath = (PWCHAR)pModule->GetAssembly()->GetManifestFile()->GetILimage()->GetPath().GetUnicode(); + ModuleNativePath = (PWCHAR)pEmptyString; + +#ifdef FEATURE_PREJIT + if(bHasNativeImage) + ModuleNativePath = (PWCHAR)pModule->GetNativeImage()->GetPath().GetUnicode(); +#endif // FEATURE_PREJIT + } + + // if we do not have a module path yet, we put the module name + if(bIsDynamicAssembly || ModuleILPath==NULL || wcslen(ModuleILPath) <= 2) + { + moduleName.SetUTF8(pModule->GetSimpleName()); + ModuleILPath = (PWCHAR)moduleName.GetUnicode(); + ModuleNativePath = (PWCHAR)pEmptyString; + } + + /* prepare events args for ETW and ETM */ +#ifndef FEATURE_DTRACE + szDtraceOutput1 = (PCWSTR)ModuleILPath; + szDtraceOutput2 = (PCWSTR)ModuleNativePath; + + // Convert PDB paths to UNICODE + StackSString managedPdbPath(SString::Utf8, cvInfoIL.path); + StackSString nativePdbPath(SString::Utf8, cvInfoNative.path); +#else // !FEATURE_DTRACE + // since DTrace do not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeOfILPath = WideCharToMultiByte(ModuleILPath, szDtraceOutput1); + if (nSizeOfILPath == 0) + return; + INT32 nSizeOfNativePath = WideCharToMultiByte(ModuleNativePath, szDtraceOutput2); + if (nSizeOfNativePath == 0) + return; +#endif // !FEATURE_DTRACE + + if(bFireDomainModuleEvents) + { + if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) + { + FireEtwDomainModuleLoad_V1(ullModuleId, ullAssemblyId, ullAppDomainId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId()); + } + else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) + { + FireEtwDomainModuleDCStart_V1(ullModuleId, ullAssemblyId, ullAppDomainId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId()); + } + else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) + { + FireEtwDomainModuleDCEnd_V1(ullModuleId, ullAssemblyId, ullAppDomainId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId()); + } + else + { + _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)); + } + } + else + { + if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) || (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad)) + { + FireEtwModuleLoad_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath); + } + else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) + { + FireEtwModuleUnload_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath); + } + else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) || (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart)) + { + FireEtwModuleDCStart_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath); + } + else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) || (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd)) + { + FireEtwModuleDCEnd_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath); + } + else + { + _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeEnabledAny)); + + } +#if !defined(FEATURE_PAL) + if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeEnabledAny) + { + // Fire ModuleRangeLoad, ModuleRangeDCStart, ModuleRangeDCEnd or ModuleRangeLoadPrivate event for this Module + SendModuleRange(pModule, dwEventOptions); + } +#endif + } +} + +/*****************************************************************/ +/* This routine is used to send an ETW event just before a method starts jitting*/ +/*****************************************************************/ +void ETW::MethodLog::SendMethodJitStartEvent(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + } CONTRACTL_END; + + Module *pModule = NULL; + Module *pLoaderModule = NULL; // This must not be used except for getting the ModuleID + + ULONGLONG ullMethodIdentifier=0; + ULONGLONG ullModuleID=0; + ULONG ulMethodToken=0; + ULONG ulMethodILSize=0; +#ifndef FEATURE_DTRACE + PCWSTR szDtraceOutput1=L"",szDtraceOutput2=L"",szDtraceOutput3=L""; +#else + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + CHAR szDtraceOutput2[DTRACE_OUTPUT_STRING_LEN]; + CHAR szDtraceOutput3[DTRACE_OUTPUT_STRING_LEN]; +#endif // !FEATURE_DTRACE + + if(pMethodDesc) { + pModule = pMethodDesc->GetModule_NoLogging(); + + if(!pMethodDesc->IsRestored()) { + return; + } + + bool bIsDynamicMethod = pMethodDesc->IsDynamicMethod(); + BOOL bIsGenericMethod = FALSE; + if(pMethodDesc->GetMethodTable_NoLogging()) + bIsGenericMethod = pMethodDesc->HasClassOrMethodInstantiation_NoLogging(); + + ullModuleID = (ULONGLONG)(TADDR) pModule; + ullMethodIdentifier = (ULONGLONG)pMethodDesc; + + // Use MethodDesc if Dynamic or Generic methods + if( bIsDynamicMethod || bIsGenericMethod) + { + if(bIsGenericMethod) + ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging(); + if(bIsDynamicMethod) // if its a generic and a dynamic method, we would set the methodtoken to 0 + ulMethodToken = (ULONG)0; + } + else + ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging(); + + if(pMethodDesc->IsIL()) + { + COR_ILMETHOD_DECODER::DecoderStatus decoderstatus = COR_ILMETHOD_DECODER::FORMAT_ERROR; + COR_ILMETHOD_DECODER ILHeader(pMethodDesc->GetILHeader(), pMethodDesc->GetMDImport(), &decoderstatus); + ulMethodILSize = (ULONG)ILHeader.GetCodeSize(); + } + + SString tNamespace, tMethodName, tMethodSignature; + if(!namespaceOrClassName|| !methodName|| !methodSignature || (methodName->IsEmpty() && namespaceOrClassName->IsEmpty() && methodSignature->IsEmpty())) + { + pMethodDesc->GetMethodInfo(tNamespace, tMethodName, tMethodSignature); + namespaceOrClassName = &tNamespace; + methodName = &tMethodName; + methodSignature = &tMethodSignature; + } + + // fire method information + /* prepare events args for ETW and ETM */ +#ifndef FEATURE_DTRACE + szDtraceOutput1 = (PCWSTR)namespaceOrClassName->GetUnicode(); + szDtraceOutput2 = (PCWSTR)methodName->GetUnicode(); + szDtraceOutput3 = (PCWSTR)methodSignature->GetUnicode(); +#else // !FEATURE_DTRACE + // since DTrace do not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeOfNamespaceOrClassName = WideCharToMultiByte((PCWSTR)namespaceOrClassName->GetUnicode(), szDtraceOutput1); + if (nSizeOfNamespaceOrClassName == 0) + return; + INT32 nSizeOfMethodName = WideCharToMultiByte((PCWSTR)methodName->GetUnicode(), szDtraceOutput2); + if (nSizeOfMethodName == 0) + return; + INT32 nSizeMethodsignature = WideCharToMultiByte((PCWSTR)methodSignature->GetUnicode(), szDtraceOutput3); + if (nSizeMethodsignature == 0) + return; +#endif // !FEATURE_DTRACE + + FireEtwMethodJittingStarted_V1(ullMethodIdentifier, + ullModuleID, + ulMethodToken, + ulMethodILSize, + szDtraceOutput1, + szDtraceOutput2, + szDtraceOutput3, + GetClrInstanceId()); + } +} + +/****************************************************************************/ +/* This routine is used to send a method load/unload or rundown event */ +/****************************************************************************/ +void ETW::MethodLog::SendMethodEvent(MethodDesc *pMethodDesc, DWORD dwEventOptions, BOOL bIsJit, SString *namespaceOrClassName, SString *methodName, SString *methodSignature, SIZE_T pCode, ReJITID rejitID) +{ + CONTRACTL { + THROWS; + GC_NOTRIGGER; + SO_NOT_MAINLINE; + } CONTRACTL_END; + + Module *pModule = NULL; + Module *pLoaderModule = NULL; // This must not be used except for getting the ModuleID + ULONGLONG ullMethodStartAddress=0, ullColdMethodStartAddress=0, ullModuleID=0, ullMethodIdentifier=0; + ULONG ulMethodSize=0, ulColdMethodSize=0, ulMethodToken=0, ulMethodFlags=0, ulColdMethodFlags=0; + PWCHAR pMethodName=NULL, pNamespaceName=NULL, pMethodSignature=NULL; + BOOL bHasNativeImage = FALSE, bShowVerboseOutput = FALSE, bIsDynamicMethod = FALSE, bHasSharedGenericCode = FALSE, bIsGenericMethod = FALSE; +#ifndef FEATURE_DTRACE + PCWSTR szDtraceOutput1=L"",szDtraceOutput2=L"",szDtraceOutput3=L""; +#else + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; + CHAR szDtraceOutput2[DTRACE_OUTPUT_STRING_LEN]; + CHAR szDtraceOutput3[DTRACE_OUTPUT_STRING_LEN]; +#endif // !FEATURE_DTRACE + + BOOL bIsRundownProvider = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd)); + + BOOL bIsRuntimeProvider = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload)); + + if (pMethodDesc == NULL) + return; + + if(!pMethodDesc->IsRestored()) + { + // Forcibly restoring ngen methods can cause all sorts of deadlocks and contract violations + // These events are therefore put under the private provider + if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_PRIVATENGENFORCERESTORE_KEYWORD)) + { + PERMANENT_CONTRACT_VIOLATION(GCViolation, ReasonNonShippingCode); + pMethodDesc->CheckRestore(); + } + else + { + return; + } + } + + + if(bIsRundownProvider) + { + bShowVerboseOutput = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, + TRACE_LEVEL_VERBOSE, + KEYWORDZERO); + } + else if(bIsRuntimeProvider) + { + bShowVerboseOutput = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_VERBOSE, + KEYWORDZERO); + } + + pModule = pMethodDesc->GetModule_NoLogging(); +#ifdef FEATURE_PREJIT + bHasNativeImage = pModule->HasNativeImage(); +#endif // FEATURE_PREJIT + bIsDynamicMethod = (BOOL)pMethodDesc->IsDynamicMethod(); + bHasSharedGenericCode = pMethodDesc->IsSharedByGenericInstantiations(); + + if(pMethodDesc->GetMethodTable_NoLogging()) + bIsGenericMethod = pMethodDesc->HasClassOrMethodInstantiation_NoLogging(); + + ulMethodFlags = ((ulMethodFlags | + (bHasSharedGenericCode ? ETW::MethodLog::MethodStructs::SharedGenericCode : 0) | + (bIsGenericMethod ? ETW::MethodLog::MethodStructs::GenericMethod : 0) | + (bIsDynamicMethod ? ETW::MethodLog::MethodStructs::DynamicMethod : 0) | + (bIsJit ? ETW::MethodLog::MethodStructs::JittedMethod : 0))); + + // Intentionally set the extent flags (cold vs. hot) only after all the other common + // flags (above) have been set. + ulColdMethodFlags = ulMethodFlags | ETW::MethodLog::MethodStructs::ColdSection; // Method Extent (bits 28, 29, 30, 31) + ulMethodFlags = ulMethodFlags | ETW::MethodLog::MethodStructs::HotSection; // Method Extent (bits 28, 29, 30, 31) + + // MethodDesc ==> Code Address ==>JitMananger + TADDR start = pCode ? pCode : PCODEToPINSTR(pMethodDesc->GetNativeCode()); + if(start == 0) { + // this method hasn't been jitted + return; + } + + // EECodeInfo is technically initialized by a "PCODE", but it can also be initialized + // by a TADDR (i.e., w/out thumb bit set on ARM) + EECodeInfo codeInfo(start); + + // MethodToken ==> MethodRegionInfo + IJitManager::MethodRegionInfo methodRegionInfo; + codeInfo.GetMethodRegionInfo(&methodRegionInfo); + + ullMethodStartAddress = (ULONGLONG)methodRegionInfo.hotStartAddress; + ulMethodSize = (ULONG)methodRegionInfo.hotSize; + + ullModuleID = (ULONGLONG)(TADDR) pModule; + ullMethodIdentifier = (ULONGLONG)pMethodDesc; + + // Use MethodDesc if Dynamic or Generic methods + if( bIsDynamicMethod || bIsGenericMethod) + { + bShowVerboseOutput = TRUE; + if(bIsGenericMethod) + ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging(); + if(bIsDynamicMethod) // if its a generic and a dynamic method, we would set the methodtoken to 0 + ulMethodToken = (ULONG)0; + } + else + ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging(); + + if(bHasNativeImage) + { + ullColdMethodStartAddress = (ULONGLONG)methodRegionInfo.coldStartAddress; + ulColdMethodSize = (ULONG)methodRegionInfo.coldSize; // methodRegionInfo.coldSize is size_t and info.MethodLoadInfo.MethodSize is 32 bit; will give incorrect values on a 64-bit machine + } + + SString tNamespace, tMethodName, tMethodSignature; + + // if verbose method load info needed, only then + // find method name and signature and fire verbose method load info + if(bShowVerboseOutput) + { + if(!namespaceOrClassName|| !methodName|| !methodSignature || (methodName->IsEmpty() && namespaceOrClassName->IsEmpty() && methodSignature->IsEmpty())) + { + pMethodDesc->GetMethodInfo(tNamespace, tMethodName, tMethodSignature); + namespaceOrClassName = &tNamespace; + methodName = &tMethodName; + methodSignature = &tMethodSignature; + } + pNamespaceName = (PWCHAR)namespaceOrClassName->GetUnicode(); + pMethodName = (PWCHAR)methodName->GetUnicode(); + pMethodSignature = (PWCHAR)methodSignature->GetUnicode(); + } + + BOOL bFireEventForColdSection = (bHasNativeImage && ullColdMethodStartAddress && ulColdMethodSize); + + /* prepare events args for ETW and ETM */ +#ifndef FEATURE_DTRACE + szDtraceOutput1 = (PCWSTR)pNamespaceName; + szDtraceOutput2 = (PCWSTR)pMethodName; + szDtraceOutput3 = (PCWSTR)pMethodSignature; +#else // !FEATURE_DTRACE + // since DTrace do not support UNICODE string, they need to be converted to ANSI string + INT32 nSizeTempNamespaceName = WideCharToMultiByte(pNamespaceName, szDtraceOutput1); + if (nSizeTempNamespaceName == 0) + return; + INT32 nSizeTempMethodName = WideCharToMultiByte(pMethodName, szDtraceOutput2); + if (nSizeTempMethodName == 0) + return; + INT32 nSizeMothodSignature = WideCharToMultiByte(pMethodSignature, szDtraceOutput3); + if (nSizeMothodSignature == 0) + return; +#endif // !FEATURE_DTRACE + + if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad)) + { + if(bShowVerboseOutput) + { + FireEtwMethodLoadVerbose_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullMethodStartAddress, + ulMethodSize, + ulMethodToken, + ulMethodFlags, + szDtraceOutput1, + szDtraceOutput2, + szDtraceOutput3, + GetClrInstanceId(), + rejitID); + } + else + { + FireEtwMethodLoad_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullMethodStartAddress, + ulMethodSize, + ulMethodToken, + ulMethodFlags, + GetClrInstanceId(), + rejitID); + } + if(bFireEventForColdSection) + { + if(bShowVerboseOutput) + { + FireEtwMethodLoadVerbose_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullColdMethodStartAddress, + ulColdMethodSize, + ulMethodToken, + ulColdMethodFlags, + szDtraceOutput1, + szDtraceOutput2, + szDtraceOutput3, + GetClrInstanceId(), + rejitID); + } + else + { + FireEtwMethodLoad_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullColdMethodStartAddress, + ulColdMethodSize, + ulMethodToken, + ulColdMethodFlags, + GetClrInstanceId(), + rejitID); + } + } + } + else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload)) + { + if(bShowVerboseOutput) + { + FireEtwMethodUnloadVerbose_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullMethodStartAddress, + ulMethodSize, + ulMethodToken, + ulMethodFlags, + szDtraceOutput1, + szDtraceOutput2, + szDtraceOutput3, + GetClrInstanceId(), + rejitID); + } + else + { + FireEtwMethodUnload_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullMethodStartAddress, + ulMethodSize, + ulMethodToken, + ulMethodFlags, + GetClrInstanceId(), + rejitID); + } + if(bFireEventForColdSection) + { + if(bShowVerboseOutput) + { + FireEtwMethodUnloadVerbose_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullColdMethodStartAddress, + ulColdMethodSize, + ulMethodToken, + ulColdMethodFlags, + szDtraceOutput1, + szDtraceOutput2, + szDtraceOutput3, + GetClrInstanceId(), + rejitID); + } + else + { + FireEtwMethodUnload_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullColdMethodStartAddress, + ulColdMethodSize, + ulMethodToken, + ulColdMethodFlags, + GetClrInstanceId(), + rejitID); + } + } + } + else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart)) + { + if(bShowVerboseOutput) + { + FireEtwMethodDCStartVerbose_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullMethodStartAddress, + ulMethodSize, + ulMethodToken, + ulMethodFlags, + szDtraceOutput1, + szDtraceOutput2, + szDtraceOutput3, + GetClrInstanceId(), + rejitID); + } + else + { + FireEtwMethodDCStart_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullMethodStartAddress, + ulMethodSize, + ulMethodToken, + ulMethodFlags, + GetClrInstanceId(), + rejitID); + } + if(bFireEventForColdSection) + { + if(bShowVerboseOutput) + { + FireEtwMethodDCStartVerbose_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullColdMethodStartAddress, + ulColdMethodSize, + ulMethodToken, + ulColdMethodFlags, + szDtraceOutput1, + szDtraceOutput2, + szDtraceOutput3, + GetClrInstanceId(), + rejitID); + } + else + { + FireEtwMethodDCStart_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullColdMethodStartAddress, + ulColdMethodSize, + ulMethodToken, + ulColdMethodFlags, + GetClrInstanceId(), + rejitID); + } + } + } + else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd)) + { + if(bShowVerboseOutput) + { + FireEtwMethodDCEndVerbose_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullMethodStartAddress, + ulMethodSize, + ulMethodToken, + ulMethodFlags, + szDtraceOutput1, + szDtraceOutput2, + szDtraceOutput3, + GetClrInstanceId(), + rejitID); + } + else + { + FireEtwMethodDCEnd_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullMethodStartAddress, + ulMethodSize, + ulMethodToken, + ulMethodFlags, + GetClrInstanceId(), + rejitID); + } + if(bFireEventForColdSection) + { + if(bShowVerboseOutput) + { + FireEtwMethodDCEndVerbose_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullColdMethodStartAddress, + ulColdMethodSize, + ulMethodToken, + ulColdMethodFlags, + szDtraceOutput1, + szDtraceOutput2, + szDtraceOutput3, + GetClrInstanceId(), + rejitID); + } + else + { + FireEtwMethodDCEnd_V1_or_V2(ullMethodIdentifier, + ullModuleID, + ullColdMethodStartAddress, + ulColdMethodSize, + ulMethodToken, + ulColdMethodFlags, + GetClrInstanceId(), + rejitID); + } + } + } + else + { + _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart) || + (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd)); + } +} + +// This event cannot be supported yet on coreclr, since Silverlight needs to support +// XP, and this event uses a format (dynamic-sized arrays) only supported by the +// Vista+ Crimson event format. So stub out the whole function to a no-op on pre-Vista +// platforms. +#ifndef WINXP_AND_WIN2K3_BUILD_SUPPORT +//--------------------------------------------------------------------------------------- +// +// Fires the IL-to-native map event for JITted methods. This is used for the runtime, +// rundown start, and rundown end events that include the il-to-native map information +// +// Arguments: +// pMethodDesc - MethodDesc for which we'll fire the map event +// dwEventOptions - Options that tells us, in the rundown case, whether we're +// supposed to fire the start or end rundown events. +// + +// static +void ETW::MethodLog::SendMethodILToNativeMapEvent(MethodDesc * pMethodDesc, DWORD dwEventOptions, ReJITID rejitID) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + SO_NOT_MAINLINE; + } + CONTRACTL_END; + + // This is the limit on how big the il-to-native map can get, as measured by number + // of entries in each parallel array (IL offset array and native offset array). + // This number was chosen to ensure the overall event stays under the Windows limit + // of 64K + const USHORT kMapEntriesMax = 7000; + + if (pMethodDesc == NULL) + return; + + if (pMethodDesc->HasClassOrMethodInstantiation() && pMethodDesc->IsTypicalMethodDefinition()) + return; + + // g_pDebugInterface is initialized on startup on desktop CLR, regardless of whether a debugger + // or profiler is loaded. So it should always be available. + _ASSERTE(g_pDebugInterface != NULL); + + ULONGLONG ullMethodIdentifier = (ULONGLONG)pMethodDesc; + + USHORT cMap; + NewArrayHolder rguiILOffset; + NewArrayHolder rguiNativeOffset; + + HRESULT hr = g_pDebugInterface->GetILToNativeMappingIntoArrays( + pMethodDesc, + kMapEntriesMax, + &cMap, + &rguiILOffset, + &rguiNativeOffset); + if (FAILED(hr)) + return; + + // Runtime provider. + // + // This macro already checks for the JittedMethodILToNativeMapKeyword before + // choosing to fire the event + if ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodILToNativeMap) != 0) + { + FireEtwMethodILToNativeMap( + ullMethodIdentifier, + rejitID, + 0, // Extent: This event is only sent for JITted (not NGENd) methods, and + // currently there is only one extent (hot) for JITted methods. + cMap, + rguiILOffset, + rguiNativeOffset, + GetClrInstanceId()); + } + + // Rundown provider + // + // These macros already check for the JittedMethodILToNativeMapRundownKeyword + // before choosing to fire the event--we further check our options to see if we + // should fire the Start and / or End flavor of the event (since the keyword alone + // is insufficient to distinguish these). + // + // (for an explanation of the parameters see the FireEtwMethodILToNativeMap call above) + if ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::MethodDCStartILToNativeMap) != 0) + FireEtwMethodDCStartILToNativeMap(ullMethodIdentifier, 0, 0, cMap, rguiILOffset, rguiNativeOffset, GetClrInstanceId()); + if ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::MethodDCEndILToNativeMap) != 0) + FireEtwMethodDCEndILToNativeMap(ullMethodIdentifier, 0, 0, cMap, rguiILOffset, rguiNativeOffset, GetClrInstanceId()); +} +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT + + +void ETW::MethodLog::SendHelperEvent(ULONGLONG ullHelperStartAddress, ULONG ulHelperSize, LPCWSTR pHelperName) +{ + WRAPPER_NO_CONTRACT; + if(pHelperName) + { +#ifndef FEATURE_DTRACE + PCWSTR szDtraceOutput1=L""; +#else + CHAR szDtraceOutput1[DTRACE_OUTPUT_STRING_LEN]; +#endif // !FEATURE_DTRACE + ULONG methodFlags = ETW::MethodLog::MethodStructs::JitHelperMethod; // helper flag set +#ifndef FEATURE_DTRACE + FireEtwMethodLoadVerbose_V1(ullHelperStartAddress, + 0, + ullHelperStartAddress, + ulHelperSize, + 0, + methodFlags, + NULL, + pHelperName, + NULL, + GetClrInstanceId()); +#else // !FEATURE_DTRACE + // since DTrace do not support UNICODE string, they need to be converted to ANSI string + INT32 nTempHelperName = WideCharToMultiByte(pHelperName, szDtraceOutput1); + if (nTempHelperName == 0) + return; + // in the action, printf, of DTtrace, it cannot print an arg with value NULL when the format is set %s. + // Dtrace does not provide the condition statement so that we give a string "NULL" to it. + FireEtwMethodLoadVerbose_V1(ullHelperStartAddress, + 0, + ullHelperStartAddress, + ulHelperSize, + 0, + methodFlags, + szDtraceOutputNULL, + szDtraceOutput1, + szDtraceOutputNULL, + GetClrInstanceId()); +#endif // !FEATURE_DTRACE + } +} + + +/****************************************************************************/ +/* This routine sends back method events of type 'dwEventOptions', for all + NGEN methods in pModule */ +/****************************************************************************/ +void ETW::MethodLog::SendEventsForNgenMethods(Module *pModule, DWORD dwEventOptions) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + } CONTRACTL_END; + +#ifdef FEATURE_PREJIT + if(!pModule || !pModule->HasNativeImage()) + return; + + MethodIterator mi(pModule); + + while(mi.Next()) + { + MethodDesc *hotDesc = (MethodDesc *)mi.GetMethodDesc(); + ETW::MethodLog::SendMethodEvent(hotDesc, dwEventOptions, FALSE); + } +#endif // FEATURE_PREJIT +} + +/****************************************************************************/ +/* This routine sends back method events of type 'dwEventOptions', for all + JITed methods in either a given LoaderAllocator (if pLoaderAllocatorFilter is non NULL) + or in a given Domain (if pDomainFilter is non NULL) or for + all methods (if both filters are null) */ +/****************************************************************************/ +void ETW::MethodLog::SendEventsForJitMethods(BaseDomain *pDomainFilter, LoaderAllocator *pLoaderAllocatorFilter, DWORD dwEventOptions) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + } CONTRACTL_END; + +#if !defined(FEATURE_PAL) && !defined(DACCESS_COMPILE) + + // This is only called for JITted methods loading xor unloading + BOOL fLoadOrDCStart = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoadOrDCStartAny) != 0); + BOOL fUnloadOrDCEnd = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnloadOrDCEndAny) != 0); + _ASSERTE((fLoadOrDCStart || fUnloadOrDCEnd) && !(fLoadOrDCStart && fUnloadOrDCEnd)); + + BOOL fSendMethodEvent = + (dwEventOptions & + (ETW::EnumerationLog::EnumerationStructs::JitMethodLoad | + ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart | + ETW::EnumerationLog::EnumerationStructs::JitMethodUnload | + ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd)) != 0; + + BOOL fSendILToNativeMapEvent = + (dwEventOptions & + (ETW::EnumerationLog::EnumerationStructs::MethodDCStartILToNativeMap | + ETW::EnumerationLog::EnumerationStructs::MethodDCEndILToNativeMap)) != 0; + + BOOL fCollectibleLoaderAllocatorFilter = + ((pLoaderAllocatorFilter != NULL) && (pLoaderAllocatorFilter->IsCollectible())); +#ifndef WINXP_AND_WIN2K3_BUILD_SUPPORT + if (fSendILToNativeMapEvent) + { + // The call to SendMethodILToNativeMapEvent assumes that the debugger's lazy + // data has already been initialized, to ensure we don't try to do the lazy init + // while under the implicit, notrigger CodeHeapIterator lock below. + + // g_pDebugInterface is initialized on startup on desktop CLR, regardless of whether a debugger + // or profiler is loaded. So it should always be available. + _ASSERTE(g_pDebugInterface != NULL); + g_pDebugInterface->InitializeLazyDataIfNecessary(); + } +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT + + EEJitManager::CodeHeapIterator heapIterator(pDomainFilter, pLoaderAllocatorFilter); + while(heapIterator.Next()) + { + MethodDesc * pMD = heapIterator.GetMethod(); + if (pMD == NULL) + continue; + + TADDR codeStart = heapIterator.GetMethodCode(); + + // Grab rejitID from the rejit manager. Short-circuit the call if we're filtering + // by a collectible loader allocator, since rejit is not supported on RefEmit + // assemblies. This also allows us to avoid having to pre-enter the rejit + // manager locks (which we have to do when filtering by domain; see + // code:#TableLockHolder). + ReJITID rejitID = + fCollectibleLoaderAllocatorFilter ? + 0 : + pMD->GetReJitManager()->GetReJitIdNoLock(pMD, codeStart); + + // There are small windows of time where the heap iterator may come across a + // codeStart that is not yet published to the MethodDesc. This may happen if + // we're JITting the method right now on another thread, and have not completed + // yet. Detect the race, and skip the method if appropriate. (If rejitID is + // nonzero, there is no race, as GetReJitIdNoLock will not return a nonzero + // rejitID if the codeStart has not yet been published for that rejitted version + // of the method.) This check also catches recompilations due to EnC, which we do + // not want to issue events for, in order to ensure xperf's assumption that + // MethodDesc* + ReJITID + extent (hot vs. cold) form a unique key for code + // ranges of methods + if ((rejitID == 0) && (codeStart != PCODEToPINSTR(pMD->GetNativeCode()))) + continue; + + // When we're called to announce loads, then the methodload event itself must + // precede any supplemental events, so that the method load or method jitting + // event is the first event the profiler sees for that MethodID (and not, say, + // the MethodILToNativeMap event.) + if (fLoadOrDCStart) + { + if (fSendMethodEvent) + { + ETW::MethodLog::SendMethodEvent( + pMD, + dwEventOptions, + TRUE, // bIsJit + NULL, // namespaceOrClassName + NULL, // methodName + NULL, // methodSignature + codeStart, + rejitID); + } + } + + // Send any supplemental events requested for this MethodID +#ifndef WINXP_AND_WIN2K3_BUILD_SUPPORT + if (fSendILToNativeMapEvent) + ETW::MethodLog::SendMethodILToNativeMapEvent(pMD, dwEventOptions, rejitID); +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT + + // When we're called to announce unloads, then the methodunload event itself must + // come after any supplemental events, so that the method unload event is the + // last event the profiler sees for this MethodID + if (fUnloadOrDCEnd) + { + if (fSendMethodEvent) + { + ETW::MethodLog::SendMethodEvent( + pMD, + dwEventOptions, + TRUE, // bIsJit + NULL, // namespaceOrClassName + NULL, // methodName + NULL, // methodSignature + codeStart, + rejitID); + } + } + } +#endif // !FEATURE_PAL && !DACCESS_COMPILE +} + +//--------------------------------------------------------------------------------------- +// +// Wrapper around IterateDomain, which locks the AppDomain to be < +// STAGE_FINALIZED until the iteration is complete. +// +// Arguments: +// pAppDomain - AppDomain to iterate +// enumerationOptions - Flags indicating what to enumerate. Just passed +// straight through to IterateDomain +// +void ETW::EnumerationLog::IterateAppDomain(AppDomain * pAppDomain, DWORD enumerationOptions) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + PRECONDITION(pAppDomain != NULL); + } + CONTRACTL_END; + + // Hold the system domain lock during the entire iteration, so we can + // ensure the App Domain does not get finalized until we're all done + SystemDomain::LockHolder lh; + + if (pAppDomain->IsFinalized()) + { + return; + } + + // Since we're not FINALIZED yet, the handle table should remain intact, + // as should all type information in this AppDomain + _ASSERTE(!pAppDomain->NoAccessToHandleTable()); + + // Now it's safe to do the iteration + IterateDomain(pAppDomain, enumerationOptions); + + // Since we're holding the system domain lock, the AD type info should be + // there throughout the entire iteration we just did + _ASSERTE(!pAppDomain->NoAccessToHandleTable()); +} + +/********************************************************************************/ +/* This routine fires ETW events for + Domain, + Assemblies in them, + DomainModule's in them, + Modules in them, + JIT methods in them, + and the NGEN methods in them + based on enumerationOptions.*/ +/********************************************************************************/ +void ETW::EnumerationLog::IterateDomain(BaseDomain *pDomain, DWORD enumerationOptions) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + PRECONDITION(pDomain != NULL); + } CONTRACTL_END; + +#if defined(_DEBUG) && !defined(DACCESS_COMPILE) + // Do not call IterateDomain() directly with an AppDomain. Use + // IterateAppDomain(), whch wraps this function with a hold on the + // SystemDomain lock, which ensures pDomain's type data doesn't disappear + // on us. + if (pDomain->IsAppDomain()) + { + _ASSERTE(SystemDomain::IsUnderDomainLock()); + } +#endif // defined(_DEBUG) && !defined(DACCESS_COMPILE) + + EX_TRY + { + // DC Start events for Domain + if(enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) + { + ETW::LoaderLog::SendDomainEvent(pDomain, enumerationOptions); + } + + // DC End or Unload Jit Method events + if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnloadOrDCEndAny) + { + ETW::MethodLog::SendEventsForJitMethods(pDomain, NULL, enumerationOptions); + } + + if (pDomain->IsAppDomain()) + { + AppDomain::AssemblyIterator assemblyIterator = pDomain->AsAppDomain()->IterateAssembliesEx( + (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution)); + CollectibleAssemblyHolder pDomainAssembly; + while (assemblyIterator.Next(pDomainAssembly.This())) + { + CollectibleAssemblyHolder pAssembly = pDomainAssembly->GetLoadedAssembly(); + BOOL bIsDomainNeutral = pAssembly->IsDomainNeutral(); + if (bIsDomainNeutral) + continue; + if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) + { + ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions); + } + + DomainModuleIterator domainModuleIterator = pDomainAssembly->IterateModules(kModIterIncludeLoaded); + while (domainModuleIterator.Next()) + { + Module * pModule = domainModuleIterator.GetModule(); + ETW::EnumerationLog::IterateModule(pModule, enumerationOptions); + } + + if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) || + (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)) + { + ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions); + } + } + } + else + { + SharedDomain::SharedAssemblyIterator sharedDomainIterator; + while (sharedDomainIterator.Next()) + { + Assembly * pAssembly = sharedDomainIterator.GetAssembly(); + if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) + { + ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions); + } + + ModuleIterator domainModuleIterator = pAssembly->IterateModules(); + while (domainModuleIterator.Next()) + { + Module * pModule = domainModuleIterator.GetModule(); + ETW::EnumerationLog::IterateModule(pModule, enumerationOptions); + } + + if ((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) || + (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)) + { + ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions); + } + } + } + + // DC Start or Load Jit Method events + if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoadOrDCStartAny) + { + ETW::MethodLog::SendEventsForJitMethods(pDomain, NULL, enumerationOptions); + } + + // DC End or Unload events for Domain + if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) || + (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)) + { + ETW::LoaderLog::SendDomainEvent(pDomain, enumerationOptions); + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + + +/********************************************************************************/ +/* This routine fires ETW events for + Assembly in LoaderAllocator, + DomainModule's in them, + Modules in them, + JIT methods in them, + and the NGEN methods in them + based on enumerationOptions.*/ +/********************************************************************************/ +void ETW::EnumerationLog::IterateCollectibleLoaderAllocator(AssemblyLoaderAllocator *pLoaderAllocator, DWORD enumerationOptions) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + PRECONDITION(pLoaderAllocator != NULL); + } CONTRACTL_END; + + EX_TRY + { + // Unload Jit Method events + if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) + { + ETW::MethodLog::SendEventsForJitMethods(NULL, pLoaderAllocator, enumerationOptions); + } + + Assembly *pAssembly = pLoaderAllocator->Id()->GetDomainAssembly()->GetAssembly(); + _ASSERTE(!pAssembly->IsDomainNeutral()); // Collectible Assemblies are not domain neutral. + + DomainModuleIterator domainModuleIterator = pLoaderAllocator->Id()->GetDomainAssembly()->IterateModules(kModIterIncludeLoaded); + while (domainModuleIterator.Next()) + { + Module *pModule = domainModuleIterator.GetModule(); + ETW::EnumerationLog::IterateModule(pModule, enumerationOptions); + } + + if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) + { + ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions); + } + + // Load Jit Method events + if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) + { + ETW::MethodLog::SendEventsForJitMethods(NULL, pLoaderAllocator, enumerationOptions); + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/********************************************************************************/ +/* This routine fires ETW events for Assembly and the DomainModule's in them + based on enumerationOptions.*/ +/********************************************************************************/ +void ETW::EnumerationLog::IterateAssembly(Assembly *pAssembly, DWORD enumerationOptions) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + PRECONDITION(pAssembly != NULL); + } CONTRACTL_END; + + EX_TRY + { + // DC Start events for Assembly + if(enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) + { + ETW::LoaderLog::SendAssemblyEvent(pAssembly, enumerationOptions); + } + + // DC Start, DCEnd, events for DomainModule + if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) || + (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)) + { + if(pAssembly->GetDomain()->IsAppDomain()) + { + DomainModuleIterator dmIterator = pAssembly->FindDomainAssembly(pAssembly->GetDomain()->AsAppDomain())->IterateModules(kModIterIncludeLoaded); + while (dmIterator.Next()) + { + ETW::LoaderLog::SendModuleEvent(dmIterator.GetModule(), enumerationOptions, TRUE); + } + } + } + + // DC End or Unload events for Assembly + if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) || + (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)) + { + ETW::LoaderLog::SendAssemblyEvent(pAssembly, enumerationOptions); + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +/********************************************************************************/ +/* This routine fires ETW events for Module, their range information and the NGEN methods in them + based on enumerationOptions.*/ +/********************************************************************************/ +void ETW::EnumerationLog::IterateModule(Module *pModule, DWORD enumerationOptions) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + PRECONDITION(pModule != NULL); + } CONTRACTL_END; + + EX_TRY + { + // DC Start events for Module + if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) || + (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart)) + { + ETW::LoaderLog::SendModuleEvent(pModule, enumerationOptions); + } + + // DC Start or Load or DC End or Unload Ngen Method events + if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad) || + (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart) || + (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload) || + (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd)) + { + ETW::MethodLog::SendEventsForNgenMethods(pModule, enumerationOptions); + } + + // DC End or Unload events for Module + if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) || + (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) || + (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd)) + { + ETW::LoaderLog::SendModuleEvent(pModule, enumerationOptions); + } + + // If we're logging types, then update the internal Type hash table to account + // for the module's unloading + if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::TypeUnload) + { + ETW::TypeSystemLog::OnModuleUnload(pModule); + } + + // ModuleRangeLoadPrivate events for module range information from attach/detach scenarios + if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_PERFTRACK_PRIVATE_KEYWORD) && + (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate)) + { + ETW::LoaderLog::SendModuleEvent(pModule, enumerationOptions); + } + } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions); +} + +//--------------------------------------------------------------------------------------- +// +// This routine sends back domain, assembly, module and method events based on +// enumerationOptions. +// +// Arguments: +// * moduleFilter - if non-NULL, events from only moduleFilter module are reported +// * domainFilter - if non-NULL, events from only domainFilter domain are reported +// * enumerationOptions - Flags from ETW::EnumerationLog::EnumerationStructs which +// describe which events should be sent. +// +// Notes: +// * if all filter args are NULL, events from all domains are reported +// +// #TableLockHolder: +// +// A word about ReJitManager::TableLockHolder... As we enumerate through the functions, +// we may need to grab their ReJITIDs. The ReJitManager grabs its table Crst in order to +// fetch these. However, several other kinds of locks are being taken during this +// enumeration, such as the SystemDomain lock and the EEJitManager::CodeHeapIterator's +// lock. In order to avoid lock-leveling issues, we grab the appropriate ReJitManager +// table locks up front. In particular, we need to grab the SharedDomain's ReJitManager +// table lock as well as the specific AppDomain's ReJitManager table lock for the current +// AppDomain we're iterating. Why the SharedDomain's ReJitManager lock? For any given +// AppDomain we're iterating over, the MethodDescs we find may be managed by that +// AppDomain's ReJitManger OR the SharedDomain's ReJitManager. (This is due to generics +// and whether given instantiations may be shared based on their arguments.) Therefore, +// we proactively take the SharedDomain's ReJitManager's table lock up front, and then +// individually take the appropriate AppDomain's ReJitManager's table lock that +// corresponds to the domain or module we're currently iterating over. +// + +// static +void ETW::EnumerationLog::EnumerationHelper(Module *moduleFilter, BaseDomain *domainFilter, DWORD enumerationOptions) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + } CONTRACTL_END; + + // Disable IBC logging during ETW enumeration since we call a lot of functionality + // that does logging and causes problems in the shutdown path due to critical + // section access for IBC logging + IBCLoggingDisabler disableLogging; + + // See code:#TableLockHolder + ReJitManager::TableLockHolder lkRejitMgrSharedDomain(SharedDomain::GetDomain()->GetReJitManager()); + + if(moduleFilter) + { + // See code:#TableLockHolder + ReJitManager::TableLockHolder lkRejitMgrModule(moduleFilter->GetReJitManager()); + + + // DC End or Unload Jit Method events from all Domains + if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnloadOrDCEndAny) + { + ETW::MethodLog::SendEventsForJitMethods(NULL, NULL, enumerationOptions); + } + + ETW::EnumerationLog::IterateModule(moduleFilter, enumerationOptions); + + // DC Start or Load Jit Method events from all Domains + if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoadOrDCStartAny) + { + ETW::MethodLog::SendEventsForJitMethods(NULL, NULL, enumerationOptions); + } + } + else + { + if(domainFilter) + { + // See code:#TableLockHolder + ReJitManager::TableLockHolder lkRejitMgrAD(domainFilter->GetReJitManager()); + + if(domainFilter->IsAppDomain()) + { + ETW::EnumerationLog::IterateAppDomain(domainFilter->AsAppDomain(), enumerationOptions); + } + else + { + ETW::EnumerationLog::IterateDomain(domainFilter, enumerationOptions); + } + } + else + { + AppDomainIterator appDomainIterator(FALSE); + while(appDomainIterator.Next()) + { + AppDomain *pDomain = appDomainIterator.GetDomain(); + if (pDomain != NULL) + { + // See code:#TableLockHolder + ReJitManager::TableLockHolder lkRejitMgrAD(pDomain->GetReJitManager()); + + ETW::EnumerationLog::IterateAppDomain(pDomain, enumerationOptions); + } + } + ETW::EnumerationLog::IterateDomain(SharedDomain::GetDomain(), enumerationOptions); + } + } +} + +#endif // !FEATURE_REDHAWK +#endif // defined(FEATURE_REDHAWK) || !defined(FEATURE_PAL) || defined(FEATURE_DTRACE) diff --git a/src/coreclr/nativeaot/Runtime/eventtrace.h b/src/coreclr/nativeaot/Runtime/eventtrace.h new file mode 100644 index 00000000000000..1c6e44bab57383 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/eventtrace.h @@ -0,0 +1,343 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: eventtrace.h +// Abstract: This module implements Event Tracing support. This includes +// eventtracebase.h, and adds VM-specific ETW helpers to support features like type +// logging, allocation logging, and gc heap walk logging. +// +// #EventTracing +// Windows +// ETW (Event Tracing for Windows) is a high-performance, low overhead and highly scalable +// tracing facility provided by the Windows Operating System. ETW is available on Win2K and above. There are +// four main types of components in ETW: event providers, controllers, consumers, and event trace sessions. +// An event provider is a logical entity that writes events to ETW sessions. The event provider must register +// a provider ID with ETW through the registration API. A provider first registers with ETW and writes events +// from various points in the code by invoking the ETW logging API. When a provider is enabled dynamically by +// the ETW controller application, calls to the logging API sends events to a specific trace session +// designated by the controller. Each event sent by the event provider to the trace session consists of a +// fixed header that includes event metadata and additional variable user-context data. CLR is an event +// provider. + +// Mac +// DTrace is similar to ETW and has been made to look like ETW at most of the places. +// For convenience, it is called ETM (Event Tracing for Mac) and exists only on the Mac Leopard OS +// ============================================================================ + +#ifndef _VMEVENTTRACE_H_ +#define _VMEVENTTRACE_H_ + +#include "eventtracebase.h" +#include "gcinterface.h" + +#ifdef FEATURE_EVENT_TRACE +struct ProfilingScanContext : ScanContext +{ + BOOL fProfilerPinned; + void * pvEtwContext; + void *pHeapId; + + ProfilingScanContext(BOOL fProfilerPinnedParam); +}; +#endif // defined(FEATURE_EVENT_TRACE) + +namespace ETW +{ +#ifndef FEATURE_REDHAWK + + class LoggedTypesFromModule; + + // We keep a hash of these to keep track of: + // * Which types have been logged through ETW (so we can avoid logging dupe Type + // events), and + // * GCSampledObjectAllocation stats to help with "smart sampling" which + // dynamically adjusts sampling rate of objects by type. + // See code:LoggedTypesFromModuleTraits + struct TypeLoggingInfo + { + public: + TypeLoggingInfo(TypeHandle thParam) + { + Init(thParam); + } + + TypeLoggingInfo() + { + Init(TypeHandle()); + } + + void Init(TypeHandle thParam) + { + th = thParam; + dwTickOfCurrentTimeBucket = 0; + dwAllocCountInCurrentBucket = 0; + flAllocPerMSec = 0; + + dwAllocsToSkipPerSample = 0; + dwAllocsSkippedForSample = 0; + cbIgnoredSizeForSample = 0; + }; + + // The type this TypeLoggingInfo represents + TypeHandle th; + + // Smart sampling + + // These bucket values remember stats of a particular time slice that are used to + // help adjust the sampling rate + DWORD dwTickOfCurrentTimeBucket; + DWORD dwAllocCountInCurrentBucket; + float flAllocPerMSec; + + // The number of data points to ignore before taking a "sample" (i.e., logging a + // GCSampledObjectAllocation ETW event for this type) + DWORD dwAllocsToSkipPerSample; + + // The current number of data points actually ignored for the current sample + DWORD dwAllocsSkippedForSample; + + // The current count of bytes of objects of this type actually allocated (and + // ignored) for the current sample + SIZE_T cbIgnoredSizeForSample; + }; + + // Class to wrap all type system logic for ETW + class TypeSystemLog + { + private: + static AllLoggedTypes * s_pAllLoggedTypes; + + // See code:ETW::TypeSystemLog::PostRegistrationInit + static BOOL s_fHeapAllocEventEnabledOnStartup; + static BOOL s_fHeapAllocHighEventEnabledNow; + static BOOL s_fHeapAllocLowEventEnabledNow; + + // If COMPLUS_UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec is set, then + // this is used to determine the event frequency, overriding + // s_nDefaultMsBetweenEvents above (regardless of which + // GCSampledObjectAllocation*Keyword was used) + static int s_nCustomMsBetweenEvents; + + public: + // This customizes the type logging behavior in LogTypeAndParametersIfNecessary + enum TypeLogBehavior + { + // Take lock, and consult hash table to see if this is the first time we've + // encountered the type, in which case, log it + kTypeLogBehaviorTakeLockAndLogIfFirstTime, + + // Caller has already taken lock, so just directly consult hash table to see + // if this is the first time we've encountered the type, in which case, log + // it + kTypeLogBehaviorAssumeLockAndLogIfFirstTime, + + // Don't take lock, don't consult hash table. Just log the type. (This is + // used in cases when checking for dupe type logging isn't worth it, such as + // when logging the finalization of an object.) + kTypeLogBehaviorAlwaysLog, + + // When logging the type for GCSampledObjectAllocation events, we don't need + // the lock (as it's already held by the code doing the stats for smart + // sampling), and we already know we need to log the type (since we already + // looked it up in the hash). But we would still need to consult the hash + // for any type parameters, so kTypeLogBehaviorAlwaysLog isn't appropriate, + // and this is used instead. + kTypeLogBehaviorAssumeLockAndAlwaysLogTopLevelType, + }; + + static HRESULT PreRegistrationInit(); + static void PostRegistrationInit(); + static BOOL IsHeapAllocEventEnabled(); + static void SendObjectAllocatedEvent(Object * pObject); + static CrstBase * GetHashCrst(); + static void LogTypeAndParametersIfNecessary(BulkTypeEventLogger * pBulkTypeEventLogger, ULONGLONG thAsAddr, TypeLogBehavior typeLogBehavior); + static void OnModuleUnload(Module * pModule); + static void OnKeywordsChanged(); + + private: + static BOOL ShouldLogType(TypeHandle th); + static BOOL ShouldLogTypeNoLock(TypeHandle th); + static TypeLoggingInfo LookupOrCreateTypeLoggingInfo(TypeHandle th, BOOL * pfCreatedNew, LoggedTypesFromModule ** ppLoggedTypesFromModule = NULL); + static BOOL AddOrReplaceTypeLoggingInfo(ETW::LoggedTypesFromModule * pLoggedTypesFromModule, const ETW::TypeLoggingInfo * pTypeLoggingInfo); + static int GetDefaultMsBetweenEvents(); + static void OnTypesKeywordTurnedOff(); + }; + +#endif // FEATURE_REDHAWK + + // Class to wrap all GC logic for ETW + class GCLog + { + private: + // When WPA triggers a GC, it gives us this unique number to append to our + // GCStart event so WPA can correlate the CLR's GC with the JScript GC they + // triggered at the same time. + // + // We set this value when the GC is triggered, and then retrieve the value on the + // first subsequent FireGcStart() method call for a full, induced GC, assuming + // that that's the GC that WPA triggered. This is imperfect, and if we were in + // the act of beginning another full, induced GC (for some other reason), then + // we'll attach this sequence number to that GC instead of to the WPA-induced GC, + // but who cares? When parsing ETW logs later on, it's indistinguishable if both + // GCs really were induced at around the same time. +#ifdef FEATURE_REDHAWK + static volatile LONGLONG s_l64LastClientSequenceNumber; +#else // FEATURE_REDHAWK + static Volatile s_l64LastClientSequenceNumber; +#endif // FEATURE_REDHAWK + + public: + typedef union st_GCEventInfo { + typedef struct _GenerationInfo { + ULONGLONG GenerationSize; + ULONGLONG TotalPromotedSize; + } GenerationInfo; + + struct { + GenerationInfo GenInfo[4]; // the heap info on gen0, gen1, gen2 and the large object heap. + ULONGLONG FinalizationPromotedSize; //not available per generation + ULONGLONG FinalizationPromotedCount; //not available per generation + ULONG PinnedObjectCount; + ULONG SinkBlockCount; + ULONG GCHandleCount; + } HeapStats; + + typedef enum _HeapType { + SMALL_OBJECT_HEAP, LARGE_OBJECT_HEAP, READ_ONLY_HEAP + } HeapType; + struct { + ULONGLONG Address; + ULONGLONG Size; + HeapType Type; + } GCCreateSegment; + + struct { + ULONGLONG Address; + } GCFreeSegment; + struct { + ULONG Count; + ULONG Depth; + } GCEnd; + + typedef enum _AllocationKind { + AllocationSmall = 0, + AllocationLarge + }AllocationKind; + struct { + ULONG Allocation; + AllocationKind Kind; + } AllocationTick; + + // These values are gotten from the gc_reason + // in gcimpl.h + typedef enum _GC_REASON { + GC_ALLOC_SOH = 0 , + GC_INDUCED = 1 , + GC_LOWMEMORY = 2, + GC_EMPTY = 3, + GC_ALLOC_LOH = 4, + GC_OOS_SOH = 5, + GC_OOS_LOH = 6, + GC_INDUCED_NOFORCE = 7 + } GC_REASON; + typedef enum _GC_TYPE { + GC_NGC = 0 , GC_BGC = 1 , GC_FGC = 2 + } GC_TYPE; + struct { + ULONG Count; + ULONG Depth; + GC_REASON Reason; + GC_TYPE Type; + } GCStart; + + struct { + ULONG Count; // how many finalizers we called. + } GCFinalizers; + + struct { + ULONG Reason; + // This is only valid when SuspendEE is called by GC (ie, Reason is either + // SUSPEND_FOR_GC or SUSPEND_FOR_GC_PREP. + ULONG GcCount; + } SuspendEE; + + struct { + ULONG HeapNum; + } GCMark; + + struct { + ULONGLONG SegmentSize; + ULONGLONG LargeObjectSegmentSize; + BOOL ServerGC; // TRUE means it's server GC; FALSE means it's workstation. + } GCSettings; + + struct { + // The generation that triggered this notification. + ULONG Count; + // 1 means the notification was due to allocation; 0 means it was due to other factors. + ULONG Alloc; + } GCFullNotify; + } ETW_GC_INFO, *PETW_GC_INFO; + +#ifdef FEATURE_EVENT_TRACE + static void GCSettingsEvent(); +#else + static void GCSettingsEvent() {}; +#endif // FEATURE_EVENT_TRACE + + static BOOL ShouldWalkHeapObjectsForEtw(); + static BOOL ShouldWalkHeapRootsForEtw(); + static BOOL ShouldTrackMovementForEtw(); + static BOOL ShouldWalkStaticsAndCOMForEtw(); + static HRESULT ForceGCForDiagnostics(); + static void ForceGC(LONGLONG l64ClientSequenceNumber); + static void FireGcStart(ETW_GC_INFO * pGcInfo); + static void RootReference( + LPVOID pvHandle, + Object * pRootedNode, + Object * pSecondaryNodeForDependentHandle, + BOOL fDependentHandle, + ProfilingScanContext * profilingScanContext, + DWORD dwGCFlags, + DWORD rootFlags); + static void ObjectReference( + ProfilerWalkHeapContext * profilerWalkHeapContext, + Object * pObjReferenceSource, + ULONGLONG typeID, + ULONGLONG cRefs, + Object ** rgObjReferenceTargets); + static void EndHeapDump(ProfilerWalkHeapContext * profilerWalkHeapContext); + static void BeginMovedReferences(size_t * pProfilingContext); + static void MovedReference(BYTE * pbMemBlockStart, BYTE * pbMemBlockEnd, ptrdiff_t cbRelocDistance, size_t profilingContext, BOOL fCompacting, BOOL fAllowProfApiNotification = TRUE); + static void EndMovedReferences(size_t profilingContext, BOOL fAllowProfApiNotification = TRUE); + static void WalkStaticsAndCOMForETW(); +#ifndef FEATURE_REDHAWK + static void SendFinalizeObjectEvent(MethodTable * pMT, Object * pObj); +#endif // FEATURE_REDHAWK + }; +}; + +#ifndef FEATURE_ETW +inline BOOL ETW::GCLog::ShouldWalkHeapObjectsForEtw() { return FALSE; } +inline BOOL ETW::GCLog::ShouldWalkHeapRootsForEtw() { return FALSE; } +inline BOOL ETW::GCLog::ShouldTrackMovementForEtw() { return FALSE; } +inline BOOL ETW::GCLog::ShouldWalkStaticsAndCOMForEtw() { return FALSE; } +inline void ETW::GCLog::FireGcStart(ETW_GC_INFO * pGcInfo) { } +inline void ETW::GCLog::EndHeapDump(ProfilerWalkHeapContext * profilerWalkHeapContext) { } +inline void ETW::GCLog::BeginMovedReferences(size_t * pProfilingContext) { } +inline void ETW::GCLog::MovedReference(BYTE * pbMemBlockStart, BYTE * pbMemBlockEnd, ptrdiff_t cbRelocDistance, size_t profilingContext, BOOL fCompacting) { } +inline void ETW::GCLog::EndMovedReferences(size_t profilingContext) { } +inline void ETW::GCLog::WalkStaticsAndCOMForETW() { } +inline void ETW::GCLog::RootReference( + LPVOID pvHandle, + Object * pRootedNode, + Object * pSecondaryNodeForDependentHandle, + BOOL fDependentHandle, + ProfilingScanContext * profilingScanContext, + DWORD dwGCFlags, + DWORD rootFlags) { } +#endif + +inline BOOL EventEnabledPinObjectAtGCTime() { return FALSE; } + +#endif //_VMEVENTTRACE_H_ diff --git a/src/coreclr/nativeaot/Runtime/eventtracebase.h b/src/coreclr/nativeaot/Runtime/eventtracebase.h new file mode 100644 index 00000000000000..afe833ef597584 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/eventtracebase.h @@ -0,0 +1,1095 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// File: eventtracebase.h +// Abstract: This module implements base Event Tracing support (excluding some of the +// CLR VM-specific ETW helpers). +// +// #EventTracing +// Windows +// ETW (Event Tracing for Windows) is a high-performance, low overhead and highly scalable +// tracing facility provided by the Windows Operating System. ETW is available on Win2K and above. There are +// four main types of components in ETW: event providers, controllers, consumers, and event trace sessions. +// An event provider is a logical entity that writes events to ETW sessions. The event provider must register +// a provider ID with ETW through the registration API. A provider first registers with ETW and writes events +// from various points in the code by invoking the ETW logging API. When a provider is enabled dynamically by +// the ETW controller application, calls to the logging API sends events to a specific trace session +// designated by the controller. Each event sent by the event provider to the trace session consists of a +// fixed header that includes event metadata and additional variable user-context data. CLR is an event +// provider. + +// Mac +// DTrace is similar to ETW and has been made to look like ETW at most of the places. +// For convenience, it is called ETM (Event Tracing for Mac) and exists only on the Mac Leopard OS +// ============================================================================ + +#ifndef _ETWTRACER_HXX_ +#define _ETWTRACER_HXX_ + +struct EventStructTypeData; +void InitializeEventTracing(); + +#ifdef FEATURE_EVENT_TRACE + +// !!!!!!! NOTE !!!!!!!! +// The flags must match those in the ETW manifest exactly +// !!!!!!! NOTE !!!!!!!! + +enum EtwTypeFlags +{ + kEtwTypeFlagsDelegate = 0x1, + kEtwTypeFlagsFinalizable = 0x2, + kEtwTypeFlagsExternallyImplementedCOMObject = 0x4, + kEtwTypeFlagsArray = 0x8, + kEtwTypeFlagsModuleBaseAddress = 0x10, +}; + +enum EtwThreadFlags +{ + kEtwThreadFlagGCSpecial = 0x00000001, + kEtwThreadFlagFinalizer = 0x00000002, + kEtwThreadFlagThreadPoolWorker = 0x00000004, +}; + + +// During a heap walk, this is the storage for keeping track of all the nodes and edges +// being batched up by ETW, and for remembering whether we're also supposed to call into +// a profapi profiler. This is allocated toward the end of a GC and passed to us by the +// GC heap walker. +struct ProfilerWalkHeapContext +{ +public: + ProfilerWalkHeapContext(BOOL fProfilerPinnedParam, LPVOID pvEtwContextParam) + { + fProfilerPinned = fProfilerPinnedParam; + pvEtwContext = pvEtwContextParam; + } + + BOOL fProfilerPinned; + LPVOID pvEtwContext; +}; + +class Object; + +/******************************/ +/* CLR ETW supported versions */ +/******************************/ +#define ETW_SUPPORTED_MAJORVER 5 // ETW is supported on win2k and above +#define ETW_ENABLED_MAJORVER 6 // OS versions >= to this we enable ETW registration by default, since on XP and Windows 2003, registration is too slow. + +/***************************************/ +/* Tracing levels supported by CLR ETW */ +/***************************************/ +#define ETWMAX_TRACE_LEVEL 6 // Maximum Number of Trace Levels supported +#define TRACE_LEVEL_NONE 0 // Tracing is not on +#define TRACE_LEVEL_FATAL 1 // Abnormal exit or termination +#define TRACE_LEVEL_ERROR 2 // Severe errors that need logging +#define TRACE_LEVEL_WARNING 3 // Warnings such as allocation failure +#define TRACE_LEVEL_INFORMATION 4 // Includes non-error cases such as Entry-Exit +#define TRACE_LEVEL_VERBOSE 5 // Detailed traces from intermediate steps + +struct ProfilingScanContext; + +// +// Use this macro to check if ETW is initialized and the event is enabled +// +#define ETW_TRACING_ENABLED(Context, EventDescriptor) \ + (Context.IsEnabled && ETW_TRACING_INITIALIZED(Context.RegistrationHandle) && ETW_EVENT_ENABLED(Context, EventDescriptor)) + +// +// Using KEYWORDZERO means when checking the events category ignore the keyword +// +#define KEYWORDZERO 0x0 + +// +// Use this macro to check if ETW is initialized and the category is enabled +// +#define ETW_TRACING_CATEGORY_ENABLED(Context, Level, Keyword) \ + (ETW_TRACING_INITIALIZED(Context.RegistrationHandle) && ETW_CATEGORY_ENABLED(Context, Level, Keyword)) + +#ifdef FEATURE_DTRACE + #define ETWOnStartup(StartEventName, EndEventName) \ + ETWTraceStartup trace(StartEventName, EndEventName); + #define ETWFireEvent(EventName) \ + FireEtw##EventName(GetClrInstanceId()); +#else + #define ETWOnStartup(StartEventName, EndEventName) \ + ETWTraceStartup trace##StartEventName##(Microsoft_Windows_DotNETRuntimePrivateHandle, &StartEventName, &StartupId, &EndEventName, &StartupId); + #define ETWFireEvent(EventName) \ + ETWTraceStartup::StartupTraceEvent(Microsoft_Windows_DotNETRuntimePrivateHandle, &EventName, &StartupId); +#endif // FEATURE_DTRACE + +#ifndef FEATURE_REDHAWK + +// Headers +#ifndef FEATURE_PAL +#include +#include +#include +#include +#if !defined(DONOT_DEFINE_ETW_CALLBACK) && !defined(DACCESS_COMPILE) +#define GetVersionEx(Version) (GetOSVersion((LPOSVERSIONINFOW)Version)) +#else +#define GetVersionEx(Version) (WszGetVersionEx((LPOSVERSIONINFOW)Version)) +#endif // !DONOT_DEFINE_ETW_CALLBACK && !DACCESS_COMPILE +#endif // !FEATURE_PAL + +#if FEATURE_DTRACE +#include "clrdtrace.h" +#endif + +#endif //!FEATURE_REDHAWK + + +#else // FEATURE_EVENT_TRACE + +#include "etmdummy.h" +#endif // FEATURE_EVENT_TRACE + +#ifndef FEATURE_REDHAWK + +#if defined(FEATURE_CORECLR) && !defined(FEATURE_CORESYSTEM) +// For Silverlight non-CoreSys builds we still use an older toolset, +// headers/libs, and a different value for WINVER. We use this symbol +// to distinguish between whether we built the ETW header files from +// the ETW manifest using the -mof command line or not. +#define WINXP_AND_WIN2K3_BUILD_SUPPORT +#endif +#include "corprof.h" + +// g_nClrInstanceId is defined in Utilcode\Util.cpp. The definition goes into Utilcode.lib. +// This enables both the VM and Utilcode to raise ETW events. +extern UINT32 g_nClrInstanceId; +extern BOOL g_fEEManagedEXEStartup; +extern BOOL g_fEEIJWStartup; + +#define GetClrInstanceId() (static_cast(g_nClrInstanceId)) + +#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL) +// Callback and stack support +#if !defined(DONOT_DEFINE_ETW_CALLBACK) && !defined(DACCESS_COMPILE) +extern "C" { + /* ETW control callback + * Desc: This function handles the ETW control + * callback. + * Ret: success or failure + ***********************************************/ + void EtwCallback( + _In_ LPCGUID SourceId, + _In_ ULONG ControlCode, + _In_ UCHAR Level, + _In_ ULONGLONG MatchAnyKeyword, + _In_ ULONGLONG MatchAllKeyword, + _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData, + _Inout_opt_ PVOID CallbackContext); +} + +// +// User defined callback +// +#define MCGEN_PRIVATE_ENABLE_CALLBACK(RequestCode, Context, InOutBufferSize, Buffer) \ + EtwCallback(NULL /* SourceId */, (RequestCode==WMI_ENABLE_EVENTS) ? EVENT_CONTROL_CODE_ENABLE_PROVIDER : EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0 /* Level */, 0 /* MatchAnyKeyword */, 0 /* MatchAllKeyword */, NULL /* FilterData */, Context) + +// +// User defined callback2 +// +#define MCGEN_PRIVATE_ENABLE_CALLBACK_V2(SourceId, ControlCode, Level, MatchAnyKeyword, MatchAllKeyword, FilterData, CallbackContext) \ + EtwCallback(SourceId, ControlCode, Level, MatchAnyKeyword, MatchAllKeyword, FilterData, CallbackContext) + +extern "C" { + /* ETW callout + * Desc: This function handles the ETW callout + * Ret: success or failure + ***********************************************/ + void EtwCallout( + REGHANDLE RegHandle, + PCEVENT_DESCRIPTOR Descriptor, + ULONG ArgumentCount, + PEVENT_DATA_DESCRIPTOR EventData); +} + +// +// Call user defined callout +// +#define MCGEN_CALLOUT(RegHandle, Descriptor, NumberOfArguments, EventData) \ + EtwCallout(RegHandle, Descriptor, NumberOfArguments, EventData) +#endif //!DONOT_DEFINE_ETW_CALLBACK && !DACCESS_COMPILE + +#include +// The bulk type event is too complex for MC.exe to auto-generate proper code. +// Use code:BulkTypeEventLogger instead. +#ifdef FireEtwBulkType +#undef FireEtwBulkType +#endif // FireEtwBulkType +#endif // FEATURE_EVENT_TRACE && !FEATURE_PAL + +/**************************/ +/* CLR ETW infrastructure */ +/**************************/ +// #CEtwTracer +// On Windows Vista, ETW has gone through a major upgrade, and one of the most significant changes is the +// introduction of the unified event provider model and APIs. The older architecture used the classic ETW +// events. The new ETW architecture uses the manifest based events. To support both types of events at the +// same time, we use the manpp tool for generating event macros that can be directly used to fire ETW events +// from various components within the CLR. +// (http://diagnostics/sites/etw/Lists/Announcements/DispForm.aspx?ID=10&Source=http%3A%2F%2Fdiagnostics%2Fsites%2Fetw%2Fdefault%2Easpx) +// Every ETW provider has to Register itself to the system, so that when enabled, it is capable of firing +// ETW events. file:../VM/eventtrace.cpp#Registration is where the actual Provider Registration takes place. +// At process shutdown, a registered provider need to be unregistered. +// file:../VM/eventtrace.cpp#Unregistration. Since ETW can also be enabled at any instant after the process +// has started, one may want to do something useful when that happens (e.g enumerate all the loaded modules +// in the system). To enable this, we have to implement a callback routine. +// file:../VM/eventtrace.cpp#EtwCallback is CLR's implementation of the callback. +// + +#include "daccess.h" +class Module; +class Assembly; +class MethodDesc; +class MethodTable; +class BaseDomain; +class AppDomain; +class SString; +class CrawlFrame; +class LoaderAllocator; +class AssemblyLoaderAllocator; +struct AllLoggedTypes; +class CrstBase; +class BulkTypeEventLogger; +class TypeHandle; +class Thread; + + +// All ETW helpers must be a part of this namespace +// We have auto-generated macros to directly fire the events +// but in some cases, gathering the event payload information involves some work +// and it can be done in a relevant helper class like the one's in this namespace +namespace ETW +{ + // Class to wrap the ETW infrastructure logic + class CEtwTracer + { +#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL) + ULONG RegGuids(LPCGUID ProviderId, PENABLECALLBACK EnableCallback, PVOID CallbackContext, PREGHANDLE RegHandle); +#endif // !FEATURE_PAL + + public: +#ifdef FEATURE_EVENT_TRACE + // Registers all the Event Tracing providers + HRESULT Register(); + + // Unregisters all the Event Tracing providers + HRESULT UnRegister(); +#else + HRESULT Register() + { + return S_OK; + } + HRESULT UnRegister() + { + return S_OK; + } +#endif // FEATURE_EVENT_TRACE + }; + + class LoaderLog; + class MethodLog; + // Class to wrap all the enumeration logic for ETW + class EnumerationLog + { + friend class ETW::LoaderLog; + friend class ETW::MethodLog; +#ifdef FEATURE_EVENT_TRACE + static void SendThreadRundownEvent(); + static void IterateDomain(BaseDomain *pDomain, DWORD enumerationOptions); + static void IterateAppDomain(AppDomain * pAppDomain, DWORD enumerationOptions); + static void IterateCollectibleLoaderAllocator(AssemblyLoaderAllocator *pLoaderAllocator, DWORD enumerationOptions); + static void IterateAssembly(Assembly *pAssembly, DWORD enumerationOptions); + static void IterateModule(Module *pModule, DWORD enumerationOptions); + static void EnumerationHelper(Module *moduleFilter, BaseDomain *domainFilter, DWORD enumerationOptions); + static DWORD GetEnumerationOptionsFromRuntimeKeywords(); + public: + typedef union _EnumerationStructs + { + typedef enum _EnumerationOptions + { + None= 0x00000000, + DomainAssemblyModuleLoad= 0x00000001, + DomainAssemblyModuleUnload= 0x00000002, + DomainAssemblyModuleDCStart= 0x00000004, + DomainAssemblyModuleDCEnd= 0x00000008, + JitMethodLoad= 0x00000010, + JitMethodUnload= 0x00000020, + JitMethodDCStart= 0x00000040, + JitMethodDCEnd= 0x00000080, + NgenMethodLoad= 0x00000100, + NgenMethodUnload= 0x00000200, + NgenMethodDCStart= 0x00000400, + NgenMethodDCEnd= 0x00000800, + ModuleRangeLoad= 0x00001000, + ModuleRangeDCStart= 0x00002000, + ModuleRangeDCEnd= 0x00004000, + ModuleRangeLoadPrivate= 0x00008000, + MethodDCStartILToNativeMap= 0x00010000, + MethodDCEndILToNativeMap= 0x00020000, + JitMethodILToNativeMap= 0x00040000, + TypeUnload= 0x00080000, + + // Helpers + ModuleRangeEnabledAny = ModuleRangeLoad | ModuleRangeDCStart | ModuleRangeDCEnd | ModuleRangeLoadPrivate, + JitMethodLoadOrDCStartAny = JitMethodLoad | JitMethodDCStart | MethodDCStartILToNativeMap, + JitMethodUnloadOrDCEndAny = JitMethodUnload | JitMethodDCEnd | MethodDCEndILToNativeMap, + }EnumerationOptions; + }EnumerationStructs; + + static void ProcessShutdown(); + static void ModuleRangeRundown(); + static void StartRundown(); + static void EndRundown(); + static void EnumerateForCaptureState(); +#else + public: + static void ProcessShutdown() {}; + static void StartRundown() {}; + static void EndRundown() {}; +#endif // FEATURE_EVENT_TRACE + }; + + + // Class to wrap all the sampling logic for ETW + class SamplingLog + { + // StackWalk available only when !FEATURE_PAL +#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL) + public: + typedef enum _EtwStackWalkStatus + { + Completed = 0, + UnInitialized = 1, + InProgress = 2 + } EtwStackWalkStatus; + private: + static const UINT8 s_MaxStackSize=100; + UINT32 m_FrameCount; + SIZE_T m_EBPStack[SamplingLog::s_MaxStackSize]; + void Append(SIZE_T currentFrame); + EtwStackWalkStatus SaveCurrentStack(int skipTopNFrames=1); + public: + static ULONG SendStackTrace(MCGEN_TRACE_CONTEXT TraceContext, PCEVENT_DESCRIPTOR Descriptor, LPCGUID EventGuid); + EtwStackWalkStatus GetCurrentThreadsCallStack(UINT32 *frameCount, PVOID **Stack); +#endif // FEATURE_EVENT_TRACE && !FEATURE_PAL + }; + + // Class to wrap all Loader logic for ETW + class LoaderLog + { + friend class ETW::EnumerationLog; +#ifdef FEATURE_EVENT_TRACE + static void SendModuleEvent(Module *pModule, DWORD dwEventOptions, BOOL bFireDomainModuleEvents=FALSE); +#if !defined(FEATURE_PAL) + static ULONG SendModuleRange(Module *pModule, DWORD dwEventOptions); +#endif // !FEATURE_PAL + static void SendAssemblyEvent(Assembly *pAssembly, DWORD dwEventOptions); + static void SendDomainEvent(BaseDomain *pBaseDomain, DWORD dwEventOptions, LPCWSTR wszFriendlyName=NULL); + public: + typedef union _LoaderStructs + { + typedef enum _AppDomainFlags + { + DefaultDomain=0x1, + ExecutableDomain=0x2, + SharedDomain=0x4 + }AppDomainFlags; + + typedef enum _AssemblyFlags + { + DomainNeutralAssembly=0x1, + DynamicAssembly=0x2, + NativeAssembly=0x4, + CollectibleAssembly=0x8, + }AssemblyFlags; + + typedef enum _ModuleFlags + { + DomainNeutralModule=0x1, + NativeModule=0x2, + DynamicModule=0x4, + ManifestModule=0x8, + IbcOptimized=0x10 + }ModuleFlags; + + typedef enum _RangeFlags + { + HotRange=0x0 + }RangeFlags; + + }LoaderStructs; + + static void DomainLoadReal(BaseDomain *pDomain, __in_opt LPWSTR wszFriendlyName=NULL); + + static void DomainLoad(BaseDomain *pDomain, __in_opt LPWSTR wszFriendlyName = NULL) + { +#ifndef FEATURE_PAL + if (MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context.IsEnabled) +#endif + { + DomainLoadReal(pDomain, wszFriendlyName); + } + } + + static void DomainUnload(AppDomain *pDomain); + static void CollectibleLoaderAllocatorUnload(AssemblyLoaderAllocator *pLoaderAllocator); + static void ModuleLoad(Module *pModule, LONG liReportedSharedModule); +#else + public: + static void DomainLoad(BaseDomain *pDomain, __in_opt LPWSTR wszFriendlyName=NULL) {}; + static void DomainUnload(AppDomain *pDomain) {}; + static void CollectibleLoaderAllocatorUnload(AssemblyLoaderAllocator *pLoaderAllocator) {}; + static void ModuleLoad(Module *pModule, LONG liReportedSharedModule) {}; +#endif // FEATURE_EVENT_TRACE + }; + + // Class to wrap all Method logic for ETW + class MethodLog + { + friend class ETW::EnumerationLog; +#ifdef FEATURE_EVENT_TRACE + static void SendEventsForJitMethods(BaseDomain *pDomainFilter, LoaderAllocator *pLoaderAllocatorFilter, DWORD dwEventOptions); + static void SendEventsForNgenMethods(Module *pModule, DWORD dwEventOptions); + static void SendMethodJitStartEvent(MethodDesc *pMethodDesc, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL); +#ifndef WINXP_AND_WIN2K3_BUILD_SUPPORT + static void SendMethodILToNativeMapEvent(MethodDesc * pMethodDesc, DWORD dwEventOptions, ReJITID rejitID); +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT + static void SendMethodEvent(MethodDesc *pMethodDesc, DWORD dwEventOptions, BOOL bIsJit, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL, SIZE_T pCode = 0, ReJITID rejitID = 0); + static void SendHelperEvent(ULONGLONG ullHelperStartAddress, ULONG ulHelperSize, LPCWSTR pHelperName); + public: + typedef union _MethodStructs + { + typedef enum _MethodFlags + { + DynamicMethod=0x1, + GenericMethod=0x2, + SharedGenericCode=0x4, + JittedMethod=0x8, + JitHelperMethod=0x10 + }MethodFlags; + + typedef enum _MethodExtent + { + HotSection=0x00000000, + ColdSection=0x10000000 + }MethodExtent; + + }MethodStructs; + + static void MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL); + static void MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL, SIZE_T pCode = 0, ReJITID rejitID = 0); + static void StubInitialized(ULONGLONG ullHelperStartAddress, LPCWSTR pHelperName); + static void StubsInitialized(PVOID *pHelperStartAddresss, PVOID *pHelperNames, LONG ulNoOfHelpers); + static void MethodRestored(MethodDesc * pMethodDesc); + static void MethodTableRestored(MethodTable * pMethodTable); + static void DynamicMethodDestroyed(MethodDesc *pMethodDesc); +#else // FEATURE_EVENT_TRACE + public: + static void MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL) {}; + static void MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL, SIZE_T pCode = 0, ReJITID rejitID = 0) {}; + static void StubInitialized(ULONGLONG ullHelperStartAddress, LPCWSTR pHelperName) {}; + static void StubsInitialized(PVOID *pHelperStartAddresss, PVOID *pHelperNames, LONG ulNoOfHelpers) {}; + static void MethodRestored(MethodDesc * pMethodDesc) {}; + static void MethodTableRestored(MethodTable * pMethodTable) {}; + static void DynamicMethodDestroyed(MethodDesc *pMethodDesc) {}; +#endif // FEATURE_EVENT_TRACE + }; + + // Class to wrap all Security logic for ETW + class SecurityLog + { +#ifdef FEATURE_EVENT_TRACE + public: + static void StrongNameVerificationStart(DWORD dwInFlags, __in LPWSTR strFullyQualifiedAssemblyName); + static void StrongNameVerificationStop(DWORD dwInFlags,ULONG result, __in LPWSTR strFullyQualifiedAssemblyName); + + static void FireFieldTransparencyComputationStart(LPCWSTR wszFieldName, + LPCWSTR wszModuleName, + DWORD dwAppDomain); + static void FireFieldTransparencyComputationEnd(LPCWSTR wszFieldName, + LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsCritical, + BOOL fIsTreatAsSafe); + + static void FireMethodTransparencyComputationStart(LPCWSTR wszMethodName, + LPCWSTR wszModuleName, + DWORD dwAppDomain); + static void FireMethodTransparencyComputationEnd(LPCWSTR wszMethodName, + LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsCritical, + BOOL fIsTreatAsSafe); + + static void FireModuleTransparencyComputationStart(LPCWSTR wszModuleName, DWORD dwAppDomain); + static void FireModuleTransparencyComputationEnd(LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsAllCritical, + BOOL fIsAllTransparent, + BOOL fIsTreatAsSafe, + BOOL fIsOpportunisticallyCritical, + DWORD dwSecurityRuleSet); + + static void FireTokenTransparencyComputationStart(DWORD dwToken, + LPCWSTR wszModuleName, + DWORD dwAppDomain); + static void FireTokenTransparencyComputationEnd(DWORD dwToken, + LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsCritical, + BOOL fIsTreatAsSafe); + + static void FireTypeTransparencyComputationStart(LPCWSTR wszTypeName, + LPCWSTR wszModuleName, + DWORD dwAppDomain); + static void FireTypeTransparencyComputationEnd(LPCWSTR wszTypeName, + LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsAllCritical, + BOOL fIsAllTransparent, + BOOL fIsCritical, + BOOL fIsTreatAsSafe); +#else + public: + static void StrongNameVerificationStart(DWORD dwInFlags,LPWSTR strFullyQualifiedAssemblyName) {}; + static void StrongNameVerificationStop(DWORD dwInFlags,ULONG result, LPWSTR strFullyQualifiedAssemblyName) {}; + + static void FireFieldTransparencyComputationStart(LPCWSTR wszFieldName, + LPCWSTR wszModuleName, + DWORD dwAppDomain) {}; + static void FireFieldTransparencyComputationEnd(LPCWSTR wszFieldName, + LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsCritical, + BOOL fIsTreatAsSafe) {}; + + static void FireMethodTransparencyComputationStart(LPCWSTR wszMethodName, + LPCWSTR wszModuleName, + DWORD dwAppDomain) {}; + static void FireMethodTransparencyComputationEnd(LPCWSTR wszMethodName, + LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsCritical, + BOOL fIsTreatAsSafe) {}; + + static void FireModuleTransparencyComputationStart(LPCWSTR wszModuleName, DWORD dwAppDomain) {}; + static void FireModuleTransparencyComputationEnd(LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsAllCritical, + BOOL fIsAllTransparent, + BOOL fIsTreatAsSafe, + BOOL fIsOpportunisticallyCritical, + DWORD dwSecurityRuleSet) {}; + + static void FireTokenTransparencyComputationStart(DWORD dwToken, + LPCWSTR wszModuleName, + DWORD dwAppDomain) {}; + static void FireTokenTransparencyComputationEnd(DWORD dwToken, + LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsCritical, + BOOL fIsTreatAsSafe) {}; + + static void FireTypeTransparencyComputationStart(LPCWSTR wszTypeName, + LPCWSTR wszModuleName, + DWORD dwAppDomain) {}; + static void FireTypeTransparencyComputationEnd(LPCWSTR wszTypeName, + LPCWSTR wszModuleName, + DWORD dwAppDomain, + BOOL fIsAllCritical, + BOOL fIsAllTransparent, + BOOL fIsCritical, + BOOL fIsTreatAsSafe) {}; +#endif // FEATURE_EVENT_TRACE + }; + + // Class to wrap all Binder logic for ETW + class BinderLog + { + public: + typedef union _BinderStructs { + typedef enum _NGENBINDREJECT_REASON { + NGEN_BIND_START_BIND = 0, + NGEN_BIND_NO_INDEX = 1, + NGEN_BIND_SYSTEM_ASSEMBLY_NOT_AVAILABLE = 2, + NGEN_BIND_NO_NATIVE_IMAGE = 3, + NGEN_BIND_REJECT_CONFIG_MASK = 4, + NGEN_BIND_FAIL = 5, + NGEN_BIND_INDEX_CORRUPTION = 6, + NGEN_BIND_REJECT_TIMESTAMP = 7, + NGEN_BIND_REJECT_NATIVEIMAGE_NOT_FOUND = 8, + NGEN_BIND_REJECT_IL_SIG = 9, + NGEN_BIND_REJECT_LOADER_EVAL_FAIL = 10, + NGEN_BIND_MISSING_FOUND = 11, + NGEN_BIND_REJECT_HOSTASM = 12, + NGEN_BIND_REJECT_IL_NOT_FOUND = 13, + NGEN_BIND_REJECT_APPBASE_NOT_FILE = 14, + NGEN_BIND_BIND_DEPEND_REJECT_REF_DEF_MISMATCH = 15, + NGEN_BIND_BIND_DEPEND_REJECT_NGEN_SIG = 16, + NGEN_BIND_APPLY_EXTERNAL_RELOCS_FAILED = 17, + NGEN_BIND_SYSTEM_ASSEMBLY_NATIVEIMAGE_NOT_AVAILABLE = 18, + NGEN_BIND_ASSEMBLY_HAS_DIFFERENT_GRANT = 19, + NGEN_BIND_ASSEMBLY_NOT_DOMAIN_NEUTRAL = 20, + NGEN_BIND_NATIVEIMAGE_VERSION_MISMATCH = 21, + NGEN_BIND_LOADFROM_NOT_ALLOWED = 22, + NGEN_BIND_DEPENDENCY_HAS_DIFFERENT_IDENTITY = 23 + } NGENBINDREJECT_REASON; + } BinderStructs; + }; + + // Class to wrap all Exception logic for ETW + class ExceptionLog + { + public: +#ifdef FEATURE_EVENT_TRACE + static void ExceptionThrown(CrawlFrame *pCf, BOOL bIsReThrownException, BOOL bIsNewException); +#else + static void ExceptionThrown(CrawlFrame *pCf, BOOL bIsReThrownException, BOOL bIsNewException) {}; +#endif // FEATURE_EVENT_TRACE + typedef union _ExceptionStructs + { + typedef enum _ExceptionThrownFlags + { + HasInnerException=0x1, + IsNestedException=0x2, + IsReThrownException=0x4, + IsCSE=0x8, + IsCLSCompliant=0x10 + }ExceptionThrownFlags; + }ExceptionStructs; + }; + // Class to wrap all Contention logic for ETW + class ContentionLog + { + public: + typedef union _ContentionStructs + { + typedef enum _ContentionFlags { + ManagedContention=0, + NativeContention=1 + } ContentionFlags; + } ContentionStructs; + }; + // Class to wrap all Interop logic for ETW + class InteropLog + { + public: + }; + + // Class to wrap all Information logic for ETW + class InfoLog + { + public: + typedef union _InfoStructs + { + typedef enum _StartupMode + { + ManagedExe=0x1, + HostedCLR=0x2, + IJW=0x4, + COMActivated=0x8, + Other=0x10 + }StartupMode; + + typedef enum _Sku + { + DesktopCLR=0x1, + CoreCLR=0x2 + }Sku; + + typedef enum _EtwMode + { + Normal=0x0, + Callback=0x1 + }EtwMode; + }InfoStructs; + +#ifdef FEATURE_EVENT_TRACE + static void RuntimeInformation(INT32 type); +#else + static void RuntimeInformation(INT32 type) {}; +#endif // FEATURE_EVENT_TRACE + }; +}; + + +// +// The ONE and only ONE global instantiation of this class +// +extern ETW::CEtwTracer * g_pEtwTracer; +#define ETW_IS_TRACE_ON(level) ( FALSE ) // for fusion which is eventually going to get removed +#define ETW_IS_FLAG_ON(flag) ( FALSE ) // for fusion which is eventually going to get removed + +// Commonly used constats for ETW Assembly Loader and Assembly Binder events. +#define ETWLoadContextNotAvailable (LOADCTX_TYPE_HOSTED + 1) +#define ETWAppDomainIdNotAvailable 0 // Valid AppDomain IDs start from 1 + +#define ETWFieldUnused 0 // Indicates that a particular field in the ETW event payload template is currently unused. + +#define ETWLoaderLoadTypeNotAvailable 0 // Static or Dynamic Load is only valid at LoaderPhaseStart and LoaderPhaseEnd events - for other events, 0 indicates "not available" +#define ETWLoaderStaticLoad 0 // Static reference load +#define ETWLoaderDynamicLoad 1 // Dynamic assembly load + +#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL) && !defined(WINXP_AND_WIN2K3_BUILD_SUPPORT) +// "mc.exe -MOF" already generates this block for XP-suported builds inside ClrEtwAll.h; +// on Vista+ builds, mc is run without -MOF, and we still have code that depends on it, so +// we manually place it here. +FORCEINLINE +BOOLEAN __stdcall +McGenEventTracingEnabled( + __in PMCGEN_TRACE_CONTEXT EnableInfo, + __in PCEVENT_DESCRIPTOR EventDescriptor + ) +{ + + if(!EnableInfo){ + return FALSE; + } + + + // + // Check if the event Level is lower than the level at which + // the channel is enabled. + // If the event Level is 0 or the channel is enabled at level 0, + // all levels are enabled. + // + + if ((EventDescriptor->Level <= EnableInfo->Level) || // This also covers the case of Level == 0. + (EnableInfo->Level == 0)) { + + // + // Check if Keyword is enabled + // + + if ((EventDescriptor->Keyword == (ULONGLONG)0) || + ((EventDescriptor->Keyword & EnableInfo->MatchAnyKeyword) && + ((EventDescriptor->Keyword & EnableInfo->MatchAllKeyword) == EnableInfo->MatchAllKeyword))) { + return TRUE; + } + } + + return FALSE; +} +#endif // defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL) && !defined(WINXP_AND_WIN2K3_BUILD_SUPPORT) + + +#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL) +ETW_INLINE +ULONG +ETW::SamplingLog::SendStackTrace( + MCGEN_TRACE_CONTEXT TraceContext, + PCEVENT_DESCRIPTOR Descriptor, + LPCGUID EventGuid) +{ +#define ARGUMENT_COUNT_CLRStackWalk 5 + ULONG Result = ERROR_SUCCESS; +typedef struct _MCGEN_TRACE_BUFFER { + EVENT_TRACE_HEADER Header; + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_CLRStackWalk]; +} MCGEN_TRACE_BUFFER; + + REGHANDLE RegHandle = TraceContext.RegistrationHandle; + if(!TraceContext.IsEnabled || !McGenEventTracingEnabled(&TraceContext, Descriptor)) + { + return Result; + } + + PVOID *Stack = NULL; + UINT32 FrameCount = 0; + ETW::SamplingLog stackObj; + if(stackObj.GetCurrentThreadsCallStack(&FrameCount, &Stack) == ETW::SamplingLog::Completed) + { + UCHAR Reserved1=0, Reserved2=0; + UINT16 ClrInstanceId = GetClrInstanceId(); + MCGEN_TRACE_BUFFER TraceBuf; + PEVENT_DATA_DESCRIPTOR EventData = TraceBuf.EventData; + + EventDataDescCreate(&EventData[0], &ClrInstanceId, sizeof(const UINT16) ); + + EventDataDescCreate(&EventData[1], &Reserved1, sizeof(const UCHAR) ); + + EventDataDescCreate(&EventData[2], &Reserved2, sizeof(const UCHAR) ); + + EventDataDescCreate(&EventData[3], &FrameCount, sizeof(const unsigned int) ); + + EventDataDescCreate(&EventData[4], Stack, sizeof(PVOID) * FrameCount ); + +#ifdef WINXP_AND_WIN2K3_BUILD_SUPPORT + if (!McGenPreVista) + { + return PfnEventWrite(RegHandle, Descriptor, ARGUMENT_COUNT_CLRStackWalk, EventData); + } + else + { + const MCGEN_TRACE_CONTEXT* Context = (const MCGEN_TRACE_CONTEXT*)(ULONG_PTR)RegHandle; + // + // Fill in header fields + // + + TraceBuf.Header.GuidPtr = (ULONGLONG)EventGuid; + TraceBuf.Header.Flags = WNODE_FLAG_TRACED_GUID |WNODE_FLAG_USE_GUID_PTR|WNODE_FLAG_USE_MOF_PTR; + TraceBuf.Header.Class.Version = (SHORT)Descriptor->Version; + TraceBuf.Header.Class.Level = Descriptor->Level; + TraceBuf.Header.Class.Type = Descriptor->Opcode; + TraceBuf.Header.Size = sizeof(MCGEN_TRACE_BUFFER); + + return TraceEvent(Context->Logger, &TraceBuf.Header); + } +#else // !WINXP_AND_WIN2K3_BUILD_SUPPORT + return EventWrite(RegHandle, Descriptor, ARGUMENT_COUNT_CLRStackWalk, EventData); +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT + } + return Result; +}; + +#endif // FEATURE_EVENT_TRACE && !FEATURE_PAL + +#ifdef FEATURE_EVENT_TRACE +#ifdef TARGET_X86 +struct CallStackFrame +{ + struct CallStackFrame* m_Next; + SIZE_T m_ReturnAddress; +}; +#endif // TARGET_X86 +#endif // FEATURE_EVENT_TRACE + +#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL) +FORCEINLINE +BOOLEAN __stdcall +McGenEventProviderEnabled( + __in PMCGEN_TRACE_CONTEXT Context, + __in UCHAR Level, + __in ULONGLONG Keyword + ) +{ + if(!Context) { + return FALSE; + } + +#ifdef WINXP_AND_WIN2K3_BUILD_SUPPORT + if(McGenPreVista){ + return ( ((Level <= Context->Level) || (Context->Level == 0)) && + (((ULONG)(Keyword & 0xFFFFFFFF) == 0) || ((ULONG)(Keyword & 0xFFFFFFFF) & Context->Flags))); + } +#endif // WINXP_AND_WIN2K3_BUILD_SUPPORT + + // + // Check if the event Level is lower than the level at which + // the channel is enabled. + // If the event Level is 0 or the channel is enabled at level 0, + // all levels are enabled. + // + + if ((Level <= Context->Level) || // This also covers the case of Level == 0. + (Context->Level == 0)) { + + // + // Check if Keyword is enabled + // + + if ((Keyword == (ULONGLONG)0) || + ((Keyword & Context->MatchAnyKeyword) && + ((Keyword & Context->MatchAllKeyword) == Context->MatchAllKeyword))) { + return TRUE; + } + } + return FALSE; +} +#endif // FEATURE_EVENT_TRACE && !FEATURE_PAL + +#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL) + +// This macro only checks if a provider is enabled +// It does not check the flags and keywords for which it is enabled +#define ETW_PROVIDER_ENABLED(ProviderSymbol) \ + ProviderSymbol##_Context.IsEnabled + +#define FireEtwGCPerHeapHistorySpecial(DataPerHeap, DataSize, ClrInstanceId)\ + MCGEN_ENABLE_CHECK(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, GCPerHeapHistory) ?\ + Etw_GCDataPerHeapSpecial(&GCPerHeapHistory, &GarbageCollectionPrivateId, DataPerHeap, DataSize, ClrInstanceId)\ + : ERROR_SUCCESS\ + +// The GC uses this macro around its heap walk so the TypeSystemLog's crst can be held +// for the duration of the walk (if the ETW client has requested type information). +#define ETW_HEAP_WALK_HOLDER(__fShouldWalkHeapRootsForEtw, __fShouldWalkHeapObjectsForEtw) \ + CrstHolderWithState __crstHolderWithState(ETW::TypeSystemLog::GetHashCrst(), ((__fShouldWalkHeapRootsForEtw) || (__fShouldWalkHeapObjectsForEtw))) + +#else + +// For ETM, we rely on DTrace to do the checking +#define ETW_PROVIDER_ENABLED(ProviderSymbol) TRUE +#define FireEtwGCPerHeapHistorySpecial(DataPerHeap, DataSize, ClrInstanceId) 0 + +#endif // FEATURE_EVENT_TRACE && !FEATURE_PAL + +#endif // !FEATURE_REDHAWK +// These parts of the ETW namespace are common for both FEATURE_REDHAWK and +// !FEATURE_REDHAWK builds. + + +struct ProfilingScanContext; +struct ProfilerWalkHeapContext; +class Object; + +namespace ETW +{ + // Class to wrap the logging of threads (runtime and rundown providers) + class ThreadLog + { + private: + static DWORD GetEtwThreadFlags(Thread * pThread); + + public: + static void FireThreadCreated(Thread * pThread); + static void FireThreadDC(Thread * pThread); + }; +}; + +#ifndef FEATURE_REDHAWK + +#ifdef FEATURE_EVENT_TRACE + +// +// Use this macro at the least before calling the Event Macros +// + +#define ETW_TRACING_INITIALIZED(RegHandle) \ + (g_pEtwTracer && RegHandle) + +// +// Use this macro to check if an event is enabled +// if the fields in the event are not cheap to calculate +// +#define ETW_EVENT_ENABLED(Context, EventDescriptor) \ + (MCGEN_ENABLE_CHECK(Context, EventDescriptor)) + +// +// Use this macro to check if a category of events is enabled +// + +#define ETW_CATEGORY_ENABLED(Context, Level, Keyword) \ + (Context.IsEnabled && McGenEventProviderEnabled(&Context, Level, Keyword)) + + + +// +// Special Handling of Startup events +// + +#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL) && !defined(WINXP_AND_WIN2K3_BUILD_SUPPORT) +// "mc.exe -MOF" already generates this block for XP-suported builds inside ClrEtwAll.h; +// on Vista+ builds, mc is run without -MOF, and we still have code that depends on it, so +// we manually place it here. +ETW_INLINE +ULONG +CoMofTemplate_h( + __in REGHANDLE RegHandle, + __in PCEVENT_DESCRIPTOR Descriptor, + __in_opt LPCGUID EventGuid, + __in const unsigned short ClrInstanceID + ) +{ +#define ARGUMENT_COUNT_h 1 + ULONG Error = ERROR_SUCCESS; +typedef struct _MCGEN_TRACE_BUFFER { + EVENT_TRACE_HEADER Header; + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_h]; +} MCGEN_TRACE_BUFFER; + + MCGEN_TRACE_BUFFER TraceBuf; + PEVENT_DATA_DESCRIPTOR EventData = TraceBuf.EventData; + + EventDataDescCreate(&EventData[0], &ClrInstanceID, sizeof(const unsigned short) ); + + + { + Error = EventWrite(RegHandle, Descriptor, ARGUMENT_COUNT_h, EventData); + + } + +#ifdef MCGEN_CALLOUT +MCGEN_CALLOUT(RegHandle, + Descriptor, + ARGUMENT_COUNT_h, + EventData); +#endif + + return Error; +} +#endif // defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL) && !defined(WINXP_AND_WIN2K3_BUILD_SUPPORT) + +class ETWTraceStartup { +#ifndef FEATURE_DTRACE + REGHANDLE TraceHandle; + PCEVENT_DESCRIPTOR EventStartDescriptor; + LPCGUID EventStartGuid; + PCEVENT_DESCRIPTOR EventEndDescriptor; + LPCGUID EventEndGuid; +public: + ETWTraceStartup(REGHANDLE _TraceHandle, PCEVENT_DESCRIPTOR _EventStartDescriptor, LPCGUID _EventStartGuid, PCEVENT_DESCRIPTOR _EventEndDescriptor, LPCGUID _EventEndGuid) { + TraceHandle = _TraceHandle; + EventStartDescriptor = _EventStartDescriptor; + EventEndDescriptor = _EventEndDescriptor; + EventStartGuid = _EventStartGuid; + EventEndGuid = _EventEndGuid; + StartupTraceEvent(TraceHandle, EventStartDescriptor, EventStartGuid); + } + ~ETWTraceStartup() { + StartupTraceEvent(TraceHandle, EventEndDescriptor, EventEndGuid); + } + static void StartupTraceEvent(REGHANDLE _TraceHandle, PCEVENT_DESCRIPTOR _EventDescriptor, LPCGUID _EventGuid) { + EVENT_DESCRIPTOR desc = *_EventDescriptor; + if(ETW_TRACING_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, desc)) + { +#ifndef FEATURE_PAL + CoMofTemplate_h(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context.RegistrationHandle, _EventDescriptor, _EventGuid, GetClrInstanceId()); +#endif // !FEATURE_PAL + } + } +#else //!FEATURE_DTRACE + void (*startFP)(); + void (*endFP)(); +public: + ETWTraceStartup(void (*sFP)(), void (*eFP)()) : startFP (sFP), endFP(eFP) { + (*startFP)(); + } + ~ETWTraceStartup() { + (*endFP)(); + } +#endif //!FEATURE_DTRACE +}; + + + +#else // FEATURE_EVENT_TRACE + +#define ETWOnStartup(StartEventName, EndEventName) +#define ETWFireEvent(EventName) + +// Use this macro at the least before calling the Event Macros +#define ETW_TRACING_INITIALIZED(RegHandle) (FALSE) + +// Use this macro to check if an event is enabled +// if the fields in the event are not cheap to calculate +#define ETW_EVENT_ENABLED(Context, EventDescriptor) (FALSE) + +// Use this macro to check if a category of events is enabled +#define ETW_CATEGORY_ENABLED(Context, Level, Keyword) (FALSE) + +// Use this macro to check if ETW is initialized and the event is enabled +#define ETW_TRACING_ENABLED(Context, EventDescriptor) (FALSE) + +// Use this macro to check if ETW is initialized and the category is enabled +#define ETW_TRACING_CATEGORY_ENABLED(Context, Level, Keyword) (FALSE) + +#endif // FEATURE_EVENT_TRACE + +#endif // FEATURE_REDHAWK + +#endif //_ETWTRACER_HXX_ diff --git a/src/coreclr/nativeaot/Runtime/eventtracepriv.h b/src/coreclr/nativeaot/Runtime/eventtracepriv.h new file mode 100644 index 00000000000000..3e425cdd881a87 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/eventtracepriv.h @@ -0,0 +1,213 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: eventtracepriv.h +// +// Contains some private definitions used by eventrace.cpp, but that aren't needed by +// clients of eventtrace.cpp, and thus don't belong in eventtrace.h. Also, since +// inclusions of this file are tightly controlled (basically just by eventtrace.cpp), we +// can assume some classes are defined that aren't necessarily defined when eventtrace.h +// is #included (e.g., StackSString and StackSArray). +// +// ============================================================================ + +#ifndef __EVENTTRACEPRIV_H__ +#define __EVENTTRACEPRIV_H__ + +#ifdef FEATURE_REDHAWK +#include "holder.h" +#endif // FEATURE_REDHAWK + +#ifndef _countof +#define _countof(_array) (sizeof(_array)/sizeof(_array[0])) +#endif + +const UINT cbMaxEtwEvent = 64 * 1024; + +//--------------------------------------------------------------------------------------- +// C++ copies of ETW structures +//--------------------------------------------------------------------------------------- + +// !!!!!!! NOTE !!!!!!!! +// The EventStruct* structs are described in the ETW manifest event templates, and the +// LAYOUT MUST MATCH THE MANIFEST EXACTLY! +// !!!!!!! NOTE !!!!!!!! + +#pragma pack(push, 1) + +struct EventStructGCBulkRootEdgeValue +{ + LPVOID RootedNodeAddress; + BYTE GCRootKind; + DWORD GCRootFlag; + LPVOID GCRootID; +}; + +struct EventStructGCBulkRootConditionalWeakTableElementEdgeValue +{ + LPVOID GCKeyNodeID; + LPVOID GCValueNodeID; + LPVOID GCRootID; +}; + +struct EventStructGCBulkNodeValue +{ + LPVOID Address; + ULONGLONG Size; + ULONGLONG TypeID; + ULONGLONG EdgeCount; +}; + +struct EventStructGCBulkEdgeValue +{ + LPVOID Value; + ULONG ReferencingFieldID; +}; + +struct EventStructGCBulkSurvivingObjectRangesValue +{ + LPVOID RangeBase; + ULONGLONG RangeLength; +}; + +struct EventStructGCBulkMovedObjectRangesValue +{ + LPVOID OldRangeBase; + LPVOID NewRangeBase; + ULONGLONG RangeLength; +}; + +// This only contains the fixed-size data at the top of each struct in +// the bulk type event. These fields must still match exactly the initial +// fields of the struct described in the manifest. +struct EventStructBulkTypeFixedSizedData +{ + ULONGLONG TypeID; + ULONGLONG ModuleID; + ULONG TypeNameID; + ULONG Flags; + BYTE CorElementType; +}; + +#pragma pack(pop) + + + +// Represents one instance of the Value struct inside a single BulkType event +class BulkTypeValue +{ +public: + BulkTypeValue(); + void Clear(); + + // How many bytes will this BulkTypeValue take up when written into the actual ETW + // event? + int GetByteCountInEvent() + { + return + sizeof(fixedSizedData) + + sizeof(cTypeParameters) + +#ifdef FEATURE_REDHAWK + sizeof(WCHAR) + // No name in event, so just the null terminator + cTypeParameters * sizeof(ULONGLONG); // Type parameters +#else + (sName.GetCount() + 1) * sizeof(WCHAR) + // Size of name, including null terminator + rgTypeParameters.GetCount() * sizeof(ULONGLONG);// Type parameters +#endif + } + + EventStructBulkTypeFixedSizedData fixedSizedData; + + // Below are the remainder of each struct in the bulk type event (i.e., the + // variable-sized data). The var-sized fields are copied into the event individually + // (not directly), so they don't need to have the same layout as in the ETW manifest + + // This is really a denorm of the size already stored in rgTypeParameters, but we + // need a persistent place to stash this away so EventDataDescCreate & EventWrite + // have a reliable place to copy it from. This is filled in at the last minute, + // when sending the event. (On ProjectN, which doesn't have StackSArray, this is + // filled in earlier and used in more places.) + ULONG cTypeParameters; + +#ifdef FEATURE_REDHAWK + // If > 1 type parameter, this is an array of their MethodTable*'s + NewArrayHolder rgTypeParameters; + + // If exactly one type parameter, this is its MethodTable*. (If != 1 type parameter, + // this is 0.) + ULONGLONG ullSingleTypeParameter; +#else // FEATURE_REDHAWK + StackSString sName; + StackSArray rgTypeParameters; +#endif // FEATURE_REDHAWK +}; + +// Encapsulates all the type event batching we need to do. This is used by +// ETW::TypeSystemLog, which calls LogTypeAndParameters for each type to be logged. +// BulkTypeEventLogger will batch each type and its generic type parameters, and flush to +// ETW as necessary. ETW::TypeSystemLog also calls FireBulkTypeEvent directly to force a +// flush (e.g., once at end of GC heap traversal, or on each object allocation). +class BulkTypeEventLogger +{ +private: + + // Estimate of how many bytes we can squeeze in the event data for the value struct + // array. (Intentionally overestimate the size of the non-array parts to keep it safe.) + static const int kMaxBytesTypeValues = (cbMaxEtwEvent - 0x30); + + // Estimate of how many type value elements we can put into the struct array, while + // staying under the ETW event size limit. Note that this is impossible to calculate + // perfectly, since each element of the struct array has variable size. + // + // In addition to the byte-size limit per event, Windows always forces on us a + // max-number-of-descriptors per event, which in the case of BulkType, will kick in + // far sooner. There's a max number of 128 descriptors allowed per event. 2 are used + // for Count + ClrInstanceID. Then 4 per batched value. (Might actually be 3 if there + // are no type parameters to log, but let's overestimate at 4 per value). + static const int kMaxCountTypeValues = (128 - 2) / 4; + // Note: This results in a relatively small batch (about 31 types per event). We + // could increase this substantially by creating a single, contiguous buffer, which + // would let us max out the number of type values to batch by allowing the byte-size + // limit to kick in before the max-descriptor limit. We could esimate that as + // follows: + // + // static const int kMaxCountTypeValues = kMaxBytesTypeValues / + // (sizeof(EventStructBulkTypeFixedSizedData) + + // 200 * sizeof(WCHAR) + // Assume 199 + 1 terminating-NULL character in type name + // sizeof(UINT) + // Type parameter count + // 10 * sizeof(ULONGLONG)); // Assume 10 type parameters + // + // The downside, though, is that we would have to do a lot more copying to fill out + // that buffer before sending the event. It's unclear that increasing the batch size + // is enough of a win to offset all the extra buffer copying. So for now, we'll keep + // the batch size low and avoid extra copying. + + // How many types have we batched? + int m_nBulkTypeValueCount; + + // What is the byte size of all the types we've batched? + int m_nBulkTypeValueByteCount; + + // List of types we've batched. + BulkTypeValue m_rgBulkTypeValues[kMaxCountTypeValues]; + +#ifdef FEATURE_REDHAWK + int LogSingleType(MethodTable * pEEType); +#else + int LogSingleType(TypeHandle th); +#endif + +public: + BulkTypeEventLogger() : + m_nBulkTypeValueCount(0), + m_nBulkTypeValueByteCount(0) + { + LIMITED_METHOD_CONTRACT; + } + + void LogTypeAndParameters(ULONGLONG thAsAddr, ETW::TypeSystemLog::TypeLogBehavior typeLogBehavior); + void FireBulkTypeEvent(); + void Cleanup(); +}; + +#endif // __EVENTTRACEPRIV_H__ diff --git a/src/coreclr/nativeaot/Runtime/forward_declarations.h b/src/coreclr/nativeaot/Runtime/forward_declarations.h new file mode 100644 index 00000000000000..55e8b376eb454f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/forward_declarations.h @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This file may be included by header files to forward declare common +// public types. The intent here is that .CPP files should need to +// include fewer header files. + +#define FWD_DECL(x) \ + class x; \ + typedef DPTR(x) PTR_##x; + +// rtu +FWD_DECL(AllocHeap) +FWD_DECL(CObjectHeader) +FWD_DECL(CLREventStatic) +FWD_DECL(CrstHolder) +FWD_DECL(CrstStatic) +FWD_DECL(EEMethodInfo) +FWD_DECL(EECodeManager) +FWD_DECL(EEThreadId) +FWD_DECL(MethodInfo) +FWD_DECL(Module) +FWD_DECL(Object) +FWD_DECL(OBJECTHANDLEHolder) +FWD_DECL(PageEntry) +FWD_DECL(PAL_EnterHolder) +FWD_DECL(PAL_LeaveHolder) +FWD_DECL(SpinLock) +FWD_DECL(RCOBJECTHANDLEHolder) +FWD_DECL(RedhawkGCInterface) +FWD_DECL(RtuObjectRef) +FWD_DECL(RuntimeInstance) +FWD_DECL(StackFrameIterator) +FWD_DECL(SyncClean) +FWD_DECL(SyncState) +FWD_DECL(Thread) +FWD_DECL(ThreadStore) + +#ifdef FEATURE_RWX_MEMORY +namespace rh { + namespace util { + FWD_DECL(MemRange) + FWD_DECL(MemAccessMgr) + FWD_DECL(WriteAccessHolder) + } +} +#endif // FEATURE_RWX_MEMORY + +// inc +FWD_DECL(EEInterfaceInfo) +FWD_DECL(MethodTable) + diff --git a/src/coreclr/nativeaot/Runtime/gcdump.cpp b/src/coreclr/nativeaot/Runtime/gcdump.cpp new file mode 100644 index 00000000000000..51169cfc15c337 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gcdump.cpp @@ -0,0 +1,709 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +/***************************************************************************** + * GCDump.cpp + * + * Defines functions to display the GCInfo as defined by the GC-encoding + * spec. The GC information may be either dynamically created by a + * Just-In-Time compiler conforming to the standard code-manager spec, + * or may be persisted by a managed native code compiler conforming + * to the standard code-manager spec. + */ +#include "common.h" + +#if (defined(_DEBUG) || defined(DACCESS_COMPILE)) + +#include "gcenv.h" +#include "varint.h" +#include "gcinfo.h" +#include "gcdump.h" + +/*****************************************************************************/ + +#ifdef DACCESS_COMPILE +static void DacNullPrintf(const char* , ...) {} +#endif + +GCDump::GCDump() +{ +#ifndef DACCESS_COMPILE + // By default, use the standard printf function to dump + GCDump::gcPrintf = (printfFtn) ::printf; +#else + // Default for DAC is a no-op. + GCDump::gcPrintf = DacNullPrintf; +#endif +} + + + +/*****************************************************************************/ + +static const char * const calleeSaveRegMaskBitNumberToName[] = +{ +#if defined(TARGET_X86) + "EBX", + "ESI", + "EDI", + "EBP", +#elif defined(TARGET_AMD64) + "RBX", + "RSI", + "RDI", + "RBP", + "R12", + "R13", + "R14", + "R15" +#elif defined(TARGET_ARM) + "R4", + "R5", + "R6", + "R7", + "R8", + "R9", + "R10", + "R11", + "LR", +#elif defined(TARGET_ARM64) + "LR", + "X19", + "X20", + "X21", + "X22", + "X23", + "X24", + "X25", + "X26", + "X27", + "X28", + "FP", +#else +#error unknown architecture +#endif +}; + +char const * GetReturnKindString(GCInfoHeader::MethodReturnKind returnKind) +{ + switch (returnKind) + { + case GCInfoHeader::MRK_ReturnsScalar: return "scalar"; + case GCInfoHeader::MRK_ReturnsObject: return "object"; + case GCInfoHeader::MRK_ReturnsByref: return "byref"; + case GCInfoHeader::MRK_ReturnsToNative: return "native"; +#if defined(TARGET_ARM64) + case GCInfoHeader::MRK_Scalar_Obj: return "{scalar, object}"; + case GCInfoHeader::MRK_Obj_Obj: return "{object, object}"; + case GCInfoHeader::MRK_Byref_Obj: return "{byref, object}"; + case GCInfoHeader::MRK_Scalar_Byref: return "{scalar, byref}"; + case GCInfoHeader::MRK_Obj_Byref: return "{object, byref}"; + case GCInfoHeader::MRK_Byref_Byref: return "{byref, byref}"; +#endif // defined(TARGET_ARM64) + default: return "???"; + } +} + +char const * GetFramePointerRegister() +{ +#if defined(TARGET_X86) + return "EBP"; +#elif defined(TARGET_AMD64) + return "RBP"; +#elif defined(TARGET_ARM) + return "R7"; +#elif defined(TARGET_ARM64) + return "FP"; +#else +#error unknown architecture +#endif +} + +char const * GetStackPointerRegister() +{ +#if defined(TARGET_X86) + return "ESP"; +#elif defined(TARGET_AMD64) + return "RSP"; +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) + return "SP"; +#else +#error unknown architecture +#endif +} + +size_t FASTCALL GCDump::DumpInfoHeader (PTR_UInt8 gcInfo, + Tables * pTables, + GCInfoHeader * pHeader /* OUT */ + ) +{ + size_t headerSize = 0; + PTR_UInt8 gcInfoStart = gcInfo; + PTR_UInt8 pbStackChanges = 0; + PTR_UInt8 pbUnwindInfo = 0; + + unsigned unwindInfoBlobOffset = VarInt::ReadUnsigned(gcInfo); + bool inlineUnwindInfo = (unwindInfoBlobOffset == 0); + + if (inlineUnwindInfo) + { + // it is inline.. + pbUnwindInfo = gcInfo; + } + else + { + // The offset was adjusted by 1 to reserve the 0 encoding for the inline case, so we re-adjust it to + // the actual offset here. + pbUnwindInfo = pTables->pbUnwindInfoBlob + unwindInfoBlobOffset - 1; + } + + // @TODO: decode all funclet headers as well. + pbStackChanges = pHeader->DecodeHeader(0, pbUnwindInfo, &headerSize ); + + if (inlineUnwindInfo) + gcInfo += headerSize; + + unsigned epilogCount = pHeader->GetEpilogCount(); + bool epilogAtEnd = pHeader->IsEpilogAtEnd(); + + gcPrintf(" prologSize: %d\n", pHeader->GetPrologSize()); + if (pHeader->HasVaryingEpilogSizes()) + gcPrintf(" epilogSize: (varies)\n"); + else + gcPrintf(" epilogSize: %d\n", pHeader->GetFixedEpilogSize()); + + gcPrintf(" epilogCount: %d %s\n", epilogCount, epilogAtEnd ? "[end]" : ""); + gcPrintf(" returnKind: %s\n", GetReturnKindString(pHeader->GetReturnKind())); + gcPrintf(" frameKind: %s", pHeader->HasFramePointer() ? GetFramePointerRegister() : GetStackPointerRegister()); +#ifdef TARGET_AMD64 + if (pHeader->HasFramePointer()) + gcPrintf(" offset: %d", pHeader->GetFramePointerOffset()); +#endif // HOST_AMD64 + gcPrintf("\n"); + gcPrintf(" frameSize: %d\n", pHeader->GetFrameSize()); + + if (pHeader->HasDynamicAlignment()) { + gcPrintf(" alignment: %d\n", (1 << pHeader->GetDynamicAlignment())); + if (pHeader->GetParamPointerReg() != RN_NONE) { + gcPrintf(" paramReg: %d\n", pHeader->GetParamPointerReg()); + } + } + + gcPrintf(" savedRegs: "); + CalleeSavedRegMask savedRegs = pHeader->GetSavedRegs(); + CalleeSavedRegMask mask = (CalleeSavedRegMask) 1; + for (int i = 0; i < RBM_CALLEE_SAVED_REG_COUNT; i++) + { + if (savedRegs & mask) + { + gcPrintf("%s ", calleeSaveRegMaskBitNumberToName[i]); + } + mask = (CalleeSavedRegMask)(mask << 1); + } + gcPrintf("\n"); + +#ifdef TARGET_ARM + gcPrintf(" parmRegsPushedCount: %d\n", pHeader->ParmRegsPushedCount()); +#endif + +#ifdef TARGET_X86 + gcPrintf(" returnPopSize: %d\n", pHeader->GetReturnPopSize()); + if (pHeader->HasStackChanges()) + { + // @TODO: need to read the stack changes string that follows + ASSERT(!"NYI -- stack changes for ESP frames"); + } +#endif + + if (pHeader->ReturnsToNative()) + { + gcPrintf(" reversePinvokeFrameOffset: 0x%02x\n", pHeader->GetReversePinvokeFrameOffset()); + } + + + if (!epilogAtEnd && !pHeader->IsFunclet()) + { + gcPrintf(" epilog offsets: "); + unsigned previousOffset = 0; + for (unsigned idx = 0; idx < epilogCount; idx++) + { + unsigned newOffset = previousOffset + VarInt::ReadUnsigned(gcInfo); + gcPrintf("0x%04x ", newOffset); + if (pHeader->HasVaryingEpilogSizes()) + gcPrintf("(%u bytes) ", VarInt::ReadUnsigned(gcInfo)); + previousOffset = newOffset; + } + gcPrintf("\n"); + } + + return gcInfo - gcInfoStart; +} + +// TODO: Can we unify this code with ReportLocalSlot in RHCodeMan.cpp? +void GCDump::PrintLocalSlot(uint32_t slotNum, GCInfoHeader const * pHeader) +{ + char const * baseReg; + int32_t offset; + + if (pHeader->HasFramePointer()) + { + baseReg = GetFramePointerRegister(); +#ifdef TARGET_ARM + offset = pHeader->GetFrameSize() - ((slotNum + 1) * POINTER_SIZE); +#elif defined(TARGET_ARM64) + if (pHeader->AreFPLROnTop()) + { + offset = -(int32_t)((slotNum + 1) * POINTER_SIZE); + } + else + { + offset = (slotNum + 2) * POINTER_SIZE; + } +#elif defined(TARGET_X86) + offset = -pHeader->GetPreservedRegsSaveSize() - (slotNum * POINTER_SIZE); +#elif defined(TARGET_AMD64) + if (pHeader->GetFramePointerOffset() == 0) + { + offset = -pHeader->GetPreservedRegsSaveSize() - (slotNum * POINTER_SIZE); + } + else + { + offset = (slotNum * POINTER_SIZE); + } +#else +#error unknown architecture +#endif + } + else + { + baseReg = GetStackPointerRegister(); + offset = pHeader->GetFrameSize() - ((slotNum + 1) * POINTER_SIZE); + } + + char const * sign = "+"; + if (offset < 0) + { + sign = "-"; + offset = -offset; + } + gcPrintf("local slot 0n%d, [%s%s%02X]\n", slotNum, baseReg, sign, offset); +} + +// Reads a 7-bit-encoded register mask: +// - 0RRRRRRR for non-ARM64 registers and { x0-x6 } ARM64 registers +// - 1RRRRRRR 0RRRRRRR for { x0-x13 } ARM64 registers +// - 1RRRRRRR 1RRRRRRR 000RRRRR for { x0-x15, xip0, xip1, lr } ARM64 registers +// Returns the number of bytes read. +size_t ReadRegisterMaskBy7Bit(PTR_UInt8 pCursor, uint32_t* pMask) +{ + uint32_t byte0 = *pCursor; + if (!(byte0 & 0x80)) + { + *pMask = byte0; + return 1; + } + +#if defined(TARGET_ARM64) + uint32_t byte1 = *(pCursor + 1); + if (!(byte1 & 0x80)) + { + // XOR with 0x80 discards the most significant bit of byte0 + *pMask = (byte1 << 7) ^ byte0 ^ 0x80; + return 2; + } + + uint32_t byte2 = *(pCursor + 2); + if (!(byte2 & 0x80)) + { + // XOR with 0x4080 discards the most significant bits of byte0 and byte1 + *pMask = (byte2 << 14) ^ (byte1 << 7) ^ byte0 ^ 0x4080; + return 3; + } +#endif + + UNREACHABLE_MSG("Register mask is too long"); +} + +void GCDump::DumpCallsiteString(uint32_t callsiteOffset, PTR_UInt8 pbCallsiteString, + GCInfoHeader const * pHeader) +{ + gcPrintf("%04x: ", callsiteOffset); + + int count = 0; + uint8_t b; + PTR_UInt8 pCursor = pbCallsiteString; + + bool last = false; + bool first = true; + + do + { + if (!first) + gcPrintf(" "); + + first = false; + + b = *pCursor++; + last = ((b & 0x20) == 0x20); + + switch (b & 0xC0) + { + case 0x00: + { + // case 2 -- "register set" + gcPrintf("%02x | 2 ", b); +#ifdef TARGET_ARM + if (b & CSR_MASK_R4) { gcPrintf("R4 "); count++; } + if (b & CSR_MASK_R5) { gcPrintf("R5 "); count++; } + if (b & CSR_MASK_R6) { gcPrintf("R6 "); count++; } + if (b & CSR_MASK_R7) { gcPrintf("R7 "); count++; } + if (b & CSR_MASK_R8) { gcPrintf("R8 "); count++; } +#elif defined(TARGET_ARM64) + uint16_t regs = (b & 0xF); + if (b & 0x10) { regs |= (*pCursor++ << 4); } + + ASSERT(!(regs & CSR_MASK_LR)); + if (regs & CSR_MASK_X19) { gcPrintf("X19 "); count++; } + if (regs & CSR_MASK_X20) { gcPrintf("X20 "); count++; } + if (regs & CSR_MASK_X21) { gcPrintf("X21 "); count++; } + if (regs & CSR_MASK_X22) { gcPrintf("X22 "); count++; } + if (regs & CSR_MASK_X23) { gcPrintf("X23 "); count++; } + if (regs & CSR_MASK_X24) { gcPrintf("X24 "); count++; } + if (regs & CSR_MASK_X25) { gcPrintf("X25 "); count++; } + if (regs & CSR_MASK_X26) { gcPrintf("X26 "); count++; } + if (regs & CSR_MASK_X27) { gcPrintf("X27 "); count++; } + if (regs & CSR_MASK_X28) { gcPrintf("X28 "); count++; } + if (regs & CSR_MASK_FP ) { gcPrintf("FP " ); count++; } +#elif defined(TARGET_AMD64) + if (b & CSR_MASK_RBX) { gcPrintf("RBX "); count++; } + if (b & CSR_MASK_RSI) { gcPrintf("RSI "); count++; } + if (b & CSR_MASK_RDI) { gcPrintf("RDI "); count++; } + if (b & CSR_MASK_RBP) { gcPrintf("RBP "); count++; } + if (b & CSR_MASK_R12) { gcPrintf("R12 "); count++; } +#elif defined(TARGET_X86) + if (b & CSR_MASK_RBX) { gcPrintf("EBX "); count++; } + if (b & CSR_MASK_RSI) { gcPrintf("ESI "); count++; } + if (b & CSR_MASK_RDI) { gcPrintf("EDI "); count++; } + if (b & CSR_MASK_RBP) { gcPrintf("EBP "); count++; } +#else +#error unknown architecture +#endif + gcPrintf("\n"); + } + break; + + case 0x40: + { + // case 3 -- "register" + const char* regName = "???"; + const char* interior = (b & 0x10) ? "+" : ""; + const char* pinned = (b & 0x08) ? "!" : ""; + + switch (b & 0x7) + { +#ifdef TARGET_ARM + case CSR_NUM_R4: regName = "R4"; break; + case CSR_NUM_R5: regName = "R5"; break; + case CSR_NUM_R6: regName = "R6"; break; + case CSR_NUM_R7: regName = "R7"; break; + case CSR_NUM_R8: regName = "R8"; break; + case CSR_NUM_R9: regName = "R9"; break; + case CSR_NUM_R10: regName = "R10"; break; + case CSR_NUM_R11: regName = "R11"; break; +#elif defined(TARGET_ARM64) + case CSR_NUM_X19: regName = "X19"; break; + case CSR_NUM_X20: regName = "X20"; break; + case CSR_NUM_X21: regName = "X21"; break; + case CSR_NUM_X22: regName = "X22"; break; + case CSR_NUM_X23: regName = "X23"; break; + case CSR_NUM_X24: regName = "X24"; break; + case CSR_NUM_X25: regName = "X25"; break; + case 0: + switch (*pCursor++) + { + case CSR_NUM_X26: regName = "X26"; break; + case CSR_NUM_X27: regName = "X27"; break; + case CSR_NUM_X28: regName = "X28"; break; + case CSR_NUM_FP : regName = "FP" ; break; + } + break; +#elif defined(TARGET_AMD64) + case CSR_NUM_RBX: regName = "RBX"; break; + case CSR_NUM_RSI: regName = "RSI"; break; + case CSR_NUM_RDI: regName = "RDI"; break; + case CSR_NUM_RBP: regName = "RBP"; break; + case CSR_NUM_R12: regName = "R12"; break; + case CSR_NUM_R13: regName = "R13"; break; + case CSR_NUM_R14: regName = "R14"; break; + case CSR_NUM_R15: regName = "R15"; break; +#elif defined(TARGET_X86) + case CSR_NUM_RBX: regName = "EBX"; break; + case CSR_NUM_RSI: regName = "ESI"; break; + case CSR_NUM_RDI: regName = "EDI"; break; + case CSR_NUM_RBP: regName = "EBP"; break; +#else +#error unknown architecture +#endif + } + gcPrintf("%02x | 3 %s%s%s \n", b, regName, interior, pinned); + count++; + } + break; + + case 0x80: + { + if (b & 0x10) + { + // case 4 -- "local slot set" or "common var tail" + if ((b & 0x0f) != 0) + { + gcPrintf("%02x | 4 ", b); + bool isFirst = true; + + int mask = 0x01; + int slotNum = 0; + while (mask <= 0x08) + { + if (b & mask) + { + if (!isFirst) + { + if (!first) + gcPrintf(" "); + gcPrintf(" | "); + } + + PrintLocalSlot(slotNum, pHeader); + + isFirst = false; + count++; + } + mask <<= 1; + slotNum++; + } + } + else + { + unsigned commonVarInx = 0; + if ((b & 0x20) == 0) + commonVarInx = VarInt::ReadUnsigned(pCursor); + + gcPrintf("%02x | 8 set #%04u\n", b, commonVarInx); + } + } + else + { + // case 5 -- "local slot" + int slotNum = (int)(b & 0xF) + 4; + gcPrintf("%02x | 5 ", b); + PrintLocalSlot(slotNum, pHeader); + + count++; + } + } + break; + case 0xC0: + { + if ((b & 0xC7) == 0xC2) + { + // case 7 - live scratch regs + gcPrintf("%02x | 7 ", b); + + uint32_t regs, byrefRegs = 0, pinnedRegs = 0; + pCursor += ReadRegisterMaskBy7Bit(pCursor, ®s); + if (b & 0x10) + pCursor += ReadRegisterMaskBy7Bit(pCursor, &byrefRegs); + if (b & 0x08) + pCursor += ReadRegisterMaskBy7Bit(pCursor, &pinnedRegs); + + for (uint32_t reg = 0; ; reg++) + { + uint32_t regMask = (1 << reg); + if (regMask > regs) + break; + + if (regs & regMask) + { + char* pinned = (pinnedRegs & regMask) ? "!" : ""; + char* interior = (byrefRegs & regMask) ? "+" : ""; + char* regStr = "???"; + + switch (reg) + { +#if defined(TARGET_ARM) + case SR_NUM_R0: regStr = "R0"; break; + case SR_NUM_R1: regStr = "R1"; break; + case SR_NUM_R2: regStr = "R2"; break; + case SR_NUM_R3: regStr = "R3"; break; + case SR_NUM_R12: regStr = "R12"; break; + case SR_NUM_LR: regStr = "LR"; break; +#elif defined(TARGET_ARM64) + case SR_NUM_X0: regStr = "X0"; break; + case SR_NUM_X1: regStr = "X1"; break; + case SR_NUM_X2: regStr = "X2"; break; + case SR_NUM_X3: regStr = "X3"; break; + case SR_NUM_X4: regStr = "X4"; break; + case SR_NUM_X5: regStr = "X5"; break; + case SR_NUM_X6: regStr = "X6"; break; + case SR_NUM_X7: regStr = "X7"; break; + case SR_NUM_X8: regStr = "X8"; break; + case SR_NUM_X9: regStr = "X9"; break; + case SR_NUM_X10: regStr = "X10"; break; + case SR_NUM_X11: regStr = "X11"; break; + case SR_NUM_X12: regStr = "X12"; break; + case SR_NUM_X13: regStr = "X13"; break; + case SR_NUM_X14: regStr = "X14"; break; + case SR_NUM_X15: regStr = "X15"; break; + case SR_NUM_XIP0: regStr = "XIP0"; break; + case SR_NUM_XIP1: regStr = "XIP1"; break; + case SR_NUM_LR: regStr = "LR"; break; +#elif defined(TARGET_AMD64) + case SR_NUM_RAX: regStr = "RAX"; break; + case SR_NUM_RCX: regStr = "RCX"; break; + case SR_NUM_RDX: regStr = "RDX"; break; + case SR_NUM_R8: regStr = "R8"; break; + case SR_NUM_R9: regStr = "R9"; break; + case SR_NUM_R10: regStr = "R10"; break; + case SR_NUM_R11: regStr = "R11"; break; +#elif defined(TARGET_X86) + case SR_NUM_RAX: regStr = "EAX"; break; + case SR_NUM_RCX: regStr = "ECX"; break; + case SR_NUM_RDX: regStr = "EDX"; break; +#else +#error unknown architecture +#endif + } + gcPrintf("%s%s%s ", regStr, interior, pinned); + count++; + } + } + } + else + { + // case 6 - stack slot / stack slot set + gcPrintf("%02x ", b); + unsigned mask = 0; + PTR_UInt8 pInts = pCursor; + unsigned offset = VarInt::ReadUnsigned(pCursor); + const char* interior = (b & 0x10) ? "+" : ""; + const char* pinned = (b & 0x08) ? "!" : ""; + const char* baseReg = (b & 0x04) ? GetFramePointerRegister() : GetStackPointerRegister(); + const char* sign = (b & 0x02) ? "-" : "+"; + if (b & 0x01) + { + mask = VarInt::ReadUnsigned(pCursor); + } + + int c = 1; + while (pInts != pCursor) + { + gcPrintf("%02x ", *pInts++); + c++; + } + + for (; c < 4; c++) + { + gcPrintf(" "); + } + + gcPrintf("| 6 [%s%s%02X]%s%s\n", baseReg, sign, offset, interior, pinned); + count++; + + while (mask > 0) + { + offset += POINTER_SIZE; + if (mask & 1) + { + if (!first) + gcPrintf(" "); + + gcPrintf(" | [%s%s%02X]%s%s\n", baseReg, sign, offset, interior, pinned); + count++; + } + mask >>= 1; + } + } + } + break; + } + } + while (!last); + + //gcPrintf("\n"); +} + +size_t FASTCALL GCDump::DumpGCTable (PTR_UInt8 gcInfo, + Tables * pTables, + const GCInfoHeader& header) +{ + PTR_UInt8 pCursor = gcInfo; + + if (header.HasCommonVars()) + { + uint32_t commonVarCount = VarInt::ReadUnsigned(pCursor); + for (uint32_t i = 0; i < commonVarCount; i++) + { + VarInt::SkipUnsigned(pCursor); + } + } + + // + // Decode the method GC info + // + // 0ddddccc -- SMALL ENCODING + // + // -- dddd is an index into the delta shortcut table + // -- ccc is an offset into the callsite strings blob + // + // 1ddddddd { info offset } -- BIG ENCODING + // + // -- ddddddd is a 7-bit delta + // -- { info offset } is a variable-length unsigned encoding of the offset into the callsite + // strings blob for this callsite. + // + // 10000000 { delta } -- FORWARDER + // + // -- { delta } is a variable-length unsigned encoding of the offset to the next callsite + // + // 11111111 -- STRING TERMINATOR + // + + uint32_t curOffset = 0; + + for (;;) + { + uint8_t b = *pCursor++; + unsigned infoOffset; + + if (b & 0x80) + { + uint8_t lowBits = (b & 0x7F); + // FORWARDER + if (lowBits == 0) + { + curOffset += VarInt::ReadUnsigned(pCursor); + continue; + } + else + if (lowBits == 0x7F) // STRING TERMINATOR + break; + + // BIG ENCODING + curOffset += lowBits; + infoOffset = VarInt::ReadUnsigned(pCursor); + } + else + { + // SMALL ENCODING + infoOffset = (b & 0x7); + curOffset += pTables->pbDeltaShortcutTable[b >> 3]; + } + + DumpCallsiteString(curOffset, pTables->pbCallsiteInfoBlob + infoOffset, &header); + } + + gcPrintf("-------\n"); + + return 0; +} + +#endif // _DEBUG || DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/gcdump.h b/src/coreclr/nativeaot/Runtime/gcdump.h new file mode 100644 index 00000000000000..401099cb5a4f02 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gcdump.h @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +/***************************************************************************** + * GCDump.h + * + * Defines functions to display the GCInfo as defined by the GC-encoding + * spec. The GC information may be either dynamically created by a + * Just-In-Time compiler conforming to the standard code-manager spec, + * or may be persisted by a managed native code compiler conforming + * to the standard code-manager spec. + */ + +/*****************************************************************************/ +#ifndef __GCDUMP_H__ +#define __GCDUMP_H__ +/*****************************************************************************/ + +struct GCInfoHeader; + +#ifndef FASTCALL +#define FASTCALL __fastcall +#endif + + +class GCDump +{ +public: + + struct Tables + { + PTR_UInt8 pbDeltaShortcutTable; + PTR_UInt8 pbUnwindInfoBlob; + PTR_UInt8 pbCallsiteInfoBlob; + }; + + + GCDump (); + + /*------------------------------------------------------------------------- + * Dumps the GCInfoHeader to 'stdout' + * gcInfo : Start of the GC info block + * Return value : Size in bytes of the header encoding + */ + + size_t FASTCALL DumpInfoHeader(PTR_UInt8 gcInfo, + Tables * pTables, + GCInfoHeader * header /* OUT */ + ); + + /*------------------------------------------------------------------------- + * Dumps the GC tables to 'stdout' + * gcInfo : Ptr to the start of the table part of the GC info. + * This immediately follows the GCinfo header + * Return value : Size in bytes of the GC table encodings + */ + + size_t FASTCALL DumpGCTable(PTR_UInt8 gcInfo, + Tables * pTables, + const GCInfoHeader& header + ); + + + typedef void (*printfFtn)(const char* fmt, ...); + printfFtn gcPrintf; + + + + //------------------------------------------------------------------------- +protected: + + void PrintLocalSlot(uint32_t slotNum, GCInfoHeader const * pHeader); + void DumpCallsiteString(uint32_t callsiteOffset, PTR_UInt8 pbCallsiteString, GCInfoHeader const * pHeader); +}; + +/*****************************************************************************/ +#endif // __GC_DUMP_H__ +/*****************************************************************************/ diff --git a/src/coreclr/nativeaot/Runtime/gcenv.h b/src/coreclr/nativeaot/Runtime/gcenv.h new file mode 100644 index 00000000000000..5530667b368af3 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gcenv.h @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#ifndef __GCENV_H__ +#define __GCENV_H__ + +#ifdef _MSC_VER +#pragma warning( disable: 4189 ) // 'hp': local variable is initialized but not referenced -- common in GC +#pragma warning( disable: 4127 ) // conditional expression is constant -- common in GC +#endif + +#include +#include +#include +#include +#include + +#include "sal.h" +#include "gcenv.structs.h" +#include "gcenv.interlocked.h" +#include "gcenv.base.h" +#include "gcenv.os.h" + +#include "Crst.h" +#include "event.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "TargetPtrs.h" +#include "MethodTable.h" +#include "ObjectLayout.h" +#include "rheventtrace.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "gcrhinterface.h" +#include "gcenv.interlocked.inl" + +#include "slist.h" +#include "RWLock.h" +#include "shash.h" +#include "TypeManager.h" +#include "RuntimeInstance.h" +#include "MethodTable.inl" +#include "volatile.h" + +#include "gcenv.inl" + +#include "stressLog.h" +#ifdef FEATURE_ETW + + #ifndef _INC_WINDOWS + typedef void* LPVOID; + typedef uint32_t UINT; + typedef void* PVOID; + typedef uint64_t ULONGLONG; + typedef uint32_t ULONG; + typedef int64_t LONGLONG; + typedef uint8_t BYTE; + typedef uint16_t UINT16; + #endif // _INC_WINDOWS + + #include "etwevents.h" + #include "eventtrace.h" + +#else // FEATURE_ETW + + #include "etmdummy.h" + #define ETW_EVENT_ENABLED(e,f) false + +#endif // FEATURE_ETW + +#define LOG(x) + +// Adapter for GC's view of Array +class ArrayBase : Array +{ +public: + DWORD GetNumComponents() + { + return m_Length; + } + + static size_t GetOffsetOfNumComponents() + { + return offsetof(ArrayBase, m_Length); + } +}; + +EXTERN_C uint32_t _tls_index; +inline uint16_t GetClrInstanceId() +{ + return (uint16_t)_tls_index; +} + +class IGCHeap; +typedef DPTR(IGCHeap) PTR_IGCHeap; +typedef DPTR(uint32_t) PTR_uint32_t; + +enum CLRDataEnumMemoryFlags : int; + +/* _TRUNCATE */ +#if !defined (_TRUNCATE) +#define _TRUNCATE ((size_t)-1) +#endif /* !defined (_TRUNCATE) */ + +#endif // __GCENV_H__ diff --git a/src/coreclr/nativeaot/Runtime/gchandleutilities.h b/src/coreclr/nativeaot/Runtime/gchandleutilities.h new file mode 100644 index 00000000000000..a4e6ae384cbc90 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gchandleutilities.h @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _GCHANDLEUTILITIES_H_ +#define _GCHANDLEUTILITIES_H_ + +#include "gcinterface.h" + +extern "C" IGCHandleManager* g_pGCHandleManager; + +class GCHandleUtilities +{ +public: + // Retrieves the GC handle table. + static IGCHandleManager* GetGCHandleManager() + { + LIMITED_METHOD_CONTRACT; + + assert(g_pGCHandleManager != nullptr); + return g_pGCHandleManager; + } + +private: + // This class should never be instantiated. + GCHandleUtilities() = delete; +}; + +// Given a handle, returns an OBJECTREF for the object it refers to. +inline OBJECTREF ObjectFromHandle(OBJECTHANDLE handle) +{ + _ASSERTE(handle); + + // Wrap the raw OBJECTREF and return it + return UNCHECKED_OBJECTREF_TO_OBJECTREF(*PTR_UNCHECKED_OBJECTREF(handle)); +} + +#endif // _GCHANDLEUTILITIES_H_ diff --git a/src/coreclr/nativeaot/Runtime/gcheaputilities.cpp b/src/coreclr/nativeaot/Runtime/gcheaputilities.cpp new file mode 100644 index 00000000000000..1a0125b1316a13 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gcheaputilities.cpp @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" +#include "gcenv.h" +#include "gcheaputilities.h" +#include "gchandleutilities.h" + +#include "gceventstatus.h" + +// This is the global GC heap, maintained by the VM. +GPTR_IMPL(IGCHeap, g_pGCHeap); + +// These globals are variables used within the GC and maintained +// by the EE for use in write barriers. It is the responsibility +// of the GC to communicate updates to these globals to the EE through +// GCToEEInterface::StompWriteBarrier. +GPTR_IMPL_INIT(uint32_t, g_card_table, nullptr); +GPTR_IMPL_INIT(uint8_t, g_lowest_address, nullptr); +GPTR_IMPL_INIT(uint8_t, g_highest_address, nullptr); +GVAL_IMPL_INIT(GCHeapType, g_heap_type, GC_HEAP_INVALID); +uint8_t* g_ephemeral_low = (uint8_t*)1; +uint8_t* g_ephemeral_high = (uint8_t*)~0; + +#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES +uint32_t* g_card_bundle_table = nullptr; +#endif + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +uint8_t* g_write_watch_table = nullptr; +bool g_sw_ww_enabled_for_gc_heap = false; +#endif + +IGCHandleManager* g_pGCHandleManager = nullptr; + +GcDacVars g_gc_dac_vars; +GPTR_IMPL(GcDacVars, g_gcDacGlobals); + +// GC entrypoints for the the linked-in GC. These symbols are invoked +// directly if we are not using a standalone GC. +extern "C" HRESULT GC_Initialize( + /* In */ IGCToCLR* clrToGC, + /* Out */ IGCHeap** gcHeap, + /* Out */ IGCHandleManager** gcHandleManager, + /* Out */ GcDacVars* gcDacVars +); + +#ifndef DACCESS_COMPILE + +// Initializes a non-standalone GC. The protocol for initializing a non-standalone GC +// is similar to loading a standalone one, except that the GC_VersionInfo and +// GC_Initialize symbols are linked to directory and thus don't need to be loaded. +// +HRESULT GCHeapUtilities::InitializeDefaultGC() +{ + // we should only call this once on startup. Attempting to load a GC + // twice is an error. + assert(g_pGCHeap == nullptr); + + IGCHeap* heap; + IGCHandleManager* manager; + HRESULT initResult = GC_Initialize(nullptr, &heap, &manager, &g_gc_dac_vars); + if (initResult == S_OK) + { + g_pGCHeap = heap; + g_pGCHandleManager = manager; + g_gcDacGlobals = &g_gc_dac_vars; + LOG((LF_GC, LL_INFO100, "GC load successful\n")); + } + else + { + LOG((LF_GC, LL_FATALERROR, "GC initialization failed with HR = 0x%X\n", initResult)); + } + + return initResult; +} + +void GCHeapUtilities::RecordEventStateChange(bool isPublicProvider, GCEventKeyword keywords, GCEventLevel level) +{ + // CoreRT does not support standalone GC. Call GCEventStatus directly to keep things simple. + GCEventStatus::Set(isPublicProvider ? GCEventProvider_Default : GCEventProvider_Private, keywords, level); +} + +#endif // DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/gcheaputilities.h b/src/coreclr/nativeaot/Runtime/gcheaputilities.h new file mode 100644 index 00000000000000..140dd9cda18eea --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gcheaputilities.h @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _GCHEAPUTILITIES_H_ +#define _GCHEAPUTILITIES_H_ + +#include "gcinterface.h" +#include "daccess.h" + +// The singular heap instance. +GPTR_DECL(IGCHeap, g_pGCHeap); + +#ifndef DACCESS_COMPILE +extern "C" { +#endif // !DACCESS_COMPILE +GPTR_DECL(uint8_t,g_lowest_address); +GPTR_DECL(uint8_t,g_highest_address); +GPTR_DECL(uint32_t,g_card_table); +GVAL_DECL(GCHeapType, g_heap_type); +#ifndef DACCESS_COMPILE +} +#endif // !DACCESS_COMPILE + +#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES +extern "C" uint32_t* g_card_bundle_table; +#endif // FEATURE_MANUALLY_MANAGED_CARD_BUNDLES + +extern "C" uint8_t* g_ephemeral_low; +extern "C" uint8_t* g_ephemeral_high; + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +extern "C" bool g_sw_ww_enabled_for_gc_heap; +extern "C" uint8_t* g_write_watch_table; +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + + +// g_gc_dac_vars is a structure of pointers to GC globals that the +// DAC uses. It is not exposed directly to the DAC. +extern GcDacVars g_gc_dac_vars; + +// Instead of exposing g_gc_dac_vars to the DAC, a pointer to it +// is exposed here (g_gcDacGlobals). The reason for this is to avoid +// a problem in which a debugger attaches to a program while the program +// is in the middle of initializing the GC DAC vars - if the "publishing" +// of DAC vars isn't atomic, the debugger could see a partially initialized +// GcDacVars structure. +// +// Instead, the debuggee "publishes" GcDacVars by assigning a pointer to g_gc_dac_vars +// to this global, and the DAC will read this global. +typedef DPTR(GcDacVars) PTR_GcDacVars; +GPTR_DECL(GcDacVars, g_gcDacGlobals); + +// GCHeapUtilities provides a number of static methods +// that operate on the global heap instance. It can't be +// instantiated. +class GCHeapUtilities { +public: + // Retrieves the GC heap. + inline static IGCHeap* GetGCHeap() + { + assert(g_pGCHeap != nullptr); + return g_pGCHeap; + } + + // Returns true if the heap has been initialized, false otherwise. + inline static bool IsGCHeapInitialized() + { + return g_pGCHeap != nullptr; + } + + // Returns true if a the heap is initialized and a garbage collection + // is in progress, false otherwise. + inline static BOOL IsGCInProgress(BOOL bConsiderGCStart = FALSE) + { + return GetGCHeap()->IsGCInProgressHelper(bConsiderGCStart); + } + + // Returns true if the held GC heap is a Server GC heap, false otherwise. + inline static bool IsServerHeap() + { + LIMITED_METHOD_CONTRACT; + +#ifdef FEATURE_SVR_GC + _ASSERTE(g_heap_type != GC_HEAP_INVALID); + return (g_heap_type == GC_HEAP_SVR); +#else + return false; +#endif // FEATURE_SVR_GC + } + +#ifndef DACCESS_COMPILE + // Initializes a non-standalone GC. + static HRESULT InitializeDefaultGC(); + + // Records a change in eventing state. This ultimately will inform the GC that it needs to be aware + // of new events being enabled. + static void RecordEventStateChange(bool isPublicProvider, GCEventKeyword keywords, GCEventLevel level); +#endif // DACCESS_COMPILE + +private: + // This class should never be instantiated. + GCHeapUtilities() = delete; +}; + +#endif // _GCHEAPUTILITIES_H_ diff --git a/src/coreclr/nativeaot/Runtime/gcinfodecoder.cpp b/src/coreclr/nativeaot/Runtime/gcinfodecoder.cpp new file mode 100644 index 00000000000000..0d50d12abe9636 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gcinfodecoder.cpp @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#define _common_h_ + +#include "../../vm/gcinfodecoder.cpp" diff --git a/src/coreclr/nativeaot/Runtime/gcinfodecoder.h b/src/coreclr/nativeaot/Runtime/gcinfodecoder.h new file mode 100644 index 00000000000000..4e1de4ce194908 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gcinfodecoder.h @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "../../inc/gcinfodecoder.h" diff --git a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp new file mode 100644 index 00000000000000..8c59b033ed7676 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp @@ -0,0 +1,1567 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This module provides data storage and implementations needed by gcrhenv.h to help provide an isolated build +// and runtime environment in which GC and HandleTable code can exist with minimal modifications from the CLR +// mainline. See gcrhenv.h for a more detailed explanation of how this all fits together. +// + +#include "common.h" + +#include "gcenv.h" +#include "gcheaputilities.h" +#include "gchandleutilities.h" +#include "profheapwalkhelper.h" + +#include "gcenv.ee.h" + +#include "RestrictedCallouts.h" + +#include "gcrhinterface.h" + +#include "slist.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" + +#include "thread.h" + +#include "shash.h" +#include "RWLock.h" +#include "TypeManager.h" +#include "RuntimeInstance.h" +#include "objecthandle.h" +#include "MethodTable.inl" +#include "RhConfig.h" + +#include "threadstore.h" +#include "threadstore.inl" +#include "thread.inl" + +#include "gcdesc.h" +#include "SyncClean.hpp" + +#include "daccess.h" + +#include "GCMemoryHelpers.h" + +#include "holder.h" +#include "volatile.h" + +#ifdef FEATURE_ETW + #ifndef _INC_WINDOWS + typedef void* LPVOID; + typedef uint32_t UINT; + typedef void* PVOID; + typedef uint64_t ULONGLONG; + typedef uint32_t ULONG; + typedef int64_t LONGLONG; + typedef uint8_t BYTE; + typedef uint16_t UINT16; + #endif // _INC_WINDOWS + + #include "etwevents.h" + #include "eventtrace.h" +#else // FEATURE_ETW + #include "etmdummy.h" + #define ETW_EVENT_ENABLED(e,f) false +#endif // FEATURE_ETW + +GPTR_IMPL(MethodTable, g_pFreeObjectEEType); + +#include "gctoclreventsink.h" + +#ifndef DACCESS_COMPILE + +bool RhInitializeFinalization(); +bool RhStartFinalizerThread(); +void RhEnableFinalization(); + +// A few settings are now backed by the cut-down version of Redhawk configuration values. +static RhConfig g_sRhConfig; +RhConfig * g_pRhConfig = &g_sRhConfig; + +#ifdef FEATURE_ETW +// +// ----------------------------------------------------------------------------------------------------------- +// +// The automatically generated part of the Redhawk ETW infrastructure (EtwEvents.h) calls the following +// function whenever the system enables or disables tracing for this provider. +// + +uint32_t EtwCallback(uint32_t IsEnabled, RH_ETW_CONTEXT * pContext) +{ + GCHeapUtilities::RecordEventStateChange(!!(pContext->RegistrationHandle == Microsoft_Windows_Redhawk_GC_PublicHandle), + static_cast(pContext->MatchAnyKeyword), + static_cast(pContext->Level)); + + if (IsEnabled && + (pContext->RegistrationHandle == Microsoft_Windows_Redhawk_GC_PrivateHandle) && + GCHeapUtilities::IsGCHeapInitialized()) + { + FireEtwGCSettings(GCHeapUtilities::GetGCHeap()->GetValidSegmentSize(FALSE), + GCHeapUtilities::GetGCHeap()->GetValidSegmentSize(TRUE), + GCHeapUtilities::IsServerHeap()); + GCHeapUtilities::GetGCHeap()->DiagTraceGCSegments(); + } + + // Special check for the runtime provider's GCHeapCollectKeyword. Profilers + // flick this to force a full GC. + if (IsEnabled && + (pContext->RegistrationHandle == Microsoft_Windows_Redhawk_GC_PublicHandle) && + GCHeapUtilities::IsGCHeapInitialized() && + ((pContext->MatchAnyKeyword & CLR_GCHEAPCOLLECT_KEYWORD) != 0)) + { + // Profilers may (optionally) specify extra data in the filter parameter + // to log with the GCStart event. + LONGLONG l64ClientSequenceNumber = 0; + if ((pContext->FilterData != NULL) && + (pContext->FilterData->Type == 1) && + (pContext->FilterData->Size == sizeof(l64ClientSequenceNumber))) + { + l64ClientSequenceNumber = *(LONGLONG *) (pContext->FilterData->Ptr); + } + ETW::GCLog::ForceGC(l64ClientSequenceNumber); + } + + return 0; +} +#endif // FEATURE_ETW + +// +// ----------------------------------------------------------------------------------------------------------- +// +// The rest of Redhawk needs to be able to talk to the GC/HandleTable code (to initialize it, allocate +// objects etc.) without pulling in the entire adaptation layer provided by this file and gcrhenv.h. To this +// end the rest of Redhawk talks to us via a simple interface described in gcrhinterface.h. We provide the +// implementation behind those APIs here. +// + +// Perform any runtime-startup initialization needed by the GC, HandleTable or environmental code in gcrhenv. +// The boolean parameter should be true if a server GC is required and false for workstation. Returns true on +// success or false if a subsystem failed to initialize. + +#ifndef DACCESS_COMPILE +CrstStatic g_SuspendEELock; +#ifdef _MSC_VER +#pragma warning(disable:4815) // zero-sized array in stack object will have no elements +#endif // _MSC_VER +MethodTable g_FreeObjectEEType; + +// static +bool RedhawkGCInterface::InitializeSubsystems() +{ +#ifdef FEATURE_ETW + MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled = FALSE; + MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.IsEnabled = FALSE; + + // Register the Redhawk event provider with the system. + RH_ETW_REGISTER_Microsoft_Windows_Redhawk_GC_Private(); + RH_ETW_REGISTER_Microsoft_Windows_Redhawk_GC_Public(); + + MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_Redhawk_GC_PrivateHandle; + MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_Redhawk_GC_PublicHandle; +#endif // FEATURE_ETW + + // Initialize the special MethodTable used to mark free list entries in the GC heap. + g_FreeObjectEEType.InitializeAsGcFreeType(); + g_pFreeObjectEEType = &g_FreeObjectEEType; + + if (!g_SuspendEELock.InitNoThrow(CrstSuspendEE)) + return false; + +#ifdef FEATURE_SVR_GC + g_heap_type = (g_pRhConfig->GetgcServer() && PalGetProcessCpuCount() > 1) ? GC_HEAP_SVR : GC_HEAP_WKS; +#else + g_heap_type = GC_HEAP_WKS; +#endif + + HRESULT hr = GCHeapUtilities::InitializeDefaultGC(); + if (FAILED(hr)) + return false; + + // Apparently the Windows linker removes global variables if they are never + // read from, which is a problem for g_gcDacGlobals since it's expected that + // only the DAC will read from it. This forces the linker to include + // g_gcDacGlobals. + volatile void* _dummy = g_gcDacGlobals; + + // Initialize the GC subsystem. + hr = g_pGCHeap->Initialize(); + if (FAILED(hr)) + return false; + + if (!RhInitializeFinalization()) + return false; + + // Initialize HandleTable. + if (!GCHandleUtilities::GetGCHandleManager()->Initialize()) + return false; + + return true; +} +#endif // !DACCESS_COMPILE + +Object* GcAllocInternal(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, Thread* pThread) +{ + ASSERT(!pThread->IsDoNotTriggerGcSet()); + + size_t cbSize = pEEType->get_BaseSize(); + + if (pEEType->get_ComponentSize() != 0) + { + // Impose limits on maximum array length to prevent corner case integer overflow bugs + // Keep in sync with Array.MaxLength in BCL. + if (pEEType->IsSzArray()) // multi-dimensional arrays are checked up-front + { + const int MaxArrayLength = 0x7FFFFFC7; + if (numElements > MaxArrayLength) + return NULL; + } + +#ifndef HOST_64BIT + // if the element count is <= 0x10000, no overflow is possible because the component size is + // <= 0xffff, and thus the product is <= 0xffff0000, and the base size is only ~12 bytes + if (numElements > 0x10000) + { + // Perform the size computation using 64-bit integeres to detect overflow + uint64_t size64 = (uint64_t)cbSize + ((uint64_t)numElements * (uint64_t)pEEType->get_ComponentSize()); + size64 = (size64 + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1); + + cbSize = (size_t)size64; + if (cbSize != size64) + { + return NULL; + } + } + else +#endif // !HOST_64BIT + { + cbSize = cbSize + ((size_t)numElements * (size_t)pEEType->get_ComponentSize()); + cbSize = ALIGN_UP(cbSize, sizeof(uintptr_t)); + } + } + else + { + ASSERT(numElements == 0); + } + + if (cbSize >= RH_LARGE_OBJECT_SIZE) + { + uFlags |= GC_ALLOC_LARGE_OBJECT_HEAP; + +#ifdef HOST_64BIT + const size_t max_object_size = (INT64_MAX - 7 - min_obj_size); +#else + const size_t max_object_size = (INT32_MAX - 7 - min_obj_size); +#endif + + if (cbSize >= max_object_size) + return NULL; + } + + // Save the MethodTable for instrumentation purposes. + RedhawkGCInterface::SetLastAllocEEType(pEEType); + + Object * pObject = GCHeapUtilities::GetGCHeap()->Alloc(pThread->GetAllocContext(), cbSize, uFlags); + if (pObject == NULL) + return NULL; + + pObject->set_EEType(pEEType); + if (pEEType->get_ComponentSize() != 0) + { + ASSERT(numElements == (uint32_t)numElements); + ((Array*)pObject)->InitArrayLength((uint32_t)numElements); + } + + if (uFlags & GC_ALLOC_USER_OLD_HEAP) + GCHeapUtilities::GetGCHeap()->PublishObject((uint8_t*)pObject); + +#ifdef _DEBUG + // We assume that the allocation quantum is never big enough for LARGE_OBJECT_SIZE. + gc_alloc_context* acontext = pThread->GetAllocContext(); + ASSERT(acontext->alloc_limit - acontext->alloc_ptr <= RH_LARGE_OBJECT_SIZE); +#endif + + return pObject; +} + +// Allocate an object on the GC heap. +// pEEType - type of the object +// uFlags - GC type flags (see gc.h GC_ALLOC_*) +// numElements - number of array elements +// pTransitionFrame- transition frame to make stack crawable +// Returns a pointer to the object allocated or NULL on failure. + +COOP_PINVOKE_HELPER(void*, RhpGcAlloc, (MethodTable* pEEType, uint32_t uFlags, uintptr_t numElements, void* pTransitionFrame)) +{ + Thread* pThread = ThreadStore::GetCurrentThread(); + + pThread->SetCurrentThreadPInvokeTunnelForGcAlloc(pTransitionFrame); + + return GcAllocInternal(pEEType, uFlags, numElements, pThread); +} + +// static +void RedhawkGCInterface::InitAllocContext(gc_alloc_context * pAllocContext) +{ + // NOTE: This method is currently unused because the thread's alloc_context is initialized via + // static initialization of tls_CurrentThread. If the initial contents of the alloc_context + // ever change, then a matching change will need to be made to the tls_CurrentThread static + // initializer. + + pAllocContext->init(); +} + +// static +void RedhawkGCInterface::ReleaseAllocContext(gc_alloc_context * pAllocContext) +{ + s_DeadThreadsNonAllocBytes += pAllocContext->alloc_limit - pAllocContext->alloc_ptr; + GCHeapUtilities::GetGCHeap()->FixAllocContext(pAllocContext, NULL, NULL); +} + +// static +void RedhawkGCInterface::WaitForGCCompletion() +{ + GCHeapUtilities::GetGCHeap()->WaitUntilGCComplete(); +} + +//------------------------------------------------------------------------------------------------- +// Used only by GC initialization, this initializes the MethodTable used to mark free entries in the GC heap. It +// should be an array type with a component size of one (so the GC can easily size it as appropriate) and +// should be marked as not containing any references. The rest of the fields don't matter: the GC does not +// query them and the rest of the runtime will never hold a reference to free object. + +void MethodTable::InitializeAsGcFreeType() +{ + m_usComponentSize = 1; + m_usFlags = ParameterizedEEType; + m_uBaseSize = sizeof(Array) + SYNC_BLOCK_SKEW; +} + +#endif // !DACCESS_COMPILE + +extern void GcEnumObject(PTR_OBJECTREF pObj, uint32_t flags, EnumGcRefCallbackFunc * fnGcEnumRef, EnumGcRefScanContext * pSc); +extern void GcEnumObjectsConservatively(PTR_OBJECTREF pLowerBound, PTR_OBJECTREF pUpperBound, EnumGcRefCallbackFunc * fnGcEnumRef, EnumGcRefScanContext * pSc); +extern void GcBulkEnumObjects(PTR_OBJECTREF pObjs, DWORD cObjs, EnumGcRefCallbackFunc * fnGcEnumRef, EnumGcRefScanContext * pSc); + +struct EnumGcRefContext : GCEnumContext +{ + EnumGcRefCallbackFunc * f; + EnumGcRefScanContext * sc; +}; + +bool IsOnReadablePortionOfThread(EnumGcRefScanContext * pSc, PTR_VOID pointer) +{ + if (!pSc->thread_under_crawl->IsWithinStackBounds(pointer)) + { + return false; + } + + // If the stack_limit is 0, then it wasn't set properly, and the check below will not + // operate correctly. + ASSERT(pSc->stack_limit != 0); + + // This ensures that the pointer is not in a currently-unused portion of the stack + // because the above check is only verifying against the entire stack bounds, + // but stack_limit is describing the current bound of the stack + if (PTR_TO_TADDR(pointer) < pSc->stack_limit) + { + return false; + } + return true; +} + +#ifdef HOST_64BIT +#define CONSERVATIVE_REGION_MAGIC_NUMBER 0x87DF7A104F09E0A9ULL +#else +#define CONSERVATIVE_REGION_MAGIC_NUMBER 0x4F09E0A9 +#endif + +// This is a structure that is created by executing runtime code in order to report a conservative +// region. In managed code if there is a pinned byref pointer to one of this (with the appropriate +// magic number set in it, and a hash that matches up) then the region from regionPointerLow to +// regionPointerHigh will be reported conservatively. This can only be used to report memory regions +// on the current stack and the structure must itself be located on the stack. +struct ConservativelyReportedRegionDesc +{ + // If this is really a ConservativelyReportedRegionDesc then the magic value will be + // CONSERVATIVE_REGION_MAGIC_NUMBER, and the hash will be the result of CalculateHash + // across magic, regionPointerLow, and regionPointerHigh + uintptr_t magic; + PTR_VOID regionPointerLow; + PTR_VOID regionPointerHigh; + uintptr_t hash; + + static uintptr_t CalculateHash(uintptr_t h1, uintptr_t h2, uintptr_t h3) + { + uintptr_t hash = h1; + hash = ((hash << 13) ^ hash) ^ h2; + hash = ((hash << 13) ^ hash) ^ h3; + return hash; + } +}; + +typedef DPTR(ConservativelyReportedRegionDesc) PTR_ConservativelyReportedRegionDesc; + +bool IsPtrAligned(TADDR value) +{ + return (value & (POINTER_SIZE - 1)) == 0; +} + +// Logic to actually conservatively report a ConservativelyReportedRegionDesc +// This logic is to be used when attempting to promote a pinned, interior pointer. +// It will attempt to heuristically identify ConservativelyReportedRegionDesc structures +// and if they exist, it will conservatively report a memory region. +static void ReportExplicitConservativeReportedRegionIfValid(EnumGcRefContext * pCtx, PTR_PTR_VOID pObject) +{ + // If the stack_limit isn't set (which can only happen for frames which make a p/invoke call + // there cannot be a ConservativelyReportedRegionDesc + if (pCtx->sc->stack_limit == 0) + return; + + PTR_ConservativelyReportedRegionDesc conservativeRegionDesc = (PTR_ConservativelyReportedRegionDesc)(*pObject); + + // Ensure that conservativeRegionDesc pointer points at a readable memory region + if (!IsPtrAligned(PTR_TO_TADDR(conservativeRegionDesc))) + { + return; + } + + if (!IsOnReadablePortionOfThread(pCtx->sc, conservativeRegionDesc)) + { + return; + } + if (!IsOnReadablePortionOfThread(pCtx->sc, conservativeRegionDesc + 1)) + { + return; + } + + // Now, check to see if what we're pointing at is actually a ConservativeRegionDesc + // First: check the magic number. If that doesn't match, it cannot be one + if (conservativeRegionDesc->magic != CONSERVATIVE_REGION_MAGIC_NUMBER) + { + return; + } + + // Second: check to see that the region pointers point at memory which is aligned + // such that the pointers could be pointers to object references + if (!IsPtrAligned(PTR_TO_TADDR(conservativeRegionDesc->regionPointerLow))) + { + return; + } + if (!IsPtrAligned(PTR_TO_TADDR(conservativeRegionDesc->regionPointerHigh))) + { + return; + } + + // Third: check that start is before end. + if (conservativeRegionDesc->regionPointerLow >= conservativeRegionDesc->regionPointerHigh) + { + return; + } + +#ifndef DACCESS_COMPILE + // This fails for cross-bitness dac compiles and isn't really needed in the DAC anyways. + + // Fourth: Compute a hash of the above numbers. Check to see that the hash matches the hash + // value stored + if (ConservativelyReportedRegionDesc::CalculateHash(CONSERVATIVE_REGION_MAGIC_NUMBER, + (uintptr_t)PTR_TO_TADDR(conservativeRegionDesc->regionPointerLow), + (uintptr_t)PTR_TO_TADDR(conservativeRegionDesc->regionPointerHigh)) + != conservativeRegionDesc->hash) + { + return; + } +#endif // DACCESS_COMPILE + + // Fifth: Check to see that the region pointed at is within the bounds of the thread + if (!IsOnReadablePortionOfThread(pCtx->sc, conservativeRegionDesc->regionPointerLow)) + { + return; + } + if (!IsOnReadablePortionOfThread(pCtx->sc, ((PTR_OBJECTREF)conservativeRegionDesc->regionPointerHigh) - 1)) + { + return; + } + + // At this point we're most likely working with a ConservativeRegionDesc. We'll assume + // that's true, and perform conservative reporting. (We've done enough checks to ensure that + // this conservative reporting won't itself cause an AV, even if our heuristics are wrong + // with the second and fifth set of checks) + GcEnumObjectsConservatively((PTR_OBJECTREF)conservativeRegionDesc->regionPointerLow, (PTR_OBJECTREF)conservativeRegionDesc->regionPointerHigh, pCtx->f, pCtx->sc); +} + +static void EnumGcRefsCallback(void * hCallback, PTR_PTR_VOID pObject, uint32_t flags) +{ + EnumGcRefContext * pCtx = (EnumGcRefContext *)hCallback; + + GcEnumObject((PTR_OBJECTREF)pObject, flags, pCtx->f, pCtx->sc); + + const uint32_t interiorPinned = GC_CALL_INTERIOR | GC_CALL_PINNED; + // If this is an interior pinned pointer, check to see if we're working with a ConservativeRegionDesc + // and if so, report a conservative region. NOTE: do this only during promotion as conservative + // reporting has no value during other GC phases. + if (((flags & interiorPinned) == interiorPinned) && (pCtx->sc->promotion)) + { + ReportExplicitConservativeReportedRegionIfValid(pCtx, pObject); + } +} + +// static +void RedhawkGCInterface::EnumGcRefs(ICodeManager * pCodeManager, + MethodInfo * pMethodInfo, + PTR_VOID safePointAddress, + REGDISPLAY * pRegisterSet, + void * pfnEnumCallback, + void * pvCallbackData) +{ + EnumGcRefContext ctx; + ctx.pCallback = EnumGcRefsCallback; + ctx.f = (EnumGcRefCallbackFunc *)pfnEnumCallback; + ctx.sc = (EnumGcRefScanContext *)pvCallbackData; + ctx.sc->stack_limit = pRegisterSet->GetSP(); + + pCodeManager->EnumGcRefs(pMethodInfo, + safePointAddress, + pRegisterSet, + &ctx); +} + +// static +void RedhawkGCInterface::EnumGcRefsInRegionConservatively(PTR_RtuObjectRef pLowerBound, + PTR_RtuObjectRef pUpperBound, + void * pfnEnumCallback, + void * pvCallbackData) +{ + GcEnumObjectsConservatively((PTR_OBJECTREF)pLowerBound, (PTR_OBJECTREF)pUpperBound, (EnumGcRefCallbackFunc *)pfnEnumCallback, (EnumGcRefScanContext *)pvCallbackData); +} + +// static +void RedhawkGCInterface::EnumGcRef(PTR_RtuObjectRef pRef, GCRefKind kind, void * pfnEnumCallback, void * pvCallbackData) +{ + ASSERT((GCRK_Object == kind) || (GCRK_Byref == kind)); + + DWORD flags = 0; + + if (kind == GCRK_Byref) + { + flags |= GC_CALL_INTERIOR; + } + + GcEnumObject((PTR_OBJECTREF)pRef, flags, (EnumGcRefCallbackFunc *)pfnEnumCallback, (EnumGcRefScanContext *)pvCallbackData); +} + +#ifndef DACCESS_COMPILE + +// static +void RedhawkGCInterface::BulkEnumGcObjRef(PTR_RtuObjectRef pRefs, uint32_t cRefs, void * pfnEnumCallback, void * pvCallbackData) +{ + GcBulkEnumObjects((PTR_OBJECTREF)pRefs, cRefs, (EnumGcRefCallbackFunc *)pfnEnumCallback, (EnumGcRefScanContext *)pvCallbackData); +} + +// static +GcSegmentHandle RedhawkGCInterface::RegisterFrozenSegment(void * pSection, size_t SizeSection) +{ +#ifdef FEATURE_BASICFREEZE + segment_info seginfo; + + seginfo.pvMem = pSection; + seginfo.ibFirstObject = sizeof(ObjHeader); + seginfo.ibAllocated = SizeSection; + seginfo.ibCommit = seginfo.ibAllocated; + seginfo.ibReserved = seginfo.ibAllocated; + + return (GcSegmentHandle)GCHeapUtilities::GetGCHeap()->RegisterFrozenSegment(&seginfo); +#else // FEATURE_BASICFREEZE + return NULL; +#endif // FEATURE_BASICFREEZE +} + +// static +void RedhawkGCInterface::UnregisterFrozenSegment(GcSegmentHandle segment) +{ + GCHeapUtilities::GetGCHeap()->UnregisterFrozenSegment((segment_handle)segment); +} + +EXTERN_C UInt32_BOOL g_fGcStressStarted = UInt32_FALSE; // UInt32_BOOL because asm code reads it +#ifdef FEATURE_GC_STRESS +// static +void RedhawkGCInterface::StressGc() +{ + // The GarbageCollect operation below may trash the last win32 error. We save the error here so that it can be + // restored after the GC operation; + int32_t lastErrorOnEntry = PalGetLastError(); + + if (g_fGcStressStarted && !ThreadStore::GetCurrentThread()->IsSuppressGcStressSet() && !ThreadStore::GetCurrentThread()->IsDoNotTriggerGcSet()) + { + GCHeapUtilities::GetGCHeap()->GarbageCollect(); + } + + // Restore the saved error + PalSetLastError(lastErrorOnEntry); +} +#endif // FEATURE_GC_STRESS + + +#ifdef FEATURE_GC_STRESS +COOP_PINVOKE_HELPER(void, RhpInitializeGcStress, ()) +{ + g_fGcStressStarted = UInt32_TRUE; +} +#endif // FEATURE_GC_STRESS + +#endif // !DACCESS_COMPILE + +// +// Support for scanning the GC heap, objects and roots. +// + +// Enumerate every reference field in an object, calling back to the specified function with the given context +// for each such reference found. +// static +void RedhawkGCInterface::ScanObject(void *pObject, GcScanObjectFunction pfnScanCallback, void *pContext) +{ +#if !defined(DACCESS_COMPILE) && defined(FEATURE_EVENT_TRACE) + GCHeapUtilities::GetGCHeap()->DiagWalkObject((Object*)pObject, (walk_fn)pfnScanCallback, pContext); +#else + UNREFERENCED_PARAMETER(pObject); + UNREFERENCED_PARAMETER(pfnScanCallback); + UNREFERENCED_PARAMETER(pContext); +#endif // DACCESS_COMPILE +} + +// When scanning for object roots we use existing GC APIs used for object promotion and moving. We use an +// adapter callback to transform the promote function signature used for these methods into something simpler +// that avoids exposing unnecessary implementation details. The pointer to a ScanContext normally passed to +// promotion functions is actually a pointer to the structure below which serves to recall the actual function +// pointer and context for the real context. +struct ScanRootsContext +{ + GcScanRootFunction m_pfnCallback; + void * m_pContext; +}; + +// Callback with a EnumGcRefCallbackFunc signature that forwards the call to a callback with a GcScanFunction signature +// and its own context. +void ScanRootsCallbackWrapper(Object** pObject, EnumGcRefScanContext* pContext, DWORD dwFlags) +{ + UNREFERENCED_PARAMETER(dwFlags); + + ScanRootsContext * pRealContext = (ScanRootsContext*)pContext; + + (*pRealContext->m_pfnCallback)((void**)&pObject, pRealContext->m_pContext); +} + +// Enumerate all the object roots located on the specified thread's stack. It is only safe to call this from +// the context of a GC. +// +// static +void RedhawkGCInterface::ScanStackRoots(Thread *pThread, GcScanRootFunction pfnScanCallback, void *pContext) +{ +#ifndef DACCESS_COMPILE + ScanRootsContext sContext; + sContext.m_pfnCallback = pfnScanCallback; + sContext.m_pContext = pContext; + + pThread->GcScanRoots(reinterpret_cast(ScanRootsCallbackWrapper), &sContext); +#else + UNREFERENCED_PARAMETER(pThread); + UNREFERENCED_PARAMETER(pfnScanCallback); + UNREFERENCED_PARAMETER(pContext); +#endif // !DACCESS_COMPILE +} + +// Enumerate all the object roots located in statics. It is only safe to call this from the context of a GC. +// +// static +void RedhawkGCInterface::ScanStaticRoots(GcScanRootFunction pfnScanCallback, void *pContext) +{ +#ifndef DACCESS_COMPILE + ScanRootsContext sContext; + sContext.m_pfnCallback = pfnScanCallback; + sContext.m_pContext = pContext; + + GetRuntimeInstance()->EnumAllStaticGCRefs(reinterpret_cast(ScanRootsCallbackWrapper), &sContext); +#else + UNREFERENCED_PARAMETER(pfnScanCallback); + UNREFERENCED_PARAMETER(pContext); +#endif // !DACCESS_COMPILE +} + +// Enumerate all the object roots located in handle tables. It is only safe to call this from the context of a +// GC. +// +// static +void RedhawkGCInterface::ScanHandleTableRoots(GcScanRootFunction pfnScanCallback, void *pContext) +{ +#if !defined(DACCESS_COMPILE) && defined(FEATURE_EVENT_TRACE) + ScanRootsContext sContext; + sContext.m_pfnCallback = pfnScanCallback; + sContext.m_pContext = pContext; + Ref_ScanPointers(2, 2, (EnumGcRefScanContext*)&sContext, ScanRootsCallbackWrapper); +#else + UNREFERENCED_PARAMETER(pfnScanCallback); + UNREFERENCED_PARAMETER(pContext); +#endif // !DACCESS_COMPILE +} + +#ifndef DACCESS_COMPILE + +uint32_t RedhawkGCInterface::GetGCDescSize(void * pType) +{ + MethodTable * pMT = (MethodTable *)pType; + + if (!pMT->ContainsPointersOrCollectible()) + return 0; + + return (uint32_t)CGCDesc::GetCGCDescFromMT(pMT)->GetSize(); +} + +COOP_PINVOKE_HELPER(void, RhpCopyObjectContents, (Object* pobjDest, Object* pobjSrc)) +{ + size_t cbDest = pobjDest->GetSize() - sizeof(ObjHeader); + size_t cbSrc = pobjSrc->GetSize() - sizeof(ObjHeader); + if (cbSrc != cbDest) + return; + + ASSERT(pobjDest->get_EEType()->HasReferenceFields() == pobjSrc->get_EEType()->HasReferenceFields()); + + if (pobjDest->get_EEType()->HasReferenceFields()) + { + GCSafeCopyMemoryWithWriteBarrier(pobjDest, pobjSrc, cbDest); + } + else + { + memcpy(pobjDest, pobjSrc, cbDest); + } +} + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhCompareObjectContentsAndPadding, (Object* pObj1, Object* pObj2)) +{ + ASSERT(pObj1->get_EEType()->IsEquivalentTo(pObj2->get_EEType())); + MethodTable * pEEType = pObj1->get_EEType(); + size_t cbFields = pEEType->get_BaseSize() - (sizeof(ObjHeader) + sizeof(MethodTable*)); + + uint8_t * pbFields1 = (uint8_t*)pObj1 + sizeof(MethodTable*); + uint8_t * pbFields2 = (uint8_t*)pObj2 + sizeof(MethodTable*); + + FC_RETURN_BOOL(memcmp(pbFields1, pbFields2, cbFields) == 0); +} + +// Thread static representing the last allocation. +// This is used to log the type information for each slow allocation. +DECLSPEC_THREAD +MethodTable * RedhawkGCInterface::tls_pLastAllocationEEType = NULL; + +// Get the last allocation for this thread. +MethodTable * RedhawkGCInterface::GetLastAllocEEType() +{ + return tls_pLastAllocationEEType; +} + +// Set the last allocation for this thread. +void RedhawkGCInterface::SetLastAllocEEType(MethodTable * pEEType) +{ + tls_pLastAllocationEEType = pEEType; +} + +uint64_t RedhawkGCInterface::s_DeadThreadsNonAllocBytes = 0; + +uint64_t RedhawkGCInterface::GetDeadThreadsNonAllocBytes() +{ +#ifdef HOST_64BIT + return s_DeadThreadsNonAllocBytes; +#else + // As it could be noticed we read 64bit values that may be concurrently updated. + // Such reads are not guaranteed to be atomic on 32bit so extra care should be taken. + return PalInterlockedCompareExchange64((int64_t*)&s_DeadThreadsNonAllocBytes, 0, 0); +#endif +} + +void RedhawkGCInterface::DestroyTypedHandle(void * handle) +{ + GCHandleUtilities::GetGCHandleManager()->DestroyHandleOfUnknownType((OBJECTHANDLE)handle); +} + +void* RedhawkGCInterface::CreateTypedHandle(void* pObject, int type) +{ + return (void*)GCHandleUtilities::GetGCHandleManager()->GetGlobalHandleStore()->CreateHandleOfType((Object*)pObject, (HandleType)type); +} + +void GCToEEInterface::SuspendEE(SUSPEND_REASON reason) +{ +#ifdef FEATURE_EVENT_TRACE + ETW::GCLog::ETW_GC_INFO Info; + Info.SuspendEE.Reason = reason; + Info.SuspendEE.GcCount = (((reason == SUSPEND_FOR_GC) || (reason == SUSPEND_FOR_GC_PREP)) ? + (uint32_t)GCHeapUtilities::GetGCHeap()->GetGcCount() : (uint32_t)-1); +#endif // FEATURE_EVENT_TRACE + + FireEtwGCSuspendEEBegin_V1(Info.SuspendEE.Reason, Info.SuspendEE.GcCount, GetClrInstanceId()); + + g_SuspendEELock.Enter(); + + GCHeapUtilities::GetGCHeap()->SetGCInProgress(TRUE); + + GetThreadStore()->SuspendAllThreads(true); + + FireEtwGCSuspendEEEnd_V1(GetClrInstanceId()); +} + +void GCToEEInterface::RestartEE(bool /*bFinishedGC*/) +{ + FireEtwGCRestartEEBegin_V1(GetClrInstanceId()); + +#if defined(TARGET_ARM) || defined(TARGET_ARM64) + // Flush the store buffers on all CPUs, to ensure that they all see changes made + // by the GC threads. This only matters on weak memory ordered processors as + // the strong memory ordered processors wouldn't have reordered the relevant reads. + // This is needed to synchronize threads that were running in preemptive mode while + // the runtime was suspended and that will return to cooperative mode after the runtime + // is restarted. + ::FlushProcessWriteBuffers(); +#endif //TARGET_ARM || TARGET_ARM64 + + SyncClean::CleanUp(); + + GetThreadStore()->ResumeAllThreads(true); + GCHeapUtilities::GetGCHeap()->SetGCInProgress(FALSE); + + g_SuspendEELock.Leave(); + + FireEtwGCRestartEEEnd_V1(GetClrInstanceId()); +} + +void GCToEEInterface::GcStartWork(int condemned, int /*max_gen*/) +{ + // Invoke any registered callouts for the start of the collection. + RestrictedCallouts::InvokeGcCallouts(GCRC_StartCollection, condemned); +} + +void GCToEEInterface::BeforeGcScanRoots(int condemned, bool is_bgc, bool is_concurrent) +{ +} + +// EE can perform post stack scanning action, while the user threads are still suspended +void GCToEEInterface::AfterGcScanRoots(int condemned, int /*max_gen*/, ScanContext* /*sc*/) +{ + // Invoke any registered callouts for the end of the mark phase. + RestrictedCallouts::InvokeGcCallouts(GCRC_AfterMarkPhase, condemned); +} + +void GCToEEInterface::GcDone(int condemned) +{ + // Invoke any registered callouts for the end of the collection. + RestrictedCallouts::InvokeGcCallouts(GCRC_EndCollection, condemned); +} + +bool GCToEEInterface::RefCountedHandleCallbacks(Object * pObject) +{ + return RestrictedCallouts::InvokeRefCountedHandleCallbacks(pObject); +} + +void GCToEEInterface::SyncBlockCacheWeakPtrScan(HANDLESCANPROC /*scanProc*/, uintptr_t /*lp1*/, uintptr_t /*lp2*/) +{ +} + +void GCToEEInterface::SyncBlockCacheDemote(int /*max_gen*/) +{ +} + +void GCToEEInterface::SyncBlockCachePromotionsGranted(int /*max_gen*/) +{ +} + +uint32_t GCToEEInterface::GetActiveSyncBlockCount() +{ + return 0; +} + +gc_alloc_context * GCToEEInterface::GetAllocContext() +{ + return ThreadStore::GetCurrentThread()->GetAllocContext(); +} +#endif // !DACCESS_COMPILE + +uint8_t* GCToEEInterface::GetLoaderAllocatorObjectForGC(Object* pObject) +{ + return nullptr; +} + +bool GCToEEInterface::IsPreemptiveGCDisabled() +{ + return ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode(); +} + +bool GCToEEInterface::EnablePreemptiveGC() +{ +#ifndef DACCESS_COMPILE + Thread* pThread = ThreadStore::GetCurrentThread(); + + if (pThread->IsCurrentThreadInCooperativeMode()) + { + pThread->EnablePreemptiveMode(); + return true; + } +#else + UNREFERENCED_PARAMETER(pThread); +#endif + return false; +} + +void GCToEEInterface::DisablePreemptiveGC() +{ +#ifndef DACCESS_COMPILE + ThreadStore::GetCurrentThread()->DisablePreemptiveMode(); +#else + UNREFERENCED_PARAMETER(pThread); +#endif +} + +Thread* GCToEEInterface::GetThread() +{ +#ifndef DACCESS_COMPILE + return ThreadStore::GetCurrentThread(); +#else + return NULL; +#endif +} + +#ifndef DACCESS_COMPILE + +#ifdef FEATURE_EVENT_TRACE +void ProfScanRootsHelper(Object** ppObject, ScanContext* pSC, uint32_t dwFlags) +{ + Object* pObj = *ppObject; + if (dwFlags& GC_CALL_INTERIOR) + { + pObj = GCHeapUtilities::GetGCHeap()->GetContainingObject(pObj, true); + if (pObj == nullptr) + return; + } + ScanRootsHelper(pObj, ppObject, pSC, dwFlags); +} + +void GcScanRootsForETW(promote_func* fn, int condemned, int max_gen, ScanContext* sc) +{ + UNREFERENCED_PARAMETER(condemned); + UNREFERENCED_PARAMETER(max_gen); + + FOREACH_THREAD(pThread) + { + if (pThread->IsGCSpecial()) + continue; + + if (GCHeapUtilities::GetGCHeap()->IsThreadUsingAllocationContextHeap(pThread->GetAllocContext(), sc->thread_number)) + continue; + + sc->thread_under_crawl = pThread; + sc->dwEtwRootKind = kEtwGCRootKindStack; + pThread->GcScanRoots(reinterpret_cast(fn), sc); + sc->dwEtwRootKind = kEtwGCRootKindOther; + } + END_FOREACH_THREAD +} + +void ScanHandleForETW(Object** pRef, Object* pSec, uint32_t flags, ScanContext* context, bool isDependent) +{ + ProfilingScanContext* pSC = (ProfilingScanContext*)context; + + // Notify ETW of the handle + if (ETW::GCLog::ShouldWalkHeapRootsForEtw()) + { + ETW::GCLog::RootReference( + pRef, + *pRef, // object being rooted + pSec, // pSecondaryNodeForDependentHandle + isDependent, + pSC, + 0, // dwGCFlags, + flags); // ETW handle flags + } +} + +// This is called only if we've determined that either: +// a) The Profiling API wants to do a walk of the heap, and it has pinned the +// profiler in place (so it cannot be detached), and it's thus safe to call into the +// profiler, OR +// b) ETW infrastructure wants to do a walk of the heap either to log roots, +// objects, or both. +// This can also be called to do a single walk for BOTH a) and b) simultaneously. Since +// ETW can ask for roots, but not objects +void GCProfileWalkHeapWorker(BOOL fShouldWalkHeapRootsForEtw, BOOL fShouldWalkHeapObjectsForEtw) +{ + ProfilingScanContext SC(FALSE); + unsigned max_generation = GCHeapUtilities::GetGCHeap()->GetMaxGeneration(); + + // **** Scan roots: Only scan roots if profiling API wants them or ETW wants them. + if (fShouldWalkHeapRootsForEtw) + { + GcScanRootsForETW(&ProfScanRootsHelper, max_generation, max_generation, &SC); + SC.dwEtwRootKind = kEtwGCRootKindFinalizer; + GCHeapUtilities::GetGCHeap()->DiagScanFinalizeQueue(&ProfScanRootsHelper, &SC); + + // Handles are kept independent of wks/svr/concurrent builds + SC.dwEtwRootKind = kEtwGCRootKindHandle; + GCHeapUtilities::GetGCHeap()->DiagScanHandles(&ScanHandleForETW, max_generation, &SC); + } + + // **** Scan dependent handles: only if ETW wants roots + if (fShouldWalkHeapRootsForEtw) + { + // GcScanDependentHandlesForProfiler double-checks + // CORProfilerTrackConditionalWeakTableElements() before calling into the profiler + + ProfilingScanContext* pSC = &SC; + + // we'll re-use pHeapId (which was either unused (0) or freed by EndRootReferences2 + // (-1)), so reset it to NULL + _ASSERTE((*((size_t *)(&pSC->pHeapId)) == (size_t)(-1)) || + (*((size_t *)(&pSC->pHeapId)) == (size_t)(0))); + pSC->pHeapId = NULL; + + GCHeapUtilities::GetGCHeap()->DiagScanDependentHandles(&ScanHandleForETW, max_generation, &SC); + } + + ProfilerWalkHeapContext profilerWalkHeapContext(FALSE, SC.pvEtwContext); + + // **** Walk objects on heap: only if ETW wants them. + if (fShouldWalkHeapObjectsForEtw) + { + GCHeapUtilities::GetGCHeap()->DiagWalkHeap(&HeapWalkHelper, &profilerWalkHeapContext, max_generation, true /* walk the large object heap */); + } + + #ifdef FEATURE_EVENT_TRACE + // **** Done! Indicate to ETW helpers that the heap walk is done, so any buffers + // should be flushed into the ETW stream + if (fShouldWalkHeapObjectsForEtw || fShouldWalkHeapRootsForEtw) + { + ETW::GCLog::EndHeapDump(&profilerWalkHeapContext); + } +#endif // FEATURE_EVENT_TRACE +} +#endif // defined(FEATURE_EVENT_TRACE) + +void GCProfileWalkHeap() +{ + +#ifdef FEATURE_EVENT_TRACE + if (ETW::GCLog::ShouldWalkStaticsAndCOMForEtw()) + ETW::GCLog::WalkStaticsAndCOMForETW(); + + BOOL fShouldWalkHeapRootsForEtw = ETW::GCLog::ShouldWalkHeapRootsForEtw(); + BOOL fShouldWalkHeapObjectsForEtw = ETW::GCLog::ShouldWalkHeapObjectsForEtw(); +#else // !FEATURE_EVENT_TRACE + BOOL fShouldWalkHeapRootsForEtw = FALSE; + BOOL fShouldWalkHeapObjectsForEtw = FALSE; +#endif // FEATURE_EVENT_TRACE + +#ifdef FEATURE_EVENT_TRACE + // we need to walk the heap if one of GC_PROFILING or FEATURE_EVENT_TRACE + // is defined, since both of them make use of the walk heap worker. + if (fShouldWalkHeapRootsForEtw || fShouldWalkHeapObjectsForEtw) + { + GCProfileWalkHeapWorker(fShouldWalkHeapRootsForEtw, fShouldWalkHeapObjectsForEtw); + } +#endif // defined(FEATURE_EVENT_TRACE) +} + + +void GCToEEInterface::DiagGCStart(int gen, bool isInduced) +{ + UNREFERENCED_PARAMETER(gen); + UNREFERENCED_PARAMETER(isInduced); +} + +void GCToEEInterface::DiagUpdateGenerationBounds() +{ +} + +void GCToEEInterface::DiagWalkFReachableObjects(void* gcContext) +{ + UNREFERENCED_PARAMETER(gcContext); +} + +void GCToEEInterface::DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent) +{ + UNREFERENCED_PARAMETER(index); + UNREFERENCED_PARAMETER(gen); + UNREFERENCED_PARAMETER(reason); + + if (!fConcurrent) + { + GCProfileWalkHeap(); + } +} + +// Note on last parameter: when calling this for bgc, only ETW +// should be sending these events so that existing profapi profilers +// don't get confused. +void WalkMovedReferences(uint8_t* begin, uint8_t* end, + ptrdiff_t reloc, + void* context, + bool fCompacting, + bool fBGC) +{ + UNREFERENCED_PARAMETER(begin); + UNREFERENCED_PARAMETER(end); + UNREFERENCED_PARAMETER(reloc); + UNREFERENCED_PARAMETER(context); + UNREFERENCED_PARAMETER(fCompacting); + UNREFERENCED_PARAMETER(fBGC); +} + +// +// Diagnostics code +// + +#ifdef FEATURE_EVENT_TRACE +// Tracks all surviving objects (moved or otherwise). +inline bool ShouldTrackSurvivorsForProfilerOrEtw() +{ + if (ETW::GCLog::ShouldTrackMovementForEtw()) + return true; + + return false; +} +#endif // FEATURE_EVENT_TRACE + +void GCToEEInterface::DiagWalkSurvivors(void* gcContext, bool fCompacting) +{ +#ifdef FEATURE_EVENT_TRACE + if (ShouldTrackSurvivorsForProfilerOrEtw()) + { + size_t context = 0; + ETW::GCLog::BeginMovedReferences(&context); + GCHeapUtilities::GetGCHeap()->DiagWalkSurvivorsWithType(gcContext, &WalkMovedReferences, (void*)context, walk_for_gc); + ETW::GCLog::EndMovedReferences(context); + } +#else + UNREFERENCED_PARAMETER(gcContext); +#endif // FEATURE_EVENT_TRACE +} + +void GCToEEInterface::DiagWalkUOHSurvivors(void* gcContext, int gen) +{ +#ifdef FEATURE_EVENT_TRACE + if (ShouldTrackSurvivorsForProfilerOrEtw()) + { + size_t context = 0; + ETW::GCLog::BeginMovedReferences(&context); + GCHeapUtilities::GetGCHeap()->DiagWalkSurvivorsWithType(gcContext, &WalkMovedReferences, (void*)context, walk_for_uoh, gen); + ETW::GCLog::EndMovedReferences(context); + } +#else + UNREFERENCED_PARAMETER(gcContext); +#endif // FEATURE_EVENT_TRACE +} + +void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext) +{ +#ifdef FEATURE_EVENT_TRACE + if (ShouldTrackSurvivorsForProfilerOrEtw()) + { + size_t context = 0; + ETW::GCLog::BeginMovedReferences(&context); + GCHeapUtilities::GetGCHeap()->DiagWalkSurvivorsWithType(gcContext, &WalkMovedReferences, (void*)context, walk_for_bgc); + ETW::GCLog::EndMovedReferences(context); + } +#else + UNREFERENCED_PARAMETER(gcContext); +#endif // FEATURE_EVENT_TRACE +} + +#if defined(FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP) && (!defined(TARGET_ARM64) || !defined(TARGET_UNIX)) +#error FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP is only implemented for ARM64 and UNIX +#endif + +void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) +{ + // CoreRT doesn't patch the write barrier like CoreCLR does, but it + // still needs to record the changes in the GC heap. + + bool is_runtime_suspended = args->is_runtime_suspended; + + switch (args->operation) + { + case WriteBarrierOp::StompResize: + // StompResize requires a new card table, a new lowest address, and + // a new highest address + assert(args->card_table != nullptr); + assert(args->lowest_address != nullptr); + assert(args->highest_address != nullptr); + + // We are sensitive to the order of writes here(more comments on this further in the method) + // In particular g_card_table must be written before writing the heap bounds. + // For platforms with weak memory ordering we will issue fences, for x64/x86 we are ok + // as long as compiler does not reorder these writes. + // That is unlikely since we have method calls in between. + // Just to be robust agains possible refactoring/inlining we will do a compiler-fenced store here. + VolatileStore(&g_card_table, args->card_table); + +#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES + assert(args->card_bundle_table != nullptr); + g_card_bundle_table = args->card_bundle_table; +#endif + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + if (g_sw_ww_enabled_for_gc_heap && (args->write_watch_table != nullptr)) + { + assert(args->is_runtime_suspended); + g_write_watch_table = args->write_watch_table; + } +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + + // IMPORTANT: managed heap segments may surround unmanaged/stack segments. In such cases adding another managed + // heap segment may put a stack/unmanaged write inside the new heap range. However the old card table would + // not cover it. Therefore we must ensure that the write barriers see the new table before seeing the new bounds. + // + // On architectures with strong ordering, we only need to prevent compiler reordering. + // Otherwise we put a process-wide fence here (so that we could use an ordinary read in the barrier) + +#if defined(HOST_ARM64) || defined(HOST_ARM) + if (!is_runtime_suspended) + { + // If runtime is not suspended, force all threads to see the changed table before seeing updated heap boundaries. + // See: http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/346765 + FlushProcessWriteBuffers(); + } +#endif + + g_lowest_address = args->lowest_address; + g_highest_address = args->highest_address; + +#if defined(HOST_ARM64) || defined(HOST_ARM) + if (!is_runtime_suspended) + { + // If runtime is not suspended, force all threads to see the changed state before observing future allocations. + FlushProcessWriteBuffers(); + } +#endif + return; + case WriteBarrierOp::StompEphemeral: + // StompEphemeral requires a new ephemeral low and a new ephemeral high + assert(args->ephemeral_low != nullptr); + assert(args->ephemeral_high != nullptr); + g_ephemeral_low = args->ephemeral_low; + g_ephemeral_high = args->ephemeral_high; + return; + case WriteBarrierOp::Initialize: + // This operation should only be invoked once, upon initialization. + assert(g_card_table == nullptr); + assert(g_lowest_address == nullptr); + assert(g_highest_address == nullptr); + assert(args->card_table != nullptr); + assert(args->lowest_address != nullptr); + assert(args->highest_address != nullptr); + assert(args->ephemeral_low != nullptr); + assert(args->ephemeral_high != nullptr); + assert(args->is_runtime_suspended && "the runtime must be suspended here!"); + + g_card_table = args->card_table; + +#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES + assert(g_card_bundle_table == nullptr); + g_card_bundle_table = args->card_bundle_table; +#endif + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + assert(g_write_watch_table == nullptr); + g_write_watch_table = args->write_watch_table; +#endif + + g_lowest_address = args->lowest_address; + g_highest_address = args->highest_address; + g_ephemeral_low = args->ephemeral_low; + g_ephemeral_high = args->ephemeral_high; + return; + case WriteBarrierOp::SwitchToWriteWatch: +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + assert(args->is_runtime_suspended && "the runtime must be suspended here!"); + assert(args->write_watch_table != nullptr); + g_write_watch_table = args->write_watch_table; + g_sw_ww_enabled_for_gc_heap = true; +#else + assert(!"should never be called without FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP"); +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + break; + + case WriteBarrierOp::SwitchToNonWriteWatch: +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + assert(args->is_runtime_suspended && "the runtime must be suspended here!"); + g_write_watch_table = nullptr; + g_sw_ww_enabled_for_gc_heap = false; +#else + assert(!"should never be called without FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP"); +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + return; + default: + assert(!"Unknokwn WriteBarrierOp enum"); + return; + } +} + +void GCToEEInterface::EnableFinalization(bool foundFinalizers) +{ + if (foundFinalizers) + RhEnableFinalization(); +} + +void GCToEEInterface::HandleFatalError(unsigned int exitCode) +{ + UNREFERENCED_PARAMETER(exitCode); + EEPOLICY_HANDLE_FATAL_ERROR(exitCode); +} + +bool GCToEEInterface::EagerFinalized(Object* obj) +{ + UNREFERENCED_PARAMETER(obj); + return false; +} + +bool GCToEEInterface::IsGCThread() +{ + Thread* pCurrentThread = ThreadStore::RawGetCurrentThread(); + return pCurrentThread->IsGCSpecial() || pCurrentThread == ThreadStore::GetSuspendingThread(); +} + +bool GCToEEInterface::WasCurrentThreadCreatedByGC() +{ + return ThreadStore::RawGetCurrentThread()->IsGCSpecial(); +} + +struct ThreadStubArguments +{ + void (*m_pRealStartRoutine)(void*); + void* m_pRealContext; + bool m_isSuspendable; + CLREventStatic m_ThreadStartedEvent; +}; + +bool GCToEEInterface::CreateThread(void (*threadStart)(void*), void* arg, bool is_suspendable, const char* name) +{ + UNREFERENCED_PARAMETER(name); + + ThreadStubArguments threadStubArgs; + + threadStubArgs.m_pRealStartRoutine = threadStart; + threadStubArgs.m_pRealContext = arg; + threadStubArgs.m_isSuspendable = is_suspendable; + + if (!threadStubArgs.m_ThreadStartedEvent.CreateAutoEventNoThrow(false)) + { + return false; + } + + // Helper used to wrap the start routine of background GC threads so we can do things like initialize the + // Redhawk thread state which requires running in the new thread's context. + auto threadStub = [](void* argument) -> DWORD + { + ThreadStubArguments* pStartContext = (ThreadStubArguments*)argument; + + if (pStartContext->m_isSuspendable) + { + // Initialize the Thread for this thread. The false being passed indicates that the thread store lock + // should not be acquired as part of this operation. This is necessary because this thread is created in + // the context of a garbage collection and the lock is already held by the GC. + ASSERT(GCHeapUtilities::IsGCInProgress()); + + ThreadStore::AttachCurrentThread(false); + } + + ThreadStore::RawGetCurrentThread()->SetGCSpecial(true); + + auto realStartRoutine = pStartContext->m_pRealStartRoutine; + void* realContext = pStartContext->m_pRealContext; + + pStartContext->m_ThreadStartedEvent.Set(); + + STRESS_LOG_RESERVE_MEM(GC_STRESSLOG_MULTIPLY); + + realStartRoutine(realContext); + + return 0; + }; + + if (!PalStartBackgroundGCThread(threadStub, &threadStubArgs)) + { + threadStubArgs.m_ThreadStartedEvent.CloseEvent(); + return false; + } + + uint32_t res = threadStubArgs.m_ThreadStartedEvent.Wait(INFINITE, FALSE); + threadStubArgs.m_ThreadStartedEvent.CloseEvent(); + ASSERT(res == WAIT_OBJECT_0); + + return true; +} + +// CoreRT does not use async pinned handles +void GCToEEInterface::WalkAsyncPinnedForPromotion(Object* object, ScanContext* sc, promote_func* callback) +{ + UNREFERENCED_PARAMETER(object); + UNREFERENCED_PARAMETER(sc); + UNREFERENCED_PARAMETER(callback); +} + +void GCToEEInterface::WalkAsyncPinned(Object* object, void* context, void (*callback)(Object*, Object*, void*)) +{ + UNREFERENCED_PARAMETER(object); + UNREFERENCED_PARAMETER(context); + UNREFERENCED_PARAMETER(callback); +} + +IGCToCLREventSink* GCToEEInterface::EventSink() +{ + return &g_gcToClrEventSink; +} + +uint32_t GCToEEInterface::GetTotalNumSizedRefHandles() +{ + return -1; +} + +bool GCToEEInterface::AnalyzeSurvivorsRequested(int condemnedGeneration) +{ + return false; +} + +void GCToEEInterface::AnalyzeSurvivorsFinished(size_t gcIndex, int condemnedGeneration, uint64_t promoted_bytes, void (*reportGenerationBounds)()) +{ +} + +void GCToEEInterface::VerifySyncTableEntry() +{ +} + +void GCToEEInterface::UpdateGCEventStatus(int currentPublicLevel, int currentPublicKeywords, int currentPrivateLevel, int currentPrivateKeywords) +{ + UNREFERENCED_PARAMETER(currentPublicLevel); + UNREFERENCED_PARAMETER(currentPublicKeywords); + UNREFERENCED_PARAMETER(currentPrivateLevel); + UNREFERENCED_PARAMETER(currentPrivateKeywords); + // TODO: Linux LTTng +} + +uint32_t GCToEEInterface::GetCurrentProcessCpuCount() +{ + return PalGetProcessCpuCount(); +} + +void GCToEEInterface::DiagAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved) +{ +} + +MethodTable* GCToEEInterface::GetFreeObjectMethodTable() +{ + assert(g_pFreeObjectEEType != nullptr); + return (MethodTable*)g_pFreeObjectEEType; +} + +bool GCToEEInterface::GetBooleanConfigValue(const char* privateKey, const char* publicKey, bool* value) +{ + // these configuration values are given to us via startup flags. + if (strcmp(privateKey, "gcServer") == 0) + { + *value = g_heap_type == GC_HEAP_SVR; + return true; + } + + if (strcmp(privateKey, "gcConservative") == 0) + { + *value = true; + return true; + } + +#ifdef UNICODE + size_t keyLength = strlen(privateKey) + 1; + TCHAR* pKey = (TCHAR*)_alloca(sizeof(TCHAR) * keyLength); + for (int i = 0; i < keyLength; i++) + pKey[i] = privateKey[i]; +#else + const TCHAR* pKey = privateKey; +#endif + + uint32_t uiValue; + if (!g_pRhConfig->ReadConfigValue(pKey, &uiValue)) + return false; + + *value = uiValue != 0; + return true; +} + +bool GCToEEInterface::GetIntConfigValue(const char* privateKey, const char* publicKey, int64_t* value) +{ +#ifdef UNICODE + size_t keyLength = strlen(privateKey) + 1; + TCHAR* pKey = (TCHAR*)_alloca(sizeof(TCHAR) * keyLength); + for (int i = 0; i < keyLength; i++) + pKey[i] = privateKey[i]; +#else + const TCHAR* pKey = privateKey; +#endif + + uint32_t uiValue; + if (!g_pRhConfig->ReadConfigValue(pKey, &uiValue)) + return false; + + *value = uiValue; + return true; +} + +bool GCToEEInterface::GetStringConfigValue(const char* privateKey, const char* publicKey, const char** value) +{ + UNREFERENCED_PARAMETER(privateKey); + UNREFERENCED_PARAMETER(publicKey); + UNREFERENCED_PARAMETER(value); + + return false; +} + +void GCToEEInterface::FreeStringConfigValue(const char* value) +{ + delete[] value; +} + +#endif // !DACCESS_COMPILE + +// NOTE: this method is not in thread.cpp because it needs access to the layout of alloc_context for DAC to know the +// size, but thread.cpp doesn't generally need to include the GC environment headers for any other reason. +gc_alloc_context * Thread::GetAllocContext() +{ + return dac_cast(dac_cast(this) + offsetof(Thread, m_rgbAllocContextBuffer)); +} + +GPTR_IMPL(Thread, g_pFinalizerThread); +GPTR_IMPL(Thread, g_pGcThread); + +#ifndef DACCESS_COMPILE + +bool __SwitchToThread(uint32_t dwSleepMSec, uint32_t /*dwSwitchCount*/) +{ + if (dwSleepMSec > 0) + { + PalSleep(dwSleepMSec); + return true; + } + return !!PalSwitchToThread(); +} + +#endif // DACCESS_COMPILE + +void LogSpewAlways(const char * /*fmt*/, ...) +{ +} + +#if defined(FEATURE_EVENT_TRACE) && !defined(DACCESS_COMPILE) +ProfilingScanContext::ProfilingScanContext(BOOL fProfilerPinnedParam) + : ScanContext() +{ + pHeapId = NULL; + fProfilerPinned = fProfilerPinnedParam; + pvEtwContext = NULL; + promotion = true; +} +#endif // defined(FEATURE_EVENT_TRACE) && !defined(DACCESS_COMPILE) diff --git a/src/coreclr/nativeaot/Runtime/gcrhinterface.h b/src/coreclr/nativeaot/Runtime/gcrhinterface.h new file mode 100644 index 00000000000000..f1159fade703a7 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gcrhinterface.h @@ -0,0 +1,168 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This header contains the definition of an interface between the GC/HandleTable portions of the Redhawk +// codebase and the regular Redhawk code. The former has all sorts of legacy environmental requirements (see +// gcrhenv.h) that we don't wish to pull into the rest of Redhawk. +// +// Since this file is included in both worlds it has no dependencies and uses a very simple subset of types +// etc. so that it will build cleanly in both. The actual implementation of the class defined here is in +// gcrhenv.cpp, since the implementation needs access to the guts of the GC/HandleTable. +// +// This is just an initial stab at the interface. +// + +#ifndef __GCRHINTERFACE_INCLUDED +#define __GCRHINTERFACE_INCLUDED + +#ifndef DACCESS_COMPILE +// Global data cells exported by the GC. +extern "C" unsigned char *g_ephemeral_low; +extern "C" unsigned char *g_ephemeral_high; +extern "C" unsigned char *g_lowest_address; +extern "C" unsigned char *g_highest_address; +#endif + +struct gc_alloc_context; +class MethodInfo; +struct REGDISPLAY; +class Thread; +enum GCRefKind : unsigned char; +class ICodeManager; +class MethodTable; + +// ----------------------------------------------------------------------------------------------------------- +// RtuObjectRef +// ----------------------------------------------------------------------------------------------------------- +// +// READ THIS! +// +// This struct exists for type description purposes, but you must never directly refer to the object +// reference. The only code allowed to do this is the code inherited directly from the CLR, which all +// includes gcrhenv.h. If your code is outside the namespace of gcrhenv.h, direct object reference +// manipulation is prohibited--use C# instead. +// +// To enforce this, we declare RtuObjectRef as a class with no public members. +// +class RtuObjectRef +{ +#ifndef DACCESS_COMPILE +private: +#else +public: +#endif + TADDR pvObject; +}; + +typedef DPTR(RtuObjectRef) PTR_RtuObjectRef; + +// ----------------------------------------------------------------------------------------------------------- + +// We provide various ways to enumerate GC objects or roots, each of which calls back to a user supplied +// function for each object (within the context of a garbage collection). The following function types +// describe these callbacks. Unfortunately the signatures aren't very specific: we don't want to reference +// Object* or Object** from this module, see the comment for RtuObjectRef, but this very narrow category of +// callers can't use RtuObjectRef (they really do need to drill down into the Object). The lesser evil here is +// to be a bit loose in the signature rather than exposing the Object class to the rest of Redhawk. + +// Callback when enumerating objects on the GC heap or objects referenced from instance fields of another +// object. The GC dictates the shape of this signature (we're hijacking functionality originally developed for +// profiling). The real signature is: +// int ScanFunction(Object* pObject, void* pContext) +// where: +// return : treated as a boolean, zero indicates the enumeration should terminate, all other values +// say continue +// pObject : pointer to the current object being scanned +// pContext : user context passed to the original scan function and otherwise uninterpreted +typedef int (*GcScanObjectFunction)(void*, void*); + +// Callback when enumerating GC roots (stack locations, statics and handles). Similar to the callback above +// except there is no means to terminate the scan (no return value) and the root location (pointer to pointer +// to object) is returned instead of a direct pointer to the object: +// void ScanFunction(Object** pRoot, void* pContext) +typedef void (*GcScanRootFunction)(void**, void*); + +typedef void * GcSegmentHandle; + +#define RH_LARGE_OBJECT_SIZE 85000 + +// A 'clump' is defined as the size of memory covered by 1 byte in the card table. These constants are +// verified against gcpriv.h in gcrhee.cpp. +#if (POINTER_SIZE == 8) +#define CLUMP_SIZE 0x800 +#define LOG2_CLUMP_SIZE 11 +#elif (POINTER_SIZE == 4) +#define CLUMP_SIZE 0x400 +#define LOG2_CLUMP_SIZE 10 +#else +#error unexpected pointer size +#endif + +class RedhawkGCInterface +{ +public: + // Perform any runtime-startup initialization needed by the GC, HandleTable or environmental code in + // gcrhenv. Returns true on success or false if a subsystem failed to initialize. + static bool InitializeSubsystems(); + + static void InitAllocContext(gc_alloc_context * pAllocContext); + static void ReleaseAllocContext(gc_alloc_context * pAllocContext); + + static void WaitForGCCompletion(); + + static void EnumGcRef(PTR_RtuObjectRef pRef, GCRefKind kind, void * pfnEnumCallback, void * pvCallbackData); + + static void BulkEnumGcObjRef(PTR_RtuObjectRef pRefs, uint32_t cRefs, void * pfnEnumCallback, void * pvCallbackData); + + static void EnumGcRefs(ICodeManager * pCodeManager, + MethodInfo * pMethodInfo, + PTR_VOID safePointAddress, + REGDISPLAY * pRegisterSet, + void * pfnEnumCallback, + void * pvCallbackData); + + static void EnumGcRefsInRegionConservatively(PTR_RtuObjectRef pLowerBound, + PTR_RtuObjectRef pUpperBound, + void * pfnEnumCallback, + void * pvCallbackData); + + static GcSegmentHandle RegisterFrozenSegment(void * pSection, size_t SizeSection); + static void UnregisterFrozenSegment(GcSegmentHandle segment); + +#ifdef FEATURE_GC_STRESS + static void StressGc(); +#endif // FEATURE_GC_STRESS + + // Various routines used to enumerate objects contained within a given scope (on the GC heap, as reference + // fields of an object, on a thread stack, in a static or in one of the handle tables). + static void ScanObject(void *pObject, GcScanObjectFunction pfnScanCallback, void *pContext); + static void ScanStackRoots(Thread *pThread, GcScanRootFunction pfnScanCallback, void *pContext); + static void ScanStaticRoots(GcScanRootFunction pfnScanCallback, void *pContext); + static void ScanHandleTableRoots(GcScanRootFunction pfnScanCallback, void *pContext); + + // Returns size GCDesc. Used by type cloning. + static uint32_t GetGCDescSize(void * pType); + + // These methods are used to get and set the type information for the last allocation on each thread. + static MethodTable * GetLastAllocEEType(); + static void SetLastAllocEEType(MethodTable *pEEType); + + static uint64_t GetDeadThreadsNonAllocBytes(); + + // Used by debugger hook + static void* CreateTypedHandle(void* object, int type); + static void DestroyTypedHandle(void* handle); + +private: + // The MethodTable for the last allocation. This value is used inside of the GC allocator + // to emit allocation ETW events with type information. We set this value unconditionally to avoid + // race conditions where ETW is enabled after the value is set. + DECLSPEC_THREAD static MethodTable * tls_pLastAllocationEEType; + + // Tracks the amount of bytes that were reserved for threads in their gc_alloc_context and went unused when they died. + // Used for GC.GetTotalAllocatedBytes + static uint64_t s_DeadThreadsNonAllocBytes; +}; + +#endif // __GCRHINTERFACE_INCLUDED diff --git a/src/coreclr/nativeaot/Runtime/gcrhscan.cpp b/src/coreclr/nativeaot/Runtime/gcrhscan.cpp new file mode 100644 index 00000000000000..be64a0685dc552 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gcrhscan.cpp @@ -0,0 +1,167 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" + +#include "gcenv.h" +#include "gcheaputilities.h" +#include "objecthandle.h" + +#include "gcenv.ee.h" + +#include "PalRedhawkCommon.h" + +#include "gcrhinterface.h" + +#include "slist.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" + +#include "thread.h" + +#include "shash.h" +#include "RWLock.h" +#include "RuntimeInstance.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "thread.inl" + +#ifndef DACCESS_COMPILE + +void GcEnumObjectsConservatively(PTR_PTR_Object ppLowerBound, PTR_PTR_Object ppUpperBound, EnumGcRefCallbackFunc * fnGcEnumRef, EnumGcRefScanContext * pSc); + +void EnumAllStaticGCRefs(EnumGcRefCallbackFunc * fn, EnumGcRefScanContext * sc) +{ + GetRuntimeInstance()->EnumAllStaticGCRefs(reinterpret_cast(fn), sc); +} + +/* + * Scan all stack and statics roots + */ + +void GCToEEInterface::GcScanRoots(EnumGcRefCallbackFunc * fn, int condemned, int max_gen, EnumGcRefScanContext * sc) +{ + // STRESS_LOG1(LF_GCROOTS, LL_INFO10, "GCScan: Phase = %s\n", sc->promotion ? "promote" : "relocate"); + + FOREACH_THREAD(pThread) + { + // Skip "GC Special" threads which are really background workers that will never have any roots. + if (pThread->IsGCSpecial()) + continue; + +#if !defined (ISOLATED_HEAPS) + // @TODO: it is very bizarre that this IsThreadUsingAllocationContextHeap takes a copy of the + // allocation context instead of a reference or a pointer to it. This seems very wasteful given how + // large the alloc_context is. + if (!GCHeapUtilities::GetGCHeap()->IsThreadUsingAllocationContextHeap(pThread->GetAllocContext(), + sc->thread_number)) + { + // STRESS_LOG2(LF_GC|LF_GCROOTS, LL_INFO100, "{ Scan of Thread %p (ID = %x) declined by this heap\n", + // pThread, pThread->GetThreadId()); + } + else +#endif + { + STRESS_LOG1(LF_GC|LF_GCROOTS, LL_INFO100, "{ Starting scan of Thread %p\n", pThread); + sc->thread_under_crawl = pThread; +#if defined(FEATURE_EVENT_TRACE) && !defined(DACCESS_COMPILE) + sc->dwEtwRootKind = kEtwGCRootKindStack; +#endif + pThread->GcScanRoots(reinterpret_cast(fn), sc); + +#if defined(FEATURE_EVENT_TRACE) && !defined(DACCESS_COMPILE) + sc->dwEtwRootKind = kEtwGCRootKindOther; +#endif + STRESS_LOG1(LF_GC|LF_GCROOTS, LL_INFO100, "Ending scan of Thread %p }\n", pThread); + } + } + END_FOREACH_THREAD + + sc->thread_under_crawl = NULL; + + if ((!GCHeapUtilities::IsServerHeap() || sc->thread_number == 0) ||(condemned == max_gen && sc->promotion)) + { +#if defined(FEATURE_EVENT_TRACE) && !defined(DACCESS_COMPILE) + sc->dwEtwRootKind = kEtwGCRootKindHandle; +#endif + EnumAllStaticGCRefs(fn, sc); + } +} + +void GCToEEInterface::GcEnumAllocContexts (enum_alloc_context_func* fn, void* param) +{ + FOREACH_THREAD(thread) + { + (*fn) (thread->GetAllocContext(), param); + } + END_FOREACH_THREAD +} + +#endif //!DACCESS_COMPILE + +void PromoteCarefully(PTR_PTR_Object obj, uint32_t flags, EnumGcRefCallbackFunc * fnGcEnumRef, EnumGcRefScanContext * pSc) +{ + // + // Sanity check that the flags contain only these three values + // + assert((flags & ~(GC_CALL_INTERIOR|GC_CALL_PINNED|GC_CALL_CHECK_APP_DOMAIN)) == 0); + + // + // Sanity check that GC_CALL_INTERIOR FLAG is set + // + assert(flags & GC_CALL_INTERIOR); + + // If the object reference points into the stack, we + // must not promote it, the GC cannot handle these. + if (pSc->thread_under_crawl->IsWithinStackBounds(*obj)) + return; + + fnGcEnumRef(obj, pSc, flags); +} + +void GcEnumObject(PTR_PTR_Object ppObj, uint32_t flags, EnumGcRefCallbackFunc * fnGcEnumRef, EnumGcRefScanContext * pSc) +{ + // + // Sanity check that the flags contain only these three values + // + assert((flags & ~(GC_CALL_INTERIOR|GC_CALL_PINNED|GC_CALL_CHECK_APP_DOMAIN)) == 0); + + // for interior pointers, we optimize the case in which + // it points into the current threads stack area + // + if (flags & GC_CALL_INTERIOR) + PromoteCarefully (ppObj, flags, fnGcEnumRef, pSc); + else + fnGcEnumRef(ppObj, pSc, flags); +} + +void GcBulkEnumObjects(PTR_PTR_Object pObjs, uint32_t cObjs, EnumGcRefCallbackFunc * fnGcEnumRef, EnumGcRefScanContext * pSc) +{ + PTR_PTR_Object ppObj = pObjs; + + for (uint32_t i = 0; i < cObjs; i++) + fnGcEnumRef(ppObj++, pSc, 0); +} + +// Scan a contiguous range of memory and report everything that looks like it could be a GC reference as a +// pinned interior reference. Pinned in case we are wrong (so the GC won't try to move the object and thus +// corrupt the original memory value by relocating it). Interior since we (a) can't easily tell whether a +// real reference is interior or not and interior is the more conservative choice that will work for both and +// (b) because it might not be a real GC reference at all and in that case falsely listing the reference as +// non-interior will cause the GC to make assumptions and crash quite quickly. +void GcEnumObjectsConservatively(PTR_PTR_Object ppLowerBound, PTR_PTR_Object ppUpperBound, EnumGcRefCallbackFunc * fnGcEnumRef, EnumGcRefScanContext * pSc) +{ + // Only report potential references in the promotion phase. Since we report everything as pinned there + // should be no work to do in the relocation phase. + if (pSc->promotion) + { + for (PTR_PTR_Object ppObj = ppLowerBound; ppObj < ppUpperBound; ppObj++) + { + // Only report values that lie in the GC heap range. This doesn't conclusively guarantee that the + // value is a GC heap reference but it's a cheap check that weeds out a lot of spurious values. + PTR_Object pObj = *ppObj; + if (((PTR_UInt8)pObj >= g_lowest_address) && ((PTR_UInt8)pObj <= g_highest_address)) + fnGcEnumRef(ppObj, pSc, GC_CALL_INTERIOR|GC_CALL_PINNED); + } + } +} diff --git a/src/coreclr/nativeaot/Runtime/gctoclreventsink.cpp b/src/coreclr/nativeaot/Runtime/gctoclreventsink.cpp new file mode 100644 index 00000000000000..32fe819e8af3d6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gctoclreventsink.cpp @@ -0,0 +1,335 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" +#include "gctoclreventsink.h" + +GCToCLREventSink g_gcToClrEventSink; + +void GCToCLREventSink::FireDynamicEvent(const char* eventName, void* payload, uint32_t payloadSize) +{ + LIMITED_METHOD_CONTRACT; + +#ifndef FEATURE_REDHAWK + const size_t EventNameMaxSize = 255; + + WCHAR wideEventName[EventNameMaxSize]; + if (MultiByteToWideChar(CP_ACP, 0, eventName, -1, wideEventName, EventNameMaxSize) == 0) + { + return; + } + + FireEtwGCDynamicEvent(wideEventName, payloadSize, (const BYTE*)payload, GetClrInstanceId()); +#endif // !FEATURE_REDHAWK +} + +void GCToCLREventSink::FireGCStart_V2(uint32_t count, uint32_t depth, uint32_t reason, uint32_t type) +{ + LIMITED_METHOD_CONTRACT; + +#ifdef FEATURE_ETW + ETW::GCLog::ETW_GC_INFO gcStartInfo; + gcStartInfo.GCStart.Count = count; + gcStartInfo.GCStart.Depth = depth; + gcStartInfo.GCStart.Reason = static_cast(reason); + gcStartInfo.GCStart.Type = static_cast(type); + ETW::GCLog::FireGcStart(&gcStartInfo); +#endif // FEATURE_ETW +} + +void GCToCLREventSink::FireGCGenerationRange(uint8_t generation, void* rangeStart, uint64_t rangeUsedLength, uint64_t rangeReservedLength) +{ + LIMITED_METHOD_CONTRACT; + + FireEtwGCGenerationRange(generation, rangeStart, rangeUsedLength, rangeReservedLength, GetClrInstanceId()); +} + +void GCToCLREventSink::FireGCEnd_V1(uint32_t count, uint32_t depth) +{ + LIMITED_METHOD_CONTRACT; + + FireEtwGCEnd_V1(count, depth, GetClrInstanceId()); +} + +void GCToCLREventSink::FireGCHeapStats_V2( + uint64_t generationSize0, + uint64_t totalPromotedSize0, + uint64_t generationSize1, + uint64_t totalPromotedSize1, + uint64_t generationSize2, + uint64_t totalPromotedSize2, + uint64_t generationSize3, + uint64_t totalPromotedSize3, + uint64_t generationSize4, + uint64_t totalPromotedSize4, + uint64_t finalizationPromotedSize, + uint64_t finalizationPromotedCount, + uint32_t pinnedObjectCount, + uint32_t sinkBlockCount, + uint32_t gcHandleCount) +{ + LIMITED_METHOD_CONTRACT; + + // TODO: FireEtwGCHeapStats_V2 + FireEtwGCHeapStats_V1(generationSize0, totalPromotedSize0, generationSize1, totalPromotedSize1, + generationSize2, totalPromotedSize2, generationSize3, totalPromotedSize3, + finalizationPromotedSize, finalizationPromotedCount, pinnedObjectCount, + sinkBlockCount, gcHandleCount, GetClrInstanceId()); +} + +void GCToCLREventSink::FireGCCreateSegment_V1(void* address, size_t size, uint32_t type) +{ + LIMITED_METHOD_CONTRACT; + + FireEtwGCCreateSegment_V1((uint64_t)address, static_cast(size), type, GetClrInstanceId()); +} + +void GCToCLREventSink::FireGCFreeSegment_V1(void* address) +{ + LIMITED_METHOD_CONTRACT; + + FireEtwGCFreeSegment_V1((uint64_t)address, GetClrInstanceId()); +} + +void GCToCLREventSink::FireGCCreateConcurrentThread_V1() +{ + LIMITED_METHOD_CONTRACT; + + FireEtwGCCreateConcurrentThread_V1(GetClrInstanceId()); +} + +void GCToCLREventSink::FireGCTerminateConcurrentThread_V1() +{ + LIMITED_METHOD_CONTRACT; + + FireEtwGCTerminateConcurrentThread_V1(GetClrInstanceId()); +} + +void GCToCLREventSink::FireGCTriggered(uint32_t reason) +{ + LIMITED_METHOD_CONTRACT; + + FireEtwGCTriggered(reason, GetClrInstanceId()); +} + +void GCToCLREventSink::FireGCMarkWithType(uint32_t heapNum, uint32_t type, uint64_t bytes) +{ + LIMITED_METHOD_CONTRACT; + + FireEtwGCMarkWithType(heapNum, GetClrInstanceId(), type, bytes); +} + +void GCToCLREventSink::FireGCJoin_V2(uint32_t heap, uint32_t joinTime, uint32_t joinType, uint32_t joinId) +{ + LIMITED_METHOD_CONTRACT; + + FireEtwGCJoin_V2(heap, joinTime, joinType, GetClrInstanceId(), joinId); +} + +void GCToCLREventSink::FireGCGlobalHeapHistory_V4(uint64_t finalYoungestDesired, + int32_t numHeaps, + uint32_t condemnedGeneration, + uint32_t gen0reductionCount, + uint32_t reason, + uint32_t globalMechanisms, + uint32_t pauseMode, + uint32_t memoryPressure, + uint32_t condemnReasons0, + uint32_t condemnReasons1, + uint32_t count, + uint32_t valuesLen, + void* values) +{ + LIMITED_METHOD_CONTRACT; + + // TODO: FireEtwGCGlobalHeapHistory_V4 + FireEtwGCGlobalHeapHistory_V2(finalYoungestDesired, numHeaps, condemnedGeneration, gen0reductionCount, reason, + globalMechanisms, GetClrInstanceId(), pauseMode, memoryPressure); +} + +void GCToCLREventSink::FireGCAllocationTick_V1(uint32_t allocationAmount, uint32_t allocationKind) +{ + LIMITED_METHOD_CONTRACT; + + FireEtwGCAllocationTick_V1(allocationAmount, allocationKind, GetClrInstanceId()); +} + +void GCToCLREventSink::FireGCAllocationTick_V4(uint64_t allocationAmount, + uint32_t allocationKind, + uint32_t heapIndex, + void* objectAddress, + uint64_t objectSize) +{ + LIMITED_METHOD_CONTRACT; + + void * typeId = RedhawkGCInterface::GetLastAllocEEType(); + const WCHAR * name = nullptr; + + if (typeId != nullptr) + { + FireEtwGCAllocationTick_V3(static_cast(allocationAmount), + allocationKind, + GetClrInstanceId(), + allocationAmount, + typeId, + name, + heapIndex, + objectAddress); + } +} + +void GCToCLREventSink::FirePinObjectAtGCTime(void* object, uint8_t** ppObject) +{ + UNREFERENCED_PARAMETER(object); + UNREFERENCED_PARAMETER(ppObject); +} + +void GCToCLREventSink::FireGCLOHCompact(uint16_t count, uint32_t valuesLen, void* values) +{ + UNREFERENCED_PARAMETER(count); + UNREFERENCED_PARAMETER(valuesLen); + UNREFERENCED_PARAMETER(values); +} + +void GCToCLREventSink::FireGCFitBucketInfo(uint16_t bucketKind, size_t size, uint16_t count, uint32_t valuesLen, void* values) +{ + UNREFERENCED_PARAMETER(bucketKind); + UNREFERENCED_PARAMETER(size); + UNREFERENCED_PARAMETER(count); + UNREFERENCED_PARAMETER(valuesLen); + UNREFERENCED_PARAMETER(values); +} + +void GCToCLREventSink::FirePinPlugAtGCTime(uint8_t* plugStart, uint8_t* plugEnd, uint8_t* gapBeforeSize) +{ + FireEtwPinPlugAtGCTime(plugStart, plugEnd, gapBeforeSize, GetClrInstanceId()); +} + +void GCToCLREventSink::FireGCPerHeapHistory_V3(void *freeListAllocated, + void *freeListRejected, + void *endOfSegAllocated, + void *condemnedAllocated, + void *pinnedAllocated, + void *pinnedAllocatedAdvance, + uint32_t runningFreeListEfficiency, + uint32_t condemnReasons0, + uint32_t condemnReasons1, + uint32_t compactMechanisms, + uint32_t expandMechanisms, + uint32_t heapIndex, + void *extraGen0Commit, + uint32_t count, + uint32_t valuesLen, + void *values) +{ + FireEtwGCPerHeapHistory_V3(GetClrInstanceId(), + freeListAllocated, + freeListRejected, + endOfSegAllocated, + condemnedAllocated, + pinnedAllocated, + pinnedAllocatedAdvance, + runningFreeListEfficiency, + condemnReasons0, + condemnReasons1, + compactMechanisms, + expandMechanisms, + heapIndex, + extraGen0Commit, + count, + valuesLen, + values); +} + + + +void GCToCLREventSink::FireBGCBegin() +{ + FireEtwBGCBegin(GetClrInstanceId()); +} + +void GCToCLREventSink::FireBGC1stNonConEnd() +{ + FireEtwBGC1stNonConEnd(GetClrInstanceId()); +} + +void GCToCLREventSink::FireBGC1stConEnd() +{ + FireEtwBGC1stConEnd(GetClrInstanceId()); +} + +void GCToCLREventSink::FireBGC1stSweepEnd(uint32_t genNumber) +{ + //FireEtwBGC1stSweepEnd(genNumber, GetClrInstanceId()); TODO +} + +void GCToCLREventSink::FireBGC2ndNonConBegin() +{ + FireEtwBGC2ndNonConBegin(GetClrInstanceId()); +} + +void GCToCLREventSink::FireBGC2ndNonConEnd() +{ + FireEtwBGC2ndNonConEnd(GetClrInstanceId()); +} + +void GCToCLREventSink::FireBGC2ndConBegin() +{ + FireEtwBGC2ndConBegin(GetClrInstanceId()); +} + +void GCToCLREventSink::FireBGC2ndConEnd() +{ + FireEtwBGC2ndConEnd(GetClrInstanceId()); +} + +void GCToCLREventSink::FireBGCDrainMark(uint64_t objects) +{ + FireEtwBGCDrainMark(objects, GetClrInstanceId()); +} + +void GCToCLREventSink::FireBGCRevisit(uint64_t pages, uint64_t objects, uint32_t isLarge) +{ + FireEtwBGCRevisit(pages, objects, isLarge, GetClrInstanceId()); +} + +void GCToCLREventSink::FireBGCOverflow_V1(uint64_t min, uint64_t max, uint64_t objects, uint32_t isLarge, uint32_t genNumber) +{ + // TODO: FireBGCOverflow_V1 + FireEtwBGCOverflow(min, max, objects, isLarge, GetClrInstanceId()); +} + +void GCToCLREventSink::FireBGCAllocWaitBegin(uint32_t reason) +{ + FireEtwBGCAllocWaitBegin(reason, GetClrInstanceId()); +} + +void GCToCLREventSink::FireBGCAllocWaitEnd(uint32_t reason) +{ + FireEtwBGCAllocWaitEnd(reason, GetClrInstanceId()); +} + +void GCToCLREventSink::FireGCFullNotify_V1(uint32_t genNumber, uint32_t isAlloc) +{ + FireEtwGCFullNotify_V1(genNumber, isAlloc, GetClrInstanceId()); +} + +void GCToCLREventSink::FireSetGCHandle(void* handleID, void* objectID, uint32_t kind, uint32_t generation) +{ + FireEtwSetGCHandle(handleID, objectID, kind, generation, -1, GetClrInstanceId()); +} + +void GCToCLREventSink::FirePrvSetGCHandle(void* handleID, void* objectID, uint32_t kind, uint32_t generation) +{ + FireEtwPrvSetGCHandle(handleID, objectID, kind, generation, -1, GetClrInstanceId()); +} + +void GCToCLREventSink::FireDestroyGCHandle(void *handleID) +{ + FireEtwDestroyGCHandle(handleID, GetClrInstanceId()); +} + +void GCToCLREventSink::FirePrvDestroyGCHandle(void *handleID) +{ + FireEtwPrvDestroyGCHandle(handleID, GetClrInstanceId()); +} diff --git a/src/coreclr/nativeaot/Runtime/gctoclreventsink.h b/src/coreclr/nativeaot/Runtime/gctoclreventsink.h new file mode 100644 index 00000000000000..a6ffed6f2ce9c7 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/gctoclreventsink.h @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __GCTOCLREVENTSINK_H__ +#define __GCTOCLREVENTSINK_H__ + +#include "gcenv.h" +#include "gcinterface.h" + +class GCToCLREventSink : public IGCToCLREventSink +{ +public: + void FireDynamicEvent(const char* eventName, void* payload, uint32_t payloadSize); + void FireGCStart_V2(uint32_t count, uint32_t depth, uint32_t reason, uint32_t type); + void FireGCEnd_V1(uint32_t count, uint32_t depth); + void FireGCGenerationRange(uint8_t generation, void* rangeStart, uint64_t rangeUsedLength, uint64_t rangeReservedLength); + void FireGCHeapStats_V2(uint64_t generationSize0, + uint64_t totalPromotedSize0, + uint64_t generationSize1, + uint64_t totalPromotedSize1, + uint64_t generationSize2, + uint64_t totalPromotedSize2, + uint64_t generationSize3, + uint64_t totalPromotedSize3, + uint64_t generationSize4, + uint64_t totalPromotedSize4, + uint64_t finalizationPromotedSize, + uint64_t finalizationPromotedCount, + uint32_t pinnedObjectCount, + uint32_t sinkBlockCount, + uint32_t gcHandleCount); + void FireGCCreateSegment_V1(void* address, size_t size, uint32_t type); + void FireGCFreeSegment_V1(void* address); + void FireGCCreateConcurrentThread_V1(); + void FireGCTerminateConcurrentThread_V1(); + void FireGCTriggered(uint32_t reason); + void FireGCMarkWithType(uint32_t heapNum, uint32_t type, uint64_t bytes); + void FireGCJoin_V2(uint32_t heap, uint32_t joinTime, uint32_t joinType, uint32_t joinId); + void FireGCGlobalHeapHistory_V4(uint64_t finalYoungestDesired, + int32_t numHeaps, + uint32_t condemnedGeneration, + uint32_t gen0reductionCount, + uint32_t reason, + uint32_t globalMechanisms, + uint32_t pauseMode, + uint32_t memoryPressure, + uint32_t condemnReasons0, + uint32_t condemnReasons1, + uint32_t count, + uint32_t valuesLen, + void *values); + void FireGCAllocationTick_V1(uint32_t allocationAmount, uint32_t allocationKind); + void FireGCAllocationTick_V4(uint64_t allocationAmount, + uint32_t allocationKind, + uint32_t heapIndex, + void* objectAddress, + uint64_t objectSize); + void FirePinObjectAtGCTime(void* object, uint8_t** ppObject); + void FireGCLOHCompact(uint16_t count, uint32_t valuesLen, void* values); + void FireGCFitBucketInfo(uint16_t bucketKind, size_t size, uint16_t count, uint32_t valuesLen, void* values); + void FirePinPlugAtGCTime(uint8_t* plug_start, uint8_t* plug_end, uint8_t* gapBeforeSize); + void FireGCPerHeapHistory_V3(void *freeListAllocated, + void *freeListRejected, + void *endOfSegAllocated, + void *condemnedAllocated, + void *pinnedAllocated, + void *pinnedAllocatedAdvance, + uint32_t runningFreeListEfficiency, + uint32_t condemnReasons0, + uint32_t condemnReasons1, + uint32_t compactMechanisms, + uint32_t expandMechanisms, + uint32_t heapIndex, + void *extraGen0Commit, + uint32_t count, + uint32_t valuesLen, + void *values); + void FireBGCBegin(); + void FireBGC1stNonConEnd(); + void FireBGC1stConEnd(); + void FireBGC1stSweepEnd(uint32_t genNumber); + void FireBGC2ndNonConBegin(); + void FireBGC2ndNonConEnd(); + void FireBGC2ndConBegin(); + void FireBGC2ndConEnd(); + void FireBGCDrainMark(uint64_t objects); + void FireBGCRevisit(uint64_t pages, uint64_t objects, uint32_t isLarge); + void FireBGCOverflow_V1(uint64_t min, uint64_t max, uint64_t objects, uint32_t isLarge, uint32_t genNumber); + void FireBGCAllocWaitBegin(uint32_t reason); + void FireBGCAllocWaitEnd(uint32_t reason); + void FireGCFullNotify_V1(uint32_t genNumber, uint32_t isAlloc); + void FireSetGCHandle(void *handleID, void *objectID, uint32_t kind, uint32_t generation); + void FirePrvSetGCHandle(void *handleID, void *objectID, uint32_t kind, uint32_t generation); + void FireDestroyGCHandle(void *handleID); + void FirePrvDestroyGCHandle(void *handleID); +}; + +extern GCToCLREventSink g_gcToClrEventSink; + +#endif // __GCTOCLREVENTSINK_H__ + diff --git a/src/coreclr/nativeaot/Runtime/holder.h b/src/coreclr/nativeaot/Runtime/holder.h new file mode 100644 index 00000000000000..22bd3dbfe3f5e0 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/holder.h @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// ----------------------------------------------------------------------------------------------------------- +// Cut down versions of the Holder and Wrapper template classes used in the CLR. If this coding pattern is +// also common in the Redhawk code then it might be worth investigating pulling the whole holder.h header file +// over (a quick look indicates it might not drag in too many extra dependencies). +// + +// ----------------------------------------------------------------------------------------------------------- +// This version of holder does not have a default constructor. + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define EQUALS_DEFAULT +#else +#define EQUALS_DEFAULT = default +#endif + +template +class HolderNoDefaultValue +{ +public: + HolderNoDefaultValue(TYPE value, bool fTake = true) : m_value(value), m_held(false) + { if (fTake) { ACQUIRE_FUNC(value); m_held = true; } } + + ~HolderNoDefaultValue() { if (m_held) RELEASE_FUNC(m_value); } + + TYPE GetValue() { return m_value; } + + void Acquire() { ACQUIRE_FUNC(m_value); m_held = true; } + void Release() { if (m_held) { RELEASE_FUNC(m_value); m_held = false; } } + void SuppressRelease() { m_held = false; } + TYPE Extract() { m_held = false; return GetValue(); } + + HolderNoDefaultValue(HolderNoDefaultValue && other) EQUALS_DEFAULT; + +protected: + TYPE m_value; + bool m_held; + +private: + // No one should be copying around holder types. + HolderNoDefaultValue & operator=(const HolderNoDefaultValue & other); + HolderNoDefaultValue(const HolderNoDefaultValue & other); +}; + +// ----------------------------------------------------------------------------------------------------------- +template +class Holder : public HolderNoDefaultValue +{ + typedef HolderNoDefaultValue MY_PARENT; +public: + Holder() : MY_PARENT(DEFAULTVALUE, false) {} + Holder(TYPE value, bool fTake = true) : MY_PARENT(value, fTake) {} + + Holder(Holder && other) EQUALS_DEFAULT; + +private: + // No one should be copying around holder types. + Holder & operator=(const Holder & other); + Holder(const Holder & other); +}; + +// ----------------------------------------------------------------------------------------------------------- +template +class Wrapper : public Holder +{ + typedef Holder MY_PARENT; + +public: + Wrapper() : MY_PARENT() {} + Wrapper(TYPE value, bool fTake = true) : MY_PARENT(value, fTake) {} + Wrapper(Wrapper && other) EQUALS_DEFAULT; + + FORCEINLINE TYPE& operator=(TYPE const & value) + { + MY_PARENT::Release(); + MY_PARENT::m_value = value; + MY_PARENT::Acquire(); + return MY_PARENT::m_value; + } + + FORCEINLINE const TYPE &operator->() { return MY_PARENT::m_value; } + FORCEINLINE const TYPE &operator*() { return MY_PARENT::m_value; } + FORCEINLINE operator TYPE() { return MY_PARENT::m_value; } + +private: + // No one should be copying around wrapper types. + Wrapper & operator=(const Wrapper & other); + Wrapper(const Wrapper & other); +}; + +// ----------------------------------------------------------------------------------------------------------- +template +FORCEINLINE void DoNothing(TYPE /*value*/) +{ +} + +// ----------------------------------------------------------------------------------------------------------- +template +FORCEINLINE void Delete(TYPE *value) +{ + delete value; +} + +// ----------------------------------------------------------------------------------------------------------- +template , + void (*RELEASE_FUNC)(PTR_TYPE) = Delete, + PTR_TYPE NULL_VAL = nullptr, + typename BASE = Wrapper > +class NewHolder : public BASE +{ +public: + NewHolder(PTR_TYPE p = NULL_VAL) : BASE(p) + { } + + PTR_TYPE& operator=(PTR_TYPE p) + { return BASE::operator=(p); } + + bool IsNull() + { return BASE::GetValue() == NULL_VAL; } +}; + +//----------------------------------------------------------------------------- +// NewArrayHolder : New []'ed pointer holder +// { +// NewArrayHolder foo = new (nothrow) Foo [30]; +// } // delete [] foo on out of scope +//----------------------------------------------------------------------------- + +template +FORCEINLINE void DeleteArray(TYPE *value) +{ + delete [] value; + value = NULL; +} + +template , + void (*RELEASE_FUNC)(PTR_TYPE) = DeleteArray, + PTR_TYPE NULL_VAL = nullptr, + typename BASE = Wrapper > +class NewArrayHolder : public BASE +{ +public: + NewArrayHolder(PTR_TYPE p = NULL_VAL) : BASE(p) + { } + + PTR_TYPE& operator=(PTR_TYPE p) + { return BASE::operator=(p); } + + bool IsNull() + { return BASE::GetValue() == NULL_VAL; } +}; + +// ----------------------------------------------------------------------------------------------------------- +template +FORCEINLINE void Destroy(TYPE * value) +{ + value->Destroy(); +} + +// ----------------------------------------------------------------------------------------------------------- +template , + void (*RELEASE_FUNC)(PTR_TYPE) = Destroy, + PTR_TYPE NULL_VAL = nullptr, + typename BASE = Wrapper > +class CreateHolder : public BASE +{ +public: + CreateHolder(PTR_TYPE p = NULL_VAL) : BASE(p) + { } + + PTR_TYPE& operator=(PTR_TYPE p) + { return BASE::operator=(p); } +}; + + diff --git a/src/coreclr/nativeaot/Runtime/i386/AllocFast.S b/src/coreclr/nativeaot/Runtime/i386/AllocFast.S new file mode 100644 index 00000000000000..876f2dfbcb80d6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/AllocFast.S @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// TODO: Implement diff --git a/src/coreclr/nativeaot/Runtime/i386/AllocFast.asm b/src/coreclr/nativeaot/Runtime/i386/AllocFast.asm new file mode 100644 index 00000000000000..8d28e94c944177 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/AllocFast.asm @@ -0,0 +1,387 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + .586 + .model flat + option casemap:none + .code + + +include AsmMacros.inc + +;; Allocate non-array, non-finalizable object. If the allocation doesn't fit into the current thread's +;; allocation context then automatically fallback to the slow allocation path. +;; ECX == MethodTable +FASTCALL_FUNC RhpNewFast, 4 + + ;; edx = GetThread(), TRASHES eax + INLINE_GETTHREAD edx, eax + + ;; + ;; ecx contains MethodTable pointer + ;; + mov eax, [ecx + OFFSETOF__MethodTable__m_uBaseSize] + + ;; + ;; eax: base size + ;; ecx: MethodTable pointer + ;; edx: Thread pointer + ;; + + add eax, [edx + OFFSETOF__Thread__m_alloc_context__alloc_ptr] + cmp eax, [edx + OFFSETOF__Thread__m_alloc_context__alloc_limit] + ja AllocFailed + + ;; set the new alloc pointer + mov [edx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], eax + + ;; calc the new object pointer + sub eax, [ecx + OFFSETOF__MethodTable__m_uBaseSize] + + ;; set the new object's MethodTable pointer + mov [eax], ecx + ret + +AllocFailed: + + ;; + ;; ecx: MethodTable pointer + ;; + push ebp + mov ebp, esp + + PUSH_COOP_PINVOKE_FRAME edx + + ;; Preserve MethodTable in ESI. + mov esi, ecx + + ;; Push alloc helper arguments + push edx ; transition frame + push 0 ; numElements + xor edx, edx ; Flags + ;; Passing MethodTable in ecx + + ;; void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + call RhpGcAlloc + + test eax, eax + jz NewFast_OOM + + POP_COOP_PINVOKE_FRAME + + pop ebp + ret + +NewFast_OOM: + ;; This is the failure path. We're going to tail-call to a managed helper that will throw + ;; an out of memory exception that the caller of this allocator understands. + + mov eax, esi ; Preserve MethodTable pointer over POP_COOP_PINVOKE_FRAME + + POP_COOP_PINVOKE_FRAME + + ;; Cleanup our ebp frame + pop ebp + + mov ecx, eax ; MethodTable pointer + xor edx, edx ; Indicate that we should throw OOM. + jmp RhExceptionHandling_FailedAllocation + +FASTCALL_ENDFUNC + +;; Allocate non-array object with finalizer. +;; ECX == MethodTable +FASTCALL_FUNC RhpNewFinalizable, 4 + ;; Create EBP frame. + push ebp + mov ebp, esp + + PUSH_COOP_PINVOKE_FRAME edx + + ;; Preserve MethodTable in ESI + mov esi, ecx + + ;; Push alloc helper arguments + push edx ; transition frame + push 0 ; numElements + mov edx, GC_ALLOC_FINALIZE ; Flags + ;; Passing MethodTable in ecx + + ;; void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + call RhpGcAlloc + + test eax, eax + jz NewFinalizable_OOM + + POP_COOP_PINVOKE_FRAME + + ;; Collapse EBP frame and return + pop ebp + ret + +NewFinalizable_OOM: + ;; This is the failure path. We're going to tail-call to a managed helper that will throw + ;; an out of memory exception that the caller of this allocator understands. + + mov eax, esi ; Preserve MethodTable pointer over POP_COOP_PINVOKE_FRAME + + POP_COOP_PINVOKE_FRAME + + ;; Cleanup our ebp frame + pop ebp + + mov ecx, eax ; MethodTable pointer + xor edx, edx ; Indicate that we should throw OOM. + jmp RhExceptionHandling_FailedAllocation + +FASTCALL_ENDFUNC + +;; Allocate a new string. +;; ECX == MethodTable +;; EDX == element count +FASTCALL_FUNC RhNewString, 8 + + push ecx + push edx + + ;; Make sure computing the aligned overall allocation size won't overflow + cmp edx, MAX_STRING_LENGTH + ja StringSizeOverflow + + ; Compute overall allocation size (align(base size + (element size * elements), 4)). + lea eax, [(edx * STRING_COMPONENT_SIZE) + (STRING_BASE_SIZE + 3)] + and eax, -4 + + ; ECX == MethodTable + ; EAX == allocation size + ; EDX == scratch + + INLINE_GETTHREAD edx, ecx ; edx = GetThread(), TRASHES ecx + + ; ECX == scratch + ; EAX == allocation size + ; EDX == thread + + mov ecx, eax + add eax, [edx + OFFSETOF__Thread__m_alloc_context__alloc_ptr] + jc StringAllocContextOverflow + cmp eax, [edx + OFFSETOF__Thread__m_alloc_context__alloc_limit] + ja StringAllocContextOverflow + + ; ECX == allocation size + ; EAX == new alloc ptr + ; EDX == thread + + ; set the new alloc pointer + mov [edx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], eax + + ; calc the new object pointer + sub eax, ecx + + pop edx + pop ecx + + ; set the new object's MethodTable pointer and element count + mov [eax + OFFSETOF__Object__m_pEEType], ecx + mov [eax + OFFSETOF__String__m_Length], edx + ret + +StringAllocContextOverflow: + ; ECX == string size + ; original ECX pushed + ; original EDX pushed + + ; Re-push original ECX + push [esp + 4] + + ; Create EBP frame. + mov [esp + 8], ebp + lea ebp, [esp + 8] + + PUSH_COOP_PINVOKE_FRAME edx + + ; Get the MethodTable and put it in ecx. + mov ecx, dword ptr [ebp - 8] + + ; Push alloc helper arguments (thread, size, flags, MethodTable). + push edx ; transition frame + push [ebp - 4] ; numElements + xor edx, edx ; Flags + ;; Passing MethodTable in ecx + + ;; void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + call RhpGcAlloc + + test eax, eax + jz StringOutOfMemoryWithFrame + + POP_COOP_PINVOKE_FRAME + add esp, 8 ; pop ecx / edx + pop ebp + ret + +StringOutOfMemoryWithFrame: + ; This is the OOM failure path. We're going to tail-call to a managed helper that will throw + ; an out of memory exception that the caller of this allocator understands. + + mov eax, [ebp - 8] ; Preserve MethodTable pointer over POP_COOP_PINVOKE_FRAME + + POP_COOP_PINVOKE_FRAME + add esp, 8 ; pop ecx / edx + pop ebp ; restore ebp + + mov ecx, eax ; MethodTable pointer + xor edx, edx ; Indicate that we should throw OOM. + jmp RhExceptionHandling_FailedAllocation + +StringSizeOverflow: + ;; We get here if the size of the final string object can't be represented as an unsigned + ;; 32-bit value. We're going to tail-call to a managed helper that will throw + ;; an OOM exception that the caller of this allocator understands. + + add esp, 8 ; pop ecx / edx + + ;; ecx holds MethodTable pointer already + xor edx, edx ; Indicate that we should throw OOM. + jmp RhExceptionHandling_FailedAllocation + +FASTCALL_ENDFUNC + + +;; Allocate one dimensional, zero based array (SZARRAY). +;; ECX == MethodTable +;; EDX == element count +FASTCALL_FUNC RhpNewArray, 8 + + push ecx + push edx + + ; Compute overall allocation size (align(base size + (element size * elements), 4)). + ; if the element count is <= 0x10000, no overflow is possible because the component size is + ; <= 0xffff, and thus the product is <= 0xffff0000, and the base size for the worst case + ; (32 dimensional MdArray) is less than 0xffff. + movzx eax, word ptr [ecx + OFFSETOF__MethodTable__m_usComponentSize] + cmp edx,010000h + ja ArraySizeBig + mul edx + add eax, [ecx + OFFSETOF__MethodTable__m_uBaseSize] + add eax, 3 +ArrayAlignSize: + and eax, -4 + + ; ECX == MethodTable + ; EAX == array size + ; EDX == scratch + + INLINE_GETTHREAD edx, ecx ; edx = GetThread(), TRASHES ecx + + ; ECX == scratch + ; EAX == array size + ; EDX == thread + + mov ecx, eax + add eax, [edx + OFFSETOF__Thread__m_alloc_context__alloc_ptr] + jc ArrayAllocContextOverflow + cmp eax, [edx + OFFSETOF__Thread__m_alloc_context__alloc_limit] + ja ArrayAllocContextOverflow + + ; ECX == array size + ; EAX == new alloc ptr + ; EDX == thread + + ; set the new alloc pointer + mov [edx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], eax + + ; calc the new object pointer + sub eax, ecx + + pop edx + pop ecx + + ; set the new object's MethodTable pointer and element count + mov [eax + OFFSETOF__Object__m_pEEType], ecx + mov [eax + OFFSETOF__Array__m_Length], edx + ret + +ArraySizeBig: + ; Compute overall allocation size (align(base size + (element size * elements), 4)). + ; if the element count is negative, it's an overflow, otherwise it's out of memory + cmp edx, 0 + jl ArraySizeOverflow + mul edx + jc ArrayOutOfMemoryNoFrame + add eax, [ecx + OFFSETOF__MethodTable__m_uBaseSize] + jc ArrayOutOfMemoryNoFrame + add eax, 3 + jc ArrayOutOfMemoryNoFrame + jmp ArrayAlignSize + +ArrayAllocContextOverflow: + ; ECX == array size + ; original ECX pushed + ; original EDX pushed + + ; Re-push original ECX + push [esp + 4] + + ; Create EBP frame. + mov [esp + 8], ebp + lea ebp, [esp + 8] + + PUSH_COOP_PINVOKE_FRAME edx + + ; Get the MethodTable and put it in ecx. + mov ecx, dword ptr [ebp - 8] + + ; Push alloc helper arguments (thread, size, flags, MethodTable). + push edx ; transition frame + push [ebp - 4] ; numElements + xor edx, edx ; Flags + ;; Passing MethodTable in ecx + + ;; void* RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame) + call RhpGcAlloc + + test eax, eax + jz ArrayOutOfMemoryWithFrame + + POP_COOP_PINVOKE_FRAME + add esp, 8 ; pop ecx / edx + pop ebp + ret + +ArrayOutOfMemoryWithFrame: + ; This is the OOM failure path. We're going to tail-call to a managed helper that will throw + ; an out of memory exception that the caller of this allocator understands. + + mov eax, [ebp - 8] ; Preserve MethodTable pointer over POP_COOP_PINVOKE_FRAME + + POP_COOP_PINVOKE_FRAME + add esp, 8 ; pop ecx / edx + pop ebp ; restore ebp + + mov ecx, eax ; MethodTable pointer + xor edx, edx ; Indicate that we should throw OOM. + jmp RhExceptionHandling_FailedAllocation + +ArrayOutOfMemoryNoFrame: + add esp, 8 ; pop ecx / edx + + ; ecx holds MethodTable pointer already + xor edx, edx ; Indicate that we should throw OOM. + jmp RhExceptionHandling_FailedAllocation + +ArraySizeOverflow: + ; We get here if the size of the final array object can't be represented as an unsigned + ; 32-bit value. We're going to tail-call to a managed helper that will throw + ; an overflow exception that the caller of this allocator understands. + + add esp, 8 ; pop ecx / edx + + ; ecx holds MethodTable pointer already + mov edx, 1 ; Indicate that we should throw OverflowException + jmp RhExceptionHandling_FailedAllocation + +FASTCALL_ENDFUNC + + end diff --git a/src/coreclr/nativeaot/Runtime/i386/AsmMacros.inc b/src/coreclr/nativeaot/Runtime/i386/AsmMacros.inc new file mode 100644 index 00000000000000..5abc9842dbffd4 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/AsmMacros.inc @@ -0,0 +1,215 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +include AsmOffsets.inc ; generated by the build from AsmOffsets.cpp + +;; +;; MACROS +;; + +FASTCALL_FUNC macro FuncName,cbArgs + FuncNameReal EQU @&FuncName&@&cbArgs + FuncNameReal proc public +endm + +FASTCALL_ENDFUNC macro + FuncNameReal endp +endm + +ALTERNATE_ENTRY macro Name + +decoratedName TEXTEQU @CatStr( _, Name ) ) + +decoratedName label proc +PUBLIC decoratedName + endm + +LABELED_RETURN_ADDRESS macro Name + +decoratedName TEXTEQU @CatStr( _, Name ) ) + +decoratedName label proc +PUBLIC decoratedName + endm + +EXPORT_POINTER_TO_ADDRESS macro Name + + local AddressToExport + +AddressToExport label proc + + .const + + align 4 + +Name dd offset AddressToExport + + public Name + + .code + + endm + +__tls_array equ 2Ch ;; offsetof(TEB, ThreadLocalStoragePointer) + +;; +;; __declspec(thread) version +;; +INLINE_GETTHREAD macro destReg, trashReg + ASSUME fs : NOTHING + EXTERN __tls_index : DWORD + EXTERN _tls_CurrentThread : DWORD + + mov destReg, [__tls_index] + mov trashReg, fs:[__tls_array] + mov destReg, [trashReg + destReg * 4] + add destReg, SECTIONREL _tls_CurrentThread +endm + + +INLINE_THREAD_UNHIJACK macro threadReg, trashReg1, trashReg2 + ;; + ;; Thread::Unhijack() + ;; + mov trashReg1, [threadReg + OFFSETOF__Thread__m_pvHijackedReturnAddress] + cmp trashReg1, 0 + je @F + + mov trashReg2, [threadReg + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + mov [trashReg2], trashReg1 + mov dword ptr [threadReg + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], 0 + mov dword ptr [threadReg + OFFSETOF__Thread__m_pvHijackedReturnAddress], 0 + +@@: +endm + +;; +;; Macro used from unmanaged helpers called from managed code where the helper does not transition immediately +;; into pre-emptive mode but may cause a GC and thus requires the stack is crawlable. This is typically the +;; case for helpers that meddle in GC state (e.g. allocation helpers) where the code must remain in +;; cooperative mode since it handles object references and internal GC state directly but a garbage collection +;; may be inevitable. In these cases we need to be able to transition to pre-meptive mode deep within the +;; unmanaged code but still be able to initialize the stack iterator at the first stack frame which may hold +;; interesting GC references. In all our helper cases this corresponds to the most recent managed frame (e.g. +;; the helper's caller). +;; +;; This macro builds a frame describing the current state of managed code. +;; +;; The macro assumes it is called from a helper that has already set up an EBP frame and that the values of +;; EBX, ESI and EDI remain unchanged from their values in managed code. It pushes the frame at the top of the +;; stack. +;; +;; EAX is trashed by this macro. +;; +PUSH_COOP_PINVOKE_FRAME macro transitionFrameReg + lea eax, [ebp + 8] ; get the ESP of the caller + push eax ; save ESP + push edi + push esi + push ebx + push PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_RSP + push eax ; Thread * (unused by stackwalker) + mov eax, [ebp + 0] ; Find previous EBP value + push eax ; save EBP + mov eax, [ebp + 4] ; Find the return address + push eax ; save m_RIP + + lea transitionFrameReg, [esp + 0] ; transitionFrameReg == address of frame +endm + +;; +;; Remove the frame from a previous call to PUSH_COOP_PINVOKE_FRAME from the top of the stack and restore EBX, +;; ESI and EDI to their previous values. +;; +;; TRASHES ECX +;; +POP_COOP_PINVOKE_FRAME macro + add esp, 4*4 + pop ebx + pop esi + pop edi + pop ecx +endm + + +;; +;; CONSTANTS -- INTEGER +;; +TSF_Attached equ 01h +TSF_SuppressGcStress equ 08h +TSF_DoNotTriggerGc equ 10h + +;; GC type flags +GC_ALLOC_FINALIZE equ 1 + +;; Note: these must match the defs in PInvokeTransitionFrameFlags +PTFF_SAVE_RBX equ 00000001h +PTFF_SAVE_RSI equ 00000002h +PTFF_SAVE_RDI equ 00000004h +PTFF_SAVE_ALL_PRESERVED equ 00000007h ;; NOTE: RBP is not included in this set! +PTFF_SAVE_RSP equ 00008000h +PTFF_SAVE_RAX equ 00000100h ;; RAX is saved if it contains a GC ref and we're in hijack handler +PTFF_SAVE_ALL_SCRATCH equ 00000700h +PTFF_RAX_IS_GCREF equ 00010000h ;; iff PTFF_SAVE_RAX: set -> eax is Object, clear -> eax is scalar +PTFF_RAX_IS_BYREF equ 00020000h ;; iff PTFF_SAVE_RAX: set -> eax is ByRef, clear -> eax is Object or scalar +PTFF_THREAD_ABORT equ 00040000h ;; indicates that ThreadAbortException should be thrown when returning from the transition + +;; These must match the TrapThreadsFlags enum +TrapThreadsFlags_None equ 0 +TrapThreadsFlags_AbortInProgress equ 1 +TrapThreadsFlags_TrapThreads equ 2 + +;; This must match HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT +STATUS_REDHAWK_THREAD_ABORT equ 43h + +;; +;; Rename fields of nested structs +;; +OFFSETOF__Thread__m_alloc_context__alloc_ptr equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr +OFFSETOF__Thread__m_alloc_context__alloc_limit equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit + +;; +;; CONSTANTS -- SYMBOLS +;; + +RhDebugBreak equ @RhDebugBreak@0 +RhpGcAlloc equ @RhpGcAlloc@16 +G_LOWEST_ADDRESS equ _g_lowest_address +G_HIGHEST_ADDRESS equ _g_highest_address +G_EPHEMERAL_LOW equ _g_ephemeral_low +G_EPHEMERAL_HIGH equ _g_ephemeral_high +G_CARD_TABLE equ _g_card_table +RhpWaitForSuspend2 equ @RhpWaitForSuspend2@0 +RhpWaitForGC2 equ @RhpWaitForGC2@4 +RhpReversePInvokeAttachOrTrapThread2 equ @RhpReversePInvokeAttachOrTrapThread2@4 +RhpTrapThreads equ _RhpTrapThreads + +ifdef FEATURE_GC_STRESS +THREAD__HIJACKFORGCSTRESS equ ?HijackForGcStress@Thread@@SGXPAUPAL_LIMITED_CONTEXT@@@Z +REDHAWKGCINTERFACE__STRESSGC equ ?StressGc@RedhawkGCInterface@@SGXXZ +endif ;; FEATURE_GC_STRESS + +;; +;; IMPORTS +;; +EXTERN RhpGcAlloc : PROC +EXTERN RhDebugBreak : PROC +EXTERN RhpWaitForSuspend2 : PROC +EXTERN RhpWaitForGC2 : PROC +EXTERN RhpReversePInvokeAttachOrTrapThread2 : PROC +EXTERN RhExceptionHandling_FailedAllocation : PROC +EXTERN RhThrowHwEx : PROC +EXTERN RhThrowEx : PROC +EXTERN RhRethrow : PROC + +ifdef FEATURE_GC_STRESS +EXTERN THREAD__HIJACKFORGCSTRESS : PROC +EXTERN REDHAWKGCINTERFACE__STRESSGC : PROC +endif ;; FEATURE_GC_STRESS + +EXTERN G_LOWEST_ADDRESS : DWORD +EXTERN G_HIGHEST_ADDRESS : DWORD +EXTERN G_EPHEMERAL_LOW : DWORD +EXTERN G_EPHEMERAL_HIGH : DWORD +EXTERN G_CARD_TABLE : DWORD +EXTERN RhpTrapThreads : DWORD diff --git a/src/coreclr/nativeaot/Runtime/i386/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/i386/AsmOffsetsCpu.h new file mode 100644 index 00000000000000..a92f24d789b47a --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/AsmOffsetsCpu.h @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This file is used by AsmOffsets.h to validate that our +// assembly-code offsets always match their C++ counterparts. +// +// NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix + +PLAT_ASM_SIZEOF(c0, ExInfo) +PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) +PLAT_ASM_OFFSET(4, ExInfo, m_pExContext) +PLAT_ASM_OFFSET(8, ExInfo, m_exception) +PLAT_ASM_OFFSET(0c, ExInfo, m_kind) +PLAT_ASM_OFFSET(0d, ExInfo, m_passNumber) +PLAT_ASM_OFFSET(10, ExInfo, m_idxCurClause) +PLAT_ASM_OFFSET(14, ExInfo, m_frameIter) +PLAT_ASM_OFFSET(bc, ExInfo, m_notifyDebuggerSP) + +PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP) +PLAT_ASM_OFFSET(4, PInvokeTransitionFrame, m_FramePointer) +PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_pThread) +PLAT_ASM_OFFSET(0c, PInvokeTransitionFrame, m_Flags) +PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_PreservedRegs) + +PLAT_ASM_SIZEOF(a8, StackFrameIterator) +PLAT_ASM_OFFSET(08, StackFrameIterator, m_FramePointer) +PLAT_ASM_OFFSET(0c, StackFrameIterator, m_ControlPC) +PLAT_ASM_OFFSET(10, StackFrameIterator, m_RegDisplay) +PLAT_ASM_OFFSET(a4, StackFrameIterator, m_OriginalControlPC) + +PLAT_ASM_SIZEOF(1c, PAL_LIMITED_CONTEXT) +PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, IP) + +PLAT_ASM_OFFSET(4, PAL_LIMITED_CONTEXT, Rsp) +PLAT_ASM_OFFSET(8, PAL_LIMITED_CONTEXT, Rbp) +PLAT_ASM_OFFSET(0c, PAL_LIMITED_CONTEXT, Rdi) +PLAT_ASM_OFFSET(10, PAL_LIMITED_CONTEXT, Rsi) +PLAT_ASM_OFFSET(14, PAL_LIMITED_CONTEXT, Rax) +PLAT_ASM_OFFSET(18, PAL_LIMITED_CONTEXT, Rbx) + +PLAT_ASM_SIZEOF(28, REGDISPLAY) +PLAT_ASM_OFFSET(1c, REGDISPLAY, SP) + +PLAT_ASM_OFFSET(0c, REGDISPLAY, pRbx) +PLAT_ASM_OFFSET(10, REGDISPLAY, pRbp) +PLAT_ASM_OFFSET(14, REGDISPLAY, pRsi) +PLAT_ASM_OFFSET(18, REGDISPLAY, pRdi) diff --git a/src/coreclr/nativeaot/Runtime/i386/CallDescrWorker.S b/src/coreclr/nativeaot/Runtime/i386/CallDescrWorker.S new file mode 100644 index 00000000000000..876f2dfbcb80d6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/CallDescrWorker.S @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// TODO: Implement diff --git a/src/coreclr/nativeaot/Runtime/i386/CallDescrWorker.asm b/src/coreclr/nativeaot/Runtime/i386/CallDescrWorker.asm new file mode 100644 index 00000000000000..481cba1d898863 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/CallDescrWorker.asm @@ -0,0 +1,96 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + .586 + .model flat + option casemap:none + .code + +include AsmMacros.inc + +ifdef FEATURE_DYNAMIC_CODE +;;;;;;;;;;;;;;;;;;;;;;; CallingConventionConverter Thunks Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------------------------------------------------------------------------------ +; This helper routine enregisters the appropriate arguments and makes the +; actual call. +;------------------------------------------------------------------------------ +; void __fastcall CallDescrWorker(CallDescrWorkerParams * pParams) +FASTCALL_FUNC RhCallDescrWorker, 4 + push ebp + mov ebp, esp + push ebx + mov ebx, ecx + + mov ecx, [ebx + OFFSETOF__CallDescrData__numStackSlots] + mov eax, [ebx + OFFSETOF__CallDescrData__pSrc] ; copy the stack + test ecx, ecx + jz donestack + lea eax, [eax + 4 * ecx - 4] ; last argument + push dword ptr [eax] + dec ecx + jz donestack + sub eax, 4 + push dword ptr [eax] + dec ecx + jz donestack +stackloop: + sub eax, 4 + push dword ptr [eax] + dec ecx + jnz stackloop +donestack: + + ; now we must push each field of the ArgumentRegister structure + mov eax, [ebx + OFFSETOF__CallDescrData__pArgumentRegisters] + mov edx, dword ptr [eax] + mov ecx, dword ptr [eax + 4] + mov eax,[ebx + OFFSETOF__CallDescrData__pTarget] + call eax + + EXPORT_POINTER_TO_ADDRESS _PointerToReturnFromCallDescrThunk + + ; Symbol used to identify thunk call to managed function so the special + ; case unwinder can unwind through this function. Sadly we cannot directly + ; export this symbol right now because it confuses DIA unwinder to believe + ; it's the beginning of a new method, therefore we export the address + ; by means of an auxiliary variable. + + ; Save FP return value if necessary + mov ecx, [ebx + OFFSETOF__CallDescrData__fpReturnSize] + cmp ecx, 0 + je ReturnsInt + + cmp ecx, 4 + je ReturnsFloat + cmp ecx, 8 + je ReturnsDouble + ; unexpected + jmp Epilog + +ReturnsInt: +; Unlike desktop returnValue is a pointer to a return buffer, not the buffer itself + mov ebx, [ebx + OFFSETOF__CallDescrData__pReturnBuffer] + mov [ebx], eax + mov [ebx + 4], edx + +Epilog: + pop ebx + pop ebp + retn + +ReturnsFloat: + mov ebx, [ebx + OFFSETOF__CallDescrData__pReturnBuffer] + fstp dword ptr [ebx] ; Spill the Float return value + jmp Epilog + +ReturnsDouble: + mov ebx, [ebx + OFFSETOF__CallDescrData__pReturnBuffer] + fstp qword ptr [ebx] ; Spill the Double return value + jmp Epilog + +FASTCALL_ENDFUNC + +endif + +end diff --git a/src/coreclr/nativeaot/Runtime/i386/CallingConventionConverterHelpers.S b/src/coreclr/nativeaot/Runtime/i386/CallingConventionConverterHelpers.S new file mode 100644 index 00000000000000..876f2dfbcb80d6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/CallingConventionConverterHelpers.S @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// TODO: Implement diff --git a/src/coreclr/nativeaot/Runtime/i386/CallingConventionConverterHelpers.asm b/src/coreclr/nativeaot/Runtime/i386/CallingConventionConverterHelpers.asm new file mode 100644 index 00000000000000..bd46406dbc9c67 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/CallingConventionConverterHelpers.asm @@ -0,0 +1,126 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +.586 +.model flat +option casemap:none +.code + +;; ----------------------------------------------------------------------------------------------------------- +;; standard macros +;; ----------------------------------------------------------------------------------------------------------- +LEAF_ENTRY macro Name, Section + Section segment para 'CODE' + public Name + Name proc +endm + +LEAF_END macro Name, Section + Name endp + Section ends +endm + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; +;; struct ReturnBlock +;; { +;; 8 bytes of space +;; Used to hold return information. +;; eax, and 32bit float returns use the first 4 bytes, +;; eax,edx and 64bit float returns use the full 8 bytes +;; }; +;; + +ReturnInformation__ReturnData EQU 4h + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interop Thunks Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; ? CallingConventionConverter_ReturnVoidReturnThunk(int cbBytesOfStackToPop) +;; +LEAF_ENTRY CallingConventionConverter_ReturnVoidReturnThunk, _TEXT + pop edx ; pop return address into edx + add esp,ecx ; remove ecx bytes from the call stack + push edx ; put the return address back on the stack + ret ; return to it (use a push/ret pair here so that the return stack buffer still works) +LEAF_END CallingConventionConverter_ReturnVoidReturnThunk, _TEXT + +;; +;; int CallingConventionConverter_ReturnIntegerReturnThunk(int cbBytesOfStackToPop, ReturnBlock*) +;; +LEAF_ENTRY CallingConventionConverter_ReturnIntegerReturnThunk, _TEXT + pop eax ; pop return address into edx + add esp,ecx ; remove ecx bytes from the call stack + push eax ; put the return address back on the stack + mov eax, [edx] ; setup eax and edx to hold the return value + mov edx, [edx + 4] + ret ; return (use a push/ret pair here so that the return stack buffer still works) +LEAF_END CallingConventionConverter_ReturnIntegerReturnThunk, _TEXT + +;; +;; float CallingConventionConverter_Return4ByteFloatReturnThunk(int cbBytesOfStackToPop, ReturnBlock*) +;; +LEAF_ENTRY CallingConventionConverter_Return4ByteFloatReturnThunk, _TEXT + pop eax ; pop return address into edx + add esp,ecx ; remove ecx bytes from the call stack + push eax ; put the return address back on the stack + fld dword ptr [edx]; fill in the return value + ret ; return (use a push/ret pair here so that the return stack buffer still works) +LEAF_END CallingConventionConverter_Return4ByteFloatReturnThunk, _TEXT + +;; +;; double CallingConventionConverter_Return4ByteFloatReturnThunk(int cbBytesOfStackToPop, ReturnBlock*) +;; +LEAF_ENTRY CallingConventionConverter_Return8ByteFloatReturnThunk, _TEXT + pop eax ; pop return address into edx + add esp,ecx ; remove ecx bytes from the call stack + push eax ; put the return address back on the stack + fld qword ptr [edx]; fill in the return value + ret ; return (use a push/ret pair here so that the return stack buffer still works) +LEAF_END CallingConventionConverter_Return8ByteFloatReturnThunk, _TEXT + +;; +;; Note: The "__jmpstub__" prefix is used to indicate to debugger +;; that it must step-through this stub when it encounters it while +;; stepping. +;; + +;; +;; __jmpstub__CallingConventionConverter_CommonCallingStub(?) +;; +LEAF_ENTRY __jmpstub__CallingConventionConverter_CommonCallingStub, _TEXT + ;; rax <- stub info + push ebp + mov ebp, esp + push [eax] ; First argument + mov eax,[eax+4] ; + push [eax] ; Pointer to CallingConventionConverter Managed thunk + mov eax,[eax+4] ; Pointer to UniversalTransitionThunk + jmp eax +LEAF_END __jmpstub__CallingConventionConverter_CommonCallingStub, _TEXT + + ;; + ;; void CallingConventionConverter_GetStubs(IntPtr *returnVoidStub, IntPtr *returnIntegerStub, IntPtr* commonCallingStub, IntPtr *return4ByteFloat, IntPtr *return8ByteFloat) + ;; +LEAF_ENTRY CallingConventionConverter_GetStubs, _TEXT + lea eax, [CallingConventionConverter_ReturnVoidReturnThunk] + mov ecx, [esp+04h] + mov [ecx], eax + lea eax, [CallingConventionConverter_ReturnIntegerReturnThunk] + mov ecx, [esp+08h] + mov [ecx], eax + lea eax, [__jmpstub__CallingConventionConverter_CommonCallingStub] + mov ecx, [esp+0Ch] + mov [ecx], eax + lea eax, [CallingConventionConverter_Return4ByteFloatReturnThunk] + mov ecx, [esp+10h] + mov [ecx], eax + lea eax, [CallingConventionConverter_Return8ByteFloatReturnThunk] + mov ecx, [esp+14h] + mov [ecx], eax + retn 14h +LEAF_END CallingConventionConverter_GetStubs, _TEXT + + +end diff --git a/src/coreclr/nativeaot/Runtime/i386/DivModHelpers.asm b/src/coreclr/nativeaot/Runtime/i386/DivModHelpers.asm new file mode 100644 index 00000000000000..f13321a33c5542 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/DivModHelpers.asm @@ -0,0 +1,256 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + + .586 + .model flat + option casemap:none + .code + + +include AsmMacros.inc + +EXTERN RhExceptionHandling_ThrowClasslibOverflowException : PROC +EXTERN RhExceptionHandling_ThrowClasslibDivideByZeroException : PROC +EXTERN __alldiv : PROC +EXTERN __allrem : PROC +EXTERN __aulldiv : PROC +EXTERN __aullrem : PROC +EXTERN __aulldvrm : PROC +EXTERN __alldvrm : PROC + +esp_offsetof_dividend_low equ 4 +esp_offsetof_dividend_high equ 8 +esp_offsetof_divisor_low equ 12 +esp_offsetof_divisor_high equ 16 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpLDiv +;; +;; INPUT: [ESP+4]: dividend low +;; [ESP+8]: dividend high +;; [ESP+12]: divisor low +;; [ESP+16]: divisor high +;; +;; OUTPUT: EAX: result low +;; EDX: result high +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpLDiv, 16 + + ;; pretest for the problematic cases of overflow and divide by zero + ;; overflow: dividend = 0x80000000`00000000 and divisor = -1l = 0xffffffff`ffffffff + ;; divide by zero: divisor = 0x00000000`00000000 + ;; + ;; quick pretest - if the two halves of the divisor are unequal, we cannot + ;; have one of the problematic cases + mov eax,[esp+esp_offsetof_divisor_low] + cmp eax,[esp+esp_offsetof_divisor_high] + je LDivDoMoreTests +LDivOkToDivide: + ;; tailcall to the actual divide routine + jmp __alldiv +LDivDoMoreTests: + ;; we know the high and low halves of the divisor are equal + ;; + ;; check for the divide by zero case + test eax,eax + je ThrowClasslibDivideByZeroException + ;; + ;; is the divisor == -1l? I.e., can we have the overflow case? + cmp eax,-1 + jne LDivOkToDivide + ;; + ;; is the dividend == 0x80000000`00000000? + cmp dword ptr [esp+esp_offsetof_dividend_low],0 + jne LDivOkToDivide + cmp dword ptr [esp+esp_offsetof_dividend_high],80000000h + jne LDivOkToDivide +FASTCALL_ENDFUNC + + ;; make it look like the managed code called this directly + ;; by popping the parameters and putting the return address in the proper place +ThrowClasslibOverflowException proc + pop ecx + add esp,16 + push ecx + ;; passing return address in ecx + jmp RhExceptionHandling_ThrowClasslibOverflowException +ThrowClasslibOverflowException endp + +ThrowClasslibDivideByZeroException proc + pop ecx + add esp,16 + push ecx + ;; passing return address in ecx + jmp RhExceptionHandling_ThrowClasslibDivideByZeroException +ThrowClasslibDivideByZeroException endp + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpLMod +;; +;; INPUT: [ESP+4]: dividend low +;; [ESP+8]: dividend high +;; [ESP+12]: divisor low +;; [ESP+16]: divisor high +;; +;; OUTPUT: EAX: result low +;; EDX: result high +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpLMod, 16 + + ;; pretest for the problematic cases of overflow and divide by zero + ;; overflow: dividend = 0x80000000`00000000 and divisor = -1l = 0xffffffff`ffffffff + ;; divide by zero: divisor = 0x00000000`00000000 + ;; + ;; quick pretest - if the two halves of the divisor are unequal, we cannot + ;; have one of the problematic cases + mov eax,[esp+esp_offsetof_divisor_low] + cmp eax,[esp+esp_offsetof_divisor_high] + je LModDoMoreTests +LModOkToDivide: + jmp __allrem +LModDoMoreTests: + ;; we know the high and low halves of the divisor are equal + ;; + ;; check for the divide by zero case + test eax,eax + je ThrowClasslibDivideByZeroException + ;; + ;; is the divisor == -1l? I.e., can we have the overflow case? + cmp eax,-1 + jne LModOkToDivide + ;; + ;; is the dividend == 0x80000000`00000000? + cmp dword ptr [esp+esp_offsetof_dividend_low],0 + jne LModOkToDivide + cmp dword ptr [esp+esp_offsetof_dividend_high],80000000h + jne LModOkToDivide + jmp ThrowClasslibOverflowException + +FASTCALL_ENDFUNC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpLDivMod +;; +;; INPUT: [ESP+4]: dividend low +;; [ESP+8]: dividend high +;; [ESP+12]: divisor low +;; [ESP+16]: divisor high +;; +;; OUTPUT: EAX: quotient low +;; EDX: quotient high +;; ECX: remainder high +;; EBX: remainder high +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpLDivMod, 16 + + ;; pretest for the problematic cases of overflow and divide by zero + ;; overflow: dividend = 0x80000000`00000000 and divisor = -1l = 0xffffffff`ffffffff + ;; divide by zero: divisor = 0x00000000`00000000 + ;; + ;; quick pretest - if the two halves of the divisor are unequal, we cannot + ;; have one of the problematic cases + mov eax,[esp+esp_offsetof_divisor_low] + cmp eax,[esp+esp_offsetof_divisor_high] + je LDivModDoMoreTests +LDivModOkToDivide: + jmp __alldvrm +LDivModDoMoreTests: + ;; we know the high and low halves of the divisor are equal + ;; + ;; check for the divide by zero case + test eax,eax + je ThrowClasslibDivideByZeroException + ;; + ;; is the divisor == -1l? I.e., can we have the overflow case? + cmp eax,-1 + jne LDivModOkToDivide + ;; + ;; is the dividend == 0x80000000`00000000? + cmp dword ptr [esp+esp_offsetof_dividend_low],0 + jne LDivModOkToDivide + cmp dword ptr [esp+esp_offsetof_dividend_high],80000000h + jne LDivModOkToDivide + jmp ThrowClasslibOverflowException + +FASTCALL_ENDFUNC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpULDiv +;; +;; INPUT: [ESP+4]: dividend low +;; [ESP+8]: dividend high +;; [ESP+12]: divisor low +;; [ESP+16]: divisor high +;; +;; OUTPUT: EAX: result low +;; EDX: result high +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpULDiv, 16 + + ;; pretest for divide by zero + mov eax,[esp+esp_offsetof_divisor_low] + or eax,[esp+esp_offsetof_divisor_high] + jne __aulldiv + jmp ThrowClasslibDivideByZeroException + +FASTCALL_ENDFUNC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpULMod +;; +;; INPUT: [ESP+4]: dividend low +;; [ESP+8]: dividend high +;; [ESP+12]: divisor low +;; [ESP+16]: divisor high +;; +;; OUTPUT: EAX: result low +;; EDX: result high +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpULMod, 16 + + ;; pretest for divide by zero + mov eax,[esp+esp_offsetof_divisor_low] + or eax,[esp+esp_offsetof_divisor_high] + jne __aullrem + jmp ThrowClasslibDivideByZeroException + +FASTCALL_ENDFUNC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpULDivMod +;; +;; INPUT: [ESP+4]: dividend low +;; [ESP+8]: dividend high +;; [ESP+12]: divisor low +;; [ESP+16]: divisor high +;; +;; OUTPUT: EAX: quotient low +;; EDX: quotient high +;; ECX: remainder high +;; EBX: remainder high +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpULDivMod, 16 + + ;; pretest for divide by zero + mov eax,[esp+esp_offsetof_divisor_low] + or eax,[esp+esp_offsetof_divisor_high] + jne __aulldvrm + jmp ThrowClasslibDivideByZeroException + +FASTCALL_ENDFUNC + + + end diff --git a/src/coreclr/nativeaot/Runtime/i386/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/i386/ExceptionHandling.S new file mode 100644 index 00000000000000..876f2dfbcb80d6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/ExceptionHandling.S @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// TODO: Implement diff --git a/src/coreclr/nativeaot/Runtime/i386/ExceptionHandling.asm b/src/coreclr/nativeaot/Runtime/i386/ExceptionHandling.asm new file mode 100644 index 00000000000000..42251d21e46f98 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/ExceptionHandling.asm @@ -0,0 +1,480 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + + .586 + .model flat + option casemap:none + .code + + +include AsmMacros.inc + +RhpCallFunclet equ @RhpCallFunclet@0 +RhpThrowHwEx equ @RhpThrowHwEx@0 + +extern RhpCallFunclet : proc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowHwEx +;; +;; INPUT: ECX: exception code of fault +;; EDX: faulting RIP +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpThrowHwEx, 0 + + esp_offsetof_ExInfo textequ %0 + esp_offsetof_Context textequ %SIZEOF__ExInfo + + push edx ; make it look like we were called by pushing the faulting IP like a return address + push ebp + mov ebp, esp + + lea eax, [esp+8] ;; calculate the RSP of the throw site + ;; edx already contains the throw site IP + +;; struct PAL_LIMITED_CONTEXT +;; { + push ebx + push eax + push esi + push edi + mov ebx, [ebp] + push ebx ;; 'faulting' Rbp + push eax ;; 'faulting' Rsp + push edx ;; 'faulting' IP +;; }; + + sub esp, SIZEOF__ExInfo + + INLINE_GETTHREAD eax, edx ;; eax <- thread, edx <- trashed + + lea edx, [esp + esp_offsetof_ExInfo] ;; edx <- ExInfo* + + xor esi, esi + mov [edx + OFFSETOF__ExInfo__m_exception], esi ;; init the exception object to null + mov byte ptr [edx + OFFSETOF__ExInfo__m_passNumber], 1 ;; init to the first pass + mov dword ptr [edx + OFFSETOF__ExInfo__m_idxCurClause], 0FFFFFFFFh + mov byte ptr [edx + OFFSETOF__ExInfo__m_kind], 2 ;; ExKind.HardwareFault + + ;; link the ExInfo into the thread's ExInfo chain + mov ebx, [eax + OFFSETOF__Thread__m_pExInfoStackHead] + mov [edx + OFFSETOF__ExInfo__m_pPrevExInfo], ebx ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + mov [eax + OFFSETOF__Thread__m_pExInfoStackHead], edx ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + lea ebx, [esp + esp_offsetof_Context] ;; ebx <- PAL_LIMITED_CONTEXT* + mov [edx + OFFSETOF__ExInfo__m_pExContext], ebx ;; init ExInfo.m_pExContext + + ;; ecx still contains the exception code + ;; edx contains the address of the ExInfo + call RhThrowHwEx + + EXPORT_POINTER_TO_ADDRESS _PointerToRhpThrowHwEx2 + + ;; no return + int 3 + +FASTCALL_ENDFUNC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowEx +;; +;; INPUT: ECX: exception object +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpThrowEx, 0 + + esp_offsetof_ExInfo textequ %0 + esp_offsetof_Context textequ %SIZEOF__ExInfo + + push ebp + mov ebp, esp + + lea eax, [esp+8] ;; calculate the RSP of the throw site + mov edx, [esp+4] ;; get the throw site IP via the return address + +;; struct PAL_LIMITED_CONTEXT +;; { + push ebx + push eax + push esi + push edi + mov ebx, [ebp] + push ebx ;; 'faulting' Rbp + push eax ;; 'faulting' Rsp + push edx ;; 'faulting' IP +;; }; + + sub esp, SIZEOF__ExInfo + + ;; ------------------------- + + lea ebx, [eax-4] ;; ebx <- addr of return address + INLINE_GETTHREAD eax, edx ;; eax <- thread, edx <- trashed + + ;; There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return + ;; address could have been hijacked when we were in that C# code and we must remove the hijack and + ;; reflect the correct return address in our exception context record. The other throw helpers don't + ;; need this because they cannot be tail-called from C#. + + INLINE_THREAD_UNHIJACK eax, esi, edx ;; trashes esi, edx + + mov edx, [ebx] ;; edx <- return address + mov [esp + esp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP], edx ;; set 'faulting' IP after unhijack + + lea edx, [esp + esp_offsetof_ExInfo] ;; edx <- ExInfo* + + xor esi, esi + mov [edx + OFFSETOF__ExInfo__m_exception], esi ;; init the exception object to null + mov byte ptr [edx + OFFSETOF__ExInfo__m_passNumber], 1 ;; init to the first pass + mov dword ptr [edx + OFFSETOF__ExInfo__m_idxCurClause], 0FFFFFFFFh + mov byte ptr [edx + OFFSETOF__ExInfo__m_kind], 1 ;; ExKind.Throw + + ;; link the ExInfo into the thread's ExInfo chain + mov ebx, [eax + OFFSETOF__Thread__m_pExInfoStackHead] + mov [edx + OFFSETOF__ExInfo__m_pPrevExInfo], ebx ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + mov [eax + OFFSETOF__Thread__m_pExInfoStackHead], edx ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + lea ebx, [esp + esp_offsetof_Context] ;; ebx <- PAL_LIMITED_CONTEXT* + mov [edx + OFFSETOF__ExInfo__m_pExContext], ebx ;; init ExInfo.m_pExContext + + ;; ecx still contains the exception object + ;; edx contains the address of the ExInfo + call RhThrowEx + + EXPORT_POINTER_TO_ADDRESS _PointerToRhpThrowEx2 + + ;; no return + int 3 + +FASTCALL_ENDFUNC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void FASTCALL RhpRethrow() +;; +;; SUMMARY: Similar to RhpThrowEx, except that it passes along the currently active ExInfo +;; +;; INPUT: +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpRethrow, 0 + + + esp_offsetof_ExInfo textequ %0 + esp_offsetof_Context textequ %SIZEOF__ExInfo + + push ebp + mov ebp, esp + + lea eax, [esp+8] ;; calculate the RSP of the throw site + mov edx, [esp+4] ;; get the throw site IP via the return address + +;; struct PAL_LIMITED_CONTEXT +;; { + push ebx + push eax + push esi + push edi + mov ebx, [ebp] + push ebx ;; 'faulting' Rbp + push eax ;; 'faulting' Rsp + push edx ;; 'faulting' IP +;; }; + + sub esp, SIZEOF__ExInfo + + ;; ------------------------- + + lea ebx, [eax-4] ;; ebx <- addr of return address + INLINE_GETTHREAD eax, edx ;; eax <- thread, edx <- trashed + + lea edx, [esp + esp_offsetof_ExInfo] ;; edx <- ExInfo* + + xor esi, esi + mov [edx + OFFSETOF__ExInfo__m_exception], esi ;; init the exception object to null + mov byte ptr [edx + OFFSETOF__ExInfo__m_passNumber], 1 ;; init to the first pass + mov dword ptr [edx + OFFSETOF__ExInfo__m_idxCurClause], 0FFFFFFFFh + mov byte ptr [edx + OFFSETOF__ExInfo__m_kind], 0 ;; init to a deterministic value (ExKind.None) + + ;; link the ExInfo into the thread's ExInfo chain + mov ecx, [eax + OFFSETOF__Thread__m_pExInfoStackHead] ;; ecx <- currently active ExInfo + mov [edx + OFFSETOF__ExInfo__m_pPrevExInfo], ecx ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + mov [eax + OFFSETOF__Thread__m_pExInfoStackHead], edx ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + lea ebx, [esp + esp_offsetof_Context] ;; ebx <- PAL_LIMITED_CONTEXT* + mov [edx + OFFSETOF__ExInfo__m_pExContext], ebx ;; init ExInfo.m_pExContext + + ;; ecx contains the currently active ExInfo + ;; edx contains the address of the new ExInfo + call RhRethrow + + EXPORT_POINTER_TO_ADDRESS _PointerToRhpRethrow2 + + ;; no return + int 3 + +FASTCALL_ENDFUNC + +;; +;; Prologue of all funclet calling helpers (RhpCallXXXXFunclet) +;; +FUNCLET_CALL_PROLOGUE macro localsCount + push ebp + mov ebp, esp + + push ebx ;; save preserved registers (for the stackwalker) + push esi ;; + push edi ;; + + stack_alloc_size = localsCount * 4 + + if stack_alloc_size ne 0 + sub esp, stack_alloc_size + endif +endm + +;; +;; Epilogue of all funclet calling helpers (RhpCallXXXXFunclet) +;; +FUNCLET_CALL_EPILOGUE macro + if stack_alloc_size ne 0 + add esp, stack_alloc_size + endif + pop edi + pop esi + pop ebx + pop ebp +endm + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* FASTCALL RhpCallCatchFunclet(RtuObjectRef exceptionObj, void* pHandlerIP, REGDISPLAY* pRegDisplay, +;; ExInfo* pExInfo) +;; +;; INPUT: ECX: exception object +;; EDX: handler funclet address +;; [ESP + 4]: REGDISPLAY* +;; [ESP + 8]: ExInfo* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpCallCatchFunclet, 0 + + FUNCLET_CALL_PROLOGUE 2 + + esp_offsetof_ResumeIP textequ %00h ;; [esp + 00h]: continuation address + esp_offsetof_is_handling_thread_abort textequ %04h ;; [esp + 04h]: set if we are handling ThreadAbortException + ;; [esp + 08h]: edi save + ;; [esp + 0ch]: esi save + ;; [esp + 10h]: ebx save + esp_offsetof_PrevEBP textequ %14h ;; [esp + 14h]: prev ebp + esp_offsetof_RetAddr textequ %18h ;; [esp + 18h]: return address + esp_offsetof_RegDisplay textequ %1ch ;; [esp + 1Ch]: REGDISPLAY* + esp_offsetof_ExInfo textequ %20h ;; [esp + 20h]: ExInfo* + + ;; Clear the DoNotTriggerGc state before calling out to our managed catch funclet. + INLINE_GETTHREAD eax, ebx ;; eax <- Thread*, ebx is trashed + lock and dword ptr [eax + OFFSETOF__Thread__m_ThreadStateFlags], NOT TSF_DoNotTriggerGc + + cmp ecx, [eax + OFFSETOF__Thread__m_threadAbortException] + setz byte ptr [esp + esp_offsetof_is_handling_thread_abort] + + mov edi, [esp + esp_offsetof_RegDisplay] ;; edi <- REGDISPLAY * + + mov eax, [edi + OFFSETOF__REGDISPLAY__pRbx] + mov ebx, [eax] + + mov eax, [edi + OFFSETOF__REGDISPLAY__pRbp] + mov eax, [eax] + push eax ; save the funclet's EBP value for later + + mov eax, [edi + OFFSETOF__REGDISPLAY__pRsi] + mov esi, [eax] + + mov eax, [edi + OFFSETOF__REGDISPLAY__pRdi] + mov edi, [eax] + + pop eax ; get the funclet's EBP value + + ;; ECX still contains the exception object + ;; EDX: funclet IP + ;; EAX: funclet EBP + call RhpCallFunclet + + EXPORT_POINTER_TO_ADDRESS _PointerToRhpCallCatchFunclet2 + + ;; eax: resume IP + mov [esp + esp_offsetof_ResumeIP], eax ;; save for later + + INLINE_GETTHREAD edx, ecx ;; edx <- Thread*, trash ecx + + ;; We must unhijack the thread at this point because the section of stack where the hijack is applied + ;; may go dead. If it does, then the next time we try to unhijack the thread, it will corrupt the stack. + INLINE_THREAD_UNHIJACK edx, ecx, eax ;; Thread in edx, trashes ecx and eax + + mov ecx, [esp + esp_offsetof_ExInfo] ;; ecx <- current ExInfo * + mov eax, [esp + esp_offsetof_RegDisplay] ;; eax <- REGDISPLAY* + mov eax, [eax + OFFSETOF__REGDISPLAY__SP] ;; eax <- resume SP value + + @@: mov ecx, [ecx + OFFSETOF__ExInfo__m_pPrevExInfo] ;; ecx <- next ExInfo + cmp ecx, 0 + je @F ;; we're done if it's null + cmp ecx, eax + jl @B ;; keep looping if it's lower than the new SP + + @@: mov [edx + OFFSETOF__Thread__m_pExInfoStackHead], ecx ;; store the new head on the Thread + + test [RhpTrapThreads], TrapThreadsFlags_AbortInProgress + jz @f + + ;; test if the exception handled by the catch was the ThreadAbortException + cmp byte ptr [esp + esp_offsetof_is_handling_thread_abort], 0 + je @f + + ;; RhpCallFunclet preserved our local EBP value, so let's fetch the correct one for the resume address + mov ecx, [esp + esp_offsetof_RegDisplay] ;; ecx <- REGDISPLAY * + mov ecx, [ecx + OFFSETOF__REGDISPLAY__pRbp] + mov ebp, [ecx] + + ;; It was the ThreadAbortException, so rethrow it + mov ecx, STATUS_REDHAWK_THREAD_ABORT + mov edx, [esp + esp_offsetof_ResumeIP] + mov esp, eax ;; reset the SP to resume SP value + jmp RhpThrowHwEx ;; Throw the ThreadAbortException as a special kind of hardware exception + + @@: + ;; RhpCallFunclet preserved our local EBP value, so let's fetch the correct one for the resume address + mov ecx, [esp + esp_offsetof_RegDisplay] ;; ecx <- REGDISPLAY * + mov ecx, [ecx + OFFSETOF__REGDISPLAY__pRbp] + mov ebp, [ecx] + + ;; reset ESP and jump to the continuation address + mov ecx, [esp + esp_offsetof_ResumeIP] + mov esp, eax + jmp ecx + +FASTCALL_ENDFUNC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void FASTCALL RhpCallFinallyFunclet(void* pHandlerIP, REGDISPLAY* pRegDisplay) +;; +;; INPUT: ECX: handler funclet address +;; EDX: REGDISPLAY* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpCallFinallyFunclet, 0 + + FUNCLET_CALL_PROLOGUE 0 + + push edx ;; save REGDISPLAY* + + ;; Clear the DoNotTriggerGc state before calling out to our managed catch funclet. + INLINE_GETTHREAD eax, ebx ;; eax <- Thread*, ebx is trashed + lock and dword ptr [eax + OFFSETOF__Thread__m_ThreadStateFlags], NOT TSF_DoNotTriggerGc + + ;; + ;; load preserved registers for funclet + ;; + + mov eax, [edx + OFFSETOF__REGDISPLAY__pRbx] + mov ebx, [eax] + + mov eax, [edx + OFFSETOF__REGDISPLAY__pRsi] + mov esi, [eax] + + mov eax, [edx + OFFSETOF__REGDISPLAY__pRdi] + mov edi, [eax] + + mov eax, [edx + OFFSETOF__REGDISPLAY__pRbp] + mov eax, [eax] + mov edx, ecx + + ;; ECX: not used + ;; EDX: funclet IP + ;; EAX: funclet EBP + call RhpCallFunclet + + EXPORT_POINTER_TO_ADDRESS _PointerToRhpCallFinallyFunclet2 + + pop edx ;; restore REGDISPLAY* + + ;; + ;; save preserved registers from funclet + ;; + mov eax, [edx + OFFSETOF__REGDISPLAY__pRbx] + mov [eax], ebx + + mov eax, [edx + OFFSETOF__REGDISPLAY__pRsi] + mov [eax], esi + + mov eax, [edx + OFFSETOF__REGDISPLAY__pRdi] + mov [eax], edi + + INLINE_GETTHREAD eax, ebx ;; eax <- Thread*, ebx is trashed + lock or dword ptr [eax + OFFSETOF__Thread__m_ThreadStateFlags], TSF_DoNotTriggerGc + + FUNCLET_CALL_EPILOGUE + ret + +FASTCALL_ENDFUNC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* FASTCALL RhpCallFilterFunclet(RtuObjectRef exceptionObj, void* pFilterIP, REGDISPLAY* pRegDisplay) +;; +;; INPUT: ECX: exception object +;; EDX: filter funclet address +;; [ESP + 4]: REGDISPLAY* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpCallFilterFunclet, 0 + + FUNCLET_CALL_PROLOGUE 0 + + push edx ;; save filter funclet address + + ;; + ;; load preserved registers for funclet + ;; + mov edx, [ebp + 8] + mov eax, [edx + OFFSETOF__REGDISPLAY__pRbp] + mov eax, [eax] + + ;; ECX still contains exception object + ;; EAX contains the funclet EBP value + mov edx, [esp + 0] ;; reload filter funclet address + + call RhpCallFunclet + + EXPORT_POINTER_TO_ADDRESS _PointerToRhpCallFilterFunclet2 + + ;; EAX contains the result of the filter execution + mov edx, [ebp + 8] + + pop ecx ;; pop scratch slot + + FUNCLET_CALL_EPILOGUE + ret + +FASTCALL_ENDFUNC + + end diff --git a/src/coreclr/nativeaot/Runtime/i386/FloatingPoint.asm b/src/coreclr/nativeaot/Runtime/i386/FloatingPoint.asm new file mode 100644 index 00000000000000..7545ac0f8e0356 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/FloatingPoint.asm @@ -0,0 +1,77 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + .586 + .xmm + .model flat + option casemap:none + .code + + +include AsmMacros.inc + +FASTCALL_FUNC RhpFltRemRev, 8 ; float dividend, float divisor + + fld dword ptr [esp+8] ; divisor + fld dword ptr [esp+4] ; dividend + +fremloop: + fprem + wait + fnstsw ax + wait + sahf + jp fremloop ; Continue while the FPU status bit C2 is set + + fxch st(1) ; swap, so divisor is on top and result is in st(1) + fstp st(0) ; Pop the divisor from the FP stack + + ret 8 + +FASTCALL_ENDFUNC + +FASTCALL_FUNC RhpDblRemRev, 16 ; double dividend, double divisor + + fld qword ptr [esp+0Ch] + fld qword ptr [esp+4] + +fremloopd: + fprem + wait + fnstsw ax + wait + sahf + jp fremloopd ; Continue while the FPU status bit C2 is set + + fxch st(1) ; swap, so divisor is on top and result is in st(1) + fstp st(0) ; Pop the divisor from the FP stack + + ret 10h + +FASTCALL_ENDFUNC + + +FASTCALL_FUNC RhpFltRemRev_SSE2, 0 ; float dividend, float divisor + sub esp, 12 ;; 4 bytes of our stack, 8 bytes args + movd dword ptr [esp], xmm0 + movd dword ptr [esp+4], xmm1 + call @RhpFltRemRev@8 ;; pops 8 bytes of stack + fstp dword ptr [esp] + movd xmm0, dword ptr [esp] + add esp, 4 + ret +FASTCALL_ENDFUNC + +FASTCALL_FUNC RhpDblRemRev_SSE2, 0 ; float dividend, float divisor + sub esp, 24 ;; 8 bytes of our stack, 16 bytes args + movq qword ptr [esp], xmm0 + movq qword ptr [esp+8], xmm1 + call @RhpDblRemRev@16 ;; pops 16 bytes of stack + fstp qword ptr [esp] + movq xmm0, qword ptr [esp] + add esp, 8 + ret +FASTCALL_ENDFUNC + + + end diff --git a/src/coreclr/nativeaot/Runtime/i386/GC.asm b/src/coreclr/nativeaot/Runtime/i386/GC.asm new file mode 100644 index 00000000000000..bf79142e9286ab --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/GC.asm @@ -0,0 +1,15 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +;; +;; Unmanaged helpers used by the managed System.GC class. +;; + + .586 + .model flat + option casemap:none + .code + +include AsmMacros.inc + + end diff --git a/src/coreclr/nativeaot/Runtime/i386/GcProbe.asm b/src/coreclr/nativeaot/Runtime/i386/GcProbe.asm new file mode 100644 index 00000000000000..41fe6a13027ce7 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/GcProbe.asm @@ -0,0 +1,556 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + .586 + .xmm + .model flat + option casemap:none + .code + + +include AsmMacros.inc + +DEFAULT_PROBE_SAVE_FLAGS equ PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_RSP +PROBE_SAVE_FLAGS_EVERYTHING equ DEFAULT_PROBE_SAVE_FLAGS + PTFF_SAVE_ALL_SCRATCH +PROBE_SAVE_FLAGS_RAX_IS_GCREF equ DEFAULT_PROBE_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_RAX_IS_GCREF +;; +;; Macro to clear the hijack state. This is safe to do because the suspension code will not Unhijack this +;; thread if it finds it at an IP that isn't managed code. +;; +;; Register state on entry: +;; EDX: thread pointer +;; +;; Register state on exit: +;; No changes +;; +ClearHijackState macro + mov dword ptr [edx + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], 0 + mov dword ptr [edx + OFFSETOF__Thread__m_pvHijackedReturnAddress], 0 +endm + +;; +;; The prolog for all GC suspension hijackes (normal and stress). Sets up an EBP frame, +;; fixes up the hijacked return address, and clears the hijack state. +;; +;; Register state on entry: +;; All registers correct for return to the original return address. +;; +;; Register state on exit: +;; EAX: not trashed or saved +;; EBP: new EBP frame with correct return address +;; ESP: points to saved scratch registers (ECX & EDX) +;; ECX: trashed +;; EDX: thread pointer +;; +HijackFixupProlog macro + push eax ; save a slot for the repaired return address + push ebp + mov ebp, esp + push ecx ; save scratch registers + push edx ; save scratch registers + + ;; edx <- GetThread(), TRASHES ecx + INLINE_GETTHREAD edx, ecx + + ;; + ;; Fix the stack by pushing the original return address + ;; + mov ecx, [edx + OFFSETOF__Thread__m_pvHijackedReturnAddress] + mov [ebp + 4], ecx + + ClearHijackState +endm + +;; +;; Epilog for the normal and GC stress hijack functions. Restores scratch registers +;; and returns to the original return address. +;; +;; Register state on entry: +;; ESP: points to saved scratch registers +;; EBP: ebp frame +;; ECX, EDX: trashed +;; All other registers correct for return to the original return address. +;; +;; Register state on exit: +;; All registers restored as they were when the hijack was first reached. +;; +HijackFixupEpilog macro + pop edx + pop ecx + pop ebp + ret +endm + +;; +;; Sets up a PInvokeTranstionFrame with room for all registers. +;; +;; Register state on entry: +;; EDX: thread pointer +;; BITMASK_REG_OR_VALUE: register bitmask, PTTR_SAVE_ALL_PRESERVED at a minimum +;; EBP: ebp frame setup with correct return address +;; ESP: points to saved scratch registers +;; +;; Register state on exit: +;; ESP: pointer to a PInvokeTransitionFrame on the stack +;; EBX: thread pointer +;; EAX: trashed +;; ESI, EDI, EBX, EAX all saved in the frame +;; +;; ECX is NOT trashed if BITMASK_REG_OR_VALUE is a literal value and not a register +;; +PushProbeFrame macro BITMASK_REG_OR_VALUE + push eax ; EAX + lea eax, [ebp + 8] ; get caller ESP + push eax ; ESP + push edi ; EDI + push esi ; ESI + push ebx ; EBX + push BITMASK_REG_OR_VALUE ; register bitmask +ifdef _DEBUG + mov eax, BITMASK_REG_OR_VALUE + and eax, DEFAULT_PROBE_SAVE_FLAGS + cmp eax, DEFAULT_PROBE_SAVE_FLAGS ; make sure we have at least the flags to match what the macro pushes + je @F + call RhDebugBreak +@@: +endif ;; _DEBUG + push edx ; Thread * + mov eax, [ebp + 0] ; find previous EBP value + push eax ; m_FramePointer + mov eax, [ebp + 4] ; get return address + push eax ; m_RIP + + mov ebx, edx ; save Thread pointer for later +endm + +;; +;; Pops off the PInvokeTransitionFrame setup in PushProbeFrame above, restoring all registers. +;; +;; Register state on entry: +;; ESP: pointer to a PInvokeTransitionFrame on the stack +;; +;; Register state on exit: +;; ESP: points to saved scratch registers, PInvokeTransitionFrame removed +;; EBX: restored +;; ESI: restored +;; EDI: restored +;; EAX: restored +;; +PopProbeFrame macro + add esp, 4*4h + pop ebx + pop esi + pop edi + pop eax ; discard ESP + pop eax +endm + +;; +;; Set the Thread state and wait for a GC to complete. +;; +;; Register state on entry: +;; ESP: pointer to a PInvokeTransitionFrame on the stack +;; EBX: thread pointer +;; EBP: EBP frame +;; +;; Register state on exit: +;; ESP: pointer to a PInvokeTransitionFrame on the stack +;; EBX: thread pointer +;; EBP: EBP frame +;; All other registers trashed +;; + +EXTERN _RhpWaitForGCNoAbort : PROC + +WaitForGCCompletion macro + test dword ptr [ebx + OFFSETOF__Thread__m_ThreadStateFlags], TSF_SuppressGcStress + TSF_DoNotTriggerGc + jnz @F + + mov ecx, esp + call _RhpWaitForGCNoAbort +@@: + +endm + +RhpThrowHwEx equ @RhpThrowHwEx@0 +extern RhpThrowHwEx : proc + +;; +;; Main worker for our GC probes. Do not call directly!! This assumes that HijackFixupProlog has been done. +;; Instead, go through RhpGcProbeHijack* or RhpGcStressHijack*. This waits for the +;; GC to complete then returns to the original return address. +;; +;; Register state on entry: +;; ECX: register bitmask +;; EDX: thread pointer +;; EBP: EBP frame +;; ESP: scratch registers pushed (ECX & EDX) +;; +;; Register state on exit: +;; All registers restored as they were when the hijack was first reached. +;; +RhpGcProbe proc + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jnz SynchronousRendezVous + + HijackFixupEpilog + +SynchronousRendezVous: + PushProbeFrame ecx ; bitmask in ECX + + WaitForGCCompletion + + mov edx, [esp + OFFSETOF__PInvokeTransitionFrame__m_Flags] + ;; + ;; Restore preserved registers -- they may have been updated by GC + ;; + PopProbeFrame + + test edx, PTFF_THREAD_ABORT + jnz Abort + + HijackFixupEpilog +Abort: + mov ecx, STATUS_REDHAWK_THREAD_ABORT + pop edx + pop eax ;; ecx was pushed here, but we don't care for its value + pop ebp + pop edx ;; return address as exception RIP + jmp RhpThrowHwEx + +RhpGcProbe endp + +ifdef FEATURE_GC_STRESS +;; +;; Set the Thread state and invoke RedhawkGCInterface::StressGC(). +;; +;; Assumes EBX is the Thread pointer. +;; +;; Register state on entry: +;; EBX: thread pointer +;; EBP: EBP frame +;; ESP: pointer to a PInvokeTransitionFrame on the stack +;; +;; Register state on exit: +;; ESP: pointer to a PInvokeTransitionFrame on the stack +;; EBP: EBP frame +;; All other registers trashed +;; +StressGC macro + mov [ebx + OFFSETOF__Thread__m_pHackPInvokeTunnel], esp + call REDHAWKGCINTERFACE__STRESSGC +endm + +;; +;; Worker for our GC stress probes. Do not call directly!! +;; Instead, go through RhpGcStressHijack. This performs the GC Stress +;; work and returns to the original return address. +;; +;; Register state on entry: +;; EDX: thread pointer +;; ECX: register bitmask +;; EBP: EBP frame +;; ESP: scratch registers pushed (ECX and EDX) +;; +;; Register state on exit: +;; All registers restored as they were when the hijack was first reached. +;; +RhpGcStressProbe proc + PushProbeFrame ecx ; bitmask in ECX + + StressGC + + ;; + ;; Restore preserved registers -- they may have been updated by GC + ;; + PopProbeFrame + + HijackFixupEpilog + +RhpGcStressProbe endp + +endif ;; FEATURE_GC_STRESS + +FASTCALL_FUNC RhpGcProbeHijackScalar, 0 + + HijackFixupProlog + mov ecx, DEFAULT_PROBE_SAVE_FLAGS + jmp RhpGcProbe + +FASTCALL_ENDFUNC + +FASTCALL_FUNC RhpGcProbeHijackObject, 0 + + HijackFixupProlog + mov ecx, DEFAULT_PROBE_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_RAX_IS_GCREF + jmp RhpGcProbe + +FASTCALL_ENDFUNC + +FASTCALL_FUNC RhpGcProbeHijackByref, 0 + + HijackFixupProlog + mov ecx, DEFAULT_PROBE_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_RAX_IS_BYREF + jmp RhpGcProbe + +FASTCALL_ENDFUNC + +ifdef FEATURE_GC_STRESS +FASTCALL_FUNC RhpGcStressHijackScalar, 0 + + HijackFixupProlog + mov ecx, DEFAULT_PROBE_SAVE_FLAGS + jmp RhpGcStressProbe + +FASTCALL_ENDFUNC + +FASTCALL_FUNC RhpGcStressHijackObject, 0 + + HijackFixupProlog + mov ecx, DEFAULT_PROBE_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_RAX_IS_GCREF + jmp RhpGcStressProbe + +FASTCALL_ENDFUNC + +FASTCALL_FUNC RhpGcStressHijackByref, 0 + + HijackFixupProlog + mov ecx, DEFAULT_PROBE_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_RAX_IS_BYREF + jmp RhpGcStressProbe + +FASTCALL_ENDFUNC + +FASTCALL_FUNC RhpHijackForGcStress, 0 + push ebp + mov ebp, esp + + ;; + ;; Setup a PAL_LIMITED_CONTEXT that looks like what you'd get if you had suspended this thread at the + ;; IP after the call to this helper. + ;; + + push edx + push ecx + push ebx + push eax + push esi + push edi + + mov eax, [ebp] + push eax ;; (caller) Ebp + lea eax, [ebp + 8] + push eax ;; Esp + mov eax, [ebp + 4] + push eax ;; Eip + + push esp ;; address of PAL_LIMITED_CONTEXT + call THREAD__HIJACKFORGCSTRESS + + ;; Note: we only restore the scratch registers here. No GC has occured, so restoring + ;; the callee saved ones is unnecessary. + add esp, 14h + pop eax + pop ebx + pop ecx + pop edx + pop ebp + ret +FASTCALL_ENDFUNC +endif ;; FEATURE_GC_STRESS + +;; +;; The following functions are _jumped_ to when we need to transfer control from one method to another for EH +;; dispatch. These are needed to properly coordinate with the GC hijacking logic. We are essentially replacing +;; the return from the throwing method with a jump to the handler in the caller, but we need to be aware of +;; any return address hijack that may be in place for GC suspension. These routines use a quick test of the +;; return address against a specific GC hijack routine, and then fixup the stack pointer to what it would be +;; after a real return from the throwing method. Then, if we are not hijacked we can simply jump to the +;; handler in the caller. +;; +;; If we are hijacked, then we jump to a routine that will unhijack appropriatley and wait for the GC to +;; complete. There are also variants for GC stress. +;; +;; Note that at this point we are eiher hijacked or we are not, and this will not change until we return to +;; managed code. It is an invariant of the system that a thread will only attempt to hijack or unhijack +;; another thread while the target thread is suspended in managed code, and this is _not_ managed code. +;; +;; Register state on entry: +;; EAX: handler address we want to jump to. +;; ECX: reference to the exception object. +;; EDX: what ESP should be after the return address and arg space are removed. +;; EBX, ESI, EDI, and EBP are all already correct for return to the caller. +;; The stack still contains the return address and the arguments to the call. +;; +;; Register state on exit: +;; ESP: what it would be after a complete return to the caller. +;; +RTU_EH_JUMP_HELPER macro funcName, hijackFuncName, isStress, stressFuncName +FASTCALL_FUNC funcName, 0 + cmp [esp], hijackFuncName + je RhpGCProbeForEHJump + +IF isStress EQ 1 + cmp [esp], stressFuncName + je RhpGCStressProbeForEHJump +ENDIF + + ;; We are not hijacked, so we can return to the handler. + ;; We return to keep the call/return prediction balanced. + mov esp, edx ; The stack is now as if we have returned from the call. + push eax ; Push the handler as the return address. + ret + +FASTCALL_ENDFUNC +endm + + +;; We need an instance of the helper for each possible hijack function. The binder has enough +;; information to determine which one we need to use for any function. +RTU_EH_JUMP_HELPER RhpEHJumpScalar, @RhpGcProbeHijackScalar@0, 0, 0 +RTU_EH_JUMP_HELPER RhpEHJumpObject, @RhpGcProbeHijackObject@0, 0, 0 +RTU_EH_JUMP_HELPER RhpEHJumpByref, @RhpGcProbeHijackByref@0, 0, 0 +ifdef FEATURE_GC_STRESS +RTU_EH_JUMP_HELPER RhpEHJumpScalarGCStress, @RhpGcProbeHijackScalar@0, 1, @RhpGcStressHijackScalar@0 +RTU_EH_JUMP_HELPER RhpEHJumpObjectGCStress, @RhpGcProbeHijackObject@0, 1, @RhpGcStressHijackObject@0 +RTU_EH_JUMP_HELPER RhpEHJumpByrefGCStress, @RhpGcProbeHijackByref@0, 1, @RhpGcStressHijackByref@0 +endif + +;; +;; Macro to setup our EBP frame and adjust the location of the EH object reference for EH jump probe funcs. +;; +;; Register state on entry: +;; EAX: handler address we want to jump to. +;; ECX: reference to the exception object. +;; EDX: scratch +;; EBX, ESI, EDI, and EBP are all already correct for return to the caller. +;; The stack is as if we have returned from the call +;; +;; Register state on exit: +;; ESP: ebp frame +;; EBP: ebp frame setup with space reserved for the repaired return address +;; EAX: reference to the exception object +;; ECX: scratch +;; +EHJumpProbeProlog macro + push eax ; save a slot for the repaired return address + push ebp ; setup an ebp frame to keep the stack nicely crawlable + mov ebp, esp + push eax ; save the handler address so we can jump to it later + mov eax, ecx ; move the ex object reference into eax so we can report it +endm + +;; +;; Macro to re-adjust the location of the EH object reference, cleanup the EBP frame, and make the +;; final jump to the handler for EH jump probe funcs. +;; +;; Register state on entry: +;; EAX: reference to the exception object +;; ESP: ebp frame +;; EBP: ebp frame setup with the correct return (handler) address +;; ECX: scratch +;; EDX: scratch +;; +;; Register state on exit: +;; ESP: correct for return to the caller +;; EBP: previous ebp frame +;; ECX: reference to the exception object +;; EDX: trashed +;; +EHJumpProbeEpilog macro + mov ecx, eax ; Put the EX obj ref back into ecx for the handler. + pop eax ; Recover the handler address. + pop ebp ; Pop the ebp frame we setup. + pop edx ; Pop the original return address, which we do not need. + push eax ; Push the handler as the return address. + ret +endm + +;; +;; We are hijacked for a normal GC (not GC stress), so we need to unhijcak and wait for the GC to complete. +;; +;; Register state on entry: +;; EAX: handler address we want to jump to. +;; ECX: reference to the exception object. +;; EDX: what ESP should be after the return address and arg space are removed. +;; EBX, ESI, EDI, and EBP are all already correct for return to the caller. +;; The stack is as if we have returned from the call +;; +;; Register state on exit: +;; ESP: correct for return to the caller +;; EBP: previous ebp frame +;; ECX: reference to the exception object +;; +RhpGCProbeForEHJump proc + mov esp, edx ; The stack is now as if we have returned from the call. + EHJumpProbeProlog + + ;; edx <- GetThread(), TRASHES ecx + INLINE_GETTHREAD edx, ecx + + ;; Fix the stack by pushing the original return address + mov ecx, [edx + OFFSETOF__Thread__m_pvHijackedReturnAddress] + mov [ebp + 4], ecx + + ClearHijackState + +ifdef _DEBUG + ;; + ;; If we get here, then we have been hijacked for a real GC, and our SyncState must + ;; reflect that we've been requested to synchronize. + + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jnz @F + + call RhDebugBreak +@@: +endif ;; _DEBUG + + + PushProbeFrame PROBE_SAVE_FLAGS_RAX_IS_GCREF + WaitForGCCompletion + PopProbeFrame + + EHJumpProbeEpilog + +RhpGCProbeForEHJump endp + +ifdef FEATURE_GC_STRESS +;; +;; We are hijacked for GC Stress (not a normal GC) so we need to invoke the GC stress helper. +;; +;; Register state on entry: +;; EAX: handler address we want to jump to. +;; ECX: reference to the exception object. +;; EDX: what ESP should be after the return address and arg space are removed. +;; EBX, ESI, EDI, and EBP are all already correct for return to the caller. +;; The stack is as if we have returned from the call +;; +;; Register state on exit: +;; ESP: correct for return to the caller +;; EBP: previous ebp frame +;; ECX: reference to the exception object +;; +RhpGCStressProbeForEHJump proc + mov esp, edx ; The stack is now as if we have returned from the call. + EHJumpProbeProlog + + ;; edx <- GetThread(), TRASHES ecx + INLINE_GETTHREAD edx, ecx + + ;; Fix the stack by pushing the original return address + mov ecx, [edx + OFFSETOF__Thread__m_pvHijackedReturnAddress] + mov [ebp + 4], ecx + + ClearHijackState + + PushProbeFrame PROBE_SAVE_FLAGS_RAX_IS_GCREF + StressGC + PopProbeFrame + + EHJumpProbeEpilog + +RhpGCStressProbeForEHJump endp + +endif ;; FEATURE_GC_STRESS + + end diff --git a/src/coreclr/nativeaot/Runtime/i386/GetThread.asm b/src/coreclr/nativeaot/Runtime/i386/GetThread.asm new file mode 100644 index 00000000000000..b330406b9a098a --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/GetThread.asm @@ -0,0 +1,31 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + .586 + .model flat + option casemap:none + .code + + +include AsmMacros.inc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpGetThread +;; +;; +;; INPUT: none +;; +;; OUTPUT: EAX: Thread pointer +;; +;; MUST PRESERVE ARGUMENT REGISTERS +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpGetThread, 0 + push ecx + INLINE_GETTHREAD eax, ecx ; eax dest, ecx trash + pop ecx + ret +FASTCALL_ENDFUNC + + end diff --git a/src/coreclr/nativeaot/Runtime/i386/Interlocked.S b/src/coreclr/nativeaot/Runtime/i386/Interlocked.S new file mode 100644 index 00000000000000..876f2dfbcb80d6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/Interlocked.S @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// TODO: Implement diff --git a/src/coreclr/nativeaot/Runtime/i386/Interlocked.asm b/src/coreclr/nativeaot/Runtime/i386/Interlocked.asm new file mode 100644 index 00000000000000..f9599b1b8666e3 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/Interlocked.asm @@ -0,0 +1,3 @@ +;; TODO: Implement + +end diff --git a/src/coreclr/nativeaot/Runtime/i386/InteropThunksHelpers.S b/src/coreclr/nativeaot/Runtime/i386/InteropThunksHelpers.S new file mode 100644 index 00000000000000..876f2dfbcb80d6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/InteropThunksHelpers.S @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// TODO: Implement diff --git a/src/coreclr/nativeaot/Runtime/i386/InteropThunksHelpers.asm b/src/coreclr/nativeaot/Runtime/i386/InteropThunksHelpers.asm new file mode 100644 index 00000000000000..f47ff5eb3c1664 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/InteropThunksHelpers.asm @@ -0,0 +1,101 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + +.586 +.model flat +option casemap:none +.code + +;; ----------------------------------------------------------------------------------------------------------- +;; standard macros +;; ----------------------------------------------------------------------------------------------------------- +LEAF_ENTRY macro Name, Section + Section segment para 'CODE' + public Name + Name proc +endm + +LEAF_END macro Name, Section + Name endp + Section ends +endm + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +__tls_array equ 2Ch ;; offsetof(TEB, ThreadLocalStoragePointer) + +POINTER_SIZE equ 04h + +;; TLS variables +_TLS SEGMENT ALIAS(".tls$") + ThunkParamSlot DD 00000000H +_TLS ENDS + +ASSUME fs : NOTHING +EXTRN __tls_index:DWORD + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interop Thunks Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; +;; RhCommonStub +;; +LEAF_ENTRY RhCommonStub, _TEXT + ;; There are arbitrary callers passing arguments with arbitrary signatures. + ;; Custom calling convention: + ;; eax: pointer to the current thunk's data block (data contains 2 pointer values: context + target pointers) + + ;; Save context data into the ThunkParamSlot thread-local variable + ;; A pointer to the delegate and function pointer for open static delegate should have been saved in the thunk's context cell during thunk allocation + + ;; make some scratch regs + push ecx + push edx + + mov ecx, [__tls_index] + mov edx, fs:[__tls_array] + mov ecx, [edx + ecx * POINTER_SIZE] + + ;; eax = address of context cell in thunk's data + ;; ecx = base address of TLS data + ;; edx = trashed + + ;; store thunk address in thread static + mov edx, [eax] + mov eax, [eax + POINTER_SIZE] ;; eax <- target slot data + mov [ecx + OFFSET ThunkParamSlot], edx ;; ThunkParamSlot <- context slot data + + ;; restore the regs we used + pop edx + pop ecx + + ;; jump to the target + jmp eax +LEAF_END RhCommonStub, _TEXT + + +;; +;; IntPtr RhGetCommonStubAddress() +;; +LEAF_ENTRY RhGetCommonStubAddress, _TEXT + lea eax, [RhCommonStub] + ret +LEAF_END RhGetCommonStubAddress, _TEXT + + +;; +;; IntPtr RhGetCurrentThunkContext() +;; +LEAF_ENTRY RhGetCurrentThunkContext, _TEXT + mov ecx, [__tls_index] + mov edx, fs:[__tls_array] + mov ecx, [edx + ecx * POINTER_SIZE] + mov eax, [ecx + OFFSET ThunkParamSlot] ;; eax <- ThunkParamSlot + ret +LEAF_END RhGetCurrentThunkContext, _TEXT + + +end diff --git a/src/coreclr/nativeaot/Runtime/i386/MemClrForGC.asm b/src/coreclr/nativeaot/Runtime/i386/MemClrForGC.asm new file mode 100644 index 00000000000000..d093e0adce0bb5 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/MemClrForGC.asm @@ -0,0 +1,148 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + .586 + .xmm + .model flat + option casemap:none + + +EXTERN _IsProcessorFeaturePresent@4 : PROC + +PF_XMMI64_INSTRUCTIONS_AVAILABLE equ 10 + + .data +canUseSSE2 db 0 + + .code + +_memclr_for_gc@8 proc public + +; x86 version + +; we get the following parameters +; ecx = destination address +; edx = size to clear + + push ebx + push edi + + xor eax, eax + + ; load destination + mov edi,[esp+8+4] + + ; load size + mov ebx,[esp+8+8] + + ; check alignment of destination + test edi,3 + jnz alignDest +alignDone: + ; now destination is dword aligned + + ; compute number of bytes to clear non-temporally + ; we wish to clear the first 8k or so with rep stos, + ; anything above that non-temporally + + xor edx,edx + cmp ebx,8*1024 + jbe noNonTempClear + + ; can we use SSE2 instructions? + cmp canUseSSE2,0 + js noNonTempClear + jz computeCanUseSSE2 + +computeNonTempClear: + + ; compute the number of bytes above 8k + ; and round down to a multiple of 64 + mov edx,ebx + sub edx,8*1024 + and edx,not 63 + + ; compute remaining size to clear temporally + sub ebx,edx + +noNonTempClear: + ; do the temporal clear + mov ecx,ebx + shr ecx,2 + rep stosd + + ; do the non-temporal clear + test edx,edx + jne nonTempClearLoop + +nonTempClearDone: + + ; clear any remaining bytes + mov ecx,ebx + and ecx,3 + rep stosb + + pop edi + pop ebx + ret 8 + + ; this is the infrequent case, hence out of line +nonTempClearLoop: + movnti [edi+ 0],eax + movnti [edi+ 4],eax + movnti [edi+ 8],eax + movnti [edi+12],eax + + movnti [edi+16],eax + movnti [edi+20],eax + movnti [edi+24],eax + movnti [edi+28],eax + + movnti [edi+32],eax + movnti [edi+36],eax + movnti [edi+40],eax + movnti [edi+44],eax + + movnti [edi+48],eax + movnti [edi+52],eax + movnti [edi+56],eax + movnti [edi+60],eax + + add edi,64 + sub edx,64 + ja nonTempClearLoop + jmp nonTempClearDone + +alignDest: + test ebx,ebx + je alignDone +alignLoop: + mov [edi],al + add edi,1 + sub ebx,1 + jz alignDone + test edi,3 + jnz alignLoop + jmp alignDone + +computeCanUseSSE2: + ; we are not using the sse2 register set, + ; just sse2 instructions (movnti), + ; thus we just ask the OS about the usability of the instructions + ; OS bugs about saving/restoring registers like in early versions + ; of Vista etc. in the WoW shouldn't matter + + push PF_XMMI64_INSTRUCTIONS_AVAILABLE + call _IsProcessorFeaturePresent@4 + mov ecx,eax + xor eax,eax ; reset eax to 0 + test ecx,ecx + mov canUseSSE2,1 + jne computeNonTempClear + mov canUseSSE2,-1 + xor edx,edx + jmp noNonTempClear + +_memclr_for_gc@8 endp + + end diff --git a/src/coreclr/nativeaot/Runtime/i386/MiscStubs.S b/src/coreclr/nativeaot/Runtime/i386/MiscStubs.S new file mode 100644 index 00000000000000..3ea675f2c9dc55 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/MiscStubs.S @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include + +// *********************************************************************/ +// RhpStackProbe +// +// Purpose: +// the helper will access ("probe") a word on each page of the stack +// starting with the page right beneath esp down to the one pointed to by eax. +// The procedure is needed to make sure that the "guard" page is pushed down below the allocated stack frame. +// The call to the helper will be emitted by JIT in the function prolog when large (larger than 0x3000 bytes) stack frame is required. +// +// NOTE: this helper will modify a value of esp and must establish the frame pointer. +// NOTE: On Linux we must advance the stack pointer as we probe - it is not allowed to access 65535 bytes below esp. +// +#define PAGE_SIZE 0x1000 +NESTED_ENTRY RhpStackProbe, _TEXT, NoHandler + // On entry: + // eax - the lowest address of the stack frame being allocated (i.e. [InitialSp - FrameSize]) + // + // NOTE: this helper will probe at least one page below the one pointed by esp. + PROLOG_BEG + PROLOG_END + + and esp, -PAGE_SIZE // esp points to the **lowest address** on the last probed page + // This is done to make the loop end condition simpler. + +LOCAL_LABEL(ProbeLoop): + sub esp, PAGE_SIZE // esp points to the lowest address of the **next page** to probe + test [esp], eax // esp points to the lowest address on the **last probed** page + cmp esp, eax + jg LOCAL_LABEL(ProbeLoop) // if esp > eax, then we need to probe at least one more page. + + EPILOG_BEG + mov esp, ebp + EPILOG_END + ret + +NESTED_END RhpStackProbe, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/i386/MiscStubs.asm b/src/coreclr/nativeaot/Runtime/i386/MiscStubs.asm new file mode 100644 index 00000000000000..3bd8a528cbaf15 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/MiscStubs.asm @@ -0,0 +1,216 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + .586 + .model flat + option casemap:none + .code + +include AsmMacros.inc + +EXTERN _memcpy : PROC +EXTERN _memcpyGCRefs : PROC +EXTERN _memcpyGCRefsWithWriteBarrier : PROC +EXTERN _memcpyAnyWithWriteBarrier : PROC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* __cdecl RhpCopyMultibyteNoGCRefs(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; +_RhpCopyMultibyteNoGCRefs PROC PUBLIC + + ; #locals, num_params, prolog bytes, #regs saved, use ebp, frame type (0 == FRAME_FPO) + .FPO( 0, 3, 0, 0, 0, 0) + + ; [esp + 0] return address + ; [esp + 4] dest + ; [esp + 8] src + ; [esp + c] count + + cmp dword ptr [esp + 0Ch], 0 ; check for a zero-length copy + jz NothingToCopy + + mov ecx, [esp + 4] ; ecx <- dest + mov edx, [esp + 8] ; edx <- src + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. +ALTERNATE_ENTRY RhpCopyMultibyteNoGCRefsDestAVLocation + cmp byte ptr [ecx], 0 +ALTERNATE_ENTRY RhpCopyMultibyteNoGCRefsSrcAVLocation + cmp byte ptr [edx], 0 + + ; tail-call to plain-old-memcpy + jmp _memcpy + +NothingToCopy: + mov eax, [esp + 4] ; return dest + ret + +_RhpCopyMultibyteNoGCRefs ENDP + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* __cdecl RhpCopyMultibyte(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; +_RhpCopyMultibyte PROC PUBLIC + + ; #locals, num_params, prolog bytes, #regs saved, use ebp, frame type (0 == FRAME_FPO) + .FPO( 0, 3, 0, 0, 0, 0) + + ; [esp + 0] return address + ; [esp + 4] dest + ; [esp + 8] src + ; [esp + c] count + + cmp dword ptr [esp + 0Ch], 0 ; check for a zero-length copy + jz NothingToCopy + + mov ecx, [esp + 4] ; ecx <- dest + mov edx, [esp + 8] ; edx <- src + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. +ALTERNATE_ENTRY RhpCopyMultibyteDestAVLocation + cmp byte ptr [ecx], 0 +ALTERNATE_ENTRY RhpCopyMultibyteSrcAVLocation + cmp byte ptr [edx], 0 + + ; tail-call to the GC-safe memcpy implementation + ; NOTE: this is also a __cdecl function + jmp _memcpyGCRefs + +NothingToCopy: + mov eax, [esp + 4] ; return dest + ret + +_RhpCopyMultibyte ENDP + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* __cdecl RhpCopyMultibyteWithWriteBarrier(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; Runs a card table update via RhpBulkWriteBarrier after the copy +;; +_RhpCopyMultibyteWithWriteBarrier PROC PUBLIC + + ; #locals, num_params, prolog bytes, #regs saved, use ebp, frame type (0 == FRAME_FPO) + .FPO( 0, 3, 0, 0, 0, 0) + + ; [esp + 0] return address + ; [esp + 4] dest + ; [esp + 8] src + ; [esp + c] count + + cmp dword ptr [esp + 0Ch], 0 ; check for a zero-length copy + jz NothingToCopy + + mov ecx, [esp + 4] ; ecx <- dest + mov edx, [esp + 8] ; edx <- src + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. +ALTERNATE_ENTRY RhpCopyMultibyteWithWriteBarrierDestAVLocation + cmp byte ptr [ecx], 0 +ALTERNATE_ENTRY RhpCopyMultibyteWithWriteBarrierSrcAVLocation + cmp byte ptr [edx], 0 + + ; tail-call to the GC-safe memcpy implementation + ; NOTE: this is also a __cdecl function + jmp _memcpyGCRefsWithWriteBarrier + +NothingToCopy: + mov eax, [esp + 4] ; return dest + ret + +_RhpCopyMultibyteWithWriteBarrier ENDP + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* __cdecl RhpCopyAnyWithWriteBarrier(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; Runs a card table update via RhpBulkWriteBarrier after the copy if it contained GC pointers +;; +_RhpCopyAnyWithWriteBarrier PROC PUBLIC + + ; #locals, num_params, prolog bytes, #regs saved, use ebp, frame type (0 == FRAME_FPO) + .FPO( 0, 3, 0, 0, 0, 0) + + ; [esp + 0] return address + ; [esp + 4] dest + ; [esp + 8] src + ; [esp + c] count + + cmp dword ptr [esp + 0Ch], 0 ; check for a zero-length copy + jz NothingToCopy + + mov ecx, [esp + 4] ; ecx <- dest + mov edx, [esp + 8] ; edx <- src + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. +ALTERNATE_ENTRY RhpCopyAnyWithWriteBarrierDestAVLocation + cmp byte ptr [ecx], 0 +ALTERNATE_ENTRY RhpCopyAnyWithWriteBarrierSrcAVLocation + cmp byte ptr [edx], 0 + + ; tail-call to the GC-safe memcpy implementation + ; NOTE: this is also a __cdecl function + jmp _memcpyAnyWithWriteBarrier + +NothingToCopy: + mov eax, [esp + 4] ; return dest + ret + +_RhpCopyAnyWithWriteBarrier ENDP + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; The following helper will access ("probe") a word on each page of the stack +; starting with the page right beneath esp down to the one pointed to by eax. +; The procedure is needed to make sure that the "guard" page is pushed down below the allocated stack frame. +; The call to the helper will be emitted by JIT in the function prolog when large (larger than 0x3000 bytes) stack frame is required. +; +; NOTE: this helper will modify a value of esp and must establish the frame pointer. +PAGE_SIZE equ 1000h + +_RhpStackProbe PROC public + ; On entry: + ; eax - the lowest address of the stack frame being allocated (i.e. [InitialSp - FrameSize]) + ; + ; NOTE: this helper will probe at least one page below the one pointed by esp. + push ebp + mov ebp, esp + + and esp, -PAGE_SIZE ; esp points to the **lowest address** on the last probed page + ; This is done to make the loop end condition simpler. +ProbeLoop: + sub esp, PAGE_SIZE ; esp points to the lowest address of the **next page** to probe + test [esp], eax ; esp points to the lowest address on the **last probed** page + cmp esp, eax + jg ProbeLoop ; if esp > eax, then we need to probe at least one more page. + + mov esp, ebp + pop ebp + ret + +_RhpStackProbe ENDP + +end diff --git a/src/coreclr/nativeaot/Runtime/i386/PInvoke.S b/src/coreclr/nativeaot/Runtime/i386/PInvoke.S new file mode 100644 index 00000000000000..876f2dfbcb80d6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/PInvoke.S @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// TODO: Implement diff --git a/src/coreclr/nativeaot/Runtime/i386/PInvoke.asm b/src/coreclr/nativeaot/Runtime/i386/PInvoke.asm new file mode 100644 index 00000000000000..526a04ba40a001 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/PInvoke.asm @@ -0,0 +1,117 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + .586 + .model flat + option casemap:none + .code + + +include AsmMacros.inc + +extern RhpReversePInvokeBadTransition : proc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForSuspend -- rare path for RhpPInvoke and RhpReversePInvokeReturn +;; +;; +;; INPUT: none +;; +;; TRASHES: none +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +_RhpWaitForSuspend proc public + push ebp + mov ebp, esp + push eax + push ecx + push edx + + call RhpWaitForSuspend2 + + pop edx + pop ecx + pop eax + pop ebp + ret +_RhpWaitForSuspend endp + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGCNoAbort +;; +;; +;; INPUT: ECX: transition frame +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +_RhpWaitForGCNoAbort proc public + push ebp + mov ebp, esp + push eax + push edx + push ebx + push esi + + mov esi, [ecx + OFFSETOF__PInvokeTransitionFrame__m_pThread] + + test dword ptr [esi + OFFSETOF__Thread__m_ThreadStateFlags], TSF_DoNotTriggerGc + jnz Done + + ; passing transition frame pointer in ecx + call RhpWaitForGC2 + +Done: + pop esi + pop ebx + pop edx + pop eax + pop ebp + ret +_RhpWaitForGCNoAbort endp + +RhpThrowHwEx equ @RhpThrowHwEx@0 +EXTERN RhpThrowHwEx : PROC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGC +;; +;; +;; INPUT: ECX: transition frame +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +_RhpWaitForGC proc public + push ebp + mov ebp, esp + push ebx + + mov ebx, ecx + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jz NoWait + + call _RhpWaitForGCNoAbort +NoWait: + test [RhpTrapThreads], TrapThreadsFlags_AbortInProgress + jz Done + test dword ptr [ebx + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_THREAD_ABORT + jz Done + + mov ecx, STATUS_REDHAWK_THREAD_ABORT + pop ebx + pop ebp + pop edx ; return address as exception RIP + jmp RhpThrowHwEx ; Throw the ThreadAbortException as a special kind of hardware exception +Done: + pop ebx + pop ebp + ret +_RhpWaitForGC endp + + + end diff --git a/src/coreclr/nativeaot/Runtime/i386/StubDispatch.S b/src/coreclr/nativeaot/Runtime/i386/StubDispatch.S new file mode 100644 index 00000000000000..876f2dfbcb80d6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/StubDispatch.S @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// TODO: Implement diff --git a/src/coreclr/nativeaot/Runtime/i386/StubDispatch.asm b/src/coreclr/nativeaot/Runtime/i386/StubDispatch.asm new file mode 100644 index 00000000000000..b1101fac53778e --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/StubDispatch.asm @@ -0,0 +1,133 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + .586 + .model flat + option casemap:none + .code + + +include AsmMacros.inc + + +ifdef FEATURE_CACHED_INTERFACE_DISPATCH + +EXTERN RhpCidResolve : PROC +EXTERN _RhpUniversalTransition_DebugStepTailCall@0 : PROC + + +;; Macro that generates code to check a single cache entry. +CHECK_CACHE_ENTRY macro entry +NextLabel textequ @CatStr( Attempt, %entry+1 ) + cmp ebx, [eax + (OFFSETOF__InterfaceDispatchCache__m_rgEntries + (entry * 8))] + jne @F + pop ebx + jmp dword ptr [eax + (OFFSETOF__InterfaceDispatchCache__m_rgEntries + (entry * 8) + 4)] +@@: +endm + + +;; Macro that generates a stub consuming a cache with the given number of entries. +DEFINE_INTERFACE_DISPATCH_STUB macro entries + +StubName textequ @CatStr( _RhpInterfaceDispatch, entries ) + + StubName proc public + + ;; Check the instance here to catch null references. We're going to touch it again below (to cache + ;; the MethodTable pointer), but that's after we've pushed ebx below, and taking an A/V there will + ;; mess up the stack trace for debugging. We also don't have a spare scratch register (eax holds + ;; the cache pointer and the push of ebx below is precisely so we can access a second register + ;; to hold the MethodTable pointer). + test ecx, ecx + je RhpInterfaceDispatchNullReference + + ;; eax currently contains the indirection cell address. We need to update it to point to the cache + ;; block instead. + mov eax, [eax + OFFSETOF__InterfaceDispatchCell__m_pCache] + + ;; Cache pointer is already loaded in the only scratch register we have so far, eax. We need + ;; another scratch register to hold the instance type so save the value of ebx and use that. + push ebx + + ;; Load the MethodTable from the object instance in ebx. + mov ebx, [ecx] + +CurrentEntry = 0 + while CurrentEntry lt entries + CHECK_CACHE_ENTRY %CurrentEntry +CurrentEntry = CurrentEntry + 1 + endm + + ;; eax currently contains the cache block. We need to point it back to the + ;; indirection cell using the back pointer in the cache block + mov eax, [eax + OFFSETOF__InterfaceDispatchCache__m_pCell] + pop ebx + jmp RhpInterfaceDispatchSlow + + StubName endp + + endm ;; DEFINE_INTERFACE_DISPATCH_STUB + + +;; Define all the stub routines we currently need. +DEFINE_INTERFACE_DISPATCH_STUB 1 +DEFINE_INTERFACE_DISPATCH_STUB 2 +DEFINE_INTERFACE_DISPATCH_STUB 4 +DEFINE_INTERFACE_DISPATCH_STUB 8 +DEFINE_INTERFACE_DISPATCH_STUB 16 +DEFINE_INTERFACE_DISPATCH_STUB 32 +DEFINE_INTERFACE_DISPATCH_STUB 64 + +;; Shared out of line helper used on cache misses. +RhpInterfaceDispatchSlow proc +;; eax points at InterfaceDispatchCell + + ;; Setup call to Universal Transition thunk + push ebp + mov ebp, esp + push eax ; First argument (Interface Dispatch Cell) + lea eax, [RhpCidResolve] + push eax ; Second argument (RhpCidResolve) + + ;; Jump to Universal Transition + jmp _RhpUniversalTransition_DebugStepTailCall@0 +RhpInterfaceDispatchSlow endp + +;; Out of line helper used when we try to interface dispatch on a null pointer. Sets up the stack so the +;; debugger gives a reasonable stack trace. +RhpInterfaceDispatchNullReference proc public + push ebp + mov ebp, esp + mov ebx, [ecx] ;; This should A/V + int 3 +RhpInterfaceDispatchNullReference endp + +;; Stub dispatch routine for dispatch to a vtable slot +_RhpVTableOffsetDispatch proc public + ;; eax currently contains the indirection cell address. We need to update it to point to the vtable offset (which is in the m_pCache field) + mov eax, [eax + OFFSETOF__InterfaceDispatchCell__m_pCache] + + ;; add the vtable offset to the MethodTable pointer + add eax, [ecx] + + ;; Load the target address of the vtable into eax + mov eax, [eax] + + ;; tail-jump to the target + jmp eax +_RhpVTableOffsetDispatch endp + + +;; Initial dispatch on an interface when we don't have a cache yet. +_RhpInitialInterfaceDispatch proc public + ALTERNATE_ENTRY RhpInitialDynamicInterfaceDispatch + + jmp RhpInterfaceDispatchSlow + +_RhpInitialInterfaceDispatch endp + + +endif ;; FEATURE_CACHED_INTERFACE_DISPATCH + +end diff --git a/src/coreclr/nativeaot/Runtime/i386/ThunkPoolThunks.asm b/src/coreclr/nativeaot/Runtime/i386/ThunkPoolThunks.asm new file mode 100644 index 00000000000000..a1fb558290ad53 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/ThunkPoolThunks.asm @@ -0,0 +1,297 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +.586 +.model flat +option casemap:none +.code + +include AsmMacros.inc + +;; ----------------------------------------------------------------------------------------------------------- +;; standard macros +;; ----------------------------------------------------------------------------------------------------------- +LEAF_ENTRY macro Name, Section + Section segment para 'CODE' + public Name + Name proc +endm + +NAMED_LEAF_ENTRY macro Name, Section, SectionAlias + Section segment para alias(SectionAlias) 'CODE' + public Name + Name proc +endm + +LEAF_END macro Name, Section + Name endp + Section ends +endm + +NAMED_READONLY_DATA_SECTION macro Section, SectionAlias + Section segment para alias(SectionAlias) read 'DATA' + DD 0 + Section ends +endm + +NAMED_READWRITE_DATA_SECTION macro Section, SectionAlias + Section segment para alias(SectionAlias) read write 'DATA' + DD 0 + Section ends +endm + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; STUBS & DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +THUNK_CODESIZE equ 20h ;; 5-byte call, 1 byte pop, 6-byte lea, 6-byte jmp, 14 bytes of padding +THUNK_DATASIZE equ 08h ;; 2 dwords + +THUNK_POOL_NUM_THUNKS_PER_PAGE equ 078h ;; 120 thunks per page + +PAGE_SIZE equ 01000h ;; 4K +POINTER_SIZE equ 04h + + +GET_CURRENT_IP macro + ALIGN 10h ;; make sure we align to 16-byte boundary for CFG table + call @F + @@: pop eax +endm + +LOAD_DATA_ADDRESS macro groupIndex, index + ;; start : eax points to current instruction of the current thunk + ;; set eax to begining of data page : eax <- [eax - (size of the call instruction + (THUNK_CODESIZE * current thunk's index)) + PAGE_SIZE] + ;; fix offset of the data : eax <- eax + (THUNK_DATASIZE * current thunk's index) + lea eax,[eax - (5 + groupIndex * THUNK_CODESIZE * 10 + THUNK_CODESIZE * index) + PAGE_SIZE + (groupIndex * THUNK_DATASIZE * 10 + THUNK_DATASIZE * index)] +endm + +JUMP_TO_COMMON macro groupIndex, index + ;; start : eax points to current thunk's data block + ;; re-point eax to begining of data page : eax <- [eax - (THUNK_DATASIZE * current thunk's index)] + ;; jump to the location pointed at by the last dword in the data page : jump [eax + PAGE_SIZE - POINTER_SIZE] + jmp dword ptr[eax - (groupIndex * THUNK_DATASIZE * 10 + THUNK_DATASIZE * index) + PAGE_SIZE - POINTER_SIZE] +endm + +TenThunks macro groupIndex + ;; Each thunk will load the address of its corresponding data (from the page that immediately follows) + ;; and call a common stub. The address of the common stub is setup by the caller (last dword + ;; in the thunks data section) depending on the 'kind' of thunks needed (interop, fat function pointers, etc...) + + ;; Each data block used by a thunk consists of two dword values: + ;; - Context: some value given to the thunk as context (passed in eax). Example for fat-fptrs: context = generic dictionary + ;; - Target : target code that the thunk eventually jumps to. + + GET_CURRENT_IP + LOAD_DATA_ADDRESS groupIndex,0 + JUMP_TO_COMMON groupIndex,0 + + GET_CURRENT_IP + LOAD_DATA_ADDRESS groupIndex,1 + JUMP_TO_COMMON groupIndex,1 + + GET_CURRENT_IP + LOAD_DATA_ADDRESS groupIndex,2 + JUMP_TO_COMMON groupIndex,2 + + GET_CURRENT_IP + LOAD_DATA_ADDRESS groupIndex,3 + JUMP_TO_COMMON groupIndex,3 + + GET_CURRENT_IP + LOAD_DATA_ADDRESS groupIndex,4 + JUMP_TO_COMMON groupIndex,4 + + GET_CURRENT_IP + LOAD_DATA_ADDRESS groupIndex,5 + JUMP_TO_COMMON groupIndex,5 + + GET_CURRENT_IP + LOAD_DATA_ADDRESS groupIndex,6 + JUMP_TO_COMMON groupIndex,6 + + GET_CURRENT_IP + LOAD_DATA_ADDRESS groupIndex,7 + JUMP_TO_COMMON groupIndex,7 + + GET_CURRENT_IP + LOAD_DATA_ADDRESS groupIndex,8 + JUMP_TO_COMMON groupIndex,8 + + GET_CURRENT_IP + LOAD_DATA_ADDRESS groupIndex,9 + JUMP_TO_COMMON groupIndex,9 +endm + +THUNKS_PAGE_BLOCK macro + TenThunks 0 + TenThunks 1 + TenThunks 2 + TenThunks 3 + TenThunks 4 + TenThunks 5 + TenThunks 6 + TenThunks 7 + TenThunks 8 + TenThunks 9 + TenThunks 10 + TenThunks 11 +endm + +;; +;; The first thunks section should be 64K aligned because it can get +;; mapped multiple times in memory, and mapping works on allocation +;; granularity boundaries (we don't want to map more than what we need) +;; +;; The easiest way to do so is by having the thunks section at the +;; first 64K aligned virtual address in the binary. We provide a section +;; layout file to the linker to tell it how to layout the thunks sections +;; that we care about. (ndp\rh\src\runtime\DLLs\app\mrt100_app_sectionlayout.txt) +;; +;; The PE spec says images cannot have gaps between sections (other +;; than what is required by the section alignment value in the header), +;; therefore we need a couple of padding data sections (otherwise the +;; OS will not load the image). +;; + +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment0, ".pad0" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment1, ".pad1" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment2, ".pad2" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment3, ".pad3" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment4, ".pad4" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment5, ".pad5" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment6, ".pad6" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment7, ".pad7" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment8, ".pad8" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment9, ".pad9" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment10, ".pad10" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment11, ".pad11" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment12, ".pad12" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment13, ".pad13" +NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment14, ".pad14" + +;; +;; Thunk Stubs +;; NOTE: Keep number of blocks in sync with macro/constant named 'NUM_THUNK_BLOCKS' in: +;; - ndp\FxCore\src\System.Private.CoreLib\System\Runtime\InteropServices\ThunkPool.cs +;; - ndp\rh\src\tools\rhbind\zapimage.h +;; +NAMED_LEAF_ENTRY ThunkPool, TKS0, ".tks0" + THUNKS_PAGE_BLOCK +LEAF_END ThunkPool, TKS0 + +NAMED_READWRITE_DATA_SECTION ThunkData0, ".tkd0" + +NAMED_LEAF_ENTRY ThunkPool1, TKS1, ".tks1" + THUNKS_PAGE_BLOCK +LEAF_END ThunkPool1, TKS1 + +NAMED_READWRITE_DATA_SECTION ThunkData1, ".tkd1" + +NAMED_LEAF_ENTRY ThunkPool2, TKS2, ".tks2" + THUNKS_PAGE_BLOCK +LEAF_END ThunkPool2, TKS2 + +NAMED_READWRITE_DATA_SECTION ThunkData2, ".tkd2" + +NAMED_LEAF_ENTRY ThunkPool3, TKS3, ".tks3" + THUNKS_PAGE_BLOCK +LEAF_END ThunkPool3, TKS3 + +NAMED_READWRITE_DATA_SECTION ThunkData3, ".tkd3" + +NAMED_LEAF_ENTRY ThunkPool4, TKS4, ".tks4" + THUNKS_PAGE_BLOCK +LEAF_END ThunkPool4, TKS4 + +NAMED_READWRITE_DATA_SECTION ThunkData4, ".tkd4" + +NAMED_LEAF_ENTRY ThunkPool5, TKS5, ".tks5" + THUNKS_PAGE_BLOCK +LEAF_END ThunkPool5, TKS5 + +NAMED_READWRITE_DATA_SECTION ThunkData5, ".tkd5" + +NAMED_LEAF_ENTRY ThunkPool6, TKS6, ".tks6" + THUNKS_PAGE_BLOCK +LEAF_END ThunkPool6, TKS6 + +NAMED_READWRITE_DATA_SECTION ThunkData6, ".tkd6" + +NAMED_LEAF_ENTRY ThunkPool7, TKS7, ".tks7" + THUNKS_PAGE_BLOCK +LEAF_END ThunkPool7, TKS7 + +NAMED_READWRITE_DATA_SECTION ThunkData7, ".tkd7" + + +;; +;; IntPtr RhpGetThunksBase() +;; +FASTCALL_FUNC RhpGetThunksBase, 0 + ;; Return the address of the first thunk pool to the caller (this is really the base address) + lea eax, [ThunkPool] + ret +FASTCALL_ENDFUNC + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; General Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; +;; int RhpGetNumThunksPerBlock() +;; +FASTCALL_FUNC RhpGetNumThunksPerBlock, 0 + mov eax, THUNK_POOL_NUM_THUNKS_PER_PAGE + ret +FASTCALL_ENDFUNC + +;; +;; int RhpGetThunkSize() +;; +FASTCALL_FUNC RhpGetThunkSize, 0 + mov eax, THUNK_CODESIZE + ret +FASTCALL_ENDFUNC + +;; +;; int RhpGetNumThunkBlocksPerMapping() +;; +FASTCALL_FUNC RhpGetNumThunkBlocksPerMapping, 0 + mov eax, 8 + ret +FASTCALL_ENDFUNC + +;; +;; int RhpGetThunkBlockSize +;; +FASTCALL_FUNC RhpGetThunkBlockSize, 0 + mov eax, PAGE_SIZE * 2 + ret +FASTCALL_ENDFUNC + +;; +;; IntPtr RhpGetThunkDataBlockAddress(IntPtr thunkStubAddress) +;; +FASTCALL_FUNC RhpGetThunkDataBlockAddress, 4 + mov eax, ecx + mov ecx, PAGE_SIZE - 1 + not ecx + and eax, ecx + add eax, PAGE_SIZE + ret +FASTCALL_ENDFUNC + +;; +;; IntPtr RhpGetThunkStubsBlockAddress(IntPtr thunkDataAddress) +;; +FASTCALL_FUNC RhpGetThunkStubsBlockAddress, 4 + mov eax, ecx + mov ecx, PAGE_SIZE - 1 + not ecx + and eax, ecx + sub eax, PAGE_SIZE + ret +FASTCALL_ENDFUNC + + +end diff --git a/src/coreclr/nativeaot/Runtime/i386/UniversalTransition.S b/src/coreclr/nativeaot/Runtime/i386/UniversalTransition.S new file mode 100644 index 00000000000000..876f2dfbcb80d6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/UniversalTransition.S @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// TODO: Implement diff --git a/src/coreclr/nativeaot/Runtime/i386/UniversalTransition.asm b/src/coreclr/nativeaot/Runtime/i386/UniversalTransition.asm new file mode 100644 index 00000000000000..488a5b9d18355e --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/UniversalTransition.asm @@ -0,0 +1,101 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + + .586 + .model flat + option casemap:none + .code + +include AsmMacros.inc + +ifdef FEATURE_DYNAMIC_CODE + +;; +;; Defines an assembly thunk used to make a transition from managed code to a callee, +;; then (based on the return value from the callee), either returning or jumping to +;; a new location while preserving the input arguments. The usage of this thunk also +;; ensures arguments passed are properly reported. +;; +;; TODO: This code currently only tailcalls, and does not return. +;; +;; Inputs: +;; ecx, edx, stack space three pops down: arguments as normal +;; first register sized fields on the stack is the location of the target code +;; the UniversalTransitionThunk will call +;; second register sized field on the stack is the parameter to the target function +;; followed by the return address of the whole method. (This method cannot be called +;; via a call instruction, it must be jumped to.) The fake entrypoint is in place to +;; convince the stack walker this is a normal framed function. +;; +;; NOTE! FOR CORRECTNESS THIS FUNCTION REQUIRES THAT ALL NON-LEAF MANAGED FUNCTIONS HAVE +;; FRAME POINTERS, OR THE STACK WALKER CAN'T STACKWALK OUT OF HERE +;; + +; +; Frame layout is: +; +; {StackPassedArgs} ChildSP+018 CallerSP+000 +; {CallerRetaddr} ChildSP+014 CallerSP-004 +; {CallerEBP} ChildSP+010 CallerSP-008 +; {ReturnBlock (0x8 bytes)} ChildSP+008 CallerSP-010 +; -- On input (i.e., when control jumps to RhpUniversalTransition), the low 4 bytes of +; the ReturnBlock area holds the address of the callee and the high 4 bytes holds the +; extra argument to pass to the callee. +; {IntArgRegs (edx,ecx) (0x8 bytes)} ChildSP+000 CallerSP-018 +; {CalleeRetaddr} ChildSP-004 CallerSP-01c +; +; NOTE: If the frame layout ever changes, the C++ UniversalTransitionStackFrame structure +; must be updated as well. +; +; NOTE: The callee receives a pointer to the base of the pushed IntArgRegs, and the callee +; has knowledge of the exact layout of the entire frame. +; +; NOTE: The stack walker guarantees that conservative GC reporting will be applied to +; everything between the base of the IntArgRegs and the top of the StackPassedArgs. +; + +UNIVERSAL_TRANSITION macro FunctionName + +FASTCALL_FUNC Rhp&FunctionName&_FAKE_ENTRY, 0 + ; Set up an ebp frame + push ebp + mov ebp, esp + push eax + push eax +ALTERNATE_ENTRY Rhp&FunctionName&@0 + push ecx + push edx + + ; + ; Call out to the target, while storing and reporting arguments to the GC. + ; + mov eax, [ebp-8] ; Get the address of the callee + mov edx, [ebp-4] ; Get the extra argument to pass to the callee + lea ecx, [ebp-10h] ; Get pointer to edx value pushed above + call eax + + EXPORT_POINTER_TO_ADDRESS _PointerToReturnFrom&FunctionName + + ; We cannot make the label public as that tricks DIA stackwalker into thinking + ; it's the beginning of a method. For this reason we export an auxiliary variable + ; holding the address instead. + + pop edx + pop ecx + add esp, 8 + pop ebp + jmp eax + +FASTCALL_ENDFUNC + + endm + + ; To enable proper step-in behavior in the debugger, we need to have two instances + ; of the thunk. For the first one, the debugger steps into the call in the function, + ; for the other, it steps over it. + UNIVERSAL_TRANSITION UniversalTransition + UNIVERSAL_TRANSITION UniversalTransition_DebugStepTailCall + +endif + +end diff --git a/src/coreclr/nativeaot/Runtime/i386/WriteBarriers.S b/src/coreclr/nativeaot/Runtime/i386/WriteBarriers.S new file mode 100644 index 00000000000000..876f2dfbcb80d6 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/WriteBarriers.S @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// TODO: Implement diff --git a/src/coreclr/nativeaot/Runtime/i386/WriteBarriers.asm b/src/coreclr/nativeaot/Runtime/i386/WriteBarriers.asm new file mode 100644 index 00000000000000..a7038354094567 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/i386/WriteBarriers.asm @@ -0,0 +1,266 @@ +;; Licensed to the .NET Foundation under one or more agreements. +;; The .NET Foundation licenses this file to you under the MIT license. + +;; +;; Define the helpers used to implement the write barrier required when writing an object reference into a +;; location residing on the GC heap. Such write barriers allow the GC to optimize which objects in +;; non-ephemeral generations need to be scanned for references to ephemeral objects during an ephemeral +;; collection. +;; + + .xmm + .model flat + option casemap:none + .code + +include AsmMacros.inc + +;; Macro used to copy contents of newly updated GC heap locations to a shadow copy of the heap. This is used +;; during garbage collections to verify that object references where never written to the heap without using a +;; write barrier. Note that we're potentially racing to update the shadow heap while other threads are writing +;; new references to the real heap. Since this can't be solved perfectly without critical sections around the +;; entire update process, we instead update the shadow location and then re-check the real location (as two +;; ordered operations) and if there is a disparity we'll re-write the shadow location with a special value +;; (INVALIDGCVALUE) which disables the check for that location. Since the shadow heap is only validated at GC +;; time and these write barrier operations are atomic wrt to GCs this is sufficient to guarantee that the +;; shadow heap contains only valid copies of real heap values or INVALIDGCVALUE. +ifdef WRITE_BARRIER_CHECK + +g_GCShadow TEXTEQU +g_GCShadowEnd TEXTEQU +INVALIDGCVALUE EQU 0CCCCCCCDh + +EXTERN g_GCShadow : DWORD +EXTERN g_GCShadowEnd : DWORD + +UPDATE_GC_SHADOW macro BASENAME, DESTREG, REFREG + + ;; If g_GCShadow is 0, don't perform the check. + cmp g_GCShadow, 0 + je &BASENAME&_UpdateShadowHeap_Done_&DESTREG&_&REFREG& + + ;; Save DESTREG since we're about to modify it (and we need the original value both within the macro and + ;; once we exit the macro). + push DESTREG + + ;; Transform DESTREG into the equivalent address in the shadow heap. + sub DESTREG, G_LOWEST_ADDRESS + jb &BASENAME&_UpdateShadowHeap_PopThenDone_&DESTREG&_&REFREG& + add DESTREG, [g_GCShadow] + cmp DESTREG, [g_GCShadowEnd] + ja &BASENAME&_UpdateShadowHeap_PopThenDone_&DESTREG&_&REFREG& + + ;; Update the shadow heap. + mov [DESTREG], REFREG + + ;; Now check that the real heap location still contains the value we just wrote into the shadow heap. This + ;; read must be strongly ordered wrt to the previous write to prevent race conditions. We also need to + ;; recover the old value of DESTREG for the comparison so use an xchg instruction (which has an implicit lock + ;; prefix). + xchg [esp], DESTREG + cmp [DESTREG], REFREG + jne &BASENAME&_UpdateShadowHeap_Invalidate_&DESTREG&_&REFREG& + + ;; The original DESTREG value is now restored but the stack has a value (the shadow version of the + ;; location) pushed. Need to discard this push before we are done. + add esp, 4 + jmp &BASENAME&_UpdateShadowHeap_Done_&DESTREG&_&REFREG& + +&BASENAME&_UpdateShadowHeap_Invalidate_&DESTREG&_&REFREG&: + ;; Someone went and updated the real heap. We need to invalidate the shadow location since we can't + ;; guarantee whose shadow update won. + + ;; Retrieve shadow location from the stack and restore original DESTREG to the stack. This is an + ;; additional memory barrier we don't require but it's on the rare path and x86 doesn't have an xchg + ;; variant that doesn't implicitly specify the lock prefix. + xchg [esp], DESTREG + mov dword ptr [DESTREG], INVALIDGCVALUE + +&BASENAME&_UpdateShadowHeap_PopThenDone_&DESTREG&_&REFREG&: + ;; Restore original DESTREG value from the stack. + pop DESTREG + +&BASENAME&_UpdateShadowHeap_Done_&DESTREG&_&REFREG&: +endm + +else ; WRITE_BARRIER_CHECK + +UPDATE_GC_SHADOW macro BASENAME, DESTREG, REFREG +endm + +endif ; WRITE_BARRIER_CHECK + +;; There are several different helpers used depending on which register holds the object reference. Since all +;; the helpers have identical structure we use a macro to define this structure. Two arguments are taken, the +;; name of the register that points to the location to be updated and the name of the register that holds the +;; object reference (this should be in upper case as it's used in the definition of the name of the helper). +DEFINE_WRITE_BARRIER macro DESTREG, REFREG + +;; Define a helper with a name of the form RhpAssignRefEAX etc. (along with suitable calling standard +;; decoration). The location to be updated is in DESTREG. The object reference that will be assigned into that +;; location is in one of the other general registers determined by the value of REFREG. +FASTCALL_FUNC RhpAssignRef&REFREG&, 0 + + ;; Export the canonical write barrier under unqualified name as well + ifidni , + @RhpAssignRef@0 label proc + PUBLIC @RhpAssignRef@0 + ALTERNATE_ENTRY RhpAssignRefAVLocation + endif + + ;; Write the reference into the location. Note that we rely on the fact that no GC can occur between here + ;; and the card table update we may perform below. + mov dword ptr [DESTREG], REFREG + + ;; Update the shadow copy of the heap with the same value (if enabled). + UPDATE_GC_SHADOW RhpAssignRef, DESTREG, REFREG + + ;; If the reference is to an object that's not in an ephemeral generation we have no need to track it + ;; (since the object won't be collected or moved by an ephemeral collection). + cmp REFREG, [G_EPHEMERAL_LOW] + jb WriteBarrier_NoBarrierRequired_&DESTREG&_&REFREG& + cmp REFREG, [G_EPHEMERAL_HIGH] + jae WriteBarrier_NoBarrierRequired_&DESTREG&_&REFREG& + + ;; We have a location on the GC heap being updated with a reference to an ephemeral object so we must + ;; track this write. The location address is translated into an offset in the card table bitmap. We set + ;; an entire byte in the card table since it's quicker than messing around with bitmasks and we only write + ;; the byte if it hasn't already been done since writes are expensive and impact scaling. + shr DESTREG, 10 + add DESTREG, [G_CARD_TABLE] + cmp byte ptr [DESTREG], 0FFh + jne WriteBarrier_UpdateCardTable_&DESTREG&_&REFREG& + +WriteBarrier_NoBarrierRequired_&DESTREG&_&REFREG&: + ret + +;; We get here if it's necessary to update the card table. +WriteBarrier_UpdateCardTable_&DESTREG&_&REFREG&: + mov byte ptr [DESTREG], 0FFh + ret +FASTCALL_ENDFUNC +endm + +RET4 macro + ret 4 +endm + +DEFINE_CHECKED_WRITE_BARRIER_CORE macro BASENAME, DESTREG, REFREG, RETINST + + ;; The location being updated might not even lie in the GC heap (a handle or stack location for instance), + ;; in which case no write barrier is required. + cmp DESTREG, [G_LOWEST_ADDRESS] + jb &BASENAME&_NoBarrierRequired_&DESTREG&_&REFREG& + cmp DESTREG, [G_HIGHEST_ADDRESS] + jae &BASENAME&_NoBarrierRequired_&DESTREG&_&REFREG& + + ;; Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + ;; we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW BASENAME, DESTREG, REFREG + + ;; If the reference is to an object that's not in an ephemeral generation we have no need to track it + ;; (since the object won't be collected or moved by an ephemeral collection). + cmp REFREG, [G_EPHEMERAL_LOW] + jb &BASENAME&_NoBarrierRequired_&DESTREG&_&REFREG& + cmp REFREG, [G_EPHEMERAL_HIGH] + jae &BASENAME&_NoBarrierRequired_&DESTREG&_&REFREG& + + ;; We have a location on the GC heap being updated with a reference to an ephemeral object so we must + ;; track this write. The location address is translated into an offset in the card table bitmap. We set + ;; an entire byte in the card table since it's quicker than messing around with bitmasks and we only write + ;; the byte if it hasn't already been done since writes are expensive and impact scaling. + shr DESTREG, 10 + add DESTREG, [G_CARD_TABLE] + cmp byte ptr [DESTREG], 0FFh + jne &BASENAME&_UpdateCardTable_&DESTREG&_&REFREG& + +&BASENAME&_NoBarrierRequired_&DESTREG&_&REFREG&: + RETINST + +;; We get here if it's necessary to update the card table. +&BASENAME&_UpdateCardTable_&DESTREG&_&REFREG&: + mov byte ptr [DESTREG], 0FFh + RETINST + +endm + + +;; This macro is very much like the one above except that it generates a variant of the function which also +;; checks whether the destination is actually somewhere within the GC heap. +DEFINE_CHECKED_WRITE_BARRIER macro DESTREG, REFREG + +;; Define a helper with a name of the form RhpCheckedAssignRefEAX etc. (along with suitable calling standard +;; decoration). The location to be updated is in DESTREG. The object reference that will be assigned into +;; that location is in one of the other general registers determined by the value of REFREG. + +;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen on the first instruction +;; - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +FASTCALL_FUNC RhpCheckedAssignRef&REFREG&, 0 + + ;; Export the canonical write barrier under unqualified name as well + ifidni , + @RhpCheckedAssignRef@0 label proc + PUBLIC @RhpCheckedAssignRef@0 + ALTERNATE_ENTRY RhpCheckedAssignRefAVLocation + endif + + ;; Write the reference into the location. Note that we rely on the fact that no GC can occur between here + ;; and the card table update we may perform below. + mov dword ptr [DESTREG], REFREG + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedAssignRef, DESTREG, REFREG, ret + +FASTCALL_ENDFUNC + +endm + +;; One day we might have write barriers for all the possible argument registers but for now we have +;; just one write barrier that assumes the input register is EDX. +DEFINE_CHECKED_WRITE_BARRIER ECX, EDX +DEFINE_WRITE_BARRIER ECX, EDX + +;; Need some more write barriers to run CLR compiled MDIL on Redhawk - commented out for now +;; DEFINE_WRITE_BARRIER EDX, EAX +;; DEFINE_WRITE_BARRIER EDX, ECX +;; DEFINE_WRITE_BARRIER EDX, EBX +;; DEFINE_WRITE_BARRIER EDX, ESI +;; DEFINE_WRITE_BARRIER EDX, EDI +;; DEFINE_WRITE_BARRIER EDX, EBP + +;; DEFINE_CHECKED_WRITE_BARRIER EDX, EAX +;; DEFINE_CHECKED_WRITE_BARRIER EDX, ECX +;; DEFINE_CHECKED_WRITE_BARRIER EDX, EBX +;; DEFINE_CHECKED_WRITE_BARRIER EDX, ESI +;; DEFINE_CHECKED_WRITE_BARRIER EDX, EDI +;; DEFINE_CHECKED_WRITE_BARRIER EDX, EBP + +;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at @RhpCheckedLockCmpXchgAVLocation@0 +;; - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +;; pass third argument in EAX +FASTCALL_FUNC RhpCheckedLockCmpXchg +ALTERNATE_ENTRY RhpCheckedLockCmpXchgAVLocation + lock cmpxchg [ecx], edx + jne RhpCheckedLockCmpXchg_NoBarrierRequired_ECX_EDX + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedLockCmpXchg, ECX, EDX, ret + +FASTCALL_ENDFUNC + +;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at @RhpCheckedXchgAVLocation@0 +;; - Function "UnwindSimpleHelperToCaller" assumes the stack contains just the pushed return address +FASTCALL_FUNC RhpCheckedXchg, 0 + + ;; Setup eax with the new object for the exchange, that way it will automatically hold the correct result + ;; afterwards and we can leave edx unaltered ready for the GC write barrier below. + mov eax, edx +ALTERNATE_ENTRY RhpCheckedXchgAVLocation + xchg [ecx], eax + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedXchg, ECX, EDX, ret + +FASTCALL_ENDFUNC + + end diff --git a/src/coreclr/nativeaot/Runtime/inc/CommonTypes.h b/src/coreclr/nativeaot/Runtime/inc/CommonTypes.h new file mode 100644 index 00000000000000..76b55aed711c32 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/CommonTypes.h @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __COMMON_TYPES_H__ +#define __COMMON_TYPES_H__ + +#include +#include +#include +#include + +using std::nothrow; +using std::size_t; +using std::uintptr_t; +using std::intptr_t; + +typedef wchar_t WCHAR; +typedef void * HANDLE; + +typedef uint32_t UInt32_BOOL; // windows 4-byte BOOL, 0 -> false, everything else -> true +#define UInt32_FALSE 0 +#define UInt32_TRUE 1 + +#endif // __COMMON_TYPES_H__ diff --git a/src/coreclr/nativeaot/Runtime/inc/DebugMacrosExt.h b/src/coreclr/nativeaot/Runtime/inc/DebugMacrosExt.h new file mode 100644 index 00000000000000..54622f5a45c8a2 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/DebugMacrosExt.h @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// DebugMacrosExt.h +// +// Simple debugging macros that take no dependencies on CLR headers. +// This header can be used from outside the CLR. +// +//***************************************************************************** + +#ifndef __DebugMacrosExt_h__ +#define __DebugMacrosExt_h__ + +#if !defined(_DEBUG_IMPL) && defined(_DEBUG) && !defined(DACCESS_COMPILE) +#define _DEBUG_IMPL 1 +#endif + +#ifdef _DEBUG +// A macro to execute a statement only in _DEBUG. +#define DEBUG_STMT(stmt) stmt +#define INDEBUG(x) x +#define INDEBUG_COMMA(x) x, +#define COMMA_INDEBUG(x) ,x +#define NOT_DEBUG(x) +#else +#define DEBUG_STMT(stmt) +#define INDEBUG(x) +#define INDEBUG_COMMA(x) +#define COMMA_INDEBUG(x) +#define NOT_DEBUG(x) x +#endif + + +#ifdef _DEBUG_IMPL +#define INDEBUGIMPL(x) x +#define INDEBUGIMPL_COMMA(x) x, +#define COMMA_INDEBUGIMPL(x) ,x +#else +#define INDEBUGIMPL(x) +#define INDEBUGIMPL_COMMA(x) +#define COMMA_INDEBUGIMPL(x) +#endif + + +#endif diff --git a/src/coreclr/nativeaot/Runtime/inc/MethodTable.h b/src/coreclr/nativeaot/Runtime/inc/MethodTable.h new file mode 100644 index 00000000000000..f39cf58b930130 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/MethodTable.h @@ -0,0 +1,370 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Fundamental runtime type representation + +#pragma warning(push) +#pragma warning(disable:4200) // nonstandard extension used : zero-sized array in struct/union +//------------------------------------------------------------------------------------------------- +// Forward declarations + +class MethodTable; +class TypeManager; +struct TypeManagerHandle; +class DynamicModule; +struct EETypeRef; + +#if !defined(USE_PORTABLE_HELPERS) +#define SUPPORTS_WRITABLE_DATA 1 +#endif + +//------------------------------------------------------------------------------------------------- +// Array of these represents the interfaces implemented by a type + +class EEInterfaceInfo +{ + public: + MethodTable * GetInterfaceEEType() + { + return ((UIntTarget)m_pInterfaceEEType & ((UIntTarget)1)) ? + *(MethodTable**)((UIntTarget)m_ppInterfaceEETypeViaIAT & ~((UIntTarget)1)) : + m_pInterfaceEEType; + } + + private: + union + { + MethodTable * m_pInterfaceEEType; // m_uFlags == InterfaceFlagNormal + MethodTable ** m_ppInterfaceEETypeViaIAT; // m_uFlags == InterfaceViaIATFlag + }; +}; + +//------------------------------------------------------------------------------------------------- +// The subset of TypeFlags that Redhawk knows about at runtime +// This should match the TypeFlags enum in the managed type system. +enum EETypeElementType : uint8_t +{ + // Primitive + ElementType_Unknown = 0x00, + ElementType_Void = 0x01, + ElementType_Boolean = 0x02, + ElementType_Char = 0x03, + ElementType_SByte = 0x04, + ElementType_Byte = 0x05, + ElementType_Int16 = 0x06, + ElementType_UInt16 = 0x07, + ElementType_Int32 = 0x08, + ElementType_UInt32 = 0x09, + ElementType_Int64 = 0x0A, + ElementType_UInt64 = 0x0B, + ElementType_IntPtr = 0x0C, + ElementType_UIntPtr = 0x0D, + ElementType_Single = 0x0E, + ElementType_Double = 0x0F, + + ElementType_ValueType = 0x10, + // Enum = 0x11, // EETypes store enums as their underlying type + ElementType_Nullable = 0x12, + // Unused 0x13, + + ElementType_Class = 0x14, + ElementType_Interface = 0x15, + + ElementType_SystemArray = 0x16, // System.Array type + + ElementType_Array = 0x17, + ElementType_SzArray = 0x18, + ElementType_ByRef = 0x19, + ElementType_Pointer = 0x1A, +}; + +//------------------------------------------------------------------------------------------------- +// Support for encapsulating the location of fields in the MethodTable that have variable offsets or may be +// optional. +// +// The following enumaration gives symbolic names for these fields and is used with the GetFieldPointer() and +// GetFieldOffset() APIs. +enum EETypeField +{ + ETF_InterfaceMap, + ETF_TypeManagerIndirection, + ETF_WritableData, + ETF_Finalizer, + ETF_OptionalFieldsPtr, + ETF_SealedVirtualSlots, + ETF_DynamicTemplateType, + ETF_DynamicDispatchMap, + ETF_DynamicModule, + ETF_GenericDefinition, + ETF_GenericComposition, + ETF_DynamicGcStatics, + ETF_DynamicNonGcStatics, + ETF_DynamicThreadStaticOffset, +}; + +//------------------------------------------------------------------------------------------------- +// Fundamental runtime type representation +typedef DPTR(class MethodTable) PTR_EEType; +typedef DPTR(PTR_EEType) PTR_PTR_EEType; + +extern "C" void PopulateDebugHeaders(); + +class MethodTable +{ + friend class AsmOffsets; + friend void PopulateDebugHeaders(); + +private: + struct RelatedTypeUnion + { + union + { + // Kinds.CanonicalEEType + MethodTable* m_pBaseType; + MethodTable** m_ppBaseTypeViaIAT; + + // Kinds.ClonedEEType + MethodTable** m_pCanonicalType; + MethodTable** m_ppCanonicalTypeViaIAT; + + // Kinds.ParameterizedEEType + MethodTable* m_pRelatedParameterType; + MethodTable** m_ppRelatedParameterTypeViaIAT; + }; + }; + + uint16_t m_usComponentSize; + uint16_t m_usFlags; + uint32_t m_uBaseSize; + RelatedTypeUnion m_RelatedType; + uint16_t m_usNumVtableSlots; + uint16_t m_usNumInterfaces; + uint32_t m_uHashCode; + + TgtPTR_Void m_VTable[]; // make this explicit so the binder gets the right alignment + + // after the m_usNumVtableSlots vtable slots, we have m_usNumInterfaces slots of + // EEInterfaceInfo, and after that a couple of additional pointers based on whether the type is + // finalizable (the address of the finalizer code) or has optional fields (pointer to the compacted + // fields). + + enum Flags + { + // There are four kinds of EETypes, the three of them regular types that use the full MethodTable encoding + // plus a fourth kind used as a grab bag of unusual edge cases which are encoded in a smaller, + // simplified version of MethodTable. See LimitedEEType definition below. + EETypeKindMask = 0x0003, + + // This flag is set when m_pRelatedType is in a different module. In that case, m_pRelatedType + // actually points to a 'fake' MethodTable whose m_pRelatedType field lines up with an IAT slot in this + // module, which then points to the desired MethodTable. In other words, there is an extra indirection + // through m_pRelatedType to get to the related type in the other module. + RelatedTypeViaIATFlag = 0x0004, + + IsDynamicTypeFlag = 0x0008, + + // This MethodTable represents a type which requires finalization + HasFinalizerFlag = 0x0010, + + // This type contain gc pointers + HasPointersFlag = 0x0020, + + // This type is generic and one or more of it's type parameters is co- or contra-variant. This only + // applies to interface and delegate types. + GenericVarianceFlag = 0x0080, + + // This type has optional fields present. + OptionalFieldsFlag = 0x0100, + + // Unused = 0x0200, + + // This type is generic. + IsGenericFlag = 0x0400, + + // We are storing a EETypeElementType in the upper bits for unboxing enums + ElementTypeMask = 0xf800, + ElementTypeShift = 11, + }; + +public: + + enum Kinds + { + CanonicalEEType = 0x0000, + ClonedEEType = 0x0001, + ParameterizedEEType = 0x0002, + GenericTypeDefEEType = 0x0003, + }; + + uint32_t get_BaseSize() + { return m_uBaseSize; } + + uint16_t get_ComponentSize() + { return m_usComponentSize; } + + PTR_Code get_Slot(uint16_t slotNumber); + + PTR_PTR_Code get_SlotPtr(uint16_t slotNumber); + + Kinds get_Kind(); + + bool IsCloned() + { return get_Kind() == ClonedEEType; } + + bool IsRelatedTypeViaIAT() + { return ((m_usFlags & (uint16_t)RelatedTypeViaIATFlag) != 0); } + + bool IsArray() + { + EETypeElementType elementType = GetElementType(); + return elementType == ElementType_Array || elementType == ElementType_SzArray; + } + + bool IsSzArray() + { return GetElementType() == ElementType_SzArray; } + + bool IsParameterizedType() + { return (get_Kind() == ParameterizedEEType); } + + bool IsGenericTypeDefinition() + { return (get_Kind() == GenericTypeDefEEType); } + + bool IsCanonical() + { return get_Kind() == CanonicalEEType; } + + bool IsInterface() + { return GetElementType() == ElementType_Interface; } + + MethodTable * get_CanonicalEEType(); + + MethodTable * get_RelatedParameterType(); + + // A parameterized type shape less than SZARRAY_BASE_SIZE indicates that this is not + // an array but some other parameterized type (see: ParameterizedTypeShapeConstants) + // For arrays, this number uniquely captures both Sz/Md array flavor and rank. + uint32_t get_ParameterizedTypeShape() { return m_uBaseSize; } + + bool get_IsValueType() + { return GetElementType() < ElementType_Class; } + + bool HasFinalizer() + { + return (m_usFlags & HasFinalizerFlag) != 0; + } + + bool HasReferenceFields() + { + return (m_usFlags & HasPointersFlag) != 0; + } + + bool HasOptionalFields() + { + return (m_usFlags & OptionalFieldsFlag) != 0; + } + + bool IsEquivalentTo(MethodTable * pOtherEEType) + { + if (this == pOtherEEType) + return true; + + MethodTable * pThisEEType = this; + + if (pThisEEType->IsCloned()) + pThisEEType = pThisEEType->get_CanonicalEEType(); + + if (pOtherEEType->IsCloned()) + pOtherEEType = pOtherEEType->get_CanonicalEEType(); + + if (pThisEEType == pOtherEEType) + return true; + + if (pThisEEType->IsParameterizedType() && pOtherEEType->IsParameterizedType()) + { + return pThisEEType->get_RelatedParameterType()->IsEquivalentTo(pOtherEEType->get_RelatedParameterType()) && + pThisEEType->get_ParameterizedTypeShape() == pOtherEEType->get_ParameterizedTypeShape(); + } + + return false; + } + + // How many vtable slots are there? + uint16_t GetNumVtableSlots() + { return m_usNumVtableSlots; } + + // How many entries are in the interface map after the vtable slots? + uint16_t GetNumInterfaces() + { return m_usNumInterfaces; } + + // Does this class (or its base classes) implement any interfaces? + bool HasInterfaces() + { return GetNumInterfaces() != 0; } + + bool IsGeneric() + { return (m_usFlags & IsGenericFlag) != 0; } + + DynamicModule* get_DynamicModule(); + + TypeManagerHandle* GetTypeManagerPtr(); + + // Used only by GC initialization, this initializes the MethodTable used to mark free entries in the GC heap. + // It should be an array type with a component size of one (so the GC can easily size it as appropriate) + // and should be marked as not containing any references. The rest of the fields don't matter: the GC does + // not query them and the rest of the runtime will never hold a reference to free object. + inline void InitializeAsGcFreeType(); + +#ifdef DACCESS_COMPILE + bool DacVerify(); + static bool DacVerifyWorker(MethodTable* pThis); +#endif // DACCESS_COMPILE + + // Mark or determine that a type is generic and one or more of it's type parameters is co- or + // contra-variant. This only applies to interface and delegate types. + bool HasGenericVariance() + { return (m_usFlags & GenericVarianceFlag) != 0; } + + EETypeElementType GetElementType() + { return (EETypeElementType)((m_usFlags & ElementTypeMask) >> ElementTypeShift); } + + // Determine whether a type is an instantiation of Nullable. + bool IsNullable() + { return GetElementType() == ElementType_Nullable; } + + // Determine whether a type was created by dynamic type loader + bool IsDynamicType() + { return (m_usFlags & IsDynamicTypeFlag) != 0; } + + uint32_t GetHashCode(); + + // Helper methods that deal with MethodTable topology (size and field layout). These are useful since as we + // optimize for pay-for-play we increasingly want to customize exactly what goes into an MethodTable on a + // per-type basis. The rules that govern this can be both complex and volatile and we risk sprinkling + // various layout rules through the binder and runtime that obscure the basic meaning of the code and are + // brittle: easy to overlook when one of the rules changes. + // + // The following methods can in some cases have fairly complex argument lists of their own and in that way + // they expose more of the implementation details than we'd ideally like. But regardless they still serve + // an arguably more useful purpose: they identify all the places that rely on the MethodTable layout. As we + // change layout rules we might have to change the arguments to the methods below but in doing so we will + // instantly identify all the other parts of the binder and runtime that need to be updated. + + // Calculate the offset of a field of the MethodTable that has a variable offset. + inline uint32_t GetFieldOffset(EETypeField eField); + + // Validate an MethodTable extracted from an object. + bool Validate(bool assertOnFail = true); + +public: + // Methods expected by the GC + uint32_t GetBaseSize() { return get_BaseSize(); } + uint16_t GetComponentSize() { return get_ComponentSize(); } + uint16_t RawGetComponentSize() { return get_ComponentSize(); } + uint32_t ContainsPointers() { return HasReferenceFields(); } + uint32_t ContainsPointersOrCollectible() { return HasReferenceFields(); } + bool HasComponentSize() const { return true; } + bool HasCriticalFinalizer() { return false; } + bool IsValueType() { return get_IsValueType(); } + UInt32_BOOL SanityCheck() { return Validate(); } +}; + +#pragma warning(pop) + diff --git a/src/coreclr/nativeaot/Runtime/inc/MethodTable.inl b/src/coreclr/nativeaot/Runtime/inc/MethodTable.inl new file mode 100644 index 00000000000000..f8f7215989ed03 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/MethodTable.inl @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __eetype_inl__ +#define __eetype_inl__ +//----------------------------------------------------------------------------------------------------------- +inline uint32_t MethodTable::GetHashCode() +{ + return m_uHashCode; +} + +//----------------------------------------------------------------------------------------------------------- +inline PTR_Code MethodTable::get_Slot(uint16_t slotNumber) +{ + ASSERT(slotNumber < m_usNumVtableSlots); + return *get_SlotPtr(slotNumber); +} + +//----------------------------------------------------------------------------------------------------------- +inline PTR_PTR_Code MethodTable::get_SlotPtr(uint16_t slotNumber) +{ + ASSERT(slotNumber < m_usNumVtableSlots); + return dac_cast(dac_cast(this) + offsetof(MethodTable, m_VTable)) + slotNumber; +} + +#ifdef DACCESS_COMPILE +inline bool MethodTable::DacVerify() +{ + // Use a separate static worker because the worker validates + // the whole chain of EETypes and we don't want to accidentally + // answer questions from 'this' that should have come from the + // 'current' MethodTable. + return DacVerifyWorker(this); +} +// static +inline bool MethodTable::DacVerifyWorker(MethodTable* pThis) +{ + //********************************************************************* + //**** ASSUMES MAX TYPE HIERARCHY DEPTH OF 1024 TYPES **** + //********************************************************************* + const int MAX_SANE_RELATED_TYPES = 1024; + //********************************************************************* + //**** ASSUMES MAX OF 200 INTERFACES IMPLEMENTED ON ANY GIVEN TYPE **** + //********************************************************************* + const int MAX_SANE_NUM_INSTANCES = 200; + + + PTR_EEType pCurrentType = dac_cast(pThis); + for (int i = 0; i < MAX_SANE_RELATED_TYPES; i++) + { + // Verify interface map + if (pCurrentType->GetNumInterfaces() > MAX_SANE_NUM_INSTANCES) + return false; + + // Validate the current type + if (!pCurrentType->Validate(false)) + return false; + + // + // Now on to the next type in the hierarchy. + // + + if (pCurrentType->IsRelatedTypeViaIAT()) + pCurrentType = *dac_cast(reinterpret_cast(pCurrentType->m_RelatedType.m_ppBaseTypeViaIAT)); + else + pCurrentType = dac_cast(reinterpret_cast(pCurrentType->m_RelatedType.m_pBaseType)); + + if (pCurrentType == NULL) + break; + } + + if (pCurrentType != NULL) + return false; // assume we found an infinite loop + + return true; +} +#endif + +#if !defined(DACCESS_COMPILE) +inline PTR_UInt8 FollowRelativePointer(const int32_t* pDist) +{ + int32_t dist = *pDist; + + PTR_UInt8 result = (PTR_UInt8)pDist + dist; + + return result; +} + +inline TypeManagerHandle* MethodTable::GetTypeManagerPtr() +{ + uint32_t cbOffset = GetFieldOffset(ETF_TypeManagerIndirection); + +#if !defined(USE_PORTABLE_HELPERS) + if (!IsDynamicType()) + { + return (TypeManagerHandle*)FollowRelativePointer((int32_t*)((uint8_t*)this + cbOffset)); + } + else +#endif + { + return *(TypeManagerHandle**)((uint8_t*)this + cbOffset); + } +} +#endif // !defined(DACCESS_COMPILE) + +// Calculate the offset of a field of the MethodTable that has a variable offset. +__forceinline uint32_t MethodTable::GetFieldOffset(EETypeField eField) +{ + // First part of MethodTable consists of the fixed portion followed by the vtable. + uint32_t cbOffset = offsetof(MethodTable, m_VTable) + (sizeof(UIntTarget) * m_usNumVtableSlots); + + // Then we have the interface map. + if (eField == ETF_InterfaceMap) + { + ASSERT(GetNumInterfaces() > 0); + return cbOffset; + } + cbOffset += sizeof(EEInterfaceInfo) * GetNumInterfaces(); + + const uint32_t relativeOrFullPointerOffset = +#if USE_PORTABLE_HELPERS + sizeof(UIntTarget); +#else + IsDynamicType() ? sizeof(UIntTarget) : sizeof(uint32_t); +#endif + + // Followed by the type manager indirection cell. + if (eField == ETF_TypeManagerIndirection) + { + return cbOffset; + } + cbOffset += relativeOrFullPointerOffset; + +#if SUPPORTS_WRITABLE_DATA + // Followed by writable data. + if (eField == ETF_WritableData) + { + return cbOffset; + } + cbOffset += relativeOrFullPointerOffset; +#endif + + // Followed by the pointer to the finalizer method. + if (eField == ETF_Finalizer) + { + ASSERT(HasFinalizer()); + return cbOffset; + } + if (HasFinalizer()) + cbOffset += relativeOrFullPointerOffset; + + // Followed by the pointer to the optional fields. + if (eField == ETF_OptionalFieldsPtr) + { + ASSERT(HasOptionalFields()); + return cbOffset; + } + if (HasOptionalFields()) + cbOffset += relativeOrFullPointerOffset; + + // Followed by the pointer to the sealed virtual slots + if (eField == ETF_SealedVirtualSlots) + return cbOffset; + + ASSERT(!"Decoding the rest requires rare flags"); + + return 0; +} +#endif // __eetype_inl__ diff --git a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h new file mode 100644 index 00000000000000..fa3607b93b9775 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Please keep the data structures in this file in sync with the managed version at +// src/Common/src/Internal/Runtime/ModuleHeaders.cs +// + +struct ReadyToRunHeaderConstants +{ + static const uint32_t Signature = 0x00525452; // 'RTR' + + static const uint32_t CurrentMajorVersion = 5; + static const uint32_t CurrentMinorVersion = 0; +}; + +struct ReadyToRunHeader +{ + uint32_t Signature; // ReadyToRunHeaderConstants.Signature + uint16_t MajorVersion; + uint16_t MinorVersion; + + uint32_t Flags; + + uint16_t NumberOfSections; + uint8_t EntrySize; + uint8_t EntryType; + + // Array of sections follows. +}; + +// +// ReadyToRunSectionType IDs are used by the runtime to look up specific global data sections +// from each module linked into the final binary. New sections should be added at the bottom +// of the enum and deprecated sections should not be removed to preserve ID stability. +// +// Eventually this will be reconciled with ReadyToRunSectionType from +// https://github.com/dotnet/coreclr/blob/master/src/inc/readytorun.h +// +enum class ReadyToRunSectionType +{ + StringTable = 200, + GCStaticRegion = 201, + ThreadStaticRegion = 202, + InterfaceDispatchTable = 203, + TypeManagerIndirection = 204, + EagerCctor = 205, + FrozenObjectRegion = 206, + GCStaticDesc = 207, + ThreadStaticOffsetRegion = 208, + ThreadStaticGCDescRegion = 209, + ThreadStaticIndex = 210, + LoopHijackFlag = 211, + ImportAddressTables = 212, + + // Sections 300 - 399 are reserved for RhFindBlob backwards compatibility + ReadonlyBlobRegionStart = 300, + ReadonlyBlobRegionEnd = 399, +}; + +enum class ModuleInfoFlags +{ + HasEndPointer = 0x1, +}; diff --git a/src/coreclr/nativeaot/Runtime/inc/TargetPtrs.h b/src/coreclr/nativeaot/Runtime/inc/TargetPtrs.h new file mode 100644 index 00000000000000..f581705c85db76 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/TargetPtrs.h @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#ifndef _TARGETPTRS_H_ +#define _TARGETPTRS_H_ + +typedef DPTR(class MethodTable) PTR_EEType; +typedef SPTR(struct StaticGcDesc) PTR_StaticGcDesc; + +#ifdef TARGET_AMD64 +typedef uint64_t UIntTarget; +#elif defined(TARGET_X86) +typedef uint32_t UIntTarget; +#elif defined(TARGET_ARM) +typedef uint32_t UIntTarget; +#elif defined(TARGET_ARM64) +typedef uint64_t UIntTarget; +#elif defined(TARGET_WASM) +typedef uint32_t UIntTarget; +#else +#error unexpected target architecture +#endif + +typedef PTR_UInt8 TgtPTR_UInt8; +typedef PTR_UInt32 TgtPTR_UInt32; +typedef void * TgtPTR_Void; +typedef PTR_EEType TgtPTR_EEType; +typedef class Thread * TgtPTR_Thread; +typedef struct CORINFO_Object * TgtPTR_CORINFO_Object; +typedef PTR_StaticGcDesc TgtPTR_StaticGcDesc; + +#endif // !_TARGETPTRS_H_ diff --git a/src/coreclr/nativeaot/Runtime/inc/daccess.h b/src/coreclr/nativeaot/Runtime/inc/daccess.h new file mode 100644 index 00000000000000..8ef6bc929e133d --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/daccess.h @@ -0,0 +1,2387 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//***************************************************************************** +// File: daccess.h +// +// Support for external access of runtime data structures. These +// macros and templates hide the details of pointer and data handling +// so that data structures and code can be compiled to work both +// in-process and through a special memory access layer. +// +// This code assumes the existence of two different pieces of code, +// the target, the runtime code that is going to be examined, and +// the host, the code that's doing the examining. Access to the +// target is abstracted so the target may be a live process on the +// same machine, a live process on a different machine, a dump file +// or whatever. No assumptions should be made about accessibility +// of the target. +// +// This code assumes that the data in the target is static. Any +// time the target's data changes the interfaces must be reset so +// that potentially stale data is discarded. +// +// This code is intended for read access and there is no +// way to write data back currently. +// +// DAC-ized code: +// - is read-only (non-invasive). So DACized codepaths can not trigger a GC. +// - has no Thread* object. In reality, DAC-ized codepaths are +// ReadProcessMemory calls from out-of-process. Conceptually, they +// are like a pure-native (preemptive) thread. +//// +// This means that in particular, you cannot DACize a GCTRIGGERS function. +// Neither can you DACize a function that throws if this will involve +// allocating a new exception object. There may be +// exceptions to these rules if you can guarantee that the DACized +// part of the code path cannot cause a garbage collection (see +// EditAndContinueModule::ResolveField for an example). +// If you need to DACize a function that may trigger +// a GC, it is probably best to refactor the function so that the DACized +// part of the code path is in a separate function. For instance, +// functions with GetOrCreate() semantics are hard to DAC-ize because +// they the Create portion is inherently invasive. Instead, consider refactoring +// into a GetOrFail() function that DAC can call; and then make GetOrCreate() +// a wrapper around that. + +// +// This code works by hiding the details of access to target memory. +// Access is divided into two types: +// 1. DPTR - access to a piece of data. +// 2. VPTR - access to a class with a vtable. The class can only have +// a single vtable pointer at the beginning of the class instance. +// Things only need to be declared as VPTRs when it is necessary to +// call virtual functions in the host. In that case the access layer +// must do extra work to provide a host vtable for the object when +// it is retrieved so that virtual functions can be called. +// +// When compiling with DACCESS_COMPILE the macros turn into templates +// which replace pointers with smart pointers that know how to fetch +// data from the target process and provide a host process version of it. +// Normal data structure access will transparently receive a host copy +// of the data and proceed, so code such as +// typedef DPTR(Class) PTR_Class; +// PTR_Class cls; +// int val = cls->m_Int; +// will work without modification. The appropriate operators are overloaded +// to provide transparent access, such as the -> operator in this case. +// Note that the convention is to create an appropriate typedef for +// each type that will be accessed. This hides the particular details +// of the type declaration and makes the usage look more like regular code. +// +// The ?PTR classes also have an implicit base type cast operator to +// produce a host-pointer instance of the given type. For example +// Class* cls = PTR_Class(addr); +// works by implicit conversion from the PTR_Class created by wrapping +// to a host-side Class instance. Again, this means that existing code +// can work without modification. +// +// Code Example: +// +// typedef struct _rangesection +// { +// PTR_IJitManager pjit; +// PTR_RangeSection pright; +// PTR_RangeSection pleft; +// ... Other fields omitted ... +// } RangeSection; +// +// RangeSection* pRS = m_RangeTree; +// +// while (pRS != NULL) +// { +// if (currentPC < pRS->LowAddress) +// pRS=pRS->pleft; +// else if (currentPC > pRS->HighAddress) +// pRS=pRS->pright; +// else +// { +// return pRS->pjit; +// } +// } +// +// This code does not require any modifications. The global reference +// provided by m_RangeTree will be a host version of the RangeSection +// instantiated by conversion. The references to pRS->pleft and +// pRS->pright will refer to DPTRs due to the modified declaration. +// In the assignment statement the compiler will automatically use +// the implicit conversion from PTR_RangeSection to RangeSection*, +// causing a host instance to be created. Finally, if an appropriate +// section is found the use of pRS->pjit will cause an implicit +// conversion from PTR_IJitManager to IJitManager. The VPTR code +// will look at target memory to determine the actual derived class +// for the JitManager and instantiate the right class in the host so +// that host virtual functions can be used just as they would in +// the target. +// +// There are situations where code modifications are required, though. +// +// 1. Any time the actual value of an address matters, such as using +// it as a search key in a tree, the target address must be used. +// +// An example of this is the RangeSection tree used to locate JIT +// managers. A portion of this code is shown above. Each +// RangeSection node in the tree describes a range of addresses +// managed by the JitMan. These addresses are just being used as +// values, not to dereference through, so there are not DPTRs. When +// searching the range tree for an address the address used in the +// search must be a target address as that's what values are kept in +// the RangeSections. In the code shown above, currentPC must be a +// target address as the RangeSections in the tree are all target +// addresses. Use dac_cast to retrieve the target address +// of a ?PTR, as well as to convert a host address to the +// target address used to retrieve that particular instance. Do not +// use dac_cast with any raw target pointer types (such as BYTE*). +// +// 2. Any time an address is modified, such as by address arithmetic, +// the arithmetic must be performed on the target address. +// +// When a host instance is created it is created for the type in use. +// There is no particular relation to any other instance, so address +// arithmetic cannot be used to get from one instance to any other +// part of memory. For example +// char* Func(Class* cls) +// { +// // String follows the basic Class data. +// return (char*)(cls + 1); +// } +// does not work with external access because the Class* used would +// have retrieved only a Class worth of data. There is no string +// following the host instance. Instead, this code should use +// dac_cast to get the target address of the Class +// instance, add sizeof(*cls) and then create a new ?PTR to access +// the desired data. Note that the newly retrieved data will not +// be contiguous with the Class instance, so address arithmetic +// will still not work. +// +// Previous Code: +// +// BOOL IsTarget(LPVOID ip) +// { +// StubCallInstrs* pStubCallInstrs = GetStubCallInstrs(); +// +// if (ip == (LPVOID) &(pStubCallInstrs->m_op)) +// { +// return TRUE; +// } +// +// Modified Code: +// +// BOOL IsTarget(LPVOID ip) +// { +// StubCallInstrs* pStubCallInstrs = GetStubCallInstrs(); +// +// if ((TADDR)ip == dac_cast(pStubCallInstrs) + +// (TADDR)offsetof(StubCallInstrs, m_op)) +// { +// return TRUE; +// } +// +// The parameter ip is a target address, so the host pStubCallInstrs +// cannot be used to derive an address from. The member & reference +// has to be replaced with a conversion from host to target address +// followed by explicit offsetting for the field. +// +// PTR_HOST_MEMBER_TADDR is a convenience macro that encapsulates +// these two operations, so the above code could also be: +// +// if ((TADDR)ip == +// PTR_HOST_MEMBER_TADDR(StubCallInstrs, pStubCallInstrs, m_op)) +// +// 3. Any time the amount of memory referenced through an address +// changes, such as by casting to a different type, a new ?PTR +// must be created. +// +// Host instances are created and stored based on both the target +// address and size of access. The access code has no way of knowing +// all possible ways that data will be retrieved for a given address +// so if code changes the way it accesses through an address a new +// ?PTR must be used, which may lead to a difference instance and +// different host address. This means that pointer identity does not hold +// across casts, so code like +// Class* cls = PTR_Class(addr); +// Class2* cls2 = PTR_Class2(addr); +// return cls == cls2; +// will fail because the host-side instances have no relation to each +// other. That isn't a problem, since by rule #1 you shouldn't be +// relying on specific host address values. +// +// Previous Code: +// +// return (ArrayClass *) m_pMethTab->GetClass(); +// +// Modified Code: +// +// return PTR_ArrayClass(m_pMethTab->GetClass()); +// +// The ?PTR templates have an implicit conversion from a host pointer +// to a target address, so the cast above constructs a new +// PTR_ArrayClass by implicitly converting the host pointer result +// from GetClass() to its target address and using that as the address +// of the new PTR_ArrayClass. As mentioned, the actual host-side +// pointer values may not be the same. +// +// Host pointer identity can be assumed as long as the type of access +// is the same. In the example above, if both accesses were of type +// Class then the host pointer will be the same, so it is safe to +// retrieve the target address of an instance and then later get +// a new host pointer for the target address using the same type as +// the host pointer in that case will be the same. This is enabled +// by caching all of the retrieved host instances. This cache is searched +// by the addr:size pair and when there's a match the existing instance +// is reused. This increases performance and also allows simple +// pointer identity to hold. It does mean that host memory grows +// in proportion to the amount of target memory being referenced, +// so retrieving extraneous data should be avoided. +// The host-side data cache grows until the Flush() method is called, +// at which point all host-side data is discarded. No host +// instance pointers should be held across a Flush(). +// +// Accessing into an object can lead to some unusual behavior. For +// example, the SList class relies on objects to contain an SLink +// instance that it uses for list maintenance. This SLink can be +// embedded anywhere in the larger object. The SList access is always +// purely to an SLink, so when using the access layer it will only +// retrieve an SLink's worth of data. The SList template will then +// do some address arithmetic to determine the start of the real +// object and cast the resulting pointer to the final object type. +// When using the access layer this results in a new ?PTR being +// created and used, so a new instance will result. The internal +// SLink instance will have no relation to the new object instance +// even though in target address terms one is embedded in the other. +// The assumption of data stability means that this won't cause +// a problem, but care must be taken with the address arithmetic, +// as layed out in rules #2 and #3. +// +// 4. Global address references cannot be used. Any reference to a +// global piece of code or data, such as a function address, global +// variable or class static variable, must be changed. +// +// The external access code may load at a different base address than +// the target process code. Global addresses are therefore not +// meaningful and must be replaced with something else. There isn't +// a single solution, so replacements must be done on a case-by-case +// basis. +// +// The simplest case is a global or class static variable. All +// declarations must be replaced with a special declaration that +// compiles into a modified accessor template value when compiled for +// external data access. Uses of the variable automatically are fixed +// up by the template instance. Note that assignment to the global +// must be independently ifdef'ed as the external access layer should +// not make any modifications. +// +// Macros allow for simple declaration of a class static and global +// values that compile into an appropriate templated value. +// +// Previous Code: +// +// static RangeSection* m_RangeTree; +// RangeSection* ExecutionManager::m_RangeTree; +// +// extern ThreadStore* g_pThreadStore; +// ThreadStore* g_pThreadStore = &StaticStore; +// class SystemDomain : public BaseDomain { +// ... +// ArrayListStatic m_appDomainIndexList; +// ... +// } +// +// SystemDomain::m_appDomainIndexList; +// +// extern DWORD gThreadTLSIndex; +// +// DWORD gThreadTLSIndex = TLS_OUT_OF_INDEXES; +// +// Modified Code: +// +// typedef DPTR(RangeSection) PTR_RangeSection; +// SPTR_DECL(RangeSection, m_RangeTree); +// SPTR_IMPL(RangeSection, ExecutionManager, m_RangeTree); +// +// typedef DPTR(ThreadStore) PTR_ThreadStore +// GPTR_DECL(ThreadStore, g_pThreadStore); +// GPTR_IMPL_INIT(ThreadStore, g_pThreadStore, &StaticStore); +// +// class SystemDomain : public BaseDomain { +// ... +// SVAL_DECL(ArrayListStatic; m_appDomainIndexList); +// ... +// } +// +// SVAL_IMPL(ArrayListStatic, SystemDomain, m_appDomainIndexList); +// +// GVAL_DECL(DWORD, gThreadTLSIndex); +// +// GVAL_IMPL_INIT(DWORD, gThreadTLSIndex, TLS_OUT_OF_INDEXES); +// +// When declaring the variable, the first argument declares the +// variable's type and the second argument declares the variable's +// name. When defining the variable the arguments are similar, with +// an extra class name parameter for the static class variable case. +// If an initializer is needed the IMPL_INIT macro should be used. +// +// Things get slightly more complicated when declaring an embedded +// array. In this case the data element is not a single element and +// therefore cannot be represented by a ?PTR. In the case of a global +// array, you should use the GARY_DECL and GARY_IMPL macros. +// We durrently have no support for declaring static array data members +// or initialized arrays. Array data members that are dynamically allocated +// need to be treated as pointer members. To reference individual elements +// you must use pointer arithmetic (see rule 2 above). An array declared +// as a local variable within a function does not need to be DACized. +// +// +// All uses of ?VAL_DECL must have a corresponding entry given in the +// DacGlobals structure in src\inc\dacvars.h. For SVAL_DECL the entry +// is class__name. For GVAL_DECL the entry is dac__name. You must add +// these entries in dacvars.h using the DEFINE_DACVAR macro. Note that +// these entries also are used for dumping memory in mini dumps and +// heap dumps. If it's not appropriate to dump a variable, (e.g., +// it's an array or some other value that is not important to have +// in a minidump) a second macro, DEFINE_DACVAR_NO_DUMP, will allow +// you to make the required entry in the DacGlobals structure without +// dumping its value. +// +// For convenience, here is a list of the various variable declaration and +// initialization macros: +// SVAL_DECL(type, name) static non-pointer data class MyClass +// member declared within { +// the class declaration // static int i; +// SVAL_DECL(int, i); +// } +// +// SVAL_IMPL(type, cls, name) static non-pointer data // int MyClass::i; +// member defined outside SVAL_IMPL(int, MyClass, i); +// the class declaration +// +// SVAL_IMPL_INIT(type, cls, static non-pointer data // int MyClass::i = 0; +// name, val) member defined and SVAL_IMPL_INIT(int, MyClass, i, 0); +// initialized outside the +// class declaration +// ------------------------------------------------------------------------------------------------ +// SPTR_DECL(type, name) static pointer data class MyClass +// member declared within { +// the class declaration // static int * pInt; +// SPTR_DECL(int, pInt); +// } +// +// SPTR_IMPL(type, cls, name) static pointer data // int * MyClass::pInt; +// member defined outside SPTR_IMPL(int, MyClass, pInt); +// the class declaration +// +// SPTR_IMPL_INIT(type, cls, static pointer data // int * MyClass::pInt = NULL; +// name, val) member defined and SPTR_IMPL_INIT(int, MyClass, pInt, NULL); +// initialized outside the +// class declaration +// ------------------------------------------------------------------------------------------------ +// GVAL_DECL(type, name) extern declaration of // extern int g_i +// global non-pointer GVAL_DECL(int, g_i); +// variable +// +// GVAL_IMPL(type, name) declaration of a // int g_i +// global non-pointer GVAL_IMPL(int, g_i); +// variable +// +// GVAL_IMPL_INIT (type, declaration and // int g_i = 0; +// name, initialization of a GVAL_IMPL_INIT(int, g_i, 0); +// val) global non-pointer +// variable +// ****Note**** +// If you use GVAL_? to declare a global variable of a structured type and you need to +// access a member of the type, you cannot use the dot operator. Instead, you must take the +// address of the variable and use the arrow operator. For example: +// struct +// { +// int x; +// char ch; +// } MyStruct; +// GVAL_IMPL(MyStruct, g_myStruct); +// int i = (&g_myStruct)->x; +// ------------------------------------------------------------------------------------------------ +// GPTR_DECL(type, name) extern declaration of // extern int * g_pInt +// global pointer GPTR_DECL(int, g_pInt); +// variable +// +// GPTR_IMPL(type, name) declaration of a // int * g_pInt +// global pointer GPTR_IMPL(int, g_pInt); +// variable +// +// GPTR_IMPL_INIT (type, declaration and // int * g_pInt = 0; +// name, initialization of a GPTR_IMPL_INIT(int, g_pInt, NULL); +// val) global pointer +// variable +// ------------------------------------------------------------------------------------------------ +// GARY_DECL(type, name) extern declaration of // extern int g_rgIntList[MAX_ELEMENTS]; +// a global array GPTR_DECL(int, g_rgIntList, MAX_ELEMENTS); +// variable +// +// GARY_IMPL(type, name) declaration of a // int g_rgIntList[MAX_ELEMENTS]; +// global pointer GPTR_IMPL(int, g_rgIntList, MAX_ELEMENTS); +// variable +// +// +// Certain pieces of code, such as the stack walker, rely on identifying +// an object from its vtable address. As the target vtable addresses +// do not necessarily correspond to the vtables used in the host, these +// references must be translated. The access layer maintains translation +// tables for all classes used with VPTR and can return the target +// vtable pointer for any host vtable in the known list of VPTR classes. +// +// ----- Errors: +// +// All errors in the access layer are reported via exceptions. The +// formal access layer methods catch all such exceptions and turn +// them into the appropriate error, so this generally isn't visible +// to users of the access layer. +// +// ----- DPTR Declaration: +// +// Create a typedef for the type with typedef DPTR(type) PTR_type; +// Replace type* with PTR_type. +// +// ----- VPTR Declaration: +// +// VPTR can only be used on classes that have a single vtable +// pointer at the beginning of the object. This should be true +// for a normal single-inheritance object. +// +// All of the classes that may be instantiated need to be identified +// and marked. In the base class declaration add either +// VPTR_BASE_VTABLE_CLASS if the class is abstract or +// VPTR_BASE_CONCRETE_VTABLE_CLASS if the class is concrete. In each +// derived class add VPTR_VTABLE_CLASS. If you end up with compile or +// link errors for an unresolved method called VPtrSize you missed a +// derived class declaration. +// +// As described above, dac can only handle classes with a single +// vtable. However, there's a special case for multiple inheritance +// situations when only one of the classes is needed for dac. If +// the base class needed is the first class in the derived class's +// layout then it can be used with dac via using the VPTR_MULTI_CLASS +// macros. Use with extreme care. +// +// All classes to be instantiated must be listed in src\inc\vptr_list.h. +// +// Create a typedef for the type with typedef VPTR(type) PTR_type; +// When using a VPTR, replace Class* with PTR_Class. +// +// ----- Specific Macros: +// +// PTR_TO_TADDR(ptr) +// Retrieves the raw target address for a ?PTR. +// See code:dac_cast for the preferred alternative +// +// PTR_HOST_TO_TADDR(host) +// Given a host address of an instance produced by a ?PTR reference, +// return the original target address. The host address must +// be an exact match for an instance. +// See code:dac_cast for the preferred alternative +// +// PTR_HOST_INT_TO_TADDR(host) +// Given a host address which resides somewhere within an instance +// produced by a ?PTR reference (a host interior pointer) return the +// corresponding target address. This is useful for evaluating +// relative pointers (e.g. RelativePointer) where calculating the +// target address requires knowledge of the target address of the +// relative pointer field itself. This lookup is slower than that for +// a non-interior host pointer so use it sparingly. +// +// VPTR_HOST_VTABLE_TO_TADDR(host) +// Given the host vtable pointer for a known VPTR class, return +// the target vtable pointer. +// +// PTR_HOST_MEMBER_TADDR(type, host, memb) +// Retrieves the target address of a host instance pointer and +// offsets it by the given member's offset within the type. +// +// PTR_HOST_INT_MEMBER_TADDR(type, host, memb) +// As above but will work for interior host pointers (see the +// description of PTR_HOST_INT_TO_TADDR for an explanation of host +// interior pointers). +// +// PTR_READ(addr, size) +// Reads a block of memory from the target and returns a host +// pointer for it. Useful for reading blocks of data from the target +// whose size is only known at runtime, such as raw code for a jitted +// method. If the data being read is actually an object, use SPTR +// instead to get better type semantics. +// +// DAC_EMPTY() +// DAC_EMPTY_ERR() +// DAC_EMPTY_RET(retVal) +// DAC_UNEXPECTED() +// Provides an empty method implementation when compiled +// for DACCESS_COMPILE. For example, use to stub out methods needed +// for vtable entries but otherwise unused. +// +// These macros are designed to turn into normal code when compiled +// without DACCESS_COMPILE. +// +//***************************************************************************** +// See code:EEStartup#TableOfContents for EE overview + +#ifndef __daccess_h__ +#define __daccess_h__ + +#ifndef __in +#include +#endif + +#define DACCESS_TABLE_RESOURCE L"COREXTERNALDATAACCESSRESOURCE" + +#include "type_traits.hpp" + +#ifdef DACCESS_COMPILE + +#include "safemath.h" + +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) +typedef uint64_t UIntTarget; +#elif defined(TARGET_X86) +typedef uint32_t UIntTarget; +#elif defined(TARGET_ARM) +typedef uint32_t UIntTarget; +#else +#error unexpected target architecture +#endif + +// +// This version of things wraps pointer access in +// templates which understand how to retrieve data +// through an access layer. In this case no assumptions +// can be made that the current compilation processor or +// pointer types match the target's processor or pointer types. +// + +// Define TADDR as a non-pointer value so use of it as a pointer +// will not work properly. Define it as unsigned so +// pointer comparisons aren't affected by sign. +// This requires special casting to ULONG64 to sign-extend if necessary. +// XXX drewb - Cheating right now by not supporting cross-plat. +typedef UIntTarget TADDR; + +// TSIZE_T used for counts or ranges that need to span the size of a +// target pointer. For cross-plat, this may be different than SIZE_T +// which reflects the host pointer size. +typedef UIntTarget TSIZE_T; + +// Information stored in the DAC table of interest to the DAC implementation +// Note that this information is shared between all instantiations of ClrDataAccess, so initialize +// it just once in code:ClrDataAccess.GetDacGlobals (rather than use fields in ClrDataAccess); +struct DacTableInfo +{ + // On Windows, the first DWORD is the 32-bit timestamp read out of the runtime dll's debug directory. + // The remaining 3 DWORDS must all be 0. + // On Mac, this is the 16-byte UUID of the runtime dll. + // It is used to validate that mscorwks is the same version as mscordacwks + uint32_t dwID0; + uint32_t dwID1; + uint32_t dwID2; + uint32_t dwID3; +}; +extern DacTableInfo g_dacTableInfo; + +// +// The following table contains all the global information that data access needs to begin +// operation. All of the values stored here are RVAs. DacGlobalBase() returns the current +// base address to combine with to get a full target address. +// + +typedef struct _DacGlobals +{ +// These will define all of the dac related mscorwks static and global variables +// TODO: update DacTableGen to parse "uint32_t" instead of "ULONG32" for the ids +#ifdef DAC_CLR_ENVIRONMENT +#define DEFINE_DACVAR(id_type, size, id) id_type id; +#define DEFINE_DACVAR_NO_DUMP(id_type, size, id) id_type id; +#else +#define DEFINE_DACVAR(id_type, size, id) uint32_t id; +#define DEFINE_DACVAR_NO_DUMP(id_type, size, id) uint32_t id; +#endif +#include "dacvars.h" +#undef DEFINE_DACVAR_NO_DUMP +#undef DEFINE_DACVAR + +/* + // Global functions. + ULONG fn__QueueUserWorkItemCallback; + ULONG fn__ThreadpoolMgr__AsyncCallbackCompletion; + ULONG fn__ThreadpoolMgr__AsyncTimerCallbackCompletion; + ULONG fn__DACNotifyCompilationFinished; +#ifdef HOST_X86 + ULONG fn__NativeDelayFixupAsmStub; + ULONG fn__NativeDelayFixupAsmStubRet; +#endif // HOST_X86 + ULONG fn__PInvokeCalliReturnFromCall; + ULONG fn__NDirectGenericStubReturnFromCall; + ULONG fn__DllImportForDelegateGenericStubReturnFromCall; +*/ + +} DacGlobals; + +extern DacGlobals g_dacGlobals; + +#ifdef __cplusplus +extern "C" { +#endif + +// These two functions are largely just for marking code +// that is not fully converted. DacWarning prints a debug +// message, while DacNotImpl throws a not-implemented exception. +void __cdecl DacWarning(__in __in_z char* format, ...); +void DacNotImpl(void); +void DacError(HRESULT err); +void __declspec(noreturn) DacError_NoRet(HRESULT err); +TADDR DacGlobalBase(void); +HRESULT DacReadAll(TADDR addr, void* buffer, uint32_t size, bool throwEx); +#ifdef DAC_CLR_ENVIRONMENT +HRESULT DacWriteAll(TADDR addr, PVOID buffer, ULONG32 size, bool throwEx); +HRESULT DacAllocVirtual(TADDR addr, ULONG32 size, + ULONG32 typeFlags, ULONG32 protectFlags, + bool throwEx, TADDR* mem); +HRESULT DacFreeVirtual(TADDR mem, ULONG32 size, ULONG32 typeFlags, + bool throwEx); + +#endif // DAC_CLR_ENVIRONMENT + +/* We are simulating a tiny bit of memory existing in the debuggee address space that really isn't there. + The memory appears to exist in the last 1KB of the memory space to make minimal risk that + it collides with any legitimate debuggee memory. When the DAC uses + DacInstantiateTypeByAddressHelper on these high addresses instead of getting back a pointer + in the DAC_INSTANCE cache it will get back a pointer to specifically configured block of + debugger memory. + + Rationale: + This method was invented to solve a problem when doing stack walking in the DAC. When + running in-process the register context has always been written to memory somewhere before + the stackwalker begins to operate. The stackwalker doesn't track the registers themselves, + but rather the storage locations where registers were written. + When the DAC runs the registers haven't been saved anywhere - there is no memory address + that refers to them. It would be easy to store the registers in the debugger's memory space + but the Regdisplay is typed as PTR_UIntNative, not uintptr_t*. We could change REGDISPLAY + to point at debugger local addresses, but then we would have the opposite problem, being unable + to refer to stack addresses that are in the debuggee memory space. Options we could do: + 1) Add discriminant bits to REGDISPLAY fields to record whether the pointer is local or remote + a) Do it in the runtime definition - adds size and complexity to mrt100 for a debug only scenario + b) Do it only in the DAC definition - breaks marshalling for types that are or contain REGDISPLAY + (ie StackFrameIterator). + 2) Add a new DebuggerREGDISPLAY type that can hold local or remote addresses, and then create + parallel DAC stackwalking code that uses it. This is a bunch of work and + has higher maintenance cost to keep both code paths operational and functionally identical. + 3) Allocate space in debuggee that will be used to stash the registers when doing a debug stackwalk - + increases runtime working set for debug only scenario and won't work for dumps + 4) Same as #3, but don't actually allocate the space at runtime, just simulate that it was allocated + within the debugger - risk of colliding with real runtime allocations, adds complexity to the + DAC. + + #4 seems the best option to me, so we wound up here. +*/ + +// This address is picked to be very unlikely to collide with any real memory usage in the target +#define SIMULATED_DEBUGGEE_MEMORY_BASE_ADDRESS ((TADDR) -1024) +// The byte at ((TADDR)-1) isn't addressable at all, so we only have 1023 bytes of usable space +// At the moment we only need 256 bytes at most. +#define SIMULATED_DEBUGGEE_MEMORY_MAX_SIZE 1023 + +// Sets the simulated debuggee memory region, or clears it if pSimulatedDebuggeeMemory = NULL +// See large comment above for more details. +void SetSimulatedDebuggeeMemory(void* pSimulatedDebuggeeMemory, uint32_t cbSimulatedDebuggeeMemory); + +void* DacInstantiateTypeByAddress(TADDR addr, uint32_t size, bool throwEx); +void* DacInstantiateTypeByAddressNoReport(TADDR addr, uint32_t size, bool throwEx); +void* DacInstantiateClassByVTable(TADDR addr, uint32_t minSize, bool throwEx); + +// This method should not be used casually. Make sure simulatedTargetAddr does not cause collisions. See comment in dacfn.cpp for more details. +void* DacInstantiateTypeAtSimulatedAddress(TADDR simulatedTargetAddr, uint32_t size, void* pLocalBuffer, bool throwEx); + +// Copy a null-terminated ascii or unicode string from the target to the host. +// Note that most of the work here is to find the null terminator. If you know the exact length, +// then you can also just call DacInstantiateTypebyAddress. +char* DacInstantiateStringA(TADDR addr, uint32_t maxChars, bool throwEx); +wchar_t* DacInstantiateStringW(TADDR addr, uint32_t maxChars, bool throwEx); + +TADDR DacGetTargetAddrForHostAddr(const void* ptr, bool throwEx); +TADDR DacGetTargetAddrForHostInteriorAddr(const void* ptr, bool throwEx); +TADDR DacGetTargetVtForHostVt(const void* vtHost, bool throwEx); +wchar_t* DacGetVtNameW(TADDR targetVtable); + +// Report a region of memory to the debugger +void DacEnumMemoryRegion(TADDR addr, TSIZE_T size, bool fExpectSuccess = true); + +HRESULT DacWriteHostInstance(void * host, bool throwEx); + +#ifdef DAC_CLR_ENVIRONMENT + +// Occasionally it's necessary to allocate some host memory for +// instance data that's created on the fly and so doesn't directly +// correspond to target memory. These are held and freed on flush +// like other instances but can't be looked up by address. +PVOID DacAllocHostOnlyInstance(ULONG32 size, bool throwEx); + +// Determines whether ASSERTs should be raised when inconsistencies in the target are detected +bool DacTargetConsistencyAssertsEnabled(); + +// Host instances can be marked as they are enumerated in +// order to break cycles. This function returns true if +// the instance is already marked, otherwise it marks the +// instance and returns false. +bool DacHostPtrHasEnumMark(LPCVOID host); + +// Determines if EnumMemoryRegions has been called on a method descriptor. +// This helps perf for minidumps of apps with large managed stacks. +bool DacHasMethodDescBeenEnumerated(LPCVOID pMD); + +// Sets a flag indicating that EnumMemoryRegions on a method desciptor +// has been successfully called. The function returns true if +// this flag had been previously set. +bool DacSetMethodDescEnumerated(LPCVOID pMD); + +// Determines if a method descriptor is valid +BOOL DacValidateMD(LPCVOID pMD); + +// Enumerate the instructions around a call site to help debugger stack walking heuristics +void DacEnumCodeForStackwalk(TADDR taCallEnd); + +// Given the address and the size of a memory range which is stored in the buffer, replace all the patches +// in the buffer with the real opcodes. This is especially important on X64 where the unwinder needs to +// disassemble the native instructions. +class MemoryRange; +HRESULT DacReplacePatchesInHostMemory(MemoryRange range, PVOID pBuffer); + +// +// Convenience macros for EnumMemoryRegions implementations. +// + +// Enumerate the given host instance and return +// true if the instance hasn't already been enumerated. +#define DacEnumHostDPtrMem(host) \ + (!DacHostPtrHasEnumMark(host) ? \ + (DacEnumMemoryRegion(PTR_HOST_TO_TADDR(host), sizeof(*host)), \ + true) : false) +#define DacEnumHostSPtrMem(host, type) \ + (!DacHostPtrHasEnumMark(host) ? \ + (DacEnumMemoryRegion(PTR_HOST_TO_TADDR(host), \ + type::DacSize(PTR_HOST_TO_TADDR(host))), \ + true) : false) +#define DacEnumHostVPtrMem(host) \ + (!DacHostPtrHasEnumMark(host) ? \ + (DacEnumMemoryRegion(PTR_HOST_TO_TADDR(host), (host)->VPtrSize()), \ + true) : false) + +// Check enumeration of 'this' and return if this has already been +// enumerated. Making this the first line of an object's EnumMemoryRegions +// method will prevent cycles. +#define DAC_CHECK_ENUM_THIS() \ + if (DacHostPtrHasEnumMark(this)) return +#define DAC_ENUM_DTHIS() \ + if (!DacEnumHostDPtrMem(this)) return +#define DAC_ENUM_STHIS(type) \ + if (!DacEnumHostSPtrMem(this, type)) return +#define DAC_ENUM_VTHIS() \ + if (!DacEnumHostVPtrMem(this)) return + +#endif // DAC_CLR_ENVIRONMENT + +#ifdef __cplusplus +} + +// +// Computes (taBase + (dwIndex * dwElementSize()), with overflow checks. +// +// Arguments: +// taBase the base TADDR value +// dwIndex the index of the offset +// dwElementSize the size of each element (to multiply the offset by) +// +// Return value: +// The resulting TADDR, or throws CORDB_E_TARGET_INCONSISTENT on overlow. +// +// Notes: +// The idea here is that overflows during address arithmetic suggest that we're operating on corrupt +// pointers. It helps to improve reliability to detect the cases we can (like overflow) and fail. Note +// that this is just a heuristic, not a security measure. We can't trust target data regardless - +// failing on overflow is just one easy case of corruption to detect. There is no need to use checked +// arithmetic everywhere in the DAC infrastructure, this is intended just for the places most likely to +// help catch bugs (eg. __DPtr::operator[]). +// +inline TADDR DacTAddrOffset( TADDR taBase, TSIZE_T dwIndex, TSIZE_T dwElementSize ) +{ +#ifdef DAC_CLR_ENVIRONMENT + ClrSafeInt t(taBase); + t += ClrSafeInt(dwIndex) * ClrSafeInt(dwElementSize); + if( t.IsOverflow() ) + { + // Pointer arithmetic overflow - probably due to corrupt target data + //DacError(CORDBG_E_TARGET_INCONSISTENT); + DacError(E_FAIL); + } + return t.Value(); +#else // TODO: port safe math + return taBase + (dwIndex*dwElementSize); +#endif +} + +// Base pointer wrapper which provides common behavior. +class __TPtrBase +{ +public: + __TPtrBase() + { + // Make uninitialized pointers obvious. + m_addr = (TADDR)-1; + } + explicit __TPtrBase(TADDR addr) + { + m_addr = addr; + } + + bool operator!() const + { + return m_addr == 0; + } + // We'd like to have an implicit conversion to bool here since the C++ + // standard says all pointer types are implicitly converted to bool. + // Unfortunately, that would cause ambiguous overload errors for uses + // of operator== and operator!=. Instead callers will have to compare + // directly against NULL. + + bool operator==(TADDR addr) const + { + return m_addr == addr; + } + bool operator!=(TADDR addr) const + { + return m_addr != addr; + } + bool operator<(TADDR addr) const + { + return m_addr < addr; + } + bool operator>(TADDR addr) const + { + return m_addr > addr; + } + bool operator<=(TADDR addr) const + { + return m_addr <= addr; + } + bool operator>=(TADDR addr) const + { + return m_addr >= addr; + } + + TADDR GetAddr(void) const + { + return m_addr; + } + TADDR SetAddr(TADDR addr) + { + m_addr = addr; + return addr; + } + +protected: + TADDR m_addr; +}; + +// Adds comparison operations +// Its possible we just want to merge these into __TPtrBase, but SPtr isn't comparable with +// other types right now and I would rather stay conservative +class __ComparableTPtrBase : public __TPtrBase +{ +protected: + __ComparableTPtrBase(void) : __TPtrBase() + {} + + explicit __ComparableTPtrBase(TADDR addr) : __TPtrBase(addr) + {} + +public: + bool operator==(const __ComparableTPtrBase& ptr) const + { + return m_addr == ptr.m_addr; + } + bool operator!=(const __ComparableTPtrBase& ptr) const + { + return !operator==(ptr); + } + bool operator<(const __ComparableTPtrBase& ptr) const + { + return m_addr < ptr.m_addr; + } + bool operator>(const __ComparableTPtrBase& ptr) const + { + return m_addr > ptr.m_addr; + } + bool operator<=(const __ComparableTPtrBase& ptr) const + { + return m_addr <= ptr.m_addr; + } + bool operator>=(const __ComparableTPtrBase& ptr) const + { + return m_addr >= ptr.m_addr; + } +}; + +// Pointer wrapper base class for various forms of normal data. +// This has the common functionality between __DPtr and __ArrayDPtr. +// The DPtrType type parameter is the actual derived type in use. This is necessary so that +// inhereted functions preserve exact return types. +template +class __DPtrBase : public __ComparableTPtrBase +{ +public: + typedef type _Type; + typedef type* _Ptr; + +protected: + // Constructors + // All protected - this type should not be used directly - use one of the derived types instead. + __DPtrBase< type, DPtrType >(void) : __ComparableTPtrBase() + {} + + explicit __DPtrBase< type, DPtrType >(TADDR addr) : __ComparableTPtrBase(addr) + {} + + explicit __DPtrBase(__TPtrBase addr) + { + m_addr = addr.GetAddr(); + } + explicit __DPtrBase(type const * host) + { + m_addr = DacGetTargetAddrForHostAddr(host, true); + } + +public: + DPtrType& operator=(const __TPtrBase& ptr) + { + m_addr = ptr.GetAddr(); + return DPtrType(m_addr); + } + DPtrType& operator=(TADDR addr) + { + m_addr = addr; + return DPtrType(m_addr); + } + + type& operator*(void) const + { + return *(type*)DacInstantiateTypeByAddress(m_addr, sizeof(type), true); + } + + + using __ComparableTPtrBase::operator==; + using __ComparableTPtrBase::operator!=; + using __ComparableTPtrBase::operator<; + using __ComparableTPtrBase::operator>; + using __ComparableTPtrBase::operator<=; + using __ComparableTPtrBase::operator>=; + bool operator==(TADDR addr) const + { + return m_addr == addr; + } + bool operator!=(TADDR addr) const + { + return m_addr != addr; + } + + // Array index operator + // we want an operator[] for all possible numeric types (rather than rely on + // implicit numeric conversions on the argument) to prevent ambiguity with + // DPtr's implicit conversion to type* and the built-in operator[]. + // @dbgtodo rbyers: we could also use this technique to simplify other operators below. + template + type& operator[](indexType index) + { + // Compute the address of the element. + TADDR elementAddr; + if( index >= 0 ) + { + elementAddr = DacTAddrOffset(m_addr, index, sizeof(type)); + } + else + { + // Don't bother trying to do overflow checking for negative indexes - they are rare compared to + // positive ones. ClrSafeInt doesn't support signed datatypes yet (although we should be able to add it + // pretty easily). + elementAddr = m_addr + index * sizeof(type); + } + + // Marshal over a single instance and return a reference to it. + return *(type*) DacInstantiateTypeByAddress(elementAddr, sizeof(type), true); + } + + template + type const & operator[](indexType index) const + { + return (*const_cast<__DPtrBase*>(this))[index]; + } + + //------------------------------------------------------------------------- + // operator+ + + DPtrType operator+(unsigned short val) + { + return DPtrType(DacTAddrOffset(m_addr, val, sizeof(type))); + } + DPtrType operator+(short val) + { + return DPtrType(m_addr + val * sizeof(type)); + } + // size_t is unsigned int on Win32, so we need + // to ifdef here to make sure the unsigned int + // and size_t overloads don't collide. size_t + // is marked __w64 so a simple unsigned int + // will not work on Win32, it has to be size_t. + DPtrType operator+(size_t val) + { + return DPtrType(DacTAddrOffset(m_addr, val, sizeof(type))); + } +#if (!defined (HOST_X86) && !defined(_SPARC_) && !defined(HOST_ARM)) || (defined(HOST_X86) && defined(__APPLE__)) + DPtrType operator+(unsigned int val) + { + return DPtrType(DacTAddrOffset(m_addr, val, sizeof(type))); + } +#endif // (!defined (HOST_X86) && !defined(_SPARC_) && !defined(HOST_ARM)) || (defined(HOST_X86) && defined(__APPLE__)) + DPtrType operator+(int val) + { + return DPtrType(m_addr + val * sizeof(type)); + } +#ifndef TARGET_UNIX // for now, everything else is 32 bit + DPtrType operator+(unsigned long val) + { + return DPtrType(DacTAddrOffset(m_addr, val, sizeof(type))); + } + DPtrType operator+(long val) + { + return DPtrType(m_addr + val * sizeof(type)); + } +#endif // !TARGET_UNIX // for now, everything else is 32 bit +#if !defined(HOST_ARM) && !defined(HOST_X86) + DPtrType operator+(intptr_t val) + { + return DPtrType(m_addr + val * sizeof(type)); + } +#endif + + //------------------------------------------------------------------------- + // operator- + + DPtrType operator-(unsigned short val) + { + return DPtrType(m_addr - val * sizeof(type)); + } + DPtrType operator-(short val) + { + return DPtrType(m_addr - val * sizeof(type)); + } + // size_t is unsigned int on Win32, so we need + // to ifdef here to make sure the unsigned int + // and size_t overloads don't collide. size_t + // is marked __w64 so a simple unsigned int + // will not work on Win32, it has to be size_t. + DPtrType operator-(size_t val) + { + return DPtrType(m_addr - val * sizeof(type)); + } + DPtrType operator-(signed __int64 val) + { + return DPtrType(m_addr - val * sizeof(type)); + } +#if !defined (HOST_X86) && !defined(_SPARC_) && !defined(HOST_ARM) + DPtrType operator-(unsigned int val) + { + return DPtrType(m_addr - val * sizeof(type)); + } +#endif // !defined (HOST_X86) && !defined(_SPARC_) && !defined(HOST_ARM) + DPtrType operator-(int val) + { + return DPtrType(m_addr - val * sizeof(type)); + } +#ifdef _MSC_VER // for now, everything else is 32 bit + DPtrType operator-(unsigned long val) + { + return DPtrType(m_addr - val * sizeof(type)); + } + DPtrType operator-(long val) + { + return DPtrType(m_addr - val * sizeof(type)); + } +#endif // _MSC_VER // for now, everything else is 32 bit + size_t operator-(const DPtrType& val) + { + return (size_t)((m_addr - val.m_addr) / sizeof(type)); + } + + //------------------------------------------------------------------------- + + DPtrType& operator+=(size_t val) + { + m_addr += val * sizeof(type); + return static_cast(*this); + } + DPtrType& operator-=(size_t val) + { + m_addr -= val * sizeof(type); + return static_cast(*this); + } + + DPtrType& operator++() + { + m_addr += sizeof(type); + return static_cast(*this); + } + DPtrType& operator--() + { + m_addr -= sizeof(type); + return static_cast(*this); + } + DPtrType operator++(int postfix) + { + UNREFERENCED_PARAMETER(postfix); + DPtrType orig = DPtrType(*this); + m_addr += sizeof(type); + return orig; + } + DPtrType operator--(int postfix) + { + UNREFERENCED_PARAMETER(postfix); + DPtrType orig = DPtrType(*this); + m_addr -= sizeof(type); + return orig; + } + + bool IsValid(void) const + { + return m_addr && + DacInstantiateTypeByAddress(m_addr, sizeof(type), + false) != NULL; + } + void EnumMem(void) const + { + DacEnumMemoryRegion(m_addr, sizeof(type)); + } +}; + +// Pointer wrapper for objects which are just plain data +// and need no special handling. +template +class __DPtr : public __DPtrBase > +{ +#ifdef __GNUC__ +protected: + //there seems to be a bug in GCC's inference logic. It can't find m_addr. + using __DPtrBase >::m_addr; +#endif // __GNUC__ +public: + // constructors - all chain to __DPtrBase constructors + __DPtr< type >(void) : __DPtrBase >() {} + __DPtr< type >(TADDR addr) : __DPtrBase >(addr) {} + + // construct const from non-const + typedef typename type_traits::remove_const::type mutable_type; + __DPtr< type >(__DPtr const & rhs) : __DPtrBase >(rhs.GetAddr()) {} + + explicit __DPtr< type >(__TPtrBase addr) : __DPtrBase >(addr) {} + explicit __DPtr< type >(type const * host) : __DPtrBase >(host) {} + + operator type*() const + { + return (type*)DacInstantiateTypeByAddress(m_addr, sizeof(type), true); + } + type* operator->() const + { + return (type*)DacInstantiateTypeByAddress(m_addr, sizeof(type), true); + } +}; + +#define DPTR(type) __DPtr< type > + +// A restricted form of DPtr that doesn't have any conversions to pointer types. +// This is useful for pointer types that almost always represent arrays, as opposed +// to pointers to single instances (eg. PTR_BYTE). In these cases, allowing implicit +// conversions to (for eg.) BYTE* would usually result in incorrect usage (eg. pointer +// arithmetic and array indexing), since only a single instance has been marshalled to the host. +// If you really must marshal a single instance (eg. converting T* to PTR_T is too painful for now), +// then use code:DacUnsafeMarshalSingleElement so we can identify such unsafe code. +template +class __ArrayDPtr : public __DPtrBase > +{ +public: + // constructors - all chain to __DPtrBase constructors + __ArrayDPtr< type >(void) : __DPtrBase >() {} + __ArrayDPtr< type >(TADDR addr) : __DPtrBase >(addr) {} + + // construct const from non-const + typedef typename type_traits::remove_const::type mutable_type; + __ArrayDPtr< type >(__ArrayDPtr const & rhs) : __DPtrBase >(rhs.GetAddr()) {} + + explicit __ArrayDPtr< type >(__TPtrBase addr) : __DPtrBase >(addr) {} + + // Note that there is also no explicit constructor from host instances (type*). + // Going this direction is less problematic, but often still represents risky coding. +}; + +#define ArrayDPTR(type) __ArrayDPtr< type > + + +// Pointer wrapper for objects which are just plain data +// but whose size is not the same as the base type size. +// This can be used for prefetching data for arrays or +// for cases where an object has a variable size. +template +class __SPtr : public __TPtrBase +{ +public: + typedef type _Type; + typedef type* _Ptr; + + __SPtr< type >(void) : __TPtrBase() {} + __SPtr< type >(TADDR addr) : __TPtrBase(addr) {} + explicit __SPtr< type >(__TPtrBase addr) + { + m_addr = addr.GetAddr(); + } + explicit __SPtr< type >(type* host) + { + m_addr = DacGetTargetAddrForHostAddr(host, true); + } + + __SPtr< type >& operator=(const __TPtrBase& ptr) + { + m_addr = ptr.m_addr; + return *this; + } + __SPtr< type >& operator=(TADDR addr) + { + m_addr = addr; + return *this; + } + + operator type*() const + { + if (m_addr) + { + return (type*)DacInstantiateTypeByAddress(m_addr, + type::DacSize(m_addr), + true); + } + else + { + return (type*)NULL; + } + } + type* operator->() const + { + if (m_addr) + { + return (type*)DacInstantiateTypeByAddress(m_addr, + type::DacSize(m_addr), + true); + } + else + { + return (type*)NULL; + } + } + type& operator*(void) const + { + if (!m_addr) + { + DacError(E_INVALIDARG); + } + + return *(type*)DacInstantiateTypeByAddress(m_addr, + type::DacSize(m_addr), + true); + } + + bool IsValid(void) const + { + return m_addr && + DacInstantiateTypeByAddress(m_addr, type::DacSize(m_addr), + false) != NULL; + } + void EnumMem(void) const + { + if (m_addr) + { + DacEnumMemoryRegion(m_addr, type::DacSize(m_addr)); + } + } +}; + +#define SPTR(type) __SPtr< type > + +// Pointer wrapper for objects which have a single leading +// vtable, such as objects in a single-inheritance tree. +// The base class of all such trees must have use +// VPTR_BASE_VTABLE_CLASS in their declaration and all +// instantiable members of the tree must be listed in vptr_list.h. +template +class __VPtr : public __TPtrBase +{ +public: + // VPtr::_Type has to be a pointer as + // often the type is an abstract class. + // This type is not expected to be used anyway. + typedef type* _Type; + typedef type* _Ptr; + + __VPtr< type >(void) : __TPtrBase() {} + __VPtr< type >(TADDR addr) : __TPtrBase(addr) {} + explicit __VPtr< type >(__TPtrBase addr) + { + m_addr = addr.GetAddr(); + } + explicit __VPtr< type >(type* host) + { + m_addr = DacGetTargetAddrForHostAddr(host, true); + } + + __VPtr< type >& operator=(const __TPtrBase& ptr) + { + m_addr = ptr.m_addr; + return *this; + } + __VPtr< type >& operator=(TADDR addr) + { + m_addr = addr; + return *this; + } + + operator type*() const + { + return (type*)DacInstantiateClassByVTable(m_addr, sizeof(type), true); + } + type* operator->() const + { + return (type*)DacInstantiateClassByVTable(m_addr, sizeof(type), true); + } + + bool operator==(const __VPtr< type >& ptr) const + { + return m_addr == ptr.m_addr; + } + bool operator==(TADDR addr) const + { + return m_addr == addr; + } + bool operator!=(const __VPtr< type >& ptr) const + { + return !operator==(ptr); + } + bool operator!=(TADDR addr) const + { + return m_addr != addr; + } + + bool IsValid(void) const + { + return m_addr && + DacInstantiateClassByVTable(m_addr, sizeof(type), false) != NULL; + } + void EnumMem(void) const + { + if (IsValid()) + { + DacEnumMemoryRegion(m_addr, (operator->())->VPtrSize()); + } + } +}; + +#define VPTR(type) __VPtr< type > + +// Pointer wrapper for 8-bit strings. +#ifdef DAC_CLR_ENVIRONMENT +template +#else +template +#endif +class __Str8Ptr : public __DPtr +{ +public: + typedef type _Type; + typedef type* _Ptr; + + __Str8Ptr< type, maxChars >(void) : __DPtr() {} + __Str8Ptr< type, maxChars >(TADDR addr) : __DPtr(addr) {} + explicit __Str8Ptr< type, maxChars >(__TPtrBase addr) + { + m_addr = addr.GetAddr(); + } + explicit __Str8Ptr< type, maxChars >(type* host) + { + m_addr = DacGetTargetAddrForHostAddr(host, true); + } + + __Str8Ptr< type, maxChars >& operator=(const __TPtrBase& ptr) + { + m_addr = ptr.m_addr; + return *this; + } + __Str8Ptr< type, maxChars >& operator=(TADDR addr) + { + m_addr = addr; + return *this; + } + + operator type*() const + { + return (type*)DacInstantiateStringA(m_addr, maxChars, true); + } + + bool IsValid(void) const + { + return m_addr && + DacInstantiateStringA(m_addr, maxChars, false) != NULL; + } + void EnumMem(void) const + { + char* str = DacInstantiateStringA(m_addr, maxChars, false); + if (str) + { + DacEnumMemoryRegion(m_addr, strlen(str) + 1); + } + } +}; + +#define S8PTR(type) __Str8Ptr< type > +#define S8PTRMAX(type, maxChars) __Str8Ptr< type, maxChars > + +// Pointer wrapper for 16-bit strings. +#ifdef DAC_CLR_ENVIRONMENT +template +#else +template +#endif +class __Str16Ptr : public __DPtr +{ +public: + typedef type _Type; + typedef type* _Ptr; + + __Str16Ptr< type, maxChars >(void) : __DPtr() {} + __Str16Ptr< type, maxChars >(TADDR addr) : __DPtr(addr) {} + explicit __Str16Ptr< type, maxChars >(__TPtrBase addr) + { + m_addr = addr.GetAddr(); + } + explicit __Str16Ptr< type, maxChars >(type* host) + { + m_addr = DacGetTargetAddrForHostAddr(host, true); + } + + __Str16Ptr< type, maxChars >& operator=(const __TPtrBase& ptr) + { + m_addr = ptr.m_addr; + return *this; + } + __Str16Ptr< type, maxChars >& operator=(TADDR addr) + { + m_addr = addr; + return *this; + } + + operator type*() const + { + return (type*)DacInstantiateStringW(m_addr, maxChars, true); + } + + bool IsValid(void) const + { + return m_addr && + DacInstantiateStringW(m_addr, maxChars, false) != NULL; + } + void EnumMem(void) const + { + char* str = DacInstantiateStringW(m_addr, maxChars, false); + if (str) + { + DacEnumMemoryRegion(m_addr, strlen(str) + 1); + } + } +}; + +#define S16PTR(type) __Str16Ptr< type > +#define S16PTRMAX(type, maxChars) __Str16Ptr< type, maxChars > + +template +class __GlobalVal +{ +public: +#ifdef DAC_CLR_ENVIRONMENT + __GlobalVal< type >(PULONG rvaPtr) +#else + __GlobalVal< type >(uint32_t* rvaPtr) +#endif + { + m_rvaPtr = rvaPtr; + } + + operator type() const + { + return (type)*__DPtr< type >(DacGlobalBase() + *m_rvaPtr); + } + + __DPtr< type > operator&() const + { + return __DPtr< type >(DacGlobalBase() + *m_rvaPtr); + } + + // @dbgtodo rbyers dac support: This updates values in the host. This seems extremely dangerous + // to do silently. I'd prefer that a specific (searchable) write function + // was used. Try disabling this and see what fails... + type & operator=(type & val) + { + type* ptr = __DPtr< type >(DacGlobalBase() + *m_rvaPtr); + // Update the host copy; + *ptr = val; + // Write back to the target. + DacWriteHostInstance(ptr, true); + return val; + } + + bool IsValid(void) const + { + return __DPtr< type >(DacGlobalBase() + *m_rvaPtr).IsValid(); + } + void EnumMem(void) const + { + TADDR p = DacGlobalBase() + *m_rvaPtr; + __DPtr< type >(p).EnumMem(); + } + +private: +#ifdef DAC_CLR_ENVIRONMENT + PULONG m_rvaPtr; +#else + uint32_t* m_rvaPtr; +#endif +}; + +template +class __GlobalArray +{ +public: +#ifdef DAC_CLR_ENVIRONMENT + __GlobalArray< type, size >(PULONG rvaPtr) +#else + __GlobalArray< type, size >(uint32_t* rvaPtr) +#endif + { + m_rvaPtr = rvaPtr; + } + + __DPtr< type > operator&() const + { + return __DPtr< type >(DacGlobalBase() + *m_rvaPtr); + } + + type& operator[](unsigned int index) const + { + return __DPtr< type >(DacGlobalBase() + *m_rvaPtr)[index]; + } + + bool IsValid(void) const + { + // Only validates the base pointer, not the full array range. + return __DPtr< type >(DacGlobalBase() + *m_rvaPtr).IsValid(); + } + void EnumMem(void) const + { + DacEnumMemoryRegion(DacGlobalBase() + *m_rvaPtr, sizeof(type) * size); + } + +private: +#ifdef DAC_CLR_ENVIRONMENT + PULONG m_rvaPtr; +#else + uint32_t* m_rvaPtr; +#endif +}; + +template +class __GlobalPtr +{ +public: +#ifdef DAC_CLR_ENVIRONMENT + __GlobalPtr< acc_type, store_type >(PULONG rvaPtr) +#else + __GlobalPtr< acc_type, store_type >(uint32_t* rvaPtr) +#endif + { + m_rvaPtr = rvaPtr; + } + + __DPtr< store_type > operator&() const + { + return __DPtr< store_type >(DacGlobalBase() + *m_rvaPtr); + } + + store_type & operator=(store_type & val) + { + store_type* ptr = __DPtr< store_type >(DacGlobalBase() + *m_rvaPtr); + // Update the host copy; + *ptr = val; + // Write back to the target. + DacWriteHostInstance(ptr, true); + return val; + } + + acc_type operator->() const + { + return (acc_type)*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr); + } + operator acc_type() const + { + return (acc_type)*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr); + } + operator store_type() const + { + return *__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr); + } + bool operator!() const + { + return !*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr); + } + + typename store_type operator[](int index) const + { + return (*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr))[index]; + } + + typename store_type operator[](unsigned int index) const + { + return (*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr))[index]; + } + + TADDR GetAddr() const + { + return (*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr)).GetAddr(); + } + + TADDR GetAddrRaw () const + { + return DacGlobalBase() + *m_rvaPtr; + } + + // This is only testing the the pointer memory is available but does not verify + // the memory that it points to. + // + bool IsValidPtr(void) const + { + return __DPtr< store_type >(DacGlobalBase() + *m_rvaPtr).IsValid(); + } + + bool IsValid(void) const + { + return __DPtr< store_type >(DacGlobalBase() + *m_rvaPtr).IsValid() && + (*__DPtr< store_type >(DacGlobalBase() + *m_rvaPtr)).IsValid(); + } + void EnumMem(void) const + { + __DPtr< store_type > ptr(DacGlobalBase() + *m_rvaPtr); + ptr.EnumMem(); + if (ptr.IsValid()) + { + (*ptr).EnumMem(); + } + } + +#ifdef DAC_CLR_ENVIRONMENT + PULONG m_rvaPtr; +#else + uint32_t* m_rvaPtr; +#endif +}; + +template +inline bool operator==(const __GlobalPtr& gptr, + acc_type host) +{ + return DacGetTargetAddrForHostAddr(host, true) == + *__DPtr< TADDR >(DacGlobalBase() + *gptr.m_rvaPtr); +} +template +inline bool operator!=(const __GlobalPtr& gptr, + acc_type host) +{ + return !operator==(gptr, host); +} + +template +inline bool operator==(acc_type host, + const __GlobalPtr& gptr) +{ + return DacGetTargetAddrForHostAddr(host, true) == + *__DPtr< TADDR >(DacGlobalBase() + *gptr.m_rvaPtr); +} +template +inline bool operator!=(acc_type host, + const __GlobalPtr& gptr) +{ + return !operator==(host, gptr); +} + + +// +// __VoidPtr is a type that behaves like void* but for target pointers. +// Behavior of PTR_VOID: +// * has void* semantics. Will compile to void* in non-DAC builds (just like +// other PTR types. Unlike TADDR, we want pointer semantics. +// * NOT assignable from host pointer types or convertible to host pointer +// types - ensures we can't confuse host and target pointers (we'll get +// compiler errors if we try and cast between them). +// * like void*, no pointer arithmetic or dereferencing is allowed +// * like TADDR, can be used to construct any __DPtr / __VPtr instance +// * representation is the same as a void* (for marshalling / casting) +// +// One way in which __VoidPtr is unlike void* is that it can't be cast to +// pointer or integer types. On the one hand, this is a good thing as it forces +// us to keep target pointers separate from other data types. On the other hand +// in practice this means we have to use dac_cast in places where we used +// to use a (TADDR) cast. Unfortunately C++ provides us no way to allow the +// explicit cast to primitive types without also allowing implicit conversions. +// +// This is very similar in spirit to TADDR. The primary difference is that +// PTR_VOID has pointer semantics, where TADDR has integer semantics. When +// dacizing uses of void* to TADDR, casts must be inserted everywhere back to +// pointer types. If we switch a use of TADDR to PTR_VOID, those casts in +// DACCESS_COMPILE regions no longer compile (see above). Also, TADDR supports +// pointer arithmetic, but that might not be necessary (could use PTR_BYTE +// instead etc.). Ideally we'd probably have just one type for this purpose +// (named TADDR but with the semantics of PTR_VOID), but outright conversion +// would require too much work. +// + +template <> +class __DPtr : public __ComparableTPtrBase +{ +public: + __DPtr(void) : __ComparableTPtrBase() {} + __DPtr(TADDR addr) : __ComparableTPtrBase(addr) {} + + // Note, unlike __DPtr, this ctor form is not explicit. We allow implicit + // conversions from any pointer type (just like for void*). + __DPtr(__TPtrBase addr) + { + m_addr = addr.GetAddr(); + } + + // Like TPtrBase, VoidPtrs can also be created impicitly from all GlobalPtrs + template + __DPtr(__GlobalPtr globalPtr) + { + m_addr = globalPtr.GetAddr(); + } + + // Note, unlike __DPtr, there is no explicit conversion from host pointer + // types. Since void* cannot be marshalled, there is no such thing as + // a void* DAC instance in the host. + + // Also, we don't want an implicit conversion to TADDR because then the + // compiler will allow pointer arithmetic (which it wouldn't allow for + // void*). Instead, callers can use dac_cast if they want. + + // Note, unlike __DPtr, any pointer type can be assigned to a __DPtr + // This is to mirror the assignability of any pointer type to a void* + __DPtr& operator=(const __TPtrBase& ptr) + { + m_addr = ptr.GetAddr(); + return *this; + } + __DPtr& operator=(TADDR addr) + { + m_addr = addr; + return *this; + } + + // note, no marshalling operators (type* conversion, operator ->, operator*) + // A void* can't be marshalled because we don't know how much to copy + + // PTR_Void can be compared to any other pointer type (because conceptually, + // any other pointer type should be implicitly convertible to void*) + using __ComparableTPtrBase::operator==; + using __ComparableTPtrBase::operator!=; + using __ComparableTPtrBase::operator<; + using __ComparableTPtrBase::operator>; + using __ComparableTPtrBase::operator<=; + using __ComparableTPtrBase::operator>=; + bool operator==(TADDR addr) const + { + return m_addr == addr; + } + bool operator!=(TADDR addr) const + { + return m_addr != addr; + } +}; + +typedef __DPtr __VoidPtr; +typedef __VoidPtr PTR_VOID; +typedef DPTR(PTR_VOID) PTR_PTR_VOID; + +// For now we treat pointers to const and non-const void the same in DAC +// builds. In general, DAC is read-only anyway and so there isn't a danger of +// writing to these pointers. Also, the non-dac builds will ensure +// const-correctness. However, if we wanted to support true void* / const void* +// behavior, we could probably build the follow functionality by templating +// __VoidPtr: +// * A PTR_VOID would be implicitly convertable to PTR_CVOID +// * An explicit coercion (ideally const_cast) would be required to convert a +// PTR_CVOID to a PTR_VOID +// * Similarily, an explicit coercion would be required to convert a cost PTR +// type (eg. PTR_CBYTE) to a PTR_VOID. +typedef __VoidPtr PTR_CVOID; + + +// The special empty ctor declared here allows the whole +// class hierarchy to be instantiated easily by the +// external access code. The actual class body will be +// read externally so no members should be initialized. + +// Safe access for retrieving the target address of a PTR. +#define PTR_TO_TADDR(ptr) ((ptr).GetAddr()) + +#define GFN_TADDR(name) (DacGlobalBase() + g_dacGlobals.fn__ ## name) + +// ROTORTODO - g++ 3 doesn't like the use of the operator& in __GlobalVal +// here. Putting GVAL_ADDR in to get things to compile while I discuss +// this matter with the g++ authors. + +#define GVAL_ADDR(g) \ + ((g).operator&()) + +// +// References to class static and global data. +// These all need to be redirected through the global +// data table. +// + +#define _SPTR_DECL(acc_type, store_type, var) \ + static __GlobalPtr< acc_type, store_type > var +#define _SPTR_IMPL(acc_type, store_type, cls, var) \ + __GlobalPtr< acc_type, store_type > cls::var(&g_dacGlobals.cls##__##var) +#define _SPTR_IMPL_INIT(acc_type, store_type, cls, var, init) \ + __GlobalPtr< acc_type, store_type > cls::var(&g_dacGlobals.cls##__##var) +#define _SPTR_IMPL_NS(acc_type, store_type, ns, cls, var) \ + __GlobalPtr< acc_type, store_type > cls::var(&g_dacGlobals.ns##__##cls##__##var) +#define _SPTR_IMPL_NS_INIT(acc_type, store_type, ns, cls, var, init) \ + __GlobalPtr< acc_type, store_type > cls::var(&g_dacGlobals.ns##__##cls##__##var) + +#define _GPTR_DECL(acc_type, store_type, var) \ + extern __GlobalPtr< acc_type, store_type > var +#define _GPTR_IMPL(acc_type, store_type, var) \ + __GlobalPtr< acc_type, store_type > var(&g_dacGlobals.dac__##var) +#define _GPTR_IMPL_INIT(acc_type, store_type, var, init) \ + __GlobalPtr< acc_type, store_type > var(&g_dacGlobals.dac__##var) + +#define SVAL_DECL(type, var) \ + static __GlobalVal< type > var +#define SVAL_IMPL(type, cls, var) \ + __GlobalVal< type > cls::var(&g_dacGlobals.cls##__##var) +#define SVAL_IMPL_INIT(type, cls, var, init) \ + __GlobalVal< type > cls::var(&g_dacGlobals.cls##__##var) +#define SVAL_IMPL_NS(type, ns, cls, var) \ + __GlobalVal< type > cls::var(&g_dacGlobals.ns##__##cls##__##var) +#define SVAL_IMPL_NS_INIT(type, ns, cls, var, init) \ + __GlobalVal< type > cls::var(&g_dacGlobals.ns##__##cls##__##var) + +#define GVAL_DECL(type, var) \ + extern __GlobalVal< type > var +#define GVAL_IMPL(type, var) \ + __GlobalVal< type > var(&g_dacGlobals.dac__##var) +#define GVAL_IMPL_INIT(type, var, init) \ + __GlobalVal< type > var(&g_dacGlobals.dac__##var) + +#define GARY_DECL(type, var, size) \ + extern __GlobalArray< type, size > var +#define GARY_IMPL(type, var, size) \ + __GlobalArray< type, size > var(&g_dacGlobals.dac__##var) + +// Translation from a host pointer back to the target address +// that was used to retrieve the data for the host pointer. +#define PTR_HOST_TO_TADDR(host) DacGetTargetAddrForHostAddr(host, true) + +// Translation from a host interior pointer back to the corresponding +// target address. The host address must reside within a previously +// retrieved instance. +#define PTR_HOST_INT_TO_TADDR(host) DacGetTargetAddrForHostInteriorAddr(host, true) + +// Construct a pointer to a member of the given type. +#define PTR_HOST_MEMBER_TADDR(type, host, memb) \ + (PTR_HOST_TO_TADDR(host) + (TADDR)offsetof(type, memb)) + +// in the DAC build this is still typed TADDR, but in the runtime +// build it preserves the member type. +#define PTR_HOST_MEMBER(type, host, memb) \ + (PTR_HOST_TO_TADDR(host) + (TADDR)offsetof(type, memb)) + +// Construct a pointer to a member of the given type given an interior +// host address. +#define PTR_HOST_INT_MEMBER_TADDR(type, host, memb) \ + (PTR_HOST_INT_TO_TADDR(host) + (TADDR)offsetof(type, memb)) + +#define PTR_TO_MEMBER_TADDR(type, ptr, memb) \ + (PTR_TO_TADDR(ptr) + (TADDR)offsetof(type, memb)) + +// in the DAC build this is still typed TADDR, but in the runtime +// build it preserves the member type. +#define PTR_TO_MEMBER(type, ptr, memb) \ + (PTR_TO_TADDR(ptr) + (TADDR)offsetof(type, memb)) + +// Constructs an arbitrary data instance for a piece of +// memory in the target. +#define PTR_READ(addr, size) \ + DacInstantiateTypeByAddress(addr, size, true) + +// This value is used to initialize target pointers to NULL. We want this to be TADDR type +// (as opposed to, say, __TPtrBase) so that it can be used in the non-explicit ctor overloads, +// eg. as an argument default value. +// We can't always just use NULL because that's 0 which (in C++) can be any integer or pointer +// type (causing an ambiguous overload compiler error when used in explicit ctor forms). +#define PTR_NULL ((TADDR)0) + +// Provides an empty method implementation when compiled +// for DACCESS_COMPILE. For example, use to stub out methods needed +// for vtable entries but otherwise unused. +// Note that these functions are explicitly NOT marked SUPPORTS_DAC so that we'll get a +// DacCop warning if any calls to them are detected. +// @dbgtodo rbyers: It's probably almost always wrong to call any such function, so +// we should probably throw a better error (DacNotImpl), and ideally mark the function +// DECLSPEC_NORETURN so we don't have to deal with fabricating return values and we can +// get compiler warnings (unreachable code) anytime functions marked this way are called. +#define DAC_EMPTY() { LEAF_CONTRACT; } +#define DAC_EMPTY_ERR() { LEAF_CONTRACT; DacError(E_UNEXPECTED); } +#define DAC_EMPTY_RET(retVal) { LEAF_CONTRACT; DacError(E_UNEXPECTED); return retVal; } +#define DAC_UNEXPECTED() { LEAF_CONTRACT; DacError_NoRet(E_UNEXPECTED); } + +#endif // __cplusplus + +HRESULT DacGetTargetAddrForHostAddr(const void* ptr, TADDR * pTADDR); + +// Implementation details for dac_cast, should never be accessed directly. +// See code:dac_cast for details and discussion. +namespace dac_imp +{ + //--------------------------------------------- + // Conversion to TADDR + + // Forward declarations. + template + struct conversionHelper; + + template + TADDR getTaddr(T&& val); + + // Helper structs to get the target address of specific types + + // This non-specialized struct handles all instances of asTADDR that don't + // take partially-specialized arguments. + template + struct conversionHelper + { + inline static TADDR asTADDR(__TPtrBase const & tptr) + { return PTR_TO_TADDR(tptr); } + + inline static TADDR asTADDR(TADDR addr) + { return addr; } + }; + + // Handles + template + struct conversionHelper + { + inline static TADDR asTADDR(TypeT * src) + { + TADDR addr = 0; + if (DacGetTargetAddrForHostAddr(src, &addr) != S_OK) + addr = DacGetTargetAddrForHostInteriorAddr(src, true); + return addr; + } + }; + + template + struct conversionHelper<__GlobalPtr const & > + { + inline static TADDR asTADDR(__GlobalPtr const & gptr) + { return PTR_TO_TADDR(gptr); } + }; + + // It is an error to try dac_cast on a __GlobalVal or a __GlobalArray. + template + struct conversionHelper< __GlobalVal const & > + { + inline static TADDR asTADDR(__GlobalVal const & gval) + { static_assert(false, "Cannot use dac_cast on a __GlobalVal; first you must get its address using the '&' operator."); } + }; + + template + struct conversionHelper< __GlobalArray const & > + { + inline static TADDR asTADDR(__GlobalArray const & garr) + { static_assert(false, "Cannot use dac_cast on a __GlobalArray; first you must get its address using the '&' operator."); } + }; + + // This is the main helper function, and it delegates to the above helper functions. + // NOTE: this works because of C++0x reference collapsing rules for rvalue reference + // arguments in template functions. + template + TADDR getTaddr(T&& val) + { return conversionHelper::asTADDR(val); } + + //--------------------------------------------- + // Conversion to DAC instance + + // Helper class to instantiate DAC instances from a TADDR + // The default implementation assumes we want to create an instance of a PTR type + template + struct makeDacInst + { + // First constructing a __TPtrBase and then constructing the target type + // ensures that the target type can construct itself from a __TPtrBase. + // This also prevents unknown user conversions from producing incorrect + // results (since __TPtrBase can only be constructed from TADDR values). + static inline T fromTaddr(TADDR addr) + { return T(__TPtrBase(addr)); } + }; + + // Specialization for creating TADDRs from TADDRs. + template<> struct makeDacInst + { + static inline TADDR fromTaddr(TADDR addr) { return addr; } + }; + + // Partial specialization for creating host instances. + template + struct makeDacInst + { + static inline T * fromTaddr(TADDR addr) + { return makeDacInst::fromTaddr(addr); } + }; + + /* + struct Yes { char c[2]; }; + struct No { char c; }; + Yes& HasTPtrBase(__TPtrBase const *, ); + No& HasTPtrBase(...); + + template + typename rh::std::enable_if< + sizeof(HasTPtrBase(typename rh::std::remove_reference::type *)) == sizeof(Yes), + T>::type + makeDacInst(TADDR addr) + */ + +} // namespace dac_imp + +// DacCop in-line exclusion mechanism + +// Warnings - official home is DacCop\Shared\Warnings.cs, but we want a way for users to indicate +// warning codes in a way that is descriptive to readers (not just code numbers). The names here +// don't matter - DacCop just looks at the value +enum DacCopWarningCode +{ + // General Rules + FieldAccess = 1, + PointerArith = 2, + PointerComparison = 3, + InconsistentMarshalling = 4, + CastBetweenAddressSpaces = 5, + CastOfMarshalledType = 6, + VirtualCallToNonVPtr = 7, + UndacizedGlobalVariable = 8, + + // Function graph related + CallUnknown = 701, + CallNonDac = 702, + CallVirtualUnknown = 704, + CallVirtualNonDac = 705, +}; + +// DACCOP_IGNORE is a mechanism to suppress DacCop violations from within the source-code. +// See the DacCop wiki for guidance on how best to use this: http://mswikis/clr/dev/Pages/DacCop.aspx +// +// DACCOP_IGNORE will suppress a DacCop violation for the following (non-compound) statement. +// For example: +// // The "dual-mode DAC problem" occurs in a few places where a class is used both +// // in the host, and marshalled from the target ... +// DACCOP_IGNORE(CastBetweenAddressSpaces,"SBuffer has the dual-mode DAC problem"); +// TADDR bufAddr = (TADDR)m_buffer; +// +// A call to DACCOP_IGNORE must occur as it's own statement, and can apply only to following +// single-statements (not to compound statement blocks). Occasionally it is necessary to hoist +// violation-inducing code out to its own statement (e.g., if it occurs in the conditional of an +// if). +// +// Arguments: +// code: a literal value from DacCopWarningCode indicating which violation should be suppressed. +// szReasonString: a short description of why this exclusion is necessary. This is intended just +// to help readers of the code understand the source of the problem, and what would be required +// to fix it. More details can be provided in comments if desired. +// +inline void DACCOP_IGNORE(DacCopWarningCode code, const char * szReasonString) +{ + UNREFERENCED_PARAMETER(code); + UNREFERENCED_PARAMETER(szReasonString); + // DacCop detects calls to this function. No implementation is necessary. +} + +#else // !DACCESS_COMPILE + +// +// This version of the macros turns into normal pointers +// for unmodified in-proc compilation. + +// ******************************************************* +// !!!!!!!!!!!!!!!!!!!!!!!!!NOTE!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// Please search this file for the type name to find the +// DAC versions of these definitions +// +// !!!!!!!!!!!!!!!!!!!!!!!!!NOTE!!!!!!!!!!!!!!!!!!!!!!!!!! +// ******************************************************* + +// Declare TADDR as a non-pointer type so that arithmetic +// can be done on it directly, as with the DACCESS_COMPILE definition. +// This also helps expose pointer usage that may need to be changed. +typedef uintptr_t TADDR; + +typedef void* PTR_VOID; +typedef void** PTR_PTR_VOID; + +#define DPTR(type) type* +#define ArrayDPTR(type) type* +#define SPTR(type) type* +#define VPTR(type) type* +#define S8PTR(type) type* +#define S8PTRMAX(type, maxChars) type* +#define S16PTR(type) type* +#define S16PTRMAX(type, maxChars) type* + +#ifndef __GCENV_BASE_INCLUDED__ +#define PTR_TO_TADDR(ptr) (reinterpret_cast(ptr)) +#endif // __GCENV_BASE_INCLUDED__ +#define GFN_TADDR(name) (reinterpret_cast(&(name))) + +#define GVAL_ADDR(g) (&(g)) +#define _SPTR_DECL(acc_type, store_type, var) \ + static store_type var +#define _SPTR_IMPL(acc_type, store_type, cls, var) \ + store_type cls::var +#define _SPTR_IMPL_INIT(acc_type, store_type, cls, var, init) \ + store_type cls::var = init +#define _SPTR_IMPL_NS(acc_type, store_type, ns, cls, var) \ + store_type cls::var +#define _SPTR_IMPL_NS_INIT(acc_type, store_type, ns, cls, var, init) \ + store_type cls::var = init +#define _GPTR_DECL(acc_type, store_type, var) \ + extern store_type var +#define _GPTR_IMPL(acc_type, store_type, var) \ + store_type var +#define _GPTR_IMPL_INIT(acc_type, store_type, var, init) \ + store_type var = init +#define SVAL_DECL(type, var) \ + static type var +#define SVAL_IMPL(type, cls, var) \ + type cls::var +#define SVAL_IMPL_INIT(type, cls, var, init) \ + type cls::var = init +#define SVAL_IMPL_NS(type, ns, cls, var) \ + type cls::var +#define SVAL_IMPL_NS_INIT(type, ns, cls, var, init) \ + type cls::var = init +#define GVAL_DECL(type, var) \ + extern type var +#define GVAL_IMPL(type, var) \ + type var +#define GVAL_IMPL_INIT(type, var, init) \ + type var = init +#define GARY_DECL(type, var, size) \ + extern type var[size] +#define GARY_IMPL(type, var, size) \ + type var[size] +#define PTR_HOST_TO_TADDR(host) (reinterpret_cast(host)) +#define PTR_HOST_INT_TO_TADDR(host) ((TADDR)(host)) +#define VPTR_HOST_VTABLE_TO_TADDR(host) (reinterpret_cast(host)) +#define PTR_HOST_MEMBER_TADDR(type, host, memb) (reinterpret_cast(&(host)->memb)) +#define PTR_HOST_MEMBER(type, host, memb) (&((host)->memb)) +#define PTR_HOST_INT_MEMBER_TADDR(type, host, memb) ((TADDR)&(host)->memb) +#define PTR_TO_MEMBER_TADDR(type, ptr, memb) (reinterpret_cast(&((ptr)->memb))) +#define PTR_TO_MEMBER(type, ptr, memb) (&((ptr)->memb)) +#define PTR_READ(addr, size) (reinterpret_cast(addr)) + +#define PTR_NULL NULL + +#define DAC_EMPTY() +#define DAC_EMPTY_ERR() +#define DAC_EMPTY_RET(retVal) +#define DAC_UNEXPECTED() + +#define DACCOP_IGNORE(warningCode, reasonString) + +#endif // !DACCESS_COMPILE + +//---------------------------------------------------------------------------- +// dac_cast +// Casting utility, to be used for casting one class pointer type to another. +// Use as you would use static_cast +// +// dac_cast is designed to act just as static_cast does when +// dealing with pointers and their DAC abstractions. Specifically, +// it handles these coversions: +// +// dac_cast(SourceTypeVal) +// +// where TargetType <- SourceTypeVal are +// +// ?PTR(Tgt) <- TADDR - Create PTR type (DPtr etc.) from TADDR +// ?PTR(Tgt) <- ?PTR(Src) - Convert one PTR type to another +// ?PTR(Tgt) <- Src * - Create PTR type from dac host object instance +// TADDR <- ?PTR(Src) - Get TADDR of PTR object (DPtr etc.) +// TADDR <- Src * - Get TADDR of dac host object instance +// +// Note that there is no direct convertion to other host-pointer types (because we don't +// know if you want a DPTR or VPTR etc.). However, due to the implicit DAC conversions, +// you can just use dac_cast and assign that to a Foo*. +// +// The beauty of this syntax is that it is consistent regardless +// of source and target casting types. You just use dac_cast +// and the partial template specialization will do the right thing. +// +// One important thing to realise is that all "Foo *" types are +// assumed to be pointers to host instances that were marshalled by DAC. This should +// fail at runtime if it's not the case. +// +// Some examples would be: +// +// - Host pointer of one type to a related host pointer of another +// type, i.e., MethodDesc * <-> InstantiatedMethodDesc * +// Syntax: with MethodDesc *pMD, InstantiatedMethodDesc *pInstMD +// pInstMd = dac_cast(pMD) +// pMD = dac_cast(pInstMD) +// +// - (D|V)PTR of one encapsulated pointer type to a (D|V)PTR of +// another type, i.e., PTR_AppDomain <-> PTR_BaseDomain +// Syntax: with PTR_AppDomain pAD, PTR_BaseDomain pBD +// dac_cast(pBD) +// dac_cast(pAD) +// +// Example comparsions of some old and new syntax, where +// h is a host pointer, such as "Foo *h;" +// p is a DPTR, such as "PTR_Foo p;" +// +// PTR_HOST_TO_TADDR(h) ==> dac_cast(h) +// PTR_TO_TADDR(p) ==> dac_cast(p) +// PTR_Foo(PTR_HOST_TO_TADDR(h)) ==> dac_cast(h) +// +//---------------------------------------------------------------------------- +template +inline Tgt dac_cast(Src src) +{ +#ifdef DACCESS_COMPILE + // In DAC builds, first get a TADDR for the source, then create the + // appropriate destination instance. + TADDR addr = dac_imp::getTaddr(src); + return dac_imp::makeDacInst::fromTaddr(addr); +#else // !DACCESS_COMPILE + // In non-DAC builds, dac_cast is the same as a C-style cast because we need to support: + // - casting away const + // - conversions between pointers and TADDR + // Perhaps we should more precisely restrict it's usage, but we get the precise + // restrictions in DAC builds, so it wouldn't buy us much. + return (Tgt)(src); +#endif // !DACCESS_COMPILE +} + +//---------------------------------------------------------------------------- +// +// Convenience macros which work for either mode. +// +//---------------------------------------------------------------------------- + +#define SPTR_DECL(type, var) _SPTR_DECL(type*, PTR_##type, var) +#define SPTR_IMPL(type, cls, var) _SPTR_IMPL(type*, PTR_##type, cls, var) +#define SPTR_IMPL_INIT(type, cls, var, init) _SPTR_IMPL_INIT(type*, PTR_##type, cls, var, init) +#define SPTR_IMPL_NS(type, ns, cls, var) _SPTR_IMPL_NS(type*, PTR_##type, ns, cls, var) +#define SPTR_IMPL_NS_INIT(type, ns, cls, var, init) _SPTR_IMPL_NS_INIT(type*, PTR_##type, ns, cls, var, init) +#define GPTR_DECL(type, var) _GPTR_DECL(type*, PTR_##type, var) +#define GPTR_IMPL(type, var) _GPTR_IMPL(type*, PTR_##type, var) +#define GPTR_IMPL_INIT(type, var, init) _GPTR_IMPL_INIT(type*, PTR_##type, var, init) + +// If you want to marshal a single instance of an ArrayDPtr over to the host and +// return a pointer to it, you can use this function. However, this is unsafe because +// users of value may assume they can do pointer arithmetic on it. This is exactly +// the bugs ArrayDPtr is designed to prevent. See code:__ArrayDPtr for details. +template +inline type* DacUnsafeMarshalSingleElement( ArrayDPTR(type) arrayPtr ) +{ + return (DPTR(type))(arrayPtr); +} + +typedef DPTR(int8_t) PTR_Int8; +typedef DPTR(int16_t) PTR_Int16; +typedef DPTR(int32_t) PTR_Int32; +typedef DPTR(int64_t) PTR_Int64; +typedef ArrayDPTR(uint8_t) PTR_UInt8; +typedef DPTR(PTR_UInt8) PTR_PTR_UInt8; +typedef DPTR(PTR_PTR_UInt8) PTR_PTR_PTR_UInt8; +typedef DPTR(uint16_t) PTR_UInt16; +typedef DPTR(uint32_t) PTR_UInt32; +typedef DPTR(uint64_t) PTR_UInt64; +typedef DPTR(uintptr_t) PTR_UIntNative; + +typedef DPTR(size_t) PTR_size_t; + +typedef uint8_t Code; +typedef DPTR(Code) PTR_Code; +typedef DPTR(PTR_Code) PTR_PTR_Code; + +#if defined(DACCESS_COMPILE) && defined(DAC_CLR_ENVIRONMENT) +#include +#include +//#include +#endif // defined(DACCESS_COMPILE) && defined(DAC_CLR_ENVIRONMENT) + +//---------------------------------------------------------------------------- +// PCODE is pointer to any executable code. +typedef TADDR PCODE; +typedef DPTR(TADDR) PTR_PCODE; + +//---------------------------------------------------------------------------- +// +// The access code compile must compile data structures that exactly +// match the real structures for access to work. The access code +// doesn't want all of the debugging validation code, though, so +// distinguish between _DEBUG, for declaring general debugging data +// and always-on debug code, and _DEBUG_IMPL, for debugging code +// which will be disabled when compiling for external access. +// +//---------------------------------------------------------------------------- + +#if !defined(_DEBUG_IMPL) && defined(_DEBUG) && !defined(DACCESS_COMPILE) +#define _DEBUG_IMPL 1 +#endif + +// Helper macro for tracking EnumMemoryRegions progress. +#if 0 +#define EMEM_OUT(args) DacWarning args +#else // !0 +#define EMEM_OUT(args) +#endif // !0 + +// TARGET_CONSISTENCY_CHECK represents a condition that should not fail unless the DAC target is corrupt. +// This is in contrast to ASSERTs in DAC infrastructure code which shouldn't fail regardless of the memory +// read from the target. At the moment we treat these the same, but in the future we will want a mechanism +// for disabling just the target consistency checks (eg. for tests that intentionally use corrupted targets). +// @dbgtodo rbyers: Separating asserts and target consistency checks is tracked by DevDiv Bugs 31674 +#define TARGET_CONSISTENCY_CHECK(expr,msg) _ASSERTE_MSG(expr,msg) + +#ifdef DACCESS_COMPILE +#define NO_DAC() static_assert(false, "Cannot use this method in builds DAC: " __FILE__ ":" __LINE__) +#else +#define NO_DAC() do {} while (0) +#endif + +#endif // !__daccess_h__ diff --git a/src/coreclr/nativeaot/Runtime/inc/gcinfo.h b/src/coreclr/nativeaot/Runtime/inc/gcinfo.h new file mode 100644 index 00000000000000..381c94138852f2 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/gcinfo.h @@ -0,0 +1,1588 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*****************************************************************************/ +#ifndef _GCINFO_H_ +#define _GCINFO_H_ +/*****************************************************************************/ + +// Keep definitions in this file in sync with Nutc\UTC\gcinfo.h + +#ifdef TARGET_ARM + +#define NUM_PRESERVED_REGS 9 + +enum RegMask +{ + RBM_R0 = 0x0001, + RBM_R1 = 0x0002, + RBM_R2 = 0x0004, + RBM_R3 = 0x0008, + RBM_R4 = 0x0010, // callee saved + RBM_R5 = 0x0020, // callee saved + RBM_R6 = 0x0040, // callee saved + RBM_R7 = 0x0080, // callee saved + RBM_R8 = 0x0100, // callee saved + RBM_R9 = 0x0200, // callee saved + RBM_R10 = 0x0400, // callee saved + RBM_R11 = 0x0800, // callee saved + RBM_R12 = 0x1000, + RBM_SP = 0x2000, + RBM_LR = 0x4000, // callee saved, but not valid to be alive across a call! + RBM_PC = 0x8000, + RBM_RETVAL = RBM_R0, + RBM_CALLEE_SAVED_REGS = (RBM_R4|RBM_R5|RBM_R6|RBM_R7|RBM_R8|RBM_R9|RBM_R10|RBM_R11|RBM_LR), + RBM_CALLEE_SAVED_REG_COUNT = 9, + // Special case: LR is callee saved, but may not appear as a live GC ref except + // in the leaf frame because calls will trash it. Therefore, we ALSO consider + // it a scratch register. + RBM_SCRATCH_REGS = (RBM_R0|RBM_R1|RBM_R2|RBM_R3|RBM_R12|RBM_LR), + RBM_SCRATCH_REG_COUNT = 6, +}; + +enum RegNumber +{ + RN_R0 = 0, + RN_R1 = 1, + RN_R2 = 2, + RN_R3 = 3, + RN_R4 = 4, + RN_R5 = 5, + RN_R6 = 6, + RN_R7 = 7, + RN_R8 = 8, + RN_R9 = 9, + RN_R10 = 10, + RN_R11 = 11, + RN_R12 = 12, + RN_SP = 13, + RN_LR = 14, + RN_PC = 15, + + RN_NONE = 16, +}; + +enum CalleeSavedRegNum +{ + CSR_NUM_R4 = 0x00, + CSR_NUM_R5 = 0x01, + CSR_NUM_R6 = 0x02, + CSR_NUM_R7 = 0x03, + CSR_NUM_R8 = 0x04, + CSR_NUM_R9 = 0x05, + CSR_NUM_R10 = 0x06, + CSR_NUM_R11 = 0x07, + // NOTE: LR is omitted because it may not be live except as a 'scratch' reg +}; + +enum CalleeSavedRegMask +{ + CSR_MASK_NONE = 0x00, + CSR_MASK_R4 = 0x001, + CSR_MASK_R5 = 0x002, + CSR_MASK_R6 = 0x004, + CSR_MASK_R7 = 0x008, + CSR_MASK_R8 = 0x010, + CSR_MASK_R9 = 0x020, + CSR_MASK_R10 = 0x040, + CSR_MASK_R11 = 0x080, + CSR_MASK_LR = 0x100, + + CSR_MASK_ALL = 0x1ff, + CSR_MASK_HIGHEST = 0x100, +}; + +enum ScratchRegNum +{ + SR_NUM_R0 = 0x00, + SR_NUM_R1 = 0x01, + SR_NUM_R2 = 0x02, + SR_NUM_R3 = 0x03, + SR_NUM_R12 = 0x04, + SR_NUM_LR = 0x05, +}; + +enum ScratchRegMask +{ + SR_MASK_NONE = 0x00, + SR_MASK_R0 = 0x01, + SR_MASK_R1 = 0x02, + SR_MASK_R2 = 0x04, + SR_MASK_R3 = 0x08, + SR_MASK_R12 = 0x10, + SR_MASK_LR = 0x20, +}; + +#elif defined(TARGET_ARM64) + +enum RegMask +{ + RBM_NONE = 0, + + RBM_X0 = 0x00000001, + RBM_X1 = 0x00000002, + RBM_X2 = 0x00000004, + RBM_X3 = 0x00000008, + RBM_X4 = 0x00000010, + RBM_X5 = 0x00000020, + RBM_X6 = 0x00000040, + RBM_X7 = 0x00000080, + RBM_X8 = 0x00000100, // ARM64 ABI: indirect result register + RBM_X9 = 0x00000200, + RBM_X10 = 0x00000400, + RBM_X11 = 0x00000800, + RBM_X12 = 0x00001000, + RBM_X13 = 0x00002000, + RBM_X14 = 0x00004000, + RBM_X15 = 0x00008000, + + RBM_XIP0 = 0x00010000, // This one is occasionally used as a scratch register (but can be destroyed by branching or a call) + RBM_XIP1 = 0x00020000, // This one may be also used as a scratch register (but can be destroyed by branching or a call) + RBM_XPR = 0x00040000, + + RBM_X19 = 0x00080000, // RA_CALLEESAVE + RBM_X20 = 0x00100000, // RA_CALLEESAVE + RBM_X21 = 0x00200000, // RA_CALLEESAVE + RBM_X22 = 0x00400000, // RA_CALLEESAVE + RBM_X23 = 0x00800000, // RA_CALLEESAVE + RBM_X24 = 0x01000000, // RA_CALLEESAVE + RBM_X25 = 0x02000000, // RA_CALLEESAVE + RBM_X26 = 0x04000000, // RA_CALLEESAVE + RBM_X27 = 0x08000000, // RA_CALLEESAVE + RBM_X28 = 0x10000000, // RA_CALLEESAVE + + RBM_FP = 0x20000000, + RBM_LR = 0x40000000, + RBM_SP = 0x80000000, + + RBM_RETVAL = RBM_X8, + // Note: Callee saved regs: X19-X28; FP and LR are treated as callee-saved in unwinding code + RBM_CALLEE_SAVED_REG_COUNT = 12, + + // Scratch regs: X0-X15, XIP0, XIP1, LR + RBM_SCRATCH_REG_COUNT = 19, +}; + +#define NUM_PRESERVED_REGS RBM_CALLEE_SAVED_REG_COUNT + +// Number of the callee-saved registers stored in the fixed header +#define NUM_PRESERVED_REGS_LOW 9 +#define MASK_PRESERVED_REGS_LOW ((1 << NUM_PRESERVED_REGS_LOW) - 1) + +enum RegNumber +{ + RN_X0 = 0, + RN_X1 = 1, + RN_X2 = 2, + RN_X3 = 3, + RN_X4 = 4, + RN_X5 = 5, + RN_X6 = 6, + RN_X7 = 7, + RN_X8 = 8, // indirect result register + RN_X9 = 9, + RN_X10 = 10, + RN_X11 = 11, + RN_X12 = 12, + RN_X13 = 13, + RN_X14 = 14, + RN_X15 = 15, + + RN_XIP0 = 16, + RN_XIP1 = 17, + RN_XPR = 18, + + RN_X19 = 19, // RA_CALLEESAVE + RN_X20 = 20, // RA_CALLEESAVE + RN_X21 = 21, // RA_CALLEESAVE + RN_X22 = 22, // RA_CALLEESAVE + RN_X23 = 23, // RA_CALLEESAVE + RN_X24 = 24, // RA_CALLEESAVE + RN_X25 = 25, // RA_CALLEESAVE + RN_X26 = 26, // RA_CALLEESAVE + RN_X27 = 27, // RA_CALLEESAVE + RN_X28 = 28, // RA_CALLEESAVE + + RN_FP = 29, + RN_LR = 30, + RN_SP = 31, + + RN_NONE = 32, +}; + +enum CalleeSavedRegNum +{ + // NOTE: LR is omitted because it may not be live except as a 'scratch' reg + CSR_NUM_X19 = 1, + CSR_NUM_X20 = 2, + CSR_NUM_X21 = 3, + CSR_NUM_X22 = 4, + CSR_NUM_X23 = 5, + CSR_NUM_X24 = 6, + CSR_NUM_X25 = 7, + CSR_NUM_X26 = 8, + CSR_NUM_X27 = 9, + CSR_NUM_X28 = 10, + CSR_NUM_FP = 11, + CSR_NUM_NONE = 12, +}; + +enum CalleeSavedRegMask +{ + CSR_MASK_NONE = 0x00, + // LR is placed here to reduce the frequency of the long encoding + CSR_MASK_LR = 0x001, + CSR_MASK_X19 = 0x002, + CSR_MASK_X20 = 0x004, + CSR_MASK_X21 = 0x008, + CSR_MASK_X22 = 0x010, + CSR_MASK_X23 = 0x020, + CSR_MASK_X24 = 0x040, + CSR_MASK_X25 = 0x080, + CSR_MASK_X26 = 0x100, + CSR_MASK_X27 = 0x200, + CSR_MASK_X28 = 0x400, + CSR_MASK_FP = 0x800, + + CSR_MASK_ALL = 0xfff, + CSR_MASK_HIGHEST = 0x800, +}; + +enum ScratchRegNum +{ + SR_NUM_X0 = 0, + SR_NUM_X1 = 1, + SR_NUM_X2 = 2, + SR_NUM_X3 = 3, + SR_NUM_X4 = 4, + SR_NUM_X5 = 5, + SR_NUM_X6 = 6, + SR_NUM_X7 = 7, + SR_NUM_X8 = 8, + SR_NUM_X9 = 9, + SR_NUM_X10 = 10, + SR_NUM_X11 = 11, + SR_NUM_X12 = 12, + SR_NUM_X13 = 13, + SR_NUM_X14 = 14, + SR_NUM_X15 = 15, + + SR_NUM_XIP0 = 16, + SR_NUM_XIP1 = 17, + SR_NUM_LR = 18, + + SR_NUM_NONE = 19, +}; + +enum ScratchRegMask +{ + SR_MASK_NONE = 0x00, + SR_MASK_X0 = 0x01, + SR_MASK_X1 = 0x02, + SR_MASK_X2 = 0x04, + SR_MASK_X3 = 0x08, + SR_MASK_X4 = 0x10, + SR_MASK_X5 = 0x20, + SR_MASK_X6 = 0x40, + SR_MASK_X7 = 0x80, + SR_MASK_X8 = 0x100, + SR_MASK_X9 = 0x200, + SR_MASK_X10 = 0x400, + SR_MASK_X11 = 0x800, + SR_MASK_X12 = 0x1000, + SR_MASK_X13 = 0x2000, + SR_MASK_X14 = 0x4000, + SR_MASK_X15 = 0x8000, + + SR_MASK_XIP0 = 0x10000, + SR_MASK_XIP1 = 0x20000, + SR_MASK_LR = 0x40000, +}; + +#else // TARGET_ARM + +#ifdef TARGET_AMD64 +#define NUM_PRESERVED_REGS 8 +#else +#define NUM_PRESERVED_REGS 4 +#endif + +enum RegMask +{ + RBM_EAX = 0x0001, + RBM_ECX = 0x0002, + RBM_EDX = 0x0004, + RBM_EBX = 0x0008, // callee saved + RBM_ESP = 0x0010, + RBM_EBP = 0x0020, // callee saved + RBM_ESI = 0x0040, // callee saved + RBM_EDI = 0x0080, // callee saved + + RBM_R8 = 0x0100, + RBM_R9 = 0x0200, + RBM_R10 = 0x0400, + RBM_R11 = 0x0800, + RBM_R12 = 0x1000, // callee saved + RBM_R13 = 0x2000, // callee saved + RBM_R14 = 0x4000, // callee saved + RBM_R15 = 0x8000, // callee saved + + RBM_RETVAL = RBM_EAX, + +#ifdef TARGET_AMD64 + RBM_CALLEE_SAVED_REGS = (RBM_EDI|RBM_ESI|RBM_EBX|RBM_EBP|RBM_R12|RBM_R13|RBM_R14|RBM_R15), + RBM_CALLEE_SAVED_REG_COUNT = 8, + RBM_SCRATCH_REGS = (RBM_EAX|RBM_ECX|RBM_EDX|RBM_R8|RBM_R9|RBM_R10|RBM_R11), + RBM_SCRATCH_REG_COUNT = 7, +#else + RBM_CALLEE_SAVED_REGS = (RBM_EDI|RBM_ESI|RBM_EBX|RBM_EBP), + RBM_CALLEE_SAVED_REG_COUNT = 4, + RBM_SCRATCH_REGS = (RBM_EAX|RBM_ECX|RBM_EDX), + RBM_SCRATCH_REG_COUNT = 3, +#endif // TARGET_AMD64 +}; + +enum RegNumber +{ + RN_EAX = 0, + RN_ECX = 1, + RN_EDX = 2, + RN_EBX = 3, + RN_ESP = 4, + RN_EBP = 5, + RN_ESI = 6, + RN_EDI = 7, + RN_R8 = 8, + RN_R9 = 9, + RN_R10 = 10, + RN_R11 = 11, + RN_R12 = 12, + RN_R13 = 13, + RN_R14 = 14, + RN_R15 = 15, + + RN_NONE = 16, +}; + +enum CalleeSavedRegNum +{ + CSR_NUM_RBX = 0x00, + CSR_NUM_RSI = 0x01, + CSR_NUM_RDI = 0x02, + CSR_NUM_RBP = 0x03, +#ifdef TARGET_AMD64 + CSR_NUM_R12 = 0x04, + CSR_NUM_R13 = 0x05, + CSR_NUM_R14 = 0x06, + CSR_NUM_R15 = 0x07, +#endif // TARGET_AMD64 +}; + +enum CalleeSavedRegMask +{ + CSR_MASK_NONE = 0x00, + CSR_MASK_RBX = 0x01, + CSR_MASK_RSI = 0x02, + CSR_MASK_RDI = 0x04, + CSR_MASK_RBP = 0x08, + CSR_MASK_R12 = 0x10, + CSR_MASK_R13 = 0x20, + CSR_MASK_R14 = 0x40, + CSR_MASK_R15 = 0x80, + +#ifdef TARGET_AMD64 + CSR_MASK_ALL = 0xFF, + CSR_MASK_HIGHEST = 0x80, +#else + CSR_MASK_ALL = 0x0F, + CSR_MASK_HIGHEST = 0x08, +#endif +}; + +enum ScratchRegNum +{ + SR_NUM_RAX = 0x00, + SR_NUM_RCX = 0x01, + SR_NUM_RDX = 0x02, +#ifdef TARGET_AMD64 + SR_NUM_R8 = 0x03, + SR_NUM_R9 = 0x04, + SR_NUM_R10 = 0x05, + SR_NUM_R11 = 0x06, +#endif // TARGET_AMD64 +}; + +enum ScratchRegMask +{ + SR_MASK_NONE = 0x00, + SR_MASK_RAX = 0x01, + SR_MASK_RCX = 0x02, + SR_MASK_RDX = 0x04, + SR_MASK_R8 = 0x08, + SR_MASK_R9 = 0x10, + SR_MASK_R10 = 0x20, + SR_MASK_R11 = 0x40, +}; + +#endif // TARGET_ARM + +struct GCInfoHeader +{ +private: + uint16_t prologSize : 6; // 0 [0:5] // @TODO: define an 'overflow' encoding for big prologs? + uint16_t hasFunclets : 1; // 0 [6] + uint16_t fixedEpilogSize : 6; // 0 [7] + 1 [0:4] '0' encoding implies that epilog size varies and is encoded for each epilog + uint16_t epilogCountSmall : 2; // 1 [5:6] '3' encoding implies the number of epilogs is encoded separately + uint16_t hasExtraData : 1; // 1 [7] 1: more data follows (dynamic alignment, GS cookie, common vars, etc.) + +#ifdef TARGET_ARM + uint16_t returnKind : 2; // 2 [0:1] one of: MethodReturnKind enum + uint16_t ebpFrame : 1; // 2 [2] on x64, this means "has frame pointer and it is RBP", on ARM R7 + uint16_t epilogAtEnd : 1; // 2 [3] + uint16_t hasFrameSize : 1; // 2 [4] 1: frame size is encoded below, 0: frame size is 0 + uint16_t calleeSavedRegMask : NUM_PRESERVED_REGS; // 2 [5:7] 3 [0:5] + uint16_t arm_areParmOrVfpRegsPushed:1; // 3 [6] 1: pushed param reg set (R0-R3) and pushed fp reg start and count are encoded below, 0: no pushed param or fp registers +#elif defined (TARGET_ARM64) + uint16_t returnKind : 2; // 2 [0:1] one of: MethodReturnKind enum + uint16_t ebpFrame : 1; // 2 [2] 1: has frame pointer and it is FP + uint16_t epilogAtEnd : 1; // 2 [3] + uint16_t hasFrameSize : 1; // 2 [4] 1: frame size is encoded below, 0: frame size is 0 + uint16_t arm64_longCsrMask : 1; // 2 [5] 1: high bits of calleeSavedRegMask are encoded below + uint16_t arm64_areParmOrVfpRegsPushed : 1; // 2 [6] 1: pushed param reg count (X0-X7) and pushed fp reg set (D8-D15) are encoded below, 0: no pushed param or fp registers + uint16_t arm64_calleeSavedRegMaskLow : NUM_PRESERVED_REGS_LOW; // 2 [7] 3 [0:7] +#else + uint8_t returnKind : 2; // 2 [0:1] one of: MethodReturnKind enum + uint8_t ebpFrame : 1; // 2 [2] on x64, this means "has frame pointer and it is RBP", on ARM R7 + uint8_t epilogAtEnd : 1; // 2 [3] +#ifdef TARGET_AMD64 + uint8_t hasFrameSize : 1; // 2 [4] 1: frame size is encoded below, 0: frame size is 0 + uint8_t x64_framePtrOffsetSmall : 2; // 2 [5:6] 00: framePtrOffset = 0x20 + // 01: framePtrOffset = 0x30 + // 10: framePtrOffset = 0x40 + // 11: a variable-length integer 'x64_frameOffset' follows. + uint8_t x64_hasSavedXmmRegs : 1; // 2 [7] any saved xmm registers? +#endif + // X86 X64 + uint8_t calleeSavedRegMask : NUM_PRESERVED_REGS; // 2 [4:7] 3 [0:7] + +#ifdef TARGET_X86 + uint8_t x86_argCountLow : 5; // 3 [0-4] expressed in pointer-sized units // @TODO: steal more bits here? + uint8_t x86_argCountIsLarge : 1; // 3 [5] if this bit is set, then the high 8 bits are encoded in x86_argCountHigh + uint8_t x86_hasStackChanges : 1; // 3 [6] x86-only, !ebpFrame-only, this method has pushes + // and pops in it, and a string follows this header + // which describes them + uint8_t hasFrameSize : 1; // 3 [7] 1: frame size is encoded below, 0: frame size is 0 +#endif +#endif + + // + // OPTIONAL FIELDS FOLLOW + // + // The following values are encoded with variable-length integers on disk, but are decoded into these + // fields in memory. + // + + // For ARM and ARM64 this field stores the offset of the callee-saved area relative to FP/SP + uint32_t frameSize; // expressed in pointer-sized units, only encoded if hasFrameSize==1 + // OPTIONAL: only encoded if returnKind = MRK_ReturnsToNative + uint32_t reversePinvokeFrameOffset; // expressed in pointer-sized units away from the frame pointer + +#ifdef TARGET_AMD64 + // OPTIONAL: only encoded if x64_framePtrOffsetSmall = 11 + // + // ENCODING NOTE: In the encoding, the variable-sized unsigned will be 7 less than the total number + // of 16-byte units that make up the frame pointer offset. + // + // In memory, this value will always be set and will always be the total number of 16-byte units that make + // up the frame pointer offset. + uint8_t x64_framePtrOffset; // expressed in 16-byte unit + + // OPTIONAL: only encoded using a variable-sized unsigned if x64_hasSavedXmmRegs is set. + // + // An additional optimization is possible because registers xmm0 .. xmm5 should never be saved, + // so they are not encoded in the variable-sized unsigned - instead the mask is shifted right 6 bits + // for encoding. Thus, any subset of registers xmm6 .. xmm12 can be represented using one byte + // - this covers the most frequent cases. + // + // The shift applies to decoding/encoding only though - the actual header field below uses the + // straightforward mapping where bit 0 corresponds to xmm0, bit 1 corresponds to xmm1 and so on. + // + uint16_t x64_savedXmmRegMask; // which xmm regs were saved +#elif defined(TARGET_X86) + // OPTIONAL: only encoded if x86_argCountIsLarge = 1 + // NOTE: because we are using pointer-sized units, only 14 bits are required to represent the entire range + // that can be expressed by a 'ret NNNN' instruction. Therefore, with 6 in the 'low' field and 8 in the + // 'high' field, we are not losing any range here. (Although the need for that full range is debatable.) + uint8_t x86_argCountHigh; +#elif defined(TARGET_ARM) + // OPTIONAL: only encoded if arm_areParmOrVfpRegsPushed = 1 + uint8_t arm_parmRegsPushedSet; + uint8_t arm_vfpRegFirstPushed; + uint8_t arm_vfpRegPushedCount; +#elif defined(TARGET_ARM64) + // OPTIONAL: high bits of calleeSavedRegMask are encoded only if arm64_longCsrMask = 1; low bits equal to arm64_calleeSavedRegMaskLow + uint16_t calleeSavedRegMask; + + // OPTIONAL: only encoded if arm64_areParmOrVfpRegsPushed = 1 + uint8_t arm64_parmRegsPushedCount; // how many of X0-X7 registers are saved + uint8_t arm64_vfpRegsPushedMask; // which of D8-D15 registers are saved +#endif + + // + // OPTIONAL: only encoded if hasExtraData = 1 + union + { + struct + { +#if defined(TARGET_ARM64) + uint8_t FPLRAreOnTop : 1; // [0] 1: FP and LR are saved on top of locals, not at the bottom (see MdmSaveFPAndLRAtTopOfLocalsArea) + uint8_t reg1ReturnKind : 2; // [1:2] One of MRK_Returns{Scalar|Object|Byref} constants describing value returned in x1 if any + uint8_t hasGSCookie : 1; // [3] 1: frame uses GS cookie + uint8_t hasCommonVars : 1; // [4] 1: method has a list of "common vars" + // as an optimization for methods with many call sites and variables + uint8_t : 3; // [5:7] unused bits +#else + uint8_t logStackAlignment : 4; // [0:3] binary logarithm of frame alignment (3..15) or 0 + uint8_t hasGSCookie : 1; // [4] 1: frame uses GS cookie + uint8_t hasCommonVars : 1; // [5] 1: method has a list of "common vars" + // as an optimization for methods with many call sites and variables + uint8_t : 2; // [6:7] unused bits +#endif +#pragma warning(suppress:4201) // nameless struct + }; + uint8_t extraDataHeader; + }; + + // OPTIONAL: only encoded if logStackAlignment != 0 + uint8_t paramPointerReg; + + // OPTIONAL: only encoded if epilogCountSmall = 3 + uint16_t epilogCount; + + // OPTIONAL: only encoded if gsCookie = 1 + uint32_t gsCookieOffset; // expressed in pointer-sized units away from the frame pointer + + // + // OPTIONAL: only encoded if hasFunclets = 1 + // {numFunclets} // encoded as variable-length unsigned + // {start-funclet0} // offset from start of previous funclet, encoded as variable-length unsigned + // {start-funclet1} // + // {start-funclet2} + // ... + // {sizeof-funclet(N-1)} // numFunclets == N (i.e. there are N+1 sizes here) + // ----------------- + // {GCInfoHeader-funclet0} // encoded as normal, must not have 'hasFunclets' set. + // {GCInfoHeader-funclet1} + // ... + // {GCInfoHeader-funclet(N-1)} + + // WARNING: + // WARNING: Do not add fields to the file-format after the funclet header encodings -- these are decoded + // WARNING: recursively and 'in-place' when looking for the info associated with a funclet. Therefore, + // WARNING: in that case, we cannot easily continue to decode things associated with the main body + // WARNING: GCInfoHeader once we start this recursive decode. + // WARNING: + + // ------------------------------------------------------------------------------------------------------- + // END of file-encoding-related-fields + // ------------------------------------------------------------------------------------------------------- + + // The following fields are not encoded in the file format, they are just used as convenience placeholders + // for decode state. + uint32_t funcletOffset; // non-zero indicates that this GCInfoHeader is for a funclet + +public: + // + // CONSTANTS / STATIC STUFF + // + + enum MethodReturnKind + { + MRK_ReturnsScalar = 0, + MRK_ReturnsObject = 1, + MRK_ReturnsByref = 2, + MRK_ReturnsToNative = 3, + +#if defined(TARGET_ARM64) + // Cases for structs returned in two registers. + // Naming scheme: MRK_reg0Kind_reg1Kind. + // Encoding scheme: . + // We do not distinguish returning a scalar in reg1 and no return value in reg1, + // which means we can use MRK_ReturnsObject for MRK_Obj_Scalar, etc. + MRK_Scalar_Obj = (MRK_ReturnsObject << 2) | MRK_ReturnsScalar, + MRK_Obj_Obj = (MRK_ReturnsObject << 2) | MRK_ReturnsObject, + MRK_Byref_Obj = (MRK_ReturnsObject << 2) | MRK_ReturnsByref, + MRK_Scalar_Byref = (MRK_ReturnsByref << 2) | MRK_ReturnsScalar, + MRK_Obj_Byref = (MRK_ReturnsByref << 2) | MRK_ReturnsObject, + MRK_Byref_Byref = (MRK_ReturnsByref << 2) | MRK_ReturnsByref, + + MRK_LastValid = MRK_Byref_Byref, + // Illegal or uninitialized value. Never written to the image. + MRK_Unknown = 0xff, +#else + MRK_LastValid = MRK_ReturnsToNative, + // Illegal or uninitialized value. Never written to the image. + MRK_Unknown = 4, +#endif + }; + + enum EncodingConstants + { + EC_SizeOfFixedHeader = 4, + EC_MaxFrameByteSize = 10*1024*1024, + EC_MaxReversePInvokeFrameByteOffset = 10*1024*1024, + EC_MaxX64FramePtrByteOffset = UINT16_MAX * 0x10, + EC_MaxEpilogCountSmall = 3, + EC_MaxEpilogCount = 64*1024 - 1, + }; + + // + // MEMBER FUNCTIONS + // + + void Init() + { + memset(this, 0, sizeof(GCInfoHeader)); + } + + // + // SETTERS + // + + void SetPrologSize(uint32_t sizeInBytes) + { +#if defined (TARGET_ARM64) + // For arm64 we encode multiples of 4, rather than raw bytes, since instructions are all same size. + ASSERT((sizeInBytes & 3) == 0); + prologSize = sizeInBytes >> 2; + ASSERT(prologSize == sizeInBytes >> 2); +#else + prologSize = sizeInBytes; + ASSERT(prologSize == sizeInBytes); +#endif + } + + void SetHasFunclets(bool fHasFunclets) + { + hasFunclets = fHasFunclets ? 1 : 0; + } + + void PokeFixedEpilogSize(uint32_t sizeInBytes) + { +#if defined (TARGET_ARM64) + // For arm64 we encode multiples of 4, rather than raw bytes, since instructions are all same size. + ASSERT((sizeInBytes & 3) == 0); + fixedEpilogSize = sizeInBytes >> 2; + ASSERT(fixedEpilogSize == sizeInBytes >> 2); +#else + fixedEpilogSize = sizeInBytes; + ASSERT(fixedEpilogSize == sizeInBytes); +#endif + } + + void SetFixedEpilogSize(uint32_t sizeInBytes, bool varyingSizes) + { + if (varyingSizes) + fixedEpilogSize = 0; + else + { + ASSERT(sizeInBytes != 0); +#if defined (TARGET_ARM64) + // For arm64 we encode multiples of 4, rather than raw bytes, since instructions are all same size. + ASSERT((sizeInBytes & 3) == 0); + fixedEpilogSize = sizeInBytes >> 2; + ASSERT(fixedEpilogSize == sizeInBytes >> 2); +#else + fixedEpilogSize = sizeInBytes; + ASSERT(fixedEpilogSize == sizeInBytes); +#endif + } + } + + void SetEpilogCount(uint32_t count, bool isAtEnd) + { + epilogCount = ToUInt16(count); + epilogAtEnd = isAtEnd ? 1 : 0; + + ASSERT(epilogCount == count); + ASSERT((count == 1) || !isAtEnd); + epilogCountSmall = count < EC_MaxEpilogCountSmall ? count : EC_MaxEpilogCountSmall; + } + +#if !defined(TARGET_ARM64) + void SetReturnKind(MethodReturnKind kind) + { + ASSERT(kind <= MRK_ReturnsToNative); // not enough bits to encode 'unknown' + returnKind = kind; + } + + void SetDynamicAlignment(uint8_t logByteAlignment) + { +#ifdef TARGET_X86 + ASSERT(logByteAlignment >= 3); // 4 byte aligned frames +#else + ASSERT(logByteAlignment >= 4); // 8 byte aligned frames +#endif + + hasExtraData = 1; + logStackAlignment = logByteAlignment; + ASSERT(logStackAlignment == logByteAlignment); + paramPointerReg = RN_NONE; + } +#endif // !defined(TARGET_ARM64) + +#if defined(TARGET_ARM64) + void SetFPLROnTop(void) + { + hasExtraData = 1; + FPLRAreOnTop = 1; + } +#endif + + void SetGSCookieOffset(uint32_t offsetInBytes) + { + ASSERT(offsetInBytes != 0); + ASSERT(0 == (offsetInBytes % POINTER_SIZE)); + hasExtraData = 1; + hasGSCookie = 1; + gsCookieOffset = offsetInBytes / POINTER_SIZE; + } + + void SetHasCommonVars() + { + hasExtraData = 1; + hasCommonVars = 1; + } + + void SetParamPointer(RegNumber regNum, uint32_t offsetInBytes, bool isOffsetFromSP = false) + { + UNREFERENCED_PARAMETER(offsetInBytes); + UNREFERENCED_PARAMETER(isOffsetFromSP); + ASSERT(HasDynamicAlignment()); // only expected for dynamic aligned frames + ASSERT(offsetInBytes==0); // not yet supported + + paramPointerReg = (uint8_t)regNum; + } + + void SetFramePointer(RegNumber regNum, uint32_t offsetInBytes, bool isOffsetFromSP = false) + { + UNREFERENCED_PARAMETER(offsetInBytes); + UNREFERENCED_PARAMETER(isOffsetFromSP); + + if (regNum == RN_NONE) + { + ebpFrame = 0; + } + else + { +#ifdef TARGET_ARM + ASSERT(regNum == RN_R7); +#elif defined(TARGET_AMD64) || defined(TARGET_X86) + ASSERT(regNum == RN_EBP); +#elif defined(TARGET_ARM64) + ASSERT(regNum == RN_FP); +#else + ASSERT(!"NYI"); +#endif + ebpFrame = 1; + } + ASSERT(offsetInBytes == 0 || isOffsetFromSP); + +#ifdef TARGET_AMD64 + if (isOffsetFromSP) + offsetInBytes += SKEW_FOR_OFFSET_FROM_SP; + + ASSERT((offsetInBytes % 0x10) == 0); + uint32_t offsetInSlots = offsetInBytes / 0x10; + if (offsetInSlots >= 3 && offsetInSlots <= 3 + 2) + { + x64_framePtrOffsetSmall = offsetInSlots - 3; + } + else + { + x64_framePtrOffsetSmall = 3; + } + x64_framePtrOffset = (uint8_t)offsetInSlots; + ASSERT(x64_framePtrOffset == offsetInSlots); +#else + ASSERT(offsetInBytes == 0 && !isOffsetFromSP); +#endif // TARGET_AMD64 + } + + void SetFrameSize(uint32_t frameSizeInBytes) + { + ASSERT(0 == (frameSizeInBytes % POINTER_SIZE)); + frameSize = (frameSizeInBytes / POINTER_SIZE); + ASSERT(frameSize == (frameSizeInBytes / POINTER_SIZE)); + if (frameSize != 0) + { + hasFrameSize = 1; + } + } + + void SetSavedRegs(CalleeSavedRegMask regMask) + { + calleeSavedRegMask = (uint16_t)regMask; + } + + void SetRegSaved(CalleeSavedRegMask regMask) + { + calleeSavedRegMask |= regMask; + } + + void SetReversePinvokeFrameOffset(int offsetInBytes) + { + ASSERT(HasFramePointer()); + ASSERT((offsetInBytes % POINTER_SIZE) == 0); + ASSERT(GetReturnKind() == MRK_ReturnsToNative); + +#if defined(TARGET_ARM) || defined(TARGET_AMD64) || defined(TARGET_ARM64) + // The offset can be either positive or negative on ARM and x64. + bool isNeg = (offsetInBytes < 0); + uint32_t uOffsetInBytes = isNeg ? -offsetInBytes : offsetInBytes; + uint32_t uEncodedVal = ((uOffsetInBytes / POINTER_SIZE) << 1) | (isNeg ? 1 : 0); + reversePinvokeFrameOffset = uEncodedVal; + ASSERT(reversePinvokeFrameOffset == uEncodedVal); +#elif defined (TARGET_X86) + // Use a positive number because it encodes better and + // the offset is always negative on x86. + ASSERT(offsetInBytes < 0); + reversePinvokeFrameOffset = (-offsetInBytes / POINTER_SIZE); + ASSERT(reversePinvokeFrameOffset == (uint32_t)(-offsetInBytes / POINTER_SIZE)); +#else + ASSERT(!"NYI"); +#endif + } + +#ifdef TARGET_X86 + void SetReturnPopSize(uint32_t popSizeInBytes) + { + ASSERT(0 == (popSizeInBytes % POINTER_SIZE)); + ASSERT(GetReturnPopSize() == 0 || GetReturnPopSize() == (int)popSizeInBytes); + + uint32_t argCount = popSizeInBytes / POINTER_SIZE; + x86_argCountLow = argCount & 0x1F; + if (argCount != x86_argCountLow) + { + x86_argCountIsLarge = 1; + x86_argCountHigh = (uint8_t)(argCount >> 5); + } + } + + void SetHasStackChanges() + { + x86_hasStackChanges = 1; + } +#endif // TARGET_X86 + +#ifdef TARGET_ARM + void SetParmRegsPushed(ScratchRegMask pushedParmRegs) + { + // should be a subset of {RO-R3} + ASSERT((pushedParmRegs & ~(SR_MASK_R0|SR_MASK_R1|SR_MASK_R2|SR_MASK_R3)) == 0); + arm_areParmOrVfpRegsPushed = pushedParmRegs != 0 || arm_vfpRegPushedCount != 0; + arm_parmRegsPushedSet = (uint8_t)pushedParmRegs; + } + + void SetVfpRegsPushed(uint8_t vfpRegFirstPushed, uint8_t vfpRegPushedCount) + { + // mrt100.dll really only supports pushing a subinterval of d8-d15 + // these are the preserved floating point registers according to the ABI spec + ASSERT(8 <= vfpRegFirstPushed && vfpRegFirstPushed + vfpRegPushedCount <= 16 || vfpRegPushedCount == 0); + arm_vfpRegFirstPushed = vfpRegFirstPushed; + arm_vfpRegPushedCount = vfpRegPushedCount; + arm_areParmOrVfpRegsPushed = arm_parmRegsPushedSet != 0 || vfpRegPushedCount != 0; + } +#elif defined(TARGET_ARM64) + void SetParmRegsPushedCount(uint8_t parmRegsPushedCount) + { + // pushed parameter registers are a subset of {R0-R7} + ASSERT(parmRegsPushedCount <= 8); + arm64_parmRegsPushedCount = parmRegsPushedCount; + arm64_areParmOrVfpRegsPushed = (arm64_parmRegsPushedCount != 0) || (arm64_vfpRegsPushedMask != 0); + } + + void SetVfpRegsPushed(uint8_t vfpRegsPushedMask) + { + arm64_vfpRegsPushedMask = vfpRegsPushedMask; + arm64_areParmOrVfpRegsPushed = (arm64_parmRegsPushedCount != 0) || (arm64_vfpRegsPushedMask != 0); + } +#elif defined(TARGET_AMD64) + void SetSavedXmmRegs(uint32_t savedXmmRegMask) + { + // any subset of xmm6-xmm15 may be saved, but no registers in xmm0-xmm5 should be present + ASSERT((savedXmmRegMask & 0xffff003f) == 0); + x64_hasSavedXmmRegs = savedXmmRegMask != 0; + x64_savedXmmRegMask = (uint16_t)savedXmmRegMask; + } +#endif + + void SetFuncletOffset(uint32_t offset) + { + funcletOffset = offset; + } + + // + // GETTERS + // + uint32_t GetPrologSize() + { +#if defined (TARGET_ARM64) + return prologSize << 2; +#else + return prologSize; +#endif + } + + bool HasFunclets() + { + return (hasFunclets != 0); + } + + bool HasVaryingEpilogSizes() + { + return fixedEpilogSize == 0; + } + + uint32_t PeekFixedEpilogSize() + { +#if defined (TARGET_ARM64) + return fixedEpilogSize << 2; +#else + return fixedEpilogSize; +#endif + } + + uint32_t GetFixedEpilogSize() + { + ASSERT(!HasVaryingEpilogSizes()); +#if defined (TARGET_ARM64) + return fixedEpilogSize << 2; +#else + return fixedEpilogSize; +#endif + } + + uint32_t GetEpilogCount() + { + return epilogCount; + } + + bool IsEpilogAtEnd() + { + return (epilogAtEnd != 0); + } + + MethodReturnKind GetReturnKind() + { +#if defined(TARGET_ARM64) + return (MethodReturnKind)((reg1ReturnKind << 2) | returnKind); +#else + return (MethodReturnKind)returnKind; +#endif + } + + bool ReturnsToNative() + { + return (GetReturnKind() == MRK_ReturnsToNative); + } + + bool HasFramePointer() const + { + return !!ebpFrame; + } + + bool IsFunclet() + { + return funcletOffset != 0; + } + + uint32_t GetFuncletOffset() + { + return funcletOffset; + } + + int GetPreservedRegsSaveSize() const // returned in bytes + { + uint32_t count = 0; + uint32_t mask = calleeSavedRegMask; + while (mask != 0) + { + count += mask & 1; + mask >>= 1; + } + + return count * POINTER_SIZE; + } + + int GetParamPointerReg() + { + return paramPointerReg; + } + + bool HasDynamicAlignment() + { +#if defined(TARGET_ARM64) + return false; +#else + return !!logStackAlignment; +#endif + } + + uint32_t GetDynamicAlignment() + { +#if defined(TARGET_ARM64) + ASSERT(!"Not supported"); + return 1; +#else + return 1 << logStackAlignment; +#endif + } + + bool HasGSCookie() + { + return hasGSCookie; + } + +#if defined(TARGET_ARM64) + bool AreFPLROnTop() const + { + return FPLRAreOnTop; + } +#endif + + uint32_t GetGSCookieOffset() + { + ASSERT(hasGSCookie); + return gsCookieOffset * POINTER_SIZE; + } + + bool HasCommonVars() const + { + return hasCommonVars; + } + +#ifdef TARGET_AMD64 + static const uint32_t SKEW_FOR_OFFSET_FROM_SP = 0x10; + + int GetFramePointerOffset() const // returned in bytes + { + // traditional frames where FP points to the pushed FP have fp offset == 0 + if (x64_framePtrOffset == 0) + return 0; + + // otherwise it's an x64 style frame where the fp offset is measured from the sp + // at the end of the prolog + int offsetFromSP = GetFramePointerOffsetFromSP(); + + int preservedRegsSaveSize = GetPreservedRegsSaveSize(); + + // we when called from the binder, rbp isn't set to be a preserved reg, + // when called from the runtime, it is - compensate for this inconsistency + if (IsRegSaved(CSR_MASK_RBP)) + preservedRegsSaveSize -= POINTER_SIZE; + + return offsetFromSP - preservedRegsSaveSize - GetFrameSize(); + } + + bool IsFramePointerOffsetFromSP() const + { + return x64_framePtrOffset != 0; + } + + int GetFramePointerOffsetFromSP() const + { + ASSERT(IsFramePointerOffsetFromSP()); + int offsetFromSP; + offsetFromSP = x64_framePtrOffset * 0x10; + ASSERT(offsetFromSP >= SKEW_FOR_OFFSET_FROM_SP); + offsetFromSP -= SKEW_FOR_OFFSET_FROM_SP; + + return offsetFromSP; + } + + int GetFramePointerReg() + { + return RN_EBP; + } + + bool HasSavedXmmRegs() + { + return x64_hasSavedXmmRegs != 0; + } + + uint16_t GetSavedXmmRegMask() + { + ASSERT(x64_hasSavedXmmRegs); + return x64_savedXmmRegMask; + } +#elif defined(TARGET_X86) + int GetReturnPopSize() // returned in bytes + { + if (!x86_argCountIsLarge) + { + return x86_argCountLow * POINTER_SIZE; + } + return ((x86_argCountHigh << 5) | x86_argCountLow) * POINTER_SIZE; + } + + bool HasStackChanges() + { + return !!x86_hasStackChanges; + } +#endif + + int GetFrameSize() const + { + return frameSize * POINTER_SIZE; + } + + + int GetReversePinvokeFrameOffset() + { +#if defined(TARGET_ARM) || defined(TARGET_AMD64) || defined(TARGET_ARM64) + // The offset can be either positive or negative on ARM. + int32_t offsetInBytes; + uint32_t uEncodedVal = reversePinvokeFrameOffset; + bool isNeg = ((uEncodedVal & 1) == 1); + offsetInBytes = (uEncodedVal >> 1) * POINTER_SIZE; + offsetInBytes = isNeg ? -offsetInBytes : offsetInBytes; + return offsetInBytes; +#elif defined(TARGET_X86) + // it's always at "EBP - something", so we encode it as a positive + // number and then apply the negative here. + int unsignedOffset = reversePinvokeFrameOffset * POINTER_SIZE; + return -unsignedOffset; +#else + ASSERT(!"NYI"); +#endif + } + + CalleeSavedRegMask GetSavedRegs() + { + return (CalleeSavedRegMask) calleeSavedRegMask; + } + + bool IsRegSaved(CalleeSavedRegMask reg) const + { + return (0 != (calleeSavedRegMask & reg)); + } + +#ifdef TARGET_ARM + bool AreParmRegsPushed() + { + return arm_parmRegsPushedSet != 0; + } + + uint16_t ParmRegsPushedCount() + { + uint8_t set = arm_parmRegsPushedSet; + uint8_t count = 0; + while (set != 0) + { + count += set & 1; + set >>= 1; + } + return count; + } + + uint8_t GetVfpRegFirstPushed() + { + return arm_vfpRegFirstPushed; + } + + uint8_t GetVfpRegPushedCount() + { + return arm_vfpRegPushedCount; + } +#elif defined(TARGET_ARM64) + uint8_t ParmRegsPushedCount() + { + return arm64_parmRegsPushedCount; + } + + uint8_t GetVfpRegsPushedMask() + { + return arm64_vfpRegsPushedMask; + } +#endif + + // + // ENCODING HELPERS + // +#ifndef DACCESS_COMPILE + size_t EncodeHeader(uint8_t * & pDest) + { +#ifdef _DEBUG + uint8_t * pStart = pDest; +#endif // _DEBUG + +#if defined(TARGET_ARM64) + uint8_t calleeSavedRegMaskHigh = calleeSavedRegMask >> NUM_PRESERVED_REGS_LOW; + arm64_calleeSavedRegMaskLow = calleeSavedRegMask & MASK_PRESERVED_REGS_LOW; + if (calleeSavedRegMaskHigh) + { + arm64_longCsrMask = 1; + } +#endif + + size_t size = EC_SizeOfFixedHeader; + if (pDest) + { + memcpy(pDest, this, EC_SizeOfFixedHeader); + pDest += EC_SizeOfFixedHeader; + } + + if (hasFrameSize) + size += WriteUnsigned(pDest, frameSize); + + if (returnKind == MRK_ReturnsToNative) + size += WriteUnsigned(pDest, reversePinvokeFrameOffset); + +#ifdef TARGET_AMD64 + if (x64_framePtrOffsetSmall == 0x3) + size += WriteUnsigned(pDest, x64_framePtrOffset); + + if (x64_hasSavedXmmRegs) + { + ASSERT((x64_savedXmmRegMask & 0x3f) == 0); + uint32_t encodedValue = x64_savedXmmRegMask >> 6; + size += WriteUnsigned(pDest, encodedValue); + } +#elif defined(TARGET_X86) + if (x86_argCountIsLarge) + { + size += 1; + if (pDest) + *pDest++ = x86_argCountHigh; + } + ASSERT(!x86_hasStackChanges || !"NYI -- stack changes for ESP frames"); +#elif defined(TARGET_ARM) + if (arm_areParmOrVfpRegsPushed) + { + // we encode a bit field where the low 4 bits represent the pushed parameter register + // set, the next 8 bits are the number of pushed floating point registers, and the highest + // bits are the first pushed floating point register plus 1. + // The 0 encoding means the first floating point register is 8 as this is the most frequent. + uint32_t encodedValue = arm_parmRegsPushedSet | (arm_vfpRegPushedCount << 4); + // usually, the first pushed floating point register is d8 + if (arm_vfpRegFirstPushed != 8) + encodedValue |= (arm_vfpRegFirstPushed+1) << (8+4); + + size += WriteUnsigned(pDest, encodedValue); + } +#elif defined(TARGET_ARM64) + if (calleeSavedRegMaskHigh) + { + size += 1; + if (pDest) + *pDest++ = calleeSavedRegMaskHigh; + } + + if (arm64_areParmOrVfpRegsPushed) + { + // At present arm64_parmRegsPushedCount is non-zero only for variadic functions, so place this field higher + uint32_t encodedValue = arm64_vfpRegsPushedMask | (arm64_parmRegsPushedCount << 8); + size += WriteUnsigned(pDest, encodedValue); + } +#endif + + // encode dynamic alignment and GS cookie information + if (hasExtraData) + { + size += WriteUnsigned(pDest, extraDataHeader); + } + if (HasDynamicAlignment()) + { + size += WriteUnsigned(pDest, paramPointerReg); + } + if (hasGSCookie) + { + size += WriteUnsigned(pDest, gsCookieOffset); + } + + if (epilogCountSmall == EC_MaxEpilogCountSmall) + { + size += WriteUnsigned(pDest, epilogCount); + } + + // WARNING: + // WARNING: Do not add fields to the file-format after the funclet header encodings -- these are + // WARNING: decoded recursively and 'in-place' when looking for the info associated with a funclet. + // WARNING: Therefore, in that case, we cannot easily continue to decode things associated with the + // WARNING: main body GCInfoHeader once we start this recursive decode. + // WARNING: + size += EncodeFuncletInfo(pDest); + +#ifdef _DEBUG + ASSERT(!pDest || (size == (size_t)(pDest - pStart))); +#endif // _DEBUG + + return size; + } + + size_t WriteUnsigned(uint8_t * & pDest, uint32_t value) + { + size_t size = (size_t)VarInt::WriteUnsigned(pDest, value); + pDest = pDest ? (pDest + size) : pDest; + return size; + } +#endif // DACCESS_COMPILE + + uint16_t ToUInt16(uint32_t val) + { + uint16_t result = (uint16_t)val; + ASSERT(val == result); + return result; + } + + uint8_t ToUInt8(uint32_t val) + { + uint8_t result = (uint8_t)val; + ASSERT(val == result); + return result; + } + + // + // DECODING HELPERS + // + // Returns a pointer to the 'stack change string' on x86. + PTR_UInt8 DecodeHeader(uint32_t methodOffset, PTR_UInt8 pbHeaderEncoding, size_t* pcbHeader) + { + PTR_UInt8 pbStackChangeString = NULL; + + TADDR pbTemp = PTR_TO_TADDR(pbHeaderEncoding); + memcpy(this, PTR_READ(pbTemp, EC_SizeOfFixedHeader), EC_SizeOfFixedHeader); + + PTR_UInt8 pbDecode = pbHeaderEncoding + EC_SizeOfFixedHeader; + frameSize = hasFrameSize + ? VarInt::ReadUnsigned(pbDecode) + : 0; + + reversePinvokeFrameOffset = (returnKind == MRK_ReturnsToNative) + ? VarInt::ReadUnsigned(pbDecode) + : 0; + +#ifdef TARGET_AMD64 + x64_framePtrOffset = (x64_framePtrOffsetSmall == 0x3) + ? ToUInt8(VarInt::ReadUnsigned(pbDecode)) + : x64_framePtrOffsetSmall + 3; + + + x64_savedXmmRegMask = 0; + if (x64_hasSavedXmmRegs) + { + uint32_t encodedValue = VarInt::ReadUnsigned(pbDecode); + ASSERT((encodedValue & ~0x3ff) == 0); + x64_savedXmmRegMask = ToUInt16(encodedValue << 6); + } + +#elif defined(TARGET_X86) + if (x86_argCountIsLarge) + x86_argCountHigh = *pbDecode++; + else + x86_argCountHigh = 0; + + if (x86_hasStackChanges) + { + pbStackChangeString = pbDecode; + + bool last = false; + while (!last) + { + uint8_t b = *pbDecode++; + // 00111111 {delta} forwarder + // 00dddddd push 1, dddddd = delta + // nnnldddd pop nnn-1, l = last, dddd = delta (nnn=0 and nnn=1 are disallowed) + if (b == 0x3F) + { + // 00111111 {delta} forwarder + VarInt::ReadUnsigned(pbDecode); + } + else if (0 != (b & 0xC0)) + { + // nnnldddd pop nnn-1, l = last, dddd = delta (nnn=0 and nnn=1 are disallowed) + last = ((b & 0x10) == 0x10); + } + } + } +#elif defined(TARGET_ARM) + arm_parmRegsPushedSet = 0; + arm_vfpRegPushedCount = 0; + arm_vfpRegFirstPushed = 0; + if (arm_areParmOrVfpRegsPushed) + { + uint32_t encodedValue = VarInt::ReadUnsigned(pbDecode); + arm_parmRegsPushedSet = encodedValue & 0x0f; + arm_vfpRegPushedCount = (uint8_t)(encodedValue >> 4); + uint32_t vfpRegFirstPushed = encodedValue >> (8 + 4); + if (vfpRegFirstPushed == 0) + arm_vfpRegFirstPushed = 8; + else + arm_vfpRegFirstPushed = (uint8_t)(vfpRegFirstPushed - 1); + } +#elif defined(TARGET_ARM64) + calleeSavedRegMask = arm64_calleeSavedRegMaskLow; + if (arm64_longCsrMask) + { + calleeSavedRegMask |= (*pbDecode++ << NUM_PRESERVED_REGS_LOW); + } + + arm64_parmRegsPushedCount = 0; + arm64_vfpRegsPushedMask = 0; + if (arm64_areParmOrVfpRegsPushed) + { + uint32_t encodedValue = VarInt::ReadUnsigned(pbDecode); + arm64_vfpRegsPushedMask = (uint8_t)encodedValue; + arm64_parmRegsPushedCount = (uint8_t)(encodedValue >> 8); + ASSERT(arm64_parmRegsPushedCount <= 8); + } +#endif + + extraDataHeader = hasExtraData ? ToUInt8(VarInt::ReadUnsigned(pbDecode)) : 0; + paramPointerReg = HasDynamicAlignment() ? ToUInt8(VarInt::ReadUnsigned(pbDecode)) : (uint8_t)RN_NONE; + gsCookieOffset = hasGSCookie ? VarInt::ReadUnsigned(pbDecode) : 0; + + epilogCount = epilogCountSmall < EC_MaxEpilogCountSmall ? epilogCountSmall : ToUInt16(VarInt::ReadUnsigned(pbDecode)); + + this->funcletOffset = 0; + if (hasFunclets) + { + // WORKAROUND: Epilog tables are still per-method instead of per-funclet, but we don't deal with + // them here. So we will simply overwrite the funclet's epilogAtEnd and epilogCount + // with the values from the main code body -- these were the values used to generate + // the per-method epilog table, so at least we're consistent with what is encoded. + uint8_t mainEpilogAtEnd = epilogAtEnd; + uint16_t mainEpilogCount = epilogCount; + uint16_t mainFixedEpilogSize = fixedEpilogSize; // Either in bytes or in instructions + uint8_t mainHasCommonVars = hasCommonVars; + // ------- + + int nFunclets = (int)VarInt::ReadUnsigned(pbDecode); + int idxFunclet = -2; + uint32_t offsetFunclet = 0; + // Decode the funclet start offsets, remembering which one is of interest. + uint32_t prevFuncletStart = 0; + for (int i = 0; i < nFunclets; i++) + { + uint32_t offsetThisFunclet = prevFuncletStart + VarInt::ReadUnsigned(pbDecode); + if ((idxFunclet == -2) && (methodOffset < offsetThisFunclet)) + { + idxFunclet = (i - 1); + offsetFunclet = prevFuncletStart; + } + prevFuncletStart = offsetThisFunclet; + } + if ((idxFunclet == -2) && (methodOffset >= prevFuncletStart)) + { + idxFunclet = (nFunclets - 1); + offsetFunclet = prevFuncletStart; + } + + // Now decode headers until we find the one we want. Keep decoding if we need to report a size. + if (pcbHeader || (idxFunclet >= 0)) + { + for (int i = 0; i < nFunclets; i++) + { + size_t hdrSize; + if (i == idxFunclet) + { + this->DecodeHeader(methodOffset, pbDecode, &hdrSize); + pbDecode += hdrSize; + this->funcletOffset = offsetFunclet; + if (!pcbHeader) // if nobody is going to look at the header size, we don't need to keep going + break; + } + else + { + // keep decoding into a temp just to get the right header size + GCInfoHeader tmp; + tmp.DecodeHeader(methodOffset, pbDecode, &hdrSize); + pbDecode += hdrSize; + } + } + } + + // WORKAROUND: see above + this->epilogAtEnd = mainEpilogAtEnd; + this->epilogCount = mainEpilogCount; + this->PokeFixedEpilogSize(mainFixedEpilogSize); + this->hasCommonVars = mainHasCommonVars; + + // ------- + } + + // WARNING: + // WARNING: Do not add fields to the file-format after the funclet header encodings -- these are + // WARNING: decoded recursively and 'in-place' when looking for the info associated with a funclet. + // WARNING: Therefore, in that case, we cannot easily continue to decode things associated with the + // WARNING: main body GCInfoHeader once we start this recursive decode. + // WARNING: + + if (pcbHeader) + *pcbHeader = pbDecode - pbHeaderEncoding; + + return pbStackChangeString; + } + + void GetFuncletInfo(PTR_UInt8 pbHeaderEncoding, uint32_t* pnFuncletsOut, PTR_UInt8* pEncodedFuncletStartOffsets) + { + ASSERT(hasFunclets); + + PTR_UInt8 pbDecode = pbHeaderEncoding + EC_SizeOfFixedHeader; + if (hasFrameSize) { VarInt::SkipUnsigned(pbDecode); } + if (returnKind == MRK_ReturnsToNative) { VarInt::SkipUnsigned(pbDecode); } + if (hasExtraData) { VarInt::SkipUnsigned(pbDecode); } + if (HasDynamicAlignment()) { VarInt::SkipUnsigned(pbDecode); } + if (hasGSCookie) { VarInt::SkipUnsigned(pbDecode); } + +#ifdef TARGET_AMD64 + if (x64_framePtrOffsetSmall == 0x3) { VarInt::SkipUnsigned(pbDecode); } +#elif defined(TARGET_X86) + if (x86_argCountIsLarge) + pbDecode++; + + if (x86_hasStackChanges) + { + bool last = false; + while (!last) + { + uint8_t b = *pbDecode++; + // 00111111 {delta} forwarder + // 00dddddd push 1, dddddd = delta + // nnnldddd pop nnn-1, l = last, dddd = delta (nnn=0 and nnn=1 are disallowed) + if (b == 0x3F) + { + // 00111111 {delta} forwarder + VarInt::SkipUnsigned(pbDecode); + } + else if (0 != (b & 0xC0)) + { + // nnnldddd pop nnn-1, l = last, dddd = delta (nnn=0 and nnn=1 are disallowed) + last = ((b & 0x10) == 0x10); + } + } + } +#elif defined(TARGET_ARM) + if (arm_areParmOrVfpRegsPushed) { VarInt::SkipUnsigned(pbDecode); } +#elif defined(TARGET_ARM64) + if (arm64_longCsrMask) { pbDecode++; } + if (arm64_areParmOrVfpRegsPushed) { VarInt::SkipUnsigned(pbDecode); } +#endif + + *pnFuncletsOut = VarInt::ReadUnsigned(pbDecode); + *pEncodedFuncletStartOffsets = pbDecode; + } + + bool IsValidEpilogOffset(uint32_t epilogOffset, uint32_t epilogSize) + { + if (!this->HasVaryingEpilogSizes()) + return (epilogOffset < this->GetFixedEpilogSize()); + else + return (epilogOffset < epilogSize); + } +}; + +/*****************************************************************************/ +#endif //_GCINFO_H_ +/*****************************************************************************/ diff --git a/src/coreclr/nativeaot/Runtime/inc/rhbinder.h b/src/coreclr/nativeaot/Runtime/inc/rhbinder.h new file mode 100644 index 00000000000000..7a2503b8a7a16b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/rhbinder.h @@ -0,0 +1,647 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This header contains binder-generated data structures that the runtime consumes. +// +#include "TargetPtrs.h" + +class GcPollInfo +{ +public: + static const uint32_t indirCellsPerBitmapBit = 64 / POINTER_SIZE; // one cache line per bit + + static const uint32_t cbChunkCommonCode_X64 = 17; + static const uint32_t cbChunkCommonCode_X86 = 16; + static const uint32_t cbChunkCommonCode_ARM = 32; +#ifdef TARGET_ARM + // on ARM, the index of the indirection cell can be computed + // from the pointer to the indirection cell left in R12, + // thus we need only one entry point on ARM, + // thus entries take no space, and you can have as many as you want + static const uint32_t cbEntry = 0; + static const uint32_t cbBundleCommonCode = 0; + static const uint32_t entriesPerBundle = 0x7fffffff; + static const uint32_t bundlesPerChunk = 0x7fffffff; + static const uint32_t entriesPerChunk = 0x7fffffff; +#else + static const uint32_t cbEntry = 4; // push imm8 / jmp rel8 + static const uint32_t cbBundleCommonCode = 5; // jmp rel32 + + static const uint32_t entriesPerSubBundlePos = 32; // for the half with forward jumps + static const uint32_t entriesPerSubBundleNeg = 30; // for the half with negative jumps + static const uint32_t entriesPerBundle = entriesPerSubBundlePos + entriesPerSubBundleNeg; + static const uint32_t bundlesPerChunk = 4; + static const uint32_t entriesPerChunk = bundlesPerChunk * entriesPerBundle; +#endif + + static const uint32_t cbFullBundle = cbBundleCommonCode + + (entriesPerBundle * cbEntry); + + static uint32_t EntryIndexToStubOffset(uint32_t entryIndex) + { +# if defined(TARGET_ARM) + return EntryIndexToStubOffset(entryIndex, cbChunkCommonCode_ARM); +# elif defined(TARGET_AMD64) + return EntryIndexToStubOffset(entryIndex, cbChunkCommonCode_X64); +# else + return EntryIndexToStubOffset(entryIndex, cbChunkCommonCode_X86); +# endif + } + + static uint32_t EntryIndexToStubOffset(uint32_t entryIndex, uint32_t cbChunkCommonCode) + { +# if defined(TARGET_ARM) + UNREFERENCED_PARAMETER(entryIndex); + UNREFERENCED_PARAMETER(cbChunkCommonCode); + + return 0; +# else + uint32_t cbFullChunk = cbChunkCommonCode + + (bundlesPerChunk * cbBundleCommonCode) + + (entriesPerChunk * cbEntry); + + uint32_t numFullChunks = entryIndex / entriesPerChunk; + uint32_t numEntriesInLastChunk = entryIndex - (numFullChunks * entriesPerChunk); + + uint32_t numFullBundles = numEntriesInLastChunk / entriesPerBundle; + uint32_t numEntriesInLastBundle = numEntriesInLastChunk - (numFullBundles * entriesPerBundle); + + uint32_t offset = (numFullChunks * cbFullChunk) + + cbChunkCommonCode + + (numFullBundles * cbFullBundle) + + (numEntriesInLastBundle * cbEntry); + + if (numEntriesInLastBundle >= entriesPerSubBundlePos) + offset += cbBundleCommonCode; + + return offset; +# endif + } +}; + +struct StaticGcDesc +{ + struct GCSeries + { + uint32_t m_size; + uint32_t m_startOffset; + }; + + uint32_t m_numSeries; + GCSeries m_series[1]; + + uint32_t GetSize() + { + return (uint32_t)(offsetof(StaticGcDesc, m_series) + (m_numSeries * sizeof(GCSeries))); + } + +#ifdef DACCESS_COMPILE + static uint32_t DacSize(TADDR addr); +#endif +}; + +typedef SPTR(StaticGcDesc) PTR_StaticGcDesc; +typedef DPTR(StaticGcDesc::GCSeries) PTR_StaticGcDescGCSeries; + +class MethodTable; + +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH + +enum class DispatchCellType +{ + InterfaceAndSlot = 0x0, + MetadataToken = 0x1, + VTableOffset = 0x2, +}; + +struct DispatchCellInfo +{ + DispatchCellType CellType; + MethodTable *InterfaceType = nullptr; + uint16_t InterfaceSlot = 0; + uint8_t HasCache = 0; + uint32_t MetadataToken = 0; + uint32_t VTableOffset = 0; +}; + +struct InterfaceDispatchCacheHeader +{ +private: + enum Flags + { + CH_TypeAndSlotIndex = 0x0, + CH_MetadataToken = 0x1, + CH_Mask = 0x3, + CH_Shift = 0x2, + }; + +public: + void Initialize(MethodTable *pInterfaceType, uint16_t interfaceSlot, uint32_t metadataToken) + { + if (pInterfaceType != nullptr) + { + ASSERT(metadataToken == 0); + m_pInterfaceType = pInterfaceType; + m_slotIndexOrMetadataTokenEncoded = CH_TypeAndSlotIndex | (((uint32_t)interfaceSlot) << CH_Shift); + } + else + { + ASSERT(pInterfaceType == nullptr); + ASSERT(interfaceSlot == 0); + m_pInterfaceType = nullptr; + m_slotIndexOrMetadataTokenEncoded = CH_MetadataToken | (metadataToken << CH_Shift); + } + } + + void Initialize(const DispatchCellInfo *pCellInfo) + { + ASSERT((pCellInfo->CellType == DispatchCellType::InterfaceAndSlot) || + (pCellInfo->CellType == DispatchCellType::MetadataToken)); + if (pCellInfo->CellType == DispatchCellType::InterfaceAndSlot) + { + ASSERT(pCellInfo->MetadataToken == 0); + Initialize(pCellInfo->InterfaceType, pCellInfo->InterfaceSlot, 0); + } + else + { + ASSERT(pCellInfo->CellType == DispatchCellType::MetadataToken); + ASSERT(pCellInfo->InterfaceType == nullptr); + Initialize(nullptr, 0, pCellInfo->MetadataToken); + } + } + + DispatchCellInfo GetDispatchCellInfo() + { + DispatchCellInfo cellInfo; + + if ((m_slotIndexOrMetadataTokenEncoded & CH_Mask) == CH_TypeAndSlotIndex) + { + cellInfo.InterfaceType = m_pInterfaceType; + cellInfo.InterfaceSlot = (uint16_t)(m_slotIndexOrMetadataTokenEncoded >> CH_Shift); + cellInfo.CellType = DispatchCellType::InterfaceAndSlot; + } + else + { + cellInfo.MetadataToken = m_slotIndexOrMetadataTokenEncoded >> CH_Shift; + cellInfo.CellType = DispatchCellType::MetadataToken; + } + cellInfo.HasCache = 1; + return cellInfo; + } + +private: + MethodTable * m_pInterfaceType; // MethodTable of interface to dispatch on + uint32_t m_slotIndexOrMetadataTokenEncoded; +}; + +// One of these is allocated per interface call site. It holds the stub to call, data to pass to that stub +// (cache information) and the interface contract, i.e. the interface type and slot being called. +struct InterfaceDispatchCell +{ + // The first two fields must remain together and at the beginning of the structure. This is due to the + // synchronization requirements of the code that updates these at runtime and the instructions generated + // by the binder for interface call sites. + UIntTarget m_pStub; // Call this code to execute the interface dispatch + volatile UIntTarget m_pCache; // Context used by the stub above (one or both of the low two bits are set + // for initial dispatch, and if not set, using this as a cache pointer or + // as a vtable offset.) + // + // In addition, there is a Slot/Flag use of this field. DispatchCells are + // emitted as a group, and the final one in the group (identified by m_pStub + // having the null value) will have a Slot field is the low 16 bits of the + // m_pCache field, and in the second lowest 16 bits, a Flags field. For the interface + // case Flags shall be 0, and for the metadata token case, Flags shall be 1. + + // + // Keep these in sync with the managed copy in src\Common\src\Internal\Runtime\InterfaceCachePointerType.cs + // + enum Flags + { + // The low 2 bits of the m_pCache pointer are treated specially so that we can avoid the need for + // extra fields on this type. + // OR if the m_pCache value is less than 0x1000 then this it is a vtable offset and should be used as such + IDC_CachePointerIsInterfaceRelativePointer = 0x3, + IDC_CachePointerIsIndirectedInterfaceRelativePointer = 0x2, + IDC_CachePointerIsInterfacePointerOrMetadataToken = 0x1, // Metadata token is a 30 bit number in this case. + // Tokens are required to have at least one of their upper 20 bits set + // But they are not required by this part of the system to follow any specific + // token format + IDC_CachePointerPointsAtCache = 0x0, + IDC_CachePointerMask = 0x3, + IDC_CachePointerMaskShift = 0x2, + IDC_MaxVTableOffsetPlusOne = 0x1000, + }; + + DispatchCellInfo GetDispatchCellInfo() + { + // Capture m_pCache into a local for safe access (this is a volatile read of a value that may be + // modified on another thread while this function is executing.) + UIntTarget cachePointerValue = m_pCache; + DispatchCellInfo cellInfo; + + if ((cachePointerValue < IDC_MaxVTableOffsetPlusOne) && ((cachePointerValue & IDC_CachePointerMask) == IDC_CachePointerPointsAtCache)) + { + cellInfo.VTableOffset = (uint32_t)cachePointerValue; + cellInfo.CellType = DispatchCellType::VTableOffset; + cellInfo.HasCache = 1; + return cellInfo; + } + + // If there is a real cache pointer, grab the data from there. + if ((cachePointerValue & IDC_CachePointerMask) == IDC_CachePointerPointsAtCache) + { + return ((InterfaceDispatchCacheHeader*)cachePointerValue)->GetDispatchCellInfo(); + } + + // Otherwise, walk to cell with Flags and Slot field + + // The slot number/flags for a dispatch cell is encoded once per run of DispatchCells + // The run is terminated by having an dispatch cell with a null stub pointer. + const InterfaceDispatchCell *currentCell = this; + while (currentCell->m_pStub != 0) + { + currentCell = currentCell + 1; + } + UIntTarget cachePointerValueFlags = currentCell->m_pCache; + + DispatchCellType cellType = (DispatchCellType)(cachePointerValueFlags >> 16); + cellInfo.CellType = cellType; + + if (cellType == DispatchCellType::InterfaceAndSlot) + { + cellInfo.InterfaceSlot = (uint16_t)cachePointerValueFlags; + + switch (cachePointerValue & IDC_CachePointerMask) + { + case IDC_CachePointerIsInterfacePointerOrMetadataToken: + cellInfo.InterfaceType = (MethodTable*)(cachePointerValue & ~IDC_CachePointerMask); + break; + + case IDC_CachePointerIsInterfaceRelativePointer: + case IDC_CachePointerIsIndirectedInterfaceRelativePointer: + { + UIntTarget interfacePointerValue = (UIntTarget)&m_pCache + (int32_t)cachePointerValue; + interfacePointerValue &= ~IDC_CachePointerMask; + if ((cachePointerValue & IDC_CachePointerMask) == IDC_CachePointerIsInterfaceRelativePointer) + { + cellInfo.InterfaceType = (MethodTable*)interfacePointerValue; + } + else + { + cellInfo.InterfaceType = *(MethodTable**)interfacePointerValue; + } + } + break; + } + } + else + { + cellInfo.MetadataToken = (uint32_t)(cachePointerValue >> IDC_CachePointerMaskShift); + } + + return cellInfo; + } + + static bool IsCache(UIntTarget value) + { + if (((value & IDC_CachePointerMask) != 0) || (value < IDC_MaxVTableOffsetPlusOne)) + { + return false; + } + else + { + return true; + } + } + + InterfaceDispatchCacheHeader* GetCache() const + { + // Capture m_pCache into a local for safe access (this is a volatile read of a value that may be + // modified on another thread while this function is executing.) + UIntTarget cachePointerValue = m_pCache; + if (IsCache(cachePointerValue)) + { + return (InterfaceDispatchCacheHeader*)cachePointerValue; + } + else + { + return 0; + } + } +}; + +#endif // FEATURE_CACHED_INTERFACE_DISPATCH + +#ifdef TARGET_ARM +// Note for ARM: try and keep the flags in the low 16-bits, since they're not easy to load into a register in +// a single instruction within our stubs. +enum PInvokeTransitionFrameFlags +{ + // NOTE: Keep in sync with ndp\FxCore\CoreRT\src\Native\Runtime\arm\AsmMacros.h + + // NOTE: The order in which registers get pushed in the PInvokeTransitionFrame's m_PreservedRegs list has + // to match the order of these flags (that's also the order in which they are read in StackFrameIterator.cpp + + // standard preserved registers + PTFF_SAVE_R4 = 0x00000001, + PTFF_SAVE_R5 = 0x00000002, + PTFF_SAVE_R6 = 0x00000004, + PTFF_SAVE_R7 = 0x00000008, // should never be used, we require FP frames for methods with + // pinvoke and it is saved into the frame pointer field instead + PTFF_SAVE_R8 = 0x00000010, + PTFF_SAVE_R9 = 0x00000020, + PTFF_SAVE_R10 = 0x00000040, + PTFF_SAVE_SP = 0x00000100, // Used for 'coop pinvokes' in runtime helper routines. Methods with + // PInvokes are required to have a frame pointers, but methods which + // call runtime helpers are not. Therefore, methods that call runtime + // helpers may need SP to seed the stackwalk. + + // scratch registers + PTFF_SAVE_R0 = 0x00000200, + PTFF_SAVE_R1 = 0x00000400, + PTFF_SAVE_R2 = 0x00000800, + PTFF_SAVE_R3 = 0x00001000, + PTFF_SAVE_LR = 0x00002000, // this is useful for the case of loop hijacking where we need both + // a return address pointing into the hijacked method and that method's + // lr register, which may hold a gc pointer + + PTFF_R0_IS_GCREF = 0x00004000, // used by hijack handler to report return value of hijacked method + PTFF_R0_IS_BYREF = 0x00008000, // used by hijack handler to report return value of hijacked method + + PTFF_THREAD_ABORT = 0x00010000, // indicates that ThreadAbortException should be thrown when returning from the transition +}; +#elif defined(TARGET_ARM64) +enum PInvokeTransitionFrameFlags : uint64_t +{ + // NOTE: Keep in sync with ndp\FxCore\CoreRT\src\Native\Runtime\arm64\AsmMacros.h + + // NOTE: The order in which registers get pushed in the PInvokeTransitionFrame's m_PreservedRegs list has + // to match the order of these flags (that's also the order in which they are read in StackFrameIterator.cpp + + // standard preserved registers + PTFF_SAVE_X19 = 0x0000000000000001, + PTFF_SAVE_X20 = 0x0000000000000002, + PTFF_SAVE_X21 = 0x0000000000000004, + PTFF_SAVE_X22 = 0x0000000000000008, + PTFF_SAVE_X23 = 0x0000000000000010, + PTFF_SAVE_X24 = 0x0000000000000020, + PTFF_SAVE_X25 = 0x0000000000000040, + PTFF_SAVE_X26 = 0x0000000000000080, + PTFF_SAVE_X27 = 0x0000000000000100, + PTFF_SAVE_X28 = 0x0000000000000200, + + PTFF_SAVE_SP = 0x0000000000000400, // Used for 'coop pinvokes' in runtime helper routines. Methods with + // PInvokes are required to have a frame pointers, but methods which + // call runtime helpers are not. Therefore, methods that call runtime + // helpers may need SP to seed the stackwalk. + + // Scratch registers + PTFF_SAVE_X0 = 0x0000000000000800, + PTFF_SAVE_X1 = 0x0000000000001000, + PTFF_SAVE_X2 = 0x0000000000002000, + PTFF_SAVE_X3 = 0x0000000000004000, + PTFF_SAVE_X4 = 0x0000000000008000, + PTFF_SAVE_X5 = 0x0000000000010000, + PTFF_SAVE_X6 = 0x0000000000020000, + PTFF_SAVE_X7 = 0x0000000000040000, + PTFF_SAVE_X8 = 0x0000000000080000, + PTFF_SAVE_X9 = 0x0000000000100000, + PTFF_SAVE_X10 = 0x0000000000200000, + PTFF_SAVE_X11 = 0x0000000000400000, + PTFF_SAVE_X12 = 0x0000000000800000, + PTFF_SAVE_X13 = 0x0000000001000000, + PTFF_SAVE_X14 = 0x0000000002000000, + PTFF_SAVE_X15 = 0x0000000004000000, + PTFF_SAVE_X16 = 0x0000000008000000, + PTFF_SAVE_X17 = 0x0000000010000000, + PTFF_SAVE_X18 = 0x0000000020000000, + + PTFF_SAVE_FP = 0x0000000040000000, // should never be used, we require FP frames for methods with + // pinvoke and it is saved into the frame pointer field instead + + PTFF_SAVE_LR = 0x0000000080000000, // this is useful for the case of loop hijacking where we need both + // a return address pointing into the hijacked method and that method's + // lr register, which may hold a gc pointer + + // used by hijack handler to report return value of hijacked method + PTFF_X0_IS_GCREF = 0x0000000100000000, + PTFF_X0_IS_BYREF = 0x0000000200000000, + PTFF_X1_IS_GCREF = 0x0000000400000000, + PTFF_X1_IS_BYREF = 0x0000000800000000, + + PTFF_THREAD_ABORT = 0x0000001000000000, // indicates that ThreadAbortException should be thrown when returning from the transition +}; + +// TODO: Consider moving the PInvokeTransitionFrameFlags definition to a separate file to simplify header dependencies +#ifdef ICODEMANAGER_INCLUDED +// Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back +C_ASSERT(PTFF_X0_IS_GCREF == ((uint64_t)GCRK_Object << 32)); +C_ASSERT(PTFF_X0_IS_BYREF == ((uint64_t)GCRK_Byref << 32)); +C_ASSERT(PTFF_X1_IS_GCREF == ((uint64_t)GCRK_Scalar_Obj << 32)); +C_ASSERT(PTFF_X1_IS_BYREF == ((uint64_t)GCRK_Scalar_Byref << 32)); + +inline uint64_t ReturnKindToTransitionFrameFlags(GCRefKind returnKind) +{ + if (returnKind == GCRK_Scalar) + return 0; + + return PTFF_SAVE_X0 | PTFF_SAVE_X1 | ((uint64_t)returnKind << 32); +} + +inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) +{ + GCRefKind returnKind = (GCRefKind)((transFrameFlags & (PTFF_X0_IS_GCREF | PTFF_X0_IS_BYREF | PTFF_X1_IS_GCREF | PTFF_X1_IS_BYREF)) >> 32); + ASSERT((returnKind == GCRK_Scalar) || ((transFrameFlags & PTFF_SAVE_X0) && (transFrameFlags & PTFF_SAVE_X1))); + return returnKind; +} +#endif // ICODEMANAGER_INCLUDED +#else // TARGET_ARM +enum PInvokeTransitionFrameFlags +{ + // NOTE: Keep in sync with ndp\FxCore\CoreRT\src\Native\Runtime\[amd64|i386]\AsmMacros.inc + + // NOTE: The order in which registers get pushed in the PInvokeTransitionFrame's m_PreservedRegs list has + // to match the order of these flags (that's also the order in which they are read in StackFrameIterator.cpp + + // standard preserved registers + PTFF_SAVE_RBX = 0x00000001, + PTFF_SAVE_RSI = 0x00000002, + PTFF_SAVE_RDI = 0x00000004, + PTFF_SAVE_RBP = 0x00000008, // should never be used, we require RBP frames for methods with + // pinvoke and it is saved into the frame pointer field instead + PTFF_SAVE_R12 = 0x00000010, + PTFF_SAVE_R13 = 0x00000020, + PTFF_SAVE_R14 = 0x00000040, + PTFF_SAVE_R15 = 0x00000080, + + PTFF_SAVE_RSP = 0x00008000, // Used for 'coop pinvokes' in runtime helper routines. Methods with + // PInvokes are required to have a frame pointers, but methods which + // call runtime helpers are not. Therefore, methods that call runtime + // helpers may need RSP to seed the stackwalk. + // + // NOTE: despite the fact that this flag's bit is out of order, it is + // still expected to be saved here after the preserved registers and + // before the scratch registers + PTFF_SAVE_RAX = 0x00000100, + PTFF_SAVE_RCX = 0x00000200, + PTFF_SAVE_RDX = 0x00000400, + PTFF_SAVE_R8 = 0x00000800, + PTFF_SAVE_R9 = 0x00001000, + PTFF_SAVE_R10 = 0x00002000, + PTFF_SAVE_R11 = 0x00004000, + + PTFF_RAX_IS_GCREF = 0x00010000, // used by hijack handler to report return value of hijacked method + PTFF_RAX_IS_BYREF = 0x00020000, // used by hijack handler to report return value of hijacked method + + PTFF_THREAD_ABORT = 0x00040000, // indicates that ThreadAbortException should be thrown when returning from the transition +}; +#endif // TARGET_ARM + +#pragma warning(push) +#pragma warning(disable:4200) // nonstandard extension used: zero-sized array in struct/union +class Thread; +#if defined(USE_PORTABLE_HELPERS) +//the members of this structure are currently unused except m_pThread and exist only to allow compilation +//of StackFrameIterator their values are not currently being filled in and will require significant rework +//in order to satisfy the runtime requirements of StackFrameIterator +struct PInvokeTransitionFrame +{ + void* m_RIP; + Thread* m_pThread; // unused by stack crawler, this is so GetThread is only called once per method + // can be an invalid pointer in universal transition cases (which never need to call GetThread) + uint32_t m_Flags; // PInvokeTransitionFrameFlags +}; +#else // USE_PORTABLE_HELPERS +struct PInvokeTransitionFrame +{ +#ifdef TARGET_ARM + TgtPTR_Void m_ChainPointer; // R11, used by OS to walk stack quickly +#endif +#ifdef TARGET_ARM64 + // On arm64, the FP and LR registers are pushed in that order when setting up frames + TgtPTR_Void m_FramePointer; + TgtPTR_Void m_RIP; +#else + TgtPTR_Void m_RIP; + TgtPTR_Void m_FramePointer; +#endif + TgtPTR_Thread m_pThread; // unused by stack crawler, this is so GetThread is only called once per method + // can be an invalid pointer in universal transition cases (which never need to call GetThread) +#ifdef TARGET_ARM64 + uint64_t m_Flags; // PInvokeTransitionFrameFlags +#else + uint32_t m_Flags; // PInvokeTransitionFrameFlags +#endif + UIntTarget m_PreservedRegs[]; +}; +#endif // USE_PORTABLE_HELPERS +#pragma warning(pop) + +#ifdef TARGET_AMD64 +// RBX, RSI, RDI, R12, R13, R14, R15, RAX, RSP +#define PInvokeTransitionFrame_SaveRegs_count 9 +#elif defined(TARGET_X86) +// RBX, RSI, RDI, RAX, RSP +#define PInvokeTransitionFrame_SaveRegs_count 5 +#elif defined(TARGET_ARM) +// R4-R10, R0, SP +#define PInvokeTransitionFrame_SaveRegs_count 9 +#endif +#define PInvokeTransitionFrame_MAX_SIZE (sizeof(PInvokeTransitionFrame) + (POINTER_SIZE * PInvokeTransitionFrame_SaveRegs_count)) + +#ifdef TARGET_AMD64 +#define OFFSETOF__Thread__m_pTransitionFrame 0x40 +#elif defined(TARGET_ARM64) +#define OFFSETOF__Thread__m_pTransitionFrame 0x40 +#elif defined(TARGET_X86) +#define OFFSETOF__Thread__m_pTransitionFrame 0x2c +#elif defined(TARGET_ARM) +#define OFFSETOF__Thread__m_pTransitionFrame 0x2c +#endif + +typedef DPTR(MethodTable) PTR_EEType; +typedef DPTR(PTR_EEType) PTR_PTR_EEType; + +struct EETypeRef +{ + union + { + MethodTable * pEEType; + MethodTable ** ppEEType; + uint8_t * rawPtr; + UIntTarget rawTargetPtr; // x86_amd64: keeps union big enough for target-platform pointer + }; + + static const size_t DOUBLE_INDIR_FLAG = 1; + + PTR_EEType GetValue() + { + if (dac_cast(rawTargetPtr) & DOUBLE_INDIR_FLAG) + return *dac_cast(rawTargetPtr - DOUBLE_INDIR_FLAG); + else + return dac_cast(rawTargetPtr); + } +}; + +// Blobs are opaque data passed from the compiler, through the binder and into the native image. At runtime we +// provide a simple API to retrieve these blobs (they're keyed by a simple integer ID). Blobs are passed to +// the binder from the compiler and stored in native images by the binder in a sequential stream, each blob +// having the following header. +struct BlobHeader +{ + uint32_t m_flags; // Flags describing the blob (used by the binder only at the moment) + uint32_t m_id; // Unique identifier of the blob (used to access the blob at runtime) + // also used by BlobTypeFieldPreInit to identify (at bind time) which field to pre-init. + uint32_t m_size; // Size of the individual blob excluding this header (DWORD aligned) +}; + +#ifdef FEATURE_CUSTOM_IMPORTS +struct CustomImportDescriptor +{ + uint32_t RvaEATAddr; // RVA of the indirection cell of the address of the EAT for that module + uint32_t RvaIAT; // RVA of IAT array for that module + uint32_t CountIAT; // Count of entries in the above array +}; +#endif // FEATURE_CUSTOM_IMPORTS + +enum RhEHClauseKind +{ + RH_EH_CLAUSE_TYPED = 0, + RH_EH_CLAUSE_FAULT = 1, + RH_EH_CLAUSE_FILTER = 2, + RH_EH_CLAUSE_UNUSED = 3 +}; + +#define RH_EH_CLAUSE_TYPED_INDIRECT RH_EH_CLAUSE_UNUSED + +// mapping of cold code blocks to the corresponding hot entry point RVA +// format is a as follows: +// ------------------- +// | subSectionCount | # of subsections, where each subsection has a run of hot bodies +// ------------------- followed by a run of cold bodies +// | hotMethodCount | # of hot bodies in subsection +// | coldMethodCount | # of cold bodies in subsection +// ------------------- +// ... possibly repeated on ARM +// ------------------- +// | hotRVA #1 | RVA of the hot entry point corresponding to the 1st cold body +// | hotRVA #2 | RVA of the hot entry point corresponding to the 2nd cold body +// ... one entry for each cold body containing the corresponding hot entry point + +// number of hot and cold bodies in a subsection of code +// in x86 and x64 there's only one subsection, on ARM there may be several +// for large modules with > 16 MB of code +struct SubSectionDesc +{ + uint32_t hotMethodCount; + uint32_t coldMethodCount; +}; + +// this is the structure describing the cold to hot mapping info +struct ColdToHotMapping +{ + uint32_t subSectionCount; + SubSectionDesc subSection[/*subSectionCount*/1]; + // UINT32 hotRVAofColdMethod[/*coldMethodCount*/]; +}; diff --git a/src/coreclr/nativeaot/Runtime/inc/stressLog.h b/src/coreclr/nativeaot/Runtime/inc/stressLog.h new file mode 100644 index 00000000000000..cbfce6fa46216d --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/stressLog.h @@ -0,0 +1,836 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// --------------------------------------------------------------------------- +// StressLog.h +// +// StressLog infrastructure +// +// The StressLog is a binary, memory based circular queue of logging messages. +// It is intended to be used in retail builds during stress runs (activated +// by registry key), to help find bugs that only turn up during stress runs. +// +// Differently from the desktop implementation the RH implementation of the +// stress log will log all facilities, and only filter on logging level. +// +// The log has a very simple structure, and is meant to be dumped from an NTSD +// extention (eg. strike). +// +// debug\rhsos\stresslogdump.cpp contains the dumper utility that parses this +// log. +// --------------------------------------------------------------------------- + +#ifndef StressLog_h +#define StressLog_h 1 + +#define SUPPRESS_WARNING_4127 \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) /* conditional expression is constant*/ + +#define POP_WARNING_STATE \ + __pragma(warning(pop)) + +#define WHILE_0 \ + SUPPRESS_WARNING_4127 \ + while(0) \ + POP_WARNING_STATE \ + + +// let's keep STRESS_LOG defined always... +#if !defined(STRESS_LOG) && !defined(NO_STRESS_LOG) +#define STRESS_LOG +#endif + +#if defined(STRESS_LOG) + +// +// Logging levels and facilities +// +#define DEFINE_LOG_FACILITY(logname, value) logname = value, + +enum LogFacilitiesEnum: unsigned int { +#include "loglf.h" + LF_ALWAYS = 0x80000000u, // Log message irrepespective of LogFacility (if the level matches) + LF_ALL = 0xFFFFFFFFu, // Used only to mask bits. Never use as LOG((LF_ALL, ...)) +}; + + +#define LL_EVERYTHING 10 +#define LL_INFO1000000 9 // can be expected to generate 1,000,000 logs per small but not trival run +#define LL_INFO100000 8 // can be expected to generate 100,000 logs per small but not trival run +#define LL_INFO10000 7 // can be expected to generate 10,000 logs per small but not trival run +#define LL_INFO1000 6 // can be expected to generate 1,000 logs per small but not trival run +#define LL_INFO100 5 // can be expected to generate 100 logs per small but not trival run +#define LL_INFO10 4 // can be expected to generate 10 logs per small but not trival run +#define LL_WARNING 3 +#define LL_ERROR 2 +#define LL_FATALERROR 1 +#define LL_ALWAYS 0 // impossible to turn off (log level never negative) + +// +// +// + +#ifndef _ASSERTE +#define _ASSERTE(expr) +#endif + + +#ifndef DACCESS_COMPILE + + +//========================================================================================== +// The STRESS_LOG* macros +// +// The STRESS_LOG* macros work like printf. In fact the use printf in their implementation +// so all printf format specifications work. In addition the Stress log dumper knows +// about certain suffixes for the %p format specification (normally used to print a pointer) +// +// %pM // The pointer is a MethodInfo -- not supported yet (use %pK instead) +// %pT // The pointer is a type (MethodTable) +// %pV // The pointer is a C++ Vtable pointer +// %pK // The pointer is a code address (used for call stacks or method names) +// + +// STRESS_LOG_VA was added to allow sending GC trace output to the stress log. msg must be enclosed +// in ()'s and contain a format string followed by 0 - 4 arguments. The arguments must be numbers or +// string literals. LogMsgOL is overloaded so that all of the possible sets of parameters are covered. +// This was done becasue GC Trace uses dprintf which dosen't contain info on how many arguments are +// getting passed in and using va_args would require parsing the format string during the GC +// + +#define STRESS_LOG_VA(msg) do { \ + if (StressLog::StressLogOn(LF_GC, LL_ALWAYS)) \ + StressLog::LogMsgOL msg; \ + } WHILE_0 + +#define STRESS_LOG0(facility, level, msg) do { \ + if (StressLog::StressLogOn(facility, level)) \ + StressLog::LogMsg(facility, 0, msg); \ + } WHILE_0 \ + +#define STRESS_LOG1(facility, level, msg, data1) do { \ + if (StressLog::StressLogOn(facility, level)) \ + StressLog::LogMsg(facility, 1, msg, (void*)(size_t)(data1)); \ + } WHILE_0 + +#define STRESS_LOG2(facility, level, msg, data1, data2) do { \ + if (StressLog::StressLogOn(facility, level)) \ + StressLog::LogMsg(facility, 2, msg, \ + (void*)(size_t)(data1), (void*)(size_t)(data2)); \ + } WHILE_0 + +#define STRESS_LOG3(facility, level, msg, data1, data2, data3) do { \ + if (StressLog::StressLogOn(facility, level)) \ + StressLog::LogMsg(facility, 3, msg, \ + (void*)(size_t)(data1),(void*)(size_t)(data2),(void*)(size_t)(data3)); \ + } WHILE_0 + +#define STRESS_LOG4(facility, level, msg, data1, data2, data3, data4) do { \ + if (StressLog::StressLogOn(facility, level)) \ + StressLog::LogMsg(facility, 4, msg, (void*)(size_t)(data1), \ + (void*)(size_t)(data2),(void*)(size_t)(data3),(void*)(size_t)(data4)); \ + } WHILE_0 + +#define STRESS_LOG5(facility, level, msg, data1, data2, data3, data4, data5) do { \ + if (StressLog::StressLogOn(facility, level)) \ + StressLog::LogMsg(facility, 5, msg, (void*)(size_t)(data1), \ + (void*)(size_t)(data2),(void*)(size_t)(data3),(void*)(size_t)(data4), \ + (void*)(size_t)(data5)); \ + } WHILE_0 + +#define STRESS_LOG6(facility, level, msg, data1, data2, data3, data4, data5, data6) do { \ + if (StressLog::StressLogOn(facility, level)) \ + StressLog::LogMsg(facility, 6, msg, (void*)(size_t)(data1), \ + (void*)(size_t)(data2),(void*)(size_t)(data3),(void*)(size_t)(data4), \ + (void*)(size_t)(data5), (void*)(size_t)(data6)); \ + } WHILE_0 + +#define STRESS_LOG7(facility, level, msg, data1, data2, data3, data4, data5, data6, data7) do { \ + if (StressLog::StressLogOn(facility, level)) \ + StressLog::LogMsg(facility, 7, msg, (void*)(size_t)(data1), \ + (void*)(size_t)(data2),(void*)(size_t)(data3),(void*)(size_t)(data4), \ + (void*)(size_t)(data5), (void*)(size_t)(data6), (void*)(size_t)(data7)); \ + } WHILE_0 + +#define STRESS_LOG_COND0(facility, level, msg) do { \ + if (StressLog::StressLogOn(facility, level) && (cond)) \ + StressLog::LogMsg(facility, 0, msg); \ + } WHILE_0 + +#define STRESS_LOG_COND1(facility, level, cond, msg, data1) do { \ + if (StressLog::StressLogOn(facility, level) && (cond)) \ + StressLog::LogMsg(facility, 1, msg, (void*)(size_t)(data1)); \ + } WHILE_0 + +#define STRESS_LOG_COND2(facility, level, cond, msg, data1, data2) do { \ + if (StressLog::StressLogOn(facility, level) && (cond)) \ + StressLog::LogMsg(facility, 2, msg, \ + (void*)(size_t)(data1), (void*)(size_t)(data2)); \ + } WHILE_0 + +#define STRESS_LOG_COND3(facility, level, cond, msg, data1, data2, data3) do { \ + if (StressLog::StressLogOn(facility, level) && (cond)) \ + StressLog::LogMsg(facility, 3, msg, \ + (void*)(size_t)(data1),(void*)(size_t)(data2),(void*)(size_t)(data3)); \ + } WHILE_0 + +#define STRESS_LOG_COND4(facility, level, cond, msg, data1, data2, data3, data4) do { \ + if (StressLog::StressLogOn(facility, level) && (cond)) \ + StressLog::LogMsg(facility, 4, msg, (void*)(size_t)(data1), \ + (void*)(size_t)(data2),(void*)(size_t)(data3),(void*)(size_t)(data4)); \ + } WHILE_0 + +#define STRESS_LOG_COND5(facility, level, cond, msg, data1, data2, data3, data4, data5) do { \ + if (StressLog::StressLogOn(facility, level) && (cond)) \ + StressLog::LogMsg(facility, 5, msg, (void*)(size_t)(data1), \ + (void*)(size_t)(data2),(void*)(size_t)(data3),(void*)(size_t)(data4), \ + (void*)(size_t)(data5)); \ + } WHILE_0 + +#define STRESS_LOG_COND6(facility, level, cond, msg, data1, data2, data3, data4, data5, data6) do { \ + if (StressLog::StressLogOn(facility, level) && (cond)) \ + StressLog::LogMsg(facility, 6, msg, (void*)(size_t)(data1), \ + (void*)(size_t)(data2),(void*)(size_t)(data3),(void*)(size_t)(data4), \ + (void*)(size_t)(data5), (void*)(size_t)(data6)); \ + } WHILE_0 + +#define STRESS_LOG_COND7(facility, level, cond, msg, data1, data2, data3, data4, data5, data6, data7) do { \ + if (StressLog::StressLogOn(facility, level) && (cond)) \ + StressLog::LogMsg(facility, 7, msg, (void*)(size_t)(data1), \ + (void*)(size_t)(data2),(void*)(size_t)(data3),(void*)(size_t)(data4), \ + (void*)(size_t)(data5), (void*)(size_t)(data6), (void*)(size_t)(data7)); \ + } WHILE_0 + +#define STRESS_LOG_RESERVE_MEM(numChunks) do { \ + if (StressLog::StressLogOn(LF_ALL, LL_ALWAYS)) \ + {StressLog::ReserveStressLogChunks (numChunks);} \ + } WHILE_0 + +// !!! WARNING !!! +// !!! DO NOT ADD STRESS_LOG8, as the stress log infrastructure supports a maximum of 7 arguments +// !!! WARNING !!! + +#define STRESS_LOG_PLUG_MOVE(plug_start, plug_end, plug_delta) do { \ + if (StressLog::StressLogOn(LF_GC, LL_INFO1000)) \ + StressLog::LogMsg(LF_GC, 3, ThreadStressLog::gcPlugMoveMsg(), \ + (void*)(size_t)(plug_start), (void*)(size_t)(plug_end), (void*)(size_t)(plug_delta)); \ + } WHILE_0 + +#define STRESS_LOG_ROOT_PROMOTE(root_addr, objPtr, methodTable) do { \ + if (StressLog::StressLogOn(LF_GC|LF_GCROOTS, LL_INFO1000)) \ + StressLog::LogMsg(LF_GC|LF_GCROOTS, 3, ThreadStressLog::gcRootPromoteMsg(), \ + (void*)(size_t)(root_addr), (void*)(size_t)(objPtr), (void*)(size_t)(methodTable)); \ + } WHILE_0 + +#define STRESS_LOG_ROOT_RELOCATE(root_addr, old_value, new_value, methodTable) do { \ + if (StressLog::StressLogOn(LF_GC|LF_GCROOTS, LL_INFO1000) && ((size_t)(old_value) != (size_t)(new_value))) \ + StressLog::LogMsg(LF_GC|LF_GCROOTS, 4, ThreadStressLog::gcRootMsg(), \ + (void*)(size_t)(root_addr), (void*)(size_t)(old_value), \ + (void*)(size_t)(new_value), (void*)(size_t)(methodTable)); \ + } WHILE_0 + +#define STRESS_LOG_GC_START(gcCount, Gen, collectClasses) do { \ + if (StressLog::StressLogOn(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10)) \ + StressLog::LogMsg(LF_GCROOTS|LF_GC|LF_GCALLOC, 3, ThreadStressLog::gcStartMsg(), \ + (void*)(size_t)(gcCount), (void*)(size_t)(Gen), (void*)(size_t)(collectClasses)); \ + } WHILE_0 + +#define STRESS_LOG_GC_END(gcCount, Gen, collectClasses) do { \ + if (StressLog::StressLogOn(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10)) \ + StressLog::LogMsg(LF_GCROOTS|LF_GC|LF_GCALLOC, 3, ThreadStressLog::gcEndMsg(),\ + (void*)(size_t)(gcCount), (void*)(size_t)(Gen), (void*)(size_t)(collectClasses), 0);\ + } WHILE_0 + +#if defined(_DEBUG) +#define MAX_CALL_STACK_TRACE 20 +#define STRESS_LOG_OOM_STACK(size) do { \ + if (StressLog::StressLogOn(LF_ALWAYS, LL_ALWAYS)) \ + { \ + StressLog::LogMsgOL("OOM on alloc of size %x \n", (void*)(size_t)(size)); \ + StressLog::LogCallStack ("OOM"); \ + } \ + } WHILE_0 +#define STRESS_LOG_GC_STACK do { \ + if (StressLog::StressLogOn(LF_GC |LF_GCINFO, LL_ALWAYS)) \ + { \ + StressLog::LogMsgOL("GC is triggered \n"); \ + StressLog::LogCallStack ("GC"); \ + } \ + } WHILE_0 +#else //_DEBUG +#define STRESS_LOG_OOM_STACK(size) +#define STRESS_LOG_GC_STACK +#endif //_DEBUG + +#endif // DACCESS_COMPILE + +// +// forward declarations: +// +class CrstStatic; +class Thread; +typedef DPTR(Thread) PTR_Thread; +class StressLog; +typedef DPTR(StressLog) PTR_StressLog; +class ThreadStressLog; +typedef DPTR(ThreadStressLog) PTR_ThreadStressLog; +struct StressLogChunk; +typedef DPTR(StressLogChunk) PTR_StressLogChunk; +struct DacpStressLogEnumCBArgs; +extern "C" void PopulateDebugHeaders(); + + +//========================================================================================== +// StressLog - per-thread circular queue of stresslog messages +// +class StressLog { + friend void PopulateDebugHeaders(); +public: +// private: + unsigned facilitiesToLog; // Bitvector of facilities to log (see loglf.h) + unsigned levelToLog; // log level + unsigned MaxSizePerThread; // maximum number of bytes each thread should have before wrapping + unsigned MaxSizeTotal; // maximum memory allowed for stress log + int32_t totalChunk; // current number of total chunks allocated + PTR_ThreadStressLog logs; // the list of logs for every thread. + int32_t deadCount; // count of dead threads in the log + CrstStatic *pLock; // lock + unsigned __int64 tickFrequency; // number of ticks per second + unsigned __int64 startTimeStamp; // start time from when tick counter started + FILETIME startTime; // time the application started + size_t moduleOffset; // Used to compute format strings. + +#ifndef DACCESS_COMPILE +public: + static void Initialize(unsigned facilities, unsigned level, unsigned maxBytesPerThread, + unsigned maxBytesTotal, HANDLE hMod); + // Called at DllMain THREAD_DETACH to recycle thread's logs + static void ThreadDetach(ThreadStressLog *msgs); + static long NewChunk () { return PalInterlockedIncrement (&theLog.totalChunk); } + static long ChunkDeleted () { return PalInterlockedDecrement (&theLog.totalChunk); } + + //the result is not 100% accurate. If multiple threads call this funciton at the same time, + //we could allow the total size be bigger than required. But the memory won't grow forever + //and this is not critical so we don't try to fix the race + static bool AllowNewChunk (long numChunksInCurThread); + + //preallocate Stress log chunks for current thread. The memory we could preallocate is still + //bounded by per thread size limit and total size limit. If chunksToReserve is 0, we will try to + //preallocate up to per thread size limit + static bool ReserveStressLogChunks (unsigned int chunksToReserve); + +// private: + static ThreadStressLog* CreateThreadStressLog(Thread * pThread); + static ThreadStressLog* CreateThreadStressLogHelper(Thread * pThread); + +#else // DACCESS_COMPILE +public: + bool Initialize(); + + // Can't refer to the types in sospriv.h because it drags in windows.h + void EnumerateStressMsgs(/*STRESSMSGCALLBACK*/ void* smcb, /*ENDTHREADLOGCALLBACK*/ void* etcb, + void *token); + void EnumStressLogMemRanges(/*STRESSLOGMEMRANGECALLBACK*/ void* slmrcb, void *token); + + // Called while dumping logs after operations are completed, to ensure DAC-caches + // allow the stress logs to be dumped again + void ResetForRead(); + + ThreadStressLog* FindLatestThreadLog() const; + + friend class ClrDataAccess; + +#endif // DACCESS_COMPILE + +#ifndef DACCESS_COMPILE +public: + FORCEINLINE static bool StressLogOn(unsigned /*facility*/, unsigned level) + { + #if defined(DACCESS_COMPILE) + UNREFERENCED_PARAMETER(level); + return FALSE; + #else + // In Redhawk we have rationalized facility codes and have much + // fewer compared to desktop, as such we'll log all facilities and + // limit the filtering to the log level... + return + // (theLog.facilitiesToLog & facility) + // && + (level <= theLog.levelToLog); + #endif + } + + static void LogMsg(unsigned facility, int cArgs, const char* format, ... ); + + // Support functions for STRESS_LOG_VA + // We disable the warning "conversion from 'type' to 'type' of greater size" since everything will + // end up on the stack, and LogMsg will know the size of the variable based on the format string. + #ifdef _MSC_VER + #pragma warning( push ) + #pragma warning( disable : 4312 ) + #endif + static void LogMsgOL(const char* format) + { LogMsg(LF_GC, 0, format); } + + template < typename T1 > + static void LogMsgOL(const char* format, T1 data1) + { + C_ASSERT(sizeof(T1) <= sizeof(void*)); + LogMsg(LF_GC, 1, format, (void*)(size_t)data1); + } + + template < typename T1, typename T2 > + static void LogMsgOL(const char* format, T1 data1, T2 data2) + { + C_ASSERT(sizeof(T1) <= sizeof(void*) && sizeof(T2) <= sizeof(void*)); + LogMsg(LF_GC, 2, format, (void*)(size_t)data1, (void*)(size_t)data2); + } + + template < typename T1, typename T2, typename T3 > + static void LogMsgOL(const char* format, T1 data1, T2 data2, T3 data3) + { + C_ASSERT(sizeof(T1) <= sizeof(void*) && sizeof(T2) <= sizeof(void*) && sizeof(T3) <= sizeof(void*)); + LogMsg(LF_GC, 3, format, (void*)(size_t)data1, (void*)(size_t)data2, (void*)(size_t)data3); + } + + template < typename T1, typename T2, typename T3, typename T4 > + static void LogMsgOL(const char* format, T1 data1, T2 data2, T3 data3, T4 data4) + { + C_ASSERT(sizeof(T1) <= sizeof(void*) && sizeof(T2) <= sizeof(void*) && sizeof(T3) <= sizeof(void*) && sizeof(T4) <= sizeof(void*)); + LogMsg(LF_GC, 4, format, (void*)(size_t)data1, (void*)(size_t)data2, (void*)(size_t)data3, (void*)(size_t)data4); + } + + template < typename T1, typename T2, typename T3, typename T4, typename T5 > + static void LogMsgOL(const char* format, T1 data1, T2 data2, T3 data3, T4 data4, T5 data5) + { + C_ASSERT(sizeof(T1) <= sizeof(void*) && sizeof(T2) <= sizeof(void*) && sizeof(T3) <= sizeof(void*) && sizeof(T4) <= sizeof(void*) && sizeof(T5) <= sizeof(void*)); + LogMsg(LF_GC, 5, format, (void*)(size_t)data1, (void*)(size_t)data2, (void*)(size_t)data3, (void*)(size_t)data4, (void*)(size_t)data5); + } + + template < typename T1, typename T2, typename T3, typename T4, typename T5, typename T6 > + static void LogMsgOL(const char* format, T1 data1, T2 data2, T3 data3, T4 data4, T5 data5, T6 data6) + { + C_ASSERT(sizeof(T1) <= sizeof(void*) && sizeof(T2) <= sizeof(void*) && sizeof(T3) <= sizeof(void*) && sizeof(T4) <= sizeof(void*) && sizeof(T5) <= sizeof(void*) && sizeof(T6) <= sizeof(void*)); + LogMsg(LF_GC, 6, format, (void*)(size_t)data1, (void*)(size_t)data2, (void*)(size_t)data3, (void*)(size_t)data4, (void*)(size_t)data5, (void*)(size_t)data6); + } + + template < typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7 > + static void LogMsgOL(const char* format, T1 data1, T2 data2, T3 data3, T4 data4, T5 data5, T6 data6, T7 data7) + { + C_ASSERT(sizeof(T1) <= sizeof(void*) && sizeof(T2) <= sizeof(void*) && sizeof(T3) <= sizeof(void*) && sizeof(T4) <= sizeof(void*) && sizeof(T5) <= sizeof(void*) && sizeof(T6) <= sizeof(void*) && sizeof(T7) <= sizeof(void*)); + LogMsg(LF_GC, 7, format, (void*)(size_t)data1, (void*)(size_t)data2, (void*)(size_t)data3, (void*)(size_t)data4, (void*)(size_t)data5, (void*)(size_t)data6, (void*)(size_t)data7); + } + + #ifdef _MSC_VER + #pragma warning( pop ) + #endif + +// We can only log the stacktrace on DEBUG builds! +#ifdef _DEBUG + static void LogCallStack(const char *const callTag); +#endif //_DEBUG + +#endif // DACCESS_COMPILE + +// private: // static variables + static StressLog theLog; // We only have one log, and this is it +}; + + +//========================================================================================== +// Private classes +// + +#if defined(_MSC_VER) +// don't warn about 0 sized array below or unnamed structures +#pragma warning(disable:4200 4201) +#endif + +//========================================================================================== +// StressMsg +// +// The order of fields is important. Keep the prefix length as the first field. +// And make sure the timeStamp field is naturally aligned, so we don't waste +// space on 32-bit platforms +// +struct StressMsg { + union { + struct { + uint32_t numberOfArgs : 3; // at most 7 arguments + uint32_t formatOffset : 29; // offset of string in mscorwks + }; + uint32_t fmtOffsCArgs; // for optimized access + }; + uint32_t facility; // facility used to log the entry + unsigned __int64 timeStamp; // time when mssg was logged + void* args[0]; // size given by numberOfArgs + + static const size_t maxArgCnt = 7; + static const size_t maxOffset = 0x20000000; + static size_t maxMsgSize () + { return sizeof(StressMsg) + maxArgCnt*sizeof(void*); } + + friend void PopulateDebugHeaders(); + friend class ThreadStressLog; + friend class StressLog; +}; + +#ifdef _WIN64 +#define STRESSLOG_CHUNK_SIZE (32 * 1024) +#else //_WIN64 +#define STRESSLOG_CHUNK_SIZE (16 * 1024) +#endif //_WIN64 +#define GC_STRESSLOG_MULTIPLY (5) + +//========================================================================================== +// StressLogChunk +// +// A chunk of contiguous memory containing instances of StressMsg +// +struct StressLogChunk +{ + PTR_StressLogChunk prev; + PTR_StressLogChunk next; + char buf[STRESSLOG_CHUNK_SIZE]; + uint32_t dwSig1; + uint32_t dwSig2; + +#ifndef DACCESS_COMPILE + + StressLogChunk (PTR_StressLogChunk p = NULL, PTR_StressLogChunk n = NULL) + :prev (p), next (n), dwSig1 (0xCFCFCFCF), dwSig2 (0xCFCFCFCF) + {} + +#endif //!DACCESS_COMPILE + + char * StartPtr () + { + return buf; + } + + char * EndPtr () + { + return buf + STRESSLOG_CHUNK_SIZE; + } + + bool IsValid () const + { + return dwSig1 == 0xCFCFCFCF && dwSig2 == 0xCFCFCFCF; + } +}; + +//========================================================================================== +// ThreadStressLog +// +// This class implements a circular stack of variable sized elements +// .The buffer between startPtr-endPtr is used in a circular manner +// to store instances of the variable-sized struct StressMsg. +// The StressMsg are always aligned to endPtr, while the space +// left between startPtr and the last element is 0-padded. +// .curPtr points to the most recently written log message +// .readPtr points to the next log message to be dumped +// .hasWrapped is TRUE while dumping the log, if we had wrapped +// past the endPtr marker, back to startPtr +// The AdvanceRead/AdvanceWrite operations simply update the +// readPtr / curPtr fields. thecaller is responsible for reading/writing +// to the corresponding field +class ThreadStressLog { + PTR_ThreadStressLog next; // we keep a linked list of these + uint64_t threadId; // the id for the thread using this buffer + bool isDead; // Is this thread dead + bool readHasWrapped; // set when read ptr has passed chunListTail + bool writeHasWrapped; // set when write ptr has passed chunListHead + StressMsg* curPtr; // where packets are being put on the queue + StressMsg* readPtr; // where we are reading off the queue (used during dumping) + PTR_StressLogChunk chunkListHead; //head of a list of stress log chunks + PTR_StressLogChunk chunkListTail; //tail of a list of stress log chunks + PTR_StressLogChunk curReadChunk; //the stress log chunk we are currently reading + PTR_StressLogChunk curWriteChunk; //the stress log chunk we are currently writing + long chunkListLength; // how many stress log chunks are in this stress log + PTR_Thread pThread; // thread associated with these stress logs + StressMsg * origCurPtr; // this holds the original curPtr before we start the dump + + friend void PopulateDebugHeaders(); + friend class StressLog; + +#ifndef DACCESS_COMPILE +public: + inline ThreadStressLog (); + inline ~ThreadStressLog (); + + void LogMsg ( uint32_t facility, int cArgs, const char* format, ... ) + { + va_list Args; + va_start(Args, format); + LogMsg (facility, cArgs, format, Args); + } + + void LogMsg ( uint32_t facility, int cArgs, const char* format, va_list Args); + +private: + FORCEINLINE StressMsg* AdvanceWrite(int cArgs); + inline StressMsg* AdvWritePastBoundary(int cArgs); + FORCEINLINE bool GrowChunkList (); + +#else // DACCESS_COMPILE +public: + friend class ClrDataAccess; + + // Called while dumping. Returns true after all messages in log were dumped + FORCEINLINE bool CompletedDump (); + +private: + FORCEINLINE bool IsReadyForRead() { return readPtr != NULL; } + FORCEINLINE StressMsg* AdvanceRead(); + inline StressMsg* AdvReadPastBoundary(); +#endif //!DACCESS_COMPILE + +public: + void Activate (Thread * pThread); + + bool IsValid () const + { + return chunkListHead != NULL && (!curWriteChunk || curWriteChunk->IsValid ()); + } + + static const char* gcStartMsg() + { + return "{ =========== BEGINGC %d, (requested generation = %lu, collect_classes = %lu) ==========\n"; + } + + static const char* gcEndMsg() + { + return "========== ENDGC %d (gen = %lu, collect_classes = %lu) ===========}\n"; + } + + static const char* gcRootMsg() + { + return " GC Root %p RELOCATED %p -> %p MT = %pT\n"; + } + + static const char* gcRootPromoteMsg() + { + return " GCHeap::Promote: Promote GC Root *%p = %p MT = %pT\n"; + } + + static const char* gcPlugMoveMsg() + { + return "GC_HEAP RELOCATING Objects in heap within range [%p %p) by -0x%x bytes\n"; + } + +}; + + +//========================================================================================== +// Inline implementations: +// + +#ifdef DACCESS_COMPILE + +//------------------------------------------------------------------------------------------ +// Called while dumping. Returns true after all messages in log were dumped +FORCEINLINE bool ThreadStressLog::CompletedDump () +{ + return readPtr->timeStamp == 0 + //if read has passed end of list but write has not passed head of list yet, we are done + //if write has also wrapped, we are at the end if read pointer passed write pointer + || (readHasWrapped && + (!writeHasWrapped || (curReadChunk == curWriteChunk && readPtr >= curPtr))); +} + +//------------------------------------------------------------------------------------------ +// Called when dumping the log (by StressLog::Dump()) +// Updates readPtr to point to next stress messaage to be dumped +inline StressMsg* ThreadStressLog::AdvanceRead() { + // advance the marker + readPtr = (StressMsg*)((char*)readPtr + sizeof(StressMsg) + readPtr->numberOfArgs*sizeof(void*)); + // wrap around if we need to + if (readPtr >= (StressMsg *)curReadChunk->EndPtr ()) + { + AdvReadPastBoundary(); + } + return readPtr; +} + +//------------------------------------------------------------------------------------------ +// The factored-out slow codepath for AdvanceRead(), only called by AdvanceRead(). +// Updates readPtr to and returns the first stress message >= startPtr +inline StressMsg* ThreadStressLog::AdvReadPastBoundary() { + //if we pass boundary of tail list, we need to set has Wrapped + if (curReadChunk == chunkListTail) + { + readHasWrapped = true; + //If write has not wrapped, we know the contents from list head to + //cur pointer is garbage, we don't need to read them + if (!writeHasWrapped) + { + return readPtr; + } + } + curReadChunk = curReadChunk->next; + void** p = (void**)curReadChunk->StartPtr(); + while (*p == NULL && (size_t)(p-(void**)curReadChunk->StartPtr ()) < (StressMsg::maxMsgSize()/sizeof(void*))) + { + ++p; + } + // if we failed to find a valid start of a StressMsg fallback to startPtr (since timeStamp==0) + if (*p == NULL) + { + p = (void**) curReadChunk->StartPtr (); + } + readPtr = (StressMsg*)p; + + return readPtr; +} + +#else // DACCESS_COMPILE + +//------------------------------------------------------------------------------------------ +// Initialize a ThreadStressLog +inline ThreadStressLog::ThreadStressLog() +{ + chunkListHead = chunkListTail = curWriteChunk = NULL; + StressLogChunk * newChunk = new (nothrow) StressLogChunk; + //OOM or in cantalloc region + if (newChunk == NULL) + { + return; + } + StressLog::NewChunk (); + + newChunk->prev = newChunk; + newChunk->next = newChunk; + + chunkListHead = chunkListTail = newChunk; + + next = NULL; + isDead = TRUE; + curPtr = NULL; + readPtr = NULL; + writeHasWrapped = FALSE; + curReadChunk = NULL; + curWriteChunk = NULL; + chunkListLength = 1; + origCurPtr = NULL; +} + +inline ThreadStressLog::~ThreadStressLog () +{ + //no thing to do if the list is empty (failed to initialize) + if (chunkListHead == NULL) + { + return; + } + + StressLogChunk * chunk = chunkListHead; + + do + { + StressLogChunk * tmp = chunk; + chunk = chunk->next; + delete tmp; + StressLog::ChunkDeleted (); + } while (chunk != chunkListHead); +} + +//------------------------------------------------------------------------------------------ +// Called when logging, checks if we can increase the number of stress log chunks associated +// with the current thread +FORCEINLINE bool ThreadStressLog::GrowChunkList () +{ + _ASSERTE (chunkListLength >= 1); + if (!StressLog::AllowNewChunk (chunkListLength)) + { + return FALSE; + } + StressLogChunk * newChunk = new (nothrow) StressLogChunk (chunkListTail, chunkListHead); + if (newChunk == NULL) + { + return FALSE; + } + StressLog::NewChunk (); + chunkListLength++; + chunkListHead->prev = newChunk; + chunkListTail->next = newChunk; + chunkListHead = newChunk; + + return TRUE; +} + +//------------------------------------------------------------------------------------------ +// Called at runtime when writing the log (by StressLog::LogMsg()) +// Updates curPtr to point to the next spot in the log where we can write +// a stress message with cArgs arguments +// For convenience it returns a pointer to the empty slot where we can +// write the next stress message. +// cArgs is the number of arguments in the message to be written. +inline StressMsg* ThreadStressLog::AdvanceWrite(int cArgs) { + // _ASSERTE(cArgs <= StressMsg::maxArgCnt); + // advance the marker + StressMsg* p = (StressMsg*)((char*)curPtr - sizeof(StressMsg) - cArgs*sizeof(void*)); + + //past start of current chunk + //wrap around if we need to + if (p < (StressMsg*)curWriteChunk->StartPtr ()) + { + curPtr = AdvWritePastBoundary(cArgs); + } + else + { + curPtr = p; + } + + return curPtr; +} + +//------------------------------------------------------------------------------------------ +// This is the factored-out slow codepath for AdvanceWrite() and is only called by +// AdvanceWrite(). +// Returns the stress message flushed against endPtr +// In addition it writes NULLs b/w the startPtr and curPtr +inline StressMsg* ThreadStressLog::AdvWritePastBoundary(int cArgs) { + //zeroed out remaining buffer + memset (curWriteChunk->StartPtr (), 0, (char *)curPtr - (char *)curWriteChunk->StartPtr ()); + + //if we are already at head of the list, try to grow the list + if (curWriteChunk == chunkListHead) + { + GrowChunkList (); + } + + curWriteChunk = curWriteChunk->prev; + if (curWriteChunk == chunkListTail) + { + writeHasWrapped = TRUE; + } + curPtr = (StressMsg*)((char*)curWriteChunk->EndPtr () - sizeof(StressMsg) - cArgs * sizeof(void*)); + return curPtr; +} + +#endif // DACCESS_COMPILE + +#endif // STRESS_LOG + +#ifndef __GCENV_BASE_INCLUDED__ +#if !defined(STRESS_LOG) || defined(DACCESS_COMPILE) +#define STRESS_LOG_VA(msg) do { } WHILE_0 +#define STRESS_LOG0(facility, level, msg) do { } WHILE_0 +#define STRESS_LOG1(facility, level, msg, data1) do { } WHILE_0 +#define STRESS_LOG2(facility, level, msg, data1, data2) do { } WHILE_0 +#define STRESS_LOG3(facility, level, msg, data1, data2, data3) do { } WHILE_0 +#define STRESS_LOG4(facility, level, msg, data1, data2, data3, data4) do { } WHILE_0 +#define STRESS_LOG5(facility, level, msg, data1, data2, data3, data4, data5) do { } WHILE_0 +#define STRESS_LOG6(facility, level, msg, data1, data2, data3, data4, data5, data6) do { } WHILE_0 +#define STRESS_LOG7(facility, level, msg, data1, data2, data3, data4, data5, data6, data7) do { } WHILE_0 +#define STRESS_LOG_PLUG_MOVE(plug_start, plug_end, plug_delta) do { } WHILE_0 +#define STRESS_LOG_ROOT_PROMOTE(root_addr, objPtr, methodTable) do { } WHILE_0 +#define STRESS_LOG_ROOT_RELOCATE(root_addr, old_value, new_value, methodTable) do { } WHILE_0 +#define STRESS_LOG_GC_START(gcCount, Gen, collectClasses) do { } WHILE_0 +#define STRESS_LOG_GC_END(gcCount, Gen, collectClasses) do { } WHILE_0 +#define STRESS_LOG_OOM_STACK(size) do { } WHILE_0 +#define STRESS_LOG_GC_STACK do { } WHILE_0 +#define STRESS_LOG_RESERVE_MEM(numChunks) do { } WHILE_0 +#endif // !STRESS_LOG || DACCESS_COMPILE +#endif // !__GCENV_BASE_INCLUDED__ + +#endif // StressLog_h diff --git a/src/coreclr/nativeaot/Runtime/inc/type_traits.hpp b/src/coreclr/nativeaot/Runtime/inc/type_traits.hpp new file mode 100644 index 00000000000000..0bd237aa1bc2e0 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/type_traits.hpp @@ -0,0 +1,311 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// type_traits.hpp +// +// Type trait metaprogramming utilities. +// + +#ifndef __TYPE_TRAITS_HPP__ +#define __TYPE_TRAITS_HPP__ + +#include "CommonTypes.h" + +namespace type_traits +{ + +namespace imp +{ + +struct true_type { static const bool value = true; }; +struct false_type { static const bool value = false; }; + +//////////////////////////////////////////////////////////////////////////////// +// Helper types Small and Big - guarantee that sizeof(Small) < sizeof(Big) +// + +template +struct conversion_helper +{ + typedef char Small; + struct Big { char dummy[2]; }; + static Big Test(...); + static Small Test(U); + static T MakeT(); +}; + +//////////////////////////////////////////////////////////////////////////////// +// class template conversion +// Figures out the conversion relationships between two types +// Invocations (T and U are types): +// a) conversion::exists +// returns (at compile time) true if there is an implicit conversion from T +// to U (example: Derived to Base) +// b) conversion::exists2Way +// returns (at compile time) true if there are both conversions from T +// to U and from U to T (example: int to char and back) +// c) conversion::sameType +// returns (at compile time) true if T and U represent the same type +// +// NOTE: might not work if T and U are in a private inheritance hierarchy. +// + +template +struct conversion +{ + typedef imp::conversion_helper H; + static const bool exists = sizeof(typename H::Small) == sizeof((H::Test(H::MakeT()))); + static const bool exists2Way = exists && conversion::exists; + static const bool sameType = false; +}; + +template +struct conversion +{ + static const bool exists = true; + static const bool exists2Way = true; + static const bool sameType = true; +}; + +template +struct conversion +{ + static const bool exists = false; + static const bool exists2Way = false; + static const bool sameType = false; +}; + +template +struct conversion +{ + static const bool exists = false; + static const bool exists2Way = false; + static const bool sameType = false; +}; + +template <> +struct conversion +{ + static const bool exists = true; + static const bool exists2Way = true; + static const bool sameType = true; +}; + +template +struct is_base_of_helper; + +template <> +struct is_base_of_helper : public true_type {} ; + +template <> +struct is_base_of_helper : public false_type {} ; + +}// imp + +//////////////////////////////////////////////////////////////////////////////// +// is_base_of::value is typedefed to be true if TDerived derives from TBase +// and false otherwise. +// +// +// NOTE: use TR1 type_traits::is_base_of when available. +// +#ifdef _MSC_VER + +template +struct is_base_of : public imp::is_base_of_helper<__is_base_of( TBase, TDerived)> {}; + +#else + +// Note that we need to compare pointer types here, since conversion of types by-value +// just tells us whether or not an implicit conversion constructor exists. We handle +// type parameters that are already pointers specially; see below. +template +struct is_base_of : public imp::is_base_of_helper::exists> {}; + +// Specialization to handle type parameters that are already pointers. +template +struct is_base_of : public imp::is_base_of_helper::exists> {}; + +// Specialization to handle invalid mixing of pointer types. +template +struct is_base_of : public imp::false_type {}; + +// Specialization to handle invalid mixing of pointer types. +template +struct is_base_of : public imp::false_type {}; + +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Remove const qualifications, if any. Access using remove_const::type +// +template struct remove_const { typedef T type; }; +template struct remove_const { typedef T type; }; + +//////////////////////////////////////////////////////////////////////////////// +// is_signed::value is true if T is a signed integral type, false otherwise. +// +template +struct is_signed { static const bool value = (static_cast(-1) < 0); }; + +} + +//////////////////////////////////////////////////////////////////////////////// +// These are related to type traits, but they are more like asserts of type +// traits in that the result is that either the compiler does or does not +// produce an error. +// +namespace type_constraints +{ + +//////////////////////////////////////////////////////////////////////////////// +// derived_from will produce a compiler error if TDerived does not +// derive from TBase. +// +// NOTE: use TR1 type_traits::is_base_of when available. +// + +template struct is_base_of +{ + is_base_of() + { + static_assert((type_traits::is_base_of::value), + "is_base_of() constraint violation: TDerived does not derive from TBase"); + } +}; + +}; // namespace type_constraints + +namespace rh { namespace std +{ + // Import some select components of the STL + + // TEMPLATE FUNCTION for_each + template + inline + _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func) + { // perform function for each element + for (; _First != _Last; ++_First) + _Func(*_First); + return (_Func); + } + + template + inline + _InIt find(_InIt _First, _InIt _Last, const _Ty& _Val) + { // find first matching _Val + for (; _First != _Last; ++_First) + if (*_First == _Val) + break; + return (_First); + } + + template + inline + _InIt find_if(_InIt _First, _InIt _Last, _Pr _Pred) + { // find first satisfying _Pred + for (; _First != _Last; ++_First) + if (_Pred(*_First)) + break; + return (_First); + } + + template + inline + bool exists(_InIt _First, _InIt _Last, const _Ty& _Val) + { + return find(_First, _Last, _Val) != _Last; + } + + template + inline + bool exists_if(_InIt _First, _InIt _Last, _Pr _Pred) + { + return find_if(_First, _Last, _Pred) != _Last; + } + + template + inline + uintptr_t count(_InIt _First, _InIt _Last, const _Ty& _Val) + { + uintptr_t _Ret = 0; + for (; _First != _Last; _First++) + if (*_First == _Val) + ++_Ret; + return _Ret; + } + + template + inline + uintptr_t count_if(_InIt _First, _InIt _Last, _Pr _Pred) + { + uintptr_t _Ret = 0; + for (; _First != _Last; _First++) + if (_Pred(*_First)) + ++_Ret; + return _Ret; + } + + // Forward declaration, each collection requires specialization + template + inline + _FwdIt remove(_FwdIt _First, _FwdIt _Last, const _Ty& _Val); +} // namespace std +} // namespace rh + +#if 0 + +// ----------------------------------------------------------------- +// Holding place for unused-but-possibly-useful-in-the-future code. + +// ------------------------------------------------- +// This belongs in type_traits.hpp + +// +// is_pointer::value is true if the type is a pointer, false otherwise +// +template struct is_pointer : public false_type {}; +template struct is_pointer : public true_type {}; + +// +// Remove pointer from type, if it has one. Use remove_pointer::type +// Further specialized in daccess.h +// +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; + +// ------------------------------------------------- +// This belongs in daccess.h + +namespace type_traits +{ + +// +// is_pointer::value is true if the type is a pointer, false otherwise +// specialized from type_traits.hpp +// +template struct is_pointer > : public type_traits::true_type {}; + +// +// remove_pointer::type is T with one less pointer qualification, if it had one. +// specialized from type_traits.hpp +// +template struct remove_pointer > { typedef T type; }; + +} // type_traits + +namespace dac +{ + +// +// is_dptr::value is true if T is a __DPtr, false otherwise. +// This is a partial specialization case for the positive case. +// +//template struct is_dptr > : public type_traits::true_type {}; + +} + +#endif + +#endif + diff --git a/src/coreclr/nativeaot/Runtime/inc/varint.h b/src/coreclr/nativeaot/Runtime/inc/varint.h new file mode 100644 index 00000000000000..4dfe7cf21da4f5 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/varint.h @@ -0,0 +1,144 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +class VarInt +{ +public: + static uint32_t ReadUnsigned(PTR_UInt8 & pbEncoding) + { + uintptr_t lengthBits = *pbEncoding & 0x0F; + size_t negLength = s_negLengthTab[lengthBits]; + uintptr_t shift = s_shiftTab[lengthBits]; + uint32_t result = *(PTR_UInt32)(pbEncoding - negLength - 4); + + result >>= shift; + pbEncoding -= negLength; + + return result; + } + + // + // WARNING: This method returns the negative of the length of the value that it just skipped! + // + // This was helpful in the GC info scan loop because it allowed us to always skip past unsigned values in + // the body of the loop. At the end of loop, we use this negative sign to distinguish between two cases + // and that allows us to decode the unsigned value that we need outside of the loop. Note that we encode + // the negatives in the s_negLengthTable to avoid any additional operations in the body of the GC scan + // loop. + // + static intptr_t SkipUnsigned(PTR_UInt8 & pbEncoding) + { + uintptr_t lengthBits = *pbEncoding & 0x0F; + size_t negLength = s_negLengthTab[lengthBits]; + pbEncoding -= negLength; + return negLength; + } + + static uintptr_t WriteUnsigned(PTR_UInt8 pbDest, uint32_t value) + { + if (pbDest == NULL) + { + if (value < 128) + return 1; + + if (value < 128*128) + return 2; + + if (value < 128*128*128) + return 3; + + if (value < 128*128*128*128) + return 4; + + return 5; + } + + if (value < 128) + { + *pbDest++ = (uint8_t)(value*2 + 0); + return 1; + } + + if (value < 128*128) + { + *pbDest++ = (uint8_t)(value*4 + 1); + *pbDest++ = (uint8_t)(value >> 6); + return 2; + } + + if (value < 128*128*128) + { + *pbDest++ = (uint8_t)(value*8 + 3); + *pbDest++ = (uint8_t)(value >> 5); + *pbDest++ = (uint8_t)(value >> 13); + return 3; + } + + if (value < 128*128*128*128) + { + *pbDest++ = (uint8_t)(value*16 + 7); + *pbDest++ = (uint8_t)(value >> 4); + *pbDest++ = (uint8_t)(value >> 12); + *pbDest++ = (uint8_t)(value >> 20); + return 4; + } + + *pbDest++ = 15; + *pbDest++ = (uint8_t)value; + *pbDest++ = (uint8_t)(value >> 8); + *pbDest++ = (uint8_t)(value >> 16); + *pbDest++ = (uint8_t)(value >> 24); + return 5; + } + +private: + static int8_t s_negLengthTab[16]; + static uint8_t s_shiftTab[16]; +}; + +__declspec(selectany) +int8_t VarInt::s_negLengthTab[16] = +{ + -1, // 0 + -2, // 1 + -1, // 2 + -3, // 3 + + -1, // 4 + -2, // 5 + -1, // 6 + -4, // 7 + + -1, // 8 + -2, // 9 + -1, // 10 + -3, // 11 + + -1, // 12 + -2, // 13 + -1, // 14 + -5, // 15 +}; + +__declspec(selectany) +uint8_t VarInt::s_shiftTab[16] = +{ + 32-7*1, // 0 + 32-7*2, // 1 + 32-7*1, // 2 + 32-7*3, // 3 + + 32-7*1, // 4 + 32-7*2, // 5 + 32-7*1, // 6 + 32-7*4, // 7 + + 32-7*1, // 8 + 32-7*2, // 9 + 32-7*1, // 10 + 32-7*3, // 11 + + 32-7*1, // 12 + 32-7*2, // 13 + 32-7*1, // 14 + 0, // 15 +}; diff --git a/src/coreclr/nativeaot/Runtime/loglf.h b/src/coreclr/nativeaot/Runtime/loglf.h new file mode 100644 index 00000000000000..ff6abd88b9ff74 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/loglf.h @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// The code in sos.DumpStressLog depends on the facility codes +// being bit flags sorted in increasing order. +// See code:EEStartup#TableOfContents for EE overview +DEFINE_LOG_FACILITY(LF_GC ,0x00000001) +DEFINE_LOG_FACILITY(LF_GCINFO ,0x00000002) +DEFINE_LOG_FACILITY(LF_GCALLOC ,0x00000004) +DEFINE_LOG_FACILITY(LF_GCROOTS ,0x00000008) +DEFINE_LOG_FACILITY(LF_STARTUP ,0x00000010) // Log startup and shutdown failures +DEFINE_LOG_FACILITY(LF_STACKWALK ,0x00000020) +// LF_ALWAYS 0x80000000 // make certain you don't try to use this bit for a real facility +// LF_ALL 0xFFFFFFFF +// +#undef DEFINE_LOG_FACILITY + diff --git a/src/coreclr/nativeaot/Runtime/portable.cpp b/src/coreclr/nativeaot/Runtime/portable.cpp new file mode 100644 index 00000000000000..5ff51c6807c2de --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/portable.cpp @@ -0,0 +1,576 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" + +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "CommonMacros.inl" +#include "volatile.h" +#include "PalRedhawk.h" +#include "rhassert.h" + +#include "slist.h" +#include "gcrhinterface.h" +#include "shash.h" +#include "RWLock.h" +#include "varint.h" +#include "holder.h" +#include "rhbinder.h" +#include "Crst.h" +#include "RuntimeInstance.h" +#include "event.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "threadstore.h" +#include "threadstore.inl" + +#include "MethodTable.h" +#include "TypeManager.h" +#include "MethodTable.inl" +#include "ObjectLayout.h" + +#include "GCMemoryHelpers.h" +#include "GCMemoryHelpers.inl" + +#if defined(USE_PORTABLE_HELPERS) +EXTERN_C REDHAWK_API void* REDHAWK_CALLCONV RhpGcAlloc(MethodTable *pEEType, uint32_t uFlags, uintptr_t numElements, void * pTransitionFrame); + +struct gc_alloc_context +{ + uint8_t* alloc_ptr; + uint8_t* alloc_limit; + __int64 alloc_bytes; //Number of bytes allocated on SOH by this context + __int64 alloc_bytes_loh; //Number of bytes allocated on LOH by this context + void* gc_reserved_1; + void* gc_reserved_2; + int alloc_count; +}; + +// +// Allocations +// +COOP_PINVOKE_HELPER(Object *, RhpNewFast, (MethodTable* pEEType)) +{ + ASSERT(!pEEType->HasFinalizer()); + + Thread * pCurThread = ThreadStore::GetCurrentThread(); + gc_alloc_context * acontext = pCurThread->GetAllocContext(); + Object * pObject; + + size_t size = pEEType->get_BaseSize(); + + uint8_t* alloc_ptr = acontext->alloc_ptr; + ASSERT(alloc_ptr <= acontext->alloc_limit); + if ((size_t)(acontext->alloc_limit - alloc_ptr) >= size) + { + acontext->alloc_ptr = alloc_ptr + size; + pObject = (Object *)alloc_ptr; + pObject->set_EEType(pEEType); + return pObject; + } + + pObject = (Object *)RhpGcAlloc(pEEType, 0, size, NULL); + if (pObject == nullptr) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw OOM + } + + return pObject; +} + +#define GC_ALLOC_FINALIZE 0x1 // TODO: Defined in gc.h +#define GC_ALLOC_ALIGN8_BIAS 0x4 // TODO: Defined in gc.h +#define GC_ALLOC_ALIGN8 0x8 // TODO: Defined in gc.h + +COOP_PINVOKE_HELPER(Object *, RhpNewFinalizable, (MethodTable* pEEType)) +{ + ASSERT(pEEType->HasFinalizer()); + + size_t size = pEEType->get_BaseSize(); + + Object * pObject = (Object *)RhpGcAlloc(pEEType, GC_ALLOC_FINALIZE, size, NULL); + if (pObject == nullptr) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw OOM + } + + return pObject; +} + +COOP_PINVOKE_HELPER(Array *, RhpNewArray, (MethodTable * pArrayEEType, int numElements)) +{ + Thread * pCurThread = ThreadStore::GetCurrentThread(); + gc_alloc_context * acontext = pCurThread->GetAllocContext(); + Array * pObject; + + if (numElements < 0) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw overflow + } + + size_t size; +#ifndef HOST_64BIT + // if the element count is <= 0x10000, no overflow is possible because the component size is + // <= 0xffff, and thus the product is <= 0xffff0000, and the base size is only ~12 bytes + if (numElements > 0x10000) + { + // Perform the size computation using 64-bit integeres to detect overflow + uint64_t size64 = (uint64_t)pArrayEEType->get_BaseSize() + ((uint64_t)numElements * (uint64_t)pArrayEEType->get_ComponentSize()); + size64 = (size64 + (sizeof(uintptr_t)-1)) & ~(sizeof(uintptr_t)-1); + + size = (size_t)size64; + if (size != size64) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw overflow + } + } + else +#endif // !HOST_64BIT + { + size = (size_t)pArrayEEType->get_BaseSize() + ((size_t)numElements * (size_t)pArrayEEType->get_ComponentSize()); + size = ALIGN_UP(size, sizeof(uintptr_t)); + } + + uint8_t* alloc_ptr = acontext->alloc_ptr; + ASSERT(alloc_ptr <= acontext->alloc_limit); + if ((size_t)(acontext->alloc_limit - alloc_ptr) >= size) + { + acontext->alloc_ptr = alloc_ptr + size; + pObject = (Array *)alloc_ptr; + pObject->set_EEType(pArrayEEType); + pObject->InitArrayLength((uint32_t)numElements); + return pObject; + } + + pObject = (Array *)RhpGcAlloc(pArrayEEType, 0, size, NULL); + if (pObject == nullptr) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw OOM + } + + return pObject; +} + +COOP_PINVOKE_HELPER(String *, RhNewString, (MethodTable * pArrayEEType, int numElements)) +{ + // TODO: Implement. We tail call to RhpNewArray for now since there's a bunch of TODOs in the places + // that matter anyway. + return (String*)RhpNewArray(pArrayEEType, numElements); +} + +#endif +#if defined(USE_PORTABLE_HELPERS) +#if defined(FEATURE_64BIT_ALIGNMENT) + +GPTR_DECL(MethodTable, g_pFreeObjectEEType); + +COOP_PINVOKE_HELPER(Object *, RhpNewFinalizableAlign8, (MethodTable* pEEType)) +{ + Object * pObject = nullptr; + /* Not reachable as finalizable types are never align8 */ ASSERT_UNCONDITIONALLY("UNREACHABLE"); + return pObject; +} + +#ifndef HOST_64BIT +COOP_PINVOKE_HELPER(Object *, RhpNewFastAlign8, (MethodTable* pEEType)) +{ + ASSERT(!pEEType->HasFinalizer()); + + Thread* pCurThread = ThreadStore::GetCurrentThread(); + gc_alloc_context* acontext = pCurThread->GetAllocContext(); + Object* pObject; + + size_t size = pEEType->get_BaseSize(); + size = (size + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1); + + uint8_t* result = acontext->alloc_ptr; + + int requiresPadding = ((uint32_t)result) & 7; + size_t paddedSize = size; + if (requiresPadding) + { + if(paddedSize > SIZE_MAX - 12) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw overflow + } + paddedSize += 12; + } + + uint8_t* alloc_ptr = acontext->alloc_ptr; + ASSERT(alloc_ptr <= acontext->alloc_limit); + if ((size_t)(acontext->alloc_limit - alloc_ptr) >= paddedSize) + { + acontext->alloc_ptr = alloc_ptr + paddedSize; + if (requiresPadding) + { + Object* dummy = (Object*)alloc_ptr; + dummy->set_EEType(g_pFreeObjectEEType); + alloc_ptr += 12; // if result + paddedSize was ok, then cant overflow + } + pObject = (Object *)alloc_ptr; + pObject->set_EEType(pEEType); + return pObject; + } + + pObject = (Object*)RhpGcAlloc(pEEType, GC_ALLOC_ALIGN8, size, NULL); + if (pObject == nullptr) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw OOM + } + + return pObject; +} + +COOP_PINVOKE_HELPER(Object*, RhpNewFastMisalign, (MethodTable* pEEType)) +{ + Thread* pCurThread = ThreadStore::GetCurrentThread(); + gc_alloc_context* acontext = pCurThread->GetAllocContext(); + Object* pObject; + + size_t size = pEEType->get_BaseSize(); + uint8_t* result = acontext->alloc_ptr; + + int requiresPadding = (((uint32_t)result) & 7) != 4; + size_t paddedSize = size; + if (requiresPadding) + { + if(paddedSize > SIZE_MAX - 12) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw overflow + } + paddedSize += 12; + } + uint8_t* alloc_ptr = acontext->alloc_ptr; + ASSERT(alloc_ptr <= acontext->alloc_limit); + if ((size_t)(acontext->alloc_limit - alloc_ptr) >= paddedSize) + { + acontext->alloc_ptr = alloc_ptr + paddedSize; + if (requiresPadding) + { + Object* dummy = (Object*)alloc_ptr; + dummy->set_EEType(g_pFreeObjectEEType); + alloc_ptr += 12; // if result + paddedSize was ok, then cant overflow + } + pObject = (Object *)alloc_ptr; + pObject->set_EEType(pEEType); + return pObject; + } + + pObject = (Object*)RhpGcAlloc(pEEType, GC_ALLOC_ALIGN8 | GC_ALLOC_ALIGN8_BIAS, size, NULL); + if (pObject == nullptr) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw OOM + } + + return pObject; +} + +COOP_PINVOKE_HELPER(Array *, RhpNewArrayAlign8, (MethodTable * pArrayEEType, int numElements)) +{ + Thread* pCurThread = ThreadStore::GetCurrentThread(); + gc_alloc_context* acontext = pCurThread->GetAllocContext(); + Array* pObject; + + if (numElements < 0) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw overflow + } + + size_t size; + + uint32_t baseSize = pArrayEEType->get_BaseSize(); + // if the element count is <= 0x10000, no overflow is possible because the component size is + // <= 0xffff, and thus the product is <= 0xffff0000, and the base size is only ~12 bytes + if (numElements > 0x10000) + { + // Perform the size computation using 64-bit integeres to detect overflow + uint64_t size64 = (uint64_t)baseSize + ((uint64_t)numElements * (uint64_t)pArrayEEType->get_ComponentSize()); + size64 = (size64 + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1); + + size = (size_t)size64; + if (size != size64) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw overflow + } + } + else + { + size = (size_t)baseSize + ((size_t)numElements * (size_t)pArrayEEType->get_ComponentSize()); + size = ALIGN_UP(size, sizeof(uintptr_t)); + } + uint8_t* result = acontext->alloc_ptr; + int requiresAlignObject = ((uint32_t)result) & 7; + size_t paddedSize = size; + if (requiresAlignObject) + { + if(paddedSize > SIZE_MAX - 12) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw overflow + } + paddedSize += 12; + } + uint8_t* alloc_ptr = acontext->alloc_ptr; + ASSERT(alloc_ptr <= acontext->alloc_limit); + if ((size_t)(acontext->alloc_limit - alloc_ptr) >= paddedSize) + { + acontext->alloc_ptr = alloc_ptr + paddedSize; + if (requiresAlignObject) + { + Object* dummy = (Object*)alloc_ptr; + dummy->set_EEType(g_pFreeObjectEEType); + alloc_ptr += 12; // if result + paddedSize was ok, then cant overflow + } + pObject = (Array*)alloc_ptr; + pObject->set_EEType(pArrayEEType); + pObject->InitArrayLength((uint32_t)numElements); + return pObject; + } + + pObject = (Array*)RhpGcAlloc(pArrayEEType, GC_ALLOC_ALIGN8, size, NULL); + if (pObject == nullptr) + { + ASSERT_UNCONDITIONALLY("NYI"); // TODO: Throw OOM + } + + return pObject; +} +#endif // !HOST_64BIT +#endif // defined(HOST_ARM) || defined(HOST_WASM) + +COOP_PINVOKE_HELPER(void, RhpInitialDynamicInterfaceDispatch, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch1, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch2, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch4, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch8, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch16, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch32, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpInterfaceDispatch64, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void, RhpVTableOffsetDispatch, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +// @TODO Implement UniversalTransition +EXTERN_C void * ReturnFromUniversalTransition; +void * ReturnFromUniversalTransition; + +// @TODO Implement UniversalTransition_DebugStepTailCall +EXTERN_C void * ReturnFromUniversalTransition_DebugStepTailCall; +void * ReturnFromUniversalTransition_DebugStepTailCall; + +#endif // USE_PORTABLE_HELPERS + +// @TODO Implement CallDescrThunk +EXTERN_C void * ReturnFromCallDescrThunk; +#ifdef USE_PORTABLE_HELPERS +void * ReturnFromCallDescrThunk; +#endif + +#if defined(USE_PORTABLE_HELPERS) || defined(TARGET_UNIX) +// +// Return address hijacking +// +#if !defined (HOST_ARM64) +COOP_PINVOKE_HELPER(void, RhpGcProbeHijackScalar, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} +COOP_PINVOKE_HELPER(void, RhpGcProbeHijackObject, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} +COOP_PINVOKE_HELPER(void, RhpGcProbeHijackByref, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} +COOP_PINVOKE_HELPER(void, RhpGcStressHijackScalar, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} +COOP_PINVOKE_HELPER(void, RhpGcStressHijackObject, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} +COOP_PINVOKE_HELPER(void, RhpGcStressHijackByref, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} +#else // !defined (HOST_ARM64) +COOP_PINVOKE_HELPER(void, RhpGcProbeHijack, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} +COOP_PINVOKE_HELPER(void, RhpGcStressHijack, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} +#endif // !defined (HOST_ARM64) +#endif // defined(USE_PORTABLE_HELPERS) || defined(TARGET_UNIX) + +#if defined(USE_PORTABLE_HELPERS) + +#if !defined (HOST_ARM64) +COOP_PINVOKE_HELPER(void, RhpAssignRef, (Object ** dst, Object * ref)) +{ + // @TODO: USE_PORTABLE_HELPERS - Null check + *dst = ref; + InlineWriteBarrier(dst, ref); +} + +COOP_PINVOKE_HELPER(void, RhpCheckedAssignRef, (Object ** dst, Object * ref)) +{ + // @TODO: USE_PORTABLE_HELPERS - Null check + *dst = ref; + InlineCheckedWriteBarrier(dst, ref); +} +#endif + +COOP_PINVOKE_HELPER(Object *, RhpCheckedLockCmpXchg, (Object ** location, Object * value, Object * comparand)) +{ + // @TODO: USE_PORTABLE_HELPERS - Null check + Object * ret = (Object *)PalInterlockedCompareExchangePointer((void * volatile *)location, value, comparand); + InlineCheckedWriteBarrier(location, value); + return ret; +} + +COOP_PINVOKE_HELPER(Object *, RhpCheckedXchg, (Object ** location, Object * value)) +{ + // @TODO: USE_PORTABLE_HELPERS - Null check + Object * ret = (Object *)PalInterlockedExchangePointer((void * volatile *)location, value); + InlineCheckedWriteBarrier(location, value); + return ret; +} + +COOP_PINVOKE_HELPER(int32_t, RhpLockCmpXchg32, (int32_t * location, int32_t value, int32_t comparand)) +{ + // @TODO: USE_PORTABLE_HELPERS - Null check + return PalInterlockedCompareExchange(location, value, comparand); +} + +COOP_PINVOKE_HELPER(int64_t, RhpLockCmpXchg64, (int64_t * location, int64_t value, int64_t comparand)) +{ + // @TODO: USE_PORTABLE_HELPERS - Null check + return PalInterlockedCompareExchange64(location, value, comparand); +} + +#endif // USE_PORTABLE_HELPERS + +COOP_PINVOKE_HELPER(void, RhpMemoryBarrier, ()) +{ + PalMemoryBarrier(); +} + +#if defined(USE_PORTABLE_HELPERS) +EXTERN_C REDHAWK_API void* __cdecl RhAllocateThunksMapping() +{ + return NULL; +} + +COOP_PINVOKE_HELPER(void *, RhpGetThunksBase, ()) +{ + return NULL; +} + +COOP_PINVOKE_HELPER(int, RhpGetNumThunkBlocksPerMapping, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); + return 0; +} + +COOP_PINVOKE_HELPER(int, RhpGetNumThunksPerBlock, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); + return 0; +} + +COOP_PINVOKE_HELPER(int, RhpGetThunkSize, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); + return 0; +} + +COOP_PINVOKE_HELPER(void*, RhpGetThunkDataBlockAddress, (void* pThunkStubAddress)) +{ + ASSERT_UNCONDITIONALLY("NYI"); + return NULL; +} + +COOP_PINVOKE_HELPER(void*, RhpGetThunkStubsBlockAddress, (void* pThunkDataAddress)) +{ + ASSERT_UNCONDITIONALLY("NYI"); + return NULL; +} + +COOP_PINVOKE_HELPER(int, RhpGetThunkBlockSize, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); + return NULL; +} + +COOP_PINVOKE_HELPER(void, RhCallDescrWorker, (void * callDescr)) +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +#ifdef CALLDESCR_FPARGREGSARERETURNREGS +COOP_PINVOKE_HELPER(void, CallingConventionConverter_GetStubs, (uintptr_t* pReturnVoidStub, uintptr_t* pReturnIntegerStub, uintptr_t* pCommonStub)) +#else +COOP_PINVOKE_HELPER(void, CallingConventionConverter_GetStubs, (uintptr_t* pReturnVoidStub, uintptr_t* pReturnIntegerStub, uintptr_t* pCommonStub, uintptr_t* pReturnFloatingPointReturn4Thunk, uintptr_t* pReturnFloatingPointReturn8Thunk)) +#endif +{ + ASSERT_UNCONDITIONALLY("NYI"); +} + +COOP_PINVOKE_HELPER(void *, RhGetCommonStubAddress, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); + return NULL; +} + +COOP_PINVOKE_HELPER(void *, RhGetCurrentThunkContext, ()) +{ + ASSERT_UNCONDITIONALLY("NYI"); + return NULL; +} + +COOP_PINVOKE_HELPER(void, RhpGcPoll, ()) +{ + // TODO: implement +} + +#endif diff --git a/src/coreclr/nativeaot/Runtime/profheapwalkhelper.cpp b/src/coreclr/nativeaot/Runtime/profheapwalkhelper.cpp new file mode 100644 index 00000000000000..2f87718aa31fcf --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/profheapwalkhelper.cpp @@ -0,0 +1,210 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// On desktop CLR, GC ETW event firing borrows heavily from code in the profiling API, +// as the GC already called hooks in the profapi to notify it of roots & references. +// This file shims up that profapi code the GC expects, though only for the purpose of +// firing ETW events (not for getting a full profapi up on redhawk). +// + +#include "common.h" + +#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) + +#include "gcenv.h" +#include "gcheaputilities.h" +#include "eventtrace.h" +#include "profheapwalkhelper.h" + +//--------------------------------------------------------------------------------------- +// +// Callback of type promote_func called by GC while scanning roots (in GCProfileWalkHeap, +// called after the collection). Wrapper around EEToProfInterfaceImpl::RootReference2, +// which does the real work. +// +// Arguments: +// pObj - Object reference encountered +/// ppRoot - Address that references ppObject (can be interior pointer) +// pSC - ProfilingScanContext * containing the root kind and GCReferencesData used +// by RootReference2 +// dwFlags - Properties of the root as GC_CALL* constants (this function converts +// to COR_PRF_GC_ROOT_FLAGS. +// + +void ScanRootsHelper(Object* pObj, Object** ppRoot, ScanContext * pSC, DWORD dwFlags) +{ + ProfilingScanContext *pPSC = (ProfilingScanContext *)pSC; + + DWORD dwEtwRootFlags = 0; + if (dwFlags & GC_CALL_INTERIOR) + dwEtwRootFlags |= kEtwGCRootFlagsInterior; + if (dwFlags & GC_CALL_PINNED) + dwEtwRootFlags |= kEtwGCRootFlagsPinning; + + // Notify ETW of the root + + if (ETW::GCLog::ShouldWalkHeapRootsForEtw()) + { + ETW::GCLog::RootReference( + ppRoot, // root address + pObj, // object being rooted + NULL, // pSecondaryNodeForDependentHandle is NULL, cuz this isn't a dependent handle + FALSE, // is dependent handle + pPSC, + dwFlags, // dwGCFlags + dwEtwRootFlags); + } +} + +//--------------------------------------------------------------------------------------- +// +// Callback of type walk_fn used by GCHeap::WalkObject. Keeps a count of each +// object reference found. +// +// Arguments: +// pBO - Object reference encountered in walk +// context - running count of object references encountered +// +// Return Value: +// Always returns TRUE to object walker so it walks the entire object +// + +bool CountContainedObjectRef(Object * pBO, void * context) +{ + LIMITED_METHOD_CONTRACT; + UNREFERENCED_PARAMETER(pBO); + // Increase the count + (*((size_t *)context))++; + + return TRUE; +} + +//--------------------------------------------------------------------------------------- +// +// Callback of type walk_fn used by GCHeap::WalkObject. Stores each object reference +// encountered into an array. +// +// Arguments: +// pBO - Object reference encountered in walk +// context - Array of locations within the walked object that point to other +// objects. On entry, (*context) points to the next unfilled array +// entry. On exit, that location is filled, and (*context) is incremented +// to point to the next entry. +// +// Return Value: +// Always returns TRUE to object walker so it walks the entire object +// + +bool SaveContainedObjectRef(Object * pBO, void * context) +{ + LIMITED_METHOD_CONTRACT; + // Assign the value + **((Object ***)context) = pBO; + + // Now increment the array pointer + // + // Note that HeapWalkHelper has already walked the references once to count them up, + // and then allocated an array big enough to hold those references. First time this + // callback is called for a given object, (*context) points to the first entry in the + // array. So "blindly" incrementing (*context) here and using it next time around + // for the next reference, over and over again, should be safe. + (*((Object ***)context))++; + + return TRUE; +} + +//--------------------------------------------------------------------------------------- +// +// Callback of type walk_fn used by the GC when walking the heap, to help profapi +// track objects. This guy orchestrates the use of the above callbacks which dig +// into object references contained each object encountered by this callback. +// +// Arguments: +// pBO - Object reference encountered on the heap +// +// Return Value: +// BOOL indicating whether the heap walk should continue. +// TRUE=continue +// FALSE=stop +// + +bool HeapWalkHelper(Object * pBO, void * pvContext) +{ + OBJECTREF * arrObjRef = NULL; + size_t cNumRefs = 0; + bool bOnStack = false; + //MethodTable * pMT = pBO->GetMethodTable(); + + ProfilerWalkHeapContext * pProfilerWalkHeapContext = (ProfilerWalkHeapContext *) pvContext; + + //if (pMT->ContainsPointersOrCollectible()) + { + // First round through calculates the number of object refs for this class + GCHeapUtilities::GetGCHeap()->DiagWalkObject(pBO, &CountContainedObjectRef, (void *)&cNumRefs); + + if (cNumRefs > 0) + { + // Create an array to contain all of the refs for this object + bOnStack = cNumRefs <= 32 ? true : false; + + if (bOnStack) + { + // It's small enough, so just allocate on the stack + arrObjRef = (OBJECTREF *)_alloca(cNumRefs * sizeof(OBJECTREF)); + } + else + { + // Otherwise, allocate from the heap + arrObjRef = new (nothrow) OBJECTREF[cNumRefs]; + + if (!arrObjRef) + { + return FALSE; + } + } + + // Second round saves off all of the ref values + OBJECTREF * pCurObjRef = arrObjRef; + GCHeapUtilities::GetGCHeap()->DiagWalkObject(pBO, &SaveContainedObjectRef, (void *)&pCurObjRef); + } + } + + HRESULT hr = E_FAIL; + +#ifdef FEATURE_ETW + if (ETW::GCLog::ShouldWalkHeapObjectsForEtw()) + { + ETW::GCLog::ObjectReference( + pProfilerWalkHeapContext, + pBO, + ULONGLONG(pBO->get_SafeEEType()), + cNumRefs, + (Object **) arrObjRef); + } +#endif // FEATURE_ETW + + // If the data was not allocated on the stack, need to clean it up. + if ((arrObjRef != NULL) && !bOnStack) + { + delete [] arrObjRef; + } + + // Return TRUE iff we want to the heap walk to continue. The only way we'd abort the + // heap walk is if we're issuing profapi callbacks, and the profapi profiler + // intentionally returned a failed HR (as its request that we stop the walk). There's + // a potential conflict here. If a profapi profiler and an ETW profiler are both + // monitoring the heap dump, and the profapi profiler requests to abort the walk (but + // the ETW profiler may not want to abort the walk), then what do we do? The profapi + // profiler gets precedence. We don't want to accidentally send more callbacks to a + // profapi profiler that explicitly requested an abort. The ETW profiler will just + // have to deal. In theory, I could make the code more complex by remembering that a + // profapi profiler requested to abort the dump but an ETW profiler is still + // attached, and then intentionally inhibit the remainder of the profapi callbacks + // for this GC. But that's unnecessary complexity. In practice, it should be + // extremely rare that a profapi profiler is monitoring heap dumps AND an ETW + // profiler is also monitoring heap dumps. + return TRUE; +} + +#endif // defined(FEATURE_EVENT_TRACE) || defined(GC_PROFILING) diff --git a/src/coreclr/nativeaot/Runtime/profheapwalkhelper.h b/src/coreclr/nativeaot/Runtime/profheapwalkhelper.h new file mode 100644 index 00000000000000..9c0da119aeabb7 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/profheapwalkhelper.h @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _GCHEAPWALKHELPER_H_ +#define _GCHEAPWALKHELPER_H_ + + +// These two functions are utilized to scan the heap if requested by ETW +// or a profiler. The implementations of these two functions are in profheapwalkhelper.cpp. +#if defined(FEATURE_EVENT_TRACE) || defined(GC_PROFILING) +void ScanRootsHelper(Object* pObj, Object** ppRoot, ScanContext* pSC, DWORD dwFlags); +bool HeapWalkHelper(Object* pBO, void* pvContext); +#endif + + +#endif // _GCHEAPWALKHELPER_H_ diff --git a/src/coreclr/nativeaot/Runtime/regdisplay.h b/src/coreclr/nativeaot/Runtime/regdisplay.h new file mode 100644 index 00000000000000..0ad03c76adc2b5 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/regdisplay.h @@ -0,0 +1,162 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if defined(TARGET_X86) || defined(TARGET_AMD64) + +struct REGDISPLAY +{ + PTR_UIntNative pRax; + PTR_UIntNative pRcx; + PTR_UIntNative pRdx; + PTR_UIntNative pRbx; + // pEsp; + PTR_UIntNative pRbp; + PTR_UIntNative pRsi; + PTR_UIntNative pRdi; +#ifdef TARGET_AMD64 + PTR_UIntNative pR8; + PTR_UIntNative pR9; + PTR_UIntNative pR10; + PTR_UIntNative pR11; + PTR_UIntNative pR12; + PTR_UIntNative pR13; + PTR_UIntNative pR14; + PTR_UIntNative pR15; +#endif // TARGET_AMD64 + + uintptr_t SP; + PTR_PCODE pIP; + PCODE IP; + +#if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI) + Fp128 Xmm[16-6]; // preserved xmm6..xmm15 regs for EH stackwalk + // these need to be unwound during a stack walk + // for EH, but not adjusted, so we only need + // their values, not their addresses +#endif // TARGET_AMD64 && !UNIX_AMD64_ABI + + inline PCODE GetIP() { return IP; } + inline PTR_PCODE GetAddrOfIP() { return pIP; } + inline uintptr_t GetSP() { return SP; } + inline uintptr_t GetFP() { return *pRbp; } + inline uintptr_t GetPP() { return *pRbx; } + + inline void SetIP(PCODE IP) { this->IP = IP; } + inline void SetAddrOfIP(PTR_PCODE pIP) { this->pIP = pIP; } + inline void SetSP(uintptr_t SP) { this->SP = SP; } +}; + +#elif defined(TARGET_ARM) + +struct REGDISPLAY +{ + PTR_UIntNative pR0; + PTR_UIntNative pR1; + PTR_UIntNative pR2; + PTR_UIntNative pR3; + PTR_UIntNative pR4; + PTR_UIntNative pR5; + PTR_UIntNative pR6; + PTR_UIntNative pR7; + PTR_UIntNative pR8; + PTR_UIntNative pR9; + PTR_UIntNative pR10; + PTR_UIntNative pR11; + PTR_UIntNative pR12; + PTR_UIntNative pLR; + + uintptr_t SP; + PTR_PCODE pIP; + PCODE IP; + + uint64_t D[16-8]; // preserved D registers D8..D15 (note that D16-D31 are not preserved according to the ABI spec) + // these need to be unwound during a stack walk + // for EH, but not adjusted, so we only need + // their values, not their addresses + + inline PCODE GetIP() { return IP; } + inline PTR_PCODE GetAddrOfIP() { return pIP; } + inline uintptr_t GetSP() { return SP; } + inline uintptr_t GetFP() { return *pR11; } + inline void SetIP(PCODE IP) { this->IP = IP; } + inline void SetAddrOfIP(PTR_PCODE pIP) { this->pIP = pIP; } + inline void SetSP(uintptr_t SP) { this->SP = SP; } +}; + +#elif defined(TARGET_ARM64) + +struct REGDISPLAY +{ + PTR_UIntNative pX0; + PTR_UIntNative pX1; + PTR_UIntNative pX2; + PTR_UIntNative pX3; + PTR_UIntNative pX4; + PTR_UIntNative pX5; + PTR_UIntNative pX6; + PTR_UIntNative pX7; + PTR_UIntNative pX8; + PTR_UIntNative pX9; + PTR_UIntNative pX10; + PTR_UIntNative pX11; + PTR_UIntNative pX12; + PTR_UIntNative pX13; + PTR_UIntNative pX14; + PTR_UIntNative pX15; + PTR_UIntNative pX16; + PTR_UIntNative pX17; + PTR_UIntNative pX18; + PTR_UIntNative pX19; + PTR_UIntNative pX20; + PTR_UIntNative pX21; + PTR_UIntNative pX22; + PTR_UIntNative pX23; + PTR_UIntNative pX24; + PTR_UIntNative pX25; + PTR_UIntNative pX26; + PTR_UIntNative pX27; + PTR_UIntNative pX28; + PTR_UIntNative pFP; // X29 + PTR_UIntNative pLR; // X30 + + uintptr_t SP; + PTR_PCODE pIP; + PCODE IP; + + uint64_t D[16-8]; // Only the bottom 64-bit value of the V registers V8..V15 needs to be preserved + // (V0-V7 and V16-V31 are not preserved according to the ABI spec). + // These need to be unwound during a stack walk + // for EH, but not adjusted, so we only need + // their values, not their addresses + + inline PCODE GetIP() { return IP; } + inline PTR_PCODE GetAddrOfIP() { return pIP; } + inline uintptr_t GetSP() { return SP; } + inline uintptr_t GetFP() { return *pFP; } + + inline void SetIP(PCODE IP) { this->IP = IP; } + inline void SetAddrOfIP(PTR_PCODE pIP) { this->pIP = pIP; } + inline void SetSP(uintptr_t SP) { this->SP = SP; } +}; +#elif defined(TARGET_WASM) + +struct REGDISPLAY +{ + // TODO: WebAssembly doesn't really have registers. What exactly do we need here? + + uintptr_t SP; + PTR_PCODE pIP; + PCODE IP; + + inline PCODE GetIP() { return NULL; } + inline PTR_PCODE GetAddrOfIP() { return NULL; } + inline uintptr_t GetSP() { return 0; } + inline uintptr_t GetFP() { return 0; } + + inline void SetIP(PCODE IP) { } + inline void SetAddrOfIP(PTR_PCODE pIP) { } + inline void SetSP(uintptr_t SP) { } +}; +#endif // HOST_X86 || HOST_AMD64 || HOST_ARM || HOST_ARM64 || HOST_WASM + +typedef REGDISPLAY * PREGDISPLAY; diff --git a/src/coreclr/nativeaot/Runtime/rhassert.cpp b/src/coreclr/nativeaot/Runtime/rhassert.cpp new file mode 100644 index 00000000000000..f69a919450b390 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/rhassert.cpp @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" + + +#include "RhConfig.h" + +#ifdef _DEBUG + +#define MB_ABORTRETRYIGNORE 0x00000002L +#define IDABORT 3 +#define IDRETRY 4 +#define IDIGNORE 5 + +void Assert(const char * expr, const char * file, uint32_t line_num, const char * message) +{ +#ifndef DACCESS_COMPILE +#ifdef NO_UI_ASSERT + PalDebugBreak(); +#else + if (g_pRhConfig->GetBreakOnAssert()) + { + printf( + "--------------------------------------------------\n" + "Debug Assertion Violation\n\n" + "%s%s%s" + "Expression: '%s'\n\n" + "File: %s, Line: %u\n" + "--------------------------------------------------\n", + message ? ("Message: ") : (""), + message ? (message) : (""), + message ? ("\n\n") : (""), + expr, file, line_num); + + // Flush standard output before failing fast to make sure the assertion failure message + // is retained when tests are being run with redirected stdout. + fflush(stdout); + + // If there's no debugger attached, we just FailFast + if (!PalIsDebuggerPresent()) + PalRaiseFailFastException(NULL, NULL, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS); + + // If there is a debugger attached, we break and then allow continuation. + PalDebugBreak(); + return; + } + + char buffer[4096]; + + sprintf_s(buffer, COUNTOF(buffer), + "--------------------------------------------------\n" + "Debug Assertion Violation\n\n" + "%s%s%s" + "Expression: '%s'\n\n" + "File: %s, Line: %u\n" + "--------------------------------------------------\n" + "Abort: Exit Immediately\n" + "Retry: DebugBreak()\n" + "Ignore: Keep Going\n" + "--------------------------------------------------\n", + message ? ("Message: ") : (""), + message ? (message) : (""), + message ? ("\n\n") : (""), + expr, file, line_num); + + HANDLE hMod = PalLoadLibraryExW(L"user32.dll", NULL, 0); + int32_t (* pfn)(HANDLE, char *, const char *, uint32_t) = + (int32_t (*)(HANDLE, char *, const char *, uint32_t))PalGetProcAddress(hMod, "MessageBoxA"); + + int32_t result = pfn(NULL, buffer, "Redhawk Assert", MB_ABORTRETRYIGNORE); + + switch (result) + { + case IDABORT: + PalTerminateCurrentProcess(42); + break; + case IDRETRY: + PalDebugBreak(); + break; + case IDIGNORE: + break; + } +#endif +#else + UNREFERENCED_PARAMETER(expr); + UNREFERENCED_PARAMETER(file); + UNREFERENCED_PARAMETER(line_num); + UNREFERENCED_PARAMETER(message); +#endif //!DACCESS_COMPILE +} + +#endif // _DEBUG diff --git a/src/coreclr/nativeaot/Runtime/rhassert.h b/src/coreclr/nativeaot/Runtime/rhassert.h new file mode 100644 index 00000000000000..ef9ce47b841c93 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/rhassert.h @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#ifndef __RHASSERT_H__ +#define __RHASSERT_H__ + +#ifdef _MSC_VER +#define ASSUME(expr) __assume(expr) +#else // _MSC_VER +#define ASSUME(expr) do { if (!(expr)) __builtin_unreachable(); } while (0) +#endif // _MSC_VER + +#if defined(_DEBUG) && !defined(DACCESS_COMPILE) + +#define ASSERT(expr) \ + { \ + if (!(expr)) { Assert(#expr, __FILE__, __LINE__, NULL); } \ + } \ + +#define ASSERT_MSG(expr, msg) \ + { \ + if (!(expr)) { Assert(#expr, __FILE__, __LINE__, msg); } \ + } \ + +#define VERIFY(expr) ASSERT((expr)) + +#define ASSERT_UNCONDITIONALLY(message) \ + Assert("ASSERT_UNCONDITIONALLY", __FILE__, __LINE__, message); \ + +void Assert(const char * expr, const char * file, unsigned int line_num, const char * message); + +#else + +#define ASSERT(expr) + +#define ASSERT_MSG(expr, msg) + +#define VERIFY(expr) (expr) + +#define ASSERT_UNCONDITIONALLY(message) + +#endif + +#ifndef _ASSERTE +#define _ASSERTE(_expr) ASSERT(_expr) +#endif + +#define PORTABILITY_ASSERT(message) \ + ASSERT_UNCONDITIONALLY(message); \ + ASSUME(0); \ + +#define UNREACHABLE() \ + ASSERT_UNCONDITIONALLY("UNREACHABLE"); \ + ASSUME(0); \ + +#define UNREACHABLE_MSG(message) \ + ASSERT_UNCONDITIONALLY(message); \ + ASSUME(0); \ + +#define FAIL_FAST_GENERATE_EXCEPTION_ADDRESS 0x1 + +#define RhFailFast() PalRaiseFailFastException(NULL, NULL, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS) + +#endif // __RHASSERT_H__ diff --git a/src/coreclr/nativeaot/Runtime/rhcommon.h b/src/coreclr/nativeaot/Runtime/rhcommon.h new file mode 100644 index 00000000000000..434f665aa86fec --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/rhcommon.h @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This file is here because we share some common code with the CLR and that platform uses common.h as a +// precompiled header. Due to limitations on precompilation (a precompiled header must be included first +// and must not be preceded by any other preprocessor directive) we cannot conditionally include common.h, +// so the simplest solution is to maintain this empty header under Redhawk. +// + +// +// For our DAC build, we precompile gcrhenv.h because it is extremely large (~3MB of text). For non-DAC +// builds, we do not do this because the majority of the files have more constrained #includes. +// + +#include "stdint.h" diff --git a/src/coreclr/nativeaot/Runtime/rheventtrace.cpp b/src/coreclr/nativeaot/Runtime/rheventtrace.cpp new file mode 100644 index 00000000000000..f524085d66fa26 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/rheventtrace.cpp @@ -0,0 +1,375 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Redhawk-specific ETW helper code. +// +// When Redhawk does stuff substantially different from desktop CLR, the +// Redhawk-specific implementations should go here. +// +#include "common.h" +#include "gcenv.h" +#include "rheventtrace.h" +#include "eventtrace.h" +#include "rhbinder.h" +#include "slist.h" +#include "rwlock.h" +#include "runtimeinstance.h" +#include "shash.h" +#include "eventtracepriv.h" +#include "shash.inl" +#include "palredhawk.h" + +#if defined(FEATURE_EVENT_TRACE) + +//--------------------------------------------------------------------------------------- +// BulkTypeEventLogger is a helper class to batch up type information and then flush to +// ETW once the event reaches its max # descriptors + + +//--------------------------------------------------------------------------------------- +// +// Batches up ETW information for a type and pops out to recursively call +// ETW::TypeSystemLog::LogTypeAndParametersIfNecessary for any +// "type parameters". Generics info is not reliably available, so "type parameter" +// really just refers to the type of array elements if thAsAddr is an array. +// +// Arguments: +// * thAsAddr - MethodTable to log +// * typeLogBehavior - Ignored in Redhawk builds +// + +void BulkTypeEventLogger::LogTypeAndParameters(uint64_t thAsAddr, ETW::TypeSystemLog::TypeLogBehavior typeLogBehavior) +{ + if (!ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_TYPE_KEYWORD)) + { + return; + } + + MethodTable * pEEType = (MethodTable *) thAsAddr; + + // Batch up this type. This grabs useful info about the type, including any + // type parameters it may have, and sticks it in m_rgBulkTypeValues + int iBulkTypeEventData = LogSingleType(pEEType); + if (iBulkTypeEventData == -1) + { + // There was a failure trying to log the type, so don't bother with its type + // parameters + return; + } + + // Look at the type info we just batched, so we can get the type parameters + BulkTypeValue * pVal = &m_rgBulkTypeValues[iBulkTypeEventData]; + + // We're about to recursively call ourselves for the type parameters, so make a + // local copy of their type handles first (else, as we log them we could flush + // and clear out m_rgBulkTypeValues, thus trashing pVal) + NewArrayHolder rgTypeParameters; + DWORD cTypeParams = pVal->cTypeParameters; + if (cTypeParams == 1) + { + ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(this, pVal->ullSingleTypeParameter, typeLogBehavior); + } + else if (cTypeParams > 1) + { + rgTypeParameters = new (nothrow) ULONGLONG[cTypeParams]; + for (DWORD i=0; i < cTypeParams; i++) + { + rgTypeParameters[i] = pVal->rgTypeParameters[i]; + } + + // Recursively log any referenced parameter types + for (DWORD i=0; i < cTypeParams; i++) + { + ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(this, rgTypeParameters[i], typeLogBehavior); + } + } +} + +// We keep a hash of these to keep track of: +// * Which types have been logged through ETW (so we can avoid logging dupe Type +// events), and +// * GCSampledObjectAllocation stats to help with "smart sampling" which +// dynamically adjusts sampling rate of objects by type. +// See code:LoggedTypesFromModuleTraits + +class LoggedTypesTraits : public DefaultSHashTraits +{ +public: + + // explicitly declare local typedefs for these traits types, otherwise + // the compiler may get confused + typedef MethodTable* key_t; + + static key_t GetKey(const element_t &e) + { + LIMITED_METHOD_CONTRACT; + return e; + } + + static BOOL Equals(key_t k1, key_t k2) + { + LIMITED_METHOD_CONTRACT; + return (k1 == k2); + } + + static count_t Hash(key_t k) + { + LIMITED_METHOD_CONTRACT; + return (count_t) (uintptr_t) k; + } + + static bool IsNull(const element_t &e) + { + LIMITED_METHOD_CONTRACT; + return (e == NULL); + } + + static const element_t Null() + { + LIMITED_METHOD_CONTRACT; + return NULL; + } +}; + +enum class CorElementType : uint8_t +{ + ELEMENT_TYPE_END = 0x0, + + ELEMENT_TYPE_BOOLEAN = 0x2, + ELEMENT_TYPE_CHAR = 0x3, + ELEMENT_TYPE_I1 = 0x4, + ELEMENT_TYPE_U1 = 0x5, + ELEMENT_TYPE_I2 = 0x6, + ELEMENT_TYPE_U2 = 0x7, + ELEMENT_TYPE_I4 = 0x8, + ELEMENT_TYPE_U4 = 0x9, + ELEMENT_TYPE_I8 = 0xa, + ELEMENT_TYPE_U8 = 0xb, + ELEMENT_TYPE_R4 = 0xc, + ELEMENT_TYPE_R8 = 0xd, + + ELEMENT_TYPE_I = 0x18, + ELEMENT_TYPE_U = 0x19, +}; + +static CorElementType ElementTypeToCorElementType(EETypeElementType elementType) +{ + switch (elementType) + { + case EETypeElementType::ElementType_Boolean: + return CorElementType::ELEMENT_TYPE_BOOLEAN; + case EETypeElementType::ElementType_Char: + return CorElementType::ELEMENT_TYPE_CHAR; + case EETypeElementType::ElementType_SByte: + return CorElementType::ELEMENT_TYPE_I1; + case EETypeElementType::ElementType_Byte: + return CorElementType::ELEMENT_TYPE_U1; + case EETypeElementType::ElementType_Int16: + return CorElementType::ELEMENT_TYPE_I2; + case EETypeElementType::ElementType_UInt16: + return CorElementType::ELEMENT_TYPE_U2; + case EETypeElementType::ElementType_Int32: + return CorElementType::ELEMENT_TYPE_I4; + case EETypeElementType::ElementType_UInt32: + return CorElementType::ELEMENT_TYPE_U4; + case EETypeElementType::ElementType_Int64: + return CorElementType::ELEMENT_TYPE_I8; + case EETypeElementType::ElementType_UInt64: + return CorElementType::ELEMENT_TYPE_U8; + case EETypeElementType::ElementType_Single: + return CorElementType::ELEMENT_TYPE_R4; + case EETypeElementType::ElementType_Double: + return CorElementType::ELEMENT_TYPE_R8; + case EETypeElementType::ElementType_IntPtr: + return CorElementType::ELEMENT_TYPE_I; + case EETypeElementType::ElementType_UIntPtr: + return CorElementType::ELEMENT_TYPE_U; + } + return CorElementType::ELEMENT_TYPE_END; +} + +// Avoid reporting the same type twice by keeping a hash of logged types. +SHash* s_loggedTypesHash = NULL; + +//--------------------------------------------------------------------------------------- +// +// Interrogates MethodTable for the info that's interesting to include in the BulkType ETW +// event. Does not recursively call self for type parameters. +// +// Arguments: +// * pEEType - MethodTable to log info about +// +// Return Value: +// Index into internal array where the info got batched. Or -1 if there was a +// failure. +// + +int BulkTypeEventLogger::LogSingleType(MethodTable * pEEType) +{ +#ifdef MULTIPLE_HEAPS + // We need to add a lock to protect the types hash for Server GC. + ASSERT_UNCONDITIONALLY("Add a lock to protect s_loggedTypesHash access!"); +#endif + //Avoid logging the same type twice, but using the hash of loggged types. + if (s_loggedTypesHash == NULL) + s_loggedTypesHash = new SHash(); + MethodTable* preexistingType = s_loggedTypesHash->Lookup(pEEType); + if (preexistingType != NULL) + { + return -1; + } + else + { + s_loggedTypesHash->Add(pEEType); + } + + // If there's no room for another type, flush what we've got + if (m_nBulkTypeValueCount == _countof(m_rgBulkTypeValues)) + { + FireBulkTypeEvent(); + } + + _ASSERTE(m_nBulkTypeValueCount < _countof(m_rgBulkTypeValues)); + + BulkTypeValue * pVal = &m_rgBulkTypeValues[m_nBulkTypeValueCount]; + + // Clear out pVal before filling it out (array elements can get reused if there + // are enough types that we need to flush to multiple events). + pVal->Clear(); + + pVal->fixedSizedData.TypeID = (ULONGLONG) pEEType; + pVal->fixedSizedData.Flags = kEtwTypeFlagsModuleBaseAddress; + pVal->fixedSizedData.CorElementType = (BYTE)ElementTypeToCorElementType(pEEType->GetElementType()); + + ULONGLONG * rgTypeParamsForEvent = NULL; + ULONGLONG typeParamForNonGenericType = 0; + + // Determine this MethodTable's module. + RuntimeInstance * pRuntimeInstance = GetRuntimeInstance(); + + ULONGLONG osModuleHandle = (ULONGLONG) pEEType->GetTypeManagerPtr()->AsTypeManager()->GetOsModuleHandle(); + + pVal->fixedSizedData.ModuleID = osModuleHandle; + + if (pEEType->IsParameterizedType()) + { + ASSERT(pEEType->IsArray()); + // Array + pVal->fixedSizedData.Flags |= kEtwTypeFlagsArray; + pVal->cTypeParameters = 1; + pVal->ullSingleTypeParameter = (ULONGLONG) pEEType->get_RelatedParameterType(); + } + else + { + // Note: if pEEType->IsCloned(), then no special handling is necessary. All the + // functionality we need from the MethodTable below work just as well from cloned types. + + // Note: For generic types, we do not necessarily know the generic parameters. + // So we leave it to the profiler at post-processing time to determine that via + // the PDBs. We'll leave pVal->cTypeParameters as 0, even though there could be + // type parameters. + + // Flags + if (pEEType->HasFinalizer()) + { + pVal->fixedSizedData.Flags |= kEtwTypeFlagsFinalizable; + } + + // Note: Pn runtime knows nothing about delegates, and there are no CCWs/RCWs. + // So no other type flags are applicable to set + } + + ULONGLONG rvaType = osModuleHandle == 0 ? 0 : (ULONGLONG(pEEType) - osModuleHandle); + pVal->fixedSizedData.TypeNameID = (DWORD) rvaType; + + // Now that we know the full size of this type's data, see if it fits in our + // batch or whether we need to flush + + int cbVal = pVal->GetByteCountInEvent(); + if (cbVal > kMaxBytesTypeValues) + { + // This type is apparently so huge, it's too big to squeeze into an event, even + // if it were the only type batched in the whole event. Bail + ASSERT(!"Type too big to log via ETW"); + return -1; + } + + if (m_nBulkTypeValueByteCount + cbVal > kMaxBytesTypeValues) + { + // Although this type fits into the array, its size is so big that the entire + // array can't be logged via ETW. So flush the array, and start over by + // calling ourselves--this refetches the type info and puts it at the + // beginning of the array. Since we know this type is small enough to be + // batched into an event on its own, this recursive call will not try to + // call itself again. + FireBulkTypeEvent(); + return LogSingleType(pEEType); + } + + // The type fits into the batch, so update our state + m_nBulkTypeValueCount++; + m_nBulkTypeValueByteCount += cbVal; + return m_nBulkTypeValueCount - 1; // Index of type we just added +} + + +void BulkTypeEventLogger::Cleanup() +{ + if (s_loggedTypesHash != NULL) + { + delete s_loggedTypesHash; + s_loggedTypesHash = NULL; + } +} + +#endif // defined(FEATURE_EVENT_TRACE) + + +//--------------------------------------------------------------------------------------- +// +// Outermost level of ETW-type-logging. Clients outside (rh)eventtrace.cpp call this to log +// an EETypes and (recursively) its type parameters when present. This guy then calls +// into the appropriate BulkTypeEventLogger to do the batching and logging +// +// Arguments: +// * pBulkTypeEventLogger - If our caller is keeping track of batched types, it +// passes this to us so we can use it to batch the current type (GC heap walk +// does this). In Redhawk builds this should not be NULL. +// * thAsAddr - MethodTable to batch +// * typeLogBehavior - Unused in Redhawk builds +// + +void ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(BulkTypeEventLogger * pLogger, uint64_t thAsAddr, ETW::TypeSystemLog::TypeLogBehavior typeLogBehavior) +{ +#if defined(FEATURE_EVENT_TRACE) + + if (!ETW_TRACING_CATEGORY_ENABLED( + MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, + TRACE_LEVEL_INFORMATION, + CLR_TYPE_KEYWORD)) + { + return; + } + + _ASSERTE(pLogger != NULL); + pLogger->LogTypeAndParameters(thAsAddr, typeLogBehavior); + +#endif // defined(FEATURE_EVENT_TRACE) +} + +COOP_PINVOKE_HELPER(void, RhpEtwExceptionThrown, (LPCWSTR exceptionTypeName, LPCWSTR exceptionMessage, void* faultingIP, HRESULT hresult)) +{ + FireEtwExceptionThrown_V1(exceptionTypeName, + exceptionMessage, + faultingIP, + hresult, + 0, + GetClrInstanceId()); +} + + + diff --git a/src/coreclr/nativeaot/Runtime/rheventtrace.h b/src/coreclr/nativeaot/Runtime/rheventtrace.h new file mode 100644 index 00000000000000..c891860ad16644 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/rheventtrace.h @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This header provides Redhawk-specific ETW code and macros, to allow sharing of common +// ETW code between Redhawk and desktop CLR. +// +#ifndef __RHEVENTTRACE_INCLUDED +#define __RHEVENTTRACE_INCLUDED + + +#ifdef FEATURE_ETW + +// FireEtwGCPerHeapHistorySpecial() has to be defined manually rather than via the manifest because it does +// not have a standard signature. +#define FireEtwGCPerHeapHistorySpecial(DataPerHeap, DataSize, ClrId) (MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context.IsEnabled && PalEventEnabled(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCPerHeapHistory)) ? Template_GCPerHeapHistorySpecial(Microsoft_Windows_Redhawk_GC_PrivateHandle, &GCPerHeapHistory, DataPerHeap, DataSize, ClrId) : 0 + +// Map the CLR private provider to our version so we can avoid inserting more #ifdef's in the code. +#define MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context MICROSOFT_WINDOWS_REDHAWK_GC_PRIVATE_PROVIDER_Context +#define MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context MICROSOFT_WINDOWS_REDHAWK_GC_PUBLIC_PROVIDER_Context +#define Microsoft_Windows_DotNETRuntimeHandle Microsoft_Windows_Redhawk_GC_PublicHandle + +#define CLR_GC_KEYWORD 0x1 +#define CLR_FUSION_KEYWORD 0x4 +#define CLR_LOADER_KEYWORD 0x8 +#define CLR_JIT_KEYWORD 0x10 +#define CLR_NGEN_KEYWORD 0x20 +#define CLR_STARTENUMERATION_KEYWORD 0x40 +#define CLR_ENDENUMERATION_KEYWORD 0x80 +#define CLR_SECURITY_KEYWORD 0x400 +#define CLR_APPDOMAINRESOURCEMANAGEMENT_KEYWORD 0x800 +#define CLR_JITTRACING_KEYWORD 0x1000 +#define CLR_INTEROP_KEYWORD 0x2000 +#define CLR_CONTENTION_KEYWORD 0x4000 +#define CLR_EXCEPTION_KEYWORD 0x8000 +#define CLR_THREADING_KEYWORD 0x10000 +#define CLR_JITTEDMETHODILTONATIVEMAP_KEYWORD 0x20000 +#define CLR_OVERRIDEANDSUPPRESSNGENEVENTS_KEYWORD 0x40000 +#define CLR_TYPE_KEYWORD 0x80000 +#define CLR_GCHEAPDUMP_KEYWORD 0x100000 +#define CLR_GCHEAPALLOC_KEYWORD 0x200000 +#define CLR_GCHEAPSURVIVALANDMOVEMENT_KEYWORD 0x400000 +#define CLR_GCHEAPCOLLECT_KEYWORD 0x800000 +#define CLR_GCHEAPANDTYPENAMES_KEYWORD 0x1000000 +#define CLR_PERFTRACK_KEYWORD 0x20000000 +#define CLR_STACK_KEYWORD 0x40000000 +#ifndef ERROR_SUCCESS +#define ERROR_SUCCESS 0 +#endif + +#undef ETW_TRACING_INITIALIZED +#define ETW_TRACING_INITIALIZED(RegHandle) (RegHandle != NULL) + +#undef ETW_CATEGORY_ENABLED +#define ETW_CATEGORY_ENABLED(Context, LevelParam, Keyword) \ + (Context.IsEnabled && \ + ( \ + (LevelParam <= ((Context).Level)) || \ + ((Context.Level) == 0) \ + ) && \ + ( \ + (Keyword == (ULONGLONG)0) || \ + ( \ + (Keyword & (Context.MatchAnyKeyword)) && \ + ( \ + (Keyword & (Context.MatchAllKeyword)) == (Context.MatchAllKeyword) \ + ) \ + ) \ + ) \ + ) + +class MethodTable; +class BulkTypeEventLogger; + +namespace ETW +{ + // Class to wrap all type system logic for ETW + class TypeSystemLog + { + public: + // This enum is unused on Redhawk, but remains here to keep Redhawk / desktop CLR + // code shareable. + enum TypeLogBehavior + { + kTypeLogBehaviorTakeLockAndLogIfFirstTime, + kTypeLogBehaviorAssumeLockAndLogIfFirstTime, + kTypeLogBehaviorAlwaysLog, + }; + + static void LogTypeAndParametersIfNecessary(BulkTypeEventLogger * pLogger, uint64_t thAsAddr, TypeLogBehavior typeLogBehavior); + }; +}; + +#else +#define FireEtwGCPerHeapHistorySpecial(DataPerHeap, DataSize, ClrId) +#endif + +#endif //__RHEVENTTRACE_INCLUDED diff --git a/src/coreclr/nativeaot/Runtime/shash.h b/src/coreclr/nativeaot/Runtime/shash.h new file mode 100644 index 00000000000000..27b90157250a4f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/shash.h @@ -0,0 +1,634 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// SHash is a templated closed chaining hash table of pointers. It provides +// for multiple entries under the same key, and also for deleting elements. + +// Synchronization: +// Synchronization requirements depend on use. There are several properties to take into account: +// +// - Lookups may be asynchronous with each other +// - Lookups must be exclusive with Add operations +// (@todo: this can be remedied by delaying destruction of old tables during reallocation, e.g. during GC) +// - Remove operations may be asynchronous with Lookup/Add, unless elements are also deallocated. (In which +// case full synchronization is required) + + +// A SHash is templated by a class of TRAITS. These traits define the various specifics of the +// particular hash table. +// The required traits are: +// +// element_t Type of elements in the hash table. These elements are stored +// by value in the hash table. Elements must look more or less +// like primitives - they must support assignment relatively +// efficiently. There are 2 required sentinel values: +// Null and Deleted (described below). (Note that element_t is +// very commonly a pointer type.) +// +// The key must be derivable from the element; if your +// table's keys are independent of the stored values, element_t +// should be a key/value pair. +// +// key_t Type of the lookup key. The key is used for identity +// comparison between elements, and also as a key for lookup. +// This is also used by value and should support +// efficient assignment. +// +// count_t integral type for counts. Typically inherited by default +// Traits (count_t). +// +// static key_t GetKey(const element_t &e) Get key from element. Should be stable for a given e. +// static bool Equals(key_t k1, key_t k2) Compare 2 keys for equality. Again, should be stable. +// static count_t Hash(key_t k) Compute hash from a key. For efficient operation, the hashes +// for a set of elements should have random uniform distribution. +// +// static element_t Null() Return the Null sentinel value. May be inherited from +// default traits if it can be assigned from 0. +// static element_t Deleted() Return the Deleted sentinel value. May be inherited from the +// default traits if it can be assigned from -1. +// static bool IsNull(const ELEMENT &e) Compare element with Null sentinel value. May be inherited from +// default traits if it can be assigned from 0. +// static bool IsDeleted(const ELEMENT &e) Compare element with Deleted sentinel value. May be inherited +// from the default traits if it can be assigned from -1. +// static void OnFailure(FailureType failureType) Called when a failure occurs during SHash operation +// +// s_growth_factor_numerator +// s_growth_factor_denominator Factor to grow allocation (numerator/denominator). +// Typically inherited from default traits (3/2) +// +// s_density_factor_numerator +// s_density_factor_denominator Maximum occupied density of table before growth +// occurs (num/denom). Typically inherited (3/4). +// +// s_minimum_allocation Minimum table allocation count (size on first growth.) It is +// probably preferable to call Reallocate on initialization rather +// than override his from the default traits. (7) +// +// s_supports_remove Set to false for a slightly faster implementation that does not +// support deletes. There is a downside to the s_supports_remove flag, +// in that there may be more copies of the template instantiated through +// the system as different variants are used. + +#ifndef __shash_h__ +#define __shash_h__ + +// disable the "Conditional expression is constant" warning +#pragma warning(push) +#pragma warning(disable:4127) + + +enum FailureType { ftAllocation, ftOverflow }; + +// DefaultHashTraits provides defaults for seldomly customized values in traits classes. + +template < typename ELEMENT, typename COUNT_T = uint32_t > +class DefaultSHashTraits +{ + public: + typedef COUNT_T count_t; + typedef ELEMENT element_t; + typedef DPTR(element_t) PTR_element_t; // by default SHash is DAC-aware. For RS + // only SHash use NonDacAwareSHashTraits + // (which typedefs element_t* PTR_element_t) + static const count_t s_growth_factor_numerator = 3; + static const count_t s_growth_factor_denominator = 2; + + static const count_t s_density_factor_numerator = 3; + static const count_t s_density_factor_denominator = 4; + + static const count_t s_minimum_allocation = 7; + + static const bool s_supports_remove = true; + + static const ELEMENT Null() { return (const ELEMENT) 0; } + static const ELEMENT Deleted() { return (const ELEMENT) -1; } + static bool IsNull(const ELEMENT &e) { return e == (const ELEMENT) 0; } + static bool IsDeleted(const ELEMENT &e) { return e == (const ELEMENT) -1; } + + static void OnFailure(FailureType /*ft*/) { } + + // No defaults - must specify: + // + // typedef key_t; + // static key_t GetKey(const element_t &i); + // static bool Equals(key_t k1, key_t k2); + // static count_t Hash(key_t k); +}; + +// Hash table class definition + +template +class SHash : public TRAITS +{ + private: + class Index; + friend class Index; + + class KeyIndex; + friend class KeyIndex; + class Iterator; + class KeyIterator; + + public: + // explicitly declare local typedefs for these traits types, otherwise + // the compiler may get confused + typedef typename TRAITS::element_t element_t; + typedef typename TRAITS::PTR_element_t PTR_element_t; + typedef typename TRAITS::key_t key_t; + typedef typename TRAITS::count_t count_t; + + // Constructor/destructor. SHash tables always start out empty, with no + // allocation overhead. Call Reallocate to prime with an initial size if + // desired. + + SHash(); + ~SHash(); + + // Lookup an element in the table by key. Returns NULL if no element in the table + // has the given key. Note that multiple entries for the same key may be stored - + // this will return the first element added. Use KeyIterator to find all elements + // with a given key. + + element_t Lookup(key_t key) const; + + // Pointer-based flavor of Lookup (allows efficient access to tables of structures) + + const element_t* LookupPtr(key_t key) const; + + // Add an element to the hash table. This will never replace an element; multiple + // elements may be stored with the same key. + // + // Returns 'true' on success, 'false' on failure. + + bool Add(const element_t &element); + + // Add a new element to the hash table, if no element with the same key is already + // there. Otherwise, it will replace the existing element. This has the effect of + // updating an element rather than adding a duplicate. + // + // Returns 'true' on success, 'false' on failure. + + bool AddOrReplace(const element_t & element); + + // Remove the first element matching the key from the hash table. + + void Remove(key_t key); + + // Remove the specific element. + + void Remove(Iterator& i); + void Remove(KeyIterator& i); + + // Pointer-based flavor of Remove (allows efficient access to tables of structures) + + void RemovePtr(element_t * element); + + // Remove all elements in the hashtable + + void RemoveAll(); + + // Begin and End pointers for iteration over entire table. + + Iterator Begin() const; + Iterator End() const; + + // Begin and End pointers for iteration over all elements with a given key. + + KeyIterator Begin(key_t key) const; + KeyIterator End(key_t key) const; + + // Return the number of elements currently stored in the table + + count_t GetCount() const; + + // Return the number of elements that the table is capable storing currently + + count_t GetCapacity() const; + + // Reallocates a hash table to a specific size. The size must be big enough + // to hold all elements in the table appropriately. + // + // Note that the actual table size must always be a prime number; the number + // passed in will be upward adjusted if necessary. + // + // Returns 'true' on success, 'false' on failure. + + bool Reallocate(count_t newTableSize); + + // See if it is OK to grow the hash table by one element. If not, reallocate + // the hash table. + // + // Returns 'true' on success, 'false' on failure. + + bool CheckGrowth(); + + // See if it is OK to grow the hash table by N elementsone element. If not, reallocate + // the hash table. + + bool CheckGrowth(count_t newElements); + +private: + + // Resizes a hash table for growth. The new size is computed based + // on the current population, growth factor, and maximum density factor. + // + // Returns 'true' on success, 'false' on failure. + + bool Grow(); + + // Utility function to add a new element to the hash table. Note that + // it is perfectly find for the element to be a duplicate - if so it + // is added an additional time. Returns true if a new empty spot was used; + // false if an existing deleted slot. + + static bool Add(element_t *table, count_t tableSize, const element_t &element); + + // Utility function to add a new element to the hash table, if no element with the same key + // is already there. Otherwise, it will replace the existing element. This has the effect of + // updating an element rather than adding a duplicate. + + void AddOrReplace(element_t *table, count_t tableSize, const element_t &element); + + // Utility function to find the first element with the given key in + // the hash table. + + static const element_t* Lookup(PTR_element_t table, count_t tableSize, key_t key); + + // Utility function to remove the first element with the given key + // in the hash table. + + void Remove(element_t *table, count_t tableSize, key_t key); + + // Utility function to remove the specific element. + + void RemoveElement(element_t *table, count_t tableSize, element_t *element); + + // + // Enumerator, provides a template to produce an iterator on an existing class + // with a single iteration variable. + // + + template + class Enumerator + { + private: + const SUBTYPE *This() const + { + return (const SUBTYPE *) this; + } + + SUBTYPE *This() + { + return (SUBTYPE *)this; + } + + public: + + Enumerator() + { + } + + const element_t &operator*() const + { + return This()->Get(); + } + const element_t *operator->() const + { + return &(This()->Get()); + } + SUBTYPE &operator++() + { + This()->Next(); + return *This(); + } + SUBTYPE operator++(int) + { + SUBTYPE i = *This(); + This()->Next(); + return i; + } + bool operator==(const SUBTYPE &i) const + { + return This()->Equal(i); + } + bool operator!=(const SUBTYPE &i) const + { + return !This()->Equal(i); + } + }; + + // + // Index for whole table iterator. This is also the base for the keyed iterator. + // + + class Index + { + friend class SHash; + friend class Iterator; + friend class Enumerator; + + // The methods implementation has to be here for portability + // Some compilers won't compile the separate implementation in shash.inl + protected: + + PTR_element_t m_table; + count_t m_tableSize; + count_t m_index; + + Index(const SHash *hash, bool begin) + : m_table(hash->m_table), + m_tableSize(hash->m_tableSize), + m_index(begin ? 0 : m_tableSize) + { + } + + const element_t &Get() const + { + return m_table[m_index]; + } + + void First() + { + if (m_index < m_tableSize) + if (TRAITS::IsNull(m_table[m_index]) || TRAITS::IsDeleted(m_table[m_index])) + Next(); + } + + void Next() + { + if (m_index >= m_tableSize) + return; + + for (;;) + { + m_index++; + if (m_index >= m_tableSize) + break; + if (!TRAITS::IsNull(m_table[m_index]) && !TRAITS::IsDeleted(m_table[m_index])) + break; + } + } + + bool Equal(const Index &i) const + { + return i.m_index == m_index; + } + }; + + class Iterator : public Index, public Enumerator + { + friend class SHash; + + public: + Iterator(const SHash *hash, bool begin) + : Index(hash, begin) + { + } + }; + + // + // Index for iterating elements with a given key. + // Note that the m_index field is artificially bumped to m_tableSize when the end + // of iteration is reached. This allows a canonical End iterator to be used. + // + + class KeyIndex : public Index + { + friend class SHash; + friend class KeyIterator; + friend class Enumerator; + + // The methods implementation has to be here for portability + // Some compilers won't compile the separate implementation in shash.inl + protected: + key_t m_key; + count_t m_increment; + + KeyIndex(const SHash *hash, bool begin) + : Index(hash, begin), + m_increment(0) + { + } + + void SetKey(key_t key) + { + if (m_tableSize > 0) + { + m_key = key; + count_t hash = Hash(key); + + TRAITS::m_index = hash % m_tableSize; + m_increment = (hash % (m_tableSize-1)) + 1; + + // Find first valid element + if (IsNull(m_table[TRAITS::m_index])) + TRAITS::m_index = m_tableSize; + else if (IsDeleted(m_table[TRAITS::m_index]) + || !Equals(m_key, GetKey(m_table[TRAITS::m_index]))) + Next(); + } + } + + void Next() + { + while (true) + { + TRAITS::m_index += m_increment; + if (TRAITS::m_index >= m_tableSize) + TRAITS::m_index -= m_tableSize; + + if (IsNull(m_table[TRAITS::m_index])) + { + TRAITS::m_index = m_tableSize; + break; + } + + if (!IsDeleted(m_table[TRAITS::m_index]) + && Equals(m_key, GetKey(m_table[TRAITS::m_index]))) + { + break; + } + } + } + }; + + class KeyIterator : public KeyIndex, public Enumerator + { + friend class SHash; + + public: + + operator Iterator &() + { + return *(Iterator*)this; + } + + operator const Iterator &() + { + return *(const Iterator*)this; + } + + KeyIterator(const SHash *hash, bool begin) + : KeyIndex(hash, begin) + { + } + }; + + // Test for prime number. + static bool IsPrime(count_t number); + + // Find the next prime number >= the given value. + + static count_t NextPrime(count_t number); + + // Instance members + + PTR_element_t m_table; // pointer to table + count_t m_tableSize; // allocated size of table + count_t m_tableCount; // number of elements in table + count_t m_tableOccupied; // number, includes deleted slots + count_t m_tableMax; // maximum occupied count before reallocating +}; + +// disables support for DAC marshaling. Useful for defining right-side only SHashes + +template +class NonDacAwareSHashTraits : public PARENT +{ +public: + typedef typename PARENT::element_t element_t; + typedef element_t * PTR_element_t; +}; + +// disables support for removing elements - produces slightly faster implementation + +template +class NoRemoveSHashTraits : public PARENT +{ +public: + // explicitly declare local typedefs for these traits types, otherwise + // the compiler may get confused + typedef typename PARENT::element_t element_t; + typedef typename PARENT::count_t count_t; + + static const bool s_supports_remove = false; + static const element_t Deleted() { UNREACHABLE(); } + static bool IsDeleted(const element_t &e) { UNREFERENCED_PARAMETER(e); return false; } +}; + +// PtrHashTraits is a template to provides useful defaults for pointer hash tables +// It relies on methods GetKey and Hash defined on ELEMENT + +template +class PtrSHashTraits : public DefaultSHashTraits +{ + public: + + // explicitly declare local typedefs for these traits types, otherwise + // the compiler may get confused + typedef DefaultSHashTraits PARENT; + typedef typename PARENT::element_t element_t; + typedef typename PARENT::count_t count_t; + + typedef KEY key_t; + + static key_t GetKey(const element_t &e) + { + return e->GetKey(); + } + static bool Equals(key_t k1, key_t k2) + { + return k1 == k2; + } + static count_t Hash(key_t k) + { + return ELEMENT::Hash(k); + } +}; + +template +class PtrSHash : public SHash< PtrSHashTraits > +{ +}; + +template +class KeyValuePair { + KEY key; + VALUE value; + +public: + KeyValuePair() + { + } + + KeyValuePair(const KEY& k, const VALUE& v) + : key(k), value(v) + { + } + + KEY const & Key() const + { + return key; + } + + VALUE const & Value() const + { + return value; + } +}; + +template +class MapSHashTraits : public DefaultSHashTraits< KeyValuePair > +{ +public: + // explicitly declare local typedefs for these traits types, otherwise + // the compiler may get confused + typedef typename DefaultSHashTraits< KeyValuePair >::element_t element_t; + typedef typename DefaultSHashTraits< KeyValuePair >::count_t count_t; + + typedef KEY key_t; + + static key_t GetKey(element_t e) + { + return e.Key(); + } + static bool Equals(key_t k1, key_t k2) + { + return k1 == k2; + } + static count_t Hash(key_t k) + { + return (count_t)(size_t)k; + } + + static const element_t Null() { return element_t((KEY)0,(VALUE)0); } + static bool IsNull(const element_t &e) { return e.Key() == (KEY)0; } +}; + +template +class MapSHash : public SHash< NoRemoveSHashTraits< MapSHashTraits > > +{ + typedef SHash< NoRemoveSHashTraits< MapSHashTraits > > PARENT; + +public: + void Add(KEY key, VALUE value) + { + PARENT::Add(KeyValuePair(key, value)); + } + + bool Lookup(KEY key, VALUE* pValue) + { + const KeyValuePair *pRet = PARENT::LookupPtr(key); + if (pRet == NULL) + return false; + + *pValue = pRet->Value(); + return true; + } +}; + + +// restore "Conditional expression is constant" warning to previous value +#pragma warning(pop) + +#endif // __shash_h__ diff --git a/src/coreclr/nativeaot/Runtime/shash.inl b/src/coreclr/nativeaot/Runtime/shash.inl new file mode 100644 index 00000000000000..489d5c666cb04c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/shash.inl @@ -0,0 +1,470 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// disable the "Conditional expression is constant" warning +#pragma warning(disable:4127) + + +template +SHash::SHash() + : m_table(nullptr), + m_tableSize(0), + m_tableCount(0), + m_tableOccupied(0), + m_tableMax(0) +{ + C_ASSERT(TRAITS::s_growth_factor_numerator > TRAITS::s_growth_factor_denominator); + C_ASSERT(TRAITS::s_density_factor_numerator < TRAITS::s_density_factor_denominator); +} + +template +SHash::~SHash() +{ + delete [] m_table; +} + +template +typename SHash::count_t SHash::GetCount() const +{ + return m_tableCount; +} + +template +typename SHash::count_t SHash::GetCapacity() const +{ + return m_tableMax; +} + +template +typename SHash< TRAITS>::element_t SHash::Lookup(key_t key) const +{ + const element_t *pRet = Lookup(m_table, m_tableSize, key); + return ((pRet != NULL) ? (*pRet) : TRAITS::Null()); +} + +template +const typename SHash< TRAITS>::element_t* SHash::LookupPtr(key_t key) const +{ + return Lookup(m_table, m_tableSize, key); +} + +template +bool SHash::Add(const element_t &element) +{ + if (!CheckGrowth()) + return false; + + if (Add(m_table, m_tableSize, element)) + m_tableOccupied++; + m_tableCount++; + + return true; +} + +template +bool SHash::AddOrReplace(const element_t &element) +{ + if (!CheckGrowth()) + return false; + + AddOrReplace(m_table, m_tableSize, element); + return true; +} + +template +void SHash::Remove(key_t key) +{ + Remove(m_table, m_tableSize, key); +} + +template +void SHash::Remove(Iterator& i) +{ + RemoveElement(m_table, m_tableSize, (element_t*)&(*i)); +} + +template +void SHash::Remove(KeyIterator& i) +{ + RemoveElement(m_table, m_tableSize, (element_t*)&(*i)); +} + +template +void SHash::RemovePtr(element_t * p) +{ + RemoveElement(m_table, m_tableSize, p); +} + +template +void SHash::RemoveAll() +{ + delete [] m_table; + + m_table = NULL; + m_tableSize = 0; + m_tableCount = 0; + m_tableOccupied = 0; + m_tableMax = 0; +} + +template +typename SHash::Iterator SHash::Begin() const +{ + Iterator i(this, true); + i.First(); + return i; +} + +template +typename SHash::Iterator SHash::End() const +{ + return Iterator(this, false); +} + +template +typename SHash::KeyIterator SHash::Begin(key_t key) const +{ + KeyIterator k(this, true); + k.SetKey(key); + return k; +} + +template +typename SHash::KeyIterator SHash::End(key_t key) const +{ + return KeyIterator(this, false); +} + +template +bool SHash::CheckGrowth() +{ + if (m_tableOccupied == m_tableMax) + { + return Grow(); + } + + return true; +} + +template +bool SHash::Grow() +{ + count_t newSize = (count_t) (m_tableCount + * TRAITS::s_growth_factor_numerator / TRAITS::s_growth_factor_denominator + * TRAITS::s_density_factor_denominator / TRAITS::s_density_factor_numerator); + if (newSize < TRAITS::s_minimum_allocation) + newSize = TRAITS::s_minimum_allocation; + + // handle potential overflow + if (newSize < m_tableCount) + { + TRAITS::OnFailure(ftOverflow); + return false; + } + + return Reallocate(newSize); +} + +template +bool SHash::CheckGrowth(count_t newElements) +{ + count_t newCount = (m_tableCount + newElements); + + // handle potential overflow + if (newCount < newElements) + { + TRAITS::OnFailure(ftOverflow); + return false; + } + + // enough space in the table? + if (newCount < m_tableMax) + return true; + + count_t newSize = (count_t) (newCount * TRAITS::s_density_factor_denominator / TRAITS::s_density_factor_numerator) + 1; + + // handle potential overflow + if (newSize < newCount) + { + TRAITS::OnFailure(ftOverflow); + return false; + } + + // accelerate the growth to avoid unnecessary rehashing + count_t newSize2 = (m_tableCount * TRAITS::s_growth_factor_numerator / TRAITS::s_growth_factor_denominator + * TRAITS::s_density_factor_denominator / TRAITS::s_density_factor_numerator); + + if (newSize < newSize2) + newSize = newSize2; + + if (newSize < TRAITS::s_minimum_allocation) + newSize = TRAITS::s_minimum_allocation; + + return Reallocate(newSize); +} + +template +bool SHash::Reallocate(count_t newTableSize) +{ + ASSERT(newTableSize >= + (count_t) (GetCount() * TRAITS::s_density_factor_denominator / TRAITS::s_density_factor_numerator)); + + // Allocation size must be a prime number. This is necessary so that hashes uniformly + // distribute to all indices, and so that chaining will visit all indices in the hash table. + newTableSize = NextPrime(newTableSize); + if (newTableSize == 0) + { + TRAITS::OnFailure(ftOverflow); + return false; + } + + element_t *newTable = new (nothrow) element_t [newTableSize]; + if (newTable == NULL) + { + TRAITS::OnFailure(ftAllocation); + return false; + } + + element_t *p = newTable, *pEnd = newTable + newTableSize; + while (p < pEnd) + { + *p = TRAITS::Null(); + p++; + } + + // Move all entries over to new table. + + for (Iterator i = Begin(), end = End(); i != end; i++) + { + const element_t & cur = (*i); + if (!TRAITS::IsNull(cur) && !TRAITS::IsDeleted(cur)) + Add(newTable, newTableSize, cur); + } + + // @todo: + // We might want to try to delay this cleanup to allow asynchronous readers + + delete [] m_table; + + m_table = PTR_element_t(newTable); + m_tableSize = newTableSize; + m_tableMax = (count_t) (newTableSize * TRAITS::s_density_factor_numerator / TRAITS::s_density_factor_denominator); + m_tableOccupied = m_tableCount; + + return true; +} + +template +const typename SHash::element_t * SHash::Lookup(PTR_element_t table, count_t tableSize, key_t key) +{ + if (tableSize == 0) + return NULL; + + count_t hash = TRAITS::Hash(key); + count_t index = hash % tableSize; + count_t increment = 0; // delay computation + + while (true) + { + element_t& current = table[index]; + + if (TRAITS::IsNull(current)) + return NULL; + + if (!TRAITS::IsDeleted(current) + && TRAITS::Equals(key, TRAITS::GetKey(current))) + { + return ¤t; + } + + if (increment == 0) + increment = (hash % (tableSize-1)) + 1; + + index += increment; + if (index >= tableSize) + index -= tableSize; + } +} + +template +bool SHash::Add(element_t *table, count_t tableSize, const element_t &element) +{ + key_t key = TRAITS::GetKey(element); + + count_t hash = TRAITS::Hash(key); + count_t index = hash % tableSize; + count_t increment = 0; // delay computation + + while (true) + { + element_t& current = table[index]; + + if (TRAITS::IsNull(current)) + { + table[index] = element; + return true; + } + + if (TRAITS::IsDeleted(current)) + { + table[index] = element; + return false; + } + + if (increment == 0) + increment = (hash % (tableSize-1)) + 1; + + index += increment; + if (index >= tableSize) + index -= tableSize; + } +} + +template +void SHash::AddOrReplace(element_t *table, count_t tableSize, const element_t &element) +{ + ASSERT(!TRAITS::s_supports_remove); + + key_t key = TRAITS::GetKey(element); + + count_t hash = TRAITS::Hash(key); + count_t index = hash % tableSize; + count_t increment = 0; // delay computation + + while (true) + { + element_t& current = table[index]; + ASSERT(!TRAITS::IsDeleted(current)); + + if (TRAITS::IsNull(current)) + { + table[index] = element; + m_tableCount++; + m_tableOccupied++; + return; + } + else if (TRAITS::Equals(key, TRAITS::GetKey(current))) + { + table[index] = element; + return; + } + + if (increment == 0) + increment = (hash % (tableSize-1)) + 1; + + index += increment; + if (index >= tableSize) + index -= tableSize; + } +} + +#ifdef _MSC_VER +#pragma warning (disable: 4702) // Workaround bogus unreachable code warning +#endif +template +void SHash::Remove(element_t *table, count_t tableSize, key_t key) +{ + ASSERT(TRAITS::s_supports_remove); + ASSERT(Lookup(table, tableSize, key) != NULL); + + count_t hash = TRAITS::Hash(key); + count_t index = hash % tableSize; + count_t increment = 0; // delay computation + + while (true) + { + element_t& current = table[index]; + + if (TRAITS::IsNull(current)) + return; + + if (!TRAITS::IsDeleted(current) + && TRAITS::Equals(key, TRAITS::GetKey(current))) + { + table[index] = TRAITS::Deleted(); + m_tableCount--; + return; + } + + if (increment == 0) + increment = (hash % (tableSize-1)) + 1; + + index += increment; + if (index >= tableSize) + index -= tableSize; + } +} +#ifdef _MSC_VER +#pragma warning (default: 4702) +#endif + +template +void SHash::RemoveElement(element_t *table, count_t tableSize, element_t *element) +{ + ASSERT(TRAITS::s_supports_remove); + ASSERT(table <= element && element < table + tableSize); + ASSERT(!TRAITS::IsNull(*element) && !TRAITS::IsDeleted(*element)); + + *element = TRAITS::Deleted(); + m_tableCount--; +} + +template +bool SHash::IsPrime(count_t number) +{ + // This is a very low-tech check for primality, which doesn't scale very well. + // There are more efficient tests if this proves to be burdensome for larger + // tables. + + if ((number&1) == 0) + return false; + + count_t factor = 3; + while (factor * factor <= number) + { + if ((number % factor) == 0) + return false; + factor += 2; + } + + return true; +} + +namespace +{ + const uint32_t g_shash_primes[] = { + 11,17,23,29,37,47,59,71,89,107,131,163,197,239,293,353,431,521,631,761,919, + 1103,1327,1597,1931,2333,2801,3371,4049,4861,5839,7013,8419,10103,12143,14591, + 17519,21023,25229,30293,36353,43627,52361,62851,75431,90523, 108631, 130363, + 156437, 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, + 968897, 1162687, 1395263, 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, + 4999559, 5999471, 7199369 }; +} + + +// Returns a prime larger than 'number' or 0, in case of overflow +template +typename SHash::count_t SHash::NextPrime(typename SHash::count_t number) +{ + for (int i = 0; i < (int) (sizeof(g_shash_primes) / sizeof(g_shash_primes[0])); i++) + { + if (g_shash_primes[i] >= number) + return (typename SHash::count_t)(g_shash_primes[i]); + } + + if ((number&1) == 0) + number++; + + while (number != 1) + { + if (IsPrime(number)) + return number; + number += 2; + } + + return 0; +} + +// restore "Conditional expression is constant" warning to default value +#pragma warning(default:4127) + diff --git a/src/coreclr/nativeaot/Runtime/slist.h b/src/coreclr/nativeaot/Runtime/slist.h new file mode 100644 index 00000000000000..4525ba3b586efd --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/slist.h @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __slist_h__ +#define __slist_h__ + +#include "forward_declarations.h" + +MSVC_SAVE_WARNING_STATE() +MSVC_DISABLE_WARNING(4127) // conditional expression is constant -- it's intentionally constant + +struct DoNothingFailFastPolicy +{ + static inline void FailFast(); +}; + +template +struct DefaultSListTraits : public FailFastPolicy +{ + typedef DPTR(T) PTR_T; + typedef DPTR(PTR_T) PTR_PTR_T; + + static inline PTR_PTR_T GetNextPtr(PTR_T pT); + static inline bool Equals(PTR_T pA, PTR_T pB); +}; + +//------------------------------------------------------------------------------------------------------------ +// class SList, to use a singly linked list. +// +// To use, either expose a field DPTR(T) m_pNext by adding DefaultSListTraits as a friend class, or +// define a new Traits class derived from DefaultSListTraits and override the GetNextPtr function. +// +// SList supports lockless head insert and Remove methods. However, PushHeadInterlocked and +// PopHeadInterlocked must be used very carefully, as the rest of the mutating methods are not +// interlocked. In general, code must be careful to ensure that it will never use more than one +// synchronization mechanism at any given time to control access to a resource, and this is no +// exception. In particular, if synchronized access to other SList operations (such as FindAndRemove) +// are required, than a separate synchronization mechanism (such as a critical section) must be used. +//------------------------------------------------------------------------------------------------------------ +template > +class SList : public Traits +{ +protected: + typedef typename Traits::PTR_T PTR_T; + typedef typename Traits::PTR_PTR_T PTR_PTR_T; + +public: + SList(); + + // Returns true if there are no entries in the list. + bool IsEmpty(); + + // Returns the value of (but does not remove) the first element in the list. + PTR_T GetHead(); + + // Inserts pItem at the front of the list. See class header for more information. + void PushHead(PTR_T pItem); + void PushHeadInterlocked(PTR_T pItem); + + // Removes and returns the first entry in the list. See class header for more information. + PTR_T PopHead(); + + class Iterator + { + friend SList; + + public: + Iterator(Iterator const &it); + Iterator& operator=(Iterator const &it); + + PTR_T operator->(); + PTR_T operator*(); + + Iterator & operator++(); + Iterator operator++(int); + + bool operator==(Iterator const &rhs); + bool operator==(PTR_T pT); + bool operator!=(Iterator const &rhs); + + private: + Iterator(PTR_PTR_T ppItem); + + Iterator Insert(PTR_T pItem); + Iterator Remove(); + + static Iterator End(); + PTR_PTR_T m_ppCur; +#ifdef _DEBUG + mutable bool m_fIsValid; +#endif + + PTR_T _Value() const; + + enum e_ValidateOperation + { + e_CanCompare, // Will assert in debug if m_fIsValid == false. + e_CanInsert, // i.e., not the fake End() value of m_ppCur == NULL + e_HasValue, // i.e., m_ppCur != NULL && *m_ppCur != NULL + }; + void _Validate(e_ValidateOperation op) const; + }; + + Iterator Begin(); + Iterator End(); + + // Returns iterator to first list item matching pItem + Iterator FindFirst(PTR_T pItem); + bool RemoveFirst(PTR_T pItem); + + // Inserts pItem *before* it. Returns iterator pointing to inserted item. + Iterator Insert(Iterator & it, PTR_T pItem); + + // Removes item pointed to by it from the list. Returns iterator pointing + // to following item. + Iterator Remove(Iterator & it); + +private: + PTR_T m_pHead; +}; + +MSVC_RESTORE_WARNING_STATE() + +#endif // __slist_h__ diff --git a/src/coreclr/nativeaot/Runtime/slist.inl b/src/coreclr/nativeaot/Runtime/slist.inl new file mode 100644 index 00000000000000..dc437fe1c9ba9b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/slist.inl @@ -0,0 +1,361 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +MSVC_SAVE_WARNING_STATE() +MSVC_DISABLE_WARNING(4127) // conditional expression is constant -- + // while (true) loops and compile time template constants cause this. + + +//------------------------------------------------------------------------------------------------- +namespace rh { namespace std +{ + // Specialize rh::std::find for SList iterators so that it will use _Traits::Equals. + template + inline + typename SList<_Tx, _Traits>::Iterator find( + typename SList<_Tx, _Traits>::Iterator _First, + typename SList<_Tx, _Traits>::Iterator _Last, + const _Ty& _Val) + { // find first matching _Val + for (; _First != _Last; ++_First) + if (_Traits::Equals(*_First, _Val)) + break; + return (_First); + } +} // namespace std +} // namespace rh + +//------------------------------------------------------------------------------------------------- +inline +void DoNothingFailFastPolicy::FailFast() +{ + // Intentionally a no-op. +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename DefaultSListTraits::PTR_PTR_T DefaultSListTraits::GetNextPtr( + PTR_T pT) +{ + ASSERT(pT != NULL); + return dac_cast(dac_cast(pT) + offsetof(T, m_pNext)); +} + +//------------------------------------------------------------------------------------------------- +template +inline +bool DefaultSListTraits::Equals( + PTR_T pA, + PTR_T pB) +{ // Default is pointer comparison + return pA == pB; +} + +//------------------------------------------------------------------------------------------------- +template +inline +SList::SList() + : m_pHead(NULL) +{ +} + +//------------------------------------------------------------------------------------------------- +template +inline +bool SList::IsEmpty() +{ + return Begin() == End(); +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::PTR_T SList::GetHead() +{ + return m_pHead; +} + +//------------------------------------------------------------------------------------------------- +template +inline +void SList::PushHead( + PTR_T pItem) +{ + NO_DAC(); + Begin().Insert(pItem); +} + +//------------------------------------------------------------------------------------------------- +template +inline +void SList::PushHeadInterlocked( + PTR_T pItem) +{ + NO_DAC(); + ASSERT(pItem != NULL); + ASSERT(IS_ALIGNED(&m_pHead, sizeof(void*))); + + while (true) + { + *Traits::GetNextPtr(pItem) = *reinterpret_cast(&m_pHead); + if (PalInterlockedCompareExchangePointer( + reinterpret_cast(&m_pHead), + reinterpret_cast(pItem), + reinterpret_cast(*Traits::GetNextPtr(pItem))) == reinterpret_cast(*Traits::GetNextPtr(pItem))) + { + break; + } + } +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::PTR_T SList::PopHead() +{ + NO_DAC(); + PTR_T pRet = *Begin(); + Begin().Remove(); + return pRet; +} + +//------------------------------------------------------------------------------------------------- +template +inline +SList::Iterator::Iterator( + Iterator const &it) + : m_ppCur(it.m_ppCur) +#ifdef _DEBUG + , m_fIsValid(it.m_fIsValid) +#endif +{ +} + +//------------------------------------------------------------------------------------------------- +template +inline +SList::Iterator::Iterator( + PTR_PTR_T ppItem) + : m_ppCur(ppItem) +#ifdef _DEBUG + , m_fIsValid(true) +#endif +{ +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::Iterator& SList::Iterator::operator=( + Iterator const &it) +{ + m_ppCur = it.m_ppCur; +#ifdef _DEBUG + m_fIsValid = it.m_fIsValid; +#endif + return *this; +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::PTR_T SList::Iterator::operator->() +{ + _Validate(e_HasValue); + return _Value(); +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::PTR_T SList::Iterator::operator*() +{ + _Validate(e_HasValue); + return _Value(); +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::Iterator & SList::Iterator::operator++() +{ + _Validate(e_HasValue); // Having a value means we're not at the end. + m_ppCur = Traits::GetNextPtr(_Value()); + return *this; +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::Iterator SList::Iterator::operator++( + int) +{ + _Validate(e_HasValue); // Having a value means we're not at the end. + PTR_PTR_T ppRet = m_ppCur; + ++(*this); + return Iterator(ppRet); +} + +//------------------------------------------------------------------------------------------------- +template +inline +bool SList::Iterator::operator==( + Iterator const &rhs) +{ + _Validate(e_CanCompare); + rhs._Validate(e_CanCompare); + return Traits::Equals(_Value(), rhs._Value()); +} + +//------------------------------------------------------------------------------------------------- +template +inline +bool SList::Iterator::operator==( + PTR_T pT) +{ + _Validate(e_CanCompare); + return Traits::Equals(_Value(), pT); +} + +//------------------------------------------------------------------------------------------------- +template +inline +bool SList::Iterator::operator!=( + Iterator const &rhs) +{ + return !operator==(rhs); +} + +//------------------------------------------------------------------------------------------------- +template +inline /*static*/ +typename SList::Iterator SList::Iterator::End() +{ + return Iterator(NULL); +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::Iterator SList::Iterator::Insert( + PTR_T pItem) +{ + NO_DAC(); + _Validate(e_CanInsert); + *Traits::GetNextPtr(pItem) = *m_ppCur; + *m_ppCur = pItem; + Iterator itRet(m_ppCur); + ++(*this); + return itRet; +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::Iterator SList::Iterator::Remove() +{ + NO_DAC(); + _Validate(e_HasValue); + *m_ppCur = *Traits::GetNextPtr(*m_ppCur); + PTR_PTR_T ppRet = m_ppCur; + // Set it to End, so that subsequent misuse of this iterator will + // result in an AV rather than possible memory corruption. + *this = End(); + return Iterator(ppRet); +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::PTR_T SList::Iterator::_Value() const +{ + ASSERT(m_fIsValid); + return dac_cast(m_ppCur == NULL ? NULL : *m_ppCur); +} + +//------------------------------------------------------------------------------------------------- +template +inline +void SList::Iterator::_Validate(e_ValidateOperation op) const +{ + ASSERT(m_fIsValid); + ASSERT(op == e_CanCompare || op == e_CanInsert || op == e_HasValue); + + if ((op != e_CanCompare && m_ppCur == NULL) || + (op == e_HasValue && *m_ppCur == NULL)) + { + // NOTE: Default of DoNothingFailFastPolicy is a no-op, and so this function will be + // eliminated in retail builds. This is ok, as the subsequent operation will cause + // an AV, which will itself trigger a FailFast. Provide a different policy to get + // different behavior. + ASSERT_MSG(false, "Invalid SList::Iterator use."); + Traits::FailFast(); +#ifdef _DEBUG + m_fIsValid = false; +#endif + } +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::Iterator SList::Begin() +{ + typedef SList T_THIS; + return Iterator(dac_cast( + dac_cast(this) + offsetof(T_THIS, m_pHead))); +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::Iterator SList::End() +{ + return Iterator::End(); +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::Iterator SList::FindFirst(PTR_T pItem) +{ + return rh::std::find(Begin(), End(), pItem); +} + +//------------------------------------------------------------------------------------------------- +template +inline +bool SList::RemoveFirst(PTR_T pItem) +{ + NO_DAC(); + Iterator it = FindFirst(pItem); + if (it != End()) + { + it.Remove(); + return true; + } + else + { + return false; + } +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::Iterator SList::Insert(Iterator & it, PTR_T pItem) +{ + return it.Insert(pItem); +} + +//------------------------------------------------------------------------------------------------- +template +inline +typename SList::Iterator SList::Remove(Iterator & it) +{ + return it.Remove(); +} + + +MSVC_RESTORE_WARNING_STATE() + diff --git a/src/coreclr/nativeaot/Runtime/startup.cpp b/src/coreclr/nativeaot/Runtime/startup.cpp new file mode 100644 index 00000000000000..af4a025050e479 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/startup.cpp @@ -0,0 +1,432 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "gcrhinterface.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "holder.h" +#include "Crst.h" +#include "event.h" +#include "RWLock.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "RuntimeInstance.h" +#include "rhbinder.h" +#include "CachedInterfaceDispatch.h" +#include "RhConfig.h" +#include "stressLog.h" +#include "RestrictedCallouts.h" +#include "yieldprocessornormalized.h" + +#ifndef DACCESS_COMPILE + +#ifdef PROFILE_STARTUP +unsigned __int64 g_startupTimelineEvents[NUM_STARTUP_TIMELINE_EVENTS] = { 0 }; +#endif // PROFILE_STARTUP + +#ifdef TARGET_UNIX +int32_t RhpHardwareExceptionHandler(uintptr_t faultCode, uintptr_t faultAddress, PAL_LIMITED_CONTEXT* palContext, uintptr_t* arg0Reg, uintptr_t* arg1Reg); +#else +int32_t __stdcall RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs); +#endif + +extern "C" void PopulateDebugHeaders(); + +static bool DetectCPUFeatures(); + +extern RhConfig * g_pRhConfig; + +EXTERN_C bool g_fHasFastFxsave = false; + +CrstStatic g_CastCacheLock; +CrstStatic g_ThunkPoolLock; + +#if defined(HOST_X86) || defined(HOST_AMD64) || defined(HOST_ARM64) +// This field is inspected from the generated code to determine what intrinsics are available. +EXTERN_C int g_cpuFeatures = 0; +// This field is defined in the generated code and sets the ISA expectations. +EXTERN_C int g_requiredCpuFeatures; +#endif + +static bool InitDLL(HANDLE hPalInstance) +{ +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH + // + // Initialize interface dispatch. + // + if (!InitializeInterfaceDispatch()) + return false; +#endif + + // + // Initialize support for registering GC and HandleTable callouts. + // + if (!RestrictedCallouts::Initialize()) + return false; + + // + // Initialize RuntimeInstance state + // + if (!RuntimeInstance::Initialize(hPalInstance)) + return false; + + // Note: The global exception handler uses RuntimeInstance +#if !defined(USE_PORTABLE_HELPERS) +#ifndef TARGET_UNIX + PalAddVectoredExceptionHandler(1, RhpVectoredExceptionHandler); +#else + PalSetHardwareExceptionHandler(RhpHardwareExceptionHandler); +#endif +#endif // !USE_PORTABLE_HELPERS + + InitializeYieldProcessorNormalizedCrst(); + + STARTUP_TIMELINE_EVENT(NONGC_INIT_COMPLETE); + + if (!RedhawkGCInterface::InitializeSubsystems()) + return false; + + STARTUP_TIMELINE_EVENT(GC_INIT_COMPLETE); + +#ifdef STRESS_LOG + uint32_t dwTotalStressLogSize = g_pRhConfig->GetTotalStressLogSize(); + uint32_t dwStressLogLevel = g_pRhConfig->GetStressLogLevel(); + + unsigned facility = (unsigned)LF_ALL; + unsigned dwPerThreadChunks = (dwTotalStressLogSize / 24) / STRESSLOG_CHUNK_SIZE; + if (dwTotalStressLogSize != 0) + { + StressLog::Initialize(facility, dwStressLogLevel, + dwPerThreadChunks * STRESSLOG_CHUNK_SIZE, + (unsigned)dwTotalStressLogSize, hPalInstance); + } +#endif // STRESS_LOG + +#ifndef USE_PORTABLE_HELPERS + if (!DetectCPUFeatures()) + return false; +#endif + + if (!g_CastCacheLock.InitNoThrow(CrstType::CrstCastCache)) + return false; + + if (!g_ThunkPoolLock.InitNoThrow(CrstType::CrstCastCache)) + return false; + + return true; +} + +#ifndef USE_PORTABLE_HELPERS + +bool DetectCPUFeatures() +{ +#if defined(HOST_X86) || defined(HOST_AMD64) || defined(HOST_ARM64) + +#if defined(HOST_X86) || defined(HOST_AMD64) + + int cpuidInfo[4]; + + const int EAX = 0; + const int EBX = 1; + const int ECX = 2; + const int EDX = 3; + + __cpuid(cpuidInfo, 0x00000000); + uint32_t maxCpuId = static_cast(cpuidInfo[EAX]); + + if (maxCpuId >= 1) + { + __cpuid(cpuidInfo, 0x00000001); + + if (((cpuidInfo[EDX] & (1 << 25)) != 0) && ((cpuidInfo[EDX] & (1 << 26)) != 0)) // SSE & SSE2 + { + if ((cpuidInfo[ECX] & (1 << 25)) != 0) // AESNI + { + g_cpuFeatures |= XArchIntrinsicConstants_Aes; + } + + if ((cpuidInfo[ECX] & (1 << 1)) != 0) // PCLMULQDQ + { + g_cpuFeatures |= XArchIntrinsicConstants_Pclmulqdq; + } + + if ((cpuidInfo[ECX] & (1 << 0)) != 0) // SSE3 + { + g_cpuFeatures |= XArchIntrinsicConstants_Sse3; + + if ((cpuidInfo[ECX] & (1 << 9)) != 0) // SSSE3 + { + g_cpuFeatures |= XArchIntrinsicConstants_Ssse3; + + if ((cpuidInfo[ECX] & (1 << 19)) != 0) // SSE4.1 + { + g_cpuFeatures |= XArchIntrinsicConstants_Sse41; + + if ((cpuidInfo[ECX] & (1 << 20)) != 0) // SSE4.2 + { + g_cpuFeatures |= XArchIntrinsicConstants_Sse42; + + if ((cpuidInfo[ECX] & (1 << 23)) != 0) // POPCNT + { + g_cpuFeatures |= XArchIntrinsicConstants_Popcnt; + } + + if (((cpuidInfo[ECX] & (1 << 27)) != 0) && ((cpuidInfo[ECX] & (1 << 28)) != 0)) // OSXSAVE & AVX + { + if (PalIsAvxEnabled() && (xmmYmmStateSupport() == 1)) + { + g_cpuFeatures |= XArchIntrinsicConstants_Avx; + + if ((cpuidInfo[ECX] & (1 << 12)) != 0) // FMA + { + g_cpuFeatures |= XArchIntrinsicConstants_Fma; + } + + if (maxCpuId >= 0x07) + { + __cpuidex(cpuidInfo, 0x00000007, 0x00000000); + + if ((cpuidInfo[EBX] & (1 << 5)) != 0) // AVX2 + { + g_cpuFeatures |= XArchIntrinsicConstants_Avx2; + } + } + } + } + } + } + } + } + } + + if (maxCpuId >= 0x07) + { + __cpuidex(cpuidInfo, 0x00000007, 0x00000000); + + if ((cpuidInfo[EBX] & (1 << 3)) != 0) // BMI1 + { + g_cpuFeatures |= XArchIntrinsicConstants_Bmi1; + } + + if ((cpuidInfo[EBX] & (1 << 8)) != 0) // BMI2 + { + g_cpuFeatures |= XArchIntrinsicConstants_Bmi2; + } + } + } + + __cpuid(cpuidInfo, 0x80000000); + uint32_t maxCpuIdEx = static_cast(cpuidInfo[EAX]); + + if (maxCpuIdEx >= 0x80000001) + { + __cpuid(cpuidInfo, 0x80000001); + + if ((cpuidInfo[ECX] & (1 << 5)) != 0) // LZCNT + { + g_cpuFeatures |= XArchIntrinsicConstants_Lzcnt; + } + +#ifdef HOST_AMD64 + // AMD has a "fast" mode for fxsave/fxrstor, which omits the saving of xmm registers. The OS will enable this mode + // if it is supported. So if we continue to use fxsave/fxrstor, we must manually save/restore the xmm registers. + // fxsr_opt is bit 25 of EDX + if ((cpuidInfo[EDX] & (1 << 25)) != 0) + g_fHasFastFxsave = true; +#endif + } +#endif // HOST_X86 || HOST_AMD64 + +#if defined(HOST_ARM64) + PAL_GetCpuCapabilityFlags (&g_cpuFeatures); +#endif + + if ((g_cpuFeatures & g_requiredCpuFeatures) != g_requiredCpuFeatures) + { + return false; + } +#endif // HOST_X86 || HOST_AMD64 + + return true; +} +#endif // !USE_PORTABLE_HELPERS + +#ifdef PROFILE_STARTUP +#define STD_OUTPUT_HANDLE ((uint32_t)-11) + +struct RegisterModuleTrace +{ + LARGE_INTEGER Begin; + LARGE_INTEGER End; +}; + +const int NUM_REGISTER_MODULE_TRACES = 16; +int g_registerModuleCount = 0; + +RegisterModuleTrace g_registerModuleTraces[NUM_REGISTER_MODULE_TRACES] = { 0 }; + +static void AppendInt64(char * pBuffer, uint32_t* pLen, uint64_t value) +{ + char localBuffer[20]; + int cch = 0; + + do + { + localBuffer[cch++] = '0' + (value % 10); + value = value / 10; + } while (value); + + for (int i = 0; i < cch; i++) + { + pBuffer[(*pLen)++] = localBuffer[cch - i - 1]; + } + + pBuffer[(*pLen)++] = ','; + pBuffer[(*pLen)++] = ' '; +} +#endif // PROFILE_STARTUP + +static void UninitDLL() +{ +#ifdef PROFILE_STARTUP + char buffer[1024]; + + uint32_t len = 0; + + AppendInt64(buffer, &len, g_startupTimelineEvents[PROCESS_ATTACH_BEGIN]); + AppendInt64(buffer, &len, g_startupTimelineEvents[NONGC_INIT_COMPLETE]); + AppendInt64(buffer, &len, g_startupTimelineEvents[GC_INIT_COMPLETE]); + AppendInt64(buffer, &len, g_startupTimelineEvents[PROCESS_ATTACH_COMPLETE]); + + for (int i = 0; i < g_registerModuleCount; i++) + { + AppendInt64(buffer, &len, g_registerModuleTraces[i].Begin.QuadPart); + AppendInt64(buffer, &len, g_registerModuleTraces[i].End.QuadPart); + } + + buffer[len++] = '\n'; + + fwrite(buffer, len, 1, stdout); +#endif // PROFILE_STARTUP +} + +volatile bool g_processShutdownHasStarted = false; + +static void DllThreadDetach() +{ + // BEWARE: loader lock is held here! + + // Should have already received a call to FiberDetach for this thread's "home" fiber. + Thread* pCurrentThread = ThreadStore::GetCurrentThreadIfAvailable(); + if (pCurrentThread != NULL && !pCurrentThread->IsDetached()) + { + // Once shutdown starts, RuntimeThreadShutdown callbacks are ignored, implying that + // it is no longer guaranteed that exiting threads will be detached. + if (!g_processShutdownHasStarted) + { + ASSERT_UNCONDITIONALLY("Detaching thread whose home fiber has not been detached"); + RhFailFast(); + } + } +} + +void RuntimeThreadShutdown(void* thread) +{ + // Note: loader lock is normally *not* held here! + // The one exception is that the loader lock may be held during the thread shutdown callback + // that is made for the single thread that runs the final stages of orderly process + // shutdown (i.e., the thread that delivers the DLL_PROCESS_DETACH notifications when the + // process is being torn down via an ExitProcess call). + + UNREFERENCED_PARAMETER(thread); + +#ifdef TARGET_UNIX + // Some Linux toolset versions call thread-local destructors during shutdown on a wrong thread. + if ((Thread*)thread != ThreadStore::GetCurrentThread()) + { + return; + } +#else + ASSERT((Thread*)thread == ThreadStore::GetCurrentThread()); +#endif + + if (g_processShutdownHasStarted) + { + return; + } + + ThreadStore::DetachCurrentThread(); +} + +extern "C" bool RhInitialize() +{ + if (!PalInit()) + return false; + + if (!InitDLL(PalGetModuleHandleFromPointer((void*)&RhInitialize))) + return false; + + // Populate the values needed for debugging + PopulateDebugHeaders(); + + return true; +} + +COOP_PINVOKE_HELPER(void, RhpEnableConservativeStackReporting, ()) +{ + GetRuntimeInstance()->EnableConservativeStackReporting(); +} + +// +// Currently called only from a managed executable once Main returns, this routine does whatever is needed to +// cleanup managed state before exiting. There's not a lot here at the moment since we're always about to let +// the OS tear the process down anyway. +// +// @TODO: Eventually we'll probably have a hosting API and explicit shutdown request. When that happens we'll +// something more sophisticated here since we won't be able to rely on the OS cleaning up after us. +// +COOP_PINVOKE_HELPER(void, RhpShutdown, ()) +{ + // Indicate that runtime shutdown is complete and that the caller is about to start shutting down the entire process. + g_processShutdownHasStarted = true; +} + +#ifdef _WIN32 +EXTERN_C UInt32_BOOL WINAPI RtuDllMain(HANDLE hPalInstance, uint32_t dwReason, void* /*pvReserved*/) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + { + STARTUP_TIMELINE_EVENT(PROCESS_ATTACH_BEGIN); + + if (!InitDLL(hPalInstance)) + return FALSE; + + STARTUP_TIMELINE_EVENT(PROCESS_ATTACH_COMPLETE); + } + break; + + case DLL_PROCESS_DETACH: + UninitDLL(); + break; + + case DLL_THREAD_DETACH: + DllThreadDetach(); + break; + } + + return TRUE; +} +#endif // _WIN32 + +#endif // !DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/stressLog.cpp b/src/coreclr/nativeaot/Runtime/stressLog.cpp new file mode 100644 index 00000000000000..12df357966d78e --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/stressLog.cpp @@ -0,0 +1,598 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// --------------------------------------------------------------------------- +// StressLog.cpp +// +// StressLog infrastructure +// --------------------------------------------------------------------------- + +#include "common.h" +#ifdef DACCESS_COMPILE +#include +#include "sospriv.h" +#endif // DACCESS_COMPILE +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "daccess.h" +#include "stressLog.h" +#include "holder.h" +#include "Crst.h" +#include "rhassert.h" +#include "slist.h" +#include "gcrhinterface.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "RWLock.h" +#include "event.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "thread.inl" + +template inline T VolatileLoad(T const * pt) { return *(T volatile const *)pt; } +template inline void VolatileStore(T* pt, T val) { *(T volatile *)pt = val; } + +#ifdef STRESS_LOG + +typedef DPTR(StressLog) PTR_StressLog; +GPTR_IMPL(StressLog, g_pStressLog /*, &StressLog::theLog*/); + +#ifndef DACCESS_COMPILE + +/*********************************************************************************/ +#if defined(HOST_X86) + +/* This is like QueryPerformanceCounter but a lot faster. On machines with + variable-speed CPUs (for power management), this is not accurate, but may + be good enough. +*/ +inline __declspec(naked) unsigned __int64 getTimeStamp() { + + __asm { + RDTSC // read time stamp counter + ret + }; +} + +#else // HOST_X86 +unsigned __int64 getTimeStamp() { + + LARGE_INTEGER ret; + ZeroMemory(&ret, sizeof(LARGE_INTEGER)); + + PalQueryPerformanceCounter(&ret); + + return ret.QuadPart; +} + +#endif // HOST_X86 else + +/*********************************************************************************/ +/* Get the the frequency corresponding to 'getTimeStamp'. For non-x86 + architectures, this is just the performance counter frequency. +*/ +unsigned __int64 getTickFrequency() +{ + LARGE_INTEGER ret; + ZeroMemory(&ret, sizeof(LARGE_INTEGER)); + PalQueryPerformanceFrequency(&ret); + return ret.QuadPart; +} + +#endif // DACCESS_COMPILE + +StressLog StressLog::theLog = { 0, 0, 0, 0, 0, 0 }; +const static unsigned __int64 RECYCLE_AGE = 0x40000000L; // after a billion cycles, we can discard old threads + +/*********************************************************************************/ + +#ifndef DACCESS_COMPILE + +void StressLog::Initialize(unsigned facilities, unsigned level, unsigned maxBytesPerThread, + unsigned maxBytesTotal, HANDLE hMod) +{ + if (theLog.MaxSizePerThread != 0) + { + // guard ourself against multiple initialization. First init wins. + return; + } + + g_pStressLog = &theLog; + + theLog.pLock = new (nothrow) CrstStatic(); + theLog.pLock->Init(CrstStressLog); + if (maxBytesPerThread < STRESSLOG_CHUNK_SIZE) + { + maxBytesPerThread = STRESSLOG_CHUNK_SIZE; + } + theLog.MaxSizePerThread = maxBytesPerThread; + + if (maxBytesTotal < STRESSLOG_CHUNK_SIZE * 256) + { + maxBytesTotal = STRESSLOG_CHUNK_SIZE * 256; + } + theLog.MaxSizeTotal = maxBytesTotal; + theLog.totalChunk = 0; + theLog.facilitiesToLog = facilities | LF_ALWAYS; + theLog.levelToLog = level; + theLog.deadCount = 0; + + theLog.tickFrequency = getTickFrequency(); + + PalGetSystemTimeAsFileTime (&theLog.startTime); + theLog.startTimeStamp = getTimeStamp(); + + theLog.moduleOffset = (size_t)hMod; // HMODULES are base addresses. +} + +/*********************************************************************************/ +/* create a new thread stress log buffer associated with pThread */ + +ThreadStressLog* StressLog::CreateThreadStressLog(Thread * pThread) { + + if (theLog.facilitiesToLog == 0) + return NULL; + + if (pThread == NULL) + pThread = ThreadStore::GetCurrentThread(); + + ThreadStressLog* msgs = reinterpret_cast(pThread->GetThreadStressLog()); + if (msgs != NULL) + { + return msgs; + } + + //if we are not allowed to allocate stress log, we should not even try to take the lock + if (pThread->IsInCantAllocStressLogRegion()) + { + return NULL; + } + + // if it looks like we won't be allowed to allocate a new chunk, exit early + if (VolatileLoad(&theLog.deadCount) == 0 && !AllowNewChunk (0)) + { + return NULL; + } + + CrstHolder holder(theLog.pLock); + + msgs = CreateThreadStressLogHelper(pThread); + + return msgs; +} + +ThreadStressLog* StressLog::CreateThreadStressLogHelper(Thread * pThread) { + + bool skipInsert = FALSE; + ThreadStressLog* msgs = NULL; + + // See if we can recycle a dead thread + if (VolatileLoad(&theLog.deadCount) > 0) + { + unsigned __int64 recycleStamp = getTimeStamp() - RECYCLE_AGE; + msgs = VolatileLoad(&theLog.logs); + //find out oldest dead ThreadStressLog in case we can't find one within + //recycle age but can't create a new chunk + ThreadStressLog * oldestDeadMsg = NULL; + + while(msgs != 0) + { + if (msgs->isDead) + { + bool hasTimeStamp = msgs->curPtr != (StressMsg *)msgs->chunkListTail->EndPtr(); + if (hasTimeStamp && msgs->curPtr->timeStamp < recycleStamp) + { + skipInsert = TRUE; + PalInterlockedDecrement(&theLog.deadCount); + break; + } + + if (!oldestDeadMsg) + { + oldestDeadMsg = msgs; + } + else if (hasTimeStamp && oldestDeadMsg->curPtr->timeStamp > msgs->curPtr->timeStamp) + { + oldestDeadMsg = msgs; + } + } + + msgs = msgs->next; + } + + //if the total stress log size limit is already passed and we can't add new chunk, + //always reuse the oldest dead msg + if (!AllowNewChunk (0) && !msgs) + { + msgs = oldestDeadMsg; + skipInsert = TRUE; + PalInterlockedDecrement(&theLog.deadCount); + } + } + + if (msgs == 0) { + msgs = new (nothrow) ThreadStressLog(); + + if (msgs == 0 ||!msgs->IsValid ()) + { + delete msgs; + msgs = 0; + goto LEAVE; + } + } + + msgs->Activate (pThread); + + if (!skipInsert) { +#ifdef _DEBUG + ThreadStressLog* walk = VolatileLoad(&theLog.logs); + while (walk) + { + _ASSERTE (walk != msgs); + walk = walk->next; + } +#endif + // Put it into the stress log + msgs->next = VolatileLoad(&theLog.logs); + VolatileStore(&theLog.logs, msgs); + } + +LEAVE: + ; + return msgs; +} + +/*********************************************************************************/ +/* static */ +void StressLog::ThreadDetach(ThreadStressLog *msgs) { + + if (msgs == 0) + { + return; + } + + // We should write this message to the StressLog for deleted fiber. + msgs->LogMsg (LF_STARTUP, 0, "******* DllMain THREAD_DETACH called Thread dying *******\n"); + + msgs->isDead = TRUE; + PalInterlockedIncrement(&theLog.deadCount); +} + +bool StressLog::AllowNewChunk (long numChunksInCurThread) +{ + Thread* pCurrentThread = ThreadStore::GetCurrentThread(); + if (pCurrentThread->IsInCantAllocStressLogRegion()) + { + return FALSE; + } + + _ASSERTE (numChunksInCurThread <= VolatileLoad(&theLog.totalChunk)); + uint32_t perThreadLimit = theLog.MaxSizePerThread; + + if (numChunksInCurThread == 0 /*&& IsSuspendEEThread()*/) + return TRUE; + + if (pCurrentThread->IsGCSpecial()) + { + perThreadLimit *= GC_STRESSLOG_MULTIPLY; + } + + if ((uint32_t)numChunksInCurThread * STRESSLOG_CHUNK_SIZE >= perThreadLimit) + { + return FALSE; + } + + return (uint32_t)VolatileLoad(&theLog.totalChunk) * STRESSLOG_CHUNK_SIZE < theLog.MaxSizeTotal; +} + +bool StressLog::ReserveStressLogChunks (unsigned chunksToReserve) +{ + Thread *pThread = ThreadStore::GetCurrentThread(); + ThreadStressLog* msgs = reinterpret_cast(pThread->GetThreadStressLog()); + if (msgs == 0) + { + msgs = CreateThreadStressLog(pThread); + + if (msgs == 0) + return FALSE; + } + + if (chunksToReserve == 0) + { + chunksToReserve = (theLog.MaxSizePerThread + STRESSLOG_CHUNK_SIZE - 1) / STRESSLOG_CHUNK_SIZE; + } + + long numTries = (long)chunksToReserve - msgs->chunkListLength; + for (long i = 0; i < numTries; i++) + { + msgs->GrowChunkList (); + } + + return msgs->chunkListLength >= (long)chunksToReserve; +} + +/*********************************************************************************/ +/* fetch a buffer that can be used to write a stress message, it is thread safe */ + +void ThreadStressLog::LogMsg ( uint32_t facility, int cArgs, const char* format, va_list Args) +{ + + // Asserts in this function cause infinite loops in the asserting mechanism. + // Just use debug breaks instead. + + ASSERT( cArgs >= 0 && cArgs <= StressMsg::maxArgCnt ); + + size_t offs = ((size_t)format - StressLog::theLog.moduleOffset); + + ASSERT(offs < StressMsg::maxOffset); + if (offs >= StressMsg::maxOffset) + { + // Set it to this string instead. + offs = +#ifdef _DEBUG + (size_t)""; +#else // _DEBUG + 0; // a 0 offset is ignored by StressLog::Dump +#endif // _DEBUG else + } + + // Get next available slot + StressMsg* msg = AdvanceWrite(cArgs); + + msg->timeStamp = getTimeStamp(); + msg->facility = facility; + msg->formatOffset = offs; + msg->numberOfArgs = cArgs; + + for ( int i = 0; i < cArgs; ++i ) + { + void* data = va_arg(Args, void*); + msg->args[i] = data; + } + + ASSERT(IsValid() && threadId == PalGetCurrentThreadIdForLogging()); +} + + +void ThreadStressLog::Activate (Thread * pThread) +{ + _ASSERTE(pThread != NULL); + //there is no need to zero buffers because we could handle garbage contents + threadId = PalGetCurrentThreadIdForLogging(); + isDead = FALSE; + curWriteChunk = chunkListTail; + curPtr = (StressMsg *)curWriteChunk->EndPtr (); + writeHasWrapped = FALSE; + this->pThread = pThread; + ASSERT(pThread->IsCurrentThread()); +} + +/* static */ +void StressLog::LogMsg (unsigned facility, int cArgs, const char* format, ... ) +{ + _ASSERTE ( cArgs >= 0 && cArgs <= StressMsg::maxArgCnt ); + + va_list Args; + va_start(Args, format); + + Thread *pThread = ThreadStore::GetCurrentThread(); + if (pThread == NULL) + return; + + ThreadStressLog* msgs = reinterpret_cast(pThread->GetThreadStressLog()); + + if (msgs == 0) { + msgs = CreateThreadStressLog(pThread); + + if (msgs == 0) + return; + } + msgs->LogMsg (facility, cArgs, format, Args); +} + +#ifdef _DEBUG + +/* static */ +void StressLog::LogCallStack(const char *const callTag){ + + size_t CallStackTrace[MAX_CALL_STACK_TRACE]; + uint32_t hash; + unsigned short stackTraceCount = PalCaptureStackBackTrace (2, MAX_CALL_STACK_TRACE, (void**)CallStackTrace, &hash); + if (stackTraceCount > MAX_CALL_STACK_TRACE) + stackTraceCount = MAX_CALL_STACK_TRACE; + LogMsgOL("Start of %s stack \n", callTag); + unsigned short i = 0; + for (;i < stackTraceCount; i++) + { + LogMsgOL("(%s stack)%pK\n", callTag, CallStackTrace[i]); + } + LogMsgOL("End of %s stack\n", callTag); +} + +#endif //_DEBUG + +#else // DACCESS_COMPILE + +bool StressLog::Initialize() +{ + ThreadStressLog* logs = 0; + + ThreadStressLog* curThreadStressLog = this->logs; + unsigned __int64 lastTimeStamp = 0; // timestamp of last log entry + while(curThreadStressLog != 0) + { + if (!curThreadStressLog->IsReadyForRead()) + { + if (curThreadStressLog->origCurPtr == NULL) + curThreadStressLog->origCurPtr = curThreadStressLog->curPtr; + + // avoid repeated calls into this function + StressLogChunk * head = curThreadStressLog->chunkListHead; + StressLogChunk * curChunk = head; + bool curPtrInitialized = false; + do + { + if (!curChunk->IsValid ()) + { + // TODO: Report corrupt chunk PTR_HOST_TO_TADDR(curChunk) + } + + if (!curPtrInitialized && curChunk == curThreadStressLog->curWriteChunk) + { + // adjust curPtr to the debugger's address space + curThreadStressLog->curPtr = (StressMsg *)((uint8_t *)curChunk + ((uint8_t *)curThreadStressLog->curPtr - (uint8_t *)PTR_HOST_TO_TADDR(curChunk))); + curPtrInitialized = true; + } + + curChunk = curChunk->next; + } while (curChunk != head); + + if (!curPtrInitialized) + { + delete curThreadStressLog; + return false; + } + + // adjust readPtr and curPtr if needed + curThreadStressLog->Activate (NULL); + } + curThreadStressLog = curThreadStressLog->next; + } + return true; +} + +void StressLog::ResetForRead() +{ + ThreadStressLog* curThreadStressLog = this->logs; + while(curThreadStressLog != 0) + { + curThreadStressLog->readPtr = NULL; + curThreadStressLog->curPtr = curThreadStressLog->origCurPtr; + curThreadStressLog = curThreadStressLog->next; + } +} + +// Initialization of the ThreadStressLog when dumping the log +inline void ThreadStressLog::Activate (Thread * /*pThread*/) +{ + // avoid repeated calls into this function + if (IsReadyForRead()) + return; + + curReadChunk = curWriteChunk; + readPtr = curPtr; + readHasWrapped = false; + // the last written log, if it wrapped around may have partially overwritten + // a previous record. Update curPtr to reflect the last safe beginning of a record, + // but curPtr shouldn't wrap around, otherwise it'll break our assumptions about stress + // log + curPtr = (StressMsg*)((char*)curPtr - StressMsg::maxMsgSize()); + if (curPtr < (StressMsg*)curWriteChunk->StartPtr()) + { + curPtr = (StressMsg *)curWriteChunk->StartPtr(); + } + // corner case: the log is empty + if (readPtr == (StressMsg *)curReadChunk->EndPtr ()) + { + AdvReadPastBoundary(); + } +} + +ThreadStressLog* StressLog::FindLatestThreadLog() const +{ + const ThreadStressLog* latestLog = 0; + for (const ThreadStressLog* ptr = this->logs; ptr != NULL; ptr = ptr->next) + { + if (ptr->readPtr != NULL) + if (latestLog == 0 || ptr->readPtr->timeStamp > latestLog->readPtr->timeStamp) + latestLog = ptr; + } + return const_cast(latestLog); +} + +// Can't refer to the types in sospriv.h because it drags in windows.h +void StressLog::EnumerateStressMsgs(/*STRESSMSGCALLBACK*/void* smcbWrapper, /*ENDTHREADLOGCALLBACK*/void* etcbWrapper, void *token) +{ + STRESSMSGCALLBACK smcb = (STRESSMSGCALLBACK)smcbWrapper; + ENDTHREADLOGCALLBACK etcb = (ENDTHREADLOGCALLBACK) etcbWrapper; + void *argsCopy[StressMsg::maxArgCnt]; + + for (;;) + { + ThreadStressLog* latestLog = this->FindLatestThreadLog(); + + if (latestLog == 0) + { + break; + } + StressMsg* latestMsg = latestLog->readPtr; + if (latestMsg->formatOffset != 0 && !latestLog->CompletedDump()) + { + char format[256]; + TADDR taFmt = (latestMsg->formatOffset) + (TADDR)(this->moduleOffset); + HRESULT hr = DacReadAll(taFmt, format, _countof(format), false); + if (hr != S_OK) + strcpy_s(format, _countof(format), "Could not read address of format string"); + + double deltaTime = ((double) (latestMsg->timeStamp - this->startTimeStamp)) / this->tickFrequency; + + // Pass a copy of the args to the callback to avoid foreign code overwriting the stress log + // entries (this was the case for %s arguments) + memcpy_s(argsCopy, sizeof(argsCopy), latestMsg->args, (latestMsg->numberOfArgs)*sizeof(void*)); + + // @TODO: CORERT: Truncating threadId to 32-bit + if (!smcb((UINT32)latestLog->threadId, deltaTime, latestMsg->facility, format, argsCopy, token)) + break; + } + + latestLog->readPtr = latestLog->AdvanceRead(); + if (latestLog->CompletedDump()) + { + latestLog->readPtr = NULL; + + // @TODO: CORERT: Truncating threadId to 32-bit + if (!etcb((UINT32)latestLog->threadId, token)) + break; + } + } +} + +typedef DPTR(SIZE_T) PTR_SIZE_T; + +// Can't refer to the types in sospriv.h because it drags in windows.h +void StressLog::EnumStressLogMemRanges(/*STRESSLOGMEMRANGECALLBACK*/void* slmrcbWrapper, void *token) +{ + STRESSLOGMEMRANGECALLBACK slmrcb = (STRESSLOGMEMRANGECALLBACK)slmrcbWrapper; + + // we go to extreme lengths to ensure we don't read in the whole memory representation + // of the stress log, but only the ranges... + // + + size_t ThreadStressLogAddr = *dac_cast(PTR_HOST_MEMBER_TADDR(StressLog, this, logs)); + while (ThreadStressLogAddr != NULL) + { + size_t ChunkListHeadAddr = *dac_cast(ThreadStressLogAddr + offsetof(ThreadStressLog, chunkListHead)); + size_t StressLogChunkAddr = ChunkListHeadAddr; + + do + { + slmrcb(StressLogChunkAddr, sizeof (StressLogChunk), token); + StressLogChunkAddr = *dac_cast(StressLogChunkAddr + offsetof (StressLogChunk, next)); + if (StressLogChunkAddr == NULL) + { + return; + } + } while (StressLogChunkAddr != ChunkListHeadAddr); + + ThreadStressLogAddr = *dac_cast(ThreadStressLogAddr + offsetof(ThreadStressLog, next)); + } +} + + +#endif // !DACCESS_COMPILE + +#endif // STRESS_LOG + diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp new file mode 100644 index 00000000000000..5a76666c11faad --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -0,0 +1,1428 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "gcrhinterface.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "holder.h" +#include "Crst.h" +#include "event.h" +#include "RWLock.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "thread.inl" +#include "RuntimeInstance.h" +#include "shash.h" +#include "rhbinder.h" +#include "stressLog.h" +#include "RhConfig.h" + +#ifndef DACCESS_COMPILE + +EXTERN_C REDHAWK_API void* REDHAWK_CALLCONV RhpHandleAlloc(void* pObject, int type); +EXTERN_C REDHAWK_API void REDHAWK_CALLCONV RhHandleSet(void* handle, void* pObject); +EXTERN_C REDHAWK_API void REDHAWK_CALLCONV RhHandleFree(void* handle); + +static int (*g_RuntimeInitializationCallback)(); +static Thread* g_RuntimeInitializingThread; + +#ifdef _MSC_VER +extern "C" void _ReadWriteBarrier(void); +#pragma intrinsic(_ReadWriteBarrier) +#else // _MSC_VER +#define _ReadWriteBarrier() __asm__ volatile("" : : : "memory") +#endif // _MSC_VER +#endif //!DACCESS_COMPILE + +PTR_VOID Thread::GetTransitionFrame() +{ + if (ThreadStore::GetSuspendingThread() == this) + { + // This thread is in cooperative mode, so we grab the transition frame + // from the 'tunnel' location, which will have the frame from the most + // recent 'cooperative pinvoke' transition that brought us here. + ASSERT(m_pHackPInvokeTunnel != NULL); + return m_pHackPInvokeTunnel; + } + + ASSERT(m_pCachedTransitionFrame != NULL); + return m_pCachedTransitionFrame; +} + +#ifndef DACCESS_COMPILE + +PTR_VOID Thread::GetTransitionFrameForStackTrace() +{ + ASSERT_MSG(ThreadStore::GetSuspendingThread() == NULL, "Not allowed when suspended for GC."); + ASSERT_MSG(this == ThreadStore::GetCurrentThread(), "Only supported for current thread."); + ASSERT(Thread::IsCurrentThreadInCooperativeMode()); + ASSERT(m_pHackPInvokeTunnel != NULL); + return m_pHackPInvokeTunnel; +} + +void Thread::WaitForSuspend() +{ + Unhijack(); + GetThreadStore()->WaitForSuspendComplete(); +} + +void Thread::WaitForGC(void * pTransitionFrame) +{ + ASSERT(!IsDoNotTriggerGcSet()); + + do + { + m_pTransitionFrame = pTransitionFrame; + + Unhijack(); + RedhawkGCInterface::WaitForGCCompletion(); + + m_pTransitionFrame = NULL; + + // We need to prevent compiler reordering between above write and below read. + _ReadWriteBarrier(); + } + while (ThreadStore::IsTrapThreadsRequested()); +} + +// +// This is used by the suspension code when driving all threads to unmanaged code. It is performed after +// the FlushProcessWriteBuffers call so that we know that once the thread reaches unmanaged code, it won't +// reenter managed code. Therefore, the m_pTransitionFrame is stable. Except that it isn't. The return-to- +// managed sequence will temporarily overwrite the m_pTransitionFrame to be 0. As a result, we need to cache +// the non-zero m_pTransitionFrame value that we saw during suspend so that stackwalks can read this value +// without concern of sometimes reading a 0, as would be the case if they read m_pTransitionFrame directly. +// +// Returns true if it sucessfully cached the transition frame (i.e. the thread was in unmanaged). +// Returns false otherwise. +// +bool Thread::CacheTransitionFrameForSuspend() +{ + if (m_pCachedTransitionFrame != NULL) + return true; + + PTR_VOID temp = m_pTransitionFrame; // volatile read + if (temp == NULL) + return false; + + m_pCachedTransitionFrame = temp; + return true; +} + +void Thread::ResetCachedTransitionFrame() +{ + // @TODO: I don't understand this assert because ResumeAllThreads is clearly written + // to be reseting other threads' cached transition frames. + + //ASSERT((ThreadStore::GetCurrentThreadIfAvailable() == this) || + // (m_pCachedTransitionFrame != NULL)); + m_pCachedTransitionFrame = NULL; +} + +// This function simulates a PInvoke transition using a frame pointer from somewhere further up the stack that +// was passed in via the m_pHackPInvokeTunnel field. It is used to allow us to grandfather-in the set of GC +// code that runs in cooperative mode without having to rewrite it in managed code. The result is that the +// code that calls into this special mode must spill preserved registers as if it's going to PInvoke, but +// record its transition frame pointer in m_pHackPInvokeTunnel and leave the thread in the cooperative +// mode. Later on, when this function is called, we effect the state transition to 'unmanaged' using the +// previously setup transition frame. +void Thread::EnablePreemptiveMode() +{ + ASSERT(ThreadStore::GetCurrentThread() == this); +#if !defined(HOST_WASM) + ASSERT(m_pHackPInvokeTunnel != NULL); +#endif + + Unhijack(); + + // ORDERING -- this write must occur before checking the trap + m_pTransitionFrame = m_pHackPInvokeTunnel; + + // We need to prevent compiler reordering between above write and below read. Both the read and the write + // are volatile, so it's possible that the particular semantic for volatile that MSVC provides is enough, + // but if not, this barrier would be required. If so, it won't change anything to add the barrier. + _ReadWriteBarrier(); + + if (ThreadStore::IsTrapThreadsRequested()) + { + WaitForSuspend(); + } +} + +void Thread::DisablePreemptiveMode() +{ + ASSERT(ThreadStore::GetCurrentThread() == this); + + // ORDERING -- this write must occur before checking the trap + m_pTransitionFrame = NULL; + + // We need to prevent compiler reordering between above write and below read. Both the read and the write + // are volatile, so it's possible that the particular semantic for volatile that MSVC provides is enough, + // but if not, this barrier would be required. If so, it won't change anything to add the barrier. + _ReadWriteBarrier(); + + if (ThreadStore::IsTrapThreadsRequested() && (this != ThreadStore::GetSuspendingThread())) + { + WaitForGC(m_pHackPInvokeTunnel); + } +} +#endif // !DACCESS_COMPILE + +bool Thread::IsCurrentThreadInCooperativeMode() +{ +#ifndef DACCESS_COMPILE + ASSERT(ThreadStore::GetCurrentThread() == this); +#endif // !DACCESS_COMPILE + return (m_pTransitionFrame == NULL); +} + +// +// This is used by the EH system to find the place where execution left managed code when an exception leaks out of a +// pinvoke and we need to FailFast via the appropriate class library. +// +// May only be used from the same thread and while in preemptive mode with an active pinvoke on the stack. +// +#ifndef DACCESS_COMPILE +void * Thread::GetCurrentThreadPInvokeReturnAddress() +{ + ASSERT(ThreadStore::GetCurrentThread() == this); + ASSERT(!IsCurrentThreadInCooperativeMode()); + return ((PInvokeTransitionFrame*)m_pTransitionFrame)->m_RIP; +} +#endif // !DACCESS_COMPILE + +#if defined(FEATURE_GC_STRESS) & !defined(DACCESS_COMPILE) +void Thread::SetRandomSeed(uint32_t seed) +{ + ASSERT(!IsStateSet(TSF_IsRandSeedSet)); + m_uRand = seed; + SetState(TSF_IsRandSeedSet); +} + +// Generates pseudo random numbers in the range [0, 2^31) +// using only multiplication and addition +uint32_t Thread::NextRand() +{ + // Uses Carta's algorithm for Park-Miller's PRNG: + // x_{k+1} = 16807 * x_{k} mod (2^31-1) + + uint32_t hi,lo; + + // (high word of seed) * 16807 - at most 31 bits + hi = 16807 * (m_uRand >> 16); + // (low word of seed) * 16807 - at most 31 bits + lo = 16807 * (m_uRand & 0xFFFF); + + // Proof that below operations (multiplication and addition only) + // are equivalent to the original formula: + // x_{k+1} = 16807 * x_{k} mod (2^31-1) + // We denote hi2 as the low 15 bits in hi, + // and hi1 as the remaining 16 bits in hi: + // (hi * 2^16 + lo) mod (2^31-1) = + // ((hi1 * 2^15 + hi2) * 2^16 + lo) mod (2^31-1) = + // ( hi1 * 2^31 + hi2 * 2^16 + lo) mod (2^31-1) = + // ( hi1 * (2^31-1) + hi1 + hi2 * 2^16 + lo) mod (2^31-1) = + // ( hi2 * 2^16 + hi1 + lo ) mod (2^31-1) + + // lo + (hi2 * 2^16) + lo += (hi & 0x7FFF) << 16; + // lo + (hi2 * 2^16) + hi1 + lo += (hi >> 15); + // modulo (2^31-1) + if (lo > 0x7fffFFFF) + lo -= 0x7fffFFFF; + + m_uRand = lo; + + return m_uRand; +} + +bool Thread::IsRandInited() +{ + return IsStateSet(TSF_IsRandSeedSet); +} +#endif // FEATURE_GC_STRESS & !DACCESS_COMPILE + +PTR_ExInfo Thread::GetCurExInfo() +{ + ValidateExInfoStack(); + return m_pExInfoStackHead; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef DACCESS_COMPILE + +void Thread::Construct() +{ +#ifndef USE_PORTABLE_HELPERS + C_ASSERT(OFFSETOF__Thread__m_pTransitionFrame == + (offsetof(Thread, m_pTransitionFrame))); +#endif // USE_PORTABLE_HELPERS + + m_numDynamicTypesTlsCells = 0; + m_pDynamicTypesTlsCells = NULL; + + m_pThreadLocalModuleStatics = NULL; + m_numThreadLocalModuleStatics = 0; + + // NOTE: We do not explicitly defer to the GC implementation to initialize the alloc_context. The + // alloc_context will be initialized to 0 via the static initialization of tls_CurrentThread. If the + // alloc_context ever needs different initialization, a matching change to the tls_CurrentThread + // static initialization will need to be made. + + m_uPalThreadIdForLogging = PalGetCurrentThreadIdForLogging(); + m_threadId.SetToCurrentThread(); + + HANDLE curProcessPseudo = PalGetCurrentProcess(); + HANDLE curThreadPseudo = PalGetCurrentThread(); + + // This can fail! Users of m_hPalThread must be able to handle INVALID_HANDLE_VALUE!! + PalDuplicateHandle(curProcessPseudo, curThreadPseudo, curProcessPseudo, &m_hPalThread, + 0, // ignored + FALSE, // inherit + DUPLICATE_SAME_ACCESS); + + if (!PalGetMaximumStackBounds(&m_pStackLow, &m_pStackHigh)) + RhFailFast(); + + m_pTEB = PalNtCurrentTeb(); + +#ifdef STRESS_LOG + if (StressLog::StressLogOn(~0u, 0)) + m_pThreadStressLog = StressLog::CreateThreadStressLog(this); +#endif // STRESS_LOG + + m_threadAbortException = NULL; +} + +bool Thread::IsInitialized() +{ + return (m_ThreadStateFlags != TSF_Unknown); +} + +// ----------------------------------------------------------------------------------------------------------- +// GC support APIs - do not use except from GC itself +// +void Thread::SetGCSpecial(bool isGCSpecial) +{ + if (!IsInitialized()) + Construct(); + if (isGCSpecial) + SetState(TSF_IsGcSpecialThread); + else + ClearState(TSF_IsGcSpecialThread); +} + +bool Thread::IsGCSpecial() +{ + return IsStateSet(TSF_IsGcSpecialThread); +} + +bool Thread::CatchAtSafePoint() +{ + // This is only called by the GC on a background GC worker thread that's explicitly interested in letting + // a foreground GC proceed at that point. So it's always safe to return true. + ASSERT(IsGCSpecial()); + return true; +} + +uint64_t Thread::GetPalThreadIdForLogging() +{ + return m_uPalThreadIdForLogging; +} + +bool Thread::IsCurrentThread() +{ + return m_threadId.IsCurrentThread(); +} + +void Thread::Detach() +{ + // Thread::Destroy is called when the thread's "home" fiber dies. We mark the thread as "detached" here + // so that we can validate, in our DLL_THREAD_DETACH handler, that the thread was already destroyed at that + // point. + SetDetached(); + + RedhawkGCInterface::ReleaseAllocContext(GetAllocContext()); +} + +void Thread::Destroy() +{ + ASSERT(IsDetached()); + + if (m_hPalThread != INVALID_HANDLE_VALUE) + PalCloseHandle(m_hPalThread); + + if (m_pDynamicTypesTlsCells != NULL) + { + for (uint32_t i = 0; i < m_numDynamicTypesTlsCells; i++) + { + if (m_pDynamicTypesTlsCells[i] != NULL) + delete[] m_pDynamicTypesTlsCells[i]; + } + delete[] m_pDynamicTypesTlsCells; + } + + if (m_pThreadLocalModuleStatics != NULL) + { + for (uint32_t i = 0; i < m_numThreadLocalModuleStatics; i++) + { + if (m_pThreadLocalModuleStatics[i] != NULL) + { + RhHandleFree(m_pThreadLocalModuleStatics[i]); + } + } + delete[] m_pThreadLocalModuleStatics; + } + +#ifdef STRESS_LOG + ThreadStressLog* ptsl = reinterpret_cast(GetThreadStressLog()); + StressLog::ThreadDetach(ptsl); +#endif // STRESS_LOG +} + +#ifdef HOST_WASM +extern RtuObjectRef * t_pShadowStackTop; +extern RtuObjectRef * t_pShadowStackBottom; + +void GcScanWasmShadowStack(void * pfnEnumCallback, void * pvCallbackData) +{ + // Wasm does not permit iteration of stack frames so is uses a shadow stack instead + RedhawkGCInterface::EnumGcRefsInRegionConservatively(t_pShadowStackBottom, t_pShadowStackTop, pfnEnumCallback, pvCallbackData); +} +#endif + +void Thread::GcScanRoots(void * pfnEnumCallback, void * pvCallbackData) +{ +#ifdef HOST_WASM + GcScanWasmShadowStack(pfnEnumCallback, pvCallbackData); +#else + StackFrameIterator frameIterator(this, GetTransitionFrame()); + GcScanRootsWorker(pfnEnumCallback, pvCallbackData, frameIterator); +#endif +} + +#endif // !DACCESS_COMPILE + +#ifdef DACCESS_COMPILE +// A trivial wrapper that unpacks the DacScanCallbackData and calls the callback provided to GcScanRoots +void GcScanRootsCallbackWrapper(PTR_RtuObjectRef ppObject, DacScanCallbackData* callbackData, uint32_t flags) +{ + Thread::GcScanRootsCallbackFunc * pfnUserCallback = (Thread::GcScanRootsCallbackFunc *)callbackData->pfnUserCallback; + pfnUserCallback(ppObject, callbackData->token, flags); +} + +bool Thread::GcScanRoots(GcScanRootsCallbackFunc * pfnEnumCallback, void * token, PTR_PAL_LIMITED_CONTEXT pInitialContext) +{ + DacScanCallbackData callbackDataWrapper; + callbackDataWrapper.thread_under_crawl = this; + callbackDataWrapper.promotion = true; + callbackDataWrapper.token = token; + callbackDataWrapper.pfnUserCallback = pfnEnumCallback; + //When debugging we might be trying to enumerate with or without a transition frame + //on top of the stack. If there is one use it, otherwise the debugger provides a set of initial registers + //to use. + PTR_VOID pTransitionFrame = GetTransitionFrame(); + if(pTransitionFrame != NULL) + { + StackFrameIterator frameIterator(this, GetTransitionFrame()); + GcScanRootsWorker(&GcScanRootsCallbackWrapper, &callbackDataWrapper, frameIterator); + } + else + { + if(pInitialContext == NULL) + return false; + StackFrameIterator frameIterator(this, pInitialContext); + GcScanRootsWorker(&GcScanRootsCallbackWrapper, &callbackDataWrapper, frameIterator); + } + return true; +} +#endif //DACCESS_COMPILE + +void Thread::GcScanRootsWorker(void * pfnEnumCallback, void * pvCallbackData, StackFrameIterator & frameIterator) +{ + PTR_RtuObjectRef pHijackedReturnValue = NULL; + GCRefKind returnValueKind = GCRK_Unknown; + + if (frameIterator.GetHijackedReturnValueLocation(&pHijackedReturnValue, &returnValueKind)) + { +#ifdef TARGET_ARM64 + GCRefKind reg0Kind = ExtractReg0ReturnKind(returnValueKind); + GCRefKind reg1Kind = ExtractReg1ReturnKind(returnValueKind); + + // X0 and X1 are saved next to each other in this order + if (reg0Kind != GCRK_Scalar) + { + RedhawkGCInterface::EnumGcRef(pHijackedReturnValue, reg0Kind, pfnEnumCallback, pvCallbackData); + } + if (reg1Kind != GCRK_Scalar) + { + RedhawkGCInterface::EnumGcRef(pHijackedReturnValue + 1, reg1Kind, pfnEnumCallback, pvCallbackData); + } +#else + RedhawkGCInterface::EnumGcRef(pHijackedReturnValue, returnValueKind, pfnEnumCallback, pvCallbackData); +#endif + } + +#ifndef DACCESS_COMPILE + if (GetRuntimeInstance()->IsConservativeStackReportingEnabled()) + { + if (frameIterator.IsValid()) + { + PTR_VOID pLowerBound = dac_cast(frameIterator.GetRegisterSet()->GetSP()); + + // Transition frame may contain callee saved registers that need to be reported as well + PTR_VOID pTransitionFrame = GetTransitionFrame(); + ASSERT(pTransitionFrame != NULL); + if (pTransitionFrame < pLowerBound) + pLowerBound = pTransitionFrame; + + PTR_VOID pUpperBound = m_pStackHigh; + + RedhawkGCInterface::EnumGcRefsInRegionConservatively( + dac_cast(pLowerBound), + dac_cast(pUpperBound), + pfnEnumCallback, + pvCallbackData); + } + } + else +#endif // !DACCESS_COMPILE + { + while (frameIterator.IsValid()) + { + frameIterator.CalculateCurrentMethodState(); + + STRESS_LOG1(LF_GCROOTS, LL_INFO1000, "Scanning method %pK\n", (void*)frameIterator.GetRegisterSet()->IP); + + if (!frameIterator.ShouldSkipRegularGcReporting()) + { + RedhawkGCInterface::EnumGcRefs(frameIterator.GetCodeManager(), + frameIterator.GetMethodInfo(), + frameIterator.GetEffectiveSafePointAddress(), + frameIterator.GetRegisterSet(), + pfnEnumCallback, + pvCallbackData); + } + + // Each enumerated frame (including the first one) may have an associated stack range we need to + // report conservatively (every pointer aligned value that looks like it might be a GC reference is + // reported as a pinned interior reference). This occurs in an edge case where a managed method whose + // signature the runtime is not aware of calls into the runtime which subsequently calls back out + // into managed code (allowing the possibility of a garbage collection). This can happen in certain + // interface invocation slow paths for instance. Since the original managed call may have passed GC + // references which are unreported by any managed method on the stack at the time of the GC we + // identify (again conservatively) the range of the stack that might contain these references and + // report everything. Since it should be a very rare occurrence indeed that we actually have to do + // this this, it's considered a better trade-off than storing signature metadata for every potential + // callsite of the type described above. + if (frameIterator.HasStackRangeToReportConservatively()) + { + PTR_RtuObjectRef pLowerBound; + PTR_RtuObjectRef pUpperBound; + frameIterator.GetStackRangeToReportConservatively(&pLowerBound, &pUpperBound); + RedhawkGCInterface::EnumGcRefsInRegionConservatively(pLowerBound, + pUpperBound, + pfnEnumCallback, + pvCallbackData); + } + + frameIterator.Next(); + } + } + + // ExInfos hold exception objects that are not reported by anyone else. In fact, sometimes they are in + // logically dead parts of the stack that the typical GC stackwalk skips. (This happens in the case where + // one exception dispatch superseded a previous one.) We keep them alive as long as they are in the + // ExInfo chain to aid in post-mortem debugging. SOS will access them through the DAC and the exported + // API, RhGetExceptionsForCurrentThread, will access them at runtime to gather additional information to + // add to a dump file during FailFast. + for (PTR_ExInfo curExInfo = GetCurExInfo(); curExInfo != NULL; curExInfo = curExInfo->m_pPrevExInfo) + { + PTR_RtuObjectRef pExceptionObj = dac_cast(&curExInfo->m_exception); + RedhawkGCInterface::EnumGcRef(pExceptionObj, GCRK_Object, pfnEnumCallback, pvCallbackData); + } + + // Keep alive the ThreadAbortException that's stored in the target thread during thread abort + PTR_RtuObjectRef pThreadAbortExceptionObj = dac_cast(&m_threadAbortException); + RedhawkGCInterface::EnumGcRef(pThreadAbortExceptionObj, GCRK_Object, pfnEnumCallback, pvCallbackData); +} + +#ifndef DACCESS_COMPILE + +#ifndef TARGET_ARM64 +EXTERN_C void FASTCALL RhpGcProbeHijackScalar(); +EXTERN_C void FASTCALL RhpGcProbeHijackObject(); +EXTERN_C void FASTCALL RhpGcProbeHijackByref(); + +static void* NormalHijackTargets[3] = +{ + reinterpret_cast(RhpGcProbeHijackScalar), // GCRK_Scalar = 0, + reinterpret_cast(RhpGcProbeHijackObject), // GCRK_Object = 1, + reinterpret_cast(RhpGcProbeHijackByref) // GCRK_Byref = 2, +}; +#else // TARGET_ARM64 +EXTERN_C void FASTCALL RhpGcProbeHijack(); + +static void* NormalHijackTargets[1] = +{ + reinterpret_cast(RhpGcProbeHijack) +}; +#endif // TARGET_ARM64 + +#ifdef FEATURE_GC_STRESS +#ifndef TARGET_ARM64 +EXTERN_C void FASTCALL RhpGcStressHijackScalar(); +EXTERN_C void FASTCALL RhpGcStressHijackObject(); +EXTERN_C void FASTCALL RhpGcStressHijackByref(); + +static void* GcStressHijackTargets[3] = +{ + reinterpret_cast(RhpGcStressHijackScalar), // GCRK_Scalar = 0, + reinterpret_cast(RhpGcStressHijackObject), // GCRK_Object = 1, + reinterpret_cast(RhpGcStressHijackByref) // GCRK_Byref = 2, +}; +#else // TARGET_ARM64 +EXTERN_C void FASTCALL RhpGcStressHijack(); + +static void* GcStressHijackTargets[1] = +{ + reinterpret_cast(RhpGcStressHijack) +}; +#endif // TARGET_ARM64 +#endif // FEATURE_GC_STRESS + +// static +bool Thread::IsHijackTarget(void * address) +{ + for (int i = 0; i < COUNTOF(NormalHijackTargets); i++) + { + if (NormalHijackTargets[i] == address) + return true; + } +#ifdef FEATURE_GC_STRESS + for (int i = 0; i < COUNTOF(GcStressHijackTargets); i++) + { + if (GcStressHijackTargets[i] == address) + return true; + } +#endif // FEATURE_GC_STRESS + return false; +} + +bool Thread::Hijack() +{ + ASSERT(ThreadStore::GetCurrentThread() == ThreadStore::GetSuspendingThread()); + + ASSERT_MSG(ThreadStore::GetSuspendingThread() != this, "You may not hijack a thread from itself."); + + if (m_hPalThread == INVALID_HANDLE_VALUE) + { + // cannot proceed + return false; + } + + // requires THREAD_SUSPEND_RESUME / THREAD_GET_CONTEXT / THREAD_SET_CONTEXT permissions + + Thread* pCurrentThread = ThreadStore::GetCurrentThread(); + pCurrentThread->EnterCantAllocRegion(); + uint32_t result = PalHijack(m_hPalThread, HijackCallback, this); + pCurrentThread->LeaveCantAllocRegion(); + return result == 0; + +} + +UInt32_BOOL Thread::HijackCallback(HANDLE /*hThread*/, PAL_LIMITED_CONTEXT* pThreadContext, void* pCallbackContext) +{ + Thread* pThread = (Thread*) pCallbackContext; + + // + // WARNING: The hijack operation will take a read lock on the RuntimeInstance's module list. + // (This is done to find a Module based on an IP.) Therefore, if the thread we've just + // suspended owns the write lock on the module list, we'll deadlock with it when we try to + // take the read lock below. So we must attempt a non-blocking acquire of the read lock + // early and fail the hijack if we can't get it. This will cause us to simply retry later. + // + if (GetRuntimeInstance()->m_ModuleListLock.DangerousTryPulseReadLock()) + { + if (pThread->CacheTransitionFrameForSuspend()) + { + // IMPORTANT: GetThreadContext should not be trusted arbitrarily. We are careful here to recheck + // the thread's state flag that indicates whether or not it has made it to unmanaged code. If + // it has reached unmanaged code (even our own wait helper routines), then we cannot trust the + // context returned by it. This is due to various races that occur updating the reported context + // during syscalls. + return TRUE; + } + else + { + return pThread->InternalHijack(pThreadContext, NormalHijackTargets) ? TRUE : FALSE; + } + } + + return FALSE; +} + +#ifdef FEATURE_GC_STRESS +// This is a helper called from RhpHijackForGcStress which will place a GC Stress +// hijack on this thread's call stack. This is never called from another thread. +// static +void Thread::HijackForGcStress(PAL_LIMITED_CONTEXT * pSuspendCtx) +{ + Thread * pCurrentThread = ThreadStore::GetCurrentThread(); + + // don't hijack for GC stress if we're in a "no GC stress" region + if (pCurrentThread->IsSuppressGcStressSet()) + return; + + RuntimeInstance * pInstance = GetRuntimeInstance(); + + uintptr_t ip = pSuspendCtx->GetIp(); + + bool bForceGC = g_pRhConfig->GetGcStressThrottleMode() == 0; + // we enable collecting statistics by callsite even for stochastic-only + // stress mode. this will force a stack walk, but it's worthwhile for + // collecting data (we only actually need the IP when + // (g_pRhConfig->GetGcStressThrottleMode() & 1) != 0) + if (!bForceGC) + { + StackFrameIterator sfi(pCurrentThread, pSuspendCtx); + if (sfi.IsValid()) + { + pCurrentThread->Unhijack(); + sfi.CalculateCurrentMethodState(); + // unwind to method below the one whose epilog set up the hijack + sfi.Next(); + if (sfi.IsValid()) + { + ip = sfi.GetRegisterSet()->GetIP(); + } + } + } + if (bForceGC || pInstance->ShouldHijackCallsiteForGcStress(ip)) + { + pCurrentThread->InternalHijack(pSuspendCtx, GcStressHijackTargets); + } +} +#endif // FEATURE_GC_STRESS + +// This function is called in one of two scenarios: +// 1) from a thread to place a return hijack onto its own stack. This is only done for GC stress cases +// via Thread::HijackForGcStress above. +// 2) from another thread to place a return hijack onto this thread's stack. In this case the target +// thread is OS suspended someplace in managed code. The only constraint on the suspension is that the +// stack be crawlable enough to yield the location of the return address. +bool Thread::InternalHijack(PAL_LIMITED_CONTEXT * pSuspendCtx, void * pvHijackTargets[]) +{ + bool fSuccess = false; + + if (IsDoNotTriggerGcSet()) + return false; + + StackFrameIterator frameIterator(this, pSuspendCtx); + + if (frameIterator.IsValid()) + { + frameIterator.CalculateCurrentMethodState(); + + frameIterator.GetCodeManager()->UnsynchronizedHijackMethodLoops(frameIterator.GetMethodInfo()); + + PTR_PTR_VOID ppvRetAddrLocation; + GCRefKind retValueKind; + + if (frameIterator.GetCodeManager()->GetReturnAddressHijackInfo(frameIterator.GetMethodInfo(), + frameIterator.GetRegisterSet(), + &ppvRetAddrLocation, + &retValueKind)) + { + // ARM64 epilogs have a window between loading the hijackable return address into LR and the RET instruction. + // We cannot hijack or unhijack a thread while it is suspended in that window unless we implement hijacking + // via LR register modification. Therefore it is important to check our ability to hijack the thread before + // unhijacking it. + CrossThreadUnhijack(); + + void* pvRetAddr = *ppvRetAddrLocation; + ASSERT(ppvRetAddrLocation != NULL); + ASSERT(pvRetAddr != NULL); + + ASSERT(StackFrameIterator::IsValidReturnAddress(pvRetAddr)); + + m_ppvHijackedReturnAddressLocation = ppvRetAddrLocation; + m_pvHijackedReturnAddress = pvRetAddr; +#ifdef TARGET_ARM64 + m_uHijackedReturnValueFlags = ReturnKindToTransitionFrameFlags(retValueKind); + *ppvRetAddrLocation = pvHijackTargets[0]; +#else + void* pvHijackTarget = pvHijackTargets[retValueKind]; + ASSERT_MSG(IsHijackTarget(pvHijackTarget), "unexpected method used as hijack target"); + *ppvRetAddrLocation = pvHijackTarget; +#endif + fSuccess = true; + } + } + + STRESS_LOG3(LF_STACKWALK, LL_INFO10000, "InternalHijack: TgtThread = %llx, IP = %p, result = %d\n", + GetPalThreadIdForLogging(), pSuspendCtx->GetIp(), fSuccess); + + return fSuccess; +} + +// This is the standard Unhijack, which is only allowed to be called on your own thread. +// Note that all the asm-implemented Unhijacks should also only be operating on their +// own thread. +void Thread::Unhijack() +{ + ASSERT(ThreadStore::GetCurrentThread() == this); + UnhijackWorker(); +} + +// This unhijack routine is only called from Thread::InternalHijack() to undo a possibly existing +// hijack before placing a new one. Although there are many code sequences (here and in asm) to +// perform an unhijack operation, they will never execute concurrently. A thread may unhijack itself +// at any time so long as it does so from unmanaged code. This ensures that another thread will not +// suspend it and attempt to unhijack it, since we only suspend threads that are executing managed +// code. +void Thread::CrossThreadUnhijack() +{ + ASSERT((ThreadStore::GetCurrentThread() == this) || DebugIsSuspended()); + UnhijackWorker(); +} + +// This is the hijack worker routine which merely implements the hijack mechanism. +// DO NOT USE DIRECTLY. Use Unhijack() or CrossThreadUnhijack() instead. +void Thread::UnhijackWorker() +{ + if (m_pvHijackedReturnAddress == NULL) + { + ASSERT(m_ppvHijackedReturnAddressLocation == NULL); + return; + } + + // Restore the original return address. + ASSERT(m_ppvHijackedReturnAddressLocation != NULL); + *m_ppvHijackedReturnAddressLocation = m_pvHijackedReturnAddress; + + // Clear the hijack state. + m_ppvHijackedReturnAddressLocation = NULL; + m_pvHijackedReturnAddress = NULL; +#ifdef TARGET_ARM64 + m_uHijackedReturnValueFlags = 0; +#endif +} + +#if _DEBUG +bool Thread::DebugIsSuspended() +{ + ASSERT(ThreadStore::GetCurrentThread() != this); +#if 0 + PalSuspendThread(m_hPalThread); + uint32_t suspendCount = PalResumeThread(m_hPalThread); + return (suspendCount > 0); +#else + // @TODO: I don't trust the above implementation, so I want to implement this myself + // by marking the thread state as "yes, we suspended it" and checking that state here. + return true; +#endif +} +#endif + +// @TODO: it would be very, very nice if we did not have to bleed knowledge of hijacking +// and hijack state to other components in the runtime. For now, these are only used +// when getting EH info during exception dispatch. We should find a better way to encapsulate +// this. +bool Thread::IsHijacked() +{ + // Note: this operation is only valid from the current thread. If one thread invokes + // this on another then it may be racing with other changes to the thread's hijack state. + ASSERT(ThreadStore::GetCurrentThread() == this); + + return m_pvHijackedReturnAddress != NULL; +} + +// +// WARNING: This method must ONLY be called during stackwalks when we believe that all threads are +// synchronized and there is no other thread racing with us trying to apply hijacks. +// +bool Thread::DangerousCrossThreadIsHijacked() +{ + // If we have a CachedTransitionFrame available, then we're in the proper state. Otherwise, this method + // was called from an improper state. + ASSERT(GetTransitionFrame() != NULL); + return m_pvHijackedReturnAddress != NULL; +} + +void * Thread::GetHijackedReturnAddress() +{ + // Note: this operation is only valid from the current thread. If one thread invokes + // this on another then it may be racing with other changes to the thread's hijack state. + ASSERT(IsHijacked()); + ASSERT(ThreadStore::GetCurrentThread() == this); + + return m_pvHijackedReturnAddress; +} + +void * Thread::GetUnhijackedReturnAddress(void ** ppvReturnAddressLocation) +{ + ASSERT(ThreadStore::GetCurrentThread() == this); + + void * pvReturnAddress; + if (m_ppvHijackedReturnAddressLocation == ppvReturnAddressLocation) + pvReturnAddress = m_pvHijackedReturnAddress; + else + pvReturnAddress = *ppvReturnAddressLocation; + + ASSERT(NULL != GetRuntimeInstance()->FindCodeManagerByAddress(pvReturnAddress)); + return pvReturnAddress; +} + +void Thread::SetState(ThreadStateFlags flags) +{ + PalInterlockedOr(&m_ThreadStateFlags, flags); +} + +void Thread::ClearState(ThreadStateFlags flags) +{ + PalInterlockedAnd(&m_ThreadStateFlags, ~flags); +} + +bool Thread::IsStateSet(ThreadStateFlags flags) +{ + return ((m_ThreadStateFlags & flags) == (uint32_t) flags); +} + +bool Thread::IsSuppressGcStressSet() +{ + return IsStateSet(TSF_SuppressGcStress); +} + +void Thread::SetSuppressGcStress() +{ + ASSERT(!IsStateSet(TSF_SuppressGcStress)); + SetState(TSF_SuppressGcStress); +} + +void Thread::ClearSuppressGcStress() +{ + ASSERT(IsStateSet(TSF_SuppressGcStress)); + ClearState(TSF_SuppressGcStress); +} + +#endif //!DACCESS_COMPILE + +#ifndef DACCESS_COMPILE +#ifdef FEATURE_GC_STRESS +#ifdef HOST_X86 // the others are implemented in assembly code to avoid trashing the argument registers +EXTERN_C void FASTCALL RhpSuppressGcStress() +{ + ThreadStore::GetCurrentThread()->SetSuppressGcStress(); +} +#endif // HOST_X86 + +EXTERN_C void FASTCALL RhpUnsuppressGcStress() +{ + ThreadStore::GetCurrentThread()->ClearSuppressGcStress(); +} +#else +EXTERN_C void FASTCALL RhpSuppressGcStress() +{ +} +EXTERN_C void FASTCALL RhpUnsuppressGcStress() +{ +} +#endif // FEATURE_GC_STRESS + +// Standard calling convention variant and actual implementation for RhpWaitForSuspend +EXTERN_C NOINLINE void FASTCALL RhpWaitForSuspend2() +{ + // The wait operation below may trash the last win32 error. We save the error here so that it can be + // restored after the wait operation; + int32_t lastErrorOnEntry = PalGetLastError(); + + ThreadStore::GetCurrentThread()->WaitForSuspend(); + + // Restore the saved error + PalSetLastError(lastErrorOnEntry); +} + +// Standard calling convention variant and actual implementation for RhpWaitForGC +EXTERN_C NOINLINE void FASTCALL RhpWaitForGC2(PInvokeTransitionFrame * pFrame) +{ + + Thread * pThread = pFrame->m_pThread; + + if (pThread->IsDoNotTriggerGcSet()) + return; + + // The wait operation below may trash the last win32 error. We save the error here so that it can be + // restored after the wait operation; + int32_t lastErrorOnEntry = PalGetLastError(); + + pThread->WaitForGC(pFrame); + + // Restore the saved error + PalSetLastError(lastErrorOnEntry); +} + +// Standard calling convention variant and actual implementation for RhpGcPoll +EXTERN_C NOINLINE void FASTCALL RhpGcPoll2(PInvokeTransitionFrame* pFrame) +{ + Thread* pThread = ThreadStore::GetCurrentThread(); + pFrame->m_pThread = pThread; + + RhpWaitForGC2(pFrame); +} + +void Thread::PushExInfo(ExInfo * pExInfo) +{ + ValidateExInfoStack(); + + pExInfo->m_pPrevExInfo = m_pExInfoStackHead; + m_pExInfoStackHead = pExInfo; +} + +void Thread::ValidateExInfoPop(ExInfo * pExInfo, void * limitSP) +{ +#ifdef _DEBUG + ValidateExInfoStack(); + ASSERT_MSG(pExInfo == m_pExInfoStackHead, "not popping the head element"); + pExInfo = pExInfo->m_pPrevExInfo; + + while (pExInfo && pExInfo < limitSP) + { + ASSERT_MSG(pExInfo->m_kind & EK_SupersededFlag, "popping a non-superseded ExInfo"); + pExInfo = pExInfo->m_pPrevExInfo; + } +#else + UNREFERENCED_PARAMETER(pExInfo); + UNREFERENCED_PARAMETER(limitSP); +#endif // _DEBUG +} + +COOP_PINVOKE_HELPER(void, RhpValidateExInfoPop, (Thread * pThread, ExInfo * pExInfo, void * limitSP)) +{ + pThread->ValidateExInfoPop(pExInfo, limitSP); +} + +bool Thread::IsDoNotTriggerGcSet() +{ + return IsStateSet(TSF_DoNotTriggerGc); +} + +void Thread::SetDoNotTriggerGc() +{ + ASSERT(!IsStateSet(TSF_DoNotTriggerGc)); + SetState(TSF_DoNotTriggerGc); +} + +void Thread::ClearDoNotTriggerGc() +{ + // Allowing unmatched clears simplifies the EH dispatch code, so we do not assert anything here. + ClearState(TSF_DoNotTriggerGc); +} + +bool Thread::IsDetached() +{ + return IsStateSet(TSF_Detached); +} + +void Thread::SetDetached() +{ + ASSERT(!IsStateSet(TSF_Detached)); + SetState(TSF_Detached); +} + +#endif // !DACCESS_COMPILE + +void Thread::ValidateExInfoStack() +{ +#ifndef DACCESS_COMPILE +#ifdef _DEBUG + ExInfo temp; + + ExInfo* pCur = m_pExInfoStackHead; + while (pCur) + { + ASSERT_MSG((this != ThreadStore::GetCurrentThread()) || (pCur > &temp), "an entry in the ExInfo chain points into dead stack"); + ASSERT_MSG(pCur < m_pStackHigh, "an entry in the ExInfo chain isn't on this stack"); + pCur = pCur->m_pPrevExInfo; + } +#endif // _DEBUG +#endif // !DACCESS_COMPILE +} + + + +// Retrieve the start of the TLS storage block allocated for the given thread for a specific module identified +// by the TLS slot index allocated to that module and the offset into the OS allocated block at which +// Redhawk-specific data is stored. +PTR_UInt8 Thread::GetThreadLocalStorage(uint32_t uTlsIndex, uint32_t uTlsStartOffset) +{ +#if 0 + return (*(uint8_t***)(m_pTEB + OFFSETOF__TEB__ThreadLocalStoragePointer))[uTlsIndex] + uTlsStartOffset; +#else + return (*dac_cast(dac_cast(m_pTEB) + OFFSETOF__TEB__ThreadLocalStoragePointer))[uTlsIndex] + uTlsStartOffset; +#endif +} + +PTR_UInt8 Thread::GetThreadLocalStorageForDynamicType(uint32_t uTlsTypeOffset) +{ + // Note: When called from GC root enumeration, no changes can be made by the AllocateThreadLocalStorageForDynamicType to + // the 2 variables accessed here because AllocateThreadLocalStorageForDynamicType is called in cooperative mode. + + uTlsTypeOffset &= ~DYNAMIC_TYPE_TLS_OFFSET_FLAG; + return dac_cast(uTlsTypeOffset < m_numDynamicTypesTlsCells ? m_pDynamicTypesTlsCells[uTlsTypeOffset] : NULL); +} + +#ifndef DACCESS_COMPILE +PTR_UInt8 Thread::AllocateThreadLocalStorageForDynamicType(uint32_t uTlsTypeOffset, uint32_t tlsStorageSize, uint32_t numTlsCells) +{ + uTlsTypeOffset &= ~DYNAMIC_TYPE_TLS_OFFSET_FLAG; + + if (m_pDynamicTypesTlsCells == NULL || m_numDynamicTypesTlsCells <= uTlsTypeOffset) + { + // Keep at least a 2x grow so that we don't have to reallocate everytime a new type with TLS statics is created + if (numTlsCells < 2 * m_numDynamicTypesTlsCells) + numTlsCells = 2 * m_numDynamicTypesTlsCells; + + PTR_UInt8* pTlsCells = new (nothrow) PTR_UInt8[numTlsCells]; + if (pTlsCells == NULL) + return NULL; + + memset(&pTlsCells[m_numDynamicTypesTlsCells], 0, sizeof(PTR_UInt8) * (numTlsCells - m_numDynamicTypesTlsCells)); + + if (m_pDynamicTypesTlsCells != NULL) + { + memcpy(pTlsCells, m_pDynamicTypesTlsCells, sizeof(PTR_UInt8) * m_numDynamicTypesTlsCells); + delete[] m_pDynamicTypesTlsCells; + } + + m_pDynamicTypesTlsCells = pTlsCells; + m_numDynamicTypesTlsCells = numTlsCells; + } + + ASSERT(uTlsTypeOffset < m_numDynamicTypesTlsCells); + + if (m_pDynamicTypesTlsCells[uTlsTypeOffset] == NULL) + { + uint8_t* pTlsStorage = new (nothrow) uint8_t[tlsStorageSize]; + if (pTlsStorage == NULL) + return NULL; + + // Initialize storage to 0's before returning it + memset(pTlsStorage, 0, tlsStorageSize); + + m_pDynamicTypesTlsCells[uTlsTypeOffset] = pTlsStorage; + } + + return m_pDynamicTypesTlsCells[uTlsTypeOffset]; +} + +#ifndef TARGET_UNIX +EXTERN_C REDHAWK_API uint32_t __cdecl RhCompatibleReentrantWaitAny(UInt32_BOOL alertable, uint32_t timeout, uint32_t count, HANDLE* pHandles) +{ + return PalCompatibleWaitAny(alertable, timeout, count, pHandles, /*allowReentrantWait:*/ TRUE); +} +#endif // TARGET_UNIX + +FORCEINLINE bool Thread::InlineTryFastReversePInvoke(ReversePInvokeFrame * pFrame) +{ + // Do we need to attach the thread? + if (!IsStateSet(TSF_Attached)) + return false; // thread is not attached + + // If the thread is already in cooperative mode, this is a bad transition that will be a fail fast unless we are in + // a do not trigger mode. The exception to the rule allows us to have [UnmanagedCallersOnly] methods that are called via + // the "restricted GC callouts" as well as from native, which is necessary because the methods are CCW vtable + // methods on interfaces passed to native. + if (IsCurrentThreadInCooperativeMode()) + { + if (IsDoNotTriggerGcSet()) + { + // RhpTrapThreads will always be set in this case, so we must skip that check. We must be sure to + // zero-out our 'previous transition frame' state first, however. + pFrame->m_savedPInvokeTransitionFrame = NULL; + return true; + } + + return false; // bad transition + } + + // save the previous transition frame + pFrame->m_savedPInvokeTransitionFrame = m_pTransitionFrame; + + // set our mode to cooperative + m_pTransitionFrame = NULL; + + // We need to prevent compiler reordering between above write and below read. + _ReadWriteBarrier(); + + // now check if we need to trap the thread + if (ThreadStore::IsTrapThreadsRequested()) + { + // put the previous frame back (sets us back to preemptive mode) + m_pTransitionFrame = pFrame->m_savedPInvokeTransitionFrame; + return false; // need to trap the thread + } + + return true; +} + +EXTERN_C void RhSetRuntimeInitializationCallback(int (*fPtr)()) +{ + g_RuntimeInitializationCallback = fPtr; +} + +void Thread::ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame) +{ + if (!IsStateSet(TSF_Attached)) + { + if (g_RuntimeInitializationCallback != NULL && g_RuntimeInitializingThread != this) + { + EnsureRuntimeInitialized(); + } + + ThreadStore::AttachCurrentThread(); + } + + // If the thread is already in cooperative mode, this is a bad transition. + if (IsCurrentThreadInCooperativeMode()) + { + // The TSF_DoNotTriggerGc mode is handled by the fast path (InlineTryFastReversePInvoke or equivalent assembly code) + ASSERT(!IsDoNotTriggerGcSet()); + + // The platform specific assembly PInvoke helpers will route this fault to the class library inferred from the return + // address for nicer error reporting. For configurations without assembly helpers, we are going to fail fast without + // going through the class library here. + // RhpReversePInvokeBadTransition(return address); + RhFailFast(); + } + + // save the previous transition frame + pFrame->m_savedPInvokeTransitionFrame = m_pTransitionFrame; + + // set our mode to cooperative + m_pTransitionFrame = NULL; + + // We need to prevent compiler reordering between above write and below read. + _ReadWriteBarrier(); + + // now check if we need to trap the thread + if (ThreadStore::IsTrapThreadsRequested()) + { + WaitForGC(pFrame->m_savedPInvokeTransitionFrame); + } +} + +void Thread::EnsureRuntimeInitialized() +{ + while (PalInterlockedCompareExchangePointer((void *volatile *)&g_RuntimeInitializingThread, this, NULL) != NULL) + { + PalSleep(1); + } + + if (g_RuntimeInitializationCallback != NULL) + { + if (g_RuntimeInitializationCallback() != 0) + RhFailFast(); + + g_RuntimeInitializationCallback = NULL; + } + + PalInterlockedExchangePointer((void *volatile *)&g_RuntimeInitializingThread, NULL); +} + +FORCEINLINE void Thread::InlineReversePInvokeReturn(ReversePInvokeFrame * pFrame) +{ + m_pTransitionFrame = pFrame->m_savedPInvokeTransitionFrame; + if (ThreadStore::IsTrapThreadsRequested()) + { + RhpWaitForSuspend2(); + } +} + +FORCEINLINE void Thread::InlinePInvoke(PInvokeTransitionFrame * pFrame) +{ + pFrame->m_pThread = this; + // set our mode to preemptive + m_pTransitionFrame = pFrame; + + // We need to prevent compiler reordering between above write and below read. + _ReadWriteBarrier(); + + // now check if we need to trap the thread + if (ThreadStore::IsTrapThreadsRequested()) + { + RhpWaitForSuspend2(); + } +} + +FORCEINLINE void Thread::InlinePInvokeReturn(PInvokeTransitionFrame * pFrame) +{ + m_pTransitionFrame = NULL; + if (ThreadStore::IsTrapThreadsRequested()) + { + RhpWaitForGC2(pFrame); + } +} + +Object * Thread::GetThreadAbortException() +{ + return m_threadAbortException; +} + +void Thread::SetThreadAbortException(Object *exception) +{ + m_threadAbortException = exception; +} + +COOP_PINVOKE_HELPER(Object *, RhpGetThreadAbortException, ()) +{ + Thread * pCurThread = ThreadStore::RawGetCurrentThread(); + return pCurThread->GetThreadAbortException(); +} + +Object* Thread::GetThreadStaticStorageForModule(uint32_t moduleIndex) +{ + // Return a pointer to the TLS storage if it has already been + // allocated for the specified module. + if (moduleIndex < m_numThreadLocalModuleStatics) + { + Object** threadStaticsStorageHandle = (Object**)m_pThreadLocalModuleStatics[moduleIndex]; + if (threadStaticsStorageHandle != NULL) + { + return *threadStaticsStorageHandle; + } + } + + return NULL; +} + +bool Thread::SetThreadStaticStorageForModule(Object * pStorage, uint32_t moduleIndex) +{ + // Grow thread local storage if needed. + if (m_numThreadLocalModuleStatics <= moduleIndex) + { + uint32_t newSize = moduleIndex + 1; + if (newSize < moduleIndex) + { + return false; + } + + PTR_PTR_VOID pThreadLocalModuleStatics = new (nothrow) PTR_VOID[newSize]; + if (pThreadLocalModuleStatics == NULL) + { + return false; + } + + memset(&pThreadLocalModuleStatics[m_numThreadLocalModuleStatics], 0, sizeof(PTR_VOID) * (newSize - m_numThreadLocalModuleStatics)); + + if (m_pThreadLocalModuleStatics != NULL) + { + memcpy(pThreadLocalModuleStatics, m_pThreadLocalModuleStatics, sizeof(PTR_VOID) * m_numThreadLocalModuleStatics); + delete[] m_pThreadLocalModuleStatics; + } + + m_pThreadLocalModuleStatics = pThreadLocalModuleStatics; + m_numThreadLocalModuleStatics = newSize; + } + + if (m_pThreadLocalModuleStatics[moduleIndex] != NULL) + { + RhHandleSet(m_pThreadLocalModuleStatics[moduleIndex], pStorage); + } + else + { + void* threadStaticsStorageHandle = RhpHandleAlloc(pStorage, 2 /* Normal */); + if (threadStaticsStorageHandle == NULL) + { + return false; + } + m_pThreadLocalModuleStatics[moduleIndex] = threadStaticsStorageHandle; + } + + return true; +} + +COOP_PINVOKE_HELPER(Object*, RhGetThreadStaticStorageForModule, (uint32_t moduleIndex)) +{ + Thread * pCurrentThread = ThreadStore::RawGetCurrentThread(); + return pCurrentThread->GetThreadStaticStorageForModule(moduleIndex); +} + +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhSetThreadStaticStorageForModule, (Array * pStorage, uint32_t moduleIndex)) +{ + Thread * pCurrentThread = ThreadStore::RawGetCurrentThread(); + FC_RETURN_BOOL(pCurrentThread->SetThreadStaticStorageForModule((Object*)pStorage, moduleIndex)); +} + +// This is function is used to quickly query a value that can uniquely identify a thread +COOP_PINVOKE_HELPER(uint8_t*, RhCurrentNativeThreadId, ()) +{ +#ifndef TARGET_UNIX + return PalNtCurrentTeb(); +#else + return (uint8_t*)ThreadStore::RawGetCurrentThread(); +#endif // TARGET_UNIX +} + +// This function is used to get the OS thread identifier for the current thread. +COOP_PINVOKE_HELPER(uint64_t, RhCurrentOSThreadId, ()) +{ + return PalGetCurrentThreadIdForLogging(); +} + +// Standard calling convention variant and actual implementation for RhpReversePInvokeAttachOrTrapThread +EXTERN_C NOINLINE void FASTCALL RhpReversePInvokeAttachOrTrapThread2(ReversePInvokeFrame * pFrame) +{ + ASSERT(pFrame->m_savedThread == ThreadStore::RawGetCurrentThread()); + pFrame->m_savedThread->ReversePInvokeAttachOrTrapThread(pFrame); +} + +// +// PInvoke +// + +COOP_PINVOKE_HELPER(void, RhpReversePInvoke, (ReversePInvokeFrame * pFrame)) +{ + Thread * pCurThread = ThreadStore::RawGetCurrentThread(); + pFrame->m_savedThread = pCurThread; + if (pCurThread->InlineTryFastReversePInvoke(pFrame)) + return; + + RhpReversePInvokeAttachOrTrapThread2(pFrame); +} + +COOP_PINVOKE_HELPER(void, RhpReversePInvokeReturn, (ReversePInvokeFrame * pFrame)) +{ + pFrame->m_savedThread->InlineReversePInvokeReturn(pFrame); +} + +#ifdef USE_PORTABLE_HELPERS + +COOP_PINVOKE_HELPER(void, RhpPInvoke2, (PInvokeTransitionFrame* pFrame)) +{ + Thread * pCurThread = ThreadStore::RawGetCurrentThread(); + pCurThread->InlinePInvoke(pFrame); +} + +COOP_PINVOKE_HELPER(void, RhpPInvokeReturn2, (PInvokeTransitionFrame* pFrame)) +{ + //reenter cooperative mode + pFrame->m_pThread->InlinePInvokeReturn(pFrame); +} + +#endif //USE_PORTABLE_HELPERS + +#endif // !DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/thread.h b/src/coreclr/nativeaot/Runtime/thread.h new file mode 100644 index 00000000000000..10fb95a9b3f5d9 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/thread.h @@ -0,0 +1,308 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "forward_declarations.h" + +struct gc_alloc_context; +class RuntimeInstance; +class ThreadStore; +class CLREventStatic; +class Thread; + +// The offsets of some fields in the thread (in particular, m_pTransitionFrame) are known to the compiler and get +// inlined into the code. Let's make sure they don't change just because we enable/disable server GC in a particular +// runtime build. +#define KEEP_THREAD_LAYOUT_CONSTANT + +#ifndef HOST_64BIT +# if defined(FEATURE_SVR_GC) || defined(KEEP_THREAD_LAYOUT_CONSTANT) +# define SIZEOF_ALLOC_CONTEXT 40 +# else +# define SIZEOF_ALLOC_CONTEXT 28 +# endif +#else // HOST_64BIT +# if defined(FEATURE_SVR_GC) || defined(KEEP_THREAD_LAYOUT_CONSTANT) +# define SIZEOF_ALLOC_CONTEXT 56 +# else +# define SIZEOF_ALLOC_CONTEXT 40 +# endif +#endif // HOST_64BIT + +#define TOP_OF_STACK_MARKER ((PTR_VOID)(uintptr_t)(intptr_t)-1) + +#define DYNAMIC_TYPE_TLS_OFFSET_FLAG 0x80000000 + + +enum SyncRequestResult +{ + TryAgain, + SuccessUnmanaged, + SuccessManaged, +}; + +typedef DPTR(PAL_LIMITED_CONTEXT) PTR_PAL_LIMITED_CONTEXT; + +struct ExInfo; +typedef DPTR(ExInfo) PTR_ExInfo; + + +// Also defined in ExceptionHandling.cs, layouts must match. +// When adding new fields to this struct, ensure they get properly initialized in the exception handling +// assembly stubs +struct ExInfo +{ + + PTR_ExInfo m_pPrevExInfo; + PTR_PAL_LIMITED_CONTEXT m_pExContext; + PTR_Object m_exception; // actual object reference, specially reported by GcScanRootsWorker + ExKind m_kind; + uint8_t m_passNumber; + uint32_t m_idxCurClause; + StackFrameIterator m_frameIter; + volatile void* m_notifyDebuggerSP; +}; + +struct ThreadBuffer +{ + uint8_t m_rgbAllocContextBuffer[SIZEOF_ALLOC_CONTEXT]; + uint32_t volatile m_ThreadStateFlags; // see Thread::ThreadStateFlags enum +#if DACCESS_COMPILE + PTR_VOID m_pTransitionFrame; +#else + PTR_VOID volatile m_pTransitionFrame; +#endif + PTR_VOID m_pHackPInvokeTunnel; // see Thread::EnablePreemptiveMode + PTR_VOID m_pCachedTransitionFrame; + PTR_Thread m_pNext; // used by ThreadStore's SList + HANDLE m_hPalThread; // WARNING: this may legitimately be INVALID_HANDLE_VALUE + void ** m_ppvHijackedReturnAddressLocation; + void * m_pvHijackedReturnAddress; +#ifdef HOST_64BIT + uintptr_t m_uHijackedReturnValueFlags; // used on ARM64 only; however, ARM64 and AMD64 share field offsets +#endif // HOST_64BIT + PTR_ExInfo m_pExInfoStackHead; + Object* m_threadAbortException; // ThreadAbortException instance -set only during thread abort + PTR_PTR_VOID m_pThreadLocalModuleStatics; + uint32_t m_numThreadLocalModuleStatics; + PTR_VOID m_pStackLow; + PTR_VOID m_pStackHigh; + PTR_UInt8 m_pTEB; // Pointer to OS TEB structure for this thread + uint64_t m_uPalThreadIdForLogging; // @TODO: likely debug-only + EEThreadId m_threadId; + PTR_VOID m_pThreadStressLog; // pointer to head of thread's StressLogChunks + uint32_t m_cantAlloc; +#ifdef FEATURE_GC_STRESS + uint32_t m_uRand; // current per-thread random number +#endif // FEATURE_GC_STRESS + + // Thread Statics Storage for dynamic types + uint32_t m_numDynamicTypesTlsCells; + PTR_PTR_UInt8 m_pDynamicTypesTlsCells; + +}; + +struct ReversePInvokeFrame +{ + void* m_savedPInvokeTransitionFrame; + Thread* m_savedThread; +}; + +class Thread : private ThreadBuffer +{ + friend class AsmOffsets; + friend struct DefaultSListTraits; + friend class ThreadStore; + IN_DAC(friend class ClrDataAccess;) + +public: + enum ThreadStateFlags + { + TSF_Unknown = 0x00000000, // Threads are created in this state + TSF_Attached = 0x00000001, // Thread was inited by first U->M transition on this thread + TSF_Detached = 0x00000002, // Thread was detached by DllMain + TSF_SuppressGcStress = 0x00000008, // Do not allow gc stress on this thread, used in DllMain + // ...and on the Finalizer thread + TSF_DoNotTriggerGc = 0x00000010, // Do not allow hijacking of this thread, also intended to + // ...be checked during allocations in debug builds. + TSF_IsGcSpecialThread = 0x00000020, // Set to indicate a GC worker thread used for background GC +#ifdef FEATURE_GC_STRESS + TSF_IsRandSeedSet = 0x00000040, // set to indicate the random number generator for GCStress was inited +#endif // FEATURE_GC_STRESS + }; +private: + + void Construct(); + + void SetState(ThreadStateFlags flags); + void ClearState(ThreadStateFlags flags); + bool IsStateSet(ThreadStateFlags flags); + + static UInt32_BOOL HijackCallback(HANDLE hThread, PAL_LIMITED_CONTEXT* pThreadContext, void* pCallbackContext); + bool InternalHijack(PAL_LIMITED_CONTEXT * pSuspendCtx, void * pvHijackTargets[]); + + bool CacheTransitionFrameForSuspend(); + void ResetCachedTransitionFrame(); + void CrossThreadUnhijack(); + void UnhijackWorker(); + void EnsureRuntimeInitialized(); +#ifdef _DEBUG + bool DebugIsSuspended(); +#endif + + // + // SyncState members + // + PTR_VOID GetTransitionFrame(); + + void GcScanRootsWorker(void * pfnEnumCallback, void * pvCallbackData, StackFrameIterator & sfIter); + +public: + + void Detach(); // First phase of thread destructor, executed with thread store lock taken + void Destroy(); // Second phase of thread destructor, executed without thread store lock taken + + bool IsInitialized(); + + gc_alloc_context * GetAllocContext(); // @TODO: I would prefer to not expose this in this way + +#ifndef DACCESS_COMPILE + uint64_t GetPalThreadIdForLogging(); + bool IsCurrentThread(); + + void GcScanRoots(void * pfnEnumCallback, void * pvCallbackData); +#else + typedef void GcScanRootsCallbackFunc(PTR_RtuObjectRef ppObject, void* token, uint32_t flags); + bool GcScanRoots(GcScanRootsCallbackFunc * pfnCallback, void * token, PTR_PAL_LIMITED_CONTEXT pInitialContext); +#endif + + bool Hijack(); + void Unhijack(); +#ifdef FEATURE_GC_STRESS + static void HijackForGcStress(PAL_LIMITED_CONTEXT * pSuspendCtx); +#endif // FEATURE_GC_STRESS + bool IsHijacked(); + void * GetHijackedReturnAddress(); + void * GetUnhijackedReturnAddress(void** ppvReturnAddressLocation); + bool DangerousCrossThreadIsHijacked(); + + bool IsSuppressGcStressSet(); + void SetSuppressGcStress(); + void ClearSuppressGcStress(); + bool IsWithinStackBounds(PTR_VOID p); + + void GetStackBounds(PTR_VOID * ppStackLow, PTR_VOID * ppStackHigh); + + PTR_UInt8 AllocateThreadLocalStorageForDynamicType(uint32_t uTlsTypeOffset, uint32_t tlsStorageSize, uint32_t numTlsCells); + + PTR_UInt8 GetThreadLocalStorageForDynamicType(uint32_t uTlsTypeOffset); + PTR_UInt8 GetThreadLocalStorage(uint32_t uTlsIndex, uint32_t uTlsStartOffset); + + void PushExInfo(ExInfo * pExInfo); + void ValidateExInfoPop(ExInfo * pExInfo, void * limitSP); + void ValidateExInfoStack(); + bool IsDoNotTriggerGcSet(); + void SetDoNotTriggerGc(); + void ClearDoNotTriggerGc(); + + bool IsDetached(); + void SetDetached(); + + PTR_VOID GetThreadStressLog() const; +#ifndef DACCESS_COMPILE + void SetThreadStressLog(void * ptsl); +#endif // DACCESS_COMPILE + void EnterCantAllocRegion(); + void LeaveCantAllocRegion(); + bool IsInCantAllocStressLogRegion(); +#ifdef FEATURE_GC_STRESS + void SetRandomSeed(uint32_t seed); + uint32_t NextRand(); + bool IsRandInited(); +#endif // FEATURE_GC_STRESS + PTR_ExInfo GetCurExInfo(); + + bool IsCurrentThreadInCooperativeMode(); + + PTR_VOID GetTransitionFrameForStackTrace(); + void * GetCurrentThreadPInvokeReturnAddress(); + + static bool IsHijackTarget(void * address); + + // + // The set of operations used to support unmanaged code running in cooperative mode + // + void EnablePreemptiveMode(); + void DisablePreemptiveMode(); + + // Set the m_pHackPInvokeTunnel field for GC allocation helpers that setup transition frame + // in assembly code. Do not use anywhere else. + void SetCurrentThreadPInvokeTunnelForGcAlloc(void * pTransitionFrame); + + // Setup the m_pHackPInvokeTunnel field for GC helpers entered via regular PInvoke. + // Do not use anywhere else. + void SetupHackPInvokeTunnel(); + + // + // GC support APIs - do not use except from GC itself + // + void SetGCSpecial(bool isGCSpecial); + bool IsGCSpecial(); + bool CatchAtSafePoint(); + + // + // Managed/unmanaged interop transitions support APIs + // + void WaitForSuspend(); + void WaitForGC(void * pTransitionFrame); + + void ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame); + + bool InlineTryFastReversePInvoke(ReversePInvokeFrame * pFrame); + void InlineReversePInvokeReturn(ReversePInvokeFrame * pFrame); + + void InlinePInvoke(PInvokeTransitionFrame * pFrame); + void InlinePInvokeReturn(PInvokeTransitionFrame * pFrame); + + Object * GetThreadAbortException(); + void SetThreadAbortException(Object *exception); + + Object* GetThreadStaticStorageForModule(uint32_t moduleIndex); + bool SetThreadStaticStorageForModule(Object* pStorage, uint32_t moduleIndex); +}; + +#ifndef __GCENV_BASE_INCLUDED__ +typedef DPTR(Object) PTR_Object; +typedef DPTR(PTR_Object) PTR_PTR_Object; +#endif // !__GCENV_BASE_INCLUDED__ +#ifdef DACCESS_COMPILE + +// The DAC uses DebuggerEnumGcRefContext in place of a GCCONTEXT when doing reference +// enumeration. The GC passes through additional data in the ScanContext which the debugger +// neither has nor needs. While we could refactor the GC code to make an interface +// with less coupling, that might affect perf or make integration messier. Instead +// we use some typedefs so DAC and runtime can get strong yet distinct types. + + +// Ideally we wouldn't need this wrapper, but PromoteCarefully needs access to the +// thread and a promotion field. We aren't assuming the user's token will have this data. +struct DacScanCallbackData +{ + Thread* thread_under_crawl; // the thread being scanned + bool promotion; // are we emulating the GC promote phase or relocate phase? + // different references are reported for each + void* token; // the callback data passed to GCScanRoots + void* pfnUserCallback; // the callback passed in to GcScanRoots + uintptr_t stack_limit; // Lowest point on the thread stack that the scanning logic is permitted to read +}; + +typedef DacScanCallbackData EnumGcRefScanContext; +typedef void EnumGcRefCallbackFunc(PTR_PTR_Object, EnumGcRefScanContext* callbackData, uint32_t flags); + +#else // DACCESS_COMPILE +#ifndef __GCENV_BASE_INCLUDED__ +struct ScanContext; +typedef void promote_func(PTR_PTR_Object, ScanContext*, unsigned); +#endif // !__GCENV_BASE_INCLUDED__ +typedef promote_func EnumGcRefCallbackFunc; +typedef ScanContext EnumGcRefScanContext; + +#endif // DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/thread.inl b/src/coreclr/nativeaot/Runtime/thread.inl new file mode 100644 index 00000000000000..c8c13111c72dd8 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/thread.inl @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef DACCESS_COMPILE +inline void Thread::SetCurrentThreadPInvokeTunnelForGcAlloc(void * pTransitionFrame) +{ + ASSERT(ThreadStore::GetCurrentThread() == this); + ASSERT(Thread::IsCurrentThreadInCooperativeMode()); + m_pHackPInvokeTunnel = pTransitionFrame; +} + +inline void Thread::SetupHackPInvokeTunnel() +{ + ASSERT(ThreadStore::GetCurrentThread() == this); + ASSERT(!Thread::IsCurrentThreadInCooperativeMode()); + m_pHackPInvokeTunnel = m_pTransitionFrame; +} +#endif // DACCESS_COMPILE + +inline bool Thread::IsWithinStackBounds(PTR_VOID p) +{ + ASSERT((m_pStackLow != 0) && (m_pStackHigh != 0)); + return (m_pStackLow <= p) && (p < m_pStackHigh); +} + +inline void Thread::GetStackBounds(PTR_VOID * ppStackLow, PTR_VOID * ppStackHigh) +{ + ASSERT((m_pStackLow != 0) && (m_pStackHigh != 0)); + *ppStackLow = m_pStackLow; + *ppStackHigh = m_pStackHigh; +} + +#ifndef DACCESS_COMPILE +inline void Thread::SetThreadStressLog(void* ptsl) +{ + m_pThreadStressLog = ptsl; +} +#endif // DACCESS_COMPILE + +inline PTR_VOID Thread::GetThreadStressLog() const +{ + return m_pThreadStressLog; +} + +inline void Thread::EnterCantAllocRegion() +{ + m_cantAlloc++; +} + +inline void Thread::LeaveCantAllocRegion() +{ + m_cantAlloc--; +} + +inline bool Thread::IsInCantAllocStressLogRegion() +{ + return m_cantAlloc != 0; +} diff --git a/src/coreclr/nativeaot/Runtime/threadstore.cpp b/src/coreclr/nativeaot/Runtime/threadstore.cpp new file mode 100644 index 00000000000000..e0a7d528358ab5 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/threadstore.cpp @@ -0,0 +1,519 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" +#include "gcenv.h" +#include "gcheaputilities.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "gcrhinterface.h" +#include "varint.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "holder.h" +#include "rhbinder.h" +#include "RWLock.h" +#include "threadstore.h" +#include "threadstore.inl" +#include "RuntimeInstance.h" +#include "TargetPtrs.h" +#include "yieldprocessornormalized.h" + +#include "slist.inl" +#include "GCMemoryHelpers.h" + +EXTERN_C volatile uint32_t RhpTrapThreads = (uint32_t)TrapThreadsFlags::None; + +GVAL_IMPL_INIT(PTR_Thread, RhpSuspendingThread, 0); + +ThreadStore * GetThreadStore() +{ + return GetRuntimeInstance()->GetThreadStore(); +} + +ThreadStore::Iterator::Iterator() : + m_readHolder(&GetThreadStore()->m_Lock), + m_pCurrentPosition(GetThreadStore()->m_ThreadList.GetHead()) +{ +} + +ThreadStore::Iterator::~Iterator() +{ +} + +PTR_Thread ThreadStore::Iterator::GetNext() +{ + PTR_Thread pResult = m_pCurrentPosition; + if (NULL != pResult) + m_pCurrentPosition = pResult->m_pNext; + return pResult; +} + +//static +PTR_Thread ThreadStore::GetSuspendingThread() +{ + return (RhpSuspendingThread); +} + +#ifndef DACCESS_COMPILE + + +ThreadStore::ThreadStore() : + m_ThreadList(), + m_Lock(true /* writers (i.e. attaching/detaching threads) should wait on GC event */) +{ + SaveCurrentThreadOffsetForDAC(); +} + +ThreadStore::~ThreadStore() +{ +} + +// static +ThreadStore * ThreadStore::Create(RuntimeInstance * pRuntimeInstance) +{ + NewHolder pNewThreadStore = new (nothrow) ThreadStore(); + if (NULL == pNewThreadStore) + return NULL; + + if (!pNewThreadStore->m_SuspendCompleteEvent.CreateManualEventNoThrow(true)) + return NULL; + + pNewThreadStore->m_pRuntimeInstance = pRuntimeInstance; + + pNewThreadStore.SuppressRelease(); + return pNewThreadStore; +} + +void ThreadStore::Destroy() +{ + delete this; +} + +// static +void ThreadStore::AttachCurrentThread(bool fAcquireThreadStoreLock) +{ + // + // step 1: ThreadStore::InitCurrentThread + // step 2: add this thread to the ThreadStore + // + + // The thread has been constructed, during which some data is initialized (like which RuntimeInstance the + // thread belongs to), but it hasn't been added to the thread store because doing so takes a lock, which + // we want to avoid at construction time because the loader lock is held then. + Thread * pAttachingThread = RawGetCurrentThread(); + + // The thread was already initialized, so it is already attached + if (pAttachingThread->IsInitialized()) + { + return; + } + + PalAttachThread(pAttachingThread); + + // + // Init the thread buffer + // + pAttachingThread->Construct(); + ASSERT(pAttachingThread->m_ThreadStateFlags == Thread::TSF_Unknown); + + // The runtime holds the thread store lock for the duration of thread suspension for GC, so let's check to + // see if that's going on and, if so, use a proper wait instead of the RWL's spinning. NOTE: when we are + // called with fAcquireThreadStoreLock==false, we are being called in a situation where the GC is trying to + // init a GC thread, so we must honor the flag to mean "do not block on GC" or else we will deadlock. + if (fAcquireThreadStoreLock && (RhpTrapThreads != (uint32_t)TrapThreadsFlags::None)) + RedhawkGCInterface::WaitForGCCompletion(); + + ThreadStore* pTS = GetThreadStore(); + ReaderWriterLock::WriteHolder write(&pTS->m_Lock, fAcquireThreadStoreLock); + + // + // Set thread state to be attached + // + ASSERT(pAttachingThread->m_ThreadStateFlags == Thread::TSF_Unknown); + pAttachingThread->m_ThreadStateFlags = Thread::TSF_Attached; + + pTS->m_ThreadList.PushHead(pAttachingThread); +} + +// static +void ThreadStore::AttachCurrentThread() +{ + AttachCurrentThread(true); +} + +void ThreadStore::DetachCurrentThread() +{ + // The thread may not have been initialized because it may never have run managed code before. + Thread * pDetachingThread = RawGetCurrentThread(); + + // The thread was not initialized yet, so it was not attached + if (!pDetachingThread->IsInitialized()) + { + return; + } + + if (!PalDetachThread(pDetachingThread)) + { + return; + } + + { + ThreadStore* pTS = GetThreadStore(); + ReaderWriterLock::WriteHolder write(&pTS->m_Lock); + ASSERT(rh::std::count(pTS->m_ThreadList.Begin(), pTS->m_ThreadList.End(), pDetachingThread) == 1); + pTS->m_ThreadList.RemoveFirst(pDetachingThread); + pDetachingThread->Detach(); + } + + pDetachingThread->Destroy(); +} + +// Used by GC to prevent new threads during a GC. New threads must take a write lock to +// modify the list, but they won't be allowed to until all outstanding read locks are +// released. This way, the GC always enumerates a consistent set of threads each time +// it enumerates threads between SuspendAllThreads and ResumeAllThreads. +// +// @TODO: Investigate if this requirement is actually necessary. Threads already may +// not enter managed code during GC, so if new threads are added to the thread store, +// but haven't yet entered managed code, is that really a problem? +// +// @TODO: Investigate the suspend/resume algorithm's dependence on this lock's side- +// effect of being a memory barrier. +void ThreadStore::LockThreadStore() +{ + m_Lock.AcquireReadLock(); +} + +void ThreadStore::UnlockThreadStore() +{ + m_Lock.ReleaseReadLock(); +} + +void ThreadStore::SuspendAllThreads(bool waitForGCEvent) +{ + Thread * pThisThread = GetCurrentThreadIfAvailable(); + + LockThreadStore(); + + RhpSuspendingThread = pThisThread; + + if (waitForGCEvent) + { + GCHeapUtilities::GetGCHeap()->ResetWaitForGCEvent(); + } + m_SuspendCompleteEvent.Reset(); + + // set the global trap for pinvoke leave and return + RhpTrapThreads |= (uint32_t)TrapThreadsFlags::TrapThreads; + + // Set each module's loop hijack flag + GetRuntimeInstance()->SetLoopHijackFlags(RhpTrapThreads); + + // Our lock-free algorithm depends on flushing write buffers of all processors running RH code. The + // reason for this is that we essentially implement Dekker's algorithm, which requires write ordering. + PalFlushProcessWriteBuffers(); + + bool keepWaiting; + YieldProcessorNormalizationInfo normalizationInfo; + do + { + keepWaiting = false; + FOREACH_THREAD(pTargetThread) + { + if (pTargetThread == pThisThread) + continue; + + if (!pTargetThread->CacheTransitionFrameForSuspend()) + { + // We drive all threads to preemptive mode by hijacking them with both a + // return-address hijack and loop hijacks. + keepWaiting = true; + pTargetThread->Hijack(); + } + else if (pTargetThread->DangerousCrossThreadIsHijacked()) + { + // Once a thread is safely in preemptive mode, we must wait until it is also + // unhijacked. This is done because, otherwise, we might race on into the + // stackwalk and find the hijack still on the stack, which will cause the + // stackwalking code to crash. + keepWaiting = true; + } + } + END_FOREACH_THREAD + + if (keepWaiting) + { + if (PalSwitchToThread() == 0 && g_RhNumberOfProcessors > 1) + { + // No threads are scheduled on this processor. Perhaps we're waiting for a thread + // that's scheduled on another processor. If so, let's give it a little time + // to make forward progress. + // Note that we do not call Sleep, because the minimum granularity of Sleep is much + // too long (we probably don't need a 15ms wait here). Instead, we'll just burn some + // cycles. + // @TODO: need tuning for spin + YieldProcessorNormalizedForPreSkylakeCount(normalizationInfo, 10000); + } + } + + } while (keepWaiting); + + m_SuspendCompleteEvent.Set(); +} + +void ThreadStore::ResumeAllThreads(bool waitForGCEvent) +{ + FOREACH_THREAD(pTargetThread) + { + pTargetThread->ResetCachedTransitionFrame(); + } + END_FOREACH_THREAD + + RhpTrapThreads &= ~(uint32_t)TrapThreadsFlags::TrapThreads; + + // Reset module's hijackLoops flag + GetRuntimeInstance()->SetLoopHijackFlags(0); + + RhpSuspendingThread = NULL; + if (waitForGCEvent) + { + GCHeapUtilities::GetGCHeap()->SetWaitForGCEvent(); + } + UnlockThreadStore(); +} // ResumeAllThreads + +void ThreadStore::WaitForSuspendComplete() +{ + uint32_t waitResult = m_SuspendCompleteEvent.Wait(INFINITE, false); + if (waitResult == WAIT_FAILED) + RhFailFast(); +} + +#ifndef DACCESS_COMPILE + +void ThreadStore::InitiateThreadAbort(Thread* targetThread, Object * threadAbortException, bool doRudeAbort) +{ + SuspendAllThreads(/* waitForGCEvent = */ false); + // TODO: consider enabling multiple thread aborts running in parallel on different threads + ASSERT((RhpTrapThreads & (uint32_t)TrapThreadsFlags::AbortInProgress) == 0); + RhpTrapThreads |= (uint32_t)TrapThreadsFlags::AbortInProgress; + + targetThread->SetThreadAbortException(threadAbortException); + + // TODO: Stage 2: Queue APC to the target thread to break out of possible wait + + bool initiateAbort = false; + + if (!doRudeAbort) + { + // TODO: Stage 3: protected regions (finally, catch) handling + // If it was in a protected region, set the "throw at protected region end" flag on the native Thread object + // TODO: Stage 4: reverse PInvoke handling + // If there was a reverse Pinvoke frame between the current frame and the funceval frame of the target thread, + // find the outermost reverse Pinvoke frame below the funceval frame and set the thread abort flag in its transition frame. + // If both of these cases happened at once, find out which one of the outermost frame of the protected region + // and the outermost reverse Pinvoke frame is closer to the funceval frame and perform one of the two actions + // described above based on the one that's closer. + initiateAbort = true; + } + else + { + initiateAbort = true; + } + + if (initiateAbort) + { + PInvokeTransitionFrame* transitionFrame = reinterpret_cast(targetThread->GetTransitionFrame()); + transitionFrame->m_Flags |= PTFF_THREAD_ABORT; + } + + ResumeAllThreads(/* waitForGCEvent = */ false); +} + +void ThreadStore::CancelThreadAbort(Thread* targetThread) +{ + SuspendAllThreads(/* waitForGCEvent = */ false); + + ASSERT((RhpTrapThreads & (uint32_t)TrapThreadsFlags::AbortInProgress) != 0); + RhpTrapThreads &= ~(uint32_t)TrapThreadsFlags::AbortInProgress; + + PInvokeTransitionFrame* transitionFrame = reinterpret_cast(targetThread->GetTransitionFrame()); + if (transitionFrame != nullptr) + { + transitionFrame->m_Flags &= ~PTFF_THREAD_ABORT; + } + + targetThread->SetThreadAbortException(nullptr); + + ResumeAllThreads(/* waitForGCEvent = */ false); +} + +COOP_PINVOKE_HELPER(void *, RhpGetCurrentThread, ()) +{ + return ThreadStore::GetCurrentThread(); +} + +COOP_PINVOKE_HELPER(void, RhpInitiateThreadAbort, (void* thread, Object * threadAbortException, CLR_BOOL doRudeAbort)) +{ + GetThreadStore()->InitiateThreadAbort((Thread*)thread, threadAbortException, doRudeAbort); +} + +COOP_PINVOKE_HELPER(void, RhpCancelThreadAbort, (void* thread)) +{ + GetThreadStore()->CancelThreadAbort((Thread*)thread); +} + +#endif // DACCESS_COMPILE + +C_ASSERT(sizeof(Thread) == sizeof(ThreadBuffer)); + +EXTERN_C DECLSPEC_THREAD ThreadBuffer tls_CurrentThread = +{ + { 0 }, // m_rgbAllocContextBuffer + Thread::TSF_Unknown, // m_ThreadStateFlags + TOP_OF_STACK_MARKER, // m_pTransitionFrame + TOP_OF_STACK_MARKER, // m_pHackPInvokeTunnel + 0, // m_pCachedTransitionFrame + 0, // m_pNext + INVALID_HANDLE_VALUE, // m_hPalThread + 0, // m_ppvHijackedReturnAddressLocation + 0, // m_pvHijackedReturnAddress + 0, // all other fields are initialized by zeroes +}; + +EXTERN_C ThreadBuffer* RhpGetThread() +{ + return &tls_CurrentThread; +} + +#endif // !DACCESS_COMPILE + +#ifdef _WIN32 + +#ifndef DACCESS_COMPILE + +// Keep a global variable in the target process which contains +// the address of _tls_index. This is the breadcrumb needed +// by DAC to read _tls_index since we don't control the +// declaration of _tls_index directly. + +// volatile to prevent the compiler from removing the unused global variable +volatile uint32_t * p_tls_index; +volatile uint32_t SECTIONREL__tls_CurrentThread; + +EXTERN_C uint32_t _tls_index; +#if defined(TARGET_ARM64) +// ARM64TODO: Re-enable optimization +#pragma optimize("", off) +#endif +void ThreadStore::SaveCurrentThreadOffsetForDAC() +{ + p_tls_index = &_tls_index; + + uint8_t * pTls = *(uint8_t **)(PalNtCurrentTeb() + OFFSETOF__TEB__ThreadLocalStoragePointer); + + uint8_t * pOurTls = *(uint8_t **)(pTls + (_tls_index * sizeof(void*))); + + SECTIONREL__tls_CurrentThread = (uint32_t)((uint8_t *)&tls_CurrentThread - pOurTls); +} +#if defined(TARGET_ARM64) +#pragma optimize("", on) +#endif +#else // DACCESS_COMPILE + +GPTR_IMPL(uint32_t, p_tls_index); +GVAL_IMPL(uint32_t, SECTIONREL__tls_CurrentThread); + +// +// This routine supports the !Thread debugger extension routine +// +typedef DPTR(TADDR) PTR_TADDR; +// static +PTR_Thread ThreadStore::GetThreadFromTEB(TADDR pTEB) +{ + if (pTEB == NULL) + return NULL; + + uint32_t tlsIndex = *p_tls_index; + TADDR pTls = *(PTR_TADDR)(pTEB + OFFSETOF__TEB__ThreadLocalStoragePointer); + if (pTls == NULL) + return NULL; + + TADDR pOurTls = *(PTR_TADDR)(pTls + (tlsIndex * sizeof(void*))); + if (pOurTls == NULL) + return NULL; + + return (PTR_Thread)(pOurTls + SECTIONREL__tls_CurrentThread); +} + +#endif // DACCESS_COMPILE + +#else // _WIN32 + +void ThreadStore::SaveCurrentThreadOffsetForDAC() +{ +} + +#endif // _WIN32 + + +#ifndef DACCESS_COMPILE + +// internal static extern unsafe bool RhGetExceptionsForCurrentThread(Exception[] outputArray, out int writtenCountOut); +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhGetExceptionsForCurrentThread, (Array* pOutputArray, int32_t* pWrittenCountOut)) +{ + FC_RETURN_BOOL(GetThreadStore()->GetExceptionsForCurrentThread(pOutputArray, pWrittenCountOut)); +} + +bool ThreadStore::GetExceptionsForCurrentThread(Array* pOutputArray, int32_t* pWrittenCountOut) +{ + int32_t countWritten = 0; + Object** pArrayElements; + Thread * pThread = GetCurrentThread(); + + for (PTR_ExInfo pInfo = pThread->m_pExInfoStackHead; pInfo != NULL; pInfo = pInfo->m_pPrevExInfo) + { + if (pInfo->m_exception == NULL) + continue; + + countWritten++; + } + + // No input array provided, or it was of the wrong kind. We'll fill out the count and return false. + if ((pOutputArray == NULL) || (pOutputArray->get_EEType()->get_ComponentSize() != POINTER_SIZE)) + goto Error; + + // Input array was not big enough. We don't even partially fill it. + if (pOutputArray->GetArrayLength() < (uint32_t)countWritten) + goto Error; + + *pWrittenCountOut = countWritten; + + // Success, but nothing to report. + if (countWritten == 0) + return true; + + pArrayElements = (Object**)pOutputArray->GetArrayData(); + for (PTR_ExInfo pInfo = pThread->m_pExInfoStackHead; pInfo != NULL; pInfo = pInfo->m_pPrevExInfo) + { + if (pInfo->m_exception == NULL) + continue; + + *pArrayElements = pInfo->m_exception; + pArrayElements++; + } + + RhpBulkWriteBarrier(pArrayElements, countWritten * POINTER_SIZE); + return true; + +Error: + *pWrittenCountOut = countWritten; + return false; +} +#endif // DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/threadstore.h b/src/coreclr/nativeaot/Runtime/threadstore.h new file mode 100644 index 00000000000000..b67b4c4a1ef003 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/threadstore.h @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +class Thread; +class CLREventStatic; +class RuntimeInstance; +class Array; +typedef DPTR(RuntimeInstance) PTR_RuntimeInstance; + +enum class TrapThreadsFlags +{ + None = 0, + AbortInProgress = 1, + TrapThreads = 2 +}; + +extern "C" void PopulateDebugHeaders(); + +class ThreadStore +{ + friend void PopulateDebugHeaders(); + + SList m_ThreadList; + PTR_RuntimeInstance m_pRuntimeInstance; + CLREventStatic m_SuspendCompleteEvent; + ReaderWriterLock m_Lock; + +private: + ThreadStore(); + + void LockThreadStore(); + void UnlockThreadStore(); + +public: + class Iterator + { + ReaderWriterLock::ReadHolder m_readHolder; + PTR_Thread m_pCurrentPosition; + public: + Iterator(); + ~Iterator(); + PTR_Thread GetNext(); + }; + + ~ThreadStore(); + static ThreadStore * Create(RuntimeInstance * pRuntimeInstance); + static Thread * RawGetCurrentThread(); + static Thread * GetCurrentThread(); + static Thread * GetCurrentThreadIfAvailable(); + static PTR_Thread GetSuspendingThread(); + static void AttachCurrentThread(); + static void AttachCurrentThread(bool fAcquireThreadStoreLock); + static void DetachCurrentThread(); +#ifndef DACCESS_COMPILE + static void SaveCurrentThreadOffsetForDAC(); + void InitiateThreadAbort(Thread* targetThread, Object * threadAbortException, bool doRudeAbort); + void CancelThreadAbort(Thread* targetThread); +#else + static PTR_Thread GetThreadFromTEB(TADDR pvTEB); +#endif + bool GetExceptionsForCurrentThread(Array* pOutputArray, int32_t* pWrittenCountOut); + + void Destroy(); + void SuspendAllThreads(bool waitForGCEvent); + void ResumeAllThreads(bool waitForGCEvent); + + static bool IsTrapThreadsRequested(); + void WaitForSuspendComplete(); +}; +typedef DPTR(ThreadStore) PTR_ThreadStore; + +ThreadStore * GetThreadStore(); + +#define FOREACH_THREAD(p_thread_name) \ +{ \ + ThreadStore::Iterator __threads; \ + Thread * p_thread_name; \ + while ((p_thread_name = __threads.GetNext()) != NULL) \ + { \ + +#define END_FOREACH_THREAD \ + } \ +} \ + diff --git a/src/coreclr/nativeaot/Runtime/threadstore.inl b/src/coreclr/nativeaot/Runtime/threadstore.inl new file mode 100644 index 00000000000000..aa450b72fb448b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/threadstore.inl @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +EXTERN_C DECLSPEC_THREAD ThreadBuffer tls_CurrentThread; + +// static +inline Thread * ThreadStore::RawGetCurrentThread() +{ + return (Thread *) &tls_CurrentThread; +} + +// static +inline Thread * ThreadStore::GetCurrentThread() +{ + Thread * pCurThread = RawGetCurrentThread(); + + // If this assert fires, and you only need the Thread pointer if the thread has ever previously + // entered the runtime, then you should be using GetCurrentThreadIfAvailable instead. + ASSERT(pCurThread->IsInitialized()); + return pCurThread; +} + +// static +inline Thread * ThreadStore::GetCurrentThreadIfAvailable() +{ + Thread * pCurThread = RawGetCurrentThread(); + if (pCurThread->IsInitialized()) + return pCurThread; + + return NULL; +} + +EXTERN_C volatile uint32_t RhpTrapThreads; + +// static +inline bool ThreadStore::IsTrapThreadsRequested() +{ + return (RhpTrapThreads & (uint32_t)TrapThreadsFlags::TrapThreads) != 0; +} diff --git a/src/coreclr/nativeaot/Runtime/unix/AsmOffsets.cpp b/src/coreclr/nativeaot/Runtime/unix/AsmOffsets.cpp new file mode 100644 index 00000000000000..1dd26302cde49d --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/AsmOffsets.cpp @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#define HASH_DEFINE #define +#define PLAT_ASM_OFFSET(offset, cls, member) HASH_DEFINE OFFSETOF__##cls##__##member 0x##offset +#define PLAT_ASM_SIZEOF(size, cls ) HASH_DEFINE SIZEOF__##cls 0x##size +#define PLAT_ASM_CONST(constant, expr) HASH_DEFINE expr 0x##constant + +#include diff --git a/src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.cpp b/src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.cpp new file mode 100644 index 00000000000000..b904c9e9ed4df4 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.cpp @@ -0,0 +1,648 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "CommonTypes.h" +#include "PalRedhawkCommon.h" +#include "CommonMacros.h" +#include "config.h" +#include "daccess.h" +#include "regdisplay.h" +#include "UnixContext.h" + +#include +#include "HardwareExceptions.h" + +#if !HAVE_SIGINFO_T +#error Cannot handle hardware exceptions on this platform +#endif + +#define REDHAWK_PALEXPORT extern "C" +#define REDHAWK_PALAPI + +#define EXCEPTION_ACCESS_VIOLATION 0xC0000005u +#define EXCEPTION_DATATYPE_MISALIGNMENT 0x80000002u +#define EXCEPTION_BREAKPOINT 0x80000003u +#define EXCEPTION_SINGLE_STEP 0x80000004u +#define EXCEPTION_ARRAY_BOUNDS_EXCEEDED 0xC000008Cu +#define EXCEPTION_FLT_DENORMAL_OPERAND 0xC000008Du +#define EXCEPTION_FLT_DIVIDE_BY_ZERO 0xC000008Eu +#define EXCEPTION_FLT_INEXACT_RESULT 0xC000008Fu +#define EXCEPTION_FLT_INVALID_OPERATION 0xC0000090u +#define EXCEPTION_FLT_OVERFLOW 0xC0000091u +#define EXCEPTION_FLT_STACK_CHECK 0xC0000092u +#define EXCEPTION_FLT_UNDERFLOW 0xC0000093u +#define EXCEPTION_INT_DIVIDE_BY_ZERO 0xC0000094u +#define EXCEPTION_INT_OVERFLOW 0xC0000095u +#define EXCEPTION_PRIV_INSTRUCTION 0xC0000096u +#define EXCEPTION_IN_PAGE_ERROR 0xC0000006u +#define EXCEPTION_ILLEGAL_INSTRUCTION 0xC000001Du +#define EXCEPTION_NONCONTINUABLE_EXCEPTION 0xC0000025u +#define EXCEPTION_STACK_OVERFLOW 0xC00000FDu +#define EXCEPTION_INVALID_DISPOSITION 0xC0000026u +#define EXCEPTION_GUARD_PAGE 0x80000001u +#define EXCEPTION_INVALID_HANDLE 0xC0000008u + +#define EXCEPTION_CONTINUE_EXECUTION (-1) +#define EXCEPTION_CONTINUE_SEARCH (0) +#define EXCEPTION_EXECUTE_HANDLER (1) + +struct sigaction g_previousSIGSEGV; +struct sigaction g_previousSIGFPE; + +typedef void (*SignalHandler)(int code, siginfo_t *siginfo, void *context); + +// Exception handler for hardware exceptions +static PHARDWARE_EXCEPTION_HANDLER g_hardwareExceptionHandler = NULL; + +#ifdef HOST_AMD64 + +// Get value of an instruction operand represented by the ModR/M field +// Parameters: +// uint8_t rex : REX prefix, 0 if there was none +// uint8_t* ip : instruction pointer pointing to the ModR/M field +// void* context : context containing the registers +// bool is8Bit : true if the operand size is 8 bit +// bool hasOpSizePrefix : true if the instruction has op size prefix (0x66) +uint64_t GetModRMOperandValue(uint8_t rex, uint8_t* ip, void* context, bool is8Bit, bool hasOpSizePrefix) +{ + uint64_t result; + uint64_t resultReg; + + uint8_t rex_b = (rex & 0x1); // high bit to modrm r/m field or SIB base field + uint8_t rex_x = (rex & 0x2) >> 1; // high bit to sib index field + uint8_t rex_r = (rex & 0x4) >> 2; // high bit to modrm reg field + uint8_t rex_w = (rex & 0x8) >> 3; // 1 = 64 bit operand size, 0 = operand size determined by hasOpSizePrefix + + uint8_t modrm = *ip++; + + ASSERT(modrm != 0); + + uint8_t mod = (modrm & 0xC0) >> 6; + uint8_t reg = (modrm & 0x38) >> 3; + uint8_t rm = (modrm & 0x07); + + reg |= (rex_r << 3); + uint8_t rmIndex = rm | (rex_b << 3); + + // 8 bit idiv without the REX prefix uses registers AH, CH, DH, BH for rm 4..8 + // which is an exception from the regular register indexes. + bool isAhChDhBh = is8Bit && (rex == 0) && (rm >= 4); + + // See: Tables A-15,16,17 in AMD Dev Manual 3 for information + // about how the ModRM/SIB/REX uint8_ts interact. + + switch (mod) + { + case 0: + case 1: + case 2: + if (rm == 4) // we have an SIB uint8_t following + { + // + // Get values from the SIB uint8_t + // + uint8_t sib = *ip++; + + ASSERT(sib != 0); + + uint8_t ss = (sib & 0xC0) >> 6; + uint8_t index = (sib & 0x38) >> 3; + uint8_t base = (sib & 0x07); + + index |= (rex_x << 3); + base |= (rex_b << 3); + + // + // Get starting value + // + if ((mod == 0) && (base == 5)) + { + result = 0; + } + else + { + result = GetRegisterValueByIndex(context, base); + } + + // + // Add in the [index] + // + if (index != 4) + { + result += GetRegisterValueByIndex(context, index) << ss; + } + + // + // Finally add in the offset + // + if (mod == 0) + { + if (base == 5) + { + result += *((int32_t*)ip); + } + } + else if (mod == 1) + { + result += *((int8_t*)ip); + } + else // mod == 2 + { + result += *((int32_t*)ip); + } + + } + else + { + // + // Get the value we need from the register. + // + + // Check for RIP-relative addressing mode. + if ((mod == 0) && (rm == 5)) + { + result = (uint64_t)ip + sizeof(int32_t) + *(int32_t*)ip; + } + else + { + result = GetRegisterValueByIndex(context, rmIndex); + + if (mod == 1) + { + result += *((int8_t*)ip); + } + else if (mod == 2) + { + result += *((int32_t*)ip); + } + } + } + + break; + + case 3: + default: + // The operand is stored in a register. + if (isAhChDhBh) + { + // 8 bit idiv without the REX prefix uses registers AH, CH, DH or BH for rm 4..8. + // So we shift the register index to get the real register index. + rmIndex -= 4; + } + + resultReg = GetRegisterValueByIndex(context, rmIndex); + result = (uint64_t)&resultReg; + + if (isAhChDhBh) + { + // Move one uint8_t higher to get an address of the AH, CH, DH or BH + result++; + } + + break; + + } + + // Now dereference thru the result to get the resulting value. + if (is8Bit) + { + result = *((uint8_t*)result); + } + else if (rex_w != 0) + { + result = *((uint64_t*)result); + } + else if (hasOpSizePrefix) + { + result = *((uint16_t*)result); + } + else + { + result = *((uint32_t*)result); + } + + return result; +} + +// Skip all prefixes until the instruction code or the REX prefix is found +// Parameters: +// uint8_t** ip : Pointer to the current instruction pointer. Updated +// as the function walks the codes. +// bool* hasOpSizePrefix : Pointer to bool, on exit set to true if a op size prefix +// was found. +// Return value : +// Code of the REX prefix or the instruction code after the prefixes. +uint8_t SkipPrefixes(uint8_t **ip, bool* hasOpSizePrefix) +{ + *hasOpSizePrefix = false; + + while (true) + { + uint8_t code = *(*ip)++; + + switch (code) + { + case 0x66: // Operand-Size + *hasOpSizePrefix = true; + break; + + // Segment overrides + case 0x26: // ES + case 0x2E: // CS + case 0x36: // SS + case 0x3E: // DS + case 0x64: // FS + case 0x65: // GS + + // Size overrides + case 0x67: // Address-Size + + // Lock + case 0xf0: + + // String REP prefixes + case 0xf2: // REPNE/REPNZ + case 0xf3: + break; + + default: + // Return address of the nonprefix code + return code; + } + } +} + +// Check if a division by zero exception is in fact a division overflow. The +// x64 processor generate the same exception in both cases for the IDIV / DIV +// instruction. So we need to decode the instruction argument and check +// whether it was zero or not. +bool IsDivByZeroAnIntegerOverflow(void* context) +{ + uint8_t * ip = (uint8_t*)GetPC(context); + uint8_t rex = 0; + bool hasOpSizePrefix = false; + + uint8_t code = SkipPrefixes(&ip, &hasOpSizePrefix); + + // The REX prefix must directly preceed the instruction code + if ((code & 0xF0) == 0x40) + { + rex = code; + code = *ip++; + } + + uint64_t divisor = 0; + + // Check if the instruction is IDIV or DIV. The instruction code includes the three + // 'reg' bits in the ModRM uint8_t. These are 7 for IDIV and 6 for DIV + uint8_t regBits = (*ip & 0x38) >> 3; + if ((code == 0xF7 || code == 0xF6) && (regBits == 7 || regBits == 6)) + { + bool is8Bit = (code == 0xF6); + divisor = GetModRMOperandValue(rex, ip, context, is8Bit, hasOpSizePrefix); + } + else + { + ASSERT_UNCONDITIONALLY("Invalid instruction (expected IDIV or DIV)"); + } + + // If the division operand is zero, it was division by zero. Otherwise the failure + // must have been an overflow. + return divisor != 0; +} +#endif //HOST_AMD64 + +// Translates signal and context information to a Win32 exception code. +uint32_t GetExceptionCodeForSignal(const siginfo_t *siginfo, const void *context) +{ + // IMPORTANT NOTE: This function must not call any signal unsafe functions + // since it is called from signal handlers. +#ifdef ILL_ILLOPC + switch (siginfo->si_signo) + { + case SIGILL: + switch (siginfo->si_code) + { + case ILL_ILLOPC: // Illegal opcode + case ILL_ILLOPN: // Illegal operand + case ILL_ILLADR: // Illegal addressing mode + case ILL_ILLTRP: // Illegal trap + case ILL_COPROC: // Co-processor error + return EXCEPTION_ILLEGAL_INSTRUCTION; + case ILL_PRVOPC: // Privileged opcode + case ILL_PRVREG: // Privileged register + return EXCEPTION_PRIV_INSTRUCTION; + case ILL_BADSTK: // Internal stack error + return EXCEPTION_STACK_OVERFLOW; + default: + break; + } + break; + case SIGFPE: + switch (siginfo->si_code) + { + case FPE_INTDIV: + return EXCEPTION_INT_DIVIDE_BY_ZERO; + case FPE_INTOVF: + return EXCEPTION_INT_OVERFLOW; + case FPE_FLTDIV: + return EXCEPTION_FLT_DIVIDE_BY_ZERO; + case FPE_FLTOVF: + return EXCEPTION_FLT_OVERFLOW; + case FPE_FLTUND: + return EXCEPTION_FLT_UNDERFLOW; + case FPE_FLTRES: + return EXCEPTION_FLT_INEXACT_RESULT; + case FPE_FLTINV: + return EXCEPTION_FLT_INVALID_OPERATION; + case FPE_FLTSUB: + return EXCEPTION_FLT_INVALID_OPERATION; + default: + break; + } + break; + case SIGSEGV: + switch (siginfo->si_code) + { + case SI_USER: // User-generated signal, sometimes sent + // for SIGSEGV under normal circumstances + case SEGV_MAPERR: // Address not mapped to object + case SEGV_ACCERR: // Invalid permissions for mapped object + return EXCEPTION_ACCESS_VIOLATION; + +#ifdef SI_KERNEL + case SI_KERNEL: + { + return EXCEPTION_ACCESS_VIOLATION; + } +#endif + default: + break; + } + break; + case SIGBUS: + switch (siginfo->si_code) + { + case BUS_ADRALN: // Invalid address alignment + return EXCEPTION_DATATYPE_MISALIGNMENT; + case BUS_ADRERR: // Non-existent physical address + return EXCEPTION_ACCESS_VIOLATION; + case BUS_OBJERR: // Object-specific hardware error + default: + break; + } + break; + case SIGTRAP: + switch (siginfo->si_code) + { +#ifdef SI_KERNEL + case SI_KERNEL: +#endif + case SI_USER: + case TRAP_BRKPT: // Process breakpoint + return EXCEPTION_BREAKPOINT; + case TRAP_TRACE: // Process trace trap + return EXCEPTION_SINGLE_STEP; + default: + // Got unknown SIGTRAP signal with code siginfo->si_code; + return EXCEPTION_ILLEGAL_INSTRUCTION; + } + default: + break; + } + + // Got unknown signal number siginfo->si_signo with code siginfo->si_code; + return EXCEPTION_ILLEGAL_INSTRUCTION; +#else // ILL_ILLOPC + int trap; + + if (siginfo->si_signo == SIGFPE) + { + // Floating point exceptions are mapped by their si_code. + switch (siginfo->si_code) + { + case FPE_INTDIV : + return EXCEPTION_INT_DIVIDE_BY_ZERO; + case FPE_INTOVF : + return EXCEPTION_INT_OVERFLOW; + case FPE_FLTDIV : + return EXCEPTION_FLT_DIVIDE_BY_ZERO; + case FPE_FLTOVF : + return EXCEPTION_FLT_OVERFLOW; + case FPE_FLTUND : + return EXCEPTION_FLT_UNDERFLOW; + case FPE_FLTRES : + return EXCEPTION_FLT_INEXACT_RESULT; + case FPE_FLTINV : + return EXCEPTION_FLT_INVALID_OPERATION; + case FPE_FLTSUB :/* subscript out of range */ + return EXCEPTION_FLT_INVALID_OPERATION; + default: + // Got unknown signal code siginfo->si_code; + return 0; + } + } + + trap = ((ucontext_t*)context)->uc_mcontext.mc_trapno; + switch (trap) + { + case T_PRIVINFLT : /* privileged instruction */ + return EXCEPTION_PRIV_INSTRUCTION; + case T_BPTFLT : /* breakpoint instruction */ + return EXCEPTION_BREAKPOINT; + case T_ARITHTRAP : /* arithmetic trap */ + return 0; /* let the caller pick an exception code */ +#ifdef T_ASTFLT + case T_ASTFLT : /* system forced exception : ^C, ^\. SIGINT signal + handler shouldn't be calling this function, since + it doesn't need an exception code */ + // Trap code T_ASTFLT received, shouldn't get here; + return 0; +#endif // T_ASTFLT + case T_PROTFLT : /* protection fault */ + return EXCEPTION_ACCESS_VIOLATION; + case T_TRCTRAP : /* debug exception (sic) */ + return EXCEPTION_SINGLE_STEP; + case T_PAGEFLT : /* page fault */ + return EXCEPTION_ACCESS_VIOLATION; + case T_ALIGNFLT : /* alignment fault */ + return EXCEPTION_DATATYPE_MISALIGNMENT; + case T_DIVIDE : + return EXCEPTION_INT_DIVIDE_BY_ZERO; + case T_NMI : /* non-maskable trap */ + return EXCEPTION_ILLEGAL_INSTRUCTION; + case T_OFLOW : + return EXCEPTION_INT_OVERFLOW; + case T_BOUND : /* bound instruction fault */ + return EXCEPTION_ARRAY_BOUNDS_EXCEEDED; + case T_DNA : /* device not available fault */ + return EXCEPTION_ILLEGAL_INSTRUCTION; + case T_DOUBLEFLT : /* double fault */ + return EXCEPTION_ILLEGAL_INSTRUCTION; + case T_FPOPFLT : /* fp coprocessor operand fetch fault */ + return EXCEPTION_FLT_INVALID_OPERATION; + case T_TSSFLT : /* invalid tss fault */ + return EXCEPTION_ILLEGAL_INSTRUCTION; + case T_SEGNPFLT : /* segment not present fault */ + return EXCEPTION_ACCESS_VIOLATION; + case T_STKFLT : /* stack fault */ + return EXCEPTION_STACK_OVERFLOW; + case T_MCHK : /* machine check trap */ + return EXCEPTION_ILLEGAL_INSTRUCTION; + case T_RESERVED : /* reserved (unknown) */ + return EXCEPTION_ILLEGAL_INSTRUCTION; + default: + // Got unknown trap code trap; + break; + } + return EXCEPTION_ILLEGAL_INSTRUCTION; +#endif // ILL_ILLOPC +} + +// Common handler for hardware exception signals +bool HardwareExceptionHandler(int code, siginfo_t *siginfo, void *context, void* faultAddress) +{ + if (g_hardwareExceptionHandler != NULL) + { + uintptr_t faultCode = GetExceptionCodeForSignal(siginfo, context); + +#ifdef HOST_AMD64 + // It is possible that an overflow was mapped to a divide-by-zero exception. + // This happens when we try to divide the maximum negative value of a + // signed integer with -1. + // + // Thus, we will attempt to decode the instruction @ RIP to determine if that + // is the case using the faulting context. + if ((faultCode == EXCEPTION_INT_DIVIDE_BY_ZERO) && IsDivByZeroAnIntegerOverflow(context)) + { + // The exception was an integer overflow, so augment the fault code. + faultCode = EXCEPTION_INT_OVERFLOW; + } +#endif //HOST_AMD64 + + PAL_LIMITED_CONTEXT palContext; + NativeContextToPalContext(context, &palContext); + + uintptr_t arg0Reg; + uintptr_t arg1Reg; + int32_t disposition = g_hardwareExceptionHandler(faultCode, (uintptr_t)faultAddress, &palContext, &arg0Reg, &arg1Reg); + if (disposition == EXCEPTION_CONTINUE_EXECUTION) + { + // TODO: better name + RedirectNativeContext(context, &palContext, arg0Reg, arg1Reg); + return true; + } + } + + return false; +} + +// Add handler for hardware exception signal +bool AddSignalHandler(int signal, SignalHandler handler, struct sigaction* previousAction) +{ + struct sigaction newAction; + + newAction.sa_flags = SA_RESTART; + newAction.sa_handler = NULL; + newAction.sa_sigaction = handler; + newAction.sa_flags |= SA_SIGINFO; + + sigemptyset(&newAction.sa_mask); + + if (sigaction(signal, NULL, previousAction) == -1) + { + ASSERT_UNCONDITIONALLY("Failed to get previous signal handler"); + return false; + } + + if (previousAction->sa_flags & SA_ONSTACK) + { + // If the previous signal handler uses an alternate stack, we need to use it too + // so that when we chain-call the previous handler, it is called on the kind of + // stack it expects. + // We also copy the signal mask to make sure that if some signals were blocked + // from execution on the alternate stack by the previous action, we honor that. + newAction.sa_flags |= SA_ONSTACK; + newAction.sa_mask = previousAction->sa_mask; + } + + if (sigaction(signal, &newAction, previousAction) == -1) + { + ASSERT_UNCONDITIONALLY("Failed to install signal handler"); + return false; + } + + return true; +} + +// Restore original handler for hardware exception signal +void RestoreSignalHandler(int signal_id, struct sigaction *previousAction) +{ + if (-1 == sigaction(signal_id, previousAction, NULL)) + { + ASSERT_UNCONDITIONALLY("RestoreSignalHandler: sigaction() call failed"); + } +} + +// Handler for the SIGSEGV signal +void SIGSEGVHandler(int code, siginfo_t *siginfo, void *context) +{ + bool isHandled = HardwareExceptionHandler(code, siginfo, context, siginfo->si_addr); + if (isHandled) + { + return; + } + + if (g_previousSIGSEGV.sa_sigaction != NULL) + { + g_previousSIGSEGV.sa_sigaction(code, siginfo, context); + } + else + { + // Restore the original or default handler and restart h/w exception + RestoreSignalHandler(code, &g_previousSIGSEGV); + } +} + +// Handler for the SIGFPE signal +void SIGFPEHandler(int code, siginfo_t *siginfo, void *context) +{ + bool isHandled = HardwareExceptionHandler(code, siginfo, context, NULL); + if (isHandled) + { + return; + } + + if (g_previousSIGFPE.sa_sigaction != NULL) + { + g_previousSIGFPE.sa_sigaction(code, siginfo, context); + } + else + { + // Restore the original or default handler and restart h/w exception + RestoreSignalHandler(code, &g_previousSIGFPE); + } +} + +// Initialize hardware exception handling +bool InitializeHardwareExceptionHandling() +{ + if (!AddSignalHandler(SIGSEGV, SIGSEGVHandler, &g_previousSIGSEGV)) + { + return false; + } + + if (!AddSignalHandler(SIGFPE, SIGFPEHandler, &g_previousSIGFPE)) + { + return false; + } + + return true; +} + +// Set CoreRT hardware exception handler +REDHAWK_PALEXPORT void REDHAWK_PALAPI PalSetHardwareExceptionHandler(PHARDWARE_EXCEPTION_HANDLER handler) +{ + ASSERT_MSG(g_hardwareExceptionHandler == NULL, "Hardware exception handler already set") + g_hardwareExceptionHandler = handler; +} diff --git a/src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.h b/src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.h new file mode 100644 index 00000000000000..8a4a18af1a64df --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.h @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __HARDWARE_EXCEPTIONS_H__ +#define __HARDWARE_EXCEPTIONS_H__ + +// Initialize hardware exception handling +bool InitializeHardwareExceptionHandling(); + +#endif // __HARDWARE_EXCEPTIONS_H__ diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkInline.h b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkInline.h new file mode 100644 index 00000000000000..39641088e5487f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkInline.h @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Implementation of Redhawk PAL inline functions + +#include + +FORCEINLINE int32_t PalInterlockedIncrement(_Inout_ int32_t volatile *pDst) +{ + return __sync_add_and_fetch(pDst, 1); +} + +FORCEINLINE int32_t PalInterlockedDecrement(_Inout_ int32_t volatile *pDst) +{ + return __sync_sub_and_fetch(pDst, 1); +} + +FORCEINLINE uint32_t PalInterlockedOr(_Inout_ uint32_t volatile *pDst, uint32_t iValue) +{ + return __sync_or_and_fetch(pDst, iValue); +} + +FORCEINLINE uint32_t PalInterlockedAnd(_Inout_ uint32_t volatile *pDst, uint32_t iValue) +{ + return __sync_and_and_fetch(pDst, iValue); +} + +FORCEINLINE int32_t PalInterlockedExchange(_Inout_ int32_t volatile *pDst, int32_t iValue) +{ + return __sync_swap(pDst, iValue); +} + +FORCEINLINE int64_t PalInterlockedExchange64(_Inout_ int64_t volatile *pDst, int64_t iValue) +{ + return __sync_swap(pDst, iValue); +} + +FORCEINLINE int32_t PalInterlockedCompareExchange(_Inout_ int32_t volatile *pDst, int32_t iValue, int32_t iComparand) +{ + return __sync_val_compare_and_swap(pDst, iComparand, iValue); +} + +FORCEINLINE int64_t PalInterlockedCompareExchange64(_Inout_ int64_t volatile *pDst, int64_t iValue, int64_t iComparand) +{ + return __sync_val_compare_and_swap(pDst, iComparand, iValue); +} + +#if defined(HOST_AMD64) || defined(HOST_ARM64) +FORCEINLINE uint8_t PalInterlockedCompareExchange128(_Inout_ int64_t volatile *pDst, int64_t iValueHigh, int64_t iValueLow, int64_t *pComparandAndResult) +{ + __int128_t iComparand = ((__int128_t)pComparandAndResult[1] << 64) + (uint64_t)pComparandAndResult[0]; + __int128_t iResult = __sync_val_compare_and_swap((__int128_t volatile*)pDst, iComparand, ((__int128_t)iValueHigh << 64) + (uint64_t)iValueLow); + pComparandAndResult[0] = (int64_t)iResult; pComparandAndResult[1] = (int64_t)(iResult >> 64); + return iComparand == iResult; +} +#endif // HOST_AMD64 + +#ifdef HOST_64BIT + +#define PalInterlockedExchangePointer(_pDst, _pValue) \ + ((void *)PalInterlockedExchange64((int64_t volatile *)(_pDst), (int64_t)(size_t)(_pValue))) + +#define PalInterlockedCompareExchangePointer(_pDst, _pValue, _pComparand) \ + ((void *)PalInterlockedCompareExchange64((int64_t volatile *)(_pDst), (int64_t)(size_t)(_pValue), (int64_t)(size_t)(_pComparand))) + +#else // HOST_64BIT + +#define PalInterlockedExchangePointer(_pDst, _pValue) \ + ((void *)PalInterlockedExchange((int32_t volatile *)(_pDst), (int32_t)(size_t)(_pValue))) + +#define PalInterlockedCompareExchangePointer(_pDst, _pValue, _pComparand) \ + ((void *)PalInterlockedCompareExchange((int32_t volatile *)(_pDst), (int32_t)(size_t)(_pValue), (int32_t)(size_t)(_pComparand))) + +#endif // HOST_64BIT + + +FORCEINLINE void PalYieldProcessor() +{ +#if defined(HOST_X86) || defined(HOST_AMD64) + __asm__ __volatile__( + "rep\n" + "nop" + ); +#endif +} + +FORCEINLINE void PalMemoryBarrier() +{ + __sync_synchronize(); +} + +#define PalDebugBreak() abort() + +FORCEINLINE int32_t PalGetLastError() +{ + return errno; +} + +FORCEINLINE void PalSetLastError(int32_t error) +{ + errno = error; +} diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp new file mode 100644 index 00000000000000..70f6c71f4f1548 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp @@ -0,0 +1,1336 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Implementation of the Redhawk Platform Abstraction Layer (PAL) library when Unix is the platform. +// + +#include +#include +#include +#include +#include "config.h" +#include "UnixHandle.h" +#include +#include "gcenv.h" +#include "holder.h" +#include "HardwareExceptions.h" +#include "cgroupcpu.h" + +#define _T(s) s +#include "RhConfig.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_PTHREAD_GETTHREADID_NP +#include +#endif + +#if HAVE_LWP_SELF +#include +#endif + +#if HAVE_SYS_VMPARAM_H +#include +#endif // HAVE_SYS_VMPARAM_H + +#if HAVE_MACH_VM_TYPES_H +#include +#endif // HAVE_MACH_VM_TYPES_H + +#if HAVE_MACH_VM_PARAM_H +#include +#endif // HAVE_MACH_VM_PARAM_H + +#ifdef __APPLE__ +#include +#include +#include +#include +#include +#endif // __APPLE__ + +#if HAVE_CLOCK_GETTIME_NSEC_NP +#include +#endif + +using std::nullptr_t; + +#define PalRaiseFailFastException RaiseFailFastException + +#define FATAL_ASSERT(e, msg) \ + do \ + { \ + if (!(e)) \ + { \ + fprintf(stderr, "FATAL ERROR: " msg); \ + RhFailFast(); \ + } \ + } \ + while(0) + +#define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1) + +#define PAGE_NOACCESS 0x01 +#define PAGE_READWRITE 0x04 +#define PAGE_EXECUTE_READ 0x20 +#define PAGE_EXECUTE_READWRITE 0x40 +#define MEM_COMMIT 0x1000 +#define MEM_RESERVE 0x2000 +#define MEM_DECOMMIT 0x4000 +#define MEM_RELEASE 0x8000 + +#define WAIT_OBJECT_0 0 +#define WAIT_TIMEOUT 258 +#define WAIT_FAILED 0xFFFFFFFF + +static const int tccSecondsToMilliSeconds = 1000; +static const int tccSecondsToMicroSeconds = 1000000; +static const int tccSecondsToNanoSeconds = 1000000000; +static const int tccMilliSecondsToMicroSeconds = 1000; +static const int tccMilliSecondsToNanoSeconds = 1000000; +static const int tccMicroSecondsToNanoSeconds = 1000; + +extern "C" void RaiseFailFastException(PEXCEPTION_RECORD arg1, PCONTEXT arg2, uint32_t arg3) +{ + // Abort aborts the process and causes creation of a crash dump + abort(); +} + +static void TimeSpecAdd(timespec* time, uint32_t milliseconds) +{ + uint64_t nsec = time->tv_nsec + (uint64_t)milliseconds * tccMilliSecondsToNanoSeconds; + if (nsec >= tccSecondsToNanoSeconds) + { + time->tv_sec += nsec / tccSecondsToNanoSeconds; + nsec %= tccSecondsToNanoSeconds; + } + + time->tv_nsec = nsec; +} + +// Convert nanoseconds to the timespec structure +// Parameters: +// nanoseconds - time in nanoseconds to convert +// t - the target timespec structure +static void NanosecondsToTimeSpec(uint64_t nanoseconds, timespec* t) +{ + t->tv_sec = nanoseconds / tccSecondsToNanoSeconds; + t->tv_nsec = nanoseconds % tccSecondsToNanoSeconds; +} + +void ReleaseCondAttr(pthread_condattr_t* condAttr) +{ + int st = pthread_condattr_destroy(condAttr); + ASSERT_MSG(st == 0, "Failed to destroy pthread_condattr_t object"); +} + +class PthreadCondAttrHolder : public Wrapper +{ +public: + PthreadCondAttrHolder(pthread_condattr_t* attrs) + : Wrapper(attrs) + { + } +}; + +class UnixEvent +{ + pthread_cond_t m_condition; + pthread_mutex_t m_mutex; + bool m_manualReset; + bool m_state; + bool m_isValid; + +public: + + UnixEvent(bool manualReset, bool initialState) + : m_manualReset(manualReset), + m_state(initialState), + m_isValid(false) + { + } + + bool Initialize() + { + pthread_condattr_t attrs; + int st = pthread_condattr_init(&attrs); + if (st != 0) + { + ASSERT_UNCONDITIONALLY("Failed to initialize UnixEvent condition attribute"); + return false; + } + + PthreadCondAttrHolder attrsHolder(&attrs); + +#if HAVE_PTHREAD_CONDATTR_SETCLOCK && !HAVE_CLOCK_GETTIME_NSEC_NP + // Ensure that the pthread_cond_timedwait will use CLOCK_MONOTONIC + st = pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC); + if (st != 0) + { + ASSERT_UNCONDITIONALLY("Failed to set UnixEvent condition variable wait clock"); + return false; + } +#endif // HAVE_PTHREAD_CONDATTR_SETCLOCK && !HAVE_CLOCK_GETTIME_NSEC_NP + + st = pthread_mutex_init(&m_mutex, NULL); + if (st != 0) + { + ASSERT_UNCONDITIONALLY("Failed to initialize UnixEvent mutex"); + return false; + } + + st = pthread_cond_init(&m_condition, &attrs); + if (st != 0) + { + ASSERT_UNCONDITIONALLY("Failed to initialize UnixEvent condition variable"); + + st = pthread_mutex_destroy(&m_mutex); + ASSERT_MSG(st == 0, "Failed to destroy UnixEvent mutex"); + return false; + } + + m_isValid = true; + + return true; + } + + bool Destroy() + { + bool success = true; + + if (m_isValid) + { + int st = pthread_mutex_destroy(&m_mutex); + ASSERT_MSG(st == 0, "Failed to destroy UnixEvent mutex"); + success = success && (st == 0); + + st = pthread_cond_destroy(&m_condition); + ASSERT_MSG(st == 0, "Failed to destroy UnixEvent condition variable"); + success = success && (st == 0); + } + + return success; + } + + uint32_t Wait(uint32_t milliseconds) + { + timespec endTime; +#if HAVE_CLOCK_GETTIME_NSEC_NP + uint64_t endNanoseconds; + if (milliseconds != INFINITE) + { + uint64_t nanoseconds = (uint64_t)milliseconds * tccMilliSecondsToNanoSeconds; + NanosecondsToTimeSpec(nanoseconds, &endTime); + endNanoseconds = clock_gettime_nsec_np(CLOCK_UPTIME_RAW) + nanoseconds; + } +#elif HAVE_PTHREAD_CONDATTR_SETCLOCK + if (milliseconds != INFINITE) + { + clock_gettime(CLOCK_MONOTONIC, &endTime); + TimeSpecAdd(&endTime, milliseconds); + } +#else +#error Don't know how to perform timed wait on this platform +#endif + + int st = 0; + + pthread_mutex_lock(&m_mutex); + while (!m_state) + { + if (milliseconds == INFINITE) + { + st = pthread_cond_wait(&m_condition, &m_mutex); + } + else + { +#if HAVE_CLOCK_GETTIME_NSEC_NP + // Since OSX doesn't support CLOCK_MONOTONIC, we use relative variant of the + // timed wait and we need to handle spurious wakeups properly. + st = pthread_cond_timedwait_relative_np(&m_condition, &m_mutex, &endTime); + if ((st == 0) && !m_state) + { + uint64_t currentNanoseconds = clock_gettime_nsec_np(CLOCK_UPTIME_RAW); + if (currentNanoseconds < endNanoseconds) + { + // The wake up was spurious, recalculate the relative endTime + uint64_t remainingNanoseconds = (endNanoseconds - currentNanoseconds); + NanosecondsToTimeSpec(remainingNanoseconds, &endTime); + } + else + { + // Although the timed wait didn't report a timeout, time calculated from the + // mach time shows we have already reached the end time. It can happen if + // the wait was spuriously woken up right before the timeout. + st = ETIMEDOUT; + } + } +#else // HAVE_CLOCK_GETTIME_NSEC_NP + st = pthread_cond_timedwait(&m_condition, &m_mutex, &endTime); +#endif // HAVE_CLOCK_GETTIME_NSEC_NP + // Verify that if the wait timed out, the event was not set + ASSERT((st != ETIMEDOUT) || !m_state); + } + + if (st != 0) + { + // wait failed or timed out + break; + } + } + + if ((st == 0) && !m_manualReset) + { + // Clear the state for auto-reset events so that only one waiter gets released + m_state = false; + } + + pthread_mutex_unlock(&m_mutex); + + uint32_t waitStatus; + + if (st == 0) + { + waitStatus = WAIT_OBJECT_0; + } + else if (st == ETIMEDOUT) + { + waitStatus = WAIT_TIMEOUT; + } + else + { + waitStatus = WAIT_FAILED; + } + + return waitStatus; + } + + void Set() + { + pthread_mutex_lock(&m_mutex); + m_state = true; + pthread_mutex_unlock(&m_mutex); + + // Unblock all threads waiting for the condition variable + pthread_cond_broadcast(&m_condition); + } + + void Reset() + { + pthread_mutex_lock(&m_mutex); + m_state = false; + pthread_mutex_unlock(&m_mutex); + } +}; + +class EventUnixHandle : public UnixHandle +{ +public: + EventUnixHandle(UnixEvent event) + : UnixHandle(event) + { + } + + virtual bool Destroy() + { + return m_object.Destroy(); + } +}; + +typedef UnixHandle ThreadUnixHandle; + +#if !HAVE_THREAD_LOCAL +extern "C" int __cxa_thread_atexit(void (*)(void*), void*, void *); +extern "C" void *__dso_handle; +#endif + +// This functions configures behavior of the signals that are not +// related to hardware exception handling. +void ConfigureSignals() +{ + // The default action for SIGPIPE is process termination. + // Since SIGPIPE can be signaled when trying to write on a socket for which + // the connection has been dropped, we need to tell the system we want + // to ignore this signal. + // Instead of terminating the process, the system call which would had + // issued a SIGPIPE will, instead, report an error and set errno to EPIPE. + signal(SIGPIPE, SIG_IGN); +} + +extern bool GetCpuLimit(uint32_t* val); + +void InitializeCurrentProcessCpuCount() +{ + uint32_t count; + + // If the configuration value has been set, it takes precedence. Otherwise, take into account + // process affinity and CPU quota limit. + + const unsigned int MAX_PROCESSOR_COUNT = 0xffff; + uint32_t configValue; + + if (g_pRhConfig->ReadConfigValue(_T("PROCESSOR_COUNT"), &configValue, true /* decimal */) && + 0 < configValue && configValue <= MAX_PROCESSOR_COUNT) + { + count = configValue; + } + else + { +#if HAVE_SCHED_GETAFFINITY + + cpu_set_t cpuSet; + int st = sched_getaffinity(getpid(), sizeof(cpu_set_t), &cpuSet); + if (st != 0) + { + _ASSERTE(!"sched_getaffinity failed"); + } + + count = CPU_COUNT(&cpuSet); +#else // HAVE_SCHED_GETAFFINITY + count = GCToOSInterface::GetTotalProcessorCount(); +#endif // HAVE_SCHED_GETAFFINITY + + uint32_t cpuLimit; + if (GetCpuLimit(&cpuLimit) && cpuLimit < count) + count = cpuLimit; + } + + _ASSERTE(count > 0); + g_RhNumberOfProcessors = count; +} + +// The Redhawk PAL must be initialized before any of its exports can be called. Returns true for a successful +// initialization and false on failure. +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalInit() +{ +#ifndef USE_PORTABLE_HELPERS + if (!InitializeHardwareExceptionHandling()) + { + return false; + } +#endif // !USE_PORTABLE_HELPERS + + ConfigureSignals(); + + if (!GCToOSInterface::Initialize()) + { + return false; + } + + InitializeCpuCGroup(); + + InitializeCurrentProcessCpuCount(); + + return true; +} + +#if HAVE_THREAD_LOCAL + +struct TlsDestructionMonitor +{ + void* m_thread = nullptr; + + void SetThread(void* thread) + { + m_thread = thread; + } + + ~TlsDestructionMonitor() + { + if (m_thread != nullptr) + { + RuntimeThreadShutdown(m_thread); + } + } +}; + +// This thread local object is used to detect thread shutdown. Its destructor +// is called when a thread is being shut down. +thread_local TlsDestructionMonitor tls_destructionMonitor; + +#endif // HAVE_THREAD_LOCAL + +// This thread local variable is used for delegate marshalling +DECLSPEC_THREAD intptr_t tls_thunkData; + +#ifdef FEATURE_EMULATED_TLS +EXTERN_C intptr_t* RhpGetThunkData() +{ + return &tls_thunkData; +} + +EXTERN_C intptr_t RhGetCurrentThunkContext() +{ + return tls_thunkData; +} +#endif //FEATURE_EMULATED_TLS + +// Attach thread to PAL. +// It can be called multiple times for the same thread. +// It fails fast if a different thread was already registered. +// Parameters: +// thread - thread to attach +extern "C" void PalAttachThread(void* thread) +{ +#if HAVE_THREAD_LOCAL + tls_destructionMonitor.SetThread(thread); +#else + __cxa_thread_atexit(RuntimeThreadShutdown, thread, &__dso_handle); +#endif +} + +// Detach thread from PAL. +// It fails fast if some other thread value was attached to PAL. +// Parameters: +// thread - thread to detach +// Return: +// true if the thread was detached, false if there was no attached thread +extern "C" bool PalDetachThread(void* thread) +{ + UNREFERENCED_PARAMETER(thread); + if (g_threadExitCallback != nullptr) + { + g_threadExitCallback(); + } + return true; +} + +#if !defined(USE_PORTABLE_HELPERS) && !defined(FEATURE_RX_THUNKS) +REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalAllocateThunksFromTemplate(HANDLE hTemplateModule, uint32_t templateRva, size_t templateSize, void** newThunksOut) +{ + PORTABILITY_ASSERT("UNIXTODO: Implement this function"); +} + +REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalFreeThunksFromTemplate(void *pBaseAddress) +{ + PORTABILITY_ASSERT("UNIXTODO: Implement this function"); +} +#endif // !USE_PORTABLE_HELPERS && !FEATURE_RX_THUNKS + +REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalMarkThunksAsValidCallTargets( + void *virtualAddress, + int thunkSize, + int thunksPerBlock, + int thunkBlockSize, + int thunkBlocksPerMapping) +{ + return UInt32_TRUE; +} + +REDHAWK_PALEXPORT void REDHAWK_PALAPI PalSleep(uint32_t milliseconds) +{ +#if HAVE_CLOCK_NANOSLEEP + timespec endTime; + clock_gettime(CLOCK_MONOTONIC, &endTime); + TimeSpecAdd(&endTime, milliseconds); + while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &endTime, NULL) == EINTR) + { + } +#else // HAVE_CLOCK_NANOSLEEP + timespec requested; + requested.tv_sec = milliseconds / tccSecondsToMilliSeconds; + requested.tv_nsec = (milliseconds - requested.tv_sec * tccSecondsToMilliSeconds) * tccMilliSecondsToNanoSeconds; + + timespec remaining; + while (nanosleep(&requested, &remaining) == EINTR) + { + requested = remaining; + } +#endif // HAVE_CLOCK_NANOSLEEP +} + +REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI __stdcall PalSwitchToThread() +{ + // sched_yield yields to another thread in the current process. This implementation + // won't work well for cross-process synchronization. + return sched_yield() == 0; +} + +extern "C" UInt32_BOOL CloseHandle(HANDLE handle) +{ + if ((handle == NULL) || (handle == INVALID_HANDLE_VALUE)) + { + return UInt32_FALSE; + } + + UnixHandleBase* handleBase = (UnixHandleBase*)handle; + + bool success = handleBase->Destroy(); + + delete handleBase; + + return success ? UInt32_TRUE : UInt32_FALSE; +} + +REDHAWK_PALEXPORT HANDLE REDHAWK_PALAPI PalCreateEventW(_In_opt_ LPSECURITY_ATTRIBUTES pEventAttributes, UInt32_BOOL manualReset, UInt32_BOOL initialState, _In_opt_z_ const wchar_t* pName) +{ + UnixEvent event = UnixEvent(manualReset, initialState); + if (!event.Initialize()) + { + return INVALID_HANDLE_VALUE; + } + + EventUnixHandle* handle = new (nothrow) EventUnixHandle(event); + + if (handle == NULL) + { + return INVALID_HANDLE_VALUE; + } + + return handle; +} + +typedef uint32_t(__stdcall *BackgroundCallback)(_In_opt_ void* pCallbackContext); + +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartBackgroundWork(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext, UInt32_BOOL highPriority) +{ +#ifdef HOST_WASM + // No threads, so we can't start one + ASSERT(false); +#endif // HOST_WASM + pthread_attr_t attrs; + + int st = pthread_attr_init(&attrs); + ASSERT(st == 0); + + static const int NormalPriority = 0; + static const int HighestPriority = -20; + + // TODO: Figure out which scheduler to use, the default one doesn't seem to + // support per thread priorities. +#if 0 + sched_param params; + memset(¶ms, 0, sizeof(params)); + + params.sched_priority = highPriority ? HighestPriority : NormalPriority; + + // Set the priority of the thread + st = pthread_attr_setschedparam(&attrs, ¶ms); + ASSERT(st == 0); +#endif + // Create the thread as detached, that means not joinable + st = pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED); + ASSERT(st == 0); + + pthread_t threadId; + st = pthread_create(&threadId, &attrs, (void *(*)(void*))callback, pCallbackContext); + + int st2 = pthread_attr_destroy(&attrs); + ASSERT(st2 == 0); + + return st == 0; +} + +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartBackgroundGCThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext) +{ + return PalStartBackgroundWork(callback, pCallbackContext, UInt32_FALSE); +} + +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartFinalizerThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext) +{ +#ifdef HOST_WASM + // WASMTODO: No threads so we can't start the finalizer thread + return true; +#else // HOST_WASM + return PalStartBackgroundWork(callback, pCallbackContext, UInt32_TRUE); +#endif // HOST_WASM +} + +// Returns a 64-bit tick count with a millisecond resolution. It tries its best +// to return monotonically increasing counts and avoid being affected by changes +// to the system clock (either due to drift or due to explicit changes to system +// time). +REDHAWK_PALEXPORT uint64_t REDHAWK_PALAPI PalGetTickCount64() +{ + uint64_t retval = 0; + +#if HAVE_CLOCK_GETTIME_NSEC_NP + { + retval = clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / tccMilliSecondsToNanoSeconds; + } +#elif HAVE_CLOCK_MONOTONIC + { + clockid_t clockType = +#if HAVE_CLOCK_MONOTONIC_COARSE + CLOCK_MONOTONIC_COARSE; // good enough resolution, fastest speed +#else + CLOCK_MONOTONIC; +#endif + struct timespec ts; + if (clock_gettime(clockType, &ts) == 0) + { + retval = (ts.tv_sec * tccSecondsToMilliSeconds) + (ts.tv_nsec / tccMilliSecondsToNanoSeconds); + } + else + { + ASSERT_UNCONDITIONALLY("clock_gettime(CLOCK_MONOTONIC) failed\n"); + } + } +#else + { + struct timeval tv; + if (gettimeofday(&tv, NULL) == 0) + { + retval = (tv.tv_sec * tccSecondsToMilliSeconds) + (tv.tv_usec / tccMilliSecondsToMicroSeconds); + } + else + { + ASSERT_UNCONDITIONALLY("gettimeofday() failed\n"); + } + } +#endif + + return retval; +} + +REDHAWK_PALEXPORT HANDLE REDHAWK_PALAPI PalGetModuleHandleFromPointer(_In_ void* pointer) +{ + HANDLE moduleHandle = NULL; + + // Emscripten's implementation of dladdr corrupts memory, + // but always returns 0 for the module handle, so just skip the call +#if !defined(HOST_WASM) + Dl_info info; + int st = dladdr(pointer, &info); + if (st != 0) + { + moduleHandle = info.dli_fbase; + } +#endif //!defined(HOST_WASM) + + return moduleHandle; +} + +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalIsAvxEnabled() +{ + return true; +} + +REDHAWK_PALEXPORT void PalPrintFatalError(const char* message) +{ + // Write the message using lowest-level OS API available. This is used to print the stack overflow + // message, so there is not much that can be done here. + write(STDERR_FILENO, message, sizeof(message)); +} + +static int W32toUnixAccessControl(uint32_t flProtect) +{ + int prot = 0; + + switch (flProtect & 0xff) + { + case PAGE_NOACCESS: + prot = PROT_NONE; + break; + case PAGE_READWRITE: + prot = PROT_READ | PROT_WRITE; + break; + case PAGE_EXECUTE_READ: + prot = PROT_READ | PROT_EXEC; + break; + case PAGE_EXECUTE_READWRITE: + prot = PROT_READ | PROT_WRITE | PROT_EXEC; + break; + default: + ASSERT(false); + break; + } + return prot; +} + +REDHAWK_PALEXPORT _Ret_maybenull_ _Post_writable_byte_size_(size) void* REDHAWK_PALAPI PalVirtualAlloc(_In_opt_ void* pAddress, size_t size, uint32_t allocationType, uint32_t protect) +{ + // TODO: thread safety! + + if ((allocationType & ~(MEM_RESERVE | MEM_COMMIT)) != 0) + { + // TODO: Implement + return NULL; + } + + ASSERT(((size_t)pAddress & (OS_PAGE_SIZE - 1)) == 0); + + // Align size to whole pages + size = (size + (OS_PAGE_SIZE - 1)) & ~(OS_PAGE_SIZE - 1); + int unixProtect = W32toUnixAccessControl(protect); + + if (allocationType & (MEM_RESERVE | MEM_COMMIT)) + { + // For Windows compatibility, let the PalVirtualAlloc reserve memory with 64k alignment. + static const size_t Alignment = 64 * 1024; + + size_t alignedSize = size + (Alignment - OS_PAGE_SIZE); + + void * pRetVal = mmap(pAddress, alignedSize, unixProtect, MAP_ANON | MAP_PRIVATE, -1, 0); + + if (pRetVal != NULL) + { + void * pAlignedRetVal = (void *)(((size_t)pRetVal + (Alignment - 1)) & ~(Alignment - 1)); + size_t startPadding = (size_t)pAlignedRetVal - (size_t)pRetVal; + if (startPadding != 0) + { + int ret = munmap(pRetVal, startPadding); + ASSERT(ret == 0); + } + + size_t endPadding = alignedSize - (startPadding + size); + if (endPadding != 0) + { + int ret = munmap((void *)((size_t)pAlignedRetVal + size), endPadding); + ASSERT(ret == 0); + } + + pRetVal = pAlignedRetVal; + } + + return pRetVal; + } + + if (allocationType & MEM_COMMIT) + { + int ret = mprotect(pAddress, size, unixProtect); + return (ret == 0) ? pAddress : NULL; + } + + return NULL; +} + +REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalVirtualFree(_In_ void* pAddress, size_t size, uint32_t freeType) +{ + ASSERT(((freeType & MEM_RELEASE) != MEM_RELEASE) || size == 0); + ASSERT((freeType & (MEM_RELEASE | MEM_DECOMMIT)) != (MEM_RELEASE | MEM_DECOMMIT)); + ASSERT(freeType != 0); + + // UNIXTODO: Implement this function + return UInt32_TRUE; +} + +REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalVirtualProtect(_In_ void* pAddress, size_t size, uint32_t protect) +{ + int unixProtect = W32toUnixAccessControl(protect); + + return mprotect(pAddress, size, unixProtect) == 0; +} + +REDHAWK_PALEXPORT _Ret_maybenull_ void* REDHAWK_PALAPI PalSetWerDataBuffer(_In_ void* pNewBuffer) +{ + static void* pBuffer; + return _InterlockedExchangePointer(&pBuffer, pNewBuffer); +} + +extern "C" HANDLE GetCurrentProcess() +{ + return (HANDLE)-1; +} + +extern "C" uint32_t GetCurrentProcessId() +{ + return getpid(); +} + +extern "C" HANDLE GetCurrentThread() +{ + return (HANDLE)-2; +} + +extern "C" UInt32_BOOL DuplicateHandle( + HANDLE hSourceProcessHandle, + HANDLE hSourceHandle, + HANDLE hTargetProcessHandle, + HANDLE * lpTargetHandle, + uint32_t dwDesiredAccess, + UInt32_BOOL bInheritHandle, + uint32_t dwOptions) +{ + // We can only duplicate the current thread handle. That is all that the MRT uses. + ASSERT(hSourceProcessHandle == GetCurrentProcess()); + ASSERT(hTargetProcessHandle == GetCurrentProcess()); + ASSERT(hSourceHandle == GetCurrentThread()); + *lpTargetHandle = new (nothrow) ThreadUnixHandle(pthread_self()); + + return lpTargetHandle != nullptr; +} + +extern "C" UInt32_BOOL InitializeCriticalSection(CRITICAL_SECTION * lpCriticalSection) +{ + return pthread_mutex_init(&lpCriticalSection->mutex, NULL) == 0; +} + +extern "C" UInt32_BOOL InitializeCriticalSectionEx(CRITICAL_SECTION * lpCriticalSection, uint32_t arg2, uint32_t arg3) +{ + return InitializeCriticalSection(lpCriticalSection); +} + + +extern "C" void DeleteCriticalSection(CRITICAL_SECTION * lpCriticalSection) +{ + pthread_mutex_destroy(&lpCriticalSection->mutex); +} + +extern "C" void EnterCriticalSection(CRITICAL_SECTION * lpCriticalSection) +{ + pthread_mutex_lock(&lpCriticalSection->mutex);; +} + +extern "C" void LeaveCriticalSection(CRITICAL_SECTION * lpCriticalSection) +{ + pthread_mutex_unlock(&lpCriticalSection->mutex); +} + +extern "C" UInt32_BOOL IsDebuggerPresent() +{ +#ifdef HOST_WASM + // For now always true since the browser will handle it in case of WASM. + return UInt32_TRUE; +#else + // UNIXTODO: Implement this function + return UInt32_FALSE; +#endif +} + +extern "C" UInt32_BOOL SetEvent(HANDLE event) +{ + EventUnixHandle* unixHandle = (EventUnixHandle*)event; + unixHandle->GetObject()->Set(); + + return UInt32_TRUE; +} + +extern "C" UInt32_BOOL ResetEvent(HANDLE event) +{ + EventUnixHandle* unixHandle = (EventUnixHandle*)event; + unixHandle->GetObject()->Reset(); + + return UInt32_TRUE; +} + +extern "C" uint32_t GetEnvironmentVariableA(const char * name, char * buffer, uint32_t size) +{ + // Using std::getenv instead of getenv since it is guaranteed to be thread safe w.r.t. other + // std::getenv calls in C++11 + const char* value = std::getenv(name); + if (value == NULL) + { + return 0; + } + + size_t valueLen = strlen(value); + + if (valueLen < size) + { + strcpy(buffer, value); + return valueLen; + } + + // return required size including the null character or 0 if the size doesn't fit into uint32_t + return (valueLen < UINT32_MAX) ? (valueLen + 1) : 0; +} + +extern "C" uint16_t RtlCaptureStackBackTrace(uint32_t arg1, uint32_t arg2, void* arg3, uint32_t* arg4) +{ + // UNIXTODO: Implement this function + return 0; +} + +typedef uint32_t (__stdcall *HijackCallback)(HANDLE hThread, _In_ PAL_LIMITED_CONTEXT* pThreadContext, _In_opt_ void* pCallbackContext); + +REDHAWK_PALEXPORT uint32_t REDHAWK_PALAPI PalHijack(HANDLE hThread, _In_ HijackCallback callback, _In_opt_ void* pCallbackContext) +{ + // UNIXTODO: Implement PalHijack + return E_FAIL; +} + +extern "C" uint32_t WaitForSingleObjectEx(HANDLE handle, uint32_t milliseconds, UInt32_BOOL alertable) +{ + // The handle can only represent an event here + // TODO: encapsulate this stuff + UnixHandleBase* handleBase = (UnixHandleBase*)handle; + ASSERT(handleBase->GetType() == UnixHandleType::Event); + EventUnixHandle* unixHandle = (EventUnixHandle*)handleBase; + + return unixHandle->GetObject()->Wait(milliseconds); +} + +REDHAWK_PALEXPORT uint32_t REDHAWK_PALAPI PalCompatibleWaitAny(UInt32_BOOL alertable, uint32_t timeout, uint32_t handleCount, HANDLE* pHandles, UInt32_BOOL allowReentrantWait) +{ + // Only a single handle wait for event is supported + ASSERT(handleCount == 1); + + return WaitForSingleObjectEx(pHandles[0], timeout, alertable); +} + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if !__has_builtin(_mm_pause) +extern "C" void _mm_pause() +// Defined for implementing PalYieldProcessor in PalRedhawk.h +{ +#if defined(HOST_AMD64) || defined(HOST_X86) + __asm__ volatile ("pause"); +#endif +} +#endif + +extern "C" int32_t _stricmp(const char *string1, const char *string2) +{ + return strcasecmp(string1, string2); +} + +uint32_t g_RhNumberOfProcessors; + +REDHAWK_PALEXPORT int32_t PalGetProcessCpuCount() +{ + ASSERT(g_RhNumberOfProcessors > 0); + return g_RhNumberOfProcessors; +} + +__thread void* pStackHighOut = NULL; +__thread void* pStackLowOut = NULL; + +// Retrieves the entire range of memory dedicated to the calling thread's stack. This does +// not get the current dynamic bounds of the stack, which can be significantly smaller than +// the maximum bounds. +REDHAWK_PALEXPORT bool PalGetMaximumStackBounds(_Out_ void** ppStackLowOut, _Out_ void** ppStackHighOut) +{ + if (pStackHighOut == NULL) + { +#ifdef __APPLE__ + // This is a Mac specific method + pStackHighOut = pthread_get_stackaddr_np(pthread_self()); + pStackLowOut = ((uint8_t *)pStackHighOut - pthread_get_stacksize_np(pthread_self())); +#else // __APPLE__ + pthread_attr_t attr; + size_t stackSize; + int status; + + pthread_t thread = pthread_self(); + + status = pthread_attr_init(&attr); + ASSERT_MSG(status == 0, "pthread_attr_init call failed"); + +#if HAVE_PTHREAD_ATTR_GET_NP + status = pthread_attr_get_np(thread, &attr); +#elif HAVE_PTHREAD_GETATTR_NP + status = pthread_getattr_np(thread, &attr); +#else +#error Dont know how to get thread attributes on this platform! +#endif + ASSERT_MSG(status == 0, "pthread_getattr_np call failed"); + + status = pthread_attr_getstack(&attr, &pStackLowOut, &stackSize); + ASSERT_MSG(status == 0, "pthread_attr_getstack call failed"); + + status = pthread_attr_destroy(&attr); + ASSERT_MSG(status == 0, "pthread_attr_destroy call failed"); + + pStackHighOut = (uint8_t*)pStackLowOut + stackSize; +#endif // __APPLE__ + } + + *ppStackLowOut = pStackLowOut; + *ppStackHighOut = pStackHighOut; + + return true; +} + +// retrieves the full path to the specified module, if moduleBase is NULL retreieves the full path to the +// executable module of the current process. +// +// Return value: number of characters in name string +// +REDHAWK_PALEXPORT int32_t PalGetModuleFileName(_Out_ const TCHAR** pModuleNameOut, HANDLE moduleBase) +{ +#if defined(HOST_WASM) + // Emscripten's implementation of dladdr corrupts memory and doesn't have the real name, so make up a name instead + const TCHAR* wasmModuleName = "WebAssemblyModule"; + *pModuleNameOut = wasmModuleName; + return strlen(wasmModuleName); +#else // HOST_WASM + Dl_info dl; + if (dladdr(moduleBase, &dl) == 0) + { + *pModuleNameOut = NULL; + return 0; + } + + *pModuleNameOut = dl.dli_fname; + return strlen(dl.dli_fname); +#endif // defined(HOST_WASM) +} + +extern "C" void FlushProcessWriteBuffers() +{ + GCToOSInterface::FlushProcessWriteBuffers(); +} + +static const int64_t SECS_BETWEEN_1601_AND_1970_EPOCHS = 11644473600LL; +static const int64_t SECS_TO_100NS = 10000000; /* 10^7 */ + +extern "C" void GetSystemTimeAsFileTime(FILETIME *lpSystemTimeAsFileTime) +{ + struct timeval time = { 0 }; + gettimeofday(&time, NULL); + + int64_t result = ((int64_t)time.tv_sec + SECS_BETWEEN_1601_AND_1970_EPOCHS) * SECS_TO_100NS + + (time.tv_usec * 10); + + lpSystemTimeAsFileTime->dwLowDateTime = (uint32_t)result; + lpSystemTimeAsFileTime->dwHighDateTime = (uint32_t)(result >> 32); +} + +extern "C" UInt32_BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount) +{ + // TODO: More efficient, platform-specific implementation + struct timeval tv; + if (gettimeofday(&tv, NULL) == -1) + { + ASSERT_UNCONDITIONALLY("gettimeofday() failed"); + return UInt32_FALSE; + } + lpPerformanceCount->QuadPart = + (int64_t) tv.tv_sec * (int64_t) tccSecondsToMicroSeconds + (int64_t) tv.tv_usec; + return UInt32_TRUE; +} + +extern "C" UInt32_BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency) +{ + lpFrequency->QuadPart = (int64_t) tccSecondsToMicroSeconds; + return UInt32_TRUE; +} + +extern "C" uint64_t PalGetCurrentThreadIdForLogging() +{ +#if defined(__linux__) + return (uint64_t)syscall(SYS_gettid); +#elif defined(__APPLE__) + uint64_t tid; + pthread_threadid_np(pthread_self(), &tid); + return (uint64_t)tid; +#elif HAVE_PTHREAD_GETTHREADID_NP + return (uint64_t)pthread_getthreadid_np(); +#elif HAVE_LWP_SELF + return (uint64_t)_lwp_self(); +#else + // Fallback in case we don't know how to get integer thread id on the current platform + return (uint64_t)pthread_self(); +#endif +} + +#if defined(HOST_X86) || defined(HOST_AMD64) + +REDHAWK_PALEXPORT void __cpuid(int cpuInfo[4], int function_id) +{ + // Based on the Clang implementation provided in cpuid.h: + // https://github.com/llvm/llvm-project/blob/master/clang/lib/Headers/cpuid.h + + __asm(" cpuid\n" \ + : "=a"(cpuInfo[0]), "=b"(cpuInfo[1]), "=c"(cpuInfo[2]), "=d"(cpuInfo[3]) \ + : "0"(function_id) + ); +} + +REDHAWK_PALEXPORT void __cpuidex(int cpuInfo[4], int function_id, int subFunction_id) +{ + // Based on the Clang implementation provided in cpuid.h: + // https://github.com/llvm/llvm-project/blob/master/clang/lib/Headers/cpuid.h + + __asm(" cpuid\n" \ + : "=a"(cpuInfo[0]), "=b"(cpuInfo[1]), "=c"(cpuInfo[2]), "=d"(cpuInfo[3]) \ + : "0"(function_id), "2"(subFunction_id) + ); +} + +REDHAWK_PALEXPORT uint32_t REDHAWK_PALAPI xmmYmmStateSupport() +{ + DWORD eax; + __asm(" xgetbv\n" \ + : "=a"(eax) /*output in eax*/\ + : "c"(0) /*inputs - 0 in ecx*/\ + : "edx" /* registers that are clobbered*/ + ); + // check OS has enabled both XMM and YMM state support + return ((eax & 0x06) == 0x06) ? 1 : 0; +} +#endif // defined(HOST_X86) || defined(HOST_AMD64) + +#if defined (HOST_ARM64) + +#if HAVE_AUXV_HWCAP_H +#include +#include +#endif + +// Based on PAL_GetJitCpuCapabilityFlags from CoreCLR (jitsupport.cpp) +REDHAWK_PALEXPORT void REDHAWK_PALAPI PAL_GetCpuCapabilityFlags(int* flags) +{ + *flags = 0; + +#if HAVE_AUXV_HWCAP_H + unsigned long hwCap = getauxval(AT_HWCAP); + + // HWCAP_* flags are introduced by ARM into the Linux kernel as new extensions are published. + // For a given kernel, some of these flags may not be present yet. + // Use ifdef for each to allow for compilation with any vintage kernel. + // From a single binary distribution perspective, compiling with latest kernel asm/hwcap.h should + // include all published flags. Given flags are merged to kernel and published before silicon is + // available, using the latest kernel for release should be sufficient. + *flags |= ARM64IntrinsicConstants_ArmBase; + *flags |= ARM64IntrinsicConstants_ArmBase_Arm64; + +#ifdef HWCAP_AES + if (hwCap & HWCAP_AES) + *flags |= ARM64IntrinsicConstants_Aes; +#endif +#ifdef HWCAP_ATOMICS + if (hwCap & HWCAP_ATOMICS) + *flags |= ARM64IntrinsicConstants_Atomics; +#endif +#ifdef HWCAP_CRC32 + if (hwCap & HWCAP_CRC32) + *flags |= ARM64IntrinsicConstants_Crc32; +#endif +#ifdef HWCAP_DCPOP +// if (hwCap & HWCAP_DCPOP) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_ASIMDDP +// if (hwCap & HWCAP_ASIMDDP) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_FCMA +// if (hwCap & HWCAP_FCMA) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_FP +// if (hwCap & HWCAP_FP) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_FPHP +// if (hwCap & HWCAP_FPHP) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_JSCVT +// if (hwCap & HWCAP_JSCVT) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_LRCPC +// if (hwCap & HWCAP_LRCPC) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_PMULL +// if (hwCap & HWCAP_PMULL) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_SHA1 + if (hwCap & HWCAP_SHA1) + *flags |= ARM64IntrinsicConstants_Sha1; +#endif +#ifdef HWCAP_SHA2 + if (hwCap & HWCAP_SHA2) + *flags |= ARM64IntrinsicConstants_Sha256; +#endif +#ifdef HWCAP_SHA512 +// if (hwCap & HWCAP_SHA512) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_SHA3 +// if (hwCap & HWCAP_SHA3) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_ASIMD + if (hwCap & HWCAP_ASIMD) + { + *flags |= ARM64IntrinsicConstants_AdvSimd; + *flags |= ARM64IntrinsicConstants_AdvSimd_Arm64; + } +#endif +#ifdef HWCAP_ASIMDRDM +// if (hwCap & HWCAP_ASIMDRDM) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_ASIMDHP +// if (hwCap & HWCAP_ASIMDHP) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_SM3 +// if (hwCap & HWCAP_SM3) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_SM4 +// if (hwCap & HWCAP_SM4) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP_SVE +// if (hwCap & HWCAP_SVE) +// *flags |= ARM64IntrinsicConstants_???; +#endif + +#ifdef AT_HWCAP2 + unsigned long hwCap2 = getauxval(AT_HWCAP2); + +#ifdef HWCAP2_DCPODP +// if (hwCap2 & HWCAP2_DCPODP) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP2_SVE2 +// if (hwCap2 & HWCAP2_SVE2) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP2_SVEAES +// if (hwCap2 & HWCAP2_SVEAES) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP2_SVEPMULL +// if (hwCap2 & HWCAP2_SVEPMULL) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP2_SVEBITPERM +// if (hwCap2 & HWCAP2_SVEBITPERM) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP2_SVESHA3 +// if (hwCap2 & HWCAP2_SVESHA3) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP2_SVESM4 +// if (hwCap2 & HWCAP2_SVESM4) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP2_FLAGM2 +// if (hwCap2 & HWCAP2_FLAGM2) +// *flags |= ARM64IntrinsicConstants_???; +#endif +#ifdef HWCAP2_FRINT +// if (hwCap2 & HWCAP2_FRINT) +// *flags |= ARM64IntrinsicConstants_???; +#endif + +#endif // AT_HWCAP2 + +#else // !HAVE_AUXV_HWCAP_H + // Every ARM64 CPU should support SIMD and FP + // If the OS have no function to query for CPU capabilities we set just these + + *flags |= ARM64IntrinsicConstants_ArmBase; + *flags |= ARM64IntrinsicConstants_ArmBase_Arm64; + *flags |= ARM64IntrinsicConstants_AdvSimd; + *flags |= ARM64IntrinsicConstants_AdvSimd_Arm64; +#endif // HAVE_AUXV_HWCAP_H +} +#endif diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp new file mode 100644 index 00000000000000..8adf74b32e49bc --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp @@ -0,0 +1,636 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" + +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "regdisplay.h" +#include "config.h" + +#include + +#if HAVE_UCONTEXT_T +#include +#endif // HAVE_UCONTEXT_T + +#include "UnixContext.h" +#include "UnwindHelpers.h" + +// WebAssembly has a slightly different version of LibUnwind that doesn't define unw_get_save_loc +#if defined(HOST_WASM) +enum unw_save_loc_type_t +{ + UNW_SLT_NONE, /* register is not saved ("not an l-value") */ + UNW_SLT_MEMORY, /* register has been saved in memory */ + UNW_SLT_REG /* register has been saved in (another) register */ +}; +typedef enum unw_save_loc_type_t unw_save_loc_type_t; + +struct unw_save_loc_t +{ + unw_save_loc_type_t type; + union + { + unw_word_t addr; /* valid if type==UNW_SLT_MEMORY */ + unw_regnum_t regnum; /* valid if type==UNW_SLT_REG */ + } + u; +}; +typedef struct unw_save_loc_t unw_save_loc_t; + +int unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*) +{ + return -1; +} +#endif // _WASM + +#ifdef __APPLE__ + +#define MCREG_Rip(mc) ((mc)->__ss.__rip) +#define MCREG_Rsp(mc) ((mc)->__ss.__rsp) +#define MCREG_Rax(mc) ((mc)->__ss.__rax) +#define MCREG_Rbx(mc) ((mc)->__ss.__rbx) +#define MCREG_Rcx(mc) ((mc)->__ss.__rcx) +#define MCREG_Rdx(mc) ((mc)->__ss.__rdx) +#define MCREG_Rsi(mc) ((mc)->__ss.__rsi) +#define MCREG_Rdi(mc) ((mc)->__ss.__rdi) +#define MCREG_Rbp(mc) ((mc)->__ss.__rbp) +#define MCREG_R8(mc) ((mc)->__ss.__r8) +#define MCREG_R9(mc) ((mc)->__ss.__r9) +#define MCREG_R10(mc) ((mc)->__ss.__r10) +#define MCREG_R11(mc) ((mc)->__ss.__r11) +#define MCREG_R12(mc) ((mc)->__ss.__r12) +#define MCREG_R13(mc) ((mc)->__ss.__r13) +#define MCREG_R14(mc) ((mc)->__ss.__r14) +#define MCREG_R15(mc) ((mc)->__ss.__r15) + +#else + +#if HAVE___GREGSET_T + +#ifdef HOST_64BIT +#define MCREG_Rip(mc) ((mc).__gregs[_REG_RIP]) +#define MCREG_Rsp(mc) ((mc).__gregs[_REG_RSP]) +#define MCREG_Rax(mc) ((mc).__gregs[_REG_RAX]) +#define MCREG_Rbx(mc) ((mc).__gregs[_REG_RBX]) +#define MCREG_Rcx(mc) ((mc).__gregs[_REG_RCX]) +#define MCREG_Rdx(mc) ((mc).__gregs[_REG_RDX]) +#define MCREG_Rsi(mc) ((mc).__gregs[_REG_RSI]) +#define MCREG_Rdi(mc) ((mc).__gregs[_REG_RDI]) +#define MCREG_Rbp(mc) ((mc).__gregs[_REG_RBP]) +#define MCREG_R8(mc) ((mc).__gregs[_REG_R8]) +#define MCREG_R9(mc) ((mc).__gregs[_REG_R9]) +#define MCREG_R10(mc) ((mc).__gregs[_REG_R10]) +#define MCREG_R11(mc) ((mc).__gregs[_REG_R11]) +#define MCREG_R12(mc) ((mc).__gregs[_REG_R12]) +#define MCREG_R13(mc) ((mc).__gregs[_REG_R13]) +#define MCREG_R14(mc) ((mc).__gregs[_REG_R14]) +#define MCREG_R15(mc) ((mc).__gregs[_REG_R15]) + +#else // HOST_64BIT + +#define MCREG_Eip(mc) ((mc).__gregs[_REG_EIP]) +#define MCREG_Esp(mc) ((mc).__gregs[_REG_ESP]) +#define MCREG_Eax(mc) ((mc).__gregs[_REG_EAX]) +#define MCREG_Ebx(mc) ((mc).__gregs[_REG_EBX]) +#define MCREG_Ecx(mc) ((mc).__gregs[_REG_ECX]) +#define MCREG_Edx(mc) ((mc).__gregs[_REG_EDX]) +#define MCREG_Esi(mc) ((mc).__gregs[_REG_ESI]) +#define MCREG_Edi(mc) ((mc).__gregs[_REG_EDI]) +#define MCREG_Ebp(mc) ((mc).__gregs[_REG_EBP]) + +#endif // HOST_64BIT + +#elif HAVE_GREGSET_T + +#ifdef HOST_64BIT +#define MCREG_Rip(mc) ((mc).gregs[REG_RIP]) +#define MCREG_Rsp(mc) ((mc).gregs[REG_RSP]) +#define MCREG_Rax(mc) ((mc).gregs[REG_RAX]) +#define MCREG_Rbx(mc) ((mc).gregs[REG_RBX]) +#define MCREG_Rcx(mc) ((mc).gregs[REG_RCX]) +#define MCREG_Rdx(mc) ((mc).gregs[REG_RDX]) +#define MCREG_Rsi(mc) ((mc).gregs[REG_RSI]) +#define MCREG_Rdi(mc) ((mc).gregs[REG_RDI]) +#define MCREG_Rbp(mc) ((mc).gregs[REG_RBP]) +#define MCREG_R8(mc) ((mc).gregs[REG_R8]) +#define MCREG_R9(mc) ((mc).gregs[REG_R9]) +#define MCREG_R10(mc) ((mc).gregs[REG_R10]) +#define MCREG_R11(mc) ((mc).gregs[REG_R11]) +#define MCREG_R12(mc) ((mc).gregs[REG_R12]) +#define MCREG_R13(mc) ((mc).gregs[REG_R13]) +#define MCREG_R14(mc) ((mc).gregs[REG_R14]) +#define MCREG_R15(mc) ((mc).gregs[REG_R15]) + +#else // HOST_64BIT + +#define MCREG_Eip(mc) ((mc).gregs[REG_EIP]) +#define MCREG_Esp(mc) ((mc).gregs[REG_ESP]) +#define MCREG_Eax(mc) ((mc).gregs[REG_EAX]) +#define MCREG_Ebx(mc) ((mc).gregs[REG_EBX]) +#define MCREG_Ecx(mc) ((mc).gregs[REG_ECX]) +#define MCREG_Edx(mc) ((mc).gregs[REG_EDX]) +#define MCREG_Esi(mc) ((mc).gregs[REG_ESI]) +#define MCREG_Edi(mc) ((mc).gregs[REG_EDI]) +#define MCREG_Ebp(mc) ((mc).gregs[REG_EBP]) + +#endif // HOST_64BIT + +#else // HAVE_GREGSET_T + +#ifdef HOST_64BIT + +#if defined(HOST_ARM64) + +#define MCREG_Pc(mc) ((mc).pc) +#define MCREG_Sp(mc) ((mc).sp) +#define MCREG_Lr(mc) ((mc).regs[30]) +#define MCREG_X0(mc) ((mc).regs[0]) +#define MCREG_X1(mc) ((mc).regs[1]) +#define MCREG_X19(mc) ((mc).regs[19]) +#define MCREG_X20(mc) ((mc).regs[20]) +#define MCREG_X21(mc) ((mc).regs[21]) +#define MCREG_X22(mc) ((mc).regs[22]) +#define MCREG_X23(mc) ((mc).regs[23]) +#define MCREG_X24(mc) ((mc).regs[24]) +#define MCREG_X25(mc) ((mc).regs[25]) +#define MCREG_X26(mc) ((mc).regs[26]) +#define MCREG_X27(mc) ((mc).regs[27]) +#define MCREG_X28(mc) ((mc).regs[28]) +#define MCREG_Fp(mc) ((mc).regs[29]) + +#else + +// For FreeBSD, as found in x86/ucontext.h +#define MCREG_Rip(mc) ((mc).mc_rip) +#define MCREG_Rsp(mc) ((mc).mc_rsp) +#define MCREG_Rax(mc) ((mc).mc_rax) +#define MCREG_Rbx(mc) ((mc).mc_rbx) +#define MCREG_Rcx(mc) ((mc).mc_rcx) +#define MCREG_Rdx(mc) ((mc).mc_rdx) +#define MCREG_Rsi(mc) ((mc).mc_rsi) +#define MCREG_Rdi(mc) ((mc).mc_rdi) +#define MCREG_Rbp(mc) ((mc).mc_rbp) +#define MCREG_R8(mc) ((mc).mc_r8) +#define MCREG_R9(mc) ((mc).mc_r9) +#define MCREG_R10(mc) ((mc).mc_r10) +#define MCREG_R11(mc) ((mc).mc_r11) +#define MCREG_R12(mc) ((mc).mc_r12) +#define MCREG_R13(mc) ((mc).mc_r13) +#define MCREG_R14(mc) ((mc).mc_r14) +#define MCREG_R15(mc) ((mc).mc_r15) + +#endif + +#else // HOST_64BIT + +#if defined(HOST_ARM) + +#define MCREG_Pc(mc) ((mc).arm_pc) +#define MCREG_Sp(mc) ((mc).arm_sp) +#define MCREG_Lr(mc) ((mc).arm_lr) +#define MCREG_R0(mc) ((mc).arm_r0) +#define MCREG_R1(mc) ((mc).arm_r1) +#define MCREG_R4(mc) ((mc).arm_r4) +#define MCREG_R5(mc) ((mc).arm_r5) +#define MCREG_R6(mc) ((mc).arm_r6) +#define MCREG_R7(mc) ((mc).arm_r7) +#define MCREG_R8(mc) ((mc).arm_r8) +#define MCREG_R9(mc) ((mc).arm_r9) +#define MCREG_R10(mc) ((mc).arm_r10) +#define MCREG_R11(mc) ((mc).arm_fp) + +#elif defined(HOST_X86) + +#define MCREG_Eip(mc) ((mc).mc_eip) +#define MCREG_Esp(mc) ((mc).mc_esp) +#define MCREG_Eax(mc) ((mc).mc_eax) +#define MCREG_Ebx(mc) ((mc).mc_ebx) +#define MCREG_Ecx(mc) ((mc).mc_ecx) +#define MCREG_Edx(mc) ((mc).mc_edx) +#define MCREG_Esi(mc) ((mc).mc_esi) +#define MCREG_Edi(mc) ((mc).mc_edi) +#define MCREG_Ebp(mc) ((mc).mc_ebp) + +#else +#error "Unsupported arch" +#endif + +#endif // HOST_64BIT + +#endif // HAVE_GREGSET_T + +#endif // __APPLE__ + +// Update unw_cursor_t from REGDISPLAY. +// NOTE: We don't set the IP here since the current use cases for this function +// don't require it. +static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *cursor) +{ +#define ASSIGN_REG(regName1, regName2) \ + unw_set_reg(cursor, regName1, regDisplay->regName2, 0); + +#define ASSIGN_REG_PTR(regName1, regName2) \ + if (regDisplay->p##regName2 != NULL) \ + unw_set_reg(cursor, regName1, *(regDisplay->p##regName2), 0); + +#if defined(HOST_AMD64) + ASSIGN_REG(UNW_REG_SP, SP) + ASSIGN_REG_PTR(UNW_X86_64_RBP, Rbp) + ASSIGN_REG_PTR(UNW_X86_64_RBX, Rbx) + ASSIGN_REG_PTR(UNW_X86_64_R12, R12) + ASSIGN_REG_PTR(UNW_X86_64_R13, R13) + ASSIGN_REG_PTR(UNW_X86_64_R14, R14) + ASSIGN_REG_PTR(UNW_X86_64_R15, R15) +#elif HOST_ARM + ASSIGN_REG(UNW_ARM_SP, SP) + ASSIGN_REG_PTR(UNW_ARM_R4, R4) + ASSIGN_REG_PTR(UNW_ARM_R5, R5) + ASSIGN_REG_PTR(UNW_ARM_R6, R6) + ASSIGN_REG_PTR(UNW_ARM_R7, R7) + ASSIGN_REG_PTR(UNW_ARM_R8, R8) + ASSIGN_REG_PTR(UNW_ARM_R9, R9) + ASSIGN_REG_PTR(UNW_ARM_R10, R10) + ASSIGN_REG_PTR(UNW_ARM_R11, R11) + ASSIGN_REG_PTR(UNW_ARM_R14, LR) +#elif HOST_ARM64 + ASSIGN_REG(UNW_ARM64_SP, SP) + ASSIGN_REG_PTR(UNW_ARM64_FP, FP) + ASSIGN_REG_PTR(UNW_ARM64_X19, X19) + ASSIGN_REG_PTR(UNW_ARM64_X20, X20) + ASSIGN_REG_PTR(UNW_ARM64_X21, X21) + ASSIGN_REG_PTR(UNW_ARM64_X22, X22) + ASSIGN_REG_PTR(UNW_ARM64_X23, X23) + ASSIGN_REG_PTR(UNW_ARM64_X24, X24) + ASSIGN_REG_PTR(UNW_ARM64_X25, X25) + ASSIGN_REG_PTR(UNW_ARM64_X26, X26) + ASSIGN_REG_PTR(UNW_ARM64_X27, X27) + ASSIGN_REG_PTR(UNW_ARM64_X28, X28) +#elif defined(HOST_X86) + ASSIGN_REG(UNW_REG_SP, SP) + ASSIGN_REG_PTR(UNW_X86_EBP, Rbp) + ASSIGN_REG_PTR(UNW_X86_EBX, Rbx) +#endif + +#undef ASSIGN_REG +#undef ASSIGN_REG_PTR +} + +// Returns the unw_proc_info_t for a given IP. +bool GetUnwindProcInfo(PCODE ip, unw_proc_info_t *procInfo) +{ + int st; + + unw_context_t unwContext; + unw_cursor_t cursor; + + st = unw_getcontext(&unwContext); + if (st < 0) + { + return false; + } + +#ifdef HOST_AMD64 + // We manually index into the unw_context_t's internals for now because there's + // no better way to modify it. This will go away in the future when we locate the + // LSDA and other information without initializing an unwind cursor. + unwContext.data[16] = ip; +#elif HOST_ARM + ((uint32_t*)(unwContext.data))[15] = ip; +#elif HOST_ARM64 + unwContext.data[32] = ip; +#elif HOST_WASM + ASSERT(false); +#elif HOST_X86 + ASSERT(false); +#else + #error "GetUnwindProcInfo is not supported on this arch yet." +#endif + + st = unw_init_local(&cursor, &unwContext); + if (st < 0) + { + return false; + } + + st = unw_get_proc_info(&cursor, procInfo); + if (st < 0) + { + return false; + } + + return true; +} + +// Initialize unw_cursor_t and unw_context_t from REGDISPLAY +bool InitializeUnwindContextAndCursor(REGDISPLAY* regDisplay, unw_cursor_t* cursor, unw_context_t* unwContext) +{ + int st; + + st = unw_getcontext(unwContext); + if (st < 0) + { + return false; + } + + // Set the IP here instead of after unwinder initialization. unw_init_local + // will do some initialization of internal structures based on the IP value. + // We manually index into the unw_context_t's internals for now because there's + // no better way to modify it. This whole function will go away in the future + // when we are able to read unwind info without initializing an unwind cursor. +#ifdef HOST_AMD64 + unwContext->data[16] = regDisplay->IP; +#elif HOST_ARM + ((uint32_t*)(unwContext->data))[15] = regDisplay->IP; +#elif HOST_ARM64 + ((uint32_t*)(unwContext->data))[32] = regDisplay->IP; +#elif HOST_X86 + ASSERT(false); +#else + #error "InitializeUnwindContextAndCursor is not supported on this arch yet." +#endif + + st = unw_init_local(cursor, unwContext); + if (st < 0) + { + return false; + } + + // Set the unwind context to the specified Windows context. + RegDisplayToUnwindCursor(regDisplay, cursor); + + return true; +} + +// Update context pointer for a register from the unw_cursor_t. +static void GetContextPointer(unw_cursor_t *cursor, unw_context_t *unwContext, int reg, PTR_UIntNative *contextPointer) +{ + unw_save_loc_t saveLoc; + unw_get_save_loc(cursor, reg, &saveLoc); + if (saveLoc.type == UNW_SLT_MEMORY) + { + PTR_UIntNative pLoc = (PTR_UIntNative)saveLoc.u.addr; + // Filter out fake save locations that point to unwContext + if (unwContext == NULL || (pLoc < (PTR_UIntNative)unwContext) || ((PTR_UIntNative)(unwContext + 1) <= pLoc)) + *contextPointer = (PTR_UIntNative)saveLoc.u.addr; + } +} + +#if defined(HOST_AMD64) +#define GET_CONTEXT_POINTERS \ + GET_CONTEXT_POINTER(UNW_X86_64_RBP, Rbp) \ + GET_CONTEXT_POINTER(UNW_X86_64_RBX, Rbx) \ + GET_CONTEXT_POINTER(UNW_X86_64_R12, R12) \ + GET_CONTEXT_POINTER(UNW_X86_64_R13, R13) \ + GET_CONTEXT_POINTER(UNW_X86_64_R14, R14) \ + GET_CONTEXT_POINTER(UNW_X86_64_R15, R15) +#elif defined(HOST_ARM) +#define GET_CONTEXT_POINTERS \ + GET_CONTEXT_POINTER(UNW_ARM_R4, R4) \ + GET_CONTEXT_POINTER(UNW_ARM_R5, R5) \ + GET_CONTEXT_POINTER(UNW_ARM_R6, R6) \ + GET_CONTEXT_POINTER(UNW_ARM_R7, R7) \ + GET_CONTEXT_POINTER(UNW_ARM_R8, R8) \ + GET_CONTEXT_POINTER(UNW_ARM_R9, R9) \ + GET_CONTEXT_POINTER(UNW_ARM_R10, R10) \ + GET_CONTEXT_POINTER(UNW_ARM_R11, R11) +#elif defined(HOST_ARM64) +#define GET_CONTEXT_POINTERS \ + GET_CONTEXT_POINTER(UNW_ARM64_X19, X19) \ + GET_CONTEXT_POINTER(UNW_ARM64_X20, X20) \ + GET_CONTEXT_POINTER(UNW_ARM64_X21, X21) \ + GET_CONTEXT_POINTER(UNW_ARM64_X22, X22) \ + GET_CONTEXT_POINTER(UNW_ARM64_X23, X23) \ + GET_CONTEXT_POINTER(UNW_ARM64_X24, X24) \ + GET_CONTEXT_POINTER(UNW_ARM64_X25, X25) \ + GET_CONTEXT_POINTER(UNW_ARM64_X26, X26) \ + GET_CONTEXT_POINTER(UNW_ARM64_X27, X27) \ + GET_CONTEXT_POINTER(UNW_ARM64_X28, X28) \ + GET_CONTEXT_POINTER(UNW_ARM64_FP, FP) +#elif defined(HOST_X86) +#define GET_CONTEXT_POINTERS \ + GET_CONTEXT_POINTER(UNW_X86_EBP, Rbp) \ + GET_CONTEXT_POINTER(UNW_X86_EBX, Rbx) +#elif defined (HOST_WASM) +// No registers +#define GET_CONTEXT_POINTERS +#else +#error unsupported architecture +#endif + +// Update REGDISPLAY from the unw_cursor_t and unw_context_t +void UnwindCursorToRegDisplay(unw_cursor_t *cursor, unw_context_t *unwContext, REGDISPLAY *regDisplay) +{ +#define GET_CONTEXT_POINTER(unwReg, rdReg) GetContextPointer(cursor, unwContext, unwReg, ®Display->p##rdReg); + GET_CONTEXT_POINTERS +#undef GET_CONTEXT_POINTER + + unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) ®Display->IP); + unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) ®Display->SP); + +#if defined(HOST_AMD64) + regDisplay->pIP = PTR_PCODE(regDisplay->SP - sizeof(TADDR)); +#endif + +#if defined(HOST_ARM) || defined(HOST_ARM64) + regDisplay->IP |= 1; +#endif +} + +#if defined(HOST_AMD64) +#define ASSIGN_CONTROL_REGS \ + ASSIGN_REG(Rip, IP) \ + ASSIGN_REG(Rsp, Rsp) + +#define ASSIGN_INTEGER_REGS \ + ASSIGN_REG(Rbx, Rbx) \ + ASSIGN_REG(Rbp, Rbp) \ + ASSIGN_REG(R12, R12) \ + ASSIGN_REG(R13, R13) \ + ASSIGN_REG(R14, R14) \ + ASSIGN_REG(R15, R15) + +#define ASSIGN_TWO_ARGUMENT_REGS(arg0Reg, arg1Reg) \ + MCREG_Rdi(nativeContext->uc_mcontext) = arg0Reg; \ + MCREG_Rsi(nativeContext->uc_mcontext) = arg1Reg; + +#elif defined(HOST_X86) +#define ASSIGN_CONTROL_REGS \ + ASSIGN_REG(Eip, IP) \ + ASSIGN_REG(Esp, Rsp) + +#define ASSIGN_INTEGER_REGS \ + ASSIGN_REG(Ebx, Rbx) \ + ASSIGN_REG(Ebp, Rbp) + +#define ASSIGN_TWO_ARGUMENT_REGS(arg0Reg, arg1Reg) \ + MCREG_Ecx(nativeContext->uc_mcontext) = arg0Reg; \ + MCREG_Edx(nativeContext->uc_mcontext) = arg1Reg; + +#elif defined(HOST_ARM) + +#define ASSIGN_CONTROL_REGS \ + ASSIGN_REG(Pc, IP) \ + ASSIGN_REG(Sp, SP) \ + ASSIGN_REG(Lr, LR) + +#define ASSIGN_INTEGER_REGS \ + ASSIGN_REG(R4, R4) \ + ASSIGN_REG(R5, R5) \ + ASSIGN_REG(R6, R6) \ + ASSIGN_REG(R7, R7) \ + ASSIGN_REG(R8, R8) \ + ASSIGN_REG(R9, R9) \ + ASSIGN_REG(R10, R10) \ + ASSIGN_REG(R11, R11) + +#define ASSIGN_TWO_ARGUMENT_REGS(arg0Reg, arg1Reg) \ + MCREG_R0(nativeContext->uc_mcontext) = arg0Reg; \ + MCREG_R1(nativeContext->uc_mcontext) = arg1Reg; + +#elif defined(HOST_ARM64) + +#define ASSIGN_CONTROL_REGS \ + ASSIGN_REG(Pc, IP) \ + ASSIGN_REG(Sp, SP) \ + ASSIGN_REG(Fp, FP) \ + ASSIGN_REG(Lr, LR) + +#define ASSIGN_INTEGER_REGS \ + ASSIGN_REG(X19, X19) \ + ASSIGN_REG(X20, X20) \ + ASSIGN_REG(X21, X21) \ + ASSIGN_REG(X22, X22) \ + ASSIGN_REG(X23, X23) \ + ASSIGN_REG(X24, X24) \ + ASSIGN_REG(X25, X25) \ + ASSIGN_REG(X26, X26) \ + ASSIGN_REG(X27, X27) \ + ASSIGN_REG(X28, X28) + +#define ASSIGN_TWO_ARGUMENT_REGS \ + MCREG_X0(nativeContext->uc_mcontext) = arg0Reg; \ + MCREG_X1(nativeContext->uc_mcontext) = arg1Reg; + +#elif defined(HOST_WASM) + // TODO: determine how unwinding will work on WebAssembly +#define ASSIGN_CONTROL_REGS +#define ASSIGN_INTEGER_REGS +#define ASSIGN_TWO_ARGUMENT_REGS +#else +#error unsupported architecture +#endif + +// Convert Unix native context to PAL_LIMITED_CONTEXT +void NativeContextToPalContext(const void* context, PAL_LIMITED_CONTEXT* palContext) +{ + ucontext_t *nativeContext = (ucontext_t*)context; +#define ASSIGN_REG(regNative, regPal) palContext->regPal = MCREG_##regNative(nativeContext->uc_mcontext); + ASSIGN_CONTROL_REGS + ASSIGN_INTEGER_REGS +#undef ASSIGN_REG +} + +// Redirect Unix native context to the PAL_LIMITED_CONTEXT and also set the first two argument registers +void RedirectNativeContext(void* context, const PAL_LIMITED_CONTEXT* palContext, uintptr_t arg0Reg, uintptr_t arg1Reg) +{ + ucontext_t *nativeContext = (ucontext_t*)context; + +#define ASSIGN_REG(regNative, regPal) MCREG_##regNative(nativeContext->uc_mcontext) = palContext->regPal; + ASSIGN_CONTROL_REGS +#undef ASSIGN_REG + ASSIGN_TWO_ARGUMENT_REGS(arg0Reg, arg1Reg); +} + +#ifdef HOST_AMD64 +// Get value of a register from the native context +// Parameters: +// void* context - context containing the registers +// uint32_t index - index of the register +// Rax = 0, Rcx = 1, Rdx = 2, Rbx = 3 +// Rsp = 4, Rbp = 5, Rsi = 6, Rdi = 7 +// R8 = 8, R9 = 9, R10 = 10, R11 = 11 +// R12 = 12, R13 = 13, R14 = 14, R15 = 15 +uint64_t GetRegisterValueByIndex(void* context, uint32_t index) +{ + ucontext_t *nativeContext = (ucontext_t*)context; + switch (index) + { + case 0: + return MCREG_Rax(nativeContext->uc_mcontext); + case 1: + return MCREG_Rcx(nativeContext->uc_mcontext); + case 2: + return MCREG_Rdx(nativeContext->uc_mcontext); + case 3: + return MCREG_Rbx(nativeContext->uc_mcontext); + case 4: + return MCREG_Rsp(nativeContext->uc_mcontext); + case 5: + return MCREG_Rbp(nativeContext->uc_mcontext); + case 6: + return MCREG_Rsi(nativeContext->uc_mcontext); + case 7: + return MCREG_Rdi(nativeContext->uc_mcontext); + case 8: + return MCREG_R8(nativeContext->uc_mcontext); + case 9: + return MCREG_R9(nativeContext->uc_mcontext); + case 10: + return MCREG_R10(nativeContext->uc_mcontext); + case 11: + return MCREG_R11(nativeContext->uc_mcontext); + case 12: + return MCREG_R12(nativeContext->uc_mcontext); + case 13: + return MCREG_R13(nativeContext->uc_mcontext); + case 14: + return MCREG_R14(nativeContext->uc_mcontext); + case 15: + return MCREG_R15(nativeContext->uc_mcontext); + } + + ASSERT(false); + return 0; +} + +// Get value of the program counter from the native context +uint64_t GetPC(void* context) +{ + ucontext_t *nativeContext = (ucontext_t*)context; + return MCREG_Rip(nativeContext->uc_mcontext); +} + +#endif // HOST_AMD64 + +// Find LSDA and start address for a function at address controlPC +bool FindProcInfo(uintptr_t controlPC, uintptr_t* startAddress, uintptr_t* lsda) +{ + unw_proc_info_t procInfo; + + if (!GetUnwindProcInfo((PCODE)controlPC, &procInfo)) + { + return false; + } + + assert((procInfo.start_ip <= controlPC) && (controlPC < procInfo.end_ip)); + +#if defined(HOST_ARM) + // libunwind fills by reference not by value for ARM + *lsda = *((uintptr_t *)procInfo.lsda); +#else + *lsda = procInfo.lsda; +#endif + *startAddress = procInfo.start_ip; + + return true; +} + +// Virtually unwind stack to the caller of the context specified by the REGDISPLAY +bool VirtualUnwind(REGDISPLAY* pRegisterSet) +{ + return UnwindHelpers::StepFrame(pRegisterSet); +} diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixContext.h b/src/coreclr/nativeaot/Runtime/unix/UnixContext.h new file mode 100644 index 00000000000000..dec744a7282d09 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/UnixContext.h @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __UNIX_CONTEXT_H__ +#define __UNIX_CONTEXT_H__ + +// Convert Unix native context to PAL_LIMITED_CONTEXT +void NativeContextToPalContext(const void* context, PAL_LIMITED_CONTEXT* palContext); +// Redirect Unix native context to the PAL_LIMITED_CONTEXT and also set the first two argument registers +void RedirectNativeContext(void* context, const PAL_LIMITED_CONTEXT* palContext, uintptr_t arg0Reg, uintptr_t arg1Reg); + +// Find LSDA and start address for a function at address controlPC +bool FindProcInfo(uintptr_t controlPC, uintptr_t* startAddress, uintptr_t* lsda); +// Virtually unwind stack to the caller of the context specified by the REGDISPLAY +bool VirtualUnwind(REGDISPLAY* pRegisterSet); + +#ifdef HOST_AMD64 +// Get value of a register from the native context. The index is the processor specific +// register index stored in machine instructions. +uint64_t GetRegisterValueByIndex(void* context, uint32_t index); +// Get value of the program counter from the native context +uint64_t GetPC(void* context); +#endif // HOST_AMD64 + +#endif // __UNIX_CONTEXT_H__ diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixHandle.h b/src/coreclr/nativeaot/Runtime/unix/UnixHandle.h new file mode 100644 index 00000000000000..ac9712a145633f --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/UnixHandle.h @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __UNIX_HANDLE_H__ +#define __UNIX_HANDLE_H__ + +enum class UnixHandleType +{ + Thread, + Event +}; + +// TODO: add validity check for usage / closing? +class UnixHandleBase +{ + UnixHandleType m_type; +protected: + UnixHandleBase(UnixHandleType type) + : m_type(type) + { + } + +public: + + virtual ~UnixHandleBase() + { + } + + virtual bool Destroy() + { + return true; + } + + UnixHandleType GetType() + { + return m_type; + } +}; + +template +class UnixHandle : UnixHandleBase +{ +protected: + T m_object; +public: + + UnixHandle(T object) + : UnixHandleBase(HT), + m_object(object) + { + } + + T* GetObject() + { + return &m_object; + } +}; + +#endif // __UNIX_HANDLE_H__ diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp new file mode 100644 index 00000000000000..8d1be1309e0646 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -0,0 +1,481 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" + +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "regdisplay.h" +#include "ICodeManager.h" +#include "UnixNativeCodeManager.h" +#include "varint.h" +#include "holder.h" + +#include "CommonMacros.inl" + +#define GCINFODECODER_NO_EE +#include "gcinfodecoder.cpp" + +#include "UnixContext.h" + +#define UBF_FUNC_KIND_MASK 0x03 +#define UBF_FUNC_KIND_ROOT 0x00 +#define UBF_FUNC_KIND_HANDLER 0x01 +#define UBF_FUNC_KIND_FILTER 0x02 + +#define UBF_FUNC_HAS_EHINFO 0x04 +#define UBF_FUNC_REVERSE_PINVOKE 0x08 +#define UBF_FUNC_HAS_ASSOCIATED_DATA 0x10 + +struct UnixNativeMethodInfo +{ + PTR_VOID pMethodStartAddress; + PTR_UInt8 pMainLSDA; + PTR_UInt8 pLSDA; + bool executionAborted; +}; + +// Ensure that UnixNativeMethodInfo fits into the space reserved by MethodInfo +static_assert(sizeof(UnixNativeMethodInfo) <= sizeof(MethodInfo), "UnixNativeMethodInfo too big"); + +UnixNativeCodeManager::UnixNativeCodeManager(TADDR moduleBase, + PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange, + PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions) + : m_moduleBase(moduleBase), + m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange), + m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions) +{ +} + +UnixNativeCodeManager::~UnixNativeCodeManager() +{ +} + +bool UnixNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC, + MethodInfo * pMethodInfoOut) +{ + // Stackwalker may call this with ControlPC that does not belong to this code manager + if (dac_cast(ControlPC) < dac_cast(m_pvManagedCodeStartRange) || + dac_cast(m_pvManagedCodeStartRange) + m_cbManagedCodeRange <= dac_cast(ControlPC)) + { + return false; + } + + UnixNativeMethodInfo * pMethodInfo = (UnixNativeMethodInfo *)pMethodInfoOut; + uintptr_t startAddress; + uintptr_t lsda; + + if (!FindProcInfo((uintptr_t)ControlPC, &startAddress, &lsda)) + { + return false; + } + + PTR_UInt8 p = dac_cast(lsda); + + pMethodInfo->pLSDA = p; + + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT) + { + // Funclets just refer to the main function's blob + pMethodInfo->pMainLSDA = p + *dac_cast(p); + p += sizeof(int32_t); + + pMethodInfo->pMethodStartAddress = dac_cast(startAddress - *dac_cast(p)); + } + else + { + pMethodInfo->pMainLSDA = dac_cast(lsda); + pMethodInfo->pMethodStartAddress = dac_cast(startAddress); + } + + pMethodInfo->executionAborted = false; + + return true; +} + +bool UnixNativeCodeManager::IsFunclet(MethodInfo * pMethodInfo) +{ + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + + uint8_t unwindBlockFlags = *(pNativeMethodInfo->pLSDA); + return (unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT; +} + +bool UnixNativeCodeManager::IsFilter(MethodInfo * pMethodInfo) +{ + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + + uint8_t unwindBlockFlags = *(pNativeMethodInfo->pLSDA); + return (unwindBlockFlags & UBF_FUNC_KIND_MASK) == UBF_FUNC_KIND_FILTER; +} + +PTR_VOID UnixNativeCodeManager::GetFramePointer(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet) +{ + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + + // Return frame pointer for methods with EH and funclets + uint8_t unwindBlockFlags = *(pNativeMethodInfo->pLSDA); + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0 || (unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT) + { + return (PTR_VOID)pRegisterSet->GetFP(); + } + + return NULL; +} + +void UnixNativeCodeManager::EnumGcRefs(MethodInfo * pMethodInfo, + PTR_VOID safePointAddress, + REGDISPLAY * pRegisterSet, + GCEnumContext * hCallback) +{ + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + + PTR_UInt8 p = pNativeMethodInfo->pMainLSDA; + + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + + uint32_t codeOffset = (uint32_t)(PINSTRToPCODE(dac_cast(safePointAddress)) - PINSTRToPCODE(dac_cast(pNativeMethodInfo->pMethodStartAddress))); + + GcInfoDecoder decoder( + GCInfoToken(p), + GcInfoDecoderFlags(DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG), + codeOffset - 1 // TODO: Is this adjustment correct? + ); + + ICodeManagerFlags flags = (ICodeManagerFlags)0; + if (pNativeMethodInfo->executionAborted) + flags = ICodeManagerFlags::ExecutionAborted; + if (IsFilter(pMethodInfo)) + flags = (ICodeManagerFlags)(flags | ICodeManagerFlags::NoReportUntracked); + + if (!decoder.EnumerateLiveSlots( + pRegisterSet, + false /* reportScratchSlots */, + flags, + hCallback->pCallback, + hCallback + )) + { + assert(false); + } +} + +uintptr_t UnixNativeCodeManager::GetConservativeUpperBoundForOutgoingArgs(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet) +{ + // Return value + uintptr_t upperBound; + + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + + PTR_UInt8 p = pNativeMethodInfo->pMainLSDA; + + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) + { + // Reverse PInvoke transition should be on the main function body only + assert(pNativeMethodInfo->pMainLSDA == pNativeMethodInfo->pLSDA); + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + + GcInfoDecoder decoder(GCInfoToken(p), DECODE_REVERSE_PINVOKE_VAR); + INT32 slot = decoder.GetReversePInvokeFrameStackSlot(); + assert(slot != NO_REVERSE_PINVOKE_FRAME); + + TADDR basePointer = NULL; + UINT32 stackBasedRegister = decoder.GetStackBaseRegister(); + if (stackBasedRegister == NO_STACK_BASE_REGISTER) + { + basePointer = dac_cast(pRegisterSet->GetSP()); + } + else + { + basePointer = dac_cast(pRegisterSet->GetFP()); + } + + // Reverse PInvoke case. The embedded reverse PInvoke frame is guaranteed to reside above + // all outgoing arguments. + upperBound = (uintptr_t)dac_cast(basePointer + slot); + } + else + { + // The passed in pRegisterSet should be left intact + REGDISPLAY localRegisterSet = *pRegisterSet; + + bool result = VirtualUnwind(&localRegisterSet); + assert(result); + + // All common ABIs have outgoing arguments under caller SP (minus slot reserved for return address). + // There are ABI-specific optimizations that could applied here, but they are not worth the complexity + // given that this path is used rarely. +#if defined(TARGET_X86) || defined(TARGET_AMD64) + upperBound = dac_cast(localRegisterSet.GetSP() - sizeof(TADDR)); +#else + upperBound = dac_cast(localRegisterSet.GetSP()); +#endif + } + + return upperBound; +} + +bool UnixNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet, // in/out + PTR_VOID * ppPreviousTransitionFrame) // out +{ + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + + PTR_UInt8 p = pNativeMethodInfo->pMainLSDA; + + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) + { + // Reverse PInvoke transition should be on the main function body only + assert(pNativeMethodInfo->pMainLSDA == pNativeMethodInfo->pLSDA); + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + + GcInfoDecoder decoder(GCInfoToken(p), DECODE_REVERSE_PINVOKE_VAR); + INT32 slot = decoder.GetReversePInvokeFrameStackSlot(); + assert(slot != NO_REVERSE_PINVOKE_FRAME); + + TADDR basePointer = NULL; + UINT32 stackBasedRegister = decoder.GetStackBaseRegister(); + if (stackBasedRegister == NO_STACK_BASE_REGISTER) + { + basePointer = dac_cast(pRegisterSet->GetSP()); + } + else + { + basePointer = dac_cast(pRegisterSet->GetFP()); + } + *ppPreviousTransitionFrame = *(void**)(basePointer + slot); + return true; + } + + *ppPreviousTransitionFrame = NULL; + + if (!VirtualUnwind(pRegisterSet)) + { + return false; + } + + return true; +} + +bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet, // in + PTR_PTR_VOID * ppvRetAddrLocation, // out + GCRefKind * pRetValueKind) // out +{ + // @TODO: CORERT: GetReturnAddressHijackInfo + return false; +} + +void UnixNativeCodeManager::UnsynchronizedHijackMethodLoops(MethodInfo * pMethodInfo) +{ + // @TODO: CORERT: UnsynchronizedHijackMethodLoops +} + +PTR_VOID UnixNativeCodeManager::RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC) +{ + // GCInfo decoder needs to know whether execution of the method is aborted + // while querying for gc-info. But ICodeManager::EnumGCRef() doesn't receive any + // flags from mrt. Call to this method is used as a cue to mark the method info + // as execution aborted. Note - if pMethodInfo was cached, this scheme would not work. + // + // If the method has EH, then JIT will make sure the method is fully interruptible + // and we will have GC-info available at the faulting address as well. + + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + pNativeMethodInfo->executionAborted = true; + + return controlPC; +} + +struct UnixEHEnumState +{ + PTR_UInt8 pMethodStartAddress; + PTR_UInt8 pEHInfo; + uint32_t uClause; + uint32_t nClauses; +}; + +// Ensure that UnixEHEnumState fits into the space reserved by EHEnumState +static_assert(sizeof(UnixEHEnumState) <= sizeof(EHEnumState), "UnixEHEnumState too big"); + +bool UnixNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMethodStartAddress, EHEnumState * pEHEnumStateOut) +{ + assert(pMethodInfo != NULL); + assert(pMethodStartAddress != NULL); + assert(pEHEnumStateOut != NULL); + + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + + PTR_UInt8 p = pNativeMethodInfo->pMainLSDA; + + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + // return if there is no EH info associated with this method + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) == 0) + { + return false; + } + + UnixEHEnumState * pEnumState = (UnixEHEnumState *)pEHEnumStateOut; + + *pMethodStartAddress = pNativeMethodInfo->pMethodStartAddress; + + pEnumState->pMethodStartAddress = dac_cast(pNativeMethodInfo->pMethodStartAddress); + pEnumState->pEHInfo = dac_cast(p + *dac_cast(p)); + pEnumState->uClause = 0; + pEnumState->nClauses = VarInt::ReadUnsigned(pEnumState->pEHInfo); + + return true; +} + +bool UnixNativeCodeManager::EHEnumNext(EHEnumState * pEHEnumState, EHClause * pEHClauseOut) +{ + assert(pEHEnumState != NULL); + assert(pEHClauseOut != NULL); + + UnixEHEnumState * pEnumState = (UnixEHEnumState *)pEHEnumState; + if (pEnumState->uClause >= pEnumState->nClauses) + { + return false; + } + + pEnumState->uClause++; + + pEHClauseOut->m_tryStartOffset = VarInt::ReadUnsigned(pEnumState->pEHInfo); + + uint32_t tryEndDeltaAndClauseKind = VarInt::ReadUnsigned(pEnumState->pEHInfo); + pEHClauseOut->m_clauseKind = (EHClauseKind)(tryEndDeltaAndClauseKind & 0x3); + pEHClauseOut->m_tryEndOffset = pEHClauseOut->m_tryStartOffset + (tryEndDeltaAndClauseKind >> 2); + + // For each clause, we have up to 4 integers: + // 1) try start offset + // 2) (try length << 2) | clauseKind + // 3) if (typed || fault || filter) { handler start offset } + // 4a) if (typed) { type RVA } + // 4b) if (filter) { filter start offset } + // + // The first two integers have already been decoded + + switch (pEHClauseOut->m_clauseKind) + { + case EH_CLAUSE_TYPED: + pEHClauseOut->m_handlerAddress = dac_cast(PINSTRToPCODE(dac_cast(pEnumState->pMethodStartAddress))) + VarInt::ReadUnsigned(pEnumState->pEHInfo); + + // Read target type + { + // @TODO: CORERT: Compress EHInfo using type table index scheme + // https://github.com/dotnet/corert/issues/972 + int32_t typeRelAddr = *((PTR_Int32&)pEnumState->pEHInfo); + pEHClauseOut->m_pTargetType = dac_cast(pEnumState->pEHInfo + typeRelAddr); + pEnumState->pEHInfo += 4; + } + break; + case EH_CLAUSE_FAULT: + pEHClauseOut->m_handlerAddress = dac_cast(PINSTRToPCODE(dac_cast(pEnumState->pMethodStartAddress))) + VarInt::ReadUnsigned(pEnumState->pEHInfo); + break; + case EH_CLAUSE_FILTER: + pEHClauseOut->m_handlerAddress = dac_cast(PINSTRToPCODE(dac_cast(pEnumState->pMethodStartAddress))) + VarInt::ReadUnsigned(pEnumState->pEHInfo); + pEHClauseOut->m_filterAddress = dac_cast(PINSTRToPCODE(dac_cast(pEnumState->pMethodStartAddress))) + VarInt::ReadUnsigned(pEnumState->pEHInfo); + break; + default: + UNREACHABLE_MSG("unexpected EHClauseKind"); + } + + return true; +} + +PTR_VOID UnixNativeCodeManager::GetOsModuleHandle() +{ + return (PTR_VOID)m_moduleBase; +} + +PTR_VOID UnixNativeCodeManager::GetMethodStartAddress(MethodInfo * pMethodInfo) +{ + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + return pNativeMethodInfo->pMethodStartAddress; +} + +void * UnixNativeCodeManager::GetClasslibFunction(ClasslibFunctionId functionId) +{ + uint32_t id = (uint32_t)functionId; + + if (id >= m_nClasslibFunctions) + { + return nullptr; + } + + return m_pClasslibFunctions[id]; +} + +PTR_VOID UnixNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC) +{ + UnixNativeMethodInfo methodInfo; + if (!FindMethodInfo(ControlPC, (MethodInfo*)&methodInfo)) + return NULL; + + PTR_UInt8 p = methodInfo.pMainLSDA; + + uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) == 0) + return NULL; + + return dac_cast(p + *dac_cast(p)); +} + +extern "C" bool RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, uint32_t cbRange); +extern "C" void UnregisterCodeManager(ICodeManager * pCodeManager); +extern "C" bool RegisterUnboxingStubs(PTR_VOID pvStartRange, uint32_t cbRange); + +extern "C" +bool RhRegisterOSModule(void * pModule, + void * pvManagedCodeStartRange, uint32_t cbManagedCodeRange, + void * pvUnboxingStubsStartRange, uint32_t cbUnboxingStubsRange, + void ** pClasslibFunctions, uint32_t nClasslibFunctions) +{ + NewHolder pUnixNativeCodeManager = new (nothrow) UnixNativeCodeManager((TADDR)pModule, + pvManagedCodeStartRange, cbManagedCodeRange, + pClasslibFunctions, nClasslibFunctions); + + if (pUnixNativeCodeManager == nullptr) + return false; + + if (!RegisterCodeManager(pUnixNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange)) + return false; + + if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange)) + { + UnregisterCodeManager(pUnixNativeCodeManager); + return false; + } + + pUnixNativeCodeManager.SuppressRelease(); + + return true; +} diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h new file mode 100644 index 00000000000000..84eb852473f3c8 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +class UnixNativeCodeManager : public ICodeManager +{ + TADDR m_moduleBase; + + PTR_VOID m_pvManagedCodeStartRange; + uint32_t m_cbManagedCodeRange; + + PTR_PTR_VOID m_pClasslibFunctions; + uint32_t m_nClasslibFunctions; + +public: + UnixNativeCodeManager(TADDR moduleBase, + PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange, + PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions); + + virtual ~UnixNativeCodeManager(); + + // + // Code manager methods + // + + bool FindMethodInfo(PTR_VOID ControlPC, + MethodInfo * pMethodInfoOut); + + bool IsFunclet(MethodInfo * pMethodInfo); + + bool IsFilter(MethodInfo * pMethodInfo); + + PTR_VOID GetFramePointer(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet); + + void EnumGcRefs(MethodInfo * pMethodInfo, + PTR_VOID safePointAddress, + REGDISPLAY * pRegisterSet, + GCEnumContext * hCallback); + + bool UnwindStackFrame(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet, // in/out + PTR_VOID * ppPreviousTransitionFrame); // out + + uintptr_t GetConservativeUpperBoundForOutgoingArgs(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet); + + bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet, // in + PTR_PTR_VOID * ppvRetAddrLocation, // out + GCRefKind * pRetValueKind); // out + + void UnsynchronizedHijackMethodLoops(MethodInfo * pMethodInfo); + + PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC); + + bool EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMethodStartAddress, EHEnumState * pEHEnumState); + + bool EHEnumNext(EHEnumState * pEHEnumState, EHClause * pEHClause); + + PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo); + + void * GetClasslibFunction(ClasslibFunctionId functionId); + + PTR_VOID GetAssociatedData(PTR_VOID ControlPC); + + PTR_VOID GetOsModuleHandle(); +}; diff --git a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp new file mode 100644 index 00000000000000..7fe9b91637a65c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp @@ -0,0 +1,828 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" +#include "daccess.h" +#include "rhassert.h" + +#define UNW_STEP_SUCCESS 1 +#define UNW_STEP_END 0 + +#ifdef __APPLE__ +#include +#endif + +#include +#include "UnwindHelpers.h" + +// libunwind headers +#include +#include +#include +#include +#if defined(TARGET_ARM) +#include +#endif +#include + + +#if defined(TARGET_AMD64) +using libunwind::Registers_x86_64; +#elif defined(TARGET_ARM) +using libunwind::Registers_arm; +#elif defined(TARGET_ARM64) +using libunwind::Registers_arm64; +#elif defined(TARGET_X86) +using libunwind::Registers_x86; +#else +#error "Unwinding is not implemented for this architecture yet." +#endif +using libunwind::LocalAddressSpace; +using libunwind::EHHeaderParser; +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND +using libunwind::DwarfInstructions; +#endif +using libunwind::UnwindInfoSections; + +LocalAddressSpace _addressSpace; + +#ifdef TARGET_AMD64 + +// Shim that implements methods required by libunwind over REGDISPLAY +struct Registers_REGDISPLAY : REGDISPLAY +{ + static int getArch() { return libunwind::REGISTERS_X86_64; } + + inline uint64_t getRegister(int regNum) const + { + switch (regNum) + { + case UNW_REG_IP: + return IP; + case UNW_REG_SP: + return SP; + case UNW_X86_64_RAX: + return *pRax; + case UNW_X86_64_RDX: + return *pRdx; + case UNW_X86_64_RCX: + return *pRcx; + case UNW_X86_64_RBX: + return *pRbx; + case UNW_X86_64_RSI: + return *pRsi; + case UNW_X86_64_RDI: + return *pRdi; + case UNW_X86_64_RBP: + return *pRbp; + case UNW_X86_64_RSP: + return SP; + case UNW_X86_64_R8: + return *pR8; + case UNW_X86_64_R9: + return *pR9; + case UNW_X86_64_R10: + return *pR10; + case UNW_X86_64_R11: + return *pR11; + case UNW_X86_64_R12: + return *pR12; + case UNW_X86_64_R13: + return *pR13; + case UNW_X86_64_R14: + return *pR14; + case UNW_X86_64_R15: + return *pR15; + } + + // Unsupported register requested + abort(); + } + + inline void setRegister(int regNum, uint64_t value, uint64_t location) + { + switch (regNum) + { + case UNW_REG_IP: + IP = value; + pIP = (PTR_PCODE)location; + return; + case UNW_REG_SP: + SP = value; + return; + case UNW_X86_64_RAX: + pRax = (PTR_UIntNative)location; + return; + case UNW_X86_64_RDX: + pRdx = (PTR_UIntNative)location; + return; + case UNW_X86_64_RCX: + pRcx = (PTR_UIntNative)location; + return; + case UNW_X86_64_RBX: + pRbx = (PTR_UIntNative)location; + return; + case UNW_X86_64_RSI: + pRsi = (PTR_UIntNative)location; + return; + case UNW_X86_64_RDI: + pRdi = (PTR_UIntNative)location; + return; + case UNW_X86_64_RBP: + pRbp = (PTR_UIntNative)location; + return; + case UNW_X86_64_RSP: + SP = value; + return; + case UNW_X86_64_R8: + pR8 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R9: + pR9 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R10: + pR10 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R11: + pR11 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R12: + pR12 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R13: + pR13 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R14: + pR14 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R15: + pR15 = (PTR_UIntNative)location; + return; + } + + // Unsupported x86_64 register + abort(); + } + + // N/A for x86_64 + inline bool validFloatRegister(int) { return false; } + inline bool validVectorRegister(int) { return false; } + + inline static int lastDwarfRegNum() { return 16; } + + inline bool validRegister(int regNum) const + { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 15) + return false; + return true; + } + + // N/A for x86_64 + inline double getFloatRegister(int) const { abort(); } + inline void setFloatRegister(int, double) { abort(); } + inline double getVectorRegister(int) const { abort(); } + inline void setVectorRegister(int, ...) { abort(); } + + uint64_t getSP() const { return SP; } + void setSP(uint64_t value, uint64_t location) { SP = value; } + + uint64_t getIP() const { return IP; } + + void setIP(uint64_t value, uint64_t location) + { + IP = value; + pIP = (PTR_PCODE)location; + } + + uint64_t getRBP() const { return *pRbp; } + void setRBP(uint64_t value, uint64_t location) { pRbp = (PTR_UIntNative)location; } + uint64_t getRBX() const { return *pRbx; } + void setRBX(uint64_t value, uint64_t location) { pRbx = (PTR_UIntNative)location; } + uint64_t getR12() const { return *pR12; } + void setR12(uint64_t value, uint64_t location) { pR12 = (PTR_UIntNative)location; } + uint64_t getR13() const { return *pR13; } + void setR13(uint64_t value, uint64_t location) { pR13 = (PTR_UIntNative)location; } + uint64_t getR14() const { return *pR14; } + void setR14(uint64_t value, uint64_t location) { pR14 = (PTR_UIntNative)location; } + uint64_t getR15() const { return *pR15; } + void setR15(uint64_t value, uint64_t location) { pR15 = (PTR_UIntNative)location; } +}; + +#endif // TARGET_AMD64 +#if defined(TARGET_X86) +struct Registers_REGDISPLAY : REGDISPLAY +{ + static int getArch() { return libunwind::REGISTERS_X86; } + + inline uint64_t getRegister(int regNum) const + { + switch (regNum) + { + case UNW_REG_IP: + return IP; + case UNW_REG_SP: + return SP; + case UNW_X86_EAX: + return *pRax; + case UNW_X86_EDX: + return *pRdx; + case UNW_X86_ECX: + return *pRcx; + case UNW_X86_EBX: + return *pRbx; + case UNW_X86_ESI: + return *pRsi; + case UNW_X86_EDI: + return *pRdi; + case UNW_X86_EBP: + return *pRbp; + case UNW_X86_ESP: + return SP; + } + + // Unsupported register requested + abort(); + } + + inline void setRegister(int regNum, uint64_t value, uint64_t location) + { + switch (regNum) + { + case UNW_REG_IP: + IP = value; + pIP = (PTR_PCODE)location; + return; + case UNW_REG_SP: + SP = value; + return; + case UNW_X86_EAX: + pRax = (PTR_UIntNative)location; + return; + case UNW_X86_EDX: + pRdx = (PTR_UIntNative)location; + return; + case UNW_X86_ECX: + pRcx = (PTR_UIntNative)location; + return; + case UNW_X86_EBX: + pRbx = (PTR_UIntNative)location; + return; + case UNW_X86_ESI: + pRsi = (PTR_UIntNative)location; + return; + case UNW_X86_EDI: + pRdi = (PTR_UIntNative)location; + return; + case UNW_X86_EBP: + pRbp = (PTR_UIntNative)location; + return; + case UNW_X86_ESP: + SP = value; + return; + } + + // Unsupported x86_64 register + abort(); + } + + // N/A for x86 + inline bool validFloatRegister(int) { return false; } + inline bool validVectorRegister(int) { return false; } + + inline static int lastDwarfRegNum() { return 16; } + + inline bool validRegister(int regNum) const + { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 15) + return false; + return true; + } + + // N/A for x86 + inline double getFloatRegister(int) const { abort(); } + inline void setFloatRegister(int, double) { abort(); } + inline double getVectorRegister(int) const { abort(); } + inline void setVectorRegister(int, ...) { abort(); } + + void setSP(uint64_t value, uint64_t location) { SP = value; } + + uint64_t getIP() const { return IP; } + + void setIP(uint64_t value, uint64_t location) + { + IP = value; + pIP = (PTR_PCODE)location; + } + + uint64_t getEBP() const { return *pRbp; } + void setEBP(uint64_t value, uint64_t location) { pRbp = (PTR_UIntNative)location; } + uint64_t getEBX() const { return *pRbx; } + void setEBX(uint64_t value, uint64_t location) { pRbx = (PTR_UIntNative)location; } +}; + +#endif // TARGET_X86 +#if defined(TARGET_ARM) + +class Registers_arm_rt: public libunwind::Registers_arm { +public: + Registers_arm_rt() { abort(); }; + Registers_arm_rt(void *registers) { regs = (REGDISPLAY *)registers; }; + uint32_t getRegister(int num); + void setRegister(int num, uint32_t value, uint32_t location); + uint32_t getRegisterLocation(int regNum) const { abort();} + unw_fpreg_t getFloatRegister(int num) { abort();} + void setFloatRegister(int num, unw_fpreg_t value) {abort();} + bool validVectorRegister(int num) const { abort();} + uint32_t getVectorRegister(int num) const {abort();}; + void setVectorRegister(int num, uint32_t value) {abort();}; + void jumpto() { abort();}; + uint32_t getSP() const { return regs->SP;} + void setSP(uint32_t value, uint32_t location) { regs->SP = value;} + uint32_t getIP() const { return regs->IP;} + void setIP(uint32_t value, uint32_t location) + { regs->IP = value; regs->pIP = (PTR_UIntNative)location; } + void saveVFPAsX() {abort();}; +private: + REGDISPLAY *regs; +}; + +inline uint32_t Registers_arm_rt::getRegister(int regNum) { + if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) + return regs->SP; + + if (regNum == UNW_ARM_LR) + return *regs->pLR; + + if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) + return regs->IP; + + switch (regNum) + { + case (UNW_ARM_R0): + return *regs->pR0; + case (UNW_ARM_R1): + return *regs->pR1; + case (UNW_ARM_R2): + return *regs->pR2; + case (UNW_ARM_R3): + return *regs->pR3; + case (UNW_ARM_R4): + return *regs->pR4; + case (UNW_ARM_R5): + return *regs->pR5; + case (UNW_ARM_R6): + return *regs->pR6; + case (UNW_ARM_R7): + return *regs->pR7; + case (UNW_ARM_R8): + return *regs->pR8; + case (UNW_ARM_R9): + return *regs->pR9; + case (UNW_ARM_R10): + return *regs->pR10; + case (UNW_ARM_R11): + return *regs->pR11; + case (UNW_ARM_R12): + return *regs->pR12; + } + + PORTABILITY_ASSERT("unsupported arm register"); +} + +void Registers_arm_rt::setRegister(int num, uint32_t value, uint32_t location) +{ + + if (num == UNW_REG_SP || num == UNW_ARM_SP) { + regs->SP = (uintptr_t )value; + return; + } + + if (num == UNW_ARM_LR) { + regs->pLR = (PTR_UIntNative)location; + return; + } + + if (num == UNW_REG_IP || num == UNW_ARM_IP) { + regs->IP = value; + /* the location could be NULL, we could try to recover + pointer to value in stack from pLR */ + if ((!location) && (regs->pLR) && (*regs->pLR == value)) + regs->pIP = regs->pLR; + else + regs->pIP = (PTR_UIntNative)location; + return; + } + + switch (num) + { + case (UNW_ARM_R0): + regs->pR0 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R1): + regs->pR1 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R2): + regs->pR2 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R3): + regs->pR3 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R4): + regs->pR4 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R5): + regs->pR5 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R6): + regs->pR6 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R7): + regs->pR7 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R8): + regs->pR8 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R9): + regs->pR9 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R10): + regs->pR10 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R11): + regs->pR11 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R12): + regs->pR12 = (PTR_UIntNative)location; + break; + default: + PORTABILITY_ASSERT("unsupported arm register"); + } +} + +#endif // TARGET_ARM + +#if defined(TARGET_ARM64) + +// Shim that implements methods required by libunwind over REGDISPLAY +struct Registers_REGDISPLAY : REGDISPLAY +{ + inline static int getArch() { return libunwind::REGISTERS_ARM64; } + inline static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64; } + + bool validRegister(int num) const; + bool validFloatRegister(int num) { return false; }; + bool validVectorRegister(int num) const; + + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value, uint64_t location); + + double getFloatRegister(int num) {abort();} + void setFloatRegister(int num, double value) {abort();} + + libunwind::v128 getVectorRegister(int num) const; + void setVectorRegister(int num, libunwind::v128 value); + + uint64_t getSP() const { return SP;} + void setSP(uint64_t value, uint64_t location) { SP = value;} + uint64_t getIP() const { return IP;} + void setIP(uint64_t value, uint64_t location) + { IP = value; pIP = (PTR_UIntNative)location; } +}; + +inline bool Registers_REGDISPLAY::validRegister(int num) const { + if (num == UNW_REG_SP || num == UNW_ARM64_SP) + return true; + + if (num == UNW_ARM64_FP) + return true; + + if (num == UNW_ARM64_LR) + return true; + + if (num == UNW_REG_IP) + return true; + + if (num >= UNW_ARM64_X0 && num <= UNW_ARM64_X28) + return true; + + return false; +} + +bool Registers_REGDISPLAY::validVectorRegister(int num) const +{ + if (num >= UNW_ARM64_D8 && num <= UNW_ARM64_D15) + return true; + + return false; +} + +inline uint64_t Registers_REGDISPLAY::getRegister(int regNum) const { + if (regNum == UNW_REG_SP || regNum == UNW_ARM64_SP) + return SP; + + if (regNum == UNW_ARM64_FP) + return *pFP; + + if (regNum == UNW_ARM64_LR) + return *pLR; + + if (regNum == UNW_REG_IP) + return IP; + + switch (regNum) + { + case (UNW_ARM64_X0): + return *pX0; + case (UNW_ARM64_X1): + return *pX1; + case (UNW_ARM64_X2): + return *pX2; + case (UNW_ARM64_X3): + return *pX3; + case (UNW_ARM64_X4): + return *pX4; + case (UNW_ARM64_X5): + return *pX5; + case (UNW_ARM64_X6): + return *pX6; + case (UNW_ARM64_X7): + return *pX7; + case (UNW_ARM64_X8): + return *pX8; + case (UNW_ARM64_X9): + return *pX9; + case (UNW_ARM64_X10): + return *pX10; + case (UNW_ARM64_X11): + return *pX11; + case (UNW_ARM64_X12): + return *pX12; + case (UNW_ARM64_X13): + return *pX13; + case (UNW_ARM64_X14): + return *pX14; + case (UNW_ARM64_X15): + return *pX15; + case (UNW_ARM64_X16): + return *pX16; + case (UNW_ARM64_X17): + return *pX17; + case (UNW_ARM64_X18): + return *pX18; + case (UNW_ARM64_X19): + return *pX19; + case (UNW_ARM64_X20): + return *pX20; + case (UNW_ARM64_X21): + return *pX21; + case (UNW_ARM64_X22): + return *pX22; + case (UNW_ARM64_X23): + return *pX23; + case (UNW_ARM64_X24): + return *pX24; + case (UNW_ARM64_X25): + return *pX25; + case (UNW_ARM64_X26): + return *pX26; + case (UNW_ARM64_X27): + return *pX27; + case (UNW_ARM64_X28): + return *pX28; + } + + PORTABILITY_ASSERT("unsupported arm64 register"); +} + +void Registers_REGDISPLAY::setRegister(int num, uint64_t value, uint64_t location) +{ + if (num == UNW_REG_SP || num == UNW_ARM64_SP) { + SP = (uintptr_t )value; + return; + } + + if (num == UNW_ARM64_FP) { + pFP = (PTR_UIntNative)location; + return; + } + + if (num == UNW_ARM64_LR) { + pLR = (PTR_UIntNative)location; + return; + } + + if (num == UNW_REG_IP) { + IP = value; + return; + } + + switch (num) + { + case (UNW_ARM64_X0): + pX0 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X1): + pX1 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X2): + pX2 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X3): + pX3 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X4): + pX4 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X5): + pX5 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X6): + pX6 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X7): + pX7 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X8): + pX8 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X9): + pX9 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X10): + pX10 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X11): + pX11 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X12): + pX12 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X13): + pX13 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X14): + pX14 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X15): + pX15 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X16): + pX16 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X17): + pX17 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X18): + pX18 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X19): + pX19 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X20): + pX20 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X21): + pX21 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X22): + pX22 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X23): + pX23 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X24): + pX24 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X25): + pX25 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X26): + pX26 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X27): + pX27 = (PTR_UIntNative)location; + break; + case (UNW_ARM64_X28): + pX28 = (PTR_UIntNative)location; + break; + default: + PORTABILITY_ASSERT("unsupported arm64 register"); + } +} + +libunwind::v128 Registers_REGDISPLAY::getVectorRegister(int num) const +{ + num -= UNW_ARM64_D8; + + if (num < 0 || num >= sizeof(D) / sizeof(uint64_t)) + { + PORTABILITY_ASSERT("unsupported arm64 vector register"); + } + + libunwind::v128 result; + + result.vec[0] = 0; + result.vec[1] = 0; + result.vec[2] = D[num] >> 32; + result.vec[3] = D[num] & 0xFFFFFFFF; + + return result; +} + +void Registers_REGDISPLAY::setVectorRegister(int num, libunwind::v128 value) +{ + num -= UNW_ARM64_D8; + + if (num < 0 || num >= sizeof(D) / sizeof(uint64_t)) + { + PORTABILITY_ASSERT("unsupported arm64 vector register"); + } + + D[num] = (uint64_t)value.vec[2] << 32 | (uint64_t)value.vec[3]; +} + +#endif // TARGET_ARM64 + +bool DoTheStep(uintptr_t pc, UnwindInfoSections uwInfoSections, REGDISPLAY *regs) +{ +#if defined(TARGET_AMD64) + libunwind::UnwindCursor uc(_addressSpace); +#elif defined(TARGET_ARM) + libunwind::UnwindCursor uc(_addressSpace, regs); +#elif defined(TARGET_ARM64) + libunwind::UnwindCursor uc(_addressSpace, regs); +#elif defined(HOST_X86) + libunwind::UnwindCursor uc(_addressSpace, regs); +#else + #error "Unwinding is not implemented for this architecture yet." +#endif + +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND + bool retVal = uc.getInfoFromDwarfSection(pc, uwInfoSections, 0 /* fdeSectionOffsetHint */); + if (!retVal) + { + return false; + } + + unw_proc_info_t procInfo; + uc.getInfo(&procInfo); + +#if defined(TARGET_ARM) + DwarfInstructions dwarfInst; + int stepRet = dwarfInst.stepWithDwarf(_addressSpace, pc, procInfo.unwind_info, *(Registers_arm_rt*)regs); +#else + DwarfInstructions dwarfInst; + int stepRet = dwarfInst.stepWithDwarf(_addressSpace, pc, procInfo.unwind_info, *(Registers_REGDISPLAY*)regs); +#endif + + if (stepRet != UNW_STEP_SUCCESS) + { + return false; + } + +#if !defined(TARGET_ARM64) + regs->pIP = PTR_PCODE(regs->SP - sizeof(TADDR)); +#endif + +#elif defined(_LIBUNWIND_ARM_EHABI) + uc.setInfoBasedOnIPRegister(true); + int stepRet = uc.step(); + if ((stepRet != UNW_STEP_SUCCESS) && (stepRet != UNW_STEP_END)) + { + return false; + } +#endif + + return true; +} + +bool UnwindHelpers::StepFrame(REGDISPLAY *regs) +{ + UnwindInfoSections uwInfoSections; +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND + uintptr_t pc = regs->GetIP(); + if (!_addressSpace.findUnwindSections(pc, uwInfoSections)) + { + return false; + } + return DoTheStep(pc, uwInfoSections, regs); +#elif defined(_LIBUNWIND_ARM_EHABI) + // unwind section is located later for ARM + // pc will be taked from regs parameter + return DoTheStep(0, uwInfoSections, regs); +#else + PORTABILITY_ASSERT("StepFrame"); +#endif +} diff --git a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.h b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.h new file mode 100644 index 00000000000000..c30be78ddba18b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.h @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" + +// This class is used to encapsulate the internals of our unwinding implementation +// and any custom versions of libunwind structures that we use for performance +// reasons. +class UnwindHelpers +{ +public: + static bool StepFrame(REGDISPLAY *regs); +}; diff --git a/src/coreclr/nativeaot/Runtime/unix/cgroup.cpp b/src/coreclr/nativeaot/Runtime/unix/cgroup.cpp new file mode 100644 index 00000000000000..fc7b25bb44cd45 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/cgroup.cpp @@ -0,0 +1,515 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*++ + +Module Name: + + cgroup.cpp + +Abstract: + Read cpu limits for the current process +--*/ +#ifdef __FreeBSD__ +#define _WITH_GETLINE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#include +#else +#include +#endif +#include +#include + +#include "config.gc.h" + +#include "cgroupcpu.h" + +#define CGROUP2_SUPER_MAGIC 0x63677270 +#define TMPFS_MAGIC 0x01021994 + +#define BASE_TEN 10 + +#define PROC_MOUNTINFO_FILENAME "/proc/self/mountinfo" +#define PROC_CGROUP_FILENAME "/proc/self/cgroup" +#define CGROUP1_CFS_QUOTA_FILENAME "/cpu.cfs_quota_us" +#define CGROUP1_CFS_PERIOD_FILENAME "/cpu.cfs_period_us" +#define CGROUP2_CPU_MAX_FILENAME "/cpu.max" + +// the cgroup version number or 0 to indicate cgroups are not found or not enabled +static int s_cgroup_version = 0; +static char *s_cpu_cgroup_path = nullptr; + +class CGroup +{ +public: + static void Initialize() + { + s_cgroup_version = FindCGroupVersion(); + s_cpu_cgroup_path = FindCGroupPath(s_cgroup_version == 1 ? &IsCGroup1CpuSubsystem : nullptr); + } + + static void Cleanup() + { + free(s_cpu_cgroup_path); + } + + static bool GetCpuLimit(uint32_t *val) + { + if (s_cgroup_version == 0) + return false; + else if (s_cgroup_version == 1) + return GetCGroup1CpuLimit(val); + else if (s_cgroup_version == 2) + return GetCGroup2CpuLimit(val); + else + { + assert(!"Unknown cgroup version."); + return false; + } + } + +private: + static int FindCGroupVersion() + { + // It is possible to have both cgroup v1 and v2 enabled on a system. + // Most non-bleeding-edge Linux distributions fall in this group. We + // look at the file system type of /sys/fs/cgroup to determine which + // one is the default. For more details, see: + // https://systemd.io/CGROUP_DELEGATION/#three-different-tree-setups- + // We dont care about the difference between the "legacy" and "hybrid" + // modes because both of those involve cgroup v1 controllers managing + // resources. + +#if !HAVE_NON_LEGACY_STATFS + return 0; +#else + + struct statfs stats; + int result = statfs("/sys/fs/cgroup", &stats); + if (result != 0) + return 0; + + switch (stats.f_type) + { + case TMPFS_MAGIC: return 1; + case CGROUP2_SUPER_MAGIC: return 2; + default: + assert(!"Unexpected file system type for /sys/fs/cgroup"); + return 0; + } +#endif + } + + static bool IsCGroup1CpuSubsystem(const char *strTok){ + return strcmp("cpu", strTok) == 0; + } + + static char* FindCGroupPath(bool (*is_subsystem)(const char *)){ + char *cgroup_path = nullptr; + char *hierarchy_mount = nullptr; + char *hierarchy_root = nullptr; + char *cgroup_path_relative_to_mount = nullptr; + size_t common_path_prefix_len; + + FindHierarchyMount(is_subsystem, &hierarchy_mount, &hierarchy_root); + if (hierarchy_mount == nullptr || hierarchy_root == nullptr) + goto done; + + cgroup_path_relative_to_mount = FindCGroupPathForSubsystem(is_subsystem); + if (cgroup_path_relative_to_mount == nullptr) + goto done; + + cgroup_path = (char*)malloc(strlen(hierarchy_mount) + strlen(cgroup_path_relative_to_mount) + 1); + if (cgroup_path == nullptr) + goto done; + + strcpy(cgroup_path, hierarchy_mount); + // For a host cgroup, we need to append the relative path. + // The root and cgroup path can share a common prefix of the path that should not be appended. + // Example 1 (docker): + // hierarchy_mount: /sys/fs/cgroup/cpu + // hierarchy_root: /docker/87ee2de57e51bc75175a4d2e81b71d162811b179d549d6601ed70b58cad83578 + // cgroup_path_relative_to_mount: /docker/87ee2de57e51bc75175a4d2e81b71d162811b179d549d6601ed70b58cad83578/my_named_cgroup + // append do the cgroup_path: /my_named_cgroup + // final cgroup_path: /sys/fs/cgroup/cpu/my_named_cgroup + // + // Example 2 (out of docker) + // hierarchy_mount: /sys/fs/cgroup/cpu + // hierarchy_root: / + // cgroup_path_relative_to_mount: /my_named_cgroup + // append do the cgroup_path: /my_named_cgroup + // final cgroup_path: /sys/fs/cgroup/cpu/my_named_cgroup + common_path_prefix_len = strlen(hierarchy_root); + if ((common_path_prefix_len == 1) || strncmp(hierarchy_root, cgroup_path_relative_to_mount, common_path_prefix_len) != 0) + { + common_path_prefix_len = 0; + } + + assert((cgroup_path_relative_to_mount[common_path_prefix_len] == '/') || (cgroup_path_relative_to_mount[common_path_prefix_len] == '\0')); + + strcat(cgroup_path, cgroup_path_relative_to_mount + common_path_prefix_len); + + + done: + free(hierarchy_mount); + free(hierarchy_root); + free(cgroup_path_relative_to_mount); + return cgroup_path; + } + + static void FindHierarchyMount(bool (*is_subsystem)(const char *), char** pmountpath, char** pmountroot) + { + char *line = nullptr; + size_t lineLen = 0, maxLineLen = 0; + char *filesystemType = nullptr; + char *options = nullptr; + char *mountpath = nullptr; + char *mountroot = nullptr; + + FILE *mountinfofile = fopen(PROC_MOUNTINFO_FILENAME, "r"); + if (mountinfofile == nullptr) + goto done; + + while (getline(&line, &lineLen, mountinfofile) != -1) + { + if (filesystemType == nullptr || lineLen > maxLineLen) + { + free(filesystemType); + filesystemType = nullptr; + free(options); + options = nullptr; + filesystemType = (char*)malloc(lineLen+1); + if (filesystemType == nullptr) + goto done; + options = (char*)malloc(lineLen+1); + if (options == nullptr) + goto done; + maxLineLen = lineLen; + } + + char* separatorChar = strstr(line, " - "); + + // See man page of proc to get format for /proc/self/mountinfo file + int sscanfRet = sscanf(separatorChar, + " - %s %*s %s", + filesystemType, + options); + if (sscanfRet != 2) + { + assert(!"Failed to parse mount info file contents with sscanf."); + goto done; + } + + if (strncmp(filesystemType, "cgroup", 6) == 0) + { + bool isSubsystemMatch = is_subsystem == nullptr; + if (!isSubsystemMatch) + { + char* context = nullptr; + char* strTok = strtok_r(options, ",", &context); + while (!isSubsystemMatch && strTok != nullptr) + { + isSubsystemMatch = is_subsystem(strTok); + strTok = strtok_r(nullptr, ",", &context); + } + } + if (isSubsystemMatch) + { + mountpath = (char*)malloc(lineLen+1); + if (mountpath == nullptr) + goto done; + mountroot = (char*)malloc(lineLen+1); + if (mountroot == nullptr) + goto done; + + sscanfRet = sscanf(line, + "%*s %*s %*s %s %s ", + mountroot, + mountpath); + if (sscanfRet != 2) + assert(!"Failed to parse mount info file contents with sscanf."); + + // assign the output arguments and clear the locals so we don't free them. + *pmountpath = mountpath; + *pmountroot = mountroot; + mountpath = mountroot = nullptr; + } + } + } + done: + free(mountpath); + free(mountroot); + free(filesystemType); + free(options); + free(line); + if (mountinfofile) + fclose(mountinfofile); + } + + static char* FindCGroupPathForSubsystem(bool (*is_subsystem)(const char *)) + { + char *line = nullptr; + size_t lineLen = 0; + size_t maxLineLen = 0; + char *subsystem_list = nullptr; + char *cgroup_path = nullptr; + bool result = false; + + FILE *cgroupfile = fopen(PROC_CGROUP_FILENAME, "r"); + if (cgroupfile == nullptr) + goto done; + + while (!result && getline(&line, &lineLen, cgroupfile) != -1) + { + if (subsystem_list == nullptr || lineLen > maxLineLen) + { + free(subsystem_list); + subsystem_list = nullptr; + free(cgroup_path); + cgroup_path = nullptr; + subsystem_list = (char*)malloc(lineLen+1); + if (subsystem_list == nullptr) + goto done; + cgroup_path = (char*)malloc(lineLen+1); + if (cgroup_path == nullptr) + goto done; + maxLineLen = lineLen; + } + + if (s_cgroup_version == 1) + { + // See man page of proc to get format for /proc/self/cgroup file + int sscanfRet = sscanf(line, + "%*[^:]:%[^:]:%s", + subsystem_list, + cgroup_path); + if (sscanfRet != 2) + { + assert(!"Failed to parse cgroup info file contents with sscanf."); + goto done; + } + + char* context = nullptr; + char* strTok = strtok_r(subsystem_list, ",", &context); + while (strTok != nullptr) + { + if (is_subsystem(strTok)) + { + result = true; + break; + } + strTok = strtok_r(nullptr, ",", &context); + } + } + else if (s_cgroup_version == 2) + { + // See https://www.kernel.org/doc/Documentation/cgroup-v2.txt + // Look for a "0::/some/path" + int sscanfRet = sscanf(line, + "0::%s", + cgroup_path); + if (sscanfRet == 1) + { + result = true; + } + } + else + { + assert(!"Unknown cgroup version in mountinfo."); + goto done; + } + } + done: + free(subsystem_list); + if (!result) + { + free(cgroup_path); + cgroup_path = nullptr; + } + free(line); + if (cgroupfile) + fclose(cgroupfile); + return cgroup_path; + } + + static bool GetCGroup1CpuLimit(uint32_t *val) + { + long long quota; + long long period; + + quota = ReadCpuCGroupValue(CGROUP1_CFS_QUOTA_FILENAME); + if (quota <= 0) + return false; + + period = ReadCpuCGroupValue(CGROUP1_CFS_PERIOD_FILENAME); + if (period <= 0) + return false; + + ComputeCpuLimit(period, quota, val); + + return true; + } + + static bool GetCGroup2CpuLimit(uint32_t *val) + { + char *filename = nullptr; + FILE *file = nullptr; + char *endptr = nullptr; + char *max_quota_string = nullptr; + char *period_string = nullptr; + char *context = nullptr; + char *line = nullptr; + size_t lineLen = 0; + + long long quota = 0; + long long period = 0; + + bool result = false; + + if (s_cpu_cgroup_path == nullptr) + return false; + + if (asprintf(&filename, "%s%s", s_cpu_cgroup_path, CGROUP2_CPU_MAX_FILENAME) < 0) + return false; + + file = fopen(filename, "r"); + if (file == nullptr) + goto done; + + if (getline(&line, &lineLen, file) == -1) + goto done; + + // The expected format is: + // $MAX $PERIOD + // Where "$MAX" may be the string literal "max" + + max_quota_string = strtok_r(line, " ", &context); + if (max_quota_string == nullptr) + { + assert(!"Unable to parse " CGROUP2_CPU_MAX_FILENAME " file contents."); + goto done; + } + period_string = strtok_r(nullptr, " ", &context); + if (period_string == nullptr) + { + assert(!"Unable to parse " CGROUP2_CPU_MAX_FILENAME " file contents."); + goto done; + } + + // "max" means no cpu limit + if (strcmp("max", max_quota_string) == 0) + goto done; + + errno = 0; + quota = strtoll(max_quota_string, &endptr, BASE_TEN); + if (max_quota_string == endptr || errno != 0) + goto done; + + period = strtoll(period_string, &endptr, BASE_TEN); + if (period_string == endptr || errno != 0) + goto done; + + ComputeCpuLimit(period, quota, val); + result = true; + + done: + if (file) + fclose(file); + free(filename); + free(line); + + return result; + } + + static void ComputeCpuLimit(long long period, long long quota, uint32_t *val) + { + // Cannot have less than 1 CPU + if (quota <= period) + { + *val = 1; + return; + } + + // Calculate cpu count based on quota and round it up + double cpu_count = (double) quota / period + 0.999999999; + *val = (cpu_count < UINT32_MAX) ? (uint32_t)cpu_count : UINT32_MAX; + } + + static long long ReadCpuCGroupValue(const char* subsystemFilename){ + char *filename = nullptr; + bool result = false; + long long val; + + if (s_cpu_cgroup_path == nullptr) + return -1; + + if (asprintf(&filename, "%s%s", s_cpu_cgroup_path, subsystemFilename) < 0) + return -1; + + result = ReadLongLongValueFromFile(filename, &val); + free(filename); + if (!result) + return -1; + + return val; + } + + static bool ReadLongLongValueFromFile(const char* filename, long long* val) + { + bool result = false; + char *line = nullptr; + size_t lineLen = 0; + char *endptr = nullptr; + + if (val == nullptr) + return false; + + FILE* file = fopen(filename, "r"); + if (file == nullptr) + goto done; + + if (getline(&line, &lineLen, file) == -1) + goto done; + + errno = 0; + *val = strtoll(line, &endptr, BASE_TEN); + if (line == endptr || errno != 0) + goto done; + + result = true; + done: + if (file) + fclose(file); + free(line); + return result; + } +}; + +void InitializeCpuCGroup() +{ + CGroup::Initialize(); +} + +void CleanupCpuCGroup() +{ + CGroup::Cleanup(); +} + +bool GetCpuLimit(uint32_t* val) +{ + if (val == nullptr) + return false; + + return CGroup::GetCpuLimit(val); +} diff --git a/src/coreclr/nativeaot/Runtime/unix/cgroupcpu.h b/src/coreclr/nativeaot/Runtime/unix/cgroupcpu.h new file mode 100644 index 00000000000000..52830b7d3842db --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/cgroupcpu.h @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +#ifndef __CGROUPCPU_H__ +#define __CGROUPCPU_H__ + +void InitializeCpuCGroup(); +void CleanupCpuCGroup(); + +#endif // __CGROUPCPU_H__ + diff --git a/src/coreclr/nativeaot/Runtime/unix/config.h.in b/src/coreclr/nativeaot/Runtime/unix/config.h.in new file mode 100644 index 00000000000000..2ea62004428ceb --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/config.h.in @@ -0,0 +1,30 @@ +#ifndef _PAL_CONFIG_H_INCLUDED +#define _PAL_CONFIG_H_INCLUDED 1 + +#cmakedefine01 HAVE_SYS_VMPARAM_H +#cmakedefine01 HAVE_MACH_VM_TYPES_H +#cmakedefine01 HAVE_MACH_VM_PARAM_H + +#cmakedefine01 HAVE_PTHREAD_ATTR_GET_NP +#cmakedefine01 HAVE_PTHREAD_GETATTR_NP +#cmakedefine01 HAVE_PTHREAD_CONDATTR_SETCLOCK +#cmakedefine01 HAVE_PTHREAD_GETTHREADID_NP + +#cmakedefine01 HAVE_CLOCK_NANOSLEEP + +#cmakedefine01 HAVE_GREGSET_T +#cmakedefine01 HAVE___GREGSET_T + +#cmakedefine01 HAVE_SIGINFO_T +#cmakedefine01 HAVE_UCONTEXT_T + +#cmakedefine01 HAVE_LWP_SELF +#cmakedefine01 HAVE_SCHED_GETCPU +#cmakedefine01 HAVE_CLOCK_MONOTONIC +#cmakedefine01 HAVE_CLOCK_MONOTONIC_COARSE +#cmakedefine01 HAVE_CLOCK_GETTIME_NSEC_NP +#cmakedefine01 HAVE_SCHED_GETAFFINITY + +#cmakedefine01 HAVE_THREAD_LOCAL + +#endif diff --git a/src/coreclr/nativeaot/Runtime/unix/configure.cmake b/src/coreclr/nativeaot/Runtime/unix/configure.cmake new file mode 100644 index 00000000000000..fda52b933b4c46 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/configure.cmake @@ -0,0 +1,115 @@ +include(CheckCXXSourceCompiles) +include(CheckCXXSourceRuns) +include(CheckCXXSymbolExists) +include(CheckFunctionExists) +include(CheckIncludeFiles) +include(CheckStructHasMember) +include(CheckTypeSize) +include(CheckLibraryExists) + +if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) + set(CMAKE_REQUIRED_INCLUDES /usr/local/include) +elseif(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + set(CMAKE_REQUIRED_DEFINITIONS "-D_BSD_SOURCE -D_SVID_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L") +endif() + +list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_FILE_OFFSET_BITS=64) + +check_include_files(sys/vmparam.h HAVE_SYS_VMPARAM_H) +check_include_files(mach/vm_types.h HAVE_MACH_VM_TYPES_H) +check_include_files(mach/vm_param.h HAVE_MACH_VM_PARAM_H) + +check_library_exists(pthread pthread_create "" HAVE_LIBPTHREAD) +check_library_exists(c pthread_create "" HAVE_PTHREAD_IN_LIBC) + +if (HAVE_LIBPTHREAD) + set(PTHREAD_LIBRARY pthread) +elseif (HAVE_PTHREAD_IN_LIBC) + set(PTHREAD_LIBRARY c) +endif() + +check_library_exists(${PTHREAD_LIBRARY} pthread_attr_get_np "" HAVE_PTHREAD_ATTR_GET_NP) +check_library_exists(${PTHREAD_LIBRARY} pthread_getattr_np "" HAVE_PTHREAD_GETATTR_NP) +check_library_exists(${PTHREAD_LIBRARY} pthread_condattr_setclock "" HAVE_PTHREAD_CONDATTR_SETCLOCK) +check_library_exists(${PTHREAD_LIBRARY} pthread_getthreadid_np "" HAVE_PTHREAD_GETTHREADID_NP) + +check_function_exists(clock_nanosleep HAVE_CLOCK_NANOSLEEP) + +check_struct_has_member ("ucontext_t" uc_mcontext.gregs[0] ucontext.h HAVE_GREGSET_T) +check_struct_has_member ("ucontext_t" uc_mcontext.__gregs[0] ucontext.h HAVE___GREGSET_T) + +set(CMAKE_EXTRA_INCLUDE_FILES) +set(CMAKE_EXTRA_INCLUDE_FILES signal.h) +check_type_size(siginfo_t SIGINFO_T) +set(CMAKE_EXTRA_INCLUDE_FILES) +set(CMAKE_EXTRA_INCLUDE_FILES ucontext.h) +check_type_size(ucontext_t UCONTEXT_T) + +check_cxx_source_compiles(" +#include + +int main(int argc, char **argv) +{ + return (int)_lwp_self(); +}" HAVE_LWP_SELF) + +set(CMAKE_REQUIRED_LIBRARIES ${PTHREAD_LIBRARY}) +check_cxx_source_runs(" +#include +#include + +int main(void) +{ + if (sched_getcpu() >= 0) + { + exit(0); + } + exit(1); +}" HAVE_SCHED_GETCPU) +set(CMAKE_REQUIRED_LIBRARIES) + +check_cxx_source_runs(" +#include +#include +#include + +int main() +{ + int ret; + struct timespec ts; + ret = clock_gettime(CLOCK_MONOTONIC, &ts); + + exit(ret); +}" HAVE_CLOCK_MONOTONIC) + +check_cxx_source_runs(" +#include +#include +#include + +int main() +{ + int ret; + struct timespec ts; + ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); + + exit(ret); +}" HAVE_CLOCK_MONOTONIC_COARSE) + +check_symbol_exists( + clock_gettime_nsec_np + time.h + HAVE_CLOCK_GETTIME_NSEC_NP) + +check_library_exists(c sched_getaffinity "" HAVE_SCHED_GETAFFINITY) + +check_cxx_source_compiles(" +thread_local int x; + +int main(int argc, char **argv) +{ + x = 1; + return 0; +}" HAVE_THREAD_LOCAL) + +configure_file(${CMAKE_CURRENT_LIST_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacros.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacros.inc new file mode 100644 index 00000000000000..ef6d393fd248b1 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacros.inc @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#define INVALIDGCVALUE 0xCCCCCCCD + +#if defined(__APPLE__) +#define C_FUNC(name) _##name +#define EXTERNAL_C_FUNC(name) C_FUNC(name) +#define LOCAL_LABEL(name) L##name +#else +#define C_FUNC(name) name +#define EXTERNAL_C_FUNC(name) C_FUNC(name)@plt +#define LOCAL_LABEL(name) .L##name +#endif + +#if defined(__APPLE__) +#define C_PLTFUNC(name) _##name +#else +#define C_PLTFUNC(name) name@PLT +#endif + +.macro END_PROLOGUE +.endm + +.macro SETALIAS New, Old + .equiv \New, \Old +.endm + +#if defined(HOST_AMD64) +#include "unixasmmacrosamd64.inc" +#elif defined(HOST_ARM) +#include "unixasmmacrosarm.inc" +#elif defined(HOST_ARM64) +#include "unixasmmacrosarm64.inc" +#elif defined(HOST_X86) +#include "unixasmmacrosx86.inc" +#endif diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc new file mode 100644 index 00000000000000..47e0e386df5246 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc @@ -0,0 +1,351 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#define C_VAR(Name) rip + C_FUNC(Name) + +.macro NESTED_ENTRY Name, Section, Handler + LEAF_ENTRY \Name, \Section + .ifnc \Handler, NoHandler +#if defined(__APPLE__) + .cfi_personality 0x9b, C_FUNC(\Handler) // 0x9b == DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4 +#else + .cfi_personality 0, C_FUNC(\Handler) // 0 == DW_EH_PE_absptr +#endif + .endif +.endm + +.macro NESTED_END Name, Section + LEAF_END \Name, \Section +#if defined(__APPLE__) + .set LOCAL_LABEL(\Name\()_Size), . - C_FUNC(\Name) + .section __LD,__compact_unwind,regular,debug + .quad C_FUNC(\Name) + .long LOCAL_LABEL(\Name\()_Size) + .long 0x04000000 # DWARF + .quad 0 + .quad 0 +#endif +.endm + +.macro PATCH_LABEL Name + .global C_FUNC(\Name) +C_FUNC(\Name): +.endm + +.macro ALTERNATE_ENTRY Name + .global C_FUNC(\Name) +C_FUNC(\Name): +.endm + +.macro LEAF_ENTRY Name, Section + .global C_FUNC(\Name) +#if defined(__APPLE__) + .text +#else + .global C_FUNC(_\Name) + .type \Name, %function +#endif +C_FUNC(\Name): + .cfi_startproc +.endm + +.macro LEAF_END Name, Section +#if !defined(__APPLE__) + .size \Name, .-\Name +#endif + .cfi_endproc +.endm + +.macro push_nonvol_reg Register + push \Register + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset \Register, 0 +.endm + +.macro pop_nonvol_reg Register + pop \Register + .cfi_adjust_cfa_offset -8 + .cfi_restore \Register +.endm + +.macro alloc_stack Size +.att_syntax + lea -(\Size)(%rsp), %rsp +.intel_syntax noprefix + .cfi_adjust_cfa_offset (\Size) +.endm + +.macro free_stack Size +.att_syntax + lea (\Size)(%rsp), %rsp +.intel_syntax noprefix + .cfi_adjust_cfa_offset -(\Size) +.endm + +.macro set_cfa_register Reg, Offset + .cfi_def_cfa_register \Reg + .cfi_def_cfa_offset \Offset +.endm + +.macro save_reg_postrsp Reg, Offset + __Offset = \Offset + mov qword ptr [rsp + __Offset], \Reg + .cfi_rel_offset \Reg, __Offset +.endm + +.macro restore_reg Reg, Offset + __Offset = \Offset + mov \Reg, [rsp + __Offset] + .cfi_restore \Reg +.endm + +.macro save_xmm128_postrsp Reg, Offset + __Offset = \Offset + movdqa xmmword ptr [rsp + __Offset], \Reg + // NOTE: We cannot use ".cfi_rel_offset \Reg, __Offset" here, + // the xmm registers are not supported by the libunwind +.endm + +.macro restore_xmm128 Reg, ofs + __Offset = \ofs + movdqa \Reg, xmmword ptr [rsp + __Offset] + // NOTE: We cannot use ".cfi_restore \Reg" here, + // the xmm registers are not supported by the libunwind + +.endm + +.macro RESET_FRAME_WITH_RBP + + mov rsp, rbp + set_cfa_register rsp, 16 + pop_nonvol_reg rbp + .cfi_same_value rbp + +.endm + +.macro PUSH_CALLEE_SAVED_REGISTERS + + push_register rbp + push_register rbx + push_register r15 + push_register r14 + push_register r13 + push_register r12 + +.endm + +.macro POP_CALLEE_SAVED_REGISTERS + + pop_nonvol_reg r12 + pop_nonvol_reg r13 + pop_nonvol_reg r14 + pop_nonvol_reg r15 + pop_nonvol_reg rbx + pop_nonvol_reg rbp + +.endm + +.macro push_register Reg + push \Reg + .cfi_adjust_cfa_offset 8 +.endm + +.macro push_imm imm +.att_syntax + push $\imm +.intel_syntax noprefix + .cfi_adjust_cfa_offset 8 +.endm + +.macro push_eflags + pushfq + .cfi_adjust_cfa_offset 8 +.endm + +.macro push_argument_register Reg + push_register \Reg +.endm + +.macro PUSH_ARGUMENT_REGISTERS + + push_argument_register r9 + push_argument_register r8 + push_argument_register rcx + push_argument_register rdx + push_argument_register rsi + push_argument_register rdi + +.endm + +.macro pop_register Reg + pop \Reg + .cfi_adjust_cfa_offset -8 +.endm + +.macro pop_eflags + popfq + .cfi_adjust_cfa_offset -8 +.endm + +.macro pop_argument_register Reg + pop_register \Reg +.endm + +.macro POP_ARGUMENT_REGISTERS + + pop_argument_register rdi + pop_argument_register rsi + pop_argument_register rdx + pop_argument_register rcx + pop_argument_register r8 + pop_argument_register r9 + +.endm + +#define SIZEOF_FP_REGS 0x80 + +.macro SAVE_FLOAT_ARGUMENT_REGISTERS ofs + + save_xmm128_postrsp xmm0, \ofs + save_xmm128_postrsp xmm1, \ofs + 0x10 + save_xmm128_postrsp xmm2, \ofs + 0x20 + save_xmm128_postrsp xmm3, \ofs + 0x30 + save_xmm128_postrsp xmm4, \ofs + 0x40 + save_xmm128_postrsp xmm5, \ofs + 0x50 + save_xmm128_postrsp xmm6, \ofs + 0x60 + save_xmm128_postrsp xmm7, \ofs + 0x70 + +.endm + +.macro RESTORE_FLOAT_ARGUMENT_REGISTERS ofs + + restore_xmm128 xmm0, \ofs + restore_xmm128 xmm1, \ofs + 0x10 + restore_xmm128 xmm2, \ofs + 0x20 + restore_xmm128 xmm3, \ofs + 0x30 + restore_xmm128 xmm4, \ofs + 0x40 + restore_xmm128 xmm5, \ofs + 0x50 + restore_xmm128 xmm6, \ofs + 0x60 + restore_xmm128 xmm7, \ofs + 0x70 + +.endm + +.macro EXPORT_POINTER_TO_ADDRESS Name + +// NOTE: The label is intentionally left as 2 - otherwise on OSX 0b or 1b will be incorrectly interpreted as binary integers + +2: + + .data + .align 8 +C_FUNC(\Name): + .quad 2b + .global C_FUNC(\Name) + .text + +.endm + +// +// CONSTANTS -- INTEGER +// +#define TSF_Attached 0x01 +#define TSF_SuppressGcStress 0x08 +#define TSF_DoNotTriggerGc 0x10 + +// +// Rename fields of nested structs +// +#define OFFSETOF__Thread__m_alloc_context__alloc_ptr OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr +#define OFFSETOF__Thread__m_alloc_context__alloc_limit OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit + +// GC type flags +#define GC_ALLOC_FINALIZE 1 + +// Note: these must match the defs in PInvokeTransitionFrameFlags +#define PTFF_SAVE_RBX 0x00000001 +#define PTFF_SAVE_R12 0x00000010 +#define PTFF_SAVE_R13 0x00000020 +#define PTFF_SAVE_R14 0x00000040 +#define PTFF_SAVE_R15 0x00000080 +#define PTFF_SAVE_ALL_PRESERVED 0x000000F1 // NOTE: RBP is not included in this set! +#define PTFF_SAVE_RSP 0x00008000 +#define PTFF_SAVE_RAX 0x00000100 // RAX is saved if it contains a GC ref and we're in hijack handler +#define PTFF_SAVE_ALL_SCRATCH 0x00007F00 +#define PTFF_RAX_IS_GCREF 0x00010000 // iff PTFF_SAVE_RAX: set -> eax is Object, clear -> eax is scalar +#define PTFF_RAX_IS_BYREF 0x00020000 // iff PTFF_SAVE_RAX: set -> eax is ByRef, clear -> eax is Object or scalar +#define PTFF_THREAD_ABORT 0x00040000 // indicates that ThreadAbortException should be thrown when returning from the transition + +// These must match the TrapThreadsFlags enum +#define TrapThreadsFlags_None 0 +#define TrapThreadsFlags_AbortInProgress 1 +#define TrapThreadsFlags_TrapThreads 2 + +.macro INLINE_GET_TLS_VAR Var + .att_syntax +#if defined(__APPLE__) + movq _\Var@TLVP(%rip), %rdi + callq *(%rdi) +#else + leaq \Var@TLSLD(%rip), %rdi + callq __tls_get_addr@PLT + addq $\Var@DTPOFF, %rax +#endif + .intel_syntax noprefix +.endm + + +.macro INLINE_GETTHREAD + // Inlined version of call C_FUNC(RhpGetThread) + INLINE_GET_TLS_VAR tls_CurrentThread +.endm + +.macro INLINE_THREAD_UNHIJACK threadReg, trashReg1, trashReg2 + // + // Thread::Unhijack() + // + mov \trashReg1, [\threadReg + OFFSETOF__Thread__m_pvHijackedReturnAddress] + cmp \trashReg1, 0 + je 1f + + mov \trashReg2, [\threadReg + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + mov [\trashReg2], \trashReg1 + mov qword ptr [\threadReg + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], 0 + mov qword ptr [\threadReg + OFFSETOF__Thread__m_pvHijackedReturnAddress], 0 + +1: +.endm + +DEFAULT_FRAME_SAVE_FLAGS = PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_RSP + +.macro PUSH_COOP_PINVOKE_FRAME trashReg + push_nonvol_reg rbp // push RBP frame + mov rbp, rsp + lea \trashReg, [rsp + 0x10] + push_register \trashReg // save caller's RSP + push_nonvol_reg r15 // save preserved registers + push_nonvol_reg r14 // .. + push_nonvol_reg r13 // .. + push_nonvol_reg r12 // .. + push_nonvol_reg rbx // .. + push_imm DEFAULT_FRAME_SAVE_FLAGS // save the register bitmask + push_register \trashReg // Thread * (unused by stackwalker) + mov \trashReg, [rsp + 8*8] // Find and save the callers RBP + push_register \trashReg + mov \trashReg, [rsp + 10*8] // Find and save the return address + push_register \trashReg + lea \trashReg, [rsp] // trashReg == address of frame +.endm + +.macro POP_COOP_PINVOKE_FRAME + pop_register r10 // discard RIP + pop_nonvol_reg rbp // restore RBP + pop_register r10 // discard thread + pop_register r10 // discard bitmask + pop_nonvol_reg rbx + pop_nonvol_reg r12 + pop_nonvol_reg r13 + pop_nonvol_reg r14 + pop_nonvol_reg r15 + pop_register r10 // discard caller RSP + pop_register r10 // discard RBP frame +.endm diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc new file mode 100644 index 00000000000000..95b0cad1681b5b --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc @@ -0,0 +1,297 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// CONSTANTS -- INTEGER +// + +// GC type flags +#define GC_ALLOC_FINALIZE 1 +#define GC_ALLOC_ALIGN8_BIAS 4 +#define GC_ALLOC_ALIGN8 8 + +#define TSF_Attached 0x01 +#define TSF_SuppressGcStress 0x08 +#define TSF_DoNotTriggerGc 0x10 + +#define PTFF_SAVE_ALL_PRESERVED 0x0000007F // NOTE: R11 is not included in this set! +#define PTFF_SAVE_SP 0x00000100 +#define DEFAULT_FRAME_SAVE_FLAGS (PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_SP) + +// These must match the TrapThreadsFlags enum +#define TrapThreadsFlags_None 0 +#define TrapThreadsFlags_AbortInProgress 1 +#define TrapThreadsFlags_TrapThreads 2 + +// Rename fields of nested structs +#define OFFSETOF__Thread__m_alloc_context__alloc_ptr (OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr) +#define OFFSETOF__Thread__m_alloc_context__alloc_limit (OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit) + +// GC minimal sized object. We use this to switch between 4 and 8 byte alignment in the GC heap (see AllocFast.asm). +#define SIZEOF__MinObject 12 + +// Maximum subsection number in .text section +#define MAX_NUMBER_SUBSECTION_TEXT 0x2000 + +.macro NESTED_ENTRY Name, Section, Handler + LEAF_ENTRY \Name, \Section + .ifnc \Handler, NoHandler + .personality C_FUNC(\Handler) + .endif +.endm + +.macro NESTED_END Name, Section + LEAF_END \Name, \Section +.endm + +.macro PATCH_LABEL Name + .thumb_func + .global C_FUNC(\Name) +C_FUNC(\Name): +.endm + +.macro ALTERNATE_ENTRY Name + .global C_FUNC(\Name) +C_FUNC(\Name): +.endm + +.macro LEAF_ENTRY Name, Section + .thumb_func + .global C_FUNC(\Name) + .type \Name, %function +C_FUNC(\Name): + .fnstart +.endm + +.macro LEAF_END Name, Section + .size \Name, .-\Name + .fnend +.endm + +.macro PREPARE_EXTERNAL_VAR Name, HelperReg + ldr \HelperReg, [pc, #C_FUNC(\Name)@GOTPCREL] +.endm + +.macro push_nonvol_reg Register + push \Register + .save \Register +.endm + +.macro pop_nonvol_reg Register + pop \Register +.endm + +.macro vpush_nonvol_reg Register + vpush \Register + .vsave \Register +.endm + +.macro vpop_nonvol_reg Register + vpop \Register +.endm + +.macro alloc_stack Size + sub sp, sp, (\Size) + .pad #(\Size) +.endm + +.macro free_stack Size + add sp, sp, (\Size) + .pad #-(\Size) +.endm + +.macro POP_CALLEE_SAVED_REGISTERS + pop_nonvol_reg "{r4-r11, lr}" +.endm + +.macro PUSH_CALLEE_SAVED_REGISTERS + push_nonvol_reg "{r4-r11, lr}" +.endm + +.macro push_register Reg + push \Reg +.endm + +.macro push_argument_register Reg + push_register \Reg +.endm + +.macro PUSH_ARGUMENT_REGISTERS + push {r0-r3} +.endm + +.macro pop_register Reg + pop \Reg +.endm + +.macro pop_argument_register Reg + pop_register \Reg +.endm + +.macro POP_ARGUMENT_REGISTERS + pop {r0-r3} +.endm + +.macro EMIT_BREAKPOINT + .inst.w 0xde01 +.endm + +.macro PROLOG_PUSH RegList + push_nonvol_reg "\RegList" +.endm + +.macro PROLOG_VPUSH RegList + vpush_nonvol_reg "\RegList" +.endm + +.macro PROLOG_STACK_SAVE Register + .setfp \Register, sp + mov \Register, sp +.endm + +.macro EPILOG_STACK_RESTORE Register + mov sp, \Register +.endm + +.macro EPILOG_POP RegList + pop_nonvol_reg "\RegList" +.endm + +.macro EPILOG_VPOP RegList + vpop_nonvol_reg "\RegList" +.endm + +.macro PROLOG_STACK_ALLOC Size + sub sp, sp, #\Size + .pad #\Size +.endm + +.macro EPILOG_STACK_FREE Size + add sp, sp, #\Size + .pad #-\Size +.endm + +//----------------------------------------------------------------------------- +// Macro used to check (in debug builds only) whether the stack is 64-bit aligned (a requirement before calling +// out into C++/OS code). Invoke this directly after your prolog (if the stack frame size is fixed) or directly +// before a call (if you have a frame pointer and a dynamic stack). A breakpoint will be invoked if the stack +// is misaligned. +// +.macro CHECK_STACK_ALIGNMENT + +#ifdef _DEBUG + push {r0} + add r0, sp, #4 + tst r0, #7 + pop {r0} + beq 0f + EMIT_BREAKPOINT +0: +#endif +.endm + +// Loads a 32bit constant into destination register +.macro MOV32 DestReg, Constant + movw \DestReg, #((\Constant) & 0xFFFF) + movt \DestReg, #((\Constant) >> 16) +.endm + +.macro EXPORT_POINTER_TO_ADDRESS Name + +1: + + .data + .align 4 +C_FUNC(\Name): + .word 1b + 1 // Add 1 to indicate thumb code + .global C_FUNC(\Name) + .text + +.endm + +// +// Macro used from unmanaged helpers called from managed code where the helper does not transition immediately +// into pre-emptive mode but may cause a GC and thus requires the stack is crawlable. This is typically the +// case for helpers that meddle in GC state (e.g. allocation helpers) where the code must remain in +// cooperative mode since it handles object references and internal GC state directly but a garbage collection +// may be inevitable. In these cases we need to be able to transition to pre-meptive mode deep within the +// unmanaged code but still be able to initialize the stack iterator at the first stack frame which may hold +// interesting GC references. In all our helper cases this corresponds to the most recent managed frame (e.g. +// the helper's caller). +// +// This macro builds a frame describing the current state of managed code. +// +// INVARIANTS +// - The macro assumes it defines the method prolog, it should typically be the first code in a method and +// certainly appear before any attempt to alter the stack pointer. +// - This macro uses trashReg (after its initial value has been saved in the frame) and upon exit trashReg +// will contain the address of transition frame. +// +.macro PUSH_COOP_PINVOKE_FRAME trashReg + + PROLOG_STACK_ALLOC 4 // Save space for caller's SP + PROLOG_PUSH "{r4-r10}" // Save preserved registers + PROLOG_STACK_ALLOC 8 // Save space for flags and Thread* + PROLOG_PUSH "{r11}" // Save caller's FP + PROLOG_PUSH "{r11,lr}" // Save caller's frame-chain pointer and PC + + // Compute SP value at entry to this method and save it in the last slot of the frame (slot #12). + add \trashReg, sp, #(13 * 4) + str \trashReg, [sp, #(12 * 4)] + + // Record the bitmask of saved registers in the frame (slot #4). + mov \trashReg, #DEFAULT_FRAME_SAVE_FLAGS + str \trashReg, [sp, #(4 * 4)] + + mov \trashReg, sp +.endm + +// Pop the frame and restore register state preserved by PUSH_COOP_PINVOKE_FRAME +.macro POP_COOP_PINVOKE_FRAME + EPILOG_POP "{r11,lr}" // Restore caller's frame-chain pointer and PC (return address) + EPILOG_POP "{r11}" // Restore caller's FP + EPILOG_STACK_FREE 8 // Discard flags and Thread* + EPILOG_POP "{r4-r10}" // Restore preserved registers + EPILOG_STACK_FREE 4 // Discard caller's SP +.endm + +// thumb with PIC version +.macro INLINE_GET_TLS_VAR Var + ldr r0, 2f +1: + add r0, pc, r0 + bl __tls_get_addr(PLT) + // push data at the end of text section + .pushsection .text, MAX_NUMBER_SUBSECTION_TEXT, "aM", %progbits, 4 + .balign 4 +2: + .4byte \Var(TLSGD) + (. - 1b - 4) + .popsection +.endm + +.macro INLINE_GETTHREAD + // Inlined version of call C_FUNC(RhpGetThread) + INLINE_GET_TLS_VAR tls_CurrentThread +.endm + +.macro INLINE_THREAD_UNHIJACK threadReg, trashReg1, trashReg2 + // + // Thread::Unhijack() + // + ldr \trashReg1, [\threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + cbz \trashReg1, 1f + + ldr \trashReg2, [\threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str \trashReg1, [\trashReg2] + mov \trashReg1, #0 + str \trashReg1, [\threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str \trashReg1, [\threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + +1: +.endm + +.macro EPILOG_BRANCH_REG reg + + bx \reg + +.endm diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc new file mode 100644 index 00000000000000..bd0a64bd420dec --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc @@ -0,0 +1,359 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "AsmOffsets.inc" + +.macro NESTED_ENTRY Name, Section, Handler + LEAF_ENTRY \Name, \Section + .ifnc \Handler, NoHandler + .cfi_personality 0x1b, C_FUNC(\Handler) // 0x1b == DW_EH_PE_pcrel | DW_EH_PE_sdata4 + // @TODO: Check if we have systems that use indirect personality 0x9b == DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4 + .endif +.endm + +.macro NESTED_END Name, Section + LEAF_END \Name, \Section +.endm + +.macro PATCH_LABEL Name + .global C_FUNC(\Name) +C_FUNC(\Name): +.endm + +.macro ALTERNATE_ENTRY Name + .global C_FUNC(\Name) + .hidden C_FUNC(\Name) +C_FUNC(\Name): +.endm + +.macro LABELED_RETURN_ADDRESS Name + .global C_FUNC(\Name) + .hidden C_FUNC(\Name) +C_FUNC(\Name): +.endm + +.macro LEAF_ENTRY Name, Section + .global C_FUNC(\Name) + .hidden C_FUNC(\Name) + .type \Name, %function +C_FUNC(\Name): + .cfi_startproc +.endm + +.macro LEAF_END Name, Section + .size \Name, .-\Name + .cfi_endproc +.endm + +.macro PREPARE_EXTERNAL_VAR Name, HelperReg + adrp \HelperReg, C_FUNC(\Name) + add \HelperReg, \HelperReg, :lo12:C_FUNC(\Name) +.endm + +.macro PREPARE_EXTERNAL_VAR_INDIRECT Name, HelperReg + adrp \HelperReg, C_FUNC(\Name) + ldr \HelperReg, [\HelperReg, :lo12:C_FUNC(\Name)] +.endm + +.macro PREPARE_EXTERNAL_VAR_INDIRECT_W Name, HelperReg + adrp x\HelperReg, C_FUNC(\Name) + ldr w\HelperReg, [x\HelperReg, :lo12:C_FUNC(\Name)] +.endm + + +.macro PROLOG_STACK_ALLOC Size + sub sp, sp, \Size +.endm + +.macro EPILOG_STACK_FREE Size + add sp, sp, \Size + .cfi_adjust_cfa_offset -\Size +.endm + +.macro EPILOG_STACK_RESTORE + mov sp, fp + .cfi_restore sp +.endm + +.macro PROLOG_SAVE_REG reg, ofs + str \reg, [sp, \ofs] + .cfi_rel_offset \reg, \ofs +.endm + +.macro PROLOG_SAVE_REG_PAIR reg1, reg2, ofs + stp \reg1, \reg2, [sp, \ofs] + .cfi_rel_offset \reg1, \ofs + .cfi_rel_offset \reg2, \ofs + 8 + .ifc \reg1, fp + mov fp, sp + .cfi_def_cfa_register fp + .endif +.endm + +.macro PROLOG_SAVE_REG_PAIR_INDEXED reg1, reg2, ofs + stp \reg1, \reg2, [sp, \ofs]! + .cfi_adjust_cfa_offset -\ofs + .cfi_rel_offset \reg1, 0 + .cfi_rel_offset \reg2, 8 + .ifc \reg1, fp + mov fp, sp + .cfi_def_cfa_register fp + .endif +.endm + +.macro PROLOG_SAVE_REG_PAIR_NO_FP_INDEXED reg1, reg2, ofs + stp \reg1, \reg2, [sp, \ofs]! + .cfi_adjust_cfa_offset -\ofs + .cfi_rel_offset \reg1, 0 + .cfi_rel_offset \reg2, 8 +.endm + + +.macro EPILOG_RESTORE_REG reg, ofs + ldr \reg, [sp, \ofs] + .cfi_restore \reg +.endm + +.macro EPILOG_RESTORE_REG_PAIR reg1, reg2, ofs + ldp \reg1, \reg2, [sp, \ofs] + .cfi_restore \reg1 + .cfi_restore \reg2 +.endm + +.macro EPILOG_RESTORE_REG_PAIR_INDEXED reg1, reg2, ofs + ldp \reg1, \reg2, [sp], \ofs + .cfi_restore \reg1 + .cfi_restore \reg2 + .cfi_adjust_cfa_offset -\ofs +.endm + +.macro EPILOG_RETURN + ret +.endm + +.macro EMIT_BREAKPOINT + brk #0 +.endm + +//----------------------------------------------------------------------------- +// The Following sets of SAVE_*_REGISTERS expect the memory to be reserved and +// base address to be passed in $reg +// + +// Reserve 64 bytes of memory before calling SAVE_ARGUMENT_REGISTERS +.macro SAVE_ARGUMENT_REGISTERS reg, ofs + + stp x0, x1, [\reg, #(\ofs)] + stp x2, x3, [\reg, #(\ofs + 16)] + stp x4, x5, [\reg, #(\ofs + 32)] + stp x6, x7, [\reg, #(\ofs + 48)] + +.endm + +// Reserve 64 bytes of memory before calling SAVE_FLOAT_ARGUMENT_REGISTERS +.macro SAVE_FLOAT_ARGUMENT_REGISTERS reg, ofs + + stp d0, d1, [\reg, #(\ofs)] + stp d2, d3, [\reg, #(\ofs + 16)] + stp d4, d5, [\reg, #(\ofs + 32)] + stp d6, d7, [\reg, #(\ofs + 48)] + +.endm + +.macro RESTORE_ARGUMENT_REGISTERS reg, ofs + + ldp x0, x1, [\reg, #(\ofs)] + ldp x2, x3, [\reg, #(\ofs + 16)] + ldp x4, x5, [\reg, #(\ofs + 32)] + ldp x6, x7, [\reg, #(\ofs + 48)] + +.endm + +.macro RESTORE_FLOAT_ARGUMENT_REGISTERS reg, ofs + + ldp d0, d1, [\reg, #(\ofs)] + ldp d2, d3, [\reg, #(\ofs + 16)] + ldp d4, d5, [\reg, #(\ofs + 32)] + ldp d6, d7, [\reg, #(\ofs + 48)] + +.endm + +.macro EPILOG_BRANCH_REG reg + + br \reg + +.endm + +#define xip0 x16 +#define xip1 x17 +#define xpr x18 + +// Loads the address of a thread-local variable into the target register, +// which cannot be x0. Preserves all other registers. +.macro INLINE_GET_TLS_VAR target, var + .ifc \target, x0 + .error "target cannot be x0" + .endif + + stp x0, lr, [sp,#-0x10]! + + // This sequence of instructions is recognized and potentially patched + // by the linker (GD->IE/LE relaxation). + adrp x0, :tlsdesc:\var + ldr \target, [x0, #:tlsdesc_lo12:\var] + add x0, x0, :tlsdesc_lo12:\var +.tlsdesccall \var + blr \target + // End of the sequence + + mrs \target, tpidr_el0 + add \target, \target, x0 + ldp x0, lr, [sp],#0x10 +.endm + +// Inlined version of RhpGetThread. Target cannot be x0. +.macro INLINE_GETTHREAD target + INLINE_GET_TLS_VAR \target, tls_CurrentThread +.endm + +// Do not use these ETLS macros in functions that already create a stack frame. +// Creating two stack frames in one function can confuse the unwinder/debugger + +.macro GETTHREAD_ETLS_1 + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32 // ;; Push down stack pointer and store FP and LR + str x0, [sp, #0x10] + + bl RhpGetThread + mov x1, x0 + + ldr x0, [sp, #0x10] + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 +.endm + +.macro GETTHREAD_ETLS_3 + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -48 // ;; Push down stack pointer and store FP and LR + stp x0, x1, [sp, #0x10] + str x2, [sp, #0x20] + + bl RhpGetThread + mov x3, x0 + + ldp x0, x1, [sp, #0x10] + ldr x2, [sp, #0x20] + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 48 +.endm + +.macro GETTHUNKDATA_ETLS_9 + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -96 // ;; Push down stack pointer and store FP and LR + stp x0, x1, [sp, #0x10] + stp x2, x3, [sp, #0x20] + stp x4, x5, [sp, #0x30] + stp x6, x7, [sp, #0x40] + stp x8, xip0, [sp, #0x50] + + bl RhpGetThunkData + mov x9, x0 + + ldp x0, x1, [sp, #0x10] + ldp x2, x3, [sp, #0x20] + ldp x4, x5, [sp, #0x30] + ldp x6, x7, [sp, #0x40] + ldp x8, xip0, [sp, #0x50] + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 96 +.endm + +.macro ArmInterlockedOperationBarrier + dmb ish +.endm + +.macro INLINE_THREAD_UNHIJACK threadReg, trashReg1, trashReg2 + // + // Thread::Unhijack() + // + ldr \trashReg1, [\threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + cbz \trashReg1, 0f + + ldr \trashReg2, [\threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str \trashReg1, [\trashReg2] + str xzr, [\threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str xzr, [\threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] +0: +.endm + + +.macro EXPORT_POINTER_TO_ADDRESS Name + +1: + + .data + .align 8 +C_FUNC(\Name): + .quad 1b + .global C_FUNC(\Name) + .hidden C_FUNC(\Name) + .text +.endm + +// Note: these must match the defs in PInvokeTransitionFrameFlags +PTFF_SAVE_SP = 0x00000400 +PTFF_SAVE_ALL_PRESERVED = 0x000003FF // NOTE: x19-x28 + +DEFAULT_FRAME_SAVE_FLAGS = PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_SP + +.macro PUSH_COOP_PINVOKE_FRAME trashReg + + PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -0x80 // Push down stack pointer and store FP and LR + + // 0x10 bytes reserved for Thread* and flags + + // Save callee saved registers + PROLOG_SAVE_REG_PAIR x19, x20, 0x20 + PROLOG_SAVE_REG_PAIR x21, x22, 0x30 + PROLOG_SAVE_REG_PAIR x23, x24, 0x40 + PROLOG_SAVE_REG_PAIR x25, x26, 0x50 + PROLOG_SAVE_REG_PAIR x27, x28, 0x60 + + // Save the value of SP before stack allocation to the last slot in the frame (slot #15) + add \trashReg, sp, #0x80 + str \trashReg, [sp, #0x70] + + // Record the bitmask of saved registers in the frame (slot #3) + mov \trashReg, #DEFAULT_FRAME_SAVE_FLAGS + str \trashReg, [sp, #0x18] + + mov \trashReg, sp +.endm + +// Pop the frame and restore register state preserved by PUSH_COOP_PINVOKE_FRAME +.macro POP_COOP_PINVOKE_FRAME + + EPILOG_RESTORE_REG_PAIR x19, x20, 0x20 + EPILOG_RESTORE_REG_PAIR x21, x22, 0x30 + EPILOG_RESTORE_REG_PAIR x23, x24, 0x40 + EPILOG_RESTORE_REG_PAIR x25, x26, 0x50 + EPILOG_RESTORE_REG_PAIR x27, x28, 0x60 + EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 0x80 +.endm + +// Bit position for the flags above, to be used with tbz / tbnz instructions +PTFF_THREAD_ABORT_BIT = 36 + +// +// CONSTANTS -- INTEGER +// +#define TSF_Attached 0x01 +#define TSF_SuppressGcStress 0x08 +#define TSF_DoNotTriggerGc 0x10 +#define TSF_SuppressGcStress__OR__TSF_DoNotTriggerGC 0x18 + +// Bit position for the flags above, to be used with tbz / tbnz instructions +TrapThreadsFlags_AbortInProgress_Bit = 0 +TrapThreadsFlags_TrapThreads_Bit = 1 + +// This must match HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT +#define STATUS_REDHAWK_THREAD_ABORT 0x43 + +// These must match the TrapThreadsFlags enum +#define TrapThreadsFlags_None 0 +#define TrapThreadsFlags_AbortInProgress 1 +#define TrapThreadsFlags_TrapThreads 2 diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosx86.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosx86.inc new file mode 100644 index 00000000000000..08d57bb93be0c7 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosx86.inc @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.macro NESTED_ENTRY Name, Section, Handler + LEAF_ENTRY \Name, \Section + .ifnc \Handler, NoHandler + .cfi_personality 0x1b, C_FUNC(\Handler) // 0x1b == DW_EH_PE_pcrel | DW_EH_PE_sdata4 + .endif +.endm + +.macro NESTED_END Name, Section + LEAF_END \Name, \Section +.endm + +.macro LEAF_ENTRY Name, Section + .global C_FUNC(\Name) + .type \Name, %function +C_FUNC(\Name): + .cfi_startproc +.endm + +.macro PATCH_LABEL Name + .global C_FUNC(\Name) +C_FUNC(\Name): +.endm + +.macro LEAF_END Name, Section + .size \Name, .-\Name + .cfi_endproc +.endm + +.macro LEAF_END_MARKED Name, Section +C_FUNC(\Name\()_End): + .global C_FUNC(\Name\()_End) + LEAF_END \Name, \Section +.endm + +.macro PROLOG_BEG + push ebp + .cfi_def_cfa_offset 8 + .cfi_offset ebp, -8 + mov ebp, esp +.endm + +.macro PROLOG_PUSH Reg + push \Reg + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset \Reg, 0 +.endm + +.macro PROLOG_END + .cfi_def_cfa_register ebp + .cfi_def_cfa_offset 8 +.endm + +.macro EPILOG_BEG +.endm + +.macro EPILOG_POP Reg + pop \Reg + .cfi_restore \Reg +.endm + +.macro EPILOG_END + pop ebp +.endm + +.macro ESP_PROLOG_BEG +.endm + +.macro ESP_PROLOG_PUSH Reg + PROLOG_PUSH \Reg +.endm + +.macro ESP_PROLOG_ALLOC Size + sub esp, \Size + .cfi_adjust_cfa_offset \Size +.endm + +.macro ESP_PROLOG_END + .cfi_def_cfa_register esp +.endm + +.macro ESP_EPILOG_BEG +.endm + +.macro ESP_EPILOG_POP Reg + EPILOG_POP \Reg +.endm + +.macro ESP_EPILOG_FREE Size + add esp, \Size + .cfi_adjust_cfa_offset -\Size +.endm + +.macro ESP_EPILOG_END +.endm + +.macro PREPARE_EXTERNAL_VAR Name, Reg +.att_syntax + call 0f +0: + popl %\Reg +1: + addl $_GLOBAL_OFFSET_TABLE_ + (1b - 0b), %\Reg + movl C_FUNC(\Name)@GOT(%\Reg), %\Reg +.intel_syntax noprefix +.endm + +.macro CHECK_STACK_ALIGNMENT +#ifdef _DEBUG + test esp, 0Fh + je 0f + int3 +0: +#endif // _DEBUG +.endm diff --git a/src/coreclr/nativeaot/Runtime/windows/AsmOffsets.cpp b/src/coreclr/nativeaot/Runtime/windows/AsmOffsets.cpp new file mode 100644 index 00000000000000..8a852548d49ebc --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/windows/AsmOffsets.cpp @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#if defined(HOST_ARM) || defined(HOST_ARM64) + +#define HASH_DEFINE #define +#define PLAT_ASM_OFFSET(offset, cls, member) HASH_DEFINE OFFSETOF__##cls##__##member 0x##offset +#define PLAT_ASM_SIZEOF(size, cls ) HASH_DEFINE SIZEOF__##cls 0x##size +#define PLAT_ASM_CONST(constant, expr) HASH_DEFINE expr 0x##constant + +#else + +#define PLAT_ASM_OFFSET(offset, cls, member) OFFSETOF__##cls##__##member equ 0##offset##h +#define PLAT_ASM_SIZEOF(size, cls ) SIZEOF__##cls equ 0##size##h +#define PLAT_ASM_CONST(constant, expr) expr equ 0##constant##h + +#endif + +#include "AsmOffsets.h" diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp new file mode 100644 index 00000000000000..28732604fc425c --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp @@ -0,0 +1,914 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include "common.h" + +#include + +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "regdisplay.h" +#include "ICodeManager.h" +#include "CoffNativeCodeManager.h" +#include "varint.h" +#include "holder.h" + +#include "CommonMacros.inl" + +#define GCINFODECODER_NO_EE +#include "gcinfodecoder.cpp" + +#define UBF_FUNC_KIND_MASK 0x03 +#define UBF_FUNC_KIND_ROOT 0x00 +#define UBF_FUNC_KIND_HANDLER 0x01 +#define UBF_FUNC_KIND_FILTER 0x02 + +#define UBF_FUNC_HAS_EHINFO 0x04 +#define UBF_FUNC_REVERSE_PINVOKE 0x08 +#define UBF_FUNC_HAS_ASSOCIATED_DATA 0x10 + +#ifdef TARGET_X86 +// +// x86 ABI does not define RUNTIME_FUNCTION. Define our own to allow unification between x86 and other platforms. +// +typedef struct _RUNTIME_FUNCTION { + DWORD BeginAddress; + DWORD EndAddress; + DWORD UnwindData; +} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION; + +typedef struct _KNONVOLATILE_CONTEXT_POINTERS { + + // The ordering of these fields should be aligned with that + // of corresponding fields in CONTEXT + // + // (See REGDISPLAY in Runtime/regdisp.h for details) + PDWORD Edi; + PDWORD Esi; + PDWORD Ebx; + PDWORD Edx; + PDWORD Ecx; + PDWORD Eax; + + PDWORD Ebp; + +} KNONVOLATILE_CONTEXT_POINTERS, *PKNONVOLATILE_CONTEXT_POINTERS; + +typedef struct _UNWIND_INFO { + ULONG FunctionLength; +} UNWIND_INFO, *PUNWIND_INFO; + +#elif defined(TARGET_AMD64) + +#define UNW_FLAG_NHANDLER 0x0 +#define UNW_FLAG_EHANDLER 0x1 +#define UNW_FLAG_UHANDLER 0x2 +#define UNW_FLAG_CHAININFO 0x4 + +// +// The following structures are defined in Windows x64 unwind info specification +// http://www.bing.com/search?q=msdn+Exception+Handling+x64 +// +typedef union _UNWIND_CODE { + struct { + uint8_t CodeOffset; + uint8_t UnwindOp : 4; + uint8_t OpInfo : 4; + }; + + uint16_t FrameOffset; +} UNWIND_CODE, *PUNWIND_CODE; + +typedef struct _UNWIND_INFO { + uint8_t Version : 3; + uint8_t Flags : 5; + uint8_t SizeOfProlog; + uint8_t CountOfUnwindCodes; + uint8_t FrameRegister : 4; + uint8_t FrameOffset : 4; + UNWIND_CODE UnwindCode[1]; +} UNWIND_INFO, *PUNWIND_INFO; + +#endif // TARGET_X86 + +typedef DPTR(struct _UNWIND_INFO) PTR_UNWIND_INFO; +typedef DPTR(union _UNWIND_CODE) PTR_UNWIND_CODE; + +static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFunction, /* out */ size_t * pSize) +{ +#if defined(TARGET_AMD64) + PTR_UNWIND_INFO pUnwindInfo(dac_cast(moduleBase + pRuntimeFunction->UnwindInfoAddress)); + + size_t size = offsetof(UNWIND_INFO, UnwindCode) + sizeof(UNWIND_CODE) * pUnwindInfo->CountOfUnwindCodes; + + // Chained unwind info is not supported at this time + ASSERT((pUnwindInfo->Flags & UNW_FLAG_CHAININFO) == 0); + + if (pUnwindInfo->Flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER)) + { + // Personality routine + size = ALIGN_UP(size, sizeof(DWORD)) + sizeof(DWORD); + } + + *pSize = size; + + return pUnwindInfo; + +#elif defined(TARGET_X86) + + PTR_UNWIND_INFO pUnwindInfo(dac_cast(moduleBase + pRuntimeFunction->UnwindInfoAddress)); + + *pSize = sizeof(UNWIND_INFO); + + return pUnwindInfo; + +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) + + // if this function uses packed unwind data then at least one of the two least significant bits + // will be non-zero. if this is the case then there will be no xdata record to enumerate. + ASSERT((pRuntimeFunction->UnwindData & 0x3) == 0); + + // compute the size of the unwind info + PTR_UInt32 xdata = dac_cast(pRuntimeFunction->UnwindData + moduleBase); + int size = 4; + +#if defined(TARGET_ARM) + // See https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling + int unwindWords = xdata[0] >> 28; + int epilogScopes = (xdata[0] >> 23) & 0x1f; +#else + // See https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling + int unwindWords = xdata[0] >> 27; + int epilogScopes = (xdata[0] >> 22) & 0x1f; +#endif + + if (unwindWords == 0 && epilogScopes == 0) + { + size += 4; + unwindWords = (xdata[1] >> 16) & 0xff; + epilogScopes = xdata[1] & 0xffff; + } + + if (!(xdata[0] & (1 << 21))) + size += 4 * epilogScopes; + + size += 4 * unwindWords; + + if ((xdata[0] & (1 << 20)) != 0) + { + // Personality routine + size += 4; + } + + *pSize = size; + return xdata; +#else + PORTABILITY_ASSERT("GetUnwindDataBlob"); + *pSize = 0; + return NULL; +#endif +} + + +CoffNativeCodeManager::CoffNativeCodeManager(TADDR moduleBase, + PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange, + PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable, + PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions) + : m_moduleBase(moduleBase), + m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange), + m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable), + m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions) +{ +} + +CoffNativeCodeManager::~CoffNativeCodeManager() +{ +} + +static int LookupUnwindInfoForMethod(uint32_t relativePc, + PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, + int low, + int high) +{ +#ifdef TARGET_ARM + relativePc |= THUMB_CODE; +#endif + + // Binary search the RUNTIME_FUNCTION table + // Use linear search once we get down to a small number of elements + // to avoid Binary search overhead. + while (high - low > 10) + { + int middle = low + (high - low) / 2; + + PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + middle; + if (relativePc < pFunctionEntry->BeginAddress) + { + high = middle - 1; + } + else + { + low = middle; + } + } + + for (int i = low; i < high; i++) + { + PTR_RUNTIME_FUNCTION pNextFunctionEntry = pRuntimeFunctionTable + (i + 1); + if (relativePc < pNextFunctionEntry->BeginAddress) + { + high = i; + break; + } + } + + PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + high; + if (relativePc >= pFunctionEntry->BeginAddress) + { + return high; + } + + ASSERT_UNCONDITIONALLY("Invalid code address"); + return -1; +} + +struct CoffNativeMethodInfo +{ + PTR_RUNTIME_FUNCTION mainRuntimeFunction; + PTR_RUNTIME_FUNCTION runtimeFunction; + bool executionAborted; +}; + +// Ensure that CoffNativeMethodInfo fits into the space reserved by MethodInfo +static_assert(sizeof(CoffNativeMethodInfo) <= sizeof(MethodInfo), "CoffNativeMethodInfo too big"); + +bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC, + MethodInfo * pMethodInfoOut) +{ + // Stackwalker may call this with ControlPC that does not belong to this code manager + if (dac_cast(ControlPC) < dac_cast(m_pvManagedCodeStartRange) || + dac_cast(m_pvManagedCodeStartRange) + m_cbManagedCodeRange <= dac_cast(ControlPC)) + { + return false; + } + + CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethodInfoOut; + + TADDR relativePC = dac_cast(ControlPC) - m_moduleBase; + + int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable, + 0, m_nRuntimeFunctionTable - 1); + if (MethodIndex < 0) + return false; + + PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex; + + pMethodInfo->runtimeFunction = pRuntimeFunction; + + // The runtime function could correspond to a funclet. We need to get to the + // runtime function of the main method. + for (;;) + { + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pRuntimeFunction, &unwindDataBlobSize); + + uint8_t unwindBlockFlags = *(dac_cast(pUnwindDataBlob) + unwindDataBlobSize); + if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) == UBF_FUNC_KIND_ROOT) + break; + + pRuntimeFunction--; + } + + pMethodInfo->mainRuntimeFunction = pRuntimeFunction; + + pMethodInfo->executionAborted = false; + + return true; +} + +bool CoffNativeCodeManager::IsFunclet(MethodInfo * pMethInfo) +{ + CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethInfo; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pMethodInfo->runtimeFunction, &unwindDataBlobSize); + + uint8_t unwindBlockFlags = *(dac_cast(pUnwindDataBlob) + unwindDataBlobSize); + + // A funclet will have an entry in funclet to main method map + return (unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT; +} + +bool CoffNativeCodeManager::IsFilter(MethodInfo * pMethInfo) +{ + CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethInfo; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pMethodInfo->runtimeFunction, &unwindDataBlobSize); + + uint8_t unwindBlockFlags = *(dac_cast(pUnwindDataBlob) + unwindDataBlobSize); + + return (unwindBlockFlags & UBF_FUNC_KIND_MASK) == UBF_FUNC_KIND_FILTER; +} + +PTR_VOID CoffNativeCodeManager::GetFramePointer(MethodInfo * pMethInfo, + REGDISPLAY * pRegisterSet) +{ + CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethInfo; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pMethodInfo->runtimeFunction, &unwindDataBlobSize); + + uint8_t unwindBlockFlags = *(dac_cast(pUnwindDataBlob) + unwindDataBlobSize); + + // Return frame pointer for methods with EH and funclets + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0 || (unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT) + { + return (PTR_VOID)pRegisterSet->GetFP(); + } + + return NULL; +} + +void CoffNativeCodeManager::EnumGcRefs(MethodInfo * pMethodInfo, + PTR_VOID safePointAddress, + REGDISPLAY * pRegisterSet, + GCEnumContext * hCallback) +{ + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->mainRuntimeFunction, &unwindDataBlobSize); + + PTR_UInt8 p = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; + + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + + TADDR methodStartAddress = m_moduleBase + pNativeMethodInfo->mainRuntimeFunction->BeginAddress; + uint32_t codeOffset = (uint32_t)(dac_cast(safePointAddress) - methodStartAddress); + + GcInfoDecoder decoder( + GCInfoToken(p), + GcInfoDecoderFlags(DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG), + codeOffset - 1 // TODO: Is this adjustment correct? + ); + + ICodeManagerFlags flags = (ICodeManagerFlags)0; + if (pNativeMethodInfo->executionAborted) + flags = ICodeManagerFlags::ExecutionAborted; + if (IsFilter(pMethodInfo)) + flags = (ICodeManagerFlags)(flags | ICodeManagerFlags::NoReportUntracked); + + if (!decoder.EnumerateLiveSlots( + pRegisterSet, + false /* reportScratchSlots */, + flags, + hCallback->pCallback, + hCallback + )) + { + assert(false); + } +} + +uintptr_t CoffNativeCodeManager::GetConservativeUpperBoundForOutgoingArgs(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet) +{ + // Return value + TADDR upperBound; + CoffNativeMethodInfo* pNativeMethodInfo = (CoffNativeMethodInfo *) pMethodInfo; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->runtimeFunction, &unwindDataBlobSize); + PTR_UInt8 p = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) + { + // Reverse PInvoke transition should be on the main function body only + assert(pNativeMethodInfo->mainRuntimeFunction == pNativeMethodInfo->runtimeFunction); + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + + GcInfoDecoder decoder(GCInfoToken(p), DECODE_REVERSE_PINVOKE_VAR); + INT32 slot = decoder.GetReversePInvokeFrameStackSlot(); + assert(slot != NO_REVERSE_PINVOKE_FRAME); + + TADDR basePointer; + UINT32 stackBasedRegister = decoder.GetStackBaseRegister(); + + if (stackBasedRegister == NO_STACK_BASE_REGISTER) + { + basePointer = dac_cast(pRegisterSet->GetSP()); + } + else + { + // REVIEW: Verify that stackBasedRegister is FP + basePointer = dac_cast(pRegisterSet->GetFP()); + } + + // Reverse PInvoke case. The embedded reverse PInvoke frame is guaranteed to reside above + // all outgoing arguments. + upperBound = dac_cast(basePointer + slot); + } + else + { +#if defined(TARGET_AMD64) + // Check for a pushed RBP value + if (GetFramePointer(pMethodInfo, pRegisterSet) == NULL) + { + // Unwind the current method context to get the caller's stack pointer + // and use it as the upper bound for the callee + SIZE_T EstablisherFrame; + PVOID HandlerData; + CONTEXT context; + context.Rsp = pRegisterSet->GetSP(); + context.Rbp = pRegisterSet->GetFP(); + context.Rip = pRegisterSet->GetIP(); + + RtlVirtualUnwind(NULL, + dac_cast(m_moduleBase), + pRegisterSet->IP, + (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction, + &context, + &HandlerData, + &EstablisherFrame, + NULL); + + // Skip the return address immediately below the stack pointer + upperBound = dac_cast(context.Rsp - sizeof(TADDR)); + } + else + { + // In amd64, it is guaranteed that if there is a pushed RBP + // value at the top of the frame it resides above all outgoing arguments. Unlike x86, + // the frame pointer generally points to a location that is separated from the pushed RBP + // value by an offset that is recorded in the info header. Recover the address of the + // pushed RBP value by subtracting this offset. + upperBound = dac_cast(pRegisterSet->GetFP() - ((PTR_UNWIND_INFO) pUnwindDataBlob)->FrameOffset); + } + +#elif defined(TARGET_ARM) || defined(TARGET_ARM64) + // Unwind the current method context to get the caller's stack pointer + // and use it as the upper bound for the callee + SIZE_T EstablisherFrame; + PVOID HandlerData; + CONTEXT context; + context.Sp = pRegisterSet->GetSP(); + context.Fp = pRegisterSet->GetFP(); + context.Pc = pRegisterSet->GetIP(); + + RtlVirtualUnwind(NULL, + dac_cast(m_moduleBase), + pRegisterSet->IP, + (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction, + &context, + &HandlerData, + &EstablisherFrame, + NULL); + + upperBound = dac_cast(context.Sp); + +#else + PORTABILITY_ASSERT("GetConservativeUpperBoundForOutgoingArgs"); + upperBound = NULL; + RhFailFast(); +#endif + } + return upperBound; +} + +bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet, // in/out + PTR_VOID * ppPreviousTransitionFrame) // out +{ + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->runtimeFunction, &unwindDataBlobSize); + + PTR_UInt8 p = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; + + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) + { + // Reverse PInvoke transition should be on the main function body only + assert(pNativeMethodInfo->mainRuntimeFunction == pNativeMethodInfo->runtimeFunction); + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + + GcInfoDecoder decoder(GCInfoToken(p), DECODE_REVERSE_PINVOKE_VAR); + INT32 slot = decoder.GetReversePInvokeFrameStackSlot(); + assert(slot != NO_REVERSE_PINVOKE_FRAME); + + TADDR basePointer = NULL; + UINT32 stackBasedRegister = decoder.GetStackBaseRegister(); + if (stackBasedRegister == NO_STACK_BASE_REGISTER) + { + basePointer = dac_cast(pRegisterSet->GetSP()); + } + else + { + basePointer = dac_cast(pRegisterSet->GetFP()); + } + *ppPreviousTransitionFrame = *(void**)(basePointer + slot); + return true; + } + + *ppPreviousTransitionFrame = NULL; + + CONTEXT context; + KNONVOLATILE_CONTEXT_POINTERS contextPointers; + +#ifdef _DEBUG + memset(&context, 0xDD, sizeof(context)); + memset(&contextPointers, 0xDD, sizeof(contextPointers)); +#endif + +#if defined(TARGET_X86) + #define FOR_EACH_NONVOLATILE_REGISTER(F) \ + F(Eax, pRax) F(Ecx, pRcx) F(Edx, pRdx) F(Ebx, pRbx) F(Ebp, pRbp) F(Esi, pRsi) F(Edi, pRdi) + #define WORDPTR PDWORD +#elif defined(TARGET_AMD64) + #define FOR_EACH_NONVOLATILE_REGISTER(F) \ + F(Rax, pRax) F(Rcx, pRcx) F(Rdx, pRdx) F(Rbx, pRbx) F(Rbp, pRbp) F(Rsi, pRsi) F(Rdi, pRdi) \ + F(R8, pR8) F(R9, pR9) F(R10, pR10) F(R11, pR11) F(R12, pR12) F(R13, pR13) F(R14, pR14) F(R15, pR15) + #define WORDPTR PDWORD64 +#elif defined(TARGET_ARM64) + #define FOR_EACH_NONVOLATILE_REGISTER(F) \ + F(X19, pX19) F(X20, pX20) F(X21, pX21) F(X22, pX22) F(X23, pX23) F(X24, pX24) \ + F(X25, pX25) F(X26, pX26) F(X27, pX27) F(X28, pX28) F(Fp, pFP) F(Lr, pLR) + #define WORDPTR PDWORD64 +#endif // defined(TARGET_X86) + +#define REGDISPLAY_TO_CONTEXT(contextField, regDisplayField) \ + contextPointers.contextField = (WORDPTR) pRegisterSet->regDisplayField; \ + if (pRegisterSet->regDisplayField != NULL) context.contextField = *pRegisterSet->regDisplayField; + +#define CONTEXT_TO_REGDISPLAY(contextField, regDisplayField) \ + pRegisterSet->regDisplayField = (PTR_UIntNative) contextPointers.contextField; + + FOR_EACH_NONVOLATILE_REGISTER(REGDISPLAY_TO_CONTEXT); + +#if defined(TARGET_X86) + PORTABILITY_ASSERT("CoffNativeCodeManager::UnwindStackFrame"); +#elif defined(TARGET_AMD64) + memcpy(&context.Xmm6, pRegisterSet->Xmm, sizeof(pRegisterSet->Xmm)); + + context.Rsp = pRegisterSet->SP; + context.Rip = pRegisterSet->IP; + + SIZE_T EstablisherFrame; + PVOID HandlerData; + + RtlVirtualUnwind(NULL, + dac_cast(m_moduleBase), + pRegisterSet->IP, + (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction, + &context, + &HandlerData, + &EstablisherFrame, + &contextPointers); + + pRegisterSet->SP = context.Rsp; + pRegisterSet->IP = context.Rip; + + pRegisterSet->pIP = PTR_PCODE(pRegisterSet->SP - sizeof(TADDR)); + + memcpy(pRegisterSet->Xmm, &context.Xmm6, sizeof(pRegisterSet->Xmm)); +#elif defined(TARGET_ARM64) + for (int i = 8; i < 16; i++) + context.V[i].Low = pRegisterSet->D[i - 8]; + + context.Sp = pRegisterSet->SP; + context.Pc = pRegisterSet->IP; + + SIZE_T EstablisherFrame; + PVOID HandlerData; + + RtlVirtualUnwind(NULL, + dac_cast(m_moduleBase), + pRegisterSet->IP, + (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction, + &context, + &HandlerData, + &EstablisherFrame, + &contextPointers); + + pRegisterSet->SP = context.Sp; + pRegisterSet->IP = context.Pc; + + pRegisterSet->pIP = contextPointers.Lr; + + for (int i = 8; i < 16; i++) + pRegisterSet->D[i - 8] = context.V[i].Low; +#endif // defined(TARGET_X86) + + FOR_EACH_NONVOLATILE_REGISTER(CONTEXT_TO_REGDISPLAY); + +#undef FOR_EACH_NONVOLATILE_REGISTER +#undef WORDPTR +#undef REGDISPLAY_TO_CONTEXT +#undef CONTEXT_TO_REGDISPLAY + + return true; +} + +// Convert the return kind that was encoded by RyuJIT to the +// value that CoreRT runtime can understand and support. +GCRefKind GetGcRefKind(ReturnKind returnKind) +{ + static_assert((GCRefKind)ReturnKind::RT_Scalar == GCRK_Scalar, "ReturnKind::RT_Scalar does not match GCRK_Scalar"); + static_assert((GCRefKind)ReturnKind::RT_Object == GCRK_Object, "ReturnKind::RT_Object does not match GCRK_Object"); + static_assert((GCRefKind)ReturnKind::RT_ByRef == GCRK_Byref, "ReturnKind::RT_ByRef does not match GCRK_Byref"); + ASSERT((returnKind == RT_Scalar) || (returnKind == GCRK_Object) || (returnKind == GCRK_Byref)); + + return (GCRefKind)returnKind; +} + +bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet, // in + PTR_PTR_VOID * ppvRetAddrLocation, // out + GCRefKind * pRetValueKind) // out +{ +#if defined(TARGET_AMD64) + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->runtimeFunction, &unwindDataBlobSize); + + PTR_UInt8 p = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; + + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + // Check whether this is a funclet + if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT) + return false; + + // Skip hijacking a reverse-pinvoke method - it doesn't get us much because we already synchronize + // with the GC on the way back to native code. + if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) + return false; + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + + // Decode the GC info for the current method to determine its return type + GcInfoDecoder decoder( + GCInfoToken(p), + GcInfoDecoderFlags(DECODE_RETURN_KIND), + 0 + ); + + GCRefKind gcRefKind = GetGcRefKind(decoder.GetReturnKind()); + + // Unwind the current method context to the caller's context to get its stack pointer + // and obtain the location of the return address on the stack + SIZE_T EstablisherFrame; + PVOID HandlerData; + CONTEXT context; + context.Rsp = pRegisterSet->GetSP(); + context.Rbp = pRegisterSet->GetFP(); + context.Rip = pRegisterSet->GetIP(); + + RtlVirtualUnwind(NULL, + dac_cast(m_moduleBase), + pRegisterSet->IP, + (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction, + &context, + &HandlerData, + &EstablisherFrame, + NULL); + + *ppvRetAddrLocation = (PTR_PTR_VOID)(context.Rsp - sizeof (PVOID)); + *pRetValueKind = gcRefKind; + return true; +#else + return false; +#endif // defined(TARGET_AMD64) +} + +void CoffNativeCodeManager::UnsynchronizedHijackMethodLoops(MethodInfo * pMethodInfo) +{ + // @TODO: CORERT: UnsynchronizedHijackMethodLoops +} + +PTR_VOID CoffNativeCodeManager::RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC) +{ + // GCInfo decoder needs to know whether execution of the method is aborted + // while querying for gc-info. But ICodeManager::EnumGCRef() doesn't receive any + // flags from mrt. Call to this method is used as a cue to mark the method info + // as execution aborted. Note - if pMethodInfo was cached, this scheme would not work. + // + // If the method has EH, then JIT will make sure the method is fully interruptible + // and we will have GC-info available at the faulting address as well. + + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + pNativeMethodInfo->executionAborted = true; + + return controlPC; +} + +struct CoffEHEnumState +{ + PTR_UInt8 pMethodStartAddress; + PTR_UInt8 pEHInfo; + uint32_t uClause; + uint32_t nClauses; +}; + +// Ensure that CoffEHEnumState fits into the space reserved by EHEnumState +static_assert(sizeof(CoffEHEnumState) <= sizeof(EHEnumState), "CoffEHEnumState too big"); + +bool CoffNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMethodStartAddress, EHEnumState * pEHEnumStateOut) +{ + assert(pMethodInfo != NULL); + assert(pMethodStartAddress != NULL); + assert(pEHEnumStateOut != NULL); + + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + CoffEHEnumState * pEnumState = (CoffEHEnumState *)pEHEnumStateOut; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->mainRuntimeFunction, &unwindDataBlobSize); + + PTR_UInt8 p = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; + + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + // return if there is no EH info associated with this method + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) == 0) + { + return false; + } + + *pMethodStartAddress = dac_cast(m_moduleBase + pNativeMethodInfo->mainRuntimeFunction->BeginAddress); + + pEnumState->pMethodStartAddress = dac_cast(*pMethodStartAddress); + pEnumState->pEHInfo = dac_cast(m_moduleBase + *dac_cast(p)); + pEnumState->uClause = 0; + pEnumState->nClauses = VarInt::ReadUnsigned(pEnumState->pEHInfo); + + return true; +} + +bool CoffNativeCodeManager::EHEnumNext(EHEnumState * pEHEnumState, EHClause * pEHClauseOut) +{ + assert(pEHEnumState != NULL); + assert(pEHClauseOut != NULL); + + CoffEHEnumState * pEnumState = (CoffEHEnumState *)pEHEnumState; + if (pEnumState->uClause >= pEnumState->nClauses) + return false; + pEnumState->uClause++; + + pEHClauseOut->m_tryStartOffset = VarInt::ReadUnsigned(pEnumState->pEHInfo); + + uint32_t tryEndDeltaAndClauseKind = VarInt::ReadUnsigned(pEnumState->pEHInfo); + pEHClauseOut->m_clauseKind = (EHClauseKind)(tryEndDeltaAndClauseKind & 0x3); + pEHClauseOut->m_tryEndOffset = pEHClauseOut->m_tryStartOffset + (tryEndDeltaAndClauseKind >> 2); + + // For each clause, we have up to 4 integers: + // 1) try start offset + // 2) (try length << 2) | clauseKind + // 3) if (typed || fault || filter) { handler start offset } + // 4a) if (typed) { type RVA } + // 4b) if (filter) { filter start offset } + // + // The first two integers have already been decoded + + switch (pEHClauseOut->m_clauseKind) + { + case EH_CLAUSE_TYPED: + pEHClauseOut->m_handlerAddress = pEnumState->pMethodStartAddress + VarInt::ReadUnsigned(pEnumState->pEHInfo); + + // Read target type + { + // @TODO: CORERT: Compress EHInfo using type table index scheme + // https://github.com/dotnet/corert/issues/972 + uint32_t typeRVA = *((PTR_UInt32&)pEnumState->pEHInfo)++; + pEHClauseOut->m_pTargetType = dac_cast(m_moduleBase + typeRVA); + } + break; + case EH_CLAUSE_FAULT: + pEHClauseOut->m_handlerAddress = pEnumState->pMethodStartAddress + VarInt::ReadUnsigned(pEnumState->pEHInfo); + break; + case EH_CLAUSE_FILTER: + pEHClauseOut->m_handlerAddress = pEnumState->pMethodStartAddress + VarInt::ReadUnsigned(pEnumState->pEHInfo); + pEHClauseOut->m_filterAddress = pEnumState->pMethodStartAddress + VarInt::ReadUnsigned(pEnumState->pEHInfo); + break; + default: + UNREACHABLE_MSG("unexpected EHClauseKind"); + } + + return true; +} + +PTR_VOID CoffNativeCodeManager::GetOsModuleHandle() +{ + return dac_cast(m_moduleBase); +} + +PTR_VOID CoffNativeCodeManager::GetMethodStartAddress(MethodInfo * pMethodInfo) +{ + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + return dac_cast(m_moduleBase + pNativeMethodInfo->mainRuntimeFunction->BeginAddress); +} + +void * CoffNativeCodeManager::GetClasslibFunction(ClasslibFunctionId functionId) +{ + uint32_t id = (uint32_t)functionId; + + if (id >= m_nClasslibFunctions) + return nullptr; + + return m_pClasslibFunctions[id]; +} + +PTR_VOID CoffNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC) +{ + if (dac_cast(ControlPC) < dac_cast(m_pvManagedCodeStartRange) || + dac_cast(m_pvManagedCodeStartRange) + m_cbManagedCodeRange <= dac_cast(ControlPC)) + { + return NULL; + } + + TADDR relativePC = dac_cast(ControlPC) - m_moduleBase; + + int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable, 0, m_nRuntimeFunctionTable - 1); + if (MethodIndex < 0) + return NULL; + + PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pRuntimeFunction, &unwindDataBlobSize); + + PTR_UInt8 p = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; + + uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) == 0) + return NULL; + + uint32_t dataRVA = *(uint32_t*)p; + return dac_cast(m_moduleBase + dataRVA); +} + +extern "C" bool __stdcall RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, uint32_t cbRange); +extern "C" void __stdcall UnregisterCodeManager(ICodeManager * pCodeManager); +extern "C" bool __stdcall RegisterUnboxingStubs(PTR_VOID pvStartRange, uint32_t cbRange); + +extern "C" +bool RhRegisterOSModule(void * pModule, + void * pvManagedCodeStartRange, uint32_t cbManagedCodeRange, + void * pvUnboxingStubsStartRange, uint32_t cbUnboxingStubsRange, + void ** pClasslibFunctions, uint32_t nClasslibFunctions) +{ + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pModule; + PIMAGE_NT_HEADERS pNTHeaders = (PIMAGE_NT_HEADERS)((TADDR)pModule + pDosHeader->e_lfanew); + + IMAGE_DATA_DIRECTORY * pRuntimeFunctions = &(pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]); + + NewHolder pCoffNativeCodeManager = new (nothrow) CoffNativeCodeManager((TADDR)pModule, + pvManagedCodeStartRange, cbManagedCodeRange, + dac_cast((TADDR)pModule + pRuntimeFunctions->VirtualAddress), + pRuntimeFunctions->Size / sizeof(RUNTIME_FUNCTION), + pClasslibFunctions, nClasslibFunctions); + + if (pCoffNativeCodeManager == nullptr) + return false; + + if (!RegisterCodeManager(pCoffNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange)) + return false; + + if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange)) + { + UnregisterCodeManager(pCoffNativeCodeManager); + return false; + } + + pCoffNativeCodeManager.SuppressRelease(); + + return true; +} diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h new file mode 100644 index 00000000000000..3a67ecc3f95384 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#if defined(TARGET_AMD64) || defined(TARGET_X86) +struct T_RUNTIME_FUNCTION { + uint32_t BeginAddress; + uint32_t EndAddress; + uint32_t UnwindInfoAddress; +}; +#elif defined(TARGET_ARM) +struct T_RUNTIME_FUNCTION { + uint32_t BeginAddress; + uint32_t UnwindData; +}; +#elif defined(TARGET_ARM64) +struct T_RUNTIME_FUNCTION { + uint32_t BeginAddress; + union { + uint32_t UnwindData; + struct { + uint32_t Flag : 2; + uint32_t FunctionLength : 11; + uint32_t RegF : 3; + uint32_t RegI : 4; + uint32_t H : 1; + uint32_t CR : 2; + uint32_t FrameSize : 9; + } PackedUnwindData; + }; +}; +#else +#error unexpected target architecture +#endif + +typedef DPTR(T_RUNTIME_FUNCTION) PTR_RUNTIME_FUNCTION; + +class CoffNativeCodeManager : public ICodeManager +{ + TADDR m_moduleBase; + + PTR_VOID m_pvManagedCodeStartRange; + uint32_t m_cbManagedCodeRange; + + PTR_RUNTIME_FUNCTION m_pRuntimeFunctionTable; + uint32_t m_nRuntimeFunctionTable; + + PTR_PTR_VOID m_pClasslibFunctions; + uint32_t m_nClasslibFunctions; + +public: + CoffNativeCodeManager(TADDR moduleBase, + PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange, + PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable, + PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions); + ~CoffNativeCodeManager(); + + // + // Code manager methods + // + + bool FindMethodInfo(PTR_VOID ControlPC, + MethodInfo * pMethodInfoOut); + + bool IsFunclet(MethodInfo * pMethodInfo); + + bool IsFilter(MethodInfo * pMethodInfo); + + PTR_VOID GetFramePointer(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet); + + void EnumGcRefs(MethodInfo * pMethodInfo, + PTR_VOID safePointAddress, + REGDISPLAY * pRegisterSet, + GCEnumContext * hCallback); + + bool UnwindStackFrame(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet, // in/out + PTR_VOID * ppPreviousTransitionFrame); // out + + uintptr_t GetConservativeUpperBoundForOutgoingArgs(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet); + + bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet, // in + PTR_PTR_VOID * ppvRetAddrLocation, // out + GCRefKind * pRetValueKind); // out + + void UnsynchronizedHijackMethodLoops(MethodInfo * pMethodInfo); + + PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC); + + bool EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMethodStartAddress, EHEnumState * pEHEnumState); + + bool EHEnumNext(EHEnumState * pEHEnumState, EHClause * pEHClause); + + PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo); + + void * GetClasslibFunction(ClasslibFunctionId functionId); + + PTR_VOID GetAssociatedData(PTR_VOID ControlPC); + + PTR_VOID GetOsModuleHandle(); +}; diff --git a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkCommon.cpp b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkCommon.cpp new file mode 100644 index 00000000000000..366bb63e6d246d --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkCommon.cpp @@ -0,0 +1,178 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Implementation of the portions of the Redhawk Platform Abstraction Layer (PAL) library that are common among +// multiple PAL variants. +// +// Note that in general we don't want to assume that Windows and Redhawk global definitions can co-exist. +// Since this code must include Windows headers to do its job we can't therefore safely include general +// Redhawk header files. +// + +#include +#include +#include +#include +#include "CommonTypes.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include +#include "CommonMacros.h" +#include "rhassert.h" + + +#define REDHAWK_PALEXPORT extern "C" +#define REDHAWK_PALAPI __stdcall + + +// Given the OS handle of a loaded module, compute the upper and lower virtual address bounds (inclusive). +REDHAWK_PALEXPORT void REDHAWK_PALAPI PalGetModuleBounds(HANDLE hOsHandle, _Out_ uint8_t ** ppLowerBound, _Out_ uint8_t ** ppUpperBound) +{ + BYTE *pbModule = (BYTE*)hOsHandle; + DWORD cbModule; + + IMAGE_NT_HEADERS *pNtHeaders = (IMAGE_NT_HEADERS*)(pbModule + ((IMAGE_DOS_HEADER*)hOsHandle)->e_lfanew); + if (pNtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + cbModule = ((IMAGE_OPTIONAL_HEADER32*)&pNtHeaders->OptionalHeader)->SizeOfImage; + else + cbModule = ((IMAGE_OPTIONAL_HEADER64*)&pNtHeaders->OptionalHeader)->SizeOfImage; + + *ppLowerBound = pbModule; + *ppUpperBound = pbModule + cbModule - 1; +} + +uint32_t g_RhNumberOfProcessors; + +REDHAWK_PALEXPORT int32_t REDHAWK_PALAPI PalGetProcessCpuCount() +{ + ASSERT(g_RhNumberOfProcessors > 0); + return g_RhNumberOfProcessors; +} + +// Retrieves the entire range of memory dedicated to the calling thread's stack. This does +// not get the current dynamic bounds of the stack, which can be significantly smaller than +// the maximum bounds. +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalGetMaximumStackBounds(_Out_ void** ppStackLowOut, _Out_ void** ppStackHighOut) +{ + // VirtualQuery on the address of a local variable to get the allocation + // base of the stack. Then use the StackBase field in the TEB to give + // the highest address of the stack region. + MEMORY_BASIC_INFORMATION mbi = { 0 }; + SIZE_T cb = VirtualQuery(&mbi, &mbi, sizeof(mbi)); + if (cb != sizeof(mbi)) + return false; + + NT_TIB* pTib = (NT_TIB*)NtCurrentTeb(); + *ppStackHighOut = pTib->StackBase; // stack base is the highest address + *ppStackLowOut = mbi.AllocationBase; // allocation base is the lowest address + return true; +} + +#if !defined(_INC_WINDOWS) + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING; +typedef UNICODE_STRING *PUNICODE_STRING; +typedef const UNICODE_STRING *PCUNICODE_STRING; + +typedef struct _PEB_LDR_DATA { + BYTE Reserved1[8]; + PVOID Reserved2[3]; + LIST_ENTRY InMemoryOrderModuleList; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + +typedef struct _LDR_DATA_TABLE_ENTRY { + PVOID Reserved1[2]; + LIST_ENTRY InMemoryOrderLinks; + PVOID Reserved2[2]; + PVOID DllBase; + PVOID Reserved3[2]; + UNICODE_STRING FullDllName; + BYTE Reserved4[8]; + PVOID Reserved5[3]; + union { + ULONG CheckSum; + PVOID Reserved6; + } DUMMYUNIONNAME; + ULONG TimeDateStamp; +} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; + +typedef struct _PEB { + BYTE Reserved1[2]; + BYTE BeingDebugged; + BYTE Reserved2[1]; + PVOID Reserved3[2]; + PPEB_LDR_DATA Ldr; + PVOID /*PRTL_USER_PROCESS_PARAMETERS*/ ProcessParameters; + PVOID Reserved4[3]; + PVOID AtlThunkSListPtr; + PVOID Reserved5; + ULONG Reserved6; + PVOID Reserved7; + ULONG Reserved8; + ULONG AtlThunkSListPtr32; + PVOID Reserved9[45]; + BYTE Reserved10[96]; + PVOID /*PPS_POST_PROCESS_INIT_ROUTINE*/ PostProcessInitRoutine; + BYTE Reserved11[128]; + PVOID Reserved12[1]; + ULONG SessionId; +} PEB, *PPEB; + +typedef struct _TEB { + PVOID Reserved1[12]; + PPEB ProcessEnvironmentBlock; + PVOID Reserved2[399]; + BYTE Reserved3[1952]; + PVOID TlsSlots[64]; + BYTE Reserved4[8]; + PVOID Reserved5[26]; + PVOID ReservedForOle; // Windows 2000 only + PVOID Reserved6[4]; + PVOID TlsExpansionSlots; +} TEB, *PTEB; + +#endif // !defined(_INC_WINDOWS) + +// retrieves the full path to the specified module, if moduleBase is NULL retreieves the full path to the +// executable module of the current process. +// +// Return value: number of characters in name string +// +//NOTE: This implementation exists because calling GetModuleFileName is not wack compliant. if we later decide +// that the framework package containing mrt100_app no longer needs to be wack compliant, this should be +// removed and the windows implementation of GetModuleFileName should be substitued on windows. +REDHAWK_PALEXPORT int32_t PalGetModuleFileName(_Out_ const TCHAR** pModuleNameOut, HANDLE moduleBase) +{ + TEB* pTEB = NtCurrentTeb(); + LIST_ENTRY* pStartLink = &(pTEB->ProcessEnvironmentBlock->Ldr->InMemoryOrderModuleList); + LIST_ENTRY* pCurLink = pStartLink->Flink; + + do + { + LDR_DATA_TABLE_ENTRY* pEntry = CONTAINING_RECORD(pCurLink, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); + + //null moduleBase will result in the first module being returned + //since the module list is ordered this is the executable module of the current process + if ((pEntry->DllBase == moduleBase) || (moduleBase == NULL)) + { + *pModuleNameOut = pEntry->FullDllName.Buffer; + return pEntry->FullDllName.Length / 2; + } + pCurLink = pCurLink->Flink; + } + while (pCurLink != pStartLink); + + *pModuleNameOut = NULL; + return 0; +} + +REDHAWK_PALEXPORT uint64_t __cdecl PalGetTickCount64() +{ + return GetTickCount64(); +} diff --git a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkInline.h b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkInline.h new file mode 100644 index 00000000000000..c6ed7d641a20bf --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkInline.h @@ -0,0 +1,157 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Implementation of Redhawk PAL inline functions + +EXTERN_C long __cdecl _InterlockedIncrement(long volatile *); +#pragma intrinsic(_InterlockedIncrement) +FORCEINLINE int32_t PalInterlockedIncrement(_Inout_ int32_t volatile *pDst) +{ + return _InterlockedIncrement((long volatile *)pDst); +} + +EXTERN_C long __cdecl _InterlockedDecrement(long volatile *); +#pragma intrinsic(_InterlockedDecrement) +FORCEINLINE int32_t PalInterlockedDecrement(_Inout_ int32_t volatile *pDst) +{ + return _InterlockedDecrement((long volatile *)pDst); +} + +EXTERN_C long _InterlockedOr(long volatile *, long); +#pragma intrinsic(_InterlockedOr) +FORCEINLINE uint32_t PalInterlockedOr(_Inout_ uint32_t volatile *pDst, uint32_t iValue) +{ + return _InterlockedOr((long volatile *)pDst, iValue); +} + +EXTERN_C long _InterlockedAnd(long volatile *, long); +#pragma intrinsic(_InterlockedAnd) +FORCEINLINE uint32_t PalInterlockedAnd(_Inout_ uint32_t volatile *pDst, uint32_t iValue) +{ + return _InterlockedAnd((long volatile *)pDst, iValue); +} + +EXTERN_C long __PN__MACHINECALL_CDECL_OR_DEFAULT _InterlockedExchange(long volatile *, long); +#pragma intrinsic(_InterlockedExchange) +FORCEINLINE int32_t PalInterlockedExchange(_Inout_ int32_t volatile *pDst, int32_t iValue) +{ + return _InterlockedExchange((long volatile *)pDst, iValue); +} + +EXTERN_C long __PN__MACHINECALL_CDECL_OR_DEFAULT _InterlockedCompareExchange(long volatile *, long, long); +#pragma intrinsic(_InterlockedCompareExchange) +FORCEINLINE int32_t PalInterlockedCompareExchange(_Inout_ int32_t volatile *pDst, int32_t iValue, int32_t iComparand) +{ + return _InterlockedCompareExchange((long volatile *)pDst, iValue, iComparand); +} + +EXTERN_C int64_t _InterlockedCompareExchange64(int64_t volatile *, int64_t, int64_t); +#pragma intrinsic(_InterlockedCompareExchange64) +FORCEINLINE int64_t PalInterlockedCompareExchange64(_Inout_ int64_t volatile *pDst, int64_t iValue, int64_t iComparand) +{ + return _InterlockedCompareExchange64(pDst, iValue, iComparand); +} + +#if defined(HOST_AMD64) || defined(HOST_ARM64) +EXTERN_C uint8_t _InterlockedCompareExchange128(int64_t volatile *, int64_t, int64_t, int64_t *); +#pragma intrinsic(_InterlockedCompareExchange128) +FORCEINLINE uint8_t PalInterlockedCompareExchange128(_Inout_ int64_t volatile *pDst, int64_t iValueHigh, int64_t iValueLow, int64_t *pComparandAndResult) +{ + return _InterlockedCompareExchange128(pDst, iValueHigh, iValueLow, pComparandAndResult); +} +#endif // HOST_AMD64 + +#ifdef HOST_64BIT + +EXTERN_C void * _InterlockedExchangePointer(void * volatile *, void *); +#pragma intrinsic(_InterlockedExchangePointer) +FORCEINLINE void * PalInterlockedExchangePointer(_Inout_ void * volatile *pDst, _In_ void *pValue) +{ + return _InterlockedExchangePointer((void * volatile *)pDst, pValue); +} + +EXTERN_C void * _InterlockedCompareExchangePointer(void * volatile *, void *, void *); +#pragma intrinsic(_InterlockedCompareExchangePointer) +FORCEINLINE void * PalInterlockedCompareExchangePointer(_Inout_ void * volatile *pDst, _In_ void *pValue, _In_ void *pComparand) +{ + return _InterlockedCompareExchangePointer((void * volatile *)pDst, pValue, pComparand); +} + +#else // HOST_64BIT + +#define PalInterlockedExchangePointer(_pDst, _pValue) \ + ((void *)_InterlockedExchange((long volatile *)(_pDst), (long)(size_t)(_pValue))) + +#define PalInterlockedCompareExchangePointer(_pDst, _pValue, _pComparand) \ + ((void *)_InterlockedCompareExchange((long volatile *)(_pDst), (long)(size_t)(_pValue), (long)(size_t)(_pComparand))) + +#endif // HOST_64BIT + +EXTERN_C __declspec(dllimport) unsigned long __stdcall GetLastError(); +FORCEINLINE int PalGetLastError() +{ + return (int)GetLastError(); +} + +EXTERN_C __declspec(dllimport) void __stdcall SetLastError(unsigned long error); +FORCEINLINE void PalSetLastError(int error) +{ + SetLastError((unsigned long)error); +} + +#if defined(HOST_X86) + +EXTERN_C void _mm_pause(); +#pragma intrinsic(_mm_pause) +#define PalYieldProcessor() _mm_pause() + +FORCEINLINE void PalMemoryBarrier() +{ + long Barrier; + _InterlockedOr(&Barrier, 0); +} + +#elif defined(HOST_AMD64) + +EXTERN_C void _mm_pause(); +#pragma intrinsic(_mm_pause) +#define PalYieldProcessor() _mm_pause() + +EXTERN_C void __faststorefence(); +#pragma intrinsic(__faststorefence) +#define PalMemoryBarrier() __faststorefence() + + +#elif defined(HOST_ARM) + +EXTERN_C void __yield(void); +#pragma intrinsic(__yield) +EXTERN_C void __dmb(unsigned int _Type); +#pragma intrinsic(__dmb) +FORCEINLINE void PalYieldProcessor() +{ + __dmb(0xA /* _ARM_BARRIER_ISHST */); + __yield(); +} + +#define PalMemoryBarrier() __dmb(0xF /* _ARM_BARRIER_SY */) + +#elif defined(HOST_ARM64) + +EXTERN_C void __yield(void); +#pragma intrinsic(__yield) +EXTERN_C void __dmb(unsigned int _Type); +#pragma intrinsic(__dmb) +FORCEINLINE void PalYieldProcessor() +{ + __dmb(0xA /* _ARM64_BARRIER_ISHST */); + __yield(); +} + +#define PalMemoryBarrier() __dmb(0xF /* _ARM64_BARRIER_SY */) + +#else +#error Unsupported architecture +#endif + +#define PalDebugBreak() __debugbreak() diff --git a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp new file mode 100644 index 00000000000000..8e0b1590d85850 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp @@ -0,0 +1,582 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Implementation of the Redhawk Platform Abstraction Layer (PAL) library when MinWin is the platform. In this +// case most or all of the import requirements which Redhawk has can be satisfied via a forwarding export to +// some native MinWin library. Therefore most of the work is done in the .def file and there is very little +// code here. +// +// Note that in general we don't want to assume that Windows and Redhawk global definitions can co-exist. +// Since this code must include Windows headers to do its job we can't therefore safely include general +// Redhawk header files. +// +#include "common.h" +#include +#include +#include +#include + +#include "holder.h" + +#define _T(s) L##s +#include "RhConfig.h" + +#define PalRaiseFailFastException RaiseFailFastException + +uint32_t PalEventWrite(REGHANDLE arg1, const EVENT_DESCRIPTOR * arg2, uint32_t arg3, EVENT_DATA_DESCRIPTOR * arg4) +{ + return EventWrite(arg1, arg2, arg3, arg4); +} + +#include "gcenv.h" + + +#define REDHAWK_PALEXPORT extern "C" +#define REDHAWK_PALAPI __stdcall + +// Index for the fiber local storage of the attached thread pointer +static uint32_t g_flsIndex = FLS_OUT_OF_INDEXES; + +// This is called when each *fiber* is destroyed. When the home fiber of a thread is destroyed, +// it means that the thread itself is destroyed. +// Since we receive that notification outside of the Loader Lock, it allows us to safely acquire +// the ThreadStore lock in the RuntimeThreadShutdown. +void __stdcall FiberDetachCallback(void* lpFlsData) +{ + ASSERT(g_flsIndex != FLS_OUT_OF_INDEXES); + ASSERT(lpFlsData == FlsGetValue(g_flsIndex)); + + if (lpFlsData != NULL) + { + // The current fiber is the home fiber of a thread, so the thread is shutting down + RuntimeThreadShutdown(lpFlsData); + } +} + +void InitializeCurrentProcessCpuCount() +{ + DWORD count; + + // If the configuration value has been set, it takes precedence. Otherwise, take into account + // process affinity and CPU quota limit. + + const unsigned int MAX_PROCESSOR_COUNT = 0xffff; + uint32_t configValue; + + if (g_pRhConfig->ReadConfigValue(_T("PROCESSOR_COUNT"), &configValue, true /* decimal */) && + 0 < configValue && configValue <= MAX_PROCESSOR_COUNT) + { + count = configValue; + } + else + { + DWORD_PTR pmask, smask; + + if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask)) + { + count = 1; + } + else + { + pmask &= smask; + count = 0; + + while (pmask) + { + pmask &= (pmask - 1); + count++; + } + + // GetProcessAffinityMask can return pmask=0 and smask=0 on systems with more + // than 64 processors, which would leave us with a count of 0. Since the GC + // expects there to be at least one processor to run on (and thus at least one + // heap), we'll return 64 here if count is 0, since there are likely a ton of + // processors available in that case. + if (count == 0) + count = 64; + } + + JOBOBJECT_CPU_RATE_CONTROL_INFORMATION cpuRateControl; + + if (QueryInformationJobObject(NULL, JobObjectCpuRateControlInformation, &cpuRateControl, + sizeof(cpuRateControl), NULL)) + { + const DWORD HardCapEnabled = JOB_OBJECT_CPU_RATE_CONTROL_ENABLE | JOB_OBJECT_CPU_RATE_CONTROL_HARD_CAP; + const DWORD MinMaxRateEnabled = JOB_OBJECT_CPU_RATE_CONTROL_ENABLE | JOB_OBJECT_CPU_RATE_CONTROL_MIN_MAX_RATE; + DWORD maxRate = 0; + + if ((cpuRateControl.ControlFlags & HardCapEnabled) == HardCapEnabled) + { + maxRate = cpuRateControl.CpuRate; + } + else if ((cpuRateControl.ControlFlags & MinMaxRateEnabled) == MinMaxRateEnabled) + { + maxRate = cpuRateControl.MaxRate; + } + + // The rate is the percentage times 100 + const DWORD MAXIMUM_CPU_RATE = 10000; + + if (0 < maxRate && maxRate < MAXIMUM_CPU_RATE) + { + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + + DWORD cpuLimit = (maxRate * systemInfo.dwNumberOfProcessors + MAXIMUM_CPU_RATE - 1) / MAXIMUM_CPU_RATE; + if (cpuLimit < count) + count = cpuLimit; + } + } + } + + _ASSERTE(count > 0); + g_RhNumberOfProcessors = count; +} + +// The Redhawk PAL must be initialized before any of its exports can be called. Returns true for a successful +// initialization and false on failure. +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalInit() +{ + // We use fiber detach callbacks to run our thread shutdown code because the fiber detach + // callback is made without the OS loader lock + g_flsIndex = FlsAlloc(FiberDetachCallback); + if (g_flsIndex == FLS_OUT_OF_INDEXES) + { + return false; + } + + if (!GCToOSInterface::Initialize()) + { + return false; + } + + InitializeCurrentProcessCpuCount(); + + return true; +} + +// Attach thread to PAL. +// It can be called multiple times for the same thread. +// It fails fast if a different thread was already registered with the current fiber +// or if the thread was already registered with a different fiber. +// Parameters: +// thread - thread to attach +REDHAWK_PALEXPORT void REDHAWK_PALAPI PalAttachThread(void* thread) +{ + void* threadFromCurrentFiber = FlsGetValue(g_flsIndex); + + if (threadFromCurrentFiber != NULL) + { + ASSERT_UNCONDITIONALLY("Multiple threads encountered from a single fiber"); + RhFailFast(); + } + + // Associate the current fiber with the current thread. This makes the current fiber the thread's "home" + // fiber. This fiber is the only fiber allowed to execute managed code on this thread. When this fiber + // is destroyed, we consider the thread to be destroyed. + FlsSetValue(g_flsIndex, thread); +} + +// Detach thread from PAL. +// It fails fast if some other thread value was attached to PAL. +// Parameters: +// thread - thread to detach +// Return: +// true if the thread was detached, false if there was no attached thread +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalDetachThread(void* thread) +{ + ASSERT(g_flsIndex != FLS_OUT_OF_INDEXES); + void* threadFromCurrentFiber = FlsGetValue(g_flsIndex); + + if (threadFromCurrentFiber == NULL) + { + // we've seen this thread, but not this fiber. It must be a "foreign" fiber that was + // borrowing this thread. + return false; + } + + if (threadFromCurrentFiber != thread) + { + ASSERT_UNCONDITIONALLY("Detaching a thread from the wrong fiber"); + RhFailFast(); + } + + if (g_threadExitCallback != NULL) + { + g_threadExitCallback(); + } + + FlsSetValue(g_flsIndex, NULL); + return true; +} + +extern "C" uint64_t PalGetCurrentThreadIdForLogging() +{ + return GetCurrentThreadId(); +} + +#if !defined(USE_PORTABLE_HELPERS) && !defined(FEATURE_RX_THUNKS) +REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalAllocateThunksFromTemplate(_In_ HANDLE hTemplateModule, uint32_t templateRva, size_t templateSize, _Outptr_result_bytebuffer_(templateSize) void** newThunksOut) +{ +#ifdef XBOX_ONE + return E_NOTIMPL; +#else + BOOL success = FALSE; + HANDLE hMap = NULL, hFile = INVALID_HANDLE_VALUE; + + const WCHAR * wszModuleFileName = NULL; + if (PalGetModuleFileName(&wszModuleFileName, hTemplateModule) == 0 || wszModuleFileName == NULL) + return FALSE; + + hFile = CreateFileW(wszModuleFileName, GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + goto cleanup; + + hMap = CreateFileMapping(hFile, NULL, SEC_IMAGE | PAGE_READONLY, 0, 0, NULL); + if (hMap == NULL) + goto cleanup; + + *newThunksOut = MapViewOfFile(hMap, 0, 0, templateRva, templateSize); + success = ((*newThunksOut) != NULL); + +cleanup: + CloseHandle(hMap); + CloseHandle(hFile); + + return success; +#endif +} + +REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalFreeThunksFromTemplate(_In_ void *pBaseAddress) +{ +#ifdef XBOX_ONE + return TRUE; +#else + return UnmapViewOfFile(pBaseAddress); +#endif +} +#endif // !USE_PORTABLE_HELPERS && !FEATURE_RX_THUNKS + +REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalMarkThunksAsValidCallTargets( + void *virtualAddress, + int thunkSize, + int thunksPerBlock, + int thunkBlockSize, + int thunkBlocksPerMapping) +{ + // For CoreRT we are using RWX pages so there is no need for this API for now. + // Once we have a scenario for non-RWX pages we should be able to put the implementation here + return TRUE; +} + +REDHAWK_PALEXPORT uint32_t REDHAWK_PALAPI PalCompatibleWaitAny(UInt32_BOOL alertable, uint32_t timeout, uint32_t handleCount, HANDLE* pHandles, UInt32_BOOL allowReentrantWait) +{ + if (!allowReentrantWait) + { + return WaitForMultipleObjectsEx(handleCount, pHandles, FALSE, timeout, alertable); + } + else + { + DWORD index; + SetLastError(ERROR_SUCCESS); // recommended by MSDN. + HRESULT hr = CoWaitForMultipleHandles(alertable ? COWAIT_ALERTABLE : 0, timeout, handleCount, pHandles, &index); + + switch (hr) + { + case S_OK: + return index; + + case RPC_S_CALLPENDING: + return WAIT_TIMEOUT; + + default: + SetLastError(HRESULT_CODE(hr)); + return WAIT_FAILED; + } + } +} + +REDHAWK_PALEXPORT void REDHAWK_PALAPI PalSleep(uint32_t milliseconds) +{ + return Sleep(milliseconds); +} + +REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalSwitchToThread() +{ + return SwitchToThread(); +} + +REDHAWK_PALEXPORT HANDLE REDHAWK_PALAPI PalCreateEventW(_In_opt_ LPSECURITY_ATTRIBUTES pEventAttributes, UInt32_BOOL manualReset, UInt32_BOOL initialState, _In_opt_z_ LPCWSTR pName) +{ + return CreateEventW(pEventAttributes, manualReset, initialState, pName); +} + +REDHAWK_PALEXPORT _Success_(return) bool REDHAWK_PALAPI PalGetThreadContext(HANDLE hThread, _Out_ PAL_LIMITED_CONTEXT * pCtx) +{ + CONTEXT win32ctx; + + win32ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_EXCEPTION_REQUEST; + + if (!GetThreadContext(hThread, &win32ctx)) + return false; + + // The CONTEXT_SERVICE_ACTIVE and CONTEXT_EXCEPTION_ACTIVE output flags indicate we suspended the thread + // at a point where the kernel cannot guarantee a completely accurate context. We'll fail the request in + // this case (which should force our caller to resume the thread and try again -- since this is a fairly + // narrow window we're highly likely to succeed next time). + // Note: in some cases (x86 WOW64, ARM32 on ARM64) the OS will not set the CONTEXT_EXCEPTION_REPORTING flag + // if the thread is executing in kernel mode (i.e. in the middle of a syscall or exception handling). + // Therefore, we should treat the absence of the CONTEXT_EXCEPTION_REPORTING flag as an indication that + // it is not safe to manipulate with the current state of the thread context. + if ((win32ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) == 0 || + (win32ctx.ContextFlags & (CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE))) + return false; + +#ifdef HOST_X86 + pCtx->IP = win32ctx.Eip; + pCtx->Rsp = win32ctx.Esp; + pCtx->Rbp = win32ctx.Ebp; + pCtx->Rdi = win32ctx.Edi; + pCtx->Rsi = win32ctx.Esi; + pCtx->Rax = win32ctx.Eax; + pCtx->Rbx = win32ctx.Ebx; +#elif defined(HOST_AMD64) + pCtx->IP = win32ctx.Rip; + pCtx->Rsp = win32ctx.Rsp; + pCtx->Rbp = win32ctx.Rbp; + pCtx->Rdi = win32ctx.Rdi; + pCtx->Rsi = win32ctx.Rsi; + pCtx->Rax = win32ctx.Rax; + pCtx->Rbx = win32ctx.Rbx; + pCtx->R12 = win32ctx.R12; + pCtx->R13 = win32ctx.R13; + pCtx->R14 = win32ctx.R14; + pCtx->R15 = win32ctx.R15; +#elif defined(HOST_ARM) + pCtx->IP = win32ctx.Pc; + pCtx->R0 = win32ctx.R0; + pCtx->R4 = win32ctx.R4; + pCtx->R5 = win32ctx.R5; + pCtx->R6 = win32ctx.R6; + pCtx->R7 = win32ctx.R7; + pCtx->R8 = win32ctx.R8; + pCtx->R9 = win32ctx.R9; + pCtx->R10 = win32ctx.R10; + pCtx->R11 = win32ctx.R11; + pCtx->SP = win32ctx.Sp; + pCtx->LR = win32ctx.Lr; +#elif defined(HOST_ARM64) + pCtx->IP = win32ctx.Pc; + pCtx->X0 = win32ctx.X0; + pCtx->X1 = win32ctx.X1; + // TODO: Copy X2-X7 when we start supporting HVA's + pCtx->X19 = win32ctx.X19; + pCtx->X20 = win32ctx.X20; + pCtx->X21 = win32ctx.X21; + pCtx->X22 = win32ctx.X22; + pCtx->X23 = win32ctx.X23; + pCtx->X24 = win32ctx.X24; + pCtx->X25 = win32ctx.X25; + pCtx->X26 = win32ctx.X26; + pCtx->X27 = win32ctx.X27; + pCtx->X28 = win32ctx.X28; + pCtx->SP = win32ctx.Sp; + pCtx->LR = win32ctx.Lr; + pCtx->FP = win32ctx.Fp; +#else +#error Unsupported platform +#endif + return true; +} + + +REDHAWK_PALEXPORT uint32_t REDHAWK_PALAPI PalHijack(HANDLE hThread, _In_ PalHijackCallback callback, _In_opt_ void* pCallbackContext) +{ + if (hThread == INVALID_HANDLE_VALUE) + { + return (uint32_t)E_INVALIDARG; + } + + if (SuspendThread(hThread) == (DWORD)-1) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + PAL_LIMITED_CONTEXT ctx; + HRESULT result; + if (!PalGetThreadContext(hThread, &ctx)) + { + result = HRESULT_FROM_WIN32(GetLastError()); + } + else + { + result = callback(hThread, &ctx, pCallbackContext) ? S_OK : E_FAIL; + } + + ResumeThread(hThread); + + return result; +} + +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartBackgroundWork(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext, BOOL highPriority) +{ + HANDLE hThread = CreateThread( + NULL, + 0, + (LPTHREAD_START_ROUTINE)callback, + pCallbackContext, + highPriority ? CREATE_SUSPENDED : 0, + NULL); + + if (hThread == NULL) + return false; + + if (highPriority) + { + SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST); + ResumeThread(hThread); + } + + CloseHandle(hThread); + return true; +} + +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartBackgroundGCThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext) +{ + return PalStartBackgroundWork(callback, pCallbackContext, FALSE); +} + +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartFinalizerThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext) +{ + return PalStartBackgroundWork(callback, pCallbackContext, TRUE); +} + +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalEventEnabled(REGHANDLE regHandle, _In_ const EVENT_DESCRIPTOR* eventDescriptor) +{ + return !!EventEnabled(regHandle, eventDescriptor); +} + +REDHAWK_PALEXPORT void REDHAWK_PALAPI PalTerminateCurrentProcess(uint32_t arg2) +{ + TerminateProcess(GetCurrentProcess(), arg2); +} + +REDHAWK_PALEXPORT HANDLE REDHAWK_PALAPI PalGetModuleHandleFromPointer(_In_ void* pointer) +{ + // CoreRT is not designed to be unloadable today. Use GET_MODULE_HANDLE_EX_FLAG_PIN to prevent + // the module from ever unloading. + + HMODULE module; + if (!GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, + (LPCWSTR)pointer, + &module)) + { + return NULL; + } + + return (HANDLE)module; +} + +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalIsAvxEnabled() +{ + typedef DWORD64(WINAPI* PGETENABLEDXSTATEFEATURES)(); + PGETENABLEDXSTATEFEATURES pfnGetEnabledXStateFeatures = NULL; + + HMODULE hMod = LoadLibraryExW(L"kernel32", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (hMod == NULL) + return FALSE; + + pfnGetEnabledXStateFeatures = (PGETENABLEDXSTATEFEATURES)GetProcAddress(hMod, "GetEnabledXStateFeatures"); + + if (pfnGetEnabledXStateFeatures == NULL) + { + return FALSE; + } + + DWORD64 FeatureMask = pfnGetEnabledXStateFeatures(); + if ((FeatureMask & XSTATE_MASK_AVX) == 0) + { + return FALSE; + } + + return TRUE; +} + +REDHAWK_PALEXPORT void* REDHAWK_PALAPI PalAddVectoredExceptionHandler(uint32_t firstHandler, _In_ PVECTORED_EXCEPTION_HANDLER vectoredHandler) +{ + return AddVectoredExceptionHandler(firstHandler, vectoredHandler); +} + +REDHAWK_PALEXPORT void PalPrintFatalError(const char* message) +{ + // Write the message using lowest-level OS API available. This is used to print the stack overflow + // message, so there is not much that can be done here. + DWORD dwBytesWritten; + WriteFile(GetStdHandle(STD_ERROR_HANDLE), message, (DWORD)strlen(message), &dwBytesWritten, NULL); +} + +REDHAWK_PALEXPORT _Ret_maybenull_ _Post_writable_byte_size_(size) void* REDHAWK_PALAPI PalVirtualAlloc(_In_opt_ void* pAddress, uintptr_t size, uint32_t allocationType, uint32_t protect) +{ + return VirtualAlloc(pAddress, size, allocationType, protect); +} + +#pragma warning (push) +#pragma warning (disable:28160) // warnings about invalid potential parameter combinations that would cause VirtualFree to fail - those are asserted for below +REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalVirtualFree(_In_ void* pAddress, uintptr_t size, uint32_t freeType) +{ + assert(((freeType & MEM_RELEASE) != MEM_RELEASE) || size == 0); + assert((freeType & (MEM_RELEASE | MEM_DECOMMIT)) != (MEM_RELEASE | MEM_DECOMMIT)); + assert(freeType != 0); + + return VirtualFree(pAddress, size, freeType); +} +#pragma warning (pop) + +REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalVirtualProtect(_In_ void* pAddress, uintptr_t size, uint32_t protect) +{ + DWORD oldProtect; + return VirtualProtect(pAddress, size, protect, &oldProtect); +} + +REDHAWK_PALEXPORT _Ret_maybenull_ void* REDHAWK_PALAPI PalSetWerDataBuffer(_In_ void* pNewBuffer) +{ + static void* pBuffer; + return InterlockedExchangePointer(&pBuffer, pNewBuffer); +} + +#if defined(HOST_ARM64) + +#include "IntrinsicConstants.h" + +REDHAWK_PALIMPORT void REDHAWK_PALAPI PAL_GetCpuCapabilityFlags(int* flags) +{ + *flags = 0; + + // FP and SIMD support are enabled by default + *flags |= ARM64IntrinsicConstants_ArmBase; + *flags |= ARM64IntrinsicConstants_ArmBase_Arm64; + *flags |= ARM64IntrinsicConstants_AdvSimd; + *flags |= ARM64IntrinsicConstants_AdvSimd_Arm64; + + if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) + { + *flags |= ARM64IntrinsicConstants_Aes; + *flags |= ARM64IntrinsicConstants_Sha1; + *flags |= ARM64IntrinsicConstants_Sha256; + } + + if (IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)) + { + *flags |= ARM64IntrinsicConstants_Crc32; + *flags |= ARM64IntrinsicConstants_Crc32_Arm64; + } + + if (IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE)) + { + *flags |= ARM64IntrinsicConstants_Atomics; + } +} + +#endif diff --git a/src/coreclr/nativeaot/Runtime/yieldprocessornormalized.cpp b/src/coreclr/nativeaot/Runtime/yieldprocessornormalized.cpp new file mode 100644 index 00000000000000..83fd70bd49dcbd --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/yieldprocessornormalized.cpp @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" +#include "gcenv.h" +#include "gcheaputilities.h" +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "DebugMacrosExt.h" +#include "PalRedhawkCommon.h" +#include "PalRedhawk.h" +#include "rhassert.h" +#include "slist.h" +#include "volatile.h" +#include "yieldprocessornormalized.h" + +#define ULONGLONG int64_t + +static Volatile s_isYieldProcessorNormalizedInitialized = false; +static CrstStatic s_initializeYieldProcessorNormalizedCrst; + +// Defaults are for when InitializeYieldProcessorNormalized has not yet been called or when no measurement is done, and are +// tuned for Skylake processors +unsigned int g_yieldsPerNormalizedYield = 1; // current value is for Skylake processors, this is expected to be ~8 for pre-Skylake +unsigned int g_optimalMaxNormalizedYieldsPerSpinIteration = 7; + +void InitializeYieldProcessorNormalizedCrst() +{ + WRAPPER_NO_CONTRACT; + s_initializeYieldProcessorNormalizedCrst.Init(CrstYieldProcessorNormalized); +} + +static void InitializeYieldProcessorNormalized() +{ + WRAPPER_NO_CONTRACT; + + CrstHolder lock(&s_initializeYieldProcessorNormalizedCrst); + + if (s_isYieldProcessorNormalizedInitialized) + { + return; + } + + // Intel pre-Skylake processor: measured typically 14-17 cycles per yield + // Intel post-Skylake processor: measured typically 125-150 cycles per yield + const int MeasureDurationMs = 10; + const int NsPerSecond = 1000 * 1000 * 1000; + + LARGE_INTEGER li; + if (!PalQueryPerformanceFrequency(&li) || (ULONGLONG)li.QuadPart < 1000 / MeasureDurationMs) + { + // High precision clock not available or clock resolution is too low, resort to defaults + s_isYieldProcessorNormalizedInitialized = true; + return; + } + ULONGLONG ticksPerSecond = li.QuadPart; + + // Measure the nanosecond delay per yield + ULONGLONG measureDurationTicks = ticksPerSecond / (1000 / MeasureDurationMs); + unsigned int yieldCount = 0; + PalQueryPerformanceCounter(&li); + ULONGLONG startTicks = li.QuadPart; + ULONGLONG elapsedTicks; + do + { + // On some systems, querying the high performance counter has relatively significant overhead. Do enough yields to mask + // the timing overhead. Assuming one yield has a delay of MinNsPerNormalizedYield, 1000 yields would have a delay in the + // low microsecond range. + for (int i = 0; i < 1000; ++i) + { + System_YieldProcessor(); + } + yieldCount += 1000; + + PalQueryPerformanceCounter(&li); + ULONGLONG nowTicks = li.QuadPart; + elapsedTicks = nowTicks - startTicks; + } while (elapsedTicks < measureDurationTicks); + double nsPerYield = (double)elapsedTicks * NsPerSecond / ((double)yieldCount * ticksPerSecond); + if (nsPerYield < 1) + { + nsPerYield = 1; + } + + // Calculate the number of yields required to span the duration of a normalized yield. Since nsPerYield is at least 1, this + // value is naturally limited to MinNsPerNormalizedYield. + int yieldsPerNormalizedYield = (int)(MinNsPerNormalizedYield / nsPerYield + 0.5); + if (yieldsPerNormalizedYield < 1) + { + yieldsPerNormalizedYield = 1; + } + _ASSERTE(yieldsPerNormalizedYield <= (int)MinNsPerNormalizedYield); + + // Calculate the maximum number of yields that would be optimal for a late spin iteration. Typically, we would not want to + // spend excessive amounts of time (thousands of cycles) doing only YieldProcessor, as SwitchToThread/Sleep would do a + // better job of allowing other work to run. + int optimalMaxNormalizedYieldsPerSpinIteration = + (int)(NsPerOptimalMaxSpinIterationDuration / (yieldsPerNormalizedYield * nsPerYield) + 0.5); + if (optimalMaxNormalizedYieldsPerSpinIteration < 1) + { + optimalMaxNormalizedYieldsPerSpinIteration = 1; + } + + g_yieldsPerNormalizedYield = yieldsPerNormalizedYield; + g_optimalMaxNormalizedYieldsPerSpinIteration = optimalMaxNormalizedYieldsPerSpinIteration; + s_isYieldProcessorNormalizedInitialized = true; + + GCHeapUtilities::GetGCHeap()->SetYieldProcessorScalingFactor((float)yieldsPerNormalizedYield); +} + +void EnsureYieldProcessorNormalizedInitialized() +{ + WRAPPER_NO_CONTRACT; + + if (!s_isYieldProcessorNormalizedInitialized) + { + InitializeYieldProcessorNormalized(); + } +} diff --git a/src/coreclr/nativeaot/Runtime/yieldprocessornormalized.h b/src/coreclr/nativeaot/Runtime/yieldprocessornormalized.h new file mode 100644 index 00000000000000..8c74bf3cfe3002 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/yieldprocessornormalized.h @@ -0,0 +1,229 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include + +// Undefine YieldProcessor to encourage using the normalized versions below instead. System_YieldProcessor() can be used where +// the intention is to use the system-default implementation of YieldProcessor(). +#define HAS_SYSTEM_YIELDPROCESSOR +FORCEINLINE void System_YieldProcessor() { PalYieldProcessor(); } +#ifdef YieldProcessor +#undef YieldProcessor +#endif +#define YieldProcessor Dont_Use_YieldProcessor +#ifdef PalYieldProcessor +#undef PalYieldProcessor +#endif +#define PalYieldProcessor Dont_Use_PalYieldProcessor + +#define SIZE_T uintptr_t + +const unsigned int MinNsPerNormalizedYield = 37; // measured typically 37-46 on post-Skylake +const unsigned int NsPerOptimalMaxSpinIterationDuration = 272; // approx. 900 cycles, measured 281 on pre-Skylake, 263 on post-Skylake + +extern unsigned int g_yieldsPerNormalizedYield; +extern unsigned int g_optimalMaxNormalizedYieldsPerSpinIteration; + +void InitializeYieldProcessorNormalizedCrst(); +void EnsureYieldProcessorNormalizedInitialized(); + +class YieldProcessorNormalizationInfo +{ +private: + unsigned int yieldsPerNormalizedYield; + unsigned int optimalMaxNormalizedYieldsPerSpinIteration; + unsigned int optimalMaxYieldsPerSpinIteration; + +public: + YieldProcessorNormalizationInfo() + : yieldsPerNormalizedYield(g_yieldsPerNormalizedYield), + optimalMaxNormalizedYieldsPerSpinIteration(g_optimalMaxNormalizedYieldsPerSpinIteration), + optimalMaxYieldsPerSpinIteration(yieldsPerNormalizedYield * optimalMaxNormalizedYieldsPerSpinIteration) + { + } + + friend void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &); + friend void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &, unsigned int); + friend void YieldProcessorNormalizedForPreSkylakeCount(const YieldProcessorNormalizationInfo &, unsigned int); + friend void YieldProcessorWithBackOffNormalized(const YieldProcessorNormalizationInfo &, unsigned int); +}; + +// See YieldProcessorNormalized() for preliminary info. Typical usage: +// if (!condition) +// { +// YieldProcessorNormalizationInfo normalizationInfo; +// do +// { +// YieldProcessorNormalized(normalizationInfo); +// } while (!condition); +// } +FORCEINLINE void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &normalizationInfo) +{ + unsigned int n = normalizationInfo.yieldsPerNormalizedYield; + _ASSERTE(n != 0); + do + { + System_YieldProcessor(); + } while (--n != 0); +} + +// Delays execution of the current thread for a short duration. Unlike YieldProcessor(), an effort is made to normalize the +// delay across processors. The actual delay may be meaningful in several ways, including but not limited to the following: +// - The delay should be long enough that a tiny spin-wait like the following has a decent likelihood of observing a new value +// for the condition (when changed by a different thread) on each iteration, otherwise it may unnecessary increase CPU usage +// and decrease scalability of the operation. +// while(!condition) +// { +// YieldProcessorNormalized(); +// } +// - The delay should be short enough that a tiny spin-wait like above would not miss multiple cross-thread changes to the +// condition, otherwise it may unnecessarily increase latency of the operation +// - In reasonably short spin-waits, the actual delay may not matter much. In unreasonably long spin-waits that progress in +// yield count per iteration for each failed check of the condition, the progression can significantly magnify the second +// issue above on later iterations. +// - This function and variants are intended to provide a decent balance between the above issues, as ideal solutions to each +// issue have trade-offs between them. If latency of the operation is far more important in the scenario, consider using +// System_YieldProcessor() instead, which would issue a delay that is typically <= the delay issued by this method. +FORCEINLINE void YieldProcessorNormalized() +{ + YieldProcessorNormalized(YieldProcessorNormalizationInfo()); +} + +// See YieldProcessorNormalized(count) for preliminary info. Typical usage: +// if (!moreExpensiveCondition) +// { +// YieldProcessorNormalizationInfo normalizationInfo; +// do +// { +// YieldProcessorNormalized(normalizationInfo, 2); +// } while (!moreExpensiveCondition); +// } +FORCEINLINE void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &normalizationInfo, unsigned int count) +{ + _ASSERTE(count != 0); + + if (sizeof(SIZE_T) <= sizeof(unsigned int)) + { + // On platforms with a small SIZE_T, prevent overflow on the multiply below. normalizationInfo.yieldsPerNormalizedYield + // is limited to MinNsPerNormalizedYield by InitializeYieldProcessorNormalized(). + const unsigned int MaxCount = UINT_MAX / MinNsPerNormalizedYield; + if (count > MaxCount) + { + count = MaxCount; + } + } + + SIZE_T n = (SIZE_T)count * normalizationInfo.yieldsPerNormalizedYield; + _ASSERTE(n != 0); + do + { + System_YieldProcessor(); + } while (--n != 0); +} + +// See YieldProcessorNormalized() for preliminary info. This function repeats the delay 'count' times. This overload is +// preferred over the single-count overload when multiple yields are desired per spin-wait iteration. Typical usage: +// while(!moreExpensiveCondition) +// { +// YieldProcessorNormalized(2); +// } +FORCEINLINE void YieldProcessorNormalized(unsigned int count) +{ + YieldProcessorNormalized(YieldProcessorNormalizationInfo(), count); +} + +// Please DO NOT use this function in new code! See YieldProcessorNormalizedForPreSkylakeCount(preSkylakeCount) for preliminary +// info. Typical usage: +// if (!condition) +// { +// YieldProcessorNormalizationInfo normalizationInfo; +// do +// { +// YieldProcessorNormalizedForPreSkylakeCount(normalizationInfo, 100); +// } while (!condition); +// } +FORCEINLINE void YieldProcessorNormalizedForPreSkylakeCount( + const YieldProcessorNormalizationInfo &normalizationInfo, + unsigned int preSkylakeCount) +{ + _ASSERTE(preSkylakeCount != 0); + + if (sizeof(SIZE_T) <= sizeof(unsigned int)) + { + // On platforms with a small SIZE_T, prevent overflow on the multiply below. normalizationInfo.yieldsPerNormalizedYield + // is limited to MinNsPerNormalizedYield by InitializeYieldProcessorNormalized(). + const unsigned int MaxCount = UINT_MAX / MinNsPerNormalizedYield; + if (preSkylakeCount > MaxCount) + { + preSkylakeCount = MaxCount; + } + } + + const unsigned int PreSkylakeCountToSkylakeCountDivisor = 8; + SIZE_T n = (SIZE_T)preSkylakeCount * normalizationInfo.yieldsPerNormalizedYield / PreSkylakeCountToSkylakeCountDivisor; + if (n == 0) + { + n = 1; + } + do + { + System_YieldProcessor(); + } while (--n != 0); +} + +// Please DO NOT use this function in new code! This function is to be used for old spin-wait loops that have not been retuned +// for recent processors, and especially where the yield count may be unreasonably high. The function scales the yield count in +// an attempt to normalize the total delay across processors, to approximately the total delay that would be issued on a +// pre-Skylake processor. New code should be tuned with YieldProcessorNormalized() or variants instead. Typical usage: +// while(!condition) +// { +// YieldProcessorNormalizedForPreSkylakeCount(100); +// } +FORCEINLINE void YieldProcessorNormalizedForPreSkylakeCount(unsigned int preSkylakeCount) +{ + YieldProcessorNormalizedForPreSkylakeCount(YieldProcessorNormalizationInfo(), preSkylakeCount); +} + +// See YieldProcessorNormalized() for preliminary info. This function is to be used when there is a decent possibility that the +// condition would not be satisfied within a short duration. The current implementation increases the delay per spin-wait +// iteration exponentially up to a limit. Typical usage: +// if (!conditionThatMayNotBeSatisfiedSoon) +// { +// YieldProcessorNormalizationInfo normalizationInfo; +// do +// { +// YieldProcessorWithBackOffNormalized(normalizationInfo); // maybe Sleep(0) occasionally +// } while (!conditionThatMayNotBeSatisfiedSoon); +// } +FORCEINLINE void YieldProcessorWithBackOffNormalized( + const YieldProcessorNormalizationInfo &normalizationInfo, + unsigned int spinIteration) +{ + // normalizationInfo.optimalMaxNormalizedYieldsPerSpinIteration cannot exceed the value below based on calculations done in + // InitializeYieldProcessorNormalized() + const unsigned int MaxOptimalMaxNormalizedYieldsPerSpinIteration = + NsPerOptimalMaxSpinIterationDuration * 3 / (MinNsPerNormalizedYield * 2) + 1; + _ASSERTE(normalizationInfo.optimalMaxNormalizedYieldsPerSpinIteration <= MaxOptimalMaxNormalizedYieldsPerSpinIteration); + + // This shift value should be adjusted based on the asserted condition below + const uint8_t MaxShift = 3; + static_assert(((unsigned int)1 << (MaxShift + 1)) >= MaxOptimalMaxNormalizedYieldsPerSpinIteration, ""); + + unsigned int n; + if (spinIteration <= MaxShift && + ((unsigned int)1 << spinIteration) < normalizationInfo.optimalMaxNormalizedYieldsPerSpinIteration) + { + n = ((unsigned int)1 << spinIteration) * normalizationInfo.yieldsPerNormalizedYield; + } + else + { + n = normalizationInfo.optimalMaxYieldsPerSpinIteration; + } + _ASSERTE(n != 0); + do + { + System_YieldProcessor(); + } while (--n != 0); +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Unix.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Unix.xml new file mode 100644 index 00000000000000..d136958e03c47c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Unix.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.xml new file mode 100644 index 00000000000000..3aeeaf70faefd9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs new file mode 100644 index 00000000000000..8fefdd42132fee --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs @@ -0,0 +1,150 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// We want the Debug.WriteLine statements below to actually do something. +#define DEBUG + +using System; +using System.Text; +using System.Runtime; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Reflection; + +using Internal.Runtime.Augments; + +namespace Internal.DeveloperExperience +{ + [System.Runtime.CompilerServices.ReflectionBlocked] + public class DeveloperExperience + { + /// + /// Check the AppCompat switch 'Diagnostics.DisableMetadataStackTraceResolution'. + /// Some customers use DIA-based tooling to translate stack traces in the raw format + /// (module)+RVA - for them, stack trace and reflection metadata-based resolution + /// constitutes technically a regression because these two resolution methods today cannot + /// provide file name and line number information; PDB-based tooling can easily do that + /// based on the RVA information. + /// + private static bool IsMetadataStackTraceResolutionDisabled() + { + bool disableMetadata = false; + AppContext.TryGetSwitch("Diagnostics.DisableMetadataStackTraceResolution", out disableMetadata); + return disableMetadata; + } + + public virtual string CreateStackTraceString(IntPtr ip, bool includeFileInfo) + { + if (!IsMetadataStackTraceResolutionDisabled()) + { + StackTraceMetadataCallbacks stackTraceCallbacks = RuntimeAugments.StackTraceCallbacksIfAvailable; + if (stackTraceCallbacks != null) + { + IntPtr methodStart = RuntimeImports.RhFindMethodStartAddress(ip); + if (methodStart != IntPtr.Zero) + { + string methodName = stackTraceCallbacks.TryGetMethodNameFromStartAddress(methodStart); + if (methodName != null) + { + if (ip != methodStart) + { + methodName += " + 0x" + (ip.ToInt64() - methodStart.ToInt64()).ToString("x"); + } + return methodName; + } + } + } + } + + // If we don't have precise information, try to map it at least back to the right module. + string moduleFullFileName = RuntimeAugments.TryGetFullPathToApplicationModule(ip, out IntPtr moduleBase); + + // Without any callbacks or the ability to map ip correctly we better admit that we don't know + if (string.IsNullOrEmpty(moduleFullFileName)) + { + return ""; + } + + StringBuilder sb = new StringBuilder(); + string fileNameWithoutExtension = GetFileNameWithoutExtension(moduleFullFileName); + int rva = (int)(ip.ToInt64() - moduleBase.ToInt64()); + sb.Append(fileNameWithoutExtension); + sb.Append("!+0x"); + sb.Append(rva.ToString("x")); + return sb.ToString(); + } + + public virtual void TryGetSourceLineInfo(IntPtr ip, out string fileName, out int lineNumber, out int columnNumber) + { + fileName = null; + lineNumber = 0; + columnNumber = 0; + } + + public virtual void TryGetILOffsetWithinMethod(IntPtr ip, out int ilOffset) + { + ilOffset = StackFrame.OFFSET_UNKNOWN; + } + + /// + /// Makes reasonable effort to get the MethodBase reflection info. Returns null if it can't. + /// + public virtual void TryGetMethodBase(IntPtr methodStartAddress, out MethodBase method) + { + ReflectionExecutionDomainCallbacks reflectionCallbacks = RuntimeAugments.CallbacksIfAvailable; + method = null; + if (reflectionCallbacks != null) + { + method = reflectionCallbacks.GetMethodBaseFromStartAddressIfAvailable(methodStartAddress); + } + } + + public virtual bool OnContractFailure(string? stackTrace, ContractFailureKind contractFailureKind, string? displayMessage, string userMessage, string conditionText, Exception innerException) + { + Debug.WriteLine("Assertion failed: " + (displayMessage == null ? "" : displayMessage)); + if (Debugger.IsAttached) + Debugger.Break(); + return false; + } + + public static DeveloperExperience Default + { + get + { + DeveloperExperience result = s_developerExperience; + if (result == null) + return new DeveloperExperience(); // Provide the bare-bones default if a custom one hasn't been supplied. + return result; + } + + set + { + s_developerExperience = value; + } + } + + private static string GetFileNameWithoutExtension(string path) + { + path = GetFileName(path); + int i; + if ((i = path.LastIndexOf('.')) == -1) + return path; // No path extension found + else + return path.Substring(0, i); + } + + private static string GetFileName(string path) + { + int length = path.Length; + for (int i = length; --i >= 0;) + { + char ch = path[i]; + if (ch == '/' || ch == '\\' || ch == ':') + return path.Substring(i + 1, length - i - 1); + } + return path; + } + + private static DeveloperExperience s_developerExperience; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs new file mode 100644 index 00000000000000..2a74b9560d4f48 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs @@ -0,0 +1,104 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// The algoritm to choose the default comparer is duplicated in the IL compiler. The compiler will replace the code within +// Comparer.Create method with more specific implementation based on what sort of type is being compared where possible. + +using System; +using System.Collections.Generic; +using System.Runtime; +using System.Runtime.CompilerServices; + +using Internal.Runtime.Augments; + +namespace Internal.IntrinsicSupport +{ + internal static class ComparerHelpers + { + private static bool ImplementsIComparable(RuntimeTypeHandle t) + { + EETypePtr objectType = t.ToEETypePtr(); + EETypePtr icomparableType = typeof(IComparable<>).TypeHandle.ToEETypePtr(); + int interfaceCount = objectType.Interfaces.Count; + for (int i = 0; i < interfaceCount; i++) + { + EETypePtr interfaceType = objectType.Interfaces[i]; + + if (!interfaceType.IsGeneric) + continue; + + if (interfaceType.GenericDefinition == icomparableType) + { + var instantiation = interfaceType.Instantiation; + if (instantiation.Length != 1) + continue; + + if (objectType.IsValueType) + { + if (instantiation[0] == objectType) + { + return true; + } + } + else if (RuntimeImports.AreTypesAssignable(objectType, instantiation[0])) + { + return true; + } + } + } + + return false; + } + + internal static object GetComparer(RuntimeTypeHandle t) + { + RuntimeTypeHandle comparerType; + RuntimeTypeHandle openComparerType = default(RuntimeTypeHandle); + RuntimeTypeHandle comparerTypeArgument = default(RuntimeTypeHandle); + + if (RuntimeAugments.IsNullable(t)) + { + RuntimeTypeHandle nullableType = RuntimeAugments.GetNullableType(t); + if (ImplementsIComparable(nullableType)) + { + openComparerType = typeof(NullableComparer<>).TypeHandle; + comparerTypeArgument = nullableType; + } + } + if (EqualityComparerHelpers.IsEnum(t)) + { + openComparerType = typeof(EnumComparer<>).TypeHandle; + comparerTypeArgument = t; + } + + if (openComparerType.Equals(default(RuntimeTypeHandle))) + { + if (ImplementsIComparable(t)) + { + openComparerType = typeof(GenericComparer<>).TypeHandle; + comparerTypeArgument = t; + } + else + { + openComparerType = typeof(ObjectComparer<>).TypeHandle; + comparerTypeArgument = t; + } + } + + bool success = RuntimeAugments.TypeLoaderCallbacks.TryGetConstructedGenericTypeForComponents(openComparerType, new RuntimeTypeHandle[] { comparerTypeArgument }, out comparerType); + if (!success) + { + Environment.FailFast("Unable to create comparer"); + } + + return RuntimeAugments.NewObject(comparerType); + } + + // This one is an intrinsic that is used to make enum comparisions more efficient. + [Intrinsic] + internal static int EnumOnlyCompare(T x, T y) where T : struct, Enum + { + return x.CompareTo(y); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs new file mode 100644 index 00000000000000..1339c917fe9f94 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// The algoritm to choose the default equality comparer is duplicated in the IL compiler. The compiler will replace the code within +// EqualityComparer.Create method with more specific implementation based on what sort of type is being compared where possible. +// +// In addition, there are a set of generic functions which are used by Array.IndexOf to perform equality checking +// in a similar manner. Array.IndexOf uses these functions instead of the EqualityComparer infrastructure because constructing +// a full EqualityComparer has substantial size costs due to Array.IndexOf use within all arrays. + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Internal.Runtime.Augments; + +namespace Internal.IntrinsicSupport +{ + internal static class EqualityComparerHelpers + { + private static bool ImplementsIEquatable(RuntimeTypeHandle t) + { + EETypePtr objectType = t.ToEETypePtr(); + EETypePtr iequatableType = typeof(IEquatable<>).TypeHandle.ToEETypePtr(); + int interfaceCount = objectType.Interfaces.Count; + for (int i = 0; i < interfaceCount; i++) + { + EETypePtr interfaceType = objectType.Interfaces[i]; + + if (!interfaceType.IsGeneric) + continue; + + if (interfaceType.GenericDefinition == iequatableType) + { + var instantiation = interfaceType.Instantiation; + + if (instantiation.Length != 1) + continue; + + if (instantiation[0] == objectType) + { + return true; + } + } + } + + return false; + } + + internal static bool IsEnum(RuntimeTypeHandle t) + { + return t.ToEETypePtr().IsEnum; + } + + // this function utilizes the template type loader to generate new + // EqualityComparer types on the fly + internal static object GetComparer(RuntimeTypeHandle t) + { + RuntimeTypeHandle comparerType; + RuntimeTypeHandle openComparerType = default(RuntimeTypeHandle); + RuntimeTypeHandle comparerTypeArgument = default(RuntimeTypeHandle); + + if (RuntimeAugments.IsNullable(t)) + { + RuntimeTypeHandle nullableType = RuntimeAugments.GetNullableType(t); + if (ImplementsIEquatable(nullableType)) + { + openComparerType = typeof(NullableEqualityComparer<>).TypeHandle; + comparerTypeArgument = nullableType; + } + } + if (IsEnum(t)) + { + openComparerType = typeof(EnumEqualityComparer<>).TypeHandle; + comparerTypeArgument = t; + } + + if (openComparerType.Equals(default(RuntimeTypeHandle))) + { + if (ImplementsIEquatable(t)) + { + openComparerType = typeof(GenericEqualityComparer<>).TypeHandle; + comparerTypeArgument = t; + } + else + { + openComparerType = typeof(ObjectEqualityComparer<>).TypeHandle; + comparerTypeArgument = t; + } + } + + bool success = RuntimeAugments.TypeLoaderCallbacks.TryGetConstructedGenericTypeForComponents(openComparerType, new RuntimeTypeHandle[] { comparerTypeArgument }, out comparerType); + if (!success) + { + Environment.FailFast("Unable to create comparer"); + } + + return RuntimeAugments.NewObject(comparerType); + } + + //----------------------------------------------------------------------- + // Redirection target functions for redirecting behavior of Array.IndexOf + //----------------------------------------------------------------------- + + // This one is an intrinsic that is used to make enum comparisions more efficient. + [Intrinsic] + internal static bool EnumOnlyEquals(T x, T y) where T : struct + { + return x.Equals(y); + } + + private static bool StructOnlyEqualsIEquatable(T x, T y) where T : IEquatable + { + return x.Equals(y); + } + + private static bool StructOnlyEqualsNullable(Nullable x, Nullable y) where T : struct, IEquatable + { + if (x.HasValue) + { + if (y.HasValue) + return x.Value.Equals(y.Value); + return false; + } + + if (y.HasValue) + return false; + + return true; + } + + // These functions look odd, as they are part of a complex series of compiler intrinsics + // designed to produce very high quality code for equality comparison cases without utilizing + // reflection like other platforms. The major complication is that the specification of + // IndexOf is that it is supposed to use IEquatable if possible, but that requirement + // cannot be expressed in IL directly due to the lack of constraints. + // Instead, specialization at call time is used within the compiler. + // + // General Approach + // - Perform fancy redirection for EqualityComparerHelpers.GetComparerForReferenceTypesOnly(). If T is a reference + // type or UniversalCanon, have this redirect to EqualityComparer.get_Default, Otherwise, use + // the function as is. (will return null in that case) + // - Change the contents of the IndexOf functions to have a pair of loops. One for if + // GetComparerForReferenceTypesOnly returns null, and one for when it does not. + // - If it does not return null, call the EqualityComparer code. + // - If it does return null, use a special function StructOnlyEquals(). + // - Calls to that function result in calls to a pair of helper function in + // EqualityComparerHelpers (StructOnlyEqualsIEquatable, or StructOnlyEqualsNullable) + // depending on whether or not they are the right function to call. + // - The end result is that in optimized builds, we have the same single function compiled size + // characteristics that the old EqualsOnlyComparer.Equals function had, but we maintain + // correctness as well. + [Intrinsic] + internal static EqualityComparer GetComparerForReferenceTypesOnly() + { + return EqualityComparer.Default; + } + + private static bool StructOnlyNormalEquals(T left, T right) + { + return left.Equals(right); + } + + [Intrinsic] + internal static bool StructOnlyEquals(T left, T right) + { + return EqualityComparer.Default.Equals(left, right); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs new file mode 100644 index 00000000000000..211e3fbf16d4e9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs @@ -0,0 +1,171 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Internal.Reflection.Augments +// ------------------------------------------------- +// Why does this exist?: +// Also, IntrospectionServices.GetTypeInfo() and Assembly.Load() +// are defined in System.Reflection but need a way to "call into" +// Reflection.Core.dll to do the real work. +// +// This contract adds the additional entrypoints needed to System.Reflection. +// +// Implemented by: +// System.Reflection.dll on RH (may use ILMerging instead) +// mscorlib.dll on desktop +// +// Consumed by: +// Reflection.Core.dll + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +using EETypeElementType = Internal.Runtime.EETypeElementType; + +namespace Internal.Reflection.Augments +{ + [System.Runtime.CompilerServices.ReflectionBlocked] + public static class ReflectionAugments + { + // + // One time start up initialization - called by Reflection.Core.dll to provide System.Reflection with a way to call back + // into Reflection.Core.dll. + // + public static void Initialize(ReflectionCoreCallbacks reflectionCoreCallbacks) + { + s_reflectionCoreCallbacks = reflectionCoreCallbacks; + } + + public static TypeCode GetRuntimeTypeCode(Type type) + { + Debug.Assert(type != null); + + EETypePtr eeType; + if (!type.TryGetEEType(out eeType)) + { + // Type exists in metadata only. Aside from the enums, there is no chance a type with a TypeCode would not have an MethodTable, + // so if it's not an enum, return the default. + if (!type.IsEnum) + return TypeCode.Object; + Type underlyingType = Enum.GetUnderlyingType(type); + eeType = underlyingType.TypeHandle.ToEETypePtr(); + } + + // Note: Type.GetTypeCode() is expected to return the underlying type's TypeCode for enums. EETypePtr.CorElementType does the same, + // so this one switch handles both cases. + EETypeElementType rhType = eeType.ElementType; + switch (rhType) + { + case EETypeElementType.Boolean: return TypeCode.Boolean; + case EETypeElementType.Char: return TypeCode.Char; + case EETypeElementType.SByte: return TypeCode.SByte; + case EETypeElementType.Byte: return TypeCode.Byte; + case EETypeElementType.Int16: return TypeCode.Int16; + case EETypeElementType.UInt16: return TypeCode.UInt16; + case EETypeElementType.Int32: return TypeCode.Int32; + case EETypeElementType.UInt32: return TypeCode.UInt32; + case EETypeElementType.Int64: return TypeCode.Int64; + case EETypeElementType.UInt64: return TypeCode.UInt64; + case EETypeElementType.Single: return TypeCode.Single; + case EETypeElementType.Double: return TypeCode.Double; + default: + break; + } + + if (type == typeof(string)) + return TypeCode.String; + + if (type == typeof(DateTime)) + return TypeCode.DateTime; + + if (type == typeof(decimal)) + return TypeCode.Decimal; + + if (type == typeof(DBNull)) + return TypeCode.DBNull; + + return TypeCode.Object; + } + + public static Type MakeGenericSignatureType(Type genericTypeDefinition, Type[] genericTypeArguments) + { + return new SignatureConstructedGenericType(genericTypeDefinition, genericTypeArguments); + } + + public static TypeLoadException CreateTypeLoadException(string message, string typeName) + { + return new TypeLoadException(message, typeName); + } + + internal static ReflectionCoreCallbacks ReflectionCoreCallbacks + { + get + { + ReflectionCoreCallbacks callbacks = s_reflectionCoreCallbacks; + if (callbacks == null) + throw new InvalidOperationException(SR.InvalidOperation_TooEarly); + return callbacks; + } + } + + private static ReflectionCoreCallbacks s_reflectionCoreCallbacks; + } + + // + // This class is implemented by Internal.Reflection.Core.dll and provides the actual implementation + // of Type.GetTypeInfo() and (on Project N) (Assembly.Load()). + // + [System.Runtime.CompilerServices.ReflectionBlocked] + public abstract class ReflectionCoreCallbacks + { + public abstract Assembly Load(AssemblyName refName, bool throwOnFileNotFound); + public abstract Assembly Load(byte[] rawAssembly, byte[] pdbSymbolStore); + public abstract Assembly Load(string assemblyPath); + + public abstract MethodBase GetMethodFromHandle(RuntimeMethodHandle runtimeMethodHandle); + public abstract MethodBase GetMethodFromHandle(RuntimeMethodHandle runtimeMethodHandle, RuntimeTypeHandle declaringTypeHandle); + public abstract FieldInfo GetFieldFromHandle(RuntimeFieldHandle runtimeFieldHandle); + public abstract FieldInfo GetFieldFromHandle(RuntimeFieldHandle runtimeFieldHandle, RuntimeTypeHandle declaringTypeHandle); + + public abstract EventInfo GetImplicitlyOverriddenBaseClassEvent(EventInfo e); + public abstract MethodInfo GetImplicitlyOverriddenBaseClassMethod(MethodInfo m); + public abstract PropertyInfo GetImplicitlyOverriddenBaseClassProperty(PropertyInfo p); + + public abstract object ActivatorCreateInstance( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type, bool nonPublic); + public abstract object ActivatorCreateInstance( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes); + + // V2 api: Creates open or closed delegates to static or instance methods - relaxed signature checking allowed. + public abstract Delegate CreateDelegate(Type type, object? firstArgument, MethodInfo method, bool throwOnBindFailure); + + // V1 api: Creates open delegates to static or instance methods - relaxed signature checking allowed. + public abstract Delegate CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure); + + // V1 api: Creates closed delegates to instance methods only, relaxed signature checking disallowed. + [RequiresUnreferencedCode("The target method might be removed")] + public abstract Delegate CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure); + + // V1 api: Creates open delegates to static methods only, relaxed signature checking disallowed. + public abstract Delegate CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method, bool ignoreCase, bool throwOnBindFailure); + +#if FEATURE_COMINTEROP + public abstract Type GetTypeFromCLSID(Guid clsid, string server, bool throwOnError); +#endif + + public abstract IntPtr GetFunctionPointer(RuntimeMethodHandle runtimeMethodHandle, RuntimeTypeHandle declaringTypeHandle); + + public abstract void RunModuleConstructor(Module module); + + public abstract void MakeTypedReference(object target, FieldInfo[] flds, out Type type, out int offset); + + public abstract Assembly[] GetLoadedAssemblies(); + + public abstract EnumInfo GetEnumInfo(Type type); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RuntimeTypeUnifier.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RuntimeTypeUnifier.cs new file mode 100644 index 00000000000000..dda21e79597841 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RuntimeTypeUnifier.cs @@ -0,0 +1,119 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; + +using Internal.Runtime.Augments; + +namespace Internal.Reflection.Core.NonPortable +{ + // + // ! If you change this policy to not unify all instances, you must change the implementation of Equals/GetHashCode in the runtime type classes. + // + // The RuntimeTypeUnifier maintains a record of all System.Type objects created by the runtime. The split into two classes is an artifact of + // reflection being implemented partly in System.Private.CoreLib and partly in S.R.R. + // + // Though the present incarnation enforces the "one instance per semantic identity rule", its surface area is also designed + // to be able to switch to a non-unified model if desired. + // + // ! If you do switch away from a "one instance per semantic identity rule", you must also change the implementation + // ! of RuntimeType.Equals() and RuntimeType.GetHashCode(). + // + // + // Internal details: + // + // The RuntimeType is not a single class but a family of classes that can be categorized along two dimensions: + // + // - Type structure (named vs. array vs. generic instance, etc.) + // + // - Is invokable (i.e. has a RuntimeTypeHandle.) + // + // Taking advantage of this, RuntimeTypeUnifier splits the unification across several type tables, each with its own separate lock. + // Each type table owns a specific group of RuntimeTypes. These groups can overlap. In particular, types with EETypes can and do + // appear in both TypeTableForTypesWithEETypes and the "inspection" type table for the type's specific flavor. This allows + // fast lookups for both the Object.GetType() calls and the metadata initiated lookups. + // + internal static partial class RuntimeTypeUnifier + { + // + // Retrieves the unified Type object for given RuntimeTypeHandle (this is basically the Type.GetTypeFromHandle() api without the input validation.) + // + internal static Type GetRuntimeTypeForEEType(EETypePtr eeType) + { + // If writable data is supported, we shouldn't be using the hashtable - the runtime type + // is accessible through a couple indirections from the EETypePtr which is much faster. + Debug.Assert(!Internal.Runtime.MethodTable.SupportsWritableData); + return RuntimeTypeHandleToTypeCache.Table.GetOrAdd(eeType.RawValue); + } + + // + // TypeTable mapping raw RuntimeTypeHandles (normalized or otherwise) to Types. + // + // Unlike most unifier tables, RuntimeTypeHandleToRuntimeTypeCache exists for fast lookup, not unification. It hashes and compares + // on the raw IntPtr value of the RuntimeTypeHandle. Because Redhawk can and does create multiple EETypes for the same + // semantically identical type, the same RuntimeType can legitimately appear twice in this table. The factory, however, + // does a second lookup in the true unifying tables rather than creating the Type itself. + // Thus, the one-to-one relationship between Type reference identity and Type semantic identity is preserved. + // + private sealed class RuntimeTypeHandleToTypeCache : ConcurrentUnifierW + { + private RuntimeTypeHandleToTypeCache() { } + + protected sealed override Type Factory(IntPtr rawRuntimeTypeHandleKey) + { + EETypePtr eeType = new EETypePtr(rawRuntimeTypeHandleKey); + return GetRuntimeTypeBypassCache(eeType); + } + + public static readonly RuntimeTypeHandleToTypeCache Table = new RuntimeTypeHandleToTypeCache(); + } + + // This bypasses the CoreLib's unifier, but there's another unifier deeper within the reflection stack: + // this code is safe to call without locking. See comment above. + public static Type GetRuntimeTypeBypassCache(EETypePtr eeType) + { + RuntimeTypeHandle runtimeTypeHandle = new RuntimeTypeHandle(eeType); + + ReflectionExecutionDomainCallbacks callbacks = RuntimeAugments.Callbacks; + + if (eeType.IsDefType) + { + if (eeType.IsGenericTypeDefinition) + { + return callbacks.GetNamedTypeForHandle(runtimeTypeHandle, isGenericTypeDefinition: true); + } + else if (eeType.IsGeneric) + { + return callbacks.GetConstructedGenericTypeForHandle(runtimeTypeHandle); + } + else + { + return callbacks.GetNamedTypeForHandle(runtimeTypeHandle, isGenericTypeDefinition: false); + } + } + else if (eeType.IsArray) + { + if (!eeType.IsSzArray) + return callbacks.GetMdArrayTypeForHandle(runtimeTypeHandle, eeType.ArrayRank); + else + return callbacks.GetArrayTypeForHandle(runtimeTypeHandle); + } + else if (eeType.IsPointer) + { + return callbacks.GetPointerTypeForHandle(runtimeTypeHandle); + } + else if (eeType.IsByRef) + { + return callbacks.GetByRefTypeForHandle(runtimeTypeHandle); + } + else + { + throw new ArgumentException(SR.Arg_InvalidRuntimeTypeHandle); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInheritanceRules.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInheritanceRules.cs new file mode 100644 index 00000000000000..2f414c13b7f3bc --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInheritanceRules.cs @@ -0,0 +1,242 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +using Internal.Reflection.Augments; + +//================================================================================================================== +// Dependency note: +// This class must depend only on the CustomAttribute properties that return IEnumerable. +// All of the other custom attribute api route back here so calls to them will cause an infinite recursion. +//================================================================================================================== + +namespace Internal.Reflection.Extensions.NonPortable +{ + public static class CustomAttributeInheritanceRules + { + //============================================================================================================================== + // Api helpers: Computes the effective set of custom attributes for various Reflection elements and returns them + // as CustomAttributeData objects. + //============================================================================================================================== + public static IEnumerable GetMatchingCustomAttributes(this Assembly element, Type optionalAttributeTypeFilter, bool skipTypeValidation = false) + { + return AssemblyCustomAttributeSearcher.Default.GetMatchingCustomAttributes(element, optionalAttributeTypeFilter, inherit: false, skipTypeValidation: skipTypeValidation); + } + + public static IEnumerable GetMatchingCustomAttributes(this Module element, Type optionalAttributeTypeFilter, bool skipTypeValidation = false) + { + return ModuleCustomAttributeSearcher.Default.GetMatchingCustomAttributes(element, optionalAttributeTypeFilter, inherit: false, skipTypeValidation: skipTypeValidation); + } + + public static IEnumerable GetMatchingCustomAttributes(this ParameterInfo element, Type optionalAttributeTypeFilter, bool inherit, bool skipTypeValidation = false) + { + return ParameterCustomAttributeSearcher.Default.GetMatchingCustomAttributes(element, optionalAttributeTypeFilter, inherit, skipTypeValidation: skipTypeValidation); + } + + public static IEnumerable GetMatchingCustomAttributes(this MemberInfo element, Type optionalAttributeTypeFilter, bool inherit, bool skipTypeValidation = false) + { + { + Type? type = element as Type; + if (type != null) + return TypeCustomAttributeSearcher.Default.GetMatchingCustomAttributes(type, optionalAttributeTypeFilter, inherit, skipTypeValidation: skipTypeValidation); + } + { + ConstructorInfo? constructorInfo = element as ConstructorInfo; + if (constructorInfo != null) + return ConstructorCustomAttributeSearcher.Default.GetMatchingCustomAttributes(constructorInfo, optionalAttributeTypeFilter, inherit: false, skipTypeValidation: skipTypeValidation); + } + { + MethodInfo? methodInfo = element as MethodInfo; + if (methodInfo != null) + return MethodCustomAttributeSearcher.Default.GetMatchingCustomAttributes(methodInfo, optionalAttributeTypeFilter, inherit, skipTypeValidation: skipTypeValidation); + } + { + FieldInfo? fieldInfo = element as FieldInfo; + if (fieldInfo != null) + return FieldCustomAttributeSearcher.Default.GetMatchingCustomAttributes(fieldInfo, optionalAttributeTypeFilter, inherit: false, skipTypeValidation: skipTypeValidation); + } + { + PropertyInfo? propertyInfo = element as PropertyInfo; + if (propertyInfo != null) + return PropertyCustomAttributeSearcher.Default.GetMatchingCustomAttributes(propertyInfo, optionalAttributeTypeFilter, inherit, skipTypeValidation: skipTypeValidation); + } + { + EventInfo? eventInfo = element as EventInfo; + if (eventInfo != null) + return EventCustomAttributeSearcher.Default.GetMatchingCustomAttributes(eventInfo, optionalAttributeTypeFilter, inherit, skipTypeValidation: skipTypeValidation); + } + + if (element == null) + throw new ArgumentNullException(); + + throw new NotSupportedException(); // Shouldn't get here. + } + + + + + //============================================================================================================================== + // Searcher class for Assemblies. + //============================================================================================================================== + private sealed class AssemblyCustomAttributeSearcher : CustomAttributeSearcher + { + protected sealed override IEnumerable GetDeclaredCustomAttributes(Assembly element) + { + return element.CustomAttributes; + } + + public static readonly AssemblyCustomAttributeSearcher Default = new AssemblyCustomAttributeSearcher(); + } + + //============================================================================================================================== + // Searcher class for Modules. + //============================================================================================================================== + private sealed class ModuleCustomAttributeSearcher : CustomAttributeSearcher + { + protected sealed override IEnumerable GetDeclaredCustomAttributes(Module element) + { + return element.CustomAttributes; + } + + public static readonly ModuleCustomAttributeSearcher Default = new ModuleCustomAttributeSearcher(); + } + + //============================================================================================================================== + // Searcher class for TypeInfos. + //============================================================================================================================== + private sealed class TypeCustomAttributeSearcher : CustomAttributeSearcher + { + protected sealed override IEnumerable GetDeclaredCustomAttributes(Type element) + { + return element.CustomAttributes; + } + + public sealed override Type GetParent(Type e) + { + Type? baseType = e.BaseType; + if (baseType == null) + return null; + + // Optimization: We shouldn't have any public inheritable attributes on Object or ValueType so don't bother scanning this one. + // Since many types derive directly from Object, this should a lot of type. + if (baseType == typeof(object) || baseType == typeof(ValueType)) + return null; + + return baseType; + } + + public static readonly TypeCustomAttributeSearcher Default = new TypeCustomAttributeSearcher(); + } + + //============================================================================================================================== + // Searcher class for FieldInfos. + //============================================================================================================================== + private sealed class FieldCustomAttributeSearcher : CustomAttributeSearcher + { + protected sealed override IEnumerable GetDeclaredCustomAttributes(FieldInfo element) + { + return element.CustomAttributes; + } + + public static readonly FieldCustomAttributeSearcher Default = new FieldCustomAttributeSearcher(); + } + + //============================================================================================================================== + // Searcher class for ConstructorInfos. + //============================================================================================================================== + private sealed class ConstructorCustomAttributeSearcher : CustomAttributeSearcher + { + protected sealed override IEnumerable GetDeclaredCustomAttributes(ConstructorInfo element) + { + return element.CustomAttributes; + } + + public static readonly ConstructorCustomAttributeSearcher Default = new ConstructorCustomAttributeSearcher(); + } + + //============================================================================================================================== + // Searcher class for MethodInfos. + //============================================================================================================================== + private sealed class MethodCustomAttributeSearcher : CustomAttributeSearcher + { + protected sealed override IEnumerable GetDeclaredCustomAttributes(MethodInfo element) + { + return element.CustomAttributes; + } + + public sealed override MethodInfo GetParent(MethodInfo e) + { + return ReflectionAugments.ReflectionCoreCallbacks.GetImplicitlyOverriddenBaseClassMethod(e); + } + + public static readonly MethodCustomAttributeSearcher Default = new MethodCustomAttributeSearcher(); + } + + //============================================================================================================================== + // Searcher class for PropertyInfos. + //============================================================================================================================== + private sealed class PropertyCustomAttributeSearcher : CustomAttributeSearcher + { + protected sealed override IEnumerable GetDeclaredCustomAttributes(PropertyInfo element) + { + return element.CustomAttributes; + } + + public sealed override PropertyInfo GetParent(PropertyInfo e) + { + return ReflectionAugments.ReflectionCoreCallbacks.GetImplicitlyOverriddenBaseClassProperty(e); + } + + public static readonly PropertyCustomAttributeSearcher Default = new PropertyCustomAttributeSearcher(); + } + + + //============================================================================================================================== + // Searcher class for EventInfos. + //============================================================================================================================== + private sealed class EventCustomAttributeSearcher : CustomAttributeSearcher + { + protected sealed override IEnumerable GetDeclaredCustomAttributes(EventInfo element) + { + return element.CustomAttributes; + } + + public sealed override EventInfo GetParent(EventInfo e) + { + return ReflectionAugments.ReflectionCoreCallbacks.GetImplicitlyOverriddenBaseClassEvent(e); + } + + public static readonly EventCustomAttributeSearcher Default = new EventCustomAttributeSearcher(); + } + + //============================================================================================================================== + // Searcher class for ParameterInfos. + //============================================================================================================================== + private sealed class ParameterCustomAttributeSearcher : CustomAttributeSearcher + { + protected sealed override IEnumerable GetDeclaredCustomAttributes(ParameterInfo element) + { + return element.CustomAttributes; + } + + public sealed override ParameterInfo GetParent(ParameterInfo e) + { + MethodInfo? method = e.Member as MethodInfo; + if (method == null) + return null; // This is a constructor parameter. + MethodInfo? methodParent = new MethodCustomAttributeSearcher().GetParent(method); + if (methodParent == null) + return null; + return methodParent.GetParametersNoCopy()[e.Position]; + } + + public static readonly ParameterCustomAttributeSearcher Default = new ParameterCustomAttributeSearcher(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInstantiator.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInstantiator.cs new file mode 100644 index 00000000000000..399a31119bf01d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInstantiator.cs @@ -0,0 +1,187 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +using Internal.Runtime.Augments; + +//================================================================================================================== +// Dependency note: +// This class must depend only on the CustomAttribute properties that return IEnumerable. +// All of the other custom attribute api route back here so calls to them will cause an infinite recursion. +//================================================================================================================== + +namespace Internal.Reflection.Extensions.NonPortable +{ + public static class CustomAttributeInstantiator + { + // + // Turn a CustomAttributeData into a live Attribute object. There's nothing actually non-portable about this one, + // however, it is included as a concession to that the fact the Reflection.Execution which implements this contract + // also needs this functionality to implement default values, and we don't want to duplicate this code. + // + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", + Justification = "property setters and fiels which are accessed by any attribute instantiation which is present in the code linker has analyzed." + + "As such enumerating all fields and properties may return different results after trimming" + + "but all those which are needed to actually have data should be there.")] + public static Attribute Instantiate(this CustomAttributeData cad) + { + if (cad == null) + return null; + Type attributeType = cad.AttributeType; + + // + // Find the public constructor that matches the supplied arguments. + // + ConstructorInfo? matchingCtor = null; + ParameterInfo[]? matchingParameters = null; + IList constructorArguments = cad.ConstructorArguments; + foreach (ConstructorInfo ctor in attributeType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + ParameterInfo[] parameters = ctor.GetParametersNoCopy(); + if (parameters.Length != constructorArguments.Count) + continue; + int i; + for (i = 0; i < parameters.Length; i++) + { + Type parameterType = parameters[i].ParameterType; + if (!(parameterType.Equals(constructorArguments[i].ArgumentType) || + parameterType == typeof(object))) + break; + } + if (i == parameters.Length) + { + matchingCtor = ctor; + matchingParameters = parameters; + break; + } + } + if (matchingCtor == null) + throw new MissingMethodException(attributeType.FullName, ConstructorInfo.ConstructorName); + + // + // Found the right constructor. Instantiate the Attribute. + // + int arity = matchingParameters.Length; + object?[] invokeArguments = new object[arity]; + for (int i = 0; i < arity; i++) + { + invokeArguments[i] = constructorArguments[i].Convert(); + } + Attribute newAttribute = (Attribute)(matchingCtor.Invoke(invokeArguments)); + + // + // If there any named arguments, evaluate them and set the appropriate field or property. + // + foreach (CustomAttributeNamedArgument namedArgument in cad.NamedArguments) + { + object? argumentValue = namedArgument.TypedValue.Convert(); + Type walk = attributeType; + string name = namedArgument.MemberName; + if (namedArgument.IsField) + { + // Field + for (;;) + { + FieldInfo? fieldInfo = walk.GetField(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); + if (fieldInfo != null) + { + fieldInfo.SetValue(newAttribute, argumentValue); + break; + } + Type? baseType = walk.BaseType; + if (baseType == null) + throw new CustomAttributeFormatException(SR.Format(SR.CustomAttributeFormat_InvalidFieldFail, name)); + walk = baseType; + } + } + else + { + // Property + for (;;) + { + PropertyInfo? propertyInfo = walk.GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); + if (propertyInfo != null) + { + propertyInfo.SetValue(newAttribute, argumentValue); + break; + } + Type? baseType = walk.BaseType; + if (baseType == null) + throw new CustomAttributeFormatException(SR.Format(SR.CustomAttributeFormat_InvalidPropertyFail, name)); + walk = baseType; + } + } + } + + return newAttribute; + } + + // + // Convert the argument value reported by Reflection into an actual object. + // + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "The AOT compiler ensures array types required by custom attribute blobs are generated.")] + private static object? Convert(this CustomAttributeTypedArgument typedArgument) + { + Type argumentType = typedArgument.ArgumentType; + if (!argumentType.IsArray) + { + bool isEnum = argumentType.IsEnum; + object? argumentValue = typedArgument.Value; + if (isEnum) + argumentValue = Enum.ToObject(argumentType, argumentValue!); + return argumentValue; + } + else + { + IList? typedElements = (IList?)(typedArgument.Value); + if (typedElements == null) + return null; + Type? elementType = argumentType.GetElementType(); + Array array = Array.CreateInstance(elementType, typedElements.Count); + for (int i = 0; i < typedElements.Count; i++) + { + object? elementValue = typedElements[i].Convert(); + array.SetValue(elementValue, i); + } + return array; + } + } + + // + // Only public instance fields can be targets of named arguments. + // + private static bool IsValidNamedArgumentTarget(this FieldInfo fieldInfo) + { + if ((fieldInfo.Attributes & (FieldAttributes.FieldAccessMask | FieldAttributes.Static | FieldAttributes.Literal)) != + FieldAttributes.Public) + return false; + return true; + } + + // + // Only public read/write instance properties can be targets of named arguments. + // + private static bool IsValidNamedArgumentTarget(this PropertyInfo propertyInfo) + { + MethodInfo? getter = propertyInfo.GetMethod; + MethodInfo? setter = propertyInfo.SetMethod; + if (getter == null) + return false; + if ((getter.Attributes & (MethodAttributes.Static | MethodAttributes.MemberAccessMask)) != MethodAttributes.Public) + return false; + if (setter == null) + return false; + if ((setter.Attributes & (MethodAttributes.Static | MethodAttributes.MemberAccessMask)) != MethodAttributes.Public) + return false; + return true; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeSearcher.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeSearcher.cs new file mode 100644 index 00000000000000..a963522d5eca4d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeSearcher.cs @@ -0,0 +1,209 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +//================================================================================================================== +// Dependency note: +// This class must depend only on the CustomAttribute properties that return IEnumerable. +// All of the other custom attribute api route back here so calls to them will cause an infinite recursion. +//================================================================================================================== + +namespace Internal.Reflection.Extensions.NonPortable +{ + // + // Common logic for computing the effective set of custom attributes on a reflection element of type E where + // E is a MemberInfo, ParameterInfo, Assembly or Module. + // + // This class is only used by the CustomAttributeExtensions class - hence, we bake in the CustomAttributeExtensions behavior of + // filtering out WinRT attributes. + // + internal abstract class CustomAttributeSearcher + where E : class + { + // + // Returns the effective set of custom attributes on a reflection element. + // + public IEnumerable GetMatchingCustomAttributes(E element, Type optionalAttributeTypeFilter, bool inherit, bool skipTypeValidation = false) + { + // Do all parameter validation here before we enter the iterator function (so that exceptions from validations + // show up immediately rather than on the first MoveNext()). + if (element == null) + throw new ArgumentNullException(nameof(element)); + + bool typeFilterKnownToBeSealed = false; + if (!skipTypeValidation) + { + if (optionalAttributeTypeFilter == null) + throw new ArgumentNullException("type"); + if (!(optionalAttributeTypeFilter == typeof(Attribute) || + optionalAttributeTypeFilter.IsSubclassOf(typeof(Attribute)))) + throw new ArgumentException(SR.Argument_MustHaveAttributeBaseClass); + + try + { + typeFilterKnownToBeSealed = optionalAttributeTypeFilter.IsSealed; + } + catch (MissingMetadataException) + { + // If we got here, the custom attribute type itself was not opted into metadata. This can and does happen in the real world when an app + // contains a check for custom attributes that never actually appear on any entity within the app. + // + // Since "typeFilterKnownToBeSealed" is only used to enable an optimization, it's always safe to leave it "false". + // + // Because the Project N toolchain removes any custom attribute that refuses to opt into metadata so at this point, + // we could simply return an empty enumeration and "be correct." However, the code paths following this already do that naturally. + // (i.e. the "passFilter" will never return true, thus we will never attempt to query the custom attribute type for its + // own AttributeUsage custom attribute.) If the toolchain behavior changes in the future, it's preferable that + // this shows up as new MissingMetadataExceptions rather than incorrect results from the api so we will not put + // in an explicit return here. + } + } + + Func passesFilter; + if (optionalAttributeTypeFilter == null) + { + passesFilter = + delegate (Type actualType) + { + return true; + }; + } + else + { + passesFilter = + delegate (Type actualType) + { + if (optionalAttributeTypeFilter.Equals(actualType)) + return true; + if (typeFilterKnownToBeSealed) + return false; + return optionalAttributeTypeFilter.IsAssignableFrom(actualType); + }; + } + + return GetMatchingCustomAttributesIterator(element, passesFilter, inherit); + } + + // + // Subclasses should override this to compute the "parent" of the element for the purpose of finding "inherited" custom attributes. + // Return null if no parent. + // + public virtual E GetParent(E e) + { + return null; + } + + + // + // Main iterator. + // + private IEnumerable GetMatchingCustomAttributesIterator(E element, Func rawPassesFilter, bool inherit) + { + Func passesFilter = + delegate (Type attributeType) + { + // Windows prohibits instantiating WinRT custom attributes. Filter them from the search as the desktop CLR does. + TypeAttributes typeAttributes = attributeType.Attributes; + if (0 != (typeAttributes & TypeAttributes.WindowsRuntime)) + return false; + return rawPassesFilter(attributeType); + }; + + LowLevelList immediateResults = new LowLevelList(); + foreach (CustomAttributeData cad in GetDeclaredCustomAttributes(element)) + { + if (passesFilter(cad.AttributeType)) + { + yield return cad; + immediateResults.Add(cad); + } + } + if (inherit) + { + // Because the "inherit" parameter defaults to "true", we probably get here for a lot of elements that + // don't actually have any inheritance chains. Try to avoid doing any unnecessary setup for the inheritance walk + // unless we have to. + element = GetParent(element); + if (element != null) + { + // This dictionary serves two purposes: + // - Let us know which attribute types we've encountered at lower levels so we can block them from appearing twice in the results + // if appropriate. + // + // - Cache the results of retrieving the usage attribute. + // + LowLevelDictionary encounteredTypes = new LowLevelDictionary(11); + + for (int i = 0; i < immediateResults.Count; i++) + { + Type attributeType = immediateResults[i].AttributeType; + AttributeUsageAttribute? usage; + TypeUnificationKey attributeTypeKey = new TypeUnificationKey(attributeType); + if (!encounteredTypes.TryGetValue(attributeTypeKey, out usage)) + encounteredTypes.Add(attributeTypeKey, null); + } + + do + { + foreach (CustomAttributeData cad in GetDeclaredCustomAttributes(element)) + { + Type attributeType = cad.AttributeType; + if (!passesFilter(attributeType)) + continue; + AttributeUsageAttribute? usage; + TypeUnificationKey attributeTypeKey = new TypeUnificationKey(attributeType); + if (!encounteredTypes.TryGetValue(attributeTypeKey, out usage)) + { + // Type was not encountered before. Only include it if it is inheritable. + usage = GetAttributeUsage(attributeType); + encounteredTypes.Add(attributeTypeKey, usage); + if (usage.Inherited) + yield return cad; + } + else + { + if (usage == null) + usage = GetAttributeUsage(attributeType); + encounteredTypes[attributeTypeKey] = usage; + // Type was encountered at a lower level. Only include it if its inheritable AND allowMultiple. + if (usage.Inherited && usage.AllowMultiple) + yield return cad; + } + } + } + while ((element = GetParent(element)) != null); + } + } + } + + // + // Internal helper to compute the AttributeUsage. This must be coded specially to avoid an infinite recursion. + // + private AttributeUsageAttribute GetAttributeUsage(Type attributeType) + { + // This is only invoked when the seacher is called with "inherit: true", thus calling the searcher again + // with "inherit: false" will not cause infinite recursion. + // + // Legacy: Why aren't we checking the parent types? Answer: Although AttributeUsageAttribute is itself marked inheritable, desktop Reflection + // treats it as *non*-inheritable for the purpose of deciding whether another attribute class is inheritable. + // This behavior goes all the way back to at least 3.5 (and perhaps earlier). For compat reasons, + // we won't-fixed this in 4.5 and we won't-fix this in Project N. + // + AttributeUsageAttribute? usage = attributeType.GetCustomAttribute(inherit: false); + if (usage == null) + return new AttributeUsageAttribute(AttributeTargets.All) { AllowMultiple = false, Inherited = true }; + return usage; + } + + // + // Subclasses must implement this to call the element-specific CustomAttributes property. + // + protected abstract IEnumerable GetDeclaredCustomAttributes(E element); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/DynamicDelegateAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/DynamicDelegateAugments.cs new file mode 100644 index 00000000000000..efa896b0a8662b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/DynamicDelegateAugments.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +//Internal.Runtime.Augments +//------------------------------------------------- +// Why does this exist?: +// Reflection.Execution cannot physically live in System.Private.CoreLib.dll +// as it has a dependency on System.Reflection.Metadata. Its inherently +// low-level nature means, however, it is closely tied to System.Private.CoreLib.dll. +// This contract provides the two-communication between those two .dll's. +// +// +// Implemented by: +// System.Private.CoreLib.dll +// +// Consumed by: +// Reflection.Execution.dll + +using System; + +namespace Internal.Runtime.Augments +{ + public static class DynamicDelegateAugments + { + // + // Helper to create a interpreted delegate for LINQ and DLR expression trees + // + public static Delegate CreateObjectArrayDelegate(Type delegateType, Func invoker) + { + return Delegate.CreateObjectArrayDelegate(delegateType, invoker); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/InteropCallbacks.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/InteropCallbacks.cs new file mode 100644 index 00000000000000..3dbf1831575eb0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/InteropCallbacks.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Internal.Runtime.Augments +{ + [CLSCompliant(false)] + [System.Runtime.CompilerServices.ReflectionBlocked] + public abstract class InteropCallbacks + { + public abstract IntPtr GetForwardDelegateCreationStub(RuntimeTypeHandle delegateTypeHandle); + + public abstract IntPtr GetDelegateMarshallingStub(RuntimeTypeHandle delegateTypeHandle, bool openStaticDelegate); + + public abstract bool TryGetStructUnmarshalStub(RuntimeTypeHandle structureTypeHandle, out IntPtr unmarshalStub); + + public abstract IntPtr GetStructUnmarshalStub(RuntimeTypeHandle structureTypeHandle); + + public abstract bool TryGetStructMarshalStub(RuntimeTypeHandle structureTypeHandle, out IntPtr marshalStub); + + public abstract IntPtr GetStructMarshalStub(RuntimeTypeHandle structureTypeHandle); + + public abstract bool TryGetDestroyStructureStub(RuntimeTypeHandle structureTypeHandle, out IntPtr destroyStructureStub, out bool hasInvalidLayout); + + public abstract IntPtr GetDestroyStructureStub(RuntimeTypeHandle structureTypeHandle, out bool hasInvalidLayout); + + public abstract bool TryGetStructFieldOffset(RuntimeTypeHandle structureTypeHandle, string fieldName, out bool structExists, out uint offset); + + public abstract uint GetStructFieldOffset(RuntimeTypeHandle structureTypeHandle, string fieldName); + + public abstract bool TryGetStructUnsafeStructSize(RuntimeTypeHandle structureTypeHandle, out int size); + + public abstract int GetStructUnsafeStructSize(RuntimeTypeHandle structureTypeHandle); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs new file mode 100644 index 00000000000000..61938ed1bbd7be --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +//Internal.Runtime.Augments +//------------------------------------------------- +// Why does this exist?: +// Internal.Reflection.Execution cannot physically live in System.Private.CoreLib.dll +// as it has a dependency on System.Reflection.Metadata. It's inherently +// low-level nature means, however, it is closely tied to System.Private.CoreLib.dll. +// This contract provides the two-communication between those two .dll's. +// +// +// Implemented by: +// System.Private.CoreLib.dll +// +// Consumed by: +// Reflection.Execution.dll + +using System; +using System.Reflection; + +namespace Internal.Runtime.Augments +{ + [CLSCompliant(false)] + [System.Runtime.CompilerServices.ReflectionBlocked] + public abstract class ReflectionExecutionDomainCallbacks + { + // Api's that are exposed in System.Runtime but are really reflection apis. + public abstract Type GetType(string typeName, Func? assemblyResolver, Func? typeResolver, bool throwOnError, bool ignoreCase, string defaultAssembly); + + public abstract IntPtr TryGetStaticClassConstructionContext(RuntimeTypeHandle runtimeTypeHandle); + + public abstract bool IsReflectionBlocked(RuntimeTypeHandle typeHandle); + + //======================================================================================= + // This group of methods jointly service the Type.GetTypeFromHandle() path. The caller + // is responsible for analyzing the RuntimeTypeHandle to figure out which flavor to call. + //======================================================================================= + public abstract Type GetNamedTypeForHandle(RuntimeTypeHandle typeHandle, bool isGenericTypeDefinition); + public abstract Type GetArrayTypeForHandle(RuntimeTypeHandle typeHandle); + public abstract Type GetMdArrayTypeForHandle(RuntimeTypeHandle typeHandle, int rank); + public abstract Type GetPointerTypeForHandle(RuntimeTypeHandle typeHandle); + public abstract Type GetByRefTypeForHandle(RuntimeTypeHandle typeHandle); + public abstract Type GetConstructedGenericTypeForHandle(RuntimeTypeHandle typeHandle); + + // Flotsam and jetsam. + public abstract Exception CreateMissingMetadataException(Type typeWithMissingMetadata); + + public abstract string GetBetterDiagnosticInfoIfAvailable(RuntimeTypeHandle runtimeTypeHandle); + public abstract MethodBase GetMethodBaseFromStartAddressIfAvailable(IntPtr methodStartAddress); + public abstract Assembly GetAssemblyForHandle(RuntimeTypeHandle typeHandle); + + /// + /// Retrieves the default value for a parameter of a method. + /// + /// The default parameters context used to invoke the method, + /// this should identify the method in question. This is passed to the RuntimeAugments.CallDynamicInvokeMethod. + /// The type of the parameter to retrieve. + /// The index of the parameter on the method to retrieve. + /// The default value of the parameter if available. + /// true if the default parameter value is available, otherwise false. + public abstract bool TryGetDefaultParameterValue(object defaultParametersContext, RuntimeTypeHandle thType, int argIndex, out object defaultValue); + + public abstract RuntimeTypeHandle GetTypeHandleIfAvailable(Type type); + public abstract bool SupportsReflection(Type type); + + public abstract MethodInfo GetDelegateMethod(Delegate del); + + public abstract Exception GetExceptionForHR(int hr); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs new file mode 100644 index 00000000000000..13e4042887460e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -0,0 +1,1098 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +//Internal.Runtime.Augments +//------------------------------------------------- +// Why does this exist?: +// Reflection.Execution cannot physically live in System.Private.CoreLib.dll +// as it has a dependency on System.Reflection.Metadata. Its inherently +// low-level nature means, however, it is closely tied to System.Private.CoreLib.dll. +// This contract provides the two-communication between those two .dll's. +// +// +// Implemented by: +// System.Private.CoreLib.dll +// +// Consumed by: +// Reflection.Execution.dll + +using System; +using System.Runtime; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Threading; + +using Internal.Runtime.CompilerHelpers; +using Internal.Runtime.CompilerServices; + +namespace Internal.Runtime.Augments +{ + using BinderBundle = System.Reflection.BinderBundle; + using Pointer = System.Reflection.Pointer; + + [ReflectionBlocked] + public static class RuntimeAugments + { + /// + /// Callbacks used for metadata-based stack trace resolution. + /// + private static StackTraceMetadataCallbacks s_stackTraceMetadataCallbacks; + + //============================================================================================== + // One-time initialization. + //============================================================================================== + [CLSCompliant(false)] + public static void Initialize(ReflectionExecutionDomainCallbacks callbacks) + { + s_reflectionExecutionDomainCallbacks = callbacks; + } + + [CLSCompliant(false)] + public static void InitializeLookups(TypeLoaderCallbacks callbacks) + { + s_typeLoaderCallbacks = callbacks; + } + + [CLSCompliant(false)] + public static void InitializeInteropLookups(InteropCallbacks callbacks) + { + s_interopCallbacks = callbacks; + } + + [CLSCompliant(false)] + public static void InitializeStackTraceMetadataSupport(StackTraceMetadataCallbacks callbacks) + { + s_stackTraceMetadataCallbacks = callbacks; + } + + //============================================================================================== + // Access to the underlying execution engine's object allocation routines. + //============================================================================================== + + // + // Perform the equivalent of a "newobj", but without invoking any constructors. Other than the MethodTable, the result object is zero-initialized. + // + // Special cases: + // + // Strings: The .ctor performs both the construction and initialization + // and compiler special cases these. + // + // Nullable: the boxed result is the underlying type rather than Nullable so the constructor + // cannot truly initialize it. + // + // In these cases, this helper returns "null" and ConstructorInfo.Invoke() must deal with these specially. + // + public static object NewObject(RuntimeTypeHandle typeHandle) + { + EETypePtr eeType = typeHandle.ToEETypePtr(); + if (eeType.IsNullable + || eeType == EETypePtr.EETypePtrOf() + ) + return null; + return RuntimeImports.RhNewObject(eeType); + } + + // + // Helper API to perform the equivalent of a "newobj" for any MethodTable. + // Unlike the NewObject API, this is the raw version that does not special case any MethodTable, and should be used with + // caution for very specific scenarios. + // + public static object RawNewObject(RuntimeTypeHandle typeHandle) + { + return RuntimeImports.RhNewObject(typeHandle.ToEETypePtr()); + } + + // + // Perform the equivalent of a "newarr" The resulting array is zero-initialized. + // + public static Array NewArray(RuntimeTypeHandle typeHandleForArrayType, int count) + { + // Don't make the easy mistake of passing in the element MethodTable rather than the "array of element" MethodTable. + Debug.Assert(typeHandleForArrayType.ToEETypePtr().IsSzArray); + return RuntimeImports.RhNewArray(typeHandleForArrayType.ToEETypePtr(), count); + } + + // + // Perform the equivalent of a "newarr" The resulting array is zero-initialized. + // + // Note that invoking NewMultiDimArray on a rank-1 array type is not the same thing as invoking NewArray(). + // + // As a concession to the fact that we don't actually support non-zero lower bounds, "lowerBounds" accepts "null" + // to avoid unnecessary array allocations by the caller. + // + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "The compiler ensures that if we have a TypeHandle of a Rank-1 MdArray, we also generated the SzArray.")] + public static unsafe Array NewMultiDimArray(RuntimeTypeHandle typeHandleForArrayType, int[] lengths, int[]? lowerBounds) + { + Debug.Assert(lengths != null); + Debug.Assert(lowerBounds == null || lowerBounds.Length == lengths.Length); + + if (lowerBounds != null) + { + foreach (int lowerBound in lowerBounds) + { + if (lowerBound != 0) + throw new PlatformNotSupportedException(SR.Arg_NotSupportedNonZeroLowerBound); + } + } + + if (lengths.Length == 1) + { + // We just checked above that all lower bounds are zero. In that case, we should actually allocate + // a new SzArray instead. + RuntimeTypeHandle elementTypeHandle = new RuntimeTypeHandle(typeHandleForArrayType.ToEETypePtr().ArrayElementType); + int length = lengths[0]; + if (length < 0) + throw new OverflowException(); // For compat: we need to throw OverflowException(): Array.CreateInstance throws ArgumentOutOfRangeException() + return Array.CreateInstance(Type.GetTypeFromHandle(elementTypeHandle), length); + } + + // Create a local copy of the lenghts that cannot be motified by the caller + int* pLengths = stackalloc int[lengths.Length]; + for (int i = 0; i < lengths.Length; i++) + pLengths[i] = lengths[i]; + + return Array.NewMultiDimArray(typeHandleForArrayType.ToEETypePtr(), pLengths, lengths.Length); + } + + // + // Helper to create an array from a newobj instruction + // + public static unsafe Array NewObjArray(RuntimeTypeHandle typeHandleForArrayType, int[] arguments) + { + EETypePtr eeTypePtr = typeHandleForArrayType.ToEETypePtr(); + Debug.Assert(eeTypePtr.IsArray); + + fixed (int* pArguments = arguments) + { + return ArrayHelpers.NewObjArray((IntPtr)eeTypePtr.ToPointer(), arguments.Length, pArguments); + } + } + + public static ref byte GetSzArrayElementAddress(Array array, int index) + { + if ((uint)index >= (uint)array.Length) + throw new IndexOutOfRangeException(); + + ref byte start = ref Unsafe.As(array).Data; + return ref Unsafe.Add(ref start, (IntPtr)(nint)((nuint)index * array.ElementSize)); + } + + public static IntPtr GetAllocateObjectHelperForType(RuntimeTypeHandle type) + { + return RuntimeImports.RhGetRuntimeHelperForType(CreateEETypePtr(type), RuntimeHelperKind.AllocateObject); + } + + public static IntPtr GetAllocateArrayHelperForType(RuntimeTypeHandle type) + { + return RuntimeImports.RhGetRuntimeHelperForType(CreateEETypePtr(type), RuntimeHelperKind.AllocateArray); + } + + public static IntPtr GetCastingHelperForType(RuntimeTypeHandle type, bool throwing) + { + return RuntimeImports.RhGetRuntimeHelperForType(CreateEETypePtr(type), + throwing ? RuntimeHelperKind.CastClass : RuntimeHelperKind.IsInst); + } + + public static IntPtr GetDispatchMapForType(RuntimeTypeHandle typeHandle) + { + return CreateEETypePtr(typeHandle).DispatchMap; + } + + public static IntPtr GetFallbackDefaultConstructor() + { + return Activator.GetFallbackDefaultConstructor(); + } + + // + // Helper to create a delegate on a runtime-supplied type. + // + public static Delegate CreateDelegate(RuntimeTypeHandle typeHandleForDelegate, IntPtr ldftnResult, object thisObject, bool isStatic, bool isOpen) + { + return Delegate.CreateDelegate(typeHandleForDelegate.ToEETypePtr(), ldftnResult, thisObject, isStatic: isStatic, isOpen: isOpen); + } + + // + // Helper to extract the artifact that uniquely identifies a method in the runtime mapping tables. + // + public static IntPtr GetDelegateLdFtnResult(Delegate d, out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver, out bool isInterpreterEntrypoint) + { + return d.GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out isOpenResolver, out isInterpreterEntrypoint); + } + + public static void GetDelegateData(Delegate delegateObj, out object firstParameter, out object helperObject, out IntPtr extraFunctionPointerOrData, out IntPtr functionPointer) + { + firstParameter = delegateObj.m_firstParameter; + helperObject = delegateObj.m_helperObject; + extraFunctionPointerOrData = delegateObj.m_extraFunctionPointerOrData; + functionPointer = delegateObj.m_functionPointer; + } + + public static int GetLoadedModules(TypeManagerHandle[] resultArray) + { + return Internal.Runtime.CompilerHelpers.StartupCodeHelpers.GetLoadedModules(resultArray); + } + + public static IntPtr GetOSModuleFromPointer(IntPtr pointerVal) + { + return RuntimeImports.RhGetOSModuleFromPointer(pointerVal); + } + + public static unsafe bool FindBlob(TypeManagerHandle typeManager, int blobId, IntPtr ppbBlob, IntPtr pcbBlob) + { + return RuntimeImports.RhFindBlob(typeManager, (uint)blobId, (byte**)ppbBlob, (uint*)pcbBlob); + } + + public static IntPtr GetPointerFromTypeHandle(RuntimeTypeHandle typeHandle) + { + return typeHandle.ToEETypePtr().RawValue; + } + + public static TypeManagerHandle GetModuleFromTypeHandle(RuntimeTypeHandle typeHandle) + { + return RuntimeImports.RhGetModuleFromEEType(GetPointerFromTypeHandle(typeHandle)); + } + + public static RuntimeTypeHandle CreateRuntimeTypeHandle(IntPtr ldTokenResult) + { + return new RuntimeTypeHandle(new EETypePtr(ldTokenResult)); + } + + public static unsafe void StoreValueTypeField(IntPtr address, object fieldValue, RuntimeTypeHandle fieldType) + { + RuntimeImports.RhUnbox(fieldValue, ref *(byte*)address, fieldType.ToEETypePtr()); + } + + public static unsafe ref byte GetRawData(object obj) + { + return ref obj.GetRawData(); + } + + public static unsafe object LoadValueTypeField(IntPtr address, RuntimeTypeHandle fieldType) + { + return RuntimeImports.RhBox(fieldType.ToEETypePtr(), ref *(byte*)address); + } + + public static unsafe object LoadPointerTypeField(IntPtr address, RuntimeTypeHandle fieldType) + { + return Pointer.Box(*(void**)address, Type.GetTypeFromHandle(fieldType)); + } + + public static unsafe void StoreValueTypeField(ref byte address, object fieldValue, RuntimeTypeHandle fieldType) + { + RuntimeImports.RhUnbox(fieldValue, ref address, fieldType.ToEETypePtr()); + } + + public static unsafe void StoreValueTypeField(object obj, int fieldOffset, object fieldValue, RuntimeTypeHandle fieldType) + { + ref byte address = ref Unsafe.AddByteOffset(ref obj.GetRawData(), new IntPtr(fieldOffset - ObjectHeaderSize)); + RuntimeImports.RhUnbox(fieldValue, ref address, fieldType.ToEETypePtr()); + } + + public static unsafe object LoadValueTypeField(object obj, int fieldOffset, RuntimeTypeHandle fieldType) + { + ref byte address = ref Unsafe.AddByteOffset(ref obj.GetRawData(), new IntPtr(fieldOffset - ObjectHeaderSize)); + return RuntimeImports.RhBox(fieldType.ToEETypePtr(), ref address); + } + + public static unsafe object LoadPointerTypeField(object obj, int fieldOffset, RuntimeTypeHandle fieldType) + { + ref byte address = ref Unsafe.AddByteOffset(ref obj.GetRawData(), new IntPtr(fieldOffset - ObjectHeaderSize)); + return Pointer.Box((void*)Unsafe.As(ref address), Type.GetTypeFromHandle(fieldType)); + } + + public static unsafe void StoreReferenceTypeField(IntPtr address, object fieldValue) + { + Volatile.Write(ref Unsafe.As(ref *(IntPtr*)address), fieldValue); + } + + public static unsafe object LoadReferenceTypeField(IntPtr address) + { + return Volatile.Read(ref Unsafe.As(ref *(IntPtr*)address)); + } + + public static void StoreReferenceTypeField(object obj, int fieldOffset, object fieldValue) + { + ref byte address = ref Unsafe.AddByteOffset(ref obj.GetRawData(), new IntPtr(fieldOffset - ObjectHeaderSize)); + Volatile.Write(ref Unsafe.As(ref address), fieldValue); + } + + public static object LoadReferenceTypeField(object obj, int fieldOffset) + { + ref byte address = ref Unsafe.AddByteOffset(ref obj.GetRawData(), new IntPtr(fieldOffset - ObjectHeaderSize)); + return Unsafe.As(ref address); + } + + [CLSCompliant(false)] + public static void StoreValueTypeFieldValueIntoValueType(TypedReference typedReference, int fieldOffset, object fieldValue, RuntimeTypeHandle fieldTypeHandle) + { + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + + RuntimeImports.RhUnbox(fieldValue, ref Unsafe.Add(ref typedReference.Value, fieldOffset), fieldTypeHandle.ToEETypePtr()); + } + + [CLSCompliant(false)] + public static object LoadValueTypeFieldValueFromValueType(TypedReference typedReference, int fieldOffset, RuntimeTypeHandle fieldTypeHandle) + { + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + Debug.Assert(fieldTypeHandle.ToEETypePtr().IsValueType); + + return RuntimeImports.RhBox(fieldTypeHandle.ToEETypePtr(), ref Unsafe.Add(ref typedReference.Value, fieldOffset)); + } + + [CLSCompliant(false)] + public static void StoreReferenceTypeFieldValueIntoValueType(TypedReference typedReference, int fieldOffset, object fieldValue) + { + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + + Unsafe.As(ref Unsafe.Add(ref typedReference.Value, fieldOffset)) = fieldValue; + } + + [CLSCompliant(false)] + public static object LoadReferenceTypeFieldValueFromValueType(TypedReference typedReference, int fieldOffset) + { + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + + return Unsafe.As(ref Unsafe.Add(ref typedReference.Value, fieldOffset)); + } + + [CLSCompliant(false)] + public static unsafe object LoadPointerTypeFieldValueFromValueType(TypedReference typedReference, int fieldOffset, RuntimeTypeHandle fieldTypeHandle) + { + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + Debug.Assert(fieldTypeHandle.ToEETypePtr().IsPointer); + + IntPtr ptrValue = Unsafe.As(ref Unsafe.Add(ref typedReference.Value, fieldOffset)); + return Pointer.Box((void*)ptrValue, Type.GetTypeFromHandle(fieldTypeHandle)); + } + + public static unsafe object GetThreadStaticBase(IntPtr cookie) + { + return ThreadStatics.GetThreadStaticBaseForType(*(TypeManagerSlot**)cookie, (int)*((IntPtr*)(cookie) + 1)); + } + + public static unsafe int ObjectHeaderSize => sizeof(EETypePtr); + + [DebuggerGuidedStepThroughAttribute] + public static object CallDynamicInvokeMethod( + object thisPtr, + IntPtr methodToCall, + IntPtr dynamicInvokeHelperMethod, + IntPtr dynamicInvokeHelperGenericDictionary, + object defaultParametersContext, + object[] parameters, + BinderBundle binderBundle, + bool wrapInTargetInvocationException, + bool methodToCallIsThisCall) + { + object result = InvokeUtils.CallDynamicInvokeMethod( + thisPtr, + methodToCall, + dynamicInvokeHelperMethod, + dynamicInvokeHelperGenericDictionary, + defaultParametersContext, + parameters, + binderBundle, + wrapInTargetInvocationException, + methodToCallIsThisCall); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + return result; + } + + public static unsafe void EnsureClassConstructorRun(IntPtr staticClassConstructionContext) + { + StaticClassConstructionContext* context = (StaticClassConstructionContext*)staticClassConstructionContext; + ClassConstructorRunner.EnsureClassConstructorRun(context); + } + + public static object GetEnumValue(Enum e) + { + return e.GetValue(); + } + + public static Type GetEnumUnderlyingType(RuntimeTypeHandle enumTypeHandle) + { + Debug.Assert(enumTypeHandle.ToEETypePtr().IsEnum); + + EETypeElementType elementType = enumTypeHandle.ToEETypePtr().ElementType; + switch (elementType) + { + case EETypeElementType.Boolean: + return typeof(bool); + case EETypeElementType.Char: + return typeof(char); + case EETypeElementType.SByte: + return typeof(sbyte); + case EETypeElementType.Byte: + return typeof(byte); + case EETypeElementType.Int16: + return typeof(short); + case EETypeElementType.UInt16: + return typeof(ushort); + case EETypeElementType.Int32: + return typeof(int); + case EETypeElementType.UInt32: + return typeof(uint); + case EETypeElementType.Int64: + return typeof(long); + case EETypeElementType.UInt64: + return typeof(ulong); + default: + throw new NotSupportedException(); + } + } + + public static RuntimeTypeHandle GetRelatedParameterTypeHandle(RuntimeTypeHandle parameterTypeHandle) + { + EETypePtr elementType = parameterTypeHandle.ToEETypePtr().ArrayElementType; + return new RuntimeTypeHandle(elementType); + } + + public static bool IsValueType(RuntimeTypeHandle type) + { + return type.ToEETypePtr().IsValueType; + } + + public static bool IsInterface(RuntimeTypeHandle type) + { + return type.ToEETypePtr().IsInterface; + } + + public static unsafe object Box(RuntimeTypeHandle type, IntPtr address) + { + return RuntimeImports.RhBox(type.ToEETypePtr(), ref *(byte*)address); + } + + // Used to mutate the first parameter in a closed static delegate. Note that this does no synchronization of any kind; + // use only on delegate instances you're sure nobody else is using. + public static void SetClosedStaticDelegateFirstParameter(Delegate del, object firstParameter) + { + del.SetClosedStaticFirstParameter(firstParameter); + } + + //============================================================================================== + // Execution engine policies. + //============================================================================================== + // + // This returns a generic type with one generic parameter (representing the array element type) + // whose base type and interface list determines what TypeInfo.BaseType and TypeInfo.ImplementedInterfaces + // return for types that return true for IsArray. + // + public static RuntimeTypeHandle ProjectionTypeForArrays + { + get + { + return typeof(Array<>).TypeHandle; + } + } + + // + // Returns the name of a virtual assembly we dump types private class library-Reflectable ty[es for internal class library use. + // The assembly binder visible to apps will never reveal this assembly. + // + // Note that this is not versionable as it is exposed as a const (and needs to be a const so we can used as a custom attribute argument - which + // is the other reason this string is not versionable.) + // + public const string HiddenScopeAssemblyName = "HiddenScope, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; + + // + // This implements the "IsAssignableFrom()" api for runtime-created types. By policy, we let the underlying runtime decide assignability. + // + public static bool IsAssignableFrom(RuntimeTypeHandle dstType, RuntimeTypeHandle srcType) + { + EETypePtr dstEEType = dstType.ToEETypePtr(); + EETypePtr srcEEType = srcType.ToEETypePtr(); + + return RuntimeImports.AreTypesAssignable(srcEEType, dstEEType); + } + + public static bool IsInstanceOfInterface(object obj, RuntimeTypeHandle interfaceTypeHandle) + { + return (null != RuntimeImports.IsInstanceOfInterface(interfaceTypeHandle.ToEETypePtr(), obj)); + } + + // + // Return a type's base type using the runtime type system. If the underlying runtime type system does not support + // this operation, return false and TypeInfo.BaseType will fall back to metadata. + // + // Note that "default(RuntimeTypeHandle)" is a valid result that will map to a null result. (For example, System.Object has a "null" base type.) + // + public static bool TryGetBaseType(RuntimeTypeHandle typeHandle, out RuntimeTypeHandle baseTypeHandle) + { + EETypePtr eeType = typeHandle.ToEETypePtr(); + if (eeType.IsGenericTypeDefinition || eeType.IsPointer || eeType.IsByRef) + { + baseTypeHandle = default(RuntimeTypeHandle); + return false; + } + baseTypeHandle = new RuntimeTypeHandle(eeType.BaseType); + return true; + } + + // + // Return a type's transitive implemeted interface list using the runtime type system. If the underlying runtime type system does not support + // this operation, return null and TypeInfo.ImplementedInterfaces will fall back to metadata. Note that returning null is not the same thing + // as returning a 0-length enumerable. + // + public static IEnumerable TryGetImplementedInterfaces(RuntimeTypeHandle typeHandle) + { + EETypePtr eeType = typeHandle.ToEETypePtr(); + if (eeType.IsGenericTypeDefinition || eeType.IsPointer || eeType.IsByRef) + return null; + + LowLevelList implementedInterfaces = new LowLevelList(); + for (int i = 0; i < eeType.Interfaces.Count; i++) + { + EETypePtr ifcEEType = eeType.Interfaces[i]; + RuntimeTypeHandle ifcrth = new RuntimeTypeHandle(ifcEEType); + if (Callbacks.IsReflectionBlocked(ifcrth)) + continue; + + implementedInterfaces.Add(ifcrth); + } + return implementedInterfaces.ToArray(); + } + + private static RuntimeTypeHandle CreateRuntimeTypeHandle(EETypePtr eeType) + { + return new RuntimeTypeHandle(eeType); + } + + private static EETypePtr CreateEETypePtr(RuntimeTypeHandle runtimeTypeHandle) + { + return runtimeTypeHandle.ToEETypePtr(); + } + + public static int GetGCDescSize(RuntimeTypeHandle typeHandle) + { + EETypePtr eeType = CreateEETypePtr(typeHandle); + return RuntimeImports.RhGetGCDescSize(eeType); + } + + public static int GetInterfaceCount(RuntimeTypeHandle typeHandle) + { + return typeHandle.ToEETypePtr().Interfaces.Count; + } + + public static RuntimeTypeHandle GetInterface(RuntimeTypeHandle typeHandle, int index) + { + EETypePtr eeInterface = typeHandle.ToEETypePtr().Interfaces[index]; + return CreateRuntimeTypeHandle(eeInterface); + } + + public static IntPtr NewInterfaceDispatchCell(RuntimeTypeHandle interfaceTypeHandle, int slotNumber) + { + EETypePtr eeInterfaceType = CreateEETypePtr(interfaceTypeHandle); + IntPtr cell = RuntimeImports.RhNewInterfaceDispatchCell(eeInterfaceType, slotNumber); + if (cell == IntPtr.Zero) + throw new OutOfMemoryException(); + return cell; + } + + public static int GetValueTypeSize(RuntimeTypeHandle typeHandle) + { + return (int)typeHandle.ToEETypePtr().ValueTypeSize; + } + + [Intrinsic] + public static RuntimeTypeHandle GetCanonType(CanonTypeKind kind) + { + // Compiler needs to expand this. This is not expressible in IL. + throw new NotSupportedException(); + } + + public static RuntimeTypeHandle GetGenericDefinition(RuntimeTypeHandle typeHandle) + { + EETypePtr eeType = typeHandle.ToEETypePtr(); + Debug.Assert(eeType.IsGeneric); + return new RuntimeTypeHandle(eeType.GenericDefinition); + } + + public static RuntimeTypeHandle GetGenericArgument(RuntimeTypeHandle typeHandle, int argumentIndex) + { + EETypePtr eeType = typeHandle.ToEETypePtr(); + Debug.Assert(eeType.IsGeneric); + return new RuntimeTypeHandle(eeType.Instantiation[argumentIndex]); + } + + public static RuntimeTypeHandle GetGenericInstantiation(RuntimeTypeHandle typeHandle, out RuntimeTypeHandle[] genericTypeArgumentHandles) + { + EETypePtr eeType = typeHandle.ToEETypePtr(); + + Debug.Assert(eeType.IsGeneric); + + var instantiation = eeType.Instantiation; + genericTypeArgumentHandles = new RuntimeTypeHandle[instantiation.Length]; + for (int i = 0; i < instantiation.Length; i++) + { + genericTypeArgumentHandles[i] = new RuntimeTypeHandle(instantiation[i]); + } + + return new RuntimeTypeHandle(eeType.GenericDefinition); + } + + public static bool IsGenericType(RuntimeTypeHandle typeHandle) + { + return typeHandle.ToEETypePtr().IsGeneric; + } + + public static bool IsArrayType(RuntimeTypeHandle typeHandle) + { + return typeHandle.ToEETypePtr().IsArray; + } + + public static bool IsByRefLike(RuntimeTypeHandle typeHandle) => typeHandle.ToEETypePtr().IsByRefLike; + + public static bool IsDynamicType(RuntimeTypeHandle typeHandle) + { + return typeHandle.ToEETypePtr().IsDynamicType; + } + + public static bool HasCctor(RuntimeTypeHandle typeHandle) + { + return typeHandle.ToEETypePtr().HasCctor; + } + + public static RuntimeTypeHandle RuntimeTypeHandleOf() + { + return new RuntimeTypeHandle(EETypePtr.EETypePtrOf()); + } + + public static IntPtr ResolveDispatchOnType(RuntimeTypeHandle instanceType, RuntimeTypeHandle interfaceType, int slot) + { + return RuntimeImports.RhResolveDispatchOnType(CreateEETypePtr(instanceType), CreateEETypePtr(interfaceType), checked((ushort)slot)); + } + + public static IntPtr ResolveDispatch(object instance, RuntimeTypeHandle interfaceType, int slot) + { + return RuntimeImports.RhResolveDispatch(instance, CreateEETypePtr(interfaceType), checked((ushort)slot)); + } + + public static IntPtr GVMLookupForSlot(RuntimeTypeHandle type, RuntimeMethodHandle slot) + { + return GenericVirtualMethodSupport.GVMLookupForSlot(type, slot); + } + + public static bool IsUnmanagedPointerType(RuntimeTypeHandle typeHandle) + { + return typeHandle.ToEETypePtr().IsPointer; + } + + public static bool IsByRefType(RuntimeTypeHandle typeHandle) + { + return typeHandle.ToEETypePtr().IsByRef; + } + + public static bool IsGenericTypeDefinition(RuntimeTypeHandle typeHandle) + { + return typeHandle.ToEETypePtr().IsGenericTypeDefinition; + } + + // + // This implements the equivalent of the desktop's InvokeUtil::CanPrimitiveWiden() routine. + // + public static bool CanPrimitiveWiden(RuntimeTypeHandle srcType, RuntimeTypeHandle dstType) + { + EETypePtr srcEEType = srcType.ToEETypePtr(); + EETypePtr dstEEType = dstType.ToEETypePtr(); + + if (srcEEType.IsGenericTypeDefinition || dstEEType.IsGenericTypeDefinition) + return false; + if (srcEEType.IsPointer || dstEEType.IsPointer) + return false; + if (srcEEType.IsByRef || dstEEType.IsByRef) + return false; + + if (!srcEEType.IsPrimitive) + return false; + if (!dstEEType.IsPrimitive) + return false; + if (!srcEEType.CorElementTypeInfo.CanWidenTo(dstEEType.CorElementType)) + return false; + return true; + } + + public static object CheckArgument(object srcObject, RuntimeTypeHandle dstType, BinderBundle binderBundle) + { + return InvokeUtils.CheckArgument(srcObject, dstType, binderBundle); + } + + // FieldInfo.SetValueDirect() has a completely different set of rules on how to coerce the argument from + // the other Reflection api. + public static object CheckArgumentForDirectFieldAccess(object srcObject, RuntimeTypeHandle dstType) + { + return InvokeUtils.CheckArgument(srcObject, dstType.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.SetFieldDirect, binderBundle: null); + } + + public static bool IsAssignable(object srcObject, RuntimeTypeHandle dstType) + { + EETypePtr srcEEType = srcObject.EETypePtr; + return RuntimeImports.AreTypesAssignable(srcEEType, dstType.ToEETypePtr()); + } + + //============================================================================================== + // Nullable<> support + //============================================================================================== + public static bool IsNullable(RuntimeTypeHandle declaringTypeHandle) + { + return declaringTypeHandle.ToEETypePtr().IsNullable; + } + + public static RuntimeTypeHandle GetNullableType(RuntimeTypeHandle nullableType) + { + EETypePtr theT = nullableType.ToEETypePtr().NullableType; + return new RuntimeTypeHandle(theT); + } + + /// + /// Locate the file path for a given native application module. + /// + /// Address inside the module + /// Module base address + public static unsafe string TryGetFullPathToApplicationModule(IntPtr ip, out IntPtr moduleBase) + { + moduleBase = RuntimeImports.RhGetOSModuleFromPointer(ip); + if (moduleBase == IntPtr.Zero) + return null; +#if TARGET_UNIX + // RhGetModuleFileName on Unix calls dladdr that accepts any ip. Avoid the redundant lookup + // and pass the ip into RhGetModuleFileName directly. Also, older versions of Musl have a bug + // that leads to crash with the redundant lookup. + byte* pModuleNameUtf8; + int numUtf8Chars = RuntimeImports.RhGetModuleFileName(ip, out pModuleNameUtf8); + string modulePath = System.Text.Encoding.UTF8.GetString(pModuleNameUtf8, numUtf8Chars); +#else // TARGET_UNIX + char* pModuleName; + int numChars = RuntimeImports.RhGetModuleFileName(moduleBase, out pModuleName); + string modulePath = new string(pModuleName, 0, numChars); +#endif // TARGET_UNIX + return modulePath; + } + + public static IntPtr GetRuntimeTypeHandleRawValue(RuntimeTypeHandle runtimeTypeHandle) + { + return runtimeTypeHandle.RawValue; + } + + // if functionPointer points at an import or unboxing stub, find the target of the stub + public static IntPtr GetCodeTarget(IntPtr functionPointer) + { + return RuntimeImports.RhGetCodeTarget(functionPointer); + } + + public static IntPtr GetTargetOfUnboxingAndInstantiatingStub(IntPtr functionPointer) + { + return RuntimeImports.RhGetTargetOfUnboxingAndInstantiatingStub(functionPointer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IntPtr RuntimeCacheLookup(IntPtr context, IntPtr signature, RuntimeObjectFactory factory, object contextObject, out IntPtr auxResult) + { + return TypeLoaderExports.RuntimeCacheLookupInCache(context, signature, factory, contextObject, out auxResult); + } + + //============================================================================================== + // Internals + //============================================================================================== + [CLSCompliant(false)] + public static ReflectionExecutionDomainCallbacks CallbacksIfAvailable + { + get + { + return s_reflectionExecutionDomainCallbacks; + } + } + + [CLSCompliant(false)] + public static ReflectionExecutionDomainCallbacks Callbacks + { + get + { + ReflectionExecutionDomainCallbacks callbacks = s_reflectionExecutionDomainCallbacks; + if (callbacks != null) + return callbacks; + throw new InvalidOperationException(SR.InvalidOperation_TooEarly); + } + } + + internal static TypeLoaderCallbacks TypeLoaderCallbacksIfAvailable + { + get + { + return s_typeLoaderCallbacks; + } + } + + internal static TypeLoaderCallbacks TypeLoaderCallbacks + { + get + { + TypeLoaderCallbacks callbacks = s_typeLoaderCallbacks; + if (callbacks != null) + return callbacks; + throw new InvalidOperationException(SR.InvalidOperation_TooEarly); + } + } + + internal static InteropCallbacks InteropCallbacks + { + get + { + InteropCallbacks callbacks = s_interopCallbacks; + if (callbacks != null) + return callbacks; + throw new InvalidOperationException(SR.InvalidOperation_TooEarly); + } + } + + internal static StackTraceMetadataCallbacks StackTraceCallbacksIfAvailable + { + get + { + return s_stackTraceMetadataCallbacks; + } + } + + public static string TryGetMethodDisplayStringFromIp(IntPtr ip) + { + StackTraceMetadataCallbacks callbacks = StackTraceCallbacksIfAvailable; + if (callbacks == null) + return null; + + ip = RuntimeImports.RhFindMethodStartAddress(ip); + if (ip == IntPtr.Zero) + return null; + + return callbacks.TryGetMethodNameFromStartAddress(ip); + } + + private static volatile ReflectionExecutionDomainCallbacks s_reflectionExecutionDomainCallbacks; + private static TypeLoaderCallbacks s_typeLoaderCallbacks; + private static InteropCallbacks s_interopCallbacks; + + public static void ReportUnhandledException(Exception exception) + { + RuntimeExceptionHelpers.ReportUnhandledException(exception); + } + + public static unsafe RuntimeTypeHandle GetRuntimeTypeHandleFromObjectReference(object obj) + { + return new RuntimeTypeHandle(obj.EETypePtr); + } + + // Move memory which may be on the heap which may have object references in it. + // In general, a memcpy on the heap is unsafe, but this is able to perform the + // correct write barrier such that the GC is not incorrectly impacted. + public static unsafe void BulkMoveWithWriteBarrier(IntPtr dmem, IntPtr smem, int size) + { + RuntimeImports.RhBulkMoveWithWriteBarrier(ref *(byte*)dmem.ToPointer(), ref *(byte*)smem.ToPointer(), (uint)size); + } + + public static IntPtr GetUniversalTransitionThunk() + { + return RuntimeImports.RhGetUniversalTransitionThunk(); + } + + public static object CreateThunksHeap(IntPtr commonStubAddress) + { + object newHeap = RuntimeImports.RhCreateThunksHeap(commonStubAddress); + if (newHeap == null) + throw new OutOfMemoryException(); + return newHeap; + } + + public static IntPtr AllocateThunk(object thunksHeap) + { + IntPtr newThunk = RuntimeImports.RhAllocateThunk(thunksHeap); + if (newThunk == IntPtr.Zero) + throw new OutOfMemoryException(); + TypeLoaderCallbacks.RegisterThunk(newThunk); + return newThunk; + } + + public static void FreeThunk(object thunksHeap, IntPtr thunkAddress) + { + RuntimeImports.RhFreeThunk(thunksHeap, thunkAddress); + } + + public static void SetThunkData(object thunksHeap, IntPtr thunkAddress, IntPtr context, IntPtr target) + { + RuntimeImports.RhSetThunkData(thunksHeap, thunkAddress, context, target); + } + + public static bool TryGetThunkData(object thunksHeap, IntPtr thunkAddress, out IntPtr context, out IntPtr target) + { + return RuntimeImports.RhTryGetThunkData(thunksHeap, thunkAddress, out context, out target); + } + + public static int GetThunkSize() + { + return RuntimeImports.RhGetThunkSize(); + } + + [DebuggerStepThrough] + /* TEMP workaround due to bug 149078 */ + [MethodImpl(MethodImplOptions.NoInlining)] + public static void CallDescrWorker(IntPtr callDescr) + { + RuntimeImports.RhCallDescrWorker(callDescr); + } + + [DebuggerStepThrough] + /* TEMP workaround due to bug 149078 */ + [MethodImpl(MethodImplOptions.NoInlining)] + public static void CallDescrWorkerNative(IntPtr callDescr) + { + RuntimeImports.RhCallDescrWorkerNative(callDescr); + } + + public static Delegate CreateObjectArrayDelegate(Type delegateType, Func invoker) + { + return Delegate.CreateObjectArrayDelegate(delegateType, invoker); + } + + internal static class RawCalliHelper + { + [DebuggerHidden] + [DebuggerStepThrough] + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static unsafe void Call(System.IntPtr pfn, void* arg1, ref T arg2) + => ((delegate*)pfn)(arg1, ref arg2); + + [DebuggerHidden] + [DebuggerStepThrough] + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static unsafe void Call(System.IntPtr pfn, void* arg1, ref T arg2, ref U arg3) + => ((delegate*)pfn)(arg1, ref arg2, ref arg3); + } + + /// + /// This method creates a conservatively reported region and calls a function + /// while that region is conservatively reported. + /// + /// size of buffer to allocated (buffer size described in bytes) + /// function pointer to execute. + /// context to pass to inner function. Passed by-ref to allow for efficient use of a struct as a context. + [DebuggerGuidedStepThroughAttribute] + [CLSCompliant(false)] + public static unsafe void RunFunctionWithConservativelyReportedBuffer(int cbBuffer, delegate* pfnTargetToInvoke, ref T context) + { + RuntimeImports.ConservativelyReportedRegionDesc regionDesc = default; + RunFunctionWithConservativelyReportedBufferInternal(cbBuffer, pfnTargetToInvoke, ref context, ref regionDesc); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } + + // Marked as no-inlining so optimizer won't decide to optimize away the fact that pRegionDesc is a pinned interior pointer. + // This function must also not make a p/invoke transition, or the fixed statement reporting of the ConservativelyReportedRegionDesc + // will be ignored. + [DebuggerGuidedStepThroughAttribute] + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe void RunFunctionWithConservativelyReportedBufferInternal(int cbBuffer, delegate* pfnTargetToInvoke, ref T context, ref RuntimeImports.ConservativelyReportedRegionDesc regionDesc) + { + fixed (RuntimeImports.ConservativelyReportedRegionDesc* pRegionDesc = ®ionDesc) + { + int cbBufferAligned = (cbBuffer + (sizeof(IntPtr) - 1)) & ~(sizeof(IntPtr) - 1); + // The conservative region must be IntPtr aligned, and a multiple of IntPtr in size + void* region = stackalloc IntPtr[cbBufferAligned / sizeof(IntPtr)]; + Buffer.ZeroMemory((byte*)region, (nuint)cbBufferAligned); + RuntimeImports.RhInitializeConservativeReportingRegion(pRegionDesc, region, cbBufferAligned); + + RawCalliHelper.Call((IntPtr)pfnTargetToInvoke, region, ref context); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + + RuntimeImports.RhDisableConservativeReportingRegion(pRegionDesc); + } + } + + /// + /// This method creates a conservatively reported region and calls a function + /// while that region is conservatively reported. + /// + /// size of buffer to allocated (buffer size described in bytes) + /// function pointer to execute. + /// context to pass to inner function. Passed by-ref to allow for efficient use of a struct as a context. + /// context2 to pass to inner function. Passed by-ref to allow for efficient use of a struct as a context. + [DebuggerGuidedStepThroughAttribute] + [CLSCompliant(false)] + public static unsafe void RunFunctionWithConservativelyReportedBuffer(int cbBuffer, delegate* pfnTargetToInvoke, ref T context, ref U context2) + { + RuntimeImports.ConservativelyReportedRegionDesc regionDesc = default; + RunFunctionWithConservativelyReportedBufferInternal(cbBuffer, pfnTargetToInvoke, ref context, ref context2, ref regionDesc); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } + + // Marked as no-inlining so optimizer won't decide to optimize away the fact that pRegionDesc is a pinned interior pointer. + // This function must also not make a p/invoke transition, or the fixed statement reporting of the ConservativelyReportedRegionDesc + // will be ignored. + [DebuggerGuidedStepThroughAttribute] + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe void RunFunctionWithConservativelyReportedBufferInternal(int cbBuffer, delegate* pfnTargetToInvoke, ref T context, ref U context2, ref RuntimeImports.ConservativelyReportedRegionDesc regionDesc) + { + fixed (RuntimeImports.ConservativelyReportedRegionDesc* pRegionDesc = ®ionDesc) + { + int cbBufferAligned = (cbBuffer + (sizeof(IntPtr) - 1)) & ~(sizeof(IntPtr) - 1); + // The conservative region must be IntPtr aligned, and a multiple of IntPtr in size + void* region = stackalloc IntPtr[cbBufferAligned / sizeof(IntPtr)]; + Buffer.ZeroMemory((byte*)region, (nuint)cbBufferAligned); + RuntimeImports.RhInitializeConservativeReportingRegion(pRegionDesc, region, cbBufferAligned); + + RawCalliHelper.Call((IntPtr)pfnTargetToInvoke, region, ref context, ref context2); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + + RuntimeImports.RhDisableConservativeReportingRegion(pRegionDesc); + } + } + + public static string GetLastResortString(RuntimeTypeHandle typeHandle) + { + return typeHandle.LastResortToString; + } + + public static IntPtr RhHandleAlloc(object value, GCHandleType type) + { + return RuntimeImports.RhHandleAlloc(value, type); + } + + public static void RhHandleFree(IntPtr handle) + { + RuntimeImports.RhHandleFree(handle); + } + + public static IntPtr RhpGetCurrentThread() + { + return RuntimeImports.RhpGetCurrentThread(); + } + + public static void RhpInitiateThreadAbort(IntPtr thread, bool rude) + { + Exception ex = new ThreadAbortException(); + RuntimeImports.RhpInitiateThreadAbort(thread, ex, rude); + } + + public static void RhpCancelThreadAbort(IntPtr thread) + { + RuntimeImports.RhpCancelThreadAbort(thread); + } + + public static void RhYield() + { + RuntimeImports.RhYield(); + } + + public static bool SupportsRelativePointers + { + get + { + return Internal.Runtime.MethodTable.SupportsRelativePointers; + } + } + + public static bool IsPrimitive(RuntimeTypeHandle typeHandle) + { + return typeHandle.ToEETypePtr().IsPrimitive && !typeHandle.ToEETypePtr().IsEnum; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs new file mode 100644 index 00000000000000..6a48e27a579b84 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime.CompilerServices; + +namespace Internal.Runtime.Augments +{ + /// + /// This helper class is used to access metadata-based resolution of call stack addresses. + /// To activate the stack trace resolution support, set up an instance of a class + /// derived from this one using the method + /// + /// Internal.Runtime.Augments.RuntimeAugments.InitializeStackTraceMetadataSupport(StackTraceMetadataCallbacks callbacks); + /// + /// + [System.Runtime.CompilerServices.ReflectionBlocked] + [CLSCompliant(false)] + public abstract class StackTraceMetadataCallbacks + { + /// + /// Helper function to format a given method address using the stack trace metadata. + /// Return null if stack trace information is not available. + /// + /// Memory address representing the start of a method + /// Formatted method name or null if metadata for the method is not available + public abstract string TryGetMethodNameFromStartAddress(IntPtr methodStartAddress); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs new file mode 100644 index 00000000000000..98a140c1fb1417 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Collections.Generic; + +using Internal.Runtime.CompilerServices; + +namespace Internal.Runtime.Augments +{ + [CLSCompliant(false)] + [System.Runtime.CompilerServices.ReflectionBlocked] + public abstract class TypeLoaderCallbacks + { + public abstract bool TryGetConstructedGenericTypeForComponents(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle); + public abstract int GetThreadStaticsSizeForDynamicType(int index, out int numTlsCells); + public abstract IntPtr GenericLookupFromContextAndSignature(IntPtr context, IntPtr signature, out IntPtr auxResult); + public abstract bool GetRuntimeMethodHandleComponents(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodArgs); + public abstract bool CompareMethodSignatures(RuntimeSignature signature1, RuntimeSignature signature2); + public abstract IntPtr GetDelegateThunk(Delegate delegateObject, int thunkKind); + public abstract IntPtr TryGetDefaultConstructorForType(RuntimeTypeHandle runtimeTypeHandle); + public abstract bool TryGetGenericVirtualTargetForTypeAndSlot(RuntimeTypeHandle targetHandle, ref RuntimeTypeHandle declaringType, RuntimeTypeHandle[] genericArguments, ref string methodName, ref RuntimeSignature methodSignature, bool lookForDefaultImplementations, out IntPtr methodPointer, out IntPtr dictionaryPointer, out bool slotUpdated); + public abstract bool GetRuntimeFieldHandleComponents(RuntimeFieldHandle runtimeFieldHandle, out RuntimeTypeHandle declaringTypeHandle, out string fieldName); + public abstract bool TryGetPointerTypeForTargetType(RuntimeTypeHandle pointeeTypeHandle, out RuntimeTypeHandle pointerTypeHandle); + public abstract bool TryGetArrayTypeForElementType(RuntimeTypeHandle elementTypeHandle, bool isMdArray, int rank, out RuntimeTypeHandle arrayTypeHandle); + public abstract IntPtr UpdateFloatingDictionary(IntPtr context, IntPtr dictionaryPtr); + + /// + /// Register a new runtime-allocated code thunk in the diagnostic stream. + /// + /// Address of thunk to register + public abstract void RegisterThunk(IntPtr thunkAddress); + + /// + /// Convert an unboxing function pointer to a non-unboxing function pointer + /// + public abstract IntPtr ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(IntPtr unboxingFunctionPointer, RuntimeTypeHandle declaringType); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ArrayHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ArrayHelpers.cs new file mode 100644 index 00000000000000..a6a24bceded8fb --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ArrayHelpers.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Diagnostics.CodeAnalysis; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// Array helpers for generated code. + /// + internal static class ArrayHelpers + { + /// + /// Helper for array allocations via `newobj` IL instruction. Dimensions are passed in as block of integers. + /// The content of the dimensions block may be modified by the helper. + /// + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "The compiler ensures that if we have a TypeHandle of a Rank-1 MdArray, we also generated the SzArray.")] + public static unsafe Array NewObjArray(IntPtr pEEType, int nDimensions, int* pDimensions) + { + EETypePtr eeType = new EETypePtr(pEEType); + Debug.Assert(eeType.IsArray); + Debug.Assert(nDimensions > 0); + + if (eeType.IsSzArray) + { + Array ret = (Array)RuntimeImports.RhNewArray(eeType, pDimensions[0]); + + if (nDimensions > 1) + { + // Jagged arrays have constructor for each possible depth + EETypePtr elementType = eeType.ArrayElementType; + Debug.Assert(elementType.IsSzArray); + + Array[] arrayOfArrays = (Array[])ret; + for (int i = 0; i < arrayOfArrays.Length; i++) + arrayOfArrays[i] = NewObjArray(elementType.RawValue, nDimensions - 1, pDimensions + 1); + } + + return ret; + } + else + { + // Multidimensional arrays have two ctors, one with and one without lower bounds + int rank = eeType.ArrayRank; + Debug.Assert(rank == nDimensions || 2 * rank == nDimensions); + + if (rank < nDimensions) + { + for (int i = 0; i < rank; i++) + { + if (pDimensions[2 * i] != 0) + throw new PlatformNotSupportedException(SR.Arg_NotSupportedNonZeroLowerBound); + + pDimensions[i] = pDimensions[2 * i + 1]; + } + } + + if (rank == 1) + { + // Multidimensional array of rank 1 with 0 lower bounds gets actually allocated + // as an SzArray. SzArray is castable to MdArray rank 1. + int length = pDimensions[0]; + if (length < 0) + { + // Compat: we need to throw OverflowException. Array.CreateInstance would throw ArgumentOutOfRange + throw new OverflowException(); + } + + RuntimeTypeHandle elementTypeHandle = new RuntimeTypeHandle(eeType.ArrayElementType); + return Array.CreateInstance(Type.GetTypeFromHandle(elementTypeHandle), length); + } + + return Array.NewMultiDimArray(eeType, pDimensions, rank); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/DelegateHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/DelegateHelpers.cs new file mode 100644 index 00000000000000..8606466e593696 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/DelegateHelpers.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// Delegate helpers for generated code. + /// + internal static class DelegateHelpers + { + private static object[] s_emptyObjectArray = Array.Empty(); + + internal static object[] GetEmptyObjectArray() + { + return s_emptyObjectArray; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs new file mode 100644 index 00000000000000..7379e9ed471581 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs @@ -0,0 +1,612 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Loader; +using System.Text; +using System.Threading; + +using Internal.Runtime.Augments; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// These methods are used to throw exceptions from generated code. + /// + internal static class InteropHelpers + { + internal static unsafe byte* StringToAnsiString(string str, bool bestFit, bool throwOnUnmappableChar) + { + return PInvokeMarshal.StringToAnsiString(str, bestFit, throwOnUnmappableChar); + } + + public static unsafe string AnsiStringToString(byte* buffer) + { + return PInvokeMarshal.AnsiStringToString(buffer); + } + + internal static unsafe byte* StringToUTF8String(string str) + { + if (str == null) + return null; + + fixed (char* charsPtr = str) + { + int length = Encoding.UTF8.GetByteCount(str); + byte* bytesPtr = (byte*)Marshal.AllocCoTaskMem(checked(length + 1)); + int bytes = Encoding.UTF8.GetBytes(charsPtr, str.Length, bytesPtr, length); + Debug.Assert(bytes == length); + bytesPtr[length] = 0; + return bytesPtr; + } + } + + public static unsafe string UTF8StringToString(byte* buffer) + { + if (buffer == null) + return null; + + return Encoding.UTF8.GetString(buffer, string.strlen(buffer)); + } + + internal static unsafe void StringToByValAnsiString(string str, byte* pNative, int charCount, bool bestFit, bool throwOnUnmappableChar) + { + if (str != null) + { + // Truncate the string if it is larger than specified by SizeConst + int lenUnicode = str.Length; + if (lenUnicode >= charCount) + lenUnicode = charCount - 1; + + fixed (char* pManaged = str) + { + PInvokeMarshal.StringToAnsiString(pManaged, lenUnicode, pNative, /*terminateWithNull=*/true, bestFit, throwOnUnmappableChar); + } + } + else + { + (*pNative) = (byte)'\0'; + } + } + + public static unsafe string ByValAnsiStringToString(byte* buffer, int length) + { + int end = SpanHelpers.IndexOf(ref *(byte*)buffer, 0, length); + if (end != -1) + { + length = end; + } + + return new string((sbyte*)buffer, 0, length); + } + + internal static unsafe void StringToUnicodeFixedArray(string str, ushort* buffer, int length) + { + ReadOnlySpan managed = str; + Span native = new Span((char*)buffer, length); + + int numChars = Math.Min(managed.Length, length - 1); + + managed.Slice(0, numChars).CopyTo(native); + native[numChars] = '\0'; + } + + internal static unsafe string UnicodeToStringFixedArray(ushort* buffer, int length) + { + int end = SpanHelpers.IndexOf(ref *(char*)buffer, '\0', length); + if (end != -1) + { + length = end; + } + + return new string((char*)buffer, 0, length); + } + + internal static unsafe char* StringToUnicodeBuffer(string str) + { + return (char*)Marshal.StringToCoTaskMemUni(str); + } + + public static unsafe byte* AllocMemoryForAnsiStringBuilder(StringBuilder sb) + { + if (sb == null) + { + return null; + } + return (byte *)CoTaskMemAllocAndZeroMemory(checked((sb.Capacity + 2) * Marshal.SystemMaxDBCSCharSize)); + } + + public static unsafe char* AllocMemoryForUnicodeStringBuilder(StringBuilder sb) + { + if (sb == null) + { + return null; + } + return (char *)CoTaskMemAllocAndZeroMemory(checked((sb.Capacity + 2) * 2)); + } + + public static unsafe byte* AllocMemoryForAnsiCharArray(char[] chArray) + { + if (chArray == null) + { + return null; + } + return (byte*)CoTaskMemAllocAndZeroMemory(checked((chArray.Length + 2) * Marshal.SystemMaxDBCSCharSize)); + } + + public static unsafe void AnsiStringToStringBuilder(byte* newBuffer, System.Text.StringBuilder stringBuilder) + { + if (stringBuilder == null) + return; + + PInvokeMarshal.AnsiStringToStringBuilder(newBuffer, stringBuilder); + } + + public static unsafe void UnicodeStringToStringBuilder(ushort* newBuffer, System.Text.StringBuilder stringBuilder) + { + if (stringBuilder == null) + return; + + PInvokeMarshal.UnicodeStringToStringBuilder(newBuffer, stringBuilder); + } + + public static unsafe void StringBuilderToAnsiString(System.Text.StringBuilder stringBuilder, byte* pNative, + bool bestFit, bool throwOnUnmappableChar) + { + if (pNative == null) + return; + + PInvokeMarshal.StringBuilderToAnsiString(stringBuilder, pNative, bestFit, throwOnUnmappableChar); + } + + public static unsafe void StringBuilderToUnicodeString(System.Text.StringBuilder stringBuilder, ushort* destination) + { + if (destination == null) + return; + + PInvokeMarshal.StringBuilderToUnicodeString(stringBuilder, destination); + } + + public static unsafe void WideCharArrayToAnsiCharArray(char[] managedArray, byte* pNative, bool bestFit, bool throwOnUnmappableChar) + { + PInvokeMarshal.WideCharArrayToAnsiCharArray(managedArray, pNative, bestFit, throwOnUnmappableChar); + } + + /// + /// Convert ANSI ByVal byte array to UNICODE wide char array, best fit + /// + /// + /// * This version works with array instead to string, it means that the len must be provided and there will be NO NULL to + /// terminate the array. + /// * The buffer to the UNICODE wide char array must be allocated by the caller. + /// + /// Pointer to the ANSI byte array. Could NOT be null. + /// Wide char array that has already been allocated. + public static unsafe void AnsiCharArrayToWideCharArray(byte* pNative, char[] managedArray) + { + PInvokeMarshal.AnsiCharArrayToWideCharArray(pNative, managedArray); + } + + /// + /// Convert a single UNICODE wide char to a single ANSI byte. + /// + /// single UNICODE wide char value + /// Enable best-fit mapping behavior + /// Throw an exception on an unmappable Unicode character + public static unsafe byte WideCharToAnsiChar(char managedValue, bool bestFit, bool throwOnUnmappableChar) + { + return PInvokeMarshal.WideCharToAnsiChar(managedValue, bestFit, throwOnUnmappableChar); + } + + /// + /// Convert a single ANSI byte value to a single UNICODE wide char value, best fit. + /// + /// Single ANSI byte value. + public static unsafe char AnsiCharToWideChar(byte nativeValue) + { + return PInvokeMarshal.AnsiCharToWideChar(nativeValue); + } + + internal static double DateTimeToOleDateTime(DateTime value) + { + return value.ToOADate(); + } + + internal static DateTime OleDateTimeToDateTime(double value) + { + return DateTime.FromOADate(value); + } + + internal static long DecimalToOleCurrency(decimal value) + { + return decimal.ToOACurrency(value); + } + + internal static decimal OleCurrencyToDecimal(long value) + { + return decimal.FromOACurrency(value); + } + + internal static unsafe string BstrBufferToString(char* buffer) + { + if (buffer == null) + return null; + + return Marshal.PtrToStringBSTR((IntPtr)buffer); + } + + internal static unsafe byte* StringToAnsiBstrBuffer(string s) + { + if (s is null) + { + return (byte*)IntPtr.Zero; + } + + int stringLength = s.Length; + fixed (char* pStr = s) + { + int nativeLength = PInvokeMarshal.GetByteCount(pStr, stringLength); + byte* bstr = (byte*)Marshal.AllocBSTRByteLen((uint)nativeLength); + PInvokeMarshal.ConvertWideCharToMultiByte(pStr, stringLength, bstr, nativeLength, bestFit: false, throwOnUnmappableChar: false); + return bstr; + } + } + + internal static unsafe string AnsiBstrBufferToString(byte* buffer) + { + if (buffer == null) + return null; + + return Marshal.PtrToStringAnsi((IntPtr)buffer, (int)Marshal.SysStringByteLen((IntPtr)buffer)); + } + + internal static unsafe IntPtr ResolvePInvoke(MethodFixupCell* pCell) + { + if (pCell->Target != IntPtr.Zero) + return pCell->Target; + + return ResolvePInvokeSlow(pCell); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static unsafe IntPtr ResolvePInvokeSlow(MethodFixupCell* pCell) + { + int lastSystemError = Marshal.GetLastSystemError(); + + ModuleFixupCell* pModuleCell = pCell->Module; + IntPtr hModule = pModuleCell->Handle; + if (hModule == IntPtr.Zero) + { + FixupModuleCell(pModuleCell); + hModule = pModuleCell->Handle; + } + + FixupMethodCell(hModule, pCell); + + Marshal.SetLastSystemError(lastSystemError); + + return pCell->Target; + } + + internal static unsafe void FreeLibrary(IntPtr hModule) + { +#if !TARGET_UNIX + Interop.Kernel32.FreeLibrary(hModule); +#else + Interop.Sys.FreeLibrary(hModule); +#endif + } + + private static unsafe string GetModuleName(ModuleFixupCell* pCell) + { + byte* pModuleName = (byte*)pCell->ModuleName; + return Encoding.UTF8.GetString(pModuleName, string.strlen(pModuleName)); + } + + internal static unsafe void FixupModuleCell(ModuleFixupCell* pCell) + { + string moduleName = GetModuleName(pCell); + + uint dllImportSearchPath = 0; + bool hasDllImportSearchPath = (pCell->DllImportSearchPathAndCookie & InteropDataConstants.HasDllImportSearchPath) != 0; + if (hasDllImportSearchPath) + { + dllImportSearchPath = pCell->DllImportSearchPathAndCookie & ~InteropDataConstants.HasDllImportSearchPath; + } + + Assembly callingAssembly = RuntimeAugments.Callbacks.GetAssemblyForHandle(new RuntimeTypeHandle(pCell->CallingAssemblyType)); + + // First check if there's a NativeLibrary callback and call it to attempt the resolution + IntPtr hModule = NativeLibrary.LoadLibraryCallbackStub(moduleName, callingAssembly, hasDllImportSearchPath, dllImportSearchPath); + if (hModule == IntPtr.Zero) + { + // NativeLibrary callback didn't resolve the library. Use built-in rules. + NativeLibrary.LoadLibErrorTracker loadLibErrorTracker = default; + + hModule = NativeLibrary.LoadBySearch( + callingAssembly, + searchAssemblyDirectory: false, + dllImportSearchPathFlags: 0, + ref loadLibErrorTracker, + moduleName); + + if (hModule == IntPtr.Zero) + { + // Built-in rules didn't resolve the library. Use AssemblyLoadContext as a last chance attempt. + AssemblyLoadContext loadContext = AssemblyLoadContext.GetLoadContext(callingAssembly); + hModule = loadContext.GetResolvedUnmanagedDll(callingAssembly, moduleName); + } + + if (hModule == IntPtr.Zero) + { + // If the module is still unresolved, this is an error. + loadLibErrorTracker.Throw(moduleName); + } + } + + Debug.Assert(hModule != IntPtr.Zero); + var oldValue = Interlocked.CompareExchange(ref pCell->Handle, hModule, IntPtr.Zero); + if (oldValue != IntPtr.Zero) + { + // Some other thread won the race to fix it up. + FreeLibrary(hModule); + } + } + + internal static unsafe void FixupMethodCell(IntPtr hModule, MethodFixupCell* pCell) + { + byte* methodName = (byte*)pCell->MethodName; + IntPtr pTarget; + +#if TARGET_WINDOWS + CharSet charSetMangling = pCell->CharSetMangling; + if (charSetMangling == 0) + { + // Look for the user-provided entry point name only + pTarget = Interop.Kernel32.GetProcAddress(hModule, methodName); + } + else + if (charSetMangling == CharSet.Ansi) + { + // For ANSI, look for the user-provided entry point name first. + // If that does not exist, try the charset suffix. + pTarget = Interop.Kernel32.GetProcAddress(hModule, methodName); + if (pTarget == IntPtr.Zero) + pTarget = GetProcAddressWithSuffix(hModule, methodName, (byte)'A'); + } + else + { + // For Unicode, look for the entry point name with the charset suffix first. + // The 'W' API takes precedence over the undecorated one. + pTarget = GetProcAddressWithSuffix(hModule, methodName, (byte)'W'); + if (pTarget == IntPtr.Zero) + pTarget = Interop.Kernel32.GetProcAddress(hModule, methodName); + } +#else + pTarget = Interop.Sys.GetProcAddress(hModule, methodName); +#endif + if (pTarget == IntPtr.Zero) + { + string entryPointName = Encoding.UTF8.GetString(methodName, string.strlen(methodName)); + throw new EntryPointNotFoundException(SR.Format(SR.Arg_EntryPointNotFoundExceptionParameterized, entryPointName, GetModuleName(pCell->Module))); + } + + pCell->Target = pTarget; + } + +#if TARGET_WINDOWS + private static unsafe IntPtr GetProcAddressWithSuffix(IntPtr hModule, byte* methodName, byte suffix) + { + int nameLength = string.strlen(methodName); + + // We need to add an extra byte for the suffix, and an extra byte for the null terminator + byte* probedMethodName = stackalloc byte[nameLength + 2]; + + for (int i = 0; i < nameLength; i++) + { + probedMethodName[i] = methodName[i]; + } + + probedMethodName[nameLength + 1] = 0; + + probedMethodName[nameLength] = suffix; + + return Interop.Kernel32.GetProcAddress(hModule, probedMethodName); + } +#endif + + internal static unsafe void* CoTaskMemAllocAndZeroMemory(int size) + { + byte* ptr = (byte*)Marshal.AllocCoTaskMem(size); + + // Marshal.AllocCoTaskMem will throw OOMException if out of memory + Debug.Assert(ptr != null); + + Buffer.ZeroMemory(ptr, (uint)size); + return ptr; + } + + internal static unsafe void CoTaskMemFree(void* p) + { + Marshal.FreeCoTaskMem((IntPtr)p); + } + + /// + /// Retrieves the function pointer for the current open static delegate that is being called + /// + public static IntPtr GetCurrentCalleeOpenStaticDelegateFunctionPointer() + { + return PInvokeMarshal.GetCurrentCalleeOpenStaticDelegateFunctionPointer(); + } + + /// + /// Retrieves the current delegate that is being called + /// + public static T GetCurrentCalleeDelegate() where T : class // constraint can't be System.Delegate + { + return PInvokeMarshal.GetCurrentCalleeDelegate(); + } + + public static IntPtr ConvertManagedComInterfaceToNative(object pUnk, Guid interfaceGuid) + { + if (pUnk == null) + { + return IntPtr.Zero; + } + +#if TARGET_WINDOWS +#pragma warning disable CA1416 + return ComWrappers.ComInterfaceForObject(pUnk, interfaceGuid); +#pragma warning restore CA1416 +#else + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); +#endif + } + + public static IntPtr ConvertManagedComInterfaceToIUnknown(object pUnk) + { + if (pUnk == null) + { + return IntPtr.Zero; + } + +#if TARGET_WINDOWS +#pragma warning disable CA1416 + return ComWrappers.ComInterfaceForObject(pUnk); +#pragma warning restore CA1416 +#else + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); +#endif + } + + public static object ConvertNativeComInterfaceToManaged(IntPtr pUnk) + { + if (pUnk == IntPtr.Zero) + { + return null; + } + +#if TARGET_WINDOWS +#pragma warning disable CA1416 + return ComWrappers.ComObjectForInterface(pUnk); +#pragma warning restore CA1416 +#else + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); +#endif + } + + internal static int AsAnyGetNativeSize(object o) + { + // Array, string and StringBuilder are not implemented. + if (o.EETypePtr.IsArray || + o is string || + o is StringBuilder) + { + throw new PlatformNotSupportedException(); + } + + // Assume that this is a type with layout. + return Marshal.SizeOf(o.GetType()); + } + + internal static void AsAnyMarshalManagedToNative(object o, IntPtr address) + { + // Array, string and StringBuilder are not implemented. + if (o.EETypePtr.IsArray || + o is string || + o is StringBuilder) + { + throw new PlatformNotSupportedException(); + } + + Marshal.StructureToPtr(o, address, fDeleteOld: false); + } + + internal static void AsAnyMarshalNativeToManaged(IntPtr address, object o) + { + // Array, string and StringBuilder are not implemented. + if (o.EETypePtr.IsArray || + o is string || + o is StringBuilder) + { + throw new PlatformNotSupportedException(); + } + + Marshal.PtrToStructureImpl(address, o); + } + + internal static void AsAnyCleanupNative(IntPtr address, object o) + { + // Array, string and StringBuilder are not implemented. + if (o.EETypePtr.IsArray || + o is string || + o is StringBuilder) + { + throw new PlatformNotSupportedException(); + } + + Marshal.DestroyStructure(address, o.GetType()); + } + + internal static unsafe object? VariantToObject(IntPtr pSrcNativeVariant) + { + if (pSrcNativeVariant == IntPtr.Zero) + { + return null; + } + +#if TARGET_WINDOWS +#pragma warning disable CA1416 + return Marshal.GetObjectForNativeVariant(pSrcNativeVariant); +#pragma warning restore CA1416 +#else + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); +#endif + } + + internal static unsafe void ConvertObjectToVariant(object? obj, IntPtr pDstNativeVariant) + { +#if TARGET_WINDOWS +#pragma warning disable CA1416 + Marshal.GetNativeVariantForObject(obj, pDstNativeVariant); +#pragma warning restore CA1416 +#else + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); +#endif + } + + internal static unsafe void CleanupVariant(IntPtr pDstNativeVariant) + { +#if TARGET_WINDOWS +#pragma warning disable CA1416 + Variant* data = (Variant*)pDstNativeVariant; + data->Clear(); +#pragma warning restore CA1416 +#else + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); +#endif + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct ModuleFixupCell + { + public IntPtr Handle; + public IntPtr ModuleName; + public EETypePtr CallingAssemblyType; + public uint DllImportSearchPathAndCookie; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct MethodFixupCell + { + public IntPtr Target; + public IntPtr MethodName; + public ModuleFixupCell* Module; + public CharSet CharSetMangling; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LdTokenHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LdTokenHelpers.cs new file mode 100644 index 00000000000000..aba24c09682714 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LdTokenHelpers.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Reflection.Core.NonPortable; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// These methods are used to implement ldtoken instruction. + /// + internal static class LdTokenHelpers + { + private static RuntimeTypeHandle GetRuntimeTypeHandle(IntPtr pEEType) + { + return new RuntimeTypeHandle(new EETypePtr(pEEType)); + } + + private static unsafe RuntimeMethodHandle GetRuntimeMethodHandle(IntPtr pHandleSignature) + { + RuntimeMethodHandle returnValue; + *(IntPtr*)&returnValue = pHandleSignature; + return returnValue; + } + + private static unsafe RuntimeFieldHandle GetRuntimeFieldHandle(IntPtr pHandleSignature) + { + RuntimeFieldHandle returnValue; + *(IntPtr*)&returnValue = pHandleSignature; + return returnValue; + } + + private static Type GetRuntimeType(IntPtr pEEType) + { + return Type.GetTypeFromEETypePtr(new EETypePtr(pEEType)); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 00000000000000..720f0c3180b207 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Runtime.CompilerServices; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// Container class to run specific class constructors in a defined order. Since we can't + /// directly invoke class constructors in C#, they're renamed Initialize. + /// + internal static class LibraryInitializer + { + public static void InitializeLibrary() + { + PreallocatedOutOfMemoryException.Initialize(); + ClassConstructorRunner.Initialize(); + TypeLoaderExports.Initialize(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs new file mode 100644 index 00000000000000..5f19f6b01e1970 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs @@ -0,0 +1,390 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Runtime.CompilerServices; + +using Internal.Runtime; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// Math helpers for generated code. The helpers marked with [RuntimeExport] and the type + /// itself need to be public because they constitute a public contract with the .NET Native toolchain. + /// + [CLSCompliant(false)] + public static class MathHelpers + { +#if !TARGET_64BIT + // + // 64-bit checked multiplication for 32-bit platforms + // + + private const string RuntimeLibrary = "*"; + + // Helper to multiply two 32-bit uints + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Mul32x32To64(uint a, uint b) + { + return a * (ulong)b; + } + + // Helper to get high 32-bit of 64-bit int + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Hi32Bits(long a) + { + return (uint)(a >> 32); + } + + // Helper to get high 32-bit of 64-bit int + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Hi32Bits(ulong a) + { + return (uint)(a >> 32); + } + + [RuntimeExport("LMulOvf")] + public static long LMulOvf(long i, long j) + { + long ret; + + // Remember the sign of the result + int sign = (int)(Hi32Bits(i) ^ Hi32Bits(j)); + + // Convert to unsigned multiplication + if (i < 0) i = -i; + if (j < 0) j = -j; + + // Get the upper 32 bits of the numbers + uint val1High = Hi32Bits(i); + uint val2High = Hi32Bits(j); + + ulong valMid; + + if (val1High == 0) + { + // Compute the 'middle' bits of the long multiplication + valMid = Mul32x32To64(val2High, (uint)i); + } + else + { + if (val2High != 0) + goto ThrowExcep; + // Compute the 'middle' bits of the long multiplication + valMid = Mul32x32To64(val1High, (uint)j); + } + + // See if any bits after bit 32 are set + if (Hi32Bits(valMid) != 0) + goto ThrowExcep; + + ret = (long)(Mul32x32To64((uint)i, (uint)j) + (valMid << 32)); + + // check for overflow + if (Hi32Bits(ret) < (uint)valMid) + goto ThrowExcep; + + if (sign >= 0) + { + // have we spilled into the sign bit? + if (ret < 0) + goto ThrowExcep; + } + else + { + ret = -ret; + // have we spilled into the sign bit? + if (ret > 0) + goto ThrowExcep; + } + return ret; + + ThrowExcep: + return ThrowLngOvf(); + } + + [RuntimeExport("ULMulOvf")] + public static ulong ULMulOvf(ulong i, ulong j) + { + ulong ret; + + // Get the upper 32 bits of the numbers + uint val1High = Hi32Bits(i); + uint val2High = Hi32Bits(j); + + ulong valMid; + + if (val1High == 0) + { + if (val2High == 0) + return Mul32x32To64((uint)i, (uint)j); + // Compute the 'middle' bits of the long multiplication + valMid = Mul32x32To64(val2High, (uint)i); + } + else + { + if (val2High != 0) + goto ThrowExcep; + // Compute the 'middle' bits of the long multiplication + valMid = Mul32x32To64(val1High, (uint)j); + } + + // See if any bits after bit 32 are set + if (Hi32Bits(valMid) != 0) + goto ThrowExcep; + + ret = Mul32x32To64((uint)i, (uint)j) + (valMid << 32); + + // check for overflow + if (Hi32Bits(ret) < (uint)valMid) + goto ThrowExcep; + return ret; + + ThrowExcep: + return ThrowULngOvf(); + } + + [RuntimeImport(RuntimeLibrary, "RhpULMod")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern ulong RhpULMod(ulong i, ulong j); + + public static ulong ULMod(ulong i, ulong j) + { + if (j == 0) + return ThrowULngDivByZero(); + else + return RhpULMod(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpLMod")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern long RhpLMod(long i, long j); + + public static long LMod(long i, long j) + { + if (j == 0) + return ThrowLngDivByZero(); + else + return RhpLMod(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpULDiv")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern ulong RhpULDiv(ulong i, ulong j); + + public static ulong ULDiv(ulong i, ulong j) + { + if (j == 0) + return ThrowULngDivByZero(); + else + return RhpULDiv(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpLDiv")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern long RhpLDiv(long i, long j); + + public static long LDiv(long i, long j) + { + if (j == 0) + return ThrowLngDivByZero(); + else if (j == -1 && i == long.MinValue) + return ThrowLngArithExc(); + else + return RhpLDiv(i, j); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static long ThrowLngDivByZero() + { + throw new DivideByZeroException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static ulong ThrowULngDivByZero() + { + throw new DivideByZeroException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static long ThrowLngArithExc() + { + throw new ArithmeticException(); + } +#endif // TARGET_64BIT + + [RuntimeExport("Dbl2IntOvf")] + public static int Dbl2IntOvf(double val) + { + const double two31 = 2147483648.0; + + // Note that this expression also works properly for val = NaN case + if (val > -two31 - 1 && val < two31) + return unchecked((int)val); + + return ThrowIntOvf(); + } + + [RuntimeExport("Dbl2UIntOvf")] + public static uint Dbl2UIntOvf(double val) + { + // Note that this expression also works properly for val = NaN case + if (val > -1.0 && val < 4294967296.0) + return unchecked((uint)val); + + return ThrowUIntOvf(); + } + + [RuntimeExport("Dbl2LngOvf")] + public static long Dbl2LngOvf(double val) + { + const double two63 = 2147483648.0 * 4294967296.0; + + // Note that this expression also works properly for val = NaN case + // We need to compare with the very next double to two63. 0x402 is epsilon to get us there. + if (val > -two63 - 0x402 && val < two63) + return unchecked((long)val); + + return ThrowLngOvf(); + } + + [RuntimeExport("Dbl2ULngOvf")] + public static ulong Dbl2ULngOvf(double val) + { + const double two64 = 2.0 * 2147483648.0 * 4294967296.0; + + // Note that this expression also works properly for val = NaN case + if (val > -1.0 && val < two64) + return unchecked((ulong)val); + + return ThrowULngOvf(); + } + + [RuntimeExport("Flt2IntOvf")] + public static int Flt2IntOvf(float val) + { + const double two31 = 2147483648.0; + + // Note that this expression also works properly for val = NaN case + if (val > -two31 - 1 && val < two31) + return ((int)val); + + return ThrowIntOvf(); + } + + [RuntimeExport("Flt2LngOvf")] + public static long Flt2LngOvf(float val) + { + const double two63 = 2147483648.0 * 4294967296.0; + + // Note that this expression also works properly for val = NaN case + // We need to compare with the very next double to two63. 0x402 is epsilon to get us there. + if (val > -two63 - 0x402 && val < two63) + return ((long)val); + + return ThrowIntOvf(); + } + +#if TARGET_ARM + [RuntimeImport(RuntimeLibrary, "RhpIDiv")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern int RhpIDiv(int i, int j); + + public static int IDiv(int i, int j) + { + if (j == 0) + return ThrowIntDivByZero(); + else if (j == -1 && i == int.MinValue) + return ThrowIntArithExc(); + else + return RhpIDiv(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpUDiv")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern uint RhpUDiv(uint i, uint j); + + public static long UDiv(uint i, uint j) + { + if (j == 0) + return ThrowUIntDivByZero(); + else + return RhpUDiv(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpIMod")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern int RhpIMod(int i, int j); + + public static int IMod(int i, int j) + { + if (j == 0) + return ThrowIntDivByZero(); + else + return RhpIMod(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpUMod")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern uint RhpUMod(uint i, uint j); + + public static long UMod(uint i, uint j) + { + if (j == 0) + return ThrowUIntDivByZero(); + else + return RhpUMod(i, j); + } +#endif // TARGET_ARM + + // + // Matching return types of throw helpers enables tailcalling them. It improves performance + // of the hot path because of it does not need to raise full stackframe. + // + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int ThrowIntOvf() + { + throw new OverflowException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static uint ThrowUIntOvf() + { + throw new OverflowException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static long ThrowLngOvf() + { + throw new OverflowException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static ulong ThrowULngOvf() + { + throw new OverflowException(); + } + +#if TARGET_ARM + [MethodImpl(MethodImplOptions.NoInlining)] + private static int ThrowIntDivByZero() + { + throw new DivideByZeroException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static uint ThrowUIntDivByZero() + { + throw new DivideByZeroException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int ThrowIntArithExc() + { + throw new ArithmeticException(); + } +#endif // TARGET_ARM + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ReflectionHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ReflectionHelpers.cs new file mode 100644 index 00000000000000..f84d0899ba2eca --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ReflectionHelpers.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; + +using Internal.Runtime.Augments; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// Set of helpers used to implement callsite-specific reflection intrinsics. + /// + internal static class ReflectionHelpers + { + // This entry is used to implement Type.GetType()'s ability to detect the calling assembly and use it as + // a default assembly name. + public static Type GetType(string typeName, string callingAssemblyName, bool throwOnError, bool ignoreCase) + { + return ExtensibleGetType(typeName, callingAssemblyName, null, null, throwOnError: throwOnError, ignoreCase: ignoreCase); + } + + // This entry is used to implement Type.GetType()'s ability to detect the calling assembly and use it as + // a default assembly name. + public static Type ExtensibleGetType(string typeName, string callingAssemblyName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase) + { + return RuntimeAugments.Callbacks.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, callingAssemblyName); + } + + // This supports Assembly.GetExecutingAssembly() intrinsic expansion in the compiler + public static Assembly GetExecutingAssembly(RuntimeTypeHandle typeHandle) + { + return RuntimeAugments.Callbacks.GetAssemblyForHandle(typeHandle); + } + + // This supports MethodBase.GetCurrentMethod() intrinsic expansion in the compiler + public static MethodBase GetCurrentMethodNonGeneric(RuntimeMethodHandle methodHandle) + { + return MethodBase.GetMethodFromHandle(methodHandle); + } + + // This supports MethodBase.GetCurrentMethod() intrinsic expansion in the compiler + public static MethodBase GetCurrentMethodGeneric(RuntimeMethodHandle methodHandle, RuntimeTypeHandle typeHandle) + { + return MethodBase.GetMethodFromHandle(methodHandle, typeHandle); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SharedCodeHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SharedCodeHelpers.cs new file mode 100644 index 00000000000000..92894018a0b3b7 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SharedCodeHelpers.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// These methods are used to implement shared generic code. + /// + internal static class SharedCodeHelpers + { + public static unsafe MethodTable* GetOrdinalInterface(MethodTable* pType, ushort interfaceIndex) + { + Debug.Assert(interfaceIndex <= pType->NumInterfaces); + return pType->InterfaceMap[interfaceIndex].InterfaceType; + } + + public static unsafe MethodTable* GetCurrentSharedThunkContext() + { + // TODO: We should return the current context from the ThunkPool + // https://github.com/dotnet/runtimelab/issues/1442 + return null; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Extensions.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Extensions.cs new file mode 100644 index 00000000000000..544118d375b4e0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Extensions.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.Versioning; +using System.Threading; + +using Internal.Runtime.Augments; + +using Debug = Internal.Runtime.CompilerHelpers.StartupDebug; + +namespace Internal.Runtime.CompilerHelpers +{ + public partial class StartupCodeHelpers + { + internal static unsafe void InitializeCommandLineArgsW(int argc, char** argv) + { + string[] args = new string[argc]; + for (int i = 0; i < argc; ++i) + { + args[i] = new string(argv[i]); + } + Environment.SetCommandLineArgs(args); + } + + internal static unsafe void InitializeCommandLineArgs(int argc, sbyte** argv) + { + string[] args = new string[argc]; + for (int i = 0; i < argc; ++i) + { + args[i] = new string(argv[i]); + } + Environment.SetCommandLineArgs(args); + } + + private static string[] GetMainMethodArguments() + { + // GetCommandLineArgs includes the executable name, Main() arguments do not. + string[] args = Environment.GetCommandLineArgs(); + + Debug.Assert(args.Length > 0); + + string[] mainArgs = new string[args.Length - 1]; + Array.Copy(args, 1, mainArgs, 0, mainArgs.Length); + + return mainArgs; + } + + private static void SetLatchedExitCode(int exitCode) + { + Environment.ExitCode = exitCode; + } + + // Shuts down the class library and returns the process exit code. + private static int Shutdown() + { + Thread.WaitForForegroundThreads(); + + Environment.ShutdownCore(); + + return Environment.ExitCode; + } + +#if TARGET_WINDOWS + [SupportedOSPlatform("windows")] + private static void InitializeApartmentState(ApartmentState state) + { + Thread.CurrentThread.SetApartmentState(state); + } +#endif + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Reflection.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Reflection.cs new file mode 100644 index 00000000000000..7fc2641f43cea6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Reflection.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Assembly = System.Reflection.Assembly; + +namespace Internal.Runtime.CompilerHelpers +{ + public partial class StartupCodeHelpers + { + private static RuntimeTypeHandle s_entryAssemblyType; + + internal static void InitializeEntryAssembly(RuntimeTypeHandle entryAssemblyType) + { + s_entryAssemblyType = entryAssemblyType; + } + + internal static Assembly? GetEntryAssembly() + { + return Type.GetTypeFromHandle(s_entryAssemblyType)?.Assembly; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs new file mode 100644 index 00000000000000..b19f4622deaf6c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// Set of helpers used to implement synchronized methods. + /// + internal static class SynchronizedMethodHelpers + { + private static void MonitorEnter(object obj, ref bool lockTaken) + { + // Inlined Monitor.Enter with a few tweaks + Lock lck = Monitor.GetLock(obj); + if (lck.TryAcquire(0)) + { + lockTaken = true; + return; + } + Monitor.TryAcquireContended(lck, obj, Timeout.Infinite); + lockTaken = true; + } + private static void MonitorExit(object obj, ref bool lockTaken) + { + // Inlined Monitor.Exit with a few tweaks + if (!lockTaken) return; + Monitor.GetLock(obj).Release(); + lockTaken = false; + } + + private static void MonitorEnterStatic(IntPtr pEEType, ref bool lockTaken) + { + // Inlined Monitor.Enter with a few tweaks + object obj = GetStaticLockObject(pEEType); + Lock lck = Monitor.GetLock(obj); + if (lck.TryAcquire(0)) + { + lockTaken = true; + return; + } + Monitor.TryAcquireContended(lck, obj, Timeout.Infinite); + lockTaken = true; + } + private static void MonitorExitStatic(IntPtr pEEType, ref bool lockTaken) + { + // Inlined Monitor.Exit with a few tweaks + if (!lockTaken) return; + object obj = GetStaticLockObject(pEEType); + Monitor.GetLock(obj).Release(); + lockTaken = false; + } + + private static object GetStaticLockObject(IntPtr pEEType) + { + return Type.GetTypeFromEETypePtr(new EETypePtr(pEEType)); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs new file mode 100644 index 00000000000000..a25590771a03b6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// These methods are used to throw exceptions from generated code. The type and methods + /// need to be public as they constitute a public contract with the .NET Native toolchain. + /// + public static class ThrowHelpers + { + internal static void ThrowBodyRemoved() + { + throw new NotSupportedException(SR.NotSupported_BodyRemoved); + } + + internal static void ThrowFeatureBodyRemoved() + { + throw new NotSupportedException(SR.NotSupported_FeatureBodyRemoved); + } + + internal static void ThrowInstanceBodyRemoved() + { + throw new NotSupportedException(SR.NotSupported_InstanceBodyRemoved); + } + + internal static void ThrowUnavailableType() + { + throw new TypeLoadException(SR.Arg_UnavailableTypeLoadException); + } + + public static void ThrowOverflowException() + { + throw new OverflowException(); + } + + public static void ThrowIndexOutOfRangeException() + { + throw new IndexOutOfRangeException(); + } + + public static void ThrowNullReferenceException() + { + throw new NullReferenceException(); + } + + public static void ThrowDivideByZeroException() + { + throw new DivideByZeroException(); + } + + public static void ThrowArrayTypeMismatchException() + { + throw new ArrayTypeMismatchException(); + } + + public static void ThrowPlatformNotSupportedException() + { + throw new PlatformNotSupportedException(); + } + + public static void ThrowNotImplementedException() + { + throw NotImplemented.ByDesign; + } + + public static void ThrowNotSupportedException() + { + throw new NotSupportedException(); + } + + public static void ThrowBadImageFormatException(ExceptionStringID id) + { + throw TypeLoaderExceptionHelper.CreateBadImageFormatException(id); + } + + public static void ThrowTypeLoadException(ExceptionStringID id, string className, string typeName) + { + throw TypeLoaderExceptionHelper.CreateTypeLoadException(id, className, typeName); + } + + public static void ThrowTypeLoadExceptionWithArgument(ExceptionStringID id, string className, string typeName, string messageArg) + { + throw TypeLoaderExceptionHelper.CreateTypeLoadException(id, className, typeName, messageArg); + } + + public static void ThrowMissingMethodException(ExceptionStringID id, string methodName) + { + throw TypeLoaderExceptionHelper.CreateMissingMethodException(id, methodName); + } + + public static void ThrowMissingFieldException(ExceptionStringID id, string fieldName) + { + throw TypeLoaderExceptionHelper.CreateMissingFieldException(id, fieldName); + } + + public static void ThrowFileNotFoundException(ExceptionStringID id, string fileName) + { + throw TypeLoaderExceptionHelper.CreateFileNotFoundException(id, fileName); + } + + public static void ThrowInvalidProgramException(ExceptionStringID id) + { + throw TypeLoaderExceptionHelper.CreateInvalidProgramException(id); + } + + public static void ThrowInvalidProgramExceptionWithArgument(ExceptionStringID id, string methodName) + { + throw TypeLoaderExceptionHelper.CreateInvalidProgramException(id, methodName); + } + + public static void ThrowMarshalDirectiveException(ExceptionStringID id) + { + throw TypeLoaderExceptionHelper.CreateMarshalDirectiveException(id); + } + + public static void ThrowArgumentException() + { + throw new ArgumentException(); + } + + public static void ThrowArgumentOutOfRangeException() + { + throw new ArgumentOutOfRangeException(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/TypedReferenceHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/TypedReferenceHelpers.cs new file mode 100644 index 00000000000000..2f44bc8d62fa59 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/TypedReferenceHelpers.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// These methods are used to implement TypedReference-related instructions. + /// + internal static class TypedReferenceHelpers + { + public static Type TypeHandleToRuntimeTypeMaybeNull(RuntimeTypeHandle typeHandle) + { + if (typeHandle.IsNull) + return null; + return Type.GetTypeFromHandle(typeHandle); + } + + public static RuntimeTypeHandle TypeHandleToRuntimeTypeHandleMaybeNull(RuntimeTypeHandle typeHandle) + { + return typeHandle; + } + + public static ref byte GetRefAny(RuntimeTypeHandle type, TypedReference typedRef) + { + if (!TypedReference.RawTargetTypeToken(typedRef).Equals(type)) + { + throw new InvalidCastException(); + } + + return ref typedRef.Value; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FixupRuntimeTypeHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FixupRuntimeTypeHandle.cs new file mode 100644 index 00000000000000..53ce39b83a4ca0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FixupRuntimeTypeHandle.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime; + +namespace Internal.Runtime.CompilerServices +{ + public unsafe struct FixupRuntimeTypeHandle + { + private IntPtr _value; + + public FixupRuntimeTypeHandle(RuntimeTypeHandle runtimeTypeHandle) + { + _value = *(IntPtr*)&runtimeTypeHandle; + } + + public RuntimeTypeHandle RuntimeTypeHandle + { + get + { + // Managed debugger uses this logic to figure out the interface's type + // Update managed debugger too whenever this is changed. + // See CordbObjectValue::WalkPtrAndTypeData in debug\dbi\values.cpp + + if (((_value.ToInt64()) & IndirectionConstants.IndirectionCellPointer) != 0) + { + return *(RuntimeTypeHandle*)(_value.ToInt64() - IndirectionConstants.IndirectionCellPointer); + } + else + { + RuntimeTypeHandle returnValue = default(RuntimeTypeHandle); + *(IntPtr*)&returnValue = _value; + return returnValue; + } + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs new file mode 100644 index 00000000000000..cb89e087340fc7 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs @@ -0,0 +1,155 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Internal.Runtime.Augments; + +namespace Internal.Runtime.CompilerServices +{ + [System.Runtime.CompilerServices.ReflectionBlocked] + public static class FunctionPointerOps + { +#if TARGET_WASM + private const int FatFunctionPointerOffset = 1 << 31; +#else + private const int FatFunctionPointerOffset = 2; +#endif + + private struct GenericMethodDescriptorInfo : IEquatable + { + public override bool Equals(object? obj) + { + if (!(obj is GenericMethodDescriptorInfo)) + return false; + + return Equals((GenericMethodDescriptorInfo)obj); + } + + public bool Equals(GenericMethodDescriptorInfo other) + { + if (MethodFunctionPointer != other.MethodFunctionPointer) + return false; + + if (InstantiationArgument != other.InstantiationArgument) + return false; + + return true; + } + + public override int GetHashCode() + { + int a = InstantiationArgument.GetHashCode(); + int b = MethodFunctionPointer.GetHashCode(); + return (a ^ b) + (a << 11) - (b >> 13); + } + + public IntPtr MethodFunctionPointer; + public IntPtr InstantiationArgument; + } + + private static uint s_genericFunctionPointerNextIndex; + private const uint c_genericDictionaryChunkSize = 1024; + private static LowLevelList s_genericFunctionPointerCollection = new LowLevelList(); + private static LowLevelDictionary s_genericFunctionPointerDictionary = new LowLevelDictionary(); + + public static unsafe IntPtr GetGenericMethodFunctionPointer(IntPtr canonFunctionPointer, IntPtr instantiationArgument) + { + if (instantiationArgument == IntPtr.Zero) + return canonFunctionPointer; + + lock (s_genericFunctionPointerDictionary) + { + var key = new GenericMethodDescriptorInfo + { + MethodFunctionPointer = canonFunctionPointer, + InstantiationArgument = instantiationArgument + }; + + uint index = 0; + if (!s_genericFunctionPointerDictionary.TryGetValue(key, out index)) + { + // Capture new index value + index = s_genericFunctionPointerNextIndex; + + int newChunkIndex = (int)(index / c_genericDictionaryChunkSize); + uint newSubChunkIndex = index % c_genericDictionaryChunkSize; + + // Generate new chunk if existing chunks are insufficient + if (s_genericFunctionPointerCollection.Count <= newChunkIndex) + { + System.Diagnostics.Debug.Assert(newSubChunkIndex == 0); + + // New generic descriptors are allocated on the native heap and not tracked in the GC. + IntPtr pNewMem = Marshal.AllocHGlobal((int)(c_genericDictionaryChunkSize * sizeof(GenericMethodDescriptor))); + s_genericFunctionPointerCollection.Add(pNewMem); + } + + ((GenericMethodDescriptor*)s_genericFunctionPointerCollection[newChunkIndex])[newSubChunkIndex] = + new GenericMethodDescriptor(canonFunctionPointer, instantiationArgument); + + s_genericFunctionPointerDictionary.LookupOrAdd(key, index); + + // Now that we can no longer have failed, update the next index. + s_genericFunctionPointerNextIndex++; + } + + // Lookup within list + int chunkIndex = (int)(index / c_genericDictionaryChunkSize); + uint subChunkIndex = index % c_genericDictionaryChunkSize; + GenericMethodDescriptor* genericFunctionPointer = &((GenericMethodDescriptor*)s_genericFunctionPointerCollection[chunkIndex])[subChunkIndex]; + + System.Diagnostics.Debug.Assert(canonFunctionPointer == genericFunctionPointer->MethodFunctionPointer); + System.Diagnostics.Debug.Assert(instantiationArgument == genericFunctionPointer->InstantiationArgument); + + return (IntPtr)((byte*)genericFunctionPointer + FatFunctionPointerOffset); + } + } + + public static unsafe bool IsGenericMethodPointer(IntPtr functionPointer) + { + // Check the low bit to find out what kind of function pointer we have here. +#if TARGET_64BIT + if ((functionPointer.ToInt64() & FatFunctionPointerOffset) == FatFunctionPointerOffset) +#else + if ((functionPointer.ToInt32() & FatFunctionPointerOffset) == FatFunctionPointerOffset) +#endif + { + return true; + } + return false; + } + + [CLSCompliant(false)] + public static unsafe GenericMethodDescriptor* ConvertToGenericDescriptor(IntPtr functionPointer) + { + return (GenericMethodDescriptor*)((byte*)functionPointer - FatFunctionPointerOffset); + } + + public static unsafe bool Compare(IntPtr functionPointerA, IntPtr functionPointerB) + { + if (!IsGenericMethodPointer(functionPointerA)) + { + IntPtr codeTargetA = RuntimeAugments.GetCodeTarget(functionPointerA); + IntPtr codeTargetB = RuntimeAugments.GetCodeTarget(functionPointerB); + return codeTargetA == codeTargetB; + } + else + { + if (!IsGenericMethodPointer(functionPointerB)) + return false; + + GenericMethodDescriptor* pointerDefA = ConvertToGenericDescriptor(functionPointerA); + GenericMethodDescriptor* pointerDefB = ConvertToGenericDescriptor(functionPointerB); + + if (pointerDefA->InstantiationArgument != pointerDefB->InstantiationArgument) + return false; + + IntPtr codeTargetA = RuntimeAugments.GetCodeTarget(pointerDefA->MethodFunctionPointer); + IntPtr codeTargetB = RuntimeAugments.GetCodeTarget(pointerDefB->MethodFunctionPointer); + return codeTargetA == codeTargetB; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/GenericMethodDescriptor.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/GenericMethodDescriptor.cs new file mode 100644 index 00000000000000..f23d9aa8165f20 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/GenericMethodDescriptor.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Internal.Runtime.CompilerServices +{ + [System.Runtime.CompilerServices.ReflectionBlocked] + public struct GenericMethodDescriptor + { + public readonly IntPtr MethodFunctionPointer; + public readonly IntPtr InstantiationArgument; + + public GenericMethodDescriptor(IntPtr methodFunctionPointer, IntPtr instantiationArgument) + => (MethodFunctionPointer, InstantiationArgument) = (methodFunctionPointer, instantiationArgument); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/GenericVirtualMethodSupport.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/GenericVirtualMethodSupport.cs new file mode 100644 index 00000000000000..3ceaf03e3f0b40 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/GenericVirtualMethodSupport.cs @@ -0,0 +1,92 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Runtime.Augments; + +namespace Internal.Runtime.CompilerServices +{ + internal static class GenericVirtualMethodSupport + { + private static unsafe IntPtr GVMLookupForSlotWorker(RuntimeTypeHandle type, RuntimeTypeHandle declaringType, RuntimeTypeHandle[] genericArguments, MethodNameAndSignature methodNameAndSignature) + { + bool slotChanged = false; + + IntPtr resolution = IntPtr.Zero; + IntPtr functionPointer = IntPtr.Zero; + IntPtr genericDictionary = IntPtr.Zero; + + bool lookForDefaultImplementations = false; + + again: + // Walk parent hierarchy attempting to resolve + EETypePtr eeType = type.ToEETypePtr(); + + while (!eeType.IsNull) + { + RuntimeTypeHandle handle = new RuntimeTypeHandle(eeType); + string methodName = methodNameAndSignature.Name; + RuntimeSignature methodSignature = methodNameAndSignature.Signature; + if (RuntimeAugments.TypeLoaderCallbacks.TryGetGenericVirtualTargetForTypeAndSlot(handle, ref declaringType, genericArguments, ref methodName, ref methodSignature, lookForDefaultImplementations, out functionPointer, out genericDictionary, out slotChanged)) + { + methodNameAndSignature = new MethodNameAndSignature(methodName, methodSignature); + + if (!slotChanged) + resolution = FunctionPointerOps.GetGenericMethodFunctionPointer(functionPointer, genericDictionary); + break; + } + + eeType = eeType.BaseType; + } + + // If the current slot to examine has changed, restart the lookup. + // This happens when there is an interface call. + if (slotChanged) + { + return GVMLookupForSlotWorker(type, declaringType, genericArguments, methodNameAndSignature); + } + + if (resolution == IntPtr.Zero + && !lookForDefaultImplementations + && declaringType.ToEETypePtr().IsInterface) + { + lookForDefaultImplementations = true; + goto again; + } + + if (resolution == IntPtr.Zero) + { + var sb = new System.Text.StringBuilder(); + sb.AppendLine("Generic virtual method pointer lookup failure."); + sb.AppendLine(); + sb.AppendLine("Declaring type: " + declaringType.LastResortToString); + sb.AppendLine("Target type: " + type.LastResortToString); + sb.AppendLine("Method name: " + methodNameAndSignature.Name); + sb.AppendLine("Instantiation:"); + for (int i = 0; i < genericArguments.Length; i++) + { + sb.AppendLine(" Argument " + i.LowLevelToString() + ": " + genericArguments[i].LastResortToString); + } + + Environment.FailFast(sb.ToString()); + } + + return resolution; + } + + internal static unsafe IntPtr GVMLookupForSlot(RuntimeTypeHandle type, RuntimeMethodHandle slot) + { + RuntimeTypeHandle declaringTypeHandle; + MethodNameAndSignature nameAndSignature; + RuntimeTypeHandle[] genericMethodArgs; + if (!RuntimeAugments.TypeLoaderCallbacks.GetRuntimeMethodHandleComponents(slot, out declaringTypeHandle, out nameAndSignature, out genericMethodArgs)) + { + System.Diagnostics.Debug.Assert(false); + return IntPtr.Zero; + } + + return GVMLookupForSlotWorker(type, declaringTypeHandle, genericMethodArgs, nameAndSignature); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs new file mode 100644 index 00000000000000..7b458a8539d33b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs @@ -0,0 +1,255 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime; + +using Internal.Runtime.Augments; + +namespace Internal.Runtime.CompilerServices +{ + // This structure is used to resolve a instance method given an object instance. To use this type + // 1) New up an instance using one of the constructors below. + // 2) Use the ToIntPtr() method to get the interned instance of this type. This will permanently allocate + // a block of memory that can be used to represent a virtual method resolution. This memory is interned + // so that repeated allocation of the same resolver will not leak. + // 3) Use the ResolveMethod function to do the virtual lookup. This function takes advantage of + // a lockless cache so the resolution is very fast for repeated lookups. + [ReflectionBlocked] + public struct OpenMethodResolver : IEquatable + { + public const short DispatchResolve = 0; + public const short GVMResolve = 1; + public const short OpenNonVirtualResolve = 2; + public const short OpenNonVirtualResolveLookthruUnboxing = 3; + + private readonly short _resolveType; + private readonly GCHandle _readerGCHandle; + private readonly int _handle; + private readonly IntPtr _methodHandleOrSlotOrCodePointer; + private readonly IntPtr _nonVirtualOpenInvokeCodePointer; + private readonly EETypePtr _declaringType; + + public OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, int slot, GCHandle readerGCHandle, int handle) + { + _resolveType = DispatchResolve; + _declaringType = declaringTypeOfSlot.ToEETypePtr(); + _methodHandleOrSlotOrCodePointer = new IntPtr(slot); + _handle = handle; + _readerGCHandle = readerGCHandle; + _nonVirtualOpenInvokeCodePointer = IntPtr.Zero; + } + + public unsafe OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, RuntimeMethodHandle gvmSlot, GCHandle readerGCHandle, int handle) + { + _resolveType = GVMResolve; + _methodHandleOrSlotOrCodePointer = *(IntPtr*)&gvmSlot; + _declaringType = declaringTypeOfSlot.ToEETypePtr(); + _handle = handle; + _readerGCHandle = readerGCHandle; + _nonVirtualOpenInvokeCodePointer = IntPtr.Zero; + } + + public OpenMethodResolver(RuntimeTypeHandle declaringType, IntPtr codePointer, GCHandle readerGCHandle, int handle) + { + _resolveType = OpenNonVirtualResolve; + _nonVirtualOpenInvokeCodePointer = _methodHandleOrSlotOrCodePointer = codePointer; + _declaringType = declaringType.ToEETypePtr(); + _handle = handle; + _readerGCHandle = readerGCHandle; + } + + public OpenMethodResolver(RuntimeTypeHandle declaringType, IntPtr codePointer, GCHandle readerGCHandle, int handle, short resolveType) + { + _resolveType = resolveType; + _methodHandleOrSlotOrCodePointer = codePointer; + _declaringType = declaringType.ToEETypePtr(); + _handle = handle; + _readerGCHandle = readerGCHandle; + if (resolveType == OpenNonVirtualResolve) + _nonVirtualOpenInvokeCodePointer = codePointer; + else if (resolveType == OpenNonVirtualResolveLookthruUnboxing) + _nonVirtualOpenInvokeCodePointer = RuntimeAugments.TypeLoaderCallbacks.ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(codePointer, declaringType); + else + throw new NotSupportedException(); + } + + public short ResolverType + { + get + { + return _resolveType; + } + } + + public RuntimeTypeHandle DeclaringType + { + get + { + return new RuntimeTypeHandle(_declaringType); + } + } + + public unsafe RuntimeMethodHandle GVMMethodHandle + { + get + { + IntPtr localIntPtr = _methodHandleOrSlotOrCodePointer; + IntPtr* pMethodHandle = &localIntPtr; + return *(RuntimeMethodHandle*)pMethodHandle; + } + } + + public bool IsOpenNonVirtualResolve + { + get + { + switch (_resolveType) + { + case OpenNonVirtualResolve: + case OpenNonVirtualResolveLookthruUnboxing: + return true; + default: + return false; + } + } + } + + public IntPtr CodePointer + { + get + { + return _methodHandleOrSlotOrCodePointer; + } + } + + public object Reader + { + get + { + return _readerGCHandle.Target; + } + } + + public int Handle + { + get + { + return _handle; + } + } + + private unsafe IntPtr ResolveMethod(object thisObject) + { + if (_resolveType == DispatchResolve) + { + return RuntimeImports.RhResolveDispatch(thisObject, _declaringType, (ushort)_methodHandleOrSlotOrCodePointer); + } + else if (_resolveType == GVMResolve) + { + return TypeLoaderExports.GVMLookupForSlot(thisObject, GVMMethodHandle); + } + else + { + throw new NotSupportedException(); // Should never happen, in this case, the dispatch should be resolved in the other ResolveMethod function + } + } + + internal static unsafe IntPtr ResolveMethodWorker(IntPtr resolver, object thisObject) + { + return ((OpenMethodResolver*)resolver)->ResolveMethod(thisObject); + } + + public static unsafe IntPtr ResolveMethod(IntPtr resolver, object thisObject) + { + IntPtr nonVirtualOpenInvokeCodePointer = ((OpenMethodResolver*)resolver)->_nonVirtualOpenInvokeCodePointer; + if (nonVirtualOpenInvokeCodePointer != IntPtr.Zero) + return nonVirtualOpenInvokeCodePointer; + + return TypeLoaderExports.OpenInstanceMethodLookup(resolver, thisObject); + } + + public static unsafe IntPtr ResolveMethod(IntPtr resolverPtr, RuntimeTypeHandle thisType) + { + OpenMethodResolver* resolver = ((OpenMethodResolver*)resolverPtr); + IntPtr nonVirtualOpenInvokeCodePointer = resolver->_nonVirtualOpenInvokeCodePointer; + if (nonVirtualOpenInvokeCodePointer != IntPtr.Zero) + return nonVirtualOpenInvokeCodePointer; + + return RuntimeImports.RhResolveDispatchOnType(thisType.ToEETypePtr(), resolver->_declaringType, (ushort)resolver->_methodHandleOrSlotOrCodePointer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int _rotl(int value, int shift) + { + return (int)(((uint)value << shift) | ((uint)value >> (32 - shift))); + } + + private static int CalcHashCode(int hashCode1, int hashCode2, int hashCode3, int hashCode4) + { + int length = 4; + + int hash1 = 0x449b3ad6; + int hash2 = (length << 3) + 0x55399219; + + hash1 = (hash1 + _rotl(hash1, 5)) ^ hashCode1; + hash2 = (hash2 + _rotl(hash2, 5)) ^ hashCode2; + hash1 = (hash1 + _rotl(hash1, 5)) ^ hashCode3; + hash2 = (hash2 + _rotl(hash2, 5)) ^ hashCode4; + + hash1 += _rotl(hash1, 8); + hash2 += _rotl(hash2, 8); + + return hash1 ^ hash2; + } + + public override int GetHashCode() + { + return CalcHashCode(_resolveType, _handle, _methodHandleOrSlotOrCodePointer.GetHashCode(), _declaringType.IsNull ? 0 : _declaringType.GetHashCode()); + } + + public bool Equals(OpenMethodResolver other) + { + if (other._resolveType != _resolveType) + return false; + + if (other._handle != _handle) + return false; + + if (other._methodHandleOrSlotOrCodePointer != _methodHandleOrSlotOrCodePointer) + return false; + + return other._declaringType.Equals(_declaringType); + } + + public override bool Equals(object? obj) + { + if (!(obj is OpenMethodResolver)) + { + return false; + } + + return ((OpenMethodResolver)obj).Equals(this); + } + + private static LowLevelDictionary s_internedResolverHash = new LowLevelDictionary(); + + public unsafe IntPtr ToIntPtr() + { + lock (s_internedResolverHash) + { + IntPtr returnValue; + if (s_internedResolverHash.TryGetValue(this, out returnValue)) + return returnValue; + returnValue = Marshal.AllocHGlobal(sizeof(OpenMethodResolver)); + *((OpenMethodResolver*)returnValue) = this; + s_internedResolverHash.Add(this, returnValue); + return returnValue; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RuntimeFieldHandleInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RuntimeFieldHandleInfo.cs new file mode 100644 index 00000000000000..53f4d7e9ac2585 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RuntimeFieldHandleInfo.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +namespace Internal.Runtime.CompilerServices +{ + [StructLayout(LayoutKind.Sequential)] + [CLSCompliant(false)] + public unsafe struct RuntimeFieldHandleInfo + { + public IntPtr NativeLayoutInfoSignature; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RuntimeMethodHandleInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RuntimeMethodHandleInfo.cs new file mode 100644 index 00000000000000..7cb1f976ff61e8 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RuntimeMethodHandleInfo.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using System.Runtime.InteropServices; +using System; +using Internal.Runtime.Augments; +using System.Diagnostics; + +namespace Internal.Runtime.CompilerServices +{ + public class MethodNameAndSignature + { + public string Name { get; private set; } + public RuntimeSignature Signature { get; private set; } + + public MethodNameAndSignature(string name, RuntimeSignature signature) + { + Name = name; + Signature = signature; + } + + public override bool Equals(object? compare) + { + if (compare == null) + return false; + + MethodNameAndSignature? other = compare as MethodNameAndSignature; + if (other == null) + return false; + + if (Name != other.Name) + return false; + + return Signature.Equals(other.Signature); + } + + public override int GetHashCode() + { + int hash = Name.GetHashCode(); + + return hash; + } + } + + [StructLayout(LayoutKind.Sequential)] + [CLSCompliant(false)] + public unsafe struct RuntimeMethodHandleInfo + { + public IntPtr NativeLayoutInfoSignature; + + public static unsafe RuntimeMethodHandle InfoToHandle(RuntimeMethodHandleInfo* info) + { + RuntimeMethodHandle returnValue = default(RuntimeMethodHandle); + *(RuntimeMethodHandleInfo**)&returnValue = info; + return returnValue; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RuntimeSignature.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RuntimeSignature.cs new file mode 100644 index 00000000000000..7e372818fd3dea --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RuntimeSignature.cs @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Diagnostics; +using Internal.Runtime.Augments; + +namespace Internal.Runtime.CompilerServices +{ + public struct RuntimeSignature + { + private IntPtr _moduleHandle; + private int _tokenOrOffset; + private bool _isNativeLayoutSignature; + + [CLSCompliant(false)] + public static RuntimeSignature CreateFromNativeLayoutSignature(TypeManagerHandle moduleHandle, uint nativeLayoutOffset) + { + return new RuntimeSignature + { + _moduleHandle = moduleHandle.GetIntPtrUNSAFE(), + _tokenOrOffset = (int)nativeLayoutOffset, + _isNativeLayoutSignature = true, + }; + } + + [CLSCompliant(false)] + public static RuntimeSignature CreateFromNativeLayoutSignature(RuntimeSignature oldSignature, uint newNativeLayoutOffset) + { + return new RuntimeSignature + { + _moduleHandle = oldSignature._moduleHandle, + _tokenOrOffset = (int)newNativeLayoutOffset, + _isNativeLayoutSignature = true, + }; + } + + public static RuntimeSignature CreateFromMethodHandle(TypeManagerHandle moduleHandle, int token) + { + return new RuntimeSignature + { + _moduleHandle = moduleHandle.GetIntPtrUNSAFE(), + _tokenOrOffset = token, + _isNativeLayoutSignature = false, + }; + } + + public static RuntimeSignature CreateFromMethodHandle(IntPtr moduleHandle, int token) + { + return new RuntimeSignature + { + _moduleHandle = moduleHandle, + _tokenOrOffset = token, + _isNativeLayoutSignature = false, + }; + } + + [CLSCompliant(false)] + public static RuntimeSignature CreateFromNativeLayoutSignatureForDebugger(uint nativeLayoutOffset) + { + // This is a RuntimeSignature object used by the debugger only, + // the fact that the _moduleHandle is NULL signify that information. + return new RuntimeSignature + { + _moduleHandle = IntPtr.Zero, + _tokenOrOffset = (int)nativeLayoutOffset, + _isNativeLayoutSignature = true, + }; + } + + public bool IsNativeLayoutSignature + { + get + { + return _isNativeLayoutSignature; + } + } + + public int Token + { + get + { + if (_isNativeLayoutSignature) + { + Debug.Assert(false); + return -1; + } + return _tokenOrOffset; + } + } + + [CLSCompliant(false)] + public uint NativeLayoutOffset + { + get + { + if (!_isNativeLayoutSignature) + { + Debug.Assert(false); + return unchecked((uint)-1); + } + return (uint)_tokenOrOffset; + } + } + + public IntPtr ModuleHandle + { + get + { + return _moduleHandle; + } + } + + public bool Equals(RuntimeSignature other) + { + if (IsNativeLayoutSignature && other.IsNativeLayoutSignature) + { + if ((ModuleHandle == other.ModuleHandle) && (NativeLayoutOffset == other.NativeLayoutOffset)) + return true; + } + else if (!IsNativeLayoutSignature && !other.IsNativeLayoutSignature) + { + if ((ModuleHandle == other.ModuleHandle) && (Token == other.Token)) + return true; + } + + // Walk both signatures to check for equality the slow way + return RuntimeAugments.TypeLoaderCallbacks.CompareMethodSignatures(this, other); + } + + /// + /// Fast equality check + /// + public bool StructuralEquals(RuntimeSignature other) + { + if (_moduleHandle != other._moduleHandle) + return false; + + if (_tokenOrOffset != other._tokenOrOffset) + return false; + + if (_isNativeLayoutSignature != other._isNativeLayoutSignature) + return false; + + return true; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/Unsafe.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/Unsafe.cs new file mode 100644 index 00000000000000..ce8ff17ba6043c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/Unsafe.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +namespace Internal.Runtime.CompilerServices +{ + [ReflectionBlocked] + public static partial class Unsafe + { + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs new file mode 100644 index 00000000000000..d149dba7a3220b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Runtime.InteropServices; + +namespace Internal.Runtime +{ + static unsafe class IDynamicCastableSupport + { + [RuntimeExport("IDynamicCastableIsInterfaceImplemented")] + internal static bool IDynamicCastableIsInterfaceImplemented(IDynamicInterfaceCastable instance, MethodTable* interfaceType, bool throwIfNotImplemented) + { + return instance.IsInterfaceImplemented(new RuntimeTypeHandle(new EETypePtr(interfaceType)), throwIfNotImplemented); + } + + [RuntimeExport("IDynamicCastableGetInterfaceImplementation")] + internal static IntPtr IDynamicCastableGetInterfaceImplementation(IDynamicInterfaceCastable instance, MethodTable* interfaceType, ushort slot) + { + RuntimeTypeHandle handle = instance.GetInterfaceImplementation(new RuntimeTypeHandle(new EETypePtr(interfaceType))); + EETypePtr implType = handle.ToEETypePtr(); + if (implType.IsNull) + { + ThrowInvalidCastException(instance, interfaceType); + } + if (!implType.IsInterface) + { + ThrowInvalidOperationException(implType); + } + IntPtr result = RuntimeImports.RhResolveDispatchOnType(implType, new EETypePtr(interfaceType), slot); + if (result == IntPtr.Zero) + { + IDynamicCastableGetInterfaceImplementationFailure(instance, interfaceType, implType); + } + return result; + } + + private static void ThrowInvalidCastException(object instance, MethodTable* interfaceType) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, instance.GetType(), Type.GetTypeFromEETypePtr(new EETypePtr(interfaceType)))); + } + + private static void ThrowInvalidOperationException(EETypePtr resolvedImplType) + { + throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_NotInterface, Type.GetTypeFromEETypePtr(resolvedImplType))); + } + + private static void IDynamicCastableGetInterfaceImplementationFailure(object instance, MethodTable* interfaceType, EETypePtr resolvedImplType) + { + if (resolvedImplType.DispatchMap == IntPtr.Zero) + throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_MissingImplementationAttribute, Type.GetTypeFromEETypePtr(resolvedImplType), nameof(DynamicInterfaceCastableImplementationAttribute))); + + bool implementsInterface = false; + var interfaces = resolvedImplType.Interfaces; + for (int i = 0; i < interfaces.Count; i++) + { + if (interfaces[i] == new EETypePtr(interfaceType)) + { + implementsInterface = true; + break; + } + } + + if (!implementsInterface) + throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_DoesNotImplementRequested, Type.GetTypeFromEETypePtr(resolvedImplType), Type.GetTypeFromEETypePtr(new EETypePtr(interfaceType)))); + + throw new EntryPointNotFoundException(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/MethodTable.Runtime.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/MethodTable.Runtime.cs new file mode 100644 index 00000000000000..474f550bbb63df --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/MethodTable.Runtime.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.InteropServices; + +namespace Internal.Runtime +{ + // Extensions to MethodTable that are specific to the use in the CoreLib. + internal unsafe partial struct MethodTable + { +#if !INPLACE_RUNTIME + internal MethodTable* GetArrayEEType() + { + + return EETypePtr.EETypePtrOf().ToPointer(); + } +#endif + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs new file mode 100644 index 00000000000000..fded4506f35214 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Runtime.CompilerServices; + +using Internal.Runtime.CompilerHelpers; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime +{ + /// + /// This class is used by ReadyToRun helpers to get access to thread static fields of a type + /// and to allocate required TLS memory. + /// + internal static class ThreadStatics + { + /// + /// This method is called from a ReadyToRun helper to get base address of thread + /// static storage for the given type. + /// + internal static unsafe object GetThreadStaticBaseForType(TypeManagerSlot* pModuleData, int typeTlsIndex) + { + // Get the array that holds thread static memory blocks for each type in the given module + object[] storage = RuntimeImports.RhGetThreadStaticStorageForModule(pModuleData->ModuleIndex); + + // Check whether thread static storage has already been allocated for this module and type. + if ((storage != null) && ((uint)typeTlsIndex < (uint)storage.Length) && (storage[typeTlsIndex] != null)) + { + return storage[typeTlsIndex]; + } + + return GetThreadStaticBaseForTypeSlow(pModuleData, typeTlsIndex); + } + + [RuntimeExport("RhpGetThreadStaticBaseForTypeSlow")] + [MethodImpl(MethodImplOptions.NoInlining)] + internal static unsafe object GetThreadStaticBaseForTypeSlow(TypeManagerSlot* pModuleData, int typeTlsIndex) + { + // Get the array that holds thread static memory blocks for each type in the given module + object[] storage = RuntimeImports.RhGetThreadStaticStorageForModule(pModuleData->ModuleIndex); + + // This the first access to the thread statics of the type corresponding to typeTlsIndex. + // Make sure there is enough storage allocated to hold it. + storage = EnsureThreadStaticStorage(pModuleData->ModuleIndex, storage, requiredSize: typeTlsIndex + 1); + + // Allocate an object that will represent a memory block for all thread static fields of the type + object threadStaticBase = AllocateThreadStaticStorageForType(pModuleData->TypeManager, typeTlsIndex); + + Debug.Assert(storage[typeTlsIndex] == null); + + storage[typeTlsIndex] = threadStaticBase; + + return threadStaticBase; + } + + /// + /// if it is required, this method extends thread static storage of the given module + /// to the specified size and then registers the memory with the runtime. + /// + private static object[] EnsureThreadStaticStorage(int moduleIndex, object[] existingStorage, int requiredSize) + { + if ((existingStorage != null) && (requiredSize < existingStorage.Length)) + { + return existingStorage; + } + + object[] newStorage = new object[requiredSize]; + if (existingStorage != null) + { + Array.Copy(existingStorage, newStorage, existingStorage.Length); + } + + // Install the newly created array as thread static storage for the given module + // on the current thread. This call can fail due to a failure to allocate/extend required + // internal thread specific resources. + if (!RuntimeImports.RhSetThreadStaticStorageForModule(newStorage, moduleIndex)) + { + throw new OutOfMemoryException(); + } + + return newStorage; + } + + /// + /// This method allocates an object that represents a memory block for all thread static fields of the type + /// that corresponds to the specified TLS index. + /// + private static unsafe object AllocateThreadStaticStorageForType(TypeManagerHandle typeManager, int typeTlsIndex) + { + int length; + IntPtr* threadStaticRegion; + + // Get a pointer to the beginning of the module's Thread Static section. Then get a pointer + // to the MethodTable that represents a memory map for thread statics storage. + threadStaticRegion = (IntPtr*)RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.ThreadStaticRegion, out length); + return RuntimeImports.RhNewObject(new EETypePtr(threadStaticRegion[typeTlsIndex])); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/TypeLoaderExceptionHelper.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/TypeLoaderExceptionHelper.cs new file mode 100644 index 00000000000000..38ac79b6f30ed3 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/TypeLoaderExceptionHelper.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime +{ + /// + /// Helper class to create exceptions. This is expected to be used from two places: + /// from the throw helpers generated by the compiler after encountering a method that fails to + /// compile, and from the runtime type loader (living in a universe with a separate + /// System.Object) to create classlib exceptions. + /// + internal static class TypeLoaderExceptionHelper + { + public static Exception CreateBadImageFormatException(ExceptionStringID id) + { + return new BadImageFormatException(GetFormatString(id)); + } + + public static Exception CreateTypeLoadException(ExceptionStringID id, string typeName, string moduleName) + { + return new TypeLoadException(SR.Format(GetFormatString(id), typeName, moduleName), typeName); + } + + public static Exception CreateTypeLoadException(ExceptionStringID id, string typeName, string moduleName, string messageArg) + { + return new TypeLoadException(SR.Format(GetFormatString(id), typeName, moduleName, messageArg), typeName); + } + + public static Exception CreateMissingFieldException(ExceptionStringID id, string fieldName) + { + return new MissingFieldException(SR.Format(GetFormatString(id), fieldName)); + } + + public static Exception CreateMissingMethodException(ExceptionStringID id, string methodName) + { + throw new MissingMethodException(SR.Format(GetFormatString(id), methodName)); + } + + public static Exception CreateFileNotFoundException(ExceptionStringID id, string fileName) + { + throw new System.IO.FileNotFoundException(SR.Format(GetFormatString(id), fileName), fileName); + } + + public static Exception CreateInvalidProgramException(ExceptionStringID id) + { + throw new InvalidProgramException(GetFormatString(id)); + } + + public static Exception CreateInvalidProgramException(ExceptionStringID id, string methodName) + { + throw new InvalidProgramException(SR.Format(GetFormatString(id), methodName)); + } + + public static Exception CreateMarshalDirectiveException(ExceptionStringID id) + { + throw new MarshalDirectiveException(GetFormatString(id)); + } + + // TODO: move to a place where we can share this with the compiler + private static string GetFormatString(ExceptionStringID id) + { + switch (id) + { + case ExceptionStringID.ClassLoadGeneral: + return SR.ClassLoad_General; + case ExceptionStringID.ClassLoadExplicitGeneric: + return SR.ClassLoad_ExplicitGeneric; + case ExceptionStringID.ClassLoadBadFormat: + return SR.ClassLoad_BadFormat; + case ExceptionStringID.ClassLoadValueClassTooLarge: + return SR.ClassLoad_ValueClassTooLarge; + case ExceptionStringID.ClassLoadExplicitLayout: + return SR.ClassLoad_ExplicitLayout; + case ExceptionStringID.ClassLoadRankTooLarge: + return SR.ClassLoad_RankTooLarge; + case ExceptionStringID.InvalidProgramDefault: + return SR.InvalidProgram_Default; + case ExceptionStringID.InvalidProgramSpecific: + return SR.InvalidProgram_Specific; + case ExceptionStringID.InvalidProgramVararg: + return SR.InvalidProgram_Vararg; + case ExceptionStringID.InvalidProgramCallVirtFinalize: + return SR.InvalidProgram_CallVirtFinalize; + case ExceptionStringID.InvalidProgramUnmanagedCallersOnly: + return SR.InvalidProgram_UnmanagedCallersOnly; + case ExceptionStringID.MissingField: + return SR.EE_MissingField; + case ExceptionStringID.MissingMethod: + return SR.EE_MissingMethod; + case ExceptionStringID.FileLoadErrorGeneric: + return SR.IO_FileNotFound_FileName; + case ExceptionStringID.BadImageFormatGeneric: + return SR.Arg_BadImageFormatException; + case ExceptionStringID.MarshalDirectiveGeneric: + return SR.Arg_MarshalDirectiveException; + default: + Debug.Assert(false); + return ""; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Interop/Windows/Interop.Libraries.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Interop/Windows/Interop.Libraries.cs new file mode 100644 index 00000000000000..90fc9029c15a0f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Interop/Windows/Interop.Libraries.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + internal static partial class Libraries + { + internal const string ThreadPool = "api-ms-win-core-threadpool-l1-2-0.dll"; + internal const string ProcessEnvironment = "api-ms-win-core-processenvironment-l1-1-0.dll"; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs new file mode 100644 index 00000000000000..d6bf95212d4e4a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.Win32.SafeHandles +{ + internal class SafeThreadPoolIOHandle : SafeHandle + { + public SafeThreadPoolIOHandle() + : base(IntPtr.Zero, true) + { + } + + public override bool IsInvalid + { + get { return handle == IntPtr.Zero; } + } + + protected override bool ReleaseHandle() + { + Interop.Kernel32.CloseThreadpoolIo(handle); + return true; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Resources/Strings.resx b/src/coreclr/nativeaot/System.Private.CoreLib/src/Resources/Strings.resx new file mode 100644 index 00000000000000..85a0a88f88e5f3 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Resources/Strings.resx @@ -0,0 +1,3253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name: + + + There are no context policies. + + + Default principal object cannot be set twice. + + + Ambiguous implementation found. + + + Cannot access member. + + + Attempted to read or write protected memory. This is often an indication that other memory is corrupt. + + + Error in the application. + + + Value does not fall within the expected range. + + + Specified argument was out of the range of valid values. + + + Overflow or underflow in the arithmetic operation. + + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Attempted to access an element as a type incompatible with the array. + + + Array must not be of length zero. + + + Format of the executable (.exe) or library (.dll) is invalid. + + + Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: '{0}'. + + + TimeSpan does not accept floating point Not-a-Number values. + + + String cannot contain a minus sign if the base is not 10. + + + The usage of IKeyComparer and IHashCodeProvider/IComparer interfaces cannot be mixed; use one or the other. + + + Attempt to unload the AppDomain failed. + + + Arrays must contain only blittable data in order to be copied to unmanaged memory. + + + Requested range extends past the end of the array. + + + Error HRESULT E_FAIL has been returned from a call to a COM component. + + + Error occurred during a cryptographic operation. + + + A datatype misalignment was detected in a load or store instruction. + + + Combination of arguments to the DateTime constructor is out of the legal range. + + + Attempted to access a path that is not on the disk. + + + Decimal byte array constructor requires an array of length four containing valid decimal bytes. + + + Attempted to load a type that was not created during ahead of time compilation. + + + Attempted to divide by zero. + + + Delegate to an instance method cannot have null 'this'. + + + Delegates must be of the same type. + + + Duplicate objects in argument. + + + This ExceptionHandlingClause is not a filter. + + + Object must be the same type as the enum. The type passed in was '{0}'; the enum type was '{1}'. + + + Entry point was not found. + + + Unable to find an entry point named '{0}' in DLL '{1}'. + + + Unable to find an entry point named '{0}' in DLL. + + + Illegal enum value: {0}. + + + Internal error in the runtime. + + + External component has thrown an exception. + + + Attempted to access a field that is not accessible by the caller. + + + One of the identified items was in an invalid format. + + + Byte array for GUID must be exactly {0} bytes long. + + + The number style AllowHexSpecifier is not supported on floating point data types. + + + Hashtable's capacity overflowed and went negative. Check load factor, capacity and the current size of the table. + + + Index was outside the bounds of the array. + + + Insufficient stack to continue executing the program safely. This can happen from having too many functions on the call stack or function on the stack using too much stack space. + + + Invalid Base. + + + Specified cast is not valid. + + + With the AllowHexSpecifier bit set in the enum bit field, the only other valid bits that can be combined into the enum value must be a subset of those in HexNumber. + + + Operation is not valid due to the current state of the object. + + + Not a legal OleAut date. + + + OleAut date did not convert to a DateTime correctly. + + + Invalid RuntimeTypeHandle. + + + I/O error occurred. + + + The given key was not present in the dictionary. + + + The given key '{0}' was not present in the dictionary. + + + Source string was not long enough. Check sourceIndex and count. + + + The arrays' lower bounds must be identical. + + + Attempted to access a non-existing field. + + + Attempt to access the method failed. + + + Attempted to access a missing member. + + + Attempted to access a missing method. + + + Attempted to add multiple callbacks to a delegate that does not support multicast. + + + Object must be of type Boolean. + + + Object must be of type Byte. + + + Object must be of type Char. + + + Object must be of type DateOnly. + {Locked="DateOnly"} + + + Object must be of type TimeOnly. + {Locked="TimeOnly"} + + + Object must be of type DateTime. + + + Object must be of type DateTimeOffset. + + + Object must be of type Decimal. + + + Object must be of type Double. + + + Drive name must be a root directory (i.e. 'C:\') or a drive letter ('C'). + + + Type provided must be an Enum. + + + Object must be of type GUID. + + + Object must be of type Int16. + + + Object must be of type Int32. + + + Object must be of type Int64. + + + Object must be of type IntPtr. + + + Object must be an array of primitives. + + + Object must be of type RuntimeAssembly. + + + Object must be of type SByte. + + + Object must be of type Single. + + + Method must be a static method. + + + Object must be of type String. + + + The pointer passed in as a String must not be in the bottom 64K of the process's address space. + + + Object must be of type TimeSpan. + + + Argument must be true. + + + Object must be of type UInt16. + + + Object must be of type UInt32. + + + Object must be of type UInt64. + + + Object must be of type UIntPtr. + + + Object must be of type Version. + + + Must provide at least one rank. + + + Array was not a one-dimensional array. + + + Array was not a two-dimensional array. + + + Array was not a three-dimensional array. + + + Argument count must not be negative. + + + Arg_NotFiniteNumberException = Number encountered was not a finite quantity. + + + The lower bound of target array must be zero. + + + Method may only be called on a Type for which Type.IsGenericParameter is true. + + + The method or operation is not implemented. + + + Specified method is not supported. + + + Arrays with non-zero lower bounds are not supported. + + + Object reference not set to an instance of an object. + + + Object of type '{0}' cannot be converted to type '{1}'. + + + Arithmetic operation resulted in an overflow. + + + Insufficient memory to continue the execution of the program. + + + Operation is not supported on this platform. + + + Parameter name: {0} + + + The path is empty. + + + The UNC path '{0}' should be of the form \\\\server\\share. + + + Attempted to operate on an array with the incorrect number of dimensions. + + + Indices length does not match the array rank. + + + Only single dimensional arrays are supported for the requested action. + + + The specified arrays must have the same number of dimensions. + + + Number of lengths and lowerBounds must match. + + + RegistryKey.GetValue does not allow a String that has a length greater than Int32.MaxValue. + + + The specified registry key does not exist. + + + Specified array was not of the expected rank. + + + Specified array was not of the expected type. + + + Security error. + + + Operation caused a stack overflow. + + + Object synchronization method was called from an unsynchronized block of code. + + + System error. + + + Exception has been thrown by the target of an invocation. + + + Number of parameters specified does not match the expected number. + + + Missing parameter does not have a default value. + + + Thread failed to start. + + + Thread was in an invalid state for the operation being executed. + + + The operation has timed out. + + + Attempt to access the type failed. + + + Failure has occurred while loading a type. + + + Attempted to perform an unauthorized operation. + + + Version string portion was too short or too long. + + + The value '{0}' is not of type '{1}' and cannot be used in this generic collection. + + + Absolute path information is required. + + + An item with the same key has already been added. + + + Item has already been added. Key in dictionary: '{0}' Key being added: '{1}' + + + An item with the same key has already been added. Key: {0} + + + The AdjustmentRule array cannot contain null elements. + + + The elements of the AdjustmentRule array must be in chronological order and must not overlap. + + + The alignment must be a power of two. + + + Attribute names must be unique. + + + Format specifier was invalid. + + + {0} is not a supported code page. + + + CompareOption.Ordinal cannot be used with other options. + + + The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, AssumeUniversal or AdjustToUniversal. + + + The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together. + + + Conversion buffer overflow. + + + The conversion could not be completed because the supplied DateTime did not have the Kind property set correctly. For example, when the Kind property is DateTimeKind.Local, the source time zone must be TimeZoneInfo.Local. + + + {0} is an invalid culture identifier. + + + Culture IETF Name {0} is not a recognized IETF name. + + + Culture ID {0} (0x{0:X4}) is a neutral culture; a region cannot be created from it. + + + Culture is not supported. + + + Only the invariant culture is supported in globalization-invariant mode. See https://aka.ms/GlobalizationInvariantMode for more information. + + + Customized cultures cannot be passed by LCID, only by name. + + + The binary data must result in a DateTime with ticks between DateTime.MinValue.Ticks and DateTime.MaxValue.Ticks. + + + The supplied DateTime must have the Year, Month, and Day properties set to 1. The time cannot be specified more precisely than whole milliseconds. + + + The supplied DateTime includes a TimeOfDay setting. This is not supported. + + + The supplied DateTime represents an invalid time. For example, when the clock is adjusted forward, any time in the period that is skipped is invalid. + + + The supplied DateTime is not in an ambiguous time range. + + + The supplied DateTime must have the Kind property set to DateTimeKind.Unspecified. + + + The supplied DateTime must have the Kind property set to DateTimeKind.Unspecified or DateTimeKind.Utc. + + + The DateTimeStyles value 'NoCurrentDateDefault' is not allowed when parsing DateTimeOffset. + + + The supplied DateTimeOffset is not in an ambiguous time range. + + + Decimal separator cannot be the empty string. + + + Empty file name is not legal. + + + Empty name is not legal. + + + Waithandle array may not be empty. + + + Must complete Convert() operation or call Encoder.Reset() before calling GetBytes() or GetByteCount(). Encoder '{0}' fallback '{1}'. + + + The output byte buffer is too small to contain the encoded data, encoding '{0}' fallback '{1}'. + + + The output char buffer is too small to contain the decoded characters, encoding '{0}' fallback '{1}'. + + + '{0}' is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method. + + + The argument type, '{0}', is not the same as the enum type '{1}'. + + + Cannot change fallback when buffer is not empty. Previous Convert() call left data in the fallback buffer. + + + At least one object must implement IComparable. + + + Type of argument is not compatible with the generic comparer. + + + Length of the array must be {0}. + + + Target array type is not compatible with the type of items in the collection. + + + Not a valid calendar for the given culture. + + + Invalid Unicode code point found at index {0}. + + + String contains invalid Unicode code points. + + + Unable to translate bytes {0} at index {1} from specified code page to Unicode. + + + Unable to translate Unicode character \\u{0:X4} at index {1} to specified code page. + + + Culture name '{0}' is not supported. + + + Culture name '{0}' is not a predefined culture. + + + Invalid DateTimeKind value. + + + An undefined DateTimeStyles value is being used. + + + The only allowed values for the styles are AllowWhiteSpaces, AllowTrailingWhite, AllowLeadingWhite, and AllowInnerWhite. + {Locked="AllowWhiteSpaces, AllowTrailingWhite, AllowLeadingWhite, and AllowInnerWhite"} + + + The DigitSubstitution property must be of a valid member of the DigitShapes enumeration. Valid entries include Context, NativeNational or None. + + + Invalid element name '{0}'. + + + Invalid element tag '{0}'. + + + Invalid element text '{0}'. + + + Invalid element value '{0}'. + + + The value '{0}' is not valid for this usage of the type {1}. + + + Value of flags is invalid. + + + Every element in the value array should be between one and nine, except for the last element, which can be zero. + + + Found a high surrogate char without a following low surrogate at index: {0}. The input may not be in this encoding, or may not contain valid Unicode (UTF-16) characters. + + + The specified ID parameter '{0}' is not supported. + + + Found a low surrogate char without a preceding high surrogate at index: {0}. The input may not be in this encoding, or may not contain valid Unicode (UTF-16) characters. + + + The NativeDigits array must contain exactly ten members. + + + Each member of the NativeDigits array must be a single text element (one or more UTF16 code points) with a Unicode Nd (Number, Decimal Digit) property indicating it is a digit. + + + The region name {0} should not correspond to neutral culture; a specific culture name is required. + + + Invalid normalization form. + + + An undefined NumberStyles value is being used. + + + Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection. + + + Illegal characters in path. + + + The REG_TZI_FORMAT structure is corrupt. + + + The given culture name '{0}' cannot be used to locate a resource file. Resource filenames must consist of only letters, numbers, hyphens or underscores. + + + The specified serialized string '{0}' is not supported. + + + An undefined TimeSpanStyles value is being used. + + + Argument must be initialized to false + + + Assembly must be a runtime Assembly object. + + + Type must be a runtime Type object. + + + The specified structure '{0}' must be blittable or have layout information. + + + The specified object must not be an instance of a generic type. + + + The specified Type must not be a generic type definition. + + + No Era was supplied. + + + There is no region associated with the Invariant Culture (Culture ID: 0x7F). + + + Object contains non-primitive or non-blittable data. + + + Path cannot be the empty string or all whitespace. + + + Array size exceeds addressing limitations. + + + The UTC Offset of the local dateTime parameter does not match the offset argument. + + + Offset must be specified in whole minutes. + + + Offset must be within plus or minus 14 hours. + + + The UTC Offset for Utc DateTime instances must be 0. + + + Culture name {0} or {1} is not supported. + + + Only mscorlib's assembly is valid. + + + The DateStart property must come before the DateEnd property. + + + Number was less than the array's lower bound in the first dimension. + + + Arrays larger than 2GB are not supported. + + + Index was out of range. Must be non-negative and less than the size of the collection. + + + The specified length exceeds maximum capacity of SecureString. + + + The specified length exceeds the maximum value of {0}. + + + Argument must be less than or equal to 2^31 - 1 milliseconds. + + + Non-negative number required. + + + The ID parameter must be in the range {0} through {1}. + + + The name of the type is invalid. + + + The format of the path '{0}' is not supported. + + + Recursive fallback not allowed for character \\u{0:X4}. + + + Recursive fallback not allowed for bytes {0}. + + + The result is out of the supported range for this calendar. The result should be between {0} (Gregorian date) and {1} (Gregorian date), inclusive. + + + The initial count for the semaphore must be greater than or equal to zero and less than the maximum count. + + + The structure must not be a value class. + + + The TimeSpan parameter cannot be specified more precisely than whole minutes. + + + The time zone ID '{0}' was not found on the local computer. + + + The tzfile does not begin with the magic characters 'TZif'. Please verify that the file is not corrupt. + + + The TZif data structure is corrupt. + + + fromInclusive must be less than or equal to toExclusive. + + + The DaylightTransitionStart property must not equal the DaylightTransitionEnd property. + + + The type must not be imported from COM. + + + The UTC time represented when the offset is applied must be between year 0 and 10,000. + + + The name can be no more than {0} characters in length. + + + Object is not a array with the same number of elements as the array to compare it to. + + + Argument must be of type {0}. + + + The last element of an eight element tuple must be a Tuple. + + + Argument must be of type {0}. + + + The last element of an eight element ValueTuple must be a ValueTuple. + + + Array cannot be null. + + + At least one element in the specified array was null. + + + Found a null value within an array. + + + Cannot have a null child. + + + Collection cannot be null. + + + Dictionary cannot be null. + + + File name cannot be null. + + + Value cannot be null. + + + Key cannot be null. + + + String reference not set to an instance of a String. + + + Type cannot be null. + + + The waitHandles parameter cannot be null. + + + Value to add was out of range. + + + Actual value was {0}. + + + Year, Month, and Day parameters describe an un-representable DateTime. + + + Hour, Minute, and Second parameters describe an un-representable DateTime. + + + Must be less than or equal to the size of the collection. + + + Argument must be between {0} and {1}. + + + Specified time is not supported in this calendar. It should be between {0} (Gregorian date) and {1} (Gregorian date), inclusive. + + + Capacity exceeds maximum capacity. + + + Count must be positive and count must refer to a location within the string/array/collection. + + + The added or subtracted value results in an un-representable DateTime. + + + Months value must be between +/-120000. + + + Ticks must be between DateTime.MinValue.Ticks and DateTime.MaxValue.Ticks. + + + Ticks must be between 0 and and TimeOnly.MaxValue.Ticks. + + + Years value must be between +/-10000. + + + Day must be between 1 and {0} for month {1}. + + + The DayOfWeek enumeration must be in the range 0 through 6. + + + The Day parameter must be in the range 1 through 31. + + + Decimal can only round to between 0 and 28 digits of precision. + + + Decimal's scale value must be between 0 and 28, inclusive. + + + endIndex cannot be greater than startIndex. + + + Enum value was out of legal range. + + + Time value was out of era range. + + + Not a valid Win32 FileTime. + + + Value must be positive. + + + Too many characters. The resulting number of bytes is larger than what can be returned as an int. + + + Too many bytes. The resulting number of chars is larger than what can be returned as an int. + + + Load factor needs to be between 0.1 and 1.0. + + + Index and count must refer to a location within the string. + + + Index and count must refer to a location within the buffer. + + + Index and length must refer to a location within the string. + + + Index was out of range. Must be non-negative and less than the length of the string. + + + Input is too large to be processed. + + + Era value was not valid. + + + A valid high surrogate character is between 0xd800 and 0xdbff, inclusive. + + + A valid low surrogate character is between 0xdc00 and 0xdfff, inclusive. + + + A valid UTF32 value is between 0x000000 and 0x10ffff, inclusive, and should not include surrogate codepoint values (0x00d800 ~ 0x00dfff). + + + The length cannot be greater than the capacity. + + + Index must be within the bounds of the List. + + + Index was out of range. Must be non-negative and less than the size of the list. + + + Index was out of range. Must be non-negative and less than the size of the list. + + + Month must be between one and twelve. + + + Day number must be between 0 and DateOnly.MaxValue.DayNumber. + {Locked="DateOnly.MaxValue.DayNumber"} + + + The Month parameter must be in the range 1 through 12. + + + Value must be non-negative and less than or equal to Int32.MaxValue. + + + '{0}' must be non-negative. + + + '{0}' must be greater than zero. + + + Number must be either non-negative and less than or equal to Int32.MaxValue or -1. + + + Positive number required. + + + Capacity must be positive. + + + Count cannot be less than zero. + + + Length cannot be less than zero. + + + lohSize can't be greater than totalSize + + + Offset and length must refer to a position in the string. + + + Either offset did not refer to a position in the string, or there is an insufficient length of destination character array. + + + Pointer startIndex and length do not refer to a valid string. + + + Valid values are between {0} and {1}, inclusive. + + + Rounding digits must be between 0 and 15, inclusive. + + + Rounding digits must be between 0 and 6, inclusive. + + + capacity was less than the current size. + + + MaxCapacity must be one or greater. + + + StartIndex cannot be less than zero. + + + startIndex cannot be larger than length of string. + + + startIndex must be less than length of string. + + + The TimeSpan parameter must be within plus or minus 14.0 hours. + + + The sum of the BaseUtcOffset and DaylightDelta properties must within plus or minus 14.0 hours. + + + Version's parameters must be greater than or equal to zero. + + + The Week parameter must be in the range 1 through 5. + + + Year must be between 1 and 9999. + + + Function does not accept floating point Not-a-Number values. + + + Source array type cannot be assigned to destination array type. + + + Cannot unload non-collectible AssemblyLoadContext. + + + Unload called on AssemblyLoadContext that is unloading or that was already unloaded. + + + AssemblyLoadContext is unloading or was already unloaded. + + + Could not load file or assembly '{0}'. An attempt was made to load a program with an incorrect format. + + + A resolver is already set for the assembly. + + + A prior operation on this collection was interrupted by an exception. Collection's state is no longer trusted. + + + This range in the underlying list is invalid. A possible cause is that elements were removed. + + + --- End of inner exception stack trace --- + + + --- End of stack trace from previous location --- + + + Exception of type '{0}' was thrown. + + + The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. + + + Invalid length for a Base-64 char array or string. + + + String was not recognized as a valid Boolean. + + + Format specifier '{0}' was invalid. + + + No format specifiers were provided. + + + The input is not a valid hex string as it contains a non-hex character. + + + The input is not a valid hex string as its length is not a multiple of 2. + + + Cannot find a matching quote character for the character '{0}'. + + + Input string was either empty or contained only whitespace. + + + Expected 0x prefix. + + + Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). + + + Guid string should only contain hexadecimal characters. + + + Expected {0xdddddddd, etc}. + + + Could not find a comma, or the length between the previous token and the comma was zero (i.e., '0x,'etc.). + + + Could not find a brace, or the length between the previous token and the brace was zero (i.e., '0x,'etc.). + + + Dashes are in the wrong position for GUID parsing. + + + Could not find the ending brace. + + + Additional non-parsable characters are at the end of the string. + + + Unrecognized Guid format. + + + Index (zero based) must be greater than or equal to zero and less than the size of the argument list. + + + Format String can be only 'D', 'd', 'N', 'n', 'P', 'p', 'B', 'b', 'X' or 'x'. + + + Input string was not in a correct format. + + + String must be exactly one character long. + + + Could not find any recognizable digits. + + + String was not recognized as a valid TimeSpan. + + + Insufficient available memory to meet the expected demands of an operation at this time. Please try again later. + + + Insufficient memory to meet the expected demands of an operation, and this system is likely to never satisfy this request. If this is a 32 bit system, consider booting in 3 GB mode. + + + Insufficient available memory to meet the expected demands of an operation at this time, possibly due to virtual address space fragmentation. Please try again later. + + + Cannot marshal: Encountered unmappable character. + + + Null object cannot be converted to a value type. + + + At least one element in the source array could not be cast down to the destination array type. + + + Invalid cast from '{0}' to '{1}'. + + + Object must implement IConvertible. + + + Object cannot be stored in an array of this type. + + + WinRT Interop has already been initialized and cannot be initialized again. + + + Internal Error in DateTime and Calendar operations. + + + Enumeration already finished. + + + Collection was modified; enumeration operation may not execute. + + + Enumeration has not started. Call MoveNext. + + + Enumeration has either not started or has already finished. + + + OSVersion's call to GetVersionEx failed. + + + Handle is not initialized. + + + Handle is not pinned. + + + Hashtable insert failed. Load factor too high. The most common cause is multiple threads writing to the Hashtable simultaneously. + + + Failed to compare two elements in the array. + + + Nullable object must have a value. + + + The underlying array is null. + + + Cannot pack a packed Overlapped again. + + + Instance is read-only. + + + The thread was created with a ThreadStart delegate that does not accept a parameter. + + + Unknown enum type. + + + This property has already been set and cannot be modified. + + + Array.CreateInstance() can only accept Type objects created by the runtime. + + + Internal Error: This operation cannot be invoked in an eager class constructor. + + + Cannot call Set on a null context + + + AsyncFlowControl object must be used on the thread where it was created. + + + Cannot restore context flow when it is not suppressed. + + + Context flow is already suppressed. + + + AsyncFlowControl object can be used only once to call Undo(). + + + AsyncFlowControl objects can be used to restore flow only on a Context that had its flow suppressed. + + + The stream is currently in use by a previous operation on the stream. + + + Common Language Runtime detected an invalid program. + + + Common Language Runtime detected an invalid program. The body of method '{0}' is invalid. + + + Method '{0}' has a variable argument list. Variable argument lists are not supported in .NET Core. + + + Object.Finalize() can not be called directly. It is only callable by the runtime. + + + UnmanagedCallersOnly method cannot be called from managed code. + + + The time zone ID '{0}' was found on the local computer, but the registry information was corrupt. + + + The time zone ID '{0}' was found on the local computer, but the file at '{1}' was corrupt. + + + Invalid Julian day in POSIX strings. + + + Julian n day in POSIX strings is not supported. + + + There are no ttinfo structures in the tzfile. At least one ttinfo structure is required in order to construct a TimeZoneInfo object. + + + '{0}' is not a valid POSIX-TZ-environment-variable MDate rule. A valid rule has the format 'Mm.w.d'. + + + Could not find the drive '{0}'. The drive might not be ready or might not be mapped. + + + The file '{0}' already exists. + + + File name: '{0}' + + + Unable to find the specified file. + + + Could not load file or assembly '{0}'. The system cannot find the file specified. + + + Could not load the specified file. + + + Could not load the file '{0}'. + + + Could not find a part of the path. + + + Could not find a part of the path '{0}'. + + + The specified file name or path is too long, or a component of the specified path is too long. + + + The path '{0}' is too long, or a component of the specified path is too long. + + + Too many levels of symbolic links in '{0}'. + + + The process cannot access the file '{0}' because it is being used by another process. + + + The process cannot access the file because it is being used by another process. + + + Cannot create '{0}' because a file or directory with the same name already exists. + + + Failed to create '{0}' with allocation size '{1}' because the disk was full. + + + Failed to create '{0}' with allocation size '{1}' because the file was too large. + + + Access to the path is denied. + + + Access to the path '{0}' is denied. + + + The lazily-initialized type does not have a public, parameterless constructor. + + + The mode argument specifies an invalid value. + + + ValueFactory returned null. + + + Value is not created. + + + ValueFactory attempted to access the Value property of this instance. + + + Constructor on type '{0}' not found. + + + An assembly (probably '{1}') must be rewritten using the code contracts binary rewriter (CCRewrite) because it is calling Contract.{0} and the CONTRACTS_FULL symbol is defined. Remove any explicit definitions of the CONTRACTS_FULL symbol from your project and rebuild. CCRewrite can be downloaded from http://go.microsoft.com/fwlink/?LinkID=169180. \r\nAfter the rewriter is installed, it can be enabled in Visual Studio from the project's Properties page on the Code Contracts pane. Ensure that 'Perform Runtime Contract Checking' is enabled, which will define CONTRACTS_FULL. + + + Collection was of a fixed size. + + + The number of WaitHandles must be less than or equal to 64. + + + The number of WaitHandles on a STA thread must be less than or equal to 63. + + + No data is available for encoding {0}. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method. + + + Function not marked with UnmanagedCallersOnlyAttribute. + + + Collection is read-only. + + + The specified operation is not supported on Ranges. + + + The string comparison type passed in is currently not supported. + + + Arrays of System.Void are not supported. + + + Cannot create boxed ByRef-like values. + + + Type is not supported. + + + WaitAll for multiple handles on a STA thread is not supported. + + + Overlapped I/O is not supported. + + + Cannot access a disposed object. + + + Object name: '{0}'. + + + Safe handle has been closed. + + + Value was either too large or too small for an unsigned byte. + + + Value was either too large or too small for a character. + + + Value was either too large or too small for a Decimal. + + + Value was either too large or too small for a Double. + + + The TimeSpan could not be parsed because at least one of the numeric components is out of range or contains too many digits. + + + The duration cannot be returned for TimeSpan.MinValue because the absolute value of TimeSpan.MinValue exceeds the value of TimeSpan.MaxValue. + + + Value was either too large or too small for an Int16. + + + Value was either too large or too small for an Int32. + + + Value was either too large or too small for an Int64. + + + Negating the minimum value of a twos complement number is invalid. + + + The string was being parsed as an unsigned number and could not have a negative sign. + + + Value was either too large or too small for a signed byte. + + + Value was either too large or too small for a Single. + + + TimeSpan overflowed because the duration is too long. + + + Value was either too large or too small for a UInt16. + + + Value was either too large or too small for a UInt32. + + + Value was either too large or too small for a UInt64. + + + Only single dimension arrays are supported here. + + + An object that does not derive from System.Exception has been wrapped in a RuntimeWrappedException. + + + The condition argument is null. + + + The value of the field '{0}' is invalid. The serialized data is corrupt. + + + An error occurred while deserializing the object. The serialized data is corrupt. + + + The serialized data contained an invalid escape sequence '\\{0}'. + + + Only system-provided types can be passed to the GetUninitializedObject method. '{0}' is not a valid instance of a type. + + + The timeout must represent a value between -1 and Int32.MaxValue, inclusive. + + + The wait completed due to an abandoned mutex. + + + Adding the specified count to the semaphore would cause it to exceed its maximum count. + + + Thread was interrupted from a waiting state. + + + No handle of the given name exists. + + + A WaitHandle with system-wide name '{0}' cannot be created. A WaitHandle of a different type might have the same name. + + + The WaitHandle cannot be signaled because it would exceed its maximum count. + + + The time zone ID '{0}' was not found on the local computer. + + + Type constructor threw an exception. + + + The type initializer for '{0}' threw an exception. + + + A type initializer threw an exception. To determine which type, inspect the InnerException's StackTrace property. + + + Operation could destabilize the runtime. + + + Enum underlying type and the object must be same type or object. Type passed in was '{0}'; the enum underlying type was '{1}'. + + + Format String can be only 'G', 'g', 'X', 'x', 'F', 'f', 'D' or 'd'. + + + The value passed in must be an enum base or an underlying type for an enum, such as an Int32. + + + Enum underlying type and the object must be same type or object must be a String. Type passed in was '{0}'; the enum underlying type was '{1}'. + + + Type must be a type provided by the runtime. + + + Must specify valid information for parsing in the string. + + + Requested value '{0}' was not found. + + + Destination array was not long enough. Check the destination index, length, and the array's lower bounds. + + + Source array was not long enough. Check the source index, length, and the array's lower bounds. + + + String cannot be of zero length. + + + The first char in the string is the null character. + + + Environment variable name or value is too long. + + + Environment variable name cannot contain equal character. + + + Assumption failed. + + + Assumption failed: {0} + + + Assertion failed. + + + Assertion failed: {0} + + + Precondition failed. + + + Precondition failed: {0} + + + Postcondition failed. + + + Postcondition failed: {0} + + + Postcondition failed after throwing an exception. + + + Postcondition failed after throwing an exception: {0} + + + The home directory of the current user could not be determined. + + + Invariant failed. + + + Invariant failed: {0} + + + Could not find a resource entry for the encoding codepage '{0} - {1}' + + + Unicode + + + Unicode (Big-Endian) + + + Unicode (UTF-32) + + + Unicode (UTF-32 Big-Endian) + + + US-ASCII + + + Western European (ISO) + + + Unicode (UTF-7) + + + Unicode (UTF-8) + + + ---- DEBUG ASSERTION FAILED ---- + + + ---- Assert Long Message ---- + + + ---- Assert Short Message ---- + + + Object cannot be cast to Empty. + + + Unknown TypeCode value. + + + Could not determine the order of year, month, and date from '{0}'. + + + String '{0}' was not recognized as a valid DateTime. + + + String '{0}' was not recognized as a valid DateOnly. + {Locked="DateOnly"} + + + String '{0}' was not recognized as a valid TimeOnly. + {Locked="TimeOnly"} + + + String '{0}' contains parts which are not specific to the {1}. + + + The DateTime represented by the string '{0}' is not supported in calendar '{1}'. + + + String '{0}' was not recognized as a valid DateTime because the day of week was incorrect. + + + The DateTime represented by the string '{0}' is out of range. + + + There must be at least a partial date with a year present in the input string '{0}'. + + + The time zone offset of string '{0}' must be within plus or minus 14 hours. + + + DateTime pattern '{0}' appears more than once with different values. + + + The string '{0}' was not recognized as a valid DateTime. There is an unknown word starting at index '{1}'. + + + The UTC representation of the date '{0}' falls outside the year range 1-9999. + + + Ambiguous match found. + + + One or more errors occurred. + + + An element of innerExceptions was null. + + + The serialization stream contains no inner exceptions. + + + (Inner Exception #{0}) + This text is prepended to each inner exception description during aggregate exception formatting + + + Time-out interval must be less than 2^32-2. + + + Period must be less than 2^32-2. + + + The current SynchronizationContext may not be used as a TaskScheduler. + + + ExecuteTask may not be called for a task which was previously queued to a different TaskScheduler. + + + The TryExecuteTaskInline call to the underlying scheduler succeeded, but the task body was not invoked. + + + An exception was thrown by a TaskScheduler. + + + It is invalid to exclude specific continuation kinds for continuations off of multiple tasks. + + + The specified TaskContinuationOptions combined LongRunning and ExecuteSynchronously. Synchronous continuations should not be long running. + + + The tasks argument contains no tasks. + + + The tasks argument included a null value. + + + It is invalid to specify TaskCreationOptions.PreferFairness in calls to FromAsync. + + + It is invalid to specify TaskCreationOptions.LongRunning in calls to FromAsync. + + + The builder was not properly initialized. + + + An attempt was made to transition a task to a final state when it had already completed. + + + {Not yet computed} + + + The operation was canceled. + + + No tokens were supplied. + + + The CancellationTokenSource has been disposed. + + + The CancellationTokenSource associated with this CancellationToken has been disposed. + + + (Internal)Expected an Exception or an IEnumerable<Exception> + + + A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. + + + The value needs to be either -1 (signifying an infinite timeout), 0 or a positive integer. + + + The value needs to translate in milliseconds to -1 (signifying an infinite timeout), 0 or a positive integer less than or equal to Int32.MaxValue. + + + A task may only be disposed if it is in a completion state (RanToCompletion, Faulted or Canceled). + + + The tasks array included at least one null element. + + + The specified TaskContinuationOptions excluded all continuation kinds. + + + The value needs to translate in milliseconds to -1 (signifying an infinite timeout), 0, or a positive integer less than or equal to the maximum allowed timer duration. + + + RunSynchronously may not be called on a task that was already started. + + + The task has been disposed. + + + RunSynchronously may not be called on a task that has already completed. + + + RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method. + + + RunSynchronously may not be called on a continuation task. + + + Start may not be called on a task that was already started. + + + Start may not be called on a continuation task. + + + Start may not be called on a promise-style task. + + + Start may not be called on a task that has completed. + + + A task was canceled. + + + The exceptions collection was empty. + + + The exceptions collection included at least one null element. + + + '{0}' cannot be greater than {1}. + + + An exception was not handled in an AsyncLocal<T> notification callback. + + + Either the IAsyncResult object did not come from the corresponding async method on this type, or the End method was called multiple times with the same IAsyncResult. + + + Thread tracking is disabled. + + + The calling thread already holds the lock. + + + The calling thread does not hold the lock. + + + The tookLock argument must be set to false before calling this method. + + + The timeout must be a value between -1 and Int32.MaxValue, inclusive. + + + The event has been disposed. + + + The spinCount argument must be in the range 0 to {0}, inclusive. + + + There are too many threads currently waiting on the event. A maximum of {0} waiting threads are supported. + + + Send is not supported in the Windows Runtime SynchronizationContext + + + The semaphore has been disposed. + + + The releaseCount argument must be greater than zero. + + + The timeout must represent a value between -1 and Int32.MaxValue, inclusive. + + + The maximumCount argument must be a positive number. If a maximum is not required, use the constructor without a maxCount parameter. + + + The initialCount argument must be non-negative and less than or equal to the maximumCount. + + + The ThreadLocal object is not tracking values. To use the Values property, use a ThreadLocal constructor that accepts the trackAllValues parameter and set the parameter to true. + + + ValueFactory attempted to access the Value property of this instance. + + + The ThreadLocal object has been disposed. + + + Write lock may not be acquired with read lock held. This pattern is prone to deadlocks. Please ensure that read locks are released before taking a write lock. If an upgrade is necessary, use an upgrade lock in place of the read lock. + + + Recursive write lock acquisitions not allowed in this mode. + + + A read lock may not be acquired with the write lock held in this mode. + + + Recursive upgradeable lock acquisitions not allowed in this mode. + + + Recursive read lock acquisitions not allowed in this mode. + + + The lock is being disposed while still being used. It either is being held by a thread and/or has active waiters waiting to acquire the lock. + + + The write lock is being released without being held. + + + Upgradeable lock may not be acquired with read lock held. + + + Upgradeable lock may not be acquired with write lock held in this mode. Acquiring Upgradeable lock gives the ability to read along with an option to upgrade to a writer. + + + The upgradeable lock is being released without being held. + + + The read lock is being released without being held. + + + Timeouts are not supported on this stream. + + + The Timer was already closed using an incompatible Dispose method. + + + Stream does not support reading. + + + Stream does not support writing. + + + Cannot access a closed Stream. + + + Derived classes must provide an implementation. + + + Cannot remove the event handler since no public remove method exists for the event. + + + Cannot add the event handler since no public add method exists for the event. + + + Serialization error. + + + Member '{0}' was not found. + + + Version value must be positive. + + + Cannot add the same member twice to a SerializationInfo object. + + + This non-CLS method is not implemented. + + + Cannot resolve {0} to a TypeInfo object. + + + Binary format of the specified custom attribute was invalid. + + + The member must be either a field or a property. + + + Specified filter criteria was invalid. + + + Attempt has been made to use a COM object that does not have a backing class factory. + + + Specified OLE variant was invalid. + + + Must specify one or more parameters. + + + Type must be a Pointer. + + + Invalid handle. + + + The Enum type should contain one and only one instance field. + + + Type passed in must be derived from System.Attribute or System.Attribute itself. + + + A String must be provided for the filter criteria. + + + An Int32 must be provided for the filter criteria. + + + Adding or removing event handlers dynamically is not supported on WinRT events. + + + COM Interop is not supported on this platform. + + + ReflectionOnly loading is not supported on this platform. + + + Locking/unlocking file regions is not supported on this platform. Use FileShare on the entire file instead. + + + This API is specific to the way in which Windows handles asynchronous I/O, and is not supported on this platform. + + + Dynamic code generation is not supported on this platform. + + + Secondary AppDomains are not supported on this platform. + + + Code Access Security is not supported on this platform. + + + AppDomain resource monitoring is not supported on this platform. + + + Windows Principal functionality is not supported on this platform. + + + Thread abort is not supported on this platform. + + + Thread suspend is not supported on this platform. + + + Member '{0}' not found. + + + Method '{0}' not found. + + + Field '{0}' not found. + + + String cannot have zero length. + + + Failed to get marshaler for IID {0}. + + + The time zone ID '{0}' was found on the local computer, but the application does not have permission to read the file. + + + The time zone ID '{0}' was found on the local computer, but the application does not have permission to read the registry information. + + + Invalid assembly public key. + + + Requested registry access is not allowed. + + + Could not load type '{0}' from assembly '{1}'. + + + '{0}' from assembly '{1}' has too many dimensions. + + + Could not load type '{0}' from assembly '{1}' because generic types cannot have explicit layout. + + + Could not load type '{0}' from assembly '{1}' because the format is invalid. + + + Array of type '{0}' from assembly '{1}' cannot be created because base value type is too large. + + + Could not load type '{0}' from assembly '{1}' because it contains an object field at offset '{2}' that is incorrectly aligned or overlapped by a non-object field. + + + Method not found: '{0}'. + + + Field not found: '{0}'. + + + Access to the registry key '{0}' is denied. + + + Unknown error '{0}'. + + + The specified Type must be a struct containing no references. + + + Buffer cannot be null. + + + The number of bytes cannot exceed the virtual address space on a 32 bit machine. + + + The length of the buffer must be less than the maximum UIntPtr value for your platform. + + + Not enough space available in the buffer. + + + You must call Initialize on this object instance before using it. + + + The buffer is not associated with this pool and may not be returned to it. + + + Offset and length were greater than the size of the SafeBuffer. + + + Invalid seek origin. + + + There are not enough bytes remaining in the accessor to read at this position. + + + There are not enough bytes remaining in the accessor to write at this position. + + + Offset and capacity were greater than the size of the view. + + + UnmanagedMemoryStream length must be non-negative and less than 2^63 - 1 - baseAddress. + + + The UnmanagedMemoryAccessor capacity and offset would wrap around the high end of the address space. + + + Stream length must be non-negative and less than 2^31 - 1 - origin. + + + The UnmanagedMemoryStream capacity would wrap around the high end of the address space. + + + The method cannot be called twice on the same instance. + + + Unable to expand length of this stream beyond its capacity. + + + An attempt was made to move the position before the beginning of the stream. + + + Stream was too long. + + + Read an invalid decimal value from the buffer. + + + Accessor does not support reading. + + + This operation is not supported for an UnmanagedMemoryStream created from a SafeBuffer. + + + Accessor does not support writing. + + + Stream does not support seeking. + + + Unmanaged memory stream position was beyond the capacity of the stream. + + + ArrayWithOffset: offset exceeds array size. + + + Cannot access a closed accessor. + + + The position may not be greater or equal to the capacity of the accessor. + + + Unable to read beyond the end of the stream. + + + Attempted to read past the end of the stream. + + + Cannot access a closed file. + + + Search pattern '{0}' cannot contain ".." to move up directories and can be contained only internally in file/directory names, as in "a..b". + + + Specified file length was too large for the file system. + + + 'handle' has been disposed or is an invalid handle. + + + 'handle' has already been bound to the thread pool, or was not opened for asynchronous I/O. + + + 'preAllocated' is already in use. + + + 'overlapped' has already been freed. + + + 'overlapped' was not allocated by this ThreadPoolBoundHandle instance. + + + Handle does not support asynchronous operations. The parameters to the FileStream constructor may need to be changed to indicate that the handle was opened synchronously (that is, it was not opened for overlapped I/O). + + + Path cannot be null. + + + SafeHandle cannot be null. + + + Empty path name is not legal. + + + Append access can be requested only in write-only mode. + + + Preallocation size can be requested only in write mode. + + + Preallocation size can be requested only for new files. + + + Combining FileMode: {0} with FileAccess: {1} is invalid. + + + [Unknown] + + + The OS handle's position is not what FileStream expected. Do not use a handle simultaneously in one FileStream and in Win32 code or another FileStream. This may cause data loss. + + + The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size. + + + IO operation will not work. Most likely the file will become too long. + + + IO operation will not work. Most likely the file will become too long or the handle was not opened to support synchronous IO operations. + + + FileStream was asked to open a device that was not a file. For support for devices like 'com1:' or 'lpt1:', call CreateFile, then use the FileStream constructors that take an OS handle as an IntPtr. + + + BindHandle for ThreadPool failed on this handle. + + + The link's file system entry type is inconsistent with that of its target: {0} + + + Handle does not support synchronous operations. The parameters to the FileStream constructor may need to be changed to indicate that the handle was opened asynchronously (that is, it was opened explicitly for overlapped I/O). + + + Unable to truncate data that previously existed in a file opened in Append mode. + + + Unable seek backward to overwrite data that previously existed in a file opened in Append mode. + + + The ResourceReader class does not know how to read this version of .resources files. + + + Stream is not a valid resource file. + + + Corrupt .resources file. Unable to read resources from this file because of invalid header information. Try regenerating the .resources file. + + + Stream was not readable. + + + Corrupt .resources file. String length must be non-negative. + + + Corrupt .resources file. The Invalid offset into name section is . + + + Corrupt .resources file. The specified type doesn't match the available data in the stream. + + + Corrupt .resources file. The resource name for name index that extends past the end of the stream is + + + Corrupt .resources file. Invalid offset into data section is + + + Too many bytes in what should have been a 7-bit encoded integer. + + + Corrupt .resources file. The specified type doesn't exist. + + + ResourceReader is closed. + + + Unable to find manifest resource. + + + The keys for this dictionary are missing. + + + One of the serialized keys is null. + + + Mutating a key collection derived from a dictionary is not allowed. + + + Mutating a value collection derived from a dictionary is not allowed. + + + MemoryStream's internal buffer cannot be accessed. + + + Memory stream is not expandable. + + + Stream cannot be null. + + + BinaryReader encountered an invalid string length of {0} characters. + + + The number of bytes requested does not fit into BinaryReader's internal buffer. + + + Insufficient state to deserialize the object. Missing field '{0}'. + + + The UnitySerializationHolder object is designed to transmit information about other types and is not serializable itself. + + + The given module {0} cannot be found within the assembly {1}. + + + Invalid Unity type. + + + The handle is invalid. + + + The named version of this synchronization primitive is not supported on this platform. + + + The current thread attempted to reacquire a mutex that has reached its maximum acquire count. + + + Insufficient state to return the real object. + + + Cannot get the member '{0}'. + + + The method signature cannot be null. + + + Unknown member type. + + + Non existent ParameterInfo. Position bigger than member's parameters length. + + + Serialized member does not have a ParameterInfo. + + + Assembly cannot be null. + + + The NeutralResourcesLanguageAttribute on the assembly "{0}" specifies an invalid culture name: "{1}". + + + The NeutralResourcesLanguageAttribute specifies an invalid or unrecognized ultimate resource fallback location: "{0}". + + + Satellite contract version attribute on the assembly '{0}' specifies an invalid version: {1}. + + + Type parameter must refer to a subclass of ResourceSet. + + + Corrupt .resources file. A resource name extends past the end of the stream. + + + Corrupt .resources file. Resource name extends past the end of the file. + + + '{0}': ResourceSet derived classes must provide a constructor that takes a String file name and a constructor that takes a Stream. + + + Resource '{0}' was not a Stream - call GetObject instead. + + + A case-insensitive lookup for resource file "{0}" in assembly "{1}" found multiple entries. Remove the duplicates or specify the exact case. + + + Could not find the resource "{0}" among the resources {2} embedded in the assembly "{1}", nor among the resources in any satellite assemblies for the specified culture. Perhaps the resources were embedded with an incorrect name. + + + Could not find any resources appropriate for the specified culture (or the neutral culture) on disk. + + + Unable to open Package Resource Index. + + + Unable to load resources for resource file "{0}" in package "{1}". + + + The satellite assembly named "{1}" for fallback culture "{0}" either could not be found or could not be loaded. This is generally a setup problem. Please consider reinstalling or repairing the application. + + + Resource lookup fell back to the ultimate fallback resources in a satellite assembly, but that satellite either was not found or could not be loaded. Please consider reinstalling or repairing the application. + + + Delegates that are not of type MulticastDelegate may not be combined. + + + Found an obsolete .resources file in assembly '{0}'. Rebuild that .resources file then rebuild that assembly. + + + Cannot read resources that depend on serialization. + + + Cannot access a closed resource set. + + + The specified resource name "{0}" does not exist in the resource file. + + + Corrupt .resources file. The specified data length '{0}' is not a valid position in the stream. + + + Corrupt .resources file. String for name index '{0}' extends past the end of the file. + + + Resource '{0}' was not a String - call GetObject instead. + + + Resource was of type '{0}' instead of String - call GetObject instead. + + + This .resources file should not be read with this reader. The resource reader type is "{0}". + + + Type must derive from Delegate. + + + Serialization of global methods (including implicit serialization via the use of asynchronous delegates) is not supported. + + + This operation is invalid on overlapping buffers. + + + DelegateSerializationHolder objects are designed to represent a delegate during serialization and are not serializable themselves. + + + The delegate cannot be serialized properly due to missing metadata for the target method. + + + Uninitialized Strings cannot be created. + + + totalSize is too large. For more information about setting the maximum size, see \"Latency Modes\" in http://go.microsoft.com/fwlink/?LinkId=522706. + + + The NoGCRegion mode was already in progress. + + + Allocated memory exceeds specified memory for NoGCRegion mode. + + + Garbage collection was induced in NoGCRegion mode. + + + NoGCRegion mode must be set. + + + The NoGCRegion mode is in progress. End it and then set a different mode. + + + This API is not available when the concurrent GC is enabled. + + + Thread is running or terminated; it cannot restart. + + + Thread is dead; priority cannot be accessed. + + + Thread is dead; state cannot be accessed. + + + Thread has not been started. + + + Unable to set thread priority. + + + Object fields may not be properly initialized. + + + OnDeserialization method was called while the object was not being deserialized. + + + Cannot create an abstract class. + + + Cannot create a type for which Type.ContainsGenericParameters is true. + + + Value was invalid. + + + Cannot create uninitialized instances of types requiring managed activation. + + + ResourceManager method '{0}' is not supported when reading from .resw resource files. + + + ResourceManager property '{0}' is not supported when reading from .resw resource files. + + + Object cannot be cast to DBNull. + + + This feature is not currently implemented. + + + The corresponding delegate has been garbage collected. Please make sure the delegate is still referenced by managed code when you are using the marshalled native function pointer. + + + Ambiguous match found. + + + ChangeType operation is not supported. + + + Array may not be empty. + + + Member not found. + + + Field not found. + + + Object cannot be cast from DBNull to other types. + + + Only one DBNull instance may exist, and calls to DBNull deserialization methods are not allowed. + + + The invoked member is not supported in a dynamic assembly. + + + The serialized Capacity property of StringBuilder must be positive, less than or equal to MaxCapacity and greater than or equal to the String length. + + + The serialized MaxCapacity property of StringBuilder must be positive and greater than or equal to the String length. + + + Remoting is not supported on this platform. + + + Strong-name signing is not supported on this platform. + + + Invalid serialized DateTime data. Unable to find 'ticks' or 'dateData'. + + + The values for this dictionary are missing. + + + Invalid serialized DateTime data. Ticks must be between DateTime.MinValue.Ticks and DateTime.MaxValue.Ticks. + + + The ANSI string passed in could not be converted from the default ANSI code page to Unicode. + + + The string must be null-terminated. + + + ArgIterator is not supported on this platform. + + + Type had been unloaded. + + + Value was either too large or too small for a Currency. + + + Secure binary serialization is not supported on this platform. + + + An IntPtr or UIntPtr with an eight byte value cannot be deserialized on a machine with a four byte word size. + + + The keys and values arrays have different sizes. + + + Abstract event source must not declare event methods ({0} with ID {1}). + + + Abstract event source must not declare {0} nested type. + + + Getting out of bounds during scalar addition. + + + Bad Hexidecimal digit "{0}". + + + Channel {0} does not match event channel value {1}. + + + Data descriptors are out of range. + + + Multiple definitions for string "{0}". + + + The type of {0} is not expected in {1}. + + + Must have an even number of Hexidecimal digits. + + + Channel {0} has a value of {1} which is outside the legal range (16-254). + + + Event {0} has ID {1} which is already in use. + + + Event {0} (with ID {1}) has a non-default opcode but not a task. + + + Event method {0} (with ID {1}) is an explicit interface method implementation. Re-write method as implicit implementation. + + + Event {0} (with ID {1}) has a name that is not the concatenation of its task name and opcode. + + + Event name {0} used more than once. If you wish to overload a method, the overloaded method should have a NonEvent attribute. + + + Event {0} was called with {1} argument(s), but it is defined with {2} parameter(s). + + + An instance of EventSource with Guid {0} already exists. + + + The payload for a single event is too large. + + + Event {0} specifies an Admin channel {1}. It must specify a Message property. + + + Keyword {0} has a value of {1} which is outside the legal range (0-0x0000080000000000). + + + Opcode {0} has a value of {1} which is outside the legal range (11-238). + + + Task {0} has a value of {1} which is outside the legal range (1-65535). + + + Illegal value "{0}" (prefix strings with @ to indicate a literal string). + + + Incorrectly-authored TypeInfo - a type should be serialized as one field or as one group + + + Invalid command value. + + + Can't specify both etw event format flags. + + + Keywords {0} and {1} are defined with the same value ({2}). + + + Value {0} for keyword {1} needs to be a power of 2. + + + Creating an EventListener inside a EventListener callback. + + + Listener not found. + + + An error occurred when writing to a listener. + + + Attempt to define more than the maximum limit of 8 channels for a provider. + + + Event {0} was assigned event ID {1} but {2} was passed to WriteEvent. + + + The Guid of an EventSource must be non zero. + + + The name of an EventSource must not be null. + + + Event IDs must be positive integers. + + + No Free Buffers available from the operating system (e.g. event rate too fast). + + + The API supports only anonymous types or types decorated with the EventDataAttribute. Non-compliant type: {0} dataType. + + + EventSource expects the first parameter of the Event method to be of type Guid and to be named "relatedActivityId" when calling WriteEventWithRelatedActivityId. + + + Arrays of Binary are not supported. + + + Arrays of Nil are not supported. + + + Arrays of null-terminated string are not supported. + + + Enumerables of custom-serialized data are not supported + + + Nested arrays/enumerables are not supported. + + + Null passed as a event argument. + + + Opcodes {0} and {1} are defined with the same value ({2}). + + + Pins are out of range. + + + Recursive type definition is not supported. + + + Bit position in AllKeywords ({0}) must equal the command argument named "EtwSessionKeyword" ({1}). + + + An event with stop suffix must follow a corresponding event with a start suffix. + + + Tasks {0} and {1} are defined with the same value ({2}). + + + Event {0} (with ID {1}) has the same task/opcode pair as event {2} (with ID {3}). + + + Too many arguments. + + + Too many fields in structure. + + + EventSource({0}, {1}) + + + There must be an even number of trait strings (they are key-value pairs). + + + Event source types must be sealed or abstract. + + + Event source types must derive from EventSource. + + + Use of undefined channel value {0} for event {1}. + + + Use of undefined keyword value {0} for event {1}. + + + Use of undefined opcode value {0} for event {1}. + + + Unknown ETW trait "{0}". + + + Unsupported type {0} in event source. + + + Event {0} specifies an illegal or unsupported formatting message ("{1}"). + + + The parameters to the Event method do not match the parameters to the WriteEvent method. This may cause the event to be displayed incorrectly. + + + Stream was not writable. + + + Unicode surrogate characters must be written out as pairs together in the same call, not individually. Consider passing in a character array instead. + + + '{0}' field specified was not found. + + + '{0}' property specified was not found. + + + Equals() on Span and ReadOnlySpan is not supported. Use operator== instead. + + + GetHashCode() on Span and ReadOnlySpan is not supported. + + + Destination is too short. + + + Cannot use type '{0}'. Only value types without pointers or references are supported. + + + Overlapping spans have mismatching alignment. + + + Array.ConstrainedCopy will only work on array types that are provably compatible, without any form of boxing, unboxing, widening, or casting of each array element. Change the array types (i.e., copy a Derived[] to a Base[]), or use a mitigation strategy in the CER for Array.Copy's less powerful reliability contract, such as cloning the array or throwing away the potentially corrupt destination array. + + + Dll was not found. + + + Unable to load DLL '{0}': The specified module could not be found. + + + Attempted to access a drive that is not available. + + + Type could not be marshaled because the length of an embedded array instance does not match the declared length in the layout. + + + Cannot marshal: Encountered unmappable character. + + + Marshaling directives are invalid. + + + No value exists with that name. + + + Registry value names should not be greater than 16,383 characters. + + + Serializing delegates is not supported on this platform. + + + Cannot create an instance of {0} as it is an open type. + + + AssemblyName.GetAssemblyName() is not supported on this platform. + + + Cannot create arrays of open type. + + + Cannot create arrays of ByRef-like values. + + + at + + + --- End of stack trace from previous location where exception was thrown --- + + + The given assembly name or codebase was invalid + + + Must be an array type. + + + Left to right characters may not be mixed with right to left characters in IDN labels. + + + IDN labels must be between 1 and 63 characters long. + + + IDN names must be between 1 and {0} characters long. + + + Invalid IDN encoded string. + + + Label contains character '{0}' not allowed with UseStd3AsciiRules + + + Decoded string is not a valid IDN name. + + + This operation is only valid on generic types. + + + This method is not supported on signature types. + + + Memory<T> has been disposed. + + + Release all references before disposing this instance. + + + HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code. + + + HashCode is a mutable struct and should not be compared with other HashCodes. + + + Cannot write to a closed TextWriter. + + + Cannot read from a closed TextReader. + + + The read operation returned an invalid length. + + + Basepath argument is not fully qualified. + + + Number of elements in source vector is greater than the destination array + + + Specified type is not supported + + + The method was called with a null array argument. + + + At least {0} element(s) are expected in the parameter "{1}". + + + The target method returned a null reference. + + + Computer name could not be obtained. + + + Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct. + + + Failed to set the specified COM apartment state. + + + Use CompressedStack.(Capture/Run) instead. + + + This operation must be performed on the same thread as that represented by the Thread instance. + + + The SyncRoot property may not be used for the synchronization of concurrent collections. + + + IAsyncResult object did not come from the corresponding async method on this type. + + + EndRead can only be called once for each asynchronous operation. + + + EndWrite can only be called once for each asynchronous operation. + + + Either the IAsyncResult object did not come from the corresponding async method on this type, or EndRead was called multiple times with the same IAsyncResult. + + + Either the IAsyncResult object did not come from the corresponding async method on this type, or EndWrite was called multiple times with the same IAsyncResult. + + + The week parameter must be in the range 1 through 53. + + + The type serialized in the .resources file was not the same type that the .resources file said it contained. Expected '{0}' but read '{1}'. + + + Cannot extract a Unicode scalar value from the specified index in the input. + + + Characters following the format symbol must be a number of {0} or less. + + + The 'G' format combined with a precision is not supported. + + + Precision cannot be larger than {0}. + + + Cannot load hostpolicy library. AssemblyDependencyResolver is currently only supported if the runtime is hosted through hostpolicy library. + + + Dependency resolution failed for component {0} with error code {1}. Detailed error: {2} + + + An action was attempted during deserialization that could lead to a security vulnerability. The action has been aborted. + + + An action was attempted during deserialization that could lead to a security vulnerability. The action has been aborted. To allow the action, set the '{0}' AppContext switch to true. + + + Assembly.LoadFrom with hashValue is not supported. + + + Bad IL format. + + + Length of items must be same as length of keys. + + + Cannot write to a BufferedStream while the read buffer is not empty if the underlying stream is not seekable. Ensure that the stream underlying this BufferedStream can seek or avoid interleaving read and write operations on this BufferedStream. + + + Found invalid data while decoding. + + + Resource type in the ResourceScope enum is going from a more restrictive resource type to a more general one. From: "{0}" To: "{1}" + + + The type parameter cannot be null when scoping the resource's visibility to Private or Assembly. + + + Unknown value for the ResourceScope: {0} Too many resource type bits may be set. + + + Unknown value for the ResourceScope: {0} Too many resource visibility bits may be set. + + + The parameter '{0}' cannot be an empty string. + + + ApplicationId cannot have an empty string for the name. + + + FrameworkName is invalid. + + + FrameworkName version component is invalid. + + + FrameworkName version component is missing. + + + FrameworkName cannot have less than two components or more than three components. + + + Non-exhaustive switch expression failed to match its input. + + + Attempted to marshal an object across a context boundary. + + + Attempted to access an unloaded AppDomain. + + + Unmatched value was {0}. + + + Support for UTF-7 is disabled. See {0} for more information. + + + Type '{0}' returned by IDynamicInterfaceCastable does not implement the requested interface '{1}'. + + + Type '{0}' returned by IDynamicInterfaceCastable does not have the attribute '{1}'. + + + Type '{0}' returned by IDynamicInterfaceCastable is not an interface. + + + The body of this method was removed by the AOT compiler because it's not callable. + + + The feature associated with this method was removed. + + + The body of this instance method was removed by the AOT compiler. This can happen if the owning type was not seen as allocated by the AOT compiler. + + + Object must be of type Half. + + + Object must be of type Rune. + + + BinaryFormatter serialization and deserialization are disabled within this application. See https://aka.ms/binaryformatter for more information. + + + The argv[0] argument cannot include a double quote. + + + Attempt to update previously set global instance. + + + Use of ResourceManager for custom types is disabled. Set the MSBuild Property CustomResourceTypesSupport to true in order to enable it. + + + COM Interop requires ComWrapper instance registered for marshalling. + + + Queue empty. + + + The target file '{0}' is a directory, not a file. + + + Invalid File or Directory attributes value. + + + Second path fragment must not be a drive or UNC name. + + + Path must not be a drive. + + + The stream's length cannot be changed. + + + The directory specified, '{0}', is not a subdirectory of '{1}'. + + + The specified directory '{0}' cannot be created. + + + The source '{0}' and destination '{1}' are the same file. + + + The specified path '{0}' is not a file. + + + Source and destination path must be different. + + + Source and destination path must have identical roots. Move will not work across volumes. + + + Synchronous operations should not be performed on the UI thread. Consider wrapping this method in Task.Run. + + + Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader. + + + File encryption is not supported on this platform. + + + A MemberInfo that matches '{0}' could not be found. + + + NullabilityInfoContext is not supported in the current application because 'System.Reflection.NullabilityInfoContext.IsSupported' is set to false. Set the MSBuild Property 'NullabilityInfoContextSupport' to true in order to enable it. + + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj new file mode 100644 index 00000000000000..98bc1de6522ca9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -0,0 +1,450 @@ + + + true + + + + + + + + + + + SYSTEM_PRIVATE_CORELIB;FEATURE_MANAGED_ETW_CHANNELS;FEATURE_MANAGED_ETW;$(DefineConstants) + true + true + ..\..\Runtime.Base\src\ + + + + false + true + + + false + true + + + true + true + + + true + $(DefineConstants);FEATURE_GENERIC_MATH + + + + + + Internal\Runtime\RuntimeConstants.cs + + + Internal\Runtime\InteropConstants.cs + + + ExceptionStringID.cs + + + + + + + + + + + + Internal\Runtime\CompilerHelpers\StartupCode\StartupCodeHelpers.cs + + + Internal\Runtime\TypeManagerHandle.cs + + + Internal\Runtime\TypeManagerHandle.Equality.cs + + + Internal\Runtime\CompilerHelpers\StartupCode\StartupDebug.cs + + + Internal\Runtime\ModuleHeaders.cs + + + + + Common\src\Internal\NativeFormat\NativeFormatReader.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interop\Windows\Kernel32\Interop.ExitProcess.cs + + + + + + + System\Runtime\InteropServices\Variant.cs + + + Interop\Windows\Kernel32\Interop.GetCurrentThreadId.cs + + + Interop\Windows\Kernel32\Interop.IsDebuggerPresent.cs + + + Interop\Windows\Kernel32\Interop.RaiseFailFastException.cs + + + Interop\Windows\Ole32\Interop.CoInitializeEx.cs + + + Interop\Windows\Ole32\Interop.CoUninitialize.cs + + + Interop\Windows\Ole32\Interop.CoGetApartmentType.cs + + + Interop\Windows\OleAut32\Interop.VariantClear.cs + + + + + Interop\Windows\Kernel32\Interop.GetTickCount64.cs + + + Interop\Windows\Kernel32\Interop.GetCurrentProcessorNumber.cs + + + Interop\Windows\Kernel32\Interop.ThreadPool.cs + + + + Interop\Windows\Kernel32\Interop.Timer.cs + + + Interop\Windows\Kernel32\Interop.DynamicLoad.cs + + + + + + + + + + + Interop\Windows\Kernel32\Interop.ThreadPoolIO.cs + + + + + + + + + + Interop\Unix\System.Native\Interop.Abort.cs + + + Interop\Unix\System.Native\Interop.Exit.cs + + + Interop\Unix\System.Native\Interop.GetEnv.cs + + + Interop\Unix\System.Native\Interop.SchedGetCpu.cs + + + Interop\Unix\System.Native\Interop.DynamicLoad.cs + + + Interop\Unix\System.Native\Interop.Threading.cs + + + Interop\Unix\System.Native\Interop.GetEnviron.cs + + + + + Utilities\LockFreeReaderHashtable.cs + + + System\Collections\Generic\LowLevelList.cs + + + System\Collections\Generic\LowLevelDictionary.cs + + + System\Collections\Concurrent\ConcurrentUnifier.cs + + + System\Collections\Concurrent\ConcurrentUnifierW.cs + + + System\Collections\Concurrent\ConcurrentUnifierWKeyed.cs + + + System\Collections\Concurrent\IKeyedItem.cs + + + Internal\LowLevelLinq\LowLevelEnumerable.cs + + + Internal\LowLevelLinq\LowLevelEnumerable.ToArray.cs + + + System\Runtime\CompilerServices\__BlockReflectionAttribute.cs + + + Internal\NativeFormat\NativeFormatReader.Primitives.cs + + + Internal\Runtime\CanonTypeKind.cs + + + Internal\Runtime\MethodTable.cs + + + Internal\Runtime\MethodTable.Constants.cs + + + Internal\Runtime\LowLevelStringConverter.cs + + + + + true + + + INPLACE_RUNTIME;$(DefineConstants) + FEATURE_64BIT_ALIGNMENT;$(DefineConstants) + FEATURE_64BIT_ALIGNMENT;$(DefineConstants) + FEATURE_64BIT_ALIGNMENT;$(DefineConstants) + + $(ArtifactsObjDir)\coreclr\$(TargetOS).$(TargetArchitecture).$(CoreCLRConfiguration) + $(IntermediatesDir)\ide + + + + Runtime.Base\src\System\Runtime\CachedInterfaceDispatch.cs + + + Runtime.Base\src\System\Runtime\DispatchResolve.cs + + + Runtime.Base\src\System\Runtime\GCStress.cs + + + Runtime.Base\src\System\Runtime\__Finalizer.cs + + + Runtime.Base\src\System\Runtime\MethodTable.Runtime.cs + + + Runtime.Base\src\System\Runtime\ExceptionHandling.cs + + + Runtime.Base\src\System\Runtime\InternalCalls.cs + + + Runtime.Base\src\System\Runtime\RuntimeExports.cs + + + Runtime.Base\src\System\Runtime\StackFrameIterator.cs + + + Runtime.Base\src\System\Runtime\ThunkPool.cs + + + Runtime.Base\src\System\Runtime\TypeCast.cs + + + Runtime.Base\src\RhBaseName.cs + + + Common\TransitionBlock.cs + + + + + + + + + + + + + + + + + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Activator.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Activator.CoreRT.cs new file mode 100644 index 00000000000000..5b2170110f09cc --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Activator.CoreRT.cs @@ -0,0 +1,143 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Activator is an object that contains the Activation (CreateInstance/New) +// methods for late bound support. +// + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Remoting; +using System.Runtime; + +using Internal.Reflection.Augments; +using Internal.Runtime.CompilerServices; + +namespace System +{ + public static partial class Activator + { + // The following methods and helper class implement the functionality of Activator.CreateInstance() + // The implementation relies on several compiler intrinsics that expand to quick dictionary lookups in shared + // code, and direct constant references in unshared code. + // + // This method is the public surface area. It wraps the CreateInstance intrinsic with the appropriate try/catch + // block so that the correct exceptions are generated. Also, it handles the cases where the T type doesn't have + // a default constructor. + // + // This method is intrinsic. The compiler might replace it with more efficient implementation. + [DebuggerGuidedStepThrough] + [Intrinsic] + public static unsafe T CreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>() + { + // Grab the pointer to the default constructor of the type. If T doesn't have a default + // constructor, the intrinsic returns a marker pointer that we check for. + IntPtr defaultConstructor = DefaultConstructorOf(); + + // Check if we got the marker back. + // + // TODO: might want to disambiguate the different cases for abstract class, interface, etc. + if (defaultConstructor == (IntPtr)(delegate*)&MissingConstructorMethod) + throw new MissingMethodException(SR.Format(SR.MissingConstructor_Name, typeof(T))); + + T t; + try + { + // Call the default constructor on the allocated instance. + if (RuntimeHelpers.IsReference()) + { + // Grab a pointer to the optimized allocator for the type and call it. + IntPtr allocator = AllocatorOf(); + t = RawCalliHelper.Call(allocator, EETypePtr.EETypePtrOf().RawValue); + RawCalliHelper.Call(defaultConstructor, t); + + // Debugger goo so that stepping in works. Only affects debug info generation. + // The call gets optimized away. + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } + else + { + t = default!; + RawCalliHelper.Call(defaultConstructor, ref Unsafe.As(ref t)); + + // Debugger goo so that stepping in works. Only affects debug info generation. + // The call gets optimized away. + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } + + return t; + } + catch (Exception e) + { + throw new TargetInvocationException(e); + } + } + + [Intrinsic] + private static IntPtr DefaultConstructorOf() + { + // Codegens must expand this intrinsic to the pointer to the default constructor of T + // or to a marker that lets us detect there's no default constructor. + // We could implement a fallback with the type loader if we wanted to, but it will be slow and unreliable. + throw new NotSupportedException(); + } + + [Intrinsic] + private static IntPtr AllocatorOf() + { + // Codegens must expand this intrinsic to the pointer to the allocator suitable to allocate an instance of T. + // We could implement a fallback with the type loader if we wanted to, but it will be slow and unreliable. + throw new NotSupportedException(); + } + + internal static unsafe IntPtr GetFallbackDefaultConstructor() + { + return (IntPtr)(delegate*)&MissingConstructorMethod; + } + + // This is a marker method. We return a GUID just to make sure the body is unique + // and under no circumstances gets folded. + private static Guid MissingConstructorMethod() => new Guid(0x68be9718, 0xf787, 0x45ab, 0x84, 0x3b, 0x1f, 0x31, 0xb6, 0x12, 0x65, 0xeb); + // The constructor of this struct is used when there's no constructor + struct StructWithNoConstructor { public StructWithNoConstructor() { } } + + [DebuggerHidden] + [DebuggerStepThrough] + public static object? CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type, bool nonPublic) + => ReflectionAugments.ReflectionCoreCallbacks.ActivatorCreateInstance(type, nonPublic); + + [DebuggerHidden] + [DebuggerStepThrough] + public static object? CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, BindingFlags bindingAttr, Binder? binder, object?[]? args, CultureInfo? culture, object?[]? activationAttributes) + => ReflectionAugments.ReflectionCoreCallbacks.ActivatorCreateInstance(type, bindingAttr, binder, args, culture, activationAttributes); + + [RequiresUnreferencedCode("Type and its constructor could be removed")] + public static ObjectHandle CreateInstance(string assemblyName, string typeName) + { + throw new PlatformNotSupportedException(); // https://github.com/dotnet/corefx/issues/30845 + } + + [RequiresUnreferencedCode("Type and its constructor could be removed")] + public static ObjectHandle CreateInstance(string assemblyName, + string typeName, + bool ignoreCase, + BindingFlags bindingAttr, + Binder? binder, + object[]? args, + CultureInfo? culture, + object[]? activationAttributes) + { + throw new PlatformNotSupportedException(); // https://github.com/dotnet/corefx/issues/30845 + } + + [RequiresUnreferencedCode("Type and its constructor could be removed")] + public static ObjectHandle CreateInstance(string assemblyName, string typeName, object[]? activationAttributes) + { + throw new PlatformNotSupportedException(); // https://github.com/dotnet/corefx/issues/30845 + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/AppContext.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/AppContext.CoreRT.cs new file mode 100644 index 00000000000000..830ae290d5a2c9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/AppContext.CoreRT.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; +using System.Runtime.ExceptionServices; + +namespace System +{ + public static partial class AppContext + { + [RuntimeExport("OnFirstChanceException")] + internal static void OnFirstChanceException(object e) + { + FirstChanceException?.Invoke(/* AppDomain */ null, new FirstChanceExceptionEventArgs((Exception)e)); + } + + [RuntimeExport("OnUnhandledException")] + internal static void OnUnhandledException(object e) + { + UnhandledException?.Invoke(/* AppDomain */ null, new UnhandledExceptionEventArgs(e, true)); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ArgIterator.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ArgIterator.cs new file mode 100644 index 00000000000000..6aa6f39c58a7f2 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ArgIterator.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System +{ + [StructLayout(LayoutKind.Sequential)] + public ref struct ArgIterator + { + public ArgIterator(RuntimeArgumentHandle arglist) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); + } + + [CLSCompliant(false)] + public unsafe ArgIterator(RuntimeArgumentHandle arglist, void* ptr) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); + } + + public void End() + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); + } + + public override bool Equals(object? o) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); + } + + public override int GetHashCode() + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); + } + + [CLSCompliant(false)] + public System.TypedReference GetNextArg() + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); + } + + [CLSCompliant(false)] + public System.TypedReference GetNextArg(System.RuntimeTypeHandle rth) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); + } + + public unsafe System.RuntimeTypeHandle GetNextArgType() + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); + } + + public int GetRemainingCount() + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.CoreRT.cs new file mode 100644 index 00000000000000..9fee9c1bb39d8a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.CoreRT.cs @@ -0,0 +1,1418 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; +using System.Threading; +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; +using Internal.Reflection.Core.NonPortable; +using Internal.IntrinsicSupport; +using MethodTable = Internal.Runtime.MethodTable; +using EETypeElementType = Internal.Runtime.EETypeElementType; + +namespace System +{ + // Note that we make a T[] (single-dimensional w/ zero as the lower bound) implement both + // IList and IReadOnlyList, where T : U dynamically. See the SZArrayHelper class for details. + public abstract partial class Array : ICollection, IEnumerable, IList, IStructuralComparable, IStructuralEquatable, ICloneable + { + // CS0169: The field 'Array._numComponents' is never used +#pragma warning disable 0169 + // This field should be the first field in Array as the runtime/compilers depend on it + [NonSerialized] + private int _numComponents; +#pragma warning restore + +#if TARGET_64BIT + private const int POINTER_SIZE = 8; +#else + private const int POINTER_SIZE = 4; +#endif + // Header + m_pEEType + _numComponents (with an optional padding) + private const int SZARRAY_BASE_SIZE = POINTER_SIZE + POINTER_SIZE + POINTER_SIZE; + + public int Length => checked((int)Unsafe.As(this).Length); + + // This could return a length greater than int.MaxValue + internal nuint NativeLength => Unsafe.As(this).Length; + + public long LongLength => (long)NativeLength; + + internal bool IsSzArray + { + get + { + return this.EETypePtr.BaseSize == SZARRAY_BASE_SIZE; + } + } + + // This is the classlib-provided "get array MethodTable" function that will be invoked whenever the runtime + // needs to know the base type of an array. + [RuntimeExport("GetSystemArrayEEType")] + private static unsafe MethodTable* GetSystemArrayEEType() + { + return EETypePtr.EETypePtrOf().ToPointer(); + } + + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] + public static Array CreateInstance(Type elementType, int length) + { + if (elementType is null) + throw new ArgumentNullException(nameof(elementType)); + + return CreateSzArray(elementType, length); + } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "MDArrays of Rank != 1 can be created because they don't implement generic interfaces.")] + public static unsafe Array CreateInstance(Type elementType, int length1, int length2) + { + if (elementType is null) + throw new ArgumentNullException(nameof(elementType)); + if (length1 < 0) + throw new ArgumentOutOfRangeException(nameof(length1)); + if (length2 < 0) + throw new ArgumentOutOfRangeException(nameof(length2)); + + Type arrayType = GetArrayTypeFromElementType(elementType, true, 2); + int* pLengths = stackalloc int[2]; + pLengths[0] = length1; + pLengths[1] = length2; + return NewMultiDimArray(arrayType.TypeHandle.ToEETypePtr(), pLengths, 2); + } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "MDArrays of Rank != 1 can be created because they don't implement generic interfaces.")] + public static unsafe Array CreateInstance(Type elementType, int length1, int length2, int length3) + { + if (elementType is null) + throw new ArgumentNullException(nameof(elementType)); + if (length1 < 0) + throw new ArgumentOutOfRangeException(nameof(length1)); + if (length2 < 0) + throw new ArgumentOutOfRangeException(nameof(length2)); + if (length3 < 0) + throw new ArgumentOutOfRangeException(nameof(length3)); + + Type arrayType = GetArrayTypeFromElementType(elementType, true, 3); + int* pLengths = stackalloc int[3]; + pLengths[0] = length1; + pLengths[1] = length2; + pLengths[2] = length3; + return NewMultiDimArray(arrayType.TypeHandle.ToEETypePtr(), pLengths, 3); + } + + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] + public static Array CreateInstance(Type elementType, params int[] lengths) + { + if (elementType is null) + throw new ArgumentNullException(nameof(elementType)); + if (lengths is null) + throw new ArgumentNullException(nameof(lengths)); + if (lengths.Length == 0) + throw new ArgumentException(SR.Arg_NeedAtLeast1Rank); + + // Check to make sure the lengths are all positive. Note that we check this here to give + // a good exception message if they are not; however we check this again inside the execution + // engine's low level allocation function after having made a copy of the array to prevent a + // malicious caller from mutating the array after this check. + for (int i = 0; i < lengths.Length; i++) + if (lengths[i] < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.lengths, i, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + + if (lengths.Length == 1) + { + int length = lengths[0]; + return CreateSzArray(elementType, length); + } + else + { + return CreateMultiDimArray(elementType, lengths, null); + } + } + + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] + public static Array CreateInstance(Type elementType, int[] lengths, int[] lowerBounds) + { + if (elementType is null) + throw new ArgumentNullException(nameof(elementType)); + if (lengths is null) + throw new ArgumentNullException(nameof(lengths)); + if (lowerBounds is null) + throw new ArgumentNullException(nameof(lowerBounds)); + if (lengths.Length != lowerBounds.Length) + throw new ArgumentException(SR.Arg_RanksAndBounds); + if (lengths.Length == 0) + throw new ArgumentException(SR.Arg_NeedAtLeast1Rank); + + return CreateMultiDimArray(elementType, lengths, lowerBounds); + } + + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] + private static Array CreateSzArray(Type elementType, int length) + { + // Though our callers already validated length once, this parameter is passed via arrays, so we must check it again + // in case a malicious caller modified the array after the check. + if (length < 0) + throw new ArgumentOutOfRangeException(nameof(length)); + + Type arrayType = GetArrayTypeFromElementType(elementType, false, 1); + return RuntimeImports.RhNewArray(arrayType.TypeHandle.ToEETypePtr(), length); + } + + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] + private static Array CreateMultiDimArray(Type elementType, int[] lengths, int[] lowerBounds) + { + Debug.Assert(lengths != null); + Debug.Assert(lowerBounds == null || lowerBounds.Length == lengths.Length); + + for (int i = 0; i < lengths.Length; i++) + { + if (lengths[i] < 0) + throw new ArgumentOutOfRangeException("lengths[" + i + "]", SR.ArgumentOutOfRange_NeedNonNegNum); + } + + int rank = lengths.Length; + Type arrayType = GetArrayTypeFromElementType(elementType, true, rank); + return RuntimeAugments.NewMultiDimArray(arrayType.TypeHandle, lengths, lowerBounds); + } + + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] + private static Type GetArrayTypeFromElementType(Type elementType, bool multiDim, int rank) + { + elementType = elementType.UnderlyingSystemType; + ValidateElementType(elementType); + + if (multiDim) + return elementType.MakeArrayType(rank); + else + return elementType.MakeArrayType(); + } + + private static void ValidateElementType(Type elementType) + { + if (elementType is not RuntimeType) + throw new ArgumentException(SR.Arg_MustBeType, nameof(elementType)); + while (elementType.IsArray) + { + elementType = elementType.GetElementType()!; + } + if (elementType.IsByRef || elementType.IsByRefLike) + throw new NotSupportedException(SR.NotSupported_ByRefLikeArray); + if (elementType == typeof(void)) + throw new NotSupportedException(SR.NotSupported_VoidArray); + if (elementType.ContainsGenericParameters) + throw new NotSupportedException(SR.NotSupported_OpenType); + } + + public void Initialize() + { + // Project N port note: On the desktop, this api is a nop unless the array element type is a value type with + // an explicit nullary constructor. Such a type cannot be expressed in C# so Project N does not support this. + // The ILC toolchain fails the build if it encounters such a type. + return; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ref int GetRawMultiDimArrayBounds() + { + Debug.Assert(!IsSzArray); + return ref Unsafe.As(ref Unsafe.As(this).Data); + } + + // Provides a strong exception guarantee - either it succeeds, or + // it throws an exception with no side effects. The arrays must be + // compatible array types based on the array element type - this + // method does not support casting, boxing, or primitive widening. + // It will up-cast, assuming the array types are correct. + public static void ConstrainedCopy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + { + CopyImpl(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable: true); + } + + public static void Copy(Array sourceArray, Array destinationArray, int length) + { + if (sourceArray is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.sourceArray); + if (destinationArray is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destinationArray); + + EETypePtr eeType = sourceArray.EETypePtr; + if (eeType.FastEquals(destinationArray.EETypePtr) && + eeType.IsSzArray && + (uint)length <= sourceArray.NativeLength && + (uint)length <= destinationArray.NativeLength) + { + nuint byteCount = (uint)length * (nuint)eeType.ComponentSize; + ref byte src = ref Unsafe.As(sourceArray).Data; + ref byte dst = ref Unsafe.As(destinationArray).Data; + + if (eeType.HasPointers) + Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount); + else + Buffer.Memmove(ref dst, ref src, byteCount); + + // GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray + return; + } + + // Less common + CopyImpl(sourceArray, sourceArray.GetLowerBound(0), destinationArray, destinationArray.GetLowerBound(0), length, reliable: false); + } + + public static unsafe void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + { + if (sourceArray != null && destinationArray != null) + { + EETypePtr eeType = sourceArray.EETypePtr; + if (eeType.FastEquals(destinationArray.EETypePtr) && + eeType.IsSzArray && + length >= 0 && sourceIndex >= 0 && destinationIndex >= 0 && + (uint)(sourceIndex + length) <= sourceArray.NativeLength && + (uint)(destinationIndex + length) <= destinationArray.NativeLength) + { + nuint elementSize = (nuint)eeType.ComponentSize; + nuint byteCount = (uint)length * elementSize; + ref byte src = ref Unsafe.AddByteOffset(ref Unsafe.As(sourceArray).Data, (uint)sourceIndex * elementSize); + ref byte dst = ref Unsafe.AddByteOffset(ref Unsafe.As(destinationArray).Data, (uint)destinationIndex * elementSize); + + if (eeType.HasPointers) + Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount); + else + Buffer.Memmove(ref dst, ref src, byteCount); + + // GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray + return; + } + } + + // Less common + CopyImpl(sourceArray!, sourceIndex, destinationArray!, destinationIndex, length, reliable: false); + } + + // + // Funnel for all the Array.Copy() overloads. The "reliable" parameter indicates whether the caller for ConstrainedCopy() + // (must leave destination array unchanged on any exception.) + // + private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) + { + if (sourceArray is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.sourceArray); + if (destinationArray is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destinationArray); + + if (sourceArray.GetType() != destinationArray.GetType() && sourceArray.Rank != destinationArray.Rank) + throw new RankException(SR.Rank_MustMatch); + + if (length < 0) + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); + + const int srcLB = 0; + if (sourceIndex < srcLB || sourceIndex - srcLB < 0) + throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_ArrayLB); + sourceIndex -= srcLB; + + const int dstLB = 0; + if (destinationIndex < dstLB || destinationIndex - dstLB < 0) + throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_ArrayLB); + destinationIndex -= dstLB; + + if ((uint)(sourceIndex + length) > sourceArray.NativeLength) + throw new ArgumentException(SR.Arg_LongerThanSrcArray, nameof(sourceArray)); + if ((uint)(destinationIndex + length) > destinationArray.NativeLength) + throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); + + EETypePtr sourceElementEEType = sourceArray.ElementEEType; + EETypePtr destinationElementEEType = destinationArray.ElementEEType; + + if (!destinationElementEEType.IsValueType && !destinationElementEEType.IsPointer) + { + if (!sourceElementEEType.IsValueType && !sourceElementEEType.IsPointer) + { + CopyImplGcRefArray(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable); + } + else if (RuntimeImports.AreTypesAssignable(sourceElementEEType, destinationElementEEType)) + { + CopyImplValueTypeArrayToReferenceArray(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable); + } + else + { + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + } + else + { + if (RuntimeImports.AreTypesEquivalent(sourceElementEEType, destinationElementEEType)) + { + if (sourceElementEEType.HasPointers) + { + CopyImplValueTypeArrayWithInnerGcRefs(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable); + } + else + { + CopyImplValueTypeArrayNoInnerGcRefs(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + } + } + else if (sourceElementEEType.IsPointer && destinationElementEEType.IsPointer) + { + // CLR compat note: CLR only allows Array.Copy between pointee types that would be assignable + // to using array covariance rules (so int*[] can be copied to uint*[], but not to float*[]). + // This is rather weird since e.g. we don't allow casting int*[] to uint*[] otherwise. + // Instead of trying to replicate the behavior, we're choosing to be simply more permissive here. + CopyImplValueTypeArrayNoInnerGcRefs(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + } + else if (IsSourceElementABaseClassOrInterfaceOfDestinationValueType(sourceElementEEType, destinationElementEEType)) + { + CopyImplReferenceArrayToValueTypeArray(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable); + } + else if (sourceElementEEType.IsPrimitive && destinationElementEEType.IsPrimitive) + { + if (RuntimeImports.AreTypesAssignable(sourceArray.EETypePtr, destinationArray.EETypePtr)) + { + // If we're okay casting between these two, we're also okay blitting the values over + CopyImplValueTypeArrayNoInnerGcRefs(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + } + else + { + // The only case remaining is that primitive types could have a widening conversion between the source element type and the destination + // If a widening conversion does not exist we are going to throw an ArrayTypeMismatchException from it. + CopyImplPrimitiveTypeWithWidening(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable); + } + } + else + { + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + } + } + + private static bool IsSourceElementABaseClassOrInterfaceOfDestinationValueType(EETypePtr sourceElementEEType, EETypePtr destinationElementEEType) + { + if (sourceElementEEType.IsValueType || sourceElementEEType.IsPointer) + return false; + + // It may look like we're passing the arguments to AreTypesAssignable in the wrong order but we're not. The source array is an interface or Object array, the destination + // array is a value type array. Our job is to check if the destination value type implements the interface - which is what this call to AreTypesAssignable does. + // The copy loop still checks each element to make sure it actually is the correct valuetype. + if (!RuntimeImports.AreTypesAssignable(destinationElementEEType, sourceElementEEType)) + return false; + return true; + } + + // + // Array.CopyImpl case: Gc-ref array to gc-ref array copy. + // + private static unsafe void CopyImplGcRefArray(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) + { + // For mismatched array types, the desktop Array.Copy has a policy that determines whether to throw an ArrayTypeMismatch without any attempt to copy + // or to throw an InvalidCastException in the middle of a copy. This code replicates that policy. + EETypePtr sourceElementEEType = sourceArray.ElementEEType; + EETypePtr destinationElementEEType = destinationArray.ElementEEType; + + Debug.Assert(!sourceElementEEType.IsValueType && !sourceElementEEType.IsPointer); + Debug.Assert(!destinationElementEEType.IsValueType && !destinationElementEEType.IsPointer); + + bool attemptCopy = RuntimeImports.AreTypesAssignable(sourceElementEEType, destinationElementEEType); + bool mustCastCheckEachElement = !attemptCopy; + if (reliable) + { + if (mustCastCheckEachElement) + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy); + } + else + { + attemptCopy = attemptCopy || RuntimeImports.AreTypesAssignable(destinationElementEEType, sourceElementEEType); + + // If either array is an interface array, we allow the attempt to copy even if the other element type does not statically implement the interface. + // We don't have an "IsInterface" property in EETypePtr so we instead check for a null BaseType. The only the other MethodTable with a null BaseType is + // System.Object but if that were the case, we would already have passed one of the AreTypesAssignable checks above. + attemptCopy = attemptCopy || sourceElementEEType.BaseType.IsNull; + attemptCopy = attemptCopy || destinationElementEEType.BaseType.IsNull; + + if (!attemptCopy) + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + + bool reverseCopy = ((object)sourceArray == (object)destinationArray) && (sourceIndex < destinationIndex); + ref object? refDestinationArray = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(destinationArray)); + ref object? refSourceArray = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(sourceArray)); + if (reverseCopy) + { + sourceIndex += length - 1; + destinationIndex += length - 1; + for (int i = 0; i < length; i++) + { + object? value = Unsafe.Add(ref refSourceArray, sourceIndex - i); + if (mustCastCheckEachElement && value != null && RuntimeImports.IsInstanceOf(destinationElementEEType, value) == null) + throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); + Unsafe.Add(ref refDestinationArray, destinationIndex - i) = value; + } + } + else + { + for (int i = 0; i < length; i++) + { + object? value = Unsafe.Add(ref refSourceArray, sourceIndex + i); + if (mustCastCheckEachElement && value != null && RuntimeImports.IsInstanceOf(destinationElementEEType, value) == null) + throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); + Unsafe.Add(ref refDestinationArray, destinationIndex + i) = value; + } + } + } + + // + // Array.CopyImpl case: Value-type array to Object[] or interface array copy. + // + private static unsafe void CopyImplValueTypeArrayToReferenceArray(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) + { + Debug.Assert(sourceArray.ElementEEType.IsValueType || sourceArray.ElementEEType.IsPointer); + Debug.Assert(!destinationArray.ElementEEType.IsValueType && !destinationArray.ElementEEType.IsPointer); + + // Caller has already validated this. + Debug.Assert(RuntimeImports.AreTypesAssignable(sourceArray.ElementEEType, destinationArray.ElementEEType)); + + if (reliable) + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy); + + EETypePtr sourceElementEEType = sourceArray.ElementEEType; + nuint sourceElementSize = sourceArray.ElementSize; + + fixed (byte* pSourceArray = &MemoryMarshal.GetArrayDataReference(sourceArray)) + { + byte* pElement = pSourceArray + (nuint)sourceIndex * sourceElementSize; + ref object refDestinationArray = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(destinationArray)); + for (int i = 0; i < length; i++) + { + object boxedValue = RuntimeImports.RhBox(sourceElementEEType, ref *pElement); + Unsafe.Add(ref refDestinationArray, destinationIndex + i) = boxedValue; + pElement += sourceElementSize; + } + } + } + + // + // Array.CopyImpl case: Object[] or interface array to value-type array copy. + // + private static unsafe void CopyImplReferenceArrayToValueTypeArray(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) + { + Debug.Assert(!sourceArray.ElementEEType.IsValueType && !sourceArray.ElementEEType.IsPointer); + Debug.Assert(destinationArray.ElementEEType.IsValueType || destinationArray.ElementEEType.IsPointer); + + if (reliable) + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + + EETypePtr destinationElementEEType = destinationArray.ElementEEType; + nuint destinationElementSize = destinationArray.ElementSize; + bool isNullable = destinationElementEEType.IsNullable; + + fixed (byte* pDestinationArray = &MemoryMarshal.GetArrayDataReference(destinationArray)) + { + ref object refSourceArray = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(sourceArray)); + byte* pElement = pDestinationArray + (nuint)destinationIndex * destinationElementSize; + + for (int i = 0; i < length; i++) + { + object boxedValue = Unsafe.Add(ref refSourceArray, sourceIndex + i); + if (boxedValue == null) + { + if (!isNullable) + throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); + } + else + { + EETypePtr eeType = boxedValue.EETypePtr; + if (!(RuntimeImports.AreTypesAssignable(eeType, destinationElementEEType))) + throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); + } + + RuntimeImports.RhUnbox(boxedValue, ref *pElement, destinationElementEEType); + pElement += destinationElementSize; + } + } + } + + + // + // Array.CopyImpl case: Value-type array with embedded gc-references. + // + private static unsafe void CopyImplValueTypeArrayWithInnerGcRefs(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) + { + Debug.Assert(RuntimeImports.AreTypesEquivalent(sourceArray.EETypePtr, destinationArray.EETypePtr)); + Debug.Assert(sourceArray.ElementEEType.IsValueType); + + EETypePtr sourceElementEEType = sourceArray.EETypePtr.ArrayElementType; + bool reverseCopy = ((object)sourceArray == (object)destinationArray) && (sourceIndex < destinationIndex); + + // Copy scenario: ValueType-array to value-type array with embedded gc-refs. + object[]? boxedElements = null; + if (reliable) + { + boxedElements = new object[length]; + reverseCopy = false; + } + + fixed (byte* pDstArray = &MemoryMarshal.GetArrayDataReference(destinationArray), pSrcArray = &MemoryMarshal.GetArrayDataReference(sourceArray)) + { + nuint cbElementSize = sourceArray.ElementSize; + byte* pSourceElement = pSrcArray + (nuint)sourceIndex * cbElementSize; + byte* pDestinationElement = pDstArray + (nuint)destinationIndex * cbElementSize; + if (reverseCopy) + { + pSourceElement += (nuint)length * cbElementSize; + pDestinationElement += (nuint)length * cbElementSize; + } + + for (int i = 0; i < length; i++) + { + if (reverseCopy) + { + pSourceElement -= cbElementSize; + pDestinationElement -= cbElementSize; + } + + object boxedValue = RuntimeImports.RhBox(sourceElementEEType, ref *pSourceElement); + if (boxedElements != null) + boxedElements[i] = boxedValue; + else + RuntimeImports.RhUnbox(boxedValue, ref *pDestinationElement, sourceElementEEType); + + if (!reverseCopy) + { + pSourceElement += cbElementSize; + pDestinationElement += cbElementSize; + } + } + } + + if (boxedElements != null) + { + fixed (byte* pDstArray = &MemoryMarshal.GetArrayDataReference(destinationArray)) + { + nuint cbElementSize = sourceArray.ElementSize; + byte* pDestinationElement = pDstArray + (nuint)destinationIndex * cbElementSize; + for (int i = 0; i < length; i++) + { + RuntimeImports.RhUnbox(boxedElements[i], ref *pDestinationElement, sourceElementEEType); + pDestinationElement += cbElementSize; + } + } + } + } + + // + // Array.CopyImpl case: Value-type array without embedded gc-references. + // + private static unsafe void CopyImplValueTypeArrayNoInnerGcRefs(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + { + Debug.Assert((sourceArray.ElementEEType.IsValueType && !sourceArray.ElementEEType.HasPointers) || + sourceArray.ElementEEType.IsPointer); + Debug.Assert((destinationArray.ElementEEType.IsValueType && !destinationArray.ElementEEType.HasPointers) || + destinationArray.ElementEEType.IsPointer); + + // Copy scenario: ValueType-array to value-type array with no embedded gc-refs. + nuint elementSize = sourceArray.ElementSize; + + Buffer.Memmove( + ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(destinationArray), (nuint)destinationIndex * elementSize), + ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(sourceArray), (nuint)sourceIndex * elementSize), + elementSize * (nuint)length); + } + + // + // Array.CopyImpl case: Primitive types that have a widening conversion + // + private static unsafe void CopyImplPrimitiveTypeWithWidening(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) + { + EETypePtr sourceElementEEType = sourceArray.ElementEEType; + EETypePtr destinationElementEEType = destinationArray.ElementEEType; + + Debug.Assert(sourceElementEEType.IsPrimitive && destinationElementEEType.IsPrimitive); // Caller has already validated this. + + EETypeElementType sourceElementType = sourceElementEEType.ElementType; + EETypeElementType destElementType = destinationElementEEType.ElementType; + + nuint srcElementSize = sourceArray.ElementSize; + nuint destElementSize = destinationArray.ElementSize; + + if ((sourceElementEEType.IsEnum || destinationElementEEType.IsEnum) && sourceElementType != destElementType) + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + + if (reliable) + { + // ContrainedCopy() cannot even widen - it can only copy same type or enum to its exact integral subtype. + if (sourceElementType != destElementType) + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy); + } + + fixed (byte* pSrcArray = &MemoryMarshal.GetArrayDataReference(sourceArray), pDstArray = &MemoryMarshal.GetArrayDataReference(destinationArray)) + { + byte* srcData = pSrcArray + (nuint)sourceIndex * srcElementSize; + byte* data = pDstArray + (nuint)destinationIndex * destElementSize; + + if (sourceElementType == destElementType) + { + // Multidim arrays and enum->int copies can still reach this path. + Buffer.Memmove(ref *data, ref *srcData, (nuint)length * srcElementSize); + return; + } + + ulong dummyElementForZeroLengthCopies = 0; + // If the element types aren't identical and the length is zero, we're still obliged to check the types for widening compatibility. + // We do this by forcing the loop below to copy one dummy element. + if (length == 0) + { + srcData = (byte*)&dummyElementForZeroLengthCopies; + data = (byte*)&dummyElementForZeroLengthCopies; + length = 1; + } + + for (int i = 0; i < length; i++, srcData += srcElementSize, data += destElementSize) + { + // We pretty much have to do some fancy datatype mangling every time here, for + // converting w/ sign extension and floating point conversions. + switch (sourceElementType) + { + case EETypeElementType.Byte: + { + switch (destElementType) + { + case EETypeElementType.Single: + *(float*)data = *(byte*)srcData; + break; + + case EETypeElementType.Double: + *(double*)data = *(byte*)srcData; + break; + + case EETypeElementType.Char: + case EETypeElementType.Int16: + case EETypeElementType.UInt16: + *(short*)data = *(byte*)srcData; + break; + + case EETypeElementType.Int32: + case EETypeElementType.UInt32: + *(int*)data = *(byte*)srcData; + break; + + case EETypeElementType.Int64: + case EETypeElementType.UInt64: + *(long*)data = *(byte*)srcData; + break; + + default: + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + break; + } + + case EETypeElementType.SByte: + switch (destElementType) + { + case EETypeElementType.Int16: + *(short*)data = *(sbyte*)srcData; + break; + + case EETypeElementType.Int32: + *(int*)data = *(sbyte*)srcData; + break; + + case EETypeElementType.Int64: + *(long*)data = *(sbyte*)srcData; + break; + + case EETypeElementType.Single: + *(float*)data = *(sbyte*)srcData; + break; + + case EETypeElementType.Double: + *(double*)data = *(sbyte*)srcData; + break; + + default: + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + break; + + case EETypeElementType.UInt16: + case EETypeElementType.Char: + switch (destElementType) + { + case EETypeElementType.Single: + *(float*)data = *(ushort*)srcData; + break; + + case EETypeElementType.Double: + *(double*)data = *(ushort*)srcData; + break; + + case EETypeElementType.UInt16: + case EETypeElementType.Char: + *(ushort*)data = *(ushort*)srcData; + break; + + case EETypeElementType.Int32: + case EETypeElementType.UInt32: + *(uint*)data = *(ushort*)srcData; + break; + + case EETypeElementType.Int64: + case EETypeElementType.UInt64: + *(ulong*)data = *(ushort*)srcData; + break; + + default: + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + break; + + case EETypeElementType.Int16: + switch (destElementType) + { + case EETypeElementType.Int32: + *(int*)data = *(short*)srcData; + break; + + case EETypeElementType.Int64: + *(long*)data = *(short*)srcData; + break; + + case EETypeElementType.Single: + *(float*)data = *(short*)srcData; + break; + + case EETypeElementType.Double: + *(double*)data = *(short*)srcData; + break; + + default: + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + break; + + case EETypeElementType.Int32: + switch (destElementType) + { + case EETypeElementType.Int64: + *(long*)data = *(int*)srcData; + break; + + case EETypeElementType.Single: + *(float*)data = (float)*(int*)srcData; + break; + + case EETypeElementType.Double: + *(double*)data = *(int*)srcData; + break; + + default: + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + break; + + case EETypeElementType.UInt32: + switch (destElementType) + { + case EETypeElementType.Int64: + case EETypeElementType.UInt64: + *(long*)data = *(uint*)srcData; + break; + + case EETypeElementType.Single: + *(float*)data = (float)*(uint*)srcData; + break; + + case EETypeElementType.Double: + *(double*)data = *(uint*)srcData; + break; + + default: + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + break; + + + case EETypeElementType.Int64: + switch (destElementType) + { + case EETypeElementType.Single: + *(float*)data = (float)*(long*)srcData; + break; + + case EETypeElementType.Double: + *(double*)data = (double)*(long*)srcData; + break; + + default: + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + break; + + case EETypeElementType.UInt64: + switch (destElementType) + { + case EETypeElementType.Single: + + //*(float*) data = (float) *(Ulong*)srcData; + long srcValToFloat = *(long*)srcData; + float f = (float)srcValToFloat; + if (srcValToFloat < 0) + f += 4294967296.0f * 4294967296.0f; // This is 2^64 + + *(float*)data = f; + break; + + case EETypeElementType.Double: + //*(double*) data = (double) *(Ulong*)srcData; + long srcValToDouble = *(long*)srcData; + double d = (double)srcValToDouble; + if (srcValToDouble < 0) + d += 4294967296.0 * 4294967296.0; // This is 2^64 + + *(double*)data = d; + break; + + default: + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + break; + + case EETypeElementType.Single: + switch (destElementType) + { + case EETypeElementType.Double: + *(double*)data = *(float*)srcData; + break; + + default: + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + break; + + default: + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + } + } + } + + public static unsafe void Clear(Array array) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + EETypePtr eeType = array.EETypePtr; + nuint totalByteLength = eeType.ComponentSize * array.NativeLength; + ref byte pStart = ref MemoryMarshal.GetArrayDataReference(array); + + if (!eeType.HasPointers) + { + SpanHelpers.ClearWithoutReferences(ref pStart, totalByteLength); + } + else + { + Debug.Assert(totalByteLength % (nuint)sizeof(IntPtr) == 0); + SpanHelpers.ClearWithReferences(ref Unsafe.As(ref pStart), totalByteLength / (nuint)sizeof(IntPtr)); + } + } + + public static unsafe void Clear(Array array, int index, int length) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + ref byte p = ref Unsafe.As(array).Data; + int lowerBound = 0; + + EETypePtr eeType = array.EETypePtr; + if (!eeType.IsSzArray) + { + int rank = eeType.ArrayRank; + lowerBound = Unsafe.Add(ref Unsafe.As(ref p), rank); + p = ref Unsafe.Add(ref p, 2 * sizeof(int) * rank); // skip the bounds + } + + int offset = index - lowerBound; + + if (index < lowerBound || offset < 0 || length < 0 || (uint)(offset + length) > array.NativeLength) + ThrowHelper.ThrowIndexOutOfRangeException(); + + nuint elementSize = eeType.ComponentSize; + + ref byte ptr = ref Unsafe.AddByteOffset(ref p, (uint)offset * elementSize); + nuint byteLength = (uint)length * elementSize; + + if (eeType.HasPointers) + { + Debug.Assert(byteLength % (nuint)sizeof(IntPtr) == 0); + SpanHelpers.ClearWithReferences(ref Unsafe.As(ref ptr), byteLength / (uint)sizeof(IntPtr)); + } + else + { + SpanHelpers.ClearWithoutReferences(ref ptr, byteLength); + } + + // GC.KeepAlive(array) not required. pMT kept alive via `ptr` + } + + [Intrinsic] + public int GetLength(int dimension) + { + int length = GetUpperBound(dimension) + 1; + // We don't support non-zero lower bounds so don't incur the cost of obtaining it. + Debug.Assert(GetLowerBound(dimension) == 0); + return length; + } + + public int Rank + { + get + { + return this.EETypePtr.ArrayRank; + } + } + + // Allocate new multidimensional array of given dimensions. Assumes that that pLengths is immutable. + internal static unsafe Array NewMultiDimArray(EETypePtr eeType, int* pLengths, int rank) + { + Debug.Assert(eeType.IsArray && !eeType.IsSzArray); + Debug.Assert(rank == eeType.ArrayRank); + + // Code below assumes 0 lower bounds. MdArray of rank 1 with zero lower bounds should never be allocated. + // The runtime always allocates an SzArray for those: + // * newobj instance void int32[0...]::.ctor(int32)" actually gives you int[] + // * int[] is castable to int[*] to make it mostly transparent + // The callers need to check for this. + Debug.Assert(rank != 1); + + ulong totalLength = 1; + bool maxArrayDimensionLengthOverflow = false; + + for (int i = 0; i < rank; i++) + { + int length = pLengths[i]; + if (length < 0) + throw new OverflowException(); + if (length > MaxLength) + maxArrayDimensionLengthOverflow = true; + totalLength = totalLength * (ulong)length; + if (totalLength > int.MaxValue) + throw new OutOfMemoryException(); // "Array dimensions exceeded supported range." + } + + // Throw this exception only after everything else was validated for backward compatibility. + if (maxArrayDimensionLengthOverflow) + throw new OutOfMemoryException(); // "Array dimensions exceeded supported range." + + Array ret = RuntimeImports.RhNewArray(eeType, (int)totalLength); + + ref int bounds = ref ret.GetRawMultiDimArrayBounds(); + for (int i = 0; i < rank; i++) + { + Unsafe.Add(ref bounds, i) = pLengths[i]; + } + + return ret; + } + + [Intrinsic] + public int GetLowerBound(int dimension) + { + if (!IsSzArray) + { + int rank = Rank; + if ((uint)dimension >= rank) + throw new IndexOutOfRangeException(); + + return Unsafe.Add(ref GetRawMultiDimArrayBounds(), rank + dimension); + } + + if (dimension != 0) + throw new IndexOutOfRangeException(); + return 0; + } + + [Intrinsic] + public int GetUpperBound(int dimension) + { + if (!IsSzArray) + { + int rank = Rank; + if ((uint)dimension >= rank) + throw new IndexOutOfRangeException(); + + ref int bounds = ref GetRawMultiDimArrayBounds(); + + int length = Unsafe.Add(ref bounds, dimension); + int lowerBound = Unsafe.Add(ref bounds, rank + dimension); + return length + lowerBound - 1; + } + + if (dimension != 0) + throw new IndexOutOfRangeException(); + return Length - 1; + } + + private unsafe nint GetFlattenedIndex(ReadOnlySpan indices) + { + // Checked by the caller + Debug.Assert(indices.Length == Rank); + + if (!IsSzArray) + { + ref int bounds = ref GetRawMultiDimArrayBounds(); + nint flattenedIndex = 0; + for (int i = 0; i < indices.Length; i++) + { + int index = indices[i] - Unsafe.Add(ref bounds, indices.Length + i); + int length = Unsafe.Add(ref bounds, i); + if ((uint)index >= (uint)length) + ThrowHelper.ThrowIndexOutOfRangeException(); + flattenedIndex = (length * flattenedIndex) + index; + } + Debug.Assert((nuint)flattenedIndex < NativeLength); + return flattenedIndex; + } + else + { + int index = indices[0]; + if ((uint)index >= NativeLength) + ThrowHelper.ThrowIndexOutOfRangeException(); + return index; + } + } + + internal object? InternalGetValue(nint flattenedIndex) + { + Debug.Assert((nuint)flattenedIndex < NativeLength); + + if (ElementEEType.IsPointer) + throw new NotSupportedException(SR.NotSupported_Type); + + ref byte element = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(this), (nuint)flattenedIndex * ElementSize); + + EETypePtr pElementEEType = ElementEEType; + if (pElementEEType.IsValueType) + { + return RuntimeImports.RhBox(pElementEEType, ref element); + } + else + { + Debug.Assert(!pElementEEType.IsPointer); + return Unsafe.As(ref element); + } + } + + private unsafe void InternalSetValue(object? value, nint flattenedIndex) + { + Debug.Assert((nuint)flattenedIndex < NativeLength); + + if (ElementEEType.IsPointer) + throw new NotSupportedException(SR.NotSupported_Type); + + ref byte element = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(this), (nuint)flattenedIndex * ElementSize); + + EETypePtr pElementEEType = ElementEEType; + if (pElementEEType.IsValueType) + { + // Unlike most callers of InvokeUtils.ChangeType(), Array.SetValue() does *not* permit conversion from a primitive to an Enum. + if (value != null && !(value.EETypePtr == pElementEEType) && pElementEEType.IsEnum) + throw new InvalidCastException(SR.Format(SR.Arg_ObjObjEx, value.GetType(), Type.GetTypeFromHandle(new RuntimeTypeHandle(pElementEEType)))); + + value = InvokeUtils.CheckArgument(value, pElementEEType, InvokeUtils.CheckArgumentSemantics.ArraySet, binderBundle: null); + Debug.Assert(value == null || RuntimeImports.AreTypesAssignable(value.EETypePtr, pElementEEType)); + + RuntimeImports.RhUnbox(value, ref element, pElementEEType); + } + else if (pElementEEType.IsPointer) + { + throw new NotSupportedException(SR.NotSupported_Type); + } + else + { + try + { + RuntimeImports.RhCheckArrayStore(this, value); + } + catch (ArrayTypeMismatchException) + { + throw new InvalidCastException(SR.InvalidCast_StoreArrayElement); + } + Unsafe.As(ref element) = value; + } + } + + internal EETypePtr ElementEEType + { + get + { + return this.EETypePtr.ArrayElementType; + } + } + + internal CorElementType GetCorElementTypeOfElementType() + { + return ElementEEType.CorElementType; + } + + internal bool IsValueOfElementType(object o) + { + return ElementEEType.Equals(o.EETypePtr); + } + + // + // Return storage size of an individual element in bytes. + // + internal nuint ElementSize + { + get + { + return EETypePtr.ComponentSize; + } + } + + private static int IndexOfImpl(T[] array, T value, int startIndex, int count) + { + // See comment in EqualityComparerHelpers.GetComparerForReferenceTypesOnly for details + EqualityComparer comparer = EqualityComparerHelpers.GetComparerForReferenceTypesOnly(); + + int endIndex = startIndex + count; + if (comparer != null) + { + for (int i = startIndex; i < endIndex; i++) + { + if (comparer.Equals(array[i], value)) + return i; + } + } + else + { + for (int i = startIndex; i < endIndex; i++) + { + if (EqualityComparerHelpers.StructOnlyEquals(array[i], value)) + return i; + } + } + + return -1; + } + + private static int LastIndexOfImpl(T[] array, T value, int startIndex, int count) + { + // See comment in EqualityComparerHelpers.GetComparerForReferenceTypesOnly for details + EqualityComparer comparer = EqualityComparerHelpers.GetComparerForReferenceTypesOnly(); + + int endIndex = startIndex - count + 1; + if (comparer != null) + { + for (int i = startIndex; i >= endIndex; i--) + { + if (comparer.Equals(array[i], value)) + return i; + } + } + else + { + for (int i = startIndex; i >= endIndex; i--) + { + if (EqualityComparerHelpers.StructOnlyEquals(array[i], value)) + return i; + } + } + + return -1; + } + } + + internal class ArrayEnumeratorBase : ICloneable + { + protected int _index; + protected int _endIndex; + + internal ArrayEnumeratorBase() + { + _index = -1; + } + + public bool MoveNext() + { + if (_index < _endIndex) + { + _index++; + return (_index < _endIndex); + } + return false; + } + + public void Dispose() + { + } + + public object Clone() + { + return MemberwiseClone(); + } + } + + // + // Note: the declared base type and interface list also determines what Reflection returns from TypeInfo.BaseType and TypeInfo.ImplementedInterfaces for array types. + // This also means the class must be declared "public" so that the framework can reflect on it. + // + public class Array : Array, IEnumerable, ICollection, IList, IReadOnlyList + { + // Prevent the C# compiler from generating a public default constructor + private Array() { } + + public new IEnumerator GetEnumerator() + { + // get length so we don't have to call the Length property again in ArrayEnumerator constructor + // and avoid more checking there too. + int length = this.Length; + return length == 0 ? ArrayEnumerator.Empty : new ArrayEnumerator(Unsafe.As(this), length); + } + + public int Count + { + get + { + return this.Length; + } + } + + // + // Fun fact: + // + // ((int[])a).IsReadOnly returns false. + // ((IList)a).IsReadOnly returns true. + // + public new bool IsReadOnly + { + get + { + return true; + } + } + + public void Add(T item) + { + ThrowHelper.ThrowNotSupportedException(); + } + + public void Clear() + { + ThrowHelper.ThrowNotSupportedException(); + } + + public bool Contains(T item) + { + T[] array = Unsafe.As(this); + return Array.IndexOf(array, item, 0, array.Length) >= 0; + } + + public void CopyTo(T[] array, int arrayIndex) + { + Array.Copy(Unsafe.As(this), 0, array, arrayIndex, this.Length); + } + + public bool Remove(T item) + { + ThrowHelper.ThrowNotSupportedException(); + return false; // unreachable + } + + public T this[int index] + { + get + { + try + { + return Unsafe.As(this)[index]; + } + catch (IndexOutOfRangeException) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + return default; // unreachable + } + } + set + { + try + { + Unsafe.As(this)[index] = value; + } + catch (IndexOutOfRangeException) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + } + } + + public int IndexOf(T item) + { + T[] array = Unsafe.As(this); + return Array.IndexOf(array, item, 0, array.Length); + } + + public void Insert(int index, T item) + { + ThrowHelper.ThrowNotSupportedException(); + } + + public void RemoveAt(int index) + { + ThrowHelper.ThrowNotSupportedException(); + } + + private sealed class ArrayEnumerator : ArrayEnumeratorBase, IEnumerator + { + private readonly T[] _array; + + // Passing -1 for endIndex so that MoveNext always returns false without mutating _index + internal static readonly ArrayEnumerator Empty = new ArrayEnumerator(null, -1); + + internal ArrayEnumerator(T[] array, int endIndex) + { + _array = array; + _endIndex = endIndex; + } + + public T Current + { + get + { + if ((uint)_index >= (uint)_endIndex) + ThrowHelper.ThrowInvalidOperationException(); + return _array[_index]; + } + } + + object IEnumerator.Current + { + get + { + return Current; + } + } + + void IEnumerator.Reset() + { + _index = -1; + } + } + } + + public class MDArray + { + public const int MinRank = 1; + public const int MaxRank = 32; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Buffer.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Buffer.CoreRT.cs new file mode 100644 index 00000000000000..1cd5d7c97c719e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Buffer.CoreRT.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; + +namespace System +{ + public static partial class Buffer + { + // Non-inlinable wrapper around the QCall that avoids polluting the fast path + // with P/Invoke prolog/epilog. + [MethodImpl(MethodImplOptions.NoInlining)] + internal static unsafe void _ZeroMemory(ref byte b, nuint byteLength) + { + fixed (byte* bytePointer = &b) + { + RuntimeImports.memset(bytePointer, 0, byteLength); + } + } + + internal static void BulkMoveWithWriteBarrier(ref byte dmem, ref byte smem, nuint size) + => RuntimeImports.RhBulkMoveWithWriteBarrier(ref dmem, ref smem, size); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void __Memmove(byte* dest, byte* src, nuint len) => + RuntimeImports.memmove(dest, src, len); + + // This method has different signature for x64 and other platforms and is done for performance reasons. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe void Memmove(ref T destination, ref T source, nuint elementCount) + { + if (!RuntimeHelpers.IsReferenceOrContainsReferences()) + { + // Blittable memmove + + Memmove( + ref Unsafe.As(ref destination), + ref Unsafe.As(ref source), + elementCount * (nuint)Unsafe.SizeOf()); + } + else + { + // Non-blittable memmove + RuntimeImports.RhBulkMoveWithWriteBarrier( + ref Unsafe.As(ref destination), + ref Unsafe.As(ref source), + elementCount * (nuint)Unsafe.SizeOf()); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreRT.cs new file mode 100644 index 00000000000000..682cf4e75c5059 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreRT.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Diagnostics; +using System.Runtime.Versioning; + +namespace System.Collections.Generic +{ + internal partial class ArraySortHelper + { + private static readonly ArraySortHelper s_defaultArraySortHelper = new ArraySortHelper(); + + public static ArraySortHelper Default + { + get + { + return s_defaultArraySortHelper; + } + } + } + + internal partial class ArraySortHelper + { + private static readonly ArraySortHelper s_defaultArraySortHelper = new ArraySortHelper(); + + public static ArraySortHelper Default + { + get + { + return s_defaultArraySortHelper; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/Comparer.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/Comparer.CoreRT.cs new file mode 100644 index 00000000000000..091b5a18fd57c1 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/Comparer.CoreRT.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable +using System.Runtime.CompilerServices; +using System.Threading; + +using Internal.IntrinsicSupport; +using Internal.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + public abstract partial class Comparer : IComparer, IComparer + { + private static Comparer s_default; + + // The AOT compiler can flip this to false under certain circumstances. + private static bool SupportsGenericIComparableInterfaces => true; + + [Intrinsic] + private static Comparer Create() + { + // The compiler will overwrite the Create method with optimized + // instantiation-specific implementation. + // This body serves as a fallback when instantiation-specific implementation is unavailable. + // If that happens, the compiler ensures we generate data structures to make the fallback work + // when this method is compiled. + Interlocked.CompareExchange(ref s_default, + SupportsGenericIComparableInterfaces + ? Unsafe.As>(ComparerHelpers.GetComparer(typeof(T).TypeHandle)) + : new ObjectComparer(), + null); + return s_default; + } + + public static Comparer Default + { + [Intrinsic] + get + { + // Lazy initialization produces smaller code for CoreRT than initialization in constructor + return s_default ?? Create(); + } + } + } + + internal sealed partial class EnumComparer : Comparer where T : struct, Enum + { + public override int Compare(T x, T y) + { + return ComparerHelpers.EnumOnlyCompare(x, y); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/EqualOnlyComparer.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/EqualOnlyComparer.cs new file mode 100644 index 00000000000000..7e51058a43895f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/EqualOnlyComparer.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections; + +namespace System.Collections.Generic +{ + internal static class EqualOnlyComparerHelper + { + public static bool Equals(sbyte x, sbyte y) + { + return x == y; + } + + public static bool Equals(byte x, byte y) + { + return x == y; + } + + public static bool Equals(short x, short y) + { + return x == y; + } + + public static bool Equals(ushort x, ushort y) + { + return x == y; + } + + public static bool Equals(int x, int y) + { + return x == y; + } + + public static bool Equals(uint x, uint y) + { + return x == y; + } + + public static bool Equals(long x, long y) + { + return x == y; + } + + public static bool Equals(ulong x, ulong y) + { + return x == y; + } + + public static bool Equals(IntPtr x, IntPtr y) + { + return x == y; + } + + public static bool Equals(UIntPtr x, UIntPtr y) + { + return x == y; + } + + public static bool Equals(float x, float y) + { + return x == y; + } + + public static bool Equals(double x, double y) + { + return x == y; + } + + public static bool Equals(decimal x, decimal y) + { + return x == y; + } + + public static bool Equals(string? x, string? y) + { + return x == y; + } + } + + /// + /// Minimum comparer for Array.IndexOf/Contains which each Array needs. So it's important to be small. + /// + /// + internal static class EqualOnlyComparer + { + // Force the compiler to inline this method. Normally the compiler will shy away from inlining such + // a large function, however in this case the method compiles down to almost nothing so help the + // compiler out a bit with this hint. Once the compiler supports bottom-up codegen analysis it should + // inline this without a hint. + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static bool Equals(T x, T y) + { + // Specialized Comparers + if (typeof(T) == typeof(sbyte)) + return EqualOnlyComparerHelper.Equals(((sbyte)(object)(x!)), ((sbyte)(object)(y!))); + else if (typeof(T) == typeof(byte)) + return EqualOnlyComparerHelper.Equals(((byte)(object)(x!)), ((byte)(object)(y!))); + else if (typeof(T) == typeof(short)) + return EqualOnlyComparerHelper.Equals(((short)(object)(x!)), ((short)(object)(y!))); + else if (typeof(T) == typeof(ushort)) + return EqualOnlyComparerHelper.Equals(((ushort)(object)(x!)), ((ushort)(object)(y!))); + else if (typeof(T) == typeof(int)) + return EqualOnlyComparerHelper.Equals(((int)(object)(x!)), ((int)(object)(y!))); + else if (typeof(T) == typeof(uint)) + return EqualOnlyComparerHelper.Equals(((uint)(object)(x!)), ((uint)(object)(y!))); + else if (typeof(T) == typeof(long)) + return EqualOnlyComparerHelper.Equals(((long)(object)(x!)), ((long)(object)(y!))); + else if (typeof(T) == typeof(ulong)) + return EqualOnlyComparerHelper.Equals(((ulong)(object)(x!)), ((ulong)(object)(y!))); + else if (typeof(T) == typeof(System.IntPtr)) + return EqualOnlyComparerHelper.Equals(((System.IntPtr)(object)(x!)), ((System.IntPtr)(object)(y!))); + else if (typeof(T) == typeof(System.UIntPtr)) + return EqualOnlyComparerHelper.Equals(((System.UIntPtr)(object)(x!)), ((System.UIntPtr)(object)(y!))); + else if (typeof(T) == typeof(float)) + return EqualOnlyComparerHelper.Equals(((float)(object)(x!)), ((float)(object)(y!))); + else if (typeof(T) == typeof(double)) + return EqualOnlyComparerHelper.Equals(((double)(object)(x!)), ((double)(object)(y!))); + else if (typeof(T) == typeof(decimal)) + return EqualOnlyComparerHelper.Equals(((decimal)(object)(x!)), ((decimal)(object)(y!))); + else if (typeof(T) == typeof(string)) + return EqualOnlyComparerHelper.Equals(((string?)(object?)(x)), ((string?)(object?)(y))); + + // Default Comparer + + if (x == null) + { + return y == null; + } + + if (y == null) + { + return false; + } + + return x.Equals(y); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.CoreRT.cs new file mode 100644 index 00000000000000..e3019e4c7baae9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.CoreRT.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable +using System.Runtime.CompilerServices; +using System.Threading; + +using Internal.IntrinsicSupport; +using Internal.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + public abstract partial class EqualityComparer : IEqualityComparer, IEqualityComparer + { + private static EqualityComparer s_default; + + // The AOT compiler can flip this to false under certain circumstances. + private static bool SupportsGenericIEquatableInterfaces => true; + + [Intrinsic] + private static EqualityComparer Create() + { + // The compiler will overwrite the Create method with optimized + // instantiation-specific implementation. + // This body serves as a fallback when instantiation-specific implementation is unavailable. + // If that happens, the compiler ensures we generate data structures to make the fallback work + // when this method is compiled. + Interlocked.CompareExchange(ref s_default, + SupportsGenericIEquatableInterfaces + ? Unsafe.As>(EqualityComparerHelpers.GetComparer(typeof(T).TypeHandle)) + : new ObjectEqualityComparer(), + null); + return s_default; + } + + public static EqualityComparer Default + { + [Intrinsic] + get + { + // Lazy initialization produces smaller code for CoreRT than initialization in constructor + return s_default ?? Create(); + } + } + } + + public sealed partial class EnumEqualityComparer : EqualityComparer where T : struct, Enum + { + public sealed override bool Equals(T x, T y) + { + return EqualityComparerHelpers.EnumOnlyEquals(x, y); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs new file mode 100644 index 00000000000000..95995bda41003f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs @@ -0,0 +1,492 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; +using System.Runtime; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Internal.Reflection.Augments; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +namespace System +{ + [DebuggerDisplay("Target method(s) = {GetTargetMethodsDescriptionForDebugger()}")] + public abstract partial class Delegate : ICloneable, ISerializable + { + // V1 API: Create closed instance delegates. Method name matching is case sensitive. + [RequiresUnreferencedCode("The target method might be removed")] + protected Delegate(object target, string method) + { + // This constructor cannot be used by application code. To create a delegate by specifying the name of a method, an + // overload of the public static CreateDelegate method is used. This will eventually end up calling into the internal + // implementation of CreateDelegate below, and does not invoke this constructor. + // The constructor is just for API compatibility with the public contract of the Delegate class. + throw new PlatformNotSupportedException(); + } + + // V1 API: Create open static delegates. Method name matching is case insensitive. + protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method) + { + // This constructor cannot be used by application code. To create a delegate by specifying the name of a method, an + // overload of the public static CreateDelegate method is used. This will eventually end up calling into the internal + // implementation of CreateDelegate below, and does not invoke this constructor. + // The constructor is just for API compatibility with the public contract of the Delegate class. + throw new PlatformNotSupportedException(); + } + + // New Delegate Implementation + + internal object m_firstParameter; + internal object m_helperObject; + internal IntPtr m_extraFunctionPointerOrData; + internal IntPtr m_functionPointer; + + // WARNING: These constants are also declared in System.Private.TypeLoader\Internal\Runtime\TypeLoader\CallConverterThunk.cs + // Do not change their values without updating the values in the calling convention converter component + private protected const int MulticastThunk = 0; + private protected const int ClosedStaticThunk = 1; + private protected const int OpenStaticThunk = 2; + private protected const int ClosedInstanceThunkOverGenericMethod = 3; // This may not exist + private protected const int DelegateInvokeThunk = 4; + private protected const int OpenInstanceThunk = 5; // This may not exist + private protected const int ObjectArrayThunk = 6; // This may not exist + + // + // If the thunk does not exist, the function will return IntPtr.Zero. + private protected virtual IntPtr GetThunk(int whichThunk) + { +#if PROJECTN + // The GetThunk function should be overriden on all delegate types, except for universal + // canonical delegates which use calling convention converter thunks to marshal arguments + // for the delegate call. If we execute this version of GetThunk, we can at least assert + // that the current delegate type is a generic type. + Debug.Assert(this.EETypePtr.IsGeneric); + return TypeLoaderExports.GetDelegateThunk(this, whichThunk); +#else + // CoreRT doesn't support Universal Shared Code right now, so let's make this method return null for now. + // When CoreRT adds USG support we'll probably want to do some level of IL switching here so that + // we don't have this static call into type loader when USG is not enabled at compile time. + // The static call hurts size in our minimal targets. + return IntPtr.Zero; +#endif + } + + /// + /// Used by various parts of the runtime as a replacement for Delegate.Method + /// + /// The Interop layer uses this to distinguish between different methods on a + /// single type, and to get the function pointer for delegates to static functions + /// + /// The reflection apis use this api to figure out what MethodInfo is related + /// to a delegate. + /// + /// + /// + /// This value indicates which type an delegate's function pointer is associated with + /// This value is ONLY set for delegates where the function pointer points at an instance method + /// + /// + /// This value indicates if the returned pointer is an open resolver structure. + /// + /// + /// Delegate points to an object array thunk (the delegate wraps a Func<object[], object> delegate). This + /// is typically a delegate pointing to the LINQ expression interpreter. + /// + /// + internal unsafe IntPtr GetFunctionPointer(out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver, out bool isInterpreterEntrypoint) + { + typeOfFirstParameterIfInstanceDelegate = default(RuntimeTypeHandle); + isOpenResolver = false; + isInterpreterEntrypoint = false; + + if (GetThunk(MulticastThunk) == m_functionPointer) + { + return IntPtr.Zero; + } + else if (GetThunk(ObjectArrayThunk) == m_functionPointer) + { + isInterpreterEntrypoint = true; + return IntPtr.Zero; + } + else if (m_extraFunctionPointerOrData != IntPtr.Zero) + { + if (GetThunk(OpenInstanceThunk) == m_functionPointer) + { + typeOfFirstParameterIfInstanceDelegate = ((OpenMethodResolver*)m_extraFunctionPointerOrData)->DeclaringType; + isOpenResolver = true; + } + return m_extraFunctionPointerOrData; + } + else + { + if (m_firstParameter != null) + typeOfFirstParameterIfInstanceDelegate = new RuntimeTypeHandle(m_firstParameter.EETypePtr); + + // TODO! Implementation issue for generic invokes here ... we need another IntPtr for uniqueness. + + return m_functionPointer; + } + } + + // This function is known to the IL Transformer. + private void InitializeClosedInstance(object firstParameter, IntPtr functionPointer) + { + if (firstParameter is null) + throw new ArgumentException(SR.Arg_DlgtNullInst); + + m_functionPointer = functionPointer; + m_firstParameter = firstParameter; + } + + // This function is known to the IL Transformer. + private void InitializeClosedInstanceSlow(object firstParameter, IntPtr functionPointer) + { + // This method is like InitializeClosedInstance, but it handles ALL cases. In particular, it handles generic method with fun function pointers. + + if (firstParameter is null) + throw new ArgumentException(SR.Arg_DlgtNullInst); + + if (!FunctionPointerOps.IsGenericMethodPointer(functionPointer)) + { + m_functionPointer = functionPointer; + m_firstParameter = firstParameter; + } + else + { + m_firstParameter = this; + m_functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod); + m_extraFunctionPointerOrData = functionPointer; + m_helperObject = firstParameter; + } + } + + // This function is known to the compiler. + private void InitializeClosedInstanceWithGVMResolution(object firstParameter, RuntimeMethodHandle tokenOfGenericVirtualMethod) + { + if (firstParameter is null) + throw new ArgumentException(SR.Arg_DlgtNullInst); + + IntPtr functionResolution = TypeLoaderExports.GVMLookupForSlot(firstParameter, tokenOfGenericVirtualMethod); + + if (functionResolution == IntPtr.Zero) + { + // TODO! What to do when GVM resolution fails. Should never happen + throw new InvalidOperationException(); + } + if (!FunctionPointerOps.IsGenericMethodPointer(functionResolution)) + { + m_functionPointer = functionResolution; + m_firstParameter = firstParameter; + } + else + { + m_firstParameter = this; + m_functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod); + m_extraFunctionPointerOrData = functionResolution; + m_helperObject = firstParameter; + } + + return; + } + + private void InitializeClosedInstanceToInterface(object firstParameter, IntPtr dispatchCell) + { + if (firstParameter is null) + throw new ArgumentException(SR.Arg_DlgtNullInst); + + m_functionPointer = RuntimeImports.RhpResolveInterfaceMethod(firstParameter, dispatchCell); + m_firstParameter = firstParameter; + } + + // This is used to implement MethodInfo.CreateDelegate() in a desktop-compatible way. Yes, the desktop really + // let you use that api to invoke an instance method with a null 'this'. + private void InitializeClosedInstanceWithoutNullCheck(object firstParameter, IntPtr functionPointer) + { + if (!FunctionPointerOps.IsGenericMethodPointer(functionPointer)) + { + m_functionPointer = functionPointer; + m_firstParameter = firstParameter; + } + else + { + m_firstParameter = this; + m_functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod); + m_extraFunctionPointerOrData = functionPointer; + m_helperObject = firstParameter; + } + } + + // This function is known to the compiler backend. + private void InitializeClosedStaticThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk) + { + m_extraFunctionPointerOrData = functionPointer; + m_helperObject = firstParameter; + m_functionPointer = functionPointerThunk; + m_firstParameter = this; + } + + // This function is known to the compiler backend. + private void InitializeOpenStaticThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk) + { + // This sort of delegate is invoked by calling the thunk function pointer with the arguments to the delegate + a reference to the delegate object itself. + m_firstParameter = this; + m_functionPointer = functionPointerThunk; + m_extraFunctionPointerOrData = functionPointer; + } + + private void InitializeOpenInstanceThunkDynamic(IntPtr functionPointer, IntPtr functionPointerThunk) + { + // This sort of delegate is invoked by calling the thunk function pointer with the arguments to the delegate + a reference to the delegate object itself. + m_firstParameter = this; + m_functionPointer = functionPointerThunk; + m_extraFunctionPointerOrData = functionPointer; + } + + internal void SetClosedStaticFirstParameter(object firstParameter) + { + // Closed static delegates place a value in m_helperObject that they pass to the target method. + Debug.Assert(m_functionPointer == GetThunk(ClosedStaticThunk)); + m_helperObject = firstParameter; + } + + // This function is only ever called by the open instance method thunk, and in that case, + // m_extraFunctionPointerOrData always points to an OpenMethodResolver + [MethodImpl(MethodImplOptions.NoInlining)] + private IntPtr GetActualTargetFunctionPointer(object thisObject) + { + return OpenMethodResolver.ResolveMethod(m_extraFunctionPointerOrData, thisObject); + } + + public override int GetHashCode() + { + return GetType().GetHashCode(); + } + + internal bool IsDynamicDelegate() + { + if (this.GetThunk(MulticastThunk) == IntPtr.Zero) + { + return true; + } + + return false; + } + + [DebuggerGuidedStepThroughAttribute] + protected virtual object DynamicInvokeImpl(object[]? args) + { + if (IsDynamicDelegate()) + { + // DynamicDelegate case + object result = ((Func)m_helperObject)(args); + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + return result; + } + else + { + IntPtr invokeThunk = this.GetThunk(DelegateInvokeThunk); + + IntPtr genericDictionary = IntPtr.Zero; + if (FunctionPointerOps.IsGenericMethodPointer(invokeThunk)) + { + unsafe + { + GenericMethodDescriptor* descriptor = FunctionPointerOps.ConvertToGenericDescriptor(invokeThunk); + genericDictionary = descriptor->InstantiationArgument; + invokeThunk = descriptor->MethodFunctionPointer; + } + } + + object result = InvokeUtils.CallDynamicInvokeMethod(this.m_firstParameter, this.m_functionPointer, invokeThunk, genericDictionary, this, args, binderBundle: null, wrapInTargetInvocationException: true); + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + return result; + } + } + + protected virtual MethodInfo GetMethodImpl() + { + return RuntimeAugments.Callbacks.GetDelegateMethod(this); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + // It is expected that all real uses of the Equals method will hit the MulticastDelegate.Equals logic instead of this + // therefore, instead of duplicating the desktop behavior where direct calls to this Equals function do not behave + // correctly, we'll just throw here. + throw new PlatformNotSupportedException(); + } + + public object Target + { + get + { + // Multi-cast delegates return the Target of the last delegate in the list + if (m_functionPointer == GetThunk(MulticastThunk)) + { + Delegate[] invocationList = (Delegate[])m_helperObject; + int invocationCount = (int)m_extraFunctionPointerOrData; + return invocationList[invocationCount - 1].Target; + } + + // Closed static delegates place a value in m_helperObject that they pass to the target method. + if (m_functionPointer == GetThunk(ClosedStaticThunk) || + m_functionPointer == GetThunk(ClosedInstanceThunkOverGenericMethod) || + m_functionPointer == GetThunk(ObjectArrayThunk)) + return m_helperObject; + + // Other non-closed thunks can be identified as the m_firstParameter field points at this. + if (object.ReferenceEquals(m_firstParameter, this)) + { + return null; + } + + // Closed instance delegates place a value in m_firstParameter, and we've ruled out all other types of delegates + return m_firstParameter; + } + } + + // V2 api: Creates open or closed delegates to static or instance methods - relaxed signature checking allowed. + public static Delegate CreateDelegate(Type type, object? firstArgument, MethodInfo method, bool throwOnBindFailure) => ReflectionAugments.ReflectionCoreCallbacks.CreateDelegate(type, firstArgument, method, throwOnBindFailure); + + // V1 api: Creates open delegates to static or instance methods - relaxed signature checking allowed. + public static Delegate CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure) => ReflectionAugments.ReflectionCoreCallbacks.CreateDelegate(type, method, throwOnBindFailure); + + // V1 api: Creates closed delegates to instance methods only, relaxed signature checking disallowed. + [RequiresUnreferencedCode("The target method might be removed")] + public static Delegate CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure) => ReflectionAugments.ReflectionCoreCallbacks.CreateDelegate(type, target, method, ignoreCase, throwOnBindFailure); + + // V1 api: Creates open delegates to static methods only, relaxed signature checking disallowed. + public static Delegate CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method, bool ignoreCase, bool throwOnBindFailure) => ReflectionAugments.ReflectionCoreCallbacks.CreateDelegate(type, target, method, ignoreCase, throwOnBindFailure); + + internal bool IsOpenStatic + { + get + { + return GetThunk(OpenStaticThunk) == m_functionPointer; + } + } + + internal static bool InternalEqualTypes(object a, object b) + { + return a.EETypePtr == b.EETypePtr; + } + + // Returns a new delegate of the specified type whose implementation is provied by the + // provided delegate. + internal static Delegate CreateObjectArrayDelegate(Type t, Func handler) + { + EETypePtr delegateEEType; + if (!t.TryGetEEType(out delegateEEType)) + { + throw new InvalidOperationException(); + } + + if (!delegateEEType.IsDefType || delegateEEType.IsGenericTypeDefinition) + { + throw new InvalidOperationException(); + } + + Delegate del = (Delegate)(RuntimeImports.RhNewObject(delegateEEType)); + + IntPtr objArrayThunk = del.GetThunk(Delegate.ObjectArrayThunk); + if (objArrayThunk == IntPtr.Zero) + { + throw new InvalidOperationException(); + } + + del.m_helperObject = handler; + del.m_functionPointer = objArrayThunk; + del.m_firstParameter = del; + return del; + } + + // + // Internal (and quite unsafe) helper to create delegates of an arbitrary type. This is used to support Reflection invoke. + // + // Note that delegates constructed the normal way do not come through here. The IL transformer generates the equivalent of + // this code customized for each delegate type. + // + internal static Delegate CreateDelegate(EETypePtr delegateEEType, IntPtr ldftnResult, object thisObject, bool isStatic, bool isOpen) + { + Delegate del = (Delegate)(RuntimeImports.RhNewObject(delegateEEType)); + + // What? No constructor call? That's right, and it's not an oversight. All "construction" work happens in + // the Initialize() methods. This helper has a hard dependency on this invariant. + + if (isStatic) + { + if (isOpen) + { + IntPtr thunk = del.GetThunk(Delegate.OpenStaticThunk); + del.InitializeOpenStaticThunk(null, ldftnResult, thunk); + } + else + { + IntPtr thunk = del.GetThunk(Delegate.ClosedStaticThunk); + del.InitializeClosedStaticThunk(thisObject, ldftnResult, thunk); + } + } + else + { + if (isOpen) + { + IntPtr thunk = del.GetThunk(Delegate.OpenInstanceThunk); + del.InitializeOpenInstanceThunkDynamic(ldftnResult, thunk); + } + else + { + del.InitializeClosedInstanceWithoutNullCheck(thisObject, ldftnResult); + } + } + return del; + } + + private string GetTargetMethodsDescriptionForDebugger() + { + if (m_functionPointer == GetThunk(MulticastThunk)) + { + // Multi-cast delegates return the Target of the last delegate in the list + Delegate[] invocationList = (Delegate[])m_helperObject; + int invocationCount = (int)m_extraFunctionPointerOrData; + StringBuilder builder = new StringBuilder(); + for (int c = 0; c < invocationCount; c++) + { + if (c != 0) + builder.Append(", "); + + builder.Append(invocationList[c].GetTargetMethodsDescriptionForDebugger()); + } + + return builder.ToString(); + } + else + { + RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate; + IntPtr functionPointer = GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out bool _, out bool _); + if (!FunctionPointerOps.IsGenericMethodPointer(functionPointer)) + { + return DebuggerFunctionPointerFormattingHook(functionPointer, typeOfFirstParameterIfInstanceDelegate); + } + else + { + unsafe + { + GenericMethodDescriptor* pointerDef = FunctionPointerOps.ConvertToGenericDescriptor(functionPointer); + return DebuggerFunctionPointerFormattingHook(pointerDef->InstantiationArgument, typeOfFirstParameterIfInstanceDelegate); + } + } + } + } + + private static string DebuggerFunctionPointerFormattingHook(IntPtr functionPointer, RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate) + { + // This method will be hooked by the debugger and the debugger will cause it to return a description for the function pointer + throw new NotSupportedException(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/Debug.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/Debug.CoreRT.cs new file mode 100644 index 00000000000000..9c94f1965b0530 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/Debug.CoreRT.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.DeveloperExperience; +using System.Runtime.CompilerServices; +using System.Security; + +namespace System.Diagnostics +{ + // .NET Native-specific Debug implementation + public static partial class Debug + { + [DebuggerHidden] + [Intrinsic] + internal static void DebugBreak() + { + // IMPORTANT: This call will let us detect if debug break is broken, and also + // gives double chances. + DebugBreak(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/DebugAnnotations.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/DebugAnnotations.cs new file mode 100644 index 00000000000000..cdb6dd7e6c642a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/DebugAnnotations.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics +{ + /// + /// Annotations used by debugger + /// + [System.Runtime.CompilerServices.ReflectionBlocked] + public static class DebugAnnotations + { + /// + /// Informs debugger that previous line contains code that debugger needs to dive deeper inside. + /// + public static void PreviousCallContainsDebuggerStepInCode() + { + // This is a marker method and has no code in method body + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/Debugger.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/Debugger.cs new file mode 100644 index 00000000000000..b05cbfee697410 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/Debugger.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +namespace System.Diagnostics +{ + public static class Debugger + { + [MethodImpl(MethodImplOptions.NoInlining)] + [DebuggerHidden] // this helps VS appear to stop on the source line calling Debugger.Break() instead of inside it + public static void Break() + { +#if TARGET_WINDOWS + // IsAttached is always true when IsDebuggerPresent is true, so no need to check for it + if (Interop.Kernel32.IsDebuggerPresent()) + Debug.DebugBreak(); +#else + // UNIXTODO: Implement Debugger.Break +#endif + } + + public static bool IsAttached + { + get + { + return _isDebuggerAttached; + } + } + + public static bool Launch() + { + throw new PlatformNotSupportedException(); + } + + public static void NotifyOfCrossThreadDependency() + { + // nothing to do...yet + } + +#pragma warning disable 649 // Suppress compiler warning about _isDebuggerAttached never being assigned to. + // _isDebuggerAttached: Do not remove: This field is known to the debugger and modified directly by the debugger. + private static bool _isDebuggerAttached; +#pragma warning restore 649 + + /// + /// Constants representing the importance level of messages to be logged. + /// + /// An attached debugger can enable or disable which messages will + /// actually be reported to the user through the COM+ debugger + /// services API. This info is communicated to the runtime so only + /// desired events are actually reported to the debugger. + /// Constant representing the default category + /// + public static readonly string DefaultCategory; + + /// + /// Posts a message for the attached debugger. If there is no + /// debugger attached, has no effect. The debugger may or may not + /// report the message depending on its settings. + /// + public static void Log(int level, string category, string message) + { + if (IsLogging()) + { + throw new NotImplementedException(); // TODO: CoreRT issue# 3235: NS2.0 - implement Debugger.Log, IsLogging + } + } + + /// + /// Checks to see if an attached debugger has logging enabled + /// + public static bool IsLogging() + { + if (string.Empty.Length != 0) + { + throw new NotImplementedException(); // TODO: CoreRT issue# 3235: NS2.0 - implement Debugger.Log, IsLogging + } + return false; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/DebuggerGuidedStepThroughAttribute.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/DebuggerGuidedStepThroughAttribute.cs new file mode 100644 index 00000000000000..41eaf9cbd26d9b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/DebuggerGuidedStepThroughAttribute.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics +{ + [System.Runtime.CompilerServices.ReflectionBlocked] + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, Inherited = false)] + public sealed class DebuggerGuidedStepThroughAttribute : Attribute + { + public DebuggerGuidedStepThroughAttribute() { } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreRT.cs new file mode 100644 index 00000000000000..2e746cd92b7ac1 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreRT.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Text; + +using Internal.DeveloperExperience; + +namespace System.Diagnostics +{ + /// + /// Stack frame represents a single frame in a stack trace; frames + /// corresponding to methods with available symbolic information + /// provide source file / line information. Some frames may provide IL + /// offset information and / or MethodBase reflection information. + /// There is no good reason for the methods of this class to be virtual. + /// + public partial class StackFrame + { + /// + /// IP address representing this stack frame. + /// + private IntPtr _ipAddress; + + /// + /// File info flag to use for stack trace-style formatting. + /// + private bool _needFileInfo; + + /// + /// Constructs a StackFrame corresponding to a given IP address. + /// + internal StackFrame(IntPtr ipAddress, bool needFileInfo) + { + InitializeForIpAddress(ipAddress, needFileInfo); + } + + /// + /// Internal stack frame initialization based on IP address. + /// + private void InitializeForIpAddress(IntPtr ipAddress, bool needFileInfo) + { + _ipAddress = ipAddress; + _needFileInfo = needFileInfo; + + if (_ipAddress == Exception.EdiSeparator) + { + _isLastFrameFromForeignExceptionStackTrace = true; + } + else if (_ipAddress != IntPtr.Zero) + { + IntPtr methodStartAddress = RuntimeImports.RhFindMethodStartAddress(ipAddress); + + _nativeOffset = (int)((nint)_ipAddress - (nint)methodStartAddress); + + DeveloperExperience.Default.TryGetILOffsetWithinMethod(_ipAddress, out _ilOffset); + DeveloperExperience.Default.TryGetMethodBase(methodStartAddress, out _method); + + if (needFileInfo) + { + DeveloperExperience.Default.TryGetSourceLineInfo( + _ipAddress, + out _fileName, + out _lineNumber, + out _columnNumber); + } + } + } + + /// + /// Internal stack frame initialization based on frame index within the stack of the current thread. + /// + [MethodImplAttribute(MethodImplOptions.NoInlining)] + private void BuildStackFrame(int frameIndex, bool needFileInfo) + { + const int SystemDiagnosticsStackDepth = 2; + + frameIndex += SystemDiagnosticsStackDepth; + IntPtr[] frameArray = new IntPtr[frameIndex + 1]; + int returnedFrameCount = RuntimeImports.RhGetCurrentThreadStackTrace(frameArray); + int realFrameCount = (returnedFrameCount >= 0 ? returnedFrameCount : frameArray.Length); + + IntPtr ipAddress = (frameIndex < realFrameCount) ? frameArray[frameIndex] : IntPtr.Zero; + InitializeForIpAddress(ipAddress, needFileInfo); + } + + /// + /// Return native IP address for this stack frame. + /// + internal IntPtr GetNativeIPAddress() + { + return _ipAddress; + } + + /// + /// Check whether method info is available. + /// + internal bool HasMethod() + { + return _method != null; + } + + /// + /// Format stack frame without MethodBase info. Return true if the stack info + /// is valid and line information should be appended if available. + /// + private bool AppendStackFrameWithoutMethodBase(StringBuilder builder) + { + builder.Append(DeveloperExperience.Default.CreateStackTraceString(_ipAddress, includeFileInfo: false)); + return true; + } + + /// + /// Set rethrow marker. + /// + internal void SetIsLastFrameFromForeignExceptionStackTrace() + { + _isLastFrameFromForeignExceptionStackTrace = true; + } + + /// + /// Builds a representation of the stack frame for use in the stack trace. + /// + internal void AppendToStackTrace(StringBuilder builder) + { + if (_ipAddress != Exception.EdiSeparator) + { + // Passing a default string for "at" in case SR.UsingResourceKeys() is true + // as this is a special case and we don't want to have "Word_At" on stack traces. + string word_At = SR.GetResourceString(nameof(SR.Word_At), defaultString: "at"); + builder.AppendFormat(" {0} ", word_At); + builder.AppendLine(DeveloperExperience.Default.CreateStackTraceString(_ipAddress, _needFileInfo)); + } + if (_isLastFrameFromForeignExceptionStackTrace) + { + builder.AppendLine(SR.StackTrace_EndStackTraceFromPreviousThrow); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackFrameExtensions.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackFrameExtensions.cs new file mode 100644 index 00000000000000..23294a6115873a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackFrameExtensions.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; + +namespace System.Diagnostics +{ + public static class StackFrameExtensions + { + /// + /// Return load address of the native image pointed to by the stack frame. + /// + public static IntPtr GetNativeImageBase(this StackFrame stackFrame) + { + return RuntimeImports.RhGetOSModuleFromPointer(stackFrame.GetNativeIPAddress()); + } + + /// + /// Return stack frame native IP address. + /// + public static IntPtr GetNativeIP(this StackFrame stackFrame) + { + return stackFrame.GetNativeIPAddress(); + } + + /// + /// Return true when the stack frame information can be converted to IL offset information + /// within the MSIL method body. + /// + public static bool HasILOffset(this StackFrame stackFrame) + { + return stackFrame.GetILOffset() != StackFrame.OFFSET_UNKNOWN; + } + + /// + /// Return true when a MethodBase reflection info is available for the stack frame + /// + public static bool HasMethod(this StackFrame stackFrame) + { + return stackFrame.HasMethod(); + } + + /// + /// Return true when the stack frame information corresponds to a native image + /// + public static bool HasNativeImage(this StackFrame stackFrame) + { + // In .NET Native, everything has a native image (at least today) + return true; + } + + /// + /// Return true when stack frame information supports source file / line information lookup + /// + public static bool HasSource(this StackFrame stackFrame) + { + return stackFrame.GetFileName() != null; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.cs new file mode 100644 index 00000000000000..32db59daefe2c2 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Text; + +namespace System.Diagnostics +{ + public partial class StackTrace + { +#if !TARGET_WASM + /// + /// Initialize the stack trace based on current thread and given initial frame index. + /// + [MethodImplAttribute(MethodImplOptions.NoInlining)] + private void InitializeForCurrentThread(int skipFrames, bool needFileInfo) + { + const int SystemDiagnosticsStackDepth = 2; + + int frameCount = -RuntimeImports.RhGetCurrentThreadStackTrace(Array.Empty()); + Debug.Assert(frameCount >= 0); + IntPtr[] stackTrace = new IntPtr[frameCount]; + int trueFrameCount = RuntimeImports.RhGetCurrentThreadStackTrace(stackTrace); + Debug.Assert(trueFrameCount == frameCount); + InitializeForIpAddressArray(stackTrace, skipFrames + SystemDiagnosticsStackDepth, frameCount, needFileInfo); + } +#endif + + /// + /// Initialize the stack trace based on a given exception and initial frame index. + /// + private void InitializeForException(Exception exception, int skipFrames, bool needFileInfo) + { + IntPtr[] stackIPs = exception.GetStackIPs(); + InitializeForIpAddressArray(stackIPs, skipFrames, stackIPs.Length, needFileInfo); + } + + /// + /// Initialize the stack trace based on a given array of IP addresses. + /// + private void InitializeForIpAddressArray(IntPtr[] ipAddresses, int skipFrames, int endFrameIndex, bool needFileInfo) + { + int frameCount = (skipFrames < endFrameIndex ? endFrameIndex - skipFrames : 0); + + // Calculate true frame count upfront - we need to skip EdiSeparators which get + // collapsed onto boolean flags on the preceding stack frame + int outputFrameCount = 0; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) + { + if (ipAddresses[frameIndex + skipFrames] != Exception.EdiSeparator) + { + outputFrameCount++; + } + } + + if (outputFrameCount > 0) + { + _stackFrames = new StackFrame[outputFrameCount]; + int outputFrameIndex = 0; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) + { + IntPtr ipAddress = ipAddresses[frameIndex + skipFrames]; + if (ipAddress != Exception.EdiSeparator) + { + _stackFrames[outputFrameIndex++] = new StackFrame(ipAddress, needFileInfo); + } + else if (outputFrameIndex > 0) + { + _stackFrames[outputFrameIndex - 1].SetIsLastFrameFromForeignExceptionStackTrace(); + } + } + Debug.Assert(outputFrameIndex == outputFrameCount); + } + + _numOfFrames = outputFrameCount; + _methodsToSkip = 0; + } + +#if !TARGET_WASM + internal void ToString(TraceFormat traceFormat, StringBuilder builder) + { + if (_stackFrames == null) + { + return; + } + + foreach (StackFrame frame in _stackFrames) + { + frame.AppendToStackTrace(builder); + } + + if (traceFormat == TraceFormat.Normal && builder.Length >= Environment.NewLine.Length) + builder.Length -= Environment.NewLine.Length; + } +#endif + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/EETypePtr.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/EETypePtr.cs new file mode 100644 index 00000000000000..4e2335759056b6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/EETypePtr.cs @@ -0,0 +1,490 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================ +** +** +** +** Purpose: Pointer Type to a MethodTable in the runtime. +** +** +===========================================================*/ + +using System.Runtime; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Internal.Runtime.CompilerServices; + +using MethodTable = Internal.Runtime.MethodTable; +using EETypeElementType = Internal.Runtime.EETypeElementType; +using EETypeRef = Internal.Runtime.EETypeRef; +using CorElementType = System.Reflection.CorElementType; + +namespace System +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct EETypePtr : IEquatable + { + private MethodTable* _value; + + public EETypePtr(IntPtr value) + { + _value = (MethodTable*)value; + } + + internal EETypePtr(MethodTable* value) + { + _value = value; + } + + internal MethodTable* ToPointer() + { + return _value; + } + + public override bool Equals(object? obj) + { + if (obj is EETypePtr) + { + return this == (EETypePtr)obj; + } + return false; + } + + public bool Equals(EETypePtr p) + { + return this == p; + } + + public static bool operator ==(EETypePtr value1, EETypePtr value2) + { + if (value1.IsNull) + return value2.IsNull; + else if (value2.IsNull) + return false; + else + return RuntimeImports.AreTypesEquivalent(value1, value2); + } + + public static bool operator !=(EETypePtr value1, EETypePtr value2) + { + return !(value1 == value2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + { + return (int)_value->HashCode; + } + + // + // Faster version of Equals for use on EETypes that are known not to be null and where the "match" case is the hot path. + // + public bool FastEquals(EETypePtr other) + { + Debug.Assert(!this.IsNull); + Debug.Assert(!other.IsNull); + + // Fast check for raw equality before making call to helper. + if (this.RawValue == other.RawValue) + return true; + return RuntimeImports.AreTypesEquivalent(this, other); + } + + // Caution: You cannot safely compare RawValue's as RH does NOT unify EETypes. Use the == or Equals() methods exposed by EETypePtr itself. + internal IntPtr RawValue + { + get + { + return (IntPtr)_value; + } + } + + internal bool IsNull + { + get + { + return _value == null; + } + } + + internal bool IsArray + { + get + { + return _value->IsArray; + } + } + + internal bool IsSzArray + { + get + { + return _value->IsSzArray; + } + } + + internal bool IsPointer + { + get + { + return _value->IsPointerType; + } + } + + internal bool IsByRef + { + get + { + return _value->IsByRefType; + } + } + + internal bool IsValueType + { + get + { + return _value->IsValueType; + } + } + + internal bool IsString + { + get + { + return _value->IsString; + } + } + + // Warning! UNLIKE the similarly named Reflection api, this method also returns "true" for Enums. + internal bool IsPrimitive + { + get + { + return _value->IsPrimitive; + } + } + + // WARNING: Never call unless the MethodTable came from an instanced object. Nested enums can be open generics (typeof(Outer<>).NestedEnum) + // and this helper has undefined behavior when passed such as a enum. + internal bool IsEnum + { + get + { + // Q: When is an enum type a constructed generic type? + // A: When it's nested inside a generic type. + if (!(IsDefType)) + return false; + + // Generic type definitions that return true for IsPrimitive are type definitions of generic enums. + // Otherwise check the base type. + return (IsGenericTypeDefinition && IsPrimitive) || this.BaseType == EETypePtr.EETypePtrOf(); + } + } + + // Gets a value indicating whether this is a generic type definition (an uninstantiated generic type). + internal bool IsGenericTypeDefinition + { + get + { + return _value->IsGenericTypeDefinition; + } + } + + // Gets a value indicating whether this is an instantiated generic type. + internal bool IsGeneric + { + get + { + return _value->IsGeneric; + } + } + + internal GenericArgumentCollection Instantiation + { + get + { + return new GenericArgumentCollection(_value->GenericArity, _value->GenericArguments); + } + } + + internal EETypePtr GenericDefinition + { + get + { + return new EETypePtr(_value->GenericDefinition); + } + } + + /// + /// Gets a value indicating whether this is a class, a struct, an enum, or an interface. + /// + internal bool IsDefType + { + get + { + return !_value->IsParameterizedType; + } + } + + internal bool IsDynamicType + { + get + { + return _value->IsDynamicType; + } + } + + internal bool IsInterface + { + get + { + return _value->IsInterface; + } + } + + internal bool IsAbstract + { + get + { + return _value->IsAbstract; + } + } + + internal bool IsByRefLike + { + get + { + return _value->IsByRefLike; + } + } + + internal bool IsNullable + { + get + { + return _value->IsNullable; + } + } + + internal bool HasCctor + { + get + { + return _value->HasCctor; + } + } + + internal EETypePtr NullableType + { + get + { + return new EETypePtr(_value->NullableType); + } + } + + internal EETypePtr ArrayElementType + { + get + { + return new EETypePtr(_value->RelatedParameterType); + } + } + + internal int ArrayRank + { + get + { + return _value->ArrayRank; + } + } + + internal InterfaceCollection Interfaces + { + get + { + return new InterfaceCollection(_value); + } + } + + internal EETypePtr BaseType + { + get + { + if (IsArray) + return EETypePtr.EETypePtrOf(); + + if (IsPointer || IsByRef) + return new EETypePtr(default(IntPtr)); + + EETypePtr baseEEType = new EETypePtr(_value->NonArrayBaseType); + return baseEEType; + } + } + + internal ushort ComponentSize + { + get + { + return _value->ComponentSize; + } + } + + internal uint BaseSize + { + get + { + return _value->BaseSize; + } + } + + internal IntPtr DispatchMap + { + get + { + return (IntPtr)_value->DispatchMap; + } + } + + // Has internal gc pointers. + internal bool HasPointers + { + get + { + return _value->HasGCPointers; + } + } + + internal uint ValueTypeSize + { + get + { + return _value->ValueTypeSize; + } + } + + internal CorElementType CorElementType + { + get + { + Debug.Assert((int)CorElementType.ELEMENT_TYPE_BOOLEAN == (int)EETypeElementType.Boolean); + Debug.Assert((int)CorElementType.ELEMENT_TYPE_I1 == (int)EETypeElementType.SByte); + Debug.Assert((int)CorElementType.ELEMENT_TYPE_I8 == (int)EETypeElementType.Int64); + EETypeElementType elementType = ElementType; + + if (elementType <= EETypeElementType.UInt64) + return (CorElementType)elementType; + else if (elementType == EETypeElementType.Single) + return CorElementType.ELEMENT_TYPE_R4; + else if (elementType == EETypeElementType.Double) + return CorElementType.ELEMENT_TYPE_R8; + else if (elementType == EETypeElementType.IntPtr) + return CorElementType.ELEMENT_TYPE_I; + else if (elementType == EETypeElementType.UIntPtr) + return CorElementType.ELEMENT_TYPE_U; + else if (IsValueType) + return CorElementType.ELEMENT_TYPE_VALUETYPE; + else if (IsByRef) + return CorElementType.ELEMENT_TYPE_BYREF; + else if (IsPointer) + return CorElementType.ELEMENT_TYPE_PTR; + else if (IsSzArray) + return CorElementType.ELEMENT_TYPE_SZARRAY; + else if (IsArray) + return CorElementType.ELEMENT_TYPE_ARRAY; + else + return CorElementType.ELEMENT_TYPE_CLASS; + + } + } + + internal EETypeElementType ElementType + { + get + { + return _value->ElementType; + } + } + + internal RuntimeImports.RhCorElementTypeInfo CorElementTypeInfo + { + get + { + return RuntimeImports.GetRhCorElementTypeInfo(CorElementType); + } + } + + internal ref T GetWritableData() where T : unmanaged + { + Debug.Assert(Internal.Runtime.WritableData.GetSize(IntPtr.Size) == sizeof(T)); + return ref Unsafe.AsRef((void*)_value->WritableData); + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static EETypePtr EETypePtrOf() + { + // Compilers are required to provide a low level implementation of this method. + throw new NotImplementedException(); + } + + public struct InterfaceCollection + { + private MethodTable* _value; + + internal InterfaceCollection(MethodTable* value) + { + _value = value; + } + + public int Count + { + get + { + return _value->NumInterfaces; + } + } + + public EETypePtr this[int index] + { + get + { + Debug.Assert((uint)index < _value->NumInterfaces); + + return new EETypePtr(_value->InterfaceMap[index].InterfaceType); + } + } + } + + public struct GenericArgumentCollection + { + private EETypeRef* _arguments; + private uint _argumentCount; + + internal GenericArgumentCollection(uint argumentCount, EETypeRef* arguments) + { + _argumentCount = argumentCount; + _arguments = arguments; + } + + public int Length + { + get + { + return (int)_argumentCount; + } + } + + public EETypePtr this[int index] + { + get + { + Debug.Assert((uint)index < _argumentCount); + return new EETypePtr(_arguments[index].Value); + } + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Enum.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Enum.CoreRT.cs new file mode 100644 index 00000000000000..40b30cd712b9a6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Enum.CoreRT.cs @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime; +using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; +using Internal.Reflection.Augments; + +using CorElementType = System.Reflection.CorElementType; +using EETypeElementType = Internal.Runtime.EETypeElementType; + +namespace System +{ + public abstract partial class Enum : ValueType, IComparable, IFormattable, IConvertible + { + internal static EnumInfo GetEnumInfo(Type enumType, bool getNames = true) + { + Debug.Assert(enumType != null); + Debug.Assert(enumType is RuntimeType); + Debug.Assert(enumType.IsEnum); + + return ReflectionAugments.ReflectionCoreCallbacks.GetEnumInfo(enumType); + } + + private static object InternalBoxEnum(Type enumType, long value) + { + return ToObject(enumType.TypeHandle.ToEETypePtr(), value); + } + + private CorElementType InternalGetCorElementType() + { + return this.EETypePtr.CorElementType; + } + + // + // Note: This works on both Enum's and underlying integer values. + // + // + // This returns the underlying enum values as "ulong" regardless of the actual underlying type. Signed integral + // types get sign-extended into the 64-bit value, unsigned types get zero-extended. + // + // The return value is "bool" if "value" is not an enum or an "integer type" as defined by the BCL Enum apis. + // + internal static bool TryGetUnboxedValueOfEnumOrInteger(object value, out ulong result) + { + EETypePtr eeType = value.EETypePtr; + // For now, this check is required to flush out pointers. + if (!eeType.IsDefType) + { + result = 0; + return false; + } + EETypeElementType elementType = eeType.ElementType; + + ref byte pValue = ref value.GetRawData(); + + switch (elementType) + { + case EETypeElementType.Boolean: + result = Unsafe.As(ref pValue) ? 1UL : 0UL; + return true; + + case EETypeElementType.Char: + result = (ulong)(long)Unsafe.As(ref pValue); + return true; + + case EETypeElementType.SByte: + result = (ulong)(long)Unsafe.As(ref pValue); + return true; + + case EETypeElementType.Byte: + result = (ulong)(long)Unsafe.As(ref pValue); + return true; + + case EETypeElementType.Int16: + result = (ulong)(long)Unsafe.As(ref pValue); + return true; + + case EETypeElementType.UInt16: + result = (ulong)(long)Unsafe.As(ref pValue); + return true; + + case EETypeElementType.Int32: + result = (ulong)(long)Unsafe.As(ref pValue); + return true; + + case EETypeElementType.UInt32: + result = (ulong)(long)Unsafe.As(ref pValue); + return true; + + case EETypeElementType.Int64: + result = (ulong)(long)Unsafe.As(ref pValue); + return true; + + case EETypeElementType.UInt64: + result = (ulong)(long)Unsafe.As(ref pValue); + return true; + + default: + result = 0; + return false; + } + } + + internal static Type InternalGetUnderlyingType(RuntimeType enumType) + { + Debug.Assert(enumType is RuntimeType); + Debug.Assert(enumType.IsEnum); + + return GetEnumInfo(enumType).UnderlyingType; + } + + public static TEnum[] GetValues() where TEnum : struct, Enum + { + Array values = GetEnumInfo(typeof(TEnum)).ValuesAsUnderlyingType; + TEnum[] result = new TEnum[values.Length]; + Array.Copy(values, result, values.Length); + return result; + } + + // + // Checks if value.GetType() matches enumType exactly. + // + internal static bool ValueTypeMatchesEnumType(Type enumType, object value) + { + EETypePtr enumEEType; + if (!enumType.TryGetEEType(out enumEEType)) + return false; + if (!(enumEEType == value.EETypePtr)) + return false; + return true; + } + + [Conditional("BIGENDIAN")] + private static unsafe void AdjustForEndianness(ref byte* pValue, EETypePtr enumEEType) + { + // On Debug builds, include the big-endian code to help deter bitrot (the "Conditional("BIGENDIAN")" will prevent it from executing on little-endian). + // On Release builds, exclude code to deter IL bloat and toolchain work. +#if BIGENDIAN || DEBUG + EETypeElementType elementType = enumEEType.ElementType; + switch (elementType) + { + case EETypeElementType.SByte: + case EETypeElementType.Byte: + pValue += sizeof(long) - sizeof(byte); + break; + + case EETypeElementType.Int16: + case EETypeElementType.UInt16: + pValue += sizeof(long) - sizeof(short); + break; + + case EETypeElementType.Int32: + case EETypeElementType.UInt32: + pValue += sizeof(long) - sizeof(int); + break; + + case EETypeElementType.Int64: + case EETypeElementType.UInt64: + break; + + default: + throw new NotSupportedException(); + } +#endif //BIGENDIAN || DEBUG + } + + #region ToObject + + internal static unsafe object ToObject(EETypePtr enumEEType, long value) + { + Debug.Assert(enumEEType.IsEnum); + + byte* pValue = (byte*)&value; + AdjustForEndianness(ref pValue, enumEEType); + return RuntimeImports.RhBox(enumEEType, ref *pValue); + } + #endregion + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.CoreRT.Unix.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.CoreRT.Unix.cs new file mode 100644 index 00000000000000..f472c7b2f106ff --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.CoreRT.Unix.cs @@ -0,0 +1,172 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Runtime; +using System.Text; +using System.Threading; + +namespace System +{ + public static partial class Environment + { + internal static int CurrentNativeThreadId => ManagedThreadId.Current; + + private static Dictionary s_environment; + + private static string? GetEnvironmentVariableCore(string variable) + { + Debug.Assert(variable != null); + + if (s_environment == null) + { + return Marshal.PtrToStringAnsi(Interop.Sys.GetEnv(variable)); + } + + lock (s_environment) + { + variable = TrimStringOnFirstZero(variable); + s_environment.TryGetValue(variable, out string? value); + return value; + } + } + + private static void SetEnvironmentVariableCore(string variable, string? value) + { + Debug.Assert(variable != null); + + EnsureEnvironmentCached(); + lock (s_environment) + { + variable = TrimStringOnFirstZero(variable); + value = value == null ? null : TrimStringOnFirstZero(value); + if (string.IsNullOrEmpty(value)) + { + s_environment.Remove(variable); + } + else + { + s_environment[variable] = value; + } + } + } + + public static IDictionary GetEnvironmentVariables() + { + var results = new Hashtable(); + + EnsureEnvironmentCached(); + lock (s_environment) + { + foreach (var keyValuePair in s_environment) + { + results.Add(keyValuePair.Key, keyValuePair.Value); + } + } + + return results; + } + + private static string TrimStringOnFirstZero(string value) + { + int index = value.IndexOf('\0'); + if (index >= 0) + { + return value.Substring(0, index); + } + return value; + } + + private static void EnsureEnvironmentCached() + { + if (s_environment == null) + { + Interlocked.CompareExchange(ref s_environment, GetSystemEnvironmentVariables(), null); + } + } + + private static Dictionary GetSystemEnvironmentVariables() + { + var results = new Dictionary(); + + IntPtr block = Interop.Sys.GetEnviron(); + if (block != IntPtr.Zero) + { + try + { + IntPtr blockIterator = block; + + // Per man page, environment variables come back as an array of pointers to strings + // Parse each pointer of strings individually + while (ParseEntry(blockIterator, out string? key, out string? value)) + { + if (key != null && value != null) + { + try + { + // Add may throw if the environment block was corrupted leading to duplicate entries. + // We allow such throws and eat them (rather than proactively checking for duplication) + // to provide a non-fatal notification about the corruption. + results.Add(key, value); + } + catch (ArgumentException) { } + } + + // Increment to next environment variable entry + blockIterator += IntPtr.Size; + } + } + finally + { + Interop.Sys.FreeEnviron(block); + } + } + + return results; + + // Use a local, unsafe function since we cannot use `yield return` inside of an `unsafe` block + static unsafe bool ParseEntry(IntPtr current, out string? key, out string? value) + { + // Setup + key = null; + value = null; + + // Point to current entry + byte* entry = *(byte**)current; + + // Per man page, "The last pointer in this array has the value NULL" + // Therefore, if entry is null then we're at the end and can bail + if (entry == null) + return false; + + // Parse each byte of the entry until we hit either the separator '=' or '\0'. + // This finds the split point for creating key/value strings below. + // On some old OS, the environment block can be corrupted. + // Some will not have '=', so we need to check for '\0'. + byte* splitpoint = entry; + while (*splitpoint != '=' && *splitpoint != '\0') + splitpoint++; + + // Skip over entries starting with '=' and entries with no value (just a null-terminating char '\0') + if (splitpoint == entry || *splitpoint == '\0') + return true; + + // The key is the bytes from start (0) until our splitpoint + key = new string((sbyte*)entry, 0, checked((int)(splitpoint - entry))); + // The value is the rest of the bytes starting after the splitpoint + value = new string((sbyte*)(splitpoint + 1)); + + return true; + } + } + + [DoesNotReturn] + private static void ExitRaw() => Interop.Sys.Exit(s_latchedExitCode); + + public static long TickCount64 => (long)RuntimeImports.PalGetTickCount64(); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.CoreRT.Win32.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.CoreRT.Win32.cs new file mode 100644 index 00000000000000..b07aa8800028ae --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.CoreRT.Win32.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace System +{ + public static partial class Environment + { + [DoesNotReturn] + private static void ExitRaw() => Interop.Kernel32.ExitProcess(s_latchedExitCode); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.CoreRT.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.CoreRT.Windows.cs new file mode 100644 index 00000000000000..d657b88f64316c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.CoreRT.Windows.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System +{ + public static partial class Environment + { + internal static int CurrentNativeThreadId => unchecked((int)Interop.Kernel32.GetCurrentThreadId()); + + public static long TickCount64 => (long)Interop.Kernel32.GetTickCount64(); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.CoreRT.cs new file mode 100644 index 00000000000000..175cf16e6cc6bb --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.CoreRT.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Threading; +using Internal.DeveloperExperience; +using System.Runtime; + +namespace System +{ + public static partial class Environment + { + public static int CurrentManagedThreadId => ManagedThreadId.Current; + + private static int s_latchedExitCode; + + public static int ExitCode + { + get => s_latchedExitCode; + set => s_latchedExitCode = value; + } + + [DoesNotReturn] + public static void Exit(int exitCode) + { + s_latchedExitCode = exitCode; + ShutdownCore(); + RuntimeImports.RhpShutdown(); + ExitRaw(); + } + + // Note: The CLR's Watson bucketization code looks at the caller of the FCALL method + // to assign blame for crashes. Don't mess with this, such as by making it call + // another managed helper method, unless you consult with some CLR Watson experts. + [DoesNotReturn] + public static void FailFast(string message) => + RuntimeExceptionHelpers.FailFast(message); + + [DoesNotReturn] + public static void FailFast(string message, Exception exception) => + RuntimeExceptionHelpers.FailFast(message, exception); + + internal static void FailFast(string message, Exception exception, string errorSource) + { + // TODO: errorSource originates from CoreCLR (See: https://github.com/dotnet/coreclr/pull/15895) + // For now, we ignore errorSource on CoreRT but we should distinguish the way FailFast prints exception message using errorSource + bool result = DeveloperExperience.Default.OnContractFailure(exception.StackTrace, ContractFailureKind.Assert, message, null, null, null); + if (!result) + { + RuntimeExceptionHelpers.FailFast(message, exception); + } + } + + private static int GetProcessorCount() => Runtime.RuntimeImports.RhGetProcessCpuCount(); + + internal static void ShutdownCore() + { +#if !TARGET_WASM // WASMTODO Be careful what happens here as if the code has called emscripten_set_main_loop then the main loop method will normally be called repeatedly after this method + AppContext.OnProcessExit(); +#endif + } + + public static int TickCount => (int)TickCount64; + + public static string[] GetCommandLineArgs() => (string[])s_commandLineArgs.Clone(); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Exception.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Exception.CoreRT.cs new file mode 100644 index 00000000000000..10fadea9d090be --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Exception.CoreRT.cs @@ -0,0 +1,280 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime; +using System.Runtime.Serialization; +using System.Runtime.InteropServices; + +using MethodBase = System.Reflection.MethodBase; + +namespace System +{ + public partial class Exception + { + public MethodBase? TargetSite + { + [RequiresUnreferencedCode("Metadata for the method might be incomplete or removed")] + get + { + if (!HasBeenThrown) + return null; + + return new StackFrame(_corDbgStackTrace[0], needFileInfo: false).GetMethod(); + } + } + + private IDictionary CreateDataContainer() => new ListDictionaryInternal(); + + private string? SerializationWatsonBuckets => null; + + private string? CreateSourceName() => HasBeenThrown ? "" : null; + + // WARNING: We allow diagnostic tools to directly inspect these three members (_message, _innerException and _HResult) + // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. + // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. + // Get in touch with the diagnostics team if you have questions. + internal string? _message; + private IDictionary? _data; + private Exception? _innerException; + private string? _helpURL; + private string? _source; // Mainly used by VB. + private int _HResult; // HResult + + // To maintain compatibility across runtimes, if this object was deserialized, it will store its stack trace as a string + private string? _stackTraceString; + private string? _remoteStackTraceString; + + internal IntPtr[] GetStackIPs() + { + IntPtr[] ips = new IntPtr[_idxFirstFreeStackTraceEntry]; + if (_corDbgStackTrace != null) + { + Array.Copy(_corDbgStackTrace, ips, ips.Length); + } + return ips; + } + + // WARNING: We allow diagnostic tools to directly inspect these two members (_corDbgStackTrace and _idxFirstFreeStackTraceEntry) + // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. + // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. + // Get in touch with the diagnostics team if you have questions. + + // _corDbgStackTrace: Do not rename: This is for the use of the CorDbg interface. Contains the stack trace as an array of EIP's (ordered from + // most nested call to least.) May also include a few "special" IP's from the SpecialIP class: + private IntPtr[]? _corDbgStackTrace; + private int _idxFirstFreeStackTraceEntry; + + internal static IntPtr EdiSeparator => (IntPtr)1; // Marks a boundary where an ExceptionDispatchInfo rethrew an exception. + + private void AppendStackIP(IntPtr IP, bool isFirstRethrowFrame) + { + if (_idxFirstFreeStackTraceEntry == 0) + { + _corDbgStackTrace = new IntPtr[16]; + } + else if (isFirstRethrowFrame) + { + // For the first frame after rethrow, we replace the last entry in the stack trace with the IP + // of the rethrow. This is overwriting the IP of where control left the corresponding try + // region for the catch that is rethrowing. + _corDbgStackTrace[_idxFirstFreeStackTraceEntry - 1] = IP; + return; + } + + if (_idxFirstFreeStackTraceEntry >= _corDbgStackTrace.Length) + GrowStackTrace(); + + _corDbgStackTrace[_idxFirstFreeStackTraceEntry++] = IP; + } + + private void GrowStackTrace() + { + IntPtr[] newArray = new IntPtr[_corDbgStackTrace.Length * 2]; + for (int i = 0; i < _corDbgStackTrace.Length; i++) + { + newArray[i] = _corDbgStackTrace[i]; + } + _corDbgStackTrace = newArray; + } + + private bool HasBeenThrown => _idxFirstFreeStackTraceEntry != 0; + + private enum RhEHFrameType + { + RH_EH_FIRST_FRAME = 1, + RH_EH_FIRST_RETHROW_FRAME = 2, + } + + [RuntimeExport("AppendExceptionStackFrame")] + private static void AppendExceptionStackFrame(object exceptionObj, IntPtr IP, int flags) + { + // This method is called by the runtime's EH dispatch code and is not allowed to leak exceptions + // back into the dispatcher. + try + { + Exception? ex = exceptionObj as Exception; + if (ex == null) + Environment.FailFast("Exceptions must derive from the System.Exception class"); + + if (!RuntimeExceptionHelpers.SafeToPerformRichExceptionSupport) + return; + + bool isFirstFrame = (flags & (int)RhEHFrameType.RH_EH_FIRST_FRAME) != 0; + bool isFirstRethrowFrame = (flags & (int)RhEHFrameType.RH_EH_FIRST_RETHROW_FRAME) != 0; + + // When we're throwing an exception object, we first need to clear its stacktrace with two exceptions: + // 1. Don't clear if we're rethrowing with `throw;`. + // 2. Don't clear if we're throwing through ExceptionDispatchInfo. + // This is done through invoking RestoreDispatchState which sets the last frame to EdiSeparator followed by throwing normally using `throw ex;`. + if (!isFirstRethrowFrame && isFirstFrame && ex._idxFirstFreeStackTraceEntry > 0 && ex._corDbgStackTrace[ex._idxFirstFreeStackTraceEntry - 1] != EdiSeparator) + ex._idxFirstFreeStackTraceEntry = 0; + + // If out of memory, avoid any calls that may allocate. Otherwise, they may fail + // with another OutOfMemoryException, which may lead to infinite recursion. + bool fatalOutOfMemory = ex == PreallocatedOutOfMemoryException.Instance; + + if (!fatalOutOfMemory) + ex.AppendStackIP(IP, isFirstRethrowFrame); + + // UNIX-TODO: RhpEtwExceptionThrown +#if TARGET_WINDOWS + if (isFirstFrame) + { + string typeName = !fatalOutOfMemory ? ex.GetType().ToString() : "System.OutOfMemoryException"; + string message = !fatalOutOfMemory ? ex.Message : + "Insufficient memory to continue the execution of the program."; + + unsafe + { + fixed (char* exceptionTypeName = typeName, exceptionMessage = message) + RuntimeImports.RhpEtwExceptionThrown(exceptionTypeName, exceptionMessage, IP, ex.HResult); + } + } +#endif + } + catch + { + // We may end up with a confusing stack trace or a confusing ETW trace log, but at least we + // can continue to dispatch this exception. + } + } + + //================================================================================================================== + // Support for ExceptionDispatchInfo class - imports and exports the stack trace. + //================================================================================================================== + + internal DispatchState CaptureDispatchState() + { + IntPtr[]? stackTrace = _corDbgStackTrace; + if (stackTrace != null) + { + IntPtr[] newStackTrace = new IntPtr[stackTrace.Length]; + Array.Copy(stackTrace, 0, newStackTrace, 0, stackTrace.Length); + stackTrace = newStackTrace; + } + return new DispatchState(stackTrace); + } + + internal void RestoreDispatchState(DispatchState DispatchState) + { + IntPtr[] stackTrace = DispatchState.StackTrace; + int idxFirstFreeStackTraceEntry = 0; + if (stackTrace != null) + { + IntPtr[] newStackTrace = new IntPtr[stackTrace.Length + 1]; + Array.Copy(stackTrace, 0, newStackTrace, 0, stackTrace.Length); + stackTrace = newStackTrace; + while (stackTrace[idxFirstFreeStackTraceEntry] != (IntPtr)0) + idxFirstFreeStackTraceEntry++; + stackTrace[idxFirstFreeStackTraceEntry++] = EdiSeparator; + } + + // Since EDI can be created at various points during exception dispatch (e.g. at various frames on the stack) for the same exception instance, + // they can have different data to be restored. Thus, to ensure atomicity of restoration from each EDI, perform the restore under a lock. + lock (s_DispatchStateLock) + { + _corDbgStackTrace = stackTrace; + _idxFirstFreeStackTraceEntry = idxFirstFreeStackTraceEntry; + } + } + + internal readonly struct DispatchState + { + public readonly IntPtr[]? StackTrace; + + public DispatchState(IntPtr[]? stackTrace) + { + StackTrace = stackTrace; + } + } + + // This is the object against which a lock will be taken + // when attempt to restore the EDI. Since its static, its possible + // that unrelated exception object restorations could get blocked + // for a small duration but that sounds reasonable considering + // such scenarios are going to be extremely rare, where timing + // matches precisely. + private static readonly object s_DispatchStateLock = new object(); + + /// + /// This is the binary format for serialized exceptions that get saved into a special buffer that is + /// known to WER (by way of a runtime API) and will be saved into triage dumps. This format is known + /// to SOS, so any changes must update CurrentSerializationVersion and have corresponding updates to + /// SOS. + /// + [StructLayout(LayoutKind.Sequential)] + private struct SERIALIZED_EXCEPTION_HEADER + { + internal IntPtr ExceptionEEType; + internal int HResult; + internal int StackTraceElementCount; + // IntPtr * N : StackTrace elements + } + internal const int CurrentSerializationSignature = 0x31305845; // 'EX01' + + /// + /// This method performs the serialization of one Exception object into the returned byte[]. + /// + internal unsafe byte[] SerializeForDump() + { + checked + { + int nStackTraceElements = _idxFirstFreeStackTraceEntry; + int cbBuffer = sizeof(SERIALIZED_EXCEPTION_HEADER) + (nStackTraceElements * IntPtr.Size); + + byte[] buffer = new byte[cbBuffer]; + fixed (byte* pBuffer = &buffer[0]) + { + SERIALIZED_EXCEPTION_HEADER* pHeader = (SERIALIZED_EXCEPTION_HEADER*)pBuffer; + pHeader->HResult = _HResult; + pHeader->ExceptionEEType = (IntPtr)MethodTable; + pHeader->StackTraceElementCount = nStackTraceElements; + IntPtr* pStackTraceElements = (IntPtr*)(pBuffer + sizeof(SERIALIZED_EXCEPTION_HEADER)); + for (int i = 0; i < nStackTraceElements; i++) + { + pStackTraceElements[i] = _corDbgStackTrace[i]; + } + } + + return buffer; + } + } + + // Returns true if setting the _remoteStackTraceString field is legal, false if not (immutable exception). + // A false return value means the caller should early-exit the operation. + // Can also throw InvalidOperationException if a stack trace is already set or if object has been thrown. + private bool CanSetRemoteStackTrace() + { + // Check to see if the exception already has a stack set in it. + if (HasBeenThrown || _stackTraceString != null || _remoteStackTraceString != null) + { + ThrowHelper.ThrowInvalidOperationException(); + } + + return true; // CoreRT runtime doesn't have immutable agile exceptions, always return true + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.cs new file mode 100644 index 00000000000000..a01d78f9f07b07 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.cs @@ -0,0 +1,763 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Exposes features of the Garbage Collector to managed code. +// + +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +using Internal.Runtime.CompilerServices; +using Internal.Runtime; + +namespace System +{ + // !!!!!!!!!!!!!!!!!!!!!!! + // Make sure you change the def in rtu\gc.h if you change this! + public enum GCCollectionMode + { + Default = 0, + Forced = 1, + Optimized = 2 + } + + public enum GCNotificationStatus + { + Succeeded = 0, + Failed = 1, + Canceled = 2, + Timeout = 3, + NotApplicable = 4 + } + + internal enum InternalGCCollectionMode + { + NonBlocking = 0x00000001, + Blocking = 0x00000002, + Optimized = 0x00000004, + Compacting = 0x00000008, + } + + internal enum StartNoGCRegionStatus + { + Succeeded = 0, + NotEnoughMemory = 1, + AmountTooLarge = 2, + AlreadyInProgress = 3 + } + + internal enum EndNoGCRegionStatus + { + Succeeded = 0, + NotInProgress = 1, + GCInduced = 2, + AllocationExceeded = 3 + } + + public static class GC + { + public static int GetGeneration(object obj) + { + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + + return RuntimeImports.RhGetGeneration(obj); + } + + /// + /// Returns the current generation number of the target + /// of a specified . + /// + /// The WeakReference whose target is the object + /// whose generation will be returned + /// The generation of the target of the WeakReference + /// The target of the weak reference + /// has already been garbage collected. + public static int GetGeneration(WeakReference wo) + { + // note - this throws an NRE if given a null weak reference. This isn't + // documented, but it's the behavior of Desktop and CoreCLR. + object handleRef = RuntimeImports.RhHandleGet(wo.m_handle); + if (handleRef == null) + { + throw new ArgumentNullException(nameof(wo)); + } + + int result = RuntimeImports.RhGetGeneration(handleRef); + KeepAlive(wo); + return result; + } + + // Forces a collection of all generations from 0 through Generation. + public static void Collect(int generation) + { + Collect(generation, GCCollectionMode.Default); + } + + // Garbage collect all generations. + public static void Collect() + { + //-1 says to GC all generations. + RuntimeImports.RhCollect(-1, InternalGCCollectionMode.Blocking); + } + + public static void Collect(int generation, GCCollectionMode mode) + { + Collect(generation, mode, true); + } + + public static void Collect(int generation, GCCollectionMode mode, bool blocking) + { + Collect(generation, mode, blocking, false); + } + + public static void Collect(int generation, GCCollectionMode mode, bool blocking, bool compacting) + { + if (generation < 0) + { + throw new ArgumentOutOfRangeException(nameof(generation), SR.ArgumentOutOfRange_GenericPositive); + } + + if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Optimized)) + { + throw new ArgumentOutOfRangeException(nameof(mode), SR.ArgumentOutOfRange_Enum); + } + + int iInternalModes = 0; + + if (mode == GCCollectionMode.Optimized) + { + iInternalModes |= (int)InternalGCCollectionMode.Optimized; + } + + if (compacting) + { + iInternalModes |= (int)InternalGCCollectionMode.Compacting; + } + + if (blocking) + { + iInternalModes |= (int)InternalGCCollectionMode.Blocking; + } + else if (!compacting) + { + iInternalModes |= (int)InternalGCCollectionMode.NonBlocking; + } + + RuntimeImports.RhCollect(generation, (InternalGCCollectionMode)iInternalModes); + } + + /// + /// Specifies that a garbage collection notification should be raised when conditions are favorable + /// for a full garbage collection and when the collection has been completed. + /// + /// A number between 1 and 99 that specifies when the notification + /// should be raised based on the objects allocated in Gen 2. + /// A number between 1 and 99 that specifies when the notification + /// should be raised based on the objects allocated in the large object heap. + /// If either of the two arguments are not between 1 and 99 + /// If Concurrent GC is enabled" + public static void RegisterForFullGCNotification(int maxGenerationThreshold, int largeObjectHeapThreshold) + { + if (maxGenerationThreshold < 1 || maxGenerationThreshold > 99) + { + throw new ArgumentOutOfRangeException( + nameof(maxGenerationThreshold), + string.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, 1, 99)); + } + + if (largeObjectHeapThreshold < 1 || largeObjectHeapThreshold > 99) + { + throw new ArgumentOutOfRangeException( + nameof(largeObjectHeapThreshold), + string.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, 1, 99)); + } + + // This is not documented on MSDN, but CoreCLR throws when the GC's + // RegisterForFullGCNotification returns false + if (!RuntimeImports.RhRegisterForFullGCNotification(maxGenerationThreshold, largeObjectHeapThreshold)) + { + throw new InvalidOperationException(SR.InvalidOperation_NotWithConcurrentGC); + } + } + + /// + /// Returns the status of a registered notification about whether a blocking garbage collection + /// is imminent. May wait indefinitely for a full collection. + /// + /// The status of a registered full GC notification + public static GCNotificationStatus WaitForFullGCApproach() + { + return (GCNotificationStatus)RuntimeImports.RhWaitForFullGCApproach(-1); + } + + /// + /// Returns the status of a registered notification about whether a blocking garbage collection + /// is imminent. May wait up to a given timeout for a full collection. + /// + /// The timeout on waiting for a full collection + /// The status of a registered full GC notification + public static GCNotificationStatus WaitForFullGCApproach(int millisecondsTimeout) + { + if (millisecondsTimeout < -1) + { + throw new ArgumentOutOfRangeException( + nameof(millisecondsTimeout), + SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + } + + return (GCNotificationStatus)RuntimeImports.RhWaitForFullGCApproach(millisecondsTimeout); + } + + /// + /// Returns the status of a registered notification about whether a blocking garbage collection + /// has completed. May wait indefinitely for a full collection. + /// + /// The status of a registered full GC notification + public static GCNotificationStatus WaitForFullGCComplete() + { + return (GCNotificationStatus)RuntimeImports.RhWaitForFullGCComplete(-1); + } + + /// + /// Returns the status of a registered notification about whether a blocking garbage collection + /// has completed. May wait up to a specified timeout for a full collection. + /// + /// The timeout on waiting for a full collection + /// + public static GCNotificationStatus WaitForFullGCComplete(int millisecondsTimeout) + { + if (millisecondsTimeout < -1) + { + throw new ArgumentOutOfRangeException( + nameof(millisecondsTimeout), + SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + } + + return (GCNotificationStatus)RuntimeImports.RhWaitForFullGCComplete(millisecondsTimeout); + } + + /// + /// Cancels an outstanding full GC notification. + /// + /// Raised if called + /// with concurrent GC enabled + public static void CancelFullGCNotification() + { + if (!RuntimeImports.RhCancelFullGCNotification()) + { + throw new InvalidOperationException(SR.InvalidOperation_NotWithConcurrentGC); + } + } + + /// + /// Attempts to disallow garbage collection during execution of a critical path. + /// + /// Disallows garbage collection if a specified amount of + /// of memory is available. + /// True if the disallowing of garbage collection was successful, False otherwise + /// If the amount of memory requested + /// is too large for the GC to accommodate + /// If the GC is already in a NoGCRegion + public static bool TryStartNoGCRegion(long totalSize) + { + return StartNoGCRegionWorker(totalSize, false, 0, false); + } + + /// + /// Attempts to disallow garbage collection during execution of a critical path. + /// + /// Disallows garbage collection if a specified amount of + /// of memory is available. + /// Disallows garbagte collection if a specified amount of + /// large object heap space is available. + /// True if the disallowing of garbage collection was successful, False otherwise + /// If the amount of memory requested + /// is too large for the GC to accomodate + /// If the GC is already in a NoGCRegion + public static bool TryStartNoGCRegion(long totalSize, long lohSize) + { + return StartNoGCRegionWorker(totalSize, true, lohSize, false); + } + + /// + /// Attempts to disallow garbage collection during execution of a critical path. + /// + /// Disallows garbage collection if a specified amount of + /// of memory is available. + /// Controls whether or not a full blocking GC + /// is performed if the requested amount of memory is not available + /// True if the disallowing of garbage collection was successful, False otherwise + /// If the amount of memory requested + /// is too large for the GC to accomodate + /// If the GC is already in a NoGCRegion + public static bool TryStartNoGCRegion(long totalSize, bool disallowFullBlockingGC) + { + return StartNoGCRegionWorker(totalSize, false, 0, disallowFullBlockingGC); + } + + /// + /// Attempts to disallow garbage collection during execution of a critical path. + /// + /// Disallows garbage collection if a specified amount of + /// of memory is available. + /// Disallows garbagte collection if a specified amount of + /// large object heap space is available. + /// Controls whether or not a full blocking GC + /// is performed if the requested amount of memory is not available + /// True if the disallowing of garbage collection was successful, False otherwise + /// If the amount of memory requested + /// is too large for the GC to accomodate + /// If the GC is already in a NoGCRegion + public static bool TryStartNoGCRegion(long totalSize, long lohSize, bool disallowFullBlockingGC) + { + return StartNoGCRegionWorker(totalSize, true, lohSize, disallowFullBlockingGC); + } + + private static bool StartNoGCRegionWorker(long totalSize, bool hasLohSize, long lohSize, bool disallowFullBlockingGC) + { + if (totalSize <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(totalSize), + SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(totalSize))); + } + + if (hasLohSize) + { + if (lohSize <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(lohSize), + SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(lohSize))); + } + + if (lohSize > totalSize) + { + throw new ArgumentOutOfRangeException(nameof(lohSize), SR.ArgumentOutOfRange_NoGCLohSizeGreaterTotalSize); + } + } + + StartNoGCRegionStatus status = + (StartNoGCRegionStatus)RuntimeImports.RhStartNoGCRegion(totalSize, hasLohSize, lohSize, disallowFullBlockingGC); + switch (status) + { + case StartNoGCRegionStatus.NotEnoughMemory: + return false; + case StartNoGCRegionStatus.AlreadyInProgress: + throw new InvalidOperationException(SR.InvalidOperationException_AlreadyInNoGCRegion); + case StartNoGCRegionStatus.AmountTooLarge: + throw new ArgumentOutOfRangeException(nameof(totalSize), SR.ArgumentOutOfRangeException_NoGCRegionSizeTooLarge); + } + + Debug.Assert(status == StartNoGCRegionStatus.Succeeded); + return true; + } + + /// + /// Exits the current no GC region. + /// + /// If the GC is not in a no GC region + /// If the no GC region was exited due to an induced GC + /// If the no GC region was exited due to memory allocations + /// exceeding the amount given to + public static void EndNoGCRegion() + { + EndNoGCRegionStatus status = (EndNoGCRegionStatus)RuntimeImports.RhEndNoGCRegion(); + if (status == EndNoGCRegionStatus.NotInProgress) + { + throw new InvalidOperationException( + SR.InvalidOperationException_NoGCRegionNotInProgress); + } + else if (status == EndNoGCRegionStatus.GCInduced) + { + throw new InvalidOperationException( + SR.InvalidOperationException_NoGCRegionInduced); + } + else if (status == EndNoGCRegionStatus.AllocationExceeded) + { + throw new InvalidOperationException( + SR.InvalidOperationException_NoGCRegionAllocationExceeded); + } + } + + // Block until the next finalization pass is complete. + public static void WaitForPendingFinalizers() + { + RuntimeImports.RhWaitForPendingFinalizers(Thread.ReentrantWaitsEnabled); + } + + public static void SuppressFinalize(object obj) + { + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + + RuntimeImports.RhSuppressFinalize(obj); + } + + public static void ReRegisterForFinalize(object obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + RuntimeImports.RhReRegisterForFinalize(obj); + } + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations + public static void KeepAlive(object? obj) + { + } + + // Returns the maximum GC generation. Currently assumes only 1 heap. + // + public static int MaxGeneration + { + get { return RuntimeImports.RhGetMaxGcGeneration(); } + } + + public static int CollectionCount(int generation) + { + if (generation < 0) + throw new ArgumentOutOfRangeException(nameof(generation), SR.ArgumentOutOfRange_GenericPositive); + + return RuntimeImports.RhGetGcCollectionCount(generation, false); + } + + // Support for AddMemoryPressure and RemoveMemoryPressure below. + private const uint PressureCount = 4; +#if TARGET_64BIT + private const uint MinGCMemoryPressureBudget = 4 * 1024 * 1024; +#else + private const uint MinGCMemoryPressureBudget = 3 * 1024 * 1024; +#endif + + private const uint MaxGCMemoryPressureRatio = 10; + + private static int[] s_gcCounts = new int[] { 0, 0, 0 }; + + private static long[] s_addPressure = new long[] { 0, 0, 0, 0 }; + private static long[] s_removePressure = new long[] { 0, 0, 0, 0 }; + private static uint s_iteration; + + /// + /// Resets the pressure accounting after a gen2 GC has occured. + /// + private static void CheckCollectionCount() + { + if (s_gcCounts[2] != CollectionCount(2)) + { + for (int i = 0; i < 3; i++) + { + s_gcCounts[i] = CollectionCount(i); + } + + s_iteration++; + + uint p = s_iteration % PressureCount; + + s_addPressure[p] = 0; + s_removePressure[p] = 0; + } + } + + private static long InterlockedAddMemoryPressure(ref long pAugend, long addend) + { + long oldMemValue; + long newMemValue; + + do + { + oldMemValue = pAugend; + newMemValue = oldMemValue + addend; + + // check for overflow + if (newMemValue < oldMemValue) + { + newMemValue = long.MaxValue; + } + } while (Interlocked.CompareExchange(ref pAugend, newMemValue, oldMemValue) != oldMemValue); + + return newMemValue; + } + + /// + /// New AddMemoryPressure implementation (used by RCW and the CLRServicesImpl class) + /// 1. Less sensitive than the original implementation (start budget 3 MB) + /// 2. Focuses more on newly added memory pressure + /// 3. Budget adjusted by effectiveness of last 3 triggered GC (add / remove ratio, max 10x) + /// 4. Budget maxed with 30% of current managed GC size + /// 5. If Gen2 GC is happening naturally, ignore past pressure + /// + /// Here's a brief description of the ideal algorithm for Add/Remove memory pressure: + /// Do a GC when (HeapStart is less than X * MemPressureGrowth) where + /// - HeapStart is GC Heap size after doing the last GC + /// - MemPressureGrowth is the net of Add and Remove since the last GC + /// - X is proportional to our guess of the ummanaged memory death rate per GC interval, + /// and would be calculated based on historic data using standard exponential approximation: + /// Xnew = UMDeath/UMTotal * 0.5 + Xprev + /// + /// + public static void AddMemoryPressure(long bytesAllocated) + { + if (bytesAllocated <= 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesAllocated), + SR.ArgumentOutOfRange_NeedPosNum); + } + +#if !TARGET_64BIT + if (bytesAllocated > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(bytesAllocated), + SR.ArgumentOutOfRange_MustBeNonNegInt32); + } +#endif + + CheckCollectionCount(); + uint p = s_iteration % PressureCount; + long newMemValue = InterlockedAddMemoryPressure(ref s_addPressure[p], bytesAllocated); + + Debug.Assert(PressureCount == 4, "GC.AddMemoryPressure contains unrolled loops which depend on the PressureCount"); + + if (newMemValue >= MinGCMemoryPressureBudget) + { + long add = s_addPressure[0] + s_addPressure[1] + s_addPressure[2] + s_addPressure[3] - s_addPressure[p]; + long rem = s_removePressure[0] + s_removePressure[1] + s_removePressure[2] + s_removePressure[3] - s_removePressure[p]; + + long budget = MinGCMemoryPressureBudget; + + if (s_iteration >= PressureCount) // wait until we have enough data points + { + // Adjust according to effectiveness of GC + // Scale budget according to past m_addPressure / m_remPressure ratio + if (add >= rem * MaxGCMemoryPressureRatio) + { + budget = MinGCMemoryPressureBudget * MaxGCMemoryPressureRatio; + } + else if (add > rem) + { + Debug.Assert(rem != 0); + + // Avoid overflow by calculating addPressure / remPressure as fixed point (1 = 1024) + budget = (add * 1024 / rem) * budget / 1024; + } + } + + // If still over budget, check current managed heap size + if (newMemValue >= budget) + { + long heapOver3 = RuntimeImports.RhGetCurrentObjSize() / 3; + + if (budget < heapOver3) //Max + { + budget = heapOver3; + } + + if (newMemValue >= budget) + { + // last check - if we would exceed 20% of GC "duty cycle", do not trigger GC at this time + if ((RuntimeImports.RhGetGCNow() - RuntimeImports.RhGetLastGCStartTime(2)) > (RuntimeImports.RhGetLastGCDuration(2) * 5)) + { + RuntimeImports.RhCollect(2, InternalGCCollectionMode.NonBlocking); + CheckCollectionCount(); + } + } + } + } + } + + public static void RemoveMemoryPressure(long bytesAllocated) + { + if (bytesAllocated <= 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesAllocated), + SR.ArgumentOutOfRange_NeedPosNum); + } + +#if !TARGET_64BIT + if (bytesAllocated > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(bytesAllocated), + SR.ArgumentOutOfRange_MustBeNonNegInt32); + } +#endif + + CheckCollectionCount(); + uint p = s_iteration % PressureCount; + InterlockedAddMemoryPressure(ref s_removePressure[p], bytesAllocated); + } + + public static long GetTotalMemory(bool forceFullCollection) + { + long size = RuntimeImports.RhGetGcTotalMemory(); + + if (forceFullCollection) + { + // If we force a full collection, we will run the finalizers on all + // existing objects and do a collection until the value stabilizes. + // The value is "stable" when either the value is within 5% of the + // previous call to GetTotalMemory, or if we have been sitting + // here for more than x times (we don't want to loop forever here). + int reps = 20; // Number of iterations + + long diff; + + do + { + GC.WaitForPendingFinalizers(); + GC.Collect(); + + long newSize = RuntimeImports.RhGetGcTotalMemory(); + diff = (newSize - size) * 100 / size; + size = newSize; + } + while (reps-- > 0 && !(-5 < diff && diff < 5)); + } + + return size; + } + + private static IntPtr _RegisterFrozenSegment(IntPtr sectionAddress, IntPtr sectionSize) + { + return RuntimeImports.RhpRegisterFrozenSegment(sectionAddress, sectionSize); + } + + private static void _UnregisterFrozenSegment(IntPtr segmentHandle) + { + RuntimeImports.RhpUnregisterFrozenSegment(segmentHandle); + } + + public static long GetAllocatedBytesForCurrentThread() + { + return RuntimeImports.RhGetAllocatedBytesForCurrentThread(); + } + + public static long GetTotalAllocatedBytes(bool precise = false) + { + return precise ? RuntimeImports.RhGetTotalAllocatedBytesPrecise() : RuntimeImports.RhGetTotalAllocatedBytes(); + } + + /// Gets garbage collection memory information. + /// An object that contains information about the garbage collector's memory usage. + public static GCMemoryInfo GetGCMemoryInfo() => GetGCMemoryInfo(GCKind.Any); + + /// Gets garbage collection memory information. + /// The kind of collection for which to retrieve memory information. + /// An object that contains information about the garbage collector's memory usage. + public static GCMemoryInfo GetGCMemoryInfo(GCKind kind) + { + if ((kind < GCKind.Any) || (kind > GCKind.Background)) + { + throw new ArgumentOutOfRangeException(nameof(kind), + SR.Format( + SR.ArgumentOutOfRange_Bounds_Lower_Upper, + GCKind.Any, + GCKind.Background)); + } + + var data = new GCMemoryInfoData(); + RuntimeImports.RhGetMemoryInfo(ref data.GetRawData(), kind); + return new GCMemoryInfo(data); + } + + internal static ulong GetSegmentSize() + { + return RuntimeImports.RhGetGCSegmentSize(); + } + + /// + /// Allocate an array while skipping zero-initialization if possible. + /// + /// Specifies the type of the array element. + /// Specifies the length of the array. + /// Specifies whether the allocated array must be pinned. + /// + /// If pinned is set to true, must not be a reference type or a type that contains object references. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] // forced to ensure no perf drop for small memory buffers (hot path) + public static unsafe T[] AllocateUninitializedArray(int length, bool pinned = false) + { + if (!pinned) + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + return new T[length]; + } + + // for debug builds we always want to call AllocateNewArray to detect AllocateNewArray bugs +#if !DEBUG + // small arrays are allocated using `new[]` as that is generally faster. + if (length < 2048 / Unsafe.SizeOf()) + { + return new T[length]; + } +#endif + } + else if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + } + + // kept outside of the small arrays hot path to have inlining without big size growth + return AllocateNewUninitializedArray(length, pinned); + + static T[] AllocateNewUninitializedArray(int length, bool pinned) + { + GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_ZEROING_OPTIONAL; + if (pinned) + flags |= GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP; + + if (length < 0) + throw new OverflowException(); + + T[]? array = null; + RuntimeImports.RhAllocateNewArray(EETypePtr.EETypePtrOf().RawValue, (uint)length, (uint)flags, Unsafe.AsPointer(ref array)); + if (array == null) + throw new OutOfMemoryException(); + + return array; + } + } + + /// + /// Allocate an array. + /// + /// Specifies the type of the array element. + /// Specifies the length of the array. + /// Specifies whether the allocated array must be pinned. + /// + /// If pinned is set to true, must not be a reference type or a type that contains object references. + /// + public static unsafe T[] AllocateArray(int length, bool pinned = false) + { + GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS; + + if (pinned) + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + + flags = GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP; + } + + if (length < 0) + throw new OverflowException(); + + T[]? array = null; + RuntimeImports.RhAllocateNewArray(EETypePtr.EETypePtrOf().RawValue, (uint)length, (uint)flags, Unsafe.AsPointer(ref array)); + if (array == null) + throw new OutOfMemoryException(); + + return array; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Helpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Helpers.cs new file mode 100644 index 00000000000000..23a17a0e6702c5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Helpers.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Implements System.Type +// + +using System; +using Internal.Runtime.Augments; + +namespace System +{ + internal static class Helpers + { + public static bool TryGetEEType(this Type type, out EETypePtr eeType) + { + RuntimeTypeHandle typeHandle = RuntimeAugments.Callbacks.GetTypeHandleIfAvailable(type); + if (typeHandle.IsNull) + { + eeType = default(EETypePtr); + return false; + } + eeType = typeHandle.ToEETypePtr(); + return true; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/IO/FileLoadException.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/IO/FileLoadException.CoreRT.cs new file mode 100644 index 00000000000000..4d90a89afafcc5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/IO/FileLoadException.CoreRT.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.IO +{ + public partial class FileLoadException + { + internal static string FormatFileLoadExceptionMessage(string? fileName, int hResult) + { + return fileName == null ? SR.IO_FileLoad : SR.Format(SR.IO_FileLoad_FileName, fileName); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs new file mode 100644 index 00000000000000..98ae0f132d9791 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs @@ -0,0 +1,688 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Diagnostics; + +using Internal.Reflection.Core.NonPortable; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using EETypeElementType = Internal.Runtime.EETypeElementType; +using Interlocked = System.Threading.Interlocked; + +namespace System +{ + [System.Runtime.CompilerServices.ReflectionBlocked] + public static class InvokeUtils + { + // + // Various reflection scenarios (Array.SetValue(), reflection Invoke, delegate DynamicInvoke and FieldInfo.Set()) perform + // automatic conveniences such as automatically widening primitive types to fit the destination type. + // + // This method attempts to collect as much of that logic as possible in place. (This may not be completely possible + // as the desktop CLR is not particularly consistent across all these scenarios either.) + // + // The transforms supported are: + // + // Value-preserving widenings of primitive integrals and floats. + // Enums can be converted to the same or wider underlying primitive. + // Primitives can be converted to an enum with the same or wider underlying primitive. + // + // null converted to default(T) (this is important when T is a valuetype.) + // + // There is also another transform of T -> Nullable. This method acknowledges that rule but does not actually transform the T. + // Rather, the transformation happens naturally when the caller unboxes the value to its final destination. + // + // This method is targeted by the Delegate ILTransformer. + // + // + public static object? CheckArgument(object? srcObject, RuntimeTypeHandle dstType, BinderBundle? binderBundle) + { + EETypePtr dstEEType = dstType.ToEETypePtr(); + return CheckArgument(srcObject, dstEEType, CheckArgumentSemantics.DynamicInvoke, binderBundle, ref Unsafe.NullRef()); + } + + // This option tweaks the coercion rules to match classic inconsistencies. + internal enum CheckArgumentSemantics + { + ArraySet, // Throws InvalidCastException + DynamicInvoke, // Throws ArgumentException + SetFieldDirect, // Throws ArgumentException - other than that, like DynamicInvoke except that enums and integers cannot be intermingled, and null cannot substitute for default(valuetype). + } + + internal static object? CheckArgument(object? srcObject, EETypePtr dstEEType, CheckArgumentSemantics semantics, BinderBundle? binderBundle) + { + return CheckArgument(srcObject, dstEEType, semantics, binderBundle, ref Unsafe.NullRef()); + } + + internal static object? CheckArgument(object? srcObject, EETypePtr dstEEType, CheckArgumentSemantics semantics, BinderBundle? binderBundle, ref ArgSetupState argSetupState) + { + // Methods with ByRefLike types in signatures should be filtered out by the compiler + Debug.Assert(!dstEEType.IsByRefLike); + + if (srcObject == null) + { + // null -> default(T) + if (dstEEType.IsPointer) + { + return default(IntPtr); + } + else if (dstEEType.IsValueType && !dstEEType.IsNullable) + { + if (semantics == CheckArgumentSemantics.SetFieldDirect) + throw CreateChangeTypeException(typeof(object).TypeHandle.ToEETypePtr(), dstEEType, semantics); + return Runtime.RuntimeImports.RhNewObject(dstEEType); + } + else + { + return null; + } + } + else + { + EETypePtr srcEEType = srcObject.EETypePtr; + + if (RuntimeImports.AreTypesAssignable(srcEEType, dstEEType)) + return srcObject; + + if (dstEEType.IsInterface) + { + if (srcObject is Runtime.InteropServices.IDynamicInterfaceCastable castable + && castable.IsInterfaceImplemented(new RuntimeTypeHandle(dstEEType), throwIfNotImplemented: false)) + return srcObject; + } + + object dstObject; + Exception exception = ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(srcObject, srcEEType, dstEEType, semantics, out dstObject); + if (exception == null) + return dstObject; + + if (binderBundle == null) + throw exception; + + // Our normal coercion rules could not convert the passed in argument but we were supplied a custom binder. See if it can do it. + Type exactDstType; + if (Unsafe.IsNullRef(ref argSetupState)) + { + // We were called by someone other than DynamicInvokeParamHelperCore(). Those callers pass the correct dstEEType. + exactDstType = Type.GetTypeFromHandle(new RuntimeTypeHandle(dstEEType))!; + } + else + { + // We were called by DynamicInvokeParamHelperCore(). He passes a dstEEType that enums folded to int and possibly other adjustments. A custom binder + // is app code however and needs the exact type. + exactDstType = GetExactTypeForCustomBinder(argSetupState); + } + + srcObject = binderBundle.ChangeType(srcObject, exactDstType); + + // For compat with desktop, the result of the binder call gets processed through the default rules again. + dstObject = CheckArgument(srcObject, dstEEType, semantics, binderBundle: null, ref Unsafe.NullRef()); + return dstObject; + } + } + + // Special coersion rules for primitives, enums and pointer. + private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(object srcObject, EETypePtr srcEEType, EETypePtr dstEEType, CheckArgumentSemantics semantics, out object dstObject) + { + if (semantics == CheckArgumentSemantics.SetFieldDirect && (srcEEType.IsEnum || dstEEType.IsEnum)) + { + dstObject = null; + return CreateChangeTypeException(srcEEType, dstEEType, semantics); + } + + if (dstEEType.IsPointer) + { + Exception exception = ConvertPointerIfPossible(srcObject, srcEEType, dstEEType, semantics, out IntPtr dstIntPtr); + if (exception != null) + { + dstObject = null; + return exception; + } + dstObject = dstIntPtr; + return null; + } + + if (!(srcEEType.IsPrimitive && dstEEType.IsPrimitive)) + { + dstObject = null; + return CreateChangeTypeException(srcEEType, dstEEType, semantics); + } + + CorElementType dstCorElementType = dstEEType.CorElementType; + if (!srcEEType.CorElementTypeInfo.CanWidenTo(dstCorElementType)) + { + dstObject = null; + return CreateChangeTypeArgumentException(srcEEType, dstEEType); + } + + switch (dstCorElementType) + { + case CorElementType.ELEMENT_TYPE_BOOLEAN: + bool boolValue = Convert.ToBoolean(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, boolValue ? 1 : 0) : boolValue; + break; + + case CorElementType.ELEMENT_TYPE_CHAR: + char charValue = Convert.ToChar(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, charValue) : charValue; + break; + + case CorElementType.ELEMENT_TYPE_I1: + sbyte sbyteValue = Convert.ToSByte(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, sbyteValue) : sbyteValue; + break; + + case CorElementType.ELEMENT_TYPE_I2: + short shortValue = Convert.ToInt16(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, shortValue) : shortValue; + break; + + case CorElementType.ELEMENT_TYPE_I4: + int intValue = Convert.ToInt32(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, intValue) : intValue; + break; + + case CorElementType.ELEMENT_TYPE_I8: + long longValue = Convert.ToInt64(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, longValue) : longValue; + break; + + case CorElementType.ELEMENT_TYPE_U1: + byte byteValue = Convert.ToByte(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, byteValue) : byteValue; + break; + + case CorElementType.ELEMENT_TYPE_U2: + ushort ushortValue = Convert.ToUInt16(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, ushortValue) : ushortValue; + break; + + case CorElementType.ELEMENT_TYPE_U4: + uint uintValue = Convert.ToUInt32(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, uintValue) : uintValue; + break; + + case CorElementType.ELEMENT_TYPE_U8: + ulong ulongValue = Convert.ToUInt64(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, (long)ulongValue) : ulongValue; + break; + + case CorElementType.ELEMENT_TYPE_R4: + if (srcEEType.CorElementType == CorElementType.ELEMENT_TYPE_CHAR) + { + dstObject = (float)(char)srcObject; + } + else + { + dstObject = Convert.ToSingle(srcObject); + } + break; + + case CorElementType.ELEMENT_TYPE_R8: + if (srcEEType.CorElementType == CorElementType.ELEMENT_TYPE_CHAR) + { + dstObject = (double)(char)srcObject; + } + else + { + dstObject = Convert.ToDouble(srcObject); + } + break; + + default: + Debug.Fail("Unexpected CorElementType: " + dstCorElementType + ": Not a valid widening target."); + dstObject = null; + return CreateChangeTypeException(srcEEType, dstEEType, semantics); + } + + Debug.Assert(dstObject.EETypePtr == dstEEType); + return null; + } + + private static Exception ConvertPointerIfPossible(object srcObject, EETypePtr srcEEType, EETypePtr dstEEType, CheckArgumentSemantics semantics, out IntPtr dstIntPtr) + { + if (srcObject is IntPtr srcIntPtr) + { + dstIntPtr = srcIntPtr; + return null; + } + + if (srcObject is Pointer srcPointer) + { + if (dstEEType == typeof(void*).TypeHandle.ToEETypePtr() || RuntimeImports.AreTypesAssignable(pSourceType: srcPointer.GetPointerType().TypeHandle.ToEETypePtr(), pTargetType: dstEEType)) + { + dstIntPtr = srcPointer.GetPointerValue(); + return null; + } + } + + dstIntPtr = IntPtr.Zero; + return CreateChangeTypeException(srcEEType, dstEEType, semantics); + } + + private static Exception CreateChangeTypeException(EETypePtr srcEEType, EETypePtr dstEEType, CheckArgumentSemantics semantics) + { + switch (semantics) + { + case CheckArgumentSemantics.DynamicInvoke: + case CheckArgumentSemantics.SetFieldDirect: + return CreateChangeTypeArgumentException(srcEEType, dstEEType); + case CheckArgumentSemantics.ArraySet: + return CreateChangeTypeInvalidCastException(srcEEType, dstEEType); + default: + Debug.Fail("Unexpected CheckArgumentSemantics value: " + semantics); + throw new InvalidOperationException(); + } + } + + private static ArgumentException CreateChangeTypeArgumentException(EETypePtr srcEEType, EETypePtr dstEEType) + { + return new ArgumentException(SR.Format(SR.Arg_ObjObjEx, Type.GetTypeFromHandle(new RuntimeTypeHandle(srcEEType)), Type.GetTypeFromHandle(new RuntimeTypeHandle(dstEEType)))); + } + + private static InvalidCastException CreateChangeTypeInvalidCastException(EETypePtr srcEEType, EETypePtr dstEEType) + { + return new InvalidCastException(SR.InvalidCast_StoreArrayElement); + } + + // ----------------------------------------------- + // Infrastructure and logic for Dynamic Invocation + // ----------------------------------------------- + public enum DynamicInvokeParamType + { + In = 0, + Ref = 1 + } + + public enum DynamicInvokeParamLookupType + { + ValuetypeObjectReturned = 0, + IndexIntoObjectArrayReturned = 1, + } + + public struct ArgSetupState + { + public bool fComplete; + public object?[]? parameters; + public object[] nullableCopyBackObjects; + public int curIndex; + public object targetMethodOrDelegate; + public BinderBundle? binderBundle; + public object?[] customBinderProvidedParameters; + } + + private static object GetDefaultValue(object targetMethodOrDelegate, RuntimeTypeHandle thType, int argIndex) + { + if (targetMethodOrDelegate == null) + { + throw new ArgumentException(SR.Arg_DefaultValueMissingException); + } + + bool hasDefaultValue = RuntimeAugments.Callbacks.TryGetDefaultParameterValue(targetMethodOrDelegate, thType, argIndex, out object defaultValue); + if (!hasDefaultValue) + { + throw new ArgumentException(SR.Arg_DefaultValueMissingException, "parameters"); + } + + // Note that we might return null even for value types which cannot have null value here. + // This case is handled in the CheckArgument method which is called after this one on the returned parameter value. + return defaultValue; + } + + // This is only called if we have to invoke a custom binder to coerce a parameter type. It leverages s_targetMethodOrDelegate to retrieve + // the unaltered parameter type to pass to the binder. + private static Type GetExactTypeForCustomBinder(in ArgSetupState argSetupState) + { + Debug.Assert(argSetupState.binderBundle != null && argSetupState.targetMethodOrDelegate is MethodBase); + MethodBase method = (MethodBase)argSetupState.targetMethodOrDelegate; + + // DynamicInvokeParamHelperCore() increments s_curIndex before calling us - that's why we have to subtract 1. + return method.GetParametersNoCopy()[argSetupState.curIndex - 1].ParameterType; + } + + [DebuggerGuidedStepThroughAttribute] + internal static unsafe object CallDynamicInvokeMethod( + object thisPtr, + IntPtr methodToCall, + IntPtr dynamicInvokeHelperMethod, + IntPtr dynamicInvokeHelperGenericDictionary, + object targetMethodOrDelegate, + object[]? parameters, + BinderBundle? binderBundle, + bool wrapInTargetInvocationException, + bool methodToCallIsThisCall = true) + { + // This assert is needed because we've double-purposed "targetMethodOrDelegate" (which is actually a MethodBase anytime a custom binder is used) + // as a way of obtaining the true parameter type which we need to pass to Binder.ChangeType(). (The type normally passed to DynamicInvokeParamHelperCore + // isn't always the exact type (byref stripped off, enums converted to int, etc.) + Debug.Assert(!(binderBundle != null && !(targetMethodOrDelegate is MethodBase)), "The only callers that can pass a custom binder are those servicing MethodBase.Invoke() apis."); + + ArgSetupState argSetupState = new ArgSetupState + { + binderBundle = binderBundle, + targetMethodOrDelegate = targetMethodOrDelegate, + }; + + { + // If the passed in array is not an actual object[] instance, we need to copy it over to an actual object[] + // instance so that the rest of the code can safely create managed object references to individual elements. + if (parameters != null && EETypePtr.EETypePtrOf() != parameters.EETypePtr) + { + argSetupState.parameters = new object[parameters.Length]; + Array.Copy(parameters, argSetupState.parameters, parameters.Length); + } + else + { + argSetupState.parameters = parameters; + } + + object result; + try + { + { + if (dynamicInvokeHelperGenericDictionary != IntPtr.Zero) + { + result = ((delegate*)dynamicInvokeHelperMethod) + (dynamicInvokeHelperGenericDictionary, thisPtr, methodToCall, ref argSetupState, methodToCallIsThisCall); + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } + else + { + result = ((delegate*)dynamicInvokeHelperMethod) + (thisPtr, methodToCall, ref argSetupState, methodToCallIsThisCall); + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } + } + } + catch (Exception e) when (wrapInTargetInvocationException && argSetupState.fComplete) + { + throw new TargetInvocationException(e); + } + finally + { + if (argSetupState.parameters != parameters) + { + Array.Copy(argSetupState.parameters, parameters, parameters.Length); + } + + if (argSetupState.fComplete) + { + // Nullable objects can't take advantage of the ability to update the boxed value on the heap directly, so perform + // an update of the parameters array now. + if (argSetupState.nullableCopyBackObjects != null) + { + for (int i = 0; i < argSetupState.nullableCopyBackObjects.Length; i++) + { + if (argSetupState.nullableCopyBackObjects[i] != null) + { + parameters[i] = DynamicInvokeBoxIntoNonNullable(argSetupState.nullableCopyBackObjects[i]); + } + } + } + } + } + + if (result == NullByRefValueSentinel) + throw new NullReferenceException(SR.NullReference_InvokeNullRefReturned); + + return result; + } + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + internal static void DynamicInvokeArgSetupComplete(ref ArgSetupState argSetupState) + { + int parametersLength = argSetupState.parameters != null ? argSetupState.parameters.Length : 0; + + if (argSetupState.curIndex != parametersLength) + { + throw new System.Reflection.TargetParameterCountException(); + } + argSetupState.fComplete = true; + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + public static unsafe void DynamicInvokeArgSetupPtrComplete(IntPtr argSetupStatePtr) + { + // argSetupStatePtr is a pointer to a *pinned* ArgSetupState object + DynamicInvokeArgSetupComplete(ref Unsafe.As(ref *(byte*)argSetupStatePtr)); + } + + private static void DynamicInvokeUnboxIntoActualNullable(object actualBoxedNullable, object boxedFillObject, EETypePtr nullableType) + { + // get a byref to the data within the actual boxed nullable, and then call RhUnBox with the boxedFillObject as the boxed object, and nullableType as the unbox type, and unbox into the actualBoxedNullable + RuntimeImports.RhUnbox(boxedFillObject, ref actualBoxedNullable.GetRawData(), nullableType); + } + + private static object DynamicInvokeBoxIntoNonNullable(object actualBoxedNullable) + { + // grab the pointer to data, box using the MethodTable of the actualBoxedNullable, and then return the boxed object + return RuntimeImports.RhBox(actualBoxedNullable.EETypePtr, ref actualBoxedNullable.GetRawData()); + } + + [DebuggerStepThrough] + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + internal static ref IntPtr DynamicInvokeParamHelperIn(ref ArgSetupState argSetupState, RuntimeTypeHandle rth) + { + // + // Call DynamicInvokeParamHelperCore as an in parameter, and return a managed byref to the interesting bit. + // + // This function exactly matches DynamicInvokeParamHelperRef except for the value of the enum passed to DynamicInvokeParamHelperCore + // + + object obj = DynamicInvokeParamHelperCore(ref argSetupState, rth, out DynamicInvokeParamLookupType paramLookupType, out int index, DynamicInvokeParamType.In); + + if (paramLookupType == DynamicInvokeParamLookupType.ValuetypeObjectReturned) + { + return ref Unsafe.As(ref obj.GetRawData()); + } + else + { + return ref Unsafe.As(ref Unsafe.As(obj)[index]); + } + } + + [DebuggerStepThrough] + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + internal static ref IntPtr DynamicInvokeParamHelperRef(ref ArgSetupState argSetupState, RuntimeTypeHandle rth) + { + // + // Call DynamicInvokeParamHelperCore as a ref parameter, and return a managed byref to the interesting bit. As this can't actually be defined in C# there is an IL transform that fills this in. + // + // This function exactly matches DynamicInvokeParamHelperIn except for the value of the enum passed to DynamicInvokeParamHelperCore + // + + object obj = DynamicInvokeParamHelperCore(ref argSetupState, rth, out DynamicInvokeParamLookupType paramLookupType, out int index, DynamicInvokeParamType.Ref); + + if (paramLookupType == DynamicInvokeParamLookupType.ValuetypeObjectReturned) + { + return ref Unsafe.As(ref obj.GetRawData()); + } + else + { + return ref Unsafe.As(ref Unsafe.As(obj)[index]); + } + } + + internal static object DynamicInvokeBoxedValuetypeReturn(out DynamicInvokeParamLookupType paramLookupType, object? boxedValuetype, object[]? parameters, int index, RuntimeTypeHandle type, DynamicInvokeParamType paramType, ref object[] nullableCopyBackObjects) + { + object finalObjectToReturn = boxedValuetype; + EETypePtr eeType = type.ToEETypePtr(); + bool nullable = eeType.IsNullable; + + if (finalObjectToReturn == null || nullable || paramType == DynamicInvokeParamType.Ref) + { + finalObjectToReturn = RuntimeImports.RhNewObject(eeType); + if (boxedValuetype != null) + { + DynamicInvokeUnboxIntoActualNullable(finalObjectToReturn, boxedValuetype, eeType); + } + } + + if (nullable) + { + if (paramType == DynamicInvokeParamType.Ref) + { + if (nullableCopyBackObjects == null) + { + nullableCopyBackObjects = new object[parameters.Length]; + } + + nullableCopyBackObjects[index] = finalObjectToReturn; + parameters[index] = null; + } + } + else + { + System.Diagnostics.Debug.Assert(finalObjectToReturn != null); + if (paramType == DynamicInvokeParamType.Ref) + parameters[index] = finalObjectToReturn; + } + + paramLookupType = DynamicInvokeParamLookupType.ValuetypeObjectReturned; + return finalObjectToReturn; + } + + internal static object DynamicInvokeUnmanagedPointerReturn(out DynamicInvokeParamLookupType paramLookupType, object? boxedPointerType, int index, RuntimeTypeHandle type, DynamicInvokeParamType paramType) + { + object finalObjectToReturn = boxedPointerType; + + Debug.Assert(finalObjectToReturn is IntPtr); + paramLookupType = DynamicInvokeParamLookupType.ValuetypeObjectReturned; + return finalObjectToReturn; + } + + public static unsafe object DynamicInvokeParamHelperCore(IntPtr argSetupState, RuntimeTypeHandle type, out DynamicInvokeParamLookupType paramLookupType, out int index, DynamicInvokeParamType paramType) + { + return DynamicInvokeParamHelperCore(ref Unsafe.AsRef((void*)argSetupState), type, out paramLookupType, out index, paramType); + } + + public static object DynamicInvokeParamHelperCore(ref ArgSetupState argSetupState, RuntimeTypeHandle type, out DynamicInvokeParamLookupType paramLookupType, out int index, DynamicInvokeParamType paramType) + { + index = argSetupState.curIndex++; + int parametersLength = argSetupState.parameters != null ? argSetupState.parameters.Length : 0; + + if (index >= parametersLength) + throw new System.Reflection.TargetParameterCountException(); + + Debug.Assert(argSetupState.parameters != null); + object? incomingParam = argSetupState.parameters[index]; + + // Handle default parameters + if ((incomingParam == System.Reflection.Missing.Value) && paramType == DynamicInvokeParamType.In) + { + incomingParam = GetDefaultValue(argSetupState.targetMethodOrDelegate, type, index); + + // The default value is captured into the parameters array + argSetupState.parameters[index] = incomingParam; + } + + RuntimeTypeHandle widenAndCompareType = type; + bool nullable = type.ToEETypePtr().IsNullable; + if (nullable) + { + widenAndCompareType = new RuntimeTypeHandle(type.ToEETypePtr().NullableType); + } + + if (widenAndCompareType.ToEETypePtr().IsPrimitive || type.ToEETypePtr().IsEnum) + { + // Nullable requires exact matching + if (incomingParam != null) + { + if (nullable || paramType == DynamicInvokeParamType.Ref) + { + if (widenAndCompareType.ToEETypePtr() != incomingParam.EETypePtr) + { + if (argSetupState.binderBundle == null) + throw CreateChangeTypeArgumentException(incomingParam.EETypePtr, type.ToEETypePtr()); + Type exactDstType = GetExactTypeForCustomBinder(argSetupState); + incomingParam = argSetupState.binderBundle.ChangeType(incomingParam, exactDstType); + if (incomingParam != null && widenAndCompareType.ToEETypePtr() != incomingParam.EETypePtr) + throw CreateChangeTypeArgumentException(incomingParam.EETypePtr, type.ToEETypePtr()); + } + } + else + { + if (widenAndCompareType.ToEETypePtr().ElementType != incomingParam.EETypePtr.ElementType) + { + System.Diagnostics.Debug.Assert(paramType == DynamicInvokeParamType.In); + incomingParam = InvokeUtils.CheckArgument(incomingParam, widenAndCompareType.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.DynamicInvoke, argSetupState.binderBundle, ref argSetupState); + } + } + } + + return DynamicInvokeBoxedValuetypeReturn(out paramLookupType, incomingParam, argSetupState.parameters, index, type, paramType, ref argSetupState.nullableCopyBackObjects); + } + else if (type.ToEETypePtr().IsValueType) + { + incomingParam = InvokeUtils.CheckArgument(incomingParam, type.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.DynamicInvoke, argSetupState.binderBundle, ref argSetupState); + if (argSetupState.binderBundle == null) + { + System.Diagnostics.Debug.Assert(argSetupState.parameters[index] == null || object.ReferenceEquals(incomingParam, argSetupState.parameters[index])); + } + return DynamicInvokeBoxedValuetypeReturn(out paramLookupType, incomingParam, argSetupState.parameters, index, type, paramType, ref argSetupState.nullableCopyBackObjects); + } + else if (type.ToEETypePtr().IsPointer) + { + incomingParam = InvokeUtils.CheckArgument(incomingParam, type.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.DynamicInvoke, argSetupState.binderBundle, ref argSetupState); + return DynamicInvokeUnmanagedPointerReturn(out paramLookupType, incomingParam, index, type, paramType); + } + else + { + incomingParam = InvokeUtils.CheckArgument(incomingParam, widenAndCompareType.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.DynamicInvoke, argSetupState.binderBundle, ref argSetupState); + paramLookupType = DynamicInvokeParamLookupType.IndexIntoObjectArrayReturned; + if (argSetupState.binderBundle == null) + { + System.Diagnostics.Debug.Assert(object.ReferenceEquals(incomingParam, argSetupState.parameters[index])); + return argSetupState.parameters; + } + else + { + if (object.ReferenceEquals(incomingParam, argSetupState.parameters[index])) + { + return argSetupState.parameters; + } + else + { + // If we got here, the original argument object was superceded by invoking the custom binder. + + if (paramType == DynamicInvokeParamType.Ref) + { + argSetupState.parameters[index] = incomingParam; + return argSetupState.parameters; + } + else + { + // Since this not a by-ref parameter, we don't want to bash the original user-owned argument array but the rules of DynamicInvokeParamHelperCore() require + // that we return non-value types as the "index"th element in an array. Thus, create an on-demand throwaway array just for this purpose. + if (argSetupState.customBinderProvidedParameters == null) + { + argSetupState.customBinderProvidedParameters = new object[argSetupState.parameters.Length]; + } + argSetupState.customBinderProvidedParameters[index] = incomingParam; + return argSetupState.customBinderProvidedParameters; + } + } + } + } + } + + private static volatile object _nullByRefValueSentinel; + public static object NullByRefValueSentinel + { + get + { + if (_nullByRefValueSentinel == null) + { + Interlocked.CompareExchange(ref _nullByRefValueSentinel, new object(), null); + } + return _nullByRefValueSentinel; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Math.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Math.CoreRT.cs new file mode 100644 index 00000000000000..8a53e1588b83d2 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Math.CoreRT.cs @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================ +** +** Purpose: Some floating-point math operations +** +===========================================================*/ + +//This class contains only static members and doesn't require serialization. + +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System +{ + public static partial class Math + { + [Intrinsic] + public static float Abs(float value) + { + return RuntimeImports.fabsf(value); + } + + [Intrinsic] + public static double Abs(double value) + { + return RuntimeImports.fabs(value); + } + + [Intrinsic] + public static double Acos(double d) + { + return RuntimeImports.acos(d); + } + + [Intrinsic] + public static double Acosh(double d) + { + return RuntimeImports.acosh(d); + } + + [Intrinsic] + public static double Asin(double d) + { + return RuntimeImports.asin(d); + } + + [Intrinsic] + public static double Asinh(double d) + { + return RuntimeImports.asinh(d); + } + + [Intrinsic] + public static double Atan(double d) + { + return RuntimeImports.atan(d); + } + + [Intrinsic] + public static double Atan2(double y, double x) + { + return RuntimeImports.atan2(y, x); + } + + [Intrinsic] + public static double Atanh(double d) + { + return RuntimeImports.atanh(d); + } + + [Intrinsic] + public static double Cbrt(double d) + { + return RuntimeImports.cbrt(d); + } + + [Intrinsic] + public static double Ceiling(double a) + { + return RuntimeImports.ceil(a); + } + + [Intrinsic] + public static double Cos(double d) + { + return RuntimeImports.cos(d); + } + + [Intrinsic] + public static double Cosh(double value) + { + return RuntimeImports.cosh(value); + } + + [Intrinsic] + public static double Exp(double d) + { + return RuntimeImports.exp(d); + } + + [Intrinsic] + public static double Floor(double d) + { + return RuntimeImports.floor(d); + } + + [Intrinsic] + public static double FusedMultiplyAdd(double x, double y, double z) + { + return RuntimeImports.fma(x, y, z); + } + + [Intrinsic] + public static double Log(double d) + { + return RuntimeImports.log(d); + } + + [Intrinsic] + public static double Log2(double x) + { + return RuntimeImports.log2(x); + } + + [Intrinsic] + public static double Log10(double d) + { + return RuntimeImports.log10(d); + } + + [Intrinsic] + public static double Pow(double x, double y) + { + return RuntimeImports.pow(x, y); + } + + [Intrinsic] + public static double Sin(double a) + { + return RuntimeImports.sin(a); + } + + [Intrinsic] + public static double Sinh(double value) + { + return RuntimeImports.sinh(value); + } + + [Intrinsic] + public static double Sqrt(double d) + { + return RuntimeImports.sqrt(d); + } + + [Intrinsic] + public static double Tan(double a) + { + return RuntimeImports.tan(a); + } + + [Intrinsic] + public static double Tanh(double value) + { + return RuntimeImports.tanh(value); + } + + [Intrinsic] + private static double FMod(double x, double y) + { + return RuntimeImports.fmod(x, y); + } + + [Intrinsic] + private static unsafe double ModF(double x, double* intptr) + { + return RuntimeImports.modf(x, intptr); + } + + public static (double Sin, double Cos) SinCos(double x) => (Sin(x), Cos(x)); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MathF.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MathF.CoreRT.cs new file mode 100644 index 00000000000000..2f42ad90e94c32 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MathF.CoreRT.cs @@ -0,0 +1,171 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================ +** +** Purpose: Some single-precision floating-point math operations +** +===========================================================*/ + +//This class contains only static members and doesn't require serialization. + +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System +{ + public static partial class MathF + { + [Intrinsic] + public static float Acos(float x) + { + return RuntimeImports.acosf(x); + } + + [Intrinsic] + public static float Acosh(float x) + { + return RuntimeImports.acoshf(x); + } + + [Intrinsic] + public static float Asin(float x) + { + return RuntimeImports.asinf(x); + } + + [Intrinsic] + public static float Asinh(float x) + { + return RuntimeImports.asinhf(x); + } + + [Intrinsic] + public static float Atan(float x) + { + return RuntimeImports.atanf(x); + } + + [Intrinsic] + public static float Atan2(float y, float x) + { + return RuntimeImports.atan2f(y, x); + } + + [Intrinsic] + public static float Atanh(float x) + { + return RuntimeImports.atanhf(x); + } + + [Intrinsic] + public static float Cbrt(float x) + { + return RuntimeImports.cbrtf(x); + } + + [Intrinsic] + public static float Ceiling(float x) + { + return RuntimeImports.ceilf(x); + } + + [Intrinsic] + public static float Cos(float x) + { + return RuntimeImports.cosf(x); + } + + [Intrinsic] + public static float Cosh(float x) + { + return RuntimeImports.coshf(x); + } + + [Intrinsic] + public static float Exp(float x) + { + return RuntimeImports.expf(x); + } + + [Intrinsic] + public static float Floor(float x) + { + return RuntimeImports.floorf(x); + } + + [Intrinsic] + public static float FusedMultiplyAdd(float x, float y, float z) + { + return RuntimeImports.fmaf(x, y, z); + } + + [Intrinsic] + public static float Log(float x) + { + return RuntimeImports.logf(x); + } + + [Intrinsic] + public static float Log2(float x) + { + return RuntimeImports.log2f(x); + } + + [Intrinsic] + public static float Log10(float x) + { + return RuntimeImports.log10f(x); + } + + [Intrinsic] + public static float Pow(float x, float y) + { + return RuntimeImports.powf(x, y); + } + + [Intrinsic] + public static float Sin(float x) + { + return RuntimeImports.sinf(x); + } + + [Intrinsic] + public static float Sinh(float x) + { + return RuntimeImports.sinhf(x); + } + + [Intrinsic] + public static float Sqrt(float x) + { + return RuntimeImports.sqrtf(x); + } + + [Intrinsic] + public static float Tan(float x) + { + return RuntimeImports.tanf(x); + } + + [Intrinsic] + public static float Tanh(float x) + { + return RuntimeImports.tanhf(x); + } + + [Intrinsic] + private static float FMod(float x, float y) + { + return RuntimeImports.fmodf(x, y); + } + + [Intrinsic] + private static unsafe float ModF(float x, float* intptr) + { + return RuntimeImports.modff(x, intptr); + } + + public static (float Sin, float Cos) SinCos(float x) => (Sin(x), Cos(x)); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MissingMemberException.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MissingMemberException.CoreRT.cs new file mode 100644 index 00000000000000..32a9d781bccb3e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MissingMemberException.CoreRT.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Serialization; + +namespace System +{ + public partial class MissingMemberException : MemberAccessException + { + internal static string FormatSignature(byte[] signature) + { + // This is not the correct implementation, however, it's probably not worth the time to port given that + // (1) it's for a diagnostic + // (2) Signature is non-null when this exception is created from the native runtime. Which we don't do in .Net Native. + // (3) Only other time the signature is non-null is if this exception object is deserialized from a persisted blob from an older runtime. + return string.Empty; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ModuleHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ModuleHandle.cs new file mode 100644 index 00000000000000..3714e7ce510fba --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ModuleHandle.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace System +{ + public struct ModuleHandle + { + public static readonly ModuleHandle EmptyHandle; + + private Module _ptr; + + internal Module AssociatedModule => _ptr; + + // Not an api but has to be public because of the Reflection.Core/CoreLib divide. + public ModuleHandle(Module module) + { + _ptr = module; + } + + public override int GetHashCode() + { + return _ptr != null ? _ptr.GetHashCode() : 0; + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (!(obj is ModuleHandle)) + return false; + + ModuleHandle handle = (ModuleHandle)obj; + + return handle._ptr == _ptr; + } + + public bool Equals(ModuleHandle handle) + { + return handle._ptr == _ptr; + } + + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public RuntimeFieldHandle GetRuntimeFieldHandleFromMetadataToken(int fieldToken) + { + throw new PlatformNotSupportedException(); + } + + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public RuntimeMethodHandle GetRuntimeMethodHandleFromMetadataToken(int methodToken) + { + throw new PlatformNotSupportedException(); + } + + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public RuntimeTypeHandle GetRuntimeTypeHandleFromMetadataToken(int typeToken) + { + throw new PlatformNotSupportedException(); + } + + public int MDStreamVersion + { + get + { + throw new PlatformNotSupportedException(); + } + } + + public static bool operator ==(ModuleHandle left, ModuleHandle right) + { + return left.Equals(right); + } + + public static bool operator !=(ModuleHandle left, ModuleHandle right) + { + return !left.Equals(right); + } + + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public RuntimeFieldHandle ResolveFieldHandle(int fieldToken) + { + throw new PlatformNotSupportedException(); + } + + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public RuntimeFieldHandle ResolveFieldHandle(int fieldToken, RuntimeTypeHandle[] typeInstantiationContext, RuntimeTypeHandle[] methodInstantiationContext) + { + throw new PlatformNotSupportedException(); + } + + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public RuntimeMethodHandle ResolveMethodHandle(int methodToken) + { + throw new PlatformNotSupportedException(); + } + + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public RuntimeMethodHandle ResolveMethodHandle(int methodToken, RuntimeTypeHandle[] typeInstantiationContext, RuntimeTypeHandle[] methodInstantiationContext) + { + throw new PlatformNotSupportedException(); + } + + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public RuntimeTypeHandle ResolveTypeHandle(int typeToken) + { + throw new PlatformNotSupportedException(); + } + + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public RuntimeTypeHandle ResolveTypeHandle(int typeToken, RuntimeTypeHandle[] typeInstantiationContext, RuntimeTypeHandle[] methodInstantiationContext) + { + throw new PlatformNotSupportedException(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MulticastDelegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MulticastDelegate.cs new file mode 100644 index 00000000000000..eda6c45aa296cb --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MulticastDelegate.cs @@ -0,0 +1,419 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime; +using System.Runtime.Serialization; +using System.Runtime.CompilerServices; + +using Internal.Runtime.CompilerServices; + +namespace System +{ + public abstract class MulticastDelegate : Delegate, ISerializable + { + // This constructor is called from the class generated by the + // compiler generated code (This must match the constructor + // in Delegate + [RequiresUnreferencedCode("The target method might be removed")] + protected MulticastDelegate(object target, string method) : base(target, method) + { + } + + // This constructor is called from a class to generate a + // delegate based upon a static method name and the Type object + // for the class defining the method. + protected MulticastDelegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method) : base(target, method) + { + } + + private bool InvocationListEquals(MulticastDelegate d) + { + Delegate[] invocationList = m_helperObject as Delegate[]; + if (d.m_extraFunctionPointerOrData != m_extraFunctionPointerOrData) + return false; + + int invocationCount = (int)m_extraFunctionPointerOrData; + for (int i = 0; i < invocationCount; i++) + { + Delegate dd = invocationList[i]; + Delegate[] dInvocationList = d.m_helperObject as Delegate[]; + if (!dd.Equals(dInvocationList[i])) + return false; + } + return true; + } + + public override sealed bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == null) + return false; + if (object.ReferenceEquals(this, obj)) + return true; + if (!InternalEqualTypes(this, obj)) + return false; + + // Since this is a MulticastDelegate and we know + // the types are the same, obj should also be a + // MulticastDelegate + Debug.Assert(obj is MulticastDelegate, "Shouldn't have failed here since we already checked the types are the same!"); + var d = Unsafe.As(obj); + + // there are 2 kind of delegate kinds for comparision + // 1- Multicast (m_helperObject is Delegate[]) + // 2- Single-cast delegate, which can be compared with a structural comparision + + if (m_functionPointer == GetThunk(MulticastThunk)) + { + return InvocationListEquals(d); + } + else + { + if (!object.ReferenceEquals(m_helperObject, d.m_helperObject) || + (!FunctionPointerOps.Compare(m_extraFunctionPointerOrData, d.m_extraFunctionPointerOrData)) || + (!FunctionPointerOps.Compare(m_functionPointer, d.m_functionPointer))) + { + return false; + } + + // Those delegate kinds with thunks put themselves into the m_firstParamter, so we can't + // blindly compare the m_firstParameter fields for equality. + if (object.ReferenceEquals(m_firstParameter, this)) + { + return object.ReferenceEquals(d.m_firstParameter, d); + } + + return object.ReferenceEquals(m_firstParameter, d.m_firstParameter); + } + } + + public override sealed int GetHashCode() + { + Delegate[]? invocationList = m_helperObject as Delegate[]; + if (invocationList == null) + { + return base.GetHashCode(); + } + else + { + int hash = 0; + for (int i = 0; i < (int)m_extraFunctionPointerOrData; i++) + { + hash = hash * 33 + invocationList[i].GetHashCode(); + } + + return hash; + } + } + + // Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(MulticastDelegate? d1, MulticastDelegate? d2) + { + // Test d2 first to allow branch elimination when inlined for null checks (== null) + // so it can become a simple test + if (d2 is null) + { + // return true/false not the test result https://github.com/dotnet/runtime/issues/4207 + return (d1 is null) ? true : false; + } + + return ReferenceEquals(d2, d1) ? true : d2.Equals((object?)d1); + } + + // Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(MulticastDelegate? d1, MulticastDelegate? d2) + { + // Can't call the == operator as it will call object== + + // Test d2 first to allow branch elimination when inlined for not null checks (!= null) + // so it can become a simple test + if (d2 is null) + { + // return true/false not the test result https://github.com/dotnet/runtime/issues/4207 + return (d1 is null) ? false : true; + } + + return ReferenceEquals(d2, d1) ? false : !d2.Equals(d1); + } + + private MulticastDelegate NewMulticastDelegate(Delegate[] invocationList, int invocationCount, bool thisIsMultiCastAlready = false) + { + // First, allocate a new multicast delegate just like this one, i.e. same type as the this object + MulticastDelegate result = (MulticastDelegate)RuntimeImports.RhNewObject(this.EETypePtr); + + // Performance optimization - if this already points to a true multicast delegate, + // copy _methodPtr and _methodPtrAux fields rather than calling into the EE to get them + if (thisIsMultiCastAlready) + { + result.m_functionPointer = this.m_functionPointer; + } + else + { + result.m_functionPointer = GetThunk(MulticastThunk); + } + result.m_firstParameter = result; + result.m_helperObject = invocationList; + result.m_extraFunctionPointerOrData = (IntPtr)invocationCount; + + return result; + } + + private bool TrySetSlot(Delegate[] a, int index, Delegate o) + { + if (a[index] == null && System.Threading.Interlocked.CompareExchange(ref a[index], o, null) == null) + return true; + + // The slot may be already set because we have added and removed the same method before. + // Optimize this case, because it's cheaper than copying the array. + if (a[index] != null) + { + MulticastDelegate d = (MulticastDelegate)o; + MulticastDelegate dd = (MulticastDelegate)a[index]; + + if (object.ReferenceEquals(dd.m_firstParameter, d.m_firstParameter) && + object.ReferenceEquals(dd.m_helperObject, d.m_helperObject) && + dd.m_extraFunctionPointerOrData == d.m_extraFunctionPointerOrData && + dd.m_functionPointer == d.m_functionPointer) + { + return true; + } + } + return false; + } + + + // This method will combine this delegate with the passed delegate + // to form a new delegate. + protected sealed override Delegate CombineImpl(Delegate? follow) + { + if (follow is null) // cast to object for a more efficient test + return this; + + // Verify that the types are the same... + if (!InternalEqualTypes(this, follow)) + throw new ArgumentException(SR.Arg_DlgtTypeMis); + + if (IsDynamicDelegate() && follow.IsDynamicDelegate()) + { + throw new InvalidOperationException(); + } + + MulticastDelegate dFollow = (MulticastDelegate)follow; + Delegate[]? resultList; + int followCount = 1; + Delegate[]? followList = dFollow.m_helperObject as Delegate[]; + if (followList != null) + followCount = (int)dFollow.m_extraFunctionPointerOrData; + + int resultCount; + Delegate[]? invocationList = m_helperObject as Delegate[]; + if (invocationList == null) + { + resultCount = 1 + followCount; + resultList = new Delegate[resultCount]; + resultList[0] = this; + if (followList == null) + { + resultList[1] = dFollow; + } + else + { + for (int i = 0; i < followCount; i++) + resultList[1 + i] = followList[i]; + } + return NewMulticastDelegate(resultList, resultCount); + } + else + { + int invocationCount = (int)m_extraFunctionPointerOrData; + resultCount = invocationCount + followCount; + resultList = null; + if (resultCount <= invocationList.Length) + { + resultList = invocationList; + if (followList == null) + { + if (!TrySetSlot(resultList, invocationCount, dFollow)) + resultList = null; + } + else + { + for (int i = 0; i < followCount; i++) + { + if (!TrySetSlot(resultList, invocationCount + i, followList[i])) + { + resultList = null; + break; + } + } + } + } + + if (resultList == null) + { + int allocCount = invocationList.Length; + while (allocCount < resultCount) + allocCount *= 2; + + resultList = new Delegate[allocCount]; + + for (int i = 0; i < invocationCount; i++) + resultList[i] = invocationList[i]; + + if (followList == null) + { + resultList[invocationCount] = dFollow; + } + else + { + for (int i = 0; i < followCount; i++) + resultList[invocationCount + i] = followList[i]; + } + } + return NewMulticastDelegate(resultList, resultCount, true); + } + } + + private Delegate[] DeleteFromInvocationList(Delegate[] invocationList, int invocationCount, int deleteIndex, int deleteCount) + { + Delegate[] thisInvocationList = m_helperObject as Delegate[]; + int allocCount = thisInvocationList.Length; + while (allocCount / 2 >= invocationCount - deleteCount) + allocCount /= 2; + + Delegate[] newInvocationList = new Delegate[allocCount]; + + for (int i = 0; i < deleteIndex; i++) + newInvocationList[i] = invocationList[i]; + + for (int i = deleteIndex + deleteCount; i < invocationCount; i++) + newInvocationList[i - deleteCount] = invocationList[i]; + + return newInvocationList; + } + + private bool EqualInvocationLists(Delegate[] a, Delegate[] b, int start, int count) + { + for (int i = 0; i < count; i++) + { + if (!(a[start + i].Equals(b[i]))) + return false; + } + return true; + } + + // This method currently looks backward on the invocation list + // for an element that has Delegate based equality with value. (Doesn't + // look at the invocation list.) If this is found we remove it from + // this list and return a new delegate. If its not found a copy of the + // current list is returned. + protected sealed override Delegate? RemoveImpl(Delegate value) + { + // There is a special case were we are removing using a delegate as + // the value we need to check for this case + // + MulticastDelegate? v = value as MulticastDelegate; + + if (v is null) + return this; + if (v.m_helperObject as Delegate[] == null) + { + Delegate[]? invocationList = m_helperObject as Delegate[]; + if (invocationList == null) + { + // they are both not real Multicast + if (this.Equals(v)) + return null; + } + else + { + int invocationCount = (int)m_extraFunctionPointerOrData; + for (int i = invocationCount; --i >= 0;) + { + if (v.Equals(invocationList[i])) + { + if (invocationCount == 2) + { + // Special case - only one value left, either at the beginning or the end + return invocationList[1 - i]; + } + else + { + Delegate[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1); + return NewMulticastDelegate(list, invocationCount - 1, true); + } + } + } + } + } + else + { + Delegate[]? invocationList = m_helperObject as Delegate[]; + if (invocationList != null) + { + int invocationCount = (int)m_extraFunctionPointerOrData; + int vInvocationCount = (int)v.m_extraFunctionPointerOrData; + for (int i = invocationCount - vInvocationCount; i >= 0; i--) + { + if (EqualInvocationLists(invocationList, v.m_helperObject as Delegate[], i, vInvocationCount)) + { + if (invocationCount - vInvocationCount == 0) + { + // Special case - no values left + return null; + } + else if (invocationCount - vInvocationCount == 1) + { + // Special case - only one value left, either at the beginning or the end + return invocationList[i != 0 ? 0 : invocationCount - 1]; + } + else + { + Delegate[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount); + return NewMulticastDelegate(list, invocationCount - vInvocationCount, true); + } + } + } + } + } + + return this; + } + + public sealed override Delegate[] GetInvocationList() + { + Delegate[] del; + Delegate[]? invocationList = m_helperObject as Delegate[]; + if (invocationList == null) + { + del = new Delegate[1]; + del[0] = this; + } + else + { + // Create an array of delegate copies and each + // element into the array + int invocationCount = (int)m_extraFunctionPointerOrData; + del = new Delegate[invocationCount]; + + for (int i = 0; i < del.Length; i++) + del[i] = invocationList[i]; + } + return del; + } + + protected override MethodInfo GetMethodImpl() + { + return base.GetMethodImpl(); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(SR.Serialization_DelegatesNotSupported); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Object.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Object.CoreRT.cs new file mode 100644 index 00000000000000..9f028e2f62b233 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Object.CoreRT.cs @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Internal.Reflection.Core.NonPortable; + +using Internal.Runtime; +using Internal.Runtime.CompilerServices; + +namespace System +{ + // CONTRACT with Runtime + // The Object type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type EEType_ptr (or void * till a tool bug can be fixed) + // VTable Contract: The first vtable slot should be the finalizer for object => The first virtual method in the object class should be the Finalizer + + public unsafe partial class Object + { + // CS0649: Field '{blah}' is never assigned to, and will always have its default value +#pragma warning disable 649 + // Marked as internal for now so that some classes (System.Buffer, System.Enum) can use C#'s fixed + // statement on partially typed objects. Wouldn't have to do this if we could directly declared pinned + // locals. + [NonSerialized] + private MethodTable* m_pEEType; +#pragma warning restore + +#if INPLACE_RUNTIME + internal unsafe MethodTable* MethodTable + { + get + { + return m_pEEType; + } + } +#endif + + [Runtime.CompilerServices.Intrinsic] + internal static extern MethodTable* MethodTableOf(); + + [Intrinsic] + public Type GetType() + { + return Type.GetTypeFromEETypePtr(EETypePtr); + } + + internal EETypePtr EETypePtr + { + get + { + return new EETypePtr(m_pEEType); + } + } + + [Intrinsic] + protected object MemberwiseClone() + { + return RuntimeImports.RhMemberwiseClone(this); + } + + internal ref byte GetRawData() + { + return ref Unsafe.As(this).Data; + } + + /// + /// Return size of all data (excluding ObjHeader and MethodTable*). + /// Note that for strings/arrays this would include the Length as well. + /// + internal unsafe uint GetRawDataSize() + { + return EETypePtr.BaseSize - (uint)sizeof(ObjHeader) - (uint)sizeof(MethodTable*); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Assembly.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Assembly.CoreRT.cs new file mode 100644 index 00000000000000..d42d16e742ffdc --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Assembly.CoreRT.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Configuration.Assemblies; +using System.Runtime.Serialization; +using System.IO; + +using Internal.Reflection.Augments; +using Internal.Reflection.Core.NonPortable; + +namespace System.Reflection +{ + public abstract partial class Assembly : ICustomAttributeProvider, ISerializable + { + private static Assembly? GetEntryAssemblyInternal() => Internal.Runtime.CompilerHelpers.StartupCodeHelpers.GetEntryAssembly(); + + [System.Runtime.CompilerServices.Intrinsic] + public static Assembly GetExecutingAssembly() { throw NotImplemented.ByDesign; } //Implemented by toolchain. + + public static Assembly GetCallingAssembly() + { + if (AppContext.TryGetSwitch("Switch.System.Reflection.Assembly.SimulatedCallingAssembly", out bool isSimulated) && isSimulated) + return GetEntryAssembly(); + + throw new PlatformNotSupportedException(); + } + + public static Assembly Load(AssemblyName assemblyRef) => ReflectionAugments.ReflectionCoreCallbacks.Load(assemblyRef, throwOnFileNotFound: true); + + public static Assembly Load(string assemblyString) + { + if (assemblyString == null) + throw new ArgumentNullException(nameof(assemblyString)); + + AssemblyName name = new AssemblyName(assemblyString); + return Load(name); + } + + [Obsolete("This method has been deprecated. Please use Assembly.Load() instead. https://go.microsoft.com/fwlink/?linkid=14202")] + public static Assembly LoadWithPartialName(string partialName) + { + if (partialName == null) + throw new ArgumentNullException(nameof(partialName)); + + if ((partialName.Length == 0) || (partialName[0] == '\0')) + throw new ArgumentException(SR.Format_StringZeroLength, nameof(partialName)); + + try + { + return Load(partialName); + } + catch (FileNotFoundException) + { + return null; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyName.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyName.CoreRT.cs new file mode 100644 index 00000000000000..0ce40043573650 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyName.CoreRT.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Serialization; + +namespace System.Reflection +{ + public sealed partial class AssemblyName : ICloneable, IDeserializationCallback, ISerializable + { + public AssemblyName(string assemblyName) + : this() + { + if (assemblyName == null) + throw new ArgumentNullException(nameof(assemblyName)); + if ((assemblyName.Length == 0) || + (assemblyName[0] == '\0')) + throw new ArgumentException(SR.Format_StringZeroLength); + + _name = assemblyName; + RuntimeAssemblyName runtimeAssemblyName = AssemblyNameParser.Parse(_name); + runtimeAssemblyName.CopyToAssemblyName(this); + } + + private byte[] ComputePublicKeyToken() + { + return AssemblyNameHelpers.ComputePublicKeyToken(_publicKey); + } + + private static AssemblyName GetFileInformationCore(string assemblyFile) + { + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported_AssemblyName_GetAssemblyName); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.StrongName.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.StrongName.cs new file mode 100644 index 00000000000000..85e437548102e3 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.StrongName.cs @@ -0,0 +1,104 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers.Binary; +using System.Runtime; +using System.Security; + +namespace System.Reflection +{ + public static partial class AssemblyNameHelpers + { + public static byte[]? ComputePublicKeyToken(byte[]? publicKey) + { + if (publicKey == null) + return null; + + if (publicKey.Length == 0) + return Array.Empty(); + + if (!IsValidPublicKey(publicKey)) + throw new SecurityException(SR.Security_InvalidAssemblyPublicKey); + + Span hash = stackalloc byte[20]; + + Sha1ForNonSecretPurposes sha1 = default; + sha1.Start(); + sha1.Append(publicKey); + sha1.Finish(hash); + + byte[] publicKeyToken = new byte[PublicKeyTokenLength]; + for (int i = 0; i < publicKeyToken.Length; i++) + publicKeyToken[i] = hash[hash.Length - 1 - i]; + return publicKeyToken; + } + + // + // This validation logic is a manual port of StrongNameIsValidPublicKey() in the desktop CLR (see clr\src\StrongName\api\StrongNameInternal.cpp) + // + private static bool IsValidPublicKey(byte[] publicKey) + { + uint publicKeyLength = (uint)(publicKey.Length); + + // The buffer must be at least as large as the public key structure (for compat with desktop, we actually compare with the size of the header + 4). + if (publicKeyLength < SizeOfPublicKeyBlob + 4) + return false; + + // Poor man's reinterpret_cast into the PublicKeyBlob structure. + ReadOnlySpan publicKeyBlob = new ReadOnlySpan(publicKey); + uint sigAlgID = BinaryPrimitives.ReadUInt32LittleEndian(publicKeyBlob); + uint hashAlgID = BinaryPrimitives.ReadUInt32LittleEndian(publicKeyBlob.Slice(4)); + uint cbPublicKey = BinaryPrimitives.ReadUInt32LittleEndian(publicKeyBlob.Slice(8)); + + // The buffer must be the same size as the structure header plus the trailing key data + if (cbPublicKey != publicKeyLength - SizeOfPublicKeyBlob) + return false; + + // The buffer itself looks reasonable, but the public key structure needs to be validated as well + + // The ECMA key doesn't look like a valid key so it will fail the below checks. If we were passed that + // key, then we can skip them. + if (EcmaKey.SequenceEqual(publicKeyBlob)) + return true; + + // If a hash algorithm is specified, it must be a sensible value + bool fHashAlgorithmValid = GetAlgClass(hashAlgID) == ALG_CLASS_HASH && GetAlgSid(hashAlgID) == ALG_SID_SHA1; + if (hashAlgID != 0 && !fHashAlgorithmValid) + return false; + + // If a signature algorithm is specified, it must be a sensible value + bool fSignatureAlgorithmValid = GetAlgClass(sigAlgID) == ALG_CLASS_SIGNATURE; + if (sigAlgID != 0 && !fSignatureAlgorithmValid) + return false; + + // The key blob must indicate that it is a PUBLICKEYBLOB + if (publicKey[SizeOfPublicKeyBlob] != PUBLICKEYBLOB) + return false; + + return true; + } + + // Constants and macros copied from WinCrypt.h: + + private static uint GetAlgClass(uint x) + { + return (x & (7 << 13)); + } + + private static uint GetAlgSid(uint x) + { + return (x & (511)); + } + + private const uint ALG_CLASS_HASH = (4 << 13); + private const uint ALG_SID_SHA1 = 4; + private const uint ALG_CLASS_SIGNATURE = (1 << 13); + private const uint PUBLICKEYBLOB = 0x6; + + private const uint SizeOfPublicKeyBlob = 12; + + private const int PublicKeyTokenLength = 8; + + private static ReadOnlySpan EcmaKey => new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.cs new file mode 100644 index 00000000000000..b259e2b6bfbdc3 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyNameHelpers.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Globalization; +using System.IO; +using System.Text; +using System.Collections.Generic; + +namespace System.Reflection +{ + [System.Runtime.CompilerServices.ReflectionBlocked] + public static partial class AssemblyNameHelpers + { + // + // Converts an AssemblyName to a RuntimeAssemblyName that is free from any future mutations on the AssemblyName. + // + public static RuntimeAssemblyName ToRuntimeAssemblyName(this AssemblyName assemblyName) + { + if (assemblyName.Name == null) + throw new ArgumentException(); + + AssemblyNameFlags flags = assemblyName.Flags; + AssemblyContentType contentType = assemblyName.ContentType; +#pragma warning disable SYSLIB0037 // AssemblyName.ProcessorArchitecture is obsolete + ProcessorArchitecture processorArchitecture = assemblyName.ProcessorArchitecture; +#pragma warning restore SYSLIB0037 + AssemblyNameFlags combinedFlags = CombineAssemblyNameFlags(flags, contentType, processorArchitecture); + byte[]? pkOriginal; + if (0 != (flags & AssemblyNameFlags.PublicKey)) + pkOriginal = assemblyName.GetPublicKey(); + else + pkOriginal = assemblyName.GetPublicKeyToken(); + + // AssemblyName's PKT property getters do NOT copy the array before giving it out. Make our own copy + // as the original is wide open to tampering by anyone. + byte[]? pkCopy = null; + if (pkOriginal != null) + { + pkCopy = new byte[pkOriginal.Length]; + ((ICollection)pkOriginal).CopyTo(pkCopy, 0); + } + + return new RuntimeAssemblyName(assemblyName.Name, assemblyName.Version, assemblyName.CultureName, combinedFlags, pkCopy); + } + + // + // These helpers convert between the combined flags+contentType+processorArchitecture value and the separated parts. + // + // Since these are only for trusted callers, they do NOT check for out of bound bits. + // + + internal static AssemblyContentType ExtractAssemblyContentType(this AssemblyNameFlags flags) + { + return (AssemblyContentType)((((int)flags) >> 9) & 0x7); + } + + internal static ProcessorArchitecture ExtractProcessorArchitecture(this AssemblyNameFlags flags) + { + return (ProcessorArchitecture)((((int)flags) >> 4) & 0x7); + } + + public static AssemblyNameFlags ExtractAssemblyNameFlags(this AssemblyNameFlags combinedFlags) + { + return combinedFlags & unchecked((AssemblyNameFlags)0xFFFFF10F); + } + + internal static AssemblyNameFlags CombineAssemblyNameFlags(AssemblyNameFlags flags, AssemblyContentType contentType, ProcessorArchitecture processorArchitecture) + { + return (AssemblyNameFlags)(((int)flags) | (((int)contentType) << 9) | ((int)processorArchitecture << 4)); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyNameLexer.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyNameLexer.cs new file mode 100644 index 00000000000000..d2dca88f24e386 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyNameLexer.cs @@ -0,0 +1,137 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Text; +using System.Globalization; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace System.Reflection +{ + // + // A simple lexer for assembly display names. + // + internal struct AssemblyNameLexer + { + internal AssemblyNameLexer(string s) + { + // Convert string to char[] with NUL terminator. (An actual NUL terminator in the input string will be treated + // as an actual end of string: this is compatible with desktop behavior.) + char[] chars = new char[s.Length + 1]; + s.CopyTo(0, chars, 0, s.Length); + _chars = chars; + _index = 0; + } + + // + // Return the next token in assembly name. If you expect the result to be DisplayNameToken.String, + // use GetNext(out String) instead. + // + internal Token GetNext() + { + string ignore; + return GetNext(out ignore); + } + + // + // Return the next token in assembly name. If the result is DisplayNameToken.String, + // sets "tokenString" to the tokenized string. + // + internal Token GetNext(out string tokenString) + { + tokenString = null; + while (char.IsWhiteSpace(_chars[_index])) + _index++; + + char c = _chars[_index++]; + if (c == 0) + return Token.End; + if (c == ',') + return Token.Comma; + if (c == '=') + return Token.Equals; + + StringBuilder sb = new StringBuilder(); + + char quoteChar = (char)0; + if (c == '\'' || c == '\"') + { + quoteChar = c; + c = _chars[_index++]; + } + + for (;;) + { + if (c == 0) + { + _index--; + break; // Terminate: End of string (desktop compat: if string was quoted, permitted to terminate without end-quote.) + } + + if (quoteChar != 0 && c == quoteChar) + break; // Terminate: Found closing quote of quoted string. + + if (quoteChar == 0 && (c == ',' || c == '=')) + { + _index--; + break; // Terminate: Found start of a new ',' or '=' token. + } + + if (quoteChar == 0 && (c == '\'' || c == '\"')) + throw new FileLoadException(); // Desktop compat: Unescaped quote illegal unless entire string is quoted. + + if (c == '\\') + { + c = _chars[_index++]; + + switch (c) + { + case '\\': + case ',': + case '=': + case '\'': + case '"': + sb.Append(c); + break; + case 't': + sb.Append('\t'); + break; + case 'r': + sb.Append('\r'); + break; + case 'n': + sb.Append('\n'); + break; + default: + throw new FileLoadException(); // Unrecognized escape + } + } + else + { + sb.Append(c); + } + + c = _chars[_index++]; + } + + tokenString = sb.ToString(); + if (quoteChar == 0) + tokenString = tokenString.Trim(); // Unless quoted, whitespace at beginning or end doesn't count. + return Token.String; + } + + // Token categories for display name lexer. + internal enum Token + { + Equals = 1, + Comma = 2, + String = 3, + End = 4, + } + + private readonly char[] _chars; + private int _index; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs new file mode 100644 index 00000000000000..5d8f2ef1c34b0a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs @@ -0,0 +1,230 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Text; +using System.Diagnostics; +using System.Globalization; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace System.Reflection +{ + // + // Parses an assembly name. + // + [System.Runtime.CompilerServices.ReflectionBlocked] + public static class AssemblyNameParser + { + public static void Parse(AssemblyName blank, string s) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + RuntimeAssemblyName runtimeAssemblyName = Parse(s); + runtimeAssemblyName.CopyToAssemblyName(blank); + } + + public static RuntimeAssemblyName Parse(string s) + { + Debug.Assert(s != null); + + int indexOfNul = s.IndexOf((char)0); + if (indexOfNul != -1) + s = s.Substring(0, indexOfNul); + if (s.Length == 0) + throw new ArgumentException(SR.Format_StringZeroLength); + + AssemblyNameLexer lexer = new AssemblyNameLexer(s); + + // Name must come first. + string name; + AssemblyNameLexer.Token token = lexer.GetNext(out name); + if (token != AssemblyNameLexer.Token.String) + throw new FileLoadException(SR.InvalidAssemblyName); + + if (name == string.Empty || name.IndexOfAny(s_illegalCharactersInSimpleName) != -1) + throw new FileLoadException(SR.InvalidAssemblyName); + + Version? version = null; + string? cultureName = null; + byte[]? pkt = null; + AssemblyNameFlags flags = 0; + + LowLevelList alreadySeen = new LowLevelList(); + token = lexer.GetNext(); + while (token != AssemblyNameLexer.Token.End) + { + if (token != AssemblyNameLexer.Token.Comma) + throw new FileLoadException(SR.InvalidAssemblyName); + string attributeName; + + token = lexer.GetNext(out attributeName); + if (token != AssemblyNameLexer.Token.String) + throw new FileLoadException(SR.InvalidAssemblyName); + token = lexer.GetNext(); + + // Compat note: Inside AppX apps, the desktop CLR's AssemblyName parser skips past any elements that don't follow the "=" pattern. + // (when running classic Windows apps, such an illegal construction throws an exception as expected.) + // Naturally, at least one app unwittingly takes advantage of this. + if (token == AssemblyNameLexer.Token.Comma || token == AssemblyNameLexer.Token.End) + continue; + + if (token != AssemblyNameLexer.Token.Equals) + throw new FileLoadException(SR.InvalidAssemblyName); + string attributeValue; + token = lexer.GetNext(out attributeValue); + if (token != AssemblyNameLexer.Token.String) + throw new FileLoadException(SR.InvalidAssemblyName); + + if (attributeName == string.Empty) + throw new FileLoadException(SR.InvalidAssemblyName); + + for (int i = 0; i < alreadySeen.Count; i++) + { + if (alreadySeen[i].Equals(attributeName, StringComparison.OrdinalIgnoreCase)) + throw new FileLoadException(SR.InvalidAssemblyName); // Cannot specify the same attribute twice. + } + alreadySeen.Add(attributeName); + + if (attributeName.Equals("Version", StringComparison.OrdinalIgnoreCase)) + { + version = ParseVersion(attributeValue); + } + + if (attributeName.Equals("Culture", StringComparison.OrdinalIgnoreCase)) + { + cultureName = ParseCulture(attributeValue); + } + + if (attributeName.Equals("PublicKeyToken", StringComparison.OrdinalIgnoreCase)) + { + pkt = ParsePKT(attributeValue); + } + + if (attributeName.Equals("ProcessorArchitecture", StringComparison.OrdinalIgnoreCase)) + { + flags |= (AssemblyNameFlags)(((int)ParseProcessorArchitecture(attributeValue)) << 4); + } + + if (attributeName.Equals("Retargetable", StringComparison.OrdinalIgnoreCase)) + { + if (attributeValue.Equals("Yes", StringComparison.OrdinalIgnoreCase)) + flags |= AssemblyNameFlags.Retargetable; + else if (attributeValue.Equals("No", StringComparison.OrdinalIgnoreCase)) + { + // nothing to do + } + else + throw new FileLoadException(SR.InvalidAssemblyName); + } + + if (attributeName.Equals("ContentType", StringComparison.OrdinalIgnoreCase)) + { + if (attributeValue.Equals("WindowsRuntime", StringComparison.OrdinalIgnoreCase)) + flags |= (AssemblyNameFlags)(((int)AssemblyContentType.WindowsRuntime) << 9); + else + throw new FileLoadException(SR.InvalidAssemblyName); + } + + // Desktop compat: If we got here, the attribute name is unknown to us. Ignore it (as long it's not duplicated.) + token = lexer.GetNext(); + } + return new RuntimeAssemblyName(name, version, cultureName, flags, pkt); + } + + private static Version ParseVersion(string attributeValue) + { + string[] parts = attributeValue.Split('.'); + if (parts.Length > 4) + throw new FileLoadException(SR.InvalidAssemblyName); + ushort[] versionNumbers = new ushort[4]; + for (int i = 0; i < versionNumbers.Length; i++) + { + if (i >= parts.Length) + versionNumbers[i] = ushort.MaxValue; + else + { + // Desktop compat: TryParse is a little more forgiving than Fusion. + for (int j = 0; j < parts[i].Length; j++) + { + if (!char.IsDigit(parts[i][j])) + throw new FileLoadException(SR.InvalidAssemblyName); + } + if (!(ushort.TryParse(parts[i], out versionNumbers[i]))) + { + throw new FileLoadException(SR.InvalidAssemblyName); + } + } + } + + if (versionNumbers[0] == ushort.MaxValue || versionNumbers[1] == ushort.MaxValue) + throw new FileLoadException(SR.InvalidAssemblyName); + if (versionNumbers[2] == ushort.MaxValue) + return new Version(versionNumbers[0], versionNumbers[1]); + if (versionNumbers[3] == ushort.MaxValue) + return new Version(versionNumbers[0], versionNumbers[1], versionNumbers[2]); + return new Version(versionNumbers[0], versionNumbers[1], versionNumbers[2], versionNumbers[3]); + } + + private static string ParseCulture(string attributeValue) + { + if (attributeValue.Equals("Neutral", StringComparison.OrdinalIgnoreCase)) + { + return ""; + } + else + { + CultureInfo culture = CultureInfo.GetCultureInfo(attributeValue); // Force a CultureNotFoundException if not a valid culture. + return culture.Name; + } + } + + private static byte[] ParsePKT(string attributeValue) + { + if (attributeValue.Equals("null", StringComparison.OrdinalIgnoreCase) || attributeValue == string.Empty) + return Array.Empty(); + + if (attributeValue.Length != 8 * 2) + throw new FileLoadException(SR.InvalidAssemblyName); + + byte[] pkt = new byte[8]; + int srcIndex = 0; + for (int i = 0; i < 8; i++) + { + char hi = attributeValue[srcIndex++]; + char lo = attributeValue[srcIndex++]; + pkt[i] = (byte)((ParseHexNybble(hi) << 4) | ParseHexNybble(lo)); + } + return pkt; + } + + private static ProcessorArchitecture ParseProcessorArchitecture(string attributeValue) + { + if (attributeValue.Equals("msil", StringComparison.OrdinalIgnoreCase)) + return ProcessorArchitecture.MSIL; + if (attributeValue.Equals("x86", StringComparison.OrdinalIgnoreCase)) + return ProcessorArchitecture.X86; + if (attributeValue.Equals("ia64", StringComparison.OrdinalIgnoreCase)) + return ProcessorArchitecture.IA64; + if (attributeValue.Equals("amd64", StringComparison.OrdinalIgnoreCase)) + return ProcessorArchitecture.Amd64; + if (attributeValue.Equals("arm", StringComparison.OrdinalIgnoreCase)) + return ProcessorArchitecture.Arm; + throw new FileLoadException(SR.InvalidAssemblyName); + } + + private static byte ParseHexNybble(char c) + { + if (c >= '0' && c <= '9') + return (byte)(c - '0'); + if (c >= 'a' && c <= 'f') + return (byte)(c - 'a' + 10); + if (c >= 'A' && c <= 'F') + return (byte)(c - 'A' + 10); + throw new FileLoadException(SR.InvalidAssemblyName); + } + + private static readonly char[] s_illegalCharactersInSimpleName = { '/', '\\', ':' }; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Attribute.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Attribute.CoreRT.cs new file mode 100644 index 00000000000000..d7f9bb693ea03f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Attribute.CoreRT.cs @@ -0,0 +1,161 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using Internal.LowLevelLinq; +using Internal.Reflection.Extensions.NonPortable; + +namespace System +{ + public abstract partial class Attribute + { + public static Attribute GetCustomAttribute(Assembly element, Type attributeType) + { + return OneOrNull(element.GetMatchingCustomAttributes(attributeType)); + } + public static Attribute GetCustomAttribute(Assembly element, Type attributeType, bool inherit) => GetCustomAttribute(element, attributeType); // "inherit" is meaningless for assemblies + + public static Attribute GetCustomAttribute(MemberInfo element, Type attributeType) => GetCustomAttribute(element, attributeType, inherit: true); + public static Attribute GetCustomAttribute(MemberInfo element, Type attributeType, bool inherit) + { + return OneOrNull(element.GetMatchingCustomAttributes(attributeType, inherit)); + } + + public static Attribute GetCustomAttribute(Module element, Type attributeType) + { + return OneOrNull(element.GetMatchingCustomAttributes(attributeType)); + } + public static Attribute GetCustomAttribute(Module element, Type attributeType, bool inherit) => GetCustomAttribute(element, attributeType); // "inherit" is meaningless for modules + + public static Attribute GetCustomAttribute(ParameterInfo element, Type attributeType) => CustomAttributeExtensions.GetCustomAttribute(element, attributeType, inherit: true); + public static Attribute GetCustomAttribute(ParameterInfo element, Type attributeType, bool inherit) + { + return OneOrNull(element.GetMatchingCustomAttributes(attributeType, inherit)); + } + + public static Attribute[] GetCustomAttributes(Assembly element) + { + IEnumerable matches = element.GetMatchingCustomAttributes(null, skipTypeValidation: true); + return matches.Select(m => m.Instantiate()).ToArray(); + } + public static Attribute[] GetCustomAttributes(Assembly element, bool inherit) => GetCustomAttributes(element); // "inherit" is meaningless for assemblies + public static Attribute[] GetCustomAttributes(Assembly element, Type attributeType) + { + return Instantiate(element.GetMatchingCustomAttributes(attributeType), attributeType); + } + public static Attribute[] GetCustomAttributes(Assembly element, Type attributeType, bool inherit) => GetCustomAttributes(element, attributeType); // "inherit" is meaningless for modules + + public static Attribute[] GetCustomAttributes(MemberInfo element) => GetCustomAttributes(element, inherit: true); + public static Attribute[] GetCustomAttributes(MemberInfo element, bool inherit) + { + IEnumerable matches = element.GetMatchingCustomAttributes(null, inherit, skipTypeValidation: true); + return matches.Select(m => m.Instantiate()).ToArray(); + } + public static Attribute[] GetCustomAttributes(MemberInfo element, Type attributeType) => GetCustomAttributes(element, attributeType, inherit: true); + public static Attribute[] GetCustomAttributes(MemberInfo element, Type attributeType, bool inherit) + { + return Instantiate(element.GetMatchingCustomAttributes(attributeType, inherit), attributeType); + } + + public static Attribute[] GetCustomAttributes(Module element) + { + IEnumerable matches = element.GetMatchingCustomAttributes(null, skipTypeValidation: true); + return matches.Select(m => m.Instantiate()).ToArray(); + } + public static Attribute[] GetCustomAttributes(Module element, bool inherit) => GetCustomAttributes(element); // "inherit" is meaningless for assemblies + public static Attribute[] GetCustomAttributes(Module element, Type attributeType) + { + return Instantiate(element.GetMatchingCustomAttributes(attributeType), attributeType); + } + public static Attribute[] GetCustomAttributes(Module element, Type attributeType, bool inherit) => GetCustomAttributes(element, attributeType); // "inherit" is meaningless for modules + + public static Attribute[] GetCustomAttributes(ParameterInfo element) => GetCustomAttributes(element, inherit: true); + public static Attribute[] GetCustomAttributes(ParameterInfo element, bool inherit) + { + IEnumerable matches = element.GetMatchingCustomAttributes(null, inherit, skipTypeValidation: true); + return matches.Select(m => m.Instantiate()).ToArray(); + } + public static Attribute[] GetCustomAttributes(ParameterInfo element, Type attributeType) => GetCustomAttributes(element, attributeType, inherit: true); + public static Attribute[] GetCustomAttributes(ParameterInfo element, Type attributeType, bool inherit) + { + return Instantiate(element.GetMatchingCustomAttributes(attributeType, inherit), attributeType); + } + + public static bool IsDefined(Assembly element, Type attributeType) + { + IEnumerable matches = element.GetMatchingCustomAttributes(attributeType); + return matches.Any(); + } + public static bool IsDefined(Assembly element, Type attributeType, bool inherit) => IsDefined(element, attributeType); // "inherit" is meaningless for assemblies + + public static bool IsDefined(MemberInfo element, Type attributeType) => IsDefined(element, attributeType, inherit: true); + public static bool IsDefined(MemberInfo element, Type attributeType, bool inherit) + { + IEnumerable matches = element.GetMatchingCustomAttributes(attributeType, inherit); + return matches.Any(); + } + + public static bool IsDefined(Module element, Type attributeType) + { + IEnumerable matches = element.GetMatchingCustomAttributes(attributeType); + return matches.Any(); + } + public static bool IsDefined(Module element, Type attributeType, bool inherit) => IsDefined(element, attributeType); // "inherit" is meaningless for modules + + public static bool IsDefined(ParameterInfo element, Type attributeType) => IsDefined(element, attributeType, inherit: true); + public static bool IsDefined(ParameterInfo element, Type attributeType, bool inherit) + { + IEnumerable matches = element.GetMatchingCustomAttributes(attributeType, inherit); + return matches.Any(); + } + + //============================================================================================================================== + // Helper for the GetCustomAttribute() family. + //============================================================================================================================== + private static Attribute OneOrNull(IEnumerable results) + { + IEnumerator enumerator = results.GetEnumerator(); + if (!enumerator.MoveNext()) + return null; + CustomAttributeData result = enumerator.Current; + if (enumerator.MoveNext()) + throw new AmbiguousMatchException(); + return result.Instantiate(); + } + + //============================================================================================================================== + // Helper for the GetCustomAttributes() methods that take a specific attribute type. For desktop compatibility, + // we return a freshly allocated array of the specific attribute type even though the api's return type promises only an Attribute[]. + // There are known store apps that cast the results of apis and expect the cast to work. + //============================================================================================================================== + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "Arrays of reference types are safe to create.")] + private static Attribute[] Instantiate(IEnumerable cads, Type actualElementType) + { + LowLevelList attributes = new LowLevelList(); + foreach (CustomAttributeData cad in cads) + { + Attribute instantiatedAttribute = cad.Instantiate(); + attributes.Add(instantiatedAttribute); + } + int count = attributes.Count; + Attribute[] result; + try + { + result = (Attribute[])Array.CreateInstance(actualElementType, count); + } + catch (NotSupportedException) when (actualElementType.ContainsGenericParameters) + { + // This is here for desktop compatibility (using try-catch as control flow to avoid slowing down the mainline case.) + // GetCustomAttributes() normally returns an array of the exact attribute type requested except when + // the requested type is an open type. Its ICustomAttributeProvider counterpart would return an Object[] array but that's + // not possible with this api's return type so it returns null instead. + return null; + } + attributes.CopyTo(result, 0); + return result; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/BinderBundle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/BinderBundle.cs new file mode 100644 index 00000000000000..29ddf87589dae3 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/BinderBundle.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Globalization; + +namespace System.Reflection +{ + // If allocated, indicates the non-default Binder and culture to use for coercing parameters to a dynamic Invoke. Combining + // these two items in a class makes custom binders more pay-for-play (one less parameter and TLS local for the non-binder case + // to manage.) + // + // This is not an api type but needs to be public as both Reflection.Core and System.Private.Corelib accesses it. + [System.Runtime.CompilerServices.ReflectionBlocked] + public sealed class BinderBundle + { + public BinderBundle(Binder binder, CultureInfo culture) + { + // This is not just performance, it is correctness too. The default binder's ChangeType() method throws a NotSupportedException so you really can't treat it + // as "just another binder." + Debug.Assert(binder != null && binder != Type.DefaultBinder, "Not permitted to allocate a BinderBundle for the default Binder. Must pass a null BinderBundle instread."); + _binder = binder; + _culture = culture; + } + + public object ChangeType(object value, Type type) + { + return _binder.ChangeType(value, type, _culture); + } + + private readonly Binder _binder; + private readonly CultureInfo _culture; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs new file mode 100644 index 00000000000000..7e663bb64c6f6b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.IO; + +namespace System.Reflection.Emit +{ + public sealed partial class AssemblyBuilder : Assembly + { + internal AssemblyBuilder() + { + // Prevent generating a default constructor + } + + public override string FullName + { + get + { + return default; + } + } + + public override Module ManifestModule + { + get + { + return default; + } + } + + [RequiresDynamicCode("Generating new code at runtime is not supported with native AOT.")] + public static AssemblyBuilder DefineDynamicAssembly(AssemblyName name, AssemblyBuilderAccess access) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + [RequiresDynamicCode("Generating new code at runtime is not supported with native AOT.")] + public static AssemblyBuilder DefineDynamicAssembly(AssemblyName name, AssemblyBuilderAccess access, IEnumerable assemblyAttributes) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + public ModuleBuilder DefineDynamicModule(string name) + { + return default; + } + + public override bool Equals(object? obj) + { + return default; + } + + public ModuleBuilder GetDynamicModule(string name) + { + return default; + } + + public override int GetHashCode() + { + return default; + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs new file mode 100644 index 00000000000000..c4e70d9da995e3 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; + +namespace System.Reflection.Emit +{ + public sealed class ConstructorBuilder : ConstructorInfo + { + internal ConstructorBuilder() + { + // Prevent generating a default constructor + } + + public override MethodAttributes Attributes + { + get + { + return default; + } + } + + public override CallingConventions CallingConvention + { + get + { + return default; + } + } + + public override Type DeclaringType + { + get + { + return default; + } + } + + public bool InitLocals + { + get + { + return default; + } + set + { + } + } + + public override RuntimeMethodHandle MethodHandle + { + get + { + return default; + } + } + + public override Module Module + { + get + { + return default; + } + } + + public override string Name + { + get + { + return default; + } + } + + public override Type ReflectedType + { + get + { + return default; + } + } + + public ParameterBuilder DefineParameter(int iSequence, ParameterAttributes attributes, string strParamName) + { + return default; + } + + public override object[] GetCustomAttributes(bool inherit) + { + return default; + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return default; + } + + public ILGenerator GetILGenerator() + { + return default; + } + + public ILGenerator GetILGenerator(int streamSize) + { + return default; + } + + public override MethodImplAttributes GetMethodImplementationFlags() + { + return default; + } + + public override ParameterInfo[] GetParameters() + { + return default; + } + + public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + return default; + } + + public override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + return default; + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return default; + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + } + + public void SetImplementationFlags(MethodImplAttributes attributes) + { + } + + public override string ToString() + { + return default; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs new file mode 100644 index 00000000000000..5a80b4318eddb1 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/CustomAttributeBuilder.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Emit +{ + public class CustomAttributeBuilder + { + public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + + public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, FieldInfo[] namedFields, object[] fieldValues) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + + public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + + public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues, FieldInfo[] namedFields, object[] fieldValues) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILInfo.cs new file mode 100644 index 00000000000000..86e62ea765e496 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILInfo.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Emit +{ + public class DynamicILInfo + { + internal DynamicILInfo() + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + + public DynamicMethod DynamicMethod { get { return default; } } + + public void SetCode(byte[] code, int maxStackSize) { } + + [CLSCompliant(false)] + public unsafe void SetCode(byte* code, int codeSize, int maxStackSize) { } + + public void SetExceptions(byte[] exceptions) { } + + [CLSCompliant(false)] + public unsafe void SetExceptions(byte* exceptions, int exceptionsSize) { } + + public void SetLocalSignature(byte[] localSignature) { } + + [CLSCompliant(false)] + public unsafe void SetLocalSignature(byte* localSignature, int signatureSize) { } + + public int GetTokenFor(RuntimeMethodHandle method) + { + return default; + } + public int GetTokenFor(DynamicMethod method) + { + return default; + } + public int GetTokenFor(RuntimeMethodHandle method, RuntimeTypeHandle contextType) + { + return default; + } + public int GetTokenFor(RuntimeFieldHandle field) + { + return default; + } + public int GetTokenFor(RuntimeFieldHandle field, RuntimeTypeHandle contextType) + { + return default; + } + public int GetTokenFor(RuntimeTypeHandle type) + { + return default; + } + public int GetTokenFor(string literal) + { + return default; + } + public int GetTokenFor(byte[] signature) + { + return default; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs new file mode 100644 index 00000000000000..87d4ab1f1135a5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs @@ -0,0 +1,212 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +namespace System.Reflection.Emit +{ + public sealed class DynamicMethod : MethodInfo + { + [RequiresDynamicCode("Generating new code at runtime is not supported with native AOT.")] + public DynamicMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Module m, bool skipVisibility) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + + [RequiresDynamicCode("Generating new code at runtime is not supported with native AOT.")] + public DynamicMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type owner, bool skipVisibility) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + + [RequiresDynamicCode("Generating new code at runtime is not supported with native AOT.")] + public DynamicMethod(string name, Type returnType, Type[] parameterTypes) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + + [RequiresDynamicCode("Generating new code at runtime is not supported with native AOT.")] + public DynamicMethod(string name, Type returnType, Type[] parameterTypes, bool restrictedSkipVisibility) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + + [RequiresDynamicCode("Generating new code at runtime is not supported with native AOT.")] + public DynamicMethod(string name, Type returnType, Type[] parameterTypes, Module m) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + + [RequiresDynamicCode("Generating new code at runtime is not supported with native AOT.")] + public DynamicMethod(string name, Type returnType, Type[] parameterTypes, Module m, bool skipVisibility) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + + [RequiresDynamicCode("Generating new code at runtime is not supported with native AOT.")] + public DynamicMethod(string name, Type returnType, Type[] parameterTypes, Type owner) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + + [RequiresDynamicCode("Generating new code at runtime is not supported with native AOT.")] + public DynamicMethod(string name, Type returnType, Type[] parameterTypes, Type owner, bool skipVisibility) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + } + + public override MethodAttributes Attributes + { + get + { + return default; + } + } + + public override CallingConventions CallingConvention + { + get + { + return default; + } + } + + public override Type DeclaringType + { + get + { + return default; + } + } + + public bool InitLocals + { + get + { + return default; + } + set + { + } + } + + public override RuntimeMethodHandle MethodHandle + { + get + { + return default; + } + } + + public override string Name + { + get + { + return default; + } + } + + public override Type ReflectedType + { + get + { + return default; + } + } + + public override ParameterInfo ReturnParameter + { + get + { + return default; + } + } + + public override Type ReturnType + { + get + { + return default; + } + } + + public override ICustomAttributeProvider ReturnTypeCustomAttributes + { + get + { + return default; + } + } + + public sealed override Delegate CreateDelegate(Type delegateType) + { + return default; + } + + public sealed override Delegate CreateDelegate(Type delegateType, object target) + { + return default; + } + + public ParameterBuilder DefineParameter(int position, ParameterAttributes attributes, string parameterName) + { + return default; + } + + public DynamicILInfo GetDynamicILInfo() + { + return default; + } + + public override MethodInfo GetBaseDefinition() + { + return default; + } + + public override object[] GetCustomAttributes(bool inherit) + { + return default; + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return default; + } + + public ILGenerator GetILGenerator() + { + return default; + } + + public ILGenerator GetILGenerator(int streamSize) + { + return default; + } + + public override MethodImplAttributes GetMethodImplementationFlags() + { + return default; + } + + public override ParameterInfo[] GetParameters() + { + return default; + } + + public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + return default; + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return default; + } + + public override string ToString() + { + return default; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs new file mode 100644 index 00000000000000..58958a755267ba --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs @@ -0,0 +1,388 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; + +namespace System.Reflection.Emit +{ + public sealed partial class EnumBuilder : TypeInfo + { + internal EnumBuilder() + { + // Prevent generating a default constructor + } + + public override Assembly Assembly + { + get + { + return default; + } + } + + public override string AssemblyQualifiedName + { + get + { + return default; + } + } + + public override Type BaseType + { + get + { + return default; + } + } + + public override Type DeclaringType + { + get + { + return default; + } + } + + public override string FullName + { + get + { + return default; + } + } + + public override Guid GUID + { + get + { + return default; + } + } + + public override bool IsConstructedGenericType + { + get + { + return default; + } + } + + public override Module Module + { + get + { + return default; + } + } + + public override string Name + { + get + { + return default; + } + } + + public override string Namespace + { + get + { + return default; + } + } + + public override Type ReflectedType + { + get + { + return default; + } + } + + public override RuntimeTypeHandle TypeHandle + { + get + { + return default; + } + } + + public FieldBuilder UnderlyingField + { + get + { + return default; + } + } + + public override Type UnderlyingSystemType + { + get + { + return default; + } + } + + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public Type? CreateType() + { + return default; + } + + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public TypeInfo? CreateTypeInfo() + { + return default; + } + + public FieldBuilder DefineLiteral(string literalName, object literalValue) + { + return default; + } + + protected override TypeAttributes GetAttributeFlagsImpl() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) + { + return default; + } + + public override object[] GetCustomAttributes(bool inherit) + { + return default; + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return default; + } + + public override Type GetElementType() + { + return default; + } + + public override Type GetEnumUnderlyingType() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo GetEvent(string name, BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents)] + public override EventInfo[] GetEvents() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo[] GetEvents(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo GetField(string name, BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo[] GetFields(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public override Type? GetInterface(string name, bool ignoreCase) + { + return default; + } + + public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public override Type[] GetInterfaces() + { + return default; + } + + [DynamicallyAccessedMembers(GetAllMembers)] + public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(GetAllMembers)] + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, System.Type[] types, ParameterModifier[] modifiers) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type GetNestedType(string name, BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type[] GetNestedTypes(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) + { + return default; + } + + protected override bool HasElementTypeImpl() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] namedParameters) + { + return default; + } + + protected override bool IsArrayImpl() + { + return default; + } + + public override bool IsAssignableFrom([NotNullWhen(true)] TypeInfo? typeInfo) + { + return default; + } + + protected override bool IsByRefImpl() + { + return default; + } + + public override bool IsByRefLike + { + get + { + return default; + } + } + + protected override bool IsCOMObjectImpl() + { + return default; + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return default; + } + + protected override bool IsPointerImpl() + { + return default; + } + + protected override bool IsPrimitiveImpl() + { + return default; + } + + public override bool IsTypeDefinition + { + get + { + return default; + } + } + + public override bool IsSZArray + { + get + { + return default; + } + } + + public override bool IsVariableBoundArray + { + get + { + return default; + } + } + + protected override bool IsValueTypeImpl() + { + return default; + } + + public override Type MakeArrayType() + { + return default; + } + + public override Type MakeArrayType(int rank) + { + return default; + } + + public override Type MakeByRefType() + { + return default; + } + + public override Type MakePointerType() + { + return default; + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/EventBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/EventBuilder.cs new file mode 100644 index 00000000000000..7abaec6cb2ec70 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/EventBuilder.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Emit +{ + public sealed class EventBuilder + { + internal EventBuilder() + { + // Prevent generating a default constructor + } + + public void AddOtherMethod(MethodBuilder mdBuilder) + { + } + + public void SetAddOnMethod(MethodBuilder mdBuilder) + { + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + } + + public void SetRaiseMethod(MethodBuilder mdBuilder) + { + } + + public void SetRemoveOnMethod(MethodBuilder mdBuilder) + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/FieldBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/FieldBuilder.cs new file mode 100644 index 00000000000000..6c57ca73eebf8b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/FieldBuilder.cs @@ -0,0 +1,104 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; + +namespace System.Reflection.Emit +{ + + public sealed class FieldBuilder : FieldInfo + { + internal FieldBuilder() + { + // Prevent generating a default constructor + } + + public override FieldAttributes Attributes + { + get + { + return default; + } + } + + public override Type DeclaringType + { + get + { + return default; + } + } + + public override RuntimeFieldHandle FieldHandle + { + get + { + return default; + } + } + + public override Type FieldType + { + get + { + return default; + } + } + + public override string Name + { + get + { + return default; + } + } + + public override Type ReflectedType + { + get + { + return default; + } + } + + public override object[] GetCustomAttributes(bool inherit) + { + return default; + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return default; + } + + public override object GetValue(object obj) + { + return default; + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return default; + } + + public void SetConstant(object defaultValue) + { + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + } + + public void SetOffset(int iOffset) + { + } + + public override void SetValue(object obj, object val, BindingFlags invokeAttr, Binder binder, CultureInfo culture) + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs new file mode 100644 index 00000000000000..38ad036dab2315 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs @@ -0,0 +1,467 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; + +namespace System.Reflection.Emit +{ + public sealed partial class GenericTypeParameterBuilder : TypeInfo + { + internal GenericTypeParameterBuilder() + { + // Prevent generating a default constructor + } + + public override Assembly Assembly + { + get + { + return default; + } + } + + public override string AssemblyQualifiedName + { + get + { + return default; + } + } + + public override Type BaseType + { + get + { + return default; + } + } + + public override bool ContainsGenericParameters + { + get + { + return default; + } + } + + public override MethodBase DeclaringMethod + { + get + { + return default; + } + } + + public override Type DeclaringType + { + get + { + return default; + } + } + + public override string FullName + { + get + { + return default; + } + } + + public override GenericParameterAttributes GenericParameterAttributes + { + get + { + return default; + } + } + + public override int GenericParameterPosition + { + get + { + return default; + } + } + + public override Guid GUID + { + get + { + return default; + } + } + + public override bool IsConstructedGenericType + { + get + { + return default; + } + } + + public override bool IsGenericParameter + { + get + { + return default; + } + } + + public override bool IsGenericType + { + get + { + return default; + } + } + + public override bool IsGenericTypeDefinition + { + get + { + return default; + } + } + + public override Module Module + { + get + { + return default; + } + } + + public override string Name + { + get + { + return default; + } + } + + public override string Namespace + { + get + { + return default; + } + } + + public override Type ReflectedType + { + get + { + return default; + } + } + + public override RuntimeTypeHandle TypeHandle + { + get + { + return default; + } + } + + public override Type UnderlyingSystemType + { + get + { + return default; + } + } + + public override bool Equals(object? o) + { + return default; + } + + protected override TypeAttributes GetAttributeFlagsImpl() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) + { + return default; + } + + public override object[] GetCustomAttributes(bool inherit) + { + return default; + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return default; + } + + public override Type GetElementType() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo GetEvent(string name, BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents)] + public override EventInfo[] GetEvents() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo[] GetEvents(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo GetField(string name, BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo[] GetFields(BindingFlags bindingAttr) + { + return default; + } + + public override Type[] GetGenericArguments() + { + return default; + } + + public override Type GetGenericTypeDefinition() + { + return default; + } + + public override int GetHashCode() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public override Type GetInterface(string name, bool ignoreCase) + { + return default; + } + + public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public override Type[] GetInterfaces() + { + return default; + } + + [DynamicallyAccessedMembers(GetAllMembers)] + public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(GetAllMembers)] + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type GetNestedType(string name, BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type[] GetNestedTypes(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) + { + return default; + } + + protected override bool HasElementTypeImpl() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, Globalization.CultureInfo culture, string[] namedParameters) + { + return default; + } + + protected override bool IsArrayImpl() + { + return default; + } + + public override bool IsAssignableFrom([NotNullWhen(true)] TypeInfo? typeInfo) + { + return default; + } + + public override bool IsAssignableFrom([NotNullWhen(true)] Type? c) + { + return default; + } + + protected override bool IsByRefImpl() + { + return default; + } + + public override bool IsByRefLike + { + get + { + return default; + } + } + + protected override bool IsCOMObjectImpl() + { + return default; + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return default; + } + + protected override bool IsPointerImpl() + { + return default; + } + + protected override bool IsPrimitiveImpl() + { + return default; + } + + public override bool IsSubclassOf(Type c) + { + return default; + } + + public override bool IsTypeDefinition + { + get + { + return default; + } + } + + public override bool IsSZArray + { + get + { + return default; + } + } + + public override bool IsVariableBoundArray + { + get + { + return default; + } + } + + protected override bool IsValueTypeImpl() + { + return default; + } + + public override Type MakeArrayType() + { + return default; + } + + public override Type MakeArrayType(int rank) + { + return default; + } + + public override Type MakeByRefType() + { + return default; + } + + [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + public override Type MakeGenericType(params Type[] typeArguments) + { + return default; + } + + public override Type MakePointerType() + { + return default; + } + + public void SetBaseTypeConstraint([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type baseTypeConstraint) + { + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + } + + public void SetGenericParameterAttributes(GenericParameterAttributes genericParameterAttributes) + { + } + + public void SetInterfaceConstraints(params Type[] interfaceConstraints) + { + } + + public override string ToString() + { + return default; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs new file mode 100644 index 00000000000000..b5e8e0dc0230f9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs @@ -0,0 +1,177 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace System.Reflection.Emit +{ + public partial class ILGenerator + { + internal ILGenerator() + { + // Prevent generating a default constructor + } + + public virtual int ILOffset + { + get + { + return default; + } + } + + public virtual void BeginCatchBlock(Type exceptionType) + { + } + + public virtual void BeginExceptFilterBlock() + { + } + + public virtual Label BeginExceptionBlock() + { + return default; + } + + public virtual void BeginFaultBlock() + { + } + + public virtual void BeginFinallyBlock() + { + } + + public virtual void BeginScope() + { + } + + public virtual LocalBuilder DeclareLocal(Type localType) + { + return default; + } + + public virtual LocalBuilder DeclareLocal(Type localType, bool pinned) + { + return default; + } + + public virtual Label DefineLabel() + { + return default; + } + + public virtual void Emit(OpCode opcode) + { + } + + public virtual void Emit(OpCode opcode, byte arg) + { + } + + public virtual void Emit(OpCode opcode, double arg) + { + } + + public virtual void Emit(OpCode opcode, short arg) + { + } + + public virtual void Emit(OpCode opcode, int arg) + { + } + + public virtual void Emit(OpCode opcode, long arg) + { + } + + public virtual void Emit(OpCode opcode, ConstructorInfo con) + { + } + + public virtual void Emit(OpCode opcode, Label label) + { + } + + public virtual void Emit(OpCode opcode, Label[] labels) + { + } + + public virtual void Emit(OpCode opcode, LocalBuilder local) + { + } + + public virtual void Emit(OpCode opcode, SignatureHelper signature) + { + } + + public virtual void Emit(OpCode opcode, FieldInfo field) + { + } + + public virtual void Emit(OpCode opcode, MethodInfo meth) + { + } + + [CLSCompliantAttribute(false)] + public void Emit(OpCode opcode, sbyte arg) + { + } + + public virtual void Emit(OpCode opcode, float arg) + { + } + + public virtual void Emit(OpCode opcode, string str) + { + } + + public virtual void Emit(OpCode opcode, Type cls) + { + } + + public virtual void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[] optionalParameterTypes) + { + } + + public virtual void EmitCalli(OpCode opcode, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes) + { + } + + public virtual void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes) + { + } + + public virtual void EmitWriteLine(LocalBuilder localBuilder) + { + } + + public virtual void EmitWriteLine(FieldInfo fld) + { + } + + public virtual void EmitWriteLine(string value) + { + } + + public virtual void EndExceptionBlock() + { + } + + public virtual void EndScope() + { + } + + public virtual void MarkLabel(Label loc) + { + } + + public virtual void ThrowException([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type excType) + { + } + + public virtual void UsingNamespace(string usingNamespace) + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs new file mode 100644 index 00000000000000..2d1afc04d5b730 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/LocalBuilder.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Emit +{ + public sealed class LocalBuilder : LocalVariableInfo + { + internal LocalBuilder() + { + // Prevent generating a default constructor + } + + public override bool IsPinned + { + get + { + return default; + } + } + + public override int LocalIndex + { + get + { + return default; + } + } + + public override Type LocalType + { + get + { + return default; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs new file mode 100644 index 00000000000000..4f0a6262887782 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs @@ -0,0 +1,249 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; + +namespace System.Reflection.Emit +{ + public sealed class MethodBuilder : MethodInfo + { + internal MethodBuilder() + { + // Prevent generating a default constructor + } + + public override MethodAttributes Attributes + { + get + { + return default; + } + } + + public override CallingConventions CallingConvention + { + get + { + return default; + } + } + + public override bool ContainsGenericParameters + { + get + { + return default; + } + } + + public override Type DeclaringType + { + get + { + return default; + } + } + + public bool InitLocals + { + get + { + return default; + } + set + { + } + } + + public override bool IsGenericMethod + { + get + { + return default; + } + } + + public override bool IsGenericMethodDefinition + { + get + { + return default; + } + } + + public override bool IsConstructedGenericMethod + { + get + { + return default; + } + } + + public override RuntimeMethodHandle MethodHandle + { + get + { + return default; + } + } + + public override Module Module + { + get + { + return default; + } + } + + public override string Name + { + get + { + return default; + } + } + + public override Type ReflectedType + { + get + { + return default; + } + } + + public override ParameterInfo ReturnParameter + { + get + { + return default; + } + } + + public override Type ReturnType + { + get + { + return default; + } + } + + public override ICustomAttributeProvider ReturnTypeCustomAttributes + { + get + { + return default; + } + } + + public GenericTypeParameterBuilder[] DefineGenericParameters(params string[] names) + { + return default; + } + + public ParameterBuilder DefineParameter(int position, ParameterAttributes attributes, string strParamName) + { + return default; + } + + public override bool Equals(object? obj) + { + return default; + } + + public override MethodInfo GetBaseDefinition() + { + return default; + } + + public override object[] GetCustomAttributes(bool inherit) + { + return default; + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return default; + } + + public override Type[] GetGenericArguments() + { + return default; + } + + public override MethodInfo GetGenericMethodDefinition() + { + return default; + } + + public override int GetHashCode() + { + return default; + } + + public ILGenerator GetILGenerator() + { + return default; + } + + public ILGenerator GetILGenerator(int size) + { + return default; + } + + public override MethodImplAttributes GetMethodImplementationFlags() + { + return default; + } + + public override ParameterInfo[] GetParameters() + { + return default; + } + + public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, Globalization.CultureInfo culture) + { + return default; + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return default; + } + + [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] + [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + public override MethodInfo MakeGenericMethod(params Type[] typeArguments) + { + return default; + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + } + + public void SetImplementationFlags(MethodImplAttributes attributes) + { + } + + public void SetParameters(params Type[] parameterTypes) + { + } + + public void SetReturnType(Type returnType) + { + } + + public void SetSignature(Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers, Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers) + { + } + + public override string ToString() + { + return default; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs new file mode 100644 index 00000000000000..1715d80ef6702c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs @@ -0,0 +1,146 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace System.Reflection.Emit +{ + public class ModuleBuilder : Module + { + internal ModuleBuilder() + { + // Prevent generating a default constructor + } + + public override Assembly Assembly + { + get + { + return default; + } + } + + [RequiresAssemblyFiles(UnknownStringMessageInRAF)] + public override string FullyQualifiedName + { + get + { + return default; + } + } + + [RequiresAssemblyFiles(UnknownStringMessageInRAF)] + public override string Name + { + get + { + return default; + } + } + + public void CreateGlobalFunctions() + { + } + + public EnumBuilder DefineEnum(string name, TypeAttributes visibility, Type underlyingType) + { + return default; + } + + public MethodBuilder DefineGlobalMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes) + { + return default; + } + + public MethodBuilder DefineGlobalMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] requiredReturnTypeCustomModifiers, Type[] optionalReturnTypeCustomModifiers, Type[] parameterTypes, Type[][] requiredParameterTypeCustomModifiers, Type[][] optionalParameterTypeCustomModifiers) + { + return default; + } + + public MethodBuilder DefineGlobalMethod(string name, MethodAttributes attributes, Type returnType, Type[] parameterTypes) + { + return default; + } + + public FieldBuilder DefineInitializedData(string name, byte[] data, FieldAttributes attributes) + { + return default; + } + + [RequiresUnreferencedCode("P/Invoke marshalling may dynamically access members that could be trimmed.")] + public MethodBuilder DefinePInvokeMethod(string name, string dllName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + return default; + } + + [RequiresUnreferencedCode("P/Invoke marshalling may dynamically access members that could be trimmed.")] + public MethodBuilder DefinePInvokeMethod(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + return default; + } + + public TypeBuilder DefineType(string name) + { + return default; + } + + public TypeBuilder DefineType(string name, TypeAttributes attr) + { + return default; + } + + public TypeBuilder DefineType(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type parent) + { + return default; + } + + public TypeBuilder DefineType(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type parent, int typesize) + { + return default; + } + + public TypeBuilder DefineType(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type parent, PackingSize packsize) + { + return default; + } + + public TypeBuilder DefineType(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type parent, PackingSize packingSize, int typesize) + { + return default; + } + + public TypeBuilder DefineType(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type parent, Type[] interfaces) + { + return default; + } + + public FieldBuilder DefineUninitializedData(string name, int size, FieldAttributes attributes) + { + return default; + } + + public override bool Equals(object? obj) + { + return default; + } + + public MethodInfo GetArrayMethod(Type arrayClass, string methodName, CallingConventions callingConvention, Type returnType, Type[] parameterTypes) + { + return default; + } + + public override int GetHashCode() + { + return default; + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.cs new file mode 100644 index 00000000000000..2c02ff1f1ea7b6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ParameterBuilder.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Emit +{ + public partial class ParameterBuilder + { + internal ParameterBuilder() + { + // Prevent generating a default constructor + } + + public virtual int Attributes + { + get + { + return default; + } + } + + public bool IsIn + { + get + { + return default; + } + } + + public bool IsOptional + { + get + { + return default; + } + } + + public bool IsOut + { + get + { + return default; + } + } + + public virtual string Name + { + get + { + return default; + } + } + + public virtual int Position + { + get + { + return default; + } + } + + public virtual void SetConstant(object defaultValue) + { + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/PropertyBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/PropertyBuilder.cs new file mode 100644 index 00000000000000..0407041f9c5704 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/PropertyBuilder.cs @@ -0,0 +1,154 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Emit +{ + public sealed class PropertyBuilder : PropertyInfo + { + internal PropertyBuilder() + { + // Prevent generating a default constructor + } + + public override PropertyAttributes Attributes + { + get + { + return default; + } + } + + public override bool CanRead + { + get + { + return default; + } + } + + public override bool CanWrite + { + get + { + return default; + } + } + + public override Type DeclaringType + { + get + { + return default; + } + } + + public override Module Module + { + get + { + return default; + } + } + + public override string Name + { + get + { + return default; + } + } + + public override Type PropertyType + { + get + { + return default; + } + } + + public override Type ReflectedType + { + get + { + return default; + } + } + + public void AddOtherMethod(MethodBuilder mdBuilder) + { + } + + public override MethodInfo[] GetAccessors(bool nonPublic) + { + return default; + } + + public override object[] GetCustomAttributes(bool inherit) + { + return default; + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return default; + } + + public override MethodInfo GetGetMethod(bool nonPublic) + { + return default; + } + + public override ParameterInfo[] GetIndexParameters() + { + return default; + } + + public override MethodInfo GetSetMethod(bool nonPublic) + { + return default; + } + + public override object GetValue(object obj, object[] index) + { + return default; + } + + public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, Globalization.CultureInfo culture) + { + return default; + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return default; + } + + public void SetConstant(object defaultValue) + { + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + } + + public void SetGetMethod(MethodBuilder mdBuilder) + { + } + + public void SetSetMethod(MethodBuilder mdBuilder) + { + } + + public override void SetValue(object obj, object value, object[] index) + { + } + + public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, Globalization.CultureInfo culture) + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ReflectionEmitThrower.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ReflectionEmitThrower.cs new file mode 100644 index 00000000000000..c00a6142c51b88 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/ReflectionEmitThrower.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Emit +{ + internal static class ReflectionEmitThrower + { + public static void ThrowPlatformNotSupportedException() + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionEmit); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/SignatureHelper.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/SignatureHelper.cs new file mode 100644 index 00000000000000..2625d0a34290b7 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/SignatureHelper.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Emit +{ + public sealed class SignatureHelper + { + internal SignatureHelper() + { + // Prevent generating a default constructor + } + + public void AddArgument(Type clsArgument) + { + } + + public void AddArgument(Type argument, bool pinned) + { + } + + public void AddArgument(Type argument, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers) + { + } + + public void AddArguments(Type[] arguments, Type[][] requiredCustomModifiers, Type[][] optionalCustomModifiers) + { + } + + public void AddSentinel() + { + } + + public override bool Equals(object? obj) + { + return default; + } + + public static SignatureHelper GetFieldSigHelper(Module mod) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + public override int GetHashCode() + { + return default; + } + + public static SignatureHelper GetLocalVarSigHelper() + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + public static SignatureHelper GetLocalVarSigHelper(Module mod) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + public static SignatureHelper GetMethodSigHelper(CallingConventions callingConvention, Type returnType) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + public static SignatureHelper GetMethodSigHelper(Module mod, CallingConventions callingConvention, Type returnType) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + public static SignatureHelper GetMethodSigHelper(Module mod, Type returnType, Type[] parameterTypes) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + public static SignatureHelper GetPropertySigHelper(Module mod, CallingConventions callingConvention, Type returnType, Type[] requiredReturnTypeCustomModifiers, Type[] optionalReturnTypeCustomModifiers, Type[] parameterTypes, Type[][] requiredParameterTypeCustomModifiers, Type[][] optionalParameterTypeCustomModifiers) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + public static SignatureHelper GetPropertySigHelper(Module mod, Type returnType, Type[] parameterTypes) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + public static SignatureHelper GetPropertySigHelper(Module mod, Type returnType, Type[] requiredReturnTypeCustomModifiers, Type[] optionalReturnTypeCustomModifiers, Type[] parameterTypes, Type[][] requiredParameterTypeCustomModifiers, Type[][] optionalParameterTypeCustomModifiers) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + public byte[] GetSignature() + { + return default; + } + + public override string ToString() + { + return default; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs new file mode 100644 index 00000000000000..86ab332f749552 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs @@ -0,0 +1,668 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace System.Reflection.Emit +{ + public sealed partial class TypeBuilder : TypeInfo + { + internal TypeBuilder() + { + // Prevent generating a default constructor + } + + public const int UnspecifiedTypeSize = 0; + + public override Assembly Assembly + { + get + { + return default; + } + } + + public override string AssemblyQualifiedName + { + get + { + return default; + } + } + + public override Type BaseType + { + get + { + return default; + } + } + + public override MethodBase DeclaringMethod + { + get + { + return default; + } + } + + public override Type DeclaringType + { + get + { + return default; + } + } + + public override string FullName + { + get + { + return default; + } + } + + public override GenericParameterAttributes GenericParameterAttributes + { + get + { + return default; + } + } + + public override int GenericParameterPosition + { + get + { + return default; + } + } + + public override Guid GUID + { + get + { + return default; + } + } + + public override bool IsByRefLike + { + get + { + return default; + } + } + + public override bool IsConstructedGenericType + { + get + { + return default; + } + } + + public override bool IsGenericParameter + { + get + { + return default; + } + } + + public override bool IsGenericType + { + get + { + return default; + } + } + + public override bool IsGenericTypeDefinition + { + get + { + return default; + } + } + + public override bool IsSecurityCritical + { + get + { + return default; + } + } + + public override bool IsSecuritySafeCritical + { + get + { + return default; + } + } + + public override bool IsSecurityTransparent + { + get + { + return default; + } + } + + public override Module Module + { + get + { + return default; + } + } + + public override string Name + { + get + { + return default; + } + } + + public override string Namespace + { + get + { + return default; + } + } + + public PackingSize PackingSize + { + get + { + return default; + } + } + + public override Type ReflectedType + { + get + { + return default; + } + } + + public int Size + { + get + { + return default; + } + } + + public override RuntimeTypeHandle TypeHandle + { + get + { + return default; + } + } + + public override Type UnderlyingSystemType + { + get + { + return default; + } + } + + public void AddInterfaceImplementation([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type interfaceType) { } + + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public Type? CreateType() + { + return default; + } + + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public TypeInfo? CreateTypeInfo() + { + return default; + } + + public ConstructorBuilder DefineConstructor(MethodAttributes attributes, CallingConventions callingConvention, Type[] parameterTypes) + { + return default; + } + + public ConstructorBuilder DefineConstructor(MethodAttributes attributes, CallingConventions callingConvention, Type[] parameterTypes, Type[][] requiredCustomModifiers, Type[][] optionalCustomModifiers) + { + return default; + } + + public ConstructorBuilder DefineDefaultConstructor(MethodAttributes attributes) + { + return default; + } + + public EventBuilder DefineEvent(string name, EventAttributes attributes, Type eventtype) + { + return default; + } + + public FieldBuilder DefineField(string fieldName, Type type, FieldAttributes attributes) + { + return default; + } + + public FieldBuilder DefineField(string fieldName, Type type, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers, FieldAttributes attributes) + { + return default; + } + + public GenericTypeParameterBuilder[] DefineGenericParameters(params string[] names) + { + return default; + } + + public FieldBuilder DefineInitializedData(string name, byte[] data, FieldAttributes attributes) + { + return default; + } + + public MethodBuilder DefineMethod(string name, MethodAttributes attributes) + { + return default; + } + + public MethodBuilder DefineMethod(string name, MethodAttributes attributes, CallingConventions callingConvention) + { + return default; + } + + public MethodBuilder DefineMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes) + { + return default; + } + + public MethodBuilder DefineMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers, Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers) + { + return default; + } + + public MethodBuilder DefineMethod(string name, MethodAttributes attributes, Type returnType, Type[] parameterTypes) + { + return default; + } + + public void DefineMethodOverride(MethodInfo methodInfoBody, MethodInfo methodInfoDeclaration) + { + } + + public TypeBuilder DefineNestedType(string name) + { + return default; + } + + public TypeBuilder DefineNestedType(string name, TypeAttributes attr) + { + return default; + } + + public TypeBuilder DefineNestedType(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type parent) + { + return default; + } + + public TypeBuilder DefineNestedType(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type parent, int typeSize) + { + return default; + } + + public TypeBuilder DefineNestedType(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type parent, PackingSize packSize) + { + return default; + } + + public TypeBuilder DefineNestedType(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type parent, PackingSize packSize, int typeSize) + { + return default; + } + + public TypeBuilder DefineNestedType(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type parent, Type[] interfaces) + { + return default; + } + + [RequiresUnreferencedCode("P/Invoke marshalling may dynamically access members that could be trimmed.")] + public MethodBuilder DefinePInvokeMethod(string name, string dllName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + return default; + } + + [RequiresUnreferencedCode("P/Invoke marshalling may dynamically access members that could be trimmed.")] + public MethodBuilder DefinePInvokeMethod(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + return default; + } + + [RequiresUnreferencedCode("P/Invoke marshalling may dynamically access members that could be trimmed.")] + public MethodBuilder DefinePInvokeMethod(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers, Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + return default; + } + + public PropertyBuilder DefineProperty(string name, PropertyAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes) + { + return default; + } + + public PropertyBuilder DefineProperty(string name, PropertyAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers, Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers) + { + return default; + } + + public PropertyBuilder DefineProperty(string name, PropertyAttributes attributes, Type returnType, Type[] parameterTypes) + { + return default; + } + + public PropertyBuilder DefineProperty(string name, PropertyAttributes attributes, Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers, Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers) + { + return default; + } + + public ConstructorBuilder DefineTypeInitializer() + { + return default; + } + + public FieldBuilder DefineUninitializedData(string name, int size, FieldAttributes attributes) + { + return default; + } + + protected override TypeAttributes GetAttributeFlagsImpl() + { + return default; + } + + public static ConstructorInfo GetConstructor(Type type, ConstructorInfo constructor) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) + { + return default; + } + + public override object[] GetCustomAttributes(bool inherit) + { + return default; + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return default; + } + + public override Type GetElementType() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo GetEvent(string name, BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents)] + public override EventInfo[] GetEvents() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo[] GetEvents(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo GetField(string name, BindingFlags bindingAttr) + { + return default; + } + + public static FieldInfo GetField(Type type, FieldInfo field) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo[] GetFields(BindingFlags bindingAttr) + { + return default; + } + + public override Type[] GetGenericArguments() + { + return default; + } + + public override Type GetGenericTypeDefinition() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public override Type GetInterface(string name, bool ignoreCase) + { + return default; + } + + public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public override Type[] GetInterfaces() + { + return default; + } + + [DynamicallyAccessedMembers(GetAllMembers)] + public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(GetAllMembers)] + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) + { + return default; + } + + public static MethodInfo GetMethod(Type type, MethodInfo method) + { + ReflectionEmitThrower.ThrowPlatformNotSupportedException(); + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type GetNestedType(string name, BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type[] GetNestedTypes(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) + { + return default; + } + + protected override bool HasElementTypeImpl() + { + return default; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, Globalization.CultureInfo culture, string[] namedParameters) + { + return default; + } + + protected override bool IsArrayImpl() + { + return default; + } + + public override bool IsAssignableFrom([NotNullWhen(true)] TypeInfo? typeInfo) + { + return default; + } + + public override bool IsAssignableFrom([NotNullWhen(true)] Type? c) + { + return default; + } + + protected override bool IsByRefImpl() + { + return default; + } + + protected override bool IsCOMObjectImpl() + { + return default; + } + + public bool IsCreated() + { + return default; + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return default; + } + + protected override bool IsPointerImpl() + { + return default; + } + + protected override bool IsPrimitiveImpl() + { + return default; + } + + public override bool IsSubclassOf(Type c) + { + return default; + } + + public override bool IsTypeDefinition + { + get + { + return default; + } + } + + public override bool IsSZArray + { + get + { + return default; + } + } + + public override bool IsVariableBoundArray + { + get + { + return default; + } + } + + public override Type MakeArrayType() + { + return default; + } + + public override Type MakeArrayType(int rank) + { + return default; + } + + public override Type MakeByRefType() + { + return default; + } + + [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + public override Type MakeGenericType(params Type[] typeArguments) + { + return default; + } + + public override Type MakePointerType() + { + return default; + } + + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) + { + } + + public void SetCustomAttribute(CustomAttributeBuilder customBuilder) + { + } + + public void SetParent([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type parent) + { + } + + public override string ToString() + { + return default; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/EnumInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/EnumInfo.cs new file mode 100644 index 00000000000000..761a38833bdaf0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/EnumInfo.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System.Reflection +{ + [ReflectionBlocked] + public sealed class EnumInfo + { + public EnumInfo(Type underlyingType, object[] rawValues, string[] names, bool isFlags) + { + Debug.Assert(rawValues.Length == names.Length); + + UnderlyingType = underlyingType; + + int numValues = rawValues.Length; + ulong[] values = new ulong[numValues]; + for (int i = 0; i < numValues; i++) + { + object rawValue = rawValues[i]; + + ulong rawUnboxedValue; + if (rawValue is ulong) + { + rawUnboxedValue = (ulong)rawValue; + } + else + { + // This conversion is this way for compatibility: do a value-preseving cast to long - then store (and compare) as ulong. This affects + // the order in which the Enum apis return names and values. + rawUnboxedValue = (ulong)(((IConvertible)rawValue).ToInt64(null)); + } + values[i] = rawUnboxedValue; + } + + // Need to sort the `names` and `rawValues` arrays according to the `values` array + ulong[] valuesCopy = (ulong[])values.Clone(); + Array.Sort(keys: valuesCopy, items: rawValues, comparer: Comparer.Default); + Array.Sort(keys: values, items: names, comparer: Comparer.Default); + + Names = names; + Values = values; + + // Create the unboxed version of values for the Values property to return. (We didn't do this earlier because + // declaring "rawValues" as "Array" would prevent us from using the generic overload of Array.Sort()). + // + // The array element type is the underlying type, not the enum type. (The enum type could be an open generic.) + ValuesAsUnderlyingType = Type.GetTypeCode(UnderlyingType) switch + { + TypeCode.Byte => new byte[numValues], + TypeCode.SByte => new sbyte[numValues], + TypeCode.UInt16 => new ushort[numValues], + TypeCode.Int16 => new short[numValues], + TypeCode.UInt32 => new uint[numValues], + TypeCode.Int32 => new int[numValues], + TypeCode.UInt64 => new ulong[numValues], + TypeCode.Int64 => new long[numValues], + _ => throw new NotSupportedException(), + }; + Array.Copy(rawValues, ValuesAsUnderlyingType, numValues); + + HasFlagsAttribute = isFlags; + } + + internal Type UnderlyingType { get; } + internal string[] Names { get; } + internal ulong[] Values { get; } + internal Array ValuesAsUnderlyingType { get; } + internal bool HasFlagsAttribute { get; } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/FieldInfo.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/FieldInfo.CoreRT.cs new file mode 100644 index 00000000000000..4eacd387bbb7cf --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/FieldInfo.CoreRT.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Reflection.Augments; + +namespace System.Reflection +{ + public abstract partial class FieldInfo : MemberInfo + { + public static FieldInfo GetFieldFromHandle(RuntimeFieldHandle handle) => ReflectionAugments.ReflectionCoreCallbacks.GetFieldFromHandle(handle); + public static FieldInfo GetFieldFromHandle(RuntimeFieldHandle handle, RuntimeTypeHandle declaringType) => ReflectionAugments.ReflectionCoreCallbacks.GetFieldFromHandle(handle, declaringType); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs new file mode 100644 index 00000000000000..bca2a005239c0a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Reflection.Metadata +{ + public static class AssemblyExtensions + { + // Retrieves the metadata section of the assembly, for use with System.Reflection.Metadata.MetadataReader. + // - Returns false upon failure. Metadata might not be available for some assemblies, such as AssemblyBuilder, .NET + // native images, etc. + // - Callers should not write to the metadata blob + // - The metadata blob pointer will remain valid as long as the AssemblyLoadContext with which the assembly is + // associated, is alive. The caller is responsible for keeping the assembly object alive while accessing the + // metadata blob. + [CLSCompliant(false)] // out byte* blob + public static unsafe bool TryGetRawMetadata(this Assembly assembly, out byte* blob, out int length) + { + if (assembly == null) + { + throw new ArgumentNullException(nameof(assembly)); + } + + blob = null; + length = 0; + return false; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs new file mode 100644 index 00000000000000..dac94886d752c5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Metadata +{ + public static class MetadataUpdater + { + public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDelta, ReadOnlySpan ilDelta, ReadOnlySpan pdbDelta) + { + throw new PlatformNotSupportedException(); + } + + public static bool IsSupported => false; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreRT.cs new file mode 100644 index 00000000000000..ecc6242f7e7905 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreRT.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using Internal.Reflection.Augments; + +namespace System.Reflection +{ + public abstract partial class MethodBase : MemberInfo + { + public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle) => ReflectionAugments.ReflectionCoreCallbacks.GetMethodFromHandle(handle); + public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) => ReflectionAugments.ReflectionCoreCallbacks.GetMethodFromHandle(handle, declaringType); + + [RequiresUnreferencedCode("Metadata for the method might be incomplete or removed")] + [System.Runtime.CompilerServices.Intrinsic] + public static MethodBase GetCurrentMethod() { throw NotImplemented.ByDesign; } //Implemented by toolchain. + + // This is not an api but needs to be declared public so that System.Private.Reflection.Core can access (and override it) + public virtual ParameterInfo[] GetParametersNoCopy() => GetParameters(); + + // + // MethodBase MetadataDefinitionMethod { get; } + // + // Returns the canonical MethodBase that this is an instantiation or reflected mirror of. + // If MethodBase is already a canonical MethodBase, returns "this". + // + // Guarantees on returned MethodBase: + // + // IsConstructedGenericMethod == false. + // DeclaringType.IsConstructedGenericType == false. + // ReflectedType == DeclaringType + // + // Throws NotSupportedException if the MethodBase is not a metadata-represented method + // (for example, the methods returned on Array types.) + // + // This is not an api but needs to be declared public so that System.Private.Reflection.Core can access (and override it) + public virtual MethodBase MetadataDefinitionMethod { get { throw NotImplemented.ByDesign; } } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/MissingMetadataException.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/MissingMetadataException.cs new file mode 100644 index 00000000000000..51c5acd7d3905d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/MissingMetadataException.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace System.Reflection +{ + public sealed class MissingMetadataException : TypeAccessException + { + public MissingMetadataException() + { + } + + public MissingMetadataException(string message) + : base(message) + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs new file mode 100644 index 00000000000000..24a10f91faa7b4 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection +{ + // Base class for runtime implemented Assembly + public abstract class RuntimeAssembly : Assembly + { + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/RuntimeAssemblyName.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/RuntimeAssemblyName.cs new file mode 100644 index 00000000000000..90c0fe5ff824a8 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/RuntimeAssemblyName.cs @@ -0,0 +1,170 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Collections.Generic; + +namespace System.Reflection +{ + // + // This is a private assembly name abstraction that's more suitable for use as keys in our caches. + // + // - Immutable, unlike the public AssemblyName + // - Has a useful Equals() override, unlike the public AssemblyName. + // + // We use this as our internal interchange type and only convert to and from the public AssemblyName class at public boundaries. + // + public sealed class RuntimeAssemblyName : IEquatable + { + public RuntimeAssemblyName(string name, Version version, string cultureName, AssemblyNameFlags flags, byte[] publicKeyOrToken) + { + Debug.Assert(name != null); + this.Name = name; + + // Optional version. + this.Version = version; + + // Optional culture name. + this.CultureName = cultureName; + + // Optional flags (this is actually an OR of the classic flags and the ContentType.) + this.Flags = flags; + + // Optional public key (if Flags.PublicKey == true) or public key token. + this.PublicKeyOrToken = publicKeyOrToken; + } + + // Simple name. + public string Name { get; } + + // Optional version. + public Version Version { get; } + + // Optional culture name. + public string CultureName { get; } + + // Optional flags (this is actually an OR of the classic flags and the ContentType.) + public AssemblyNameFlags Flags { get; } + + // Optional public key (if Flags.PublicKey == true) or public key token. + public byte[] PublicKeyOrToken { get; } + + // Equality - this compares every bit of data in the RuntimeAssemblyName which is acceptable for use as keys in a cache + // where semantic duplication is permissible. This method is *not* meant to define ref->def binding rules or + // assembly binding unification rules. + public bool Equals(RuntimeAssemblyName? other) + { + if (other == null) + return false; + if (!this.Name.Equals(other.Name)) + return false; + if (this.Version == null) + { + if (other.Version != null) + return false; + } + else + { + if (!this.Version.Equals(other.Version)) + return false; + } + if (!string.Equals(this.CultureName, other.CultureName)) + return false; + if (this.Flags != other.Flags) + return false; + + byte[] thisPK = this.PublicKeyOrToken; + byte[] otherPK = other.PublicKeyOrToken; + if (thisPK == null) + { + if (otherPK != null) + return false; + } + else if (otherPK == null) + { + return false; + } + else if (thisPK.Length != otherPK.Length) + { + return false; + } + else + { + for (int i = 0; i < thisPK.Length; i++) + { + if (thisPK[i] != otherPK[i]) + return false; + } + } + + return true; + } + + public sealed override bool Equals(object? obj) + { + RuntimeAssemblyName? other = obj as RuntimeAssemblyName; + if (other == null) + return false; + return Equals(other); + } + + public sealed override int GetHashCode() + { + return this.Name.GetHashCode(); + } + + // + // Converts an RuntimeAssemblyName to a freshly allocated AssemblyName with no data aliasing to any other object. + // + public AssemblyName ToAssemblyName() + { + AssemblyName assemblyName = new AssemblyName(); + CopyToAssemblyName(assemblyName); + return assemblyName; + } + + // + // Copies a RuntimeAssemblyName into a freshly allocated AssemblyName with no data aliasing to any other object. + // + public void CopyToAssemblyName(AssemblyName blank) + { + blank.Name = this.Name; + if (this.Version != null) + blank.Version = this.Version; + if (this.CultureName != null) + blank.CultureName = this.CultureName; + + // Our "Flags" contain both the classic flags and the ProcessorArchitecture + ContentType bits. The public AssemblyName has separate properties for + // these. The setters for these properties quietly mask out any bits intended for the other one, so we needn't do that ourselves.. + blank.Flags = this.Flags.ExtractAssemblyNameFlags(); + blank.ContentType = this.Flags.ExtractAssemblyContentType(); +#pragma warning disable SYSLIB0037 // AssemblyName.ProcessorArchitecture is obsolete + blank.ProcessorArchitecture = this.Flags.ExtractProcessorArchitecture(); +#pragma warning restore SYSLIB0037 + + if (this.PublicKeyOrToken != null) + { + // We must not hand out our own copy of the PKT to AssemblyName as AssemblyName is amazingly trusting and gives untrusted callers + // full freedom to scribble on its PKT array. (As do we but we only have trusted callers!) + byte[] pkCopy = new byte[this.PublicKeyOrToken.Length]; + ((ICollection)(this.PublicKeyOrToken)).CopyTo(pkCopy, 0); + + if (0 != (this.Flags & AssemblyNameFlags.PublicKey)) + blank.SetPublicKey(pkCopy); + else + blank.SetPublicKeyToken(pkCopy); + } + + return; + } + + public string FullName + { + get + { + byte[] pkt = (0 != (Flags & AssemblyNameFlags.PublicKey)) ? AssemblyNameHelpers.ComputePublicKeyToken(PublicKeyOrToken) : PublicKeyOrToken; + return AssemblyNameFormatter.ComputeDisplayName(Name, Version, CultureName, pkt, Flags.ExtractAssemblyNameFlags(), Flags.ExtractAssemblyContentType()); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.CoreRT.cs new file mode 100644 index 00000000000000..4439ead68b7ea9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.CoreRT.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Text; +using Internal.Reflection.Augments; + +namespace System.Resources +{ + internal partial class ManifestBasedResourceGroveler + { + // Internal version of GetSatelliteAssembly that avoids throwing FileNotFoundException + private static Assembly? InternalGetSatelliteAssembly(Assembly mainAssembly, + CultureInfo culture, + Version? version) + { + AssemblyName mainAssemblyAn = mainAssembly.GetName(); + AssemblyName an = new AssemblyName(); + + an.CultureInfo = culture; + an.Name = mainAssemblyAn.Name + ".resources"; + an.SetPublicKeyToken(mainAssemblyAn.GetPublicKeyToken()); + an.Flags = mainAssemblyAn.Flags; + an.Version = version ?? mainAssemblyAn.Version; + + Assembly? retAssembly = ReflectionAugments.ReflectionCoreCallbacks.Load(an, false); + + if (retAssembly == mainAssembly) + { + retAssembly = null; + } + + return retAssembly; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs new file mode 100644 index 00000000000000..a3847253fa3f07 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs @@ -0,0 +1,558 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; +using System.Diagnostics; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +using Internal.Runtime; +using Internal.Runtime.CompilerHelpers; + +namespace System.Runtime.CompilerServices +{ + internal static partial class ClassConstructorRunner + { + //============================================================================================================== + // Ensures the class constructor for the given type has run. + // + // Called by the runtime when it finds a class whose static class constructor has probably not run + // (probably because it checks in the initialized flag without thread synchronization). + // + // The context structure passed by reference lives in the image of one of the application's modules. + // The contents are thus fixed (do not require pinning) and the address can be used as a unique + // identifier for the context. + // + // This guarantee is violated in one specific case: where a class constructor cycle would cause a deadlock. If + // so, per ECMA specs, this method returns without guaranteeing that the .cctor has run. + // + // No attempt is made to detect or break deadlocks due to other synchronization mechanisms. + //============================================================================================================== + + private static unsafe object CheckStaticClassConstructionReturnGCStaticBase(StaticClassConstructionContext* context, object gcStaticBase) + { + EnsureClassConstructorRun(context); + return gcStaticBase; + } + + private static unsafe IntPtr CheckStaticClassConstructionReturnNonGCStaticBase(StaticClassConstructionContext* context, IntPtr nonGcStaticBase) + { + EnsureClassConstructorRun(context); + return nonGcStaticBase; + } + + private static unsafe object CheckStaticClassConstructionReturnThreadStaticBase(TypeManagerSlot* pModuleData, int typeTlsIndex, StaticClassConstructionContext* context) + { + object threadStaticBase = ThreadStatics.GetThreadStaticBaseForType(pModuleData, typeTlsIndex); + EnsureClassConstructorRun(context); + return threadStaticBase; + } + + public static unsafe void EnsureClassConstructorRun(StaticClassConstructionContext* pContext) + { + IntPtr pfnCctor = pContext->cctorMethodAddress; + NoisyLog("EnsureClassConstructorRun, cctor={0}, thread={1}", pfnCctor, CurrentManagedThreadId); + + // If we were called from MRT, this check is redundant but harmless. This is in case someone within classlib + // (cough, Reflection) needs to call this explicitly. + if (pContext->initialized == 1) + { + NoisyLog("Cctor already run, cctor={0}, thread={1}", pfnCctor, CurrentManagedThreadId); + return; + } + + CctorHandle cctor = Cctor.GetCctor(pContext); + Cctor[] cctors = cctor.Array; + int cctorIndex = cctor.Index; + try + { + Lock cctorLock = cctors[cctorIndex].Lock; + if (DeadlockAwareAcquire(cctor, pfnCctor)) + { + int currentManagedThreadId = CurrentManagedThreadId; + try + { + NoisyLog("Acquired cctor lock, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId); + + cctors[cctorIndex].HoldingThread = currentManagedThreadId; + if (pContext->initialized == 0) // Check again in case some thread raced us while we were acquiring the lock. + { + TypeInitializationException priorException = cctors[cctorIndex].Exception; + if (priorException != null) + throw priorException; + try + { + NoisyLog("Calling cctor, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId); + + ((delegate*)pfnCctor)(); + + // Insert a memory barrier here to order any writes executed as part of static class + // construction above with respect to the initialized flag update we're about to make + // below. This is important since the fast path for checking the cctor uses a normal read + // and doesn't come here so without the barrier it could observe initialized == 1 but + // still see uninitialized static fields on the class. + Interlocked.MemoryBarrier(); + + NoisyLog("Set type inited, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId); + + pContext->initialized = 1; + } + catch (Exception e) + { + TypeInitializationException wrappedException = new TypeInitializationException(null, SR.TypeInitialization_Type_NoTypeAvailable, e); + cctors[cctorIndex].Exception = wrappedException; + throw wrappedException; + } + } + } + finally + { + cctors[cctorIndex].HoldingThread = ManagedThreadIdNone; + NoisyLog("Releasing cctor lock, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId); + + cctorLock.Release(); + } + } + else + { + // Cctor cycle resulted in a deadlock. We will break the guarantee and return without running the + // .cctor. + } + } + finally + { + Cctor.Release(cctor); + } + NoisyLog("EnsureClassConstructorRun complete, cctor={0}, thread={1}", pfnCctor, CurrentManagedThreadId); + } + + //========================================================================================================= + // Return value: + // true - lock acquired. + // false - deadlock detected. Lock not acquired. + //========================================================================================================= + private static bool DeadlockAwareAcquire(CctorHandle cctor, IntPtr pfnCctor) + { + const int WaitIntervalSeedInMS = 1; // seed with 1ms and double every time through the loop + const int WaitIntervalLimitInMS = WaitIntervalSeedInMS << 7; // limit of 128ms + + int waitIntervalInMS = WaitIntervalSeedInMS; + + int cctorIndex = cctor.Index; + Cctor[] cctors = cctor.Array; + Lock lck = cctors[cctorIndex].Lock; + if (lck.IsAcquired) + return false; // Thread recursively triggered the same cctor. + + if (lck.TryAcquire(waitIntervalInMS)) + return true; + + // We couldn't acquire the lock. See if this .cctor is involved in a cross-thread deadlock. If so, break + // the deadlock by breaking the guarantee - we'll skip running the .cctor and let the caller take his chances. + int currentManagedThreadId = CurrentManagedThreadId; + int unmarkCookie = -1; + try + { + // We'll spin in a forever-loop of checking for a deadlock state, then waiting a short time, then + // checking for a deadlock state again, and so on. This is because the BlockedRecord info has a built-in + // lag time - threads don't report themselves as blocking until they've been blocked for a non-trivial + // amount of time. + // + // If the threads are deadlocked for any reason other a class constructor cycling, this loop will never + // terminate - this is by design. If the user code inside the class constructors were to + // deadlock themselves, then that's a bug in user code. + for (;;) + { + using (LockHolder.Hold(s_cctorGlobalLock)) + { + // Ask the guy who holds the cctor lock we're trying to acquire who he's waiting for. Keep + // walking down that chain until we either discover a cycle or reach a non-blocking state. Note + // that reaching a non-blocking state is not proof that we've avoided a deadlock due to the + // BlockingRecord reporting lag. + CctorHandle cctorWalk = cctor; + int chainStepCount = 0; + for (; chainStepCount < Cctor.Count; chainStepCount++) + { + int cctorWalkIndex = cctorWalk.Index; + Cctor[] cctorWalkArray = cctorWalk.Array; + + int holdingThread = cctorWalkArray[cctorWalkIndex].HoldingThread; + if (holdingThread == currentManagedThreadId) + { + // Deadlock detected. We will break the guarantee and return without running the .cctor. + DebugLog("A class constructor was skipped due to class constructor cycle. cctor={0}, thread={1}", + pfnCctor, currentManagedThreadId); + + // We are maintaining an invariant that the BlockingRecords never show a cycle because, + // before we add a record, we first check for a cycle. As a result, once we've said + // we're waiting, we are committed to waiting and will not need to skip running this + // .cctor. + Debug.Assert(unmarkCookie == -1); + return false; + } + + if (holdingThread == ManagedThreadIdNone) + { + // No one appears to be holding this cctor lock. Give the current thread some more time + // to acquire the lock. + break; + } + + cctorWalk = BlockingRecord.GetCctorThatThreadIsBlockedOn(holdingThread); + if (cctorWalk.Array == null) + { + // The final thread in the chain appears to be blocked on nothing. Give the current + // thread some more time to acquire the lock. + break; + } + } + + // We don't allow cycles in the BlockingRecords, so we must always enumerate at most each entry, + // but never more. + Debug.Assert(chainStepCount < Cctor.Count); + + // We have not discovered a deadlock, so let's register the fact that we're waiting on another + // thread and continue to wait. It is important that we only signal that we are blocked after + // we check for a deadlock because, otherwise, we give all threads involved in the deadlock the + // opportunity to break it themselves and that leads to "ping-ponging" between the cctors + // involved in the cycle, allowing intermediate cctor results to be observed. + // + // The invariant here is that we never 'publish' a BlockingRecord that forms a cycle. So it is + // important that the look-for-cycle-and-then-publish-wait-status operation be atomic with + // respect to other updates to the BlockingRecords. + if (unmarkCookie == -1) + { + NoisyLog("Mark thread blocked, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId); + + unmarkCookie = BlockingRecord.MarkThreadAsBlocked(currentManagedThreadId, cctor); + } + } // _cctorGlobalLock scope + + if (waitIntervalInMS < WaitIntervalLimitInMS) + waitIntervalInMS *= 2; + + // We didn't find a cycle yet, try to take the lock again. + if (lck.TryAcquire(waitIntervalInMS)) + return true; + } // infinite loop + } + finally + { + if (unmarkCookie != -1) + { + NoisyLog("Unmark thread blocked, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId); + BlockingRecord.UnmarkThreadAsBlocked(unmarkCookie); + } + } + } + + //============================================================================================================== + // These structs are allocated on demand whenever the runtime tries to run a class constructor. Once the + // the class constructor has been successfully initialized, we reclaim this structure. The structure is long- + // lived only if the class constructor threw an exception. + //============================================================================================================== + private unsafe struct Cctor + { + public Lock Lock; + public TypeInitializationException Exception; + public int HoldingThread; + private int _refCount; + private StaticClassConstructionContext* _pContext; + + //========================================================================================================== + // Gets the Cctor entry associated with a specific class constructor context (creating it if necessary.) + //========================================================================================================== + public static CctorHandle GetCctor(StaticClassConstructionContext* pContext) + { +#if DEBUG + const int Grow = 2; +#else + const int Grow = 10; +#endif + + // WASMTODO: Remove this when the Initialize method gets called by the runtime startup +#if TARGET_WASM + if (s_cctorGlobalLock == null) + { + Interlocked.CompareExchange(ref s_cctorGlobalLock, new Lock(), null); + } + if (s_cctorArrays == null) + { + Interlocked.CompareExchange(ref s_cctorArrays, new Cctor[10][], null); + } +#endif // TARGET_WASM + + using (LockHolder.Hold(s_cctorGlobalLock)) + { + Cctor[]? resultArray = null; + int resultIndex = -1; + + if (s_count != 0) + { + // Search for the cctor context in our existing arrays + for (int cctorIndex = 0; cctorIndex < s_cctorArraysCount; ++cctorIndex) + { + Cctor[] segment = s_cctorArrays[cctorIndex]; + for (int i = 0; i < segment.Length; i++) + { + if (segment[i]._pContext == pContext) + { + resultArray = segment; + resultIndex = i; + break; + } + } + if (resultArray != null) + break; + } + } + if (resultArray == null) + { + // look for an empty entry in an existing array + for (int cctorIndex = 0; cctorIndex < s_cctorArraysCount; ++cctorIndex) + { + Cctor[] segment = s_cctorArrays[cctorIndex]; + for (int i = 0; i < segment.Length; i++) + { + if (segment[i]._pContext == default(StaticClassConstructionContext*)) + { + resultArray = segment; + resultIndex = i; + break; + } + } + if (resultArray != null) + break; + } + if (resultArray == null) + { + // allocate a new array + resultArray = new Cctor[Grow]; + if (s_cctorArraysCount == s_cctorArrays.Length) + { + // grow the container + Array.Resize(ref s_cctorArrays, (s_cctorArrays.Length * 2) + 1); + } + // store the array in the container, this cctor gets index 0 + s_cctorArrays[s_cctorArraysCount] = resultArray; + s_cctorArraysCount++; + resultIndex = 0; + } + + Debug.Assert(resultArray[resultIndex]._pContext == default(StaticClassConstructionContext*)); + resultArray[resultIndex]._pContext = pContext; + resultArray[resultIndex].Lock = new Lock(); + s_count++; + } + + Interlocked.Increment(ref resultArray[resultIndex]._refCount); + return new CctorHandle(resultArray, resultIndex); + } + } + + public static int Count + { + get + { + Debug.Assert(s_cctorGlobalLock.IsAcquired); + return s_count; + } + } + + public static void Release(CctorHandle cctor) + { + using (LockHolder.Hold(s_cctorGlobalLock)) + { + Cctor[] cctors = cctor.Array; + int cctorIndex = cctor.Index; + if (0 == Interlocked.Decrement(ref cctors[cctorIndex]._refCount)) + { + if (cctors[cctorIndex].Exception == null) + { + cctors[cctorIndex] = default; + s_count--; + } + } + } + } + } + + private struct CctorHandle + { + public CctorHandle(Cctor[] array, int index) + { + _array = array; + _index = index; + } + + public Cctor[] Array { get { return _array; } } + public int Index { get { return _index; } } + + private Cctor[] _array; + private int _index; + } + + //============================================================================================================== + // Keeps track of threads that are blocked on a cctor lock (alas, we don't have ThreadLocals here in + // System.Private.CoreLib so we have to use a side table.) + // + // This is used for cross-thread deadlock detection. + // + // - Data is only entered here if a thread has been blocked past a certain timeout (otherwise, it's certainly + // not participating of a deadlock.) + // - Reads and writes to _blockingRecord are guarded by _cctorGlobalLock. + // - BlockingRecords for individual threads are created on demand. Since this is a rare event, we won't attempt + // to recycle them directly (however, + // ManagedThreadId's are themselves recycled pretty quickly - and threads that inherit the managed id also + // inherit the BlockingRecord.) + //============================================================================================================== + private struct BlockingRecord + { + public int ManagedThreadId; // ManagedThreadId of the blocked thread + public CctorHandle BlockedOn; + + public static int MarkThreadAsBlocked(int managedThreadId, CctorHandle blockedOn) + { +#if DEBUG + const int Grow = 2; +#else + const int Grow = 10; +#endif + using (LockHolder.Hold(s_cctorGlobalLock)) + { + if (s_blockingRecords == null) + s_blockingRecords = new BlockingRecord[Grow]; + int found; + for (found = 0; found < s_nextBlockingRecordIndex; found++) + { + if (s_blockingRecords[found].ManagedThreadId == managedThreadId) + break; + } + if (found == s_nextBlockingRecordIndex) + { + if (s_nextBlockingRecordIndex == s_blockingRecords.Length) + { + BlockingRecord[] newBlockingRecords = new BlockingRecord[s_blockingRecords.Length + Grow]; + for (int i = 0; i < s_blockingRecords.Length; i++) + { + newBlockingRecords[i] = s_blockingRecords[i]; + } + s_blockingRecords = newBlockingRecords; + } + s_blockingRecords[s_nextBlockingRecordIndex].ManagedThreadId = managedThreadId; + s_nextBlockingRecordIndex++; + } + s_blockingRecords[found].BlockedOn = blockedOn; + return found; + } + } + + public static void UnmarkThreadAsBlocked(int blockRecordIndex) + { + // This method must never throw + s_cctorGlobalLock.Acquire(); + s_blockingRecords[blockRecordIndex].BlockedOn = new CctorHandle(null, 0); + s_cctorGlobalLock.Release(); + } + + public static CctorHandle GetCctorThatThreadIsBlockedOn(int managedThreadId) + { + Debug.Assert(s_cctorGlobalLock.IsAcquired); + for (int i = 0; i < s_nextBlockingRecordIndex; i++) + { + if (s_blockingRecords[i].ManagedThreadId == managedThreadId) + return s_blockingRecords[i].BlockedOn; + } + return new CctorHandle(null, 0); + } + + private static BlockingRecord[] s_blockingRecords; + private static int s_nextBlockingRecordIndex; + } + + private static int CurrentManagedThreadId => ManagedThreadId.Current; + private const int ManagedThreadIdNone = ManagedThreadId.IdNone; + + private static Lock s_cctorGlobalLock; + + // These three statics are used by ClassConstructorRunner.Cctor but moved out to avoid an unnecessary + // extra class constructor call. + // + // Because Cctor's are mutable structs, we have to give our callers raw references to the underlying arrays + // for this collection to be usable. This also means once we place a Cctor in an array, we can't grow or + // reallocate the array. + private static Cctor[][] s_cctorArrays; + private static int s_cctorArraysCount; + private static int s_count; + + // Eager construction called from LibraryInitialize Cctor.GetCctor uses _cctorGlobalLock. + internal static void Initialize() + { + s_cctorArrays = new Cctor[10][]; + s_cctorGlobalLock = new Lock(); + } + + [Conditional("ENABLE_NOISY_CCTOR_LOG")] + private static void NoisyLog(string format, IntPtr cctorMethod, int threadId) + { + // We cannot utilize any of the typical number formatting code because it triggers globalization code to run + // and this cctor code is layered below globalization. +#if DEBUG + Debug.WriteLine(format, ToHexString(cctorMethod), ToHexString(threadId)); +#endif // DEBUG + } + + [Conditional("DEBUG")] + private static void DebugLog(string format, IntPtr cctorMethod, int threadId) + { + // We cannot utilize any of the typical number formatting code because it triggers globalization code to run + // and this cctor code is layered below globalization. +#if DEBUG + Debug.WriteLine(format, ToHexString(cctorMethod), ToHexString(threadId)); +#endif + } + + // We cannot utilize any of the typical number formatting code because it triggers globalization code to run + // and this cctor code is layered below globalization. +#if DEBUG + private static string ToHexString(int num) + { + return ToHexStringUnsignedLong((ulong)num, false, 8); + } + private static string ToHexString(IntPtr num) + { + return ToHexStringUnsignedLong((ulong)num, false, 16); + } + private static char GetHexChar(uint u) + { + if (u < 10) + return unchecked((char)('0' + u)); + + return unchecked((char)('a' + (u - 10))); + } + public static unsafe string ToHexStringUnsignedLong(ulong u, bool zeroPrepad, int numChars) + { + char[] chars = new char[numChars]; + + int i = numChars - 1; + + for (; i >= 0; i--) + { + chars[i] = GetHexChar((uint)(u % 16)); + u = u / 16; + + if ((i == 0) || (!zeroPrepad && (u == 0))) + break; + } + + string str; + fixed (char* p = &chars[i]) + { + str = new string(p, 0, numChars - i); + } + return str; + } +#endif + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/EagerStaticClassConstructionAttribute.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/EagerStaticClassConstructionAttribute.cs new file mode 100644 index 00000000000000..a0d83b3ce28d8f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/EagerStaticClassConstructionAttribute.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + // When applied to a type this custom attribute will cause any static class constructor to be run eagerly + // at module load time rather than deferred till just before the class is used. + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] + public class EagerStaticClassConstructionAttribute : Attribute + { + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/ForceDictionaryLookupsAttribute.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/ForceDictionaryLookupsAttribute.cs new file mode 100644 index 00000000000000..104bd032bd6592 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/ForceDictionaryLookupsAttribute.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + // When applied to a type this custom attribute will force use of statically precompiled dictionary looks that + // do not depend on lazy resolution by the template type loader + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] + public class ForceDictionaryLookupsAttribute : Attribute + { + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/ForceLazyDictionaryAttribute.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/ForceLazyDictionaryAttribute.cs new file mode 100644 index 00000000000000..70a0c8dcea5c0f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/ForceLazyDictionaryAttribute.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + // + // When applied to a generic type or a method, this custom attribute forces use of lazy dictionaries. This allows static compilation + // to succeed in the presence of constructs that trigger infinite generic expansion. + // + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + public sealed class ForceLazyDictionaryAttribute : Attribute + { + public ForceLazyDictionaryAttribute() { } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/ReflectionBlockedAttribute.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/ReflectionBlockedAttribute.cs new file mode 100644 index 00000000000000..3b6c28e21f540d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/ReflectionBlockedAttribute.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + // When applied to a type this custom attribute cause the type to be treated as reflection blocked. + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] + public class ReflectionBlockedAttribute : Attribute + { + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.CoreRT.cs new file mode 100644 index 00000000000000..2babd66c359732 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.CoreRT.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + public static partial class RuntimeFeature + { + public static bool IsDynamicCodeSupported => false; + public static bool IsDynamicCodeCompiled => false; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreRT.cs new file mode 100644 index 00000000000000..68b0ec7d5890bf --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreRT.cs @@ -0,0 +1,352 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Reflection.Augments; +using Internal.Reflection.Core.NonPortable; +using Internal.Runtime.Augments; +using System.Diagnostics.CodeAnalysis; +using System.Runtime; +using System.Runtime.Serialization; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +using Debug = System.Diagnostics.Debug; + +namespace System.Runtime.CompilerServices +{ + public static partial class RuntimeHelpers + { + [Intrinsic] + public static void InitializeArray(Array array, RuntimeFieldHandle fldHandle) + { + // We only support this intrinsic when it occurs within a well-defined IL sequence. + // If a call to this method occurs within the recognized sequence, codegen must expand the IL sequence completely. + // For any other purpose, the API is currently unsupported. + // https://github.com/dotnet/corert/issues/364 + throw new PlatformNotSupportedException(); + } + + private static unsafe void* GetSpanDataFrom( + RuntimeFieldHandle fldHandle, + RuntimeTypeHandle targetTypeHandle, + out int count) + { + // We only support this intrinsic when it occurs within a well-defined IL sequence. + // If a call to this method occurs within the recognized sequence, codegen must expand the IL sequence completely. + // For any other purpose, the API is currently unsupported. + // https://github.com/dotnet/corert/issues/364 + throw new PlatformNotSupportedException(); + } + + [RequiresUnreferencedCode("Trimmer can't guarantee existence of class constructor")] + public static void RunClassConstructor(RuntimeTypeHandle type) + { + if (type.IsNull) + throw new ArgumentException(SR.InvalidOperation_HandleIsNotInitialized); + + IntPtr pStaticClassConstructionContext = RuntimeAugments.Callbacks.TryGetStaticClassConstructionContext(type); + if (pStaticClassConstructionContext == IntPtr.Zero) + return; + + unsafe + { + ClassConstructorRunner.EnsureClassConstructorRun((StaticClassConstructionContext*)pStaticClassConstructionContext); + } + } + + public static void RunModuleConstructor(ModuleHandle module) + { + if (module.AssociatedModule == null) + throw new ArgumentException(SR.InvalidOperation_HandleIsNotInitialized); + + ReflectionAugments.ReflectionCoreCallbacks.RunModuleConstructor(module.AssociatedModule); + } + + public static object GetObjectValue(object? obj) + { + if (obj == null) + return null; + + EETypePtr eeType = obj.EETypePtr; + if ((!eeType.IsValueType) || eeType.IsPrimitive) + return obj; + + return RuntimeImports.RhMemberwiseClone(obj); + } + + public static new bool Equals(object? o1, object? o2) + { + if (o1 == o2) + return true; + + if ((o1 == null) || (o2 == null)) + return false; + + // If it's not a value class, don't compare by value + if (!o1.EETypePtr.IsValueType) + return false; + + // Make sure they are the same type. + if (o1.EETypePtr != o2.EETypePtr) + return false; + + return RuntimeImports.RhCompareObjectContentsAndPadding(o1, o2); + } + + [ThreadStatic] + private static int t_hashSeed; + + internal static int GetNewHashCode() + { + int multiplier = Environment.CurrentManagedThreadId * 4 + 5; + // Every thread has its own generator for hash codes so that we won't get into a situation + // where two threads consistently give out the same hash codes. + // Choice of multiplier guarantees period of 2**32 - see Knuth Vol 2 p16 (3.2.1.2 Theorem A). + t_hashSeed = t_hashSeed * multiplier + 1; + return t_hashSeed; + } + + public static unsafe int GetHashCode(object o) + { + return ObjectHeader.GetHashCode(o); + } + + public static int OffsetToStringData + { + // This offset is baked in by string indexer intrinsic, so there is no harm + // in getting it baked in here as well. + [System.Runtime.Versioning.NonVersionable] + get => + // Number of bytes from the address pointed to by a reference to + // a String to the first 16-bit character in the String. Skip + // over the MethodTable pointer, & String + // length. Of course, the String reference points to the memory + // after the sync block, so don't count that. + // This property allows C#'s fixed statement to work on Strings. + // On 64 bit platforms, this should be 12 (8+4) and on 32 bit 8 (4+4). +#if TARGET_64BIT + 12; +#else // 32 + 8; +#endif // TARGET_64BIT + + } + + [ThreadStatic] + private static unsafe byte* t_sufficientStackLimit; + + public static unsafe void EnsureSufficientExecutionStack() + { + byte* limit = t_sufficientStackLimit; + if (limit == null) + limit = GetSufficientStackLimit(); + + byte* currentStackPtr = (byte*)(&limit); + if (currentStackPtr < limit) + throw new InsufficientExecutionStackException(); + } + + public static unsafe bool TryEnsureSufficientExecutionStack() + { + byte* limit = t_sufficientStackLimit; + if (limit == null) + limit = GetSufficientStackLimit(); + + byte* currentStackPtr = (byte*)(&limit); + return (currentStackPtr >= limit); + } + + [MethodImpl(MethodImplOptions.NoInlining)] // Only called once per thread, no point in inlining. + private static unsafe byte* GetSufficientStackLimit() + { + IntPtr lower, upper; + RuntimeImports.RhGetCurrentThreadStackBounds(out lower, out upper); + + // Compute the limit used by EnsureSufficientExecutionStack and cache it on the thread. This minimum + // stack size should be sufficient to allow a typical non-recursive call chain to execute, including + // potential exception handling and garbage collection. + +#if TARGET_64BIT + const int MinExecutionStackSize = 128 * 1024; +#else + const int MinExecutionStackSize = 64 * 1024; +#endif + + byte* limit = (((byte*)upper - (byte*)lower > MinExecutionStackSize)) ? + ((byte*)lower + MinExecutionStackSize) : ((byte*)upper); + + return (t_sufficientStackLimit = limit); + } + + [Intrinsic] + public static bool IsReferenceOrContainsReferences() + { + var pEEType = EETypePtr.EETypePtrOf(); + return !pEEType.IsValueType || pEEType.HasPointers; + } + + [Intrinsic] + internal static bool IsReference() + { + var pEEType = EETypePtr.EETypePtrOf(); + return !pEEType.IsValueType; + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsBitwiseEquatable() + { + // Only reachable for universal shared code - the compiler replaces this otherwise. + // Returning false is conservative. + return false; + } + + // Returns true iff the object has a component size; + // i.e., is variable length like System.String or Array. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool ObjectHasComponentSize(object obj) + { + Debug.Assert(obj != null); + return obj.EETypePtr.ComponentSize != 0; + } + + public static void PrepareMethod(RuntimeMethodHandle method) + { + if (method.Value == IntPtr.Zero) + throw new ArgumentException(SR.InvalidOperation_HandleIsNotInitialized, nameof(method)); + } + + public static void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeHandle[] instantiation) + { + if (method.Value == IntPtr.Zero) + throw new ArgumentException(SR.InvalidOperation_HandleIsNotInitialized, nameof(method)); + } + + /// + /// Allocate memory that is associated with the and + /// will be freed if and when the is unloaded. + /// + /// Type associated with the allocated memory. + /// Amount of memory in bytes to allocate. + /// The allocated memory + public static unsafe IntPtr AllocateTypeAssociatedMemory(Type type, int size) + { + if (type is not RuntimeType) + throw new ArgumentException(SR.Arg_MustBeType, nameof(type)); + + if (size < 0) + throw new ArgumentOutOfRangeException(nameof(size)); + + // We don't support unloading; the memory will never be freed. + return (IntPtr)NativeMemory.Alloc((uint)size); + } + + public static void PrepareDelegate(Delegate d) + { + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2059:UnrecognizedReflectionPattern", + Justification = "We keep class constructors of all types with an MethodTable")] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "Constructed MethodTable of a Nullable forces a constructed MethodTable of the element type")] + public static object GetUninitializedObject( + // This API doesn't call any constructors, but the type needs to be seen as constructed. + // A type is seen as constructed if a constructor is kept. + // This obviously won't cover a type with no constructor. Reference types with no + // constructor are an academic problem. Valuetypes with no constructors are a problem, + // but IL Linker currently treats them as always implicitly boxed. + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type) + { + if (type is null) + { + throw new ArgumentNullException(nameof(type), SR.ArgumentNull_Type); + } + + if (type is not RuntimeType) + { + throw new SerializationException(SR.Format(SR.Serialization_InvalidType, type)); + } + + if (type.HasElementType || type.IsGenericParameter) + { + throw new ArgumentException(SR.Argument_InvalidValue); + } + + if (type.ContainsGenericParameters) + { + throw new MemberAccessException(SR.Acc_CreateGeneric); + } + + if (type.IsCOMObject) + { + throw new NotSupportedException(SR.NotSupported_ManagedActivation); + } + + EETypePtr eeTypePtr = type.TypeHandle.ToEETypePtr(); + + if (eeTypePtr.ElementType == Internal.Runtime.EETypeElementType.Void) + { + throw new ArgumentException(SR.Argument_InvalidValue); + } + + // Don't allow strings (we already checked for arrays above) + if (eeTypePtr.ComponentSize != 0) + { + throw new ArgumentException(SR.Argument_NoUninitializedStrings); + } + + if (RuntimeImports.AreTypesAssignable(eeTypePtr, EETypePtr.EETypePtrOf())) + { + throw new MemberAccessException(); + } + + if (eeTypePtr.IsAbstract) + { + throw new MemberAccessException(SR.Acc_CreateAbst); + } + + if (eeTypePtr.IsByRefLike) + { + throw new NotSupportedException(SR.NotSupported_ByRefLike); + } + + if (eeTypePtr.IsNullable) + { + return GetUninitializedObject(Type.GetTypeFromEETypePtr(eeTypePtr.NullableType)); + } + + // Triggering the .cctor here is slightly different than desktop/CoreCLR, which + // decide based on BeforeFieldInit, but we don't want to include BeforeFieldInit + // in MethodTable just for this API to behave slightly differently. + RunClassConstructor(type.TypeHandle); + + return RuntimeImports.RhNewObject(eeTypePtr); + } + } + + // CLR arrays are laid out in memory as follows (multidimensional array bounds are optional): + // [ sync block || pMethodTable || num components || MD array bounds || array data .. ] + // ^ ^ ^ ^ returned reference + // | | \-- ref Unsafe.As(array).Data + // \-- array \-- ref Unsafe.As(array).Data + // The BaseSize of an array includes all the fields before the array data, + // including the sync block and method table. The reference to RawData.Data + // points at the number of components, skipping over these two pointer-sized fields. + [StructLayout(LayoutKind.Sequential)] + internal class RawArrayData + { + public uint Length; // Array._numComponents padded to IntPtr +#if TARGET_64BIT + public uint Padding; +#endif + public byte Data; + } + + [StructLayout(LayoutKind.Sequential)] + internal class RawData + { + public byte Data; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/StaticClassConstructionContext.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/StaticClassConstructionContext.cs new file mode 100644 index 00000000000000..455cc5ed014550 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/StaticClassConstructionContext.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using System.Runtime; +using System.Runtime.InteropServices; + +namespace System.Runtime.CompilerServices +{ + // This structure is used to pass context about a type's static class construction state from the runtime + // to the classlibrary via the CheckStaticClassConstruction callback. The runtime knows about the first + // two fields (cctorMethodAddress and initialized) and thus these must remain the first two fields in the + // same order and at the same offset (hence the sequential layout attribute). It is permissable for the + // classlibrary to add its own fields after these for its own use however. These must not contain GC + // references and will be zero initialized. + [CLSCompliant(false)] + [StructLayout(LayoutKind.Sequential)] + public struct StaticClassConstructionContext + { + // Pointer to the code for the static class constructor method. This is initialized by the + // binder/runtime. + public IntPtr cctorMethodAddress; + + // Initialization state of the class. This is initialized to 0. Every time managed code checks the + // cctor state the runtime will call the classlibrary's CheckStaticClassConstruction with this context + // structure unless initialized == 1. This check is specific to allow the classlibrary to store more + // than a binary state for each cctor if it so desires. + public volatile int initialized; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs new file mode 100644 index 00000000000000..b9ae606d095504 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/DependentHandle.cs @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime +{ + public struct DependentHandle : IDisposable + { + private IntPtr _handle; + + public DependentHandle(object? target, object? dependent) => + _handle = RuntimeImports.RhHandleAllocDependent(target, dependent); + + public bool IsAllocated => _handle != IntPtr.Zero; + + public object? Target + { + get + { + IntPtr handle = _handle; + + if ((nint)handle == 0) + { + ThrowHelper.ThrowInvalidOperationException(); + } + + return RuntimeImports.RhHandleGet(handle); + } + set + { + IntPtr handle = _handle; + + if ((nint)handle == 0 || value is not null) + { + ThrowHelper.ThrowInvalidOperationException(); + } + + RuntimeImports.RhHandleSet(handle, null); + } + } + + + public object? Dependent + { + get + { + IntPtr handle = _handle; + + if ((nint)handle == 0) + { + ThrowHelper.ThrowInvalidOperationException(); + } + + RuntimeImports.RhHandleGetDependent(handle, out object? dependent); + return dependent; + } + set + { + IntPtr handle = _handle; + + if ((nint)handle == 0) + { + ThrowHelper.ThrowInvalidOperationException(); + } + + RuntimeImports.RhHandleSetDependentSecondary(handle, value); + } + } + + public (object? Target, object? Dependent) TargetAndDependent + { + get + { + IntPtr handle = _handle; + + if ((nint)handle == 0) + { + ThrowHelper.ThrowInvalidOperationException(); + } + + object? target = RuntimeImports.RhHandleGetDependent(handle, out object? dependent); + + return (target, dependent); + } + } + + internal object? UnsafeGetTarget() + { + return RuntimeImports.RhHandleGet(_handle); + } + + internal object? UnsafeGetTargetAndDependent(out object? dependent) + { + return RuntimeImports.RhHandleGetDependent(_handle, out dependent); + } + + internal void UnsafeSetTargetToNull() + { + RuntimeImports.RhHandleSet(_handle, null); + } + + internal void UnsafeSetDependent(object? dependent) + { + RuntimeImports.RhHandleSetDependentSecondary(_handle, dependent); + } + + public void Dispose() + { + // Forces the DependentHandle back to non-allocated state + // (if not already there) and frees the handle if needed. + IntPtr handle = _handle; + + if ((nint)handle != 0) + { + _handle = IntPtr.Zero; + + RuntimeImports.RhHandleFree(handle); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ExceptionIDs.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ExceptionIDs.cs new file mode 100644 index 00000000000000..022a964020c182 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ExceptionIDs.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime +{ + public enum ExceptionIDs + { + OutOfMemory = 1, + Arithmetic = 2, + ArrayTypeMismatch = 3, + DivideByZero = 4, + IndexOutOfRange = 5, + InvalidCast = 6, + Overflow = 7, + NullReference = 8, + AccessViolation = 9, + DataMisaligned = 10, + EntrypointNotFound = 11, + AmbiguousImplementation = 12, + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/GCSettings.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/GCSettings.CoreRT.cs new file mode 100644 index 00000000000000..febad32e3eec1e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/GCSettings.CoreRT.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime +{ + public static partial class GCSettings + { + public static bool IsServerGC => + RuntimeImports.RhIsServerGc(); + + private static GCLatencyMode GetGCLatencyMode() => + RuntimeImports.RhGetGcLatencyMode(); + + private static SetLatencyModeStatus SetGCLatencyMode(GCLatencyMode value) => + (SetLatencyModeStatus)RuntimeImports.RhSetGcLatencyMode(value); + + private static GCLargeObjectHeapCompactionMode GetLOHCompactionMode() => + (GCLargeObjectHeapCompactionMode)RuntimeImports.RhGetLohCompactionMode(); + + private static void SetLOHCompactionMode(GCLargeObjectHeapCompactionMode value) => + RuntimeImports.RhSetLohCompactionMode((int)value); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InitializeFinalizerThread.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InitializeFinalizerThread.cs new file mode 100644 index 00000000000000..d0021229b7522c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InitializeFinalizerThread.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; + +namespace System.Runtime +{ + internal static class FinalizerInitRunner + { + // Here, we are subscribing to a callback from the runtime. This callback is made from the finalizer + // thread before any objects are finalized. + [RuntimeExport("InitializeFinalizerThread")] + public static void DoInitialize() + { + // Make sure that the finalizer thread is CoInitialized before any objects are finalized. If this + // fails, it will throw an exception and that will go unhandled, triggering a FailFast. + Thread.InitializeComForFinalizerThread(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComEventsHelper.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComEventsHelper.CoreRT.cs new file mode 100644 index 00000000000000..2ae1a795dc1f20 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComEventsHelper.CoreRT.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Versioning; + +namespace System.Runtime.InteropServices +{ + [SupportedOSPlatform("windows")] + public static class ComEventsHelper + { + public static void Combine(object rcw, Guid iid, int dispid, Delegate d) + { + throw new PlatformNotSupportedException(); + } + + public static Delegate? Remove(object rcw, Guid iid, int dispid, Delegate d) + { + throw new PlatformNotSupportedException(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.CoreRT.cs new file mode 100644 index 00000000000000..788bad4bbc1814 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.CoreRT.cs @@ -0,0 +1,733 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Threading; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// + /// Class for managing wrappers of COM IUnknown types. + /// + public abstract partial class ComWrappers + { + private const int TrackerRefShift = 32; + private const ulong TrackerRefCounter = 1UL << TrackerRefShift; + private const ulong DestroySentinel = 0x0000000080000000UL; + private const ulong TrackerRefCountMask = 0xffffffff00000000UL; + private const ulong ComRefCountMask = 0x000000007fffffffUL; + + internal static IntPtr DefaultIUnknownVftblPtr { get; } = CreateDefaultIUnknownVftbl(); + internal static IntPtr DefaultIReferenceTrackerTargetVftblPtr { get; } = CreateDefaultIReferenceTrackerTargetVftbl(); + + internal static Guid IID_IUnknown = new Guid(0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + internal static Guid IID_IReferenceTrackerTarget = new Guid(0x64bd43f8, 0xbfee, 0x4ec4, 0xb7, 0xeb, 0x29, 0x35, 0x15, 0x8d, 0xae, 0x21); + + private readonly ConditionalWeakTable _ccwTable = new ConditionalWeakTable(); + private readonly Lock _lock = new Lock(); + private readonly Dictionary _rcwCache = new Dictionary(); + private readonly ConditionalWeakTable _rcwTable = new ConditionalWeakTable(); + + /// + /// ABI for function dispatch of a COM interface. + /// + public unsafe partial struct ComInterfaceDispatch + { + /// + /// Given a from a generated Vtable, convert to the target type. + /// + /// Desired type. + /// Pointer supplied to Vtable function entry. + /// Instance of type associated with dispatched function call. + public static unsafe T GetInstance(ComInterfaceDispatch* dispatchPtr) where T : class + { + ManagedObjectWrapper* comInstance = ToManagedObjectWrapper(dispatchPtr); + return Unsafe.As(RuntimeImports.RhHandleGet(comInstance->Target)); + } + + internal static unsafe ManagedObjectWrapper* ToManagedObjectWrapper(ComInterfaceDispatch* dispatchPtr) + { + return ((InternalComInterfaceDispatch*)dispatchPtr)->_thisPtr; + } + } + + internal unsafe struct InternalComInterfaceDispatch + { + public IntPtr Vtable; + internal ManagedObjectWrapper* _thisPtr; + } + + internal enum CreateComInterfaceFlagsEx + { + None = 0, + + /// + /// The caller will provide an IUnknown Vtable. + /// + /// + /// This is useful in scenarios when the caller has no need to rely on an IUnknown instance + /// that is used when running managed code is not possible (i.e. during a GC). In traditional + /// COM scenarios this is common, but scenarios involving Reference Tracker hosting + /// calling of the IUnknown API during a GC is possible. + /// + CallerDefinedIUnknown = 1, + + /// + /// Flag used to indicate the COM interface should implement IReferenceTrackerTarget. + /// When this flag is passed, the resulting COM interface will have an internal implementation of IUnknown + /// and as such none should be supplied by the caller. + /// + TrackerSupport = 2, + + LacksICustomQueryInterface = 1 << 29, + IsComActivated = 1 << 30, + IsPegged = 1 << 31, + + InternalMask = IsPegged | IsComActivated | LacksICustomQueryInterface, + } + + internal unsafe struct ManagedObjectWrapper + { + public IntPtr Target; // This is GC Handle + public ulong RefCount; + + public int UserDefinedCount; + public ComInterfaceEntry* UserDefined; + internal InternalComInterfaceDispatch* Dispatches; + + internal CreateComInterfaceFlagsEx Flags; + + public uint AddRef() + { + return GetComCount(Interlocked.Increment(ref RefCount)); + } + + public uint Release() + { + Debug.Assert(GetComCount(RefCount) != 0); + return GetComCount(Interlocked.Decrement(ref RefCount)); + } + + public uint AddRefFromReferenceTracker() + { + ulong prev; + ulong curr; + do + { + prev = RefCount; + curr = prev + TrackerRefCounter; + } while (Interlocked.CompareExchange(ref RefCount, curr, prev) != prev); + + return GetTrackerCount(curr); + } + + public uint ReleaseFromReferenceTracker() + { + Debug.Assert(GetTrackerCount(RefCount) != 0); + ulong prev; + ulong curr; + do + { + prev = RefCount; + curr = prev - TrackerRefCounter; + } + while (Interlocked.CompareExchange(ref RefCount, curr, prev) != prev); + + // If we observe the destroy sentinel, then this release + // must destroy the wrapper. + if (RefCount == DestroySentinel) + Destroy(); + + return GetTrackerCount(RefCount); + } + + public uint Peg() + { + SetFlag(CreateComInterfaceFlagsEx.IsPegged); + return HResults.S_OK; + } + + public uint Unpeg() + { + ResetFlag(CreateComInterfaceFlagsEx.IsPegged); + return HResults.S_OK; + } + + public unsafe int QueryInterface(in Guid riid, out IntPtr ppvObject) + { + ppvObject = AsRuntimeDefined(in riid); + if (ppvObject == IntPtr.Zero) + { + ppvObject = AsUserDefined(in riid); + if (ppvObject == IntPtr.Zero) + return HResults.COR_E_INVALIDCAST; + } + + AddRef(); + return HResults.S_OK; + } + + public IntPtr As(in Guid riid) + { + // Find target interface and return dispatcher or null if not found. + IntPtr typeMaybe = AsRuntimeDefined(in riid); + if (typeMaybe == IntPtr.Zero) + typeMaybe = AsUserDefined(in riid); + + return typeMaybe; + } + + public unsafe void Destroy() + { + if (Target == IntPtr.Zero) + { + return; + } + + RuntimeImports.RhHandleFree(Target); + Target = IntPtr.Zero; + } + + private unsafe IntPtr AsRuntimeDefined(in Guid riid) + { + int i = UserDefinedCount; + if ((Flags & CreateComInterfaceFlagsEx.CallerDefinedIUnknown) == 0) + { + if (riid == IID_IUnknown) + { + return (IntPtr)(Dispatches + i); + } + + i++; + } + + if ((Flags & CreateComInterfaceFlagsEx.TrackerSupport) != 0) + { + if (riid == IID_IReferenceTrackerTarget) + { + return (IntPtr)(Dispatches + i); + } + + i++; + } + + return IntPtr.Zero; + } + + private unsafe IntPtr AsUserDefined(in Guid riid) + { + for (int i = 0; i < UserDefinedCount; ++i) + { + if (UserDefined[i].IID == riid) + { + return (IntPtr)(Dispatches + i); + } + } + + return IntPtr.Zero; + } + + private void SetFlag(CreateComInterfaceFlagsEx flag) + { + int setMask = (int)flag; + Interlocked.Or(ref Unsafe.As(ref Flags), setMask); + } + + private void ResetFlag(CreateComInterfaceFlagsEx flag) + { + int resetMask = ~(int)flag; + Interlocked.And(ref Unsafe.As(ref Flags), resetMask); + } + + private static uint GetTrackerCount(ulong c) + { + return (uint)((c & TrackerRefCountMask) >> TrackerRefShift); + } + + private static uint GetComCount(ulong c) + { + return (uint)(c & ComRefCountMask); + } + + private static bool IsMarkedToDestroy(ulong c) + { + return (c & DestroySentinel) != 0; + } + } + + internal unsafe class ManagedObjectWrapperHolder + { + private ManagedObjectWrapper* _wrapper; + + public ManagedObjectWrapperHolder(ManagedObjectWrapper* wrapper) + { + _wrapper = wrapper; + } + + public unsafe IntPtr ComIp => _wrapper->As(in ComWrappers.IID_IUnknown); + + ~ManagedObjectWrapperHolder() + { + // Release GC handle created when MOW was built. + _wrapper->Destroy(); + Marshal.FreeCoTaskMem((IntPtr)_wrapper); + } + } + + internal unsafe class NativeObjectWrapper + { + private readonly IntPtr _externalComObject; + private readonly ComWrappers _comWrappers; + internal GCHandle _proxyHandle; + + public NativeObjectWrapper(IntPtr externalComObject, ComWrappers comWrappers, object comProxy) + { + _externalComObject = externalComObject; + _comWrappers = comWrappers; + Marshal.AddRef(externalComObject); + _proxyHandle = GCHandle.Alloc(comProxy, GCHandleType.Weak); + } + + ~NativeObjectWrapper() + { + _comWrappers.RemoveRCWFromCache(_externalComObject); + if (_proxyHandle.IsAllocated) + { + _proxyHandle.Free(); + } + + Marshal.Release(_externalComObject); + } + } + + /// + /// Globally registered instance of the ComWrappers class for reference tracker support. + /// + private static ComWrappers? s_globalInstanceForTrackerSupport; + + /// + /// Globally registered instance of the ComWrappers class for marshalling. + /// + private static ComWrappers? s_globalInstanceForMarshalling; + + /// + /// Create a COM representation of the supplied object that can be passed to a non-managed environment. + /// + /// The managed object to expose outside the .NET runtime. + /// Flags used to configure the generated interface. + /// The generated COM interface that can be passed outside the .NET runtime. + /// + /// If a COM representation was previously created for the specified using + /// this instance, the previously created COM interface will be returned. + /// If not, a new one will be created. + /// + public unsafe IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) + { + if (instance == null) + throw new ArgumentNullException(nameof(instance)); + + ManagedObjectWrapperHolder ccwValue; + if (_ccwTable.TryGetValue(instance, out ccwValue)) + { + return ccwValue.ComIp; + } + + ccwValue = _ccwTable.GetValue(instance, (c) => + { + ManagedObjectWrapper* value = CreateCCW(c, flags); + return new ManagedObjectWrapperHolder(value); + }); + return ccwValue.ComIp; + } + + private unsafe ManagedObjectWrapper* CreateCCW(object instance, CreateComInterfaceFlags flags) + { + ComInterfaceEntry* userDefined = ComputeVtables(instance, flags, out int userDefinedCount); + + // Maximum number of runtime supplied vtables. + Span runtimeDefinedVtable = stackalloc IntPtr[4]; + int runtimeDefinedCount = 0; + + // Check if the caller will provide the IUnknown table. + if ((flags & CreateComInterfaceFlags.CallerDefinedIUnknown) == CreateComInterfaceFlags.None) + { + runtimeDefinedVtable[runtimeDefinedCount++] = DefaultIUnknownVftblPtr; + } + + if ((flags & CreateComInterfaceFlags.TrackerSupport) != 0) + { + runtimeDefinedVtable[runtimeDefinedCount++] = DefaultIReferenceTrackerTargetVftblPtr; + } + + // Compute size for ManagedObjectWrapper instance. + int totalDefinedCount = runtimeDefinedCount + userDefinedCount; + + // Allocate memory for the ManagedObjectWrapper. + IntPtr wrapperMem = Marshal.AllocCoTaskMem( + sizeof(ManagedObjectWrapper) + totalDefinedCount * sizeof(InternalComInterfaceDispatch)); + + // Compute the dispatch section offset and ensure it is aligned. + ManagedObjectWrapper* mow = (ManagedObjectWrapper*)wrapperMem; + + // Dispatches follow immediately after ManagedObjectWrapper + InternalComInterfaceDispatch* pDispatches = (InternalComInterfaceDispatch*)(wrapperMem + sizeof(ManagedObjectWrapper)); + for (int i = 0; i < totalDefinedCount; i++) + { + pDispatches[i].Vtable = (i < userDefinedCount) ? userDefined[i].Vtable : runtimeDefinedVtable[i - userDefinedCount]; + pDispatches[i]._thisPtr = mow; + } + + mow->Target = RuntimeImports.RhHandleAlloc(instance, GCHandleType.Normal); + mow->RefCount = 1; + mow->UserDefinedCount = userDefinedCount; + mow->UserDefined = userDefined; + mow->Flags = (CreateComInterfaceFlagsEx)flags; + mow->Dispatches = pDispatches; + return mow; + } + + /// + /// Get the currently registered managed object or creates a new managed object and registers it. + /// + /// Object to import for usage into the .NET runtime. + /// Flags used to describe the external object. + /// Returns a managed object associated with the supplied external COM object. + /// + /// If a managed object was previously created for the specified + /// using this instance, the previously created object will be returned. + /// If not, a new one will be created. + /// + public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags) + { + object? obj; + if (!TryGetOrCreateObjectForComInstanceInternal(externalComObject, IntPtr.Zero, flags, null, out obj)) + throw new ArgumentNullException(nameof(externalComObject)); + + return obj!; + } + + /// + /// Get the currently registered managed object or uses the supplied managed object and registers it. + /// + /// Object to import for usage into the .NET runtime. + /// Flags used to describe the external object. + /// The to be used as the wrapper for the external object + /// Returns a managed object associated with the supplied external COM object. + /// + /// If the instance already has an associated external object a will be thrown. + /// + public object GetOrRegisterObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, object wrapper) + { + return GetOrRegisterObjectForComInstance(externalComObject, flags, wrapper, IntPtr.Zero); + } + + /// + /// Get the currently registered managed object or uses the supplied managed object and registers it. + /// + /// Object to import for usage into the .NET runtime. + /// Flags used to describe the external object. + /// The to be used as the wrapper for the external object + /// Inner for COM aggregation scenarios + /// Returns a managed object associated with the supplied external COM object. + /// + /// This method override is for registering an aggregated COM instance with its associated inner. The inner + /// will be released when the associated wrapper is eventually freed. Note that it will be released on a thread + /// in an unknown apartment state. If the supplied inner is not known to be a free-threaded instance then + /// it is advised to not supply the inner. + /// + /// If the instance already has an associated external object a will be thrown. + /// + public object GetOrRegisterObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, object wrapper, IntPtr inner) + { + if (wrapper == null) + throw new ArgumentNullException(nameof(wrapper)); + + object? obj; + if (!TryGetOrCreateObjectForComInstanceInternal(externalComObject, inner, flags, wrapper, out obj)) + throw new ArgumentNullException(nameof(externalComObject)); + + return obj!; + } + + private unsafe ComInterfaceDispatch* TryGetComInterfaceDispatch(IntPtr comObject) + { + // If the first Vtable entry is part of the ManagedObjectWrapper IUnknown impl, + // we know how to interpret the IUnknown. + if (((IntPtr*)((IntPtr*)comObject)[0])[0] != ((IntPtr*)DefaultIUnknownVftblPtr)[0]) + { + return null; + } + + return (ComInterfaceDispatch*)comObject; + } + + /// + /// Get the currently registered managed object or creates a new managed object and registers it. + /// + /// Object to import for usage into the .NET runtime. + /// The inner instance if aggregation is involved + /// Flags used to describe the external object. + /// The to be used as the wrapper for the external object. + /// The managed object associated with the supplied external COM object or null if it could not be created. + /// Returns true if a managed object could be retrieved/created, false otherwise + private unsafe bool TryGetOrCreateObjectForComInstanceInternal( + IntPtr externalComObject, + IntPtr innerMaybe, + CreateObjectFlags flags, + object? wrapperMaybe, + out object? retValue) + { + if (externalComObject == IntPtr.Zero) + throw new ArgumentNullException(nameof(externalComObject)); + + if (flags.HasFlag(CreateObjectFlags.Aggregation)) + throw new NotImplementedException(); + + if (flags.HasFlag(CreateObjectFlags.Unwrap)) + { + var comInterfaceDispatch = TryGetComInterfaceDispatch(externalComObject); + if (comInterfaceDispatch != null) + { + retValue = ComInterfaceDispatch.GetInstance(comInterfaceDispatch); + return true; + } + } + + if (!flags.HasFlag(CreateObjectFlags.UniqueInstance)) + { + using (LockHolder.Hold(_lock)) + { + if (_rcwCache.TryGetValue(externalComObject, out GCHandle handle)) + { + retValue = handle.Target; + return false; + } + } + } + + retValue = CreateObject(externalComObject, flags); + if (retValue == null) + { + // If ComWrappers instance cannot create wrapper, we can do nothing here. + return false; + } + + if (flags.HasFlag(CreateObjectFlags.UniqueInstance)) + { + // No need to cache NativeObjectWrapper for unique instances. They are not cached. + return true; + } + + using (LockHolder.Hold(_lock)) + { + if (_rcwCache.TryGetValue(externalComObject, out var existingHandle)) + { + retValue = existingHandle.Target; + } + else + { + NativeObjectWrapper wrapper = new NativeObjectWrapper( + externalComObject, + this, + retValue); + _rcwTable.Add(retValue, wrapper); + _rcwCache.Add(externalComObject, wrapper._proxyHandle); + } + } + + return true; + } + + private void RemoveRCWFromCache(IntPtr comPointer) + { + using (LockHolder.Hold(_lock)) + { + _rcwCache.Remove(comPointer); + } + } + + /// + /// Register a instance to be used as the global instance for reference tracker support. + /// + /// Instance to register + /// + /// This function can only be called a single time. Subsequent calls to this function will result + /// in a being thrown. + /// + /// Scenarios where this global instance may be used are: + /// * Object tracking via the and flags. + /// + public static void RegisterForTrackerSupport(ComWrappers instance) + { + if (instance == null) + throw new ArgumentNullException(nameof(instance)); + + if (null != Interlocked.CompareExchange(ref s_globalInstanceForTrackerSupport, instance, null)) + { + throw new InvalidOperationException(SR.InvalidOperation_ResetGlobalComWrappersInstance); + } + } + + /// + /// Register a instance to be used as the global instance for marshalling in the runtime. + /// + /// Instance to register + /// + /// This function can only be called a single time. Subsequent calls to this function will result + /// in a being thrown. + /// + /// Scenarios where this global instance may be used are: + /// * Usage of COM-related Marshal APIs + /// * P/Invokes with COM-related types + /// * COM activation + /// + [SupportedOSPlatformAttribute("windows")] + public static void RegisterForMarshalling(ComWrappers instance) + { + if (instance == null) + throw new ArgumentNullException(nameof(instance)); + + if (null != Interlocked.CompareExchange(ref s_globalInstanceForMarshalling, instance, null)) + { + throw new InvalidOperationException(SR.InvalidOperation_ResetGlobalComWrappersInstance); + } + } + + /// + /// Get the runtime provided IUnknown implementation. + /// + /// Function pointer to QueryInterface. + /// Function pointer to AddRef. + /// Function pointer to Release. + protected internal static unsafe void GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease) + { + fpQueryInterface = (IntPtr)(delegate* unmanaged)&ComWrappers.IUnknown_QueryInterface; + fpAddRef = (IntPtr)(delegate* unmanaged)&ComWrappers.IUnknown_AddRef; + fpRelease = (IntPtr)(delegate* unmanaged)&ComWrappers.IUnknown_Release; + } + + internal static IntPtr ComInterfaceForObject(object instance) + { + if (s_globalInstanceForMarshalling == null) + { + throw new InvalidOperationException(SR.InvalidOperation_ComInteropRequireComWrapperInstance); + } + + return s_globalInstanceForMarshalling.GetOrCreateComInterfaceForObject(instance, CreateComInterfaceFlags.None); + } + + internal static unsafe IntPtr ComInterfaceForObject(object instance, Guid targetIID) + { + IntPtr unknownPtr = ComInterfaceForObject(instance); + IntPtr comObjectInterface; + ManagedObjectWrapper* wrapper = ComInterfaceDispatch.ToManagedObjectWrapper((ComInterfaceDispatch*)unknownPtr); + int resultCode = wrapper->QueryInterface(in targetIID, out comObjectInterface); + // We no longer need IUnknownPtr, release reference + Marshal.Release(unknownPtr); + if (resultCode != 0) + { + throw new InvalidCastException(); + } + + return comObjectInterface; + } + + internal static object ComObjectForInterface(IntPtr externalComObject) + { + if (s_globalInstanceForMarshalling == null) + { + throw new InvalidOperationException(SR.InvalidOperation_ComInteropRequireComWrapperInstance); + } + + return s_globalInstanceForMarshalling.GetOrCreateObjectForComInstance(externalComObject, CreateObjectFlags.Unwrap); + } + + [UnmanagedCallersOnly] + internal static unsafe int IUnknown_QueryInterface(IntPtr pThis, Guid* guid, IntPtr* ppObject) + { + ManagedObjectWrapper* wrapper = ComInterfaceDispatch.ToManagedObjectWrapper((ComInterfaceDispatch*)pThis); + return wrapper->QueryInterface(in *guid, out *ppObject); + } + + [UnmanagedCallersOnly] + internal static unsafe uint IUnknown_AddRef(IntPtr pThis) + { + ManagedObjectWrapper* wrapper = ComInterfaceDispatch.ToManagedObjectWrapper((ComInterfaceDispatch*)pThis); + return wrapper->AddRef(); + } + + [UnmanagedCallersOnly] + internal static unsafe uint IUnknown_Release(IntPtr pThis) + { + ManagedObjectWrapper* wrapper = ComInterfaceDispatch.ToManagedObjectWrapper((ComInterfaceDispatch*)pThis); + uint refcount = wrapper->Release(); + if (refcount == 0) + { + wrapper->Destroy(); + } + + return refcount; + } + + [UnmanagedCallersOnly] + internal static unsafe int IReferenceTrackerTarget_QueryInterface(IntPtr pThis, Guid* guid, IntPtr* ppObject) + { + ManagedObjectWrapper* wrapper = ComInterfaceDispatch.ToManagedObjectWrapper((ComInterfaceDispatch*)pThis); + return wrapper->QueryInterface(in *guid, out *ppObject); + } + + [UnmanagedCallersOnly] + internal static unsafe uint IReferenceTrackerTarget_AddRefFromReferenceTracker(IntPtr pThis) + { + ManagedObjectWrapper* wrapper = ComInterfaceDispatch.ToManagedObjectWrapper((ComInterfaceDispatch*)pThis); + return wrapper->AddRefFromReferenceTracker(); + } + + [UnmanagedCallersOnly] + internal static unsafe uint IReferenceTrackerTarget_ReleaseFromReferenceTracker(IntPtr pThis) + { + ManagedObjectWrapper* wrapper = ComInterfaceDispatch.ToManagedObjectWrapper((ComInterfaceDispatch*)pThis); + return wrapper->ReleaseFromReferenceTracker(); + } + + [UnmanagedCallersOnly] + internal static unsafe uint IReferenceTrackerTarget_Peg(IntPtr pThis) + { + ManagedObjectWrapper* wrapper = ComInterfaceDispatch.ToManagedObjectWrapper((ComInterfaceDispatch*)pThis); + return wrapper->Peg(); + } + + [UnmanagedCallersOnly] + internal static unsafe uint IReferenceTrackerTarget_Unpeg(IntPtr pThis) + { + ManagedObjectWrapper* wrapper = ComInterfaceDispatch.ToManagedObjectWrapper((ComInterfaceDispatch*)pThis); + return wrapper->Unpeg(); + } + + private static unsafe IntPtr CreateDefaultIUnknownVftbl() + { + IntPtr* vftbl = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(ComWrappers), 3 * sizeof(IntPtr)); + GetIUnknownImpl(out vftbl[0], out vftbl[1], out vftbl[2]); + return (IntPtr)vftbl; + } + + private static unsafe IntPtr CreateDefaultIReferenceTrackerTargetVftbl() + { + IntPtr* vftbl = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(ComWrappers), 7 * sizeof(IntPtr)); + vftbl[0] = (IntPtr)(delegate* unmanaged)&ComWrappers.IReferenceTrackerTarget_QueryInterface; + vftbl[1] = (IntPtr)(delegate* unmanaged)&ComWrappers.IUnknown_AddRef; + vftbl[2] = (IntPtr)(delegate* unmanaged)&ComWrappers.IUnknown_Release; + vftbl[3] = (IntPtr)(delegate* unmanaged)&ComWrappers.IReferenceTrackerTarget_AddRefFromReferenceTracker; + vftbl[4] = (IntPtr)(delegate* unmanaged)&ComWrappers.IReferenceTrackerTarget_ReleaseFromReferenceTracker; + vftbl[5] = (IntPtr)(delegate* unmanaged)&ComWrappers.IReferenceTrackerTarget_Peg; + vftbl[6] = (IntPtr)(delegate* unmanaged)&ComWrappers.IReferenceTrackerTarget_Unpeg; + return (IntPtr)vftbl; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/CriticalHandle.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/CriticalHandle.CoreRT.cs new file mode 100644 index 00000000000000..15f17d3af19ce5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/CriticalHandle.CoreRT.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.ConstrainedExecution; + +namespace System.Runtime.InteropServices +{ + public abstract partial class CriticalHandle : CriticalFinalizerObject, IDisposable + { + internal void ReleaseHandleFailed() + { + } + + internal void SetHandleInternal(IntPtr handle) + { + SetHandle(handle); + } + + internal IntPtr GetHandleInternal() + { + return this.handle; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreRT.cs new file mode 100644 index 00000000000000..bcadaa56b04a05 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreRT.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + public partial struct GCHandle + { + private static IntPtr InternalAlloc(object value, GCHandleType type) => RuntimeImports.RhHandleAlloc(value, type); + + private static void InternalFree(IntPtr handle) => RuntimeImports.RhHandleFree(handle); + + private static object InternalGet(IntPtr handle) => RuntimeImports.RhHandleGet(handle); + + private static void InternalSet(IntPtr handle, object value) => RuntimeImports.RhHandleSet(handle, value); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs new file mode 100644 index 00000000000000..e3cc38081028e3 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// + /// Hooks for System.Private.Interop.dll code to access internal functionality in System.Private.CoreLib.dll. + /// + /// Methods added to InteropExtensions should also be added to the System.Private.CoreLib.InteropServices contract + /// in order to be accessible from System.Private.Interop.dll. + /// + [CLSCompliant(false)] + [ReflectionBlocked] + public static class InteropExtensions + { + public static int GetElementSize(this Array array) + { + return array.EETypePtr.ComponentSize; + } + + internal static bool MightBeBlittable(this EETypePtr eeType) + { + // + // This is used as the approximate implementation of MethodTable::IsBlittable(). It will err in the direction of declaring + // things blittable since it is used for argument validation only. + // + return !eeType.HasPointers; + } + + public static bool IsBlittable(this RuntimeTypeHandle handle) + { + return handle.ToEETypePtr().MightBeBlittable(); + } + + public static bool IsBlittable(this object obj) + { + return obj.EETypePtr.MightBeBlittable(); + } + + public static bool IsGenericType(this RuntimeTypeHandle handle) + { + EETypePtr eeType = handle.ToEETypePtr(); + return eeType.IsGeneric; + } + + public static bool IsGenericTypeDefinition(this RuntimeTypeHandle handle) + { + EETypePtr eeType = handle.ToEETypePtr(); + return eeType.IsGenericTypeDefinition; + } + + // + // Returns the raw function pointer for a open static delegate - if the function has a jump stub + // it returns the jump target. Therefore the function pointer returned + // by two delegates may NOT be unique + // + public static IntPtr GetRawFunctionPointerForOpenStaticDelegate(this Delegate del) + { + //If it is not open static then return IntPtr.Zero + if (!del.IsOpenStatic) + return IntPtr.Zero; + + RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate; + + IntPtr funcPtr = del.GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out bool _, out bool _); + return funcPtr; + } + + public static int GetValueTypeSize(this RuntimeTypeHandle handle) + { + return (int)handle.ToEETypePtr().ValueTypeSize; + } + + public static bool IsValueType(this RuntimeTypeHandle handle) + { + return handle.ToEETypePtr().IsValueType; + } + + public static bool IsEnum(this RuntimeTypeHandle handle) + { + return handle.ToEETypePtr().IsEnum; + } + + public static bool IsInterface(this RuntimeTypeHandle handle) + { + return handle.ToEETypePtr().IsInterface; + } + + public static bool AreTypesAssignable(RuntimeTypeHandle sourceType, RuntimeTypeHandle targetType) + { + return RuntimeImports.AreTypesAssignable(sourceType.ToEETypePtr(), targetType.ToEETypePtr()); + } + + public static RuntimeTypeHandle GetTypeHandle(this object target) + { + return new RuntimeTypeHandle(target.EETypePtr); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Com.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Com.cs new file mode 100644 index 00000000000000..a7588ee047d5c4 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Com.cs @@ -0,0 +1,431 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.InteropServices.ComTypes; +using System.Runtime.Versioning; + +using Internal.Reflection.Augments; + +namespace System.Runtime.InteropServices +{ + public static partial class Marshal + { + internal static bool IsBuiltInComSupported => false; + + private const int DISP_E_PARAMNOTFOUND = unchecked((int)0x80020004); + + public static int GetHRForException(Exception? e) + { + return PInvokeMarshal.GetHRForException(e); + } + + public static bool AreComObjectsAvailableForCleanup() => false; + + [SupportedOSPlatform("windows")] + public static IntPtr CreateAggregatedObject(IntPtr pOuter, object o) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [RequiresUnreferencedCode("Built-in COM support is not trim compatible", Url = "https://aka.ms/dotnet-illink/com")] + [SupportedOSPlatform("windows")] + public static object BindToMoniker(string monikerName) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + public static void CleanupUnusedObjectsInCurrentContext() + { + } + + [SupportedOSPlatform("windows")] + public static IntPtr CreateAggregatedObject(IntPtr pOuter, T o) where T : notnull + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static object? CreateWrapperOfType(object? o, Type t) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static TWrapper CreateWrapperOfType(T? o) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static void ChangeWrapperHandleStrength(object otp, bool fIsWeak) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static int FinalReleaseComObject(object o) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static IntPtr GetComInterfaceForObject(object o, Type T) + { + if (o is null) + { + throw new ArgumentNullException(nameof(o)); + } + + if (T is null) + { + throw new ArgumentNullException(nameof(T)); + } + + return ComWrappers.ComInterfaceForObject(o, T.GUID); + } + + [SupportedOSPlatform("windows")] + public static IntPtr GetComInterfaceForObject(object o, Type T, CustomQueryInterfaceMode mode) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static IntPtr GetComInterfaceForObject([DisallowNull] T o) + { + return GetComInterfaceForObject(o!, typeof(T)); + } + + [SupportedOSPlatform("windows")] + public static object? GetComObjectData(object obj, object key) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static IntPtr GetIDispatchForObject(object o) + { + if (o is null) + { + throw new ArgumentNullException(nameof(o)); + } + + return ComWrappers.ComInterfaceForObject(o, new Guid(0x00020400, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46) /* IID_IDispatch */); + } + + [SupportedOSPlatform("windows")] + public static IntPtr GetIUnknownForObject(object o) + { + return ComWrappers.ComInterfaceForObject(o); + } + + [SupportedOSPlatform("windows")] + public static unsafe void GetNativeVariantForObject(object? obj, IntPtr pDstNativeVariant) + { + if (pDstNativeVariant == IntPtr.Zero) + { + throw new ArgumentNullException(nameof(pDstNativeVariant)); + } + + Variant* data = (Variant*)pDstNativeVariant; + if (obj == null) + { + data->VariantType = VarEnum.VT_EMPTY; + return; + } + + switch (obj) + { + // Int and String most used types. + case int value: + data->AsI4 = value; + break; + case string value: + data->AsBstr = value; + break; + + case bool value: + data->AsBool = value; + break; + case byte value: + data->AsUi1 = value; + break; + case sbyte value: + data->AsI1 = value; + break; + case short value: + data->AsI2 = value; + break; + case ushort value: + data->AsUi2 = value; + break; + case uint value: + data->AsUi4 = value; + break; + case long value: + data->AsI8 = value; + break; + case ulong value: + data->AsUi8 = value; + break; + case float value: + data->AsR4 = value; + break; + case double value: + data->AsR8 = value; + break; + case DateTime value: + data->AsDate = value; + break; + case decimal value: + data->AsDecimal = value; + break; + case char value: + data->AsUi2 = value; + break; + case BStrWrapper value: + data->AsBstr = value.WrappedObject; + break; + case CurrencyWrapper value: + data->AsCy = value.WrappedObject; + break; + case UnknownWrapper value: + data->AsUnknown = value.WrappedObject; + break; + case DispatchWrapper value: + data->AsDispatch = value.WrappedObject; + break; + case ErrorWrapper value: + data->AsError = value.ErrorCode; + break; + case VariantWrapper value: + throw new ArgumentException(); + case DBNull value: + data->SetAsNULL(); + break; + case Missing value: + data->AsError = DISP_E_PARAMNOTFOUND; + break; + case IConvertible value: + switch (value.GetTypeCode()) + { + case TypeCode.Empty: + data->VariantType = VarEnum.VT_EMPTY; + break; + case TypeCode.Object: + data->AsUnknown = value; + break; + case TypeCode.DBNull: + data->SetAsNULL(); + break; + case TypeCode.Boolean: + data->AsBool = value.ToBoolean(null); + break; + case TypeCode.Char: + data->AsUi2 = value.ToChar(null); + break; + case TypeCode.SByte: + data->AsI1 = value.ToSByte(null); + break; + case TypeCode.Byte: + data->AsUi1 = value.ToByte(null); + break; + case TypeCode.Int16: + data->AsI2 = value.ToInt16(null); + break; + case TypeCode.UInt16: + data->AsUi2 = value.ToUInt16(null); + break; + case TypeCode.Int32: + data->AsI4 = value.ToInt32(null); + break; + case TypeCode.UInt32: + data->AsUi4 = value.ToUInt32(null); + break; + case TypeCode.Int64: + data->AsI8 = value.ToInt64(null); + break; + case TypeCode.UInt64: + data->AsUi8 = value.ToUInt64(null); + break; + case TypeCode.Single: + data->AsR4 = value.ToSingle(null); + break; + case TypeCode.Double: + data->AsR8 = value.ToDouble(null); + break; + case TypeCode.Decimal: + data->AsDecimal = value.ToDecimal(null); + break; + case TypeCode.DateTime: + data->AsDate = value.ToDateTime(null); + break; + case TypeCode.String: + data->AsBstr = value.ToString(); + break; + default: + throw new NotSupportedException(); + } + break; + case CriticalHandle: + throw new ArgumentException(); + case SafeHandle: + throw new ArgumentException(); + case Array: + // SAFEARRAY implementation goes here. + throw new NotSupportedException("VT_ARRAY"); + case ValueType: + throw new NotSupportedException("VT_RECORD"); + default: + data->AsDispatch = obj; + break; + } + } + + [SupportedOSPlatform("windows")] + public static void GetNativeVariantForObject(T? obj, IntPtr pDstNativeVariant) + { + GetNativeVariantForObject((object?)obj, pDstNativeVariant); + } + + [SupportedOSPlatform("windows")] + public static object GetTypedObjectForIUnknown(IntPtr pUnk, Type t) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static object GetObjectForIUnknown(IntPtr pUnk) + { + return ComWrappers.ComObjectForInterface(pUnk); + } + + [SupportedOSPlatform("windows")] + public static unsafe object? GetObjectForNativeVariant(IntPtr pSrcNativeVariant) + { + if (pSrcNativeVariant == IntPtr.Zero) + { + throw new ArgumentNullException(nameof(pSrcNativeVariant)); + } + + Variant* data = (Variant*)pSrcNativeVariant; + + if (data->IsEmpty) + { + return null; + } + + switch (data->VariantType) + { + case VarEnum.VT_NULL: + return DBNull.Value; + + case VarEnum.VT_I1: return data->AsI1; + case VarEnum.VT_I2: return data->AsI2; + case VarEnum.VT_I4: return data->AsI4; + case VarEnum.VT_I8: return data->AsI8; + case VarEnum.VT_UI1: return data->AsUi1; + case VarEnum.VT_UI2: return data->AsUi2; + case VarEnum.VT_UI4: return data->AsUi4; + case VarEnum.VT_UI8: return data->AsUi8; + case VarEnum.VT_INT: return data->AsInt; + case VarEnum.VT_UINT: return data->AsUint; + case VarEnum.VT_BOOL: return data->AsBool; + case VarEnum.VT_ERROR: return data->AsError; + case VarEnum.VT_R4: return data->AsR4; + case VarEnum.VT_R8: return data->AsR8; + case VarEnum.VT_DECIMAL: return data->AsDecimal; + case VarEnum.VT_CY: return data->AsCy; + case VarEnum.VT_DATE: return data->AsDate; + case VarEnum.VT_BSTR: return data->AsBstr; + case VarEnum.VT_UNKNOWN: return data->AsUnknown; + case VarEnum.VT_DISPATCH: return data->AsDispatch; + + default: + // Other VARIANT types not supported yet. + throw new NotSupportedException(); + } + } + + [return: MaybeNull] + [SupportedOSPlatform("windows")] + public static T GetObjectForNativeVariant(IntPtr pSrcNativeVariant) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static object?[] GetObjectsForNativeVariants(IntPtr aSrcNativeVariant, int cVars) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static T[] GetObjectsForNativeVariants(IntPtr aSrcNativeVariant, int cVars) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static int GetStartComSlot(Type t) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static int GetEndComSlot(Type t) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + internal static Type? GetTypeFromCLSID(Guid clsid, string? server, bool throwOnError) + { + return ReflectionAugments.ReflectionCoreCallbacks.GetTypeFromCLSID(clsid, server, throwOnError); + } + + [SupportedOSPlatform("windows")] + public static string GetTypeInfoName(ITypeInfo typeInfo) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static object GetUniqueObjectForIUnknown(IntPtr unknown) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + public static bool IsComObject(object o) + { + if (o is null) + { + throw new ArgumentNullException(nameof(o)); + } + + return false; + } + + public static bool IsTypeVisibleFromCom(Type t) + { + if (t is null) + { + throw new ArgumentNullException(nameof(t)); + } + return false; + } + + [SupportedOSPlatform("windows")] + public static int ReleaseComObject(object o) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + [SupportedOSPlatform("windows")] + public static bool SetComObjectData(object obj, object key, object? data) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreRT.cs new file mode 100644 index 00000000000000..a7c62a82c44efb --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreRT.cs @@ -0,0 +1,370 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Text; + +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + public static partial class Marshal + { + internal static int SizeOfHelper(Type t, bool throwIfNotMarshalable) + { + Debug.Assert(throwIfNotMarshalable); + return RuntimeAugments.InteropCallbacks.GetStructUnsafeStructSize(t.TypeHandle); + } + + public static IntPtr OffsetOf(Type t, string fieldName) + { + if (t == null) + throw new ArgumentNullException(nameof(t)); + + if (string.IsNullOrEmpty(fieldName)) + throw new ArgumentNullException(nameof(fieldName)); + + if (t.TypeHandle.IsGenericTypeDefinition()) + throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(t)); + + return new IntPtr(RuntimeAugments.InteropCallbacks.GetStructFieldOffset(t.TypeHandle, fieldName)); + } + + private static void PtrToStructureHelper(IntPtr ptr, object structure, bool allowValueClasses) + { + if (ptr == IntPtr.Zero) + throw new ArgumentNullException(nameof(ptr)); + + if (structure == null) + throw new ArgumentNullException(nameof(structure)); + + if (!allowValueClasses && structure.EETypePtr.IsValueType) + { + throw new ArgumentException(nameof(structure), SR.Argument_StructMustNotBeValueClass); + } + + PtrToStructureImpl(ptr, structure); + } + + internal static unsafe void PtrToStructureImpl(IntPtr ptr, object structure) + { + RuntimeTypeHandle structureTypeHandle = structure.GetType().TypeHandle; + + IntPtr unmarshalStub; + if (structureTypeHandle.IsBlittable()) + { + if (!RuntimeAugments.InteropCallbacks.TryGetStructUnmarshalStub(structureTypeHandle, out unmarshalStub)) + { + unmarshalStub = IntPtr.Zero; + } + } + else + { + unmarshalStub = RuntimeAugments.InteropCallbacks.GetStructUnmarshalStub(structureTypeHandle); + } + + if (unmarshalStub != IntPtr.Zero) + { + if (structureTypeHandle.IsValueType()) + { + ((delegate*)unmarshalStub)(ref *(byte*)ptr, ref structure.GetRawData()); + } + else + { + ((delegate*)unmarshalStub)(ref *(byte*)ptr, structure); + } + } + else + { + nuint size = (nuint)RuntimeAugments.InteropCallbacks.GetStructUnsafeStructSize(structureTypeHandle); + + Buffer.Memmove(ref structure.GetRawData(), ref *(byte*)ptr, size); + } + } + + [RequiresDynamicCode("Marshalling code for the object might not be available. Use the DestroyStructure overload instead.")] + public static unsafe void DestroyStructure(IntPtr ptr, Type structuretype) + { + if (ptr == IntPtr.Zero) + throw new ArgumentNullException(nameof(ptr)); + + if (structuretype == null) + throw new ArgumentNullException(nameof(structuretype)); + + RuntimeTypeHandle structureTypeHandle = structuretype.TypeHandle; + + if (structureTypeHandle.IsGenericType() || structureTypeHandle.IsGenericTypeDefinition()) + throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(structuretype)); + + if (structureTypeHandle.IsEnum() || + structureTypeHandle.IsInterface() || + InteropExtensions.AreTypesAssignable(typeof(Delegate).TypeHandle, structureTypeHandle)) + { + throw new ArgumentException(SR.Format(SR.Argument_MustHaveLayoutOrBeBlittable, structureTypeHandle.LastResortToString)); + } + + if (structureTypeHandle.IsBlittable()) + { + // ok to call with blittable structure, but no work to do in this case. + return; + } + + IntPtr destroyStructureStub = RuntimeAugments.InteropCallbacks.GetDestroyStructureStub(structureTypeHandle, out bool hasInvalidLayout); + if (hasInvalidLayout) + throw new ArgumentException(SR.Format(SR.Argument_MustHaveLayoutOrBeBlittable, structureTypeHandle.LastResortToString)); + // DestroyStructureStub == IntPtr.Zero means its fields don't need to be destroyed + if (destroyStructureStub != IntPtr.Zero) + { + ((delegate*)destroyStructureStub)(ref *(byte*)ptr); + } + } + + [RequiresDynamicCode("Marshalling code for the object might not be available. Use the StructureToPtr overload instead.")] + public static unsafe void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld) + { + if (structure == null) + throw new ArgumentNullException(nameof(structure)); + + if (ptr == IntPtr.Zero) + throw new ArgumentNullException(nameof(ptr)); + + if (fDeleteOld) + { + DestroyStructure(ptr, structure.GetType()); + } + + RuntimeTypeHandle structureTypeHandle = structure.GetType().TypeHandle; + + if (structureTypeHandle.IsGenericType() || structureTypeHandle.IsGenericTypeDefinition()) + { + throw new ArgumentException(SR.Argument_NeedNonGenericObject, nameof(structure)); + } + + IntPtr marshalStub; + if (structureTypeHandle.IsBlittable()) + { + if (!RuntimeAugments.InteropCallbacks.TryGetStructMarshalStub(structureTypeHandle, out marshalStub)) + { + marshalStub = IntPtr.Zero; + } + } + else + { + marshalStub = RuntimeAugments.InteropCallbacks.GetStructMarshalStub(structureTypeHandle); + } + + if (marshalStub != IntPtr.Zero) + { + if (structureTypeHandle.IsValueType()) + { + ((delegate*)marshalStub)(ref structure.GetRawData(), ref *(byte*)ptr); + } + else + { + ((delegate*)marshalStub)(structure, ref *(byte*)ptr); + } + } + else + { + nuint size = (nuint)RuntimeAugments.InteropCallbacks.GetStructUnsafeStructSize(structureTypeHandle); + + Buffer.Memmove(ref *(byte*)ptr, ref structure.GetRawData(), size); + } + } + + private static void PrelinkCore(MethodInfo m) + { + // Note: This method is effectively a no-op in ahead-of-time compilation scenarios. In CoreCLR and Desktop, this will pre-generate + // the P/Invoke, but everything is pre-generated in CoreRT. + } + + internal static Delegate GetDelegateForFunctionPointerInternal(IntPtr ptr, Type t) + { + return PInvokeMarshal.GetDelegateForFunctionPointer(ptr, t.TypeHandle); + } + + internal static IntPtr GetFunctionPointerForDelegateInternal(Delegate d) + { + return PInvokeMarshal.GetFunctionPointerForDelegate(d); + } + + public static int GetLastPInvokeError() + { + return PInvokeMarshal.t_lastError; + } + + public static void SetLastPInvokeError(int error) + { + PInvokeMarshal.t_lastError = error; + } + + internal static bool IsPinnable(object o) + { + return (o == null) || o.EETypePtr.MightBeBlittable(); + } + + public static int GetExceptionCode() + { + // Obsolete + throw new PlatformNotSupportedException(); + } + + public static IntPtr GetExceptionPointers() + { + throw new PlatformNotSupportedException(); + } + + [RequiresDynamicCode("Marshalling code for the object might not be available")] + public static unsafe byte ReadByte(object ptr, int ofs) + { + return ReadValueSlow(ptr, ofs, &ReadByte); + } + + [RequiresDynamicCode("Marshalling code for the object might not be available")] + public static unsafe short ReadInt16(object ptr, int ofs) + { + return ReadValueSlow(ptr, ofs, &ReadInt16); + } + + [RequiresDynamicCode("Marshalling code for the object might not be available")] + public static unsafe int ReadInt32(object ptr, int ofs) + { + return ReadValueSlow(ptr, ofs, &ReadInt32); + } + + [RequiresDynamicCode("Marshalling code for the object might not be available")] + public static unsafe long ReadInt64(object ptr, int ofs) + { + return ReadValueSlow(ptr, ofs, &ReadInt64); + } + + //==================================================================== + // Read value from marshaled object (marshaled using AsAny) + // It's quite slow and can return back dangling pointers + // It's only there for backcompact + // People should instead use the IntPtr overloads + //==================================================================== + [RequiresDynamicCode("Marshalling code for the object might not be available")] + private static unsafe T ReadValueSlow(object ptr, int ofs, delegate* readValueHelper) + { + // Consumers of this method are documented to throw AccessViolationException on any AV + if (ptr is null) + { + throw new AccessViolationException(); + } + + if (ptr.EETypePtr.IsArray || + ptr is string || + ptr is StringBuilder) + { + // We could implement these if really needed. + throw new PlatformNotSupportedException(); + } + + // We are going to assume this is a Sequential or Explicit layout type because + // we don't want to touch reflection metadata for this. + // If we're wrong, this will throw the exception we get for missing interop data + // instead of an ArgumentException. + // That's quite acceptable for an obsoleted API. + + Type structType = ptr.GetType(); + + int size = SizeOf(structType); + + // Compat note: CLR wouldn't bother with a range check. If someone does this, + // they're likely taking dependency on some CLR implementation detail quirk. + if (checked(ofs + Unsafe.SizeOf()) > size) + throw new ArgumentOutOfRangeException(nameof(ofs)); + + IntPtr nativeBytes = AllocCoTaskMem(size); + Buffer.ZeroMemory((byte*)nativeBytes, (nuint)size); + + try + { + StructureToPtr(ptr, nativeBytes, false); + return readValueHelper(nativeBytes, ofs); + } + finally + { + DestroyStructure(nativeBytes, structType); + FreeCoTaskMem(nativeBytes); + } + } + + [RequiresDynamicCode("Marshalling code for the object might not be available")] + public static unsafe void WriteByte(object ptr, int ofs, byte val) + { + WriteValueSlow(ptr, ofs, val, &WriteByte); + } + + [RequiresDynamicCode("Marshalling code for the object might not be available")] + public static unsafe void WriteInt16(object ptr, int ofs, short val) + { + WriteValueSlow(ptr, ofs, val, &WriteInt16); + } + + [RequiresDynamicCode("Marshalling code for the object might not be available")] + public static unsafe void WriteInt32(object ptr, int ofs, int val) + { + WriteValueSlow(ptr, ofs, val, &WriteInt32); + } + + [RequiresDynamicCode("Marshalling code for the object might not be available")] + public static unsafe void WriteInt64(object ptr, int ofs, long val) + { + WriteValueSlow(ptr, ofs, val, &WriteInt64); + } + + [RequiresDynamicCode("Marshalling code for the object might not be available")] + private static unsafe void WriteValueSlow(object ptr, int ofs, T val, delegate* writeValueHelper) + { + // Consumers of this method are documented to throw AccessViolationException on any AV + if (ptr is null) + { + throw new AccessViolationException(); + } + + if (ptr.EETypePtr.IsArray || + ptr is string || + ptr is StringBuilder) + { + // We could implement these if really needed. + throw new PlatformNotSupportedException(); + } + + // We are going to assume this is a Sequential or Explicit layout type because + // we don't want to touch reflection metadata for this. + // If we're wrong, this will throw the exception we get for missing interop data + // instead of an ArgumentException. + // That's quite acceptable for an obsoleted API. + + Type structType = ptr.GetType(); + + int size = SizeOf(structType); + + // Compat note: CLR wouldn't bother with a range check. If someone does this, + // they're likely taking dependency on some CLR implementation detail quirk. + if (checked(ofs + Unsafe.SizeOf()) > size) + throw new ArgumentOutOfRangeException(nameof(ofs)); + + IntPtr nativeBytes = AllocCoTaskMem(size); + Buffer.ZeroMemory((byte*)nativeBytes, (nuint)size); + + try + { + StructureToPtr(ptr, nativeBytes, false); + writeValueHelper(nativeBytes, ofs, val); + PtrToStructureImpl(nativeBytes, ptr); + } + finally + { + DestroyStructure(nativeBytes, structType); + FreeCoTaskMem(nativeBytes); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.CoreRT.cs new file mode 100644 index 00000000000000..8bc85e82a23e59 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.CoreRT.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + public static unsafe partial class MemoryMarshal + { + /// + /// Returns a reference to the 0th element of . If the array is empty, returns a reference to where the 0th element + /// would have been stored. Such a reference may be used for pinning but must never be dereferenced. + /// + /// is . + /// + /// This method does not perform array variance checks. The caller must manually perform any array variance checks + /// if the caller wishes to write to the returned reference. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T GetArrayDataReference(T[] array) => + ref Unsafe.As(ref Unsafe.As(array).Data); + + /// + /// Returns a reference to the 0th element of . If the array is empty, returns a reference to where the 0th element + /// would have been stored. Such a reference may be used for pinning but must never be dereferenced. + /// + /// is . + /// + /// The caller must manually reinterpret the returned ref byte as a ref to the array's underlying elemental type, + /// perhaps utilizing an API such as System.Runtime.CompilerServices.Unsafe.As to assist with the reinterpretation. + /// This technique does not perform array variance checks. The caller must manually perform any array variance checks + /// if the caller wishes to write to the returned reference. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref byte GetArrayDataReference(Array array) + { + // If needed, we can save one or two instructions per call by marking this method as intrinsic and asking the JIT + // to special-case arrays of known type and dimension. + + // See comment on RawArrayData (in RuntimeHelpers.CoreCLR.cs) for details + return ref Unsafe.AddByteOffset(ref Unsafe.As(array).Data, (nuint)array.EETypePtr.BaseSize - (nuint)(2 * sizeof(IntPtr))); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeFunctionPointerWrapper.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeFunctionPointerWrapper.cs new file mode 100644 index 00000000000000..21c4b32166aeeb --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeFunctionPointerWrapper.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + /// + /// Base class for all 'wrapper' classes that wraps a native function pointer + /// The forward delegates (that wraps native function pointers) points to derived Invoke method of this + /// class, and the Invoke method would implement the marshalling and making the call + /// + public abstract class NativeFunctionPointerWrapper + { + public NativeFunctionPointerWrapper(IntPtr nativeFunctionPointer) + { + m_nativeFunctionPointer = nativeFunctionPointer; + } + + IntPtr m_nativeFunctionPointer; + + public IntPtr NativeFunctionPointer + { + get { return m_nativeFunctionPointer; } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreRT.cs new file mode 100644 index 00000000000000..78567538db945a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreRT.cs @@ -0,0 +1,217 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Text; +using LibraryNameVariation = System.Runtime.Loader.LibraryNameVariation; + +namespace System.Runtime.InteropServices +{ + public static partial class NativeLibrary + { + internal static IntPtr LoadLibraryByName(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, bool throwOnError) + { + // First checks if a default dllImportSearchPathFlags was passed in, if so, use that value. + // Otherwise checks if the assembly has the DefaultDllImportSearchPathsAttribute attribute. + // If so, use that value. + + int searchPathFlags; + bool searchAssemblyDirectory; + if (searchPath.HasValue) + { + searchPathFlags = (int)(searchPath.Value & ~DllImportSearchPath.AssemblyDirectory); + searchAssemblyDirectory = (searchPath.Value & DllImportSearchPath.AssemblyDirectory) != 0; + } + else + { + GetDllImportSearchPathFlags(assembly, out searchPathFlags, out searchAssemblyDirectory); + } + + LoadLibErrorTracker errorTracker = default; + IntPtr ret = LoadBySearch(assembly, searchAssemblyDirectory, searchPathFlags, ref errorTracker, libraryName); + if (throwOnError && ret == IntPtr.Zero) + { + errorTracker.Throw(libraryName); + } + + return ret; + } + + internal static void GetDllImportSearchPathFlags(Assembly callingAssembly, out int searchPathFlags, out bool searchAssemblyDirectory) + { + var searchPath = DllImportSearchPath.AssemblyDirectory; + + foreach (CustomAttributeData cad in callingAssembly.CustomAttributes) + { + if (cad.AttributeType == typeof(DefaultDllImportSearchPathsAttribute)) + { + searchPath = (DllImportSearchPath)cad.ConstructorArguments[0].Value!; + } + } + + searchPathFlags = (int)(searchPath & ~DllImportSearchPath.AssemblyDirectory); + searchAssemblyDirectory = (searchPath & DllImportSearchPath.AssemblyDirectory) != 0; + } + + internal static IntPtr LoadBySearch(Assembly callingAssembly, bool searchAssemblyDirectory, int dllImportSearchPathFlags, ref LoadLibErrorTracker errorTracker, string libraryName) + { + IntPtr ret = IntPtr.Zero; + + int loadWithAlteredPathFlags = 0; + bool libNameIsRelativePath = !Path.IsPathFullyQualified(libraryName); + + // P/Invokes are often declared with variations on the actual library name. + // For example, it's common to leave off the extension/suffix of the library + // even if it has one, or to leave off a prefix like "lib" even if it has one + // (both of these are typically done to smooth over cross-platform differences). + // We try to dlopen with such variations on the original. + foreach (LibraryNameVariation libraryNameVariation in LibraryNameVariation.DetermineLibraryNameVariations(libraryName, libNameIsRelativePath)) + { + string currLibNameVariation = libraryNameVariation.Prefix + libraryName + libraryNameVariation.Suffix; + + if (!libNameIsRelativePath) + { + int flags = loadWithAlteredPathFlags; + if ((dllImportSearchPathFlags & (int)DllImportSearchPath.UseDllDirectoryForDependencies) != 0) + { + // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR is the only flag affecting absolute path. Don't OR the flags + // unconditionally as all absolute path P/Invokes could then lose LOAD_WITH_ALTERED_SEARCH_PATH. + flags |= dllImportSearchPathFlags; + } + + ret = LoadLibraryHelper(currLibNameVariation, flags, ref errorTracker); + if (ret != IntPtr.Zero) + { + return ret; + } + } + else if ((callingAssembly != null) && searchAssemblyDirectory) + { + // Try to load the module alongside the assembly where the PInvoke was declared. + // This only makes sense in dynamic scenarios (JIT/interpreter), so leaving this out for now. + } + + ret = LoadLibraryHelper(currLibNameVariation, dllImportSearchPathFlags, ref errorTracker); + if (ret != IntPtr.Zero) + { + return ret; + } + } + + return IntPtr.Zero; + } + + private static IntPtr LoadFromPath(string libraryName, bool throwOnError) + { + LoadLibErrorTracker errorTracker = default; + IntPtr ret = LoadLibraryHelper(libraryName, 0, ref errorTracker); + if (throwOnError && ret == IntPtr.Zero) + { + errorTracker.Throw(libraryName); + } + + return ret; + } + + private static IntPtr LoadLibraryHelper(string libraryName, int flags, ref LoadLibErrorTracker errorTracker) + { +#if TARGET_WINDOWS + IntPtr ret = Interop.Kernel32.LoadLibraryEx(libraryName, IntPtr.Zero, flags); + if (ret != IntPtr.Zero) + { + return ret; + } + + int lastError = Marshal.GetLastWin32Error(); + if (lastError != LoadLibErrorTracker.ERROR_INVALID_PARAMETER) + { + errorTracker.TrackErrorCode(lastError); + } + + return ret; +#else + IntPtr ret = IntPtr.Zero; + if (libraryName == null) + { + errorTracker.TrackErrorCode(LoadLibErrorTracker.ERROR_MOD_NOT_FOUND); + } + else if (libraryName == string.Empty) + { + errorTracker.TrackErrorCode(LoadLibErrorTracker.ERROR_INVALID_PARAMETER); + } + else + { + // TODO: FileDosToUnixPathA + ret = Interop.Sys.LoadLibrary(libraryName); + if (ret == IntPtr.Zero) + { + errorTracker.TrackErrorCode(LoadLibErrorTracker.ERROR_MOD_NOT_FOUND); + } + } + + return ret; +#endif + } + + private static void FreeLib(IntPtr handle) + { + Debug.Assert(handle != IntPtr.Zero); + +#if !TARGET_UNIX + bool result = Interop.Kernel32.FreeLibrary(handle); + if (!result) + throw new InvalidOperationException(); +#else + Interop.Sys.FreeLibrary(handle); +#endif + } + + private static unsafe IntPtr GetSymbol(IntPtr handle, string symbolName, bool throwOnError) + { + IntPtr ret; +#if !TARGET_UNIX + var symbolBytes = new byte[Encoding.UTF8.GetByteCount(symbolName) + 1]; + Encoding.UTF8.GetBytes(symbolName, symbolBytes); + fixed (byte* pSymbolBytes = symbolBytes) + { + ret = Interop.Kernel32.GetProcAddress(handle, pSymbolBytes); + } +#else + ret = Interop.Sys.GetProcAddress(handle, symbolName); +#endif + if (throwOnError && ret == IntPtr.Zero) + throw new EntryPointNotFoundException(SR.Format(SR.Arg_EntryPointNotFoundExceptionParameterizedNoLibrary, symbolName)); + + return ret; + } + + // TODO: copy the nice error logic from CoreCLR's LoadLibErrorTracker + // to get fine-grained error messages that take into account access denied, etc. + internal struct LoadLibErrorTracker + { + internal const int ERROR_INVALID_PARAMETER = 0x57; + internal const int ERROR_MOD_NOT_FOUND = 126; + internal const int ERROR_BAD_EXE_FORMAT = 193; + + private int _errorCode; + + public void Throw(string libraryName) + { + if (_errorCode == ERROR_BAD_EXE_FORMAT) + { + throw new BadImageFormatException(); + } + + throw new DllNotFoundException(SR.Format(SR.Arg_DllNotFoundExceptionParameterized, libraryName)); + } + + public void TrackErrorCode(int errorCode) + { + _errorCode = errorCode; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs new file mode 100644 index 00000000000000..dc36a6e5845fbc --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Security; + +namespace System.Runtime.InteropServices +{ + /// + /// This PInvokeMarshal class should provide full public Marshal + /// implementation for all things related to P/Invoke marshalling + /// + public partial class PInvokeMarshal + { + public static void SaveLastError() + { + t_lastError = Interop.Sys.GetErrNo(); + } + + public static void ClearLastError() + { + Interop.Sys.SetErrNo(0); + } + + #region String marshalling + + public static unsafe int ConvertMultiByteToWideChar(byte* multiByteStr, + int multiByteLen, + char* wideCharStr, + int wideCharLen) + { + return System.Text.Encoding.UTF8.GetChars(multiByteStr, multiByteLen, wideCharStr, wideCharLen); + } + + public static unsafe int ConvertWideCharToMultiByte(char* wideCharStr, + int wideCharLen, + byte* multiByteStr, + int multiByteLen, + bool bestFit, + bool throwOnUnmappableChar) + { + return System.Text.Encoding.UTF8.GetBytes(wideCharStr, wideCharLen, multiByteStr, multiByteLen); + } + + public static unsafe int ConvertWideCharToMultiByte(char* wideCharStr, + int wideCharLen, + byte* multiByteStr, + int multiByteLen) + { + return System.Text.Encoding.UTF8.GetBytes(wideCharStr, wideCharLen, multiByteStr, multiByteLen); + } + + public static unsafe int GetByteCount(char* wideCharStr, int wideCharLen) + { + return System.Text.Encoding.UTF8.GetByteCount(wideCharStr, wideCharLen); + } + + public static unsafe int GetCharCount(byte* multiByteStr, int multiByteLen) + { + return System.Text.Encoding.UTF8.GetCharCount(multiByteStr, multiByteLen); + } + #endregion + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Windows.cs new file mode 100644 index 00000000000000..a829d4d16e478a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Windows.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Security; + +namespace System.Runtime.InteropServices +{ + /// + /// This PInvokeMarshal class should provide full public Marshal + /// implementation for all things related to P/Invoke marshalling + /// + public partial class PInvokeMarshal + { + public static void SaveLastError() + { + t_lastError = Interop.Kernel32.GetLastError(); + } + + public static void ClearLastError() + { + Interop.Kernel32.SetLastError(0); + } + + #region String marshalling + + public static unsafe int ConvertMultiByteToWideChar(byte* buffer, int ansiLength, char* pWChar, int uniLength) + { + return Interop.Kernel32.MultiByteToWideChar(Interop.Kernel32.CP_ACP, 0, buffer, ansiLength, pWChar, uniLength); + } + + // Convert a UTF16 string to ANSI byte array + public static unsafe int ConvertWideCharToMultiByte(char* wideCharStr, int wideCharLen, byte* multiByteStr, int multiByteLen) + { + return Interop.Kernel32.WideCharToMultiByte(Interop.Kernel32.CP_ACP, + 0, + wideCharStr, + wideCharLen, + multiByteStr, + multiByteLen, + default(IntPtr), + default(IntPtr) + ); + } + + // Convert a UTF16 string to ANSI byte array using flags + public static unsafe int ConvertWideCharToMultiByte(char* wideCharStr, + int wideCharLen, + byte* multiByteStr, + int multiByteLen, + bool bestFit, + bool throwOnUnmappableChar) + { + uint flags = (bestFit ? 0 : Interop.Kernel32.WC_NO_BEST_FIT_CHARS); + int defaultCharUsed = 0; + int ret = Interop.Kernel32.WideCharToMultiByte(Interop.Kernel32.CP_ACP, + flags, + wideCharStr, + wideCharLen, + multiByteStr, + multiByteLen, + default(IntPtr), + throwOnUnmappableChar ? new System.IntPtr(&defaultCharUsed) : default(IntPtr) + ); + if (defaultCharUsed != 0) + { + throw new ArgumentException(SR.Arg_InteropMarshalUnmappableChar); + } + + return ret; + } + + // Return size in bytes required to convert a UTF16 string to byte array. + public static unsafe int GetByteCount(char* wStr, int wideStrLen) + { + return Interop.Kernel32.WideCharToMultiByte(Interop.Kernel32.CP_ACP, + 0, + wStr, + wideStrLen, + default(byte*), + 0, + default(IntPtr), + default(IntPtr) + ); + } + + // Return number of charaters encoded in native byte array lpMultiByteStr + public static unsafe int GetCharCount(byte* multiByteStr, int multiByteLen) + { + return Interop.Kernel32.MultiByteToWideChar(Interop.Kernel32.CP_ACP, 0, multiByteStr, multiByteLen, default(char*), 0); + } + #endregion + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs new file mode 100644 index 00000000000000..be104462e5aeb4 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs @@ -0,0 +1,596 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Security; +using Debug = System.Diagnostics.Debug; +using System.Collections.Generic; +using System.Threading; +using System.Runtime.CompilerServices; + +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerHelpers; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// + /// This PInvokeMarshal class should provide full public Marshal + /// implementation for all things related to P/Invoke marshalling + /// + [CLSCompliant(false)] + public partial class PInvokeMarshal + { + [ThreadStatic] + internal static int t_lastError; + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static int GetHRForException(Exception e) + { + if (e == null) + { + return HResults.S_OK; + } + + // @TODO: Setup IErrorInfo + return e.HResult; + } + + #region Delegate marshalling + + private static object s_thunkPoolHeap; + + /// + /// Return the stub to the pinvoke marshalling stub + /// + /// The delegate + public static IntPtr GetFunctionPointerForDelegate(Delegate del) + { + if (del == null) + return IntPtr.Zero; + + NativeFunctionPointerWrapper? fpWrapper = del.Target as NativeFunctionPointerWrapper; + if (fpWrapper != null) + { + // + // Marshalling a delegate created from native function pointer back into function pointer + // This is easy - just return the 'wrapped' native function pointer + // + return fpWrapper.NativeFunctionPointer; + } + else + { + // + // Marshalling a managed delegate created from managed code into a native function pointer + // + return GetPInvokeDelegates().GetValue(del, s_AllocateThunk ?? (s_AllocateThunk = AllocateThunk)).Thunk; + } + } + + /// + /// Used to lookup whether a delegate already has thunk allocated for it + /// + private static ConditionalWeakTable s_pInvokeDelegates; + private static ConditionalWeakTable.CreateValueCallback s_AllocateThunk; + + private static ConditionalWeakTable GetPInvokeDelegates() + { + // + // Create the dictionary on-demand + // + if (s_pInvokeDelegates == null) + { + Interlocked.CompareExchange( + ref s_pInvokeDelegates, + new ConditionalWeakTable(), + null + ); + } + + return s_pInvokeDelegates; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal unsafe struct ThunkContextData + { + public GCHandle Handle; // A weak GCHandle to the delegate + public IntPtr FunctionPtr; // Function pointer for open static delegates + } + + internal sealed class PInvokeDelegateThunk + { + public IntPtr Thunk; // Thunk pointer + public IntPtr ContextData; // ThunkContextData pointer which will be stored in the context slot of the thunk + + public PInvokeDelegateThunk(Delegate del) + { + + Thunk = RuntimeAugments.AllocateThunk(s_thunkPoolHeap); + Debug.Assert(Thunk != IntPtr.Zero); + + if (Thunk == IntPtr.Zero) + { + // We've either run out of memory, or failed to allocate a new thunk due to some other bug. Now we should fail fast + Environment.FailFast("Insufficient number of thunks."); + } + else + { + // + // Allocate unmanaged memory for GCHandle of delegate and function pointer of open static delegate + // We will store this pointer on the context slot of thunk data + // + ContextData = Marshal.AllocHGlobal(2 * IntPtr.Size); + unsafe + { + ThunkContextData* thunkData = (ThunkContextData*)ContextData; + + // allocate a weak GChandle for the delegate + thunkData->Handle = GCHandle.Alloc(del, GCHandleType.Weak); + + // if it is an open static delegate get the function pointer + thunkData->FunctionPtr = del.GetRawFunctionPointerForOpenStaticDelegate(); + } + } + } + + ~PInvokeDelegateThunk() + { + // Free the thunk + RuntimeAugments.FreeThunk(s_thunkPoolHeap, Thunk); + unsafe + { + if (ContextData != IntPtr.Zero) + { + // free the GCHandle + GCHandle handle = ((ThunkContextData*)ContextData)->Handle; + if (handle.IsAllocated) + { + handle.Free(); + } + + // Free the allocated context data memory + Marshal.FreeHGlobal(ContextData); + } + } + } + } + + private static PInvokeDelegateThunk AllocateThunk(Delegate del) + { + if (s_thunkPoolHeap == null) + { + // TODO: Free s_thunkPoolHeap if the thread lose the race + Interlocked.CompareExchange( + ref s_thunkPoolHeap, + RuntimeAugments.CreateThunksHeap(RuntimeImports.GetInteropCommonStubAddress()), + null + ); + Debug.Assert(s_thunkPoolHeap != null); + } + + var delegateThunk = new PInvokeDelegateThunk(del); + + // + // For open static delegates set target to ReverseOpenStaticDelegateStub which calls the static function pointer directly + // + bool openStaticDelegate = del.GetRawFunctionPointerForOpenStaticDelegate() != IntPtr.Zero; + + IntPtr pTarget = RuntimeAugments.InteropCallbacks.GetDelegateMarshallingStub(del.GetTypeHandle(), openStaticDelegate); + Debug.Assert(pTarget != IntPtr.Zero); + + RuntimeAugments.SetThunkData(s_thunkPoolHeap, delegateThunk.Thunk, delegateThunk.ContextData, pTarget); + + return delegateThunk; + } + + /// + /// Retrieve the corresponding P/invoke instance from the stub + /// + public static unsafe Delegate GetDelegateForFunctionPointer(IntPtr ptr, RuntimeTypeHandle delegateType) + { + if (ptr == IntPtr.Zero) + return null; + // + // First try to see if this is one of the thunks we've allocated when we marshal a managed + // delegate to native code + // s_thunkPoolHeap will be null if there isn't any managed delegate to native + // + IntPtr pContext; + IntPtr pTarget; + if (s_thunkPoolHeap != null && RuntimeAugments.TryGetThunkData(s_thunkPoolHeap, ptr, out pContext, out pTarget)) + { + GCHandle handle; + unsafe + { + // Pull out Handle from context + handle = ((ThunkContextData*)pContext)->Handle; + } + Delegate target = Unsafe.As(handle.Target); + + // + // The delegate might already been garbage collected + // User should use GC.KeepAlive or whatever ways necessary to keep the delegate alive + // until they are done with the native function pointer + // + if (target == null) + { + Environment.FailFast(SR.Delegate_GarbageCollected); + } + + return target; + } + + // + // Otherwise, the stub must be a pure native function pointer + // We need to create the delegate that points to the invoke method of a + // NativeFunctionPointerWrapper derived class + // + IntPtr pDelegateCreationStub = RuntimeAugments.InteropCallbacks.GetForwardDelegateCreationStub(delegateType); + Debug.Assert(pDelegateCreationStub != IntPtr.Zero); + + return ((delegate*)pDelegateCreationStub)(ptr); + } + + /// + /// Retrieves the function pointer for the current open static delegate that is being called + /// + public static IntPtr GetCurrentCalleeOpenStaticDelegateFunctionPointer() + { + // + // RH keeps track of the current thunk that is being called through a secret argument / thread + // statics. No matter how that's implemented, we get the current thunk which we can use for + // look up later + // + IntPtr pContext = RuntimeImports.GetCurrentInteropThunkContext(); + Debug.Assert(pContext != IntPtr.Zero); + + IntPtr fnPtr; + unsafe + { + // Pull out function pointer for open static delegate + fnPtr = ((ThunkContextData*)pContext)->FunctionPtr; + } + Debug.Assert(fnPtr != IntPtr.Zero); + + return fnPtr; + } + + /// + /// Retrieves the current delegate that is being called + /// + public static T GetCurrentCalleeDelegate() where T : class // constraint can't be System.Delegate + { + // + // RH keeps track of the current thunk that is being called through a secret argument / thread + // statics. No matter how that's implemented, we get the current thunk which we can use for + // look up later + // + IntPtr pContext = RuntimeImports.GetCurrentInteropThunkContext(); + + Debug.Assert(pContext != IntPtr.Zero); + + GCHandle handle; + unsafe + { + // Pull out Handle from context + handle = ((ThunkContextData*)pContext)->Handle; + + } + + T target = Unsafe.As(handle.Target); + + // + // The delegate might already been garbage collected + // User should use GC.KeepAlive or whatever ways necessary to keep the delegate alive + // until they are done with the native function pointer + // + if (target == null) + { + Environment.FailFast(SR.Delegate_GarbageCollected); + } + return target; + } + #endregion + + #region String marshalling + public static unsafe void StringBuilderToUnicodeString(System.Text.StringBuilder stringBuilder, ushort* destination) + { + int length = stringBuilder.Length; + stringBuilder.CopyTo(0, new Span((char*)destination, length), length); + destination[length] = '\0'; + } + + public static unsafe void UnicodeStringToStringBuilder(ushort* newBuffer, System.Text.StringBuilder stringBuilder) + { + stringBuilder.ReplaceBuffer((char*)newBuffer); + } + + public static unsafe void StringBuilderToAnsiString(System.Text.StringBuilder stringBuilder, byte* pNative, + bool bestFit, bool throwOnUnmappableChar) + { + int len; + + // Convert StringBuilder to UNICODE string + + // Optimize for the most common case. If there is only a single char[] in the StringBuilder, + // get it and convert it to ANSI + char[] buffer = stringBuilder.GetBuffer(out len); + + if (buffer != null) + { + fixed (char* pManaged = buffer) + { + StringToAnsiString(pManaged, len, pNative, /*terminateWithNull=*/true, bestFit, throwOnUnmappableChar); + } + } + else // Otherwise, convert StringBuilder to string and then convert to ANSI + { + string str = stringBuilder.ToString(); + + // Convert UNICODE string to ANSI string + fixed (char* pManaged = str) + { + StringToAnsiString(pManaged, str.Length, pNative, /*terminateWithNull=*/true, bestFit, throwOnUnmappableChar); + } + } + } + + public static unsafe void AnsiStringToStringBuilder(byte* newBuffer, System.Text.StringBuilder stringBuilder) + { + if (newBuffer == null) + throw new ArgumentNullException(nameof(newBuffer)); + + int lenAnsi; + int lenUnicode; + CalculateStringLength(newBuffer, out lenAnsi, out lenUnicode); + + if (lenUnicode > 0) + { + char[] buffer = new char[lenUnicode]; + fixed (char* pTemp = &buffer[0]) + { + ConvertMultiByteToWideChar(newBuffer, + lenAnsi, + pTemp, + lenUnicode); + } + stringBuilder.ReplaceBuffer(buffer); + } + else + { + stringBuilder.Clear(); + } + } + + /// + /// Convert ANSI string to unicode string, with option to free native memory. + /// + /// Input assumed to be zero terminated. Generates String.Empty for zero length string. + /// This version is more efficient than ConvertToUnicode in src\Interop\System\Runtime\InteropServices\Marshal.cs in that it can skip calling + /// MultiByteToWideChar for ASCII string, and it does not need another char[] buffer + public static unsafe string AnsiStringToString(byte* pchBuffer) + { + if (pchBuffer == null) + { + return null; + } + + int lenAnsi; + int lenUnicode; + CalculateStringLength(pchBuffer, out lenAnsi, out lenUnicode); + + string result = string.Empty; + + if (lenUnicode > 0) + { + result = string.FastAllocateString(lenUnicode); + + fixed (char* pTemp = result) + { + ConvertMultiByteToWideChar(pchBuffer, + lenAnsi, + pTemp, + lenUnicode); + } + } + + return result; + } + + /// + /// Convert UNICODE string to ANSI string. + /// + /// This version is more efficient than StringToHGlobalAnsi in Interop\System\Runtime\InteropServices\Marshal.cs in that + /// it could allocate single byte per character, instead of SystemMaxDBCSCharSize per char, and it can skip calling WideCharToMultiByte for ASCII string + public static unsafe byte* StringToAnsiString(string str, bool bestFit, bool throwOnUnmappableChar) + { + if (str != null) + { + int lenUnicode = str.Length; + + fixed (char* pManaged = str) + { + return StringToAnsiString(pManaged, lenUnicode, null, /*terminateWithNull=*/true, bestFit, throwOnUnmappableChar); + } + } + + return null; + } + + public static unsafe void WideCharArrayToAnsiCharArray(char[] managedArray, byte* pNative, bool bestFit, bool throwOnUnmappableChar) + { + // Do nothing if array is NULL. This matches desktop CLR behavior + if (managedArray == null) + return; + + // Desktop CLR crash (AV at runtime) - we can do better in .NET Native + if (pNative == null) + throw new ArgumentNullException(nameof(pNative)); + + int lenUnicode = managedArray.Length; + fixed (char* pManaged = managedArray) + { + StringToAnsiString(pManaged, lenUnicode, pNative, /*terminateWithNull=*/false, bestFit, throwOnUnmappableChar); + } + } + + /// + /// Convert ANSI ByVal byte array to UNICODE wide char array, best fit + /// + /// + /// * This version works with array instead to string, it means that the len must be provided and there will be NO NULL to + /// terminate the array. + /// * The buffer to the UNICODE wide char array must be allocated by the caller. + /// + /// Pointer to the ANSI byte array. Could NOT be null. + /// Wide char array that has already been allocated. + public static unsafe void AnsiCharArrayToWideCharArray(byte* pNative, char[] managedArray) + { + // Do nothing if native is NULL. This matches desktop CLR behavior + if (pNative == null) + return; + + // Desktop CLR crash (AV at runtime) - we can do better in .NET Native + if (managedArray == null) + throw new ArgumentNullException(nameof(managedArray)); + + // COMPAT: Use the managed array length as the maximum length of native buffer + // This obviously doesn't make sense but desktop CLR does that + int lenInBytes = managedArray.Length; + fixed (char* pManaged = managedArray) + { + ConvertMultiByteToWideChar(pNative, + lenInBytes, + pManaged, + lenInBytes); + } + } + + /// + /// Convert a single UNICODE wide char to a single ANSI byte. + /// + /// single UNICODE wide char value + /// Enable best-fit mapping behavior + /// Throw an exception on an unmappable Unicode character + public static unsafe byte WideCharToAnsiChar(char managedValue, bool bestFit, bool throwOnUnmappableChar) + { + // @TODO - we really shouldn't allocate one-byte arrays and then destroy it + byte* nativeArray = StringToAnsiString(&managedValue, 1, null, /*terminateWithNull=*/false, bestFit, throwOnUnmappableChar); + byte native = (*nativeArray); + Marshal.FreeCoTaskMem(new IntPtr(nativeArray)); + return native; + } + + /// + /// Convert a single ANSI byte value to a single UNICODE wide char value, best fit. + /// + /// Single ANSI byte value. + public static unsafe char AnsiCharToWideChar(byte nativeValue) + { + char ch; + ConvertMultiByteToWideChar(&nativeValue, 1, &ch, 1); + return ch; + } + + // c# string (UTF-16) to UTF-8 encoded byte array + internal static unsafe byte* StringToAnsiString(char* pManaged, int lenUnicode, byte* pNative, bool terminateWithNull, + bool bestFit, bool throwOnUnmappableChar) + { + bool allAscii = true; + + for (int i = 0; i < lenUnicode; i++) + { + if (pManaged[i] >= 128) + { + allAscii = false; + break; + } + } + + int length; + + if (allAscii) // If all ASCII, map one UNICODE character to one ANSI char + { + length = lenUnicode; + } + else // otherwise, let OS count number of ANSI chars + { + length = GetByteCount(pManaged, lenUnicode); + } + + if (pNative == null) + { + pNative = (byte*)Marshal.AllocCoTaskMem(checked(length + 1)); + } + if (allAscii) // ASCII conversion + { + byte* pDst = pNative; + char* pSrc = pManaged; + + while (lenUnicode > 0) + { + unchecked + { + *pDst++ = (byte)(*pSrc++); + lenUnicode--; + } + } + } + else // Let OS convert + { + ConvertWideCharToMultiByte(pManaged, + lenUnicode, + pNative, + length, + bestFit, + throwOnUnmappableChar); + } + + // Zero terminate + if (terminateWithNull) + *(pNative + length) = 0; + + return pNative; + } + + /// + /// This is a auxiliary function that counts the length of the ansi buffer and + /// estimate the length of the buffer in Unicode. It returns true if all bytes + /// in the buffer are ANSII. + /// + private static unsafe bool CalculateStringLength(byte* pchBuffer, out int ansiBufferLen, out int unicodeBufferLen) + { + ansiBufferLen = 0; + + bool allAscii = true; + + { + byte* p = pchBuffer; + byte b = *p++; + + while (b != 0) + { + if (b >= 128) + { + allAscii = false; + } + + ansiBufferLen++; + + b = *p++; + } + } + + if (allAscii) + { + unicodeBufferLen = ansiBufferLen; + } + else // If non ASCII, let OS calculate number of characters + { + unicodeBufferLen = GetCharCount(pchBuffer, ansiBufferLen); + } + return allAscii; + } + + #endregion + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.CoreRT.cs new file mode 100644 index 00000000000000..82d6fa476152bf --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.CoreRT.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Runtime.InteropServices +{ + public abstract partial class SafeHandle + { + // The handle cannot be closed until we are sure that no other objects might + // be using it. In the case of finalization, there may be other objects in + // the finalization queue that still hold a reference to this SafeHandle. + // So we can't assume that just because our finalizer is running, no other + // object will need to access this handle. + // + // The CLR solves this by having SafeHandle derive from CriticalFinalizerObject. + // This ensures that SafeHandle's finalizer will run only after all "normal" + // finalizers in the queue. But MRT doesn't support CriticalFinalizerObject, or + // any other explicit control of finalization order. + // + // For now, we'll hack this by not releasing the handle when our finalizer + // is called. Instead, we create a new DelayedFinalizer instance, whose + // finalizer will release the handle. Thus the handle won't be released in this + // finalization cycle, but should be released in the next. + // + // This has the effect of delaying cleanup for much longer than would have + // happened on the CLR. This also means that we may not close some handles + // at shutdown, since there may not be another finalization cycle to run + // the delayed finalizer. If either of these end up being a problem, we should + // consider adding more control over finalization order to MRT (or, better, + // turning over control of finalization ordering to System.Private.CoreLib). + + private sealed class DelayedFinalizer + { + private readonly SafeHandle _safeHandle; + + public DelayedFinalizer(SafeHandle safeHandle) + { + _safeHandle = safeHandle; + } + + ~DelayedFinalizer() + { + _safeHandle.Dispose(disposing: false); + } + } + + ~SafeHandle() + { + if (_fullyInitialized) + { + new DelayedFinalizer(this); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/UnsafeGCHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/UnsafeGCHandle.cs new file mode 100644 index 00000000000000..bea66b1dfe1c06 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/UnsafeGCHandle.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// + /// The unsafe version of the GCHandle structure. + /// + /// + /// Differences from the GCHandle structure: + /// + /// The constructor assumes the handle type is valid; no range check is performed. + /// The pinned flag is not stored in the _handle field. + /// The Target getter and setter assume the UnsafeGCHandle has been allocated. + /// No blittable check is performed when allocating a pinned UnsafeGCHandle or setting its target. + /// The GetRawTargetAddress method returns the raw address of the target (the pointer to + /// its m_pEEType field). + /// The Free method is not thread-safe and does not throw if the UnsafeGCHandle + /// has not been allocated or has been already freed. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public struct UnsafeGCHandle + { + // IMPORTANT: This must be kept in sync with the GCHandleType enum. + private const GCHandleType MaxHandleType = GCHandleType.Pinned; + + // The actual integer handle value that the EE uses internally. + private IntPtr _handle; + + // Allocate a handle storing the object and the type. + private UnsafeGCHandle(object value, GCHandleType type) + { + Debug.Assert((uint)type <= (uint)MaxHandleType, "Unexpected handle type"); + _handle = RuntimeImports.RhHandleAlloc(value, type); + } + + public static UnsafeGCHandle Alloc(object value, GCHandleType type) + { + return new UnsafeGCHandle(value, type); + } + + // Target property - allows getting / updating of the handle's referent. + public object Target + { + get + { + Debug.Assert(IsAllocated, "Handle is not initialized"); + return RuntimeImports.RhHandleGet(_handle); + } + + set + { + Debug.Assert(IsAllocated, "Handle is not initialized"); + RuntimeImports.RhHandleSet(_handle, value); + } + } + + // Frees a GC handle. This method is not thread-safe! + public void Free() + { + if (_handle != default(IntPtr)) + { + RuntimeImports.RhHandleFree(_handle); + } + } + + // Returns the raw address of the target assuming it is pinned. + public unsafe IntPtr GetRawTargetAddress() + { + return *(IntPtr*)_handle; + } + + // Determine whether this handle has been allocated or not. + public bool IsAllocated + { + get + { + return _handle != default(IntPtr); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.CoreRT.cs new file mode 100644 index 00000000000000..247b02e0c0feb5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.CoreRT.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.Intrinsics.X86 +{ + public abstract partial class X86Base + { + private static unsafe void __cpuidex(int* cpuInfo, int functionId, int subFunctionId) => RuntimeImports.RhCpuIdEx(cpuInfo, functionId, subFunctionId); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/JitInfo.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/JitInfo.CoreRT.cs new file mode 100644 index 00000000000000..fbf56b62521ad1 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/JitInfo.CoreRT.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime +{ + public static partial class JitInfo + { + public static long GetCompiledILBytes(bool currentThread = false) => 0; + + public static long GetCompiledMethodCount(bool currentThread = false) => 0; + + private static long GetCompilationTimeInTicks(bool currentThread = false) => 0; + } +} \ No newline at end of file diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreRT.cs new file mode 100644 index 00000000000000..095cf17930f3f5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreRT.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using System.Runtime.InteropServices; + +using Internal.Reflection.Augments; + +// This type is just stubbed out to be harmonious with CoreCLR +namespace System.Runtime.Loader +{ + public partial class AssemblyLoadContext + { + internal static Assembly[] GetLoadedAssemblies() => ReflectionAugments.ReflectionCoreCallbacks.GetLoadedAssemblies(); + + public Assembly LoadFromAssemblyName(AssemblyName assemblyName) + { + return Assembly.Load(assemblyName); + } + + private static IntPtr InitializeAssemblyLoadContext(IntPtr ptrAssemblyLoadContext, bool fRepresentsTPALoadContext, bool isCollectible) + { + return IntPtr.Zero; + } + + private static void PrepareForAssemblyLoadContextRelease(IntPtr ptrNativeAssemblyLoadContext, IntPtr ptrAssemblyLoadContextStrong) + { + } + + public static AssemblyLoadContext? GetLoadContext(Assembly assembly) + { + return Default; + } + + public void SetProfileOptimizationRoot(string directoryPath) + { + } + + public void StartProfileOptimization(string profile) + { + } + + private Assembly InternalLoadFromPath(string? assemblyPath, string? nativeImagePath) + { + // TODO: This is not passing down the AssemblyLoadContext, + // so it won't actually work properly when multiple assemblies with the same identity get loaded. + return ReflectionAugments.ReflectionCoreCallbacks.Load(assemblyPath); + } + + internal Assembly InternalLoad(byte[] arrAssembly, byte[] arrSymbols) + { + return ReflectionAugments.ReflectionCoreCallbacks.Load(arrAssembly, arrSymbols); + } + + private void ReferenceUnreferencedEvents() + { + // Dummy method to avoid CS0067 "Event is never used" warning. + // These are defined in the shared partition and it's not worth the ifdeffing. + _ = AssemblyLoad; + _ = ResourceResolve; + _ = _resolving; + _ = TypeResolve; + _ = AssemblyResolve; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeExportAttribute.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeExportAttribute.cs new file mode 100644 index 00000000000000..874f7d2f3f5673 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeExportAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class RuntimeExportAttribute : Attribute + { + public string EntryPoint; + + public RuntimeExportAttribute(string entry) + { + EntryPoint = entry; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImportAttribute.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImportAttribute.cs new file mode 100644 index 00000000000000..bd6c6caff5f1b5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImportAttribute.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime +{ + // Exposed in Internal.CompilerServices only + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] + public sealed class RuntimeImportAttribute : Attribute + { + public string DllName { get; } + public string EntryPoint { get; } + + public RuntimeImportAttribute(string entry) + { + EntryPoint = entry; + } + + public RuntimeImportAttribute(string dllName, string entry) + { + EntryPoint = entry; + DllName = dllName; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs new file mode 100644 index 00000000000000..8ba463d55405a9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -0,0 +1,1086 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Internal.Runtime; +using Internal.Runtime.CompilerServices; + +using CorElementType = System.Reflection.CorElementType; + +namespace System.Runtime +{ + // CONTRACT with Runtime + // This class lists all the static methods that the redhawk runtime exports to a class library + // These are not expected to change much but are needed by the class library to implement its functionality + // + // The contents of this file can be modified if needed by the class library + // E.g., the class and methods are marked internal assuming that only the base class library needs them + // but if a class library wants to factor differently (such as putting the GCHandle methods in an + // optional library, those methods can be moved to a different file/namespace/dll + [ReflectionBlocked] + public static class RuntimeImports + { + private const string RuntimeLibrary = "*"; + + [DllImport(RuntimeLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + [SuppressGCTransition] + internal static extern ulong PalGetTickCount64(); + + [DllImport(RuntimeLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr RhpGetCurrentThread(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpInitiateThreadAbort")] + internal static extern void RhpInitiateThreadAbort(IntPtr thread, Exception exception, bool doRudeAbort); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpCancelThreadAbort")] + internal static extern void RhpCancelThreadAbort(IntPtr thread); + + // + // calls to GC + // These methods are needed to implement System.GC like functionality (optional) + // + + // Force a garbage collection. + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhCollect")] + internal static extern void RhCollect(int generation, InternalGCCollectionMode mode); + + // Mark an object instance as already finalized. + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhSuppressFinalize")] + internal static extern void RhSuppressFinalize(object obj); + + internal static void RhReRegisterForFinalize(object obj) + { + if (!_RhReRegisterForFinalize(obj)) + throw new OutOfMemoryException(); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhReRegisterForFinalize")] + private static extern bool _RhReRegisterForFinalize(object obj); + + // Wait for all pending finalizers. This must be a p/invoke to avoid starving the GC. + [DllImport(RuntimeLibrary, ExactSpelling = true)] + private static extern void RhWaitForPendingFinalizers(int allowReentrantWait); + + // Temporary workaround to unblock shareable assembly bring-up - without shared interop, + // we must prevent RhWaitForPendingFinalizers from using marshaling because it would + // rewrite System.Private.CoreLib to reference the non-shareable interop assembly. With shared interop, + // we will be able to remove this helper method and change the DllImport above + // to directly accept a boolean parameter. + internal static void RhWaitForPendingFinalizers(bool allowReentrantWait) + { + RhWaitForPendingFinalizers(allowReentrantWait ? 1 : 0); + } + + [DllImport(RuntimeLibrary, ExactSpelling = true)] + internal static extern void RhInitializeFinalizerThread(); + + // Get maximum GC generation number. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetMaxGcGeneration")] + internal static extern int RhGetMaxGcGeneration(); + + // Get count of collections so far. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetGcCollectionCount")] + internal static extern int RhGetGcCollectionCount(int generation, bool getSpecialGCCount); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetGeneration")] + internal static extern int RhGetGeneration(object obj); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetGcLatencyMode")] + internal static extern GCLatencyMode RhGetGcLatencyMode(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhSetGcLatencyMode")] + internal static extern int RhSetGcLatencyMode(GCLatencyMode newLatencyMode); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhIsServerGc")] + internal static extern bool RhIsServerGc(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetGcTotalMemory")] + internal static extern long RhGetGcTotalMemory(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetLohCompactionMode")] + internal static extern int RhGetLohCompactionMode(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhSetLohCompactionMode")] + internal static extern void RhSetLohCompactionMode(int newLohCompactionMode); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetCurrentObjSize")] + internal static extern long RhGetCurrentObjSize(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetGCNow")] + internal static extern long RhGetGCNow(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetLastGCStartTime")] + internal static extern long RhGetLastGCStartTime(int generation); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetLastGCDuration")] + internal static extern long RhGetLastGCDuration(int generation); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpRegisterFrozenSegment")] + internal static extern IntPtr RhpRegisterFrozenSegment(IntPtr pSegmentStart, IntPtr length); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpUnregisterFrozenSegment")] + internal static extern void RhpUnregisterFrozenSegment(IntPtr pSegmentHandle); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhRegisterForFullGCNotification")] + internal static extern bool RhRegisterForFullGCNotification(int maxGenerationThreshold, int largeObjectHeapThreshold); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhWaitForFullGCApproach")] + internal static extern int RhWaitForFullGCApproach(int millisecondsTimeout); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhWaitForFullGCComplete")] + internal static extern int RhWaitForFullGCComplete(int millisecondsTimeout); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhCancelFullGCNotification")] + internal static extern bool RhCancelFullGCNotification(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhStartNoGCRegion")] + internal static extern int RhStartNoGCRegion(long totalSize, bool hasLohSize, long lohSize, bool disallowFullBlockingGC); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhEndNoGCRegion")] + internal static extern int RhEndNoGCRegion(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpShutdown")] + internal static extern void RhpShutdown(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetGCSegmentSize")] + internal static extern ulong RhGetGCSegmentSize(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetAllocatedBytesForCurrentThread")] + internal static extern long RhGetAllocatedBytesForCurrentThread(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetTotalAllocatedBytes")] + internal static extern long RhGetTotalAllocatedBytes(); + + [DllImport(RuntimeLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern long RhGetTotalAllocatedBytesPrecise(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetMemoryInfo")] + internal static extern void RhGetMemoryInfo(ref byte info, GCKind kind); + + [DllImport(RuntimeLibrary, ExactSpelling = true)] + internal static unsafe extern void RhAllocateNewArray(IntPtr pArrayEEType, uint numElements, uint flags, void* pResult); + + [DllImport(RuntimeLibrary, ExactSpelling = true)] + internal static unsafe extern void RhAllocateNewObject(IntPtr pEEType, uint flags, void* pResult); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhCompareObjectContentsAndPadding")] + internal static extern bool RhCompareObjectContentsAndPadding(object obj1, object obj2); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetProcessCpuCount")] + internal static extern int RhGetProcessCpuCount(); + + // + // calls for GCHandle. + // These methods are needed to implement GCHandle class like functionality (optional) + // + + // Allocate handle. + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpHandleAlloc")] + private static extern IntPtr RhpHandleAlloc(object value, GCHandleType type); + + internal static IntPtr RhHandleAlloc(object value, GCHandleType type) + { + IntPtr h = RhpHandleAlloc(value, type); + if (h == IntPtr.Zero) + throw new OutOfMemoryException(); + return h; + } + + // Allocate handle for dependent handle case where a secondary can be set at the same time. + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpHandleAllocDependent")] + private static extern IntPtr RhpHandleAllocDependent(object primary, object secondary); + + internal static IntPtr RhHandleAllocDependent(object primary, object secondary) + { + IntPtr h = RhpHandleAllocDependent(primary, secondary); + if (h == IntPtr.Zero) + throw new OutOfMemoryException(); + return h; + } + + // Free handle. + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhHandleFree")] + internal static extern void RhHandleFree(IntPtr handle); + + // Get object reference from handle. + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhHandleGet")] + private static extern object _RhHandleGet(IntPtr handle); + + internal static unsafe object RhHandleGet(IntPtr handle) + { +#if DEBUG + // The runtime performs additional checks in debug builds + return _RhHandleGet(handle); +#else + return Unsafe.As(ref *(IntPtr*)(nint)handle); +#endif + } + + // Get primary and secondary object references from dependent handle. + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhHandleGetDependent")] + internal static extern object RhHandleGetDependent(IntPtr handle, out object secondary); + + // Set object reference into handle. + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhHandleSet")] + internal static extern void RhHandleSet(IntPtr handle, object? value); + + // Set the secondary object reference into a dependent handle. + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhHandleSetDependentSecondary")] + internal static extern void RhHandleSetDependentSecondary(IntPtr handle, object secondary); + + // + // calls to runtime for type equality checks + // + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhTypeCast_AreTypesEquivalent")] + private static unsafe extern bool AreTypesEquivalent(MethodTable* pType1, MethodTable* pType2); + + internal static unsafe bool AreTypesEquivalent(EETypePtr pType1, EETypePtr pType2) + => AreTypesEquivalent(pType1.ToPointer(), pType2.ToPointer()); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhTypeCast_AreTypesAssignable")] + private static unsafe extern bool AreTypesAssignable(MethodTable* pSourceType, MethodTable* pTargetType); + + internal static unsafe bool AreTypesAssignable(EETypePtr pSourceType, EETypePtr pTargetType) + => AreTypesAssignable(pSourceType.ToPointer(), pTargetType.ToPointer()); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhTypeCast_CheckArrayStore")] + internal static extern void RhCheckArrayStore(object array, object? obj); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhTypeCast_IsInstanceOf")] + private static unsafe extern object IsInstanceOf(MethodTable* pTargetType, object obj); + + internal static unsafe object IsInstanceOf(EETypePtr pTargetType, object obj) + => IsInstanceOf(pTargetType.ToPointer(), obj); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhTypeCast_IsInstanceOfClass")] + private static unsafe extern object IsInstanceOfClass(MethodTable* pTargetType, object obj); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhTypeCast_IsInstanceOfInterface")] + internal static unsafe extern object IsInstanceOfInterface(MethodTable* pTargetType, object obj); + + internal static unsafe object IsInstanceOfInterface(EETypePtr pTargetType, object obj) + => IsInstanceOfInterface(pTargetType.ToPointer(), obj); + + // + // calls to runtime for allocation + // These calls are needed in types which cannot use "new" to allocate and need to do it manually + // + // calls to runtime for allocation + // + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhBoxAny")] + private static unsafe extern object RhBoxAny(ref byte pData, MethodTable* pEEType); + + internal static unsafe object RhBoxAny(ref byte pData, EETypePtr pEEType) + => RhBoxAny(ref pData, pEEType.ToPointer()); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhNewObject")] + private static unsafe extern object RhNewObject(MethodTable* pEEType); + + internal static unsafe object RhNewObject(EETypePtr pEEType) + => RhNewObject(pEEType.ToPointer()); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhNewArray")] + private static unsafe extern Array RhNewArray(MethodTable* pEEType, int length); + + internal static unsafe Array RhNewArray(EETypePtr pEEType, int length) + => RhNewArray(pEEType.ToPointer(), length); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhNewString")] + internal static unsafe extern string RhNewString(MethodTable* pEEType, int length); + + internal static unsafe string RhNewString(EETypePtr pEEType, int length) + => RhNewString(pEEType.ToPointer(), length); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhBox")] + private static extern unsafe object RhBox(MethodTable* pEEType, ref byte data); + + internal static unsafe object RhBox(EETypePtr pEEType, ref byte data) + => RhBox(pEEType.ToPointer(), ref data); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhUnbox")] + private static extern unsafe void RhUnbox(object? obj, ref byte data, MethodTable* pUnboxToEEType); + + internal static unsafe void RhUnbox(object? obj, ref byte data, EETypePtr pUnboxToEEType) + => RhUnbox(obj, ref data, pUnboxToEEType.ToPointer()); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhMemberwiseClone")] + internal static extern object RhMemberwiseClone(object obj); + + // Busy spin for the given number of iterations. + [DllImport(RuntimeLibrary, EntryPoint = "RhSpinWait", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + [SuppressGCTransition] + internal static extern void RhSpinWait(int iterations); + + // Yield the cpu to another thread ready to process, if one is available. + [DllImport(RuntimeLibrary, EntryPoint = "RhYield", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + private static extern int _RhYield(); + internal static bool RhYield() { return (_RhYield() != 0); } + + [DllImport(RuntimeLibrary, EntryPoint = "RhFlushProcessWriteBuffers", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + internal static extern void RhFlushProcessWriteBuffers(); + +#if !TARGET_UNIX + // Wait for any object to be signalled, in a way that's compatible with the CLR's behavior in an STA. + // ExactSpelling = 'true' to force MCG to resolve it to default + [DllImport(RuntimeLibrary, ExactSpelling = true)] + private static extern unsafe int RhCompatibleReentrantWaitAny(int alertable, int timeout, int count, IntPtr* handles); + + // Temporary workaround to unblock shareable assembly bring-up - without shared interop, + // we must prevent RhCompatibleReentrantWaitAny from using marshaling because it would + // rewrite System.Private.CoreLib to reference the non-shareable interop assembly. With shared interop, + // we will be able to remove this helper method and change the DllImport above + // to directly accept a boolean parameter and use the SetLastError = true modifier. + internal static unsafe int RhCompatibleReentrantWaitAny(bool alertable, int timeout, int count, IntPtr* handles) + { + return RhCompatibleReentrantWaitAny(alertable ? 1 : 0, timeout, count, handles); + } +#endif + + // + // MethodTable interrogation methods. + // + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetGCDescSize")] + internal static extern int RhGetGCDescSize(EETypePtr eeType); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhNewInterfaceDispatchCell")] + internal static extern unsafe IntPtr RhNewInterfaceDispatchCell(EETypePtr pEEType, int slotNumber); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhResolveDispatch")] + internal static extern IntPtr RhResolveDispatch(object pObject, EETypePtr pInterfaceType, ushort slot); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpResolveInterfaceMethod")] + internal static extern IntPtr RhpResolveInterfaceMethod(object pObject, IntPtr pCell); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhCreateThunksHeap")] + internal static extern object RhCreateThunksHeap(IntPtr commonStubAddress); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhAllocateThunk")] + internal static extern IntPtr RhAllocateThunk(object thunksHeap); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhFreeThunk")] + internal static extern void RhFreeThunk(object thunksHeap, IntPtr thunkAddress); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhSetThunkData")] + internal static extern void RhSetThunkData(object thunksHeap, IntPtr thunkAddress, IntPtr context, IntPtr target); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhTryGetThunkData")] + internal static extern bool RhTryGetThunkData(object thunksHeap, IntPtr thunkAddress, out IntPtr context, out IntPtr target); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetThunkSize")] + internal static extern int RhGetThunkSize(); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetThreadLocalStorageForDynamicType")] + internal static extern IntPtr RhGetThreadLocalStorageForDynamicType(int index, int tlsStorageSize, int numTlsCells); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhResolveDispatchOnType")] + internal static extern IntPtr RhResolveDispatchOnType(EETypePtr instanceType, EETypePtr interfaceType, ushort slot); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetRuntimeHelperForType")] + internal static extern unsafe IntPtr RhGetRuntimeHelperForType(EETypePtr pEEType, RuntimeHelperKind kind); + + // + // Support for GC and HandleTable callouts. + // + + internal enum GcRestrictedCalloutKind + { + StartCollection = 0, // Collection is about to begin + EndCollection = 1, // Collection has completed + AfterMarkPhase = 2, // All live objects are marked (not including ready for finalization objects), + // no handles have been cleared + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhRegisterGcCallout")] + internal static extern bool RhRegisterGcCallout(GcRestrictedCalloutKind eKind, IntPtr pCalloutMethod); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhUnregisterGcCallout")] + internal static extern void RhUnregisterGcCallout(GcRestrictedCalloutKind eKind, IntPtr pCalloutMethod); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhRegisterRefCountedHandleCallback")] + internal static extern bool RhRegisterRefCountedHandleCallback(IntPtr pCalloutMethod, EETypePtr pTypeFilter); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhUnregisterRefCountedHandleCallback")] + internal static extern void RhUnregisterRefCountedHandleCallback(IntPtr pCalloutMethod, EETypePtr pTypeFilter); + + // + // Blob support + // + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhFindBlob")] + private static extern unsafe bool RhFindBlob(ref TypeManagerHandle typeManagerHandle, uint blobId, byte** ppbBlob, uint* pcbBlob); + + internal static unsafe bool RhFindBlob(TypeManagerHandle typeManagerHandle, uint blobId, byte** ppbBlob, uint* pcbBlob) + { + return RhFindBlob(ref typeManagerHandle, blobId, ppbBlob, pcbBlob); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpCreateTypeManager")] + internal static extern unsafe TypeManagerHandle RhpCreateTypeManager(IntPtr osModule, IntPtr moduleHeader, IntPtr* pClasslibFunctions, int nClasslibFunctions); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpRegisterOsModule")] + internal static extern unsafe IntPtr RhpRegisterOsModule(IntPtr osModule); + + [RuntimeImport(RuntimeLibrary, "RhpGetModuleSection")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern IntPtr RhGetModuleSection(ref TypeManagerHandle module, ReadyToRunSectionType section, out int length); + + internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSectionType section, out int length) + { + return RhGetModuleSection(ref module, section, out length); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetLoadedOSModules")] + internal static extern uint RhGetLoadedOSModules(IntPtr[] resultArray); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetOSModuleFromPointer")] + internal static extern IntPtr RhGetOSModuleFromPointer(IntPtr pointerVal); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetModuleFromEEType")] + internal static extern TypeManagerHandle RhGetModuleFromEEType(IntPtr pEEType); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetOSModuleFromEEType")] + internal static extern IntPtr RhGetOSModuleFromEEType(IntPtr pEEType); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetThreadStaticStorageForModule")] + internal static unsafe extern object[] RhGetThreadStaticStorageForModule(int moduleIndex); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhSetThreadStaticStorageForModule")] + internal static unsafe extern bool RhSetThreadStaticStorageForModule(object[] storage, int moduleIndex); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhCurrentNativeThreadId")] + internal static unsafe extern IntPtr RhCurrentNativeThreadId(); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhCurrentOSThreadId")] + internal static unsafe extern ulong RhCurrentOSThreadId(); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport("*", "RhGetCurrentThunkContext")] + internal static extern IntPtr GetCurrentInteropThunkContext(); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport("*", "RhGetCommonStubAddress")] + internal static extern IntPtr GetInteropCommonStubAddress(); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetCodeTarget")] + public static extern IntPtr RhGetCodeTarget(IntPtr pCode); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetTargetOfUnboxingAndInstantiatingStub")] + public static extern IntPtr RhGetTargetOfUnboxingAndInstantiatingStub(IntPtr pCode); + + // + // EH helpers + // + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetModuleFileName")] +#if TARGET_UNIX + internal static extern unsafe int RhGetModuleFileName(IntPtr moduleHandle, out byte* moduleName); +#else + internal static extern unsafe int RhGetModuleFileName(IntPtr moduleHandle, out char* moduleName); +#endif + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetExceptionsForCurrentThread")] + internal static extern unsafe bool RhGetExceptionsForCurrentThread(Exception[] outputArray, out int writtenCountOut); + + // returns the previous value. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhSetErrorInfoBuffer")] + internal static extern unsafe void* RhSetErrorInfoBuffer(void* pNewBuffer); + + // + // StackTrace helper + // + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhFindMethodStartAddress")] + internal static extern unsafe IntPtr RhFindMethodStartAddress(IntPtr codeAddr); + + // Fetch a (managed) stack trace. Fills in the given array with "return address IPs" for the current + // thread's (managed) stack (array index 0 will be the caller of this method). The return value is + // the number of frames in the stack or a negative number (representing the required array size) if + // the passed-in buffer is too small. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetCurrentThreadStackTrace")] + internal static extern int RhGetCurrentThreadStackTrace(IntPtr[] outputBuffer); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetCurrentThreadStackBounds")] + internal static extern void RhGetCurrentThreadStackBounds(out IntPtr pStackLow, out IntPtr pStackHigh); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhSetThreadExitCallback")] + internal static extern unsafe void RhSetThreadExitCallback(delegate* unmanaged pCallback); + + // Functions involved in thunks from managed to managed functions (Universal transition transitions + // from an arbitrary method call into a defined function, and CallDescrWorker goes the other way. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetUniversalTransitionThunk")] + internal static extern IntPtr RhGetUniversalTransitionThunk(); + + // For Managed to Managed calls + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhCallDescrWorker")] + internal static extern void RhCallDescrWorker(IntPtr callDescr); + + // For Managed to Native calls + [DllImport(RuntimeLibrary, EntryPoint = "RhCallDescrWorker", ExactSpelling = true)] + internal static extern void RhCallDescrWorkerNative(IntPtr callDescr); + + // Moves memory from smem to dmem. Size must be a positive value. + // This copy uses an intrinsic to be safe for copying arbitrary bits of + // heap memory + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhBulkMoveWithWriteBarrier")] + internal static extern unsafe void RhBulkMoveWithWriteBarrier(ref byte dmem, ref byte smem, nuint size); + + // The GC conservative reporting descriptor is a special structure of data that the GC + // parses to determine whether there are specific regions of memory that it should not + // collect or move around. + // This can only be used to report memory regions on the current stack and the structure must itself + // be located on the stack. + // This structure is contractually required to be 4 pointers in size. All details about + // the contents are abstracted into the runtime + // To use, place one of these structures on the stack, and then pass it by ref to a function + // which will pin the byref to create a pinned interior pointer. + // Then, call RhInitializeConservativeReportingRegion to mark the region as conservatively reported. + // When done, call RhDisableConservativeReportingRegion to disable conservative reporting, or + // simply let it be pulled off the stack. + internal struct ConservativelyReportedRegionDesc + { + private IntPtr _ptr1; + private IntPtr _ptr2; + private IntPtr _ptr3; + private IntPtr _ptr4; + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhInitializeConservativeReportingRegion")] + internal static extern unsafe void RhInitializeConservativeReportingRegion(ConservativelyReportedRegionDesc* regionDesc, void* bufferBegin, int cbBuffer); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhDisableConservativeReportingRegion")] + internal static extern unsafe void RhDisableConservativeReportingRegion(ConservativelyReportedRegionDesc* regionDesc); + + // + // ETW helpers. + // + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpEtwExceptionThrown")] + internal static extern unsafe void RhpEtwExceptionThrown(char* exceptionTypeName, char* exceptionMessage, IntPtr faultingIP, long hresult); + + // + // Interlocked helpers + // + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpLockCmpXchg32")] + internal static extern int InterlockedCompareExchange(ref int location1, int value, int comparand); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpLockCmpXchg64")] + internal static extern long InterlockedCompareExchange(ref long location1, long value, long comparand); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpCheckedLockCmpXchg")] + internal static extern object InterlockedCompareExchange(ref object? location1, object? value, object? comparand); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpCheckedXchg")] + internal static extern object InterlockedExchange([NotNullIfNotNull("value")] ref object? location1, object? value); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpMemoryBarrier")] + internal static extern void MemoryBarrier(); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "fabs")] + internal static extern double fabs(double x); + + [Intrinsic] + internal static float fabsf(float x) + { + // fabsf is not a real export for some architectures + return (float)fabs(x); + } + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "acos")] + internal static extern double acos(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "acosf")] + internal static extern float acosf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "acosh")] + internal static extern double acosh(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "acoshf")] + internal static extern float acoshf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "asin")] + internal static extern double asin(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "asinf")] + internal static extern float asinf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "asinh")] + internal static extern double asinh(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "asinhf")] + internal static extern float asinhf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "atan")] + internal static extern double atan(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "atanf")] + internal static extern float atanf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "atan2")] + internal static extern double atan2(double y, double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "atan2f")] + internal static extern float atan2f(float y, float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "atanh")] + internal static extern double atanh(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "atanhf")] + internal static extern float atanhf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "cbrt")] + internal static extern double cbrt(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "cbrtf")] + internal static extern float cbrtf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "ceil")] + internal static extern double ceil(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "ceilf")] + internal static extern float ceilf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "cos")] + internal static extern double cos(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "cosf")] + internal static extern float cosf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "cosh")] + internal static extern double cosh(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "coshf")] + internal static extern float coshf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "exp")] + internal static extern double exp(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "expf")] + internal static extern float expf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "floor")] + internal static extern double floor(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "floorf")] + internal static extern float floorf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "log")] + internal static extern double log(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "logf")] + internal static extern float logf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "log2")] + internal static extern double log2(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "log2f")] + internal static extern float log2f(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "log10")] + internal static extern double log10(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "log10f")] + internal static extern float log10f(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "pow")] + internal static extern double pow(double x, double y); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "powf")] + internal static extern float powf(float x, float y); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "sin")] + internal static extern double sin(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "sinf")] + internal static extern float sinf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "sinh")] + internal static extern double sinh(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "sinhf")] + internal static extern float sinhf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "sqrt")] + internal static extern double sqrt(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "sqrtf")] + internal static extern float sqrtf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "tan")] + internal static extern double tan(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "tanf")] + internal static extern float tanf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "tanh")] + internal static extern double tanh(double x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "tanhf")] + internal static extern float tanhf(float x); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "fmod")] + internal static extern double fmod(double x, double y); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "fmodf")] + internal static extern float fmodf(float x, float y); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "fma")] + internal static extern double fma(double x, double y, double z); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "fmaf")] + internal static extern float fmaf(float x, float y, float z); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "modf")] + internal static extern unsafe double modf(double x, double* intptr); + + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "modff")] + internal static extern unsafe float modff(float x, float* intptr); + + [DllImport(RuntimeImports.RuntimeLibrary, ExactSpelling = true)] + internal static extern unsafe void* memmove(byte* dmem, byte* smem, nuint size); + + [DllImport(RuntimeImports.RuntimeLibrary, ExactSpelling = true)] + internal static extern unsafe void* memset(byte* mem, int value, nuint size); + +#if TARGET_X86 || TARGET_AMD64 + [DllImport(RuntimeLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe void RhCpuIdEx(int* cpuInfo, int functionId, int subFunctionId); +#endif + + internal static RhCorElementTypeInfo GetRhCorElementTypeInfo(CorElementType elementType) + { + return RhCorElementTypeInfo.GetRhCorElementTypeInfo(elementType); + } + + internal struct RhCorElementTypeInfo + { + public RhCorElementTypeInfo(ushort widenMask, bool isPrimitive = false) + { + RhCorElementTypeInfoFlags flags = RhCorElementTypeInfoFlags.IsValid; + if (isPrimitive) + flags |= RhCorElementTypeInfoFlags.IsPrimitive; + _flags = flags; + _widenMask = widenMask; + } + + public bool IsPrimitive + { + get + { + return 0 != (_flags & RhCorElementTypeInfoFlags.IsPrimitive); + } + } + + public bool IsFloat + { + get + { + return 0 != (_flags & RhCorElementTypeInfoFlags.IsFloat); + } + } + + // + // This is a port of InvokeUtil::CanPrimitiveWiden() in the desktop runtime. This is used by various apis such as Array.SetValue() + // and Delegate.DynamicInvoke() which allow value-preserving widenings from one primitive type to another. + // + public bool CanWidenTo(CorElementType targetElementType) + { + // Caller expected to ensure that both sides are primitive before calling us. + Debug.Assert(this.IsPrimitive); + Debug.Assert(GetRhCorElementTypeInfo(targetElementType).IsPrimitive); + + // Once we've asserted that the target is a primitive, we can also assert that it is >= ET_BOOLEAN. + Debug.Assert(targetElementType >= CorElementType.ELEMENT_TYPE_BOOLEAN); + byte targetElementTypeAsByte = (byte)targetElementType; + ushort mask = (ushort)(1 << targetElementTypeAsByte); // This is expected to overflow on larger ET_I and ET_U - this is ok and anticipated. + if (0 != (_widenMask & mask)) + return true; + return false; + } + + internal static RhCorElementTypeInfo GetRhCorElementTypeInfo(CorElementType elementType) + { + // The _lookupTable array only covers a subset of RhCorElementTypes, so we return a default + // info when someone asks for an elementType which does not have an entry in the table. + if ((int)elementType > s_lookupTable.Length) + return default(RhCorElementTypeInfo); + + return s_lookupTable[(int)elementType]; + } + + + private RhCorElementTypeInfoFlags _flags; + + [Flags] + private enum RhCorElementTypeInfoFlags : byte + { + IsValid = 0x01, // Set for all valid CorElementTypeInfo's + IsPrimitive = 0x02, // Is it a primitive type (as defined by TypeInfo.IsPrimitive) + IsFloat = 0x04, // Is it a floating point type + } + + private ushort _widenMask; + + private static RhCorElementTypeInfo[] s_lookupTable = new RhCorElementTypeInfo[] + { + // index = 0x0 + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 }, + // index = 0x1 + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 }, + // index = 0x2 = ELEMENT_TYPE_BOOLEAN (W = BOOL) + new RhCorElementTypeInfo { _widenMask = 0x0004, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive }, + // index = 0x3 = ELEMENT_TYPE_CHAR (W = U2, CHAR, I4, U4, I8, U8, R4, R8) (U2 == Char) + new RhCorElementTypeInfo { _widenMask = 0x3f88, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive }, + // index = 0x4 = ELEMENT_TYPE_I1 (W = I1, I2, I4, I8, R4, R8) + new RhCorElementTypeInfo { _widenMask = 0x3550, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive }, + // index = 0x5 = ELEMENT_TYPE_U1 (W = CHAR, U1, I2, U2, I4, U4, I8, U8, R4, R8) + new RhCorElementTypeInfo { _widenMask = 0x3FE8, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive }, + // index = 0x6 = ELEMENT_TYPE_I2 (W = I2, I4, I8, R4, R8) + new RhCorElementTypeInfo { _widenMask = 0x3540, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive }, + // index = 0x7 = ELEMENT_TYPE_U2 (W = U2, CHAR, I4, U4, I8, U8, R4, R8) + new RhCorElementTypeInfo { _widenMask = 0x3F88, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive }, + // index = 0x8 = ELEMENT_TYPE_I4 (W = I4, I8, R4, R8) + new RhCorElementTypeInfo { _widenMask = 0x3500, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive }, + // index = 0x9 = ELEMENT_TYPE_U4 (W = U4, I8, R4, R8) + new RhCorElementTypeInfo { _widenMask = 0x3E00, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive }, + // index = 0xa = ELEMENT_TYPE_I8 (W = I8, R4, R8) + new RhCorElementTypeInfo { _widenMask = 0x3400, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive }, + // index = 0xb = ELEMENT_TYPE_U8 (W = U8, R4, R8) + new RhCorElementTypeInfo { _widenMask = 0x3800, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive }, + // index = 0xc = ELEMENT_TYPE_R4 (W = R4, R8) + new RhCorElementTypeInfo { _widenMask = 0x3000, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive|RhCorElementTypeInfoFlags.IsFloat }, + // index = 0xd = ELEMENT_TYPE_R8 (W = R8) + new RhCorElementTypeInfo { _widenMask = 0x2000, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive|RhCorElementTypeInfoFlags.IsFloat }, + // index = 0xe + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 }, + // index = 0xf + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 }, + // index = 0x10 + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 }, + // index = 0x11 + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 }, + // index = 0x12 + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 }, + // index = 0x13 + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 }, + // index = 0x14 + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 }, + // index = 0x15 + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 }, + // index = 0x16 + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 }, + // index = 0x17 + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = 0 }, + // index = 0x18 = ELEMENT_TYPE_I + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive }, + // index = 0x19 = ELEMENT_TYPE_U + new RhCorElementTypeInfo { _widenMask = 0x0000, _flags = RhCorElementTypeInfoFlags.IsValid|RhCorElementTypeInfoFlags.IsPrimitive }, + }; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs new file mode 100644 index 00000000000000..6bdfbe7922d9c5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs @@ -0,0 +1,429 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime.Augments; +using System.Diagnostics; +using System.Threading; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Runtime +{ + [ReflectionBlocked] + public static class TypeLoaderExports + { + public static IntPtr GetThreadStaticsForDynamicType(int index) + { + IntPtr result = RuntimeImports.RhGetThreadLocalStorageForDynamicType(index, 0, 0); + if (result != IntPtr.Zero) + return result; + + int numTlsCells; + int tlsStorageSize = RuntimeAugments.TypeLoaderCallbacks.GetThreadStaticsSizeForDynamicType(index, out numTlsCells); + result = RuntimeImports.RhGetThreadLocalStorageForDynamicType(index, tlsStorageSize, numTlsCells); + + if (result == IntPtr.Zero) + throw new OutOfMemoryException(); + + return result; + } + + public static unsafe void ActivatorCreateInstanceAny(ref object ptrToData, IntPtr pEETypePtr) + { + EETypePtr pEEType = new EETypePtr(pEETypePtr); + + if (pEEType.IsValueType) + { + // Nothing else to do for value types. + return; + } + + // For reference types, we need to: + // 1- Allocate the new object + // 2- Call its default ctor + // 3- Update ptrToData to point to that newly allocated object + ptrToData = RuntimeImports.RhNewObject(pEEType); + + Entry entry = LookupInCache(s_cache, pEETypePtr, pEETypePtr); + if (entry == null) + { + entry = CacheMiss(pEETypePtr, pEETypePtr, + (IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) => + { + IntPtr result = RuntimeAugments.TypeLoaderCallbacks.TryGetDefaultConstructorForType(new RuntimeTypeHandle(new EETypePtr(context))); + if (result == IntPtr.Zero) + result = RuntimeAugments.GetFallbackDefaultConstructor(); + return result; + }); + } + RawCalliHelper.Call(entry.Result, ptrToData); + } + + // + // Generic lookup cache + // + + private class Entry + { + public IntPtr Context; + public IntPtr Signature; + public IntPtr Result; + public IntPtr AuxResult; + public Entry Next; + } + + // Initialize the cache eagerly to avoid null checks. + // Use array with just single element to make this pay-for-play. The actual cache will be allocated only + // once the lazy lookups are actually needed. + private static Entry[] s_cache; + + private static Lock s_lock; + private static GCHandle s_previousCache; + + internal static void Initialize() + { + s_cache = new Entry[1]; + } + + public static IntPtr GenericLookup(IntPtr context, IntPtr signature) + { + Entry entry = LookupInCache(s_cache, context, signature); + if (entry == null) + { + entry = CacheMiss(context, signature); + } + return entry.Result; + } + + public static void GenericLookupAndCallCtor(object arg, IntPtr context, IntPtr signature) + { + Entry entry = LookupInCache(s_cache, context, signature); + if (entry == null) + { + entry = CacheMiss(context, signature); + } + RawCalliHelper.Call(entry.Result, arg); + } + + public static object GenericLookupAndAllocObject(IntPtr context, IntPtr signature) + { + Entry entry = LookupInCache(s_cache, context, signature); + if (entry == null) + { + entry = CacheMiss(context, signature); + } + return RawCalliHelper.Call(entry.Result, entry.AuxResult); + } + + public static object GenericLookupAndAllocArray(IntPtr context, IntPtr arg, IntPtr signature) + { + Entry entry = LookupInCache(s_cache, context, signature); + if (entry == null) + { + entry = CacheMiss(context, signature); + } + return RawCalliHelper.Call(entry.Result, entry.AuxResult, arg); + } + + public static void GenericLookupAndCheckArrayElemType(IntPtr context, object arg, IntPtr signature) + { + Entry entry = LookupInCache(s_cache, context, signature); + if (entry == null) + { + entry = CacheMiss(context, signature); + } + RawCalliHelper.Call(entry.Result, entry.AuxResult, arg); + } + + public static object GenericLookupAndCast(object arg, IntPtr context, IntPtr signature) + { + Entry entry = LookupInCache(s_cache, context, signature); + if (entry == null) + { + entry = CacheMiss(context, signature); + } + return RawCalliHelper.Call(entry.Result, arg, entry.AuxResult); + } + + public static IntPtr UpdateTypeFloatingDictionary(IntPtr eetypePtr, IntPtr dictionaryPtr) + { + // No caching needed. Update is in-place, and happens once per dictionary + return RuntimeAugments.TypeLoaderCallbacks.UpdateFloatingDictionary(eetypePtr, dictionaryPtr); + } + + public static IntPtr UpdateMethodFloatingDictionary(IntPtr dictionaryPtr) + { + // No caching needed. Update is in-place, and happens once per dictionary + return RuntimeAugments.TypeLoaderCallbacks.UpdateFloatingDictionary(dictionaryPtr, dictionaryPtr); + } + + public static unsafe IntPtr GetDelegateThunk(object delegateObj, int whichThunk) + { + Entry entry = LookupInCache(s_cache, (IntPtr)delegateObj.MethodTable, new IntPtr(whichThunk)); + if (entry == null) + { + entry = CacheMiss((IntPtr)delegateObj.MethodTable, new IntPtr(whichThunk), + (IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) + => RuntimeAugments.TypeLoaderCallbacks.GetDelegateThunk((Delegate)contextObject, (int)signature), + delegateObj); + } + return entry.Result; + } + + public static unsafe IntPtr GVMLookupForSlot(object obj, RuntimeMethodHandle slot) + { + Entry entry = LookupInCache(s_cache, (IntPtr)obj.MethodTable, *(IntPtr*)&slot); + if (entry == null) + { + entry = CacheMiss((IntPtr)obj.MethodTable, *(IntPtr*)&slot, + (IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) + => Internal.Runtime.CompilerServices.GenericVirtualMethodSupport.GVMLookupForSlot(new RuntimeTypeHandle(new EETypePtr(context)), *(RuntimeMethodHandle*)&signature)); + } + return entry.Result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe IntPtr OpenInstanceMethodLookup(IntPtr openResolver, object obj) + { + Entry entry = LookupInCache(s_cache, (IntPtr)obj.MethodTable, openResolver); + if (entry == null) + { + entry = CacheMiss((IntPtr)obj.MethodTable, openResolver, + (IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) + => Internal.Runtime.CompilerServices.OpenMethodResolver.ResolveMethodWorker(signature, contextObject), + obj); + } + return entry.Result; + } + + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + private static Entry LookupInCache(Entry[] cache, IntPtr context, IntPtr signature) + { + int key = ((context.GetHashCode() >> 4) ^ signature.GetHashCode()) & (cache.Length - 1); + Entry entry = cache[key]; + while (entry != null) + { + if (entry.Context == context && entry.Signature == signature) + break; + entry = entry.Next; + } + return entry; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static IntPtr RuntimeCacheLookupInCache(IntPtr context, IntPtr signature, RuntimeObjectFactory factory, object contextObject, out IntPtr auxResult) + { + Entry entry = LookupInCache(s_cache, context, signature); + if (entry == null) + { + entry = CacheMiss(context, signature, factory, contextObject); + } + auxResult = entry.AuxResult; + return entry.Result; + } + + private static Entry CacheMiss(IntPtr ctx, IntPtr sig) + { + return CacheMiss(ctx, sig, + (IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) => + RuntimeAugments.TypeLoaderCallbacks.GenericLookupFromContextAndSignature(context, signature, out auxResult) + ); + } + + private static unsafe Entry CacheMiss(IntPtr context, IntPtr signature, RuntimeObjectFactory factory, object contextObject = null) + { + IntPtr result = IntPtr.Zero, auxResult = IntPtr.Zero; + bool previouslyCached = false; + + // + // Try to find the entry in the previous version of the cache that is kept alive by weak reference + // + if (s_previousCache.IsAllocated) + { + Entry[]? previousCache = (Entry[]?)s_previousCache.Target; + if (previousCache != null) + { + Entry previousEntry = LookupInCache(previousCache, context, signature); + if (previousEntry != null) + { + result = previousEntry.Result; + auxResult = previousEntry.AuxResult; + previouslyCached = true; + } + } + } + + // + // Call into the type loader to compute the target + // + if (!previouslyCached) + { + result = factory(context, signature, contextObject, ref auxResult); + } + + // + // Update the cache under the lock + // + if (s_lock == null) + Interlocked.CompareExchange(ref s_lock, new Lock(), null); + + s_lock.Acquire(); + try + { + // Avoid duplicate entries + Entry existingEntry = LookupInCache(s_cache, context, signature); + if (existingEntry != null) + return existingEntry; + + // Resize cache as necessary + Entry[] cache = ResizeCacheForNewEntryAsNecessary(); + + int key = ((context.GetHashCode() >> 4) ^ signature.GetHashCode()) & (cache.Length - 1); + + Entry newEntry = new Entry() { Context = context, Signature = signature, Result = result, AuxResult = auxResult, Next = cache[key] }; + cache[key] = newEntry; + return newEntry; + } + finally + { + s_lock.Release(); + } + } + + // + // Parameters and state used by generic lookup cache resizing algorithm + // + + private const int InitialCacheSize = 128; // MUST BE A POWER OF TWO + private const int DefaultCacheSize = 1024; + private const int MaximumCacheSize = 128 * 1024; + + private static long s_tickCountOfLastOverflow; + private static int s_entries; + private static bool s_roundRobinFlushing; + + private static Entry[] ResizeCacheForNewEntryAsNecessary() + { + Entry[] cache = s_cache; + + if (cache.Length < InitialCacheSize) + { + // Start with small cache size so that the cache entries used by startup one-time only initialization will get flushed soon + return s_cache = new Entry[InitialCacheSize]; + } + + int entries = s_entries++; + + // If the cache has spare space, we are done + if (2 * entries < cache.Length) + { + if (s_roundRobinFlushing) + { + cache[2 * entries] = null; + cache[2 * entries + 1] = null; + } + return cache; + } + + // + // Now, we have cache that is overflowing with the stuff. We need to decide whether to resize it or start flushing the old entries instead + // + + // Start over counting the entries + s_entries = 0; + + // See how long it has been since the last time the cache was overflowing + long tickCount = Environment.TickCount64; + long tickCountSinceLastOverflow = tickCount - s_tickCountOfLastOverflow; + s_tickCountOfLastOverflow = tickCount; + + bool shrinkCache = false; + bool growCache = false; + + if (cache.Length < DefaultCacheSize) + { + // If the cache have not reached the default size, just grow it without thinking about it much + growCache = true; + } + else + { + if (tickCountSinceLastOverflow < cache.Length / 128) + { + // If the fill rate of the cache is faster than ~0.01ms per entry, grow it + if (cache.Length < MaximumCacheSize) + growCache = true; + } + else + if (tickCountSinceLastOverflow > cache.Length * 16) + { + // If the fill rate of the cache is slower than 16ms per entry, shrink it + if (cache.Length > DefaultCacheSize) + shrinkCache = true; + } + // Otherwise, keep the current size and just keep flushing the entries round robin + } + + if (growCache || shrinkCache) + { + s_roundRobinFlushing = false; + + // Keep the reference to the old cache in a weak handle. We will try to use to avoid + // hitting the type loader until GC collects it. + if (s_previousCache.IsAllocated) + { + s_previousCache.Target = cache; + } + else + { + s_previousCache = GCHandle.Alloc(cache, GCHandleType.Weak); + } + + return s_cache = new Entry[shrinkCache ? (cache.Length / 2) : (cache.Length * 2)]; + } + else + { + s_roundRobinFlushing = true; + return cache; + } + } + } + + [ReflectionBlocked] + public delegate IntPtr RuntimeObjectFactory(IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult); + + internal static unsafe class RawCalliHelper + { + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static void Call(System.IntPtr pfn, ref byte data) + => ((delegate*)pfn)(ref data); + + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static T Call(System.IntPtr pfn, IntPtr arg) + => ((delegate*)pfn)(arg); + + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static void Call(System.IntPtr pfn, object arg) + => ((delegate*)pfn)(arg); + + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static T Call(System.IntPtr pfn, IntPtr arg1, IntPtr arg2) + => ((delegate*)pfn)(arg1, arg2); + + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static T Call(System.IntPtr pfn, IntPtr arg1, IntPtr arg2, object arg3, out IntPtr arg4) + => ((delegate*)pfn)(arg1, arg2, arg3, out arg4); + + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static void Call(System.IntPtr pfn, IntPtr arg1, object arg2) + => ((delegate*)pfn)(arg1, arg2); + + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static T Call(System.IntPtr pfn, object arg1, IntPtr arg2) + => ((delegate*)pfn)(arg1, arg2); + + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static T Call(IntPtr pfn, string[] arg0) + => ((delegate*)pfn)(arg0); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeArgumentHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeArgumentHandle.cs new file mode 100644 index 00000000000000..7b3f186ffe8a25 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeArgumentHandle.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System +{ + [StructLayout(LayoutKind.Sequential)] + public ref struct RuntimeArgumentHandle + { + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs new file mode 100644 index 00000000000000..c46c6925670111 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -0,0 +1,681 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime; +using System.Runtime.CompilerServices; + +using Internal.DeveloperExperience; +using Internal.Runtime.Augments; + +namespace System +{ + internal static class PreallocatedOutOfMemoryException + { + public static OutOfMemoryException Instance { get; private set; } + + // Eagerly preallocate instance of out of memory exception to avoid infinite recursion once we run out of memory + internal static void Initialize() + { + Instance = new OutOfMemoryException(message: null); // Cannot call the nullary constructor as that triggers non-trivial resource manager logic. + } + } + + [ReflectionBlocked] + public class RuntimeExceptionHelpers + { + //------------------------------------------------------------------------------------------------------------ + // @TODO: this function is related to throwing exceptions out of Rtm. If we did not have to throw + // out of Rtm, then we would note have to have the code below to get a classlib exception object given + // an exception id, or the special functions to back up the MDIL THROW_* instructions, or the allocation + // failure helper. If we could move to a world where we never throw out of Rtm, perhaps by moving parts + // of Rtm that do need to throw out to Bartok- or Binder-generated functions, then we could remove all of this. + //------------------------------------------------------------------------------------------------------------ + + [ThreadStatic] + private static bool t_allocatingOutOfMemoryException; + + // This is the classlib-provided "get exception" function that will be invoked whenever the runtime + // needs to throw an exception back to a method in a non-runtime module. The classlib is expected + // to convert every code in the ExceptionIDs enum to an exception object. + [RuntimeExport("GetRuntimeException")] + public static Exception? GetRuntimeException(ExceptionIDs id) + { + if (!SafeToPerformRichExceptionSupport) + return null; + + // This method is called by the runtime's EH dispatch code and is not allowed to leak exceptions + // back into the dispatcher. + try + { + // @TODO: this function should return pre-allocated exception objects, either frozen in the image + // or preallocated during DllMain(). In particular, this function will be called when out of memory, + // and failure to create an exception will result in infinite recursion and therefore a stack overflow. + switch (id) + { + case ExceptionIDs.OutOfMemory: + Exception outOfMemoryException = PreallocatedOutOfMemoryException.Instance; + + // If possible, try to allocate proper out-of-memory exception with error message and stack trace + if (!t_allocatingOutOfMemoryException) + { + t_allocatingOutOfMemoryException = true; + try + { + outOfMemoryException = new OutOfMemoryException(); + } + catch + { + } + t_allocatingOutOfMemoryException = false; + } + + return outOfMemoryException; + + case ExceptionIDs.Arithmetic: + return new ArithmeticException(); + + case ExceptionIDs.ArrayTypeMismatch: + return new ArrayTypeMismatchException(); + + case ExceptionIDs.DivideByZero: + return new DivideByZeroException(); + + case ExceptionIDs.IndexOutOfRange: + return new IndexOutOfRangeException(); + + case ExceptionIDs.InvalidCast: + return new InvalidCastException(); + + case ExceptionIDs.Overflow: + return new OverflowException(); + + case ExceptionIDs.NullReference: + return new NullReferenceException(); + + case ExceptionIDs.AccessViolation: + FailFast("Access Violation: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. The application will be terminated since this platform does not support throwing an AccessViolationException."); + return null; + + case ExceptionIDs.DataMisaligned: + return new DataMisalignedException(); + + case ExceptionIDs.EntrypointNotFound: + return new EntryPointNotFoundException(); + + case ExceptionIDs.AmbiguousImplementation: + return new AmbiguousImplementationException(); + + default: + FailFast("The runtime requires an exception for a case that this class library does not understand."); + return null; + } + } + catch + { + return null; // returning null will cause the runtime to FailFast via the class library. + } + } + + public enum RhFailFastReason + { + Unknown = 0, + InternalError = 1, // "Runtime internal error" + UnhandledException_ExceptionDispatchNotAllowed = 2, // "Unhandled exception: no handler found before escaping a finally clause or other fail-fast scope." + UnhandledException_CallerDidNotHandle = 3, // "Unhandled exception: no handler found in calling method." + ClassLibDidNotTranslateExceptionID = 4, // "Unable to translate failure into a classlib-specific exception object." + IllegalUnmanagedCallersOnlyEntry = 5, // "Invalid Program: attempted to call a UnmanagedCallersOnly method from runtime-typesafe code." + PN_UnhandledException = 6, // ProjectN: "Unhandled exception: a managed exception was not handled before reaching unmanaged code" + PN_UnhandledExceptionFromPInvoke = 7, // ProjectN: "Unhandled exception: an unmanaged exception was thrown out of a managed-to-native transition." + Max + } + + private static string GetStringForFailFastReason(RhFailFastReason reason) + { + switch (reason) + { + case RhFailFastReason.InternalError: + return "Runtime internal error"; + case RhFailFastReason.UnhandledException_ExceptionDispatchNotAllowed: + return "Unhandled exception: no handler found before escaping a finally clause or other fail-fast scope."; + case RhFailFastReason.UnhandledException_CallerDidNotHandle: + return "Unhandled exception: no handler found in calling method."; + case RhFailFastReason.ClassLibDidNotTranslateExceptionID: + return "Unable to translate failure into a classlib-specific exception object."; + case RhFailFastReason.IllegalUnmanagedCallersOnlyEntry: + return "Invalid Program: attempted to call a UnmanagedCallersOnly method from runtime-typesafe code."; + case RhFailFastReason.PN_UnhandledException: + return "Unhandled exception: a managed exception was not handled before reaching unmanaged code."; + case RhFailFastReason.PN_UnhandledExceptionFromPInvoke: + return "Unhandled exception: an unmanaged exception was thrown out of a managed-to-native transition."; + default: + return "Unknown reason."; + } + } + + + [DoesNotReturn] + public static void FailFast(string message) + { + FailFast(message, null, RhFailFastReason.Unknown, IntPtr.Zero, IntPtr.Zero); + } + + [DoesNotReturn] + public static unsafe void FailFast(string message, Exception? exception) + { + FailFast(message, exception, RhFailFastReason.Unknown, IntPtr.Zero, IntPtr.Zero); + } + + // Used to report exceptions that *logically* go unhandled in the Fx code. For example, an + // exception that escapes from a ThreadPool workitem, or from a void-returning async method. + public static void ReportUnhandledException(Exception exception) + { +#if FEATURE_DUMP_DEBUGGING + // ReportUnhandledError will also call this in APPX scenarios, + // but WinRT can failfast before we get another chance + // (in APPX scenarios, this one will get overwritten by the one with the CCW pointer) + GenerateExceptionInformationForDump(exception, IntPtr.Zero); +#endif + +#if ENABLE_WINRT + // If possible report the exception to GEH, if not fail fast. + WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks; + if (callbacks == null || !callbacks.ReportUnhandledError(exception)) + FailFast(GetStringForFailFastReason(RhFailFastReason.PN_UnhandledException), exception); +#else + FailFast(GetStringForFailFastReason(RhFailFastReason.PN_UnhandledException), exception); +#endif + } + + // This is the classlib-provided fail-fast function that will be invoked whenever the runtime + // needs to cause the process to exit. It is the classlib's opprotunity to customize the + // termination behavior in whatever way necessary. + [RuntimeExport("FailFast")] + public static void RuntimeFailFast(RhFailFastReason reason, Exception? exception, IntPtr pExAddress, IntPtr pExContext) + { + if (!SafeToPerformRichExceptionSupport) + return; + + // This method is called by the runtime's EH dispatch code and is not allowed to leak exceptions + // back into the dispatcher. + try + { + // Avoid complex processing and allocations if we are already in failfast or recursive out of memory. + // We do not set InFailFast.Value here, because we want rich diagnostics in the FailFast + // call below and reentrancy is not possible for this method (all exceptions are ignored). + bool minimalFailFast = InFailFast.Value || (exception == PreallocatedOutOfMemoryException.Instance); + string failFastMessage = ""; + + if (!minimalFailFast) + { + if ((reason == RhFailFastReason.PN_UnhandledException) && (exception != null)) + { + Debug.WriteLine("Unhandled Exception: " + exception.ToString()); + } + + failFastMessage = string.Format("Runtime-generated FailFast: ({0}): {1}{2}", + reason.ToString(), // Explicit call to ToString() to avoid MissingMetadataException inside String.Format() + GetStringForFailFastReason(reason), + exception != null ? " [exception object available]" : ""); + } + + FailFast(failFastMessage, exception, reason, pExAddress, pExContext); + } + catch + { + // Returning from this callback will cause the runtime to FailFast without involving the class + // library. + } + } + + [DoesNotReturn] + internal static void FailFast(string message, Exception? exception, RhFailFastReason reason, IntPtr pExAddress, IntPtr pExContext) + { + // If this a recursive call to FailFast, avoid all unnecessary and complex activity the second time around to avoid the recursion + // that got us here the first time (Some judgement is required as to what activity is "unnecessary and complex".) + bool minimalFailFast = InFailFast.Value || (exception == PreallocatedOutOfMemoryException.Instance); + InFailFast.Value = true; + + if (!minimalFailFast) + { + string prefix; + string outputMessage; + if (exception != null) + { + prefix = "Unhandled Exception: "; + outputMessage = exception.ToString(); + } + else + { + prefix = "Process terminated. "; + outputMessage = message; + } + + Internal.Console.Error.Write(prefix); + if (outputMessage != null) + Internal.Console.Error.Write(outputMessage); + Internal.Console.Error.Write(Environment.NewLine); + +#if FEATURE_DUMP_DEBUGGING + GenerateExceptionInformationForDump(exception, IntPtr.Zero); +#endif + } + +#if TARGET_WINDOWS + uint errorCode = 0x80004005; // E_FAIL + // To help enable testing to bucket the failures we choose one of the following as errorCode: + // * hashcode of EETypePtr if it is an unhandled managed exception + // * HRESULT, if available + // * RhFailFastReason, if it is one of the known reasons + if (exception != null) + { + if (reason == RhFailFastReason.PN_UnhandledException) + errorCode = (uint)(exception.EETypePtr.GetHashCode()); + else if (exception.HResult != 0) + errorCode = (uint)exception.HResult; + } + else if (reason != RhFailFastReason.Unknown) + { + errorCode = (uint)reason + 0x1000; // Add something to avoid common low level exit codes + } + + Interop.Kernel32.RaiseFailFastException(errorCode, pExAddress, pExContext); +#else + Interop.Sys.Abort(); +#endif + } + + // Use a nested class to avoid running the class constructor of the outer class when + // accessing this flag. + private static class InFailFast + { + // This boolean is used to stop runaway FailFast recursions. Though this is technically a concurrently set field, it only gets set during + // fatal process shutdowns and it's only purpose is a reasonable-case effort to make a bad situation a little less bad. + // Trying to use locks or other concurrent access apis would actually defeat the purpose of making FailFast as robust as possible. + public static bool Value; + } + + // This returns "true" once enough of the framework has been initialized to safely perform operations + // such as filling in the stack frame and generating diagnostic support. + public static bool SafeToPerformRichExceptionSupport + { + get + { + // Reflection needs to work as the exception code calls GetType() and GetType().ToString() + if (RuntimeAugments.CallbacksIfAvailable == null) + return false; + return true; + } + } + +#if FEATURE_DUMP_DEBUGGING + +#pragma warning disable 414 // field is assigned, but never used -- This is because C# doesn't realize that we + // copy the field into a buffer. + /// + /// This is the header that describes our 'error report' buffer to the minidump auxiliary provider. + /// Its format is know to that system-wide DLL, so do not change it. The remainder of the buffer is + /// opaque to the minidump auxiliary provider, so it'll have its own format that is more easily + /// changed. + /// + [StructLayout(LayoutKind.Sequential)] + private struct ERROR_REPORT_BUFFER_HEADER + { + private int _headerSignature; + private int _bufferByteCount; + + public void WriteHeader(int cbBuffer) + { + _headerSignature = 0x31304244; // 'DB01' + _bufferByteCount = cbBuffer; + } + } + + /// + /// This header describes the contents of the serialized error report to DAC, which can deserialize it + /// from a dump file or live debugging session. This format is easier to change than the + /// ERROR_REPORT_BUFFER_HEADER, but it is still well-known to DAC, so any changes must update the + /// version number and also have corresponding changes made to DAC. + /// + [StructLayout(LayoutKind.Sequential)] + private struct SERIALIZED_ERROR_REPORT_HEADER + { + private int _errorReportSignature; // This is the version of the 'container format'. + private int _exceptionSerializationVersion; // This is the version of the Exception format. It is + // separate from the 'container format' version since the + // implementation of the Exception serialization is owned by + // the Exception class. + private int _exceptionCount; // We just contain a logical array of exceptions. + private int _loadedModuleCount; // Number of loaded modules. present when signature >= ER02. + // {ExceptionCount} serialized Exceptions follow. + // {LoadedModuleCount} module handles follow. present when signature >= ER02. + + public void WriteHeader(int nExceptions, int nLoadedModules) + { + _errorReportSignature = 0x32305245; // 'ER02' + _exceptionSerializationVersion = Exception.CurrentSerializationSignature; + _exceptionCount = nExceptions; + _loadedModuleCount = nLoadedModules; + } + } + + /// + /// Holds metadata about an exception in flight. Class because ConditionalWeakTable only accepts reference types + /// + private class ExceptionData + { + public ExceptionData() + { + // Set this to a non-zero value so that logic mapping entries to threads + // doesn't think an uninitialized ExceptionData is on thread 0 + ExceptionMetadata.ThreadId = 0xFFFFFFFF; + } + + public struct ExceptionMetadataStruct + { + public uint ExceptionId { get; set; } // Id assigned to the exception. May not be contiguous or start at 0. + public uint InnerExceptionId { get; set; } // ID of the inner exception or 0xFFFFFFFF for 'no inner exception' + public uint ThreadId { get; set; } // Managed thread ID the eception was thrown on + public int NestingLevel { get; set; } // If multiple exceptions are currently active on a thread, this gives the ordering for them. + // The highest number is the most recent exception. -1 means the exception is not currently in flight + // (but it may still be an InnerException). + public IntPtr ExceptionCCWPtr { get; set; } // If the exception was thrown in an interop scenario, this contains the CCW pointer, otherwise, IntPtr.Zero + } + + public ExceptionMetadataStruct ExceptionMetadata; + + /// + /// Data created by Exception.SerializeForDump() + /// + public byte[] SerializedExceptionData { get; set; } + + /// + /// Serializes the exception metadata and SerializedExceptionData + /// + public unsafe byte[] Serialize() + { + checked + { + byte[] serializedData = new byte[sizeof(ExceptionMetadataStruct) + SerializedExceptionData.Length]; + fixed (byte* pSerializedData = &serializedData[0]) + { + ExceptionMetadataStruct* pMetadata = (ExceptionMetadataStruct*)pSerializedData; + pMetadata->ExceptionId = ExceptionMetadata.ExceptionId; + pMetadata->InnerExceptionId = ExceptionMetadata.InnerExceptionId; + pMetadata->ThreadId = ExceptionMetadata.ThreadId; + pMetadata->NestingLevel = ExceptionMetadata.NestingLevel; + pMetadata->ExceptionCCWPtr = ExceptionMetadata.ExceptionCCWPtr; + + SerializedExceptionData.AsSpan().CopyTo(new Span(pSerializedData + sizeof(ExceptionMetadataStruct), SerializedExceptionData.Length)); + } + return serializedData; + } + } + } + + /// + /// Table of exceptions that were on stacks triggering GenerateExceptionInformationForDump + /// + private static readonly ConditionalWeakTable s_exceptionDataTable = new ConditionalWeakTable(); + + /// + /// Counter for exception ID assignment + /// + private static int s_currentExceptionId; + + /// + /// This method will call the runtime to gather the Exception objects from every exception dispatch in + /// progress on the current thread. It will then serialize them into a new buffer and pass that + /// buffer back to the runtime, which will publish it to a place where a global "minidump auxiliary + /// provider" will be able to save the buffer's contents into triage dumps. + /// + /// Thread safety information: The guarantee of this method is that the buffer it produces will have + /// complete and correct information for all live exceptions on the current thread (as long as the same exception object + /// is not thrown simultaneously on multiple threads). It will do a best-effort attempt to serialize information about exceptions + /// already recorded on other threads, but that data can be lost or corrupted. The restrictions are: + /// 1. Only exceptions active or recorded on the current thread have their table data modified. + /// 2. After updating data in the table, we serialize a snapshot of the table (provided by ConditionalWeakTable.Values), + /// regardless of what other threads might do to the table before or after. However, because of #1, this thread's + /// exception data should stay stable + /// 3. There is a dependency on the fact that ConditionalWeakTable's members are all threadsafe and that .Values returns a snapshot + /// + public static void GenerateExceptionInformationForDump(Exception currentException, IntPtr exceptionCCWPtr) + { + LowLevelList serializedExceptions = new LowLevelList(); + + // If currentException is null, there's a state corrupting exception in flight and we can't serialize it + if (currentException != null) + { + SerializeExceptionsForDump(currentException, exceptionCCWPtr, serializedExceptions); + } + + GenerateErrorReportForDump(serializedExceptions); + } + + private static void SerializeExceptionsForDump(Exception currentException, IntPtr exceptionCCWPtr, LowLevelList serializedExceptions) + { + const uint NoInnerExceptionValue = 0xFFFFFFFF; + + // Approximate upper size limit for the serialized exceptions (but we'll always serialize currentException) + // If we hit the limit, because we serialize in arbitrary order, there may be missing InnerExceptions or nested exceptions. + const int MaxBufferSize = 20000; + + int nExceptions; + RuntimeImports.RhGetExceptionsForCurrentThread(null, out nExceptions); + Exception[] curThreadExceptions = new Exception[nExceptions]; + RuntimeImports.RhGetExceptionsForCurrentThread(curThreadExceptions, out nExceptions); + LowLevelList exceptions = new LowLevelList(curThreadExceptions); + LowLevelList nonThrownInnerExceptions = new LowLevelList(); + + uint currentThreadId = (uint)Environment.CurrentNativeThreadId; + + // Reset nesting levels for exceptions on this thread that might not be currently in flight + foreach (KeyValuePair item in s_exceptionDataTable) + { + ExceptionData exceptionData = item.Value; + if (exceptionData.ExceptionMetadata.ThreadId == currentThreadId) + { + exceptionData.ExceptionMetadata.NestingLevel = -1; + } + } + + // Find all inner exceptions, even if they're not currently being handled + for (int i = 0; i < exceptions.Count; i++) + { + if (exceptions[i].InnerException != null && !exceptions.Contains(exceptions[i].InnerException)) + { + exceptions.Add(exceptions[i].InnerException); + nonThrownInnerExceptions.Add(exceptions[i].InnerException); + } + } + + int currentNestingLevel = curThreadExceptions.Length - 1; + + // Make sure we serialize currentException + if (!exceptions.Contains(currentException)) + { + // When this happens, currentException is probably passed to this function through System.Environment.FailFast(), we + // would want to treat as if this exception is last thrown in the current thread. + exceptions.Insert(0, currentException); + currentNestingLevel++; + } + + // Populate exception data for all exceptions interesting to this thread. + // Whether or not there was previously data for that object, it might have changed. + for (int i = 0; i < exceptions.Count; i++) + { + ExceptionData exceptionData = s_exceptionDataTable.GetOrCreateValue(exceptions[i]); + + exceptionData.ExceptionMetadata.ExceptionId = (uint)System.Threading.Interlocked.Increment(ref s_currentExceptionId); + if (exceptionData.ExceptionMetadata.ExceptionId == NoInnerExceptionValue) + { + exceptionData.ExceptionMetadata.ExceptionId = (uint)System.Threading.Interlocked.Increment(ref s_currentExceptionId); + } + + exceptionData.ExceptionMetadata.ThreadId = currentThreadId; + + // Only include nesting information for exceptions that were thrown on this thread + if (!nonThrownInnerExceptions.Contains(exceptions[i])) + { + exceptionData.ExceptionMetadata.NestingLevel = currentNestingLevel; + currentNestingLevel--; + } + else + { + exceptionData.ExceptionMetadata.NestingLevel = -1; + } + + // Only match the CCW pointer up to the current exception + if (object.ReferenceEquals(exceptions[i], currentException)) + { + exceptionData.ExceptionMetadata.ExceptionCCWPtr = exceptionCCWPtr; + } + + byte[] serializedEx = exceptions[i].SerializeForDump(); + exceptionData.SerializedExceptionData = serializedEx; + } + + // Populate inner exception ids now that we have all of them in the table + for (int i = 0; i < exceptions.Count; i++) + { + ExceptionData exceptionData; + if (!s_exceptionDataTable.TryGetValue(exceptions[i], out exceptionData)) + { + // This shouldn't happen, but we can't meaningfully throw here + continue; + } + + if (exceptions[i].InnerException != null) + { + ExceptionData innerExceptionData; + if (s_exceptionDataTable.TryGetValue(exceptions[i].InnerException, out innerExceptionData)) + { + exceptionData.ExceptionMetadata.InnerExceptionId = innerExceptionData.ExceptionMetadata.ExceptionId; + } + } + else + { + exceptionData.ExceptionMetadata.InnerExceptionId = NoInnerExceptionValue; + } + } + + int totalSerializedExceptionSize = 0; + // Make sure we include the current exception, regardless of buffer size + ExceptionData currentExceptionData = null; + if (s_exceptionDataTable.TryGetValue(currentException, out currentExceptionData)) + { + byte[] serializedExceptionData = currentExceptionData.Serialize(); + serializedExceptions.Add(serializedExceptionData); + totalSerializedExceptionSize = serializedExceptionData.Length; + } + + checked + { + foreach (KeyValuePair item in s_exceptionDataTable) + { + ExceptionData exceptionData = item.Value; + + // Already serialized currentException + if (currentExceptionData != null && exceptionData.ExceptionMetadata.ExceptionId == currentExceptionData.ExceptionMetadata.ExceptionId) + { + continue; + } + + byte[] serializedExceptionData = exceptionData.Serialize(); + if (totalSerializedExceptionSize + serializedExceptionData.Length >= MaxBufferSize) + { + break; + } + + serializedExceptions.Add(serializedExceptionData); + totalSerializedExceptionSize += serializedExceptionData.Length; + } + } + } + + private static unsafe void GenerateErrorReportForDump(LowLevelList serializedExceptions) + { + checked + { + int loadedModuleCount = (int)RuntimeImports.RhGetLoadedOSModules(null); + int cbModuleHandles = sizeof(System.IntPtr) * loadedModuleCount; + int cbFinalBuffer = sizeof(ERROR_REPORT_BUFFER_HEADER) + sizeof(SERIALIZED_ERROR_REPORT_HEADER) + cbModuleHandles; + for (int i = 0; i < serializedExceptions.Count; i++) + { + cbFinalBuffer += serializedExceptions[i].Length; + } + + byte[] finalBuffer = new byte[cbFinalBuffer]; + fixed (byte* pBuffer = &finalBuffer[0]) + { + byte* pCursor = pBuffer; + int cbRemaining = cbFinalBuffer; + + ERROR_REPORT_BUFFER_HEADER* pDacHeader = (ERROR_REPORT_BUFFER_HEADER*)pCursor; + pDacHeader->WriteHeader(cbFinalBuffer); + pCursor += sizeof(ERROR_REPORT_BUFFER_HEADER); + cbRemaining -= sizeof(ERROR_REPORT_BUFFER_HEADER); + + SERIALIZED_ERROR_REPORT_HEADER* pPayloadHeader = (SERIALIZED_ERROR_REPORT_HEADER*)pCursor; + pPayloadHeader->WriteHeader(serializedExceptions.Count, loadedModuleCount); + pCursor += sizeof(SERIALIZED_ERROR_REPORT_HEADER); + cbRemaining -= sizeof(SERIALIZED_ERROR_REPORT_HEADER); + + // copy the serialized exceptions to report buffer + for (int i = 0; i < serializedExceptions.Count; i++) + { + int cbChunk = serializedExceptions[i].Length; + serializedExceptions[i].AsSpan().CopyTo(new Span(pCursor, cbChunk)); + cbRemaining -= cbChunk; + pCursor += cbChunk; + } + + // copy the module-handle array to report buffer + IntPtr[] loadedModuleHandles = new IntPtr[loadedModuleCount]; + RuntimeImports.RhGetLoadedOSModules(loadedModuleHandles); + loadedModuleHandles.AsSpan().CopyTo(new Span(pCursor, loadedModuleHandles.Length)); + cbRemaining -= cbModuleHandles; + pCursor += cbModuleHandles; + + Debug.Assert(cbRemaining == 0); + } + UpdateErrorReportBuffer(finalBuffer); + } + } + + private static GCHandle s_ExceptionInfoBufferPinningHandle; + private static Lock s_ExceptionInfoBufferLock = new Lock(); + + private static unsafe void UpdateErrorReportBuffer(byte[] finalBuffer) + { + Debug.Assert(finalBuffer?.Length > 0); + + using (LockHolder.Hold(s_ExceptionInfoBufferLock)) + { + fixed (byte* pBuffer = &finalBuffer[0]) + { + byte* pPrevBuffer = (byte*)RuntimeImports.RhSetErrorInfoBuffer(pBuffer); + Debug.Assert(s_ExceptionInfoBufferPinningHandle.IsAllocated == (pPrevBuffer != null)); + if (pPrevBuffer != null) + { + byte[] currentExceptionInfoBuffer = (byte[])s_ExceptionInfoBufferPinningHandle.Target; + Debug.Assert(currentExceptionInfoBuffer?.Length > 0); + fixed (byte* pPrev = ¤tExceptionInfoBuffer[0]) + Debug.Assert(pPrev == pPrevBuffer); + } + if (!s_ExceptionInfoBufferPinningHandle.IsAllocated) + { + // We allocate a pinning GC handle because we are logically giving the runtime 'unmanaged memory'. + s_ExceptionInfoBufferPinningHandle = GCHandle.Alloc(finalBuffer, GCHandleType.Pinned); + } + else + { + s_ExceptionInfoBufferPinningHandle.Target = finalBuffer; + } + } + } + } +#endif // FEATURE_DUMP_DEBUGGING + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs new file mode 100644 index 00000000000000..8e060328d62eaa --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Runtime.CompilerServices; + +using Internal.Runtime.Augments; + +namespace System +{ + [StructLayoutAttribute(LayoutKind.Sequential)] + public struct RuntimeFieldHandle : ISerializable + { + private IntPtr _value; + + public IntPtr Value => _value; + + public override bool Equals(object? obj) + { + if (!(obj is RuntimeFieldHandle)) + return false; + + return Equals((RuntimeFieldHandle)obj); + } + + public bool Equals(RuntimeFieldHandle handle) + { + if (_value == handle._value) + return true; + + if (_value == IntPtr.Zero || handle._value == IntPtr.Zero) + return false; + + string fieldName1, fieldName2; + RuntimeTypeHandle declaringType1, declaringType2; + + RuntimeAugments.TypeLoaderCallbacks.GetRuntimeFieldHandleComponents(this, out declaringType1, out fieldName1); + RuntimeAugments.TypeLoaderCallbacks.GetRuntimeFieldHandleComponents(handle, out declaringType2, out fieldName2); + + return declaringType1.Equals(declaringType2) && fieldName1 == fieldName2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int _rotl(int value, int shift) + { + return (int)(((uint)value << shift) | ((uint)value >> (32 - shift))); + } + + public override int GetHashCode() + { + if (_value == IntPtr.Zero) + return 0; + + string fieldName; + RuntimeTypeHandle declaringType; + RuntimeAugments.TypeLoaderCallbacks.GetRuntimeFieldHandleComponents(this, out declaringType, out fieldName); + + int hashcode = declaringType.GetHashCode(); + return (hashcode + _rotl(hashcode, 13)) ^ fieldName.GetHashCode(); + } + + public static bool operator ==(RuntimeFieldHandle left, RuntimeFieldHandle right) + { + return left.Equals(right); + } + + public static bool operator !=(RuntimeFieldHandle left, RuntimeFieldHandle right) + { + return !left.Equals(right); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs new file mode 100644 index 00000000000000..5063c24c12c398 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Runtime.CompilerServices; + +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; +using Internal.Reflection.Augments; + +namespace System +{ + [StructLayout(LayoutKind.Sequential)] + public struct RuntimeMethodHandle : ISerializable + { + private IntPtr _value; + + public unsafe IntPtr Value => _value; + + public override bool Equals(object? obj) + { + if (!(obj is RuntimeMethodHandle)) + return false; + + return Equals((RuntimeMethodHandle)obj); + } + + public bool Equals(RuntimeMethodHandle handle) + { + if (_value == handle._value) + return true; + + if (_value == IntPtr.Zero || handle._value == IntPtr.Zero) + return false; + + RuntimeTypeHandle declaringType1, declaringType2; + MethodNameAndSignature nameAndSignature1, nameAndSignature2; + RuntimeTypeHandle[] genericArgs1, genericArgs2; + + RuntimeAugments.TypeLoaderCallbacks.GetRuntimeMethodHandleComponents(this, out declaringType1, out nameAndSignature1, out genericArgs1); + RuntimeAugments.TypeLoaderCallbacks.GetRuntimeMethodHandleComponents(handle, out declaringType2, out nameAndSignature2, out genericArgs2); + + if (!declaringType1.Equals(declaringType2)) + return false; + if (!nameAndSignature1.Equals(nameAndSignature2)) + return false; + if ((genericArgs1 == null && genericArgs2 != null) || (genericArgs1 != null && genericArgs2 == null)) + return false; + if (genericArgs1 != null) + { + if (genericArgs1.Length != genericArgs2!.Length) + return false; + for (int i = 0; i < genericArgs1.Length; i++) + { + if (!genericArgs1[i].Equals(genericArgs2![i])) + return false; + } + } + + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int _rotl(int value, int shift) + { + return (int)(((uint)value << shift) | ((uint)value >> (32 - shift))); + } + + public override int GetHashCode() + { + if (_value == IntPtr.Zero) + return 0; + + RuntimeTypeHandle declaringType; + MethodNameAndSignature nameAndSignature; + RuntimeTypeHandle[] genericArgs; + RuntimeAugments.TypeLoaderCallbacks.GetRuntimeMethodHandleComponents(this, out declaringType, out nameAndSignature, out genericArgs); + + int hashcode = declaringType.GetHashCode(); + hashcode = (hashcode + _rotl(hashcode, 13)) ^ nameAndSignature.Name.GetHashCode(); + if (genericArgs != null) + { + for (int i = 0; i < genericArgs.Length; i++) + { + int argumentHashCode = genericArgs[i].GetHashCode(); + hashcode = (hashcode + _rotl(hashcode, 13)) ^ argumentHashCode; + } + } + + return hashcode; + } + + public static bool operator ==(RuntimeMethodHandle left, RuntimeMethodHandle right) + { + return left.Equals(right); + } + + public static bool operator !=(RuntimeMethodHandle left, RuntimeMethodHandle right) + { + return !left.Equals(right); + } + + public IntPtr GetFunctionPointer() + { + RuntimeTypeHandle declaringType; + MethodNameAndSignature nameAndSignature; + RuntimeTypeHandle[] genericArgs; + RuntimeAugments.TypeLoaderCallbacks.GetRuntimeMethodHandleComponents(this, out declaringType, out nameAndSignature, out genericArgs); + + return ReflectionAugments.ReflectionCoreCallbacks.GetFunctionPointer(this, declaringType); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs new file mode 100644 index 00000000000000..f56a1cc1bfb913 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs @@ -0,0 +1,113 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using System.Diagnostics.CodeAnalysis; + +namespace System +{ + // Base class for runtime implemented Type + public abstract class RuntimeType : TypeInfo + { + public sealed override string GetEnumName(object value) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + ulong rawValue; + if (!Enum.TryGetUnboxedValueOfEnumOrInteger(value, out rawValue)) + throw new ArgumentException(SR.Arg_MustBeEnumBaseTypeOrEnum, nameof(value)); + + // For desktop compatibility, do not bounce an incoming integer that's the wrong size. + // Do a value-preserving cast of both it and the enum values and do a 64-bit compare. + + if (!IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum); + + return Enum.GetEnumName(this, rawValue); + } + + public sealed override string[] GetEnumNames() + { + if (!IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + string[] ret = Enum.InternalGetNames(this); + + // Make a copy since we can't hand out the same array since users can modify them + return new ReadOnlySpan(ret).ToArray(); + } + + public sealed override Type GetEnumUnderlyingType() + { + if (!IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + return Enum.InternalGetUnderlyingType(this); + } + + public sealed override bool IsEnumDefined(object value) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + if (!IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum); + + if (value is string valueAsString) + { + EnumInfo enumInfo = Enum.GetEnumInfo(this); + foreach (string name in enumInfo.Names) + { + if (valueAsString == name) + return true; + } + return false; + } + else + { + ulong rawValue; + if (!Enum.TryGetUnboxedValueOfEnumOrInteger(value, out rawValue)) + { + if (Type.IsIntegerType(value.GetType())) + throw new ArgumentException(SR.Format(SR.Arg_EnumUnderlyingTypeAndObjectMustBeSameType, value.GetType(), Enum.InternalGetUnderlyingType(this))); + else + throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType); + } + + if (value is Enum) + { + if (!Enum.ValueTypeMatchesEnumType(this, value)) + throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, value.GetType(), this)); + } + else + { + Type underlyingType = Enum.InternalGetUnderlyingType(this); + if (!(underlyingType.TypeHandle.ToEETypePtr() == value.EETypePtr)) + throw new ArgumentException(SR.Format(SR.Arg_EnumUnderlyingTypeAndObjectMustBeSameType, value.GetType(), underlyingType)); + } + + return Enum.GetEnumName(this, rawValue) != null; + } + } + + [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues overload instead.")] + public sealed override Array GetEnumValues() + { + if (!IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + Array values = Enum.GetEnumInfo(this).ValuesAsUnderlyingType; + int count = values.Length; + // Without universal shared generics, chances are slim that we'll have the appropriate + // array type available. Offer an escape hatch that avoids a MissingMetadataException + // at the cost of a small appcompat risk. + Array result; + if (AppContext.TryGetSwitch("Switch.System.Enum.RelaxedGetValues", out bool isRelaxed) && isRelaxed) + result = Array.CreateInstance(Enum.InternalGetUnderlyingType(this), count); + else + result = Array.CreateInstance(this, count); + Array.Copy(values, result, values.Length); + return result; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs new file mode 100644 index 00000000000000..8097a471242a1c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs @@ -0,0 +1,161 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Diagnostics; + +using Internal.Runtime; +using Internal.Runtime.Augments; + +namespace System +{ + [StructLayout(LayoutKind.Sequential)] + public unsafe struct RuntimeTypeHandle : IEquatable, ISerializable + { + // + // Caution: There can be and are multiple MethodTable for the "same" type (e.g. int[]). That means + // you can't use the raw IntPtr value for comparisons. + // + + internal RuntimeTypeHandle(EETypePtr pEEType) + { + _value = pEEType.RawValue; + } + + public override bool Equals(object? obj) + { + if (obj is RuntimeTypeHandle) + { + RuntimeTypeHandle handle = (RuntimeTypeHandle)obj; + return Equals(handle); + } + return false; + } + + public override int GetHashCode() + { + if (IsNull) + return 0; + + return this.ToEETypePtr().GetHashCode(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(RuntimeTypeHandle handle) + { + if (_value == handle._value) + { + return true; + } + else if (this.IsNull || handle.IsNull) + { + return false; + } + else + { + return RuntimeImports.AreTypesEquivalent(this.ToEETypePtr(), handle.ToEETypePtr()); + } + } + + public static bool operator ==(object? left, RuntimeTypeHandle right) + { + if (left is RuntimeTypeHandle) + return right.Equals((RuntimeTypeHandle)left); + return false; + } + + public static bool operator ==(RuntimeTypeHandle left, object? right) + { + if (right is RuntimeTypeHandle) + return left.Equals((RuntimeTypeHandle)right); + return false; + } + + public static bool operator !=(object? left, RuntimeTypeHandle right) + { + if (left is RuntimeTypeHandle) + return !right.Equals((RuntimeTypeHandle)left); + return true; + } + + public static bool operator !=(RuntimeTypeHandle left, object? right) + { + if (right is RuntimeTypeHandle) + return !left.Equals((RuntimeTypeHandle)right); + return true; + } + + public IntPtr Value => _value; + + public ModuleHandle GetModuleHandle() + { + Type? type = Type.GetTypeFromHandle(this); + if (type == null) + return default(ModuleHandle); + + return type.Module.ModuleHandle; + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal EETypePtr ToEETypePtr() + { + return new EETypePtr(_value); + } + + internal bool IsNull + { + get + { + return _value == new IntPtr(0); + } + } + + // Last resort string for Type.ToString() when no metadata around. + internal string LastResortToString + { + get + { + string s; + EETypePtr eeType = this.ToEETypePtr(); + IntPtr rawEEType = eeType.RawValue; + IntPtr moduleBase = RuntimeImports.RhGetOSModuleFromEEType(rawEEType); + if (moduleBase != IntPtr.Zero) + { + uint rva = (uint)(rawEEType.ToInt64() - moduleBase.ToInt64()); + s = "EETypeRva:0x" + rva.LowLevelToString(); + } + else + { + s = "EETypePointer:0x" + rawEEType.LowLevelToString(); + } + + ReflectionExecutionDomainCallbacks callbacks = RuntimeAugments.CallbacksIfAvailable; + if (callbacks != null) + { + string penultimateLastResortString = callbacks.GetBetterDiagnosticInfoIfAvailable(this); + if (penultimateLastResortString != null) + s += "(" + penultimateLastResortString + ")"; + } + return s; + } + } + + internal IntPtr RawValue + { + get + { + return _value; + } + } + + private IntPtr _value; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/String.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/String.CoreRT.cs new file mode 100644 index 00000000000000..5e9344ea291922 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/String.CoreRT.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System +{ + // This class is marked EagerStaticClassConstruction because it's nice to have this + // eagerly constructed to avoid the cost of defered ctors. I can't imagine any app that doesn't use string + // + [EagerStaticClassConstruction] + public partial class String + { + [Intrinsic] + public static readonly string Empty = ""; + + internal static string FastAllocateString(int length) + { + // We allocate one extra char as an interop convenience so that our strings are null- + // terminated, however, we don't pass the extra +1 to the string allocation because the base + // size of this object includes the _firstChar field. + string newStr = RuntimeImports.RhNewString(EETypePtr.EETypePtrOf(), length); + Debug.Assert(newStr._stringLength == length); + return newStr; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/String.Intern.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/String.Intern.cs new file mode 100644 index 00000000000000..2cded8f357e305 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/String.Intern.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using Internal.TypeSystem; + +namespace System +{ + public partial class String + { + public static string Intern(string str) + { + if (str == null) + throw new ArgumentNullException(nameof(str)); + + return InternTable.GetOrCreateValue(str); + } + + public static string IsInterned(string str) + { + if (str == null) + throw new ArgumentNullException(nameof(str)); + + string canonicalString; + if (!InternTable.TryGetValue(str, out canonicalString)) + return null; + return canonicalString; + } + + private sealed class StringInternTable : LockFreeReaderHashtable + { + protected sealed override bool CompareKeyToValue(string key, string value) => key == value; + protected sealed override bool CompareValueToValue(string value1, string value2) => value1 == value2; + protected sealed override string CreateValueFromKey(string key) => key; + protected sealed override int GetKeyHashCode(string key) => key.GetHashCode(); + protected sealed override int GetValueHashCode(string value) => value.GetHashCode(); + } + + private static StringInternTable InternTable + { + get + { + if (s_lazyInternTable == null) + { + StringInternTable internTable = new StringInternTable(); + internTable.AddOrGetExisting(string.Empty); + Interlocked.CompareExchange(ref s_lazyInternTable, internTable, null); + } + return s_lazyInternTable; + } + } + + private static volatile StringInternTable? s_lazyInternTable; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Text/StringBuilder.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Text/StringBuilder.CoreRT.cs new file mode 100644 index 00000000000000..16c7b20cb1f546 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Text/StringBuilder.CoreRT.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Text +{ + public partial class StringBuilder + { + + /// + /// Calculate the new length for array allocation when marshalling StringBuilder in interop + /// while taking current capacity into account + /// This is needed to ensure compat with desktop CLR behavior + /// + internal int GetAllocationLength(int requiredLength) + { + int currentLength = Capacity; + if (currentLength < requiredLength) + { + // round the current length to the nearest multiple of 2 + // that is >= the required length + return (requiredLength + 1) & ~1; + } + + return currentLength; + } + + /// + /// Throw away the current contents in this StringBuffer and replace it with a new char[] + /// NOTE: The buffer in StringBuilder is *not* NULL-terminated. + /// This is only called from MCG code + /// + internal unsafe void ReplaceBuffer(char* newBuffer) + { + int len = string.wcslen(newBuffer); + + // the '+1' is for back-compat with desktop CLR in terms of length calculation because desktop + // CLR had '\0' + char[] chunkChars = new char[GetAllocationLength(len + 1)]; + + new ReadOnlySpan(newBuffer, len).CopyTo(chunkChars); + + ReplaceBufferInternal(chunkChars, len); + } + + /// + /// Throw away the current contents in this StringBuffer and replace it with a new char[] + /// NOTE: The buffer in StringBuilder is *not* NULL-terminated. + /// This is only called from MCG code + /// + internal void ReplaceBuffer(char[] chunkCharsCandidate) + { + int len = chunkCharsCandidate.Length; + int newLen = GetAllocationLength(len + 1); + if (len == newLen) + { + ReplaceBufferInternal(chunkCharsCandidate, len); + } + else + { + char[] chunkChars = new char[GetAllocationLength(len + 1)]; + + new ReadOnlySpan(chunkCharsCandidate, 0, len).CopyTo(chunkChars); + + ReplaceBufferInternal(chunkChars, len); + } + } + + /// + /// Replace the internal buffer with the specified length + /// This is only called from MCG code + /// + private void ReplaceBufferInternal(char[] chunkChars, int length) + { + m_ChunkChars = chunkChars; + m_ChunkOffset = 0; + m_ChunkLength = length; + m_ChunkPrevious = null; + } + + /// + /// Return buffer if single chunk, for MCG interop + /// + internal char[] GetBuffer(out int len) + { + len = 0; + + if (m_ChunkOffset == 0) + { + len = Length; + + return m_ChunkChars; + } + else + { + return null; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Condition.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Condition.cs new file mode 100644 index 00000000000000..b4eddbbffd9966 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Condition.cs @@ -0,0 +1,170 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma warning disable 0420 //passing volatile fields by ref + + +using System.Diagnostics; + +namespace System.Threading +{ + [System.Runtime.CompilerServices.ReflectionBlocked] + public sealed class Condition + { + internal class Waiter + { + public Waiter? next; + public Waiter? prev; + public AutoResetEvent ev = new AutoResetEvent(false); + public bool signalled; + } + + [ThreadStatic] + private static Waiter t_waiterForCurrentThread; + + private static Waiter GetWaiterForCurrentThread() + { + Waiter waiter = t_waiterForCurrentThread; + if (waiter == null) + waiter = t_waiterForCurrentThread = new Waiter(); + waiter.signalled = false; + return waiter; + } + + private readonly Lock _lock; + private Waiter? _waitersHead; + private Waiter? _waitersTail; + + private unsafe void AssertIsInList(Waiter waiter) + { + Debug.Assert(_waitersHead != null && _waitersTail != null); + Debug.Assert((_waitersHead == waiter) == (waiter.prev == null)); + Debug.Assert((_waitersTail == waiter) == (waiter.next == null)); + + for (Waiter? current = _waitersHead; current != null; current = current.next) + if (current == waiter) + return; + Debug.Fail("Waiter is not in the waiter list"); + } + + private unsafe void AssertIsNotInList(Waiter waiter) + { + Debug.Assert(waiter.next == null && waiter.prev == null); + Debug.Assert((_waitersHead == null) == (_waitersTail == null)); + + for (Waiter? current = _waitersHead; current != null; current = current.next) + if (current == waiter) + Debug.Fail("Waiter is in the waiter list, but should not be"); + } + + private unsafe void AddWaiter(Waiter waiter) + { + Debug.Assert(_lock.IsAcquired); + AssertIsNotInList(waiter); + + waiter.prev = _waitersTail; + if (waiter.prev != null) + waiter.prev.next = waiter; + + _waitersTail = waiter; + + if (_waitersHead == null) + _waitersHead = waiter; + } + + private unsafe void RemoveWaiter(Waiter waiter) + { + Debug.Assert(_lock.IsAcquired); + AssertIsInList(waiter); + + if (waiter.next != null) + waiter.next.prev = waiter.prev; + else + _waitersTail = waiter.prev; + + if (waiter.prev != null) + waiter.prev.next = waiter.next; + else + _waitersHead = waiter.next; + + waiter.next = null; + waiter.prev = null; + } + + public Condition(Lock @lock) + { + if (@lock == null) + throw new ArgumentNullException(nameof(@lock)); + _lock = @lock; + } + + public bool Wait() => Wait(Timeout.Infinite); + + public bool Wait(TimeSpan timeout) => Wait(WaitHandle.ToTimeoutMilliseconds(timeout)); + + public unsafe bool Wait(int millisecondsTimeout) + { + if (millisecondsTimeout < -1) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + + if (!_lock.IsAcquired) + throw new SynchronizationLockException(); + + Waiter waiter = GetWaiterForCurrentThread(); + AddWaiter(waiter); + + uint recursionCount = _lock.ReleaseAll(); + bool success = false; + try + { + success = waiter.ev.WaitOne(millisecondsTimeout); + } + finally + { + _lock.Reacquire(recursionCount); + Debug.Assert(_lock.IsAcquired); + + if (!waiter.signalled) + { + RemoveWaiter(waiter); + } + else if (!success) + { + // + // The wait timed out, but we were signalled before we could reacquire the lock. + // Since WaitOne timed out, it didn't trigger the auto-reset of the AutoResetEvent. + // So, we need to manually reset the event. + // + waiter.ev.Reset(); + } + + AssertIsNotInList(waiter); + } + + return waiter.signalled; + } + + public unsafe void SignalAll() + { + if (!_lock.IsAcquired) + throw new SynchronizationLockException(); + + while (_waitersHead != null) + SignalOne(); + } + + public unsafe void SignalOne() + { + if (!_lock.IsAcquired) + throw new SynchronizationLockException(); + + Waiter? waiter = _waitersHead; + if (waiter != null) + { + RemoveWaiter(waiter); + waiter.signalled = true; + waiter.ev.Set(); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Interlocked.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Interlocked.cs new file mode 100644 index 00000000000000..2400803a8332b2 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Interlocked.cs @@ -0,0 +1,219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Versioning; + +using Internal.Runtime.CompilerServices; + +namespace System.Threading +{ + public static partial class Interlocked + { + #region CompareExchange + + [Intrinsic] + public static int CompareExchange(ref int location1, int value, int comparand) + { + return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); + } + + [Intrinsic] + public static long CompareExchange(ref long location1, long value, long comparand) + { + return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); + } + + [Intrinsic] + public static unsafe float CompareExchange(ref float location1, float value, float comparand) + { + float ret; + *(int*)&ret = CompareExchange(ref Unsafe.As(ref location1), *(int*)&value, *(int*)&comparand); + return ret; + } + + [Intrinsic] + public static unsafe double CompareExchange(ref double location1, double value, double comparand) + { + double ret; + *(long*)&ret = CompareExchange(ref Unsafe.As(ref location1), *(long*)&value, *(long*)&comparand); + return ret; + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NotNullIfNotNull("location1")] + public static T CompareExchange(ref T location1, T value, T comparand) where T : class? + { + return Unsafe.As(RuntimeImports.InterlockedCompareExchange(ref Unsafe.As(ref location1), value, comparand)); + } + + [Intrinsic] + [return: NotNullIfNotNull("location1")] + public static object? CompareExchange(ref object? location1, object? value, object? comparand) + { + return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); + } + + #endregion + + #region Exchange + + [Intrinsic] + public static int Exchange(ref int location1, int value) + { + int oldValue; + + do + { + oldValue = location1; + } while (CompareExchange(ref location1, value, oldValue) != oldValue); + + return oldValue; + } + + [Intrinsic] + public static long Exchange(ref long location1, long value) + { + long oldValue; + + do + { + oldValue = location1; + } while (CompareExchange(ref location1, value, oldValue) != oldValue); + + return oldValue; + } + + [Intrinsic] + public static unsafe float Exchange(ref float location1, float value) + { + float ret; + *(int*)&ret = Exchange(ref Unsafe.As(ref location1), *(int*)&value); + return ret; + } + + [Intrinsic] + public static unsafe double Exchange(ref double location1, double value) + { + double ret; + *(long*)&ret = Exchange(ref Unsafe.As(ref location1), *(long*)&value); + return ret; + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NotNullIfNotNull("location1")] + public static T Exchange([NotNullIfNotNull("value")] ref T location1, T value) where T : class? + { + return Unsafe.As(RuntimeImports.InterlockedExchange(ref Unsafe.As(ref location1), value)); + } + + [Intrinsic] + [return: NotNullIfNotNull("location1")] + public static object? Exchange([NotNullIfNotNull("value")] ref object? location1, object? value) + { + return RuntimeImports.InterlockedExchange(ref location1, value); + } + + #endregion + + #region Increment + + [Intrinsic] + public static int Increment(ref int location) + { + return ExchangeAdd(ref location, 1) + 1; + } + + [Intrinsic] + public static long Increment(ref long location) + { + return ExchangeAdd(ref location, 1) + 1; + } + + #endregion + + #region Decrement + + [Intrinsic] + public static int Decrement(ref int location) + { + return ExchangeAdd(ref location, -1) - 1; + } + + [Intrinsic] + public static long Decrement(ref long location) + { + return ExchangeAdd(ref location, -1) - 1; + } + + #endregion + + #region Add + + [Intrinsic] + public static int Add(ref int location1, int value) + { + return ExchangeAdd(ref location1, value) + value; + } + + [Intrinsic] + public static long Add(ref long location1, long value) + { + return ExchangeAdd(ref location1, value) + value; + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ExchangeAdd(ref int location1, int value) + { + int oldValue; + + do + { + oldValue = location1; + } while (CompareExchange(ref location1, oldValue + value, oldValue) != oldValue); + + return oldValue; + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static long ExchangeAdd(ref long location1, long value) + { + long oldValue; + + do + { + oldValue = location1; + } while (CompareExchange(ref location1, oldValue + value, oldValue) != oldValue); + + return oldValue; + } + + #endregion + + #region MemoryBarrier + [Intrinsic] + public static void MemoryBarrier() + { + RuntimeImports.MemoryBarrier(); + } + #endregion + + #region Read + public static long Read(ref long location) + { + return CompareExchange(ref location, 0, 0); + } + #endregion + + public static void MemoryBarrierProcessWide() + { + RuntimeImports.RhFlushProcessWriteBuffers(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Lock.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Lock.cs new file mode 100644 index 00000000000000..fa81ae7a43684a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Lock.cs @@ -0,0 +1,383 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System.Threading +{ + [ReflectionBlocked] + public sealed class Lock : IDisposable + { + // The following constants define characteristics of spinning logic in the Lock class + private const int SpinningNotInitialized = 0; + private const int SpinningDisabled = -1; + private const int MaxSpinningValue = 10000; + + // + // NOTE: Lock must not have a static (class) constructor, as Lock itself is used to synchronize + // class construction. If Lock has its own class constructor, this can lead to infinite recursion. + // All static data in Lock must be pre-initialized. + // + private static int s_maxSpinCount; + + // + // m_state layout: + // + // bit 0: True if the lock is held, false otherwise. + // + // bit 1: True if we've set the event to wake a waiting thread. The waiter resets this to false when it + // wakes up. This avoids the overhead of setting the event multiple times. + // + // everything else: A count of the number of threads waiting on the event. + // + private const int Locked = 1; + private const int WaiterWoken = 2; + private const int WaiterCountIncrement = 4; + + private const int Uncontended = 0; + + private volatile int _state; + + private uint _recursionCount; + private IntPtr _owningThreadId; + private volatile AutoResetEvent? _lazyEvent; + + private AutoResetEvent Event + { + get + { + // + // Can't use LazyInitializer.EnsureInitialized because Lock needs to stay low level enough + // for the purposes of lazy generic lookups. LazyInitializer uses a generic delegate. + // + if (_lazyEvent == null) + Interlocked.CompareExchange(ref _lazyEvent, new AutoResetEvent(false), null); + + return _lazyEvent; + } + } + + public void Dispose() + { + _lazyEvent?.Dispose(); + } + + private static IntPtr CurrentNativeThreadId => (IntPtr)RuntimeImports.RhCurrentNativeThreadId(); + + // On platforms where CurrentNativeThreadId redirects to ManagedThreadId.Current the inlined + // version of Lock.Acquire has the ManagedThreadId.Current call not inlined, while the non-inlined + // version has it inlined. So it saves code to keep this function not inlined while having + // the same runtime cost. + [MethodImpl(MethodImplOptions.NoInlining)] + public void Acquire() + { + IntPtr currentThreadId = CurrentNativeThreadId; + + // + // Make one quick attempt to acquire an uncontended lock + // + if (Interlocked.CompareExchange(ref _state, Locked, Uncontended) == Uncontended) + { + Debug.Assert(_owningThreadId == IntPtr.Zero); + Debug.Assert(_recursionCount == 0); + _owningThreadId = currentThreadId; + return; + } + + // + // Fall back to the slow path for contention + // + bool success = TryAcquireContended(currentThreadId, Timeout.Infinite); + Debug.Assert(success); + } + + public bool TryAcquire(TimeSpan timeout) + { + return TryAcquire(WaitHandle.ToTimeoutMilliseconds(timeout)); + } + + public bool TryAcquire(int millisecondsTimeout, bool trackContentions = false) + { + if (millisecondsTimeout < -1) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + + IntPtr currentThreadId = CurrentNativeThreadId; + + // + // Make one quick attempt to acquire an uncontended lock + // + if (Interlocked.CompareExchange(ref _state, Locked, Uncontended) == Uncontended) + { + Debug.Assert(_owningThreadId == IntPtr.Zero); + Debug.Assert(_recursionCount == 0); + _owningThreadId = currentThreadId; + return true; + } + + // + // Fall back to the slow path for contention + // + return TryAcquireContended(currentThreadId, millisecondsTimeout, trackContentions); + } + + private bool TryAcquireContended(IntPtr currentThreadId, int millisecondsTimeout, bool trackContentions = false) + { + // + // If we already own the lock, just increment the recursion count. + // + if (_owningThreadId == currentThreadId) + { + checked { _recursionCount++; } + return true; + } + + // + // We've already made one lock attempt at this point, so bail early if the timeout is zero. + // + if (millisecondsTimeout == 0) + return false; + + int spins = 1; + + if (s_maxSpinCount == SpinningNotInitialized) + { + // Use RhGetProcessCpuCount directly to avoid Environment.ProcessorCount->ClassConstructorRunner->Lock->Environment.ProcessorCount cycle + s_maxSpinCount = (RuntimeImports.RhGetProcessCpuCount() > 1) ? MaxSpinningValue : SpinningDisabled; + } + + while (true) + { + // + // Try to grab the lock. We may take the lock here even if there are existing waiters. This creates the possibility + // of starvation of waiters, but it also prevents lock convoys from destroying perf. + // The starvation issue is largely mitigated by the priority boost the OS gives to a waiter when we set + // the event, after we release the lock. Eventually waiters will be boosted high enough to preempt this thread. + // + int oldState = _state; + if ((oldState & Locked) == 0 && Interlocked.CompareExchange(ref _state, oldState | Locked, oldState) == oldState) + goto GotTheLock; + + // + // Back off by a factor of 2 for each attempt, up to MaxSpinCount + // + if (spins <= s_maxSpinCount) + { + RuntimeImports.RhSpinWait(spins); + spins *= 2; + } + else if (oldState != 0) + { + // + // We reached our spin limit, and need to wait. Increment the waiter count. + // Note that we do not do any overflow checking on this increment. In order to overflow, + // we'd need to have about 1 billion waiting threads, which is inconceivable anytime in the + // forseeable future. + // + int newState = (oldState + WaiterCountIncrement) & ~WaiterWoken; + if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) + break; + } + } + + // + // Now we wait. + // + + if (trackContentions) + { + Monitor.IncrementLockContentionCount(); + } + + TimeoutTracker timeoutTracker = TimeoutTracker.Start(millisecondsTimeout); + AutoResetEvent ev = Event; + + while (true) + { + Debug.Assert(_state >= WaiterCountIncrement); + + bool waitSucceeded = ev.WaitOne(timeoutTracker.Remaining); + + while (true) + { + int oldState = _state; + Debug.Assert(oldState >= WaiterCountIncrement); + + // Clear the "waiter woken" bit. + int newState = oldState & ~WaiterWoken; + + if ((oldState & Locked) == 0) + { + // The lock is available, try to get it. + newState |= Locked; + newState -= WaiterCountIncrement; + + if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) + goto GotTheLock; + } + else if (!waitSucceeded) + { + // The lock is not available, and we timed out. We're not going to wait agin. + newState -= WaiterCountIncrement; + + if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) + return false; + } + else + { + // The lock is not available, and we didn't time out. We're going to wait again. + if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) + break; + } + } + } + + GotTheLock: + Debug.Assert((_state | Locked) != 0); + Debug.Assert(_owningThreadId == IntPtr.Zero); + Debug.Assert(_recursionCount == 0); + _owningThreadId = currentThreadId; + return true; + } + + public bool IsAcquired + { + get + { + // + // The comment below is for platforms where CurrentNativeThreadId redirects to + // ManagedThreadId.Current instead of being a compiler intrinsic. + // + // Compare the current owning thread ID with the current thread ID. We need + // to read the current thread's ID before we read m_owningThreadId. Otherwise, + // the following might happen: + // + // 1) We read m_owningThreadId, and get, say 42, which belongs to another thread. + // 2) Thread 42 releases the lock, and exits. + // 3) We call ManagedThreadId.Current. If this is the first time it's been called + // on this thread, we'll go get a new ID. We may reuse thread 42's ID, since + // that thread is dead. + // 4) Now we're thread 42, and it looks like we own the lock, even though we don't. + // + // However, as long as we get this thread's ID first, we know it won't be reused, + // because while we're doing this check the current thread is definitely still + // alive. + // + IntPtr currentThreadId = CurrentNativeThreadId; + bool acquired = (currentThreadId == _owningThreadId); + if (acquired) + Debug.Assert((_state & Locked) != 0); + return acquired; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Release() + { + if (!IsAcquired) + throw new SynchronizationLockException(); + + if (_recursionCount > 0) + _recursionCount--; + else + ReleaseCore(); + } + + internal uint ReleaseAll() + { + Debug.Assert(IsAcquired); + + uint recursionCount = _recursionCount; + _recursionCount = 0; + + ReleaseCore(); + + return recursionCount; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReleaseCore() + { + Debug.Assert(_recursionCount == 0); + _owningThreadId = IntPtr.Zero; + + // + // Make one quick attempt to release an uncontended lock + // + if (Interlocked.CompareExchange(ref _state, Uncontended, Locked) == Locked) + return; + + // + // We have waiters; take the slow path. + // + ReleaseContended(); + } + + private void ReleaseContended() + { + Debug.Assert(_recursionCount == 0); + Debug.Assert(_owningThreadId == IntPtr.Zero); + + while (true) + { + int oldState = _state; + + // clear the lock bit. + int newState = oldState & ~Locked; + + if (oldState >= WaiterCountIncrement && (oldState & WaiterWoken) == 0) + { + // there are waiters, and nobody has woken one. + newState |= WaiterWoken; + if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) + { + Event.Set(); + return; + } + } + else + { + // no need to wake a waiter. + if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) + return; + } + } + } + + internal void Reacquire(uint previousRecursionCount) + { + Acquire(); + Debug.Assert(_recursionCount == 0); + _recursionCount = previousRecursionCount; + } + + internal struct TimeoutTracker + { + private int _start; + private int _timeout; + + public static TimeoutTracker Start(int timeout) + { + TimeoutTracker tracker = new TimeoutTracker(); + tracker._timeout = timeout; + if (timeout != Timeout.Infinite) + tracker._start = Environment.TickCount; + return tracker; + } + + public int Remaining + { + get + { + if (_timeout == Timeout.Infinite) + return Timeout.Infinite; + int elapsed = Environment.TickCount - _start; + if (elapsed > _timeout) + return 0; + return _timeout - elapsed; + } + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/LockHolder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/LockHolder.cs new file mode 100644 index 00000000000000..1c459d6ebd1af0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/LockHolder.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +namespace System.Threading +{ + [ReflectionBlocked] + public struct LockHolder : IDisposable + { + private Lock _lock; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static LockHolder Hold(Lock l) + { + LockHolder h; + l.Acquire(); + h._lock = l; + return h; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + _lock.Release(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs new file mode 100644 index 00000000000000..a0873fc273ff32 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace System.Threading +{ + /// + /// A LIFO semaphore. + /// Waits on this semaphore are uninterruptible. + /// + internal sealed partial class LowLevelLifoSemaphore : IDisposable + { + private WaitSubsystem.WaitableObject _semaphore; + + private void Create(int maximumSignalCount) + { + _semaphore = WaitSubsystem.WaitableObject.NewSemaphore(0, maximumSignalCount); + } + + public void Dispose() + { + } + + private bool WaitCore(int timeoutMs) + { + return WaitSubsystem.Wait(_semaphore, timeoutMs, false, true) == WaitHandle.WaitSuccess; + } + + private void ReleaseCore(int count) + { + WaitSubsystem.ReleaseSemaphore(_semaphore, count); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs new file mode 100644 index 00000000000000..f242a86ad895a7 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs @@ -0,0 +1,289 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// Thread tracks managed thread IDs, recycling them when threads die to keep the set of +// live IDs compact. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +using System.Diagnostics; + +namespace System.Threading +{ + internal class ManagedThreadId + { + // + // Binary tree used to keep track of active thread ids. Each node of the tree keeps track of 32 consecutive ids. + // Implemented as immutable collection to avoid locks. Each modification creates a new top level node. + // + private class ImmutableIdDispenser + { + private readonly ImmutableIdDispenser? _left; // Child nodes + private readonly ImmutableIdDispenser? _right; + + private readonly int _used; // Number of ids tracked by this node and all its childs + private readonly int _size; // Maximum number of ids that can be tracked by this node and all its childs + + private readonly uint _bitmap; // Bitmap of ids tracked by this node + + private const int BitsPerNode = 32; + + private ImmutableIdDispenser(ImmutableIdDispenser? left, ImmutableIdDispenser? right, int used, int size, uint bitmap) + { + _left = left; + _right = right; + _used = used; + _size = size; + _bitmap = bitmap; + + CheckInvariants(); + } + + [Conditional("DEBUG")] + private void CheckInvariants() + { + int actualUsed = 0; + + uint countBits = _bitmap; + while (countBits != 0) + { + actualUsed += (int)(countBits & 1); + countBits >>= 1; + } + + if (_left != null) + { + Debug.Assert(_left._size == ChildSize); + actualUsed += _left._used; + } + if (_right != null) + { + Debug.Assert(_right._size == ChildSize); + actualUsed += _right._used; + } + + Debug.Assert(actualUsed == _used); + Debug.Assert(_used <= _size); + } + + private int ChildSize + { + get + { + Debug.Assert((_size / 2) >= (BitsPerNode / 2)); + return (_size / 2) - (BitsPerNode / 2); + } + } + + public static ImmutableIdDispenser Empty + { + get + { + // The empty dispenser has the id=0 allocated, so it is not really empty. + // It saves us from dealing with the corner case of true empty dispenser, + // and it ensures that IdNone will not be ever given out. + return new ImmutableIdDispenser(null, null, 1, BitsPerNode, 1); + } + } + + public ImmutableIdDispenser AllocateId(out int id) + { + if (_used == _size) + { + id = _size; + return new ImmutableIdDispenser(this, null, _size + 1, checked(2 * _size + BitsPerNode), 1); + } + + var bitmap = _bitmap; + var left = _left; + var right = _right; + + // Any free bits in current node? + if (bitmap != uint.MaxValue) + { + int bit = 0; + while ((bitmap & (uint)(1 << bit)) != 0) + bit++; + bitmap |= (uint)(1 << bit); + id = ChildSize + bit; + } + else + { + Debug.Assert(ChildSize > 0); + if (left == null) + { + left = new ImmutableIdDispenser(null, null, 1, ChildSize, 1); + id = left.ChildSize; + } + else + if (right == null) + { + right = new ImmutableIdDispenser(null, null, 1, ChildSize, 1); + id = ChildSize + BitsPerNode + right.ChildSize; + } + else + { + if (left._used < right._used) + { + Debug.Assert(left._used < left._size); + left = left.AllocateId(out id); + } + else + { + Debug.Assert(right._used < right._size); + right = right.AllocateId(out id); + id += (ChildSize + BitsPerNode); + } + } + } + return new ImmutableIdDispenser(left, right, _used + 1, _size, bitmap); + } + + public ImmutableIdDispenser? RecycleId(int id) + { + Debug.Assert(id < _size); + + if (_used == 1) + return null; + + var bitmap = _bitmap; + var left = _left; + var right = _right; + + int childSize = ChildSize; + if (id < childSize) + { + left = left.RecycleId(id); + } + else + { + id -= childSize; + if (id < BitsPerNode) + { + Debug.Assert((bitmap & (uint)(1 << id)) != 0); + bitmap &= ~(uint)(1 << id); + } + else + { + right = right.RecycleId(id - BitsPerNode); + } + } + return new ImmutableIdDispenser(left, right, _used - 1, _size, bitmap); + } + } + + public const int IdNone = 0; + + // The main thread takes the first available id, which is 1. This id will not be recycled until the process exit. + // We use this id to detect the main thread and report it as a foreground one. + public const int IdMainThread = 1; + + // We store ManagedThreadId both here and in the Thread.CurrentThread object. We store it here, + // because we may need the id very early in the process lifetime (e.g., in ClassConstructorRunner), + // when a Thread object cannot be created yet. We also store it in the Thread.CurrentThread object, + // because that object may have longer lifetime than the OS thread. + [ThreadStatic] + private static ManagedThreadId t_currentThreadId; + [ThreadStatic] + private static int t_currentManagedThreadId; + + // We have to avoid the static constructors on the ManagedThreadId class, otherwise we can run into stack overflow as first time Current property get called, + // the runtime will ensure running the static constructor and this process will call the Current property again (when taking any lock) + // System::Environment.get_CurrentManagedThreadId + // System::Threading::Lock.Acquire + // System::Runtime::CompilerServices::ClassConstructorRunner::Cctor.GetCctor + // System::Runtime::CompilerServices::ClassConstructorRunner.EnsureClassConstructorRun + // System::Threading::ManagedThreadId.get_Current + // System::Environment.get_CurrentManagedThreadId + + private static ImmutableIdDispenser? s_idDispenser; + + private int _managedThreadId; + + public int Id => _managedThreadId; + + public static int AllocateId() + { + if (s_idDispenser == null) + Interlocked.CompareExchange(ref s_idDispenser, ImmutableIdDispenser.Empty, null); + + int id; + + var priorIdDispenser = Volatile.Read(ref s_idDispenser); + for (;;) + { + var updatedIdDispenser = priorIdDispenser.AllocateId(out id); + var interlockedResult = Interlocked.CompareExchange(ref s_idDispenser, updatedIdDispenser, priorIdDispenser); + if (object.ReferenceEquals(priorIdDispenser, interlockedResult)) + break; + priorIdDispenser = interlockedResult; // we already have a volatile read that we can reuse for the next loop + } + + Debug.Assert(id != IdNone); + + return id; + } + + public static void RecycleId(int id) + { + if (id == IdNone) + { + return; + } + + var priorIdDispenser = Volatile.Read(ref s_idDispenser); + for (;;) + { + var updatedIdDispenser = s_idDispenser.RecycleId(id); + var interlockedResult = Interlocked.CompareExchange(ref s_idDispenser, updatedIdDispenser, priorIdDispenser); + if (object.ReferenceEquals(priorIdDispenser, interlockedResult)) + break; + priorIdDispenser = interlockedResult; // we already have a volatile read that we can reuse for the next loop + } + } + + public static int Current + { + get + { + int currentManagedThreadId = t_currentManagedThreadId; + if (currentManagedThreadId == IdNone) + return MakeForCurrentThread(); + else + return currentManagedThreadId; + } + } + + public static ManagedThreadId GetCurrentThreadId() + { + if (t_currentManagedThreadId == IdNone) + MakeForCurrentThread(); + + return t_currentThreadId; + } + + private static int MakeForCurrentThread() + { + return SetForCurrentThread(new ManagedThreadId()); + } + + public static int SetForCurrentThread(ManagedThreadId threadId) + { + t_currentThreadId = threadId; + t_currentManagedThreadId = threadId.Id; + return threadId.Id; + } + + public ManagedThreadId() + { + _managedThreadId = AllocateId(); + } + + ~ManagedThreadId() + { + RecycleId(_managedThreadId); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Monitor.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Monitor.CoreRT.cs new file mode 100644 index 00000000000000..92f2c2ad3e94dd --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Monitor.CoreRT.cs @@ -0,0 +1,261 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================================= +** +** +** +** Purpose: Synchronizes access to a shared resource or region of code in a multi-threaded +** program. +** +** +=============================================================================*/ + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +using Internal.Runtime.CompilerServices; + +namespace System.Threading +{ + public static partial class Monitor + { + #region Object->Lock/Condition mapping + + private static ConditionalWeakTable s_conditionTable = new ConditionalWeakTable(); + private static ConditionalWeakTable.CreateValueCallback s_createCondition = (o) => new Condition(GetLock(o)); + + internal static Lock GetLock(object obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + Debug.Assert(!(obj is Lock), + "Do not use Monitor.Enter or TryEnter on a Lock instance; use Lock methods directly instead."); + + return ObjectHeader.GetLockObject(obj); + } + + private static Condition GetCondition(object obj) + { + Debug.Assert( + !(obj is Condition || obj is Lock), + "Do not use Monitor.Pulse or Wait on a Lock or Condition instance; use the methods on Condition instead."); + return s_conditionTable.GetValue(obj, s_createCondition); + } + #endregion + + #region Public Enter/Exit methods + + public static void Enter(object obj) + { + Lock lck = GetLock(obj); + if (lck.TryAcquire(0)) + return; + TryAcquireContended(lck, obj, Timeout.Infinite); + } + + public static void Enter(object obj, ref bool lockTaken) + { + if (lockTaken) + throw new ArgumentException(SR.Argument_MustBeFalse, nameof(lockTaken)); + + Lock lck = GetLock(obj); + if (lck.TryAcquire(0)) + { + lockTaken = true; + return; + } + TryAcquireContended(lck, obj, Timeout.Infinite); + lockTaken = true; + } + + public static bool TryEnter(object obj) + { + return GetLock(obj).TryAcquire(0); + } + + public static void TryEnter(object obj, ref bool lockTaken) + { + if (lockTaken) + throw new ArgumentException(SR.Argument_MustBeFalse, nameof(lockTaken)); + + lockTaken = GetLock(obj).TryAcquire(0); + } + + public static bool TryEnter(object obj, int millisecondsTimeout) + { + if (millisecondsTimeout < -1) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + + Lock lck = GetLock(obj); + if (lck.TryAcquire(0)) + return true; + return TryAcquireContended(lck, obj, millisecondsTimeout); + } + + public static void TryEnter(object obj, int millisecondsTimeout, ref bool lockTaken) + { + if (lockTaken) + throw new ArgumentException(SR.Argument_MustBeFalse, nameof(lockTaken)); + if (millisecondsTimeout < -1) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + + Lock lck = GetLock(obj); + if (lck.TryAcquire(0)) + { + lockTaken = true; + return; + } + lockTaken = TryAcquireContended(lck, obj, millisecondsTimeout); + } + + public static void Exit(object obj) + { + GetLock(obj).Release(); + } + + public static bool IsEntered(object obj) + { + return GetLock(obj).IsAcquired; + } + + #endregion + + #region Public Wait/Pulse methods + + [UnsupportedOSPlatform("browser")] + public static bool Wait(object obj, int millisecondsTimeout) + { + Condition condition = GetCondition(obj); + DebugBlockingItem blockingItem; + + using (new DebugBlockingScope(obj, DebugBlockingItemType.MonitorEvent, millisecondsTimeout, out blockingItem)) + { + return condition.Wait(millisecondsTimeout); + } + } + + public static void Pulse(object obj) + { + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + + GetCondition(obj).SignalOne(); + } + + public static void PulseAll(object obj) + { + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + + GetCondition(obj).SignalAll(); + } + + #endregion + + #region Slow path for Entry/TryEnter methods. + + internal static bool TryAcquireContended(Lock lck, object obj, int millisecondsTimeout) + { + DebugBlockingItem blockingItem; + + using (new DebugBlockingScope(obj, DebugBlockingItemType.MonitorCriticalSection, millisecondsTimeout, out blockingItem)) + { + return lck.TryAcquire(millisecondsTimeout, trackContentions: true); + } + } + + #endregion + + #region Debugger support + + // The debugger binds to the fields below by name. Do not change any names or types without + // updating the debugger! + + // The head of the list of DebugBlockingItem stack objects used by the debugger to implement + // ICorDebugThread4::GetBlockingObjects. Usually the list either is empty or contains a single + // item. However, a wait on an STA thread may reenter via the message pump and cause the thread + // to be blocked on a second object. + [ThreadStatic] + private static IntPtr t_firstBlockingItem; + + // Different ways a thread can be blocked that the debugger will expose. + // Do not change or add members without updating the debugger code. + private enum DebugBlockingItemType + { + MonitorCriticalSection = 0, + MonitorEvent = 1 + } + + // Represents an item a thread is blocked on. This structure is allocated on the stack and accessed by the debugger. + // Fields are volatile to avoid potential compiler optimizations. + private struct DebugBlockingItem + { + // The object the thread is waiting on + public volatile object _object; + + // Indicates how the thread is blocked on the item + public volatile DebugBlockingItemType _blockingType; + + // Blocking timeout in milliseconds or Timeout.Infinite for no timeout + public volatile int _timeout; + + // Next pointer in the linked list of DebugBlockingItem records + public volatile IntPtr _next; + } + + private unsafe struct DebugBlockingScope : IDisposable + { + public DebugBlockingScope(object obj, DebugBlockingItemType blockingType, int timeout, out DebugBlockingItem blockingItem) + { + blockingItem._object = obj; + blockingItem._blockingType = blockingType; + blockingItem._timeout = timeout; + blockingItem._next = t_firstBlockingItem; + + t_firstBlockingItem = (IntPtr)Unsafe.AsPointer(ref blockingItem); + } + + public void Dispose() + { + t_firstBlockingItem = Unsafe.Read((void*)t_firstBlockingItem)._next; + } + } + + #endregion + + #region Metrics + + private static readonly ThreadInt64PersistentCounter s_lockContentionCounter = new ThreadInt64PersistentCounter(); + + [ThreadStatic] + private static object t_ContentionCountObject; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static object CreateThreadLocalContentionCountObject() + { + Debug.Assert(t_ContentionCountObject == null); + + object threadLocalContentionCountObject = s_lockContentionCounter.CreateThreadLocalCountObject(); + t_ContentionCountObject = threadLocalContentionCountObject; + return threadLocalContentionCountObject; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void IncrementLockContentionCount() => ThreadInt64PersistentCounter.Increment(t_ContentionCountObject ?? CreateThreadLocalContentionCountObject()); + + /// + /// Gets the number of times there was contention upon trying to take a 's lock so far. + /// + public static long LockContentionCount => s_lockContentionCounter.Count; + + #endregion + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ObjectHeader.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ObjectHeader.cs new file mode 100644 index 00000000000000..c9c5d5c0bc1cfc --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ObjectHeader.cs @@ -0,0 +1,214 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Threading +{ + /// + /// Manipulates the object header located 4 bytes before each object's MethodTable pointer + /// in the managed heap. + /// + /// + /// Do not store managed pointers (ref int) to the object header in locals or parameters + /// as they may be incorrectly updated during garbage collection. + /// + internal static class ObjectHeader + { + // The following two header bits are used by the GC engine: + // BIT_SBLK_FINALIZER_RUN = 0x40000000 + // BIT_SBLK_GC_RESERVE = 0x20000000 + // + // All other bits may be used to store runtime data: hash code, sync entry index, etc. + // Here we use the same bit layout as in CLR: if bit 26 (BIT_SBLK_IS_HASHCODE) is set, + // all the lower bits 0..25 store the hash code, otherwise they store either the sync + // entry index or all zero. + // + // If needed, the MASK_HASHCODE_INDEX bit mask may be made wider or narrower than the + // current 26 bits; the BIT_SBLK_IS_HASHCODE bit is not required to be adjacent to the + // mask. The code only assumes that MASK_HASHCODE_INDEX occupies the lowest bits of the + // header (i.e. ends with bit 0) and that (MASK_HASHCODE_INDEX + 1) does not overflow + // the Int32 type (i.e. the mask may be no longer than 30 bits). + + private const int IS_HASHCODE_BIT_NUMBER = 26; + private const int BIT_SBLK_IS_HASHCODE = 1 << IS_HASHCODE_BIT_NUMBER; + internal const int MASK_HASHCODE_INDEX = BIT_SBLK_IS_HASHCODE - 1; + +#if TARGET_ARM || TARGET_ARM64 + [MethodImpl(MethodImplOptions.NoInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public static unsafe int ReadVolatileMemory(int* pHeader) + { + // While in x86/amd64 Volatile.Read is cheap, in arm we have to pay the + // cost of a barrier. We do no inlining to get around that. +#if TARGET_ARM || TARGET_ARM64 + return *pHeader; +#else + return Volatile.Read(ref *pHeader); +#endif + } + + /// + /// Returns the hash code assigned to the object. If no hash code has yet been assigned, + /// it assigns one in a thread-safe way. + /// + public static unsafe int GetHashCode(object o) + { + if (o == null) + { + return 0; + } + + fixed (byte* pRawData = &o.GetRawData()) + { + // The header is 4 bytes before m_pEEType field on all architectures + int* pHeader = (int*)(pRawData - sizeof(IntPtr) - sizeof(int)); + + int bits = ReadVolatileMemory(pHeader); + int hashOrIndex = bits & MASK_HASHCODE_INDEX; + if ((bits & BIT_SBLK_IS_HASHCODE) != 0) + { + // Found the hash code in the header + Debug.Assert(hashOrIndex != 0); + return hashOrIndex; + } + if (hashOrIndex != 0) + { + // Look up the hash code in the SyncTable + int hashCode = SyncTable.GetHashCode(hashOrIndex); + if (hashCode != 0) + { + return hashCode; + } + } + // The hash code has not yet been set. Assign some value. + return AssignHashCode(pHeader); + } + } + + /// + /// Assigns a hash code to the object in a thread-safe way. + /// + private static unsafe int AssignHashCode(int* pHeader) + { + int newHash = RuntimeHelpers.GetNewHashCode() & MASK_HASHCODE_INDEX; + int bitAndValue; + + // Never use the zero hash code. SyncTable treats the zero value as "not assigned". + if (newHash == 0) + { + newHash = 1; + } + + while (true) + { + int oldBits = Volatile.Read(ref *pHeader); + bitAndValue = oldBits & (BIT_SBLK_IS_HASHCODE | MASK_HASHCODE_INDEX); + if (bitAndValue != 0) + { + // The header already stores some value + break; + } + + // The header stores nothing. Try to store the hash code. + int newBits = oldBits | BIT_SBLK_IS_HASHCODE | newHash; + if (Interlocked.CompareExchange(ref *pHeader, newBits, oldBits) == oldBits) + { + return newHash; + } + + // Another thread modified the header; try again + } + + if ((bitAndValue & BIT_SBLK_IS_HASHCODE) == 0) + { + // Set the hash code in SyncTable. This call will resolve the potential race. + return SyncTable.SetHashCode(bitAndValue, newHash); + } + + // Another thread set the hash code, use it + Debug.Assert((bitAndValue & ~BIT_SBLK_IS_HASHCODE) != 0); + return bitAndValue & ~BIT_SBLK_IS_HASHCODE; + } + + /// + /// Extracts the sync entry index or the hash code from the header value. Returns true + /// if the header value stores the sync entry index. + /// + // Inlining is important for lock performance + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool GetSyncEntryIndex(int header, out int hashOrIndex) + { + hashOrIndex = header & MASK_HASHCODE_INDEX; + // The following is equivalent to: + // return (hashOrIndex != 0) && ((header & BIT_SBLK_IS_HASHCODE) == 0); + // Shifting the BIT_SBLK_IS_HASHCODE bit to the sign bit saves one branch. + int bitAndValue = header & (BIT_SBLK_IS_HASHCODE | MASK_HASHCODE_INDEX); + return (bitAndValue << (31 - IS_HASHCODE_BIT_NUMBER)) > 0; + } + + /// + /// Returns the Monitor synchronization object assigned to this object. If no synchronization + /// object has yet been assigned, it assigns one in a thread-safe way. + /// + // Called from Monitor.Enter only; inlining is important for lock performance + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe Lock GetLockObject(object o) + { + fixed (byte* pRawData = &o.GetRawData()) + { + // The header is 4 bytes before m_pEEType field on all architectures + int* pHeader = (int*)(pRawData - sizeof(IntPtr) - sizeof(int)); + + if (GetSyncEntryIndex(ReadVolatileMemory(pHeader), out int hashOrIndex)) + { + // Already have a sync entry for this object, return the synchronization object + // stored in the entry. + return SyncTable.GetLockObject(hashOrIndex); + } + // Assign a new sync entry + int syncIndex = SyncTable.AssignEntry(o, pHeader); + return SyncTable.GetLockObject(syncIndex); + } + } + + /// + /// Sets the sync entry index in a thread-safe way. + /// + public static unsafe void SetSyncEntryIndex(int* pHeader, int syncIndex) + { + // Holding this lock implies there is at most one thread setting the sync entry index at + // any given time. We also require that the sync entry index has not been already set. + Debug.Assert(SyncTable.s_lock.IsAcquired); + Debug.Assert((syncIndex & MASK_HASHCODE_INDEX) == syncIndex); + int oldBits, newBits, hashOrIndex; + + do + { + oldBits = Volatile.Read(ref *pHeader); + newBits = oldBits; + + if (GetSyncEntryIndex(oldBits, out hashOrIndex)) + { + // Must not get here; see the contract + throw new InvalidOperationException(); + } + + Debug.Assert(((oldBits & BIT_SBLK_IS_HASHCODE) == 0) || (hashOrIndex != 0)); + if (hashOrIndex != 0) + { + // Move the hash code to the sync entry + SyncTable.MoveHashCodeToNewEntry(syncIndex, hashOrIndex); + } + + // Store the sync entry index + newBits &= ~(BIT_SBLK_IS_HASHCODE | MASK_HASHCODE_INDEX); + newBits |= syncIndex; + } + while (Interlocked.CompareExchange(ref *pHeader, newBits, oldBits) != oldBits); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Overlapped.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Overlapped.cs new file mode 100644 index 00000000000000..df86052ba3d23a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Overlapped.cs @@ -0,0 +1,334 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/* + * This files defines the following types: + * - _IOCompletionCallback + * - OverlappedData + * - Overlapped + */ + +/*============================================================================= +** +** +** +** Purpose: Class for converting information to and from the native +** overlapped structure used in asynchronous file i/o +** +** +=============================================================================*/ + +using System.Diagnostics; +using System.Runtime; +using System.Runtime.InteropServices; +using Internal.Runtime.CompilerServices; + +namespace System.Threading +{ + #region class _IOCompletionCallback + + internal unsafe class _IOCompletionCallback + { + private IOCompletionCallback _ioCompletionCallback; + private ExecutionContext _executionContext; + private uint _errorCode; // Error code + private uint _numBytes; // No. of bytes transferred + private NativeOverlapped* _pNativeOverlapped; + + internal _IOCompletionCallback(IOCompletionCallback ioCompletionCallback, ExecutionContext executionContext) + { + _ioCompletionCallback = ioCompletionCallback; + _executionContext = executionContext; + } + + // Context callback: same sig for SendOrPostCallback and ContextCallback + internal static ContextCallback s_ccb = new ContextCallback(IOCompletionCallback_Context); + private static void IOCompletionCallback_Context(object? state) + { + Debug.Assert(state != null, "_IOCompletionCallback cannot be null"); + _IOCompletionCallback helper = (_IOCompletionCallback)state; + helper._ioCompletionCallback(helper._errorCode, helper._numBytes, helper._pNativeOverlapped); + } + + internal static unsafe void PerformIOCompletionCallback(uint errorCode, uint numBytes, NativeOverlapped* pNativeOverlapped) + { + do + { + OverlappedData overlapped = OverlappedData.GetOverlappedFromNative(pNativeOverlapped); + + if (overlapped._callback is IOCompletionCallback iocb) + { + // We got here because of UnsafePack (or) Pack with EC flow suppressed + iocb(errorCode, numBytes, pNativeOverlapped); + } + else + { + // We got here because of Pack + var helper = (_IOCompletionCallback)overlapped._callback; + helper._errorCode = errorCode; + helper._numBytes = numBytes; + helper._pNativeOverlapped = pNativeOverlapped; + ExecutionContext.Run(helper._executionContext, s_ccb, helper); + } + + //Quickly check the VM again, to see if a packet has arrived. + //OverlappedData.CheckVMForIOPacket(out pOVERLAP, out errorCode, out numBytes); + pNativeOverlapped = null; + } while (pNativeOverlapped != null); + } + } + + #endregion class _IOCompletionCallback + + #region class OverlappedData + + internal unsafe sealed class OverlappedData + { + internal IAsyncResult _asyncResult; + internal object _callback; // IOCompletionCallback or _IOCompletionCallback + internal Overlapped _overlapped; + private object _userObject; + private NativeOverlapped * _pNativeOverlapped; + private IntPtr _eventHandle; + private int _offsetLow; + private int _offsetHigh; + private GCHandle[] _pinnedData; + + internal ref IAsyncResult AsyncResult => ref _asyncResult; + + internal ref int OffsetLow => ref (_pNativeOverlapped != null) ? ref _pNativeOverlapped->OffsetLow : ref _offsetLow; + internal ref int OffsetHigh => ref (_pNativeOverlapped != null) ? ref _pNativeOverlapped->OffsetHigh : ref _offsetHigh; + internal ref IntPtr EventHandle => ref (_pNativeOverlapped != null) ? ref _pNativeOverlapped->EventHandle : ref _eventHandle; + + internal unsafe NativeOverlapped* Pack(IOCompletionCallback iocb, object userData) + { + if (_pNativeOverlapped != null) + { + throw new InvalidOperationException(SR.InvalidOperation_Overlapped_Pack); + } + + if (iocb != null) + { + ExecutionContext? ec = ExecutionContext.Capture(); + _callback = (ec != null && !ec.IsDefault) ? new _IOCompletionCallback(iocb, ec) : (object)iocb; + } + else + { + _callback = null; + } + _userObject = userData; + return AllocateNativeOverlapped(); + } + + internal unsafe NativeOverlapped* UnsafePack(IOCompletionCallback iocb, object userData) + { + if (_pNativeOverlapped != null) + { + throw new InvalidOperationException(SR.InvalidOperation_Overlapped_Pack); + } + _userObject = userData; + _callback = iocb; + return AllocateNativeOverlapped(); + } + + private unsafe NativeOverlapped* AllocateNativeOverlapped() + { + Debug.Assert(_pinnedData == null); + + bool success = false; + try + { + if (_userObject != null) + { + if (_userObject.GetType() == typeof(object[])) + { + object[] objArray = (object[])_userObject; + + _pinnedData = new GCHandle[objArray.Length]; + for (int i = 0; i < objArray.Length; i++) + { + _pinnedData[i] = GCHandle.Alloc(objArray[i], GCHandleType.Pinned); + } + } + else + { + _pinnedData = new GCHandle[1]; + _pinnedData[0] = GCHandle.Alloc(_userObject, GCHandleType.Pinned); + } + } + + NativeOverlapped* pNativeOverlapped = (NativeOverlapped*)Marshal.AllocHGlobal(sizeof(NativeOverlapped) + sizeof(GCHandle)); + *(GCHandle*)(pNativeOverlapped + 1) = default(GCHandle); + _pNativeOverlapped = pNativeOverlapped; + + _pNativeOverlapped->InternalLow = default; + _pNativeOverlapped->InternalHigh = default; + _pNativeOverlapped->OffsetLow = _offsetLow; + _pNativeOverlapped->OffsetHigh = _offsetHigh; + _pNativeOverlapped->EventHandle = _eventHandle; + + *(GCHandle*)(_pNativeOverlapped + 1) = GCHandle.Alloc(this); + + success = true; + return _pNativeOverlapped; + } + finally + { + if (!success) + FreeNativeOverlapped(); + } + } + + internal static unsafe void FreeNativeOverlapped(NativeOverlapped* nativeOverlappedPtr) + { + OverlappedData overlappedData = OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr); + overlappedData.FreeNativeOverlapped(); + } + + private void FreeNativeOverlapped() + { + if (_pinnedData != null) + { + for (int i = 0; i < _pinnedData.Length; i++) + { + if (_pinnedData[i].IsAllocated) + { + _pinnedData[i].Free(); + } + } + _pinnedData = null; + } + + if (_pNativeOverlapped != null) + { + GCHandle handle = *(GCHandle*)(_pNativeOverlapped + 1); + if (handle.IsAllocated) + handle.Free(); + + Marshal.FreeHGlobal((IntPtr)_pNativeOverlapped); + _pNativeOverlapped = null; + } + } + + internal static unsafe OverlappedData GetOverlappedFromNative(NativeOverlapped* pNativeOverlapped) + { + GCHandle handle = *(GCHandle*)(pNativeOverlapped + 1); + return (OverlappedData)handle.Target; + } + } + + #endregion class OverlappedData + + #region class Overlapped + + public class Overlapped + { + private OverlappedData _overlappedData; + + public Overlapped() + { + _overlappedData = new OverlappedData(); + _overlappedData._overlapped = this; + } + + public Overlapped(int offsetLo, int offsetHi, IntPtr hEvent, IAsyncResult ar) : this() + { + _overlappedData.OffsetLow = offsetLo; + _overlappedData.OffsetHigh = offsetHi; + _overlappedData.EventHandle = hEvent; + _overlappedData.AsyncResult = ar; + } + + [Obsolete("This constructor is not 64-bit compatible. Use the constructor that takes an IntPtr for the event handle. http://go.microsoft.com/fwlink/?linkid=14202")] + public Overlapped(int offsetLo, int offsetHi, int hEvent, IAsyncResult ar) : this(offsetLo, offsetHi, new IntPtr(hEvent), ar) + { + } + + public IAsyncResult AsyncResult + { + get { return _overlappedData.AsyncResult; } + set { _overlappedData.AsyncResult = value; } + } + + public int OffsetLow + { + get { return _overlappedData.OffsetLow; } + set { _overlappedData.OffsetLow = value; } + } + + public int OffsetHigh + { + get { return _overlappedData.OffsetHigh; } + set { _overlappedData.OffsetHigh = value; } + } + + [Obsolete("This property is not 64-bit compatible. Use EventHandleIntPtr instead. http://go.microsoft.com/fwlink/?linkid=14202")] + public int EventHandle + { + get { return EventHandleIntPtr.ToInt32(); } + set { EventHandleIntPtr = new IntPtr(value); } + } + + public IntPtr EventHandleIntPtr + { + get { return _overlappedData.EventHandle; } + set { _overlappedData.EventHandle = value; } + } + + /*==================================================================== + * Packs a managed overlapped class into native Overlapped struct. + * Roots the iocb and stores it in the ReservedCOR field of native Overlapped + * Pins the native Overlapped struct and returns the pinned index. + ====================================================================*/ + [Obsolete("This method is not safe. Use Pack (iocb, userData) instead. http://go.microsoft.com/fwlink/?linkid=14202")] + [CLSCompliant(false)] + public unsafe NativeOverlapped* Pack(IOCompletionCallback iocb) + { + return Pack(iocb, null); + } + + [CLSCompliant(false)] + public unsafe NativeOverlapped* Pack(IOCompletionCallback iocb, object userData) + { + return _overlappedData.Pack(iocb, userData); + } + + [Obsolete("This method is not safe. Use UnsafePack (iocb, userData) instead. http://go.microsoft.com/fwlink/?linkid=14202")] + [CLSCompliant(false)] + public unsafe NativeOverlapped* UnsafePack(IOCompletionCallback iocb) + { + return UnsafePack(iocb, null); + } + + [CLSCompliant(false)] + public unsafe NativeOverlapped* UnsafePack(IOCompletionCallback iocb, object userData) + { + return _overlappedData.UnsafePack(iocb, userData); + } + + /*==================================================================== + * Unpacks an unmanaged native Overlapped struct. + * Unpins the native Overlapped struct + ====================================================================*/ + [CLSCompliant(false)] + public static unsafe Overlapped Unpack(NativeOverlapped* nativeOverlappedPtr) + { + if (nativeOverlappedPtr == null) + throw new ArgumentNullException(nameof(nativeOverlappedPtr)); + + return OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr)._overlapped; + } + + [CLSCompliant(false)] + public static unsafe void Free(NativeOverlapped* nativeOverlappedPtr) + { + if (nativeOverlappedPtr == null) + throw new ArgumentNullException(nameof(nativeOverlappedPtr)); + + OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr)._overlapped._overlappedData = null; + OverlappedData.FreeNativeOverlapped(nativeOverlappedPtr); + } + } + + #endregion class Overlapped +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.PlatformNotSupported.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.PlatformNotSupported.cs new file mode 100644 index 00000000000000..d84ac07463d536 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.PlatformNotSupported.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Threading +{ + public sealed class PreAllocatedOverlapped : IDisposable + { + [CLSCompliant(false)] + public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) : + this(callback, state, pinData, flowExecutionContext: true) + { + } + + [CLSCompliant(false)] + public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => + new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); + + private PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + { + if (callback == null) + throw new ArgumentNullException(nameof(callback)); + + throw new PlatformNotSupportedException(SR.NotSupported_Overlapped); + } + + public void Dispose() + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/SyncTable.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/SyncTable.cs new file mode 100644 index 00000000000000..c58a719a72bbc7 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/SyncTable.cs @@ -0,0 +1,360 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System.Threading +{ + /// + /// Stores the hash code and the Monitor synchronization object for all managed objects used + /// with the Monitor.Enter/TryEnter/Exit methods. + /// + /// + /// This implementation is faster than ConditionalWeakTable since we store the synchronization + /// entry index in the object header, which avoids a hash table lookup. It closely matches + /// implementation of SyncBlocks. + /// + /// SyncTable assigns a unique table entry to each object it is asked for. The assigned entry + /// index is stored in the object header and preserved during table expansion (we never shrink + /// the table). Each table entry contains a long weak GC handle representing the owner object + /// of that entry and may be in one of the three states: + /// 1) Free (IsAllocated == false). These entries have been either never used or used and + /// freed/recycled after their owners died. We keep a linked list of recycled entries and + /// use it to dispense entries to new objects. + /// 2) Live (Target != null). These entries store the hash code and the Monitor synchronization + /// object assigned to Target. + /// 3) Dead (Target == null). These entries lost their owners and are ready to be freed/recycled. + /// + /// Here is the state diagram for an entry: + /// Free --{AssignEntry}--> Live --{GC}--> Dead --{(Recycle|Free)DeadEntries} --> Free + /// + /// The public methods operates on live entries only and acquire the following locks: + /// * GetLockObject : Lock-free. We always allocate a Monitor synchronization object before + /// the entry goes live. The returned object may be used as normal; no + /// additional synchronization required. + /// * GetHashCode : Lock-free. A stale zero value may be returned. + /// * SetHashCode : Acquires s_lock. + /// * AssignEntry : Acquires s_lock. + /// + /// The important part here is that all read operations are lock-free and fast, and write + /// operations are expected to be much less frequent than read ones. + /// + /// + [EagerStaticClassConstruction] + internal static class SyncTable + { + /// + /// The initial size of the table. Must be positive and not greater than + /// ObjectHeader.MASK_HASHCODE_INDEX + 1. + /// + /// + /// CLR uses 250 as the initial table size. In contrast to CoreRT, CLR creates sync + /// entries less frequently since uncontended Monitor synchronization employs a thin lock + /// stored in the object header. + /// +#if DEBUG + // Exercise table expansion more frequently in debug builds + private const int InitialSize = 1; +#else + private const int InitialSize = 1 << 7; +#endif + + /// + /// The table size threshold for doubling in size. Must be positive. + /// + private const int DoublingSizeThreshold = 1 << 20; + + /// + /// Protects all mutable operations on s_entrie, s_freeEntryList, s_unusedEntryIndex. Also protects growing the table. + /// + internal static Lock s_lock = new Lock(); + + /// + /// The dynamically growing array of sync entries. + /// + private static Entry[] s_entries = new Entry[InitialSize]; + + /// + /// The head of the list of freed entries linked using the Next property. + /// + private static int s_freeEntryList; + + /// + /// The index of the lowest never used entry. We skip the 0th entry and start with 1. + /// If all entries have been used, s_unusedEntryIndex == s_entries.Length. This counter + /// never decreases. + /// + private static int s_unusedEntryIndex = 1; + + /// + /// Assigns a sync table entry to the object in a thread-safe way. + /// + public static unsafe int AssignEntry(object obj, int* pHeader) + { + // Allocate the synchronization object outside the lock + Lock lck = new Lock(); + DeadEntryCollector collector = new DeadEntryCollector(); + DependentHandle handle = new DependentHandle(obj, collector); + + try + { + using (LockHolder.Hold(s_lock)) + { + // After acquiring the lock check whether another thread already assigned the sync entry + if (ObjectHeader.GetSyncEntryIndex(*pHeader, out int hashOrIndex)) + { + return hashOrIndex; + } + + int syncIndex; + if (s_freeEntryList != 0) + { + // Grab a free entry from the list + syncIndex = s_freeEntryList; + + ref Entry freeEntry = ref s_entries[syncIndex]; + s_freeEntryList = freeEntry.Next; + freeEntry.Next = 0; + } + else + { + if (s_unusedEntryIndex >= s_entries.Length) + { + // No free entries, use the slow path. This call may OOM. + Grow(); + } + + // Grab the next unused entry + Debug.Assert(s_unusedEntryIndex < s_entries.Length); + syncIndex = s_unusedEntryIndex++; + } + + ref Entry entry = ref s_entries[syncIndex]; + + // Found a free entry to assign + Debug.Assert(!entry.Owner.IsAllocated); + Debug.Assert(entry.Lock == null); + Debug.Assert(entry.HashCode == 0); + + // Set up the new entry. We should not fail after this point. + entry.Lock = lck; + + // The hash code will be set by the SetSyncEntryIndex call below + entry.Owner = handle; + handle = default; + + collector.Activate(syncIndex); + collector = default!; + + // Finally, store the entry index in the object header + ObjectHeader.SetSyncEntryIndex(pHeader, syncIndex); + return syncIndex; + } + } + finally + { + if (collector != null) + GC.SuppressFinalize(collector); + handle.Dispose(); + } + } + + /// + /// Grows the sync table. If memory is not available, it throws an OOM exception keeping + /// the state valid. + /// + private static void Grow() + { + Debug.Assert(s_lock.IsAcquired); + + int oldSize = s_entries.Length; + int newSize = CalculateNewSize(oldSize); + Entry[] newEntries = new Entry[newSize]; + + // Copy the shallow content of the table + Array.Copy(s_entries, newEntries, oldSize); + + // Publish the new table. Lock-free reader threads must not see the new value of + // s_entries until all the content is copied to the new table. + Volatile.Write(ref s_entries, newEntries); + } + + /// + /// Calculates the new size of the sync table if it needs to grow. Throws an OOM exception + /// in case of size overflow. + /// + private static int CalculateNewSize(int oldSize) + { + Debug.Assert(oldSize > 0); + Debug.Assert(ObjectHeader.MASK_HASHCODE_INDEX < int.MaxValue); + int newSize; + + if (oldSize <= DoublingSizeThreshold) + { + // Double in size; overflow is checked below + newSize = unchecked(oldSize * 2); + } + else + { + // For bigger tables use a smaller factor 1.5 + Debug.Assert(oldSize > 1); + newSize = unchecked(oldSize + (oldSize >> 1)); + } + + // All indices must fit in the mask, limit the size accordingly + newSize = Math.Min(newSize, ObjectHeader.MASK_HASHCODE_INDEX + 1); + + // Make sure the new size has not overflowed and is actually bigger + if (newSize <= oldSize) + { + throw new OutOfMemoryException(); + } + + return newSize; + } + + /// + /// Returns the stored hash code. The zero value indicates the hash code has not yet been + /// assigned or visible to this thread. + /// + public static int GetHashCode(int syncIndex) + { + // This thread may be looking at an old version of s_entries. If the old version had + // no hash code stored, GetHashCode returns zero and the subsequent SetHashCode call + // will resolve the potential race. + return s_entries[syncIndex].HashCode; + } + + /// + /// Sets the hash code in a thread-safe way. + /// + public static int SetHashCode(int syncIndex, int hashCode) + { + Debug.Assert((0 < syncIndex) && (syncIndex < s_unusedEntryIndex)); + + // Acquire the lock to ensure we are updating the latest version of s_entries. This + // lock may be avoided if we store the hash code and Monitor synchronization data in + // the same object accessed by a reference. + using (LockHolder.Hold(s_lock)) + { + int currentHash = s_entries[syncIndex].HashCode; + if (currentHash != 0) + { + return currentHash; + } + s_entries[syncIndex].HashCode = hashCode; + return hashCode; + } + } + + /// + /// Sets the hash code assuming the caller holds s_lock. Use for not yet + /// published entries only. + /// + public static void MoveHashCodeToNewEntry(int syncIndex, int hashCode) + { + Debug.Assert(s_lock.IsAcquired); + Debug.Assert((0 < syncIndex) && (syncIndex < s_unusedEntryIndex)); + s_entries[syncIndex].HashCode = hashCode; + } + + /// + /// Returns the Monitor synchronization object. The return value is never null. + /// + public static Lock GetLockObject(int syncIndex) + { + // Note that we do not take a lock here. When we replace s_entries, we preserve all + // indices and Lock references. + return s_entries[syncIndex].Lock; + } + + private sealed class DeadEntryCollector + { + private int _index; + + public DeadEntryCollector() + { + } + + public void Activate(int index) => _index = index; + + ~DeadEntryCollector() + { + if (_index == 0) + return; + + Lock? lockToDispose = default; + DependentHandle dependentHadleToDispose = default; + + using (LockHolder.Hold(s_lock)) + { + ref Entry entry = ref s_entries[_index]; + + if (entry.Owner.Target != null) + { + // Retry later if the owner is not collected yet. + GC.ReRegisterForFinalize(this); + return; + } + + dependentHadleToDispose = entry.Owner; + entry.Owner = default; + + lockToDispose = entry.Lock; + entry.Lock = default; + + entry.Next = s_freeEntryList; + s_freeEntryList = _index; + } + + // Dispose outside the lock + dependentHadleToDispose.Dispose(); + lockToDispose?.Dispose(); + } + } + + /// + /// Stores the Monitor synchronization object and the hash code for an arbitrary object. + /// + private struct Entry + { + /// + /// The Monitor synchronization object. + /// + public Lock Lock; + + /// + /// Contains either the hash code or the index of the next freed entry. + /// + private int _hashOrNext; + + /// + /// The dependent GC handle representing the owner object of this sync entry and the collector responsible + /// for freeing the entry. + /// + public DependentHandle Owner; + + /// + /// For entries in use, this property gets or sets the hash code of the owner object. + /// The zero value indicates the hash code has not yet been assigned. + /// + public int HashCode + { + get { return _hashOrNext; } + set { _hashOrNext = value; } + } + + /// + /// For freed entries, this property gets or sets the index of the next freed entry. + /// The zero value indicates the end of the list. + /// + public int Next + { + get { return _hashOrNext; } + set { _hashOrNext = value; } + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.CoreRT.Unix.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.CoreRT.Unix.cs new file mode 100644 index 00000000000000..a45adb76fc7115 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.CoreRT.Unix.cs @@ -0,0 +1,171 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + public sealed partial class Thread + { + // Event signaling that the thread has stopped + private ManualResetEvent _stopped; + + private WaitSubsystem.ThreadWaitInfo _waitInfo; + + internal WaitSubsystem.ThreadWaitInfo WaitInfo => _waitInfo; + + private void PlatformSpecificInitialize() + { + _waitInfo = new WaitSubsystem.ThreadWaitInfo(this); + } + + // Platform-specific initialization of foreign threads, i.e. threads not created by Thread.Start + private void PlatformSpecificInitializeExistingThread() + { + _stopped = new ManualResetEvent(false); + } + + private ThreadPriority GetPriorityLive() + { + return ThreadPriority.Normal; + } + + private bool SetPriorityLive(ThreadPriority priority) + { + return true; + } + + [UnmanagedCallersOnly] + private static void OnThreadExit() + { + Thread? currentThread = t_currentThread; + if (currentThread != null) + { + // Inform the wait subsystem that the thread is exiting. For instance, this would abandon any mutexes locked by + // the thread. + currentThread._waitInfo.OnThreadExiting(); + StopThread(currentThread); + currentThread._stopped.Set(); + } + } + + private bool JoinInternal(int millisecondsTimeout) + { + // This method assumes the thread has been started + Debug.Assert(!GetThreadStateBit(ThreadState.Unstarted) || (millisecondsTimeout == 0)); + SafeWaitHandle waitHandle = _stopped.SafeWaitHandle; + + // If an OS thread is terminated and its Thread object is resurrected, waitHandle may be finalized and closed + if (waitHandle.IsClosed) + { + return true; + } + + // Prevent race condition with the finalizer + try + { + waitHandle.DangerousAddRef(); + } + catch (ObjectDisposedException) + { + return true; + } + + try + { + return _stopped.WaitOne(millisecondsTimeout); + } + finally + { + waitHandle.DangerousRelease(); + } + } + + private unsafe bool CreateThread(GCHandle thisThreadHandle) + { + // Create the Stop event before starting the thread to make sure + // it is ready to be signaled at thread shutdown time. + // This also avoids OOM after creating the thread. + _stopped = new ManualResetEvent(false); + + if (!Interop.Sys.CreateThread((IntPtr)_startHelper!._maxStackSize, &ThreadEntryPoint, (IntPtr)thisThreadHandle)) + { + return false; + } + + // CoreCLR ignores OS errors while setting the priority, so do we + SetPriorityLive(_priority); + + return true; + } + + /// + /// This is an entry point for managed threads created by application + /// + [UnmanagedCallersOnly] + private static IntPtr ThreadEntryPoint(IntPtr parameter) + { + StartThread(parameter); + return IntPtr.Zero; + } + + public ApartmentState GetApartmentState() + { + return ApartmentState.Unknown; + } + + private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) + { + if (state != ApartmentState.Unknown) + { + if (throwOnError) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + return false; + } + + return true; + } + + private void InitializeComOnNewThread() + { + } + + internal static void InitializeComForFinalizerThread() + { + } + + public void DisableComObjectEagerCleanup() { } + + public void Interrupt() => WaitSubsystem.Interrupt(this); + + internal const bool ReentrantWaitsEnabled = false; + + internal static void SuppressReentrantWaits() + { + throw new PlatformNotSupportedException(); + } + + internal static void RestoreReentrantWaits() + { + throw new PlatformNotSupportedException(); + } + + private static int ComputeCurrentProcessorId() + { + int processorId = Interop.Sys.SchedGetCpu(); + + // sched_getcpu doesn't exist on all platforms. On those it doesn't exist on, the shim + // returns -1. As a fallback in that case and to spread the threads across the buckets + // by default, we use the current managed thread ID as a proxy. + if (processorId < 0) processorId = Environment.CurrentManagedThreadId; + + return processorId; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.CoreRT.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.CoreRT.Windows.cs new file mode 100644 index 00000000000000..e9ce1e5590ad70 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.CoreRT.Windows.cs @@ -0,0 +1,498 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + using OSThreadPriority = Interop.Kernel32.ThreadPriority; + + public sealed partial class Thread + { + [ThreadStatic] + private static int t_reentrantWaitSuppressionCount; + + [ThreadStatic] + private static ApartmentType t_apartmentType; + + [ThreadStatic] + private static ComState t_comState; + + private SafeWaitHandle _osHandle; + + private ApartmentState _initialApartmentState = ApartmentState.Unknown; + + private static volatile bool s_comInitializedOnFinalizerThread; + + private void PlatformSpecificInitialize() + { + } + + // Platform-specific initialization of foreign threads, i.e. threads not created by Thread.Start + private void PlatformSpecificInitializeExistingThread() + { + _osHandle = GetOSHandleForCurrentThread(); + } + + private static SafeWaitHandle GetOSHandleForCurrentThread() + { + IntPtr currentProcHandle = Interop.Kernel32.GetCurrentProcess(); + IntPtr currentThreadHandle = Interop.Kernel32.GetCurrentThread(); + SafeWaitHandle threadHandle; + + if (Interop.Kernel32.DuplicateHandle(currentProcHandle, currentThreadHandle, currentProcHandle, + out threadHandle, 0, false, Interop.Kernel32.DUPLICATE_SAME_ACCESS)) + { + return threadHandle; + } + + // Throw an ApplicationException for compatibility with CoreCLR. First save the error code. + int errorCode = Marshal.GetLastWin32Error(); + var ex = new ApplicationException(); + ex.HResult = errorCode; + throw ex; + } + + private static ThreadPriority MapFromOSPriority(OSThreadPriority priority) + { + if (priority <= OSThreadPriority.Lowest) + { + // OS thread priorities in the [Idle,Lowest] range are mapped to ThreadPriority.Lowest + return ThreadPriority.Lowest; + } + switch (priority) + { + case OSThreadPriority.BelowNormal: + return ThreadPriority.BelowNormal; + + case OSThreadPriority.Normal: + return ThreadPriority.Normal; + + case OSThreadPriority.AboveNormal: + return ThreadPriority.AboveNormal; + + case OSThreadPriority.ErrorReturn: + Debug.Fail("GetThreadPriority failed"); + return ThreadPriority.Normal; + } + // Handle OSThreadPriority.ErrorReturn value before this check! + if (priority >= OSThreadPriority.Highest) + { + // OS thread priorities in the [Highest,TimeCritical] range are mapped to ThreadPriority.Highest + return ThreadPriority.Highest; + } + Debug.Fail("Unreachable"); + return ThreadPriority.Normal; + } + + private static OSThreadPriority MapToOSPriority(ThreadPriority priority) + { + switch (priority) + { + case ThreadPriority.Lowest: + return OSThreadPriority.Lowest; + + case ThreadPriority.BelowNormal: + return OSThreadPriority.BelowNormal; + + case ThreadPriority.Normal: + return OSThreadPriority.Normal; + + case ThreadPriority.AboveNormal: + return OSThreadPriority.AboveNormal; + + case ThreadPriority.Highest: + return OSThreadPriority.Highest; + + default: + Debug.Fail("Unreachable"); + return OSThreadPriority.Normal; + } + } + + private ThreadPriority GetPriorityLive() + { + Debug.Assert(!_osHandle.IsInvalid); + return MapFromOSPriority(Interop.Kernel32.GetThreadPriority(_osHandle)); + } + + private bool SetPriorityLive(ThreadPriority priority) + { + Debug.Assert(!_osHandle.IsInvalid); + return Interop.Kernel32.SetThreadPriority(_osHandle, (int)MapToOSPriority(priority)); + } + + [UnmanagedCallersOnly] + private static void OnThreadExit() + { + Thread? currentThread = t_currentThread; + if (currentThread != null) + { + StopThread(currentThread); + } + } + + private bool JoinInternal(int millisecondsTimeout) + { + // This method assumes the thread has been started + Debug.Assert(!GetThreadStateBit(ThreadState.Unstarted) || (millisecondsTimeout == 0)); + SafeWaitHandle waitHandle = _osHandle; + + // If an OS thread is terminated and its Thread object is resurrected, _osHandle may be finalized and closed + if (waitHandle.IsClosed) + { + return true; + } + + // Handle race condition with the finalizer + try + { + waitHandle.DangerousAddRef(); + } + catch (ObjectDisposedException) + { + return true; + } + + try + { + int result; + + if (millisecondsTimeout == 0) + { + result = (int)Interop.Kernel32.WaitForSingleObject(waitHandle.DangerousGetHandle(), 0); + } + else + { + result = WaitHandle.WaitOneCore(waitHandle.DangerousGetHandle(), millisecondsTimeout); + } + + return result == (int)Interop.Kernel32.WAIT_OBJECT_0; + } + finally + { + waitHandle.DangerousRelease(); + } + } + + private unsafe bool CreateThread(GCHandle thisThreadHandle) + { + const int AllocationGranularity = 0x10000; // 64 KiB + + int stackSize = _startHelper._maxStackSize; + if ((0 < stackSize) && (stackSize < AllocationGranularity)) + { + // If StackSizeParamIsAReservation flag is set and the reserve size specified by CreateThread's + // dwStackSize parameter is less than or equal to the initially committed stack size specified in + // the executable header, the reserve size will be set to the initially committed size rounded up + // to the nearest multiple of 1 MiB. In all cases the reserve size is rounded up to the nearest + // multiple of the system's allocation granularity (typically 64 KiB). + // + // To prevent overreservation of stack memory for small stackSize values, we increase stackSize to + // the allocation granularity. We assume that the SizeOfStackCommit field of IMAGE_OPTIONAL_HEADER + // is strictly smaller than the allocation granularity (the field's default value is 4 KiB); + // otherwise, at least 1 MiB of memory will be reserved. Note that the desktop CLR increases + // stackSize to 256 KiB if it is smaller than that. + stackSize = AllocationGranularity; + } + + uint threadId; + _osHandle = Interop.Kernel32.CreateThread(IntPtr.Zero, (IntPtr)stackSize, + &ThreadEntryPoint, (IntPtr)thisThreadHandle, + Interop.Kernel32.CREATE_SUSPENDED | Interop.Kernel32.STACK_SIZE_PARAM_IS_A_RESERVATION, + out threadId); + + if (_osHandle.IsInvalid) + { + return false; + } + + // CoreCLR ignores OS errors while setting the priority, so do we + SetPriorityLive(_priority); + + Interop.Kernel32.ResumeThread(_osHandle); + return true; + } + + /// + /// This is an entry point for managed threads created by application + /// + [UnmanagedCallersOnly] + private static uint ThreadEntryPoint(IntPtr parameter) + { + StartThread(parameter); + return 0; + } + + public ApartmentState GetApartmentState() + { + if (this != CurrentThread) + { + if (HasStarted()) + throw new ThreadStateException(); + return _initialApartmentState; + } + + switch (GetCurrentApartmentType()) + { + case ApartmentType.STA: + return ApartmentState.STA; + case ApartmentType.MTA: + return ApartmentState.MTA; + default: + return ApartmentState.Unknown; + } + } + + private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) + { + if (this != CurrentThread) + { + using (LockHolder.Hold(_lock)) + { + if (HasStarted()) + throw new ThreadStateException(); + _initialApartmentState = state; + return true; + } + } + + if ((t_comState & ComState.Locked) == 0) + { + if (state != ApartmentState.Unknown) + { + InitializeCom(state); + } + else + { + UninitializeCom(); + } + } + + // Clear the cache and check whether new state matches the desired state + t_apartmentType = ApartmentType.Unknown; + + ApartmentState retState = GetApartmentState(); + + if (retState != state) + { + if (throwOnError) + { + string msg = SR.Format(SR.Thread_ApartmentState_ChangeFailed, retState); + throw new InvalidOperationException(msg); + } + + return false; + } + + return true; + } + + private void InitializeComOnNewThread() + { + InitializeCom(_initialApartmentState); + } + + internal static void InitializeComForFinalizerThread() + { + InitializeCom(); + + // Prevent re-initialization of COM model on finalizer thread + t_comState |= ComState.Locked; + + s_comInitializedOnFinalizerThread = true; + } + + private static void InitializeComForThreadPoolThread() + { + // Initialized COM - take advantage of implicit MTA initialized by the finalizer thread + SpinWait sw = new SpinWait(); + while (!s_comInitializedOnFinalizerThread) + { + RuntimeImports.RhInitializeFinalizerThread(); + sw.SpinOnce(0); + } + + // Prevent re-initialization of COM model on threadpool threads + t_comState |= ComState.Locked; + } + + private static void InitializeCom(ApartmentState state = ApartmentState.MTA) + { + if ((t_comState & ComState.InitializedByUs) != 0) + return; + +#if ENABLE_WINRT + int hr = Interop.WinRT.RoInitialize( + (state == ApartmentState.STA) ? Interop.WinRT.RO_INIT_SINGLETHREADED + : Interop.WinRT.RO_INIT_MULTITHREADED); +#else + int hr = Interop.Ole32.CoInitializeEx(IntPtr.Zero, + (state == ApartmentState.STA) ? Interop.Ole32.COINIT_APARTMENTTHREADED + : Interop.Ole32.COINIT_MULTITHREADED); +#endif + if (hr < 0) + { + // RPC_E_CHANGED_MODE indicates this thread has been already initialized with a different + // concurrency model. We stay away and let whoever else initialized the COM to be in control. + if (hr == HResults.RPC_E_CHANGED_MODE) + return; + + // CoInitializeEx returns E_NOTIMPL on Windows Nano Server for STA + if (hr == HResults.E_NOTIMPL) + throw new PlatformNotSupportedException(); + + throw new OutOfMemoryException(); + } + + t_comState |= ComState.InitializedByUs; + + // If the thread has already been CoInitialized to the proper mode, then + // we don't want to leave an outstanding CoInit so we CoUninit. + if (hr > 0) + UninitializeCom(); + } + + private static void UninitializeCom() + { + if ((t_comState & ComState.InitializedByUs) == 0) + return; + +#if ENABLE_WINRT + Interop.WinRT.RoUninitialize(); +#else + Interop.Ole32.CoUninitialize(); +#endif + + t_comState &= ~ComState.InitializedByUs; + } + + // TODO: https://github.com/dotnet/corefx/issues/20766 + public void DisableComObjectEagerCleanup() { } + + private static Thread InitializeExistingThreadPoolThread() + { + ThreadPool.InitializeForThreadPoolThread(); + + InitializeComForThreadPoolThread(); + + Thread thread = CurrentThread; + thread.SetThreadStateBit(ThreadPoolThread); + return thread; + } + + // Use ThreadPoolCallbackWrapper instead of calling this function directly + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Thread EnsureThreadPoolThreadInitialized() + { + Thread? thread = t_currentThread; + if (thread != null && thread.GetThreadStateBit(ThreadPoolThread)) + return thread; + return InitializeExistingThreadPoolThread(); + } + + public void Interrupt() { throw new PlatformNotSupportedException(); } + + // + // Suppresses reentrant waits on the current thread, until a matching call to RestoreReentrantWaits. + // This should be used by code that's expected to be called inside the STA message pump, so that it won't + // reenter itself. In an ASTA, this should only be the CCW implementations of IUnknown and IInspectable. + // + internal static void SuppressReentrantWaits() + { + t_reentrantWaitSuppressionCount++; + } + + internal static void RestoreReentrantWaits() + { + Debug.Assert(t_reentrantWaitSuppressionCount > 0); + t_reentrantWaitSuppressionCount--; + } + + internal static bool ReentrantWaitsEnabled => + GetCurrentApartmentType() == ApartmentType.STA && t_reentrantWaitSuppressionCount == 0; + + internal static ApartmentType GetCurrentApartmentType() + { + ApartmentType currentThreadType = t_apartmentType; + if (currentThreadType != ApartmentType.Unknown) + return currentThreadType; + + Interop.APTTYPE aptType; + Interop.APTTYPEQUALIFIER aptTypeQualifier; + int result = Interop.Ole32.CoGetApartmentType(out aptType, out aptTypeQualifier); + + ApartmentType type = ApartmentType.Unknown; + + switch (result) + { + case HResults.CO_E_NOTINITIALIZED: + type = ApartmentType.None; + break; + + case HResults.S_OK: + switch (aptType) + { + case Interop.APTTYPE.APTTYPE_STA: + case Interop.APTTYPE.APTTYPE_MAINSTA: + type = ApartmentType.STA; + break; + + case Interop.APTTYPE.APTTYPE_MTA: + type = ApartmentType.MTA; + break; + + case Interop.APTTYPE.APTTYPE_NA: + switch (aptTypeQualifier) + { + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MTA: + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA: + type = ApartmentType.MTA; + break; + + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_STA: + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MAINSTA: + type = ApartmentType.STA; + break; + + default: + Debug.Fail("NA apartment without NA qualifier"); + break; + } + break; + } + break; + + default: + Debug.Fail("bad return from CoGetApartmentType"); + break; + } + + if (type != ApartmentType.Unknown) + t_apartmentType = type; + return type; + } + + internal enum ApartmentType : byte + { + Unknown = 0, + None, + STA, + MTA + } + + [Flags] + internal enum ComState : byte + { + InitializedByUs = 1, + Locked = 2, + } + + // TODO: Use GetCurrentProcessorNumberEx for NUMA + private static int ComputeCurrentProcessorId() => (int)Interop.Kernel32.GetCurrentProcessorNumber(); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.CoreRT.cs new file mode 100644 index 00000000000000..cc6b2c3e62c2df --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.CoreRT.cs @@ -0,0 +1,507 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Globalization; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System.Threading +{ + public sealed partial class Thread + { + // Extra bits used in _threadState + private const ThreadState ThreadPoolThread = (ThreadState)0x1000; + + // Bits of _threadState that are returned by the ThreadState property + private const ThreadState PublicThreadStateMask = (ThreadState)0x1FF; + + internal ExecutionContext? _executionContext; + internal SynchronizationContext? _synchronizationContext; + + private volatile int _threadState = (int)ThreadState.Unstarted; + private ThreadPriority _priority; + private ManagedThreadId _managedThreadId; + private string? _name; + private StartHelper? _startHelper; + + // Protects starting the thread and setting its priority + private Lock _lock = new Lock(); + + // This is used for a quick check on thread pool threads after running a work item to determine if the name, background + // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, + // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. + private bool _mayNeedResetForThreadPool; + + // so far the only place we initialize it is `WaitForForegroundThreads` + // and only in the case when there are running foreground threads + // by the moment of `StartupCodeHelpers.Shutdown()` invocation + private static ManualResetEvent s_allDone; + + private static int s_foregroundRunningCount; + + private Thread() + { + _managedThreadId = System.Threading.ManagedThreadId.GetCurrentThreadId(); + + PlatformSpecificInitialize(); + RegisterThreadExitCallback(); + } + + private void Initialize() + { + _priority = ThreadPriority.Normal; + _managedThreadId = new ManagedThreadId(); + + PlatformSpecificInitialize(); + RegisterThreadExitCallback(); + } + + private unsafe void RegisterThreadExitCallback() + { + RuntimeImports.RhSetThreadExitCallback(&OnThreadExit); + } + + internal static ulong CurrentOSThreadId + { + get + { + return RuntimeImports.RhCurrentOSThreadId(); + } + } + + // Slow path executed once per thread + [MethodImpl(MethodImplOptions.NoInlining)] + private static Thread InitializeCurrentThread() + { + Debug.Assert(t_currentThread == null); + + var currentThread = new Thread(); + Debug.Assert(currentThread._threadState == (int)ThreadState.Unstarted); + + ThreadState state = 0; + + // The main thread is foreground, other ones are background + if (currentThread._managedThreadId.Id != System.Threading.ManagedThreadId.IdMainThread) + { + state |= ThreadState.Background; + } + else + { + Interlocked.Increment(ref s_foregroundRunningCount); + } + + currentThread._threadState = (int)(state | ThreadState.Running); + currentThread.PlatformSpecificInitializeExistingThread(); + currentThread._priority = currentThread.GetPriorityLive(); + t_currentThread = currentThread; + + return currentThread; + } + + /// + /// Returns true if the underlying OS thread has been created and started execution of managed code. + /// + private bool HasStarted() + { + return !GetThreadStateBit(ThreadState.Unstarted); + } + + public bool IsAlive + { + get + { + return ((ThreadState)_threadState & (ThreadState.Unstarted | ThreadState.Stopped | ThreadState.Aborted)) == 0; + } + } + + private bool IsDead() + { + return ((ThreadState)_threadState & (ThreadState.Stopped | ThreadState.Aborted)) != 0; + } + + public bool IsBackground + { + get + { + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + return GetThreadStateBit(ThreadState.Background); + } + set + { + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + // we changing foreground count only for started threads + // on thread start we count its state in `StartThread` + if (value) + { + int threadState = SetThreadStateBit(ThreadState.Background); + // was foreground and has started + if ((threadState & ((int)ThreadState.Background | (int)ThreadState.Unstarted)) == 0) + { + DecrementRunningForeground(); + } + } + else + { + int threadState = ClearThreadStateBit(ThreadState.Background); + // was background and has started + if ((threadState & ((int)ThreadState.Background | (int)ThreadState.Unstarted)) == (int)ThreadState.Background) + { + IncrementRunningForeground(); + _mayNeedResetForThreadPool = true; + } + } + } + } + + public bool IsThreadPoolThread + { + get + { + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + return GetThreadStateBit(ThreadPoolThread); + } + internal set + { + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + if (value) + { + SetThreadStateBit(ThreadPoolThread); + } + else + { + ClearThreadStateBit(ThreadPoolThread); + } + } + } + + public int ManagedThreadId + { + [Intrinsic] + get => _managedThreadId.Id; + } + + partial void ThreadNameChanged(string? value) + { + // TODO: Inform the debugger and the profiler + } + + public ThreadPriority Priority + { + get + { + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_Priority); + } + if (!HasStarted()) + { + // The thread has not been started yet; return the value assigned to the Priority property. + // Race condition with setting the priority or starting the thread is OK, we may return an old value. + return _priority; + } + // The priority might have been changed by external means. Obtain the actual value from the OS + // rather than using the value saved in _priority. + return GetPriorityLive(); + } + set + { + if ((value < ThreadPriority.Lowest) || (ThreadPriority.Highest < value)) + { + throw new ArgumentOutOfRangeException(SR.Argument_InvalidFlag); + } + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_Priority); + } + + // Prevent race condition with starting this thread + using (LockHolder.Hold(_lock)) + { + if (HasStarted() && !SetPriorityLive(value)) + { + throw new ThreadStateException(SR.ThreadState_SetPriorityFailed); + } + _priority = value; + } + + if (value != ThreadPriority.Normal) + { + _mayNeedResetForThreadPool = true; + } + } + } + + public ThreadState ThreadState => ((ThreadState)_threadState & PublicThreadStateMask); + + private bool GetThreadStateBit(ThreadState bit) + { + Debug.Assert((bit & ThreadState.Stopped) == 0, "ThreadState.Stopped bit may be stale; use GetThreadState instead."); + return (_threadState & (int)bit) != 0; + } + + private int SetThreadStateBit(ThreadState bit) + { + int oldState, newState; + do + { + oldState = _threadState; + newState = oldState | (int)bit; + } while (Interlocked.CompareExchange(ref _threadState, newState, oldState) != oldState); + return oldState; + } + + private int ClearThreadStateBit(ThreadState bit) + { + int oldState, newState; + do + { + oldState = _threadState; + newState = oldState & ~(int)bit; + } while (Interlocked.CompareExchange(ref _threadState, newState, oldState) != oldState); + return oldState; + } + + internal void SetWaitSleepJoinState() + { + Debug.Assert(this == CurrentThread); + Debug.Assert(!GetThreadStateBit(ThreadState.WaitSleepJoin)); + + SetThreadStateBit(ThreadState.WaitSleepJoin); + } + + internal void ClearWaitSleepJoinState() + { + Debug.Assert(this == CurrentThread); + Debug.Assert(GetThreadStateBit(ThreadState.WaitSleepJoin)); + + ClearThreadStateBit(ThreadState.WaitSleepJoin); + } + + private static int VerifyTimeoutMilliseconds(int millisecondsTimeout) + { + if (millisecondsTimeout < -1) + { + throw new ArgumentOutOfRangeException( + nameof(millisecondsTimeout), + millisecondsTimeout, + SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + } + return millisecondsTimeout; + } + + public bool Join(int millisecondsTimeout) + { + VerifyTimeoutMilliseconds(millisecondsTimeout); + if (GetThreadStateBit(ThreadState.Unstarted)) + { + throw new ThreadStateException(SR.ThreadState_NotStarted); + } + return JoinInternal(millisecondsTimeout); + } + + /// + /// Max value to be passed into for optimal delaying. Currently, the value comes from + /// defaults in CoreCLR's Thread::InitializeYieldProcessorNormalized(). This value is supposed to be normalized to be + /// appropriate for the processor. + /// TODO: See issue https://github.com/dotnet/corert/issues/4430 + /// + internal const int OptimalMaxSpinWaitsPerSpinIteration = 64; + + public static void SpinWait(int iterations) => RuntimeImports.RhSpinWait(iterations); + + [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. + public static bool Yield() => RuntimeImports.RhYield(); + + private void StartCore() + { + using (LockHolder.Hold(_lock)) + { + if (!GetThreadStateBit(ThreadState.Unstarted)) + { + throw new ThreadStateException(SR.ThreadState_AlreadyStarted); + } + + bool waitingForThreadStart = false; + GCHandle threadHandle = GCHandle.Alloc(this); + + try + { + if (!CreateThread(threadHandle)) + { + throw new OutOfMemoryException(); + } + + // Skip cleanup if any asynchronous exception happens while waiting for the thread start + waitingForThreadStart = true; + + // Wait until the new thread either dies or reports itself as started + while (GetThreadStateBit(ThreadState.Unstarted) && !JoinInternal(0)) + { + Yield(); + } + + waitingForThreadStart = false; + } + finally + { + Debug.Assert(!waitingForThreadStart, "Leaked threadHandle"); + if (!waitingForThreadStart) + { + threadHandle.Free(); + } + } + + if (GetThreadStateBit(ThreadState.Unstarted)) + { + // Lack of memory is the only expected reason for thread creation failure + throw new ThreadStartException(new OutOfMemoryException()); + } + } + } + + private static void StartThread(IntPtr parameter) + { + GCHandle threadHandle = (GCHandle)parameter; + Thread thread = (Thread)threadHandle.Target; + + try + { + t_currentThread = thread; + System.Threading.ManagedThreadId.SetForCurrentThread(thread._managedThreadId); + thread.InitializeComOnNewThread(); + } + catch (OutOfMemoryException) + { +#if TARGET_UNIX + // This should go away once OnThreadExit stops using t_currentThread to signal + // shutdown of the thread on Unix. + thread._stopped!.Set(); +#endif + // Terminate the current thread. The creator thread will throw a ThreadStartException. + return; + } + + // Report success to the creator thread, which will free threadHandle + int state = thread.ClearThreadStateBit(ThreadState.Unstarted); + if ((state & (int)ThreadState.Background) == 0) + { + IncrementRunningForeground(); + } + + try + { + StartHelper? startHelper = thread._startHelper; + Debug.Assert(startHelper != null); + thread._startHelper = null; + + startHelper.Run(); + } + finally + { + thread.SetThreadStateBit(ThreadState.Stopped); + } + } + + private static void StopThread(Thread thread) + { + int state = thread._threadState; + if ((state & (int)(ThreadState.Stopped | ThreadState.Aborted)) == 0) + { + thread.SetThreadStateBit(ThreadState.Stopped); + } + if ((state & (int)ThreadState.Background) == 0) + { + DecrementRunningForeground(); + } + } + + // The upper bits of t_currentProcessorIdCache are the currentProcessorId. The lower bits of + // the t_currentProcessorIdCache are counting down to get it periodically refreshed. + // TODO: Consider flushing the currentProcessorIdCache on Wait operations or similar + // actions that are likely to result in changing the executing core + [ThreadStatic] + private static int t_currentProcessorIdCache; + + private const int ProcessorIdCacheShift = 16; + private const int ProcessorIdCacheCountDownMask = (1 << ProcessorIdCacheShift) - 1; + private const int ProcessorIdRefreshRate = 5000; + + private static int RefreshCurrentProcessorId() + { + int currentProcessorId = ComputeCurrentProcessorId(); + + // Add offset to make it clear that it is not guaranteed to be 0-based processor number + currentProcessorId += 100; + + Debug.Assert(ProcessorIdRefreshRate <= ProcessorIdCacheCountDownMask); + + // Mask with int.MaxValue to ensure the execution Id is not negative + t_currentProcessorIdCache = ((currentProcessorId << ProcessorIdCacheShift) & int.MaxValue) + ProcessorIdRefreshRate; + + return currentProcessorId; + } + + // Cached processor id used as a hint for which per-core stack to access. It is periodically + // refreshed to trail the actual thread core affinity. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetCurrentProcessorId() + { + int currentProcessorIdCache = t_currentProcessorIdCache--; + if ((currentProcessorIdCache & ProcessorIdCacheCountDownMask) == 0) + return RefreshCurrentProcessorId(); + return (currentProcessorIdCache >> ProcessorIdCacheShift); + } + + internal static void IncrementRunningForeground() + { + Interlocked.Increment(ref s_foregroundRunningCount); + } + + internal static void DecrementRunningForeground() + { + if (Interlocked.Decrement(ref s_foregroundRunningCount) == 0) + { + // Interlocked.Decrement issues full memory barrier + // so most recent write to s_allDone should be visible here + s_allDone?.Set(); + } + } + + internal static void WaitForForegroundThreads() + { + Thread.CurrentThread.IsBackground = true; + // last read/write inside `IsBackground` issues full barrier no matter of logic flow + // so we can just read `s_foregroundRunningCount` + if (s_foregroundRunningCount == 0) + { + // current thread is the last foreground thread, so let the runtime finish + return; + } + Volatile.Write(ref s_allDone, new ManualResetEvent(false)); + // other foreground threads could have their job finished meanwhile + // Volatile.Write above issues release barrier + // but we need acquire barrier to observe most recent write to s_foregroundRunningCount + if (Volatile.Read(ref s_foregroundRunningCount) == 0) + { + return; + } + s_allDone.WaitOne(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreRT.cs new file mode 100644 index 00000000000000..06666525d3e34e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreRT.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================================= +** +** +** +** Purpose: Class for creating and managing a threadpool +** +** +=============================================================================*/ + +#pragma warning disable 0420 + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System.Threading +{ + public static partial class ThreadPool + { + internal static void ReportThreadStatus(bool isWorking) + { + } + + private static unsafe void NativeOverlappedCallback(object? obj) + { + Debug.Assert(obj != null); + NativeOverlapped* overlapped = (NativeOverlapped*)(IntPtr)obj; + _IOCompletionCallback.PerformIOCompletionCallback(0, 0, overlapped); + } + + [CLSCompliant(false)] + [SupportedOSPlatform("windows")] + public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) + { + // OS doesn't signal handle, so do it here (CoreCLR does this assignment in ThreadPoolNative::CorPostQueuedCompletionStatus) + overlapped->InternalLow = (IntPtr)0; + // Both types of callbacks are executed on the same thread pool + return UnsafeQueueUserWorkItem(NativeOverlappedCallback, (IntPtr)overlapped); + } + + [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)] + [SupportedOSPlatform("windows")] + public static bool BindHandle(IntPtr osHandle) + { + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle + } + + [SupportedOSPlatform("windows")] + public static bool BindHandle(SafeHandle osHandle) + { + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle + } + + private static long PendingUnmanagedWorkItemCount => 0; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs new file mode 100644 index 00000000000000..44536e1145e646 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -0,0 +1,409 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System.Threading +{ + // + // Windows-specific implementation of ThreadPool + // + [UnsupportedOSPlatform("browser")] + public sealed class RegisteredWaitHandle : MarshalByRefObject + { + private readonly Lock _lock; + private SafeWaitHandle _waitHandle; + private readonly _ThreadPoolWaitOrTimerCallback _callbackHelper; + private readonly uint _millisecondsTimeout; + private bool _repeating; + private bool _unregistering; + + // Handle to this object to keep it alive + private GCHandle _gcHandle; + + // Pointer to the TP_WAIT structure + private IntPtr _tpWait; + + internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, + uint millisecondsTimeout, bool repeating) + { + _lock = new Lock(); + + // Protect the handle from closing while we are waiting on it (VSWhidbey 285642) + waitHandle.DangerousAddRef(); + _waitHandle = waitHandle; + + _callbackHelper = callbackHelper; + _millisecondsTimeout = millisecondsTimeout; + _repeating = repeating; + + // Allocate _gcHandle and _tpWait as the last step and make sure they are never leaked + _gcHandle = GCHandle.Alloc(this); + + _tpWait = Interop.Kernel32.CreateThreadpoolWait(&RegisteredWaitCallback, (IntPtr)_gcHandle, IntPtr.Zero); + + if (_tpWait == IntPtr.Zero) + { + _gcHandle.Free(); + throw new OutOfMemoryException(); + } + } + + [UnmanagedCallersOnly] + internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) + { + var wrapper = ThreadPoolCallbackWrapper.Enter(); + GCHandle handle = (GCHandle)context; + RegisteredWaitHandle registeredWaitHandle = (RegisteredWaitHandle)handle.Target; + Debug.Assert((handle == registeredWaitHandle._gcHandle) && (wait == registeredWaitHandle._tpWait)); + + bool timedOut = (waitResult == (uint)Interop.Kernel32.WAIT_TIMEOUT); + registeredWaitHandle.PerformCallback(timedOut); + ThreadPool.IncrementCompletedWorkItemCount(); + wrapper.Exit(); + } + + private void PerformCallback(bool timedOut) + { + bool lockAcquired; + var spinner = new SpinWait(); + + // Prevent the race condition with Unregister and the previous PerformCallback call, which may still be + // holding the _lock. + while (!(lockAcquired = _lock.TryAcquire(0)) && !Volatile.Read(ref _unregistering)) + { + spinner.SpinOnce(); + } + + // If another thread is running Unregister, no need to restart the timer or clean up + if (lockAcquired) + { + try + { + if (!_unregistering) + { + if (_repeating) + { + // Allow this wait to fire again. Restart the timer before executing the callback. + RestartWait(); + } + else + { + // This wait will not be fired again. Free the GC handle to allow the GC to collect this object. + Debug.Assert(_gcHandle.IsAllocated); + _gcHandle.Free(); + } + } + } + finally + { + _lock.Release(); + } + } + + _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(_callbackHelper, timedOut); + } + + internal unsafe void RestartWait() + { + long timeout; + long* pTimeout = null; // Null indicates infinite timeout + + if (_millisecondsTimeout != Timeout.UnsignedInfinite) + { + timeout = -10000L * _millisecondsTimeout; + pTimeout = &timeout; + } + + // We can use DangerousGetHandle because of DangerousAddRef in the constructor + Interop.Kernel32.SetThreadpoolWait(_tpWait, _waitHandle.DangerousGetHandle(), (IntPtr)pTimeout); + } + + public bool Unregister(WaitHandle waitObject) + { + // Hold the lock during the synchronous part of Unregister (as in CoreCLR) + using (LockHolder.Hold(_lock)) + { + if (!_unregistering) + { + // Ensure callbacks will not call SetThreadpoolWait anymore + _unregistering = true; + + // Cease queueing more callbacks + Interop.Kernel32.SetThreadpoolWait(_tpWait, IntPtr.Zero, IntPtr.Zero); + + // Should we wait for callbacks synchronously? Note that we treat the zero handle as the asynchronous case. + SafeWaitHandle safeWaitHandle = waitObject?.SafeWaitHandle; + bool blocking = ((safeWaitHandle != null) && (safeWaitHandle.DangerousGetHandle() == new IntPtr(-1))); + + if (blocking) + { + FinishUnregistering(); + } + else + { + // Wait for callbacks and dispose resources asynchronously + ThreadPool.QueueUserWorkItem(FinishUnregisteringAsync, safeWaitHandle); + } + + return true; + } + } + return false; + } + + private void FinishUnregistering() + { + Debug.Assert(_unregistering); + + // Wait for outstanding wait callbacks to complete + Interop.Kernel32.WaitForThreadpoolWaitCallbacks(_tpWait, false); + + // Now it is safe to dispose resources + Interop.Kernel32.CloseThreadpoolWait(_tpWait); + _tpWait = IntPtr.Zero; + + if (_gcHandle.IsAllocated) + { + _gcHandle.Free(); + } + + Debug.Assert(_waitHandle != null); + _waitHandle.DangerousRelease(); + _waitHandle = null; + + GC.SuppressFinalize(this); + } + + private void FinishUnregisteringAsync(object? waitObject) + { + FinishUnregistering(); + + // Signal the provided wait object + SafeWaitHandle? safeWaitHandle = (SafeWaitHandle?)waitObject; + + if ((safeWaitHandle != null) && !safeWaitHandle.IsInvalid) + { + Interop.Kernel32.SetEvent(safeWaitHandle); + } + } + + ~RegisteredWaitHandle() + { + // If _gcHandle is allocated, it points to this object, so this object must not be collected by the GC + Debug.Assert(!_gcHandle.IsAllocated); + + // If this object gets resurrected and another thread calls Unregister, that creates a race condition. + // Do not block the finalizer thread. If another thread is running Unregister, it will clean up for us. + // The _lock may be null in case of OOM in the constructor. + if ((_lock != null) && _lock.TryAcquire(0)) + { + try + { + if (!_unregistering) + { + _unregistering = true; + + if (_tpWait != IntPtr.Zero) + { + // There must be no in-flight callbacks; just dispose resources + Interop.Kernel32.CloseThreadpoolWait(_tpWait); + _tpWait = IntPtr.Zero; + } + + if (_waitHandle != null) + { + _waitHandle.DangerousRelease(); + _waitHandle = null; + } + } + } + finally + { + _lock.Release(); + } + } + } + } + + public static partial class ThreadPool + { + internal const bool IsWorkerTrackingEnabledInConfig = false; + + internal const bool SupportsTimeSensitiveWorkItems = false; // the timer currently doesn't queue time-sensitive work + + /// + /// The maximum number of threads in the default thread pool on Windows 10 as computed by + /// TppComputeDefaultMaxThreads(TppMaxGlobalPool). + /// + /// + /// Note that Windows 8 and 8.1 used a different value: Math.Max(4 * Environment.ProcessorCount, 512). + /// + private static readonly int MaxThreadCount = Math.Max(8 * Environment.ProcessorCount, 768); + + private static IntPtr s_work; + + private class ThreadCountHolder + { + internal ThreadCountHolder() => Interlocked.Increment(ref s_threadCount); + ~ThreadCountHolder() => Interlocked.Decrement(ref s_threadCount); + } + + [ThreadStatic] + private static ThreadCountHolder t_threadCountHolder; + private static int s_threadCount; + + [StructLayout(LayoutKind.Sequential)] + private struct WorkingThreadCounter + { + private readonly Internal.PaddingFor32 pad1; + + public volatile int Count; + + private readonly Internal.PaddingFor32 pad2; + } + + // The number of threads executing work items in the Dispatch method + private static WorkingThreadCounter s_workingThreadCounter; + + private static readonly ThreadInt64PersistentCounter s_completedWorkItemCounter = new ThreadInt64PersistentCounter(); + + [ThreadStatic] + private static object? t_completionCountObject; + + internal static void InitializeForThreadPoolThread() => t_threadCountHolder = new ThreadCountHolder(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void IncrementCompletedWorkItemCount() => ThreadInt64PersistentCounter.Increment(GetOrCreateThreadLocalCompletionCountObject()); + + internal static object GetOrCreateThreadLocalCompletionCountObject() => + t_completionCountObject ?? CreateThreadLocalCompletionCountObject(); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static object CreateThreadLocalCompletionCountObject() + { + Debug.Assert(t_completionCountObject == null); + + object threadLocalCompletionCountObject = s_completedWorkItemCounter.CreateThreadLocalCountObject(); + t_completionCountObject = threadLocalCompletionCountObject; + return threadLocalCompletionCountObject; + } + + public static bool SetMaxThreads(int workerThreads, int completionPortThreads) + { + // Not supported at present + return false; + } + + public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) + { + // Note that worker threads and completion port threads share the same thread pool. + // The total number of threads cannot exceed MaxThreadCount. + workerThreads = MaxThreadCount; + completionPortThreads = MaxThreadCount; + } + + public static bool SetMinThreads(int workerThreads, int completionPortThreads) + { + // Not supported at present + return false; + } + + public static void GetMinThreads(out int workerThreads, out int completionPortThreads) + { + workerThreads = 0; + completionPortThreads = 0; + } + + public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) + { + // Make sure we return a non-negative value if thread pool defaults are changed + int availableThreads = Math.Max(MaxThreadCount - s_workingThreadCounter.Count, 0); + + workerThreads = availableThreads; + completionPortThreads = availableThreads; + } + + /// + /// Gets the number of thread pool threads that currently exist. + /// + /// + /// For a thread pool implementation that may have different types of threads, the count includes all types. + /// + public static int ThreadCount => s_threadCount; + + /// + /// Gets the number of work items that have been processed so far. + /// + /// + /// For a thread pool implementation that may have different types of work items, the count includes all types. + /// + public static long CompletedWorkItemCount => s_completedWorkItemCounter.Count; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void NotifyWorkItemProgress() => IncrementCompletedWorkItemCount(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool NotifyWorkItemComplete(object? threadLocalCompletionCountObject, int currentTimeMs) + { + ThreadInt64PersistentCounter.Increment(threadLocalCompletionCountObject); + return true; + } + + internal static bool NotifyThreadBlocked() { return false; } + internal static void NotifyThreadUnblocked() { } + + [UnmanagedCallersOnly] + private static void DispatchCallback(IntPtr instance, IntPtr context, IntPtr work) + { + var wrapper = ThreadPoolCallbackWrapper.Enter(); + Debug.Assert(s_work == work); + Interlocked.Increment(ref s_workingThreadCounter.Count); + ThreadPoolWorkQueue.Dispatch(); + Interlocked.Decrement(ref s_workingThreadCounter.Count); + // We reset the thread after executing each callback + wrapper.Exit(resetThread: false); + } + + internal static unsafe void RequestWorkerThread() + { + if (s_work == IntPtr.Zero) + { + IntPtr work = Interop.Kernel32.CreateThreadpoolWork(&DispatchCallback, IntPtr.Zero, IntPtr.Zero); + if (work == IntPtr.Zero) + throw new OutOfMemoryException(); + + if (Interlocked.CompareExchange(ref s_work, work, IntPtr.Zero) != IntPtr.Zero) + Interop.Kernel32.CloseThreadpoolWork(work); + } + + Interop.Kernel32.SubmitThreadpoolWork(s_work); + } + + private static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + object state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce, + bool flowExecutionContext) + { + if (waitObject == null) + throw new ArgumentNullException(nameof(waitObject)); + + if (callBack == null) + throw new ArgumentNullException(nameof(callBack)); + + var callbackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext); + var registeredWaitHandle = new RegisteredWaitHandle(waitObject.SafeWaitHandle, callbackHelper, millisecondsTimeOutInterval, !executeOnlyOnce); + + registeredWaitHandle.RestartWait(); + return registeredWaitHandle; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs new file mode 100644 index 00000000000000..26ab052805ec86 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Threading +{ + /// + /// Ensures Thread.CurrentThread is initialized for a callback running on a thread pool thread. + /// If WinRT is enabled, also ensures the Windows Runtime is initialized during the execution of the callback. + /// + /// + /// This structure does not implement IDisposable to save on exception support, which callers do not need. + /// + internal struct ThreadPoolCallbackWrapper + { + private Thread _currentThread; + + public static ThreadPoolCallbackWrapper Enter() + { + return new ThreadPoolCallbackWrapper + { + _currentThread = Thread.EnsureThreadPoolThreadInitialized(), + }; + } + + public void Exit(bool resetThread = true) + { + if (resetThread) + { + _currentThread.ResetThreadPoolThread(); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs new file mode 100644 index 00000000000000..1e2cc88ff78290 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace System.Threading +{ + // + // Windows-specific implementation of Timer + // + internal partial class TimerQueue + { + private IntPtr _nativeTimer; + private readonly int _id; + + private TimerQueue(int id) + { + _id = id; + } + + [UnmanagedCallersOnly] + private static void TimerCallback(IntPtr instance, IntPtr context, IntPtr timer) + { + int id = (int)context; + var wrapper = ThreadPoolCallbackWrapper.Enter(); + Instances[id].FireNextTimers(); + ThreadPool.IncrementCompletedWorkItemCount(); + wrapper.Exit(); + } + + private unsafe bool SetTimer(uint actualDuration) + { + if (_nativeTimer == IntPtr.Zero) + { + _nativeTimer = Interop.Kernel32.CreateThreadpoolTimer(&TimerCallback, (IntPtr)_id, IntPtr.Zero); + if (_nativeTimer == IntPtr.Zero) + throw new OutOfMemoryException(); + } + + // Negative time indicates the amount of time to wait relative to the current time, in 100 nanosecond units + long dueTime = -10000 * (long)actualDuration; + Interop.Kernel32.SetThreadpoolTimer(_nativeTimer, &dueTime, 0, 0); + + return true; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs new file mode 100644 index 00000000000000..e1a3ea34dda2d9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs @@ -0,0 +1,218 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.IO; + +namespace System.Threading +{ + // + // Implementation of ThreadPoolBoundHandle that sits on top of the Win32 ThreadPool + // + public sealed class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable + { + private readonly SafeHandle _handle; + private readonly SafeThreadPoolIOHandle _threadPoolHandle; + private DeferredDisposableLifetime _lifetime; + + private ThreadPoolBoundHandle(SafeHandle handle, SafeThreadPoolIOHandle threadPoolHandle) + { + _threadPoolHandle = threadPoolHandle; + _handle = handle; + } + + public SafeHandle Handle + { + get { return _handle; } + } + + public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) + { + if (handle == null) + throw new ArgumentNullException(nameof(handle)); + + if (handle.IsClosed || handle.IsInvalid) + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + + SafeThreadPoolIOHandle threadPoolHandle = Interop.Kernel32.CreateThreadpoolIo(handle, &OnNativeIOCompleted, IntPtr.Zero, IntPtr.Zero); + if (threadPoolHandle.IsInvalid) + { + int errorCode = Marshal.GetLastWin32Error(); + if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE) // Bad handle + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + + if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) // Handle already bound or sync handle + throw new ArgumentException(SR.Argument_AlreadyBoundOrSyncHandle, nameof(handle)); + + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + + return new ThreadPoolBoundHandle(handle, threadPoolHandle); + } + + [CLSCompliant(false)] + public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => + AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: true); + + [CLSCompliant(false)] + public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => + AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: false); + + private unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object state, object pinData, bool flowExecutionContext) + { + if (callback == null) + throw new ArgumentNullException(nameof(callback)); + + AddRef(); + try + { + Win32ThreadPoolNativeOverlapped* overlapped = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, preAllocated: null, flowExecutionContext); + overlapped->Data._boundHandle = this; + + Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle); + + return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped); + } + catch + { + Release(); + throw; + } + } + + [CLSCompliant(false)] + public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) + { + if (preAllocated == null) + throw new ArgumentNullException(nameof(preAllocated)); + + bool addedRefToThis = false; + bool addedRefToPreAllocated = false; + try + { + addedRefToThis = AddRef(); + addedRefToPreAllocated = preAllocated.AddRef(); + + Win32ThreadPoolNativeOverlapped.OverlappedData data = preAllocated._overlapped->Data; + if (data._boundHandle != null) + throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated)); + + data._boundHandle = this; + + Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle); + + return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlapped); + } + catch + { + if (addedRefToPreAllocated) + preAllocated.Release(); + if (addedRefToThis) + Release(); + throw; + } + } + + [CLSCompliant(false)] + public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) + { + if (overlapped == null) + throw new ArgumentNullException(nameof(overlapped)); + + Win32ThreadPoolNativeOverlapped* threadPoolOverlapped = Win32ThreadPoolNativeOverlapped.FromNativeOverlapped(overlapped); + Win32ThreadPoolNativeOverlapped.OverlappedData data = GetOverlappedData(threadPoolOverlapped, this); + + if (!data._completed) + { + Interop.Kernel32.CancelThreadpoolIo(_threadPoolHandle); + Release(); + } + + data._boundHandle = null; + data._completed = false; + + if (data._preAllocated != null) + data._preAllocated.Release(); + else + Win32ThreadPoolNativeOverlapped.Free(threadPoolOverlapped); + } + + [CLSCompliant(false)] + public static unsafe object GetNativeOverlappedState(NativeOverlapped* overlapped) + { + if (overlapped == null) + throw new ArgumentNullException(nameof(overlapped)); + + Win32ThreadPoolNativeOverlapped* threadPoolOverlapped = Win32ThreadPoolNativeOverlapped.FromNativeOverlapped(overlapped); + Win32ThreadPoolNativeOverlapped.OverlappedData data = GetOverlappedData(threadPoolOverlapped, null); + + return data._state; + } + + private static unsafe Win32ThreadPoolNativeOverlapped.OverlappedData GetOverlappedData(Win32ThreadPoolNativeOverlapped* overlapped, ThreadPoolBoundHandle expectedBoundHandle) + { + Win32ThreadPoolNativeOverlapped.OverlappedData data = overlapped->Data; + + if (data._boundHandle == null) + throw new ArgumentException(SR.Argument_NativeOverlappedAlreadyFree, nameof(overlapped)); + + if (expectedBoundHandle != null && data._boundHandle != expectedBoundHandle) + throw new ArgumentException(SR.Argument_NativeOverlappedWrongBoundHandle, nameof(overlapped)); + + return data; + } + + [UnmanagedCallersOnly] + private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, IntPtr overlappedPtr, uint ioResult, UIntPtr numberOfBytesTransferred, IntPtr ioPtr) + { + var wrapper = ThreadPoolCallbackWrapper.Enter(); + Win32ThreadPoolNativeOverlapped* overlapped = (Win32ThreadPoolNativeOverlapped*)overlappedPtr; + + ThreadPoolBoundHandle boundHandle = overlapped->Data._boundHandle; + if (boundHandle == null) + throw new InvalidOperationException(SR.Argument_NativeOverlappedAlreadyFree); + + boundHandle.Release(); + + Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped); + ThreadPool.IncrementCompletedWorkItemCount(); + wrapper.Exit(); + } + + private bool AddRef() + { + return _lifetime.AddRef(); + } + + private void Release() + { + _lifetime.Release(this); + } + + public void Dispose() + { + _lifetime.Dispose(this); + GC.SuppressFinalize(this); + } + + ~ThreadPoolBoundHandle() + { + // + // During shutdown, don't automatically clean up, because this instance may still be + // reachable/usable by other code. + // + if (!Environment.HasShutdownStarted) + Dispose(); + } + + void IDeferredDisposable.OnFinalRelease(bool disposed) + { + if (disposed) + _threadPoolHandle.Dispose(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.ExecutionContextCallbackArgs.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.ExecutionContextCallbackArgs.cs new file mode 100644 index 00000000000000..173a237759875b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.ExecutionContextCallbackArgs.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Threading +{ + internal partial struct Win32ThreadPoolNativeOverlapped + { + private unsafe class ExecutionContextCallbackArgs + { + internal uint _errorCode; + internal uint _bytesWritten; + internal Win32ThreadPoolNativeOverlapped* _overlapped; + internal OverlappedData _data; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.OverlappedData.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.OverlappedData.cs new file mode 100644 index 00000000000000..798f11584fbd70 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.OverlappedData.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + internal partial struct Win32ThreadPoolNativeOverlapped + { + internal class OverlappedData + { + internal GCHandle[] _pinnedData; + internal IOCompletionCallback? _callback; + internal object? _state; + internal ExecutionContext? _executionContext; + internal ThreadPoolBoundHandle _boundHandle; + internal PreAllocatedOverlapped? _preAllocated; + internal bool _completed; + + internal void Reset() + { + Debug.Assert(_boundHandle == null); //not in use + + if (_pinnedData != null) + { + for (int i = 0; i < _pinnedData.Length; i++) + { + if (_pinnedData[i].IsAllocated && _pinnedData[i].Target != null) + _pinnedData[i].Target = null; + } + } + + _callback = null; + _state = null; + _executionContext = null; + _completed = false; + _preAllocated = null; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs new file mode 100644 index 00000000000000..ae87d54c1b6459 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs @@ -0,0 +1,237 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + [StructLayout(LayoutKind.Sequential)] + internal partial struct Win32ThreadPoolNativeOverlapped + { + // Per-thread cache of the args object, so we don't have to allocate a new one each time. + [ThreadStatic] + private static ExecutionContextCallbackArgs t_executionContextCallbackArgs; + + private static ContextCallback s_executionContextCallback; + private static OverlappedData[] s_dataArray; + private static int s_dataCount; // Current number of valid entries in _dataArray + private static IntPtr s_freeList; // Lock-free linked stack of free ThreadPoolNativeOverlapped instances. + + private NativeOverlapped _overlapped; // must be first, so we can cast to and from NativeOverlapped. + private IntPtr _nextFree; // if this instance if free, points to the next free instance. + private int _dataIndex; // Index in _dataArray of this instance's OverlappedData. + + internal OverlappedData Data + { + get { return s_dataArray[_dataIndex]; } + } + + internal static unsafe Win32ThreadPoolNativeOverlapped* Allocate(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionControl) + { + Win32ThreadPoolNativeOverlapped* overlapped = AllocateNew(); + try + { + overlapped->SetData(callback, state, pinData, preAllocated, flowExecutionControl); + } + catch + { + Free(overlapped); + throw; + } + return overlapped; + } + + private static unsafe Win32ThreadPoolNativeOverlapped* AllocateNew() + { + IntPtr freePtr; + Win32ThreadPoolNativeOverlapped* overlapped; + OverlappedData data; + + // Find a free Overlapped + while ((freePtr = Volatile.Read(ref s_freeList)) != IntPtr.Zero) + { + overlapped = (Win32ThreadPoolNativeOverlapped*)freePtr; + + if (Interlocked.CompareExchange(ref s_freeList, overlapped->_nextFree, freePtr) != freePtr) + continue; + + overlapped->_nextFree = IntPtr.Zero; + return overlapped; + } + + // None are free; allocate a new one. + overlapped = (Win32ThreadPoolNativeOverlapped*)Marshal.AllocHGlobal(sizeof(Win32ThreadPoolNativeOverlapped)); + *overlapped = default(Win32ThreadPoolNativeOverlapped); + + // Allocate a OverlappedData object, and an index at which to store it in _dataArray. + data = new OverlappedData(); + int dataIndex = Interlocked.Increment(ref s_dataCount) - 1; + + // Make sure we didn't wrap around. + if (dataIndex < 0) + Environment.FailFast("Too many outstanding Win32ThreadPoolNativeOverlapped instances"); + + while (true) + { + OverlappedData[] dataArray = Volatile.Read(ref s_dataArray); + int currentLength = dataArray == null ? 0 : dataArray.Length; + + // If the current array is too small, create a new, larger one. + if (currentLength <= dataIndex) + { + int newLength = currentLength; + if (newLength == 0) + newLength = 128; + while (newLength <= dataIndex) + newLength = (newLength * 3) / 2; + + OverlappedData[] newDataArray = dataArray; + Array.Resize(ref newDataArray, newLength); + + if (Interlocked.CompareExchange(ref s_dataArray, newDataArray, dataArray) != dataArray) + continue; // Someone else got the free one, try again + + dataArray = newDataArray; + } + + // If we haven't stored this object in the array yet, do so now. Then we need to make another pass through + // the loop, in case another thread resized the array before we made this update. + if (s_dataArray[dataIndex] == null) + { + // Full fence so this write can't move past subsequent reads. + Interlocked.Exchange(ref dataArray[dataIndex], data); + continue; + } + + // We're already in the array, so we're done. + Debug.Assert(dataArray[dataIndex] == data); + overlapped->_dataIndex = dataIndex; + return overlapped; + } + } + + private void SetData(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionContext) + { + Debug.Assert(callback != null); + + OverlappedData data = Data; + + data._callback = callback; + data._state = state; + data._executionContext = flowExecutionContext ? ExecutionContext.Capture() : null; + data._preAllocated = preAllocated; + + // + // pinData can be any blittable type to be pinned, *or* an instance of object[] each element of which refers to + // an instance of a blittable type to be pinned. + // + if (pinData != null) + { + object[] objArray = pinData as object[]; + if (objArray != null && objArray.GetType() == typeof(object[])) + { + if (data._pinnedData == null || data._pinnedData.Length < objArray.Length) + Array.Resize(ref data._pinnedData, objArray.Length); + + for (int i = 0; i < objArray.Length; i++) + { + if (!data._pinnedData[i].IsAllocated) + data._pinnedData[i] = GCHandle.Alloc(objArray[i], GCHandleType.Pinned); + else + data._pinnedData[i].Target = objArray[i]; + } + } + else + { + if (data._pinnedData == null) + data._pinnedData = new GCHandle[1]; + + if (!data._pinnedData[0].IsAllocated) + data._pinnedData[0] = GCHandle.Alloc(pinData, GCHandleType.Pinned); + else + data._pinnedData[0].Target = pinData; + } + } + } + + internal static unsafe void Free(Win32ThreadPoolNativeOverlapped* overlapped) + { + // Reset all data. + overlapped->Data.Reset(); + overlapped->_overlapped = default(NativeOverlapped); + + // Add to the free list. + while (true) + { + IntPtr freePtr = Volatile.Read(ref s_freeList); + overlapped->_nextFree = freePtr; + + if (Interlocked.CompareExchange(ref s_freeList, (IntPtr)overlapped, freePtr) == freePtr) + break; + } + } + + internal static unsafe NativeOverlapped* ToNativeOverlapped(Win32ThreadPoolNativeOverlapped* overlapped) + { + return (NativeOverlapped*)overlapped; + } + + internal static unsafe Win32ThreadPoolNativeOverlapped* FromNativeOverlapped(NativeOverlapped* overlapped) + { + return (Win32ThreadPoolNativeOverlapped*)overlapped; + } + + internal static unsafe void CompleteWithCallback(uint errorCode, uint bytesWritten, Win32ThreadPoolNativeOverlapped* overlapped) + { + OverlappedData data = overlapped->Data; + + Debug.Assert(!data._completed); + data._completed = true; + + if (data._executionContext == null) + { + data._callback(errorCode, bytesWritten, ToNativeOverlapped(overlapped)); + return; + } + + ContextCallback callback = s_executionContextCallback; + if (callback == null) + s_executionContextCallback = callback = OnExecutionContextCallback; + + // Get an args object from the per-thread cache. + ExecutionContextCallbackArgs args = t_executionContextCallbackArgs; + if (args == null) + args = new ExecutionContextCallbackArgs(); + + t_executionContextCallbackArgs = null; + + args._errorCode = errorCode; + args._bytesWritten = bytesWritten; + args._overlapped = overlapped; + args._data = data; + + ExecutionContext.Run(data._executionContext, callback, args); + } + + private static unsafe void OnExecutionContextCallback(object? state) + { + Debug.Assert(state != null); + ExecutionContextCallbackArgs args = (ExecutionContextCallbackArgs)state; + + uint errorCode = args._errorCode; + uint bytesWritten = args._bytesWritten; + Win32ThreadPoolNativeOverlapped* overlapped = args._overlapped; + OverlappedData data = args._data; + + // Put the args object back in the per-thread cache, now that we're done with it. + args._data = null; + t_executionContextCallbackArgs = args; + + data._callback(errorCode, bytesWritten, ToNativeOverlapped(overlapped)); + } + + internal bool IsUserObject(byte[]? buffer) => ReferenceEquals(Data._pinnedData, buffer); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs new file mode 100644 index 00000000000000..21235042d42469 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Threading +{ + public sealed class PreAllocatedOverlapped : IDisposable, IDeferredDisposable + { + internal unsafe readonly Win32ThreadPoolNativeOverlapped* _overlapped; + private DeferredDisposableLifetime _lifetime; + + [CLSCompliant(false)] + public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) : + this(callback, state, pinData, flowExecutionContext: true) + { + } + + [CLSCompliant(false)] + public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => + new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); + + private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + { + if (callback == null) + throw new ArgumentNullException(nameof(callback)); + + _overlapped = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); + } + + internal bool AddRef() + { + return _lifetime.AddRef(); + } + + internal void Release() + { + _lifetime.Release(this); + } + + public void Dispose() + { + _lifetime.Dispose(this); + GC.SuppressFinalize(this); + } + + ~PreAllocatedOverlapped() + { + Dispose(); + } + + unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) + { + if (_overlapped != null) + { + if (disposed) + Win32ThreadPoolNativeOverlapped.Free(_overlapped); + else + *Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(_overlapped) = default(NativeOverlapped); + } + } + + internal unsafe bool IsUserObject(byte[]? buffer) => _overlapped->IsUserObject(buffer); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.CoreRT.cs new file mode 100644 index 00000000000000..617858d451794a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.CoreRT.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Threading; + +using Internal.Reflection.Augments; +using Internal.Reflection.Core.NonPortable; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +namespace System +{ + public abstract partial class Type : MemberInfo, IReflect + { + public bool IsInterface => (GetAttributeFlagsImpl() & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface; + + [Intrinsic] + public static Type? GetTypeFromHandle(RuntimeTypeHandle handle) => handle.IsNull ? null : GetTypeFromEETypePtr(handle.ToEETypePtr()); + + internal static Type GetTypeFromEETypePtr(EETypePtr eeType) + { + // If we support the writable data section on EETypes, the runtime type associated with the MethodTable + // is cached there. If writable data is not supported, we need to do a lookup in the runtime type + // unifier's hash table. + if (Internal.Runtime.MethodTable.SupportsWritableData) + { + ref GCHandle handle = ref eeType.GetWritableData(); + if (handle.IsAllocated) + { + return Unsafe.As(handle.Target); + } + else + { + return GetTypeFromEETypePtrSlow(eeType, ref handle); + } + } + else + { + return RuntimeTypeUnifier.GetRuntimeTypeForEEType(eeType); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static Type GetTypeFromEETypePtrSlow(EETypePtr eeType, ref GCHandle handle) + { + // Note: this is bypassing the "fast" unifier cache (based on a simple IntPtr + // identity of MethodTable pointers). There is another unifier behind that cache + // that ensures this code is race-free. + Type result = RuntimeTypeUnifier.GetRuntimeTypeBypassCache(eeType); + GCHandle tempHandle = GCHandle.Alloc(result); + + // We don't want to leak a handle if there's a race + if (Interlocked.CompareExchange(ref Unsafe.As(ref handle), (IntPtr)tempHandle, default) != default) + { + tempHandle.Free(); + } + + return result; + } + + [Intrinsic] + [RequiresUnreferencedCode("The type might be removed")] + public static Type GetType(string typeName) => GetType(typeName, throwOnError: false, ignoreCase: false); + [Intrinsic] + [RequiresUnreferencedCode("The type might be removed")] + public static Type GetType(string typeName, bool throwOnError) => GetType(typeName, throwOnError: throwOnError, ignoreCase: false); + [Intrinsic] + [RequiresUnreferencedCode("The type might be removed")] + public static Type GetType(string typeName, bool throwOnError, bool ignoreCase) => GetType(typeName, null, null, throwOnError: throwOnError, ignoreCase: ignoreCase); + + [Intrinsic] + [RequiresUnreferencedCode("The type might be removed")] + public static Type GetType(string typeName, Func? assemblyResolver, Func? typeResolver) => GetType(typeName, assemblyResolver, typeResolver, throwOnError: false, ignoreCase: false); + [Intrinsic] + [RequiresUnreferencedCode("The type might be removed")] + public static Type GetType(string typeName, Func? assemblyResolver, Func? typeResolver, bool throwOnError) => GetType(typeName, assemblyResolver, typeResolver, throwOnError: throwOnError, ignoreCase: false); + [Intrinsic] + [RequiresUnreferencedCode("The type might be removed")] + public static Type GetType(string typeName, Func? assemblyResolver, Func? typeResolver, bool throwOnError, bool ignoreCase) => RuntimeAugments.Callbacks.GetType(typeName, assemblyResolver, typeResolver, throwOnError: throwOnError, ignoreCase: ignoreCase, defaultAssembly: null); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.Internal.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.Internal.cs new file mode 100644 index 00000000000000..53867b10b0f3ae --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.Internal.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; + +namespace System +{ + // + // This file contains methods on Type that are internal to the framework. + // + // Before adding new entries to this, ask yourself: is it ever referenced by System.Private.CoreLib? + // If not, don't put it here. Put it on RuntimeTypeInfo instead. + // + // Some of these "internal" methods are declared "public" because both Reflection.Core and System.Private.CoreLib need to reference them. + // + public abstract partial class Type + { + /// + /// Return Type.Name if sufficient metadata is available to do so - otherwise return null. + /// + public string InternalNameIfAvailable + { + get + { + Type? ignore = null; + return InternalGetNameIfAvailable(ref ignore); + } + } + + /// + /// Return Type.Name if sufficient metadata is available to do so - otherwise return null and set "rootCauseForFailure" to an object to pass to MissingMetadataException. + /// + public virtual string InternalGetNameIfAvailable(ref Type? rootCauseForFailure) => Name; + + /// + /// Return Type.Name if sufficient metadata is available to do so - otherwise return a default (non-null) string. + /// + internal string NameOrDefault + { + get + { + string name = InternalNameIfAvailable; + return name != null ? name : DefaultTypeNameWhenMissingMetadata; + } + } + + /// + /// Return Type.FullName if sufficient metadata is available to do so - otherwise return a default (non-null) string. + /// + internal string FullNameOrDefault + { + get + { + // First, see if Type.Name is available. If Type.Name is available, then we can be reasonably confident that it is safe to call Type.FullName. + // We'll still wrap the call in a try-catch as a failsafe. + if (InternalNameIfAvailable == null) + return DefaultTypeNameWhenMissingMetadata; + + try + { + return FullName; + } + catch (MissingMetadataException) + { + return DefaultTypeNameWhenMissingMetadata; + } + } + } + + // + // This is a port of the desktop CLR's RuntimeType.FormatTypeName() routine. This routine is used by various Reflection ToString() methods + // to display the name of a type. Do not use for any other purpose as it inherits some pretty quirky desktop behavior. + // + // The Project N version takes a raw metadata handle rather than a completed type so that it remains robust in the face of missing metadata. + // + public string FormatTypeNameForReflection() + { + try + { + // Though we wrap this in a try-catch as a failsafe, this code must still strive to avoid triggering MissingMetadata exceptions + // (non-error exceptions are very annoying when debugging.) + + // Legacy: this doesn't make sense, why use only Name for nested types but otherwise + // ToString() which contains namespace. + Type rootElementType = this; + while (rootElementType.HasElementType) + rootElementType = rootElementType.GetElementType()!; + if (rootElementType.IsNested) + { + string name = InternalNameIfAvailable; + return name == null ? DefaultTypeNameWhenMissingMetadata : name; + } + + // Legacy: why removing "System"? Is it just because C# has keywords for these types? + // If so why don't we change it to lower case to match the C# keyword casing? + string typeName = ToString(); + if (typeName.StartsWith("System.")) + { + if (rootElementType.IsPrimitive || rootElementType == typeof(void)) + { + typeName = typeName.Substring("System.".Length); + } + } + return typeName; + } + catch (Exception) + { + return DefaultTypeNameWhenMissingMetadata; + } + } + + public const string DefaultTypeNameWhenMissingMetadata = "UnknownType"; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypeLoadException.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypeLoadException.CoreRT.cs new file mode 100644 index 00000000000000..9ca654774c8131 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypeLoadException.CoreRT.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System +{ + public partial class TypeLoadException + { + internal TypeLoadException(string message, string typeName) + : base(message) + { + HResult = HResults.COR_E_TYPELOAD; + _className = typeName; + } + + private void SetMessageField() + { + if (_message == null) + _message = SR.Arg_TypeLoadException; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypeUnificationKey.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypeUnificationKey.cs new file mode 100644 index 00000000000000..48f437c544ec6b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypeUnificationKey.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Implements System.Type +// + +using System; +using System.Diagnostics; + +namespace System +{ + // + // Type doesn't implement IEquatable which makes it impossible to use a key in our unification tables. + // This wrapper is here to compensate for that. + // + internal struct TypeUnificationKey : IEquatable + { + public TypeUnificationKey(Type type) + { + Debug.Assert(type != null); + Type = type; + } + + public override bool Equals(object? obj) + { + if (!(obj is TypeUnificationKey)) + return false; + return Equals((TypeUnificationKey)obj); + } + + public bool Equals(TypeUnificationKey other) + { + return Type.Equals(other.Type); + } + + public override int GetHashCode() + { + return Type.GetHashCode(); + } + + public Type Type { get; } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs new file mode 100644 index 00000000000000..cb0878884a8ee4 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Internal.Runtime.CompilerServices; +using Internal.Reflection.Augments; + +namespace System +{ + [CLSCompliant(false)] + [StructLayout(LayoutKind.Sequential)] + public ref struct TypedReference + { + // Do not change the ordering of these fields. The JIT has a dependency on this layout. + private readonly ByReference _value; + private readonly RuntimeTypeHandle _typeHandle; + + private TypedReference(object target, int offset, RuntimeTypeHandle typeHandle) + { + _value = new ByReference(ref Unsafe.Add(ref target.GetRawData(), offset)); + _typeHandle = typeHandle; + } + + public static TypedReference MakeTypedReference(object target, FieldInfo[] flds) + { + Type type; + int offset; + ReflectionAugments.ReflectionCoreCallbacks.MakeTypedReference(target, flds, out type, out offset); + return new TypedReference(target, offset, type.TypeHandle); + } + + public static Type? GetTargetType(TypedReference value) => Type.GetTypeFromHandle(value._typeHandle); + + public static RuntimeTypeHandle TargetTypeToken(TypedReference value) + { + if (value._typeHandle.IsNull) + throw new NullReferenceException(); // For compatibility; + return value._typeHandle; + } + + internal static RuntimeTypeHandle RawTargetTypeToken(TypedReference value) + { + return value._typeHandle; + } + + public static object ToObject(TypedReference value) + { + RuntimeTypeHandle typeHandle = value._typeHandle; + if (typeHandle.IsNull) + throw new ArgumentNullException(); // For compatibility. + + EETypePtr eeType = typeHandle.ToEETypePtr(); + if (eeType.IsValueType) + { + return RuntimeImports.RhBox(eeType, ref value.Value); + } + else if (eeType.IsPointer) + { + return RuntimeImports.RhBox(EETypePtr.EETypePtrOf(), ref value.Value); + } + else + { + return Unsafe.As(ref value.Value); + } + } + + public static void SetTypedReference(TypedReference target, object? value) { throw new NotSupportedException(); } + + public override bool Equals(object? o) { throw new NotSupportedException(SR.NotSupported_NYI); } + public override int GetHashCode() => _typeHandle.IsNull ? 0 : _typeHandle.GetHashCode(); + + // Not an api - declared public because of CoreLib/Reflection.Core divide. + public bool IsNull => _typeHandle.IsNull; + + internal ref byte Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref _value.Value; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ValueType.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ValueType.cs new file mode 100644 index 00000000000000..6f0463ec9acea8 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ValueType.cs @@ -0,0 +1,198 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================ +** +** +** +** Purpose: Base class for all value classes. +** +** +===========================================================*/ + +using System.Diagnostics.CodeAnalysis; +using System.Runtime; + +using Internal.Runtime.CompilerServices; +using Internal.Runtime.Augments; + +using Debug = System.Diagnostics.Debug; + +namespace System +{ + // CONTRACT with Runtime + // Place holder type for type hierarchy, Compiler/Runtime requires this class + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public abstract class ValueType + { + public override string? ToString() + { + return this.GetType().ToString(); + } + + private const int UseFastHelper = -1; + private const int GetNumFields = -1; + + // An override of this method will be injected by the compiler into all valuetypes that cannot be compared + // using a simple memory comparison. + // This API is a bit awkward because we want to avoid burning more than one vtable slot on this. + // When index == GetNumFields, this method is expected to return the number of fields of this + // valuetype. Otherwise, it returns the offset and type handle of the index-th field on this type. + internal virtual int __GetFieldHelper(int index, out EETypePtr eeType) + { + // Value types that don't override this method will use the fast path that looks at bytes, not fields. + Debug.Assert(index == GetNumFields); + eeType = default; + return UseFastHelper; + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == null || obj.EETypePtr != this.EETypePtr) + return false; + + int numFields = __GetFieldHelper(GetNumFields, out _); + + ref byte thisRawData = ref this.GetRawData(); + ref byte thatRawData = ref obj.GetRawData(); + + if (numFields == UseFastHelper) + { + // Sanity check - if there are GC references, we should not be comparing bytes + Debug.Assert(!this.EETypePtr.HasPointers); + + // Compare the memory + int valueTypeSize = (int)this.EETypePtr.ValueTypeSize; + for (int i = 0; i < valueTypeSize; i++) + { + if (Unsafe.Add(ref thisRawData, i) != Unsafe.Add(ref thatRawData, i)) + return false; + } + } + else + { + // Foreach field, box and call the Equals method. + for (int i = 0; i < numFields; i++) + { + int fieldOffset = __GetFieldHelper(i, out EETypePtr fieldType); + + // Fetch the value of the field on both types + object thisField = RuntimeImports.RhBoxAny(ref Unsafe.Add(ref thisRawData, fieldOffset), fieldType); + object thatField = RuntimeImports.RhBoxAny(ref Unsafe.Add(ref thatRawData, fieldOffset), fieldType); + + // Compare the fields + if (thisField == null) + { + if (thatField != null) + return false; + } + else if (!thisField.Equals(thatField)) + { + return false; + } + } + } + + return true; + } + + public override int GetHashCode() + { + int hashCode = this.EETypePtr.GetHashCode(); + + hashCode ^= GetHashCodeImpl(); + + return hashCode; + } + + private int GetHashCodeImpl() + { + int numFields = __GetFieldHelper(GetNumFields, out _); + + if (numFields == UseFastHelper) + return FastGetValueTypeHashCodeHelper(this.EETypePtr, ref this.GetRawData()); + + return RegularGetValueTypeHashCode(this.EETypePtr, ref this.GetRawData(), numFields); + } + + private static int FastGetValueTypeHashCodeHelper(EETypePtr type, ref byte data) + { + // Sanity check - if there are GC references, we should not be hashing bytes + Debug.Assert(!type.HasPointers); + + int size = (int)type.ValueTypeSize; + int hashCode = 0; + + for (int i = 0; i < size / 4; i++) + { + hashCode ^= Unsafe.As(ref Unsafe.Add(ref data, i * 4)); + } + + return hashCode; + } + + private int RegularGetValueTypeHashCode(EETypePtr type, ref byte data, int numFields) + { + int hashCode = 0; + + // We only take the hashcode for the first non-null field. That's what the CLR does. + for (int i = 0; i < numFields; i++) + { + int fieldOffset = __GetFieldHelper(i, out EETypePtr fieldType); + ref byte fieldData = ref Unsafe.Add(ref data, fieldOffset); + + Debug.Assert(!fieldType.IsPointer); + + if (fieldType.ElementType == Internal.Runtime.EETypeElementType.Single) + { + hashCode = Unsafe.Read(ref fieldData).GetHashCode(); + } + else if (fieldType.ElementType == Internal.Runtime.EETypeElementType.Double) + { + hashCode = Unsafe.Read(ref fieldData).GetHashCode(); + } + else if (fieldType.IsPrimitive) + { + hashCode = FastGetValueTypeHashCodeHelper(fieldType, ref fieldData); + } + else if (fieldType.IsValueType) + { + // We have no option but to box since this value type could have + // GC pointers (we could find out if we want though), or fields of type Double/Single (we can't + // really find out). Double/Single have weird requirements around -0.0 and +0.0. + // If this boxing becomes a problem, we could build a piece of infrastructure that determines the slot + // of __GetFieldHelper, decodes the unboxing stub pointed to by the slot to the real target + // (we already have that part), and calls the entrypoint that expects a byref `this`, and use the + // data to decide between calling fast or regular hashcode helper. + var fieldValue = (ValueType)RuntimeImports.RhBox(fieldType, ref fieldData); + if (fieldValue != null) + { + hashCode = fieldValue.GetHashCodeImpl(); + } + else + { + // nullable type with no value, try next + continue; + } + } + else + { + object fieldValue = Unsafe.Read(ref fieldData); + if (fieldValue != null) + { + hashCode = fieldValue.GetHashCode(); + } + else + { + // null object reference, try next + continue; + } + } + break; + } + + return hashCode; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/WeakReference.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/WeakReference.CoreRT.cs new file mode 100644 index 00000000000000..41e765934c3013 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/WeakReference.CoreRT.cs @@ -0,0 +1,204 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Threading; +using System.Diagnostics; + +using Internal.Runtime.Augments; + +namespace System +{ + public partial class WeakReference + { + // If you fix bugs here, please fix them in WeakReference at the same time. + + // Most methods using m_handle should use GC.KeepAlive(this) to avoid potential handle recycling + // attacks (i.e. if the WeakReference instance is finalized away underneath you when you're still + // handling a cached value of the handle then the handle could be freed and reused). + internal volatile IntPtr m_handle; + internal bool m_IsLongReference; + + private void Create(object? target, bool trackResurrection) + { + m_IsLongReference = trackResurrection; + m_handle = GCHandle.ToIntPtr(GCHandle.Alloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak)); + + if (target != null) + { + // Set the conditional weak table if the target is a __ComObject. + TrySetComTarget(target); + } + } + + //Determines whether or not this instance of WeakReference still refers to an object + //that has not been collected. + // + public virtual bool IsAlive + { + get + { + IntPtr h = m_handle; + + // In determining whether it is valid to use this object, we need to at least expose this + // without throwing an exception. + if (default(IntPtr) == h) + return false; + + bool result = (RuntimeImports.RhHandleGet(h) != null || TryGetComTarget() != null); + + // We want to ensure that if the target is live, then we will + // return it to the user. We need to keep this WeakReference object + // live so m_handle doesn't get set to 0 or reused. + // Since m_handle is volatile, the following statement will + // guarantee the weakref object is live till the following + // statement. + return (m_handle == default(IntPtr)) ? false : result; + } + } + + //Returns a boolean indicating whether or not we're tracking objects until they're collected (true) + //or just until they're finalized (false). + // + public virtual bool TrackResurrection + { + get { return m_IsLongReference; } + } + + //Gets the Object stored in the handle if it's accessible. + // Or sets it. + // + public virtual object? Target + { + get + { + IntPtr h = m_handle; + // Should only happen when used illegally, like using a + // WeakReference from a finalizer. + if (default(IntPtr) == h) + return null; + + object o = RuntimeImports.RhHandleGet(h); + + if (o == null) + { + o = TryGetComTarget(); + } + + // We want to ensure that if the target is live, then we will + // return it to the user. We need to keep this WeakReference object + // live so m_handle doesn't get set to 0 or reused. + // Since m_handle is volatile, the following statement will + // guarantee the weakref object is live till the following + // statement. + return (m_handle == default(IntPtr)) ? null : o; + } + + set + { + IntPtr h = m_handle; + if (h == default(IntPtr)) + throw new InvalidOperationException(SR.InvalidOperation_HandleIsNotInitialized); + +#if false + // There is a race w/ finalization where m_handle gets set to + // NULL and the WeakReference becomes invalid. Here we have to + // do the following in order: + // + // 1. Get the old object value + // 2. Get m_handle + // 3. HndInterlockedCompareExchange(m_handle, newValue, oldValue); + // + // If the interlocked-cmp-exchange fails, then either we lost a race + // with another updater, or we lost a race w/ the finalizer. In + // either case, we can just let the other guy win. + Object oldValue = RuntimeImports.RhHandleGet(h); + h = m_handle; + if (h == default(IntPtr)) + throw new InvalidOperationException(SR.InvalidOperation_HandleIsNotInitialized); + GCHandle.InternalCompareExchange(h, value, oldValue, false /* isPinned */); +#else + // The above logic seems somewhat paranoid and even wrong. + // + // 1. It's the GC rather than any finalizer that clears weak handles (indeed there's no guarantee any finalizer is involved + // at all). + // 2. Retrieving the object from the handle atomically creates a strong reference to it, so + // as soon as we get the handle contents above (before it's even assigned into oldValue) + // the only race we can be in is with another setter. + // 3. We don't really care who wins in a race between two setters: last update wins is just + // as good as first update wins. If there was a race with the "finalizer" though, we'd + // probably want the setter to win (otherwise we could nullify a set just because it raced + // with the old object becoming unreferenced). + // + // The upshot of all of this is that we can just go ahead and set the handle. I suspect that + // with further review I could prove that this class doesn't need to mess around with raw + // IntPtrs at all and can simply use GCHandle directly, avoiding all these internal calls. + + // Check whether the new value is __COMObject. If so, add the new entry to conditional weak table. + TrySetComTarget(value); + RuntimeImports.RhHandleSet(h, value); +#endif + + // Ensure we don't have any handle recycling attacks in this + // method where the finalizer frees the handle. + GC.KeepAlive(this); + } + } + + /// + /// This method checks whether the target to the weakreference is a native COMObject in which case the native object might still be alive although the RuntimeHandle could be null. + /// Hence we check in the conditionalweaktable maintained by the System.private.Interop.dll that maps weakreferenceInstance->nativeComObject to check whether the native COMObject is alive or not. + /// and gets\create a new RCW in case it is alive. + /// + /// + private object? TryGetComTarget() + { +#if ENABLE_WINRT + WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks; + if (callbacks != null) + { + return callbacks.GetCOMWeakReferenceTarget(this); + } + else + { + Debug.Fail("WinRTInteropCallback is null"); + } +#endif // ENABLE_WINRT + return null; + } + + /// + /// This method notifies the System.private.Interop.dll to update the conditionalweaktable for weakreferenceInstance->target in case the target is __ComObject. This ensures that we have a means to + /// go from the managed weak reference to the actual native object even though the managed counterpart might have been collected. + /// + /// + private void TrySetComTarget(object? target) + { +#if ENABLE_WINRT + WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks; + if (callbacks != null) + { + callbacks.SetCOMWeakReferenceTarget(this, target); + } + else + { + Debug.Fail("WinRTInteropCallback is null"); + } +#endif // ENABLE_WINRT + } + + // Free all system resources associated with this reference. + ~WeakReference() + { +#pragma warning disable 420 // FYI - ref m_handle causes this. I asked the C# team to add in "ref volatile T" as a parameter type in a future version. + IntPtr handle = Interlocked.Exchange(ref m_handle, default(IntPtr)); +#pragma warning restore 420 + if (handle != default(IntPtr)) + ((GCHandle)handle).Free(); + } + + private bool IsTrackResurrection() => m_IsLongReference; + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/WeakReference.T.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/WeakReference.T.CoreRT.cs new file mode 100644 index 00000000000000..e98c423c4d9388 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/WeakReference.T.CoreRT.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; +using System.Runtime.InteropServices; +using System.Threading; + +using Internal.Runtime.CompilerServices; + +namespace System +{ + public sealed partial class WeakReference + where T : class? + { + // If you fix bugs here, please fix them in WeakReference at the same time. + + internal volatile IntPtr m_handle; + private bool m_trackResurrection; + + + //Creates a new WeakReference that keeps track of target. + // + private void Create(T target, bool trackResurrection) + { + m_handle = (IntPtr)GCHandle.Alloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak); + m_trackResurrection = trackResurrection; + + if (target != null) + { + // Set the conditional weak table if the target is a __ComObject. + TrySetComTarget(target); + } + } + + public void SetTarget(T target) + { + if (m_handle == default(IntPtr)) + throw new InvalidOperationException(SR.InvalidOperation_HandleIsNotInitialized); + + // Update the conditionalweakTable in case the target is __ComObject. + TrySetComTarget(target); + + RuntimeImports.RhHandleSet(m_handle, target); + GC.KeepAlive(this); + } + + private T? Target + { + get + { + IntPtr h = m_handle; + + // Should only happen for corner cases, like using a + // WeakReference from a finalizer. + if (default(IntPtr) == h) + return default; + + T? target = Unsafe.As(RuntimeImports.RhHandleGet(h)); + + if (target == null) + { + target = TryGetComTarget() as T; + } + + // We want to ensure that the handle was still alive when we fetched the target, + // so we double check m_handle here. Note that the reading of the handle + // value has to be volatile for this to work, but reading of m_handle does not. + + if (default(IntPtr) == m_handle) + return default; + + return target; + } + } + + /// + /// This method checks whether the target to the weakreference is a native COMObject in which case the native object might still be alive although the RuntimeHandle could be null. + /// Hence we check in the conditionalweaktable maintained by the System.Private.Interop.dll that maps weakreferenceInstance->nativeComObject to check whether the native COMObject is alive or not. + /// and gets\create a new RCW in case it is alive. + /// + /// + private object? TryGetComTarget() + { +#if ENABLE_WINRT + WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks; + if (callbacks != null) + { + return callbacks.GetCOMWeakReferenceTarget(this); + } + else + { + Debug.Fail("WinRTInteropCallback is null"); + } +#endif // ENABLE_WINRT + return null; + } + + /// + /// This method notifies the System.Private.Interop.dll to update the conditionalweaktable for weakreferenceInstance->target in case the target is __ComObject. This ensures that we have a means to + /// go from the managed weak reference to the actual native object even though the managed counterpart might have been collected. + /// + /// + private void TrySetComTarget(object? target) + { +#if ENABLE_WINRT + WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks; + if (callbacks != null) + callbacks.SetCOMWeakReferenceTarget(this, target); + else + { + Debug.Fail("WinRTInteropCallback is null"); + } +#endif // ENABLE_WINRT + } + + // Free all system resources associated with this reference. + // + // Note: The WeakReference finalizer is not usually run, but + // treated specially in gc.cpp's ScanForFinalization + // This is needed for subclasses deriving from WeakReference, however. + // Additionally, there may be some cases during shutdown when we run this finalizer. + ~WeakReference() + { + IntPtr old_handle = m_handle; + if (old_handle != default(IntPtr)) + { + if (old_handle == Interlocked.CompareExchange(ref m_handle, default(IntPtr), old_handle)) + ((GCHandle)old_handle).Free(); + } + } + + private bool IsTrackResurrection() => m_trackResurrection; + } +} diff --git a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionCoreCallbacksImplementation.cs b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionCoreCallbacksImplementation.cs new file mode 100644 index 00000000000000..3a0b5667c67387 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionCoreCallbacksImplementation.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using Internal.Reflection.Augments; +using Internal.Runtime.Augments; + +namespace Internal.Reflection +{ + internal class ReflectionCoreCallbacksImplementation : ReflectionCoreCallbacks + { + public override EnumInfo GetEnumInfo(Type type) + { + return new EnumInfo( + RuntimeAugments.GetEnumUnderlyingType(type.TypeHandle), + rawValues: Array.Empty(), + names: Array.Empty(), + isFlags: false); + } + + public override object ActivatorCreateInstance( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type, bool nonPublic) => throw new NotSupportedException(SR.Reflection_Disabled); + public override object ActivatorCreateInstance( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes) => throw new NotSupportedException(SR.Reflection_Disabled); + public override Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure) => throw new NotSupportedException(SR.Reflection_Disabled); + public override Delegate CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure) => throw new NotSupportedException(SR.Reflection_Disabled); + [RequiresUnreferencedCode("The target method might be removed")] + public override Delegate CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure) => throw new NotSupportedException(SR.Reflection_Disabled); + public override Delegate CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method, bool ignoreCase, bool throwOnBindFailure) => throw new NotSupportedException(SR.Reflection_Disabled); + public override FieldInfo GetFieldFromHandle(RuntimeFieldHandle runtimeFieldHandle) => throw new NotSupportedException(SR.Reflection_Disabled); + public override FieldInfo GetFieldFromHandle(RuntimeFieldHandle runtimeFieldHandle, RuntimeTypeHandle declaringTypeHandle) => throw new NotSupportedException(SR.Reflection_Disabled); + public override IntPtr GetFunctionPointer(RuntimeMethodHandle runtimeMethodHandle, RuntimeTypeHandle declaringTypeHandle) => throw new NotSupportedException(SR.Reflection_Disabled); + public override EventInfo GetImplicitlyOverriddenBaseClassEvent(EventInfo e) => throw new NotSupportedException(SR.Reflection_Disabled); + public override MethodInfo GetImplicitlyOverriddenBaseClassMethod(MethodInfo m) => throw new NotSupportedException(SR.Reflection_Disabled); + public override PropertyInfo GetImplicitlyOverriddenBaseClassProperty(PropertyInfo p) => throw new NotSupportedException(SR.Reflection_Disabled); + public override Assembly[] GetLoadedAssemblies() => throw new NotSupportedException(SR.Reflection_Disabled); + public override MethodBase GetMethodFromHandle(RuntimeMethodHandle runtimeMethodHandle) => throw new NotSupportedException(SR.Reflection_Disabled); + public override MethodBase GetMethodFromHandle(RuntimeMethodHandle runtimeMethodHandle, RuntimeTypeHandle declaringTypeHandle) => throw new NotSupportedException(SR.Reflection_Disabled); +#if FEATURE_COMINTEROP + public override Type GetTypeFromCLSID(Guid clsid, string server, bool throwOnError) => throw new NotSupportedException(SR.Reflection_Disabled); +#endif + public override Assembly Load(AssemblyName refName, bool throwOnFileNotFound) => throw new NotSupportedException(SR.Reflection_Disabled); + public override Assembly Load(byte[] rawAssembly, byte[] pdbSymbolStore) => throw new NotSupportedException(SR.Reflection_Disabled); + public override Assembly Load(string assemblyPath) => throw new NotSupportedException(SR.Reflection_Disabled); + public override void MakeTypedReference(object target, FieldInfo[] flds, out Type type, out int offset) => throw new NotSupportedException(SR.Reflection_Disabled); + public override void RunModuleConstructor(Module module) => throw new NotSupportedException(SR.Reflection_Disabled); + } +} diff --git a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionExecutionDomainCallbacksImplementation.cs b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionExecutionDomainCallbacksImplementation.cs new file mode 100644 index 00000000000000..61c1574840b5c9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionExecutionDomainCallbacksImplementation.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; + +using Internal.Runtime.Augments; + +namespace Internal.Reflection +{ + internal class ReflectionExecutionDomainCallbacksImplementation : ReflectionExecutionDomainCallbacks + { + public override Exception CreateMissingMetadataException(Type typeWithMissingMetadata) => throw new NotImplementedException(); + public override Type GetArrayTypeForHandle(RuntimeTypeHandle typeHandle) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle); + public override Assembly GetAssemblyForHandle(RuntimeTypeHandle typeHandle) => new RuntimeAssemblyInfo(typeHandle); + public override string GetBetterDiagnosticInfoIfAvailable(RuntimeTypeHandle runtimeTypeHandle) => null; + public override Type GetByRefTypeForHandle(RuntimeTypeHandle typeHandle) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle); + public override Type GetConstructedGenericTypeForHandle(RuntimeTypeHandle typeHandle) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle); + public override MethodInfo GetDelegateMethod(Delegate del) => throw new NotSupportedException(SR.Reflection_Disabled); + public override Exception GetExceptionForHR(int hr) => throw new NotImplementedException(); + public override Type GetMdArrayTypeForHandle(RuntimeTypeHandle typeHandle, int rank) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle); + public override MethodBase GetMethodBaseFromStartAddressIfAvailable(IntPtr methodStartAddress) => null; + public override Type GetNamedTypeForHandle(RuntimeTypeHandle typeHandle, bool isGenericTypeDefinition) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle); + public override Type GetPointerTypeForHandle(RuntimeTypeHandle typeHandle) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle); + public override Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, string defaultAssembly) => throw new NotSupportedException(SR.Reflection_Disabled); + public override RuntimeTypeHandle GetTypeHandleIfAvailable(Type type) => type.TypeHandle; + public override bool IsReflectionBlocked(RuntimeTypeHandle typeHandle) => false; + public override bool SupportsReflection(Type type) => false; + public override bool TryGetDefaultParameterValue(object defaultParametersContext, RuntimeTypeHandle thType, int argIndex, out object defaultValue) + { + defaultValue = null; + return false; + } + public override IntPtr TryGetStaticClassConstructionContext(RuntimeTypeHandle runtimeTypeHandle) => throw new NotSupportedException(SR.Reflection_Disabled); + } +} diff --git a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/RuntimeAssemblyInfo.cs b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/RuntimeAssemblyInfo.cs new file mode 100644 index 00000000000000..71798018026c14 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/RuntimeAssemblyInfo.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection; + +using Internal.Reflection.Core.NonPortable; + +namespace Internal.Reflection +{ + internal sealed class RuntimeAssemblyInfo : RuntimeAssembly + { + private readonly RuntimeTypeHandle _moduleType; + + public RuntimeAssemblyInfo(RuntimeTypeHandle moduleType) + { + _moduleType = moduleType; + } + + public override bool Equals(object? o) + { + return o is RuntimeAssemblyInfo other && other._moduleType.Equals(_moduleType); + } + + public override int GetHashCode() + { + return _moduleType.GetHashCode(); + } + + public override IEnumerable CustomAttributes => new List(); + } +} diff --git a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/RuntimeTypeInfo.cs b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/RuntimeTypeInfo.cs new file mode 100644 index 00000000000000..5788e64c17540a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/RuntimeTypeInfo.cs @@ -0,0 +1,278 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; + +using Internal.Runtime.Augments; +using Internal.Reflection.Augments; +using Internal.Reflection.Core.NonPortable; +using System.Collections.Generic; + +namespace Internal.Reflection +{ + internal sealed class RuntimeTypeInfo : RuntimeType + { + private readonly RuntimeTypeHandle _typeHandle; + + public RuntimeTypeInfo(RuntimeTypeHandle typeHandle) + { + _typeHandle = typeHandle; + } + + private bool DoNotThrowForNames => AppContext.TryGetSwitch("Switch.System.Reflection.Disabled.DoNotThrowForNames", out bool doNotThrow) && doNotThrow; + + private bool DoNotThrowForAssembly => AppContext.TryGetSwitch("Switch.System.Reflection.Disabled.DoNotThrowForAssembly", out bool doNotThrow) && doNotThrow; + + private bool DoNotThrowForAttributes => AppContext.TryGetSwitch("Switch.System.Reflection.Disabled.DoNotThrowForAttributes", out bool doNotThrow) && doNotThrow; + + public override RuntimeTypeHandle TypeHandle => _typeHandle; + + public override bool IsGenericType => RuntimeAugments.IsGenericTypeDefinition(_typeHandle) || RuntimeAugments.IsGenericType(_typeHandle); + + public override string Name => DoNotThrowForNames ? RuntimeAugments.GetLastResortString(_typeHandle) : throw new NotSupportedException(SR.Reflection_Disabled); + + public override string Namespace => DoNotThrowForNames ? "" : throw new NotSupportedException(SR.Reflection_Disabled); + + public override string FullName => Name; + + public override string AssemblyQualifiedName => throw new NotSupportedException(SR.Reflection_Disabled); + + public override Assembly Assembly => DoNotThrowForAssembly ? Assembly.GetExecutingAssembly() : throw new NotSupportedException(SR.Reflection_Disabled); + + public override Module Module => throw new NotSupportedException(SR.Reflection_Disabled); + + public override Type UnderlyingSystemType => this; + + public override Guid GUID => throw new NotSupportedException(SR.Reflection_Disabled); + + public override Type BaseType + { + get + { + if (RuntimeAugments.TryGetBaseType(_typeHandle, out RuntimeTypeHandle baseTypeHandle)) + { + return GetRuntimeTypeInfo(baseTypeHandle); + } + + return null; + } + } + + public override bool IsByRefLike => RuntimeAugments.IsByRefLike(_typeHandle); + + protected override bool IsValueTypeImpl() + { + return RuntimeAugments.IsValueType(_typeHandle); + } + + protected override TypeCode GetTypeCodeImpl() + { + return ReflectionAugments.GetRuntimeTypeCode(this); + } + + public override string ToString() + { + return RuntimeAugments.GetLastResortString(_typeHandle); + } + + public override int GetHashCode() + { + return _typeHandle.GetHashCode(); + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => throw new NotSupportedException(SR.Reflection_Disabled); + + public override object[] GetCustomAttributes(bool inherit) => DoNotThrowForAttributes ? Array.Empty() : throw new NotSupportedException(SR.Reflection_Disabled); + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) => DoNotThrowForAttributes ? Array.Empty() : throw new NotSupportedException(SR.Reflection_Disabled); + + public override IList GetCustomAttributesData() => DoNotThrowForAttributes ? new List().AsReadOnly() : throw new NotSupportedException(SR.Reflection_Disabled); + + public override Type GetElementType() + { + if (RuntimeAugments.IsArrayType(_typeHandle) || RuntimeAugments.IsUnmanagedPointerType(_typeHandle) || RuntimeAugments.IsByRefType(_typeHandle)) + { + return GetRuntimeTypeInfo(RuntimeAugments.GetRelatedParameterTypeHandle(_typeHandle)); + } + + return null; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo GetEvent(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo[] GetEvents(BindingFlags bindingAttr) => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo GetField(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo[] GetFields(BindingFlags bindingAttr) => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063:UnrecognizedReflectionPattern", + Justification = "Linker doesn't recognize always throwing method. https://github.com/mono/linker/issues/2025")] + public override Type GetInterface(string name, bool ignoreCase) => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public override Type[] GetInterfaces() + { + int count = RuntimeAugments.GetInterfaceCount(_typeHandle); + if (count == 0) + return Type.EmptyTypes; + + Type[] result = new Type[count]; + for (int i = 0; i < result.Length; i++) + { + result[i] = GetRuntimeTypeInfo(RuntimeAugments.GetInterface(_typeHandle, i)); + } + + return result; + } + + [DynamicallyAccessedMembers(GetAllMembers)] + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type GetNestedType(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type[] GetNestedTypes(BindingFlags bindingAttr) => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) + => throw new NotSupportedException(SR.Reflection_Disabled); + + public override bool IsDefined(Type attributeType, bool inherit) => throw new NotSupportedException(SR.Reflection_Disabled); + + protected override TypeAttributes GetAttributeFlagsImpl() => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + => throw new NotSupportedException(SR.Reflection_Disabled); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) + => throw new NotSupportedException(SR.Reflection_Disabled); + + protected override bool HasElementTypeImpl() + { + return RuntimeAugments.IsArrayType(_typeHandle) || RuntimeAugments.IsUnmanagedPointerType(_typeHandle) || RuntimeAugments.IsByRefType(_typeHandle); + } + + protected override bool IsArrayImpl() + { + return RuntimeAugments.IsArrayType(_typeHandle); + } + + protected override bool IsByRefImpl() + { + return RuntimeAugments.IsByRefType(_typeHandle); + } + + protected override bool IsCOMObjectImpl() => throw new NotSupportedException(SR.Reflection_Disabled); + + protected override bool IsPointerImpl() + { + return RuntimeAugments.IsUnmanagedPointerType(_typeHandle); + } + + protected override bool IsPrimitiveImpl() + { + return RuntimeAugments.IsPrimitive(_typeHandle); + } + + internal static RuntimeTypeInfo GetRuntimeTypeInfo(RuntimeTypeHandle typeHandle) + { + return RuntimeTypeTable.Table.GetOrAdd(new RuntimeTypeHandleKey(typeHandle)); + } + + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] + public override Type MakeArrayType() + { + // We support enough of MakeArrayType to make enum operations work + if (IsPrimitive) + { + if (this == typeof(sbyte)) + return typeof(sbyte[]); + else if (this == typeof(byte)) + return typeof(byte[]); + else if (this == typeof(short)) + return typeof(short[]); + else if (this == typeof(ushort)) + return typeof(ushort[]); + else if (this == typeof(int)) + return typeof(int[]); + else if (this == typeof(uint)) + return typeof(uint[]); + else if (this == typeof(long)) + return typeof(long[]); + else if (this == typeof(ulong)) + return typeof(ulong[]); + } + + return base.MakeArrayType(); + } + + private sealed class RuntimeTypeTable : ConcurrentUnifierW + { + protected sealed override RuntimeTypeInfo Factory(RuntimeTypeHandleKey key) + { + return new RuntimeTypeInfo(key.TypeHandle); + } + + public static readonly RuntimeTypeTable Table = new RuntimeTypeTable(); + } + + internal struct RuntimeTypeHandleKey : IEquatable + { + public RuntimeTypeHandleKey(RuntimeTypeHandle typeHandle) + { + TypeHandle = typeHandle; + } + + public RuntimeTypeHandle TypeHandle { get; } + + public override bool Equals(object obj) + { + if (!(obj is RuntimeTypeHandleKey other)) + return false; + return Equals(other); + } + + public bool Equals(RuntimeTypeHandleKey other) + { + return TypeHandle.Equals(other.TypeHandle); + } + + public override int GetHashCode() + { + return TypeHandle.GetHashCode(); + } + } + + internal const DynamicallyAccessedMemberTypes GetAllMembers = DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | + DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; + } +} diff --git a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 00000000000000..9a402f3b206567 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Reflection; +using Internal.Reflection.Augments; +using Internal.Runtime.Augments; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + RuntimeAugments.Initialize(new ReflectionExecutionDomainCallbacksImplementation()); + ReflectionAugments.Initialize(new ReflectionCoreCallbacksImplementation()); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Resources/Strings.resx b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Resources/Strings.resx new file mode 100644 index 00000000000000..ee0f58a933adf0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Resources/Strings.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Hashtable's capacity overflowed and went negative. Check load factor, capacity and the current size of the table. + + + This operation is not available because the reflection support was disabled at compile time. + + diff --git a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/System.Private.DisabledReflection.csproj b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/System.Private.DisabledReflection.csproj new file mode 100644 index 00000000000000..4e84ebfc35dda5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/System.Private.DisabledReflection.csproj @@ -0,0 +1,19 @@ + + + + + + + System\Collections\Concurrent\ConcurrentUnifierW.cs + + + System\Collections\HashHelpers.cs + + + + + + + + + diff --git a/src/coreclr/nativeaot/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/coreclr/nativeaot/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 00000000000000..1893267d1521d8 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Internal.Runtime.Augments; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// Container class to run specific class constructors in a defined order. Since we can't + /// directly invoke class constructors in C#, they're renamed Initialize. + /// + public static class LibraryInitializer + { + public static void InitializeLibrary() + { + RuntimeAugments.InitializeInteropLookups(new RuntimeInteropData()); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.CoreRT.cs b/src/coreclr/nativeaot/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.CoreRT.cs new file mode 100644 index 00000000000000..376ee967cd4042 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.CoreRT.cs @@ -0,0 +1,188 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime.Augments; +using Internal.NativeFormat; +using Internal.Runtime.TypeLoader; +using Internal.Reflection.Execution; +using System.Runtime.InteropServices; + +namespace Internal.Runtime.CompilerHelpers +{ + internal partial class RuntimeInteropData + { + public override IntPtr GetForwardDelegateCreationStub(RuntimeTypeHandle delegateTypeHandle) + { + GetMarshallersForDelegate(delegateTypeHandle, out _, out _, out IntPtr delegateCreationStub); + if (delegateCreationStub == IntPtr.Zero) + throw new MissingInteropDataException(SR.DelegateMarshalling_MissingInteropData, Type.GetTypeFromHandle(delegateTypeHandle)); + return delegateCreationStub; + } + + public override IntPtr GetDelegateMarshallingStub(RuntimeTypeHandle delegateTypeHandle, bool openStaticDelegate) + { + GetMarshallersForDelegate(delegateTypeHandle, out IntPtr openStub, out IntPtr closedStub, out _); + IntPtr pStub = openStaticDelegate ? openStub : closedStub; + if (pStub == IntPtr.Zero) + throw new MissingInteropDataException(SR.DelegateMarshalling_MissingInteropData, Type.GetTypeFromHandle(delegateTypeHandle)); + return pStub; + } + + #region "Struct Data" + public override bool TryGetStructUnmarshalStub(RuntimeTypeHandle structureTypeHandle, out IntPtr unmarshalStub) + => TryGetMarshallersForStruct(structureTypeHandle, out _, out unmarshalStub, out _, out _, out _); + + public override bool TryGetStructMarshalStub(RuntimeTypeHandle structureTypeHandle, out IntPtr marshalStub) + => TryGetMarshallersForStruct(structureTypeHandle, out marshalStub, out _, out _, out _, out _); + + public override bool TryGetDestroyStructureStub(RuntimeTypeHandle structureTypeHandle, out IntPtr destroyStub, out bool hasInvalidLayout) + => TryGetMarshallersForStruct(structureTypeHandle, out _, out _, out destroyStub, out hasInvalidLayout, out _); + + public override bool TryGetStructUnsafeStructSize(RuntimeTypeHandle structureTypeHandle, out int size) + => TryGetMarshallersForStruct(structureTypeHandle, out _, out _, out _, out _, out size); + + public override bool TryGetStructFieldOffset(RuntimeTypeHandle structureTypeHandle, string fieldName, out bool structExists, out uint offset) + { + ExternalReferencesTable externalReferences; + NativeParser entryParser; + structExists = false; + if (TryGetStructData(structureTypeHandle, out externalReferences, out entryParser)) + { + structExists = true; + + uint mask = entryParser.GetUnsigned(); + if ((mask & InteropDataConstants.HasMarshallers) != 0) + { + // skip the first 4 IntPtrs(3 stubs and size) + entryParser.SkipInteger(); + entryParser.SkipInteger(); + entryParser.SkipInteger(); + entryParser.SkipInteger(); + } + + uint fieldCount = mask >> InteropDataConstants.FieldCountShift; + for (uint index = 0; index < fieldCount; index++) + { + string name = entryParser.GetString(); + offset = entryParser.GetUnsigned(); + if (name == fieldName) + { + return true; + } + } + } + offset = 0; + return false; + } + #endregion + + private static unsafe bool TryGetNativeReaderForBlob(NativeFormatModuleInfo module, ReflectionMapBlob blob, out NativeReader reader) + { + byte* pBlob; + uint cbBlob; + + if (module.TryFindBlob((int)blob, out pBlob, out cbBlob)) + { + reader = new NativeReader(pBlob, cbBlob); + return true; + } + + reader = default(NativeReader); + return false; + } + + private unsafe bool GetMarshallersForDelegate(RuntimeTypeHandle delegateTypeHandle, out IntPtr openStub, out IntPtr closedStub, out IntPtr delegateCreationStub) + { + int delegateHashcode = delegateTypeHandle.GetHashCode(); + openStub = IntPtr.Zero; + closedStub = IntPtr.Zero; + delegateCreationStub = IntPtr.Zero; + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + NativeReader delegateMapReader; + if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.DelegateMarshallingStubMap, out delegateMapReader)) + { + NativeParser delegateMapParser = new NativeParser(delegateMapReader, 0); + NativeHashtable delegateHashtable = new NativeHashtable(delegateMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = delegateHashtable.Lookup(delegateHashcode); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle foundDelegateType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (foundDelegateType.Equals(delegateTypeHandle)) + { + openStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + closedStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + delegateCreationStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + return true; + } + } + } + } + return false; + } + + private unsafe bool TryGetStructData(RuntimeTypeHandle structTypeHandle, out ExternalReferencesTable externalReferences, out NativeParser entryParser) + { + int structHashcode = structTypeHandle.GetHashCode(); + externalReferences = default(ExternalReferencesTable); + entryParser = default(NativeParser); + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + NativeReader structMapReader; + if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.StructMarshallingStubMap, out structMapReader)) + { + NativeParser structMapParser = new NativeParser(structMapReader, 0); + NativeHashtable structHashtable = new NativeHashtable(structMapParser); + + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = structHashtable.Lookup(structHashcode); + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle foundStructType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (foundStructType.Equals(structTypeHandle)) + { + return true; + } + } + } + } + return false; + } + + private unsafe bool TryGetMarshallersForStruct(RuntimeTypeHandle structTypeHandle, out IntPtr marshalStub, out IntPtr unmarshalStub, out IntPtr destroyStub, out bool hasInvalidLayout, out int size) + { + marshalStub = IntPtr.Zero; + unmarshalStub = IntPtr.Zero; + destroyStub = IntPtr.Zero; + hasInvalidLayout = true; + size = 0; + + ExternalReferencesTable externalReferences; + NativeParser entryParser; + if (TryGetStructData(structTypeHandle, out externalReferences, out entryParser)) + { + uint mask = entryParser.GetUnsigned(); + if ((mask & InteropDataConstants.HasMarshallers) != 0) + { + hasInvalidLayout = (mask & InteropDataConstants.HasInvalidLayout) != 0; + + size = (int)entryParser.GetUnsigned(); + marshalStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + unmarshalStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + destroyStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + + return true; + } + } + return false; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.cs b/src/coreclr/nativeaot/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.cs new file mode 100644 index 00000000000000..c8796344b523e5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using Internal.Runtime.Augments; +using Internal.NativeFormat; +using Internal.Runtime.TypeLoader; +using Internal.Reflection.Execution; +using System.Runtime.InteropServices; + +namespace Internal.Runtime.CompilerHelpers +{ + internal partial class RuntimeInteropData : InteropCallbacks + { + public override uint GetStructFieldOffset(RuntimeTypeHandle structureTypeHandle, string fieldName) + { + if (TryGetStructFieldOffset(structureTypeHandle, fieldName, out bool structExists, out uint offset)) + { + return offset; + } + + // if we can find the struct but couldn't find its field, throw Argument Exception + if (structExists) + { + throw new ArgumentException(SR.Format(SR.Argument_OffsetOfFieldNotFound, RuntimeAugments.GetLastResortString(structureTypeHandle)), nameof(fieldName)); + } + + throw new MissingInteropDataException(SR.StructMarshalling_MissingInteropData, Type.GetTypeFromHandle(structureTypeHandle)); + } + + public override int GetStructUnsafeStructSize(RuntimeTypeHandle structureTypeHandle) + { + if (TryGetStructUnsafeStructSize(structureTypeHandle, out int size)) + { + return size; + } + + // IsBlittable() checks whether the type contains GC references. It is approximate check with false positives. + // This fallback path will return incorrect answer for types that do not contain GC references, but that are + // not actually blittable; e.g. for types with bool fields. + if (structureTypeHandle.IsBlittable() && structureTypeHandle.IsValueType()) + { + return structureTypeHandle.GetValueTypeSize(); + } + + throw new MissingInteropDataException(SR.StructMarshalling_MissingInteropData, Type.GetTypeFromHandle(structureTypeHandle)); + } + + public override IntPtr GetStructUnmarshalStub(RuntimeTypeHandle structureTypeHandle) + { + if (TryGetStructUnmarshalStub(structureTypeHandle, out IntPtr stub)) + { + return stub; + } + + throw new MissingInteropDataException(SR.StructMarshalling_MissingInteropData, Type.GetTypeFromHandle(structureTypeHandle)); + } + + public override IntPtr GetStructMarshalStub(RuntimeTypeHandle structureTypeHandle) + { + if (TryGetStructMarshalStub(structureTypeHandle, out IntPtr stub)) + { + return stub; + } + + throw new MissingInteropDataException(SR.StructMarshalling_MissingInteropData, Type.GetTypeFromHandle(structureTypeHandle)); + } + + public override IntPtr GetDestroyStructureStub(RuntimeTypeHandle structureTypeHandle, out bool hasInvalidLayout) + { + if (TryGetDestroyStructureStub(structureTypeHandle, out IntPtr stub, out hasInvalidLayout)) + { + return stub; + } + + throw new MissingInteropDataException(SR.StructMarshalling_MissingInteropData, Type.GetTypeFromHandle(structureTypeHandle)); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Interop/src/Resources/Strings.resx b/src/coreclr/nativeaot/System.Private.Interop/src/Resources/Strings.resx new file mode 100644 index 00000000000000..a7149829bf0c26 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Interop/src/Resources/Strings.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + {0} is missing structure marshalling data. To enable structure marshalling data, add a MarshalStructure directive to the application rd.xml file. For more information, please visit http://go.microsoft.com/fwlink/?LinkID=393965 + + + {0} is missing delegate marshalling data. To enable delegate marshalling data, add a MarshalDelegate directive to the application rd.xml file. For more information, please visit http://go.microsoft.com/fwlink/?LinkID=393965 + + + Field passed in is not a marshaled member of the type '{0}'. + + \ No newline at end of file diff --git a/src/coreclr/nativeaot/System.Private.Interop/src/System.Private.Interop.csproj b/src/coreclr/nativeaot/System.Private.Interop/src/System.Private.Interop.csproj new file mode 100644 index 00000000000000..527750f8e61b15 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Interop/src/System.Private.Interop.csproj @@ -0,0 +1,35 @@ + + + + + + + + $(CompilerCommonPath)\Internal\NativeFormat + + + + + + + + + + + + + + Internal\Runtime\InteropConstants.cs + + + + + + MetadataBlob.cs + + + System\Runtime\CompilerServices\__BlockReflectionAttribute.cs + + + + diff --git a/src/coreclr/nativeaot/System.Private.Interop/src/System/Runtime/InteropServices/MissingInteropDataException.cs b/src/coreclr/nativeaot/System.Private.Interop/src/System/Runtime/InteropServices/MissingInteropDataException.cs new file mode 100644 index 00000000000000..437c9e41bf6acf --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Interop/src/System/Runtime/InteropServices/MissingInteropDataException.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +namespace System.Runtime.InteropServices +{ + /// + /// Thrown when a manual marshalling method is called, but the type was not found + /// by static analysis or in the rd.xml file. + /// + class MissingInteropDataException : Exception + { + public Type MissingType { get; private set; } + public MissingInteropDataException(string resourceFormat, Type pertainantType): + base(SR.Format(resourceFormat, pertainantType.ToString())) + { + MissingType = pertainantType; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/ILLink/ILLink.Substitutions.xml b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/ILLink/ILLink.Substitutions.xml new file mode 100644 index 00000000000000..f7df139a82b626 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/ILLink/ILLink.Substitutions.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs new file mode 100644 index 00000000000000..53edc6a6ec5e49 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs @@ -0,0 +1,380 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeInfos.NativeFormat; +using System.Reflection.Runtime.Assemblies; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.MethodInfos.NativeFormat; +#if ECMA_METADATA_SUPPORT +using System.Reflection.Runtime.TypeInfos.EcmaFormat; +using System.Reflection.Runtime.MethodInfos.EcmaFormat; +#endif +using System.Reflection.Runtime.TypeParsing; +using System.Reflection.Runtime.CustomAttributes; +using Internal.Metadata.NativeFormat; +using Internal.Runtime.Augments; + +namespace Internal.Reflection.Core.Execution +{ + // + // This singleton class acts as an entrypoint from System.Private.Reflection.Execution to System.Private.Reflection.Core. + // + public sealed class ExecutionDomain + { + internal ExecutionDomain(ReflectionDomainSetup executionDomainSetup, ExecutionEnvironment executionEnvironment) + { + ExecutionEnvironment = executionEnvironment; + ReflectionDomainSetup = executionDomainSetup; + } + + // + // Retrieves a type by name. Helper to implement Type.GetType(); + // + public Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, IList defaultAssemblyNames) + { + if (typeName == null) + throw new ArgumentNullException(); + + if (typeName.Length == 0) + { + if (throwOnError) + throw new TypeLoadException(SR.Arg_TypeLoadNullStr); + else + return null; + } + + TypeName parsedName = TypeParser.ParseAssemblyQualifiedTypeName(typeName, throwOnError: throwOnError); + if (parsedName == null) + return null; + CoreAssemblyResolver coreAssemblyResolver = CreateCoreAssemblyResolver(assemblyResolver); + CoreTypeResolver coreTypeResolver = CreateCoreTypeResolver(typeResolver, defaultAssemblyNames, throwOnError: throwOnError, ignoreCase: ignoreCase); + GetTypeOptions getTypeOptions = new GetTypeOptions(coreAssemblyResolver, coreTypeResolver, throwOnError: throwOnError, ignoreCase: ignoreCase); + + return parsedName.ResolveType(null, getTypeOptions); + } + + private static CoreAssemblyResolver CreateCoreAssemblyResolver(Func assemblyResolver) + { + if (assemblyResolver == null) + { + return RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists; + } + else + { + return delegate (RuntimeAssemblyName runtimeAssemblyName) + { + AssemblyName assemblyName = runtimeAssemblyName.ToAssemblyName(); + Assembly assembly = assemblyResolver(assemblyName); + return assembly; + }; + } + } + + private static CoreTypeResolver CreateCoreTypeResolver(Func typeResolver, IList defaultAssemblyNames, bool throwOnError, bool ignoreCase) + { + if (typeResolver == null) + { + return delegate (Assembly containingAssemblyIfAny, string coreTypeName) + { + if (containingAssemblyIfAny != null) + { + return containingAssemblyIfAny.GetTypeCore(coreTypeName, ignoreCase: ignoreCase); + } + else + { + foreach (string defaultAssemblyName in defaultAssemblyNames) + { + RuntimeAssemblyName runtimeAssemblyName = AssemblyNameParser.Parse(defaultAssemblyName); + RuntimeAssemblyInfo defaultAssembly = RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(runtimeAssemblyName); + if (defaultAssembly == null) + continue; + Type resolvedType = defaultAssembly.GetTypeCore(coreTypeName, ignoreCase: ignoreCase); + if (resolvedType != null) + return resolvedType; + } + + if (throwOnError && defaultAssemblyNames.Count > 0) + { + // Though we don't have to throw a TypeLoadException exception (that's our caller's job), we can throw a more specific exception than he would so just do it. + throw Helpers.CreateTypeLoadException(coreTypeName, defaultAssemblyNames[0]); + } + return null; + } + }; + } + else + { + return delegate (Assembly containingAssemblyIfAny, string coreTypeName) + { + string escapedName = coreTypeName.EscapeTypeNameIdentifier(); + Type type = typeResolver(containingAssemblyIfAny, escapedName, ignoreCase); + return type; + }; + } + } + + // + // Retrieves the MethodBase for a given method handle. Helper to implement Delegate.GetMethodInfo() + // + public MethodBase GetMethod(RuntimeTypeHandle declaringTypeHandle, QMethodDefinition methodHandle, RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + RuntimeTypeInfo contextTypeInfo = declaringTypeHandle.GetTypeForRuntimeTypeHandle(); + RuntimeNamedMethodInfo runtimeNamedMethodInfo = null; + + if (methodHandle.IsNativeFormatMetadataBased) + { + MethodHandle nativeFormatMethodHandle = methodHandle.NativeFormatHandle; + NativeFormatRuntimeNamedTypeInfo definingTypeInfo = contextTypeInfo.AnchoringTypeDefinitionForDeclaredMembers.CastToNativeFormatRuntimeNamedTypeInfo(); + MetadataReader reader = definingTypeInfo.Reader; + if (nativeFormatMethodHandle.IsConstructor(reader)) + { + return RuntimePlainConstructorInfo.GetRuntimePlainConstructorInfo(new NativeFormatMethodCommon(nativeFormatMethodHandle, definingTypeInfo, contextTypeInfo)); + } + else + { + // RuntimeMethodHandles always yield methods whose ReflectedType is the DeclaringType. + RuntimeTypeInfo reflectedType = contextTypeInfo; + runtimeNamedMethodInfo = RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(new NativeFormatMethodCommon(nativeFormatMethodHandle, definingTypeInfo, contextTypeInfo), reflectedType); + } + } +#if ECMA_METADATA_SUPPORT + else + { + System.Reflection.Metadata.MethodDefinitionHandle ecmaFormatMethodHandle = methodHandle.EcmaFormatHandle; + EcmaFormatRuntimeNamedTypeInfo definingEcmaTypeInfo = contextTypeInfo.AnchoringTypeDefinitionForDeclaredMembers.CastToEcmaFormatRuntimeNamedTypeInfo(); + System.Reflection.Metadata.MetadataReader reader = definingEcmaTypeInfo.Reader; + if (ecmaFormatMethodHandle.IsConstructor(reader)) + { + return RuntimePlainConstructorInfo.GetRuntimePlainConstructorInfo(new EcmaFormatMethodCommon(ecmaFormatMethodHandle, definingEcmaTypeInfo, contextTypeInfo)); + } + else + { + // RuntimeMethodHandles always yield methods whose ReflectedType is the DeclaringType. + RuntimeTypeInfo reflectedType = contextTypeInfo; + runtimeNamedMethodInfo = RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(new EcmaFormatMethodCommon(ecmaFormatMethodHandle, definingEcmaTypeInfo, contextTypeInfo), reflectedType); + } + } +#endif + + if (!runtimeNamedMethodInfo.IsGenericMethod || genericMethodTypeArgumentHandles == null) + { + return runtimeNamedMethodInfo; + } + else + { + RuntimeTypeInfo[] genericTypeArguments = new RuntimeTypeInfo[genericMethodTypeArgumentHandles.Length]; + for (int i = 0; i < genericMethodTypeArgumentHandles.Length; i++) + { + genericTypeArguments[i] = genericMethodTypeArgumentHandles[i].GetTypeForRuntimeTypeHandle(); + } + return RuntimeConstructedGenericMethodInfo.GetRuntimeConstructedGenericMethodInfo(runtimeNamedMethodInfo, genericTypeArguments); + } + } + + //======================================================================================= + // This group of methods jointly service the Type.GetTypeFromHandle() path. The caller + // is responsible for analyzing the RuntimeTypeHandle to figure out which flavor to call. + //======================================================================================= + public Type GetNamedTypeForHandle(RuntimeTypeHandle typeHandle, bool isGenericTypeDefinition) + { + QTypeDefinition qTypeDefinition; + + if (ExecutionEnvironment.TryGetMetadataForNamedType(typeHandle, out qTypeDefinition)) + { +#if ECMA_METADATA_SUPPORT + if (qTypeDefinition.IsNativeFormatMetadataBased) +#endif + { + return qTypeDefinition.NativeFormatHandle.GetNamedType(qTypeDefinition.NativeFormatReader, typeHandle); + } +#if ECMA_METADATA_SUPPORT + else + { + return System.Reflection.Runtime.TypeInfos.EcmaFormat.EcmaFormatRuntimeNamedTypeInfo.GetRuntimeNamedTypeInfo(qTypeDefinition.EcmaFormatReader, + qTypeDefinition.EcmaFormatHandle, + typeHandle); + } +#endif + } + else + { + if (ExecutionEnvironment.IsReflectionBlocked(typeHandle)) + { + return RuntimeBlockedTypeInfo.GetRuntimeBlockedTypeInfo(typeHandle, isGenericTypeDefinition); + } + else + { + return RuntimeNoMetadataNamedTypeInfo.GetRuntimeNoMetadataNamedTypeInfo(typeHandle, isGenericTypeDefinition); + } + } + } + + public Type GetArrayTypeForHandle(RuntimeTypeHandle typeHandle) + { + RuntimeTypeHandle elementTypeHandle; + if (!ExecutionEnvironment.TryGetArrayTypeElementType(typeHandle, out elementTypeHandle)) + throw CreateMissingMetadataException((Type)null); + + return elementTypeHandle.GetTypeForRuntimeTypeHandle().GetArrayType(typeHandle); + } + + public Type GetMdArrayTypeForHandle(RuntimeTypeHandle typeHandle, int rank) + { + RuntimeTypeHandle elementTypeHandle; + if (!ExecutionEnvironment.TryGetArrayTypeElementType(typeHandle, out elementTypeHandle)) + throw CreateMissingMetadataException((Type)null); + + return elementTypeHandle.GetTypeForRuntimeTypeHandle().GetMultiDimArrayType(rank, typeHandle); + } + + public Type GetPointerTypeForHandle(RuntimeTypeHandle typeHandle) + { + RuntimeTypeHandle targetTypeHandle; + if (!ExecutionEnvironment.TryGetPointerTypeTargetType(typeHandle, out targetTypeHandle)) + throw CreateMissingMetadataException((Type)null); + + return targetTypeHandle.GetTypeForRuntimeTypeHandle().GetPointerType(typeHandle); + } + + public Type GetByRefTypeForHandle(RuntimeTypeHandle typeHandle) + { + RuntimeTypeHandle targetTypeHandle; + if (!ExecutionEnvironment.TryGetByRefTypeTargetType(typeHandle, out targetTypeHandle)) + throw CreateMissingMetadataException((Type)null); + + return targetTypeHandle.GetTypeForRuntimeTypeHandle().GetByRefType(typeHandle); + } + + public Type GetConstructedGenericTypeForHandle(RuntimeTypeHandle typeHandle) + { + RuntimeTypeHandle genericTypeDefinitionHandle; + RuntimeTypeHandle[] genericTypeArgumentHandles; + genericTypeDefinitionHandle = RuntimeAugments.GetGenericInstantiation(typeHandle, out genericTypeArgumentHandles); + + // Reflection blocked constructed generic types simply pretend to not be generic + // This is reasonable, as the behavior of reflection blocked types is supposed + // to be that they expose the minimal information about a type that is necessary + // for users of Object.GetType to move from that type to a type that isn't + // reflection blocked. By not revealing that reflection blocked types are generic + // we are making it appear as if implementation detail types exposed to user code + // are all non-generic, which is theoretically possible, and by doing so + // we avoid (in all known circumstances) the very complicated case of representing + // the interfaces, base types, and generic parameter types of reflection blocked + // generic type definitions. + if (ExecutionEnvironment.IsReflectionBlocked(genericTypeDefinitionHandle)) + { + return RuntimeBlockedTypeInfo.GetRuntimeBlockedTypeInfo(typeHandle, isGenericTypeDefinition: false); + } + + RuntimeTypeInfo genericTypeDefinition = genericTypeDefinitionHandle.GetTypeForRuntimeTypeHandle(); + int count = genericTypeArgumentHandles.Length; + RuntimeTypeInfo[] genericTypeArguments = new RuntimeTypeInfo[count]; + for (int i = 0; i < count; i++) + { + genericTypeArguments[i] = genericTypeArgumentHandles[i].GetTypeForRuntimeTypeHandle(); + } + return genericTypeDefinition.GetConstructedGenericType(genericTypeArguments, typeHandle); + } + + //======================================================================================= + // MissingMetadataExceptions. + //======================================================================================= + public Exception CreateMissingMetadataException(Type pertainant) + { + return this.ReflectionDomainSetup.CreateMissingMetadataException(pertainant); + } + + public Exception CreateMissingMetadataException(TypeInfo pertainant) + { + return this.ReflectionDomainSetup.CreateMissingMetadataException(pertainant); + } + + public Exception CreateMissingMetadataException(TypeInfo pertainant, string nestedTypeName) + { + return this.ReflectionDomainSetup.CreateMissingMetadataException(pertainant, nestedTypeName); + } + + public Exception CreateNonInvokabilityException(MemberInfo pertainant) + { + return this.ReflectionDomainSetup.CreateNonInvokabilityException(pertainant); + } + + public Exception CreateMissingArrayTypeException(Type elementType, bool isMultiDim, int rank) + { + return ReflectionDomainSetup.CreateMissingArrayTypeException(elementType, isMultiDim, rank); + } + + public Exception CreateMissingConstructedGenericTypeException(Type genericTypeDefinition, Type[] genericTypeArguments) + { + return ReflectionDomainSetup.CreateMissingConstructedGenericTypeException(genericTypeDefinition, genericTypeArguments); + } + + //======================================================================================= + // Miscellaneous. + //======================================================================================= + public RuntimeTypeHandle GetTypeHandleIfAvailable(Type type) + { + if (type is not RuntimeType) + return default(RuntimeTypeHandle); + + RuntimeTypeInfo runtimeType = type.CastToRuntimeTypeInfo(); + if (runtimeType == null) + return default(RuntimeTypeHandle); + return runtimeType.InternalTypeHandleIfAvailable; + } + + public bool SupportsReflection(Type type) + { + if (type is not RuntimeType) + return false; + + RuntimeTypeInfo runtimeType = type.CastToRuntimeTypeInfo(); + if (null == runtimeType.InternalNameIfAvailable) + return false; + + if (ExecutionEnvironment.IsReflectionBlocked(type.TypeHandle)) + { + // The type is an internal framework type and is blocked from reflection + return false; + } + + if (runtimeType.InternalFullNameOfAssembly == Internal.Runtime.Augments.RuntimeAugments.HiddenScopeAssemblyName) + { + // The type is an internal framework type but is reflectable for internal class library use + // where we make the type appear in a hidden assembly + return false; + } + + return true; + } + + internal ExecutionEnvironment ExecutionEnvironment { get; } + + internal ReflectionDomainSetup ReflectionDomainSetup { get; } + + internal IEnumerable PrimitiveTypes => s_primitiveTypes; + + private static readonly Type[] s_primitiveTypes = + { + typeof(bool), + typeof(char), + typeof(sbyte), + typeof(byte), + typeof(short), + typeof(ushort), + typeof(int), + typeof(uint), + typeof(long), + typeof(ulong), + typeof(float), + typeof(double), + typeof(IntPtr), + typeof(UIntPtr), + }; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs new file mode 100644 index 00000000000000..64942f3c0d0823 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Reflection; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using Internal.Metadata.NativeFormat; + +using OpenMethodInvoker = System.Reflection.Runtime.MethodInfos.OpenMethodInvoker; +using System.Reflection.Runtime.MethodInfos; + +namespace Internal.Reflection.Core.Execution +{ + // + // This class abstracts the underlying Redhawk (or whatever execution engine) runtime and exposes the services + // that I.R.Core.Execution needs. + // + public abstract class ExecutionEnvironment + { + //============================================================================================== + // Access to the underlying execution engine's object allocation routines. + //============================================================================================== + public abstract object NewObject(RuntimeTypeHandle typeHandle); + public abstract Array NewArray(RuntimeTypeHandle typeHandleForArrayType, int count); + public abstract Array NewMultiDimArray(RuntimeTypeHandle typeHandleForArrayType, int[] lengths, int[] lowerBounds); + + //============================================================================================== + // Execution engine policies. + //============================================================================================== + + // + // This returns a generic type with one generic parameter (representing the array element type) + // whose base type and interface list determines what TypeInfo.BaseType and TypeInfo.ImplementedInterfaces + // return for types that return true for IsArray. + // + public abstract RuntimeTypeHandle ProjectionTypeForArrays { get; } + public abstract bool IsAssignableFrom(RuntimeTypeHandle dstType, RuntimeTypeHandle srcType); + public abstract bool TryGetBaseType(RuntimeTypeHandle typeHandle, out RuntimeTypeHandle baseTypeHandle); + public abstract IEnumerable TryGetImplementedInterfaces(RuntimeTypeHandle typeHandle); + public abstract void VerifyInterfaceIsImplemented(RuntimeTypeHandle typeHandle, RuntimeTypeHandle ifaceHandle); + public abstract void GetInterfaceMap(Type instanceType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType, out MethodInfo[] interfaceMethods, out MethodInfo[] targetMethods); + public abstract bool IsReflectionBlocked(RuntimeTypeHandle typeHandle); + public abstract string GetLastResortString(RuntimeTypeHandle typeHandle); + + //============================================================================================== + // Reflection Mapping Tables + //============================================================================================== + public abstract bool TryGetMetadataForNamedType(RuntimeTypeHandle runtimeTypeHandle, out QTypeDefinition qTypeDefinition); + public abstract bool TryGetNamedTypeForMetadata(QTypeDefinition qTypeDefinition, out RuntimeTypeHandle runtimeTypeHandle); + + public abstract bool TryGetTypeReferenceForNamedType(RuntimeTypeHandle runtimeTypeHandle, out MetadataReader metadataReader, out TypeReferenceHandle typeRefHandle); + public abstract bool TryGetNamedTypeForTypeReference(MetadataReader metadataReader, TypeReferenceHandle typeRefHandle, out RuntimeTypeHandle runtimeTypeHandle); + + public abstract bool TryGetArrayTypeForElementType(RuntimeTypeHandle elementTypeHandle, out RuntimeTypeHandle arrayTypeHandle); + public abstract bool TryGetArrayTypeElementType(RuntimeTypeHandle arrayTypeHandle, out RuntimeTypeHandle elementTypeHandle); + + public abstract bool TryGetMultiDimArrayTypeForElementType(RuntimeTypeHandle elementTypeHandle, int rank, out RuntimeTypeHandle arrayTypeHandle); + + public abstract bool TryGetPointerTypeForTargetType(RuntimeTypeHandle targetTypeHandle, out RuntimeTypeHandle pointerTypeHandle); + public abstract bool TryGetPointerTypeTargetType(RuntimeTypeHandle pointerTypeHandle, out RuntimeTypeHandle targetTypeHandle); + + public abstract bool TryGetByRefTypeForTargetType(RuntimeTypeHandle targetTypeHandle, out RuntimeTypeHandle byRefTypeHandle); + public abstract bool TryGetByRefTypeTargetType(RuntimeTypeHandle byRefTypeHandle, out RuntimeTypeHandle targetTypeHandle); + + public abstract bool TryGetConstructedGenericTypeForComponents(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle); + + //============================================================================================== + // Invoke and field access support. + //============================================================================================== + public abstract MethodInvoker TryGetMethodInvoker(RuntimeTypeHandle declaringTypeHandle, QMethodDefinition methodHandle, RuntimeTypeHandle[] genericMethodTypeArgumentHandles); + public abstract FieldAccessor TryGetFieldAccessor(MetadataReader reader, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle, FieldHandle fieldHandle); + + //============================================================================================== + // RuntimeMethodHandle and RuntimeFieldHandle support. + //============================================================================================== + public abstract bool TryGetMethodFromHandle(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles); + public abstract bool TryGetMethodFromHandleAndType(RuntimeMethodHandle runtimeMethodHandle, RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles); + public abstract bool TryGetFieldFromHandle(RuntimeFieldHandle runtimeFieldHandle, out RuntimeTypeHandle declaringTypeHandle, out FieldHandle fieldHandle); + public abstract bool TryGetFieldFromHandleAndType(RuntimeFieldHandle runtimeFieldHandle, RuntimeTypeHandle declaringTypeHandle, out FieldHandle fieldHandle); + + + //============================================================================================== + // Manifest resource stream support. + //============================================================================================== + public abstract ManifestResourceInfo GetManifestResourceInfo(Assembly assembly, string resourceName); + public abstract string[] GetManifestResourceNames(Assembly assembly); + public abstract Stream GetManifestResourceStream(Assembly assembly, string name); + + //============================================================================================== + // Other + //============================================================================================== + public abstract bool IsCOMObject(Type type); + public abstract FieldAccessor CreateLiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle); + public abstract EnumInfo GetEnumInfo(RuntimeTypeHandle typeHandle); + + //============================================================================================== + // Non-public methods + //============================================================================================== + internal MethodInvoker GetMethodInvoker(RuntimeTypeInfo declaringType, QMethodDefinition methodHandle, RuntimeTypeInfo[] genericMethodTypeArguments, MemberInfo exceptionPertainant, out Exception exception) + { + exception = null; + + if (declaringType.ContainsGenericParameters) + return new OpenMethodInvoker(); + for (int i = 0; i < genericMethodTypeArguments.Length; i++) + { + if (genericMethodTypeArguments[i].ContainsGenericParameters) + return new OpenMethodInvoker(); + } + + RuntimeTypeHandle typeDefinitionHandle = declaringType.TypeHandle; + RuntimeTypeHandle[] genericMethodTypeArgumentHandles = new RuntimeTypeHandle[genericMethodTypeArguments.Length]; + + for (int i = 0; i < genericMethodTypeArguments.Length; i++) + { + genericMethodTypeArgumentHandles[i] = genericMethodTypeArguments[i].TypeHandle; + } + MethodInvoker methodInvoker = TryGetMethodInvoker(typeDefinitionHandle, methodHandle, genericMethodTypeArgumentHandles); + if (methodInvoker == null) + exception = ReflectionCoreExecution.ExecutionDomain.CreateNonInvokabilityException(exceptionPertainant); + return methodInvoker; + } + + protected MethodInvoker GetMethodInvoker(MethodInfo methodInfo) + { + return ((RuntimeMethodInfo)methodInfo).MethodInvoker; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/FieldAccessor.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/FieldAccessor.cs new file mode 100644 index 00000000000000..c241534e9972b0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/FieldAccessor.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; + +namespace Internal.Reflection.Core.Execution +{ + // + // This class abstracts the underlying Redhawk (or whatever execution engine) runtime that sets and gets fields. + // + public abstract class FieldAccessor + { + protected FieldAccessor() { } + public abstract object GetField(object obj); + public abstract object GetFieldDirect(TypedReference typedReference); + + public abstract void SetField(object obj, object value, BinderBundle binderBundle); + public abstract void SetFieldDirect(TypedReference typedReference, object value); + + /// + /// Returns the field offset (asserts and throws if not an instance field). Does not include the size of the object header. + /// + public abstract int Offset { get; } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/MethodInvoker.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/MethodInvoker.cs new file mode 100644 index 00000000000000..4ee695844ccc1e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/MethodInvoker.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Globalization; +using System.Reflection.Runtime.General; + +using Internal.Runtime.Augments; + +namespace Internal.Reflection.Core.Execution +{ + // + // This class polymorphically implements the MethodBase.Invoke() api and its close cousins. MethodInvokers are designed to be built once and cached + // for maximum Invoke() throughput. + // + public abstract class MethodInvoker + { + protected MethodInvoker() { } + + [DebuggerGuidedStepThrough] + public object Invoke(object thisObject, object[] arguments, Binder binder, BindingFlags invokeAttr, CultureInfo cultureInfo) + { + BinderBundle binderBundle = binder.ToBinderBundle(invokeAttr, cultureInfo); + bool wrapInTargetInvocationException = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; + object result = Invoke(thisObject, arguments, binderBundle, wrapInTargetInvocationException); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + return result; + } + protected abstract object Invoke(object thisObject, object[] arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException); + public abstract Delegate CreateDelegate(RuntimeTypeHandle delegateType, object target, bool isStatic, bool isVirtual, bool isOpen); + + // This property is used to retrieve the target method pointer. It is used by the RuntimeMethodHandle.GetFunctionPointer API + public abstract IntPtr LdFtnResult { get; } + + protected static void ValidateThis(object thisObject, RuntimeTypeHandle declaringTypeHandle) + { + if (thisObject == null) + throw new TargetException(SR.RFLCT_Targ_StatMethReqTarg); + + if (RuntimeAugments.IsAssignable(thisObject, declaringTypeHandle)) + return; + + if (RuntimeAugments.IsInterface(declaringTypeHandle)) + { + if (RuntimeAugments.IsInstanceOfInterface(thisObject, declaringTypeHandle)) + return; + } + + throw new TargetException(SR.RFLCT_Targ_ITargMismatch); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ReflectionCoreExecution.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ReflectionCoreExecution.cs new file mode 100644 index 00000000000000..28341dab408f0f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ReflectionCoreExecution.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Reflection.Runtime.General; + +using Internal.LowLevelLinq; +using Internal.Reflection.Augments; +using Internal.Reflection.Core.Execution; + +namespace Internal.Reflection.Core.Execution +{ + public static class ReflectionCoreExecution + { + // + // One time initialization to supply the information needed to initialize the execution environment. + // + public static void InitializeExecutionDomain(ReflectionDomainSetup executionDomainSetup, ExecutionEnvironment executionEnvironment) + { + ExecutionDomain executionDomain = new ExecutionDomain(executionDomainSetup, executionEnvironment); + //@todo: This check has a race window but since this is a private api targeted by the toolchain, perhaps this is not so critical. + if (s_executionDomain != null) + throw new InvalidOperationException(); // Multiple Initializes not allowed. + s_executionDomain = executionDomain; + + ReflectionCoreCallbacks reflectionCallbacks = new ReflectionCoreCallbacksImplementation(); + ReflectionAugments.Initialize(reflectionCallbacks); + return; + } + + public static ExecutionDomain ExecutionDomain + { + get + { + return s_executionDomain; + } + } + + internal static ExecutionEnvironment ExecutionEnvironment + { + get + { + return ExecutionDomain.ExecutionEnvironment; + } + } + + private static volatile ExecutionDomain s_executionDomain; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/ReflectionDomainSetup.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/ReflectionDomainSetup.cs new file mode 100644 index 00000000000000..db5c093cdd4d7e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Core/ReflectionDomainSetup.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Reflection.Runtime.General; + +namespace Internal.Reflection.Core +{ + public abstract class ReflectionDomainSetup + { + protected ReflectionDomainSetup() { } + public abstract AssemblyBinder AssemblyBinder { get; } + public abstract Exception CreateMissingMetadataException(TypeInfo pertainant); + public abstract Exception CreateMissingMetadataException(Type pertainant); + public abstract Exception CreateMissingMetadataException(TypeInfo pertainant, string nestedTypeName); + public abstract Exception CreateNonInvokabilityException(MemberInfo pertainant); + public abstract Exception CreateMissingArrayTypeException(Type elementType, bool isMultiDim, int rank); + public abstract Exception CreateMissingConstructedGenericTypeException(Type genericTypeDefinition, Type[] genericTypeArguments); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Tracing/ITraceableTypeMember.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Tracing/ITraceableTypeMember.cs new file mode 100644 index 00000000000000..c3a35e36a94a86 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Internal/Reflection/Tracing/ITraceableTypeMember.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; + +namespace Internal.Reflection.Tracing +{ + internal interface ITraceableTypeMember + { + // Returns the Name value *without recursing into the public Name implementation.* + string MemberName { get; } + + // Returns the DeclaringType value *without recursing into the public DeclaringType implementation.* + Type ContainingType { get; } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Resources/Strings.resx b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Resources/Strings.resx new file mode 100644 index 00000000000000..84cbca76e08bbe --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/Resources/Strings.resx @@ -0,0 +1,372 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + A problem was found in this image's metadata. + + + Cannot create an instance of {0} because it is an abstract class. + + + Type initializer was not callable. + + + Cannot set a constant field. + + + A MemberInfo that matches '{0}' could not be found. + + + {0} is not a GenericMethodDefinition. MakeGenericMethod may only be called on a method for which MethodBase.IsGenericMethodDefinition is true. + + + Method may only be called on a Type for which Type.IsGenericParameter is true. + + + A null or zero length string does not represent a valid Type. + + + The type or method has {1} generic parameter(s), but {0} generic argument(s) were provided. A generic argument must be provided for each generic parameter. + + + The object '{0}' was created by a custom ReflectionContext and cannot be used here. + + + The type '{0}' cannot be found. + + + The type '{0}' cannot be found in assembly '{1}'. + + + Cannot load assembly '{0}'. No metadata found for this assembly. + + + Hashtable's capacity overflowed and went negative. Check load factor, capacity and the current size of the table. + + + Cannot bind to the target method because its signature is not compatible with that of the delegate type. + + + Type must derive from Delegate. + + + Cannot create a delegate on type '{0}' as it is missing metadata for the Invoke method. + + + An item with the same key has already been added. Key: {0} + + + The handle is invalid. + + + Type handle '{0}' and method handle with declaring type '{1}' are incompatible. Get RuntimeMethodHandle and declaring RuntimeTypeHandle off the same MethodBase. + + + Type handle '{0}' and field handle with declaring type '{1}' are incompatible. Get RuntimeFieldHandle and declaring RuntimeTypeHandle off the same FieldInfo. + + + Cannot resolve method {0} because the declaring type of the method handle is generic. Explicitly provide the declaring type to GetMethodFromHandle. + + + Cannot resolve field {0} because the declaring type of the field handle is generic. Explicitly provide the declaring type to GetFieldFromHandle. + + + Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true. + + + Type names passed to Assembly.GetType() must not specify an assembly. + + + Cannot add the event handler since no public add method exists for the event. + + + Cannot remove the event handler since no public remove method exists for the event. + + + {0} is not a GenericTypeDefinition. MakeGenericType may only be called on a type for which Type.IsGenericTypeDefinition is true. + + + PlatformNotSupported_MakeGenericType", @"MakeGenericType can only accept Type objects created by the runtime. + + + The type '{0}' may not be used as a type argument. + + + The type '{0}' may not be used as an array element type. + + + TypeHandles are not supported for types that return true for ContainsGenericParameters. + + + Must be an array type. + + + This operation is only valid on generic types. + + + Property get method not found. + + + Property set method not found. + + + Array may not be empty. + + + Member not found. + + + Ambiguous match found. + + + There is no metadata token available for the given member. + + + Field not found. + + + Type must be a type provided by the runtime. + + + ChangeType operation is not supported. + + + No parameterless constructor defined for this object. + + + Activation Attributes are not supported. + + + Vararg calling convention not supported. + + + Cannot create an instance of {0} because Type.ContainsGenericParameters is true. + + + Cannot dynamically create an instance of System.Void. + + + Method must be called on a Type for which Type.IsGenericParameter is false. + + + Must specify binding flags describing the invoke operation required (BindingFlags.InvokeMethod CreateInstance GetField SetField GetProperty SetProperty). + + + Named parameter array cannot be bigger than argument array. + + + Cannot specify both Get and Set on a property. + + + Cannot specify Set on a property and Invoke on a method. + + + Named parameter value must not be null. + + + Cannot specify both CreateInstance and another access type. + + + Cannot specify both Get and Set on a field. + + + Cannot specify both GetField and SetProperty. + + + Cannot specify both SetField and GetProperty. + + + Cannot specify Set on a Field and Invoke on a method. + + + All indexes must be of type Int32. + + + No arguments can be provided to Get a field value. + + + Only the field value can be specified to set a field value. + + + InvokeMember on a COM object is not supported on this platform. + + + Type must be a runtime Type object. + + + MethodInfo must be a runtime MethodInfo object. + + + Array must not be of length zero. + + + FieldInfo must be a runtime FieldInfo object. + + + Field in TypedReferences cannot be static. + + + FieldInfo does not match the target Type. + + + TypedReferences cannot be redefined as primitives. + + + TypedReference can only be made on nested value Types. + + + The TypedReference must be initialized. + + + Cannot create an abstract class. + + + Cannot create an instance of an interface. + + + Cannot create a byref of a byref: {0} + + + Cannot create a pointer to a byref: {0} + + + Literal value was not found. + + + Cannot create boxed ByRef-like values. + + + Cannot instantiate a generic type on a byref-like type. + + + Non-static method requires a target. + + + Object does not match target type. + + + Nullable object must have a value. + + + Interface maps for generic interfaces on arrays cannot be retrieved. + + + CodeBase is not supported on assemblies loaded from a single-file bundle. + + diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj new file mode 100644 index 00000000000000..640fe1a215e29d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj @@ -0,0 +1,205 @@ + + + + $(NoWarn);CS0672 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System\NotImplemented.cs + + + + System\Collections\Generic\LowLevelList.cs + + + System\Collections\Generic\LowLevelDictionary.cs + + + Internal\LowLevelLinq\LowLevelEnumerable.cs + + + Internal\LowLevelLinq\LowLevelEnumerable.ToArray.cs + + + System\Collections\HashHelpers.cs + + + System\Collections\Generic\EnumerableExtensions.cs + + + System\Collections\Generic\Empty.cs + + + System\Collections\Concurrent\ConcurrentUnifier.cs + + + System\Collections\Concurrent\ConcurrentUnifierW.cs + + + System\Collections\Concurrent\ConcurrentUnifierWKeyed.cs + + + System\Collections\Concurrent\IKeyedItem.cs + + + System\Runtime\CompilerServices\DeveloperExperienceModeOnlyAttribute.cs + + + System\Runtime\CompilerServices\DeveloperExperienceState.cs + + + System\Runtime\CompilerServices\__BlockAllReflectionAttribute.cs + + + + + + diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs new file mode 100644 index 00000000000000..d5ea4c2dd34de2 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.BindingFlagSupport; + +using Internal.Runtime.Augments; + +namespace System +{ + internal static class ActivatorImplementation + { + [DebuggerGuidedStepThrough] + public static object CreateInstance( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type, bool nonPublic) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + type = type.UnderlyingSystemType; + CreateInstanceCheckType(type); + + BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; + if (nonPublic) + bindingFlags |= BindingFlags.NonPublic; + ConstructorInfo constructor = type.GetConstructor(bindingFlags, null, CallingConventions.Any, Array.Empty(), null); + if (constructor == null) + { + if (type.IsValueType) + return RuntimeAugments.NewObject(type.TypeHandle); + + throw new MissingMethodException(SR.Arg_NoDefCTor); + } + object result = constructor.Invoke(Array.Empty()); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + return result; + } + + [DebuggerGuidedStepThrough] + public static object CreateInstance( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + // If they didn't specify a lookup, then we will provide the default lookup. + const BindingFlags LookupMask = (BindingFlags)0x000000FF; + if ((bindingAttr & LookupMask) == 0) + bindingAttr |= BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance; + + if (activationAttributes != null && activationAttributes.Length > 0) + throw new PlatformNotSupportedException(SR.NotSupported_ActivAttr); + + type = type.UnderlyingSystemType; + CreateInstanceCheckType(type); + + if (args == null) + args = Array.Empty(); + int numArgs = args.Length; + + Type[] argTypes = new Type[numArgs]; + for (int i = 0; i < numArgs; i++) + { + argTypes[i] = args[i]?.GetType(); + } + + ConstructorInfo[] candidates = type.GetConstructors(bindingAttr); + ListBuilder matches = new ListBuilder(candidates.Length); + for (int i = 0; i < candidates.Length; i++) + { + if (candidates[i].QualifiesBasedOnParameterCount(bindingAttr, CallingConventions.Any, argTypes)) + matches.Add(candidates[i]); + } + if (matches.Count == 0) + { + if (numArgs == 0 && type.IsValueType) + return RuntimeAugments.NewObject(type.TypeHandle); + + throw new MissingMethodException(SR.Arg_NoDefCTor); + } + + if (binder == null) + binder = Type.DefaultBinder; + + object state = null; + MethodBase invokeMethod = binder.BindToMethod(bindingAttr, matches.ToArray(), ref args, null, culture, null, out state); + if (invokeMethod.GetParametersNoCopy().Length == 0) + { + if (args.Length != 0) + { + + Debug.Assert((invokeMethod.CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs); + throw new NotSupportedException(SR.NotSupported_CallToVarArg); + } + + // Desktop compat: CoreClr invokes a "fast-path" here (call Activator.CreateInstance(type, true)) that also + // bypasses the binder.ReorderArgumentArray() call. That "fast-path" isn't a fast-path for us so we won't do that + // but we'll still null out the "state" variable to bypass the Reorder call. + // + // The only time this matters at all is if (1) a third party binder is being used and (2) it actually reordered the array + // which it shouldn't have done because (a) we didn't request it to bind arguments by name, and (b) it's kinda hard to + // reorder a zero-length args array. But who knows what a third party binder will do if we make a call to it that we didn't + // used to do, so we'll preserve the CoreClr order of calls just to be safe. + state = null; + } + + object result = ((ConstructorInfo)invokeMethod).Invoke(bindingAttr, binder, args, culture); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + if (state != null) + binder.ReorderArgumentArray(ref args, state); + return result; + } + + private static void CreateInstanceCheckType(Type type) + { + if (type is not RuntimeType) + throw new ArgumentException(SR.Arg_MustBeType, nameof(type)); + + if (type.IsAbstract) + throw new MissingMethodException(type.IsInterface ? SR.Acc_CreateInterface : SR.Acc_CreateAbst); // Strange but compatible exception. + + if (type.ContainsGenericParameters) + throw new ArgumentException(SR.Format(SR.Acc_CreateGenericEx, type)); + + if (type.IsByRefLike) + throw new NotSupportedException(SR.NotSupported_ByRefLike); + + Type elementType = type; + while (elementType.HasElementType) + elementType = elementType.GetElementType(); + if (elementType == typeof(void)) + throw new NotSupportedException(SR.Acc_CreateVoid); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseInsensitive.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseInsensitive.cs new file mode 100644 index 00000000000000..266f6cba264005 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseInsensitive.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Text; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.Modules; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeParsing; +using System.Reflection.Runtime.CustomAttributes; +using System.Collections.Generic; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; +using Internal.Metadata.NativeFormat; + +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.Assemblies.NativeFormat +{ + internal partial class NativeFormatRuntimeAssembly + { + internal sealed override RuntimeTypeInfo GetTypeCoreCaseInsensitive(string fullName) + { + LowLevelDictionary dict = CaseInsensitiveTypeDictionary; + QHandle qualifiedHandle; + if (!dict.TryGetValue(fullName.ToLowerInvariant(), out qualifiedHandle)) + { + return null; + } + + MetadataReader reader = qualifiedHandle.Reader; + Handle typeDefOrForwarderHandle = qualifiedHandle.Handle; + + HandleType handleType = typeDefOrForwarderHandle.HandleType; + switch (handleType) + { + case HandleType.TypeDefinition: + { + TypeDefinitionHandle typeDefinitionHandle = typeDefOrForwarderHandle.ToTypeDefinitionHandle(reader); + return typeDefinitionHandle.ResolveTypeDefinition(reader); + } + case HandleType.TypeForwarder: + { + TypeForwarder typeForwarder = typeDefOrForwarderHandle.ToTypeForwarderHandle(reader).GetTypeForwarder(reader); + ScopeReferenceHandle destinationScope = typeForwarder.Scope; + RuntimeAssemblyName destinationAssemblyName = destinationScope.ToRuntimeAssemblyName(reader); + RuntimeAssemblyInfo destinationAssembly = RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(destinationAssemblyName); + if (destinationAssembly == null) + return null; + return destinationAssembly.GetTypeCoreCaseInsensitive(fullName); + } + default: + throw new InvalidOperationException(); + } + } + + private LowLevelDictionary CaseInsensitiveTypeDictionary + { + get + { + return _lazyCaseInsensitiveTypeDictionary ?? (_lazyCaseInsensitiveTypeDictionary = CreateCaseInsensitiveTypeDictionary()); + } + } + + private LowLevelDictionary CreateCaseInsensitiveTypeDictionary() + { + // + // Collect all of the *non-nested* types and type-forwards. + // + // The keys are full typenames in lower-cased form. + // The value is a tuple containing either a TypeDefinitionHandle or TypeForwarderHandle and the associated Reader + // for that handle. + // + // We do not store nested types here. The container type is resolved and chosen first, then the nested type chosen from + // that. If we chose the wrong container type and fail the match as a result, that's too bad. (The desktop CLR has the + // same issue.) + // + + LowLevelDictionary dict = new LowLevelDictionary(); + + foreach (QScopeDefinition scope in AllScopes) + { + MetadataReader reader = scope.Reader; + ScopeDefinition scopeDefinition = scope.ScopeDefinition; + IEnumerable topLevelNamespaceHandles = new NamespaceDefinitionHandle[] { scopeDefinition.RootNamespaceDefinition }; + IEnumerable allNamespaceHandles = reader.GetTransitiveNamespaces(topLevelNamespaceHandles); + foreach (NamespaceDefinitionHandle namespaceHandle in allNamespaceHandles) + { + string ns = namespaceHandle.ToNamespaceName(reader); + if (ns.Length != 0) + ns = ns + "."; + ns = ns.ToLowerInvariant(); + + NamespaceDefinition namespaceDefinition = namespaceHandle.GetNamespaceDefinition(reader); + foreach (TypeDefinitionHandle typeDefinitionHandle in namespaceDefinition.TypeDefinitions) + { + string fullName = ns + typeDefinitionHandle.GetTypeDefinition(reader).Name.GetString(reader).ToLowerInvariant(); + QHandle existingValue; + if (!dict.TryGetValue(fullName, out existingValue)) + { + dict.Add(fullName, new QHandle(reader, typeDefinitionHandle)); + } + } + + foreach (TypeForwarderHandle typeForwarderHandle in namespaceDefinition.TypeForwarders) + { + string fullName = ns + typeForwarderHandle.GetTypeForwarder(reader).Name.GetString(reader).ToLowerInvariant(); + QHandle existingValue; + if (!dict.TryGetValue(fullName, out existingValue)) + { + dict.Add(fullName, new QHandle(reader, typeForwarderHandle)); + } + } + } + } + + return dict; + } + + private volatile LowLevelDictionary _lazyCaseInsensitiveTypeDictionary; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseSensitive.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseSensitive.cs new file mode 100644 index 00000000000000..a9ef45fbd207e1 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseSensitive.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.Modules; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeParsing; +using System.Reflection.Runtime.CustomAttributes; +using System.Collections.Generic; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; +using Internal.Metadata.NativeFormat; + +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.Assemblies.NativeFormat +{ + internal partial class NativeFormatRuntimeAssembly + { + internal sealed override RuntimeTypeInfo UncachedGetTypeCoreCaseSensitive(string fullName) + { + string[] parts = fullName.Split('.'); + int numNamespaceParts = parts.Length - 1; + string[] namespaceParts = new string[numNamespaceParts]; + for (int i = 0; i < numNamespaceParts; i++) + namespaceParts[numNamespaceParts - i - 1] = parts[i]; + string name = parts[numNamespaceParts]; + + foreach (QScopeDefinition scopeDefinition in AllScopes) + { + MetadataReader reader = scopeDefinition.Reader; + ScopeDefinitionHandle scopeDefinitionHandle = scopeDefinition.Handle; + + NamespaceDefinition namespaceDefinition; + if (!TryResolveNamespaceDefinitionCaseSensitive(reader, namespaceParts, scopeDefinitionHandle, out namespaceDefinition)) + continue; + + // We've successfully drilled down the namespace chain. Now look for a top-level type matching the type name. + TypeDefinitionHandleCollection candidateTypes = namespaceDefinition.TypeDefinitions; + foreach (TypeDefinitionHandle candidateType in candidateTypes) + { + TypeDefinition typeDefinition = candidateType.GetTypeDefinition(reader); + if (typeDefinition.Name.StringEquals(name, reader)) + return candidateType.ResolveTypeDefinition(reader); + } + + // No match found in this assembly - see if there's a matching type forwarder. + TypeForwarderHandleCollection candidateTypeForwarders = namespaceDefinition.TypeForwarders; + foreach (TypeForwarderHandle typeForwarderHandle in candidateTypeForwarders) + { + TypeForwarder typeForwarder = typeForwarderHandle.GetTypeForwarder(reader); + if (typeForwarder.Name.StringEquals(name, reader)) + { + RuntimeAssemblyName redirectedAssemblyName = typeForwarder.Scope.ToRuntimeAssemblyName(reader); + RuntimeAssemblyInfo redirectedAssembly = RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(redirectedAssemblyName); + if (redirectedAssembly == null) + return null; + return redirectedAssembly.GetTypeCoreCaseSensitive(fullName); + } + } + } + + return null; + } + + private bool TryResolveNamespaceDefinitionCaseSensitive(MetadataReader reader, string[] namespaceParts, ScopeDefinitionHandle scopeDefinitionHandle, out NamespaceDefinition namespaceDefinition) + { + namespaceDefinition = scopeDefinitionHandle.GetScopeDefinition(reader).RootNamespaceDefinition.GetNamespaceDefinition(reader); + NamespaceDefinitionHandleCollection candidates = namespaceDefinition.NamespaceDefinitions; + int idx = namespaceParts.Length; + while (idx-- != 0) + { + // Each iteration finds a match for one segment of the namespace chain. + string expected = namespaceParts[idx]; + bool foundMatch = false; + foreach (NamespaceDefinitionHandle candidate in candidates) + { + namespaceDefinition = candidate.GetNamespaceDefinition(reader); + if (namespaceDefinition.Name.StringOrNullEquals(expected, reader)) + { + // Found a match for this segment of the namespace chain. Move on to the next level. + foundMatch = true; + candidates = namespaceDefinition.NamespaceDefinitions; + break; + } + } + + if (!foundMatch) + { + return false; + } + } + + return true; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.cs new file mode 100644 index 00000000000000..a6450efa380a27 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.cs @@ -0,0 +1,240 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Text; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.MethodInfos.NativeFormat; +using System.Reflection.Runtime.Modules; +using System.Reflection.Runtime.Modules.NativeFormat; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeInfos.NativeFormat; +using System.Reflection.Runtime.TypeParsing; +using System.Reflection.Runtime.CustomAttributes; +using System.Collections.Generic; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; +using Internal.Metadata.NativeFormat; +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.Assemblies.NativeFormat +{ + internal sealed partial class NativeFormatRuntimeAssembly : RuntimeAssemblyInfo + { + private NativeFormatRuntimeAssembly(MetadataReader reader, ScopeDefinitionHandle scope, IEnumerable overflowScopes) + { + Scope = new QScopeDefinition(reader, scope); + OverflowScopes = overflowScopes; + } + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.Assembly_CustomAttributes(this); +#endif + + foreach (QScopeDefinition scope in AllScopes) + { + foreach (CustomAttributeData cad in RuntimeCustomAttributeData.GetCustomAttributes(scope.Reader, scope.ScopeDefinition.CustomAttributes)) + yield return cad; + } + } + } + + public sealed override IEnumerable DefinedTypes + { + [RequiresUnreferencedCode("Types might be removed")] + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.Assembly_DefinedTypes(this); +#endif + + foreach (QScopeDefinition scope in AllScopes) + { + MetadataReader reader = scope.Reader; + ScopeDefinition scopeDefinition = scope.ScopeDefinition; + IEnumerable topLevelNamespaceHandles = new NamespaceDefinitionHandle[] { scopeDefinition.RootNamespaceDefinition }; + IEnumerable allNamespaceHandles = reader.GetTransitiveNamespaces(topLevelNamespaceHandles); + IEnumerable allTopLevelTypes = reader.GetTopLevelTypes(allNamespaceHandles); + IEnumerable allTypes = reader.GetTransitiveTypes(allTopLevelTypes, publicOnly: false); + foreach (TypeDefinitionHandle typeDefinitionHandle in allTypes) + yield return typeDefinitionHandle.GetNamedType(reader); + } + } + } + + public sealed override IEnumerable ExportedTypes + { + [RequiresUnreferencedCode("Types might be removed")] + get + { + foreach (QScopeDefinition scope in AllScopes) + { + MetadataReader reader = scope.Reader; + ScopeDefinition scopeDefinition = scope.ScopeDefinition; + IEnumerable topLevelNamespaceHandles = new NamespaceDefinitionHandle[] { scopeDefinition.RootNamespaceDefinition }; + IEnumerable allNamespaceHandles = reader.GetTransitiveNamespaces(topLevelNamespaceHandles); + IEnumerable allTopLevelTypes = reader.GetTopLevelTypes(allNamespaceHandles); + IEnumerable allTypes = reader.GetTransitiveTypes(allTopLevelTypes, publicOnly: true); + foreach (TypeDefinitionHandle typeDefinitionHandle in allTypes) + yield return typeDefinitionHandle.ResolveTypeDefinition(reader); + } + } + } + + public sealed override MethodInfo EntryPoint + { + get + { + // The scope that defines metadata for the owning type of the entrypoint will be the one + // to carry the entrypoint token information. Find it by iterating over all scopes. + + foreach (QScopeDefinition scope in AllScopes) + { + MetadataReader reader = scope.Reader; + + QualifiedMethodHandle entrypointHandle = scope.ScopeDefinition.EntryPoint; + if (!entrypointHandle.IsNull(reader)) + { + QualifiedMethod entrypointMethod = entrypointHandle.GetQualifiedMethod(reader); + TypeDefinitionHandle declaringTypeHandle = entrypointMethod.EnclosingType; + MethodHandle methodHandle = entrypointMethod.Method; + NativeFormatRuntimeNamedTypeInfo containingType = NativeFormatRuntimeNamedTypeInfo.GetRuntimeNamedTypeInfo(reader, declaringTypeHandle, default(RuntimeTypeHandle)); + return RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(new NativeFormatMethodCommon(methodHandle, containingType, containingType), containingType); + } + } + + return null; + } + } + + protected sealed override IEnumerable TypeForwardInfos + { + get + { + foreach (QScopeDefinition scope in AllScopes) + { + MetadataReader reader = scope.Reader; + ScopeDefinition scopeDefinition = scope.ScopeDefinition; + IEnumerable topLevelNamespaceHandles = new NamespaceDefinitionHandle[] { scopeDefinition.RootNamespaceDefinition }; + IEnumerable allNamespaceHandles = reader.GetTransitiveNamespaces(topLevelNamespaceHandles); + foreach (NamespaceDefinitionHandle namespaceHandle in allNamespaceHandles) + { + string namespaceName = null; + foreach (TypeForwarderHandle typeForwarderHandle in namespaceHandle.GetNamespaceDefinition(reader).TypeForwarders) + { + if (namespaceName == null) + { + namespaceName = namespaceHandle.ToNamespaceName(reader); + } + + TypeForwarder typeForwarder = typeForwarderHandle.GetTypeForwarder(reader); + string typeName = typeForwarder.Name.GetString(reader); + RuntimeAssemblyName redirectedAssemblyName = typeForwarder.Scope.ToRuntimeAssemblyName(reader); + + yield return new TypeForwardInfo(redirectedAssemblyName, namespaceName, typeName); + } + } + } + } + } + + public sealed override ManifestResourceInfo GetManifestResourceInfo(string resourceName) + { + return ReflectionCoreExecution.ExecutionEnvironment.GetManifestResourceInfo(this, resourceName); + } + + public sealed override string[] GetManifestResourceNames() + { + return ReflectionCoreExecution.ExecutionEnvironment.GetManifestResourceNames(this); + } + + public sealed override Stream GetManifestResourceStream(string name) + { + return ReflectionCoreExecution.ExecutionEnvironment.GetManifestResourceStream(this, name); + } + + public sealed override string ImageRuntimeVersion + { + get + { + // Needed to make RuntimeEnvironment.GetSystemVersion() work. Will not be correct always but anticipating most callers are not making + // actual decisions based on the value. + return "v4.0.30319"; + } + } + + public sealed override Module ManifestModule + { + get + { + return NativeFormatRuntimeModule.GetRuntimeModule(this); + } + } + + internal sealed override RuntimeAssemblyName RuntimeAssemblyName + { + get + { + return Scope.Handle.ToRuntimeAssemblyName(Scope.Reader); + } + } + + public sealed override bool Equals(object obj) + { + NativeFormatRuntimeAssembly other = obj as NativeFormatRuntimeAssembly; + return Equals(other); + } + + public bool Equals(NativeFormatRuntimeAssembly other) + { + if (other == null) + return false; + if (!(this.Scope.Reader == other.Scope.Reader)) + return false; + if (!(this.Scope.Handle.Equals(other.Scope.Handle))) + return false; + return true; + } + + public sealed override int GetHashCode() + { + return Scope.Handle.GetHashCode(); + } + + internal QScopeDefinition Scope { get; } + + internal IEnumerable OverflowScopes { get; } + + internal IEnumerable AllScopes + { + get + { + yield return Scope; + + foreach (QScopeDefinition overflowScope in OverflowScopes) + { + yield return overflowScope; + } + } + } + + internal sealed override void RunModuleConstructor() + { + // Nothing to do for the native format. ILC groups all module cctors into StartupCodeTrigger, and this executes at + // the begining of the process. All module cctors execute eagerly. + return; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/RuntimeAssemblyInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/RuntimeAssemblyInfo.cs new file mode 100644 index 00000000000000..5a5c5988b29f77 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/RuntimeAssemblyInfo.cs @@ -0,0 +1,342 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Text; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.Serialization; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.Modules; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeParsing; +using System.Reflection.Runtime.CustomAttributes; +using System.Collections.Generic; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; +using Internal.Reflection.Core.NonPortable; + +using Internal.Reflection.Tracing; +using System.Security; + +namespace System.Reflection.Runtime.Assemblies +{ + // + // The runtime's implementation of an Assembly. + // + internal abstract partial class RuntimeAssemblyInfo : RuntimeAssembly, IEquatable + { + public bool Equals(RuntimeAssemblyInfo other) + { + if (other == null) + return false; + + return this.Equals((object)other); + } + + public sealed override string FullName + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.Assembly_FullName(this); +#endif + + return GetName().FullName; + } + } + + public sealed override void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + public abstract override Module ManifestModule { get; } + + public sealed override IEnumerable Modules + { + get + { + yield return ManifestModule; + } + } + + [RequiresUnreferencedCode("Types might be removed")] + public sealed override Type GetType(string name, bool throwOnError, bool ignoreCase) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.Assembly_GetType(this, name); +#endif + + if (name == null) + throw new ArgumentNullException(); + if (name.Length == 0) + throw new ArgumentException(); + + TypeName typeName = TypeParser.ParseAssemblyQualifiedTypeName(name, throwOnError: throwOnError); + if (typeName == null) + return null; + if (typeName is AssemblyQualifiedTypeName) + { + if (throwOnError) + throw new ArgumentException(SR.Argument_AssemblyGetTypeCannotSpecifyAssembly); // Cannot specify an assembly qualifier in a typename passed to Assembly.GetType() + else + return null; + } + + CoreAssemblyResolver coreAssemblyResolver = GetRuntimeAssemblyIfExists; + CoreTypeResolver coreTypeResolver = + delegate (Assembly containingAssemblyIfAny, string coreTypeName) + { + if (containingAssemblyIfAny == null) + return GetTypeCore(coreTypeName, ignoreCase: ignoreCase); + else + return containingAssemblyIfAny.GetTypeCore(coreTypeName, ignoreCase: ignoreCase); + }; + GetTypeOptions getTypeOptions = new GetTypeOptions(coreAssemblyResolver, coreTypeResolver, throwOnError: throwOnError, ignoreCase: ignoreCase); + + return typeName.ResolveType(this, getTypeOptions); + } + +#pragma warning disable 0067 // Silence warning about ModuleResolve not being used. + public sealed override event ModuleResolveEventHandler ModuleResolve; +#pragma warning restore 0067 + + public sealed override bool ReflectionOnly => false; // ReflectionOnly loading not supported. + + public sealed override bool IsCollectible => false; // Unloading not supported. + + internal abstract RuntimeAssemblyName RuntimeAssemblyName { get; } + + public sealed override AssemblyName GetName() + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.Assembly_GetName(this); +#endif + return RuntimeAssemblyName.ToAssemblyName(); + } + + [RequiresUnreferencedCode("Types might be removed")] + public sealed override Type[] GetForwardedTypes() + { + List types = new List(); + List exceptions = null; + + foreach (TypeForwardInfo typeForwardInfo in TypeForwardInfos) + { + string fullTypeName = typeForwardInfo.NamespaceName.Length == 0 ? typeForwardInfo.TypeName : typeForwardInfo.NamespaceName + "." + typeForwardInfo.TypeName; + RuntimeAssemblyName redirectedAssemblyName = typeForwardInfo.RedirectedAssemblyName; + + Type type = null; + RuntimeAssemblyInfo redirectedAssembly; + Exception exception = TryGetRuntimeAssembly(redirectedAssemblyName, out redirectedAssembly); + if (exception == null) + { + type = redirectedAssembly.GetTypeCore(fullTypeName, ignoreCase: false); // GetTypeCore() will follow any further type-forwards if needed. + if (type == null) + exception = Helpers.CreateTypeLoadException(fullTypeName.EscapeTypeNameIdentifier(), redirectedAssembly); + } + + Debug.Assert((type != null) != (exception != null)); // Exactly one of these must be non-null. + + if (type != null) + { + types.Add(type); + AddPublicNestedTypes(type, types); + } + else + { + if (exceptions == null) + { + exceptions = new List(); + } + exceptions.Add(exception); + } + } + + if (exceptions != null) + { + int numTypes = types.Count; + int numExceptions = exceptions.Count; + types.AddRange(new Type[numExceptions]); // add one null Type for each exception. + exceptions.InsertRange(0, new Exception[numTypes]); // align the Exceptions with the null Types. + throw new ReflectionTypeLoadException(types.ToArray(), exceptions.ToArray()); + } + + return types.ToArray(); + } + + /// + /// Intentionally excludes forwards to nested types. + /// + protected abstract IEnumerable TypeForwardInfos { get; } + + [RequiresUnreferencedCode("Types might be removed")] + private static void AddPublicNestedTypes(Type type, List types) + { + foreach (Type nestedType in type.GetNestedTypes(BindingFlags.Public)) + { + types.Add(nestedType); + AddPublicNestedTypes(nestedType, types); + } + } + + /// + /// Helper routine for the more general Type.GetType() family of apis. + /// + /// Resolves top-level named types only. No nested types. No constructed types. + /// + /// Returns null if the type does not exist. Throws for all other error cases. + /// + internal RuntimeTypeInfo GetTypeCore(string fullName, bool ignoreCase) + { + if (ignoreCase) + return GetTypeCoreCaseInsensitive(fullName); + else + return GetTypeCoreCaseSensitive(fullName); + } + + // Types that derive from RuntimeAssembly must implement the following public surface area members + public abstract override IEnumerable CustomAttributes { get; } + public abstract override IEnumerable DefinedTypes + { + [RequiresUnreferencedCode("Types might be removed")] + get; + } + public abstract override MethodInfo EntryPoint { get; } + public abstract override IEnumerable ExportedTypes + { + [RequiresUnreferencedCode("Types might be removed")] + get; + } + public abstract override ManifestResourceInfo GetManifestResourceInfo(string resourceName); + public abstract override string[] GetManifestResourceNames(); + public abstract override Stream GetManifestResourceStream(string name); + public abstract override string ImageRuntimeVersion { get; } + public abstract override bool Equals(object obj); + public abstract override int GetHashCode(); + + /// + /// Ensures a module is loaded and that its module constructor is executed. If the module is fully + /// loaded and its constructor already ran, we do not run it again. + /// + internal abstract void RunModuleConstructor(); + + /// + /// Perform a lookup for a type based on a name. Overriders are expected to + /// have a non-cached implementation, as the result is expected to be cached by + /// callers of this method. Should be implemented by every format specific + /// RuntimeAssembly implementor + /// + internal abstract RuntimeTypeInfo UncachedGetTypeCoreCaseSensitive(string fullName); + + + /// + /// Perform a lookup for a type based on a name. Overriders may or may not + /// have a cached implementation, as the result is not expected to be cached by + /// callers of this method, but it is also a rarely used api. Should be + /// implemented by every format specific RuntimeAssembly implementor + /// + internal abstract RuntimeTypeInfo GetTypeCoreCaseInsensitive(string fullName); + + internal RuntimeTypeInfo GetTypeCoreCaseSensitive(string fullName) + { + return this.CaseSensitiveTypeTable.GetOrAdd(fullName); + } + + private CaseSensitiveTypeCache CaseSensitiveTypeTable + { + get + { + return _lazyCaseSensitiveTypeTable ?? (_lazyCaseSensitiveTypeTable = new CaseSensitiveTypeCache(this)); + } + } + + public sealed override bool GlobalAssemblyCache + { + get + { + return false; + } + } + + public sealed override long HostContext + { + get + { + return 0; + } + } + + [RequiresUnreferencedCode("Types and members the loaded module depends on might be removed")] + public sealed override Module LoadModule(string moduleName, byte[] rawModule, byte[] rawSymbolStore) + { + throw new PlatformNotSupportedException(); + } + + internal const string ThrowingMessageInRAF = "This member throws an exception for assemblies embedded in a single-file app"; + + [RequiresAssemblyFiles(ThrowingMessageInRAF)] + public sealed override FileStream GetFile(string name) + { + throw new PlatformNotSupportedException(); + } + + [RequiresAssemblyFiles(ThrowingMessageInRAF)] + public sealed override FileStream[] GetFiles(bool getResourceModules) + { + throw new PlatformNotSupportedException(); + } + + public sealed override SecurityRuleSet SecurityRuleSet + { + get + { + return SecurityRuleSet.None; + } + } + + /// + /// Returns a *freshly allocated* array of loaded Assemblies. + /// + internal static Assembly[] GetLoadedAssemblies() + { + // Important: The result of this method is the return value of the AppDomain.GetAssemblies() api so + // so it must return a freshly allocated array on each call. + + AssemblyBinder binder = ReflectionCoreExecution.ExecutionDomain.ReflectionDomainSetup.AssemblyBinder; + IList bindResults = binder.GetLoadedAssemblies(); + Assembly[] results = new Assembly[bindResults.Count]; + for (int i = 0; i < bindResults.Count; i++) + { + Assembly assembly = GetRuntimeAssembly(bindResults[i]); + results[i] = assembly; + } + return results; + } + + private volatile CaseSensitiveTypeCache _lazyCaseSensitiveTypeTable; + + private sealed class CaseSensitiveTypeCache : ConcurrentUnifier + { + public CaseSensitiveTypeCache(RuntimeAssemblyInfo runtimeAssembly) + { + _runtimeAssembly = runtimeAssembly; + } + + protected sealed override RuntimeTypeInfo Factory(string key) + { + return _runtimeAssembly.UncachedGetTypeCoreCaseSensitive(key); + } + + private readonly RuntimeAssemblyInfo _runtimeAssembly; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/ConstructorPolicies.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/ConstructorPolicies.cs new file mode 100644 index 00000000000000..6b7b539996aec7 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/ConstructorPolicies.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Reflection.Runtime.TypeInfos; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + //========================================================================================================================== + // Policies for constructors. + //========================================================================================================================== + internal sealed class ConstructorPolicies : MemberPolicies + { + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Reflection implementation")] + public sealed override IEnumerable GetDeclaredMembers(TypeInfo typeInfo) + { + return typeInfo.DeclaredConstructors; + } + + public sealed override IEnumerable CoreGetDeclaredMembers(RuntimeTypeInfo type, NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType) + { + Debug.Assert(reflectedType.Equals(type)); // Constructor queries are always performed as if BindingFlags.DeclaredOnly are set so the reflectedType should always be the declaring type. + return type.CoreGetDeclaredConstructors(optionalNameFilter); + } + + public sealed override BindingFlags ModifyBindingFlags(BindingFlags bindingFlags) + { + // Constructors are not inherited. + return bindingFlags | BindingFlags.DeclaredOnly; + } + + public sealed override bool AlwaysTreatAsDeclaredOnly => true; + + public sealed override void GetMemberAttributes(ConstructorInfo member, out MethodAttributes visibility, out bool isStatic, out bool isVirtual, out bool isNewSlot) + { + MethodAttributes methodAttributes = member.Attributes; + visibility = methodAttributes & MethodAttributes.MemberAccessMask; + isStatic = (0 != (methodAttributes & MethodAttributes.Static)); + isVirtual = false; + isNewSlot = false; + } + + public sealed override bool ImplicitlyOverrides(ConstructorInfo baseMember, ConstructorInfo derivedMember) => false; + + public sealed override bool IsSuppressedByMoreDerivedMember(ConstructorInfo member, ConstructorInfo[] priorMembers, int startIndex, int endIndex) + { + return false; + } + + public sealed override bool OkToIgnoreAmbiguity(ConstructorInfo m1, ConstructorInfo m2) + { + // Constructors are only resolvable using an array of parameter types so this should never be called. + Debug.Fail("This code path should be unreachable."); + throw new NotSupportedException(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/EventPolicies.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/EventPolicies.cs new file mode 100644 index 00000000000000..8b2716dfce9f13 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/EventPolicies.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Reflection.Runtime.TypeInfos; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + //========================================================================================================================== + // Policies for events. + //========================================================================================================================== + internal sealed class EventPolicies : MemberPolicies + { + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Reflection implementation")] + public sealed override IEnumerable GetDeclaredMembers(TypeInfo typeInfo) + { + return typeInfo.DeclaredEvents; + } + + public sealed override IEnumerable CoreGetDeclaredMembers(RuntimeTypeInfo type, NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType) + { + return type.CoreGetDeclaredEvents(optionalNameFilter, reflectedType); + } + + public sealed override bool AlwaysTreatAsDeclaredOnly => false; + + public sealed override void GetMemberAttributes(EventInfo member, out MethodAttributes visibility, out bool isStatic, out bool isVirtual, out bool isNewSlot) + { + MethodInfo accessorMethod = GetAccessorMethod(member); + MethodAttributes methodAttributes = accessorMethod.Attributes; + visibility = methodAttributes & MethodAttributes.MemberAccessMask; + isStatic = (0 != (methodAttributes & MethodAttributes.Static)); + isVirtual = (0 != (methodAttributes & MethodAttributes.Virtual)); + isNewSlot = (0 != (methodAttributes & MethodAttributes.NewSlot)); + } + + // + // Desktop compat: Events hide events in base types if they have the same name. + // + public sealed override bool IsSuppressedByMoreDerivedMember(EventInfo member, EventInfo[] priorMembers, int startIndex, int endIndex) + { + for (int i = startIndex; i < endIndex; i++) + { + if (priorMembers[i].Name == member.Name) + return true; + } + return false; + } + + public sealed override bool ImplicitlyOverrides(EventInfo baseMember, EventInfo derivedMember) + { + MethodInfo baseAccessor = GetAccessorMethod(baseMember); + MethodInfo derivedAccessor = GetAccessorMethod(derivedMember); + return MemberPolicies.Default.ImplicitlyOverrides(baseAccessor, derivedAccessor); + } + + public sealed override bool OkToIgnoreAmbiguity(EventInfo m1, EventInfo m2) + { + return false; + } + + private MethodInfo GetAccessorMethod(EventInfo e) + { + MethodInfo accessor = e.AddMethod; + return accessor; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/FieldPolicies.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/FieldPolicies.cs new file mode 100644 index 00000000000000..d17a43ce09252f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/FieldPolicies.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Reflection.Runtime.TypeInfos; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + //========================================================================================================================== + // Policies for fields. + //========================================================================================================================== + internal sealed class FieldPolicies : MemberPolicies + { + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Reflection implementation")] + public sealed override IEnumerable GetDeclaredMembers(TypeInfo typeInfo) + { + return typeInfo.DeclaredFields; + } + + public sealed override IEnumerable CoreGetDeclaredMembers(RuntimeTypeInfo type, NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType) + { + return type.CoreGetDeclaredFields(optionalNameFilter, reflectedType); + } + + public sealed override bool AlwaysTreatAsDeclaredOnly => false; + + public sealed override void GetMemberAttributes(FieldInfo member, out MethodAttributes visibility, out bool isStatic, out bool isVirtual, out bool isNewSlot) + { + FieldAttributes fieldAttributes = member.Attributes; + visibility = (MethodAttributes)(fieldAttributes & FieldAttributes.FieldAccessMask); + isStatic = (0 != (fieldAttributes & FieldAttributes.Static)); + isVirtual = false; + isNewSlot = false; + } + + public sealed override bool ImplicitlyOverrides(FieldInfo baseMember, FieldInfo derivedMember) => false; + + public sealed override bool IsSuppressedByMoreDerivedMember(FieldInfo member, FieldInfo[] priorMembers, int startIndex, int endIndex) + { + return false; + } + + public sealed override bool OkToIgnoreAmbiguity(FieldInfo m1, FieldInfo m2) + { + return true; // Unlike most member types, Field ambiguities are tolerated as long as they're defined in different classes. + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MemberPolicies.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MemberPolicies.cs new file mode 100644 index 00000000000000..cbb620e5be3c39 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MemberPolicies.cs @@ -0,0 +1,238 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.TypeInfos; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + //================================================================================================================= + // This class encapsulates the minimum set of arcane desktop CLR policies needed to implement the Get*(BindingFlags) apis. + // + // In particular, it encapsulates behaviors such as what exactly determines the "visibility" of a property and event, and + // what determines whether and how they are overridden. + //================================================================================================================= + internal abstract class MemberPolicies where M : MemberInfo + { + //================================================================================================================= + // Subclasses for specific MemberInfo types must override these: + //================================================================================================================= + + // + // Returns all of the directly declared members on the given TypeInfo. + // + public abstract IEnumerable GetDeclaredMembers(TypeInfo typeInfo); + + // + // Returns all of the directly declared members on the given TypeInfo whose name matches optionalNameFilter. If optionalNameFilter is null, + // returns all directly declared members. + // + public abstract IEnumerable CoreGetDeclaredMembers(RuntimeTypeInfo type, NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType); + + // + // Policy to decide whether a member is considered "virtual", "virtual new" and what its member visibility is. + // (For "visibility", we reuse the MethodAttributes enum since Reflection lacks an element-agnostic enum for this. + // Only the MemberAccessMask bits are set.) + // + public abstract void GetMemberAttributes(M member, out MethodAttributes visibility, out bool isStatic, out bool isVirtual, out bool isNewSlot); + + // + // Policy to decide whether "derivedMember" is a virtual override of "baseMember." Used to implement MethodInfo.GetBaseDefinition(), + // parent chain traversal for discovering inherited custom attributes, and suppressing lookup results in the Type.Get*() api family. + // + // Does not consider explicit overrides (methodimpls.) Does not consider "overrides" of interface methods. + // + public abstract bool ImplicitlyOverrides(M baseMember, M derivedMember); + + // + // Policy to decide how BindingFlags should be reinterpreted for a given member type. + // This is overridden for nested types which all match on any combination Instance | Static and are never inherited. + // It is also overridden for constructors which are never inherited. + // + public virtual BindingFlags ModifyBindingFlags(BindingFlags bindingFlags) + { + return bindingFlags; + } + + // + // Policy to decide if BindingFlags is always interpreted as having set DeclaredOnly. + // + public abstract bool AlwaysTreatAsDeclaredOnly { get; } + + // + // Policy to decide how or if members in more derived types hide same-named members in base types. + // Due to desktop compat concerns, the definitions are a bit more arbitrary than we'd like. + // + public abstract bool IsSuppressedByMoreDerivedMember(M member, M[] priorMembers, int startIndex, int endIndex); + + // + // Policy to decide whether to throw an AmbiguousMatchException on an ambiguous Type.Get*() call. + // Does not apply to GetConstructor/GetMethod/GetProperty calls that have a non-null Type[] array passed to it. + // + // If method returns true, the Get() api will pick the member that's in the most derived type. + // If method returns false, the Get() api throws AmbiguousMatchException. + // + public abstract bool OkToIgnoreAmbiguity(M m1, M m2); + + // + // Helper method for determining whether two methods are signature-compatible. + // + protected static bool AreNamesAndSignaturesEqual(MethodInfo method1, MethodInfo method2) + { + if (method1.Name != method2.Name) + return false; + + ParameterInfo[] p1 = method1.GetParametersNoCopy(); + ParameterInfo[] p2 = method2.GetParametersNoCopy(); + if (p1.Length != p2.Length) + return false; + + bool isGenericMethod1 = method1.IsGenericMethodDefinition; + bool isGenericMethod2 = method2.IsGenericMethodDefinition; + if (isGenericMethod1 != isGenericMethod2) + return false; + if (!isGenericMethod1) + { + for (int i = 0; i < p1.Length; i++) + { + Type parameterType1 = p1[i].ParameterType; + Type parameterType2 = p2[i].ParameterType; + if (!(parameterType1.Equals(parameterType2))) + { + return false; + } + } + } + else + { + if (method1.GetGenericArguments().Length != method2.GetGenericArguments().Length) + return false; + for (int i = 0; i < p1.Length; i++) + { + Type parameterType1 = p1[i].ParameterType; + Type parameterType2 = p2[i].ParameterType; + if (!GenericMethodAwareAreParameterTypesEqual(parameterType1, parameterType2)) + { + return false; + } + } + } + return true; + } + + // + // This helper compares the types of the corresponding parameters of two methods to see if one method is signature equivalent to the other. + // This is needed when comparing the signatures of two generic methods as Type.Equals() is not up to that job. + // + private static bool GenericMethodAwareAreParameterTypesEqual(Type t1, Type t2) + { + // Fast-path - if Reflection has already deemed them equivalent, we can trust its result. + if (t1.Equals(t2)) + return true; + + // If we got here, Reflection determined the types not equivalent. Most of the time, that's the result we want. + // There is however, one wrinkle. If the type is or embeds a generic method parameter type, Reflection will always report them + // non-equivalent, since generic parameter type comparison always compares both the position and the declaring method. For our purposes, though, + // we only want to consider the position. + + // Fast-path: if the types don't embed any generic parameters, we can go ahead and use Reflection's result. + if (!(t1.ContainsGenericParameters && t2.ContainsGenericParameters)) + return false; + + if ((t1.IsArray && t2.IsArray) || (t1.IsByRef && t2.IsByRef) || (t1.IsPointer && t2.IsPointer)) + { + if (t1.IsSZArray != t2.IsSZArray) + return false; + + if (t1.IsArray && (t1.GetArrayRank() != t2.GetArrayRank())) + return false; + + return GenericMethodAwareAreParameterTypesEqual(t1.GetElementType(), t2.GetElementType()); + } + + if (t1.IsConstructedGenericType && t2.IsConstructedGenericType) + { + // We can use regular old Equals() rather than recursing into GenericMethodAwareAreParameterTypesEqual() since the + // generic type definition will always be a plain old named type and won't embed any generic method parameters. + if (!(t1.GetGenericTypeDefinition().Equals(t2.GetGenericTypeDefinition()))) + return false; + + Type[] ga1 = t1.GenericTypeArguments; + Type[] ga2 = t2.GenericTypeArguments; + if (ga1.Length != ga2.Length) + return false; + + for (int i = 0; i < ga1.Length; i++) + { + if (!GenericMethodAwareAreParameterTypesEqual(ga1[i], ga2[i])) + return false; + } + return true; + } + + if (t1.IsGenericMethodParameter && t2.IsGenericMethodParameter) + { + // A generic method parameter. The DeclaringMethods will be different but we don't care about that - we can assume that + // the declaring method will be the method that declared the parameter's whose type we're testing. We only need to + // compare the positions. + return t1.GenericParameterPosition == t2.GenericParameterPosition; + } + + // If we got here, either t1 and t2 are different flavors of types or they are both simple named types or both generic type parameters. + // Either way, we can trust Reflection's result here. + return false; + } + + static MemberPolicies() + { + Type t = typeof(M); + if (t.Equals(typeof(FieldInfo))) + { + MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.Field; + Default = (MemberPolicies)(object)(new FieldPolicies()); + } + else if (t.Equals(typeof(MethodInfo))) + { + MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.Method; + Default = (MemberPolicies)(object)(new MethodPolicies()); + } + else if (t.Equals(typeof(ConstructorInfo))) + { + MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.Constructor; + Default = (MemberPolicies)(object)(new ConstructorPolicies()); + } + else if (t.Equals(typeof(PropertyInfo))) + { + MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.Property; ; + Default = (MemberPolicies)(object)(new PropertyPolicies()); + } + else if (t.Equals(typeof(EventInfo))) + { + MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.Event; + Default = (MemberPolicies)(object)(new EventPolicies()); + } + else if (t.Equals(typeof(Type))) + { + MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.NestedType; + Default = (MemberPolicies)(object)(new NestedTypePolicies()); + } + else + { + Debug.Fail("Unknown MemberInfo type."); + } + } + + // + // This is a singleton class one for each MemberInfo category: Return the appropriate one. + // + public static readonly MemberPolicies Default; + + // + // This returns a fixed value from 0 to MemberIndex.Count-1 with each possible type of M + // being assigned a unique index (see the MemberTypeIndex for possible values). This is useful + // for converting a type reference to M to an array index or switch case label. + // + public static readonly int MemberTypeIndex; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MemberTypeIndex.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MemberTypeIndex.cs new file mode 100644 index 00000000000000..b225d098f975fc --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MemberTypeIndex.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + // + // When we want to store an object of type Foo for each possible M, it's convenient to use + // an array of length MemberTypeIndex.Count, which each possible M assigned an index. + // + // This is defined as a set of consts rather than enum to avoid having to cast to int. + // + internal static class MemberTypeIndex + { + public const int Constructor = 0; + public const int Event = 1; + public const int Field = 2; + public const int Method = 3; + public const int NestedType = 4; + public const int Property = 5; + + public const int Count = 6; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MethodPolicies.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MethodPolicies.cs new file mode 100644 index 00000000000000..41b6452300c4f1 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MethodPolicies.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Reflection.Runtime.TypeInfos; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + //========================================================================================================================== + // Policies for methods. + //========================================================================================================================== + internal sealed class MethodPolicies : MemberPolicies + { + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Reflection implementation")] + public sealed override IEnumerable GetDeclaredMembers(TypeInfo typeInfo) + { + return typeInfo.DeclaredMethods; + } + + public sealed override IEnumerable CoreGetDeclaredMembers(RuntimeTypeInfo type, NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType) + { + return type.CoreGetDeclaredMethods(optionalNameFilter, reflectedType); + } + + public sealed override bool AlwaysTreatAsDeclaredOnly => false; + + public sealed override void GetMemberAttributes(MethodInfo member, out MethodAttributes visibility, out bool isStatic, out bool isVirtual, out bool isNewSlot) + { + MethodAttributes methodAttributes = member.Attributes; + visibility = methodAttributes & MethodAttributes.MemberAccessMask; + isStatic = (0 != (methodAttributes & MethodAttributes.Static)); + isVirtual = (0 != (methodAttributes & MethodAttributes.Virtual)); + isNewSlot = (0 != (methodAttributes & MethodAttributes.NewSlot)); + } + + public sealed override bool ImplicitlyOverrides(MethodInfo baseMember, MethodInfo derivedMember) + { + // TODO (https://github.com/dotnet/corert/issues/1896) Comparing signatures is lame. The runtime and/or toolchain should have a way of sharing this info. + return AreNamesAndSignaturesEqual(baseMember, derivedMember); + } + + // + // Methods hide methods in base types if they share the same vtable slot. + // + public sealed override bool IsSuppressedByMoreDerivedMember(MethodInfo member, MethodInfo[] priorMembers, int startIndex, int endIndex) + { + if (!member.IsVirtual) + return false; + + for (int i = startIndex; i < endIndex; i++) + { + MethodInfo prior = priorMembers[i]; + MethodAttributes attributes = prior.Attributes & (MethodAttributes.Virtual | MethodAttributes.VtableLayoutMask); + if (attributes != (MethodAttributes.Virtual | MethodAttributes.ReuseSlot)) + continue; + if (!ImplicitlyOverrides(member, prior)) + continue; + + return true; + } + return false; + } + + public sealed override bool OkToIgnoreAmbiguity(MethodInfo m1, MethodInfo m2) + { + return DefaultBinder.CompareMethodSig(m1, m2); // If all candidates have the same signature, pick the most derived one without throwing an AmbiguousMatchException. + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/NameFilter.NativeFormat.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/NameFilter.NativeFormat.cs new file mode 100644 index 00000000000000..88962ffc1d002d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/NameFilter.NativeFormat.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + internal abstract partial class NameFilter + { + public abstract bool Matches(ConstantStringValueHandle stringHandle, MetadataReader reader); + } + + internal sealed partial class NameFilterCaseSensitive : NameFilter + { + public sealed override bool Matches(ConstantStringValueHandle stringHandle, MetadataReader reader) => stringHandle.StringEquals(ExpectedName, reader); + } + + internal sealed partial class NameFilterCaseInsensitive : NameFilter + { + public sealed override bool Matches(ConstantStringValueHandle stringHandle, MetadataReader reader) => stringHandle.GetConstantStringValue(reader).Value.Equals(ExpectedName, StringComparison.OrdinalIgnoreCase); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/NameFilter.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/NameFilter.cs new file mode 100644 index 00000000000000..2fe7754a6f2744 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/NameFilter.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + internal abstract partial class NameFilter + { + protected NameFilter(string expectedName) + { + ExpectedName = expectedName; + } + + public abstract bool Matches(string name); + protected string ExpectedName { get; } + } + + internal sealed partial class NameFilterCaseSensitive : NameFilter + { + public NameFilterCaseSensitive(string expectedName) + : base(expectedName) + { + } + + public sealed override bool Matches(string name) => name.Equals(ExpectedName, StringComparison.Ordinal); + } + + internal sealed partial class NameFilterCaseInsensitive : NameFilter + { + public NameFilterCaseInsensitive(string expectedName) + : base(expectedName) + { + } + + public sealed override bool Matches(string name) => name.Equals(ExpectedName, StringComparison.OrdinalIgnoreCase); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/NestedTypePolicies.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/NestedTypePolicies.cs new file mode 100644 index 00000000000000..d22d42ac12e309 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/NestedTypePolicies.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Reflection.Runtime.TypeInfos; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + //========================================================================================================================== + // Policies for nested types. + // + // Nested types enumerate a little differently than other members: + // + // Base classes are never searched, regardless of BindingFlags.DeclaredOnly value. + // + // Public|NonPublic|IgnoreCase are the only relevant BindingFlags. The apis ignore any other bits. + // + // There is no such thing as a "static" or "instanced" nested type. For enumeration purposes, + // we'll arbitrarily denote all nested types as "static." + // + //========================================================================================================================== + internal sealed class NestedTypePolicies : MemberPolicies + { + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Reflection implementation")] + public sealed override IEnumerable GetDeclaredMembers(TypeInfo typeInfo) + { + return typeInfo.DeclaredNestedTypes; + } + + public sealed override IEnumerable CoreGetDeclaredMembers(RuntimeTypeInfo type, NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType) + { + Debug.Assert(reflectedType.Equals(type)); // NestedType queries are always performed as if BindingFlags.DeclaredOnly are set so the reflectedType should always be the declaring type. + return type.CoreGetDeclaredNestedTypes(optionalNameFilter); + } + + public sealed override bool AlwaysTreatAsDeclaredOnly => true; + + public sealed override void GetMemberAttributes(Type member, out MethodAttributes visibility, out bool isStatic, out bool isVirtual, out bool isNewSlot) + { + isStatic = true; + isVirtual = false; + isNewSlot = false; + + // Since we never search base types for nested types, we don't need to map every visibility value one to one. + // We just need to distinguish between "public" and "everything else." + visibility = member.IsNestedPublic ? MethodAttributes.Public : MethodAttributes.Private; + } + + public sealed override bool ImplicitlyOverrides(Type baseMember, Type derivedMember) => false; + + public sealed override bool IsSuppressedByMoreDerivedMember(Type member, Type[] priorMembers, int startIndex, int endIndex) + { + return false; + } + + public sealed override BindingFlags ModifyBindingFlags(BindingFlags bindingFlags) + { + bindingFlags &= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase; + bindingFlags |= BindingFlags.Static | BindingFlags.DeclaredOnly; + return bindingFlags; + } + + public sealed override bool OkToIgnoreAmbiguity(Type m1, Type m2) + { + return false; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/PropertyPolicies.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/PropertyPolicies.cs new file mode 100644 index 00000000000000..458b4f71665258 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/PropertyPolicies.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Reflection.Runtime.TypeInfos; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + //========================================================================================================================== + // Policies for properties. + //========================================================================================================================== + internal sealed class PropertyPolicies : MemberPolicies + { + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Reflection implementation")] + public sealed override IEnumerable GetDeclaredMembers(TypeInfo typeInfo) + { + return typeInfo.DeclaredProperties; + } + + public sealed override IEnumerable CoreGetDeclaredMembers(RuntimeTypeInfo type, NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType) + { + return type.CoreGetDeclaredProperties(optionalNameFilter, reflectedType); + } + + public sealed override bool AlwaysTreatAsDeclaredOnly => false; + + public sealed override void GetMemberAttributes(PropertyInfo member, out MethodAttributes visibility, out bool isStatic, out bool isVirtual, out bool isNewSlot) + { + MethodInfo accessorMethod = GetAccessorMethod(member); + if (accessorMethod == null) + { + // If we got here, this is a inherited PropertyInfo that only had private accessors and is now refusing to give them out + // because that's what the rules of inherited PropertyInfo's are. Such a PropertyInfo is also considered private and will never be + // given out of a Type.GetProperty() call. So all we have to do is set its visibility to Private and it will get filtered out. + // Other values need to be set to satisify C# but they are meaningless. + visibility = MethodAttributes.Private; + isStatic = false; + isVirtual = false; + isNewSlot = true; + return; + } + + MethodAttributes methodAttributes = accessorMethod.Attributes; + visibility = methodAttributes & MethodAttributes.MemberAccessMask; + isStatic = (0 != (methodAttributes & MethodAttributes.Static)); + isVirtual = (0 != (methodAttributes & MethodAttributes.Virtual)); + isNewSlot = (0 != (methodAttributes & MethodAttributes.NewSlot)); + } + + public sealed override bool ImplicitlyOverrides(PropertyInfo baseMember, PropertyInfo derivedMember) + { + MethodInfo baseAccessor = GetAccessorMethod(baseMember); + MethodInfo derivedAccessor = GetAccessorMethod(derivedMember); + return MemberPolicies.Default.ImplicitlyOverrides(baseAccessor, derivedAccessor); + } + + // + // Desktop compat: Properties hide properties in base types if they share the same vtable slot, or + // have the same name, return type, signature and hasThis value. + // + public sealed override bool IsSuppressedByMoreDerivedMember(PropertyInfo member, PropertyInfo[] priorMembers, int startIndex, int endIndex) + { + MethodInfo baseAccessor = GetAccessorMethod(member); + for (int i = startIndex; i < endIndex; i++) + { + PropertyInfo prior = priorMembers[i]; + MethodInfo derivedAccessor = GetAccessorMethod(prior); + if (!AreNamesAndSignaturesEqual(baseAccessor, derivedAccessor)) + continue; + if (derivedAccessor.IsStatic != baseAccessor.IsStatic) + continue; + if (!(prior.PropertyType.Equals(member.PropertyType))) + continue; + + return true; + } + return false; + } + + public sealed override bool OkToIgnoreAmbiguity(PropertyInfo m1, PropertyInfo m2) + { + return false; + } + + private MethodInfo GetAccessorMethod(PropertyInfo property) + { + MethodInfo accessor = property.GetMethod; + if (accessor == null) + { + accessor = property.SetMethod; + } + + return accessor; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/QueriedMemberList.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/QueriedMemberList.cs new file mode 100644 index 00000000000000..d1ce2475ea8fd5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/QueriedMemberList.cs @@ -0,0 +1,203 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; + +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + // + // Stores the result of a member filtering that's filtered by name and visibility from base class (as defined by the Type.Get*() family of apis). + // + // The results are as if you'd passed in a bindingFlags value of "Public | NonPublic | Instance | Static | FlattenHierarchy" + // In addition, if "ignoreCase" was passed to Create(), BindingFlags.IgnoreCase is also in effect. + // + // Results are sorted by declaring type. The members declared by the most derived type appear first, then those declared by his base class, and so on. + // The Disambiguation logic takes advantage of this. + // + // This object is a good candidate for long term caching. + // + internal sealed class QueriedMemberList where M : MemberInfo + { + private QueriedMemberList() + { + _members = new M[Grow]; + _allFlagsThatMustMatch = new BindingFlags[Grow]; + } + + private QueriedMemberList(int totalCount, int declaredOnlyCount, M[] members, BindingFlags[] allFlagsThatMustMatch, RuntimeTypeInfo typeThatBlockedBrowsing) + { + _totalCount = totalCount; + _declaredOnlyCount = declaredOnlyCount; + _members = members; + _allFlagsThatMustMatch = allFlagsThatMustMatch; + _typeThatBlockedBrowsing = typeThatBlockedBrowsing; + } + + /// + /// Returns the # of candidates for a non-DeclaredOnly search. Caution: Can throw MissingMetadataException. Use DeclaredOnlyCount if you don't want to search base classes. + /// + public int TotalCount + { + get + { + if (_typeThatBlockedBrowsing != null) + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(_typeThatBlockedBrowsing); + return _totalCount; + } + } + + /// + /// Returns the # of candidates for a DeclaredOnly search + /// + public int DeclaredOnlyCount => _declaredOnlyCount; + + public M this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Debug.Assert(index >= 0 && index < _totalCount); + return _members[index]; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Matches(int index, BindingFlags bindingAttr) + { + Debug.Assert(index >= 0 && index < _totalCount); + BindingFlags allFlagsThatMustMatch = _allFlagsThatMustMatch[index]; + return ((bindingAttr & allFlagsThatMustMatch) == allFlagsThatMustMatch); + } + + public QueriedMemberList Filter(Func predicate) + { + BindingFlags[] newAllFlagsThatMustMatch = new BindingFlags[_totalCount]; + M[] newMembers = new M[_totalCount]; + int newDeclaredOnlyCount = 0; + int newTotalCount = 0; + for (int i = 0; i < _totalCount; i++) + { + M member = _members[i]; + if (predicate(member)) + { + newMembers[newTotalCount] = member; + newAllFlagsThatMustMatch[newTotalCount] = _allFlagsThatMustMatch[i]; + newTotalCount++; + if (i < _declaredOnlyCount) + newDeclaredOnlyCount++; + } + } + + return new QueriedMemberList(newTotalCount, newDeclaredOnlyCount, newMembers, newAllFlagsThatMustMatch, _typeThatBlockedBrowsing); + } + + // + // Filter by name and visibility from the ReflectedType. + // + public static QueriedMemberList Create(RuntimeTypeInfo type, string optionalNameFilter, bool ignoreCase) + { + RuntimeTypeInfo reflectedType = type; + + MemberPolicies policies = MemberPolicies.Default; + + NameFilter nameFilter; + if (optionalNameFilter == null) + nameFilter = null; + else if (ignoreCase) + nameFilter = new NameFilterCaseInsensitive(optionalNameFilter); + else + nameFilter = new NameFilterCaseSensitive(optionalNameFilter); + + bool inBaseClass = false; + QueriedMemberList queriedMembers = new QueriedMemberList(); + while (type != null) + { + int numCandidatesInDerivedTypes = queriedMembers._totalCount; + + foreach (M member in policies.CoreGetDeclaredMembers(type, nameFilter, reflectedType)) + { + MethodAttributes visibility; + bool isStatic; + bool isVirtual; + bool isNewSlot; + policies.GetMemberAttributes(member, out visibility, out isStatic, out isVirtual, out isNewSlot); + + if (inBaseClass && visibility == MethodAttributes.Private) + continue; + + if (numCandidatesInDerivedTypes != 0 && policies.IsSuppressedByMoreDerivedMember(member, queriedMembers._members, startIndex: 0, endIndex: numCandidatesInDerivedTypes)) + continue; + + BindingFlags allFlagsThatMustMatch = default(BindingFlags); + allFlagsThatMustMatch |= (isStatic ? BindingFlags.Static : BindingFlags.Instance); + if (isStatic && inBaseClass) + allFlagsThatMustMatch |= BindingFlags.FlattenHierarchy; + allFlagsThatMustMatch |= ((visibility == MethodAttributes.Public) ? BindingFlags.Public : BindingFlags.NonPublic); + + queriedMembers.Add(member, allFlagsThatMustMatch); + } + + if (!inBaseClass) + { + queriedMembers._declaredOnlyCount = queriedMembers._totalCount; + if (policies.AlwaysTreatAsDeclaredOnly) + break; + inBaseClass = true; + } + + type = type.BaseType.CastToRuntimeTypeInfo(); + + if (type != null && !type.CanBrowseWithoutMissingMetadataExceptions) + { + // If we got here, one of the base classes is missing metadata. We don't want to throw a MissingMetadataException now because we may be + // building a cached result for a caller who passed BindingFlags.DeclaredOnly. So we'll mark the results in a way that + // it will throw a MissingMetadataException if a caller attempts to iterate past the declared-only subset. + queriedMembers._typeThatBlockedBrowsing = type; + queriedMembers._totalCount = queriedMembers._declaredOnlyCount; + break; + } + } + + return queriedMembers; + } + + public void Compact() + { + Array.Resize(ref _members, _totalCount); + Array.Resize(ref _allFlagsThatMustMatch, _totalCount); + } + + private void Add(M member, BindingFlags allFlagsThatMustMatch) + { + const BindingFlags validBits = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy; + Debug.Assert((allFlagsThatMustMatch & ~validBits) == 0); + Debug.Assert(((allFlagsThatMustMatch & BindingFlags.Public) == 0) != ((allFlagsThatMustMatch & BindingFlags.NonPublic) == 0)); + Debug.Assert(((allFlagsThatMustMatch & BindingFlags.Instance) == 0) != ((allFlagsThatMustMatch & BindingFlags.Static) == 0)); + Debug.Assert((allFlagsThatMustMatch & BindingFlags.FlattenHierarchy) == 0 || (allFlagsThatMustMatch & BindingFlags.Static) != 0); + + int count = _totalCount; + if (count == _members.Length) + { + Array.Resize(ref _members, count + Grow); + Array.Resize(ref _allFlagsThatMustMatch, count + Grow); + } + + _members[count] = member; + _allFlagsThatMustMatch[count] = allFlagsThatMustMatch; + _totalCount++; + } + + private int _totalCount; // # of entries including members in base classes. + private int _declaredOnlyCount; // # of entries for members only in the most derived class. + private M[] _members; // Length is equal to or greater than _totalCount. Entries beyond _totalCount contain null or garbage and should be read. + private BindingFlags[] _allFlagsThatMustMatch; // Length will be equal to _members.Length + private RuntimeTypeInfo _typeThatBlockedBrowsing; // If non-null, one of the base classes was missing metadata. + + private const int Grow = 64; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.Enumerator.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.Enumerator.cs new file mode 100644 index 00000000000000..d3826a2010bc77 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.Enumerator.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + internal partial struct QueryResult where M : MemberInfo + { + internal struct QueryResultEnumerator + { + public QueryResultEnumerator(QueryResult queryResult) + { + _bindingAttr = queryResult._bindingAttr; + _unfilteredCount = queryResult.UnfilteredCount; + _queriedMembers = queryResult._queriedMembers; + _index = -1; + } + + public bool MoveNext() + { + while (++_index < _unfilteredCount && !_queriedMembers.Matches(_index, _bindingAttr)) + { + } + + if (_index < _unfilteredCount) + return true; + + _index = _unfilteredCount; // guard against wiseguys calling MoveNext() over and over after the end. + return false; + } + + public M Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return _queriedMembers[_index]; + } + } + + private int _index; + private readonly int _unfilteredCount; + private readonly BindingFlags _bindingAttr; + private readonly QueriedMemberList _queriedMembers; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs new file mode 100644 index 00000000000000..1674efe1ea57ad --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs @@ -0,0 +1,136 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Collections.Generic; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + // + // Stores the result of a member filtering that's further filtered by the Public, NonPublic, Instance, Static and FlatternHierarchy bits of BindingFlags. + // This object is not considered a candidate for long term caching. + // + // Note: The uninitialized state ("qr = default(QueryResult)) is considered a valid state for this object, and represents an empty list of members. + // + internal partial struct QueryResult where M : MemberInfo + { + public QueryResult(BindingFlags bindingAttr, QueriedMemberList queriedMembers) + { + _lazyCount = 0; + _bindingAttr = bindingAttr; + _queriedMembers = queriedMembers; + } + + public QueryResultEnumerator GetEnumerator() => new QueryResultEnumerator(this); + + /// + /// Returns the number of matching results. + /// + public int Count + { + get + { + int count = _lazyCount; + if (count == 0) + { + if (_queriedMembers == null) + return 0; // This is an uninitialized QueryResult, which is supported and represents a 0-length list of matches. + + int unfilteredCount = UnfilteredCount; + for (int i = 0; i < unfilteredCount; i++) + { + if (_queriedMembers.Matches(i, _bindingAttr)) + count++; + } + + if (count == 0) + { + // If no matches were found, set ourselves back to the "uninitialized" state so that future + // calls to Count won't go through this calculation again. + _queriedMembers = null; + } + + _lazyCount = count; + + } + return count; + } + } + + /// + /// Copies the results to a freshly allocated array. Use this at api boundary points. + /// + public M[] ToArray() + { + int count = Count; + if (count == 0) + return Array.Empty(); + + M[] newArray = new M[count]; + CopyTo(newArray, 0); + return newArray; + } + + /// + /// Copies the results into an existing array. + /// + public void CopyTo(MemberInfo[] array, int startIndex) + { + if (_queriedMembers == null) + return; // This is an uninitialized QueryResult, which is supported and represents a 0-length list of matches. + + int unfilteredCount = UnfilteredCount; + for (int i = 0; i < unfilteredCount; i++) + { + if (_queriedMembers.Matches(i, _bindingAttr)) + { + array[startIndex++] = _queriedMembers[i]; + } + } + } + + /// + /// Returns a single member, null or throws AmbigousMatchException, for the Type.Get*(string name,...) family of apis. + /// + public M Disambiguate() + { + if (_queriedMembers == null) + return null; // This is an uninitialized QueryResult, which is supported and represents a 0-length list of matches. + + int unfilteredCount = UnfilteredCount; + + M match = null; + for (int i = 0; i < unfilteredCount; i++) + { + if (_queriedMembers.Matches(i, _bindingAttr)) + { + if (match != null) + { + M challenger = _queriedMembers[i]; + + // Assuming the policy says it's ok to ignore the ambiguity, we're to resolve in favor of the member + // declared by the most derived type. Since QueriedMemberLists are sorted in order of decreasing derivation, + // that means we let the first match win - unless, of course, they're both the "most derived member". + if (match.DeclaringType.Equals(challenger.DeclaringType)) + throw new AmbiguousMatchException(); + + MemberPolicies policies = MemberPolicies.Default; + if (!policies.OkToIgnoreAmbiguity(match, challenger)) + throw new AmbiguousMatchException(); + } + else + { + match = _queriedMembers[i]; + } + } + } + return match; + } + + private int UnfilteredCount => ((_bindingAttr & BindingFlags.DeclaredOnly) != 0) ? _queriedMembers.DeclaredOnlyCount : _queriedMembers.TotalCount; + + private readonly BindingFlags _bindingAttr; + private int _lazyCount; // Intentionally not marking as volatile. QueryResult is for short-term use within a single method call - no aspiration to be thread-safe. + private QueriedMemberList _queriedMembers; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/Shared.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/Shared.cs new file mode 100644 index 00000000000000..1f9d754916f749 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/Shared.cs @@ -0,0 +1,195 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Reflection.Runtime.General; + +namespace System.Reflection.Runtime.BindingFlagSupport +{ + internal static class Shared + { + // + // This is similar to FilterApplyMethodBase from CoreClr with some important differences: + // + // - Does *not* filter on Public|NonPublic|Instance|Static|FlatternHierarchy. Caller is expected to have done that. + // + // - ArgumentTypes cannot be null. + // + // Used by Type.GetMethodImpl(), Type.GetConstructorImpl(), Type.InvokeMember() and Activator.CreateInstance(). Does some + // preliminary weeding out of candidate methods based on the supplied calling convention and parameter list lengths. + // + // Candidates must pass this screen before we involve the binder. + // + public static bool QualifiesBasedOnParameterCount(this MethodBase methodBase, BindingFlags bindingFlags, CallingConventions callConv, Type[] argumentTypes) + { + Debug.Assert(methodBase is not null); + Debug.Assert(argumentTypes is not null); +#if DEBUG + bindingFlags &= ~(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.FlattenHierarchy | BindingFlags.IgnoreCase); +#endif + + #region Check CallingConvention + if ((callConv & CallingConventions.Any) == 0) + { + if ((callConv & CallingConventions.VarArgs) != 0 && (methodBase.CallingConvention & CallingConventions.VarArgs) == 0) + return false; + + if ((callConv & CallingConventions.Standard) != 0 && (methodBase.CallingConvention & CallingConventions.Standard) == 0) + return false; + } + #endregion + + #region ArgumentTypes + ParameterInfo[] parameterInfos = methodBase.GetParametersNoCopy(); + + if (argumentTypes.Length != parameterInfos.Length) + { + #region Invoke Member, Get\Set & Create Instance specific case + // If the number of supplied arguments differs than the number in the signature AND + // we are not filtering for a dynamic call -- InvokeMethod or CreateInstance -- filter out the method. + if ((bindingFlags & (BindingFlags.InvokeMethod | BindingFlags.CreateInstance | BindingFlags.GetProperty | BindingFlags.SetProperty)) == 0) + return false; + + bool testForParamArray = false; + bool excessSuppliedArguments = argumentTypes.Length > parameterInfos.Length; + + if (excessSuppliedArguments) + { // more supplied arguments than parameters, additional arguments could be vararg + #region Varargs + // If method is not vararg, additional arguments can not be passed as vararg + if ((methodBase.CallingConvention & CallingConventions.VarArgs) == 0) + { + testForParamArray = true; + } + else + { + // If Binding flags did not include varargs we would have filtered this vararg method. + // This Invariant established during callConv check. + Debug.Assert((callConv & CallingConventions.VarArgs) != 0); + } + #endregion + } + else + {// fewer supplied arguments than parameters, missing arguments could be optional + #region OptionalParamBinding + if ((bindingFlags & BindingFlags.OptionalParamBinding) == 0) + { + testForParamArray = true; + } + else + { + // From our existing code, our policy here is that if a parameterInfo + // is optional then all subsequent parameterInfos shall be optional. + + // Thus, iff the first parameterInfo is not optional then this MethodInfo is no longer a canidate. + if (!parameterInfos[argumentTypes.Length].IsOptional) + testForParamArray = true; + } + #endregion + } + + #region ParamArray + if (testForParamArray) + { + if (parameterInfos.Length == 0) + return false; + + // The last argument of the signature could be a param array. + bool shortByMoreThanOneSuppliedArgument = argumentTypes.Length < parameterInfos.Length - 1; + + if (shortByMoreThanOneSuppliedArgument) + return false; + + ParameterInfo lastParameter = parameterInfos[parameterInfos.Length - 1]; + + if (!lastParameter.ParameterType.IsArray) + return false; + + if (!lastParameter.IsDefined(typeof(ParamArrayAttribute), false)) + return false; + } + #endregion + + #endregion + } + else + { + #region Exact Binding + if ((bindingFlags & BindingFlags.ExactBinding) != 0) + { + // Legacy behavior is to ignore ExactBinding when InvokeMember is specified. + // Why filter by InvokeMember? If the answer is we leave this to the binder then why not leave + // all the rest of this to the binder too? Further, what other semanitc would the binder + // use for BindingFlags.ExactBinding besides this one? Further, why not include CreateInstance + // in this if statement? That's just InvokeMethod with a constructor, right? + if ((bindingFlags & (BindingFlags.InvokeMethod)) == 0) + { + for (int i = 0; i < parameterInfos.Length; i++) + { + // a null argument type implies a null arg which is always a perfect match + if (argumentTypes[i] is not null && !argumentTypes[i].MatchesParameterTypeExactly(parameterInfos[i])) + return false; + } + } + } + #endregion + } + #endregion + + return true; + } + + // + // If member is a virtual member that implicitly overrides a member in a base class, return the overridden member. + // Otherwise, return null. + // + // - MethodImpls ignored. (I didn't say it made sense, this is just how the desktop api we're porting behaves.) + // - Implemented interfaces ignores. (I didn't say it made sense, this is just how the desktop api we're porting behaves.) + // + public static M GetImplicitlyOverriddenBaseClassMember(this M member) where M : MemberInfo + { + MemberPolicies policies = MemberPolicies.Default; + MethodAttributes visibility; + bool isStatic; + bool isVirtual; + bool isNewSlot; + policies.GetMemberAttributes(member, out visibility, out isStatic, out isVirtual, out isNewSlot); + if (isNewSlot || !isVirtual) + { + return null; + } + string name = member.Name; + TypeInfo typeInfo = member.DeclaringType.GetTypeInfo(); + for (;;) + { + Type baseType = typeInfo.BaseType; + if (baseType == null) + { + return null; + } + typeInfo = baseType.GetTypeInfo(); + foreach (M candidate in policies.GetDeclaredMembers(typeInfo)) + { + if (candidate.Name != name) + { + continue; + } + MethodAttributes candidateVisibility; + bool isCandidateStatic; + bool isCandidateVirtual; + bool isCandidateNewSlot; + policies.GetMemberAttributes(member, out candidateVisibility, out isCandidateStatic, out isCandidateVirtual, out isCandidateNewSlot); + if (!isCandidateVirtual) + { + continue; + } + if (!policies.ImplicitlyOverrides(candidate, member)) + { + continue; + } + return candidate; + } + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/NativeFormat/NativeFormatCustomAttributeData.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/NativeFormat/NativeFormatCustomAttributeData.cs new file mode 100644 index 00000000000000..20a2d5ff1e3d45 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/NativeFormat/NativeFormatCustomAttributeData.cs @@ -0,0 +1,222 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeInfos.NativeFormat; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.MethodInfos.NativeFormat; + +using Internal.LowLevelLinq; +using Internal.Reflection.Core; +using Internal.Reflection.Augments; +using Internal.Reflection.Core.Execution; +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.CustomAttributes.NativeFormat +{ + // + // The Runtime's implementation of CustomAttributeData for normal metadata-based attributes + // + internal sealed class NativeFormatCustomAttributeData : RuntimeCustomAttributeData + { + internal NativeFormatCustomAttributeData(MetadataReader reader, CustomAttributeHandle customAttributeHandle) + { + _reader = reader; + _customAttribute = customAttributeHandle.GetCustomAttribute(reader); + } + + public sealed override Type AttributeType + { + get + { + Type lazyAttributeType = _lazyAttributeType; + if (lazyAttributeType == null) + { + lazyAttributeType = _lazyAttributeType = _customAttribute.GetAttributeTypeHandle(_reader).Resolve(_reader, new TypeContext(null, null)); + } + return lazyAttributeType; + } + } + + public sealed override ConstructorInfo Constructor + { + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "Metadata generation ensures custom attribute constructors are resolvable.")] + get + { + MetadataReader reader = _reader; + HandleType constructorHandleType = _customAttribute.Constructor.HandleType; + + if (constructorHandleType == HandleType.QualifiedMethod) + { + QualifiedMethod qualifiedMethod = _customAttribute.Constructor.ToQualifiedMethodHandle(reader).GetQualifiedMethod(reader); + TypeDefinitionHandle declaringType = qualifiedMethod.EnclosingType; + MethodHandle methodHandle = qualifiedMethod.Method; + NativeFormatRuntimeNamedTypeInfo attributeType = NativeFormatRuntimeNamedTypeInfo.GetRuntimeNamedTypeInfo(reader, declaringType, default(RuntimeTypeHandle)); + return RuntimePlainConstructorInfo.GetRuntimePlainConstructorInfo(new NativeFormatMethodCommon(methodHandle, attributeType, attributeType)); + } + else if (constructorHandleType == HandleType.MemberReference) + { + MemberReference memberReference = _customAttribute.Constructor.ToMemberReferenceHandle(reader).GetMemberReference(reader); + + // There is no chance a custom attribute type will be an open type specification so we can safely pass in the empty context here. + TypeContext typeContext = new TypeContext(Array.Empty(), Array.Empty()); + RuntimeTypeInfo attributeType = memberReference.Parent.Resolve(reader, typeContext); + MethodSignature sig = memberReference.Signature.ParseMethodSignature(reader); + HandleCollection parameters = sig.Parameters; + int numParameters = parameters.Count; + if (numParameters == 0) + return ResolveAttributeConstructor(attributeType, Array.Empty()); + + Type[] expectedParameterTypes = new Type[numParameters]; + int index = 0; + foreach (Handle _parameterHandle in parameters) + { + Handle parameterHandle = _parameterHandle; + expectedParameterTypes[index++] = parameterHandle.Resolve(reader, attributeType.TypeContext); + } + return ResolveAttributeConstructor(attributeType, expectedParameterTypes); + } + else + { + throw new BadImageFormatException(); + } + } + } + + internal sealed override string AttributeTypeString + { + get + { + return new QTypeDefRefOrSpec(_reader, _customAttribute.GetAttributeTypeHandle(_reader)).FormatTypeName(new TypeContext(null, null)); + } + } + + // + // If throwIfMissingMetadata is false, returns null rather than throwing a MissingMetadataException. + // + internal sealed override IList GetConstructorArguments(bool throwIfMissingMetadata) + { + int index = 0; + + HandleCollection parameterTypeSignatureHandles; + HandleType handleType = _customAttribute.Constructor.HandleType; + switch (handleType) + { + case HandleType.QualifiedMethod: + parameterTypeSignatureHandles = _customAttribute.Constructor.ToQualifiedMethodHandle(_reader).GetQualifiedMethod(_reader).Method.GetMethod(_reader).Signature.GetMethodSignature(_reader).Parameters; + break; + + case HandleType.MemberReference: + parameterTypeSignatureHandles = _customAttribute.Constructor.ToMemberReferenceHandle(_reader).GetMemberReference(_reader).Signature.ToMethodSignatureHandle(_reader).GetMethodSignature(_reader).Parameters; + break; + default: + throw new BadImageFormatException(); + } + Handle[] ctorTypeHandles = parameterTypeSignatureHandles.ToArray(); + + LowLevelListWithIList customAttributeTypedArguments = new LowLevelListWithIList(); + foreach (Handle fixedArgumentHandle in _customAttribute.FixedArguments) + { + Handle typeHandle = ctorTypeHandles[index]; + Exception exception = null; + RuntimeTypeInfo argumentType = typeHandle.TryResolve(_reader, new TypeContext(null, null), ref exception); + if (argumentType == null) + { + if (throwIfMissingMetadata) + throw exception; + return null; + } + + Exception e = fixedArgumentHandle.TryParseConstantValue(_reader, out object value); + CustomAttributeTypedArgument customAttributeTypedArgument; + if (e != null) + { + if (throwIfMissingMetadata) + throw e; + else + return null; + } + else + { + customAttributeTypedArgument = WrapInCustomAttributeTypedArgument(value, argumentType); + } + + customAttributeTypedArguments.Add(customAttributeTypedArgument); + index++; + } + + return customAttributeTypedArguments; + } + + // + // If throwIfMissingMetadata is false, returns null rather than throwing a MissingMetadataException. + // + internal sealed override IList GetNamedArguments(bool throwIfMissingMetadata) + { + LowLevelListWithIList customAttributeNamedArguments = new LowLevelListWithIList(); + foreach (NamedArgumentHandle namedArgumentHandle in _customAttribute.NamedArguments) + { + NamedArgument namedArgument = namedArgumentHandle.GetNamedArgument(_reader); + string memberName = namedArgument.Name.GetString(_reader); + bool isField = (namedArgument.Flags == NamedArgumentMemberKind.Field); + + Exception exception = null; + RuntimeTypeInfo argumentType = namedArgument.Type.TryResolve(_reader, new TypeContext(null, null), ref exception); + if (argumentType == null) + { + if (throwIfMissingMetadata) + throw exception; + else + return null; + } + + object value; + Exception e = namedArgument.Value.TryParseConstantValue(_reader, out value); + if (e != null) + { + if (throwIfMissingMetadata) + throw e; + else + return null; + } + CustomAttributeTypedArgument typedValue = WrapInCustomAttributeTypedArgument(value, argumentType); + + customAttributeNamedArguments.Add(CreateCustomAttributeNamedArgument(this.AttributeType, memberName, isField, typedValue)); + } + return customAttributeNamedArguments; + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Metadata generation ensures fields/properties referenced from attributes are preserved.")] + private static CustomAttributeNamedArgument CreateCustomAttributeNamedArgument(Type attributeType, string memberName, bool isField, CustomAttributeTypedArgument typedValue) + { + MemberInfo memberInfo; + + if (isField) + memberInfo = attributeType.GetField(memberName, BindingFlags.Public | BindingFlags.Instance); + else + memberInfo = attributeType.GetProperty(memberName, BindingFlags.Public | BindingFlags.Instance); + + if (memberInfo == null) + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(attributeType); + + return new CustomAttributeNamedArgument(memberInfo, typedValue); + } + + // Equals/GetHashCode no need to override (they just implement reference equality but desktop never unified these things.) + + private readonly MetadataReader _reader; + private readonly CustomAttribute _customAttribute; + + private volatile Type _lazyAttributeType; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimeCustomAttributeData.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimeCustomAttributeData.cs new file mode 100644 index 00000000000000..7db743d5e8143d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimeCustomAttributeData.cs @@ -0,0 +1,210 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; + +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.CustomAttributes +{ + // + // Common base class for the Runtime's implementation of CustomAttributeData. + // + internal abstract partial class RuntimeCustomAttributeData : CustomAttributeData + { + public abstract override Type AttributeType { get; } + + public abstract override ConstructorInfo Constructor { get; } + + public sealed override IList ConstructorArguments + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.CustomAttributeData_ConstructorArguments(this); +#endif + + return new ReadOnlyCollection(GetConstructorArguments(throwIfMissingMetadata: true)); + } + } + + // Equals/GetHashCode no need to override (they just implement reference equality but desktop never unified these things.) + + public sealed override IList NamedArguments + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.CustomAttributeData_NamedArguments(this); +#endif + + return new ReadOnlyCollection(GetNamedArguments(throwIfMissingMetadata: true)); + } + } + + public sealed override string ToString() + { + try + { + string ctorArgs = ""; + IList constructorArguments = GetConstructorArguments(throwIfMissingMetadata: false); + if (constructorArguments == null) + return LastResortToString; + for (int i = 0; i < constructorArguments.Count; i++) + ctorArgs += string.Format(i == 0 ? "{0}" : ", {0}", ComputeTypedArgumentString(constructorArguments[i], typed: false)); + + string namedArgs = ""; + IList namedArguments = GetNamedArguments(throwIfMissingMetadata: false); + if (namedArguments == null) + return LastResortToString; + for (int i = 0; i < namedArguments.Count; i++) + { + CustomAttributeNamedArgument namedArgument = namedArguments[i]; + + // Legacy: Desktop sets "typed" to "namedArgument.ArgumentType != typeof(Object)" - on Project N, this property is not available + // (nor conveniently computable as it's not captured in the Project N metadata.) The only consequence is that for + // the rare case of fields and properties typed "Object", we won't decorate the argument value with its actual type name. + bool typed = true; + namedArgs += string.Format( + i == 0 && ctorArgs.Length == 0 ? "{0} = {1}" : ", {0} = {1}", + namedArgument.MemberName, + ComputeTypedArgumentString(namedArgument.TypedValue, typed)); + } + + return string.Format("[{0}({1}{2})]", AttributeTypeString, ctorArgs, namedArgs); + } + catch (MissingMetadataException) + { + return LastResortToString; + } + } + + protected static ConstructorInfo ResolveAttributeConstructor( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type attributeType, Type[] parameterTypes) + { + int parameterCount = parameterTypes.Length; + foreach (ConstructorInfo candidate in attributeType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + ParameterInfo[] candidateParameters = candidate.GetParametersNoCopy(); + if (parameterCount != candidateParameters.Length) + continue; + + for (int i = 0; i < parameterCount; i++) + { + if (!parameterTypes[i].Equals(candidateParameters[i])) + continue; + } + + return candidate; + } + + throw new MissingMethodException(); + } + + internal abstract string AttributeTypeString { get; } + + // + // If throwIfMissingMetadata is false, returns null rather than throwing a MissingMetadataException. + // + internal abstract IList GetConstructorArguments(bool throwIfMissingMetadata); + + // + // If throwIfMissingMetadata is false, returns null rather than throwing a MissingMetadataException. + // + internal abstract IList GetNamedArguments(bool throwIfMissingMetadata); + + // + // Computes the ToString() value for a CustomAttributeTypedArgument struct. + // + private static string ComputeTypedArgumentString(CustomAttributeTypedArgument cat, bool typed) + { + Type argumentType = cat.ArgumentType; + if (argumentType == null) + return cat.ToString(); + + object value = cat.Value; + if (argumentType.IsEnum) + return string.Format(typed ? "{0}" : "({1}){0}", value, argumentType.FullName); + + if (value == null) + return string.Format(typed ? "null" : "({0})null", argumentType.Name); + + if (argumentType == typeof(string)) + return string.Format("\"{0}\"", value); + + if (argumentType == typeof(char)) + return string.Format("'{0}'", value); + + if (argumentType == typeof(Type)) + return string.Format("typeof({0})", ((Type)value).FullName); + + else if (argumentType.IsArray) + { + string result = null; + IList array = value as IList; + + Type elementType = argumentType.GetElementType(); + result = string.Format(@"new {0}[{1}] {{ ", elementType.IsEnum ? elementType.FullName : elementType.Name, array.Count); + + for (int i = 0; i < array.Count; i++) + result += string.Format(i == 0 ? "{0}" : ", {0}", ComputeTypedArgumentString(array[i], elementType != typeof(object))); + + return result += " }"; + } + + return string.Format(typed ? "{0}" : "({1}){0}", value, argumentType.Name); + } + + private string LastResortToString + { + get + { + // This emulates Object.ToString() for consistency with prior .Net Native implementations. + return GetType().ToString(); + } + } + + // + // Wrap a custom attribute argument (or an element of an array-typed custom attribute argument) in a CustomAttributeTypeArgument structure + // for insertion into a CustomAttributeData value. + // + protected CustomAttributeTypedArgument WrapInCustomAttributeTypedArgument(object value, Type argumentType) + { + if (argumentType == typeof(object)) + { + // If the declared attribute type is System.Object, we must report the type based on the runtime value. + if (value == null) + argumentType = typeof(string); // Why is null reported as System.String? Because that's what the desktop CLR does. + else if (value is Type) + argumentType = typeof(Type); // value.GetType() will not actually be System.Type - rather it will be some internal implementation type. We only want to report it as System.Type. + else + argumentType = value.GetType(); + } + + // Handle the array case + if (value is IEnumerable enumerableValue && !(value is string)) + { + if (!argumentType.IsArray) + throw new BadImageFormatException(); + Type reportedElementType = argumentType.GetElementType(); + LowLevelListWithIList elementTypedArguments = new LowLevelListWithIList(); + foreach (object elementValue in enumerableValue) + { + CustomAttributeTypedArgument elementTypedArgument = WrapInCustomAttributeTypedArgument(elementValue, reportedElementType); + elementTypedArguments.Add(elementTypedArgument); + } + return new CustomAttributeTypedArgument(argumentType, new ReadOnlyCollection(elementTypedArguments)); + } + else + { + return new CustomAttributeTypedArgument(argumentType, value); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimePseudoCustomAttributeData.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimePseudoCustomAttributeData.cs new file mode 100644 index 00000000000000..0e81484c5dcdcb --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimePseudoCustomAttributeData.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; + +namespace System.Reflection.Runtime.CustomAttributes +{ + // + // The Runtime's implementation of a pseudo-CustomAttributeData. + // + internal sealed class RuntimePseudoCustomAttributeData : RuntimeCustomAttributeData + { + public RuntimePseudoCustomAttributeData( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type attributeType, IList constructorArguments) + { + _attributeType = attributeType; + if (constructorArguments == null) + constructorArguments = Array.Empty(); + _constructorArguments = new ReadOnlyCollection(constructorArguments); + } + + public sealed override Type AttributeType + { + get + { + return _attributeType; + } + } + + public sealed override ConstructorInfo Constructor + { + get + { + int numArguments = _constructorArguments.Count; + if (numArguments == 0) + return ResolveAttributeConstructor(_attributeType, Array.Empty()); + + Type[] expectedParameterTypes = new Type[numArguments]; + for (int i = 0; i < numArguments; i++) + { + expectedParameterTypes[i] = _constructorArguments[i].ArgumentType; + } + return ResolveAttributeConstructor(_attributeType, expectedParameterTypes); + } + } + + internal sealed override string AttributeTypeString + { + get + { + return _attributeType.FormatTypeNameForReflection(); + } + } + + internal sealed override IList GetConstructorArguments(bool throwIfMissingMetadata) + { + return _constructorArguments; + } + + internal sealed override IList GetNamedArguments(bool throwIfMissingMetadata) + { + // Note: if we ever need to return non-empty named arguments, we need to ensure the reflection metadata for the + // corresponding fields/properties is kept (we might have to bump the dataflow annotation on _attributeType). + return Array.Empty(); + } + + // Equals/GetHashCode no need to override (they just implement reference equality but desktop never unified these things.) + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + private readonly Type _attributeType; + private readonly ReadOnlyCollection _constructorArguments; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DefaultDispenserPolicy.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DefaultDispenserPolicy.cs new file mode 100644 index 00000000000000..20cabcfd203d90 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DefaultDispenserPolicy.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +namespace System.Reflection.Runtime.Dispensers +{ + // + // For now, this is the dispenser policy used inside S.R.R. + // + internal sealed class DefaultDispenserPolicy : DispenserPolicy + { + public sealed override DispenserAlgorithm GetAlgorithm(DispenserScenario scenario) + { + switch (scenario) + { + // Assembly + NamespaceTypeName to Type + case DispenserScenario.AssemblyAndNamespaceTypeName_Type: + return DispenserAlgorithm.ReuseAsLongAsValueIsAlive; + + // Assembly refName to Assembly + case DispenserScenario.AssemblyRefName_Assembly: + return DispenserAlgorithm.ReuseAsLongAsValueIsAlive; + + // RuntimeAssembly to CaseInsensitiveTypeDictionary + case DispenserScenario.RuntimeAssembly_CaseInsensitiveTypeDictionary: + return DispenserAlgorithm.ReuseAlways; + + // Scope definition handle to RuntimeAssembly + case DispenserScenario.Scope_Assembly: + return DispenserAlgorithm.ReuseAsLongAsValueIsAlive; + + default: + return DispenserAlgorithm.CreateAlways; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/Dispenser.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/Dispenser.cs new file mode 100644 index 00000000000000..591e187700146a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/Dispenser.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +namespace System.Reflection.Runtime.Dispensers +{ + // + // Abstract base for reflection caches. + // + internal abstract class Dispenser + where K : IEquatable + where V : class + { + public abstract V GetOrAdd(K key); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserAlgorithm.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserAlgorithm.cs new file mode 100644 index 00000000000000..f16b11039a7b24 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserAlgorithm.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +namespace System.Reflection.Runtime.Dispensers +{ + // + // A parameterizable monikor for the various cache algorithms available. + // + internal sealed class DispenserAlgorithm + { + public static readonly DispenserAlgorithm CreateAlways = new DispenserAlgorithm(); // Always create a new object (i.e. no caching at all.) + public static readonly DispenserAlgorithm ReuseAlways = new DispenserAlgorithm(); // Every object is saved permanently (i.e. complete unification.) + public static readonly DispenserAlgorithm ReuseAsLongAsValueIsAlive = new DispenserAlgorithm(); // Every object is saved using weak references. + + // + // Associates the value with key using a hash table but does not prevent key from gc'd. + // Restriction: The algorithm uses ConditionalWeakTable so it is subject to the following limitations: + // The key cannot be a value type. + // Keys are compared using Object.ReferenceEquals. + // + public static readonly DispenserAlgorithm ReuseAsLongAsKeyIsAlive = new DispenserAlgorithm(); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserFactory.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserFactory.cs new file mode 100644 index 00000000000000..8c05ae4d7983a6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserFactory.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Reflection.Runtime.TypeInfos; + +namespace System.Reflection.Runtime.Dispensers +{ + // + // Creates the appropriate Dispenser for a scenario, based on the dispenser policy. + // + internal static class DispenserFactory + { + // + // Note: If your K is a valuetype, use CreateDispenserV() instead. Some algorithms will not be available for use. + // + public static Dispenser CreateDispenser(DispenserScenario scenario, Func factory) + where K : class, IEquatable + where V : class + { + DispenserAlgorithm algorithm = s_dispenserPolicy.GetAlgorithm(scenario); + if (algorithm == DispenserAlgorithm.ReuseAsLongAsKeyIsAlive) + return new DispenserThatReusesAsLongAsKeyIsAlive(factory); + else + return CreateDispenserV(scenario, factory); + + throw new Exception(); + } + + + // + // This is similar to CreateDispenser() except it doesn't constrain the key to be a reference type. + // As a result, some algorithms will not be available for use. + // + public static Dispenser CreateDispenserV(DispenserScenario scenario, Func factory) + where K : IEquatable + where V : class + { + DispenserAlgorithm algorithm = s_dispenserPolicy.GetAlgorithm(scenario); + + Debug.Assert(algorithm != DispenserAlgorithm.ReuseAsLongAsKeyIsAlive, + "Use CreateDispenser() if you want to use this algorithm. The key must not be a valuetype."); + + if (algorithm == DispenserAlgorithm.CreateAlways) + return new DispenserThatAlwaysCreates(factory); + else if (algorithm == DispenserAlgorithm.ReuseAlways) + return new DispenserThatAlwaysReuses(factory); + else if (algorithm == DispenserAlgorithm.ReuseAsLongAsValueIsAlive) + return new DispenserThatReusesAsLongAsValueIsAlive(factory); + + throw new Exception(); + } + + + private static readonly DispenserPolicy s_dispenserPolicy = new DefaultDispenserPolicy(); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserPolicy.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserPolicy.cs new file mode 100644 index 00000000000000..916f9dbc7174b9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserPolicy.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +namespace System.Reflection.Runtime.Dispensers +{ + // + // Base class for a policy that maps dispense scenarios to the caching algorithm used. + // + internal abstract class DispenserPolicy + { + public abstract DispenserAlgorithm GetAlgorithm(DispenserScenario scenario); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserScenario.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserScenario.cs new file mode 100644 index 00000000000000..e35baa3e134043 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserScenario.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +namespace System.Reflection.Runtime.Dispensers +{ + // + // A monikor for each reflection cache. The name should follow the style "key" followed by underscore followed by "value". + // + internal enum DispenserScenario + { + // Assembly + NamespaceTypeName to Type + AssemblyAndNamespaceTypeName_Type, + + // Assembly refName to Assembly + AssemblyRefName_Assembly, + + // RuntimeAssembly to CaseInsensitiveTypeDictionary + RuntimeAssembly_CaseInsensitiveTypeDictionary, + + // Scope definition handle to RuntimeAssembly + Scope_Assembly, + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatAlwaysCreates.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatAlwaysCreates.cs new file mode 100644 index 00000000000000..cdafba33cf1fb3 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatAlwaysCreates.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +namespace System.Reflection.Runtime.Dispensers +{ + // + // This dispenser always creates things anew. + // + internal sealed class DispenserThatAlwaysCreates : Dispenser + where K : IEquatable + where V : class + { + public DispenserThatAlwaysCreates(Func factory) + { + _factory = factory; + } + + public sealed override V GetOrAdd(K key) + { + return _factory(key); + } + + private readonly Func _factory; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatAlwaysReuses.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatAlwaysReuses.cs new file mode 100644 index 00000000000000..e140e99a186f07 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatAlwaysReuses.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Concurrent; + +namespace System.Reflection.Runtime.Dispensers +{ + // + // This dispenser stores every instance permanently. + // + internal sealed class DispenserThatAlwaysReuses : Dispenser + where K : IEquatable + where V : class + { + public DispenserThatAlwaysReuses(Func factory) + { + _concurrentUnifier = new FactoryConcurrentUnifier(factory); + } + + public sealed override V GetOrAdd(K key) + { + return _concurrentUnifier.GetOrAdd(key); + } + + private sealed class FactoryConcurrentUnifier : ConcurrentUnifier + { + public FactoryConcurrentUnifier(Func factory) + { + _factory = factory; + } + + protected sealed override V Factory(K key) + { + return _factory(key); + } + + private readonly Func _factory; + } + + private readonly FactoryConcurrentUnifier _concurrentUnifier; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsKeyIsAlive.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsKeyIsAlive.cs new file mode 100644 index 00000000000000..bb946878333fcb --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsKeyIsAlive.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Reflection.Runtime.Dispensers +{ + internal sealed class DispenserThatReusesAsLongAsKeyIsAlive : Dispenser + where K : class, IEquatable + where V : class + { + public DispenserThatReusesAsLongAsKeyIsAlive(Func factory) + { + _createValueCallback = CreateValue; + _conditionalWeakTable = new ConditionalWeakTable(); + _factory = factory; + } + + public sealed override V GetOrAdd(K key) + { + return _conditionalWeakTable.GetValue(key, _createValueCallback); + } + + private V CreateValue(K key) + { + return _factory(key); + } + + private readonly Func _factory; + private readonly ConditionalWeakTable _conditionalWeakTable; + private readonly ConditionalWeakTable.CreateValueCallback _createValueCallback; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsKeyedValueIsAlive.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsKeyedValueIsAlive.cs new file mode 100644 index 00000000000000..9c918697719c05 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsKeyedValueIsAlive.cs @@ -0,0 +1,3 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsValueIsAlive.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsValueIsAlive.cs new file mode 100644 index 00000000000000..5c28e17c5cad2e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsValueIsAlive.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Concurrent; + +namespace System.Reflection.Runtime.Dispensers +{ + // + // This dispenser stores every instance using weak references. + // + internal sealed class DispenserThatReusesAsLongAsValueIsAlive : Dispenser + where K : IEquatable + where V : class + { + public DispenserThatReusesAsLongAsValueIsAlive(Func factory) + { + _concurrentUnifier = new FactoryConcurrentUnifierW(factory); + } + + public sealed override V GetOrAdd(K key) + { + return _concurrentUnifier.GetOrAdd(key); + } + + private sealed class FactoryConcurrentUnifierW : ConcurrentUnifierW + { + public FactoryConcurrentUnifierW(Func factory) + { + _factory = factory; + } + + protected sealed override V Factory(K key) + { + return _factory(key); + } + + private readonly Func _factory; + } + + private readonly FactoryConcurrentUnifierW _concurrentUnifier; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs new file mode 100644 index 00000000000000..60f842aad1a3cb --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs @@ -0,0 +1,184 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeInfos.NativeFormat; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.MethodInfos.NativeFormat; +using System.Reflection.Runtime.ParameterInfos; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Metadata.NativeFormat; +using NativeFormatMethodSemanticsAttributes = global::Internal.Metadata.NativeFormat.MethodSemanticsAttributes; + +using Internal.Reflection.Core.Execution; +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.EventInfos.NativeFormat +{ + [DebuggerDisplay("{_debugName}")] + internal sealed partial class NativeFormatRuntimeEventInfo : RuntimeEventInfo + { + // + // eventHandle - the "tkEventDef" that identifies the event. + // definingType - the "tkTypeDef" that defined the field (this is where you get the metadata reader that created eventHandle.) + // contextType - the type that supplies the type context (i.e. substitutions for generic parameters.) Though you + // get your raw information from "definingType", you report "contextType" as your DeclaringType property. + // + // For example: + // + // typeof(Foo<>).GetTypeInfo().DeclaredMembers + // + // The definingType and contextType are both Foo<> + // + // typeof(Foo).GetTypeInfo().DeclaredMembers + // + // The definingType is "Foo<,>" + // The contextType is "Foo" + // + // We don't report any DeclaredMembers for arrays or generic parameters so those don't apply. + // + private NativeFormatRuntimeEventInfo(EventHandle eventHandle, NativeFormatRuntimeNamedTypeInfo definingTypeInfo, RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType) : + base(contextTypeInfo, reflectedType) + { + _eventHandle = eventHandle; + _definingTypeInfo = definingTypeInfo; + _reader = definingTypeInfo.Reader; + _event = eventHandle.GetEvent(_reader); + } + + protected sealed override MethodInfo GetEventMethod(EventMethodSemantics whichMethod) + { + NativeFormatMethodSemanticsAttributes localMethodSemantics; + switch (whichMethod) + { + case EventMethodSemantics.Add: + localMethodSemantics = NativeFormatMethodSemanticsAttributes.AddOn; + break; + + case EventMethodSemantics.Fire: + localMethodSemantics = NativeFormatMethodSemanticsAttributes.Fire; + break; + + case EventMethodSemantics.Remove: + localMethodSemantics = NativeFormatMethodSemanticsAttributes.RemoveOn; + break; + + default: + return null; + } + + foreach (MethodSemanticsHandle methodSemanticsHandle in _event.MethodSemantics) + { + MethodSemantics methodSemantics = methodSemanticsHandle.GetMethodSemantics(_reader); + if (methodSemantics.Attributes == localMethodSemantics) + { + return RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(new NativeFormatMethodCommon(methodSemantics.Method, _definingTypeInfo, ContextTypeInfo), ReflectedTypeInfo); + } + } + + return null; + } + + public sealed override EventAttributes Attributes + { + get + { + return _event.Flags; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.EventInfo_CustomAttributes(this); +#endif + + return RuntimeCustomAttributeData.GetCustomAttributes(_reader, _event.CustomAttributes); + } + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + if (!(other is NativeFormatRuntimeEventInfo otherEvent)) + return false; + if (!(_reader == otherEvent._reader)) + return false; + if (!(_eventHandle.Equals(otherEvent._eventHandle))) + return false; + if (!(_definingTypeInfo.Equals(otherEvent._definingTypeInfo))) + return false; + return true; + } + + public sealed override bool Equals(object obj) + { + if (!(obj is NativeFormatRuntimeEventInfo other)) + return false; + if (!(_reader == other._reader)) + return false; + if (!(_eventHandle.Equals(other._eventHandle))) + return false; + if (!(ContextTypeInfo.Equals(other.ContextTypeInfo))) + return false; + if (!(ReflectedType.Equals(other.ReflectedType))) + return false; + return true; + } + + public sealed override int GetHashCode() + { + return _eventHandle.GetHashCode(); + } + + public sealed override Type EventHandlerType + { + get + { + return _event.Type.Resolve(_reader, ContextTypeInfo.TypeContext); + } + } + + public sealed override int MetadataToken + { + get + { + throw new InvalidOperationException(SR.NoMetadataTokenAvailable); + } + } + + protected sealed override string MetadataName + { + get + { + return _event.Name.GetString(_reader); + } + } + + protected sealed override RuntimeTypeInfo DefiningTypeInfo + { + get + { + return _definingTypeInfo; + } + } + + private readonly NativeFormatRuntimeNamedTypeInfo _definingTypeInfo; + private readonly EventHandle _eventHandle; + + private readonly MetadataReader _reader; + private readonly Event _event; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs new file mode 100644 index 00000000000000..c99e6307de929e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs @@ -0,0 +1,221 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Reflection.Core.Execution; +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.EventInfos +{ + // + // The runtime's implementation of EventInfo's + // + [DebuggerDisplay("{_debugName}")] + internal abstract partial class RuntimeEventInfo : EventInfo, ITraceableTypeMember + { + protected RuntimeEventInfo(RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType) + { + ContextTypeInfo = contextTypeInfo; + ReflectedTypeInfo = reflectedType; + } + + public sealed override MethodInfo AddMethod + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.EventInfo_AddMethod(this); +#endif + + MethodInfo adder = _lazyAdder; + if (adder == null) + { + adder = GetEventMethod(EventMethodSemantics.Add); + if (adder != null) + return _lazyAdder = adder; + + throw new BadImageFormatException(); // Added is a required method. + } + return adder; + } + } + + public sealed override Type DeclaringType + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.EventInfo_DeclaringType(this); +#endif + + return ContextTypeInfo; + } + } + + public sealed override MethodInfo[] GetOtherMethods(bool nonPublic) + { + throw new PlatformNotSupportedException(); + } + + public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); + + public sealed override Module Module + { + get + { + return DefiningTypeInfo.Module; + } + } + + public sealed override string Name + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.EventInfo_Name(this); +#endif + + return MetadataName; + } + } + + public sealed override Type ReflectedType + { + get + { + return ReflectedTypeInfo; + } + } + + public sealed override MethodInfo RaiseMethod + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.EventInfo_RaiseMethod(this); +#endif + + return GetEventMethod(EventMethodSemantics.Fire); + } + } + + public sealed override MethodInfo RemoveMethod + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.EventInfo_RemoveMethod(this); +#endif + + MethodInfo remover = _lazyRemover; + if (remover == null) + { + remover = GetEventMethod(EventMethodSemantics.Remove); + if (remover != null) + return _lazyRemover = remover; + + throw new BadImageFormatException(); // Removed is a required method. + } + return remover; + } + } + + public sealed override string ToString() + { + MethodInfo addMethod = this.AddMethod; + ParameterInfo[] parameters = addMethod.GetParametersNoCopy(); + if (parameters.Length == 0) + throw new InvalidOperationException(); // Legacy: Why is a ToString() intentionally throwing an exception? + RuntimeParameterInfo runtimeParameterInfo = (RuntimeParameterInfo)(parameters[0]); + return runtimeParameterInfo.ParameterTypeString + " " + this.Name; + } + + string ITraceableTypeMember.MemberName + { + get + { + return MetadataName; + } + } + + Type ITraceableTypeMember.ContainingType + { + get + { + return ContextTypeInfo; + } + } + + protected RuntimeEventInfo WithDebugName() + { + bool populateDebugNames = DeveloperExperienceState.DeveloperExperienceModeEnabled; +#if DEBUG + populateDebugNames = true; +#endif + if (!populateDebugNames) + return this; + + if (_debugName == null) + { + _debugName = "Constructing..."; // Protect against any inadvertent reentrancy. + _debugName = MetadataName; + } + return this; + } + + // Types that derive from RuntimeEventInfo must implement the following public surface area members + public abstract override EventAttributes Attributes { get; } + public abstract override IEnumerable CustomAttributes { get; } + public abstract override bool Equals(object obj); + public abstract override int GetHashCode(); + public abstract override Type EventHandlerType { get; } + public abstract override int MetadataToken { get; } + + protected enum EventMethodSemantics + { + Add, + Remove, + Fire + } + + /// + /// Override to return the Method that corresponds to the specified semantic. + /// Return null if no method is to be found. + /// + protected abstract MethodInfo GetEventMethod(EventMethodSemantics whichMethod); + + /// + /// Override to provide the metadata based name of an event. (Different from the Name + /// property in that it does not go into the reflection trace logic.) + /// + protected abstract string MetadataName { get; } + + /// + /// Return the DefiningTypeInfo as a RuntimeTypeInfo (instead of as a format specific type info) + /// + protected abstract RuntimeTypeInfo DefiningTypeInfo { get; } + + + protected readonly RuntimeTypeInfo ContextTypeInfo; + protected readonly RuntimeTypeInfo ReflectedTypeInfo; + + private volatile MethodInfo _lazyAdder; + private volatile MethodInfo _lazyRemover; + + private string _debugName; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs new file mode 100644 index 00000000000000..bb971d1aa65d68 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs @@ -0,0 +1,177 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Globalization; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +using System.Reflection.Runtime.FieldInfos; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.General.NativeFormat; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeInfos.NativeFormat; +using System.Reflection.Runtime.CustomAttributes; +using System.Reflection.Runtime.BindingFlagSupport; + +using Internal.Metadata.NativeFormat; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; +using Internal.Reflection.Tracing; + +using Internal.Runtime.TypeLoader; + +namespace System.Reflection.Runtime.FieldInfos.NativeFormat +{ + // + // The Runtime's implementation of fields. + // + [DebuggerDisplay("{_debugName}")] + internal sealed partial class NativeFormatRuntimeFieldInfo : RuntimeFieldInfo + { + // + // fieldHandle - the "tkFieldDef" that identifies the field. + // definingType - the "tkTypeDef" that defined the field (this is where you get the metadata reader that created fieldHandle.) + // contextType - the type that supplies the type context (i.e. substitutions for generic parameters.) Though you + // get your raw information from "definingType", you report "contextType" as your DeclaringType property. + // + // For example: + // + // typeof(Foo<>).GetTypeInfo().DeclaredMembers + // + // The definingType and contextType are both Foo<> + // + // typeof(Foo).GetTypeInfo().DeclaredMembers + // + // The definingType is "Foo<,>" + // The contextType is "Foo" + // + // We don't report any DeclaredMembers for arrays or generic parameters so those don't apply. + // + private NativeFormatRuntimeFieldInfo(FieldHandle fieldHandle, NativeFormatRuntimeNamedTypeInfo definingTypeInfo, RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType) : + base(contextTypeInfo, reflectedType) + { + _fieldHandle = fieldHandle; + _definingTypeInfo = definingTypeInfo; + _reader = definingTypeInfo.Reader; + _field = fieldHandle.GetField(_reader); + } + + public sealed override FieldAttributes Attributes + { + get + { + return _field.Flags; + } + } + + public sealed override Type[] GetOptionalCustomModifiers() => FieldTypeHandle.GetCustomModifiers(_reader, _contextTypeInfo.TypeContext, optional: true); + + public sealed override Type[] GetRequiredCustomModifiers() => FieldTypeHandle.GetCustomModifiers(_reader, _contextTypeInfo.TypeContext, optional: false); + + public sealed override int MetadataToken + { + get + { + throw new InvalidOperationException(SR.NoMetadataTokenAvailable); + } + } + + protected sealed override string MetadataName + { + get + { + return _field.Name.GetString(_reader); + } + } + + public sealed override string ToString() + { + TypeContext typeContext = _contextTypeInfo.TypeContext; + Handle typeHandle = _field.Signature.GetFieldSignature(_reader).Type; + return (new QTypeDefRefOrSpec(_reader, typeHandle).FormatTypeName(typeContext)) + " " + this.Name; + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + if (!(other is NativeFormatRuntimeFieldInfo otherField)) + return false; + if (!(_reader == otherField._reader)) + return false; + if (!(_fieldHandle.Equals(otherField._fieldHandle))) + return false; + if (!(_definingTypeInfo.Equals(otherField._definingTypeInfo))) + return false; + return true; + } + + public sealed override bool Equals(object obj) + { + if (!(obj is NativeFormatRuntimeFieldInfo other)) + return false; + if (!(_reader == other._reader)) + return false; + if (!(_fieldHandle.Equals(other._fieldHandle))) + return false; + if (!(_contextTypeInfo.Equals(other._contextTypeInfo))) + return false; + if (!(_reflectedType.Equals(other._reflectedType))) + return false; + return true; + } + + public sealed override int GetHashCode() + { + return _fieldHandle.GetHashCode(); + } + + public sealed override RuntimeFieldHandle FieldHandle + { + get + { + return TypeLoaderEnvironment.Instance.GetRuntimeFieldHandleForComponents( + DeclaringType.TypeHandle, + Name); + } + } + + protected sealed override bool GetDefaultValueIfAvailable(bool raw, out object defaultValue) + { + return DefaultValueParser.GetDefaultValueIfAny(_reader, _field.DefaultValue, FieldType, CustomAttributes, raw, out defaultValue); + } + + protected sealed override FieldAccessor TryGetFieldAccessor() + { + return ReflectionCoreExecution.ExecutionEnvironment.TryGetFieldAccessor(this._reader, this.DeclaringType.TypeHandle, this.FieldType.TypeHandle, _fieldHandle); + } + + protected sealed override RuntimeTypeInfo FieldRuntimeType + { + get + { + TypeContext typeContext = _contextTypeInfo.TypeContext; + return FieldTypeHandle.Resolve(_reader, typeContext); + } + } + + protected sealed override RuntimeTypeInfo DefiningType { get { return _definingTypeInfo; } } + + protected sealed override IEnumerable TrueCustomAttributes => RuntimeCustomAttributeData.GetCustomAttributes(_reader, _field.CustomAttributes); + + protected sealed override int ExplicitLayoutFieldOffsetData => (int)(_field.Offset); + + private Handle FieldTypeHandle => _field.Signature.GetFieldSignature(_reader).Type; + + private readonly NativeFormatRuntimeNamedTypeInfo _definingTypeInfo; + private readonly FieldHandle _fieldHandle; + + private readonly MetadataReader _reader; + private readonly Field _field; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs new file mode 100644 index 00000000000000..b676005195594c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs @@ -0,0 +1,310 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Globalization; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.CustomAttributes; +using System.Reflection.Runtime.BindingFlagSupport; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.FieldInfos +{ + // + // The Runtime's implementation of fields. + // + [DebuggerDisplay("{_debugName}")] + internal abstract partial class RuntimeFieldInfo : FieldInfo, ITraceableTypeMember + { + // + // contextType - the type that supplies the type context (i.e. substitutions for generic parameters.) Though you + // get your raw information from "definingType", you report "contextType" as your DeclaringType property. + // + // For example: + // + // typeof(Foo<>).GetTypeInfo().DeclaredMembers + // + // The definingType and contextType are both Foo<> + // + // typeof(Foo).GetTypeInfo().DeclaredMembers + // + // The definingType is "Foo<,>" + // The contextType is "Foo" + // + // We don't report any DeclaredMembers for arrays or generic parameters so those don't apply. + // + protected RuntimeFieldInfo(RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType) + { + _contextTypeInfo = contextTypeInfo; + _reflectedType = reflectedType; + } + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.FieldInfo_CustomAttributes(this); +#endif + + foreach (CustomAttributeData cad in TrueCustomAttributes) + yield return cad; + + if (DeclaringType.IsExplicitLayout) + { + int offset = ExplicitLayoutFieldOffsetData; + CustomAttributeTypedArgument offsetArgument = new CustomAttributeTypedArgument(typeof(int), offset); + yield return new RuntimePseudoCustomAttributeData(typeof(FieldOffsetAttribute), new CustomAttributeTypedArgument[] { offsetArgument }); + } + + FieldAttributes attributes = Attributes; + if (0 != (attributes & FieldAttributes.NotSerialized)) + { + yield return new RuntimePseudoCustomAttributeData(typeof(NonSerializedAttribute), null); + } + } + } + + public sealed override Type DeclaringType + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.FieldInfo_DeclaringType(this); +#endif + + return _contextTypeInfo; + } + } + + public sealed override Type FieldType + { + get + { + Type fieldType = _lazyFieldType; + if (fieldType == null) + { + _lazyFieldType = fieldType = this.FieldRuntimeType; + } + + return fieldType; + } + } + + public abstract override Type[] GetOptionalCustomModifiers(); + + public abstract override Type[] GetRequiredCustomModifiers(); + + public sealed override object GetValue(object obj) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.FieldInfo_GetValue(this, obj); +#endif + + FieldAccessor fieldAccessor = this.FieldAccessor; + return fieldAccessor.GetField(obj); + } + + public sealed override object GetValueDirect(TypedReference obj) + { + if (obj.IsNull) + throw new ArgumentException(SR.Arg_TypedReference_Null); + + FieldAccessor fieldAccessor = this.FieldAccessor; + return fieldAccessor.GetFieldDirect(obj); + } + + public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); + + public sealed override Module Module + { + get + { + return DefiningType.Module; + } + } + + public sealed override Type ReflectedType + { + get + { + return _reflectedType; + } + } + + public sealed override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.FieldInfo_SetValue(this, obj, value); +#endif + + FieldAccessor fieldAccessor = this.FieldAccessor; + BinderBundle binderBundle = binder.ToBinderBundle(invokeAttr, culture); + fieldAccessor.SetField(obj, value, binderBundle); + } + + public sealed override void SetValueDirect(TypedReference obj, object value) + { + if (obj.IsNull) + throw new ArgumentException(SR.Arg_TypedReference_Null); + + FieldAccessor fieldAccessor = this.FieldAccessor; + fieldAccessor.SetFieldDirect(obj, value); + } + + Type ITraceableTypeMember.ContainingType + { + get + { + return _contextTypeInfo; + } + } + + /// + /// Override to provide the metadata based name of a field. (Different from the Name + /// property in that it does not go into the reflection trace logic.) + /// + protected abstract string MetadataName { get; } + + public sealed override string Name + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.FieldInfo_Name(this); +#endif + + return MetadataName; + } + } + + string ITraceableTypeMember.MemberName + { + get + { + return MetadataName; + } + } + + public sealed override object GetRawConstantValue() + { + if (!IsLiteral) + throw new InvalidOperationException(); + + object defaultValue; + if (!GetDefaultValueIfAvailable(raw: true, defaultValue: out defaultValue)) + throw new BadImageFormatException(); // Field marked literal but has no default value. + + return defaultValue; + } + + // Types that derive from RuntimeFieldInfo must implement the following public surface area members + public abstract override FieldAttributes Attributes { get; } + public abstract override int MetadataToken { get; } + public abstract override string ToString(); + public abstract override bool Equals(object obj); + public abstract override int GetHashCode(); + public abstract override RuntimeFieldHandle FieldHandle { get; } + + /// + /// Get the default value if exists for a field by parsing metadata. Return false if there is no default value. + /// + protected abstract bool GetDefaultValueIfAvailable(bool raw, out object defaultValue); + + /// + /// Return a FieldAccessor object for accessing the value of a non-literal field. May rely on metadata to create correct accessor. + /// + protected abstract FieldAccessor TryGetFieldAccessor(); + + private FieldAccessor FieldAccessor + { + get + { + FieldAccessor fieldAccessor = _lazyFieldAccessor; + if (fieldAccessor == null) + { + if (this.IsLiteral) + { + // Legacy: ECMA335 does not require that the metadata literal match the type of the field that declares it. + // For desktop compat, we return the metadata literal as is and do not attempt to convert or validate against the Field type. + + object defaultValue; + if (!GetDefaultValueIfAvailable(raw: false, defaultValue: out defaultValue)) + { + throw new BadImageFormatException(); // Field marked literal but has no default value. + } + + _lazyFieldAccessor = fieldAccessor = ReflectionCoreExecution.ExecutionEnvironment.CreateLiteralFieldAccessor(defaultValue, FieldType.TypeHandle); + } + else + { + _lazyFieldAccessor = fieldAccessor = TryGetFieldAccessor(); + if (fieldAccessor == null) + throw ReflectionCoreExecution.ExecutionDomain.CreateNonInvokabilityException(this); + } + } + return fieldAccessor; + } + } + + /// + /// Return the type of the field by parsing metadata. + /// + protected abstract RuntimeTypeInfo FieldRuntimeType { get; } + + protected RuntimeFieldInfo WithDebugName() + { + bool populateDebugNames = DeveloperExperienceState.DeveloperExperienceModeEnabled; +#if DEBUG + populateDebugNames = true; +#endif + if (!populateDebugNames) + return this; + + if (_debugName == null) + { + _debugName = "Constructing..."; // Protect against any inadvertent reentrancy. + _debugName = ((ITraceableTypeMember)this).MemberName; + } + return this; + } + + /// + /// Return the DefiningTypeInfo as a RuntimeTypeInfo (instead of as a format specific type info) + /// + protected abstract RuntimeTypeInfo DefiningType { get; } + + protected abstract IEnumerable TrueCustomAttributes { get; } + protected abstract int ExplicitLayoutFieldOffsetData { get; } + + /// + /// Returns the field offset (asserts and throws if not an instance field). Does not include the size of the object header. + /// + internal int Offset => FieldAccessor.Offset; + + protected readonly RuntimeTypeInfo _contextTypeInfo; + protected readonly RuntimeTypeInfo _reflectedType; + + private volatile FieldAccessor _lazyFieldAccessor; + + private volatile Type _lazyFieldType; + + private string _debugName; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Assignability.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Assignability.cs new file mode 100644 index 00000000000000..074b49aab2e930 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Assignability.cs @@ -0,0 +1,387 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Runtime.TypeInfos; + +using Internal.Reflection.Core; + +namespace System.Reflection.Runtime.General +{ + internal static class Assignability + { + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:UnrecognizedReflectionPattern", + Justification = "Just instantiating over formals for desktop compat reasons")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", + Justification = "Just instantiating over formals for desktop compat reasons")] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Looking at interface list is safe because we wouldn't remove reflection-visible interface from a reflection-visible type")] + public static bool IsAssignableFrom(Type toTypeInfo, Type fromTypeInfo) + { + if (toTypeInfo == null) + throw new NullReferenceException(); + if (fromTypeInfo == null) + return false; // It would be more appropriate to throw ArgumentNullException here, but returning "false" is the desktop-compat behavior. + + if (fromTypeInfo.Equals(toTypeInfo)) + return true; + + if (toTypeInfo.IsGenericTypeDefinition) + { + // Asking whether something can cast to a generic type definition is arguably meaningless. The desktop CLR Reflection layer converts all + // generic type definitions to generic type instantiations closed over the formal generic type parameters. The .NET Native framework + // keeps the two separate. Fortunately, under either interpretation, returning "false" unless the two types are identical is still a + // defensible behavior. To avoid having the rest of the code deal with the differing interpretations, we'll short-circuit this now. + return false; + } + + if (fromTypeInfo.IsGenericTypeDefinition) + { + // The desktop CLR Reflection layer converts all generic type definitions to generic type instantiations closed over the formal + // generic type parameters. The .NET Native framework keeps the two separate. For the purpose of IsAssignableFrom(), + // it makes sense to unify the two for the sake of backward compat. We'll just make the transform here so that the rest of code + // doesn't need to know about this quirk. + fromTypeInfo = fromTypeInfo.GetGenericTypeDefinition().MakeGenericType(fromTypeInfo.GetGenericTypeParameters()); + } + + if (fromTypeInfo.CanCastTo(toTypeInfo)) + return true; + + // Desktop compat: IsAssignableFrom() considers T as assignable to Nullable (but does not check if T is a generic parameter.) + if (!fromTypeInfo.IsGenericParameter) + { + Type nullableUnderlyingType = Nullable.GetUnderlyingType(toTypeInfo); + if (nullableUnderlyingType != null && nullableUnderlyingType.Equals(fromTypeInfo)) + return true; + } + return false; + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Looking at interface list is safe because we wouldn't remove reflection-visible interface from a reflection-visible type")] + private static bool CanCastTo(this Type fromTypeInfo, Type toTypeInfo) + { + if (fromTypeInfo.Equals(toTypeInfo)) + return true; + + if (fromTypeInfo.IsArray) + { + if (toTypeInfo.IsInterface) + return fromTypeInfo.CanCastArrayToInterface(toTypeInfo); + + if (fromTypeInfo.IsSubclassOf(toTypeInfo)) + return true; // T[] is castable to Array or Object. + + if (!toTypeInfo.IsArray) + return false; + + int rank = fromTypeInfo.GetArrayRank(); + if (rank != toTypeInfo.GetArrayRank()) + return false; + + bool fromTypeIsSzArray = fromTypeInfo.IsSZArray; + bool toTypeIsSzArray = toTypeInfo.IsSZArray; + if (fromTypeIsSzArray != toTypeIsSzArray) + { + // T[] is assignable to T[*] but not vice-versa. + if (!(rank == 1 && !toTypeIsSzArray)) + { + return false; // T[*] is not castable to T[] + } + } + + Type toElementTypeInfo = toTypeInfo.GetElementType(); + Type fromElementTypeInfo = fromTypeInfo.GetElementType(); + return fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo); + } + + if (fromTypeInfo.IsByRef) + { + if (!toTypeInfo.IsByRef) + return false; + + Type toElementTypeInfo = toTypeInfo.GetElementType(); + Type fromElementTypeInfo = fromTypeInfo.GetElementType(); + return fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo); + } + + if (fromTypeInfo.IsPointer) + { + if (!toTypeInfo.IsPointer) + return false; + + Type toElementTypeInfo = toTypeInfo.GetElementType(); + Type fromElementTypeInfo = fromTypeInfo.GetElementType(); + return fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo); + } + + if (fromTypeInfo.IsGenericParameter) + { + // + // A generic parameter can be cast to any of its constraints, or object, if none are specified, or ValueType if the "struct" constraint is + // specified. + // + // This has to be coded as its own case as TypeInfo.BaseType on a generic parameter doesn't always return what you'd expect. + // + if (toTypeInfo == typeof(object)) + return true; + + if (toTypeInfo == typeof(ValueType)) + { + GenericParameterAttributes attributes = fromTypeInfo.GenericParameterAttributes; + if ((attributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0) + return true; + } + + foreach (Type constraintType in fromTypeInfo.GetGenericParameterConstraints()) + { + if (constraintType.CanCastTo(toTypeInfo)) + return true; + } + + return false; + } + + if (toTypeInfo.IsArray || toTypeInfo.IsByRef || toTypeInfo.IsPointer || toTypeInfo.IsGenericParameter) + return false; + + if (fromTypeInfo.MatchesWithVariance(toTypeInfo)) + return true; + + if (toTypeInfo.IsInterface) + { + foreach (Type ifc in fromTypeInfo.GetInterfaces()) + { + if (ifc.MatchesWithVariance(toTypeInfo)) + return true; + } + return false; + } + else + { + // Interfaces are always castable to System.Object. The code below will not catch this as interfaces report their BaseType as null. + if (toTypeInfo == typeof(object) && fromTypeInfo.IsInterface) + return true; + + Type walk = fromTypeInfo; + for (;;) + { + Type baseType = walk.BaseType; + if (baseType == null) + return false; + walk = baseType; + if (walk.MatchesWithVariance(toTypeInfo)) + return true; + } + } + } + + // + // Check a base type or implemented interface type for equivalence (taking into account variance for generic instantiations.) + // Does not check ancestors recursively. + // + private static bool MatchesWithVariance(this Type fromTypeInfo, Type toTypeInfo) + { + Debug.Assert(!(fromTypeInfo.IsArray || fromTypeInfo.IsByRef || fromTypeInfo.IsPointer || fromTypeInfo.IsGenericParameter)); + Debug.Assert(!(toTypeInfo.IsArray || toTypeInfo.IsByRef || toTypeInfo.IsPointer || toTypeInfo.IsGenericParameter)); + + if (fromTypeInfo.Equals(toTypeInfo)) + return true; + + if (!(fromTypeInfo.IsConstructedGenericType && toTypeInfo.IsConstructedGenericType)) + return false; + + Type genericTypeDefinition = fromTypeInfo.GetGenericTypeDefinition(); + if (!genericTypeDefinition.Equals(toTypeInfo.GetGenericTypeDefinition())) + return false; + + Type[] fromTypeArguments = fromTypeInfo.GenericTypeArguments; + Type[] toTypeArguments = toTypeInfo.GenericTypeArguments; + Type[] genericTypeParameters = genericTypeDefinition.GetGenericTypeParameters(); + for (int i = 0; i < genericTypeParameters.Length; i++) + { + Type fromTypeArgumentInfo = fromTypeArguments[i]; + Type toTypeArgumentInfo = toTypeArguments[i]; + + GenericParameterAttributes attributes = genericTypeParameters[i].GenericParameterAttributes; + switch (attributes & GenericParameterAttributes.VarianceMask) + { + case GenericParameterAttributes.Covariant: + if (!(fromTypeArgumentInfo.IsGcReferenceTypeAndCastableTo(toTypeArgumentInfo))) + return false; + break; + + case GenericParameterAttributes.Contravariant: + if (!(toTypeArgumentInfo.IsGcReferenceTypeAndCastableTo(fromTypeArgumentInfo))) + return false; + break; + + case GenericParameterAttributes.None: + if (!(fromTypeArgumentInfo.Equals(toTypeArgumentInfo))) + return false; + break; + + default: + throw new BadImageFormatException(); // Unexpected variance value in metadata. + } + } + return true; + } + + // + // A[] can cast to B[] if one of the following are true: + // + // A can cast to B under variance rules. + // + // A and B are both integers or enums and have the same reduced type (i.e. represent the same-sized integer, ignoring signed/unsigned differences.) + // "char" is not interchangable with short/ushort. "bool" is not interchangable with byte/sbyte. + // + // For desktop compat, A& and A* follow the same rules. + // + private static bool IsElementTypeCompatibleWith(this Type fromTypeInfo, Type toTypeInfo) + { + if (fromTypeInfo.IsGcReferenceTypeAndCastableTo(toTypeInfo)) + return true; + + Type reducedFromType = fromTypeInfo.ReducedType(); + Type reducedToType = toTypeInfo.ReducedType(); + if (reducedFromType.Equals(reducedToType)) + return true; + + return false; + } + + private static Type ReducedType(this Type t) + { + if (t.IsEnum) + t = Enum.GetUnderlyingType(t); + + if (t == typeof(byte)) + return typeof(sbyte); + + if (t == typeof(ushort)) + return typeof(short); + + if (t == typeof(uint)) + return typeof(int); + + if (t == typeof(ulong)) + return typeof(long); + + if (t == typeof(UIntPtr) || t == typeof(IntPtr)) + { +#if TARGET_64BIT + return typeof(long); +#else + return typeof(int); +#endif + } + + return t; + } + + // + // Contra/CoVariance. + // + // IEnumerable can cast to IEnumerable if D can cast to B and if there's no possibility that D is a value type. + // + private static bool IsGcReferenceTypeAndCastableTo(this Type fromTypeInfo, Type toTypeInfo) + { + if (fromTypeInfo.Equals(toTypeInfo)) + return true; + + if (fromTypeInfo.ProvablyAGcReferenceType()) + return fromTypeInfo.CanCastTo(toTypeInfo); + + return false; + } + + // + // A true result indicates that a type can never be a value type. This is important when testing variance-compatibility. + // + private static bool ProvablyAGcReferenceType(this Type t) + { + if (t.IsGenericParameter) + { + GenericParameterAttributes attributes = t.GenericParameterAttributes; + if ((attributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0) + return true; // generic parameter with a "class" constraint. + } + + return t.ProvablyAGcReferenceTypeHelper(); + } + + private static bool ProvablyAGcReferenceTypeHelper(this Type t) + { + if (t.IsArray) + return true; + + if (t.IsByRef || t.IsPointer) + return false; + + if (t.IsGenericParameter) + { + // We intentionally do not check for a "class" constraint on generic parameter ancestors. + // That's because this property does not propagate up the constraining hierarchy. + // (e.g. "class A where S : T, where T : class" does not guarantee that S is a class.) + + foreach (Type constraintType in t.GetGenericParameterConstraints()) + { + if (constraintType.ProvablyAGcReferenceTypeHelper()) + return true; + } + return false; + } + + return t.IsClass && t != typeof(object) && t != typeof(ValueType) && t != typeof(Enum); + } + + // + // T[] casts to IList. This could be handled by the normal ancestor-walking code + // but for one complication: T[] also casts to IList if T[] casts to U[]. + // + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Looking at interface list is safe because we wouldn't remove reflection-visible interface from a reflection-visible type")] + private static bool CanCastArrayToInterface(this Type fromTypeInfo, Type toTypeInfo) + { + Debug.Assert(fromTypeInfo.IsArray); + Debug.Assert(toTypeInfo.IsInterface); + + if (toTypeInfo.IsConstructedGenericType) + { + Type[] toTypeGenericTypeArguments = toTypeInfo.GenericTypeArguments; + if (toTypeGenericTypeArguments.Length != 1) + return false; + Type toElementTypeInfo = toTypeGenericTypeArguments[0]; + + Type toTypeGenericTypeDefinition = toTypeInfo.GetGenericTypeDefinition(); + Type fromElementTypeInfo = fromTypeInfo.GetElementType(); + foreach (Type ifc in fromTypeInfo.GetInterfaces()) + { + if (ifc.IsConstructedGenericType) + { + Type ifcGenericTypeDefinition = ifc.GetGenericTypeDefinition(); + if (ifcGenericTypeDefinition.Equals(toTypeGenericTypeDefinition)) + { + if (fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo)) + return true; + } + } + } + return false; + } + else + { + foreach (Type ifc in fromTypeInfo.GetInterfaces()) + { + if (ifc.Equals(toTypeInfo)) + return true; + } + return false; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/BlockedRuntimeTypeNameGenerator.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/BlockedRuntimeTypeNameGenerator.cs new file mode 100644 index 00000000000000..fd2ffc78705462 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/BlockedRuntimeTypeNameGenerator.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Diagnostics; +using System.Collections.Concurrent; + +namespace System.Reflection.Runtime.General +{ + // + // This class dispenses randomized strings (that serve as both the fake name and fake assembly container) for + // reflection-blocked types. + // + // The names are randomized to prevent apps from hard-wiring dependencies on them or attempting to serialize them + // across app execution. + // + internal static class BlockedRuntimeTypeNameGenerator + { + public static string GetNameForBlockedRuntimeType(RuntimeTypeHandle typeHandle) + { + string name = s_blockedNameTable.GetOrAdd(new RuntimeTypeHandleKey(typeHandle)); + return name; + } + + private sealed class BlockedRuntimeTypeNameTable : ConcurrentUnifier + { + protected sealed override string Factory(RuntimeTypeHandleKey key) + { + uint count = s_counter++; + return $"$BlockedFromReflection_{count}_{Guid.NewGuid().ToString().Substring(0, 8)}"; + } + + private static uint s_counter; + } + + private static readonly BlockedRuntimeTypeNameTable s_blockedNameTable = new BlockedRuntimeTypeNameTable(); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.NativeFormat.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.NativeFormat.cs new file mode 100644 index 00000000000000..26d2e4bc6b0900 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.NativeFormat.cs @@ -0,0 +1,207 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Collections.Generic; + +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeInfos.NativeFormat; +using System.Reflection.Runtime.Assemblies; +using System.Reflection.Runtime.Assemblies.NativeFormat; +using System.Reflection.Runtime.Dispensers; +using System.Reflection.Runtime.PropertyInfos; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +using Internal.Metadata.NativeFormat; + +//================================================================================================================= +// This file collects the various chokepoints that create the various Runtime*Info objects. This allows +// easy reviewing of the overall caching and unification policy. +// +// The dispenser functions are defined as static members of the associated Info class. This permits us +// to keep the constructors private to ensure that these really are the only ways to obtain these objects. +//================================================================================================================= + +namespace System.Reflection.Runtime.Assemblies +{ + //----------------------------------------------------------------------------------------------------------- + // Assemblies (maps 1-1 with a MetadataReader/ScopeDefinitionHandle. + //----------------------------------------------------------------------------------------------------------- + internal partial class RuntimeAssemblyInfo + { + static partial void GetNativeFormatRuntimeAssembly(AssemblyBindResult bindResult, ref RuntimeAssembly runtimeAssembly) + { + if (bindResult.Reader != null) + runtimeAssembly = NativeFormatRuntimeAssembly.GetRuntimeAssembly(bindResult.Reader, bindResult.ScopeDefinitionHandle, bindResult.OverflowScopes); + } + } +} + +namespace System.Reflection.Runtime.Assemblies.NativeFormat +{ + internal sealed partial class NativeFormatRuntimeAssembly + { + internal static RuntimeAssembly GetRuntimeAssembly(MetadataReader reader, ScopeDefinitionHandle scope, IEnumerable overflowScopes) + { + return s_scopeToAssemblyDispenser.GetOrAdd(new RuntimeAssemblyKey(reader, scope, overflowScopes)); + } + + private static readonly Dispenser s_scopeToAssemblyDispenser = + DispenserFactory.CreateDispenserV( + DispenserScenario.Scope_Assembly, + delegate (RuntimeAssemblyKey qScopeDefinition) + { + return (RuntimeAssembly)new NativeFormat.NativeFormatRuntimeAssembly(qScopeDefinition.Reader, qScopeDefinition.Handle, qScopeDefinition.Overflows); + } + ); + + //----------------------------------------------------------------------------------------------------------- + // Captures a qualified scope (a reader plus a handle) representing the canonical definition of an assembly, + // plus a set of "overflow" scopes representing additional pieces of the assembly. + //----------------------------------------------------------------------------------------------------------- + private struct RuntimeAssemblyKey : IEquatable + { + public RuntimeAssemblyKey(MetadataReader reader, ScopeDefinitionHandle handle, IEnumerable overflows) + { + _reader = reader; + _handle = handle; + _overflows = overflows; + } + + public MetadataReader Reader { get { return _reader; } } + public ScopeDefinitionHandle Handle { get { return _handle; } } + public IEnumerable Overflows { get { return _overflows; } } + public ScopeDefinition ScopeDefinition + { + get + { + return _handle.GetScopeDefinition(_reader); + } + } + + public override bool Equals(object obj) + { + if (!(obj is RuntimeAssemblyKey other)) + return false; + return Equals(other); + } + + + public bool Equals(RuntimeAssemblyKey other) + { + // Equality depends only on the canonical definition of an assembly, not + // the overflows. + if (!(_reader == other._reader)) + return false; + if (!(_handle.Equals(other._handle))) + return false; + return true; + } + + public override int GetHashCode() + { + return _handle.GetHashCode(); + } + + private readonly MetadataReader _reader; + private readonly ScopeDefinitionHandle _handle; + private readonly IEnumerable _overflows; + } + } +} + +namespace System.Reflection.Runtime.FieldInfos.NativeFormat +{ + //----------------------------------------------------------------------------------------------------------- + // FieldInfos + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class NativeFormatRuntimeFieldInfo + { + internal static RuntimeFieldInfo GetRuntimeFieldInfo(FieldHandle fieldHandle, NativeFormatRuntimeNamedTypeInfo definingTypeInfo, RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType) + { + return new NativeFormatRuntimeFieldInfo(fieldHandle, definingTypeInfo, contextTypeInfo, reflectedType).WithDebugName(); + } + } +} + +namespace System.Reflection.Runtime.PropertyInfos.NativeFormat +{ + //----------------------------------------------------------------------------------------------------------- + // PropertyInfos + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class NativeFormatRuntimePropertyInfo + { + internal static RuntimePropertyInfo GetRuntimePropertyInfo(PropertyHandle propertyHandle, NativeFormatRuntimeNamedTypeInfo definingTypeInfo, RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType) + { + return new NativeFormatRuntimePropertyInfo(propertyHandle, definingTypeInfo, contextTypeInfo, reflectedType).WithDebugName(); + } + } +} + +namespace System.Reflection.Runtime.EventInfos.NativeFormat +{ + //----------------------------------------------------------------------------------------------------------- + // EventInfos + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class NativeFormatRuntimeEventInfo + { + internal static RuntimeEventInfo GetRuntimeEventInfo(EventHandle eventHandle, NativeFormatRuntimeNamedTypeInfo definingTypeInfo, RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType) + { + return new NativeFormatRuntimeEventInfo(eventHandle, definingTypeInfo, contextTypeInfo, reflectedType).WithDebugName(); + } + } +} + +namespace System.Reflection.Runtime.Modules.NativeFormat +{ + //----------------------------------------------------------------------------------------------------------- + // Modules (these exist only because Modules still exist in the Win8P surface area. There is a 1-1 + // mapping between Assemblies and Modules.) + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class NativeFormatRuntimeModule + { + internal static RuntimeModule GetRuntimeModule(NativeFormatRuntimeAssembly assembly) + { + return new NativeFormatRuntimeModule(assembly); + } + } +} + +namespace System.Reflection.Runtime.ParameterInfos.NativeFormat +{ + //----------------------------------------------------------------------------------------------------------- + // ParameterInfos for MethodBase objects with Parameter metadata. + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class NativeFormatMethodParameterInfo + { + internal static NativeFormatMethodParameterInfo GetNativeFormatMethodParameterInfo(MethodBase member, MethodHandle methodHandle, int position, ParameterHandle parameterHandle, QSignatureTypeHandle qualifiedParameterType, TypeContext typeContext) + { + return new NativeFormatMethodParameterInfo(member, methodHandle, position, parameterHandle, qualifiedParameterType, typeContext); + } + } +} + +namespace System.Reflection.Runtime.CustomAttributes +{ + using NativeFormat; + + //----------------------------------------------------------------------------------------------------------- + // CustomAttributeData objects returned by various CustomAttributes properties. + //----------------------------------------------------------------------------------------------------------- + internal abstract partial class RuntimeCustomAttributeData + { + internal static IEnumerable GetCustomAttributes(MetadataReader reader, CustomAttributeHandleCollection customAttributeHandles) + { + foreach (CustomAttributeHandle customAttributeHandle in customAttributeHandles) + yield return GetCustomAttributeData(reader, customAttributeHandle); + } + + private static CustomAttributeData GetCustomAttributeData(MetadataReader reader, CustomAttributeHandle customAttributeHandle) + { + return new NativeFormatCustomAttributeData(reader, customAttributeHandle); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.cs new file mode 100644 index 00000000000000..e524aa3bb8b85f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.cs @@ -0,0 +1,246 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Collections.Generic; + +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.Assemblies; +using System.Reflection.Runtime.Dispensers; +using System.Reflection.Runtime.PropertyInfos; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +//================================================================================================================= +// This file collects the various chokepoints that create the various Runtime*Info objects. This allows +// easy reviewing of the overall caching and unification policy. +// +// The dispenser functions are defined as static members of the associated Info class. This permits us +// to keep the constructors private to ensure that these really are the only ways to obtain these objects. +//================================================================================================================= + +namespace System.Reflection.Runtime.Assemblies +{ + //----------------------------------------------------------------------------------------------------------- + // Assemblies (maps 1-1 with a MetadataReader/ScopeDefinitionHandle. + //----------------------------------------------------------------------------------------------------------- + internal partial class RuntimeAssemblyInfo + { + /// + /// Returns non-null or throws. + /// + internal static RuntimeAssembly GetRuntimeAssembly(RuntimeAssemblyName assemblyRefName) + { + Exception assemblyLoadException = TryGetRuntimeAssembly(assemblyRefName, out RuntimeAssemblyInfo result); + if (assemblyLoadException != null) + throw assemblyLoadException; + return result; + } + + /// + /// Returns non-null or throws. + /// + internal static RuntimeAssembly GetRuntimeAssemblyFromByteArray(byte[] rawAssembly, byte[] pdbSymbolStore) + { + AssemblyBinder binder = ReflectionCoreExecution.ExecutionDomain.ReflectionDomainSetup.AssemblyBinder; + if (!binder.Bind(rawAssembly, pdbSymbolStore, out AssemblyBindResult bindResult, out Exception exception)) + { + if (exception != null) + throw exception; + else + throw new BadImageFormatException(); + } + + RuntimeAssembly result = GetRuntimeAssembly(bindResult); + return result; + } + + /// + /// Returns non-null or throws. + /// + internal static RuntimeAssembly GetRuntimeAssemblyFromPath(string assemblyPath) + { + AssemblyBinder binder = ReflectionCoreExecution.ExecutionDomain.ReflectionDomainSetup.AssemblyBinder; + if (!binder.Bind(assemblyPath, out AssemblyBindResult bindResult, out Exception exception)) + { + if (exception != null) + throw exception; + else + throw new BadImageFormatException(); + } + + RuntimeAssembly result = GetRuntimeAssembly(bindResult, assemblyPath); + return result; + } + + /// + /// Returns null if no assembly matches the assemblyRefName. Throws for other error cases. + /// + internal static RuntimeAssemblyInfo GetRuntimeAssemblyIfExists(RuntimeAssemblyName assemblyRefName) + { + object runtimeAssemblyOrException = s_assemblyRefNameToAssemblyDispenser.GetOrAdd(assemblyRefName); + if (runtimeAssemblyOrException is RuntimeAssemblyInfo runtimeAssembly) + return runtimeAssembly; + return null; + } + + internal static Exception TryGetRuntimeAssembly(RuntimeAssemblyName assemblyRefName, out RuntimeAssemblyInfo result) + { + object runtimeAssemblyOrException = s_assemblyRefNameToAssemblyDispenser.GetOrAdd(assemblyRefName); + if (runtimeAssemblyOrException is RuntimeAssemblyInfo runtimeAssembly) + { + result = runtimeAssembly; + return null; + } + else + { + result = null; + return (Exception)runtimeAssemblyOrException; + } + } + + // The "object" here is either a RuntimeAssembly or an Exception. + private static readonly Dispenser s_assemblyRefNameToAssemblyDispenser = + DispenserFactory.CreateDispenser( + DispenserScenario.AssemblyRefName_Assembly, + delegate (RuntimeAssemblyName assemblyRefName) + { + AssemblyBinder binder = ReflectionCoreExecution.ExecutionDomain.ReflectionDomainSetup.AssemblyBinder; + if (!binder.Bind(assemblyRefName, cacheMissedLookups: true, out AssemblyBindResult bindResult, out Exception exception)) + return exception; + + return GetRuntimeAssembly(bindResult); + } + ); + + private static RuntimeAssembly GetRuntimeAssembly(AssemblyBindResult bindResult, string assemblyPath = null) + { + RuntimeAssembly result = null; + + GetNativeFormatRuntimeAssembly(bindResult, ref result); + if (result != null) + return result; + + GetEcmaRuntimeAssembly(bindResult, assemblyPath, ref result); + if (result != null) + return result; + + throw new PlatformNotSupportedException(); + } + + // Use C# partial method feature to avoid complex #if logic, whichever code files are included will drive behavior + static partial void GetNativeFormatRuntimeAssembly(AssemblyBindResult bindResult, ref RuntimeAssembly runtimeAssembly); + static partial void GetEcmaRuntimeAssembly(AssemblyBindResult bindResult, string assemblyPath, ref RuntimeAssembly runtimeAssembly); + } +} + +namespace System.Reflection.Runtime.MethodInfos +{ + //----------------------------------------------------------------------------------------------------------- + // ConstructorInfos + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimePlainConstructorInfo : RuntimeConstructorInfo + { + internal static RuntimePlainConstructorInfo GetRuntimePlainConstructorInfo(TRuntimeMethodCommon common) + { + return new RuntimePlainConstructorInfo(common); + } + } + + //----------------------------------------------------------------------------------------------------------- + // Constructors for array types. + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimeSyntheticConstructorInfo : RuntimeConstructorInfo + { + internal static RuntimeSyntheticConstructorInfo GetRuntimeSyntheticConstructorInfo(SyntheticMethodId syntheticMethodId, RuntimeArrayTypeInfo declaringType, RuntimeTypeInfo[] runtimeParameterTypes, InvokerOptions options, CustomMethodInvokerAction action) + { + return new RuntimeSyntheticConstructorInfo(syntheticMethodId, declaringType, runtimeParameterTypes, options, action); + } + } + +#if FEATURE_COMINTEROP + //----------------------------------------------------------------------------------------------------------- + // Nullary constructor for types manufactured by Type.GetTypeFromCLSID(). + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimeCLSIDNullaryConstructorInfo : RuntimeConstructorInfo + { + internal static RuntimeCLSIDNullaryConstructorInfo GetRuntimeCLSIDNullaryConstructorInfo(RuntimeCLSIDTypeInfo declaringType) + { + return new RuntimeCLSIDNullaryConstructorInfo(declaringType); + } + } +#endif + + //----------------------------------------------------------------------------------------------------------- + // MethodInfos for method definitions (i.e. Foo.Moo() or Foo.Moo<>() but not Foo.Moo) + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimeNamedMethodInfo + { + internal static RuntimeNamedMethodInfo GetRuntimeNamedMethodInfo(TRuntimeMethodCommon common, RuntimeTypeInfo reflectedType) + { + RuntimeNamedMethodInfo method = new RuntimeNamedMethodInfo(common, reflectedType); + method.WithDebugName(); + return method; + } + } + + //----------------------------------------------------------------------------------------------------------- + // MethodInfos for constructed generic methods (Foo.Moo but not Foo.Moo<>) + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimeConstructedGenericMethodInfo : RuntimeMethodInfo + { + internal static RuntimeMethodInfo GetRuntimeConstructedGenericMethodInfo(RuntimeNamedMethodInfo genericMethodDefinition, RuntimeTypeInfo[] genericTypeArguments) + { + return new RuntimeConstructedGenericMethodInfo(genericMethodDefinition, genericTypeArguments).WithDebugName(); + } + } + + //----------------------------------------------------------------------------------------------------------- + // MethodInfos for the Get/Set methods on array types. + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimeSyntheticMethodInfo : RuntimeMethodInfo + { + internal static RuntimeMethodInfo GetRuntimeSyntheticMethodInfo(SyntheticMethodId syntheticMethodId, string name, RuntimeArrayTypeInfo declaringType, RuntimeTypeInfo[] runtimeParameterTypes, RuntimeTypeInfo returnType, InvokerOptions options, CustomMethodInvokerAction action) + { + return new RuntimeSyntheticMethodInfo(syntheticMethodId, name, declaringType, runtimeParameterTypes, returnType, options, action).WithDebugName(); + } + } +} + +namespace System.Reflection.Runtime.ParameterInfos +{ + //----------------------------------------------------------------------------------------------------------- + // ParameterInfos for MethodBase objects with no Parameter metadata. + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimeThinMethodParameterInfo : RuntimeMethodParameterInfo + { + internal static RuntimeThinMethodParameterInfo GetRuntimeThinMethodParameterInfo(MethodBase member, int position, QSignatureTypeHandle qualifiedParameterType, TypeContext typeContext) + { + return new RuntimeThinMethodParameterInfo(member, position, qualifiedParameterType, typeContext); + } + } + + //----------------------------------------------------------------------------------------------------------- + // ParameterInfos returned by PropertyInfo.GetIndexParameters() + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimePropertyIndexParameterInfo : RuntimeParameterInfo + { + internal static RuntimePropertyIndexParameterInfo GetRuntimePropertyIndexParameterInfo(RuntimePropertyInfo member, RuntimeParameterInfo backingParameter) + { + return new RuntimePropertyIndexParameterInfo(member, backingParameter); + } + } + + //----------------------------------------------------------------------------------------------------------- + // ParameterInfos returned by Get/Set methods on array types. + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimeSyntheticParameterInfo : RuntimeParameterInfo + { + internal static RuntimeSyntheticParameterInfo GetRuntimeSyntheticParameterInfo(MemberInfo member, int position, RuntimeTypeInfo parameterType) + { + return new RuntimeSyntheticParameterInfo(member, position, parameterType); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.NativeFormat.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.NativeFormat.cs new file mode 100644 index 00000000000000..48c5ff567b2e36 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.NativeFormat.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.TypeInfos.NativeFormat; + +namespace System.Reflection.Runtime.General +{ + internal static partial class Helpers + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NativeFormatRuntimeNamedTypeInfo CastToNativeFormatRuntimeNamedTypeInfo(this Type type) + { + Debug.Assert(type is NativeFormatRuntimeNamedTypeInfo); + return (NativeFormatRuntimeNamedTypeInfo)type; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.cs new file mode 100644 index 00000000000000..2541bc60506662 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.cs @@ -0,0 +1,252 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.Assemblies; +using System.Reflection.Runtime.MethodInfos; + +using Internal.LowLevelLinq; +using Internal.Runtime.Augments; +using Internal.Reflection.Augments; +using Internal.Reflection.Core.Execution; +using Internal.Reflection.Extensions.NonPortable; + +namespace System.Reflection.Runtime.General +{ + internal static partial class Helpers + { + // This helper helps reduce the temptation to write "h == default(RuntimeTypeHandle)" which causes boxing. + public static bool IsNull(this RuntimeTypeHandle h) + { + return h.Equals(default(RuntimeTypeHandle)); + } + + // Clones a Type[] array for the purpose of returning it from an api. + public static Type[] CloneTypeArray(this Type[] types) + { + int count = types.Length; + if (count == 0) + return Array.Empty(); // Ok not to clone empty arrays - those are immutable. + + Type[] clonedTypes = new Type[count]; + for (int i = 0; i < count; i++) + { + clonedTypes[i] = types[i]; + } + return clonedTypes; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Type[] GetGenericTypeParameters(this Type type) + { + Debug.Assert(type.IsGenericTypeDefinition); + return type.GetGenericArguments(); + } + + public static RuntimeTypeInfo[] ToRuntimeTypeInfoArray(this Type[] types) + { + int count = types.Length; + RuntimeTypeInfo[] typeInfos = new RuntimeTypeInfo[count]; + for (int i = 0; i < count; i++) + { + typeInfos[i] = types[i].CastToRuntimeTypeInfo(); + } + return typeInfos; + } + + public static string LastResortString(this RuntimeTypeHandle typeHandle) + { + return ReflectionCoreExecution.ExecutionEnvironment.GetLastResortString(typeHandle); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RuntimeNamedTypeInfo CastToRuntimeNamedTypeInfo(this Type type) + { + Debug.Assert(type is RuntimeNamedTypeInfo); + return (RuntimeNamedTypeInfo)type; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RuntimeTypeInfo CastToRuntimeTypeInfo(this Type type) + { + Debug.Assert(type == null || type is RuntimeTypeInfo); + return (RuntimeTypeInfo)type; + } + + public static ReadOnlyCollection ToReadOnlyCollection(this IEnumerable enumeration) + { + return new ReadOnlyCollection(enumeration.ToArray()); + } + + public static MethodInfo FilterAccessor(this MethodInfo accessor, bool nonPublic) + { + if (nonPublic) + return accessor; + if (accessor.IsPublic) + return accessor; + return null; + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Calling Assembly.GetType on a third-party Assembly class.")] + public static Type GetTypeCore(this Assembly assembly, string name, bool ignoreCase) + { + if (assembly is RuntimeAssemblyInfo runtimeAssembly) + { + // Not a recursion - this one goes to the actual instance method on RuntimeAssembly. + return runtimeAssembly.GetTypeCore(name, ignoreCase: ignoreCase); + } + else + { + // This is a third-party Assembly object. We can emulate GetTypeCore() by calling the public GetType() + // method. This is wasteful because it'll probably reparse a type string that we've already parsed + // but it can't be helped. + string escapedName = name.EscapeTypeNameIdentifier(); + return assembly.GetType(escapedName, throwOnError: false, ignoreCase: ignoreCase); + } + } + + public static TypeLoadException CreateTypeLoadException(string typeName, Assembly assemblyIfAny) + { + if (assemblyIfAny == null) + throw new TypeLoadException(SR.Format(SR.TypeLoad_TypeNotFound, typeName)); + else + throw Helpers.CreateTypeLoadException(typeName, assemblyIfAny.FullName); + } + + public static TypeLoadException CreateTypeLoadException(string typeName, string assemblyName) + { + string message = SR.Format(SR.TypeLoad_TypeNotFoundInAssembly, typeName, assemblyName); + return ReflectionAugments.CreateTypeLoadException(message, typeName); + } + + // Escape identifiers as described in "Specifying Fully Qualified Type Names" on msdn. + // Current link is http://msdn.microsoft.com/en-us/library/yfsftwz6(v=vs.110).aspx + public static string EscapeTypeNameIdentifier(this string identifier) + { + // Some characters in a type name need to be escaped + if (identifier != null && identifier.IndexOfAny(s_charsToEscape) != -1) + { + StringBuilder sbEscapedName = new StringBuilder(identifier.Length); + foreach (char c in identifier) + { + if (c.NeedsEscapingInTypeName()) + sbEscapedName.Append('\\'); + + sbEscapedName.Append(c); + } + identifier = sbEscapedName.ToString(); + } + return identifier; + } + + public static bool NeedsEscapingInTypeName(this char c) + { + return Array.IndexOf(s_charsToEscape, c) >= 0; + } + + private static readonly char[] s_charsToEscape = new char[] { '\\', '[', ']', '+', '*', '&', ',' }; + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Delegates always generate metadata for the Invoke method")] + public static RuntimeMethodInfo GetInvokeMethod(this RuntimeTypeInfo delegateType) + { + Debug.Assert(delegateType.IsDelegate); + + MethodInfo invokeMethod = delegateType.GetMethod("Invoke", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); + if (invokeMethod == null) + { + // No Invoke method found. Since delegate types are compiler constructed, the most likely cause is missing metadata rather than + // a missing Invoke method. + + // We're deliberating calling FullName rather than ToString() because if it's the type that's missing metadata, + // the FullName property constructs a more informative MissingMetadataException than we can. + string fullName = delegateType.FullName; + throw new MissingMetadataException(SR.Format(SR.Arg_InvokeMethodMissingMetadata, fullName)); // No invoke method found. + } + return (RuntimeMethodInfo)invokeMethod; + } + + public static BinderBundle ToBinderBundle(this Binder binder, BindingFlags invokeAttr, CultureInfo cultureInfo) + { + if (binder == null || binder is DefaultBinder || ((invokeAttr & BindingFlags.ExactBinding) != 0)) + return null; + return new BinderBundle(binder, cultureInfo); + } + + // Helper for ICustomAttributeProvider.GetCustomAttributes(). The result of this helper is returned directly to apps + // so it must always return a newly allocated array. Unlike most of the newer custom attribute apis, the attribute type + // need not derive from System.Attribute. (In particular, it can be an interface or System.Object.) + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "Array.CreateInstance is only used with reference types here and is therefore safe.")] + public static object[] InstantiateAsArray(this IEnumerable cads, Type actualElementType) + { + LowLevelList attributes = new LowLevelList(); + foreach (CustomAttributeData cad in cads) + { + object instantiatedAttribute = cad.Instantiate(); + attributes.Add(instantiatedAttribute); + } + + // This is here for desktop compatibility. ICustomAttribute.GetCustomAttributes() normally returns an array of the + // exact attribute type requested except in two cases: when the passed in type is an open type and when + // it is a value type. In these two cases, it returns an array of type Object[]. + bool useObjectArray = actualElementType.ContainsGenericParameters || actualElementType.IsValueType; + int count = attributes.Count; + object[] result = useObjectArray ? new object[count] : (object[])Array.CreateInstance(actualElementType, count); + + attributes.CopyTo(result, 0); + return result; + } + + public static bool GetCustomAttributeDefaultValueIfAny(IEnumerable customAttributes, bool raw, out object defaultValue) + { + // Legacy: If there are multiple default value attribute, the desktop picks one at random (and so do we...) + foreach (CustomAttributeData cad in customAttributes) + { + Type attributeType = cad.AttributeType; + if (attributeType.IsSubclassOf(typeof(CustomConstantAttribute))) + { + if (raw) + { + foreach (CustomAttributeNamedArgument namedArgument in cad.NamedArguments) + { + if (namedArgument.MemberName.Equals("Value")) + { + defaultValue = namedArgument.TypedValue.Value; + return true; + } + } + defaultValue = null; + return false; + } + else + { + CustomConstantAttribute customConstantAttribute = (CustomConstantAttribute)(cad.Instantiate()); + defaultValue = customConstantAttribute.Value; + return true; + } + } + if (attributeType.Equals(typeof(DecimalConstantAttribute))) + { + // We should really do a non-instanting check if "raw == false" but given that we don't support + // reflection-only loads, there isn't an observable difference. + DecimalConstantAttribute decimalConstantAttribute = (DecimalConstantAttribute)(cad.Instantiate()); + defaultValue = decimalConstantAttribute.Value; + return true; + } + } + + defaultValue = null; + return false; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/IRuntimeMemberInfoWithNoMetadataDefinition.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/IRuntimeMemberInfoWithNoMetadataDefinition.cs new file mode 100644 index 00000000000000..df8f969daaa1f6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/IRuntimeMemberInfoWithNoMetadataDefinition.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Runtime.General +{ + // This interface's presence on a MemberInfo testates that + // + // 1. The MemberInfo implemented by Reflection.Core + // 2. Is to be lumped into the "no metadata token" group for the purposes + // of the HasSameMetadataDefinitionAs() api. + // + internal interface IRuntimeMemberInfoWithNoMetadataDefinition + { + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/LegacyCustomAttributeApis.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/LegacyCustomAttributeApis.cs new file mode 100644 index 00000000000000..0817a21cba19fe --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/LegacyCustomAttributeApis.cs @@ -0,0 +1,249 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// The older-style CustomAttribute-related members on the various Reflection types. The implementation dependency +// stack on .Net Native differs from that of CoreClr due to the difference in development history. +// +// - IEnumerable xInfo.get_CustomAttributes is at the very bottom of the dependency stack. +// +// - CustomAttributeExtensions layers on top of that (primarily because it's the one with the nice generic methods.) +// +// - Everything else is a thin layer over one of these two. +// +// + +using System; +using System.IO; +using System.Reflection; +using System.Collections.Generic; +using System.Reflection.Runtime.General; + +using Internal.LowLevelLinq; +using Internal.Reflection.Extensions.NonPortable; + + +namespace System.Reflection.Runtime.Assemblies +{ + internal partial class RuntimeAssemblyInfo + { + public sealed override IList GetCustomAttributesData() => CustomAttributes.ToReadOnlyCollection(); + public sealed override object[] GetCustomAttributes(bool inherit) => CustomAttributeExtensions.GetCustomAttributes(this).ToArray(); // inherit is meaningless for Assemblies + + public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, skipTypeValidation: true); // inherit is meaningless for Assemblies + return cads.InstantiateAsArray(attributeType); + } + + public sealed override bool IsDefined(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, skipTypeValidation: true); // inherit is meaningless for Assemblies + return cads.Any(); + } + } +} + +namespace System.Reflection.Runtime.MethodInfos +{ + internal abstract partial class RuntimeConstructorInfo + { + public sealed override IList GetCustomAttributesData() => CustomAttributes.ToReadOnlyCollection(); + public sealed override object[] GetCustomAttributes(bool inherit) => CustomAttributeExtensions.GetCustomAttributes(this, inherit).ToArray(); + + public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: inherit, skipTypeValidation: true); + return cads.InstantiateAsArray(attributeType); + } + + public sealed override bool IsDefined(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: inherit, skipTypeValidation: true); + return cads.Any(); + } + } +} + +namespace System.Reflection.Runtime.EventInfos +{ + internal abstract partial class RuntimeEventInfo + { + public sealed override IList GetCustomAttributesData() => CustomAttributes.ToReadOnlyCollection(); + public sealed override object[] GetCustomAttributes(bool inherit) => CustomAttributeExtensions.GetCustomAttributes(this, inherit: false).ToArray(); // Desktop compat: for events, this form of the api ignores "inherit" + + public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: false, skipTypeValidation: true); // Desktop compat: for events, this form of the api ignores "inherit" + return cads.InstantiateAsArray(attributeType); + } + + public sealed override bool IsDefined(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: false, skipTypeValidation: true); // Desktop compat: for events, this form of the api ignores "inherit" + return cads.Any(); + } + } +} + +namespace System.Reflection.Runtime.FieldInfos +{ + internal abstract partial class RuntimeFieldInfo + { + public sealed override IList GetCustomAttributesData() => CustomAttributes.ToReadOnlyCollection(); + public sealed override object[] GetCustomAttributes(bool inherit) => CustomAttributeExtensions.GetCustomAttributes(this, inherit).ToArray(); + + public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: inherit, skipTypeValidation: true); + return cads.InstantiateAsArray(attributeType); + } + + public sealed override bool IsDefined(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: inherit, skipTypeValidation: true); + return cads.Any(); + } + } +} + +namespace System.Reflection.Runtime.MethodInfos +{ + internal abstract partial class RuntimeMethodInfo + { + public sealed override IList GetCustomAttributesData() => CustomAttributes.ToReadOnlyCollection(); + public sealed override object[] GetCustomAttributes(bool inherit) => CustomAttributeExtensions.GetCustomAttributes(this, inherit).ToArray(); + + public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: inherit, skipTypeValidation: true); + return cads.InstantiateAsArray(attributeType); + } + + public sealed override bool IsDefined(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: inherit, skipTypeValidation: true); + return cads.Any(); + } + } +} + +namespace System.Reflection.Runtime.Modules +{ + internal abstract partial class RuntimeModule + { + public sealed override IList GetCustomAttributesData() => CustomAttributes.ToReadOnlyCollection(); + public sealed override object[] GetCustomAttributes(bool inherit) => CustomAttributeExtensions.GetCustomAttributes(this).ToArray(); // inherit is meaningless for Modules + + public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, skipTypeValidation: true); // inherit is meaningless for Modules + return cads.InstantiateAsArray(attributeType); + } + + public sealed override bool IsDefined(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, skipTypeValidation: true); // inherit is meaningless for Modules + return cads.Any(); + } + } +} + +namespace System.Reflection.Runtime.ParameterInfos +{ + internal abstract partial class RuntimeParameterInfo + { + public sealed override IList GetCustomAttributesData() => CustomAttributes.ToReadOnlyCollection(); + public sealed override object[] GetCustomAttributes(bool inherit) => CustomAttributeExtensions.GetCustomAttributes(this, inherit: false).ToArray(); // Desktop compat: for parameters, this form of the api ignores "inherit" + + public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: false, skipTypeValidation: true); // Desktop compat: for parameters, this form of the api ignores "inherit" + return cads.InstantiateAsArray(attributeType); + } + + public sealed override bool IsDefined(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: false, skipTypeValidation: true); // Desktop compat: for parameters, this form of the api ignores "inherit" + return cads.Any(); + } + } +} + +namespace System.Reflection.Runtime.PropertyInfos +{ + internal abstract partial class RuntimePropertyInfo + { + public sealed override IList GetCustomAttributesData() => CustomAttributes.ToReadOnlyCollection(); + public sealed override object[] GetCustomAttributes(bool inherit) => CustomAttributeExtensions.GetCustomAttributes(this, inherit: false).ToArray(); // Desktop compat: for properties, this form of the api ignores "inherit" + + public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: false, skipTypeValidation: true); // Desktop compat: for properties, this form of the api ignores "inherit" + return cads.InstantiateAsArray(attributeType); + } + + public sealed override bool IsDefined(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: false, skipTypeValidation: true); // Desktop compat: for properties, this form of the api ignores "inherit" + return cads.Any(); + } + } +} + +namespace System.Reflection.Runtime.TypeInfos +{ + internal abstract partial class RuntimeTypeInfo + { + public sealed override IList GetCustomAttributesData() => CustomAttributes.ToReadOnlyCollection(); + public sealed override object[] GetCustomAttributes(bool inherit) => CustomAttributeExtensions.GetCustomAttributes(this, inherit).ToArray(); + + public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: inherit, skipTypeValidation: true); + return cads.InstantiateAsArray(attributeType); + } + + public sealed override bool IsDefined(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + IEnumerable cads = this.GetMatchingCustomAttributes(attributeType, inherit: inherit, skipTypeValidation: true); + return cads.Any(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ListBuilder.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ListBuilder.cs new file mode 100644 index 00000000000000..12ccb40f48aa27 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ListBuilder.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Reflection.Runtime.General +{ + // + // Struct-based list builder that's special cased to avoid allocations for lists of one element. + // + internal struct ListBuilder where T : class + { + public ListBuilder(int capacity) + { + _items = null; + _item = null; + _count = 0; + _capacity = capacity; +#if DEBUG + _toArrayAlreadyCalled = false; +#endif // DEBUG + } + + public T this[int index] + { + get + { + Debug.Assert(index < Count); + return (_items != null) ? _items[index] : _item; + } + } + + public T[] ToArray() + { +#if DEBUG + // ListBuilder does not always allocate a new array, though the ToArray() name connotates that it does (and in fact, we do pass the results + // of this method across api boundaries.) The minimizing of allocations is desirable, however, so instead, we restrict you to one call per ListBuilder. + Debug.Assert(!_toArrayAlreadyCalled, "Cannot call ListBuilder.ToArray() a second time. Copy the one you already got."); +#endif // DEBUG + if (_count == 0) + return Array.Empty(); + if (_count == 1) + return new T[1] { _item }; + + Array.Resize(ref _items, _count); + +#if DEBUG + _toArrayAlreadyCalled = true; +#endif // DEBUG + + return _items; + } + + public void CopyTo(object[] array, int index) + { + if (_count == 0) + return; + + if (_count == 1) + { + array[index] = _item; + return; + } + + Array.Copy(_items, 0, array, index, _count); + } + + public int Count + { + get + { + return _count; + } + } + + public void Add(T item) + { + if (_count == 0) + { + _item = item; + } + else + { + if (_count == 1) + { + if (_capacity < 2) + _capacity = 4; + _items = new T[_capacity]; + _items[0] = _item; + } + else if (_capacity == _count) + { + int newCapacity = 2 * _capacity; + Array.Resize(ref _items, newCapacity); + _capacity = newCapacity; + } + + _items[_count] = item; + } + _count++; + } + + private T[] _items; + private T _item; + private int _count; + private int _capacity; +#if DEBUG + private bool _toArrayAlreadyCalled; +#endif // DEBUG + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/MetadataReaderExtensions.NativeFormat.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/MetadataReaderExtensions.NativeFormat.cs new file mode 100644 index 00000000000000..1c9480246005d4 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/MetadataReaderExtensions.NativeFormat.cs @@ -0,0 +1,989 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections; +using System.Collections.Generic; +using System.Reflection.Runtime.Assemblies; + +using Internal.LowLevelLinq; +using Internal.Reflection.Core; + +using Internal.Runtime.Augments; + +using Internal.Metadata.NativeFormat; +using NativeFormatAssemblyFlags = global::Internal.Metadata.NativeFormat.AssemblyFlags; + +namespace System.Reflection.Runtime.General +{ + // + // Collect various metadata reading tasks for better chunking... + // + public static class NativeFormatMetadataReaderExtensions + { + public static bool StringOrNullEquals(this ConstantStringValueHandle handle, string valueOrNull, MetadataReader reader) + { + if (valueOrNull == null) + return handle.IsNull(reader); + if (handle.IsNull(reader)) + return false; + return handle.StringEquals(valueOrNull, reader); + } + + // Needed for RuntimeMappingTable access + public static int AsInt(this TypeDefinitionHandle typeDefinitionHandle) + { + unsafe + { + return *(int*)&typeDefinitionHandle; + } + } + + public static TypeDefinitionHandle AsTypeDefinitionHandle(this int i) + { + unsafe + { + return *(TypeDefinitionHandle*)&i; + } + } + + public static int AsInt(this MethodHandle methodHandle) + { + unsafe + { + return *(int*)&methodHandle; + } + } + + public static MethodHandle AsMethodHandle(this int i) + { + unsafe + { + return *(MethodHandle*)&i; + } + } + + public static int AsInt(this FieldHandle fieldHandle) + { + unsafe + { + return *(int*)&fieldHandle; + } + } + + public static FieldHandle AsFieldHandle(this int i) + { + unsafe + { + return *(FieldHandle*)&i; + } + } + + + public static bool IsNamespaceDefinitionHandle(this Handle handle, MetadataReader reader) + { + HandleType handleType = handle.HandleType; + return handleType == HandleType.NamespaceDefinition; + } + + public static bool IsNamespaceReferenceHandle(this Handle handle, MetadataReader reader) + { + HandleType handleType = handle.HandleType; + return handleType == HandleType.NamespaceReference; + } + + // Conversion where a invalid handle type indicates bad metadata rather a mistake by the caller. + public static ScopeReferenceHandle ToExpectedScopeReferenceHandle(this Handle handle, MetadataReader reader) + { + try + { + return handle.ToScopeReferenceHandle(reader); + } + catch (ArgumentException) + { + throw new BadImageFormatException(); + } + } + + // Conversion where a invalid handle type indicates bad metadata rather a mistake by the caller. + public static NamespaceReferenceHandle ToExpectedNamespaceReferenceHandle(this Handle handle, MetadataReader reader) + { + try + { + return handle.ToNamespaceReferenceHandle(reader); + } + catch (ArgumentException) + { + throw new BadImageFormatException(); + } + } + + // Conversion where a invalid handle type indicates bad metadata rather a mistake by the caller. + public static TypeDefinitionHandle ToExpectedTypeDefinitionHandle(this Handle handle, MetadataReader reader) + { + try + { + return handle.ToTypeDefinitionHandle(reader); + } + catch (ArgumentException) + { + throw new BadImageFormatException(); + } + } + + // Return any custom modifiers modifying the passed-in type and whose required/optional bit matches the passed in boolean. + // Because this is intended to service the GetCustomModifiers() apis, this helper will always return a freshly allocated array + // safe for returning to api callers. + internal static Type[] GetCustomModifiers(this Handle handle, MetadataReader reader, TypeContext typeContext, bool optional) + { + HandleType handleType = handle.HandleType; + Debug.Assert(handleType == HandleType.TypeDefinition || handleType == HandleType.TypeReference || handleType == HandleType.TypeSpecification || handleType == HandleType.ModifiedType); + if (handleType != HandleType.ModifiedType) + return Array.Empty(); + + LowLevelList customModifiers = new LowLevelList(); + do + { + ModifiedType modifiedType = handle.ToModifiedTypeHandle(reader).GetModifiedType(reader); + if (optional == modifiedType.IsOptional) + { + Type customModifier = modifiedType.ModifierType.Resolve(reader, typeContext); + customModifiers.Insert(0, customModifier); + } + + handle = modifiedType.Type; + handleType = handle.HandleType; + } + while (handleType == HandleType.ModifiedType); + return customModifiers.ToArray(); + } + + public static Handle SkipCustomModifiers(this Handle handle, MetadataReader reader) + { + HandleType handleType = handle.HandleType; + Debug.Assert(handleType == HandleType.TypeDefinition || handleType == HandleType.TypeReference || handleType == HandleType.TypeSpecification || handleType == HandleType.ModifiedType); + if (handleType != HandleType.ModifiedType) + return handle; + + do + { + ModifiedType modifiedType = handle.ToModifiedTypeHandle(reader).GetModifiedType(reader); + handle = modifiedType.Type; + handleType = handle.HandleType; + } + while (handleType == HandleType.ModifiedType); + + return handle; + } + + public static MethodSignature ParseMethodSignature(this Handle handle, MetadataReader reader) + { + return handle.ToMethodSignatureHandle(reader).GetMethodSignature(reader); + } + + public static FieldSignature ParseFieldSignature(this Handle handle, MetadataReader reader) + { + return handle.ToFieldSignatureHandle(reader).GetFieldSignature(reader); + } + + public static PropertySignature ParsePropertySignature(this Handle handle, MetadataReader reader) + { + return handle.ToPropertySignatureHandle(reader).GetPropertySignature(reader); + } + + // + // Used to split methods between DeclaredMethods and DeclaredConstructors. + // + public static bool IsConstructor(this MethodHandle methodHandle, MetadataReader reader) + { + Method method = methodHandle.GetMethod(reader); + return IsConstructor(ref method, reader); + } + + // This is specially designed for a hot path so we make some compromises in the signature: + // + // - "method" is passed by reference even though no side-effects are intended. + // + public static bool IsConstructor(ref Method method, MetadataReader reader) + { + if ((method.Flags & (MethodAttributes.RTSpecialName | MethodAttributes.SpecialName)) != (MethodAttributes.RTSpecialName | MethodAttributes.SpecialName)) + return false; + + ConstantStringValueHandle nameHandle = method.Name; + return nameHandle.StringEquals(ConstructorInfo.ConstructorName, reader) || nameHandle.StringEquals(ConstructorInfo.TypeConstructorName, reader); + } + + private static Exception ParseBoxedEnumConstantValue(this ConstantBoxedEnumValueHandle handle, MetadataReader reader, out object value) + { + ConstantBoxedEnumValue record = handle.GetConstantBoxedEnumValue(reader); + + Exception exception = null; + Type enumType = record.Type.TryResolve(reader, new TypeContext(null, null), ref exception); + if (enumType == null) + { + value = null; + return exception; + } + + if (!enumType.IsEnum) + throw new BadImageFormatException(); + + Type underlyingType = Enum.GetUnderlyingType(enumType); + + // Now box the value as the specified enum type. + unsafe + { + switch (record.Value.HandleType) + { + case HandleType.ConstantByteValue: + { + if (underlyingType != typeof(byte)) + throw new BadImageFormatException(); + + byte v = record.Value.ToConstantByteValueHandle(reader).GetConstantByteValue(reader).Value; + value = RuntimeAugments.Box(enumType.TypeHandle, (IntPtr)(&v)); + return null; + } + case HandleType.ConstantSByteValue: + { + if (underlyingType != typeof(sbyte)) + throw new BadImageFormatException(); + + sbyte v = record.Value.ToConstantSByteValueHandle(reader).GetConstantSByteValue(reader).Value; + value = RuntimeAugments.Box(enumType.TypeHandle, (IntPtr)(&v)); + return null; + } + case HandleType.ConstantInt16Value: + { + if (underlyingType != typeof(short)) + throw new BadImageFormatException(); + + short v = record.Value.ToConstantInt16ValueHandle(reader).GetConstantInt16Value(reader).Value; + value = RuntimeAugments.Box(enumType.TypeHandle, (IntPtr)(&v)); + return null; + } + case HandleType.ConstantUInt16Value: + { + if (underlyingType != typeof(ushort)) + throw new BadImageFormatException(); + + ushort v = record.Value.ToConstantUInt16ValueHandle(reader).GetConstantUInt16Value(reader).Value; + value = RuntimeAugments.Box(enumType.TypeHandle, (IntPtr)(&v)); + return null; + } + case HandleType.ConstantInt32Value: + { + if (underlyingType != typeof(int)) + throw new BadImageFormatException(); + + int v = record.Value.ToConstantInt32ValueHandle(reader).GetConstantInt32Value(reader).Value; + value = RuntimeAugments.Box(enumType.TypeHandle, (IntPtr)(&v)); + return null; + } + case HandleType.ConstantUInt32Value: + { + if (underlyingType != typeof(uint)) + throw new BadImageFormatException(); + + uint v = record.Value.ToConstantUInt32ValueHandle(reader).GetConstantUInt32Value(reader).Value; + value = RuntimeAugments.Box(enumType.TypeHandle, (IntPtr)(&v)); + return null; + } + case HandleType.ConstantInt64Value: + { + if (underlyingType != typeof(long)) + throw new BadImageFormatException(); + + long v = record.Value.ToConstantInt64ValueHandle(reader).GetConstantInt64Value(reader).Value; + value = RuntimeAugments.Box(enumType.TypeHandle, (IntPtr)(&v)); + return null; + } + case HandleType.ConstantUInt64Value: + { + if (underlyingType != typeof(ulong)) + throw new BadImageFormatException(); + + ulong v = record.Value.ToConstantUInt64ValueHandle(reader).GetConstantUInt64Value(reader).Value; + value = RuntimeAugments.Box(enumType.TypeHandle, (IntPtr)(&v)); + return null; + } + default: + throw new BadImageFormatException(); + } + } + } + + public static object ParseConstantValue(this Handle handle, MetadataReader reader) + { + object value; + Exception exception = handle.TryParseConstantValue(reader, out value); + if (exception != null) + throw exception; + return value; + } + + public static object ParseConstantNumericValue(this Handle handle, MetadataReader reader) + { + switch (handle.HandleType) + { + case HandleType.ConstantBooleanValue: + return handle.ToConstantBooleanValueHandle(reader).GetConstantBooleanValue(reader).Value; + case HandleType.ConstantCharValue: + return handle.ToConstantCharValueHandle(reader).GetConstantCharValue(reader).Value; + case HandleType.ConstantByteValue: + return handle.ToConstantByteValueHandle(reader).GetConstantByteValue(reader).Value; + case HandleType.ConstantSByteValue: + return handle.ToConstantSByteValueHandle(reader).GetConstantSByteValue(reader).Value; + case HandleType.ConstantInt16Value: + return handle.ToConstantInt16ValueHandle(reader).GetConstantInt16Value(reader).Value; + case HandleType.ConstantUInt16Value: + return handle.ToConstantUInt16ValueHandle(reader).GetConstantUInt16Value(reader).Value; + case HandleType.ConstantInt32Value: + return handle.ToConstantInt32ValueHandle(reader).GetConstantInt32Value(reader).Value; + case HandleType.ConstantUInt32Value: + return handle.ToConstantUInt32ValueHandle(reader).GetConstantUInt32Value(reader).Value; + case HandleType.ConstantInt64Value: + return handle.ToConstantInt64ValueHandle(reader).GetConstantInt64Value(reader).Value; + case HandleType.ConstantUInt64Value: + return handle.ToConstantUInt64ValueHandle(reader).GetConstantUInt64Value(reader).Value; + case HandleType.ConstantSingleValue: + return handle.ToConstantSingleValueHandle(reader).GetConstantSingleValue(reader).Value; + case HandleType.ConstantDoubleValue: + return handle.ToConstantDoubleValueHandle(reader).GetConstantDoubleValue(reader).Value; + default: + throw new BadImageFormatException(); + } + } + + public static Exception TryParseConstantValue(this Handle handle, MetadataReader reader, out object value) + { + HandleType handleType = handle.HandleType; + switch (handleType) + { + case HandleType.ConstantBooleanValue: + case HandleType.ConstantCharValue: + case HandleType.ConstantByteValue: + case HandleType.ConstantSByteValue: + case HandleType.ConstantInt16Value: + case HandleType.ConstantUInt16Value: + case HandleType.ConstantInt32Value: + case HandleType.ConstantUInt32Value: + case HandleType.ConstantInt64Value: + case HandleType.ConstantUInt64Value: + case HandleType.ConstantSingleValue: + case HandleType.ConstantDoubleValue: + value = handle.ParseConstantNumericValue(reader); + return null; + case HandleType.ConstantStringValue: + value = handle.ToConstantStringValueHandle(reader).GetConstantStringValue(reader).Value; + return null; + case HandleType.TypeDefinition: + case HandleType.TypeReference: + case HandleType.TypeSpecification: + { + Exception exception = null; + Type type = handle.TryResolve(reader, new TypeContext(null, null), ref exception); + value = type; + return (value == null) ? exception : null; + } + case HandleType.ConstantReferenceValue: + value = null; + return null; + case HandleType.ConstantBoxedEnumValue: + { + return handle.ToConstantBoxedEnumValueHandle(reader).ParseBoxedEnumConstantValue(reader, out value); + } + default: + { + Exception exception; + value = handle.TryParseConstantArray(reader, out exception); + if (value == null) + return exception; + return null; + } + } + } + + private static Array TryParseConstantArray(this Handle handle, MetadataReader reader, out Exception exception) + { + exception = null; + + HandleType handleType = handle.HandleType; + switch (handleType) + { + case HandleType.ConstantBooleanArray: + return handle.ToConstantBooleanArrayHandle(reader).GetConstantBooleanArray(reader).Value.ToArray(); + + case HandleType.ConstantCharArray: + return handle.ToConstantCharArrayHandle(reader).GetConstantCharArray(reader).Value.ToArray(); + + case HandleType.ConstantByteArray: + return handle.ToConstantByteArrayHandle(reader).GetConstantByteArray(reader).Value.ToArray(); + + case HandleType.ConstantSByteArray: + return handle.ToConstantSByteArrayHandle(reader).GetConstantSByteArray(reader).Value.ToArray(); + + case HandleType.ConstantInt16Array: + return handle.ToConstantInt16ArrayHandle(reader).GetConstantInt16Array(reader).Value.ToArray(); + + case HandleType.ConstantUInt16Array: + return handle.ToConstantUInt16ArrayHandle(reader).GetConstantUInt16Array(reader).Value.ToArray(); + + case HandleType.ConstantInt32Array: + return handle.ToConstantInt32ArrayHandle(reader).GetConstantInt32Array(reader).Value.ToArray(); + + case HandleType.ConstantUInt32Array: + return handle.ToConstantUInt32ArrayHandle(reader).GetConstantUInt32Array(reader).Value.ToArray(); + + case HandleType.ConstantInt64Array: + return handle.ToConstantInt64ArrayHandle(reader).GetConstantInt64Array(reader).Value.ToArray(); + + case HandleType.ConstantUInt64Array: + return handle.ToConstantUInt64ArrayHandle(reader).GetConstantUInt64Array(reader).Value.ToArray(); + + case HandleType.ConstantSingleArray: + return handle.ToConstantSingleArrayHandle(reader).GetConstantSingleArray(reader).Value.ToArray(); + + case HandleType.ConstantDoubleArray: + return handle.ToConstantDoubleArrayHandle(reader).GetConstantDoubleArray(reader).Value.ToArray(); + + case HandleType.ConstantEnumArray: + return TryParseConstantEnumArray(handle.ToConstantEnumArrayHandle(reader), reader, out exception); + + case HandleType.ConstantStringArray: + { + HandleCollection constantHandles = handle.ToConstantStringArrayHandle(reader).GetConstantStringArray(reader).Value; + string[] elements = new string[constantHandles.Count]; + int i = 0; + foreach (Handle constantHandle in constantHandles) + { + object elementValue; + exception = constantHandle.TryParseConstantValue(reader, out elementValue); + if (exception != null) + return null; + elements[i] = (string)elementValue; + i++; + } + return elements; + } + + case HandleType.ConstantHandleArray: + { + HandleCollection constantHandles = handle.ToConstantHandleArrayHandle(reader).GetConstantHandleArray(reader).Value; + object[] elements = new object[constantHandles.Count]; + int i = 0; + foreach (Handle constantHandle in constantHandles) + { + exception = constantHandle.TryParseConstantValue(reader, out elements[i]); + if (exception != null) + return null; + i++; + } + return elements; + } + default: + throw new BadImageFormatException(); + } + } + + private static Array TryParseConstantEnumArray(this ConstantEnumArrayHandle handle, MetadataReader reader, out Exception exception) + { + exception = null; + + ConstantEnumArray enumArray = handle.GetConstantEnumArray(reader); + Type elementType = enumArray.ElementType.TryResolve(reader, new TypeContext(null, null), ref exception); + if (exception != null) + return null; + + switch (enumArray.Value.HandleType) + { + case HandleType.ConstantByteArray: + return enumArray.Value.ToConstantByteArrayHandle(reader).GetConstantByteArray(reader).Value.ToArray(elementType); + + case HandleType.ConstantSByteArray: + return enumArray.Value.ToConstantSByteArrayHandle(reader).GetConstantSByteArray(reader).Value.ToArray(elementType); + + case HandleType.ConstantInt16Array: + return enumArray.Value.ToConstantInt16ArrayHandle(reader).GetConstantInt16Array(reader).Value.ToArray(elementType); + + case HandleType.ConstantUInt16Array: + return enumArray.Value.ToConstantUInt16ArrayHandle(reader).GetConstantUInt16Array(reader).Value.ToArray(elementType); + + case HandleType.ConstantInt32Array: + return enumArray.Value.ToConstantInt32ArrayHandle(reader).GetConstantInt32Array(reader).Value.ToArray(elementType); + + case HandleType.ConstantUInt32Array: + return enumArray.Value.ToConstantUInt32ArrayHandle(reader).GetConstantUInt32Array(reader).Value.ToArray(elementType); + + case HandleType.ConstantInt64Array: + return enumArray.Value.ToConstantInt64ArrayHandle(reader).GetConstantInt64Array(reader).Value.ToArray(elementType); + + case HandleType.ConstantUInt64Array: + return enumArray.Value.ToConstantUInt64ArrayHandle(reader).GetConstantUInt64Array(reader).Value.ToArray(elementType); + + default: + throw new BadImageFormatException(); + } + } + + public static Handle GetAttributeTypeHandle(this CustomAttribute customAttribute, + MetadataReader reader) + { + HandleType constructorHandleType = customAttribute.Constructor.HandleType; + + if (constructorHandleType == HandleType.QualifiedMethod) + return customAttribute.Constructor.ToQualifiedMethodHandle(reader).GetQualifiedMethod(reader).EnclosingType; + else if (constructorHandleType == HandleType.MemberReference) + return customAttribute.Constructor.ToMemberReferenceHandle(reader).GetMemberReference(reader).Parent; + else + throw new BadImageFormatException(); + } + + // + // Lightweight check to see if a custom attribute's is of a well-known type. + // + // This check performs without instantating the Type object and bloating memory usage. On the flip side, + // it doesn't check on whether the type is defined in a paricular assembly. The desktop CLR typically doesn't + // check this either so this is useful from a compat perspective as well. + // + public static bool IsCustomAttributeOfType(this CustomAttributeHandle customAttributeHandle, + MetadataReader reader, + string ns, + string name) + { + string[] namespaceParts = ns.Split('.'); + Handle typeHandle = customAttributeHandle.GetCustomAttribute(reader).GetAttributeTypeHandle(reader); + HandleType handleType = typeHandle.HandleType; + if (handleType == HandleType.TypeDefinition) + { + TypeDefinition typeDefinition = typeHandle.ToTypeDefinitionHandle(reader).GetTypeDefinition(reader); + if (!typeDefinition.Name.StringEquals(name, reader)) + return false; + NamespaceDefinitionHandle nsHandle = typeDefinition.NamespaceDefinition; + int idx = namespaceParts.Length; + while (idx-- != 0) + { + string namespacePart = namespaceParts[idx]; + NamespaceDefinition namespaceDefinition = nsHandle.GetNamespaceDefinition(reader); + if (!namespaceDefinition.Name.StringOrNullEquals(namespacePart, reader)) + return false; + if (!namespaceDefinition.ParentScopeOrNamespace.IsNamespaceDefinitionHandle(reader)) + return false; + nsHandle = namespaceDefinition.ParentScopeOrNamespace.ToNamespaceDefinitionHandle(reader); + } + if (!nsHandle.GetNamespaceDefinition(reader).Name.StringOrNullEquals(null, reader)) + return false; + return true; + } + else if (handleType == HandleType.TypeReference) + { + TypeReference typeReference = typeHandle.ToTypeReferenceHandle(reader).GetTypeReference(reader); + if (!typeReference.TypeName.StringEquals(name, reader)) + return false; + if (!typeReference.ParentNamespaceOrType.IsNamespaceReferenceHandle(reader)) + return false; + NamespaceReferenceHandle nsHandle = typeReference.ParentNamespaceOrType.ToNamespaceReferenceHandle(reader); + int idx = namespaceParts.Length; + while (idx-- != 0) + { + string namespacePart = namespaceParts[idx]; + NamespaceReference namespaceReference = nsHandle.GetNamespaceReference(reader); + if (!namespaceReference.Name.StringOrNullEquals(namespacePart, reader)) + return false; + if (!namespaceReference.ParentScopeOrNamespace.IsNamespaceReferenceHandle(reader)) + return false; + nsHandle = namespaceReference.ParentScopeOrNamespace.ToNamespaceReferenceHandle(reader); + } + if (!nsHandle.GetNamespaceReference(reader).Name.StringOrNullEquals(null, reader)) + return false; + return true; + } + else + throw new NotSupportedException(); + } + + + public static string ToNamespaceName(this NamespaceDefinitionHandle namespaceDefinitionHandle, MetadataReader reader) + { + string ns = ""; + for (;;) + { + NamespaceDefinition currentNamespaceDefinition = namespaceDefinitionHandle.GetNamespaceDefinition(reader); + string name = currentNamespaceDefinition.Name.GetStringOrNull(reader); + if (name != null) + { + if (ns.Length != 0) + ns = "." + ns; + ns = name + ns; + } + Handle nextHandle = currentNamespaceDefinition.ParentScopeOrNamespace; + HandleType handleType = nextHandle.HandleType; + if (handleType == HandleType.ScopeDefinition) + break; + if (handleType == HandleType.NamespaceDefinition) + { + namespaceDefinitionHandle = nextHandle.ToNamespaceDefinitionHandle(reader); + continue; + } + + throw new BadImageFormatException(SR.Bif_InvalidMetadata); + } + return ns; + } + + public static IEnumerable GetTransitiveNamespaces(this MetadataReader reader, IEnumerable namespaceHandles) + { + foreach (NamespaceDefinitionHandle namespaceHandle in namespaceHandles) + { + yield return namespaceHandle; + + NamespaceDefinition namespaceDefinition = namespaceHandle.GetNamespaceDefinition(reader); + foreach (NamespaceDefinitionHandle childNamespaceHandle in GetTransitiveNamespaces(reader, namespaceDefinition.NamespaceDefinitions.AsEnumerable())) + yield return childNamespaceHandle; + } + } + + public static IEnumerable GetTopLevelTypes(this MetadataReader reader, IEnumerable namespaceHandles) + { + foreach (NamespaceDefinitionHandle namespaceHandle in namespaceHandles) + { + NamespaceDefinition namespaceDefinition = namespaceHandle.GetNamespaceDefinition(reader); + foreach (TypeDefinitionHandle typeDefinitionHandle in namespaceDefinition.TypeDefinitions) + { + yield return typeDefinitionHandle; + } + } + } + + public static IEnumerable GetTransitiveTypes(this MetadataReader reader, IEnumerable typeDefinitionHandles, bool publicOnly) + { + foreach (TypeDefinitionHandle typeDefinitionHandle in typeDefinitionHandles) + { + TypeDefinition typeDefinition = typeDefinitionHandle.GetTypeDefinition(reader); + + if (publicOnly) + { + TypeAttributes visibility = typeDefinition.Flags & TypeAttributes.VisibilityMask; + if (visibility != TypeAttributes.Public && visibility != TypeAttributes.NestedPublic) + continue; + } + + yield return typeDefinitionHandle; + + foreach (TypeDefinitionHandle nestedTypeDefinitionHandle in GetTransitiveTypes(reader, typeDefinition.NestedTypes.AsEnumerable(), publicOnly)) + yield return nestedTypeDefinitionHandle; + } + } + + /// + /// Reverse len characters in a StringBuilder starting at offset index + /// + private static void ReverseStringInStringBuilder(StringBuilder builder, int index, int len) + { + int back = index + len - 1; + int front = index; + while (front < back) + { + char temp = builder[front]; + builder[front] = builder[back]; + builder[back] = temp; + front++; + back--; + } + } + + public static string ToFullyQualifiedTypeName(this NamespaceReferenceHandle namespaceReferenceHandle, string typeName, MetadataReader reader) + { + StringBuilder fullName = new StringBuilder(64); + NamespaceReference namespaceReference; + for (;;) + { + namespaceReference = namespaceReferenceHandle.GetNamespaceReference(reader); + string namespacePart = namespaceReference.Name.GetStringOrNull(reader); + if (namespacePart == null) + break; + fullName.Append('.'); + int index = fullName.Length; + fullName.Append(namespacePart); + ReverseStringInStringBuilder(fullName, index, namespacePart.Length); + namespaceReferenceHandle = namespaceReference.ParentScopeOrNamespace.ToExpectedNamespaceReferenceHandle(reader); + } + ReverseStringInStringBuilder(fullName, 0, fullName.Length); + fullName.Append(typeName); + return fullName.ToString(); + } + + public static IEnumerable AsEnumerable(this NamespaceDefinitionHandleCollection collection) + { + foreach (NamespaceDefinitionHandle handle in collection) + yield return handle; + } + + public static IEnumerable AsEnumerable(this TypeDefinitionHandleCollection collection) + { + foreach (TypeDefinitionHandle handle in collection) + yield return handle; + } + + public static Handle[] ToArray(this HandleCollection collection) + { + int count = collection.Count; + Handle[] result = new Handle[count]; + int i = 0; + foreach (Handle element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static bool[] ToArray(this BooleanCollection collection) + { + int count = collection.Count; + bool[] result = new bool[count]; + int i = 0; + foreach (bool element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static char[] ToArray(this CharCollection collection) + { + int count = collection.Count; + char[] result = new char[count]; + int i = 0; + foreach (char element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static float[] ToArray(this SingleCollection collection) + { + int count = collection.Count; + float[] result = new float[count]; + int i = 0; + foreach (float element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static double[] ToArray(this DoubleCollection collection) + { + int count = collection.Count; + double[] result = new double[count]; + int i = 0; + foreach (double element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "The compiler ensures we have array types referenced from custom attribute blobs")] + public static byte[] ToArray(this ByteCollection collection, Type enumType = null) + { + int count = collection.Count; + byte[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (byte[])Array.CreateInstance(enumType, count); + } + else + { + result = new byte[count]; + } + int i = 0; + foreach (byte element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "The compiler ensures we have array types referenced from custom attribute blobs")] + public static sbyte[] ToArray(this SByteCollection collection, Type enumType = null) + { + int count = collection.Count; + sbyte[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (sbyte[])Array.CreateInstance(enumType, count); + } + else + { + result = new sbyte[count]; + } + int i = 0; + foreach (sbyte element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "The compiler ensures we have array types referenced from custom attribute blobs")] + public static ushort[] ToArray(this UInt16Collection collection, Type enumType = null) + { + int count = collection.Count; + ushort[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (ushort[])Array.CreateInstance(enumType, count); + } + else + { + result = new ushort[count]; + } + int i = 0; + foreach (ushort element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "The compiler ensures we have array types referenced from custom attribute blobs")] + public static short[] ToArray(this Int16Collection collection, Type enumType = null) + { + int count = collection.Count; + short[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (short[])Array.CreateInstance(enumType, count); + } + else + { + result = new short[count]; + } + int i = 0; + foreach (short element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "The compiler ensures we have array types referenced from custom attribute blobs")] + public static uint[] ToArray(this UInt32Collection collection, Type enumType = null) + { + int count = collection.Count; + uint[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (uint[])Array.CreateInstance(enumType, count); + } + else + { + result = new uint[count]; + } + int i = 0; + foreach (uint element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "The compiler ensures we have array types referenced from custom attribute blobs")] + public static int[] ToArray(this Int32Collection collection, Type enumType = null) + { + int count = collection.Count; + int[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (int[])Array.CreateInstance(enumType, count); + } + else + { + result = new int[count]; + } + int i = 0; + foreach (int element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "The compiler ensures we have array types referenced from custom attribute blobs")] + public static ulong[] ToArray(this UInt64Collection collection, Type enumType = null) + { + int count = collection.Count; + ulong[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (ulong[])Array.CreateInstance(enumType, count); + } + else + { + result = new ulong[count]; + } + int i = 0; + foreach (ulong element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "The compiler ensures we have array types referenced from custom attribute blobs")] + public static long[] ToArray(this Int64Collection collection, Type enumType = null) + { + int count = collection.Count; + long[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (long[])Array.CreateInstance(enumType, count); + } + else + { + result = new long[count]; + } + int i = 0; + foreach (long element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NamespaceChain.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NamespaceChain.cs new file mode 100644 index 00000000000000..e76abd7d0c0265 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NamespaceChain.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; + +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.General +{ + // + // Since computation of the fullname and the declaring assembly both require walking up the namespace chain, + // cache both results the first time we walk the chain. + // + internal sealed class NamespaceChain + { + internal NamespaceChain(MetadataReader reader, NamespaceDefinitionHandle innerMostNamespaceHandle) + { + NamespaceDefinition currentNamespaceDefinition = innerMostNamespaceHandle.GetNamespaceDefinition(reader); + ConstantStringValueHandle currentNameHandle = currentNamespaceDefinition.Name; + Handle currentNamespaceHandle = innerMostNamespaceHandle.ToHandle(reader); + LowLevelList names = new LowLevelList(); + for (;;) + { + string name = currentNameHandle.GetStringOrNull(reader); + names.Add(name); + currentNamespaceHandle = currentNamespaceDefinition.ParentScopeOrNamespace; + HandleType handleType = currentNamespaceHandle.HandleType; + if (handleType == HandleType.ScopeDefinition) + break; + if (handleType == HandleType.NamespaceDefinition) + { + NamespaceDefinitionHandle nsHandle = currentNamespaceHandle.ToNamespaceDefinitionHandle(reader); + currentNamespaceDefinition = nsHandle.GetNamespaceDefinition(reader); + currentNameHandle = currentNamespaceDefinition.Name; + continue; + } + + throw new BadImageFormatException(SR.Bif_InvalidMetadata); + } + + DefiningScope = currentNamespaceHandle.ToScopeDefinitionHandle(reader); + + int count = names.Count; + if (count == 0) + { + // Every namespace chain has to start with the root namespace. + throw new BadImageFormatException(); + } + else if (count == 1) + { + // The root namespace. For compat with the desktop, TypeInfo.NameSpaces returns null in this case. + NameSpace = null; + } + else + { + // Namespace has at least one non-root component. + StringBuilder sb = new StringBuilder(); + int idx = count - 1; + while (idx-- != 0) + { + string name = names[idx]; + if (name == null) + throw new BadImageFormatException(); // null namespace fragment found in middle. + sb.Append(name); + if (idx != 0) + sb.Append('.'); + } + NameSpace = sb.ToString(); + } + } + + internal string NameSpace { get; } + internal ScopeDefinitionHandle DefiningScope { get; } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NativeFormat/DefaultValueParser.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NativeFormat/DefaultValueParser.cs new file mode 100644 index 00000000000000..0da7bd27a6fd29 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NativeFormat/DefaultValueParser.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.General.NativeFormat +{ + internal static class DefaultValueParser + { + public static bool GetDefaultValueIfAny(MetadataReader reader, Handle constantHandle, Type declaredType, IEnumerable customAttributes, bool raw, out object defaultValue) + { + if (!(constantHandle.IsNull(reader))) + { + defaultValue = constantHandle.ParseConstantValue(reader); + if ((!raw) && declaredType.IsEnum && defaultValue != null) + defaultValue = Enum.ToObject(declaredType, defaultValue); + return true; + } + + if (Helpers.GetCustomAttributeDefaultValueIfAny(customAttributes, raw, out defaultValue)) + return true; + + defaultValue = null; + return false; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NonOverriddenApis.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NonOverriddenApis.cs new file mode 100644 index 00000000000000..d0acc96a86196d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NonOverriddenApis.cs @@ -0,0 +1,215 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Why this file exists: +// +// Because the Reflection base types have so many overridable members, it becomes difficult to distinguish +// members we decided not to override vs. those we forgot to override. It would be nice if C# had a construct to +// tell the reader (and Intellisense) that we've made an explicit decision *not* to override an inherited member, +// but since it doesn't, we'll make do with this instead. +// +// In DEBUG builds, we'll add a base-delegating override so that it's clear we made an explicit decision +// to accept the base class's implementation. In RELEASE builds, we'll #if'd these out to avoid the extra metadata and runtime +// cost. That way, every overridable member is accounted for (i.e. the codebase should always be kept in a state +// where hitting "override" + SPACE never brings up additional suggestions in Intellisense.) +// +// To avoid introducing inadvertent inconsistencies between DEBUG and RELEASE behavior due to the fragile base class +// problem, only do this for public or protected members that already exist on the public api type. Since we know +// we'll never remove those members, we'll avoid the problem of "base" being compile-bound to something different +// from the runtime "base." +// + +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; +using System.Globalization; +using System.Collections.Generic; + +namespace System.Reflection.Runtime.Assemblies +{ + internal partial class RuntimeAssemblyInfo + { +#if DEBUG + [RequiresUnreferencedCode("Assembly.CreateInstance is not supported with trimming. Use Type.GetType instead.")] + public sealed override object CreateInstance(string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes) => base.CreateInstance(typeName, ignoreCase, bindingAttr, binder, args, culture, activationAttributes); + [RequiresUnreferencedCode("Types might be removed")] + public sealed override Type GetType(string name) => base.GetType(name); + [RequiresUnreferencedCode("Types might be removed")] + public sealed override Type GetType(string name, bool throwOnError) => base.GetType(name, throwOnError); + public sealed override bool IsDynamic => base.IsDynamic; + public sealed override string ToString() => base.ToString(); + [RequiresAssemblyFilesAttribute("The code will return an empty string for assemblies embedded in a single-file app")] + public sealed override string EscapedCodeBase => base.EscapedCodeBase; + [RequiresAssemblyFiles("The code will throw for assemblies embedded in a single-file app")] + public sealed override FileStream[] GetFiles() => base.GetFiles(); +#endif //DEBUG + } +} + +namespace System.Reflection.Runtime.MethodInfos +{ + internal abstract partial class RuntimeConstructorInfo + { +#if DEBUG + public sealed override MemberTypes MemberType => base.MemberType; +#endif //DEBUG + } +} + +namespace System.Reflection.Runtime.CustomAttributes +{ + internal abstract partial class RuntimeCustomAttributeData + { +#if DEBUG + public sealed override bool Equals(object obj) => base.Equals(obj); + public sealed override int GetHashCode() => base.GetHashCode(); +#endif //DEBUG + } +} + +namespace System.Reflection.Runtime.EventInfos +{ + internal abstract partial class RuntimeEventInfo + { +#if DEBUG + public sealed override MemberTypes MemberType => base.MemberType; + public sealed override bool IsMulticast => base.IsMulticast; + public sealed override void AddEventHandler(object target, Delegate handler) => base.AddEventHandler(target, handler); + public sealed override void RemoveEventHandler(object target, Delegate handler) => base.RemoveEventHandler(target, handler); +#endif //DEBUG + } +} + +namespace System.Reflection.Runtime.FieldInfos +{ + internal abstract partial class RuntimeFieldInfo + { +#if DEBUG + public sealed override MemberTypes MemberType => base.MemberType; + public sealed override bool IsSecurityCritical => base.IsSecurityCritical; + public sealed override bool IsSecuritySafeCritical => base.IsSecuritySafeCritical; + public sealed override bool IsSecurityTransparent => base.IsSecurityTransparent; +#endif //DEBUG + } +} + +namespace System.Reflection.Runtime.MethodInfos +{ + internal abstract partial class RuntimeMethodInfo + { +#if DEBUG + public sealed override MemberTypes MemberType => base.MemberType; +#endif //DEBUG + } +} + +namespace System.Reflection.Runtime.Modules +{ + internal abstract partial class RuntimeModule + { +#if DEBUG + [RequiresUnreferencedCode("Types might be removed")] + public sealed override Type[] FindTypes(TypeFilter filter, object filterCriteria) => base.FindTypes(filter, filterCriteria); + [RequiresUnreferencedCode("Types might be removed")] + public sealed override Type GetType(string className) => base.GetType(className); + [RequiresUnreferencedCode("Types might be removed")] + public sealed override Type GetType(string className, bool ignoreCase) => base.GetType(className, ignoreCase); + public sealed override string ToString() => base.ToString(); +#endif //DEBUG + } +} + +namespace System.Reflection.Runtime.ParameterInfos +{ + internal abstract partial class RuntimeParameterInfo + { +#if DEBUG +#endif //DEBUG + } +} + +namespace System.Reflection.Runtime.PropertyInfos +{ + internal abstract partial class RuntimePropertyInfo + { +#if DEBUG + public sealed override MemberTypes MemberType => base.MemberType; + public sealed override object GetValue(object obj, object[] index) => base.GetValue(obj, index); + public sealed override void SetValue(object obj, object value, object[] index) => base.SetValue(obj, value, index); +#endif //DEBUG + } +} + +namespace System.Reflection.Runtime.TypeInfos +{ + internal abstract partial class RuntimeTypeInfo + { +#if DEBUG + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public sealed override Type[] FindInterfaces(TypeFilter filter, object filterCriteria) => base.FindInterfaces(filter, filterCriteria); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public sealed override MemberInfo[] FindMembers(MemberTypes memberType, BindingFlags bindingAttr, MemberFilter filter, object filterCriteria) => base.FindMembers(memberType, bindingAttr, filter, filterCriteria); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents)] + public sealed override EventInfo[] GetEvents() => base.GetEvents(); + protected sealed override bool IsContextfulImpl() => base.IsContextfulImpl(); + public sealed override bool IsSubclassOf(Type c) => base.IsSubclassOf(c); + protected sealed override bool IsMarshalByRefImpl() => base.IsMarshalByRefImpl(); + public sealed override bool IsInstanceOfType(object o) => base.IsInstanceOfType(o); + public sealed override bool IsSerializable => base.IsSerializable; + public sealed override bool IsEquivalentTo(Type other) => base.IsEquivalentTo(other); // Note: If we enable COM type equivalence, this is no longer the correct implementation. + public sealed override bool IsSignatureType => base.IsSignatureType; + + public sealed override IEnumerable DeclaredConstructors + { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + get => base.DeclaredConstructors; + } + public sealed override IEnumerable DeclaredEvents + { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + get => base.DeclaredEvents; + } + public sealed override IEnumerable DeclaredFields + { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + get => base.DeclaredFields; + } + public sealed override IEnumerable DeclaredMembers + { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + get => base.DeclaredMembers; + } + public sealed override IEnumerable DeclaredMethods + { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + get => base.DeclaredMethods; + } + public sealed override IEnumerable DeclaredNestedTypes + { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + get => base.DeclaredNestedTypes; + } + public sealed override IEnumerable DeclaredProperties + { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + get => base.DeclaredProperties; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public sealed override EventInfo GetDeclaredEvent(string name) => base.GetDeclaredEvent(name); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public sealed override FieldInfo GetDeclaredField(string name) => base.GetDeclaredField(name); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public sealed override MethodInfo GetDeclaredMethod(string name) => base.GetDeclaredMethod(name); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public sealed override TypeInfo GetDeclaredNestedType(string name) => base.GetDeclaredNestedType(name); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + public sealed override PropertyInfo GetDeclaredProperty(string name) => base.GetDeclaredProperty(name); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public sealed override IEnumerable GetDeclaredMethods(string name) => base.GetDeclaredMethods(name); +#endif //DEBUG + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.NativeFormat.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.NativeFormat.cs new file mode 100644 index 00000000000000..3d2e8c5f601882 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.NativeFormat.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Collection of "qualified handle" tuples. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Metadata.NativeFormat; +using Internal.Runtime.TypeLoader; + +namespace System.Reflection.Runtime.General +{ + public partial struct QSignatureTypeHandle + { + public QSignatureTypeHandle(MetadataReader reader, Handle handle, bool skipCheck = false) + { + if (!skipCheck) + { + if (!handle.IsTypeDefRefSpecOrModifiedTypeHandle(reader)) + throw new BadImageFormatException(); + } + + Debug.Assert(handle.IsTypeDefRefSpecOrModifiedTypeHandle(reader)); + _reader = reader; + _handle = handle; + +#if ECMA_METADATA_SUPPORT + _blobReader = default(global::System.Reflection.Metadata.BlobReader); +#endif + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.cs new file mode 100644 index 00000000000000..b1307eea2f27a5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Collection of "qualified handle" tuples. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection.Runtime.TypeInfos; + +namespace System.Reflection.Runtime.General +{ + public partial struct QSignatureTypeHandle + { + public object Reader { get { return _reader; } } + private object _reader; +#if ECMA_METADATA_SUPPORT + private readonly global::System.Reflection.Metadata.BlobReader _blobReader; +#endif + private global::Internal.Metadata.NativeFormat.Handle _handle; + + internal RuntimeTypeInfo Resolve(TypeContext typeContext) + { + Exception exception = null; + RuntimeTypeInfo runtimeType = TryResolve(typeContext, ref exception); + if (runtimeType == null) + throw exception; + return runtimeType; + } + + internal RuntimeTypeInfo TryResolve(TypeContext typeContext, ref Exception exception) + { + if (Reader is global::Internal.Metadata.NativeFormat.MetadataReader) + { + return _handle.TryResolve((global::Internal.Metadata.NativeFormat.MetadataReader)Reader, typeContext, ref exception); + } + +#if ECMA_METADATA_SUPPORT + if (Reader is global::System.Reflection.Metadata.MetadataReader ecmaReader) + { + return TryResolveSignature(typeContext, ref exception); + } +#endif + + throw new BadImageFormatException(); // Expected TypeRef, Def or Spec with MetadataReader + } + + // Return any custom modifiers modifying the passed-in type and whose required/optional bit matches the passed in boolean. + // Because this is intended to service the GetCustomModifiers() apis, this helper will always return a freshly allocated array + // safe for returning to api callers. + internal Type[] GetCustomModifiers(TypeContext typeContext, bool optional) + { +#if ECMA_METADATA_SUPPORT + throw new NotImplementedException(); +#else + return _handle.GetCustomModifiers((global::Internal.Metadata.NativeFormat.MetadataReader)Reader, typeContext, optional); +#endif + } + + // + // This is a port of the desktop CLR's RuntimeType.FormatTypeName() routine. This routine is used by various Reflection ToString() methods + // to display the name of a type. Do not use for any other purpose as it inherits some pretty quirky desktop behavior. + // + internal string FormatTypeName(TypeContext typeContext) + { + try + { + // Though we wrap this in a try-catch as a failsafe, this code must still strive to avoid triggering MissingMetadata exceptions + // (non-error exceptions are very annoying when debugging.) + + Exception exception = null; + RuntimeTypeInfo runtimeType = TryResolve(typeContext, ref exception); + if (runtimeType == null) + return Type.DefaultTypeNameWhenMissingMetadata; + + // Because this runtimeType came from a successful TryResolve() call, it is safe to querying the TypeInfo's of the type and its component parts. + // If we're wrong, we do have the safety net of a try-catch. + return runtimeType.FormatTypeNameForReflection(); + } + catch (Exception) + { + return Type.DefaultTypeNameWhenMissingMetadata; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs new file mode 100644 index 00000000000000..df5587374683db --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs @@ -0,0 +1,408 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeInfos.NativeFormat; +using System.Reflection.Runtime.Assemblies; +using System.Reflection.Runtime.FieldInfos; +using System.Reflection.Runtime.FieldInfos.NativeFormat; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.BindingFlagSupport; +using System.Reflection.Runtime.Modules; + +using Internal.Runtime.Augments; +using Internal.Reflection.Augments; +using Internal.Reflection.Core.Execution; +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.General +{ + internal sealed class ReflectionCoreCallbacksImplementation : ReflectionCoreCallbacks + { + internal ReflectionCoreCallbacksImplementation() + { + } + + public sealed override Assembly Load(AssemblyName assemblyRef, bool throwOnFileNotFound) + { + if (assemblyRef == null) + throw new ArgumentNullException(nameof(assemblyRef)); + if (throwOnFileNotFound) + return RuntimeAssemblyInfo.GetRuntimeAssembly(assemblyRef.ToRuntimeAssemblyName()); + else + return RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(assemblyRef.ToRuntimeAssemblyName()); + } + + public sealed override Assembly Load(byte[] rawAssembly, byte[] pdbSymbolStore) + { + if (rawAssembly == null) + throw new ArgumentNullException(nameof(rawAssembly)); + + return RuntimeAssemblyInfo.GetRuntimeAssemblyFromByteArray(rawAssembly, pdbSymbolStore); + } + + public sealed override Assembly Load(string assemblyPath) + { + if (assemblyPath == null) + throw new ArgumentNullException(nameof(assemblyPath)); + + return RuntimeAssemblyInfo.GetRuntimeAssemblyFromPath(assemblyPath); + } + + // + // This overload of GetMethodForHandle only accepts handles for methods declared on non-generic types (the method, however, + // can be an instance of a generic method.) To resolve handles for methods declared on generic types, you must pass + // the declaring type explicitly using the two-argument overload of GetMethodFromHandle. + // + // This is a vestige from desktop generic sharing that got itself enshrined in the code generated by the C# compiler for Linq Expressions. + // + public sealed override MethodBase GetMethodFromHandle(RuntimeMethodHandle runtimeMethodHandle) + { + ExecutionEnvironment executionEnvironment = ReflectionCoreExecution.ExecutionEnvironment; + QMethodDefinition methodHandle; + RuntimeTypeHandle declaringTypeHandle; + RuntimeTypeHandle[] genericMethodTypeArgumentHandles; + if (!executionEnvironment.TryGetMethodFromHandle(runtimeMethodHandle, out declaringTypeHandle, out methodHandle, out genericMethodTypeArgumentHandles)) + throw new ArgumentException(SR.Argument_InvalidHandle); + + MethodBase methodBase = ReflectionCoreExecution.ExecutionDomain.GetMethod(declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles); + if (methodBase.DeclaringType.IsConstructedGenericType) // For compat with desktop, insist that the caller pass us the declaring type to resolve members of generic types. + throw new ArgumentException(SR.Format(SR.Argument_MethodDeclaringTypeGeneric, methodBase)); + return methodBase; + } + + // + // This overload of GetMethodHandle can handle all method handles. + // + public sealed override MethodBase GetMethodFromHandle(RuntimeMethodHandle runtimeMethodHandle, RuntimeTypeHandle declaringTypeHandle) + { + ExecutionEnvironment executionEnvironment = ReflectionCoreExecution.ExecutionEnvironment; + QMethodDefinition methodHandle; + RuntimeTypeHandle[] genericMethodTypeArgumentHandles; + if (!executionEnvironment.TryGetMethodFromHandleAndType(runtimeMethodHandle, declaringTypeHandle, out methodHandle, out genericMethodTypeArgumentHandles)) + { + // This may be a method declared on a non-generic type: this api accepts that too so try the other table. + RuntimeTypeHandle actualDeclaringTypeHandle; + if (!executionEnvironment.TryGetMethodFromHandle(runtimeMethodHandle, out actualDeclaringTypeHandle, out methodHandle, out genericMethodTypeArgumentHandles)) + throw new ArgumentException(SR.Argument_InvalidHandle); + if (!actualDeclaringTypeHandle.Equals(declaringTypeHandle)) + throw new ArgumentException(SR.Format(SR.Argument_ResolveMethodHandle, + declaringTypeHandle.GetTypeForRuntimeTypeHandle(), + actualDeclaringTypeHandle.GetTypeForRuntimeTypeHandle())); + } + + MethodBase methodBase = ReflectionCoreExecution.ExecutionDomain.GetMethod(declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles); + return methodBase; + } + + // + // This overload of GetFieldForHandle only accepts handles for fields declared on non-generic types. To resolve handles for fields + // declared on generic types, you must pass the declaring type explicitly using the two-argument overload of GetFieldFromHandle. + // + // This is a vestige from desktop generic sharing that got itself enshrined in the code generated by the C# compiler for Linq Expressions. + // + public sealed override FieldInfo GetFieldFromHandle(RuntimeFieldHandle runtimeFieldHandle) + { + ExecutionEnvironment executionEnvironment = ReflectionCoreExecution.ExecutionEnvironment; + FieldHandle fieldHandle; + RuntimeTypeHandle declaringTypeHandle; + if (!executionEnvironment.TryGetFieldFromHandle(runtimeFieldHandle, out declaringTypeHandle, out fieldHandle)) + throw new ArgumentException(SR.Argument_InvalidHandle); + + FieldInfo fieldInfo = GetFieldInfo(declaringTypeHandle, fieldHandle); + if (fieldInfo.DeclaringType.IsConstructedGenericType) // For compat with desktop, insist that the caller pass us the declaring type to resolve members of generic types. + throw new ArgumentException(SR.Format(SR.Argument_FieldDeclaringTypeGeneric, fieldInfo)); + return fieldInfo; + } + + // + // This overload of GetFieldHandle can handle all field handles. + // + public sealed override FieldInfo GetFieldFromHandle(RuntimeFieldHandle runtimeFieldHandle, RuntimeTypeHandle declaringTypeHandle) + { + ExecutionEnvironment executionEnvironment = ReflectionCoreExecution.ExecutionEnvironment; + FieldHandle fieldHandle; + if (!executionEnvironment.TryGetFieldFromHandleAndType(runtimeFieldHandle, declaringTypeHandle, out fieldHandle)) + { + // This may be a field declared on a non-generic type: this api accepts that too so try the other table. + RuntimeTypeHandle actualDeclaringTypeHandle; + if (!executionEnvironment.TryGetFieldFromHandle(runtimeFieldHandle, out actualDeclaringTypeHandle, out fieldHandle)) + throw new ArgumentException(SR.Argument_InvalidHandle); + if (!actualDeclaringTypeHandle.Equals(declaringTypeHandle)) + throw new ArgumentException(SR.Format(SR.Argument_ResolveFieldHandle, + declaringTypeHandle.GetTypeForRuntimeTypeHandle(), + actualDeclaringTypeHandle.GetTypeForRuntimeTypeHandle())); + } + + FieldInfo fieldInfo = GetFieldInfo(declaringTypeHandle, fieldHandle); + return fieldInfo; + } + + public sealed override EventInfo GetImplicitlyOverriddenBaseClassEvent(EventInfo e) + { + return e.GetImplicitlyOverriddenBaseClassMember(); + } + + public sealed override MethodInfo GetImplicitlyOverriddenBaseClassMethod(MethodInfo m) + { + return m.GetImplicitlyOverriddenBaseClassMember(); + } + + public sealed override PropertyInfo GetImplicitlyOverriddenBaseClassProperty(PropertyInfo p) + { + return p.GetImplicitlyOverriddenBaseClassMember(); + } + + private FieldInfo GetFieldInfo(RuntimeTypeHandle declaringTypeHandle, FieldHandle fieldHandle) + { + RuntimeTypeInfo contextTypeInfo = declaringTypeHandle.GetTypeForRuntimeTypeHandle(); + NativeFormatRuntimeNamedTypeInfo definingTypeInfo = contextTypeInfo.AnchoringTypeDefinitionForDeclaredMembers.CastToNativeFormatRuntimeNamedTypeInfo(); + MetadataReader reader = definingTypeInfo.Reader; + + // RuntimeFieldHandles always yield FieldInfo's whose ReflectedType equals the DeclaringType. + RuntimeTypeInfo reflectedType = contextTypeInfo; + return NativeFormatRuntimeFieldInfo.GetRuntimeFieldInfo(fieldHandle, definingTypeInfo, contextTypeInfo, reflectedType); + } + + [DebuggerHidden] + [DebuggerStepThrough] + public sealed override object ActivatorCreateInstance( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type, bool nonPublic) + { + return ActivatorImplementation.CreateInstance(type, nonPublic); + } + + [DebuggerHidden] + [DebuggerStepThrough] + public sealed override object ActivatorCreateInstance( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type, BindingFlags bindingAttr, Binder? binder, object[]? args, CultureInfo? culture, object[]? activationAttributes) + { + return ActivatorImplementation.CreateInstance(type, bindingAttr, binder, args, culture, activationAttributes); + } + + // V2 api: Creates open or closed delegates to static or instance methods - relaxed signature checking allowed. + public sealed override Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure) + { + return CreateDelegateWorker(type, firstArgument, method, throwOnBindFailure, allowClosed: true); + } + + // V1 api: Creates open delegates to static or instance methods - relaxed signature checking allowed. + public sealed override Delegate CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure) + { + // This API existed in v1/v1.1 and only expected to create open + // instance delegates, so we forbid closed delegates for backward compatibility. + // But we'll allow relaxed signature checking and open static delegates because + // there's no ambiguity there (the caller would have to explicitly + // pass us a static method or a method with a non-exact signature + // and the only change in behavior from v1.1 there is that we won't + // fail the call). + return CreateDelegateWorker(type, null, method, throwOnBindFailure, allowClosed: false); + } + + private static Delegate CreateDelegateWorker(Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure, bool allowClosed) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + if (method == null) + throw new ArgumentNullException(nameof(method)); + + if (!(type is RuntimeTypeInfo runtimeDelegateType)) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + + if (!(method is RuntimeMethodInfo runtimeMethodInfo)) + throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, nameof(method)); + + if (!runtimeDelegateType.IsDelegate) + throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); + + Delegate result = runtimeMethodInfo.CreateDelegateNoThrowOnBindFailure(runtimeDelegateType, firstArgument, allowClosed); + if (result == null) + { + if (throwOnBindFailure) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + return null; + } + return result; + } + + // V1 api: Creates closed delegates to instance methods only, relaxed signature checking disallowed. + [RequiresUnreferencedCode("The target method might be removed")] + public sealed override Delegate CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + if (target == null) + throw new ArgumentNullException(nameof(target)); + if (method == null) + throw new ArgumentNullException(nameof(method)); + + if (!(type is RuntimeTypeInfo runtimeDelegateType)) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + if (!runtimeDelegateType.IsDelegate) + throw new ArgumentException(SR.Arg_MustBeDelegate); + + RuntimeTypeInfo runtimeContainingType = target.GetType().CastToRuntimeTypeInfo(); + RuntimeMethodInfo runtimeMethodInfo = LookupMethodForCreateDelegate(runtimeDelegateType, runtimeContainingType, method, isStatic: false, ignoreCase: ignoreCase); + if (runtimeMethodInfo == null) + { + if (throwOnBindFailure) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + return null; + } + return runtimeMethodInfo.CreateDelegateWithoutSignatureValidation(type, target, isStatic: false, isOpen: false); + } + + // V1 api: Creates open delegates to static methods only, relaxed signature checking disallowed. + public sealed override Delegate CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method, bool ignoreCase, bool throwOnBindFailure) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + if (target == null) + throw new ArgumentNullException(nameof(target)); + if (target.ContainsGenericParameters) + throw new ArgumentException(SR.Arg_UnboundGenParam, nameof(target)); + if (method == null) + throw new ArgumentNullException(nameof(method)); + + if (!(type is RuntimeTypeInfo runtimeDelegateType)) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + + if (!(target is RuntimeTypeInfo runtimeContainingType)) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(target)); + + if (!runtimeDelegateType.IsDelegate) + throw new ArgumentException(SR.Arg_MustBeDelegate); + + RuntimeMethodInfo runtimeMethodInfo = LookupMethodForCreateDelegate(runtimeDelegateType, runtimeContainingType, method, isStatic: true, ignoreCase: ignoreCase); + if (runtimeMethodInfo == null) + { + if (throwOnBindFailure) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + return null; + } + return runtimeMethodInfo.CreateDelegateWithoutSignatureValidation(type, target: null, isStatic: true, isOpen: true); + } + + // + // Helper for the V1/V1.1 Delegate.CreateDelegate() api. These apis take method names rather than MethodInfo and only expect to create open static delegates + // or closed instance delegates. For backward compatibility, they don't allow relaxed signature matching (which could make the choice of target method ambiguous.) + // + private static RuntimeMethodInfo LookupMethodForCreateDelegate(RuntimeTypeInfo runtimeDelegateType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] RuntimeTypeInfo containingType, string method, bool isStatic, bool ignoreCase) + { + Debug.Assert(runtimeDelegateType.IsDelegate); + + BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.ExactBinding; + if (isStatic) + { + bindingFlags |= BindingFlags.Static; + } + else + { + bindingFlags |= BindingFlags.Instance | BindingFlags.DeclaredOnly; + } + if (ignoreCase) + { + bindingFlags |= BindingFlags.IgnoreCase; + } + RuntimeMethodInfo invokeMethod = runtimeDelegateType.GetInvokeMethod(); + ParameterInfo[] parameters = invokeMethod.GetParametersNoCopy(); + int numParameters = parameters.Length; + Type[] parameterTypes = new Type[numParameters]; + for (int i = 0; i < numParameters; i++) + { + parameterTypes[i] = parameters[i].ParameterType; + } + + while (containingType != null) + { + MethodInfo methodInfo = containingType.GetMethod(method, 0, bindingFlags, null, parameterTypes, null); + if (methodInfo != null && methodInfo.ReturnType.Equals(invokeMethod.ReturnType)) + return (RuntimeMethodInfo)methodInfo; // This cast is safe since we already verified that containingType is runtime implemented. + + containingType = (RuntimeTypeInfo)(containingType.BaseType); + } + return null; + } + +#if FEATURE_COMINTEROP + public sealed override Type GetTypeFromCLSID(Guid clsid, string server, bool throwOnError) + { + // Note: "throwOnError" is a vacuous parameter. Any errors due to the CLSID not being registered or the server not being found will happen + // on the Activator.CreateInstance() call. GetTypeFromCLSID() merely wraps the data in a Type object without any validation. + return RuntimeCLSIDTypeInfo.GetRuntimeCLSIDTypeInfo(clsid, server); + } +#endif + + public sealed override IntPtr GetFunctionPointer(RuntimeMethodHandle runtimeMethodHandle, RuntimeTypeHandle declaringTypeHandle) + { + MethodBase method = GetMethodFromHandle(runtimeMethodHandle, declaringTypeHandle); + + switch (method) + { + case RuntimeMethodInfo methodInfo: + return methodInfo.LdFtnResult; + case RuntimeConstructorInfo constructorInfo: + return constructorInfo.LdFtnResult; + default: + Debug.Fail("RuntimeMethodHandle should only return a methodbase implemented by the runtime."); + throw new NotSupportedException(); + } + } + + public sealed override void RunModuleConstructor(Module module) + { + RuntimeAssemblyInfo assembly = (RuntimeAssemblyInfo)module.Assembly; + assembly.RunModuleConstructor(); + } + + public sealed override void MakeTypedReference(object target, FieldInfo[] flds, out Type type, out int offset) + { + if (target == null) + throw new ArgumentNullException(nameof(target)); + if (flds == null) + throw new ArgumentNullException(nameof(flds)); + if (flds.Length == 0) + throw new ArgumentException(SR.Arg_ArrayZeroError, nameof(flds)); + + offset = 0; + Type targetType = target.GetType(); + for (int i = 0; i < flds.Length; i++) + { + if (!(flds[i] is RuntimeFieldInfo field)) + throw new ArgumentException(SR.Argument_MustBeRuntimeFieldInfo); + if (field.IsStatic) + throw new ArgumentException(SR.Argument_TypedReferenceInvalidField); + + // For proper handling of Nullable don't change to something like 'IsAssignableFrom' + // Currently we can't make a TypedReference to fields of Nullable, which is fine. + Type declaringType = field.DeclaringType; + if (targetType != declaringType && !targetType.IsSubclassOf(declaringType)) + throw new MissingMemberException(SR.MissingMemberTypeRef); // MissingMemberException is a strange exception to throw, but it is the compatible exception. + + Type fieldType = field.FieldType; + if (fieldType.IsPrimitive) + throw new ArgumentException(SR.Arg_TypeRefPrimitve); // This check exists for compatibility (why such an ad-hoc restriction?) + if (i < (flds.Length - 1) && !fieldType.IsValueType) + throw new MissingMemberException(SR.MissingMemberNestErr); // MissingMemberException is a strange exception to throw, but it is the compatible exception. + + targetType = fieldType; + offset = checked(offset + field.Offset); + } + + type = targetType; + } + + public sealed override Assembly[] GetLoadedAssemblies() => RuntimeAssemblyInfo.GetLoadedAssemblies(); + + public sealed override EnumInfo GetEnumInfo(Type type) => type.CastToRuntimeTypeInfo().EnumInfo; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/RuntimeTypeHandleKey.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/RuntimeTypeHandleKey.cs new file mode 100644 index 00000000000000..620fd3d6361614 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/RuntimeTypeHandleKey.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; + +namespace System.Reflection.Runtime.General +{ + internal struct RuntimeTypeHandleKey : IEquatable + { + public RuntimeTypeHandleKey(RuntimeTypeHandle typeHandle) + { + TypeHandle = typeHandle; + } + + public RuntimeTypeHandle TypeHandle { get; } + + public override bool Equals(object obj) + { + if (!(obj is RuntimeTypeHandleKey other)) + return false; + return Equals(other); + } + + public bool Equals(RuntimeTypeHandleKey other) + { + return TypeHandle.Equals(other.TypeHandle); + } + + public override int GetHashCode() + { + return TypeHandle.GetHashCode(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ThunkedApis.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ThunkedApis.cs new file mode 100644 index 00000000000000..7a50b6f3b19ae7 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ThunkedApis.cs @@ -0,0 +1,230 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// The Reflection stack has grown a large legacy of apis that thunk others. +// Apis that do little more than wrap another api will be kept here to +// keep the main files less cluttered. +// + +using System.IO; +using System.Text; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection.Runtime.General; + +using Internal.LowLevelLinq; + +namespace System.Reflection.Runtime.Assemblies +{ + internal partial class RuntimeAssemblyInfo + { + [RequiresUnreferencedCode("Types might be removed")] + public sealed override Type[] GetExportedTypes() => ExportedTypes.ToArray(); + public sealed override Module[] GetLoadedModules(bool getResourceModules) => Modules.ToArray(); + public sealed override Module[] GetModules(bool getResourceModules) => Modules.ToArray(); + [RequiresUnreferencedCode("Types might be removed")] + public sealed override Type[] GetTypes() => DefinedTypes.ToArray(); + + // "copiedName" only affects whether CodeBase is set to the assembly location before or after the shadow-copy. + // That concept is meaningless on .NET Native. + public sealed override AssemblyName GetName(bool copiedName) => GetName(); + + public sealed override Stream GetManifestResourceStream(Type type, string name) + { + StringBuilder sb = new StringBuilder(); + if (type == null) + { + if (name == null) + throw new ArgumentNullException(nameof(type)); + } + else + { + string nameSpace = type.Namespace; + if (nameSpace != null) + { + sb.Append(nameSpace); + if (name != null) + { + sb.Append(Type.Delimiter); + } + } + } + + if (name != null) + { + sb.Append(name); + } + + return GetManifestResourceStream(sb.ToString()); + } + + public override string Location + { + get + { + return string.Empty; + } + } + + [RequiresAssemblyFiles("The code will throw for assemblies embedded in a single-file app")] + public sealed override string CodeBase + { + get + { + throw new NotSupportedException(SR.NotSupported_CodeBase); + } + } + + public sealed override Assembly GetSatelliteAssembly(CultureInfo culture) { throw new PlatformNotSupportedException(); } + public sealed override Assembly GetSatelliteAssembly(CultureInfo culture, Version version) { throw new PlatformNotSupportedException(); } + + [RequiresUnreferencedCode("Assembly references might be removed")] + public sealed override AssemblyName[] GetReferencedAssemblies() { throw new PlatformNotSupportedException(); } + public sealed override Module GetModule(string name) { throw new PlatformNotSupportedException(); } + } +} + +namespace System.Reflection.Runtime.MethodInfos +{ + internal abstract partial class RuntimeConstructorInfo + { + public sealed override MethodImplAttributes GetMethodImplementationFlags() => MethodImplementationFlags; + + // Partial trust doesn't exist in Aot so these legacy apis are meaningless. Will report everything as SecurityCritical by fiat. + public sealed override bool IsSecurityCritical => true; + public sealed override bool IsSecuritySafeCritical => false; + public sealed override bool IsSecurityTransparent => false; + } +} + +namespace System.Reflection.Runtime.EventInfos +{ + internal abstract partial class RuntimeEventInfo + { + public sealed override MethodInfo GetAddMethod(bool nonPublic) => AddMethod.FilterAccessor(nonPublic); + public sealed override MethodInfo GetRemoveMethod(bool nonPublic) => RemoveMethod.FilterAccessor(nonPublic); + public sealed override MethodInfo GetRaiseMethod(bool nonPublic) => RaiseMethod?.FilterAccessor(nonPublic); + } +} + +namespace System.Reflection.Runtime.MethodInfos +{ + internal abstract partial class RuntimeMethodInfo + { + public sealed override MethodImplAttributes GetMethodImplementationFlags() => MethodImplementationFlags; + public sealed override ICustomAttributeProvider ReturnTypeCustomAttributes => ReturnParameter; + + // Partial trust doesn't exist in Aot so these legacy apis are meaningless. Will report everything as SecurityCritical by fiat. + public sealed override bool IsSecurityCritical => true; + public sealed override bool IsSecuritySafeCritical => false; + public sealed override bool IsSecurityTransparent => false; + } +} + +namespace System.Reflection.Runtime.PropertyInfos +{ + internal abstract partial class RuntimePropertyInfo + { + public sealed override MethodInfo GetGetMethod(bool nonPublic) => Getter?.FilterAccessor(nonPublic); + public sealed override MethodInfo GetSetMethod(bool nonPublic) => Setter?.FilterAccessor(nonPublic); + public sealed override MethodInfo[] GetAccessors(bool nonPublic) + { + MethodInfo getter = GetGetMethod(nonPublic); + MethodInfo setter = GetSetMethod(nonPublic); + int count = 0; + if (getter != null) + count++; + if (setter != null) + count++; + MethodInfo[] accessors = new MethodInfo[count]; + int index = 0; + if (getter != null) + accessors[index++] = getter; + if (setter != null) + accessors[index++] = setter; + return accessors; + } + } +} + +namespace System.Reflection.Runtime.TypeInfos +{ + internal abstract partial class RuntimeTypeInfo + { + public sealed override Type[] GetGenericArguments() + { + if (IsConstructedGenericType) + return GenericTypeArguments; + if (IsGenericTypeDefinition) + return GenericTypeParameters; + return Array.Empty(); + } + + public sealed override bool IsGenericType => IsConstructedGenericType || IsGenericTypeDefinition; + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public sealed override Type[] GetInterfaces() => ImplementedInterfaces.ToArray(); + + // Partial trust doesn't exist in Aot so these legacy apis are meaningless. Will report everything as SecurityCritical by fiat. + public sealed override bool IsSecurityCritical => true; + public sealed override bool IsSecuritySafeCritical => false; + public sealed override bool IsSecurityTransparent => false; + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2073:UnrecognizedReflectionPattern", + Justification = "The returned interface is one of the interfaces implemented by this type and does have DynamicallyAccessedMemberTypes.Interfaces")] + public sealed override Type GetInterface(string name, bool ignoreCase) + { + if (name == null) + throw new ArgumentNullException("fullname" /* Yep, CoreCLR names this different than the ref assembly */); + + string simpleName; + string ns; + SplitTypeName(name, out simpleName, out ns); + + Type match = null; + foreach (Type ifc in ImplementedInterfaces) + { + string ifcSimpleName = ifc.Name; + bool simpleNameMatches = ignoreCase + ? (0 == CultureInfo.InvariantCulture.CompareInfo.Compare(simpleName, ifcSimpleName, CompareOptions.IgnoreCase)) // @todo: This could be expressed simpler but the necessary parts of String api not yet ported. + : simpleName.Equals(ifcSimpleName); + if (!simpleNameMatches) + continue; + + // This check exists for desktop compat: + // (1) caller can optionally omit namespace part of name in pattern- we'll still match. + // (2) ignoreCase:true does not apply to the namespace portion. + if (ns != null && !ns.Equals(ifc.Namespace)) + continue; + if (match != null) + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + match = ifc; + } + return match; + } + + private static void SplitTypeName(string fullname, out string name, out string ns) + { + Debug.Assert(fullname != null); + + // Get namespace + int nsDelimiter = fullname.LastIndexOf(".", StringComparison.Ordinal); + if (nsDelimiter != -1) + { + ns = fullname.Substring(0, nsDelimiter); + int nameLength = fullname.Length - ns.Length - 1; + name = fullname.Substring(nsDelimiter + 1, nameLength); + Debug.Assert(fullname.Equals(ns + "." + name)); + } + else + { + ns = null; + name = fullname; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ToStringUtils.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ToStringUtils.cs new file mode 100644 index 00000000000000..9fe84bac777aee --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ToStringUtils.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection.Runtime.TypeInfos; +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.General +{ + internal static class ToStringUtils + { + // + // This is a port of the desktop CLR's RuntimeType.FormatTypeName() routine. This routine is used by various Reflection ToString() methods + // to display the name of a type. Do not use for any other purpose as it inherits some pretty quirky desktop behavior. + // + // The Project N version takes a raw metadata handle rather than a completed type so that it remains robust in the face of missing metadata. + // + public static string FormatTypeName(this QTypeDefRefOrSpec qualifiedTypeHandle, TypeContext typeContext) + { + try + { + // Though we wrap this in a try-catch as a failsafe, this code must still strive to avoid triggering MissingMetadata exceptions + // (non-error exceptions are very annoying when debugging.) + + Exception exception = null; + RuntimeTypeInfo runtimeType = qualifiedTypeHandle.TryResolve(typeContext, ref exception); + if (runtimeType == null) + return Type.DefaultTypeNameWhenMissingMetadata; + + // Because this runtimeType came from a successful TryResolve() call, it is safe to querying the TypeInfo's of the type and its component parts. + // If we're wrong, we do have the safety net of a try-catch. + return runtimeType.FormatTypeNameForReflection(); + } + catch (Exception) + { + return Type.DefaultTypeNameWhenMissingMetadata; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeContext.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeContext.cs new file mode 100644 index 00000000000000..c38ce5e5cfced7 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeContext.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; + +namespace System.Reflection.Runtime.General +{ + // + // Passed as an argument to code that parses signatures or typespecs. Specifies the subsitution values for ET_VAR and ET_MVAR elements inside the signature. + // Both may be null if no generic parameters are expected. + // + + internal struct TypeContext + { + internal TypeContext(RuntimeTypeInfo[] genericTypeArguments, RuntimeTypeInfo[] genericMethodArguments) + { + _genericTypeArguments = genericTypeArguments; + _genericMethodArguments = genericMethodArguments; + } + + internal RuntimeTypeInfo[] GenericTypeArguments + { + get + { + return _genericTypeArguments; + } + } + + internal RuntimeTypeInfo[] GenericMethodArguments + { + get + { + return _genericMethodArguments; + } + } + + private readonly RuntimeTypeInfo[] _genericTypeArguments; + private readonly RuntimeTypeInfo[] _genericMethodArguments; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeForwardInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeForwardInfo.cs new file mode 100644 index 00000000000000..111f45abc14d12 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeForwardInfo.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Reflection.Runtime.General +{ + internal struct TypeForwardInfo + { + public TypeForwardInfo(RuntimeAssemblyName redirectedAssemblyName, string namespaceName, string typeName) + { + Debug.Assert(redirectedAssemblyName != null); + Debug.Assert(namespaceName != null); + Debug.Assert(typeName != null); + + RedirectedAssemblyName = redirectedAssemblyName; + NamespaceName = namespaceName; + TypeName = typeName; + } + + public RuntimeAssemblyName RedirectedAssemblyName { get; } + public string NamespaceName { get; } + public string TypeName { get; } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeResolver.NativeFormat.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeResolver.NativeFormat.cs new file mode 100644 index 00000000000000..aa2a4ebf61618d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeResolver.NativeFormat.cs @@ -0,0 +1,228 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; + +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.Assemblies; +using System.Reflection.Runtime.TypeParsing; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.General +{ + internal static partial class TypeResolver + { + // + // Main routine to resolve a typeDef/Ref/Spec. Also accepts ModifiedTypes (will unwrap and ignore the custom modifiers.) + // + internal static RuntimeTypeInfo Resolve(this Handle typeDefRefOrSpec, MetadataReader reader, TypeContext typeContext) + { + Exception exception = null; + RuntimeTypeInfo runtimeType = typeDefRefOrSpec.TryResolve(reader, typeContext, ref exception); + if (runtimeType == null) + throw exception; + return runtimeType; + } + + internal static RuntimeTypeInfo TryResolve(this Handle typeDefRefOrSpec, MetadataReader reader, TypeContext typeContext, ref Exception exception) + { + HandleType handleType = typeDefRefOrSpec.HandleType; + if (handleType == HandleType.TypeDefinition) + return typeDefRefOrSpec.ToTypeDefinitionHandle(reader).ResolveTypeDefinition(reader); + else if (handleType == HandleType.TypeReference) + return typeDefRefOrSpec.ToTypeReferenceHandle(reader).TryResolveTypeReference(reader, ref exception); + else if (handleType == HandleType.TypeSpecification) + return typeDefRefOrSpec.ToTypeSpecificationHandle(reader).TryResolveTypeSignature(reader, typeContext, ref exception); + else if (handleType == HandleType.ModifiedType) + { + ModifiedType modifiedType = typeDefRefOrSpec.ToModifiedTypeHandle(reader).GetModifiedType(reader); + return modifiedType.Type.TryResolve(reader, typeContext, ref exception); + } + else + throw new BadImageFormatException(); // Expected TypeRef, Def or Spec. + } + + + // + // Main routine to resolve a typeDefinition. + // + internal static RuntimeTypeInfo ResolveTypeDefinition(this TypeDefinitionHandle typeDefinitionHandle, MetadataReader reader) + { + return typeDefinitionHandle.GetNamedType(reader); + } + + // + // Main routine to parse a metadata type specification signature. + // + private static RuntimeTypeInfo TryResolveTypeSignature(this TypeSpecificationHandle typeSpecHandle, MetadataReader reader, TypeContext typeContext, ref Exception exception) + { + Handle typeHandle = typeSpecHandle.GetTypeSpecification(reader).Signature; + switch (typeHandle.HandleType) + { + case HandleType.ArraySignature: + { + ArraySignature sig = typeHandle.ToArraySignatureHandle(reader).GetArraySignature(reader); + int rank = sig.Rank; + if (rank <= 0) + throw new BadImageFormatException(); // Bad rank. + RuntimeTypeInfo elementType = sig.ElementType.TryResolve(reader, typeContext, ref exception); + if (elementType == null) + return null; + return elementType.GetMultiDimArrayType(rank); + } + + case HandleType.ByReferenceSignature: + { + ByReferenceSignature sig = typeHandle.ToByReferenceSignatureHandle(reader).GetByReferenceSignature(reader); + RuntimeTypeInfo targetType = sig.Type.TryResolve(reader, typeContext, ref exception); + if (targetType == null) + return null; + return targetType.GetByRefType(); + } + + case HandleType.MethodTypeVariableSignature: + { + MethodTypeVariableSignature sig = typeHandle.ToMethodTypeVariableSignatureHandle(reader).GetMethodTypeVariableSignature(reader); + return typeContext.GenericMethodArguments[sig.Number]; + } + + case HandleType.PointerSignature: + { + PointerSignature sig = typeHandle.ToPointerSignatureHandle(reader).GetPointerSignature(reader); + RuntimeTypeInfo targetType = sig.Type.TryResolve(reader, typeContext, ref exception); + if (targetType == null) + return null; + return targetType.GetPointerType(); + } + + case HandleType.SZArraySignature: + { + SZArraySignature sig = typeHandle.ToSZArraySignatureHandle(reader).GetSZArraySignature(reader); + RuntimeTypeInfo elementType = sig.ElementType.TryResolve(reader, typeContext, ref exception); + if (elementType == null) + return null; + return elementType.GetArrayType(); + } + + case HandleType.TypeDefinition: + { + return typeHandle.ToTypeDefinitionHandle(reader).ResolveTypeDefinition(reader); + } + + case HandleType.TypeInstantiationSignature: + { + TypeInstantiationSignature sig = typeHandle.ToTypeInstantiationSignatureHandle(reader).GetTypeInstantiationSignature(reader); + RuntimeTypeInfo genericTypeDefinition = sig.GenericType.TryResolve(reader, typeContext, ref exception); + if (genericTypeDefinition == null) + return null; + LowLevelList genericTypeArguments = new LowLevelList(); + foreach (Handle genericTypeArgumentHandle in sig.GenericTypeArguments) + { + RuntimeTypeInfo genericTypeArgument = genericTypeArgumentHandle.TryResolve(reader, typeContext, ref exception); + if (genericTypeArgument == null) + return null; + genericTypeArguments.Add(genericTypeArgument); + } + return genericTypeDefinition.GetConstructedGenericType(genericTypeArguments.ToArray()); + } + + case HandleType.TypeReference: + { + return typeHandle.ToTypeReferenceHandle(reader).TryResolveTypeReference(reader, ref exception); + } + + case HandleType.TypeVariableSignature: + { + TypeVariableSignature sig = typeHandle.ToTypeVariableSignatureHandle(reader).GetTypeVariableSignature(reader); + return typeContext.GenericTypeArguments[sig.Number]; + } + + default: + throw new NotSupportedException(); // Unexpected Type signature type. + } + } + + // + // Main routine to resolve a typeReference. + // + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", + Justification = "Resolves type references within metadata. We ensure metadata is consistent.")] + private static RuntimeTypeInfo TryResolveTypeReference(this TypeReferenceHandle typeReferenceHandle, MetadataReader reader, ref Exception exception) + { + RuntimeTypeHandle resolvedRuntimeTypeHandle; + if (ReflectionCoreExecution.ExecutionEnvironment.TryGetNamedTypeForTypeReference(reader, typeReferenceHandle, out resolvedRuntimeTypeHandle)) + return resolvedRuntimeTypeHandle.GetTypeForRuntimeTypeHandle(); + + TypeReference typeReference = typeReferenceHandle.GetTypeReference(reader); + string name = typeReference.TypeName.GetString(reader); + Handle parent = typeReference.ParentNamespaceOrType; + HandleType parentType = parent.HandleType; + TypeInfo outerTypeInfo = null; + + // Check if this is a reference to a nested type. + + if (parentType == HandleType.TypeDefinition) + { + outerTypeInfo = parent.ToTypeDefinitionHandle(reader).GetNamedType(reader); + } + else if (parentType == HandleType.TypeReference) + { + RuntimeTypeInfo outerType = parent.ToTypeReferenceHandle(reader).TryResolveTypeReference(reader, ref exception); + if (outerType == null) + return null; + outerTypeInfo = outerType; // Since we got to outerType via a metadata reference, we're assured GetTypeInfo() won't throw a MissingMetadataException. + } + if (outerTypeInfo != null) + { + // It was a nested type. We've already resolved the containing type recursively - just find the nested among its direct children. + TypeInfo resolvedTypeInfo = outerTypeInfo.GetDeclaredNestedType(name); + if (resolvedTypeInfo == null) + { + exception = ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(outerTypeInfo, name); + return null; + } + return resolvedTypeInfo.CastToRuntimeTypeInfo(); + } + + + // If we got here, the typeReference was to a non-nested type. + if (parentType == HandleType.NamespaceReference) + { + NamespaceReferenceHandle namespaceReferenceHandle = parent.ToNamespaceReferenceHandle(reader); + string fullName = namespaceReferenceHandle.ToFullyQualifiedTypeName(name, reader); + Handle parentHandleToSearch = parent; + + while (parentHandleToSearch.HandleType != HandleType.ScopeReference) + { + parentHandleToSearch = parentHandleToSearch.ToNamespaceReferenceHandle(reader).GetNamespaceReference(reader).ParentScopeOrNamespace; + } + ScopeReferenceHandle scopeReferenceHandle = parentHandleToSearch.ToScopeReferenceHandle(reader); + + RuntimeAssemblyName assemblyName = scopeReferenceHandle.ToRuntimeAssemblyName(reader); + RuntimeAssemblyInfo runtimeAssembly; + exception = RuntimeAssemblyInfo.TryGetRuntimeAssembly(assemblyName, out runtimeAssembly); + if (exception != null) + return null; + RuntimeTypeInfo runtimeType = runtimeAssembly.GetTypeCore(fullName, ignoreCase: false); + if (runtimeType == null) + { + exception = Helpers.CreateTypeLoadException(fullName, assemblyName.FullName); + return null; + } + return runtimeType; + } + + throw new BadImageFormatException(); // Expected TypeReference parent to be typeRef, typeDef or namespaceRef. + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeResolver.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeResolver.cs new file mode 100644 index 00000000000000..fcf6e8d4470b04 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeResolver.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; + +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.Assemblies; +using System.Reflection.Runtime.TypeParsing; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.General +{ + internal static partial class TypeResolver + { + // + // Main routine to resolve a typeDef/Ref/Spec. + // + internal static RuntimeTypeInfo Resolve(this QTypeDefRefOrSpec typeDefOrRefOrSpec, TypeContext typeContext) + { + Exception exception = null; + RuntimeTypeInfo runtimeType = typeDefOrRefOrSpec.TryResolve(typeContext, ref exception); + if (runtimeType == null) + throw exception; + return runtimeType; + } + + internal static RuntimeTypeInfo TryResolve(this QTypeDefRefOrSpec typeDefOrRefOrSpec, TypeContext typeContext, ref Exception exception) + { + if (typeDefOrRefOrSpec.IsNativeFormatMetadataBased) + { + return global::Internal.Metadata.NativeFormat.Handle.FromIntToken(typeDefOrRefOrSpec.Handle).TryResolve((global::Internal.Metadata.NativeFormat.MetadataReader)typeDefOrRefOrSpec.Reader, typeContext, ref exception); + } + +#if ECMA_METADATA_SUPPORT + if (typeDefOrRefOrSpec.Reader is global::System.Reflection.Metadata.MetadataReader ecmaReader) + return global::System.Reflection.Metadata.Ecma335.MetadataTokens.Handle(typeDefOrRefOrSpec.Handle).TryResolve(ecmaReader, typeContext, ref exception); +#endif + + throw new BadImageFormatException(); // Expected TypeRef, Def or Spec with MetadataReader + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.NativeFormat.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.NativeFormat.cs new file mode 100644 index 00000000000000..b44cf57cdff6dc --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.NativeFormat.cs @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeInfos.NativeFormat; +using System.Reflection.Runtime.MethodInfos; + +using Internal.Metadata.NativeFormat; +using Internal.Reflection.Core.Execution; + +// +// It is common practice for app code to compare Type objects using reference equality with the expectation that reference equality +// is equivalent to semantic equality. To support this, all RuntimeTypeObject objects are interned using weak references. +// +// This assumption is baked into the codebase in these places: +// +// - RuntimeTypeInfo.Equals(object) implements itself as Object.ReferenceEquals(this, obj) +// +// - RuntimeTypeInfo.GetHashCode() is implemented in a flavor-specific manner (We can't use Object.GetHashCode() +// because we don't want the hash value to change if a type is collected and resurrected later.) +// +// This assumption is actualized as follows: +// +// - RuntimeTypeInfo classes hide their constructor. The only way to instantiate a RuntimeTypeInfo +// is through its public static factory method which ensures the interning and are collected in this one +// file for easy auditing and to help ensure that they all operate in a consistent manner. +// +// - The TypeUnifier extension class provides a more friendly interface to the rest of the codebase. +// + +namespace System.Reflection.Runtime.General +{ + internal static partial class TypeUnifier + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RuntimeTypeDefinitionTypeInfo GetNamedType(this TypeDefinitionHandle typeDefinitionHandle, MetadataReader reader) + { + return typeDefinitionHandle.GetNamedType(reader, default(RuntimeTypeHandle)); + } + + //====================================================================================================== + // This next group services the Type.GetTypeFromHandle() path. Since we already have a RuntimeTypeHandle + // in that case, we pass it in as an extra argument as an optimization (otherwise, the unifier will + // waste cycles looking up the handle again from the mapping tables.) + //====================================================================================================== + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RuntimeTypeDefinitionTypeInfo GetNamedType(this TypeDefinitionHandle typeDefinitionHandle, MetadataReader reader, RuntimeTypeHandle precomputedTypeHandle) + { + return NativeFormatRuntimeNamedTypeInfo.GetRuntimeNamedTypeInfo(reader, typeDefinitionHandle, precomputedTypeHandle: precomputedTypeHandle); + } + } +} + +namespace System.Reflection.Runtime.TypeInfos.NativeFormat +{ + //----------------------------------------------------------------------------------------------------------- + // TypeInfos for type definitions (i.e. "Foo" and "Foo<>" but not "Foo") + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class NativeFormatRuntimeNamedTypeInfo : RuntimeNamedTypeInfo + { + internal static NativeFormatRuntimeNamedTypeInfo GetRuntimeNamedTypeInfo(MetadataReader metadataReader, TypeDefinitionHandle typeDefHandle, RuntimeTypeHandle precomputedTypeHandle) + { + RuntimeTypeHandle typeHandle = precomputedTypeHandle; + if (typeHandle.IsNull()) + { + if (!ReflectionCoreExecution.ExecutionEnvironment.TryGetNamedTypeForMetadata(new QTypeDefinition(metadataReader, typeDefHandle), out typeHandle)) + typeHandle = default(RuntimeTypeHandle); + } + UnificationKey key = new UnificationKey(metadataReader, typeDefHandle, typeHandle); + + NativeFormatRuntimeNamedTypeInfo type = NamedTypeTable.Table.GetOrAdd(key); + type.EstablishDebugName(); + return type; + } + + private sealed class NamedTypeTable : ConcurrentUnifierW + { + protected sealed override NativeFormatRuntimeNamedTypeInfo Factory(UnificationKey key) + { + return new NativeFormatRuntimeNamedTypeInfo(key.Reader, key.TypeDefinitionHandle, key.TypeHandle); + } + + public static readonly NamedTypeTable Table = new NamedTypeTable(); + } + } + + //----------------------------------------------------------------------------------------------------------- + // TypeInfos for generic parameters on types. + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class NativeFormatRuntimeGenericParameterTypeInfoForTypes : NativeFormatRuntimeGenericParameterTypeInfo + { + // + // For app-compat reasons, we need to make sure that only TypeInfo instance exists for a given semantic type. If you change this, you must change the way + // RuntimeTypeInfo.Equals() is implemented. + // + internal static NativeFormatRuntimeGenericParameterTypeInfoForTypes GetRuntimeGenericParameterTypeInfoForTypes(NativeFormatRuntimeNamedTypeInfo typeOwner, GenericParameterHandle genericParameterHandle) + { + UnificationKey key = new UnificationKey(typeOwner.Reader, typeOwner.TypeDefinitionHandle, genericParameterHandle); + NativeFormatRuntimeGenericParameterTypeInfoForTypes type = GenericParameterTypeForTypesTable.Table.GetOrAdd(key); + type.EstablishDebugName(); + return type; + } + + private sealed class GenericParameterTypeForTypesTable : ConcurrentUnifierW + { + protected sealed override NativeFormatRuntimeGenericParameterTypeInfoForTypes Factory(UnificationKey key) + { + RuntimeTypeDefinitionTypeInfo typeOwner = key.TypeDefinitionHandle.GetNamedType(key.Reader); + return new NativeFormatRuntimeGenericParameterTypeInfoForTypes(key.Reader, key.GenericParameterHandle, typeOwner); + } + + public static readonly GenericParameterTypeForTypesTable Table = new GenericParameterTypeForTypesTable(); + } + } + + //----------------------------------------------------------------------------------------------------------- + // TypeInfos for generic parameters on methods. + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class NativeFormatRuntimeGenericParameterTypeInfoForMethods : NativeFormatRuntimeGenericParameterTypeInfo, IKeyedItem + { + // + // For app-compat reasons, we need to make sure that only TypeInfo instance exists for a given semantic type. If you change this, you must change the way + // RuntimeTypeInfo.Equals() is implemented. + // + internal static NativeFormatRuntimeGenericParameterTypeInfoForMethods GetRuntimeGenericParameterTypeInfoForMethods(RuntimeNamedMethodInfo methodOwner, MetadataReader reader, GenericParameterHandle genericParameterHandle) + { + UnificationKey key = new UnificationKey(methodOwner, reader, genericParameterHandle); + NativeFormatRuntimeGenericParameterTypeInfoForMethods type = GenericParameterTypeForMethodsTable.Table.GetOrAdd(key); + type.EstablishDebugName(); + return type; + } + + private sealed class GenericParameterTypeForMethodsTable : ConcurrentUnifierWKeyed + { + protected sealed override NativeFormatRuntimeGenericParameterTypeInfoForMethods Factory(UnificationKey key) + { + return new NativeFormatRuntimeGenericParameterTypeInfoForMethods(key.Reader, key.GenericParameterHandle, key.MethodOwner); + } + + public static readonly GenericParameterTypeForMethodsTable Table = new GenericParameterTypeForMethodsTable(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.cs new file mode 100644 index 00000000000000..e9f8abb56ebfe2 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.cs @@ -0,0 +1,510 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.MethodInfos; + +using Internal.Reflection.Core.Execution; + +// +// It is common practice for app code to compare Type objects using reference equality with the expectation that reference equality +// is equivalent to semantic equality. To support this, all RuntimeTypeObject objects are interned using weak references. +// +// This assumption is baked into the codebase in these places: +// +// - RuntimeTypeInfo.Equals(object) implements itself as Object.ReferenceEquals(this, obj) +// +// - RuntimeTypeInfo.GetHashCode() is implemented in a flavor-specific manner (We can't use Object.GetHashCode() +// because we don't want the hash value to change if a type is collected and resurrected later.) +// +// This assumption is actualized as follows: +// +// - RuntimeTypeInfo classes hide their constructor. The only way to instantiate a RuntimeTypeInfo +// is through its public static factory method which ensures the interning and are collected in this one +// file for easy auditing and to help ensure that they all operate in a consistent manner. +// +// - The TypeUnifier extension class provides a more friendly interface to the rest of the codebase. +// + +namespace System.Reflection.Runtime.General +{ + internal static partial class TypeUnifier + { + // This can be replaced at native compile time using a feature switch. + internal static bool IsTypeConstructionEagerlyValidated => true; + + public static RuntimeTypeInfo GetArrayType(this RuntimeTypeInfo elementType) + { + return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: false, rank: 1); + } + + public static RuntimeTypeInfo GetArrayTypeWithTypeHandle(this RuntimeTypeInfo elementType) + { + return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: false, rank: 1).WithVerifiedTypeHandle(elementType); + } + + public static RuntimeTypeInfo GetMultiDimArrayType(this RuntimeTypeInfo elementType, int rank) + { + return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: true, rank: rank); + } + + public static RuntimeTypeInfo GetMultiDimArrayTypeWithTypeHandle(this RuntimeTypeInfo elementType, int rank) + { + return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: true, rank: rank).WithVerifiedTypeHandle(elementType); + } + + private static RuntimeArrayTypeInfo WithVerifiedTypeHandle(this RuntimeArrayTypeInfo arrayType, RuntimeTypeInfo elementType) + { + // We only permit creating parameterized types if the pay-for-play policy specifically allows them *or* if the result + // type would be an open type. + RuntimeTypeHandle typeHandle = arrayType.InternalTypeHandleIfAvailable; + if (IsTypeConstructionEagerlyValidated + && typeHandle.IsNull() && !elementType.ContainsGenericParameters +#if FEATURE_COMINTEROP + && !(elementType is RuntimeCLSIDTypeInfo) +#endif + ) + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingArrayTypeException(elementType, isMultiDim: false, 1); + + return arrayType; + } + + public static RuntimeTypeInfo GetByRefType(this RuntimeTypeInfo targetType) + { + return RuntimeByRefTypeInfo.GetByRefTypeInfo(targetType); + } + + public static RuntimeTypeInfo GetPointerType(this RuntimeTypeInfo targetType) + { + return RuntimePointerTypeInfo.GetPointerTypeInfo(targetType); + } + + public static RuntimeTypeInfo GetConstructedGenericType(this RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments) + { + return RuntimeConstructedGenericTypeInfo.GetRuntimeConstructedGenericTypeInfo(genericTypeDefinition, genericTypeArguments); + } + + public static RuntimeTypeInfo GetConstructedGenericTypeWithTypeHandle(this RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments) + { + return RuntimeConstructedGenericTypeInfo.GetRuntimeConstructedGenericTypeInfo(genericTypeDefinition, genericTypeArguments).WithVerifiedTypeHandle(genericTypeArguments); + } + + private static RuntimeConstructedGenericTypeInfo WithVerifiedTypeHandle(this RuntimeConstructedGenericTypeInfo genericType, RuntimeTypeInfo[] genericTypeArguments) + { + // We only permit creating parameterized types if the pay-for-play policy specifically allows them *or* if the result + // type would be an open type. + RuntimeTypeHandle typeHandle = genericType.InternalTypeHandleIfAvailable; + if (IsTypeConstructionEagerlyValidated && typeHandle.IsNull()) + { + bool atLeastOneOpenType = false; + foreach (RuntimeTypeInfo genericTypeArgument in genericTypeArguments) + { + if (genericTypeArgument.ContainsGenericParameters) + atLeastOneOpenType = true; + } + if (!atLeastOneOpenType) + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingConstructedGenericTypeException(genericType.GetGenericTypeDefinition(), genericTypeArguments.CloneTypeArray()); + } + + return genericType; + } + + public static RuntimeTypeInfo GetTypeForRuntimeTypeHandle(this RuntimeTypeHandle typeHandle) + { + Type type = Type.GetTypeFromHandle(typeHandle); + return type.CastToRuntimeTypeInfo(); + } + + //====================================================================================================== + // This next group services the Type.GetTypeFromHandle() path. Since we already have a RuntimeTypeHandle + // in that case, we pass it in as an extra argument as an optimization (otherwise, the unifier will + // waste cycles looking up the handle again from the mapping tables.) + //====================================================================================================== + + public static RuntimeTypeInfo GetArrayType(this RuntimeTypeInfo elementType, RuntimeTypeHandle precomputedTypeHandle) + { + return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: false, rank: 1, precomputedTypeHandle: precomputedTypeHandle); + } + + public static RuntimeTypeInfo GetMultiDimArrayType(this RuntimeTypeInfo elementType, int rank, RuntimeTypeHandle precomputedTypeHandle) + { + return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: true, rank: rank, precomputedTypeHandle: precomputedTypeHandle); + } + + public static RuntimeTypeInfo GetPointerType(this RuntimeTypeInfo targetType, RuntimeTypeHandle precomputedTypeHandle) + { + return RuntimePointerTypeInfo.GetPointerTypeInfo(targetType, precomputedTypeHandle); + } + + public static RuntimeTypeInfo GetByRefType(this RuntimeTypeInfo targetType, RuntimeTypeHandle precomputedTypeHandle) + { + return RuntimeByRefTypeInfo.GetByRefTypeInfo(targetType, precomputedTypeHandle); + } + + public static RuntimeTypeInfo GetConstructedGenericType(this RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments, RuntimeTypeHandle precomputedTypeHandle) + { + return RuntimeConstructedGenericTypeInfo.GetRuntimeConstructedGenericTypeInfo(genericTypeDefinition, genericTypeArguments, precomputedTypeHandle); + } + } +} + +namespace System.Reflection.Runtime.TypeInfos +{ + //----------------------------------------------------------------------------------------------------------- + // TypeInfos for type definitions (i.e. "Foo" and "Foo<>" but not "Foo") that aren't opted into metadata. + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimeNoMetadataNamedTypeInfo + { + internal static RuntimeNoMetadataNamedTypeInfo GetRuntimeNoMetadataNamedTypeInfo(RuntimeTypeHandle typeHandle, bool isGenericTypeDefinition) + { + RuntimeNoMetadataNamedTypeInfo type; + if (isGenericTypeDefinition) + type = GenericNoMetadataNamedTypeTable.Table.GetOrAdd(new RuntimeTypeHandleKey(typeHandle)); + else + type = NoMetadataNamedTypeTable.Table.GetOrAdd(new RuntimeTypeHandleKey(typeHandle)); + type.EstablishDebugName(); + return type; + } + + private sealed class NoMetadataNamedTypeTable : ConcurrentUnifierW + { + protected sealed override RuntimeNoMetadataNamedTypeInfo Factory(RuntimeTypeHandleKey key) + { + return new RuntimeNoMetadataNamedTypeInfo(key.TypeHandle, isGenericTypeDefinition: false); + } + + public static readonly NoMetadataNamedTypeTable Table = new NoMetadataNamedTypeTable(); + } + + private sealed class GenericNoMetadataNamedTypeTable : ConcurrentUnifierW + { + protected sealed override RuntimeNoMetadataNamedTypeInfo Factory(RuntimeTypeHandleKey key) + { + return new RuntimeNoMetadataNamedTypeInfo(key.TypeHandle, isGenericTypeDefinition: true); + } + + public static readonly GenericNoMetadataNamedTypeTable Table = new GenericNoMetadataNamedTypeTable(); + } + } + + //----------------------------------------------------------------------------------------------------------- + // TypeInfos that represent type definitions (i.e. Foo or Foo<>) or constructed generic types (Foo) + // that can never be reflection-enabled due to the framework Reflection block. + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimeBlockedTypeInfo + { + internal static RuntimeBlockedTypeInfo GetRuntimeBlockedTypeInfo(RuntimeTypeHandle typeHandle, bool isGenericTypeDefinition) + { + RuntimeBlockedTypeInfo type; + if (isGenericTypeDefinition) + type = GenericBlockedTypeTable.Table.GetOrAdd(new RuntimeTypeHandleKey(typeHandle)); + else + type = BlockedTypeTable.Table.GetOrAdd(new RuntimeTypeHandleKey(typeHandle)); + type.EstablishDebugName(); + return type; + } + + private sealed class BlockedTypeTable : ConcurrentUnifierW + { + protected sealed override RuntimeBlockedTypeInfo Factory(RuntimeTypeHandleKey key) + { + return new RuntimeBlockedTypeInfo(key.TypeHandle, isGenericTypeDefinition: false); + } + + public static readonly BlockedTypeTable Table = new BlockedTypeTable(); + } + + private sealed class GenericBlockedTypeTable : ConcurrentUnifierW + { + protected sealed override RuntimeBlockedTypeInfo Factory(RuntimeTypeHandleKey key) + { + return new RuntimeBlockedTypeInfo(key.TypeHandle, isGenericTypeDefinition: true); + } + + public static readonly GenericBlockedTypeTable Table = new GenericBlockedTypeTable(); + } + } + + //----------------------------------------------------------------------------------------------------------- + // TypeInfos for Sz and multi-dim Array types. + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimeArrayTypeInfo : RuntimeHasElementTypeInfo + { + internal static RuntimeArrayTypeInfo GetArrayTypeInfo(RuntimeTypeInfo elementType, bool multiDim, int rank) + { + return GetArrayTypeInfo(elementType, multiDim, rank, GetRuntimeTypeHandleIfAny(elementType, multiDim, rank)); + } + + internal static RuntimeArrayTypeInfo GetArrayTypeInfo(RuntimeTypeInfo elementType, bool multiDim, int rank, RuntimeTypeHandle precomputedTypeHandle) + { + Debug.Assert(multiDim || rank == 1); + + UnificationKey key = new UnificationKey(elementType, precomputedTypeHandle); + RuntimeArrayTypeInfo type; + if (!multiDim) + type = ArrayTypeTable.Table.GetOrAdd(key); + else + type = TypeTableForMultiDimArrayTypeTables.Table.GetOrAdd(rank).GetOrAdd(key); + type.EstablishDebugName(); + return type; + } + + private static RuntimeTypeHandle GetRuntimeTypeHandleIfAny(RuntimeTypeInfo elementType, bool multiDim, int rank) + { + Debug.Assert(multiDim || rank == 1); + + RuntimeTypeHandle elementTypeHandle = elementType.InternalTypeHandleIfAvailable; + if (elementTypeHandle.IsNull()) + return default(RuntimeTypeHandle); + + // The check is here on purpose - one of the implementations of IsByRefLike contains a custom attribute + // search and those are very expensive from size on disk footprint perspective. We purposefully + // place this call in a path that won't be part of the executable image unless more advanced reflection services + // are also needed ("pay for play"). We really don't want a typeof() to push the app into requiring the full reflection + // stack to be compiled into the final executable. + if (elementType.IsByRefLike) + throw new TypeLoadException(SR.Format(SR.ArgumentException_InvalidArrayElementType, elementType)); + + RuntimeTypeHandle typeHandle; + if (!multiDim) + { + if (!ReflectionCoreExecution.ExecutionEnvironment.TryGetArrayTypeForElementType(elementTypeHandle, out typeHandle)) + return default(RuntimeTypeHandle); + } + else + { + if (!ReflectionCoreExecution.ExecutionEnvironment.TryGetMultiDimArrayTypeForElementType(elementTypeHandle, rank, out typeHandle)) + return default(RuntimeTypeHandle); + } + + return typeHandle; + } + + private sealed class ArrayTypeTable : ConcurrentUnifierWKeyed + { + protected sealed override RuntimeArrayTypeInfo Factory(UnificationKey key) + { + ValidateElementType(key.ElementType, key.TypeHandle, multiDim: false, rank: 1); + + return new RuntimeArrayTypeInfo(key, multiDim: false, rank: 1); + } + + public static readonly ArrayTypeTable Table = new ArrayTypeTable(); + } + + private sealed class MultiDimArrayTypeTable : ConcurrentUnifierWKeyed + { + public MultiDimArrayTypeTable(int rank) + { + _rank = rank; + } + + protected sealed override RuntimeArrayTypeInfo Factory(UnificationKey key) + { + ValidateElementType(key.ElementType, key.TypeHandle, multiDim: true, rank: _rank); + + return new RuntimeArrayTypeInfo(key, multiDim: true, rank: _rank); + } + + private readonly int _rank; + } + + // + // For the hopefully rare case of multidim arrays, we have a dictionary of dictionaries. + // + private sealed class TypeTableForMultiDimArrayTypeTables : ConcurrentUnifier + { + protected sealed override MultiDimArrayTypeTable Factory(int rank) + { + Debug.Assert(rank > 0); + return new MultiDimArrayTypeTable(rank); + } + + public static readonly TypeTableForMultiDimArrayTypeTables Table = new TypeTableForMultiDimArrayTypeTables(); + } + + private static void ValidateElementType(RuntimeTypeInfo elementType, RuntimeTypeHandle typeHandle, bool multiDim, int rank) + { + Debug.Assert(multiDim || rank == 1); + + if (elementType.IsByRef) + throw new TypeLoadException(SR.Format(SR.ArgumentException_InvalidArrayElementType, elementType)); + } + } + + //----------------------------------------------------------------------------------------------------------- + // TypeInfos for ByRef types. + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimeByRefTypeInfo : RuntimeHasElementTypeInfo + { + internal static RuntimeByRefTypeInfo GetByRefTypeInfo(RuntimeTypeInfo elementType) + { + return GetByRefTypeInfo(elementType, GetRuntimeTypeHandleIfAny(elementType)); + } + + internal static RuntimeByRefTypeInfo GetByRefTypeInfo(RuntimeTypeInfo elementType, RuntimeTypeHandle precomputedTypeHandle) + { + RuntimeByRefTypeInfo type = ByRefTypeTable.Table.GetOrAdd(new UnificationKey(elementType, precomputedTypeHandle)); + type.EstablishDebugName(); + return type; + } + + private static RuntimeTypeHandle GetRuntimeTypeHandleIfAny(RuntimeTypeInfo elementType) + { + RuntimeTypeHandle elementTypeHandle = elementType.InternalTypeHandleIfAvailable; + if (elementTypeHandle.IsNull()) + return default(RuntimeTypeHandle); + + RuntimeTypeHandle typeHandle; + if (!ReflectionCoreExecution.ExecutionEnvironment.TryGetByRefTypeForTargetType(elementTypeHandle, out typeHandle)) + return default(RuntimeTypeHandle); + + return typeHandle; + } + + private sealed class ByRefTypeTable : ConcurrentUnifierWKeyed + { + protected sealed override RuntimeByRefTypeInfo Factory(UnificationKey key) + { + if (key.ElementType.IsByRef) + throw new TypeLoadException(SR.Format(SR.CannotCreateByRefOfByRef, key.ElementType)); + + return new RuntimeByRefTypeInfo(key); + } + + public static readonly ByRefTypeTable Table = new ByRefTypeTable(); + } + } + + //----------------------------------------------------------------------------------------------------------- + // TypeInfos for Pointer types. + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimePointerTypeInfo : RuntimeHasElementTypeInfo + { + internal static RuntimePointerTypeInfo GetPointerTypeInfo(RuntimeTypeInfo elementType) + { + return GetPointerTypeInfo(elementType, precomputedTypeHandle: GetRuntimeTypeHandleIfAny(elementType)); + } + + internal static RuntimePointerTypeInfo GetPointerTypeInfo(RuntimeTypeInfo elementType, RuntimeTypeHandle precomputedTypeHandle) + { + RuntimePointerTypeInfo type = PointerTypeTable.Table.GetOrAdd(new UnificationKey(elementType, precomputedTypeHandle)); + type.EstablishDebugName(); + return type; + } + + private static RuntimeTypeHandle GetRuntimeTypeHandleIfAny(RuntimeTypeInfo elementType) + { + RuntimeTypeHandle elementTypeHandle = elementType.InternalTypeHandleIfAvailable; + if (elementTypeHandle.IsNull()) + return default(RuntimeTypeHandle); + + RuntimeTypeHandle typeHandle; + if (!ReflectionCoreExecution.ExecutionEnvironment.TryGetPointerTypeForTargetType(elementTypeHandle, out typeHandle)) + return default(RuntimeTypeHandle); + + return typeHandle; + } + + private sealed class PointerTypeTable : ConcurrentUnifierWKeyed + { + protected sealed override RuntimePointerTypeInfo Factory(UnificationKey key) + { + if (key.ElementType.IsByRef) + throw new TypeLoadException(SR.Format(SR.CannotCreatePointerOfByRef, key.ElementType)); + + return new RuntimePointerTypeInfo(key); + } + + public static readonly PointerTypeTable Table = new PointerTypeTable(); + } + } + + //----------------------------------------------------------------------------------------------------------- + // TypeInfos for Constructed generic types ("Foo") + //----------------------------------------------------------------------------------------------------------- + internal sealed partial class RuntimeConstructedGenericTypeInfo : RuntimeTypeInfo, IKeyedItem + { + internal static RuntimeConstructedGenericTypeInfo GetRuntimeConstructedGenericTypeInfo(RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments) + { + return GetRuntimeConstructedGenericTypeInfo(genericTypeDefinition, genericTypeArguments, precomputedTypeHandle: GetRuntimeTypeHandleIfAny(genericTypeDefinition, genericTypeArguments)); + } + + internal static RuntimeConstructedGenericTypeInfo GetRuntimeConstructedGenericTypeInfo(RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments, RuntimeTypeHandle precomputedTypeHandle) + { + UnificationKey key = new UnificationKey(genericTypeDefinition, genericTypeArguments, precomputedTypeHandle); + RuntimeConstructedGenericTypeInfo typeInfo = ConstructedGenericTypeTable.Table.GetOrAdd(key); + typeInfo.EstablishDebugName(); + return typeInfo; + } + + private static RuntimeTypeHandle GetRuntimeTypeHandleIfAny(RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments) + { + RuntimeTypeHandle genericTypeDefinitionHandle = genericTypeDefinition.InternalTypeHandleIfAvailable; + if (genericTypeDefinitionHandle.IsNull()) + return default(RuntimeTypeHandle); + + if (ReflectionCoreExecution.ExecutionEnvironment.IsReflectionBlocked(genericTypeDefinitionHandle)) + return default(RuntimeTypeHandle); + + int count = genericTypeArguments.Length; + RuntimeTypeHandle[] genericTypeArgumentHandles = new RuntimeTypeHandle[count]; + for (int i = 0; i < count; i++) + { + RuntimeTypeHandle genericTypeArgumentHandle = genericTypeArguments[i].InternalTypeHandleIfAvailable; + if (genericTypeArgumentHandle.IsNull()) + return default(RuntimeTypeHandle); + genericTypeArgumentHandles[i] = genericTypeArgumentHandle; + } + + RuntimeTypeHandle typeHandle; + if (!ReflectionCoreExecution.ExecutionEnvironment.TryGetConstructedGenericTypeForComponents(genericTypeDefinitionHandle, genericTypeArgumentHandles, out typeHandle)) + return default(RuntimeTypeHandle); + + return typeHandle; + } + + private sealed class ConstructedGenericTypeTable : ConcurrentUnifierWKeyed + { + protected sealed override RuntimeConstructedGenericTypeInfo Factory(UnificationKey key) + { + foreach (RuntimeTypeInfo genericTypeArgument in key.GenericTypeArguments) + { + if (genericTypeArgument.IsByRef || genericTypeArgument.IsGenericTypeDefinition) + throw new ArgumentException(SR.Format(SR.ArgumentException_InvalidTypeArgument, genericTypeArgument)); + } + + return new RuntimeConstructedGenericTypeInfo(key); + } + + public static readonly ConstructedGenericTypeTable Table = new ConstructedGenericTypeTable(); + } + } + +#if FEATURE_COMINTEROP + internal sealed partial class RuntimeCLSIDTypeInfo + { + public static RuntimeCLSIDTypeInfo GetRuntimeCLSIDTypeInfo(Guid clsid, string server) + { + UnificationKey key = new UnificationKey(clsid, server); + return ClsIdTypeTable.Table.GetOrAdd(key); + } + + private sealed class ClsIdTypeTable : ConcurrentUnifierWKeyed + { + protected sealed override RuntimeCLSIDTypeInfo Factory(UnificationKey key) + { + return new RuntimeCLSIDTypeInfo(key.ClsId, key.Server); + } + + public static readonly ClsIdTypeTable Table = new ClsIdTypeTable(); + } + } +#endif +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodInvoker.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodInvoker.cs new file mode 100644 index 00000000000000..95f5c5564bf9ee --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodInvoker.cs @@ -0,0 +1,92 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +using Internal.Runtime.Augments; +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + // + // Custom invoker for edge case scenarios not handled by the toolchain. Examples: Strings and Nullables. + // + internal sealed class CustomMethodInvoker : MethodInvoker + { + public CustomMethodInvoker(Type thisType, Type[] parameterTypes, InvokerOptions options, CustomMethodInvokerAction action) + { + _action = action; + _options = options; + _thisType = thisType; + _parameterTypes = parameterTypes; + } + + protected sealed override object Invoke(object thisObject, object[] arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException) + { + Debug.Assert(arguments != null); + + // This does not handle optional parameters. None of the methods we use custom invocation for have them. + if (!(thisObject == null && 0 != (_options & InvokerOptions.AllowNullThis))) + ValidateThis(thisObject, _thisType.TypeHandle); + + if (arguments.Length != _parameterTypes.Length) + throw new TargetParameterCountException(); + + object[] convertedArguments = new object[arguments.Length]; + for (int i = 0; i < arguments.Length; i++) + { + convertedArguments[i] = RuntimeAugments.CheckArgument(arguments[i], _parameterTypes[i].TypeHandle, binderBundle); + } + object result; + try + { + result = _action(thisObject, convertedArguments, _thisType); + } + catch (Exception e) when (wrapInTargetInvocationException && ((_options & InvokerOptions.DontWrapException) == 0)) + { + throw new TargetInvocationException(e); + } + return result; + } + + public sealed override Delegate CreateDelegate(RuntimeTypeHandle delegateType, object target, bool isStatic, bool isVirtual, bool isOpen) + { + if (_thisType.IsConstructedGenericType && _thisType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + if (isOpen) + { + return DynamicDelegateAugments.CreateObjectArrayDelegate(Type.GetTypeFromHandle(delegateType), + (args) => + { + object[] arguments; + if (args.Length > 1) + { + arguments = new object[args.Length - 1]; + Array.Copy(args, 1, arguments, 0, args.Length - 1); + } + else + { + arguments = Array.Empty(); + } + + return _action(args[0], arguments, _thisType); + }); + } + else + { + // Desktop compat: MethodInfos to Nullable methods cannot be turned into delegates. + throw new ArgumentException(SR.Arg_DlgtTargMeth); + } + } + + throw new PlatformNotSupportedException(); + } + + public sealed override IntPtr LdFtnResult => throw new PlatformNotSupportedException(); + + private readonly InvokerOptions _options; + private readonly CustomMethodInvokerAction _action; + private readonly Type _thisType; + private readonly Type[] _parameterTypes; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodInvokerAction.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodInvokerAction.cs new file mode 100644 index 00000000000000..fa859a548fb38f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodInvokerAction.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Runtime.MethodInfos +{ + internal delegate object CustomMethodInvokerAction(object thisObject, object[] args, Type thisType); +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.Nullable.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.Nullable.cs new file mode 100644 index 00000000000000..bda098b67e7382 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.Nullable.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; +using System.Diagnostics.CodeAnalysis; + +namespace System.Reflection.Runtime.MethodInfos +{ + internal static partial class CustomMethodMapper + { + // + // Nullables are another edge case. + // + private static class NullableActions + { + public static Dictionary Map + { + get + { + if (s_lazyMap == null) + { + Dictionary map = new Dictionary(); + + Type type = typeof(Nullable<>); + Type theT = type.GetGenericTypeParameters()[0]; + + map.AddMethod(type, nameof(Nullable.ToString), Array.Empty(), + (object thisObject, object[] args, Type thisType) => + { + return thisObject == null ? string.Empty : thisObject.ToString(); + } + ); + + map.AddMethod(type, nameof(Nullable.Equals), new Type[] { typeof(object) }, + (object thisObject, object[] args, Type thisType) => + { + object other = args[0]; + if (thisObject == null) + return other == null; + if (other == null) + return false; + return thisObject.Equals(other); + } + ); + + map.AddMethod(type, nameof(Nullable.GetHashCode), Array.Empty(), + (object thisObject, object[] args, Type thisType) => + { + return thisObject == null ? 0 : thisObject.GetHashCode(); + } + ); + + map.AddConstructor(type, new Type[] { theT }, + (object thisObject, object[] args, Type thisType) => + { + return args[0]; + } + ); + + map.AddMethod(type, "get_" + nameof(Nullable.HasValue), Array.Empty(), + (object thisObject, object[] args, Type thisType) => + { + return thisObject != null; + } + ); + + map.AddMethod(type, "get_" + nameof(Nullable.Value), Array.Empty(), + (object thisObject, object[] args, Type thisType) => + { + if (thisObject == null) + throw new InvalidOperationException(SR.InvalidOperation_NoValue); + return thisObject; + } + ); + + map.AddMethod(type, nameof(Nullable.GetValueOrDefault), Array.Empty(), NullableGetValueOrDefault); + + static object NullableGetValueOrDefault(object thisObject, object[] args, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type thisType) + { + if (thisObject == null) + return RuntimeHelpers.GetUninitializedObject(thisType); + + return thisObject; + } + + map.AddMethod(type, nameof(Nullable.GetValueOrDefault), new Type[] { theT }, + (object thisObject, object[] args, Type thisType) => + { + if (thisObject == null) + return args[0]; + return thisObject; + } + ); + + s_lazyMap = map; + } + + return s_lazyMap; + } + } + + private static volatile Dictionary s_lazyMap; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.String.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.String.cs new file mode 100644 index 00000000000000..19596609b74b57 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.String.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; +using System.Collections.Generic; + +namespace System.Reflection.Runtime.MethodInfos +{ + internal static partial class CustomMethodMapper + { + // + // String constructors require special casing down the stack, being the only variable-sized objects created via a constructor. + // + private static class StringActions + { + public static Dictionary Map + { + get + { + if (s_lazyMap == null) + { + Dictionary map = new Dictionary(); + + Type type = typeof(string); + + unsafe + { + map.AddConstructor(type, new Type[] { typeof(char), typeof(int) }, + (object thisObject, object[] args, Type thisType) => + { + return new string((char)(args[0]), (int)(args[1])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(char[]) }, + (object thisObject, object[] args, Type thisType) => + { + return new string((char[])(args[0])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(char[]), typeof(int), typeof(int) }, + (object thisObject, object[] args, Type thisType) => + { + return new string((char[])(args[0]), (int)(args[1]), (int)(args[2])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(char*) }, + (object thisObject, object[] args, Type thisType) => + { + return new string((char*)(IntPtr)(args[0])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(char*), typeof(int), typeof(int) }, + (object thisObject, object[] args, Type thisType) => + { + return new string((char*)(IntPtr)(args[0]), (int)(args[1]), (int)(args[2])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(sbyte*) }, + (object thisObject, object[] args, Type thisType) => + { + return new string((sbyte*)(IntPtr)(args[0])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(sbyte*), typeof(int), typeof(int) }, + (object thisObject, object[] args, Type thisType) => + { + return new string((sbyte*)(IntPtr)(args[0]), (int)(args[1]), (int)(args[2])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(sbyte*), typeof(int), typeof(int), typeof(Encoding) }, + (object thisObject, object[] args, Type thisType) => + { + return new string((sbyte*)(IntPtr)(args[0]), (int)(args[1]), (int)(args[2]), (Encoding)(args[3])); + } + ); + } + + s_lazyMap = map; + } + + return s_lazyMap; + } + } + private static volatile Dictionary s_lazyMap; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.cs new file mode 100644 index 00000000000000..5848b2ed96b47b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; + +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + internal static partial class CustomMethodMapper + { + // + // Certain types and methods are edge-cases that require special handling. + // + public static MethodInvoker GetCustomMethodInvokerIfNeeded(this MethodBase methodBase) + { + Type declaringType = methodBase.DeclaringType; + bool isNullable = declaringType.IsConstructedGenericType && declaringType.GetGenericTypeDefinition() == typeof(Nullable<>); + + Dictionary map; + if (isNullable) + map = NullableActions.Map; + else if (declaringType == typeof(string)) + map = StringActions.Map; + else + return null; + + if (!(map.TryGetValue(methodBase.MetadataDefinitionMethod, out CustomMethodInvokerAction action))) + return null; + + ParameterInfo[] parameterInfos = methodBase.GetParametersNoCopy(); + Type[] parameterTypes = new Type[parameterInfos.Length]; + for (int i = 0; i < parameterInfos.Length; i++) + { + parameterTypes[i] = parameterInfos[i].ParameterType; + } + + InvokerOptions options = (methodBase.IsStatic || methodBase is ConstructorInfo || isNullable) ? InvokerOptions.AllowNullThis : InvokerOptions.None; + return new CustomMethodInvoker(declaringType, parameterTypes, options, action); + } + + private static void AddConstructor(this Dictionary map, Type declaringType, Type[] parameterTypes, CustomMethodInvokerAction action) + { + map.AddMethod(declaringType, ConstructorInfo.ConstructorName, parameterTypes, action); + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "GetConstructor/GetMethod being null is handled and expected.")] + private static void AddMethod(this Dictionary map, Type declaringType, string name, Type[] parameterTypes, CustomMethodInvokerAction action) + { + const BindingFlags bf = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.ExactBinding; + + MethodBase methodBase; + if (name == ConstructorInfo.ConstructorName) + { + methodBase = declaringType.GetConstructor(bf, null, parameterTypes, null); + } + else + { + methodBase = declaringType.GetMethod(name, 0, bf, null, parameterTypes, null); + } + + if (methodBase == null) + return; // If we got here, this specific member was not included in the metadata. + + Debug.Assert(methodBase == methodBase.MetadataDefinitionMethod); + map.Add(methodBase, action); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/IRuntimeMethodCommon.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/IRuntimeMethodCommon.cs new file mode 100644 index 00000000000000..0afd29406119a0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/IRuntimeMethodCommon.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + /// + /// These api's are to be implemented by parsing metadata. + /// + /// + internal interface IRuntimeMethodCommon where TRuntimeMethodCommon : IRuntimeMethodCommon, IEquatable + { + MethodAttributes Attributes { get; } + CallingConventions CallingConvention { get; } + + RuntimeTypeInfo ContextTypeInfo { get; } + RuntimeTypeInfo DeclaringType { get; } + RuntimeNamedTypeInfo DefiningTypeInfo { get; } + MethodImplAttributes MethodImplementationFlags { get; } + Module Module { get; } + + /// + /// Return an array of the types of the return value and parameter types. + /// + QSignatureTypeHandle[] QualifiedMethodSignature { get; } + IEnumerable TrueCustomAttributes { get; } + + /// + /// Parse the metadata that describes parameters, and for each parameter for which there is specific metadata + /// construct a RuntimeParameterInfo and fill in the VirtualRuntimeParamterInfoArray. Do remember to use contextMethod + /// instead of using the one internal to the RuntimeMethodCommon, as the runtime may pass in a subtly different context. + /// + void FillInMetadataDescribedParameters(ref VirtualRuntimeParameterInfoArray result, QSignatureTypeHandle[] parameterTypes, MethodBase contextMethod, TypeContext typeContext); + + string Name { get; } + + MethodInvoker GetUncachedMethodInvoker(RuntimeTypeInfo[] methodArguments, MemberInfo exceptionPertainant, out Exception exception); + + bool IsGenericMethodDefinition { get; } + int GenericParameterCount { get; } + + bool HasSameMetadataDefinitionAs(TRuntimeMethodCommon other); + + TRuntimeMethodCommon RuntimeMethodCommonOfUninstantiatedMethod { get; } + + RuntimeTypeInfo[] GetGenericTypeParametersWithSpecifiedOwningMethod(RuntimeNamedMethodInfo owningMethod); + + int MetadataToken { get; } + + /// + /// Retrieves the RuntimeMethodHandle for the given method. Non-null generic args should only be passed for instantiated + /// generic methods. + /// + RuntimeMethodHandle GetRuntimeMethodHandle(Type[] genericArgs); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/InvokerOptions.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/InvokerOptions.cs new file mode 100644 index 00000000000000..4f222d76da46f0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/InvokerOptions.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Runtime.MethodInfos +{ + [Flags] + internal enum InvokerOptions + { + None = 0x00000000, + AllowNullThis = 0x00000001, // Don't raise an exception if the "thisObject" parameter to Invoker is null. + DontWrapException = 0x00000002, // Don't wrap target exceptions in TargetInvocationException. + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/NativeFormat/NativeFormatMethodCommon.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/NativeFormat/NativeFormatMethodCommon.cs new file mode 100644 index 00000000000000..18ac71879e4721 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/NativeFormat/NativeFormatMethodCommon.cs @@ -0,0 +1,344 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeInfos.NativeFormat; +using System.Reflection.Runtime.ParameterInfos; +using System.Reflection.Runtime.ParameterInfos.NativeFormat; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Reflection.Core.Execution; +using Internal.Runtime; +using Internal.Runtime.CompilerServices; +using Internal.Runtime.TypeLoader; +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.MethodInfos.NativeFormat +{ + // + // Implements methods and properties common to RuntimeMethodInfo and RuntimeConstructorInfo. + // + internal struct NativeFormatMethodCommon : IRuntimeMethodCommon, IEquatable + { + public bool IsGenericMethodDefinition => GenericParameterCount != 0; + + public MethodInvoker GetUncachedMethodInvoker(RuntimeTypeInfo[] methodArguments, MemberInfo exceptionPertainant, out Exception exception) + { + return ReflectionCoreExecution.ExecutionEnvironment.GetMethodInvoker(DeclaringType, new QMethodDefinition(Reader, MethodHandle), methodArguments, exceptionPertainant, out exception); + } + + public QSignatureTypeHandle[] QualifiedMethodSignature + { + get + { + MethodSignature methodSignature = this.MethodSignature; + + QSignatureTypeHandle[] typeSignatures = new QSignatureTypeHandle[methodSignature.Parameters.Count + 1]; + typeSignatures[0] = new QSignatureTypeHandle(_reader, methodSignature.ReturnType, true); + int paramIndex = 1; + foreach (Handle parameterTypeSignatureHandle in methodSignature.Parameters) + { + typeSignatures[paramIndex++] = new QSignatureTypeHandle(_reader, parameterTypeSignatureHandle, true); + } + + return typeSignatures; + } + } + + public NativeFormatMethodCommon RuntimeMethodCommonOfUninstantiatedMethod + { + get + { + return new NativeFormatMethodCommon(MethodHandle, _definingTypeInfo, _definingTypeInfo); + } + } + + public void FillInMetadataDescribedParameters(ref VirtualRuntimeParameterInfoArray result, QSignatureTypeHandle[] typeSignatures, MethodBase contextMethod, TypeContext typeContext) + { + foreach (ParameterHandle parameterHandle in _method.Parameters) + { + Parameter parameterRecord = parameterHandle.GetParameter(_reader); + int index = parameterRecord.Sequence; + result[index] = + NativeFormatMethodParameterInfo.GetNativeFormatMethodParameterInfo( + contextMethod, + _methodHandle, + index - 1, + parameterHandle, + typeSignatures[index], + typeContext); + } + } + + public int GenericParameterCount => MethodHandle.GetMethod(Reader).GenericParameters.Count; + + public RuntimeTypeInfo[] GetGenericTypeParametersWithSpecifiedOwningMethod(RuntimeNamedMethodInfo owningMethod) + { + Method method = MethodHandle.GetMethod(Reader); + int genericParametersCount = method.GenericParameters.Count; + if (genericParametersCount == 0) + return Array.Empty(); + + RuntimeTypeInfo[] genericTypeParameters = new RuntimeTypeInfo[genericParametersCount]; + int i = 0; + foreach (GenericParameterHandle genericParameterHandle in method.GenericParameters) + { + RuntimeTypeInfo genericParameterType = NativeFormatRuntimeGenericParameterTypeInfoForMethods.GetRuntimeGenericParameterTypeInfoForMethods(owningMethod, Reader, genericParameterHandle); + genericTypeParameters[i++] = genericParameterType; + } + return genericTypeParameters; + } + + // + // methodHandle - the "tkMethodDef" that identifies the method. + // definingType - the "tkTypeDef" that defined the method (this is where you get the metadata reader that created methodHandle.) + // contextType - the type that supplies the type context (i.e. substitutions for generic parameters.) Though you + // get your raw information from "definingType", you report "contextType" as your DeclaringType property. + // + // For example: + // + // typeof(Foo<>).GetTypeInfo().DeclaredMembers + // + // The definingType and contextType are both Foo<> + // + // typeof(Foo).GetTypeInfo().DeclaredMembers + // + // The definingType is "Foo<,>" + // The contextType is "Foo" + // + // We don't report any DeclaredMembers for arrays or generic parameters so those don't apply. + // + public NativeFormatMethodCommon(MethodHandle methodHandle, NativeFormatRuntimeNamedTypeInfo definingTypeInfo, RuntimeTypeInfo contextTypeInfo) + { + _definingTypeInfo = definingTypeInfo; + _methodHandle = methodHandle; + _contextTypeInfo = contextTypeInfo; + _reader = definingTypeInfo.Reader; + _method = methodHandle.GetMethod(_reader); + } + + public MethodAttributes Attributes + { + get + { + return _method.Flags; + } + } + + public CallingConventions CallingConvention + { + get + { + return MethodSignature.CallingConvention; + } + } + + public RuntimeTypeInfo ContextTypeInfo + { + get + { + return _contextTypeInfo; + } + } + + public RuntimeTypeInfo DeclaringType + { + get + { + return _contextTypeInfo; + } + } + + public RuntimeNamedTypeInfo DefiningTypeInfo + { + get + { + return _definingTypeInfo; + } + } + + public MethodImplAttributes MethodImplementationFlags + { + get + { + return _method.ImplFlags; + } + } + + public Module Module + { + get + { + return _definingTypeInfo.Module; + } + } + + public int MetadataToken + { + get + { + throw new InvalidOperationException(SR.NoMetadataTokenAvailable); + } + } + + public RuntimeMethodHandle GetRuntimeMethodHandle(Type[] genericArgs) + { + Debug.Assert(genericArgs == null || genericArgs.Length > 0); + + RuntimeTypeHandle[] genericArgHandles; + if (genericArgs != null) + { + genericArgHandles = new RuntimeTypeHandle[genericArgs.Length]; + for (int i = 0; i < genericArgHandles.Length; i++) + genericArgHandles[i] = genericArgs[i].TypeHandle; + } + else + { + genericArgHandles = null; + } + + TypeManagerHandle typeManager = TypeLoaderEnvironment.Instance.ModuleList.GetModuleForMetadataReader(Reader); + + return TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents( + DeclaringType.TypeHandle, + Name, + RuntimeSignature.CreateFromMethodHandle(typeManager, MethodHandle.AsInt()), + genericArgHandles); + } + + // + // Returns the ParameterInfo objects for the method parameters and return parameter. + // + // The ParameterInfo objects will report "contextMethod" as their Member property and use it to get type variable information from + // the contextMethod's declaring type. The actual metadata, however, comes from "this." + // + // The methodTypeArguments provides the fill-ins for any method type variable elements in the parameter type signatures. + // + // Does not array-copy. + // + public RuntimeParameterInfo[] GetRuntimeParameters(MethodBase contextMethod, RuntimeTypeInfo[] methodTypeArguments, out RuntimeParameterInfo returnParameter) + { + MetadataReader reader = _reader; + TypeContext typeContext = contextMethod.DeclaringType.CastToRuntimeTypeInfo().TypeContext; + typeContext = new TypeContext(typeContext.GenericTypeArguments, methodTypeArguments); + MethodSignature methodSignature = this.MethodSignature; + Handle[] typeSignatures = new Handle[methodSignature.Parameters.Count + 1]; + typeSignatures[0] = methodSignature.ReturnType; + int paramIndex = 1; + foreach (Handle parameterTypeSignatureHandle in methodSignature.Parameters) + { + typeSignatures[paramIndex++] = parameterTypeSignatureHandle; + } + int count = typeSignatures.Length; + + VirtualRuntimeParameterInfoArray result = new VirtualRuntimeParameterInfoArray(count); + foreach (ParameterHandle parameterHandle in _method.Parameters) + { + Parameter parameterRecord = parameterHandle.GetParameter(_reader); + int index = parameterRecord.Sequence; + result[index] = + NativeFormatMethodParameterInfo.GetNativeFormatMethodParameterInfo( + contextMethod, + _methodHandle, + index - 1, + parameterHandle, + new QSignatureTypeHandle(reader, typeSignatures[index]), + typeContext); + } + for (int i = 0; i < count; i++) + { + if (result[i] == null) + { + result[i] = + RuntimeThinMethodParameterInfo.GetRuntimeThinMethodParameterInfo( + contextMethod, + i - 1, + new QSignatureTypeHandle(reader, typeSignatures[i]), + typeContext); + } + } + + returnParameter = result.First; + return result.Remainder; + } + + public string Name + { + get + { + return _method.Name.GetString(_reader); + } + } + + public MetadataReader Reader + { + get + { + return _reader; + } + } + + public MethodHandle MethodHandle + { + get + { + return _methodHandle; + } + } + + public bool HasSameMetadataDefinitionAs(NativeFormatMethodCommon other) + { + if (!(_reader == other._reader)) + return false; + if (!(_methodHandle.Equals(other._methodHandle))) + return false; + if (!(_definingTypeInfo.Equals(other._definingTypeInfo))) + return false; + return true; + } + + public IEnumerable TrueCustomAttributes => RuntimeCustomAttributeData.GetCustomAttributes(_reader, _method.CustomAttributes); + + public override bool Equals(object obj) + { + if (!(obj is NativeFormatMethodCommon other)) + return false; + return Equals(other); + } + + public bool Equals(NativeFormatMethodCommon other) + { + if (!(_reader == other._reader)) + return false; + if (!(_methodHandle.Equals(other._methodHandle))) + return false; + if (!(_contextTypeInfo.Equals(other._contextTypeInfo))) + return false; + return true; + } + + public override int GetHashCode() + { + return _methodHandle.GetHashCode() ^ _contextTypeInfo.GetHashCode(); + } + + private MethodSignature MethodSignature + { + get + { + return _method.Signature.GetMethodSignature(_reader); + } + } + + private readonly NativeFormatRuntimeNamedTypeInfo _definingTypeInfo; + private readonly MethodHandle _methodHandle; + private readonly RuntimeTypeInfo _contextTypeInfo; + + private readonly MetadataReader _reader; + + private readonly Method _method; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/OpenMethodInvoker.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/OpenMethodInvoker.cs new file mode 100644 index 00000000000000..945cc9e2a94d05 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/OpenMethodInvoker.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; + +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + internal sealed class OpenMethodInvoker : MethodInvoker + { + protected sealed override object Invoke(object thisObject, object[] arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException) + { + throw new InvalidOperationException(SR.Arg_UnboundGenParam); + } + + public sealed override Delegate CreateDelegate(RuntimeTypeHandle delegateType, object target, bool isStatic, bool isVirtual, bool isOpen) + { + throw new InvalidOperationException(SR.Arg_UnboundGenParam); + } + + public sealed override IntPtr LdFtnResult + { + get + { + throw new InvalidOperationException(SR.Arg_UnboundGenParam); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeClsIdNullaryConstructorInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeClsIdNullaryConstructorInfo.cs new file mode 100644 index 00000000000000..207fe4c68a8c3f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeClsIdNullaryConstructorInfo.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Globalization; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; +using System.Runtime.InteropServices; + +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + // + // This represents the synthetic nullary instance constructor for Types created by Type.GetTypeFromCLSID(). + // + internal sealed partial class RuntimeCLSIDNullaryConstructorInfo : RuntimeConstructorInfo + { + private RuntimeCLSIDNullaryConstructorInfo(RuntimeCLSIDTypeInfo declaringType) + { + _declaringType = declaringType; + } + + public sealed override MethodAttributes Attributes => MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; + public sealed override CallingConventions CallingConvention => CallingConventions.Standard | CallingConventions.HasThis; + public sealed override IEnumerable CustomAttributes => Empty.Enumerable; + public sealed override Type DeclaringType => _declaringType; + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // This logic is written to match CoreCLR's behavior. + return other is RuntimeCLSIDNullaryConstructorInfo; + } + + public sealed override bool Equals(object obj) + { + if (!(obj is RuntimeCLSIDNullaryConstructorInfo other)) + return false; + if (!(_declaringType.Equals(other._declaringType))) + return false; + return true; + } + + public sealed override int GetHashCode() => _declaringType.GetHashCode(); + + public sealed override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { +#if PROJECTN + if (parameters != null && parameters.Length != 0) + throw new TargetParameterCountException(); + + Guid clsid = _declaringType.GUID; + string server = _declaringType.Server; + IntPtr pItf = IntPtr.Zero; + try + { + pItf = McgMarshal.CoCreateInstanceEx(clsid, server); + + // CoCreateInstanceEx will throw exception if it fails to + // create an instance. + Debug.Assert(pItf != IntPtr.Zero); + + return Marshal.GetObjectForIUnknown(pItf); + } + finally + { + if (pItf != IntPtr.Zero) + Marshal.Release(pItf); + } +#else + throw new PlatformNotSupportedException(); +#endif + } + + public sealed override MethodBase MetadataDefinitionMethod { get { throw new NotSupportedException(); } } + + public sealed override int MetadataToken { get { throw new InvalidOperationException(); } } + + public sealed override RuntimeMethodHandle MethodHandle { get { throw new PlatformNotSupportedException(); } } + + public sealed override MethodImplAttributes MethodImplementationFlags => MethodImplAttributes.IL; + + public sealed override string Name => ConstructorInfo.ConstructorName; + + protected sealed override RuntimeParameterInfo[] RuntimeParameters => Array.Empty(); + + public sealed override string ToString() + { + // A constructor's "return type" is always System.Void and we don't want to allocate a ParameterInfo object to record that revelation. + // In deference to that, ComputeToString() lets us pass null as a synonym for "void." + return RuntimeMethodHelpers.ComputeToString(this, Array.Empty(), RuntimeParameters, returnParameter: null); + } + + protected sealed override MethodInvoker UncachedMethodInvoker + { + get + { + // If we got here, someone called MethodBase.Invoke() (not to be confused with ConstructorInfo.Invoke()). When invoked a constructor, that overload + // (which should never been exposed on MethodBase) reexecutes a constructor on an already constructed object. This is meaningless so leaving it PNSE + // unless there's a real-world app that does this. + throw new PlatformNotSupportedException(); + } + } + + private readonly RuntimeCLSIDTypeInfo _declaringType; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs new file mode 100644 index 00000000000000..e46d25dd91f5d6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs @@ -0,0 +1,221 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; + +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + // + // The runtime's implementation of non-constructor MethodInfo's that represent an open or closed costruction of a generic method. + // + internal sealed partial class RuntimeConstructedGenericMethodInfo : RuntimeMethodInfo + { + private RuntimeConstructedGenericMethodInfo(RuntimeNamedMethodInfo genericMethodDefinition, RuntimeTypeInfo[] genericTypeArguments) + { + _genericMethodDefinition = genericMethodDefinition; + _genericTypeArguments = genericTypeArguments; + } + + public sealed override MethodAttributes Attributes + { + get + { + return _genericMethodDefinition.Attributes; + } + } + + public sealed override CallingConventions CallingConvention + { + get + { + return _genericMethodDefinition.CallingConvention; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { + return _genericMethodDefinition.CustomAttributes; + } + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + return _genericMethodDefinition.HasSameMetadataDefinitionAs(other); + } + + public sealed override bool Equals(object obj) + { + if (!(obj is RuntimeConstructedGenericMethodInfo other)) + return false; + if (!_genericMethodDefinition.Equals(other._genericMethodDefinition)) + return false; + if (_genericTypeArguments.Length != other._genericTypeArguments.Length) + return false; + for (int i = 0; i < _genericTypeArguments.Length; i++) + { + if (!_genericTypeArguments[i].Equals(other._genericTypeArguments[i])) + return false; + } + return true; + } + + public sealed override int GetHashCode() + { + return _genericMethodDefinition.GetHashCode(); + } + + public sealed override int GenericParameterCount => _genericMethodDefinition.GenericParameterCount; + + public sealed override MethodInfo GetGenericMethodDefinition() + { + return _genericMethodDefinition; + } + + public sealed override bool IsConstructedGenericMethod + { + get + { + return true; + } + } + + public sealed override bool IsGenericMethod + { + get + { + return true; + } + } + + public sealed override bool IsGenericMethodDefinition + { + get + { + return false; + } + } + + public sealed override MethodBase MetadataDefinitionMethod + { + get + { + return _genericMethodDefinition.MetadataDefinitionMethod; + } + } + + public sealed override int MetadataToken + { + get + { + return _genericMethodDefinition.MetadataToken; + } + } + + [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] + [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + public sealed override MethodInfo MakeGenericMethod(params Type[] typeArguments) + { + throw new InvalidOperationException(SR.Format(SR.Arg_NotGenericMethodDefinition, this)); + } + + public sealed override MethodImplAttributes MethodImplementationFlags + { + get + { + return _genericMethodDefinition.MethodImplementationFlags; + } + } + + public sealed override Module Module + { + get + { + return _genericMethodDefinition.Module; + } + } + + public sealed override Type ReflectedType + { + get + { + return _genericMethodDefinition.ReflectedType; + } + } + + public sealed override string ToString() + { + return _genericMethodDefinition.ComputeToString(this); + } + + public sealed override RuntimeMethodHandle MethodHandle + { + get + { + return _genericMethodDefinition.GetRuntimeMethodHandle(GetGenericArguments()); + } + } + + protected sealed override MethodInvoker UncachedMethodInvoker + { + get + { + return _genericMethodDefinition.GetUncachedMethodInvoker(_genericTypeArguments, this); + } + } + + internal sealed override RuntimeTypeInfo RuntimeDeclaringType + { + get + { + return _genericMethodDefinition.RuntimeDeclaringType; + } + } + + internal sealed override RuntimeTypeInfo[] RuntimeGenericArgumentsOrParameters + { + get + { + return _genericTypeArguments; + } + } + + internal sealed override string RuntimeName + { + get + { + return _genericMethodDefinition.RuntimeName; + } + } + + internal sealed override RuntimeParameterInfo[] GetRuntimeParameters(RuntimeMethodInfo contextMethod, out RuntimeParameterInfo returnParameter) + { + return _genericMethodDefinition.GetRuntimeParameters(this, out returnParameter); + } + + internal sealed override RuntimeMethodInfo WithReflectedTypeSetToDeclaringType + { + get + { + if (_genericMethodDefinition.ReflectedType.Equals(_genericMethodDefinition.DeclaringType)) + return this; + + RuntimeNamedMethodInfo newGenericMethodDefinition = (RuntimeNamedMethodInfo)(_genericMethodDefinition.WithReflectedTypeSetToDeclaringType); + return RuntimeConstructedGenericMethodInfo.GetRuntimeConstructedGenericMethodInfo(newGenericMethodDefinition, _genericTypeArguments); + } + } + + private readonly RuntimeNamedMethodInfo _genericMethodDefinition; + private readonly RuntimeTypeInfo[] _genericTypeArguments; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructorInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructorInfo.cs new file mode 100644 index 00000000000000..7880071e01c310 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructorInfo.cs @@ -0,0 +1,197 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; + +using Internal.Reflection.Core.Execution; + +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.MethodInfos +{ + // + // The runtime's implementation of ConstructorInfo. + // + internal abstract partial class RuntimeConstructorInfo : ConstructorInfo + { + public abstract override MethodAttributes Attributes { get; } + + public abstract override CallingConventions CallingConvention { get; } + + public sealed override bool ContainsGenericParameters + { + get + { + return DeclaringType.ContainsGenericParameters; + } + } + + public abstract override IEnumerable CustomAttributes { get; } + + public abstract override Type DeclaringType { get; } + + public sealed override Type[] GetGenericArguments() + { + // Constructors cannot be generic. Desktop compat dictates that We throw NotSupported rather than returning a 0-length array. + throw new NotSupportedException(); + } + + [RequiresUnreferencedCode("Trimming may change method bodies. For example it can change some instructions, remove branches or local variables.")] + public sealed override MethodBody GetMethodBody() + { + throw new PlatformNotSupportedException(); + } + + public sealed override ParameterInfo[] GetParameters() + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodBase_GetParameters(this); +#endif + + RuntimeParameterInfo[] parameters = RuntimeParameters; + if (parameters.Length == 0) + return Array.Empty(); + ParameterInfo[] result = new ParameterInfo[parameters.Length]; + for (int i = 0; i < result.Length; i++) + result[i] = parameters[i]; + return result; + } + + public sealed override ParameterInfo[] GetParametersNoCopy() + { + return RuntimeParameters; + } + + public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); + + public abstract override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture); + + [DebuggerGuidedStepThrough] + public sealed override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodBase_Invoke(this, obj, parameters); +#endif + if (parameters == null) + parameters = Array.Empty(); + MethodInvoker methodInvoker; + try + { + methodInvoker = this.MethodInvoker; + } + catch (Exception) + { + // + // Project N compat note: On the desktop, ConstructorInfo.Invoke(Object[]) specifically forbids invoking static constructors (and + // for us, that check is embedded inside the MethodInvoker property call.) Howver, MethodBase.Invoke(Object, Object[]) allows it. This was + // probably an oversight on the desktop. We choose not to support this loophole on Project N for the following reasons: + // + // 1. The Project N toolchain aggressively replaces static constructors with static initialization data whenever possible. + // So the static constructor may no longer exist. + // + // 2. Invoking the static constructor through Reflection is not very useful as it invokes the static constructor whether or not + // it was already run. Since static constructors are specifically one-shot deals, this will almost certainly mess up the + // type's internal assumptions. + // + + if (this.IsStatic) + throw new PlatformNotSupportedException(SR.Acc_NotClassInit); + throw; + } + + object result = methodInvoker.Invoke(obj, parameters, binder, invokeAttr, culture); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + return result; + } + + public abstract override MethodBase MetadataDefinitionMethod { get; } + + public abstract override int MetadataToken + { + get; + } + + public sealed override Module Module + { + get + { + return DeclaringType.Module; + } + } + + public sealed override bool IsConstructedGenericMethod + { + get + { + return false; + } + } + + public sealed override bool IsGenericMethod + { + get + { + return false; + } + } + + public sealed override bool IsGenericMethodDefinition + { + get + { + return false; + } + } + + public abstract override MethodImplAttributes MethodImplementationFlags { get; } + + public abstract override string Name { get; } + + public abstract override bool Equals(object obj); + + public abstract override int GetHashCode(); + + public sealed override Type ReflectedType + { + get + { + // Constructors are always looked up as if BindingFlags.DeclaredOnly were specified. Thus, the ReflectedType will always be the DeclaringType. + return DeclaringType; + } + } + + public abstract override string ToString(); + + public abstract override RuntimeMethodHandle MethodHandle { get; } + + protected MethodInvoker MethodInvoker + { + get + { + if (_lazyMethodInvoker == null) + { + _lazyMethodInvoker = UncachedMethodInvoker; + } + return _lazyMethodInvoker; + } + } + + internal IntPtr LdFtnResult => MethodInvoker.LdFtnResult; + + protected abstract RuntimeParameterInfo[] RuntimeParameters { get; } + + protected abstract MethodInvoker UncachedMethodInvoker { get; } + + private volatile MethodInvoker _lazyMethodInvoker; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeDummyMethodInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeDummyMethodInfo.cs new file mode 100644 index 00000000000000..9220b3a0bb6ebb --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeDummyMethodInfo.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Runtime.ParameterInfos; +using System.Reflection.Runtime.TypeInfos; +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + // + // Singleton MethodInfo used as a sentinel for _lazy* latches where we can't use "null" as a sentinel. + // + internal sealed class RuntimeDummyMethodInfo : RuntimeNamedMethodInfo + { + private RuntimeDummyMethodInfo() { } + + public sealed override bool Equals(object obj) => object.ReferenceEquals(this, obj); + public sealed override int GetHashCode() => 1; + public sealed override string ToString() => string.Empty; + + public sealed override MethodInfo GetGenericMethodDefinition() { throw NotImplemented.ByDesign; } + [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] + [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + public sealed override MethodInfo MakeGenericMethod(params Type[] typeArguments) { throw NotImplemented.ByDesign; } + public sealed override MethodAttributes Attributes { get { throw NotImplemented.ByDesign; } } + public sealed override Type ReflectedType { get { throw NotImplemented.ByDesign; } } + public sealed override CallingConventions CallingConvention { get { throw NotImplemented.ByDesign; } } + public sealed override IEnumerable CustomAttributes { get { throw NotImplemented.ByDesign; } } + public sealed override bool IsConstructedGenericMethod { get { throw NotImplemented.ByDesign; } } + public sealed override bool IsGenericMethod { get { throw NotImplemented.ByDesign; } } + public sealed override bool IsGenericMethodDefinition { get { throw NotImplemented.ByDesign; } } + public sealed override int GenericParameterCount { get { throw NotImplemented.ByDesign; } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) { throw NotImplemented.ByDesign; } + public sealed override MethodImplAttributes MethodImplementationFlags { get { throw NotImplemented.ByDesign; } } + public sealed override Module Module { get { throw NotImplemented.ByDesign; } } + public sealed override MethodBase MetadataDefinitionMethod { get { throw NotImplemented.ByDesign; } } + public sealed override int MetadataToken { get { throw NotImplemented.ByDesign; } } + public sealed override RuntimeMethodHandle MethodHandle { get { throw NotImplemented.ByDesign; } } + protected sealed override MethodInvoker UncachedMethodInvoker { get { throw NotImplemented.ByDesign; } } + internal sealed override RuntimeParameterInfo[] GetRuntimeParameters(RuntimeMethodInfo contextMethod, out RuntimeParameterInfo returnParameter) { throw NotImplemented.ByDesign; } + internal sealed override RuntimeTypeInfo RuntimeDeclaringType { get { throw NotImplemented.ByDesign; } } + internal sealed override string RuntimeName { get { throw NotImplemented.ByDesign; } } + internal sealed override RuntimeTypeInfo[] RuntimeGenericArgumentsOrParameters { get { throw NotImplemented.ByDesign; } } + + protected internal sealed override string ComputeToString(RuntimeMethodInfo contextMethod) { throw NotImplemented.ByDesign; } + internal sealed override MethodInvoker GetUncachedMethodInvoker(RuntimeTypeInfo[] methodArguments, MemberInfo exceptionPertainant) { throw NotImplemented.ByDesign; } + internal sealed override RuntimeMethodHandle GetRuntimeMethodHandle(Type[] genericArgs) { throw NotImplemented.ByDesign; } + internal sealed override RuntimeMethodInfo WithReflectedTypeSetToDeclaringType { get { throw NotImplemented.ByDesign; } } + public static readonly RuntimeDummyMethodInfo Instance = new RuntimeDummyMethodInfo(); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodHelpers.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodHelpers.cs new file mode 100644 index 00000000000000..2f17ba860e9493 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodHelpers.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + internal static class RuntimeMethodHelpers + { + // + // Returns the ParameterInfo objects for the method parameters and return parameter. + // + // The ParameterInfo objects will report "contextMethod" as their Member property and use it to get type variable information from + // the contextMethod's declaring type. The actual metadata, however, comes from "this." + // + // The methodTypeArguments provides the fill-ins for any method type variable elements in the parameter type signatures. + // + // Does not array-copy. + // + internal static RuntimeParameterInfo[] GetRuntimeParameters(ref TRuntimeMethodCommon runtimeMethodCommon, MethodBase contextMethod, RuntimeTypeInfo[] methodTypeArguments, out RuntimeParameterInfo returnParameter) + where TRuntimeMethodCommon : IRuntimeMethodCommon, IEquatable + { + TypeContext typeContext = contextMethod.DeclaringType.CastToRuntimeTypeInfo().TypeContext; + typeContext = new TypeContext(typeContext.GenericTypeArguments, methodTypeArguments); + QSignatureTypeHandle[] typeSignatures = runtimeMethodCommon.QualifiedMethodSignature; + int count = typeSignatures.Length; + + VirtualRuntimeParameterInfoArray result = new VirtualRuntimeParameterInfoArray(count); + runtimeMethodCommon.FillInMetadataDescribedParameters(ref result, typeSignatures, contextMethod, typeContext); + + for (int i = 0; i < count; i++) + { + if (result[i] == null) + { + result[i] = + RuntimeThinMethodParameterInfo.GetRuntimeThinMethodParameterInfo( + contextMethod, + i - 1, + typeSignatures[i], + typeContext); + } + } + + returnParameter = result.First; + return result.Remainder; + } + + // Compute the ToString() value in a pay-to-play-safe way. + internal static string ComputeToString(ref TRuntimeMethodCommon runtimeMethodCommon, MethodBase contextMethod, RuntimeTypeInfo[] methodTypeArguments) + where TRuntimeMethodCommon : IRuntimeMethodCommon, IEquatable + { + RuntimeParameterInfo returnParameter; + RuntimeParameterInfo[] parameters = GetRuntimeParameters(ref runtimeMethodCommon, contextMethod, methodTypeArguments, out returnParameter); + return ComputeToString(contextMethod, methodTypeArguments, parameters, returnParameter); + } + + // Used by method and property ToString() methods to display the list of parameter types. Replicates the behavior of MethodBase.ConstructParameters() + // but in a pay-to-play-safe way. + internal static string ComputeParametersString(RuntimeParameterInfo[] parameters) + { + StringBuilder sb = new StringBuilder(30); + for (int i = 0; i < parameters.Length; i++) + { + if (i != 0) + sb.Append(", "); + string parameterTypeString = parameters[i].ParameterTypeString; + + // Legacy: Why use "ByRef" for by ref parameters? What language is this? + // VB uses "ByRef" but it should precede (not follow) the parameter name. + // Why don't we just use "&"? + if (parameterTypeString.EndsWith("&")) + { + sb.Append(parameterTypeString, 0, parameterTypeString.Length - 1); + sb.Append(" ByRef"); + } + else + { + sb.Append(parameterTypeString); + } + } + return sb.ToString(); + } + + internal static string ComputeToString(MethodBase contextMethod, RuntimeTypeInfo[] methodTypeArguments, RuntimeParameterInfo[] parameters, RuntimeParameterInfo returnParameter) + { + StringBuilder sb = new StringBuilder(30); + sb.Append(returnParameter == null ? "Void" : returnParameter.ParameterTypeString); // ConstructorInfos allowed to pass in null rather than craft a ReturnParameterInfo that's always of type void. + sb.Append(' '); + sb.Append(contextMethod.Name); + if (methodTypeArguments.Length != 0) + { + string sep = ""; + sb.Append('['); + foreach (RuntimeTypeInfo methodTypeArgument in methodTypeArguments) + { + sb.Append(sep); + sep = ","; + string name = methodTypeArgument.InternalNameIfAvailable; + if (name == null) + name = Type.DefaultTypeNameWhenMissingMetadata; + sb.Append(methodTypeArgument.Name); + } + sb.Append(']'); + } + sb.Append('('); + sb.Append(ComputeParametersString(parameters)); + sb.Append(')'); + + return sb.ToString(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodInfo.cs new file mode 100644 index 00000000000000..27705d3a044ef0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodInfo.cs @@ -0,0 +1,537 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; +using System.Reflection.Runtime.BindingFlagSupport; + +using Internal.Reflection.Core.Execution; +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.MethodInfos +{ + // + // Abstract base class for RuntimeNamedMethodInfo, RuntimeConstructedGenericMethodInfo. + // + [DebuggerDisplay("{_debugName}")] + internal abstract partial class RuntimeMethodInfo : MethodInfo, ITraceableTypeMember + { + protected RuntimeMethodInfo() + { + } + + public abstract override MethodAttributes Attributes + { + get; + } + + public abstract override CallingConventions CallingConvention + { + get; + } + + public sealed override bool ContainsGenericParameters + { + get + { + if (DeclaringType.ContainsGenericParameters) + return true; + + if (!IsGenericMethod) + return false; + + Type[] pis = GetGenericArguments(); + for (int i = 0; i < pis.Length; i++) + { + if (pis[i].ContainsGenericParameters) + return true; + } + + return false; + } + } + + // V4.5 api - Creates open delegates over static or instance methods. + public sealed override Delegate CreateDelegate(Type delegateType) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodInfo_CreateDelegate(this, delegateType); +#endif + + return CreateDelegateWorker(delegateType, null, allowClosed: false); + } + + // V4.5 api - Creates open or closed delegates over static or instance methods. + public sealed override Delegate CreateDelegate(Type delegateType, object target) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodInfo_CreateDelegate(this, delegateType, target); +#endif + + return CreateDelegateWorker(delegateType, target, allowClosed: true); + } + + private Delegate CreateDelegateWorker(Type delegateType, object target, bool allowClosed) + { + if (delegateType == null) + throw new ArgumentNullException(nameof(delegateType)); + + if (!(delegateType is RuntimeTypeInfo runtimeDelegateType)) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(delegateType)); + + if (!runtimeDelegateType.IsDelegate) + throw new ArgumentException(SR.Arg_MustBeDelegate); + + Delegate result = CreateDelegateNoThrowOnBindFailure(runtimeDelegateType, target, allowClosed); + if (result == null) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + return result; + } + + public abstract override IEnumerable CustomAttributes + { + get; + } + + public sealed override Type DeclaringType + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodBase_DeclaringType(this); +#endif + + return this.RuntimeDeclaringType; + } + } + + public abstract override bool Equals(object obj); + + public abstract override int GetHashCode(); + + public sealed override MethodInfo GetBaseDefinition() + { + // This check is for compatibility. Yes, it happens before we normalize constructed generic methods back to their backing definition. + Type declaringType = DeclaringType; + if (!IsVirtual || IsStatic || declaringType == null || declaringType.IsInterface) + return this; + + MethodInfo method = this; + + // For compat: Remove any instantation on generic methods. + if (method.IsConstructedGenericMethod) + method = method.GetGenericMethodDefinition(); + + while (true) + { + MethodInfo next = method.GetImplicitlyOverriddenBaseClassMember(); + if (next == null) + return ((RuntimeMethodInfo)method).WithReflectedTypeSetToDeclaringType; + + method = next; + } + } + + public sealed override Type[] GetGenericArguments() + { + return RuntimeGenericArgumentsOrParameters.CloneTypeArray(); + } + + public abstract override int GenericParameterCount { get; } + + public abstract override MethodInfo GetGenericMethodDefinition(); + + [RequiresUnreferencedCode("Trimming may change method bodies. For example it can change some instructions, remove branches or local variables.")] + public sealed override MethodBody GetMethodBody() + { + throw new PlatformNotSupportedException(); + } + + public sealed override ParameterInfo[] GetParameters() + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodBase_GetParameters(this); +#endif + + RuntimeParameterInfo[] runtimeParameterInfos = RuntimeParameters; + if (runtimeParameterInfos.Length == 0) + return Array.Empty(); + ParameterInfo[] result = new ParameterInfo[runtimeParameterInfos.Length]; + for (int i = 0; i < result.Length; i++) + result[i] = runtimeParameterInfos[i]; + return result; + } + + public sealed override ParameterInfo[] GetParametersNoCopy() + { + return RuntimeParameters; + } + + public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); + + [DebuggerGuidedStepThroughAttribute] + public sealed override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodBase_Invoke(this, obj, parameters); +#endif + if (parameters == null) + parameters = Array.Empty(); + MethodInvoker methodInvoker = this.MethodInvoker; + object result = methodInvoker.Invoke(obj, parameters, binder, invokeAttr, culture); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + return result; + } + + public abstract override bool IsConstructedGenericMethod + { + get; + } + + public abstract override bool IsGenericMethod + { + get; + } + + public abstract override bool IsGenericMethodDefinition + { + get; + } + + [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] + [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + public abstract override MethodInfo MakeGenericMethod(params Type[] typeArguments); + + public abstract override MethodBase MetadataDefinitionMethod { get; } + + public abstract override int MetadataToken + { + get; + } + + public abstract override MethodImplAttributes MethodImplementationFlags + { + get; + } + + public abstract override Module Module + { + get; + } + + public sealed override string Name + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodBase_Name(this); +#endif + return this.RuntimeName; + } + } + + public abstract override Type ReflectedType { get; } + + public sealed override ParameterInfo ReturnParameter + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodInfo_ReturnParameter(this); +#endif + + return this.RuntimeReturnParameter; + } + } + + public sealed override Type ReturnType + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodInfo_ReturnType(this); +#endif + + return ReturnParameter.ParameterType; + } + } + + public abstract override string ToString(); + + public abstract override RuntimeMethodHandle MethodHandle { get; } + + Type ITraceableTypeMember.ContainingType + { + get + { + return this.RuntimeDeclaringType; + } + } + + string ITraceableTypeMember.MemberName + { + get + { + return this.RuntimeName; + } + } + + internal abstract RuntimeTypeInfo RuntimeDeclaringType + { + get; + } + + internal abstract string RuntimeName + { + get; + } + + internal abstract RuntimeMethodInfo WithReflectedTypeSetToDeclaringType { get; } + + protected abstract MethodInvoker UncachedMethodInvoker { get; } + + // + // The non-public version of MethodInfo.GetGenericArguments() (does not array-copy and has a more truthful name.) + // + internal abstract RuntimeTypeInfo[] RuntimeGenericArgumentsOrParameters { get; } + + internal abstract RuntimeParameterInfo[] GetRuntimeParameters(RuntimeMethodInfo contextMethod, out RuntimeParameterInfo returnParameter); + + // + // The non-public version of MethodInfo.GetParameters() (does not array-copy.) + // + internal RuntimeParameterInfo[] RuntimeParameters + { + get + { + RuntimeParameterInfo[] parameters = _lazyParameters; + if (parameters == null) + { + RuntimeParameterInfo returnParameter; + parameters = _lazyParameters = GetRuntimeParameters(this, out returnParameter); + _lazyReturnParameter = returnParameter; // Opportunistically initialize the _lazyReturnParameter latch as well. + } + return parameters; + } + } + + internal RuntimeParameterInfo RuntimeReturnParameter + { + get + { + RuntimeParameterInfo returnParameter = _lazyReturnParameter; + if (returnParameter == null) + { + // Though the returnParameter is our primary objective, we can opportunistically initialize the _lazyParameters latch too. + _lazyParameters = GetRuntimeParameters(this, out returnParameter); + _lazyReturnParameter = returnParameter; + } + return returnParameter; + } + } + + private volatile RuntimeParameterInfo[] _lazyParameters; + private volatile RuntimeParameterInfo _lazyReturnParameter; + + internal MethodInvoker MethodInvoker + { + get + { + MethodInvoker methodInvoker = _lazyMethodInvoker; + if (methodInvoker == null) + { + if (ReturnType.IsByRef) + { + // The invoker is going to dereference and box (for structs) the result of the invocation + // on behalf of the caller. Can't box byref-like types and can't box void. + if (ReturnType.GetElementType().IsByRefLike || ReturnType.GetElementType() == typeof(void)) + throw new NotSupportedException(); + } + methodInvoker = _lazyMethodInvoker = this.UncachedMethodInvoker; + } + return methodInvoker; + } + } + + internal IntPtr LdFtnResult => MethodInvoker.LdFtnResult; + + private volatile MethodInvoker _lazyMethodInvoker; + + /// + /// Common CreateDelegate worker. NOTE: If the method signature is not compatible, this method returns null rather than throwing an ArgumentException. + /// This is needed to support the api overloads that have a "throwOnBindFailure" parameter. + /// + internal Delegate CreateDelegateNoThrowOnBindFailure(RuntimeTypeInfo runtimeDelegateType, object target, bool allowClosed) + { + Debug.Assert(runtimeDelegateType.IsDelegate); + + ExecutionEnvironment executionEnvironment = ReflectionCoreExecution.ExecutionEnvironment; + MethodInfo invokeMethod = runtimeDelegateType.GetInvokeMethod(); + + // Make sure the return type is assignment-compatible. + Type expectedReturnType = ReturnParameter.ParameterType; + Type actualReturnType = invokeMethod.ReturnParameter.ParameterType; + if (!IsAssignableFrom(executionEnvironment, actualReturnType, expectedReturnType)) + return null; + if (expectedReturnType.IsValueType && !actualReturnType.IsValueType) + { + // For value type returning methods, conversions between enums and primitives are allowed (and screened by the above call to IsAssignableFrom) + // but conversions to Object or interfaces implemented by the value type are not. + return null; + } + + IList delegateParameters = invokeMethod.GetParametersNoCopy(); + IList targetParameters = this.GetParametersNoCopy(); + IEnumerator delegateParameterEnumerator = delegateParameters.GetEnumerator(); + IEnumerator targetParameterEnumerator = targetParameters.GetEnumerator(); + + bool isStatic = this.IsStatic; + bool isOpen; + if (isStatic) + { + if (delegateParameters.Count == targetParameters.Count) + { + // Open static: This is the "typical" case of calling a static method. + isOpen = true; + if (target != null) + return null; + } + else + { + // Closed static: This is the "weird" v2.0 case where the delegate is closed over the target method's first parameter. + // (it make some kinda sense if you think of extension methods.) + if (!allowClosed) + return null; + isOpen = false; + if (!targetParameterEnumerator.MoveNext()) + return null; + if (target != null && !IsAssignableFrom(executionEnvironment, targetParameterEnumerator.Current.ParameterType, target.GetType())) + return null; + } + } + else + { + if (delegateParameters.Count == targetParameters.Count) + { + // Closed instance: This is the "typical" case of invoking an instance method. + isOpen = false; + if (!allowClosed) + return null; + if (target != null && !IsAssignableFrom(executionEnvironment, this.DeclaringType, target.GetType())) + return null; + } + else + { + // Open instance: This is the "weird" v2.0 case where the delegate has a leading extra parameter that's assignable to the target method's + // declaring type. + if (!delegateParameterEnumerator.MoveNext()) + return null; + isOpen = true; + Type firstParameterOfMethodType = this.DeclaringType; + if (firstParameterOfMethodType.IsValueType) + firstParameterOfMethodType = firstParameterOfMethodType.MakeByRefType(); + + if (!IsAssignableFrom(executionEnvironment, firstParameterOfMethodType, delegateParameterEnumerator.Current.ParameterType)) + return null; + if (target != null) + return null; + } + } + + // Verify that the parameters that the delegate and method have in common are assignment-compatible. + while (delegateParameterEnumerator.MoveNext()) + { + if (!targetParameterEnumerator.MoveNext()) + return null; + if (!IsAssignableFrom(executionEnvironment, targetParameterEnumerator.Current.ParameterType, delegateParameterEnumerator.Current.ParameterType)) + return null; + } + if (targetParameterEnumerator.MoveNext()) + return null; + + return CreateDelegateWithoutSignatureValidation(runtimeDelegateType, target, isStatic: isStatic, isOpen: isOpen); + } + + internal Delegate CreateDelegateWithoutSignatureValidation(Type delegateType, object target, bool isStatic, bool isOpen) + { + return MethodInvoker.CreateDelegate(delegateType.TypeHandle, target, isStatic: isStatic, isVirtual: false, isOpen: isOpen); + } + + private static bool IsAssignableFrom(ExecutionEnvironment executionEnvironment, Type dstType, Type srcType) + { + // byref types do not have a TypeHandle so we must treat these separately. + if (dstType.IsByRef && srcType.IsByRef) + { + if (!dstType.Equals(srcType)) + return false; + } + + // Enable pointers (which don't necessarily have typehandles). todo:be able to handle intptr <-> pointer, check if we need to handle + // casts via pointer where the pointer types aren't identical + if (dstType.Equals(srcType)) + { + return true; + } + + // If assignment compatible in the normal way, allow + if (executionEnvironment.IsAssignableFrom(dstType.TypeHandle, srcType.TypeHandle)) + { + return true; + } + + // they are not compatible yet enums can go into each other if their underlying element type is the same + // or into their equivalent integral type + Type dstTypeUnderlying = dstType; + if (dstType.IsEnum) + { + dstTypeUnderlying = Enum.GetUnderlyingType(dstType); + } + Type srcTypeUnderlying = srcType; + if (srcType.IsEnum) + { + srcTypeUnderlying = Enum.GetUnderlyingType(srcType); + } + if (dstTypeUnderlying.Equals(srcTypeUnderlying)) + { + return true; + } + + return false; + } + + protected RuntimeMethodInfo WithDebugName() + { + bool populateDebugNames = DeveloperExperienceState.DeveloperExperienceModeEnabled; +#if DEBUG + populateDebugNames = true; +#endif + if (!populateDebugNames) + return this; + + if (_debugName == null) + { + _debugName = "Constructing..."; // Protect against any inadvertent reentrancy. + _debugName = ((ITraceableTypeMember)this).MemberName; + } + return this; + } + + private string _debugName; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs new file mode 100644 index 00000000000000..79c2810c94f3c8 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs @@ -0,0 +1,363 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Reflection.Core.Execution; + +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.MethodInfos +{ + internal abstract class RuntimeNamedMethodInfo : RuntimeMethodInfo + { + protected internal abstract string ComputeToString(RuntimeMethodInfo contextMethod); + internal abstract MethodInvoker GetUncachedMethodInvoker(RuntimeTypeInfo[] methodArguments, MemberInfo exceptionPertainant); + internal abstract RuntimeMethodHandle GetRuntimeMethodHandle(Type[] methodArguments); + } + + // + // The runtime's implementation of non-constructor MethodInfo's that represent a method definition. + // + internal sealed partial class RuntimeNamedMethodInfo : RuntimeNamedMethodInfo + where TRuntimeMethodCommon : IRuntimeMethodCommon, IEquatable + { + // + // methodHandle - the "tkMethodDef" that identifies the method. + // definingType - the "tkTypeDef" that defined the method (this is where you get the metadata reader that created methodHandle.) + // contextType - the type that supplies the type context (i.e. substitutions for generic parameters.) Though you + // get your raw information from "definingType", you report "contextType" as your DeclaringType property. + // + // For example: + // + // typeof(Foo<>).GetTypeInfo().DeclaredMembers + // + // The definingType and contextType are both Foo<> + // + // typeof(Foo).GetTypeInfo().DeclaredMembers + // + // The definingType is "Foo<,>" + // The contextType is "Foo" + // + // We don't report any DeclaredMembers for arrays or generic parameters so those don't apply. + // + private RuntimeNamedMethodInfo(TRuntimeMethodCommon common, RuntimeTypeInfo reflectedType) + : base() + { + _common = common; + _reflectedType = reflectedType; + } + + public sealed override MethodAttributes Attributes + { + get + { + return _common.Attributes; + } + } + + public sealed override CallingConventions CallingConvention + { + get + { + return _common.CallingConvention; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodBase_CustomAttributes(this); +#endif + + foreach (CustomAttributeData cad in _common.TrueCustomAttributes) + { + yield return cad; + } + + MethodImplAttributes implAttributes = _common.MethodImplementationFlags; + if (0 != (implAttributes & MethodImplAttributes.PreserveSig)) + yield return new RuntimePseudoCustomAttributeData(typeof(PreserveSigAttribute), null); + } + } + + public sealed override MethodInfo GetGenericMethodDefinition() + { + if (IsGenericMethodDefinition) + return this; + throw new InvalidOperationException(); + } + + public sealed override bool IsConstructedGenericMethod + { + get + { + return false; + } + } + + public sealed override bool IsGenericMethod + { + get + { + return IsGenericMethodDefinition; + } + } + + public sealed override bool IsGenericMethodDefinition + { + get + { + return _common.IsGenericMethodDefinition; + } + } + + public sealed override int GenericParameterCount => _common.GenericParameterCount; + + [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] + [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + public sealed override MethodInfo MakeGenericMethod(params Type[] typeArguments) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodInfo_MakeGenericMethod(this, typeArguments); +#endif + + if (typeArguments == null) + throw new ArgumentNullException(nameof(typeArguments)); + if (GenericTypeParameters.Length == 0) + throw new InvalidOperationException(SR.Format(SR.Arg_NotGenericMethodDefinition, this)); + RuntimeTypeInfo[] genericTypeArguments = new RuntimeTypeInfo[typeArguments.Length]; + for (int i = 0; i < typeArguments.Length; i++) + { + Type typeArgument = typeArguments[i]; + if (typeArgument == null) + throw new ArgumentNullException(); + + if (typeArgument is not RuntimeType) + throw new ArgumentException(SR.Format(SR.Reflection_CustomReflectionObjectsNotSupported, typeArguments[i]), "typeArguments[" + i + "]"); // Not a runtime type. + + if (typeArgument.IsByRefLike) + throw new BadImageFormatException(SR.CannotUseByRefLikeTypeInInstantiation); + + genericTypeArguments[i] = typeArgument.CastToRuntimeTypeInfo(); + } + if (typeArguments.Length != GenericTypeParameters.Length) + throw new ArgumentException(SR.Format(SR.Argument_NotEnoughGenArguments, typeArguments.Length, GenericTypeParameters.Length)); + RuntimeMethodInfo methodInfo = (RuntimeMethodInfo)RuntimeConstructedGenericMethodInfo.GetRuntimeConstructedGenericMethodInfo(this, genericTypeArguments); + MethodInvoker methodInvoker = methodInfo.MethodInvoker; // For compatibility with other Make* apis, trigger any MissingMetadataExceptions now rather than later. + return methodInfo; + } + + public sealed override MethodBase MetadataDefinitionMethod + { + get + { + return RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(_common.RuntimeMethodCommonOfUninstantiatedMethod, _common.DefiningTypeInfo); + } + } + + public sealed override MethodImplAttributes MethodImplementationFlags + { + get + { + return _common.MethodImplementationFlags; + } + } + + public sealed override Module Module + { + get + { + return _common.Module; + } + } + + public sealed override Type ReflectedType + { + get + { + return _reflectedType; + } + } + + public sealed override int MetadataToken + { + get + { + return _common.MetadataToken; + } + } + + public sealed override string ToString() + { + return ComputeToString(this); + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // Do not rewrite as a call to IsConstructedGenericMethod - we haven't yet established that "other" is a runtime-implemented member yet! + if (other is RuntimeConstructedGenericMethodInfo otherConstructedGenericMethod) + other = otherConstructedGenericMethod.GetGenericMethodDefinition(); + + if (!(other is RuntimeNamedMethodInfo otherMethod)) + return false; + + return _common.HasSameMetadataDefinitionAs(otherMethod._common); + } + + public sealed override bool Equals(object obj) + { + if (!(obj is RuntimeNamedMethodInfo other)) + return false; + if (!_common.Equals(other._common)) + return false; + if (!(_reflectedType.Equals(other._reflectedType))) + return false; + return true; + } + + public sealed override int GetHashCode() + { + return _common.GetHashCode(); + } + + public sealed override RuntimeMethodHandle MethodHandle => GetRuntimeMethodHandle(null); + + protected internal sealed override string ComputeToString(RuntimeMethodInfo contextMethod) + { + return RuntimeMethodHelpers.ComputeToString(ref _common, contextMethod, contextMethod.RuntimeGenericArgumentsOrParameters); + } + + internal sealed override RuntimeTypeInfo[] RuntimeGenericArgumentsOrParameters + { + get + { + return this.GenericTypeParameters; + } + } + + internal sealed override RuntimeParameterInfo[] GetRuntimeParameters(RuntimeMethodInfo contextMethod, out RuntimeParameterInfo returnParameter) + { + return RuntimeMethodHelpers.GetRuntimeParameters(ref _common, contextMethod, contextMethod.RuntimeGenericArgumentsOrParameters, out returnParameter); + } + + internal sealed override RuntimeTypeInfo RuntimeDeclaringType + { + get + { + return _common.DeclaringType; + } + } + + internal sealed override string RuntimeName + { + get + { + return _common.Name; + } + } + + internal sealed override RuntimeMethodInfo WithReflectedTypeSetToDeclaringType + { + get + { + if (_reflectedType.Equals(_common.DefiningTypeInfo)) + return this; + + return RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(_common, _common.ContextTypeInfo); + } + } + + private RuntimeTypeInfo[] GenericTypeParameters + { + get + { + RuntimeNamedMethodInfo owningMethod = this; + if (DeclaringType.IsConstructedGenericType) + { + // Desktop compat: Constructed generic types and their generic type definitions share the same Type objects for method generic parameters. + TRuntimeMethodCommon uninstantiatedCommon = _common.RuntimeMethodCommonOfUninstantiatedMethod; + owningMethod = RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(uninstantiatedCommon, uninstantiatedCommon.DeclaringType); + } + else + { + // Desktop compat: DeclaringMethod always returns a MethodInfo whose ReflectedType is equal to DeclaringType. + if (!_reflectedType.Equals(_common.DeclaringType)) + owningMethod = RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(_common, _common.DeclaringType); + } + + return _common.GetGenericTypeParametersWithSpecifiedOwningMethod(owningMethod); + } + } + + internal sealed override MethodInvoker GetUncachedMethodInvoker(RuntimeTypeInfo[] methodArguments, MemberInfo exceptionPertainant) + { + MethodInvoker invoker = _common.GetUncachedMethodInvoker(methodArguments, exceptionPertainant, out Exception exception); + if (invoker == null) + { + // If we have byref-like types in the signature, the reason we couldn't find an invoker + // is that. + bool hasByRefLikeParameter = false; + foreach (ParameterInfo parameter in GetParametersNoCopy()) + { + if (Unwrap(parameter.ParameterType).IsByRefLike) + { + hasByRefLikeParameter = true; + } + } + + if (hasByRefLikeParameter || Unwrap(ReturnType).IsByRefLike) + { + throw new NotSupportedException(SR.NotSupported_ByRefLike); + } + + static Type Unwrap(Type t) + { + while (t.HasElementType) + t = t.GetElementType(); + return t; + } + + throw exception; + } + + return invoker; + } + + protected sealed override MethodInvoker UncachedMethodInvoker + { + get + { + MethodInvoker invoker = this.GetCustomMethodInvokerIfNeeded(); + if (invoker != null) + return invoker; + + return GetUncachedMethodInvoker(Array.Empty(), this); + } + } + + internal sealed override RuntimeMethodHandle GetRuntimeMethodHandle(Type[] genericArgs) + { + return _common.GetRuntimeMethodHandle(genericArgs); + } + + private TRuntimeMethodCommon _common; + private readonly RuntimeTypeInfo _reflectedType; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimePlainConstructorInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimePlainConstructorInfo.cs new file mode 100644 index 00000000000000..308d09b8efcd5e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimePlainConstructorInfo.cs @@ -0,0 +1,211 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Globalization; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; + +using Internal.Reflection.Core.Execution; + +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.MethodInfos +{ + // + // The runtime's implementation of ConstructorInfo's represented in the metadata (this is the 99% case.) + // + internal sealed partial class RuntimePlainConstructorInfo : RuntimeConstructorInfo where TRuntimeMethodCommon : IRuntimeMethodCommon, IEquatable + { + // + // methodHandle - the "tkMethodDef" that identifies the method. + // definingType - the "tkTypeDef" that defined the method (this is where you get the metadata reader that created methodHandle.) + // contextType - the type that supplies the type context (i.e. substitutions for generic parameters.) Though you + // get your raw information from "definingType", you report "contextType" as your DeclaringType property. + // + // For example: + // + // typeof(Foo<>).GetTypeInfo().DeclaredMembers + // + // The definingType and contextType are both Foo<> + // + // typeof(Foo).GetTypeInfo().DeclaredMembers + // + // The definingType is "Foo<,>" + // The contextType is "Foo" + // + // We don't report any DeclaredMembers for arrays or generic parameters so those don't apply. + // + private RuntimePlainConstructorInfo(TRuntimeMethodCommon common) + { + _common = common; + } + + public sealed override MethodAttributes Attributes + { + get + { + return _common.Attributes; + } + } + + public sealed override CallingConventions CallingConvention + { + get + { + return _common.CallingConvention; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodBase_CustomAttributes(this); +#endif + + return _common.TrueCustomAttributes; + } + } + + public sealed override Type DeclaringType + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodBase_DeclaringType(this); +#endif + + return _common.DeclaringType; + } + } + + [DebuggerGuidedStepThrough] + public sealed override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.ConstructorInfo_Invoke(this, parameters); +#endif + if (parameters == null) + parameters = Array.Empty(); + + // Most objects are allocated by NewObject and their constructors return "void". But in many frameworks, + // there are "weird" cases (e.g. String) where the constructor must do both the allocation and initialization. + // Reflection.Core does not hardcode these special cases. It's up to the ExecutionEnvironment to steer + // us the right way by coordinating the implementation of NewObject and MethodInvoker. + object newObject = ReflectionCoreExecution.ExecutionEnvironment.NewObject(this.DeclaringType.TypeHandle); + object ctorAllocatedObject = this.MethodInvoker.Invoke(newObject, parameters, binder, invokeAttr, culture); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + return newObject != null ? newObject : ctorAllocatedObject; + } + + public sealed override MethodBase MetadataDefinitionMethod + { + get + { + return RuntimePlainConstructorInfo.GetRuntimePlainConstructorInfo(_common.RuntimeMethodCommonOfUninstantiatedMethod); + } + } + + public sealed override MethodImplAttributes MethodImplementationFlags + { + get + { + return _common.MethodImplementationFlags; + } + } + + public sealed override string Name + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.MethodBase_Name(this); +#endif + + return _common.Name; + } + } + + public sealed override int MetadataToken + { + get + { + return _common.MetadataToken; + } + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + if (!(other is RuntimePlainConstructorInfo otherConstructor)) + return false; + + return _common.HasSameMetadataDefinitionAs(otherConstructor._common); + } + + public sealed override bool Equals(object obj) + { + if (!(obj is RuntimePlainConstructorInfo other)) + return false; + return _common.Equals(other._common); + } + + public sealed override int GetHashCode() + { + return _common.GetHashCode(); + } + + public sealed override string ToString() + { + return RuntimeMethodHelpers.ComputeToString(ref _common, this, Array.Empty()); + } + + public sealed override RuntimeMethodHandle MethodHandle => _common.GetRuntimeMethodHandle(null); + + protected sealed override RuntimeParameterInfo[] RuntimeParameters + { + get + { + RuntimeParameterInfo ignore; + return _lazyParameters ?? (_lazyParameters = RuntimeMethodHelpers.GetRuntimeParameters(ref _common, this, Array.Empty(), out ignore)); + } + } + + protected sealed override MethodInvoker UncachedMethodInvoker + { + get + { + if (_common.DefiningTypeInfo.IsAbstract) + throw new MemberAccessException(SR.Format(SR.Acc_CreateAbstEx, _common.DefiningTypeInfo.FullName)); + + if (this.IsStatic) + throw new MemberAccessException(SR.Acc_NotClassInit); + + MethodInvoker invoker = this.GetCustomMethodInvokerIfNeeded(); + if (invoker != null) + return invoker; + + invoker = _common.GetUncachedMethodInvoker(Array.Empty(), this, out Exception exception); + if (invoker == null) + throw exception; + + return invoker; + } + } + + private volatile RuntimeParameterInfo[] _lazyParameters; + private TRuntimeMethodCommon _common; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticConstructorInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticConstructorInfo.cs new file mode 100644 index 00000000000000..f6129972b3169a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticConstructorInfo.cs @@ -0,0 +1,175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Globalization; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; + +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + // + // The runtime's implementation of constructors exposed on array types. + // + internal sealed partial class RuntimeSyntheticConstructorInfo : RuntimeConstructorInfo, IRuntimeMemberInfoWithNoMetadataDefinition + { + private RuntimeSyntheticConstructorInfo(SyntheticMethodId syntheticMethodId, RuntimeArrayTypeInfo declaringType, RuntimeTypeInfo[] runtimeParameterTypes, InvokerOptions options, CustomMethodInvokerAction action) + { + _syntheticMethodId = syntheticMethodId; + _declaringType = declaringType; + _options = options; + _action = action; + _runtimeParameterTypes = runtimeParameterTypes; + } + + public sealed override MethodAttributes Attributes + { + get + { + return MethodAttributes.Public | MethodAttributes.PrivateScope | MethodAttributes.RTSpecialName; + } + } + + public sealed override CallingConventions CallingConvention + { + get + { + return CallingConventions.Standard | CallingConventions.HasThis; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { + return Empty.Enumerable; + } + } + + public sealed override Type DeclaringType + { + get + { + return _declaringType; + } + } + + public sealed override MethodBase MetadataDefinitionMethod + { + get + { + throw new NotSupportedException(); + } + } + + public sealed override int MetadataToken + { + get + { + throw new InvalidOperationException(SR.NoMetadataTokenAvailable); + } + } + + [DebuggerGuidedStepThrough] + public sealed override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + if (parameters == null) + parameters = Array.Empty(); + + object ctorAllocatedObject = this.MethodInvoker.Invoke(null, parameters, binder, invokeAttr, culture); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + return ctorAllocatedObject; + } + + public sealed override MethodImplAttributes MethodImplementationFlags + { + get + { + return MethodImplAttributes.IL; + } + } + + public sealed override string Name + { + get + { + return ConstructorName; + } + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // This logic is written to match CoreCLR's behavior. + return other is ConstructorInfo && other is IRuntimeMemberInfoWithNoMetadataDefinition; + } + + public sealed override bool Equals(object obj) + { + if (!(obj is RuntimeSyntheticConstructorInfo other)) + return false; + if (_syntheticMethodId != other._syntheticMethodId) + return false; + if (!(_declaringType.Equals(other._declaringType))) + return false; + return true; + } + + public sealed override int GetHashCode() + { + return _declaringType.GetHashCode(); + } + + public sealed override string ToString() + { + // A constructor's "return type" is always System.Void and we don't want to allocate a ParameterInfo object to record that revelation. + // In deference to that, ComputeToString() lets us pass null as a synonym for "void." + return RuntimeMethodHelpers.ComputeToString(this, Array.Empty(), RuntimeParameters, returnParameter: null); + } + + public sealed override RuntimeMethodHandle MethodHandle + { + get + { + throw new PlatformNotSupportedException(); + } + } + + protected sealed override RuntimeParameterInfo[] RuntimeParameters + { + get + { + RuntimeParameterInfo[] parameters = _lazyParameters; + if (parameters == null) + { + RuntimeTypeInfo[] runtimeParameterTypes = _runtimeParameterTypes; + parameters = new RuntimeParameterInfo[runtimeParameterTypes.Length]; + for (int i = 0; i < parameters.Length; i++) + { + parameters[i] = RuntimeSyntheticParameterInfo.GetRuntimeSyntheticParameterInfo(this, i, runtimeParameterTypes[i]); + } + _lazyParameters = parameters; + } + return parameters; + } + } + + protected sealed override MethodInvoker UncachedMethodInvoker => new CustomMethodInvoker(_declaringType, _runtimeParameterTypes, _options, _action); + + private volatile RuntimeParameterInfo[] _lazyParameters; + + private readonly SyntheticMethodId _syntheticMethodId; + private readonly RuntimeArrayTypeInfo _declaringType; + private readonly RuntimeTypeInfo[] _runtimeParameterTypes; + private readonly InvokerOptions _options; + private readonly CustomMethodInvokerAction _action; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs new file mode 100644 index 00000000000000..4d432d988244f5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs @@ -0,0 +1,230 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; + +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + // + // These methods implement the Get/Set methods on array types. + // + internal sealed partial class RuntimeSyntheticMethodInfo : RuntimeMethodInfo, IRuntimeMemberInfoWithNoMetadataDefinition + { + private RuntimeSyntheticMethodInfo(SyntheticMethodId syntheticMethodId, string name, RuntimeArrayTypeInfo declaringType, RuntimeTypeInfo[] parameterTypes, RuntimeTypeInfo returnType, InvokerOptions options, CustomMethodInvokerAction action) + { + _syntheticMethodId = syntheticMethodId; + _name = name; + _declaringType = declaringType; + _options = options; + _action = action; + _runtimeParameterTypes = parameterTypes; + _returnType = returnType; + } + + public sealed override MethodAttributes Attributes + { + get + { + return MethodAttributes.Public | MethodAttributes.PrivateScope; + } + } + + public sealed override CallingConventions CallingConvention + { + get + { + return CallingConventions.Standard | CallingConventions.HasThis; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { + return Empty.Enumerable; + } + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // This logic is written to match CoreCLR's behavior. + return other is MethodInfo && other is IRuntimeMemberInfoWithNoMetadataDefinition; + } + + public sealed override bool Equals(object obj) + { + if (!(obj is RuntimeSyntheticMethodInfo other)) + return false; + if (_syntheticMethodId != other._syntheticMethodId) + return false; + if (!(_declaringType.Equals(other._declaringType))) + return false; + return true; + } + + public sealed override MethodInfo GetGenericMethodDefinition() + { + throw new InvalidOperationException(); + } + + public sealed override int GetHashCode() + { + return _declaringType.GetHashCode(); + } + + public sealed override bool IsConstructedGenericMethod + { + get + { + return false; + } + } + + public sealed override bool IsGenericMethod + { + get + { + return false; + } + } + + public sealed override bool IsGenericMethodDefinition + { + get + { + return false; + } + } + + public sealed override int GenericParameterCount => 0; + + [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] + [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + public sealed override MethodInfo MakeGenericMethod(params Type[] typeArguments) + { + throw new InvalidOperationException(SR.Format(SR.Arg_NotGenericMethodDefinition, this)); + } + + public sealed override MethodBase MetadataDefinitionMethod + { + get + { + throw new NotSupportedException(); + } + } + + public sealed override MethodImplAttributes MethodImplementationFlags + { + get + { + return MethodImplAttributes.IL; + } + } + + public sealed override Module Module + { + get + { + return this.DeclaringType.Assembly.ManifestModule; + } + } + + public sealed override int MetadataToken + { + get + { + throw new InvalidOperationException(SR.NoMetadataTokenAvailable); + } + } + + public sealed override Type ReflectedType + { + get + { + // The only synthetic methods come from array types which can never be inherited from. So unless that changes, + // we don't provide a way to specify the ReflectedType. + return DeclaringType; + } + } + + public sealed override string ToString() + { + return RuntimeMethodHelpers.ComputeToString(this, Array.Empty(), RuntimeParameters, RuntimeReturnParameter); + } + + public sealed override RuntimeMethodHandle MethodHandle + { + get + { + throw new PlatformNotSupportedException(); + } + } + + protected sealed override MethodInvoker UncachedMethodInvoker => new CustomMethodInvoker(_declaringType, _runtimeParameterTypes, _options, _action); + + internal sealed override RuntimeTypeInfo[] RuntimeGenericArgumentsOrParameters + { + get + { + return Array.Empty(); + } + } + + internal sealed override RuntimeTypeInfo RuntimeDeclaringType + { + get + { + return _declaringType; + } + } + + internal sealed override string RuntimeName + { + get + { + return _name; + } + } + + internal sealed override RuntimeParameterInfo[] GetRuntimeParameters(RuntimeMethodInfo contextMethod, out RuntimeParameterInfo returnParameter) + { + RuntimeTypeInfo[] runtimeParameterTypes = _runtimeParameterTypes; + RuntimeParameterInfo[] parameters = new RuntimeParameterInfo[runtimeParameterTypes.Length]; + for (int i = 0; i < parameters.Length; i++) + { + parameters[i] = RuntimeSyntheticParameterInfo.GetRuntimeSyntheticParameterInfo(this, i, runtimeParameterTypes[i]); + } + returnParameter = RuntimeSyntheticParameterInfo.GetRuntimeSyntheticParameterInfo(this, -1, _returnType); + return parameters; + } + + internal sealed override RuntimeMethodInfo WithReflectedTypeSetToDeclaringType + { + get + { + Debug.Assert(ReflectedType.Equals(DeclaringType)); + return this; + } + } + + private readonly string _name; + private readonly SyntheticMethodId _syntheticMethodId; + private readonly RuntimeArrayTypeInfo _declaringType; + private readonly RuntimeTypeInfo[] _runtimeParameterTypes; + private readonly RuntimeTypeInfo _returnType; + private readonly InvokerOptions _options; + private readonly CustomMethodInvokerAction _action; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/SyntheticMethodId.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/SyntheticMethodId.cs new file mode 100644 index 00000000000000..dd23f9a7623c6b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/SyntheticMethodId.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + internal enum SyntheticMethodId + { + ArrayCtor = 1, + ArrayMultiDimCtor = 2, + ArrayGet = 3, + ArraySet = 4, + ArrayAddress = 5, + + // Ids from 0x80000000..0xffffffff are reserved for the jagged array constructors + // (e.g. a type such as T[][][][] has three such constructors so we need three ID's. + // We stick the parameter count into the lower bits to generate unique ids.) + ArrayCtorJagged = unchecked((int)0x80000000), + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/VirtualRuntimeParameterInfoArray.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/VirtualRuntimeParameterInfoArray.cs new file mode 100644 index 00000000000000..22e3e405f0af4b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/VirtualRuntimeParameterInfoArray.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.ParameterInfos; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + // Helper for GetRuntimeParameters() - array mimic that supports an efficient "array.Skip(1).ToArray()" operation. + internal struct VirtualRuntimeParameterInfoArray + { + public VirtualRuntimeParameterInfoArray(int count) + : this() + { + Debug.Assert(count >= 1); + Remainder = (count == 1) ? Array.Empty() : new RuntimeParameterInfo[count - 1]; + } + + public RuntimeParameterInfo this[int index] + { + get + { + return index == 0 ? First : Remainder[index - 1]; + } + + set + { + if (index == 0) + First = value; + else + Remainder[index - 1] = value; + } + } + + public RuntimeParameterInfo First { get; private set; } + public RuntimeParameterInfo[] Remainder { get; } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/NativeFormat/NativeFormatRuntimeModule.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/NativeFormat/NativeFormatRuntimeModule.cs new file mode 100644 index 00000000000000..7fe281fc385858 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/NativeFormat/NativeFormatRuntimeModule.cs @@ -0,0 +1,106 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Runtime.Assemblies; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.CustomAttributes; +using System.Collections.Generic; + +using Internal.Reflection.Core; +using Internal.Metadata.NativeFormat; +using System.Reflection.Runtime.Assemblies.NativeFormat; + +namespace System.Reflection.Runtime.Modules.NativeFormat +{ + internal sealed partial class NativeFormatRuntimeModule : RuntimeModule + { + private NativeFormatRuntimeModule(NativeFormatRuntimeAssembly assembly) + : base() + { + _assembly = assembly; + } + + public sealed override Assembly Assembly => _assembly; + + public sealed override IEnumerable CustomAttributes + { + get + { + QScopeDefinition scope = _assembly.Scope; + return RuntimeCustomAttributeData.GetCustomAttributes(scope.Reader, scope.ScopeDefinition.ModuleCustomAttributes); + } + } + + public sealed override int MetadataToken + { + get + { + throw new InvalidOperationException(SR.NoMetadataTokenAvailable); + } + } + + public sealed override Guid ModuleVersionId + { + get + { + byte[] mvid = _assembly.Scope.ScopeDefinition.Mvid.ToArray(); + if (mvid.Length == 0) + return default(Guid); // Workaround for TFS 441076 - Module data not emitted for facade assemblies. + return new Guid(mvid); + } + } + + public sealed override string ScopeName + { + get + { + QScopeDefinition scope = _assembly.Scope; + return scope.Reader.GetString(scope.ScopeDefinition.ModuleName); + } + } + + [RequiresUnreferencedCode("Fields might be removed")] + public sealed override FieldInfo GetField(string name, BindingFlags bindingAttr) + { + QScopeDefinition scope = _assembly.Scope; + MetadataReader reader = scope.Reader; + return scope.ScopeDefinition.GlobalModuleType.GetNamedType(reader).GetField(name, bindingAttr); + } + + [RequiresUnreferencedCode("Fields might be removed")] + public sealed override FieldInfo[] GetFields(BindingFlags bindingFlags) + { + QScopeDefinition scope = _assembly.Scope; + MetadataReader reader = scope.Reader; + return scope.ScopeDefinition.GlobalModuleType.GetNamedType(reader).GetFields(bindingFlags); + } + + [RequiresUnreferencedCode("Methods might be removed")] + protected sealed override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + QScopeDefinition scope = _assembly.Scope; + MetadataReader reader = scope.Reader; + TypeInfos.RuntimeTypeDefinitionTypeInfo runtimeType = scope.ScopeDefinition.GlobalModuleType.GetNamedType(reader); + + if (types == null) + { + return runtimeType.GetMethod(name, bindingAttr); + } + else + { + return runtimeType.GetMethod(name, bindingAttr, binder, callConvention, types, modifiers); + } + } + + [RequiresUnreferencedCode("Methods might be removed")] + public sealed override MethodInfo[] GetMethods(BindingFlags bindingFlags) + { + QScopeDefinition scope = _assembly.Scope; + MetadataReader reader = scope.Reader; + return scope.ScopeDefinition.GlobalModuleType.GetNamedType(reader).GetMethods(bindingFlags); + } + + private readonly NativeFormatRuntimeAssembly _assembly; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/RuntimeModule.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/RuntimeModule.cs new file mode 100644 index 00000000000000..4efce9b9a17900 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/RuntimeModule.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; +using System.Reflection.Runtime.Assemblies; +using System.Collections.Generic; + +namespace System.Reflection.Runtime.Modules +{ + // + // The runtime's implementation of a Module. + // + // Modules are quite meaningless in ProjectN but we have to keep up the appearances since they still exist in Win8P's surface area. + // As far as ProjectN is concerned, each Assembly has one module. + // + internal abstract partial class RuntimeModule : Module + { + protected RuntimeModule() + : base() + { } + + public abstract override Assembly Assembly { get; } + + public abstract override IEnumerable CustomAttributes { get; } + + internal const string UnknownStringMessageInRAF = "Returns for modules with no file path"; + + [RequiresAssemblyFiles(UnknownStringMessageInRAF)] + public sealed override string FullyQualifiedName + { + get + { + return ""; + } + } + + [RequiresAssemblyFiles(UnknownStringMessageInRAF)] + public sealed override string Name + { + get + { + return ""; + } + } + + public sealed override bool Equals(object obj) + { + if (!(obj is RuntimeModule other)) + return false; + return Assembly.Equals(other.Assembly); + } + + public sealed override int GetHashCode() + { + return Assembly.GetHashCode(); + } + + public sealed override void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + public abstract override int MetadataToken { get; } + + [RequiresUnreferencedCode("Types might be removed")] + public sealed override Type GetType(string name, bool throwOnError, bool ignoreCase) + { + return Assembly.GetType(name, throwOnError, ignoreCase); + } + + [RequiresUnreferencedCode("Types might be removed")] + public sealed override Type[] GetTypes() + { + Debug.Assert(this.Equals(Assembly.ManifestModule)); // We only support single-module assemblies so we have to be the manifest module. + return Assembly.GetTypes(); + } + + public abstract override Guid ModuleVersionId { get; } + + public sealed override bool IsResource() { throw new PlatformNotSupportedException(); } + public sealed override void GetPEKind(out PortableExecutableKinds peKind, out ImageFileMachine machine) { throw new PlatformNotSupportedException(); } + public sealed override int MDStreamVersion { get { throw new PlatformNotSupportedException(); } } + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public sealed override FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw new PlatformNotSupportedException(); } + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public sealed override MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw new PlatformNotSupportedException(); } + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public sealed override MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw new PlatformNotSupportedException(); } + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public sealed override byte[] ResolveSignature(int metadataToken) { throw new PlatformNotSupportedException(); } + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public sealed override string ResolveString(int metadataToken) { throw new PlatformNotSupportedException(); } + [RequiresUnreferencedCode("Trimming changes metadata tokens")] + public sealed override Type ResolveType(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw new PlatformNotSupportedException(); } + + protected sealed override ModuleHandle GetModuleHandleImpl() => new ModuleHandle(this); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/NativeFormat/NativeFormatMethodParameterInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/NativeFormat/NativeFormatMethodParameterInfo.cs new file mode 100644 index 00000000000000..9ca09006384523 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/NativeFormat/NativeFormatMethodParameterInfo.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.General.NativeFormat; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.ParameterInfos.NativeFormat +{ + // + // This implements ParameterInfo objects owned by MethodBase objects that have an associated Parameter metadata entity. + // + internal sealed partial class NativeFormatMethodParameterInfo : RuntimeFatMethodParameterInfo + { + private NativeFormatMethodParameterInfo(MethodBase member, MethodHandle methodHandle, int position, ParameterHandle parameterHandle, QSignatureTypeHandle qualifiedParameterTypeHandle, TypeContext typeContext) + : base(member, position, qualifiedParameterTypeHandle, typeContext) + { + _methodHandle = methodHandle; + _parameterHandle = parameterHandle; + _parameter = parameterHandle.GetParameter(Reader); + } + + private MetadataReader Reader + { + get + { + Debug.Assert(QualifiedParameterTypeHandle.Reader is MetadataReader); + return (MetadataReader)QualifiedParameterTypeHandle.Reader; + } + } + + public sealed override ParameterAttributes Attributes + { + get + { + return _parameter.Flags; + } + } + + public sealed override string Name + { + get + { + return _parameter.Name.GetStringOrNull(this.Reader); + } + } + + public sealed override int MetadataToken + { + get + { + throw new InvalidOperationException(SR.NoMetadataTokenAvailable); + } + } + + protected sealed override IEnumerable TrueCustomAttributes => RuntimeCustomAttributeData.GetCustomAttributes(this.Reader, _parameter.CustomAttributes); + + protected sealed override bool GetDefaultValueIfAvailable(bool raw, out object defaultValue) + { + return DefaultValueParser.GetDefaultValueIfAny(Reader, _parameter.DefaultValue, ParameterType, CustomAttributes, raw, out defaultValue); + } + + private readonly MethodHandle _methodHandle; + private readonly ParameterHandle _parameterHandle; + private readonly Parameter _parameter; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeFatMethodParameterInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeFatMethodParameterInfo.cs new file mode 100644 index 00000000000000..8f92256963e18c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeFatMethodParameterInfo.cs @@ -0,0 +1,90 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.CustomAttributes; + +namespace System.Reflection.Runtime.ParameterInfos +{ + // + // This implements ParameterInfo objects owned by MethodBase objects that have associated Parameter metadata. (In practice, + // this means all non-return parameters since most such parameters have at least a name.) + // + internal abstract class RuntimeFatMethodParameterInfo : RuntimeMethodParameterInfo + { + protected RuntimeFatMethodParameterInfo(MethodBase member, int position, QSignatureTypeHandle qualifiedParameterTypeHandle, TypeContext typeContext) + : base(member, position, qualifiedParameterTypeHandle, typeContext) + { + } + + public sealed override IEnumerable CustomAttributes + { + get + { + foreach (CustomAttributeData cad in TrueCustomAttributes) + yield return cad; + + ParameterAttributes attributes = Attributes; + if (0 != (attributes & ParameterAttributes.In)) + yield return new RuntimePseudoCustomAttributeData(typeof(InAttribute), null); + if (0 != (attributes & ParameterAttributes.Out)) + yield return new RuntimePseudoCustomAttributeData(typeof(OutAttribute), null); + if (0 != (attributes & ParameterAttributes.Optional)) + yield return new RuntimePseudoCustomAttributeData(typeof(OptionalAttribute), null); + } + } + + protected abstract IEnumerable TrueCustomAttributes { get; } + + public sealed override bool HasDefaultValue => DefaultValueInfo.Item1; + public sealed override object DefaultValue => DefaultValueInfo.Item2; + + public sealed override object RawDefaultValue + { + get + { + Tuple rawDefaultValueInfo = _lazyRawDefaultValueInfo; + if (rawDefaultValueInfo == null) + { + object rawDefaultValue; + bool dontCare = GetDefaultValueOrSentinel(raw: true, defaultValue: out rawDefaultValue); + rawDefaultValueInfo = _lazyRawDefaultValueInfo = Tuple.Create(rawDefaultValue); + } + return rawDefaultValueInfo.Item1; + } + } + + protected abstract bool GetDefaultValueIfAvailable(bool raw, out object defaultValue); + + private Tuple DefaultValueInfo + { + get + { + Tuple defaultValueInfo = _lazyDefaultValueInfo; + if (defaultValueInfo == null) + { + object defaultValue; + bool hasDefaultValue = GetDefaultValueOrSentinel(raw: false, defaultValue: out defaultValue); + defaultValueInfo = _lazyDefaultValueInfo = Tuple.Create(hasDefaultValue, defaultValue); + } + return defaultValueInfo; + } + } + + private bool GetDefaultValueOrSentinel(bool raw, out object defaultValue) + { + bool hasDefaultValue = GetDefaultValueIfAvailable(raw, out defaultValue); + if (!hasDefaultValue) + { + defaultValue = IsOptional ? (object)Missing.Value : (object)DBNull.Value; + } + return hasDefaultValue; + } + + private volatile Tuple _lazyDefaultValueInfo; + private volatile Tuple _lazyRawDefaultValueInfo; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeMethodParameterInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeMethodParameterInfo.cs new file mode 100644 index 00000000000000..71e30cde53378f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeMethodParameterInfo.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; + +using Internal.Reflection.Core; + +namespace System.Reflection.Runtime.ParameterInfos +{ + // + // Abstract base for all ParameterInfo objects exposed by runtime MethodBase objects + // (including the ReturnParameter.) + // + internal abstract class RuntimeMethodParameterInfo : RuntimeParameterInfo + { + protected RuntimeMethodParameterInfo(MethodBase member, int position, QSignatureTypeHandle qualifiedParameterTypeHandle, TypeContext typeContext) + : base(member, position) + { + QualifiedParameterTypeHandle = qualifiedParameterTypeHandle; + _typeContext = typeContext; + } + + public sealed override Type[] GetOptionalCustomModifiers() => QualifiedParameterTypeHandle.GetCustomModifiers(_typeContext, optional: true); + + public sealed override Type[] GetRequiredCustomModifiers() => QualifiedParameterTypeHandle.GetCustomModifiers(_typeContext, optional: false); + + public sealed override Type ParameterType + { + get + { + return _lazyParameterType ?? (_lazyParameterType = QualifiedParameterTypeHandle.Resolve(_typeContext)); + } + } + + internal sealed override string ParameterTypeString + { + get + { + return QualifiedParameterTypeHandle.FormatTypeName(_typeContext); + } + } + + protected readonly QSignatureTypeHandle QualifiedParameterTypeHandle; + private readonly TypeContext _typeContext; + private volatile Type _lazyParameterType; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeParameterInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeParameterInfo.cs new file mode 100644 index 00000000000000..24f628042880f9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeParameterInfo.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; + +namespace System.Reflection.Runtime.ParameterInfos +{ + // + // Abstract base for all ParameterInfo objects created by the Runtime. + // + internal abstract partial class RuntimeParameterInfo : ParameterInfo + { + protected RuntimeParameterInfo(MemberInfo member, int position) + { + _member = member; + _position = position; + } + + public abstract override ParameterAttributes Attributes { get; } + public abstract override IEnumerable CustomAttributes { get; } + public abstract override object DefaultValue { get; } + public abstract override object RawDefaultValue { get; } + + public sealed override bool Equals(object obj) + { + if (!(obj is RuntimeParameterInfo other)) + return false; + if (_position != other._position) + return false; + if (!(_member.Equals(other._member))) + return false; + return true; + } + + public sealed override int GetHashCode() + { + return _member.GetHashCode(); + } + + public abstract override Type[] GetOptionalCustomModifiers(); + + public abstract override Type[] GetRequiredCustomModifiers(); + + public abstract override bool HasDefaultValue { get; } + + public abstract override int MetadataToken + { + get; + } + + public sealed override MemberInfo Member + { + get + { + return _member; + } + } + + public abstract override string Name { get; } + public abstract override Type ParameterType { get; } + + public sealed override int Position + { + get + { + return _position; + } + } + + public sealed override string ToString() + { + return this.ParameterTypeString + " " + this.Name; + } + + // Gets the ToString() output of ParameterType in a pay-to-play-safe way: Other Reflection ToString() methods should always use this rather than + // "ParameterType.ToString()". + internal abstract string ParameterTypeString { get; } + + private readonly MemberInfo _member; + private readonly int _position; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimePropertyIndexParameterInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimePropertyIndexParameterInfo.cs new file mode 100644 index 00000000000000..133da4c63e8096 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimePropertyIndexParameterInfo.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.PropertyInfos; + +namespace System.Reflection.Runtime.ParameterInfos +{ + // + // This implements ParameterInfo objects returned by PropertyInfo.GetIndexParameters(). Basically, they're identical to the underling accessor method's + // ParameterInfo's except that the Member property returns the PropertyInfo rather than a MethodBase. + // + internal sealed partial class RuntimePropertyIndexParameterInfo : RuntimeParameterInfo + { + private RuntimePropertyIndexParameterInfo(RuntimePropertyInfo member, RuntimeParameterInfo backingParameter) + : base(member, backingParameter.Position) + { + _backingParameter = backingParameter; + } + + public sealed override ParameterAttributes Attributes + { + get + { + return _backingParameter.Attributes; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { + return _backingParameter.CustomAttributes; + } + } + + public sealed override object DefaultValue + { + get + { + return _backingParameter.DefaultValue; + } + } + + public sealed override object RawDefaultValue + { + get + { + return _backingParameter.RawDefaultValue; + } + } + + public sealed override Type[] GetOptionalCustomModifiers() + { + return _backingParameter.GetOptionalCustomModifiers(); + } + + public sealed override Type[] GetRequiredCustomModifiers() + { + return _backingParameter.GetRequiredCustomModifiers(); + } + + public sealed override bool HasDefaultValue + { + get + { + return _backingParameter.HasDefaultValue; + } + } + + public sealed override string Name + { + get + { + return _backingParameter.Name; + } + } + + public sealed override Type ParameterType + { + get + { + return _backingParameter.ParameterType; + } + } + + internal sealed override string ParameterTypeString + { + get + { + return _backingParameter.ParameterTypeString; + } + } + + public sealed override int MetadataToken + { + get + { + return _backingParameter.MetadataToken; + } + } + + private readonly RuntimeParameterInfo _backingParameter; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeSyntheticParameterInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeSyntheticParameterInfo.cs new file mode 100644 index 00000000000000..505f8109297a41 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeSyntheticParameterInfo.cs @@ -0,0 +1,106 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; + +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.ParameterInfos +{ + // This class is used for the "Get/Set" methods on array types. + internal sealed partial class RuntimeSyntheticParameterInfo : RuntimeParameterInfo + { + private RuntimeSyntheticParameterInfo(MemberInfo memberInfo, int position, RuntimeTypeInfo parameterType) + : base(memberInfo, position) + { + _parameterType = parameterType; + } + + public sealed override ParameterAttributes Attributes + { + get + { + return ParameterAttributes.None; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { + return Empty.Enumerable; + } + } + + public sealed override object DefaultValue + { + get + { + return null; // Legacy: This is what the desktop returns. + } + } + + public sealed override object RawDefaultValue + { + get + { + return null; // Legacy: This is what the desktop returns. + } + } + + public sealed override bool HasDefaultValue + { + get + { + // Compat: returning "true" makes no sense but this is how it's always been. + return true; + } + } + + public sealed override Type[] GetOptionalCustomModifiers() => Array.Empty(); + + public sealed override Type[] GetRequiredCustomModifiers() => Array.Empty(); + + public sealed override string Name + { + get + { + return null; // Legacy: This is what the dekstop returns. + } + } + + public sealed override Type ParameterType + { + get + { + return _parameterType; + } + } + + public sealed override int MetadataToken + { + get + { + return 0x08000000; // nil ParamDef token + } + } + + internal sealed override string ParameterTypeString + { + get + { + return _parameterType.FormatTypeNameForReflection(); + } + } + + private readonly RuntimeTypeInfo _parameterType; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeThinMethodParameterInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeThinMethodParameterInfo.cs new file mode 100644 index 00000000000000..cb53a3648beaa3 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeThinMethodParameterInfo.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; + +using Internal.Reflection.Core; + +namespace System.Reflection.Runtime.ParameterInfos +{ + // + // This implements ParameterInfo objects owned by MethodBase objects that have no associated Parameter metadata. (In practice, + // this means return type "Parameters" that don't have custom attributes. + // + internal sealed partial class RuntimeThinMethodParameterInfo : RuntimeMethodParameterInfo + { + private RuntimeThinMethodParameterInfo(MethodBase member, int position, QSignatureTypeHandle qualifiedParameterTypeHandle, TypeContext typeContext) + : base(member, position, qualifiedParameterTypeHandle, typeContext) + { + } + + public sealed override ParameterAttributes Attributes + { + get + { + return ParameterAttributes.None; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { + return Empty.Enumerable; + } + } + + public sealed override object DefaultValue + { + get + { + // Returning "null" matches the desktop behavior, though this is inconsistent with the DBNull/Missing values + // returned by non-return ParameterInfo's without default values. + return null; + } + } + + public sealed override object RawDefaultValue + { + get + { + // Returning "null" matches the desktop behavior, though this is inconsistent with the DBNull/Missing values + // returned by non-return ParameterInfo's without default values. + return null; + } + } + + public sealed override bool HasDefaultValue + { + get + { + // Compat: returning "true" makes no sense but this is how it's always been. + return true; + } + } + + public sealed override string Name + { + get + { + return null; + } + } + + public sealed override int MetadataToken + { + get + { + return 0x08000000; // nil ParamDef token + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/EcmaFormat/EcmaFormatRuntimePropertyInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/EcmaFormat/EcmaFormatRuntimePropertyInfo.cs new file mode 100644 index 00000000000000..44c5778198866c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/EcmaFormat/EcmaFormatRuntimePropertyInfo.cs @@ -0,0 +1,192 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Globalization; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.General.EcmaFormat; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeInfos.EcmaFormat; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.MethodInfos.EcmaFormat; +using System.Reflection.Runtime.ParameterInfos; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +using Internal.Reflection.Tracing; + +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; + +namespace System.Reflection.Runtime.PropertyInfos.EcmaFormat +{ + // + // The runtime's implementation of PropertyInfo's + // + [DebuggerDisplay("{_debugName}")] + internal sealed partial class EcmaFormatRuntimePropertyInfo : RuntimePropertyInfo + { + // + // propertyHandle - the "tkPropertyDef" that identifies the property. + // definingType - the "tkTypeDef" that defined the field (this is where you get the metadata reader that created propertyHandle.) + // contextType - the type that supplies the type context (i.e. substitutions for generic parameters.) Though you + // get your raw information from "definingType", you report "contextType" as your DeclaringType property. + // + // For example: + // + // typeof(Foo<>).GetTypeInfo().DeclaredMembers + // + // The definingType and contextType are both Foo<> + // + // typeof(Foo).GetTypeInfo().DeclaredMembers + // + // The definingType is "Foo<,>" + // The contextType is "Foo" + // + // We don't report any DeclaredMembers for arrays or generic parameters so those don't apply. + // + private EcmaFormatRuntimePropertyInfo(PropertyDefinitionHandle propertyHandle, EcmaFormatRuntimeNamedTypeInfo definingTypeInfo, RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType) : + base(contextTypeInfo, reflectedType) + { + _propertyHandle = propertyHandle; + _definingTypeInfo = definingTypeInfo; + _reader = definingTypeInfo.Reader; + _property = _reader.GetPropertyDefinition(propertyHandle); + } + + public sealed override PropertyAttributes Attributes + { + get + { + return _property.Attributes; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.PropertyInfo_CustomAttributes(this); +#endif + + return RuntimeCustomAttributeData.GetCustomAttributes(_reader, _property.GetCustomAttributes()); + } + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + if (!(other is EcmaFormatRuntimePropertyInfo otherProperty)) + return false; + if (!(_reader == otherProperty._reader)) + return false; + if (!(_propertyHandle.Equals(otherProperty._propertyHandle))) + return false; + return true; + } + + public sealed override bool Equals(Object obj) + { + if (!(obj is EcmaFormatRuntimePropertyInfo other)) + return false; + if (!(_reader == other._reader)) + return false; + if (!(_propertyHandle.Equals(other._propertyHandle))) + return false; + if (!(ContextTypeInfo.Equals(other.ContextTypeInfo))) + return false; + if (!(_reflectedType.Equals(other._reflectedType))) + return false; + return true; + } + + public sealed override int GetHashCode() + { + return _propertyHandle.GetHashCode(); + } + + public sealed override int MetadataToken + { + get + { + return MetadataTokens.GetToken(_propertyHandle); + } + } + + protected sealed override QSignatureTypeHandle PropertyTypeHandle + { + get + { + return new QSignatureTypeHandle(_reader, _reader.GetBlobReader(_property.Signature)); + } + } + + protected sealed override bool GetDefaultValueIfAny(bool raw, out object defaultValue) + { + return DefaultValueProcessing.GetDefaultValueIfAny(_reader, ref _property, this, raw, out defaultValue); + } + + protected sealed override RuntimeNamedMethodInfo GetPropertyMethod(PropertyMethodSemantics whichMethod) + { + MethodDefinitionHandle methodHandle; + PropertyAccessors propertyAccessors = _property.GetAccessors(); + + switch (whichMethod) + { + case PropertyMethodSemantics.Getter: + methodHandle = propertyAccessors.Getter; + break; + + case PropertyMethodSemantics.Setter: + methodHandle = propertyAccessors.Setter; + break; + + default: + return null; + } + + bool inherited = !_reflectedType.Equals(ContextTypeInfo); + if (inherited) + { + MethodAttributes flags = _reader.GetMethodDefinition(methodHandle).Attributes; + if ((flags & MethodAttributes.MemberAccessMask) == MethodAttributes.Private) + return null; + } + + return RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(new EcmaFormatMethodCommon(methodHandle, _definingTypeInfo, ContextTypeInfo), _reflectedType); + } + + protected sealed override string MetadataName + { + get + { + return _property.Name.GetString(_reader); + } + } + + protected sealed override RuntimeTypeInfo DefiningTypeInfo + { + get + { + return _definingTypeInfo; + } + } + + private readonly EcmaFormatRuntimeNamedTypeInfo _definingTypeInfo; + private readonly PropertyDefinitionHandle _propertyHandle; + + private readonly MetadataReader _reader; + private PropertyDefinition _property; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs new file mode 100644 index 00000000000000..775fd64616b953 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs @@ -0,0 +1,204 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Globalization; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.General.NativeFormat; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.TypeInfos.NativeFormat; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.MethodInfos.NativeFormat; +using System.Reflection.Runtime.ParameterInfos; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +using Internal.Reflection.Tracing; + +using Internal.Metadata.NativeFormat; +using NativeFormatMethodSemanticsAttributes = global::Internal.Metadata.NativeFormat.MethodSemanticsAttributes; + +namespace System.Reflection.Runtime.PropertyInfos.NativeFormat +{ + // + // The runtime's implementation of PropertyInfo's + // + [DebuggerDisplay("{_debugName}")] + internal sealed partial class NativeFormatRuntimePropertyInfo : RuntimePropertyInfo + { + // + // propertyHandle - the "tkPropertyDef" that identifies the property. + // definingType - the "tkTypeDef" that defined the field (this is where you get the metadata reader that created propertyHandle.) + // contextType - the type that supplies the type context (i.e. substitutions for generic parameters.) Though you + // get your raw information from "definingType", you report "contextType" as your DeclaringType property. + // + // For example: + // + // typeof(Foo<>).GetTypeInfo().DeclaredMembers + // + // The definingType and contextType are both Foo<> + // + // typeof(Foo).GetTypeInfo().DeclaredMembers + // + // The definingType is "Foo<,>" + // The contextType is "Foo" + // + // We don't report any DeclaredMembers for arrays or generic parameters so those don't apply. + // + private NativeFormatRuntimePropertyInfo(PropertyHandle propertyHandle, NativeFormatRuntimeNamedTypeInfo definingTypeInfo, RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType) : + base(contextTypeInfo, reflectedType) + { + _propertyHandle = propertyHandle; + _definingTypeInfo = definingTypeInfo; + _reader = definingTypeInfo.Reader; + _property = propertyHandle.GetProperty(_reader); + } + + public sealed override PropertyAttributes Attributes + { + get + { + return _property.Flags; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.PropertyInfo_CustomAttributes(this); +#endif + + return RuntimeCustomAttributeData.GetCustomAttributes(_reader, _property.CustomAttributes); + } + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + if (!(other is NativeFormatRuntimePropertyInfo otherProperty)) + return false; + if (!(_reader == otherProperty._reader)) + return false; + if (!(_propertyHandle.Equals(otherProperty._propertyHandle))) + return false; + if (!(_definingTypeInfo.Equals(otherProperty._definingTypeInfo))) + return false; + return true; + } + + public sealed override bool Equals(object obj) + { + if (!(obj is NativeFormatRuntimePropertyInfo other)) + return false; + if (!(_reader == other._reader)) + return false; + if (!(_propertyHandle.Equals(other._propertyHandle))) + return false; + if (!(ContextTypeInfo.Equals(other.ContextTypeInfo))) + return false; + if (!(_reflectedType.Equals(other._reflectedType))) + return false; + return true; + } + + public sealed override int GetHashCode() + { + return _propertyHandle.GetHashCode(); + } + + public sealed override int MetadataToken + { + get + { + throw new InvalidOperationException(SR.NoMetadataTokenAvailable); + } + } + + protected sealed override QSignatureTypeHandle PropertyTypeHandle + { + get + { + return new QSignatureTypeHandle(_reader, _property.Signature.GetPropertySignature(_reader).Type); + } + } + + protected sealed override bool GetDefaultValueIfAny(bool raw, out object defaultValue) + { + return DefaultValueParser.GetDefaultValueIfAny(_reader, _property.DefaultValue, PropertyType, CustomAttributes, raw, out defaultValue); + } + + protected sealed override RuntimeNamedMethodInfo GetPropertyMethod(PropertyMethodSemantics whichMethod) + { + NativeFormatMethodSemanticsAttributes localMethodSemantics; + switch (whichMethod) + { + case PropertyMethodSemantics.Getter: + localMethodSemantics = NativeFormatMethodSemanticsAttributes.Getter; + break; + + case PropertyMethodSemantics.Setter: + localMethodSemantics = NativeFormatMethodSemanticsAttributes.Setter; + break; + + default: + return null; + } + + bool inherited = !_reflectedType.Equals(ContextTypeInfo); + + foreach (MethodSemanticsHandle methodSemanticsHandle in _property.MethodSemantics) + { + MethodSemantics methodSemantics = methodSemanticsHandle.GetMethodSemantics(_reader); + if (methodSemantics.Attributes == localMethodSemantics) + { + MethodHandle methodHandle = methodSemantics.Method; + + if (inherited) + { + MethodAttributes flags = methodHandle.GetMethod(_reader).Flags; + if ((flags & MethodAttributes.MemberAccessMask) == MethodAttributes.Private) + continue; + } + + return RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(new NativeFormatMethodCommon(methodHandle, _definingTypeInfo, ContextTypeInfo), _reflectedType); + } + } + + return null; + } + + protected sealed override string MetadataName + { + get + { + return _property.Name.GetString(_reader); + } + } + + protected sealed override RuntimeTypeInfo DefiningTypeInfo + { + get + { + return _definingTypeInfo; + } + } + + private readonly NativeFormatRuntimeNamedTypeInfo _definingTypeInfo; + private readonly PropertyHandle _propertyHandle; + + private readonly MetadataReader _reader; + private readonly Property _property; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs new file mode 100644 index 00000000000000..20f4d25aed0f9c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs @@ -0,0 +1,414 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Globalization; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.ParameterInfos; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +using Internal.Reflection.Tracing; + + +namespace System.Reflection.Runtime.PropertyInfos +{ + // + // The runtime's implementation of PropertyInfo's + // + [DebuggerDisplay("{_debugName}")] + internal abstract partial class RuntimePropertyInfo : PropertyInfo, ITraceableTypeMember + { + // + // propertyHandle - the "tkPropertyDef" that identifies the property. + // definingType - the "tkTypeDef" that defined the field (this is where you get the metadata reader that created propertyHandle.) + // contextType - the type that supplies the type context (i.e. substitutions for generic parameters.) Though you + // get your raw information from "definingType", you report "contextType" as your DeclaringType property. + // + // For example: + // + // typeof(Foo<>).GetTypeInfo().DeclaredMembers + // + // The definingType and contextType are both Foo<> + // + // typeof(Foo).GetTypeInfo().DeclaredMembers + // + // The definingType is "Foo<,>" + // The contextType is "Foo" + // + // We don't report any DeclaredMembers for arrays or generic parameters so those don't apply. + // + protected RuntimePropertyInfo(RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType) + { + ContextTypeInfo = contextTypeInfo; + _reflectedType = reflectedType; + } + + public sealed override bool CanRead + { + get + { + return Getter != null; + } + } + + public sealed override bool CanWrite + { + get + { + return Setter != null; + } + } + + public sealed override Type DeclaringType + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.PropertyInfo_DeclaringType(this); +#endif + + return ContextTypeInfo; + } + } + + public sealed override ParameterInfo[] GetIndexParameters() + { + ParameterInfo[] indexParameters = _lazyIndexParameters; + if (indexParameters == null) + { + bool useGetter = CanRead; + RuntimeMethodInfo accessor = (useGetter ? Getter : Setter); + RuntimeParameterInfo[] runtimeMethodParameterInfos = accessor.RuntimeParameters; + int count = runtimeMethodParameterInfos.Length; + if (!useGetter) + count--; // If we're taking the parameters off the setter, subtract one for the "value" parameter. + if (count == 0) + { + _lazyIndexParameters = indexParameters = Array.Empty(); + } + else + { + indexParameters = new ParameterInfo[count]; + for (int i = 0; i < count; i++) + { + indexParameters[i] = RuntimePropertyIndexParameterInfo.GetRuntimePropertyIndexParameterInfo(this, runtimeMethodParameterInfos[i]); + } + _lazyIndexParameters = indexParameters; + } + } + + int numParameters = indexParameters.Length; + if (numParameters == 0) + return indexParameters; + ParameterInfo[] result = new ParameterInfo[numParameters]; + for (int i = 0; i < numParameters; i++) + { + result[i] = indexParameters[i]; + } + return result; + } + + public sealed override MethodInfo GetMethod + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.PropertyInfo_GetMethod(this); +#endif + + return Getter; + } + } + + public sealed override Type[] GetOptionalCustomModifiers() => PropertyTypeHandle.GetCustomModifiers(ContextTypeInfo.TypeContext, optional: true); + + public sealed override Type[] GetRequiredCustomModifiers() => PropertyTypeHandle.GetCustomModifiers(ContextTypeInfo.TypeContext, optional: false); + + public sealed override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.PropertyInfo_GetValue(this, obj, index); +#endif + if (_lazyGetterInvoker == null) + { + if (!CanRead) + throw new ArgumentException(); + + _lazyGetterInvoker = Getter.GetUncachedMethodInvoker(Array.Empty(), this); + } + if (index == null) + index = Array.Empty(); + return _lazyGetterInvoker.Invoke(obj, index, binder, invokeAttr, culture); + } + + public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); + + public sealed override Module Module + { + get + { + return DefiningTypeInfo.Module; + } + } + + public sealed override string Name + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.PropertyInfo_Name(this); +#endif + + return MetadataName; + } + } + + public sealed override Type PropertyType + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.PropertyInfo_PropertyType(this); +#endif + + Type propertyType = _lazyPropertyType; + if (propertyType == null) + { + TypeContext typeContext = ContextTypeInfo.TypeContext; + _lazyPropertyType = propertyType = PropertyTypeHandle.Resolve(typeContext); + } + + return propertyType; + } + } + + public sealed override Type ReflectedType + { + get + { + return _reflectedType; + } + } + + public sealed override MethodInfo SetMethod + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.PropertyInfo_SetMethod(this); +#endif + + return Setter; + } + } + + public sealed override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.PropertyInfo_SetValue(this, obj, value, index); +#endif + if (_lazySetterInvoker == null) + { + if (!CanWrite) + throw new ArgumentException(); + + _lazySetterInvoker = Setter.GetUncachedMethodInvoker(Array.Empty(), this); + } + object[] arguments; + if (index == null) + { + arguments = new object[] { value }; + } + else + { + arguments = new object[index.Length + 1]; + for (int i = 0; i < index.Length; i++) + { + arguments[i] = index[i]; + } + arguments[index.Length] = value; + } + _lazySetterInvoker.Invoke(obj, arguments, binder, invokeAttr, culture); + } + + public sealed override string ToString() + { + StringBuilder sb = new StringBuilder(30); + + TypeContext typeContext = ContextTypeInfo.TypeContext; + sb.Append(PropertyTypeHandle.FormatTypeName(typeContext)); + sb.Append(' '); + sb.Append(this.Name); + ParameterInfo[] indexParameters = this.GetIndexParameters(); + if (indexParameters.Length != 0) + { + RuntimeParameterInfo[] indexRuntimeParameters = new RuntimeParameterInfo[indexParameters.Length]; + for (int i = 0; i < indexParameters.Length; i++) + indexRuntimeParameters[i] = (RuntimeParameterInfo)(indexParameters[i]); + sb.Append(" ["); + sb.Append(RuntimeMethodHelpers.ComputeParametersString(indexRuntimeParameters)); + sb.Append(']'); + } + + return sb.ToString(); + } + + string ITraceableTypeMember.MemberName + { + get + { + return MetadataName; + } + } + + Type ITraceableTypeMember.ContainingType + { + get + { + return ContextTypeInfo; + } + } + + private RuntimeNamedMethodInfo Getter + { + get + { + RuntimeNamedMethodInfo getter = _lazyGetter; + if (getter == null) + { + getter = GetPropertyMethod(PropertyMethodSemantics.Getter); + + if (getter == null) + getter = RuntimeDummyMethodInfo.Instance; + + _lazyGetter = getter; + } + + return object.ReferenceEquals(getter, RuntimeDummyMethodInfo.Instance) ? null : getter; + } + } + + private RuntimeNamedMethodInfo Setter + { + get + { + RuntimeNamedMethodInfo setter = _lazySetter; + if (setter == null) + { + setter = GetPropertyMethod(PropertyMethodSemantics.Setter); + + if (setter == null) + setter = RuntimeDummyMethodInfo.Instance; + + _lazySetter = setter; + } + + return object.ReferenceEquals(setter, RuntimeDummyMethodInfo.Instance) ? null : setter; + } + } + + protected RuntimePropertyInfo WithDebugName() + { + bool populateDebugNames = DeveloperExperienceState.DeveloperExperienceModeEnabled; +#if DEBUG + populateDebugNames = true; +#endif + if (!populateDebugNames) + return this; + + if (_debugName == null) + { + _debugName = "Constructing..."; // Protect against any inadvertent reentrancy. + _debugName = MetadataName; + } + return this; + } + + // Types that derive from RuntimePropertyInfo must implement the following public surface area members + public abstract override PropertyAttributes Attributes { get; } + public abstract override IEnumerable CustomAttributes { get; } + public abstract override bool Equals(object obj); + public abstract override int GetHashCode(); + public abstract override int MetadataToken { get; } + + public sealed override object GetConstantValue() => GetConstantValue(raw: false); + public sealed override object GetRawConstantValue() => GetConstantValue(raw: true); + + protected abstract bool GetDefaultValueIfAny(bool raw, out object defaultValue); + + /// + /// Return a qualified handle that can be used to get the type of the property. + /// + protected abstract QSignatureTypeHandle PropertyTypeHandle { get; } + + protected enum PropertyMethodSemantics + { + Getter, + Setter, + } + + /// + /// Override to return the Method that corresponds to the specified semantic. + /// Return null if a method of the appropriate semantic does not exist + /// + protected abstract RuntimeNamedMethodInfo GetPropertyMethod(PropertyMethodSemantics whichMethod); + + /// + /// Override to provide the metadata based name of a property. (Different from the Name + /// property in that it does not go into the reflection trace logic.) + /// + protected abstract string MetadataName { get; } + + /// + /// Return the DefiningTypeInfo as a RuntimeTypeInfo (instead of as a format specific type info) + /// + protected abstract RuntimeTypeInfo DefiningTypeInfo { get; } + + protected readonly RuntimeTypeInfo ContextTypeInfo; + protected readonly RuntimeTypeInfo _reflectedType; + + private object GetConstantValue(bool raw) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.PropertyInfo_GetConstantValue(this); +#endif + + object defaultValue; + if (!GetDefaultValueIfAny(raw, out defaultValue)) + { + throw new InvalidOperationException(SR.Arg_EnumLitValueNotFound); + } + return defaultValue; + } + + private volatile MethodInvoker _lazyGetterInvoker; + private volatile MethodInvoker _lazySetterInvoker; + + private volatile RuntimeNamedMethodInfo _lazyGetter; + private volatile RuntimeNamedMethodInfo _lazySetter; + + private volatile ParameterInfo[] _lazyIndexParameters; + + private volatile Type _lazyPropertyType; + + private string _debugName; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Tracing/ReflectionEventSource.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Tracing/ReflectionEventSource.cs new file mode 100644 index 00000000000000..706a4fd8a0b7b6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/Tracing/ReflectionEventSource.cs @@ -0,0 +1,376 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.Tracing; + +namespace System.Reflection.Runtime.Tracing +{ + [EventSource(Guid = "55B578AE-32B0-48F8-822F-B3245E6FA59C", Name = "System.Reflection.Runtime.Tracing")] + internal sealed class ReflectionEventSource : EventSource + { + // Defines the singleton instance for the Resources ETW provider + public static readonly ReflectionEventSource Log = new ReflectionEventSource(); + + public static bool IsInitialized + { + get + { + return Log != null; + } + } + + private ReflectionEventSource() { } + + + #region Reflection Event Handlers + [Event(1)] + public void TypeInfo_CustomAttributes(string typeName) + { + WriteEvent(1, typeName); + } + + [Event(2)] + public void TypeInfo_Name(string typeName) + { + WriteEvent(2, typeName); + } + + [Event(3)] + public void TypeInfo_BaseType(string typeName) + { + WriteEvent(3, typeName); + } + + [Event(4)] + public void TypeInfo_DeclaredConstructors(string typeName) + { + WriteEvent(4, typeName); + } + + [Event(5)] + public void TypeInfo_DeclaredEvents(string typeName) + { + WriteEvent(5, typeName); + } + + [Event(6)] + public void TypeInfo_DeclaredFields(string typeName) + { + WriteEvent(6, typeName); + } + + [Event(7)] + public void TypeInfo_DeclaredMembers(string typeName) + { + WriteEvent(7, typeName); + } + + [Event(8)] + public void TypeInfo_DeclaredMethods(string typeName) + { + WriteEvent(8, typeName); + } + + [Event(9)] + public void TypeInfo_DeclaredNestedTypes(string typeName) + { + WriteEvent(9, typeName); + } + + [Event(10)] + public void TypeInfo_DeclaredProperties(string typeName) + { + WriteEvent(10, typeName); + } + + [Event(11)] + public void TypeInfo_DeclaringMethod(string typeName) + { + WriteEvent(11, typeName); + } + + [Event(12)] + public void TypeInfo_FullName(string typeName) + { + WriteEvent(12, typeName); + } + + [Event(13)] + public void TypeInfo_Namespace(string typeName) + { + WriteEvent(13, typeName); + } + + [Event(14)] + public void TypeInfo_GetDeclaredEvent(string typeName, string eventName) + { + WriteEvent(14, typeName, eventName); + } + + [Event(15)] + public void TypeInfo_GetDeclaredField(string typeName, string fieldName) + { + WriteEvent(15, typeName, fieldName); + } + + [Event(16)] + public void TypeInfo_GetDeclaredMethod(string typeName, string methodName) + { + WriteEvent(16, typeName, methodName); + } + + [Event(17)] + public void TypeInfo_GetDeclaredProperty(string typeName, string propertyName) + { + WriteEvent(17, typeName, propertyName); + } + + [Event(18)] + public void TypeInfo_MakeArrayType(string typeName) + { + WriteEvent(18, typeName); + } + + [Event(19)] + public void TypeInfo_MakeByRefType(string typeName) + { + WriteEvent(19, typeName); + } + + [Event(20)] + public void TypeInfo_MakeGenericType(string typeName, string typeArguments) + { + WriteEvent(20, typeName, typeArguments); + } + + [Event(21)] + public void TypeInfo_MakePointerType(string typeName) + { + WriteEvent(21, typeName); + } + + [Event(22)] + public void Assembly_DefinedTypes(string assemblyName) + { + WriteEvent(22, assemblyName); + } + + [Event(23)] + public void Assembly_GetType(string assemblyName, string typeName) + { + WriteEvent(23, assemblyName, typeName); + } + + [Event(24)] + public void Assembly_CustomAttributes(string assemblyName) + { + WriteEvent(24, assemblyName); + } + + [Event(25)] + public void Assembly_FullName(string assemblyName) + { + WriteEvent(25, assemblyName); + } + + [Event(26)] + public void Assembly_GetName(string assemblyName) + { + WriteEvent(26, assemblyName); + } + + [Event(27)] + public void CustomAttributeData_ConstructorArguments(string caName) + { + WriteEvent(27, caName); + } + + [Event(28)] + public void CustomAttributeData_NamedArguments(string caName) + { + WriteEvent(28, caName); + } + + [Event(29)] + public void EventInfo_AddMethod(string typeName, string eventName) + { + WriteEvent(29, typeName, eventName); + } + + [Event(30)] + public void EventInfo_RaiseMethod(string typeName, string eventName) + { + WriteEvent(30, typeName, eventName); + } + + [Event(31)] + public void EventInfo_RemoveMethod(string typeName, string eventName) + { + WriteEvent(31, typeName, eventName); + } + + [Event(32)] + public void EventInfo_CustomAttributes(string typeName, string eventName) + { + WriteEvent(32, typeName, eventName); + } + + [Event(33)] + public void EventInfo_Name(string typeName, string eventName) + { + WriteEvent(33, typeName, eventName); + } + + [Event(34)] + public void EventInfo_DeclaringType(string typeName, string eventName) + { + WriteEvent(34, typeName, eventName); + } + + [Event(35)] + public void FieldInfo_SetValue(string typeName, string fieldName) + { + WriteEvent(35, typeName, fieldName); + } + + [Event(36)] + public void FieldInfo_GetValue(string typeName, string fieldName) + { + WriteEvent(36, typeName, fieldName); + } + + [Event(37)] + public void FieldInfo_CustomAttributes(string typeName, string fieldName) + { + WriteEvent(37, typeName, fieldName); + } + + [Event(38)] + public void FieldInfo_Name(string typeName, string fieldName) + { + WriteEvent(38, typeName, fieldName); + } + + [Event(39)] + public void FieldInfo_DeclaringType(string typeName, string fieldName) + { + WriteEvent(39, typeName, fieldName); + } + + [Event(40)] + public void MethodBase_CustomAttributes(string typeName, string methodName) + { + WriteEvent(40, typeName, methodName); + } + + [Event(41)] + public void MethodBase_Name(string typeName, string methodName) + { + WriteEvent(41, typeName, methodName); + } + + [Event(42)] + public void MethodBase_DeclaringType(string typeName, string methodName) + { + WriteEvent(42, typeName, methodName); + } + + [Event(43)] + public void MethodBase_GetParameters(string typeName, string methodName) + { + WriteEvent(43, typeName, methodName); + } + + [Event(44)] + public void MethodBase_Invoke(string typeName, string methodName) + { + WriteEvent(44, typeName, methodName); + } + + [Event(45)] + public void MethodInfo_ReturnParameter(string typeName, string methodName) + { + WriteEvent(45, typeName, methodName); + } + + [Event(46)] + public void MethodInfo_ReturnType(string typeName, string methodName) + { + WriteEvent(46, typeName, methodName); + } + + [Event(47)] + public void MethodInfo_MakeGenericMethod(string typeName, string methodName, string typeArguments) + { + WriteEvent(47, typeName, methodName, typeArguments); + } + + [Event(48)] + public void MethodInfo_CreateDelegate(string typeName, string methodName, string delegateTypeName) + { + WriteEvent(48, typeName, methodName, delegateTypeName); + } + + [Event(49)] + public void PropertyInfo_GetValue(string typeName, string propertyName) + { + WriteEvent(49, typeName, propertyName); + } + + [Event(50)] + public void PropertyInfo_SetValue(string typeName, string propertyName) + { + WriteEvent(50, typeName, propertyName); + } + + [Event(51)] + public void PropertyInfo_GetMethod(string typeName, string propertyName) + { + WriteEvent(51, typeName, propertyName); + } + + [Event(52)] + public void PropertyInfo_SetMethod(string typeName, string propertyName) + { + WriteEvent(52, typeName, propertyName); + } + + [Event(53)] + public void PropertyInfo_GetConstantValue(string typeName, string propertyName) + { + WriteEvent(53, typeName, propertyName); + } + + [Event(54)] + public void PropertyInfo_PropertyType(string typeName, string propertyName) + { + WriteEvent(54, typeName, propertyName); + } + + [Event(55)] + public void PropertyInfo_CustomAttributes(string typeName, string propertyName) + { + WriteEvent(55, typeName, propertyName); + } + + [Event(56)] + public void PropertyInfo_Name(string typeName, string propertyName) + { + WriteEvent(56, typeName, propertyName); + } + + [Event(57)] + public void PropertyInfo_DeclaringType(string typeName, string propertyName) + { + WriteEvent(57, typeName, propertyName); + } + + [Event(58)] + public void TypeInfo_AssemblyQualifiedName(string typeName) + { + WriteEvent(58, typeName); + } + #endregion + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfo.cs new file mode 100644 index 00000000000000..11fdded50fbe6d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfo.cs @@ -0,0 +1,95 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Reflection.Tracing; + +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.TypeInfos.NativeFormat +{ + internal abstract partial class NativeFormatRuntimeGenericParameterTypeInfo : RuntimeGenericParameterTypeInfo + { + protected NativeFormatRuntimeGenericParameterTypeInfo(MetadataReader reader, GenericParameterHandle genericParameterHandle, GenericParameter genericParameter) + : base(genericParameter.Number) + { + Reader = reader; + GenericParameterHandle = genericParameterHandle; + _genericParameter = genericParameter; + } + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_CustomAttributes(this); +#endif + + return RuntimeCustomAttributeData.GetCustomAttributes(Reader, _genericParameter.CustomAttributes); + } + } + + public sealed override GenericParameterAttributes GenericParameterAttributes + { + get + { + return _genericParameter.Flags; + } + } + + public sealed override int MetadataToken + { + get + { + throw new InvalidOperationException(SR.NoMetadataTokenAvailable); + } + } + + protected sealed override int InternalGetHashCode() + { + return GenericParameterHandle.GetHashCode(); + } + + protected GenericParameterHandle GenericParameterHandle { get; } + + protected MetadataReader Reader { get; } + + public sealed override string InternalGetNameIfAvailable(ref Type rootCauseForFailure) + { + if (_genericParameter.Name.IsNull(Reader)) + return string.Empty; + return _genericParameter.Name.GetString(Reader); + } + + protected sealed override QTypeDefRefOrSpec[] Constraints + { + get + { + MetadataReader reader = Reader; + LowLevelList constraints = new LowLevelList(); + foreach (Handle constraintHandle in _genericParameter.Constraints) + { + // We're skipping custom modifiers here because Roslyn generates + // a modifier for the "unmanaged" constraint. This doesn't conform to the + // ECMA-335 spec, but we need to deal with it. The modifier is not visible + // to reflection. + constraints.Add(new QTypeDefRefOrSpec(reader, constraintHandle.SkipCustomModifiers(reader))); + } + return constraints.ToArray(); + } + } + + private readonly GenericParameter _genericParameter; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.UnificationKey.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.UnificationKey.cs new file mode 100644 index 00000000000000..7f55e2cd3a266f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.UnificationKey.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.TypeInfos; + +using Internal.Reflection.Tracing; + +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.TypeInfos.NativeFormat +{ + internal sealed partial class NativeFormatRuntimeGenericParameterTypeInfoForMethods : NativeFormatRuntimeGenericParameterTypeInfo, IKeyedItem + { + // + // Key for unification. + // + internal struct UnificationKey : IEquatable + { + public UnificationKey(RuntimeNamedMethodInfo methodOwner, MetadataReader reader, GenericParameterHandle genericParameterHandle) + { + MethodOwner = methodOwner; + GenericParameterHandle = genericParameterHandle; + Reader = reader; + } + + public RuntimeNamedMethodInfo MethodOwner { get; } + public MetadataReader Reader { get; } + public GenericParameterHandle GenericParameterHandle { get; } + + public override bool Equals(object obj) + { + if (!(obj is UnificationKey other)) + return false; + return Equals(other); + } + + public bool Equals(UnificationKey other) + { + if (!(GenericParameterHandle.Equals(other.GenericParameterHandle))) + return false; + if (!(Reader == other.Reader)) + return false; + if (!MethodOwner.Equals(other.MethodOwner)) + return false; + return true; + } + + public override int GetHashCode() + { + return GenericParameterHandle.GetHashCode(); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.cs new file mode 100644 index 00000000000000..c77d716fd5a516 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.TypeInfos; + + +using Internal.Reflection.Tracing; + +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.TypeInfos.NativeFormat +{ + internal sealed partial class NativeFormatRuntimeGenericParameterTypeInfoForMethods : NativeFormatRuntimeGenericParameterTypeInfo, IKeyedItem + { + private NativeFormatRuntimeGenericParameterTypeInfoForMethods(MetadataReader reader, GenericParameterHandle genericParameterHandle, RuntimeNamedMethodInfo declaringRuntimeNamedMethodInfo) + : base(reader, genericParameterHandle, genericParameterHandle.GetGenericParameter(reader)) + { + Debug.Assert(declaringRuntimeNamedMethodInfo.DeclaringType.IsTypeDefinition); + _declaringRuntimeNamedMethodInfo = declaringRuntimeNamedMethodInfo; + } + + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => true; + + public sealed override MethodBase DeclaringMethod + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_DeclaringMethod(this); +#endif + return _declaringRuntimeNamedMethodInfo; + } + } + + // + // Implements IKeyedItem.PrepareKey. + // + // This method is the keyed item's chance to do any lazy evaluation needed to produce the key quickly. + // Concurrent unifiers are guaranteed to invoke this method at least once and wait for it + // to complete before invoking the Key property. The unifier lock is NOT held across the call. + // + // PrepareKey() must be idempodent and thread-safe. It may be invoked multiple times and concurrently. + // + public void PrepareKey() + { + } + + // + // Implements IKeyedItem.Key. + // + // Produce the key. This is a high-traffic property and is called while the hash table's lock is held. Thus, it should + // return a precomputed stored value and refrain from invoking other methods. If the keyed item wishes to + // do lazy evaluation of the key, it should do so in the PrepareKey() method. + // + public UnificationKey Key + { + get + { + return new UnificationKey(_declaringRuntimeNamedMethodInfo, Reader, GenericParameterHandle); + } + } + + internal sealed override Type InternalDeclaringType + { + get + { + return _declaringRuntimeNamedMethodInfo.DeclaringType; + } + } + + internal sealed override TypeContext TypeContext + { + get + { + TypeContext typeContext = this.DeclaringType.CastToRuntimeTypeInfo().TypeContext; + return new TypeContext(typeContext.GenericTypeArguments, _declaringRuntimeNamedMethodInfo.RuntimeGenericArgumentsOrParameters); + } + } + + private readonly RuntimeNamedMethodInfo _declaringRuntimeNamedMethodInfo; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.UnificationKey.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.UnificationKey.cs new file mode 100644 index 00000000000000..00092acda57838 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.UnificationKey.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; + +using Internal.Reflection.Tracing; + +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.TypeInfos.NativeFormat +{ + internal sealed partial class NativeFormatRuntimeGenericParameterTypeInfoForTypes : NativeFormatRuntimeGenericParameterTypeInfo + { + // + // Key for unification. + // + internal struct UnificationKey : IEquatable + { + public UnificationKey(MetadataReader reader, TypeDefinitionHandle typeDefinitionHandle, GenericParameterHandle genericParameterHandle) + { + Reader = reader; + TypeDefinitionHandle = typeDefinitionHandle; + GenericParameterHandle = genericParameterHandle; + } + + public MetadataReader Reader { get; } + public TypeDefinitionHandle TypeDefinitionHandle { get; } + public GenericParameterHandle GenericParameterHandle { get; } + + public override bool Equals(object obj) + { + if (!(obj is UnificationKey other)) + return false; + return Equals(other); + } + + public bool Equals(UnificationKey other) + { + if (!TypeDefinitionHandle.Equals(other.TypeDefinitionHandle)) + return false; + if (!(Reader == other.Reader)) + return false; + if (!(GenericParameterHandle.Equals(other.GenericParameterHandle))) + return false; + return true; + } + + public override int GetHashCode() + { + return TypeDefinitionHandle.GetHashCode(); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.cs new file mode 100644 index 00000000000000..90a0847765555e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; + +using Internal.Reflection.Tracing; + +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.TypeInfos.NativeFormat +{ + internal sealed partial class NativeFormatRuntimeGenericParameterTypeInfoForTypes : NativeFormatRuntimeGenericParameterTypeInfo + { + private NativeFormatRuntimeGenericParameterTypeInfoForTypes(MetadataReader reader, GenericParameterHandle genericParameterHandle, RuntimeTypeDefinitionTypeInfo declaringType) + : base(reader, genericParameterHandle, genericParameterHandle.GetGenericParameter(reader)) + { + _declaringType = declaringType; + } + + public sealed override bool IsGenericTypeParameter => true; + public sealed override bool IsGenericMethodParameter => false; + + public sealed override MethodBase DeclaringMethod + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_DeclaringMethod(this); +#endif + return null; + } + } + + internal sealed override Type InternalDeclaringType + { + get + { + return _declaringType; + } + } + + internal sealed override TypeContext TypeContext + { + get + { + return _declaringType.TypeContext; + } + } + + private readonly RuntimeTypeDefinitionTypeInfo _declaringType; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.UnificationKey.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.UnificationKey.cs new file mode 100644 index 00000000000000..bd320a86b52265 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.UnificationKey.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; + +using System.Reflection.Runtime.TypeInfos; + +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.TypeInfos.NativeFormat +{ + internal sealed partial class NativeFormatRuntimeNamedTypeInfo : RuntimeNamedTypeInfo, IEquatable + { + // + // Key for unification. + // + internal struct UnificationKey : IEquatable + { + // + // Q: Why is the type handle part of the unification key when it doesn't participate in the Equals/HashCode computations? + // A: It's a passenger. + // + // The typeHandle argument is "redundant" in that it can be computed from the rest of the key. However, we have callers (Type.GetTypeFromHandle()) that + // already have the typeHandle so to avoid an unnecessary round-trip computation, we require the caller to pass it in separately. + // We allow it to ride along in the key object because the ConcurrentUnifier classes we use don't support passing "extra" parameters to + // their Factory methods. + // + public UnificationKey(MetadataReader reader, TypeDefinitionHandle typeDefinitionHandle, RuntimeTypeHandle typeHandle) + { + Reader = reader; + TypeDefinitionHandle = typeDefinitionHandle; + TypeHandle = typeHandle; + } + + public MetadataReader Reader { get; } + public TypeDefinitionHandle TypeDefinitionHandle { get; } + public RuntimeTypeHandle TypeHandle { get; } + + public override bool Equals(object obj) + { + if (!(obj is UnificationKey other)) + return false; + return Equals(other); + } + + public bool Equals(UnificationKey other) + { + if (!TypeDefinitionHandle.Equals(other.TypeDefinitionHandle)) + return false; + + if (!Reader.Equals(other.Reader)) + return false; + + // The TypeHandle is not actually part of the key but riding along for convenience (see commment at head of class.) + // If the other parts of the key matched, this must too. + Debug.Assert(TypeHandle.Equals(other.TypeHandle)); + return true; + } + + public override int GetHashCode() + { + return TypeDefinitionHandle.GetHashCode(); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs new file mode 100644 index 00000000000000..a1d0567664ff78 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs @@ -0,0 +1,341 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.Assemblies; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Reflection.Tracing; + +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.TypeInfos.NativeFormat +{ + internal sealed partial class NativeFormatRuntimeNamedTypeInfo : RuntimeNamedTypeInfo + { + private NativeFormatRuntimeNamedTypeInfo(MetadataReader reader, TypeDefinitionHandle typeDefinitionHandle, RuntimeTypeHandle typeHandle) : + base(typeHandle) + { + _reader = reader; + _typeDefinitionHandle = typeDefinitionHandle; + _typeDefinition = _typeDefinitionHandle.GetTypeDefinition(reader); + } + + public sealed override Assembly Assembly + { + get + { + // If an assembly is split across multiple metadata blobs then the defining scope may + // not be the canonical scope representing the assembly. We need to look up the assembly + // by name to ensure we get the right one. + + ScopeDefinitionHandle scopeDefinitionHandle = NamespaceChain.DefiningScope; + RuntimeAssemblyName runtimeAssemblyName = scopeDefinitionHandle.ToRuntimeAssemblyName(_reader); + + return RuntimeAssemblyInfo.GetRuntimeAssembly(runtimeAssemblyName); + } + } + + protected sealed override Guid? ComputeGuidFromCustomAttributes() + { + // + // Look for a [Guid] attribute. If found, return that. + // + foreach (CustomAttributeHandle cah in _typeDefinition.CustomAttributes) + { + // We can't reference the GuidAttribute class directly as we don't have an official dependency on System.Runtime.InteropServices. + // Following age-old CLR tradition, we search for the custom attribute using a name-based search. Since this makes it harder + // to be sure we won't run into custom attribute constructors that comply with the GuidAttribute(String) signature, + // we'll check that it does and silently skip the CA if it doesn't match the expected pattern. + if (cah.IsCustomAttributeOfType(_reader, "System.Runtime.InteropServices", "GuidAttribute")) + { + CustomAttribute ca = cah.GetCustomAttribute(_reader); + HandleCollection.Enumerator fahEnumerator = ca.FixedArguments.GetEnumerator(); + if (!fahEnumerator.MoveNext()) + continue; + Handle guidStringArgumentHandle = fahEnumerator.Current; + if (fahEnumerator.MoveNext()) + continue; + if (!(guidStringArgumentHandle.ParseConstantValue(_reader) is string guidString)) + continue; + return new Guid(guidString); + } + } + + return null; + } + + protected sealed override void GetPackSizeAndSize(out int packSize, out int size) + { + packSize = _typeDefinition.PackingSize; + size = unchecked((int)(_typeDefinition.Size)); + } + + public sealed override bool IsGenericTypeDefinition + { + get + { + return _typeDefinition.GenericParameters.GetEnumerator().MoveNext(); + } + } + + public sealed override string Namespace + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_Namespace(this); +#endif + + return NamespaceChain.NameSpace.EscapeTypeNameIdentifier(); + } + } + + public sealed override Type GetGenericTypeDefinition() + { + if (_typeDefinition.GenericParameters.GetEnumerator().MoveNext()) + return this; + return base.GetGenericTypeDefinition(); + } + + public sealed override int MetadataToken + { + get + { + throw new InvalidOperationException(SR.NoMetadataTokenAvailable); + } + } + + public sealed override string ToString() + { + StringBuilder sb = null; + + foreach (GenericParameterHandle genericParameterHandle in _typeDefinition.GenericParameters) + { + if (sb == null) + { + sb = new StringBuilder(FullName); + sb.Append('['); + } + else + { + sb.Append(','); + } + + sb.Append(genericParameterHandle.GetGenericParameter(_reader).Name.GetString(_reader)); + } + + if (sb == null) + { + return FullName; + } + else + { + return sb.Append(']').ToString(); + } + } + + protected sealed override TypeAttributes GetAttributeFlagsImpl() + { + TypeAttributes attr = _typeDefinition.Flags; + return attr; + } + + protected sealed override int InternalGetHashCode() + { + return _typeDefinitionHandle.GetHashCode(); + } + + internal sealed override Type InternalDeclaringType + { + get + { + RuntimeTypeInfo declaringType = null; + TypeDefinitionHandle enclosingTypeDefHandle = _typeDefinition.EnclosingType; + if (!enclosingTypeDefHandle.IsNull(_reader)) + { + declaringType = enclosingTypeDefHandle.ResolveTypeDefinition(_reader); + } + return declaringType; + } + } + + internal sealed override string InternalFullNameOfAssembly + { + get + { + NamespaceChain namespaceChain = NamespaceChain; + ScopeDefinitionHandle scopeDefinitionHandle = namespaceChain.DefiningScope; + return scopeDefinitionHandle.ToRuntimeAssemblyName(_reader).FullName; + } + } + + public sealed override string InternalGetNameIfAvailable(ref Type rootCauseForFailure) + { + ConstantStringValueHandle nameHandle = _typeDefinition.Name; + string name = nameHandle.GetString(_reader); + + return name.EscapeTypeNameIdentifier(); + } + + protected sealed override IEnumerable TrueCustomAttributes => RuntimeCustomAttributeData.GetCustomAttributes(_reader, _typeDefinition.CustomAttributes); + + internal sealed override RuntimeTypeInfo[] RuntimeGenericTypeParameters + { + get + { + LowLevelList genericTypeParameters = new LowLevelList(); + + foreach (GenericParameterHandle genericParameterHandle in _typeDefinition.GenericParameters) + { + RuntimeTypeInfo genericParameterType = NativeFormat.NativeFormatRuntimeGenericParameterTypeInfoForTypes.GetRuntimeGenericParameterTypeInfoForTypes(this, genericParameterHandle); + genericTypeParameters.Add(genericParameterType); + } + + return genericTypeParameters.ToArray(); + } + } + + // + // Returns the base type as a typeDef, Ref, or Spec. Default behavior is to QTypeDefRefOrSpec.Null, which causes BaseType to return null. + // + internal sealed override QTypeDefRefOrSpec TypeRefDefOrSpecForBaseType + { + get + { + Handle baseType = _typeDefinition.BaseType; + if (baseType.IsNull(_reader)) + return QTypeDefRefOrSpec.Null; + return new QTypeDefRefOrSpec(_reader, baseType); + } + } + + // + // Returns the *directly implemented* interfaces as typedefs, specs or refs. ImplementedInterfaces will take care of the transitive closure and + // insertion of the TypeContext. + // + internal sealed override QTypeDefRefOrSpec[] TypeRefDefOrSpecsForDirectlyImplementedInterfaces + { + get + { + LowLevelList directlyImplementedInterfaces = new LowLevelList(); + foreach (Handle ifcHandle in _typeDefinition.Interfaces) + directlyImplementedInterfaces.Add(new QTypeDefRefOrSpec(_reader, ifcHandle)); + return directlyImplementedInterfaces.ToArray(); + } + } + + internal MetadataReader Reader + { + get + { + return _reader; + } + } + + internal TypeDefinitionHandle TypeDefinitionHandle + { + get + { + return _typeDefinitionHandle; + } + } + + internal EventHandleCollection DeclaredEventHandles + { + get + { + return _typeDefinition.Events; + } + } + + internal FieldHandleCollection DeclaredFieldHandles + { + get + { + return _typeDefinition.Fields; + } + } + + internal MethodHandleCollection DeclaredMethodAndConstructorHandles + { + get + { + return _typeDefinition.Methods; + } + } + + internal PropertyHandleCollection DeclaredPropertyHandles + { + get + { + return _typeDefinition.Properties; + } + } + + public bool Equals(NativeFormatRuntimeNamedTypeInfo other) + { + // RuntimeTypeInfo.Equals(object) is the one that encapsulates our unification strategy so defer to him. + object otherAsObject = other; + return base.Equals(otherAsObject); + } + +#if ENABLE_REFLECTION_TRACE + internal sealed override string TraceableTypeName + { + get + { + MetadataReader reader = Reader; + + String s = ""; + TypeDefinitionHandle typeDefinitionHandle = TypeDefinitionHandle; + do + { + TypeDefinition typeDefinition = typeDefinitionHandle.GetTypeDefinition(reader); + String name = typeDefinition.Name.GetString(reader); + if (s == "") + s = name; + else + s = name + "+" + s; + typeDefinitionHandle = typeDefinition.EnclosingType; + } + while (!typeDefinitionHandle.IsNull(reader)); + + String ns = NamespaceChain.NameSpace; + if (ns != null) + s = ns + "." + s; + return s; + } + } +#endif + + internal sealed override QTypeDefRefOrSpec TypeDefinitionQHandle + { + get + { + return new QTypeDefRefOrSpec(_reader, _typeDefinitionHandle, true); + } + } + + private readonly MetadataReader _reader; + private readonly TypeDefinitionHandle _typeDefinitionHandle; + private readonly TypeDefinition _typeDefinition; + + private NamespaceChain NamespaceChain + { + get + { + if (_lazyNamespaceChain == null) + _lazyNamespaceChain = new NamespaceChain(_reader, _typeDefinition.NamespaceDefinition); + return _lazyNamespaceChain; + } + } + + private volatile NamespaceChain _lazyNamespaceChain; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeTypeInfo.CoreGetDeclared.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeTypeInfo.CoreGetDeclared.cs new file mode 100644 index 00000000000000..661926423428a3 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeTypeInfo.CoreGetDeclared.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.MethodInfos.NativeFormat; +using System.Reflection.Runtime.FieldInfos.NativeFormat; +using System.Reflection.Runtime.PropertyInfos.NativeFormat; +using System.Reflection.Runtime.EventInfos.NativeFormat; +using NameFilter = System.Reflection.Runtime.BindingFlagSupport.NameFilter; + +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.TypeInfos.NativeFormat +{ + internal sealed partial class NativeFormatRuntimeNamedTypeInfo + { + internal sealed override IEnumerable CoreGetDeclaredConstructors(NameFilter optionalNameFilter, RuntimeTypeInfo contextTypeInfo) + { + // + // - It may sound odd to get a non-null name filter for a constructor search, but Type.GetMember() is an api that does this. + // + // - All GetConstructor() apis act as if BindingFlags.DeclaredOnly were specified. So the ReflectedType will always be the declaring type and so is not passed to this method. + // + MetadataReader reader = Reader; + foreach (MethodHandle methodHandle in DeclaredMethodAndConstructorHandles) + { + Method method = methodHandle.GetMethod(reader); + + if (!NativeFormatMetadataReaderExtensions.IsConstructor(ref method, reader)) + continue; + + if (optionalNameFilter == null || optionalNameFilter.Matches(method.Name, reader)) + yield return RuntimePlainConstructorInfo.GetRuntimePlainConstructorInfo(new NativeFormatMethodCommon(methodHandle, this, contextTypeInfo)); + } + } + + internal sealed override IEnumerable CoreGetDeclaredMethods(NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType, RuntimeTypeInfo contextTypeInfo) + { + MetadataReader reader = Reader; + foreach (MethodHandle methodHandle in DeclaredMethodAndConstructorHandles) + { + Method method = methodHandle.GetMethod(reader); + + if (NativeFormatMetadataReaderExtensions.IsConstructor(ref method, reader)) + continue; + + if (optionalNameFilter == null || optionalNameFilter.Matches(method.Name, reader)) + yield return RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(new NativeFormatMethodCommon(methodHandle, this, contextTypeInfo), reflectedType); + } + } + + internal sealed override IEnumerable CoreGetDeclaredEvents(NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType, RuntimeTypeInfo contextTypeInfo) + { + MetadataReader reader = Reader; + foreach (EventHandle eventHandle in DeclaredEventHandles) + { + if (optionalNameFilter == null || optionalNameFilter.Matches(eventHandle.GetEvent(reader).Name, reader)) + yield return NativeFormatRuntimeEventInfo.GetRuntimeEventInfo(eventHandle, this, contextTypeInfo, reflectedType); + } + } + + internal sealed override IEnumerable CoreGetDeclaredFields(NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType, RuntimeTypeInfo contextTypeInfo) + { + MetadataReader reader = Reader; + foreach (FieldHandle fieldHandle in DeclaredFieldHandles) + { + if (optionalNameFilter == null || optionalNameFilter.Matches(fieldHandle.GetField(reader).Name, reader)) + yield return NativeFormatRuntimeFieldInfo.GetRuntimeFieldInfo(fieldHandle, this, contextTypeInfo, reflectedType); + } + } + + internal sealed override IEnumerable CoreGetDeclaredProperties(NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType, RuntimeTypeInfo contextTypeInfo) + { + MetadataReader reader = Reader; + foreach (PropertyHandle propertyHandle in DeclaredPropertyHandles) + { + if (optionalNameFilter == null || optionalNameFilter.Matches(propertyHandle.GetProperty(reader).Name, reader)) + yield return NativeFormatRuntimePropertyInfo.GetRuntimePropertyInfo(propertyHandle, this, contextTypeInfo, reflectedType); + } + } + + internal sealed override IEnumerable CoreGetDeclaredNestedTypes(NameFilter optionalNameFilter) + { + foreach (TypeDefinitionHandle nestedTypeHandle in _typeDefinition.NestedTypes) + { + if (optionalNameFilter == null || optionalNameFilter.Matches(nestedTypeHandle.GetTypeDefinition(_reader).Name, _reader)) + yield return nestedTypeHandle.GetNamedType(_reader); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs new file mode 100644 index 00000000000000..040b29b333d75f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs @@ -0,0 +1,327 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.MethodInfos; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.TypeInfos +{ + // + // The runtime's implementation of TypeInfo's for array types. + // + internal sealed partial class RuntimeArrayTypeInfo : RuntimeHasElementTypeInfo + { + private RuntimeArrayTypeInfo(UnificationKey key, bool multiDim, int rank) + : base(key) + { + Debug.Assert(multiDim || rank == 1); + _multiDim = multiDim; + _rank = rank; + } + + public sealed override int GetArrayRank() + { + return _rank; + } + + protected sealed override bool IsArrayImpl() => true; + public sealed override bool IsSZArray => !_multiDim; + public sealed override bool IsVariableBoundArray => _multiDim; + protected sealed override bool IsByRefImpl() => false; + protected sealed override bool IsPointerImpl() => false; + + protected sealed override TypeAttributes GetAttributeFlagsImpl() + { + return TypeAttributes.AutoLayout | TypeAttributes.AnsiClass | TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Serializable; + } + + internal sealed override IEnumerable SyntheticConstructors + { + get + { + bool multiDim = this.IsVariableBoundArray; + int rank = this.GetArrayRank(); + + RuntimeArrayTypeInfo arrayType = this; + RuntimeTypeInfo countType = typeof(int).CastToRuntimeTypeInfo(); + + { + // + // Expose a constructor that takes n Int32's (one for each dimension) and constructs a zero lower-bounded array. For example, + // + // String[,] + // + // exposes + // + // .ctor(int32, int32) + // + + RuntimeTypeInfo[] ctorParameters = new RuntimeTypeInfo[rank]; + for (int i = 0; i < rank; i++) + ctorParameters[i] = countType; + yield return RuntimeSyntheticConstructorInfo.GetRuntimeSyntheticConstructorInfo( + SyntheticMethodId.ArrayCtor, + arrayType, + ctorParameters, + InvokerOptions.AllowNullThis | InvokerOptions.DontWrapException, + delegate (object _this, object[] args, Type thisType) + { + int[] lengths = new int[rank]; + for (int i = 0; i < rank; i++) + { + lengths[i] = (int)(args[i]); + } + return ReflectionCoreExecution.ExecutionEnvironment.NewMultiDimArray(arrayType.TypeHandle, lengths, null); + } + ); + } + + if (!multiDim) + { + // + // Jagged arrays also expose constructors that take multiple indices and construct a jagged matrix. For example, + // + // String[][][][] + // + // also exposes: + // + // .ctor(int32, int32) + // .ctor(int32, int32, int32) + // .ctor(int32, int32, int32, int32) + // + + int parameterCount = 2; + RuntimeTypeInfo elementType = this.InternalRuntimeElementType; + while (elementType.IsSZArray) + { + RuntimeTypeInfo[] ctorParameters = new RuntimeTypeInfo[parameterCount]; + for (int i = 0; i < parameterCount; i++) + ctorParameters[i] = countType; + yield return RuntimeSyntheticConstructorInfo.GetRuntimeSyntheticConstructorInfo( + SyntheticMethodId.ArrayCtorJagged + parameterCount, + arrayType, + ctorParameters, + InvokerOptions.AllowNullThis | InvokerOptions.DontWrapException, + delegate (object _this, object[] args, Type thisType) + { + int[] lengths = new int[args.Length]; + for (int i = 0; i < args.Length; i++) + { + lengths[i] = (int)(args[i]); + } + Array jaggedArray = CreateJaggedArray(arrayType, lengths, 0); + return jaggedArray; + } + ); + parameterCount++; + elementType = elementType.InternalRuntimeElementType; + } + } + + if (multiDim) + { + // + // Expose a constructor that takes n*2 Int32's (two for each dimension) and constructs a arbitrarily lower-bounded array. For example, + // + // String[,] + // + // exposes + // + // .ctor(int32, int32, int32, int32) + // + + RuntimeTypeInfo[] ctorParameters = new RuntimeTypeInfo[rank * 2]; + for (int i = 0; i < rank * 2; i++) + ctorParameters[i] = countType; + yield return RuntimeSyntheticConstructorInfo.GetRuntimeSyntheticConstructorInfo( + SyntheticMethodId.ArrayMultiDimCtor, + arrayType, + ctorParameters, + InvokerOptions.AllowNullThis | InvokerOptions.DontWrapException, + delegate (object _this, object[] args, Type thisType) + { + int[] lengths = new int[rank]; + int[] lowerBounds = new int[rank]; + for (int i = 0; i < rank; i++) + { + lowerBounds[i] = (int)(args[i * 2]); + lengths[i] = (int)(args[i * 2 + 1]); + } + return ReflectionCoreExecution.ExecutionEnvironment.NewMultiDimArray(arrayType.TypeHandle, lengths, lowerBounds); + } + ); + } + } + } + + internal sealed override IEnumerable SyntheticMethods + { + get + { + int rank = this.GetArrayRank(); + + RuntimeTypeInfo indexType = typeof(int).CastToRuntimeTypeInfo(); + RuntimeArrayTypeInfo arrayType = this; + RuntimeTypeInfo elementType = arrayType.InternalRuntimeElementType; + RuntimeTypeInfo voidType = typeof(void).CastToRuntimeTypeInfo(); + + { + RuntimeTypeInfo[] getParameters = new RuntimeTypeInfo[rank]; + for (int i = 0; i < rank; i++) + getParameters[i] = indexType; + yield return RuntimeSyntheticMethodInfo.GetRuntimeSyntheticMethodInfo( + SyntheticMethodId.ArrayGet, + "Get", + arrayType, + getParameters, + elementType, + InvokerOptions.None, + delegate (object _this, object[] args, Type thisType) + { + Array array = (Array)_this; + int[] indices = new int[rank]; + for (int i = 0; i < rank; i++) + indices[i] = (int)(args[i]); + return array.GetValue(indices); + } + ); + } + + { + RuntimeTypeInfo[] setParameters = new RuntimeTypeInfo[rank + 1]; + for (int i = 0; i < rank; i++) + setParameters[i] = indexType; + setParameters[rank] = elementType; + yield return RuntimeSyntheticMethodInfo.GetRuntimeSyntheticMethodInfo( + SyntheticMethodId.ArraySet, + "Set", + arrayType, + setParameters, + voidType, + InvokerOptions.None, + delegate (object _this, object[] args, Type thisType) + { + Array array = (Array)_this; + int[] indices = new int[rank]; + for (int i = 0; i < rank; i++) + indices[i] = (int)(args[i]); + object value = args[rank]; + array.SetValue(value, indices); + return null; + } + ); + } + + { + RuntimeTypeInfo[] addressParameters = new RuntimeTypeInfo[rank]; + for (int i = 0; i < rank; i++) + addressParameters[i] = indexType; + yield return RuntimeSyntheticMethodInfo.GetRuntimeSyntheticMethodInfo( + SyntheticMethodId.ArrayAddress, + "Address", + arrayType, + addressParameters, + elementType.GetByRefType(), + InvokerOptions.None, + delegate (object _this, object[] args, Type thisType) + { + throw new NotSupportedException(); + } + ); + } + } + } + + // + // Returns the base type as a typeDef, Ref, or Spec. Default behavior is to QTypeDefRefOrSpec.Null, which causes BaseType to return null. + // + internal sealed override QTypeDefRefOrSpec TypeRefDefOrSpecForBaseType + { + get + { + return TypeDefInfoProjectionForArrays.TypeRefDefOrSpecForBaseType; + } + } + + // + // Returns the *directly implemented* interfaces as typedefs, specs or refs. ImplementedInterfaces will take care of the transitive closure and + // insertion of the TypeContext. + // + internal sealed override QTypeDefRefOrSpec[] TypeRefDefOrSpecsForDirectlyImplementedInterfaces + { + get + { + if (this.IsVariableBoundArray) + return Array.Empty(); + else + return TypeDefInfoProjectionForArrays.TypeRefDefOrSpecsForDirectlyImplementedInterfaces; + } + } + + // + // Returns the generic parameter substitutions to use when enumerating declared members, base class and implemented interfaces. + // + internal sealed override TypeContext TypeContext + { + get + { + return new TypeContext(new RuntimeTypeInfo[] { this.InternalRuntimeElementType }, null); + } + } + + protected sealed override string Suffix + { + get + { + if (!_multiDim) + return "[]"; + else if (_rank == 1) + return "[*]"; + else + return "[" + new string(',', _rank - 1) + "]"; + } + } + + // + // Arrays don't have a true typedef behind them but for the purpose of reporting base classes and interfaces, we can create a pretender. + // + private RuntimeTypeInfo TypeDefInfoProjectionForArrays + { + get + { + RuntimeTypeHandle projectionTypeHandleForArrays = ReflectionCoreExecution.ExecutionEnvironment.ProjectionTypeForArrays; + RuntimeTypeInfo projectionRuntimeTypeForArrays = projectionTypeHandleForArrays.GetTypeForRuntimeTypeHandle(); + return projectionRuntimeTypeForArrays; + } + } + + // + // Helper for jagged array constructors. + // + private Array CreateJaggedArray(RuntimeTypeInfo arrayType, int[] lengths, int index) + { + int length = lengths[index]; + Array jaggedArray = ReflectionCoreExecution.ExecutionEnvironment.NewArray(arrayType.TypeHandle, length); + if (index != lengths.Length - 1) + { + for (int i = 0; i < length; i++) + { + Array subArray = CreateJaggedArray(arrayType.InternalRuntimeElementType, lengths, index + 1); + jaggedArray.SetValue(subArray, i); + } + } + return jaggedArray; + } + + private readonly int _rank; + private readonly bool _multiDim; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeBlockedTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeBlockedTypeInfo.cs new file mode 100644 index 00000000000000..e625166fed8634 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeBlockedTypeInfo.cs @@ -0,0 +1,255 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.Assemblies; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.LowLevelLinq; +using Internal.Reflection.Tracing; +using Internal.Reflection.Core.Execution; + +using CharSet = System.Runtime.InteropServices.CharSet; +using LayoutKind = System.Runtime.InteropServices.LayoutKind; +using StructLayoutAttribute = System.Runtime.InteropServices.StructLayoutAttribute; + +namespace System.Reflection.Runtime.TypeInfos +{ + // + // TypeInfos that represent type definitions (i.e. Foo or Foo<>) or constructed generic types (Foo) + // that can never be reflection-enabled due to the framework Reflection block. + // + // These types differ from NoMetadata TypeInfos in that properties that inquire about members, + // custom attributes or interfaces return an empty list rather than throwing a MissingMetadataException. + // + // Since these represent "internal framework types", the app cannot prove we are lying. + // + internal sealed partial class RuntimeBlockedTypeInfo : RuntimeTypeDefinitionTypeInfo + { + private RuntimeBlockedTypeInfo(RuntimeTypeHandle typeHandle, bool isGenericTypeDefinition) + { + _typeHandle = typeHandle; + _isGenericTypeDefinition = isGenericTypeDefinition; + } + + public sealed override Assembly Assembly + { + get + { + return typeof(object).Assembly; + } + } + + public sealed override bool ContainsGenericParameters + { + get + { + return _isGenericTypeDefinition; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_CustomAttributes(this); +#endif + return Empty.Enumerable; + } + } + + public sealed override string FullName + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_FullName(this); +#endif + return GeneratedName; + } + } + + public sealed override Guid GUID + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + +#if DEBUG + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => base.HasSameMetadataDefinitionAs(other); +#endif + + public sealed override bool IsGenericTypeDefinition + { + get + { + return _isGenericTypeDefinition; + } + } + + public sealed override string Namespace + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_Namespace(this); +#endif + return null; // Reflection-blocked framework types report themselves as existing in the "root" namespace. + } + } + + public sealed override StructLayoutAttribute StructLayoutAttribute + { + get + { + return new StructLayoutAttribute(LayoutKind.Auto) + { + CharSet = CharSet.Ansi, + Pack = 8, + Size = 0, + }; + } + } + + public sealed override string ToString() + { + return _typeHandle.LastResortString(); + } + + public sealed override int MetadataToken + { + get + { + throw new InvalidOperationException(SR.NoMetadataTokenAvailable); + } + } + + protected sealed override TypeAttributes GetAttributeFlagsImpl() + { + return TypeAttributes.Class | TypeAttributes.NotPublic; + } + + protected sealed override int InternalGetHashCode() + { + return _typeHandle.GetHashCode(); + } + + // + // Returns the anchoring typedef that declares the members that this type wants returned by the Declared*** properties. + // The Declared*** properties will project the anchoring typedef's members by overriding their DeclaringType property with "this" + // and substituting the value of this.TypeContext into any generic parameters. + // + // Default implementation returns null which causes the Declared*** properties to return no members. + // + // Note that this does not apply to DeclaredNestedTypes. Nested types and their containers have completely separate generic instantiation environments + // (despite what C# might lead you to think.) Constructed generic types return the exact same same nested types that its generic type definition does + // - i.e. their DeclaringTypes refer back to the generic type definition, not the constructed generic type.) + // + // Note also that we cannot use this anchoring concept for base types because of generic parameters. Generic parameters return + // baseclass and interfaces based on its constraints. + // + internal sealed override RuntimeNamedTypeInfo AnchoringTypeDefinitionForDeclaredMembers + { + get + { + return null; // this causes the type to report having no members. + } + } + + internal sealed override bool CanBrowseWithoutMissingMetadataExceptions => true; + + internal sealed override RuntimeTypeInfo[] RuntimeGenericTypeParameters + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + internal sealed override Type InternalDeclaringType + { + get + { + return null; + } + } + + public sealed override string InternalGetNameIfAvailable(ref Type rootCauseForFailure) + { + return GeneratedName; + } + + internal sealed override string InternalFullNameOfAssembly + { + get + { + return GeneratedName; + } + } + + internal sealed override RuntimeTypeHandle InternalTypeHandleIfAvailable + { + get + { + return _typeHandle; + } + } + + // + // Returns the base type as a typeDef, Ref, or Spec. Default behavior is to QTypeDefRefOrSpec.Null, which causes BaseType to return null. + // + internal sealed override QTypeDefRefOrSpec TypeRefDefOrSpecForBaseType + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + // + // Returns the *directly implemented* interfaces as typedefs, specs or refs. ImplementedInterfaces will take care of the transitive closure and + // insertion of the TypeContext. + // + internal sealed override QTypeDefRefOrSpec[] TypeRefDefOrSpecsForDirectlyImplementedInterfaces + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + // + // Returns the generic parameter substitutions to use when enumerating declared members, base class and implemented interfaces. + // + internal sealed override TypeContext TypeContext + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + private string GeneratedName + { + get + { + return _lazyGeneratedName ?? (_lazyGeneratedName = BlockedRuntimeTypeNameGenerator.GetNameForBlockedRuntimeType(_typeHandle)); + } + } + + private readonly RuntimeTypeHandle _typeHandle; + private readonly bool _isGenericTypeDefinition; + private volatile string _lazyGeneratedName; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeByRefTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeByRefTypeInfo.cs new file mode 100644 index 00000000000000..56c49e78c90c96 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeByRefTypeInfo.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; + +namespace System.Reflection.Runtime.TypeInfos +{ + // + // The runtime's implementation of TypeInfo's for byref types. + // + internal sealed partial class RuntimeByRefTypeInfo : RuntimeHasElementTypeInfo + { + private RuntimeByRefTypeInfo(UnificationKey key) + : base(key) + { + } + + protected sealed override bool IsArrayImpl() => false; + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + protected sealed override bool IsByRefImpl() => true; + protected sealed override bool IsPointerImpl() => false; + + protected sealed override string Suffix + { + get + { + return "&"; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.UnificationKey.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.UnificationKey.cs new file mode 100644 index 00000000000000..ad40cc4ef971e4 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.UnificationKey.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Runtime.TypeInfos +{ + internal sealed partial class RuntimeCLSIDTypeInfo + { + // + // Key for unification. + // + private struct UnificationKey : IEquatable + { + public UnificationKey(Guid clsid, string server) + { + ClsId = clsid; + Server = server; + } + + public Guid ClsId { get; } + public string Server { get; } + + public override bool Equals(object obj) + { + if (!(obj is UnificationKey other)) + return false; + return Equals(other); + } + + public bool Equals(UnificationKey other) + { + if (!ClsId.Equals(other.ClsId)) + return false; + if (Server != other.Server) + return false; + + return true; + } + + public override int GetHashCode() + { + return ClsId.GetHashCode() ^ (Server == null ? 0 : Server.GetHashCode()); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.cs new file mode 100644 index 00000000000000..ec4b500e9a54c5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.MethodInfos; +using System.Runtime.InteropServices; + +using Internal.Reflection.Tracing; +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.TypeInfos +{ + // + // TypeInfos returned by the Type.GetTypeFromCLSID() api. These "types" are little more than mules that hold a CLSID + // and optional remote server name. The only useful thing to do with them is to pass them to Activator.CreateInstance(). + // + internal sealed partial class RuntimeCLSIDTypeInfo : RuntimeTypeDefinitionTypeInfo, IKeyedItem + { + private RuntimeCLSIDTypeInfo(Guid clsid, string server) + { + _key = new UnificationKey(clsid, server); + _constructors = new RuntimeConstructorInfo[] { RuntimeCLSIDNullaryConstructorInfo.GetRuntimeCLSIDNullaryConstructorInfo(this) }; + } + + public sealed override Assembly Assembly => BaseType.Assembly; + public sealed override bool ContainsGenericParameters => false; + public sealed override string FullName => BaseType.FullName; + public sealed override Guid GUID => _key.ClsId; + public sealed override string InternalGetNameIfAvailable(ref Type rootCauseForFailure) => BaseType.InternalGetNameIfAvailable(ref rootCauseForFailure); + public sealed override bool IsGenericTypeDefinition => false; + public sealed override int MetadataToken => BaseType.MetadataToken; + public sealed override string Namespace => BaseType.Namespace; + public sealed override StructLayoutAttribute StructLayoutAttribute => BaseType.StructLayoutAttribute; + public sealed override string ToString() => BaseType.ToString(); + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_CustomAttributes(this); +#endif + + return Empty.Enumerable; + } + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // This logic is written to match CoreCLR's behavior. + return other is RuntimeCLSIDTypeInfo; + } + + protected sealed override TypeAttributes GetAttributeFlagsImpl() => TypeAttributes.Public; + protected sealed override int InternalGetHashCode() => _key.GetHashCode(); + + internal sealed override Type BaseTypeWithoutTheGenericParameterQuirk => typeof(object); + internal sealed override bool CanBrowseWithoutMissingMetadataExceptions => BaseType.CastToRuntimeTypeInfo().CanBrowseWithoutMissingMetadataExceptions; + internal sealed override Type InternalDeclaringType => null; + internal sealed override string InternalFullNameOfAssembly => BaseType.Assembly.FullName; + internal sealed override IEnumerable SyntheticConstructors => _constructors; + + // No RuntimeTypeHandle for this flavor of type. This does lead to the oddity that Activator.CreateInstance() returns an object whose GetType() + // returns __ComObject rather than this specific type. But this has happened for years on the full framework without incident. + internal sealed override RuntimeTypeHandle InternalTypeHandleIfAvailable => default(RuntimeTypeHandle); + + internal string Server => _key.Server; + + // + // Implements IKeyedItem.PrepareKey. + // + // This method is the keyed item's chance to do any lazy evaluation needed to produce the key quickly. + // Concurrent unifiers are guaranteed to invoke this method at least once and wait for it + // to complete before invoking the Key property. The unifier lock is NOT held across the call. + // + // PrepareKey() must be idempodent and thread-safe. It may be invoked multiple times and concurrently. + // + void IKeyedItem.PrepareKey() { } + + // + // Implements IKeyedItem.Key. + // + // Produce the key. This is a high-traffic property and is called while the hash table's lock is held. Thus, it should + // return a precomputed stored value and refrain from invoking other methods. If the keyed item wishes to + // do lazy evaluation of the key, it should do so in the PrepareKey() method. + // + UnificationKey IKeyedItem.Key => _key; + + private readonly UnificationKey _key; + private readonly RuntimeConstructorInfo[] _constructors; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.UnificationKey.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.UnificationKey.cs new file mode 100644 index 00000000000000..f66f916ddd03ab --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.UnificationKey.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Collections.Concurrent; + +namespace System.Reflection.Runtime.TypeInfos +{ + internal sealed partial class RuntimeConstructedGenericTypeInfo : RuntimeTypeInfo, IKeyedItem + { + // + // Key for unification. + // + internal struct UnificationKey : IEquatable + { + // + // Q: Why is the type handle part of the unification key when it doesn't participate in the Equals/HashCode computations? + // A: It's a passenger. + // + // The typeHandle argument is "redundant" in that it can be computed from the rest of the key. However, we have callers (Type.GetTypeFromHandle()) that + // already have the typeHandle so to avoid an unnecessary round-trip computation, we require the caller to pass it in separately. + // We allow it to ride along in the key object because the ConcurrentUnifier classes we use don't support passing "extra" parameters to + // their Factory methods. + // + public UnificationKey(RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments, RuntimeTypeHandle typeHandle) + { + GenericTypeDefinition = genericTypeDefinition; + GenericTypeArguments = genericTypeArguments; + TypeHandle = typeHandle; + } + + public RuntimeTypeInfo GenericTypeDefinition { get; } + public RuntimeTypeInfo[] GenericTypeArguments { get; } + public RuntimeTypeHandle TypeHandle { get; } + + public override bool Equals(object obj) + { + if (!(obj is UnificationKey other)) + return false; + return Equals(other); + } + + public bool Equals(UnificationKey other) + { + if (!GenericTypeDefinition.Equals(other.GenericTypeDefinition)) + return false; + if (GenericTypeArguments.Length != other.GenericTypeArguments.Length) + return false; + for (int i = 0; i < GenericTypeArguments.Length; i++) + { + if (!(GenericTypeArguments[i].Equals(other.GenericTypeArguments[i]))) + return false; + } + + // The TypeHandle is not actually part of the key but riding along for convenience (see commment at head of class.) + // If the other parts of the key matched, this must too. + Debug.Assert(TypeHandle.Equals(other.TypeHandle)); + return true; + } + + public override int GetHashCode() + { + int hashCode = GenericTypeDefinition.GetHashCode(); + for (int i = 0; i < GenericTypeArguments.Length; i++) + { + hashCode ^= GenericTypeArguments[i].GetHashCode(); + } + return hashCode; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.cs new file mode 100644 index 00000000000000..63938404fae5d2 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.cs @@ -0,0 +1,341 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; + +using Internal.Reflection.Tracing; +using Internal.Reflection.Core.Execution; + +using StructLayoutAttribute = System.Runtime.InteropServices.StructLayoutAttribute; + +namespace System.Reflection.Runtime.TypeInfos +{ + // + // TypeInfos that represent constructed generic types. + // + // + internal sealed partial class RuntimeConstructedGenericTypeInfo : RuntimeTypeInfo, IKeyedItem + { + private RuntimeConstructedGenericTypeInfo(UnificationKey key) + { + _key = key; + } + + public sealed override bool IsTypeDefinition => false; + public sealed override bool IsGenericTypeDefinition => false; + protected sealed override bool HasElementTypeImpl() => false; + protected sealed override bool IsArrayImpl() => false; + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + protected sealed override bool IsByRefImpl() => false; + protected sealed override bool IsPointerImpl() => false; + public sealed override bool IsConstructedGenericType => true; + public sealed override bool IsGenericParameter => false; + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => false; + public sealed override bool IsByRefLike => GenericTypeDefinitionTypeInfo.IsByRefLike; + + // + // Implements IKeyedItem.PrepareKey. + // + // This method is the keyed item's chance to do any lazy evaluation needed to produce the key quickly. + // Concurrent unifiers are guaranteed to invoke this method at least once and wait for it + // to complete before invoking the Key property. The unifier lock is NOT held across the call. + // + // PrepareKey() must be idempodent and thread-safe. It may be invoked multiple times and concurrently. + // + public void PrepareKey() + { + } + + // + // Implements IKeyedItem.Key. + // + // Produce the key. This is a high-traffic property and is called while the hash table's lock is held. Thus, it should + // return a precomputed stored value and refrain from invoking other methods. If the keyed item wishes to + // do lazy evaluation of the key, it should do so in the PrepareKey() method. + // + public UnificationKey Key + { + get + { + return _key; + } + } + + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_CustomAttributes(this); +#endif + + return GenericTypeDefinitionTypeInfo.CustomAttributes; + } + } + + public sealed override string FullName + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_FullName(this); +#endif + // Desktop quirk: open constructions don't have "fullNames". + if (ContainsGenericParameters) + return null; + + StringBuilder fullName = new StringBuilder(); + fullName.Append(GenericTypeDefinitionTypeInfo.FullName); + fullName.Append('['); + + RuntimeTypeInfo[] genericTypeArguments = _key.GenericTypeArguments; + for (int i = 0; i < genericTypeArguments.Length; i++) + { + if (i != 0) + fullName.Append(','); + + fullName.Append('['); + fullName.Append(genericTypeArguments[i].AssemblyQualifiedName); + fullName.Append(']'); + } + fullName.Append(']'); + return fullName.ToString(); + } + } + + public sealed override Type GetGenericTypeDefinition() + { + return GenericTypeDefinitionTypeInfo; + } + + public sealed override Guid GUID + { + get + { + return GenericTypeDefinitionTypeInfo.GUID; + } + } + + public sealed override Assembly Assembly + { + get + { + return GenericTypeDefinitionTypeInfo.Assembly; + } + } + + public sealed override bool ContainsGenericParameters + { + get + { + foreach (RuntimeTypeInfo typeArgument in _key.GenericTypeArguments) + { + if (typeArgument.ContainsGenericParameters) + return true; + } + return false; + } + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + return GenericTypeDefinitionTypeInfo.HasSameMetadataDefinitionAs(other); + } + + public sealed override string Namespace + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_Namespace(this); +#endif + return GenericTypeDefinitionTypeInfo.Namespace; + } + } + + public sealed override StructLayoutAttribute StructLayoutAttribute + { + get + { + return GenericTypeDefinitionTypeInfo.StructLayoutAttribute; + } + } + + public sealed override int MetadataToken + { + get + { + return GenericTypeDefinitionTypeInfo.MetadataToken; + } + } + + public sealed override string ToString() + { + // Get the FullName of the generic type definition in a pay-for-play safe way. + RuntimeTypeInfo genericTypeDefinition = GenericTypeDefinitionTypeInfo; + string genericTypeDefinitionString = null; + if (genericTypeDefinition.InternalNameIfAvailable != null) // Want to avoid "cry-wolf" exceptions: if we can't even get the simple name, don't bother getting the FullName. + { + // Given our current pay for play policy, it should now be safe to attempt getting the FullName. (But guard with a try-catch in case this assumption is wrong.) + try + { + genericTypeDefinitionString = genericTypeDefinition.FullName; + } + catch (Exception) + { + } + } + // If all else fails, use the ToString() - it won't match the legacy CLR but with no metadata, we can't match it anyway. + if (genericTypeDefinitionString == null) + genericTypeDefinitionString = genericTypeDefinition.ToString(); + + // Now, append the generic type arguments. + StringBuilder sb = new StringBuilder(); + sb.Append(genericTypeDefinitionString); + sb.Append('['); + RuntimeTypeInfo[] genericTypeArguments = _key.GenericTypeArguments; + for (int i = 0; i < genericTypeArguments.Length; i++) + { + if (i != 0) + sb.Append(','); + sb.Append(genericTypeArguments[i].ToString()); + } + sb.Append(']'); + return sb.ToString(); + } + + protected sealed override TypeAttributes GetAttributeFlagsImpl() + { + return GenericTypeDefinitionTypeInfo.Attributes; + } + + protected sealed override int InternalGetHashCode() + { + return _key.GetHashCode(); + } + + // + // Returns the anchoring typedef that declares the members that this type wants returned by the Declared*** properties. + // The Declared*** properties will project the anchoring typedef's members by overriding their DeclaringType property with "this" + // and substituting the value of this.TypeContext into any generic parameters. + // + // Default implementation returns null which causes the Declared*** properties to return no members. + // + // Note that this does not apply to DeclaredNestedTypes. Nested types and their containers have completely separate generic instantiation environments + // (despite what C# might lead you to think.) Constructed generic types return the exact same same nested types that its generic type definition does + // - i.e. their DeclaringTypes refer back to the generic type definition, not the constructed generic type.) + // + // Note also that we cannot use this anchoring concept for base types because of generic parameters. Generic parameters return + // baseclass and interfaces based on its constraints. + // + internal sealed override RuntimeNamedTypeInfo AnchoringTypeDefinitionForDeclaredMembers + { + get + { + RuntimeTypeInfo genericTypeDefinition = this.GenericTypeDefinitionTypeInfo; + if (!(genericTypeDefinition is RuntimeNamedTypeInfo genericTypeDefinitionNamedTypeInfo)) + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(genericTypeDefinition); + return genericTypeDefinitionNamedTypeInfo; + } + } + + internal sealed override bool CanBrowseWithoutMissingMetadataExceptions => GenericTypeDefinitionTypeInfo.CanBrowseWithoutMissingMetadataExceptions; + + internal sealed override Type InternalDeclaringType + { + get + { + RuntimeTypeHandle typeHandle = InternalTypeHandleIfAvailable; + if ((!typeHandle.IsNull()) && ReflectionCoreExecution.ExecutionEnvironment.IsReflectionBlocked(typeHandle)) + return null; + return GenericTypeDefinitionTypeInfo.InternalDeclaringType; + } + } + + internal sealed override string InternalFullNameOfAssembly + { + get + { + return GenericTypeDefinitionTypeInfo.InternalFullNameOfAssembly; + } + } + + public sealed override string InternalGetNameIfAvailable(ref Type rootCauseForFailure) + { + return GenericTypeDefinitionTypeInfo.InternalGetNameIfAvailable(ref rootCauseForFailure); + } + + internal sealed override RuntimeTypeInfo[] InternalRuntimeGenericTypeArguments + { + get + { + return _key.GenericTypeArguments; + } + } + + internal sealed override RuntimeTypeHandle InternalTypeHandleIfAvailable + { + get + { + return _key.TypeHandle; + } + } + + // + // Returns the base type as a typeDef, Ref, or Spec. Default behavior is to QTypeDefRefOrSpec.Null, which causes BaseType to return null. + // + internal sealed override QTypeDefRefOrSpec TypeRefDefOrSpecForBaseType + { + get + { + return this.GenericTypeDefinitionTypeInfo.TypeRefDefOrSpecForBaseType; + } + } + + // + // Returns the *directly implemented* interfaces as typedefs, specs or refs. ImplementedInterfaces will take care of the transitive closure and + // insertion of the TypeContext. + // + internal sealed override QTypeDefRefOrSpec[] TypeRefDefOrSpecsForDirectlyImplementedInterfaces + { + get + { + return this.GenericTypeDefinitionTypeInfo.TypeRefDefOrSpecsForDirectlyImplementedInterfaces; + } + } + + // + // Returns the generic parameter substitutions to use when enumerating declared members, base class and implemented interfaces. + // + internal sealed override TypeContext TypeContext + { + get + { + return new TypeContext(this.InternalRuntimeGenericTypeArguments, null); + } + } + + private RuntimeTypeInfo GenericTypeDefinitionTypeInfo + { + get + { + return _key.GenericTypeDefinition; + } + } + + private readonly UnificationKey _key; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeGenericParameterTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeGenericParameterTypeInfo.cs new file mode 100644 index 00000000000000..2434f3b927854a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeGenericParameterTypeInfo.cs @@ -0,0 +1,215 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; + +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.TypeInfos +{ + internal abstract class RuntimeGenericParameterTypeInfo : RuntimeTypeInfo + { + protected RuntimeGenericParameterTypeInfo(int position) + { + _position = position; + } + + public sealed override bool IsTypeDefinition => false; + public sealed override bool IsGenericTypeDefinition => false; + protected sealed override bool HasElementTypeImpl() => false; + protected sealed override bool IsArrayImpl() => false; + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + protected sealed override bool IsByRefImpl() => false; + protected sealed override bool IsPointerImpl() => false; + public sealed override bool IsConstructedGenericType => false; + public sealed override bool IsGenericParameter => true; + public abstract override bool IsGenericTypeParameter { get; } + public abstract override bool IsGenericMethodParameter { get; } + public sealed override bool IsByRefLike => false; + + public sealed override Assembly Assembly + { + get + { + return DeclaringType.Assembly; + } + } + + public sealed override bool ContainsGenericParameters + { + get + { + return true; + } + } + + public abstract override MethodBase DeclaringMethod { get; } + + + + public sealed override Type[] GetGenericParameterConstraints() + { + return ConstraintInfos.CloneTypeArray(); + } + + public sealed override string FullName + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_FullName(this); +#endif + return null; // We return null as generic parameter types are not roundtrippable through Type.GetType(). + } + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // Unlike most other MemberInfo objects, generic parameter types never get cloned due to containing generic types being instantiated. + // That is, their DeclaringType is always the generic type definition. As a Type, the ReflectedType property is always equal to the DeclaringType. + // + // Because of these conditions, we can safely implement both the method token equivalence and the "is this type from the same implementor" + // check as our regular Equals() method. + return Equals(other); + } + + public sealed override int GenericParameterPosition + { + get + { + return _position; + } + } + + public sealed override string Namespace + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_Namespace(this); +#endif + return DeclaringType.Namespace; + } + } + + public sealed override StructLayoutAttribute StructLayoutAttribute + { + get + { + return null; + } + } + + public sealed override string ToString() + { + return Name; + } + + protected sealed override TypeAttributes GetAttributeFlagsImpl() + { + return TypeAttributes.Public; + } + + internal sealed override bool CanBrowseWithoutMissingMetadataExceptions => true; + + internal sealed override string InternalFullNameOfAssembly + { + get + { + Debug.Fail("Since this class always returns null for FullName, this helper should be unreachable."); + return null; + } + } + + internal sealed override RuntimeTypeHandle InternalTypeHandleIfAvailable + { + get + { + return default(RuntimeTypeHandle); + } + } + + // + // Returns the generic parameter substitutions to use when enumerating declared members, base class and implemented interfaces. + // + internal abstract override TypeContext TypeContext { get; } + + // + // Returns the base type as a typeDef, Ref, or Spec. Default behavior is to QTypeDefRefOrSpec.Null, which causes BaseType to return null. + // + internal sealed override QTypeDefRefOrSpec TypeRefDefOrSpecForBaseType + { + get + { + QTypeDefRefOrSpec[] constraints = Constraints; + TypeInfo[] constraintInfos = ConstraintInfos; + for (int i = 0; i < constraints.Length; i++) + { + TypeInfo constraintInfo = constraintInfos[i]; + if (constraintInfo.IsInterface) + continue; + return constraints[i]; + } + + RuntimeNamedTypeInfo objectTypeInfo = typeof(object).CastToRuntimeNamedTypeInfo(); + return objectTypeInfo.TypeDefinitionQHandle; + } + } + + // + // Returns the *directly implemented* interfaces as typedefs, specs or refs. ImplementedInterfaces will take care of the transitive closure and + // insertion of the TypeContext. + // + internal sealed override QTypeDefRefOrSpec[] TypeRefDefOrSpecsForDirectlyImplementedInterfaces + { + get + { + LowLevelList result = new LowLevelList(); + QTypeDefRefOrSpec[] constraints = Constraints; + TypeInfo[] constraintInfos = ConstraintInfos; + for (int i = 0; i < constraints.Length; i++) + { + if (constraintInfos[i].IsInterface) + result.Add(constraints[i]); + } + return result.ToArray(); + } + } + + protected abstract QTypeDefRefOrSpec[] Constraints { get; } + + private TypeInfo[] ConstraintInfos + { + get + { + QTypeDefRefOrSpec[] constraints = Constraints; + if (constraints.Length == 0) + return Array.Empty(); + TypeInfo[] constraintInfos = new TypeInfo[constraints.Length]; + for (int i = 0; i < constraints.Length; i++) + { + constraintInfos[i] = constraints[i].Resolve(TypeContext); + } + return constraintInfos; + } + } + + private readonly int _position; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.UnificationKey.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.UnificationKey.cs new file mode 100644 index 00000000000000..cc080b108a8643 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.UnificationKey.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Collections.Concurrent; + +namespace System.Reflection.Runtime.TypeInfos +{ + internal abstract partial class RuntimeHasElementTypeInfo : RuntimeTypeInfo + { + // + // Key for unification. + // + internal struct UnificationKey : IEquatable + { + // + // Q: Why is the type handle part of the unification key when it doesn't participate in the Equals/HashCode computations? + // A: It's a passenger. + // + // The typeHandle argument is "redundant" in that it can be computed from the rest of the key. However, we have callers (Type.GetTypeFromHandle()) that + // already have the typeHandle so to avoid an unnecessary round-trip computation, we require the caller to pass it in separately. + // We allow it to ride along in the key object because the ConcurrentUnifier classes we use don't support passing "extra" parameters to + // their Factory methods. + // + public UnificationKey(RuntimeTypeInfo elementType, RuntimeTypeHandle typeHandle) + { + ElementType = elementType; + TypeHandle = typeHandle; + } + + public RuntimeTypeInfo ElementType { get; } + public RuntimeTypeHandle TypeHandle { get; } + + public override bool Equals(object obj) + { + if (!(obj is UnificationKey other)) + return false; + return Equals(other); + } + + public bool Equals(UnificationKey other) + { + if (!ElementType.Equals(other.ElementType)) + return false; + + // The TypeHandle is not actually part of the key but riding along for convenience (see commment at head of class.) + // If the other parts of the key matched, this must too. + Debug.Assert(TypeHandle.Equals(other.TypeHandle)); + return true; + } + + public override int GetHashCode() + { + return ElementType.GetHashCode(); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.cs new file mode 100644 index 00000000000000..299cab3b9d7591 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.cs @@ -0,0 +1,219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Runtime.InteropServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; + +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.TypeInfos +{ + // + // The runtime's implementation of TypeInfo's for the "HasElement" subclass of types. + // + internal abstract partial class RuntimeHasElementTypeInfo : RuntimeTypeInfo, IKeyedItem, IRuntimeMemberInfoWithNoMetadataDefinition + { + protected RuntimeHasElementTypeInfo(UnificationKey key) + : base() + { + _key = key; + } + + public sealed override bool IsTypeDefinition => false; + public sealed override bool IsGenericTypeDefinition => false; + protected sealed override bool HasElementTypeImpl() => true; + protected abstract override bool IsArrayImpl(); + public abstract override bool IsSZArray { get; } + public abstract override bool IsVariableBoundArray { get; } + protected abstract override bool IsByRefImpl(); + protected abstract override bool IsPointerImpl(); + public sealed override bool IsConstructedGenericType => false; + public sealed override bool IsGenericParameter => false; + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => false; + public sealed override bool IsByRefLike => false; + + // + // Implements IKeyedItem.PrepareKey. + // + // This method is the keyed item's chance to do any lazy evaluation needed to produce the key quickly. + // Concurrent unifiers are guaranteed to invoke this method at least once and wait for it + // to complete before invoking the Key property. The unifier lock is NOT held across the call. + // + // PrepareKey() must be idempodent and thread-safe. It may be invoked multiple times and concurrently. + // + public void PrepareKey() + { + } + + // + // Implements IKeyedItem.Key. + // + // Produce the key. This is a high-traffic property and is called while the hash table's lock is held. Thus, it should + // return a precomputed stored value and refrain from invoking other methods. If the keyed item wishes to + // do lazy evaluation of the key, it should do so in the PrepareKey() method. + // + public UnificationKey Key + { + get + { + return _key; + } + } + + public sealed override Assembly Assembly + { + get + { + return _key.ElementType.Assembly; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_CustomAttributes(this); +#endif + + return Empty.Enumerable; + } + } + + public sealed override bool ContainsGenericParameters + { + get + { + return _key.ElementType.ContainsGenericParameters; + } + } + + public sealed override string FullName + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_FullName(this); +#endif + string elementFullName = _key.ElementType.FullName; + if (elementFullName == null) + return null; + return elementFullName + Suffix; + } + } + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // This logic is written to match CoreCLR's behavior. + return other is Type && other is IRuntimeMemberInfoWithNoMetadataDefinition; + } + + public sealed override string Namespace + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_Namespace(this); +#endif + return _key.ElementType.Namespace; + } + } + + public sealed override StructLayoutAttribute StructLayoutAttribute + { + get + { + return null; + } + } + + public sealed override string ToString() + { + return _key.ElementType.ToString() + Suffix; + } + + public sealed override int MetadataToken + { + get + { + return 0x02000000; // nil TypeDef token + } + } + + // + // Left unsealed because this implemention is correct for ByRefs and Pointers but not Arrays. + // + protected override TypeAttributes GetAttributeFlagsImpl() + { + Debug.Assert(IsByRef || IsPointer); + return TypeAttributes.AnsiClass; + } + + protected sealed override int InternalGetHashCode() + { + return _key.ElementType.GetHashCode(); + } + + internal sealed override bool CanBrowseWithoutMissingMetadataExceptions => true; + + internal sealed override Type InternalDeclaringType + { + get + { + return null; + } + } + + public sealed override string InternalGetNameIfAvailable(ref Type rootCauseForFailure) + { + string elementTypeName = _key.ElementType.InternalGetNameIfAvailable(ref rootCauseForFailure); + if (elementTypeName == null) + { + rootCauseForFailure = _key.ElementType; + return null; + } + return elementTypeName + Suffix; + } + + internal sealed override string InternalFullNameOfAssembly + { + get + { + return _key.ElementType.InternalFullNameOfAssembly; + } + } + + internal sealed override RuntimeTypeInfo InternalRuntimeElementType + { + get + { + return _key.ElementType; + } + } + + internal sealed override RuntimeTypeHandle InternalTypeHandleIfAvailable + { + get + { + return _key.TypeHandle; + } + } + + protected abstract string Suffix { get; } + + private readonly UnificationKey _key; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNamedTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNamedTypeInfo.cs new file mode 100644 index 00000000000000..209a91e0cb1028 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNamedTypeInfo.cs @@ -0,0 +1,242 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Runtime.InteropServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.Reflection.Tracing; + +namespace System.Reflection.Runtime.TypeInfos +{ + // + // TypeInfos that represent type definitions (i.e. Foo or Foo<>, but not Foo or arrays/pointers/byrefs.) + // + // + internal abstract partial class RuntimeNamedTypeInfo : RuntimeTypeDefinitionTypeInfo, IEquatable + { + protected RuntimeNamedTypeInfo(RuntimeTypeHandle typeHandle) + { + _typeHandle = typeHandle; + } + + public sealed override bool ContainsGenericParameters + { + get + { + return IsGenericTypeDefinition; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_CustomAttributes(this); +#endif + + foreach (CustomAttributeData cad in TrueCustomAttributes) + yield return cad; + + TypeAttributes attributes = Attributes; + if (0 != (attributes & TypeAttributes.Import)) + yield return new RuntimePseudoCustomAttributeData(typeof(ComImportAttribute), null); + + if (0 != (attributes & TypeAttributes.Serializable)) + yield return new RuntimePseudoCustomAttributeData(typeof(SerializableAttribute), null); + } + } + + public bool Equals(RuntimeNamedTypeInfo other) + { + // RuntimeTypeInfo.Equals(object) is the one that encapsulates our unification strategy so defer to him. + object otherAsObject = other; + return base.Equals(otherAsObject); + } + + /// + /// Override this function to read the Guid attribute from a type's metadata. If the attribute + /// is not present, or isn't parseable, return null. Should be overriden by metadata specific logic + /// + protected abstract Guid? ComputeGuidFromCustomAttributes(); + + public sealed override Guid GUID + { + get + { + Guid? guidFromAttributes = ComputeGuidFromCustomAttributes(); + if (guidFromAttributes.HasValue) + return guidFromAttributes.Value; + + // + // If we got here, there was no [Guid] attribute. + // + // Ideally, we'd now compute the same GUID the desktop returns - however, that algorithm is complex and has questionable dependencies + // (in particular, the GUID changes if the language compilers ever change the way it emits metadata tokens into certain unordered lists. + // We don't even retain that order across the Project N toolchain.) + // + // For now, this is a compromise that satisfies our app-compat goals. We ensure that each unique Type receives a different GUID (at least one app + // uses the GUID as a dictionary key to look up types.) It will not be the same GUID on multiple runs of the app but so far, there's + // no evidence that's needed. + // + return s_namedTypeToGuidTable.GetOrAdd(this).Item1; + } + } + + public sealed override string FullName + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_FullName(this); +#endif + + Debug.Assert(!IsConstructedGenericType); + Debug.Assert(!IsGenericParameter); + Debug.Assert(!HasElementType); + + string name = Name; + + Type declaringType = this.DeclaringType; + if (declaringType != null) + { + string declaringTypeFullName = declaringType.FullName; + return declaringTypeFullName + "+" + name; + } + + string ns = Namespace; + if (ns == null) + return name; + return ns + "." + name; + } + } + +#if DEBUG + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => base.HasSameMetadataDefinitionAs(other); +#endif + + protected abstract void GetPackSizeAndSize(out int packSize, out int size); + + public sealed override StructLayoutAttribute StructLayoutAttribute + { + get + { + const int DefaultPackingSize = 8; + + // Note: CoreClr checks HasElementType and IsGenericParameter in addition to IsInterface but those properties cannot be true here as this + // RuntimeTypeInfo subclass is solely for TypeDef types.) + if (IsInterface) + return null; + + TypeAttributes attributes = Attributes; + + LayoutKind layoutKind; + switch (attributes & TypeAttributes.LayoutMask) + { + case TypeAttributes.ExplicitLayout: layoutKind = LayoutKind.Explicit; break; + case TypeAttributes.AutoLayout: layoutKind = LayoutKind.Auto; break; + case TypeAttributes.SequentialLayout: layoutKind = LayoutKind.Sequential; break; + default: layoutKind = LayoutKind.Auto; break; + } + + CharSet charSet; + switch (attributes & TypeAttributes.StringFormatMask) + { + case TypeAttributes.AnsiClass: charSet = CharSet.Ansi; break; + case TypeAttributes.AutoClass: charSet = CharSet.Auto; break; + case TypeAttributes.UnicodeClass: charSet = CharSet.Unicode; break; + default: charSet = CharSet.None; break; + } + + int pack; + int size; + GetPackSizeAndSize(out pack, out size); + + // Metadata parameter checking should not have allowed 0 for packing size. + // The runtime later converts a packing size of 0 to 8 so do the same here + // because it's more useful from a user perspective. + if (pack == 0) + pack = DefaultPackingSize; + + return new StructLayoutAttribute(layoutKind) + { + CharSet = charSet, + Pack = pack, + Size = size, + }; + } + } + + protected abstract IEnumerable TrueCustomAttributes { get; } + + // + // Returns the anchoring typedef that declares the members that this type wants returned by the Declared*** properties. + // The Declared*** properties will project the anchoring typedef's members by overriding their DeclaringType property with "this" + // and substituting the value of this.TypeContext into any generic parameters. + // + // Default implementation returns null which causes the Declared*** properties to return no members. + // + // Note that this does not apply to DeclaredNestedTypes. Nested types and their containers have completely separate generic instantiation environments + // (despite what C# might lead you to think.) Constructed generic types return the exact same same nested types that its generic type definition does + // - i.e. their DeclaringTypes refer back to the generic type definition, not the constructed generic type.) + // + // Note also that we cannot use this anchoring concept for base types because of generic parameters. Generic parameters return + // baseclass and interfaces based on its constraints. + // + internal sealed override RuntimeNamedTypeInfo AnchoringTypeDefinitionForDeclaredMembers + { + get + { + return this; + } + } + + internal sealed override bool CanBrowseWithoutMissingMetadataExceptions => true; + + internal sealed override RuntimeTypeHandle InternalTypeHandleIfAvailable + { + get + { + return _typeHandle; + } + } + + // + // Returns the generic parameter substitutions to use when enumerating declared members, base class and implemented interfaces. + // + internal sealed override TypeContext TypeContext + { + get + { + return new TypeContext(this.RuntimeGenericTypeParameters, null); + } + } + +#if ENABLE_REFLECTION_TRACE + internal abstract string TraceableTypeName { get; } +#endif + + /// + /// QTypeDefRefOrSpec handle that can be used to re-acquire this type. Must be implemented + /// for all metadata sourced type implementations. + /// + internal abstract QTypeDefRefOrSpec TypeDefinitionQHandle { get; } + + private readonly RuntimeTypeHandle _typeHandle; + + private static readonly NamedTypeToGuidTable s_namedTypeToGuidTable = new NamedTypeToGuidTable(); + private sealed class NamedTypeToGuidTable : ConcurrentUnifier> + { + protected sealed override Tuple Factory(RuntimeNamedTypeInfo key) + { + return new Tuple(Guid.NewGuid()); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNoMetadataNamedTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNoMetadataNamedTypeInfo.cs new file mode 100644 index 00000000000000..cc95bb1a0c655a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNoMetadataNamedTypeInfo.cs @@ -0,0 +1,231 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.Assemblies; +using System.Reflection.Runtime.CustomAttributes; + +using Internal.LowLevelLinq; +using Internal.Reflection.Tracing; +using Internal.Reflection.Core.Execution; + +using StructLayoutAttribute = System.Runtime.InteropServices.StructLayoutAttribute; + +namespace System.Reflection.Runtime.TypeInfos +{ + // + // TypeInfos that represent type definitions (i.e. Foo or Foo<>, but not Foo or arrays/pointers/byrefs.) + // that not opted into pay-for-play metadata. + // + internal sealed partial class RuntimeNoMetadataNamedTypeInfo : RuntimeTypeDefinitionTypeInfo + { + private RuntimeNoMetadataNamedTypeInfo(RuntimeTypeHandle typeHandle, bool isGenericTypeDefinition) + { + _typeHandle = typeHandle; + _isGenericTypeDefinition = isGenericTypeDefinition; + } + + public sealed override Assembly Assembly + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + public sealed override bool ContainsGenericParameters + { + get + { + return _isGenericTypeDefinition; + } + } + + public sealed override IEnumerable CustomAttributes + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + public sealed override string FullName + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_FullName(this); +#endif + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + public sealed override Guid GUID + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + public sealed override bool IsGenericTypeDefinition + { + get + { + return _isGenericTypeDefinition; + } + } + +#if DEBUG + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => base.HasSameMetadataDefinitionAs(other); +#endif + + public sealed override string Namespace + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_Namespace(this); +#endif + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + public sealed override StructLayoutAttribute StructLayoutAttribute + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + public sealed override string ToString() + { + return _typeHandle.LastResortString(); + } + + public sealed override int MetadataToken + { + get + { + throw new InvalidOperationException(SR.NoMetadataTokenAvailable); + } + } + + protected sealed override TypeAttributes GetAttributeFlagsImpl() + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + + protected sealed override int InternalGetHashCode() + { + return _typeHandle.GetHashCode(); + } + + // + // Returns the anchoring typedef that declares the members that this type wants returned by the Declared*** properties. + // The Declared*** properties will project the anchoring typedef's members by overriding their DeclaringType property with "this" + // and substituting the value of this.TypeContext into any generic parameters. + // + // Default implementation returns null which causes the Declared*** properties to return no members. + // + // Note that this does not apply to DeclaredNestedTypes. Nested types and their containers have completely separate generic instantiation environments + // (despite what C# might lead you to think.) Constructed generic types return the exact same same nested types that its generic type definition does + // - i.e. their DeclaringTypes refer back to the generic type definition, not the constructed generic type.) + // + // Note also that we cannot use this anchoring concept for base types because of generic parameters. Generic parameters return + // baseclass and interfaces based on its constraints. + // + internal sealed override RuntimeNamedTypeInfo AnchoringTypeDefinitionForDeclaredMembers + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + internal sealed override bool CanBrowseWithoutMissingMetadataExceptions => false; + + internal sealed override Type InternalDeclaringType + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + public sealed override string InternalGetNameIfAvailable(ref Type rootCauseForFailure) + { + rootCauseForFailure = this; + return null; + } + + internal sealed override string InternalFullNameOfAssembly + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + internal sealed override RuntimeTypeHandle InternalTypeHandleIfAvailable + { + get + { + return _typeHandle; + } + } + + internal sealed override RuntimeTypeInfo[] RuntimeGenericTypeParameters + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + // + // Returns the base type as a typeDef, Ref, or Spec. Default behavior is to QTypeDefRefOrSpec.Null, which causes BaseType to return null. + // + internal sealed override QTypeDefRefOrSpec TypeRefDefOrSpecForBaseType + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + // + // Returns the *directly implemented* interfaces as typedefs, specs or refs. ImplementedInterfaces will take care of the transitive closure and + // insertion of the TypeContext. + // + internal sealed override QTypeDefRefOrSpec[] TypeRefDefOrSpecsForDirectlyImplementedInterfaces + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + // + // Returns the generic parameter substitutions to use when enumerating declared members, base class and implemented interfaces. + // + internal sealed override TypeContext TypeContext + { + get + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + private readonly RuntimeTypeHandle _typeHandle; + private readonly bool _isGenericTypeDefinition; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimePointerTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimePointerTypeInfo.cs new file mode 100644 index 00000000000000..c3675c848b75dc --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimePointerTypeInfo.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; + +namespace System.Reflection.Runtime.TypeInfos +{ + // + // The runtime's implementation of TypeInfo's for pointer types. + // + internal sealed partial class RuntimePointerTypeInfo : RuntimeHasElementTypeInfo + { + private RuntimePointerTypeInfo(UnificationKey key) + : base(key) + { + } + + protected sealed override bool IsArrayImpl() => false; + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + protected sealed override bool IsByRefImpl() => false; + protected sealed override bool IsPointerImpl() => true; + + protected sealed override string Suffix + { + get + { + return "*"; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeDefinitionTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeDefinitionTypeInfo.cs new file mode 100644 index 00000000000000..98a6718ba32e0e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeDefinitionTypeInfo.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; + +using Internal.Runtime.Augments; + +namespace System.Reflection.Runtime.TypeInfos +{ + // + // TypeInfos that represent non-constructed types (IsTypeDefinition == true) + // + internal abstract class RuntimeTypeDefinitionTypeInfo : RuntimeTypeInfo + { + public sealed override bool IsTypeDefinition => true; + public abstract override bool IsGenericTypeDefinition { get; } + protected sealed override bool HasElementTypeImpl() => false; + protected sealed override bool IsArrayImpl() => false; + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + protected sealed override bool IsByRefImpl() => false; + protected sealed override bool IsPointerImpl() => false; + public sealed override bool IsConstructedGenericType => false; + public sealed override bool IsGenericParameter => false; + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => false; + + public sealed override bool IsByRefLike + { + get + { + RuntimeTypeHandle typeHandle = InternalTypeHandleIfAvailable; + if (!typeHandle.IsNull()) + return RuntimeAugments.IsByRefLike(typeHandle); + + foreach (CustomAttributeData cad in CustomAttributes) + { + if (cad.AttributeType == typeof(IsByRefLikeAttribute)) + return true; + } + return false; + } + } + + // Left unsealed as RuntimeCLSIDTypeInfo has special behavior and needs to override. + public override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // Do not rewrite as a call to IsConstructedGenericType - we haven't yet established that "other" is a runtime-implemented member yet! + if (other is RuntimeConstructedGenericTypeInfo otherConstructedGenericType) + other = otherConstructedGenericType.GetGenericTypeDefinition(); + + // Unlike most other MemberInfo objects, types never get cloned due to containing generic types being instantiated. + // That is, their DeclaringType is always the generic type definition. As a Type, the ReflectedType property is always equal to the DeclaringType. + // + // Because of these conditions, we can safely implement both the method token equivalence and the "is this type from the same implementor" + // check as our regular Equals() method. + return Equals(other); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs new file mode 100644 index 00000000000000..45c95cfecb5c7f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs @@ -0,0 +1,221 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.BindingFlagSupport; + +namespace System.Reflection.Runtime.TypeInfos +{ + internal abstract partial class RuntimeTypeInfo + { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + public sealed override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => Query(bindingAttr).ToArray(); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + protected sealed override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + Debug.Assert(types != null); + + QueryResult queryResult = Query(bindingAttr); + ListBuilder candidates = new ListBuilder(); + foreach (ConstructorInfo candidate in queryResult) + { + if (candidate.QualifiesBasedOnParameterCount(bindingAttr, callConvention, types)) + candidates.Add(candidate); + } + + // For perf and desktop compat, fast-path these specific checks before calling on the binder to break ties. + if (candidates.Count == 0) + return null; + + if (types.Length == 0 && candidates.Count == 1) + { + ConstructorInfo firstCandidate = candidates[0]; + ParameterInfo[] parameters = firstCandidate.GetParametersNoCopy(); + if (parameters.Length == 0) + return firstCandidate; + } + + if ((bindingAttr & BindingFlags.ExactBinding) != 0) + return System.DefaultBinder.ExactBinding(candidates.ToArray(), types, modifiers) as ConstructorInfo; + + if (binder == null) + binder = DefaultBinder; + + return binder.SelectMethod(bindingAttr, candidates.ToArray(), types, modifiers) as ConstructorInfo; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public sealed override EventInfo[] GetEvents(BindingFlags bindingAttr) => Query(bindingAttr).ToArray(); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public sealed override EventInfo GetEvent(string name, BindingFlags bindingAttr) => Query(name, bindingAttr).Disambiguate(); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public sealed override FieldInfo[] GetFields(BindingFlags bindingAttr) => Query(bindingAttr).ToArray(); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public sealed override FieldInfo GetField(string name, BindingFlags bindingAttr) => Query(name, bindingAttr).Disambiguate(); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public sealed override MethodInfo[] GetMethods(BindingFlags bindingAttr) => Query(bindingAttr).ToArray(); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + protected sealed override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + return GetMethodImplCommon(name, GenericParameterCountAny, bindingAttr, binder, callConvention, types, modifiers); + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + protected sealed override MethodInfo GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + return GetMethodImplCommon(name, genericParameterCount, bindingAttr, binder, callConvention, types, modifiers); + } + + private MethodInfo GetMethodImplCommon(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + Debug.Assert(name != null); + + // GetMethodImpl() is a funnel for two groups of api. We can distinguish by comparing "types" to null. + if (types == null) + { + // Group #1: This group of api accept only a name and BindingFlags. The other parameters are hard-wired by the non-virtual api entrypoints. + Debug.Assert(genericParameterCount == GenericParameterCountAny); + Debug.Assert(binder == null); + Debug.Assert(callConvention == CallingConventions.Any); + Debug.Assert(modifiers == null); + return Query(name, bindingAttr).Disambiguate(); + } + else + { + // Group #2: This group of api takes a set of parameter types and an optional binder. + QueryResult queryResult = Query(name, bindingAttr); + ListBuilder candidates = new ListBuilder(); + foreach (MethodInfo candidate in queryResult) + { + if (genericParameterCount != GenericParameterCountAny && genericParameterCount != candidate.GenericParameterCount) + continue; + if (candidate.QualifiesBasedOnParameterCount(bindingAttr, callConvention, types)) + candidates.Add(candidate); + } + + if (candidates.Count == 0) + return null; + + // For perf and desktop compat, fast-path these specific checks before calling on the binder to break ties. + if (types.Length == 0 && candidates.Count == 1) + return candidates[0]; + + if (binder == null) + binder = DefaultBinder; + + return binder.SelectMethod(bindingAttr, candidates.ToArray(), types, modifiers) as MethodInfo; + } + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public sealed override Type[] GetNestedTypes(BindingFlags bindingAttr) => Query(bindingAttr).ToArray(); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public sealed override Type GetNestedType(string name, BindingFlags bindingAttr) => Query(name, bindingAttr).Disambiguate(); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + public sealed override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => Query(bindingAttr).ToArray(); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + protected sealed override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) + { + Debug.Assert(name != null); + + // GetPropertyImpl() is a funnel for two groups of api. We can distinguish by comparing "types" to null. + if (types == null && returnType == null) + { + // Group #1: This group of api accept only a name and BindingFlags. The other parameters are hard-wired by the non-virtual api entrypoints. + Debug.Assert(binder == null); + Debug.Assert(modifiers == null); + return Query(name, bindingAttr).Disambiguate(); + } + else + { + // Group #2: This group of api takes a set of parameter types, a return type (both cannot be null) and an optional binder. + QueryResult queryResult = Query(name, bindingAttr); + ListBuilder candidates = new ListBuilder(); + foreach (PropertyInfo candidate in queryResult) + { + if (types == null || (candidate.GetIndexParameters().Length == types.Length)) + { + candidates.Add(candidate); + } + } + + if (candidates.Count == 0) + return null; + + // For perf and desktop compat, fast-path these specific checks before calling on the binder to break ties. + if (types == null || types.Length == 0) + { + // no arguments + if (candidates.Count == 1) + { + PropertyInfo firstCandidate = candidates[0]; + if (returnType is not null && !returnType.IsEquivalentTo(firstCandidate.PropertyType)) + return null; + return firstCandidate; + } + else + { + if (returnType is null) + // if we are here we have no args or property type to select over and we have more than one property with that name + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + } + } + + if ((bindingAttr & BindingFlags.ExactBinding) != 0) + return System.DefaultBinder.ExactPropertyBinding(candidates.ToArray(), returnType, types, modifiers); + + if (binder == null) + binder = DefaultBinder; + + return binder.SelectProperty(bindingAttr, candidates.ToArray(), returnType, types, modifiers); + } + } + + private QueryResult Query(BindingFlags bindingAttr) where M : MemberInfo + { + return Query(null, bindingAttr, null); + } + + private QueryResult Query(string name, BindingFlags bindingAttr) where M : MemberInfo + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + return Query(name, bindingAttr, null); + } + + private QueryResult Query(string optionalName, BindingFlags bindingAttr, Func optionalPredicate) where M : MemberInfo + { + MemberPolicies policies = MemberPolicies.Default; + bindingAttr = policies.ModifyBindingFlags(bindingAttr); + bool ignoreCase = (bindingAttr & BindingFlags.IgnoreCase) != 0; + + TypeComponentsCache cache = Cache; + QueriedMemberList queriedMembers; + if (optionalName == null) + queriedMembers = cache.GetQueriedMembers(); + else + queriedMembers = cache.GetQueriedMembers(optionalName, ignoreCase: ignoreCase); + + if (optionalPredicate != null) + queriedMembers = queriedMembers.Filter(optionalPredicate); + return new QueryResult(bindingAttr, queriedMembers); + } + + private TypeComponentsCache Cache => _lazyCache ?? (_lazyCache = new TypeComponentsCache(this)); + + private volatile TypeComponentsCache _lazyCache; + + private const int GenericParameterCountAny = -1; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.CoreGetDeclared.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.CoreGetDeclared.cs new file mode 100644 index 00000000000000..0fd1fccfc35741 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.CoreGetDeclared.cs @@ -0,0 +1,158 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.MethodInfos; +using System.Reflection.Runtime.FieldInfos; +using System.Reflection.Runtime.PropertyInfos; +using System.Reflection.Runtime.EventInfos; +using NameFilter = System.Reflection.Runtime.BindingFlagSupport.NameFilter; + +using Internal.Reflection.Core.Execution; + +// +// The CoreGet() methods on RuntimeTypeInfo provide the raw source material for the Type.Get*() family of apis. +// +// These retrieve directly introduced (not inherited) members whose names match the passed in NameFilter (if NameFilter is null, +// return all members.) To avoid allocating objects, prefer to pass the metadata constant string value handle to NameFilter rather +// than strings. +// +// The ReflectedType is the type that the Type.Get*() api was invoked on. Use it to establish the returned MemberInfo object's +// ReflectedType. +// +namespace System.Reflection.Runtime.TypeInfos +{ + internal abstract partial class RuntimeTypeInfo + { + internal IEnumerable CoreGetDeclaredConstructors(NameFilter optionalNameFilter) + { + // + // - It may sound odd to get a non-null name filter for a constructor search, but Type.GetMember() is an api that does this. + // + // - All GetConstructor() apis act as if BindingFlags.DeclaredOnly were specified. So the ReflectedType will always be the declaring type and so is not passed to this method. + // + RuntimeNamedTypeInfo definingType = AnchoringTypeDefinitionForDeclaredMembers; + + if (definingType != null) + { + // If there is a definingType, we do not support Synthetic constructors + Debug.Assert(object.ReferenceEquals(SyntheticConstructors, Empty.Enumerable)); + + return definingType.CoreGetDeclaredConstructors(optionalNameFilter, this); + } + + return CoreGetDeclaredSyntheticConstructors(optionalNameFilter); + } + + private IEnumerable CoreGetDeclaredSyntheticConstructors(NameFilter optionalNameFilter) + { + foreach (RuntimeConstructorInfo syntheticConstructor in SyntheticConstructors) + { + if (optionalNameFilter == null || optionalNameFilter.Matches(syntheticConstructor.IsStatic ? ConstructorInfo.TypeConstructorName : ConstructorInfo.ConstructorName)) + yield return syntheticConstructor; + } + } + + internal IEnumerable CoreGetDeclaredMethods(NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType) + { + RuntimeNamedTypeInfo definingType = AnchoringTypeDefinitionForDeclaredMembers; + if (definingType != null) + { + // If there is a definingType, we do not support Synthetic constructors + Debug.Assert(object.ReferenceEquals(SyntheticMethods, Empty.Enumerable)); + + return definingType.CoreGetDeclaredMethods(optionalNameFilter, reflectedType, this); + } + + return CoreGetDeclaredSyntheticMethods(optionalNameFilter); + } + + private IEnumerable CoreGetDeclaredSyntheticMethods(NameFilter optionalNameFilter) + { + foreach (RuntimeMethodInfo syntheticMethod in SyntheticMethods) + { + if (optionalNameFilter == null || optionalNameFilter.Matches(syntheticMethod.Name)) + yield return syntheticMethod; + } + } + + internal IEnumerable CoreGetDeclaredEvents(NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType) + { + RuntimeNamedTypeInfo definingType = AnchoringTypeDefinitionForDeclaredMembers; + if (definingType != null) + { + return definingType.CoreGetDeclaredEvents(optionalNameFilter, reflectedType, this); + } + return Empty.Enumerable; + } + + internal IEnumerable CoreGetDeclaredFields(NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType) + { + RuntimeNamedTypeInfo definingType = AnchoringTypeDefinitionForDeclaredMembers; + if (definingType != null) + { + return definingType.CoreGetDeclaredFields(optionalNameFilter, reflectedType, this); + } + return Empty.Enumerable; + } + + internal IEnumerable CoreGetDeclaredProperties(NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType) + { + RuntimeNamedTypeInfo definingType = AnchoringTypeDefinitionForDeclaredMembers; + if (definingType != null) + { + return definingType.CoreGetDeclaredProperties(optionalNameFilter, reflectedType, this); + } + + return Empty.Enumerable; + } + + // + // - All GetNestedType() apis act as if BindingFlags.DeclaredOnly were specified. So the ReflectedType will always be the declaring type and so is not passed to this method. + // + // This method is left unsealed as RuntimeNamedTypeInfo and others need to override with specific implementations. + // + internal virtual IEnumerable CoreGetDeclaredNestedTypes(NameFilter optionalNameFilter) + { + return Array.Empty(); + } + } + + internal abstract partial class RuntimeNamedTypeInfo + { + // Metadata providing implementations of RuntimeNamedTypeInfo implement the following methods + // to provide filtered access to the various reflection objects by reading metadata directly. + // The loop of examining methods is done in a metadata specific manner for greater efficiency. + internal abstract IEnumerable CoreGetDeclaredConstructors(NameFilter optionalNameFilter, RuntimeTypeInfo contextTypeInfo); + internal abstract IEnumerable CoreGetDeclaredMethods(NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType, RuntimeTypeInfo contextTypeInfo); + internal abstract IEnumerable CoreGetDeclaredEvents(NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType, RuntimeTypeInfo contextTypeInfo); + internal abstract IEnumerable CoreGetDeclaredFields(NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType, RuntimeTypeInfo contextTypeInfo); + internal abstract IEnumerable CoreGetDeclaredProperties(NameFilter optionalNameFilter, RuntimeTypeInfo reflectedType, RuntimeTypeInfo contextTypeInfo); + } + + internal sealed partial class RuntimeConstructedGenericTypeInfo + { + internal sealed override IEnumerable CoreGetDeclaredNestedTypes(NameFilter optionalNameFilter) + { + return GenericTypeDefinitionTypeInfo.CoreGetDeclaredNestedTypes(optionalNameFilter); + } + } + + internal sealed partial class RuntimeBlockedTypeInfo + { + internal sealed override IEnumerable CoreGetDeclaredNestedTypes(NameFilter optionalNameFilter) + { + return Array.Empty(); + } + } + + internal sealed partial class RuntimeNoMetadataNamedTypeInfo + { + internal sealed override IEnumerable CoreGetDeclaredNestedTypes(NameFilter optionalNameFilter) + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.GetMember.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.GetMember.cs new file mode 100644 index 00000000000000..9c5741413ba55d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.GetMember.cs @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Reflection.Runtime.BindingFlagSupport; + +namespace System.Reflection.Runtime.TypeInfos +{ + internal abstract partial class RuntimeTypeInfo + { + [DynamicallyAccessedMembers(GetAllMembers)] + public sealed override MemberInfo[] GetMembers(BindingFlags bindingAttr) => GetMemberImpl(null, MemberTypes.All, bindingAttr); + + [DynamicallyAccessedMembers(GetAllMembers)] + public sealed override MemberInfo[] GetMember(string name, BindingFlags bindingAttr) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + return GetMemberImpl(name, MemberTypes.All, bindingAttr); + } + + [DynamicallyAccessedMembers(GetAllMembers)] + public sealed override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + return GetMemberImpl(name, type, bindingAttr); + } + + private MemberInfo[] GetMemberImpl(string optionalNameOrPrefix, MemberTypes type, BindingFlags bindingAttr) + { + bool prefixSearch = optionalNameOrPrefix != null && optionalNameOrPrefix.EndsWith("*", StringComparison.Ordinal); + string optionalName = prefixSearch ? null : optionalNameOrPrefix; + + Func predicate = null; + if (prefixSearch) + { + bool ignoreCase = (bindingAttr & BindingFlags.IgnoreCase) != 0; + StringComparison comparisonType = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + string prefix = optionalNameOrPrefix.Substring(0, optionalNameOrPrefix.Length - 1); + + predicate = (member => member.Name.StartsWith(prefix, comparisonType)); + } + + QueryResult methods; + QueryResult constructors; + QueryResult properties; + QueryResult events; + QueryResult fields; + QueryResult nestedTypes; + + MemberInfo[] results; + + if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Method, out methods)) != null) + return results; + if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Constructor, out constructors)) != null) + return results; + if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Property, out properties)) != null) + return results; + if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Event, out events)) != null) + return results; + if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Field, out fields)) != null) + return results; + if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.NestedType, out nestedTypes)) != null) + return results; + if ((type & (MemberTypes.NestedType | MemberTypes.TypeInfo)) == MemberTypes.TypeInfo) + { + if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.TypeInfo, out nestedTypes)) != null) + return results; + } + + int numMatches = methods.Count + constructors.Count + properties.Count + events.Count + fields.Count + nestedTypes.Count; + results = (type == (MemberTypes.Method | MemberTypes.Constructor)) ? new MethodBase[numMatches] : new MemberInfo[numMatches]; + int numCopied = 0; + + methods.CopyTo(results, numCopied); + numCopied += methods.Count; + + constructors.CopyTo(results, numCopied); + numCopied += constructors.Count; + + properties.CopyTo(results, numCopied); + numCopied += properties.Count; + + events.CopyTo(results, numCopied); + numCopied += events.Count; + + fields.CopyTo(results, numCopied); + numCopied += fields.Count; + + nestedTypes.CopyTo(results, numCopied); + numCopied += nestedTypes.Count; + + Debug.Assert(numCopied == numMatches); + + return results; + } + + private M[] QuerySpecificMemberTypeIfRequested(MemberTypes memberType, string optionalName, BindingFlags bindingAttr, Func optionalPredicate, MemberTypes targetMemberType, out QueryResult queryResult) where M : MemberInfo + { + if ((memberType & targetMemberType) == 0) + { + // This type of member was not requested. + queryResult = default(QueryResult); + return null; + } + + queryResult = Query(optionalName, bindingAttr, optionalPredicate); + + // Desktop compat: If exactly one type of member was requested, the returned array has to be of that specific type (M[], not MemberInfo[]). Create it now and return it + // to cause GetMember() to short-cut the search. + if ((memberType & ~targetMemberType) == 0) + return queryResult.ToArray(); + + // Desktop compat: If we got here, than one MemberType was requested. Return null to signal GetMember() to keep querying the other member types and concatenate the results. + return null; + } + + public sealed override MemberInfo GetMemberWithSameMetadataDefinitionAs(MemberInfo member) + { + if (member is null) + throw new ArgumentNullException(nameof(member)); + + MemberInfo result = member.MemberType switch + { + MemberTypes.Method => QueryMemberWithSameMetadataDefinitionAs(member), + MemberTypes.Constructor => QueryMemberWithSameMetadataDefinitionAs(member), + MemberTypes.Property => QueryMemberWithSameMetadataDefinitionAs(member), + MemberTypes.Field => QueryMemberWithSameMetadataDefinitionAs(member), + MemberTypes.Event => QueryMemberWithSameMetadataDefinitionAs(member), + MemberTypes.NestedType => QueryMemberWithSameMetadataDefinitionAs(member), + _ => null, + }; + + if (result is null) + throw new ArgumentException(SR.Format(SR.Arg_MemberInfoNotFound, member.Name), nameof(member)); + + return result; + } + + private M QueryMemberWithSameMetadataDefinitionAs(MemberInfo member) where M : MemberInfo + { + QueryResult members = Query(member.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + foreach (M candidate in members) + { + if (candidate.HasSameMetadataDefinitionAs(member)) + return candidate; + } + return null; + } + + // DynamicallyAccessedMemberTypes.All keeps more data than what a member can use: + // - Keeps info about interfaces + // - Complete Nested types (nested type body and all its members including other nested types) + // - Public and private base type information + // Instead, the GetAllMembers constant will keep: + // - The nested types body but not the members + // - Base type public information but not private information. This information should not + // be visible via the derived type and is ignored by reflection + internal const DynamicallyAccessedMemberTypes GetAllMembers = DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | + DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.InvokeMember.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.InvokeMember.cs new file mode 100644 index 00000000000000..40ca73df2b1f4b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.InvokeMember.cs @@ -0,0 +1,416 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Collections.Generic; +using System.Reflection.Runtime.BindingFlagSupport; + +namespace System.Reflection.Runtime.TypeInfos +{ + internal abstract partial class RuntimeTypeInfo + { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public sealed override object InvokeMember( + string name, BindingFlags bindingFlags, Binder binder, object target, + object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParams) + { + const BindingFlags MemberBindingMask = (BindingFlags)0x000000FF; + const BindingFlags InvocationMask = (BindingFlags)0x0000FF00; + const BindingFlags BinderGetSetProperty = BindingFlags.GetProperty | BindingFlags.SetProperty; + const BindingFlags BinderGetSetField = BindingFlags.GetField | BindingFlags.SetField; + const BindingFlags BinderNonFieldGetSet = (BindingFlags)0x00FFF300; + const BindingFlags BinderNonCreateInstance = BindingFlags.InvokeMethod | BinderGetSetField | BinderGetSetProperty; + + if (IsGenericParameter) + throw new InvalidOperationException(SR.Arg_GenericParameter); + + #region Preconditions + if ((bindingFlags & InvocationMask) == 0) + // "Must specify binding flags describing the invoke operation required." + throw new ArgumentException(SR.Arg_NoAccessSpec, nameof(bindingFlags)); + + // Provide a default binding mask if none is provided + if ((bindingFlags & MemberBindingMask) == 0) + { + bindingFlags |= BindingFlags.Instance | BindingFlags.Public; + + if ((bindingFlags & BindingFlags.CreateInstance) == 0) + bindingFlags |= BindingFlags.Static; + } + + // There must not be more named parameters than provided arguments + if (namedParams != null) + { + if (providedArgs != null) + { + if (namedParams.Length > providedArgs.Length) + // "Named parameter array can not be bigger than argument array." + throw new ArgumentException(SR.Arg_NamedParamTooBig, nameof(namedParams)); + } + else + { + if (namedParams.Length != 0) + // "Named parameter array can not be bigger than argument array." + throw new ArgumentException(SR.Arg_NamedParamTooBig, nameof(namedParams)); + } + } + #endregion + + #region COM Interop + if (target != null && target.GetType().IsCOMObject) + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupportedInvokeMemberCom); + #endregion + + #region Check that any named paramters are not null + if (namedParams != null && Array.IndexOf(namedParams, null) != -1) + // "Named parameter value must not be null." + throw new ArgumentException(SR.Arg_NamedParamNull, nameof(namedParams)); + #endregion + + int argCnt = (providedArgs != null) ? providedArgs.Length : 0; + + #region Get a Binder + if (binder == null) + binder = DefaultBinder; + + bool bDefaultBinder = (binder == DefaultBinder); + #endregion + + #region Delegate to Activator.CreateInstance + if ((bindingFlags & BindingFlags.CreateInstance) != 0) + { + if ((bindingFlags & BindingFlags.CreateInstance) != 0 && (bindingFlags & BinderNonCreateInstance) != 0) + // "Can not specify both CreateInstance and another access type." + throw new ArgumentException(SR.Arg_CreatInstAccess, nameof(bindingFlags)); + + return Activator.CreateInstance(this, bindingFlags, binder, providedArgs, culture); + } + #endregion + + // PutDispProperty and\or PutRefDispProperty ==> SetProperty. + if ((bindingFlags & (BindingFlags.PutDispProperty | BindingFlags.PutRefDispProperty)) != 0) + bindingFlags |= BindingFlags.SetProperty; + + #region Name + if (name == null) + throw new ArgumentNullException(nameof(name)); + + if (name.Length == 0 || name.Equals(@"[DISPID=0]")) + { + name = GetDefaultMemberName(); + + if (name == null) + { + // in InvokeMember we always pretend there is a default member if none is provided and we make it ToString + name = "ToString"; + } + } + #endregion + + #region GetField or SetField + bool IsGetField = (bindingFlags & BindingFlags.GetField) != 0; + bool IsSetField = (bindingFlags & BindingFlags.SetField) != 0; + + if (IsGetField || IsSetField) + { + #region Preconditions + if (IsGetField) + { + if (IsSetField) + // "Can not specify both Get and Set on a field." + throw new ArgumentException(SR.Arg_FldSetGet, nameof(bindingFlags)); + + if ((bindingFlags & BindingFlags.SetProperty) != 0) + // "Can not specify both GetField and SetProperty." + throw new ArgumentException(SR.Arg_FldGetPropSet, nameof(bindingFlags)); + } + else + { + Debug.Assert(IsSetField); + + if (providedArgs == null) + throw new ArgumentNullException(nameof(providedArgs)); + + if ((bindingFlags & BindingFlags.GetProperty) != 0) + // "Can not specify both SetField and GetProperty." + throw new ArgumentException(SR.Arg_FldSetPropGet, nameof(bindingFlags)); + + if ((bindingFlags & BindingFlags.InvokeMethod) != 0) + // "Can not specify Set on a Field and Invoke on a method." + throw new ArgumentException(SR.Arg_FldSetInvoke, nameof(bindingFlags)); + } + #endregion + + #region Lookup Field + FieldInfo selFld = null; + FieldInfo[] flds = GetMember(name, MemberTypes.Field, bindingFlags) as FieldInfo[]; + + Debug.Assert(flds != null); + + if (flds.Length == 1) + { + selFld = flds[0]; + } + else if (flds.Length > 0) + { + selFld = binder.BindToField(bindingFlags, flds, IsGetField ? Empty.Value : providedArgs[0], culture); + } + #endregion + + if (selFld != null) + { + #region Invocation on a field + if (selFld.FieldType.IsArray || object.ReferenceEquals(selFld.FieldType, typeof(Array))) + { + #region Invocation of an array Field + int idxCnt; + + if ((bindingFlags & BindingFlags.GetField) != 0) + { + idxCnt = argCnt; + } + else + { + idxCnt = argCnt - 1; + } + + if (idxCnt > 0) + { + // Verify that all of the index values are ints + int[] idx = new int[idxCnt]; + for (int i = 0; i < idxCnt; i++) + { + try + { + idx[i] = ((IConvertible)providedArgs[i]).ToInt32(null); + } + catch (InvalidCastException) + { + throw new ArgumentException(SR.Arg_IndexMustBeInt); + } + } + + // Set or get the value... + Array a = (Array)selFld.GetValue(target); + + // Set or get the value in the array + if ((bindingFlags & BindingFlags.GetField) != 0) + { + return a.GetValue(idx); + } + else + { + a.SetValue(providedArgs[idxCnt], idx); + return null; + } + } + #endregion + } + + if (IsGetField) + { + #region Get the field value + if (argCnt != 0) + throw new ArgumentException(SR.Arg_FldGetArgErr, nameof(bindingFlags)); + + return selFld.GetValue(target); + #endregion + } + else + { + #region Set the field Value + if (argCnt != 1) + throw new ArgumentException(SR.Arg_FldSetArgErr, nameof(bindingFlags)); + + selFld.SetValue(target, providedArgs[0], bindingFlags, binder, culture); + + return null; + #endregion + } + #endregion + } + + if ((bindingFlags & BinderNonFieldGetSet) == 0) + throw new MissingFieldException(FullName, name); + } + #endregion + + #region Property PreConditions + // @Legacy - This is RTM behavior + bool isGetProperty = (bindingFlags & BindingFlags.GetProperty) != 0; + bool isSetProperty = (bindingFlags & BindingFlags.SetProperty) != 0; + + if (isGetProperty || isSetProperty) + { + #region Preconditions + if (isGetProperty) + { + Debug.Assert(!IsSetField); + + if (isSetProperty) + throw new ArgumentException(SR.Arg_PropSetGet, nameof(bindingFlags)); + } + else + { + Debug.Assert(isSetProperty); + + Debug.Assert(!IsGetField); + + if ((bindingFlags & BindingFlags.InvokeMethod) != 0) + throw new ArgumentException(SR.Arg_PropSetInvoke, nameof(bindingFlags)); + } + #endregion + } + #endregion + + MethodInfo[] finalists = null; + MethodInfo finalist = null; + + #region BindingFlags.InvokeMethod + if ((bindingFlags & BindingFlags.InvokeMethod) != 0) + { + #region Lookup Methods + MethodInfo[] semiFinalists = GetMember(name, MemberTypes.Method, bindingFlags) as MethodInfo[]; + LowLevelListWithIList results = null; + + for (int i = 0; i < semiFinalists.Length; i++) + { + MethodInfo semiFinalist = semiFinalists[i]; + Debug.Assert(semiFinalist != null); + + if (!semiFinalist.QualifiesBasedOnParameterCount(bindingFlags, CallingConventions.Any, new Type[argCnt])) + continue; + + if (finalist == null) + { + finalist = semiFinalist; + } + else + { + if (results == null) + { + results = new LowLevelListWithIList(semiFinalists.Length); + results.Add(finalist); + } + + results.Add(semiFinalist); + } + } + + if (results != null) + { + Debug.Assert(results.Count > 1); + finalists = new MethodInfo[results.Count]; + results.CopyTo(finalists, 0); + } + #endregion + } + #endregion + + Debug.Assert(finalists == null || finalist != null); + + #region BindingFlags.GetProperty or BindingFlags.SetProperty + if (finalist == null && isGetProperty || isSetProperty) + { + #region Lookup Property + PropertyInfo[] semiFinalists = GetMember(name, MemberTypes.Property, bindingFlags) as PropertyInfo[]; + LowLevelListWithIList results = null; + + for (int i = 0; i < semiFinalists.Length; i++) + { + MethodInfo semiFinalist = null; + + if (isSetProperty) + { + semiFinalist = semiFinalists[i].GetSetMethod(true); + } + else + { + semiFinalist = semiFinalists[i].GetGetMethod(true); + } + + if (semiFinalist == null) + continue; + + BindingFlags expectedBindingFlags = semiFinalist.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic; + if ((bindingFlags & expectedBindingFlags) != expectedBindingFlags) + continue; + + if (!semiFinalist.QualifiesBasedOnParameterCount(bindingFlags, CallingConventions.Any, new Type[argCnt])) + continue; + + if (finalist == null) + { + finalist = semiFinalist; + } + else + { + if (results == null) + { + results = new LowLevelListWithIList(semiFinalists.Length); + results.Add(finalist); + } + + results.Add(semiFinalist); + } + } + + if (results != null) + { + Debug.Assert(results.Count > 1); + finalists = new MethodInfo[results.Count]; + results.CopyTo(finalists, 0); + } + #endregion + } + #endregion + + if (finalist != null) + { + #region Invoke + if (finalists == null && + argCnt == 0 && + finalist.GetParametersNoCopy().Length == 0 && + (bindingFlags & BindingFlags.OptionalParamBinding) == 0) + { + //if (useCache && argCnt == props[0].GetParameters().Length) + // AddMethodToCache(name, bindingFlags, argCnt, providedArgs, props[0]); + + return finalist.Invoke(target, bindingFlags, binder, providedArgs, culture); + } + + if (finalists == null) + finalists = new MethodInfo[] { finalist }; + + if (providedArgs == null) + providedArgs = Array.Empty(); + + object state = null; + + + MethodBase invokeMethod = null; + + try { invokeMethod = binder.BindToMethod(bindingFlags, finalists, ref providedArgs, modifiers, culture, namedParams, out state); } + catch (MissingMethodException) { } + + if (invokeMethod == null) + throw new MissingMethodException(FullName, name); + + //if (useCache && argCnt == invokeMethod.GetParameters().Length) + // AddMethodToCache(name, bindingFlags, argCnt, providedArgs, invokeMethod); + + object result = ((MethodInfo)invokeMethod).Invoke(target, bindingFlags, binder, providedArgs, culture); + + if (state != null) + binder.ReorderArgumentArray(ref providedArgs, state); + + return result; + #endregion + } + + throw new MissingMethodException(FullName, name); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.TypeComponentsCache.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.TypeComponentsCache.cs new file mode 100644 index 00000000000000..60b4a461b1751f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.TypeComponentsCache.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using System.Diagnostics; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Reflection; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.BindingFlagSupport; + +using Internal.Reflection.Core.Execution; + +using Unsafe = Internal.Runtime.CompilerServices.Unsafe; + +namespace System.Reflection.Runtime.TypeInfos +{ + internal abstract partial class RuntimeTypeInfo + { + //================================================================================================================ + // TypeComponentsCache objects are allocated on-demand on a per-Type basis to cache hot data for key scenarios. + // To maximize throughput once the cache is created, the object creates all of its internal caches up front + // and holds entries strongly (and relying on the fact that Types themselves are held weakly to avoid immortality.) + // + // Note that it is possible that two threads racing to query the same TypeInfo may allocate and query two different + // cache objects. Thus, this object must not be relied upon to preserve object identity. + //================================================================================================================ + + private sealed class TypeComponentsCache + { + public TypeComponentsCache(RuntimeTypeInfo type) + { + _type = type; + + _perNameQueryCaches_CaseSensitive = CreatePerNameQueryCaches(type, ignoreCase: false); + _perNameQueryCaches_CaseInsensitive = CreatePerNameQueryCaches(type, ignoreCase: true); + + _nameAgnosticQueryCaches = new object[MemberTypeIndex.Count]; + } + + // + // Returns the cached result of a name-specific query on the Type's members, as if you'd passed in + // + // BindingFlags == Public | NonPublic | Instance | Static | FlattenHierarchy + // + public QueriedMemberList GetQueriedMembers(string name, bool ignoreCase) where M : MemberInfo + { + int index = MemberPolicies.MemberTypeIndex; + object obj = ignoreCase ? _perNameQueryCaches_CaseInsensitive[index] : _perNameQueryCaches_CaseSensitive[index]; + Debug.Assert(obj is PerNameQueryCache); + PerNameQueryCache unifier = Unsafe.As>(obj); + QueriedMemberList result = unifier.GetOrAdd(name); + return result; + } + + // + // Returns the cached result of a name-agnostic query on the Type's members, as if you'd passed in + // + // BindingFlags == Public | NonPublic | Instance | Static | FlattenHierarchy + // + public QueriedMemberList GetQueriedMembers() where M : MemberInfo + { + int index = MemberPolicies.MemberTypeIndex; + object result = Volatile.Read(ref _nameAgnosticQueryCaches[index]); + if (result == null) + { + QueriedMemberList newResult = QueriedMemberList.Create(_type, optionalNameFilter: null, ignoreCase: false); + newResult.Compact(); + result = newResult; + Volatile.Write(ref _nameAgnosticQueryCaches[index], result); + } + + Debug.Assert(result is QueriedMemberList); + return Unsafe.As>(result); + } + + public EnumInfo EnumInfo => _lazyEnumInfo ?? (_lazyEnumInfo = ReflectionCoreExecution.ExecutionDomain.ExecutionEnvironment.GetEnumInfo(_type.TypeHandle)); + + private static object[] CreatePerNameQueryCaches(RuntimeTypeInfo type, bool ignoreCase) + { + object[] perNameCaches = new object[MemberTypeIndex.Count]; + perNameCaches[MemberTypeIndex.Constructor] = new PerNameQueryCache(type, ignoreCase: ignoreCase); + perNameCaches[MemberTypeIndex.Event] = new PerNameQueryCache(type, ignoreCase: ignoreCase); + perNameCaches[MemberTypeIndex.Field] = new PerNameQueryCache(type, ignoreCase: ignoreCase); + perNameCaches[MemberTypeIndex.Method] = new PerNameQueryCache(type, ignoreCase: ignoreCase); + perNameCaches[MemberTypeIndex.Property] = new PerNameQueryCache(type, ignoreCase: ignoreCase); + perNameCaches[MemberTypeIndex.NestedType] = new PerNameQueryCache(type, ignoreCase: ignoreCase); + return perNameCaches; + } + + // This array holds six PerNameQueryCache objects, one for each of the possible M types (ConstructorInfo, EventInfo, etc.) + // The caches are configured to do a case-sensitive query. + private readonly object[] _perNameQueryCaches_CaseSensitive; + + // This array holds six PerNameQueryCache objects, one for each of the possible M types (ConstructorInfo, EventInfo, etc.) + // The caches are configured to do a case-insensitive query. + private readonly object[] _perNameQueryCaches_CaseInsensitive; + + // This array holds six lazily created QueriedMemberList objects, one for each of the possible M types (ConstructorInfo, EventInfo, etc.). + // The objects are the results of a name-agnostic query. + private readonly object[] _nameAgnosticQueryCaches; + + private readonly RuntimeTypeInfo _type; + + private volatile EnumInfo _lazyEnumInfo; + + // + // Each PerName cache persists the results of a Type.Get(name, bindingFlags) for a particular MemberInfoType "M". + // + // where "bindingFlags" == Public | NonPublic | Instance | Static | FlattenHierarchy + // + // In addition, if "ignoreCase" was passed to the constructor, BindingFlags.IgnoreCase is also in effect. + // + private sealed class PerNameQueryCache : ConcurrentUnifier> where M : MemberInfo + { + public PerNameQueryCache(RuntimeTypeInfo type, bool ignoreCase) + { + _type = type; + _ignoreCase = ignoreCase; + } + + protected sealed override QueriedMemberList Factory(string key) + { + QueriedMemberList result = QueriedMemberList.Create(_type, key, ignoreCase: _ignoreCase); + result.Compact(); + return result; + } + + private readonly RuntimeTypeInfo _type; + private readonly bool _ignoreCase; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs new file mode 100644 index 00000000000000..e175f500abefec --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs @@ -0,0 +1,932 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Reflection; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.MethodInfos; + +using Internal.Reflection.Core.Execution; +using Internal.Reflection.Core.NonPortable; +using Internal.Reflection.Tracing; +using Internal.Reflection.Augments; + +using StructLayoutAttribute = System.Runtime.InteropServices.StructLayoutAttribute; + +namespace System.Reflection.Runtime.TypeInfos +{ + // + // Abstract base class for all TypeInfo's implemented by the runtime. + // + // This base class performs several services: + // + // - Provides default implementations whenever possible. Some of these + // return the "common" error result for narrowly applicable properties (such as those + // that apply only to generic parameters.) + // + // - Inverts the DeclaredMembers/DeclaredX relationship (DeclaredMembers is auto-implemented, others + // are overriden as abstract. This ordering makes more sense when reading from metadata.) + // + // - Overrides many "NotImplemented" members in TypeInfo with abstracts so failure to implement + // shows up as build error. + // + [DebuggerDisplay("{_debugName}")] + internal abstract partial class RuntimeTypeInfo : RuntimeType, ITraceableTypeMember, ICloneable + { + protected RuntimeTypeInfo() + { + } + + public abstract override bool IsTypeDefinition { get; } + public abstract override bool IsGenericTypeDefinition { get; } + protected abstract override bool HasElementTypeImpl(); + protected abstract override bool IsArrayImpl(); + public abstract override bool IsSZArray { get; } + public abstract override bool IsVariableBoundArray { get; } + protected abstract override bool IsByRefImpl(); + protected abstract override bool IsPointerImpl(); + public abstract override bool IsGenericParameter { get; } + public abstract override bool IsGenericTypeParameter { get; } + public abstract override bool IsGenericMethodParameter { get; } + public abstract override bool IsConstructedGenericType { get; } + public abstract override bool IsByRefLike { get; } + public sealed override bool IsCollectible => false; + + public abstract override Assembly Assembly { get; } + + public sealed override string AssemblyQualifiedName + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_AssemblyQualifiedName(this); +#endif + + string fullName = FullName; + if (fullName == null) // Some Types (such as generic parameters) return null for FullName by design. + return null; + string assemblyName = InternalFullNameOfAssembly; + return fullName + ", " + assemblyName; + } + } + + public sealed override Type AsType() + { + return this; + } + + public sealed override Type BaseType + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_BaseType(this); +#endif + + // If this has a RuntimeTypeHandle, let the underlying runtime engine have the first crack. If it refuses, fall back to metadata. + RuntimeTypeHandle typeHandle = InternalTypeHandleIfAvailable; + if (!typeHandle.IsNull()) + { + RuntimeTypeHandle baseTypeHandle; + if (ReflectionCoreExecution.ExecutionEnvironment.TryGetBaseType(typeHandle, out baseTypeHandle)) + return Type.GetTypeFromHandle(baseTypeHandle); + } + + Type baseType = BaseTypeWithoutTheGenericParameterQuirk; + if (baseType != null && baseType.IsGenericParameter) + { + // Desktop quirk: a generic parameter whose constraint is another generic parameter reports its BaseType as System.Object + // unless that other generic parameter has a "class" constraint. + GenericParameterAttributes genericParameterAttributes = baseType.GenericParameterAttributes; + if (0 == (genericParameterAttributes & GenericParameterAttributes.ReferenceTypeConstraint)) + baseType = typeof(object); + } + return baseType; + } + } + + public abstract override bool ContainsGenericParameters { get; } + + public abstract override IEnumerable CustomAttributes { get; } + + // + // Left unsealed as generic parameter types must override. + // + public override MethodBase DeclaringMethod + { + get + { + Debug.Assert(!IsGenericParameter); + throw new InvalidOperationException(SR.Arg_NotGenericParameter); + } + } + + // + // Equals()/GetHashCode() + // + // RuntimeTypeInfo objects are interned to preserve the app-compat rule that Type objects (which are the same as TypeInfo objects) + // can be compared using reference equality. + // + // We use weak pointers to intern the objects. This means we can use instance equality to implement Equals() but we cannot use + // the instance hashcode to implement GetHashCode() (otherwise, the hash code will not be stable if the TypeInfo is released and recreated.) + // Thus, we override and seal Equals() here but defer to a flavor-specific hash code implementation. + // + public sealed override bool Equals(object obj) + { + return object.ReferenceEquals(this, obj); + } + + public sealed override bool Equals(Type o) + { + return object.ReferenceEquals(this, o); + } + + public sealed override int GetHashCode() + { + return InternalGetHashCode(); + } + + public abstract override string FullName { get; } + + // + // Left unsealed as generic parameter types must override. + // + public override GenericParameterAttributes GenericParameterAttributes + { + get + { + Debug.Assert(!IsGenericParameter); + throw new InvalidOperationException(SR.Arg_NotGenericParameter); + } + } + + // + // Left unsealed as generic parameter types must override this. + // + public override int GenericParameterPosition + { + get + { + Debug.Assert(!IsGenericParameter); + throw new InvalidOperationException(SR.Arg_NotGenericParameter); + } + } + + public sealed override Type[] GenericTypeArguments + { + get + { + return InternalRuntimeGenericTypeArguments.CloneTypeArray(); + } + } + + [DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.PublicFields + | DynamicallyAccessedMemberTypes.PublicMethods + | DynamicallyAccessedMemberTypes.PublicEvents + | DynamicallyAccessedMemberTypes.PublicProperties + | DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.PublicNestedTypes)] + public sealed override MemberInfo[] GetDefaultMembers() + { + string defaultMemberName = GetDefaultMemberName(); + return defaultMemberName != null ? GetMember(defaultMemberName) : Array.Empty(); + } + + public sealed override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType) + { + // restrictions and known limitations compared to CoreCLR: + // - only interface.GetMethods() reflection visible interface methods are returned + // - all visible members of the interface must be reflection invokeable + // - this type and interfaceType must not be an open generic type + // - if this type and the method implementing the interface method are abstract, an exception is thrown + + if (IsGenericParameter) + throw new InvalidOperationException(SR.Arg_GenericParameter); + + if (interfaceType is null) + throw new ArgumentNullException(nameof(interfaceType)); + + if (!(interfaceType is RuntimeTypeInfo)) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(interfaceType)); + + RuntimeTypeHandle interfaceTypeHandle = interfaceType.TypeHandle; + + ReflectionCoreExecution.ExecutionEnvironment.VerifyInterfaceIsImplemented(TypeHandle, interfaceTypeHandle); + Debug.Assert(interfaceType.IsInterface); + Debug.Assert(!IsInterface); + + // SZArrays implement the methods on IList`1, IEnumerable`1, and ICollection`1 with + // runtime magic. We don't have accurate interface maps for them. + if (IsSZArray && interfaceType.IsGenericType) + throw new ArgumentException(SR.Argument_ArrayGetInterfaceMap); + + ReflectionCoreExecution.ExecutionEnvironment.GetInterfaceMap(this, interfaceType, out MethodInfo[] interfaceMethods, out MethodInfo[] targetMethods); + + InterfaceMapping im; + im.InterfaceType = interfaceType; + im.TargetType = this; + im.InterfaceMethods = interfaceMethods; + im.TargetMethods = targetMethods; + + return im; + } + + // + // Implements the correct GUID behavior for all "constructed" types (i.e. returning an all-zero GUID.) Left unsealed + // so that RuntimeNamedTypeInfo can override. + // + public override Guid GUID + { + get + { + return Guid.Empty; + } + } + + public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); + + public sealed override IEnumerable ImplementedInterfaces + { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", + Justification = "Interface lists on base types will be preserved same as for the current type")] + get + { + LowLevelListWithIList result = new LowLevelListWithIList(); + + bool done = false; + + // If this has a RuntimeTypeHandle, let the underlying runtime engine have the first crack. If it refuses, fall back to metadata. + RuntimeTypeHandle typeHandle = InternalTypeHandleIfAvailable; + if (!typeHandle.IsNull()) + { + IEnumerable implementedInterfaces = ReflectionCoreExecution.ExecutionEnvironment.TryGetImplementedInterfaces(typeHandle); + if (implementedInterfaces != null) + { + done = true; + + foreach (RuntimeTypeHandle th in implementedInterfaces) + { + result.Add(Type.GetTypeFromHandle(th)); + } + } + } + + if (!done) + { + TypeContext typeContext = this.TypeContext; + Type baseType = this.BaseTypeWithoutTheGenericParameterQuirk; + if (baseType != null) + result.AddRange(baseType.GetInterfaces()); + foreach (QTypeDefRefOrSpec directlyImplementedInterface in this.TypeRefDefOrSpecsForDirectlyImplementedInterfaces) + { + Type ifc = directlyImplementedInterface.Resolve(typeContext); + if (result.Contains(ifc)) + continue; + result.Add(ifc); + foreach (Type indirectIfc in ifc.GetInterfaces()) + { + if (result.Contains(indirectIfc)) + continue; + result.Add(indirectIfc); + } + } + } + + return result.AsNothingButIEnumerable(); + } + } + + public sealed override bool IsAssignableFrom(TypeInfo typeInfo) => IsAssignableFrom((Type)typeInfo); + + public sealed override bool IsAssignableFrom(Type c) + { + if (c == null) + return false; + + if (object.ReferenceEquals(c, this)) + return true; + + c = c.UnderlyingSystemType; + + Type typeInfo = c; + RuntimeTypeInfo toTypeInfo = this; + + if (typeInfo is not RuntimeType) + return false; // Desktop compat: If typeInfo is null, or implemented by a different Reflection implementation, return "false." + + RuntimeTypeInfo fromTypeInfo = typeInfo.CastToRuntimeTypeInfo(); + + if (toTypeInfo.Equals(fromTypeInfo)) + return true; + + RuntimeTypeHandle toTypeHandle = toTypeInfo.InternalTypeHandleIfAvailable; + RuntimeTypeHandle fromTypeHandle = fromTypeInfo.InternalTypeHandleIfAvailable; + bool haveTypeHandles = !(toTypeHandle.IsNull() || fromTypeHandle.IsNull()); + if (haveTypeHandles) + { + // If both types have type handles, let MRT handle this. It's not dependent on metadata. + if (ReflectionCoreExecution.ExecutionEnvironment.IsAssignableFrom(toTypeHandle, fromTypeHandle)) + return true; + + // Runtime IsAssignableFrom does not handle casts from generic type definitions: always returns false. For those, we fall through to the + // managed implementation. For everyone else, return "false". + // + // Runtime IsAssignableFrom does not handle pointer -> UIntPtr cast. + if (!(fromTypeInfo.IsGenericTypeDefinition || fromTypeInfo.IsPointer)) + return false; + } + + // If we got here, the types are open, or reduced away, or otherwise lacking in type handles. Perform the IsAssignability check in managed code. + return Assignability.IsAssignableFrom(this, typeInfo); + } + + public sealed override bool IsEnum + { + get + { + return 0 != (Classification & TypeClassification.IsEnum); + } + } + + public sealed override MemberTypes MemberType + { + get + { + if (IsPublic || IsNotPublic) + return MemberTypes.TypeInfo; + else + return MemberTypes.NestedType; + } + } + + // + // Left unsealed as there are so many subclasses. Need to be overriden by EcmaFormatRuntimeNamedTypeInfo and RuntimeConstructedGenericTypeInfo + // + public abstract override int MetadataToken + { + get; + } + + public sealed override Module Module + { + get + { + return Assembly.ManifestModule; + } + } + + public abstract override string Namespace { get; } + + public sealed override Type[] GenericTypeParameters + { + get + { + return RuntimeGenericTypeParameters.CloneTypeArray(); + } + } + + // + // Left unsealed as array types must override this. + // + public override int GetArrayRank() + { + Debug.Assert(!IsArray); + throw new ArgumentException(SR.Argument_HasToBeArrayClass); + } + + public sealed override Type GetElementType() + { + return InternalRuntimeElementType; + } + + // + // Left unsealed as generic parameter types must override. + // + public override Type[] GetGenericParameterConstraints() + { + Debug.Assert(!IsGenericParameter); + throw new InvalidOperationException(SR.Arg_NotGenericParameter); + } + + // + // Left unsealed as IsGenericType types must override this. + // + public override Type GetGenericTypeDefinition() + { + Debug.Assert(!IsGenericType); + throw new InvalidOperationException(SR.InvalidOperation_NotGenericType); + } + + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] + public sealed override Type MakeArrayType() + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_MakeArrayType(this); +#endif + + // Do not implement this as a call to MakeArrayType(1) - they are not interchangable. MakeArrayType() returns a + // vector type ("SZArray") while MakeArrayType(1) returns a multidim array of rank 1. These are distinct types + // in the ECMA model and in CLR Reflection. + return this.GetArrayTypeWithTypeHandle(); + } + + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] + public sealed override Type MakeArrayType(int rank) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_MakeArrayType(this, rank); +#endif + + if (rank <= 0) + throw new IndexOutOfRangeException(); + return this.GetMultiDimArrayTypeWithTypeHandle(rank); + } + + public sealed override Type MakeByRefType() + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_MakeByRefType(this); +#endif + return this.GetByRefType(); + } + + [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] + [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + public sealed override Type MakeGenericType(params Type[] typeArguments) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_MakeGenericType(this, typeArguments); +#endif + + if (typeArguments == null) + throw new ArgumentNullException(nameof(typeArguments)); + + if (!IsGenericTypeDefinition) + throw new InvalidOperationException(SR.Format(SR.Arg_NotGenericTypeDefinition, this)); + + // We intentionally don't validate the number of arguments or their suitability to the generic type's constraints. + // In a pay-for-play world, this can cause needless MissingMetadataExceptions. There is no harm in creating + // the Type object for an inconsistent generic type - no MethodTable will ever match it so any attempt to "invoke" it + // will throw an exception. + bool foundSignatureType = false; + RuntimeTypeInfo[] runtimeTypeArguments = new RuntimeTypeInfo[typeArguments.Length]; + for (int i = 0; i < typeArguments.Length; i++) + { + RuntimeTypeInfo runtimeTypeArgument = runtimeTypeArguments[i] = typeArguments[i] as RuntimeTypeInfo; + if (runtimeTypeArgument == null) + { + if (typeArguments[i] == null) + throw new ArgumentNullException(); + + if (typeArguments[i].IsSignatureType) + { + foundSignatureType = true; + } + else + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_MakeGenericType); // "PlatformNotSupported" because on desktop, passing in a foreign type is allowed and creates a RefEmit.TypeBuilder + } + } + } + + if (foundSignatureType) + return ReflectionAugments.MakeGenericSignatureType(this, typeArguments); + + for (int i = 0; i < typeArguments.Length; i++) + { + RuntimeTypeInfo runtimeTypeArgument = runtimeTypeArguments[i]; + + // Desktop compatibility: Treat generic type definitions as a constructed generic type using the generic parameters as type arguments. + if (runtimeTypeArgument.IsGenericTypeDefinition) + runtimeTypeArgument = runtimeTypeArguments[i] = runtimeTypeArgument.GetConstructedGenericType(runtimeTypeArgument.RuntimeGenericTypeParameters); + + if (runtimeTypeArgument.IsByRefLike) + throw new TypeLoadException(SR.CannotUseByRefLikeTypeInInstantiation); + } + + return this.GetConstructedGenericTypeWithTypeHandle(runtimeTypeArguments); + } + + public sealed override Type MakePointerType() + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_MakePointerType(this); +#endif + + return this.GetPointerType(); + } + + public sealed override Type DeclaringType + { + get + { + return this.InternalDeclaringType; + } + } + + public sealed override string Name + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_Name(this); +#endif + + Type rootCauseForFailure = null; + string name = InternalGetNameIfAvailable(ref rootCauseForFailure); + if (name == null) + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(rootCauseForFailure); + return name; + } + } + + public sealed override Type ReflectedType + { + get + { + // Desktop compat: For types, ReflectedType == DeclaringType. Nested types are always looked up as BindingFlags.DeclaredOnly was passed. + // For non-nested types, the concept of a ReflectedType doesn't even make sense. + return DeclaringType; + } + } + + public abstract override StructLayoutAttribute StructLayoutAttribute { get; } + + public abstract override string ToString(); + + public sealed override RuntimeTypeHandle TypeHandle + { + get + { + RuntimeTypeHandle typeHandle = InternalTypeHandleIfAvailable; + if (!typeHandle.IsNull()) + return typeHandle; + + // If a type doesn't have a type handle, it's either because we optimized away the MethodTable + // but the reflection metadata had to be kept around, or because we have an open type somewhere + // (open types never get EETypes). Open types are PlatformNotSupported and there's nothing + // that can be done about that. Missing MethodTable can be fixed by helping the AOT compiler + // with some hints. + if (!IsGenericTypeDefinition && ContainsGenericParameters) + throw new PlatformNotSupportedException(SR.PlatformNotSupported_NoTypeHandleForOpenTypes); + + // If got here, this is a "plain old type" that has metadata but no type handle. We can get here if the only + // representation of the type is in the native metadata and there's no MethodTable at the runtime side. + // If you squint hard, this is a missing metadata situation - the metadata is missing on the runtime side - and + // the action for the user to take is the same: go mess with RD.XML. + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(this); + } + } + + public sealed override Type UnderlyingSystemType + { + get + { + return this; + } + } + + protected abstract override TypeAttributes GetAttributeFlagsImpl(); + + protected sealed override TypeCode GetTypeCodeImpl() + { + return ReflectionAugments.GetRuntimeTypeCode(this); + } + + protected abstract int InternalGetHashCode(); + + protected sealed override bool IsCOMObjectImpl() + { + return ReflectionCoreExecution.ExecutionEnvironment.IsCOMObject(this); + } + + protected sealed override bool IsPrimitiveImpl() + { + return 0 != (Classification & TypeClassification.IsPrimitive); + } + + protected sealed override bool IsValueTypeImpl() + { + return 0 != (Classification & TypeClassification.IsValueType); + } + + string ITraceableTypeMember.MemberName + { + get + { + string name = InternalNameIfAvailable; + return name ?? string.Empty; + } + } + + Type ITraceableTypeMember.ContainingType + { + get + { + return this.InternalDeclaringType; + } + } + + // + // Returns the anchoring typedef that declares the members that this type wants returned by the Declared*** properties. + // The Declared*** properties will project the anchoring typedef's members by overriding their DeclaringType property with "this" + // and substituting the value of this.TypeContext into any generic parameters. + // + // Default implementation returns null which causes the Declared*** properties to return no members. + // + // Note that this does not apply to DeclaredNestedTypes. Nested types and their containers have completely separate generic instantiation environments + // (despite what C# might lead you to think.) Constructed generic types return the exact same same nested types that its generic type definition does + // - i.e. their DeclaringTypes refer back to the generic type definition, not the constructed generic type.) + // + // Note also that we cannot use this anchoring concept for base types because of generic parameters. Generic parameters return + // a base class and interface list based on its constraints. + // + internal virtual RuntimeNamedTypeInfo AnchoringTypeDefinitionForDeclaredMembers + { + get + { + return null; + } + } + + internal EnumInfo EnumInfo => Cache.EnumInfo; + + internal abstract Type InternalDeclaringType { get; } + + // + // Return the full name of the "defining assembly" for the purpose of computing TypeInfo.AssemblyQualifiedName; + // + internal abstract string InternalFullNameOfAssembly { get; } + + public abstract override string InternalGetNameIfAvailable(ref Type rootCauseForFailure); + + // + // Left unsealed as HasElement types must override this. + // + internal virtual RuntimeTypeInfo InternalRuntimeElementType + { + get + { + Debug.Assert(!HasElementType); + return null; + } + } + + // + // Left unsealed as constructed generic types must override this. + // + internal virtual RuntimeTypeInfo[] InternalRuntimeGenericTypeArguments + { + get + { + Debug.Assert(!IsConstructedGenericType); + return Array.Empty(); + } + } + + internal abstract RuntimeTypeHandle InternalTypeHandleIfAvailable { get; } + + internal bool IsDelegate + { + get + { + return 0 != (Classification & TypeClassification.IsDelegate); + } + } + + // + // Returns true if it's possible to ask for a list of members and the base type without triggering a MissingMetadataException. + // + internal abstract bool CanBrowseWithoutMissingMetadataExceptions { get; } + + // + // The non-public version of TypeInfo.GenericTypeParameters (does not array-copy.) + // + internal virtual RuntimeTypeInfo[] RuntimeGenericTypeParameters + { + get + { + Debug.Assert(!(this is RuntimeNamedTypeInfo)); + return Array.Empty(); + } + } + + // + // Normally returns empty: Overridden by array types to return constructors. + // + internal virtual IEnumerable SyntheticConstructors + { + get + { + return Empty.Enumerable; + } + } + + // + // Normally returns empty: Overridden by array types to return the "Get" and "Set" methods. + // + internal virtual IEnumerable SyntheticMethods + { + get + { + return Empty.Enumerable; + } + } + + // + // Returns the base type as a typeDef, Ref, or Spec. Default behavior is to QTypeDefRefOrSpec.Null, which causes BaseType to return null. + // + // If you override this method, there is no need to override BaseTypeWithoutTheGenericParameterQuirk. + // + internal virtual QTypeDefRefOrSpec TypeRefDefOrSpecForBaseType + { + get + { + return QTypeDefRefOrSpec.Null; + } + } + + // + // Returns the *directly implemented* interfaces as typedefs, specs or refs. ImplementedInterfaces will take care of the transitive closure and + // insertion of the TypeContext. + // + internal virtual QTypeDefRefOrSpec[] TypeRefDefOrSpecsForDirectlyImplementedInterfaces + { + get + { + return Array.Empty(); + } + } + + // + // Returns the generic parameter substitutions to use when enumerating declared members, base class and implemented interfaces. + // + internal virtual TypeContext TypeContext + { + get + { + return new TypeContext(null, null); + } + } + + // + // Note: This can be (and is) called multiple times. We do not do this work in the constructor as calling ToString() + // in the constructor causes some serious recursion issues. + // + internal void EstablishDebugName() + { + bool populateDebugNames = DeveloperExperienceState.DeveloperExperienceModeEnabled; +#if DEBUG + populateDebugNames = true; +#endif + if (!populateDebugNames) + return; + + if (_debugName == null) + { + _debugName = "Constructing..."; // Protect against any inadvertent reentrancy. + string debugName; +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + debugName = this.GetTraceString(); // If tracing on, call this.GetTraceString() which only gives you useful strings when metadata is available but doesn't pollute the ETW trace. + else +#endif + debugName = this.ToString(); + if (debugName == null) + debugName = ""; + _debugName = debugName; + } + return; + } + + // + // This internal method implements BaseType without the following desktop quirk: + // + // class Foo + // where X:Y + // where Y:MyReferenceClass + // + // The desktop reports "X"'s base type as "System.Object" rather than "Y", even though it does + // report any interfaces that are in MyReferenceClass's interface list. + // + // This seriously messes up the implementation of RuntimeTypeInfo.ImplementedInterfaces which assumes + // that it can recover the transitive interface closure by combining the directly mentioned interfaces and + // the BaseType's own interface closure. + // + // To implement this with the least amount of code smell, we'll implement the idealized version of BaseType here + // and make the special-case adjustment in the public version of BaseType. + // + // If you override this method, there is no need to overrride TypeRefDefOrSpecForBaseType. + // + // This method is left unsealed so that RuntimeCLSIDTypeInfo can override. + // + internal virtual Type BaseTypeWithoutTheGenericParameterQuirk + { + get + { + QTypeDefRefOrSpec baseTypeDefRefOrSpec = TypeRefDefOrSpecForBaseType; + RuntimeTypeInfo baseType = null; + if (!baseTypeDefRefOrSpec.IsValid) + { + baseType = baseTypeDefRefOrSpec.Resolve(this.TypeContext); + } + return baseType; + } + } + + private string GetDefaultMemberName() + { + Type defaultMemberAttributeType = typeof(DefaultMemberAttribute); + for (Type type = this; type != null; type = type.BaseType) + { + foreach (CustomAttributeData attribute in type.CustomAttributes) + { + if (attribute.AttributeType == defaultMemberAttributeType) + { + // NOTE: Neither indexing nor cast can fail here. Any attempt to use fewer than 1 argument + // or a non-string argument would correctly trigger MissingMethodException before + // we reach here as that would be an attempt to reference a non-existent DefaultMemberAttribute + // constructor. + Debug.Assert(attribute.ConstructorArguments.Count == 1 && attribute.ConstructorArguments[0].Value is string); + + string memberName = (string)(attribute.ConstructorArguments[0].Value); + return memberName; + } + } + } + + return null; + } + + // + // Returns a latched set of flags indicating the value of IsValueType, IsEnum, etc. + // + private TypeClassification Classification + { + get + { + if (_lazyClassification == 0) + { + TypeClassification classification = TypeClassification.Computed; + Type baseType = this.BaseType; + if (baseType != null) + { + Type enumType = typeof(Enum); + Type valueType = typeof(ValueType); + + if (baseType == enumType) + classification |= TypeClassification.IsEnum | TypeClassification.IsValueType; + if (baseType == typeof(MulticastDelegate)) + classification |= TypeClassification.IsDelegate; + if (baseType == valueType && this != enumType) + { + classification |= TypeClassification.IsValueType; + foreach (Type primitiveType in ReflectionCoreExecution.ExecutionDomain.PrimitiveTypes) + { + if (this.Equals(primitiveType)) + { + classification |= TypeClassification.IsPrimitive; + break; + } + } + } + } + _lazyClassification = classification; + } + return _lazyClassification; + } + } + + [Flags] + private enum TypeClassification + { + Computed = 0x00000001, // Always set (to indicate that the lazy evaluation has occurred) + IsValueType = 0x00000002, + IsEnum = 0x00000004, + IsPrimitive = 0x00000008, + IsDelegate = 0x00000010, + } + + object ICloneable.Clone() + { + return this; + } + + private volatile TypeClassification _lazyClassification; + + private string _debugName; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeParsing/GetTypeOptions.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeParsing/GetTypeOptions.cs new file mode 100644 index 00000000000000..1a0ff4ba007ae4 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeParsing/GetTypeOptions.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Diagnostics; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.Assemblies; + +namespace System.Reflection.Runtime.TypeParsing +{ + /// + /// Return the assembly matching the refName if one exists. If a matching assembly doesn't exist, return null. Throw for all other errors. + /// + internal delegate Assembly CoreAssemblyResolver(RuntimeAssemblyName refName); + + /// + /// Look for a type matching the name inside the provided assembly. If "containingAssemblyIfAny" is null, look in a set of default assemblies. For example, if + /// this resolver is for the Type.GetType() api, the default assemblies are the assembly that invoked Type.GetType() and mscorlib in that order. + /// If this resolver is for Assembly.GetType(), the default is that assembly. Third-party resolvers can do whatever they want. If no type exists for that name, + /// return null. Throw for all other errors. The name will be for a top-level named type only. No nested types. No constructed types. + /// + /// + /// + /// This delegate "should" take an "ignoreCase" parameter too, but pragmatically, every resolver we create is a closure for other reasons so + /// it's more convenient to let "ignoreCase" be just another variable that's captured in that closure. + /// + internal delegate Type CoreTypeResolver(Assembly containingAssemblyIfAny, string name); + + // + // Captures the various options passed to the Type.GetType() family of apis. + // + internal sealed class GetTypeOptions + { + public GetTypeOptions(CoreAssemblyResolver coreAssemblyResolver, CoreTypeResolver coreTypeResolver, bool throwOnError, bool ignoreCase) + { + Debug.Assert(coreAssemblyResolver != null); + Debug.Assert(coreTypeResolver != null); + + _coreAssemblyResolver = coreAssemblyResolver; + _coreTypeResolver = coreTypeResolver; + ThrowOnError = throwOnError; + IgnoreCase = ignoreCase; + } + + public Assembly CoreResolveAssembly(RuntimeAssemblyName name) + { + Assembly assembly = _coreAssemblyResolver(name); + if (assembly == null && ThrowOnError) + throw new FileNotFoundException(SR.Format(SR.FileNotFound_AssemblyNotFound, name.FullName)); + return assembly; + } + + public Type CoreResolveType(Assembly containingAssemblyIfAny, string name) + { + Type type = _coreTypeResolver(containingAssemblyIfAny, name); + if (type == null && ThrowOnError) + throw Helpers.CreateTypeLoadException(name.EscapeTypeNameIdentifier(), containingAssemblyIfAny); + return type; + } + + public bool ThrowOnError { get; } + public bool IgnoreCase { get; } + + private readonly CoreAssemblyResolver _coreAssemblyResolver; + private readonly CoreTypeResolver _coreTypeResolver; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs new file mode 100644 index 00000000000000..dfebfcacea21d1 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs @@ -0,0 +1,243 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.Assemblies; + +namespace System.Reflection.Runtime.TypeParsing +{ + // + // String tokenizer for typenames passed to the GetType() api's. + // + internal sealed class TypeLexer + { + public TypeLexer(string s) + { + // Turn the string into a char array with a NUL terminator. + char[] chars = new char[s.Length + 1]; + s.CopyTo(0, chars, 0, s.Length); + _chars = chars; + _index = 0; + } + + public TokenType Peek + { + get + { + SkipWhiteSpace(); + char c = _chars[_index]; + return CharToToken(c); + } + } + + public TokenType PeekSecond + { + get + { + SkipWhiteSpace(); + int index = _index + 1; + while (char.IsWhiteSpace(_chars[index])) + index++; + char c = _chars[index]; + return CharToToken(c); + } + } + + + public void Skip() + { + Debug.Assert(_index != _chars.Length); + SkipWhiteSpace(); + _index++; + } + + // Return the next token and skip index past it unless already at end of string + // or the token is not a reserved token. + public TokenType GetNextToken() + { + TokenType tokenType = Peek; + if (tokenType == TokenType.End || tokenType == TokenType.Other) + return tokenType; + Skip(); + return tokenType; + } + + // + // Lex the next segment as part of a type name. (Do not use for assembly names.) + // + // Note that unescaped "."'s do NOT terminate the identifier, but unescaped "+"'s do. + // + // Terminated by the first non-escaped reserved character ('[', ']', '+', '&', '*' or ',') + // + public string GetNextIdentifier() + { + SkipWhiteSpace(); + + int src = _index; + char[] buffer = new char[_chars.Length]; + int dst = 0; + for (;;) + { + char c = _chars[src]; + TokenType token = CharToToken(c); + if (token != TokenType.Other) + break; + src++; + if (c == '\\') + { + c = _chars[src]; + if (c != NUL) + src++; + if (!c.NeedsEscapingInTypeName()) + { + // If we got here, a backslash was used to escape a character that is not legal to escape inside a type name. + // + // Common sense would dictate throwing an ArgumentException but that's not what the desktop CLR does. + // The desktop CLR treats this case by returning FALSE from TypeName::TypeNameParser::GetIdentifier(). + // Unfortunately, no one checks this return result. Instead, the CLR keeps parsing (unfortunately, the lexer + // was left in some strange state by the previous failure but typically, this goes unnoticed) and eventually, tries to resolve + // a Type whose name is the empty string. When it can't resolve that type, the CLR throws a TypeLoadException() + // complaining about be unable to find a type with the empty name. + // + // To emulate this accidental behavior, we'll throw a special exception that's caught by the TypeParser. + // + throw new IllegalEscapeSequenceException(); + } + } + buffer[dst++] = c; + } + + _index = src; + return new string(buffer, 0, dst); + } + + // + // Lex the next segment as the assembly name at the end of an assembly-qualified type name. (Do not use for + // assembly names embedded inside generic type arguments.) + // + // Terminated by NUL. There are no escape characters defined by the typename lexer (however, AssemblyName + // does have its own escape rules.) + // + public RuntimeAssemblyName GetNextAssemblyName() + { + SkipWhiteSpace(); + + int src = _index; + char[] buffer = new char[_chars.Length]; + int dst = 0; + for (;;) + { + char c = _chars[src]; + if (c == NUL) + break; + src++; + buffer[dst++] = c; + } + _index = src; + string fullName = new string(buffer, 0, dst); + return AssemblyNameParser.Parse(fullName); + } + + // + // Lex the next segment as an assembly name embedded inside a generic argument type. + // + // Terminated by an unescaped ']'. + // + public RuntimeAssemblyName GetNextEmbeddedAssemblyName() + { + SkipWhiteSpace(); + + int src = _index; + char[] buffer = new char[_chars.Length]; + int dst = 0; + for (;;) + { + char c = _chars[src]; + if (c == NUL) + throw new ArgumentException(); + if (c == ']') + break; + src++; + + // Backslash can be used to escape a ']' - any other backslash character is left alone (along with the backslash) + // for the AssemblyName parser to handle. + if (c == '\\' && _chars[src] == ']') + { + c = _chars[src++]; + } + buffer[dst++] = c; + } + _index = src; + string fullName = new string(buffer, 0, dst); + return AssemblyNameParser.Parse(fullName); + } + + // + // Classify a character as a TokenType. (Fortunately, all tokens in typename strings other than identifiers are single-character tokens.) + // + private static TokenType CharToToken(char c) + { + switch (c) + { + case NUL: + return TokenType.End; + case '[': + return TokenType.OpenSqBracket; + case ']': + return TokenType.CloseSqBracket; + case ',': + return TokenType.Comma; + case '+': + return TokenType.Plus; + case '*': + return TokenType.Asterisk; + case '&': + return TokenType.Ampersand; + default: + return TokenType.Other; + } + } + + // + // The desktop typename parser has a strange attitude towards whitespace. It throws away whitespace between punctuation tokens and whitespace + // preceeding identifiers or assembly names (and this cannot be escaped away). But whitespace between the end of an identifier + // and the punctuation that ends it is *not* ignored. + // + // In other words, GetType(" Foo") searches for "Foo" but GetType("Foo ") searches for "Foo ". + // + // Whitespace between the end of an assembly name and the punction mark that ends it is also not ignored by this parser, + // but this is irrelevant since the assembly name is then turned over to AssemblyName for parsing, which *does* ignore trailing whitespace. + // + private void SkipWhiteSpace() + { + while (char.IsWhiteSpace(_chars[_index])) + _index++; + } + + + private int _index; + private readonly char[] _chars; + private const char NUL = (char)0; + + + public sealed class IllegalEscapeSequenceException : Exception + { + } + } + + internal enum TokenType + { + End = 0, //At end of string + OpenSqBracket = 1, //'[' + CloseSqBracket = 2, //']' + Comma = 3, //',' + Plus = 4, //'+' + Asterisk = 5, //'*' + Ampersand = 6, //'&' + Other = 7, //Type identifier, AssemblyName or embedded AssemblyName. + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeParsing/TypeName.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeParsing/TypeName.cs new file mode 100644 index 00000000000000..91fcf3981c4b29 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeParsing/TypeName.cs @@ -0,0 +1,322 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Collections; +using System.Reflection; +using System.Collections.Generic; + +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.TypeInfos; +using System.Reflection.Runtime.Assemblies; + +namespace System.Reflection.Runtime.TypeParsing +{ + // + // The TypeName class is the base class for a family of types that represent the nodes in a parse tree for + // assembly-qualified type names. + // + internal abstract class TypeName + { + /// + /// Helper for the Type.GetType() family of apis. "containingAssemblyIsAny" is the assembly to search for (as determined + /// by a qualifying assembly string in the original type string passed to Type.GetType(). If null, it means the type stream + /// didn't specify an assembly name. How to respond to that is up to the type resolver delegate in getTypeOptions - this class + /// is just a middleman. + /// + public abstract Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions); + public abstract override string ToString(); + } + + // + // Represents a parse of a type name qualified by an assembly name. + // + internal sealed class AssemblyQualifiedTypeName : TypeName + { + public AssemblyQualifiedTypeName(NonQualifiedTypeName nonQualifiedTypeName, RuntimeAssemblyName assemblyName) + { + Debug.Assert(nonQualifiedTypeName != null); + Debug.Assert(assemblyName != null); + _nonQualifiedTypeName = nonQualifiedTypeName; + _assemblyName = assemblyName; + } + + public sealed override string ToString() + { + return _nonQualifiedTypeName.ToString() + ", " + _assemblyName.FullName; + } + + public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions) + { + containingAssemblyIfAny = getTypeOptions.CoreResolveAssembly(_assemblyName); + if (containingAssemblyIfAny == null) + return null; + return _nonQualifiedTypeName.ResolveType(containingAssemblyIfAny, getTypeOptions); + } + + private readonly RuntimeAssemblyName _assemblyName; + private readonly NonQualifiedTypeName _nonQualifiedTypeName; + } + + // + // Base class for all non-assembly-qualified type names. + // + internal abstract class NonQualifiedTypeName : TypeName + { + } + + // + // Base class for namespace or nested type. + // + internal abstract class NamedTypeName : NonQualifiedTypeName + { + } + + // + // Non-nested named type. The full name is the namespace-qualified name. For example, the FullName for + // System.Collections.Generic.IList<> is "System.Collections.Generic.IList`1". + // + internal sealed partial class NamespaceTypeName : NamedTypeName + { + public NamespaceTypeName(string fullName) + { + _fullName = fullName; + } + + public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions) + { + return getTypeOptions.CoreResolveType(containingAssemblyIfAny, _fullName); + } + + public sealed override string ToString() + { + return _fullName.EscapeTypeNameIdentifier(); + } + + private readonly string _fullName; + } + + // + // A nested type. The Name is the simple name of the type (not including any portion of its declaring type name.) + // + internal sealed class NestedTypeName : NamedTypeName + { + public NestedTypeName(string nestedTypeName, NamedTypeName declaringType) + { + _nestedTypeName = nestedTypeName; + _declaringType = declaringType; + } + + public sealed override string ToString() + { + return _declaringType + "+" + _nestedTypeName.EscapeTypeNameIdentifier(); + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", + Justification = "Reflection implementation")] + public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions) + { + Type declaringType = _declaringType.ResolveType(containingAssemblyIfAny, getTypeOptions); + if (declaringType == null) + return null; + + // Desktop compat note: If there is more than one nested type that matches the name in a case-blind match, + // we might not return the same one that the desktop returns. The actual selection method is influenced both by the type's + // placement in the IL and the implementation details of the CLR's internal hashtables so it would be very + // hard to replicate here. + // + // Desktop compat note #2: Case-insensitive lookups: If we don't find a match, we do *not* go back and search + // other declaring types that might match the case-insensitive search and contain the nested type being sought. + // Though this is somewhat unsatisfactory, the desktop CLR has the same limitation. + + // Don't change these flags - we may be talking to a third party type here and we need to invoke it the way CoreClr does. + BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic; + Type nestedType; + if (!getTypeOptions.IgnoreCase) + { + nestedType = declaringType.GetNestedType(_nestedTypeName, bf); + } + else + { + // Return the first name that matches. Which one gets returned on a multiple match is an implementation detail. + // Unfortunately, compat prevents us from just throwing AmbiguousMatchException. + nestedType = null; + string lowerNestedTypeName = _nestedTypeName.ToLowerInvariant(); //@todo: Once String.Equals() works with StringComparison.InvariantIgnoreCase, it would be better to use that. + foreach (Type nt in declaringType.GetNestedTypes(bf)) + { + if (nt.Name.ToLowerInvariant() == lowerNestedTypeName) + { + nestedType = nt; + break; + } + } + } + if (nestedType == null && getTypeOptions.ThrowOnError) + throw Helpers.CreateTypeLoadException(ToString(), containingAssemblyIfAny); + return nestedType; + } + + private readonly string _nestedTypeName; + private readonly NamedTypeName _declaringType; + } + + // + // Abstract base for array, byref and pointer type names. + // + internal abstract class HasElementTypeName : NonQualifiedTypeName + { + public HasElementTypeName(TypeName elementTypeName) + { + ElementTypeName = elementTypeName; + } + + protected TypeName ElementTypeName { get; } + } + + // + // A single-dimensional zero-lower-bound array type name. + // + internal sealed class ArrayTypeName : HasElementTypeName + { + public ArrayTypeName(TypeName elementTypeName) + : base(elementTypeName) + { + } + + public sealed override string ToString() + { + return ElementTypeName + "[]"; + } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", + Justification = "Used to implement resolving types from strings.")] + public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions) + { + return ElementTypeName.ResolveType(containingAssemblyIfAny, getTypeOptions)?.MakeArrayType(); + } + } + + // + // A multidim array type name. + // + internal sealed class MultiDimArrayTypeName : HasElementTypeName + { + public MultiDimArrayTypeName(TypeName elementTypeName, int rank) + : base(elementTypeName) + { + _rank = rank; + } + + public sealed override string ToString() + { + return ElementTypeName + "[" + (_rank == 1 ? "*" : new string(',', _rank - 1)) + "]"; + } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", + Justification = "Used to implement resolving types from strings.")] + public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions) + { + return ElementTypeName.ResolveType(containingAssemblyIfAny, getTypeOptions)?.MakeArrayType(_rank); + } + + private readonly int _rank; + } + + // + // A byref type. + // + internal sealed class ByRefTypeName : HasElementTypeName + { + public ByRefTypeName(TypeName elementTypeName) + : base(elementTypeName) + { + } + + public sealed override string ToString() + { + return ElementTypeName + "&"; + } + + public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions) + { + return ElementTypeName.ResolveType(containingAssemblyIfAny, getTypeOptions)?.MakeByRefType(); + } + } + + // + // A pointer type. + // + internal sealed class PointerTypeName : HasElementTypeName + { + public PointerTypeName(TypeName elementTypeName) + : base(elementTypeName) + { + } + + public sealed override string ToString() + { + return ElementTypeName + "*"; + } + + public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions) + { + return ElementTypeName.ResolveType(containingAssemblyIfAny, getTypeOptions)?.MakePointerType(); + } + } + + // + // A constructed generic type. + // + internal sealed class ConstructedGenericTypeName : NonQualifiedTypeName + { + public ConstructedGenericTypeName(NamedTypeName genericTypeDefinition, IList genericTypeArguments) + { + _genericTypeDefinition = genericTypeDefinition; + _genericTypeArguments = genericTypeArguments; + } + + public sealed override string ToString() + { + string s = _genericTypeDefinition.ToString(); + s += "["; + string sep = ""; + foreach (TypeName genericTypeArgument in _genericTypeArguments) + { + s += sep; + sep = ","; + if (genericTypeArgument is AssemblyQualifiedTypeName) + s += "[" + genericTypeArgument.ToString() + "]"; + else + s += genericTypeArgument.ToString(); + } + s += "]"; + return s; + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:RequiresUnreferencedCode", + Justification = "Used to implement resolving types from strings.")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", + Justification = "Used to implement resolving types from strings.")] + public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions) + { + Type genericTypeDefinition = _genericTypeDefinition.ResolveType(containingAssemblyIfAny, getTypeOptions); + if (genericTypeDefinition == null) + return null; + + int numGenericArguments = _genericTypeArguments.Count; + Type[] genericArgumentTypes = new Type[numGenericArguments]; + for (int i = 0; i < numGenericArguments; i++) + { + // Do not pass containingAssemblyIfAny down to ResolveType for the generic type arguments. + if ((genericArgumentTypes[i] = _genericTypeArguments[i].ResolveType(null, getTypeOptions)) == null) + return null; + } + return genericTypeDefinition.MakeGenericType(genericArgumentTypes); + } + + private readonly NamedTypeName _genericTypeDefinition; + private readonly IList _genericTypeArguments; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs new file mode 100644 index 00000000000000..c7014e46e33e97 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs @@ -0,0 +1,222 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection.Runtime.Assemblies; + +namespace System.Reflection.Runtime.TypeParsing +{ + // + // Parser for type names passed to GetType() apis. + // + internal sealed class TypeParser + { + // + // Parses a typename. The typename may be optionally postpended with a "," followed by a legal assembly name. + // + public static TypeName ParseAssemblyQualifiedTypeName(string s, bool throwOnError) + { + if (throwOnError) + return ParseAssemblyQualifiedTypeName(s); + + try + { + return ParseAssemblyQualifiedTypeName(s); + } + catch (ArgumentException) + { + return null; + } + } + + // + // Parses a typename. The typename may be optionally postpended with a "," followed by a legal assembly name. + // + private static TypeName ParseAssemblyQualifiedTypeName(string s) + { + // Desktop compat: a whitespace-only "typename" qualified by an assembly name throws an ArgumentException rather than + // a TypeLoadException. + int idx = 0; + while (idx < s.Length && char.IsWhiteSpace(s[idx])) + { + idx++; + } + if (idx < s.Length && s[idx] == ',') + throw new ArgumentException(SR.Arg_TypeLoadNullStr); + + try + { + TypeParser parser = new TypeParser(s); + NonQualifiedTypeName typeName = parser.ParseNonQualifiedTypeName(); + TokenType token = parser._lexer.GetNextToken(); + if (token == TokenType.End) + return typeName; + if (token == TokenType.Comma) + { + RuntimeAssemblyName assemblyName = parser._lexer.GetNextAssemblyName(); + token = parser._lexer.Peek; + if (token != TokenType.End) + throw new ArgumentException(); + return new AssemblyQualifiedTypeName(typeName, assemblyName); + } + throw new ArgumentException(); + } + catch (TypeLexer.IllegalEscapeSequenceException) + { + // Emulates a CLR4.5 bug that causes any string that contains an illegal escape sequence to be parsed as the empty string. + return ParseAssemblyQualifiedTypeName(string.Empty); + } + } + + private TypeParser(string s) + { + _lexer = new TypeLexer(s); + } + + + // + // Parses a type name without any assembly name qualification. + // + private NonQualifiedTypeName ParseNonQualifiedTypeName() + { + // Parse the named type or constructed generic type part first. + NonQualifiedTypeName typeName = ParseNamedOrConstructedGenericTypeName(); + + // Iterate through any "has-element" qualifiers ([], &, *). + for (;;) + { + TokenType token = _lexer.Peek; + if (token == TokenType.End) + break; + if (token == TokenType.Asterisk) + { + _lexer.Skip(); + typeName = new PointerTypeName(typeName); + } + else if (token == TokenType.Ampersand) + { + _lexer.Skip(); + typeName = new ByRefTypeName(typeName); + } + else if (token == TokenType.OpenSqBracket) + { + _lexer.Skip(); + token = _lexer.GetNextToken(); + if (token == TokenType.Asterisk) + { + typeName = new MultiDimArrayTypeName(typeName, 1); + token = _lexer.GetNextToken(); + } + else + { + int rank = 1; + while (token == TokenType.Comma) + { + token = _lexer.GetNextToken(); + rank++; + } + if (rank == 1) + typeName = new ArrayTypeName(typeName); + else + typeName = new MultiDimArrayTypeName(typeName, rank); + } + if (token != TokenType.CloseSqBracket) + throw new ArgumentException(); + } + else + { + break; + } + } + return typeName; + } + + // + // Foo or Foo+Inner or Foo[String] or Foo+Inner[String] + // + private NonQualifiedTypeName ParseNamedOrConstructedGenericTypeName() + { + NamedTypeName namedType = ParseNamedTypeName(); + // Because "[" is used both for generic arguments and array indexes, we must peek two characters deep. + if (!(_lexer.Peek == TokenType.OpenSqBracket && (_lexer.PeekSecond == TokenType.Other || _lexer.PeekSecond == TokenType.OpenSqBracket))) + return namedType; + else + { + _lexer.Skip(); + LowLevelListWithIList genericTypeArguments = new LowLevelListWithIList(); + for (;;) + { + TypeName genericTypeArgument = ParseGenericTypeArgument(); + genericTypeArguments.Add(genericTypeArgument); + TokenType token = _lexer.GetNextToken(); + if (token == TokenType.CloseSqBracket) + break; + if (token != TokenType.Comma) + throw new ArgumentException(); + } + + return new ConstructedGenericTypeName(namedType, genericTypeArguments); + } + } + + // + // Foo or Foo+Inner + // + private NamedTypeName ParseNamedTypeName() + { + NamedTypeName namedType = ParseNamespaceTypeName(); + while (_lexer.Peek == TokenType.Plus) + { + _lexer.Skip(); + string nestedTypeName = _lexer.GetNextIdentifier(); + namedType = new NestedTypeName(nestedTypeName, namedType); + } + return namedType; + } + + // + // Non-nested named type. + // + private NamespaceTypeName ParseNamespaceTypeName() + { + string fullName = _lexer.GetNextIdentifier(); + return new NamespaceTypeName(fullName); + } + + // + // Parse a generic argument. In particular, generic arguments can take the special form [,]. + // + private TypeName ParseGenericTypeArgument() + { + TokenType token = _lexer.GetNextToken(); + if (token == TokenType.Other) + { + NonQualifiedTypeName nonQualifiedTypeName = ParseNonQualifiedTypeName(); + return nonQualifiedTypeName; + } + else if (token == TokenType.OpenSqBracket) + { + RuntimeAssemblyName assemblyName = null; + NonQualifiedTypeName typeName = ParseNonQualifiedTypeName(); + token = _lexer.GetNextToken(); + if (token == TokenType.Comma) + { + assemblyName = _lexer.GetNextEmbeddedAssemblyName(); + token = _lexer.GetNextToken(); + } + if (token != TokenType.CloseSqBracket) + throw new ArgumentException(); + if (assemblyName == null) + return typeName; + else + return new AssemblyQualifiedTypeName(typeName, assemblyName); + } + else + throw new ArgumentException(); + } + + private readonly TypeLexer _lexer; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Interop.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Interop.cs new file mode 100644 index 00000000000000..b9941aaec9444f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Interop.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; +using global::System.Runtime.InteropServices; +using global::Internal.Reflection.Core.Execution; + +namespace Internal.Reflection.Execution +{ + //========================================================================================================== + // These ExecutionEnvironment entrypoints provide access to the Interop\MCG information that + // enables Reflection invoke + //========================================================================================================== + internal sealed partial class ExecutionEnvironmentImplementation : ExecutionEnvironment + { + public sealed override bool IsCOMObject(Type type) + { +#if PROJECTN + return McgMarshal.IsComObject(type); +#else + return false; +#endif + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.ManifestResources.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.ManifestResources.cs new file mode 100644 index 00000000000000..c7b65fdf99eab5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.ManifestResources.cs @@ -0,0 +1,168 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Reflection; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.TypeLoader; + +using Internal.Reflection.Core.Execution; +using Internal.NativeFormat; + +namespace Internal.Reflection.Execution +{ + //========================================================================================================== + // These ExecutionEnvironment entrypoints implement support for manifest resource streams on the Assembly class. + //========================================================================================================== + internal sealed partial class ExecutionEnvironmentImplementation : ExecutionEnvironment + { + public sealed override ManifestResourceInfo GetManifestResourceInfo(Assembly assembly, string resourceName) + { + LowLevelList resourceInfos = GetExtractedResources(assembly); + for (int i = 0; i < resourceInfos.Count; i++) + { + if (resourceName == resourceInfos[i].Name) + { + return new ManifestResourceInfo(assembly, resourceName, ResourceLocation.Embedded); + } + } + return null; + } + + public sealed override string[] GetManifestResourceNames(Assembly assembly) + { + LowLevelList resourceInfos = GetExtractedResources(assembly); + string[] names = new string[resourceInfos.Count]; + for (int i = 0; i < resourceInfos.Count; i++) + { + names[i] = resourceInfos[i].Name; + } + return names; + } + + public sealed override Stream GetManifestResourceStream(Assembly assembly, string name) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + + + // This was most likely an embedded resource which the toolchain should have embedded + // into an assembly. + LowLevelList resourceInfos = GetExtractedResources(assembly); + for (int i = 0; i < resourceInfos.Count; i++) + { + ResourceInfo resourceInfo = resourceInfos[i]; + if (name == resourceInfo.Name) + { + return ReadResourceFromBlob(resourceInfo); + } + } + + return null; + } + + private unsafe Stream ReadResourceFromBlob(ResourceInfo resourceInfo) + { + byte* pBlob; + uint cbBlob; + + if (!resourceInfo.Module.TryFindBlob((int)ReflectionMapBlob.BlobIdResourceData, out pBlob, out cbBlob)) + { + throw new BadImageFormatException(); + } + + // resourceInfo is read from the executable image, so check it only in debug builds + Debug.Assert(resourceInfo.Index >= 0 && resourceInfo.Length >= 0 && (uint)(resourceInfo.Index + resourceInfo.Length) <= cbBlob); + return new UnmanagedMemoryStream(pBlob + resourceInfo.Index, resourceInfo.Length); + } + + private LowLevelList GetExtractedResources(Assembly assembly) + { + LowLevelDictionary> extractedResourceDictionary = this.ExtractedResourceDictionary; + string assemblyName = assembly.GetName().FullName; + LowLevelList resourceInfos; + if (!extractedResourceDictionary.TryGetValue(assemblyName, out resourceInfos)) + return new LowLevelList(); + return resourceInfos; + } + + private LowLevelDictionary> ExtractedResourceDictionary + { + get + { + if (s_extractedResourceDictionary == null) + { + // Lazily create the extracted resource dictionary. If two threads race here, we may construct two dictionaries + // and overwrite one - this is ok since the dictionaries are read-only once constructed and they contain the identical data. + + LowLevelDictionary> dict = new LowLevelDictionary>(); + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + NativeReader reader; + if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.BlobIdResourceIndex, out reader)) + { + continue; + } + NativeParser indexParser = new NativeParser(reader, 0); + NativeHashtable indexHashTable = new NativeHashtable(indexParser); + + var entryEnumerator = indexHashTable.EnumerateAllEntries(); + NativeParser entryParser; + while (!(entryParser = entryEnumerator.GetNext()).IsNull) + { + string assemblyName = entryParser.GetString(); + string resourceName = entryParser.GetString(); + int resourceOffset = (int)entryParser.GetUnsigned(); + int resourceLength = (int)entryParser.GetUnsigned(); + + ResourceInfo resourceInfo = new ResourceInfo(resourceName, resourceOffset, resourceLength, module); + + LowLevelList assemblyResources; + if (!dict.TryGetValue(assemblyName, out assemblyResources)) + { + assemblyResources = new LowLevelList(); + dict[assemblyName] = assemblyResources; + } + + assemblyResources.Add(resourceInfo); + } + } + + s_extractedResourceDictionary = dict; + } + return s_extractedResourceDictionary; + } + } + + /// + /// This dictionary gets us from assembly + resource name to the offset of a resource + /// inside the resource data blob + /// + /// The dictionary's key is a Fusion-style assembly name. + /// The dictionary's value is a list of (resourcename,index) tuples. + /// + private static volatile LowLevelDictionary> s_extractedResourceDictionary; + + private struct ResourceInfo + { + public ResourceInfo(string name, int index, int length, NativeFormatModuleInfo module) + { + Name = name; + Index = index; + Length = length; + Module = module; + } + + public string Name { get; } + public int Index { get; } + public int Length { get; } + public NativeFormatModuleInfo Module { get; } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs new file mode 100644 index 00000000000000..4439bea360a0c5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs @@ -0,0 +1,1678 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; +using global::System.Reflection; +using global::System.Collections.Generic; + +using global::Internal.Runtime.Augments; +using global::Internal.Runtime.CompilerServices; +using global::Internal.Runtime.TypeLoader; + +using global::Internal.Reflection.Core.Execution; +using global::Internal.Reflection.Execution.MethodInvokers; +using global::Internal.Reflection.Execution.FieldAccessors; + +using global::Internal.Metadata.NativeFormat; + +using global::System.Runtime.InteropServices; + +using global::Internal.Runtime; +using global::Internal.NativeFormat; + +using System.Reflection.Runtime.General; +using System.Threading; + +using CanonicalFormKind = global::Internal.TypeSystem.CanonicalFormKind; + + +using Debug = System.Diagnostics.Debug; +using ThunkKind = Internal.Runtime.TypeLoader.CallConverterThunk.ThunkKind; +using Interlocked = System.Threading.Interlocked; + +namespace Internal.Reflection.Execution +{ + //========================================================================================================== + // These ExecutionEnvironment entrypoints provide access to the NUTC-generated blob information that + // enables Reflection invoke and tie-ins to native Type artifacts. + // + // - Except when otherwise noted, ExecutionEnvironment methods use the "TryGet*" pattern rather than throwing exceptions. + // + // - All methods on this class must be multi-thread-safe. Reflection can and does invoke them on different threads with no synchronization of its own. + // + //========================================================================================================== + internal sealed partial class ExecutionEnvironmentImplementation : ExecutionEnvironment + { + private RuntimeTypeHandle GetOpenTypeDefinition(RuntimeTypeHandle typeHandle, out RuntimeTypeHandle[] typeArgumentsHandles) + { + if (RuntimeAugments.IsGenericType(typeHandle)) + { + return RuntimeAugments.GetGenericInstantiation(typeHandle, out typeArgumentsHandles); + } + + typeArgumentsHandles = null; + return typeHandle; + } + + private RuntimeTypeHandle GetTypeDefinition(RuntimeTypeHandle typeHandle) + { + if (RuntimeAugments.IsGenericType(typeHandle)) + return RuntimeAugments.GetGenericDefinition(typeHandle); + + return typeHandle; + } + + private static bool RuntimeTypeHandleIsNonDefault(RuntimeTypeHandle runtimeTypeHandle) + { + return ((IntPtr)RuntimeAugments.GetPointerFromTypeHandle(runtimeTypeHandle)) != IntPtr.Zero; + } + + private static unsafe NativeReader GetNativeReaderForBlob(NativeFormatModuleInfo module, ReflectionMapBlob blob) + { + NativeReader reader; + if (TryGetNativeReaderForBlob(module, blob, out reader)) + { + return reader; + } + + Debug.Assert(false); + return default(NativeReader); + } + + private static unsafe bool TryGetNativeReaderForBlob(NativeFormatModuleInfo module, ReflectionMapBlob blob, out NativeReader reader) + { + byte* pBlob; + uint cbBlob; + + if (module.TryFindBlob((int)blob, out pBlob, out cbBlob)) + { + reader = new NativeReader(pBlob, cbBlob); + return true; + } + + reader = default(NativeReader); + return false; + } + + /// + /// Return the metadata handle for a TypeDef if the pay-for-policy enabled this type as browsable. This is used to obtain name and other information for types + /// obtained via typeof() or Object.GetType(). This can include generic types (not to be confused with generic instances). + /// + /// Preconditions: + /// runtimeTypeHandle is a typedef (not a constructed type such as an array or generic instance.) + /// + /// Runtime handle of the type in question + /// TypeDef handle for the type + public unsafe sealed override bool TryGetMetadataForNamedType(RuntimeTypeHandle runtimeTypeHandle, out QTypeDefinition qTypeDefinition) + { + Debug.Assert(!RuntimeAugments.IsGenericType(runtimeTypeHandle)); + return TypeLoaderEnvironment.Instance.TryGetMetadataForNamedType(runtimeTypeHandle, out qTypeDefinition); + } + + // + // Return true for a TypeDef if the policy has decided this type is blocked from reflection. + // + // Preconditions: + // runtimeTypeHandle is a typedef or a generic type instance (not a constructed type such as an array) + // + public unsafe sealed override bool IsReflectionBlocked(RuntimeTypeHandle runtimeTypeHandle) + { + // For generic types, use the generic type definition + runtimeTypeHandle = GetTypeDefinition(runtimeTypeHandle); + var moduleHandle = RuntimeAugments.GetModuleFromTypeHandle(runtimeTypeHandle); + + //make sure the module is actually NativeFormatModuleInfo, if the module + //doesnt have reflection enabled it wont be a NativeFormatModuleInfo + if (!(ModuleList.Instance.TryGetModuleInfoByHandle(moduleHandle, out ModuleInfo untypedModuleInfo) && (untypedModuleInfo is NativeFormatModuleInfo module))) + { + return true; + } + + NativeReader blockedReflectionReader = GetNativeReaderForBlob(module, ReflectionMapBlob.BlockReflectionTypeMap); + NativeParser blockedReflectionParser = new NativeParser(blockedReflectionReader, 0); + NativeHashtable blockedReflectionHashtable = new NativeHashtable(blockedReflectionParser); + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + int hashcode = runtimeTypeHandle.GetHashCode(); + var lookup = blockedReflectionHashtable.Lookup(hashcode); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle entryType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!entryType.Equals(runtimeTypeHandle)) + continue; + + // Entry found, must be blocked + return true; + } + // Entry not found, must not be blocked + return false; + } + + /// + /// Return the RuntimeTypeHandle for the named type described in metadata. This is used to implement the Create and Invoke + /// apis for types. + /// + /// Preconditions: + /// metadataReader + typeDefHandle - a valid metadata reader + typeDefinitionHandle where "metadataReader" is one + /// of the metadata readers returned by ExecutionEnvironment.MetadataReaders. + /// + /// Note: Although this method has a "bool" return value like the other mapping table accessors, the Project N pay-for-play design + /// guarantees that any type enabled for metadata also has a RuntimeTypeHandle underneath. + /// + /// TypeDef handle for the type to look up + /// Runtime type handle (MethodTable) for the given type + public unsafe sealed override bool TryGetNamedTypeForMetadata(QTypeDefinition qTypeDefinition, out RuntimeTypeHandle runtimeTypeHandle) + { + return TypeLoaderEnvironment.Instance.TryGetOrCreateNamedTypeForMetadata(qTypeDefinition, out runtimeTypeHandle); + } + + /// + /// Return the metadata handle for a TypeRef if this type was referenced indirectly by other type that pay-for-play has denoted as browsable + /// (for example, as part of a method signature.) + /// + /// This is only used in "debug" builds to provide better MissingMetadataException diagnostics. + /// + /// Preconditions: + /// runtimeTypeHandle is a typedef (not a constructed type such as an array or generic instance.) + /// + /// MethodTable of the type in question + /// Metadata reader for the type + /// Located TypeRef handle + public unsafe sealed override bool TryGetTypeReferenceForNamedType(RuntimeTypeHandle runtimeTypeHandle, out MetadataReader metadataReader, out TypeReferenceHandle typeRefHandle) + { + return TypeLoaderEnvironment.TryGetTypeReferenceForNamedType(runtimeTypeHandle, out metadataReader, out typeRefHandle); + } + + /// + /// Return the RuntimeTypeHandle for the named type referenced by another type that pay-for-play denotes as browsable (for example, + /// in a member signature.) Typically, the type itself is *not* browsable (or it would have appeared in the TypeDef table.) + /// + /// This is used to ensure that we can produce a Type object if requested and that it match up with the analogous + /// Type obtained via typeof(). + /// + /// + /// Preconditions: + /// metadataReader + typeRefHandle - a valid metadata reader + typeReferenceHandle where "metadataReader" is one + /// of the metadata readers returned by ExecutionEnvironment.MetadataReaders. + /// + /// Note: Although this method has a "bool" return value like the other mapping table accessors, the Project N pay-for-play design + /// guarantees that any type that has a metadata TypeReference to it also has a RuntimeTypeHandle underneath. + /// + /// Metadata reader for module containing the type reference + /// TypeRef handle to look up + /// Resolved MethodTable for the type reference + public unsafe sealed override bool TryGetNamedTypeForTypeReference(MetadataReader metadataReader, TypeReferenceHandle typeRefHandle, out RuntimeTypeHandle runtimeTypeHandle) + { + return TypeLoaderEnvironment.TryGetNamedTypeForTypeReference(metadataReader, typeRefHandle, out runtimeTypeHandle); + } + + // + // Given a RuntimeTypeHandle for any type E, return a RuntimeTypeHandle for type E[], if the pay for play policy denotes E[] as browsable. This is used to + // implement Array.CreateInstance(). + // + // Preconditions: + // elementTypeHandle is a valid RuntimeTypeHandle. + // + // This is not equivalent to calling TryGetMultiDimTypeForElementType() with a rank of 1! + // + public unsafe sealed override bool TryGetArrayTypeForElementType(RuntimeTypeHandle elementTypeHandle, out RuntimeTypeHandle arrayTypeHandle) + { + if (RuntimeAugments.IsGenericTypeDefinition(elementTypeHandle)) + { + arrayTypeHandle = default(RuntimeTypeHandle); + return false; + } + + // For non-dynamic arrays try to look up the array type in the ArrayMap blobs; + // attempt to dynamically create a new one if that doesn't succeeed. + return TypeLoaderEnvironment.Instance.TryGetArrayTypeForElementType(elementTypeHandle, false, -1, out arrayTypeHandle); + } + + // + // Given a RuntimeTypeHandle for any array type E[], return a RuntimeTypeHandle for type E, if the pay for play policy denoted E[] as browsable. + // + // Preconditions: + // arrayTypeHandle is a valid RuntimeTypeHandle of type array. + // + // This is not equivalent to calling TryGetMultiDimTypeElementType() with a rank of 1! + // + public unsafe sealed override bool TryGetArrayTypeElementType(RuntimeTypeHandle arrayTypeHandle, out RuntimeTypeHandle elementTypeHandle) + { + elementTypeHandle = RuntimeAugments.GetRelatedParameterTypeHandle(arrayTypeHandle); + return true; + } + + + // + // Given a RuntimeTypeHandle for any type E, return a RuntimeTypeHandle for type E[,,], if the pay for policy denotes E[,,] as browsable. This is used to + // implement Type.MakeArrayType(Type, int). + // + // Preconditions: + // elementTypeHandle is a valid RuntimeTypeHandle. + // + // Calling this with rank 1 is not equivalent to calling TryGetArrayTypeForElementType()! + // + public unsafe sealed override bool TryGetMultiDimArrayTypeForElementType(RuntimeTypeHandle elementTypeHandle, int rank, out RuntimeTypeHandle arrayTypeHandle) + { + if (RuntimeAugments.IsGenericTypeDefinition(elementTypeHandle)) + { + arrayTypeHandle = default(RuntimeTypeHandle); + return false; + } + + if ((rank < MDArray.MinRank) || (rank > MDArray.MaxRank)) + { + throw new TypeLoadException(SR.Format(SR.MultiDim_Of_This_Rank_Not_Supported, rank)); + } + + return TypeLoaderEnvironment.Instance.TryGetArrayTypeForElementType(elementTypeHandle, true, rank, out arrayTypeHandle); + } + + // + // Given a RuntimeTypeHandle for any type E, return a RuntimeTypeHandle for type E*, if the pay-for-play policy denotes E* as browsable. This is used to + // ensure that "typeof(E*)" and "typeof(E).MakePointerType()" returns the same Type object. + // + // Preconditions: + // targetTypeHandle is a valid RuntimeTypeHandle. + // + public unsafe sealed override bool TryGetPointerTypeForTargetType(RuntimeTypeHandle targetTypeHandle, out RuntimeTypeHandle pointerTypeHandle) + { + return TypeLoaderEnvironment.Instance.TryGetPointerTypeForTargetType(targetTypeHandle, out pointerTypeHandle); + } + + // + // Given a RuntimeTypeHandle for any pointer type E*, return a RuntimeTypeHandle for type E, if the pay-for-play policy denotes E* as browsable. + // This is used to implement Type.GetElementType() for pointers. + // + // Preconditions: + // pointerTypeHandle is a valid RuntimeTypeHandle of type pointer. + // + public unsafe sealed override bool TryGetPointerTypeTargetType(RuntimeTypeHandle pointerTypeHandle, out RuntimeTypeHandle targetTypeHandle) + { + targetTypeHandle = RuntimeAugments.GetRelatedParameterTypeHandle(pointerTypeHandle); + return true; + } + + // + // Given a RuntimeTypeHandle for any type E, return a RuntimeTypeHandle for type E&, if the pay-for-play policy denotes E& as browsable. This is used to + // ensure that "typeof(E&)" and "typeof(E).MakeByRefType()" returns the same Type object. + // + // Preconditions: + // targetTypeHandle is a valid RuntimeTypeHandle. + // + public unsafe sealed override bool TryGetByRefTypeForTargetType(RuntimeTypeHandle targetTypeHandle, out RuntimeTypeHandle byRefTypeHandle) + { + return TypeLoaderEnvironment.Instance.TryGetByRefTypeForTargetType(targetTypeHandle, out byRefTypeHandle); + } + + // + // Given a RuntimeTypeHandle for any byref type E&, return a RuntimeTypeHandle for type E, if the pay-for-play policy denotes E& as browsable. + // This is used to implement Type.GetElementType() for byrefs. + // + // Preconditions: + // byRefTypeHandle is a valid RuntimeTypeHandle of a byref. + // + public unsafe sealed override bool TryGetByRefTypeTargetType(RuntimeTypeHandle byRefTypeHandle, out RuntimeTypeHandle targetTypeHandle) + { + targetTypeHandle = RuntimeAugments.GetRelatedParameterTypeHandle(byRefTypeHandle); + return true; + } + + // + // Given a RuntimeTypeHandle for a generic type G and a set of RuntimeTypeHandles T1, T2.., return the RuntimeTypeHandle for the generic + // instance G if the pay-for-play policy denotes G as browsable. This is used to implement Type.MakeGenericType(). + // + // Preconditions: + // runtimeTypeDefinitionHandle is a valid RuntimeTypeHandle for a generic type. + // genericTypeArgumentHandles is an array of valid RuntimeTypeHandles. + // + public unsafe sealed override bool TryGetConstructedGenericTypeForComponents(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle) + { + if (TypeLoaderEnvironment.Instance.TryLookupConstructedGenericTypeForComponents(genericTypeDefinitionHandle, genericTypeArgumentHandles, out runtimeTypeHandle)) + { + return true; + } + + TypeInfo typeDefinition = Type.GetTypeFromHandle(genericTypeDefinitionHandle).GetTypeInfo(); + + TypeInfo[] typeArguments = new TypeInfo[genericTypeArgumentHandles.Length]; + for (int i = 0; i < genericTypeArgumentHandles.Length; i++) + { + // Early out if one of the arguments is a generic definition. + // The reflection stack will use this to construct a Type that doesn't have a type handle. + // Note: this is different from the validation we do in EnsureSatisfiesClassConstraints because this + // should not throw. + if (RuntimeAugments.IsGenericTypeDefinition(genericTypeArgumentHandles[i])) + return false; + + typeArguments[i] = Type.GetTypeFromHandle(genericTypeArgumentHandles[i]).GetTypeInfo(); + } + + ConstraintValidator.EnsureSatisfiesClassConstraints(typeDefinition, typeArguments); + + return TypeLoaderEnvironment.Instance.TryGetConstructedGenericTypeForComponents(genericTypeDefinitionHandle, genericTypeArgumentHandles, out runtimeTypeHandle); + } + + public sealed override MethodInvoker TryGetMethodInvoker(RuntimeTypeHandle declaringTypeHandle, QMethodDefinition methodHandle, RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + MethodBase methodInfo = ReflectionCoreExecution.ExecutionDomain.GetMethod(declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles); + + // Validate constraints first. This is potentially useless work if the method already exists, but it prevents bad + // inputs to reach the type loader (we don't have support to e.g. represent pointer types within the type loader) + if (genericMethodTypeArgumentHandles != null && genericMethodTypeArgumentHandles.Length > 0) + ConstraintValidator.EnsureSatisfiesClassConstraints((MethodInfo)methodInfo); + + MethodSignatureComparer methodSignatureComparer = new MethodSignatureComparer(methodHandle); + + MethodInvokeInfo methodInvokeInfo; +#if GENERICS_FORCE_USG + // Stress mode to force the usage of universal canonical method targets for reflection invokes. + // It is recommended to use "/SharedGenericsMode GenerateAllUniversalGenerics" NUTC command line argument when + // compiling the application in order to effectively use the GENERICS_FORCE_USG mode. + + // If we are just trying to invoke a non-generic method on a non-generic type, we won't force the universal lookup + if (!RuntimeAugments.IsGenericType(declaringTypeHandle) && (genericMethodTypeArgumentHandles == null || genericMethodTypeArgumentHandles.Length == 0)) + methodInvokeInfo = TryGetMethodInvokeInfo(declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles, + methodInfo, ref methodSignatureComparer, CanonicalFormKind.Specific); + else + methodInvokeInfo = TryGetMethodInvokeInfo(declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles, + methodInfo, ref methodSignatureComparer, CanonicalFormKind.Universal); +#else + methodInvokeInfo = TryGetMethodInvokeInfo(declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles, + methodInfo, ref methodSignatureComparer, CanonicalFormKind.Specific); + + // If we failed to get a MethodInvokeInfo for an exact method, or a canonically equivalent method, check if there is a universal canonically + // equivalent entry that could be used (it will be much slower, and require a calling convention converter) + if (methodInvokeInfo == null) + methodInvokeInfo = TryGetMethodInvokeInfo(declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles, + methodInfo, ref methodSignatureComparer, CanonicalFormKind.Universal); +#endif + + if (methodInvokeInfo == null) + return null; + + return MethodInvokerWithMethodInvokeInfo.CreateMethodInvoker(declaringTypeHandle, methodHandle, methodInvokeInfo); + } + + // Get the pointers necessary to call a dynamic method invocation function + // + // This is either a function pointer to call, or a function pointer and template token. + private unsafe void GetDynamicMethodInvokeMethodInfo(NativeFormatModuleInfo module, uint cookie, RuntimeTypeHandle[] argHandles, + out IntPtr dynamicInvokeMethod, out IntPtr dynamicInvokeMethodGenericDictionary) + { + if ((cookie & 1) == 1) + { + // If the dynamic invoke method is a generic method, we need to consult the DynamicInvokeTemplateData table to locate + // the matching template so that we can instantiate it. The DynamicInvokeTemplateData table starts with a single UINT + // with the RVA of the type that hosts all DynamicInvoke methods. The table then follows with list of [Token, FunctionPointer] + // pairs. The cookie parameter is an index into this table and points to a single pair. + byte* pBlobAsBytes; + uint cbBlob; + bool success = module.TryFindBlob((int)ReflectionMapBlob.DynamicInvokeTemplateData, out pBlobAsBytes, out cbBlob); + Debug.Assert(success && cbBlob > 4); + + byte* pNativeLayoutInfoBlob; + uint cbNativeLayoutInfoBlob; + success = module.TryFindBlob((int)ReflectionMapBlob.NativeLayoutInfo, out pNativeLayoutInfoBlob, out cbNativeLayoutInfoBlob); + Debug.Assert(success); + + RuntimeTypeHandle declaringTypeHandle; + // All methods referred from this blob are contained in the same type. The first UINT in the blob is a reloc to that MethodTable + if (RuntimeAugments.SupportsRelativePointers) + { + // CoreRT uses 32bit relative relocs + declaringTypeHandle = RuntimeAugments.CreateRuntimeTypeHandle((IntPtr)(pBlobAsBytes + *(int*)pBlobAsBytes)); + } + else + { + declaringTypeHandle = RuntimeAugments.CreateRuntimeTypeHandle(*(IntPtr*)pBlobAsBytes); + } + + // The index points to two entries: the token of the dynamic invoke method and the function pointer to the canonical method + // Now have the type loader build or locate a dictionary for this method + uint index = cookie >> 1; + + MethodNameAndSignature nameAndSignature; + RuntimeSignature nameAndSigSignature; + + if (RuntimeAugments.SupportsRelativePointers) + { + nameAndSigSignature = RuntimeSignature.CreateFromNativeLayoutSignature(module.Handle, ((uint*)pBlobAsBytes)[index]); + } + else + { + nameAndSigSignature = RuntimeSignature.CreateFromNativeLayoutSignature(module.Handle, (uint)((IntPtr*)pBlobAsBytes)[index]); + } + + success = TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutSignature(nameAndSigSignature, out nameAndSignature); + Debug.Assert(success); + + success = TypeLoaderEnvironment.Instance.TryGetGenericMethodDictionaryForComponents(declaringTypeHandle, argHandles, nameAndSignature, out dynamicInvokeMethodGenericDictionary); + Debug.Assert(success); + + if (RuntimeAugments.SupportsRelativePointers) + { + // CoreRT uses 32bit relative relocs + int* pRelPtr32 = &((int*)pBlobAsBytes)[index + 1]; + dynamicInvokeMethod = (IntPtr)((byte*)pRelPtr32 + *pRelPtr32); + } + else + { + dynamicInvokeMethod = ((IntPtr*)pBlobAsBytes)[index + 1]; + } + } + else + { + // Nongeneric DynamicInvoke method. This is used to DynamicInvoke methods that have parameters that + // cannot be shared (or if there are no parameters to begin with). + ExternalReferencesTable extRefs = default(ExternalReferencesTable); + extRefs.InitializeCommonFixupsTable(module); + + dynamicInvokeMethod = extRefs.GetFunctionPointerFromIndex(cookie >> 1); + dynamicInvokeMethodGenericDictionary = IntPtr.Zero; + } + } + + private IntPtr GetDynamicMethodInvokerThunk(MethodBase methodInfo) + { + MethodParametersInfo methodParamsInfo = new MethodParametersInfo(methodInfo); + return CallConverterThunk.MakeThunk( + ThunkKind.ReflectionDynamicInvokeThunk, + IntPtr.Zero, + IntPtr.Zero, + false, + methodParamsInfo.ReturnTypeAndParameterTypeHandles.ToArray(), + methodParamsInfo.ReturnTypeAndParametersByRefFlags, + null); + } + + private RuntimeTypeHandle[] GetDynamicInvokeInstantiationArguments(MethodBase reflectionMethodBase) + { + // The DynamicInvoke method is a generic method with arguments that match the arguments of the target method. + // Prepare the list of arguments so that we can use it to instantiate the method. + + MethodParametersInfo methodParamsInfo = new MethodParametersInfo(reflectionMethodBase); + LowLevelList dynamicInvokeMethodGenArguments = methodParamsInfo.ParameterTypeHandles; + + // This is either a constructor ("returns" void) or an instance method + MethodInfo reflectionMethodInfo = reflectionMethodBase as MethodInfo; + + // Only use the return type if it's not void + if (reflectionMethodInfo != null && reflectionMethodInfo.ReturnType != typeof(void)) + dynamicInvokeMethodGenArguments.Add(methodParamsInfo.ReturnTypeHandle); + + for (int i = 0; i < dynamicInvokeMethodGenArguments.Count; i++) + { + // We can't instantiate over pointer types, so the DynamicInvoke method compensates for it already. + RuntimeTypeHandle type = dynamicInvokeMethodGenArguments[i]; + while (RuntimeAugments.IsUnmanagedPointerType(type)) + { + type = RuntimeAugments.GetRelatedParameterTypeHandle(type); + } + dynamicInvokeMethodGenArguments[i] = type; + } + + return dynamicInvokeMethodGenArguments.ToArray(); + } + + private static RuntimeTypeHandle[] GetTypeSequence(ref ExternalReferencesTable extRefs, ref NativeParser parser) + { + uint count = parser.GetUnsigned(); + RuntimeTypeHandle[] result = new RuntimeTypeHandle[count]; + for (uint i = 0; i < count; i++) + { + result[i] = extRefs.GetRuntimeTypeHandleFromIndex(parser.GetUnsigned()); + } + return result; + } + + private IntPtr TryGetVirtualResolveData(NativeFormatModuleInfo module, + RuntimeTypeHandle methodHandleDeclaringType, QMethodDefinition methodHandle, RuntimeTypeHandle[] genericArgs, + ref MethodSignatureComparer methodSignatureComparer) + { + TypeLoaderEnvironment.VirtualResolveDataResult lookupResult; + bool success = TypeLoaderEnvironment.TryGetVirtualResolveData(module, methodHandleDeclaringType, genericArgs, ref methodSignatureComparer, out lookupResult); + if (!success) + return IntPtr.Zero; + else + { + GCHandle reader = Internal.TypeSystem.LockFreeObjectInterner.GetInternedObjectHandle(methodHandle.Reader); + + if (lookupResult.IsGVM) + { + return (new OpenMethodResolver(lookupResult.DeclaringInvokeType, lookupResult.GVMHandle, reader, methodHandle.Token)).ToIntPtr(); + } + else + { + return (new OpenMethodResolver(lookupResult.DeclaringInvokeType, lookupResult.SlotIndex, reader, methodHandle.Token)).ToIntPtr(); + } + } + } + + /// + /// Try to look up method invoke info in metadata for all registered modules, construct + /// the calling convention converter as appropriate and fill in MethodInvokeInfo. + /// + /// Runtime handle of declaring type for the method + /// Handle of method to look up + /// Runtime handles of generic method arguments + /// MethodInfo of method to look up + /// Helper structure used for comparing signatures + /// Requested canon form + /// Constructed method invoke info, null on failure + private unsafe MethodInvokeInfo TryGetMethodInvokeInfo( + RuntimeTypeHandle declaringTypeHandle, + QMethodDefinition methodHandle, + RuntimeTypeHandle[] genericMethodTypeArgumentHandles, + MethodBase methodInfo, + ref MethodSignatureComparer methodSignatureComparer, + CanonicalFormKind canonFormKind) + { + MethodInvokeMetadata methodInvokeMetadata; + + if (!TypeLoaderEnvironment.TryGetMethodInvokeMetadata( + declaringTypeHandle, + methodHandle, + genericMethodTypeArgumentHandles, + ref methodSignatureComparer, + canonFormKind, + out methodInvokeMetadata)) + { + // Method invoke info not found + return null; + } + + if ((methodInvokeMetadata.InvokeTableFlags & InvokeTableFlags.CallingConventionMask) != 0) + { + // MethodInvokeInfo found, but it references a method with a native calling convention. + return null; + } + + if ((methodInvokeMetadata.InvokeTableFlags & InvokeTableFlags.IsUniversalCanonicalEntry) != 0) + { + // Wrap the method entry point in a calling convention converter thunk if it's a universal canonical implementation + Debug.Assert(canonFormKind == CanonicalFormKind.Universal); + methodInvokeMetadata.MethodEntryPoint = GetCallingConventionConverterForMethodEntrypoint( + methodHandle.NativeFormatReader, + declaringTypeHandle, + methodInvokeMetadata.MethodEntryPoint, + methodInvokeMetadata.DictionaryComponent, + methodInfo, + methodHandle.NativeFormatHandle); + } + + IntPtr dynamicInvokeMethod; + IntPtr dynamicInvokeMethodGenericDictionary; + if ((methodInvokeMetadata.InvokeTableFlags & InvokeTableFlags.NeedsParameterInterpretation) != 0) + { + dynamicInvokeMethod = GetDynamicMethodInvokerThunk(methodInfo); + dynamicInvokeMethodGenericDictionary = IntPtr.Zero; + } + else + { + RuntimeTypeHandle[] dynInvokeMethodArgs = GetDynamicInvokeInstantiationArguments(methodInfo); + + GetDynamicMethodInvokeMethodInfo( + methodInvokeMetadata.MappingTableModule, + methodInvokeMetadata.DynamicInvokeCookie, + dynInvokeMethodArgs, + out dynamicInvokeMethod, + out dynamicInvokeMethodGenericDictionary); + } + + IntPtr resolver = IntPtr.Zero; + if ((methodInvokeMetadata.InvokeTableFlags & InvokeTableFlags.HasVirtualInvoke) != 0) + { + resolver = TryGetVirtualResolveData(ModuleList.Instance.GetModuleInfoForMetadataReader(methodHandle.NativeFormatReader), + declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles, + ref methodSignatureComparer); + + // Unable to find virtual resolution information, cannot return valid MethodInvokeInfo + if (resolver == IntPtr.Zero) + return null; + } + + var methodInvokeInfo = new MethodInvokeInfo + { + LdFtnResult = methodInvokeMetadata.MethodEntryPoint, + DynamicInvokeMethod = dynamicInvokeMethod, + DynamicInvokeGenericDictionary = dynamicInvokeMethodGenericDictionary, + MethodInfo = methodInfo, + VirtualResolveData = resolver, + }; + return methodInvokeInfo; + } + + private static IntPtr GetCallingConventionConverterForMethodEntrypoint(MetadataReader metadataReader, RuntimeTypeHandle declaringType, IntPtr methodEntrypoint, IntPtr dictionary, MethodBase methodBase, MethodHandle mdHandle) + { + MethodParametersInfo methodParamsInfo = new MethodParametersInfo(metadataReader, methodBase, mdHandle); + + bool[] forcedByRefParameters; + if (methodParamsInfo.RequiresCallingConventionConverter(out forcedByRefParameters)) + { + RuntimeTypeHandle[] parameterTypeHandles = methodParamsInfo.ReturnTypeAndParameterTypeHandles.ToArray(); + bool[] byRefParameters = methodParamsInfo.ReturnTypeAndParametersByRefFlags; + + Debug.Assert(parameterTypeHandles.Length == byRefParameters.Length && byRefParameters.Length == forcedByRefParameters.Length); + + ThunkKind thunkKind; + if (methodBase.IsGenericMethod) + { + thunkKind = CallConverterThunk.ThunkKind.StandardToGenericInstantiating; + } + else if (RuntimeAugments.IsValueType(declaringType)) + { + // Unboxing instantiating stub + if (dictionary == IntPtr.Zero) + { + Debug.Assert(!methodBase.IsStatic); + thunkKind = CallConverterThunk.ThunkKind.StandardToGeneric; + } + else + thunkKind = CallConverterThunk.ThunkKind.StandardToGenericInstantiating; + } + else + { + thunkKind = CallConverterThunk.ThunkKind.StandardToGenericInstantiatingIfNotHasThis; + } + + return CallConverterThunk.MakeThunk( + thunkKind, + methodEntrypoint, + dictionary, + !methodBase.IsStatic, + parameterTypeHandles, + byRefParameters, + forcedByRefParameters); + } + else + { + if (dictionary == IntPtr.Zero) + return methodEntrypoint; + else + return FunctionPointerOps.GetGenericMethodFunctionPointer(methodEntrypoint, dictionary); + } + } + + private RuntimeTypeHandle GetExactDeclaringType(RuntimeTypeHandle dstType, RuntimeTypeHandle srcType) + { + // The fact that for generic types we rely solely on the template type in the mapping table causes + // trouble for lookups from method pointer to the declaring type and method metadata handle. + + // Suppose we have following code: + // class Base { void Frob() { } } + // class Derived : Base { } + // Let's pick Base, Derived as the template. + // Now if someone calls TryGetMethodForOriginalLdFtnResult with a pointer to the Frob method and a RuntimeTypeHandle + // of the Derived object instance, we are expected to return the metadata handle for Frob with *Base* + // as the declaring type. The table obviously only has an entry for Frob with Base. + + // This method needs to return "true" and "Base" for cases like this. + + RuntimeTypeHandle dstTypeDef = GetTypeDefinition(dstType); + + while (!srcType.IsNull()) + { + if (RuntimeAugments.IsAssignableFrom(dstType, srcType)) + { + return dstType; + } + + if (!dstTypeDef.IsNull() && RuntimeAugments.IsGenericType(srcType)) + { + RuntimeTypeHandle srcTypeDef = GetTypeDefinition(srcType);; + + // Compare TypeDefs. We don't look at the generic components. We already know that the right type + // to return must be somewhere in the inheritance chain. + if (dstTypeDef.Equals(srcTypeDef)) + { + // Return the *other* type handle since dstType is instantiated over different arguments + return srcType; + } + } + + if (!RuntimeAugments.TryGetBaseType(srcType, out srcType)) + { + break; + } + } + + Debug.Assert(false); + return default(RuntimeTypeHandle); + } + + private struct FunctionPointerOffsetPair : IComparable + { + public FunctionPointerOffsetPair(IntPtr functionPointer, uint offset) + { + FunctionPointer = functionPointer; + Offset = offset; + } + + public int CompareTo(FunctionPointerOffsetPair other) + { + unsafe + { + void* fptr = FunctionPointer.ToPointer(); + void* otherFptr = other.FunctionPointer.ToPointer(); + + if (fptr < otherFptr) + return -1; + else if (fptr == otherFptr) + return Offset.CompareTo(other.Offset); + else + return 1; + } + } + + public readonly IntPtr FunctionPointer; + public readonly uint Offset; + } + + private struct FunctionPointersToOffsets + { + public FunctionPointerOffsetPair[] Data; + + public bool TryGetOffsetsRange(IntPtr functionPointer, out int firstParserOffsetIndex, out int lastParserOffsetIndex) + { + firstParserOffsetIndex = -1; + lastParserOffsetIndex = -1; + + if (Data == null) + return false; + + int binarySearchIndex = Array.BinarySearch(Data, new FunctionPointerOffsetPair(functionPointer, 0)); + + // Array.BinarySearch will return either a positive number which is the first index in a range + // or a negative number which is the bitwise complement of the start of the range + // or a negative number which doesn't correspond to the range at all. + if (binarySearchIndex < 0) + binarySearchIndex = ~binarySearchIndex; + + if (binarySearchIndex >= Data.Length || Data[binarySearchIndex].FunctionPointer != functionPointer) + return false; + + // binarySearchIndex now contains the index of the start of a range of matching function pointers and offsets + firstParserOffsetIndex = binarySearchIndex; + lastParserOffsetIndex = binarySearchIndex; + while ((lastParserOffsetIndex < (Data.Length - 1)) && Data[lastParserOffsetIndex + 1].FunctionPointer == functionPointer) + { + lastParserOffsetIndex++; + } + return true; + } + } + + // ldftn reverse lookup hash. Must be cleared and reset if the module list changes. (All sets to + // this variable must happen under a lock) + private volatile KeyValuePair[] _ldftnReverseLookup_InvokeMap; + private volatile KeyValuePair[] _ldftnReverseLookup_ExactInstantiations; + private Func _computeLdFtnLookupInvokeMapInvokeMap = ComputeLdftnReverseLookup_InvokeMap; + private Func _computeLdFtnLookupExactInstantiations = ComputeLdftnReverseLookup_ExactInstantiations; + + /// + /// Initialize a lookup array of module to function pointer/parser offset pair arrays. Do so in a manner that will allow + /// future work which will invalidate the cache (by setting it to null) + /// + /// pointer to static which holds cache value. This is treated as a volatile variable + /// + /// + private KeyValuePair[] GetLdFtnReverseLookups_Helper(ref KeyValuePair[] ldftnReverseLookupStatic, Func lookupComputer) + { + KeyValuePair[] ldFtnReverseLookup = Volatile.Read(ref ldftnReverseLookupStatic); + + if (ldFtnReverseLookup != null) + return ldFtnReverseLookup; + else + { + lock (this) + { + ldFtnReverseLookup = Volatile.Read(ref ldftnReverseLookupStatic); + + // double checked lock, safe due to use of volatile on s_ldftnReverseHashes + if (ldFtnReverseLookup != null) + return ldFtnReverseLookup; + + // FUTURE: add a module load callback to invalidate this cache if a new module is loaded. + while (true) + { + int size = 0; + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + size++; + } + + ldFtnReverseLookup = new KeyValuePair[size]; + int index = 0; + bool restart = false; + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + // If the module list changes during execution of this code, rebuild from scratch + if (index >= ldFtnReverseLookup.Length) + { + restart = true; + break; + } + + ldFtnReverseLookup[index] = new KeyValuePair(module, lookupComputer(module)); + index++; + } + + if (restart) + continue; + + // unless we need to repeat the module enumeration, only execute the body of this while loop once. + break; + } + + Volatile.Write(ref ldftnReverseLookupStatic, ldFtnReverseLookup); + return ldFtnReverseLookup; + } + } + } + + private KeyValuePair[] GetLdFtnReverseLookups_InvokeMap() + { +#pragma warning disable 0420 // GetLdFtnReverseLookups_Helper treats its first parameter as volatile by using explicit Volatile operations + return GetLdFtnReverseLookups_Helper(ref _ldftnReverseLookup_InvokeMap, _computeLdFtnLookupInvokeMapInvokeMap); +#pragma warning restore 0420 + } + + private KeyValuePair[] GetLdFtnReverseLookups_ExactInstantations() + { +#pragma warning disable 0420 // GetLdFtnReverseLookups_Helper treats its first parameter as volatile by using explicit Volatile operations + return GetLdFtnReverseLookups_Helper(ref _ldftnReverseLookup_ExactInstantiations, _computeLdFtnLookupExactInstantiations); +#pragma warning restore 0420 + } + + internal unsafe void GetFunctionPointerAndInstantiationArgumentForOriginalLdFtnResult(IntPtr originalLdFtnResult, out IntPtr canonOriginalLdFtnResult, out IntPtr instantiationArgument) + { + if (FunctionPointerOps.IsGenericMethodPointer(originalLdFtnResult)) + { + GenericMethodDescriptor* realTargetData = FunctionPointerOps.ConvertToGenericDescriptor(originalLdFtnResult); + canonOriginalLdFtnResult = RuntimeAugments.GetCodeTarget(realTargetData->MethodFunctionPointer); + instantiationArgument = realTargetData->InstantiationArgument; + } + else + { + // The thunk could have been created by the TypeLoader as a dictionary slot for USG code + if (!CallConverterThunk.TryGetCallConversionTargetPointerAndInstantiatingArg(originalLdFtnResult, out canonOriginalLdFtnResult, out instantiationArgument)) + { + canonOriginalLdFtnResult = RuntimeAugments.GetCodeTarget(originalLdFtnResult); + instantiationArgument = IntPtr.Zero; + } + } + } + + internal bool TryGetMethodForOriginalLdFtnResult(IntPtr originalLdFtnResult, ref RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + GetFunctionPointerAndInstantiationArgumentForOriginalLdFtnResult(originalLdFtnResult, out IntPtr canonOriginalLdFtnResult, out IntPtr instantiationArgument); + + if (instantiationArgument != IntPtr.Zero) + { + // Search TemplateMethodMap + if (TryGetMethodForOriginalLdFtnResult_GenericMethodWithInstantiationArgument(instantiationArgument, ref declaringTypeHandle, out methodHandle, out genericMethodTypeArgumentHandles)) + return true; + } + else + { + // Search ExactInstantiationsMap + foreach (KeyValuePair perModuleLookup in GetLdFtnReverseLookups_ExactInstantations()) + { + int startIndex; + int endIndex; + + if (perModuleLookup.Value.TryGetOffsetsRange(canonOriginalLdFtnResult, out startIndex, out endIndex)) + { + for (int curIndex = startIndex; curIndex <= endIndex; curIndex++) + { + uint parserOffset = perModuleLookup.Value.Data[curIndex].Offset; + if (TryGetMethodForOriginalLdFtnResult_ExactInstantiation_Inner(perModuleLookup.Key, forStartAddress: false, canonOriginalLdFtnResult, parserOffset, + ref declaringTypeHandle, out methodHandle, out genericMethodTypeArgumentHandles)) + return true; + } + } + } + } + + // Search InvokeMap + foreach (KeyValuePair perModuleLookup in GetLdFtnReverseLookups_InvokeMap()) + { + int startIndex; + int endIndex; + + if (perModuleLookup.Value.TryGetOffsetsRange(canonOriginalLdFtnResult, out startIndex, out endIndex)) + { + for (int curIndex = startIndex; curIndex <= endIndex; curIndex++) + { + uint parserOffset = perModuleLookup.Value.Data[curIndex].Offset; + if (TryGetMethodForOriginalLdFtnResult_InvokeMap_Inner(perModuleLookup.Key, forStartAddress: false, canonOriginalLdFtnResult, instantiationArgument, parserOffset, ref declaringTypeHandle, out methodHandle, out genericMethodTypeArgumentHandles)) + return true; + } + } + } + + methodHandle = default(QMethodDefinition); + genericMethodTypeArgumentHandles = null; + return false; + } + + internal bool TryGetMethodForStartAddress(IntPtr methodStartAddress, ref RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle) + { + // Search ExactInstantiationsMap + foreach (KeyValuePair perModuleLookup in GetLdFtnReverseLookups_ExactInstantations()) + { + int startIndex; + int endIndex; + + if (perModuleLookup.Value.TryGetOffsetsRange(methodStartAddress, out startIndex, out endIndex)) + { + for (int curIndex = startIndex; curIndex <= endIndex; curIndex++) + { + uint parserOffset = perModuleLookup.Value.Data[curIndex].Offset; + if (TryGetMethodForOriginalLdFtnResult_ExactInstantiation_Inner(perModuleLookup.Key, forStartAddress: true, methodStartAddress, parserOffset, ref declaringTypeHandle, out methodHandle, out _)) + { + if (RuntimeAugments.IsGenericType(declaringTypeHandle)) + declaringTypeHandle = RuntimeAugments.GetGenericDefinition(declaringTypeHandle); + return true; + } + } + } + } + + // Search InvokeMap + foreach (KeyValuePair perModuleLookup in GetLdFtnReverseLookups_InvokeMap()) + { + int startIndex; + int endIndex; + + if (perModuleLookup.Value.TryGetOffsetsRange(methodStartAddress, out startIndex, out endIndex)) + { + for (int curIndex = startIndex; curIndex <= endIndex; curIndex++) + { + uint parserOffset = perModuleLookup.Value.Data[curIndex].Offset; + if (TryGetMethodForOriginalLdFtnResult_InvokeMap_Inner(perModuleLookup.Key, forStartAddress: true, methodStartAddress, IntPtr.Zero, parserOffset, ref declaringTypeHandle, out methodHandle, out _)) + { + if (RuntimeAugments.IsGenericType(declaringTypeHandle)) + declaringTypeHandle = RuntimeAugments.GetGenericDefinition(declaringTypeHandle); + return true; + } + } + } + } + + methodHandle = default(QMethodDefinition); + return false; + } + + private static FunctionPointersToOffsets ComputeLdftnReverseLookup_InvokeMap(NativeFormatModuleInfo mappingTableModule) + { + FunctionPointersToOffsets functionPointerToOffsetInInvokeMap = new FunctionPointersToOffsets(); + + NativeReader invokeMapReader; + if (!TryGetNativeReaderForBlob(mappingTableModule, ReflectionMapBlob.InvokeMap, out invokeMapReader)) + { + return functionPointerToOffsetInInvokeMap; + } + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(mappingTableModule); + + NativeParser invokeMapParser = new NativeParser(invokeMapReader, 0); + NativeHashtable invokeHashtable = new NativeHashtable(invokeMapParser); + + LowLevelList functionPointers = new LowLevelList(); + + var lookup = invokeHashtable.EnumerateAllEntries(); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + uint parserOffset = entryParser.Offset; + Debug.Assert(entryParser.Reader == invokeMapParser.Reader); + + InvokeTableFlags entryFlags = (InvokeTableFlags)entryParser.GetUnsigned(); + + bool hasEntrypoint = ((entryFlags & InvokeTableFlags.HasEntrypoint) != 0); + if (!hasEntrypoint) + continue; + + uint entryMethodHandleOrNameAndSigRaw = entryParser.GetUnsigned(); + uint entryDeclaringTypeRaw = entryParser.GetUnsigned(); + + IntPtr entryMethodEntrypoint = externalReferences.GetFunctionPointerFromIndex(entryParser.GetUnsigned()); + functionPointers.Add(new FunctionPointerOffsetPair(entryMethodEntrypoint, parserOffset)); + + // Add resolved stub targets to the reverse LdFtn lookup map for the purpose of reflection-based + // stack trace resolution - the reverse LdFtn lookup internally used by the reflection + // method resolution will work off an IP address on the stack which is an address + // within the actual method, not the stub. + IntPtr targetAddress = RuntimeAugments.GetCodeTarget(entryMethodEntrypoint); + if (targetAddress != IntPtr.Zero && targetAddress != entryMethodEntrypoint) + { + functionPointers.Add(new FunctionPointerOffsetPair(targetAddress, parserOffset)); + } + IntPtr targetAddress2; + if (TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(entryMethodEntrypoint, out targetAddress2) && + targetAddress2 != entryMethodEntrypoint && + targetAddress2 != targetAddress) + { + functionPointers.Add(new FunctionPointerOffsetPair(targetAddress2, parserOffset)); + } + } + + functionPointerToOffsetInInvokeMap.Data = functionPointers.ToArray(); + Array.Sort(functionPointerToOffsetInInvokeMap.Data); + + return functionPointerToOffsetInInvokeMap; + } + + private unsafe bool TryGetMethodForOriginalLdFtnResult_InvokeMap_Inner(NativeFormatModuleInfo mappingTableModule, bool forStartAddress, IntPtr canonOriginalLdFtnResult, IntPtr instantiationArgument, uint parserOffset, ref RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + methodHandle = default(QMethodDefinition); + genericMethodTypeArgumentHandles = null; + + NativeReader invokeMapReader; + if (!TryGetNativeReaderForBlob(mappingTableModule, ReflectionMapBlob.InvokeMap, out invokeMapReader)) + { + // This should have succeeded otherwise, how did we get a parser offset as an input parameter? + Debug.Assert(false); + return false; + } + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(mappingTableModule); + + NativeParser entryParser = new NativeParser(invokeMapReader, parserOffset); + + InvokeTableFlags entryFlags = (InvokeTableFlags)entryParser.GetUnsigned(); + + // If the passed in method was a fat function pointer, but the entry in the mapping table doesn't need + // an instantiation argument (or the other way around), trivially reject it. + if (!forStartAddress && ((instantiationArgument == IntPtr.Zero) != ((entryFlags & InvokeTableFlags.RequiresInstArg) == 0))) + return false; + + Debug.Assert((entryFlags & InvokeTableFlags.HasEntrypoint) != 0); + + uint entryMethodHandleOrNameAndSigRaw = entryParser.GetUnsigned(); + uint entryDeclaringTypeRaw = entryParser.GetUnsigned(); + + IntPtr entryMethodEntrypoint = externalReferences.GetFunctionPointerFromIndex(entryParser.GetUnsigned()); + + if ((entryFlags & InvokeTableFlags.NeedsParameterInterpretation) == 0) + entryParser.SkipInteger(); // skip dynamic invoke cookie + + if (forStartAddress) + { + declaringTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryDeclaringTypeRaw); + } + else + { +#if DEBUG + IntPtr targetAddress; + Debug.Assert(entryMethodEntrypoint == canonOriginalLdFtnResult || + RuntimeAugments.GetCodeTarget(entryMethodEntrypoint) == canonOriginalLdFtnResult || + TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(entryMethodEntrypoint, out targetAddress) && + targetAddress == canonOriginalLdFtnResult); +#endif + + if ((entryFlags & InvokeTableFlags.RequiresInstArg) == 0 && declaringTypeHandle.IsNull()) + declaringTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryDeclaringTypeRaw); + + if ((entryFlags & InvokeTableFlags.IsGenericMethod) != 0) + { + if ((entryFlags & InvokeTableFlags.RequiresInstArg) != 0) + { + MethodNameAndSignature dummyNameAndSignature; + bool success = TypeLoaderEnvironment.Instance.TryGetGenericMethodComponents(instantiationArgument, out declaringTypeHandle, out dummyNameAndSignature, out genericMethodTypeArgumentHandles); + Debug.Assert(success); + } + else + genericMethodTypeArgumentHandles = GetTypeSequence(ref externalReferences, ref entryParser); + } + else + { + genericMethodTypeArgumentHandles = null; + if ((entryFlags & InvokeTableFlags.RequiresInstArg) != 0) + declaringTypeHandle = RuntimeAugments.CreateRuntimeTypeHandle(instantiationArgument); + } + + RuntimeTypeHandle entryType = externalReferences.GetRuntimeTypeHandleFromIndex(entryDeclaringTypeRaw); + declaringTypeHandle = GetExactDeclaringType(entryType, declaringTypeHandle); + } + + if ((entryFlags & InvokeTableFlags.HasMetadataHandle) != 0) + { + RuntimeTypeHandle declaringTypeHandleDefinition = GetTypeDefinition(declaringTypeHandle); + QTypeDefinition qTypeDefinition; + if (!TryGetMetadataForNamedType(declaringTypeHandleDefinition, out qTypeDefinition)) + { + RuntimeExceptionHelpers.FailFast("Unable to resolve named type to having a metadata reader"); + } + + MethodHandle nativeFormatMethodHandle = + (((int)HandleType.Method << 24) | (int)entryMethodHandleOrNameAndSigRaw).AsMethodHandle(); + + methodHandle = new QMethodDefinition(qTypeDefinition.NativeFormatReader, nativeFormatMethodHandle); + } + else + { + uint nameAndSigOffset = entryMethodHandleOrNameAndSigRaw; + MethodNameAndSignature nameAndSig; + if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(mappingTableModule.Handle, nameAndSigOffset, out nameAndSig)) + { + Debug.Assert(false); + return false; + } + + if (!TypeLoaderEnvironment.Instance.TryGetMetadataForTypeMethodNameAndSignature(declaringTypeHandle, nameAndSig, out methodHandle)) + { + Debug.Assert(false); + return false; + } + } + + return true; + } + + private static FunctionPointersToOffsets ComputeLdftnReverseLookup_ExactInstantiations(NativeFormatModuleInfo mappingTableModule) + { + FunctionPointersToOffsets functionPointerToOffsetInInvokeMap = new FunctionPointersToOffsets(); + + NativeReader methodTemplateMapReader; + if (!TryGetNativeReaderForBlob(mappingTableModule, ReflectionMapBlob.ExactMethodInstantiationsHashtable, out methodTemplateMapReader)) + { + return functionPointerToOffsetInInvokeMap; + } + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeNativeReferences(mappingTableModule); + + NativeParser methodTemplateMapParser = new NativeParser(methodTemplateMapReader, 0); + NativeHashtable invokeHashtable = new NativeHashtable(methodTemplateMapParser); + + LowLevelList functionPointers = new LowLevelList(); + + var lookup = invokeHashtable.EnumerateAllEntries(); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + uint parserOffset = entryParser.Offset; + + // Declaring Handle + entryParser.SkipInteger(); + + // NameAndSig + entryParser.SkipInteger(); + + // generic method arity + int parsedArity = (int)entryParser.GetSequenceCount(); + + for (int i = 0; i < parsedArity; i++) + { + entryParser.SkipInteger(); + } + + IntPtr functionPointer = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + functionPointers.Add(new FunctionPointerOffsetPair(functionPointer, parserOffset)); + } + + functionPointerToOffsetInInvokeMap.Data = functionPointers.ToArray(); + Array.Sort(functionPointerToOffsetInInvokeMap.Data); + + return functionPointerToOffsetInInvokeMap; + } + + private unsafe bool TryGetMethodForOriginalLdFtnResult_ExactInstantiation_Inner(NativeFormatModuleInfo mappingTableModule, bool forStartAddress, IntPtr canonOriginalLdFtnResult, uint parserOffset, ref RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + methodHandle = default(QMethodDefinition); + genericMethodTypeArgumentHandles = null; + + NativeReader invokeMapReader; + if (!TryGetNativeReaderForBlob(mappingTableModule, ReflectionMapBlob.ExactMethodInstantiationsHashtable, out invokeMapReader)) + { + // This should have succeeded otherwise, how did we get a parser offset as an input parameter? + Debug.Assert(false); + return false; + } + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeNativeReferences(mappingTableModule); + + NativeParser entryParser = new NativeParser(invokeMapReader, parserOffset); + + RuntimeTypeHandle entryTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + + // Hash table names / sigs are indirected through to the native layout info + MethodNameAndSignature nameAndSignature; + if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(mappingTableModule.Handle, entryParser.GetUnsigned(), out nameAndSignature)) + return false; + + int parsedArity = (int)entryParser.GetSequenceCount(); + + if (forStartAddress) + { + for (int i = 0; i < parsedArity; i++) + { + entryParser.SkipInteger(); + } + } + else + { + genericMethodTypeArgumentHandles = new RuntimeTypeHandle[parsedArity]; + + for (int i = 0; i < parsedArity; i++) + { + genericMethodTypeArgumentHandles[i] = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + } + } + + IntPtr functionPointer = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + if (functionPointer != canonOriginalLdFtnResult) + return false; + + if (TypeLoaderEnvironment.Instance.TryGetMetadataForTypeMethodNameAndSignature(entryTypeHandle, nameAndSignature, out methodHandle)) + { + declaringTypeHandle = entryTypeHandle; + return true; + } + + return false; + } + + private unsafe bool TryGetMethodForOriginalLdFtnResult_GenericMethodWithInstantiationArgument(IntPtr instantiationArgument, ref RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + MethodNameAndSignature nameAndSig; + bool success = TypeLoaderEnvironment.Instance.TryGetGenericMethodComponents(instantiationArgument, out declaringTypeHandle, out nameAndSig, out genericMethodTypeArgumentHandles); + if (success) + { + if (TypeLoaderEnvironment.Instance.TryGetMetadataForTypeMethodNameAndSignature(declaringTypeHandle, nameAndSig, out methodHandle)) + { + return true; + } + } + + methodHandle = default(QMethodDefinition); + + return false; + } + + public sealed override FieldAccessor TryGetFieldAccessor( + MetadataReader metadataReader, + RuntimeTypeHandle declaringTypeHandle, + RuntimeTypeHandle fieldTypeHandle, + FieldHandle fieldHandle) + { + FieldAccessMetadata fieldAccessMetadata; + + if (!TypeLoaderEnvironment.TryGetFieldAccessMetadata( + metadataReader, + declaringTypeHandle, + fieldHandle, + out fieldAccessMetadata)) + { + return null; + } + + FieldTableFlags fieldBase = fieldAccessMetadata.Flags & FieldTableFlags.StorageClass; + switch (fieldBase) + { + case FieldTableFlags.Instance: + { + int fieldOffsetDelta = RuntimeAugments.IsValueType(declaringTypeHandle) ? IntPtr.Size : 0; + + return RuntimeAugments.IsValueType(fieldTypeHandle) ? + (FieldAccessor)new ValueTypeFieldAccessorForInstanceFields( + fieldAccessMetadata.Offset + fieldOffsetDelta, declaringTypeHandle, fieldTypeHandle) : + RuntimeAugments.IsUnmanagedPointerType(fieldTypeHandle) ? + (FieldAccessor)new PointerTypeFieldAccessorForInstanceFields( + fieldAccessMetadata.Offset + fieldOffsetDelta, declaringTypeHandle, fieldTypeHandle) : + (FieldAccessor)new ReferenceTypeFieldAccessorForInstanceFields( + fieldAccessMetadata.Offset + fieldOffsetDelta, declaringTypeHandle, fieldTypeHandle); + } + + case FieldTableFlags.NonGCStatic: + case FieldTableFlags.GCStatic: + case FieldTableFlags.ThreadStatic: + { + int fieldOffset; + IntPtr staticsBase; + + if (RuntimeAugments.IsGenericType(declaringTypeHandle)) + { + unsafe + { + fieldOffset = fieldAccessMetadata.Offset; + staticsBase = fieldBase switch + { + FieldTableFlags.GCStatic => TypeLoaderEnvironment.Instance.TryGetGcStaticFieldData(declaringTypeHandle), + FieldTableFlags.NonGCStatic => TypeLoaderEnvironment.Instance.TryGetNonGcStaticFieldData(declaringTypeHandle), + _ => TypeLoaderEnvironment.Instance.TryGetThreadStaticFieldData(declaringTypeHandle), + }; + } + } + else + { + Debug.Assert((fieldAccessMetadata.Flags & FieldTableFlags.IsUniversalCanonicalEntry) == 0); + + if (fieldBase != FieldTableFlags.NonGCStatic) + { + fieldOffset = fieldAccessMetadata.Offset; + staticsBase = fieldAccessMetadata.Cookie; + } + else + { + // The fieldAccessMetadata.Cookie value points directly to the field's data. We'll use that as the 'staticsBase' + // and just use a field offset of zero. + fieldOffset = 0; + staticsBase = fieldAccessMetadata.Cookie; + } + } + + IntPtr cctorContext = TryGetStaticClassConstructionContext(declaringTypeHandle); + + return RuntimeAugments.IsValueType(fieldTypeHandle) ? + (FieldAccessor)new ValueTypeFieldAccessorForStaticFields(cctorContext, staticsBase, fieldOffset, fieldAccessMetadata.Flags, fieldTypeHandle) : + RuntimeAugments.IsUnmanagedPointerType(fieldTypeHandle) ? + (FieldAccessor)new PointerTypeFieldAccessorForStaticFields(cctorContext, staticsBase, fieldOffset, fieldAccessMetadata.Flags, fieldTypeHandle) : + (FieldAccessor)new ReferenceTypeFieldAccessorForStaticFields(cctorContext, staticsBase, fieldOffset, fieldAccessMetadata.Flags, fieldTypeHandle); + } + } + + return null; + } + + // + // This resolves RuntimeMethodHandles for methods declared on non-generic types (declaringTypeHandle is an output of this method.) + // + public unsafe sealed override bool TryGetMethodFromHandle(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + MethodNameAndSignature nameAndSignature; + methodHandle = default(QMethodDefinition); + if (!TypeLoaderEnvironment.Instance.TryGetRuntimeMethodHandleComponents(runtimeMethodHandle, out declaringTypeHandle, out nameAndSignature, out genericMethodTypeArgumentHandles)) + return false; + + return TypeLoaderEnvironment.Instance.TryGetMetadataForTypeMethodNameAndSignature(declaringTypeHandle, nameAndSignature, out methodHandle); + } + + // + // This resolves RuntimeMethodHandles for methods declared on generic types (declaringTypeHandle is an input of this method.) + // + public sealed override bool TryGetMethodFromHandleAndType(RuntimeMethodHandle runtimeMethodHandle, RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + RuntimeTypeHandle dummy; + return TryGetMethodFromHandle(runtimeMethodHandle, out dummy, out methodHandle, out genericMethodTypeArgumentHandles); + } + + // + // This resolves RuntimeFieldHandles for fields declared on non-generic types (declaringTypeHandle is an output of this method.) + // + public unsafe sealed override bool TryGetFieldFromHandle(RuntimeFieldHandle runtimeFieldHandle, out RuntimeTypeHandle declaringTypeHandle, out FieldHandle fieldHandle) + { + declaringTypeHandle = default(RuntimeTypeHandle); + fieldHandle = default(FieldHandle); + + string fieldName; + if (!TypeLoaderEnvironment.Instance.TryGetRuntimeFieldHandleComponents(runtimeFieldHandle, out declaringTypeHandle, out fieldName)) + return false; + + QTypeDefinition qTypeDefinition; + RuntimeTypeHandle metadataLookupTypeHandle = GetTypeDefinition(declaringTypeHandle); + + if (!TryGetMetadataForNamedType(metadataLookupTypeHandle, out qTypeDefinition)) + return false; + + // TODO! Handle ecma style types + MetadataReader reader = qTypeDefinition.NativeFormatReader; + TypeDefinitionHandle typeDefinitionHandle = qTypeDefinition.NativeFormatHandle; + + TypeDefinition typeDefinition = typeDefinitionHandle.GetTypeDefinition(reader); + foreach (FieldHandle fh in typeDefinition.Fields) + { + Field field = fh.GetField(reader); + if (field.Name.StringEquals(fieldName, reader)) + { + fieldHandle = fh; + return true; + } + } + + return false; + } + + // + // This resolves RuntimeFieldHandles for fields declared on generic types (declaringTypeHandle is an input of this method.) + // + public sealed override bool TryGetFieldFromHandleAndType(RuntimeFieldHandle runtimeFieldHandle, RuntimeTypeHandle declaringTypeHandle, out FieldHandle fieldHandle) + { + RuntimeTypeHandle dummy; + return TryGetFieldFromHandle(runtimeFieldHandle, out dummy, out fieldHandle); + } + + /// + /// Locate the static constructor context given the runtime type handle (MethodTable) for the type in question. + /// + /// MethodTable of the type to look up + internal unsafe IntPtr TryGetStaticClassConstructionContext(RuntimeTypeHandle typeHandle) + { + return TypeLoaderEnvironment.TryGetStaticClassConstructionContext(typeHandle); + } + + private struct MethodParametersInfo + { + private MetadataReader _metadataReader; + private MethodBase _methodBase; + private MethodHandle _methodHandle; + + private Handle[] _returnTypeAndParametersHandlesCache; + private Type[] _returnTypeAndParametersTypesCache; + + public MethodParametersInfo(MethodBase methodBase) + { + _metadataReader = null; + _methodBase = methodBase; + _methodHandle = default(MethodHandle); + _returnTypeAndParametersHandlesCache = null; + _returnTypeAndParametersTypesCache = null; + } + + public MethodParametersInfo(MetadataReader metadataReader, MethodBase methodBase, MethodHandle methodHandle) + { + _metadataReader = metadataReader; + _methodBase = methodBase; + _methodHandle = methodHandle; + _returnTypeAndParametersHandlesCache = null; + _returnTypeAndParametersTypesCache = null; + } + + public LowLevelList ParameterTypeHandles + { + get + { + ParameterInfo[] parameters = _methodBase.GetParametersNoCopy(); + LowLevelList result = new LowLevelList(parameters.Length); + + for (int i = 0; i < parameters.Length; i++) + { + Type parameterType = parameters[i].ParameterType; + + if (parameterType.IsByRef) + result.Add(parameterType.GetElementType().TypeHandle); + else if (parameterType.GetTypeInfo().IsEnum && !parameters[i].HasDefaultValue) + result.Add(Enum.GetUnderlyingType(parameterType).TypeHandle); + else + result.Add(parameterType.TypeHandle); + } + + return result; + } + } + + public RuntimeTypeHandle ReturnTypeHandle + { + get + { + MethodInfo reflectionMethodInfo = _methodBase as MethodInfo; + Type returnType = reflectionMethodInfo != null ? reflectionMethodInfo.ReturnType : typeof(void); + if (returnType.IsByRef) + returnType = returnType.GetElementType(); + return returnType.TypeHandle; + } + } + + public LowLevelList ReturnTypeAndParameterTypeHandles + { + get + { + LowLevelList result = ParameterTypeHandles; + result.Insert(0, ReturnTypeHandle); + return result; + } + } + + public bool[] ReturnTypeAndParametersByRefFlags + { + get + { + ParameterInfo[] parameters = _methodBase.GetParametersNoCopy(); + bool[] result = new bool[parameters.Length + 1]; + + MethodInfo reflectionMethodInfo = _methodBase as MethodInfo; + Type returnType = reflectionMethodInfo != null ? reflectionMethodInfo.ReturnType : typeof(void); + result[0] = returnType.IsByRef; + + for (int i = 0; i < parameters.Length; i++) + result[i + 1] = parameters[i].ParameterType.IsByRef; + + return result; + } + } + + public bool RequiresCallingConventionConverter(out bool[] forcedByRefParams) + { + Handle[] handles = null; + Type[] types = null; + GetReturnTypeAndParameterTypesAndMDHandles(ref handles, ref types); + + // Compute whether any of the parameters have generic vars in their signatures ... + bool requiresCallingConventionConverter = false; + forcedByRefParams = new bool[handles.Length]; + for (int i = 0; i < handles.Length; i++) + if ((forcedByRefParams[i] = TypeSignatureHasVarsNeedingCallingConventionConverter(handles[i], types[i], isTopLevelParameterType:true))) + requiresCallingConventionConverter = true; + + return requiresCallingConventionConverter; + } + + private void GetReturnTypeAndParameterTypesAndMDHandles(ref Handle[] handles, ref Type[] types) + { + if (_returnTypeAndParametersTypesCache == null) + { + Debug.Assert(_metadataReader != null && !_methodHandle.Equals(default(MethodHandle))); + + _returnTypeAndParametersHandlesCache = new Handle[_methodBase.GetParametersNoCopy().Length + 1]; + _returnTypeAndParametersTypesCache = new Type[_methodBase.GetParametersNoCopy().Length + 1]; + + MethodSignature signature = _methodHandle.GetMethod(_metadataReader).Signature.GetMethodSignature(_metadataReader); + + // Check the return type for generic vars + MethodInfo reflectionMethodInfo = _methodBase as MethodInfo; + _returnTypeAndParametersTypesCache[0] = reflectionMethodInfo != null ? reflectionMethodInfo.ReturnType : typeof(void); + _returnTypeAndParametersHandlesCache[0] = signature.ReturnType; + + // Check the method parameters for generic vars + int index = 1; + foreach (Handle paramSigHandle in signature.Parameters) + { + _returnTypeAndParametersHandlesCache[index] = paramSigHandle; + _returnTypeAndParametersTypesCache[index] = _methodBase.GetParametersNoCopy()[index - 1].ParameterType; + index++; + } + } + + handles = _returnTypeAndParametersHandlesCache; + types = _returnTypeAndParametersTypesCache; + Debug.Assert(handles != null && types != null); + } + + // IF THESE SEMANTICS EVER CHANGE UPDATE THE LOGIC WHICH DEFINES THIS BEHAVIOR IN + // THE DYNAMIC TYPE LOADER AS WELL AS THE COMPILER. + // + // Parameter's are considered to have type layout dependent on their generic instantiation + // if the type of the parameter in its signature is a type variable, or if the type is a generic + // structure which meets 2 characteristics: + // 1. Structure size/layout is affected by the size/layout of one or more of its generic parameters + // 2. One or more of the generic parameters is a type variable, or a generic structure which also recursively + // would satisfy constraint 2. (Note, that in the recursion case, whether or not the structure is affected + // by the size/layout of its generic parameters is not investigated.) + // + // Examples parameter types, and behavior. + // + // T -> true + // List -> false + // StructNotDependentOnArgsForSize -> false + // GenStructDependencyOnArgsForSize -> true + // StructNotDependentOnArgsForSize> -> true + // StructNotDependentOnArgsForSize>>> -> false + // + // Example non-parameter type behavior + // T -> true + // List -> false + // StructNotDependentOnArgsForSize -> *true* + // GenStructDependencyOnArgsForSize -> true + // StructNotDependentOnArgsForSize> -> true + // StructNotDependentOnArgsForSize>>> -> false + // + private bool TypeSignatureHasVarsNeedingCallingConventionConverter(Handle typeHandle, Type type, bool isTopLevelParameterType) + { + if (typeHandle.HandleType == HandleType.TypeSpecification) + { + TypeSpecification typeSpec = typeHandle.ToTypeSpecificationHandle(_metadataReader).GetTypeSpecification(_metadataReader); + Handle sigHandle = typeSpec.Signature; + HandleType sigHandleType = sigHandle.HandleType; + switch (sigHandleType) + { + case HandleType.TypeVariableSignature: + case HandleType.MethodTypeVariableSignature: + return true; + + case HandleType.TypeInstantiationSignature: + { + Debug.Assert(type.IsConstructedGenericType); + TypeInstantiationSignature sig = sigHandle.ToTypeInstantiationSignatureHandle(_metadataReader).GetTypeInstantiationSignature(_metadataReader); + + if (RuntimeAugments.IsValueType(type.TypeHandle)) + { + // This generic type is a struct (its base type is System.ValueType) + int genArgIndex = 0; + bool needsCallingConventionConverter = false; + foreach (Handle genericTypeArgumentHandle in sig.GenericTypeArguments) + { + if (TypeSignatureHasVarsNeedingCallingConventionConverter(genericTypeArgumentHandle, type.GenericTypeArguments[genArgIndex++], isTopLevelParameterType:false)) + { + needsCallingConventionConverter = true; + break; + } + } + + if (needsCallingConventionConverter) + { + if (!isTopLevelParameterType) + return true; + + if (!TypeLoaderEnvironment.Instance.TryComputeHasInstantiationDeterminedSize(type.TypeHandle, out needsCallingConventionConverter)) + RuntimeExceptionHelpers.FailFast("Unable to setup calling convention converter correctly"); + return needsCallingConventionConverter; + } + } + } + return false; + } + } + + return false; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MetadataTable.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MetadataTable.cs new file mode 100644 index 00000000000000..676afe52832208 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MetadataTable.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; +using global::System.Reflection; +using global::System.Collections.Generic; + +using global::Internal.Runtime.Augments; + +using global::Internal.Reflection.Core; +using global::Internal.Reflection.Core.Execution; +using global::Internal.Reflection.Execution.MethodInvokers; + +using global::System.Runtime.CompilerServices; +using global::System.Runtime.InteropServices; + +using global::Internal.Runtime; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Reflection.Execution +{ + internal sealed partial class ExecutionEnvironmentImplementation : ExecutionEnvironment + { + private struct DynamicInvokeMapEntry + { + public const uint IsImportMethodFlag = 0x40000000; + public const uint InstantiationDetailIndexMask = 0x3FFFFFFF; + } + + private struct VirtualInvokeTableEntry + { + public const int GenericVirtualMethod = 1; + public const int FlagsMask = 1; + } + + private static class FieldAccessFlags + { + public const int RemoteStaticFieldRVA = unchecked((int)0x80000000); + } + + /// + /// This structure describes one static field in an external module. It is represented + /// by an indirection cell pointer and an offset within the cell - the final address + /// of the static field is essentially *IndirectionCell + Offset. + /// + [StructLayout(LayoutKind.Sequential)] + private struct RemoteStaticFieldDescriptor + { + public unsafe IntPtr* IndirectionCell; + public int Offset; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs new file mode 100644 index 00000000000000..cdb8b8990feacc --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs @@ -0,0 +1,166 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Reflection.Runtime.General; + +using Internal.Runtime.Augments; + +using Internal.Reflection.Core.Execution; +using Internal.Reflection.Execution.FieldAccessors; +using Internal.Reflection.Execution.MethodInvokers; + +namespace Internal.Reflection.Execution +{ + //========================================================================================================== + // These ExecutionEnvironment entrypoints provide basic runtime allocation and policy services to + // Reflection. Our implementation merely forwards to System.Private.CoreLib. + //========================================================================================================== + internal sealed partial class ExecutionEnvironmentImplementation : ExecutionEnvironment + { + public sealed override object NewObject(RuntimeTypeHandle typeHandle) + { + return RuntimeAugments.NewObject(typeHandle); + } + + public sealed override Array NewArray(RuntimeTypeHandle typeHandleForArrayType, int count) + { + return RuntimeAugments.NewArray(typeHandleForArrayType, count); + } + + public sealed override Array NewMultiDimArray(RuntimeTypeHandle typeHandleForArrayType, int[] lengths, int[] lowerBounds) + { + return RuntimeAugments.NewMultiDimArray(typeHandleForArrayType, lengths, lowerBounds); + } + + public sealed override RuntimeTypeHandle ProjectionTypeForArrays + { + get + { + return RuntimeAugments.ProjectionTypeForArrays; + } + } + + public sealed override bool IsAssignableFrom(RuntimeTypeHandle dstType, RuntimeTypeHandle srcType) + { + return RuntimeAugments.IsAssignableFrom(dstType, srcType); + } + + public sealed override bool TryGetBaseType(RuntimeTypeHandle typeHandle, out RuntimeTypeHandle baseTypeHandle) + { + return RuntimeAugments.TryGetBaseType(typeHandle, out baseTypeHandle); + } + + public sealed override IEnumerable TryGetImplementedInterfaces(RuntimeTypeHandle typeHandle) + { + return RuntimeAugments.TryGetImplementedInterfaces(typeHandle); + } + + public sealed override void VerifyInterfaceIsImplemented(RuntimeTypeHandle typeHandle, RuntimeTypeHandle ifaceHandle) + { + if (RuntimeAugments.IsInterface(typeHandle)) + { + throw new ArgumentException(SR.Argument_InterfaceMap); + } + + if (!RuntimeAugments.IsInterface(ifaceHandle)) + { + throw new ArgumentException(SR.Arg_MustBeInterface); + } + + if (RuntimeAugments.IsAssignableFrom(ifaceHandle, typeHandle)) + { + return; + } + + throw new ArgumentException(SR.Arg_NotFoundIFace); + } + + public sealed override void GetInterfaceMap(Type instanceType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType, out MethodInfo[] interfaceMethods, out MethodInfo[] targetMethods) + { + MethodInfo[] ifaceMethods = interfaceType.GetMethods(); + var tMethods = new MethodInfo[ifaceMethods.Length]; + for (int i = 0; i < ifaceMethods.Length; i++) + { + var invoker = (VirtualMethodInvoker)GetMethodInvoker(ifaceMethods[i]); + + IntPtr classRtMethodHandle = invoker.ResolveTarget(instanceType.TypeHandle); + if (classRtMethodHandle == IntPtr.Zero) + { + goto notFound; + } + + MethodBase methodBase = RuntimeAugments.Callbacks.GetMethodBaseFromStartAddressIfAvailable(classRtMethodHandle); + if (methodBase == null) + { + goto notFound; + } + + tMethods[i] = (MethodInfo)methodBase; + continue; + +notFound: + if (instanceType.IsAbstract) + { + throw new PlatformNotSupportedException(SR.Format(SR.Arg_InterfaceMapMustNotBeAbstract, interfaceType.FullName, instanceType.FullName)); + } + + throw new NotSupportedException(); + } + + interfaceMethods = ifaceMethods; + targetMethods = tMethods; + } + + public sealed override string GetLastResortString(RuntimeTypeHandle typeHandle) + { + return RuntimeAugments.GetLastResortString(typeHandle); + } + + //============================================================================================== + // Miscellaneous + //============================================================================================== + public sealed override FieldAccessor CreateLiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle) + { + return new LiteralFieldAccessor(value, fieldTypeHandle); + } + + public sealed override EnumInfo GetEnumInfo(RuntimeTypeHandle typeHandle) + { + // Handle the weird case of an enum type nested under a generic type that makes the + // enum itself generic + RuntimeTypeHandle typeDefHandle = typeHandle; + if (RuntimeAugments.IsGenericType(typeHandle)) + { + typeDefHandle = RuntimeAugments.GetGenericDefinition(typeHandle); + } + + // If the type is reflection blocked, we pretend there are no enum values defined + if (ReflectionExecution.ExecutionEnvironment.IsReflectionBlocked(typeDefHandle)) + { + return new EnumInfo(RuntimeAugments.GetEnumUnderlyingType(typeHandle), Array.Empty(), Array.Empty(), false); + } + + QTypeDefinition qTypeDefinition; + if (!ReflectionExecution.ExecutionEnvironment.TryGetMetadataForNamedType(typeDefHandle, out qTypeDefinition)) + { + throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(Type.GetTypeFromHandle(typeDefHandle)); + } + + if (qTypeDefinition.IsNativeFormatMetadataBased) + { + return NativeFormatEnumInfo.Create(typeHandle, qTypeDefinition.NativeFormatReader, qTypeDefinition.NativeFormatHandle); + } +#if ECMA_METADATA_SUPPORT + if (qTypeDefinition.IsEcmaFormatMetadataBased) + { + return EcmaFormatEnumInfo.Create(typeHandle, qTypeDefinition.EcmaFormatReader, qTypeDefinition.EcmaFormatHandle); + } +#endif + return null; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.cs new file mode 100644 index 00000000000000..ccf8329ce3a692 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::Internal.Reflection.Core.Execution; + +namespace Internal.Reflection.Execution +{ + internal sealed partial class ExecutionEnvironmentImplementation : ExecutionEnvironment + { + public unsafe ExecutionEnvironmentImplementation() + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/InstanceFieldAccessor.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/InstanceFieldAccessor.cs new file mode 100644 index 00000000000000..b23c827ade44e6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/InstanceFieldAccessor.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; + +using Internal.Runtime.Augments; +using Internal.Reflection.Core.Execution; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal abstract class InstanceFieldAccessor : FieldAccessor + { + public InstanceFieldAccessor(RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle, int offsetPlusHeader) + { + this.DeclaringTypeHandle = declaringTypeHandle; + this.FieldTypeHandle = fieldTypeHandle; + this.OffsetPlusHeader = offsetPlusHeader; + } + + public sealed override int Offset => OffsetPlusHeader - RuntimeAugments.ObjectHeaderSize; + + public sealed override object GetField(object obj) + { + if (obj == null) + throw new TargetException(SR.RFLCT_Targ_StatFldReqTarg); + if (!RuntimeAugments.IsAssignable(obj, this.DeclaringTypeHandle)) + throw new ArgumentException(); + return UncheckedGetField(obj); + } + + public sealed override object GetFieldDirect(TypedReference typedReference) + { + if (RuntimeAugments.IsValueType(this.DeclaringTypeHandle)) + { + // We're being asked to read a field from the value type pointed to by the TypedReference. This code path + // avoids boxing that value type by adding this field's offset to the TypedReference's managed pointer. + Type targetType = TypedReference.GetTargetType(typedReference); + if (!(targetType.TypeHandle.Equals(this.DeclaringTypeHandle))) + throw new ArgumentException(); + return UncheckedGetFieldDirectFromValueType(typedReference); + } + else + { + // We're being asked to read a field from a reference type. There's no boxing to optimize out in that case so just handle it as + // if this was a FieldInfo.GetValue() call. + object obj = TypedReference.ToObject(typedReference); + return GetField(obj); + } + } + + protected abstract object UncheckedGetFieldDirectFromValueType(TypedReference typedReference); + + public sealed override void SetField(object obj, object value, BinderBundle binderBundle) + { + if (obj == null) + throw new TargetException(SR.RFLCT_Targ_StatFldReqTarg); + if (!RuntimeAugments.IsAssignable(obj, this.DeclaringTypeHandle)) + throw new ArgumentException(); + value = RuntimeAugments.CheckArgument(value, this.FieldTypeHandle, binderBundle); + UncheckedSetField(obj, value); + } + + public sealed override void SetFieldDirect(TypedReference typedReference, object value) + { + if (RuntimeAugments.IsValueType(this.DeclaringTypeHandle)) + { + // We're being asked to store a field into the value type pointed to by the TypedReference. This code path + // bypasses boxing that value type by adding this field's offset to the TypedReference's managed pointer. + // (Otherwise, the store would go into a useless temporary copy rather than the intended destination.) + Type targetType = TypedReference.GetTargetType(typedReference); + if (!(targetType.TypeHandle.Equals(this.DeclaringTypeHandle))) + throw new ArgumentException(); + value = RuntimeAugments.CheckArgumentForDirectFieldAccess(value, this.FieldTypeHandle); + UncheckedSetFieldDirectIntoValueType(typedReference, value); + } + else + { + // We're being asked to store a field from a reference type. There's no boxing to bypass in that case so just handle it as + // if this was a FieldInfo.SetValue() call (but using SetValueDirect's argument coercing semantics) + object obj = TypedReference.ToObject(typedReference); + if (obj == null) + throw new TargetException(SR.RFLCT_Targ_StatFldReqTarg); + if (!RuntimeAugments.IsAssignable(obj, this.DeclaringTypeHandle)) + throw new ArgumentException(); + value = RuntimeAugments.CheckArgumentForDirectFieldAccess(value, this.FieldTypeHandle); + UncheckedSetField(obj, value); + } + } + + protected abstract void UncheckedSetFieldDirectIntoValueType(TypedReference typedReference, object value); + + protected abstract object UncheckedGetField(object obj); + protected abstract void UncheckedSetField(object obj, object value); + + protected int OffsetPlusHeader { get; } + protected RuntimeTypeHandle DeclaringTypeHandle { get; } + protected RuntimeTypeHandle FieldTypeHandle { get; } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/LiteralFieldAccessor.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/LiteralFieldAccessor.cs new file mode 100644 index 00000000000000..84a83201957861 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/LiteralFieldAccessor.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal sealed class LiteralFieldAccessor : StaticFieldAccessor + { + public LiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle) + : base(IntPtr.Zero, fieldTypeHandle) + { + _value = value; + } + + protected sealed override object GetFieldBypassCctor() => _value; + + protected sealed override void SetFieldBypassCctor(object value, BinderBundle binderBundle) + { + throw new FieldAccessException(SR.Acc_ReadOnly); + } + + protected sealed override void SetFieldDirectBypassCctor(object value) + { + throw new FieldAccessException(SR.Acc_ReadOnly); + } + + private readonly object _value; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForInstanceFields.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForInstanceFields.cs new file mode 100644 index 00000000000000..24d93ba27c9807 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForInstanceFields.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime.Augments; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal sealed class PointerTypeFieldAccessorForInstanceFields : InstanceFieldAccessor + { + public PointerTypeFieldAccessorForInstanceFields(int offsetPlusHeader, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle) + : base(declaringTypeHandle, fieldTypeHandle, offsetPlusHeader) + { + } + + protected sealed override object UncheckedGetField(object obj) + { + return RuntimeAugments.LoadPointerTypeField(obj, OffsetPlusHeader, this.FieldTypeHandle); + } + + protected sealed override object UncheckedGetFieldDirectFromValueType(TypedReference typedReference) + { + return RuntimeAugments.LoadPointerTypeFieldValueFromValueType(typedReference, this.Offset, this.FieldTypeHandle); + } + + protected sealed override void UncheckedSetField(object obj, object value) + { + RuntimeAugments.StoreValueTypeField(obj, OffsetPlusHeader, value, typeof(IntPtr).TypeHandle); + } + + protected sealed override void UncheckedSetFieldDirectIntoValueType(TypedReference typedReference, object value) + { + RuntimeAugments.StoreValueTypeFieldValueIntoValueType(typedReference, this.Offset, value, typeof(IntPtr).TypeHandle); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForStaticFields.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForStaticFields.cs new file mode 100644 index 00000000000000..d9f07affd94683 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForStaticFields.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime; +using Internal.Runtime.Augments; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal sealed class PointerTypeFieldAccessorForStaticFields : RegularStaticFieldAccessor + { + public PointerTypeFieldAccessorForStaticFields(IntPtr cctorContext, IntPtr staticsBase, int fieldOffset, FieldTableFlags fieldBase, RuntimeTypeHandle fieldTypeHandle) + : base(cctorContext, staticsBase, fieldOffset, fieldBase, fieldTypeHandle) + { + } + + protected unsafe sealed override object GetFieldBypassCctor() + { + if (FieldBase == FieldTableFlags.GCStatic) + { + object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(StaticsBase); + return RuntimeAugments.LoadPointerTypeField(gcStaticsRegion, FieldOffset, FieldTypeHandle); + } + else if (FieldBase == FieldTableFlags.NonGCStatic) + { + return RuntimeAugments.LoadPointerTypeField(StaticsBase + FieldOffset, FieldTypeHandle); + } + + Debug.Assert(FieldBase == FieldTableFlags.ThreadStatic); + object threadStaticRegion = RuntimeAugments.GetThreadStaticBase(StaticsBase); + return RuntimeAugments.LoadPointerTypeField(threadStaticRegion, FieldOffset, FieldTypeHandle); + } + + protected unsafe sealed override void UncheckedSetFieldBypassCctor(object value) + { + if (FieldBase == FieldTableFlags.GCStatic) + { + object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(StaticsBase); + RuntimeAugments.StoreValueTypeField(gcStaticsRegion, FieldOffset, value, typeof(IntPtr).TypeHandle); + } + else if (FieldBase == FieldTableFlags.NonGCStatic) + { + RuntimeAugments.StoreValueTypeField(StaticsBase + FieldOffset, value, typeof(IntPtr).TypeHandle); + } + else + { + Debug.Assert(FieldBase == FieldTableFlags.ThreadStatic); + object threadStaticsRegion = RuntimeAugments.GetThreadStaticBase(StaticsBase); + RuntimeAugments.StoreValueTypeField(threadStaticsRegion, FieldOffset, value, typeof(IntPtr).TypeHandle); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForInstanceFields.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForInstanceFields.cs new file mode 100644 index 00000000000000..39bd77cd3dd0f3 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForInstanceFields.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime.Augments; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal sealed class ReferenceTypeFieldAccessorForInstanceFields : InstanceFieldAccessor + { + public ReferenceTypeFieldAccessorForInstanceFields(int offsetPlusHeader, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle) + : base(declaringTypeHandle, fieldTypeHandle, offsetPlusHeader) + { + } + + protected sealed override object UncheckedGetField(object obj) + { + return RuntimeAugments.LoadReferenceTypeField(obj, OffsetPlusHeader); + } + + protected sealed override object UncheckedGetFieldDirectFromValueType(TypedReference typedReference) + { + return RuntimeAugments.LoadReferenceTypeFieldValueFromValueType(typedReference, this.Offset); + } + + protected sealed override void UncheckedSetField(object obj, object value) + { + RuntimeAugments.StoreReferenceTypeField(obj, OffsetPlusHeader, value); + } + + protected sealed override void UncheckedSetFieldDirectIntoValueType(TypedReference typedReference, object value) + { + RuntimeAugments.StoreReferenceTypeFieldValueIntoValueType(typedReference, this.Offset, value); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForStaticFields.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForStaticFields.cs new file mode 100644 index 00000000000000..dc8a8d557917bb --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForStaticFields.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime; +using Internal.Runtime.Augments; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal sealed class ReferenceTypeFieldAccessorForStaticFields : RegularStaticFieldAccessor + { + public ReferenceTypeFieldAccessorForStaticFields(IntPtr cctorContext, IntPtr staticsBase, int fieldOffset, FieldTableFlags fieldBase, RuntimeTypeHandle fieldTypeHandle) + : base(cctorContext, staticsBase, fieldOffset, fieldBase, fieldTypeHandle) + { + } + + protected unsafe sealed override object GetFieldBypassCctor() + { + if (FieldBase == FieldTableFlags.GCStatic) + { + object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(StaticsBase); + return RuntimeAugments.LoadReferenceTypeField(gcStaticsRegion, FieldOffset); + } + else if (FieldBase == FieldTableFlags.NonGCStatic) + { + return RuntimeAugments.LoadReferenceTypeField(StaticsBase + FieldOffset); + } + + Debug.Assert(FieldBase == FieldTableFlags.ThreadStatic); + object threadStaticRegion = RuntimeAugments.GetThreadStaticBase(StaticsBase); + return RuntimeAugments.LoadReferenceTypeField(threadStaticRegion, FieldOffset); + } + + protected unsafe sealed override void UncheckedSetFieldBypassCctor(object value) + { + if (FieldBase == FieldTableFlags.GCStatic) + { + object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(StaticsBase); + RuntimeAugments.StoreReferenceTypeField(gcStaticsRegion, FieldOffset, value); + return; + } + else if (FieldBase == FieldTableFlags.NonGCStatic) + { + RuntimeAugments.StoreReferenceTypeField(StaticsBase + FieldOffset, value); + } + else + { + Debug.Assert(FieldBase == FieldTableFlags.ThreadStatic); + object threadStaticsRegion = RuntimeAugments.GetThreadStaticBase(StaticsBase); + RuntimeAugments.StoreReferenceTypeField(threadStaticsRegion, FieldOffset, value); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/RegularStaticFieldAccessor.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/RegularStaticFieldAccessor.cs new file mode 100644 index 00000000000000..d0e506a2a147bc --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/RegularStaticFieldAccessor.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal abstract class RegularStaticFieldAccessor : WritableStaticFieldAccessor + { + protected RegularStaticFieldAccessor(IntPtr cctorContext, IntPtr staticsBase, int fieldOffset, FieldTableFlags fieldBase, RuntimeTypeHandle fieldTypeHandle) + : base(cctorContext, fieldTypeHandle) + { + StaticsBase = staticsBase; + _fieldFlags = fieldBase; + FieldOffset = fieldOffset; + } + + protected IntPtr StaticsBase { get; } + private readonly FieldTableFlags _fieldFlags; + protected int FieldOffset { get; } + protected FieldTableFlags FieldBase => _fieldFlags & FieldTableFlags.StorageClass; + protected sealed override bool IsFieldInitOnly => (_fieldFlags & FieldTableFlags.IsInitOnly) == FieldTableFlags.IsInitOnly; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/StaticFieldAccessor.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/StaticFieldAccessor.cs new file mode 100644 index 00000000000000..088cd86761fddb --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/StaticFieldAccessor.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; + +using Internal.Runtime.Augments; +using Internal.Reflection.Core.Execution; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal abstract class StaticFieldAccessor : FieldAccessor + { + protected RuntimeTypeHandle FieldTypeHandle { get; } + + private IntPtr _cctorContext; + + public StaticFieldAccessor(IntPtr cctorContext, RuntimeTypeHandle fieldTypeHandle) + { + FieldTypeHandle = fieldTypeHandle; + _cctorContext = cctorContext; + } + + public sealed override object GetField(object obj) + { + if (_cctorContext != IntPtr.Zero) + { + RuntimeAugments.EnsureClassConstructorRun(_cctorContext); + } + return GetFieldBypassCctor(); + } + + // GetValueDirect() can be used on static fields though this seems like a silly thing to do. + public sealed override object GetFieldDirect(TypedReference typedReference) => GetField(null); + + public sealed override void SetField(object obj, object value, BinderBundle binderBundle) + { + if (_cctorContext != IntPtr.Zero) + { + RuntimeAugments.EnsureClassConstructorRun(_cctorContext); + } + SetFieldBypassCctor(value, binderBundle); + } + + // SetValueDirect() can be used on static fields though this seems like a silly thing to do. + // Note that the argument coercion rules are different from SetValue. + public sealed override void SetFieldDirect(TypedReference typedReference, object value) + { + if (_cctorContext != IntPtr.Zero) + { + RuntimeAugments.EnsureClassConstructorRun(_cctorContext); + } + SetFieldDirectBypassCctor(value); + } + + public sealed override int Offset + { + get + { + Debug.Fail("Cannot call Offset on a static field."); + throw new InvalidOperationException(); + } + } + + protected abstract object GetFieldBypassCctor(); + protected abstract void SetFieldBypassCctor(object value, BinderBundle binderBundle); + protected abstract void SetFieldDirectBypassCctor(object value); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForInstanceFields.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForInstanceFields.cs new file mode 100644 index 00000000000000..326a8d03465682 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForInstanceFields.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime.Augments; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal sealed class ValueTypeFieldAccessorForInstanceFields : InstanceFieldAccessor + { + public ValueTypeFieldAccessorForInstanceFields(int offsetPlusHeader, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle) + : base(declaringTypeHandle, fieldTypeHandle, offsetPlusHeader) + { + } + + protected sealed override object UncheckedGetField(object obj) + { + return RuntimeAugments.LoadValueTypeField(obj, OffsetPlusHeader, this.FieldTypeHandle); + } + + protected sealed override object UncheckedGetFieldDirectFromValueType(TypedReference typedReference) + { + return RuntimeAugments.LoadValueTypeFieldValueFromValueType(typedReference, this.Offset, this.FieldTypeHandle); + } + + protected sealed override void UncheckedSetField(object obj, object value) + { + RuntimeAugments.StoreValueTypeField(obj, OffsetPlusHeader, value, this.FieldTypeHandle); + } + + protected sealed override void UncheckedSetFieldDirectIntoValueType(TypedReference typedReference, object value) + { + RuntimeAugments.StoreValueTypeFieldValueIntoValueType(typedReference, this.Offset, value, this.FieldTypeHandle); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForStaticFields.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForStaticFields.cs new file mode 100644 index 00000000000000..f92e8648da1ff6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForStaticFields.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime; +using Internal.Runtime.Augments; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal sealed class ValueTypeFieldAccessorForStaticFields : RegularStaticFieldAccessor + { + public ValueTypeFieldAccessorForStaticFields(IntPtr cctorContext, IntPtr staticsBase, int fieldOffset, FieldTableFlags fieldBase, RuntimeTypeHandle fieldTypeHandle) + : base(cctorContext, staticsBase, fieldOffset, fieldBase, fieldTypeHandle) + { + } + + protected unsafe sealed override object GetFieldBypassCctor() + { + if (FieldBase == FieldTableFlags.GCStatic) + { + object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(StaticsBase); + return RuntimeAugments.LoadValueTypeField(gcStaticsRegion, FieldOffset, FieldTypeHandle); + } + else if (FieldBase == FieldTableFlags.NonGCStatic) + { + return RuntimeAugments.LoadValueTypeField(StaticsBase + FieldOffset, FieldTypeHandle); + } + + Debug.Assert(FieldBase == FieldTableFlags.ThreadStatic); + object threadStaticRegion = RuntimeAugments.GetThreadStaticBase(StaticsBase); + return RuntimeAugments.LoadValueTypeField(threadStaticRegion, FieldOffset, FieldTypeHandle); + } + + protected unsafe sealed override void UncheckedSetFieldBypassCctor(object value) + { + if (FieldBase == FieldTableFlags.GCStatic) + { + object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(StaticsBase); + RuntimeAugments.StoreValueTypeField(gcStaticsRegion, FieldOffset, value, FieldTypeHandle); + } + else if (FieldBase == FieldTableFlags.NonGCStatic) + { + RuntimeAugments.StoreValueTypeField(StaticsBase + FieldOffset, value, FieldTypeHandle); + } + else + { + Debug.Assert(FieldBase == FieldTableFlags.ThreadStatic); + object threadStaticsRegion = RuntimeAugments.GetThreadStaticBase(StaticsBase); + RuntimeAugments.StoreValueTypeField(threadStaticsRegion, FieldOffset, value, FieldTypeHandle); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/WritableStaticFieldAccessor.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/WritableStaticFieldAccessor.cs new file mode 100644 index 00000000000000..514763897ed605 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/WritableStaticFieldAccessor.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using Internal.Runtime.Augments; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal abstract class WritableStaticFieldAccessor : StaticFieldAccessor + { + protected WritableStaticFieldAccessor(IntPtr cctorContext, RuntimeTypeHandle fieldTypeHandle) + : base(cctorContext, fieldTypeHandle) + { + } + + protected abstract override object GetFieldBypassCctor(); + + protected abstract bool IsFieldInitOnly { get; } + + protected sealed override void SetFieldBypassCctor(object value, BinderBundle binderBundle) + { + value = RuntimeAugments.CheckArgument(value, FieldTypeHandle, binderBundle); + + if (IsFieldInitOnly) + { + throw new FieldAccessException(SR.Acc_InitOnlyStatic); + } + + UncheckedSetFieldBypassCctor(value); + } + + protected sealed override void SetFieldDirectBypassCctor(object value) + { + value = RuntimeAugments.CheckArgumentForDirectFieldAccess(value, FieldTypeHandle); + + if (IsFieldInitOnly) + { + throw new FieldAccessException(SR.Acc_InitOnlyStatic); + } + + UncheckedSetFieldBypassCctor(value); + } + + protected abstract void UncheckedSetFieldBypassCctor(object value); + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MetadataReaderExtensions.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MetadataReaderExtensions.cs new file mode 100644 index 00000000000000..6a152dc1dada2d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MetadataReaderExtensions.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; + +using global::Internal.Metadata.NativeFormat; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Reflection.Execution +{ + internal static class MetadataReaderExtensions + { + public static string GetString(this ConstantStringValueHandle handle, MetadataReader reader) + { + return reader.GetConstantStringValue(handle).Value; + } + + // Useful for namespace Name string which can be a null handle. + public static string GetStringOrNull(this ConstantStringValueHandle handle, MetadataReader reader) + { + if (reader.IsNull(handle)) + return null; + return reader.GetConstantStringValue(handle).Value; + } + + public static bool IsMethodHandle(this int i) + { + return (HandleType)((uint)i >> 24) == HandleType.Method; + } + + public static int AsInt(this MethodHandle methodHandle) + { + unsafe + { + return *(int*)&methodHandle; + } + } + + public static MethodHandle AsMethodHandle(this int i) + { + unsafe + { + Debug.Assert((HandleType)((uint)i >> 24) == HandleType.Method); + return *(MethodHandle*)&i; + } + } + + public static int AsInt(this FieldHandle fieldHandle) + { + unsafe + { + return *(int*)&fieldHandle; + } + } + + public static FieldHandle AsFieldHandle(this int i) + { + unsafe + { + Debug.Assert((HandleType)((uint)i >> 24) == HandleType.Field); + return *(FieldHandle*)&i; + } + } + + public static TypeDefinitionHandle AsTypeDefinitionHandle(this int i) + { + unsafe + { + Debug.Assert((HandleType)((uint)i >> 24) == HandleType.TypeDefinition); + return *(TypeDefinitionHandle*)&i; + } + } + + public static int WithoutHandleType(this int constantStringValueHandle) + { + unsafe + { + return constantStringValueHandle & 0x00ffffff; + } + } + + public static bool IsConstantStringValueHandle(this int i) + { + return (HandleType)((uint)i >> 24) == HandleType.ConstantStringValue; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokeInfo.cs new file mode 100644 index 00000000000000..1c020b7c0aeffb --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokeInfo.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; +using global::System.Reflection; + +namespace Internal.Reflection.Execution +{ + internal sealed class MethodInvokeInfo + { + public IntPtr LdFtnResult { get; set; } + public IntPtr DynamicInvokeMethod { get; set; } + public IntPtr DynamicInvokeGenericDictionary { get; set; } + public MethodBase MethodInfo { get; set; } + public IntPtr VirtualResolveData { get; set; } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/InstanceMethodInvoker.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/InstanceMethodInvoker.cs new file mode 100644 index 00000000000000..054d8cde67cc8d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/InstanceMethodInvoker.cs @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; +using global::System.Threading; +using global::System.Reflection; +using System.Runtime.InteropServices; +using global::System.Diagnostics; +using global::System.Collections.Generic; + +using global::Internal.Runtime.Augments; +using global::Internal.Reflection.Execution; +using global::Internal.Reflection.Core.Execution; +using global::Internal.Runtime.CompilerServices; + +namespace Internal.Reflection.Execution.MethodInvokers +{ + // + // Implements Invoke() for non-virtual instance methods. + // + internal sealed class InstanceMethodInvoker : MethodInvokerWithMethodInvokeInfo + { + public InstanceMethodInvoker(MethodInvokeInfo methodInvokeInfo, RuntimeTypeHandle declaringTypeHandle) + : base(methodInvokeInfo) + { + _declaringTypeHandle = declaringTypeHandle; + } + + [DebuggerGuidedStepThroughAttribute] + protected sealed override object Invoke(object thisObject, object[] arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException) + { + ValidateThis(thisObject, _declaringTypeHandle); + object result = RuntimeAugments.CallDynamicInvokeMethod( + thisObject, + MethodInvokeInfo.LdFtnResult, + MethodInvokeInfo.DynamicInvokeMethod, + MethodInvokeInfo.DynamicInvokeGenericDictionary, + MethodInvokeInfo.MethodInfo, + arguments, + binderBundle, + wrapInTargetInvocationException: wrapInTargetInvocationException, + methodToCallIsThisCall: true); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + return result; + } + + public sealed override Delegate CreateDelegate(RuntimeTypeHandle delegateType, object target, bool isStatic, bool isVirtual, bool isOpen) + { + if (isOpen) + { + MethodInfo methodInfo = (MethodInfo)MethodInvokeInfo.MethodInfo; + + short resolveType = OpenMethodResolver.OpenNonVirtualResolve; + + if (methodInfo.DeclaringType.IsValueType && !methodInfo.IsStatic) + { + // Open instance method for valuetype + resolveType = OpenMethodResolver.OpenNonVirtualResolveLookthruUnboxing; + } + + return RuntimeAugments.CreateDelegate( + delegateType, + new OpenMethodResolver(_declaringTypeHandle, MethodInvokeInfo.LdFtnResult, default(GCHandle), 0, resolveType).ToIntPtr(), + target, + isStatic: isStatic, + isOpen: isOpen); + } + else + { + return base.CreateDelegate(delegateType, target, isStatic, isVirtual, isOpen); + } + } + + public sealed override IntPtr LdFtnResult => MethodInvokeInfo.LdFtnResult; + + private RuntimeTypeHandle _declaringTypeHandle; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/MethodInvokerWithMethodInvokeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/MethodInvokerWithMethodInvokeInfo.cs new file mode 100644 index 00000000000000..4e292fc2ea346d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/MethodInvokerWithMethodInvokeInfo.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; +using global::System.Reflection; + +using global::Internal.Runtime.Augments; +using global::Internal.Reflection.Core.Execution; + +using global::Internal.Metadata.NativeFormat; +using System.Reflection.Runtime.General; + +namespace Internal.Reflection.Execution.MethodInvokers +{ + internal abstract class MethodInvokerWithMethodInvokeInfo : MethodInvoker + { + public MethodInvokerWithMethodInvokeInfo(MethodInvokeInfo methodInvokeInfo) + { + MethodInvokeInfo = methodInvokeInfo; + } + + public override Delegate CreateDelegate(RuntimeTypeHandle delegateType, object target, bool isStatic, bool isVirtual, bool isOpen) + { + return RuntimeAugments.CreateDelegate( + delegateType, + MethodInvokeInfo.LdFtnResult, + target, + isStatic: isStatic, + isOpen: isOpen); + } + + // + // Creates the appropriate flavor of Invoker depending on the calling convention "shape" (static, instance or virtual.) + // + internal static MethodInvoker CreateMethodInvoker(RuntimeTypeHandle declaringTypeHandle, QMethodDefinition methodHandle, MethodInvokeInfo methodInvokeInfo) + { + bool isStatic = false; + + if (methodHandle.IsNativeFormatMetadataBased) + { + Method method = methodHandle.NativeFormatHandle.GetMethod(methodHandle.NativeFormatReader); + MethodAttributes methodAttributes = method.Flags; + if (0 != (methodAttributes & MethodAttributes.Static)) + isStatic = true; + } +#if ECMA_METADATA_SUPPORT + if (methodHandle.IsEcmaFormatMetadataBased) + { + var reader = methodHandle.EcmaFormatReader; + var method = reader.GetMethodDefinition(methodHandle.EcmaFormatHandle); + var blobReader = reader.GetBlobReader(method.Signature); + byte sigByte = blobReader.ReadByte(); + if ((sigByte & (byte)System.Reflection.Metadata.SignatureAttributes.Instance) == 0) + isStatic = true; + } +#endif + + if (isStatic) + return new StaticMethodInvoker(methodInvokeInfo); + else if (methodInvokeInfo.VirtualResolveData != IntPtr.Zero) + return new VirtualMethodInvoker(methodInvokeInfo, declaringTypeHandle); + else + return new InstanceMethodInvoker(methodInvokeInfo, declaringTypeHandle); + } + + protected MethodInvokeInfo MethodInvokeInfo { get; private set; } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StaticMethodInvoker.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StaticMethodInvoker.cs new file mode 100644 index 00000000000000..0907ee14a9412c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StaticMethodInvoker.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; +using global::System.Threading; +using global::System.Reflection; +using global::System.Diagnostics; +using global::System.Collections.Generic; + +using global::Internal.Runtime.Augments; +using global::Internal.Reflection.Execution; +using global::Internal.Reflection.Core.Execution; + +namespace Internal.Reflection.Execution.MethodInvokers +{ + // + // Implements Invoke() for static methods. + // + internal sealed class StaticMethodInvoker : MethodInvokerWithMethodInvokeInfo + { + public StaticMethodInvoker(MethodInvokeInfo methodInvokeInfo) + : base(methodInvokeInfo) + { + } + + [DebuggerGuidedStepThroughAttribute] + protected sealed override object Invoke(object thisObject, object[] arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException) + { + object result = RuntimeAugments.CallDynamicInvokeMethod( + thisObject, + MethodInvokeInfo.LdFtnResult, + MethodInvokeInfo.DynamicInvokeMethod, + MethodInvokeInfo.DynamicInvokeGenericDictionary, + MethodInvokeInfo.MethodInfo, + arguments, + binderBundle, + wrapInTargetInvocationException: wrapInTargetInvocationException, + methodToCallIsThisCall: false); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + return result; + } + + public sealed override IntPtr LdFtnResult => MethodInvokeInfo.LdFtnResult; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/VirtualMethodInvoker.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/VirtualMethodInvoker.cs new file mode 100644 index 00000000000000..14382854914ef1 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/VirtualMethodInvoker.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; +using global::System.Threading; +using global::System.Reflection; +using global::System.Diagnostics; +using global::System.Collections.Generic; + +using global::Internal.Runtime.Augments; +using global::Internal.Runtime.CompilerServices; +using global::Internal.Reflection.Execution; +using global::Internal.Reflection.Core.Execution; + +namespace Internal.Reflection.Execution.MethodInvokers +{ + // + // Implements Invoke() for virtual methods on interfaces. + // + internal sealed class VirtualMethodInvoker : MethodInvokerWithMethodInvokeInfo + { + public VirtualMethodInvoker(MethodInvokeInfo methodInvokeInfo, RuntimeTypeHandle declaringTypeHandle) + : base(methodInvokeInfo) + { + _declaringTypeHandle = declaringTypeHandle; + } + + public sealed override Delegate CreateDelegate(RuntimeTypeHandle delegateType, object target, bool isStatic, bool isVirtual, bool isOpen) + { + if (!isOpen) + { + // We're creating a delegate to a virtual override of this method, so resolve the virtual now. + IntPtr resolvedVirtual = OpenMethodResolver.ResolveMethod(MethodInvokeInfo.VirtualResolveData, target); + return RuntimeAugments.CreateDelegate( + delegateType, + resolvedVirtual, + target, + isStatic: false, + isOpen: isOpen); + } + else + { + // Create an open virtual method by providing the virtual resolver to the delegate type. + return RuntimeAugments.CreateDelegate( + delegateType, + MethodInvokeInfo.VirtualResolveData, + target, + isStatic: false, + isOpen: isOpen); + } + } + + [DebuggerGuidedStepThroughAttribute] + protected sealed override object Invoke(object thisObject, object[] arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException) + { + ValidateThis(thisObject, _declaringTypeHandle); + + IntPtr resolvedVirtual = OpenMethodResolver.ResolveMethod(MethodInvokeInfo.VirtualResolveData, thisObject); + + object result = RuntimeAugments.CallDynamicInvokeMethod( + thisObject, + resolvedVirtual, + MethodInvokeInfo.DynamicInvokeMethod, + MethodInvokeInfo.DynamicInvokeGenericDictionary, + MethodInvokeInfo.MethodInfo, + arguments, + binderBundle, + wrapInTargetInvocationException: wrapInTargetInvocationException, + methodToCallIsThisCall: true); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + return result; + } + + internal IntPtr ResolveTarget(RuntimeTypeHandle type) + { + return OpenMethodResolver.ResolveMethod(MethodInvokeInfo.VirtualResolveData, type); + } + + // On CoreCLR/Desktop, we do not attempt to resolve the target virtual method based on the type of the 'this' pointer. + // For compatibility reasons, we'll do the same here. + public sealed override IntPtr LdFtnResult + { + get + { + if (RuntimeAugments.IsInterface(_declaringTypeHandle)) + throw new PlatformNotSupportedException(); + + // Must be an abstract method + if (MethodInvokeInfo.LdFtnResult == IntPtr.Zero && MethodInvokeInfo.VirtualResolveData != IntPtr.Zero) + throw new PlatformNotSupportedException(); + + return MethodInvokeInfo.LdFtnResult; + } + } + + private RuntimeTypeHandle _declaringTypeHandle; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/NativeFormatEnumInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/NativeFormatEnumInfo.cs new file mode 100644 index 00000000000000..4ce2606b0ee4a2 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/NativeFormatEnumInfo.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Reflection.Runtime.General; + +using Internal.Runtime.Augments; +using Internal.Metadata.NativeFormat; + +namespace Internal.Reflection.Execution +{ + static class NativeFormatEnumInfo + { + public static EnumInfo Create(RuntimeTypeHandle typeHandle, MetadataReader reader, TypeDefinitionHandle typeDefHandle) + { + TypeDefinition typeDef = reader.GetTypeDefinition(typeDefHandle); + + // Count the number of static fields. The single instance field may or may not have metadata, + // so using `typeDef.Fields.Count - 1` is not reliable. + int staticFieldCount = 0; + foreach (FieldHandle fieldHandle in typeDef.Fields) + { + Field field = fieldHandle.GetField(reader); + if (0 != (field.Flags & FieldAttributes.Static)) + { + staticFieldCount++; + } + } + + string[] names = new string[staticFieldCount]; + object[] values = new object[staticFieldCount]; + + int i = 0; + foreach (FieldHandle fieldHandle in typeDef.Fields) + { + Field field = fieldHandle.GetField(reader); + if (0 != (field.Flags & FieldAttributes.Static)) + { + names[i] = field.Name.GetString(reader); + values[i] = field.DefaultValue.ParseConstantNumericValue(reader); + i++; + } + } + + bool isFlags = false; + foreach (CustomAttributeHandle cah in typeDef.CustomAttributes) + { + if (cah.IsCustomAttributeOfType(reader, "System", "FlagsAttribute")) + isFlags = true; + } + + return new EnumInfo(RuntimeAugments.GetEnumUnderlyingType(typeHandle), values, names, isFlags); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/DiagnosticMappingTables.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/DiagnosticMappingTables.cs new file mode 100644 index 00000000000000..0116d9a4c9e99b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/DiagnosticMappingTables.cs @@ -0,0 +1,180 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; +using global::System.Text; +using global::System.Collections.Generic; + +using global::Internal.Metadata.NativeFormat; + +using global::Internal.Runtime.Augments; + +using System.Reflection.Runtime.General; + +namespace Internal.Reflection.Execution.PayForPlayExperience +{ + internal static partial class DiagnosticMappingTables + { + // Get the diagnostic name string for a type. This attempts to reformat the string into something that is essentially human readable. + // Returns true if the function is successful. + // runtimeTypeHandle represents the type to get a name for + // diagnosticName is the name that is returned + // + // the genericParameterOffsets list is an optional parameter that contains the list of the locations of where generic parameters may be inserted + // to make the string represent an instantiated generic. + // + // For example for Dictionary, metadata names the type Dictionary`2, but this function will return Dictionary<,> + // For consumers of this function that will be inserting generic arguments, the genericParameterOffsets list is used to find where to insert the generic parameter name. + // + // That isn't all that interesting for Dictionary, but it becomes substantially more interesting for nested generic types, or types which are compiler named as + // those may contain embedded <> pairs and such. + public static bool TryGetDiagnosticStringForNamedType(RuntimeTypeHandle runtimeTypeHandle, out string diagnosticName, List genericParameterOffsets) + { + diagnosticName = null; + ExecutionEnvironmentImplementation executionEnvironment = ReflectionExecution.ExecutionEnvironment; + + MetadataReader reader; + TypeReferenceHandle typeReferenceHandle; + if (executionEnvironment.TryGetTypeReferenceForNamedType(runtimeTypeHandle, out reader, out typeReferenceHandle)) + { + diagnosticName = GetTypeFullNameFromTypeRef(typeReferenceHandle, reader, genericParameterOffsets); + return true; + } + + QTypeDefinition qTypeDefinition; + if (executionEnvironment.TryGetMetadataForNamedType(runtimeTypeHandle, out qTypeDefinition)) + { + TryGetFullNameFromTypeDefEcma(qTypeDefinition, genericParameterOffsets, ref diagnosticName); + if (diagnosticName != null) + return true; + + if (qTypeDefinition.IsNativeFormatMetadataBased) + { + TypeDefinitionHandle typeDefinitionHandle = qTypeDefinition.NativeFormatHandle; + diagnosticName = GetTypeFullNameFromTypeDef(typeDefinitionHandle, qTypeDefinition.NativeFormatReader, genericParameterOffsets); + return true; + } + } + return false; + } + + static partial void TryGetFullNameFromTypeDefEcma(QTypeDefinition qTypeDefinition, List genericParameterOffsets, ref string result); + + private static string GetTypeFullNameFromTypeRef(TypeReferenceHandle typeReferenceHandle, MetadataReader reader, List genericParameterOffsets) + { + string s = ""; + + TypeReference typeReference = typeReferenceHandle.GetTypeReference(reader); + s = typeReference.TypeName.GetString(reader); + Handle parentHandle = typeReference.ParentNamespaceOrType; + HandleType parentHandleType = parentHandle.HandleType; + if (parentHandleType == HandleType.TypeReference) + { + string containingTypeName = GetTypeFullNameFromTypeRef(parentHandle.ToTypeReferenceHandle(reader), reader, genericParameterOffsets); + s = containingTypeName + "." + s; + } + else if (parentHandleType == HandleType.NamespaceReference) + { + NamespaceReferenceHandle namespaceReferenceHandle = parentHandle.ToNamespaceReferenceHandle(reader); + for (;;) + { + NamespaceReference namespaceReference = namespaceReferenceHandle.GetNamespaceReference(reader); + string namespacePart = namespaceReference.Name.GetStringOrNull(reader); + if (namespacePart == null) + break; // Reached the root namespace. + s = namespacePart + "." + s; + if (namespaceReference.ParentScopeOrNamespace.HandleType != HandleType.NamespaceReference) + break; // Should have reached the root namespace first but this helper is for ToString() - better to + // return partial information than crash. + namespaceReferenceHandle = namespaceReference.ParentScopeOrNamespace.ToNamespaceReferenceHandle(reader); + } + } + else + { + // If we got here, the metadata is illegal but this helper is for ToString() - better to + // return something partial than throw. + } + return ConvertBackTickNameToNameWithReducerInputFormat(s, genericParameterOffsets); + } + + public static string ConvertBackTickNameToNameWithReducerInputFormat(string typename, List genericParameterOffsets) + { + int indexOfBackTick = typename.LastIndexOf('`'); + if (indexOfBackTick != -1) + { + string typeNameSansBackTick = typename.Substring(0, indexOfBackTick); + if ((indexOfBackTick + 1) < typename.Length) + { + string textAfterBackTick = typename.Substring(indexOfBackTick + 1); + int genericParameterCount; + if (int.TryParse(textAfterBackTick, out genericParameterCount) && (genericParameterCount > 0)) + { + // Replace the `Number with <,,,> where the count of ',' is one less than Number. + StringBuilder genericTypeName = new StringBuilder(); + genericTypeName.Append(typeNameSansBackTick); + genericTypeName.Append('<'); + if (genericParameterOffsets != null) + { + genericParameterOffsets.Add(genericTypeName.Length); + } + for (int i = 1; i < genericParameterCount; i++) + { + genericTypeName.Append(','); + if (genericParameterOffsets != null) + { + genericParameterOffsets.Add(genericTypeName.Length); + } + } + genericTypeName.Append('>'); + return genericTypeName.ToString(); + } + } + } + return typename; + } + + private static string GetTypeFullNameFromTypeDef(TypeDefinitionHandle typeDefinitionHandle, MetadataReader reader, List genericParameterOffsets) + { + string s = ""; + + TypeDefinition typeDefinition = typeDefinitionHandle.GetTypeDefinition(reader); + s = typeDefinition.Name.GetString(reader); + + TypeDefinitionHandle enclosingTypeDefHandle = typeDefinition.EnclosingType; + if (!enclosingTypeDefHandle.IsNull(reader)) + { + string containingTypeName = GetTypeFullNameFromTypeDef(enclosingTypeDefHandle, reader, genericParameterOffsets); + s = containingTypeName + "." + s; + } + else + { + NamespaceDefinitionHandle namespaceHandle = typeDefinition.NamespaceDefinition; + for (;;) + { + NamespaceDefinition namespaceDefinition = namespaceHandle.GetNamespaceDefinition(reader); + string namespacePart = namespaceDefinition.Name.GetStringOrNull(reader); + if (namespacePart == null) + break; // Reached the root namespace. + s = namespacePart + "." + s; + if (namespaceDefinition.ParentScopeOrNamespace.HandleType != HandleType.NamespaceDefinition) + break; // Should have reached the root namespace first but this helper is for ToString() - better to + // return partial information than crash. + namespaceHandle = namespaceDefinition.ParentScopeOrNamespace.ToNamespaceDefinitionHandle(reader); + } + } + return ConvertBackTickNameToNameWithReducerInputFormat(s, genericParameterOffsets); + } + + public static bool TryGetArrayTypeElementType(RuntimeTypeHandle arrayTypeHandle, out RuntimeTypeHandle elementTypeHandle) + { + elementTypeHandle = RuntimeAugments.GetRelatedParameterTypeHandle(arrayTypeHandle); + return true; + } + + public static bool TryGetPointerTypeTargetType(RuntimeTypeHandle pointerTypeHandle, out RuntimeTypeHandle targetTypeHandle) + { + targetTypeHandle = RuntimeAugments.GetRelatedParameterTypeHandle(pointerTypeHandle); + return true; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/MissingMetadataExceptionCreator.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/MissingMetadataExceptionCreator.cs new file mode 100644 index 00000000000000..37adf30ec26464 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/MissingMetadataExceptionCreator.cs @@ -0,0 +1,323 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; +using global::System.Text; +using global::System.Reflection; +using global::System.Diagnostics; +using global::System.Collections.Generic; + +using global::Internal.Runtime.Augments; + +using global::Internal.Reflection.Core.Execution; + +namespace Internal.Reflection.Execution.PayForPlayExperience +{ + public static class MissingMetadataExceptionCreator + { + internal static MissingMetadataException Create(string resourceId, MemberInfo pertainant) + { + return CreateFromMetadataObject(resourceId, pertainant); + } + + internal static MissingMetadataException Create(TypeInfo pertainant) + { + return CreateFromMetadataObject(SR.Reflection_InsufficientMetadata_EdbNeeded, pertainant); + } + + internal static MissingMetadataException Create(TypeInfo pertainant, string nestedTypeName) + { + if (pertainant == null) + return new MissingMetadataException(SR.Format(SR.Reflection_InsufficientMetadata_NoHelpAvailable, "")); + + string usefulPertainant = ComputeUsefulPertainantIfPossible(pertainant); + if (usefulPertainant == null) + return new MissingMetadataException(Format(SR.Reflection_InsufficientMetadata_NoHelpAvailable, pertainant.ToString())); + else + { + usefulPertainant = usefulPertainant + "." + DiagnosticMappingTables.ConvertBackTickNameToNameWithReducerInputFormat(nestedTypeName, null); + return new MissingMetadataException(Format(SR.Reflection_InsufficientMetadata_EdbNeeded, usefulPertainant)); + } + } + + internal static MissingMetadataException Create(Type pertainant) + { + return CreateFromMetadataObject(SR.Reflection_InsufficientMetadata_EdbNeeded, pertainant); + } + + internal static MissingMetadataException Create(RuntimeTypeHandle pertainant) + { + return CreateFromMetadataObject(SR.Reflection_InsufficientMetadata_EdbNeeded, pertainant); + } + + private static MissingMetadataException CreateFromString(string pertainant) + { + if (pertainant == null) + return new MissingMetadataException(SR.Format(SR.Reflection_InsufficientMetadata_NoHelpAvailable, "")); + else + return new MissingMetadataException(Format(SR.Reflection_InsufficientMetadata_EdbNeeded, pertainant)); + } + + internal static MissingMetadataException CreateMissingArrayTypeException(Type elementType, bool isMultiDim, int rank) + { + Debug.Assert(rank == 1 || isMultiDim); + string s = CreateArrayTypeStringIfAvailable(elementType, rank); + return CreateFromString(s); + } + + internal static MissingMetadataException CreateMissingConstructedGenericTypeException(Type genericTypeDefinition, Type[] genericTypeArguments) + { + string s = CreateConstructedGenericTypeStringIfAvailable(genericTypeDefinition, genericTypeArguments); + return CreateFromString(s); + } + + internal static MissingMetadataException CreateFromMetadataObject(string resourceId, object pertainant) + { + if (pertainant == null) + return new MissingMetadataException(SR.Format(SR.Reflection_InsufficientMetadata_NoHelpAvailable, "")); + + string usefulPertainant = ComputeUsefulPertainantIfPossible(pertainant); + if (usefulPertainant == null) + return new MissingMetadataException(Format(SR.Reflection_InsufficientMetadata_NoHelpAvailable, pertainant.ToString())); + else + return new MissingMetadataException(Format(resourceId, usefulPertainant)); + } + + public static string ComputeUsefulPertainantIfPossible(object pertainant) + { + { + Type type = null; + + if (pertainant is TypeInfo) + type = ((TypeInfo)pertainant).AsType(); + else if (pertainant is Type) + type = (Type)pertainant; + else if (pertainant is RuntimeTypeHandle) + type = Type.GetTypeFromHandle((RuntimeTypeHandle)pertainant); + + if (type != null) + return type.ToDisplayStringIfAvailable(null); + } + + if (pertainant is MemberInfo) + { + MemberInfo memberInfo = (MemberInfo)pertainant; + + StringBuilder friendlyName = new StringBuilder(memberInfo.DeclaringType.ToDisplayStringIfAvailable(null)); + friendlyName.Append('.'); + friendlyName.Append(memberInfo.Name); + if (pertainant is MethodBase) + { + MethodBase method = (MethodBase)pertainant; + bool first = true; + + // write out generic parameters + if (method.IsConstructedGenericMethod) + { + first = true; + friendlyName.Append('<'); + foreach (Type genericParameter in method.GetGenericArguments()) + { + if (!first) + friendlyName.Append(','); + + first = false; + friendlyName.Append(genericParameter.ToDisplayStringIfAvailable(null)); + } + friendlyName.Append('>'); + } + + // write out actual parameters + friendlyName.Append('('); + first = true; + foreach (ParameterInfo parameter in method.GetParametersNoCopy()) + { + if (!first) + friendlyName.Append(','); + + first = false; + if (parameter.IsOut && parameter.IsIn) + { + friendlyName.Append("ref "); + } + else if (parameter.IsOut) + { + friendlyName.Append("out "); + } + + Type parameterType = parameter.ParameterType; + if (parameterType.IsByRef) + { + parameterType = parameterType.GetElementType(); + } + + friendlyName.Append(parameter.ParameterType.ToDisplayStringIfAvailable(null)); + } + friendlyName.Append(')'); + } + + return friendlyName.ToString(); + } + + return null; //Give up + } + + internal static string ToDisplayStringIfAvailable(this Type type, List genericParameterOffsets) + { + RuntimeTypeHandle runtimeTypeHandle = ReflectionCoreExecution.ExecutionDomain.GetTypeHandleIfAvailable(type); + bool hasRuntimeTypeHandle = !runtimeTypeHandle.Equals(default(RuntimeTypeHandle)); + + if (type.HasElementType) + { + if (type.IsArray) + { + // Multidim arrays. This is the one case where GetElementType() isn't pay-for-play safe so + // talk to the diagnostic mapping tables directly if possible or give up. + if (!hasRuntimeTypeHandle) + return null; + + int rank = type.GetArrayRank(); + return CreateArrayTypeStringIfAvailable(type.GetElementType(), rank); + } + else + { + string s = type.GetElementType().ToDisplayStringIfAvailable(null); + if (s == null) + return null; + return s + (type.IsPointer ? "*" : "&"); + } + } + else if (((hasRuntimeTypeHandle && RuntimeAugments.IsGenericType(runtimeTypeHandle)) || type.IsConstructedGenericType)) + { + Type genericTypeDefinition; + Type[] genericTypeArguments; + if (hasRuntimeTypeHandle) + { + RuntimeTypeHandle genericTypeDefinitionHandle; + RuntimeTypeHandle[] genericTypeArgumentHandles; + + genericTypeDefinitionHandle = RuntimeAugments.GetGenericInstantiation(runtimeTypeHandle, out genericTypeArgumentHandles); + genericTypeDefinition = Type.GetTypeFromHandle(genericTypeDefinitionHandle); + genericTypeArguments = new Type[genericTypeArgumentHandles.Length]; + for (int i = 0; i < genericTypeArguments.Length; i++) + genericTypeArguments[i] = Type.GetTypeFromHandle(genericTypeArgumentHandles[i]); + } + else + { + genericTypeDefinition = type.GetGenericTypeDefinition(); + genericTypeArguments = type.GenericTypeArguments; + } + + return CreateConstructedGenericTypeStringIfAvailable(genericTypeDefinition, genericTypeArguments); + } + else if (type.IsGenericParameter) + { + return type.Name; + } + else if (hasRuntimeTypeHandle) + { + string s; + if (!DiagnosticMappingTables.TryGetDiagnosticStringForNamedType(runtimeTypeHandle, out s, genericParameterOffsets)) + return null; + + return s; + } + else + { + // First, see if Type.Name is available. If Type.Name is available, then we can be reasonably confident that it is safe to call Type.FullName. + // We'll still wrap the call in a try-catch as a failsafe. + string s = type.InternalNameIfAvailable; + if (s == null) + return null; + + try + { + s = type.FullName; + } + catch (MissingMetadataException) + { + } + + // Insert commas so that CreateConstructedGenericTypeStringIfAvailable can fill the blanks. + // This is not strictly correct for types nested under generic types, but at this point we're doing + // best effort within reason. + if (type.IsGenericTypeDefinition) + { + s += "["; + int genericArgCount = type.GetGenericArguments().Length; + while (genericArgCount-- > 0) + { + genericParameterOffsets.Add(s.Length); + if (genericArgCount > 0) + s = s + ","; + } + s += "]"; + } + + return s; + } + } + + private static string CreateArrayTypeStringIfAvailable(Type elementType, int rank) + { + string s = elementType.ToDisplayStringIfAvailable(null); + if (s == null) + return null; + + return s + "[" + new string(',', rank - 1) + "]"; // This does not bother to display multidims of rank 1 correctly since we bail on that case in the prior statement. + } + + private static string CreateConstructedGenericTypeStringIfAvailable(Type genericTypeDefinition, Type[] genericTypeArguments) + { + List genericParameterOffsets = new List(); + string genericTypeDefinitionString = genericTypeDefinition.ToDisplayStringIfAvailable(genericParameterOffsets); + + if (genericTypeDefinitionString == null) + return null; + + // If we found too many generic arguments to insert things, strip out the excess. This is wrong, but also, nothing is right. + if (genericTypeArguments.Length < genericParameterOffsets.Count) + { + genericParameterOffsets.RemoveRange(genericTypeArguments.Length, genericParameterOffsets.Count - genericTypeArguments.Length); + } + // Similarly, if we found too few, add them at the end. + while (genericTypeArguments.Length > genericParameterOffsets.Count) + { + genericTypeDefinitionString = genericTypeDefinitionString + ","; + genericParameterOffsets.Add(genericTypeDefinitionString.Length); + } + + // Ensure the list is sorted in ascending order + genericParameterOffsets.Sort(); + + // The s string Now contains a string like "Namespace.MoreNamespace.TypeName.NestedGenericType<,,>.MoreNestedGenericType<>" + // where the generic parameters locations are recorded in genericParameterOffsets + // Walk backwards through the generic parameter locations, filling in as needed. + StringBuilder genericTypeName = new StringBuilder(genericTypeDefinitionString); + for (int i = genericParameterOffsets.Count - 1; i >= 0; --i) + { + genericTypeName.Insert(genericParameterOffsets[i], genericTypeArguments[i].ToDisplayStringIfAvailable(null)); + } + + return genericTypeName.ToString(); + } + + // + // This is a workaround to prevent crucial information being lost when compiling console apps using the retail ILC. + // This combination turns rich error messages from the framework into resource keys without the substitution strings. + // We'll detect this case here and append the substitution string manually. + // + private static string Format(string resourceMessage, object parameter) + { + if (resourceMessage.Contains("{0}")) + return SR.Format(resourceMessage, parameter); + + // If the rich exception message was eaten by the IL2IL transform, make sure the resulting message + // has a link pointing the user towards the .NET Native debugging guide. These get normally appended + // to the restricted message by the transform, but the pattern here is not recognized by the rewriter. + // At this point we know the message doesn't come from resources (because message == resource key), so + // we can't do much to make this localizable. + return resourceMessage + ": " + parameter + ". For more information, visit http://go.microsoft.com/fwlink/?LinkId=623485"; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionDomainSetupImplementation.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionDomainSetupImplementation.cs new file mode 100644 index 00000000000000..efc5de5f13f301 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionDomainSetupImplementation.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; +using global::System.Reflection; + +using global::Internal.Reflection.Core; +using global::Internal.Reflection.Execution.PayForPlayExperience; + +namespace Internal.Reflection.Execution +{ + //========================================================================================================================= + // The setup information for the reflection domain used for Project N's "classic reflection". + //========================================================================================================================= + internal sealed class ReflectionDomainSetupImplementation : ReflectionDomainSetup + { + public ReflectionDomainSetupImplementation() + { + } + + // Obtain it lazily to avoid using RuntimeAugments.Callbacks before it is initialized + public sealed override AssemblyBinder AssemblyBinder => AssemblyBinderImplementation.Instance; + + public sealed override Exception CreateMissingMetadataException(TypeInfo pertainant) + { + return MissingMetadataExceptionCreator.Create(pertainant); + } + + public sealed override Exception CreateMissingMetadataException(Type pertainant) + { + return MissingMetadataExceptionCreator.Create(pertainant); + } + + public sealed override Exception CreateMissingMetadataException(TypeInfo pertainant, string nestedTypeName) + { + return MissingMetadataExceptionCreator.Create(pertainant, nestedTypeName); + } + + public sealed override Exception CreateNonInvokabilityException(MemberInfo pertainant) + { + string resourceName = SR.Object_NotInvokable; + + if (pertainant is MethodBase methodBase) + { + resourceName = methodBase.IsConstructedGenericMethod ? SR.MakeGenericMethod_NoMetadata : SR.Object_NotInvokable; + if (methodBase is ConstructorInfo) + { + TypeInfo declaringTypeInfo = methodBase.DeclaringType.GetTypeInfo(); + if (typeof(Delegate).GetTypeInfo().IsAssignableFrom(declaringTypeInfo)) + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CannotInvokeDelegateCtor); + } + } + + string pertainantString = MissingMetadataExceptionCreator.ComputeUsefulPertainantIfPossible(pertainant); + return new MissingRuntimeArtifactException(SR.Format(resourceName, pertainantString ?? "?")); + } + + public sealed override Exception CreateMissingArrayTypeException(Type elementType, bool isMultiDim, int rank) + { + return MissingMetadataExceptionCreator.CreateMissingArrayTypeException(elementType, isMultiDim, rank); + } + + public sealed override Exception CreateMissingConstructedGenericTypeException(Type genericTypeDefinition, Type[] genericTypeArguments) + { + return MissingMetadataExceptionCreator.CreateMissingConstructedGenericTypeException(genericTypeDefinition, genericTypeArguments); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs new file mode 100644 index 00000000000000..9ac138adf345d5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Internal.Reflection.Execution +// ------------------------------------------------- +// Why does this exist?: +// Unlike the desktop, RH uses Internal.Reflection.Core for +// "classic reflection" emulation as well as LMR, using +// the Internal.Reflection.Core.Execution contract. +// +// Internal.Reflection.Core.Execution has an abstract model +// for an "execution engine" - this contract provides the +// concrete implementation of this model for Redhawk. +// +// +// Implemented by: +// Reflection.Execution.dll on RH +// N/A on desktop: +// +// Consumed by: +// Redhawk app's directly via an under-the-hood ILTransform. +// System.Private.CoreLib.dll, via a callback (see Internal.System.Runtime.Augment) +// + +using global::System; +using global::System.Collections.Generic; +using global::System.Reflection; +using global::System.Reflection.Runtime.General; + +using global::Internal.Runtime.Augments; + +using global::Internal.Reflection.Core; +using global::Internal.Reflection.Core.Execution; +using global::Internal.Metadata.NativeFormat; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Reflection.Execution +{ + public static class ReflectionExecution + { + /// + /// Eager initialization of runtime reflection support. As part of ExecutionEnvironmentImplementation + /// initialization it enumerates the modules and registers the ones containing EmbeddedMetadata reflection blobs + /// in its _moduleToMetadataReader map. + /// + internal static void Initialize() + { + // Initialize Reflection.Core's one and only ExecutionDomain. + var executionEnvironment = new ExecutionEnvironmentImplementation(); + var setup = new ReflectionDomainSetupImplementation(); + ReflectionCoreExecution.InitializeExecutionDomain(setup, executionEnvironment); + + // Initialize our two-way communication with System.Private.CoreLib. + ExecutionDomain executionDomain = ReflectionCoreExecution.ExecutionDomain; + var runtimeCallbacks = new ReflectionExecutionDomainCallbacksImplementation(executionDomain, executionEnvironment); + RuntimeAugments.Initialize(runtimeCallbacks); + + ExecutionEnvironment = executionEnvironment; + } + + // + // This entry is targeted by the ILTransformer to implement Type.GetType()'s ability to detect the calling assembly and use it as + // a default assembly name. + // + public static Type GetType(string typeName, string callingAssemblyName, bool throwOnError, bool ignoreCase) + { + return ExtensibleGetType(typeName, callingAssemblyName, null, null, throwOnError: throwOnError, ignoreCase: ignoreCase); + } + + // + // This entry is targeted by the ILTransformer to implement Type.GetType()'s ability to detect the calling assembly and use it as + // a default assembly name. + // + public static Type ExtensibleGetType(string typeName, string callingAssemblyName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase) + { + LowLevelListWithIList defaultAssemblies = new LowLevelListWithIList(); + defaultAssemblies.Add(callingAssemblyName); + defaultAssemblies.Add(AssemblyBinder.DefaultAssemblyNameForGetType); + return ReflectionCoreExecution.ExecutionDomain.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, defaultAssemblies); + } + + public static bool TryGetMethodMetadataFromStartAddress(IntPtr methodStartAddress, out MetadataReader reader, out TypeDefinitionHandle typeHandle, out MethodHandle methodHandle) + { + reader = null; + typeHandle = default(TypeDefinitionHandle); + methodHandle = default(MethodHandle); + + // If ExecutionEnvironment is null, reflection must be disabled. + if (ExecutionEnvironment == null) + return false; + + RuntimeTypeHandle declaringTypeHandle = default(RuntimeTypeHandle); + if (!ExecutionEnvironment.TryGetMethodForStartAddress(methodStartAddress, + ref declaringTypeHandle, out QMethodDefinition qMethodDefinition)) + return false; + + if (!qMethodDefinition.IsNativeFormatMetadataBased) + return false; + + if (!ExecutionEnvironment.TryGetMetadataForNamedType(declaringTypeHandle, out QTypeDefinition qTypeDefinition)) + return false; + + Debug.Assert(qTypeDefinition.IsNativeFormatMetadataBased); + Debug.Assert(qTypeDefinition.NativeFormatReader == qMethodDefinition.NativeFormatReader); + + reader = qTypeDefinition.NativeFormatReader; + typeHandle = qTypeDefinition.NativeFormatHandle; + methodHandle = qMethodDefinition.NativeFormatHandle; + + return true; + } + + internal static ExecutionEnvironmentImplementation ExecutionEnvironment { get; private set; } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs new file mode 100644 index 00000000000000..991e19791f2b57 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs @@ -0,0 +1,201 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +using Internal.Runtime.Augments; + +using Internal.Reflection.Core; +using Internal.Reflection.Core.Execution; +using Internal.Reflection.Execution.PayForPlayExperience; +using Internal.Reflection.Extensions.NonPortable; + +using System.Reflection.Runtime.General; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Reflection.Execution +{ + //========================================================================================================================== + // This class provides various services down to System.Private.CoreLib. (Though we forward most or all of them directly up to Reflection.Core.) + //========================================================================================================================== + internal sealed class ReflectionExecutionDomainCallbacksImplementation : ReflectionExecutionDomainCallbacks + { + public ReflectionExecutionDomainCallbacksImplementation(ExecutionDomain executionDomain, ExecutionEnvironmentImplementation executionEnvironment) + { + _executionDomain = executionDomain; + _executionEnvironment = executionEnvironment; + } + + public sealed override Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, string defaultAssemblyName) + { + LowLevelListWithIList defaultAssemblies = new LowLevelListWithIList(); + if (defaultAssemblyName != null) + defaultAssemblies.Add(defaultAssemblyName); + defaultAssemblies.Add(AssemblyBinder.DefaultAssemblyNameForGetType); + return _executionDomain.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, defaultAssemblies); + } + + public sealed override bool IsReflectionBlocked(RuntimeTypeHandle typeHandle) + { + return _executionEnvironment.IsReflectionBlocked(typeHandle); + } + + //======================================================================================= + // This group of methods jointly service the Type.GetTypeFromHandle() path. The caller + // is responsible for analyzing the RuntimeTypeHandle to figure out which flavor to call. + //======================================================================================= + public sealed override Type GetNamedTypeForHandle(RuntimeTypeHandle typeHandle, bool isGenericTypeDefinition) + { + return _executionDomain.GetNamedTypeForHandle(typeHandle, isGenericTypeDefinition); + } + + public sealed override Type GetArrayTypeForHandle(RuntimeTypeHandle typeHandle) + { + return _executionDomain.GetArrayTypeForHandle(typeHandle); + } + + public sealed override Type GetMdArrayTypeForHandle(RuntimeTypeHandle typeHandle, int rank) + { + return _executionDomain.GetMdArrayTypeForHandle(typeHandle, rank); + } + + public sealed override Type GetPointerTypeForHandle(RuntimeTypeHandle typeHandle) + { + return _executionDomain.GetPointerTypeForHandle(typeHandle); + } + + public sealed override Type GetByRefTypeForHandle(RuntimeTypeHandle typeHandle) + { + return _executionDomain.GetByRefTypeForHandle(typeHandle); + } + + public sealed override Type GetConstructedGenericTypeForHandle(RuntimeTypeHandle typeHandle) + { + return _executionDomain.GetConstructedGenericTypeForHandle(typeHandle); + } + + //======================================================================================= + // MissingMetadataException support. + //======================================================================================= + public sealed override Exception CreateMissingMetadataException(Type pertainant) + { + return _executionDomain.CreateMissingMetadataException(pertainant); + } + + // This is called from the ToString() helper of a RuntimeType that does not have full metadata. + // This helper makes a "best effort" to give the caller something better than "EETypePtr nnnnnnnnn". + public sealed override string GetBetterDiagnosticInfoIfAvailable(RuntimeTypeHandle runtimeTypeHandle) + { + return Type.GetTypeFromHandle(runtimeTypeHandle).ToDisplayStringIfAvailable(null); + } + + public sealed override MethodBase GetMethodBaseFromStartAddressIfAvailable(IntPtr methodStartAddress) + { + RuntimeTypeHandle declaringTypeHandle = default(RuntimeTypeHandle); + QMethodDefinition methodHandle; + if (!ReflectionExecution.ExecutionEnvironment.TryGetMethodForStartAddress(methodStartAddress, + ref declaringTypeHandle, out methodHandle)) + { + return null; + } + + // We don't use the type argument handles as we want the uninstantiated method info + return ReflectionCoreExecution.ExecutionDomain.GetMethod(declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles: null); + } + + public sealed override Assembly GetAssemblyForHandle(RuntimeTypeHandle typeHandle) + { + return Type.GetTypeFromHandle(typeHandle).Assembly; + } + + public sealed override IntPtr TryGetStaticClassConstructionContext(RuntimeTypeHandle runtimeTypeHandle) + { + return _executionEnvironment.TryGetStaticClassConstructionContext(runtimeTypeHandle); + } + + /// + /// Retrieves the default value for a parameter of a method. + /// + /// The default parameters context used to invoke the method, + /// this should identify the method in question. This is passed to the RuntimeAugments.CallDynamicInvokeMethod. + /// The type of the parameter to retrieve. + /// The index of the parameter on the method to retrieve. + /// The default value of the parameter if available. + /// true if the default parameter value is available, otherwise false. + public sealed override bool TryGetDefaultParameterValue(object defaultParametersContext, RuntimeTypeHandle thType, int argIndex, out object defaultValue) + { + defaultValue = null; + + MethodBase methodBase = defaultParametersContext as MethodBase; + if (methodBase is null) + { + if (defaultParametersContext is Delegate) + { + methodBase = GetDelegateInvokeMethod(defaultParametersContext.GetType()); + } + + if (methodBase is null) + { + return false; + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Delegates always generate metadata for the Invoke method")] + static MethodBase GetDelegateInvokeMethod(Type delegateType) + { + MethodInfo result = delegateType.GetMethod("Invoke", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); + Debug.Assert(result != null); + return result; + } + } + + ParameterInfo parameterInfo = methodBase.GetParametersNoCopy()[argIndex]; + if (!parameterInfo.HasDefaultValue) + { + // If the parameter is optional, with no default value and we're asked for its default value, + // it means the caller specified Missing.Value as the value for the parameter. In this case the behavior + // is defined as passing in the Missing.Value, regardless of the parameter type. + // If Missing.Value is convertible to the parameter type, it will just work, otherwise we will fail + // due to type mismatch. + if (parameterInfo.IsOptional) + { + defaultValue = Missing.Value; + return true; + } + + return false; + } + + defaultValue = parameterInfo.DefaultValue; + return true; + } + + public sealed override RuntimeTypeHandle GetTypeHandleIfAvailable(Type type) + { + return _executionDomain.GetTypeHandleIfAvailable(type); + } + + public sealed override bool SupportsReflection(Type type) + { + return _executionDomain.SupportsReflection(type); + } + + public sealed override MethodInfo GetDelegateMethod(Delegate del) + { + return DelegateMethodInfoRetriever.GetDelegateMethodInfo(del); + } + + public sealed override Exception GetExceptionForHR(int hr) + { + return Marshal.GetExceptionForHR(hr); + } + + private ExecutionDomain _executionDomain; + private ExecutionEnvironmentImplementation _executionEnvironment; + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/RuntimeHandlesExtensions.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/RuntimeHandlesExtensions.cs new file mode 100644 index 00000000000000..be213572b83680 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/RuntimeHandlesExtensions.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using Internal.Runtime.Augments; + +namespace Internal.Reflection.Execution +{ + internal static class RuntimeHandlesExtensions + { + public static bool IsNull(this RuntimeTypeHandle rtth) + { + return RuntimeAugments.GetRuntimeTypeHandleRawValue(rtth) == IntPtr.Zero; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/ConstraintValidator.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/ConstraintValidator.cs new file mode 100644 index 00000000000000..d7e1a2158404c0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/ConstraintValidator.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection; +using Debug = global::System.Diagnostics.Debug; + +namespace Internal.Reflection.Execution +{ + internal static partial class ConstraintValidator + { + private static bool SatisfiesConstraints(this Type genericVariable, SigTypeContext typeContextOfConstraintDeclarer, Type typeArg) + { + GenericParameterAttributes specialConstraints = genericVariable.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask; + + if ((specialConstraints & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0) + { + if (!typeArg.IsValueType) + return false; + else + { + // the type argument is a value type, however if it is any kind of Nullable we want to fail + // as the constraint accepts any value type except Nullable types (Nullable itself is a value type) + if (typeArg.IsNullable()) + return false; + } + } + + if ((specialConstraints & GenericParameterAttributes.ReferenceTypeConstraint) != 0) + { + if (typeArg.IsValueType) + return false; + } + + if ((specialConstraints & GenericParameterAttributes.DefaultConstructorConstraint) != 0) + { + if (!typeArg.HasExplicitOrImplicitPublicDefaultConstructor()) + return false; + } + + // Now check general subtype constraints + foreach (var constraint in genericVariable.GetGenericParameterConstraints()) + { + Type instantiatedTypeConstraint = constraint.Instantiate(typeContextOfConstraintDeclarer); + + // System.Object constraint will be always satisfied - even if argList is empty + if (instantiatedTypeConstraint.IsSystemObject()) + continue; + + // if a concrete type can be cast to the constraint, then this constraint will be satisifed + if (!AreTypesAssignable(typeArg, instantiatedTypeConstraint)) + return false; + } + + return true; + } + + private static void EnsureSatisfiesClassConstraints(Type[] typeParameters, Type[] typeArguments, object definition, SigTypeContext typeContext) + { + if (typeParameters.Length != typeArguments.Length) + { + throw new ArgumentException(SR.Argument_GenericArgsCount); + } + + // Do sanity validation of all arguments first. The actual constraint validation can fail in unexpected ways + // if it hits SigTypeContext with these never valid types. + for (int i = 0; i < typeParameters.Length; i++) + { + Type actualArg = typeArguments[i]; + + if (actualArg.IsSystemVoid() || (actualArg.HasElementType && !actualArg.IsArray)) + { + throw new ArgumentException(SR.Format(SR.Argument_NeverValidGenericArgument, actualArg)); + } + } + + for (int i = 0; i < typeParameters.Length; i++) + { + Type formalArg = typeParameters[i]; + Type actualArg = typeArguments[i]; + + if (!formalArg.SatisfiesConstraints(typeContext, actualArg)) + { + throw new ArgumentException(SR.Format(SR.Argument_ConstraintFailed, actualArg, definition.ToString(), formalArg), + string.Format("GenericArguments[{0}]", i)); + } + } + } + + public static void EnsureSatisfiesClassConstraints(Type typeDefinition, Type[] typeArguments) + { + Type[] typeParameters = typeDefinition.GetGenericArguments(); + SigTypeContext typeContext = new SigTypeContext(typeArguments, null); + EnsureSatisfiesClassConstraints(typeParameters, typeArguments, typeDefinition, typeContext); + } + + public static void EnsureSatisfiesClassConstraints(MethodInfo reflectionMethodInfo) + { + MethodInfo genericMethodDefinition = reflectionMethodInfo.GetGenericMethodDefinition(); + Type[] methodArguments = reflectionMethodInfo.GetGenericArguments(); + Type[] methodParameters = genericMethodDefinition.GetGenericArguments(); + Type[] typeArguments = reflectionMethodInfo.DeclaringType.GetGenericArguments(); + SigTypeContext typeContext = new SigTypeContext(typeArguments, methodArguments); + EnsureSatisfiesClassConstraints(methodParameters, methodArguments, genericMethodDefinition, typeContext); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/ConstraintValidatorSupport.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/ConstraintValidatorSupport.cs new file mode 100644 index 00000000000000..1d0b5f42b61130 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/ConstraintValidatorSupport.cs @@ -0,0 +1,339 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; + +using Debug = global::System.Diagnostics.Debug; + +namespace Internal.Reflection.Execution +{ + internal static partial class ConstraintValidator + { + // + // We cannot do the constraint validation against real TypeInfo because of constraints need to be validated + // before the type is built. + // + // InstantiatedType allows us to use TypeInfo for constraint validation without creating a real TypeInfo. + // It implements just enough methods for constraint validation to work, and performs type variable substitution + // as necesary. + // + + private struct SigTypeContext + { + public readonly Type[] TypeInstantiation; + public readonly Type[] MethodInstantiation; + + public SigTypeContext(Type[] typeInstantiation, Type[] methodInstantiation) + { + TypeInstantiation = typeInstantiation; + MethodInstantiation = methodInstantiation; + } + } + + private sealed class InstantiatedTypeInfo : TypeInfo + { + private Type _underlyingType; + private SigTypeContext _context; + + public InstantiatedTypeInfo(Type underlyingType, SigTypeContext context) + { + _underlyingType = underlyingType; + _context = context; + } + + public Type UnderlyingType + { + get + { + return _underlyingType; + } + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2080:UnrecognizedReflectionPattern", + Justification = "We won're remove interfaces used in constraints")] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public override Type[] GetInterfaces() + { + Type[] interfaces = _underlyingType.GetInterfaces(); + for (int i = 0; i < interfaces.Length; i++) + interfaces[i] = interfaces[i].Instantiate(_context); + return interfaces; + } + + public override bool IsConstructedGenericType + { + get + { + return _underlyingType.IsConstructedGenericType; + } + } + + public override bool IsGenericType + { + get + { + return _underlyingType.IsGenericType; + } + } + + public override Type GetGenericTypeDefinition() + { + return _underlyingType.GetGenericTypeDefinition(); + } + + public override int GetArrayRank() + { + return _underlyingType.GetArrayRank(); + } + + public override Type GetElementType() + { + return _underlyingType.GetElementType().Instantiate(_context); + } + + public override Type[] GetGenericArguments() + { + Type[] arguments = _underlyingType.GetGenericArguments(); + for (int i = 0; i < arguments.Length; i++) + { + arguments[i] = arguments[i].Instantiate(_context); + } + return arguments; + } + + public override Type BaseType + { + get + { + return _underlyingType.BaseType.Instantiate(_context); + } + } + + protected override TypeAttributes GetAttributeFlagsImpl() + { + return _underlyingType.Attributes; + } + + protected override bool IsValueTypeImpl() + { + return _underlyingType.IsValueType; + } + + protected override bool IsArrayImpl() + { + return _underlyingType.IsArray; + } + + protected override bool IsByRefImpl() + { + return _underlyingType.IsByRef; + } + + protected override bool IsPointerImpl() + { + return _underlyingType.IsPointer; + } + + public override Assembly Assembly { get { Debug.Assert(false); throw NotImplemented.ByDesign; } } + public override string AssemblyQualifiedName { get { Debug.Assert(false); throw NotImplemented.ByDesign; } } + public override string FullName { get { Debug.Assert(false); throw NotImplemented.ByDesign; } } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) { Debug.Assert(false); throw NotImplemented.ByDesign; } + public override object[] GetCustomAttributes(bool inherit) { Debug.Assert(false); throw NotImplemented.ByDesign; } + public override object[] GetCustomAttributes(Type attributeType, bool inherit) { Debug.Assert(false); throw NotImplemented.ByDesign; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo GetEvent(string name, BindingFlags bindingAttr) { Debug.Assert(false); throw NotImplemented.ByDesign; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo[] GetEvents(BindingFlags bindingAttr) { Debug.Assert(false); throw NotImplemented.ByDesign; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo GetField(string name, BindingFlags bindingAttr) { Debug.Assert(false); throw NotImplemented.ByDesign; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo[] GetFields(BindingFlags bindingAttr) { Debug.Assert(false); throw NotImplemented.ByDesign; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063:UnrecognizedReflectionPattern", + Justification = "Linker doesn't recognize always throwing method. https://github.com/mono/linker/issues/2025")] + public override Type GetInterface(string name, bool ignoreCase) { Debug.Assert(false); throw NotImplemented.ByDesign; } + [DynamicallyAccessedMembers(GetAllMembers)] + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) { Debug.Assert(false); throw NotImplemented.ByDesign; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) { Debug.Assert(false); throw NotImplemented.ByDesign; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type GetNestedType(string name, BindingFlags bindingAttr) { Debug.Assert(false); throw NotImplemented.ByDesign; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type[] GetNestedTypes(BindingFlags bindingAttr) { Debug.Assert(false); throw NotImplemented.ByDesign; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) { Debug.Assert(false); throw NotImplemented.ByDesign; } + public override bool IsDefined(Type attributeType, bool inherit) { Debug.Assert(false); throw NotImplemented.ByDesign; } + public override Guid GUID { get { Debug.Assert(false); throw NotImplemented.ByDesign; } } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) { Debug.Assert(false); throw NotImplemented.ByDesign; } + public override Module Module { get { Debug.Assert(false); throw NotImplemented.ByDesign; } } + public override string Namespace { get { Debug.Assert(false); throw NotImplemented.ByDesign; } } + public override string Name { get { Debug.Assert(false); throw NotImplemented.ByDesign; } } + public override Type UnderlyingSystemType { get { Debug.Assert(false); throw NotImplemented.ByDesign; } } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) { Debug.Assert(false); throw NotImplemented.ByDesign; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) { Debug.Assert(false); throw NotImplemented.ByDesign; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) { Debug.Assert(false); throw NotImplemented.ByDesign; } + protected override bool IsCOMObjectImpl() { Debug.Assert(false); throw NotImplemented.ByDesign; } + protected override bool IsPrimitiveImpl() { Debug.Assert(false); throw NotImplemented.ByDesign; } + protected override bool HasElementTypeImpl() { Debug.Assert(false); throw NotImplemented.ByDesign; } + + internal const DynamicallyAccessedMemberTypes GetAllMembers = DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | + DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; + } + + private static Type Instantiate(this Type type, SigTypeContext context) + { + if (type.IsGenericParameter) + { + int position = type.GenericParameterPosition; + if (type.DeclaringMethod != null) + { + return context.MethodInstantiation[position]; + } + else + { + Debug.Assert(type.DeclaringType != null); + return context.TypeInstantiation[position]; + } + } + + if (type.ContainsGenericParameters) + { + // + // Note we can come here for both generic and non-generic types. Consider this example: + // + // interface IFoo { } + // class Foo : IFoo { } + // + // var foo = typeof(Foo<>); + // var ifoo = foo.ImplementedInterfaces.First(); + // var arg = ifoo.GetGenericArguments()[0]; + // + // arg.ContainsGenericParameters will be true, but arg.IsGenericType will be false. + // + return new InstantiatedTypeInfo(type, context); + } + + return type; + } + + private static bool IsInstantiatedTypeInfo(this Type type) + { + return type is InstantiatedTypeInfo; + } + + // + // Other helper methods to support constraint validation + // + + private static bool IsNullable(this Type type) + { + return type.IsGenericType && typeof(Nullable<>) == type.GetGenericTypeDefinition(); + } + + private static Type GetNullableType(this Type type) + { + Debug.Assert(type.IsNullable()); + + Type[] arguments = type.GetGenericArguments(); + Debug.Assert(arguments.Length == 1); + + return arguments[0]; + } + + private static bool IsSystemObject(this Type type) + { + return typeof(object) == type; + } + + private static bool IsSystemValueType(this Type type) + { + return typeof(ValueType) == type; + } + + private static bool IsSystemArray(this Type type) + { + return typeof(Array) == type; + } + + private static bool IsSystemVoid(this Type type) + { + return typeof(void) == type; + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Default constructors of types that could be arguments to MakeGenericType are preserved.")] + private static bool HasExplicitOrImplicitPublicDefaultConstructor(this Type type) + { + // Strip InstantiatedTypeInfo - GetConstructors is not implemented on InstantiatedTypeInfo + if (type is InstantiatedTypeInfo) + type = ((InstantiatedTypeInfo)type).UnderlyingType; + + // valuetypes have public default ctors implicitly + if (type.IsValueType) + return true; + + foreach (var ctor in type.GetConstructors()) + { + if (!ctor.IsStatic && ctor.IsPublic && ctor.GetParametersNoCopy().Length == 0) + return true; + } + return false; + } + + private static unsafe int NormalizedPrimitiveTypeSizeForIntegerTypes(this Type type) + { + // Strip InstantiatedTypeInfo - IsEnum is not implemented on InstantiatedTypeInfo + if (type is InstantiatedTypeInfo) + type = ((InstantiatedTypeInfo)type).UnderlyingType; + + Type normalizedType; + + if (type.IsEnum) + { + // TODO: Enum.GetUnderlyingType does not work for generic type definitions + return NormalizedPrimitiveTypeSizeForIntegerTypes(Enum.GetUnderlyingType(type)); + } + else + if (type.IsPrimitive) + { + normalizedType = type; + } + else + { + return 0; + } + + if (typeof(byte) == normalizedType || typeof(sbyte) == normalizedType) + return 1; + + if (typeof(ushort) == normalizedType || typeof(short) == normalizedType) + return 2; + + if (typeof(uint) == normalizedType || typeof(int) == normalizedType) + return 4; + + if (typeof(ulong) == normalizedType || typeof(long) == normalizedType) + return 8; + + if (typeof(UIntPtr) == normalizedType || typeof(IntPtr) == normalizedType) + return sizeof(IntPtr); + + return 0; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/TypeCast.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/TypeCast.cs new file mode 100644 index 00000000000000..8cda2dbf85b819 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/TypeCast.cs @@ -0,0 +1,450 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Collections.Generic; +using Debug = System.Diagnostics.Debug; + +namespace Internal.Reflection.Execution +{ + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // + // **** WARNING **** + // + // A large portion of the logic present in this file is duplicated in ndp\rh\src\rtm\system\runtime\typecast.cs + // + // **** WARNING **** + // + ///////////////////////////////////////////////////////////////////////////////////////////////////// + + // This is not a general purpose type comparison facility. It is limited to what constraint validation needs. + internal static partial class ConstraintValidator + { + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", + Justification = "Looking at interface list is safe because we wouldn't remove reflection-visible interface from a reflection-visible type")] + private static bool ImplementsInterface(Type pObjType, Type pTargetType) + { + Debug.Assert(!pTargetType.IsArray, "did not expect array type"); + Debug.Assert(pTargetType.IsInterface, "IsInstanceOfInterface called with non-interface MethodTable"); + + foreach (var pInterfaceType in pObjType.GetInterfaces()) + { + if (AreTypesEquivalentInternal(pInterfaceType, pTargetType)) + { + return true; + } + } + + // We did not find the interface type in the list of supported interfaces. There's still one + // chance left: if the target interface is generic and one or more of its type parameters is co or + // contra variant then the object can still match if it implements a different instantiation of + // the interface with type compatible generic arguments. + // + // An additional edge case occurs because of array covariance. This forces us to treat any generic + // interfaces implemented by arrays as covariant over their one type parameter. + // if (pTargetType.HasGenericVariance || (fArrayCovariance && pTargetType.IsGenericType)) + // + if (pTargetType.IsGenericType) + { + bool fArrayCovariance = pObjType.IsArray; + Type pTargetGenericType = pTargetType.GetGenericTypeDefinition(); + + // Fetch the instantiations lazily only once we get a potential match + Type[] pTargetInstantiation = null; + Type[] pTargetGenericInstantiation = null; + + foreach (var pInterfaceType in pObjType.GetInterfaces()) + { + // We can ignore interfaces which are not also marked as having generic variance + // unless we're dealing with array covariance. + // if (pInterfaceType.HasGenericVariance || (fArrayCovariance && pInterfaceType.IsGenericType)) + + if (!pInterfaceType.IsGenericType) + continue; + + // If the generic types aren't the same then the types aren't compatible. + if (!pInterfaceType.GetGenericTypeDefinition().Equals(pTargetGenericType)) + continue; + + Type[] pInterfaceInstantiation = pInterfaceType.GetGenericArguments(); + + if (pTargetInstantiation == null) + { + pTargetInstantiation = pTargetType.GetGenericArguments(); + + if (!fArrayCovariance) + pTargetGenericInstantiation = pTargetGenericType.GetGenericArguments(); + } + + // Compare the instantiations to see if they're compatible taking variance into account. + if (TypeParametersAreCompatible(pInterfaceInstantiation, + pTargetInstantiation, + pTargetGenericInstantiation, + fArrayCovariance)) + return true; + + if (fArrayCovariance) + { + Debug.Assert(pInterfaceInstantiation.Length == 1, "arity mismatch for array generic interface"); + Debug.Assert(pTargetInstantiation.Length == 1, "arity mismatch for array generic interface"); + + // Special case for generic interfaces on arrays. Arrays of integral types (including enums) + // can be cast to generic interfaces over the integral types of the same size. For example + // int[] . IList. + if (ArePrimitveTypesEquivalentSize(pInterfaceInstantiation[0], + pTargetInstantiation[0])) + { + // We have checked that the interface type definition matches above. The checks are ordered differently + // here compared with rtm\system\runtime\typecast.cs version because of TypeInfo does not let us do + // the HasGenericVariance optimization. + return true; + } + } + } + } + + return false; + } + + // Compare two types to see if they are compatible via generic variance. + private static bool TypesAreCompatibleViaGenericVariance(Type pSourceType, Type pTargetType) + { + Type pTargetGenericType = pTargetType.GetGenericTypeDefinition(); + Type pSourceGenericType = pSourceType.GetGenericTypeDefinition(); + + // If the generic types aren't the same then the types aren't compatible. + if (pTargetGenericType.Equals(pSourceGenericType)) + { + // Compare the instantiations to see if they're compatible taking variance into account. + if (TypeParametersAreCompatible(pSourceType.GetGenericArguments(), + pTargetType.GetGenericArguments(), + pTargetGenericType.GetGenericArguments(), + false)) + { + return true; + } + } + + return false; + } + + // Compare two sets of generic type parameters to see if they're assignment compatible taking generic + // variance into account. It's assumed they've already had their type definition matched (which + // implies their arities are the same as well). The fForceCovariance argument tells the method to + // override the defined variance of each parameter and instead assume it is covariant. This is used to + // implement covariant array interfaces. + private static bool TypeParametersAreCompatible(Type[] pSourceInstantiation, + Type[] pTargetInstantiation, + Type[] pVarianceInfo, + bool fForceCovariance) + { + // The types represent different instantiations of the same generic type. The + // arity of both had better be the same. + Debug.Assert(pSourceInstantiation.Length == pTargetInstantiation.Length, "arity mismatch betweeen generic instantiations"); + + Debug.Assert(fForceCovariance || pTargetInstantiation.Length == pVarianceInfo.Length, "arity mismatch betweeen generic instantiations"); + + // Walk through the instantiations comparing the cast compatibility of each pair + // of type args. + for (int i = 0; i < pTargetInstantiation.Length; i++) + { + Type pTargetArgType = pTargetInstantiation[i]; + Type pSourceArgType = pSourceInstantiation[i]; + + GenericParameterAttributes varType; + if (fForceCovariance) + varType = GenericParameterAttributes.Covariant; + else + varType = pVarianceInfo[i].GenericParameterAttributes & GenericParameterAttributes.VarianceMask; + + switch (varType) + { + case GenericParameterAttributes.None: + // Non-variant type params need to be identical. + + if (!AreTypesEquivalentInternal(pSourceArgType, pTargetArgType)) + return false; + + break; + + case GenericParameterAttributes.Covariant: + // For covariance (or out type params in C#) the object must implement an + // interface with a more derived type arg than the target interface. Or + // the object interface can have a type arg that is an interface + // implemented by the target type arg. + // For instance: + // class Foo : ICovariant is ICovariant + // class Foo : ICovariant is ICovariant + // class Foo : ICovariant is ICovariant + + if (!AreTypesAssignableInternal(pSourceArgType, pTargetArgType, false, false)) + return false; + + break; + + case GenericParameterAttributes.Contravariant: + // For contravariance (or in type params in C#) the object must implement + // an interface with a less derived type arg than the target interface. Or + // the object interface can have a type arg that is a class implementing + // the interface that is the target type arg. + // For instance: + // class Foo : IContravariant is IContravariant + // class Foo : IContravariant is IContravariant + // class Foo : IContravariant is IContravariant + + if (!AreTypesAssignableInternal(pTargetArgType, pSourceArgType, false, false)) + return false; + + break; + + default: + Debug.Fail("unknown generic variance type"); + return false; + } + } + + return true; + } + + // + // Determines if a value of the source type can be assigned to a location of the target type. + // It does not handle IDynamicInterfaceCastable, and cannot since we do not have an actual object instance here. + // This routine assumes that the source type is boxed, i.e. a value type source is presumed to be + // compatible with Object and ValueType and an enum source is additionally compatible with Enum. + // + private static bool AreTypesAssignable(Type pSourceType, Type pTargetType) + { + // Special case: T can be cast to Nullable (where T is a value type). Call this case out here + // since this is only applicable if T is boxed, which is not true for any other callers of + // AreTypesAssignableInternal, so no sense making all the other paths pay the cost of the check. + if (pTargetType.IsNullable() && pSourceType.IsValueType && !pSourceType.IsNullable()) + { + Type pNullableType = pTargetType.GetNullableType(); + + return AreTypesEquivalentInternal(pSourceType, pNullableType); + } + + return AreTypesAssignableInternal(pSourceType, pTargetType, true, false); + } + + // Internally callable version of the export method above. Has two additional parameters: + // fBoxedSource : assume the source type is boxed so that value types and enums are + // compatible with Object, ValueType and Enum (if applicable) + // fAllowSizeEquivalence : allow identically sized integral types and enums to be considered + // equivalent (currently used only for array element types) + private static bool AreTypesAssignableInternal(Type pSourceType, Type pTargetType, bool fBoxedSource, bool fAllowSizeEquivalence) + { + // + // Are the types identical? + // + if (AreTypesEquivalentInternal(pSourceType, pTargetType)) + return true; + + // + // Handle cast to interface cases. + // + if (pTargetType.IsInterface) + { + // Value types can only be cast to interfaces if they're boxed. + if (!fBoxedSource && pSourceType.IsValueType) + return false; + + if (ImplementsInterface(pSourceType, pTargetType)) + return true; + + // Are the types compatible due to generic variance? + // if (pTargetType.HasGenericVariance && pSourceType.HasGenericVariance) + if (pTargetType.IsGenericType && pSourceType.IsGenericType) + return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType); + + return false; + } + if (pSourceType.IsInterface) + { + // The only non-interface type an interface can be cast to is Object. + return pTargetType.IsSystemObject(); + } + + // + // Handle cast to array cases. + // + if (pTargetType.IsArray) + { + if (pSourceType.IsArray) + { + if (pSourceType.GetElementType().IsPointer) + { + // If the element types are pointers, then only exact matches are correct. + // As we've already called AreTypesEquivalent at the start of this function, + // return false as the exact match case has already been handled. + // int** is not compatible with uint**, nor is int*[] oompatible with uint*[]. + return false; + } + else + { + // Source type is also a pointer. Are the element types compatible? Note that using + // AreTypesAssignableInternal here handles array covariance as well as IFoo[] . Foo[] + // etc. Pass false for fBoxedSource since int[] is not assignable to object[]. + return AreTypesAssignableInternal(pSourceType.GetElementType(), pTargetType.GetElementType(), false, true); + } + } + + // Can't cast a non-array type to an array. + return false; + } + if (pSourceType.IsArray) + { + // Target type is not an array. But we can still cast arrays to Object or System.Array. + return pTargetType.IsSystemObject() || pTargetType.IsSystemArray(); + } + + // + // Handle pointer cases + // + if (pTargetType.IsPointer) + { + if (pSourceType.IsPointer) + { + if (pSourceType.GetElementType().IsPointer) + { + // If the element types are pointers, then only exact matches are correct. + // As we've already called AreTypesEquivalent at the start of this function, + // return false as the exact match case has already been handled. + // int** is not compatible with uint**, nor is int*[] compatible with uint*[]. + return false; + } + else + { + // Source type is also a pointer. Are the element types compatible? Note that using + // AreTypesAssignableInternal here handles array covariance as well as IFoo[] . Foo[] + // etc. Pass false for fBoxedSource since int[] is not assignable to object[]. + return AreTypesAssignableInternal(pSourceType.GetElementType(), pTargetType.GetElementType(), false, true); + } + } + + return false; + } + else if (pSourceType.IsPointer) + { + return false; + } + + // + // Handle cast to other (non-interface, non-array) cases. + // + + if (pSourceType.IsValueType) + { + // Certain value types of the same size are treated as equivalent when the comparison is + // between array element types (indicated by fAllowSizeEquivalence). These are integer types + // of the same size (e.g. int and uint) and the base type of enums vs all integer types of the + // same size. + if (fAllowSizeEquivalence && pTargetType.IsValueType) + { + if (ArePrimitveTypesEquivalentSize(pSourceType, pTargetType)) + return true; + + // Non-identical value types aren't equivalent in any other case (since value types are + // sealed). + return false; + } + + // If the source type is a value type but it's not boxed then we've run out of options: the types + // are not identical, the target type isn't an interface and we're not allowed to check whether + // the target type is a parent of this one since value types are sealed and thus the only matches + // would be against Object, ValueType or Enum, all of which are reference types and not compatible + // with non-boxed value types. + if (!fBoxedSource) + return false; + } + + // + // Are the types compatible via generic variance? + // + // if (pTargetType.HasGenericVariance && pSourceType.HasGenericVariance) + if (pTargetType.IsGenericType && pSourceType.IsGenericType) + { + if (TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType)) + return true; + } + + // Is the source type derived from the target type? + if (IsDerived(pSourceType, pTargetType)) + return true; + + return false; + } + + private static bool IsDerived(Type pDerivedType, Type pBaseType) + { + Debug.Assert(!pBaseType.IsInterface, "did not expect interface type"); + + for (;;) + { + if (AreTypesEquivalentInternal(pDerivedType, pBaseType)) + return true; + + Type baseType = pDerivedType.BaseType; + if (baseType == null) + return false; + + pDerivedType = baseType; + } + } + + // Method to compare two types pointers for type equality + // We cannot just compare the pointers as there can be duplicate type instances + // for cloned and constructed types. + private static bool AreTypesEquivalentInternal(Type pType1, Type pType2) + { + if (!pType1.IsInstantiatedTypeInfo() && !pType2.IsInstantiatedTypeInfo()) + return pType1.Equals(pType2); + + if (pType1.IsGenericType && pType2.IsGenericType) + { + if (!pType1.GetGenericTypeDefinition().Equals(pType2.GetGenericTypeDefinition())) + return false; + + Type[] args1 = pType1.GetGenericArguments(); + Type[] args2 = pType2.GetGenericArguments(); + Debug.Assert(args1.Length == args2.Length); + + for (int i = 0; i < args1.Length; i++) + { + if (!AreTypesEquivalentInternal(args1[i], args2[i])) + return false; + } + + return true; + } + + if (pType1.IsArray && pType2.IsArray) + { + if (pType1.GetArrayRank() != pType2.GetArrayRank()) + return false; + + return AreTypesEquivalentInternal(pType1.GetElementType(), pType2.GetElementType()); + } + + if (pType1.IsPointer && pType2.IsPointer) + { + return AreTypesEquivalentInternal(pType1.GetElementType(), pType2.GetElementType()); + } + + return false; + } + + private static bool ArePrimitveTypesEquivalentSize(Type pType1, Type pType2) + { + int normalizedType1 = NormalizedPrimitiveTypeSizeForIntegerTypes(pType1); + if (normalizedType1 == 0) + return false; + + int normalizedType2 = NormalizedPrimitiveTypeSizeForIntegerTypes(pType2); + + return normalizedType1 == normalizedType2; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Extensions/NonPortable/DelegateMethodInfoRetriever.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Extensions/NonPortable/DelegateMethodInfoRetriever.cs new file mode 100644 index 00000000000000..59e612de7d0277 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Extensions/NonPortable/DelegateMethodInfoRetriever.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System; +using global::System.Reflection; + +using global::Internal.Runtime.TypeLoader; +using global::Internal.Runtime.Augments; +using global::Internal.Runtime.CompilerServices; +using global::Internal.Reflection.Execution; +using global::Internal.Reflection.Core.Execution; + +using System.Reflection.Runtime.General; + +namespace Internal.Reflection.Extensions.NonPortable +{ + public static class DelegateMethodInfoRetriever + { + public static MethodInfo GetDelegateMethodInfo(Delegate del) + { + if (del == null) + throw new ArgumentException(); + Delegate[] invokeList = del.GetInvocationList(); + del = invokeList[invokeList.Length - 1]; + IntPtr originalLdFtnResult = RuntimeAugments.GetDelegateLdFtnResult(del, out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver, out bool isInterpreterEntrypoint); + + if (isInterpreterEntrypoint) + { + // This is a special kind of delegate where the invoke method is "ObjectArrayThunk". Typically, + // this will be a delegate that points the the LINQ Expression interpreter. We could manufacture + // a MethodInfo based on the delegate's Invoke signature, but let's just throw for now. + throw new PlatformNotSupportedException(SR.DelegateGetMethodInfo_ObjectArrayDelegate); + } + + if (originalLdFtnResult == (IntPtr)0) + return null; + + QMethodDefinition methodHandle = default(QMethodDefinition); + RuntimeTypeHandle[] genericMethodTypeArgumentHandles = null; + + bool callTryGetMethod = true; + + unsafe + { + if (isOpenResolver) + { + OpenMethodResolver* resolver = (OpenMethodResolver*)originalLdFtnResult; + if (resolver->IsOpenNonVirtualResolve) + { + originalLdFtnResult = resolver->CodePointer; + // And go on to do normal ldftn processing. + } + else if (resolver->ResolverType == OpenMethodResolver.DispatchResolve) + { + callTryGetMethod = false; + methodHandle = QMethodDefinition.FromObjectAndInt(resolver->Reader, resolver->Handle); + genericMethodTypeArgumentHandles = null; + } + else + { + System.Diagnostics.Debug.Assert(resolver->ResolverType == OpenMethodResolver.GVMResolve); + + callTryGetMethod = false; + methodHandle = QMethodDefinition.FromObjectAndInt(resolver->Reader, resolver->Handle); + + RuntimeTypeHandle declaringTypeHandleIgnored; + MethodNameAndSignature nameAndSignatureIgnored; + if (!TypeLoaderEnvironment.Instance.TryGetRuntimeMethodHandleComponents(resolver->GVMMethodHandle, out declaringTypeHandleIgnored, out nameAndSignatureIgnored, out genericMethodTypeArgumentHandles)) + throw new MissingRuntimeArtifactException(SR.DelegateGetMethodInfo_NoInstantiation); + } + } + } + + if (callTryGetMethod) + { + if (!ReflectionExecution.ExecutionEnvironment.TryGetMethodForOriginalLdFtnResult(originalLdFtnResult, ref typeOfFirstParameterIfInstanceDelegate, out methodHandle, out genericMethodTypeArgumentHandles)) + { + ReflectionExecution.ExecutionEnvironment.GetFunctionPointerAndInstantiationArgumentForOriginalLdFtnResult(originalLdFtnResult, out IntPtr ip, out IntPtr _); + + string methodDisplayString = RuntimeAugments.TryGetMethodDisplayStringFromIp(ip); + if (methodDisplayString == null) + throw new MissingRuntimeArtifactException(SR.DelegateGetMethodInfo_NoDynamic); + else + throw new MissingRuntimeArtifactException(SR.Format(SR.DelegateGetMethodInfo_NoDynamic_WithDisplayString, methodDisplayString)); + } + } + MethodBase methodBase = ReflectionCoreExecution.ExecutionDomain.GetMethod(typeOfFirstParameterIfInstanceDelegate, methodHandle, genericMethodTypeArgumentHandles); + MethodInfo methodInfo = methodBase as MethodInfo; + if (methodInfo != null) + return methodInfo; + return null; // GetMethod() returned a ConstructorInfo. + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 00000000000000..95b65977b915f5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Reflection.Execution; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + ReflectionExecution.Initialize(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Resources/Strings.resx b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Resources/Strings.resx new file mode 100644 index 00000000000000..25fa97370a3f53 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Resources/Strings.resx @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + This operation cannot be carried out because metadata for the following object was removed for performance reasons:\n\n {0}\n\nNo further information is available. Rebuild in debug mode for better information.\n\n + + + '{0}' is missing metadata. For more information, please visit http://go.microsoft.com/fwlink/?LinkID=392859 + + + Cannot bind to the target method because its signature is not compatible with that of the delegate type. + + + Array may not be empty. + + + Member not found. + + + Ambiguous match found. + + + Hashtable's capacity overflowed and went negative. Check load factor, capacity and the current size of the table. + + + Nullable object must have a value. + + + Non-static field requires a target. + + + Constructor on type '{0}' not found. + + + Multidimensional arrays of rank {0} are not supported. + + + This object cannot be invoked because no code was generated for it: '{0}'. + + + MakeGenericMethod() cannot create this generic method instantiation because no code was generated for it: '{0}'. + + + Dynamic invocation of delegate constructors is not supported on this runtime. + + + An item with the same key has already been added. + + + The number of generic arguments provided doesn't equal the arity of the generic type definition. + + + The type '{0}' may not be used as a type argument. + + + '{0}', on '{1}' violates the constraint of type '{2}'. + + + At least one object must implement IComparable. + + + The stream is currently in use by a previous operation on the stream. + + + Stream was not readable. + + + Cannot read from a closed TextReader. + + + Positive number required. + + + Buffer cannot be null. + + + Non-negative number required. + + + Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection. + + + Cannot set a constant field. + + + Cannot retrieve a MethodInfo for this delegate because the method it targeted was not enabled for metadata. + + + Cannot retrieve a MethodInfo for this delegate because the method it targeted ({0}) was not enabled for metadata. + + + Cannot retrieve a MethodInfo for this delegate because the necessary generic instantiation was not metadata-enabled. + + + Cannot retrieve a MethodInfo for this delegate because the delegate target is an interpreted LINQ expression. + + + 'this' type cannot be an interface itself. + + + Type passed must be an interface. + + + Interface not found. + + + Could not retrieve the mapping of the interface '{0}' on type '{1}' because the type implements the interface abstractly. + + + Cannot set initonly static field after its owning type is initialized. + + \ No newline at end of file diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj new file mode 100644 index 00000000000000..1d4de60c96515d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj @@ -0,0 +1,88 @@ + + + + + + + + + + + $(CompilerCommonPath)\Internal\NativeFormat + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Internal\Runtime\MetadataBlob.cs + + + System\NotImplemented.cs + + + + System\Collections\Generic\LowLevelList.cs + + + System\Collections\Generic\LowLevelDictionary.cs + + + Internal\LowLevelLinq\LowLevelEnumerable.cs + + + Internal\LowLevelLinq\LowLevelEnumerable.ToArray.cs + + + System\Collections\HashHelpers.cs + + + System\Collections\Generic\Empty.cs + + + System\Runtime\CompilerServices\__BlockAllReflectionAttribute.cs + + + diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System/Reflection/MissingRuntimeArtifactException.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System/Reflection/MissingRuntimeArtifactException.cs new file mode 100644 index 00000000000000..3690212c33b3db --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System/Reflection/MissingRuntimeArtifactException.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================ +** + Type: MissingRuntimeArtifactException +** +==============================================================*/ + +using global::System; + +namespace System.Reflection +{ + internal sealed class MissingRuntimeArtifactException : MemberAccessException + { + public MissingRuntimeArtifactException() + { + } + + public MissingRuntimeArtifactException(string message) + : base(message) + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Metadata/src/System.Private.Reflection.Metadata.csproj b/src/coreclr/nativeaot/System.Private.Reflection.Metadata/src/System.Private.Reflection.Metadata.csproj new file mode 100644 index 00000000000000..7b3f55c5bf1d20 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.Reflection.Metadata/src/System.Private.Reflection.Metadata.csproj @@ -0,0 +1,27 @@ + + + + + + $(CompilerCommonPath)\Internal\NativeFormat + $(CompilerCommonPath)\Internal\Metadata\NativeFormat + + + + + + + + + + + + + + + + + System\Runtime\CompilerServices\__BlockAllReflectionAttribute.cs + + + diff --git a/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/ILLink/ILLink.Substitutions.xml b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/ILLink/ILLink.Substitutions.xml new file mode 100644 index 00000000000000..3d124ed9adef52 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/ILLink/ILLink.Substitutions.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 00000000000000..5e84c858daec5b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + Internal.StackTraceMetadata.StackTraceMetadata.Initialize(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs new file mode 100644 index 00000000000000..cc5dd065dac28b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs @@ -0,0 +1,568 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +using Internal.Metadata.NativeFormat; + +namespace Internal.StackTraceMetadata +{ + class MethodNameFormatter + { + /// + /// Metadata reader used for the purpose of method name formatting. + /// + private readonly MetadataReader _metadataReader; + + /// + /// String builder used to construct formatted method name. + /// + private readonly StringBuilder _outputBuilder; + + /// + /// Represents the instatiation type context. + /// + private readonly SigTypeContext _typeContext; + + /// + /// Initialize the reader used for method name formatting. + /// + private MethodNameFormatter(MetadataReader metadataReader, SigTypeContext typeContext) + { + _metadataReader = metadataReader; + _outputBuilder = new StringBuilder(); + _typeContext = typeContext; + } + + public static string FormatMethodName(MetadataReader metadataReader, Handle methodHandle) + { + MethodNameFormatter formatter = new MethodNameFormatter(metadataReader, SigTypeContext.FromMethod(metadataReader, methodHandle)); + formatter.EmitMethodName(methodHandle); + return formatter._outputBuilder.ToString(); + } + + public static string FormatMethodName(MetadataReader metadataReader, TypeDefinitionHandle enclosingTypeHandle, MethodHandle methodHandle) + { + MethodNameFormatter formatter = new MethodNameFormatter(metadataReader, SigTypeContext.FromMethod(metadataReader, enclosingTypeHandle, methodHandle)); + + Method method = metadataReader.GetMethod(methodHandle); + MethodSignature methodSignature = metadataReader.GetMethodSignature(method.Signature); + formatter.EmitTypeName(enclosingTypeHandle, namespaceQualified: true); + formatter._outputBuilder.Append('.'); + formatter.EmitString(method.Name); + + bool first = true; + foreach (GenericParameterHandle handle in method.GenericParameters) + { + if (first) + { + first = false; + formatter._outputBuilder.Append('['); + } + else + { + formatter._outputBuilder.Append(", "); + } + formatter.EmitTypeName(handle, namespaceQualified: false); + } + if (!first) + { + formatter._outputBuilder.Append(']'); + } + + formatter.EmitMethodParameters(methodSignature); + + return formatter._outputBuilder.ToString(); + } + + /// + /// Emit a given method signature to a specified string builder. + /// + /// Method reference or instantiation token + private void EmitMethodName(Handle methodHandle) + { + switch (methodHandle.HandleType) + { + case HandleType.MemberReference: + EmitMethodReferenceName(methodHandle.ToMemberReferenceHandle(_metadataReader)); + break; + + case HandleType.MethodInstantiation: + EmitMethodInstantiationName(methodHandle.ToMethodInstantiationHandle(_metadataReader)); + break; + + case HandleType.QualifiedMethod: + EmitMethodDefinitionName(methodHandle.ToQualifiedMethodHandle(_metadataReader)); + break; + + default: + Debug.Assert(false); + _outputBuilder.Append("???"); + break; + } + } + + /// + /// Emit method reference to the output string builder. + /// + /// Member reference handle + private void EmitMethodReferenceName(MemberReferenceHandle memberRefHandle) + { + MemberReference methodRef = _metadataReader.GetMemberReference(memberRefHandle); + MethodSignature methodSignature; + EmitContainingTypeAndMethodName(methodRef, out methodSignature); + EmitMethodParameters(methodSignature); + } + + /// + /// Emit generic method instantiation to the output string builder. + /// + /// Method instantiation handle + private void EmitMethodInstantiationName(MethodInstantiationHandle methodInstHandle) + { + MethodInstantiation methodInst = _metadataReader.GetMethodInstantiation(methodInstHandle); + MethodSignature methodSignature; + if (methodInst.Method.HandleType == HandleType.MemberReference) + { + MemberReferenceHandle methodRefHandle = methodInst.Method.ToMemberReferenceHandle(_metadataReader); + MemberReference methodRef = methodRefHandle.GetMemberReference(_metadataReader); + EmitContainingTypeAndMethodName(methodRef, out methodSignature); + } + else + { + QualifiedMethodHandle qualifiedMethodHandle = methodInst.Method.ToQualifiedMethodHandle(_metadataReader); + QualifiedMethod qualifiedMethod = _metadataReader.GetQualifiedMethod(qualifiedMethodHandle); + EmitContainingTypeAndMethodName(qualifiedMethod, out methodSignature); + } + EmitGenericArguments(methodInst.GenericTypeArguments); + EmitMethodParameters(methodSignature); + } + + private void EmitMethodDefinitionName(QualifiedMethodHandle qualifiedMethodHandle) + { + QualifiedMethod qualifiedMethod = _metadataReader.GetQualifiedMethod(qualifiedMethodHandle); + EmitContainingTypeAndMethodName(qualifiedMethod, out MethodSignature methodSignature); + EmitMethodParameters(methodSignature); + } + + /// + /// Emit containing type and method name and extract the method signature from a method reference. + /// + /// Method reference to format + /// Output method signature + private void EmitContainingTypeAndMethodName(MemberReference methodRef, out MethodSignature methodSignature) + { + methodSignature = _metadataReader.GetMethodSignature(methodRef.Signature.ToMethodSignatureHandle(_metadataReader)); + EmitTypeName(methodRef.Parent, namespaceQualified: true); + _outputBuilder.Append('.'); + EmitString(methodRef.Name); + } + + private void EmitContainingTypeAndMethodName(QualifiedMethod qualifiedMethod, out MethodSignature methodSignature) + { + Method method = _metadataReader.GetMethod(qualifiedMethod.Method); + methodSignature = _metadataReader.GetMethodSignature(method.Signature); + EmitTypeName(qualifiedMethod.EnclosingType, namespaceQualified: true); + _outputBuilder.Append('.'); + EmitString(method.Name); + } + + /// + /// Emit parenthesized method argument type list. + /// + /// Method signature to use for parameter formatting + private void EmitMethodParameters(MethodSignature methodSignature) + { + _outputBuilder.Append('('); + EmitTypeVector(methodSignature.Parameters); + _outputBuilder.Append(')'); + } + + /// + /// Emit comma-separated list of type names into the output string builder. + /// + /// Enumeration of type handles to output + private void EmitTypeVector(HandleCollection typeVector) + { + bool first = true; + foreach (Handle handle in typeVector) + { + if (first) + { + first = false; + } + else + { + _outputBuilder.Append(", "); + } + EmitTypeName(handle, namespaceQualified: false); + } + } + + /// + /// Emit the name of a given type to the output string builder. + /// + /// Type handle to format + /// When set to true, include namespace information + private void EmitTypeName(Handle typeHandle, bool namespaceQualified) + { + switch (typeHandle.HandleType) + { + case HandleType.TypeReference: + EmitTypeReferenceName(typeHandle.ToTypeReferenceHandle(_metadataReader), namespaceQualified); + break; + + case HandleType.TypeSpecification: + EmitTypeSpecificationName(typeHandle.ToTypeSpecificationHandle(_metadataReader), namespaceQualified); + break; + + case HandleType.TypeInstantiationSignature: + EmitTypeInstantiationName(typeHandle.ToTypeInstantiationSignatureHandle(_metadataReader), namespaceQualified); + break; + + case HandleType.SZArraySignature: + EmitSZArrayTypeName(typeHandle.ToSZArraySignatureHandle(_metadataReader), namespaceQualified); + break; + + case HandleType.ArraySignature: + EmitArrayTypeName(typeHandle.ToArraySignatureHandle(_metadataReader), namespaceQualified); + break; + + case HandleType.PointerSignature: + EmitPointerTypeName(typeHandle.ToPointerSignatureHandle(_metadataReader)); + break; + + case HandleType.ByReferenceSignature: + EmitByRefTypeName(typeHandle.ToByReferenceSignatureHandle(_metadataReader)); + break; + + case HandleType.TypeDefinition: + EmitTypeDefinitionName(typeHandle.ToTypeDefinitionHandle(_metadataReader), namespaceQualified); + break; + + case HandleType.TypeVariableSignature: + EmitTypeName(_typeContext.GetTypeVariable(typeHandle.ToTypeVariableSignatureHandle(_metadataReader).GetTypeVariableSignature(_metadataReader).Number), namespaceQualified); + break; + + case HandleType.MethodTypeVariableSignature: + EmitTypeName(_typeContext.GetMethodVariable(typeHandle.ToMethodTypeVariableSignatureHandle(_metadataReader).GetMethodTypeVariableSignature(_metadataReader).Number), namespaceQualified); + break; + + case HandleType.GenericParameter: + EmitString(typeHandle.ToGenericParameterHandle(_metadataReader).GetGenericParameter(_metadataReader).Name); + break; + + default: + Debug.Assert(false); + _outputBuilder.Append("???"); + break; + } + } + + /// + /// Emit namespace reference. + /// + /// Namespace reference handle + private void EmitNamespaceReferenceName(NamespaceReferenceHandle namespaceRefHandle) + { + NamespaceReference namespaceRef = _metadataReader.GetNamespaceReference(namespaceRefHandle); + if (!namespaceRef.ParentScopeOrNamespace.IsNull(_metadataReader) && + namespaceRef.ParentScopeOrNamespace.HandleType == HandleType.NamespaceReference) + { + int charsWritten = _outputBuilder.Length; + EmitNamespaceReferenceName(namespaceRef.ParentScopeOrNamespace.ToNamespaceReferenceHandle(_metadataReader)); + if (_outputBuilder.Length - charsWritten > 0) + _outputBuilder.Append('.'); + } + EmitString(namespaceRef.Name); + } + + private void EmitNamespaceDefinitionName(NamespaceDefinitionHandle namespaceDefHandle) + { + NamespaceDefinition namespaceDef = _metadataReader.GetNamespaceDefinition(namespaceDefHandle); + if (!namespaceDef.ParentScopeOrNamespace.IsNull(_metadataReader) && + namespaceDef.ParentScopeOrNamespace.HandleType == HandleType.NamespaceDefinition) + { + int charsWritten = _outputBuilder.Length; + EmitNamespaceDefinitionName(namespaceDef.ParentScopeOrNamespace.ToNamespaceDefinitionHandle(_metadataReader)); + if (_outputBuilder.Length - charsWritten > 0) + _outputBuilder.Append('.'); + } + EmitString(namespaceDef.Name); + } + + /// + /// Emit type reference. + /// + /// Type reference handle + /// When set to true, include namespace information + private void EmitTypeReferenceName(TypeReferenceHandle typeRefHandle, bool namespaceQualified) + { + TypeReference typeRef = _metadataReader.GetTypeReference(typeRefHandle); + if (!typeRef.ParentNamespaceOrType.IsNull(_metadataReader)) + { + if (typeRef.ParentNamespaceOrType.HandleType != HandleType.NamespaceReference) + { + // Nested type + EmitTypeName(typeRef.ParentNamespaceOrType, namespaceQualified); + _outputBuilder.Append('.'); + } + else if (namespaceQualified) + { + int charsWritten = _outputBuilder.Length; + EmitNamespaceReferenceName(typeRef.ParentNamespaceOrType.ToNamespaceReferenceHandle(_metadataReader)); + if (_outputBuilder.Length - charsWritten > 0) + _outputBuilder.Append('.'); + } + } + EmitString(typeRef.TypeName); + } + + private void EmitTypeDefinitionName(TypeDefinitionHandle typeDefHandle, bool namespaceQualified) + { + TypeDefinition typeDef = _metadataReader.GetTypeDefinition(typeDefHandle); + if (!typeDef.EnclosingType.IsNull(_metadataReader)) + { + // Nested type + EmitTypeName(typeDef.EnclosingType, namespaceQualified); + _outputBuilder.Append('.'); + } + else if (namespaceQualified) + { + int charsWritten = _outputBuilder.Length; + EmitNamespaceDefinitionName(typeDef.NamespaceDefinition); + if (_outputBuilder.Length - charsWritten > 0) + _outputBuilder.Append('.'); + } + EmitString(typeDef.Name); + } + + /// + /// Emit an arbitrary type specification. + /// + /// Type specification handle + /// When set to true, include namespace information + private void EmitTypeSpecificationName(TypeSpecificationHandle typeSpecHandle, bool namespaceQualified) + { + TypeSpecification typeSpec = _metadataReader.GetTypeSpecification(typeSpecHandle); + EmitTypeName(typeSpec.Signature, namespaceQualified); + } + + /// + /// Emit generic instantiation type. + /// + /// Instantiated type specification signature handle + /// When set to true, include namespace information + private void EmitTypeInstantiationName(TypeInstantiationSignatureHandle typeInstHandle, bool namespaceQualified) + { + // Stack trace metadata ignores the instantiation arguments of the type in the CLR + TypeInstantiationSignature typeInst = _metadataReader.GetTypeInstantiationSignature(typeInstHandle); + EmitTypeName(typeInst.GenericType, namespaceQualified); + } + + /// + /// Emit SZArray (single-dimensional array with zero lower bound) type. + /// + /// SZArray type specification signature handle + /// When set to true, include namespace information + private void EmitSZArrayTypeName(SZArraySignatureHandle szArraySigHandle, bool namespaceQualified) + { + SZArraySignature szArraySig = _metadataReader.GetSZArraySignature(szArraySigHandle); + EmitTypeName(szArraySig.ElementType, namespaceQualified); + _outputBuilder.Append("[]"); + } + + /// + /// Emit multi-dimensional array type. + /// + /// Multi-dimensional array type specification signature handle + /// When set to true, include namespace information + private void EmitArrayTypeName(ArraySignatureHandle arraySigHandle, bool namespaceQualified) + { + ArraySignature arraySig = _metadataReader.GetArraySignature(arraySigHandle); + EmitTypeName(arraySig.ElementType, namespaceQualified); + _outputBuilder.Append('['); + if (arraySig.Rank > 1) + { + _outputBuilder.Append(',', arraySig.Rank - 1); + } + else + { + _outputBuilder.Append('*'); + } + _outputBuilder.Append(']'); + } + + /// + /// Emit pointer type. + /// + /// Pointer type specification signature handle + private void EmitPointerTypeName(PointerSignatureHandle pointerSigHandle) + { + PointerSignature pointerSig = _metadataReader.GetPointerSignature(pointerSigHandle); + EmitTypeName(pointerSig.Type, namespaceQualified: false); + _outputBuilder.Append('*'); + } + + /// + /// Emit by-reference type. + /// + /// ByReference type specification signature handle + private void EmitByRefTypeName(ByReferenceSignatureHandle byRefSigHandle) + { + ByReferenceSignature byRefSig = _metadataReader.GetByReferenceSignature(byRefSigHandle); + EmitTypeName(byRefSig.Type, namespaceQualified: false); + _outputBuilder.Append('&'); + } + + /// + /// Emit angle-bracketed list of type / method generic arguments. + /// + /// Collection of generic argument type handles + private void EmitGenericArguments(HandleCollection genericArguments) + { + _outputBuilder.Append('['); + EmitTypeVector(genericArguments); + _outputBuilder.Append(']'); + } + + /// + /// Emit a string (represented by a serialized ConstantStringValue) to the output string builder. + /// + /// Constant string value token (offset within stack trace native metadata) + private void EmitString(ConstantStringValueHandle stringHandle) + { + _outputBuilder.Append(_metadataReader.GetConstantStringValue(stringHandle).Value); + } + + private struct SigTypeContext + { + private readonly object _typeContext; + private readonly object _methodContext; + + public SigTypeContext(object typeContext, object methodContext) + { + _typeContext = typeContext; + _methodContext = methodContext; + } + + public static Handle GetHandleAt(HandleCollection collection, int index) + { + int currentIndex = 0; + + foreach (var currentArg in collection) + { + if (currentIndex == index) + return currentArg; + currentIndex++; + } + + Debug.Assert(false); + return default(Handle); + } + + public static Handle GetHandleAt(GenericParameterHandleCollection collection, int index) + { + int currentIndex = 0; + + foreach (var currentArg in collection) + { + if (currentIndex == index) + return currentArg; + currentIndex++; + } + + Debug.Assert(false); + return default(Handle); + } + + public Handle GetTypeVariable(int index) + { + return _typeContext is GenericParameterHandleCollection ? + GetHandleAt((GenericParameterHandleCollection)_typeContext, index) : + GetHandleAt((HandleCollection)_typeContext, index); + } + + public Handle GetMethodVariable(int index) + { + return _methodContext is GenericParameterHandleCollection ? + GetHandleAt((GenericParameterHandleCollection)_methodContext, index) : + GetHandleAt((HandleCollection)_methodContext, index); + } + + private static object GetTypeContext(MetadataReader metadataReader, Handle handle) + { + switch (handle.HandleType) + { + case HandleType.MemberReference: + MemberReference memberRef = handle.ToMemberReferenceHandle(metadataReader).GetMemberReference(metadataReader); + return GetTypeContext(metadataReader, memberRef.Parent); + + case HandleType.QualifiedMethod: + QualifiedMethod qualifiedMethod = handle.ToQualifiedMethodHandle(metadataReader).GetQualifiedMethod(metadataReader); + return GetTypeContext(metadataReader, qualifiedMethod.EnclosingType); + + case HandleType.TypeDefinition: + TypeDefinition typeDef = handle.ToTypeDefinitionHandle(metadataReader).GetTypeDefinition(metadataReader); + return typeDef.GenericParameters; + + case HandleType.TypeReference: + return default(HandleCollection); + + case HandleType.TypeSpecification: + TypeSpecification typeSpec = handle.ToTypeSpecificationHandle(metadataReader).GetTypeSpecification(metadataReader); + if (typeSpec.Signature.HandleType != HandleType.TypeInstantiationSignature) + { + Debug.Assert(false); + return default(HandleCollection); + } + return typeSpec.Signature.ToTypeInstantiationSignatureHandle(metadataReader).GetTypeInstantiationSignature(metadataReader).GenericTypeArguments; + + default: + Debug.Assert(false); + return default(HandleCollection); + } + } + + public static SigTypeContext FromMethod(MetadataReader metadataReader, Handle methodHandle) + { + object typeContext; + object methodContext; + + switch (methodHandle.HandleType) + { + case HandleType.MemberReference: + typeContext = GetTypeContext(metadataReader, methodHandle); + methodContext = default(HandleCollection); + break; + + case HandleType.MethodInstantiation: + MethodInstantiation methodInst = methodHandle.ToMethodInstantiationHandle(metadataReader).GetMethodInstantiation(metadataReader); + typeContext = GetTypeContext(metadataReader, methodInst.Method); + methodContext = methodInst.GenericTypeArguments; + break; + + case HandleType.QualifiedMethod: + QualifiedMethod qualifiedMethod = methodHandle.ToQualifiedMethodHandle(metadataReader).GetQualifiedMethod(metadataReader); + typeContext = GetTypeContext(metadataReader, qualifiedMethod.EnclosingType); + methodContext = qualifiedMethod.Method.GetMethod(metadataReader).GenericParameters; + break; + default: + Debug.Assert(false); + return default(SigTypeContext); + } + + return new SigTypeContext(typeContext, methodContext); + } + + public static SigTypeContext FromMethod(MetadataReader metadataReader, TypeDefinitionHandle enclosingTypeHandle, MethodHandle methodHandle) + { + Method method = metadataReader.GetMethod(methodHandle); + return new SigTypeContext(GetTypeContext(metadataReader, enclosingTypeHandle), method.GenericParameters); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/StackTraceMetadata.cs b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/StackTraceMetadata.cs new file mode 100644 index 00000000000000..9e95dd3c57a4ff --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/StackTraceMetadata.cs @@ -0,0 +1,246 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.Metadata.NativeFormat; +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.TypeLoader; +using Internal.TypeSystem; + +using ReflectionExecution = Internal.Reflection.Execution.ReflectionExecution; + +namespace Internal.StackTraceMetadata +{ + /// + /// This helper class is used to resolve non-reflectable method names using a special + /// compiler-generated metadata blob to enhance quality of exception call stacks + /// in situations where symbol information is not available. + /// + internal static class StackTraceMetadata + { + /// + /// Module address-keyed map of per-module method name resolvers. + /// + static PerModuleMethodNameResolverHashtable _perModuleMethodNameResolverHashtable; + + /// + /// Eager startup initialization of stack trace metadata support creates + /// the per-module method name resolver hashtable and registers the runtime augment + /// for metadata-based stack trace resolution. + /// + internal static void Initialize() + { + _perModuleMethodNameResolverHashtable = new PerModuleMethodNameResolverHashtable(); + RuntimeAugments.InitializeStackTraceMetadataSupport(new StackTraceMetadataCallbacksImpl()); + } + + /// + /// Locate the containing module for a method and try to resolve its name based on start address. + /// + public static unsafe string GetMethodNameFromStartAddressIfAvailable(IntPtr methodStartAddress) + { + IntPtr moduleStartAddress = RuntimeAugments.GetOSModuleFromPointer(methodStartAddress); + int rva = (int)((byte*)methodStartAddress - (byte*)moduleStartAddress); + foreach (TypeManagerHandle handle in ModuleList.Enumerate()) + { + if (handle.OsModuleBase == moduleStartAddress) + { + string name = _perModuleMethodNameResolverHashtable.GetOrCreateValue(handle.GetIntPtrUNSAFE()).GetMethodNameFromRvaIfAvailable(rva); + if (name != null) + return name; + } + } + + // We haven't found information in the stack trace metadata tables, but maybe reflection will have this + if (IsReflectionExecutionAvailable() && ReflectionExecution.TryGetMethodMetadataFromStartAddress(methodStartAddress, + out MetadataReader reader, + out TypeDefinitionHandle typeHandle, + out MethodHandle methodHandle)) + { + return MethodNameFormatter.FormatMethodName(reader, typeHandle, methodHandle); + } + + return null; + } + + // Can be rewritten to false through a feature switch. + private static bool IsReflectionExecutionAvailable() => true; + + /// + /// This hashtable supports mapping from module start addresses to per-module method name resolvers. + /// + private sealed class PerModuleMethodNameResolverHashtable : LockFreeReaderHashtable + { + /// + /// Given a key, compute a hash code. This function must be thread safe. + /// + protected override int GetKeyHashCode(IntPtr key) + { + return key.GetHashCode(); + } + + /// + /// Given a value, compute a hash code which would be identical to the hash code + /// for a key which should look up this value. This function must be thread safe. + /// This function must also not cause additional hashtable adds. + /// + protected override int GetValueHashCode(PerModuleMethodNameResolver value) + { + return GetKeyHashCode(value.ModuleAddress); + } + + /// + /// Compare a key and value. If the key refers to this value, return true. + /// This function must be thread safe. + /// + protected override bool CompareKeyToValue(IntPtr key, PerModuleMethodNameResolver value) + { + return key == value.ModuleAddress; + } + + /// + /// Compare a value with another value. Return true if values are equal. + /// This function must be thread safe. + /// + protected override bool CompareValueToValue(PerModuleMethodNameResolver value1, PerModuleMethodNameResolver value2) + { + return value1.ModuleAddress == value2.ModuleAddress; + } + + /// + /// Create a new value from a key. Must be threadsafe. Value may or may not be added + /// to collection. Return value must not be null. + /// + protected override PerModuleMethodNameResolver CreateValueFromKey(IntPtr key) + { + return new PerModuleMethodNameResolver(key); + } + } + + /// + /// Implementation of stack trace metadata callbacks. + /// + private sealed class StackTraceMetadataCallbacksImpl : StackTraceMetadataCallbacks + { + public override string TryGetMethodNameFromStartAddress(IntPtr methodStartAddress) + { + return GetMethodNameFromStartAddressIfAvailable(methodStartAddress); + } + } + + /// + /// Method name resolver for a single binary module + /// + private sealed class PerModuleMethodNameResolver + { + /// + /// Start address of the module in question. + /// + private readonly IntPtr _moduleAddress; + + /// + /// Dictionary mapping method RVA's to tokens within the metadata blob. + /// + private readonly Dictionary _methodRvaToTokenMap; + + /// + /// Metadata reader for the stack trace metadata. + /// + private readonly MetadataReader _metadataReader; + + /// + /// Publicly exposed module address property. + /// + public IntPtr ModuleAddress { get { return _moduleAddress; } } + + /// + /// Construct the per-module resolver by looking up the necessary blobs. + /// + public unsafe PerModuleMethodNameResolver(IntPtr moduleAddress) + { + _moduleAddress = moduleAddress; + + TypeManagerHandle handle = new TypeManagerHandle(moduleAddress); + ModuleInfo moduleInfo; + if (!ModuleList.Instance.TryGetModuleInfoByHandle(handle, out moduleInfo)) + { + // Module not found + return; + } + + NativeFormatModuleInfo nativeFormatModuleInfo = moduleInfo as NativeFormatModuleInfo; + if (nativeFormatModuleInfo == null) + { + // It is not a native format module + return; + } + + byte *metadataBlob; + uint metadataBlobSize; + + byte *rvaToTokenMapBlob; + uint rvaToTokenMapBlobSize; + + if (nativeFormatModuleInfo.TryFindBlob( + (int)ReflectionMapBlob.EmbeddedMetadata, + out metadataBlob, + out metadataBlobSize) && + nativeFormatModuleInfo.TryFindBlob( + (int)ReflectionMapBlob.BlobIdStackTraceMethodRvaToTokenMapping, + out rvaToTokenMapBlob, + out rvaToTokenMapBlobSize)) + { + _metadataReader = new MetadataReader(new IntPtr(metadataBlob), (int)metadataBlobSize); + + // RVA to token map consists of pairs of integers (method RVA - token) + int rvaToTokenMapEntryCount = (int)(rvaToTokenMapBlobSize / (2 * sizeof(int))); + _methodRvaToTokenMap = new Dictionary(rvaToTokenMapEntryCount); + PopulateRvaToTokenMap(handle, (int *)rvaToTokenMapBlob, rvaToTokenMapEntryCount); + } + } + + /// + /// Construct the dictionary mapping method RVAs to stack trace metadata tokens + /// within a single binary module. + /// + /// Module to use to construct the mapping + /// List of RVA - token pairs + /// Number of the RVA - token pairs in the list + private unsafe void PopulateRvaToTokenMap(TypeManagerHandle handle, int *rvaToTokenMap, int entryCount) + { + for (int entryIndex = 0; entryIndex < entryCount; entryIndex++) + { + int* pRelPtr32 = &rvaToTokenMap[2 * entryIndex + 0]; + byte* pointer = (byte*)pRelPtr32 + *pRelPtr32; + int methodRva = (int)(pointer - (byte*)handle.OsModuleBase); + int token = rvaToTokenMap[2 * entryIndex + 1]; + _methodRvaToTokenMap[methodRva] = token; + } + } + + /// + /// Try to resolve method name based on its address using the stack trace metadata + /// + public string GetMethodNameFromRvaIfAvailable(int rva) + { + if (_methodRvaToTokenMap == null) + { + // No stack trace metadata for this module + return null; + } + + int rawToken; + if (!_methodRvaToTokenMap.TryGetValue(rva, out rawToken)) + { + // Method RVA not found in the map + return null; + } + + return MethodNameFormatter.FormatMethodName(_metadataReader, Handle.FromIntToken(rawToken)); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/System.Private.StackTraceMetadata.csproj b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/System.Private.StackTraceMetadata.csproj new file mode 100644 index 00000000000000..c591e321e0d693 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src/System.Private.StackTraceMetadata.csproj @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + Internal\Runtime\MetadataBlob.cs + + + System\Runtime\CompilerServices\__BlockAllReflectionAttribute.cs + + + diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Core/AssemblyBinder.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Core/AssemblyBinder.cs new file mode 100644 index 00000000000000..0a7bab62ea3620 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Core/AssemblyBinder.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection; +using Internal.Metadata.NativeFormat; +using System.Reflection.Runtime.General; +using System.Runtime.InteropServices; + +namespace Internal.Reflection.Core +{ + // Auto StructLayout used to suppress warning that order of fields is not guaranteed in partial structs + [StructLayout(LayoutKind.Auto)] + public partial struct AssemblyBindResult + { + public MetadataReader Reader; + public ScopeDefinitionHandle ScopeDefinitionHandle; + public IEnumerable OverflowScopes; + } + + // + // Implements the assembly binding policy Reflection domain. This gets called any time the domain needs + // to resolve an assembly name. + // + // If the binder cannot locate an assembly, it must return null and set "exception" to an exception object. + // + public abstract class AssemblyBinder + { + public const string DefaultAssemblyNameForGetType = "System.Private.CoreLib"; + + public abstract bool Bind(RuntimeAssemblyName refName, bool cacheMissedLookups, out AssemblyBindResult result, out Exception exception); + + public abstract bool Bind(byte[] rawAssembly, byte[] rawSymbolStore, out AssemblyBindResult result, out Exception exception); + + public abstract bool Bind(string assemblyPath, out AssemblyBindResult bindResult, out Exception exception); + + public abstract IList GetLoadedAssemblies(); + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs new file mode 100644 index 00000000000000..3d2cb24adcbaa2 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs @@ -0,0 +1,280 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Generic; + +using System.Reflection.Runtime.General; + +using Internal.Reflection.Core; +using Internal.Runtime.TypeLoader; + +using Internal.Metadata.NativeFormat; + +namespace Internal.Reflection.Execution +{ + //============================================================================================================================= + // The assembly resolution policy for Project N's emulation of "classic reflection." + // + // The policy is very simple: the only assemblies that can be "loaded" are those that are statically linked into the running + // native process. There is no support for probing for assemblies in directories, user-supplied files, GACs, NICs or any + // other repository. + //============================================================================================================================= + public sealed partial class AssemblyBinderImplementation : AssemblyBinder + { + private AssemblyBinderImplementation() + { + _scopeGroups = new KeyValuePair[0]; + ModuleList.AddModuleRegistrationCallback(RegisterModule); + } + + public static AssemblyBinderImplementation Instance { get; } = new AssemblyBinderImplementation(); + + partial void BindEcmaFilePath(string assemblyPath, ref AssemblyBindResult bindResult, ref Exception exception, ref bool? result); + partial void BindEcmaByteArray(byte[] rawAssembly, byte[] rawSymbolStore, ref AssemblyBindResult bindResult, ref Exception exception, ref bool? result); + partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, bool cacheMissedLookups, ref AssemblyBindResult result, ref Exception exception, ref Exception preferredException, ref bool resultBoolean); + partial void InsertEcmaLoadedAssemblies(List loadedAssemblies); + + public sealed override bool Bind(string assemblyPath, out AssemblyBindResult bindResult, out Exception exception) + { + bool? result = null; + exception = null; + bindResult = default(AssemblyBindResult); + + BindEcmaFilePath(assemblyPath, ref bindResult, ref exception, ref result); + + // If the Ecma assembly binder isn't linked in, simply throw PlatformNotSupportedException + if (!result.HasValue) + throw new PlatformNotSupportedException(); + else + return result.Value; + } + + public sealed override bool Bind(byte[] rawAssembly, byte[] rawSymbolStore, out AssemblyBindResult bindResult, out Exception exception) + { + bool? result = null; + exception = null; + bindResult = default(AssemblyBindResult); + + BindEcmaByteArray(rawAssembly, rawSymbolStore, ref bindResult, ref exception, ref result); + + // If the Ecma assembly binder isn't linked in, simply throw PlatformNotSupportedException + if (!result.HasValue) + throw new PlatformNotSupportedException(); + else + return result.Value; + } + + public sealed override bool Bind(RuntimeAssemblyName refName, bool cacheMissedLookups, out AssemblyBindResult result, out Exception exception) + { + bool foundMatch = false; + result = default(AssemblyBindResult); + exception = null; + + Exception preferredException = null; + + foreach (KeyValuePair group in ScopeGroups) + { + if (AssemblyNameMatches(refName, group.Key, ref preferredException)) + { + if (foundMatch) + { + exception = new AmbiguousMatchException(); + return false; + } + + foundMatch = true; + ScopeDefinitionGroup scopeDefinitionGroup = group.Value; + + result.Reader = scopeDefinitionGroup.CanonicalScope.Reader; + result.ScopeDefinitionHandle = scopeDefinitionGroup.CanonicalScope.Handle; + result.OverflowScopes = scopeDefinitionGroup.OverflowScopes; + } + } + + BindEcmaAssemblyName(refName, cacheMissedLookups, ref result, ref exception, ref preferredException, ref foundMatch); + if (exception != null) + return false; + + if (!foundMatch) + { + exception = preferredException ?? new FileNotFoundException(SR.Format(SR.FileNotFound_AssemblyNotFound, refName.FullName)); + return false; + } + + return true; + } + + public sealed override IList GetLoadedAssemblies() + { + List loadedAssemblies = new List(ScopeGroups.Length); + foreach (KeyValuePair group in ScopeGroups) + { + ScopeDefinitionGroup scopeDefinitionGroup = group.Value; + + AssemblyBindResult result = default(AssemblyBindResult); + result.Reader = scopeDefinitionGroup.CanonicalScope.Reader; + result.ScopeDefinitionHandle = scopeDefinitionGroup.CanonicalScope.Handle; + result.OverflowScopes = scopeDefinitionGroup.OverflowScopes; + loadedAssemblies.Add(result); + } + + InsertEcmaLoadedAssemblies(loadedAssemblies); + + return loadedAssemblies; + } + + // + // Encapsulates the assembly ref->def matching policy. + // + private bool AssemblyNameMatches(RuntimeAssemblyName refName, RuntimeAssemblyName defName, ref Exception preferredException) + { + // + // The defName came from trusted metadata so it should be fully specified. + // + Debug.Assert(defName.Version != null); + Debug.Assert(defName.CultureName != null); + + if (!(refName.Name.Equals(defName.Name, StringComparison.OrdinalIgnoreCase))) + return false; + + if (refName.Version != null) + { + if (!AssemblyVersionMatches(refVersion: refName.Version, defVersion: defName.Version)) + { + preferredException = new FileLoadException(SR.Format(SR.FileLoadException_RefDefMismatch, refName.FullName, defName.Version, refName.Version)); + return false; + } + } + + if (refName.CultureName != null) + { + if (!(refName.CultureName.Equals(defName.CultureName))) + return false; + } + + // Strong names are ignored in .NET Core + + return true; + } + + private static bool AssemblyVersionMatches(Version refVersion, Version defVersion) + { + if (defVersion.Major < refVersion.Major) + return false; + if (defVersion.Major > refVersion.Major) + return true; + + if (defVersion.Minor < refVersion.Minor) + return false; + if (defVersion.Minor > refVersion.Minor) + return true; + + if (refVersion.Build == -1) + return true; + if (defVersion.Build < refVersion.Build) + return false; + if (defVersion.Build > refVersion.Build) + return true; + + if (refVersion.Revision == -1) + return true; + if (defVersion.Revision < refVersion.Revision) + return false; + + return true; + } + + /// + /// This callback gets called whenever a module gets registered. It adds the metadata reader + /// for the new module to the available scopes. The lock in ExecutionEnvironmentImplementation ensures + /// that this function may never be called concurrently so that we can assume that two threads + /// never update the reader and scope list at the same time. + /// + /// Module to register + private void RegisterModule(ModuleInfo moduleInfo) + { + NativeFormatModuleInfo nativeFormatModuleInfo = moduleInfo as NativeFormatModuleInfo; + + if (nativeFormatModuleInfo == null) + { + return; + } + + LowLevelDictionaryWithIEnumerable scopeGroups = new LowLevelDictionaryWithIEnumerable(); + foreach (KeyValuePair oldGroup in _scopeGroups) + { + scopeGroups.Add(oldGroup.Key, oldGroup.Value); + } + AddScopesFromReaderToGroups(scopeGroups, nativeFormatModuleInfo.MetadataReader); + + // Update reader and scope list + KeyValuePair[] scopeGroupsArray = new KeyValuePair[scopeGroups.Count]; + int i = 0; + foreach (KeyValuePair data in scopeGroups) + { + scopeGroupsArray[i] = data; + i++; + } + + _scopeGroups = scopeGroupsArray; + } + + private KeyValuePair[] ScopeGroups + { + get + { + return _scopeGroups; + } + } + + private void AddScopesFromReaderToGroups(LowLevelDictionaryWithIEnumerable groups, MetadataReader reader) + { + foreach (ScopeDefinitionHandle scopeDefinitionHandle in reader.ScopeDefinitions) + { + RuntimeAssemblyName defName = scopeDefinitionHandle.ToRuntimeAssemblyName(reader); + ScopeDefinitionGroup scopeDefinitionGroup; + if (groups.TryGetValue(defName, out scopeDefinitionGroup)) + { + scopeDefinitionGroup.AddOverflowScope(new QScopeDefinition(reader, scopeDefinitionHandle)); + } + else + { + scopeDefinitionGroup = new ScopeDefinitionGroup(new QScopeDefinition(reader, scopeDefinitionHandle)); + groups.Add(defName, scopeDefinitionGroup); + } + } + } + + private volatile KeyValuePair[] _scopeGroups; + + private class ScopeDefinitionGroup + { + public ScopeDefinitionGroup(QScopeDefinition canonicalScope) + { + _canonicalScope = canonicalScope; + } + + public QScopeDefinition CanonicalScope { get { return _canonicalScope; } } + + public IEnumerable OverflowScopes + { + get + { + return _overflowScopes.ToArray(); + } + } + + public void AddOverflowScope(QScopeDefinition overflowScope) + { + _overflowScopes.Add(overflowScope); + } + + private readonly QScopeDefinition _canonicalScope; + private ArrayBuilder _overflowScopes; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 00000000000000..777d0ae837902c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Runtime.TypeLoader; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + TypeLoaderEnvironment.Initialize(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionInfo.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionInfo.cs new file mode 100644 index 00000000000000..5562a771af20e5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionInfo.cs @@ -0,0 +1,613 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.Runtime.CallConverter; + +using ThunkKind = Internal.Runtime.TypeLoader.CallConverterThunk.ThunkKind; + +namespace Internal.Runtime.TypeLoader +{ + internal enum CallConversionInfoRegistrationKind + { + UsesMethodSignatureAndGenericArgs, + UsesArgIteratorData + } + + internal class CallConversionInfo : IEquatable + { + private CallConversionInfo() { } + + private static int s_callConvertersCount; + private static volatile CallConversionInfo[] s_callConverters = new CallConversionInfo[512]; + + private static Lock s_callConvertersCacheLock = new Lock(); + private static LowLevelDictionary s_callConvertersCache = new LowLevelDictionary(); + + private CallConversionInfoRegistrationKind _registrationKind; + + // + // Thunk data + // + private ThunkKind _thunkKind; + private IntPtr _targetFunctionPointer; + private IntPtr _instantiatingArg; + private ArgIteratorData _argIteratorData; + private bool[] _paramsByRefForced; + + // + // Method signature and generic context info. Signatures are parsed lazily when they are really needed + // + private RuntimeSignature _methodSignature; + private volatile bool _signatureParsed; + private RuntimeTypeHandle[] _typeArgs; + private RuntimeTypeHandle[] _methodArgs; + + private int? _hashCode; + +#if CCCONVERTER_TRACE + internal string ThunkKindString() + { + switch (_thunkKind) + { + case ThunkKind.StandardToStandardInstantiating: return "StandardToStandardInstantiating"; + case ThunkKind.StandardToGenericInstantiating: return "StandardToGenericInstantiating"; + case ThunkKind.StandardToGenericInstantiatingIfNotHasThis: return "StandardToGenericInstantiatingIfNotHasThis"; + case ThunkKind.StandardToGenericPassthruInstantiating: return "StandardToGenericPassthruInstantiating"; + case ThunkKind.StandardToGenericPassthruInstantiatingIfNotHasThis: return "StandardToGenericPassthruInstantiatingIfNotHasThis"; + case ThunkKind.StandardToGeneric: return "StandardToGeneric"; + case ThunkKind.GenericToStandard: return "GenericToStandard"; + case ThunkKind.StandardUnboxing: return "StandardUnboxing"; + case ThunkKind.StandardUnboxingAndInstantiatingGeneric: return "StandardUnboxingAndInstantiatingGeneric"; + case ThunkKind.GenericToStandardWithTargetPointerArg: return "GenericToStandardWithTargetPointerArg"; + case ThunkKind.GenericToStandardWithTargetPointerArgAndParamArg: return "GenericToStandardWithTargetPointerArgAndParamArg"; + case ThunkKind.GenericToStandardWithTargetPointerArgAndMaybeParamArg: return "GenericToStandardWithTargetPointerArgAndMaybeParamArg"; + case ThunkKind.DelegateInvokeOpenStaticThunk: return "DelegateInvokeOpenStaticThunk"; + case ThunkKind.DelegateInvokeClosedStaticThunk: return "DelegateInvokeClosedStaticThunk"; + case ThunkKind.DelegateInvokeOpenInstanceThunk: return "DelegateInvokeOpenInstanceThunk"; + case ThunkKind.DelegateInvokeInstanceClosedOverGenericMethodThunk: return "DelegateInvokeInstanceClosedOverGenericMethodThunk"; + case ThunkKind.DelegateMulticastThunk: return "DelegateMulticastThunk"; + case ThunkKind.DelegateObjectArrayThunk: return "DelegateObjectArrayThunk"; + case ThunkKind.DelegateDynamicInvokeThunk: return "DelegateDynamicInvokeThunk"; + case ThunkKind.ReflectionDynamicInvokeThunk: return "ReflectionDynamicInvokeThunk"; + } + return ((int)_thunkKind).LowLevelToString(); + } +#endif + + + public override bool Equals(object obj) + { + if (this == obj) return true; + + CallConversionInfo other = obj as CallConversionInfo; + if (other == null) return false; + + return Equals(other); + } + + public bool Equals(CallConversionInfo other) + { + if (_registrationKind != other._registrationKind) return false; + if (_thunkKind != other._thunkKind) return false; + if (_targetFunctionPointer != other._targetFunctionPointer) return false; + if (_instantiatingArg != other._instantiatingArg) return false; + + switch (_registrationKind) + { + case CallConversionInfoRegistrationKind.UsesMethodSignatureAndGenericArgs: + { + return (_methodSignature.StructuralEquals(other._methodSignature) && + ArraysAreEqual(_typeArgs, other._typeArgs) && + ArraysAreEqual(_methodArgs, other._methodArgs)); + } + + case CallConversionInfoRegistrationKind.UsesArgIteratorData: + { + return _argIteratorData.Equals(other._argIteratorData) && + ArraysAreEqual(_paramsByRefForced, other._paramsByRefForced); + } + } + + Debug.Fail("UNREACHABLE"); + return false; + } + + private bool ArraysAreEqual(T[] array1, T[] array2) + { + if (array1 == null) + return array2 == null; + + if (array2 == null || array1.Length != array2.Length) + return false; + + for (int i = 0; i < array1.Length; i++) + if (!array1[i].Equals(array2[i])) + return false; + + return true; + } + + public override int GetHashCode() + { + if (!_hashCode.HasValue) + { + int hashCode = 79 + 971 * (int)_thunkKind + 83 * (_targetFunctionPointer.GetHashCode() >> 7) + 13 * (_instantiatingArg.GetHashCode() >> 3); + switch (_registrationKind) + { + case CallConversionInfoRegistrationKind.UsesMethodSignatureAndGenericArgs: + hashCode ^= _methodSignature.GetHashCode(); + hashCode = _typeArgs == null ? hashCode : TypeHashingAlgorithms.ComputeGenericInstanceHashCode(hashCode, _typeArgs); + hashCode = _methodArgs == null ? hashCode : TypeHashingAlgorithms.ComputeGenericInstanceHashCode(hashCode, _methodArgs); + break; + case CallConversionInfoRegistrationKind.UsesArgIteratorData: + hashCode ^= _argIteratorData.GetHashCode(); + break; + } + _hashCode = hashCode; + } + + return _hashCode.Value; + } + + #region Construction + // + // Lazily parse the method signature, and construct the call converter data + // + private void EnsureCallConversionInfoLoaded() + { + if (_signatureParsed) + return; + + lock (this) + { + // Check if race was won by another thread and the signature got parsed + if (_signatureParsed) return; + + TypeSystemContext context = TypeSystemContextFactory.Create(); + { + Instantiation typeInstantiation = Instantiation.Empty; + Instantiation methodInstantiation = Instantiation.Empty; + + if (_typeArgs != null && _typeArgs.Length > 0) + typeInstantiation = context.ResolveRuntimeTypeHandles(_typeArgs); + if (_methodArgs != null && _methodArgs.Length > 0) + methodInstantiation = context.ResolveRuntimeTypeHandles(_methodArgs); + + bool hasThis; + TypeDesc[] parameters; + bool[] paramsByRefForced; + if (!TypeLoaderEnvironment.Instance.GetCallingConverterDataFromMethodSignature(context, _methodSignature, typeInstantiation, methodInstantiation, out hasThis, out parameters, out paramsByRefForced)) + { + Debug.Assert(false); + Environment.FailFast("Failed to get type handles for parameters in method signature"); + } + Debug.Assert(parameters != null && parameters.Length >= 1); + + bool[] byRefParameters = new bool[parameters.Length]; + RuntimeTypeHandle[] parameterHandles = new RuntimeTypeHandle[parameters.Length]; + + for (int j = 0; j < parameters.Length; j++) + { + ByRefType parameterAsByRefType = parameters[j] as ByRefType; + if (parameterAsByRefType != null) + { + parameterAsByRefType.ParameterType.RetrieveRuntimeTypeHandleIfPossible(); + parameterHandles[j] = parameterAsByRefType.ParameterType.RuntimeTypeHandle; + byRefParameters[j] = true; + } + else + { + parameters[j].RetrieveRuntimeTypeHandleIfPossible(); + parameterHandles[j] = parameters[j].RuntimeTypeHandle; + byRefParameters[j] = false; + } + + Debug.Assert(!parameterHandles[j].IsNull()); + } + + // Build thunk data + TypeHandle thReturnType = new TypeHandle(CallConverterThunk.GetByRefIndicatorAtIndex(0, byRefParameters), parameterHandles[0]); + TypeHandle[] thParameters = null; + if (parameters.Length > 1) + { + thParameters = new TypeHandle[parameters.Length - 1]; + for (int i = 1; i < parameters.Length; i++) + { + thParameters[i - 1] = new TypeHandle(CallConverterThunk.GetByRefIndicatorAtIndex(i, byRefParameters), parameterHandles[i]); + } + } + + _argIteratorData = new ArgIteratorData(hasThis, false, thParameters, thReturnType); + + // StandardToStandard thunks don't actually need any parameters to change their ABI + // so don't force any params to be adjusted + if (!StandardToStandardThunk) + { + _paramsByRefForced = paramsByRefForced; + } + } + TypeSystemContextFactory.Recycle(context); + + _signatureParsed = true; + } + } + + public static int RegisterCallConversionInfo(ThunkKind thunkKind, IntPtr targetPointer, RuntimeSignature methodSignature, IntPtr instantiatingArg, RuntimeTypeHandle[] typeArgs, RuntimeTypeHandle[] methodArgs) + { + CallConversionInfo newConversionInfo = new CallConversionInfo(); + newConversionInfo._registrationKind = CallConversionInfoRegistrationKind.UsesMethodSignatureAndGenericArgs; + newConversionInfo._thunkKind = thunkKind; + newConversionInfo._targetFunctionPointer = targetPointer; + newConversionInfo._methodSignature = methodSignature; + newConversionInfo._instantiatingArg = instantiatingArg; + newConversionInfo._typeArgs = typeArgs; + newConversionInfo._methodArgs = methodArgs; + newConversionInfo._signatureParsed = false; + + return AddConverter(newConversionInfo); + } + + public static int RegisterCallConversionInfo(ThunkKind thunkKind, + IntPtr targetPointer, + IntPtr instantiatingArg, + bool hasThis, + TypeHandle returnType, + TypeHandle[] parameterTypes, + bool[] paramsByRefForced) + { + CallConversionInfo newConversionInfo = new CallConversionInfo(); + newConversionInfo._registrationKind = CallConversionInfoRegistrationKind.UsesArgIteratorData; + newConversionInfo._thunkKind = thunkKind; + newConversionInfo._targetFunctionPointer = targetPointer; + newConversionInfo._instantiatingArg = instantiatingArg; + newConversionInfo._argIteratorData = new ArgIteratorData(hasThis, false, parameterTypes, returnType); + newConversionInfo._paramsByRefForced = paramsByRefForced; + newConversionInfo._signatureParsed = true; + + return AddConverter(newConversionInfo); + } + + public static int RegisterCallConversionInfo(ThunkKind thunkKind, + IntPtr targetPointer, + IntPtr instantiatingArg, + ArgIteratorData argIteratorData, + bool[] paramsByRefForced) + { + Debug.Assert(argIteratorData != null); + + CallConversionInfo newConversionInfo = new CallConversionInfo(); + newConversionInfo._registrationKind = CallConversionInfoRegistrationKind.UsesArgIteratorData; + newConversionInfo._thunkKind = thunkKind; + newConversionInfo._targetFunctionPointer = targetPointer; + newConversionInfo._instantiatingArg = instantiatingArg; + newConversionInfo._argIteratorData = argIteratorData; + newConversionInfo._paramsByRefForced = paramsByRefForced; + newConversionInfo._signatureParsed = true; + + return AddConverter(newConversionInfo); + } + + private static int AddConverter(CallConversionInfo newConversionInfo) + { + using (LockHolder.Hold(s_callConvertersCacheLock)) + { + int converterId; + if (s_callConvertersCache.TryGetValue(newConversionInfo, out converterId)) + { + Debug.Assert((uint)converterId < (uint)s_callConvertersCount && s_callConverters[converterId].Equals(newConversionInfo)); + return converterId; + } + + if (s_callConvertersCount >= s_callConverters.Length) + { + CallConversionInfo[] newArray = new CallConversionInfo[s_callConverters.Length * 2]; + Array.Copy(s_callConverters, newArray, s_callConvertersCount); + s_callConverters = newArray; + } + + s_callConverters[s_callConvertersCount++] = newConversionInfo; + s_callConvertersCache[newConversionInfo] = s_callConvertersCount - 1; + return s_callConvertersCount - 1; + } + } + #endregion + + public static CallConversionInfo GetConverter(int id) + { + return s_callConverters[id]; + } + + #region Conversion Properties + public IntPtr TargetFunctionPointer + { + get + { + return _targetFunctionPointer; + } + } + + public bool StandardToStandardThunk + { + get + { + switch (_thunkKind) + { + case ThunkKind.StandardToStandardInstantiating: + return true; + + default: + return false; + } + } + } + + public bool CallerHasExtraParameterWhichIsFunctionTarget + { + get + { + switch (_thunkKind) + { + case ThunkKind.GenericToStandardWithTargetPointerArg: + case ThunkKind.GenericToStandardWithTargetPointerArgAndParamArg: + case ThunkKind.GenericToStandardWithTargetPointerArgAndMaybeParamArg: + return true; + } + + return false; + } + } + + public bool CalleeMayHaveParamType + { + get + { + return _thunkKind == ThunkKind.GenericToStandardWithTargetPointerArgAndMaybeParamArg; + } + } + + public bool IsUnboxingThunk + { + get + { + switch (_thunkKind) + { + case ThunkKind.StandardUnboxing: + case ThunkKind.StandardUnboxingAndInstantiatingGeneric: + return true; + } + return false; + } + } + + public bool IsDelegateThunk + { + get + { + switch (_thunkKind) + { + case ThunkKind.DelegateInvokeOpenStaticThunk: + case ThunkKind.DelegateInvokeClosedStaticThunk: + case ThunkKind.DelegateInvokeOpenInstanceThunk: + case ThunkKind.DelegateInvokeInstanceClosedOverGenericMethodThunk: + case ThunkKind.DelegateMulticastThunk: + case ThunkKind.DelegateObjectArrayThunk: + case ThunkKind.DelegateDynamicInvokeThunk: + return true; + } + return false; + } + } + + public bool TargetDelegateFunctionIsExtraFunctionPointerOrDataField + { + get + { + switch (_thunkKind) + { + case ThunkKind.DelegateInvokeOpenStaticThunk: + case ThunkKind.DelegateInvokeClosedStaticThunk: + case ThunkKind.DelegateInvokeOpenInstanceThunk: + case ThunkKind.DelegateInvokeInstanceClosedOverGenericMethodThunk: + return true; + } + return false; + } + } + + public bool IsOpenInstanceDelegateThunk { get { return _thunkKind == ThunkKind.DelegateInvokeOpenInstanceThunk; } } + + public bool IsClosedStaticDelegate { get { return _thunkKind == ThunkKind.DelegateInvokeClosedStaticThunk; } } + + public bool IsMulticastDelegate { get { return _thunkKind == ThunkKind.DelegateMulticastThunk; } } + + public bool IsObjectArrayDelegateThunk { get { return _thunkKind == ThunkKind.DelegateObjectArrayThunk; } } + + public bool IsDelegateDynamicInvokeThunk { get { return _thunkKind == ThunkKind.DelegateDynamicInvokeThunk; } } + + public bool IsReflectionDynamicInvokerThunk { get { return _thunkKind == ThunkKind.ReflectionDynamicInvokeThunk; } } + + public bool IsAnyDynamicInvokerThunk + { + get + { + switch (_thunkKind) + { + case ThunkKind.DelegateDynamicInvokeThunk: + case ThunkKind.ReflectionDynamicInvokeThunk: + return true; + } + return false; + } + } + + public bool IsStaticDelegateThunk + { + get + { + switch (_thunkKind) + { + case ThunkKind.DelegateInvokeOpenStaticThunk: + case ThunkKind.DelegateInvokeClosedStaticThunk: + return true; + } + return false; + } + } + + public bool IsThisPointerInDelegateData + { + get + { + switch (_thunkKind) + { + case ThunkKind.DelegateInvokeOpenInstanceThunk: + case ThunkKind.DelegateInvokeInstanceClosedOverGenericMethodThunk: + case ThunkKind.DelegateMulticastThunk: + case ThunkKind.DelegateDynamicInvokeThunk: + return true; + } + return false; + } + } + + public IntPtr InstantiatingStubArgument + { + get + { + return _instantiatingArg; + } + } + + public ArgIteratorData ArgIteratorData + { + get + { + EnsureCallConversionInfoLoaded(); + return _argIteratorData; + } + } + + public bool HasKnownTargetPointerAndInstantiatingArgument + { + get + { + // If the target method pointer and/or dictionary are passed as arguments to the converter, they are + // considered unknown. + // Similarly, delegate thunks and reflection DynamicInvoke thunks do not have any target pointer or + // dictionary pointers stored in their CallConversionInfo structures. + if (CallerHasExtraParameterWhichIsFunctionTarget || IsDelegateThunk || IsAnyDynamicInvokerThunk || _targetFunctionPointer == IntPtr.Zero) + return false; + + if (_instantiatingArg != IntPtr.Zero) + return true; + + // Null instantiating arguments are considered known values for non-instantiating stubs. + return _thunkKind == ThunkKind.StandardToGeneric || _thunkKind == ThunkKind.GenericToStandard || _thunkKind == ThunkKind.StandardUnboxing; + } + } + + public bool CalleeHasParamType + { + get + { + switch (_thunkKind) + { + case ThunkKind.StandardUnboxingAndInstantiatingGeneric: + case ThunkKind.GenericToStandardWithTargetPointerArgAndParamArg: + case ThunkKind.StandardToGenericInstantiating: + case ThunkKind.StandardToStandardInstantiating: + case ThunkKind.StandardToGenericPassthruInstantiating: + return true; + + case ThunkKind.StandardToGenericPassthruInstantiatingIfNotHasThis: + case ThunkKind.StandardToGenericInstantiatingIfNotHasThis: + EnsureCallConversionInfoLoaded(); + return !_argIteratorData.HasThis(); + } + + return false; + } + } + + public bool CallerHasParamType + { + get + { + switch (_thunkKind) + { + case ThunkKind.GenericToStandardWithTargetPointerArgAndParamArg: + case ThunkKind.StandardToGenericPassthruInstantiating: + return true; + + case ThunkKind.StandardToGenericPassthruInstantiatingIfNotHasThis: + return CalleeHasParamType; + } + return false; + } + } + + private bool ForcedByRefParametersAreCaller + { + get + { + switch (_thunkKind) + { + case ThunkKind.GenericToStandard: + case ThunkKind.GenericToStandardWithTargetPointerArg: + case ThunkKind.GenericToStandardWithTargetPointerArgAndParamArg: + case ThunkKind.GenericToStandardWithTargetPointerArgAndMaybeParamArg: + return true; + } + return false; + } + } + + public bool[] CallerForcedByRefData + { + get + { + EnsureCallConversionInfoLoaded(); + + if (ForcedByRefParametersAreCaller && !IsDelegateThunk) + { + return _paramsByRefForced; + } + else + { + return null; + } + } + } + + public bool[] CalleeForcedByRefData + { + get + { + EnsureCallConversionInfoLoaded(); + + if (!ForcedByRefParametersAreCaller && !IsDelegateThunk) + { + return _paramsByRefForced; + } + else + { + return null; + } + } + } + #endregion + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionParameters.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionParameters.cs new file mode 100644 index 00000000000000..475b77b4820458 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionParameters.cs @@ -0,0 +1,678 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +#if TARGET_ARM +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define CALLDESCR_FPARGREGSARERETURNREGS // The return value floating point registers are the same as the argument registers +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define FEATURE_HFA +#elif TARGET_ARM64 +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define CALLDESCR_FPARGREGSARERETURNREGS // The return value floating point registers are the same as the argument registers +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define ENREGISTERED_PARAMTYPE_MAXSIZE +#define FEATURE_HFA +#elif TARGET_X86 +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#define CALLINGCONVENTION_CALLEE_POPS +#elif TARGET_AMD64 +#if UNIXAMD64 +#define UNIX_AMD64_ABI +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#else +#endif +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define CALLDESCR_FPARGREGSARERETURNREGS // The return value floating point registers are the same as the argument registers +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define ENREGISTERED_PARAMTYPE_MAXSIZE +#elif TARGET_WASM +#else +#error Unknown architecture! +#endif + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Internal.Runtime.Augments; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.Runtime.CallConverter; + +using ArgIterator = Internal.Runtime.CallConverter.ArgIterator; +using CallingConvention = Internal.Runtime.CallConverter.CallingConvention; + +namespace Internal.Runtime.TypeLoader +{ + internal unsafe struct CallConversionParameters + { + internal class GCHandleContainer + { + internal UnsafeGCHandle _thisPtrHandle; + internal UnsafeGCHandle _dynamicInvokeArgHandle; + internal UnsafeGCHandle _returnObjectHandle; + + internal GCHandleContainer() + { + // Allocations of pinned gc handles done only once during the lifetime of a thread + _thisPtrHandle = UnsafeGCHandle.Alloc(null, GCHandleType.Pinned); + _dynamicInvokeArgHandle = UnsafeGCHandle.Alloc(null, GCHandleType.Pinned); + _returnObjectHandle = UnsafeGCHandle.Alloc(null, GCHandleType.Pinned); + } + + ~GCHandleContainer() + { + // Free all the pinned objects when the thread dies to avoid gchandle leaks + _thisPtrHandle.Free(); + _dynamicInvokeArgHandle.Free(); + _returnObjectHandle.Free(); + } + } + + private struct DelegateData + { + internal Delegate _delegateObject; + internal object _firstParameter; + internal object _helperObject; + internal IntPtr _extraFunctionPointerOrData; + internal IntPtr _functionPointer; + + // Computed fields + internal object _boxedFirstParameter; + internal object _multicastThisPointer; + internal int _multicastTargetCount; + } + + internal CallConversionInfo _conversionInfo; + internal ArgIterator _callerArgs; + internal ArgIterator _calleeArgs; + internal byte* _callerTransitionBlock; + internal IntPtr _invokeReturnValue; + internal bool _copyReturnValue; + internal object[] _dynamicInvokeParams; + internal CallConverterThunk.DynamicInvokeByRefArgObjectWrapper[] _dynamicInvokeByRefObjectArgs; + private IntPtr _instantiatingStubArgument; + private IntPtr _functionPointerToCall; + private DelegateData _delegateData; + + [ThreadStatic] + internal static GCHandleContainer s_pinnedGCHandles; + + // Signature of the DynamicInvokeImpl method on delegates is always the same. + private static ArgIteratorData s_delegateDynamicInvokeImplArgIteratorData = new ArgIteratorData(true, false, new TypeHandle[]{ + new TypeHandle(false, typeof(object).TypeHandle), + new TypeHandle(false, typeof(IntPtr).TypeHandle), + new TypeHandle(true, typeof(InvokeUtils.ArgSetupState).TypeHandle) + }, new TypeHandle(false, typeof(object).TypeHandle)); + + // Signature of all the InvokeRetXYZ reflection invoker stubs is always the same. + private static ArgIteratorData s_reflectionDynamicInvokeImplArgIteratorData = new ArgIteratorData(false, false, new TypeHandle[]{ + new TypeHandle(false, typeof(object).TypeHandle), + new TypeHandle(false, typeof(IntPtr).TypeHandle), + new TypeHandle(true, typeof(InvokeUtils.ArgSetupState).TypeHandle), + new TypeHandle(false, typeof(bool).TypeHandle) + }, new TypeHandle(false, typeof(object).TypeHandle)); + + internal CallConversionParameters(CallConversionInfo conversionInfo, IntPtr callerTransitionBlockParam) + { + // Make sure the thred static variable has been initialized for this thread + s_pinnedGCHandles = s_pinnedGCHandles ?? new GCHandleContainer(); + + _conversionInfo = conversionInfo; + _callerTransitionBlock = (byte*)callerTransitionBlockParam.ToPointer(); + _functionPointerToCall = conversionInfo.TargetFunctionPointer; + _instantiatingStubArgument = conversionInfo.InstantiatingStubArgument; + _delegateData = default(DelegateData); + _calleeArgs = default(ArgIterator); + _invokeReturnValue = IntPtr.Zero; + _copyReturnValue = true; + _dynamicInvokeParams = null; + _dynamicInvokeByRefObjectArgs = null; + + // + // Setup input argument iterator for the caller + // + ArgIteratorData callerIteratorData; + + if (conversionInfo.IsDelegateDynamicInvokeThunk) + callerIteratorData = s_delegateDynamicInvokeImplArgIteratorData; + else if (conversionInfo.IsReflectionDynamicInvokerThunk) + callerIteratorData = s_reflectionDynamicInvokeImplArgIteratorData; + else + callerIteratorData = conversionInfo.ArgIteratorData; + + _callerArgs = new ArgIterator(callerIteratorData, + callerIteratorData.HasThis() ? + CallingConvention.ManagedInstance : + CallingConvention.ManagedStatic, + conversionInfo.CallerHasParamType, + conversionInfo.CallerHasExtraParameterWhichIsFunctionTarget, + conversionInfo.CallerForcedByRefData, + false, false); // Setup input + + bool forceCalleeHasParamType = false; + + // If the callee MAY have a param type, we need to know before we create the callee arg iterator + // To do this we need to actually load the target address and see if it has the generic method pointer + // bit set. + if (conversionInfo.CalleeMayHaveParamType) + { + ArgIterator callerArgsLookupTargetFunctionPointer = new ArgIterator(conversionInfo.ArgIteratorData, + conversionInfo.ArgIteratorData.HasThis() ? + CallingConvention.ManagedInstance : + CallingConvention.ManagedStatic, + conversionInfo.CallerHasParamType, + conversionInfo.CallerHasExtraParameterWhichIsFunctionTarget, + conversionInfo.CallerForcedByRefData, + false, false); + + // Find the last valid caller offset. That's the offset of the target function pointer. + int ofsCallerValid = TransitionBlock.InvalidOffset; + while (true) + { + // Setup argument offsets. + int ofsCallerTemp = callerArgsLookupTargetFunctionPointer.GetNextOffset(); + + // Check to see if we've handled all the arguments that we are to pass to the callee. + if (TransitionBlock.InvalidOffset == ofsCallerTemp) + break; + + ofsCallerValid = ofsCallerTemp; + } + + if (ofsCallerValid == TransitionBlock.InvalidOffset) + throw new InvalidProgramException(); + + int stackSizeCaller = callerArgsLookupTargetFunctionPointer.GetArgSize(); + Debug.Assert(stackSizeCaller == IntPtr.Size); + void* pSrc = _callerTransitionBlock + ofsCallerValid; + IntPtr tempFunctionPointer = *((IntPtr*)pSrc); + + forceCalleeHasParamType = UpdateCalleeFunctionPointer(tempFunctionPointer); + } + + // Retrieve target function pointer and instantiation argument for delegate thunks + if (conversionInfo.IsDelegateThunk) + { + Debug.Assert(_callerArgs.HasThis() && !_conversionInfo.IsUnboxingThunk); + + IntPtr locationOfThisPointer = (IntPtr)(_callerTransitionBlock + ArgIterator.GetThisOffset()); + _delegateData._delegateObject = (Delegate)Unsafe.As(ref *(IntPtr*)locationOfThisPointer); + Debug.Assert(_delegateData._delegateObject != null); + + RuntimeAugments.GetDelegateData( + _delegateData._delegateObject, + out _delegateData._firstParameter, + out _delegateData._helperObject, + out _delegateData._extraFunctionPointerOrData, + out _delegateData._functionPointer); + + if (conversionInfo.TargetDelegateFunctionIsExtraFunctionPointerOrDataField) + { + if (conversionInfo.IsOpenInstanceDelegateThunk) + { + _delegateData._boxedFirstParameter = BoxedCallerFirstArgument; + _callerArgs.Reset(); + + IntPtr resolvedTargetFunctionPointer = OpenMethodResolver.ResolveMethod(_delegateData._extraFunctionPointerOrData, _delegateData._boxedFirstParameter); + forceCalleeHasParamType = UpdateCalleeFunctionPointer(resolvedTargetFunctionPointer); + } + else + { + forceCalleeHasParamType = UpdateCalleeFunctionPointer(_delegateData._extraFunctionPointerOrData); + } + } + else if (conversionInfo.IsMulticastDelegate) + { + _delegateData._multicastTargetCount = (int)_delegateData._extraFunctionPointerOrData; + } + } + + // + // Setup output argument iterator for the callee + // + _calleeArgs = new ArgIterator(conversionInfo.ArgIteratorData, + (conversionInfo.ArgIteratorData.HasThis() && !conversionInfo.IsStaticDelegateThunk) ? + CallingConvention.ManagedInstance : + CallingConvention.ManagedStatic, + forceCalleeHasParamType || conversionInfo.CalleeHasParamType, + false, + conversionInfo.CalleeForcedByRefData, + conversionInfo.IsOpenInstanceDelegateThunk, + conversionInfo.IsClosedStaticDelegate); + + // The function pointer, 'hasParamType', and 'hasThis' flags for the callee arg iterator need to be computed/read from the caller's + // input arguments in the case of a reflection invoker thunk (the target method pointer and 'hasThis' flags are + // passed in as parameters from the caller, not loaded from a static method signature in native layout) + if (conversionInfo.IsReflectionDynamicInvokerThunk) + ComputeCalleeFlagsAndFunctionPointerForReflectionInvokeThunk(); + +#if CALLINGCONVENTION_CALLEE_POPS + // Ensure that the count of bytes in the stack is available + _callerArgs.CbStackPop(); +#endif + } + + private void ComputeCalleeFlagsAndFunctionPointerForReflectionInvokeThunk() + { + Debug.Assert(_conversionInfo.IsReflectionDynamicInvokerThunk); + Debug.Assert(!_callerArgs.Equals(default(ArgIterator)) && !_calleeArgs.Equals(default(ArgIterator))); + + _callerArgs.GetNextOffset(); // Skip thisPtr + + { + int ofsCaller = _callerArgs.GetNextOffset(); // methodToCall + Debug.Assert(TransitionBlock.InvalidOffset != ofsCaller); + + void** pSrc = (void**)(_callerTransitionBlock + ofsCaller); + + IntPtr functionPointer = new IntPtr(*pSrc); + + bool forceCalleeHasParamType = UpdateCalleeFunctionPointer(functionPointer); + _calleeArgs.SetHasParamTypeAndReset(forceCalleeHasParamType); + } + + _callerArgs.GetNextOffset(); // Skip argSetupState + + // targetIsThisCall + { + int ofsCaller = _callerArgs.GetNextOffset(); // targetIsThisCall + Debug.Assert(TransitionBlock.InvalidOffset != ofsCaller); + + bool* pSrc = (bool*)(_callerTransitionBlock + ofsCaller); + + bool targetIsThisCall = *pSrc; + + _calleeArgs.SetHasThisAndReset(targetIsThisCall); + } + + _callerArgs.Reset(); + } + + internal void ResetPinnedObjects() + { + // Reset all pinned gchandles to null. + // Freeing of gchandles is done in the destructor of GCHandleContainer when the thread dies. + s_pinnedGCHandles._thisPtrHandle.Target = null; + s_pinnedGCHandles._dynamicInvokeArgHandle.Target = null; + s_pinnedGCHandles._returnObjectHandle.Target = null; + } + + private bool UpdateCalleeFunctionPointer(IntPtr newFunctionPointer) + { + if (FunctionPointerOps.IsGenericMethodPointer(newFunctionPointer)) + { + GenericMethodDescriptor* genericTarget = FunctionPointerOps.ConvertToGenericDescriptor(newFunctionPointer); + _instantiatingStubArgument = genericTarget->InstantiationArgument; + _functionPointerToCall = genericTarget->MethodFunctionPointer; + return true; + } + else + { + _functionPointerToCall = newFunctionPointer; + return false; + } + } + + internal void PrepareNextMulticastDelegateCall(int currentIndex) + { + if (!_conversionInfo.IsMulticastDelegate) + { + Environment.FailFast("Thunk is not a multicast delegate thunk!"); + } + + Debug.Assert(currentIndex < _delegateData._multicastTargetCount); + + Debug.Assert(!_delegateData.Equals(default(DelegateData))); + Debug.Assert(_delegateData._helperObject is Delegate[]); + Debug.Assert(_delegateData._multicastTargetCount <= ((Delegate[])_delegateData._helperObject).Length); + + Delegate[] delegateArray = (Delegate[])_delegateData._helperObject; + Delegate currentDelegate = delegateArray[currentIndex]; + + object helperObject; + IntPtr extraFunctionPointerOrData; + IntPtr functionPointer; + RuntimeAugments.GetDelegateData(currentDelegate, out _delegateData._multicastThisPointer, out helperObject, out extraFunctionPointerOrData, out functionPointer); + + bool forceCalleeHasParamType = UpdateCalleeFunctionPointer(functionPointer); + _calleeArgs.SetHasParamTypeAndReset(forceCalleeHasParamType); + _callerArgs.Reset(); + } + + internal int MulticastDelegateCallCount + { + get + { + Debug.Assert(_conversionInfo.IsMulticastDelegate); + return _delegateData._multicastTargetCount; + } + } + + private object BoxedCallerFirstArgument + { + // Get the first argument that will be passed to the callee (if available), and box it if it's a value type. + // The first argument is an actual explicit argument passed to the callee... i.e. not a thispointer or instantiationStubArg + // NOTE: This method advances the caller ArgIterator and does NOT reset it. It's up to whoever calls this method + // to reset the caller ArgIterator if it needs to be reset + get + { + int ofsCaller = _callerArgs.GetNextOffset(); + if (TransitionBlock.InvalidOffset == ofsCaller) + return null; + + byte* pSrc = _callerTransitionBlock + ofsCaller; + + TypeHandle thArgType; + _callerArgs.GetArgType(out thArgType); + Debug.Assert(!thArgType.IsNull()); + + if (!thArgType.IsValueType()) + { + Debug.Assert(_callerArgs.GetArgSize() == IntPtr.Size); + + // For Open non-virtual calls to instance methods on valuetypes, the first argument + // is a byref parameter. In that case, pass null to the resolution function. + if (_callerArgs.IsArgPassedByRef()) + return null; + + Debug.Assert(!_callerArgs.IsArgPassedByRef()); + return Unsafe.As(ref *(IntPtr*)pSrc); + } + else + { + RuntimeTypeHandle argEEType = thArgType.GetRuntimeTypeHandle(); + + if (_callerArgs.IsArgPassedByRef()) + { + return RuntimeAugments.Box(argEEType, new IntPtr(*((void**)pSrc))); + } + else + { + return RuntimeAugments.Box(argEEType, new IntPtr(pSrc)); + } + } + } + } + + internal IntPtr ClosedStaticDelegateThisPointer + { + get + { + Debug.Assert(_conversionInfo.IsClosedStaticDelegate && !_delegateData.Equals(default(DelegateData))); + Debug.Assert(_delegateData._helperObject != null); + s_pinnedGCHandles._thisPtrHandle.Target = _delegateData._helperObject; + return s_pinnedGCHandles._thisPtrHandle.GetRawTargetAddress(); + } + } + + internal void* ThisPointer + { + get + { + if (_conversionInfo.IsStaticDelegateThunk) + return null; + + if (_callerArgs.HasThis() != _calleeArgs.HasThis()) + { + // Note: the _callerArgs for a reflection dynamic invoker thunk will never have a thisPtr + // because it's a static method. The _calleeArgs may have a thisPtr if the target method + // being called is an instance method. + if (!_conversionInfo.IsReflectionDynamicInvokerThunk) + { + // Whether or not a given signature has a this parameter is not allowed to change across this thunk. + Environment.FailFast("HasThis on signatures must match"); + } + } + if (_calleeArgs.HasThis()) + { + void* thisPointer = null; + + if (_conversionInfo.IsThisPointerInDelegateData || _conversionInfo.IsAnyDynamicInvokerThunk) + { + // Assert that we have extracted the delegate data + Debug.Assert(_conversionInfo.IsReflectionDynamicInvokerThunk || !_delegateData.Equals(default(DelegateData))); + + if (_conversionInfo.IsAnyDynamicInvokerThunk || _conversionInfo.IsOpenInstanceDelegateThunk) + { + // Resilience to multiple or out of order calls + _callerArgs.Reset(); + + int ofsCaller = _callerArgs.GetNextOffset(); + Debug.Assert(TransitionBlock.InvalidOffset != ofsCaller); + + void** pSrc = (void**)(_callerTransitionBlock + ofsCaller); + + // No need to pin since the thisPtr is one of the arguments on the caller TB + return *pSrc; + } + else if (_conversionInfo.IsMulticastDelegate) + { + Debug.Assert(_delegateData._multicastThisPointer != null); + s_pinnedGCHandles._thisPtrHandle.Target = _delegateData._multicastThisPointer; + } + else + { + Debug.Assert(_delegateData._helperObject != null); + s_pinnedGCHandles._thisPtrHandle.Target = _delegateData._helperObject; + } + + thisPointer = (void*)s_pinnedGCHandles._thisPtrHandle.GetRawTargetAddress(); + } + else + { + thisPointer = *((void**)(_callerTransitionBlock + ArgIterator.GetThisOffset())); + if (_conversionInfo.IsUnboxingThunk) + { + thisPointer = (void*)(((IntPtr*)thisPointer) + 1); + } + } + + return thisPointer; + } + return null; + } + } + + internal void* CallerReturnBuffer + { + get + { + // If the return buffer is treated the same way for both calling conventions + if (_callerArgs.HasRetBuffArg() == _calleeArgs.HasRetBuffArg()) + { + // Do nothing, or copy the ret buf arg around + if (_callerArgs.HasRetBuffArg()) + { + return *((void**)(_callerTransitionBlock + _callerArgs.GetRetBuffArgOffset())); + } + } + else + { + // We'll need to create a return buffer, or assign into the return buffer when the actual call completes. + if (_calleeArgs.HasRetBuffArg()) + { + TypeHandle thRetType; + bool forceByRefUnused; + void* callerRetBuffer = null; + + if (_conversionInfo.IsAnyDynamicInvokerThunk) + { + // In the case of dynamic invoke thunks that use return buffers, we need to allocate a buffer for the return + // value, of the same type as the return value type handle in the callee's arguments. + Debug.Assert(!_callerArgs.HasRetBuffArg()); + + CorElementType returnType = _calleeArgs.GetReturnType(out thRetType, out forceByRefUnused); + Debug.Assert(!thRetType.IsNull()); + RuntimeTypeHandle returnValueType = thRetType.IsValueType() ? thRetType.GetRuntimeTypeHandle() : typeof(object).TypeHandle; + s_pinnedGCHandles._returnObjectHandle.Target = RuntimeAugments.RawNewObject(returnValueType); + + // The transition block has a space reserved for storing return buffer data. This is protected conservatively. + // Copy the address of the allocated object to the protected memory to be able to safely unpin it. + callerRetBuffer = _callerTransitionBlock + TransitionBlock.GetOffsetOfReturnValuesBlock(); + *((void**)callerRetBuffer) = (void*)s_pinnedGCHandles._returnObjectHandle.GetRawTargetAddress(); + + // Unpin the allocated object (it's now protected in the caller's conservatively reported memory space) + s_pinnedGCHandles._returnObjectHandle.Target = null; + + // Point the callerRetBuffer to the begining of the actual object's data (skipping the EETypePtr slot) + callerRetBuffer = (void*)(new IntPtr(*((void**)callerRetBuffer)) + IntPtr.Size); + } + else + { + // The transition block has a space reserved for storing return buffer data. This is protected conservatively + callerRetBuffer = _callerTransitionBlock + TransitionBlock.GetOffsetOfReturnValuesBlock(); + + // Make sure buffer is nulled out, and setup the return buffer location. + CorElementType returnType = _callerArgs.GetReturnType(out thRetType, out forceByRefUnused); + int returnSize = TypeHandle.GetElemSize(returnType, thRetType); + CallConverterThunk.memzeroPointerAligned((byte*)callerRetBuffer, returnSize); + } + + Debug.Assert(callerRetBuffer != null); + return callerRetBuffer; + } + } + + return null; + } + } + + internal void* VarArgSigCookie + { + get + { + if (_calleeArgs.IsVarArg() != _callerArgs.IsVarArg()) + { + // Whether or not a given signature has a this parameter is not allowed to change across this thunk. + Environment.FailFast("IsVarArg on signatures must match"); + } + if (_calleeArgs.IsVarArg()) + { + return *((void**)(_callerTransitionBlock + _callerArgs.GetVASigCookieOffset())); + } + + return null; + } + } + + internal IntPtr InstantiatingStubArgument + { + get + { + if (_calleeArgs.HasParamType() == _callerArgs.HasParamType()) + { + if (_calleeArgs.HasParamType()) + { + return new IntPtr(*((void**)(_callerTransitionBlock + _callerArgs.GetParamTypeArgOffset()))); + } + } + else if (_calleeArgs.HasParamType()) + { + Debug.Assert(_instantiatingStubArgument != IntPtr.Zero); + return _instantiatingStubArgument; + } + else + { + // Whether or not a given signature has a this parameter is not allowed to change across this thunk. + Environment.FailFast("Uninstantiating thunks are not allowed"); + } + + return IntPtr.Zero; + } + } + + internal IntPtr FunctionPointerToCall + { + get + { + if (_conversionInfo.IsDelegateDynamicInvokeThunk) + { + // Resilience to multiple or out of order calls + { + _callerArgs.Reset(); + _callerArgs.GetNextOffset(); // thisPtr + } + + int ofsCaller = _callerArgs.GetNextOffset(); // methodToCall + Debug.Assert(TransitionBlock.InvalidOffset != ofsCaller); + + void** pSrc = (void**)(_callerTransitionBlock + ofsCaller); + + return new IntPtr(*pSrc); + } + + return _functionPointerToCall; + } + } + + internal IntPtr InvokeObjectArrayDelegate(object[] arguments) + { + if (!_conversionInfo.IsObjectArrayDelegateThunk) + Environment.FailFast("Thunk is not an object array delegate thunk!"); + + Debug.Assert(!_delegateData.Equals(default(DelegateData))); + + Func targetDelegate = _delegateData._helperObject as Func; + Debug.Assert(targetDelegate != null); + + object result = targetDelegate(arguments ?? Array.Empty()); + + TypeHandle thArgType; + bool forceByRefUnused; + _calleeArgs.GetReturnType(out thArgType, out forceByRefUnused); + Debug.Assert(!thArgType.IsNull()); + + unsafe + { + if (thArgType.IsValueType() && thArgType.GetRuntimeTypeHandle().ToEETypePtr()->IsNullable) + { + object nullableObj = RuntimeAugments.RawNewObject(thArgType.GetRuntimeTypeHandle()); + s_pinnedGCHandles._returnObjectHandle.Target = nullableObj; + if (result != null) + { + RuntimeAugments.StoreValueTypeField(ref RuntimeAugments.GetRawData(nullableObj), result, thArgType.GetRuntimeTypeHandle()); + } + } + else + { + s_pinnedGCHandles._returnObjectHandle.Target = result; + } + } + + return s_pinnedGCHandles._returnObjectHandle.GetRawTargetAddress(); + } + + internal IntPtr GetArgSetupStateDataPointer() + { + if (!_conversionInfo.IsAnyDynamicInvokerThunk) + Environment.FailFast("Thunk is not a valid dynamic invoker thunk!"); + + // Resilience to multiple or out of order calls + { + _callerArgs.Reset(); + _callerArgs.GetNextOffset(); // thisPtr + _callerArgs.GetNextOffset(); // methodToCall + } + + int ofsCaller = _callerArgs.GetNextOffset(); //argSetupState + Debug.Assert(TransitionBlock.InvalidOffset != ofsCaller); + + void** pSrc = (void**)(_callerTransitionBlock + ofsCaller); + + // No need to pin since the argSetupState is one of the arguments on the caller TB + return new IntPtr(*pSrc); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs new file mode 100644 index 00000000000000..befeea82355aba --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs @@ -0,0 +1,1415 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +#if TARGET_ARM +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define CALLDESCR_FPARGREGSARERETURNREGS // The return value floating point registers are the same as the argument registers +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define FEATURE_HFA +#elif TARGET_ARM64 +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define CALLDESCR_FPARGREGSARERETURNREGS // The return value floating point registers are the same as the argument registers +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define ENREGISTERED_PARAMTYPE_MAXSIZE +#define FEATURE_HFA +#elif TARGET_X86 +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#define CALLINGCONVENTION_CALLEE_POPS +#elif TARGET_AMD64 +#if UNIXAMD64 +#define UNIX_AMD64_ABI +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#else +#endif +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define CALLDESCR_FPARGREGSARERETURNREGS // The return value floating point registers are the same as the argument registers +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define ENREGISTERED_PARAMTYPE_MAXSIZE +#elif TARGET_WASM +#else +#error Unknown architecture! +#endif + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Internal.Runtime.Augments; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using Internal.Runtime; +using Internal.Runtime.CompilerServices; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.Runtime.CallConverter; + +using ArgIterator = Internal.Runtime.CallConverter.ArgIterator; + +namespace Internal.Runtime.TypeLoader +{ + public class CallConverterThunk + { + private static LowLevelList s_allocatedThunks = new LowLevelList(); + + private static volatile object s_thunkPoolHeap; + + internal static IntPtr CommonInputThunkStub = IntPtr.Zero; +#if CALLDESCR_FPARGREGSARERETURNREGS +#else +#if TARGET_X86 + internal static IntPtr ReturnFloatingPointReturn4Thunk = IntPtr.Zero; + internal static IntPtr ReturnFloatingPointReturn8Thunk = IntPtr.Zero; +#endif +#endif + internal static IntPtr ReturnVoidReturnThunk = IntPtr.Zero; + internal static IntPtr ReturnIntegerPointReturnThunk = IntPtr.Zero; + +#if TARGET_X86 + // Correctness of using this data structure relies on thread static structs being allocated in a location which cannot be moved by the GC + [ThreadStatic] + internal static ReturnBlock t_NonArgRegisterReturnSpace; +#endif + + // CallingConventionConverter_CommonCallingStub indirection information structure + // This is filled in during the class constructor for this type, and holds data + // that is constant across all uses of the call conversion thunks. A pointer to + // this is passed to each invocation of the common calling stub. + internal struct CallingConventionConverter_CommonCallingStub_PointerData + { + public IntPtr ManagedCallConverterThunk; + public IntPtr UniversalThunk; + } + + // Wrapper class used for reference type parameters passed byref in dynamic invoker thunks + internal class DynamicInvokeByRefArgObjectWrapper + { + internal object _object; + } + + internal static CallingConventionConverter_CommonCallingStub_PointerData s_commonStubData; + + [DllImport("*", ExactSpelling = true, EntryPoint = "CallingConventionConverter_GetStubs")] + private static extern unsafe void CallingConventionConverter_GetStubs(out IntPtr returnVoidStub, + out IntPtr returnIntegerStub, + out IntPtr commonStub +#if CALLDESCR_FPARGREGSARERETURNREGS +#else + , out IntPtr returnFloatingPointReturn4Thunk, + out IntPtr returnFloatingPointReturn8Thunk +#endif + ); + +#if TARGET_ARM + [DllImport("*", ExactSpelling = true, EntryPoint = "CallingConventionConverter_SpecifyCommonStubData")] + private static extern unsafe void CallingConventionConverter_SpecifyCommonStubData(IntPtr commonStubData); +#endif + + private static bool s_callConverterThunk = CallConverterThunk_LazyCctor(); + + private static unsafe bool CallConverterThunk_LazyCctor() + { +#if TARGET_UNIX + // TODO +#else + CallingConventionConverter_GetStubs(out ReturnVoidReturnThunk, out ReturnIntegerPointReturnThunk, out CommonInputThunkStub +#if CALLDESCR_FPARGREGSARERETURNREGS +#else + , out ReturnFloatingPointReturn4Thunk, out ReturnFloatingPointReturn8Thunk +#endif + ); + s_commonStubData.ManagedCallConverterThunk = (IntPtr)(delegate*)&CallConversionThunk; + s_commonStubData.UniversalThunk = RuntimeAugments.GetUniversalTransitionThunk(); +#if TARGET_ARM + fixed (CallingConventionConverter_CommonCallingStub_PointerData* commonStubData = &s_commonStubData) + { + CallingConventionConverter_SpecifyCommonStubData((IntPtr)commonStubData); + } +#endif //TARGET_ARM + +#endif // TARGET_UNIX + return true; + } + + internal static bool GetByRefIndicatorAtIndex(int index, bool[] lookup) + { + if (lookup == null) + return false; + + if (index < lookup.Length) + return lookup[index]; + + return false; + } + + public enum ThunkKind + { + StandardToStandardInstantiating, + StandardToGenericInstantiating, + StandardToGenericInstantiatingIfNotHasThis, + StandardToGeneric, + StandardToGenericPassthruInstantiating, + StandardToGenericPassthruInstantiatingIfNotHasThis, + GenericToStandard, + StandardUnboxing, + StandardUnboxingAndInstantiatingGeneric, + GenericToStandardWithTargetPointerArg, + GenericToStandardWithTargetPointerArgAndParamArg, + GenericToStandardWithTargetPointerArgAndMaybeParamArg, + DelegateInvokeOpenStaticThunk, + DelegateInvokeClosedStaticThunk, + DelegateInvokeOpenInstanceThunk, + DelegateInvokeInstanceClosedOverGenericMethodThunk, + DelegateMulticastThunk, + DelegateObjectArrayThunk, + DelegateDynamicInvokeThunk, + ReflectionDynamicInvokeThunk, + } + + // WARNING: These constants are also declared in System.Private.CoreLib\src\System\Delegate.cs + // Do not change their values unless you change the values decalred in Delegate.cs + private const int MulticastThunk = 0; + private const int ClosedStaticThunk = 1; + private const int OpenStaticThunk = 2; + private const int ClosedInstanceThunkOverGenericMethod = 3; + private const int DelegateInvokeThunk = 4; + private const int OpenInstanceThunk = 5; + private const int ObjectArrayThunk = 6; + + + public static unsafe IntPtr MakeThunk(ThunkKind thunkKind, + IntPtr targetPointer, + IntPtr instantiatingArg, + bool hasThis, RuntimeTypeHandle[] parameters, + bool[] byRefParameters, + bool[] paramsByRefForced) + { + // Build thunk data + TypeHandle thReturnType = new TypeHandle(GetByRefIndicatorAtIndex(0, byRefParameters), parameters[0]); + TypeHandle[] thParameters = null; + if (parameters.Length > 1) + { + thParameters = new TypeHandle[parameters.Length - 1]; + for (int i = 1; i < parameters.Length; i++) + { + thParameters[i - 1] = new TypeHandle(GetByRefIndicatorAtIndex(i, byRefParameters), parameters[i]); + } + } + + int callConversionInfo = CallConversionInfo.RegisterCallConversionInfo(thunkKind, targetPointer, instantiatingArg, hasThis, thReturnType, thParameters, paramsByRefForced); + return FindExistingOrAllocateThunk(callConversionInfo); + } + + public static unsafe IntPtr MakeThunk(ThunkKind thunkKind, IntPtr targetPointer, RuntimeSignature methodSignature, IntPtr instantiatingArg, RuntimeTypeHandle[] typeArgs, RuntimeTypeHandle[] methodArgs) + { + int callConversionInfo = CallConversionInfo.RegisterCallConversionInfo(thunkKind, targetPointer, methodSignature, instantiatingArg, typeArgs, methodArgs); + return FindExistingOrAllocateThunk(callConversionInfo); + } + + internal static unsafe IntPtr MakeThunk(ThunkKind thunkKind, IntPtr targetPointer, IntPtr instantiatingArg, ArgIteratorData argIteratorData, bool[] paramsByRefForced) + { + int callConversionInfo = CallConversionInfo.RegisterCallConversionInfo(thunkKind, targetPointer, instantiatingArg, argIteratorData, paramsByRefForced); + return FindExistingOrAllocateThunk(callConversionInfo); + } + + private static unsafe IntPtr FindExistingOrAllocateThunk(int callConversionInfo) + { + IntPtr thunk = IntPtr.Zero; + + lock (s_allocatedThunks) + { + if (callConversionInfo < s_allocatedThunks.Count && s_allocatedThunks[callConversionInfo] != IntPtr.Zero) + return s_allocatedThunks[callConversionInfo]; + + if (s_thunkPoolHeap == null) + { + s_thunkPoolHeap = RuntimeAugments.CreateThunksHeap(CommonInputThunkStub); + Debug.Assert(s_thunkPoolHeap != null); + } + + thunk = RuntimeAugments.AllocateThunk(s_thunkPoolHeap); + Debug.Assert(thunk != IntPtr.Zero); + + fixed (CallingConventionConverter_CommonCallingStub_PointerData* commonStubData = &s_commonStubData) + { + RuntimeAugments.SetThunkData(s_thunkPoolHeap, thunk, new IntPtr(callConversionInfo), new IntPtr(commonStubData)); + + if (callConversionInfo >= s_allocatedThunks.Count) + { + s_allocatedThunks.Expand(count: callConversionInfo + 1); + } + Debug.Assert(s_allocatedThunks[callConversionInfo] == IntPtr.Zero); + s_allocatedThunks[callConversionInfo] = thunk; + } + } + + return thunk; + } + + public static unsafe IntPtr GetDelegateThunk(Delegate delegateObject, int thunkKind) + { + RuntimeTypeHandle delegateType = RuntimeAugments.GetRuntimeTypeHandleFromObjectReference(delegateObject); + Debug.Assert(RuntimeAugments.IsGenericType(delegateType)); + + RuntimeTypeHandle[] typeArgs; + RuntimeTypeHandle genericTypeDefHandle; + genericTypeDefHandle = RuntimeAugments.GetGenericInstantiation(delegateType, out typeArgs); + Debug.Assert(typeArgs != null && typeArgs.Length > 0); + + RuntimeSignature invokeMethodSignature; + bool gotInvokeMethodSignature = TypeBuilder.TryGetDelegateInvokeMethodSignature(delegateType, out invokeMethodSignature); + + if (!gotInvokeMethodSignature) + { + Environment.FailFast("Unable to compute delegate invoke signature"); + } + + switch (thunkKind) + { + case DelegateInvokeThunk: + return CallConverterThunk.MakeThunk(CallConverterThunk.ThunkKind.DelegateDynamicInvokeThunk, IntPtr.Zero, invokeMethodSignature, IntPtr.Zero, typeArgs, null); + + case ObjectArrayThunk: + return CallConverterThunk.MakeThunk(CallConverterThunk.ThunkKind.DelegateObjectArrayThunk, IntPtr.Zero, invokeMethodSignature, IntPtr.Zero, typeArgs, null); + + case MulticastThunk: + return CallConverterThunk.MakeThunk(CallConverterThunk.ThunkKind.DelegateMulticastThunk, IntPtr.Zero, invokeMethodSignature, IntPtr.Zero, typeArgs, null); + + case OpenInstanceThunk: + return CallConverterThunk.MakeThunk(CallConverterThunk.ThunkKind.DelegateInvokeOpenInstanceThunk, IntPtr.Zero, invokeMethodSignature, IntPtr.Zero, typeArgs, null); + + case ClosedInstanceThunkOverGenericMethod: + return CallConverterThunk.MakeThunk(CallConverterThunk.ThunkKind.DelegateInvokeInstanceClosedOverGenericMethodThunk, IntPtr.Zero, invokeMethodSignature, IntPtr.Zero, typeArgs, null); + + case ClosedStaticThunk: + return CallConverterThunk.MakeThunk(CallConverterThunk.ThunkKind.DelegateInvokeClosedStaticThunk, IntPtr.Zero, invokeMethodSignature, IntPtr.Zero, typeArgs, null); + + case OpenStaticThunk: + return CallConverterThunk.MakeThunk(CallConverterThunk.ThunkKind.DelegateInvokeOpenStaticThunk, IntPtr.Zero, invokeMethodSignature, IntPtr.Zero, typeArgs, null); + + default: + Environment.FailFast("Invalid delegate thunk kind"); + return IntPtr.Zero; + } + } + + public static unsafe bool TryGetNonUnboxingFunctionPointerFromUnboxingAndInstantiatingStub(IntPtr potentialStub, RuntimeTypeHandle exactType, out IntPtr nonUnboxingMethod) + { + IntPtr callConversionId; + IntPtr commonStubDataPtr; + object thunkPoolHeap = s_thunkPoolHeap; + if (thunkPoolHeap == null || !RuntimeAugments.TryGetThunkData(thunkPoolHeap, potentialStub, out callConversionId, out commonStubDataPtr)) + { + // This isn't a call conversion stub + nonUnboxingMethod = IntPtr.Zero; + return false; + } + + CallConversionInfo conversionInfo = CallConversionInfo.GetConverter(callConversionId.ToInt32()); + if (conversionInfo.IsUnboxingThunk) + { + // In this case the call converter is serving as an unboxing/instantiating stub + // This case is not yet handled, and we don't need support for it yet. + throw NotImplemented.ByDesign; + } + + IntPtr underlyingTargetMethod; + IntPtr newInstantiatingArg; + + if (conversionInfo.CalleeHasParamType) + { + // In this case the call converter is an instantiating stub wrapping an unboxing thunk. + // Use the redhawk GetCodeTarget to see through the unboxing stub and get the real underlying method + // and the instantiation arg does not need changing. + underlyingTargetMethod = RuntimeAugments.GetCodeTarget(conversionInfo.TargetFunctionPointer); + newInstantiatingArg = conversionInfo.InstantiatingStubArgument; + } + else + { + // At this point we've got a standard to generic converter wrapping an unboxing and instantiating + // stub. We need to convert that into a fat function pointer directly calling the underlying method + // or a calling convention converter instantiating stub wrapping the underlying method + IntPtr underlyingUnboxingAndInstantiatingMethod = RuntimeAugments.GetCodeTarget(conversionInfo.TargetFunctionPointer); + if (!TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(underlyingUnboxingAndInstantiatingMethod, out underlyingTargetMethod)) + { + // We aren't wrapping an unboxing and instantiating stub. This should never happen + throw new NotSupportedException(); + } + + newInstantiatingArg = exactType.ToIntPtr(); + } + + Debug.Assert(conversionInfo.CallerForcedByRefData == null); + + bool canUseFatFunctionPointerInsteadOfThunk = true; + if (conversionInfo.CalleeForcedByRefData != null) + { + foreach (bool forcedByRef in conversionInfo.CalleeForcedByRefData) + { + if (forcedByRef) + { + canUseFatFunctionPointerInsteadOfThunk = false; + break; + } + } + } + + if (canUseFatFunctionPointerInsteadOfThunk) + { + nonUnboxingMethod = FunctionPointerOps.GetGenericMethodFunctionPointer(underlyingTargetMethod, newInstantiatingArg); + return true; + } + else + { + // Construct a new StandardToGenericInstantiating thunk around the underlyingTargetMethod + nonUnboxingMethod = MakeThunk(ThunkKind.StandardToGenericInstantiating, + underlyingTargetMethod, + newInstantiatingArg, + conversionInfo.ArgIteratorData, + conversionInfo.CalleeForcedByRefData); + return true; + } + } + + public static unsafe bool TryGetCallConversionTargetPointerAndInstantiatingArg(IntPtr potentialStub, out IntPtr methodTarget, out IntPtr instantiatingArg) + { + methodTarget = instantiatingArg = IntPtr.Zero; + + IntPtr callConversionId; + IntPtr commonStubDataPtr; + object thunkPoolHeap = s_thunkPoolHeap; + if (thunkPoolHeap == null || !RuntimeAugments.TryGetThunkData(thunkPoolHeap, potentialStub, out callConversionId, out commonStubDataPtr)) + { + // This isn't a call conversion stub + return false; + } + + CallConversionInfo conversionInfo = CallConversionInfo.GetConverter(callConversionId.ToInt32()); + if (!conversionInfo.HasKnownTargetPointerAndInstantiatingArgument) + return false; + + methodTarget = conversionInfo.TargetFunctionPointer; + instantiatingArg = conversionInfo.InstantiatingStubArgument; + return true; + } + + // This struct shares a layout with CallDescrData in the MRT codebase. + internal unsafe struct CallDescrData + { + // + // Input arguments + // + public void* pSrc; + public int numStackSlots; + public uint fpReturnSize; + // Both of the following pointers are always present to reduce the spread of ifdefs in the C++ and ASM definitions of the struct + public ArgumentRegisters* pArgumentRegisters; // Not used by AMD64 + public FloatArgumentRegisters* pFloatArgumentRegisters; // Not used by X86 + public void* pTarget; + + // + // Return value + // + public void* pReturnBuffer; + } + + // This function fills a piece of memory in a GC safe way. It makes the guarantee + // that it will fill memory in at least pointer sized chunks whenever possible. + // Unaligned memory at the beginning and remaining bytes at the end are written bytewise. + // We must make this guarantee whenever we clear memory in the GC heap that could contain + // object references. The GC or other user threads can read object references at any time, + // clearing them bytewise can result in a read on another thread getting incorrect data. + internal static unsafe void gcSafeMemzeroPointer(byte* pointer, int size) + { + byte* memBytes = pointer; + byte* endBytes = (pointer + size); + + // handle unaligned bytes at the beginning + while (!ArgIterator.IS_ALIGNED(new IntPtr(memBytes), (int)IntPtr.Size) && (memBytes < endBytes)) + *memBytes++ = (byte)0; + + // now write pointer sized pieces + long nPtrs = (endBytes - memBytes) / IntPtr.Size; + IntPtr* memPtr = (IntPtr*)memBytes; + for (int i = 0; i < nPtrs; i++) + *memPtr++ = IntPtr.Zero; + + // handle remaining bytes at the end + memBytes = (byte*)memPtr; + while (memBytes < endBytes) + *memBytes++ = (byte)0; + } + + internal static unsafe void memzeroPointer(byte* pointer, int size) + { + for (int i = 0; i < size; i++) + pointer[i] = 0; + } + + internal static unsafe void memzeroPointerAligned(byte* pointer, int size) + { + size = ArgIterator.ALIGN_UP(size, IntPtr.Size); + size /= IntPtr.Size; + + for (int i = 0; i < size; i++) + { + ((IntPtr*)pointer)[i] = IntPtr.Zero; + } + } + + private static unsafe bool isPointerAligned(void* pointer) + { + if (sizeof(IntPtr) == 4) + { + return ((int)pointer & 3) == 0; + } + + Debug.Assert(sizeof(IntPtr) == 8); + return ((long)pointer & 7) == 0; + } + +#if CCCONVERTER_TRACE + private static int s_numConversionsExecuted = 0; +#endif + + [DebuggerGuidedStepThroughAttribute] + private static unsafe IntPtr CallConversionThunk(IntPtr callerTransitionBlockParam, IntPtr callConversionId) + { + CallConversionParameters conversionParams = default(CallConversionParameters); + + try + { + conversionParams = new CallConversionParameters(CallConversionInfo.GetConverter(callConversionId.ToInt32()), callerTransitionBlockParam); + +#if CCCONVERTER_TRACE + System.Threading.Interlocked.Increment(ref s_numConversionsExecuted); + CallingConventionConverterLogger.WriteLine("CallConversionThunk executing... COUNT = " + s_numConversionsExecuted.LowLevelToString()); + CallingConventionConverterLogger.WriteLine("Executing thunk of type " + conversionParams._conversionInfo.ThunkKindString() + ": "); +#endif + + if (conversionParams._conversionInfo.IsMulticastDelegate) + { + MulticastDelegateInvoke(ref conversionParams); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } + else + { + // Create a transition block on the stack. + // Note that SizeOfFrameArgumentArray does overflow checks with sufficient margin to prevent overflows here + int nStackBytes = conversionParams._calleeArgs.SizeOfFrameArgumentArray(); + int dwAllocaSize = TransitionBlock.GetNegSpaceSize() + sizeof(TransitionBlock) + nStackBytes; + + RuntimeAugments.RunFunctionWithConservativelyReportedBuffer(dwAllocaSize, &InvokeTarget, ref conversionParams); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } + + + return conversionParams._invokeReturnValue; + } + finally + { + conversionParams.ResetPinnedObjects(); + } + } + + [DebuggerGuidedStepThroughAttribute] + private static unsafe IntPtr MulticastDelegateInvoke(ref CallConversionParameters conversionParams) + { + // Create a transition block on the stack. + // Note that SizeOfFrameArgumentArray does overflow checks with sufficient margin to prevent overflows here + int nStackBytes = conversionParams._calleeArgs.SizeOfFrameArgumentArray(); + int dwAllocaSize = TransitionBlock.GetNegSpaceSize() + sizeof(TransitionBlock) + nStackBytes; + + for (int i = 0; i < conversionParams.MulticastDelegateCallCount; i++) + { + conversionParams.PrepareNextMulticastDelegateCall(i); + conversionParams._copyReturnValue = (i == (conversionParams.MulticastDelegateCallCount - 1)); + + RuntimeAugments.RunFunctionWithConservativelyReportedBuffer(dwAllocaSize, &InvokeTarget, ref conversionParams); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } + + return conversionParams._invokeReturnValue; + } + + [DebuggerGuidedStepThroughAttribute] + private static unsafe void InvokeTarget(void* allocatedStackBuffer, ref CallConversionParameters conversionParams) + { + byte* callerTransitionBlock = conversionParams._callerTransitionBlock; + byte* calleeTransitionBlock = ((byte*)allocatedStackBuffer) + TransitionBlock.GetNegSpaceSize(); + + // + // Setup some of the special parameters on the output transition block + // + void* thisPointer = conversionParams.ThisPointer; + void* callerRetBuffer = conversionParams.CallerReturnBuffer; + void* VASigCookie = conversionParams.VarArgSigCookie; + void* instantiatingStubArgument = (void*)conversionParams.InstantiatingStubArgument; + { + Debug.Assert((thisPointer != null && conversionParams._calleeArgs.HasThis()) || (thisPointer == null && !conversionParams._calleeArgs.HasThis())); + if (thisPointer != null) + { + *((void**)(calleeTransitionBlock + ArgIterator.GetThisOffset())) = thisPointer; + } + + Debug.Assert((callerRetBuffer != null && conversionParams._calleeArgs.HasRetBuffArg()) || (callerRetBuffer == null && !conversionParams._calleeArgs.HasRetBuffArg())); + if (callerRetBuffer != null) + { + *((void**)(calleeTransitionBlock + conversionParams._calleeArgs.GetRetBuffArgOffset())) = callerRetBuffer; + } + + Debug.Assert((VASigCookie != null && conversionParams._calleeArgs.IsVarArg()) || (VASigCookie == null && !conversionParams._calleeArgs.IsVarArg())); + if (VASigCookie != null) + { + *((void**)(calleeTransitionBlock + conversionParams._calleeArgs.GetVASigCookieOffset())) = VASigCookie; + } + + Debug.Assert((instantiatingStubArgument != null && conversionParams._calleeArgs.HasParamType()) || (instantiatingStubArgument == null && !conversionParams._calleeArgs.HasParamType())); + if (instantiatingStubArgument != null) + { + *((void**)(calleeTransitionBlock + conversionParams._calleeArgs.GetParamTypeArgOffset())) = instantiatingStubArgument; + } + } +#if CCCONVERTER_TRACE + if (thisPointer != null) CallingConventionConverterLogger.WriteLine(" ThisPtr = " + new IntPtr(thisPointer).LowLevelToString()); + if (callerRetBuffer != null) CallingConventionConverterLogger.WriteLine(" RetBuf = " + new IntPtr(callerRetBuffer).LowLevelToString()); + if (VASigCookie != null) CallingConventionConverterLogger.WriteLine(" VASig = " + new IntPtr(VASigCookie).LowLevelToString()); + if (instantiatingStubArgument != null) CallingConventionConverterLogger.WriteLine(" InstArg = " + new IntPtr(instantiatingStubArgument).LowLevelToString()); +#endif + + object[] argumentsAsObjectArray = null; + IntPtr pinnedResultObject = IntPtr.Zero; + CallDescrData callDescrData = default(CallDescrData); + IntPtr functionPointerToCall = conversionParams.FunctionPointerToCall; + + // + // Setup the rest of the parameters on the ouput transition block by copying them from the input transition block + // + int ofsCallee; + int ofsCaller; + TypeHandle thDummy; + TypeHandle thArgType; + TypeHandle thRetType; + IntPtr argPtr; +#if CALLDESCR_FPARGREGS + FloatArgumentRegisters* pFloatArgumentRegisters = null; +#endif + { + uint arg = 0; + + while (true) + { + // Setup argument offsets. + ofsCallee = conversionParams._calleeArgs.GetNextOffset(); + ofsCaller = int.MaxValue; + + // Check to see if we've handled all the arguments that we are to pass to the callee. + if (TransitionBlock.InvalidOffset == ofsCallee) + { + if (!conversionParams._conversionInfo.IsAnyDynamicInvokerThunk) + ofsCaller = conversionParams._callerArgs.GetNextOffset(); + break; + } + +#if CALLDESCR_FPARGREGS + // Under CALLDESCR_FPARGREGS -ve offsets indicate arguments in floating point registers. If we + // have at least one such argument we point the call worker at the floating point area of the + // frame (we leave it null otherwise since the worker can perform a useful optimization if it + // knows no floating point registers need to be set up). + if ((ofsCallee < 0) && (pFloatArgumentRegisters == null)) + pFloatArgumentRegisters = (FloatArgumentRegisters*)(calleeTransitionBlock + + TransitionBlock.GetOffsetOfFloatArgumentRegisters()); +#endif + + byte* pDest = calleeTransitionBlock + ofsCallee; + byte* pSrc = null; + + int stackSizeCallee = int.MaxValue; + int stackSizeCaller = int.MaxValue; + + bool isCalleeArgPassedByRef = false; + bool isCallerArgPassedByRef = false; + + // + // Compute size and pointer to caller's arg + // + { + if (conversionParams._conversionInfo.IsClosedStaticDelegate) + { + if (arg == 0) + { + // Do not advance the caller's ArgIterator yet + argPtr = conversionParams.ClosedStaticDelegateThisPointer; + pSrc = (byte*)&argPtr; + stackSizeCaller = IntPtr.Size; + isCallerArgPassedByRef = false; + } + else + { + ofsCaller = conversionParams._callerArgs.GetNextOffset(); + pSrc = callerTransitionBlock + ofsCaller; + stackSizeCaller = conversionParams._callerArgs.GetArgSize(); + isCallerArgPassedByRef = conversionParams._callerArgs.IsArgPassedByRef(); + } + + stackSizeCallee = conversionParams._calleeArgs.GetArgSize(); + isCalleeArgPassedByRef = conversionParams._calleeArgs.IsArgPassedByRef(); + } + else if (conversionParams._conversionInfo.IsAnyDynamicInvokerThunk) + { + // The caller's ArgIterator for delegate or reflection dynamic invoke thunks has a different (and special) signature than + // the target method called by the delegate. We do not use it when setting up the callee's transition block. + // Input arguments are not read from the caller's transition block, but form the dynamic invoke infrastructure. + // Get all arguments info from the callee's ArgIterator instead. + + int index; + InvokeUtils.DynamicInvokeParamLookupType paramLookupType; + + RuntimeTypeHandle argumentRuntimeTypeHandle; + CorElementType argType = conversionParams._calleeArgs.GetArgType(out thArgType); + Debug.Assert(!thArgType.IsNull()); + + if (argType == CorElementType.ELEMENT_TYPE_BYREF) + { + TypeHandle thByRefArgType; + conversionParams._calleeArgs.GetByRefArgType(out thByRefArgType); + Debug.Assert(!thByRefArgType.IsNull()); + + argumentRuntimeTypeHandle = thByRefArgType.GetRuntimeTypeHandle(); + } + else + { + // We need to check the exact type handle of the argument being passed during reflection invoke scenarios. + argumentRuntimeTypeHandle = thArgType.GetRuntimeTypeHandle(); + } + + IntPtr argSetupStatePtr = conversionParams.GetArgSetupStateDataPointer(); + object invokeParam = InvokeUtils.DynamicInvokeParamHelperCore( + argSetupStatePtr, + argumentRuntimeTypeHandle, + out paramLookupType, + out index, + argType == CorElementType.ELEMENT_TYPE_BYREF ? InvokeUtils.DynamicInvokeParamType.Ref : InvokeUtils.DynamicInvokeParamType.In); + + if (paramLookupType == InvokeUtils.DynamicInvokeParamLookupType.ValuetypeObjectReturned) + { + CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = invokeParam; + argPtr = CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.GetRawTargetAddress() + IntPtr.Size; + } + else + { + Debug.Assert(paramLookupType == InvokeUtils.DynamicInvokeParamLookupType.IndexIntoObjectArrayReturned); + Debug.Assert((invokeParam is object[]) && (uint)index < (uint)((object[])invokeParam).Length); + + CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = ((object[])invokeParam)[index]; + pinnedResultObject = CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.GetRawTargetAddress(); + + if (conversionParams._calleeArgs.IsArgPassedByRef()) + { + // We need to keep track of the array of parameters used by the InvokeUtils infrastructure, so we can copy + // back results of byref parameters + conversionParams._dynamicInvokeParams = conversionParams._dynamicInvokeParams ?? (object[])invokeParam; + + // Use wrappers to pass objects byref (Wrappers can handle both null and non-null input byref parameters) + conversionParams._dynamicInvokeByRefObjectArgs = conversionParams._dynamicInvokeByRefObjectArgs ?? new DynamicInvokeByRefArgObjectWrapper[conversionParams._dynamicInvokeParams.Length]; + + // The wrapper objects need to be pinned while we take the address of the byref'd object, and copy it to the callee + // transition block (which is conservatively reported). Once the copy is done, we can safely unpin the wrapper object. + if (pinnedResultObject == IntPtr.Zero) + { + // Input byref parameter has a null value + conversionParams._dynamicInvokeByRefObjectArgs[index] = new DynamicInvokeByRefArgObjectWrapper(); + } + else + { + // Input byref parameter has a non-null value + conversionParams._dynamicInvokeByRefObjectArgs[index] = new DynamicInvokeByRefArgObjectWrapper { _object = conversionParams._dynamicInvokeParams[index] }; + } + + CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = conversionParams._dynamicInvokeByRefObjectArgs[index]; + argPtr = CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.GetRawTargetAddress() + IntPtr.Size; + } + else + { + argPtr = new IntPtr(&pinnedResultObject); + } + } + + if (conversionParams._calleeArgs.IsArgPassedByRef()) + { + pSrc = (byte*)&argPtr; + } + else + { + pSrc = (byte*)argPtr; + } + + stackSizeCaller = stackSizeCallee = conversionParams._calleeArgs.GetArgSize(); + isCallerArgPassedByRef = isCalleeArgPassedByRef = conversionParams._calleeArgs.IsArgPassedByRef(); + } + else + { + ofsCaller = conversionParams._callerArgs.GetNextOffset(); + pSrc = callerTransitionBlock + ofsCaller; + + stackSizeCallee = conversionParams._calleeArgs.GetArgSize(); + stackSizeCaller = conversionParams._callerArgs.GetArgSize(); + + isCalleeArgPassedByRef = conversionParams._calleeArgs.IsArgPassedByRef(); + isCallerArgPassedByRef = conversionParams._callerArgs.IsArgPassedByRef(); + } + } + Debug.Assert(stackSizeCallee == stackSizeCaller); + + + if (conversionParams._conversionInfo.IsObjectArrayDelegateThunk) + { + // Box (if needed) and copy arguments to an object array instead of the callee's transition block + + argumentsAsObjectArray = argumentsAsObjectArray ?? new object[conversionParams._callerArgs.NumFixedArgs()]; + + conversionParams._callerArgs.GetArgType(out thArgType); + Debug.Assert(!thArgType.IsNull()); + + if (!thArgType.IsValueType()) + { + Debug.Assert(!isCallerArgPassedByRef); + Debug.Assert(conversionParams._callerArgs.GetArgSize() == IntPtr.Size); + argumentsAsObjectArray[arg] = Unsafe.As(ref *(IntPtr*)pSrc); + } + else + { + if (isCallerArgPassedByRef) + { + argumentsAsObjectArray[arg] = RuntimeAugments.Box(thArgType.GetRuntimeTypeHandle(), new IntPtr(*((void**)pSrc))); + } + else + { + argumentsAsObjectArray[arg] = RuntimeAugments.Box(thArgType.GetRuntimeTypeHandle(), new IntPtr(pSrc)); + } + } + } + else + { + if (isCalleeArgPassedByRef == isCallerArgPassedByRef) + { + // Argument copies without adjusting calling convention. + if (isCalleeArgPassedByRef) + { + *((IntPtr*)pDest) = *(IntPtr*)pSrc; + } + else + { + CorElementType argElemType = conversionParams._calleeArgs.GetArgType(out thArgType); + ExtendingCopy_NoWriteBarrier(pSrc, pDest, argElemType, stackSizeCaller); + } + } + else + { + // Calling convention adjustment. Used to handle conversion from universal shared generic form to standard + // calling convention and vice versa + if (isCalleeArgPassedByRef) + { + // Pass as the byref pointer a pointer to the position in the transition block of the input argument + *((void**)pDest) = pSrc; + } + else + { + // Copy into the destination the data pointed at by the pointer in the source(caller) data. + byte* pRealSrc = *(byte**)pSrc; + CorElementType argElemType = conversionParams._calleeArgs.GetArgType(out thArgType); + ExtendingCopy_NoWriteBarrier(pRealSrc, pDest, argElemType, stackSizeCaller); + } + } + +#if CCCONVERTER_TRACE + CallingConventionConverterLogger.WriteLine(" Arg" + arg.LowLevelToString() + " " + + (isCalleeArgPassedByRef ? "ref = " : " = ") + + new IntPtr(*(void**)pDest).LowLevelToString() + + " - RTTH = " + conversionParams._calleeArgs.GetEETypeDebugName((int)arg) + + " - StackSize = " + stackSizeCallee.LowLevelToString()); +#endif + } + + if (conversionParams._conversionInfo.IsAnyDynamicInvokerThunk) + { + // The calleeTransitionBlock is GC-protected, so we can now safely unpin the return value of DynamicInvokeParamHelperCore, + // since we just copied it to the callee TB. + CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = null; + } + + arg++; + } + } + + if (conversionParams._conversionInfo.IsAnyDynamicInvokerThunk) + { + IntPtr argSetupStatePtr = conversionParams.GetArgSetupStateDataPointer(); + InvokeUtils.DynamicInvokeArgSetupPtrComplete(argSetupStatePtr); + } + + uint fpReturnSize = conversionParams._calleeArgs.GetFPReturnSize(); + + if (conversionParams._conversionInfo.IsObjectArrayDelegateThunk) + { + Debug.Assert(conversionParams._callerArgs.HasRetBuffArg() == conversionParams._calleeArgs.HasRetBuffArg()); + + pinnedResultObject = conversionParams.InvokeObjectArrayDelegate(argumentsAsObjectArray); + } + else + { + if ((TransitionBlock.InvalidOffset != ofsCaller) != conversionParams._conversionInfo.CallerHasExtraParameterWhichIsFunctionTarget && + !conversionParams._conversionInfo.IsAnyDynamicInvokerThunk) + { + // The condition on the loop above is only verifying that callee has reach the end of its arguments. + // Here we check to see that caller has done so as well. + Environment.FailFast("Argument mismatch between caller and callee"); + } + if (conversionParams._conversionInfo.CallerHasExtraParameterWhichIsFunctionTarget && !conversionParams._conversionInfo.CalleeMayHaveParamType) + { + int stackSizeCaller = conversionParams._callerArgs.GetArgSize(); + Debug.Assert(stackSizeCaller == IntPtr.Size); + void* pSrc = callerTransitionBlock + ofsCaller; + functionPointerToCall = *((IntPtr*)pSrc); + + ofsCaller = conversionParams._callerArgs.GetNextOffset(); + if (TransitionBlock.InvalidOffset != ofsCaller) + { + Environment.FailFast("Argument mismatch between caller and callee"); + } + } + + callDescrData.pSrc = calleeTransitionBlock + sizeof(TransitionBlock); + callDescrData.numStackSlots = conversionParams._calleeArgs.SizeOfFrameArgumentArray() / ArchitectureConstants.STACK_ELEM_SIZE; +#if CALLDESCR_ARGREGS + callDescrData.pArgumentRegisters = (ArgumentRegisters*)(calleeTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters()); +#endif +#if CALLDESCR_FPARGREGS + callDescrData.pFloatArgumentRegisters = pFloatArgumentRegisters; +#endif + callDescrData.fpReturnSize = fpReturnSize; + callDescrData.pTarget = (void*)functionPointerToCall; + + ReturnBlock returnBlockForIgnoredData = default(ReturnBlock); + if (conversionParams._callerArgs.HasRetBuffArg() == conversionParams._calleeArgs.HasRetBuffArg()) + { + // If there is no return buffer explictly in use, return to a buffer which is conservatively reported + // by the universal transition frame. + // OR + // If there IS a return buffer in use, the function doesn't really return anything in the normal + // return value registers, but CallDescrThunk will always copy a pointer sized chunk into the + // ret buf. Make that ok by giving it a valid location to stash bits. + callDescrData.pReturnBuffer = (void*)(callerTransitionBlock + TransitionBlock.GetOffsetOfReturnValuesBlock()); + } + else if (conversionParams._calleeArgs.HasRetBuffArg()) + { + // This is the case when the caller doesn't have a return buffer argument, but the callee does. + // In that case the return value captured by CallDescrWorker is ignored. + + // When CallDescrWorkerInternal is called, have it return values into a temporary unused buffer + // In actuality its returning its return information into the return value block already, but that return buffer + // was setup as a passed in argument instead of being filled in by the CallDescrWorker function directly. + callDescrData.pReturnBuffer = (void*)&returnBlockForIgnoredData; + } + else + { + // If there is no return buffer explictly in use by the callee, return to a buffer which is conservatively reported + // by the universal transition frame. + + // This is the case where HasRetBuffArg is false for the callee, but the caller has a return buffer. + // In this case we need to capture the direct return value from callee into a buffer which may contain + // a gc reference (or not), and then once the call is complete, copy the value into the return buffer + // passed by the caller. (Do not directly use the return buffer provided by the caller, as CallDescrWorker + // does not properly use a write barrier, and the actual return buffer provided may be on the GC heap.) + callDescrData.pReturnBuffer = (void*)(callerTransitionBlock + TransitionBlock.GetOffsetOfReturnValuesBlock()); + } + + ////////////////////////////////////////////////////////////// + //// Call the Callee + ////////////////////////////////////////////////////////////// + RuntimeAugments.CallDescrWorker(new IntPtr(&callDescrData)); + System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } + + // For dynamic invoke thunks, we need to copy back values of reference type parameters that were passed byref + if (conversionParams._conversionInfo.IsAnyDynamicInvokerThunk && conversionParams._dynamicInvokeParams != null) + { + for (int i = 0; i < conversionParams._dynamicInvokeParams.Length; i++) + { + if (conversionParams._dynamicInvokeByRefObjectArgs[i] == null) + continue; + + object byrefObjectArgValue = conversionParams._dynamicInvokeByRefObjectArgs[i]._object; + conversionParams._dynamicInvokeParams[i] = byrefObjectArgValue; + } + } + + if (!conversionParams._copyReturnValue) + return; + + bool forceByRefUnused; + CorElementType returnType; + // Note that the caller's ArgIterator for delegate dynamic invoke thunks has a different (and special) signature than + // the target method called by the delegate. Use the callee's ArgIterator instead to get the return type info + if (conversionParams._conversionInfo.IsAnyDynamicInvokerThunk) + { + returnType = conversionParams._calleeArgs.GetReturnType(out thRetType, out forceByRefUnused); + } + else + { + returnType = conversionParams._callerArgs.GetReturnType(out thRetType, out forceByRefUnused); + } + Debug.Assert(!thRetType.IsNull()); + int returnSize = TypeHandle.GetElemSize(returnType, thRetType); + + // Unbox result of object array delegate call + if (conversionParams._conversionInfo.IsObjectArrayDelegateThunk && thRetType.IsValueType() && pinnedResultObject != IntPtr.Zero) + pinnedResultObject += IntPtr.Size; + + // Process return values + if ((conversionParams._callerArgs.HasRetBuffArg() && !conversionParams._calleeArgs.HasRetBuffArg()) || + (conversionParams._callerArgs.HasRetBuffArg() && conversionParams._conversionInfo.IsObjectArrayDelegateThunk)) + { + // We should never get here for dynamic invoke thunks + Debug.Assert(!conversionParams._conversionInfo.IsAnyDynamicInvokerThunk); + + // The CallDescrWorkerInternal function will have put the return value into the return buffer, as register return values are + // extended to the size of a register, we can't just ask the CallDescrWorker to write directly into the return buffer. + // Thus we copy only the correct amount of data here to the real target address + byte* incomingRetBufPointer = *((byte**)(callerTransitionBlock + conversionParams._callerArgs.GetRetBuffArgOffset())); + + void* sourceBuffer = conversionParams._conversionInfo.IsObjectArrayDelegateThunk ? (void*)pinnedResultObject : callDescrData.pReturnBuffer; + Debug.Assert(sourceBuffer != null || conversionParams._conversionInfo.IsObjectArrayDelegateThunk); + + if (sourceBuffer == null) + { + // object array delegate thunk result is a null object. We'll fill the return buffer with 'returnSize' zeros in that case + gcSafeMemzeroPointer(incomingRetBufPointer, returnSize); + } + else + { + // Because we are copying into a caller provided buffer, we can't use a simple memory copy, we need to use a + // gc protected copy as the actual return buffer may be on the heap. + + bool useGCSafeCopy = false; + + if ((returnType == CorElementType.ELEMENT_TYPE_CLASS) || thRetType.IsValueType()) + { + // The GC Safe copy assumes that memory pointers are pointer-aligned and copy length is a multiple of pointer-size + if (isPointerAligned(incomingRetBufPointer) && isPointerAligned(sourceBuffer) && (returnSize % sizeof(IntPtr) == 0)) + { + useGCSafeCopy = true; + } + } + + if (useGCSafeCopy) + { + RuntimeAugments.BulkMoveWithWriteBarrier(new IntPtr(incomingRetBufPointer), new IntPtr(sourceBuffer), returnSize); + } + else + { + Buffer.MemoryCopy(sourceBuffer, incomingRetBufPointer, returnSize, returnSize); + } + } + +#if CALLINGCONVENTION_CALLEE_POPS + // Don't setup the callee pop argument until after copying into the ret buff. We may be using the location + // of the callee pop argument to keep track of the ret buff location + SetupCallerPopArgument(callerTransitionBlock, conversionParams._callerArgs); +#endif +#if TARGET_X86 + SetupCallerActualReturnData(callerTransitionBlock); + // On X86 the return buffer pointer is returned in eax. + t_NonArgRegisterReturnSpace.returnValue = new IntPtr(incomingRetBufPointer); + conversionParams._invokeReturnValue = ReturnIntegerPointReturnThunk; + return; +#else + // Because the return value was really returned on the heap, simply return as if void was returned. + conversionParams._invokeReturnValue = ReturnVoidReturnThunk; + return; +#endif + } + else + { +#if CALLINGCONVENTION_CALLEE_POPS + SetupCallerPopArgument(callerTransitionBlock, conversionParams._callerArgs); +#endif + + // The CallDescrWorkerInternal function will have put the return value into the return buffer. + // Here we copy the return buffer data into the argument registers for the return thunks. + // + // A return thunk takes an argument(by value) that is what is to be returned. + // + // The simplest case is the one where there is no return value + bool dummyBool; + if (conversionParams._callerArgs.GetReturnType(out thDummy, out dummyBool) == CorElementType.ELEMENT_TYPE_VOID) + { + conversionParams._invokeReturnValue = ReturnVoidReturnThunk; + return; + } + + // The second simplest case is when there is a return buffer argument for both the caller and callee + // In that case, we simply treat this as if we are returning void +#if TARGET_X86 + // Except on X86 where the return buffer is returned in the eax register, and looks like an integer return +#else + if (conversionParams._callerArgs.HasRetBuffArg() && conversionParams._calleeArgs.HasRetBuffArg()) + { + Debug.Assert(!conversionParams._conversionInfo.IsObjectArrayDelegateThunk); + Debug.Assert(!conversionParams._conversionInfo.IsAnyDynamicInvokerThunk); + conversionParams._invokeReturnValue = ReturnVoidReturnThunk; + return; + } +#endif + + void* returnValueToCopy = (void*)(callerTransitionBlock + TransitionBlock.GetOffsetOfReturnValuesBlock()); + + if (conversionParams._conversionInfo.IsObjectArrayDelegateThunk) + { + if (thRetType.IsValueType()) + { + returnValueToCopy = (void*)pinnedResultObject; +#if TARGET_X86 + Debug.Assert(returnSize <= sizeof(ReturnBlock)); + + if (returnValueToCopy == null) + { + // object array delegate thunk result is a null object. We'll fill the return buffer with 'returnSize' zeros in that case + memzeroPointer((byte*)(&((TransitionBlock*)callerTransitionBlock)->m_returnBlock), sizeof(ReturnBlock)); + } + else + { + ExtendingCopy_WriteBarrier(returnValueToCopy, &((TransitionBlock*)callerTransitionBlock)->m_returnBlock, returnType, returnSize); + } +#endif + } + else + { + returnValueToCopy = (void*)&pinnedResultObject; +#if TARGET_X86 + ((TransitionBlock*)callerTransitionBlock)->m_returnBlock.returnValue = pinnedResultObject; +#endif + } + } + else if (conversionParams._conversionInfo.IsAnyDynamicInvokerThunk && + (thRetType.IsValueType() || thRetType.IsPointerType() || returnType == CorElementType.ELEMENT_TYPE_BYREF)) + { + Debug.Assert(returnValueToCopy != null); + + if (returnType == CorElementType.ELEMENT_TYPE_VOID) + { + // Invokers returning void need to return a null object + returnValueToCopy = null; + } + else + { + if (!conversionParams._callerArgs.HasRetBuffArg() && conversionParams._calleeArgs.HasRetBuffArg()) + returnValueToCopy = (void*)(new IntPtr(*((void**)returnValueToCopy)) + IntPtr.Size); + + if (returnType == CorElementType.ELEMENT_TYPE_BYREF) + { + // If this is a byref return, we're going to dereference the result + returnValueToCopy = *(void**)returnValueToCopy; + } + + RuntimeTypeHandle returnTypeRuntimeTypeHandle = thRetType.GetRuntimeTypeHandle(); + + // Need to box value type before returning it + object returnValue; + if (returnType == CorElementType.ELEMENT_TYPE_BYREF && returnValueToCopy == null) + { + // This is a byref return and dereferencing it would result in a NullReferenceException. + // Set the return value to a sentinel that InvokeUtils will recognize. + // Can't throw from here or we would wrap this in a TargetInvocationException. + returnValue = InvokeUtils.NullByRefValueSentinel; + } + else if (RuntimeAugments.IsUnmanagedPointerType(returnTypeRuntimeTypeHandle)) + { + returnValue = System.Reflection.Pointer.Box(*(void**)returnValueToCopy, Type.GetTypeFromHandle(returnTypeRuntimeTypeHandle)); + } + else if (RuntimeAugments.IsValueType(returnTypeRuntimeTypeHandle)) + { + returnValue = RuntimeAugments.Box(returnTypeRuntimeTypeHandle, new IntPtr(returnValueToCopy)); + } + else + { + // byref return of a reference type + Debug.Assert(returnType == CorElementType.ELEMENT_TYPE_BYREF); + returnValue = Unsafe.As(ref *(byte*)returnValueToCopy); + } + CallConversionParameters.s_pinnedGCHandles._returnObjectHandle.Target = returnValue; + pinnedResultObject = CallConversionParameters.s_pinnedGCHandles._returnObjectHandle.GetRawTargetAddress(); + returnValueToCopy = (void*)&pinnedResultObject; + } + // Since we've changed the returnValueToCopy here, we need to update the idea of what we are returning + returnType = CorElementType.ELEMENT_TYPE_OBJECT; + thRetType = default(TypeHandle); + returnSize = TypeHandle.GetElemSize(returnType, thRetType); + +#if TARGET_X86 + ((TransitionBlock*)callerTransitionBlock)->m_returnBlock.returnValue = pinnedResultObject; +#endif + } + + // Handle floating point returns + + // The previous fpReturnSize was the callee fpReturnSize. Now reset to the caller return size to handle + // returning to the caller. + fpReturnSize = conversionParams._callerArgs.GetFPReturnSize(); + if (fpReturnSize != 0) + { + // We should never get here for delegate dynamic invoke thunks (the return type is always a boxed object) + Debug.Assert(!conversionParams._conversionInfo.IsAnyDynamicInvokerThunk); + +#if CALLDESCR_FPARGREGSARERETURNREGS + Debug.Assert(fpReturnSize <= sizeof(FloatArgumentRegisters)); + memzeroPointerAligned(callerTransitionBlock + TransitionBlock.GetOffsetOfFloatArgumentRegisters(), sizeof(FloatArgumentRegisters)); + if (returnValueToCopy == null) + { + // object array delegate thunk result is a null object. We'll fill the return buffer with 'returnSize' zeros in that case + Debug.Assert(conversionParams._conversionInfo.IsObjectArrayDelegateThunk); + memzeroPointer(callerTransitionBlock + TransitionBlock.GetOffsetOfFloatArgumentRegisters(), (int)fpReturnSize); + } + else + { + Buffer.MemoryCopy(returnValueToCopy, + callerTransitionBlock + TransitionBlock.GetOffsetOfFloatArgumentRegisters(), + (int)fpReturnSize, + (int)fpReturnSize); + } + conversionParams._invokeReturnValue = ReturnVoidReturnThunk; + return; +#else +#if CALLDESCR_FPARGREGS +#error Case not yet handled +#endif + Debug.Assert(fpReturnSize <= sizeof(ArgumentRegisters)); + +#if TARGET_X86 + SetupCallerActualReturnData(callerTransitionBlock); + t_NonArgRegisterReturnSpace = ((TransitionBlock*)callerTransitionBlock)->m_returnBlock; +#elif TARGET_WASM + throw new NotImplementedException(); +#else +#error Platform not implemented +#endif + +#if !TARGET_WASM + if (fpReturnSize == 4) + { + conversionParams._invokeReturnValue = ReturnFloatingPointReturn4Thunk; + } + else + { + conversionParams._invokeReturnValue = ReturnFloatingPointReturn8Thunk; + } + return; +#endif // !TARGET_WASM +#endif + } + +#if TARGET_X86 + SetupCallerActualReturnData(callerTransitionBlock); + t_NonArgRegisterReturnSpace = ((TransitionBlock*)callerTransitionBlock)->m_returnBlock; + conversionParams._invokeReturnValue = ReturnIntegerPointReturnThunk; + return; +#else + // If we reach here, we are returning value in the integer registers. + if (returnValueToCopy == null) + { + // Return result is a null object. We'll fill the return buffer with 'returnSize' zeros in that case + memzeroPointer(callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters(), returnSize); + } + else + { + ExtendingCopy_WriteBarrier(returnValueToCopy, callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters(), returnType, returnSize); + } + conversionParams._invokeReturnValue = ReturnIntegerPointReturnThunk; +#endif + } + } + + // + // Converting by-ref values to non-by-ref form requires the converter to be capable of taking a pointer to a small integer + // value anywhere in memory and then copying the referenced value into an ABI-compliant pointer-sized "slot" which + // faithfully communicates the value. In such cases, the argument slot prepared by the converter must conform to all + // sign/zero-extension rules mandated by the ABI. + // + // ARM32 requires all less-than-pointer-sized values to be sign/zero-extended when they are placed into pointer-sized + // slots (i.e., requires "producer-oriented" sign/zero-extension). x86/amd64 do not have this requirement (i.e., the + // unused high bytes of the pointer-sized slot are ignored by the consumer and are allowed to take on any value); however + // to reduce the need for ever more #ifs in this file, this behavior will not be #if'd away. (Its not wrong, its just unnecessary) + // + private static unsafe void ExtendingCopy_WriteBarrier(void* pSrcVoid, void* pDestVoid, CorElementType type, int typeSize) + { + byte* pSrc = (byte*)pSrcVoid; + byte* pDest = (byte*)pDestVoid; + + if (SignExtendType(type)) + SignExtend(pSrc, pDest, typeSize); + else if (ZeroExtendType(type)) + ZeroExtend(pSrc, pDest, typeSize); + else if (isPointerAligned(pSrc) && isPointerAligned(pDest) && (typeSize % sizeof(IntPtr) == 0)) + RuntimeAugments.BulkMoveWithWriteBarrier(new IntPtr(pDest), new IntPtr(pSrc), typeSize); + else + Buffer.MemoryCopy(pSrc, pDest, typeSize, typeSize); + } + + // + // Converting by-ref values to non-by-ref form requires the converter to be capable of taking a pointer to a small integer + // value anywhere in memory and then copying the referenced value into an ABI-compliant pointer-sized "slot" which + // faithfully communicates the value. In such cases, the argument slot prepared by the converter must conform to all + // sign/zero-extension rules mandated by the ABI. + // + // ARM32 requires all less-than-pointer-sized values to be sign/zero-extended when they are placed into pointer-sized + // slots (i.e., requires "producer-oriented" sign/zero-extension). x86/amd64 do not have this requirement (i.e., the + // unused high bytes of the pointer-sized slot are ignored by the consumer and are allowed to take on any value); however + // to reduce the need for ever more #ifs in this file, this behavior will not be #if'd away. (Its not wrong, its just unnecessary) + // + private static unsafe void ExtendingCopy_NoWriteBarrier(void* pSrcVoid, void* pDestVoid, CorElementType type, int typeSize) + { + byte* pSrc = (byte*)pSrcVoid; + byte* pDest = (byte*)pDestVoid; + + if (SignExtendType(type)) + SignExtend(pSrc, pDest, typeSize); + else if (ZeroExtendType(type)) + ZeroExtend(pSrc, pDest, typeSize); + else + Buffer.MemoryCopy(pSrc, pDest, typeSize, typeSize); + } + + private static bool SignExtendType(CorElementType type) + { + switch (type) + { + case CorElementType.ELEMENT_TYPE_I1: + case CorElementType.ELEMENT_TYPE_I2: +#if TARGET_64BIT + case CorElementType.ELEMENT_TYPE_I4: +#endif + return true; + + } + + return false; + } + + private static bool ZeroExtendType(CorElementType type) + { + switch (type) + { + case CorElementType.ELEMENT_TYPE_U1: + case CorElementType.ELEMENT_TYPE_BOOLEAN: + case CorElementType.ELEMENT_TYPE_CHAR: + case CorElementType.ELEMENT_TYPE_U2: +#if TARGET_64BIT + case CorElementType.ELEMENT_TYPE_U4: +#endif + return true; + + } + + return false; + } + + internal static unsafe void SignExtend(void* pSrcVoid, void* pDestVoid, int size) + { + byte* pSrc = (byte*)pSrcVoid; + byte* pDest = (byte*)pDestVoid; + + switch (size) + { + case 1: + *((IntPtr*)pDest) = new IntPtr(*(sbyte*)pSrc); + break; + + case 2: + *((IntPtr*)pDest) = new IntPtr(*(short*)pSrc); + break; + +#if TARGET_64BIT + // On 64 bit platforms, a 32 bit parameter may require truncation/extension + case 4: + *((IntPtr*)pDest) = new IntPtr(*(int*)pSrc); + break; +#endif + default: + Debug.Fail("Should only be called for sizes where sign extension is a meaningful concept"); + break; + } + } + + internal static unsafe void ZeroExtend(void* pSrcVoid, void* pDestVoid, int size) + { + byte* pSrc = (byte*)pSrcVoid; + byte* pDest = (byte*)pDestVoid; + + switch (size) + { + case 1: + *((UIntPtr*)pDest) = new UIntPtr(*(byte*)pSrc); + break; + + case 2: + *((UIntPtr*)pDest) = new UIntPtr(*(ushort*)pSrc); + break; + +#if TARGET_64BIT + // On 64 bit platforms, a 32 bit parameter may require truncation/extension + case 4: + *((UIntPtr*)pDest) = new UIntPtr(*(uint*)pSrc); + break; +#endif + default: + Debug.Fail("Should only be called for sizes where sign extension is a meaningful concept"); + break; + } + } + +#if CALLINGCONVENTION_CALLEE_POPS + private static unsafe void SetupCallerPopArgument(byte* callerTransitionBlock, ArgIterator callerArgs) + { + int argStackPopSize = callerArgs.CbStackPop(); + +#if TARGET_X86 + // In a callee pops architecture, we must specify how much stack space to pop to reset the frame + // to the ReturnValue thunk. + ((TransitionBlock*)callerTransitionBlock)->m_argumentRegisters.ecx = new IntPtr(argStackPopSize); +#else +#error handling of how callee pop is handled is not yet implemented for this platform +#endif + } +#endif + +#if TARGET_X86 + internal static unsafe void SetupCallerActualReturnData(byte* callerTransitionBlock) + { + // X86 needs to pass callee pop information to the return value thunks, so, since it + // only has 2 argument registers and may/may not need to return 8 bytes of data, put the return + // data in a seperate thread local store passed in the other available register (edx) + + fixed (ReturnBlock* actualReturnDataStructAddress = &t_NonArgRegisterReturnSpace) + { + ((TransitionBlock*)callerTransitionBlock)->m_argumentRegisters.edx = new IntPtr(actualReturnDataStructAddress); + } + } +#endif + } + + internal static class CallingConventionConverterLogger + { + [Conditional("CCCONVERTER_TRACE")] + public static void WriteLine(string message) + { + Debug.WriteLine(message); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallingConventions.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallingConventions.cs new file mode 100644 index 00000000000000..d1b2421957a23e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallingConventions.cs @@ -0,0 +1,1855 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +// +// This file is a line by line port of callingconvention.h from the desktop CLR. See reference source in the ReferenceSource directory +// +#if TARGET_ARM +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define FEATURE_HFA +#elif TARGET_ARM64 +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define ENREGISTERED_PARAMTYPE_MAXSIZE +#define FEATURE_HFA +#elif TARGET_X86 +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#elif TARGET_AMD64 +#if UNIXAMD64 +#define UNIX_AMD64_ABI +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#else +#endif +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define ENREGISTERED_PARAMTYPE_MAXSIZE +#elif TARGET_WASM +#else +#error Unknown architecture! +#endif + +// Provides an abstraction over platform specific calling conventions (specifically, the calling convention +// utilized by the JIT on that platform). The caller enumerates each argument of a signature in turn, and is +// provided with information mapping that argument into registers and/or stack locations. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.TypeLoader; +using Internal.NativeFormat; + +namespace Internal.Runtime.CallConverter +{ + public enum CallingConvention + { + ManagedInstance, + ManagedStatic, + StdCall, + /*FastCall, CDecl */ + } + + public static class CallingConventionInfo + { + public static bool TypeUsesReturnBuffer(RuntimeTypeHandle returnType, bool methodWithReturnTypeIsVarArg) + { + TypeHandle thReturnType = new TypeHandle(false, returnType); + CorElementType typeReturnType = thReturnType.GetCorElementType(); + + bool usesReturnBuffer; + uint fpReturnSizeIgnored; + ArgIterator.ComputeReturnValueTreatment(typeReturnType, thReturnType, methodWithReturnTypeIsVarArg, out usesReturnBuffer, out fpReturnSizeIgnored); + + return usesReturnBuffer; + } + } + + internal unsafe struct TypeHandle + { + public TypeHandle(bool isByRef, RuntimeTypeHandle MethodTable) + { + _eeType = MethodTable.ToEETypePtr(); + _isByRef = isByRef; + + if (_eeType->IsByRefType) + { + Debug.Assert(_isByRef == false); // ByRef to ByRef isn't valid + _isByRef = true; + _eeType = _eeType->RelatedParameterType; + } + } + + private readonly MethodTable* _eeType; + private readonly bool _isByRef; + + public bool Equals(TypeHandle other) + { + return _isByRef == other._isByRef && _eeType == other._eeType; + } + + public override int GetHashCode() { return (int)_eeType->HashCode; } + + public bool IsNull() { return _eeType == null && !_isByRef; } + public bool IsValueType() { if (_isByRef) return false; return _eeType->IsValueType; } + public bool IsPointerType() { if (_isByRef) return false; return _eeType->IsPointerType; } + + public unsafe uint GetSize() + { + if (IsValueType()) + return _eeType->ValueTypeSize; + else + return (uint)IntPtr.Size; + } + + public bool RequiresAlign8() + { +#if !TARGET_ARM + return false; +#else + if (_isByRef) + { + return false; + } + return _eeType->RequiresAlign8; +#endif + } + public bool IsHFA() + { +#if !TARGET_ARM && !TARGET_ARM64 + return false; +#else + if (_isByRef) + { + return false; + } + return _eeType->IsHFA; +#endif + } + + public CorElementType GetHFAType() + { + Debug.Assert(IsHFA()); +#if TARGET_ARM + if (RequiresAlign8()) + { + return CorElementType.ELEMENT_TYPE_R8; + } +#elif TARGET_ARM64 + if (_eeType->FieldAlignmentRequirement == IntPtr.Size) + { + return CorElementType.ELEMENT_TYPE_R8; + } +#endif + return CorElementType.ELEMENT_TYPE_R4; + } + + public CorElementType GetCorElementType() + { + if (_isByRef) + { + return CorElementType.ELEMENT_TYPE_BYREF; + } + + // The core redhawk runtime has a slightly different concept of what CorElementType should be for a type. It matches for primitive and enum types + // but for other types, it doesn't match the needs in this file. + EETypeElementType rhCorElementType = _eeType->ElementType; + + if (rhCorElementType >= EETypeElementType.Boolean && rhCorElementType <= EETypeElementType.UInt64) + { + Debug.Assert((int)EETypeElementType.Boolean == (int)CorElementType.ELEMENT_TYPE_BOOLEAN); + Debug.Assert((int)EETypeElementType.Int32 == (int)CorElementType.ELEMENT_TYPE_I4); + Debug.Assert((int)EETypeElementType.UInt64 == (int)CorElementType.ELEMENT_TYPE_U8); + return (CorElementType)rhCorElementType; + } + else if (rhCorElementType == EETypeElementType.Single) + { + return CorElementType.ELEMENT_TYPE_R4; + } + else if (rhCorElementType == EETypeElementType.Double) + { + return CorElementType.ELEMENT_TYPE_R8; + } + else if (rhCorElementType == EETypeElementType.IntPtr) + { + return CorElementType.ELEMENT_TYPE_I; + } + else if (rhCorElementType == EETypeElementType.UIntPtr) + { + return CorElementType.ELEMENT_TYPE_U; + } + else if (_eeType == typeof(void).TypeHandle.ToEETypePtr()) + { + return CorElementType.ELEMENT_TYPE_VOID; + } + else if (IsValueType()) + { + return CorElementType.ELEMENT_TYPE_VALUETYPE; + } + else if (_eeType->IsPointerType) + { + return CorElementType.ELEMENT_TYPE_PTR; + } + else + { + return CorElementType.ELEMENT_TYPE_CLASS; + } + } + + private static int[] s_elemSizes = new int[] + { + 0, //ELEMENT_TYPE_END 0x0 + 0, //ELEMENT_TYPE_VOID 0x1 + 1, //ELEMENT_TYPE_BOOLEAN 0x2 + 2, //ELEMENT_TYPE_CHAR 0x3 + 1, //ELEMENT_TYPE_I1 0x4 + 1, //ELEMENT_TYPE_U1 0x5 + 2, //ELEMENT_TYPE_I2 0x6 + 2, //ELEMENT_TYPE_U2 0x7 + 4, //ELEMENT_TYPE_I4 0x8 + 4, //ELEMENT_TYPE_U4 0x9 + 8, //ELEMENT_TYPE_I8 0xa + 8, //ELEMENT_TYPE_U8 0xb + 4, //ELEMENT_TYPE_R4 0xc + 8, //ELEMENT_TYPE_R8 0xd + -2, //ELEMENT_TYPE_STRING 0xe + -2, //ELEMENT_TYPE_PTR 0xf + -2, //ELEMENT_TYPE_BYREF 0x10 + -1, //ELEMENT_TYPE_VALUETYPE 0x11 + -2, //ELEMENT_TYPE_CLASS 0x12 + 0, //ELEMENT_TYPE_VAR 0x13 + -2, //ELEMENT_TYPE_ARRAY 0x14 + 0, //ELEMENT_TYPE_GENERICINST 0x15 + 0, //ELEMENT_TYPE_TYPEDBYREF 0x16 + 0, // UNUSED 0x17 + -2, //ELEMENT_TYPE_I 0x18 + -2, //ELEMENT_TYPE_U 0x19 + 0, // UNUSED 0x1a + -2, //ELEMENT_TYPE_FPTR 0x1b + -2, //ELEMENT_TYPE_OBJECT 0x1c + -2, //ELEMENT_TYPE_SZARRAY 0x1d + }; + + public static unsafe int GetElemSize(CorElementType t, TypeHandle thValueType) + { + if (((int)t) <= 0x1d) + { + int elemSize = s_elemSizes[(int)t]; + if (elemSize == -1) + { + return (int)thValueType.GetSize(); + } + if (elemSize == -2) + { + return IntPtr.Size; + } + return elemSize; + } + return 0; + } + + public RuntimeTypeHandle GetRuntimeTypeHandle() { return _eeType->ToRuntimeTypeHandle(); } + } + + // Describes how a single argument is laid out in registers and/or stack locations when given as an input to a + // managed method as part of a larger signature. + // + // Locations are split into floating point registers, general registers and stack offsets. Registers are + // obviously architecture dependent but are represented as a zero-based index into the usual sequence in which + // such registers are allocated for input on the platform in question. For instance: + // X86: 0 == ecx, 1 == edx + // ARM: 0 == r0, 1 == r1, 2 == r2 etc. + // + // Stack locations are represented as offsets from the stack pointer (at the point of the call). The offset is + // given as an index of a pointer sized slot. Similarly the size of data on the stack is given in slot-sized + // units. For instance, given an index of 2 and a size of 3: + // X86: argument starts at [ESP + 8] and is 12 bytes long + // AMD64: argument starts at [RSP + 16] and is 24 bytes long + // + // The structure is flexible enough to describe an argument that is split over several (consecutive) registers + // and possibly on to the stack as well. + internal struct ArgLocDesc + { + public int m_idxFloatReg; // First floating point register used (or -1) + public int m_cFloatReg; // Count of floating point registers used (or 0) + + public int m_idxGenReg; // First general register used (or -1) + public int m_cGenReg; // Count of general registers used (or 0) + + public int m_idxStack; // First stack slot used (or -1) + public int m_cStack; // Count of stack slots used (or 0) + +#if TARGET_ARM64 + public bool m_isSinglePrecision; // For determining if HFA is single or double precision +#endif + +#if TARGET_ARM + public bool m_fRequires64BitAlignment; // True if the argument should always be aligned (in registers or on the stack +#endif + + // Initialize to represent a non-placed argument (no register or stack slots referenced). + public void Init() + { + m_idxFloatReg = -1; + m_cFloatReg = 0; + m_idxGenReg = -1; + m_cGenReg = 0; + m_idxStack = -1; + m_cStack = 0; + +#if TARGET_ARM64 + m_isSinglePrecision = false; +#endif + +#if TARGET_ARM + m_fRequires64BitAlignment = false; +#endif + } + }; + + internal class ArgIteratorData + { + public ArgIteratorData(bool hasThis, + bool isVarArg, + TypeHandle[] parameterTypes, + TypeHandle returnType) + { + _hasThis = hasThis; + _isVarArg = isVarArg; + _parameterTypes = parameterTypes; + _returnType = returnType; + } + + private bool _hasThis; + private bool _isVarArg; + private TypeHandle[] _parameterTypes; + private TypeHandle _returnType; + + public override bool Equals(object obj) + { + if (this == obj) return true; + + ArgIteratorData other = obj as ArgIteratorData; + if (other == null) return false; + + if (_hasThis != other._hasThis || _isVarArg != other._isVarArg || !_returnType.Equals(other._returnType)) + return false; + + if (_parameterTypes == null) + return other._parameterTypes == null; + + if (other._parameterTypes == null || _parameterTypes.Length != other._parameterTypes.Length) + return false; + + for (int i = 0; i < _parameterTypes.Length; i++) + if (!_parameterTypes[i].Equals(other._parameterTypes[i])) + return false; + + return true; + } + + public override int GetHashCode() + { + return 37 + (_parameterTypes == null ? + _returnType.GetHashCode() : + TypeHashingAlgorithms.ComputeGenericInstanceHashCode(_returnType.GetHashCode(), _parameterTypes)); + } + + public bool HasThis() { return _hasThis; } + public bool IsVarArg() { return _isVarArg; } + public int NumFixedArgs() { return _parameterTypes != null ? _parameterTypes.Length : 0; } + + // Argument iteration. + public CorElementType GetArgumentType(int argNum, out TypeHandle thArgType) + { + thArgType = _parameterTypes[argNum]; + CorElementType returnValue = thArgType.GetCorElementType(); + return returnValue; + } + + public TypeHandle GetByRefArgumentType(int argNum) + { + return (argNum < _parameterTypes.Length && _parameterTypes[argNum].GetCorElementType() == CorElementType.ELEMENT_TYPE_BYREF) ? + _parameterTypes[argNum] : + default(TypeHandle); + } + + public CorElementType GetReturnType(out TypeHandle thRetType) + { + thRetType = _returnType; + return thRetType.GetCorElementType(); + } + +#if CCCONVERTER_TRACE + public string GetEETypeDebugName(int argNum) + { + Internal.TypeSystem.TypeSystemContext context = TypeSystemContextFactory.Create(); + var result = context.ResolveRuntimeTypeHandle(_parameterTypes[argNum].GetRuntimeTypeHandle()).ToString(); + TypeSystemContextFactory.Recycle(context); + return result; + } +#endif + } + + //----------------------------------------------------------------------- + // ArgIterator is helper for dealing with calling conventions. + // It is tightly coupled with TransitionBlock. It uses offsets into + // TransitionBlock to represent argument locations for efficiency + // reasons. Alternatively, it can also return ArgLocDesc for less + // performance critical code. + // + // The ARGITERATOR_BASE argument of the template is provider of the parsed + // method signature. Typically, the arg iterator works on top of MetaSig. + // Reflection invoke uses alternative implementation to save signature parsing + // time because of it has the parsed signature available. + //----------------------------------------------------------------------- + //template + internal unsafe struct ArgIterator //: public ARGITERATOR_BASE + { + private bool _hasThis; + private bool _hasParamType; + private bool _extraFunctionPointerArg; + private ArgIteratorData _argData; + private bool[] _forcedByRefParams; + private bool _skipFirstArg; + private bool _extraObjectFirstArg; + private CallingConvention _interpreterCallingConvention; + + public bool HasThis() { return _hasThis; } + public bool IsVarArg() { return _argData.IsVarArg(); } + public bool HasParamType() { return _hasParamType; } + public int NumFixedArgs() { return _argData.NumFixedArgs() + (_extraFunctionPointerArg ? 1 : 0) + (_extraObjectFirstArg ? 1 : 0); } + + // Argument iteration. + public CorElementType GetArgumentType(int argNum, out TypeHandle thArgType, out bool forceByRefReturn) + { + forceByRefReturn = false; + + if (_extraObjectFirstArg && argNum == 0) + { + thArgType = new TypeHandle(false, typeof(object).TypeHandle); + return CorElementType.ELEMENT_TYPE_CLASS; + } + + argNum = _extraObjectFirstArg ? argNum - 1 : argNum; + Debug.Assert(argNum >= 0); + + if (_forcedByRefParams != null && (argNum + 1) < _forcedByRefParams.Length) + forceByRefReturn = _forcedByRefParams[argNum + 1]; + + if (_extraFunctionPointerArg && argNum == _argData.NumFixedArgs()) + { + thArgType = new TypeHandle(false, typeof(IntPtr).TypeHandle); + return CorElementType.ELEMENT_TYPE_I; + } + + return _argData.GetArgumentType(argNum, out thArgType); + } + + public CorElementType GetReturnType(out TypeHandle thRetType, out bool forceByRefReturn) + { + if (_forcedByRefParams != null && _forcedByRefParams.Length > 0) + forceByRefReturn = _forcedByRefParams[0]; + else + forceByRefReturn = false; + + return _argData.GetReturnType(out thRetType); + } + +#if CCCONVERTER_TRACE + public string GetEETypeDebugName(int argNum) + { + if (_extraObjectFirstArg && argNum == 0) + return "System.Object"; + return _argData.GetEETypeDebugName(_extraObjectFirstArg ? argNum - 1 : argNum); + } +#endif + + public void Reset() + { + _argType = default(CorElementType); + _argTypeHandle = default(TypeHandle); + _argSize = 0; + _argNum = 0; + _argForceByRef = false; + _ITERATION_STARTED = false; + } + + //public: + //------------------------------------------------------------ + // Constructor + //------------------------------------------------------------ + public ArgIterator(ArgIteratorData argData, CallingConvention callConv, bool hasParamType, bool extraFunctionPointerArg, bool[] forcedByRefParams, bool skipFirstArg, bool extraObjectFirstArg) + { + this = default(ArgIterator); + _argData = argData; + _hasThis = callConv == CallingConvention.ManagedInstance; + _hasParamType = hasParamType; + _extraFunctionPointerArg = extraFunctionPointerArg; + _forcedByRefParams = forcedByRefParams; + _skipFirstArg = skipFirstArg; + _extraObjectFirstArg = extraObjectFirstArg; + _interpreterCallingConvention = callConv; + } + + public void SetHasParamTypeAndReset(bool value) + { + _hasParamType = value; + Reset(); + } + + public void SetHasThisAndReset(bool value) + { + _hasThis = value; + Reset(); + } + + private uint SizeOfArgStack() + { + // WRAPPER_NO_CONTRACT; + if (!_SIZE_OF_ARG_STACK_COMPUTED) + ForceSigWalk(); + Debug.Assert(_SIZE_OF_ARG_STACK_COMPUTED); + return (uint)_nSizeOfArgStack; + } + + // For use with ArgIterator. This function computes the amount of additional + // memory required above the TransitionBlock. The parameter offsets + // returned by ArgIterator::GetNextOffset are relative to a + // FramedMethodFrame, and may be in either of these regions. + public int SizeOfFrameArgumentArray() + { + // WRAPPER_NO_CONTRACT; + + uint size = SizeOfArgStack(); + +#if TARGET_AMD64 && !UNIX_AMD64_ABI + // The argument registers are not included in the stack size on AMD64 + size += ArchitectureConstants.ARGUMENTREGISTERS_SIZE; +#endif + + return (int)size; + } + + //------------------------------------------------------------------------ + +#if TARGET_X86 + public int CbStackPop() + { + // WRAPPER_NO_CONTRACT; + + if (this.IsVarArg()) + return 0; + else + return (int)SizeOfArgStack(); + } +#endif + + // Is there a hidden parameter for the return parameter? + // + public bool HasRetBuffArg() + { + // WRAPPER_NO_CONTRACT; + if (!_RETURN_FLAGS_COMPUTED) + ComputeReturnFlags(); + return _RETURN_HAS_RET_BUFFER; + } + + public uint GetFPReturnSize() + { + // WRAPPER_NO_CONTRACT; + if (!_RETURN_FLAGS_COMPUTED) + ComputeReturnFlags(); + return _fpReturnSize; + } + +#if TARGET_X86 + //========================================================================= + // Indicates whether an argument is to be put in a register using the + // default IL calling convention. This should be called on each parameter + // in the order it appears in the call signature. For a non-static meethod, + // this function should also be called once for the "this" argument, prior + // to calling it for the "real" arguments. Pass in a typ of ELEMENT_TYPE_CLASS. + // + // *pNumRegistersUsed: [in,out]: keeps track of the number of argument + // registers assigned previously. The caller should + // initialize this variable to 0 - then each call + // will update it. + // + // typ: the signature type + //========================================================================= + private static bool IsArgumentInRegister(ref int pNumRegistersUsed, CorElementType typ, TypeHandle thArgType) + { + // LIMITED_METHOD_CONTRACT; + if ((pNumRegistersUsed) < ArchitectureConstants.NUM_ARGUMENT_REGISTERS) + { + switch (typ) + { + case CorElementType.ELEMENT_TYPE_BOOLEAN: + case CorElementType.ELEMENT_TYPE_CHAR: + case CorElementType.ELEMENT_TYPE_I1: + case CorElementType.ELEMENT_TYPE_U1: + case CorElementType.ELEMENT_TYPE_I2: + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_I4: + case CorElementType.ELEMENT_TYPE_U4: + case CorElementType.ELEMENT_TYPE_STRING: + case CorElementType.ELEMENT_TYPE_PTR: + case CorElementType.ELEMENT_TYPE_BYREF: + case CorElementType.ELEMENT_TYPE_CLASS: + case CorElementType.ELEMENT_TYPE_ARRAY: + case CorElementType.ELEMENT_TYPE_I: + case CorElementType.ELEMENT_TYPE_U: + case CorElementType.ELEMENT_TYPE_FNPTR: + case CorElementType.ELEMENT_TYPE_OBJECT: + case CorElementType.ELEMENT_TYPE_SZARRAY: + pNumRegistersUsed++; + return true; + + case CorElementType.ELEMENT_TYPE_VALUETYPE: + { + // On ProjectN valuetypes of integral size are passed enregistered + int structSize = TypeHandle.GetElemSize(typ, thArgType); + switch (structSize) + { + case 1: + case 2: + case 4: + pNumRegistersUsed++; + return true; + } + break; + } + } + } + + return (false); + } +#endif // TARGET_X86 + +#if ENREGISTERED_PARAMTYPE_MAXSIZE + + // Note that this overload does not handle varargs + private static bool IsArgPassedByRef(TypeHandle th) + { + // LIMITED_METHOD_CONTRACT; + + Debug.Assert(!th.IsNull()); + + // This method only works for valuetypes. It includes true value types, + // primitives, enums and TypedReference. + Debug.Assert(th.IsValueType()); + + uint size = th.GetSize(); +#if TARGET_AMD64 + return IsArgPassedByRef((int)size); +#elif TARGET_ARM64 + // Composites greater than 16 bytes are passed by reference + return ((size > ArchitectureConstants.ENREGISTERED_PARAMTYPE_MAXSIZE) && !th.IsHFA()); +#else +#error ArgIterator::IsArgPassedByRef +#endif + } + +#if TARGET_AMD64 + // This overload should only be used in AMD64-specific code only. + private static bool IsArgPassedByRef(int size) + { + // LIMITED_METHOD_CONTRACT; + + // If the size is bigger than ENREGISTERED_PARAM_TYPE_MAXSIZE, or if the size is NOT a power of 2, then + // the argument is passed by reference. + return (size > ArchitectureConstants.ENREGISTERED_PARAMTYPE_MAXSIZE) || ((size & (size - 1)) != 0); + } +#endif + + // This overload should be used for varargs only. + private static bool IsVarArgPassedByRef(int size) + { + // LIMITED_METHOD_CONTRACT; + +#if TARGET_AMD64 + return IsArgPassedByRef(size); +#else + return (size > ArchitectureConstants.ENREGISTERED_PARAMTYPE_MAXSIZE); +#endif + } +#endif // ENREGISTERED_PARAMTYPE_MAXSIZE + + public bool IsArgPassedByRef() + { + // LIMITED_METHOD_CONTRACT; + if (IsArgForcedPassedByRef()) + { + return true; + } + + if (_argType == CorElementType.ELEMENT_TYPE_BYREF) + { + return true; + } +#if ENREGISTERED_PARAMTYPE_MAXSIZE +#if TARGET_AMD64 + return IsArgPassedByRef(_argSize); +#elif TARGET_ARM64 + if (_argType == CorElementType.ELEMENT_TYPE_VALUETYPE) + { + Debug.Assert(!_argTypeHandle.IsNull()); + return ((_argSize > ArchitectureConstants.ENREGISTERED_PARAMTYPE_MAXSIZE) && (!_argTypeHandle.IsHFA() || IsVarArg())); + } + return false; +#else +#error PORTABILITY_ASSERT("ArgIterator::IsArgPassedByRef"); +#endif +#else // ENREGISTERED_PARAMTYPE_MAXSIZE + return false; +#endif // ENREGISTERED_PARAMTYPE_MAXSIZE + } + + private bool IsArgForcedPassedByRef() + { + // This should be true for valuetypes instantiated over T in a generic signature using universal shared generic calling convention + return _argForceByRef; + } + + //------------------------------------------------------------ + // Return the offsets of the special arguments + //------------------------------------------------------------ + + public static int GetThisOffset() + { + return TransitionBlock.GetThisOffset(); + } + + public unsafe int GetRetBuffArgOffset() + { + // WRAPPER_NO_CONTRACT; + + Debug.Assert(this.HasRetBuffArg()); + +#if TARGET_X86 + // x86 is special as always + // DESKTOP BEHAVIOR ret += this.HasThis() ? ArgumentRegisters.GetOffsetOfEdx() : ArgumentRegisters.GetOffsetOfEcx(); + int ret = TransitionBlock.GetOffsetOfArgs(); +#else + // RetBuf arg is in the first argument register by default + int ret = TransitionBlock.GetOffsetOfArgumentRegisters(); + +#if TARGET_ARM64 + ret += ArgumentRegisters.GetOffsetOfx8(); +#else + // But if there is a this pointer, push it to the second. + if (this.HasThis()) + ret += IntPtr.Size; +#endif // TARGET_ARM64 +#endif // TARGET_X86 + + return ret; + } + + public unsafe int GetVASigCookieOffset() + { + // WRAPPER_NO_CONTRACT; + + Debug.Assert(this.IsVarArg()); + +#if TARGET_X86 + // x86 is special as always + return sizeof(TransitionBlock); +#else + // VaSig cookie is after this and retbuf arguments by default. + int ret = TransitionBlock.GetOffsetOfArgumentRegisters(); + + if (this.HasThis()) + { + ret += IntPtr.Size; + } + + if (this.HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) + { + ret += IntPtr.Size; + } + + return ret; +#endif + } + + public unsafe int GetParamTypeArgOffset() + { + Debug.Assert(this.HasParamType()); + +#if TARGET_X86 + // x86 is special as always + if (!_SIZE_OF_ARG_STACK_COMPUTED) + ForceSigWalk(); + + switch (_paramTypeLoc) + { + case ParamTypeLocation.Ecx:// PARAM_TYPE_REGISTER_ECX: + return TransitionBlock.GetOffsetOfArgumentRegisters() + ArgumentRegisters.GetOffsetOfEcx(); + case ParamTypeLocation.Edx: + return TransitionBlock.GetOffsetOfArgumentRegisters() + ArgumentRegisters.GetOffsetOfEdx(); + default: + break; + } + + // The param type arg is last stack argument otherwise + return sizeof(TransitionBlock); +#else + // The hidden arg is after this and retbuf arguments by default. + int ret = TransitionBlock.GetOffsetOfArgumentRegisters(); + + if (this.HasThis()) + { + ret += IntPtr.Size; + } + + if (this.HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) + { + ret += IntPtr.Size; + } + + return ret; +#endif + } + + //------------------------------------------------------------ + // Each time this is called, this returns a byte offset of the next + // argument from the TransitionBlock* pointer. This offset can be positive *or* negative. + // + // Returns TransitionBlock::InvalidOffset once you've hit the end + // of the list. + //------------------------------------------------------------ + public unsafe int GetNextOffset() + { + // WRAPPER_NO_CONTRACT; + // SUPPORTS_DAC; + + if (!_ITERATION_STARTED) + { + int numRegistersUsed = 0; +#if TARGET_X86 + int initialArgOffset = 0; +#endif + if (this.HasThis()) + numRegistersUsed++; + + if (this.HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) + { +#if !TARGET_X86 + numRegistersUsed++; +#else + // DESKTOP BEHAVIOR is to do nothing here, as ret buf is never reached by the scan algorithm that walks backwards + // but in .NET Native, the x86 argument scan is a forward scan, so we need to skip the ret buf arg (which is always + // on the stack) + initialArgOffset = IntPtr.Size; +#endif + } + + Debug.Assert(!this.IsVarArg() || !this.HasParamType()); + + // DESKTOP BEHAVIOR - This block is disabled for x86 as the param arg is the last argument on desktop x86. + if (this.HasParamType()) + { + numRegistersUsed++; + } + +#if !TARGET_X86 + if (this.IsVarArg()) + { + numRegistersUsed++; + } +#endif + +#if TARGET_X86 + if (this.IsVarArg()) + { + numRegistersUsed = ArchitectureConstants.NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs + } + +#if FEATURE_INTERPRETER + switch (_interpreterCallingConvention) + { + case CallingConvention.StdCall: + _numRegistersUsed = ArchitectureConstants.NUM_ARGUMENT_REGISTERS; + _curOfs = TransitionBlock.GetOffsetOfArgs() + numRegistersUsed * IntPtr.Size + initialArgOffset; + break; + + case CallingConvention.ManagedStatic: + case CallingConvention.ManagedInstance: + _numRegistersUsed = numRegistersUsed; + // DESKTOP BEHAVIOR _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + SizeOfArgStack()); + _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + initialArgOffset); + break; + + default: + Environment.FailFast("Unsupported calling convention."); + break; + } +#else + _numRegistersUsed = numRegistersUsed; +// DESKTOP BEHAVIOR _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + SizeOfArgStack()); + _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + initialArgOffset); +#endif + +#elif TARGET_AMD64 +#if UNIX_AMD64_ABI + _idxGenReg = numRegistersUsed; + _idxStack = 0; + _idxFPReg = 0; +#else + _curOfs = TransitionBlock.GetOffsetOfArgs() + numRegistersUsed * IntPtr.Size; +#endif +#elif TARGET_ARM + _idxGenReg = numRegistersUsed; + _idxStack = 0; + + _wFPRegs = 0; +#elif TARGET_ARM64 + _idxGenReg = numRegistersUsed; + _idxStack = 0; + + _idxFPReg = 0; +#elif TARGET_WASM + throw new NotImplementedException(); +#else + PORTABILITY_ASSERT("ArgIterator::GetNextOffset"); +#endif + +#if !TARGET_WASM + _argNum = (_skipFirstArg ? 1 : 0); + + _ITERATION_STARTED = true; +#endif // !TARGET_WASM + } + + if (_argNum >= this.NumFixedArgs()) + return TransitionBlock.InvalidOffset; + + CorElementType argType = this.GetArgumentType(_argNum, out _argTypeHandle, out _argForceByRef); + + _argTypeHandleOfByRefParam = (argType == CorElementType.ELEMENT_TYPE_BYREF ? _argData.GetByRefArgumentType(_argNum) : default(TypeHandle)); + + _argNum++; + + int argSize = TypeHandle.GetElemSize(argType, _argTypeHandle); + +#if TARGET_ARM64 + // NOT DESKTOP BEHAVIOR: The S and D registers overlap, and the UniversalTransitionThunk copies D registers to the transition blocks. We'll need + // to work with the D registers here as well. + bool processingFloatsAsDoublesFromTransitionBlock = false; + if (argType == CorElementType.ELEMENT_TYPE_VALUETYPE && _argTypeHandle.IsHFA() && _argTypeHandle.GetHFAType() == CorElementType.ELEMENT_TYPE_R4) + { + if ((argSize / sizeof(float)) + _idxFPReg <= 8) + { + argSize *= 2; + processingFloatsAsDoublesFromTransitionBlock = true; + } + } +#endif + + _argType = argType; + _argSize = argSize; + + argType = _argForceByRef ? CorElementType.ELEMENT_TYPE_BYREF : argType; + argSize = _argForceByRef ? IntPtr.Size : argSize; + +#pragma warning disable 219,168 // Unused local + int argOfs; +#pragma warning restore 219,168 + +#if TARGET_X86 +#if FEATURE_INTERPRETER + if (_interpreterCallingConvention != CallingConvention.ManagedStatic && _interpreterCallingConvention != CallingConvention.ManagedInstance) + { + argOfs = _curOfs; + _curOfs += ArchitectureConstants.StackElemSize(argSize); + return argOfs; + } +#endif + if (IsArgumentInRegister(ref _numRegistersUsed, argType, _argTypeHandle)) + { + return TransitionBlock.GetOffsetOfArgumentRegisters() + (ArchitectureConstants.NUM_ARGUMENT_REGISTERS - _numRegistersUsed) * IntPtr.Size; + } + + // DESKTOP BEHAVIOR _curOfs -= ArchitectureConstants.StackElemSize(argSize); + // DESKTOP BEHAVIOR return _curOfs; + argOfs = _curOfs; + _curOfs += ArchitectureConstants.StackElemSize(argSize); + Debug.Assert(argOfs >= TransitionBlock.GetOffsetOfArgs()); + return argOfs; +#elif TARGET_AMD64 +#if UNIX_AMD64_ABI + int cFPRegs = 0; + + switch (argType) + { + + case CorElementType.ELEMENT_TYPE_R4: + // 32-bit floating point argument. + cFPRegs = 1; + break; + + case CorElementType.ELEMENT_TYPE_R8: + // 64-bit floating point argument. + cFPRegs = 1; + break; + + case CorElementType.ELEMENT_TYPE_VALUETYPE: + { + // UNIXTODO: FEATURE_UNIX_AMD64_STRUCT_PASSING: Passing of structs, HFAs. For now, use the Windows convention. + argSize = IntPtr.Size; + break; + } + + default: + break; + } + + int cbArg = ArchitectureConstants.StackElemSize(argSize); + int cArgSlots = cbArg / ArchitectureConstants.STACK_ELEM_SIZE; + + if (cFPRegs > 0) + { + if (cFPRegs + m_idxFPReg <= 8) + { + int argOfsInner = TransitionBlock.GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; + m_idxFPReg += cFPRegs; + return argOfsInner; + } + } + else + { + if (m_idxGenReg + cArgSlots <= 6) + { + int argOfsInner = TransitionBlock.GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; + m_idxGenReg += cArgSlots; + return argOfsInner; + } + } + + argOfs = TransitionBlock.GetOffsetOfArgs() + m_idxStack * 8; + m_idxStack += cArgSlots; + return argOfs; +#else + int cFPRegs = 0; + + switch (argType) + { + case CorElementType.ELEMENT_TYPE_R4: + // 32-bit floating point argument. + cFPRegs = 1; + break; + + case CorElementType.ELEMENT_TYPE_R8: + // 64-bit floating point argument. + cFPRegs = 1; + break; + } + + // Each argument takes exactly one slot on TARGET_AMD64 + argOfs = _curOfs - TransitionBlock.GetOffsetOfArgs(); + _curOfs += IntPtr.Size; + + if ((cFPRegs == 0) || (argOfs >= sizeof(ArgumentRegisters))) + { + return argOfs + TransitionBlock.GetOffsetOfArgs(); + } + else + { + int idxFpReg = argOfs / IntPtr.Size; + return TransitionBlock.GetOffsetOfFloatArgumentRegisters() + idxFpReg * sizeof(M128A); + } +#endif +#elif TARGET_ARM + // First look at the underlying type of the argument to determine some basic properties: + // 1) The size of the argument in bytes (rounded up to the stack slot size of 4 if necessary). + // 2) Whether the argument represents a floating point primitive (ELEMENT_TYPE_R4 or ELEMENT_TYPE_R8). + // 3) Whether the argument requires 64-bit alignment (anything that contains a Int64/UInt64). + + bool fFloatingPoint = false; + bool fRequiresAlign64Bit = false; + + switch (argType) + { + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + // 64-bit integers require 64-bit alignment on TARGET_ARM. + fRequiresAlign64Bit = true; + break; + + case CorElementType.ELEMENT_TYPE_R4: + // 32-bit floating point argument. + fFloatingPoint = true; + break; + + case CorElementType.ELEMENT_TYPE_R8: + // 64-bit floating point argument. + fFloatingPoint = true; + fRequiresAlign64Bit = true; + break; + + case CorElementType.ELEMENT_TYPE_VALUETYPE: + { + // Value type case: extract the alignment requirement, note that this has to handle + // the interop "native value types". + fRequiresAlign64Bit = _argTypeHandle.RequiresAlign8(); + + // Handle HFAs: packed structures of 1-4 floats or doubles that are passed in FP argument + // registers if possible. + if (_argTypeHandle.IsHFA()) + fFloatingPoint = true; + + break; + } + + default: + // The default is are 4-byte arguments (or promoted to 4 bytes), non-FP and don't require any + // 64-bit alignment. + break; + } + + // Now attempt to place the argument into some combination of floating point or general registers and + // the stack. + + // Save the alignment requirement + _fRequires64BitAlignment = fRequiresAlign64Bit; + + int cbArg = ArchitectureConstants.StackElemSize(argSize); + int cArgSlots = cbArg / 4; + + // Ignore floating point argument placement in registers if we're dealing with a vararg function (the ABI + // specifies this so that vararg processing on the callee side is simplified). + if (fFloatingPoint && !this.IsVarArg()) + { + // Handle floating point (primitive) arguments. + + // First determine whether we can place the argument in VFP registers. There are 16 32-bit + // and 8 64-bit argument registers that share the same register space (e.g. D0 overlaps S0 and + // S1). The ABI specifies that VFP values will be passed in the lowest sequence of registers that + // haven't been used yet and have the required alignment. So the sequence (float, double, float) + // would be mapped to (S0, D1, S1) or (S0, S2/S3, S1). + // + // We use a 16-bit bitmap to record which registers have been used so far. + // + // So we can use the same basic loop for each argument type (float, double or HFA struct) we set up + // the following input parameters based on the size and alignment requirements of the arguments: + // wAllocMask : bitmask of the number of 32-bit registers we need (1 for 1, 3 for 2, 7 for 3 etc.) + // cSteps : number of loop iterations it'll take to search the 16 registers + // cShift : how many bits to shift the allocation mask on each attempt + + ushort wAllocMask = checked((ushort)((1 << (cbArg / 4)) - 1)); + ushort cSteps = (ushort)(fRequiresAlign64Bit ? 9 - (cbArg / 8) : 17 - (cbArg / 4)); + ushort cShift = fRequiresAlign64Bit ? (ushort)2 : (ushort)1; + + // Look through the availability bitmask for a free register or register pair. + for (ushort i = 0; i < cSteps; i++) + { + if ((_wFPRegs & wAllocMask) == 0) + { + // We found one, mark the register or registers as used. + _wFPRegs |= wAllocMask; + + // Indicate the registers used to the caller and return. + return TransitionBlock.GetOffsetOfFloatArgumentRegisters() + (i * cShift * 4); + } + wAllocMask <<= cShift; + } + + // The FP argument is going to live on the stack. Once this happens the ABI demands we mark all FP + // registers as unavailable. + _wFPRegs = 0xffff; + + // Doubles or HFAs containing doubles need the stack aligned appropriately. + if (fRequiresAlign64Bit) + _idxStack = ALIGN_UP(_idxStack, 2); + + // Indicate the stack location of the argument to the caller. + int argOfsInner = TransitionBlock.GetOffsetOfArgs() + _idxStack * 4; + + // Record the stack usage. + _idxStack += cArgSlots; + + return argOfsInner; + } + + // + // Handle the non-floating point case. + // + + if (_idxGenReg < 4) + { + if (fRequiresAlign64Bit) + { + // The argument requires 64-bit alignment. Align either the next general argument register if + // we have any left. See step C.3 in the algorithm in the ABI spec. + _idxGenReg = ALIGN_UP(_idxGenReg, 2); + } + + int argOfsInner = TransitionBlock.GetOffsetOfArgumentRegisters() + _idxGenReg * 4; + + int cRemainingRegs = 4 - _idxGenReg; + if (cArgSlots <= cRemainingRegs) + { + // Mark the registers just allocated as used. + _idxGenReg += cArgSlots; + return argOfsInner; + } + + // The ABI supports splitting a non-FP argument across registers and the stack. But this is + // disabled if the FP arguments already overflowed onto the stack (i.e. the stack index is not + // zero). The following code marks the general argument registers as exhausted if this condition + // holds. See steps C.5 in the algorithm in the ABI spec. + + _idxGenReg = 4; + + if (_idxStack == 0) + { + _idxStack += cArgSlots - cRemainingRegs; + return argOfsInner; + } + } + + if (fRequiresAlign64Bit) + { + // The argument requires 64-bit alignment. If it is going to be passed on the stack, align + // the next stack slot. See step C.6 in the algorithm in the ABI spec. + _idxStack = ALIGN_UP(_idxStack, 2); + } + + argOfs = TransitionBlock.GetOffsetOfArgs() + _idxStack * 4; + + // Advance the stack pointer over the argument just placed. + _idxStack += cArgSlots; + + return argOfs; +#elif TARGET_ARM64 + + int cFPRegs = 0; + + switch (argType) + { + case CorElementType.ELEMENT_TYPE_R4: + // 32-bit floating point argument. + cFPRegs = 1; + break; + + case CorElementType.ELEMENT_TYPE_R8: + // 64-bit floating point argument. + cFPRegs = 1; + break; + + case CorElementType.ELEMENT_TYPE_VALUETYPE: + { + // Handle HFAs: packed structures of 2-4 floats or doubles that are passed in FP argument + // registers if possible. + if (_argTypeHandle.IsHFA()) + { + CorElementType type = _argTypeHandle.GetHFAType(); + if (processingFloatsAsDoublesFromTransitionBlock) + cFPRegs = argSize / sizeof(double); + else + cFPRegs = (type == CorElementType.ELEMENT_TYPE_R4) ? (argSize / sizeof(float)) : (argSize / sizeof(double)); + } + else + { + // Composite greater than 16bytes should be passed by reference + if (argSize > ArchitectureConstants.ENREGISTERED_PARAMTYPE_MAXSIZE) + { + argSize = IntPtr.Size; + } + } + + break; + } + + default: + break; + } + + int cbArg = ArchitectureConstants.StackElemSize(argSize); + int cArgSlots = cbArg / ArchitectureConstants.STACK_ELEM_SIZE; + + if (cFPRegs > 0 && !this.IsVarArg()) + { + if (cFPRegs + _idxFPReg <= 8) + { + int argOfsInner = TransitionBlock.GetOffsetOfFloatArgumentRegisters() + _idxFPReg * 8; + _idxFPReg += cFPRegs; + return argOfsInner; + } + else + { + _idxFPReg = 8; + } + } + else + { + if (_idxGenReg + cArgSlots <= 8) + { + int argOfsInner = TransitionBlock.GetOffsetOfArgumentRegisters() + _idxGenReg * 8; + _idxGenReg += cArgSlots; + return argOfsInner; + } + else + { + _idxGenReg = 8; + } + } + + argOfs = TransitionBlock.GetOffsetOfArgs() + _idxStack * 8; + _idxStack += cArgSlots; + return argOfs; +#elif TARGET_WASM + throw new NotImplementedException(); +#else +#error PORTABILITY_ASSERT("ArgIterator::GetNextOffset"); +#endif + } + + + public CorElementType GetArgType(out TypeHandle pTypeHandle) + { + // LIMITED_METHOD_CONTRACT; + pTypeHandle = _argTypeHandle; + return _argType; + } + + public CorElementType GetByRefArgType(out TypeHandle pByRefArgTypeHandle) + { + // LIMITED_METHOD_CONTRACT; + pByRefArgTypeHandle = _argTypeHandleOfByRefParam; + return _argType; + } + + public int GetArgSize() + { + // LIMITED_METHOD_CONTRACT; + return _argSize; + } + + private unsafe void ForceSigWalk() + { + // This can be only used before the actual argument iteration started + Debug.Assert(!_ITERATION_STARTED); + +#if TARGET_X86 + // + // x86 is special as always + // + + int numRegistersUsed = 0; + int nSizeOfArgStack = 0; + + if (this.HasThis()) + numRegistersUsed++; + + if (this.HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) + { + // DESKTOP BEHAVIOR numRegistersUsed++; + // On ProjectN ret buff arg is passed on the call stack as the top stack arg + nSizeOfArgStack += IntPtr.Size; + } + + // DESKTOP BEHAVIOR - This block is disabled for x86 as the param arg is the last argument on desktop x86. + if (this.HasParamType()) + { + numRegistersUsed++; + _paramTypeLoc = (numRegistersUsed == 1) ? + ParamTypeLocation.Ecx : ParamTypeLocation.Edx; + Debug.Assert(numRegistersUsed <= 2); + } + + if (this.IsVarArg()) + { + nSizeOfArgStack += IntPtr.Size; + numRegistersUsed = ArchitectureConstants.NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs + } + +#if FEATURE_INTERPRETER + switch (_interpreterCallingConvention) + { + case CallingConvention.StdCall: + numRegistersUsed = ArchitectureConstants.NUM_ARGUMENT_REGISTERS; + break; + + case CallingConvention.ManagedStatic: + case CallingConvention.ManagedInstance: + break; + + default: + Environment.FailFast("Unsupported calling convention."); + break; + } +#endif // FEATURE_INTERPRETER + + int nArgs = this.NumFixedArgs(); + for (int i = (_skipFirstArg ? 1 : 0); i < nArgs; i++) + { + TypeHandle thArgType; + bool argForcedToBeByref; + CorElementType type = this.GetArgumentType(i, out thArgType, out argForcedToBeByref); + if (argForcedToBeByref) + type = CorElementType.ELEMENT_TYPE_BYREF; + + if (!IsArgumentInRegister(ref numRegistersUsed, type, thArgType)) + { + int structSize = TypeHandle.GetElemSize(type, thArgType); + + nSizeOfArgStack += ArchitectureConstants.StackElemSize(structSize); + + if (nSizeOfArgStack > ArchitectureConstants.MAX_ARG_SIZE) + { + throw new NotSupportedException(); + } + } + } + +#if DESKTOP // DESKTOP BEHAVIOR + if (this.HasParamType()) + { + if (numRegistersUsed < ArchitectureConstants.NUM_ARGUMENT_REGISTERS) + { + numRegistersUsed++; + paramTypeLoc = (numRegistersUsed == 1) ? + ParamTypeLocation.Ecx : ParamTypeLocation.Edx; + } + else + { + nSizeOfArgStack += IntPtr.Size; + paramTypeLoc = ParamTypeLocation.Stack; + } + } +#endif // DESKTOP BEHAVIOR + +#else // TARGET_X86 + + int maxOffset = TransitionBlock.GetOffsetOfArgs(); + + int ofs; + while (TransitionBlock.InvalidOffset != (ofs = GetNextOffset())) + { + int stackElemSize; + +#if TARGET_AMD64 + // All stack arguments take just one stack slot on AMD64 because of arguments bigger + // than a stack slot are passed by reference. + stackElemSize = ArchitectureConstants.STACK_ELEM_SIZE; +#else + stackElemSize = ArchitectureConstants.StackElemSize(GetArgSize()); + if (IsArgPassedByRef()) + stackElemSize = ArchitectureConstants.STACK_ELEM_SIZE; +#endif + + int endOfs = ofs + stackElemSize; + if (endOfs > maxOffset) + { + if (endOfs > ArchitectureConstants.MAX_ARG_SIZE) + { + throw new NotSupportedException(); + } + maxOffset = endOfs; + } + } + // Clear the iterator started flag + _ITERATION_STARTED = false; + + int nSizeOfArgStack = maxOffset - TransitionBlock.GetOffsetOfArgs(); + +#if TARGET_AMD64 && !UNIX_AMD64_ABI + nSizeOfArgStack = (nSizeOfArgStack > (int)sizeof(ArgumentRegisters)) ? + (nSizeOfArgStack - sizeof(ArgumentRegisters)) : 0; +#endif + +#endif // TARGET_X86 + + // Cache the result + _nSizeOfArgStack = nSizeOfArgStack; + _SIZE_OF_ARG_STACK_COMPUTED = true; + + this.Reset(); + } + + +#if !TARGET_X86 + // Accessors for built in argument descriptions of the special implicit parameters not mentioned directly + // in signatures (this pointer and the like). Whether or not these can be used successfully before all the + // explicit arguments have been scanned is platform dependent. + public unsafe void GetThisLoc(ArgLocDesc* pLoc) { GetSimpleLoc(GetThisOffset(), pLoc); } + public unsafe void GetRetBuffArgLoc(ArgLocDesc* pLoc) { GetSimpleLoc(GetRetBuffArgOffset(), pLoc); } + public unsafe void GetParamTypeLoc(ArgLocDesc* pLoc) { GetSimpleLoc(GetParamTypeArgOffset(), pLoc); } + public unsafe void GetVASigCookieLoc(ArgLocDesc* pLoc) { GetSimpleLoc(GetVASigCookieOffset(), pLoc); } +#endif // !TARGET_X86 + +#if TARGET_ARM + // Get layout information for the argument that the ArgIterator is currently visiting. + private unsafe void GetArgLoc(int argOffset, ArgLocDesc* pLoc) + { + // LIMITED_METHOD_CONTRACT; + + pLoc->Init(); + + pLoc->m_fRequires64BitAlignment = _fRequires64BitAlignment; + + int cSlots = (GetArgSize() + 3) / 4; + + if (TransitionBlock.IsFloatArgumentRegisterOffset(argOffset)) + { + pLoc->m_idxFloatReg = (argOffset - TransitionBlock.GetOffsetOfFloatArgumentRegisters()) / 4; + pLoc->m_cFloatReg = cSlots; + return; + } + + if (!TransitionBlock.IsStackArgumentOffset(argOffset)) + { + pLoc->m_idxGenReg = TransitionBlock.GetArgumentIndexFromOffset(argOffset); + + if (cSlots <= (4 - pLoc->m_idxGenReg)) + { + pLoc->m_cGenReg = cSlots; + } + else + { + pLoc->m_cGenReg = 4 - pLoc->m_idxGenReg; + + pLoc->m_idxStack = 0; + pLoc->m_cStack = cSlots - pLoc->m_cGenReg; + } + } + else + { + pLoc->m_idxStack = TransitionBlock.GetArgumentIndexFromOffset(argOffset) - 4; + pLoc->m_cStack = cSlots; + } + } +#endif // TARGET_ARM + +#if TARGET_ARM64 + // Get layout information for the argument that the ArgIterator is currently visiting. + private unsafe void GetArgLoc(int argOffset, ArgLocDesc* pLoc) + { + // LIMITED_METHOD_CONTRACT; + + pLoc->Init(); + + if (TransitionBlock.IsFloatArgumentRegisterOffset(argOffset)) + { + // Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes. + pLoc->m_idxFloatReg = (argOffset - TransitionBlock.GetOffsetOfFloatArgumentRegisters()) / 8; + + if (!_argTypeHandle.IsNull() && _argTypeHandle.IsHFA()) + { + CorElementType type = _argTypeHandle.GetHFAType(); + bool isFloatType = (type == CorElementType.ELEMENT_TYPE_R4); + + // DESKTOP BEHAVIOR pLoc->m_cFloatReg = isFloatType ? GetArgSize() / sizeof(float) : GetArgSize() / sizeof(double); + pLoc->m_cFloatReg = GetArgSize() / sizeof(double); + pLoc->m_isSinglePrecision = isFloatType; + } + else + { + pLoc->m_cFloatReg = 1; + } + return; + } + + int cSlots = (GetArgSize() + 7) / 8; + + // Composites greater than 16bytes are passed by reference + TypeHandle dummy; + if (GetArgType(out dummy) == CorElementType.ELEMENT_TYPE_VALUETYPE && GetArgSize() > ArchitectureConstants.ENREGISTERED_PARAMTYPE_MAXSIZE) + { + cSlots = 1; + } + + if (!TransitionBlock.IsStackArgumentOffset(argOffset)) + { + pLoc->m_idxGenReg = TransitionBlock.GetArgumentIndexFromOffset(argOffset); + pLoc->m_cGenReg = cSlots; + } + else + { + pLoc->m_idxStack = TransitionBlock.GetStackArgumentIndexFromOffset(argOffset); + pLoc->m_cStack = cSlots; + } + } +#endif // TARGET_ARM64 + +#if TARGET_AMD64 && UNIX_AMD64_ABI + // Get layout information for the argument that the ArgIterator is currently visiting. + unsafe void GetArgLoc(int argOffset, ArgLocDesc* pLoc) + { + // LIMITED_METHOD_CONTRACT; + + if (argOffset == TransitionBlock.StructInRegsOffset) + { + // We always already have argLocDesc for structs passed in registers, we + // compute it in the GetNextOffset for those since it is always needed. + Debug.Assert(false); + return; + } + + pLoc->Init(); + + if (TransitionBlock.IsFloatArgumentRegisterOffset(argOffset)) + { + // Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes. + pLoc->m_idxFloatReg = (argOffset - TransitionBlock.GetOffsetOfFloatArgumentRegisters()) / 8; + + // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention. + pLoc->m_cFloatReg = 1; + return; + } + + // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention. + int cSlots = 1; + + if (!TransitionBlock.IsStackArgumentOffset(argOffset)) + { + pLoc->m_idxGenReg = TransitionBlock.GetArgumentIndexFromOffset(argOffset); + pLoc->m_cGenReg = cSlots; + } + else + { + pLoc->m_idxStack = (argOffset - TransitionBlock.GetOffsetOfArgs()) / 8; + pLoc->m_cStack = cSlots; + } + } +#endif // TARGET_AMD64 && UNIX_AMD64_ABI + + private int _nSizeOfArgStack; // Cached value of SizeOfArgStack + + private int _argNum; + + // Cached information about last argument + private CorElementType _argType; + private int _argSize; + private TypeHandle _argTypeHandle; + private TypeHandle _argTypeHandleOfByRefParam; + private bool _argForceByRef; + +#if TARGET_X86 + private int _curOfs; // Current position of the stack iterator + private int _numRegistersUsed; +#endif + +#if TARGET_AMD64 +#if UNIX_AMD64_ABI + int _idxGenReg; + int _idxStack; + int _idxFPReg; +#else + private int _curOfs; // Current position of the stack iterator +#endif +#endif + +#if TARGET_ARM + private int _idxGenReg; // Next general register to be assigned a value + private int _idxStack; // Next stack slot to be assigned a value + + private ushort _wFPRegs; // Bitmask of available floating point argument registers (s0-s15/d0-d7) + private bool _fRequires64BitAlignment; // Cached info about the current arg +#endif + +#if TARGET_ARM64 + private int _idxGenReg; // Next general register to be assigned a value + private int _idxStack; // Next stack slot to be assigned a value + private int _idxFPReg; // Next FP register to be assigned a value +#endif + + // These are enum flags in CallingConvention.h, but that's really ugly in C#, so I've changed them to bools. + private bool _ITERATION_STARTED; // Started iterating over arguments + private bool _SIZE_OF_ARG_STACK_COMPUTED; + private bool _RETURN_FLAGS_COMPUTED; + private bool _RETURN_HAS_RET_BUFFER; // Cached value of HasRetBuffArg + private uint _fpReturnSize; + + // enum { + /* ITERATION_STARTED = 0x0001, + SIZE_OF_ARG_STACK_COMPUTED = 0x0002, + RETURN_FLAGS_COMPUTED = 0x0004, + RETURN_HAS_RET_BUFFER = 0x0008, // Cached value of HasRetBuffArg + */ +#if TARGET_X86 + private enum ParamTypeLocation + { + Stack, + Ecx, + Edx + } + private ParamTypeLocation _paramTypeLoc; + /* PARAM_TYPE_REGISTER_MASK = 0x0030, + PARAM_TYPE_REGISTER_STACK = 0x0010, + PARAM_TYPE_REGISTER_ECX = 0x0020, + PARAM_TYPE_REGISTER_EDX = 0x0030,*/ +#endif + + // METHOD_INVOKE_NEEDS_ACTIVATION = 0x0040, // Flag used by ArgIteratorForMethodInvoke + + // RETURN_FP_SIZE_SHIFT = 8, // The rest of the flags is cached value of GetFPReturnSize + // }; + + internal static void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetType, bool isVarArgMethod, out bool usesRetBuffer, out uint fpReturnSize) + + { + usesRetBuffer = false; + fpReturnSize = 0; + + switch (type) + { + case CorElementType.ELEMENT_TYPE_TYPEDBYREF: + throw new NotSupportedException(); +#if ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE + // if (sizeof(TypedByRef) > ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) + // flags |= RETURN_HAS_RET_BUFFER; +#else +// flags |= RETURN_HAS_RET_BUFFER; +#endif + // break; + + case CorElementType.ELEMENT_TYPE_R4: + fpReturnSize = sizeof(float); + break; + + case CorElementType.ELEMENT_TYPE_R8: + fpReturnSize = sizeof(double); + break; + + case CorElementType.ELEMENT_TYPE_VALUETYPE: +#if ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE + { + Debug.Assert(!thRetType.IsNull() && thRetType.IsValueType()); + +#if FEATURE_HFA + if (thRetType.IsHFA() && !isVarArgMethod) + { + CorElementType hfaType = thRetType.GetHFAType(); + +#if TARGET_ARM64 + // DESKTOP BEHAVIOR fpReturnSize = (hfaType == CorElementType.ELEMENT_TYPE_R4) ? (4 * (uint)sizeof(float)) : (4 * (uint)sizeof(double)); + // S and D registers overlap. Since we copy D registers in the UniversalTransitionThunk, we'll + // thread floats like doubles during copying. + fpReturnSize = 4 * (uint)sizeof(double); +#else + fpReturnSize = (hfaType == CorElementType.ELEMENT_TYPE_R4) ? + (4 * (uint)sizeof(float)) : + (4 * (uint)sizeof(double)); +#endif + + break; + } +#endif + + uint size = thRetType.GetSize(); + +#if TARGET_X86 || TARGET_AMD64 + // Return value types of size which are not powers of 2 using a RetBuffArg + if ((size & (size - 1)) != 0) + { + usesRetBuffer = true; + break; + } +#endif + + if (size <= ArchitectureConstants.ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) + break; + } +#endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE + + // Value types are returned using return buffer by default + usesRetBuffer = true; + break; + + default: + break; + } + } + + private void ComputeReturnFlags() + { + TypeHandle thRetType; + CorElementType type = this.GetReturnType(out thRetType, out _RETURN_HAS_RET_BUFFER); + + if (!_RETURN_HAS_RET_BUFFER) + { + ComputeReturnValueTreatment(type, thRetType, this.IsVarArg(), out _RETURN_HAS_RET_BUFFER, out _fpReturnSize); + } + + _RETURN_FLAGS_COMPUTED = true; + } + + +#if !TARGET_X86 + private unsafe void GetSimpleLoc(int offset, ArgLocDesc* pLoc) + { + // WRAPPER_NO_CONTRACT; + pLoc->Init(); + pLoc->m_idxGenReg = TransitionBlock.GetArgumentIndexFromOffset(offset); + pLoc->m_cGenReg = 1; + } +#endif + + public static int ALIGN_UP(int input, int align_to) + { + return (input + (align_to - 1)) & ~(align_to - 1); + } + + public static bool IS_ALIGNED(IntPtr val, int alignment) + { + Debug.Assert(0 == (alignment & (alignment - 1))); + return 0 == (val.ToInt64() & (alignment - 1)); + } + + public static bool IsRetBuffPassedAsFirstArg() + { + // WRAPPER_NO_CONTRACT; +#if !TARGET_ARM64 + return true; +#else + return false; +#endif + } + } + + internal enum CorElementType + { + ELEMENT_TYPE_END = 0x00, + + ELEMENT_TYPE_VOID = 0x1, + ELEMENT_TYPE_BOOLEAN = 0x2, + ELEMENT_TYPE_CHAR = 0x3, + ELEMENT_TYPE_I1 = 0x4, + ELEMENT_TYPE_U1 = 0x5, + ELEMENT_TYPE_I2 = 0x6, + ELEMENT_TYPE_U2 = 0x7, + ELEMENT_TYPE_I4 = 0x8, + ELEMENT_TYPE_U4 = 0x9, + ELEMENT_TYPE_I8 = 0xa, + ELEMENT_TYPE_U8 = 0xb, + ELEMENT_TYPE_R4 = 0xc, + ELEMENT_TYPE_R8 = 0xd, + ELEMENT_TYPE_STRING = 0xe, + ELEMENT_TYPE_PTR = 0xf, + ELEMENT_TYPE_BYREF = 0x10, + ELEMENT_TYPE_VALUETYPE = 0x11, + ELEMENT_TYPE_CLASS = 0x12, + + ELEMENT_TYPE_ARRAY = 0x14, + + ELEMENT_TYPE_TYPEDBYREF = 0x16, + ELEMENT_TYPE_I = 0x18, + ELEMENT_TYPE_U = 0x19, + ELEMENT_TYPE_FNPTR = 0x1b, + ELEMENT_TYPE_OBJECT = 0x1c, + ELEMENT_TYPE_SZARRAY = 0x1d, + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CanonicallyEquivalentEntryLocator.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CanonicallyEquivalentEntryLocator.cs new file mode 100644 index 00000000000000..22e5a2636fa5ae --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CanonicallyEquivalentEntryLocator.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Internal.Runtime.Augments; +using Internal.TypeSystem; + +namespace Internal.Runtime.TypeLoader +{ + public struct CanonicallyEquivalentEntryLocator + { + private RuntimeTypeHandle _typeToFind; + private RuntimeTypeHandle _genericDefinition; + private RuntimeTypeHandle[] _genericArgs; + private DefType _defType; + private CanonicalFormKind _canonKind; + + public CanonicallyEquivalentEntryLocator(RuntimeTypeHandle typeToFind, CanonicalFormKind kind) + { + if (RuntimeAugments.IsGenericType(typeToFind)) + { + _genericDefinition = RuntimeAugments.GetGenericInstantiation(typeToFind, out _genericArgs); + } + else + { + _genericArgs = null; + _genericDefinition = default(RuntimeTypeHandle); + } + + _typeToFind = typeToFind; + _canonKind = kind; + _defType = null; + } + + internal CanonicallyEquivalentEntryLocator(DefType typeToFind, CanonicalFormKind kind) + { + _genericArgs = null; + _genericDefinition = default(RuntimeTypeHandle); + _typeToFind = default(RuntimeTypeHandle); + _canonKind = kind; + _defType = typeToFind; + } + + public int LookupHashCode + { + get + { + if (_defType != null) + return _defType.ConvertToCanonForm(_canonKind).GetHashCode(); + + if (!_genericDefinition.IsNull()) + return TypeLoaderEnvironment.Instance.GetCanonicalHashCode(_typeToFind, _canonKind); + else + return _typeToFind.GetHashCode(); + } + } + + public bool IsCanonicallyEquivalent(RuntimeTypeHandle other) + { + if (_defType != null) + { + TypeDesc typeToFindAsCanon = _defType.ConvertToCanonForm(_canonKind); + TypeDesc otherTypeAsTypeDesc = _defType.Context.ResolveRuntimeTypeHandle(other); + TypeDesc otherTypeAsCanon = otherTypeAsTypeDesc.ConvertToCanonForm(_canonKind); + return typeToFindAsCanon == otherTypeAsCanon; + } + + if (!_genericDefinition.IsNull()) + { + if (RuntimeAugments.IsGenericType(other)) + { + RuntimeTypeHandle otherGenericDefinition; + RuntimeTypeHandle[] otherGenericArgs; + otherGenericDefinition = RuntimeAugments.GetGenericInstantiation(other, out otherGenericArgs); + + return _genericDefinition.Equals(otherGenericDefinition) && TypeLoaderEnvironment.Instance.CanInstantiationsShareCode(_genericArgs, otherGenericArgs, _canonKind); + } + else + return false; + } + else + return _typeToFind.Equals(other); + } + + public bool ConversionToCanonFormIsAChange() + { + if (_defType != null) + { + return _defType.ConvertToCanonForm(_canonKind) != _defType; + } + + if (_genericArgs != null) + return TypeLoaderEnvironment.Instance.ConversionToCanonFormIsAChange(_genericArgs, _canonKind); + + return false; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/DispatchCellInfo.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/DispatchCellInfo.cs new file mode 100644 index 00000000000000..ef9a093eba3100 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/DispatchCellInfo.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime; +using Internal.Runtime.Augments; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace Internal.Runtime.TypeLoader +{ +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + internal enum DispatchCellType + { + InterfaceAndSlot = 0x0, + MetadataToken = 0x1, + VTableOffset = 0x2, + } + + [StructLayout(LayoutKind.Sequential)] + internal struct DispatchCellInfo + { + public DispatchCellType CellType; + public IntPtr InterfaceType; + public ushort InterfaceSlot; + public byte HasCache; + public uint MetadataToken; + public uint VTableOffset; + } +#endif +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs new file mode 100644 index 00000000000000..aa65aecb53db4d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs @@ -0,0 +1,1163 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.InteropServices; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.TypeLoader; +using System.Collections.Generic; +using System.Threading; + +using Internal.Metadata.NativeFormat; +using Internal.TypeSystem; + +namespace Internal.Runtime.TypeLoader +{ + internal static class RuntimeTypeHandleEETypeExtensions + { + public static unsafe MethodTable* ToEETypePtr(this RuntimeTypeHandle rtth) + { + return (MethodTable*)(*(IntPtr*)&rtth); + } + + public static unsafe IntPtr ToIntPtr(this RuntimeTypeHandle rtth) + { + return *(IntPtr*)&rtth; + } + + public static unsafe bool IsDynamicType(this RuntimeTypeHandle rtth) + { + return rtth.ToEETypePtr()->IsDynamicType; + } + + public static unsafe int GetNumVtableSlots(this RuntimeTypeHandle rtth) + { + return rtth.ToEETypePtr()->NumVtableSlots; + } + + public static unsafe IntPtr GetDictionary(this RuntimeTypeHandle rtth) + { + return EETypeCreator.GetDictionary(rtth.ToEETypePtr()); + } + + public static unsafe void SetDictionary(this RuntimeTypeHandle rtth, int dictionarySlot, IntPtr dictionary) + { + Debug.Assert(rtth.ToEETypePtr()->IsDynamicType && dictionarySlot < rtth.GetNumVtableSlots()); + *(IntPtr*)((byte*)rtth.ToEETypePtr() + sizeof(MethodTable) + dictionarySlot * IntPtr.Size) = dictionary; + } + + public static unsafe void SetInterface(this RuntimeTypeHandle rtth, int interfaceIndex, RuntimeTypeHandle interfaceType) + { + rtth.ToEETypePtr()->InterfaceMap[interfaceIndex].InterfaceType = interfaceType.ToEETypePtr(); + } + + public static unsafe void SetGenericDefinition(this RuntimeTypeHandle rtth, RuntimeTypeHandle genericDefinitionHandle) + { + rtth.ToEETypePtr()->GenericDefinition = genericDefinitionHandle.ToEETypePtr(); + } + + public static unsafe void SetGenericVariance(this RuntimeTypeHandle rtth, int argumentIndex, GenericVariance variance) + { + rtth.ToEETypePtr()->GenericVariance[argumentIndex] = variance; + } + + public static unsafe void SetGenericArity(this RuntimeTypeHandle rtth, uint arity) + { + rtth.ToEETypePtr()->GenericArity = arity; + } + + public static unsafe void SetGenericArgument(this RuntimeTypeHandle rtth, int argumentIndex, RuntimeTypeHandle argumentType) + { + rtth.ToEETypePtr()->GenericArguments[argumentIndex].Value = argumentType.ToEETypePtr(); + } + + public static unsafe void SetRelatedParameterType(this RuntimeTypeHandle rtth, RuntimeTypeHandle relatedTypeHandle) + { + rtth.ToEETypePtr()->RelatedParameterType = relatedTypeHandle.ToEETypePtr(); + } + + public static unsafe void SetParameterizedTypeShape(this RuntimeTypeHandle rtth, uint value) + { + rtth.ToEETypePtr()->ParameterizedTypeShape = value; + } + + public static unsafe void SetBaseType(this RuntimeTypeHandle rtth, RuntimeTypeHandle baseTypeHandle) + { + rtth.ToEETypePtr()->BaseType = baseTypeHandle.ToEETypePtr(); + } + + public static unsafe void SetComponentSize(this RuntimeTypeHandle rtth, ushort componentSize) + { + rtth.ToEETypePtr()->ComponentSize = componentSize; + } + } + + internal static class MemoryHelpers + { + public static int AlignUp(int val, int alignment) + { + Debug.Assert(val >= 0 && alignment >= 0); + + // alignment must be a power of 2 for this implementation to work (need modulo otherwise) + Debug.Assert(0 == (alignment & (alignment - 1))); + int result = (val + (alignment - 1)) & ~(alignment - 1); + Debug.Assert(result >= val); // check for overflow + + return result; + } + + public static unsafe void Memset(IntPtr destination, int length, byte value) + { + byte* pbDest = (byte*)destination.ToPointer(); + while (length > 0) + { + *pbDest = value; + pbDest++; + length--; + } + } + + public static IntPtr AllocateMemory(int cbBytes) + { + return Marshal.AllocHGlobal(new IntPtr(cbBytes)); + } + + public static void FreeMemory(IntPtr memoryPtrToFree) + { + Marshal.FreeHGlobal(memoryPtrToFree); + } + } + + internal static unsafe class EETypeCreator + { + private static IntPtr s_emptyGCDesc; + + private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCodeOfNewType, + int arity, bool requireVtableSlotMapping, TypeBuilderState state) + { + bool successful = false; + IntPtr eeTypePtrPlusGCDesc = IntPtr.Zero; + IntPtr dynamicDispatchMapPtr = IntPtr.Zero; + IntPtr writableDataPtr = IntPtr.Zero; + DynamicModule* dynamicModulePtr = null; + IntPtr gcStaticData = IntPtr.Zero; + IntPtr nonGcStaticData = IntPtr.Zero; + IntPtr genericComposition = IntPtr.Zero; + + try + { + Debug.Assert((pTemplateEEType != null) || (state.TypeBeingBuilt as MetadataType != null)); + + // In some situations involving arrays we can find as a template a dynamically generated type. + // In that case, the correct template would be the template used to create the dynamic type in the first + // place. + if (pTemplateEEType != null && pTemplateEEType->IsDynamicType) + { + pTemplateEEType = pTemplateEEType->DynamicTemplateType; + } + + ModuleInfo moduleInfo = TypeLoaderEnvironment.GetModuleInfoForType(state.TypeBeingBuilt); + dynamicModulePtr = moduleInfo.DynamicModulePtr; + Debug.Assert(dynamicModulePtr != null); + + bool requiresDynamicDispatchMap = requireVtableSlotMapping && (pTemplateEEType != null) && pTemplateEEType->HasDispatchMap; + + uint valueTypeFieldPaddingEncoded = 0; + int baseSize = 0; + + bool isValueType; + bool hasFinalizer; + bool isNullable; + bool isArray; + bool isGeneric; + ushort componentSize = 0; + ushort flags; + ushort runtimeInterfacesLength = 0; + bool isGenericEETypeDef = false; + bool isAbstractClass; + bool isByRefLike; + IntPtr typeManager = IntPtr.Zero; + + if (state.RuntimeInterfaces != null) + { + runtimeInterfacesLength = checked((ushort)state.RuntimeInterfaces.Length); + } + + if (pTemplateEEType != null) + { + valueTypeFieldPaddingEncoded = EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue( + pTemplateEEType->ValueTypeFieldPadding, + (uint)pTemplateEEType->FieldAlignmentRequirement, + IntPtr.Size); + baseSize = (int)pTemplateEEType->BaseSize; + isValueType = pTemplateEEType->IsValueType; + hasFinalizer = pTemplateEEType->IsFinalizable; + isNullable = pTemplateEEType->IsNullable; + componentSize = pTemplateEEType->ComponentSize; + flags = pTemplateEEType->Flags; + isArray = pTemplateEEType->IsArray; + isGeneric = pTemplateEEType->IsGeneric; + isAbstractClass = pTemplateEEType->IsAbstract && !pTemplateEEType->IsInterface; + isByRefLike = pTemplateEEType->IsByRefLike; + typeManager = pTemplateEEType->PointerToTypeManager; + Debug.Assert(pTemplateEEType->NumInterfaces == runtimeInterfacesLength); + } + else if (state.TypeBeingBuilt.IsGenericDefinition) + { + flags = (ushort)EETypeKind.GenericTypeDefEEType; + isValueType = state.TypeBeingBuilt.IsValueType; + flags = EETypeBuilderHelpers.ComputeFlags(state.TypeBeingBuilt); + hasFinalizer = false; + isArray = false; + isNullable = false; + isGeneric = false; + isGenericEETypeDef = true; + isAbstractClass = false; + isByRefLike = false; + componentSize = checked((ushort)state.TypeBeingBuilt.Instantiation.Length); + baseSize = 0; + } + else + { + isValueType = state.TypeBeingBuilt.IsValueType; + hasFinalizer = state.TypeBeingBuilt.HasFinalizer; + isNullable = state.TypeBeingBuilt.GetTypeDefinition().IsNullable; + flags = EETypeBuilderHelpers.ComputeFlags(state.TypeBeingBuilt); + isArray = false; + isGeneric = state.TypeBeingBuilt.HasInstantiation; + + isAbstractClass = (state.TypeBeingBuilt is MetadataType) + && ((MetadataType)state.TypeBeingBuilt).IsAbstract + && !state.TypeBeingBuilt.IsInterface; + + isByRefLike = (state.TypeBeingBuilt is DefType) && ((DefType)state.TypeBeingBuilt).IsByRefLike; + + if (state.TypeBeingBuilt.HasVariance) + { + state.GenericVarianceFlags = new GenericVariance[state.TypeBeingBuilt.Instantiation.Length]; + int i = 0; + + foreach (GenericParameterDesc gpd in state.TypeBeingBuilt.GetTypeDefinition().Instantiation) + { + Debug.Assert((int)Internal.Runtime.GenericVariance.Covariant == (int)Internal.TypeSystem.GenericVariance.Covariant); + Debug.Assert((int)Internal.Runtime.GenericVariance.Contravariant == (int)Internal.TypeSystem.GenericVariance.Contravariant); + state.GenericVarianceFlags[i] = (GenericVariance)gpd.Variance; + i++; + } + Debug.Assert(i == state.GenericVarianceFlags.Length); + } + } + + flags |= (ushort)EETypeFlags.IsDynamicTypeFlag; + + // TODO! Change to if template is Universal or non-Existent + if (state.TypeSize.HasValue) + { + baseSize = state.TypeSize.Value; + + int baseSizeBeforeAlignment = baseSize; + + baseSize = MemoryHelpers.AlignUp(baseSize, IntPtr.Size); + + if (isValueType) + { + // Compute the valuetype padding size based on size before adding the object type pointer field to the size + uint cbValueTypeFieldPadding = (uint)(baseSize - baseSizeBeforeAlignment); + + // Add Object type pointer field to base size + baseSize += IntPtr.Size; + + valueTypeFieldPaddingEncoded = (uint)EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue(cbValueTypeFieldPadding, (uint)state.FieldAlignment.Value, IntPtr.Size); + } + + // Minimum base size is 3 pointers, and requires us to bump the size of an empty class type + if (baseSize <= IntPtr.Size) + { + // ValueTypes should already have had their size bumped up by the normal type layout process + Debug.Assert(!isValueType); + baseSize += IntPtr.Size; + } + + // Add sync block skew + baseSize += IntPtr.Size; + + // Minimum basesize is 3 pointers + Debug.Assert(baseSize >= (IntPtr.Size * 3)); + } + + // Optional fields encoding + int cbOptionalFieldsSize; + OptionalFieldsRuntimeBuilder optionalFields; + { + optionalFields = new OptionalFieldsRuntimeBuilder(pTemplateEEType != null ? pTemplateEEType->OptionalFieldsPtr : null); + + uint rareFlags = optionalFields.GetFieldValue(EETypeOptionalFieldTag.RareFlags, 0); + + if (state.NumSealedVTableEntries > 0) + rareFlags |= (uint)EETypeRareFlags.HasSealedVTableEntriesFlag; + + if (requiresDynamicDispatchMap) + rareFlags |= (uint)EETypeRareFlags.HasDynamicallyAllocatedDispatchMapFlag; + + if (state.NonGcDataSize != 0) + rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithNonGcStatics; + + if (state.GcDataSize != 0) + rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithGcStatics; + + if (state.ThreadDataSize != 0) + rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithThreadStatics; + +#if TARGET_ARM + if (state.FieldAlignment == 8) + rareFlags |= (uint)EETypeRareFlags.RequiresAlign8Flag; + else + rareFlags &= ~(uint)EETypeRareFlags.RequiresAlign8Flag; +#endif + +#if TARGET_ARM || TARGET_ARM64 + if (state.IsHFA) + rareFlags |= (uint)EETypeRareFlags.IsHFAFlag; + else + rareFlags &= ~(uint)EETypeRareFlags.IsHFAFlag; +#endif + if (state.HasStaticConstructor) + rareFlags |= (uint)EETypeRareFlags.HasCctorFlag; + else + rareFlags &= ~(uint)EETypeRareFlags.HasCctorFlag; + + if (isAbstractClass) + rareFlags |= (uint)EETypeRareFlags.IsAbstractClassFlag; + else + rareFlags &= ~(uint)EETypeRareFlags.IsAbstractClassFlag; + + if (isByRefLike) + rareFlags |= (uint)EETypeRareFlags.IsByRefLikeFlag; + else + rareFlags &= ~(uint)EETypeRareFlags.IsByRefLikeFlag; + + if (isNullable) + { + uint nullableValueOffset = state.NullableValueOffset; + + // The stored offset is never zero (Nullable has a boolean there indicating whether the value is valid). + // If the real offset is one, then the field isn't set. Otherwise the offset is encoded - 1 to save space. + if (nullableValueOffset == 1) + optionalFields.ClearField(EETypeOptionalFieldTag.NullableValueOffset); + else + optionalFields.SetFieldValue(EETypeOptionalFieldTag.NullableValueOffset, checked(nullableValueOffset - 1)); + } + else + { + optionalFields.ClearField(EETypeOptionalFieldTag.NullableValueOffset); + } + + rareFlags |= (uint)EETypeRareFlags.HasDynamicModuleFlag; + + optionalFields.SetFieldValue(EETypeOptionalFieldTag.RareFlags, rareFlags); + + // Dispatch map is fetched either from template type, or from the dynamically allocated DispatchMap field + optionalFields.ClearField(EETypeOptionalFieldTag.DispatchMap); + + optionalFields.ClearField(EETypeOptionalFieldTag.ValueTypeFieldPadding); + + if (valueTypeFieldPaddingEncoded != 0) + optionalFields.SetFieldValue(EETypeOptionalFieldTag.ValueTypeFieldPadding, valueTypeFieldPaddingEncoded); + + // Compute size of optional fields encoding + cbOptionalFieldsSize = optionalFields.Encode(); + Debug.Assert(cbOptionalFieldsSize > 0); + } + + // Note: The number of vtable slots on the MethodTable to create is not necessary equal to the number of + // vtable slots on the template type for universal generics (see ComputeVTableLayout) + ushort numVtableSlots = state.NumVTableSlots; + + // Compute the MethodTable size and allocate it + MethodTable* pEEType; + { + // In order to get the size of the MethodTable to allocate we need the following information + // 1) The number of VTable slots (from the TypeBuilderState) + // 2) The number of Interfaces (from the template) + // 3) Whether or not there is a finalizer (from the template) + // 4) Optional fields size + // 5) Whether or not the type has sealed virtuals (from the TypeBuilderState) + int cbEEType = (int)MethodTable.GetSizeofEEType( + numVtableSlots, + runtimeInterfacesLength, + hasFinalizer, + true, + state.NumSealedVTableEntries > 0, + isGeneric, + state.NonGcDataSize != 0, + state.GcDataSize != 0, + state.ThreadDataSize != 0); + + // Dynamic types have an extra pointer-sized field that contains a pointer to their template type + cbEEType += IntPtr.Size; + + // Check if we need another pointer sized field for a dynamic DispatchMap + cbEEType += (requiresDynamicDispatchMap ? IntPtr.Size : 0); + + // Add another pointer sized field for a DynamicModule + cbEEType += IntPtr.Size; + + int cbGCDesc = GetInstanceGCDescSize(state, pTemplateEEType, isValueType, isArray); + int cbGCDescAligned = MemoryHelpers.AlignUp(cbGCDesc, IntPtr.Size); + + // Allocate enough space for the MethodTable + gcDescSize + eeTypePtrPlusGCDesc = MemoryHelpers.AllocateMemory(cbGCDescAligned + cbEEType + cbOptionalFieldsSize); + + // Get the MethodTable pointer, and the template MethodTable pointer + pEEType = (MethodTable*)(eeTypePtrPlusGCDesc + cbGCDescAligned); + state.HalfBakedRuntimeTypeHandle = pEEType->ToRuntimeTypeHandle(); + + // Set basic MethodTable fields + pEEType->ComponentSize = componentSize; + pEEType->Flags = flags; + pEEType->BaseSize = (uint)baseSize; + pEEType->NumVtableSlots = numVtableSlots; + pEEType->NumInterfaces = runtimeInterfacesLength; + pEEType->HashCode = hashCodeOfNewType; + pEEType->PointerToTypeManager = typeManager; + + // Write the GCDesc + bool isSzArray = isArray ? state.ArrayRank < 1 : false; + int arrayRank = isArray ? state.ArrayRank.Value : 0; + CreateInstanceGCDesc(state, pTemplateEEType, pEEType, baseSize, cbGCDesc, isValueType, isArray, isSzArray, arrayRank); + Debug.Assert(pEEType->HasGCPointers == (cbGCDesc != 0)); + +#if GENERICS_FORCE_USG + if (state.NonUniversalTemplateType != null) + { + Debug.Assert(state.NonUniversalInstanceGCDescSize == cbGCDesc, "Non-universal instance GCDesc size not matching with universal GCDesc size!"); + Debug.Assert(cbGCDesc == 0 || pEEType->HasGCPointers); + + // The TestGCDescsForEquality helper will compare 2 GCDescs for equality, 4 bytes at a time (GCDesc contents treated as integers), and will read the + // GCDesc data in *reverse* order for instance GCDescs (subtracts 4 from the pointer values at each iteration). + // - For the first GCDesc, we use (pEEType - 4) to point to the first 4-byte integer directly preceeding the MethodTable + // - For the second GCDesc, given that the state.NonUniversalInstanceGCDesc already points to the first byte preceeding the template MethodTable, we + // subtract 3 to point to the first 4-byte integer directly preceeding the template MethodTable + TestGCDescsForEquality(new IntPtr((byte*)pEEType - 4), state.NonUniversalInstanceGCDesc - 3, cbGCDesc, true); + } +#endif + + // Copy the encoded optional fields buffer to the newly allocated memory, and update the OptionalFields field on the MethodTable + // It is important to set the optional fields first on the newly created MethodTable, because all other 'setters' + // will assert that the type is dynamic, just to make sure we are not making any changes to statically compiled types + pEEType->OptionalFieldsPtr = (byte*)pEEType + cbEEType; + optionalFields.WriteToEEType(pEEType, cbOptionalFieldsSize); + +#if !PROJECTN + pEEType->PointerToTypeManager = PermanentAllocatedMemoryBlobs.GetPointerToIntPtr(moduleInfo.Handle.GetIntPtrUNSAFE()); +#endif + pEEType->DynamicModule = dynamicModulePtr; + + // Copy VTable entries from template type + int numSlotsFilled = 0; + IntPtr* pVtable = (IntPtr*)((byte*)pEEType + sizeof(MethodTable)); + if (pTemplateEEType != null) + { + IntPtr* pTemplateVtable = (IntPtr*)((byte*)pTemplateEEType + sizeof(MethodTable)); + for (int i = 0; i < pTemplateEEType->NumVtableSlots; i++) + { + int vtableSlotInDynamicType = requireVtableSlotMapping ? state.VTableSlotsMapping.GetVTableSlotInTargetType(i) : i; + if (vtableSlotInDynamicType != -1) + { + Debug.Assert(vtableSlotInDynamicType < numVtableSlots); + + IntPtr dictionaryPtrValue; + if (requireVtableSlotMapping && state.VTableSlotsMapping.IsDictionarySlot(i, out dictionaryPtrValue)) + { + // This must be the dictionary pointer value of one of the base types of the + // current universal generic type being constructed. + pVtable[vtableSlotInDynamicType] = dictionaryPtrValue; + + // Assert that the current template vtable slot is also a NULL value since all + // universal generic template types have NULL dictionary slot values in their vtables + Debug.Assert(pTemplateVtable[i] == IntPtr.Zero); + } + else + { + pVtable[vtableSlotInDynamicType] = pTemplateVtable[i]; + } + numSlotsFilled++; + } + } + } + else if (isGenericEETypeDef) + { + // If creating a Generic Type Definition + Debug.Assert(pEEType->NumVtableSlots == 0); + } + else + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + // Dynamically loaded type + + // Fill the vtable with vtable resolution thunks in all slots except for + // the dictionary slots, which should be filled with dictionary pointers if those + // dictionaries are already published. + + TypeDesc nextTypeToExamineForDictionarySlot = state.TypeBeingBuilt; + TypeDesc typeWithDictionary; + int nextDictionarySlot = GetMostDerivedDictionarySlot(ref nextTypeToExamineForDictionarySlot, out typeWithDictionary); + + for (int iSlot = pEEType->NumVtableSlots - 1; iSlot >= 0; iSlot--) + { + bool isDictionary = iSlot == nextDictionarySlot; + if (!isDictionary) + { + pVtable[iSlot] = LazyVTableResolver.GetThunkForSlot(iSlot); + } + else + { + if (typeWithDictionary.RetrieveRuntimeTypeHandleIfPossible()) + { + pVtable[iSlot] = typeWithDictionary.RuntimeTypeHandle.GetDictionary(); + } + nextDictionarySlot = GetMostDerivedDictionarySlot(ref nextTypeToExamineForDictionarySlot, out typeWithDictionary); + } + numSlotsFilled++; + } +#else + Environment.FailFast("Template type loader is null, but metadata based type loader is not in use"); +#endif + } + + Debug.Assert(numSlotsFilled == numVtableSlots); + + // Copy Pointer to finalizer method from the template type + if (hasFinalizer) + { + if (pTemplateEEType != null) + { + pEEType->FinalizerCode = pTemplateEEType->FinalizerCode; + } + else + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + pEEType->FinalizerCode = LazyVTableResolver.GetFinalizerThunk(); +#else + Environment.FailFast("Template type loader is null, but metadata based type loader is not in use"); +#endif + } + } + } + + // Copy the sealed vtable entries if they exist on the template type + if (state.NumSealedVTableEntries > 0) + { + state.HalfBakedSealedVTable = MemoryHelpers.AllocateMemory((int)state.NumSealedVTableEntries * IntPtr.Size); + + uint cbSealedVirtualSlotsTypeOffset = pEEType->GetFieldOffset(EETypeField.ETF_SealedVirtualSlots); + *((IntPtr*)((byte*)pEEType + cbSealedVirtualSlotsTypeOffset)) = state.HalfBakedSealedVTable; + + for (ushort i = 0; i < state.NumSealedVTableEntries; i++) + { + IntPtr value = pTemplateEEType->GetSealedVirtualSlot(i); + pEEType->SetSealedVirtualSlot(value, i); + } + } + + if (MethodTable.SupportsWritableData) + { + writableDataPtr = MemoryHelpers.AllocateMemory(WritableData.GetSize(IntPtr.Size)); + MemoryHelpers.Memset(writableDataPtr, WritableData.GetSize(IntPtr.Size), 0); + pEEType->WritableData = writableDataPtr; + } + + // Create a new DispatchMap for the type + if (requiresDynamicDispatchMap) + { + DispatchMap* pTemplateDispatchMap = (DispatchMap*)RuntimeAugments.GetDispatchMapForType(pTemplateEEType->ToRuntimeTypeHandle()); + + dynamicDispatchMapPtr = MemoryHelpers.AllocateMemory(pTemplateDispatchMap->Size); + + uint cbDynamicDispatchMapOffset = pEEType->GetFieldOffset(EETypeField.ETF_DynamicDispatchMap); + *((IntPtr*)((byte*)pEEType + cbDynamicDispatchMapOffset)) = dynamicDispatchMapPtr; + + DispatchMap* pDynamicDispatchMap = (DispatchMap*)dynamicDispatchMapPtr; + pDynamicDispatchMap->NumStandardEntries = pTemplateDispatchMap->NumStandardEntries; + pDynamicDispatchMap->NumDefaultEntries = pTemplateDispatchMap->NumDefaultEntries; + + for (int i = 0; i < pTemplateDispatchMap->NumStandardEntries + pTemplateDispatchMap->NumDefaultEntries; i++) + { + DispatchMap.DispatchMapEntry* pTemplateEntry = (*pTemplateDispatchMap)[i]; + DispatchMap.DispatchMapEntry* pDynamicEntry = (*pDynamicDispatchMap)[i]; + + pDynamicEntry->_usInterfaceIndex = pTemplateEntry->_usInterfaceIndex; + pDynamicEntry->_usInterfaceMethodSlot = pTemplateEntry->_usInterfaceMethodSlot; + if (pTemplateEntry->_usImplMethodSlot < pTemplateEEType->NumVtableSlots) + { + pDynamicEntry->_usImplMethodSlot = (ushort)state.VTableSlotsMapping.GetVTableSlotInTargetType(pTemplateEntry->_usImplMethodSlot); + Debug.Assert(pDynamicEntry->_usImplMethodSlot < numVtableSlots); + } + else + { + // This is an entry in the sealed vtable. We need to adjust the slot number based on the number of vtable slots + // in the dynamic MethodTable + pDynamicEntry->_usImplMethodSlot = (ushort)(pTemplateEntry->_usImplMethodSlot - pTemplateEEType->NumVtableSlots + numVtableSlots); + Debug.Assert(state.NumSealedVTableEntries > 0 && + pDynamicEntry->_usImplMethodSlot >= numVtableSlots && + (pDynamicEntry->_usImplMethodSlot - numVtableSlots) < state.NumSealedVTableEntries); + } + } + } + + if (pTemplateEEType != null) + { + pEEType->DynamicTemplateType = pTemplateEEType; + } + else + { + // Use object as the template type for non-template based EETypes. This will + // allow correct Module identification for types. + pEEType->DynamicTemplateType = typeof(object).TypeHandle.ToEETypePtr(); + } + + int nonGCStaticDataOffset = 0; + + if (!isArray && !isGenericEETypeDef) + { + nonGCStaticDataOffset = state.HasStaticConstructor ? -TypeBuilder.ClassConstructorOffset : 0; + + // create GC desc + if (state.GcDataSize != 0 && state.GcStaticDesc == IntPtr.Zero) + { + if (state.GcStaticEEType != IntPtr.Zero) + { + // CoreRT Abi uses managed heap-allocated GC statics + object obj = RuntimeAugments.NewObject(((MethodTable*)state.GcStaticEEType)->ToRuntimeTypeHandle()); + gcStaticData = RuntimeAugments.RhHandleAlloc(obj, GCHandleType.Normal); + + pEEType->DynamicGcStaticsData = gcStaticData; + } + else + { + int cbStaticGCDesc; + state.GcStaticDesc = CreateStaticGCDesc(state.StaticGCLayout, out state.AllocatedStaticGCDesc, out cbStaticGCDesc); +#if GENERICS_FORCE_USG + TestGCDescsForEquality(state.GcStaticDesc, state.NonUniversalStaticGCDesc, cbStaticGCDesc, false); +#endif + } + } + + if (state.ThreadDataSize != 0 && state.ThreadStaticDesc == IntPtr.Zero) + { + int cbThreadStaticGCDesc; + state.ThreadStaticDesc = CreateStaticGCDesc(state.ThreadStaticGCLayout, out state.AllocatedThreadStaticGCDesc, out cbThreadStaticGCDesc); +#if GENERICS_FORCE_USG + TestGCDescsForEquality(state.ThreadStaticDesc, state.NonUniversalThreadStaticGCDesc, cbThreadStaticGCDesc, false); +#endif + } + + // If we have a class constructor, our NonGcDataSize MUST be non-zero + Debug.Assert(!state.HasStaticConstructor || (state.NonGcDataSize != 0)); + } + + if (isGeneric) + { + genericComposition = MemoryHelpers.AllocateMemory(MethodTable.GetGenericCompositionSize(arity, pEEType->HasGenericVariance)); + pEEType->SetGenericComposition(genericComposition); + + if (state.NonGcDataSize > 0) + { + nonGcStaticData = MemoryHelpers.AllocateMemory(state.NonGcDataSize); + MemoryHelpers.Memset(nonGcStaticData, state.NonGcDataSize, 0); + Debug.Assert(nonGCStaticDataOffset <= state.NonGcDataSize); + pEEType->DynamicNonGcStaticsData = (IntPtr)((byte*)nonGcStaticData + nonGCStaticDataOffset); + } + } + + if (!isGenericEETypeDef && state.ThreadDataSize != 0) + { + // TODO: thread statics + throw new NotSupportedException(); + } + + if (state.Dictionary != null) + state.HalfBakedDictionary = state.Dictionary.Allocate(); + + Debug.Assert(!state.HalfBakedRuntimeTypeHandle.IsNull()); + Debug.Assert((state.NumSealedVTableEntries == 0 && state.HalfBakedSealedVTable == IntPtr.Zero) || (state.NumSealedVTableEntries > 0 && state.HalfBakedSealedVTable != IntPtr.Zero)); + Debug.Assert((state.Dictionary == null && state.HalfBakedDictionary == IntPtr.Zero) || (state.Dictionary != null && state.HalfBakedDictionary != IntPtr.Zero)); + + successful = true; + } + finally + { + if (!successful) + { + if (eeTypePtrPlusGCDesc != IntPtr.Zero) + MemoryHelpers.FreeMemory(eeTypePtrPlusGCDesc); + if (dynamicDispatchMapPtr != IntPtr.Zero) + MemoryHelpers.FreeMemory(dynamicDispatchMapPtr); + if (state.HalfBakedSealedVTable != IntPtr.Zero) + MemoryHelpers.FreeMemory(state.HalfBakedSealedVTable); + if (state.HalfBakedDictionary != IntPtr.Zero) + MemoryHelpers.FreeMemory(state.HalfBakedDictionary); + if (state.AllocatedStaticGCDesc) + MemoryHelpers.FreeMemory(state.GcStaticDesc); + if (state.AllocatedThreadStaticGCDesc) + MemoryHelpers.FreeMemory(state.ThreadStaticDesc); + if (gcStaticData != IntPtr.Zero) + RuntimeAugments.RhHandleFree(gcStaticData); + if (genericComposition != IntPtr.Zero) + MemoryHelpers.FreeMemory(genericComposition); + if (nonGcStaticData != IntPtr.Zero) + MemoryHelpers.FreeMemory(nonGcStaticData); + if (writableDataPtr != IntPtr.Zero) + MemoryHelpers.FreeMemory(writableDataPtr); + } + } + } + + private static IntPtr CreateStaticGCDesc(LowLevelList gcBitfield, out bool allocated, out int cbGCDesc) + { + if (gcBitfield != null) + { + int series = CreateGCDesc(gcBitfield, 0, false, true, null); + if (series > 0) + { + cbGCDesc = sizeof(int) + series * sizeof(int) * 2; + IntPtr result = MemoryHelpers.AllocateMemory(cbGCDesc); + CreateGCDesc(gcBitfield, 0, false, true, (void**)result.ToPointer()); + allocated = true; + return result; + } + } + + allocated = false; + + if (s_emptyGCDesc == IntPtr.Zero) + { + IntPtr ptr = MemoryHelpers.AllocateMemory(8); + + long* gcdesc = (long*)ptr.ToPointer(); + *gcdesc = 0; + + if (Interlocked.CompareExchange(ref s_emptyGCDesc, ptr, IntPtr.Zero) != IntPtr.Zero) + MemoryHelpers.FreeMemory(ptr); + } + + cbGCDesc = IntPtr.Size; + return s_emptyGCDesc; + } + + private static void CreateInstanceGCDesc(TypeBuilderState state, MethodTable* pTemplateEEType, MethodTable* pEEType, int baseSize, int cbGCDesc, bool isValueType, bool isArray, bool isSzArray, int arrayRank) + { + var gcBitfield = state.InstanceGCLayout; + if (isArray) + { + if (cbGCDesc != 0) + { + pEEType->HasGCPointers = true; + if (state.IsArrayOfReferenceTypes) + { + IntPtr* gcDescStart = (IntPtr*)((byte*)pEEType - cbGCDesc); + gcDescStart[0] = new IntPtr(-baseSize); + gcDescStart[1] = new IntPtr(baseSize - sizeof(IntPtr)); + gcDescStart[2] = new IntPtr(1); + } + else + { + CreateArrayGCDesc(gcBitfield, arrayRank, isSzArray, ((void**)pEEType) - 1); + } + } + else + { + pEEType->HasGCPointers = false; + } + } + else if (gcBitfield != null) + { + if (cbGCDesc != 0) + { + pEEType->HasGCPointers = true; + CreateGCDesc(gcBitfield, baseSize, isValueType, false, ((void**)pEEType) - 1); + } + else + { + pEEType->HasGCPointers = false; + } + } + else if (pTemplateEEType != null) + { + Buffer.MemoryCopy((byte*)pTemplateEEType - cbGCDesc, (byte*)pEEType - cbGCDesc, cbGCDesc, cbGCDesc); + pEEType->HasGCPointers = pTemplateEEType->HasGCPointers; + } + else + { + pEEType->HasGCPointers = false; + } + } + + private static unsafe int GetInstanceGCDescSize(TypeBuilderState state, MethodTable* pTemplateEEType, bool isValueType, bool isArray) + { + var gcBitfield = state.InstanceGCLayout; + if (isArray) + { + if (state.IsArrayOfReferenceTypes) + { + // Reference type arrays have a GC desc the size of 3 pointers + return 3 * sizeof(IntPtr); + } + else + { + int series = 0; + if (gcBitfield != null) + series = CreateArrayGCDesc(gcBitfield, 1, true, null); + + return series > 0 ? (series + 2) * IntPtr.Size : 0; + } + } + else if (gcBitfield != null) + { + int series = CreateGCDesc(gcBitfield, 0, isValueType, false, null); + return series > 0 ? (series * 2 + 1) * IntPtr.Size : 0; + } + else if (pTemplateEEType != null) + { + return RuntimeAugments.GetGCDescSize(pTemplateEEType->ToRuntimeTypeHandle()); + } + else + { + return 0; + } + } + + private static unsafe int CreateArrayGCDesc(LowLevelList bitfield, int rank, bool isSzArray, void* gcdesc) + { + if (bitfield == null) + return 0; + + void** baseOffsetPtr = (void**)gcdesc - 1; + +#if TARGET_64BIT + int* ptr = (int*)baseOffsetPtr - 1; +#else + short* ptr = (short*)baseOffsetPtr - 1; +#endif + int baseOffset = 2; + if (!isSzArray) + { + baseOffset += 2 * rank / (sizeof(IntPtr) / sizeof(int)); + } + + int numSeries = 0; + int i = 0; + + bool first = true; + int last = 0; + short numPtrs = 0; + while (i < bitfield.Count) + { + if (bitfield[i]) + { + if (first) + { + baseOffset += i; + first = false; + } + else if (gcdesc != null) + { + *ptr-- = (short)((i - last) * IntPtr.Size); + *ptr-- = numPtrs; + } + + numSeries++; + numPtrs = 0; + + while ((i < bitfield.Count) && (bitfield[i])) + { + numPtrs++; + i++; + } + + last = i; + } + else + { + i++; + } + } + + if (gcdesc != null) + { + if (numSeries > 0) + { + *ptr-- = (short)((bitfield.Count - last + baseOffset - 2) * IntPtr.Size); + *ptr-- = numPtrs; + + *(void**)gcdesc = (void*)-numSeries; + *baseOffsetPtr = (void*)(baseOffset * IntPtr.Size); + } + } + + return numSeries; + } + + private static unsafe int CreateGCDesc(LowLevelList bitfield, int size, bool isValueType, bool isStatic, void* gcdesc) + { + int offs = 0; + // if this type is a class we have to account for the gcdesc. + if (isValueType) + offs = IntPtr.Size; + + if (bitfield == null) + return 0; + + void** ptr = (void**)gcdesc - 1; + + int* staticPtr = isStatic ? ((int*)gcdesc + 1) : null; + + int numSeries = 0; + int i = 0; + while (i < bitfield.Count) + { + if (bitfield[i]) + { + numSeries++; + int seriesOffset = i * IntPtr.Size + offs; + int seriesSize = 0; + + while ((i < bitfield.Count) && (bitfield[i])) + { + seriesSize += IntPtr.Size; + i++; + } + + + if (gcdesc != null) + { + if (staticPtr != null) + { + *staticPtr++ = seriesSize; + *staticPtr++ = seriesOffset; + } + else + { + seriesSize = seriesSize - size; + *ptr-- = (void*)seriesOffset; + *ptr-- = (void*)seriesSize; + } + } + } + else + { + i++; + } + } + + if (gcdesc != null) + { + if (staticPtr != null) + *(int*)gcdesc = numSeries; + else + *(void**)gcdesc = (void*)numSeries; + } + + return numSeries; + } + + [Conditional("GENERICS_FORCE_USG")] + private static unsafe void TestGCDescsForEquality(IntPtr dynamicGCDesc, IntPtr templateGCDesc, int cbGCDesc, bool isInstanceGCDesc) + { + if (templateGCDesc == IntPtr.Zero) + return; + + Debug.Assert(dynamicGCDesc != IntPtr.Zero); + Debug.Assert(cbGCDesc == MemoryHelpers.AlignUp(cbGCDesc, 4)); + + uint* pMem1 = (uint*)dynamicGCDesc.ToPointer(); + uint* pMem2 = (uint*)templateGCDesc.ToPointer(); + bool foundDifferences = false; + + for (int i = 0; i < cbGCDesc; i += 4) + { + if (*pMem1 != *pMem2) + { + // Log all the differences before the assert + Debug.WriteLine("ERROR: GCDesc comparison failed at byte #" + i.LowLevelToString() + " while comparing " + + dynamicGCDesc.LowLevelToString() + " with " + templateGCDesc.LowLevelToString() + + ": [" + (*pMem1).LowLevelToString() + "]/[" + (*pMem2).LowLevelToString() + "]"); + foundDifferences = true; + } + if (isInstanceGCDesc) + { + pMem1--; + pMem2--; + } + else + { + pMem1++; + pMem2++; + } + } + + Debug.Assert(!foundDifferences); + } + + public static RuntimeTypeHandle CreatePointerEEType(uint hashCodeOfNewType, RuntimeTypeHandle pointeeTypeHandle, TypeDesc pointerType) + { + TypeBuilderState state = new TypeBuilderState(pointerType); + + CreateEETypeWorker(typeof(void*).TypeHandle.ToEETypePtr(), hashCodeOfNewType, 0, false, state); + Debug.Assert(!state.HalfBakedRuntimeTypeHandle.IsNull()); + + TypeLoaderLogger.WriteLine("Allocated new POINTER type " + pointerType.ToString() + " with hashcode value = 0x" + hashCodeOfNewType.LowLevelToString() + " with MethodTable = " + state.HalfBakedRuntimeTypeHandle.ToIntPtr().LowLevelToString()); + + state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->RelatedParameterType = pointeeTypeHandle.ToEETypePtr(); + + return state.HalfBakedRuntimeTypeHandle; + } + + public static RuntimeTypeHandle CreateByRefEEType(uint hashCodeOfNewType, RuntimeTypeHandle pointeeTypeHandle, TypeDesc byRefType) + { + TypeBuilderState state = new TypeBuilderState(byRefType); + + // ByRef and pointer types look similar enough that we can use void* as a template. + // Ideally this should be typeof(void&) but C# doesn't support that syntax. We adjust for this below. + CreateEETypeWorker(typeof(void*).TypeHandle.ToEETypePtr(), hashCodeOfNewType, 0, false, state); + Debug.Assert(!state.HalfBakedRuntimeTypeHandle.IsNull()); + + TypeLoaderLogger.WriteLine("Allocated new BYREF type " + byRefType.ToString() + " with hashcode value = 0x" + hashCodeOfNewType.LowLevelToString() + " with MethodTable = " + state.HalfBakedRuntimeTypeHandle.ToIntPtr().LowLevelToString()); + + state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->RelatedParameterType = pointeeTypeHandle.ToEETypePtr(); + + // We used a pointer as a template. We need to make this a byref. + Debug.Assert(state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->ElementType == EETypeElementType.Pointer); + state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->Flags = EETypeBuilderHelpers.ComputeFlags(byRefType); + Debug.Assert(state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->ElementType == EETypeElementType.ByRef); + Debug.Assert(state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->ParameterizedTypeShape == ParameterizedTypeShapeConstants.Pointer); + state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->ParameterizedTypeShape = ParameterizedTypeShapeConstants.ByRef; + + return state.HalfBakedRuntimeTypeHandle; + } + + public static RuntimeTypeHandle CreateEEType(TypeDesc type, TypeBuilderState state) + { + Debug.Assert(type != null && state != null); + + MethodTable* pTemplateEEType = null; + bool requireVtableSlotMapping = false; + + if (type is PointerType || type is ByRefType) + { + Debug.Assert(0 == state.NonGcDataSize); + Debug.Assert(false == state.HasStaticConstructor); + Debug.Assert(0 == state.GcDataSize); + Debug.Assert(0 == state.ThreadStaticOffset); + Debug.Assert(0 == state.NumSealedVTableEntries); + Debug.Assert(IntPtr.Zero == state.GcStaticDesc); + Debug.Assert(IntPtr.Zero == state.ThreadStaticDesc); + + // Pointers and ByRefs only differ by the ParameterizedTypeShape and ElementType value. + RuntimeTypeHandle templateTypeHandle = typeof(void*).TypeHandle; + + pTemplateEEType = templateTypeHandle.ToEETypePtr(); + } + else if ((type is MetadataType) && (state.TemplateType == null || !state.TemplateType.RetrieveRuntimeTypeHandleIfPossible())) + { + requireVtableSlotMapping = true; + pTemplateEEType = null; + } + else if (type.IsMdArray || (type.IsSzArray && ((ArrayType)type).ElementType.IsPointer)) + { + // Multidimensional arrays and szarrays of pointers don't implement generic interfaces and + // we don't need to do much for them in terms of type building. We can pretty much just take + // the MethodTable for any of those, massage the bits that matter (GCDesc, element type, + // component size,...) to be of the right shape and we're done. + pTemplateEEType = typeof(object[,]).TypeHandle.ToEETypePtr(); + requireVtableSlotMapping = false; + } + else + { + Debug.Assert(state.TemplateType != null && !state.TemplateType.RuntimeTypeHandle.IsNull()); + requireVtableSlotMapping = state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal); + RuntimeTypeHandle templateTypeHandle = state.TemplateType.RuntimeTypeHandle; + pTemplateEEType = templateTypeHandle.ToEETypePtr(); + } + + DefType typeAsDefType = type as DefType; + // Use a checked typecast to 'ushort' for the arity to ensure its value never exceeds 65535 and cause integer + // overflows later when computing size of memory blocks to allocate for the type and its GenericInstanceDescriptor structures + int arity = checked((ushort)((typeAsDefType != null && typeAsDefType.HasInstantiation ? typeAsDefType.Instantiation.Length : 0))); + + CreateEETypeWorker(pTemplateEEType, (uint)type.GetHashCode(), arity, requireVtableSlotMapping, state); + + return state.HalfBakedRuntimeTypeHandle; + } + + public static int GetDictionaryOffsetInEEtype(MethodTable* pEEType) + { + // Dictionary slot is the first vtable slot + + MethodTable* pBaseType = pEEType->BaseType; + int dictionarySlot = (pBaseType == null ? 0 : pBaseType->NumVtableSlots); + return sizeof(MethodTable) + dictionarySlot * IntPtr.Size; + } + + public static IntPtr GetDictionaryAtOffset(MethodTable* pEEType, int offset) + { + return *(IntPtr*)((byte*)pEEType + offset); + } + + public static IntPtr GetDictionary(MethodTable* pEEType) + { + return GetDictionaryAtOffset(pEEType, GetDictionaryOffsetInEEtype(pEEType)); + } + + public static int GetDictionarySlotInVTable(TypeDesc type) + { + if (!type.CanShareNormalGenericCode()) + return -1; + + // Dictionary slot is the first slot in the vtable after the base type's vtable entries + return type.BaseType != null ? type.BaseType.GetOrCreateTypeBuilderState().NumVTableSlots : 0; + } + + private static int GetMostDerivedDictionarySlot(ref TypeDesc nextTypeToExamineForDictionarySlot, out TypeDesc typeWithDictionary) + { + while (nextTypeToExamineForDictionarySlot != null) + { + if (nextTypeToExamineForDictionarySlot.GetOrCreateTypeBuilderState().HasDictionarySlotInVTable) + { + typeWithDictionary = nextTypeToExamineForDictionarySlot; + nextTypeToExamineForDictionarySlot = nextTypeToExamineForDictionarySlot.BaseType; + return GetDictionarySlotInVTable(typeWithDictionary); + } + + nextTypeToExamineForDictionarySlot = nextTypeToExamineForDictionarySlot.BaseType; + } + + typeWithDictionary = null; + return -1; + } + + public static MethodTable* GetBaseEETypeForDictionaryPtr(MethodTable* pEEType, IntPtr dictionaryPtr) + { + // Look for the exact base type that owns the dictionary + IntPtr curDictPtr = GetDictionary(pEEType); + MethodTable* pBaseEEType = pEEType; + + while (curDictPtr != dictionaryPtr) + { + pBaseEEType = pBaseEEType->BaseType; + Debug.Assert(pBaseEEType != null); + // Since in multifile scenario, the base type's dictionary may end up having + // a copy in each module, therefore the lookup of the right base type should be + // based on the dictionary pointer in the current MethodTable, instead of the base MethodTable. + curDictPtr = GetDictionaryAtOffset(pEEType, EETypeCreator.GetDictionaryOffsetInEEtype(pBaseEEType)); + } + + return pBaseEEType; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/Empty.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/Empty.cs new file mode 100644 index 00000000000000..3794f19975b7e6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/Empty.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; + +namespace System.Collections.Generic +{ + // + // Helper class to store reusable empty arrays. We cannot use the public Array.GetEmpty() in the type loader because of + // recursive dictionary lookups. + // + [System.Runtime.CompilerServices.ForceDictionaryLookups] + internal static class Empty + { + // + // Returns a reusable empty array. + // + public static readonly T[] Array = new T[0]; + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ExternalReferencesTable.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ExternalReferencesTable.cs new file mode 100644 index 00000000000000..33f3edc5d3a906 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ExternalReferencesTable.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Runtime.Augments; + +namespace Internal.Runtime.TypeLoader +{ + public struct ExternalReferencesTable + { + private IntPtr _elements; + private uint _elementsCount; + private TypeManagerHandle _moduleHandle; + + public bool IsInitialized() { return !_moduleHandle.IsNull; } + + private unsafe bool Initialize(NativeFormatModuleInfo module, ReflectionMapBlob blobId) + { + _moduleHandle = module.Handle; + + byte* pBlob; + uint cbBlob; + if (!module.TryFindBlob(blobId, out pBlob, out cbBlob)) + { + _elements = IntPtr.Zero; + _elementsCount = 0; + return false; + } + + _elements = (IntPtr)pBlob; + _elementsCount = (uint)(cbBlob / sizeof(uint)); + + return true; + } + + /// + /// Initialize ExternalReferencesTable using the NativeReferences metadata blob on a given module. + /// + /// Module handle is used to locate the NativeReferences blob + /// true when the NativeReferences blob was found in the given module, false when not + public bool InitializeNativeReferences(NativeFormatModuleInfo module) + { + return Initialize(module, ReflectionMapBlob.NativeReferences); + } + + /// + /// Initialize ExternalReferencesTable using the NativeStatics metadata blob on a given module. + /// + /// Module handle is used to locate the NativeStatics blob + /// true when the NativeStatics blob was found in the given module, false when not + public bool InitializeNativeStatics(NativeFormatModuleInfo module) + { + return Initialize(module, ReflectionMapBlob.NativeStatics); + } + + /// + /// Initialize ExternalReferencesTable using the CommonFixupsTable metadata blob on a given module. + /// + /// Module handle is used to locate the CommonFixupsTable blob + /// true when the CommonFixupsTable blob was found in the given module, false when not + public bool InitializeCommonFixupsTable(NativeFormatModuleInfo module) + { + return Initialize(module, ReflectionMapBlob.CommonFixupsTable); + } + + public unsafe uint GetRvaFromIndex(uint index) + { + // The usage of this API will need to go away since this is not fully portable + // and we'll not be able to support this for CppCodegen. + throw new PlatformNotSupportedException(); + } + + public unsafe IntPtr GetIntPtrFromIndex(uint index) + { + return GetAddressFromIndex(index); + } + + public unsafe IntPtr GetFunctionPointerFromIndex(uint index) + { + return GetAddressFromIndex(index); + } + + public RuntimeTypeHandle GetRuntimeTypeHandleFromIndex(uint index) + { + return RuntimeAugments.CreateRuntimeTypeHandle(GetIntPtrFromIndex(index)); + } + + public IntPtr GetGenericDictionaryFromIndex(uint index) + { + return GetIntPtrFromIndex(index); + } + + public unsafe IntPtr GetAddressFromIndex(uint index) + { + if (index >= _elementsCount) + throw new BadImageFormatException(); + + // TODO: indirection through IAT + if (MethodTable.SupportsRelativePointers) + { + int* pRelPtr32 = &((int*)_elements)[index]; + return (IntPtr)((byte*)pRelPtr32 + *pRelPtr32); + } + + return (IntPtr)(((void**)_elements)[index]); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/FixupCellMetadataResolver.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/FixupCellMetadataResolver.cs new file mode 100644 index 00000000000000..91dcb278586c72 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/FixupCellMetadataResolver.cs @@ -0,0 +1,125 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Runtime.Augments; +using Internal.Runtime.TypeLoader; +using Internal.Runtime.CompilerServices; + +using Internal.TypeSystem; +using Internal.TypeSystem.NativeFormat; + +namespace Internal.Runtime.TypeLoader +{ +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + /// + /// Resolver structure for that can be used to resolve tokens in a given context + /// + internal struct FixupCellMetadataResolver + { + public FixupCellMetadataResolver(NativeFormatMetadataUnit metadataUnit) + { + _metadataUnit = metadataUnit; + _typeContext = null; + _methodContext = null; + _loadContextFromNativeLayout = null; + } + + public FixupCellMetadataResolver(NativeFormatMetadataUnit metadataUnit, TypeDesc typeContext) + { + _metadataUnit = metadataUnit; + _typeContext = typeContext; + _methodContext = null; + _loadContextFromNativeLayout = null; + } + + public FixupCellMetadataResolver(NativeFormatMetadataUnit metadataUnit, MethodDesc methodContext) + { + _metadataUnit = metadataUnit; + _methodContext = methodContext; + _typeContext = methodContext.OwningType; + _loadContextFromNativeLayout = null; + } + + public FixupCellMetadataResolver(NativeFormatMetadataUnit metadataUnit, NativeLayoutInfoLoadContext loadContext) + { + _metadataUnit = metadataUnit; + _methodContext = null; + _typeContext = null; + _loadContextFromNativeLayout = loadContext; + } + + private NativeFormatMetadataUnit _metadataUnit; + private TypeDesc _typeContext; + private MethodDesc _methodContext; + private NativeLayoutInfoLoadContext _loadContextFromNativeLayout; + + public TypeDesc GetType(Internal.Metadata.NativeFormat.Handle token) + { + TypeDesc type = _metadataUnit.GetType(token); + TypeDesc instantiatedType = type.InstantiateSignature(TypeInstantiation, MethodInstantiation); + return instantiatedType; + } + + public MethodDesc GetMethod(Internal.Metadata.NativeFormat.Handle token) + { + MethodDesc method = _metadataUnit.GetMethod(token, null); + MethodDesc instantiatedMethod = method.InstantiateSignature(TypeInstantiation, MethodInstantiation); + return instantiatedMethod; + } + + public FieldDesc GetField(Internal.Metadata.NativeFormat.Handle token) + { + FieldDesc field = _metadataUnit.GetField(token, null); + FieldDesc instantiatedField = field.InstantiateSignature(TypeInstantiation, MethodInstantiation); + return instantiatedField; + } + + public RuntimeSignature GetSignature(Internal.Metadata.NativeFormat.Handle token) + { + switch (token.HandleType) + { + // These are the only valid token types for creating a method signature + case Internal.Metadata.NativeFormat.HandleType.Method: + case Internal.Metadata.NativeFormat.HandleType.MemberReference: + case Internal.Metadata.NativeFormat.HandleType.QualifiedMethod: + case Internal.Metadata.NativeFormat.HandleType.MethodInstantiation: + case Internal.Metadata.NativeFormat.HandleType.MethodSignature: + break; + + default: + Environment.FailFast("Unknown and invalid handle type"); + break; + } + return RuntimeSignature.CreateFromMethodHandle(_metadataUnit.RuntimeModule, token.ToInt()); + } + + public Instantiation TypeInstantiation + { + get + { + if (_loadContextFromNativeLayout != null) + return _loadContextFromNativeLayout._typeArgumentHandles; + else + return _typeContext?.Instantiation ?? Instantiation.Empty; + } + } + + public Instantiation MethodInstantiation + { + get + { + if (_loadContextFromNativeLayout != null) + return _loadContextFromNativeLayout._methodArgumentHandles; + else + return _methodContext?.Instantiation ?? Instantiation.Empty; + } + } + } +#endif +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionary.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionary.cs new file mode 100644 index 00000000000000..eed0b97d01e920 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionary.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using Internal.Runtime.Augments; +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime.TypeLoader +{ + internal abstract class GenericDictionary + { + protected GenericDictionaryCell[] _cells; + protected IntPtr _addressOfFirstCellSlot; + + public GenericDictionary(GenericDictionaryCell[] cells) + { + Debug.Assert(cells != null); + _cells = cells; + } + + public abstract IntPtr Allocate(); + + public unsafe void Finish(TypeBuilder typeBuilder) + { + Debug.Assert(_cells.Length == 0 || _addressOfFirstCellSlot != IntPtr.Zero); + + IntPtr* realCells = (IntPtr*)_addressOfFirstCellSlot; + for (int i = 0; i < _cells.Length; i++) + { + _cells[i].WriteCellIntoDictionary(typeBuilder, realCells, i); + } + } + } + + internal class GenericTypeDictionary : GenericDictionary + { + public GenericTypeDictionary(GenericDictionaryCell[] cells) + : base(cells) + { } + + public override IntPtr Allocate() + { + Debug.Assert(_addressOfFirstCellSlot == IntPtr.Zero); + + if (_cells.Length > 0) + { + // Use checked typecast to int to ensure there aren't any overflows/truncations + _addressOfFirstCellSlot = MemoryHelpers.AllocateMemory(checked((int)(_cells.Length * IntPtr.Size))); + } + + return _addressOfFirstCellSlot; + } + } + + internal class GenericMethodDictionary : GenericDictionary + { + public GenericMethodDictionary(GenericDictionaryCell[] cells) + : base(cells) + { } + + public unsafe override IntPtr Allocate() + { + Debug.Assert(_addressOfFirstCellSlot == IntPtr.Zero); + + // Method dictionaries start with a header containing the hash code, which is not part of the native layout. + // The real first slot is located after the header. + // Use checked typecast to int to ensure there aren't any overflows/truncations + IntPtr dictionaryWithHeader = MemoryHelpers.AllocateMemory(checked((int)((_cells.Length + 1) * IntPtr.Size))); + + // Put a magic hash code to indicate dynamically allocated method dictionary for + // debugging purposes. + *(int*)dictionaryWithHeader = 0xD1CC0DE; // DICCODE + + _addressOfFirstCellSlot = IntPtr.Add(dictionaryWithHeader, IntPtr.Size); + + return _addressOfFirstCellSlot; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs new file mode 100644 index 00000000000000..e76c189796bb70 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs @@ -0,0 +1,1824 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection.Runtime.General; + +using Internal.Runtime.Augments; +using Internal.Runtime.TypeLoader; +using Internal.Runtime.CompilerServices; + +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.TypeSystem.NativeFormat; +using Internal.TypeSystem.NoMetadata; + +namespace Internal.Runtime.TypeLoader +{ + public abstract class GenericDictionaryCell + { + internal abstract void Prepare(TypeBuilder builder); + internal abstract IntPtr Create(TypeBuilder builder); + internal unsafe virtual void WriteCellIntoDictionary(TypeBuilder typeBuilder, IntPtr* pDictionary, int slotIndex) + { + pDictionary[slotIndex] = Create(typeBuilder); + } + + internal virtual IntPtr CreateLazyLookupCell(TypeBuilder builder, out IntPtr auxResult) + { + auxResult = IntPtr.Zero; + return Create(builder); + } + + // Helper method for nullable transform. Ideally, we would do the nullable transform upfront before + // the types is build. Unfortunately, there does not seem to be easy way to test for Nullable<> type definition + // without introducing type builder recursion + private static RuntimeTypeHandle GetRuntimeTypeHandleWithNullableTransform(TypeBuilder builder, TypeDesc type) + { + RuntimeTypeHandle th = builder.GetRuntimeTypeHandle(type); + if (RuntimeAugments.IsNullable(th)) + th = builder.GetRuntimeTypeHandle(((DefType)type).Instantiation[0]); + return th; + } + + private class PointerToOtherDictionarySlotCell : GenericDictionaryCell + { + internal uint OtherDictionarySlot; + + internal override void Prepare(TypeBuilder builder) { } + internal override IntPtr Create(TypeBuilder builder) + { + // This api should never be called. The intention is that this cell is special + // cased to have a value which is relative to other cells being emitted. + throw new NotSupportedException(); + } + + internal unsafe override void WriteCellIntoDictionary(TypeBuilder typeBuilder, IntPtr* pDictionary, int slotIndex) + { + pDictionary[slotIndex] = new IntPtr(pDictionary + OtherDictionarySlot); + } + } + + public static GenericDictionaryCell CreateTypeHandleCell(TypeDesc type) + { + TypeHandleCell typeCell = new TypeHandleCell(); + typeCell.Type = type; + return typeCell; + } + + private class TypeHandleCell : GenericDictionaryCell + { + internal TypeDesc Type; + + internal override void Prepare(TypeBuilder builder) + { + if (Type.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Canonical types do not have EETypes"); + + builder.RegisterForPreparation(Type); + } + + internal override IntPtr Create(TypeBuilder builder) + { + return builder.GetRuntimeTypeHandle(Type).ToIntPtr(); + } + } + + private class UnwrapNullableTypeCell : GenericDictionaryCell + { + internal DefType Type; + + internal override void Prepare(TypeBuilder builder) + { + if (Type.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Canonical types do not have EETypes"); + + if (Type.IsNullable) + { + Debug.Assert(Type.Instantiation.Length == 1); + builder.RegisterForPreparation(Type.Instantiation[0]); + } + else + builder.RegisterForPreparation(Type); + } + + internal override IntPtr Create(TypeBuilder builder) + { + if (Type.IsNullable) + return builder.GetRuntimeTypeHandle(Type.Instantiation[0]).ToIntPtr(); + else + return builder.GetRuntimeTypeHandle(Type).ToIntPtr(); + } + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + public static GenericDictionaryCell CreateInterfaceCallCell(TypeDesc interfaceType, int slot) + { + InterfaceCallCell dispatchCell = new InterfaceCallCell(); + dispatchCell.InterfaceType = interfaceType; + dispatchCell.Slot = slot; + return dispatchCell; + } +#endif + + private class InterfaceCallCell : GenericDictionaryCell + { + internal TypeDesc InterfaceType; + internal int Slot; + + internal override void Prepare(TypeBuilder builder) + { + if (InterfaceType.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Unable to compute call information for a canonical interface"); + + builder.RegisterForPreparation(InterfaceType); + } + + internal override IntPtr Create(TypeBuilder builder) + { + return RuntimeAugments.NewInterfaceDispatchCell(builder.GetRuntimeTypeHandle(InterfaceType), Slot); + } + } + +#if FEATURE_UNIVERSAL_GENERICS + /// + /// Used for non-generic Direct Call Constrained Methods + /// + private class NonGenericDirectConstrainedMethodCell : GenericDictionaryCell + { + internal TypeDesc ConstraintType; + internal TypeDesc ConstrainedMethodType; + internal int ConstrainedMethodSlot; + + internal override void Prepare(TypeBuilder builder) + { + if (ConstraintType.IsCanonicalSubtype(CanonicalFormKind.Any) || ConstrainedMethodType.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Unable to compute call information for a canonical type/method."); + + builder.RegisterForPreparation(ConstraintType); + builder.RegisterForPreparation(ConstrainedMethodType); + } + + internal override IntPtr Create(TypeBuilder builder) + { + return ConstrainedCallSupport.NonGenericConstrainedCallDesc.GetDirectConstrainedCallPtr(builder.GetRuntimeTypeHandle(ConstraintType), + builder.GetRuntimeTypeHandle(ConstrainedMethodType), + ConstrainedMethodSlot); + } + } + + /// + /// Used for non-generic Constrained Methods + /// + private class NonGenericConstrainedMethodCell : GenericDictionaryCell + { + internal TypeDesc ConstraintType; + internal TypeDesc ConstrainedMethodType; + internal int ConstrainedMethodSlot; + + internal override void Prepare(TypeBuilder builder) + { + if (ConstraintType.IsCanonicalSubtype(CanonicalFormKind.Any) || ConstrainedMethodType.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Unable to compute call information for a canonical type/method."); + + builder.RegisterForPreparation(ConstraintType); + builder.RegisterForPreparation(ConstrainedMethodType); + } + + internal override IntPtr Create(TypeBuilder builder) + { + return ConstrainedCallSupport.NonGenericConstrainedCallDesc.Get(builder.GetRuntimeTypeHandle(ConstraintType), + builder.GetRuntimeTypeHandle(ConstrainedMethodType), + ConstrainedMethodSlot); + } + } + + /// + /// Used for generic Constrained Methods + /// + private class GenericConstrainedMethodCell : GenericDictionaryCell + { + internal TypeDesc ConstraintType; + internal MethodDesc ConstrainedMethod; + internal IntPtr MethodName; + internal RuntimeSignature MethodSignature; + + internal override void Prepare(TypeBuilder builder) + { + if (ConstraintType.IsCanonicalSubtype(CanonicalFormKind.Any) || ConstrainedMethod.IsCanonicalMethod(CanonicalFormKind.Any)) + Environment.FailFast("Unable to compute call information for a canonical type/method."); + + builder.RegisterForPreparation(ConstraintType); + // Do not use builder.PrepareMethod here. That + // would prepare the dictionary for the method, + // and if the method is abstract, there is no + // dictionary. Also, the dictionary is not necessary + // to create the ldtoken. + builder.RegisterForPreparation(ConstrainedMethod.OwningType); + foreach (var type in ConstrainedMethod.Instantiation) + builder.RegisterForPreparation(type); + } + + internal override IntPtr Create(TypeBuilder builder) + { + RuntimeTypeHandle[] genericArgHandles = ConstrainedMethod.HasInstantiation ? + builder.GetRuntimeTypeHandles(ConstrainedMethod.Instantiation) : null; + + RuntimeMethodHandle rmh = TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents( + builder.GetRuntimeTypeHandle(ConstrainedMethod.OwningType), + MethodName, + MethodSignature, + genericArgHandles); + + return ConstrainedCallSupport.GenericConstrainedCallDesc.Get(builder.GetRuntimeTypeHandle(ConstraintType), rmh); + } + } +#endif + + private class StaticDataCell : GenericDictionaryCell + { + internal StaticDataKind DataKind; + internal TypeDesc Type; + + internal override void Prepare(TypeBuilder builder) + { + if (Type.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Unable to compute static field locations for a canonical type."); + + builder.RegisterForPreparation(Type); + } + + internal override IntPtr Create(TypeBuilder builder) + { + RuntimeTypeHandle typeHandle = builder.GetRuntimeTypeHandle(Type); + switch (DataKind) + { + case StaticDataKind.NonGc: + return TypeLoaderEnvironment.Instance.TryGetNonGcStaticFieldData(typeHandle); + + case StaticDataKind.Gc: + return TypeLoaderEnvironment.Instance.TryGetGcStaticFieldData(typeHandle); + + default: + Debug.Assert(false); + return IntPtr.Zero; + } + } + + internal override unsafe IntPtr CreateLazyLookupCell(TypeBuilder builder, out IntPtr auxResult) + { + auxResult = IntPtr.Zero; + return *(IntPtr*)Create(builder); + } + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + public static GenericDictionaryCell CreateMethodDictionaryCell(InstantiatedMethod method) + { + MethodDictionaryCell methodCell = new MethodDictionaryCell(); + methodCell.GenericMethod = method; + return methodCell; + } +#endif + + private class MethodDictionaryCell : GenericDictionaryCell + { + internal InstantiatedMethod GenericMethod; + + internal unsafe override void Prepare(TypeBuilder builder) + { + if (GenericMethod.IsCanonicalMethod(CanonicalFormKind.Any)) + Environment.FailFast("Method dictionaries of canonical methods do not exist"); + + builder.PrepareMethod(GenericMethod); + } + + internal override IntPtr Create(TypeBuilder builder) + { + // TODO (USG): What if this method's instantiation is a non-shareable one (from a normal canonical + // perspective) and there's an exact method pointer for the method in question, do we still + // construct a method dictionary to be used with the universal canonical method implementation? + Debug.Assert(GenericMethod.RuntimeMethodDictionary != IntPtr.Zero); + return GenericMethod.RuntimeMethodDictionary; + } + } + + private class FieldLdTokenCell : GenericDictionaryCell + { + internal TypeDesc ContainingType; + internal IntPtr FieldName; + + internal unsafe override void Prepare(TypeBuilder builder) + { + if (ContainingType.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Ldtoken is not permitted for a canonical field"); + + builder.RegisterForPreparation(ContainingType); + } + + internal override unsafe IntPtr Create(TypeBuilder builder) + { + RuntimeFieldHandle handle = TypeLoaderEnvironment.Instance.GetRuntimeFieldHandleForComponents( + builder.GetRuntimeTypeHandle(ContainingType), + FieldName); + + return *(IntPtr*)&handle; + } + } + + private class MethodLdTokenCell : GenericDictionaryCell + { + internal MethodDesc Method; + internal IntPtr MethodName; + internal RuntimeSignature MethodSignature; + + internal unsafe override void Prepare(TypeBuilder builder) + { + if (Method.IsCanonicalMethod(CanonicalFormKind.Any)) + Environment.FailFast("Ldtoken is not permitted for a canonical method"); + + // Do not use builder.PrepareMethod here. That + // would prepare the dictionary for the method, + // and if the method is abstract, there is no + // dictionary. Also, the dictionary is not necessary + // to create the ldtoken. + builder.RegisterForPreparation(Method.OwningType); + foreach (var type in Method.Instantiation) + builder.RegisterForPreparation(type); + } + + internal override unsafe IntPtr Create(TypeBuilder builder) + { + RuntimeTypeHandle[] genericArgHandles = Method.HasInstantiation && !Method.IsMethodDefinition ? + builder.GetRuntimeTypeHandles(Method.Instantiation) : null; + + RuntimeMethodHandle handle = TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents( + builder.GetRuntimeTypeHandle(Method.OwningType), + MethodName, + MethodSignature, + genericArgHandles); + + return *(IntPtr*)&handle; + } + } + +#if FEATURE_UNIVERSAL_GENERICS + private class TypeSizeCell : GenericDictionaryCell + { + internal TypeDesc Type; + + internal override void Prepare(TypeBuilder builder) + { + if (Type.IsCanonicalSubtype(CanonicalFormKind.Universal)) + Environment.FailFast("Universal shared generics do not have a defined size"); + + builder.RegisterForPreparation(Type); + } + + internal override IntPtr Create(TypeBuilder builder) + { + if (Type.IsValueType) + return (IntPtr)RuntimeAugments.GetValueTypeSize(builder.GetRuntimeTypeHandle(Type)); + else + return (IntPtr)IntPtr.Size; + } + } +#endif + +#if FEATURE_UNIVERSAL_GENERICS + private class FieldOffsetCell : GenericDictionaryCell + { + internal DefType ContainingType; + internal uint Ordinal; +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + internal FieldDesc Field; +#endif + internal int Offset; + + internal unsafe override void Prepare(TypeBuilder builder) + { + if (ContainingType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + Environment.FailFast("Universal shared generics do not have a defined size"); + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + if (Field != null) + Offset = Field.Offset.AsInt; + else +#endif + Offset = ContainingType.GetFieldByNativeLayoutOrdinal(Ordinal).Offset.AsInt; + } + + internal override unsafe IntPtr Create(TypeBuilder builder) + { + return (IntPtr)Offset; + } + } + + private class VTableOffsetCell : GenericDictionaryCell + { + internal TypeDesc ContainingType; + internal uint VTableSlot; + private TypeDesc ContainingTypeTemplate; + + internal unsafe override void Prepare(TypeBuilder builder) + { + builder.RegisterForPreparation(ContainingType); + ContainingTypeTemplate = ContainingType.ComputeTemplate(); + } + + // + // This helper function will traverse the hierarchy of the containing type of this vtable offset cell, in parallel with + // the hierarchy of its template type. + // When traversing the template type hierarchy, we have 2 possibilities for the base type: + // - Fully universal canonical. USG types always have a dictionary slot, so if the dynamically created type does not share + // normal canonical code, we subtract 1 from the vtable offset (the dynamic type does not have a dictionary slot in that case) + // - Exact non-canonical type. In that case, we do not need to make any changes to the vtable offset (the binder/ILCompiler + // would have written the correct vtable offset, taking in the account the existance or non-existance of a dictionary slot. + // + private void AdjustVtableSlot(TypeDesc currentType, TypeDesc currentTemplateType, ref int vtableSlot) + { + TypeDesc baseType = currentType.BaseType; + TypeDesc baseTemplateType = TypeBuilder.GetBaseTypeUsingRuntimeTypeHandle(currentTemplateType); + + Debug.Assert((baseType == null && baseTemplateType == null) || (baseType != null && baseTemplateType != null)); + + // Compute the vtable layout for the current type starting with base types first + if (baseType != null) + AdjustVtableSlot(baseType, baseTemplateType, ref vtableSlot); + + if (currentType.IsGeneric()) + { + if (!currentType.CanShareNormalGenericCode() && currentTemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + vtableSlot--; + } + } + + internal override unsafe IntPtr Create(TypeBuilder builder) + { + // Debug sanity check for the size of the MethodTable structure + // just to ensure nothing of it gets reduced + Debug.Assert(sizeof(MethodTable) == (IntPtr.Size == 8 ? 24 : 20)); + + int result = (int)VTableSlot; + + // Check if the current type can share code with normal canonical + // generic types. If not, then the vtable layout will not have a + // slot for a dictionary pointer, and we need to adjust the slot number + AdjustVtableSlot(ContainingType, ContainingTypeTemplate, ref result); + Debug.Assert(result >= 0); + + return (IntPtr)(sizeof(MethodTable) + result * IntPtr.Size); + } + } +#endif + + private class AllocateObjectCell : GenericDictionaryCell + { + internal TypeDesc Type; + + internal override void Prepare(TypeBuilder builder) + { + if (Type.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Canonical types cannot be allocated"); + + builder.RegisterForPreparation(Type); + } + + internal override IntPtr Create(TypeBuilder builder) + { + RuntimeTypeHandle th = GetRuntimeTypeHandleWithNullableTransform(builder, Type); + return RuntimeAugments.GetAllocateObjectHelperForType(th); + } + + internal override unsafe IntPtr CreateLazyLookupCell(TypeBuilder builder, out IntPtr auxResult) + { + RuntimeTypeHandle th = GetRuntimeTypeHandleWithNullableTransform(builder, Type); + auxResult = th.ToIntPtr(); + return RuntimeAugments.GetAllocateObjectHelperForType(th); + } + } + + private class DefaultConstructorCell : GenericDictionaryCell + { + internal TypeDesc Type; + + internal override void Prepare(TypeBuilder builder) + { + builder.RegisterForPreparation(Type); + } + + internal override IntPtr Create(TypeBuilder builder) + { + IntPtr result = TypeLoaderEnvironment.Instance.TryGetDefaultConstructorForType(Type); + + + if (result == IntPtr.Zero) + result = RuntimeAugments.GetFallbackDefaultConstructor(); + return result; + } + } + + public static GenericDictionaryCell CreateIntPtrCell(IntPtr ptrValue) + { + IntPtrCell typeCell = new IntPtrCell(); + typeCell.Value = ptrValue; + return typeCell; + } + + private class IntPtrCell : GenericDictionaryCell + { + internal IntPtr Value; + internal unsafe override void Prepare(TypeBuilder builder) + { + } + + internal unsafe override IntPtr Create(TypeBuilder builder) + { + return Value; + } + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + public static GenericDictionaryCell CreateExactCallableMethodCell(MethodDesc method) + { + MethodCell methodCell = new MethodCell(); + methodCell.Method = method; + if (!RuntimeSignatureHelper.TryCreate(method, out methodCell.MethodSignature)) + { + Environment.FailFast("Unable to create method signature, for method reloc"); + } + methodCell.ExactCallableAddressNeeded = true; + return methodCell; + } +#endif + + private class MethodCell : GenericDictionaryCell + { + internal MethodDesc Method; + internal RuntimeSignature MethodSignature; +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + internal bool ExactCallableAddressNeeded; +#endif + private bool _universalCanonImplementationOfCanonMethod; + private MethodDesc _methodToUseForInstantiatingParameters; + private IntPtr _exactFunctionPointer; + + internal unsafe override void Prepare(TypeBuilder builder) + { + _methodToUseForInstantiatingParameters = Method; + + IntPtr exactFunctionPointer; + + bool canUseRetrieveExactFunctionPointerIfPossible = false; + + // RetrieveExactFunctionPointerIfPossible always gets the unboxing stub if possible + if (Method.UnboxingStub) + canUseRetrieveExactFunctionPointerIfPossible = true; + else if (!Method.OwningType.IsValueType) // If the owning type isn't a valuetype, concerns about unboxing stubs are moot + canUseRetrieveExactFunctionPointerIfPossible = true; + else if (TypeLoaderEnvironment.Instance.IsStaticMethodSignature(MethodSignature)) // Static methods don't have unboxing stub concerns + canUseRetrieveExactFunctionPointerIfPossible = true; + + if (canUseRetrieveExactFunctionPointerIfPossible && + builder.RetrieveExactFunctionPointerIfPossible(Method, out exactFunctionPointer)) + { + // If we succeed in finding a non-shareable function pointer for this method, it means + // that we found a method body for it that was statically compiled. We'll use that body + // instead of the universal canonical method pointer + Debug.Assert(exactFunctionPointer != IntPtr.Zero && + exactFunctionPointer != Method.FunctionPointer && + exactFunctionPointer != Method.UsgFunctionPointer); + + _exactFunctionPointer = exactFunctionPointer; + } + else + { + // There is no exact function pointer available. This means that we'll have to + // build a method dictionary for the method instantiation, and use the shared canonical + // function pointer that was parsed from native layout. + _exactFunctionPointer = IntPtr.Zero; + builder.PrepareMethod(Method); + + // Check whether we have already resolved a canonical or universal match + IntPtr addressToUse; + TypeLoaderEnvironment.MethodAddressType foundAddressType; + if (Method.FunctionPointer != IntPtr.Zero) + { + addressToUse = Method.FunctionPointer; + foundAddressType = TypeLoaderEnvironment.MethodAddressType.Canonical; + } + else if (Method.UsgFunctionPointer != IntPtr.Zero) + { + addressToUse = Method.UsgFunctionPointer; + foundAddressType = TypeLoaderEnvironment.MethodAddressType.UniversalCanonical; + } + else + { + // No previous match, new lookup is needed + IntPtr fnptr; + IntPtr unboxingStub; + + MethodDesc searchMethod = Method; + if (Method.UnboxingStub) + { + // Find the function that isn't an unboxing stub, note the first parameter which is false + searchMethod = searchMethod.Context.ResolveGenericMethodInstantiation(false, (DefType)Method.OwningType, Method.NameAndSignature, Method.Instantiation, IntPtr.Zero, false); + } + + if (!TypeLoaderEnvironment.TryGetMethodAddressFromMethodDesc(searchMethod, out fnptr, out unboxingStub, out foundAddressType)) + { + Environment.FailFast("Unable to find method address for method:" + Method.ToString()); + } + + if (Method.UnboxingStub) + { + addressToUse = unboxingStub; + } + else + { + addressToUse = fnptr; + } + + if (foundAddressType == TypeLoaderEnvironment.MethodAddressType.Canonical || + foundAddressType == TypeLoaderEnvironment.MethodAddressType.UniversalCanonical) + { + // Cache the resolved canonical / universal pointer in the MethodDesc + // Actually it would simplify matters here if the MethodDesc held just one pointer + // and the lookup type enumeration value. + Method.SetFunctionPointer( + addressToUse, + foundAddressType == TypeLoaderEnvironment.MethodAddressType.UniversalCanonical); + } + } + + // Look at the resolution type and check whether we can set up the ExactFunctionPointer upfront + switch (foundAddressType) + { + case TypeLoaderEnvironment.MethodAddressType.Exact: + _exactFunctionPointer = addressToUse; + break; + case TypeLoaderEnvironment.MethodAddressType.Canonical: + { + bool methodRequestedIsCanonical = Method.IsCanonicalMethod(CanonicalFormKind.Specific); + bool requestedMethodNeedsDictionaryWhenCalledAsCanonical = NeedsDictionaryParameterToCallCanonicalVersion(Method); + + if (!requestedMethodNeedsDictionaryWhenCalledAsCanonical || methodRequestedIsCanonical) + { + _exactFunctionPointer = addressToUse; + } + break; + } +#if FEATURE_UNIVERSAL_GENERICS + case TypeLoaderEnvironment.MethodAddressType.UniversalCanonical: + { + if (Method.IsCanonicalMethod(CanonicalFormKind.Universal) && + !NeedsDictionaryParameterToCallCanonicalVersion(Method) && + !UniversalGenericParameterLayout.MethodSignatureHasVarsNeedingCallingConventionConverter( + Method.GetTypicalMethodDefinition().Signature)) + { + _exactFunctionPointer = addressToUse; + } + break; + } +#endif + default: + Environment.FailFast("Unexpected method address type"); + return; + } + + if (_exactFunctionPointer == IntPtr.Zero) + { + // We have exhausted exact resolution options so we must resort to calling + // convention conversion. Prepare the type parameters of the method so that + // the calling convention converter can have RuntimeTypeHandle's to work with. + // For canonical methods, convert paramters to their CanonAlike form + // as the Canonical RuntimeTypeHandle's are not permitted to exist. + Debug.Assert(!Method.IsCanonicalMethod(CanonicalFormKind.Universal)); + + bool methodRequestedIsCanonical = Method.IsCanonicalMethod(CanonicalFormKind.Specific); + MethodDesc canonAlikeForm = Method; + foreach (TypeDesc t in canonAlikeForm.Instantiation) + { + builder.PrepareType(t); + } + foreach (TypeDesc t in canonAlikeForm.OwningType.Instantiation) + { + builder.PrepareType(t); + } + + if (!(Method.GetTypicalMethodDefinition() is RuntimeMethodDesc)) + { + // Also, prepare all of the argument types as will be needed by the calling convention converter + MethodSignature signature = canonAlikeForm.Signature; + for (int i = 0; i < signature.Length; i++) + { + TypeDesc t = signature[i]; + if (t is ByRefType) + builder.PrepareType(((ByRefType)t).ParameterType); + else + builder.PrepareType(t); + } + if (signature.ReturnType is ByRefType) + builder.PrepareType((ByRefType)signature.ReturnType); + else + builder.PrepareType(signature.ReturnType); + } + + _universalCanonImplementationOfCanonMethod = methodRequestedIsCanonical; + _methodToUseForInstantiatingParameters = canonAlikeForm; + } + } + + // By the time we reach here, we should always have a function pointer of some form + Debug.Assert((_exactFunctionPointer != IntPtr.Zero) || (Method.FunctionPointer != IntPtr.Zero) || (Method.UsgFunctionPointer != IntPtr.Zero)); + } + + private bool NeedsDictionaryParameterToCallCanonicalVersion(MethodDesc method) + { + if (Method.HasInstantiation) + return true; + + if (!Method.OwningType.HasInstantiation) + return false; + + if (Method is NoMetadataMethodDesc) + { + // If the method does not have metadata, use the NameAndSignature property which should work in that case. + if (TypeLoaderEnvironment.Instance.IsStaticMethodSignature(Method.NameAndSignature.Signature)) + return true; + } + else + { + // Otherwise, use the MethodSignature + if (Method.Signature.IsStatic) + return true; + } + + return Method.OwningType.IsValueType && !Method.UnboxingStub; + } + + internal unsafe override IntPtr Create(TypeBuilder builder) + { + if (_exactFunctionPointer != IntPtr.Zero) + { + // We are done... we don't need to create any unboxing stubs or calling convertion translation + // thunks for exact non-shareable method instantiations + return _exactFunctionPointer; + } + + Debug.Assert(Method.Instantiation.Length > 0 || Method.OwningType.HasInstantiation); + + IntPtr methodDictionary = IntPtr.Zero; + + if (!_universalCanonImplementationOfCanonMethod) + { + methodDictionary = Method.Instantiation.Length > 0 ? + ((InstantiatedMethod)Method).RuntimeMethodDictionary : + builder.GetRuntimeTypeHandle(Method.OwningType).ToIntPtr(); + } + + if (Method.FunctionPointer != IntPtr.Zero) + { + if (Method.Instantiation.Length > 0 + || TypeLoaderEnvironment.Instance.IsStaticMethodSignature(MethodSignature) + || (Method.OwningType.IsValueType && !Method.UnboxingStub)) + { + Debug.Assert(methodDictionary != IntPtr.Zero); +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + if (this.ExactCallableAddressNeeded) + { + // In this case we need to build an instantiating stub + return BuildCallingConventionConverter(builder, Method.FunctionPointer, methodDictionary, false); + } + else +#endif + { + return FunctionPointerOps.GetGenericMethodFunctionPointer(Method.FunctionPointer, methodDictionary); + } + } + else + { + return Method.FunctionPointer; + } + } +#if FEATURE_UNIVERSAL_GENERICS + else if (Method.UsgFunctionPointer != IntPtr.Zero) + { + return BuildCallingConventionConverter(builder, Method.UsgFunctionPointer, methodDictionary, true); + } +#endif + + Debug.Fail("UNREACHABLE"); + return IntPtr.Zero; + } + +#if FEATURE_UNIVERSAL_GENERICS + private IntPtr BuildCallingConventionConverter(TypeBuilder builder, IntPtr pointerToUse, IntPtr dictionary, bool usgConverter) + { + RuntimeTypeHandle[] typeArgs = Empty.Array; + RuntimeTypeHandle[] methodArgs = Empty.Array; + + typeArgs = builder.GetRuntimeTypeHandles(_methodToUseForInstantiatingParameters.OwningType.Instantiation); + + bool containingTypeIsValueType = _methodToUseForInstantiatingParameters.OwningType.IsValueType; + bool genericMethod = (_methodToUseForInstantiatingParameters.Instantiation.Length > 0); + + methodArgs = builder.GetRuntimeTypeHandles(_methodToUseForInstantiatingParameters.Instantiation); + + Debug.Assert(!MethodSignature.IsNativeLayoutSignature || (MethodSignature.NativeLayoutSignature() != IntPtr.Zero)); + + CallConverterThunk.ThunkKind thunkKind = default(CallConverterThunk.ThunkKind); + if (usgConverter) + { + if (genericMethod || containingTypeIsValueType) + { + if (Method.UnboxingStub) + { + if (dictionary == IntPtr.Zero) + Environment.FailFast("Need standard to generic non-instantiating unboxing stub thunk kind"); + else + thunkKind = CallConverterThunk.ThunkKind.StandardUnboxingAndInstantiatingGeneric; + } + else + { + if (dictionary == IntPtr.Zero) + thunkKind = CallConverterThunk.ThunkKind.StandardToGenericPassthruInstantiating; + else + thunkKind = CallConverterThunk.ThunkKind.StandardToGenericInstantiating; + } + } + else + { + if (dictionary == IntPtr.Zero) + thunkKind = CallConverterThunk.ThunkKind.StandardToGenericPassthruInstantiatingIfNotHasThis; + else + thunkKind = CallConverterThunk.ThunkKind.StandardToGenericInstantiatingIfNotHasThis; + } + } + else + { + thunkKind = CallConverterThunk.ThunkKind.StandardToStandardInstantiating; + } + + IntPtr thunkPtr = CallConverterThunk.MakeThunk( + thunkKind, + Method.UsgFunctionPointer, + MethodSignature, + dictionary, + typeArgs, + methodArgs); + + Debug.Assert(thunkPtr != IntPtr.Zero); + return thunkPtr; + } +#endif + } + + private class CastingCell : GenericDictionaryCell + { + internal TypeDesc Type; + internal bool Throwing; + + internal override void Prepare(TypeBuilder builder) + { + if (Type.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Canonical types do not have EETypes"); + + builder.RegisterForPreparation(Type); + } + + internal override IntPtr Create(TypeBuilder builder) + { + RuntimeTypeHandle th = GetRuntimeTypeHandleWithNullableTransform(builder, Type); + return RuntimeAugments.GetCastingHelperForType(th, Throwing); + } + + internal override unsafe IntPtr CreateLazyLookupCell(TypeBuilder builder, out IntPtr auxResult) + { + RuntimeTypeHandle th = GetRuntimeTypeHandleWithNullableTransform(builder, Type); + auxResult = th.ToIntPtr(); + return RuntimeAugments.GetCastingHelperForType(th, Throwing); + } + } + + private class AllocateArrayCell : GenericDictionaryCell + { + internal TypeDesc Type; + + internal override void Prepare(TypeBuilder builder) + { + if (Type.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Canonical types do not have EETypes"); + + builder.RegisterForPreparation(Type); + } + + internal override IntPtr Create(TypeBuilder builder) + { + return RuntimeAugments.GetAllocateArrayHelperForType(builder.GetRuntimeTypeHandle(Type)); + } + + internal override unsafe IntPtr CreateLazyLookupCell(TypeBuilder builder, out IntPtr auxResult) + { + auxResult = builder.GetRuntimeTypeHandle(Type).ToIntPtr(); + return Create(builder); + } + } + +#if FEATURE_UNIVERSAL_GENERICS + private class CallingConventionConverterCell : GenericDictionaryCell + { + internal NativeFormat.CallingConventionConverterKind Flags; + internal RuntimeSignature Signature; + internal Instantiation MethodArgs; + internal Instantiation TypeArgs; + + internal override void Prepare(TypeBuilder builder) + { + if (!MethodArgs.IsNull) + { + foreach (TypeDesc t in MethodArgs) + { + if (t.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Canonical types do not have EETypes"); + } + } + if (!TypeArgs.IsNull) + { + foreach (TypeDesc t in TypeArgs) + { + if (t.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Canonical types do not have EETypes"); + } + } + } + + internal override IntPtr Create(TypeBuilder builder) + { + RuntimeTypeHandle[] typeArgs = null; + RuntimeTypeHandle[] methodArgs = null; + + if (!MethodArgs.IsNull) + { + methodArgs = builder.GetRuntimeTypeHandles(MethodArgs); + } + + if (!TypeArgs.IsNull) + { + typeArgs = builder.GetRuntimeTypeHandles(TypeArgs); + } + + CallConverterThunk.ThunkKind thunkKind; + + switch (Flags) + { + case NativeFormat.CallingConventionConverterKind.NoInstantiatingParam: + thunkKind = CallConverterThunk.ThunkKind.GenericToStandardWithTargetPointerArg; + break; + + case NativeFormat.CallingConventionConverterKind.HasInstantiatingParam: + thunkKind = CallConverterThunk.ThunkKind.GenericToStandardWithTargetPointerArgAndParamArg; + break; + + case NativeFormat.CallingConventionConverterKind.MaybeInstantiatingParam: + thunkKind = CallConverterThunk.ThunkKind.GenericToStandardWithTargetPointerArgAndMaybeParamArg; + break; + + default: + throw new NotSupportedException(); + } + + IntPtr result = CallConverterThunk.MakeThunk(thunkKind, IntPtr.Zero, Signature, IntPtr.Zero, typeArgs, methodArgs); + return result; + } + } +#endif + + internal static unsafe GenericDictionaryCell[] BuildDictionary(TypeBuilder typeBuilder, NativeLayoutInfoLoadContext nativeLayoutInfoLoadContext, NativeParser parser) + { + uint parserStartOffset = parser.Offset; + + uint count = parser.GetSequenceCount(); + Debug.Assert(count > 0); + + TypeLoaderLogger.WriteLine("Parsing dictionary layout @ " + parserStartOffset.LowLevelToString() + " (" + count.LowLevelToString() + " entries)"); + + GenericDictionaryCell[] dictionary = new GenericDictionaryCell[count]; + + for (uint i = 0; i < count; i++) + { + TypeLoaderLogger.WriteLine(" -> DictionaryCell[" + i.LowLevelToString() + "] = "); + + dictionary[i] = ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); + } + + for (uint i = 0; i < count; i++) + dictionary[i].Prepare(typeBuilder); + + return dictionary; + } + + internal static unsafe GenericDictionaryCell[] BuildFloatingDictionary(TypeBuilder typeBuilder, NativeLayoutInfoLoadContext nativeLayoutInfoLoadContext, NativeParser parser, out int floatingVersionCellIndex, out int floatingVersionInLayout) + { + // + // The format of a dictionary that has a floating portion is as follows: + // + // "Fixed" portion: + // - First slot is a pointer to the first cell of the "floating" portion + // - Followed by N various dictionary lookup cells + // "Floating" portion: + // - Cell containing the version number of the floating portion + // - Followed by N various dictionary lookup cells + // + + floatingVersionCellIndex = floatingVersionInLayout = -1; + + uint count = parser.GetSequenceCount(); + Debug.Assert(count > 1); + + GenericDictionaryCell cell = ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); + if (!(cell is PointerToOtherDictionarySlotCell)) + { + // This is not a dictionary layout that has a floating portion + Debug.Fail("Unreachable: we should never reach here if the target dictionary does not have a floating layout"); + return null; + } + + PointerToOtherDictionarySlotCell pointerToCell = (PointerToOtherDictionarySlotCell)cell; + floatingVersionCellIndex = (int)pointerToCell.OtherDictionarySlot; + Debug.Assert(count > pointerToCell.OtherDictionarySlot); + + GenericDictionaryCell[] dictionary = new GenericDictionaryCell[count - pointerToCell.OtherDictionarySlot]; + + for (uint i = 1; i < pointerToCell.OtherDictionarySlot; i++) + { + // Parse and discard the fixed dictionary cells. We only need to build the cells of the floating portion + ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); + } + + for (uint i = pointerToCell.OtherDictionarySlot; i < count; i++) + { + TypeLoaderLogger.WriteLine(" -> FloatingDictionaryCell[" + (i - pointerToCell.OtherDictionarySlot).LowLevelToString() + "] (" + i.LowLevelToString() + " in all) = "); + + cell = ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); + + if ((i == pointerToCell.OtherDictionarySlot) && (cell is IntPtrCell)) + { + // The first cell in the floating portion should always be the version number + floatingVersionInLayout = (int)((IntPtrCell)cell).Value; + } + + dictionary[i - pointerToCell.OtherDictionarySlot] = cell; + } + + for (uint i = pointerToCell.OtherDictionarySlot; i < count; i++) + dictionary[i - pointerToCell.OtherDictionarySlot].Prepare(typeBuilder); + + return dictionary; + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + /// + /// Build an array of GenericDictionaryCell from a NativeParser stream that has the appropriate metadata + /// Return null if there are no cells to describe + /// + internal static unsafe GenericDictionaryCell[] BuildDictionaryFromMetadataTokensAndContext(TypeBuilder typeBuilder, NativeParser parser, NativeFormatMetadataUnit nativeMetadataUnit, FixupCellMetadataResolver resolver) + { + uint parserStartOffset = parser.Offset; + + uint count = parser.GetSequenceCount(); + + // An empty dictionary isn't interesting + if (count == 0) + return null; + + Debug.Assert(count > 0); + TypeLoaderLogger.WriteLine("Parsing dictionary layout @ " + parserStartOffset.LowLevelToString() + " (" + count.LowLevelToString() + " entries)"); + + GenericDictionaryCell[] dictionary = new GenericDictionaryCell[count]; + + for (uint i = 0; i < count; i++) + { + MetadataFixupKind fixupKind = (MetadataFixupKind)parser.GetUInt8(); + Internal.Metadata.NativeFormat.Handle token = parser.GetUnsigned().AsHandle(); + Internal.Metadata.NativeFormat.Handle token2 = new Internal.Metadata.NativeFormat.Handle(); + + switch (fixupKind) + { + case MetadataFixupKind.GenericConstrainedMethod: + case MetadataFixupKind.NonGenericConstrainedMethod: + case MetadataFixupKind.NonGenericDirectConstrainedMethod: + token2 = parser.GetUnsigned().AsHandle(); + break; + } + GenericDictionaryCell cell = CreateCellFromFixupKindAndToken(fixupKind, resolver, token, token2); + cell.Prepare(typeBuilder); + dictionary[i] = cell; + } + + return dictionary; + } +#endif + + private static TypeDesc TransformNullable(TypeDesc type) + { + DefType typeAsDefType = type as DefType; + if (typeAsDefType != null && typeAsDefType.Instantiation.Length == 1 && typeAsDefType.GetTypeDefinition().RuntimeTypeHandle.Equals(typeof(Nullable<>).TypeHandle)) + return typeAsDefType.Instantiation[0]; + return type; + } + +#if FEATURE_UNIVERSAL_GENERICS + private static int ComputeConstrainedMethodSlot(MethodDesc constrainedMethod) + { + if (constrainedMethod.OwningType.IsInterface) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + ushort slot; + if (!LazyVTableResolver.TryGetInterfaceSlotNumberFromMethod(constrainedMethod, out slot)) + throw new BadImageFormatException(); + + return slot; +#else + Environment.FailFast("Unable to resolve constrained call"); + return 0; +#endif + } + else if (constrainedMethod.OwningType == constrainedMethod.Context.GetWellKnownType(WellKnownType.Object)) + { + if (constrainedMethod.Name == "ToString") + return ConstrainedCallSupport.NonGenericConstrainedCallDesc.s_ToStringSlot; + else if (constrainedMethod.Name == "GetHashCode") + return ConstrainedCallSupport.NonGenericConstrainedCallDesc.s_GetHashCodeSlot; + else if (constrainedMethod.Name == "Equals") + return ConstrainedCallSupport.NonGenericConstrainedCallDesc.s_EqualsSlot; + } + + Environment.FailFast("unable to construct constrained method slot from constrained method"); + return -1; + } +#endif + + internal static GenericDictionaryCell CreateMethodCell(MethodDesc method, bool exactCallableAddressNeeded) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + var nativeFormatMethod = (TypeSystem.NativeFormat.NativeFormatMethod)method.GetTypicalMethodDefinition(); + + return new MethodCell + { + ExactCallableAddressNeeded = exactCallableAddressNeeded, + Method = method, + MethodSignature = RuntimeSignature.CreateFromMethodHandle(nativeFormatMethod.MetadataUnit.RuntimeModule, nativeFormatMethod.Handle.ToInt()) + }; +#else + Environment.FailFast("Creating a methodcell from a MethodDesc only supported in the presence of metadata based type loading."); + return null; +#endif + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + /// + /// Create a single cell to resolve based on a MetadataFixupKind, and the matching tokens + /// + internal static GenericDictionaryCell CreateCellFromFixupKindAndToken(MetadataFixupKind kind, FixupCellMetadataResolver metadata, Internal.Metadata.NativeFormat.Handle token, Internal.Metadata.NativeFormat.Handle token2) + { + GenericDictionaryCell cell; + + switch (kind) + { + case MetadataFixupKind.TypeHandle: + { + var type = metadata.GetType(token); + TypeLoaderLogger.WriteLine("TypeHandle: " + type.ToString()); + + cell = new TypeHandleCell() { Type = type }; + } + break; + + case MetadataFixupKind.ArrayOfTypeHandle: + { + var type = metadata.GetType(token); + var arrayType = type.Context.GetArrayType(type); + TypeLoaderLogger.WriteLine("TypeHandle: " + arrayType.ToString()); + + cell = new TypeHandleCell() { Type = arrayType }; + } + break; + + case MetadataFixupKind.VirtualCallDispatch: + { + var method = metadata.GetMethod(token); + + + var containingType = method.OwningType; + if (containingType.IsInterface) + { + ushort slot; + if (!LazyVTableResolver.TryGetInterfaceSlotNumberFromMethod(method, out slot)) + { + Environment.FailFast("Unable to get interface slot while resolving InterfaceCall dictionary cell"); + } + + TypeLoaderLogger.WriteLine("InterfaceCall: " + containingType.ToString() + ", slot #" + ((int)slot).LowLevelToString()); + + cell = new InterfaceCallCell() { InterfaceType = containingType, Slot = (int)slot }; + } + else + { + // TODO! Implement virtual dispatch cell creation + throw NotImplemented.ByDesign; + } + } + break; + + case MetadataFixupKind.MethodDictionary: + { + var genericMethod = metadata.GetMethod(token); + TypeLoaderLogger.WriteLine("MethodDictionary: " + genericMethod.ToString()); + + cell = new MethodDictionaryCell { GenericMethod = (InstantiatedMethod)genericMethod }; + } + break; + + case MetadataFixupKind.GcStaticData: + { + var type = metadata.GetType(token); + var staticDataKind = StaticDataKind.Gc; + TypeLoaderLogger.WriteLine("StaticData (" + (staticDataKind == StaticDataKind.Gc ? "Gc" : "NonGc") + ": " + type.ToString()); + + cell = new StaticDataCell() { DataKind = staticDataKind, Type = type }; + } + break; + + case MetadataFixupKind.NonGcStaticData: + { + var type = metadata.GetType(token); + var staticDataKind = StaticDataKind.NonGc; + TypeLoaderLogger.WriteLine("StaticData (" + (staticDataKind == StaticDataKind.Gc ? "Gc" : "NonGc") + ": " + type.ToString()); + + cell = new StaticDataCell() { DataKind = staticDataKind, Type = type }; + } + break; + + case MetadataFixupKind.DirectGcStaticData: + { + var type = metadata.GetType(token); + var staticDataKind = StaticDataKind.Gc; + TypeLoaderLogger.WriteLine("Direct StaticData (" + (staticDataKind == StaticDataKind.Gc ? "Gc" : "NonGc") + ": " + type.ToString()); + + cell = new StaticDataCell() { DataKind = staticDataKind, Type = type }; + } + break; + + case MetadataFixupKind.DirectNonGcStaticData: + { + var type = metadata.GetType(token); + var staticDataKind = StaticDataKind.NonGc; + TypeLoaderLogger.WriteLine("Direct StaticData (" + (staticDataKind == StaticDataKind.Gc ? "Gc" : "NonGc") + ": " + type.ToString()); + + cell = new StaticDataCell() { DataKind = staticDataKind, Type = type }; + } + break; + + case MetadataFixupKind.UnwrapNullableType: + { + var type = metadata.GetType(token); + TypeLoaderLogger.WriteLine("UnwrapNullableType of: " + type.ToString()); + + if (type is DefType) + cell = new UnwrapNullableTypeCell() { Type = (DefType)type }; + else + cell = new TypeHandleCell() { Type = type }; + } + break; + + case MetadataFixupKind.FieldLdToken: + { + var field = metadata.GetField(token); + + TypeLoaderLogger.WriteLine("LdToken on: " + field.ToString()); + IntPtr fieldName = TypeLoaderEnvironment.Instance.GetNativeFormatStringForString(field.Name); + cell = new FieldLdTokenCell() { FieldName = fieldName, ContainingType = field.OwningType }; + } + break; + + case MetadataFixupKind.MethodLdToken: + { + var method = metadata.GetMethod(token); + TypeLoaderLogger.WriteLine("LdToken on: " + method.ToString()); + var nativeFormatMethod = (TypeSystem.NativeFormat.NativeFormatMethod)method.GetTypicalMethodDefinition(); + + cell = new MethodLdTokenCell + { + Method = method, + MethodName = IntPtr.Zero, + MethodSignature = RuntimeSignature.CreateFromMethodHandle(nativeFormatMethod.MetadataUnit.RuntimeModule, nativeFormatMethod.Handle.ToInt()) + }; + } + break; + + case MetadataFixupKind.TypeSize: + { + var type = metadata.GetType(token); + TypeLoaderLogger.WriteLine("TypeSize: " + type.ToString()); + + cell = new TypeSizeCell() { Type = type }; + } + break; + + case MetadataFixupKind.FieldOffset: + { + var field = metadata.GetField(token); + TypeLoaderLogger.WriteLine("FieldOffset: " + field.ToString()); + + cell = new FieldOffsetCell() { Field = field }; + } + break; + + case MetadataFixupKind.AllocateObject: + { + var type = metadata.GetType(token); + TypeLoaderLogger.WriteLine("AllocateObject on: " + type.ToString()); + + cell = new AllocateObjectCell { Type = type }; + } + break; + + case MetadataFixupKind.DefaultConstructor: + { + var type = metadata.GetType(token); + TypeLoaderLogger.WriteLine("DefaultConstructor on: " + type.ToString()); + + cell = new DefaultConstructorCell { Type = type }; + } + break; + + case MetadataFixupKind.UnboxingStubMethod: + { + var method = metadata.GetMethod(token); + TypeLoaderLogger.WriteLine("Unboxing Stub Method: " + method.ToString()); + if (method.OwningType.IsValueType) + { + // If an unboxing stub could exists, that's actually what we want + method = method.Context.ResolveGenericMethodInstantiation(true/* get the unboxing stub */, method.OwningType.GetClosestDefType(), method.NameAndSignature, method.Instantiation, IntPtr.Zero, false); + } + + var nativeFormatMethod = (TypeSystem.NativeFormat.NativeFormatMethod)method.GetTypicalMethodDefinition(); + + cell = new MethodCell + { + Method = method, + MethodSignature = RuntimeSignature.CreateFromMethodHandle(nativeFormatMethod.MetadataUnit.RuntimeModule, nativeFormatMethod.Handle.ToInt()) + }; + } + break; + + case MetadataFixupKind.Method: + { + var method = metadata.GetMethod(token); + TypeLoaderLogger.WriteLine("Method: " + method.ToString()); + var nativeFormatMethod = (TypeSystem.NativeFormat.NativeFormatMethod)method.GetTypicalMethodDefinition(); + + cell = new MethodCell + { + Method = method, + MethodSignature = RuntimeSignature.CreateFromMethodHandle(nativeFormatMethod.MetadataUnit.RuntimeModule, nativeFormatMethod.Handle.ToInt()) + }; + } + break; + + case MetadataFixupKind.CallableMethod: + { + var method = metadata.GetMethod(token); + TypeLoaderLogger.WriteLine("CallableMethod: " + method.ToString()); + var nativeFormatMethod = (TypeSystem.NativeFormat.NativeFormatMethod)method.GetTypicalMethodDefinition(); + + cell = new MethodCell + { + ExactCallableAddressNeeded = true, + Method = method, + MethodSignature = RuntimeSignature.CreateFromMethodHandle(nativeFormatMethod.MetadataUnit.RuntimeModule, nativeFormatMethod.Handle.ToInt()) + }; + } + break; + + case MetadataFixupKind.NonGenericDirectConstrainedMethod: + { + var constraintType = metadata.GetType(token); + var method = metadata.GetMethod(token2); + var constrainedMethodType = method.OwningType; + var constrainedMethodSlot = ComputeConstrainedMethodSlot(method); + + TypeLoaderLogger.WriteLine("NonGenericDirectConstrainedMethod: " + constraintType.ToString() + " Method:" + method.ToString() + " Consisting of " + constrainedMethodType.ToString() + ", slot #" + constrainedMethodSlot.LowLevelToString()); + + cell = new NonGenericDirectConstrainedMethodCell() + { + ConstraintType = constraintType, + ConstrainedMethodType = constrainedMethodType, + ConstrainedMethodSlot = (int)constrainedMethodSlot + }; + } + break; + + case MetadataFixupKind.NonGenericConstrainedMethod: + { + var constraintType = metadata.GetType(token); + var method = metadata.GetMethod(token2); + var constrainedMethodType = method.OwningType; + var constrainedMethodSlot = ComputeConstrainedMethodSlot(method); + + TypeLoaderLogger.WriteLine("NonGenericConstrainedMethod: " + constraintType.ToString() + " Method:" + method.ToString() + " Consisting of " + constrainedMethodType.ToString() + ", slot #" + constrainedMethodSlot.LowLevelToString()); + + cell = new NonGenericConstrainedMethodCell() + { + ConstraintType = constraintType, + ConstrainedMethodType = constrainedMethodType, + ConstrainedMethodSlot = (int)constrainedMethodSlot + }; + } + break; + + case MetadataFixupKind.GenericConstrainedMethod: + { + var constraintType = metadata.GetType(token); + var method = metadata.GetMethod(token2); + var nativeFormatMethod = (TypeSystem.NativeFormat.NativeFormatMethod)method.GetTypicalMethodDefinition(); + + + TypeLoaderLogger.WriteLine("GenericConstrainedMethod: " + constraintType.ToString() + " Method " + method.ToString()); + + cell = new GenericConstrainedMethodCell() + { + ConstraintType = constraintType, + ConstrainedMethod = method, + MethodName = IntPtr.Zero, + MethodSignature = RuntimeSignature.CreateFromMethodHandle(nativeFormatMethod.MetadataUnit.RuntimeModule, nativeFormatMethod.Handle.ToInt()) + }; + } + break; + + case MetadataFixupKind.IsInst: + case MetadataFixupKind.CastClass: + { + var type = metadata.GetType(token); + + TypeLoaderLogger.WriteLine("Casting on: " + type.ToString()); + + cell = new CastingCell { Type = type, Throwing = (kind == MetadataFixupKind.CastClass) }; + } + break; + + case MetadataFixupKind.AllocateArray: + { + var type = metadata.GetType(token); + + TypeLoaderLogger.WriteLine("AllocateArray on: " + type.ToString()); + + cell = new AllocateArrayCell { Type = type }; + } + break; + + case MetadataFixupKind.CheckArrayElementType: + { + var type = metadata.GetType(token); + + TypeLoaderLogger.WriteLine("CheckArrayElementType on: " + type.ToString()); + + cell = new CheckArrayElementTypeCell { Type = type }; + } + break; + + case MetadataFixupKind.CallingConventionConverter_NoInstantiatingParam: + case MetadataFixupKind.CallingConventionConverter_MaybeInstantiatingParam: + case MetadataFixupKind.CallingConventionConverter_HasInstantiatingParam: + { + CallingConventionConverterKind converterKind; + switch (kind) + { + case MetadataFixupKind.CallingConventionConverter_NoInstantiatingParam: + converterKind = CallingConventionConverterKind.NoInstantiatingParam; + break; + case MetadataFixupKind.CallingConventionConverter_MaybeInstantiatingParam: + converterKind = CallingConventionConverterKind.MaybeInstantiatingParam; + break; + case MetadataFixupKind.CallingConventionConverter_HasInstantiatingParam: + converterKind = CallingConventionConverterKind.HasInstantiatingParam; + break; + default: + Environment.FailFast("Unknown converter kind"); + throw new BadImageFormatException(); + } + + cell = new CallingConventionConverterCell + { + Flags = converterKind, + Signature = metadata.GetSignature(token), + MethodArgs = metadata.MethodInstantiation, + TypeArgs = metadata.TypeInstantiation + }; + +#if TYPE_LOADER_TRACE + TypeLoaderLogger.WriteLine("CallingConventionConverter on: "); + TypeLoaderLogger.WriteLine(" -> Flags: " + ((int)converterKind).LowLevelToString()); + TypeLoaderLogger.WriteLine(" -> Signature: " + token.ToInt().LowLevelToString()); + for (int i = 0; i < metadata.TypeInstantiation.Length; i++) + TypeLoaderLogger.WriteLine(" -> TypeArg[" + i.LowLevelToString() + "]: " + metadata.TypeInstantiation[i]); + for (int i = 0; i < metadata.MethodInstantiation.Length; i++) + TypeLoaderLogger.WriteLine(" -> MethodArg[" + i.LowLevelToString() + "]: " + metadata.MethodInstantiation[i]); +#endif + } + break; + + default: + Environment.FailFast("Unknown fixup kind"); + // Throw here so that the compiler won't complain. + throw new BadImageFormatException(); + } + + return cell; + } +#endif + + internal static GenericDictionaryCell ParseAndCreateCell(NativeLayoutInfoLoadContext nativeLayoutInfoLoadContext, ref NativeParser parser) + { + GenericDictionaryCell cell; + + var kind = parser.GetFixupSignatureKind(); + switch (kind) + { + case FixupSignatureKind.TypeHandle: + { + var type = nativeLayoutInfoLoadContext.GetType(ref parser); + TypeLoaderLogger.WriteLine("TypeHandle: " + type.ToString()); + + cell = new TypeHandleCell() { Type = type }; + } + break; + + case FixupSignatureKind.InterfaceCall: + { + var interfaceType = nativeLayoutInfoLoadContext.GetType(ref parser); + var slot = parser.GetUnsigned(); + TypeLoaderLogger.WriteLine("InterfaceCall: " + interfaceType.ToString() + ", slot #" + slot.LowLevelToString()); + + cell = new InterfaceCallCell() { InterfaceType = interfaceType, Slot = (int)slot }; + } + break; + + case FixupSignatureKind.MethodDictionary: + { + var genericMethod = nativeLayoutInfoLoadContext.GetMethod(ref parser); + Debug.Assert(genericMethod.Instantiation.Length > 0); + TypeLoaderLogger.WriteLine("MethodDictionary: " + genericMethod.ToString()); + + cell = new MethodDictionaryCell { GenericMethod = (InstantiatedMethod)genericMethod }; + } + break; + + case FixupSignatureKind.StaticData: + { + var type = nativeLayoutInfoLoadContext.GetType(ref parser); + StaticDataKind staticDataKind = (StaticDataKind)parser.GetUnsigned(); + TypeLoaderLogger.WriteLine("StaticData (" + (staticDataKind == StaticDataKind.Gc ? "Gc" : "NonGc") + ": " + type.ToString()); + + cell = new StaticDataCell() { DataKind = staticDataKind, Type = type }; + } + break; + + case FixupSignatureKind.UnwrapNullableType: + { + var type = nativeLayoutInfoLoadContext.GetType(ref parser); + TypeLoaderLogger.WriteLine("UnwrapNullableType of: " + type.ToString()); + + if (type is DefType) + cell = new UnwrapNullableTypeCell() { Type = (DefType)type }; + else + cell = new TypeHandleCell() { Type = type }; + } + break; + + case FixupSignatureKind.FieldLdToken: + { + NativeParser ldtokenSigParser = parser.GetParserFromRelativeOffset(); + + var type = nativeLayoutInfoLoadContext.GetType(ref ldtokenSigParser); + IntPtr fieldNameSig = ldtokenSigParser.Reader.OffsetToAddress(ldtokenSigParser.Offset); + TypeLoaderLogger.WriteLine("LdToken on: " + type.ToString() + "." + ldtokenSigParser.GetString()); + + cell = new FieldLdTokenCell() { FieldName = fieldNameSig, ContainingType = type }; + } + break; + + case FixupSignatureKind.MethodLdToken: + { + NativeParser ldtokenSigParser = parser.GetParserFromRelativeOffset(); + + RuntimeSignature methodNameSig; + RuntimeSignature methodSig; + var method = nativeLayoutInfoLoadContext.GetMethod(ref ldtokenSigParser, out methodNameSig, out methodSig); + TypeLoaderLogger.WriteLine("LdToken on: " + method.OwningType.ToString() + "::" + method.NameAndSignature.Name); + + cell = new MethodLdTokenCell + { + Method = method, + MethodName = methodNameSig.NativeLayoutSignature(), + MethodSignature = methodSig + }; + } + break; + +#if FEATURE_UNIVERSAL_GENERICS + case FixupSignatureKind.TypeSize: + { + var type = nativeLayoutInfoLoadContext.GetType(ref parser); + TypeLoaderLogger.WriteLine("TypeSize: " + type.ToString()); + + cell = new TypeSizeCell() { Type = type }; + } + break; + + case FixupSignatureKind.FieldOffset: + { + var type = (DefType)nativeLayoutInfoLoadContext.GetType(ref parser); + uint ordinal = parser.GetUnsigned(); + TypeLoaderLogger.WriteLine("FieldOffset on: " + type.ToString()); + + cell = new FieldOffsetCell() { Ordinal = ordinal, ContainingType = type }; + } + break; + + case FixupSignatureKind.VTableOffset: + { + var type = nativeLayoutInfoLoadContext.GetType(ref parser); + var vtableSlot = parser.GetUnsigned(); + TypeLoaderLogger.WriteLine("VTableOffset on: " + type.ToString() + ", slot: " + vtableSlot.LowLevelToString()); + + cell = new VTableOffsetCell() { ContainingType = type, VTableSlot = vtableSlot }; + } + break; +#endif + + case FixupSignatureKind.AllocateObject: + { + var type = nativeLayoutInfoLoadContext.GetType(ref parser); + TypeLoaderLogger.WriteLine("AllocateObject on: " + type.ToString()); + + cell = new AllocateObjectCell { Type = type }; + } + break; + + case FixupSignatureKind.DefaultConstructor: + { + var type = nativeLayoutInfoLoadContext.GetType(ref parser); + TypeLoaderLogger.WriteLine("DefaultConstructor on: " + type.ToString()); + + cell = new DefaultConstructorCell { Type = type }; + } + break; + + case FixupSignatureKind.Method: + { + RuntimeSignature methodSig; + RuntimeSignature methodNameSig; + var method = nativeLayoutInfoLoadContext.GetMethod(ref parser, out methodNameSig, out methodSig); + TypeLoaderLogger.WriteLine("Method: " + method.ToString()); + + cell = new MethodCell + { + Method = method, + MethodSignature = methodSig + }; + } + break; + +#if FEATURE_UNIVERSAL_GENERICS + case FixupSignatureKind.NonGenericDirectConstrainedMethod: + { + var constraintType = nativeLayoutInfoLoadContext.GetType(ref parser); + var constrainedMethodType = nativeLayoutInfoLoadContext.GetType(ref parser); + var constrainedMethodSlot = parser.GetUnsigned(); + TypeLoaderLogger.WriteLine("NonGenericDirectConstrainedMethod: " + constraintType.ToString() + " Method " + constrainedMethodType.ToString() + ", slot #" + constrainedMethodSlot.LowLevelToString()); + + cell = new NonGenericDirectConstrainedMethodCell() + { + ConstraintType = constraintType, + ConstrainedMethodType = constrainedMethodType, + ConstrainedMethodSlot = (int)constrainedMethodSlot + }; + } + break; + + case FixupSignatureKind.NonGenericConstrainedMethod: + { + var constraintType = nativeLayoutInfoLoadContext.GetType(ref parser); + var constrainedMethodType = nativeLayoutInfoLoadContext.GetType(ref parser); + var constrainedMethodSlot = parser.GetUnsigned(); + TypeLoaderLogger.WriteLine("NonGenericConstrainedMethod: " + constraintType.ToString() + " Method " + constrainedMethodType.ToString() + ", slot #" + constrainedMethodSlot.LowLevelToString()); + + cell = new NonGenericConstrainedMethodCell() + { + ConstraintType = constraintType, + ConstrainedMethodType = constrainedMethodType, + ConstrainedMethodSlot = (int)constrainedMethodSlot + }; + } + break; + + case FixupSignatureKind.GenericConstrainedMethod: + { + var constraintType = nativeLayoutInfoLoadContext.GetType(ref parser); + + NativeParser ldtokenSigParser = parser.GetParserFromRelativeOffset(); + RuntimeSignature methodNameSig; + RuntimeSignature methodSig; + var method = nativeLayoutInfoLoadContext.GetMethod(ref ldtokenSigParser, out methodNameSig, out methodSig); + + TypeLoaderLogger.WriteLine("GenericConstrainedMethod: " + constraintType.ToString() + " Method " + method.OwningType.ToString() + "::" + method.NameAndSignature.Name); + + cell = new GenericConstrainedMethodCell() + { + ConstraintType = constraintType, + ConstrainedMethod = method, + MethodName = methodNameSig.NativeLayoutSignature(), + MethodSignature = methodSig + }; + } + break; +#endif + + case FixupSignatureKind.IsInst: + case FixupSignatureKind.CastClass: + { + var type = nativeLayoutInfoLoadContext.GetType(ref parser); + + TypeLoaderLogger.WriteLine("Casting on: " + type.ToString()); + + cell = new CastingCell { Type = type, Throwing = (kind == FixupSignatureKind.CastClass) }; + } + break; + + case FixupSignatureKind.AllocateArray: + { + var type = nativeLayoutInfoLoadContext.GetType(ref parser); + + TypeLoaderLogger.WriteLine("AllocateArray on: " + type.ToString()); + + cell = new AllocateArrayCell { Type = type }; + } + break; + +#if FEATURE_UNIVERSAL_GENERICS + case FixupSignatureKind.CallingConventionConverter: + { + CallingConventionConverterKind flags = (CallingConventionConverterKind)parser.GetUnsigned(); + NativeParser sigParser = parser.GetParserFromRelativeOffset(); + RuntimeSignature signature = RuntimeSignature.CreateFromNativeLayoutSignature(nativeLayoutInfoLoadContext._module.Handle, sigParser.Offset); + + TypeLoaderLogger.WriteLine("CallingConventionConverter: Flags=" + ((int)flags).LowLevelToString() + " Signature=" + signature.NativeLayoutSignature().LowLevelToString()); + + cell = new CallingConventionConverterCell + { + Flags = flags, + Signature = signature, + MethodArgs = nativeLayoutInfoLoadContext._methodArgumentHandles, + TypeArgs = nativeLayoutInfoLoadContext._typeArgumentHandles + }; + } + break; +#endif + + case FixupSignatureKind.NotYetSupported: + case FixupSignatureKind.ThreadStaticIndex: + TypeLoaderLogger.WriteLine("Valid dictionary entry, but not yet supported by the TypeLoader!"); + throw new TypeBuilder.MissingTemplateException(); + + case FixupSignatureKind.PointerToOtherSlot: + cell = new PointerToOtherDictionarySlotCell + { + OtherDictionarySlot = parser.GetUnsigned() + }; + TypeLoaderLogger.WriteLine("PointerToOtherSlot: " + ((PointerToOtherDictionarySlotCell)cell).OtherDictionarySlot.LowLevelToString()); + break; + + case FixupSignatureKind.IntValue: + cell = new IntPtrCell + { + Value = new IntPtr((int)parser.GetUnsigned()) + }; + TypeLoaderLogger.WriteLine("IntValue: " + ((IntPtrCell)cell).Value.LowLevelToString()); + break; + + default: + parser.ThrowBadImageFormatException(); + cell = null; + break; + } + + return cell; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LazyVtableResolverThunk.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LazyVtableResolverThunk.cs new file mode 100644 index 00000000000000..fb62bf138364e7 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LazyVtableResolverThunk.cs @@ -0,0 +1,806 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Internal.Runtime.Augments; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.Runtime.CallConverter; + +using ArgIterator = Internal.Runtime.CallConverter.ArgIterator; + +namespace Internal.Runtime.TypeLoader +{ +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + public static class LazyVTableResolver + { + private static object s_lockObject = new object(); + private static object s_lazyVtableThunksPoolHeap; + + private static volatile IntPtr[] s_thunks = InitialThunks(); + + [DllImport("*", ExactSpelling = true, EntryPoint = "VTableResolver_Init")] + private static extern unsafe int VTableResolver_Init(out IntPtr firstResolverThunk, + IntPtr vtableResolveCallback, + IntPtr universalTransition, + out int pregeneratedThunkCount); + + [DllImport("*", ExactSpelling = true, EntryPoint = "VTableResolver_GetCommonCallingStub")] + private static extern unsafe IntPtr VTableResolver_GetCommonCallingStub(); + + /// + /// Build initial array of vtable thunks. These thunks are the ones directly embedded in + /// the typeloader codebase instead of being dynamically generated out of the thunk pool. + /// + private static IntPtr[] InitialThunks() + { + IntPtr firstResolverThunk; + int thunkCount; + int thunkSize = VTableResolver_Init(out firstResolverThunk, + Intrinsics.AddrOf(new Func(VTableResolveThunk)), + RuntimeAugments.GetUniversalTransitionThunk(), + out thunkCount); + IntPtr[] initialThunks = new IntPtr[thunkCount]; + for (int i = 0; i < thunkCount; i++) + { + unsafe + { + initialThunks[i] = (IntPtr)(((byte*)firstResolverThunk) + (thunkSize * i)); + } + } + + return initialThunks; + } + + /// + /// Get a thunk for resolving a call on a particular vtable slot index. + /// + public static IntPtr GetThunkForSlot(int slotIndex) + { + IntPtr[] currentThunks = s_thunks; + if ((currentThunks.Length > slotIndex) && (currentThunks[slotIndex] != IntPtr.Zero)) + return currentThunks[slotIndex]; + else + { + lock (s_lockObject) + { + currentThunks = s_thunks; + + if ((currentThunks.Length > slotIndex) && (currentThunks[slotIndex] != IntPtr.Zero)) + return currentThunks[slotIndex]; + + if ((currentThunks.Length <= slotIndex)) + { + // Need to grow thunk collection + int newLength = currentThunks.Length; + while (newLength <= slotIndex) + newLength *= 2; + + Array.Resize(ref currentThunks, newLength); + s_thunks = currentThunks; + } + + // Now that we are certain that the array has enough space to store the thunk pointer, + // create a thunk. + return currentThunks[slotIndex] = CreateDynamicThunk(slotIndex); + } + } + } + + private static IntPtr CreateDynamicThunk(int slotIndex) + { + if (s_lazyVtableThunksPoolHeap == null) + { + s_lazyVtableThunksPoolHeap = RuntimeAugments.CreateThunksHeap(VTableResolver_GetCommonCallingStub()); + Debug.Assert(s_lazyVtableThunksPoolHeap != null); + } + + IntPtr thunk = RuntimeAugments.AllocateThunk(s_lazyVtableThunksPoolHeap); + if (thunk == IntPtr.Zero) + Environment.FailFast("Not enough thunks for calling convention converter"); + + int eetypeVtableOffset = SlotIndexToEETypeVTableOffset(slotIndex); + + RuntimeAugments.SetThunkData(s_lazyVtableThunksPoolHeap, thunk, new IntPtr(eetypeVtableOffset), thunk); + + return thunk; + } + + /// + /// Convert a virtual slot index to a vtable offset from the start of an MethodTable + /// + public static unsafe int SlotIndexToEETypeVTableOffset(int slotIndex) + { + if (slotIndex < 0) + throw new BadImageFormatException(); + + return sizeof(MethodTable) + (slotIndex * IntPtr.Size); + } + + /// + /// Get the thunk that can represent all finalizers of metadata represented objects. + /// + public static IntPtr GetFinalizerThunk() + { + // Here we take advantage of the detail that the calling convention abi for a static function + // that returns no values, and takes a single object parameter matches that of a + // instance function that takes no parameters + return Intrinsics.AddrOf(new Action(FinalizeThunk)); + } + + private static unsafe int EETypeVTableOffsetToSlotIndex(int eeTypeVTableOffset) + { + return (eeTypeVTableOffset - sizeof(MethodTable)) / IntPtr.Size; + } + + /// + /// This function is called from the lazy vtable resolver thunks via the UniversalTransitionThunk to compute + /// the correct resolution of a virtual dispatch. + /// + /// pointer to the arguments of the called function + /// eeTypePointerOffsetAsIntPtr is the offset from the start of the MethodTable to the vtable slot + /// function pointer of correct override of virtual function + private static unsafe IntPtr VTableResolveThunk(IntPtr callerTransitionBlockParam, IntPtr eeTypePointerOffsetAsIntPtr) + { + int eeTypePointerOffset = (int)eeTypePointerOffsetAsIntPtr; + int vtableSlotIndex = EETypeVTableOffsetToSlotIndex(eeTypePointerOffset); + Debug.Assert(eeTypePointerOffset == SlotIndexToEETypeVTableOffset(vtableSlotIndex)); // Assert that the round trip through the slot calculations is good + + MethodTable** thisPointer = *((MethodTable***)(((byte*)callerTransitionBlockParam) + ArgIterator.GetThisOffset())); + MethodTable* MethodTable = *thisPointer; + + RuntimeTypeHandle rth = MethodTable->ToRuntimeTypeHandle(); + + TypeSystemContext context = TypeSystemContextFactory.Create(); + TypeDesc type = context.ResolveRuntimeTypeHandle(rth); + + IntPtr functionPointer = ResolveVirtualVTableFunction(type, vtableSlotIndex); + MethodTable->GetVTableStartAddress()[vtableSlotIndex] = functionPointer; + + TypeSystemContextFactory.Recycle(context); + + return functionPointer; + } + + /// + /// This thunk is used to lazily resolve Finalizer calls + /// + /// Object to be finalized + private static void FinalizeThunk(object obj) + { + RuntimeTypeHandle rthType = RuntimeAugments.GetRuntimeTypeHandleFromObjectReference(obj); + TypeSystemContext context = TypeSystemContextFactory.Create(); + TypeDesc type = context.ResolveRuntimeTypeHandle(rthType); + + MethodDesc finalizer = type.GetFinalizer(); + IntPtr fnPtrFinalizer; + if (!TryGetVTableCallableAddress(finalizer, out fnPtrFinalizer)) + { + Environment.FailFast("Method address lookup failed for: " + finalizer.ToString()); + } + + TypeSystemContextFactory.Recycle(context); + + unsafe + { + rthType.ToEETypePtr()->FinalizerCode = fnPtrFinalizer; + } + + // Call the finalizer directly. No need to play tricks with tail calling, as this is rare enough, it shouldn't happen much + + // Here we take advantage of the detail that the calling convention abi for a static function + // that returns no values, and takes a single object parameter matches that of a + // instance function that takes no parameters + Intrinsics.Call(fnPtrFinalizer, obj); + } + + private static IntPtr ResolveVirtualVTableFunction(TypeDesc type, int vtableSlotIndex) + { + IntPtr exactResult; + MethodDesc virtualFunctionDefiningSlot = ResolveVTableSlotIndexToMethodDescOrFunctionPointer(type.GetClosestDefType(), vtableSlotIndex, out exactResult); + if (virtualFunctionDefiningSlot == null) + return exactResult; + + MethodDesc virtualFunctionOverride = ((MetadataType)type.GetClosestDefType()).FindVirtualFunctionTargetMethodOnObjectType(virtualFunctionDefiningSlot); + + if (TryGetVTableCallableAddress(virtualFunctionOverride, out exactResult)) + return exactResult; + + Environment.FailFast("Method address lookup failed for: " + virtualFunctionOverride.ToString()); + return IntPtr.Zero; + } + + /// + /// Get the virtual slot index of a given virtual method. (This is only valid for non-interface virtuals) + /// + /// virtual method to get slot index of + /// slot index, or -1 + public static int VirtualMethodToSlotIndex(MethodDesc virtualMethod) + { + Debug.Assert(virtualMethod.IsVirtual); + Debug.Assert(!virtualMethod.OwningType.IsInterface); + + MethodDesc definingMethod = virtualMethod; + + // If not newslot, make sure we've got the defining method here + if (!definingMethod.IsNewSlot) + { + definingMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(definingMethod); + } + TypeDesc definingType = definingMethod.OwningType; + + // Two possible cases for determining slot index that will work + // 1. The definingType is a R2R type with full metadata. Compute the MethodDesc, by scanning the list of virtuals present in metadata. Its possible to not get a slot index. In that case return -1 + // 2. The definingType is pregenerated, but we can go from metadata to slot index via the runtime mapping tables. + + if (!IsPregeneratedOrTemplateTypeLoaded(definingType)) + { + // Case 1 + + MetadataType definingMetadataType = (MetadataType)definingType; + int baseTypeSlotCount = 0; + + if (definingMetadataType.BaseType != null) + { + unsafe + { + if (definingMetadataType.BaseType.RetrieveRuntimeTypeHandleIfPossible()) + { + baseTypeSlotCount = definingMetadataType.BaseType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; + } + else + { + baseTypeSlotCount = definingMetadataType.BaseType.GetOrCreateTypeBuilderState().NumVTableSlots; + } + } + } + + int currentSlot = baseTypeSlotCount; + + if (definingMetadataType.ConvertToCanonForm(CanonicalFormKind.Specific).IsCanonicalSubtype(CanonicalFormKind.Specific)) + { + // Deal with the space reserved for the canonical dictionary + currentSlot++; + } + + foreach (MethodDesc method in definingMetadataType.GetMethods()) + { + if (!MethodDefinesVTableSlot(method)) + continue; + + if (method == definingMethod) + return currentSlot; + else + currentSlot++; + } + + // No slot index defined. + return -1; + } + else + { + // Case 2, pregenerated type + TypeSystem.NativeFormat.NativeFormatMethod definingMethodOpenType = (TypeSystem.NativeFormat.NativeFormatMethod)definingMethod.GetTypicalMethodDefinition(); + MethodSignatureComparer methodSignatureComparer = new MethodSignatureComparer( + definingMethodOpenType.MetadataReader, definingMethodOpenType.Handle); + + if (!definingType.RetrieveRuntimeTypeHandleIfPossible()) + { + new TypeBuilder().BuildType(definingType); + } + + TypeSystem.NativeFormat.NativeFormatType definingNativeFormatType = (TypeSystem.NativeFormat.NativeFormatType)definingType.GetTypeDefinition(); + NativeFormatModuleInfo moduleToLookIn = definingNativeFormatType.MetadataUnit.RuntimeModuleInfo; + + TypeLoaderEnvironment.VirtualResolveDataResult virtualSlotInfo; + if (!TypeLoaderEnvironment.TryGetVirtualResolveData(moduleToLookIn, definingType.RuntimeTypeHandle, Array.Empty(), ref methodSignatureComparer, out virtualSlotInfo)) + return -1; + + Debug.Assert(!virtualSlotInfo.IsGVM); + return virtualSlotInfo.SlotIndex; + } + } + + /// + /// Given a type, and vtable slot index, compute either the NativeFormatMethod that defines that vtable slot, + /// OR the implementation function pointer if the method doesn't have sufficient metadata to be interesting + /// to use the virtual function resolution algorithm on. + /// + /// Type on which virtual resolution is to be completed + /// Virtual slot index which is to be examined + /// If there is no corresponding method defined in metadata, this is + /// the function pointer that should be used for calls to this vtable slot + /// MethodDesc of function that defined the slot if possible. + private static unsafe MethodDesc ResolveVTableSlotIndexToMethodDescOrFunctionPointer(DefType type, int vtableSlotIndex, out IntPtr functionPointer) + { + Debug.Assert(type.RetrieveRuntimeTypeHandleIfPossible()); + Debug.Assert(type.RuntimeTypeHandle.ToEETypePtr()->NumVtableSlots > vtableSlotIndex); + DefType definingTypeScan = type; + DefType previousDefiningType = null; + MethodTable* typePtr = null; + DefType definingType = null; + functionPointer = IntPtr.Zero; + + while (true) + { + definingTypeScan.RetrieveRuntimeTypeHandleIfPossible(); + Debug.Assert(!definingTypeScan.RuntimeTypeHandle.IsNull()); + typePtr = definingTypeScan.RuntimeTypeHandle.ToEETypePtr(); + if (typePtr->NumVtableSlots > vtableSlotIndex) + { + previousDefiningType = definingTypeScan; + definingTypeScan = definingTypeScan.BaseType; + + // We found a slot on System.Object + if (definingTypeScan == null) + { + definingType = previousDefiningType; + break; + } + } + else + { + // We've gone past the type in the type hierarchy that declared this vtable slot + // the defining type is the one we looked at previously + definingType = previousDefiningType; + break; + } + } + + // At this point, we know the type that definined the virtual slot + // There are 4 possibilities here + // 1. The definingType is a R2R type with full metadata. Compute the MethodDesc, by scanning the list of virtuals present in metadata + // 2. The definingType is pregenerated, but we can go from the slot index to metadata via the runtime mapping tables. Do so, then run + // normal algorithm + // 3. The definingType is pregenerated, but we cannot go from the slot index to metadata via the runtime mapping tables. There is + // only 1 pointer in the vtable of the most derived pregenerated type that has the same value. That's the valuable pointer. + // 4. The definingType is pregenerated, but we cannot go from the slot index to metadata via the runtime mapping tables. There are + // multiple pointers in the vtable of the most derived pregenerated types which have this same value. + // - Take that pointer value, and attempt to resolve back to a method from the implementation. If that succeeds, then + // treat that as the correct vtable slot. Otherwise, return that function pointer. (This is a very rare scenario.) + MethodDesc slotDefiningMethod = null; + if (!IsPregeneratedOrTemplateTypeLoaded(definingType)) + { + // Case 1 + + MetadataType definingMetadataType = (MetadataType)definingType; + int baseTypeSlotCount = 0; + + if (definingMetadataType.BaseType != null) + baseTypeSlotCount = definingMetadataType.BaseType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; + + int slotOnType = vtableSlotIndex - baseTypeSlotCount; + Debug.Assert(slotOnType >= 0); + + // R2R types create new slots only for methods that are marked as NewSlot + if (definingMetadataType.ConvertToCanonForm(CanonicalFormKind.Specific) != definingType) + { + // Deal with the space reserved for the canonical dictionary + slotOnType--; + } + Debug.Assert(slotOnType >= 0); + + int currentSlot = 0; + foreach (MethodDesc method in definingMetadataType.GetMethods()) + { + if (!MethodDefinesVTableSlot(method)) + continue; + + if (currentSlot == slotOnType) + { + Debug.Assert(VirtualMethodToSlotIndex(method) == vtableSlotIndex); + return method; + } + else + currentSlot++; + } + + Environment.FailFast("Unexpected failure to find virtual function that defined slot"); + return null; + } + else if (TryGetVirtualMethodFromSlot(definingType, vtableSlotIndex, out slotDefiningMethod)) + { + // Case 2 + Debug.Assert(VirtualMethodToSlotIndex(slotDefiningMethod) == vtableSlotIndex); + return slotDefiningMethod; + } + else + { + TypeDesc mostDerivedPregeneratedType = GetMostDerivedPregeneratedOrTemplateLoadedType(type); + + MethodTable* mostDerivedTypeEEType = mostDerivedPregeneratedType.GetRuntimeTypeHandle().ToEETypePtr(); + IntPtr* vtableStart = (IntPtr*)(((byte*)mostDerivedTypeEEType) + sizeof(MethodTable)); + + IntPtr possibleFunctionPointerReturn = vtableStart[vtableSlotIndex]; + int functionPointerMatches = 0; + for (int i = 0; i < mostDerivedTypeEEType->NumVtableSlots; i++) + { + if (vtableStart[i] == possibleFunctionPointerReturn) + functionPointerMatches++; + } + + if (functionPointerMatches == 1) + { + // Case 3 + functionPointer = possibleFunctionPointerReturn; + return null; + } + else + { + // Case 4 + // While this case is theoretically possible, it requires MethodImpl to MethodImpl overloading for virtual functions + // in the non-ready to run portions of the binary. Given our current shipping plans, as this does not occur in non-obfuscated + // code, we will throw NotImplementedException here. + // The real implementation would look something like + // if (!TryGetNativeFormatMethodFromFunctionPointer(possibleFunctionPointerReturn, out method)) + // { + // // this method could not have been overriden in dynamically loaded code + // functionPointer = possibleFunctionPointerReturn; + // return null; + // } + // else + // { + // return VirtualFunctionAlgorithm.GetDefiningMethod(method) + // } + // + throw NotImplemented.ByDesign; + } + } + } + + private static bool TryGetVirtualMethodFromSlot(TypeDesc definingType, int vtableSlotIndex, out MethodDesc slotDefiningMethod) + { + MethodNameAndSignature methodNameAndSig; + bool success = TypeLoaderEnvironment.TryGetMethodMethodNameAndSigFromVTableSlotForPregeneratedOrTemplateType + (definingType.Context, definingType.GetRuntimeTypeHandle(), vtableSlotIndex, out methodNameAndSig); + + if (!success) + { + slotDefiningMethod = null; + return false; + } + + TypeSystem.NativeFormat.NativeFormatType metadataDefiningType = definingType.GetClosestDefType().GetTypeDefinition() as TypeSystem.NativeFormat.NativeFormatType; + + // We're working with a NoMetadataType, or an ArrayType, neither of which have full metadata + if (metadataDefiningType == null) + { + slotDefiningMethod = null; + return false; + } + + // TryGetMethodMethodNameAndSigFromVTableSlotForPregeneratedOrTemplateType is expected to only return methodNameAndSig with NativeLayoutSignatures in them. + // If we start hitting the more general case, we can improve this algorithm. + Debug.Assert(methodNameAndSig.Signature.IsNativeLayoutSignature); + + foreach (TypeSystem.NativeFormat.NativeFormatMethod method in metadataDefiningType.GetMethods()) + { + if (!method.IsVirtual) + continue; + + if (method.HasInstantiation) + continue; + + if (!method.Name.Equals(methodNameAndSig.Name)) + continue; + + MethodSignatureComparer sigComparer = new MethodSignatureComparer(method.MetadataReader, method.Handle); + if (!sigComparer.IsMatchingNativeLayoutMethodNameAndSignature(methodNameAndSig.Name, methodNameAndSig.Signature)) + continue; + + // At this point we've matched + slotDefiningMethod = method; + return true; + } + + // Didn't find the method + slotDefiningMethod = null; + return false; + } + + /// + /// Find the type in a type's hierarchy that wasn't generated based on metadata. + /// + /// found type, or null + public static TypeDesc GetMostDerivedPregeneratedOrTemplateLoadedType(TypeDesc derivedType) + { + while ((derivedType != null) && !IsPregeneratedOrTemplateTypeLoaded(derivedType)) + { + derivedType = derivedType.BaseType; + } + + return derivedType; + } + + public static bool IsPregeneratedOrTemplateTypeLoaded(TypeDesc derivedType) + { + Debug.Assert(!derivedType.IsInterface); + + DefType defTypeDerived = derivedType as DefType; + + if (defTypeDerived == null) + { + return true; + } + else + { + if (!(defTypeDerived is TypeSystem.NoMetadata.NoMetadataType) && !defTypeDerived.HasNativeLayout) + { + if (!defTypeDerived.RetrieveRuntimeTypeHandleIfPossible()) + return false; + + unsafe + { + return !defTypeDerived.RuntimeTypeHandle.ToEETypePtr()->IsDynamicType; + } + } + return true; + } + } + + /// + /// Does a method actually recieve a VTable slot. (Must not be called for interface methods) + /// + /// + /// + public static bool MethodDefinesVTableSlot(MethodDesc method) + { + Debug.Assert(!method.OwningType.IsInterface); + + if (!method.IsVirtual) + return false; + if (!method.IsNewSlot) + return false; + // Sealed virtual methods go after normal slots at the end of VTable + if (method.IsVirtual && method.IsFinal) + return false; + // Generic virtuals are not in the vtable + if (method.HasInstantiation) + return false; + + return true; + } + + /// + /// Get the runtime interface slot number given an interface method + /// + public static bool TryGetInterfaceSlotNumberFromMethod(MethodDesc method, out ushort slot) + { + slot = 0; + + if (!method.OwningType.IsInterface) + return false; + + int iSlot = 0; + foreach (MethodDesc m in method.OwningType.GetMethods()) + { + if (m == method) + { + if (method.OwningType.IsGeneric()) + { + slot = checked((ushort)(iSlot + 1)); + } + else + { + slot = checked((ushort)(iSlot)); + } + return true; + } + iSlot++; + } + + return false; + } + + /// + /// Get the MethodDesc that corresponds to an interface type/slot pair + /// + public static bool TryGetMethodFromInterfaceSlot(TypeDesc owningType, ushort slot, out MethodDesc method) + { + int iMethod = -1; + method = null; + + // This is only valid to call on an interface + if (!owningType.IsInterface) + return false; + + // Slots on generic interface types are off by one. + if (owningType.IsGeneric()) + { + if (slot == 0) + throw new BadImageFormatException(); + + slot--; + } + + foreach (MethodDesc searchMethod in owningType.GetMethods()) + { + if (searchMethod.IsVirtual) + { + iMethod++; + if (iMethod == slot) + { + method = searchMethod; + return true; + } + } + } + + return false; + } + + /// + /// Resolve a call on the interface method targetVirtualMethod, on the type instanceDefTypeToExamine utilizing metadata to + /// its associated virtual method. + /// + /// (in) The class type on which the interface call is made, (out) the class type where the search may continue using non-metadata means + /// The interface method to translate into a virtual method for execution + /// virtual method slot which implements the interface method OR null if an implementation should fall back to non-metadata based lookup. + public static MethodDesc ResolveInterfaceMethodToVirtualMethod(TypeDesc instanceType, out TypeDesc instanceDefTypeToExamine, MethodDesc targetVirtualMethod) + { + instanceDefTypeToExamine = instanceType.GetClosestDefType(); + + MethodDesc newlyFoundVirtualMethod = null; + LowLevelList variantTargets = null; + + if (targetVirtualMethod.OwningType.HasVariance) + { + foreach (TypeDesc type in instanceType.RuntimeInterfaces) + { + if (type != targetVirtualMethod.OwningType && + type.GetTypeDefinition() == targetVirtualMethod.OwningType.GetTypeDefinition()) + { + // Check to see if these interfaces are appropriately assignable + if (RuntimeAugments.IsAssignableFrom(targetVirtualMethod.OwningType.GetRuntimeTypeHandle(), type.GetRuntimeTypeHandle())) + { + if (variantTargets == null) + variantTargets = new LowLevelList(); + + MethodDesc targetVariantMatch = type.Context.GetMethodForInstantiatedType( + targetVirtualMethod.GetTypicalMethodDefinition(), + (InstantiatedType)type); + + variantTargets.Add(targetVariantMatch); + } + } + } + } + + do + { + newlyFoundVirtualMethod = instanceDefTypeToExamine.ResolveInterfaceMethodToVirtualMethodOnType(targetVirtualMethod); + + if (newlyFoundVirtualMethod == null && variantTargets != null) + { + for (int i = 0; i < variantTargets.Count; i++) + { + newlyFoundVirtualMethod = instanceDefTypeToExamine.ResolveInterfaceMethodToVirtualMethodOnType(variantTargets[i]); + if (newlyFoundVirtualMethod != null) + break; + } + } + instanceDefTypeToExamine = instanceDefTypeToExamine.BaseType; + } while ((newlyFoundVirtualMethod == null) && (instanceDefTypeToExamine != null) && !IsPregeneratedOrTemplateTypeLoaded(instanceDefTypeToExamine)); + + return newlyFoundVirtualMethod; + } + + /// + /// Try to resolve a virtual call to targetMethod to its implementation on instanceType. + /// + /// non-interface type + /// non-generic virtual or interface method + /// function pointer resolved + /// true if successful + public static bool TryDispatchMethodOnTarget(TypeDesc instanceType, MethodDesc targetMethod, out IntPtr methodAddress) + { + methodAddress = IntPtr.Zero; + + if (targetMethod == null) + return false; + + if (IsPregeneratedOrTemplateTypeLoaded(instanceType)) + { + if (targetMethod.OwningType.IsInterface) + { + ushort interfaceSlot; + if (!TryGetInterfaceSlotNumberFromMethod(targetMethod, out interfaceSlot)) + { + return false; + } + methodAddress = RuntimeAugments.ResolveDispatchOnType(instanceType.GetRuntimeTypeHandle(), + targetMethod.OwningType.GetRuntimeTypeHandle(), + interfaceSlot); + Debug.Assert(methodAddress != IntPtr.Zero); // TODO! This should happen for IDynamicInterfaceCastable dispatch... + return true; + } + else + { + unsafe + { + int vtableSlotIndex = LazyVTableResolver.VirtualMethodToSlotIndex(targetMethod); + MethodTable* MethodTable = instanceType.GetRuntimeTypeHandle().ToEETypePtr(); + IntPtr* vtableStart = (IntPtr*)(((byte*)MethodTable) + sizeof(MethodTable)); + + methodAddress = vtableStart[vtableSlotIndex]; + return true; + } + } + } + + MethodDesc targetVirtualMethod = targetMethod; + DefType instanceDefType = instanceType.GetClosestDefType(); + + // For interface resolution, its a two step process, first get the virtual slot + if (targetVirtualMethod.OwningType.IsInterface) + { + TypeDesc instanceDefTypeToExamine; + MethodDesc newlyFoundVirtualMethod = ResolveInterfaceMethodToVirtualMethod(instanceType, out instanceDefTypeToExamine, targetVirtualMethod); + + targetVirtualMethod = newlyFoundVirtualMethod; + + // The pregenerated type must be the one that implements the interface method + // Call into Redhawk to deal with this. + if ((newlyFoundVirtualMethod == null) && (instanceDefTypeToExamine != null)) + { + ushort interfaceSlot; + if (!TryGetInterfaceSlotNumberFromMethod(targetMethod, out interfaceSlot)) + { + return false; + } + methodAddress = RuntimeAugments.ResolveDispatchOnType(instanceDefTypeToExamine.GetRuntimeTypeHandle(), + targetMethod.OwningType.GetRuntimeTypeHandle(), + interfaceSlot); + + Debug.Assert(methodAddress != IntPtr.Zero); // TODO! This should happen for IDynamicInterfaceCastable dispatch... + return true; + } + } + + // VirtualSlot can be null if the interface method isn't really implemented. This should never happen, but since our + // type loader doesn't check all interface overloads at load time, it could happen + if (targetVirtualMethod == null) + return false; + + // Resolve virtual method to exact method + MethodDesc dispatchMethod = instanceDefType.FindVirtualFunctionTargetMethodOnObjectType(targetVirtualMethod); + + return TryGetVTableCallableAddress(dispatchMethod, out methodAddress); + } + + /// + /// Get an address that can go into a vtable from a method desc + /// Based on the structure of our code, these functions are always Instance, non-generic methods + /// and therefore, we don't need to be concerned about an extra generic dictionary parameter + /// + private static bool TryGetVTableCallableAddress(MethodDesc method, out IntPtr result) + { + TypeLoaderEnvironment.MethodAddressType dummy; + IntPtr methodAddressNonUnboxing; + IntPtr unboxingMethodAddress; + + if (TypeLoaderEnvironment.TryGetMethodAddressFromMethodDesc(method, out methodAddressNonUnboxing, out unboxingMethodAddress, out dummy)) + { + if (unboxingMethodAddress != IntPtr.Zero) + { + result = unboxingMethodAddress; + } + else + { + result = methodAddressNonUnboxing; + } + return true; + } + + result = IntPtr.Zero; + return false; + } + } +#endif +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LockFreeObjectInterner.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LockFreeObjectInterner.cs new file mode 100644 index 00000000000000..e107107ea09c4c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LockFreeObjectInterner.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +namespace Internal.TypeSystem +{ + public class LockFreeObjectInterner : LockFreeReaderHashtableOfPointers + { + static LockFreeObjectInterner s_interner = new LockFreeObjectInterner(); + public static GCHandle GetInternedObjectHandle(object obj) + { + return s_interner.GetOrCreateValue(obj); + } + + /// + /// Given a key, compute a hash code. This function must be thread safe. + /// + protected override int GetKeyHashCode(object key) + { + return key.GetHashCode(); + } + + /// + /// Given a value, compute a hash code which would be identical to the hash code + /// for a key which should look up this value. This function must be thread safe. + /// + protected override int GetValueHashCode(GCHandle value) + { + return value.Target.GetHashCode(); + } + + /// + /// Compare a key and value. If the key refers to this value, return true. + /// This function must be thread safe. + /// + protected override bool CompareKeyToValue(object key, GCHandle value) + { + return key == value.Target; + } + + /// + /// Compare a value with another value. Return true if values are equal. + /// This function must be thread safe. + /// + protected override bool CompareValueToValue(GCHandle value1, GCHandle value2) + { + return value1.Target == value2.Target; + } + + /// + /// Create a new value from a key. Must be threadsafe. Value may or may not be added + /// to collection. Return value must not be null. + /// + protected override GCHandle CreateValueFromKey(object key) + { + return GCHandle.Alloc(key); + } + + /// + /// Convert a value to an IntPtr for storage into the hashtable + /// + protected override IntPtr ConvertValueToIntPtr(GCHandle value) + { + return GCHandle.ToIntPtr(value); + } + + /// + /// Convert an IntPtr into a value for comparisions, or for returning. + /// + protected override GCHandle ConvertIntPtrToValue(IntPtr pointer) + { + return GCHandle.FromIntPtr(pointer); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LowLevelStringConverter.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LowLevelStringConverter.cs new file mode 100644 index 00000000000000..7b90a25fae3e5f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LowLevelStringConverter.cs @@ -0,0 +1,152 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Text; + +using System.Reflection.Runtime.General; + +using Internal.Metadata.NativeFormat; +using Internal.Runtime.Augments; +using Internal.Runtime.TypeLoader; +using Internal.TypeSystem; + +namespace System +{ + internal static class TypeLoaderFormattingHelpers + { + public static string ToStringInvariant(this int arg) + { + return arg.LowLevelToString(); + } + + public static string ToStringInvariant(this uint arg) + { + return arg.LowLevelToString(); + } + + public static string ToStringInvariant(this byte arg) + { + return arg.LowLevelToString(); + } + + public static string ToStringInvariant(this ushort arg) + { + return arg.LowLevelToString(); + } + + public static string ToStringInvariant(this ulong arg) + { + return arg.LowLevelToString(); + } + + public static string ToStringInvariant(this float arg) + { + return "FLOAT"; + } + + public static string ToStringInvariant(this double arg) + { + return "DOUBLE"; + } + } +} + +namespace Internal.Runtime.TypeLoader +{ + /// + /// Extension methods that provide low level ToString() equivalents for some of the core types. + /// Calling regular ToString() on these types goes through a lot of the CultureInfo machinery + /// which is not low level enough for the type loader purposes. + /// + internal static partial class LowLevelStringConverter + { + private const string HexDigits = "0123456789ABCDEF"; + + private static string LowLevelToString(ulong arg, int shift) + { + StringBuilder sb = new StringBuilder(16); + while (shift > 0) + { + shift -= 4; + int digit = (int)((arg >> shift) & 0xF); + sb.Append(HexDigits[digit]); + } + return sb.ToString(); + } + + public static string LowLevelToString(this LayoutInt arg) + { + if (arg.IsIndeterminate) + return "Indeterminate"; + else + return ((uint)arg.AsInt).LowLevelToString(); + } + + public static string LowLevelToString(this byte arg) + { + return LowLevelToString((ulong)arg, 4 * 2); + } + + public static string LowLevelToString(this ushort arg) + { + return LowLevelToString((ulong)arg, 4 * 4); + } + + public static string LowLevelToString(this int arg) + { + return ((uint)arg).LowLevelToString(); + } + + public static string LowLevelToString(this uint arg) + { + return LowLevelToString((ulong)arg, 4 * 8); + } + + public static string LowLevelToString(this ulong arg) + { + return LowLevelToString((ulong)arg, 4 * 16); + } + + public static string LowLevelToString(this IntPtr arg) + { + return LowLevelToString((ulong)arg, IntPtr.Size * 8); + } + + public static string LowLevelToString(this RuntimeTypeHandle rtth) + { + TypeReferenceHandle typeRefHandle; + QTypeDefinition qTypeDefinition; + MetadataReader reader; + + // Try to get the name from metadata + if (TypeLoaderEnvironment.Instance.TryGetMetadataForNamedType(rtth, out qTypeDefinition)) + { +#if ECMA_METADATA_SUPPORT + string result = EcmaMetadataFullName(qTypeDefinition); + if (result != null) + return result; +#endif + + reader = qTypeDefinition.NativeFormatReader; + TypeDefinitionHandle typeDefHandle = qTypeDefinition.NativeFormatHandle; + return typeDefHandle.GetFullName(reader); + } + + // Try to get the name from diagnostic metadata + if (TypeLoaderEnvironment.TryGetTypeReferenceForNamedType(rtth, out reader, out typeRefHandle)) + { + return typeRefHandle.GetFullName(reader); + } + + // Fallback implementation when no metadata available + return LowLevelToStringRawEETypeAddress(rtth); + } + + public static string LowLevelToStringRawEETypeAddress(this RuntimeTypeHandle rtth) + { + return "MethodTable:0x" + LowLevelToString(rtth.ToIntPtr()); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataFixupKind.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataFixupKind.cs new file mode 100644 index 00000000000000..7674df664a4ff8 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataFixupKind.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.Runtime.TypeLoader +{ + public enum MetadataFixupKind + { + // Metadata fixups that apply to type tokens + TypeHandle = 0x0, + GcStaticData = 0x1, + NonGcStaticData = 0x2, + UnwrapNullableType = 0x3, + TypeSize = 0x4, + AllocateObject = 0x5, + DefaultConstructor = 0x6, + // unused = 0x7, + // unused = 0x8, + IsInst = 0x9, + CastClass = 0xa, + AllocateArray = 0xb, + CheckArrayElementType = 0xc, + ArrayOfTypeHandle = 0xd, + DirectGcStaticData = 0xe, + DirectNonGcStaticData = 0xf, + // Insert new fixups applying to type tokens by creating a new block of fixups which apply to type tokens + EndTypeTokenFixups, + + // Metadata fixups that apply to method tokens + VirtualCallDispatch = 0x10, + MethodDictionary = 0x11, + MethodLdToken = 0x12, + Method = 0x13, + UnboxingStubMethod = 0x14, + CallableMethod = 0x15, + + // Insert new fixups applying to method tokens before this point + EndMethodTokenFixups, + + // Metadata fixups that apply to a type/method token pair + NonGenericDirectConstrainedMethod = 0x20, + NonGenericConstrainedMethod = 0x21, + GenericConstrainedMethod = 0x22, + + // Insert new fixups applying to type/method token pairs before this point + EndConstraintMethodFixups, + + // Metadata fixups that apply to a field token + FieldLdToken = 0x30, + FieldOffset = 0x31, + + // Insert new fixups applying to field tokens before this point + EndFieldTokenFixups, + + // Metadata fixups that apply to a signature token + CallingConventionConverter_NoInstantiatingParam = 0x40, + CallingConventionConverter_HasInstantiatingParam = 0x41, + CallingConventionConverter_MaybeInstantiatingParam = 0x42, + + // Insert new fixups applying to signature tokens before this point + EndSignatureTokenFixups, + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataNameExtensions.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataNameExtensions.cs new file mode 100644 index 00000000000000..6c65c87109bc2c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataNameExtensions.cs @@ -0,0 +1,315 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using System.Reflection; +using System.Collections.Generic; +using Debug = System.Diagnostics.Debug; + +using global::Internal.Metadata.NativeFormat; + +namespace Internal.Runtime.TypeLoader +{ + internal static class MetadataNameExtentions + { + public static string GetFullName(this Handle handle, MetadataReader reader) + { + switch (handle.HandleType) + { + case HandleType.TypeDefinition: + return handle.ToTypeDefinitionHandle(reader).GetFullName(reader); + case HandleType.TypeReference: + return handle.ToTypeReferenceHandle(reader).GetFullName(reader); + + case HandleType.NamespaceDefinition: + return handle.ToNamespaceDefinitionHandle(reader).GetFullName(reader); + case HandleType.NamespaceReference: + return handle.ToNamespaceReferenceHandle(reader).GetFullName(reader); + + case HandleType.TypeSpecification: + return handle.ToTypeSpecificationHandle(reader).GetFullName(reader); + case HandleType.TypeInstantiationSignature: + return handle.ToTypeInstantiationSignatureHandle(reader).GetFullName(reader); + + case HandleType.ArraySignature: + return handle.ToArraySignatureHandle(reader).GetFullName(reader); + case HandleType.SZArraySignature: + return handle.ToSZArraySignatureHandle(reader).GetFullName(reader); + + case HandleType.PointerSignature: + return handle.ToPointerSignatureHandle(reader).GetFullName(reader); + case HandleType.ByReferenceSignature: + return handle.ToByReferenceSignatureHandle(reader).GetFullName(reader); + + case HandleType.ScopeDefinition: + return handle.ToScopeDefinitionHandle(reader).GetFullName(reader); + case HandleType.ScopeReference: + return handle.ToScopeReferenceHandle(reader).GetFullName(reader); + } + return null; + } + + public static string GetFullName(this ByReferenceSignatureHandle handle, MetadataReader reader) + { + var result = handle.GetByReferenceSignature(reader).Type.GetFullName(reader); + if (result == null) return null; + return result + "&"; + } + + public static string GetFullName(this PointerSignatureHandle handle, MetadataReader reader) + { + var result = handle.GetPointerSignature(reader).Type.GetFullName(reader); + if (result == null) return null; + return result + "*"; + } + + public static string GetFullName(this ArraySignatureHandle handle, MetadataReader reader) + { + ArraySignature array = handle.GetArraySignature(reader); + var result = array.ElementType.GetFullName(reader); + if (result == null) return null; + return result + "[" + (new string(',', array.Rank - 1)) + "]"; + } + + public static string GetFullName(this SZArraySignatureHandle handle, MetadataReader reader) + { + var result = handle.GetSZArraySignature(reader).ElementType.GetFullName(reader); + if (result == null) return null; + return result + "[]"; + } + + public static string GetFullName(this TypeSpecificationHandle typeSpecHandle, MetadataReader reader) + { + var typeSpec = typeSpecHandle.GetTypeSpecification(reader); + + if (typeSpec.Signature.IsNull(reader)) + return null; + + return typeSpec.Signature.GetFullName(reader); + } + + public static string GetFullName(this TypeInstantiationSignatureHandle typeInstSigHandle, MetadataReader reader) + { + var typeInstSig = typeInstSigHandle.GetTypeInstantiationSignature(reader); + + if (typeInstSig.GenericType.IsNull(reader)) + return null; + + var name = typeInstSig.GenericType.GetFullName(reader); + if (name == null) + return null; + + var index = 0; + string argsString = null; + foreach (var argHandle in typeInstSig.GenericTypeArguments) + { + if (index > 0) argsString += ","; + var argName = argHandle.GetFullName(reader); + if (argName == null) return name; + argsString += argName; + index++; + } + return name + "<" + argsString + ">"; + } + + public static void GetFullName(this TypeDefinitionHandle typeDefHandle, MetadataReader reader, out string name, out string enclosing, out string nspace) + { + var typeDef = typeDefHandle.GetTypeDefinition(reader); + + Debug.Assert(!typeDef.Name.IsNull(reader)); + + name = typeDef.Name.GetConstantStringValue(reader).Value; + enclosing = typeDef.EnclosingType.IsNull(reader) ? null : typeDef.EnclosingType.GetFullName(reader); + nspace = typeDef.NamespaceDefinition.IsNull(reader) ? null : typeDef.NamespaceDefinition.GetFullName(reader); + } + + public static string GetFullName(this TypeDefinitionHandle typeDefHandle, MetadataReader reader) + { + string name; + string enclosing; + string nspace; + typeDefHandle.GetFullName(reader, out name, out enclosing, out nspace); + + if (enclosing != null && name != null) + return enclosing + "+" + name; + else if (nspace != null && name != null) + return nspace + "." + name; + + return name; + } + + public static string GetContainingModuleName(this TypeDefinitionHandle typeDefHandle, MetadataReader reader) + { + var typeDef = typeDefHandle.GetTypeDefinition(reader); + + Handle currentHandle = !typeDef.EnclosingType.IsNull(reader) ? (Handle)typeDef.EnclosingType : (Handle)typeDef.NamespaceDefinition; + Debug.Assert(!currentHandle.IsNull(reader)); + + while (!currentHandle.IsNull(reader)) + { + switch (currentHandle.HandleType) + { + case HandleType.TypeDefinition: + typeDef = currentHandle.ToTypeDefinitionHandle(reader).GetTypeDefinition(reader); + currentHandle = !typeDef.EnclosingType.IsNull(reader) ? (Handle)typeDef.EnclosingType : (Handle)typeDef.NamespaceDefinition; + break; + + case HandleType.NamespaceDefinition: + currentHandle = currentHandle.ToNamespaceDefinitionHandle(reader).GetNamespaceDefinition(reader).ParentScopeOrNamespace; + break; + + case HandleType.ScopeDefinition: + return currentHandle.GetFullName(reader); + + default: + return "?"; + } + } + + return "?"; + } + public static string GetFullName(this NamespaceDefinitionHandle namespaceHandle, MetadataReader reader) + { + var nspace = namespaceHandle.GetNamespaceDefinition(reader); + + if (nspace.Name.IsNull(reader)) + return null; + + var name = nspace.Name.GetConstantStringValue(reader).Value; + var containingNamespace = nspace.ParentScopeOrNamespace.IsNull(reader) ? null : nspace.ParentScopeOrNamespace.GetFullName(reader); + + if (containingNamespace != null) + return containingNamespace + "." + name; + + return name; + } + + public static void GetFullName(this TypeReferenceHandle typeRefHandle, MetadataReader reader, out string name, out string enclosing, out string nspace) + { + var typeRef = typeRefHandle.GetTypeReference(reader); + + Debug.Assert(!typeRef.TypeName.IsNull(reader)); + + name = typeRef.TypeName.GetConstantStringValue(reader).Value; + enclosing = typeRef.ParentNamespaceOrType.HandleType == HandleType.TypeReference ? typeRef.ParentNamespaceOrType.GetFullName(reader) : null; + nspace = typeRef.ParentNamespaceOrType.HandleType == HandleType.NamespaceReference ? typeRef.ParentNamespaceOrType.GetFullName(reader) : null; + } + + public static string GetFullName(this TypeReferenceHandle typeRefHandle, MetadataReader reader) + { + string name; + string enclosing; + string nspace; + typeRefHandle.GetFullName(reader, out name, out enclosing, out nspace); + + if (enclosing != null && name != null) + return enclosing + "+" + name; + else if (nspace != null && name != null) + return nspace + "." + name; + + return name; + } + + public static string GetContainingModuleName(this TypeReferenceHandle typeRefHandle, MetadataReader reader) + { + var typeRef = typeRefHandle.GetTypeReference(reader); + + Handle currentHandle = typeRef.ParentNamespaceOrType; + Debug.Assert(!currentHandle.IsNull(reader)); + + while (!currentHandle.IsNull(reader)) + { + switch (currentHandle.HandleType) + { + case HandleType.TypeReference: + case HandleType.NamespaceReference: + currentHandle = typeRef.ParentNamespaceOrType; + break; + + case HandleType.ScopeReference: + return currentHandle.GetFullName(reader); + + default: + return "?"; + } + } + + return "?"; + } + + public static string GetFullName(this NamespaceReferenceHandle namespaceHandle, MetadataReader reader) + { + var nspace = namespaceHandle.GetNamespaceReference(reader); + + if (nspace.Name.IsNull(reader)) + return null; + + var name = nspace.Name.GetConstantStringValue(reader).Value; + var containingNamespace = nspace.ParentScopeOrNamespace.IsNull(reader) ? null : nspace.ParentScopeOrNamespace.GetFullName(reader); + + if (containingNamespace != null) + return containingNamespace + "." + name; + + return name; + } + + public static string GetFullName(this ScopeDefinitionHandle scopeDefHandle, MetadataReader reader) + { + var scopeDef = scopeDefHandle.GetScopeDefinition(reader); + + Debug.Assert(!scopeDef.Name.IsNull(reader)); + + var assemblyName = new AssemblyName + { + Name = scopeDef.Name.GetConstantStringValue(reader).Value, + CultureName = scopeDef.Culture.IsNull(reader) ? null : scopeDef.Culture.GetConstantStringValue(reader).Value, + Version = new Version(scopeDef.MajorVersion, scopeDef.MinorVersion, scopeDef.BuildNumber, scopeDef.RevisionNumber) + }; + + if (scopeDef.PublicKey.Count > 0) + { + var pkt = new byte[scopeDef.PublicKey.Count]; + int index = 0; + foreach (var b in scopeDef.PublicKey) + pkt[index++] = b; + assemblyName.SetPublicKeyToken(pkt); + } + else + { + assemblyName.SetPublicKeyToken(Array.Empty()); + } + + return assemblyName.FullName; + } + + public static string GetFullName(this ScopeReferenceHandle scopeRefHandle, MetadataReader reader) + { + var scopeRef = scopeRefHandle.GetScopeReference(reader); + + Debug.Assert(!scopeRef.Name.IsNull(reader)); + + var assemblyName = new AssemblyName + { + Name = scopeRef.Name.GetConstantStringValue(reader).Value, + CultureName = scopeRef.Culture.IsNull(reader) ? null : scopeRef.Culture.GetConstantStringValue(reader).Value, + Version = new Version(scopeRef.MajorVersion, scopeRef.MinorVersion, scopeRef.BuildNumber, scopeRef.RevisionNumber) + }; + + if (scopeRef.PublicKeyOrToken.Count > 0) + { + var pkt = new byte[scopeRef.PublicKeyOrToken.Count]; + int index = 0; + foreach (var b in scopeRef.PublicKeyOrToken) + pkt[index++] = b; + assemblyName.SetPublicKeyToken(pkt); + } + else + { + assemblyName.SetPublicKeyToken(Array.Empty()); + } + + return assemblyName.FullName; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderExtensions.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderExtensions.cs new file mode 100644 index 00000000000000..fa4cc683585740 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderExtensions.cs @@ -0,0 +1,152 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using global::System; +using global::System.Reflection; +using global::System.Collections.Generic; + +using global::Internal.Metadata.NativeFormat; + +using Debug = System.Diagnostics.Debug; +using AssemblyFlags = Internal.Metadata.NativeFormat.AssemblyFlags; + +namespace System.Reflection.Runtime.General +{ + public static partial class MetadataReaderExtensions + { + /// + /// Convert raw token to a typed metadata handle. + /// + /// Token - raw integral handle representation + /// Token converted to handle + public static unsafe Handle AsHandle(this int token) + { + return *(Handle*)&token; + } + + /// + /// Convert raw token to a typed metadata handle. + /// + /// Token - raw integral handle representation + /// Token converted to handle + public static unsafe Handle AsHandle(this uint token) + { + return *(Handle*)&token; + } + + /// + /// Convert typed metadata handle to the raw token value. + /// + /// Typed metadata handle + /// Token - raw integral handle represented as signed int + public static unsafe int AsInt(this Handle handle) + { + return *(int*)&handle; + } + + /// + /// Convert typed metadata handle to the raw token value. + /// + /// Typed metadata handle + /// Token - raw integral handle represented as unsigned int + public static unsafe uint AsUInt(this Handle handle) + { + return *(uint*)&handle; + } + + public static string GetString(this ConstantStringValueHandle handle, MetadataReader reader) + { + return reader.GetConstantStringValue(handle).Value; + } + + // Useful for namespace Name string which can be a null handle. + public static string GetStringOrNull(this ConstantStringValueHandle handle, MetadataReader reader) + { + if (reader.IsNull(handle)) + return null; + return reader.GetConstantStringValue(handle).Value; + } + + public static bool IsTypeDefRefOrSpecHandle(this Handle handle, MetadataReader reader) + { + HandleType handleType = handle.HandleType; + return handleType == HandleType.TypeDefinition || + handleType == HandleType.TypeReference || + handleType == HandleType.TypeSpecification; + } + + public static bool IsTypeDefRefSpecOrModifiedTypeHandle(this Handle handle, MetadataReader reader) + { + HandleType handleType = handle.HandleType; + return handleType == HandleType.TypeDefinition || + handleType == HandleType.TypeReference || + handleType == HandleType.TypeSpecification || + handleType == HandleType.ModifiedType; + } + + public static RuntimeAssemblyName ToRuntimeAssemblyName(this ScopeDefinitionHandle scopeDefinitionHandle, MetadataReader reader) + { + ScopeDefinition scopeDefinition = scopeDefinitionHandle.GetScopeDefinition(reader); + return CreateRuntimeAssemblyNameFromMetadata( + reader, + scopeDefinition.Name, + scopeDefinition.MajorVersion, + scopeDefinition.MinorVersion, + scopeDefinition.BuildNumber, + scopeDefinition.RevisionNumber, + scopeDefinition.Culture, + scopeDefinition.PublicKey, + scopeDefinition.Flags + ); + } + + public static RuntimeAssemblyName ToRuntimeAssemblyName(this ScopeReferenceHandle scopeReferenceHandle, MetadataReader reader) + { + ScopeReference scopeReference = scopeReferenceHandle.GetScopeReference(reader); + return CreateRuntimeAssemblyNameFromMetadata( + reader, + scopeReference.Name, + scopeReference.MajorVersion, + scopeReference.MinorVersion, + scopeReference.BuildNumber, + scopeReference.RevisionNumber, + scopeReference.Culture, + scopeReference.PublicKeyOrToken, + scopeReference.Flags + ); + } + + private static RuntimeAssemblyName CreateRuntimeAssemblyNameFromMetadata( + MetadataReader reader, + ConstantStringValueHandle name, + ushort majorVersion, + ushort minorVersion, + ushort buildNumber, + ushort revisionNumber, + ConstantStringValueHandle culture, + ByteCollection publicKeyOrToken, + global::Internal.Metadata.NativeFormat.AssemblyFlags assemblyFlags) + { + AssemblyNameFlags assemblyNameFlags = AssemblyNameFlags.None; + if (0 != (assemblyFlags & global::Internal.Metadata.NativeFormat.AssemblyFlags.PublicKey)) + assemblyNameFlags |= AssemblyNameFlags.PublicKey; + if (0 != (assemblyFlags & global::Internal.Metadata.NativeFormat.AssemblyFlags.Retargetable)) + assemblyNameFlags |= AssemblyNameFlags.Retargetable; + int contentType = ((int)assemblyFlags) & 0x00000E00; + assemblyNameFlags |= (AssemblyNameFlags)contentType; + + ArrayBuilder keyOrTokenArrayBuilder = new ArrayBuilder(); + foreach (byte b in publicKeyOrToken) + keyOrTokenArrayBuilder.Add(b); + + return new RuntimeAssemblyName( + name.GetString(reader), + new Version(majorVersion, minorVersion, buildNumber, revisionNumber), + culture.GetStringOrNull(reader), + assemblyNameFlags, + keyOrTokenArrayBuilder.ToArray() + ); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderHelpers.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderHelpers.cs new file mode 100644 index 00000000000000..2d8cea0fa56135 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderHelpers.cs @@ -0,0 +1,167 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using global::System; +using global::System.Reflection; +using global::Internal.Metadata.NativeFormat; + +using Debug = System.Diagnostics.Debug; +using AssemblyFlags = Internal.Metadata.NativeFormat.AssemblyFlags; + +namespace Internal.Runtime.TypeLoader +{ + public static class MetadataReaderHelpers + { + public static bool CompareTypeReferenceAcrossModules(TypeReferenceHandle tr1, MetadataReader mr1, TypeReferenceHandle tr2, MetadataReader mr2) + { + TypeReference trData1 = mr1.GetTypeReference(tr1); + TypeReference trData2 = mr2.GetTypeReference(tr2); + if (!trData1.TypeName.StringEquals(trData2.TypeName.GetConstantStringValue(mr2).Value, mr1)) + return false; + + if (trData1.ParentNamespaceOrType.HandleType != trData2.ParentNamespaceOrType.HandleType) + return false; + + if (trData1.ParentNamespaceOrType.HandleType == HandleType.TypeReference) + return CompareTypeReferenceAcrossModules(trData1.ParentNamespaceOrType.ToTypeReferenceHandle(mr1), mr1, trData2.ParentNamespaceOrType.ToTypeReferenceHandle(mr2), mr2); + + return CompareNamespaceReferenceAcrossModules(trData1.ParentNamespaceOrType.ToNamespaceReferenceHandle(mr1), mr1, trData2.ParentNamespaceOrType.ToNamespaceReferenceHandle(mr2), mr2); + } + + public static bool CompareNamespaceReferenceAcrossModules(NamespaceReferenceHandle nr1, MetadataReader mr1, NamespaceReferenceHandle nr2, MetadataReader mr2) + { + NamespaceReference nrData1 = mr1.GetNamespaceReference(nr1); + NamespaceReference nrData2 = mr2.GetNamespaceReference(nr2); + + if (nrData1.Name.IsNull(mr1) != nrData2.Name.IsNull(mr2)) + return false; + + if (!nrData1.Name.IsNull(mr1)) + { + if (!nrData1.Name.StringEquals(nrData2.Name.GetConstantStringValue(mr2).Value, mr1)) + return false; + } + + if (nrData1.ParentScopeOrNamespace.HandleType != nrData1.ParentScopeOrNamespace.HandleType) + return false; + + if (nrData1.ParentScopeOrNamespace.HandleType == HandleType.NamespaceReference) + return CompareNamespaceReferenceAcrossModules(nrData1.ParentScopeOrNamespace.ToNamespaceReferenceHandle(mr1), mr1, nrData2.ParentScopeOrNamespace.ToNamespaceReferenceHandle(mr2), mr2); + + return CompareScopeReferenceAcrossModules(nrData1.ParentScopeOrNamespace.ToScopeReferenceHandle(mr1), mr1, nrData2.ParentScopeOrNamespace.ToScopeReferenceHandle(mr2), mr2); + } + + public static bool CompareScopeReferenceAcrossModules(ScopeReferenceHandle sr1, MetadataReader mr1, ScopeReferenceHandle sr2, MetadataReader mr2) + { + ScopeReference srData1 = mr1.GetScopeReference(sr1); + ScopeReference srData2 = mr2.GetScopeReference(sr2); + if (!srData1.Name.StringEquals(srData2.Name.GetConstantStringValue(mr2).Value, mr1)) + return false; + + if (!srData1.Culture.StringEquals(srData2.Culture.GetConstantStringValue(mr2).Value, mr1)) + return false; + + if (srData1.MajorVersion != srData2.MajorVersion) + return false; + + if (srData1.MinorVersion != srData2.MinorVersion) + return false; + + if (srData1.RevisionNumber != srData2.RevisionNumber) + return false; + + if (srData1.BuildNumber != srData2.BuildNumber) + return false; + + return true; + } + + public static bool CompareTypeReferenceToDefinition(TypeReferenceHandle tr1, MetadataReader mr1, TypeDefinitionHandle td2, MetadataReader mr2) + { + // TODO! The correct implementation here is probably to call into the assembly binder, but that's not available due to layering. + // For now, just implement comparison, which will be equivalent in all cases until we support loading multiple copies of the same assembly + + TypeReference trData1 = mr1.GetTypeReference(tr1); + TypeDefinition tdData2 = mr2.GetTypeDefinition(td2); + + if (!trData1.TypeName.StringEquals(tdData2.Name.GetConstantStringValue(mr2).Value, mr1)) + return false; + + switch (trData1.ParentNamespaceOrType.HandleType) + { + case HandleType.TypeReference: + if (tdData2.EnclosingType.IsNull(mr2)) + return false; + + return CompareTypeReferenceToDefinition(trData1.ParentNamespaceOrType.ToTypeReferenceHandle(mr1), mr1, tdData2.EnclosingType, mr2); + + case HandleType.NamespaceReference: + return CompareNamespaceReferenceToDefinition(trData1.ParentNamespaceOrType.ToNamespaceReferenceHandle(mr1), mr1, tdData2.NamespaceDefinition, mr2); + + default: + Debug.Assert(false); + throw new BadImageFormatException(); + } + } + + public static bool CompareNamespaceReferenceToDefinition(NamespaceReferenceHandle nr1, MetadataReader mr1, NamespaceDefinitionHandle nd2, MetadataReader mr2) + { + NamespaceReference nrData1 = mr1.GetNamespaceReference(nr1); + NamespaceDefinition ndData2 = mr2.GetNamespaceDefinition(nd2); + + if (nrData1.Name.IsNull(mr1) != ndData2.Name.IsNull(mr2)) + return false; + + if (!nrData1.Name.IsNull(mr1)) + { + if (!nrData1.Name.StringEquals(ndData2.Name.GetConstantStringValue(mr2).Value, mr1)) + return false; + } + + switch (nrData1.ParentScopeOrNamespace.HandleType) + { + case HandleType.NamespaceReference: + if (ndData2.ParentScopeOrNamespace.HandleType != HandleType.NamespaceDefinition) + return false; + return CompareNamespaceReferenceToDefinition(nrData1.ParentScopeOrNamespace.ToNamespaceReferenceHandle(mr1), mr1, ndData2.ParentScopeOrNamespace.ToNamespaceDefinitionHandle(mr2), mr2); + + case HandleType.ScopeReference: + if (ndData2.ParentScopeOrNamespace.HandleType != HandleType.ScopeDefinition) + return false; + + return CompareScopeReferenceToDefinition(nrData1.ParentScopeOrNamespace.ToScopeReferenceHandle(mr1), mr1, ndData2.ParentScopeOrNamespace.ToScopeDefinitionHandle(mr2), mr2); + + default: + Debug.Assert(false); + throw new BadImageFormatException(); + } + } + + public static bool CompareScopeReferenceToDefinition(ScopeReferenceHandle sr1, MetadataReader mr1, ScopeDefinitionHandle sd2, MetadataReader mr2) + { + ScopeReference srData1 = mr1.GetScopeReference(sr1); + ScopeDefinition sdData2 = mr2.GetScopeDefinition(sd2); + if (!srData1.Name.StringEquals(sdData2.Name.GetConstantStringValue(mr2).Value, mr1)) + return false; + + if (!srData1.Culture.StringEquals(sdData2.Culture.GetConstantStringValue(mr2).Value, mr1)) + return false; + + if (srData1.MajorVersion != sdData2.MajorVersion) + return false; + + if (srData1.MinorVersion != sdData2.MinorVersion) + return false; + + if (srData1.RevisionNumber != sdData2.RevisionNumber) + return false; + + if (srData1.BuildNumber != sdData2.BuildNumber) + return false; + + return true; + } + + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MethodTable.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MethodTable.Runtime.cs new file mode 100644 index 00000000000000..20440f67a5a9a2 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MethodTable.Runtime.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +using Internal.Runtime.TypeLoader; + +namespace Internal.Runtime +{ + // Supplies type loader specific extentions to MethodTable + internal partial struct MethodTable + { + private static unsafe MethodTable* GetArrayEEType() + { + return typeof(Array).TypeHandle.ToEETypePtr(); + } + + internal unsafe RuntimeTypeHandle ToRuntimeTypeHandle() + { + fixed (MethodTable* pThis = &this) + { + IntPtr result = (IntPtr)pThis; + return *(RuntimeTypeHandle*)&result; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ModuleList.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ModuleList.cs new file mode 100644 index 00000000000000..7a89589ed6cabf --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ModuleList.cs @@ -0,0 +1,948 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime; +using System.Text; +using System.Threading; +using Internal.Runtime.Augments; +using Internal.Metadata.NativeFormat; +using Internal.Reflection.Execution; + +namespace Internal.Runtime.TypeLoader +{ + public enum ModuleType + { + Eager, + ReadyToRun, + Ecma + } + + /// + /// This class represents basic information about a native binary module including its + /// metadata. + /// + public unsafe class ModuleInfo + { + /// + /// Module handle is the TypeManager associated with this module. + /// + public TypeManagerHandle Handle { get; private set; } + + /// + /// A reference to the dynamic module is part of the MethodTable for dynamically allocated types. + /// + internal DynamicModule* DynamicModulePtr { get; private set; } + + public IntPtr DynamicModulePtrAsIntPtr => new IntPtr(DynamicModulePtr); + + /// + /// What sort of module is this? (Eager, ReadyToRun)? + /// + internal ModuleType ModuleType { get; private set; } + + /// + /// Initialize module info and construct per-module metadata reader. + /// + /// Handle (address) of module to initialize + /// Module type + internal ModuleInfo(TypeManagerHandle moduleHandle, ModuleType moduleType) + { + Handle = moduleHandle; + ModuleType = moduleType; + + DynamicModule* dynamicModulePtr = (DynamicModule*)MemoryHelpers.AllocateMemory(sizeof(DynamicModule)); + dynamicModulePtr->CbSize = DynamicModule.DynamicModuleSize; + Debug.Assert(sizeof(DynamicModule) >= dynamicModulePtr->CbSize); + + if ((moduleType == ModuleType.ReadyToRun) || (moduleType == ModuleType.Ecma)) + { + // Dynamic type load modules utilize dynamic type resolution + dynamicModulePtr->DynamicTypeSlotDispatchResolve = &ResolveTypeSlotDispatch; + } + else + { + Debug.Assert(moduleType == ModuleType.Eager); + // Pre-generated modules do not + dynamicModulePtr->DynamicTypeSlotDispatchResolve = null; + } + + dynamicModulePtr->GetRuntimeException = &RuntimeExceptionHelpers.GetRuntimeException; + + DynamicModulePtr = dynamicModulePtr; + } + + internal static unsafe IntPtr ResolveTypeSlotDispatch(MethodTable* targetType, MethodTable* interfaceType, ushort slot) + { + IntPtr methodAddress; + if (!TypeLoaderEnvironment.Instance.TryResolveTypeSlotDispatch(targetType, interfaceType, slot, out methodAddress)) + { + throw new BadImageFormatException(); + } + return methodAddress; + } + } + + public class NativeFormatModuleInfo : ModuleInfo + { + /// + /// Initialize module info and construct per-module metadata reader. + /// + /// Handle (address) of module to initialize + /// Module type + /// Module blob start address + /// Module blob length + internal NativeFormatModuleInfo(TypeManagerHandle moduleHandle, ModuleType moduleType, IntPtr pBlob, int cbBlob) : base (moduleHandle, moduleType) + { + MetadataReader = new MetadataReader((IntPtr)pBlob, (int)cbBlob); + } + + /// + /// Module metadata reader for NativeFormat metadata + /// + public MetadataReader MetadataReader { get; private set; } + + internal unsafe bool TryFindBlob(ReflectionMapBlob blobId, out byte* pBlob, out uint cbBlob) + { + pBlob = null; + cbBlob = 0; + fixed (byte** ppBlob = &pBlob) + { + fixed (uint* pcbBlob = &cbBlob) + { + return RuntimeAugments.FindBlob(Handle, (int)blobId, new IntPtr(ppBlob), new IntPtr(pcbBlob)); + } + } + } + + public unsafe bool TryFindBlob(int blobId, out byte* pBlob, out uint cbBlob) + { + pBlob = null; + cbBlob = 0; + fixed (byte** ppBlob = &pBlob) + { + fixed (uint* pcbBlob = &cbBlob) + { + return RuntimeAugments.FindBlob(Handle, (int)blobId, new IntPtr(ppBlob), new IntPtr(pcbBlob)); + } + } + } + } + + /// + /// This class represents a linear module list and a dictionary mapping module handles + /// to its indices. When a new module is registered, a new instance of this class gets + /// constructed and atomically updates the _loadedModuleMap so that at any point in time + /// all threads see the map as consistent. + /// + internal sealed class ModuleMap + { + /// + /// Array of loaded binary modules. + /// + public readonly ModuleInfo[] Modules; + + /// + /// Map of module handles to indices within the Modules array. + /// + public readonly LowLevelDictionary HandleToModuleIndex; + + internal ModuleMap(ModuleInfo[] modules) + { + Modules = modules; + HandleToModuleIndex = new LowLevelDictionary(); + for (int moduleIndex = 0; moduleIndex < Modules.Length; moduleIndex++) + { + // Ecma modules don't go in the reverse lookup hash because they share a module index with the system module + if (Modules[moduleIndex].ModuleType != ModuleType.Ecma) + HandleToModuleIndex.Add(Modules[moduleIndex].Handle, moduleIndex); + } + } + } + + /// + /// Helper class that can construct an enumerator for the module info map, possibly adjusting + /// the module order so that a given explicitly specified module goes first - this is used + /// as optimization in cases where a certain module is most likely to contain some metadata. + /// + public struct ModuleInfoEnumerable + { + /// + /// Module map to enumerate + /// + private readonly ModuleMap _moduleMap; + + /// + /// Module handle that should be enumerated first, default(IntPtr) when not used. + /// + private readonly TypeManagerHandle _preferredModuleHandle; + + /// + /// Store module map and preferred module to pass to the enumerator upon construction. + /// + /// Module map to enumerate + /// Optional module handle to enumerate first + internal ModuleInfoEnumerable(ModuleMap moduleMap, TypeManagerHandle preferredModuleHandle) + { + _moduleMap = moduleMap; + _preferredModuleHandle = preferredModuleHandle; + } + + /// + /// Construct the actual module info enumerator. + /// + public ModuleInfoEnumerator GetEnumerator() + { + return new ModuleInfoEnumerator(_moduleMap, _preferredModuleHandle); + } + } + + /// + /// This enumerator iterates the module map, possibly adjusting the order to make a given + /// module go first in the enumeration. + /// + public struct ModuleInfoEnumerator + { + /// + /// Array of modules to enumerate. + /// + private readonly ModuleInfo[] _modules; + + /// + /// Preferred module index in the array, -1 when none (in such case the array is enumerated + /// in its natural order). + /// + private int _preferredIndex; + + /// + /// Enumeration step index initially set to -1 (so that the first MoveNext increments it to 0). + /// + private int _iterationIndex; + + /// + /// Current _modules element that should be returned by Current (updated in MoveNext). + /// + private ModuleInfo _currentModule; + + /// + /// Initialize the module enumerator state machine and locate the preferred module index. + /// + /// Module map to enumerate + /// Optional module handle to enumerate first + internal ModuleInfoEnumerator(ModuleMap moduleMap, TypeManagerHandle preferredModuleHandle) + { + _modules = moduleMap.Modules; + _preferredIndex = -1; + _iterationIndex = -1; + _currentModule = null; + + if (!preferredModuleHandle.IsNull && + !moduleMap.HandleToModuleIndex.TryGetValue(preferredModuleHandle, out _preferredIndex)) + { + Environment.FailFast("Invalid module requested in enumeration: " + preferredModuleHandle.LowLevelToString()); + } + } + + /// + /// Move the enumerator state machine to the next element in the module map. + /// + /// true when [another] module is available, false when the enumeration is finished + public bool MoveNext() + { + if (_iterationIndex + 1 >= _modules.Length) + { + _currentModule = null; + return false; + } + + _iterationIndex++; + int moduleIndex = _iterationIndex; + if (moduleIndex <= _preferredIndex) + { + // Transform the index so that the _preferredIndex is returned in first iteration + moduleIndex = (moduleIndex == 0 ? _preferredIndex : moduleIndex - 1); + } + + _currentModule = _modules[moduleIndex]; + + return true; + } + + /// + /// Look up the "current" module corresponding to the previous call to MoveNext. + /// + public ModuleInfo Current + { + get + { + if (_currentModule == null) + { + Environment.FailFast("Current module queried in wrong enumerator state"); + } + return _currentModule; + } + } + } + + /// + /// Helper class that can construct an enumerator for the module info map, possibly adjusting + /// the module order so that a given explicitly specified module goes first - this is used + /// as optimization in cases where a certain module is most likely to contain some metadata. + /// + public struct NativeFormatModuleInfoEnumerable + { + /// + /// Module map to enumerate + /// + private readonly ModuleMap _moduleMap; + + /// + /// Module handle that should be enumerated first, default(IntPtr) when not used. + /// + private readonly TypeManagerHandle _preferredModuleHandle; + + /// + /// Store module map and preferred module to pass to the enumerator upon construction. + /// + /// Module map to enumerate + /// Optional module handle to enumerate first + internal NativeFormatModuleInfoEnumerable(ModuleMap moduleMap, TypeManagerHandle preferredModuleHandle) + { + _moduleMap = moduleMap; + _preferredModuleHandle = preferredModuleHandle; + } + + /// + /// Construct the actual module info enumerator. + /// + public NativeFormatModuleInfoEnumerator GetEnumerator() + { + return new NativeFormatModuleInfoEnumerator(_moduleMap, _preferredModuleHandle); + } + } + + /// + /// This enumerator iterates the module map, possibly adjusting the order to make a given + /// module go first in the enumeration. + /// + public struct NativeFormatModuleInfoEnumerator + { + /// + /// Array of modules to enumerate. + /// + private readonly ModuleInfo[] _modules; + + /// + /// Preferred module index in the array, -1 when none (in such case the array is enumerated + /// in its natural order). + /// + private int _preferredIndex; + + /// + /// Enumeration step index initially set to -1 (so that the first MoveNext increments it to 0). + /// + private int _iterationIndex; + + /// + /// Current _modules element that should be returned by Current (updated in MoveNext). + /// + private NativeFormatModuleInfo _currentModule; + + /// + /// Initialize the module enumerator state machine and locate the preferred module index. + /// + /// Module map to enumerate + /// Optional module handle to enumerate first + internal NativeFormatModuleInfoEnumerator(ModuleMap moduleMap, TypeManagerHandle preferredModuleHandle) + { + _modules = moduleMap.Modules; + _preferredIndex = -1; + _iterationIndex = -1; + _currentModule = null; + + if (!preferredModuleHandle.IsNull && + !moduleMap.HandleToModuleIndex.TryGetValue(preferredModuleHandle, out _preferredIndex)) + { + Environment.FailFast("Invalid module requested in enumeration: " + preferredModuleHandle.LowLevelToString()); + } + } + + /// + /// Move the enumerator state machine to the next element in the module map. + /// + /// true when [another] module is available, false when the enumeration is finished + public bool MoveNext() + { + do + { + if (_iterationIndex + 1 >= _modules.Length) + { + _currentModule = null; + return false; + } + + _iterationIndex++; + int moduleIndex = _iterationIndex; + if (moduleIndex <= _preferredIndex) + { + // Transform the index so that the _preferredIndex is returned in first iteration + moduleIndex = (moduleIndex == 0 ? _preferredIndex : moduleIndex - 1); + } + + _currentModule = _modules[moduleIndex] as NativeFormatModuleInfo; + } while (_currentModule == null); + + return true; + } + + /// + /// Look up the "current" module corresponding to the previous call to MoveNext. + /// + public NativeFormatModuleInfo Current + { + get + { + if (_currentModule == null) + { + Environment.FailFast("Current module queried in wrong enumerator state"); + } + return _currentModule; + } + } + } + + /// + /// Helper class that can construct an enumerator for the module handle map, possibly adjusting + /// the module order so that a given explicitly specified module goes first - this is used + /// as optimization in cases where a certain module is most likely to contain some metadata. + /// + public struct ModuleHandleEnumerable + { + /// + /// Module map to enumerate + /// + private readonly ModuleMap _moduleMap; + + /// + /// Module handle that should be enumerated first, default(IntPtr) when not used. + /// + private readonly TypeManagerHandle _preferredModuleHandle; + + /// + /// Store module map and preferred module to pass to the enumerator upon construction. + /// + /// Module map to enumerate + /// Optional module handle to enumerate first + internal ModuleHandleEnumerable(ModuleMap moduleMap, TypeManagerHandle preferredModuleHandle) + { + _moduleMap = moduleMap; + _preferredModuleHandle = preferredModuleHandle; + } + + /// + /// Create the actual module handle enumerator. + /// + public ModuleHandleEnumerator GetEnumerator() + { + return new ModuleHandleEnumerator(_moduleMap, _preferredModuleHandle); + } + } + + /// + /// Enumerator for module handles, optionally overriding module order with a given preferred + /// module to be enumerated first. + /// + public struct ModuleHandleEnumerator + { + /// + /// The underlying ModuleInfoEnumerator handles enumeration internals + /// + private ModuleInfoEnumerator _moduleInfoEnumerator; + + /// + /// Construct the underlying module info enumerator used to iterate the module map + /// + /// Module map to enumerate + /// Optional module handle to enumerate first + internal ModuleHandleEnumerator(ModuleMap moduleMap, TypeManagerHandle preferredModuleHandle) + { + _moduleInfoEnumerator = new ModuleInfoEnumerator(moduleMap, preferredModuleHandle); + } + + /// + /// Move to next element in the module map. Return true when an element is available, + /// false when the enumeration is finished. + /// + public bool MoveNext() + { + bool result; + while (true) + { + result = _moduleInfoEnumerator.MoveNext(); + // Ecma module shouldn't be reported as they should not be enumerated by ModuleHandle (as its always the System module) + if (!result || (_moduleInfoEnumerator.Current.ModuleType != ModuleType.Ecma)) + { + break; + } + } + return result; + } + + /// + /// Return current module handle. + /// + public TypeManagerHandle Current + { + get { return _moduleInfoEnumerator.Current.Handle; } + } + } + + /// + /// Helper class that can construct an enumerator for module metadata readers, possibly adjusting + /// the module order so that a given explicitly specified module goes first - this is used + /// as optimization in cases where a certain module is most likely to contain some metadata. + /// + public struct MetadataReaderEnumerable + { + /// + /// Module map to enumerate + /// + private readonly ModuleMap _moduleMap; + + /// + /// Module handle that should be enumerated first, default(IntPtr) when not used. + /// + private readonly TypeManagerHandle _preferredModuleHandle; + + /// + /// Store module map and preferred module to pass to the enumerator upon construction. + /// + /// Module map to enumerate + /// Optional module handle to enumerate first + internal MetadataReaderEnumerable(ModuleMap moduleMap, TypeManagerHandle preferredModuleHandle) + { + _moduleMap = moduleMap; + _preferredModuleHandle = preferredModuleHandle; + } + + /// + /// Create the actual module handle enumerator. + /// + public MetadataReaderEnumerator GetEnumerator() + { + return new MetadataReaderEnumerator(_moduleMap, _preferredModuleHandle); + } + } + + /// + /// Enumerator for metadata readers, optionally overriding module order with a given preferred + /// module to be enumerated first. + /// + public struct MetadataReaderEnumerator + { + /// + /// The underlying ModuleInfoEnumerator handles enumeration internals + /// + private NativeFormatModuleInfoEnumerator _moduleInfoEnumerator; + + /// + /// Construct the underlying module info enumerator used to iterate the module map + /// + /// Module map to enumerate + /// Optional module handle to enumerate first + internal MetadataReaderEnumerator(ModuleMap moduleMap, TypeManagerHandle preferredModuleHandle) + { + _moduleInfoEnumerator = new NativeFormatModuleInfoEnumerator(moduleMap, preferredModuleHandle); + } + + /// + /// Move to next element in the module map. Return true when an element is available, + /// false when the enumeration is finished. + /// + public bool MoveNext() + { + return _moduleInfoEnumerator.MoveNext(); + } + + /// + /// Return current metadata reader. + /// + public MetadataReader Current + { + get { return _moduleInfoEnumerator.Current.MetadataReader; } + } + } + + /// + /// Utilities for manipulating module list and metadata readers. + /// + public sealed class ModuleList + { + /// + /// Map of module addresses to module info. Every time a new module is loaded, + /// the reference gets atomically updated to a newly copied instance of the dictionary + /// to that consumers of this dictionary can look at the reference and enumerate / process it without locking, fear that the contents of the dictionary change + /// under its hands. + /// + private volatile ModuleMap _loadedModuleMap; + + internal ModuleMap GetLoadedModuleMapInternal() { return _loadedModuleMap; } + + /// + /// List of callbacks to execute when a module gets registered. + /// + private Action _moduleRegistrationCallbacks; + + /// + /// Lock used for serializing module registrations. + /// + private Lock _moduleRegistrationLock; + + /// + /// Base Module (module that contains System.Object) + /// + private ModuleInfo _systemModule; + + /// + /// Register initially (eagerly) loaded modules. + /// + internal ModuleList() + { + _loadedModuleMap = new ModuleMap(new ModuleInfo[0]); + _moduleRegistrationCallbacks = default(Action); + _moduleRegistrationLock = new Lock(); + + RegisterNewModules(ModuleType.Eager); + + TypeManagerHandle systemObjectModule = RuntimeAugments.GetModuleFromTypeHandle(RuntimeAugments.RuntimeTypeHandleOf()); + foreach (ModuleInfo m in _loadedModuleMap.Modules) + { + if (m.Handle == systemObjectModule) + { + _systemModule = m; + break; + } + } + } + + /// + /// Module list is a process-wide singleton that physically lives in the TypeLoaderEnvironment instance. + /// + public static ModuleList Instance + { + get { return TypeLoaderEnvironment.Instance.ModuleList; } + } + + /// + /// Register a new callback that gets called whenever a new module gets registered. + /// The module registration happens under a global lock so that the module registration + /// callbacks are never called concurrently. + /// + /// Method to call whenever a new module is registered + public static void AddModuleRegistrationCallback(Action newModuleRegistrationCallback) + { + // Accumulate callbacks to be notified upon module registration + Instance._moduleRegistrationCallbacks += newModuleRegistrationCallback; + + // Invoke the new callback for all modules that have already been registered + foreach (ModuleInfo moduleInfo in EnumerateModules()) + { + newModuleRegistrationCallback(moduleInfo); + } + } + + /// + /// Register all modules which were added (Registered) to the runtime and are not already registered with the TypeLoader. + /// + /// Type to assign to all new modules. + public void RegisterNewModules(ModuleType moduleType) + { + // prevent multiple threads from registering modules concurrently + using (LockHolder.Hold(_moduleRegistrationLock)) + { + // Fetch modules that have already been registered with the runtime + int loadedModuleCount = RuntimeAugments.GetLoadedModules(null); + TypeManagerHandle[] loadedModuleHandles = new TypeManagerHandle[loadedModuleCount]; + int loadedModuleCountUpdated = RuntimeAugments.GetLoadedModules(loadedModuleHandles); + Debug.Assert(loadedModuleCount == loadedModuleCountUpdated); + + LowLevelList newModuleHandles = new LowLevelList(loadedModuleHandles.Length); + foreach (TypeManagerHandle moduleHandle in loadedModuleHandles) + { + // Skip already registered modules. + int oldModuleIndex; + if (_loadedModuleMap.HandleToModuleIndex.TryGetValue(moduleHandle, out oldModuleIndex)) + { + continue; + } + + newModuleHandles.Add(moduleHandle); + } + + // Copy existing modules to new dictionary + int oldModuleCount = _loadedModuleMap.Modules.Length; + ModuleInfo[] updatedModules = new ModuleInfo[oldModuleCount + newModuleHandles.Count]; + if (oldModuleCount > 0) + { + Array.Copy(_loadedModuleMap.Modules, 0, updatedModules, 0, oldModuleCount); + } + + for (int newModuleIndex = 0; newModuleIndex < newModuleHandles.Count; newModuleIndex++) + { + ModuleInfo newModuleInfo; + + unsafe + { + byte* pBlob; + uint cbBlob; + + if (RuntimeAugments.FindBlob(newModuleHandles[newModuleIndex], (int)ReflectionMapBlob.EmbeddedMetadata, new IntPtr(&pBlob), new IntPtr(&cbBlob))) + { + newModuleInfo = new NativeFormatModuleInfo(newModuleHandles[newModuleIndex], moduleType, (IntPtr)pBlob, (int)cbBlob); + } + else + { + newModuleInfo = new ModuleInfo(newModuleHandles[newModuleIndex], moduleType); + } + } + + updatedModules[oldModuleCount + newModuleIndex] = newModuleInfo; + + if (_moduleRegistrationCallbacks != null) + { + _moduleRegistrationCallbacks(newModuleInfo); + } + } + + // Atomically update the module map + _loadedModuleMap = new ModuleMap(updatedModules); + } + } + + public void RegisterModule(ModuleInfo newModuleInfo) + { + // prevent multiple threads from registering modules concurrently + using (LockHolder.Hold(_moduleRegistrationLock)) + { + // Copy existing modules to new dictionary + int oldModuleCount = _loadedModuleMap.Modules.Length; + ModuleInfo[] updatedModules = new ModuleInfo[oldModuleCount + 1]; + if (oldModuleCount > 0) + { + Array.Copy(_loadedModuleMap.Modules, 0, updatedModules, 0, oldModuleCount); + } + updatedModules[oldModuleCount] = newModuleInfo; + if (_moduleRegistrationCallbacks != null) + { + _moduleRegistrationCallbacks(newModuleInfo); + } + + // Atomically update the module map + _loadedModuleMap = new ModuleMap(updatedModules); + } + } + + /// + /// Locate module info for a given module. Fail if not found or before the module registry + /// gets initialized. Must only be called for modules described as native format (not the mrt module, or an ECMA module) + /// + /// Handle of module to look up + public NativeFormatModuleInfo GetModuleInfoByHandle(TypeManagerHandle moduleHandle) + { + ModuleMap moduleMap = _loadedModuleMap; + return (NativeFormatModuleInfo)moduleMap.Modules[moduleMap.HandleToModuleIndex[moduleHandle]]; + } + + /// + /// Try to Locate module info for a given module. Returns false when not found. + /// gets initialized. + /// + /// Handle of module to look up + /// Found module info + public bool TryGetModuleInfoByHandle(TypeManagerHandle moduleHandle, out ModuleInfo moduleInfo) + { + ModuleMap moduleMap = _loadedModuleMap; + int moduleIndex; + if (moduleMap.HandleToModuleIndex.TryGetValue(moduleHandle, out moduleIndex)) + { + moduleInfo = moduleMap.Modules[moduleIndex]; + return true; + } + moduleInfo = null; + return false; + } + + /// + /// Given module handle, locate the metadata reader. Return null when not found. + /// + /// Handle of module to look up + /// Reader for the embedded metadata blob in the module, null when not found + public MetadataReader GetMetadataReaderForModule(TypeManagerHandle moduleHandle) + { + ModuleMap moduleMap = _loadedModuleMap; + int moduleIndex; + if (moduleMap.HandleToModuleIndex.TryGetValue(moduleHandle, out moduleIndex)) + { + NativeFormatModuleInfo moduleInfo = moduleMap.Modules[moduleIndex] as NativeFormatModuleInfo; + if (moduleInfo != null) + return moduleInfo.MetadataReader; + else + return null; + } + return null; + } + + /// + /// Given dynamic module handle, locate the moduleinfo + /// + /// Handle of module to look up + /// fails if not found + public unsafe ModuleInfo GetModuleInfoForDynamicModule(IntPtr dynamicModuleHandle) + { + foreach (ModuleInfo moduleInfo in _loadedModuleMap.Modules) + { + if (new IntPtr(moduleInfo.DynamicModulePtr) == dynamicModuleHandle) + return moduleInfo; + } + + // We should never have a dynamic module that is not associated with a module (where does it come from?!) + Debug.Assert(false); + return null; + } + + + /// + /// Locate the containing module for a given metadata reader. Assert when not found. + /// + /// Metadata reader to look up + /// Module handle of the module containing the given reader + public NativeFormatModuleInfo GetModuleInfoForMetadataReader(MetadataReader reader) + { + foreach (ModuleInfo moduleInfo in _loadedModuleMap.Modules) + { + NativeFormatModuleInfo nativeFormatModuleInfo = moduleInfo as NativeFormatModuleInfo; + if (nativeFormatModuleInfo != null && nativeFormatModuleInfo.MetadataReader == reader) + { + return nativeFormatModuleInfo; + } + } + + // We should never have a reader that is not associated with a module (where does it come from?!) + Debug.Assert(false); + return null; + } + + /// + /// Locate the containing module for a given metadata reader. Assert when not found. + /// + /// Metadata reader to look up + /// Module handle of the module containing the given reader + public TypeManagerHandle GetModuleForMetadataReader(MetadataReader reader) + { + foreach (ModuleInfo moduleInfo in _loadedModuleMap.Modules) + { + NativeFormatModuleInfo nativeFormatModuleInfo = moduleInfo as NativeFormatModuleInfo; + if (nativeFormatModuleInfo != null && nativeFormatModuleInfo.MetadataReader == reader) + { + return moduleInfo.Handle; + } + } + + // We should never have a reader that is not associated with a module (where does it come from?!) + Debug.Assert(false); + return default(TypeManagerHandle); + } + + /// + /// Base Module (module that contains System.Object) + /// + public ModuleInfo SystemModule + { + get + { + return _systemModule; + } + } + + /// + /// Enumerate modules. + /// + public static NativeFormatModuleInfoEnumerable EnumerateModules() + { + return new NativeFormatModuleInfoEnumerable(Instance._loadedModuleMap, default(TypeManagerHandle)); + } + + /// + /// Enumerate modules. Specify a module that should be enumerated first + /// - this is used as an optimization in cases when a certain binary module is more probable + /// to contain a certain information. + /// + /// Handle to the module which should be enumerated first + public static NativeFormatModuleInfoEnumerable EnumerateModules(TypeManagerHandle preferredModule) + { + return new NativeFormatModuleInfoEnumerable(Instance._loadedModuleMap, preferredModule); + } + + /// + /// Enumerate metadata readers. + /// + public static MetadataReaderEnumerable EnumerateMetadataReaders() + { + return new MetadataReaderEnumerable(Instance._loadedModuleMap, default(TypeManagerHandle)); + } + + /// + /// Enumerate metadata readers. Specify a module that should be enumerated first + /// - this is used as an optimization in cases when a certain binary module is more probable + /// to contain a certain information. + /// + /// Handle to the module which should be enumerated first + public static MetadataReaderEnumerable EnumerateMetadataReaders(TypeManagerHandle preferredModule) + { + return new MetadataReaderEnumerable(Instance._loadedModuleMap, preferredModule); + } + + /// + /// Enumerate module handles (simplified version for code that only needs the module addresses). + /// + public static ModuleHandleEnumerable Enumerate() + { + return new ModuleHandleEnumerable(Instance._loadedModuleMap, default(TypeManagerHandle)); + } + + /// + /// Enumerate module handles (simplified version for code that only needs the module addresses). + /// Specify a module that should be enumerated first + /// - this is used as an optimization in cases when a certain binary module is more probable + /// to contain a certain information. + /// + /// Handle to the module which should be enumerated first + public static ModuleHandleEnumerable Enumerate(TypeManagerHandle preferredModule) + { + return new ModuleHandleEnumerable(Instance._loadedModuleMap, preferredModule); + } + } + + public static partial class RuntimeSignatureHelper + { + public static ModuleInfo GetModuleInfo(this Internal.Runtime.CompilerServices.RuntimeSignature methodSignature) + { + if (methodSignature.IsNativeLayoutSignature) + { + return ModuleList.Instance.GetModuleInfoByHandle(new TypeManagerHandle(methodSignature.ModuleHandle)); + } + else + { + ModuleInfo moduleInfo; + if (!ModuleList.Instance.TryGetModuleInfoByHandle(new TypeManagerHandle(methodSignature.ModuleHandle), out moduleInfo)) + { + moduleInfo = ModuleList.Instance.GetModuleInfoForDynamicModule(methodSignature.ModuleHandle); + } + return moduleInfo; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldAlgorithm.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldAlgorithm.cs new file mode 100644 index 00000000000000..8d93e08b45b658 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldAlgorithm.cs @@ -0,0 +1,524 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; +using System.Diagnostics; +using Internal.NativeFormat; +using System.Collections.Generic; +using Internal.Runtime.Augments; + +namespace Internal.Runtime.TypeLoader +{ + /// + /// Reads field layout based on native layout data + /// information + /// + internal class NativeLayoutFieldAlgorithm : FieldLayoutAlgorithm + { + private NoMetadataFieldLayoutAlgorithm _noMetadataFieldLayoutAlgorithm = new NoMetadataFieldLayoutAlgorithm(); + private const int InstanceAlignmentEntry = 4; + + public unsafe override bool ComputeContainsGCPointers(DefType type) + { + if (type.IsTemplateCanonical()) + { + return type.ComputeTemplate().RuntimeTypeHandle.ToEETypePtr()->HasGCPointers; + } + else + { + if (type.RetrieveRuntimeTypeHandleIfPossible()) + { + return type.RuntimeTypeHandle.ToEETypePtr()->HasGCPointers; + } + + return type.GetOrCreateTypeBuilderState().InstanceGCLayout != null; + } + } + + public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType type, InstanceLayoutKind layoutKind) + { + if (!type.IsTemplateUniversal() && (layoutKind == InstanceLayoutKind.TypeOnly)) + { + // Non universal generics can just use the template's layout + DefType template = (DefType)type.ComputeTemplate(); + return _noMetadataFieldLayoutAlgorithm.ComputeInstanceLayout(template, InstanceLayoutKind.TypeOnly); + } + + // Only needed for universal generics, or when looking up an offset for a field for a universal generic + LowLevelList fieldOffsets; + LayoutInt[] position = ComputeTypeSizeAndAlignment(type, FieldLoadState.Instance, out fieldOffsets); + + int numInstanceFields = 0; + foreach (NativeLayoutFieldDesc field in type.NativeLayoutFields) + { + if (!field.IsStatic) + { + numInstanceFields++; + } + } + + TargetDetails target = type.Context.Target; + + LayoutInt byteCountAlignment = position[InstanceAlignmentEntry]; + byteCountAlignment = target.GetObjectAlignment(byteCountAlignment); + + ComputedInstanceFieldLayout layout = new ComputedInstanceFieldLayout() + { + Offsets = new FieldAndOffset[numInstanceFields], + ByteCountAlignment = byteCountAlignment, + ByteCountUnaligned = position[(int)NativeFormat.FieldStorage.Instance], + }; + + if (!type.IsValueType) + { + layout.FieldAlignment = target.LayoutPointerSize; + layout.FieldSize = target.LayoutPointerSize; + } + else + { + layout.FieldAlignment = position[InstanceAlignmentEntry]; + layout.FieldSize = LayoutInt.AlignUp(position[(int)NativeFormat.FieldStorage.Instance], layout.FieldAlignment, target); + } + + int curInstanceField = 0; + foreach (NativeLayoutFieldDesc field in type.NativeLayoutFields) + { + if (!field.IsStatic) + { + layout.Offsets[curInstanceField] = new FieldAndOffset(field, fieldOffsets[curInstanceField]); + curInstanceField++; + } + } + + return layout; + } + + public override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefType type, StaticLayoutKind layoutKind) + { + if (!type.IsTemplateUniversal() && (layoutKind == StaticLayoutKind.StaticRegionSizes)) + { + return ParseStaticRegionSizesFromNativeLayout(type); + } + + LowLevelList fieldOffsets; + LayoutInt[] position = ComputeTypeSizeAndAlignment(type, FieldLoadState.Statics, out fieldOffsets); + + int numStaticFields = 0; + foreach (NativeLayoutFieldDesc field in type.NativeLayoutFields) + { + if (field.IsStatic) + { + numStaticFields++; + } + } + + ComputedStaticFieldLayout layout = new ComputedStaticFieldLayout(); + + layout.Offsets = new FieldAndOffset[numStaticFields]; + + if (numStaticFields > 0) + { + layout.GcStatics = new StaticsBlock() { Size = position[(int)NativeFormat.FieldStorage.GCStatic], LargestAlignment = DefType.MaximumAlignmentPossible }; + layout.NonGcStatics = new StaticsBlock() { Size = position[(int)NativeFormat.FieldStorage.NonGCStatic], LargestAlignment = DefType.MaximumAlignmentPossible }; + layout.ThreadGcStatics = new StaticsBlock() { Size = position[(int)NativeFormat.FieldStorage.TLSStatic], LargestAlignment = DefType.MaximumAlignmentPossible }; + layout.ThreadNonGcStatics = new StaticsBlock() { Size = LayoutInt.Zero, LargestAlignment = LayoutInt.Zero }; + } + + int curStaticField = 0; + foreach (NativeLayoutFieldDesc field in type.NativeLayoutFields) + { + if (field.IsStatic) + { + layout.Offsets[curStaticField] = new FieldAndOffset(field, fieldOffsets[curStaticField]); + curStaticField++; + } + } + + return layout; + } + + private ComputedStaticFieldLayout ParseStaticRegionSizesFromNativeLayout(TypeDesc type) + { + LayoutInt nonGcDataSize = LayoutInt.Zero; + LayoutInt gcDataSize = LayoutInt.Zero; + LayoutInt threadDataSize = LayoutInt.Zero; + + TypeBuilderState state = type.GetOrCreateTypeBuilderState(); + NativeParser typeInfoParser = state.GetParserForNativeLayoutInfo(); + + BagElementKind kind; + while ((kind = typeInfoParser.GetBagElementKind()) != BagElementKind.End) + { + switch (kind) + { + case BagElementKind.NonGcStaticDataSize: + TypeLoaderLogger.WriteLine("Found BagElementKind.NonGcStaticDataSize"); + // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) + nonGcDataSize = new LayoutInt(checked((int)typeInfoParser.GetUnsigned())); + break; + + case BagElementKind.GcStaticDataSize: + TypeLoaderLogger.WriteLine("Found BagElementKind.GcStaticDataSize"); + // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) + gcDataSize = new LayoutInt(checked((int)typeInfoParser.GetUnsigned())); + break; + + case BagElementKind.ThreadStaticDataSize: + TypeLoaderLogger.WriteLine("Found BagElementKind.ThreadStaticDataSize"); + // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) + threadDataSize = new LayoutInt(checked((int)typeInfoParser.GetUnsigned())); + break; + + default: + typeInfoParser.SkipInteger(); + break; + } + } + + ComputedStaticFieldLayout staticLayout = new ComputedStaticFieldLayout() + { + GcStatics = new StaticsBlock() { Size = gcDataSize, LargestAlignment = DefType.MaximumAlignmentPossible }, + NonGcStatics = new StaticsBlock() { Size = nonGcDataSize, LargestAlignment = DefType.MaximumAlignmentPossible }, + Offsets = null, // We're not computing field offsets here, so return null + ThreadGcStatics = new StaticsBlock() { Size = threadDataSize, LargestAlignment = DefType.MaximumAlignmentPossible }, + ThreadNonGcStatics = new StaticsBlock() { Size = LayoutInt.Zero, LargestAlignment = LayoutInt.Zero }, + }; + + return staticLayout; + } + + internal static void EnsureFieldLayoutLoadedForGenericType(DefType type) + { + if (type.NativeLayoutFields != null) + return; + + if (!type.IsTemplateUniversal()) + { + // We can hit this case where the template of type in question is not a universal canonical type. + // Example: + // BaseType { ... } + // DerivedType : BaseType { ... } + // and an instantiation like DerivedType. In that case, BaseType will have a non-universal + // template type, and requires special handling to compute its size and field layout. + EnsureFieldLayoutLoadedForNonUniversalType(type); + } + else + { + TypeBuilderState state = type.GetOrCreateTypeBuilderState(); + NativeParser typeInfoParser = state.GetParserForNativeLayoutInfo(); + NativeParser fieldLayoutParser = typeInfoParser.GetParserForBagElementKind(BagElementKind.FieldLayout); + EnsureFieldLayoutLoadedForUniversalType(type, state.NativeLayoutInfo.LoadContext, fieldLayoutParser); + } + } + + private static void EnsureFieldLayoutLoadedForUniversalType(DefType type, NativeLayoutInfoLoadContext loadContext, NativeParser fieldLayoutParser) + { + Debug.Assert(type.HasInstantiation); + Debug.Assert(type.ComputeTemplate().IsCanonicalSubtype(CanonicalFormKind.Universal)); + + if (type.NativeLayoutFields != null) + return; + + type.NativeLayoutFields = ParseFieldLayout(type, loadContext, fieldLayoutParser); + } + + private static void EnsureFieldLayoutLoadedForNonUniversalType(DefType type) + { + Debug.Assert(type.HasInstantiation); + Debug.Assert(!type.ComputeTemplate().IsCanonicalSubtype(CanonicalFormKind.Universal)); + + if (type.NativeLayoutFields != null) + return; + + // Look up the universal template for this type. Only the universal template has field layout + // information, so we have to use it to parse the field layout. + NativeLayoutInfoLoadContext universalLayoutLoadContext; + NativeLayoutInfo universalLayoutInfo; + NativeParser typeInfoParser = type.GetOrCreateTypeBuilderState().GetParserForUniversalNativeLayoutInfo(out universalLayoutLoadContext, out universalLayoutInfo); + + if (typeInfoParser.IsNull) + throw new TypeBuilder.MissingTemplateException(); + + // Now parse that layout into the NativeLayoutFields array. + NativeParser fieldLayoutParser = typeInfoParser.GetParserForBagElementKind(BagElementKind.FieldLayout); + type.NativeLayoutFields = ParseFieldLayout(type, universalLayoutLoadContext, fieldLayoutParser); + } + + private static NativeLayoutFieldDesc[] ParseFieldLayout(DefType owningType, + NativeLayoutInfoLoadContext nativeLayoutInfoLoadContext, NativeParser fieldLayoutParser) + { + if (fieldLayoutParser.IsNull) + return Empty.Array; + + uint numFields = fieldLayoutParser.GetUnsigned(); + var fields = new NativeLayoutFieldDesc[numFields]; + + for (int i = 0; i < numFields; i++) + { + TypeDesc fieldType = nativeLayoutInfoLoadContext.GetType(ref fieldLayoutParser); + NativeFormat.FieldStorage storage = (NativeFormat.FieldStorage)fieldLayoutParser.GetUnsigned(); + fields[i] = new NativeLayoutFieldDesc(owningType, fieldType, storage); + } + + return fields; + } + + /// + /// Determine the state of things before we start processing the fields of a specific type. + /// This will initialize the state to be aware of the size/characteristics of base types, + /// and whether or not this type is a valuetype. + /// + /// Type we are computing layout for + /// What the initial Instance size should be + /// What is the basic alignment requirement of the base type or 1 if there is no base type to consider + internal void ComputeTypeSizeBeforeFields(TypeDesc type, out LayoutInt initialSize, out LayoutInt alignRequired) + { + // Account for the MethodTable pointer in objects... + initialSize = new LayoutInt(IntPtr.Size); + alignRequired = LayoutInt.One; + + if (type.IsValueType) + { + // ...unless the type is a ValueType which doesn't have the MethodTable pointer. + initialSize = LayoutInt.Zero; + } + else if (type.BaseType != null) + { + // If there is a base type, use the initialSize and alignRequired from that + DefType baseType = type.BaseType; + initialSize = baseType.InstanceByteCountUnaligned; + alignRequired = baseType.InstanceByteAlignment; + } + } + + /// + /// While computing layout, we don't generally compute the full field information. This function is used to + /// gate how much of field layout to run + /// + /// the conceptual location of the field + /// what sort of load was requested + /// + internal bool ShouldProcessField(NativeFormat.FieldStorage fieldStorage, FieldLoadState loadRequested) + { + if (fieldStorage == (int)NativeFormat.FieldStorage.Instance) + { + // Make sure we wanted to load instance fields. + if ((loadRequested & FieldLoadState.Instance) == FieldLoadState.None) + return false; + } + else if ((loadRequested & FieldLoadState.Statics) == FieldLoadState.None) + { + // Otherwise the field is a static, and we only want instance fields. + return false; + } + + return true; + } + + // The layout algorithm should probably compute results and let the caller set things + internal unsafe LayoutInt[] ComputeTypeSizeAndAlignment(TypeDesc type, FieldLoadState loadRequested, out LowLevelList fieldOffsets) + { + fieldOffsets = null; + TypeLoaderLogger.WriteLine("Laying out type " + type.ToString() + ". IsValueType: " + (type.IsValueType ? "true" : "false") + ". LoadRequested = " + ((int)loadRequested).LowLevelToString()); + + Debug.Assert(loadRequested != FieldLoadState.None); + Debug.Assert(type is ArrayType || (type is DefType && ((DefType)type).HasInstantiation)); + + bool isArray = type is ArrayType; + + LayoutInt[] position = new LayoutInt[5]; + LayoutInt alignRequired = LayoutInt.One; + + if ((loadRequested & FieldLoadState.Instance) == FieldLoadState.Instance) + { + ComputeTypeSizeBeforeFields(type, out position[(int)NativeFormat.FieldStorage.Instance], out alignRequired); + } + + if (!isArray) + { + // Once this is done, the NativeLayoutFields on the type are initialized + EnsureFieldLayoutLoadedForGenericType((DefType)type); + Debug.Assert(type.NativeLayoutFields != null); + } + + int instanceFields = 0; + + if (!isArray && type.NativeLayoutFields.Length > 0) + { + fieldOffsets = new LowLevelList(type.NativeLayoutFields.Length); + for (int i = 0; i < type.NativeLayoutFields.Length; i++) + { + TypeDesc fieldType = type.NativeLayoutFields[i].FieldType; + int fieldStorage = (int)type.NativeLayoutFields[i].FieldStorage; + + if (!ShouldProcessField((NativeFormat.FieldStorage)fieldStorage, loadRequested)) + continue; + + // For value types, we will attempt to get the size and alignment from + // the runtime if possible, otherwise GetFieldSizeAndAlignment will + // recurse to lay out nested struct fields. + LayoutInt alignment; + LayoutInt size; + GetFieldSizeAlignment(fieldType, out size, out alignment); + + Debug.Assert(alignment.AsInt > 0); + + if (fieldStorage == (int)NativeFormat.FieldStorage.Instance) + { + instanceFields++; + + // Ensure alignment of type is sufficient for this field + alignRequired = LayoutInt.Max(alignRequired, alignment); + } + + position[fieldStorage] = LayoutInt.AlignUp(position[fieldStorage], alignment, type.Context.Target); + TypeLoaderLogger.WriteLine(" --> Field type " + fieldType.ToString() + + " storage " + ((uint)(type.NativeLayoutFields[i].FieldStorage)).LowLevelToString() + + " offset " + position[fieldStorage].LowLevelToString() + + " alignment " + alignment.LowLevelToString()); + + fieldOffsets.Add(position[fieldStorage]); + position[fieldStorage] += size; + } + } + + // Pad the length of structs to be 1 if they are empty so we have no zero-length structures + if ((position[(int)NativeFormat.FieldStorage.Instance] == LayoutInt.Zero) && type.IsValueType) + position[(int)NativeFormat.FieldStorage.Instance] = LayoutInt.One; + + Debug.Assert(alignRequired == new LayoutInt(1) || + alignRequired == new LayoutInt(2) || + alignRequired == new LayoutInt(4) || + alignRequired == new LayoutInt(8)); + + position[InstanceAlignmentEntry] = alignRequired; + + return position; + } + + internal void GetFieldSizeAlignment(TypeDesc fieldType, out LayoutInt size, out LayoutInt alignment) + { + Debug.Assert(!fieldType.IsCanonicalSubtype(CanonicalFormKind.Any)); + + // All reference and array types are pointer-sized + if (!fieldType.IsValueType) + { + size = new LayoutInt(IntPtr.Size); + alignment = new LayoutInt(IntPtr.Size); + return; + } + + // Is this a type that already exists? If so, get its size from the MethodTable directly + if (fieldType.RetrieveRuntimeTypeHandleIfPossible()) + { + unsafe + { + MethodTable* MethodTable = fieldType.RuntimeTypeHandle.ToEETypePtr(); + size = new LayoutInt((int)MethodTable->ValueTypeSize); + alignment = new LayoutInt(MethodTable->FieldAlignmentRequirement); + return; + } + } + + // The type of the field must be a generic valuetype that is dynamically being constructed + Debug.Assert(fieldType.IsValueType); + DefType fieldDefType = (DefType)fieldType; + + TypeBuilderState state = fieldType.GetOrCreateTypeBuilderState(); + + size = fieldDefType.InstanceFieldSize; + alignment = fieldDefType.InstanceFieldAlignment; + } + + public unsafe override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type) + { + // Use this constant to make the code below more laconic + const ValueTypeShapeCharacteristics NotHA = ValueTypeShapeCharacteristics.None; + + Debug.Assert(type.IsValueType); + + TargetArchitecture targetArch = type.Context.Target.Architecture; + if ((targetArch != TargetArchitecture.ARM) && (targetArch != TargetArchitecture.ARM64)) + return NotHA; + + if (!type.IsValueType) + return NotHA; + + // There is no reason to compute the entire field layout for the HA type/flag if + // the template type is not a universal generic type (information stored in rare flags on the MethodTable) + TypeDesc templateType = type.ComputeTemplate(false); + if (templateType != null && !templateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + { + MethodTable* pEETemplate = templateType.GetRuntimeTypeHandle().ToEETypePtr(); + if (!pEETemplate->IsHFA) + return NotHA; + + if (pEETemplate->RequiresAlign8) + return ValueTypeShapeCharacteristics.Float64Aggregate; + else + return ValueTypeShapeCharacteristics.Float32Aggregate; + } + + // Once this is done, the NativeLayoutFields on the type are initialized + EnsureFieldLayoutLoadedForGenericType((DefType)type); + Debug.Assert(type.NativeLayoutFields != null); + + // Empty types are not HA + if (type.NativeLayoutFields.Length == 0) + return NotHA; + + // Find the common HA element type if any + ValueTypeShapeCharacteristics haResultType = NotHA; + + for (int i = 0; i < type.NativeLayoutFields.Length; i++) + { + TypeDesc fieldType = type.NativeLayoutFields[i].FieldType; + if (type.NativeLayoutFields[i].FieldStorage != NativeFormat.FieldStorage.Instance) + continue; + + // If a field isn't a DefType, then this type cannot be a HA type + if (!(fieldType is DefType fieldDefType)) + return NotHA; + + // HA types cannot contain non-HA types + ValueTypeShapeCharacteristics haFieldType = fieldDefType.ValueTypeShapeCharacteristics & ValueTypeShapeCharacteristics.AggregateMask; + if (haFieldType == NotHA) + return NotHA; + + if (haResultType == NotHA) + haResultType = haFieldType; + else if (haResultType != haFieldType) + return NotHA; // If the field doesn't have the same HA type as the one we've looked at before, the type cannot be HA + } + + // If we didn't find any instance fields, then this can't be a HA type + if (haResultType == NotHA) + return NotHA; + + int haElementSize = haResultType switch + { + ValueTypeShapeCharacteristics.Float32Aggregate => 4, + ValueTypeShapeCharacteristics.Float64Aggregate => 8, + ValueTypeShapeCharacteristics.Vector64Aggregate => 8, + ValueTypeShapeCharacteristics.Vector128Aggregate => 16, + _ => throw new ArgumentOutOfRangeException() + }; + + // Note that we check the total size, but do not perform any checks on number of fields: + // - Type of fields can be HA valuetype itself + // - Managed C++ HFA valuetypes have just one of type float to signal that + // the valuetype is HFA and explicitly specified size + int maxSize = haElementSize * type.Context.Target.MaxHomogeneousAggregateElementCount; + if (type.InstanceFieldSize.AsInt > maxSize) + return NotHA; + + return haResultType; + } + + public override bool ComputeIsUnsafeValueType(DefType type) + { + throw new NotSupportedException(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldDesc.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldDesc.cs new file mode 100644 index 00000000000000..056cb26016ef46 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldDesc.cs @@ -0,0 +1,103 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace Internal.Runtime.TypeLoader +{ + /// + /// Represents a field defined in native layout data, but without metadata + /// + internal class NativeLayoutFieldDesc : FieldDesc + { + private DefType _owningType; + private TypeDesc _fieldType; + private FieldStorage _fieldStorage; + + public NativeLayoutFieldDesc(DefType owningType, TypeDesc fieldType, FieldStorage fieldStorage) + { + _owningType = owningType; + _fieldType = fieldType; + _fieldStorage = fieldStorage; + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc FieldType + { + get + { + return _fieldType; + } + } + + public override bool HasRva + { + get + { + throw NotImplemented.ByDesign; + } + } + + public override bool IsInitOnly + { + get + { + throw NotImplemented.ByDesign; + } + } + + public override bool IsLiteral + { + get + { + return false; + } + } + + public override bool IsStatic + { + get + { + return _fieldStorage != FieldStorage.Instance; + } + } + + public override bool IsThreadStatic + { + get + { + return _fieldStorage == FieldStorage.TLSStatic; + } + } + + internal FieldStorage FieldStorage + { + get + { + return _fieldStorage; + } + } + + public override DefType OwningType + { + get + { + return _owningType; + } + } + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) + { + throw NotImplemented.ByDesign; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInfoLoadContext.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInfoLoadContext.cs new file mode 100644 index 00000000000000..9dcbbea70bdaad --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInfoLoadContext.cs @@ -0,0 +1,219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.TypeSystem.NoMetadata; + +namespace Internal.Runtime.TypeLoader +{ + // + // Wrap state required by the native layout info parsing in a structure, so that it can be conveniently passed around. + // + internal class NativeLayoutInfoLoadContext + { + public TypeSystemContext _typeSystemContext; + public NativeFormatModuleInfo _module; + private ExternalReferencesTable _staticInfoLookup; + private ExternalReferencesTable _externalReferencesLookup; + public Instantiation _typeArgumentHandles; + public Instantiation _methodArgumentHandles; + + private TypeDesc GetInstantiationType(ref NativeParser parser, uint arity) + { + DefType typeDefinition = (DefType)GetType(ref parser); + + TypeDesc[] typeArguments = new TypeDesc[arity]; + for (uint i = 0; i < arity; i++) + typeArguments[i] = GetType(ref parser); + + return _typeSystemContext.ResolveGenericInstantiation(typeDefinition, new Instantiation(typeArguments)); + } + + private TypeDesc GetModifierType(ref NativeParser parser, TypeModifierKind modifier) + { + TypeDesc typeParameter = GetType(ref parser); + + switch (modifier) + { + case TypeModifierKind.Array: + return _typeSystemContext.GetArrayType(typeParameter); + + case TypeModifierKind.ByRef: + return _typeSystemContext.GetByRefType(typeParameter); + + case TypeModifierKind.Pointer: + return _typeSystemContext.GetPointerType(typeParameter); + + default: + parser.ThrowBadImageFormatException(); + return null; + } + } + + private void InitializeExternalReferencesLookup() + { + if (!_externalReferencesLookup.IsInitialized()) + { + bool success = _externalReferencesLookup.InitializeNativeReferences(_module); + Debug.Assert(success); + } + } + + private IntPtr GetExternalReferencePointer(uint index) + { + InitializeExternalReferencesLookup(); + return _externalReferencesLookup.GetIntPtrFromIndex(index); + } + + internal TypeDesc GetExternalType(uint index) + { + InitializeExternalReferencesLookup(); + RuntimeTypeHandle rtth = _externalReferencesLookup.GetRuntimeTypeHandleFromIndex(index); + return _typeSystemContext.ResolveRuntimeTypeHandle(rtth); + } + + internal IntPtr GetGCStaticInfo(uint index) + { + if (!_staticInfoLookup.IsInitialized()) + { + bool success = _staticInfoLookup.InitializeNativeStatics(_module); + Debug.Assert(success); + } + + return _staticInfoLookup.GetIntPtrFromIndex(index); + } + + private unsafe TypeDesc GetLookbackType(ref NativeParser parser, uint lookback) + { + var lookbackParser = parser.GetLookbackParser(lookback); + return GetType(ref lookbackParser); + } + + internal TypeDesc GetType(ref NativeParser parser) + { + uint data; + var kind = parser.GetTypeSignatureKind(out data); + + switch (kind) + { + case TypeSignatureKind.Lookback: + return GetLookbackType(ref parser, data); + + case TypeSignatureKind.Variable: + uint index = data >> 1; + return (((data & 0x1) != 0) ? _methodArgumentHandles : _typeArgumentHandles)[checked((int)index)]; + + case TypeSignatureKind.Instantiation: + return GetInstantiationType(ref parser, data); + + case TypeSignatureKind.Modifier: + return GetModifierType(ref parser, (TypeModifierKind)data); + + case TypeSignatureKind.External: + return GetExternalType(data); + + case TypeSignatureKind.MultiDimArray: + { + DefType elementType = (DefType)GetType(ref parser); + int rank = (int)data; + + // Skip encoded bounds and lobounds + uint boundsCount = parser.GetUnsigned(); + while (boundsCount > 0) + { + parser.GetUnsigned(); + boundsCount--; + } + + uint loBoundsCount = parser.GetUnsigned(); + while (loBoundsCount > 0) + { + parser.GetUnsigned(); + loBoundsCount--; + } + + return _typeSystemContext.GetArrayType(elementType, rank); + } + + case TypeSignatureKind.BuiltIn: + return _typeSystemContext.GetWellKnownType((WellKnownType)data); + + case TypeSignatureKind.FunctionPointer: + Debug.Fail("NYI!"); + parser.ThrowBadImageFormatException(); + return null; + + default: + parser.ThrowBadImageFormatException(); + return null; + } + } + + internal MethodDesc GetMethod(ref NativeParser parser, out RuntimeSignature methodNameSig, out RuntimeSignature methodSig) + { + MethodFlags flags = (MethodFlags)parser.GetUnsigned(); + + IntPtr functionPointer = IntPtr.Zero; + if ((flags & MethodFlags.HasFunctionPointer) != 0) + functionPointer = GetExternalReferencePointer(parser.GetUnsigned()); + + DefType containingType = (DefType)GetType(ref parser); + MethodNameAndSignature nameAndSignature = TypeLoaderEnvironment.Instance.GetMethodNameAndSignature(ref parser, _module.Handle, out methodNameSig, out methodSig); + + bool unboxingStub = (flags & MethodFlags.IsUnboxingStub) != 0; + + MethodDesc retVal = null; + if ((flags & MethodFlags.HasInstantiation) != 0) + { + TypeDesc[] typeArguments = GetTypeSequence(ref parser); + Debug.Assert(typeArguments.Length > 0); + retVal = this._typeSystemContext.ResolveGenericMethodInstantiation(unboxingStub, containingType, nameAndSignature, new Instantiation(typeArguments), functionPointer, (flags & MethodFlags.FunctionPointerIsUSG) != 0); + } + else + { + retVal = this._typeSystemContext.ResolveRuntimeMethod(unboxingStub, containingType, nameAndSignature, functionPointer, (flags & MethodFlags.FunctionPointerIsUSG) != 0); + } + + if ((flags & MethodFlags.FunctionPointerIsUSG) != 0) + { + // TODO, consider a change such that if a USG function pointer is passed in, but we have + // a way to get a non-usg pointer, that may be preferable + Debug.Assert(retVal.UsgFunctionPointer != IntPtr.Zero); + } + + return retVal; + } + + internal MethodDesc GetMethod(ref NativeParser parser) + { + RuntimeSignature methodSig; + RuntimeSignature methodNameSig; + return GetMethod(ref parser, out methodNameSig, out methodSig); + } + + internal TypeDesc[] GetTypeSequence(ref NativeParser parser) + { + uint count = parser.GetSequenceCount(); + + TypeDesc[] sequence = new TypeDesc[count]; + + for (uint i = 0; i < count; i++) + { + sequence[i] = GetType(ref parser); + } + + return sequence; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInterfacesAlgorithm.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInterfacesAlgorithm.cs new file mode 100644 index 00000000000000..384297e70869b8 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInterfacesAlgorithm.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.NativeFormat; +using Internal.Runtime.Augments; +using Internal.TypeSystem; +using System; +using System.Diagnostics; + +namespace Internal.Runtime.TypeLoader +{ + /// + /// Reads interfaces for native layout types + /// + internal class NativeLayoutInterfacesAlgorithm : RuntimeInterfacesAlgorithm + { + public override DefType[] ComputeRuntimeInterfaces(TypeDesc type) + { + TypeBuilderState state = type.GetOrCreateTypeBuilderState(); + int totalInterfaces = RuntimeAugments.GetInterfaceCount(state.TemplateType.RuntimeTypeHandle); + + TypeLoaderLogger.WriteLine("Building runtime interfaces for type " + type.ToString() + " (total interfaces = " + totalInterfaces.LowLevelToString() + ") ..."); + + DefType[] interfaces = new DefType[totalInterfaces]; + int numInterfaces = 0; + + // + // Copy over all interfaces from base class + // + if (type.BaseType != null) + { + foreach (var baseInterface in type.BaseType.RuntimeInterfaces) + { + // There should be no duplicates + Debug.Assert(!InterfaceInSet(interfaces, numInterfaces, baseInterface)); + interfaces[numInterfaces++] = baseInterface; + TypeLoaderLogger.WriteLine(" -> Added basetype interface " + baseInterface.ToString() + " on type " + type.ToString()); + } + } + + NativeParser typeInfoParser = state.GetParserForNativeLayoutInfo(); + NativeParser interfaceParser = typeInfoParser.GetParserForBagElementKind(BagElementKind.ImplementedInterfaces); + TypeDesc[] implementedInterfaces; + if (!interfaceParser.IsNull) + implementedInterfaces = state.NativeLayoutInfo.LoadContext.GetTypeSequence(ref interfaceParser); + else + implementedInterfaces = TypeDesc.EmptyTypes; + + // Note that the order in which the interfaces are added to the list is same as the order in which the MDIL binder adds them. + // It is required for correctness + + foreach (TypeDesc interfaceType in implementedInterfaces) + { + DefType interfaceTypeAsDefType = (DefType)interfaceType; + + // Skip duplicates + if (InterfaceInSet(interfaces, numInterfaces, interfaceTypeAsDefType)) + continue; + interfaces[numInterfaces++] = interfaceTypeAsDefType; + + TypeLoaderLogger.WriteLine(" -> Added interface " + interfaceTypeAsDefType.ToString() + " on type " + type.ToString()); + + foreach (var inheritedInterface in interfaceTypeAsDefType.RuntimeInterfaces) + { + // Skip duplicates + if (InterfaceInSet(interfaces, numInterfaces, inheritedInterface)) + continue; + interfaces[numInterfaces++] = inheritedInterface; + TypeLoaderLogger.WriteLine(" -> Added inherited interface " + inheritedInterface.ToString() + " on type " + type.ToString()); + } + } + + // TODO: Handle the screwy cases of generic interface folding + Debug.Assert(numInterfaces == totalInterfaces, "Unexpected number of interfaces"); + + return interfaces; + } + + /// + /// Checks if the interface exists in the list of interfaces + /// + private static bool InterfaceInSet(DefType[] interfaces, int numInterfaces, DefType interfaceType) + { + for (int i = 0; i < numInterfaces; i++) + { + if (interfaces[i].Equals(interfaceType)) + return true; + } + + return false; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataFieldLayoutAlgorithm.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataFieldLayoutAlgorithm.cs new file mode 100644 index 00000000000000..60ea543a3ff228 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataFieldLayoutAlgorithm.cs @@ -0,0 +1,142 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; +using System.Diagnostics; + +namespace Internal.Runtime.TypeLoader +{ + /// + /// Useable when we have runtime MethodTable structures. Can represent the field layout necessary + /// to represent the size/alignment of the overall type, but must delegate to either NativeLayoutFieldAlgorithm + /// or MetadataFieldLayoutAlgorithm to get information about individual fields. + /// + internal class NoMetadataFieldLayoutAlgorithm : FieldLayoutAlgorithm + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + private MetadataFieldLayoutAlgorithm _metadataFieldLayoutAlgorithm = new MetadataFieldLayoutAlgorithm(); +#endif + private static NativeLayoutFieldAlgorithm s_nativeLayoutFieldAlgorithm = new NativeLayoutFieldAlgorithm(); + + public unsafe override bool ComputeContainsGCPointers(DefType type) + { + return type.RuntimeTypeHandle.ToEETypePtr()->HasGCPointers; + } + + /// + /// Reads the minimal information about type layout encoded in the + /// MethodTable. That doesn't include field information. + /// + public unsafe override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType type, InstanceLayoutKind layoutKind) + { + // If we need the field information, delegate to the native layout algorithm or metadata algorithm + if (layoutKind != InstanceLayoutKind.TypeOnly) + { + if (type.HasNativeLayout) + { + return s_nativeLayoutFieldAlgorithm.ComputeInstanceLayout(type, layoutKind); + } + else + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + return _metadataFieldLayoutAlgorithm.ComputeInstanceLayout(type, layoutKind); +#else + Debug.Assert(false); + return default; +#endif + } + } + + type.RetrieveRuntimeTypeHandleIfPossible(); + Debug.Assert(!type.RuntimeTypeHandle.IsNull()); + MethodTable* MethodTable = type.RuntimeTypeHandle.ToEETypePtr(); + + ComputedInstanceFieldLayout layout = new ComputedInstanceFieldLayout() + { + ByteCountAlignment = new LayoutInt(IntPtr.Size), + ByteCountUnaligned = new LayoutInt(MethodTable->IsInterface ? IntPtr.Size : checked((int)MethodTable->FieldByteCountNonGCAligned)), + FieldAlignment = new LayoutInt(MethodTable->FieldAlignmentRequirement), + Offsets = (layoutKind == InstanceLayoutKind.TypeOnly) ? null : Array.Empty(), // No fields in EETypes + }; + + if (MethodTable->IsValueType) + { + int valueTypeSize = checked((int)MethodTable->ValueTypeSize); + layout.FieldSize = new LayoutInt(valueTypeSize); + } + else + { + layout.FieldSize = new LayoutInt(IntPtr.Size); + } + + if ((MethodTable->RareFlags & EETypeRareFlags.RequiresAlign8Flag) == EETypeRareFlags.RequiresAlign8Flag) + { + layout.ByteCountAlignment = new LayoutInt(8); + } + + return layout; + } + + public override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefType type, StaticLayoutKind layoutKind) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + // We can only reach this for pre-created types where we actually need field information + // In that case, fall through to one of the other field layout algorithms. + if (type.HasNativeLayout) + return s_nativeLayoutFieldAlgorithm.ComputeStaticFieldLayout(type, layoutKind); + else if (type is MetadataType) + return _metadataFieldLayoutAlgorithm.ComputeStaticFieldLayout(type, layoutKind); + + // No statics information available + ComputedStaticFieldLayout staticLayout = new ComputedStaticFieldLayout() + { + GcStatics = default(StaticsBlock), + NonGcStatics = default(StaticsBlock), + Offsets = Array.Empty(), // No fields are considered to exist for completely NoMetadataTypes + ThreadGcStatics = default(StaticsBlock), + ThreadNonGcStatics = default(StaticsBlock), + }; + return staticLayout; +#else + Debug.Assert(false); + return default; +#endif + } + + public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type) + { + if (type.Context.Target.Architecture == TargetArchitecture.ARM) + { + unsafe + { + // On ARM, the HFA type is encoded into the MethodTable directly + type.RetrieveRuntimeTypeHandleIfPossible(); + Debug.Assert(!type.RuntimeTypeHandle.IsNull()); + MethodTable* MethodTable = type.RuntimeTypeHandle.ToEETypePtr(); + + if (!MethodTable->IsHFA) + return ValueTypeShapeCharacteristics.None; + + if (MethodTable->RequiresAlign8) + return ValueTypeShapeCharacteristics.Float64Aggregate; + else + return ValueTypeShapeCharacteristics.Float32Aggregate; + } + } + else + { + Debug.Assert( + type.Context.Target.Architecture == TargetArchitecture.X86 || + type.Context.Target.Architecture == TargetArchitecture.X64); + + return ValueTypeShapeCharacteristics.None; + } + } + + public override bool ComputeIsUnsafeValueType(DefType type) + { + throw new NotSupportedException(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataRuntimeInterfacesAlgorithm.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataRuntimeInterfacesAlgorithm.cs new file mode 100644 index 00000000000000..dc7522c819b086 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataRuntimeInterfacesAlgorithm.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; +using Internal.Runtime.Augments; + +namespace Internal.Runtime.TypeLoader +{ + /// + /// Gets interface information from the RuntimeTypeHandle for a type with no metadata + /// + internal class NoMetadataRuntimeInterfacesAlgorithm : RuntimeInterfacesAlgorithm + { + public override DefType[] ComputeRuntimeInterfaces(TypeDesc type) + { + int numInterfaces = RuntimeAugments.GetInterfaceCount(type.RuntimeTypeHandle); + DefType[] interfaces = new DefType[numInterfaces]; + for (int i = 0; i < numInterfaces; i++) + { + RuntimeTypeHandle itfHandle = RuntimeAugments.GetInterface(type.RuntimeTypeHandle, i); + TypeDesc itfType = type.Context.ResolveRuntimeTypeHandle(itfHandle); + interfaces[i] = (DefType)itfType; + } + return interfaces; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/OptionalFields.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/OptionalFields.cs new file mode 100644 index 00000000000000..187bbd7dea31a7 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/OptionalFields.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime.InteropServices; + +using Internal.NativeFormat; + +namespace Internal.Runtime.TypeLoader +{ + internal unsafe class OptionalFieldsRuntimeBuilder + { + private struct OptionalField + { + internal bool m_fPresent; + internal uint m_uiValue; + } + + internal OptionalFieldsRuntimeBuilder(byte* pInitializeFromOptionalFields = null) + { + if (pInitializeFromOptionalFields == null) + return; + + bool isLastField = false; + while (!isLastField) + { + byte fieldHeader = NativePrimitiveDecoder.ReadUInt8(ref pInitializeFromOptionalFields); + isLastField = (fieldHeader & 0x80) != 0; + EETypeOptionalFieldTag eCurrentTag = (EETypeOptionalFieldTag)(fieldHeader & 0x7f); + uint uiCurrentValue = NativePrimitiveDecoder.DecodeUnsigned(ref pInitializeFromOptionalFields); + + _rgFields[(int)eCurrentTag].m_fPresent = true; + _rgFields[(int)eCurrentTag].m_uiValue = uiCurrentValue; + } + } + + internal uint GetFieldValue(EETypeOptionalFieldTag eTag, uint defaultValueIfNotFound) + { + return _rgFields[(int)eTag].m_fPresent ? _rgFields[(int)eTag].m_uiValue : defaultValueIfNotFound; + } + + internal void SetFieldValue(EETypeOptionalFieldTag eTag, uint value) + { + _rgFields[(int)eTag].m_fPresent = true; + _rgFields[(int)eTag].m_uiValue = value; + } + + internal void ClearField(EETypeOptionalFieldTag eTag) + { + _rgFields[(int)eTag].m_fPresent = false; + } + + internal int Encode() + { + EETypeOptionalFieldTag eLastTag = EETypeOptionalFieldTag.Count; + + for (EETypeOptionalFieldTag eTag = 0; eTag < EETypeOptionalFieldTag.Count; eTag++) + eLastTag = _rgFields[(int)eTag].m_fPresent ? eTag : eLastTag; + + if (eLastTag == EETypeOptionalFieldTag.Count) + return 0; + + _encoder = new NativePrimitiveEncoder(); + _encoder.Init(); + + for (EETypeOptionalFieldTag eTag = 0; eTag < EETypeOptionalFieldTag.Count; eTag++) + { + if (!_rgFields[(int)eTag].m_fPresent) + continue; + + _encoder.WriteByte((byte)((byte)eTag | (eTag == eLastTag ? 0x80 : 0))); + _encoder.WriteUnsigned(_rgFields[(int)eTag].m_uiValue); + } + + return _encoder.Size; + } + + internal void WriteToEEType(MethodTable* pEEType, int sizeOfOptionalFieldsDataInEEType) + { + byte* pOptionalFieldsPtr = pEEType->OptionalFieldsPtr; + _encoder.Save(pOptionalFieldsPtr, sizeOfOptionalFieldsDataInEEType); + } + + private NativePrimitiveEncoder _encoder; + private OptionalField[] _rgFields = new OptionalField[(int)EETypeOptionalFieldTag.Count]; + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/PermanentAllocatedMemoryBlobs.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/PermanentAllocatedMemoryBlobs.cs new file mode 100644 index 00000000000000..b77d7505301c40 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/PermanentAllocatedMemoryBlobs.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +using Internal.Runtime; +using Internal.Runtime.Augments; + +using Internal.NativeFormat; + +namespace Internal.Runtime.TypeLoader +{ + public sealed partial class PermanentAllocatedMemoryBlobs + { + // Various functions in the type loader need to create permanent pointers for various purposes. + + private static PermanentlyAllocatedMemoryRegions_Uint_In_IntPtr s_uintCellValues = new PermanentlyAllocatedMemoryRegions_Uint_In_IntPtr(); + private static PermanentlyAllocatedMemoryRegions_IntPtr_In_IntPtr s_pointerIndirectionCellValues = new PermanentlyAllocatedMemoryRegions_IntPtr_In_IntPtr(); + + private class PermanentlyAllocatedMemoryRegions_Uint_In_IntPtr + { + private LowLevelDictionary _allocatedBlocks = new LowLevelDictionary(); + private Lock _lock = new Lock(); + + public unsafe IntPtr GetMemoryBlockForValue(uint value) + { + using (LockHolder.Hold(_lock)) + { + IntPtr result; + if (_allocatedBlocks.TryGetValue(value, out result)) + { + return result; + } + result = MemoryHelpers.AllocateMemory(IntPtr.Size); + *(uint*)(result.ToPointer()) = value; + _allocatedBlocks.Add(value, result); + return result; + } + } + } + + private class PermanentlyAllocatedMemoryRegions_IntPtr_In_IntPtr + { + private LowLevelDictionary _allocatedBlocks = new LowLevelDictionary(); + private Lock _lock = new Lock(); + + public unsafe IntPtr GetMemoryBlockForValue(IntPtr value) + { + using (LockHolder.Hold(_lock)) + { + IntPtr result; + if (_allocatedBlocks.TryGetValue(value, out result)) + { + return result; + } + result = MemoryHelpers.AllocateMemory(IntPtr.Size); + *(IntPtr*)(result.ToPointer()) = value; + _allocatedBlocks.Add(value, result); + return result; + } + } + } + + public static IntPtr GetPointerToUInt(uint value) + { + return s_uintCellValues.GetMemoryBlockForValue(value); + } + + public static IntPtr GetPointerToIntPtr(IntPtr value) + { + return s_pointerIndirectionCellValues.GetMemoryBlockForValue(value); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/CallingConvention.h b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/CallingConvention.h new file mode 100644 index 00000000000000..87a785c92321ab --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/CallingConvention.h @@ -0,0 +1,1747 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + + +// +// Provides an abstraction over platform specific calling conventions (specifically, the calling convention +// utilized by the JIT on that platform). The caller enumerates each argument of a signature in turn, and is +// provided with information mapping that argument into registers and/or stack locations. +// + +#ifndef __CALLING_CONVENTION_INCLUDED +#define __CALLING_CONVENTION_INCLUDED + +BOOL IsRetBuffPassedAsFirstArg(); + +// Describes how a single argument is laid out in registers and/or stack locations when given as an input to a +// managed method as part of a larger signature. +// +// Locations are split into floating point registers, general registers and stack offsets. Registers are +// obviously architecture dependent but are represented as a zero-based index into the usual sequence in which +// such registers are allocated for input on the platform in question. For instance: +// X86: 0 == ecx, 1 == edx +// ARM: 0 == r0, 1 == r1, 2 == r2 etc. +// +// Stack locations are represented as offsets from the stack pointer (at the point of the call). The offset is +// given as an index of a pointer sized slot. Similarly the size of data on the stack is given in slot-sized +// units. For instance, given an index of 2 and a size of 3: +// X86: argument starts at [ESP + 8] and is 12 bytes long +// AMD64: argument starts at [RSP + 16] and is 24 bytes long +// +// The structure is flexible enough to describe an argument that is split over several (consecutive) registers +// and possibly on to the stack as well. +struct ArgLocDesc +{ + int m_idxFloatReg; // First floating point register used (or -1) + int m_cFloatReg; // Count of floating point registers used (or 0) + + int m_idxGenReg; // First general register used (or -1) + int m_cGenReg; // Count of general registers used (or 0) + + int m_idxStack; // First stack slot used (or -1) + int m_cStack; // Count of stack slots used (or 0) + +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + + EEClass* m_eeClass; // For structs passed in register, it points to the EEClass of the struct + +#endif // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING + +#if defined(_TARGET_ARM64_) + bool m_isSinglePrecision; // For determining if HFA is single or double + // precision +#endif // defined(_TARGET_ARM64_) + +#if defined(_TARGET_ARM_) + BOOL m_fRequires64BitAlignment; // True if the argument should always be aligned (in registers or on the stack +#endif + + ArgLocDesc() + { + Init(); + } + + // Initialize to represent a non-placed argument (no register or stack slots referenced). + void Init() + { + m_idxFloatReg = -1; + m_cFloatReg = 0; + m_idxGenReg = -1; + m_cGenReg = 0; + m_idxStack = -1; + m_cStack = 0; +#if defined(_TARGET_ARM_) + m_fRequires64BitAlignment = FALSE; +#endif +#if defined(_TARGET_ARM64_) + m_isSinglePrecision = FALSE; +#endif // defined(_TARGET_ARM64_) +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + m_eeClass = NULL; +#endif + } +}; + +// +// TransitionBlock is layout of stack frame of method call, saved argument registers and saved callee saved registers. Even though not +// all fields are used all the time, we use uniform form for simplicity. +// +struct TransitionBlock +{ +#if defined(_TARGET_X86_) + ArgumentRegisters m_argumentRegisters; + CalleeSavedRegisters m_calleeSavedRegisters; + TADDR m_ReturnAddress; +#elif defined(_TARGET_AMD64_) +#ifdef UNIX_AMD64_ABI + ArgumentRegisters m_argumentRegisters; +#endif + CalleeSavedRegisters m_calleeSavedRegisters; + TADDR m_ReturnAddress; +#elif defined(_TARGET_ARM_) + union { + CalleeSavedRegisters m_calleeSavedRegisters; + // alias saved link register as m_ReturnAddress + struct { + INT32 r4, r5, r6, r7, r8, r9, r10; + INT32 r11; + TADDR m_ReturnAddress; + }; + }; + ArgumentRegisters m_argumentRegisters; +#elif defined(_TARGET_ARM64_) + union { + CalleeSavedRegisters m_calleeSavedRegisters; + struct { + INT64 x29; // frame pointer + TADDR m_ReturnAddress; + INT64 x19, x20, x21, x22, x23, x24, x25, x26, x27, x28; + }; + }; + ArgumentRegisters m_argumentRegisters; + TADDR padding; // Keep size of TransitionBlock as multiple of 16-byte. Simplifies code in PROLOG_WITH_TRANSITION_BLOCK +#else + PORTABILITY_ASSERT("TransitionBlock"); +#endif + + // The transition block should define everything pushed by callee. The code assumes in number of places that + // end of the transition block is caller's stack pointer. + + static int GetOffsetOfReturnAddress() + { + LIMITED_METHOD_CONTRACT; + return offsetof(TransitionBlock, m_ReturnAddress); + } + + static BYTE GetOffsetOfArgs() + { + LIMITED_METHOD_CONTRACT; + return sizeof(TransitionBlock); + } + + static int GetOffsetOfArgumentRegisters() + { + LIMITED_METHOD_CONTRACT; + int offs; +#if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI) + offs = sizeof(TransitionBlock); +#else + offs = offsetof(TransitionBlock, m_argumentRegisters); +#endif + return offs; + } + + static BOOL IsStackArgumentOffset(int offset) + { + LIMITED_METHOD_CONTRACT; + +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + return offset >= sizeof(TransitionBlock); +#else + int ofsArgRegs = GetOffsetOfArgumentRegisters(); + + return offset >= (int) (ofsArgRegs + ARGUMENTREGISTERS_SIZE); +#endif + } + + static BOOL IsArgumentRegisterOffset(int offset) + { + LIMITED_METHOD_CONTRACT; + + int ofsArgRegs = GetOffsetOfArgumentRegisters(); + + return offset >= ofsArgRegs && offset < (int) (ofsArgRegs + ARGUMENTREGISTERS_SIZE); + } + +#ifndef _TARGET_X86_ + static UINT GetArgumentIndexFromOffset(int offset) + { + LIMITED_METHOD_CONTRACT; + +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + _ASSERTE(offset != TransitionBlock::StructInRegsOffset); +#endif + return (offset - GetOffsetOfArgumentRegisters()) / sizeof(TADDR); + } + + static UINT GetStackArgumentIndexFromOffset(int offset) + { + LIMITED_METHOD_CONTRACT; + + return (offset - TransitionBlock::GetOffsetOfArgs()) / STACK_ELEM_SIZE; + } + +#endif + +#ifdef CALLDESCR_FPARGREGS + static BOOL IsFloatArgumentRegisterOffset(int offset) + { + LIMITED_METHOD_CONTRACT; +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + return (offset != TransitionBlock::StructInRegsOffset) && (offset < 0); +#else + return offset < 0; +#endif + } + + // Check if an argument has floating point register, that means that it is + // either a floating point argument or a struct passed in registers that + // has a floating point member. + static BOOL HasFloatRegister(int offset, ArgLocDesc* argLocDescForStructInRegs) + { + LIMITED_METHOD_CONTRACT; + #if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + if (offset == TransitionBlock::StructInRegsOffset) + { + return argLocDescForStructInRegs->m_cFloatReg > 0; + } + #endif + return offset < 0; + } + + static int GetOffsetOfFloatArgumentRegisters() + { + LIMITED_METHOD_CONTRACT; + return -GetNegSpaceSize(); + } +#endif // CALLDESCR_FPARGREGS + + static int GetOffsetOfCalleeSavedRegisters() + { + LIMITED_METHOD_CONTRACT; + return offsetof(TransitionBlock, m_calleeSavedRegisters); + } + + static int GetNegSpaceSize() + { + LIMITED_METHOD_CONTRACT; + int negSpaceSize = 0; +#ifdef CALLDESCR_FPARGREGS + negSpaceSize += sizeof(FloatArgumentRegisters); +#endif +#ifdef _TARGET_ARM_ + negSpaceSize += sizeof(TADDR); // padding to make FloatArgumentRegisters address 8-byte aligned +#endif + return negSpaceSize; + } + + static const int InvalidOffset = -1; +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + // Special offset value to represent struct passed in registers. Such a struct can span both + // general purpose and floating point registers, so it can have two different offsets. + static const int StructInRegsOffset = -2; +#endif +}; + +//----------------------------------------------------------------------- +// ArgIterator is helper for dealing with calling conventions. +// It is tightly coupled with TransitionBlock. It uses offsets into +// TransitionBlock to represent argument locations for efficiency +// reasons. Alternatively, it can also return ArgLocDesc for less +// performance critical code. +// +// The ARGITERATOR_BASE argument of the template is provider of the parsed +// method signature. Typically, the arg iterator works on top of MetaSig. +// Reflection invoke uses alternative implementation to save signature parsing +// time because of it has the parsed signature available. +//----------------------------------------------------------------------- +template +class ArgIteratorTemplate : public ARGITERATOR_BASE +{ +public: + //------------------------------------------------------------ + // Constructor + //------------------------------------------------------------ + ArgIteratorTemplate() + { + WRAPPER_NO_CONTRACT; + m_dwFlags = 0; + } + + UINT SizeOfArgStack() + { + WRAPPER_NO_CONTRACT; + if (!(m_dwFlags & SIZE_OF_ARG_STACK_COMPUTED)) + ForceSigWalk(); + _ASSERTE((m_dwFlags & SIZE_OF_ARG_STACK_COMPUTED) != 0); + return m_nSizeOfArgStack; + } + + // For use with ArgIterator. This function computes the amount of additional + // memory required above the TransitionBlock. The parameter offsets + // returned by ArgIteratorTemplate::GetNextOffset are relative to a + // FramedMethodFrame, and may be in either of these regions. + UINT SizeOfFrameArgumentArray() + { + WRAPPER_NO_CONTRACT; + + UINT size = SizeOfArgStack(); + +#if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI) + // The argument registers are not included in the stack size on AMD64 + size += ARGUMENTREGISTERS_SIZE; +#endif + + return size; + } + + //------------------------------------------------------------------------ + +#ifdef _TARGET_X86_ + UINT CbStackPop() + { + WRAPPER_NO_CONTRACT; + + if (this->IsVarArg()) + return 0; + else + return SizeOfArgStack(); + } +#endif + + // Is there a hidden parameter for the return parameter? + // + BOOL HasRetBuffArg() + { + WRAPPER_NO_CONTRACT; + if (!(m_dwFlags & RETURN_FLAGS_COMPUTED)) + ComputeReturnFlags(); + return (m_dwFlags & RETURN_HAS_RET_BUFFER); + } + + UINT GetFPReturnSize() + { + WRAPPER_NO_CONTRACT; + if (!(m_dwFlags & RETURN_FLAGS_COMPUTED)) + ComputeReturnFlags(); + return m_dwFlags >> RETURN_FP_SIZE_SHIFT; + } + +#ifdef _TARGET_X86_ + //========================================================================= + // Indicates whether an argument is to be put in a register using the + // default IL calling convention. This should be called on each parameter + // in the order it appears in the call signature. For a non-static method, + // this function should also be called once for the "this" argument, prior + // to calling it for the "real" arguments. Pass in a typ of ELEMENT_TYPE_CLASS. + // + // *pNumRegistersUsed: [in,out]: keeps track of the number of argument + // registers assigned previously. The caller should + // initialize this variable to 0 - then each call + // will update it. + // + // typ: the signature type + //========================================================================= + static BOOL IsArgumentInRegister(int * pNumRegistersUsed, CorElementType typ) + { + LIMITED_METHOD_CONTRACT; + if ( (*pNumRegistersUsed) < NUM_ARGUMENT_REGISTERS) { + if (gElementTypeInfo[typ].m_enregister) { + (*pNumRegistersUsed)++; + return(TRUE); + } + } + + return(FALSE); + } +#endif // _TARGET_X86_ + +#if defined(ENREGISTERED_PARAMTYPE_MAXSIZE) + + // Note that this overload does not handle varargs + static BOOL IsArgPassedByRef(TypeHandle th) + { + LIMITED_METHOD_CONTRACT; + + _ASSERTE(!th.IsNull()); + + // This method only works for valuetypes. It includes true value types, + // primitives, enums and TypedReference. + _ASSERTE(th.IsValueType()); + + size_t size = th.GetSize(); +#ifdef _TARGET_AMD64_ + return IsArgPassedByRef(size); +#elif defined(_TARGET_ARM64_) + // Composites greater than 16 bytes are passed by reference + return ((size > ENREGISTERED_PARAMTYPE_MAXSIZE) && !th.IsHFA()); +#else + PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef"); + return FALSE; +#endif + } + +#ifdef _TARGET_AMD64_ + // This overload should only be used in AMD64-specific code only. + static BOOL IsArgPassedByRef(size_t size) + { + LIMITED_METHOD_CONTRACT; + +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + // No arguments are passed by reference on AMD64 on Unix + return FALSE; +#else + // If the size is bigger than ENREGISTERED_PARAM_TYPE_MAXSIZE, or if the size is NOT a power of 2, then + // the argument is passed by reference. + return (size > ENREGISTERED_PARAMTYPE_MAXSIZE) || ((size & (size-1)) != 0); +#endif + } +#endif // _TARGET_AMD64_ + + // This overload should be used for varargs only. + static BOOL IsVarArgPassedByRef(size_t size) + { + LIMITED_METHOD_CONTRACT; + +#ifdef _TARGET_AMD64_ +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + PORTABILITY_ASSERT("ArgIteratorTemplate::IsVarArgPassedByRef"); + return FALSE; +#else // FEATURE_UNIX_AMD64_STRUCT_PASSING + return IsArgPassedByRef(size); +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING + +#else + return (size > ENREGISTERED_PARAMTYPE_MAXSIZE); +#endif + } + + BOOL IsArgPassedByRef() + { + LIMITED_METHOD_CONTRACT; + +#ifdef _TARGET_AMD64_ + return IsArgPassedByRef(m_argSize); +#elif defined(_TARGET_ARM64_) + if (m_argType == ELEMENT_TYPE_VALUETYPE) + { + _ASSERTE(!m_argTypeHandle.IsNull()); + return ((m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) && (!m_argTypeHandle.IsHFA() || this->IsVarArg())); + } + return FALSE; +#else + PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef"); + return FALSE; +#endif + } + +#endif // ENREGISTERED_PARAMTYPE_MAXSIZE + + //------------------------------------------------------------ + // Return the offsets of the special arguments + //------------------------------------------------------------ + + static int GetThisOffset(); + + int GetRetBuffArgOffset(); + int GetVASigCookieOffset(); + int GetParamTypeArgOffset(); + + //------------------------------------------------------------ + // Each time this is called, this returns a byte offset of the next + // argument from the TransitionBlock* pointer. + // + // Returns TransitionBlock::InvalidOffset once you've hit the end + // of the list. + //------------------------------------------------------------ + int GetNextOffset(); + + CorElementType GetArgType(TypeHandle *pTypeHandle = NULL) + { + LIMITED_METHOD_CONTRACT; + if (pTypeHandle != NULL) + { + *pTypeHandle = m_argTypeHandle; + } + return m_argType; + } + + int GetArgSize() + { + LIMITED_METHOD_CONTRACT; + return m_argSize; + } + + void ForceSigWalk(); + +#ifndef _TARGET_X86_ + // Accessors for built in argument descriptions of the special implicit parameters not mentioned directly + // in signatures (this pointer and the like). Whether or not these can be used successfully before all the + // explicit arguments have been scanned is platform dependent. + void GetThisLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetThisOffset(), pLoc); } + void GetRetBuffArgLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetRetBuffArgOffset(), pLoc); } + void GetParamTypeLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetParamTypeArgOffset(), pLoc); } + void GetVASigCookieLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetVASigCookieOffset(), pLoc); } +#endif // !_TARGET_X86_ + + ArgLocDesc* GetArgLocDescForStructInRegs() + { +#if (defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)) || defined (_TARGET_ARM64_) + return m_hasArgLocDescForStructInRegs ? &m_argLocDescForStructInRegs : NULL; +#else + return NULL; +#endif + } + +#ifdef _TARGET_ARM_ + // Get layout information for the argument that the ArgIterator is currently visiting. + void GetArgLoc(int argOffset, ArgLocDesc *pLoc) + { + LIMITED_METHOD_CONTRACT; + + pLoc->Init(); + + pLoc->m_fRequires64BitAlignment = m_fRequires64BitAlignment; + + int cSlots = (GetArgSize() + 3) / 4; + + if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset)) + { + pLoc->m_idxFloatReg = (argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters()) / 4; + pLoc->m_cFloatReg = cSlots; + return; + } + + if (!TransitionBlock::IsStackArgumentOffset(argOffset)) + { + pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset); + + if (cSlots <= (4 - pLoc->m_idxGenReg)) + { + pLoc->m_cGenReg = cSlots; + } + else + { + pLoc->m_cGenReg = 4 - pLoc->m_idxGenReg; + + pLoc->m_idxStack = 0; + pLoc->m_cStack = cSlots - pLoc->m_cGenReg; + } + } + else + { + pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset); + pLoc->m_cStack = cSlots; + } + } +#endif // _TARGET_ARM_ + +#ifdef _TARGET_ARM64_ + // Get layout information for the argument that the ArgIterator is currently visiting. + void GetArgLoc(int argOffset, ArgLocDesc *pLoc) + { + LIMITED_METHOD_CONTRACT; + + pLoc->Init(); + + if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset)) + { + // Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes. + pLoc->m_idxFloatReg = (argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters()) / 8; + + if (!m_argTypeHandle.IsNull() && m_argTypeHandle.IsHFA()) + { + CorElementType type = m_argTypeHandle.GetHFAType(); + bool isFloatType = (type == ELEMENT_TYPE_R4); + + pLoc->m_cFloatReg = isFloatType ? GetArgSize()/sizeof(float): GetArgSize()/sizeof(double); + pLoc->m_isSinglePrecision = isFloatType; + } + else + { + pLoc->m_cFloatReg = 1; + } + return; + } + + int cSlots = (GetArgSize() + 7)/ 8; + + // Composites greater than 16bytes are passed by reference + if (GetArgType() == ELEMENT_TYPE_VALUETYPE && GetArgSize() > ENREGISTERED_PARAMTYPE_MAXSIZE) + { + cSlots = 1; + } + + if (!TransitionBlock::IsStackArgumentOffset(argOffset)) + { + pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset); + pLoc->m_cGenReg = cSlots; + } + else + { + pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset); + pLoc->m_cStack = cSlots; + } + } +#endif // _TARGET_ARM64_ + +#if defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI) + // Get layout information for the argument that the ArgIterator is currently visiting. + void GetArgLoc(int argOffset, ArgLocDesc* pLoc) + { + LIMITED_METHOD_CONTRACT; + +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + if (m_hasArgLocDescForStructInRegs) + { + *pLoc = m_argLocDescForStructInRegs; + return; + } +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING + + if (argOffset == TransitionBlock::StructInRegsOffset) + { + // We always already have argLocDesc for structs passed in registers, we + // compute it in the GetNextOffset for those since it is always needed. + _ASSERTE(false); + return; + } + + pLoc->Init(); + + if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset)) + { + // Dividing by 16 as size of each register in FloatArgumentRegisters is 16 bytes. + pLoc->m_idxFloatReg = (argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters()) / 16; + pLoc->m_cFloatReg = 1; + } + else if (!TransitionBlock::IsStackArgumentOffset(argOffset)) + { + pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset); + pLoc->m_cGenReg = 1; + } + else + { + pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset); + pLoc->m_cStack = (GetArgSize() + STACK_ELEM_SIZE - 1) / STACK_ELEM_SIZE; + } + } +#endif // _TARGET_AMD64_ && UNIX_AMD64_ABI + +protected: + DWORD m_dwFlags; // Cached flags + int m_nSizeOfArgStack; // Cached value of SizeOfArgStack + + DWORD m_argNum; + + // Cached information about last argument + CorElementType m_argType; + int m_argSize; + TypeHandle m_argTypeHandle; +#if (defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)) || defined(_TARGET_ARM64_) + ArgLocDesc m_argLocDescForStructInRegs; + bool m_hasArgLocDescForStructInRegs; +#endif // _TARGET_AMD64_ && UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING + +#ifdef _TARGET_X86_ + int m_curOfs; // Current position of the stack iterator + int m_numRegistersUsed; +#endif + +#ifdef _TARGET_AMD64_ +#ifdef UNIX_AMD64_ABI + int m_idxGenReg; // Next general register to be assigned a value + int m_idxStack; // Next stack slot to be assigned a value + int m_idxFPReg; // Next floating point register to be assigned a value +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + bool m_fArgInRegisters; // Indicates that the current argument is stored in registers +#endif +#else + int m_curOfs; // Current position of the stack iterator +#endif +#endif + +#ifdef _TARGET_ARM_ + int m_idxGenReg; // Next general register to be assigned a value + int m_idxStack; // Next stack slot to be assigned a value + + WORD m_wFPRegs; // Bitmask of available floating point argument registers (s0-s15/d0-d7) + bool m_fRequires64BitAlignment; // Cached info about the current arg +#endif + +#ifdef _TARGET_ARM64_ + int m_idxGenReg; // Next general register to be assigned a value + int m_idxStack; // Next stack slot to be assigned a value + int m_idxFPReg; // Next FP register to be assigned a value +#endif + + enum { + ITERATION_STARTED = 0x0001, // Started iterating over arguments + SIZE_OF_ARG_STACK_COMPUTED = 0x0002, + RETURN_FLAGS_COMPUTED = 0x0004, + RETURN_HAS_RET_BUFFER = 0x0008, // Cached value of HasRetBuffArg + +#ifdef _TARGET_X86_ + PARAM_TYPE_REGISTER_MASK = 0x0030, + PARAM_TYPE_REGISTER_STACK = 0x0010, + PARAM_TYPE_REGISTER_ECX = 0x0020, + PARAM_TYPE_REGISTER_EDX = 0x0030, +#endif + + METHOD_INVOKE_NEEDS_ACTIVATION = 0x0040, // Flag used by ArgIteratorForMethodInvoke + + RETURN_FP_SIZE_SHIFT = 8, // The rest of the flags is cached value of GetFPReturnSize + }; + + void ComputeReturnFlags(); + +#ifndef _TARGET_X86_ + void GetSimpleLoc(int offset, ArgLocDesc * pLoc) + { + WRAPPER_NO_CONTRACT; + pLoc->Init(); + pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(offset); + pLoc->m_cGenReg = 1; + } +#endif +}; + + +template +int ArgIteratorTemplate::GetThisOffset() +{ + WRAPPER_NO_CONTRACT; + + // This pointer is in the first argument register by default + int ret = TransitionBlock::GetOffsetOfArgumentRegisters(); + +#ifdef _TARGET_X86_ + // x86 is special as always + ret += offsetof(ArgumentRegisters, ECX); +#endif + + return ret; +} + +template +int ArgIteratorTemplate::GetRetBuffArgOffset() +{ + WRAPPER_NO_CONTRACT; + + _ASSERTE(this->HasRetBuffArg()); + + // RetBuf arg is in the second argument register by default + int ret = TransitionBlock::GetOffsetOfArgumentRegisters(); + +#if _TARGET_X86_ + // x86 is special as always + ret += this->HasThis() ? offsetof(ArgumentRegisters, EDX) : offsetof(ArgumentRegisters, ECX); +#elif _TARGET_ARM64_ + ret += (int) offsetof(ArgumentRegisters, x[8]); +#else + if (this->HasThis()) + ret += sizeof(void *); +#endif + + return ret; +} + +template +int ArgIteratorTemplate::GetVASigCookieOffset() +{ + WRAPPER_NO_CONTRACT; + + _ASSERTE(this->IsVarArg()); + +#if defined(_TARGET_X86_) + // x86 is special as always + return sizeof(TransitionBlock); +#else + // VaSig cookie is after this and retbuf arguments by default. + int ret = TransitionBlock::GetOffsetOfArgumentRegisters(); + + if (this->HasThis()) + { + ret += sizeof(void*); + } + + if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) + { + ret += sizeof(void*); + } + + return ret; +#endif +} + +//----------------------------------------------------------- +// Get the extra param offset for shared generic code +//----------------------------------------------------------- +template +int ArgIteratorTemplate::GetParamTypeArgOffset() +{ + CONTRACTL + { + INSTANCE_CHECK; + if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; + if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS; + if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); } + MODE_ANY; + } + CONTRACTL_END + + _ASSERTE(this->HasParamType()); + +#ifdef _TARGET_X86_ + // x86 is special as always + if (!(m_dwFlags & SIZE_OF_ARG_STACK_COMPUTED)) + ForceSigWalk(); + + switch (m_dwFlags & PARAM_TYPE_REGISTER_MASK) + { + case PARAM_TYPE_REGISTER_ECX: + return TransitionBlock::GetOffsetOfArgumentRegisters() + offsetof(ArgumentRegisters, ECX); + case PARAM_TYPE_REGISTER_EDX: + return TransitionBlock::GetOffsetOfArgumentRegisters() + offsetof(ArgumentRegisters, EDX); + default: + break; + } + + // The param type arg is last stack argument otherwise + return sizeof(TransitionBlock); +#else + // The hidden arg is after this and retbuf arguments by default. + int ret = TransitionBlock::GetOffsetOfArgumentRegisters(); + + if (this->HasThis()) + { + ret += sizeof(void*); + } + + if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) + { + ret += sizeof(void*); + } + + return ret; +#endif +} + +// To avoid corner case bugs, limit maximum size of the arguments with sufficient margin +#define MAX_ARG_SIZE 0xFFFFFF + +//------------------------------------------------------------ +// Each time this is called, this returns a byte offset of the next +// argument from the Frame* pointer. This offset can be positive *or* negative. +// +// Returns TransitionBlock::InvalidOffset once you've hit the end of the list. +//------------------------------------------------------------ +template +int ArgIteratorTemplate::GetNextOffset() +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if (!(m_dwFlags & ITERATION_STARTED)) + { + int numRegistersUsed = 0; + + if (this->HasThis()) + numRegistersUsed++; + + if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) + numRegistersUsed++; + + _ASSERTE(!this->IsVarArg() || !this->HasParamType()); + +#ifndef _TARGET_X86_ + if (this->IsVarArg() || this->HasParamType()) + { + numRegistersUsed++; + } +#endif + +#ifdef _TARGET_X86_ + if (this->IsVarArg()) + { + numRegistersUsed = NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs + } + +#ifdef FEATURE_INTERPRETER + BYTE callconv = CallConv(); + switch (callconv) + { + case IMAGE_CEE_CS_CALLCONV_C: + case IMAGE_CEE_CS_CALLCONV_STDCALL: + m_numRegistersUsed = NUM_ARGUMENT_REGISTERS; + m_curOfs = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *); + m_fUnmanagedCallConv = true; + break; + + case IMAGE_CEE_CS_CALLCONV_THISCALL: + case IMAGE_CEE_CS_CALLCONV_FASTCALL: + _ASSERTE_MSG(false, "Unsupported calling convention."); + + default: + m_fUnmanagedCallConv = false; + m_numRegistersUsed = numRegistersUsed; + m_curOfs = TransitionBlock::GetOffsetOfArgs() + SizeOfArgStack(); + } +#else + m_numRegistersUsed = numRegistersUsed; + m_curOfs = TransitionBlock::GetOffsetOfArgs() + SizeOfArgStack(); +#endif + +#elif defined(_TARGET_AMD64_) +#ifdef UNIX_AMD64_ABI + m_idxGenReg = numRegistersUsed; + m_idxStack = 0; + m_idxFPReg = 0; +#else + m_curOfs = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *); +#endif +#elif defined(_TARGET_ARM_) + m_idxGenReg = numRegistersUsed; + m_idxStack = 0; + + m_wFPRegs = 0; +#elif defined(_TARGET_ARM64_) + m_idxGenReg = numRegistersUsed; + m_idxStack = 0; + + m_idxFPReg = 0; +#else + PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset"); +#endif + + m_argNum = 0; + + m_dwFlags |= ITERATION_STARTED; + } + + if (m_argNum == this->NumFixedArgs()) + return TransitionBlock::InvalidOffset; + + TypeHandle thValueType; + CorElementType argType = this->GetNextArgumentType(m_argNum++, &thValueType); + + int argSize = MetaSig::GetElemSize(argType, thValueType); + + m_argType = argType; + m_argSize = argSize; + m_argTypeHandle = thValueType; + +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + m_hasArgLocDescForStructInRegs = false; +#endif + +#ifdef _TARGET_X86_ +#ifdef FEATURE_INTERPRETER + if (m_fUnmanagedCallConv) + { + int argOfs = m_curOfs; + m_curOfs += StackElemSize(argSize); + return argOfs; + } +#endif + if (IsArgumentInRegister(&m_numRegistersUsed, argType)) + { + return TransitionBlock::GetOffsetOfArgumentRegisters() + (NUM_ARGUMENT_REGISTERS - m_numRegistersUsed) * sizeof(void *); + } + + m_curOfs -= StackElemSize(argSize); + _ASSERTE(m_curOfs >= TransitionBlock::GetOffsetOfArgs()); + return m_curOfs; +#elif defined(_TARGET_AMD64_) +#ifdef UNIX_AMD64_ABI + + m_fArgInRegisters = true; + + int cFPRegs = 0; + int cGenRegs = 0; + int cbArg = StackElemSize(argSize); + + switch (argType) + { + + case ELEMENT_TYPE_R4: + // 32-bit floating point argument. + cFPRegs = 1; + break; + + case ELEMENT_TYPE_R8: + // 64-bit floating point argument. + cFPRegs = 1; + break; + + case ELEMENT_TYPE_VALUETYPE: + { +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + MethodTable *pMT = m_argTypeHandle.AsMethodTable(); + if (pMT->IsRegPassedStruct()) + { + EEClass* eeClass = pMT->GetClass(); + cGenRegs = 0; + for (int i = 0; i < eeClass->GetNumberEightBytes(); i++) + { + switch (eeClass->GetEightByteClassification(i)) + { + case SystemVClassificationTypeInteger: + case SystemVClassificationTypeIntegerReference: + case SystemVClassificationTypeIntegerByRef: + cGenRegs++; + break; + case SystemVClassificationTypeSSE: + cFPRegs++; + break; + default: + _ASSERTE(false); + break; + } + } + + // Check if we have enough registers available for the struct passing + if ((cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS) && (cGenRegs + m_idxGenReg) <= NUM_ARGUMENT_REGISTERS) + { + m_argLocDescForStructInRegs.Init(); + m_argLocDescForStructInRegs.m_cGenReg = cGenRegs; + m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs; + m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg; + m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; + m_argLocDescForStructInRegs.m_eeClass = eeClass; + + m_hasArgLocDescForStructInRegs = true; + + m_idxGenReg += cGenRegs; + m_idxFPReg += cFPRegs; + + return TransitionBlock::StructInRegsOffset; + } + } + + // Set the register counts to indicate that this argument will not be passed in registers + cFPRegs = 0; + cGenRegs = 0; + +#else // FEATURE_UNIX_AMD64_STRUCT_PASSING + argSize = sizeof(TADDR); +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING + + break; + } + + default: + cGenRegs = cbArg / 8; // GP reg size + break; + } + + if ((cFPRegs > 0) && (cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS)) + { + int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 16; + m_idxFPReg += cFPRegs; + return argOfs; + } + else if ((cGenRegs > 0) && (m_idxGenReg + cGenRegs <= NUM_ARGUMENT_REGISTERS)) + { + int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; + m_idxGenReg += cGenRegs; + return argOfs; + } + +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + m_fArgInRegisters = false; +#endif + + int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * STACK_ELEM_SIZE; + + int cArgSlots = cbArg / STACK_ELEM_SIZE; + m_idxStack += cArgSlots; + + return argOfs; +#else + // Each argument takes exactly one slot on AMD64 on Windows + int argOfs = m_curOfs; + m_curOfs += sizeof(void *); + return argOfs; +#endif +#elif defined(_TARGET_ARM_) + // First look at the underlying type of the argument to determine some basic properties: + // 1) The size of the argument in bytes (rounded up to the stack slot size of 4 if necessary). + // 2) Whether the argument represents a floating point primitive (ELEMENT_TYPE_R4 or ELEMENT_TYPE_R8). + // 3) Whether the argument requires 64-bit alignment (anything that contains a Int64/UInt64). + + bool fFloatingPoint = false; + bool fRequiresAlign64Bit = false; + + switch (argType) + { + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + // 64-bit integers require 64-bit alignment on ARM. + fRequiresAlign64Bit = true; + break; + + case ELEMENT_TYPE_R4: + // 32-bit floating point argument. + fFloatingPoint = true; + break; + + case ELEMENT_TYPE_R8: + // 64-bit floating point argument. + fFloatingPoint = true; + fRequiresAlign64Bit = true; + break; + + case ELEMENT_TYPE_VALUETYPE: + { + // Value type case: extract the alignment requirement, note that this has to handle + // the interop "native value types". + fRequiresAlign64Bit = thValueType.RequiresAlign8(); + +#ifdef FEATURE_HFA + // Handle HFAs: packed structures of 1-4 floats or doubles that are passed in FP argument + // registers if possible. + if (thValueType.IsHFA()) + { + fFloatingPoint = true; + } +#endif + + break; + } + + default: + // The default is are 4-byte arguments (or promoted to 4 bytes), non-FP and don't require any + // 64-bit alignment. + break; + } + + // Now attempt to place the argument into some combination of floating point or general registers and + // the stack. + + // Save the alignment requirement + m_fRequires64BitAlignment = fRequiresAlign64Bit; + + int cbArg = StackElemSize(argSize); + int cArgSlots = cbArg / 4; + + // Ignore floating point argument placement in registers if we're dealing with a vararg function (the ABI + // specifies this so that vararg processing on the callee side is simplified). +#ifndef ARM_SOFTFP + if (fFloatingPoint && !this->IsVarArg()) + { + // Handle floating point (primitive) arguments. + + // First determine whether we can place the argument in VFP registers. There are 16 32-bit + // and 8 64-bit argument registers that share the same register space (e.g. D0 overlaps S0 and + // S1). The ABI specifies that VFP values will be passed in the lowest sequence of registers that + // haven't been used yet and have the required alignment. So the sequence (float, double, float) + // would be mapped to (S0, D1, S1) or (S0, S2/S3, S1). + // + // We use a 16-bit bitmap to record which registers have been used so far. + // + // So we can use the same basic loop for each argument type (float, double or HFA struct) we set up + // the following input parameters based on the size and alignment requirements of the arguments: + // wAllocMask : bitmask of the number of 32-bit registers we need (1 for 1, 3 for 2, 7 for 3 etc.) + // cSteps : number of loop iterations it'll take to search the 16 registers + // cShift : how many bits to shift the allocation mask on each attempt + + WORD wAllocMask = (1 << (cbArg / 4)) - 1; + WORD cSteps = (WORD)(fRequiresAlign64Bit ? 9 - (cbArg / 8) : 17 - (cbArg / 4)); + WORD cShift = fRequiresAlign64Bit ? 2 : 1; + + // Look through the availability bitmask for a free register or register pair. + for (WORD i = 0; i < cSteps; i++) + { + if ((m_wFPRegs & wAllocMask) == 0) + { + // We found one, mark the register or registers as used. + m_wFPRegs |= wAllocMask; + + // Indicate the registers used to the caller and return. + return TransitionBlock::GetOffsetOfFloatArgumentRegisters() + (i * cShift * 4); + } + wAllocMask <<= cShift; + } + + // The FP argument is going to live on the stack. Once this happens the ABI demands we mark all FP + // registers as unavailable. + m_wFPRegs = 0xffff; + + // Doubles or HFAs containing doubles need the stack aligned appropriately. + if (fRequiresAlign64Bit) + m_idxStack = ALIGN_UP(m_idxStack, 2); + + // Indicate the stack location of the argument to the caller. + int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * 4; + + // Record the stack usage. + m_idxStack += cArgSlots; + + return argOfs; + } +#endif // ARM_SOFTFP + + // + // Handle the non-floating point case. + // + + if (m_idxGenReg < 4) + { + if (fRequiresAlign64Bit) + { + // The argument requires 64-bit alignment. Align either the next general argument register if + // we have any left. See step C.3 in the algorithm in the ABI spec. + m_idxGenReg = ALIGN_UP(m_idxGenReg, 2); + } + + int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 4; + + int cRemainingRegs = 4 - m_idxGenReg; + if (cArgSlots <= cRemainingRegs) + { + // Mark the registers just allocated as used. + m_idxGenReg += cArgSlots; + return argOfs; + } + + // The ABI supports splitting a non-FP argument across registers and the stack. But this is + // disabled if the FP arguments already overflowed onto the stack (i.e. the stack index is not + // zero). The following code marks the general argument registers as exhausted if this condition + // holds. See steps C.5 in the algorithm in the ABI spec. + + m_idxGenReg = 4; + + if (m_idxStack == 0) + { + m_idxStack += cArgSlots - cRemainingRegs; + return argOfs; + } + } + + if (fRequiresAlign64Bit) + { + // The argument requires 64-bit alignment. If it is going to be passed on the stack, align + // the next stack slot. See step C.6 in the algorithm in the ABI spec. + m_idxStack = ALIGN_UP(m_idxStack, 2); + } + + int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * 4; + + // Advance the stack pointer over the argument just placed. + m_idxStack += cArgSlots; + + return argOfs; +#elif defined(_TARGET_ARM64_) + + int cFPRegs = 0; + + switch (argType) + { + + case ELEMENT_TYPE_R4: + // 32-bit floating point argument. + cFPRegs = 1; + break; + + case ELEMENT_TYPE_R8: + // 64-bit floating point argument. + cFPRegs = 1; + break; + + case ELEMENT_TYPE_VALUETYPE: + { + // Handle HFAs: packed structures of 2-4 floats or doubles that are passed in FP argument + // registers if possible. + if (thValueType.IsHFA()) + { + CorElementType type = thValueType.GetHFAType(); + bool isFloatType = (type == ELEMENT_TYPE_R4); + + cFPRegs = (type == ELEMENT_TYPE_R4)? (argSize/sizeof(float)): (argSize/sizeof(double)); + + m_argLocDescForStructInRegs.Init(); + m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs; + m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; + + m_argLocDescForStructInRegs.m_isSinglePrecision = isFloatType; + + m_hasArgLocDescForStructInRegs = true; + } + else + { + // Composite greater than 16bytes should be passed by reference + if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) + { + argSize = sizeof(TADDR); + } + } + + break; + } + + default: + break; + } + + int cbArg = StackElemSize(argSize); + int cArgSlots = cbArg / STACK_ELEM_SIZE; + + if (cFPRegs>0 && !this->IsVarArg()) + { + if (cFPRegs + m_idxFPReg <= 8) + { + int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; + m_idxFPReg += cFPRegs; + return argOfs; + } + else + { + m_idxFPReg = 8; + } + } + else + { + if (m_idxGenReg + cArgSlots <= 8) + { + int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; + m_idxGenReg += cArgSlots; + return argOfs; + } + else + { + m_idxGenReg = 8; + } + } + + int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * 8; + m_idxStack += cArgSlots; + return argOfs; +#else + PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset"); + return TransitionBlock::InvalidOffset; +#endif +} + +template +void ArgIteratorTemplate::ComputeReturnFlags() +{ + CONTRACTL + { + INSTANCE_CHECK; + if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; + if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS; + if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); } + MODE_ANY; + } + CONTRACTL_END + + TypeHandle thValueType; + CorElementType type = this->GetReturnType(&thValueType); + + DWORD flags = RETURN_FLAGS_COMPUTED; + switch (type) + { + case ELEMENT_TYPE_TYPEDBYREF: +#ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE + if (sizeof(TypedByRef) > ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) + flags |= RETURN_HAS_RET_BUFFER; +#else + flags |= RETURN_HAS_RET_BUFFER; +#endif + break; + + case ELEMENT_TYPE_R4: +#ifndef ARM_SOFTFP + flags |= sizeof(float) << RETURN_FP_SIZE_SHIFT; +#endif + break; + + case ELEMENT_TYPE_R8: +#ifndef ARM_SOFTFP + flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT; +#endif + break; + + case ELEMENT_TYPE_VALUETYPE: +#ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE + { + _ASSERTE(!thValueType.IsNull()); + +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + MethodTable *pMT = thValueType.AsMethodTable(); + if (pMT->IsRegPassedStruct()) + { + EEClass* eeClass = pMT->GetClass(); + + if (eeClass->GetNumberEightBytes() == 1) + { + // Structs occupying just one eightbyte are treated as int / double + if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE) + { + flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT; + } + } + else + { + // Size of the struct is 16 bytes + flags |= (16 << RETURN_FP_SIZE_SHIFT); + // The lowest two bits of the size encode the order of the int and SSE fields + if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE) + { + flags |= (1 << RETURN_FP_SIZE_SHIFT); + } + + if (eeClass->GetEightByteClassification(1) == SystemVClassificationTypeSSE) + { + flags |= (2 << RETURN_FP_SIZE_SHIFT); + } + } + + break; + } +#else // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING + +#ifdef FEATURE_HFA + if (thValueType.IsHFA() && !this->IsVarArg()) + { + CorElementType hfaType = thValueType.GetHFAType(); + + flags |= (hfaType == ELEMENT_TYPE_R4) ? + ((4 * sizeof(float)) << RETURN_FP_SIZE_SHIFT) : + ((4 * sizeof(double)) << RETURN_FP_SIZE_SHIFT); + + break; + } +#endif + + size_t size = thValueType.GetSize(); + +#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) + // Return value types of size which are not powers of 2 using a RetBuffArg + if ((size & (size-1)) != 0) + { + flags |= RETURN_HAS_RET_BUFFER; + break; + } +#endif + + if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) + break; +#endif // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING + } +#endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE + + // Value types are returned using return buffer by default + flags |= RETURN_HAS_RET_BUFFER; + break; + + default: + break; + } + + m_dwFlags |= flags; +} + +template +void ArgIteratorTemplate::ForceSigWalk() +{ + CONTRACTL + { + INSTANCE_CHECK; + if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; + if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS; + if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); } + MODE_ANY; + } + CONTRACTL_END + + // This can be only used before the actual argument iteration started + _ASSERTE((m_dwFlags & ITERATION_STARTED) == 0); + +#ifdef _TARGET_X86_ + // + // x86 is special as always + // + + int numRegistersUsed = 0; + int nSizeOfArgStack = 0; + + if (this->HasThis()) + numRegistersUsed++; + + if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) + numRegistersUsed++; + + if (this->IsVarArg()) + { + nSizeOfArgStack += sizeof(void *); + numRegistersUsed = NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs + } + +#ifdef FEATURE_INTERPRETER + BYTE callconv = CallConv(); + switch (callconv) + { + case IMAGE_CEE_CS_CALLCONV_C: + case IMAGE_CEE_CS_CALLCONV_STDCALL: + numRegistersUsed = NUM_ARGUMENT_REGISTERS; + nSizeOfArgStack = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *); + break; + + case IMAGE_CEE_CS_CALLCONV_THISCALL: + case IMAGE_CEE_CS_CALLCONV_FASTCALL: + _ASSERTE_MSG(false, "Unsupported calling convention."); + default: + } +#endif // FEATURE_INTERPRETER + + DWORD nArgs = this->NumFixedArgs(); + for (DWORD i = 0; i < nArgs; i++) + { + TypeHandle thValueType; + CorElementType type = this->GetNextArgumentType(i, &thValueType); + + if (!IsArgumentInRegister(&numRegistersUsed, type)) + { + int structSize = MetaSig::GetElemSize(type, thValueType); + + nSizeOfArgStack += StackElemSize(structSize); + +#ifndef DACCESS_COMPILE + if (nSizeOfArgStack > MAX_ARG_SIZE) + { +#ifdef _DEBUG + // We should not ever throw exception in the "FORBIDGC_LOADER_USE_ENABLED" mode. + // The contract violation is required to workaround bug in the static contract analyzer. + _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED()); + CONTRACT_VIOLATION(ThrowsViolation); +#endif + COMPlusThrow(kNotSupportedException); + } +#endif + } + } + + if (this->HasParamType()) + { + DWORD paramTypeFlags = 0; + if (numRegistersUsed < NUM_ARGUMENT_REGISTERS) + { + numRegistersUsed++; + paramTypeFlags = (numRegistersUsed == 1) ? + PARAM_TYPE_REGISTER_ECX : PARAM_TYPE_REGISTER_EDX; + } + else + { + nSizeOfArgStack += sizeof(void *); + paramTypeFlags = PARAM_TYPE_REGISTER_STACK; + } + m_dwFlags |= paramTypeFlags; + } + +#else // _TARGET_X86_ + + int maxOffset = TransitionBlock::GetOffsetOfArgs(); + + int ofs; + while (TransitionBlock::InvalidOffset != (ofs = GetNextOffset())) + { + int stackElemSize; + +#ifdef _TARGET_AMD64_ +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + if (m_fArgInRegisters) + { + // Arguments passed in registers don't consume any stack + continue; + } + + stackElemSize = StackElemSize(GetArgSize()); +#else // FEATURE_UNIX_AMD64_STRUCT_PASSING + // All stack arguments take just one stack slot on AMD64 because of arguments bigger + // than a stack slot are passed by reference. + stackElemSize = STACK_ELEM_SIZE; +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING +#else // _TARGET_AMD64_ + stackElemSize = StackElemSize(GetArgSize()); +#if defined(ENREGISTERED_PARAMTYPE_MAXSIZE) + if (IsArgPassedByRef()) + stackElemSize = STACK_ELEM_SIZE; +#endif +#endif // _TARGET_AMD64_ + + int endOfs = ofs + stackElemSize; + if (endOfs > maxOffset) + { +#if !defined(DACCESS_COMPILE) + if (endOfs > MAX_ARG_SIZE) + { +#ifdef _DEBUG + // We should not ever throw exception in the "FORBIDGC_LOADER_USE_ENABLED" mode. + // The contract violation is required to workaround bug in the static contract analyzer. + _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED()); + CONTRACT_VIOLATION(ThrowsViolation); +#endif + COMPlusThrow(kNotSupportedException); + } +#endif + maxOffset = endOfs; + } + } + // Clear the iterator started flag + m_dwFlags &= ~ITERATION_STARTED; + + int nSizeOfArgStack = maxOffset - TransitionBlock::GetOffsetOfArgs(); + +#if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI) + nSizeOfArgStack = (nSizeOfArgStack > (int)sizeof(ArgumentRegisters)) ? + (nSizeOfArgStack - sizeof(ArgumentRegisters)) : 0; +#endif + +#endif // _TARGET_X86_ + + // Cache the result + m_nSizeOfArgStack = nSizeOfArgStack; + m_dwFlags |= SIZE_OF_ARG_STACK_COMPUTED; + + this->Reset(); +} + +class ArgIteratorBase +{ +protected: + MetaSig * m_pSig; + + FORCEINLINE CorElementType GetReturnType(TypeHandle * pthValueType) + { + WRAPPER_NO_CONTRACT; +#ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE + return m_pSig->GetReturnTypeNormalized(pthValueType); +#else + return m_pSig->GetReturnTypeNormalized(); +#endif + } + + FORCEINLINE CorElementType GetNextArgumentType(DWORD iArg, TypeHandle * pthValueType) + { + WRAPPER_NO_CONTRACT; + _ASSERTE(iArg == m_pSig->GetArgNum()); + CorElementType et = m_pSig->PeekArgNormalized(pthValueType); + m_pSig->SkipArg(); + return et; + } + + FORCEINLINE void Reset() + { + WRAPPER_NO_CONTRACT; + m_pSig->Reset(); + } + +public: + BOOL HasThis() + { + LIMITED_METHOD_CONTRACT; + return m_pSig->HasThis(); + } + + BOOL HasParamType() + { + LIMITED_METHOD_CONTRACT; + return m_pSig->GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE; + } + + BOOL IsVarArg() + { + LIMITED_METHOD_CONTRACT; + return m_pSig->IsVarArg() || m_pSig->IsTreatAsVarArg(); + } + + DWORD NumFixedArgs() + { + LIMITED_METHOD_CONTRACT; + return m_pSig->NumFixedArgs(); + } + +#ifdef FEATURE_INTERPRETER + BYTE CallConv() + { + return m_pSig->GetCallingConvention(); + } +#endif // FEATURE_INTERPRETER + + // + // The following is used by the profiler to dig into the iterator for + // discovering if the method has a This pointer or a return buffer. + // Do not use this to re-initialize the signature, use the exposed Init() + // method in this class. + // + MetaSig *GetSig(void) + { + return m_pSig; + } +}; + +class ArgIterator : public ArgIteratorTemplate +{ +public: + ArgIterator(MetaSig * pSig) + { + m_pSig = pSig; + } + + // This API returns true if we are returning a structure in registers instead of using a byref return buffer + BOOL HasNonStandardByvalReturn() + { + WRAPPER_NO_CONTRACT; + +#ifdef ENREGISTERED_RETURNTYPE_MAXSIZE + CorElementType type = m_pSig->GetReturnTypeNormalized(); + return (type == ELEMENT_TYPE_VALUETYPE || type == ELEMENT_TYPE_TYPEDBYREF) && !HasRetBuffArg(); +#else + return FALSE; +#endif + } +}; + +// Conventience helper +inline BOOL HasRetBuffArg(MetaSig * pSig) +{ + WRAPPER_NO_CONTRACT; + ArgIterator argit(pSig); + return argit.HasRetBuffArg(); +} + +#ifdef UNIX_X86_ABI +// For UNIX_X86_ABI and unmanaged function, we always need RetBuf if the return type is VALUETYPE +inline BOOL HasRetBuffArgUnmanagedFixup(MetaSig * pSig) +{ + WRAPPER_NO_CONTRACT; + // We cannot just pSig->GetReturnType() here since it will return ELEMENT_TYPE_VALUETYPE for enums + CorElementType type = pSig->GetRetTypeHandleThrowing().GetVerifierCorElementType(); + return type == ELEMENT_TYPE_VALUETYPE; +} +#endif + +inline BOOL IsRetBuffPassedAsFirstArg() +{ + WRAPPER_NO_CONTRACT; +#ifndef _TARGET_ARM64_ + return TRUE; +#else + return FALSE; +#endif +} + +#endif // __CALLING_CONVENTION_INCLUDED diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/amd64/CallDescrWorkerAMD64.asm b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/amd64/CallDescrWorkerAMD64.asm new file mode 100644 index 00000000000000..a72cae0f7de767 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/amd64/CallDescrWorkerAMD64.asm @@ -0,0 +1,131 @@ +; Licensed to the .NET Foundation under one or more agreements. +; The .NET Foundation licenses this file to you under the MIT license. + +include +include + +extern CallDescrWorkerUnwindFrameChainHandler:proc + +;; +;; EXTERN_C void FastCallFinalizeWorker(Object *obj, PCODE funcPtr); +;; + NESTED_ENTRY FastCallFinalizeWorker, _TEXT, CallDescrWorkerUnwindFrameChainHandler + alloc_stack 28h ;; alloc callee scratch and align the stack + END_PROLOGUE + + ; + ; RCX: already contains obj* + ; RDX: address of finalizer method to call + ; + + ; !!!!!!!!! + ; NOTE: you cannot tail call here because we must have the CallDescrWorkerUnwindFrameChainHandler + ; personality routine on the stack. + ; !!!!!!!!! + call rdx + xor rax, rax + + ; epilog + add rsp, 28h + ret + + + NESTED_END FastCallFinalizeWorker, _TEXT + +;;extern "C" void CallDescrWorkerInternal(CallDescrData * pCallDescrData); + + NESTED_ENTRY CallDescrWorkerInternal, _TEXT, CallDescrWorkerUnwindFrameChainHandler + + push_nonvol_reg rbx ; save nonvolatile registers + push_nonvol_reg rsi ; + push_nonvol_reg rbp ; + set_frame rbp, 0 ; set frame pointer + + END_PROLOGUE + + mov rbx, rcx ; save pCallDescrData in rbx + + mov ecx, dword ptr [rbx + CallDescrData__numStackSlots] + + test ecx, 1 + jz StackAligned + push rax +StackAligned: + + mov rsi, [rbx + CallDescrData__pSrc] ; set source argument list address + lea rsi, [rsi + 8 * rcx] + +StackCopyLoop: ; copy the arguments to stack top-down to carefully probe for sufficient stack space + sub rsi, 8 + push qword ptr [rsi] + dec ecx + jnz StackCopyLoop + + ; + ; N.B. All four argument registers are loaded regardless of the actual number + ; of arguments. + ; + + mov rax, [rbx + CallDescrData__dwRegTypeMap] ; save the reg (arg) type map + + mov rcx, 0[rsp] ; load first four argument registers + movss xmm0, real4 ptr 0[rsp] ; + cmp al, ASM_ELEMENT_TYPE_R8 ; + jnz Arg2 ; + movsd xmm0, real8 ptr 0[rsp] ; +Arg2: + mov rdx, 8[rsp] ; + movss xmm1, real4 ptr 8[rsp] ; + cmp ah, ASM_ELEMENT_TYPE_R8 ; + jnz Arg3 ; + movsd xmm1, real8 ptr 8[rsp] ; +Arg3: + mov r8, 10h[rsp] ; + movss xmm2, real4 ptr 10h[rsp]; + shr eax, 16 ; + cmp al, ASM_ELEMENT_TYPE_R8 ; + jnz Arg4 ; + movsd xmm2, real8 ptr 10h[rsp]; +Arg4: + mov r9, 18h[rsp] ; + movss xmm3, real4 ptr 18h[rsp]; + cmp ah, ASM_ELEMENT_TYPE_R8 ; + jnz DoCall ; + movsd xmm3, real8 ptr 18h[rsp]; +DoCall: + call qword ptr [rbx+CallDescrData__pTarget] ; call target function + + ; Save FP return value + + mov ecx, dword ptr [rbx+CallDescrData__fpReturnSize] + test ecx, ecx + jz ReturnsInt + + cmp ecx, 4 + je ReturnsFloat + cmp ecx, 8 + je ReturnsDouble + ; unexpected + jmp Epilog + +ReturnsInt: + mov [rbx+CallDescrData__returnValue], rax + +Epilog: + lea rsp, 0[rbp] ; deallocate argument list + pop rbp ; restore nonvolatile register + pop rsi ; + pop rbx ; + ret + +ReturnsFloat: + movss real4 ptr [rbx+CallDescrData__returnValue], xmm0 + jmp Epilog + +ReturnsDouble: + movsd real8 ptr [rbx+CallDescrData__returnValue], xmm0 + jmp Epilog + + NESTED_END CallDescrWorkerInternal, _TEXT + + end diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/amd64/asmconstants.h b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/amd64/asmconstants.h new file mode 100644 index 00000000000000..8ab2328d9e9e54 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/amd64/asmconstants.h @@ -0,0 +1,741 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// See makefile.inc. During the build, this file is converted into a .inc +// file for inclusion by .asm files. The #defines are converted into EQU's. +// +// Allow multiple inclusion. + + +#ifndef _TARGET_AMD64_ +#error this file should only be used on an AMD64 platform +#endif // _TARGET_AMD64_ + +#include "../../inc/switches.h" + +#ifndef ASMCONSTANTS_C_ASSERT +#define ASMCONSTANTS_C_ASSERT(cond) +#endif + +#ifndef ASMCONSTANTS_RUNTIME_ASSERT +#define ASMCONSTANTS_RUNTIME_ASSERT(cond) +#endif + + +// Some contants are different in _DEBUG builds. This macro factors out +// ifdefs from below. +#ifdef _DEBUG +#define DBG_FRE(dbg,fre) dbg +#else +#define DBG_FRE(dbg,fre) fre +#endif + +#define DynamicHelperFrameFlags_Default 0 +#define DynamicHelperFrameFlags_ObjectArg 1 +#define DynamicHelperFrameFlags_ObjectArg2 2 + +#define ASMCONSTANT_OFFSETOF_ASSERT(struct, member) \ +ASMCONSTANTS_C_ASSERT(OFFSETOF__##struct##__##member == offsetof(struct, member)); + +#define ASMCONSTANT_SIZEOF_ASSERT(classname) \ +ASMCONSTANTS_C_ASSERT(SIZEOF__##classname == sizeof(classname)); + +#define ASM_ELEMENT_TYPE_R4 0xC +ASMCONSTANTS_C_ASSERT(ASM_ELEMENT_TYPE_R4 == ELEMENT_TYPE_R4); + +#define ASM_ELEMENT_TYPE_R8 0xD +ASMCONSTANTS_C_ASSERT(ASM_ELEMENT_TYPE_R8 == ELEMENT_TYPE_R8); + +#ifdef FEATURE_INCLUDE_ALL_INTERFACES +#define ASM_CLRTASKHOSTED 0x2 +ASMCONSTANTS_C_ASSERT(ASM_CLRTASKHOSTED == CLRTASKHOSTED); +#endif + +#define METHODDESC_REGNUM 10 +#define METHODDESC_REGISTER r10 + +#define PINVOKE_CALLI_TARGET_REGNUM 10 +#define PINVOKE_CALLI_TARGET_REGISTER r10 + +#define PINVOKE_CALLI_SIGTOKEN_REGNUM 11 +#define PINVOKE_CALLI_SIGTOKEN_REGISTER r11 + +// rcx, rdx, r8, r9 +#define SIZEOF_MAX_OUTGOING_ARGUMENT_HOMES 0x20 + +// xmm0...xmm3 +#define SIZEOF_MAX_FP_ARG_SPILL 0x40 + +#ifndef UNIX_AMD64_ABI +#define SIZEOF_CalleeSavedRegisters 0x40 +ASMCONSTANTS_C_ASSERT(SIZEOF_CalleeSavedRegisters == sizeof(CalleeSavedRegisters)); +#else +#define SIZEOF_CalleeSavedRegisters 0x30 +ASMCONSTANTS_C_ASSERT(SIZEOF_CalleeSavedRegisters == sizeof(CalleeSavedRegisters)); +#endif + +#define SIZEOF_GSCookie 0x8 +ASMCONSTANTS_C_ASSERT(SIZEOF_GSCookie == sizeof(GSCookie)); + +#define OFFSETOF__Frame____VFN_table 0 + +#define OFFSETOF__Frame__m_Next 0x8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__Frame__m_Next + == offsetof(Frame, m_Next)); + +#define SIZEOF__Frame 0x10 + +#ifdef FEATURE_COMINTEROP +#define SIZEOF__ComPrestubMethodFrame 0x20 +ASMCONSTANTS_C_ASSERT(SIZEOF__ComPrestubMethodFrame + == sizeof(ComPrestubMethodFrame)); + +#define SIZEOF__ComMethodFrame 0x20 +ASMCONSTANTS_C_ASSERT(SIZEOF__ComMethodFrame + == sizeof(ComMethodFrame)); +#endif // FEATURE_COMINTEROP + +#define OFFSETOF__UMEntryThunk__m_pUMThunkMarshInfo 0x18 +ASMCONSTANTS_C_ASSERT(OFFSETOF__UMEntryThunk__m_pUMThunkMarshInfo + == offsetof(UMEntryThunk, m_pUMThunkMarshInfo)); + +#define OFFSETOF__UMEntryThunk__m_dwDomainId 0x20 +ASMCONSTANTS_C_ASSERT(OFFSETOF__UMEntryThunk__m_dwDomainId + == offsetof(UMEntryThunk, m_dwDomainId)); + +#define OFFSETOF__UMThunkMarshInfo__m_pILStub 0x00 +ASMCONSTANTS_C_ASSERT(OFFSETOF__UMThunkMarshInfo__m_pILStub + == offsetof(UMThunkMarshInfo, m_pILStub)); + +#define OFFSETOF__UMThunkMarshInfo__m_cbActualArgSize 0x08 +ASMCONSTANTS_C_ASSERT(OFFSETOF__UMThunkMarshInfo__m_cbActualArgSize + == offsetof(UMThunkMarshInfo, m_cbActualArgSize)); + +#ifdef FEATURE_COMINTEROP + +#define OFFSETOF__ComPlusCallMethodDesc__m_pComPlusCallInfo DBG_FRE(0x30, 0x08) +ASMCONSTANTS_C_ASSERT(OFFSETOF__ComPlusCallMethodDesc__m_pComPlusCallInfo + == offsetof(ComPlusCallMethodDesc, m_pComPlusCallInfo)); + +#define OFFSETOF__ComPlusCallInfo__m_pILStub 0x0 +ASMCONSTANTS_C_ASSERT(OFFSETOF__ComPlusCallInfo__m_pILStub + == offsetof(ComPlusCallInfo, m_pILStub)); + +#endif // FEATURE_COMINTEROP + +#define OFFSETOF__Thread__m_fPreemptiveGCDisabled 0x0C +#ifndef CROSSGEN_COMPILE +ASMCONSTANTS_C_ASSERT(OFFSETOF__Thread__m_fPreemptiveGCDisabled + == offsetof(Thread, m_fPreemptiveGCDisabled)); +#endif +#define Thread_m_fPreemptiveGCDisabled OFFSETOF__Thread__m_fPreemptiveGCDisabled + +#define OFFSETOF__Thread__m_pFrame 0x10 +#ifndef CROSSGEN_COMPILE +ASMCONSTANTS_C_ASSERT(OFFSETOF__Thread__m_pFrame + == offsetof(Thread, m_pFrame)); +#endif +#define Thread_m_pFrame OFFSETOF__Thread__m_pFrame + +#ifndef CROSSGEN_COMPILE +#define OFFSETOF__Thread__m_State 0x8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__Thread__m_State + == offsetof(Thread, m_State)); + +#define OFFSETOF__Thread__m_pDomain 0x20 +ASMCONSTANTS_C_ASSERT(OFFSETOF__Thread__m_pDomain + == offsetof(Thread, m_pDomain)); + +#define OFFSETOF__Thread__m_dwLockCount 0x28 +ASMCONSTANTS_C_ASSERT(OFFSETOF__Thread__m_dwLockCount + == offsetof(Thread, m_dwLockCount)); + +#define OFFSETOF__Thread__m_ThreadId 0x2C +ASMCONSTANTS_C_ASSERT(OFFSETOF__Thread__m_ThreadId + == offsetof(Thread, m_ThreadId)); + +#define OFFSET__Thread__m_alloc_context__alloc_ptr 0x68 +ASMCONSTANTS_C_ASSERT(OFFSET__Thread__m_alloc_context__alloc_ptr == offsetof(Thread, m_alloc_context) + offsetof(alloc_context, alloc_ptr)); + +#define OFFSET__Thread__m_alloc_context__alloc_limit 0x70 +ASMCONSTANTS_C_ASSERT(OFFSET__Thread__m_alloc_context__alloc_limit == offsetof(Thread, m_alloc_context) + offsetof(alloc_context, alloc_limit)); + +#define OFFSETOF__ThreadExceptionState__m_pCurrentTracker 0x000 +ASMCONSTANTS_C_ASSERT(OFFSETOF__ThreadExceptionState__m_pCurrentTracker + == offsetof(ThreadExceptionState, m_pCurrentTracker)); + +#define THREAD_CATCHATSAFEPOINT_BITS 0x5F +ASMCONSTANTS_C_ASSERT(THREAD_CATCHATSAFEPOINT_BITS == Thread::TS_CatchAtSafePoint); +#endif // CROSSGEN_COMPILE + + +#ifdef FEATURE_REMOTING +#define TransparentProxyObject___stubData 0x10 +ASMCONSTANTS_C_ASSERT(TransparentProxyObject___stubData == offsetof(TransparentProxyObject, _stubData)) + +#define TransparentProxyObject___stub 0x28 +ASMCONSTANTS_C_ASSERT(TransparentProxyObject___stub == offsetof(TransparentProxyObject, _stub)) + +#define TransparentProxyObject___pMT 0x18 +ASMCONSTANTS_C_ASSERT(TransparentProxyObject___pMT == offsetof(TransparentProxyObject, _pMT)) +#endif // FEATURE_REMOTING + +#define OFFSETOF__NDirectMethodDesc__m_pWriteableData DBG_FRE(0x48, 0x20) +ASMCONSTANTS_C_ASSERT(OFFSETOF__NDirectMethodDesc__m_pWriteableData == offsetof(NDirectMethodDesc, ndirect.m_pWriteableData)); + +#define OFFSETOF__ObjHeader__SyncBlkIndex 0x4 +ASMCONSTANTS_C_ASSERT(OFFSETOF__ObjHeader__SyncBlkIndex + == (sizeof(ObjHeader) - offsetof(ObjHeader, m_SyncBlockValue))); + +#define SIZEOF__SyncTableEntry 0x10 +ASMCONSTANT_SIZEOF_ASSERT(SyncTableEntry); + +#define OFFSETOF__SyncTableEntry__m_SyncBlock 0x0 +ASMCONSTANT_OFFSETOF_ASSERT(SyncTableEntry, m_SyncBlock); + +#define OFFSETOF__SyncBlock__m_Monitor 0x0 +ASMCONSTANT_OFFSETOF_ASSERT(SyncBlock, m_Monitor); + +#define OFFSETOF__DelegateObject___methodPtr 0x18 +ASMCONSTANT_OFFSETOF_ASSERT(DelegateObject, _methodPtr); + +#define OFFSETOF__DelegateObject___target 0x08 +ASMCONSTANT_OFFSETOF_ASSERT(DelegateObject, _target); + +#define OFFSETOF__AwareLock__m_MonitorHeld 0x0 +ASMCONSTANTS_C_ASSERT(OFFSETOF__AwareLock__m_MonitorHeld + == offsetof(AwareLock, m_MonitorHeld)); + +#define OFFSETOF__AwareLock__m_Recursion 0x4 +ASMCONSTANTS_C_ASSERT(OFFSETOF__AwareLock__m_Recursion + == offsetof(AwareLock, m_Recursion)); + +#define OFFSETOF__AwareLock__m_HoldingThread 0x8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__AwareLock__m_HoldingThread + == offsetof(AwareLock, m_HoldingThread)); + +#define OFFSETOF__g_SystemInfo__dwNumberOfProcessors 0x20 +ASMCONSTANTS_C_ASSERT(OFFSETOF__g_SystemInfo__dwNumberOfProcessors + == offsetof(SYSTEM_INFO, dwNumberOfProcessors)); + +#define OFFSETOF__g_SpinConstants__dwInitialDuration 0x0 +ASMCONSTANTS_C_ASSERT(OFFSETOF__g_SpinConstants__dwInitialDuration + == offsetof(SpinConstants, dwInitialDuration)); + +#define OFFSETOF__g_SpinConstants__dwMaximumDuration 0x4 +ASMCONSTANTS_C_ASSERT(OFFSETOF__g_SpinConstants__dwMaximumDuration + == offsetof(SpinConstants, dwMaximumDuration)); + +#define OFFSETOF__g_SpinConstants__dwBackoffFactor 0x8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__g_SpinConstants__dwBackoffFactor + == offsetof(SpinConstants, dwBackoffFactor)); + +#define OFFSETOF__MethodTable__m_dwFlags 0x00 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTable__m_dwFlags + == offsetof(MethodTable, m_dwFlags)); + +#define OFFSET__MethodTable__m_BaseSize 0x04 +ASMCONSTANTS_C_ASSERT(OFFSET__MethodTable__m_BaseSize + == offsetof(MethodTable, m_BaseSize)); + +#define OFFSETOF__MethodTable__m_wNumInterfaces 0x0E +ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTable__m_wNumInterfaces + == offsetof(MethodTable, m_wNumInterfaces)); + +#define OFFSETOF__MethodTable__m_pParentMethodTable DBG_FRE(0x18, 0x10) +ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTable__m_pParentMethodTable + == offsetof(MethodTable, m_pParentMethodTable)); + +#define OFFSETOF__MethodTable__m_pWriteableData DBG_FRE(0x28, 0x20) +ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTable__m_pWriteableData + == offsetof(MethodTable, m_pWriteableData)); + +#define OFFSETOF__MethodTable__m_pEEClass DBG_FRE(0x30, 0x28) +ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTable__m_pEEClass + == offsetof(MethodTable, m_pEEClass)); + +#define METHODTABLE_OFFSET_VTABLE DBG_FRE(0x48, 0x40) +ASMCONSTANTS_C_ASSERT(METHODTABLE_OFFSET_VTABLE == sizeof(MethodTable)); + +#define OFFSETOF__MethodTable__m_ElementType DBG_FRE(0x38, 0x30) +ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTable__m_ElementType + == offsetof(MethodTable, m_pMultipurposeSlot1)); + +#define OFFSETOF__MethodTable__m_pInterfaceMap DBG_FRE(0x40, 0x38) +ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTable__m_pInterfaceMap + == offsetof(MethodTable, m_pMultipurposeSlot2)); + + +#define MethodTable_VtableSlotsPerChunk 8 +ASMCONSTANTS_C_ASSERT(MethodTable_VtableSlotsPerChunk == VTABLE_SLOTS_PER_CHUNK) + +#define MethodTable_VtableSlotsPerChunkLog2 3 +ASMCONSTANTS_C_ASSERT(MethodTable_VtableSlotsPerChunkLog2 == VTABLE_SLOTS_PER_CHUNK_LOG2) + +#if defined(FEATURE_TYPEEQUIVALENCE) || defined(FEATURE_REMOTING) +#define METHODTABLE_EQUIVALENCE_FLAGS 0x02000000 +ASMCONSTANTS_C_ASSERT(METHODTABLE_EQUIVALENCE_FLAGS + == MethodTable::enum_flag_HasTypeEquivalence); +#else +#define METHODTABLE_EQUIVALENCE_FLAGS 0x0 +#endif + +#ifdef FEATURE_COMINTEROP +#define METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS 0x40080000 +ASMCONSTANTS_C_ASSERT(METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS + == MethodTable::enum_flag_NonTrivialInterfaceCast); +#else +#define METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS 0x00080000 +ASMCONSTANTS_C_ASSERT(METHODTABLE_NONTRIVIALINTERFACECAST_FLAGS + == MethodTable::enum_flag_NonTrivialInterfaceCast); +#endif + +#define MethodTable__enum_flag_ContainsPointers 0x01000000 +ASMCONSTANTS_C_ASSERT(MethodTable__enum_flag_ContainsPointers + == MethodTable::enum_flag_ContainsPointers); + +#define OFFSETOF__MethodTableWriteableData__m_dwFlags 0 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodTableWriteableData__m_dwFlags + == offsetof(MethodTableWriteableData, m_dwFlags)); + +#define MethodTableWriteableData__enum_flag_Unrestored 0x04 +ASMCONSTANTS_C_ASSERT(MethodTableWriteableData__enum_flag_Unrestored + == MethodTableWriteableData::enum_flag_Unrestored); + +#define OFFSETOF__InterfaceInfo_t__m_pMethodTable 0 +ASMCONSTANTS_C_ASSERT(OFFSETOF__InterfaceInfo_t__m_pMethodTable + == offsetof(InterfaceInfo_t, m_pMethodTable)); + +#define SIZEOF__InterfaceInfo_t 0x8 +ASMCONSTANTS_C_ASSERT(SIZEOF__InterfaceInfo_t + == sizeof(InterfaceInfo_t)); + +#define OFFSETOF__AppDomain__m_dwId 0x8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__AppDomain__m_dwId + == offsetof(AppDomain, m_dwId)); + +#define OFFSETOF__AppDomain__m_sDomainLocalBlock DBG_FRE(0x10, 0x10) +ASMCONSTANTS_C_ASSERT(OFFSETOF__AppDomain__m_sDomainLocalBlock + == offsetof(AppDomain, m_sDomainLocalBlock)); + +#define OFFSETOF__DomainLocalBlock__m_pModuleSlots 0x8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__DomainLocalBlock__m_pModuleSlots + == offsetof(DomainLocalBlock, m_pModuleSlots)); + +#define OFFSETOF__DomainLocalModule__m_pDataBlob 0x030 +ASMCONSTANTS_C_ASSERT(OFFSETOF__DomainLocalModule__m_pDataBlob + == offsetof(DomainLocalModule, m_pDataBlob)); + +// If this changes then we can't just test one bit in the assembly code. +ASMCONSTANTS_C_ASSERT(ClassInitFlags::INITIALIZED_FLAG == 1); + +// End for JIT_GetSharedNonGCStaticBaseWorker + +// For JIT_GetSharedGCStaticBaseWorker + +#define OFFSETOF__DomainLocalModule__m_pGCStatics 0x020 +ASMCONSTANTS_C_ASSERT(OFFSETOF__DomainLocalModule__m_pGCStatics + == offsetof(DomainLocalModule, m_pGCStatics)); + +// End for JIT_GetSharedGCStaticBaseWorker + +#define CORINFO_NullReferenceException_ASM 0 +ASMCONSTANTS_C_ASSERT( CORINFO_NullReferenceException_ASM + == CORINFO_NullReferenceException); + +#define CORINFO_InvalidCastException_ASM 2 +ASMCONSTANTS_C_ASSERT( CORINFO_InvalidCastException_ASM + == CORINFO_InvalidCastException); + +#define CORINFO_IndexOutOfRangeException_ASM 3 +ASMCONSTANTS_C_ASSERT( CORINFO_IndexOutOfRangeException_ASM + == CORINFO_IndexOutOfRangeException); + +#define CORINFO_SynchronizationLockException_ASM 5 +ASMCONSTANTS_C_ASSERT( CORINFO_SynchronizationLockException_ASM + == CORINFO_SynchronizationLockException); + +#define CORINFO_ArrayTypeMismatchException_ASM 6 +ASMCONSTANTS_C_ASSERT( CORINFO_ArrayTypeMismatchException_ASM + == CORINFO_ArrayTypeMismatchException); + +#define CORINFO_ArgumentNullException_ASM 8 +ASMCONSTANTS_C_ASSERT( CORINFO_ArgumentNullException_ASM + == CORINFO_ArgumentNullException); + +#define CORINFO_ArgumentException_ASM 9 +ASMCONSTANTS_C_ASSERT( CORINFO_ArgumentException_ASM + == CORINFO_ArgumentException); + + +// MachState offsets (AMD64\gmscpu.h) + +#define OFFSETOF__MachState__m_Rip 0x00 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_Rip + == offsetof(MachState, m_Rip)); + +#define OFFSETOF__MachState__m_Rsp 0x08 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_Rsp + == offsetof(MachState, m_Rsp)); + +#define OFFSETOF__MachState__m_CaptureRdi 0x10 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_CaptureRdi + == offsetof(MachState, m_CaptureRdi)); + +#define OFFSETOF__MachState__m_CaptureRsi 0x18 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_CaptureRsi + == offsetof(MachState, m_CaptureRsi)); + +#define OFFSETOF__MachState__m_CaptureRbx 0x20 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_CaptureRbx + == offsetof(MachState, m_CaptureRbx)); + +#define OFFSETOF__MachState__m_CaptureRbp 0x28 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_CaptureRbp + == offsetof(MachState, m_CaptureRbp)); + +#define OFFSETOF__MachState__m_CaptureR12 0x30 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_CaptureR12 + == offsetof(MachState, m_CaptureR12)); + +#define OFFSETOF__MachState__m_CaptureR13 0x38 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_CaptureR13 + == offsetof(MachState, m_CaptureR13)); + +#define OFFSETOF__MachState__m_CaptureR14 0x40 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_CaptureR14 + == offsetof(MachState, m_CaptureR14)); + +#define OFFSETOF__MachState__m_CaptureR15 0x48 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_CaptureR15 + == offsetof(MachState, m_CaptureR15)); + +#define OFFSETOF__MachState__m_pRdi 0x50 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_pRdi + == offsetof(MachState, m_pRdi)); + +#define OFFSETOF__MachState__m_pRsi 0x58 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_pRsi + == offsetof(MachState, m_pRsi)); + +#define OFFSETOF__MachState__m_pRbx 0x60 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_pRbx + == offsetof(MachState, m_pRbx)); + +#define OFFSETOF__MachState__m_pRbp 0x68 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_pRbp + == offsetof(MachState, m_pRbp)); + +#define OFFSETOF__MachState__m_pR12 0x70 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_pR12 + == offsetof(MachState, m_pR12)); + +#define OFFSETOF__MachState__m_pR13 0x78 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_pR13 + == offsetof(MachState, m_pR13)); + +#define OFFSETOF__MachState__m_pR14 0x80 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_pR14 + == offsetof(MachState, m_pR14)); + +#define OFFSETOF__MachState__m_pR15 0x88 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState__m_pR15 + == offsetof(MachState, m_pR15)); + +#define OFFSETOF__MachState___pRetAddr 0x90 +ASMCONSTANTS_C_ASSERT(OFFSETOF__MachState___pRetAddr + == offsetof(MachState, _pRetAddr)); + +#define OFFSETOF__LazyMachState__m_CaptureRip 0x98 +ASMCONSTANTS_C_ASSERT(OFFSETOF__LazyMachState__m_CaptureRip + == offsetof(LazyMachState, m_CaptureRip)); + +#define OFFSETOF__LazyMachState__m_CaptureRsp 0xA0 +ASMCONSTANTS_C_ASSERT(OFFSETOF__LazyMachState__m_CaptureRsp + == offsetof(LazyMachState, m_CaptureRsp)); + +#define OFFSETOF__MethodDesc__m_wFlags DBG_FRE(0x2E, 0x06) +ASMCONSTANTS_C_ASSERT(OFFSETOF__MethodDesc__m_wFlags == offsetof(MethodDesc, m_wFlags)); + +#define OFFSETOF__VASigCookie__pNDirectILStub 0x8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__VASigCookie__pNDirectILStub + == offsetof(VASigCookie, pNDirectILStub)); + +#define SIZEOF__CONTEXT (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + /*XMM_SAVE_AREA32*/(2*2 + 1*2 + 2 + 4 + 2*2 + 4 + 2*2 + 4*2 + 16*8 + 16*16 + 1*96) + 26*16 + 8 + 8*5) +ASMCONSTANTS_C_ASSERT(SIZEOF__CONTEXT + == sizeof(CONTEXT)); + +#define OFFSETOF__CONTEXT__Rax (8*6 + 4*2 + 2*6 + 4 + 8*6) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Rax + == offsetof(CONTEXT, Rax)); + +#define OFFSETOF__CONTEXT__Rcx (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Rcx + == offsetof(CONTEXT, Rcx)); + +#define OFFSETOF__CONTEXT__Rdx (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*2) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Rdx + == offsetof(CONTEXT, Rdx)); + +#define OFFSETOF__CONTEXT__Rbx (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*3) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Rbx + == offsetof(CONTEXT, Rbx)); + +#define OFFSETOF__CONTEXT__Rsp (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*4) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Rsp + == offsetof(CONTEXT, Rsp)); + +#define OFFSETOF__CONTEXT__Rbp (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*5) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Rbp + == offsetof(CONTEXT, Rbp)); + +#define OFFSETOF__CONTEXT__Rsi (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*6) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Rsi + == offsetof(CONTEXT, Rsi)); + +#define OFFSETOF__CONTEXT__Rdi (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*7) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Rdi + == offsetof(CONTEXT, Rdi)); + +#define OFFSETOF__CONTEXT__R8 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*8) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__R8 + == offsetof(CONTEXT, R8)); + +#define OFFSETOF__CONTEXT__R9 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*9) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__R9 + == offsetof(CONTEXT, R9)); + +#define OFFSETOF__CONTEXT__R10 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*10) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__R10 + == offsetof(CONTEXT, R10)); + +#define OFFSETOF__CONTEXT__R11 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*11) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__R11 + == offsetof(CONTEXT, R11)); + +#define OFFSETOF__CONTEXT__R12 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*12) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__R12 + == offsetof(CONTEXT, R12)); + +#define OFFSETOF__CONTEXT__R13 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*13) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__R13 + == offsetof(CONTEXT, R13)); + +#define OFFSETOF__CONTEXT__R14 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*14) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__R14 + == offsetof(CONTEXT, R14)); + +#define OFFSETOF__CONTEXT__R15 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*15) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__R15 + == offsetof(CONTEXT, R15)); + +#define OFFSETOF__CONTEXT__Rip (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Rip + == offsetof(CONTEXT, Rip)); + +#define OFFSETOF__CONTEXT__Xmm0 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm0 + == offsetof(CONTEXT, Xmm0)); + +#define OFFSETOF__CONTEXT__Xmm1 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm1 + == offsetof(CONTEXT, Xmm1)); + +#define OFFSETOF__CONTEXT__Xmm2 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*2) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm2 + == offsetof(CONTEXT, Xmm2)); + +#define OFFSETOF__CONTEXT__Xmm3 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*3) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm3 + == offsetof(CONTEXT, Xmm3)); + +#define OFFSETOF__CONTEXT__Xmm4 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*4) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm4 + == offsetof(CONTEXT, Xmm4)); + +#define OFFSETOF__CONTEXT__Xmm5 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*5) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm5 + == offsetof(CONTEXT, Xmm5)); + +#define OFFSETOF__CONTEXT__Xmm6 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*6) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm6 + == offsetof(CONTEXT, Xmm6)); + +#define OFFSETOF__CONTEXT__Xmm7 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*7) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm7 + == offsetof(CONTEXT, Xmm7)); + +#define OFFSETOF__CONTEXT__Xmm8 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*8) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm8 + == offsetof(CONTEXT, Xmm8)); + +#define OFFSETOF__CONTEXT__Xmm9 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*9) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm9 + == offsetof(CONTEXT, Xmm9)); + +#define OFFSETOF__CONTEXT__Xmm10 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*10) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm10 + == offsetof(CONTEXT, Xmm10)); + +#define OFFSETOF__CONTEXT__Xmm11 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*11) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm11 + == offsetof(CONTEXT, Xmm11)); + +#define OFFSETOF__CONTEXT__Xmm12 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*12) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm12 + == offsetof(CONTEXT, Xmm12)); + +#define OFFSETOF__CONTEXT__Xmm13 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*13) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm13 + == offsetof(CONTEXT, Xmm13)); + +#define OFFSETOF__CONTEXT__Xmm14 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*14) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm14 + == offsetof(CONTEXT, Xmm14)); + +#define OFFSETOF__CONTEXT__Xmm15 (8*6 + 4*2 + 2*6 + 4 + 8*6 + 8*16 + 8 + 2*16 + 8*16 + 16*15) +ASMCONSTANTS_C_ASSERT(OFFSETOF__CONTEXT__Xmm15 + == offsetof(CONTEXT, Xmm15)); + +#define SIZEOF__FaultingExceptionFrame (0x20 + SIZEOF__CONTEXT) +ASMCONSTANTS_C_ASSERT(SIZEOF__FaultingExceptionFrame + == sizeof(FaultingExceptionFrame)); + +#define OFFSETOF__FaultingExceptionFrame__m_fFilterExecuted 0x10 +ASMCONSTANTS_C_ASSERT(OFFSETOF__FaultingExceptionFrame__m_fFilterExecuted + == offsetof(FaultingExceptionFrame, m_fFilterExecuted)); + +#define OFFSETOF__PtrArray__m_NumComponents 0x8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__PtrArray__m_NumComponents + == offsetof(PtrArray, m_NumComponents)); + +#define OFFSETOF__PtrArray__m_Array 0x10 +ASMCONSTANTS_C_ASSERT(OFFSETOF__PtrArray__m_Array + == offsetof(PtrArray, m_Array)); + + +#define MethodDescClassification__mdcClassification 0x7 +ASMCONSTANTS_C_ASSERT(MethodDescClassification__mdcClassification == mdcClassification); + +#define MethodDescClassification__mcInstantiated 0x5 +ASMCONSTANTS_C_ASSERT(MethodDescClassification__mcInstantiated == mcInstantiated); + +#define OFFSET__TEB__TlsSlots 0x1480 +ASMCONSTANTS_C_ASSERT(OFFSET__TEB__TlsSlots == offsetof(TEB, TlsSlots)); + +#define OFFSETOF__TEB__LastErrorValue 0x68 +ASMCONSTANTS_C_ASSERT(OFFSETOF__TEB__LastErrorValue == offsetof(TEB, LastErrorValue)); + +#ifdef _DEBUG +#define TLS_GETTER_MAX_SIZE_ASM 0x30 +#else +#define TLS_GETTER_MAX_SIZE_ASM 0x18 +#endif +ASMCONSTANTS_C_ASSERT(TLS_GETTER_MAX_SIZE_ASM == TLS_GETTER_MAX_SIZE) + + +// If you change these constants, you need to update code in +// RedirectHandledJITCase.asm and ExcepAMD64.cpp. +#define REDIRECTSTUB_ESTABLISHER_OFFSET_RBP 0 +#define REDIRECTSTUB_RBP_OFFSET_CONTEXT 0x20 + +#define THROWSTUB_ESTABLISHER_OFFSET_FaultingExceptionFrame 0x30 + + +#define UMTHUNKSTUB_HOST_NOTIFY_FLAG_RBPOFFSET (0x40) // xmm save size + +#define Thread__ObjectRefFlush ?ObjectRefFlush@Thread@@SAXPEAV1@@Z + + +#define DELEGATE_FIELD_OFFSET__METHOD_AUX 0x20 +ASMCONSTANTS_RUNTIME_ASSERT(DELEGATE_FIELD_OFFSET__METHOD_AUX == Object::GetOffsetOfFirstField() + + MscorlibBinder::GetFieldOffset(FIELD__DELEGATE__METHOD_PTR_AUX)); + + +#define ASM_LARGE_OBJECT_SIZE 85000 +ASMCONSTANTS_C_ASSERT(ASM_LARGE_OBJECT_SIZE == LARGE_OBJECT_SIZE); + +#define OFFSETOF__ArrayBase__m_NumComponents 8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__ArrayBase__m_NumComponents + == offsetof(ArrayBase, m_NumComponents)); + +#define OFFSETOF__StringObject__m_StringLength 0x8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__StringObject__m_StringLength + == offsetof(StringObject, m_StringLength)); + +#define OFFSETOF__ArrayTypeDesc__m_TemplateMT 8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__ArrayTypeDesc__m_TemplateMT + == offsetof(ArrayTypeDesc, m_TemplateMT)); + +#define OFFSETOF__ArrayTypeDesc__m_Arg 0x10 +ASMCONSTANTS_C_ASSERT(OFFSETOF__ArrayTypeDesc__m_Arg + == offsetof(ArrayTypeDesc, m_Arg)); + +#define SYNCBLOCKINDEX_OFFSET 0x4 +ASMCONSTANTS_C_ASSERT(SYNCBLOCKINDEX_OFFSET + == (sizeof(ObjHeader) - offsetof(ObjHeader, m_SyncBlockValue))); + +#define CallDescrData__pSrc 0x00 +#define CallDescrData__numStackSlots 0x08 +#ifdef UNIX_AMD64_ABI +#define CallDescrData__pArgumentRegisters 0x10 +#define CallDescrData__pFloatArgumentRegisters 0x18 +#define CallDescrData__fpReturnSize 0x20 +#define CallDescrData__pTarget 0x28 +#define CallDescrData__returnValue 0x30 +#else +#define CallDescrData__dwRegTypeMap 0x10 +#define CallDescrData__fpReturnSize 0x18 +#define CallDescrData__pTarget 0x20 +#define CallDescrData__returnValue 0x28 +#endif + +ASMCONSTANTS_C_ASSERT(CallDescrData__pSrc == offsetof(CallDescrData, pSrc)) +ASMCONSTANTS_C_ASSERT(CallDescrData__numStackSlots == offsetof(CallDescrData, numStackSlots)) +#ifdef UNIX_AMD64_ABI +ASMCONSTANTS_C_ASSERT(CallDescrData__pArgumentRegisters == offsetof(CallDescrData, pArgumentRegisters)) +ASMCONSTANTS_C_ASSERT(CallDescrData__pFloatArgumentRegisters == offsetof(CallDescrData, pFloatArgumentRegisters)) +#else +ASMCONSTANTS_C_ASSERT(CallDescrData__dwRegTypeMap == offsetof(CallDescrData, dwRegTypeMap)) +#endif +ASMCONSTANTS_C_ASSERT(CallDescrData__fpReturnSize == offsetof(CallDescrData, fpReturnSize)) +ASMCONSTANTS_C_ASSERT(CallDescrData__pTarget == offsetof(CallDescrData, pTarget)) +ASMCONSTANTS_C_ASSERT(CallDescrData__returnValue == offsetof(CallDescrData, returnValue)) + +#undef ASMCONSTANTS_RUNTIME_ASSERT +#undef ASMCONSTANTS_C_ASSERT +#undef DBG_FRE + + +//#define USE_COMPILE_TIME_CONSTANT_FINDER // Uncomment this line to use the constant finder +#if defined(__cplusplus) && defined(USE_COMPILE_TIME_CONSTANT_FINDER) +// This class causes the compiler to emit an error with the constant we're interested in +// in the error message. This is useful if a size or offset changes. To use, comment out +// the compile-time assert that is firing, enable the constant finder, add the appropriate +// constant to find to BogusFunction(), and build. +// +// Here's a sample compiler error: +// d:\dd\clr\src\ndp\clr\src\vm\i386\asmconstants.h(326) : error C2248: 'FindCompileTimeConstant::FindCompileTimeConstant' : cannot access private member declared in class 'FindCompileTimeConstant' +// with +// [ +// N=1520 +// ] +// d:\dd\clr\src\ndp\clr\src\vm\i386\asmconstants.h(321) : see declaration of 'FindCompileTimeConstant::FindCompileTimeConstant' +// with +// [ +// N=1520 +// ] +template +class FindCompileTimeConstant +{ +private: + FindCompileTimeConstant(); +}; + +void BogusFunction() +{ + // Sample usage to generate the error + FindCompileTimeConstant bogus_variable; + FindCompileTimeConstant bogus_variable2; +} +#endif // defined(__cplusplus) && defined(USE_COMPILE_TIME_CONSTANT_FINDER) diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/amd64/cGenCpu.h b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/amd64/cGenCpu.h new file mode 100644 index 00000000000000..5dabbf7b190f6b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/amd64/cGenCpu.h @@ -0,0 +1,499 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// CGENCPU.H - +// +// Various helper routines for generating AMD64 assembly code. +// +// DO NOT INCLUDE THIS FILE DIRECTLY - ALWAYS USE CGENSYS.H INSTEAD +// + + + +#ifndef _TARGET_AMD64_ +#error Should only include "AMD64\cgencpu.h" for AMD64 builds +#endif + +#ifndef __cgencpu_h__ +#define __cgencpu_h__ + +#include "xmmintrin.h" + +// Given a return address retrieved during stackwalk, +// this is the offset by which it should be decremented to lend somewhere in a call instruction. +#define STACKWALK_CONTROLPC_ADJUST_OFFSET 1 + +// preferred alignment for data +#define DATA_ALIGNMENT 8 + +class MethodDesc; +class FramedMethodFrame; +class Module; +struct VASigCookie; +class ComCallMethodDesc; + +// +// functions implemented in AMD64 assembly +// +EXTERN_C void InstantiatingMethodStubWorker(void); +EXTERN_C void SinglecastDelegateInvokeStub(); +EXTERN_C void FastCallFinalizeWorker(Object *obj, PCODE funcPtr); + +#define COMMETHOD_PREPAD 16 // # extra bytes to allocate in addition to sizeof(ComCallMethodDesc) +#define COMMETHOD_CALL_PRESTUB_SIZE 6 // 32-bit indirect relative call +#define COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET -10 // the offset of the call target address inside the prestub + +#define STACK_ALIGN_SIZE 16 + +#define JUMP_ALLOCATE_SIZE 12 // # bytes to allocate for a 64-bit jump instruction +#define BACK_TO_BACK_JUMP_ALLOCATE_SIZE 12 // # bytes to allocate for a back to back 64-bit jump instruction +#define SIZEOF_LOAD_AND_JUMP_THUNK 22 // # bytes to mov r10, X; jmp Z +#define SIZEOF_LOAD2_AND_JUMP_THUNK 32 // # bytes to mov r10, X; mov r11, Y; jmp Z + +// Also in Zapper.h, CorCompile.h, FnTableAccess.h +#define USE_INDIRECT_CODEHEADER // use CodeHeader, RealCodeHeader construct + +#define HAS_NDIRECT_IMPORT_PRECODE 1 +//#define HAS_REMOTING_PRECODE 1 // TODO: Implement +#define HAS_FIXUP_PRECODE 1 +#define HAS_FIXUP_PRECODE_CHUNKS 1 + +// ThisPtrRetBufPrecode one is necessary for closed delegates over static methods with return buffer +#define HAS_THISPTR_RETBUF_PRECODE 1 + +#define CODE_SIZE_ALIGN 16 // must alloc code blocks on 8-byte boundaries; for perf reasons we use 16 byte boundaries +#define CACHE_LINE_SIZE 64 // Current AMD64 processors have 64-byte cache lines as per AMD64 optmization manual +#define LOG2SLOT LOG2_PTRSIZE + +#define ENREGISTERED_RETURNTYPE_MAXSIZE 8 // bytes +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 8 // bytes +#define ENREGISTERED_PARAMTYPE_MAXSIZE 8 // bytes + +#ifdef UNIX_AMD64_ABI +#define CALLDESCR_ARGREGS 1 // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS 1 // CallDescrWorker has FloatArgumentRegisters parameter +#else +#define COM_STUBS_SEPARATE_FP_LOCATIONS +#define CALLDESCR_REGTYPEMAP 1 +#endif + +#define INSTRFMT_K64SMALL +#define INSTRFMT_K64 + +#define USE_REDIRECT_FOR_GCSTRESS + +// +// REX prefix byte +// +#define REX_PREFIX_BASE 0x40 // 0100xxxx +#define REX_OPERAND_SIZE_64BIT 0x08 // xxxx1xxx +#define REX_MODRM_REG_EXT 0x04 // xxxxx1xx // use for 'middle' 3 bit field of mod/r/m +#define REX_SIB_INDEX_EXT 0x02 // xxxxxx10 +#define REX_MODRM_RM_EXT 0x01 // XXXXXXX1 // use for low 3 bit field of mod/r/m +#define REX_SIB_BASE_EXT 0x01 // XXXXXXX1 +#define REX_OPCODE_REG_EXT 0x01 // XXXXXXX1 + +#define X86_REGISTER_MASK 0x7 + +#define X86RegFromAMD64Reg(extended_reg) \ + ((X86Reg)(((int)extended_reg) & X86_REGISTER_MASK)) + +// Max size of optimized TLS helpers +#ifdef _DEBUG +// Debug build needs extra space for last error trashing +#define TLS_GETTER_MAX_SIZE 0x30 +#else +#define TLS_GETTER_MAX_SIZE 0x18 +#endif + +//======================================================================= +// IMPORTANT: This value is used to figure out how much to allocate +// for a fixed array of FieldMarshaler's. That means it must be at least +// as large as the largest FieldMarshaler subclass. This requirement +// is guarded by an assert. +//======================================================================= +#define MAXFIELDMARSHALERSIZE 40 + + +// Why is the return value ARG_SLOT? On 64-bit systems, that is 64-bits +// and much bigger than necessary for R4, requiring explicit downcasts. +inline +ARG_SLOT FPSpillToR4(void* pSpillSlot) +{ + LIMITED_METHOD_CONTRACT; + return *(DWORD*)pSpillSlot; +} + +inline +ARG_SLOT FPSpillToR8(void* pSpillSlot) +{ + LIMITED_METHOD_CONTRACT; + return *(SIZE_T*)pSpillSlot; +} + +inline +void R4ToFPSpill(void* pSpillSlot, DWORD srcFloatAsDWORD) +{ + LIMITED_METHOD_CONTRACT; + *(SIZE_T*)pSpillSlot = (SIZE_T)srcFloatAsDWORD; + *((SIZE_T*)pSpillSlot + 1) = 0; +} + +inline +void R8ToFPSpill(void* pSpillSlot, SIZE_T srcDoubleAsSIZE_T) +{ + LIMITED_METHOD_CONTRACT; + *(SIZE_T*)pSpillSlot = srcDoubleAsSIZE_T; + *((SIZE_T*)pSpillSlot + 1) = 0; +} + + +#ifdef CROSSGEN_COMPILE +#define GetEEFuncEntryPoint(pfn) 0x1001 +#else +#define GetEEFuncEntryPoint(pfn) GFN_TADDR(pfn) +#endif + + +//********************************************************************** +// Parameter size +//********************************************************************** + +typedef INT64 StackElemType; +#define STACK_ELEM_SIZE sizeof(StackElemType) + +// !! This expression assumes STACK_ELEM_SIZE is a power of 2. +#define StackElemSize(parmSize) (((parmSize) + STACK_ELEM_SIZE - 1) & ~((ULONG)(STACK_ELEM_SIZE - 1))) + +//********************************************************************** +// Frames +//********************************************************************** +//-------------------------------------------------------------------- +// This represents some of the TransitionFrame fields that are +// stored at negative offsets. +//-------------------------------------------------------------------- +typedef DPTR(struct CalleeSavedRegisters) PTR_CalleeSavedRegisters; +struct CalleeSavedRegisters { +#ifndef UNIX_AMD64_ABI + INT_PTR rdi; + INT_PTR rsi; +#endif + INT_PTR rbx; + INT_PTR rbp; + INT_PTR r12; + INT_PTR r13; + INT_PTR r14; + INT_PTR r15; +}; + +struct REGDISPLAY; + +void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegisters * pRegs); + +//-------------------------------------------------------------------- +// This represents the arguments that are stored in volatile registers. +// This should not overlap the CalleeSavedRegisters since those are already +// saved separately and it would be wasteful to save the same register twice. +// If we do use a non-volatile register as an argument, then the ArgIterator +// will probably have to communicate this back to the PromoteCallerStack +// routine to avoid a double promotion. +//-------------------------------------------------------------------- +#ifdef UNIX_AMD64_ABI + +#define ENUM_ARGUMENT_REGISTERS() \ + ARGUMENT_REGISTER(RDI) \ + ARGUMENT_REGISTER(RSI) \ + ARGUMENT_REGISTER(RDX) \ + ARGUMENT_REGISTER(RCX) \ + ARGUMENT_REGISTER(R8) \ + ARGUMENT_REGISTER(R9) + +#define NUM_ARGUMENT_REGISTERS 6 + +#else // UNIX_AMD64_ABI + +#define ENUM_ARGUMENT_REGISTERS() \ + ARGUMENT_REGISTER(RCX) \ + ARGUMENT_REGISTER(RDX) \ + ARGUMENT_REGISTER(R8) \ + ARGUMENT_REGISTER(R9) + +#define NUM_ARGUMENT_REGISTERS 4 + +#endif // UNIX_AMD64_ABI + +typedef DPTR(struct ArgumentRegisters) PTR_ArgumentRegisters; +struct ArgumentRegisters { + #define ARGUMENT_REGISTER(regname) INT_PTR regname; + ENUM_ARGUMENT_REGISTERS(); + #undef ARGUMENT_REGISTER +}; + +#define SCRATCH_REGISTER_X86REG kRAX + +#ifdef UNIX_AMD64_ABI +#define THIS_REG RDI +#define THIS_kREG kRDI +#else +#define THIS_REG RCX +#define THIS_kREG kRCX +#endif + +#ifdef UNIX_AMD64_ABI + +typedef DPTR(struct FloatArgumentRegisters) PTR_FloatArgumentRegisters; +struct FloatArgumentRegisters { + M128A d[8]; // xmm0-xmm7 +}; + +#endif + + +// Sufficient context for Try/Catch restoration. +struct EHContext { + // Not used +}; + +#define ARGUMENTREGISTERS_SIZE sizeof(ArgumentRegisters) + + +#include "stublinkeramd64.h" + + + +//********************************************************************** +// Exception handling +//********************************************************************** + +inline PCODE GetIP(const CONTEXT * context) +{ + CONTRACTL + { + SO_TOLERANT; + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + + PRECONDITION(CheckPointer(context)); + } + CONTRACTL_END; + + return PCODE(context->Rip); +} + +inline void SetIP(CONTEXT* context, PCODE rip) +{ + CONTRACTL + { + SO_TOLERANT; + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + + PRECONDITION(CheckPointer(context)); + } + CONTRACTL_END; + + context->Rip = (DWORD64) rip; +} + +inline TADDR GetSP(const CONTEXT * context) +{ + CONTRACTL + { + SO_TOLERANT; + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + + PRECONDITION(CheckPointer(context)); + } + CONTRACTL_END; + + return (TADDR)context->Rsp; +} +inline void SetSP(CONTEXT *context, TADDR rsp) +{ + CONTRACTL + { + SO_TOLERANT; + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + + PRECONDITION(CheckPointer(context)); + } + CONTRACTL_END; + + context->Rsp = rsp; +} + +#define SetFP(context, ebp) +inline TADDR GetFP(const CONTEXT * context) +{ + LIMITED_METHOD_CONTRACT; + + return (TADDR)(context->Rbp); +} + +extern "C" TADDR GetCurrentSP(); + +// Emits: +// mov r10, pv1 +// mov rax, pTarget +// jmp rax +void EncodeLoadAndJumpThunk (LPBYTE pBuffer, LPVOID pv, LPVOID pTarget); + + +// Get Rel32 destination, emit jumpStub if necessary +INT32 rel32UsingJumpStub(INT32 UNALIGNED * pRel32, PCODE target, MethodDesc *pMethod, LoaderAllocator *pLoaderAllocator = NULL); + +void emitCOMStubCall (ComCallMethodDesc *pCOMMethod, PCODE target); + +void emitJump(LPBYTE pBuffer, LPVOID target); + +BOOL isJumpRel32(PCODE pCode); +PCODE decodeJump32(PCODE pCode); + +BOOL isJumpRel64(PCODE pCode); +PCODE decodeJump64(PCODE pCode); + +// +// On IA64 back to back jumps should be separated by a nop bundle to get +// the best performance from the hardware's branch prediction logic. +// For all other platforms back to back jumps don't require anything special +// That is why we have these two wrapper functions that call emitJump and decodeJump +// +inline void emitBackToBackJump(LPBYTE pBuffer, LPVOID target) +{ + WRAPPER_NO_CONTRACT; + + emitJump(pBuffer, target); +} + +inline BOOL isBackToBackJump(PCODE pCode) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return isJumpRel32(pCode) || isJumpRel64(pCode); +} + +inline PCODE decodeBackToBackJump(PCODE pCode) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + if (isJumpRel32(pCode)) + return decodeJump32(pCode); + else + if (isJumpRel64(pCode)) + return decodeJump64(pCode); + else + return NULL; +} + +extern "C" void setFPReturn(int fpSize, INT64 retVal); +extern "C" void getFPReturn(int fpSize, INT64 *retval); + + +struct ComToManagedExRecord; // defined in cgencpu.cpp + +inline BOOL IsUnmanagedValueTypeReturnedByRef(UINT sizeofvaluetype) +{ + LIMITED_METHOD_CONTRACT; + + if (sizeofvaluetype > ENREGISTERED_RETURNTYPE_MAXSIZE) + { + return TRUE; + } + else + { + return FALSE; + } +} + +#include +DECLSPEC_ALIGN(8) struct UMEntryThunkCode +{ + // padding // CC CC CC CC + // mov r10, pUMEntryThunk // 49 ba xx xx xx xx xx xx xx xx // METHODDESC_REGISTER + // mov rax, pJmpDest // 48 b8 xx xx xx xx xx xx xx xx // need to ensure this imm64 is qword aligned + // TAILJMP_RAX // 48 FF E0 + + BYTE m_padding[4]; + BYTE m_movR10[2]; // MOV R10, + LPVOID m_uet; // pointer to start of this structure + BYTE m_movRAX[2]; // MOV RAX, + DECLSPEC_ALIGN(8) + const BYTE* m_execstub; // pointer to destination code // ensure this is qword aligned + BYTE m_jmpRAX[3]; // JMP RAX + BYTE m_padding2[5]; + + void Encode(BYTE* pTargetCode, void* pvSecretParam); + + LPCBYTE GetEntryPoint() const + { + LIMITED_METHOD_CONTRACT; + + return (LPCBYTE)&m_movR10; + } + + static int GetEntryPointOffset() + { + LIMITED_METHOD_CONTRACT; + + return offsetof(UMEntryThunkCode, m_movR10); + } +}; +#include + +#ifndef DACCESS_COMPILE + +DWORD GetOffsetAtEndOfFunction(ULONGLONG uImageBase, + PRUNTIME_FUNCTION pFunctionEntry, + int offsetNum = 1); + +#endif // DACCESS_COMPILE + +// ClrFlushInstructionCache is used when we want to call FlushInstructionCache +// for a specific architecture in the common code, but not for other architectures. +// We call ClrFlushInstructionCache whenever we create or modify code in the heap. +// Currently ClrFlushInstructionCache has no effect on AMD64 +// + +inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode) +{ + // FlushInstructionCache(GetCurrentProcess(), pCodeAddr, sizeOfCode); + MemoryBarrier(); + return TRUE; +} + +#ifndef FEATURE_IMPLICIT_TLS +// +// JIT HELPER ALIASING FOR PORTABILITY. +// +// Create alias for optimized implementations of helpers provided on this platform +// +#define JIT_MonEnter JIT_MonEnter +#define JIT_MonEnterWorker JIT_MonEnterWorker_InlineGetThread +#define JIT_MonReliableEnter JIT_MonEnterWorker +#define JIT_MonTryEnter JIT_MonTryEnter_InlineGetThread +#define JIT_MonExit JIT_MonExit +#define JIT_MonExitWorker JIT_MonExitWorker_InlineGetThread +#define JIT_MonEnterStatic JIT_MonEnterStatic_InlineGetThread +#define JIT_MonExitStatic JIT_MonExitStatic_InlineGetThread + +#define JIT_GetSharedGCStaticBase JIT_GetSharedGCStaticBase_InlineGetAppDomain +#define JIT_GetSharedNonGCStaticBase JIT_GetSharedNonGCStaticBase_InlineGetAppDomain +#define JIT_GetSharedGCStaticBaseNoCtor JIT_GetSharedGCStaticBaseNoCtor_InlineGetAppDomain +#define JIT_GetSharedNonGCStaticBaseNoCtor JIT_GetSharedNonGCStaticBaseNoCtor_InlineGetAppDomain + +#endif // FEATURE_IMPLICIT_TLS + +#define JIT_ChkCastClass JIT_ChkCastClass +#define JIT_ChkCastClassSpecial JIT_ChkCastClassSpecial +#define JIT_IsInstanceOfClass JIT_IsInstanceOfClass +#define JIT_ChkCastInterface JIT_ChkCastInterface +#define JIT_IsInstanceOfInterface JIT_IsInstanceOfInterface +#define JIT_Stelem_Ref JIT_Stelem_Ref + +#endif // __cgencpu_h__ diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm/asmconstants.h b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm/asmconstants.h new file mode 100644 index 00000000000000..8345c8f25025b4 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm/asmconstants.h @@ -0,0 +1,303 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// asmconstants.h - +// +// This header defines field offsets and constants used by assembly code +// Be sure to rebuild clr/src/vm/ceemain.cpp after changing this file, to +// ensure that the constants match the expected C/C++ values + +// #ifndef _ARM_ +// #error this file should only be used on an ARM platform +// #endif // _ARM_ + +#include "..\..\inc\switches.h" + +//----------------------------------------------------------------------------- + +#ifndef ASMCONSTANTS_C_ASSERT +#define ASMCONSTANTS_C_ASSERT(cond) +#endif + +#ifndef ASMCONSTANTS_RUNTIME_ASSERT +#define ASMCONSTANTS_RUNTIME_ASSERT(cond) +#endif + +// Some contants are different in _DEBUG builds. This macro factors out ifdefs from below. +#ifdef _DEBUG +#define DBG_FRE(dbg,fre) dbg +#else +#define DBG_FRE(dbg,fre) fre +#endif + +#define DynamicHelperFrameFlags_Default 0 +#define DynamicHelperFrameFlags_ObjectArg 1 +#define DynamicHelperFrameFlags_ObjectArg2 2 + +#define REDIRECTSTUB_SP_OFFSET_CONTEXT 0 + +#define CORINFO_NullReferenceException_ASM 0 +ASMCONSTANTS_C_ASSERT( CORINFO_NullReferenceException_ASM + == CORINFO_NullReferenceException); + +#define CORINFO_IndexOutOfRangeException_ASM 3 +ASMCONSTANTS_C_ASSERT( CORINFO_IndexOutOfRangeException_ASM + == CORINFO_IndexOutOfRangeException); + + +// Offset of the array containing the address of captured registers in MachState +#define MachState__captureR4_R11 0x0 +ASMCONSTANTS_C_ASSERT(MachState__captureR4_R11 == offsetof(MachState, captureR4_R11)) + +// Offset of the array containing the address of preserved registers in MachState +#define MachState___R4_R11 0x20 +ASMCONSTANTS_C_ASSERT(MachState___R4_R11 == offsetof(MachState, _R4_R11)) + +#define MachState__isValid 0x48 +ASMCONSTANTS_C_ASSERT(MachState__isValid == offsetof(MachState, _isValid)) + +#define LazyMachState_captureR4_R11 MachState__captureR4_R11 +ASMCONSTANTS_C_ASSERT(LazyMachState_captureR4_R11 == offsetof(LazyMachState, captureR4_R11)) + +#define LazyMachState_captureSp (MachState__isValid+4) +ASMCONSTANTS_C_ASSERT(LazyMachState_captureSp == offsetof(LazyMachState, captureSp)) + +#define LazyMachState_captureIp (LazyMachState_captureSp+4) +ASMCONSTANTS_C_ASSERT(LazyMachState_captureIp == offsetof(LazyMachState, captureIp)) + +#define DelegateObject___methodPtr 0x0c +ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); + +#define DelegateObject___target 0x04 +ASMCONSTANTS_C_ASSERT(DelegateObject___target == offsetof(DelegateObject, _target)); + +#define MethodTable__m_BaseSize 0x04 +ASMCONSTANTS_C_ASSERT(MethodTable__m_BaseSize == offsetof(MethodTable, m_BaseSize)); + +#define MethodTable__m_dwFlags 0x0 +ASMCONSTANTS_C_ASSERT(MethodTable__m_dwFlags == offsetof(MethodTable, m_dwFlags)); + +#define MethodTable__m_pWriteableData DBG_FRE(0x1c, 0x18) +ASMCONSTANTS_C_ASSERT(MethodTable__m_pWriteableData == offsetof(MethodTable, m_pWriteableData)); + +#define MethodTable__enum_flag_ContainsPointers 0x01000000 +ASMCONSTANTS_C_ASSERT(MethodTable__enum_flag_ContainsPointers == MethodTable::enum_flag_ContainsPointers); + +#define MethodTable__m_ElementType DBG_FRE(0x24, 0x20) +ASMCONSTANTS_C_ASSERT(MethodTable__m_ElementType == offsetof(MethodTable, m_pMultipurposeSlot1)); + +#define SIZEOF__MethodTable DBG_FRE(0x2c, 0x28) +ASMCONSTANTS_C_ASSERT(SIZEOF__MethodTable == sizeof(MethodTable)); + +#define MethodTableWriteableData__m_dwFlags 0x00 +ASMCONSTANTS_C_ASSERT(MethodTableWriteableData__m_dwFlags == offsetof(MethodTableWriteableData, m_dwFlags)); + +#define MethodTableWriteableData__enum_flag_Unrestored 0x04 +ASMCONSTANTS_C_ASSERT(MethodTableWriteableData__enum_flag_Unrestored == MethodTableWriteableData::enum_flag_Unrestored); + +#define StringObject__m_StringLength 0x04 +ASMCONSTANTS_C_ASSERT(StringObject__m_StringLength == offsetof(StringObject, m_StringLength)); + +#define SIZEOF__BaseStringObject 0xe +ASMCONSTANTS_C_ASSERT(SIZEOF__BaseStringObject == (ObjSizeOf(StringObject) + sizeof(WCHAR))); + +#define SIZEOF__ArrayOfObjectRef 0xc +ASMCONSTANTS_C_ASSERT(SIZEOF__ArrayOfObjectRef == ObjSizeOf(ArrayBase)); + +#define SIZEOF__ArrayOfValueType 0xc +ASMCONSTANTS_C_ASSERT(SIZEOF__ArrayOfValueType == ObjSizeOf(ArrayBase)); + +#define ArrayBase__m_NumComponents 0x4 +ASMCONSTANTS_C_ASSERT(ArrayBase__m_NumComponents == offsetof(ArrayBase, m_NumComponents)); + +#define ArrayTypeDesc__m_TemplateMT 0x4 +ASMCONSTANTS_C_ASSERT(ArrayTypeDesc__m_TemplateMT == offsetof(ArrayTypeDesc, m_TemplateMT)); + +#define ArrayTypeDesc__m_Arg 0x8 +ASMCONSTANTS_C_ASSERT(ArrayTypeDesc__m_Arg == offsetof(ArrayTypeDesc, m_Arg)); + +#define PtrArray__m_Array 0x8 +ASMCONSTANTS_C_ASSERT(PtrArray__m_Array == offsetof(PtrArray, m_Array)); + +#define SYSTEM_INFO__dwNumberOfProcessors 0x14 +ASMCONSTANTS_C_ASSERT(SYSTEM_INFO__dwNumberOfProcessors == offsetof(SYSTEM_INFO, dwNumberOfProcessors)); + +#define TypeHandle_CanCast 0x1 // TypeHandle::CanCast + +// Maximum number of characters to be allocated for a string in AllocateStringFast*. Chosen so that we'll +// never have to check for overflow and will never try to allocate a string on regular heap that should have +// gone on the large object heap. Additionally the constant has been chosen such that it can be encoded in a +// single Thumb2 CMP instruction. +#define MAX_FAST_ALLOCATE_STRING_SIZE 42240 +ASMCONSTANTS_C_ASSERT(MAX_FAST_ALLOCATE_STRING_SIZE < ((LARGE_OBJECT_SIZE - SIZEOF__BaseStringObject) / 2)); + + +// Array of objectRef of this Maximum number of elements can be allocated in JIT_NewArr1OBJ_MP*. Chosen so that we'll +// never have to check for overflow and will never try to allocate the array on regular heap that should have +// gone on the large object heap. Additionally the constant has been chosen such that it can be encoded in a +// single Thumb2 CMP instruction. +#define MAX_FAST_ALLOCATE_ARRAY_OBJECTREF_SIZE 21120 +ASMCONSTANTS_C_ASSERT(MAX_FAST_ALLOCATE_ARRAY_OBJECTREF_SIZE < ((LARGE_OBJECT_SIZE - SIZEOF__ArrayOfObjectRef) / sizeof(void*))); + +// Array of valueClass of this Maximum number of characters can be allocated JIT_NewArr1VC_MP*. Chosen so that we'll +// never have to check for overflow and will never try to allocate the array on regular heap that should have +// gone on the large object heap. Additionally the constant has been chosen such that it can be encoded in a +// single Thumb2 CMP instruction. +#define MAX_FAST_ALLOCATE_ARRAY_VC_SIZE 65280 +ASMCONSTANTS_C_ASSERT(MAX_FAST_ALLOCATE_ARRAY_VC_SIZE < ((4294967296 - 1 - SIZEOF__ArrayOfValueType) / 65536)); + +#define SIZEOF__GSCookie 0x4 +ASMCONSTANTS_C_ASSERT(SIZEOF__GSCookie == sizeof(GSCookie)); + +#define SIZEOF__Frame 0x8 +ASMCONSTANTS_C_ASSERT(SIZEOF__Frame == sizeof(Frame)); + +#define SIZEOF__CONTEXT 0x1a0 +ASMCONSTANTS_C_ASSERT(SIZEOF__CONTEXT == sizeof(T_CONTEXT)); + +#define SIZEOF__CalleeSavedRegisters 0x24 +ASMCONSTANTS_C_ASSERT(SIZEOF__CalleeSavedRegisters == sizeof(CalleeSavedRegisters)) + +#define SIZEOF__ArgumentRegisters 0x10 +ASMCONSTANTS_C_ASSERT(SIZEOF__ArgumentRegisters == sizeof(ArgumentRegisters)) + +#define SIZEOF__FloatArgumentRegisters 0x40 +ASMCONSTANTS_C_ASSERT(SIZEOF__FloatArgumentRegisters == sizeof(FloatArgumentRegisters)) + +#define UMEntryThunk__m_pUMThunkMarshInfo 0x0C +ASMCONSTANTS_C_ASSERT(UMEntryThunk__m_pUMThunkMarshInfo == offsetof(UMEntryThunk, m_pUMThunkMarshInfo)) + +#define UMEntryThunk__m_dwDomainId 0x10 +ASMCONSTANTS_C_ASSERT(UMEntryThunk__m_dwDomainId == offsetof(UMEntryThunk, m_dwDomainId)) + +#define UMThunkMarshInfo__m_pILStub 0x00 +ASMCONSTANTS_C_ASSERT(UMThunkMarshInfo__m_pILStub == offsetof(UMThunkMarshInfo, m_pILStub)) + +#define UMThunkMarshInfo__m_cbActualArgSize 0x04 +ASMCONSTANTS_C_ASSERT(UMThunkMarshInfo__m_cbActualArgSize == offsetof(UMThunkMarshInfo, m_cbActualArgSize)) + +#ifdef FEATURE_REMOTING + +#define TransparentProxyObject___stubData 0x8 +ASMCONSTANTS_C_ASSERT(TransparentProxyObject___stubData == offsetof(TransparentProxyObject, _stubData)) + +#define TransparentProxyObject___stub 0x14 +ASMCONSTANTS_C_ASSERT(TransparentProxyObject___stub == offsetof(TransparentProxyObject, _stub)) + +#define TransparentProxyObject___pMT 0xc +ASMCONSTANTS_C_ASSERT(TransparentProxyObject___pMT == offsetof(TransparentProxyObject, _pMT)) + +#define RemotingPrecode__m_pMethodDesc 0x10 +ASMCONSTANTS_C_ASSERT(RemotingPrecode__m_pMethodDesc == offsetof(RemotingPrecode, m_pMethodDesc)) + +#define REMOTING_PRECODE_RET_OFFSET 0x06 + +#endif // FEATURE_REMOTING + +#define MethodDesc__m_wFlags DBG_FRE(0x1A, 0x06) +ASMCONSTANTS_C_ASSERT(MethodDesc__m_wFlags == offsetof(MethodDesc, m_wFlags)) + +#define MethodDesc__mdcClassification 0x7 +ASMCONSTANTS_C_ASSERT(MethodDesc__mdcClassification == mdcClassification) + +#ifdef FEATURE_COMINTEROP + +#define MethodDesc__mcComInterop 0x6 +ASMCONSTANTS_C_ASSERT(MethodDesc__mcComInterop == mcComInterop) + +#define Stub__m_pCode DBG_FRE(0x10, 0x0c) +ASMCONSTANTS_C_ASSERT(Stub__m_pCode == sizeof(Stub)) + +#define SIZEOF__ComMethodFrame 0x24 +ASMCONSTANTS_C_ASSERT(SIZEOF__ComMethodFrame == sizeof(ComMethodFrame)) + +#define UnmanagedToManagedFrame__m_pvDatum 0x08 +ASMCONSTANTS_C_ASSERT(UnmanagedToManagedFrame__m_pvDatum == offsetof(UnmanagedToManagedFrame, m_pvDatum)) + +// In ComCallPreStub and GenericComPlusCallStub, we setup R12 to contain address of ComCallMethodDesc after doing the following: +// +// mov r12, pc +// +// This constant defines where ComCallMethodDesc is post execution of the above instruction. +#define ComCallMethodDesc_Offset_FromR12 0x8 + +#endif // FEATURE_COMINTEROP + +#ifndef CROSSGEN_COMPILE +#define Thread__m_alloc_context__alloc_limit 0x44 +ASMCONSTANTS_C_ASSERT(Thread__m_alloc_context__alloc_limit == offsetof(Thread, m_alloc_context) + offsetof(alloc_context, alloc_limit)); + +#define Thread__m_alloc_context__alloc_ptr 0x40 +ASMCONSTANTS_C_ASSERT(Thread__m_alloc_context__alloc_ptr == offsetof(Thread, m_alloc_context) + offsetof(alloc_context, alloc_ptr)); +#endif // CROSSGEN_COMPILE + +#define Thread__m_fPreemptiveGCDisabled 0x08 +#ifndef CROSSGEN_COMPILE +ASMCONSTANTS_C_ASSERT(Thread__m_fPreemptiveGCDisabled == offsetof(Thread, m_fPreemptiveGCDisabled)); +#endif // CROSSGEN_COMPILE +#define Thread_m_fPreemptiveGCDisabled Thread__m_fPreemptiveGCDisabled + +#define Thread__m_pFrame 0x0C +#ifndef CROSSGEN_COMPILE +ASMCONSTANTS_C_ASSERT(Thread__m_pFrame == offsetof(Thread, m_pFrame)); +#endif // CROSSGEN_COMPILE +#define Thread_m_pFrame Thread__m_pFrame + +#ifndef CROSSGEN_COMPILE +#define Thread__m_pDomain 0x14 +ASMCONSTANTS_C_ASSERT(Thread__m_pDomain == offsetof(Thread, m_pDomain)); + +#define AppDomain__m_dwId 0x04 +ASMCONSTANTS_C_ASSERT(AppDomain__m_dwId == offsetof(AppDomain, m_dwId)); + +#define AppDomain__m_sDomainLocalBlock 0x08 +ASMCONSTANTS_C_ASSERT(AppDomain__m_sDomainLocalBlock == offsetof(AppDomain, m_sDomainLocalBlock)); + +#define DomainLocalBlock__m_pModuleSlots 0x04 +ASMCONSTANTS_C_ASSERT(DomainLocalBlock__m_pModuleSlots == offsetof(DomainLocalBlock, m_pModuleSlots)); + +#define DomainLocalModule__m_pDataBlob 0x18 +ASMCONSTANTS_C_ASSERT(DomainLocalModule__m_pDataBlob == offsetof(DomainLocalModule, m_pDataBlob)); + +#define DomainLocalModule__m_pGCStatics 0x10 +ASMCONSTANTS_C_ASSERT(DomainLocalModule__m_pGCStatics == offsetof(DomainLocalModule, m_pGCStatics)); + +#endif + +#define ASM__VTABLE_SLOTS_PER_CHUNK 8 +ASMCONSTANTS_C_ASSERT(ASM__VTABLE_SLOTS_PER_CHUNK == VTABLE_SLOTS_PER_CHUNK) + +#define ASM__VTABLE_SLOTS_PER_CHUNK_LOG2 3 +ASMCONSTANTS_C_ASSERT(ASM__VTABLE_SLOTS_PER_CHUNK_LOG2 == VTABLE_SLOTS_PER_CHUNK_LOG2) + +#define VASigCookie__pNDirectILStub 0x4 +ASMCONSTANTS_C_ASSERT(VASigCookie__pNDirectILStub == offsetof(VASigCookie, pNDirectILStub)) + +#define CONTEXT_Pc 0x040 +ASMCONSTANTS_C_ASSERT(CONTEXT_Pc == offsetof(T_CONTEXT,Pc)) + +#define TLS_GETTER_MAX_SIZE_ASM 0x10 +ASMCONSTANTS_C_ASSERT(TLS_GETTER_MAX_SIZE_ASM == TLS_GETTER_MAX_SIZE) + +#define CallDescrData__pSrc 0x00 +#define CallDescrData__numStackSlots 0x04 +#define CallDescrData__pArgumentRegisters 0x08 +#define CallDescrData__pFloatArgumentRegisters 0x0C +#define CallDescrData__fpReturnSize 0x10 +#define CallDescrData__pTarget 0x14 +#define CallDescrData__returnValue 0x18 + +ASMCONSTANTS_C_ASSERT(CallDescrData__pSrc == offsetof(CallDescrData, pSrc)) +ASMCONSTANTS_C_ASSERT(CallDescrData__numStackSlots == offsetof(CallDescrData, numStackSlots)) +ASMCONSTANTS_C_ASSERT(CallDescrData__pArgumentRegisters == offsetof(CallDescrData, pArgumentRegisters)) +ASMCONSTANTS_C_ASSERT(CallDescrData__pFloatArgumentRegisters == offsetof(CallDescrData, pFloatArgumentRegisters)) +ASMCONSTANTS_C_ASSERT(CallDescrData__fpReturnSize == offsetof(CallDescrData, fpReturnSize)) +ASMCONSTANTS_C_ASSERT(CallDescrData__pTarget == offsetof(CallDescrData, pTarget)) +ASMCONSTANTS_C_ASSERT(CallDescrData__returnValue == offsetof(CallDescrData, returnValue)) + +#define SIZEOF__FaultingExceptionFrame SIZEOF__Frame + 0x8 + SIZEOF__CONTEXT +#define FaultingExceptionFrame__m_fFilterExecuted SIZEOF__Frame +ASMCONSTANTS_C_ASSERT(SIZEOF__FaultingExceptionFrame == sizeof(FaultingExceptionFrame)); +ASMCONSTANTS_C_ASSERT(FaultingExceptionFrame__m_fFilterExecuted == offsetof(FaultingExceptionFrame, m_fFilterExecuted)); + +#undef ASMCONSTANTS_RUNTIME_ASSERT +#undef ASMCONSTANTS_C_ASSERT diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm/asmhelpers.asm b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm/asmhelpers.asm new file mode 100644 index 00000000000000..204ef8f42d774c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm/asmhelpers.asm @@ -0,0 +1,2746 @@ +; Licensed to the .NET Foundation under one or more agreements. +; The .NET Foundation licenses this file to you under the MIT license. + +;; ==++== +;; + +;; +;; ==--== +#include "ksarm.h" + +#include "asmconstants.h" + +#include "asmmacros.h" + + SETALIAS CTPMethodTable__s_pThunkTable, ?s_pThunkTable@CTPMethodTable@@0PAVMethodTable@@A + SETALIAS g_pObjectClass, ?g_pObjectClass@@3PAVMethodTable@@A + + IMPORT GetThread + IMPORT JIT_InternalThrow + IMPORT JIT_WriteBarrier + IMPORT TheUMEntryPrestubWorker + IMPORT CreateThreadBlockThrow + IMPORT UMThunkStubRareDisableWorker + IMPORT UM2MDoADCallBack + IMPORT PreStubWorker + IMPORT NDirectImportWorker + IMPORT ObjIsInstanceOfNoGC + IMPORT ArrayStoreCheck + IMPORT VSD_ResolveWorker + IMPORT $g_pObjectClass + +#ifdef WRITE_BARRIER_CHECK + SETALIAS g_GCShadow, ?g_GCShadow@@3PAEA + SETALIAS g_GCShadowEnd, ?g_GCShadowEnd@@3PAEA + + IMPORT g_lowest_address + IMPORT $g_GCShadow + IMPORT $g_GCShadowEnd +#endif // WRITE_BARRIER_CHECK + + +#ifdef FEATURE_REMOTING + IMPORT $CTPMethodTable__s_pThunkTable + IMPORT VSD_GetTargetForTPWorker + IMPORT VSD_GetTargetForTPWorkerQuick + IMPORT TransparentProxyStubWorker +#endif +#ifdef FEATURE_COMINTEROP + IMPORT CLRToCOMWorker + IMPORT ComPreStubWorker + IMPORT COMToCLRWorker +#endif + IMPORT CallDescrWorkerUnwindFrameChainHandler + IMPORT UMEntryPrestubUnwindFrameChainHandler + IMPORT UMThunkStubUnwindFrameChainHandler +#ifdef FEATURE_COMINTEROP + IMPORT ReverseComUnwindFrameChainHandler +#endif + +#ifdef FEATURE_HIJACK + IMPORT OnHijackObjectWorker + IMPORT OnHijackInteriorPointerWorker + IMPORT OnHijackScalarWorker +#endif ;FEATURE_HIJACK + + IMPORT GetCurrentSavedRedirectContext + +#ifdef FEATURE_MIXEDMODE + SETALIAS IJWNOADThunk__FindThunkTarget, ?FindThunkTarget@IJWNOADThunk@@QAAPBXXZ + IMPORT $IJWNOADThunk__FindThunkTarget +#endif + + ;; Imports to support virtual import fixup for ngen images + IMPORT VirtualMethodFixupWorker + ;; Import to support cross-moodule external method invocation in ngen images + IMPORT ExternalMethodFixupWorker + IMPORT StubDispatchFixupWorker + +#ifdef FEATURE_READYTORUN + IMPORT DynamicHelperWorker +#endif + + IMPORT JIT_RareDisableHelperWorker + IMPORT DoJITFailFast + IMPORT s_gsCookie + IMPORT g_TrapReturningThreads + + ;; Imports for singleDomain statics helpers + IMPORT JIT_GetSharedNonGCStaticBase_Helper + IMPORT JIT_GetSharedGCStaticBase_Helper + + TEXTAREA + +;; LPVOID __stdcall GetCurrentIP(void); + LEAF_ENTRY GetCurrentIP + mov r0, lr + bx lr + LEAF_END + +;; LPVOID __stdcall GetCurrentSP(void); + LEAF_ENTRY GetCurrentSP + mov r0, sp + bx lr + LEAF_END + +;;----------------------------------------------------------------------------- +;; This helper routine enregisters the appropriate arguments and makes the +;; actual call. +;;----------------------------------------------------------------------------- +;;void CallDescrWorkerInternal(CallDescrData * pCallDescrData); + NESTED_ENTRY CallDescrWorkerInternal,,CallDescrWorkerUnwindFrameChainHandler + PROLOG_PUSH {r4,r5,r7,lr} + PROLOG_STACK_SAVE r7 + + mov r5,r0 ; save pCallDescrData in r5 + + ldr r1, [r5,#CallDescrData__numStackSlots] + cbz r1, Ldonestack + + ;; Add frame padding to ensure frame size is a multiple of 8 (a requirement of the OS ABI). + ;; We push four registers (above) and numStackSlots arguments (below). If this comes to an odd number + ;; of slots we must pad with another. This simplifies to "if the low bit of numStackSlots is set, + ;; extend the stack another four bytes". + lsls r2, r1, #2 + and r3, r2, #4 + sub sp, sp, r3 + + ;; This loop copies numStackSlots words + ;; from [pSrcEnd-4,pSrcEnd-8,...] to [sp-4,sp-8,...] + ldr r0, [r5,#CallDescrData__pSrc] + add r0,r0,r2 +Lstackloop + ldr r2, [r0,#-4]! + str r2, [sp,#-4]! + subs r1, r1, #1 + bne Lstackloop +Ldonestack + + ;; If FP arguments are supplied in registers (r3 != NULL) then initialize all of them from the pointer + ;; given in r3. Do not use "it" since it faults in floating point even when the instruction is not executed. + ldr r3, [r5,#CallDescrData__pFloatArgumentRegisters] + cbz r3, LNoFloatingPoint + vldm r3, {s0-s15} +LNoFloatingPoint + + ;; Copy [pArgumentRegisters, ..., pArgumentRegisters + 12] + ;; into r0, ..., r3 + + ldr r4, [r5,#CallDescrData__pArgumentRegisters] + ldm r4, {r0-r3} + + CHECK_STACK_ALIGNMENT + + ;; call pTarget + ;; Note that remoting expect target in r4. + ldr r4, [r5,#CallDescrData__pTarget] + blx r4 + + ldr r3, [r5,#CallDescrData__fpReturnSize] + + ;; Save FP return value if appropriate + cbz r3, LFloatingPointReturnDone + + ;; Float return case + ;; Do not use "it" since it faults in floating point even when the instruction is not executed. + cmp r3, #4 + bne LNoFloatReturn + vmov r0, s0 + b LFloatingPointReturnDone +LNoFloatReturn + + ;; Double return case + ;; Do not use "it" since it faults in floating point even when the instruction is not executed. + cmp r3, #8 + bne LNoDoubleReturn + vmov r0, r1, s0, s1 + b LFloatingPointReturnDone +LNoDoubleReturn + + add r2, r5, #CallDescrData__returnValue + + cmp r3, #16 + bne LNoFloatHFAReturn + vstm r2, {s0-s3} + b LReturnDone +LNoFloatHFAReturn + + cmp r3, #32 + bne LNoDoubleHFAReturn + vstm r2, {d0-d3} + b LReturnDone +LNoDoubleHFAReturn + + EMIT_BREAKPOINT ; Unreachable + +LFloatingPointReturnDone + + ;; Save return value into retbuf + str r0, [r5, #(CallDescrData__returnValue + 0)] + str r1, [r5, #(CallDescrData__returnValue + 4)] + +LReturnDone + +#ifdef _DEBUG + ;; trash the floating point registers to ensure that the HFA return values + ;; won't survive by accident + vldm sp, {d0-d3} +#endif + + EPILOG_STACK_RESTORE r7 + EPILOG_POP {r4,r5,r7,pc} + + NESTED_END + + +;;----------------------------------------------------------------------------- +;; This helper routine is where returns for irregular tail calls end up +:: so they can dynamically pop their stack arguments. +;;----------------------------------------------------------------------------- +; +; Stack Layout (stack grows up, 0 at the top, offsets relative to frame pointer, r7): +; +; sp -> callee stack arguments +; : +; : +; -0Ch gsCookie +; TailCallHelperFrame -> +; -08h __VFN_table +; -04h m_Next +; r7 -> +; +00h m_calleeSavedRgisters.r4 +; +04h .r5 +; +08h .r6 +; +0Ch .r7 +; +10h .r8 +; +14h .r9 +; +18h .r10 +; r11-> +; +1Ch .r11 +; +20h .r14 -or- m_ReturnAddress +; +; r6 -> GetThread() +; r5 -> r6->m_pFrame (old Frame chain head) +; r11 is used to preserve the ETW call stack + + NESTED_ENTRY TailCallHelperStub + ; + ; This prolog is never executed, but we keep it here for reference + ; and for the unwind data it generates + ; + + ; Spill callee saved registers and return address. + PROLOG_PUSH {r4-r11,lr} + + PROLOG_STACK_SAVE r7 + + ; + ; This is the code that would have to run to setup this frame + ; like the C++ helper does before calling RtlRestoreContext + ; + ; Allocate space for the rest of the frame and GSCookie. + ; PROLOG_STACK_ALLOC 0x0C + ; + ; Set r11 for frame chain + ;add r11, r7, 0x1C + ; + ; Set the vtable for TailCallFrame + ;bl TCF_GETMETHODFRAMEVPTR + ;str r0, [r7, #-8] + ; + ; Initialize the GSCookie within the Frame + ;ldr r0, =s_gsCookie + ;str r0, [r7, #-0x0C] + ; + ; Link the TailCallFrameinto the Frame chain + ; and initialize r5 & r6 for unlinking later + ;CALL_GETTHREAD + ;mov r6, r0 + ;ldr r5, [r6, #Thread__m_pFrame] + ;str r5, [r7, #-4] + ;sub r0, r7, 8 + ;str r0, [r6, #Thread__m_pFrame] + ; + ; None of the previous stuff is ever executed, + ; but we keep it here for reference + ; + + ; + ; Here's the pretend call (make it real so the unwinder + ; doesn't think we're in the prolog) + ; + bl TailCallHelperStub + ; + ; with the real return address pointing to this real epilog + ; +JIT_TailCallHelperStub_ReturnAddress + EXPORT JIT_TailCallHelperStub_ReturnAddress + + ; + ; Our epilog (which also unlinks the StubHelperFrame) + ; Be careful not to trash the return registers + ; + +#ifdef _DEBUG + ldr r3, =s_gsCookie + ldr r3, [r3] + ldr r2, [r7, #-0x0C] + cmp r2, r3 + beq GoodGSCookie + bl DoJITFailFast +GoodGSCookie +#endif ; _DEBUG + + ; + ; unlink the TailCallFrame + ; + str r5, [r6, #Thread__m_pFrame] + + ; + ; epilog + ; + EPILOG_STACK_RESTORE r7 + EPILOG_POP {r4-r11,lr} + EPILOG_RETURN + + NESTED_END + +; ------------------------------------------------------------------ + +; void LazyMachStateCaptureState(struct LazyMachState *pState); + LEAF_ENTRY LazyMachStateCaptureState + + ;; marks that this is not yet valid + mov r1, #0 + str r1, [r0, #MachState__isValid] + + str lr, [r0, #LazyMachState_captureIp] + str sp, [r0, #LazyMachState_captureSp] + + add r1, r0, #LazyMachState_captureR4_R11 + stm r1, {r4-r11} + + mov pc, lr + + LEAF_END + +; void SinglecastDelegateInvokeStub(Delegate *pThis) + LEAF_ENTRY SinglecastDelegateInvokeStub + cmp r0, #0 + beq LNullThis + + ldr r12, [r0, #DelegateObject___methodPtr] + ldr r0, [r0, #DelegateObject___target] + + bx r12 + +LNullThis + mov r0, #CORINFO_NullReferenceException_ASM + b JIT_InternalThrow + + LEAF_END + +; +; r12 = UMEntryThunk* +; + NESTED_ENTRY TheUMEntryPrestub,,UMEntryPrestubUnwindFrameChainHandler + + PROLOG_PUSH {r0-r4,lr} + PROLOG_VPUSH {d0-d7} + + CHECK_STACK_ALIGNMENT + + mov r0, r12 + bl TheUMEntryPrestubWorker + + ; Record real target address in r12. + mov r12, r0 + + ; Epilog + EPILOG_VPOP {d0-d7} + EPILOG_POP {r0-r4,lr} + EPILOG_BRANCH_REG r12 + + NESTED_END + +; +; r12 = UMEntryThunk* +; + NESTED_ENTRY UMThunkStub,,UMThunkStubUnwindFrameChainHandler + PROLOG_PUSH {r4,r5,r7,r11,lr} + PROLOG_PUSH {r0-r3,r12} + PROLOG_STACK_SAVE r7 + + GBLA UMThunkStub_HiddenArg ; offset of saved UMEntryThunk * + GBLA UMThunkStub_StackArgs ; offset of original stack args (total size of UMThunkStub frame) +UMThunkStub_HiddenArg SETA 4*4 +UMThunkStub_StackArgs SETA 10*4 + + CHECK_STACK_ALIGNMENT + + bl GetThread + cbz r0, UMThunkStub_DoThreadSetup + +UMThunkStub_HaveThread + mov r5, r0 ; r5 = Thread * + + ldr r2, =g_TrapReturningThreads + + mov r4, 1 + str r4, [r5, #Thread__m_fPreemptiveGCDisabled] + + ldr r3, [r2] + cbnz r3, UMThunkStub_DoTrapReturningThreads + +UMThunkStub_InCooperativeMode + ldr r12, [r7, #UMThunkStub_HiddenArg] + + ldr r0, [r5, #Thread__m_pDomain] + ldr r1, [r12, #UMEntryThunk__m_dwDomainId] + ldr r0, [r0, #AppDomain__m_dwId] + ldr r3, [r12, #UMEntryThunk__m_pUMThunkMarshInfo] + cmp r0, r1 + bne UMThunkStub_WrongAppDomain + + ldr r2, [r3, #UMThunkMarshInfo__m_cbActualArgSize] + cbz r2, UMThunkStub_ArgumentsSetup + + add r0, r7, #UMThunkStub_StackArgs ; Source pointer + add r0, r0, r2 + lsr r1, r2, #2 ; Count of stack slots to copy + + and r2, r2, #4 ; Align the stack + sub sp, sp, r2 + +UMThunkStub_StackLoop + ldr r2, [r0,#-4]! + str r2, [sp,#-4]! + subs r1, r1, #1 + bne UMThunkStub_StackLoop + +UMThunkStub_ArgumentsSetup + ldr r4, [r3, #UMThunkMarshInfo__m_pILStub] + + ; reload argument registers + ldm r7, {r0-r3} + + CHECK_STACK_ALIGNMENT + + blx r4 + +UMThunkStub_PostCall + mov r4, 0 + str r4, [r5, #Thread__m_fPreemptiveGCDisabled] + + EPILOG_STACK_RESTORE r7 + EPILOG_STACK_FREE 4 * 5 + EPILOG_POP {r4,r5,r7,r11,pc} + +UMThunkStub_DoThreadSetup + sub sp, #SIZEOF__FloatArgumentRegisters + vstm sp, {d0-d7} + bl CreateThreadBlockThrow + vldm sp, {d0-d7} + add sp, #SIZEOF__FloatArgumentRegisters + b UMThunkStub_HaveThread + +UMThunkStub_DoTrapReturningThreads + sub sp, #SIZEOF__FloatArgumentRegisters + vstm sp, {d0-d7} + mov r0, r5 ; Thread* pThread + ldr r1, [r7, #UMThunkStub_HiddenArg] ; UMEntryThunk* pUMEntry + bl UMThunkStubRareDisableWorker + vldm sp, {d0-d7} + add sp, #SIZEOF__FloatArgumentRegisters + b UMThunkStub_InCooperativeMode + +UMThunkStub_WrongAppDomain + sub sp, #SIZEOF__FloatArgumentRegisters + vstm sp, {d0-d7} + + ldr r0, [r7, #UMThunkStub_HiddenArg] ; UMEntryThunk* pUMEntry + mov r2, r7 ; void * pArgs + ; remaining arguments are unused + bl UM2MDoADCallBack + + ; Restore non-FP return value. + ldr r0, [r7, #0] + ldr r1, [r7, #4] + + ; Restore FP return value or HFA. + vldm sp, {d0-d3} + b UMThunkStub_PostCall + + NESTED_END + +; UM2MThunk_WrapperHelper(void *pThunkArgs, // r0 +; int cbStackArgs, // r1 (unused) +; void *pAddr, // r2 (unused) +; UMEntryThunk *pEntryThunk, // r3 +; Thread *pThread) // [sp, #0] + + NESTED_ENTRY UM2MThunk_WrapperHelper + + PROLOG_PUSH {r4-r7,r11,lr} + PROLOG_STACK_SAVE r7 + + CHECK_STACK_ALIGNMENT + + mov r12, r3 // r12 = UMEntryThunk * + + ; + ; Note that layout of the arguments is given by UMThunkStub frame + ; + mov r5, r0 // r5 = pArgs + + ldr r3, [r12, #UMEntryThunk__m_pUMThunkMarshInfo] + + ldr r2, [r3, #UMThunkMarshInfo__m_cbActualArgSize] + cbz r2, UM2MThunk_WrapperHelper_ArgumentsSetup + + add r0, r5, #UMThunkStub_StackArgs ; Source pointer + add r0, r0, r2 + lsr r1, r2, #2 ; Count of stack slots to copy + + and r2, r2, #4 ; Align the stack + sub sp, sp, r2 + +UM2MThunk_WrapperHelper_StackLoop + ldr r2, [r0,#-4]! + str r2, [sp,#-4]! + subs r1, r1, #1 + bne UM2MThunk_WrapperHelper_StackLoop + +UM2MThunk_WrapperHelper_ArgumentsSetup + ldr r4, [r3, #UMThunkMarshInfo__m_pILStub] + + ; reload floating point registers + sub r6, r5, #SIZEOF__FloatArgumentRegisters + vldm r6, {d0-d7} + + ; reload argument registers + ldm r5, {r0-r3} + + CHECK_STACK_ALIGNMENT + + blx r4 + + ; Save non-floating point return + str r0, [r5, #0] + str r1, [r5, #4] + + ; Save FP return value or HFA. + vstm r6, {d0-d3} + +#ifdef _DEBUG + ;; trash the floating point registers to ensure that the HFA return values + ;; won't survive by accident + vldm sp, {d0-d3} +#endif + + EPILOG_STACK_RESTORE r7 + EPILOG_POP {r4-r7,r11,pc} + + NESTED_END + +; ------------------------------------------------------------------ +; +; IJWNOADThunk::MakeCall +; +; On entry: +; r12 : IJWNOADThunk * +; +; On exit: +; Tail calls to real managed target +; + +#ifdef FEATURE_MIXEDMODE + NESTED_ENTRY IJWNOADThunk__MakeCall + + ; Can't pass C++ mangled names to NESTED_ENTRY and my attempts to use EQU to define an alternate name + ; for a symbol didn't work. Just define a label for the decorated name of the method and export it + ; manually. +|?MakeCall@IJWNOADThunk@@KAXXZ| + EXPORT |?MakeCall@IJWNOADThunk@@KAXXZ| + + PROLOG_PUSH {r0-r4,lr} + PROLOG_VPUSH {d0-d7} + + CHECK_STACK_ALIGNMENT + + mov r0, r12 ; IJWNOADThunk * is this pointer for IJWNOADThunk::FindThunkTarget + bl $IJWNOADThunk__FindThunkTarget + mov r12, r0 ; Returns real jump target in r0, save this in r12 + + EPILOG_VPOP {d0-d7} + EPILOG_POP {r0-r4,lr} + EPILOG_BRANCH_REG r12 + + NESTED_END +#endif + +; ------------------------------------------------------------------ + + NESTED_ENTRY ThePreStub + + PROLOG_WITH_TRANSITION_BLOCK + + add r0, sp, #__PWTB_TransitionBlock ; pTransitionBlock + mov r1, r12 ; pMethodDesc + + bl PreStubWorker + + mov r12, r0 + + EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + EPILOG_BRANCH_REG r12 + + NESTED_END + +; ------------------------------------------------------------------ +; This method does nothing. It's just a fixed function for the debugger to put a breakpoint on. + LEAF_ENTRY ThePreStubPatch + nop +ThePreStubPatchLabel + EXPORT ThePreStubPatchLabel + bx lr + LEAF_END + +; ------------------------------------------------------------------ +; The call in ndirect import precode points to this function. + NESTED_ENTRY NDirectImportThunk + + PROLOG_PUSH {r0-r4,lr} ; Spill general argument registers, return address and + ; arbitrary register to keep stack aligned + PROLOG_VPUSH {d0-d7} ; Spill floating point argument registers + + CHECK_STACK_ALIGNMENT + + mov r0, r12 + bl NDirectImportWorker + mov r12, r0 + + EPILOG_VPOP {d0-d7} + EPILOG_POP {r0-r4,lr} + + ; If we got back from NDirectImportWorker, the MD has been successfully + ; linked. Proceed to execute the original DLL call. + EPILOG_BRANCH_REG r12 + + NESTED_END + +; ------------------------------------------------------------------ +; The call in fixup precode initally points to this function. +; The pupose of this function is to load the MethodDesc and forward the call the prestub. + NESTED_ENTRY PrecodeFixupThunk + + ; r12 = FixupPrecode * + + PROLOG_PUSH {r0-r1} + + ; Inline computation done by FixupPrecode::GetMethodDesc() + ldrb r0, [r12, #3] ; m_PrecodeChunkIndex + ldrb r1, [r12, #2] ; m_MethodDescChunkIndex + + add r12,r12,r0,lsl #3 + add r0,r12,r0,lsl #2 + ldr r0, [r0,#8] + add r12,r0,r1,lsl #2 + + EPILOG_POP {r0-r1} + EPILOG_BRANCH ThePreStub + + NESTED_END + +; ------------------------------------------------------------------ +; void ResolveWorkerAsmStub(r0, r1, r2, r3, r4:IndirectionCellAndFlags, r12:DispatchToken) +; +; The stub dispatch thunk which transfers control to VSD_ResolveWorker. + NESTED_ENTRY ResolveWorkerAsmStub + + PROLOG_WITH_TRANSITION_BLOCK + + add r0, sp, #__PWTB_TransitionBlock ; pTransitionBlock + mov r2, r12 ; token + + ; indirection cell in r4 - should be consistent with REG_ARM_STUB_SPECIAL + bic r1, r4, #3 ; indirection cell + and r3, r4, #3 ; flags + + bl VSD_ResolveWorker + + mov r12, r0 + + EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + EPILOG_BRANCH_REG r12 + + NESTED_END + +; ------------------------------------------------------------------ +; void ResolveWorkerChainLookupAsmStub(r0, r1, r2, r3, r4:IndirectionCellAndFlags, r12:DispatchToken) + NESTED_ENTRY ResolveWorkerChainLookupAsmStub + + ; ARMSTUB TODO: implement chained lookup + b ResolveWorkerAsmStub + + NESTED_END + +#if defined(FEATURE_REMOTING) || defined(FEATURE_COMINTEROP) + +; ------------------------------------------------------------------ +; setStubReturnValue +; r0 - size of floating point return value (MetaSig::GetFPReturnSize()) +; r1 - pointer to the return buffer in the stub frame + LEAF_ENTRY setStubReturnValue + + cbz r0, NoFloatingPointRetVal + + ;; Float return case + ;; Do not use "it" since it faults in floating point even when the instruction is not executed. + cmp r0, #4 + bne LNoFloatRetVal + vldr s0, [r1] + bx lr +LNoFloatRetVal + + ;; Double return case + ;; Do not use "it" since it faults in floating point even when the instruction is not executed. + cmp r0, #8 + bne LNoDoubleRetVal + vldr d0, [r1] + bx lr +LNoDoubleRetVal + + cmp r0, #16 + bne LNoFloatHFARetVal + vldm r1, {s0-s3} + bx lr +LNoFloatHFARetVal + + cmp r0, #32 + bne LNoDoubleHFARetVal + vldm r1, {d0-d3} + bx lr +LNoDoubleHFARetVal + + EMIT_BREAKPOINT ; Unreachable + +NoFloatingPointRetVal + + ;; Restore the return value from retbuf + ldr r0, [r1] + ldr r1, [r1, #4] + bx lr + + LEAF_END + +#endif // FEATURE_REMOTING || FEATURE_COMINTEROP + +#ifdef FEATURE_REMOTING + +; ------------------------------------------------------------------ +; Remoting stub used to dispatch a method invocation. This is the choke point for all remoting calls; all +; scenarios where we determine we're not a local or a COM call, regardless of whether the dispatch is +; interface, virtual or direct will wind up here sooner or later. +; +; On entry: +; r0 : transparent proxy +; r12 : target MethodDesc or slot number +; plus user arguments in registers and on the stack +; + NESTED_ENTRY TransparentProxyStub_CrossContext + + PROLOG_WITH_TRANSITION_BLOCK 0x20 + + add r0, sp, #__PWTB_TransitionBlock ; pTransitionBlock + mov r1, r12 ; pMethodDesc + + bl TransparentProxyStubWorker + + ; r0 = fpRetSize + + ; return value is stored before float argument registers + add r1, sp, #(__PWTB_FloatArgumentRegisters - 0x20) + bl setStubReturnValue + + EPILOG_WITH_TRANSITION_BLOCK_RETURN + + NESTED_END + +; ------------------------------------------------------------------ +; This method does nothing. It's just a fixed function for the debugger to put a breakpoint on. + LEAF_ENTRY TransparentProxyStubPatch + add r0, r1, r2 +TransparentProxyStubPatchLabel + EXPORT TransparentProxyStubPatchLabel + bx lr + LEAF_END + +; ------------------------------------------------------------------ +; VSD helper for performing an in-context interface dispatch on a TransparentProxy. This only happens for +; ContextBoundObjects that are invoked in the correct context, never for general remoting. +; +; On entry: +; r0 : transparent proxy +; r12 : interface MethodDesc +; plus user arguments in registers and on the stack +; +; On exit: +; Tail calls to actual target which returns as normal to the caller. +; + NESTED_ENTRY InContextTPQuickDispatchAsmStub + + ; Spill caller's volatile argument registers and some other state we wish to preserve. + PROLOG_PUSH {r0-r3,r12,lr} + PROLOG_VPUSH {d0-d7} + + CHECK_STACK_ALIGNMENT + + ; Set up arguments for VSD_GetTargetForTPWorkerQuick + ; mov r0, r0 ; this + mov r1, r12 ; Interface MethodDesc + + bl VSD_GetTargetForTPWorkerQuick + + ; If we didn't find a target head for the slow path. + cbz r0, CacheMiss + + ; Save target address since we're about to restore the value of r0. Can't place it directly into r12 + ; since that's about to be restored as well. Instead we overwrite the saved version of r12 on the + ; stack (we don't need it any more since the lookup succeeded). + str r0, [sp, #((16 * 4) + (4 * 4))] + + ; Restore caller's argument registers. + EPILOG_VPOP {d0-d7} + EPILOG_POP {r0-r3,r12,lr} + + ; Tail call to the real code using the previously computed target address. + EPILOG_BRANCH_REG r12 + +CacheMiss + ; Restore caller's argument registers. + EPILOG_VPOP {d0-d7} + EPILOG_POP {r0-r3,r12,lr} + + EPILOG_BRANCH InContextTPDispatchAsmStub + + NESTED_END + +; ------------------------------------------------------------------ + + NESTED_ENTRY InContextTPDispatchAsmStub + + PROLOG_WITH_TRANSITION_BLOCK + + add r0, sp, #__PWTB_TransitionBlock ; pTransitionBlock + mov r1, r12 ; pMethodDesc / token + + bl VSD_GetTargetForTPWorker + + mov r12, r0 + + EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + EPILOG_BRANCH_REG r12 + + NESTED_END + +; ------------------------------------------------------------------ +; Macro used to compare a MethodTable with that of __TransparentProxy. Sets the Z condition flag to indicate +; the result (Z=1 for a match, Z=0 for a mismatch). +; + MACRO + TP_TYPE_CHECK $methodTableReg, $scratchReg + + ldr $scratchReg, =$CTPMethodTable__s_pThunkTable + ldr $scratchReg, [$scratchReg] + cmp $scratchReg, $methodTableReg + MEND + +; ------------------------------------------------------------------ +; Macro used to perform a context check. +; +; Calls a user customizable routine that determines whether the current execution context warrants a context +; transition for the call. Regular remoting (as opposed to context transitioning based on ContextBoundObjects) +; always returns a context-mismatch from this call. +; +; On entry: +; r0 : this (TranparentProxy object) +; +; On exit: +; r0 : check result (0 == contexts match, non-zero == contexts mismatch) +; r1-r3,r12,lr: trashed +; + MACRO + TP_CONTEXT_CHECK + + ldr r1, [r0, #TransparentProxyObject___stub] + ldr r0, [r0, #TransparentProxyObject___stubData] + blx r1 + MEND + +; ------------------------------------------------------------------ +; Used by the remoting precode for non-virtual dispatch to instance methods which might be remoted. Performs a +; context and transparent proxy check and if both of these are negative (or the call has been made on a null +; 'this') we simply return and the precode will dispatch the call locally as normal. Otherwise we redirect to +; the remoting system and never return. +; +; On entry: +; r0 : this (may or may not be a TransparentProxy) +; r1 : trashed +; lr : return address into RemotingPrecode (RemotingPrecode* + REMOTING_PRECODE_RET_OFFSET) +; [sp, #0] : caller's saved r1 +; [sp, #4] : caller's saved lr (i.e. return address into caller of RemotingPrecode) +; plus user arguments in registers and on the stack +; + LEAF_ENTRY PrecodeRemotingThunk + + ; Send null 'this' case to local dispatch case (else we'd need to handle an A/V from this stub). + cbz r0, LocalDispatch ; predicted not taken + + ; Load MethodTable* in r12. + ldr r12, [r0] + + ; Compare MethodTable in 'this' with that of __TransparentProxy; if they're not equal we dispatch + ; locally. + TP_TYPE_CHECK r12, r1 ; r1 is a scratch register + beq TransparentProxyDispatch ; predicted not taken + +LocalDispatch + ; Recover target MethodDesc pointer from the RemotingPrecode (we have the address of this + + ; REMOTING_PRECODE_RET_OFFSET in lr). Subtract extra 1 to account for the low-bit being set in LR to + ; indicate thumb mode. + ; We do this here because even the local case needs r12 initialized. + ldr r12, [lr, #(RemotingPrecode__m_pMethodDesc - REMOTING_PRECODE_RET_OFFSET - 1)] + + bx lr + + LEAF_END + +; ------------------------------------------------------------------ +; Handles the atypical path for the remoting precode above (typically the non-local dispatch cases). The +; regular entry point defined by NESTED_ENTRY below is never called directly; it serves only to generate +; prolog unwind data matching the pushes of the caller's r1 and lr done in the remoting precode so we can +; unwind out of this frame. The real entry point is TransparentProxyDispatch called directly from +; PrecodeRemotingThunk. +; + NESTED_ENTRY TransparentProxyDispatch_FakeProlog + + ; Match what the remoting precode has pushed. + PROLOG_PUSH {r1,lr} + + ; This is where execution really starts. +TransparentProxyDispatch + + ; We need some temporary registers and to preserve lr. + PROLOG_PUSH {r0,r2-r5,lr} + + CHECK_STACK_ALIGNMENT + + ; Recover target MethodDesc pointer from the RemotingPrecode (we have the address of this + + ; REMOTING_PRECODE_RET_OFFSET in lr). Subtract extra 1 to account for the low-bit being set in LR to + ; indicate thumb mode. Stash the result in a non-volatile register to preserve it over the call to + ; TP_CONTEXT_CHECK below. + ldr r4, [lr, #(RemotingPrecode__m_pMethodDesc - REMOTING_PRECODE_RET_OFFSET - 1)] + + ; Check whether the TP is already in the correct context. This can happen for ContextBoundObjects + ; only. The following macro will trash volatile registers and lr and return the result in r0 (0 == + ; context match, non-zero for everything else). All other registers are preserved. + TP_CONTEXT_CHECK + + ; Place MethodDesc* in r12 ready for wherever we dispatch to next. + mov r12, r4 + + ; Check the result of TP_CONTEXT_CHECK + cbnz r0, ContextMismatch1 + + ; At this point we know we're being called on a transparent proxy but the source and destination + ; contexts match. This only happens for a ContextBoundObject. For an non-interface dispatch we can + ; just return to the local dispatch case; the precode will eventually redirect to the jitted code + ; which knows how to handle a TP-wrapped ContextBoundObject. For interface calls we need to hand off + ; to VSD so it can resolve to the real target method. The quickest way to determine which of these + ; cases we need is to look at the classification of the method desc. All interface methods for which a + ; remoting precode is used are marked as mcComInterop, which though non-intuitive is generally OK + ; since only COM interop and remoting can dispatch directly on an interface method desc. (Generic + ; interface methods are not classified as mcComInterop but we use a different mechanism to intercept + ; those). + ldrh r0, [r4, #MethodDesc__m_wFlags] + and r0, #MethodDesc__mdcClassification + cmp r0, #MethodDesc__mcComInterop + bne LocalDispatch1 + + ; Local interface dispatch case. Restore argument registers saved here and in the RemotingPrecode, + ; discard return address into the RemotingPrecode (we're not going back there) and restore the real + ; caller's return address to LR before tail calling into the interface dispatch helper. + EPILOG_POP {r0,r2-r5,lr} ; Restore arg registers saved by this routine and RemotingPrecode lr + EPILOG_POP {r1,lr} ; Restore r1 saved by RemotingPrecode and real return address + EPILOG_BRANCH InContextTPQuickDispatchAsmStub + +LocalDispatch1 + + ; Local dispatch case. Restore argument registers saved here and return to the remoting precode. + EPILOG_POP {r0,r2-r5,pc} + +ContextMismatch1 + ; Context-mismatch (remoted) dispatch case. Restore argument registers saved here and in the + ; RemotingPrecode, discard return address into the RemotingPrecode (we're not going back there) and + ; restore the real caller's return address to LR before tail calling into the cross-context helper. + EPILOG_POP {r0,r2-r5,lr} ; Restore arg registers saved by this routine and RemotingPrecode lr + EPILOG_POP {r1,lr} ; Restore r1 saved by RemotingPrecode and real return address + EPILOG_BRANCH TransparentProxyStub_CrossContext + + NESTED_END + +; ------------------------------------------------------------------ +; Used to dispatch an interface call that is possibly be cross-context or remoted. Normally this is handled +; by the remoting precode stub above but there is an edge case for generic interface methods that falls +; through the cracks (it is not easy to cover since the precode stub makes use of it as a quick means +; to differentiate between interface and non-interface calls in the non-cross context case). +; +; On entry: +; r0 : this (TransparentProxy object) +; r12 : interface MethodDesc +; plus user arguments in registers and on the stack +; +; On exit: +; Tail calls to the VSD in-context TP dispatcher or remoting system as appropriate. +; + NESTED_ENTRY CRemotingServices__DispatchInterfaceCall + + PROLOG_PUSH {r0-r3,r12,lr} + + CHECK_STACK_ALIGNMENT + + ; Check whether the TP is already in the correct context. This can happen for ContextBoundObjects + ; only. The following macro will trash volatile registers and lr and return the result in r0 (0 == + ; context match, non-zero for everything else). All other registers are preserved. + TP_CONTEXT_CHECK + cbnz r0, ContextMismatch2 + + ; Local interface dispatch case. Tail call to VSD helper specifically for the in-context TP dispatch + ; scenario. Interface MethodDesc is restored to r12. + EPILOG_POP {r0-r3,r12,lr} + EPILOG_BRANCH InContextTPQuickDispatchAsmStub + +ContextMismatch2 + ; Context-mismatch (remoted) dispatch case. Tail call to the general remoting dispatch code. Interface + ; MethodDesc is restored to r12. + EPILOG_POP {r0-r3,r12,lr} + EPILOG_BRANCH TransparentProxyStub_CrossContext + + NESTED_END + +; ------------------------------------------------------------------ +; Common stub used for vtable dispatch of remoted methods. A small prestub will load the vtable slot index +; into r12 and then jump here. This stub determines whether we're already in the correct context (which can +; only happen for ContextBoundObjects). Depending on the answers we'll either dispatch the call locally or +; re-direct it to the remoting system (via TransparentProxyStub_CrossContext). +; +; On entry: +; r0 : this (TransparentProxy object) +; r12 : virtual method slot number +; plus user arguments in registers and on the stack +; +; On exit: +; Tail calls to the VSD in-context TP dispatcher or remoting system as appropriate. +; + NESTED_ENTRY TransparentProxyStub + + PROLOG_PUSH {r0-r3,r12,lr} + + CHECK_STACK_ALIGNMENT + + ; Check whether the TP is already in the correct context. This can happen for ContextBoundObjects + ; only. The following macro will trash volatile registers and lr and return the result in r0 (0 == + ; context match, non-zero for everything else). All other registers are preserved. + TP_CONTEXT_CHECK + cbnz r0, ContextMismatch3 + + ; We need to perform a local vtable dispatch on the ContextBoundObject. Obviously this needs to be on + ; the real type held in the proxy, not TransparentProxy's MethodTable or we'll just end up back here + ; recursively. + + ; Recover 'this' pointer and slot number. + ldr r0, [sp] + ldr r12, [sp, #0x10] + + ; Extract real type from the TP. + ldr r0, [r0, #TransparentProxyObject___pMT] + + ; Vtables are no longer a linear array. Instead they use a two-level indirection with the first level + ; consisting of fixed sized chunks of function pointer arrays. R12 has our slot number. + + ; Calculate first level chunk index. + lsr r1, r12, #ASM__VTABLE_SLOTS_PER_CHUNK_LOG2 + + ; Load the address of the chunk from the MethodTable (the chunk table immediately follows the + ; MethodTable structure). + add r0, #SIZEOF__MethodTable + ldr r2, [r0, r1, lsl #2] + + ; Calculate the slot index within the chunk. + and r0, r12, #(ASM__VTABLE_SLOTS_PER_CHUNK - 1) + + ; Load the target address into r12 (we no longer need the slot number and we're about to restore the + ; other registers). + ldr r12, [r2, r0, lsl #2] + + ; Restore the stack state and tail call to the local target. + EPILOG_POP {r0-r3} + EPILOG_STACK_FREE 4 ; Skip restore of r12 since we've overwritten it + EPILOG_POP {lr} + EPILOG_BRANCH_REG r12 + +ContextMismatch3 + ; Contexts don't match so we have to dispatch through remoting. Clean up the stack and tail call to + ; the helper. + EPILOG_POP {r0-r3,r12,lr} + EPILOG_BRANCH TransparentProxyStub_CrossContext + + NESTED_END + +#endif // FEATURE_REMOTING +#if defined(FEATURE_REMOTING) || defined(FEATURE_COMINTEROP) +; ------------------------------------------------------------------ +; Function used by remoting/COM interop to get floating point return value (since it's not in the same +; register(s) as non-floating point values). +; +; On entry; +; r0 : size of the FP result (4 or 8 bytes) +; r1 : pointer to 64-bit buffer to receive result +; +; On exit: +; buffer pointed to by r1 on entry contains the float or double argument as appropriate +; + LEAF_ENTRY getFPReturn + + cmp r0, #4 + bne LgetFP8 + vmov r2, s0 + str r2, [r1] + bx lr +LgetFP8 + vmov r2, r3, d0 + strd r2, r3, [r1] + bx lr + + LEAF_END + +; ------------------------------------------------------------------ +; Function used by remoting/COM interop to set floating point return value (since it's not in the same +; register(s) as non-floating point values). +; +; On entry: +; r0 : size of the FP result (4 or 8 bytes) +; r2/r3 : 32-bit or 64-bit FP result +; +; On exit: +; s0 : float result if r0 == 4 +; d0 : double result if r0 == 8 +; + LEAF_ENTRY setFPReturn + + cmp r0, #4 + bne LsetFP8 + vmov s0, r2 + bx lr +LsetFP8 + vmov d0, r2, r3 + bx lr + + LEAF_END + +#endif defined(FEATURE_REMOTING) || defined(FEATURE_COMINTEROP) +#ifdef FEATURE_REMOTING + +; ------------------------------------------------------------------ +; Tail call Object.FieldGetter remotely with the given arguments. +; +; On entry: +; r0 : pMD (MethodDesc * of the Object.FieldGetter method) +; r1 : pThis (the transparent proxy) +; r2 : pFirst +; r3 : pSecond +; [sp, #0] : pThird +; +; On exit: +; Tail calls to the managed method +; + LEAF_ENTRY CRemotingServices__CallFieldGetter + + mov r12, r0 + mov r0, r1 + mov r1, r2 + mov r2, r3 + ldr r3, [sp, #0] + + b TransparentProxyStub_CrossContext + + LEAF_END + +; ------------------------------------------------------------------ +; Tail call Object.FieldSetter remotely with the given arguments. +; +; On entry: +; r0 : pMD (MethodDesc * of the Object.FieldSetter method) +; r1 : pThis (the transparent proxy) +; r2 : pFirst +; r3 : pSecond +; [sp, #0] : pThird +; +; On exit: +; Tail calls to the managed method +; + LEAF_ENTRY CRemotingServices__CallFieldSetter + + mov r12, r0 + mov r0, r1 + mov r1, r2 + mov r2, r3 + ldr r3, [sp, #0] + + b TransparentProxyStub_CrossContext + + LEAF_END + +; ------------------------------------------------------------------ +; General purpose remoting helper used to call given target with two parameters. +; +; On entry: +; r0 : pTarget +; r1 : pFirst +; r2 : pSecond +; +; + NESTED_ENTRY CTPMethodTable__CallTargetHelper2,,CallDescrWorkerUnwindFrameChainHandler + + PROLOG_PUSH {r11, lr} + + mov r12, r0 + mov r0, r1 + mov r1, r2 + + blx r12 + + ; Adding a nop so that unwind does not result in the IP being in epilog. + ; This ensures that the OS unwinder looks up the personality routine for this method. + nop + + EPILOG_POP {r11, pc} + + NESTED_END + +; ------------------------------------------------------------------ +; General purpose remoting helper used to call given target with three parameters. +; +; On entry: +; r0 : pTarget +; r1 : pFirst +; r2 : pSecond +; r3 : pThird +; +; + NESTED_ENTRY CTPMethodTable__CallTargetHelper3,,CallDescrWorkerUnwindFrameChainHandler + + PROLOG_PUSH {r11, lr} + + mov r12, r0 + mov r0, r1 + mov r1, r2 + mov r2, r3 + + blx r12 + + ; Adding a nop so that unwind does not result in the IP being in epilog. + ; This ensures that the OS unwinder looks up the personality routine for this method. + nop + + EPILOG_POP {r11, pc} + + NESTED_END + +#endif // FEATURE_REMOTING + +#ifdef FEATURE_COMINTEROP +; ------------------------------------------------------------------ +; GenericComPlusCallStub that erects a ComPlusMethodFrame and calls into the runtime +; (CLRToCOMWorker) to dispatch rare cases of the interface call. +; +; On entry: +; r0 : 'this' object +; r12 : Interface MethodDesc* +; plus user arguments in registers and on the stack +; +; On exit: +; r0/r1/s0/d0 set to return value of the call as appropriate +; + NESTED_ENTRY GenericComPlusCallStub + + PROLOG_WITH_TRANSITION_BLOCK 0x20 + + add r0, sp, #__PWTB_TransitionBlock ; pTransitionBlock + mov r1, r12 ; pMethodDesc + + ; Call CLRToCOMWorker(pFrame). This call will set up the rest of the frame (including the vfptr, + ; the GS cookie and linking to the thread), make the client call and return with correct registers set + ; (r0/r1/s0-s3/d0-d3 as appropriate). + + bl CLRToCOMWorker + + ; r0 = fpRetSize + + ; return value is stored before float argument registers + add r1, sp, #(__PWTB_FloatArgumentRegisters - 0x20) + bl setStubReturnValue + + EPILOG_WITH_TRANSITION_BLOCK_RETURN + + NESTED_END + +; ------------------------------------------------------------------ +; COM to CLR stub called the first time a particular method is invoked. +; +; On entry: +; r12 : (MethodDesc* - ComCallMethodDesc_Offset_FromR12) provided by prepad thunk +; plus user arguments in registers and on the stack +; +; On exit: +; tail calls to real method +; + NESTED_ENTRY ComCallPreStub + + GBLA ComCallPreStub_FrameSize + GBLA ComCallPreStub_FramePad + GBLA ComCallPreStub_StackAlloc + GBLA ComCallPreStub_Frame + GBLA ComCallPreStub_ErrorReturn + +; Set the defaults +ComCallPreStub_FramePad SETA 8 ; error return +ComCallPreStub_FrameSize SETA (ComCallPreStub_FramePad + SIZEOF__GSCookie + SIZEOF__ComMethodFrame) + + IF ComCallPreStub_FrameSize:MOD:8 != 0 +ComCallPreStub_FramePad SETA ComCallPreStub_FramePad + 4 +ComCallPreStub_FrameSize SETA ComCallPreStub_FrameSize + 4 + ENDIF + +ComCallPreStub_StackAlloc SETA ComCallPreStub_FrameSize - SIZEOF__ArgumentRegisters - 2 * 4 +ComCallPreStub_Frame SETA SIZEOF__FloatArgumentRegisters + ComCallPreStub_FramePad + SIZEOF__GSCookie +ComCallPreStub_ErrorReturn SETA SIZEOF__FloatArgumentRegisters + + PROLOG_PUSH {r0-r3} ; Spill general argument registers + PROLOG_PUSH {r11,lr} ; Save return address + PROLOG_STACK_ALLOC ComCallPreStub_StackAlloc ; Alloc non-spill portion of stack frame + PROLOG_VPUSH {d0-d7} ; Spill floating point argument registers + + CHECK_STACK_ALIGNMENT + + ; Finish initializing the frame. The C++ helper will fill in the GS cookie and vfptr and link us to + ; the Thread frame chain (see ComPrestubMethodFrame::Push). That leaves us with m_pFuncDesc. + ; The prepad thunk passes us a value which is the MethodDesc* - ComCallMethodDesc_Offset_FromR12 (due to encoding limitations in the + ; thunk). So we must correct this by adding 4 before storing the pointer. + add r12, #(ComCallMethodDesc_Offset_FromR12) + str r12, [sp, #(ComCallPreStub_Frame + UnmanagedToManagedFrame__m_pvDatum)] + + ; Call the C++ worker: ComPreStubWorker(&Frame) + add r0, sp, #(ComCallPreStub_Frame) + add r1, sp, #(ComCallPreStub_ErrorReturn) + bl ComPreStubWorker + + ; Handle failure case. + cbz r0, ErrorExit + + ; Stash real target address where it won't be overwritten by restoring the calling state. + mov r12, r0 + + EPILOG_VPOP {d0-d7} ; Restore floating point argument registers + EPILOG_STACK_FREE ComCallPreStub_StackAlloc + EPILOG_POP {r11,lr} + EPILOG_POP {r0-r3} ; Restore argument registers + ; Tail call the real target. Actually ComPreStubWorker returns the address of the prepad thunk on ARM, + ; that way we don't run out of volatile registers trying to remember both the new target address and + ; the hidden MethodDesc* argument. ComPreStubWorker patched the prepad though so the second time + ; through we won't end up here again. + EPILOG_BRANCH_REG r12 + +ErrorExit + ; Failed to find a stub to call. Retrieve the return value ComPreStubWorker set for us. + ldr r0, [sp, #(ComCallPreStub_ErrorReturn)] + ldr r1, [sp, #(ComCallPreStub_ErrorReturn+4)] + EPILOG_STACK_FREE ComCallPreStub_StackAlloc + SIZEOF__FloatArgumentRegisters + EPILOG_POP {r11,lr} + EPILOG_STACK_FREE SIZEOF__ArgumentRegisters + EPILOG_RETURN + + NESTED_END + +; ------------------------------------------------------------------ +; COM to CLR stub which sets up a ComMethodFrame and calls COMToCLRWorker. +; +; On entry: +; r12 : (MethodDesc* - ComCallMethodDesc_Offset_FromR12) provided by prepad thunk +; plus user arguments in registers and on the stack +; +; On exit: +; Result in r0/r1/s0/d0 as per the real method being called +; + NESTED_ENTRY GenericComCallStub,,ReverseComUnwindFrameChainHandler + +; Calculate space needed on stack for alignment padding, a GS cookie and a ComMethodFrame (minus the last +; field, m_ReturnAddress, which we'll push explicitly). + + GBLA GenericComCallStub_FrameSize + GBLA GenericComCallStub_FramePad + GBLA GenericComCallStub_StackAlloc + GBLA GenericComCallStub_Frame + +; Set the defaults +GenericComCallStub_FramePad SETA 0 +GenericComCallStub_FrameSize SETA (GenericComCallStub_FramePad + SIZEOF__GSCookie + SIZEOF__ComMethodFrame) + + IF GenericComCallStub_FrameSize:MOD:8 != 0 +GenericComCallStub_FramePad SETA 4 +GenericComCallStub_FrameSize SETA GenericComCallStub_FrameSize + GenericComCallStub_FramePad + ENDIF + +GenericComCallStub_StackAlloc SETA GenericComCallStub_FrameSize - SIZEOF__ArgumentRegisters - 2 * 4 +GenericComCallStub_Frame SETA SIZEOF__FloatArgumentRegisters + GenericComCallStub_FramePad + SIZEOF__GSCookie + + PROLOG_PUSH {r0-r3} ; Spill general argument registers + PROLOG_PUSH {r11,lr} ; Save return address + PROLOG_STACK_ALLOC GenericComCallStub_StackAlloc ; Alloc non-spill portion of stack frame + PROLOG_VPUSH {d0-d7} ; Spill floating point argument registers + + CHECK_STACK_ALIGNMENT + + ; Store MethodDesc* in frame. Due to a limitation of the prepad, r12 actually contains a value + ; "ComCallMethodDesc_Offset_FromR12" less than the pointer we want, so fix that up. + add r12, r12, #(ComCallMethodDesc_Offset_FromR12) + str r12, [sp, #(GenericComCallStub_Frame + UnmanagedToManagedFrame__m_pvDatum)] + + ; Call COMToCLRWorker(pThread, pFrame). Note that pThread is computed inside the method so we don't + ; need to set it up here. + ; + ; Setup R1 to point to the start of the explicit frame. We account for alignment padding and + ; space for GSCookie. + add r1, sp, #(GenericComCallStub_Frame) + bl COMToCLRWorker + + EPILOG_STACK_FREE GenericComCallStub_StackAlloc + SIZEOF__FloatArgumentRegisters + EPILOG_POP {r11,lr} + EPILOG_STACK_FREE SIZEOF__ArgumentRegisters + EPILOG_RETURN + + NESTED_END + +; ------------------------------------------------------------------ +; COM to CLR stub called from COMToCLRWorker that actually dispatches to the real managed method. +; +; On entry: +; r0 : dwStackSlots, count of argument stack slots to copy +; r1 : pFrame, ComMethodFrame pushed by GenericComCallStub above +; r2 : pTarget, address of code to call +; r3 : pSecretArg, hidden argument passed to target above in r12 +; [sp, #0] : pDangerousThis, managed 'this' reference +; +; On exit: +; Result in r0/r1/s0/d0 as per the real method being called +; + NESTED_ENTRY COMToCLRDispatchHelper,,CallDescrWorkerUnwindFrameChainHandler + + PROLOG_PUSH {r4-r5,r7,lr} + PROLOG_STACK_SAVE r7 + + ; Copy stack-based arguments. Make sure the eventual SP ends up 8-byte aligned. Note that the + ; following calculations assume that the prolog has left the stack already aligned. + CHECK_STACK_ALIGNMENT + + cbz r0, COMToCLRDispatchHelper_ArgumentsSetup + + lsl r4, r0, #2 ; r4 = (dwStackSlots * 4) + and r5, r4, #4 ; Align the stack + sub sp, sp, r5 + + add r5, r1, #SIZEOF__ComMethodFrame + add r5, r5, r4 + +COMToCLRDispatchHelper_StackLoop + ldr r4, [r5,#-4]! + str r4, [sp,#-4]! + subs r0, r0, #1 + bne COMToCLRDispatchHelper_StackLoop + + CHECK_STACK_ALIGNMENT + +COMToCLRDispatchHelper_ArgumentsSetup + ; Load floating point argument registers. + sub r4, r1, #(GenericComCallStub_Frame) + vldm r4, {d0-d7} + + ; Prepare the call target and hidden argument prior to overwriting r0-r3. + mov r12, r3 ; r12 = hidden argument + mov lr, r2 ; lr = target code + + ; Load general argument registers except r0. + add r4, r1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters + 4) + ldm r4, {r1-r3} + + ; Load r0 from the managed this, not the original incoming IUnknown*. + ldr r0, [r7, #(4 * 4)] + + ; Make the call. + blx lr + + EPILOG_STACK_RESTORE r7 + EPILOG_POP {r4-r5,r7,pc} + + NESTED_END + +#endif // FEATURE_COMINTEROP + +#ifdef PROFILING_SUPPORTED + +PROFILE_ENTER equ 1 +PROFILE_LEAVE equ 2 +PROFILE_TAILCALL equ 4 + + ; Define the layout of the PROFILE_PLATFORM_SPECIFIC_DATA we push on the stack for all profiler + ; helpers. + map 0 + field 4 ; r0 + field 4 ; r1 + field 4 ; r11 + field 4 ; Pc (caller's PC, i.e. LR) + field SIZEOF__FloatArgumentRegisters ; spilled floating point argument registers +functionId field 4 +probeSp field 4 +profiledSp field 4 +hiddenArg field 4 +flags field 4 + +SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA field 0 + +; ------------------------------------------------------------------ +; Macro used to generate profiler helpers. In all cases we push a partially initialized +; PROFILE_PLATFORM_SPECIFIC_DATA structure on the stack and call into a C++ helper to continue processing. +; +; On entry: +; r0 : clientInfo +; r1/r2 : return values (in case of leave) +; frame pointer(r11) must be set (in case of enter) +; all arguments are on stack at frame pointer (r11) + 8bytes (save lr & prev r11). +; +; On exit: +; All register values are preserved including volatile registers +; + MACRO + DefineProfilerHelper $HelperName, $Flags + + GBLS __ProfilerHelperFunc +__ProfilerHelperFunc SETS "$HelperName":CC:"Naked" + + NESTED_ENTRY $__ProfilerHelperFunc + + IMPORT $HelperName ; The C++ helper which does most of the work + + PROLOG_PUSH {r0,r3,r9,r12} ; save volatile general purpose registers. remaining r1 & r2 are saved below...saving r9 as it is required for virtualunwinding + PROLOG_STACK_ALLOC (6*4) ; Reserve space for tail end of structure (5*4 bytes) and extra 4 bytes is for aligning the stack at 8-byte boundary + PROLOG_VPUSH {d0-d7} ; Spill floting point argument registers + PROLOG_PUSH {r1,r11,lr} ; Save possible return value in r1, frame pointer and return address + PROLOG_PUSH {r2} ; Save possible return value in r0. Before calling Leave Hook Jit moves contents of r0 to r2 + ; so pushing r2 instead of r0. This push statement cannot be combined with the above push + ; as r2 gets pushed before r1. + + CHECK_STACK_ALIGNMENT + + ; Zero r1 for use clearing fields in the PROFILE_PLATFORM_SPECIFIC_DATA. + eor r1, r1 + + ; Clear functionId. + str r1, [sp, #functionId] + + ; Save caller's SP (at the point this helper was called). + add r2, sp, #(SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA + 20) + str r2, [sp, #probeSp] + + ; Save caller's SP (at the point where only argument registers have been spilled). + ldr r2, [r11] + add r2, r2, #8 ; location of arguments is at frame pointer(r11) + 8 (lr & prev frame ptr is saved before changing + str r2, [sp, #profiledSp] + + ; Clear hiddenArg. + str r1, [sp, #hiddenArg] + + ; Set flags to indicate type of helper called. + mov r1, #($Flags) + str r1, [sp, #flags] + + ; Call C++ portion of helper (<$HelperName>(clientInfo, &profilePlatformSpecificData)). + mov r1, sp + bl $HelperName + + EPILOG_POP {r2} + EPILOG_POP {r1,r11,lr} + EPILOG_VPOP {d0-d7} + EPILOG_STACK_FREE (6*4) + EPILOG_POP {r0,r3,r9,r12} + + EPILOG_RETURN + + NESTED_END + + MEND + + DefineProfilerHelper ProfileEnter, PROFILE_ENTER + DefineProfilerHelper ProfileLeave, PROFILE_LEAVE + DefineProfilerHelper ProfileTailcall, PROFILE_TAILCALL + +#endif // PROFILING_SUPPORTED + + ; + ; If a preserved register were pushed onto the stack between + ; the managed caller and the H_M_F, _R4_R11 will point to its + ; location on the stack and it would have been updated on the + ; stack by the GC already and it will be popped back into the + ; appropriate register when the appropriate epilog is run. + ; + ; Otherwise, the register is preserved across all the code + ; in this HCALL or FCALL, so we need to update those registers + ; here because the GC will have updated our copies in the + ; frame. + ; + ; So, if _R4_R11 points into the MachState, we need to update + ; the register here. That's what this macro does. + ; + + MACRO + RestoreRegMS $regIndex, $reg + + ; Incoming: + ; + ; R0 = address of MachState + ; + ; $regIndex: Index of the register (R4-R11). For R4, index is 4. + ; For R5, index is 5, and so on. + ; + ; $reg: Register name (e.g. R4, R5, etc) + ; + ; Get the address of the specified captured register from machine state + add r2, r0, #(MachState__captureR4_R11 + (($regIndex-4)*4)) + + ; Get the address of the specified preserved register from machine state + ldr r3, [r0, #(MachState___R4_R11 + (($regIndex-4)*4))] + + cmp r2, r3 + bne %FT0 + ldr $reg, [r2] +0 + + MEND + +; EXTERN_C int __fastcall HelperMethodFrameRestoreState( +; INDEBUG_COMMA(HelperMethodFrame *pFrame) +; MachState *pState +; ) + LEAF_ENTRY HelperMethodFrameRestoreState + +#ifdef _DEBUG + mov r0, r1 +#endif + + ; If machine state is invalid, then simply exit + ldr r1, [r0, #MachState__isValid] + cmp r1, #0 + beq Done + + RestoreRegMS 4, R4 + RestoreRegMS 5, R5 + RestoreRegMS 6, R6 + RestoreRegMS 7, R7 + RestoreRegMS 8, R8 + RestoreRegMS 9, R9 + RestoreRegMS 10, R10 + RestoreRegMS 11, R11 +Done + ; Its imperative that the return value of HelperMethodFrameRestoreState is zero + ; as it is used in the state machine to loop until it becomes zero. + ; Refer to HELPER_METHOD_FRAME_END macro for details. + mov r0,#0 + bx lr + + LEAF_END + +#ifdef FEATURE_HIJACK + +; ------------------------------------------------------------------ +; Hijack function for functions which return a reference type + NESTED_ENTRY OnHijackObjectTripThread + PROLOG_PUSH {r0,r4-r11,lr} + + CHECK_STACK_ALIGNMENT + + mov r0, sp + bl OnHijackObjectWorker + + EPILOG_POP {r0,r4-r11,pc} + NESTED_END + +; ------------------------------------------------------------------ +; Hijack function for functions which return an interior pointer within an object allocated in managed heap + NESTED_ENTRY OnHijackInteriorPointerTripThread + PROLOG_PUSH {r0,r4-r11,lr} + + CHECK_STACK_ALIGNMENT + + mov r0, sp + bl OnHijackInteriorPointerWorker + + EPILOG_POP {r0,r4-r11,pc} + NESTED_END + +; ------------------------------------------------------------------ +; Hijack function for functions which return a value type + NESTED_ENTRY OnHijackScalarTripThread + PROLOG_PUSH {r0,r4-r11,lr} + + PROLOG_VPUSH {d0-d3} ; saving as d0-d3 can have the floating point return value + PROLOG_PUSH {r1} ; saving as r1 can have partial return value when return is > 32 bits + PROLOG_STACK_ALLOC 4 ; 8 byte align + + CHECK_STACK_ALIGNMENT + + add r0, sp, #40 + bl OnHijackScalarWorker + + EPILOG_STACK_FREE 4 + EPILOG_POP {r1} + EPILOG_VPOP {d0-d3} + + EPILOG_POP {r0,r4-r11,pc} + NESTED_END + +#endif ; FEATURE_HIJACK + +; ------------------------------------------------------------------ +; Macro to generate Redirection Stubs +; +; $reason : reason for redirection +; Eg. GCThreadControl +; NOTE: If you edit this macro, make sure you update GetCONTEXTFromRedirectedStubStackFrame. +; This function is used by both the personality routine and the debugger to retrieve the original CONTEXT. + MACRO + GenerateRedirectedHandledJITCaseStub $reason + + GBLS __RedirectionStubFuncName + GBLS __RedirectionStubEndFuncName + GBLS __RedirectionFuncName +__RedirectionStubFuncName SETS "RedirectedHandledJITCaseFor":CC:"$reason":CC:"_Stub" +__RedirectionStubEndFuncName SETS "RedirectedHandledJITCaseFor":CC:"$reason":CC:"_StubEnd" +__RedirectionFuncName SETS "|?RedirectedHandledJITCaseFor":CC:"$reason":CC:"@Thread@@CAXXZ|" + + IMPORT $__RedirectionFuncName + + NESTED_ENTRY $__RedirectionStubFuncName + + PROLOG_PUSH {r7,lr} ; return address + PROLOG_STACK_ALLOC 4 ; stack slot to save the CONTEXT * + PROLOG_STACK_SAVE r7 + + ;REDIRECTSTUB_SP_OFFSET_CONTEXT is defined in asmconstants.h + ;If CONTEXT is not saved at 0 offset from SP it must be changed as well. + ASSERT REDIRECTSTUB_SP_OFFSET_CONTEXT == 0 + + ; Runtime check for 8-byte alignment. This check is necessary as this function can be + ; entered before complete execution of the prolog of another function. + and r0, r7, #4 + sub sp, sp, r0 + + ; stack must be 8 byte aligned + CHECK_STACK_ALIGNMENT + + ; + ; Save a copy of the redirect CONTEXT*. + ; This is needed for the debugger to unwind the stack. + ; + bl GetCurrentSavedRedirectContext + str r0, [r7] + + ; + ; Fetch the interrupted pc and save it as our return address. + ; + ldr r1, [r0, #CONTEXT_Pc] + str r1, [r7, #8] + + ; + ; Call target, which will do whatever we needed to do in the context + ; of the target thread, and will RtlRestoreContext when it is done. + ; + bl $__RedirectionFuncName + + EMIT_BREAKPOINT ; Unreachable + +; Put a label here to tell the debugger where the end of this function is. +$__RedirectionStubEndFuncName + EXPORT $__RedirectionStubEndFuncName + + NESTED_END + + MEND + +; ------------------------------------------------------------------ +; Redirection Stub for GC in fully interruptible method + GenerateRedirectedHandledJITCaseStub GCThreadControl +; ------------------------------------------------------------------ + GenerateRedirectedHandledJITCaseStub DbgThreadControl +; ------------------------------------------------------------------ + GenerateRedirectedHandledJITCaseStub UserSuspend +; ------------------------------------------------------------------ + GenerateRedirectedHandledJITCaseStub YieldTask + +#ifdef _DEBUG +; ------------------------------------------------------------------ +; Redirection Stub for GC Stress + GenerateRedirectedHandledJITCaseStub GCStress +#endif + +; ------------------------------------------------------------------ +; Functions to probe for stack space +; Input reg r4 = amount of stack to probe for +; value of reg r4 is preserved on exit from function +; r12 is trashed +; The below two functions were copied from vctools\crt\crtw32\startup\arm\chkstk.asm + + NESTED_ENTRY checkStack + subs r12,sp,r4 + mrc p15,#0,r4,c13,c0,#2 ; get TEB * + ldr r4,[r4,#8] ; get Stack limit + bcc checkStack_neg ; if r12 is less then 0 set it to 0 +checkStack_label1 + cmp r12, r4 + bcc stackProbe ; must probe to extend guardpage if r12 is beyond stackLimit + sub r4, sp, r12 ; restore value of r4 + EPILOG_RETURN +checkStack_neg + mov r12, #0 + b checkStack_label1 + NESTED_END + + NESTED_ENTRY stackProbe + PROLOG_PUSH {r5,r6} + mov r6, r12 + bfc r6, #0, #0xc ; align down (4K) +stackProbe_loop + sub r4,r4,#0x1000 ; dec stack Limit by 4K as page size is 4K + ldr r5,[r4] ; try to read ... this should move the guard page + cmp r4,r6 + bne stackProbe_loop + EPILOG_POP {r5,r6} + EPILOG_NOP sub r4,sp,r12 + EPILOG_RETURN + NESTED_END + +;------------------------------------------------ +; VirtualMethodFixupStub +; +; In NGEN images, virtual slots inherited from cross-module dependencies +; point to a jump thunk that calls into the following function that will +; call into a VM helper. The VM helper is responsible for patching up +; thunk, upon executing the precode, so that all subsequent calls go directly +; to the actual method body. +; +; This is done lazily for performance reasons. +; +; On entry: +; +; R0 = "this" pointer +; R12 = Address of thunk + 4 + + NESTED_ENTRY VirtualMethodFixupStub + + ; Save arguments and return address + PROLOG_PUSH {r0-r3, lr} + + ; Align stack + PROLOG_STACK_ALLOC SIZEOF__FloatArgumentRegisters + 4 + vstm sp, {d0-d7} + + + CHECK_STACK_ALIGNMENT + + ; R12 contains an address that is 4 bytes ahead of + ; where the thunk starts. Refer to ZapImportVirtualThunk::Save + ; for details on this. + ; + ; Move the correct thunk start address in R1 + sub r1, r12, #4 + + ; Call the helper in the VM to perform the actual fixup + ; and tell us where to tail call. R0 already contains + ; the this pointer. + bl VirtualMethodFixupWorker + + ; On return, R0 contains the target to tailcall to + mov r12, r0 + + ; pop the stack and restore original register state + vldm sp, {d0-d7} + EPILOG_STACK_FREE SIZEOF__FloatArgumentRegisters + 4 + EPILOG_POP {r0-r3, lr} + + PATCH_LABEL VirtualMethodFixupPatchLabel + + ; and tailcall to the actual method + EPILOG_BRANCH_REG r12 + + NESTED_END + +;------------------------------------------------ +; ExternalMethodFixupStub +; +; In NGEN images, calls to cross-module external methods initially +; point to a jump thunk that calls into the following function that will +; call into a VM helper. The VM helper is responsible for patching up the +; thunk, upon executing the precode, so that all subsequent calls go directly +; to the actual method body. +; +; This is done lazily for performance reasons. +; +; On entry: +; +; R12 = Address of thunk + 4 + + NESTED_ENTRY ExternalMethodFixupStub + + PROLOG_WITH_TRANSITION_BLOCK + + add r0, sp, #__PWTB_TransitionBlock ; pTransitionBlock + + ; Adjust (read comment above for details) and pass the address of the thunk + sub r1, r12, #4 ; pThunk + + mov r2, #0 ; sectionIndex + mov r3, #0 ; pModule + bl ExternalMethodFixupWorker + + ; mov the address we patched to in R12 so that we can tail call to it + mov r12, r0 + + EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + PATCH_LABEL ExternalMethodFixupPatchLabel + EPILOG_BRANCH_REG r12 + + NESTED_END + +;------------------------------------------------ +; StubDispatchFixupStub +; +; In NGEN images, calls to interface methods initially +; point to a jump thunk that calls into the following function that will +; call into a VM helper. The VM helper is responsible for patching up the +; thunk with actual stub dispatch stub. +; +; On entry: +; +; R4 = Address of indirection cell + + NESTED_ENTRY StubDispatchFixupStub + + PROLOG_WITH_TRANSITION_BLOCK + + ; address of StubDispatchFrame + add r0, sp, #__PWTB_TransitionBlock ; pTransitionBlock + mov r1, r4 ; siteAddrForRegisterIndirect + mov r2, #0 ; sectionIndex + mov r3, #0 ; pModule + + bl StubDispatchFixupWorker + + ; mov the address we patched to in R12 so that we can tail call to it + mov r12, r0 + + EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + PATCH_LABEL StubDispatchFixupPatchLabel + EPILOG_BRANCH_REG r12 + + NESTED_END + +;------------------------------------------------ +; JIT_RareDisableHelper +; +; The JIT expects this helper to preserve registers used for return values +; + NESTED_ENTRY JIT_RareDisableHelper + + PROLOG_PUSH {r0-r1, r11, lr} ; save integer return value + PROLOG_VPUSH {d0-d3} ; floating point return value + + CHECK_STACK_ALIGNMENT + + bl JIT_RareDisableHelperWorker + + EPILOG_VPOP {d0-d3} + EPILOG_POP {r0-r1, r11, pc} + + NESTED_END + + +#ifdef FEATURE_CORECLR +; +; JIT Static access helpers for single appdomain case +; + +; ------------------------------------------------------------------ +; void* JIT_GetSharedNonGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID) + + LEAF_ENTRY JIT_GetSharedNonGCStaticBase_SingleAppDomain + + ; If class is not initialized, bail to C++ helper + add r2, r0, #DomainLocalModule__m_pDataBlob + ldrb r2, [r2, r1] + tst r2, #1 + beq CallCppHelper1 + + bx lr + +CallCppHelper1 + ; Tail call JIT_GetSharedNonGCStaticBase_Helper + b JIT_GetSharedNonGCStaticBase_Helper + LEAF_END + + +; ------------------------------------------------------------------ +; void* JIT_GetSharedNonGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID) + + LEAF_ENTRY JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain + + bx lr + LEAF_END + + +; ------------------------------------------------------------------ +; void* JIT_GetSharedGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID) + + LEAF_ENTRY JIT_GetSharedGCStaticBase_SingleAppDomain + + ; If class is not initialized, bail to C++ helper + add r2, r0, #DomainLocalModule__m_pDataBlob + ldrb r2, [r2, r1] + tst r2, #1 + beq CallCppHelper3 + + ldr r0, [r0, #DomainLocalModule__m_pGCStatics] + bx lr + +CallCppHelper3 + ; Tail call Jit_GetSharedGCStaticBase_Helper + b JIT_GetSharedGCStaticBase_Helper + LEAF_END + + +; ------------------------------------------------------------------ +; void* JIT_GetSharedGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID) + + LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain + + ldr r0, [r0, #DomainLocalModule__m_pGCStatics] + bx lr + LEAF_END + +#endif + +; ------------------------------------------------------------------ +; __declspec(naked) void F_CALL_CONV JIT_Stelem_Ref(PtrArray* array, unsigned idx, Object* val) + LEAF_ENTRY JIT_Stelem_Ref + + ; We retain arguments as they were passed and use r0 == array; r1 == idx; r2 == val + + ; check for null array + cbz r0, ThrowNullReferenceException + + ; idx bounds check + ldr r3,[r0,#ArrayBase__m_NumComponents] + cmp r3,r1 + bls ThrowIndexOutOfRangeException + + ; fast path to null assignment (doesn't need any write-barriers) + cbz r2, AssigningNull + + ; Verify the array-type and val-type matches before writing + ldr r12, [r0] ; r12 = array MT + ldr r3, [r2] ; r3 = val->GetMethodTable() + ldr r12, [r12, #MethodTable__m_ElementType] ; array->GetArrayElementTypeHandle() + cmp r3, r12 + beq JIT_Stelem_DoWrite + + ; Types didnt match but allow writing into an array of objects + ldr r3, =$g_pObjectClass + ldr r3, [r3] ; r3 = *g_pObjectClass + cmp r3, r12 ; array type matches with Object* + beq JIT_Stelem_DoWrite + + ; array type and val type do not exactly match. Raise frame and do detailed match + b JIT_Stelem_Ref_NotExactMatch + +AssigningNull + ; Assigning null doesn't need write barrier + adds r0, r1, LSL #2 ; r0 = r0 + (r1 x 4) = array->m_array[idx] + str r2, [r0, #PtrArray__m_Array] ; array->m_array[idx] = val + bx lr + +ThrowNullReferenceException + ; Tail call JIT_InternalThrow(NullReferenceException) + ldr r0, =CORINFO_NullReferenceException_ASM + b JIT_InternalThrow + +ThrowIndexOutOfRangeException + ; Tail call JIT_InternalThrow(NullReferenceException) + ldr r0, =CORINFO_IndexOutOfRangeException_ASM + b JIT_InternalThrow + + LEAF_END + +; ------------------------------------------------------------------ +; __declspec(naked) void F_CALL_CONV JIT_Stelem_Ref_NotExactMatch(PtrArray* array, +; unsigned idx, Object* val) +; r12 = array->GetArrayElementTypeHandle() +; + NESTED_ENTRY JIT_Stelem_Ref_NotExactMatch + PROLOG_PUSH {lr} + PROLOG_PUSH {r0-r2} + + CHECK_STACK_ALIGNMENT + + ; allow in case val can be casted to array element type + ; call ObjIsInstanceOfNoGC(val, array->GetArrayElementTypeHandle()) + mov r1, r12 ; array->GetArrayElementTypeHandle() + mov r0, r2 + bl ObjIsInstanceOfNoGC + cmp r0, TypeHandle_CanCast + beq DoWrite ; ObjIsInstance returned TypeHandle::CanCast + + ; check via raising frame +NeedFrame + mov r1, sp ; r1 = &array + adds r0, sp, #8 ; r0 = &val + bl ArrayStoreCheck ; ArrayStoreCheck(&val, &array) + +DoWrite + EPILOG_POP {r0-r2} + EPILOG_POP {lr} + EPILOG_BRANCH JIT_Stelem_DoWrite + + NESTED_END + +; ------------------------------------------------------------------ +; __declspec(naked) void F_CALL_CONV JIT_Stelem_DoWrite(PtrArray* array, unsigned idx, Object* val) + LEAF_ENTRY JIT_Stelem_DoWrite + + ; Setup args for JIT_WriteBarrier. r0 = &array->m_array[idx]; r1 = val + adds r0, #PtrArray__m_Array ; r0 = &array->m_array + adds r0, r1, LSL #2 + mov r1, r2 ; r1 = val + + ; Branch to the write barrier (which is already correctly overwritten with + ; single or multi-proc code based on the current CPU + b JIT_WriteBarrier + + LEAF_END + +; ------------------------------------------------------------------ +; GC write barrier support. +; +; There's some complexity here for a couple of reasons: +; +; Firstly, there are a few variations of barrier types (input registers, checked vs unchecked, UP vs MP etc.). +; So first we define a number of helper macros that perform fundamental pieces of a barrier and then we define +; the final barrier functions by assembling these macros in various combinations. +; +; Secondly, for performance reasons we believe it's advantageous to be able to modify the barrier functions +; over the lifetime of the CLR. Specifically ARM has real problems reading the values of external globals (we +; need two memory indirections to do this) so we'd like to be able to directly set the current values of +; various GC globals (e.g. g_lowest_address and g_card_table) into the barrier code itself and then reset them +; every time they change (the GC already calls the VM to inform it of these changes). The handle this without +; creating too much fragility such as hardcoding instruction offsets in the VM update code, we wrap write +; barrier creation and GC globals access in a set of macros that create a table of descriptors describing each +; offset that must be patched. +; + +; Many of the following macros need a scratch register. Define a name for it here so it's easy to modify this +; in the future. + GBLS __wbscratch +__wbscratch SETS "r3" + +; +; First define the meta-macros used to support dynamically patching write barriers. +; + + ; WRITEBARRIERAREA + ; + ; As we assemble each write barrier function we build a descriptor for the offsets within that function + ; that need to be patched at runtime. We write these descriptors into a read-only portion of memory. Use a + ; specially-named linker section for this to ensure all the descriptors are contiguous and form a table. + ; During the final link of the CLR this section should be merged into the regular read-only data section. + ; + ; This macro handles switching assembler output to the above section (similar to the TEXTAREA or + ; RODATAAREA macros defined by kxarm.h). + ; + MACRO + WRITEBARRIERAREA + AREA |.clrwb|,DATA,READONLY + MEND + + ; BEGIN_WRITE_BARRIERS + ; + ; This macro must be invoked before any write barriers are defined. It sets up and exports a symbol, + ; g_rgWriteBarrierDescriptors, used by the VM to locate the start of the table describing the offsets in + ; each write barrier that need to be modified dynamically. + ; + MACRO + BEGIN_WRITE_BARRIERS + + ; Define a global boolean to track whether we're currently in a BEGIN_WRITE_BARRIERS section. This is + ; used purely to catch incorrect attempts to define a write barrier outside the section. + GBLL __defining_write_barriers +__defining_write_barriers SETL {true} + + ; Switch to the descriptor table section. + WRITEBARRIERAREA + + ; Define and export a symbol pointing to the start of the descriptor table. +g_rgWriteBarrierDescriptors + EXPORT g_rgWriteBarrierDescriptors + + ; Switch back to the code section. + TEXTAREA + MEND + + ; END_WRITE_BARRIERS + ; + ; This macro must be invoked after all write barriers have been defined. It finalizes the creation of the + ; barrier descriptor table by writing a sentinel value at the end. + ; + MACRO + END_WRITE_BARRIERS + + ASSERT __defining_write_barriers +__defining_write_barriers SETL {false} + + ; Switch to the descriptor table section. + WRITEBARRIERAREA + + ; Write the sentinel value to the end of the descriptor table (a function entrypoint address of zero). + DCD 0 + + ; Switch back to the code section. + TEXTAREA + MEND + + ; WRITE_BARRIER_ENTRY + ; + ; Declare the start of a write barrier function. Use similarly to NESTED_ENTRY. This is the only legal way + ; to declare a write barrier function. + ; + MACRO + WRITE_BARRIER_ENTRY $name + + ; Ensure we're called inside a BEGIN_WRITE_BARRIERS section. + ASSERT __defining_write_barriers + + ; Do the standard function declaration logic. Must use a NESTED_ENTRY since we require unwind info to + ; be registered (for the case where the barrier AVs and the runtime needs to recover). + LEAF_ENTRY $name + + ; Record the function name as it's used as the basis for unique label name creation in some of the + ; macros below. + GBLS __write_barrier_name +__write_barrier_name SETS "$name" + + ; Declare globals to collect the values of the offsets of instructions that load GC global values. + GBLA __g_lowest_address_offset + GBLA __g_highest_address_offset + GBLA __g_ephemeral_low_offset + GBLA __g_ephemeral_high_offset + GBLA __g_card_table_offset + + ; Initialize the above offsets to 0xffff. The default of zero is unsatisfactory because we could + ; legally have an offset of zero and we need some way to distinguish unset values (both for debugging + ; and because some write barriers don't use all the globals). +__g_lowest_address_offset SETA 0xffff +__g_highest_address_offset SETA 0xffff +__g_ephemeral_low_offset SETA 0xffff +__g_ephemeral_high_offset SETA 0xffff +__g_card_table_offset SETA 0xffff + + MEND + + ; WRITE_BARRIER_END + ; + ; The partner to WRITE_BARRIER_ENTRY, used like NESTED_END. + ; + MACRO + WRITE_BARRIER_END + + LTORG ; force the literal pool to be emitted here so that copy code picks it up + ; Use the standard macro to end the function definition. + LEAF_END_MARKED $__write_barrier_name + +; Define a local string to hold the name of a label identifying the end of the write barrier function. + LCLS __EndLabelName +__EndLabelName SETS "$__write_barrier_name":CC:"_End" + + ; Switch to the descriptor table section. + WRITEBARRIERAREA + + ; Emit the descripter for this write barrier. The order of these datums must be kept in sync with the + ; definition of the WriteBarrierDescriptor structure in vm\arm\stubs.cpp. + DCD $__write_barrier_name + DCD $__EndLabelName + DCD __g_lowest_address_offset + DCD __g_highest_address_offset + DCD __g_ephemeral_low_offset + DCD __g_ephemeral_high_offset + DCD __g_card_table_offset + + ; Switch back to the code section. + TEXTAREA + + MEND + + ; LOAD_GC_GLOBAL + ; + ; Used any time we want to load the value of one of the supported GC globals into a register. This records + ; the offset of the instructions used to do this (a movw/movt pair) so we can modify the actual value + ; loaded at runtime. + ; + ; Note that a given write barrier can only load a given global once (which will be compile-time asserted + ; below). + ; + MACRO + LOAD_GC_GLOBAL $regName, $globalName + + ; Map the GC global name to the name of the variable tracking the offset for this function. + LCLS __offset_name +__offset_name SETS "__$globalName._offset" + + ; Ensure that we only attempt to load this global at most once in the current barrier function (we + ; have this limitation purely because we only record one offset for each GC global). + ASSERT $__offset_name == 0xffff + + ; Define a unique name for a label we're about to define used in the calculation of the current + ; function offset. + LCLS __offset_label_name +__offset_label_name SETS "$__write_barrier_name$__offset_name" + + ; Define the label. +$__offset_label_name + + ; Write the current function offset into the tracking variable. +$__offset_name SETA ($__offset_label_name - $__FuncStartLabel) + + ; Emit the instructions which will be patched to provide the value of the GC global (we start with a + ; value of zero, so the write barriers have to be patched at least once before first use). + movw $regName, #0 + movt $regName, #0 + MEND + +; +; Now define the macros used in the bodies of write barrier implementations. +; + + ; UPDATE_GC_SHADOW + ; + ; Update the GC shadow heap to aid debugging (no-op unless WRITE_BARRIER_CHECK is defined). Assumes the + ; location being written lies on the GC heap (either we've already performed the dynamic check or this is + ; statically asserted by the JIT by calling the unchecked version of the write barrier). + ; + ; Input: + ; $ptrReg : register containing the location (in the real heap) to be updated + ; $valReg : register containing the value (an objref) to be written to the location above + ; + ; Output: + ; $__wbscratch : trashed + ; + MACRO + UPDATE_GC_SHADOW $ptrReg, $valReg +#ifdef WRITE_BARRIER_CHECK + + ; Need one additional temporary register to hold the shadow pointer. Assume r7 is OK for now (and + ; assert it). If this becomes a problem in the future the register choice can be parameterized. + LCLS pShadow +pShadow SETS "r7" + ASSERT "$ptrReg" != "$pShadow" + ASSERT "$valReg" != "$pShadow" + + push {$pShadow} + + ; Compute address of shadow heap location: + ; pShadow = g_GCShadow + ($ptrReg - g_lowest_address) + ldr $__wbscratch, =g_lowest_address + ldr $__wbscratch, [$__wbscratch] + sub $pShadow, $ptrReg, $__wbscratch + ldr $__wbscratch, =$g_GCShadow + ldr $__wbscratch, [$__wbscratch] + add $pShadow, $__wbscratch + + ; if (pShadow >= g_GCShadow) goto end + ldr $__wbscratch, =$g_GCShadowEnd + ldr $__wbscratch, [$__wbscratch] + cmp $pShadow, $__wbscratch + bhs %FT0 + + ; *pShadow = $valReg + str $valReg, [$pShadow] + + ; Ensure that the write to the shadow heap occurs before the read from the GC heap so that race + ; conditions are caught by INVALIDGCVALUE. + dmb + + ; if (*$ptrReg == $valReg) goto end + ldr $__wbscratch, [$ptrReg] + cmp $__wbscratch, $valReg + beq %FT0 + + ; *pShadow = INVALIDGCVALUE (0xcccccccd) + movw $__wbscratch, #0xcccd + movt $__wbscratch, #0xcccc + str $__wbscratch, [$pShadow] + +0 + pop {$pShadow} +#endif // WRITE_BARRIER_CHECK + MEND + + ; UPDATE_CARD_TABLE + ; + ; Update the card table as necessary (if the object reference being assigned in the barrier refers to an + ; object in the ephemeral generation). Otherwise this macro is a no-op. Assumes the location being written + ; lies on the GC heap (either we've already performed the dynamic check or this is statically asserted by + ; the JIT by calling the unchecked version of the write barrier). + ; + ; Additionally this macro can produce a uni-proc or multi-proc variant of the code. This governs whether + ; we bother to check if the card table has been updated before making our own update (on an MP system it + ; can be helpful to perform this check to avoid cache line thrashing, on an SP system the code path length + ; is more important). + ; + ; Input: + ; $ptrReg : register containing the location to be updated + ; $valReg : register containing the value (an objref) to be written to the location above + ; $mp : boolean indicating whether the code will run on an MP system + ; $tmpReg : additional register that can be trashed (can alias $ptrReg or $valReg if needed) + ; + ; Output: + ; $tmpReg : trashed (defaults to $ptrReg) + ; $__wbscratch : trashed + ; + MACRO + UPDATE_CARD_TABLE $ptrReg, $valReg, $mp, $postGrow, $tmpReg + ASSERT "$ptrReg" != "$__wbscratch" + ASSERT "$valReg" != "$__wbscratch" + ASSERT "$tmpReg" != "$__wbscratch" + + ; In most cases the callers of this macro are fine with scratching $ptrReg, the exception being the + ; ref write barrier, which wants to scratch $valReg instead. Ideally we could set $ptrReg as the + ; default for the $tmpReg parameter, but limitations in armasm won't allow that. Similarly it doesn't + ; seem to like us trying to redefine $tmpReg in the body of the macro. Instead we define a new local + ; string variable and set that either with the value of $tmpReg or $ptrReg if $tmpReg wasn't + ; specified. + LCLS tempReg + IF "$tmpReg" == "" +tempReg SETS "$ptrReg" + ELSE +tempReg SETS "$tmpReg" + ENDIF + + ; Check whether the value object lies in the ephemeral generations. If not we don't have to update the + ; card table. + LOAD_GC_GLOBAL $__wbscratch, g_ephemeral_low + cmp $valReg, $__wbscratch + blo %FT0 + ; Only in post grow higher generation can be beyond ephemeral segment + IF $postGrow + LOAD_GC_GLOBAL $__wbscratch, g_ephemeral_high + cmp $valReg, $__wbscratch + bhs %FT0 + ENDIF + + ; Update the card table. + LOAD_GC_GLOBAL $__wbscratch, g_card_table + add $__wbscratch, $__wbscratch, $ptrReg, lsr #10 + + ; On MP systems make sure the card hasn't already been set first to avoid thrashing cache lines + ; between CPUs. + ; @ARMTODO: Check that the conditional store doesn't unconditionally gain exclusive access to the + ; cache line anyway. Compare perf with a branch over and verify that omitting the compare on uniproc + ; machines really is a perf win. + IF $mp + ldrb $tempReg, [$__wbscratch] + cmp $tempReg, #0xff + movne $tempReg, #0xff + strbne $tempReg, [$__wbscratch] + ELSE + mov $tempReg, #0xff + strb $tempReg, [$__wbscratch] + ENDIF +0 + MEND + + ; CHECK_GC_HEAP_RANGE + ; + ; Verifies that the given value points into the GC heap range. If so the macro will fall through to the + ; following code. Otherwise (if the value points outside the GC heap) a branch to the supplied label will + ; be made. + ; + ; Input: + ; $ptrReg : register containing the location to be updated + ; $label : label branched to on a range check failure + ; + ; Output: + ; $__wbscratch : trashed + ; + MACRO + CHECK_GC_HEAP_RANGE $ptrReg, $label + ASSERT "$ptrReg" != "$__wbscratch" + + LOAD_GC_GLOBAL $__wbscratch, g_lowest_address + cmp $ptrReg, $__wbscratch + blo $label + LOAD_GC_GLOBAL $__wbscratch, g_highest_address + cmp $ptrReg, $__wbscratch + bhs $label + MEND + +; +; Finally define the write barrier functions themselves. Currently we don't provide variations that use +; different input registers. If the JIT wants this at a later stage in order to improve code quality it would +; be a relatively simply change to implement via an additional macro parameter to WRITE_BARRIER_ENTRY. +; +; The calling convention for the first batch of write barriers is: +; +; On entry: +; r0 : the destination address (LHS of the assignment) +; r1 : the object reference (RHS of the assignment) +; +; On exit: +; r0 : trashed +; $__wbscratch : trashed +; + + ; If you update any of the writebarrier be sure to update the sizes of patchable + ; writebarriers in + ; see ValidateWriteBarriers() + + ; The write barriers are macro taking arguments like + ; $name: Name of the write barrier + ; $mp: {true} for multi-proc, {false} otherwise + ; $post: {true} for post-grow version, {false} otherwise + + MACRO + JIT_WRITEBARRIER $name, $mp, $post + WRITE_BARRIER_ENTRY $name + IF $mp + dmb ; Perform a memory barrier + ENDIF + str r1, [r0] ; Write the reference + UPDATE_GC_SHADOW r0, r1 ; Update the shadow GC heap for debugging + UPDATE_CARD_TABLE r0, r1, $mp, $post ; Update the card table if necessary + bx lr + WRITE_BARRIER_END + MEND + + MACRO + JIT_CHECKEDWRITEBARRIER_SP $name, $post + WRITE_BARRIER_ENTRY $name + str r1, [r0] ; Write the reference + CHECK_GC_HEAP_RANGE r0, %F1 ; Check whether the destination is in the GC heap + UPDATE_GC_SHADOW r0, r1 ; Update the shadow GC heap for debugging + UPDATE_CARD_TABLE r0, r1, {false}, $post; Update the card table if necessary +1 + bx lr + WRITE_BARRIER_END + MEND + + MACRO + JIT_CHECKEDWRITEBARRIER_MP $name, $post + WRITE_BARRIER_ENTRY $name + CHECK_GC_HEAP_RANGE r0, %F1 ; Check whether the destination is in the GC heap + dmb ; Perform a memory barrier + str r1, [r0] ; Write the reference + UPDATE_GC_SHADOW r0, r1 ; Update the shadow GC heap for debugging + UPDATE_CARD_TABLE r0, r1, {true}, $post ; Update the card table if necessary + bx lr +1 + str r1, [r0] ; Write the reference + bx lr + WRITE_BARRIER_END + MEND + +; The ByRef write barriers have a slightly different interface: +; +; On entry: +; r0 : the destination address (object reference written here) +; r1 : the source address (points to object reference to write) +; +; On exit: +; r0 : incremented by 4 +; r1 : incremented by 4 +; r2 : trashed +; $__wbscratch : trashed +; + MACRO + JIT_BYREFWRITEBARRIER $name, $mp, $post + WRITE_BARRIER_ENTRY $name + IF $mp + dmb ; Perform a memory barrier + ENDIF + ldr r2, [r1] ; Load target object ref from source pointer + str r2, [r0] ; Write the reference to the destination pointer + CHECK_GC_HEAP_RANGE r0, %F1 ; Check whether the destination is in the GC heap + UPDATE_GC_SHADOW r0, r2 ; Update the shadow GC heap for debugging + UPDATE_CARD_TABLE r0, r2, $mp, $post, r2 ; Update the card table if necessary (trash r2 rather than r0) +1 + add r0, #4 ; Increment the destination pointer by 4 + add r1, #4 ; Increment the source pointer by 4 + bx lr + WRITE_BARRIER_END + MEND + + BEGIN_WRITE_BARRIERS + + ; There 4 versions of each write barriers. A 2x2 combination of multi-proc/single-proc and pre/post grow version + JIT_WRITEBARRIER JIT_WriteBarrier_SP_Pre, {false}, {false} + JIT_WRITEBARRIER JIT_WriteBarrier_SP_Post, {false}, {true} + JIT_WRITEBARRIER JIT_WriteBarrier_MP_Pre, {true}, {false} + JIT_WRITEBARRIER JIT_WriteBarrier_MP_Post, {true}, {true} + + JIT_CHECKEDWRITEBARRIER_SP JIT_CheckedWriteBarrier_SP_Pre, {false} + JIT_CHECKEDWRITEBARRIER_SP JIT_CheckedWriteBarrier_SP_Post, {true} + JIT_CHECKEDWRITEBARRIER_MP JIT_CheckedWriteBarrier_MP_Pre, {false} + JIT_CHECKEDWRITEBARRIER_MP JIT_CheckedWriteBarrier_MP_Post, {true} + + JIT_BYREFWRITEBARRIER JIT_ByRefWriteBarrier_SP_Pre, {false}, {false} + JIT_BYREFWRITEBARRIER JIT_ByRefWriteBarrier_SP_Post, {false}, {true} + JIT_BYREFWRITEBARRIER JIT_ByRefWriteBarrier_MP_Pre, {true}, {false} + JIT_BYREFWRITEBARRIER JIT_ByRefWriteBarrier_MP_Post, {true}, {true} + + END_WRITE_BARRIERS + +#ifdef FEATURE_READYTORUN + + NESTED_ENTRY DelayLoad_MethodCall_FakeProlog + + ; Match what the lazy thunk has pushed. The actual method arguments will be spilled later. + PROLOG_PUSH {r1-r3} + + ; This is where execution really starts. +DelayLoad_MethodCall + EXPORT DelayLoad_MethodCall + + PROLOG_PUSH {r0} + + PROLOG_WITH_TRANSITION_BLOCK 0x0, {true}, DoNotPushArgRegs + + ; Load the helper arguments + ldr r5, [sp,#(__PWTB_TransitionBlock+10*4)] ; pModule + ldr r6, [sp,#(__PWTB_TransitionBlock+11*4)] ; sectionIndex + ldr r7, [sp,#(__PWTB_TransitionBlock+12*4)] ; indirection + + ; Spill the actual method arguments + str r1, [sp,#(__PWTB_TransitionBlock+10*4)] + str r2, [sp,#(__PWTB_TransitionBlock+11*4)] + str r3, [sp,#(__PWTB_TransitionBlock+12*4)] + + add r0, sp, #__PWTB_TransitionBlock ; pTransitionBlock + + mov r1, r7 ; pIndirection + mov r2, r6 ; sectionIndex + mov r3, r5 ; pModule + + bl ExternalMethodFixupWorker + + ; mov the address we patched to in R12 so that we can tail call to it + mov r12, r0 + + EPILOG_WITH_TRANSITION_BLOCK_TAILCALL + + ; Share the patch label + EPILOG_BRANCH ExternalMethodFixupPatchLabel + + NESTED_END + + + MACRO + DynamicHelper $frameFlags, $suffix + + GBLS __FakePrologName +__FakePrologName SETS "DelayLoad_Helper":CC:"$suffix":CC:"_FakeProlog" + + NESTED_ENTRY $__FakePrologName + + ; Match what the lazy thunk has pushed. The actual method arguments will be spilled later. + PROLOG_PUSH {r1-r3} + + GBLS __RealName +__RealName SETS "DelayLoad_Helper":CC:"$suffix" + + ; This is where execution really starts. +$__RealName + EXPORT $__RealName + + PROLOG_PUSH {r0} + + PROLOG_WITH_TRANSITION_BLOCK 0x4, {true}, DoNotPushArgRegs + + ; Load the helper arguments + ldr r5, [sp,#(__PWTB_TransitionBlock+10*4)] ; pModule + ldr r6, [sp,#(__PWTB_TransitionBlock+11*4)] ; sectionIndex + ldr r7, [sp,#(__PWTB_TransitionBlock+12*4)] ; indirection + + ; Spill the actual method arguments + str r1, [sp,#(__PWTB_TransitionBlock+10*4)] + str r2, [sp,#(__PWTB_TransitionBlock+11*4)] + str r3, [sp,#(__PWTB_TransitionBlock+12*4)] + + add r0, sp, #__PWTB_TransitionBlock ; pTransitionBlock + + mov r1, r7 ; pIndirection + mov r2, r6 ; sectionIndex + mov r3, r5 ; pModule + + mov r4, $frameFlags + str r4, [sp,#0] + + bl DynamicHelperWorker + + EPILOG_WITH_TRANSITION_BLOCK_RETURN + + NESTED_END + + MEND + + DynamicHelper DynamicHelperFrameFlags_Default + DynamicHelper DynamicHelperFrameFlags_ObjectArg, _Obj + DynamicHelper DynamicHelperFrameFlags_ObjectArg | DynamicHelperFrameFlags_ObjectArg2, _ObjObj + +#endif // FEATURE_READYTORUN + +; Must be at very end of file + END diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm/cgencpu.h b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm/cgencpu.h new file mode 100644 index 00000000000000..e3f45eef54f063 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm/cgencpu.h @@ -0,0 +1,1303 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + + +#ifndef _TARGET_ARM_ +#error Should only include "cGenCpu.h" for ARM builds +#endif + +#ifndef __cgencpu_h__ +#define __cgencpu_h__ + +#include "utilcode.h" +#include "tls.h" + +// preferred alignment for data +#define DATA_ALIGNMENT 4 + +#define DISPATCH_STUB_FIRST_WORD 0xf8d0 +#define RESOLVE_STUB_FIRST_WORD 0xf8d0 + +class MethodDesc; +class FramedMethodFrame; +class Module; +struct DeclActionInfo; +class ComCallMethodDesc; +class BaseDomain; +class ZapNode; +struct ArgLocDesc; + +#define USE_REDIRECT_FOR_GCSTRESS + +// CPU-dependent functions +Stub * GenerateInitPInvokeFrameHelper(); + +EXTERN_C void checkStack(void); + +#ifdef CROSSGEN_COMPILE +#define GetEEFuncEntryPoint(pfn) 0x1001 +#else +#define GetEEFuncEntryPoint(pfn) GFN_TADDR(pfn) +#endif + +//********************************************************************** + +#define COMMETHOD_PREPAD 12 // # extra bytes to allocate in addition to sizeof(ComCallMethodDesc) +#ifdef FEATURE_COMINTEROP +#define COMMETHOD_CALL_PRESTUB_SIZE 12 +#define COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET 8 // the offset of the call target address inside the prestub +#endif // FEATURE_COMINTEROP + +#define STACK_ALIGN_SIZE 4 + +#define JUMP_ALLOCATE_SIZE 8 // # bytes to allocate for a jump instruction +#define BACK_TO_BACK_JUMP_ALLOCATE_SIZE 8 // # bytes to allocate for a back to back jump instruction + +//#define HAS_COMPACT_ENTRYPOINTS 1 + +#define HAS_NDIRECT_IMPORT_PRECODE 1 + +#define USE_INDIRECT_CODEHEADER + +#ifdef FEATURE_REMOTING +#define HAS_REMOTING_PRECODE 1 +#endif + +EXTERN_C void getFPReturn(int fpSize, INT64 *pRetVal); +EXTERN_C void setFPReturn(int fpSize, INT64 retVal); + +#define HAS_FIXUP_PRECODE 1 +#define HAS_FIXUP_PRECODE_CHUNKS 1 + +// ThisPtrRetBufPrecode one is necessary for closed delegates over static methods with return buffer +#define HAS_THISPTR_RETBUF_PRECODE 1 + +#define CODE_SIZE_ALIGN 4 +#define CACHE_LINE_SIZE 32 // As per Intel Optimization Manual the cache line size is 32 bytes +#define LOG2SLOT LOG2_PTRSIZE + +#define ENREGISTERED_RETURNTYPE_MAXSIZE 32 // bytes (maximum HFA size is 4 doubles) +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 4 // bytes + +#define CALLDESCR_ARGREGS 1 // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS 1 // CallDescrWorker has FloatArgumentRegisters parameter + +// Max size of optimized TLS helpers +#define TLS_GETTER_MAX_SIZE 0x10 + +// Given a return address retrieved during stackwalk, +// this is the offset by which it should be decremented to arrive at the callsite. +#define STACKWALK_CONTROLPC_ADJUST_OFFSET 2 + +//======================================================================= +// IMPORTANT: This value is used to figure out how much to allocate +// for a fixed array of FieldMarshaler's. That means it must be at least +// as large as the largest FieldMarshaler subclass. This requirement +// is guarded by an assert. +//======================================================================= +#define MAXFIELDMARSHALERSIZE 24 + +//********************************************************************** +// Parameter size +//********************************************************************** + +typedef INT32 StackElemType; +#define STACK_ELEM_SIZE sizeof(StackElemType) + +// !! This expression assumes STACK_ELEM_SIZE is a power of 2. +#define StackElemSize(parmSize) (((parmSize) + STACK_ELEM_SIZE - 1) & ~((ULONG)(STACK_ELEM_SIZE - 1))) + +//********************************************************************** +// Frames +//********************************************************************** + +//-------------------------------------------------------------------- +// This represents the callee saved (non-volatile) registers saved as +// of a FramedMethodFrame. +//-------------------------------------------------------------------- +typedef DPTR(struct CalleeSavedRegisters) PTR_CalleeSavedRegisters; +struct CalleeSavedRegisters { + INT32 r4, r5, r6, r7, r8, r9, r10; + INT32 r11; // frame pointer + INT32 r14; // link register +}; + +//-------------------------------------------------------------------- +// This represents the arguments that are stored in volatile registers. +// This should not overlap the CalleeSavedRegisters since those are already +// saved separately and it would be wasteful to save the same register twice. +// If we do use a non-volatile register as an argument, then the ArgIterator +// will probably have to communicate this back to the PromoteCallerStack +// routine to avoid a double promotion. +//-------------------------------------------------------------------- +typedef DPTR(struct ArgumentRegisters) PTR_ArgumentRegisters; +struct ArgumentRegisters { + INT32 r[4]; // r0, r1, r2, r3 +}; +#define NUM_ARGUMENT_REGISTERS 4 + +//-------------------------------------------------------------------- +// This represents the floating point argument registers which are saved +// as part of the NegInfo for a FramedMethodFrame. Note that these +// might not be saved by all stubs: typically only those that call into +// C++ helpers will need to preserve the values in these volatile +// registers. +//-------------------------------------------------------------------- +typedef DPTR(struct FloatArgumentRegisters) PTR_FloatArgumentRegisters; +struct FloatArgumentRegisters { + union + { + float s[16]; // s0-s15 + double d[8]; // d0-d7 + }; +}; + +// forward decl +struct REGDISPLAY; +typedef REGDISPLAY *PREGDISPLAY; + +// Sufficient context for Try/Catch restoration. +struct EHContext { + INT32 r[16]; // note: includes r15(pc) + void Setup(PCODE resumePC, PREGDISPLAY regs); + + inline TADDR GetSP() { + LIMITED_METHOD_CONTRACT; + return (TADDR)r[13]; + } + inline void SetSP(LPVOID esp) { + LIMITED_METHOD_CONTRACT; + r[13] = (INT32)(size_t)esp; + } + + inline LPVOID GetFP() { + LIMITED_METHOD_CONTRACT; + return (LPVOID)(UINT_PTR)r[11]; + } + + inline void SetArg(LPVOID arg) { + LIMITED_METHOD_CONTRACT; + r[0] = (INT32)(size_t)arg; + } +}; + +#define ARGUMENTREGISTERS_SIZE sizeof(ArgumentRegisters) + +//********************************************************************** +// Exception handling +//********************************************************************** + +inline PCODE GetIP(const T_CONTEXT * context) { + LIMITED_METHOD_DAC_CONTRACT; + return PCODE(context->Pc); +} + +inline void SetIP(T_CONTEXT *context, PCODE eip) { + LIMITED_METHOD_DAC_CONTRACT; + context->Pc = DWORD(eip); +} + +inline TADDR GetSP(const T_CONTEXT * context) { + LIMITED_METHOD_DAC_CONTRACT; + return TADDR(context->Sp); +} + +inline PCODE GetLR(const T_CONTEXT * context) { + LIMITED_METHOD_DAC_CONTRACT; + return PCODE(context->Lr); +} + +extern "C" LPVOID __stdcall GetCurrentSP(); + +inline void SetSP(T_CONTEXT *context, TADDR esp) { + LIMITED_METHOD_DAC_CONTRACT; + context->Sp = DWORD(esp); +} + +inline void SetFP(T_CONTEXT *context, TADDR ebp) { + LIMITED_METHOD_DAC_CONTRACT; + context->R11 = DWORD(ebp); +} + +inline TADDR GetFP(const T_CONTEXT * context) +{ + LIMITED_METHOD_DAC_CONTRACT; + return (TADDR)(context->R11); +} + +inline void ClearITState(T_CONTEXT *context) { + LIMITED_METHOD_DAC_CONTRACT; + context->Cpsr = context->Cpsr & 0xf9ff03ff; +} + +#ifdef FEATURE_COMINTEROP +void emitCOMStubCall (ComCallMethodDesc *pCOMMethod, PCODE target); +#endif // FEATURE_COMINTEROP + +//------------------------------------------------------------------------ +inline void emitJump(LPBYTE pBuffer, LPVOID target) +{ + LIMITED_METHOD_CONTRACT; + + // The PC-relative load we emit below requires 4-byte alignment for the offset to be calculated correctly. + _ASSERTE(((UINT_PTR)pBuffer & 3) == 0); + + DWORD * pCode = (DWORD *)pBuffer; + + // ldr pc, [pc, #0] + pCode[0] = 0xf000f8df; + pCode[1] = (DWORD)target; +} + +//------------------------------------------------------------------------ +// Given the same pBuffer that was used by emitJump this method +// decodes the instructions and returns the jump target +inline PCODE decodeJump(PCODE pCode) +{ + LIMITED_METHOD_CONTRACT; + + TADDR pInstr = PCODEToPINSTR(pCode); + + return *dac_cast(pInstr + sizeof(DWORD)); +} + +// +// On IA64 back to back jumps should be separated by a nop bundle to get +// the best performance from the hardware's branch prediction logic. +// For all other platforms back to back jumps don't require anything special +// That is why we have these two wrapper functions that call emitJump and decodeJump +// + +//------------------------------------------------------------------------ +inline BOOL isJump(PCODE pCode) +{ + LIMITED_METHOD_DAC_CONTRACT; + + TADDR pInstr = PCODEToPINSTR(pCode); + + return *dac_cast(pInstr) == 0xf000f8df; +} + +//------------------------------------------------------------------------ +inline BOOL isBackToBackJump(PCODE pBuffer) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return isJump(pBuffer); +} + +//------------------------------------------------------------------------ +inline void emitBackToBackJump(LPBYTE pBuffer, LPVOID target) +{ + WRAPPER_NO_CONTRACT; + emitJump(pBuffer, target); +} + +//------------------------------------------------------------------------ +inline PCODE decodeBackToBackJump(PCODE pBuffer) +{ + WRAPPER_NO_CONTRACT; + return decodeJump(pBuffer); +} + +//---------------------------------------------------------------------- +#include "stublink.h" +struct ArrayOpScript; + +#define THUMB_CODE 1 + +inline BOOL IsThumbCode(PCODE pCode) +{ + return (pCode & THUMB_CODE) != 0; +} + +struct ThumbReg +{ + int reg; + ThumbReg(int reg):reg(reg) + { + _ASSERTE(0 <= reg && reg < 16); + } + + operator int () + { + return reg; + } + + int operator == (ThumbReg other) + { + return reg == other.reg; + } + + int operator != (ThumbReg other) + { + return reg != other.reg; + } + + WORD Mask() const + { + return 1 << reg; + } + +}; + +struct ThumbCond +{ + int cond; + ThumbCond(int cond):cond(cond) + { + _ASSERTE(0 <= cond && cond < 16); + } +}; + +struct ThumbVFPSingleReg +{ + int reg; + ThumbVFPSingleReg(int reg):reg(reg) + { + _ASSERTE(0 <= reg && reg < 31); + } + + operator int () + { + return reg; + } + + int operator == (ThumbVFPSingleReg other) + { + return reg == other.reg; + } + + int operator != (ThumbVFPSingleReg other) + { + return reg != other.reg; + } + + WORD Mask() const + { + return 1 << reg; + } + +}; + +struct ThumbVFPDoubleReg +{ + int reg; + ThumbVFPDoubleReg(int reg):reg(reg) + { + _ASSERTE(0 <= reg && reg < 31); + } + + operator int () + { + return reg; + } + + int operator == (ThumbVFPDoubleReg other) + { + return reg == other.reg; + } + + int operator != (ThumbVFPDoubleReg other) + { + return reg != other.reg; + } + + WORD Mask() const + { + return 1 << reg; + } +}; + +const ThumbReg thumbRegFp = ThumbReg(11); +const ThumbReg thumbRegSp = ThumbReg(13); +const ThumbReg thumbRegLr = ThumbReg(14); +const ThumbReg thumbRegPc = ThumbReg(15); + +const ThumbCond thumbCondEq = ThumbCond(0); +const ThumbCond thumbCondNe = ThumbCond(1); +const ThumbCond thumbCondCs = ThumbCond(2); +const ThumbCond thumbCondCc = ThumbCond(3); +const ThumbCond thumbCondMi = ThumbCond(4); +const ThumbCond thumbCondPl = ThumbCond(5); +const ThumbCond thumbCondVs = ThumbCond(6); +const ThumbCond thumbCondVc = ThumbCond(7); +const ThumbCond thumbCondHi = ThumbCond(8); +const ThumbCond thumbCondLs = ThumbCond(9); +const ThumbCond thumbCondGe = ThumbCond(10); +const ThumbCond thumbCondLt = ThumbCond(11); +const ThumbCond thumbCondGt = ThumbCond(12); +const ThumbCond thumbCondLe = ThumbCond(13); +const ThumbCond thumbCondAl = ThumbCond(14); + +class StubLinkerCPU : public StubLinker +{ +public: + static void Init(); + + void ThumbEmitProlog(UINT cCalleeSavedRegs, UINT cbStackFrame, BOOL fPushArgRegs) + { + _ASSERTE(!m_fProlog); + + // Record the parameters of this prolog so that we can generate a matching epilog and unwind info. + DescribeProlog(cCalleeSavedRegs, cbStackFrame, fPushArgRegs); + + // Trivial prologs (which is all that we support initially) consist of between one and three + // instructions. + + // 1) Push argument registers. This is all or nothing (if we push, we push R0-R3). + if (fPushArgRegs) + { + // push {r0-r3} + ThumbEmitPush(ThumbReg(0).Mask() | ThumbReg(1).Mask() | ThumbReg(2).Mask() | ThumbReg(3).Mask()); + } + + // 2) Push callee saved registers. We always start pushing at R4, and only saved consecutive registers + // from there (max is R11). Additionally we always assume LR is saved for these types of prolog. + // push {r4-rX,lr} + WORD wRegisters = thumbRegLr.Mask(); + for (unsigned int i = 4; i < (4 + cCalleeSavedRegs); i++) + wRegisters |= ThumbReg(i).Mask(); + ThumbEmitPush(wRegisters); + + // 3) Reserve space on the stack for the rest of the frame. + if (cbStackFrame) + { + // sub sp, #cbStackFrame + ThumbEmitSubSp(cbStackFrame); + } + } + + void ThumbEmitEpilog() + { + // Generate an epilog matching a prolog generated by ThumbEmitProlog. + _ASSERTE(m_fProlog); + + // If additional stack space for a frame was allocated remove it now. + if (m_cbStackFrame) + { + // add sp, #m_cbStackFrame + ThumbEmitAddSp(m_cbStackFrame); + } + + // Pop callee saved registers (we always have at least LR). If no argument registers were saved then + // we can restore LR back into PC and we're done. Otherwise LR needs to be restored into LR. + // pop {r4-rX,lr|pc} + WORD wRegisters = m_fPushArgRegs ? thumbRegLr.Mask() : thumbRegPc.Mask(); + for (unsigned int i = 4; i < (4 + m_cCalleeSavedRegs); i++) + wRegisters |= ThumbReg(i).Mask(); + ThumbEmitPop(wRegisters); + + if (!m_fPushArgRegs) + return; + + // We pushed the argument registers. These aren't restored, but we need to reclaim the stack space. + // add sp, #16 + ThumbEmitAddSp(16); + + // Return. The return address has been restored into LR at this point. + // bx lr + ThumbEmitJumpRegister(thumbRegLr); + } + + void ThumbEmitGetThread(TLSACCESSMODE mode, ThumbReg dest); + + void ThumbEmitNop() + { + // nop + Emit16(0xbf00); + } + + void ThumbEmitBreakpoint() + { + // Permanently undefined instruction #0xfe (see ARMv7-A A6.2.6). The debugger seems to accept this as + // a reasonable breakpoint substitute (it's what DebugBreak uses). Bkpt #0, on the other hand, always + // seems to flow directly to the kernel debugger (even if we ignore it there it doesn't seem to be + // picked up by the user mode debugger). + Emit16(0xdefe); + } + + void ThumbEmitMovConstant(ThumbReg dest, int constant) + { + _ASSERT(dest != thumbRegPc); + + //Emit 2 Byte instructions when dest reg < 8 & constant <256 + if(dest <= 7 && constant < 256 && constant >= 0) + { + Emit16((WORD)(0x2000 | dest<<8 | (WORD)constant)); + } + else // emit 4 byte instructions + { + WORD wConstantLow = (WORD)(constant & 0xffff); + WORD wConstantHigh = (WORD)(constant >> 16); + + // movw regDest, #wConstantLow + Emit16((WORD)(0xf240 | (wConstantLow >> 12) | ((wConstantLow & 0x0800) ? 0x0400 : 0x0000))); + Emit16((WORD)((dest << 8) | (((wConstantLow >> 8) & 0x0007) << 12) | (wConstantLow & 0x00ff))); + + if (wConstantHigh) + { + // movt regDest, #wConstantHighw + Emit16((WORD)(0xf2c0 | (wConstantHigh >> 12) | ((wConstantHigh & 0x0800) ? 0x0400 : 0x0000))); + Emit16((WORD)((dest << 8) | (((wConstantHigh >> 8) & 0x0007) << 12) | (wConstantHigh & 0x00ff))); + } + } + } + + void ThumbEmitLoadRegIndirect(ThumbReg dest, ThumbReg source, int offset) + { + _ASSERTE((offset >= 0) && (offset <= 4095)); + + // ldr regDest, [regSource + #offset] + if ((dest < 8) && (source < 8) && ((offset & 0x3) == 0) && (offset < 125)) + { + // Encoding T1 + Emit16((WORD)(0x6800 | ((offset >> 2) << 6) | (source << 3) | dest)); + } + else + { + // Encoding T3 + Emit16((WORD)(0xf8d0 | source)); + Emit16((WORD)((dest << 12) | offset)); + } + } + + void ThumbEmitLoadIndirectPostIncrement(ThumbReg dest, ThumbReg source, int offset) + { + _ASSERTE((offset >= 0) && (offset <= 255)); + + // ldr regDest, [regSource], #offset + Emit16((WORD)(0xf850 | source)); + Emit16((WORD)(0x0b00 | (dest << 12) | offset)); + } + + void ThumbEmitStoreRegIndirect(ThumbReg source, ThumbReg dest, int offset) + { + _ASSERTE((offset >= -255) && (offset <= 4095)); + + // str regSource, [regDest + #offset] + if (offset < 0) + { + Emit16((WORD)(0xf840 | dest)); + Emit16((WORD)(0x0C00 | (source << 12) | (UINT8)(-offset))); + } + else + if ((dest < 8) && (source < 8) && ((offset & 0x3) == 0) && (offset < 125)) + { + // Encoding T1 + Emit16((WORD)(0x6000 | ((offset >> 2) << 6) | (dest << 3) | source)); + } + else + { + // Encoding T3 + Emit16((WORD)(0xf8c0 | dest)); + Emit16((WORD)((source << 12) | offset)); + } + } + + void ThumbEmitStoreIndirectPostIncrement(ThumbReg source, ThumbReg dest, int offset) + { + _ASSERTE((offset >= 0) && (offset <= 255)); + + // str regSource, [regDest], #offset + Emit16((WORD)(0xf840 | dest)); + Emit16((WORD)(0x0b00 | (source << 12) | offset)); + } + + void ThumbEmitLoadOffsetScaledReg(ThumbReg dest, ThumbReg base, ThumbReg offset, int shift) + { + _ASSERTE(shift >=0 && shift <=3); + + Emit16((WORD)(0xf850 | base)); + Emit16((WORD)((dest << 12) | (shift << 4) | offset)); + } + + void ThumbEmitCallRegister(ThumbReg target) + { + // blx regTarget + Emit16((WORD)(0x4780 | (target << 3))); + } + + void ThumbEmitJumpRegister(ThumbReg target) + { + // bx regTarget + Emit16((WORD)(0x4700 | (target << 3))); + } + + void ThumbEmitMovRegReg(ThumbReg dest, ThumbReg source) + { + // mov regDest, regSource + Emit16((WORD)(0x4600 | ((dest > 7) ? 0x0080 : 0x0000) | (source << 3) | (dest & 0x0007))); + } + + //Assuming SP is only subtracted in prolog + void ThumbEmitSubSp(int value) + { + _ASSERTE(value >= 0); + _ASSERTE((value & 0x3) == 0); + + if(value < 512) + { + // encoding T1 + // sub sp, sp, #(value >> 2) + Emit16((WORD)(0xb080 | (value >> 2))); + } + else if(value < 4096) + { + // Using 32-bit encoding + Emit16((WORD)(0xf2ad| ((value & 0x0800) >> 1))); + Emit16((WORD)(0x0d00| ((value & 0x0700) << 4) | (value & 0x00ff))); + } + else + { + // For values >= 4K (pageSize) must check for guard page + +#ifndef CROSSGEN_COMPILE + // mov r4, value + ThumbEmitMovConstant(ThumbReg(4), value); + // mov r12, checkStack + ThumbEmitMovConstant(ThumbReg(12), (int)checkStack); + // bl r12 + ThumbEmitCallRegister(ThumbReg(12)); +#endif + + // sub sp,sp,r4 + Emit16((WORD)0xebad); + Emit16((WORD)0x0d04); + } + } + + void ThumbEmitAddSp(int value) + { + _ASSERTE(value >= 0); + _ASSERTE((value & 0x3) == 0); + + if(value < 512) + { + // encoding T2 + // add sp, sp, #(value >> 2) + Emit16((WORD)(0xb000 | (value >> 2))); + } + else if(value < 4096) + { + // Using 32-bit encoding T4 + Emit16((WORD)(0xf20d| ((value & 0x0800) >> 1))); + Emit16((WORD)(0x0d00| ((value & 0x0700) << 4) | (value & 0x00ff))); + } + else + { + //Must use temp register for values >=4096 + ThumbEmitMovConstant(ThumbReg(12), value); + // add sp,sp,r12 + Emit16((WORD)0x44e5); + } + } + + void ThumbEmitAddReg(ThumbReg dest, ThumbReg source) + { + + _ASSERTE(dest != source); + Emit16((WORD)(0x4400 | ((dest & 0x8)<<4) | (source<<3) | (dest & 0x7))); + } + + void ThumbEmitAdd(ThumbReg dest, ThumbReg source, unsigned int value) + { + + if(value<4096) + { + // addw dest, source, #value + unsigned int i = (value & 0x800) >> 11; + unsigned int imm3 = (value & 0x700) >> 8; + unsigned int imm8 = value & 0xff; + Emit16((WORD)(0xf200 | (i << 10) | source)); + Emit16((WORD)((imm3 << 12) | (dest << 8) | imm8)); + } + else + { + // if immediate is more than 4096 only ADD (register) will work + // move immediate to dest reg and call ADD(reg) + // this will not work if dest is same as source. + _ASSERTE(dest != source); + ThumbEmitMovConstant(dest, value); + ThumbEmitAddReg(dest, source); + } + } + + void ThumbEmitSub(ThumbReg dest, ThumbReg source, unsigned int value) + { + _ASSERTE(value < 4096); + + // subw dest, source, #value + unsigned int i = (value & 0x800) >> 11; + unsigned int imm3 = (value & 0x700) >> 8; + unsigned int imm8 = value & 0xff; + Emit16((WORD)(0xf2a0 | (i << 10) | source)); + Emit16((WORD)((imm3 << 12) | (dest << 8) | imm8)); + } + + void ThumbEmitCmpReg(ThumbReg reg1, ThumbReg reg2) + { + if(reg1 < 8 && reg2 <8) + { + Emit16((WORD)(0x4280 | reg2 << 3 | reg1)); + } + else + { + _ASSERTE(reg1 != ThumbReg(15) && reg2 != ThumbReg(15)); + Emit16((WORD)(0x4500 | reg2 << 3 | reg1 & 0x7 | (reg1 & 0x8 ? 0x80 : 0x0))); + } + } + + void ThumbEmitIncrement(ThumbReg dest, unsigned int value) + { + while (value) + { + if (value >= 4095) + { + // addw , , #4095 + ThumbEmitAdd(dest, dest, 4095); + value -= 4095; + } + else if (value <= 255) + { + // add , #value + Emit16((WORD)(0x3000 | (dest << 8) | value)); + break; + } + else + { + // addw , , #value + ThumbEmitAdd(dest, dest, value); + break; + } + } + } + + void ThumbEmitPush(WORD registers) + { + _ASSERTE(registers != 0); + _ASSERTE((registers & 0xa000) == 0); // Pushing SP or PC undefined + + // push {registers} + if (CountBits(registers) == 1) + { + // Encoding T3 (exactly one register, high or low) + WORD reg = 15; + while ((registers & (WORD)(1 << reg)) == 0) + { + reg--; + } + Emit16(0xf84d); + Emit16(0x0d04 | (reg << 12)); + } + else if ((registers & 0xbf00) == 0) + { + // Encoding T1 (low registers plus maybe LR) + Emit16(0xb400 | (registers & thumbRegLr.Mask() ? 0x0100: 0x0000) | (registers & 0x00ff)); + } + else + { + // Encoding T2 (two or more registers, high or low) + Emit16(0xe92d); + Emit16(registers); + } + } + + void ThumbEmitLoadStoreMultiple(ThumbReg base, bool load, WORD registers) + { + _ASSERTE(CountBits(registers) > 1); + _ASSERTE((registers & 0xFF00) == 0); // This only supports the small encoding + _ASSERTE(base < 8); // This only supports the small encoding + _ASSERTE((base.Mask() & registers) == 0); // This only supports the small encoding + + // (LDM|STM) base, {registers} + WORD flag = load ? 0x0800 : 0; + Emit16(0xc000 | flag | ((base & 7) << 8) | (registers & 0xFF)); + } + + void ThumbEmitPop(WORD registers) + { + _ASSERTE(registers != 0); + _ASSERTE((registers & 0xc000) != 0xc000); // Popping PC and LR together undefined + + // pop {registers} + if (CountBits(registers) == 1) + { + // Encoding T3 (exactly one register, high or low) + WORD reg = 15; + while ((registers & (WORD)(1 << reg)) == 0) + { + reg--; + } + Emit16(0xf85d); + Emit16(0x0b04 | (reg << 12)); + } + else if ((registers & 0x7f00) == 0) + { + // Encoding T1 (low registers plus maybe PC) + Emit16(0xbc00 | (registers & thumbRegPc.Mask() ? 0x0100: 0x0000) | (registers & 0x00ff)); + } + else + { + // Encoding T2 (two or more registers, high or low) + Emit16(0xe8bd); + Emit16(registers); + } + } + + void ThumbEmitLoadVFPSingleRegIndirect(ThumbVFPSingleReg dest, ThumbReg source, int offset) + { + _ASSERTE((offset >= -1020) && (offset <= 1020)); + _ASSERTE(offset%4==0); + + Emit16((WORD) (0xed10 | ((offset > 0 ? 0x1: 0x0) << 7) | ((dest & 0x1) << 6) | source)); + Emit16((WORD) (0x0a00 | ((dest & 0x1e) << 11) | (abs(offset)>>2))); + } + + void ThumbEmitLoadVFPDoubleRegIndirect(ThumbVFPDoubleReg dest, ThumbReg source, int offset) + { + _ASSERTE((offset >= -1020) && (offset <= 1020)); + _ASSERTE(offset%4==0); + + Emit16((WORD) (0xed10 | ((offset > 0 ? 0x1: 0x0) << 7) | ((dest & 0x10) << 6) | source)); + Emit16((WORD) (0x0b00 | ((dest & 0xf) << 12) | (abs(offset)>>2))); + } + +#ifdef FEATURE_INTERPRETER + void ThumbEmitStoreMultipleVFPDoubleReg(ThumbVFPDoubleReg source, ThumbReg dest, unsigned numRegs) + { + _ASSERTE((numRegs + source) <= 16); + + // The third nibble is 0x8; the 0x4 bit (D) is zero because the source reg number must be less + // than 16 for double registers. + Emit16((WORD) (0xec80 | 0x80 | dest)); + Emit16((WORD) (((source & 0xf) << 12) | 0xb00 | numRegs)); + } + + void ThumbEmitLoadMultipleVFPDoubleReg(ThumbVFPDoubleReg dest, ThumbReg source, unsigned numRegs) + { + _ASSERTE((numRegs + dest) <= 16); + + // The third nibble is 0x8; the 0x4 bit (D) is zero because the source reg number must be less + // than 16 for double registers. + Emit16((WORD) (0xec90 | 0x80 | source)); + Emit16((WORD) (((dest & 0xf) << 12) | 0xb00 | numRegs)); + } +#endif // FEATURE_INTERPRETER + + void EmitStubLinkFrame(TADDR pFrameVptr, int offsetOfFrame, int offsetOfTransitionBlock); + void EmitStubUnlinkFrame(); + + void ThumbEmitCondFlagJump(CodeLabel * target,UINT cond); + + void ThumbEmitCondRegJump(CodeLabel *target, BOOL nonzero, ThumbReg reg); + + void ThumbEmitNearJump(CodeLabel *target); + + // Scratches r12. + void ThumbEmitCallManagedMethod(MethodDesc *pMD, bool fTailcall); + + void EmitUnboxMethodStub(MethodDesc* pRealMD); + static UINT_PTR HashMulticastInvoke(MetaSig* pSig); + + void EmitMulticastInvoke(UINT_PTR hash); + void EmitSecureDelegateInvoke(UINT_PTR hash); + void EmitShuffleThunk(struct ShuffleEntry *pShuffleEntryArray); +#if defined(FEATURE_SHARE_GENERIC_CODE) + void EmitInstantiatingMethodStub(MethodDesc* pSharedMD, void* extra); +#endif // FEATURE_SHARE_GENERIC_CODE + + static Stub * CreateTailCallCopyArgsThunk(CORINFO_SIG_INFO * pSig, + CorInfoHelperTailCallSpecialHandling flags); + +private: + void ThumbCopyOneTailCallArg(UINT * pnSrcAlign, const ArgLocDesc * pArgLoc, UINT * pcbStackSpace); + void ThumbEmitCallWithGenericInstantiationParameter(MethodDesc *pMD, void *pHiddenArg); +}; + +extern "C" void SinglecastDelegateInvokeStub(); + +// SEH info forward declarations + +inline BOOL IsUnmanagedValueTypeReturnedByRef(UINT sizeofvaluetype) +{ + LIMITED_METHOD_CONTRACT; + + // structure that dont fit in the machine-word size are returned + // by reference. + return (sizeofvaluetype > 4); +} + +DECLSPEC_ALIGN(4) struct UMEntryThunkCode +{ + WORD m_code[4]; + + TADDR m_pTargetCode; + TADDR m_pvSecretParam; + + void Encode(BYTE* pTargetCode, void* pvSecretParam); + + LPCBYTE GetEntryPoint() const + { + LIMITED_METHOD_CONTRACT; + + return (LPCBYTE)((TADDR)this | THUMB_CODE); + } + + static int GetEntryPointOffset() + { + LIMITED_METHOD_CONTRACT; + + return 0; + } +}; + +// ClrFlushInstructionCache is used when we want to call FlushInstructionCache +// for a specific architecture in the common code, but not for other architectures. +// On IA64 ClrFlushInstructionCache calls the Kernel FlushInstructionCache function +// to flush the instruction cache. +// We call ClrFlushInstructionCache whenever we create or modify code in the heap. +// Currently ClrFlushInstructionCache has no effect on X86 +// + +inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode) +{ +#ifdef CROSSGEN_COMPILE + // The code won't be executed when we are cross-compiling so flush instruction cache is unnecessary + return TRUE; +#else + return FlushInstructionCache(GetCurrentProcess(), pCodeAddr, sizeOfCode); +#endif +} + +// +// JIT HELPER ALIASING FOR PORTABILITY. +// +// Create alias for optimized implementations of helpers provided on this platform +// +// optimized static helpers +#define JIT_GetSharedGCStaticBase JIT_GetSharedGCStaticBase_InlineGetAppDomain +#define JIT_GetSharedNonGCStaticBase JIT_GetSharedNonGCStaticBase_InlineGetAppDomain +#define JIT_GetSharedGCStaticBaseNoCtor JIT_GetSharedGCStaticBaseNoCtor_InlineGetAppDomain +#define JIT_GetSharedNonGCStaticBaseNoCtor JIT_GetSharedNonGCStaticBaseNoCtor_InlineGetAppDomain + +#define JIT_Stelem_Ref JIT_Stelem_Ref + +//------------------------------------------------------------------------ +// +// Precode definitions +// +//------------------------------------------------------------------------ +// +// Note: If you introduce new precode implementation below, then please +// update PrecodeStubManager::CheckIsStub_Internal to account for it. + +EXTERN_C VOID STDCALL PrecodeFixupThunk(); + +#define PRECODE_ALIGNMENT CODE_SIZE_ALIGN +#define SIZEOF_PRECODE_BASE CODE_SIZE_ALIGN +#define OFFSETOF_PRECODE_TYPE 0 + +// Invalid precode type +struct InvalidPrecode { + static const int Type = 0; +}; + +struct StubPrecode { + + static const int Type = 0xdf; + + // ldr r12, [pc, #8] ; =m_pMethodDesc + // ldr pc, [pc, #0] ; =m_pTarget + // dcd pTarget + // dcd pMethodDesc + WORD m_rgCode[4]; + TADDR m_pTarget; + TADDR m_pMethodDesc; + + void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator); + + TADDR GetMethodDesc() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pMethodDesc; + } + + PCODE GetTarget() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pTarget; + } + + BOOL SetTargetInterlocked(TADDR target, TADDR expected) + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + } + CONTRACTL_END; + + EnsureWritableExecutablePages(&m_pTarget); + return (TADDR)InterlockedCompareExchange( + (long*)&m_pTarget, (long)target, (long)expected) == expected; + } + +#ifdef FEATURE_PREJIT + void Fixup(DataImage *image); +#endif +}; +typedef DPTR(StubPrecode) PTR_StubPrecode; + + +struct NDirectImportPrecode { + + static const int Type = 0xe0; + + // ldr r12, [pc, #4] ; =m_pMethodDesc + // ldr pc, [pc, #4] ; =m_pTarget + // dcd pMethodDesc + // dcd pTarget + WORD m_rgCode[4]; + TADDR m_pMethodDesc; // Notice that the fields are reversed compared to StubPrecode. Precode::GetType + // takes advantage of this to detect NDirectImportPrecode. + TADDR m_pTarget; + + void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator); + + TADDR GetMethodDesc() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pMethodDesc; + } + + PCODE GetTarget() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pTarget; + } + + LPVOID GetEntrypoint() + { + LIMITED_METHOD_CONTRACT; + return (LPVOID)(dac_cast(this) + THUMB_CODE); + } + +#ifdef FEATURE_PREJIT + void Fixup(DataImage *image); +#endif +}; +typedef DPTR(NDirectImportPrecode) PTR_NDirectImportPrecode; + + +struct FixupPrecode { + + static const int Type = 0xfc; + + // mov r12, pc + // ldr pc, [pc, #4] ; =m_pTarget + // dcb m_MethodDescChunkIndex + // dcb m_PrecodeChunkIndex + // dcd m_pTarget + WORD m_rgCode[3]; + BYTE m_MethodDescChunkIndex; + BYTE m_PrecodeChunkIndex; + TADDR m_pTarget; + + void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator, int iMethodDescChunkIndex = 0, int iPrecodeChunkIndex = 0); + + TADDR GetBase() + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + return dac_cast(this) + (m_PrecodeChunkIndex + 1) * sizeof(FixupPrecode); + } + + TADDR GetMethodDesc(); + + PCODE GetTarget() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pTarget; + } + + BOOL SetTargetInterlocked(TADDR target, TADDR expected) + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + } + CONTRACTL_END; + + EnsureWritableExecutablePages(&m_pTarget); + return (TADDR)InterlockedCompareExchange( + (long*)&m_pTarget, (long)target, (long)expected) == expected; + } + + static BOOL IsFixupPrecodeByASM(PCODE addr) + { + PTR_WORD pInstr = dac_cast(PCODEToPINSTR(addr)); + + return + (pInstr[0] == 0x46fc) && + (pInstr[1] == 0xf8df) && + (pInstr[2] == 0xf004); + } + +#ifdef FEATURE_PREJIT + // Partial initialization. Used to save regrouped chunks. + void InitForSave(int iPrecodeChunkIndex); + + void Fixup(DataImage *image, MethodDesc * pMD); +#endif + +#ifdef DACCESS_COMPILE + void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); +#endif +}; +typedef DPTR(FixupPrecode) PTR_FixupPrecode; + + +// Precode to stuffle this and retbuf for closed delegates over static methods with return buffer +struct ThisPtrRetBufPrecode { + + static const int Type = 0x84; + + // mov r12, r0 + // mov r0, r1 + // mov r1, r12 + // ldr pc, [pc, #0] ; =m_pTarget + // dcd pTarget + // dcd pMethodDesc + WORD m_rgCode[6]; + TADDR m_pTarget; + TADDR m_pMethodDesc; + + void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator); + + TADDR GetMethodDesc() + { + LIMITED_METHOD_DAC_CONTRACT; + + return m_pMethodDesc; + } + + PCODE GetTarget() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pTarget; + } + + BOOL SetTargetInterlocked(TADDR target, TADDR expected) + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + } + CONTRACTL_END; + + EnsureWritableExecutablePages(&m_pTarget); + return FastInterlockCompareExchange((LONG*)&m_pTarget, (LONG)target, (LONG)expected) == (LONG)expected; + } +}; +typedef DPTR(ThisPtrRetBufPrecode) PTR_ThisPtrRetBufPrecode; + + +#ifdef HAS_REMOTING_PRECODE + +// Precode with embedded remoting interceptor +struct RemotingPrecode { + + static const int Type = 0x02; + + // push {r1,lr} + // ldr r1, [pc, #16] ; =m_pPrecodeRemotingThunk + // blx r1 + // pop {r1,lr} + // ldr pc, [pc, #12] ; =m_pLocalTarget + // nop ; padding for alignment + // dcd m_pMethodDesc + // dcd m_pPrecodeRemotingThunk + // dcd m_pLocalTarget + WORD m_rgCode[8]; + TADDR m_pMethodDesc; + TADDR m_pPrecodeRemotingThunk; + TADDR m_pLocalTarget; + + void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator = NULL); + + TADDR GetMethodDesc() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pMethodDesc; + } + + PCODE GetTarget() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pLocalTarget; + } + + BOOL SetTargetInterlocked(TADDR target, TADDR expected) + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + } + CONTRACTL_END; + + EnsureWritableExecutablePages(&m_pLocalTarget); + return FastInterlockCompareExchange((LONG*)&m_pLocalTarget, (LONG)target, (LONG)expected) == (LONG)expected; + } + +#ifdef FEATURE_PREJIT + void Fixup(DataImage *image, ZapNode *pCodeNode); +#endif +}; +typedef DPTR(RemotingPrecode) PTR_RemotingPrecode; + +EXTERN_C void PrecodeRemotingThunk(); + +#endif // HAS_REMOTING_PRECODE + +//********************************************************************** +// Miscellaneous +//********************************************************************** + +// Given the first halfword value of an ARM (Thumb) instruction (which is either an entire +// 16-bit instruction, or the high-order halfword of a 32-bit instruction), determine how many bytes +// the instruction is (2 or 4) and return that. +inline size_t GetARMInstructionLength(WORD instr) +{ + // From the ARM Architecture Reference Manual, A6.1 "Thumb instruction set encoding": + // If bits [15:11] of the halfword being decoded take any of the following values, the halfword is the first + // halfword of a 32-bit instruction: + // 0b11101 + // 0b11110 + // 0b11111 + // Otherwise, the halfword is a 16-bit instruction. + if ((instr & 0xf800) > 0xe000) + { + return 4; + } + else + { + return 2; + } +} + +// Given a pointer to an ARM (Thumb) instruction address, determine how many bytes +// the instruction is (2 or 4) and return that. +inline size_t GetARMInstructionLength(PBYTE pInstr) +{ + return GetARMInstructionLength(*(WORD*)pInstr); +} + +EXTERN_C void FCallMemcpy(byte* dest, byte* src, int len); + +#endif // __cgencpu_h__ diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm64/CallDescrWorkerARM64.asm b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm64/CallDescrWorkerARM64.asm new file mode 100644 index 00000000000000..0f031654e4f3d1 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm64/CallDescrWorkerARM64.asm @@ -0,0 +1,137 @@ +; Licensed to the .NET Foundation under one or more agreements. +; The .NET Foundation licenses this file to you under the MIT license. + +;; ==++== +;; + +;; +;; ==--== +#include "ksarm64.h" + +#include "asmconstants.h" + + + + IMPORT CallDescrWorkerUnwindFrameChainHandler +;;----------------------------------------------------------------------------- +;; This helper routine enregisters the appropriate arguments and makes the +;; actual call. +;;----------------------------------------------------------------------------- +;;void CallDescrWorkerInternal(CallDescrData * pCallDescrData); + NESTED_ENTRY CallDescrWorkerInternal,,CallDescrWorkerUnwindFrameChainHandler + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + PROLOG_SAVE_REG x19, #16 ;the stack slot at sp+24 is empty for 16 byte alligment + + mov x19, x0 ; save pCallDescrData in x19 + + ldr w1, [x19,#CallDescrData__numStackSlots] + cbz w1, Ldonestack + + ;; Add frame padding to ensure frame size is a multiple of 16 (a requirement of the OS ABI). + ;; We push two registers (above) and numStackSlots arguments (below). If this comes to an odd number + ;; of slots we must pad with another. This simplifies to "if the low bit of numStackSlots is set, + ;; extend the stack another eight bytes". + ldr x0, [x19,#CallDescrData__pSrc] + add x0, x0, x1 lsl #3 ; pSrcEnd=pSrc+8*numStackSlots + ands x2, x1, #1 + beq Lstackloop + + ;; This loop copies numStackSlots words + ;; from [pSrcEnd-8,pSrcEnd-16,...] to [sp-8,sp-16,...] + + ;; pad and store one stack slot as number of slots are odd + ldr x4, [x0,#-8]! + str x4, [sp,#-16]! + subs x1, x1, #1 + beq Ldonestack +Lstackloop + ldp x2, x4, [x0,#-16]! + stp x2, x4, [sp,#-16]! + subs x1, x1, #2 + bne Lstackloop +Ldonestack + + ;; If FP arguments are supplied in registers (x8 != NULL) then initialize all of them from the pointer + ;; given in x8. + ldr x8, [x19,#CallDescrData__pFloatArgumentRegisters] + cbz x8, LNoFloatingPoint + ldp d0, d1, [x8] + ldp d2, d3, [x8, #16] + ldp d4, d5, [x8, #32] + ldp d6, d7, [x8, #48] +LNoFloatingPoint + + ;; Copy [pArgumentRegisters, ..., pArgumentRegisters + 56] + ;; into x0, ..., x7 + + ldr x8, [x19,#CallDescrData__pArgumentRegisters] + ldp x0, x1, [x8] + ldp x2, x3, [x8, #16] + ldp x4, x5, [x8, #32] + ldp x6, x7, [x8, #48] + + ;; ARM64TODO: => see if anything special needs to be done for remoting + ;; call pTarget + ldr x8, [x19,#CallDescrData__pTarget] + blr x8 + + ldr w3, [x19,#CallDescrData__fpReturnSize] + + ;; Int return case + cbz w3, LIntReturn + + ;; Float return case + cmp w3, #4 + beq LFloatReturn + + ;; Double return case + cmp w3, #8 + bne LNoDoubleReturn + +LFloatReturn + str d0, [x19, #(CallDescrData__returnValue + 0)] + b LReturnDone + +LNoDoubleReturn + + ;;FloatHFAReturn return case + cmp w3, #16 + bne LNoFloatHFAReturn + + stp s0, s1, [x19, #(CallDescrData__returnValue + 0)] + stp s2, s3, [x19, #(CallDescrData__returnValue + 0x08)] + b LReturnDone +LNoFloatHFAReturn + + ;;DoubleHFAReturn return case + cmp w3, #32 + bne LNoDoubleHFAReturn + + stp d0, d1, [x19, #(CallDescrData__returnValue + 0)] + stp d2, d3, [x19, #(CallDescrData__returnValue + 0x10)] + b LReturnDone + +LNoDoubleHFAReturn + + EMIT_BREAKPOINT ; Unreachable + +LIntReturn + ;; Save return value into retbuf for int + str x0, [x19, #(CallDescrData__returnValue + 0)] + +LReturnDone + +#ifdef _DEBUG + ;; trash the floating point registers to ensure that the HFA return values + ;; won't survive by accident + ldp d0, d1, [sp] + ldp d2, d3, [sp, #16] +#endif + + EPILOG_STACK_RESTORE + EPILOG_RESTORE_REG x19, #16 ;the stack slot at sp+24 is empty for 16 byte alligment + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + NESTED_END + + END diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm64/asmconstants.h b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm64/asmconstants.h new file mode 100644 index 00000000000000..52bf6e9fb75979 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm64/asmconstants.h @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// asmconstants.h - +// +// This header defines field offsets and constants used by assembly code +// Be sure to rebuild clr/src/vm/ceemain.cpp after changing this file, to +// ensure that the constants match the expected C/C++ values + +// #ifndef _ARM64_ +// #error this file should only be used on an ARM platform +// #endif // _ARM64_ + +#include "..\..\inc\switches.h" + +//----------------------------------------------------------------------------- + +#ifndef ASMCONSTANTS_C_ASSERT +#define ASMCONSTANTS_C_ASSERT(cond) +#endif + +#ifndef ASMCONSTANTS_RUNTIME_ASSERT +#define ASMCONSTANTS_RUNTIME_ASSERT(cond) +#endif + +#define Thread__m_fPreemptiveGCDisabled 0x0C +#define Thread__m_pFrame 0x10 + +#ifndef CROSSGEN_COMPILE +ASMCONSTANTS_C_ASSERT(Thread__m_fPreemptiveGCDisabled == offsetof(Thread, m_fPreemptiveGCDisabled)); +ASMCONSTANTS_C_ASSERT(Thread__m_pFrame == offsetof(Thread, m_pFrame)); +#endif // CROSSGEN_COMPILE + +#define Thread_m_pFrame Thread__m_pFrame +#define Thread_m_fPreemptiveGCDisabled Thread__m_fPreemptiveGCDisabled + +#ifndef CROSSGEN_COMPILE +#define Thread__m_pDomain 0x20 +ASMCONSTANTS_C_ASSERT(Thread__m_pDomain == offsetof(Thread, m_pDomain)); + +#define AppDomain__m_dwId 0x08 +ASMCONSTANTS_C_ASSERT(AppDomain__m_dwId == offsetof(AppDomain, m_dwId)); +#endif + +#define METHODDESC_REGISTER x12 + +#define SIZEOF__ArgumentRegisters 0x40 +ASMCONSTANTS_C_ASSERT(SIZEOF__ArgumentRegisters == sizeof(ArgumentRegisters)) + +#define SIZEOF__FloatArgumentRegisters 0x40 +ASMCONSTANTS_C_ASSERT(SIZEOF__FloatArgumentRegisters == sizeof(FloatArgumentRegisters)) + +#define CallDescrData__pSrc 0x00 +#define CallDescrData__numStackSlots 0x08 +#define CallDescrData__pArgumentRegisters 0x10 +#define CallDescrData__pFloatArgumentRegisters 0x18 +#define CallDescrData__fpReturnSize 0x20 +#define CallDescrData__pTarget 0x28 +#define CallDescrData__returnValue 0x30 + +ASMCONSTANTS_C_ASSERT(CallDescrData__pSrc == offsetof(CallDescrData, pSrc)) +ASMCONSTANTS_C_ASSERT(CallDescrData__numStackSlots == offsetof(CallDescrData, numStackSlots)) +ASMCONSTANTS_C_ASSERT(CallDescrData__pArgumentRegisters == offsetof(CallDescrData, pArgumentRegisters)) +ASMCONSTANTS_C_ASSERT(CallDescrData__pFloatArgumentRegisters == offsetof(CallDescrData, pFloatArgumentRegisters)) +ASMCONSTANTS_C_ASSERT(CallDescrData__fpReturnSize == offsetof(CallDescrData, fpReturnSize)) +ASMCONSTANTS_C_ASSERT(CallDescrData__pTarget == offsetof(CallDescrData, pTarget)) +ASMCONSTANTS_C_ASSERT(CallDescrData__returnValue == offsetof(CallDescrData, returnValue)) + +#define CORINFO_NullReferenceException_ASM 0 +ASMCONSTANTS_C_ASSERT( CORINFO_NullReferenceException_ASM + == CORINFO_NullReferenceException); + + +// Offset of the array containing the address of captured registers in MachState +#define MachState__captureX19_X28 0x0 +ASMCONSTANTS_C_ASSERT(MachState__captureX19_X28 == offsetof(MachState, captureX19_X28)) + +// Offset of the array containing the address of preserved registers in MachState +#define MachState__ptrX19_X28 0x50 +ASMCONSTANTS_C_ASSERT(MachState__ptrX19_X28 == offsetof(MachState, ptrX19_X28)) + +#define MachState__isValid 0xb8 +ASMCONSTANTS_C_ASSERT(MachState__isValid == offsetof(MachState, _isValid)) + +#define LazyMachState_captureX19_X28 MachState__captureX19_X28 +ASMCONSTANTS_C_ASSERT(LazyMachState_captureX19_X28 == offsetof(LazyMachState, captureX19_X28)) + +#define LazyMachState_captureSp (MachState__isValid+8) // padding for alignment +ASMCONSTANTS_C_ASSERT(LazyMachState_captureSp == offsetof(LazyMachState, captureSp)) + +#define LazyMachState_captureIp (LazyMachState_captureSp+8) +ASMCONSTANTS_C_ASSERT(LazyMachState_captureIp == offsetof(LazyMachState, captureIp)) + +#define LazyMachState_captureFp (LazyMachState_captureSp+16) +ASMCONSTANTS_C_ASSERT(LazyMachState_captureFp == offsetof(LazyMachState, captureFp)) + +#define VASigCookie__pNDirectILStub 0x8 +ASMCONSTANTS_C_ASSERT(VASigCookie__pNDirectILStub == offsetof(VASigCookie, pNDirectILStub)) + +#define DelegateObject___methodPtr 0x18 +ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); + +#define DelegateObject___target 0x08 +ASMCONSTANTS_C_ASSERT(DelegateObject___target == offsetof(DelegateObject, _target)); + +#define SIZEOF__GSCookie 0x8 +ASMCONSTANTS_C_ASSERT(SIZEOF__GSCookie == sizeof(GSCookie)); + +#define SIZEOF__Frame 0x10 +ASMCONSTANTS_C_ASSERT(SIZEOF__Frame == sizeof(Frame)); + +#define SIZEOF__CONTEXT 0x390 +ASMCONSTANTS_C_ASSERT(SIZEOF__CONTEXT == sizeof(T_CONTEXT)); + + +#ifdef FEATURE_COMINTEROP + +#define SIZEOF__ComMethodFrame 0x68 +ASMCONSTANTS_C_ASSERT(SIZEOF__ComMethodFrame == sizeof(ComMethodFrame)); + +#define UnmanagedToManagedFrame__m_pvDatum 0x10 +ASMCONSTANTS_C_ASSERT(UnmanagedToManagedFrame__m_pvDatum == offsetof(UnmanagedToManagedFrame, m_pvDatum)); + +#endif // FEATURE_COMINTEROP + + +#define UMEntryThunk__m_pUMThunkMarshInfo 0x18 +ASMCONSTANTS_C_ASSERT(UMEntryThunk__m_pUMThunkMarshInfo == offsetof(UMEntryThunk, m_pUMThunkMarshInfo)) + +#define UMEntryThunk__m_dwDomainId 0x20 +ASMCONSTANTS_C_ASSERT(UMEntryThunk__m_dwDomainId == offsetof(UMEntryThunk, m_dwDomainId)) + +#define UMThunkMarshInfo__m_pILStub 0x00 +ASMCONSTANTS_C_ASSERT(UMThunkMarshInfo__m_pILStub == offsetof(UMThunkMarshInfo, m_pILStub)) + +#define UMThunkMarshInfo__m_cbActualArgSize 0x08 +ASMCONSTANTS_C_ASSERT(UMThunkMarshInfo__m_cbActualArgSize == offsetof(UMThunkMarshInfo, m_cbActualArgSize)) + +#define REDIRECTSTUB_SP_OFFSET_CONTEXT 0 + +#define CONTEXT_Pc 0x108 +ASMCONSTANTS_C_ASSERT(CONTEXT_Pc == offsetof(T_CONTEXT,Pc)) + +#define SIZEOF__FaultingExceptionFrame (SIZEOF__Frame + 0x10 + SIZEOF__CONTEXT) +#define FaultingExceptionFrame__m_fFilterExecuted SIZEOF__Frame +ASMCONSTANTS_C_ASSERT(SIZEOF__FaultingExceptionFrame == sizeof(FaultingExceptionFrame)); +ASMCONSTANTS_C_ASSERT(FaultingExceptionFrame__m_fFilterExecuted == offsetof(FaultingExceptionFrame, m_fFilterExecuted)); + +#undef ASMCONSTANTS_RUNTIME_ASSERT +#undef ASMCONSTANTS_C_ASSERT diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm64/cgencpu.h b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm64/cgencpu.h new file mode 100644 index 00000000000000..6141b0b7bdfeb0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/arm64/cgencpu.h @@ -0,0 +1,684 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + + +#ifndef _TARGET_ARM64_ +#error Should only include "cGenCpu.h" for ARM64 builds +#endif + +#ifndef __cgencpu_h__ +#define __cgencpu_h__ + +#define INSTRFMT_K64 +#include + +#define USE_REDIRECT_FOR_GCSTRESS + +EXTERN_C void getFPReturn(int fpSize, INT64 *pRetVal); +EXTERN_C void setFPReturn(int fpSize, INT64 retVal); + + +class ComCallMethodDesc; + + +#define COMMETHOD_PREPAD 24 // # extra bytes to allocate in addition to sizeof(ComCallMethodDesc) +#ifdef FEATURE_COMINTEROP +#define COMMETHOD_CALL_PRESTUB_SIZE 24 +#define COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET 16 // the offset of the call target address inside the prestub +#endif // FEATURE_COMINTEROP + +#define STACK_ALIGN_SIZE 16 + +#define JUMP_ALLOCATE_SIZE 16 // # bytes to allocate for a jump instruction +#define BACK_TO_BACK_JUMP_ALLOCATE_SIZE 16 // # bytes to allocate for a back to back jump instruction + +#define HAS_NDIRECT_IMPORT_PRECODE 1 + +#define USE_INDIRECT_CODEHEADER + +#ifdef FEATURE_REMOTING +#define HAS_REMOTING_PRECODE 1 +#endif + +//ARM64TODO: Enable it once we complete work on precode +//#define HAS_FIXUP_PRECODE 1 +//#define HAS_FIXUP_PRECODE_CHUNKS 1 + +// ThisPtrRetBufPrecode one is necessary for closed delegates over static methods with return buffer +#define HAS_THISPTR_RETBUF_PRECODE 1 + +//ARM64TODO: verify this +#define CODE_SIZE_ALIGN 8 +#define CACHE_LINE_SIZE 32 // As per Intel Optimization Manual the cache line size is 32 bytes +#define LOG2SLOT LOG2_PTRSIZE + +#define ENREGISTERED_RETURNTYPE_MAXSIZE 64 // bytes (maximum HFA size is 8 doubles) +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 8 // bytes +#define ENREGISTERED_PARAMTYPE_MAXSIZE 16 // bytes (max value type size that can be passed by value) + +#define CALLDESCR_ARGREGS 1 // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS 1 // CallDescrWorker has FloatArgumentRegisters parameter + +// Given a return address retrieved during stackwalk, +// this is the offset by which it should be decremented to arrive at the callsite. +#define STACKWALK_CONTROLPC_ADJUST_OFFSET 4 + +//======================================================================= +// IMPORTANT: This value is used to figure out how much to allocate +// for a fixed array of FieldMarshaler's. That means it must be at least +// as large as the largest FieldMarshaler subclass. This requirement +// is guarded by an assert. +//======================================================================= +//ARM64TODO: verify this +#define MAXFIELDMARSHALERSIZE 40 + +//********************************************************************** +// Parameter size +//********************************************************************** + +typedef INT64 StackElemType; +#define STACK_ELEM_SIZE sizeof(StackElemType) + +// !! This expression assumes STACK_ELEM_SIZE is a power of 2. +#define StackElemSize(parmSize) (((parmSize) + STACK_ELEM_SIZE - 1) & ~((ULONG)(STACK_ELEM_SIZE - 1))) + +//********************************************************************** +// Frames +//********************************************************************** + +//-------------------------------------------------------------------- +// This represents the callee saved (non-volatile) registers saved as +// of a FramedMethodFrame. +//-------------------------------------------------------------------- +typedef DPTR(struct CalleeSavedRegisters) PTR_CalleeSavedRegisters; +struct CalleeSavedRegisters { + INT64 x29; // frame pointer + INT64 x30; // link register + INT64 x19, x20, x21, x22, x23, x24, x25, x26, x27, x28; +}; + +//-------------------------------------------------------------------- +// This represents the arguments that are stored in volatile registers. +// This should not overlap the CalleeSavedRegisters since those are already +// saved separately and it would be wasteful to save the same register twice. +// If we do use a non-volatile register as an argument, then the ArgIterator +// will probably have to communicate this back to the PromoteCallerStack +// routine to avoid a double promotion. +//-------------------------------------------------------------------- +typedef DPTR(struct ArgumentRegisters) PTR_ArgumentRegisters; +struct ArgumentRegisters { + INT64 x[8]; // x0 ....x7 +}; +#define NUM_ARGUMENT_REGISTERS 8 + +#define ARGUMENTREGISTERS_SIZE sizeof(ArgumentRegisters) + + +//-------------------------------------------------------------------- +// This represents the floating point argument registers which are saved +// as part of the NegInfo for a FramedMethodFrame. Note that these +// might not be saved by all stubs: typically only those that call into +// C++ helpers will need to preserve the values in these volatile +// registers. +//-------------------------------------------------------------------- +typedef DPTR(struct FloatArgumentRegisters) PTR_FloatArgumentRegisters; +struct FloatArgumentRegisters { + // armV8 supports 32 floating point registers. Each register is 128bits long. + // It can be accessed as 128-bit value or 64-bit value(d0-d31) or as 32-bit value (s0-s31) + // or as 16-bit value or as 8-bit values. C# only has two builtin floating datatypes float(32-bit) and + // double(64-bit). It does not have a quad-precision floating point.So therefore it does not make sense to + // store full 128-bit values in Frame when the upper 64 bit will not contain any values. + double d[8]; // d0-d7 +}; + + +//********************************************************************** +// Exception handling +//********************************************************************** + +inline PCODE GetIP(const T_CONTEXT * context) { + LIMITED_METHOD_DAC_CONTRACT; + return context->Pc; +} + +inline void SetIP(T_CONTEXT *context, PCODE eip) { + LIMITED_METHOD_DAC_CONTRACT; + context->Pc = eip; +} + +inline TADDR GetSP(const T_CONTEXT * context) { + LIMITED_METHOD_DAC_CONTRACT; + return TADDR(context->Sp); +} + +inline PCODE GetLR(const T_CONTEXT * context) { + LIMITED_METHOD_DAC_CONTRACT; + return PCODE(context->Lr); +} + +extern "C" LPVOID __stdcall GetCurrentSP(); + +inline void SetSP(T_CONTEXT *context, TADDR esp) { + LIMITED_METHOD_DAC_CONTRACT; + context->Sp = DWORD(esp); +} + +inline void SetFP(T_CONTEXT *context, TADDR ebp) { + LIMITED_METHOD_DAC_CONTRACT; + context->Fp = DWORD(ebp); +} + +inline TADDR GetFP(const T_CONTEXT * context) +{ + LIMITED_METHOD_DAC_CONTRACT; + return (TADDR)(context->Fp); +} + +#ifdef FEATURE_COMINTEROP +void emitCOMStubCall (ComCallMethodDesc *pCOMMethod, PCODE target); +#endif // FEATURE_COMINTEROP + +//------------------------------------------------------------------------ +inline void emitJump(UINT32* pCode, LPVOID target) +{ + LIMITED_METHOD_CONTRACT; + + // We require 8-byte alignment so the LDR instruction is aligned properly + _ASSERTE(((UINT_PTR)pCode & 7) == 0); + + // +0: ldr x16, [pc, #8] + // +4: br x16 + // +8: [target address] + + pCode[0] = 0x58000050UL; // ldr x16, [pc, #8] + pCode[1] = 0xD61F0200UL; // br x16 + + *((LPVOID *)(pCode + 2)) = target; // 64-bit target address +} + +//------------------------------------------------------------------------ +// Given the same pBuffer that was used by emitJump this method +// decodes the instructions and returns the jump target +inline PCODE decodeJump(PCODE pCode) +{ + LIMITED_METHOD_CONTRACT; + + TADDR pInstr = PCODEToPINSTR(pCode); + + return *dac_cast(pInstr + 2*sizeof(DWORD)); +} + +//------------------------------------------------------------------------ +inline BOOL isJump(PCODE pCode) +{ + LIMITED_METHOD_DAC_CONTRACT; + + TADDR pInstr = PCODEToPINSTR(pCode); + + return *dac_cast(pInstr) == 0x58000050; +} + +//------------------------------------------------------------------------ +inline BOOL isBackToBackJump(PCODE pBuffer) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return isJump(pBuffer); +} + +//------------------------------------------------------------------------ +inline void emitBackToBackJump(LPBYTE pBuffer, LPVOID target) +{ + WRAPPER_NO_CONTRACT; + emitJump((UINT32*)pBuffer, target); +} + +//------------------------------------------------------------------------ +inline PCODE decodeBackToBackJump(PCODE pBuffer) +{ + WRAPPER_NO_CONTRACT; + return decodeJump(pBuffer); +} + +// SEH info forward declarations + +inline BOOL IsUnmanagedValueTypeReturnedByRef(UINT sizeofvaluetype) +{ +// ARM64TODO : Check if we need to consider HFA + return (sizeofvaluetype > 8); +} + + +//---------------------------------------------------------------------- + +struct IntReg +{ + int reg; + IntReg(int reg):reg(reg) + { + _ASSERTE(0 <= reg && reg < 32); + } + + operator int () { return reg; } + operator int () const { return reg; } + int operator == (IntReg other) { return reg == other.reg; } + int operator != (IntReg other) { return reg != other.reg; } + WORD Mask() const { return 1 << reg; } +}; + +struct VecReg +{ + int reg; + VecReg(int reg):reg(reg) + { + _ASSERTE(0 <= reg && reg < 32); + } + + operator int() { return reg; } + int operator == (VecReg other) { return reg == other.reg; } + int operator != (VecReg other) { return reg != other.reg; } + WORD Mask() const { return 1 << reg; } +}; + +struct CondCode +{ + int cond; + CondCode(int cond):cond(cond) + { + _ASSERTE(0 <= cond && cond < 16); + } +}; + +const IntReg RegTeb = IntReg(18); +const IntReg RegFp = IntReg(29); +const IntReg RegLr = IntReg(30); +// Note that stack pointer and zero register share the same encoding, 31 +const IntReg RegSp = IntReg(31); + +const CondCode CondEq = CondCode(0); +const CondCode CondNe = CondCode(1); +const CondCode CondCs = CondCode(2); +const CondCode CondCc = CondCode(3); +const CondCode CondMi = CondCode(4); +const CondCode CondPl = CondCode(5); +const CondCode CondVs = CondCode(6); +const CondCode CondVc = CondCode(7); +const CondCode CondHi = CondCode(8); +const CondCode CondLs = CondCode(9); +const CondCode CondGe = CondCode(10); +const CondCode CondLt = CondCode(11); +const CondCode CondGt = CondCode(12); +const CondCode CondLe = CondCode(13); +const CondCode CondAl = CondCode(14); +const CondCode CondNv = CondCode(15); + + +#define PRECODE_ALIGNMENT CODE_SIZE_ALIGN +#define SIZEOF_PRECODE_BASE CODE_SIZE_ALIGN +#define OFFSETOF_PRECODE_TYPE 0 + +#ifdef CROSSGEN_COMPILE +#define GetEEFuncEntryPoint(pfn) 0x1001 +#else +#define GetEEFuncEntryPoint(pfn) GFN_TADDR(pfn) +#endif + +class StubLinkerCPU : public StubLinker +{ + +private: + void EmitLoadStoreRegPairImm(DWORD flags, int regNum1, int regNum2, IntReg Xn, int offset, BOOL isVec); + void EmitLoadStoreRegImm(DWORD flags, int regNum, IntReg Xn, int offset, BOOL isVec); +public: + + // BitFlags for EmitLoadStoreReg(Pair)Imm methods + enum { + eSTORE = 0x0, + eLOAD = 0x1, + eWRITEBACK = 0x2, + ePOSTINDEX = 0x4, + eFLAGMASK = 0x7 + }; + + // BitFlags for Register offsetted loads/stores + // Bits(1-3) indicate the encoding, while the bits(0) indicate the shift + enum { + eSHIFT = 0x1, // 0y0001 + eUXTW = 0x4, // 0y0100 + eSXTW = 0xC, // 0y1100 + eLSL = 0x7, // 0y0111 + eSXTX = 0xD, // 0y1110 + }; + + + static void Init(); + + void EmitUnboxMethodStub(MethodDesc* pRealMD); + void EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall); + void EmitCallLabel(CodeLabel *target, BOOL fTailCall, BOOL fIndirect); + void EmitSecureDelegateInvoke(UINT_PTR hash); + static UINT_PTR HashMulticastInvoke(MetaSig* pSig); + void EmitShuffleThunk(struct ShuffleEntry *pShuffleEntryArray); + void EmitGetThreadInlined(IntReg Xt); + +#ifdef _DEBUG + void EmitNop() { Emit32(0xD503201F); } +#endif + void EmitBreakPoint() { Emit32(0xD43E0000); } + void EmitMovConstant(IntReg target, UINT64 constant); + void EmitCmpImm(IntReg reg, int imm); + void EmitCmpReg(IntReg Xn, IntReg Xm); + void EmitCondFlagJump(CodeLabel * target, UINT cond); + void EmitJumpRegister(IntReg regTarget); + void EmitMovReg(IntReg dest, IntReg source); + + void EmitSubImm(IntReg Xd, IntReg Xn, unsigned int value); + void EmitAddImm(IntReg Xd, IntReg Xn, unsigned int value); + + void EmitLoadStoreRegPairImm(DWORD flags, IntReg Xt1, IntReg Xt2, IntReg Xn, int offset=0); + void EmitLoadStoreRegPairImm(DWORD flags, VecReg Vt1, VecReg Vt2, IntReg Xn, int offset=0); + + void EmitLoadStoreRegImm(DWORD flags, IntReg Xt, IntReg Xn, int offset=0); + void EmitLoadStoreRegImm(DWORD flags, VecReg Vt, IntReg Xn, int offset=0); + + void EmitLoadRegReg(IntReg Xt, IntReg Xn, IntReg Xm, DWORD option); + + void EmitCallRegister(IntReg reg); + void EmitProlog(unsigned short cIntRegArgs, + unsigned short cVecRegArgs, + unsigned short cCalleeSavedRegs, + unsigned short cbStackSpace = 0); + + void EmitEpilog(); + + void EmitRet(IntReg reg); + + +}; + +extern "C" void SinglecastDelegateInvokeStub(); + + +// preferred alignment for data +//ARM64TODO: double check +#define DATA_ALIGNMENT 8 + + +DECLSPEC_ALIGN(16) struct UMEntryThunkCode +{ + DWORD m_code[4]; + + TADDR m_pTargetCode; + TADDR m_pvSecretParam; + + void Encode(BYTE* pTargetCode, void* pvSecretParam); + + LPCBYTE GetEntryPoint() const + { + LIMITED_METHOD_CONTRACT; + + return (LPCBYTE)this; + } + + static int GetEntryPointOffset() + { + LIMITED_METHOD_CONTRACT; + + return 0; + } +}; + +inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode) +{ +#ifdef CROSSGEN_COMPILE + // The code won't be executed when we are cross-compiling so flush instruction cache is unnecessary + return TRUE; +#else + return FlushInstructionCache(GetCurrentProcess(), pCodeAddr, sizeOfCode); +#endif +} +EXTERN_C VOID STDCALL PrecodeFixupThunk(); + +// Invalid precode type +struct InvalidPrecode { + static const int Type = 0; +}; + +struct StubPrecode { + + static const int Type = 0x89; + + // adr x9, #16 + // ldp x10,x12,[x9] ; =m_pTarget,m_pMethodDesc + // br x10 + // 4 byte padding for 8 byte allignement + // dcd pTarget + // dcd pMethodDesc + DWORD m_rgCode[4]; + TADDR m_pTarget; + TADDR m_pMethodDesc; + + void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator); + + TADDR GetMethodDesc() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pMethodDesc; + } + + PCODE GetTarget() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pTarget; + } + + BOOL SetTargetInterlocked(TADDR target, TADDR expected) + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + } + CONTRACTL_END; + + EnsureWritableExecutablePages(&m_pTarget); + return (TADDR)InterlockedCompareExchange( + (TADDR*)&m_pTarget, (TADDR)target, (TADDR)expected) == expected; + } + +#ifdef FEATURE_PREJIT + void Fixup(DataImage *image); +#endif +}; +typedef DPTR(StubPrecode) PTR_StubPrecode; + + +struct NDirectImportPrecode { + + static const int Type = 0x88; + + // adr x8, #16 ; Notice that x8 register is used to differentiate the stub from StubPrecode which uses x9 + // ldp x10,x12,[x8] ; =m_pTarget,m_pMethodDesc + // br x10 + // 4 byte padding for 8 byte allignement + // dcd pTarget + // dcd pMethodDesc + DWORD m_rgCode[4]; + TADDR m_pTarget; + TADDR m_pMethodDesc; + + void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator); + + TADDR GetMethodDesc() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pMethodDesc; + } + + PCODE GetTarget() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_pTarget; + } + + LPVOID GetEntrypoint() + { + LIMITED_METHOD_CONTRACT; + return this; + } + +#ifdef FEATURE_PREJIT + void Fixup(DataImage *image); +#endif +}; +typedef DPTR(NDirectImportPrecode) PTR_NDirectImportPrecode; + + +struct FixupPrecode { + + static const int Type = 0xfc; + + // mov r12, pc + // ldr pc, [pc, #4] ; =m_pTarget + // dcb m_MethodDescChunkIndex + // dcb m_PrecodeChunkIndex + // dcd m_pTarget + WORD m_rgCode[3]; + BYTE m_MethodDescChunkIndex; + BYTE m_PrecodeChunkIndex; + TADDR m_pTarget; + + void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator, int iMethodDescChunkIndex = 0, int iPrecodeChunkIndex = 0); + + TADDR GetBase() + { + _ASSERTE(!"ARM64:NYI"); + return NULL; + } + + TADDR GetMethodDesc(); + + PCODE GetTarget() + { + _ASSERTE(!"ARM64:NYI"); + return NULL; + } + + BOOL SetTargetInterlocked(TADDR target, TADDR expected) + { + _ASSERTE(!"ARM64:NYI"); + return NULL; + } + + static BOOL IsFixupPrecodeByASM(PCODE addr) + { + _ASSERTE(!"ARM64:NYI"); + return NULL; + } + +#ifdef FEATURE_PREJIT + // Partial initialization. Used to save regrouped chunks. + void InitForSave(int iPrecodeChunkIndex); + + void Fixup(DataImage *image, MethodDesc * pMD); +#endif + +#ifdef DACCESS_COMPILE + void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); +#endif +}; +typedef DPTR(FixupPrecode) PTR_FixupPrecode; + + +// Precode to stuffle this and retbuf for closed delegates over static methods with return buffer +struct ThisPtrRetBufPrecode { + + static const int Type = 0x84; + + // mov r12, r0 + // mov r0, r1 + // mov r1, r12 + // ldr pc, [pc, #0] ; =m_pTarget + // dcd pTarget + // dcd pMethodDesc + WORD m_rgCode[6]; + TADDR m_pTarget; + TADDR m_pMethodDesc; + + void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator); + + TADDR GetMethodDesc() + { + _ASSERTE(!"ARM64:NYI"); + return NULL; + } + + PCODE GetTarget() + { + _ASSERTE(!"ARM64:NYI"); + return NULL; + } + + BOOL SetTargetInterlocked(TADDR target, TADDR expected) + { + _ASSERTE(!"ARM64:NYI"); + return NULL; + } +}; +typedef DPTR(ThisPtrRetBufPrecode) PTR_ThisPtrRetBufPrecode; + + +#ifdef HAS_REMOTING_PRECODE + +// Precode with embedded remoting interceptor +struct RemotingPrecode { + + static const int Type = 0x02; + + // push {r1,lr} + // ldr r1, [pc, #16] ; =m_pPrecodeRemotingThunk + // blx r1 + // pop {r1,lr} + // ldr pc, [pc, #12] ; =m_pLocalTarget + // nop ; padding for alignment + // dcd m_pMethodDesc + // dcd m_pPrecodeRemotingThunk + // dcd m_pLocalTarget + WORD m_rgCode[8]; + TADDR m_pMethodDesc; + TADDR m_pPrecodeRemotingThunk; + TADDR m_pLocalTarget; + + void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator = NULL); + + TADDR GetMethodDesc() + { + _ASSERTE(!"ARM64:NYI"); + return NULL; + } + + PCODE GetTarget() + { + _ASSERTE(!"ARM64:NYI"); + return NULL; + } + + BOOL SetTargetInterlocked(TADDR target, TADDR expected) + { + _ASSERTE(!"ARM64:NYI"); + return NULL; + } + +#ifdef FEATURE_PREJIT + void Fixup(DataImage *image, ZapNode *pCodeNode); +#endif +}; +typedef DPTR(RemotingPrecode) PTR_RemotingPrecode; + +EXTERN_C void PrecodeRemotingThunk(); + +#endif // HAS_REMOTING_PRECODE + + +#endif // __cgencpu_h__ diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/callhelpers.cpp b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/callhelpers.cpp new file mode 100644 index 00000000000000..ae37523742b052 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/callhelpers.cpp @@ -0,0 +1,682 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +/* + * CallHelpers.CPP: helpers to call managed code + * + + */ + +#include "common.h" +#include "dbginterface.h" + +// To include declaration of "AppDomainTransitionExceptionFilter" +#include "excep.h" + +// To include declaration of "SignatureNative" +#include "runtimehandles.h" + + +#if defined(FEATURE_MULTICOREJIT) && defined(_DEBUG) + +// Allow system module, and first party WinMD files for Appx + +void AssertMulticoreJitAllowedModule(PCODE pTarget) +{ + CONTRACTL + { + SO_NOT_MAINLINE; + } + CONTRACTL_END; + + MethodDesc* pMethod = Entry2MethodDesc(pTarget, NULL); + + Module * pModule = pMethod->GetModule_NoLogging(); + +#if defined(FEATURE_APPX_BINDER) + + // For Appx process, allow certain modules to load on background thread + if (AppX::IsAppXProcess()) + { + if (MulticoreJitManager::IsLoadOkay(pModule)) + { + return; + } + } +#endif + + _ASSERTE(pModule->IsSystem()); +} + +#endif + +// For X86, INSTALL_COMPLUS_EXCEPTION_HANDLER grants us sufficient protection to call into +// managed code. +// +// But on 64-bit, the personality routine will not pop frames or trackers as exceptions unwind +// out of managed code. Instead, we rely on explicit cleanup like CLRException::HandlerState::CleanupTry +// or UMThunkUnwindFrameChainHandler. +// +// So most callers should call through CallDescrWorkerWithHandler (or a wrapper like MethodDesc::Call) +// and get the platform-appropriate exception handling. A few places try to optimize by calling direct +// to managed methods (see ArrayInitializeWorker or FastCallFinalize). This sort of thing is +// dangerous. You have to worry about marking yourself as a legal managed caller and you have to +// worry about how exceptions will be handled on a WIN64EXCEPTIONS plan. It is generally only suitable +// for X86. + +//******************************************************************************* +void CallDescrWorkerWithHandler( + CallDescrData * pCallDescrData, + BOOL fCriticalCall) +{ + STATIC_CONTRACT_SO_INTOLERANT; + +#if defined(FEATURE_MULTICOREJIT) && defined(_DEBUG) + + // For multicore JITting, background thread should not call managed code, except when calling system code (e.g. throwing managed exception) + if (GetThread()->HasThreadStateNC(Thread::TSNC_CallingManagedCodeDisabled)) + { + AssertMulticoreJitAllowedModule(pCallDescrData->pTarget); + } + +#endif + + + BEGIN_CALL_TO_MANAGEDEX(fCriticalCall ? EEToManagedCriticalCall : EEToManagedDefault); + + CallDescrWorker(pCallDescrData); + + END_CALL_TO_MANAGED(); +} + + +#if !defined(_WIN64) && defined(_DEBUG) + +//******************************************************************************* +// assembly code, in i386/asmhelpers.asm +void CallDescrWorker(CallDescrData * pCallDescrData) +{ + // + // This function must not have a contract ... it's caller has pushed an FS:0 frame (COMPlusFrameHandler) that must + // be the first handler on the stack. The contract causes, at a minimum, a C++ exception handler to be pushed to + // handle the destruction of the contract object. If there is an exception in the managed code called from here, + // and that exception is handled in that same block of managed code, then the COMPlusFrameHandler will actually + // unwind the C++ handler before branching to the catch clause in managed code. That essentially causes an + // out-of-order destruction of the contract object, resulting in very odd crashes later. + // +#if 0 + CONTRACTL { + THROWS; + GC_TRIGGERS; + } CONTRACTL_END; +#endif // 0 + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_SO_TOLERANT; + + _ASSERTE(!NingenEnabled() && "You cannot invoke managed code inside the ngen compilation process."); + + TRIGGERSGC_NOSTOMP(); // Can't stomp object refs because they are args to the function + + // Save a copy of dangerousObjRefs in table. + Thread* curThread; + DWORD_PTR ObjRefTable[OBJREF_TABSIZE]; + + curThread = GetThread(); + _ASSERTE(curThread != NULL); + + static_assert_no_msg(sizeof(curThread->dangerousObjRefs) == sizeof(ObjRefTable)); + memcpy(ObjRefTable, curThread->dangerousObjRefs, sizeof(ObjRefTable)); + +#ifndef FEATURE_INTERPRETER + // When the interpreter is used, this mayb be called from preemptive code. + _ASSERTE(curThread->PreemptiveGCDisabled()); // Jitted code expects to be in cooperative mode +#endif + + // If the current thread owns spinlock or unbreakable lock, it cannot call managed code. + _ASSERTE(!curThread->HasUnbreakableLock() && + (curThread->m_StateNC & Thread::TSNC_OwnsSpinLock) == 0); + +#ifdef _TARGET_ARM_ + _ASSERTE(IsThumbCode(pCallDescrData->pTarget)); +#endif + + CallDescrWorkerInternal(pCallDescrData); + + // Restore dangerousObjRefs when we return back to EE after call + memcpy(curThread->dangerousObjRefs, ObjRefTable, sizeof(ObjRefTable)); + + TRIGGERSGC(); + + ENABLESTRESSHEAP(); +} +#endif // !defined(_WIN64) && defined(_DEBUG) + +void DispatchCallDebuggerWrapper( + CallDescrData * pCallDescrData, + ContextTransitionFrame* pFrame, + BOOL fCriticalCall +) +{ + // Use static contracts b/c we have SEH. + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_COOPERATIVE; + + struct Param : NotifyOfCHFFilterWrapperParam + { + CallDescrData * pCallDescrData; + BOOL fCriticalCall; + } param; + + param.pFrame = pFrame; + param.pCallDescrData = pCallDescrData; + param.fCriticalCall = fCriticalCall; + + PAL_TRY(Param *, pParam, ¶m) + { + CallDescrWorkerWithHandler( + pParam->pCallDescrData, + pParam->fCriticalCall); + } + PAL_EXCEPT_FILTER(AppDomainTransitionExceptionFilter) + { + // Should never reach here b/c handler should always continue search. + _ASSERTE(!"Unreachable"); + } + PAL_ENDTRY +} + +// Helper for VM->managed calls with simple signatures. +void * DispatchCallSimple( + SIZE_T *pSrc, + DWORD numStackSlotsToCopy, + PCODE pTargetAddress, + DWORD dwDispatchCallSimpleFlags) +{ + CONTRACTL + { + GC_TRIGGERS; + THROWS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + +#ifdef DEBUGGING_SUPPORTED + if (CORDebuggerTraceCall()) + g_pDebugInterface->TraceCall((const BYTE *)pTargetAddress); +#endif // DEBUGGING_SUPPORTED + + CallDescrData callDescrData; + +#ifdef CALLDESCR_ARGREGS + callDescrData.pSrc = pSrc + NUM_ARGUMENT_REGISTERS; + callDescrData.numStackSlots = numStackSlotsToCopy; + callDescrData.pArgumentRegisters = (ArgumentRegisters *)pSrc; +#else + callDescrData.pSrc = pSrc; + callDescrData.numStackSlots = numStackSlotsToCopy; +#endif +#ifdef CALLDESCR_FPARGREGS + callDescrData.pFloatArgumentRegisters = NULL; +#endif +#ifdef CALLDESCR_REGTYPEMAP + callDescrData.dwRegTypeMap = 0; +#endif + callDescrData.fpReturnSize = 0; + callDescrData.pTarget = pTargetAddress; + + if ((dwDispatchCallSimpleFlags & DispatchCallSimple_CatchHandlerFoundNotification) != 0) + { + DispatchCallDebuggerWrapper( + &callDescrData, + NULL, + dwDispatchCallSimpleFlags & DispatchCallSimple_CriticalCall); + } + else + { + CallDescrWorkerWithHandler(&callDescrData, dwDispatchCallSimpleFlags & DispatchCallSimple_CriticalCall); + } + + return *(void **)(&callDescrData.returnValue); +} + +// This method performs the proper profiler and debugger callbacks before dispatching the +// call. The caller has the responsibility of furnishing the target address, register and stack arguments. +// Stack arguments should be in reverse order, and pSrc should point to past the last argument +// Returns the return value or the exception object if one was thrown. +void DispatchCall( + CallDescrData * pCallDescrData, + OBJECTREF *pRefException, + ContextTransitionFrame* pFrame /* = NULL */ +#ifdef FEATURE_CORRUPTING_EXCEPTIONS + , CorruptionSeverity *pSeverity /*= NULL*/ +#endif // FEATURE_CORRUPTING_EXCEPTIONS + ) +{ + CONTRACTL + { + GC_TRIGGERS; + THROWS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + +#ifdef DEBUGGING_SUPPORTED + if (CORDebuggerTraceCall()) + g_pDebugInterface->TraceCall((const BYTE *)pCallDescrData->pTarget); +#endif // DEBUGGING_SUPPORTED + +#ifdef FEATURE_CORRUPTING_EXCEPTIONS + if (pSeverity != NULL) + { + // By default, assume any exception that comes out is NotCorrupting + *pSeverity = NotCorrupting; + } +#endif // FEATURE_CORRUPTING_EXCEPTIONS + + EX_TRY + { + DispatchCallDebuggerWrapper(pCallDescrData, + pFrame, + FALSE); + } + EX_CATCH + { + *pRefException = GET_THROWABLE(); + +#ifdef FEATURE_CORRUPTING_EXCEPTIONS + if (pSeverity != NULL) + { + // By default, assume any exception that comes out is NotCorrupting + *pSeverity = GetThread()->GetExceptionState()->GetLastActiveExceptionCorruptionSeverity(); + } +#endif // FEATURE_CORRUPTING_EXCEPTIONS + + } + EX_END_CATCH(RethrowTransientExceptions); +} + +#ifdef CALLDESCR_REGTYPEMAP +//******************************************************************************* +void FillInRegTypeMap(int argOffset, CorElementType typ, BYTE * pMap) +{ + CONTRACTL + { + WRAPPER(THROWS); + WRAPPER(GC_TRIGGERS); + MODE_ANY; + PRECONDITION(CheckPointer(pMap, NULL_NOT_OK)); + } + CONTRACTL_END; + + int regArgNum = TransitionBlock::GetArgumentIndexFromOffset(argOffset); + + // Create a map of the first 8 argument types. This is used in + // CallDescrWorkerInternal to load args into general registers or + // floating point registers. + // + // we put these in order from the LSB to the MSB so that we can keep + // the map in a register and just examine the low byte and then shift + // right for each arg. + + if (regArgNum < NUM_ARGUMENT_REGISTERS) + { + pMap[regArgNum] = typ; + } +} +#endif // CALLDESCR_REGTYPEMAP + +#if defined(_DEBUG) && defined(FEATURE_COMINTEROP) +extern int g_fMainThreadApartmentStateSet; +extern int g_fInitializingInitialAD; +extern Volatile g_fInExecuteMainMethod; +#endif + +//******************************************************************************* +#ifdef FEATURE_INTERPRETER +ARG_SLOT MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, bool transitionToPreemptive) +#else +ARG_SLOT MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments) +#endif +{ + // + // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + // + // This method needs to have a GC_TRIGGERS contract because it + // calls managed code. However, IT MAY NOT TRIGGER A GC ITSELF + // because the argument array is not protected and may contain gc + // refs. + // + // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + // + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + MODE_COOPERATIVE; + PRECONDITION(GetAppDomain()->CheckCanExecuteManagedCode(m_pMD)); + PRECONDITION(m_pMD->CheckActivated()); // EnsureActive will trigger, so we must already be activated + +#ifdef FEATURE_COMINTEROP + // If we're an exe, then we must either be initializing the first AD, or have already setup the main thread's + // COM apartment state. + // If you hit this assert, then you likely introduced code during startup that could inadvertently + // initialize the COM apartment state of the main thread before we set it based on the user attribute. + PRECONDITION(g_fInExecuteMainMethod ? (g_fMainThreadApartmentStateSet || g_fInitializingInitialAD) : TRUE); +#endif // FEATURE_COMINTEROP + } + CONTRACTL_END; + + _ASSERTE(!NingenEnabled() && "You cannot invoke managed code inside the ngen compilation process."); + + // If we're invoking an mscorlib method, lift the restriction on type load limits. Calls into mscorlib are + // typically calls into specific and controlled helper methods for security checks and other linktime tasks. + // + // @todo: In an ideal world, we would require each of those sites to do the override rather than disabling + // the assert broadly here. However, by limiting the override to mscorlib methods, we should still be able + // to effectively enforce the more general rule about loader recursion. + MAYBE_OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED, m_pMD->GetModule()->IsSystem()); + + LPBYTE pTransitionBlock; + UINT nStackBytes; + UINT fpReturnSize; +#ifdef CALLDESCR_REGTYPEMAP + UINT64 dwRegTypeMap; +#endif +#ifdef CALLDESCR_FPARGREGS + FloatArgumentRegisters *pFloatArgumentRegisters = NULL; +#endif + void* pvRetBuff = NULL; + + { + // + // the incoming argument array is not gc-protected, so we + // may not trigger a GC before we actually call managed code + // + GCX_FORBID(); + + // Record this call if required + g_IBCLogger.LogMethodDescAccess(m_pMD); + + // + // All types must already be loaded. This macro also sets up a FAULT_FORBID region which is + // also required for critical calls since we cannot inject any failure points between the + // caller of MethodDesc::CallDescr and the actual transition to managed code. + // + ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); + + _ASSERTE(GetAppDomain()->ShouldHaveCode()); + +#ifdef FEATURE_INTERPRETER + _ASSERTE(isCallConv(m_methodSig.GetCallingConvention(), IMAGE_CEE_CS_CALLCONV_DEFAULT) + || isCallConv(m_methodSig.GetCallingConvention(), CorCallingConvention(IMAGE_CEE_CS_CALLCONV_C)) + || isCallConv(m_methodSig.GetCallingConvention(), CorCallingConvention(IMAGE_CEE_CS_CALLCONV_VARARG)) + || isCallConv(m_methodSig.GetCallingConvention(), CorCallingConvention(IMAGE_CEE_CS_CALLCONV_NATIVEVARARG)) + || isCallConv(m_methodSig.GetCallingConvention(), CorCallingConvention(IMAGE_CEE_CS_CALLCONV_STDCALL))); +#else + _ASSERTE(isCallConv(m_methodSig.GetCallingConvention(), IMAGE_CEE_CS_CALLCONV_DEFAULT)); + _ASSERTE(!(m_methodSig.GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE)); +#endif + +#ifdef DEBUGGING_SUPPORTED + if (CORDebuggerTraceCall()) + { + g_pDebugInterface->TraceCall((const BYTE *)m_pCallTarget); + } +#endif // DEBUGGING_SUPPORTED + +#if CHECK_APP_DOMAIN_LEAKS + if (g_pConfig->AppDomainLeaks()) + { + // See if we are in the correct domain to call on the object + if (m_methodSig.HasThis() && !m_pMD->GetMethodTable()->IsValueType()) + { + CONTRACT_VIOLATION(ThrowsViolation|GCViolation|FaultViolation); + OBJECTREF pThis = ArgSlotToObj(pArguments[0]); + if (!pThis->AssignAppDomain(GetAppDomain())) + _ASSERTE(!"Attempt to call method on object in wrong domain"); + } + } +#endif // CHECK_APP_DOMAIN_LEAKS + +#ifdef _DEBUG + { + // The metasig should be reset + _ASSERTE(m_methodSig.GetArgNum() == 0); + + // Check to see that any value type args have been loaded and restored. + // This is because we may be calling a FramedMethodFrame which will use the sig + // to trace the args, but if any are unloaded we will be stuck if a GC occurs. + _ASSERTE(m_pMD->IsRestored_NoLogging()); + CorElementType argType; + while ((argType = m_methodSig.NextArg()) != ELEMENT_TYPE_END) + { + if (argType == ELEMENT_TYPE_VALUETYPE) + { + TypeHandle th = m_methodSig.GetLastTypeHandleThrowing(ClassLoader::DontLoadTypes); + CONSISTENCY_CHECK(th.CheckFullyLoaded()); + CONSISTENCY_CHECK(th.IsRestored_NoLogging()); + } + } + m_methodSig.Reset(); + } +#endif // _DEBUG + + DWORD arg = 0; + + nStackBytes = m_argIt.SizeOfFrameArgumentArray(); + + // Create a fake FramedMethodFrame on the stack. + + // Note that SizeOfFrameArgumentArray does overflow checks with sufficient margin to prevent overflows here + DWORD dwAllocaSize = TransitionBlock::GetNegSpaceSize() + sizeof(TransitionBlock) + nStackBytes; + + LPBYTE pAlloc = (LPBYTE)_alloca(dwAllocaSize); + + pTransitionBlock = pAlloc + TransitionBlock::GetNegSpaceSize(); + +#ifdef CALLDESCR_REGTYPEMAP + dwRegTypeMap = 0; + BYTE* pMap = (BYTE*)&dwRegTypeMap; +#endif // CALLDESCR_REGTYPEMAP + + if (m_argIt.HasThis()) + { + *((LPVOID*)(pTransitionBlock + m_argIt.GetThisOffset())) = ArgSlotToPtr(pArguments[arg++]); + } + + if (m_argIt.HasRetBuffArg()) + { + *((LPVOID*)(pTransitionBlock + m_argIt.GetRetBuffArgOffset())) = ArgSlotToPtr(pArguments[arg++]); + } +#ifdef FEATURE_HFA +#ifdef FEATURE_INTERPRETER + // Something is necessary for HFA's, but what's below (in the FEATURE_INTERPRETER ifdef) + // doesn't seem to do the proper test. It fires, + // incorrectly, for a one-word struct that *doesn't* have a ret buff. So we'll try this, instead: + // We're here because it doesn't have a ret buff. If it would, except that the struct being returned + // is an HFA, *then* assume the invoker made this slot a ret buff pointer. + // It's an HFA if the return type is a struct, but it has a non-zero FP return size. + // (If it were an HFA, but had a ret buff because it was varargs, then we wouldn't be here. + // Also this test won't work for float enums. + else if (m_methodSig.GetReturnType() == ELEMENT_TYPE_VALUETYPE + && m_argIt.GetFPReturnSize() > 0) +#else // FEATURE_INTERPRETER + else if (ELEMENT_TYPE_VALUETYPE == m_methodSig.GetReturnTypeNormalized()) +#endif // FEATURE_INTERPRETER + { + pvRetBuff = ArgSlotToPtr(pArguments[arg++]); + } +#endif // FEATURE_HFA + + +#ifdef FEATURE_INTERPRETER + if (m_argIt.IsVarArg()) + { + *((LPVOID*)(pTransitionBlock + m_argIt.GetVASigCookieOffset())) = ArgSlotToPtr(pArguments[arg++]); + } + + if (m_argIt.HasParamType()) + { + *((LPVOID*)(pTransitionBlock + m_argIt.GetParamTypeArgOffset())) = ArgSlotToPtr(pArguments[arg++]); + } +#endif + + int ofs; + for (; TransitionBlock::InvalidOffset != (ofs = m_argIt.GetNextOffset()); arg++) + { +#ifdef CALLDESCR_REGTYPEMAP + FillInRegTypeMap(ofs, m_argIt.GetArgType(), pMap); +#endif + +#ifdef CALLDESCR_FPARGREGS + // Under CALLDESCR_FPARGREGS -ve offsets indicate arguments in floating point registers. If we + // have at least one such argument we point the call worker at the floating point area of the + // frame (we leave it null otherwise since the worker can perform a useful optimization if it + // knows no floating point registers need to be set up). + if ((ofs < 0) && (pFloatArgumentRegisters == NULL)) + pFloatArgumentRegisters = (FloatArgumentRegisters*)(pTransitionBlock + + TransitionBlock::GetOffsetOfFloatArgumentRegisters()); +#endif + +#if CHECK_APP_DOMAIN_LEAKS + // Make sure the arg is in the right app domain + if (g_pConfig->AppDomainLeaks() && m_argIt.GetArgType() == ELEMENT_TYPE_CLASS) + { + CONTRACT_VIOLATION(ThrowsViolation|GCViolation|FaultViolation); + OBJECTREF objRef = ArgSlotToObj(pArguments[arg]); + if (!objRef->AssignAppDomain(GetAppDomain())) + _ASSERTE(!"Attempt to pass object in wrong app domain to method"); + } +#endif // CHECK_APP_DOMAIN_LEAKS + + PVOID pDest = pTransitionBlock + ofs; + + UINT32 stackSize = m_argIt.GetArgSize(); + switch (stackSize) + { + case 1: + case 2: + case 4: + *((INT32*)pDest) = (INT32)pArguments[arg]; + break; + + case 8: + *((INT64*)pDest) = pArguments[arg]; + break; + + default: + // The ARG_SLOT contains a pointer to the value-type +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + if (m_argIt.IsArgPassedByRef()) + { + // We need to pass in a pointer, but be careful of the ARG_SLOT calling convention. + // We might already have a pointer in the ARG_SLOT + *(PVOID*)pDest = stackSize>sizeof(ARG_SLOT) ? + (LPVOID)ArgSlotToPtr(pArguments[arg]) : + (LPVOID)ArgSlotEndianessFixup((ARG_SLOT*)&pArguments[arg], stackSize); + } + else +#endif // ENREGISTERED_PARAMTYPE_MAXSIZE + if (stackSize>sizeof(ARG_SLOT)) + { + CopyMemory(pDest, ArgSlotToPtr(pArguments[arg]), stackSize); + } + else + { + CopyMemory(pDest, (LPVOID) (&pArguments[arg]), stackSize); + } + break; + } + } + + fpReturnSize = m_argIt.GetFPReturnSize(); + + } // END GCX_FORBID & ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE + + CallDescrData callDescrData; + + callDescrData.pSrc = pTransitionBlock + sizeof(TransitionBlock); + callDescrData.numStackSlots = nStackBytes / STACK_ELEM_SIZE; +#ifdef CALLDESCR_ARGREGS + callDescrData.pArgumentRegisters = (ArgumentRegisters*)(pTransitionBlock + TransitionBlock::GetOffsetOfArgumentRegisters()); +#endif +#ifdef CALLDESCR_FPARGREGS + callDescrData.pFloatArgumentRegisters = pFloatArgumentRegisters; +#endif +#ifdef CALLDESCR_REGTYPEMAP + callDescrData.dwRegTypeMap = dwRegTypeMap; +#endif + callDescrData.fpReturnSize = fpReturnSize; + callDescrData.pTarget = m_pCallTarget; + +#ifdef FEATURE_INTERPRETER + if (transitionToPreemptive) + { + GCPreemp transitionIfILStub(transitionToPreemptive); + DWORD* pLastError = &GetThread()->m_dwLastErrorInterp; + CallDescrWorkerInternal(&callDescrData); + *pLastError = GetLastError(); + } + else +#endif // FEATURE_INTERPRETER + { + CallDescrWorkerWithHandler(&callDescrData); + } + + if (pvRetBuff != NULL) + { + memcpyNoGCRefs(pvRetBuff, &callDescrData.returnValue, sizeof(callDescrData.returnValue)); + } + + ARG_SLOT retval = *(ARG_SLOT *)(&callDescrData.returnValue); + +#if !defined(_WIN64) && BIGENDIAN + { + GCX_FORBID(); + + if (!m_methodSig.Is64BitReturn()) + { + retval >>= 32; + } + } +#endif // !defined(_WIN64) && BIGENDIAN + + return retval; +} + +void CallDefaultConstructor(OBJECTREF ref) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + MethodTable *pMT = ref->GetTrueMethodTable(); + + PREFIX_ASSUME(pMT != NULL); + + if (!pMT->HasDefaultConstructor()) + { + SString ctorMethodName(SString::Utf8, COR_CTOR_METHOD_NAME); + COMPlusThrowNonLocalized(kMissingMethodException, ctorMethodName.GetUnicode()); + } + + GCPROTECT_BEGIN (ref); + + MethodDesc *pMD = pMT->GetDefaultConstructor(); + + PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC(pMD); + DECLARE_ARGHOLDER_ARRAY(CtorArgs, 1); + CtorArgs[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(ref); + + // Call the ctor... + CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE; + CALL_MANAGED_METHOD_NORET(CtorArgs); + + GCPROTECT_END (); +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/callhelpers.h b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/callhelpers.h new file mode 100644 index 00000000000000..31eac1a2f3ad4d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/callhelpers.h @@ -0,0 +1,652 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +/*============================================================ +** +** File: callhelpers.h +** Purpose: Provides helpers for making managed calls +** + +===========================================================*/ +#ifndef __CALLHELPERS_H__ +#define __CALLHELPERS_H__ + +struct CallDescrData +{ + // + // Input arguments + // + LPVOID pSrc; + UINT32 numStackSlots; +#ifdef CALLDESCR_ARGREGS + const ArgumentRegisters * pArgumentRegisters; +#endif +#ifdef CALLDESCR_FPARGREGS + const FloatArgumentRegisters * pFloatArgumentRegisters; +#endif +#ifdef CALLDESCR_REGTYPEMAP + UINT64 dwRegTypeMap; +#endif + UINT32 fpReturnSize; + PCODE pTarget; + + // + // Return value + // +#ifdef ENREGISTERED_RETURNTYPE_MAXSIZE + // Use UINT64 to ensure proper alignment + UINT64 returnValue[ENREGISTERED_RETURNTYPE_MAXSIZE / sizeof(UINT64)]; +#else + UINT64 returnValue; +#endif +}; + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + +extern "C" void STDCALL CallDescrWorkerInternal(CallDescrData * pCallDescrData); + +#if !defined(_WIN64) && defined(_DEBUG) +void CallDescrWorker(CallDescrData * pCallDescrData); +#else +#define CallDescrWorker(pCallDescrData) CallDescrWorkerInternal(pCallDescrData) +#endif + +void CallDescrWorkerWithHandler( + CallDescrData * pCallDescrData, + BOOL fCriticalCall = FALSE); + +void DispatchCall( + CallDescrData * pCallDescrData, + OBJECTREF * pRefException, + ContextTransitionFrame* pFrame = NULL +#ifdef FEATURE_CORRUPTING_EXCEPTIONS + , CorruptionSeverity * pSeverity = NULL +#endif // FEATURE_CORRUPTING_EXCEPTIONS + ); + +// Helper for VM->managed calls with simple signatures. +void * DispatchCallSimple( + SIZE_T *pSrc, + DWORD numStackSlotsToCopy, + PCODE pTargetAddress, + DWORD dwDispatchCallSimpleFlags); + +bool IsCerRootMethod(MethodDesc *pMD); + +class MethodDescCallSite +{ +private: + MethodDesc* m_pMD; + PCODE m_pCallTarget; + MetaSig m_methodSig; + ArgIterator m_argIt; + +#ifdef _DEBUG + __declspec(noinline) void LogWeakAssert() + { + LIMITED_METHOD_CONTRACT; + LOG((LF_ASSERT, LL_WARNING, "%s::%s\n", m_pMD->m_pszDebugClassName, m_pMD->m_pszDebugMethodName)); + } +#endif // _DEBUG + + void DefaultInit(OBJECTREF* porProtectedThis) + { + CONTRACTL + { + MODE_ANY; + GC_TRIGGERS; + THROWS; + } + CONTRACTL_END; + +#ifdef _DEBUG + // + // Make sure we are passing in a 'this' if and only if it is required + // + if (m_pMD->IsVtableMethod()) + { + CONSISTENCY_CHECK_MSG(NULL != porProtectedThis, "You did not pass in the 'this' object for a vtable method"); + } + else + { + if (NULL != porProtectedThis) + { + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnUnneededThis)) + { + CONSISTENCY_CHECK_MSG(NULL == porProtectedThis, "You passed in a 'this' object to a non-vtable method."); + } + else + { + LogWeakAssert(); + } + + } + } +#endif // _DEBUG + + m_pCallTarget = m_pMD->GetCallTarget(porProtectedThis); + + m_argIt.ForceSigWalk(); + } + +#ifdef FEATURE_INTERPRETER +public: + ARG_SLOT CallTargetWorker(const ARG_SLOT *pArguments, bool transitionToPreemptive = false); +#else + ARG_SLOT CallTargetWorker(const ARG_SLOT *pArguments); +#endif + +public: + // Used to avoid touching metadata for mscorlib methods. + // instance methods must pass in the 'this' object + // static methods must pass null + MethodDescCallSite(BinderMethodID id, OBJECTREF* porProtectedThis = NULL) : + m_pMD( + MscorlibBinder::GetMethod(id) + ), + m_methodSig(id), + m_argIt(&m_methodSig) + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + DefaultInit(porProtectedThis); + } + + // Used to avoid touching metadata for mscorlib methods. + // instance methods must pass in the 'this' object + // static methods must pass null + MethodDescCallSite(BinderMethodID id, OBJECTHANDLE hThis) : + m_pMD( + MscorlibBinder::GetMethod(id) + ), + m_methodSig(id), + m_argIt(&m_methodSig) + { + WRAPPER_NO_CONTRACT; + + DefaultInit((OBJECTREF*)hThis); + } + + // instance methods must pass in the 'this' object + // static methods must pass null + MethodDescCallSite(MethodDesc* pMD, OBJECTREF* porProtectedThis = NULL) : + m_pMD(pMD), + m_methodSig(pMD), + m_argIt(&m_methodSig) + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + if (porProtectedThis == NULL) + { + // We don't have a "this" pointer - ensure that we have activated the containing module + m_pMD->EnsureActive(); + } + + DefaultInit(porProtectedThis); + } + + // instance methods must pass in the 'this' object + // static methods must pass null + MethodDescCallSite(MethodDesc* pMD, OBJECTHANDLE hThis) : + m_pMD(pMD), + m_methodSig(pMD), + m_argIt(&m_methodSig) + { + WRAPPER_NO_CONTRACT; + + if (hThis == NULL) + { + // We don't have a "this" pointer - ensure that we have activated the containing module + m_pMD->EnsureActive(); + } + + DefaultInit((OBJECTREF*)hThis); + } + + // instance methods must pass in the 'this' object + // static methods must pass null + MethodDescCallSite(MethodDesc* pMD, LPHARDCODEDMETASIG pwzSignature, OBJECTREF* porProtectedThis = NULL) : + m_pMD(pMD), + m_methodSig(pwzSignature), + m_argIt(&m_methodSig) + { + WRAPPER_NO_CONTRACT; + + if (porProtectedThis == NULL) + { + // We don't have a "this" pointer - ensure that we have activated the containing module + m_pMD->EnsureActive(); + } + + DefaultInit(porProtectedThis); + } + + // + // Only use this constructor if you're certain you know where + // you're going and it cannot be affected by generics/virtual + // dispatch/etc.. + // + MethodDescCallSite(MethodDesc* pMD, PCODE pCallTarget) : + m_pMD(pMD), + m_pCallTarget(pCallTarget), + m_methodSig(pMD), + m_argIt(&m_methodSig) + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + m_pMD->EnsureActive(); + + m_argIt.ForceSigWalk(); + } + +#ifdef FEATURE_INTERPRETER + MethodDescCallSite(MethodDesc* pMD, MetaSig* pSig, PCODE pCallTarget) : + m_pMD(pMD), + m_pCallTarget(pCallTarget), + m_methodSig(*pSig), + m_argIt(pSig) + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + m_pMD->EnsureActive(); + + m_argIt.ForceSigWalk(); + } +#endif // FEATURE_INTERPRETER + + MetaSig* GetMetaSig() + { + return &m_methodSig; + } + + // + // Call_RetXXX definition macros: + // + // These macros provide type protection for the return value from calls to managed + // code. This should help to prevent errors like what we're seeing on 64bit where + // the JIT64 is returning the BOOL as 1byte with the rest of the ARG_SLOT still + // polluted by the remnants of its last value. Previously we would cast to a (BOOL) + // and end up having if((BOOL)pMD->Call(...)) statements always being true. + // + + // Use OTHER_ELEMENT_TYPE when defining CallXXX_RetXXX variations where the return type + // is not in CorElementType (like LPVOID) or the return type can be one of a number of + // CorElementTypes, like XXX_RetObjPtr which is used for all kinds of Object* return + // types, or XXX_RetArgSlot which is unspecified. +#define OTHER_ELEMENT_TYPE -1 + +// Note "permitvaluetypes" is not really used for anything +#define MDCALLDEF(wrappedmethod, permitvaluetypes, ext, rettype, eltype) \ + FORCEINLINE rettype wrappedmethod##ext (const ARG_SLOT* pArguments) \ + { \ + WRAPPER_NO_CONTRACT; \ + { \ + GCX_FORBID(); /* arg array is not protected */ \ + CONSISTENCY_CHECK(eltype == OTHER_ELEMENT_TYPE || \ + eltype == m_methodSig.GetReturnType()); \ + } \ + ARG_SLOT retval; \ + retval = CallTargetWorker(pArguments); \ + return *(rettype *)ArgSlotEndianessFixup(&retval, sizeof(rettype)); \ + } + +#define MDCALLDEF_REFTYPE(wrappedmethod, permitvaluetypes, ext, ptrtype, reftype) \ + FORCEINLINE reftype wrappedmethod##ext (const ARG_SLOT* pArguments) \ + { \ + WRAPPER_NO_CONTRACT; \ + { \ + GCX_FORBID(); /* arg array is not protected */ \ + CONSISTENCY_CHECK(MetaSig::RETOBJ == m_pMD->ReturnsObject(true)); \ + } \ + ARG_SLOT retval; \ + retval = CallTargetWorker(pArguments); \ + return ObjectTo##reftype(*(ptrtype *) \ + ArgSlotEndianessFixup(&retval, sizeof(ptrtype))); \ + } + + + // The MDCALLDEF_XXX_VOID macros take a customized assertion and calls the worker without + // returning a value, this is the macro that _should_ be used to define the CallXXX variations + // (without _RetXXX extension) so that misuse will be caught at compile time. + +#define MDCALLDEF_VOID(wrappedmethod, permitvaluetypes) \ + FORCEINLINE void wrappedmethod (const ARG_SLOT* pArguments) \ + { \ + WRAPPER_NO_CONTRACT; \ + CallTargetWorker(pArguments); \ + } + +#define MDCALLDEFF_STD_RETTYPES(wrappedmethod,permitvaluetypes) \ + MDCALLDEF_VOID(wrappedmethod,permitvaluetypes) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetBool, CLR_BOOL, ELEMENT_TYPE_BOOLEAN) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetChar, CLR_CHAR, ELEMENT_TYPE_CHAR) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetI1, CLR_I1, ELEMENT_TYPE_I1) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetU1, CLR_U1, ELEMENT_TYPE_U1) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetI2, CLR_I2, ELEMENT_TYPE_I2) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetU2, CLR_U2, ELEMENT_TYPE_U2) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetI4, CLR_I4, ELEMENT_TYPE_I4) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetU4, CLR_U4, ELEMENT_TYPE_U4) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetI8, CLR_I8, ELEMENT_TYPE_I8) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetU8, CLR_U8, ELEMENT_TYPE_U8) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetR4, CLR_R4, ELEMENT_TYPE_R4) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetR8, CLR_R8, ELEMENT_TYPE_R8) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetI, CLR_I, ELEMENT_TYPE_I) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetU, CLR_U, ELEMENT_TYPE_U) \ + MDCALLDEF(wrappedmethod,permitvaluetypes, _RetArgSlot,ARG_SLOT, OTHER_ELEMENT_TYPE) + + + public: + //-------------------------------------------------------------------- + // Invoke a method. Arguments are packaged up in right->left order + // which each array element corresponding to one argument. + // + // Can throw a COM+ exception. + // + // All the appropriate "virtual" semantics (include thunking like context + // proxies) occurs inside Call. + // + // Call should never be called on interface MethodDesc's. The exception + // to this rule is when calling on a COM object. In that case the call + // needs to go through an interface MD and CallOnInterface is there + // for that. + //-------------------------------------------------------------------- + + // + // NOTE on Call methods + // MethodDesc::Call uses a virtual portable calling convention + // Arguments are put left-to-right in the ARG_SLOT array, in the following order: + // - this pointer (if any) + // - return buffer address (if signature.HasRetBuffArg()) + // - all other fixed arguments (left-to-right) + // Vararg is not supported yet. + // + // The args that fit in an ARG_SLOT are inline. The ones that don't fit in an ARG_SLOT are allocated somewhere else + // (usually on the stack) and a pointer to that area is put in the corresponding ARG_SLOT + // ARG_SLOT is guaranteed to be big enough to fit all basic types and pointer types. Basically, one has + // to check only for aggregate value-types and 80-bit floating point values or greater. + // + // Calls with value type parameters must use the CallXXXWithValueTypes + // variants. Using the WithValueTypes variant indicates that the caller + // has gc-protected the contents of value types of size greater than + // ENREGISTERED_PARAMTYPE_MAXSIZE (when it is defined, which is currently + // only on AMD64). ProtectValueClassFrame can be used to accomplish this, + // see CallDescrWithObjectArray in stackbuildersink.cpp. + // + // Not all usages of MethodDesc::CallXXX have been ported to the new convention. The end goal is to port them all and get + // rid of the non-portable BYTE* version. + // + // We have converted all usage of CallXXX in the runtime to some more specific CallXXX_RetXXX type (CallXXX usages + // where the return value is unused remain CallXXX). In most cases we were able to use something more specific than + // CallXXX_RetArgSlot (which is the equivalent of the old behavior). It is recommended that as you add usages of + // CallXXX in the future you try to avoid CallXXX_RetArgSlot whenever possible. + // + // If the return value is unused you can use the CallXXX syntax which has a void return and is not protected + // by any assertions around the return value type. This should protect against people trying to use the old + // semantics of ->Call as if they try to assign the return value to something they'll get a compile time error. + // + // If you are unable to be sure of the return type at runtime and are just blindly casting then continue to use + // CallXXX_RetArgSlot, Do not for instance use CallXXX_RetI4 as a mechanism to cast the result to an I4 as it will + // also try to assert the fact that the callee managed method actually does return an I4. + // + + // All forms of CallXXX should have at least the CallXXX_RetArgSlot definition which maps to the old behavior + // - MDCALL_ARG_____STD_RETTYPES includes CallXXX_RetArgSlot + // - MDCALL_ARG_SIG_STD_RETTYPES includes CallXXX_RetArgSlot + + // XXX Call_RetXXX(const ARG_SLOT* pArguments); + MDCALLDEFF_STD_RETTYPES(Call, FALSE) + MDCALLDEF( Call, FALSE, _RetHR, HRESULT, OTHER_ELEMENT_TYPE) + MDCALLDEF( Call, FALSE, _RetObjPtr, Object*, OTHER_ELEMENT_TYPE) + MDCALLDEF_REFTYPE( Call, FALSE, _RetOBJECTREF, Object*, OBJECTREF) + MDCALLDEF_REFTYPE( Call, FALSE, _RetSTRINGREF, StringObject*, STRINGREF) + MDCALLDEF( Call, FALSE, _RetLPVOID, LPVOID, OTHER_ELEMENT_TYPE) + + // XXX CallWithValueTypes_RetXXX(const ARG_SLOT* pArguments); + MDCALLDEF_VOID( CallWithValueTypes, TRUE) + MDCALLDEF( CallWithValueTypes, TRUE, _RetArgSlot, ARG_SLOT, OTHER_ELEMENT_TYPE) + MDCALLDEF_REFTYPE( CallWithValueTypes, TRUE, _RetOBJECTREF, Object*, OBJECTREF) + MDCALLDEF( CallWithValueTypes, TRUE, _RetOleColor, OLE_COLOR, OTHER_ELEMENT_TYPE) +#undef OTHER_ELEMENT_TYPE +#undef MDCALL_ARG_SIG_STD_RETTYPES +#undef MDCALLDEF +#undef MDCALLDEF_REFTYPE +#undef MDCALLDEF_VOID +}; // MethodDescCallSite + + +#ifdef CALLDESCR_REGTYPEMAP +void FillInRegTypeMap(int argOffset, CorElementType typ, BYTE * pMap); +#endif // CALLDESCR_REGTYPEMAP + + +/***********************************************************************/ +/* Macros used to indicate a call to managed code is starting/ending */ +/***********************************************************************/ + +enum EEToManagedCallFlags +{ + EEToManagedDefault = 0x0000, + EEToManagedCriticalCall = 0x0001, +}; + +#define BEGIN_CALL_TO_MANAGED() \ + BEGIN_CALL_TO_MANAGEDEX(EEToManagedDefault) + +#define BEGIN_CALL_TO_MANAGEDEX(flags) \ +{ \ + MAKE_CURRENT_THREAD_AVAILABLE(); \ + DECLARE_CPFH_EH_RECORD(CURRENT_THREAD); \ + _ASSERTE(CURRENT_THREAD); \ + _ASSERTE(!CURRENT_THREAD->IsAbortPrevented() || \ + CURRENT_THREAD->IsAbortCheckDisabled()); \ + _ASSERTE((CURRENT_THREAD->m_StateNC & Thread::TSNC_OwnsSpinLock) == 0); \ + /* This bit should never be set when we call into managed code. The */ \ + /* stack walking code explicitly clears this around any potential calls */ \ + /* into managed code. */ \ + _ASSERTE(!IsStackWalkerThread()); \ + /* If this isn't a critical transition, we need to check to see if a */ \ + /* thread abort has been requested */ \ + if (!(flags & EEToManagedCriticalCall)) \ + { \ + TESTHOOKCALL(AppDomainCanBeUnloaded(CURRENT_THREAD->GetDomain()->GetId().m_dwId,FALSE)); \ + if (CURRENT_THREAD->IsAbortRequested()) { \ + CURRENT_THREAD->HandleThreadAbort(); \ + } \ + } \ + BEGIN_SO_TOLERANT_CODE(CURRENT_THREAD); \ + INSTALL_COMPLUS_EXCEPTION_HANDLER_NO_DECLARE(); + +#define END_CALL_TO_MANAGED() \ + UNINSTALL_COMPLUS_EXCEPTION_HANDLER(); \ + END_SO_TOLERANT_CODE; \ +} + +/***********************************************************************/ +/* Macros that provide abstraction to the usage of DispatchCallSimple */ +/***********************************************************************/ + +enum DispatchCallSimpleFlags +{ + DispatchCallSimple_CriticalCall = 0x0001, + DispatchCallSimple_CatchHandlerFoundNotification = 0x0002, +}; + +#define ARGHOLDER_TYPE LPVOID +#define OBJECTREF_TO_ARGHOLDER(x) (LPVOID)OBJECTREFToObject(x) +#define STRINGREF_TO_ARGHOLDER(x) (LPVOID)STRINGREFToObject(x) +#define PTR_TO_ARGHOLDER(x) (LPVOID)x +#define DWORD_TO_ARGHOLDER(x) (LPVOID)(SIZE_T)x + +#define INIT_VARIABLES(count) \ + DWORD __numArgs = count; \ + DWORD __dwDispatchCallSimpleFlags = 0; \ + +#define PREPARE_NONVIRTUAL_CALLSITE(id) \ + static PCODE s_pAddr##id = NULL; \ + PCODE __pSlot = VolatileLoad(&s_pAddr##id); \ + if ( __pSlot == NULL ) \ + { \ + MethodDesc *pMeth = MscorlibBinder::GetMethod(id); \ + _ASSERTE(pMeth); \ + __pSlot = pMeth->GetMultiCallableAddrOfCode(); \ + VolatileStore(&s_pAddr##id, __pSlot); \ + } + +#define PREPARE_VIRTUAL_CALLSITE(id, objref) \ + MethodDesc *__pMeth = MscorlibBinder::GetMethod(id); \ + PCODE __pSlot = __pMeth->GetCallTarget(&objref); + +#define PREPARE_VIRTUAL_CALLSITE_USING_METHODDESC(pMD, objref) \ + PCODE __pSlot = pMD->GetCallTarget(&objref); + +#ifdef _DEBUG +#define SIMPLE_VIRTUAL_METHOD_CHECK(slotNumber, methodTable) \ + { \ + MethodDesc* __pMeth = methodTable->GetMethodDescForSlot(slotNumber); \ + _ASSERTE(__pMeth); \ + _ASSERTE(!__pMeth->HasMethodInstantiation() && \ + !__pMeth->GetMethodTable()->IsInterface()); \ + } +#else +#define SIMPLE_VIRTUAL_METHOD_CHECK(slotNumber, objref) +#endif + +// a simple virtual method is a non-interface/non-generic method +// Note: objref has to be protected! +#define PREPARE_SIMPLE_VIRTUAL_CALLSITE(id, objref) \ + static WORD s_slot##id = MethodTable::NO_SLOT; \ + WORD __slot = VolatileLoad(&s_slot##id); \ + if (__slot == MethodTable::NO_SLOT) \ + { \ + MethodDesc *pMeth = MscorlibBinder::GetMethod(id); \ + _ASSERTE(pMeth); \ + __slot = pMeth->GetSlot(); \ + VolatileStore(&s_slot##id, __slot); \ + } \ + PREPARE_SIMPLE_VIRTUAL_CALLSITE_USING_SLOT(__slot, objref) \ + +// a simple virtual method is a non-interface/non-generic method +#define PREPARE_SIMPLE_VIRTUAL_CALLSITE_USING_SLOT(slotNumber, objref) \ + MethodTable* __pObjMT = (objref)->GetMethodTable(); \ + SIMPLE_VIRTUAL_METHOD_CHECK(slotNumber, __pObjMT); \ + PCODE __pSlot = (PCODE) __pObjMT->GetRestoredSlot(slotNumber); + +#define PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC(pMD) \ + PCODE __pSlot = (pMD)->GetSingleCallableAddrOfCode(); + +#define PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(pCode) \ + PCODE __pSlot = pCode; + +#define CRITICAL_CALLSITE \ + __dwDispatchCallSimpleFlags |= DispatchCallSimple_CriticalCall; + +// This flag should be used for callsites that catch exception up the stack inside the VM. The most common causes are +// such as END_DOMAIN_TRANSITION or EX_CATCH. Catching exceptions in the managed code is properly instrumented and +// does not need this notification. +// +// The notification is what enables both the managed �unhandled exception� dialog and the �user unhandled� dialog when +// JMC is turned on. Many things that VS puts up the unhandled exception dialog for are actually cases where the native +// exception was caught, for example catching exceptions at the thread base. JMC requires further accuracy - in that case +// VS is checking to see if an exception escaped particular ranges of managed code frames. +#define CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE \ + __dwDispatchCallSimpleFlags |= DispatchCallSimple_CatchHandlerFoundNotification; + +#define PERFORM_CALL \ + void * __retval = NULL; \ + __retval = DispatchCallSimple(__pArgs, \ + __numStackSlotsToCopy, \ + __pSlot, \ + __dwDispatchCallSimpleFlags);\ + +#ifdef CALLDESCR_ARGREGS + +#if defined(_TARGET_X86_) + +// Arguments on x86 are passed backward +#define ARGNUM_0 1 +#define ARGNUM_1 0 +#define ARGNUM_N(n) __numArgs - n + 1 + +#else + +#define ARGNUM_0 0 +#define ARGNUM_1 1 +#define ARGNUM_N(n) n + +#endif + +#define PRECALL_PREP(args) \ + DWORD __numStackSlotsToCopy = (__numArgs > NUM_ARGUMENT_REGISTERS) ? (__numArgs - NUM_ARGUMENT_REGISTERS) : 0; \ + SIZE_T * __pArgs = (SIZE_T *)args; + +#define DECLARE_ARGHOLDER_ARRAY(arg, count) \ + INIT_VARIABLES(count) \ + ARGHOLDER_TYPE arg[(count <= NUM_ARGUMENT_REGISTERS ? NUM_ARGUMENT_REGISTERS : count)]; + +#else // CALLDESCR_ARGREGS + +#define ARGNUM_0 0 +#define ARGNUM_1 1 +#define ARGNUM_N(n) n + +#define PRECALL_PREP(args) \ + DWORD __numStackSlotsToCopy = (__numArgs > NUM_ARGUMENT_REGISTERS) ? __numArgs : NUM_ARGUMENT_REGISTERS; \ + SIZE_T * __pArgs = (SIZE_T *)args; + +#define DECLARE_ARGHOLDER_ARRAY(arg, count) \ + INIT_VARIABLES(count) \ + ARGHOLDER_TYPE arg[(count <= NUM_ARGUMENT_REGISTERS ? NUM_ARGUMENT_REGISTERS : count)]; + +#endif // CALLDESCR_ARGREGS + + +#define CALL_MANAGED_METHOD(ret, rettype, args) \ + PRECALL_PREP(args) \ + PERFORM_CALL \ + ret = *(rettype *)(&__retval); + +#define CALL_MANAGED_METHOD_NORET(args) \ + PRECALL_PREP(args) \ + PERFORM_CALL + +#define CALL_MANAGED_METHOD_RETREF(ret, reftype, args) \ + PRECALL_PREP(args) \ + PERFORM_CALL \ + ret = (reftype)ObjectToOBJECTREF((Object *)__retval); + +#define ARGNUM_2 ARGNUM_N(2) +#define ARGNUM_3 ARGNUM_N(3) +#define ARGNUM_4 ARGNUM_N(4) +#define ARGNUM_5 ARGNUM_N(5) +#define ARGNUM_6 ARGNUM_N(6) +#define ARGNUM_7 ARGNUM_N(7) +#define ARGNUM_8 ARGNUM_N(8) + + +void CallDefaultConstructor(OBJECTREF ref); + +#endif //!DACCESS_COMPILE && !CROSSGEN_COMPILE + +#endif // __CALLHELPERS_H__ diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/i386/asmconstants.h b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/i386/asmconstants.h new file mode 100644 index 00000000000000..e0669b07e398b8 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/i386/asmconstants.h @@ -0,0 +1,489 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// asmconstants.h - +// +// This header defines field offsets and constants used by assembly code +// Be sure to rebuild clr/src/vm/ceemain.cpp after changing this file, to +// ensure that the constants match the expected C/C++ values + +// +// If you need to figure out a constant that has changed and is causing +// a compile-time assert, check out USE_COMPILE_TIME_CONSTANT_FINDER. +// TODO: put the constant finder in a common place so other platforms can use it. + +#ifndef _TARGET_X86_ +#error this file should only be used on an X86 platform +#endif + +#include "../../inc/switches.h" + +#ifndef ASMCONSTANTS_C_ASSERT +#define ASMCONSTANTS_C_ASSERT(cond) +#endif + +#ifndef ASMCONSTANTS_RUNTIME_ASSERT +#define ASMCONSTANTS_RUNTIME_ASSERT(cond) +#endif + +// Some contants are different in _DEBUG builds. This macro factors out ifdefs from below. +#ifdef _DEBUG +#define DBG_FRE(dbg,fre) dbg +#else +#define DBG_FRE(dbg,fre) fre +#endif + +//*************************************************************************** +#if defined(_DEBUG) && defined(_TARGET_X86_) && !defined(FEATURE_CORECLR) + #define HAS_TRACK_CXX_EXCEPTION_CODE_HACK 1 + #define TRACK_CXX_EXCEPTION_CODE_HACK +#else + #define HAS_TRACK_CXX_EXCEPTION_CODE_HACK 0 +#endif + +#define INITIAL_SUCCESS_COUNT 0x100 + +#define DynamicHelperFrameFlags_Default 0 +#define DynamicHelperFrameFlags_ObjectArg 1 +#define DynamicHelperFrameFlags_ObjectArg2 2 + +#ifdef FEATURE_REMOTING +#define TransparentProxyObject___stubData 0x8 +ASMCONSTANTS_C_ASSERT(TransparentProxyObject___stubData == offsetof(TransparentProxyObject, _stubData)) + +#define TransparentProxyObject___stub 0x14 +ASMCONSTANTS_C_ASSERT(TransparentProxyObject___stub == offsetof(TransparentProxyObject, _stub)) + +#define TransparentProxyObject___pMT 0xc +ASMCONSTANTS_C_ASSERT(TransparentProxyObject___pMT == offsetof(TransparentProxyObject, _pMT)) +#endif // FEATURE_REMOTING + +// CONTEXT from rotor_pal.h +#define CONTEXT_Edi 0x9c +ASMCONSTANTS_C_ASSERT(CONTEXT_Edi == offsetof(CONTEXT,Edi)) + +#define CONTEXT_Esi 0xa0 +ASMCONSTANTS_C_ASSERT(CONTEXT_Esi == offsetof(CONTEXT,Esi)) + +#define CONTEXT_Ebx 0xa4 +ASMCONSTANTS_C_ASSERT(CONTEXT_Ebx == offsetof(CONTEXT,Ebx)) + +#define CONTEXT_Edx 0xa8 +ASMCONSTANTS_C_ASSERT(CONTEXT_Edx == offsetof(CONTEXT,Edx)) + +#define CONTEXT_Eax 0xb0 +ASMCONSTANTS_C_ASSERT(CONTEXT_Eax == offsetof(CONTEXT,Eax)) + +#define CONTEXT_Ebp 0xb4 +ASMCONSTANTS_C_ASSERT(CONTEXT_Ebp == offsetof(CONTEXT,Ebp)) + +#define CONTEXT_Eip 0xb8 +ASMCONSTANTS_C_ASSERT(CONTEXT_Eip == offsetof(CONTEXT,Eip)) + +#define CONTEXT_Esp 0xc4 +ASMCONSTANTS_C_ASSERT(CONTEXT_Esp == offsetof(CONTEXT,Esp)) + +// SYSTEM_INFO from rotor_pal.h +#define SYSTEM_INFO_dwNumberOfProcessors 20 +ASMCONSTANTS_C_ASSERT(SYSTEM_INFO_dwNumberOfProcessors == offsetof(SYSTEM_INFO,dwNumberOfProcessors)) + +// SpinConstants from clr/src/vars.h +#define SpinConstants_dwInitialDuration 0 +ASMCONSTANTS_C_ASSERT(SpinConstants_dwInitialDuration == offsetof(SpinConstants,dwInitialDuration)) + +#define SpinConstants_dwMaximumDuration 4 +ASMCONSTANTS_C_ASSERT(SpinConstants_dwMaximumDuration == offsetof(SpinConstants,dwMaximumDuration)) + +#define SpinConstants_dwBackoffFactor 8 +ASMCONSTANTS_C_ASSERT(SpinConstants_dwBackoffFactor == offsetof(SpinConstants,dwBackoffFactor)) + +// EHContext from clr/src/vm/i386/cgencpu.h +#define EHContext_Eax 0x00 +ASMCONSTANTS_C_ASSERT(EHContext_Eax == offsetof(EHContext,Eax)) + +#define EHContext_Ebx 0x04 +ASMCONSTANTS_C_ASSERT(EHContext_Ebx == offsetof(EHContext,Ebx)) + +#define EHContext_Ecx 0x08 +ASMCONSTANTS_C_ASSERT(EHContext_Ecx == offsetof(EHContext,Ecx)) + +#define EHContext_Edx 0x0c +ASMCONSTANTS_C_ASSERT(EHContext_Edx == offsetof(EHContext,Edx)) + +#define EHContext_Esi 0x10 +ASMCONSTANTS_C_ASSERT(EHContext_Esi == offsetof(EHContext,Esi)) + +#define EHContext_Edi 0x14 +ASMCONSTANTS_C_ASSERT(EHContext_Edi == offsetof(EHContext,Edi)) + +#define EHContext_Ebp 0x18 +ASMCONSTANTS_C_ASSERT(EHContext_Ebp == offsetof(EHContext,Ebp)) + +#define EHContext_Esp 0x1c +ASMCONSTANTS_C_ASSERT(EHContext_Esp == offsetof(EHContext,Esp)) + +#define EHContext_Eip 0x20 +ASMCONSTANTS_C_ASSERT(EHContext_Eip == offsetof(EHContext,Eip)) + + +// from clr/src/fjit/helperframe.h +#define SIZEOF_MachState 40 +ASMCONSTANTS_C_ASSERT(SIZEOF_MachState == sizeof(MachState)) + +#define MachState__pEdi 0 +ASMCONSTANTS_C_ASSERT(MachState__pEdi == offsetof(MachState, _pEdi)) + +#define MachState__edi 4 +ASMCONSTANTS_C_ASSERT(MachState__edi == offsetof(MachState, _edi)) + +#define MachState__pEsi 8 +ASMCONSTANTS_C_ASSERT(MachState__pEsi == offsetof(MachState, _pEsi)) + +#define MachState__esi 12 +ASMCONSTANTS_C_ASSERT(MachState__esi == offsetof(MachState, _esi)) + +#define MachState__pEbx 16 +ASMCONSTANTS_C_ASSERT(MachState__pEbx == offsetof(MachState, _pEbx)) + +#define MachState__ebx 20 +ASMCONSTANTS_C_ASSERT(MachState__ebx == offsetof(MachState, _ebx)) + +#define MachState__pEbp 24 +ASMCONSTANTS_C_ASSERT(MachState__pEbp == offsetof(MachState, _pEbp)) + +#define MachState__ebp 28 +ASMCONSTANTS_C_ASSERT(MachState__ebp == offsetof(MachState, _ebp)) + +#define MachState__esp 32 +ASMCONSTANTS_C_ASSERT(MachState__esp == offsetof(MachState, _esp)) + +#define MachState__pRetAddr 36 +ASMCONSTANTS_C_ASSERT(MachState__pRetAddr == offsetof(MachState, _pRetAddr)) + +#define LazyMachState_captureEbp 40 +ASMCONSTANTS_C_ASSERT(LazyMachState_captureEbp == offsetof(LazyMachState, captureEbp)) + +#define LazyMachState_captureEsp 44 +ASMCONSTANTS_C_ASSERT(LazyMachState_captureEsp == offsetof(LazyMachState, captureEsp)) + +#define LazyMachState_captureEip 48 +ASMCONSTANTS_C_ASSERT(LazyMachState_captureEip == offsetof(LazyMachState, captureEip)) + + +#define VASigCookie__StubOffset 4 +ASMCONSTANTS_C_ASSERT(VASigCookie__StubOffset == offsetof(VASigCookie, pNDirectILStub)) + +#define SIZEOF_TailCallFrame 32 +ASMCONSTANTS_C_ASSERT(SIZEOF_TailCallFrame == sizeof(TailCallFrame)) + +#define SIZEOF_GSCookie 4 + +// ICodeManager::SHADOW_SP_IN_FILTER from clr/src/inc/eetwain.h +#define SHADOW_SP_IN_FILTER_ASM 0x1 +ASMCONSTANTS_C_ASSERT(SHADOW_SP_IN_FILTER_ASM == ICodeManager::SHADOW_SP_IN_FILTER) + +// from clr/src/inc/corinfo.h +#define CORINFO_NullReferenceException_ASM 0 +ASMCONSTANTS_C_ASSERT(CORINFO_NullReferenceException_ASM == CORINFO_NullReferenceException) + +#define CORINFO_IndexOutOfRangeException_ASM 3 +ASMCONSTANTS_C_ASSERT(CORINFO_IndexOutOfRangeException_ASM == CORINFO_IndexOutOfRangeException) + +#define CORINFO_OverflowException_ASM 4 +ASMCONSTANTS_C_ASSERT(CORINFO_OverflowException_ASM == CORINFO_OverflowException) + +#define CORINFO_SynchronizationLockException_ASM 5 +ASMCONSTANTS_C_ASSERT(CORINFO_SynchronizationLockException_ASM == CORINFO_SynchronizationLockException) + +#define CORINFO_ArrayTypeMismatchException_ASM 6 +ASMCONSTANTS_C_ASSERT(CORINFO_ArrayTypeMismatchException_ASM == CORINFO_ArrayTypeMismatchException) + +#define CORINFO_ArgumentNullException_ASM 8 +ASMCONSTANTS_C_ASSERT(CORINFO_ArgumentNullException_ASM == CORINFO_ArgumentNullException) + +#define CORINFO_ArgumentException_ASM 9 +ASMCONSTANTS_C_ASSERT(CORINFO_ArgumentException_ASM == CORINFO_ArgumentException) + + +#ifndef CROSSGEN_COMPILE + +// from clr/src/vm/threads.h +#if defined(TRACK_CXX_EXCEPTION_CODE_HACK) // Is C++ exception code tracking turned on? + #define Thread_m_LastCxxSEHExceptionCode 0x20 + ASMCONSTANTS_C_ASSERT(Thread_m_LastCxxSEHExceptionCode == offsetof(Thread, m_LastCxxSEHExceptionCode)) + + #define Thread_m_Context 0x3C +#else + #define Thread_m_Context 0x38 +#endif // TRACK_CXX_EXCEPTION_CODE_HACK +ASMCONSTANTS_C_ASSERT(Thread_m_Context == offsetof(Thread, m_Context)) + +#define Thread_m_State 0x04 +ASMCONSTANTS_C_ASSERT(Thread_m_State == offsetof(Thread, m_State)) +#endif // CROSSGEN_COMPILE + +#define Thread_m_fPreemptiveGCDisabled 0x08 +#ifndef CROSSGEN_COMPILE +ASMCONSTANTS_C_ASSERT(Thread_m_fPreemptiveGCDisabled == offsetof(Thread, m_fPreemptiveGCDisabled)) +#endif // CROSSGEN_COMPILE + +#define Thread_m_pFrame 0x0C +#ifndef CROSSGEN_COMPILE +ASMCONSTANTS_C_ASSERT(Thread_m_pFrame == offsetof(Thread, m_pFrame)) +#endif // CROSSGEN_COMPILE + +#ifndef CROSSGEN_COMPILE +#define Thread_m_dwLockCount 0x18 +ASMCONSTANTS_C_ASSERT(Thread_m_dwLockCount == offsetof(Thread, m_dwLockCount)) + +#define Thread_m_ThreadId 0x1C +ASMCONSTANTS_C_ASSERT(Thread_m_ThreadId == offsetof(Thread, m_ThreadId)) + +#define TS_CatchAtSafePoint_ASM 0x5F +ASMCONSTANTS_C_ASSERT(Thread::TS_CatchAtSafePoint == TS_CatchAtSafePoint_ASM) + +#ifdef FEATURE_HIJACK +#define TS_Hijacked_ASM 0x80 +ASMCONSTANTS_C_ASSERT(Thread::TS_Hijacked == TS_Hijacked_ASM) +#endif + +#endif // CROSSGEN_COMPILE + + +// from clr/src/vm/appdomain.hpp + +#define AppDomain__m_dwId 0x4 +ASMCONSTANTS_C_ASSERT(AppDomain__m_dwId == offsetof(AppDomain, m_dwId)); + +// from clr/src/vm/ceeload.cpp +#ifdef FEATURE_MIXEDMODE +#define IJWNOADThunk__m_cache 0x1C +ASMCONSTANTS_C_ASSERT(IJWNOADThunk__m_cache == offsetof(IJWNOADThunk, m_cache)) + +#define IJWNOADThunk__NextCacheOffset 0x8 +ASMCONSTANTS_C_ASSERT(IJWNOADThunk__NextCacheOffset == sizeof(IJWNOADThunkStubCache)) + +#define IJWNOADThunk__CodeAddrOffsetFromADID 0x4 +ASMCONSTANTS_C_ASSERT(IJWNOADThunk__CodeAddrOffsetFromADID == offsetof(IJWNOADThunkStubCache, m_CodeAddr)) +#endif //FEATURE_MIXEDMODE + +// from clr/src/vm/syncblk.h +#define SizeOfSyncTableEntry_ASM 8 +ASMCONSTANTS_C_ASSERT(sizeof(SyncTableEntry) == SizeOfSyncTableEntry_ASM) + +#define SyncBlockIndexOffset_ASM 4 +ASMCONSTANTS_C_ASSERT(sizeof(ObjHeader) - offsetof(ObjHeader, m_SyncBlockValue) == SyncBlockIndexOffset_ASM) + +#ifndef __GNUC__ +#define SyncTableEntry_m_SyncBlock 0 +ASMCONSTANTS_C_ASSERT(offsetof(SyncTableEntry, m_SyncBlock) == SyncTableEntry_m_SyncBlock) + +#define SyncBlock_m_Monitor 0 +ASMCONSTANTS_C_ASSERT(offsetof(SyncBlock, m_Monitor) == SyncBlock_m_Monitor) + +#define AwareLock_m_MonitorHeld 0 +ASMCONSTANTS_C_ASSERT(offsetof(AwareLock, m_MonitorHeld) == AwareLock_m_MonitorHeld) +#else +// The following 3 offsets have value of 0, and must be +// defined to be an empty string. Otherwise, gas may generate assembly +// code with 0 displacement if 0 is left in the displacement field +// of an instruction. +#define SyncTableEntry_m_SyncBlock // 0 +ASMCONSTANTS_C_ASSERT(offsetof(SyncTableEntry, m_SyncBlock) == 0) + +#define SyncBlock_m_Monitor // 0 +ASMCONSTANTS_C_ASSERT(offsetof(SyncBlock, m_Monitor) == 0) + +#define AwareLock_m_MonitorHeld // 0 +ASMCONSTANTS_C_ASSERT(offsetof(AwareLock, m_MonitorHeld) == 0) +#endif // !__GNUC__ + +#define AwareLock_m_HoldingThread 8 +ASMCONSTANTS_C_ASSERT(offsetof(AwareLock, m_HoldingThread) == AwareLock_m_HoldingThread) + +#define AwareLock_m_Recursion 4 +ASMCONSTANTS_C_ASSERT(offsetof(AwareLock, m_Recursion) == AwareLock_m_Recursion) + +#define BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX_ASM 0x08000000 +ASMCONSTANTS_C_ASSERT(BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX_ASM == BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) + +#define BIT_SBLK_SPIN_LOCK_ASM 0x10000000 +ASMCONSTANTS_C_ASSERT(BIT_SBLK_SPIN_LOCK_ASM == BIT_SBLK_SPIN_LOCK) + +#define SBLK_MASK_LOCK_THREADID_ASM 0x000003FF // special value of 0 + 1023 thread ids +ASMCONSTANTS_C_ASSERT(SBLK_MASK_LOCK_THREADID_ASM == SBLK_MASK_LOCK_THREADID) + +#define SBLK_MASK_LOCK_RECLEVEL_ASM 0x0000FC00 // 64 recursion levels +ASMCONSTANTS_C_ASSERT(SBLK_MASK_LOCK_RECLEVEL_ASM == SBLK_MASK_LOCK_RECLEVEL) + +#define SBLK_LOCK_RECLEVEL_INC_ASM 0x00000400 // each level is this much higher than the previous one +ASMCONSTANTS_C_ASSERT(SBLK_LOCK_RECLEVEL_INC_ASM == SBLK_LOCK_RECLEVEL_INC) + +#define BIT_SBLK_IS_HASHCODE_ASM 0x04000000 +ASMCONSTANTS_C_ASSERT(BIT_SBLK_IS_HASHCODE_ASM == BIT_SBLK_IS_HASHCODE) + +#define MASK_SYNCBLOCKINDEX_ASM 0x03ffffff // ((1<::FindCompileTimeConstant' : cannot access private member declared in class 'FindCompileTimeConstant' +// with +// [ +// N=1520 +// ] +// d:\dd\clr\src\ndp\clr\src\vm\i386\asmconstants.h(321) : see declaration of 'FindCompileTimeConstant::FindCompileTimeConstant' +// with +// [ +// N=1520 +// ] +template +class FindCompileTimeConstant +{ +private: + FindCompileTimeConstant(); +}; + +void BogusFunction() +{ + // Sample usage to generate the error + FindCompileTimeConstant bogus_variable; +} +#endif // defined(__cplusplus) && defined(USE_COMPILE_TIME_CONSTANT_FINDER) diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/i386/asmhelpers.asm b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/i386/asmhelpers.asm new file mode 100644 index 00000000000000..142311b64a28f5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/i386/asmhelpers.asm @@ -0,0 +1,2438 @@ +; Licensed to the .NET Foundation under one or more agreements. +; The .NET Foundation licenses this file to you under the MIT license. + +; ==++== +; + +; +; ==--== +; +; FILE: asmhelpers.asm +; +; *** NOTE: If you make changes to this file, propagate the changes to +; asmhelpers.s in this directory +; + +; +; ====================================================================================== + + .586 + .model flat + +include asmconstants.inc + + assume fs: nothing + option casemap:none + .code + +EXTERN __imp__RtlUnwind@16:DWORD +ifdef _DEBUG +EXTERN _HelperMethodFrameConfirmState@20:PROC +endif +ifdef FEATURE_MIXEDMODE +EXTERN _IJWNOADThunkJumpTargetHelper@4:PROC +endif +EXTERN _StubRareEnableWorker@4:PROC +ifdef FEATURE_COMINTEROP +EXTERN _StubRareDisableHRWorker@4:PROC +endif ; FEATURE_COMINTEROP +EXTERN _StubRareDisableTHROWWorker@4:PROC +EXTERN __imp__TlsGetValue@4:DWORD +TlsGetValue PROTO stdcall +ifdef FEATURE_HIJACK +EXTERN _OnHijackObjectWorker@4:PROC +EXTERN _OnHijackInteriorPointerWorker@4:PROC +EXTERN _OnHijackScalarWorker@4:PROC +endif ;FEATURE_HIJACK +EXTERN _COMPlusEndCatch@20:PROC +EXTERN _COMPlusFrameHandler:PROC +ifdef FEATURE_COMINTEROP +EXTERN _COMPlusFrameHandlerRevCom:PROC +endif ; FEATURE_COMINTEROP +EXTERN __alloca_probe:PROC +EXTERN _NDirectImportWorker@4:PROC +EXTERN _UMThunkStubRareDisableWorker@8:PROC +ifndef FEATURE_IMPLICIT_TLS +ifdef ENABLE_GET_THREAD_GENERIC_FULL_CHECK +; This is defined in C (threads.cpp) and enforces EE_THREAD_NOT_REQUIRED contracts +GetThreadGenericFullCheck EQU ?GetThreadGenericFullCheck@@YGPAVThread@@XZ +EXTERN GetThreadGenericFullCheck:PROC +endif ; ENABLE_GET_THREAD_GENERIC_FULL_CHECK + +EXTERN _gThreadTLSIndex:DWORD +EXTERN _gAppDomainTLSIndex:DWORD +endif ; FEATURE_IMPLICIT_TLS + +EXTERN _VarargPInvokeStubWorker@12:PROC +EXTERN _GenericPInvokeCalliStubWorker@12:PROC + +; To debug that LastThrownObjectException really is EXCEPTION_COMPLUS +ifdef TRACK_CXX_EXCEPTION_CODE_HACK +EXTERN __imp____CxxFrameHandler:PROC +endif + +EXTERN _GetThread@0:PROC +EXTERN _GetAppDomain@0:PROC + +ifdef MDA_SUPPORTED +EXTERN _PInvokeStackImbalanceWorker@8:PROC +endif + +ifndef FEATURE_CORECLR +EXTERN _CopyCtorCallStubWorker@4:PROC +endif + +EXTERN _PreStubWorker@8:PROC + +ifdef FEATURE_COMINTEROP +EXTERN _CLRToCOMWorker@8:PROC +endif + +ifdef FEATURE_REMOTING +EXTERN _TransparentProxyStubWorker@8:PROC +endif + +ifdef FEATURE_PREJIT +EXTERN _ExternalMethodFixupWorker@16:PROC +EXTERN _VirtualMethodFixupWorker@8:PROC +EXTERN _StubDispatchFixupWorker@16:PROC +endif + +ifdef FEATURE_COMINTEROP +EXTERN _ComPreStubWorker@8:PROC +endif + +ifdef FEATURE_READYTORUN +EXTERN _DynamicHelperWorker@20:PROC +endif + +ifdef FEATURE_REMOTING +EXTERN _InContextTPQuickDispatchAsmStub@0:PROC +endif + +EXTERN @JIT_InternalThrow@4:PROC + +EXTERN @ProfileEnter@8:PROC +EXTERN @ProfileLeave@8:PROC +EXTERN @ProfileTailcall@8:PROC + +FASTCALL_FUNC macro FuncName,cbArgs +FuncNameReal EQU @&FuncName&@&cbArgs +FuncNameReal proc public +endm + +FASTCALL_ENDFUNC macro +FuncNameReal endp +endm + +ifdef FEATURE_COMINTEROP +ifdef _DEBUG + CPFH_STACK_SIZE equ SIZEOF_FrameHandlerExRecord + STACK_OVERWRITE_BARRIER_SIZE*4 +else ; _DEBUG + CPFH_STACK_SIZE equ SIZEOF_FrameHandlerExRecord +endif ; _DEBUG + +PUSH_CPFH_FOR_COM macro trashReg, pFrameBaseReg, pFrameOffset + + ; + ; Setup the FrameHandlerExRecord + ; + push dword ptr [pFrameBaseReg + pFrameOffset] + push _COMPlusFrameHandlerRevCom + mov trashReg, fs:[0] + push trashReg + mov fs:[0], esp + +ifdef _DEBUG + mov trashReg, STACK_OVERWRITE_BARRIER_SIZE +@@: + push STACK_OVERWRITE_BARRIER_VALUE + dec trashReg + jnz @B +endif ; _DEBUG + +endm ; PUSH_CPFH_FOR_COM + + +POP_CPFH_FOR_COM macro trashReg + + ; + ; Unlink FrameHandlerExRecord from FS:0 chain + ; +ifdef _DEBUG + add esp, STACK_OVERWRITE_BARRIER_SIZE*4 +endif + mov trashReg, [esp + OFFSETOF__FrameHandlerExRecord__m_ExReg__Next] + mov fs:[0], trashReg + add esp, SIZEOF_FrameHandlerExRecord + +endm ; POP_CPFH_FOR_COM +endif ; FEATURE_COMINTEROP + +; +; FramedMethodFrame prolog +; +STUB_PROLOG macro + ; push ebp-frame + push ebp + mov ebp,esp + + ; save CalleeSavedRegisters + push ebx + push esi + push edi + + ; push ArgumentRegisters + push ecx + push edx +endm + +; +; FramedMethodFrame epilog +; +STUB_EPILOG macro + ; pop ArgumentRegisters + pop edx + pop ecx + + ; pop CalleeSavedRegisters + pop edi + pop esi + pop ebx + pop ebp +endm + +; +; FramedMethodFrame epilog +; +STUB_EPILOG_RETURN macro + ; pop ArgumentRegisters + add esp, 8 + + ; pop CalleeSavedRegisters + pop edi + pop esi + pop ebx + pop ebp +endm + +STUB_PROLOG_2_HIDDEN_ARGS macro + + ; + ; The stub arguments are where we want to setup the TransitionBlock. We will + ; setup the TransitionBlock later once we can trash them + ; + ; push ebp-frame + ; push ebp + ; mov ebp,esp + + ; save CalleeSavedRegisters + ; push ebx + + push esi + push edi + + ; push ArgumentRegisters + push ecx + push edx + + mov ecx, [esp + 4*4] + mov edx, [esp + 5*4] + + ; Setup up proper EBP frame now that the stub arguments can be trashed + mov [esp + 4*4],ebx + mov [esp + 5*4],ebp + lea ebp, [esp + 5*4] +endm + +ResetCurrentContext PROC stdcall public + LOCAL ctrlWord:WORD + + ; Clear the direction flag (used for rep instructions) + cld + + fnstcw ctrlWord + fninit ; reset FPU + and ctrlWord, 0f00h ; preserve precision and rounding control + or ctrlWord, 007fh ; mask all exceptions + fldcw ctrlWord ; preserve precision control + RET +ResetCurrentContext ENDP + +;Incoming: +; ESP+4: Pointer to buffer to which FPU state should be saved +_CaptureFPUContext@4 PROC public + + mov ecx, [esp+4] + fnstenv [ecx] + retn 4 + +_CaptureFPUContext@4 ENDP + +; Incoming: +; ESP+4: Pointer to buffer from which FPU state should be restored +_RestoreFPUContext@4 PROC public + + mov ecx, [esp+4] + fldenv [ecx] + retn 4 + +_RestoreFPUContext@4 ENDP + +ifndef FEATURE_CORECLR +ifdef _DEBUG +; For C++ exceptions, we desperately need to know the SEH code. This allows us to properly +; distinguish managed exceptions from C++ exceptions from standard SEH like hard stack overflow. +; We do this by providing our own handler that squirrels away the exception code and then +; defers to the C++ service. Fortunately, two symbols exist for the C++ symbol. +___CxxFrameHandler3 PROC public + + ; We don't know what arguments are passed to us (except for the first arg on stack) + ; It turns out that EAX is part of the non-standard calling convention of this + ; function. + + push eax + push edx + + cmp dword ptr [_gThreadTLSIndex], -1 + je Chain ; CLR is not initialized yet + + call _GetThread@0 + + test eax, eax ; not a managed thread + jz Chain + + mov edx, [esp + 0ch] ; grab the first argument + mov edx, [edx] ; grab the SEH exception code + + mov dword ptr [eax + Thread_m_LastCxxSEHExceptionCode], edx + +Chain: + + pop edx + + ; [esp] contains the value of EAX we must restore. We would like + ; [esp] to contain the address of the real imported CxxFrameHandler + ; so we can chain to it. + + mov eax, [__imp____CxxFrameHandler] + mov eax, [eax] + xchg [esp], eax + + ret + +___CxxFrameHandler3 ENDP +endif ; _DEBUG +endif ; FEATURE_CORECLR + +; Note that RtlUnwind trashes EBX, ESI and EDI, so this wrapper preserves them +CallRtlUnwind PROC stdcall public USES ebx esi edi, pEstablisherFrame :DWORD, callback :DWORD, pExceptionRecord :DWORD, retVal :DWORD + + push retVal + push pExceptionRecord + push callback + push pEstablisherFrame + call dword ptr [__imp__RtlUnwind@16] + + ; return 1 + push 1 + pop eax + + RET +CallRtlUnwind ENDP + +_ResumeAtJitEHHelper@4 PROC public + mov edx, [esp+4] ; edx = pContext (EHContext*) + + mov ebx, [edx+EHContext_Ebx] + mov esi, [edx+EHContext_Esi] + mov edi, [edx+EHContext_Edi] + mov ebp, [edx+EHContext_Ebp] + mov ecx, [edx+EHContext_Esp] + mov eax, [edx+EHContext_Eip] + mov [ecx-4], eax + mov eax, [edx+EHContext_Eax] + mov [ecx-8], eax + mov eax, [edx+EHContext_Ecx] + mov [ecx-0Ch], eax + mov eax, [edx+EHContext_Edx] + mov [ecx-10h], eax + lea esp, [ecx-10h] + pop edx + pop ecx + pop eax + ret +_ResumeAtJitEHHelper@4 ENDP + +; int __stdcall CallJitEHFilterHelper(size_t *pShadowSP, EHContext *pContext); +; on entry, only the pContext->Esp, Ebx, Esi, Edi, Ebp, and Eip are initialized +_CallJitEHFilterHelper@8 PROC public + push ebp + mov ebp, esp + push ebx + push esi + push edi + + pShadowSP equ [ebp+8] + pContext equ [ebp+12] + + mov eax, pShadowSP ; Write esp-4 to the shadowSP slot + test eax, eax + jz DONE_SHADOWSP_FILTER + mov ebx, esp + sub ebx, 4 + or ebx, SHADOW_SP_IN_FILTER_ASM + mov [eax], ebx + DONE_SHADOWSP_FILTER: + + mov edx, [pContext] + mov eax, [edx+EHContext_Eax] + mov ebx, [edx+EHContext_Ebx] + mov esi, [edx+EHContext_Esi] + mov edi, [edx+EHContext_Edi] + mov ebp, [edx+EHContext_Ebp] + + call dword ptr [edx+EHContext_Eip] +ifdef _DEBUG + nop ; Indicate that it is OK to call managed code directly from here +endif + + pop edi + pop esi + pop ebx + pop ebp ; don't use 'leave' here, as ebp as been trashed + retn 8 +_CallJitEHFilterHelper@8 ENDP + + +; void __stdcall CallJITEHFinallyHelper(size_t *pShadowSP, EHContext *pContext); +; on entry, only the pContext->Esp, Ebx, Esi, Edi, Ebp, and Eip are initialized +_CallJitEHFinallyHelper@8 PROC public + push ebp + mov ebp, esp + push ebx + push esi + push edi + + pShadowSP equ [ebp+8] + pContext equ [ebp+12] + + mov eax, pShadowSP ; Write esp-4 to the shadowSP slot + test eax, eax + jz DONE_SHADOWSP_FINALLY + mov ebx, esp + sub ebx, 4 + mov [eax], ebx + DONE_SHADOWSP_FINALLY: + + mov edx, [pContext] + mov eax, [edx+EHContext_Eax] + mov ebx, [edx+EHContext_Ebx] + mov esi, [edx+EHContext_Esi] + mov edi, [edx+EHContext_Edi] + mov ebp, [edx+EHContext_Ebp] + call dword ptr [edx+EHContext_Eip] +ifdef _DEBUG + nop ; Indicate that it is OK to call managed code directly from here +endif + + ; Reflect the changes to the context and only update non-volatile registers. + ; This will be used later to update REGDISPLAY + mov edx, [esp+12+12] + mov [edx+EHContext_Ebx], ebx + mov [edx+EHContext_Esi], esi + mov [edx+EHContext_Edi], edi + mov [edx+EHContext_Ebp], ebp + + pop edi + pop esi + pop ebx + pop ebp ; don't use 'leave' here, as ebp as been trashed + retn 8 +_CallJitEHFinallyHelper@8 ENDP + + +_GetSpecificCpuTypeAsm@0 PROC public + push ebx ; ebx is trashed by the cpuid calls + + ; See if the chip supports CPUID + pushfd + pop ecx ; Get the EFLAGS + mov eax, ecx ; Save for later testing + xor ecx, 200000h ; Invert the ID bit. + push ecx + popfd ; Save the updated flags. + pushfd + pop ecx ; Retrive the updated flags + xor ecx, eax ; Test if it actually changed (bit set means yes) + push eax + popfd ; Restore the flags + + test ecx, 200000h + jz Assume486 + + xor eax, eax + cpuid + + test eax, eax + jz Assume486 ; brif CPUID1 not allowed + + mov eax, 1 + cpuid + + ; filter out everything except family and model + ; Note that some multi-procs have different stepping number for each proc + and eax, 0ff0h + + jmp CpuTypeDone + +Assume486: + mov eax, 0400h ; report 486 +CpuTypeDone: + pop ebx + retn +_GetSpecificCpuTypeAsm@0 ENDP + +; DWORD __stdcall GetSpecificCpuFeaturesAsm(DWORD *pInfo); +_GetSpecificCpuFeaturesAsm@4 PROC public + push ebx ; ebx is trashed by the cpuid calls + + ; See if the chip supports CPUID + pushfd + pop ecx ; Get the EFLAGS + mov eax, ecx ; Save for later testing + xor ecx, 200000h ; Invert the ID bit. + push ecx + popfd ; Save the updated flags. + pushfd + pop ecx ; Retrive the updated flags + xor ecx, eax ; Test if it actually changed (bit set means yes) + push eax + popfd ; Restore the flags + + test ecx, 200000h + jz CpuFeaturesFail + + xor eax, eax + cpuid + + test eax, eax + jz CpuFeaturesDone ; br if CPUID1 not allowed + + mov eax, 1 + cpuid + mov eax, edx ; return all feature flags + mov edx, [esp+8] + test edx, edx + jz CpuFeaturesDone + mov [edx],ebx ; return additional useful information + jmp CpuFeaturesDone + +CpuFeaturesFail: + xor eax, eax ; Nothing to report +CpuFeaturesDone: + pop ebx + retn 4 +_GetSpecificCpuFeaturesAsm@4 ENDP + + +;----------------------------------------------------------------------- +; The out-of-line portion of the code to enable preemptive GC. +; After the work is done, the code jumps back to the "pRejoinPoint" +; which should be emitted right after the inline part is generated. +; +; Assumptions: +; ebx = Thread +; Preserves +; all registers except ecx. +; +;----------------------------------------------------------------------- +_StubRareEnable proc public + push eax + push edx + + push ebx + call _StubRareEnableWorker@4 + + pop edx + pop eax + retn +_StubRareEnable ENDP + +ifdef FEATURE_COMINTEROP +_StubRareDisableHR proc public + push edx + + push ebx ; Thread + call _StubRareDisableHRWorker@4 + + pop edx + retn +_StubRareDisableHR ENDP +endif ; FEATURE_COMINTEROP + +_StubRareDisableTHROW proc public + push eax + push edx + + push ebx ; Thread + call _StubRareDisableTHROWWorker@4 + + pop edx + pop eax + retn +_StubRareDisableTHROW endp + + +ifdef FEATURE_MIXEDMODE +; VOID __stdcall IJWNOADThunkJumpTarget(void); +; This routine is used by the IJWNOADThunk to determine the callsite of the domain-specific stub to call. +_IJWNOADThunkJumpTarget@0 proc public + + push ebp + mov ebp, esp + + ; EAX contains IJWNOADThunk* + ; Must retain ebx, ecx, edx, esi, edi. + + ; save ebx - holds the IJWNOADThunk* + ; save ecx - holds the current AppDomain ID. + ; save edx - holds the cached AppDomain ID. + push ebx + push ecx + + ; put the IJWNOADThunk into ebx for safe keeping + mov ebx, eax + + ; get thread - assumes registers are preserved + call _GetThread@0 + + ; if thread is null, go down un-optimized path + test eax,eax + jz cachemiss + + ; get current domain - assumes registers are preserved + call _GetAppDomain@0 + + ; if domain is null, go down un-optimized path + test eax,eax + jz cachemiss + + ; get the current appdomain id + mov ecx, [eax + AppDomain__m_dwId] + + ; test it against each cache location + mov eax, ebx + add eax, IJWNOADThunk__m_cache + cmp ecx, [eax] + je cachehit + + add eax, IJWNOADThunk__NextCacheOffset + cmp ecx, [eax] + je cachehit + + add eax, IJWNOADThunk__NextCacheOffset + cmp ecx, [eax] + je cachehit + + add eax, IJWNOADThunk__NextCacheOffset + cmp ecx, [eax] + je cachehit + +cachemiss: + ; save extra registers + push edx + push esi + push edi + + ; call unoptimized path + push ebx ; only arg is IJWNOADThunk* + call _IJWNOADThunkJumpTargetHelper@4 + + ; restore extra registers + pop edi + pop esi + pop edx + + ; jump back up to the epilog + jmp complete + +cachehit: + ; found a matching ADID, get the code addr. + mov eax, [eax + IJWNOADThunk__CodeAddrOffsetFromADID] + + ; if the callsite is null, go down the un-optimized path + test eax, eax + jz cachemiss + +complete: + ; restore regs + pop ecx + pop ebx + + mov esp, ebp + pop ebp + + ; Jump to callsite + jmp eax + + ; This will never be executed. It is just to help out stack-walking logic + ; which disassembles the epilog to unwind the stack. + ret +_IJWNOADThunkJumpTarget@0 endp + +endif + +InternalExceptionWorker proc public + pop edx ; recover RETADDR + add esp, eax ; release caller's args + push edx ; restore RETADDR + jmp @JIT_InternalThrow@4 +InternalExceptionWorker endp + +; EAX -> number of caller arg bytes on the stack that we must remove before going +; to the throw helper, which assumes the stack is clean. +_ArrayOpStubNullException proc public + ; kFactorReg and kTotalReg could not have been modified, but let's pop + ; them anyway for consistency and to avoid future bugs. + pop esi + pop edi + mov ecx, CORINFO_NullReferenceException_ASM + jmp InternalExceptionWorker +_ArrayOpStubNullException endp + +; EAX -> number of caller arg bytes on the stack that we must remove before going +; to the throw helper, which assumes the stack is clean. +_ArrayOpStubRangeException proc public + ; kFactorReg and kTotalReg could not have been modified, but let's pop + ; them anyway for consistency and to avoid future bugs. + pop esi + pop edi + mov ecx, CORINFO_IndexOutOfRangeException_ASM + jmp InternalExceptionWorker +_ArrayOpStubRangeException endp + +; EAX -> number of caller arg bytes on the stack that we must remove before going +; to the throw helper, which assumes the stack is clean. +_ArrayOpStubTypeMismatchException proc public + ; kFactorReg and kTotalReg could not have been modified, but let's pop + ; them anyway for consistency and to avoid future bugs. + pop esi + pop edi + mov ecx, CORINFO_ArrayTypeMismatchException_ASM + jmp InternalExceptionWorker +_ArrayOpStubTypeMismatchException endp + +;------------------------------------------------------------------------------ +; This helper routine enregisters the appropriate arguments and makes the +; actual call. +;------------------------------------------------------------------------------ +; void STDCALL CallDescrWorkerInternal(CallDescrWorkerParams * pParams) +CallDescrWorkerInternal PROC stdcall public USES EBX, + pParams: DWORD + + mov ebx, pParams + + mov ecx, [ebx+CallDescrData__numStackSlots] + mov eax, [ebx+CallDescrData__pSrc] ; copy the stack + test ecx, ecx + jz donestack + lea eax, [eax+4*ecx-4] ; last argument + push dword ptr [eax] + dec ecx + jz donestack + sub eax, 4 + push dword ptr [eax] + dec ecx + jz donestack +stackloop: + sub eax, 4 + push dword ptr [eax] + dec ecx + jnz stackloop +donestack: + + ; now we must push each field of the ArgumentRegister structure + mov eax, [ebx+CallDescrData__pArgumentRegisters] + mov edx, dword ptr [eax] + mov ecx, dword ptr [eax+4] + + call [ebx+CallDescrData__pTarget] +ifdef _DEBUG + nop ; This is a tag that we use in an assert. Fcalls expect to + ; be called from Jitted code or from certain blessed call sites like + ; this one. (See HelperMethodFrame::InsureInit) +endif + + ; Save FP return value if necessary + mov ecx, [ebx+CallDescrData__fpReturnSize] + cmp ecx, 0 + je ReturnsInt + + cmp ecx, 4 + je ReturnsFloat + cmp ecx, 8 + je ReturnsDouble + ; unexpected + jmp Epilog + +ReturnsInt: + mov [ebx+CallDescrData__returnValue], eax + mov [ebx+CallDescrData__returnValue+4], edx + +Epilog: + RET + +ReturnsFloat: + fstp dword ptr [ebx+CallDescrData__returnValue] ; Spill the Float return value + jmp Epilog + +ReturnsDouble: + fstp qword ptr [ebx+CallDescrData__returnValue] ; Spill the Double return value + jmp Epilog + +CallDescrWorkerInternal endp + +ifdef _DEBUG +; int __fastcall HelperMethodFrameRestoreState(HelperMethodFrame*, struct MachState *) +FASTCALL_FUNC HelperMethodFrameRestoreState,8 + mov eax, edx ; eax = MachState* +else +; int __fastcall HelperMethodFrameRestoreState(struct MachState *) +FASTCALL_FUNC HelperMethodFrameRestoreState,4 + mov eax, ecx ; eax = MachState* +endif + ; restore the registers from the m_MachState stucture. Note that + ; we only do this for register that where not saved on the stack + ; at the time the machine state snapshot was taken. + + cmp [eax+MachState__pRetAddr], 0 + +ifdef _DEBUG + jnz noConfirm + push ebp + push ebx + push edi + push esi + push ecx ; HelperFrame* + call _HelperMethodFrameConfirmState@20 + ; on return, eax = MachState* + cmp [eax+MachState__pRetAddr], 0 +noConfirm: +endif + + jz doRet + + lea edx, [eax+MachState__esi] ; Did we have to spill ESI + cmp [eax+MachState__pEsi], edx + jnz SkipESI + mov esi, [edx] ; Then restore it +SkipESI: + + lea edx, [eax+MachState__edi] ; Did we have to spill EDI + cmp [eax+MachState__pEdi], edx + jnz SkipEDI + mov edi, [edx] ; Then restore it +SkipEDI: + + lea edx, [eax+MachState__ebx] ; Did we have to spill EBX + cmp [eax+MachState__pEbx], edx + jnz SkipEBX + mov ebx, [edx] ; Then restore it +SkipEBX: + + lea edx, [eax+MachState__ebp] ; Did we have to spill EBP + cmp [eax+MachState__pEbp], edx + jnz SkipEBP + mov ebp, [edx] ; Then restore it +SkipEBP: + +doRet: + xor eax, eax + retn +FASTCALL_ENDFUNC HelperMethodFrameRestoreState + + +ifndef FEATURE_IMPLICIT_TLS +;--------------------------------------------------------------------------- +; Portable GetThread() function: used if no platform-specific optimizations apply. +; This is in assembly code because we count on edx not getting trashed on calls +; to this function. +;--------------------------------------------------------------------------- +; Thread* __stdcall GetThreadGeneric(void); +GetThreadGeneric PROC stdcall public USES ecx edx + +ifdef _DEBUG + cmp dword ptr [_gThreadTLSIndex], -1 + jnz @F + int 3 +@@: +endif +ifdef ENABLE_GET_THREAD_GENERIC_FULL_CHECK + ; non-PAL, debug-only GetThreadGeneric should defer to GetThreadGenericFullCheck + ; to do extra contract enforcement. (See GetThreadGenericFullCheck for details.) + ; This code is intentionally not added to asmhelper.s, as this enforcement is only + ; implemented for non-PAL builds. + call GetThreadGenericFullCheck +else + push dword ptr [_gThreadTLSIndex] + call dword ptr [__imp__TlsGetValue@4] +endif + ret +GetThreadGeneric ENDP + +;--------------------------------------------------------------------------- +; Portable GetAppdomain() function: used if no platform-specific optimizations apply. +; This is in assembly code because we count on edx not getting trashed on calls +; to this function. +;--------------------------------------------------------------------------- +; Appdomain* __stdcall GetAppDomainGeneric(void); +GetAppDomainGeneric PROC stdcall public USES ecx edx + +ifdef _DEBUG + cmp dword ptr [_gAppDomainTLSIndex], -1 + jnz @F + int 3 +@@: +endif + + push dword ptr [_gAppDomainTLSIndex] + call dword ptr [__imp__TlsGetValue@4] + ret +GetAppDomainGeneric ENDP +endif + + +ifdef FEATURE_HIJACK + +; A JITted method's return address was hijacked to return to us here. What we do +; is make a __cdecl call with 2 ints. One is the return value we wish to preserve. +; The other is space for our real return address. +; +;VOID __stdcall OnHijackObjectTripThread(); +OnHijackObjectTripThread PROC stdcall public + + ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay + ; and HijackArgs + push eax ; make room for the real return address (Eip) + push ebp + push eax + push ecx + push edx + push ebx + push esi + push edi + + ; unused space for floating point state + sub esp,12 + + push esp + call _OnHijackObjectWorker@4 + + ; unused space for floating point state + add esp,12 + + pop edi + pop esi + pop ebx + pop edx + pop ecx + pop eax + pop ebp + retn ; return to the correct place, adjusted by our caller +OnHijackObjectTripThread ENDP + + +; VOID OnHijackInteriorPointerTripThread() +OnHijackInteriorPointerTripThread PROC stdcall public + + ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay + ; and HijackArgs + push eax ; make room for the real return address (Eip) + push ebp + push eax + push ecx + push edx + push ebx + push esi + push edi + + ; unused space for floating point state + sub esp,12 + + push esp + call _OnHijackInteriorPointerWorker@4 + + ; unused space for floating point state + add esp,12 + + pop edi + pop esi + pop ebx + pop edx + pop ecx + pop eax + pop ebp + retn ; return to the correct place, adjusted by our caller +OnHijackInteriorPointerTripThread ENDP + +; VOID OnHijackScalarTripThread() +OnHijackScalarTripThread PROC stdcall public + + ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay + ; and HijackArgs + push eax ; make room for the real return address (Eip) + push ebp + push eax + push ecx + push edx + push ebx + push esi + push edi + + ; unused space for floating point state + sub esp,12 + + push esp + call _OnHijackScalarWorker@4 + + ; unused space for floating point state + add esp,12 + + pop edi + pop esi + pop ebx + pop edx + pop ecx + pop eax + pop ebp + retn ; return to the correct place, adjusted by our caller +OnHijackScalarTripThread ENDP + +; VOID OnHijackFloatingPointTripThread() +OnHijackFloatingPointTripThread PROC stdcall public + + ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay + ; and HijackArgs + push eax ; make room for the real return address (Eip) + push ebp + push eax + push ecx + push edx + push ebx + push esi + push edi + + sub esp,12 + + ; save top of the floating point stack (there is return value passed in it) + ; save full 10 bytes to avoid precision loss + fstp tbyte ptr [esp] + + push esp + call _OnHijackScalarWorker@4 + + ; restore top of the floating point stack + fld tbyte ptr [esp] + + add esp,12 + + pop edi + pop esi + pop ebx + pop edx + pop ecx + pop eax + pop ebp + retn ; return to the correct place, adjusted by our caller +OnHijackFloatingPointTripThread ENDP + +endif ; FEATURE_HIJACK + + +; Note that the debugger skips this entirely when doing SetIP, +; since COMPlusCheckForAbort should always return 0. Excep.cpp:LeaveCatch +; asserts that to be true. If this ends up doing more work, then the +; debugger may need additional support. +; void __stdcall JIT_EndCatch(); +JIT_EndCatch PROC stdcall public + + ; make temp storage for return address, and push the address of that + ; as the last arg to COMPlusEndCatch + mov ecx, [esp] + push ecx; + push esp; + + ; push the rest of COMPlusEndCatch's args, right-to-left + push esi + push edi + push ebx + push ebp + + call _COMPlusEndCatch@20 ; returns old esp value in eax, stores jump address + ; now eax = new esp, [esp] = new eip + + pop edx ; edx = new eip + mov esp, eax ; esp = new esp + jmp edx ; eip = new eip + +JIT_EndCatch ENDP + +;========================================================================== +; This function is reached only via the embedded ImportThunkGlue code inside +; an NDirectMethodDesc. It's purpose is to load the DLL associated with an +; N/Direct method, then backpatch the DLL target into the methoddesc. +; +; Initial state: +; +; Preemptive GC is *enabled*: we are actually in an unmanaged state. +; +; +; [esp+...] - The *unmanaged* parameters to the DLL target. +; [esp+4] - Return address back into the JIT'ted code that made +; the DLL call. +; [esp] - Contains the "return address." Because we got here +; thru a call embedded inside a MD, this "return address" +; gives us an easy to way to find the MD (which was the +; whole purpose of the embedded call manuever.) +; +; +; +;========================================================================== +_NDirectImportThunk@0 proc public + + ; Preserve argument registers + push ecx + push edx + + ; Invoke the function that does the real work. + push eax + call _NDirectImportWorker@4 + + ; Restore argument registers + pop edx + pop ecx + + ; If we got back from NDirectImportWorker, the MD has been successfully + ; linked and "eax" contains the DLL target. Proceed to execute the + ; original DLL call. + jmp eax ; Jump to DLL target +_NDirectImportThunk@0 endp + +;========================================================================== +; The call in fixup precode initally points to this function. +; The pupose of this function is to load the MethodDesc and forward the call the prestub. +_PrecodeFixupThunk@0 proc public + + pop eax ; Pop the return address. It points right after the call instruction in the precode. + push esi + push edi + + ; Inline computation done by FixupPrecode::GetMethodDesc() + movzx esi,byte ptr [eax+2] ; m_PrecodeChunkIndex + movzx edi,byte ptr [eax+1] ; m_MethodDescChunkIndex + mov eax,dword ptr [eax+esi*8+3] + lea eax,[eax+edi*4] + + pop edi + pop esi + jmp _ThePreStub@0 + +_PrecodeFixupThunk@0 endp + +; LPVOID __stdcall CTPMethodTable__CallTargetHelper2( +; const void *pTarget, +; LPVOID pvFirst, +; LPVOID pvSecond) +CTPMethodTable__CallTargetHelper2 proc stdcall public, + pTarget : DWORD, + pvFirst : DWORD, + pvSecond : DWORD + mov ecx, pvFirst + mov edx, pvSecond + + call pTarget +ifdef _DEBUG + nop ; Mark this as a special call site that can + ; directly call unmanaged code +endif + ret +CTPMethodTable__CallTargetHelper2 endp + +; LPVOID __stdcall CTPMethodTable__CallTargetHelper3( +; const void *pTarget, +; LPVOID pvFirst, +; LPVOID pvSecond, +; LPVOID pvThird) +CTPMethodTable__CallTargetHelper3 proc stdcall public, + pTarget : DWORD, + pvFirst : DWORD, + pvSecond : DWORD, + pvThird : DWORD + push pvThird + + mov ecx, pvFirst + mov edx, pvSecond + + call pTarget +ifdef _DEBUG + nop ; Mark this as a special call site that can + ; directly call unmanaged code +endif + ret +CTPMethodTable__CallTargetHelper3 endp + + +; void __stdcall setFPReturn(int fpSize, INT64 retVal) +_setFPReturn@12 proc public + mov ecx, [esp+4] + + ; leave the return value in eax:edx if it is not the floating point case + mov eax, [esp+8] + mov edx, [esp+12] + + cmp ecx, 4 + jz setFPReturn4 + + cmp ecx, 8 + jnz setFPReturnNot8 + fld qword ptr [esp+8] +setFPReturnNot8: + retn 12 + +setFPReturn4: + fld dword ptr [esp+8] + retn 12 +_setFPReturn@12 endp + +; void __stdcall getFPReturn(int fpSize, INT64 *pretVal) +_getFPReturn@8 proc public + mov ecx, [esp+4] + mov eax, [esp+8] + cmp ecx, 4 + jz getFPReturn4 + + cmp ecx, 8 + jnz getFPReturnNot8 + fstp qword ptr [eax] +getFPReturnNot8: + retn 8 + +getFPReturn4: + fstp dword ptr [eax] + retn 8 +_getFPReturn@8 endp + +; void __stdcall UM2MThunk_WrapperHelper(void *pThunkArgs, +; int argLen, +; void *pAddr, +; UMEntryThunk *pEntryThunk, +; Thread *pThread) +UM2MThunk_WrapperHelper proc stdcall public, + pThunkArgs : DWORD, + argLen : DWORD, + pAddr : DWORD, + pEntryThunk : DWORD, + pThread : DWORD + + push ebx + + mov eax, pEntryThunk + mov ecx, pThread + mov ebx, pThunkArgs + call pAddr + + pop ebx + + ret +UM2MThunk_WrapperHelper endp + +; VOID __cdecl UMThunkStubRareDisable() +; +; @todo: this is very similar to StubRareDisable +; +_UMThunkStubRareDisable proc public + push eax + push ecx + + push eax ; Push the UMEntryThunk + push ecx ; Push thread + call _UMThunkStubRareDisableWorker@8 + + pop ecx + pop eax + retn +_UMThunkStubRareDisable endp + + +;+---------------------------------------------------------------------------- +; +; Method: CRemotingServices::CheckForContextMatch public +; +; Synopsis: This code generates a check to see if the current context and +; the context of the proxy match. +; +;+---------------------------------------------------------------------------- +; +; returns zero if contexts match +; returns non-zero if contexts do not match +; +; UINT_PTR __stdcall CRemotingServices__CheckForContextMatch(Object* pStubData) +ifdef FEATURE_REMOTING +_CRemotingServices__CheckForContextMatch@4 proc public + push ebx ; spill ebx + mov ebx, [eax+4] ; Get the internal context id by unboxing + ; the stub data + call _GetThread@0 ; Get the current thread, assumes that the + ; registers are preserved + mov eax, [eax+Thread_m_Context] ; Get the current context from the + ; thread + sub eax, ebx ; Get the pointer to the context from the + ; proxy and compare with the current context + pop ebx ; restore the value of ebx + retn +_CRemotingServices__CheckForContextMatch@4 endp +endif ; FEATURE_REMOTING + +;+---------------------------------------------------------------------------- +; +; Method: CRemotingServices::DispatchInterfaceCall public +; +; Synopsis: +; Push that method desc on the stack and jump to the +; transparent proxy stub to execute the call. +; WARNING!! This MethodDesc is not the methoddesc in the vtable +; of the object instead it is the methoddesc in the vtable of +; the interface class. Since we use the MethodDesc only to probe +; the stack via the signature of the method call we are safe. +; If we want to get any object vtable/class specific +; information this is not safe. +; +; +;+---------------------------------------------------------------------------- +; void __stdcall CRemotingServices__DispatchInterfaceCall() +ifdef FEATURE_REMOTING +_CRemotingServices__DispatchInterfaceCall@0 proc public + ; push MethodDesc* passed in eax by precode and forward to the worker + push eax + + ; NOTE: At this point the stack looks like + ; + ; esp---> saved MethodDesc of Interface method + ; return addr of calling function + ; + mov eax, [ecx + TransparentProxyObject___stubData] + call [ecx + TransparentProxyObject___stub] +ifdef _DEBUG + nop ; Mark this as a special call site that can directly + ; call managed code +endif + test eax, eax + jnz CtxMismatch + jmp _InContextTPQuickDispatchAsmStub@0 + +CtxMismatch: + pop eax ; restore MethodDesc * + jmp _TransparentProxyStub_CrossContext@0 ; jump to slow TP stub +_CRemotingServices__DispatchInterfaceCall@0 endp +endif ; FEATURE_REMOTING + + +;+---------------------------------------------------------------------------- +; +; Method: CRemotingServices::CallFieldGetter private +; +; Synopsis: Calls the field getter function (Object::__FieldGetter) in +; managed code by setting up the stack and calling the target +; +; +;+---------------------------------------------------------------------------- +; void __stdcall CRemotingServices__CallFieldGetter( +; MethodDesc *pMD, +; LPVOID pThis, +; LPVOID pFirst, +; LPVOID pSecond, +; LPVOID pThird) +ifdef FEATURE_REMOTING +CRemotingServices__CallFieldGetter proc stdcall public, + pMD : DWORD, + pThis : DWORD, + pFirst : DWORD, + pSecond : DWORD, + pThird : DWORD + + push [pSecond] ; push the second argument on the stack + push [pThird] ; push the third argument on the stack + + mov ecx, [pThis] ; enregister pThis, the 'this' pointer + mov edx, [pFirst] ; enregister pFirst, the first argument + + mov eax, [pMD] ; load MethodDesc of object::__FieldGetter + call _TransparentProxyStub_CrossContext@0 ; call the TP stub + + ret +CRemotingServices__CallFieldGetter endp +endif ; FEATURE_REMOTING + +;+---------------------------------------------------------------------------- +; +; Method: CRemotingServices::CallFieldSetter private +; +; Synopsis: Calls the field setter function (Object::__FieldSetter) in +; managed code by setting up the stack and calling the target +; +; +;+---------------------------------------------------------------------------- +; void __stdcall CRemotingServices__CallFieldSetter( +; MethodDesc *pMD, +; LPVOID pThis, +; LPVOID pFirst, +; LPVOID pSecond, +; LPVOID pThird) +ifdef FEATURE_REMOTING +CRemotingServices__CallFieldSetter proc stdcall public, + pMD : DWORD, + pThis : DWORD, + pFirst : DWORD, + pSecond : DWORD, + pThird : DWORD + + push [pSecond] ; push the field name (second arg) + push [pThird] ; push the object (third arg) on the stack + + mov ecx, [pThis] ; enregister pThis, the 'this' pointer + mov edx, [pFirst] ; enregister the first argument + + mov eax, [pMD] ; load MethodDesc of object::__FieldGetter + call _TransparentProxyStub_CrossContext@0 ; call the TP stub + + ret +CRemotingServices__CallFieldSetter endp +endif ; FEATURE_REMOTING + +;+---------------------------------------------------------------------------- +; +; Method: CTPMethodTable::GenericCheckForContextMatch private +; +; Synopsis: Calls the stub in the TP & returns TRUE if the contexts +; match, FALSE otherwise. +; +; Note: 1. Called during FieldSet/Get, used for proxy extensibility +; +;+---------------------------------------------------------------------------- +; BOOL __stdcall CTPMethodTable__GenericCheckForContextMatch(Object* orTP) +ifdef FEATURE_REMOTING +CTPMethodTable__GenericCheckForContextMatch proc stdcall public uses ecx, tp : DWORD + + mov ecx, [tp] + mov eax, [ecx + TransparentProxyObject___stubData] + call [ecx + TransparentProxyObject___stub] +ifdef _DEBUG + nop ; Mark this as a special call site that can directly + ; call managed code +endif + test eax, eax + mov eax, 0 + setz al + ; NOTE: In the CheckForXXXMatch stubs (for URT ctx/ Ole32 ctx) eax is + ; non-zero if contexts *do not* match & zero if they do. + ret +CTPMethodTable__GenericCheckForContextMatch endp +endif ; FEATURE_REMOTING + + +; void __stdcall JIT_ProfilerEnterLeaveTailcallStub(UINT_PTR ProfilerHandle) +_JIT_ProfilerEnterLeaveTailcallStub@4 proc public + ; this function must preserve all registers, including scratch + retn 4 +_JIT_ProfilerEnterLeaveTailcallStub@4 endp + +; +; Used to get the current instruction pointer value +; +; UINT_PTR __stdcall GetCurrentIP(void); +_GetCurrentIP@0 proc public + mov eax, [esp] + retn +_GetCurrentIP@0 endp + +; LPVOID __stdcall GetCurrentSP(void); +_GetCurrentSP@0 proc public + mov eax, esp + retn +_GetCurrentSP@0 endp + + +; void __stdcall ProfileEnterNaked(FunctionIDOrClientID functionIDOrClientID); +_ProfileEnterNaked@4 proc public + push esi + push edi + + ; + ; Push in reverse order the fields of ProfilePlatformSpecificData + ; + push dword ptr [esp+8] ; EIP of the managed code that we return to. -- struct ip field + push ebp ; Methods are always EBP framed + add [esp], 8 ; Skip past the return IP, straight to the stack args that were passed to our caller + ; Skip past saved EBP value: 4 bytes + ; - plus return address from caller's caller: 4 bytes + ; + ; Assuming Foo() calls Bar(), and Bar() calls ProfileEnterNake() as illustrated (stack + ; grows up). We want to get what Foo() passed on the stack to Bar(), so we need to pass + ; the return address from caller's caller which is Foo() in this example. + ; + ; ProfileEnterNaked() + ; Bar() + ; Foo() + ; + ; [ESP] is now the ESP of caller's caller pointing to the arguments to the caller. + + push ecx ; -- struct ecx field + push edx ; -- struct edx field + push eax ; -- struct eax field + push 0 ; Create buffer space in the structure -- struct floatingPointValuePresent field + push 0 ; Create buffer space in the structure -- struct floatBuffer field + push 0 ; Create buffer space in the structure -- struct doubleBuffer2 field + push 0 ; Create buffer space in the structure -- struct doubleBuffer1 field + push 0 ; Create buffer space in the structure -- struct functionId field + + mov edx, esp ; the address of the Platform structure + mov ecx, [esp+52]; The functionIDOrClientID parameter that was pushed to FunctionEnter + ; Skip past ProfilePlatformSpecificData we pushed: 40 bytes + ; - plus saved edi, esi : 8 bytes + ; - plus return address from caller: 4 bytes + + call @ProfileEnter@8 + + add esp, 20 ; Remove buffer space + pop eax + pop edx + pop ecx + add esp, 8 ; Remove buffer space + pop edi + pop esi + + retn 4 +_ProfileEnterNaked@4 endp + +; void __stdcall ProfileLeaveNaked(FunctionIDOrClientID functionIDOrClientID); +_ProfileLeaveNaked@4 proc public + push ecx ; We do not strictly need to save ECX, however + ; emitNoGChelper(CORINFO_HELP_PROF_FCN_LEAVE) returns true in the JITcompiler + push edx ; Return value may be in EAX:EDX + + ; + ; Push in reverse order the fields of ProfilePlatformSpecificData + ; + push dword ptr [esp+8] ; EIP of the managed code that we return to. -- struct ip field + push ebp ; Methods are always EBP framed + add [esp], 8 ; Skip past the return IP, straight to the stack args that were passed to our caller + ; Skip past saved EBP value: 4 bytes + ; - plus return address from caller's caller: 4 bytes + ; + ; Assuming Foo() calls Bar(), and Bar() calls ProfileEnterNake() as illustrated (stack + ; grows up). We want to get what Foo() passed on the stack to Bar(), so we need to pass + ; the return address from caller's caller which is Foo() in this example. + ; + ; ProfileEnterNaked() + ; Bar() + ; Foo() + ; + ; [ESP] is now the ESP of caller's caller pointing to the arguments to the caller. + + push ecx ; -- struct ecx field + push edx ; -- struct edx field + push eax ; -- struct eax field + + ; Check if we need to save off any floating point registers + fstsw ax + and ax, 3800h ; Check the top-of-fp-stack bits + cmp ax, 0 ; If non-zero, we have something to save + jnz SaveFPReg + + push 0 ; Create buffer space in the structure -- struct floatingPointValuePresent field + push 0 ; Create buffer space in the structure -- struct floatBuffer field + push 0 ; Create buffer space in the structure -- struct doubleBuffer2 field + push 0 ; Create buffer space in the structure -- struct doubleBuffer1 field + jmp Continue + +SaveFPReg: + push 1 ; mark that a float value is present -- struct floatingPointValuePresent field + sub esp, 4 ; Make room for the FP value + fst dword ptr [esp] ; Copy the FP value to the buffer as a float -- struct floatBuffer field + sub esp, 8 ; Make room for the FP value + fstp qword ptr [esp] ; Copy FP values to the buffer as a double -- struct doubleBuffer1 and doubleBuffer2 fields + +Continue: + push 0 ; Create buffer space in the structure -- struct functionId field + + mov edx, esp ; the address of the Platform structure + mov ecx, [esp+52]; The clientData that was pushed to FunctionEnter + ; Skip past ProfilePlatformSpecificData we pushed: 40 bytes + ; - plus saved edx, ecx : 8 bytes + ; - plus return address from caller: 4 bytes + + call @ProfileLeave@8 + + ; + ; Now see if we have to restore and floating point registers + ; + + cmp [esp + 16], 0 + jz NoRestore + + fld qword ptr [esp + 4] + +NoRestore: + + add esp, 20 ; Remove buffer space + pop eax + add esp, 16 ; Remove buffer space + pop edx + pop ecx + retn 4 +_ProfileLeaveNaked@4 endp + + +; void __stdcall ProfileTailcallNaked(FunctionIDOrClientID functionIDOrClientID); +_ProfileTailcallNaked@4 proc public + push ecx + push edx + + ; + ; Push in reverse order the fields of ProfilePlatformSpecificData + ; + push dword ptr [esp+8] ; EIP of the managed code that we return to. -- struct ip field + push ebp ; Methods are always EBP framed + add [esp], 8 ; Skip past the return IP, straight to the stack args that were passed to our caller + ; Skip past saved EBP value: 4 bytes + ; - plus return address from caller's caller: 4 bytes + ; + ; Assuming Foo() calls Bar(), and Bar() calls ProfileEnterNake() as illustrated (stack + ; grows up). We want to get what Foo() passed on the stack to Bar(), so we need to pass + ; the return address from caller's caller which is Foo() in this example. + ; + ; ProfileEnterNaked() + ; Bar() + ; Foo() + ; + ; [ESP] is now the ESP of caller's caller pointing to the arguments to the caller. + + push ecx ; -- struct ecx field + push edx ; -- struct edx field + push eax ; -- struct eax field + push 0 ; Create buffer space in the structure -- struct floatingPointValuePresent field + push 0 ; Create buffer space in the structure -- struct floatBuffer field + push 0 ; Create buffer space in the structure -- struct doubleBuffer2 field + push 0 ; Create buffer space in the structure -- struct doubleBuffer1 field + push 0 ; Create buffer space in the structure -- struct functionId field + + mov edx, esp ; the address of the Platform structure + mov ecx, [esp+52]; The clientData that was pushed to FunctionEnter + ; Skip past ProfilePlatformSpecificData we pushed: 40 bytes + ; - plus saved edx, ecx : 8 bytes + ; - plus return address from caller: 4 bytes + + call @ProfileTailcall@8 + + add esp, 40 ; Remove buffer space + pop edx + pop ecx + retn 4 +_ProfileTailcallNaked@4 endp + +;========================================================================== +; Invoked for vararg forward P/Invoke calls as a stub. +; Except for secret return buffer, arguments come on the stack so EDX is available as scratch. +; EAX - the NDirectMethodDesc +; ECX - may be return buffer address +; [ESP + 4] - the VASigCookie +; +_VarargPInvokeStub@0 proc public + ; EDX <- VASigCookie + mov edx, [esp + 4] ; skip retaddr + + mov edx, [edx + VASigCookie__StubOffset] + test edx, edx + + jz GoCallVarargWorker + ; --------------------------------------- + + ; EAX contains MD ptr for the IL stub + jmp edx + +GoCallVarargWorker: + ; + ; MD ptr in EAX, VASigCookie ptr at [esp+4] + ; + + STUB_PROLOG + + mov esi, esp + + ; save pMD + push eax + + push eax ; pMD + push dword ptr [esi + 4*7] ; pVaSigCookie + push esi ; pTransitionBlock + + call _VarargPInvokeStubWorker@12 + + ; restore pMD + pop eax + + STUB_EPILOG + + ; jump back to the helper - this time it won't come back here as the stub already exists + jmp _VarargPInvokeStub@0 + +_VarargPInvokeStub@0 endp + +;========================================================================== +; Invoked for marshaling-required unmanaged CALLI calls as a stub. +; EAX - the unmanaged target +; ECX, EDX - arguments +; [ESP + 4] - the VASigCookie +; +_GenericPInvokeCalliHelper@0 proc public + ; save the target + push eax + + ; EAX <- VASigCookie + mov eax, [esp + 8] ; skip target and retaddr + + mov eax, [eax + VASigCookie__StubOffset] + test eax, eax + + jz GoCallCalliWorker + ; --------------------------------------- + + push eax + + ; stack layout at this point: + ; + ; | ... | + ; | stack arguments | ESP + 16 + ; +----------------------+ + ; | VASigCookie* | ESP + 12 + ; +----------------------+ + ; | return address | ESP + 8 + ; +----------------------+ + ; | CALLI target address | ESP + 4 + ; +----------------------+ + ; | stub entry point | ESP + 0 + ; ------------------------ + + ; remove VASigCookie from the stack + mov eax, [esp + 8] + mov [esp + 12], eax + + ; move stub entry point below the RA + mov eax, [esp] + mov [esp + 8], eax + + ; load EAX with the target address + pop eax + pop eax + + ; stack layout at this point: + ; + ; | ... | + ; | stack arguments | ESP + 8 + ; +----------------------+ + ; | return address | ESP + 4 + ; +----------------------+ + ; | stub entry point | ESP + 0 + ; ------------------------ + + ; CALLI target address is in EAX + ret + +GoCallCalliWorker: + ; the target is on the stack and will become m_Datum of PInvokeCalliFrame + ; call the stub generating worker + pop eax + + ; + ; target ptr in EAX, VASigCookie ptr in EDX + ; + + STUB_PROLOG + + mov esi, esp + + ; save target + push eax + + push eax ; unmanaged target + push dword ptr [esi + 4*7] ; pVaSigCookie (first stack argument) + push esi ; pTransitionBlock + + call _GenericPInvokeCalliStubWorker@12 + + ; restore target + pop eax + + STUB_EPILOG + + ; jump back to the helper - this time it won't come back here as the stub already exists + jmp _GenericPInvokeCalliHelper@0 + +_GenericPInvokeCalliHelper@0 endp + +ifdef MDA_SUPPORTED + +;========================================================================== +; Invoked from on-the-fly generated stubs when the stack imbalance MDA is +; enabled. The common low-level work for both direct P/Invoke and unmanaged +; delegate P/Invoke happens here. PInvokeStackImbalanceWorker is where the +; actual imbalance check is implemented. +; [ESP + 4] - the StackImbalanceCookie +; [EBP + 8] - stack arguments (EBP frame pushed by the calling stub) +; +_PInvokeStackImbalanceHelper@0 proc public + ; StackImbalanceCookie to EBX + push ebx + lea ebx, [esp + 8] + + push esi + push edi + + ; copy stack args + mov edx, ecx + mov ecx, [ebx + StackImbalanceCookie__m_dwStackArgSize] + sub esp, ecx + + shr ecx, 2 + lea edi, [esp] + lea esi, [ebp + 8] + + cld + rep movsd + + ; record pre-call ESP + mov [ebx + StackImbalanceCookie__m_dwSavedEsp], esp + + ; call the target (restore ECX in case it's a thiscall) + mov ecx, edx + call [ebx + StackImbalanceCookie__m_pTarget] + + ; record post-call ESP and restore ESP to pre-pushed state + mov ecx, esp + lea esp, [ebp - SIZEOF_StackImbalanceCookie - 16] ; 4 DWORDs and the cookie have been pushed + + ; save return value + push eax + push edx + sub esp, 12 + +.errnz (StackImbalanceCookie__HAS_FP_RETURN_VALUE AND 00ffffffh), HAS_FP_RETURN_VALUE has changed - update asm code + + ; save top of the floating point stack if the target has FP retval + test byte ptr [ebx + StackImbalanceCookie__m_callConv + 3], (StackImbalanceCookie__HAS_FP_RETURN_VALUE SHR 24) + jz noFPURetVal + fstp tbyte ptr [esp] ; save full 10 bytes to avoid precision loss +noFPURetVal: + + ; call PInvokeStackImbalanceWorker(StackImbalanceCookie *pSICookie, DWORD dwPostESP) + push ecx + push ebx + call _PInvokeStackImbalanceWorker@8 + + ; restore return value + test byte ptr [ebx + StackImbalanceCookie__m_callConv + 3], (StackImbalanceCookie__HAS_FP_RETURN_VALUE SHR 24) + jz noFPURetValToRestore + fld tbyte ptr [esp] +noFPURetValToRestore: + + add esp, 12 + pop edx + pop eax + + ; restore registers + pop edi + pop esi + + pop ebx + + ; EBP frame and original stack arguments will be removed by the caller + ret +_PInvokeStackImbalanceHelper@0 endp + +endif ; MDA_SUPPORTED + +ifdef FEATURE_COMINTEROP + +;========================================================================== +; This is a fast alternative to CallDescr* tailored specifically for +; COM to CLR calls. Stack arguments don't come in a continuous buffer +; and secret argument can be passed in EAX. +; + +; extern "C" ARG_SLOT __fastcall COMToCLRDispatchHelper( +; INT_PTR dwArgECX, ; ecx +; INT_PTR dwArgEDX, ; edx +; PCODE pTarget, ; [esp + 4] +; PCODE pSecretArg, ; [esp + 8] +; INT_PTR *pInputStack, ; [esp + c] +; WORD wOutputStackSlots, ; [esp +10] +; UINT16 *pOutputStackOffsets, ; [esp +14] +; Frame *pCurFrame); ; [esp +18] + +FASTCALL_FUNC COMToCLRDispatchHelper, 32 + + ; ecx: dwArgECX + ; edx: dwArgEDX + + offset_pTarget equ 4 + offset_pSecretArg equ 8 + offset_pInputStack equ 0Ch + offset_wOutputStackSlots equ 10h + offset_pOutputStackOffsets equ 14h + offset_pCurFrame equ 18h + + movzx eax, word ptr [esp + offset_wOutputStackSlots] + test eax, eax + jnz CopyStackArgs + + ; There are no stack args to copy and ECX and EDX are already setup + ; with the correct arguments for the callee, so we just have to + ; push the CPFH and make the call. + + PUSH_CPFH_FOR_COM eax, esp, offset_pCurFrame ; trashes eax + + mov eax, [esp + offset_pSecretArg + CPFH_STACK_SIZE] + call [esp + offset_pTarget + CPFH_STACK_SIZE] +ifdef _DEBUG + nop ; This is a tag that we use in an assert. +endif + + POP_CPFH_FOR_COM ecx ; trashes ecx + + ret 18h + + +CopyStackArgs: + ; eax: num stack slots + ; ecx: dwArgECX + ; edx: dwArgEDX + + push ebp + mov ebp, esp + push ebx + push esi + push edi + + ebpFrame_adjust equ 4h + ebp_offset_pCurFrame equ ebpFrame_adjust + offset_pCurFrame + + PUSH_CPFH_FOR_COM ebx, ebp, ebp_offset_pCurFrame ; trashes ebx + + mov edi, [ebp + ebpFrame_adjust + offset_pOutputStackOffsets] + mov esi, [ebp + ebpFrame_adjust + offset_pInputStack] + + ; eax: num stack slots + ; ecx: dwArgECX + ; edx: dwArgEDX + ; edi: pOutputStackOffsets + ; esi: pInputStack + +CopyStackLoop: + dec eax + movzx ebx, word ptr [edi + 2 * eax] ; ebx <- input stack offset + push [esi + ebx] ; stack <- value on the input stack + jnz CopyStackLoop + + ; ECX and EDX are setup with the correct arguments for the callee, + ; and we've copied the stack arguments over as well, so now it's + ; time to make the call. + + mov eax, [ebp + ebpFrame_adjust + offset_pSecretArg] + call [ebp + ebpFrame_adjust + offset_pTarget] +ifdef _DEBUG + nop ; This is a tag that we use in an assert. +endif + + POP_CPFH_FOR_COM ecx ; trashes ecx + + pop edi + pop esi + pop ebx + pop ebp + + ret 18h + +FASTCALL_ENDFUNC + +endif ; FEATURE_COMINTEROP + +ifndef FEATURE_CORECLR + +;========================================================================== +; This is small stub whose purpose is to record current stack pointer and +; call CopyCtorCallStubWorker to invoke copy constructors and destructors +; as appropriate. This stub operates on arguments already pushed to the +; stack by JITted IL stub and must not create a new frame, i.e. it must tail +; call to the target for it to see the arguments that copy ctors have been +; called on. +; +_CopyCtorCallStub@0 proc public + ; there may be an argument in ecx - save it + push ecx + + ; push pointer to arguments + lea edx, [esp + 8] + push edx + + call _CopyCtorCallStubWorker@4 + + ; restore ecx and tail call to the target + pop ecx + jmp eax +_CopyCtorCallStub@0 endp + +endif ; !FEATURE_CORECLR + +ifdef FEATURE_PREJIT + +;========================================================================== +_StubDispatchFixupStub@0 proc public + + STUB_PROLOG + + mov esi, esp + + push 0 + push 0 + + push eax ; siteAddrForRegisterIndirect (for tailcalls) + push esi ; pTransitionBlock + + call _StubDispatchFixupWorker@16 + + STUB_EPILOG + +_StubDispatchFixupPatchLabel@0: +public _StubDispatchFixupPatchLabel@0 + + ; Tailcall target + jmp eax + + ; This will never be executed. It is just to help out stack-walking logic + ; which disassembles the epilog to unwind the stack. + ret + +_StubDispatchFixupStub@0 endp + +;========================================================================== +_ExternalMethodFixupStub@0 proc public + + pop eax ; pop off the return address to the stub + ; leaving the actual caller's return address on top of the stack + + STUB_PROLOG + + mov esi, esp + + ; EAX is return address into CORCOMPILE_EXTERNAL_METHOD_THUNK. Subtract 5 to get start address. + sub eax, 5 + + push 0 + push 0 + + push eax + + ; pTransitionBlock + push esi + + call _ExternalMethodFixupWorker@16 + + ; eax now contains replacement stub. PreStubWorker will never return + ; NULL (it throws an exception if stub creation fails.) + + ; From here on, mustn't trash eax + + STUB_EPILOG + +_ExternalMethodFixupPatchLabel@0: +public _ExternalMethodFixupPatchLabel@0 + + ; Tailcall target + jmp eax + + ; This will never be executed. It is just to help out stack-walking logic + ; which disassembles the epilog to unwind the stack. + ret + +_ExternalMethodFixupStub@0 endp + +ifdef FEATURE_READYTORUN +;========================================================================== +_DelayLoad_MethodCall@0 proc public + + STUB_PROLOG_2_HIDDEN_ARGS + + mov esi, esp + + push ecx + push edx + + push eax + + ; pTransitionBlock + push esi + + call _ExternalMethodFixupWorker@16 + + ; eax now contains replacement stub. PreStubWorker will never return + ; NULL (it throws an exception if stub creation fails.) + + ; From here on, mustn't trash eax + + STUB_EPILOG + + ; Share the patch label + jmp _ExternalMethodFixupPatchLabel@0 + + ; This will never be executed. It is just to help out stack-walking logic + ; which disassembles the epilog to unwind the stack. + ret + +_DelayLoad_MethodCall@0 endp +endif + +;======================================================================================= +; The call in softbound vtable slots initially points to this function. +; The pupose of this function is to transfer the control to right target and +; to optionally patch the target of the jump so that we do not take this slow path again. +; +_VirtualMethodFixupStub@0 proc public + + pop eax ; Pop the return address. It points right after the call instruction in the thunk. + sub eax,5 ; Calculate the address of the thunk + + ; Push ebp frame to get good callstack under debugger + push ebp + mov ebp, esp + + ; Preserve argument registers + push ecx + push edx + + push eax ; address of the thunk + push ecx ; this ptr + call _VirtualMethodFixupWorker@8 + + ; Restore argument registers + pop edx + pop ecx + + ; Pop ebp frame + pop ebp + +_VirtualMethodFixupPatchLabel@0: +public _VirtualMethodFixupPatchLabel@0 + + ; Proceed to execute the actual method. + jmp eax + + ; This will never be executed. It is just to help out stack-walking logic + ; which disassembles the epilog to unwind the stack. + ret + +_VirtualMethodFixupStub@0 endp + +endif ; FEATURE_PREJIT + +;========================================================================== +; The prestub +_ThePreStub@0 proc public + + STUB_PROLOG + + mov esi, esp + + ; EAX contains MethodDesc* from the precode. Push it here as argument + ; for PreStubWorker + push eax + + push esi + + call _PreStubWorker@8 + + ; eax now contains replacement stub. PreStubWorker will never return + ; NULL (it throws an exception if stub creation fails.) + + ; From here on, mustn't trash eax + + STUB_EPILOG + + ; Tailcall target + jmp eax + + ; This will never be executed. It is just to help out stack-walking logic + ; which disassembles the epilog to unwind the stack. + ret + +_ThePreStub@0 endp + +; This method does nothing. It's just a fixed function for the debugger to put a breakpoint +; on so that it can trace a call target. +_ThePreStubPatch@0 proc public + ; make sure that the basic block is unique + test eax,34 +_ThePreStubPatchLabel@0: +public _ThePreStubPatchLabel@0 + ret +_ThePreStubPatch@0 endp + +ifdef FEATURE_COMINTEROP +;========================================================================== +; CLR -> COM generic or late-bound call +_GenericComPlusCallStub@0 proc public + + STUB_PROLOG + + ; pTransitionBlock + mov esi, esp + + ; return value + sub esp, 8 + + ; save pMD + mov ebx, eax + + push eax ; pMD + push esi ; pTransitionBlock + call _CLRToCOMWorker@8 + + push eax + call _setFPReturn@12 ; pop & set the return value + + ; From here on, mustn't trash eax:edx + + ; Get pComPlusCallInfo for return thunk + mov ecx, [ebx + ComPlusCallMethodDesc__m_pComPlusCallInfo] + + STUB_EPILOG_RETURN + + ; Tailcall return thunk + jmp [ecx + ComPlusCallInfo__m_pRetThunk] + + ; This will never be executed. It is just to help out stack-walking logic + ; which disassembles the epilog to unwind the stack. + ret + +_GenericComPlusCallStub@0 endp +endif ; FEATURE_COMINTEROP + +ifdef FEATURE_REMOTING +_TransparentProxyStub@0 proc public + ; push slot passed in eax + push eax + + ; Move into eax the stub data and call the stub + mov eax, [ecx + TransparentProxyObject___stubData] + call [ecx + TransparentProxyObject___stub] +ifdef _DEBUG + nop ; Mark this as a special call site that can directly + ; call managed code +endif + test eax, eax + jnz CtxMismatch2 + + mov eax, [ecx + TransparentProxyObject___pMT] + + push ebx ; spill EBX + + ; Convert the slot number into the code address + ; See MethodTable.h for details on vtable layout + + mov ebx, [esp + 4] ; Reload the slot + shr ebx, ASM__VTABLE_SLOTS_PER_CHUNK_LOG2 ; indirectionSlotNumber + + mov eax,[eax + ebx*4 + SIZEOF_MethodTable] + + mov ebx, [esp + 4] ; use unchanged slot from above + and ebx, ASM__VTABLE_SLOTS_PER_CHUNK-1 ; offsetInChunk + mov eax, [eax + ebx*4] + + ; At this point, eax contains the code address + + ; Restore EBX + pop ebx + + ; Remove the slot number from the stack + lea esp, [esp+4] + + jmp eax + + ; CONTEXT MISMATCH CASE, call out to the real proxy to dispatch + +CtxMismatch2: + pop eax ; restore MethodDesc * + jmp _TransparentProxyStub_CrossContext@0 ; jump to slow TP stub + +_TransparentProxyStub@0 endp + +_TransparentProxyStub_CrossContext@0 proc public + + STUB_PROLOG + + ; pTransitionBlock + mov esi, esp + + ; return value + sub esp, 3*4 ; 64-bit return value + cb stack pop + + push eax ; pMD + push esi ; pTransitionBlock + call _TransparentProxyStubWorker@8 + + pop ebx ; cbStackPop + + push eax + call _setFPReturn@12 ; pop & set the return value + + ; From here on, mustn't trash eax:edx + mov ecx, ebx ; cbStackPop + + mov ebx, [esp+6*4] ; get retaddr + mov [esp+6*4+ecx], ebx ; put it where it belongs + + STUB_EPILOG_RETURN + + add esp, ecx ; pop all the args + ret + +_TransparentProxyStub_CrossContext@0 endp + +; This method does nothing. It's just a fixed function for the debugger to put a breakpoint +; on so that it can trace a call target. +_TransparentProxyStubPatch@0 proc public + ; make sure that the basic block is unique + test eax,12 +_TransparentProxyStubPatchLabel@0: +public _TransparentProxyStubPatchLabel@0 + ret +_TransparentProxyStubPatch@0 endp + +endif ; FEATURE_REMOTING + +ifdef FEATURE_COMINTEROP +;-------------------------------------------------------------------------- +; This is the code that all com call method stubs run initially. +; Most of the real work occurs in ComStubWorker(), a C++ routine. +; The template only does the part that absolutely has to be in assembly +; language. +;-------------------------------------------------------------------------- +_ComCallPreStub@0 proc public + pop eax ;ComCallMethodDesc* + + ; push ebp-frame + push ebp + mov ebp,esp + + ; save CalleeSavedRegisters + push ebx + push esi + push edi + + push eax ; ComCallMethodDesc* + sub esp, 5*4 ; next, vtable, gscookie, 64-bit error return + + lea edi, [esp] + lea esi, [esp+3*4] + + push edi ; pErrorReturn + push esi ; pFrame + call _ComPreStubWorker@8 + + ; eax now contains replacement stub. ComStubWorker will return NULL if stub creation fails + cmp eax, 0 + je nostub ; oops we could not create a stub + + add esp, 6*4 + + ; pop CalleeSavedRegisters + pop edi + pop esi + pop ebx + pop ebp + + jmp eax ; Reexecute with replacement stub. + ; We will never get here. This "ret" is just so that code-disassembling + ; profilers know to stop disassembling any further + ret + +nostub: + + ; Even though the ComPreStubWorker sets a 64 bit value as the error return code. + ; Only the lower 32 bits contain usefula data. The reason for this is that the + ; possible error return types are: failure HRESULT, 0 and floating point 0. + ; In each case, the data fits in 32 bits. Instead, we use the upper half of + ; the return value to store number of bytes to pop + mov eax, [edi] + mov edx, [edi+4] + + add esp, 6*4 + + ; pop CalleeSavedRegisters + pop edi + pop esi + pop ebx + pop ebp + + pop ecx ; return address + add esp, edx ; pop bytes of the stack + push ecx ; return address + + ; We need to deal with the case where the method is PreserveSig=true and has an 8 + ; byte return type. There are 2 types of 8 byte return types: integer and floating point. + ; For integer 8 byte return types, we always return 0 in case of failure. For floating + ; point return types, we return the value in the floating point register. In both cases + ; edx should be 0. + xor edx, edx ; edx <-- 0 + + ret + +_ComCallPreStub@0 endp +endif ; FEATURE_COMINTEROP + +ifdef FEATURE_READYTORUN +;========================================================================== +; Define helpers for delay loading of readytorun helpers + +DYNAMICHELPER macro frameFlags, suffix + +_DelayLoad_Helper&suffix&@0 proc public + + STUB_PROLOG_2_HIDDEN_ARGS + + mov esi, esp + + push frameFlags + push ecx ; module + push edx ; section index + + push eax ; indirection cell address. + push esi ; pTransitionBlock + + call _DynamicHelperWorker@20 + + STUB_EPILOG_RETURN + + ret + +_DelayLoad_Helper&suffix&@0 endp + + endm + +DYNAMICHELPER DynamicHelperFrameFlags_Default +DYNAMICHELPER DynamicHelperFrameFlags_ObjectArg, _Obj +DYNAMICHELPER , _ObjObj + +endif ; FEATURE_READYTORUN + + end diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/i386/cgencpu.h b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/i386/cgencpu.h new file mode 100644 index 00000000000000..2da040d89f499e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/i386/cgencpu.h @@ -0,0 +1,553 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// CGENX86.H - +// +// Various helper routines for generating x86 assembly code. +// +// DO NOT INCLUDE THIS FILE DIRECTLY - ALWAYS USE CGENSYS.H INSTEAD +// + + + +#ifndef _TARGET_X86_ +#error Should only include "cgenx86.h" for X86 builds +#endif // _TARGET_X86_ + +#ifndef __cgenx86_h__ +#define __cgenx86_h__ + +#include "utilcode.h" + +// Given a return address retrieved during stackwalk, +// this is the offset by which it should be decremented to lend somewhere in a call instruction. +#define STACKWALK_CONTROLPC_ADJUST_OFFSET 1 + +// preferred alignment for data +#define DATA_ALIGNMENT 4 + +class MethodDesc; +class FramedMethodFrame; +class Module; +class ComCallMethodDesc; +class BaseDomain; + +// CPU-dependent functions +Stub * GenerateInitPInvokeFrameHelper(); + +#ifdef MDA_SUPPORTED +EXTERN_C void STDCALL PInvokeStackImbalanceHelper(void); +#endif // MDA_SUPPORTED + +#ifndef FEATURE_CORECLR +EXTERN_C void STDCALL CopyCtorCallStub(void); +#endif // !FEATURE_CORECLR + +BOOL Runtime_Test_For_SSE2(); + +#ifdef CROSSGEN_COMPILE +#define GetEEFuncEntryPoint(pfn) 0x1001 +#else +#define GetEEFuncEntryPoint(pfn) GFN_TADDR(pfn) +#endif + +//********************************************************************** +// To be used with GetSpecificCpuInfo() + +#define CPU_X86_FAMILY(cpuType) (((cpuType) & 0x0F00) >> 8) +#define CPU_X86_MODEL(cpuType) (((cpuType) & 0x00F0) >> 4) +// Stepping is masked out by GetSpecificCpuInfo() +// #define CPU_X86_STEPPING(cpuType) (((cpuType) & 0x000F) ) + +#define CPU_X86_USE_CMOV(cpuFeat) ((cpuFeat & 0x00008001) == 0x00008001) +#define CPU_X86_USE_SSE2(cpuFeat) (((cpuFeat & 0x04000000) == 0x04000000) && Runtime_Test_For_SSE2()) + +// Values for CPU_X86_FAMILY(cpuType) +#define CPU_X86_486 4 +#define CPU_X86_PENTIUM 5 +#define CPU_X86_PENTIUM_PRO 6 +#define CPU_X86_PENTIUM_4 0xF + +// Values for CPU_X86_MODEL(cpuType) for CPU_X86_PENTIUM_PRO +#define CPU_X86_MODEL_PENTIUM_PRO_BANIAS 9 // Pentium M (Mobile PPro with P4 feautres) + +#define COMMETHOD_PREPAD 8 // # extra bytes to allocate in addition to sizeof(ComCallMethodDesc) +#ifdef FEATURE_COMINTEROP +#define COMMETHOD_CALL_PRESTUB_SIZE 5 // x86: CALL(E8) xx xx xx xx +#define COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET 1 // the offset of the call target address inside the prestub +#endif // FEATURE_COMINTEROP + +#define STACK_ALIGN_SIZE 4 + +#define JUMP_ALLOCATE_SIZE 8 // # bytes to allocate for a jump instruction +#define BACK_TO_BACK_JUMP_ALLOCATE_SIZE 8 // # bytes to allocate for a back to back jump instruction + +#define HAS_COMPACT_ENTRYPOINTS 1 + +// Needed for PInvoke inlining in ngened images +#define HAS_NDIRECT_IMPORT_PRECODE 1 + +#ifdef FEATURE_REMOTING +#define HAS_REMOTING_PRECODE 1 +#endif +#ifdef FEATURE_PREJIT +#define HAS_FIXUP_PRECODE 1 +#define HAS_FIXUP_PRECODE_CHUNKS 1 +#endif + +// ThisPtrRetBufPrecode one is necessary for closed delegates over static methods with return buffer +#define HAS_THISPTR_RETBUF_PRECODE 1 + +#define CODE_SIZE_ALIGN 4 +#define CACHE_LINE_SIZE 32 // As per Intel Optimization Manual the cache line size is 32 bytes +#define LOG2SLOT LOG2_PTRSIZE + +#define ENREGISTERED_RETURNTYPE_MAXSIZE 8 +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 4 +#define CALLDESCR_ARGREGS 1 // CallDescrWorker has ArgumentRegister parameter + +// Max size of patched TLS helpers +#ifdef _DEBUG +// Debug build needs extra space for last error trashing +#define TLS_GETTER_MAX_SIZE 0x20 +#else +#define TLS_GETTER_MAX_SIZE 0x10 +#endif + +//======================================================================= +// IMPORTANT: This value is used to figure out how much to allocate +// for a fixed array of FieldMarshaler's. That means it must be at least +// as large as the largest FieldMarshaler subclass. This requirement +// is guarded by an assert. +//======================================================================= +#define MAXFIELDMARSHALERSIZE 24 + +//********************************************************************** +// Parameter size +//********************************************************************** + +typedef INT32 StackElemType; +#define STACK_ELEM_SIZE sizeof(StackElemType) + + + +#include "stublinkerx86.h" + + + +// !! This expression assumes STACK_ELEM_SIZE is a power of 2. +#define StackElemSize(parmSize) (((parmSize) + STACK_ELEM_SIZE - 1) & ~((ULONG)(STACK_ELEM_SIZE - 1))) + + +//********************************************************************** +// Frames +//********************************************************************** +//-------------------------------------------------------------------- +// This represents some of the FramedMethodFrame fields that are +// stored at negative offsets. +//-------------------------------------------------------------------- +typedef DPTR(struct CalleeSavedRegisters) PTR_CalleeSavedRegisters; +struct CalleeSavedRegisters { + INT32 edi; + INT32 esi; + INT32 ebx; + INT32 ebp; +}; + +//-------------------------------------------------------------------- +// This represents the arguments that are stored in volatile registers. +// This should not overlap the CalleeSavedRegisters since those are already +// saved separately and it would be wasteful to save the same register twice. +// If we do use a non-volatile register as an argument, then the ArgIterator +// will probably have to communicate this back to the PromoteCallerStack +// routine to avoid a double promotion. +//-------------------------------------------------------------------- +#define ENUM_ARGUMENT_REGISTERS() \ + ARGUMENT_REGISTER(ECX) \ + ARGUMENT_REGISTER(EDX) + +#define ENUM_ARGUMENT_REGISTERS_BACKWARD() \ + ARGUMENT_REGISTER(EDX) \ + ARGUMENT_REGISTER(ECX) + +typedef DPTR(struct ArgumentRegisters) PTR_ArgumentRegisters; +struct ArgumentRegisters { + #define ARGUMENT_REGISTER(regname) INT32 regname; + ENUM_ARGUMENT_REGISTERS_BACKWARD() + #undef ARGUMENT_REGISTER +}; +#define NUM_ARGUMENT_REGISTERS 2 + +#define SCRATCH_REGISTER_X86REG kEAX + +#define THIS_REG ECX +#define THIS_kREG kECX + +#define ARGUMENT_REG1 ECX +#define ARGUMENT_REG2 EDX + +// forward decl +struct REGDISPLAY; +typedef REGDISPLAY *PREGDISPLAY; + +// Sufficient context for Try/Catch restoration. +struct EHContext { + INT32 Eax; + INT32 Ebx; + INT32 Ecx; + INT32 Edx; + INT32 Esi; + INT32 Edi; + INT32 Ebp; + INT32 Esp; + INT32 Eip; + + void Setup(PCODE resumePC, PREGDISPLAY regs); + void UpdateFrame(PREGDISPLAY regs); + + inline TADDR GetSP() { + LIMITED_METHOD_CONTRACT; + return (TADDR)Esp; + } + inline void SetSP(LPVOID esp) { + LIMITED_METHOD_CONTRACT; + Esp = (INT32)(size_t)esp; + } + + inline LPVOID GetFP() { + LIMITED_METHOD_CONTRACT; + return (LPVOID)(UINT_PTR)Ebp; + } + + inline void SetArg(LPVOID arg) { + LIMITED_METHOD_CONTRACT; + Eax = (INT32)(size_t)arg; + } + + inline void Init() + { + Eax = 0; + Ebx = 0; + Ecx = 0; + Edx = 0; + Esi = 0; + Edi = 0; + Ebp = 0; + Esp = 0; + Eip = 0; + } +}; + +#define ARGUMENTREGISTERS_SIZE sizeof(ArgumentRegisters) + +//********************************************************************** +// Exception handling +//********************************************************************** + +inline PCODE GetIP(const CONTEXT * context) { + LIMITED_METHOD_DAC_CONTRACT; + + return PCODE(context->Eip); +} + +inline void SetIP(CONTEXT *context, PCODE eip) { + LIMITED_METHOD_DAC_CONTRACT; + + context->Eip = (DWORD)eip; +} + +inline TADDR GetSP(const CONTEXT * context) { + LIMITED_METHOD_DAC_CONTRACT; + + return (TADDR)(context->Esp); +} + +EXTERN_C LPVOID STDCALL GetCurrentSP(); + +inline void SetSP(CONTEXT *context, TADDR esp) { + LIMITED_METHOD_DAC_CONTRACT; + + context->Esp = (DWORD)esp; +} + +inline void SetFP(CONTEXT *context, TADDR ebp) { + LIMITED_METHOD_DAC_CONTRACT; + + context->Ebp = (INT32)ebp; +} + +inline TADDR GetFP(const CONTEXT * context) +{ + LIMITED_METHOD_DAC_CONTRACT; + + return (TADDR)context->Ebp; +} + +// Get Rel32 destination, emit jumpStub if necessary +inline INT32 rel32UsingJumpStub(INT32 UNALIGNED * pRel32, PCODE target, MethodDesc *pMethod = NULL, LoaderAllocator *pLoaderAllocator = NULL) +{ + // We do not need jump stubs on i386 + LIMITED_METHOD_CONTRACT; + + TADDR baseAddr = (TADDR)pRel32 + 4; + return (INT32)(target - baseAddr); +} + +#ifndef CLR_STANDALONE_BINDER + +#ifdef FEATURE_COMINTEROP +inline void emitCOMStubCall (ComCallMethodDesc *pCOMMethod, PCODE target) +{ + WRAPPER_NO_CONTRACT; + + BYTE *pBuffer = (BYTE*)pCOMMethod - COMMETHOD_CALL_PRESTUB_SIZE; + + pBuffer[0] = X86_INSTR_CALL_REL32; //CALLNEAR32 + *((LPVOID*)(1+pBuffer)) = (LPVOID) (((LPBYTE)target) - (pBuffer+5)); + + _ASSERTE(IS_ALIGNED(pBuffer + COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET, sizeof(void*)) && + *((SSIZE_T*)(pBuffer + COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET)) == ((LPBYTE)target - (LPBYTE)pCOMMethod)); +} +#endif // FEATURE_COMINTEROP + +//------------------------------------------------------------------------ +WORD GetUnpatchedCodeData(LPCBYTE pAddr); + +//------------------------------------------------------------------------ +inline WORD GetUnpatchedOpcodeWORD(LPCBYTE pAddr) +{ + WRAPPER_NO_CONTRACT; + if (CORDebuggerAttached()) + { + return GetUnpatchedCodeData(pAddr); + } + else + { + return *((WORD *)pAddr); + } +} + +//------------------------------------------------------------------------ +inline BYTE GetUnpatchedOpcodeBYTE(LPCBYTE pAddr) +{ + WRAPPER_NO_CONTRACT; + if (CORDebuggerAttached()) + { + return (BYTE) GetUnpatchedCodeData(pAddr); + } + else + { + return *pAddr; + } +} + + //------------------------------------------------------------------------ +// The following must be a distinguishable set of instruction sequences for +// various stub dispatch calls. +// +// An x86 JIT which uses full stub dispatch must generate only +// the following stub dispatch calls: +// +// (1) isCallRelativeIndirect: +// call dword ptr [rel32] ; FF 15 ---rel32---- +// (2) isCallRelative: +// call abc ; E8 ---rel32---- +// (3) isCallRegisterIndirect: +// 3-byte nop ; +// call dword ptr [eax] ; FF 10 +// +// NOTE: You must be sure that pRetAddr is a true return address for +// a stub dispatch call. + +BOOL isCallRelativeIndirect(const BYTE *pRetAddr); +BOOL isCallRelative(const BYTE *pRetAddr); +BOOL isCallRegisterIndirect(const BYTE *pRetAddr); + +inline BOOL isCallRelativeIndirect(const BYTE *pRetAddr) +{ + LIMITED_METHOD_CONTRACT; + + BOOL fRet = (GetUnpatchedOpcodeWORD(&pRetAddr[-6]) == X86_INSTR_CALL_IND); + _ASSERTE(!fRet || !isCallRelative(pRetAddr)); + _ASSERTE(!fRet || !isCallRegisterIndirect(pRetAddr)); + return fRet; +} + +inline BOOL isCallRelative(const BYTE *pRetAddr) +{ + LIMITED_METHOD_CONTRACT; + + BOOL fRet = (GetUnpatchedOpcodeBYTE(&pRetAddr[-5]) == X86_INSTR_CALL_REL32); + _ASSERTE(!fRet || !isCallRelativeIndirect(pRetAddr)); + _ASSERTE(!fRet || !isCallRegisterIndirect(pRetAddr)); + return fRet; +} + +inline BOOL isCallRegisterIndirect(const BYTE *pRetAddr) +{ + LIMITED_METHOD_CONTRACT; + + BOOL fRet = (GetUnpatchedOpcodeWORD(&pRetAddr[-5]) == X86_INSTR_NOP3_1) + && (GetUnpatchedOpcodeBYTE(&pRetAddr[-3]) == X86_INSTR_NOP3_3) + && (GetUnpatchedOpcodeWORD(&pRetAddr[-2]) == X86_INSTR_CALL_IND_EAX); + _ASSERTE(!fRet || !isCallRelative(pRetAddr)); + _ASSERTE(!fRet || !isCallRelativeIndirect(pRetAddr)); + return fRet; +} + +//------------------------------------------------------------------------ +inline void emitJump(LPBYTE pBuffer, LPVOID target) +{ + LIMITED_METHOD_CONTRACT; + + pBuffer[0] = X86_INSTR_JMP_REL32; //JUMPNEAR32 + *((LPVOID*)(1+pBuffer)) = (LPVOID) (((LPBYTE)target) - (pBuffer+5)); +} + +//------------------------------------------------------------------------ +inline void emitJumpInd(LPBYTE pBuffer, LPVOID target) +{ + LIMITED_METHOD_CONTRACT; + + *((WORD*)pBuffer) = X86_INSTR_JMP_IND; // 0x25FF jmp dword ptr[addr32] + *((LPVOID*)(2+pBuffer)) = target; +} + +//------------------------------------------------------------------------ +inline PCODE isJump(PCODE pCode) +{ + LIMITED_METHOD_DAC_CONTRACT; + return *PTR_BYTE(pCode) == X86_INSTR_JMP_REL32; +} + +//------------------------------------------------------------------------ +// Given the same pBuffer that was used by emitJump this method +// decodes the instructions and returns the jump target +inline PCODE decodeJump(PCODE pCode) +{ + LIMITED_METHOD_DAC_CONTRACT; + CONSISTENCY_CHECK(*PTR_BYTE(pCode) == X86_INSTR_JMP_REL32); + return rel32Decode(pCode+1); +} + +// +// On IA64 back to back jumps should be separated by a nop bundle to get +// the best performance from the hardware's branch prediction logic. +// For all other platforms back to back jumps don't require anything special +// That is why we have these two wrapper functions that call emitJump and decodeJump +// + +//------------------------------------------------------------------------ +inline void emitBackToBackJump(LPBYTE pBuffer, LPVOID target) +{ + WRAPPER_NO_CONTRACT; + emitJump(pBuffer, target); +} + +//------------------------------------------------------------------------ +inline PCODE isBackToBackJump(PCODE pBuffer) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return isJump(pBuffer); +} + +//------------------------------------------------------------------------ +inline PCODE decodeBackToBackJump(PCODE pBuffer) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return decodeJump(pBuffer); +} + +EXTERN_C void __stdcall setFPReturn(int fpSize, INT64 retVal); +EXTERN_C void __stdcall getFPReturn(int fpSize, INT64 *pretval); + + +// SEH info forward declarations + +inline BOOL IsUnmanagedValueTypeReturnedByRef(UINT sizeofvaluetype) +{ + LIMITED_METHOD_CONTRACT; + + // odd-sized small structures are not + // enregistered e.g. struct { char a,b,c; } + return (sizeofvaluetype > 8) || + (sizeofvaluetype & (sizeofvaluetype - 1)); // check that the size is power of two +} + +#include +DECLSPEC_ALIGN(4) struct UMEntryThunkCode +{ + BYTE m_alignpad[2]; // used to guarantee alignment of backpactched portion + BYTE m_movEAX; //MOV EAX,imm32 + LPVOID m_uet; // pointer to start of this structure + BYTE m_jmp; //JMP NEAR32 + const BYTE * m_execstub; // pointer to destination code // make sure the backpatched portion is dword aligned. + + void Encode(BYTE* pTargetCode, void* pvSecretParam); + + LPCBYTE GetEntryPoint() const + { + LIMITED_METHOD_CONTRACT; + + return (LPCBYTE)&m_movEAX; + } + + static int GetEntryPointOffset() + { + LIMITED_METHOD_CONTRACT; + + return 2; + } +}; +#include +#endif //!CLR_STANDALONE_BINDER + +// ClrFlushInstructionCache is used when we want to call FlushInstructionCache +// for a specific architecture in the common code, but not for other architectures. +// On IA64 ClrFlushInstructionCache calls the Kernel FlushInstructionCache function +// to flush the instruction cache. +// We call ClrFlushInstructionCache whenever we create or modify code in the heap. +// Currently ClrFlushInstructionCache has no effect on X86 +// + +inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode) +{ + // FlushInstructionCache(GetCurrentProcess(), pCodeAddr, sizeOfCode); + MemoryBarrier(); + return TRUE; +} + +#ifndef FEATURE_IMPLICIT_TLS +// +// JIT HELPER ALIASING FOR PORTABILITY. +// +// Create alias for optimized implementations of helpers provided on this platform +// + +#define JIT_MonEnter JIT_MonEnterWorker +#define JIT_MonEnterWorker JIT_MonEnterWorker +#define JIT_MonReliableEnter JIT_MonReliableEnter +#define JIT_MonTryEnter JIT_MonTryEnter +#define JIT_MonExit JIT_MonExitWorker +#define JIT_MonExitWorker JIT_MonExitWorker +#define JIT_MonEnterStatic JIT_MonEnterStatic +#define JIT_MonExitStatic JIT_MonExitStatic + +#endif + +// optimized static helpers generated dynamically at runtime +// #define JIT_GetSharedGCStaticBase +// #define JIT_GetSharedNonGCStaticBase +// #define JIT_GetSharedGCStaticBaseNoCtor +// #define JIT_GetSharedNonGCStaticBaseNoCtor + +#define JIT_ChkCastClass JIT_ChkCastClass +#define JIT_ChkCastClassSpecial JIT_ChkCastClassSpecial +#define JIT_IsInstanceOfClass JIT_IsInstanceOfClass +#define JIT_ChkCastInterface JIT_ChkCastInterface +#define JIT_IsInstanceOfInterface JIT_IsInstanceOfInterface +#define JIT_NewCrossContext JIT_NewCrossContext +#define JIT_Stelem_Ref JIT_Stelem_Ref + +#endif // __cgenx86_h__ diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/readme.txt b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/readme.txt new file mode 100644 index 00000000000000..8636dee6ded23b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/readme.txt @@ -0,0 +1 @@ +These files are used as the porting source for callingconventions.cs. They are merged from their CLR implementation so that we can have an exact copy of what was ported into callingconventions.cs. \ No newline at end of file diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/SerializedDebugData.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/SerializedDebugData.cs new file mode 100644 index 00000000000000..a9543aa562b89f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/SerializedDebugData.cs @@ -0,0 +1,523 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Reflection.Runtime.General; + +using Internal.TypeSystem; +using Internal.Runtime.Augments; +using Internal.TypeSystem.NativeFormat; +using Internal.NativeFormat; +using System.Runtime.CompilerServices; + +namespace Internal.Runtime.TypeLoader +{ + internal class SerializedDebugData + { + // + // SerializedDebugData represents the logical buffer where all debug information is serialized. + // + // DBGVISIBLE_serializedDataHeader is the blob of memory that describes the contents of + // the logical buffer. This blob is allocated on the unmanaged heap using MemoryHelpers.AllocateMemory + // + // byte[0-3] in DBGVISIBLE_serializedDataHeader represents the version of serialization format. + // In our current format, we use multiple physical memory buffers (on unmanaged heap) to hold + // the actual serialized data and DBGVISIBLE_serializedDataHeader contains a list of pointers + // to these physical buffers. + // byte[4-7] in DBGVISIBLE_serializedDataHeader is the number of currently allocated physical + // buffers. + // byte[8-...] is a list of pointers to these physical buffers. + // + // Note that type-loader must ensure that all debug data serialization is done in a thread-safe manner. + // + + /// + /// Types of records in the serialized debug data. To maintain compatibility with previous + /// version of the diagnostic stream which used just the two bottom bits for entry type + /// information, we cannibalize entry #3, StepThroughStubAddress, which has only one bit flag + /// (IsTailCallStub shared with StepThroughStubSize), to encode additional entry types + /// in the higher-order bits. When the bits 0-1 contain 1-1 (i.e. the 'old-style' entry type + /// is StepThroughStubAddress) and bits 3-7 are non-zero, they get split such that + /// bits 3-4 are shifted right 3 times and increased by 3 to form the final blob kind, + /// bits 5-7 are shifted right 5 times to form the flags for the new blob kinds. + /// This creates space for 3 more entry types with 3 bits for flags which should hopefully suffice. + /// + internal enum SerializedDataBlobKind : byte + { + SharedType = 0, + SharedMethod = 1, + StepThroughStubSize = 2, + StepThroughStubAddress = 3, + NativeFormatType = 4, + + Limit, + }; + + [Flags] + internal enum SharedTypeFlags : byte + { + HasGCStaticFieldRegion = 0x01, + HasNonGCStaticFieldRegion = 0x02, + HasThreadStaticFieldRegion = 0x04, + HasStaticFields = 0x08, + HasInstanceFields = 0x10, + HasTypeSize = 0x20 + }; + + [Flags] + internal enum SharedMethodFlags : byte + { + HasDeclaringTypeHandle = 0x01 + }; + + [Flags] + internal enum StepThroughStubFlags : byte + { + IsTailCallStub = 0x01 + }; + + private static IntPtr DBGVISIBLE_serializedDataHeader; + + // version of the serialization format + private const int SerializationFormatVersion = 2; + + // size by which the list of pointers to physical buffers is grown + private const int HeaderBufferListSize = 100; + + // offset in DBGVISIBLE_serializedDataHeader where the list of pointers to physical buffers starts + private const int HeaderBufferListOffset = sizeof(int) * 2; + + // size of each physical buffer + private const int PhysicalBufferSize = 10 * 1024; // 10 KB + + // offset in physical buffer where the actual data blobs start + private const int PhysicalBufferDataOffset = sizeof(int); + + // the instance of SerializedDebugData via which runtime updates the debug data buffer + internal static SerializedDebugData Instance = new SerializedDebugData(); + + private IntPtr _activePhysicalBuffer; + private int _activePhysicalBufferIdx = -1; + private int _activePhysicalBufferOffset; + private int _activePhysicalBufferAvailableSize; + private int _serializedDataHeaderSize; + + private unsafe void InitializeHeader(int physicalBufferListSize) + { + int headerSize = HeaderBufferListOffset + IntPtr.Size * physicalBufferListSize; + IntPtr header = MemoryHelpers.AllocateMemory(headerSize); + IntPtr oldHeader = IntPtr.Zero; + MemoryHelpers.Memset(header, headerSize, 0); + + // check if an older header exists and copy all data from it to the newly + // allocated header. + if (_serializedDataHeaderSize > 0) + { + Debug.Assert(headerSize > _serializedDataHeaderSize); + + Buffer.MemoryCopy((byte*)DBGVISIBLE_serializedDataHeader, (byte*)header, headerSize, _serializedDataHeaderSize); + + // mark the older header for deletion + oldHeader = DBGVISIBLE_serializedDataHeader; + } + else + { + // write the serialization format version + *(int*)header = SerializationFormatVersion; + // write the total allocated number of physical buffers (0) + *(int*)(header + sizeof(int)) = 0; + Debug.Assert(_activePhysicalBufferIdx == 0); + } + + DBGVISIBLE_serializedDataHeader = header; + _serializedDataHeaderSize = headerSize; + + // delete the older header if a new one was allocated + if (oldHeader != IntPtr.Zero) + { + MemoryHelpers.FreeMemory(oldHeader); + } + } + private unsafe int GetAllocatedPhysicalBufferCount() + { + if (_serializedDataHeaderSize < HeaderBufferListOffset) + return 0; + + return *(int*)(DBGVISIBLE_serializedDataHeader + sizeof(int)); + } + private unsafe void AddAllocatedBufferToHeader(IntPtr buffer, int insertIdx) + { + Debug.Assert(insertIdx >= 0); + + int currentPhysicalBufferListSize = _serializedDataHeaderSize == 0 ? 0 : + (_serializedDataHeaderSize - HeaderBufferListOffset) / IntPtr.Size; + if (currentPhysicalBufferListSize <= insertIdx) + { + // not enough space in the header, grow it + InitializeHeader(currentPhysicalBufferListSize + HeaderBufferListSize); + } + + Debug.Assert(GetAllocatedPhysicalBufferCount() == insertIdx); + + *(void**)(DBGVISIBLE_serializedDataHeader + HeaderBufferListOffset + IntPtr.Size * insertIdx) = buffer.ToPointer(); + *(int*)(DBGVISIBLE_serializedDataHeader + sizeof(int)) = insertIdx + 1; // update the buffer count + } + + // + // Allocates a new physical buffer and returns the first offset where data can be written into buffer + // First few bytes of the physical buffer are used to describe it. + // + // buffer[0-3] = Used buffer size + // + private unsafe int AllocatePhysicalBuffer(out IntPtr buffer) + { + // Allocate a new physical buffer. + IntPtr newPhysicalBuffer = MemoryHelpers.AllocateMemory(PhysicalBufferSize); + *(int*)newPhysicalBuffer = 0; // write the used buffer size, currently 0 + + // Add the pointer to new physical buffer to DBGVISIBLE_serializedDataHeader + AddAllocatedBufferToHeader(newPhysicalBuffer, ++_activePhysicalBufferIdx); + + buffer = newPhysicalBuffer; + return PhysicalBufferDataOffset; + } + + // + // GetPhysicalBuffer returns a physical buffer of a given size. + // + // Given a requested buffer size, this method gives back a buffer pointer + // and available usable size. + // It is the caller's responsibility to update the used buffer size after writing + // to the buffer. + // + private int GetPhysicalBuffer(int requestedSize, out IntPtr bufferPtr) + { + if (_activePhysicalBufferAvailableSize == 0) + { + // no space available in active physical buffer + // allocate a new physical buffer + _activePhysicalBufferOffset = AllocatePhysicalBuffer(out _activePhysicalBuffer); + _activePhysicalBufferAvailableSize = PhysicalBufferSize - _activePhysicalBufferOffset; + } + + int availableSize = (_activePhysicalBufferAvailableSize < requestedSize) ? + _activePhysicalBufferAvailableSize : requestedSize; + + _activePhysicalBufferAvailableSize -= availableSize; + bufferPtr = new IntPtr(_activePhysicalBuffer.ToInt64() + _activePhysicalBufferOffset); + _activePhysicalBufferOffset += availableSize; + return availableSize; + } + + // Helper used to update the used buffer size in buffer[0-3] + private unsafe void UpdatePhysicalBufferUsedSize() + { + Debug.Assert(_activePhysicalBufferOffset >= PhysicalBufferDataOffset); + *(int*)_activePhysicalBuffer = _activePhysicalBufferOffset; + } + + // Write the given byte array to the logical buffer in a thread-safe manner + private unsafe void ThreadSafeWriteBytes(byte[] src) + { + lock (Instance) + { + IntPtr dst; + int requiredSize = src.Length; + int availableSize = GetPhysicalBuffer(requiredSize, out dst); + if (availableSize < requiredSize) + { + // if current physical buffer doesn't have enough space, try + // and allocate a new one + availableSize = GetPhysicalBuffer(requiredSize, out dst); + if (availableSize < requiredSize) + throw new OutOfMemoryException(); + } + src.AsSpan().CopyTo(new Span((void*)dst, src.Length)); + UpdatePhysicalBufferUsedSize(); // make sure that used physical buffer size is updated + } + } + + // Helper method to serialize the data-blob type and flags + public void SerializeDataBlobTypeAndFlags(ref NativePrimitiveEncoder encoder, SerializedDataBlobKind blobType, byte flags) + { + // make sure that blobType fits in 2 bits and flags fits in 6 bits + Debug.Assert(blobType < SerializedDataBlobKind.Limit); + Debug.Assert((byte)blobType <= 2 && flags <= 0x3F || + (byte)blobType == 3 && flags <= 1 || + (byte)blobType > 3 && flags <= 7); + byte encodedKindAndFlags; + if (blobType <= (SerializedDataBlobKind)3) + { + encodedKindAndFlags = (byte)((byte)blobType | (flags << 2)); + } + else + { + encodedKindAndFlags = (byte)(3 | (((byte)blobType - 3) << 3) | (flags << 5)); + } + encoder.WriteByte(encodedKindAndFlags); + } + + public static void RegisterDebugDataForType(TypeBuilder typeBuilder, DefType defType, TypeBuilderState state) + { + if (!defType.IsGeneric()) + { + RegisterDebugDataForNativeFormatType(typeBuilder, defType, state); + return; + } + + if (defType.IsGenericDefinition) + { + // We don't yet have an encoding for open generic types + // TODO! fill this in + return; + } + + NativePrimitiveEncoder encoder = new NativePrimitiveEncoder(); + encoder.Init(); + + IntPtr gcStaticFieldData = TypeLoaderEnvironment.Instance.TryGetGcStaticFieldData(typeBuilder.GetRuntimeTypeHandle(defType)); + IntPtr nonGcStaticFieldData = TypeLoaderEnvironment.Instance.TryGetNonGcStaticFieldData(typeBuilder.GetRuntimeTypeHandle(defType)); + + bool isUniversalGenericType = state.TemplateType != null && state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal); + bool embeddedTypeSizeAndFieldOffsets = isUniversalGenericType || (state.TemplateType == null); + uint instanceFieldCount = 0; + uint staticFieldCount = 0; + + // GetDiagnosticFields only returns the fields that are of interest for diagnostic reporting. So it doesn't + // return a meaningful list for non-universal canonical templates + IEnumerable diagnosticFields = defType.GetDiagnosticFields(); + foreach (var f in diagnosticFields) + { + if (f.IsLiteral) + continue; + + if (f.IsStatic) + { + ++staticFieldCount; + } + else + { + ++instanceFieldCount; + } + } + + SharedTypeFlags sharedTypeFlags = 0; + if (gcStaticFieldData != IntPtr.Zero) sharedTypeFlags |= SharedTypeFlags.HasGCStaticFieldRegion; + if (nonGcStaticFieldData != IntPtr.Zero) sharedTypeFlags |= SharedTypeFlags.HasNonGCStaticFieldRegion; + if (state.ThreadDataSize != 0) sharedTypeFlags |= SharedTypeFlags.HasThreadStaticFieldRegion; + if (embeddedTypeSizeAndFieldOffsets) + { + sharedTypeFlags |= SerializedDebugData.SharedTypeFlags.HasTypeSize; + + if (instanceFieldCount > 0) + sharedTypeFlags |= SerializedDebugData.SharedTypeFlags.HasInstanceFields; + + if (staticFieldCount > 0) + sharedTypeFlags |= SerializedDebugData.SharedTypeFlags.HasStaticFields; + } + + Instance.SerializeDataBlobTypeAndFlags(ref encoder, SerializedDataBlobKind.SharedType, (byte)sharedTypeFlags); + + // + // The order of these writes is a contract shared between the runtime and debugger engine. + // Changes here must also be updated in the debugger reader code + // + encoder.WriteUnsignedLong((ulong)typeBuilder.GetRuntimeTypeHandle(defType).ToIntPtr().ToInt64()); + encoder.WriteUnsigned((uint)defType.Instantiation.Length); + + foreach (var instParam in defType.Instantiation) + { + encoder.WriteUnsignedLong((ulong)typeBuilder.GetRuntimeTypeHandle(instParam).ToIntPtr().ToInt64()); + } + + if (gcStaticFieldData != IntPtr.Zero) + { + encoder.WriteUnsignedLong((ulong)gcStaticFieldData.ToInt64()); + } + + if (nonGcStaticFieldData != IntPtr.Zero) + { + encoder.WriteUnsignedLong((ulong)nonGcStaticFieldData.ToInt64()); + } + + // Write the TLS offset into the native thread's TLS buffer. That index de-referenced is the thread static + // data region for this type + if (state.ThreadDataSize != 0) + { + encoder.WriteUnsigned(state.ThreadStaticOffset); + } + + // Collect information debugger only requires for universal generics and dynamically loaded types + if (embeddedTypeSizeAndFieldOffsets) + { + Debug.Assert(state.TypeSize != null); + encoder.WriteUnsigned((uint)state.TypeSize); + + if (instanceFieldCount > 0) + { + encoder.WriteUnsigned(instanceFieldCount); + + uint i = 0; + foreach (FieldDesc f in diagnosticFields) + { + if (f.IsLiteral) + continue; + if (f.IsStatic) + continue; + + encoder.WriteUnsigned(i); + encoder.WriteUnsigned((uint)f.Offset.AsInt); + i++; + } + } + + if (staticFieldCount > 0) + { + encoder.WriteUnsigned(staticFieldCount); + + uint i = 0; + foreach (FieldDesc f in diagnosticFields) + { + if (f.IsLiteral) + continue; + if (!f.IsStatic) + continue; + + NativeLayoutFieldDesc nlfd = f as NativeLayoutFieldDesc; + FieldStorage fieldStorage; + if (nlfd != null) + { + // NativeLayoutFieldDesc's have the field storage information directly embedded in them + fieldStorage = nlfd.FieldStorage; + } + else + { + // Metadata based types do not, but the api's to get the info are available + if (f.IsThreadStatic) + { + fieldStorage = FieldStorage.TLSStatic; + } + else if (f.HasGCStaticBase) + { + fieldStorage = FieldStorage.GCStatic; + } + else + { + fieldStorage = FieldStorage.NonGCStatic; + } + } + + encoder.WriteUnsigned(i); + encoder.WriteUnsigned((uint)fieldStorage); + encoder.WriteUnsigned((uint)f.Offset.AsInt); + i++; + } + } + } + + Instance.ThreadSafeWriteBytes(encoder.GetBytes()); + } + + /// + /// Add information about dynamically created non-generic native format type + /// to the diagnostic stream in form of a NativeFormatType blob. + /// + /// TypeBuilder is used to query runtime type handle for the type + /// Type to emit to the diagnostic stream + /// + public static void RegisterDebugDataForNativeFormatType(TypeBuilder typeBuilder, DefType defType, TypeBuilderState state) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + NativeFormatType nativeFormatType = defType as NativeFormatType; + if (nativeFormatType == null) + { + return; + } + + NativePrimitiveEncoder encoder = new NativePrimitiveEncoder(); + encoder.Init(); + + byte nativeFormatTypeFlags = 0; + + Instance.SerializeDataBlobTypeAndFlags( + ref encoder, + SerializedDataBlobKind.NativeFormatType, + nativeFormatTypeFlags); + + TypeManagerHandle moduleHandle = ModuleList.Instance.GetModuleForMetadataReader(nativeFormatType.MetadataReader); + + encoder.WriteUnsignedLong(unchecked((ulong)typeBuilder.GetRuntimeTypeHandle(defType).ToIntPtr().ToInt64())); + encoder.WriteUnsigned(nativeFormatType.Handle.ToHandle(nativeFormatType.MetadataReader).AsUInt()); + encoder.WriteUnsignedLong(unchecked((ulong)moduleHandle.GetIntPtrUNSAFE().ToInt64())); + + Instance.ThreadSafeWriteBytes(encoder.GetBytes()); +#else + return; +#endif + } + + public static void RegisterDebugDataForMethod(TypeBuilder typeBuilder, InstantiatedMethod method) + { + NativePrimitiveEncoder encoder = new NativePrimitiveEncoder(); + encoder.Init(); + + byte sharedMethodFlags = 0; + sharedMethodFlags |= (byte)(method.OwningType.IsGeneric() ? SharedMethodFlags.HasDeclaringTypeHandle : 0); + + Instance.SerializeDataBlobTypeAndFlags(ref encoder, SerializedDataBlobKind.SharedMethod, sharedMethodFlags); + encoder.WriteUnsignedLong((ulong)method.RuntimeMethodDictionary.ToInt64()); + encoder.WriteUnsigned((uint)method.Instantiation.Length); + + foreach (var instParam in method.Instantiation) + { + encoder.WriteUnsignedLong((ulong)typeBuilder.GetRuntimeTypeHandle(instParam).ToIntPtr().ToInt64()); + } + + if (method.OwningType.IsGeneric()) + { + encoder.WriteUnsignedLong((ulong)typeBuilder.GetRuntimeTypeHandle(method.OwningType).ToIntPtr().ToInt64()); + } + + Instance.ThreadSafeWriteBytes(encoder.GetBytes()); + } + + // This method is called whenever a new thunk is allocated, to capture the thunk's code address + // in the serialized stream. + // This information is used by the debugger to detect thunk frames on the callstack. + private static bool s_tailCallThunkSizeRegistered; + public static void RegisterTailCallThunk(IntPtr thunk) + { + NativePrimitiveEncoder encoder = new NativePrimitiveEncoder(); + + if (!s_tailCallThunkSizeRegistered) + { + lock (Instance) + { + if (!s_tailCallThunkSizeRegistered) + { + // Write out the size of thunks used by the calling convention converter + // Make sure that this is called only once + encoder.Init(); + Instance.SerializeDataBlobTypeAndFlags(ref encoder, + SerializedDataBlobKind.StepThroughStubSize, + (byte)StepThroughStubFlags.IsTailCallStub); + encoder.WriteUnsigned((uint)RuntimeAugments.GetThunkSize()); + Instance.ThreadSafeWriteBytes(encoder.GetBytes()); + s_tailCallThunkSizeRegistered = true; + } + } + } + + encoder.Init(); + Instance.SerializeDataBlobTypeAndFlags(ref encoder, + SerializedDataBlobKind.StepThroughStubAddress, + (byte)StepThroughStubFlags.IsTailCallStub); + encoder.WriteUnsignedLong((ulong)thunk.ToInt64()); + Instance.ThreadSafeWriteBytes(encoder.GetBytes()); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TemplateLocator.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TemplateLocator.cs new file mode 100644 index 00000000000000..37752ef347200b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TemplateLocator.cs @@ -0,0 +1,308 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +using Internal.Runtime; +using Internal.Runtime.Augments; + +using Internal.NativeFormat; +using Internal.TypeSystem; + +namespace Internal.Runtime.TypeLoader +{ + internal struct TemplateLocator + { + private const uint BadTokenFixupValue = 0xFFFFFFFF; + + // + // Returns the template type handle for a generic instantation type + // + public TypeDesc TryGetTypeTemplate(TypeDesc concreteType, ref NativeLayoutInfo nativeLayoutInfo) + { +#if GENERICS_FORCE_USG + return TryGetUniversalTypeTemplate(concreteType, ref nativeLayoutInfo); +#else + // First, see if there is a specific canonical template + TypeDesc result = TryGetTypeTemplate_Internal(concreteType, CanonicalFormKind.Specific, out nativeLayoutInfo.Module, out nativeLayoutInfo.Offset); + + // If not found, see if there's a universal canonical template + if (result == null) + result = TryGetUniversalTypeTemplate(concreteType, ref nativeLayoutInfo); + + return result; +#endif + } + + public TypeDesc TryGetUniversalTypeTemplate(TypeDesc concreteType, ref NativeLayoutInfo nativeLayoutInfo) + { + return TryGetTypeTemplate_Internal(concreteType, CanonicalFormKind.Universal, out nativeLayoutInfo.Module, out nativeLayoutInfo.Offset); + } + +#if GENERICS_FORCE_USG + public TypeDesc TryGetNonUniversalTypeTemplate(TypeDesc concreteType, ref NativeLayoutInfo nativeLayoutInfo) + { + return TryGetTypeTemplate_Internal(concreteType, CanonicalFormKind.Specific, out nativeLayoutInfo.Module, out nativeLayoutInfo.Offset); + } +#endif + + /// + /// Get the NativeLayout for a type from a ReadyToRun image. + /// + public bool TryGetMetadataNativeLayout(TypeDesc concreteType, out NativeFormatModuleInfo nativeLayoutInfoModule, out uint nativeLayoutInfoToken) + { + nativeLayoutInfoModule = null; + nativeLayoutInfoToken = 0; +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + var nativeMetadataType = concreteType.GetTypeDefinition() as TypeSystem.NativeFormat.NativeFormatType; + if (nativeMetadataType == null) + return false; + + var canonForm = concreteType.ConvertToCanonForm(CanonicalFormKind.Specific); + var hashCode = canonForm.GetHashCode(); + +#if SUPPORTS_R2R_LOADING + foreach (var moduleInfo in ModuleList.EnumerateModules()) + { + if (moduleInfo.MetadataReader == null) + continue; + + ExternalReferencesTable externalFixupsTable; + NativeHashtable typeTemplatesHashtable = LoadHashtable(moduleInfo.Handle, ReflectionMapBlob.MetadataBasedTypeTemplateMap, out externalFixupsTable); + + if (typeTemplatesHashtable.IsNull) + continue; + + var enumerator = typeTemplatesHashtable.Lookup(hashCode); + var nativeMetadataUnit = nativeMetadataType.Context.ResolveMetadataUnit(moduleInfo); + + NativeParser entryParser; + while (!(entryParser = enumerator.GetNext()).IsNull) + { + var entryTypeHandle = entryParser.GetUnsigned().AsHandle(); + TypeDesc typeDesc = nativeMetadataUnit.GetType(entryTypeHandle); + Debug.Assert(typeDesc != null); + if (typeDesc == canonForm) + { + TypeLoaderLogger.WriteLine("Found metadata template for type " + concreteType.ToString() + ": " + typeDesc.ToString()); + nativeLayoutInfoToken = entryParser.GetUnsigned(); + if (nativeLayoutInfoToken == BadTokenFixupValue) + { + throw new BadImageFormatException(); + } + + nativeLayoutInfoModule = moduleHandle; + return true; + } + } + } +#endif +#endif + + return false; + } + + /// + /// Get the NativeLayout for a method from a ReadyToRun image. + /// + public bool TryGetMetadataNativeLayout(MethodDesc concreteMethod, out NativeFormatModuleInfo nativeLayoutInfoModule, out uint nativeLayoutInfoToken) + { + nativeLayoutInfoModule = null; + nativeLayoutInfoToken = 0; +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + var nativeMetadataType = concreteMethod.GetTypicalMethodDefinition() as TypeSystem.NativeFormat.NativeFormatMethod; + if (nativeMetadataType == null) + return false; + + var canonForm = concreteMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + var hashCode = canonForm.GetHashCode(); + +#if SUPPORTS_R2R_LOADING + foreach (var moduleInfo in ModuleList.EnumerateModules()) + { + if (moduleInfo.MetadataReader == null) + continue; + + ExternalReferencesTable externalFixupsTable; + NativeHashtable methodTemplatesHashtable = LoadHashtable(moduleInfo.Handle, ReflectionMapBlob.MetadataBasedGenericMethodsTemplateMap, out externalFixupsTable); + + if (methodTemplatesHashtable.IsNull) + continue; + + var enumerator = methodTemplatesHashtable.Lookup(hashCode); + var nativeMetadataUnit = nativeMetadataType.Context.ResolveMetadataUnit(moduleInfo); + + NativeParser entryParser; + while (!(entryParser = enumerator.GetNext()).IsNull) + { + var entryTypeHandle = entryParser.GetUnsigned().AsHandle(); + MethodDesc methodDesc = nativeMetadataUnit.GetMethod(entryTypeHandle, null); + Debug.Assert(methodDesc != null); + if (methodDesc == canonForm) + { + TypeLoaderLogger.WriteLine("Found metadata template for method " + concreteMethod.ToString() + ": " + methodDesc.ToString()); + nativeLayoutInfoToken = entryParser.GetUnsigned(); + if (nativeLayoutInfoToken == BadTokenFixupValue) + { + throw new BadImageFormatException(); + } + + nativeLayoutInfoModule = moduleInfo; + return true; + } + } + } +#endif +#endif + return false; + } + + private TypeDesc TryGetTypeTemplate_Internal(TypeDesc concreteType, CanonicalFormKind kind, out NativeFormatModuleInfo nativeLayoutInfoModule, out uint nativeLayoutInfoToken) + { + nativeLayoutInfoModule = null; + nativeLayoutInfoToken = 0; + var canonForm = concreteType.ConvertToCanonForm(kind); + var hashCode = canonForm.GetHashCode(); + + foreach (NativeFormatModuleInfo moduleInfo in ModuleList.EnumerateModules()) + { + ExternalReferencesTable externalFixupsTable; + NativeHashtable typeTemplatesHashtable = LoadHashtable(moduleInfo, ReflectionMapBlob.TypeTemplateMap, out externalFixupsTable); + + if (typeTemplatesHashtable.IsNull) + continue; + + var enumerator = typeTemplatesHashtable.Lookup(hashCode); + + NativeParser entryParser; + while (!(entryParser = enumerator.GetNext()).IsNull) + { + RuntimeTypeHandle candidateTemplateTypeHandle = externalFixupsTable.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + TypeDesc candidateTemplate = concreteType.Context.ResolveRuntimeTypeHandle(candidateTemplateTypeHandle); + + if (canonForm == candidateTemplate.ConvertToCanonForm(kind)) + { + TypeLoaderLogger.WriteLine("Found template for type " + concreteType.ToString() + ": " + candidateTemplate.ToString()); + nativeLayoutInfoToken = entryParser.GetUnsigned(); + if (nativeLayoutInfoToken == BadTokenFixupValue) + { + // TODO: once multifile gets fixed up, make this throw a BadImageFormatException + TypeLoaderLogger.WriteLine("ERROR: template not fixed up, skipping"); + continue; + } + + Debug.Assert( + (kind != CanonicalFormKind.Universal) || + (kind == CanonicalFormKind.Universal && candidateTemplate == candidateTemplate.ConvertToCanonForm(kind))); + + nativeLayoutInfoModule = moduleInfo; + return candidateTemplate; + } + } + } + + TypeLoaderLogger.WriteLine("ERROR: Cannot find a suitable template for type " + concreteType.ToString()); + return null; + } + + // + // Returns the template method for a generic method instantation + // + public InstantiatedMethod TryGetGenericMethodTemplate(InstantiatedMethod concreteMethod, out NativeFormatModuleInfo nativeLayoutInfoModule, out uint nativeLayoutInfoToken) + { + // First, see if there is a specific canonical template + InstantiatedMethod result = TryGetGenericMethodTemplate_Internal(concreteMethod, CanonicalFormKind.Specific, out nativeLayoutInfoModule, out nativeLayoutInfoToken); + + // If not found, see if there's a universal canonical template + if (result == null) + result = TryGetGenericMethodTemplate_Internal(concreteMethod, CanonicalFormKind.Universal, out nativeLayoutInfoModule, out nativeLayoutInfoToken); + + return result; + } + private InstantiatedMethod TryGetGenericMethodTemplate_Internal(InstantiatedMethod concreteMethod, CanonicalFormKind kind, out NativeFormatModuleInfo nativeLayoutInfoModule, out uint nativeLayoutInfoToken) + { + nativeLayoutInfoModule = null; + nativeLayoutInfoToken = 0; + var canonForm = concreteMethod.GetCanonMethodTarget(kind); + var hashCode = canonForm.GetHashCode(); + + foreach (NativeFormatModuleInfo moduleInfo in ModuleList.EnumerateModules()) + { + NativeReader nativeLayoutReader = TypeLoaderEnvironment.Instance.GetNativeLayoutInfoReader(moduleInfo.Handle); + if (nativeLayoutReader == null) + continue; + + ExternalReferencesTable externalFixupsTable; + NativeHashtable genericMethodTemplatesHashtable = LoadHashtable(moduleInfo, ReflectionMapBlob.GenericMethodsTemplateMap, out externalFixupsTable); + + if (genericMethodTemplatesHashtable.IsNull) + continue; + + var context = new NativeLayoutInfoLoadContext + { + _typeSystemContext = concreteMethod.Context, + _typeArgumentHandles = concreteMethod.OwningType.Instantiation, + _methodArgumentHandles = concreteMethod.Instantiation, + _module = moduleInfo + }; + + var enumerator = genericMethodTemplatesHashtable.Lookup(hashCode); + + NativeParser entryParser; + while (!(entryParser = enumerator.GetNext()).IsNull) + { + var methodSignatureParser = new NativeParser(nativeLayoutReader, entryParser.GetUnsigned()); + + // Get the unified generic method holder and convert it to its canonical form + var candidateTemplate = (InstantiatedMethod)context.GetMethod(ref methodSignatureParser); + Debug.Assert(candidateTemplate.Instantiation.Length > 0); + + if (canonForm == candidateTemplate.GetCanonMethodTarget(kind)) + { + TypeLoaderLogger.WriteLine("Found template for generic method " + concreteMethod.ToString() + ": " + candidateTemplate.ToString()); + nativeLayoutInfoModule = moduleInfo; + nativeLayoutInfoToken = entryParser.GetUnsigned(); + if (nativeLayoutInfoToken == BadTokenFixupValue) + { + // TODO: once multifile gets fixed up, make this throw a BadImageFormatException + TypeLoaderLogger.WriteLine("ERROR: template not fixed up, skipping"); + continue; + } + + Debug.Assert( + (kind != CanonicalFormKind.Universal) || + (kind == CanonicalFormKind.Universal && candidateTemplate == candidateTemplate.GetCanonMethodTarget(kind))); + + return candidateTemplate; + } + } + } + + TypeLoaderLogger.WriteLine("ERROR: Cannot find a suitable template for generic method " + concreteMethod.ToString()); + return null; + } + + // Lazy loadings of hashtables (load on-demand only) + private unsafe NativeHashtable LoadHashtable(NativeFormatModuleInfo module, ReflectionMapBlob hashtableBlobId, out ExternalReferencesTable externalFixupsTable) + { + // Load the common fixups table + externalFixupsTable = default(ExternalReferencesTable); + if (!externalFixupsTable.InitializeCommonFixupsTable(module)) + return default(NativeHashtable); + + // Load the hashtable + byte* pBlob; + uint cbBlob; + if (!module.TryFindBlob(hashtableBlobId, out pBlob, out cbBlob)) + return default(NativeHashtable); + + NativeReader reader = new NativeReader(pBlob, cbBlob); + NativeParser parser = new NativeParser(reader, 0); + return new NativeHashtable(parser); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs new file mode 100644 index 00000000000000..aa1c1b918168b6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs @@ -0,0 +1,2265 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime; +using System.Text; + +using System.Reflection.Runtime.General; + +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.Metadata.NativeFormat; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.TypeSystem.NativeFormat; +using Internal.TypeSystem.NoMetadata; + +namespace Internal.Runtime.TypeLoader +{ + using DynamicGenericsRegistrationData = TypeLoaderEnvironment.DynamicGenericsRegistrationData; + using GenericTypeEntry = TypeLoaderEnvironment.GenericTypeEntry; + using TypeEntryToRegister = TypeLoaderEnvironment.TypeEntryToRegister; + using GenericMethodEntry = TypeLoaderEnvironment.GenericMethodEntry; + using HandleBasedGenericTypeLookup = TypeLoaderEnvironment.HandleBasedGenericTypeLookup; + using DefTypeBasedGenericTypeLookup = TypeLoaderEnvironment.DefTypeBasedGenericTypeLookup; + using HandleBasedGenericMethodLookup = TypeLoaderEnvironment.HandleBasedGenericMethodLookup; + using MethodDescBasedGenericMethodLookup = TypeLoaderEnvironment.MethodDescBasedGenericMethodLookup; + using ThunkKind = CallConverterThunk.ThunkKind; + using VTableSlotMapper = TypeBuilderState.VTableSlotMapper; + + internal static class LowLevelListExtensions + { + public static void Expand(this LowLevelList list, int count) + { + if (list.Capacity < count) + list.Capacity = count; + + while (list.Count < count) + list.Add(default(T)); + } + + public static bool HasSetBits(this LowLevelList list) + { + for (int index = 0; index < list.Count; index++) + { + if (list[index]) + return true; + } + + return false; + } + } + + [Flags] + internal enum FieldLoadState + { + None = 0, + Instance = 1, + Statics = 2, + } + + public static class TypeBuilderApi + { + public static void ResolveMultipleCells(GenericDictionaryCell [] cells, out IntPtr[] fixups) + { + TypeBuilder.ResolveMultipleCells(cells, out fixups); + } + } + + + internal class TypeBuilder + { + public TypeBuilder() + { + TypeLoaderEnvironment.Instance.VerifyTypeLoaderLockHeld(); + } + + private const int MinimumValueTypeSize = 0x1; + + /// + /// The StaticClassConstructionContext for a type is encoded in the negative space + /// of the NonGCStatic fields of a type. + /// + public static unsafe readonly int ClassConstructorOffset = -sizeof(System.Runtime.CompilerServices.StaticClassConstructionContext); + + private LowLevelList _typesThatNeedTypeHandles = new LowLevelList(); + + private LowLevelList _methodsThatNeedDictionaries = new LowLevelList(); + + private LowLevelList _typesThatNeedPreparation; + + private object _epoch = new object(); + +#if DEBUG + private bool _finalTypeBuilding; +#endif + + // Helper exception to abort type building if we do not find the generic type template + internal class MissingTemplateException : Exception + { + } + + + private bool CheckAllHandlesValidForMethod(MethodDesc method) + { + if (!method.OwningType.RetrieveRuntimeTypeHandleIfPossible()) + return false; + + for (int i = 0; i < method.Instantiation.Length; i++) + if (!method.Instantiation[i].RetrieveRuntimeTypeHandleIfPossible()) + return false; + + return true; + } + + internal bool RetrieveExactFunctionPointerIfPossible(MethodDesc method, out IntPtr result) + { + result = IntPtr.Zero; + + if (!method.IsNonSharableMethod || !CheckAllHandlesValidForMethod(method)) + return false; + + RuntimeTypeHandle[] genMethodArgs = method.Instantiation.Length > 0 ? new RuntimeTypeHandle[method.Instantiation.Length] : Empty.Array; + for (int i = 0; i < method.Instantiation.Length; i++) + genMethodArgs[i] = method.Instantiation[i].RuntimeTypeHandle; + + return TypeLoaderEnvironment.Instance.TryLookupExactMethodPointerForComponents(method.OwningType.RuntimeTypeHandle, method.NameAndSignature, genMethodArgs, out result); + } + + internal bool RetrieveMethodDictionaryIfPossible(InstantiatedMethod method) + { + if (method.RuntimeMethodDictionary != IntPtr.Zero) + return true; + + bool allHandlesValid = CheckAllHandlesValidForMethod(method); + + TypeLoaderLogger.WriteLine("Looking for method dictionary for method " + method.ToString() + " ... " + (allHandlesValid ? "(All type arg handles valid)" : "")); + + IntPtr methodDictionary; + + if ((allHandlesValid && TypeLoaderEnvironment.Instance.TryLookupGenericMethodDictionaryForComponents(new HandleBasedGenericMethodLookup(method), out methodDictionary)) || + (!allHandlesValid && TypeLoaderEnvironment.Instance.TryLookupGenericMethodDictionaryForComponents(new MethodDescBasedGenericMethodLookup(method), out methodDictionary))) + { + TypeLoaderLogger.WriteLine("Found DICT = " + methodDictionary.LowLevelToString() + " for method " + method.ToString()); + method.AssociateWithRuntimeMethodDictionary(methodDictionary); + return true; + } + + return false; + } + + /// + /// Register the type for preparation. The preparation will be done once the current type is prepared. + /// This is the prefered way to get a dependent type prepared because of it avoids issues with cycles and recursion. + /// + public void RegisterForPreparation(TypeDesc type) + { + TypeLoaderLogger.WriteLine("Register for preparation " + type.ToString() + " ..."); + + // If this type has type handle, do nothing and return + if (type.RetrieveRuntimeTypeHandleIfPossible()) + return; + + var state = type.GetOrCreateTypeBuilderState(); + + // If this type was already inspected, do nothing and return. + if (state.NeedsTypeHandle) + return; + + state.NeedsTypeHandle = true; + + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return; + + if (_typesThatNeedPreparation == null) + _typesThatNeedPreparation = new LowLevelList(); + + _typesThatNeedPreparation.Add(type); + } + + /// + /// Collects all dependencies that need to be created in order to create + /// the method that was passed in. + /// + public void PrepareMethod(MethodDesc method) + { + TypeLoaderLogger.WriteLine("Preparing method " + method.ToString() + " ..."); + + RegisterForPreparation(method.OwningType); + + if (method.Instantiation.Length == 0) + return; + + InstantiatedMethod genericMethod = (InstantiatedMethod)method; + + if (RetrieveMethodDictionaryIfPossible(genericMethod)) + return; + + // If this method was already inspected, do nothing and return + if (genericMethod.NeedsDictionary) + return; + + genericMethod.NeedsDictionary = true; + + if (genericMethod.IsCanonicalMethod(CanonicalFormKind.Any)) + return; + + _methodsThatNeedDictionaries.Add(genericMethod); + + foreach (var type in genericMethod.Instantiation) + RegisterForPreparation(type); + + ParseNativeLayoutInfo(genericMethod); + } + + private void InsertIntoNeedsTypeHandleList(TypeBuilderState state, TypeDesc type) + { + if ((type is DefType) || (type is ArrayType) || (type is PointerType) || (type is ByRefType)) + { + _typesThatNeedTypeHandles.Add(type); + } + } + + /// + /// Collects all dependencies that need to be created in order to create + /// the type that was passed in. + /// + internal void PrepareType(TypeDesc type) + { + TypeLoaderLogger.WriteLine("Preparing type " + type.ToString() + " ..."); + + TypeBuilderState state = type.GetTypeBuilderStateIfExist(); + bool hasTypeHandle = type.RetrieveRuntimeTypeHandleIfPossible(); + + // If this type has type handle, do nothing and return unless we should prepare even in the presence of a type handle + if (hasTypeHandle) + return; + + if (state == null) + state = type.GetOrCreateTypeBuilderState(); + + // If this type was already prepared, do nothing unless we are re-preparing it for the purpose of loading the field layout + if (state.HasBeenPrepared) + { + return; + } + + state.HasBeenPrepared = true; + state.NeedsTypeHandle = true; + + if (!hasTypeHandle) + { + InsertIntoNeedsTypeHandleList(state, type); + } + + bool noExtraPreparation = false; // Set this to true for types which don't need other types to be prepared. I.e GenericTypeDefinitions + + if (type is DefType) + { + DefType typeAsDefType = (DefType)type; + + if (typeAsDefType.HasInstantiation) + { + if (typeAsDefType.IsTypeDefinition) + { + noExtraPreparation = true; + } + else + { + // This call to ComputeTemplate will find the native layout info for the type, and the template + // For metadata loaded types, a template will not exist, but we may find the NativeLayout describing the generic dictionary + typeAsDefType.ComputeTemplate(state, false); + + Debug.Assert(state.TemplateType == null || (state.TemplateType is DefType && !state.TemplateType.RuntimeTypeHandle.IsNull())); + + // Collect dependencies + + // We need the instantiation arguments to register a generic type + foreach (var instArg in typeAsDefType.Instantiation) + RegisterForPreparation(instArg); + + // We need the type definition to register a generic type + if (type.GetTypeDefinition() is MetadataType) + RegisterForPreparation(type.GetTypeDefinition()); + + ParseNativeLayoutInfo(state, type); + } + } + + if (!noExtraPreparation) + state.PrepareStaticGCLayout(); + } + else if (type is ParameterizedType) + { + PrepareType(((ParameterizedType)type).ParameterType); + + if (type is ArrayType) + { + ArrayType typeAsArrayType = (ArrayType)type; + + if (typeAsArrayType.IsSzArray && !typeAsArrayType.ElementType.IsPointer) + { + typeAsArrayType.ComputeTemplate(state); + Debug.Assert(state.TemplateType != null && state.TemplateType is ArrayType && !state.TemplateType.RuntimeTypeHandle.IsNull()); + + ParseNativeLayoutInfo(state, type); + } + else + { + Debug.Assert(typeAsArrayType.IsMdArray || typeAsArrayType.ElementType.IsPointer); + } + + // Assert that non-valuetypes are considered to have pointer size + Debug.Assert(typeAsArrayType.ParameterType.IsValueType || state.ComponentSize == IntPtr.Size); + } + } + else + { + Debug.Assert(false); + } + + // Need to prepare the base type first since it is used to compute interfaces + if (!noExtraPreparation) + { + PrepareBaseTypeAndDictionaries(type); + PrepareRuntimeInterfaces(type); + + TypeLoaderLogger.WriteLine("Layout for type " + type.ToString() + " complete." + + " IsHFA = " + (state.IsHFA ? "true" : "false") + + " Type size = " + (state.TypeSize.HasValue ? state.TypeSize.Value.LowLevelToString() : "UNDEF") + + " Fields size = " + (state.UnalignedTypeSize.HasValue ? state.UnalignedTypeSize.Value.LowLevelToString() : "UNDEF") + + " Type alignment = " + (state.FieldAlignment.HasValue ? state.FieldAlignment.Value.LowLevelToString() : "UNDEF")); + +#if FEATURE_UNIVERSAL_GENERICS + if (state.TemplateType != null && state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + { + state.VTableSlotsMapping = new VTableSlotMapper(state.TemplateType.RuntimeTypeHandle.GetNumVtableSlots()); + ComputeVTableLayout(type, state.TemplateType, state); + } +#endif + } + } + + /// + /// Recursively triggers preparation for a type's runtime interfaces + /// + private void PrepareRuntimeInterfaces(TypeDesc type) + { + // Prepare all the interfaces that might be used. (This can be a superset of the + // interfaces explicitly in the NativeLayout.) + foreach (DefType interfaceType in type.RuntimeInterfaces) + { + PrepareType(interfaceType); + } + } + + /// + /// Triggers preparation for a type's base types + /// + private void PrepareBaseTypeAndDictionaries(TypeDesc type) + { + DefType baseType = type.BaseType; + if (baseType == null) + return; + + PrepareType(baseType); + } + + private void ProcessTypesNeedingPreparation() + { + // Process the pending types + while (_typesThatNeedPreparation != null) + { + var pendingTypes = _typesThatNeedPreparation; + _typesThatNeedPreparation = null; + + for (int i = 0; i < pendingTypes.Count; i++) + PrepareType(pendingTypes[i]); + } + } + + private GenericDictionaryCell[] GetGenericMethodDictionaryCellsForMetadataBasedLoad(InstantiatedMethod method, InstantiatedMethod nonTemplateMethod) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + uint r2rNativeLayoutInfoToken; + GenericDictionaryCell[] cells = null; + NativeFormatModuleInfo r2rNativeLayoutModuleInfo; + + if ((new TemplateLocator()).TryGetMetadataNativeLayout(nonTemplateMethod, out r2rNativeLayoutModuleInfo, out r2rNativeLayoutInfoToken)) + { + // ReadyToRun dictionary parsing + NativeReader readyToRunReader = TypeLoaderEnvironment.Instance.GetNativeLayoutInfoReader(r2rNativeLayoutModuleInfo.Handle); + var readyToRunInfoParser = new NativeParser(readyToRunReader, r2rNativeLayoutInfoToken); + + // A null readyToRunInfoParser is a valid situation to end up in + // This can happen if either we have exact code for a method, or if + // we are going to use the universal generic implementation. + // In both of those cases, we do not have any generic dictionary cells + // to put into the dictionary + if (!readyToRunInfoParser.IsNull) + { + NativeFormatMetadataUnit nativeMetadataUnit = method.Context.ResolveMetadataUnit(r2rNativeLayoutModuleInfo); + FixupCellMetadataResolver resolver = new FixupCellMetadataResolver(nativeMetadataUnit, nonTemplateMethod); + cells = GenericDictionaryCell.BuildDictionaryFromMetadataTokensAndContext(this, readyToRunInfoParser, nativeMetadataUnit, resolver); + } + } + + return cells; +#else + return null; +#endif + } + + internal void ParseNativeLayoutInfo(InstantiatedMethod method) + { + TypeLoaderLogger.WriteLine("Parsing NativeLayoutInfo for method " + method.ToString() + " ..."); + + Debug.Assert(method.Dictionary == null); + + InstantiatedMethod nonTemplateMethod = method; + + // Templates are always non-unboxing stubs + if (method.UnboxingStub) + { + // Strip unboxing stub, note the first parameter which is false + nonTemplateMethod = (InstantiatedMethod)method.Context.ResolveGenericMethodInstantiation(false, (DefType)method.OwningType, method.NameAndSignature, method.Instantiation, IntPtr.Zero, false); + } + + uint nativeLayoutInfoToken; + NativeFormatModuleInfo nativeLayoutModule; + MethodDesc templateMethod = (new TemplateLocator()).TryGetGenericMethodTemplate(nonTemplateMethod, out nativeLayoutModule, out nativeLayoutInfoToken); + + // If the templateMethod found in the static image is missing or universal, see if the R2R layout + // can provide something more specific. + if ((templateMethod == null) || templateMethod.IsCanonicalMethod(CanonicalFormKind.Universal)) + { + GenericDictionaryCell[] cells = GetGenericMethodDictionaryCellsForMetadataBasedLoad(method, nonTemplateMethod); + + if (cells != null) + { + method.SetGenericDictionary(new GenericMethodDictionary(cells)); + return; + } + + if (templateMethod == null) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + // In this case we were looking for the r2r template to create the dictionary, but + // there isn't one. This implies that we don't need a Canon specific dictionary + // so just generate something empty + method.SetGenericDictionary(new GenericMethodDictionary(Array.Empty())); + return; +#else + throw new TypeBuilder.MissingTemplateException(); +#endif + } + } + + // Ensure that if this method is non-shareable from a normal canonical perspective, then + // its template MUST be a universal canonical template method + Debug.Assert(!method.IsNonSharableMethod || (method.IsNonSharableMethod && templateMethod.IsCanonicalMethod(CanonicalFormKind.Universal))); + + NativeReader nativeLayoutInfoReader = TypeLoaderEnvironment.Instance.GetNativeLayoutInfoReader(nativeLayoutModule.Handle); + + var methodInfoParser = new NativeParser(nativeLayoutInfoReader, nativeLayoutInfoToken); + var context = new NativeLayoutInfoLoadContext + { + _typeSystemContext = method.Context, + _typeArgumentHandles = method.OwningType.Instantiation, + _methodArgumentHandles = method.Instantiation, + _module = nativeLayoutModule + }; + + BagElementKind kind; + while ((kind = methodInfoParser.GetBagElementKind()) != BagElementKind.End) + { + switch (kind) + { + case BagElementKind.DictionaryLayout: + TypeLoaderLogger.WriteLine("Found BagElementKind.DictionaryLayout"); + method.SetGenericDictionary(new GenericMethodDictionary(GenericDictionaryCell.BuildDictionary(this, context, methodInfoParser.GetParserFromRelativeOffset()))); + break; + + default: + Debug.Fail("Unexpected BagElementKind for generic method with name " + method.NameAndSignature.Name + "! Only BagElementKind.DictionaryLayout should appear."); + throw new BadImageFormatException(); + } + } + + if (method.Dictionary == null) + method.SetGenericDictionary(new GenericMethodDictionary(Array.Empty())); + } + + internal void ParseNativeLayoutInfo(TypeBuilderState state, TypeDesc type) + { + TypeLoaderLogger.WriteLine("Parsing NativeLayoutInfo for type " + type.ToString() + " ..."); + + bool isTemplateUniversalCanon = false; + if (state.TemplateType != null) + { + isTemplateUniversalCanon = state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal); + } + + // If we found the universal template, see if there is a ReadyToRun dictionary description available. + // If so, use that, otherwise, run down the template type loader path with the universal template + if ((state.TemplateType == null) || isTemplateUniversalCanon) + { + // ReadyToRun case - Native Layout is just the dictionary + NativeParser readyToRunInfoParser = state.GetParserForReadyToRunNativeLayoutInfo(); + GenericDictionaryCell[] cells = null; + + // A null readyToRunInfoParser is a valid situation to end up in + // This can happen if either we have exact code for the method on a type, or if + // we are going to use the universal generic implementation. + // In both of those cases, we do not have any generic dictionary cells + // to put into the dictionary + if (!readyToRunInfoParser.IsNull) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + NativeFormatMetadataUnit nativeMetadataUnit = type.Context.ResolveMetadataUnit(state.R2RNativeLayoutInfo.Module); + FixupCellMetadataResolver resolver = new FixupCellMetadataResolver(nativeMetadataUnit, type); + cells = GenericDictionaryCell.BuildDictionaryFromMetadataTokensAndContext(this, readyToRunInfoParser, nativeMetadataUnit, resolver); +#endif + } + state.Dictionary = cells != null ? new GenericTypeDictionary(cells) : null; + + if (state.TemplateType == null) + return; + } + + NativeParser typeInfoParser = state.GetParserForNativeLayoutInfo(); + NativeLayoutInfoLoadContext context = state.NativeLayoutInfo.LoadContext; + + NativeParser baseTypeParser = new NativeParser(); + + int nonGcDataSize = 0; + int gcDataSize = 0; + int threadDataSize = 0; + bool staticSizesMeaningful = (type is DefType) // Is type permitted to have static fields + && !isTemplateUniversalCanon; // Non-universal templates always specify their statics sizes + // if the size can be greater than 0 + + int baseTypeSize = 0; + bool checkBaseTypeSize = false; + + BagElementKind kind; + while ((kind = typeInfoParser.GetBagElementKind()) != BagElementKind.End) + { + switch (kind) + { + case BagElementKind.BaseType: + TypeLoaderLogger.WriteLine("Found BagElementKind.BaseType"); + Debug.Assert(baseTypeParser.IsNull); + baseTypeParser = typeInfoParser.GetParserFromRelativeOffset(); + break; + + case BagElementKind.BaseTypeSize: + TypeLoaderLogger.WriteLine("Found BagElementKind.BaseTypeSize"); + Debug.Assert(state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)); + baseTypeSize = checked((int)typeInfoParser.GetUnsigned()); + break; + + case BagElementKind.ImplementedInterfaces: + TypeLoaderLogger.WriteLine("Found BagElementKind.ImplementedInterfaces"); + // Interface handling is done entirely in NativeLayoutInterfacesAlgorithm + typeInfoParser.GetUnsigned(); + break; + + case BagElementKind.TypeFlags: + { + TypeLoaderLogger.WriteLine("Found BagElementKind.TypeFlags"); + Internal.NativeFormat.TypeFlags flags = (Internal.NativeFormat.TypeFlags)typeInfoParser.GetUnsigned(); + Debug.Assert(state.HasStaticConstructor == ((flags & Internal.NativeFormat.TypeFlags.HasClassConstructor) != 0)); + } + break; + + case BagElementKind.ClassConstructorPointer: + TypeLoaderLogger.WriteLine("Found BagElementKind.ClassConstructorPointer"); + state.ClassConstructorPointer = context.GetGCStaticInfo(typeInfoParser.GetUnsigned()); + break; + + case BagElementKind.NonGcStaticDataSize: + TypeLoaderLogger.WriteLine("Found BagElementKind.NonGcStaticDataSize"); + // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) + nonGcDataSize = checked((int)typeInfoParser.GetUnsigned()); + Debug.Assert(staticSizesMeaningful); + break; + + case BagElementKind.GcStaticDataSize: + TypeLoaderLogger.WriteLine("Found BagElementKind.GcStaticDataSize"); + // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) + gcDataSize = checked((int)typeInfoParser.GetUnsigned()); + Debug.Assert(staticSizesMeaningful); + break; + + case BagElementKind.ThreadStaticDataSize: + TypeLoaderLogger.WriteLine("Found BagElementKind.ThreadStaticDataSize"); + // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) + threadDataSize = checked((int)typeInfoParser.GetUnsigned()); + Debug.Assert(staticSizesMeaningful); + break; + + case BagElementKind.GcStaticDesc: + TypeLoaderLogger.WriteLine("Found BagElementKind.GcStaticDesc"); + state.GcStaticDesc = context.GetGCStaticInfo(typeInfoParser.GetUnsigned()); + break; + + case BagElementKind.ThreadStaticDesc: + TypeLoaderLogger.WriteLine("Found BagElementKind.ThreadStaticDesc"); + state.ThreadStaticDesc = context.GetGCStaticInfo(typeInfoParser.GetUnsigned()); + break; + + case BagElementKind.GenericVarianceInfo: + TypeLoaderLogger.WriteLine("Found BagElementKind.GenericVarianceInfo"); + NativeParser varianceInfoParser = typeInfoParser.GetParserFromRelativeOffset(); + state.GenericVarianceFlags = new GenericVariance[varianceInfoParser.GetSequenceCount()]; + for (int i = 0; i < state.GenericVarianceFlags.Length; i++) + state.GenericVarianceFlags[i] = checked((GenericVariance)varianceInfoParser.GetUnsigned()); + break; + + case BagElementKind.FieldLayout: + TypeLoaderLogger.WriteLine("Found BagElementKind.FieldLayout"); + typeInfoParser.SkipInteger(); // Handled in type layout algorithm + break; + +#if FEATURE_UNIVERSAL_GENERICS + case BagElementKind.VTableMethodSignatures: + TypeLoaderLogger.WriteLine("Found BagElementKind.VTableMethodSignatures"); + ParseVTableMethodSignatures(state, context, typeInfoParser.GetParserFromRelativeOffset()); + break; +#endif + + case BagElementKind.SealedVTableEntries: + TypeLoaderLogger.WriteLine("Found BagElementKind.SealedVTableEntries"); + state.NumSealedVTableEntries = typeInfoParser.GetUnsigned(); + break; + + case BagElementKind.DictionaryLayout: + TypeLoaderLogger.WriteLine("Found BagElementKind.DictionaryLayout"); + Debug.Assert(!isTemplateUniversalCanon, "Universal template nativelayout do not have DictionaryLayout"); + + Debug.Assert(state.Dictionary == null); + if (!state.TemplateType.RetrieveRuntimeTypeHandleIfPossible()) + { + TypeLoaderLogger.WriteLine("ERROR: failed to get type handle for template type " + state.TemplateType.ToString()); + throw new TypeBuilder.MissingTemplateException(); + } + state.Dictionary = new GenericTypeDictionary(GenericDictionaryCell.BuildDictionary(this, context, typeInfoParser.GetParserFromRelativeOffset())); + break; + + default: + TypeLoaderLogger.WriteLine("Found unknown BagElementKind: " + ((int)kind).LowLevelToString()); + typeInfoParser.SkipInteger(); + break; + } + } + + if (staticSizesMeaningful) + { + Debug.Assert((state.NonGcDataSize + (state.HasStaticConstructor ? TypeBuilder.ClassConstructorOffset : 0)) == nonGcDataSize); + Debug.Assert(state.GcDataSize == gcDataSize); + Debug.Assert(state.ThreadDataSize == threadDataSize); + } + +#if GENERICS_FORCE_USG + if (isTemplateUniversalCanon && type.CanShareNormalGenericCode()) + { + // Even in the GENERICS_FORCE_USG stress mode today, codegen will generate calls to normal-canonical target methods whenever possible. + // Given that we use universal template types to build the dynamic EETypes, these dynamic types will end up with NULL dictionary + // entries, causing the normal-canonical code sharing to fail. + // To fix this problem, we will load the generic dictionary from the non-universal template type, and build a generic dictionary out of + // it for the dynamic type, and store that dictionary pointer in the dynamic MethodTable's structure. + TypeBuilderState tempState = new TypeBuilderState(); + tempState.NativeLayoutInfo = new NativeLayoutInfo(); + state.NonUniversalTemplateType = tempState.TemplateType = type.Context.TemplateLookup.TryGetNonUniversalTypeTemplate(type, ref tempState.NativeLayoutInfo); + if (tempState.TemplateType != null) + { + Debug.Assert(!tempState.TemplateType.IsCanonicalSubtype(CanonicalFormKind.UniversalCanonLookup)); + NativeParser nonUniversalTypeInfoParser = GetNativeLayoutInfoParser(type, ref tempState.NativeLayoutInfo); + NativeParser dictionaryLayoutParser = nonUniversalTypeInfoParser.GetParserForBagElementKind(BagElementKind.DictionaryLayout); + if (!dictionaryLayoutParser.IsNull) + state.Dictionary = new GenericTypeDictionary(GenericDictionaryCell.BuildDictionary(this, context, dictionaryLayoutParser)); + + // Get the non-universal GCDesc pointers, so we can compare them the ones we will dynamically construct for the type + // and verify they are equal (This is an easy and predictable way of validation for the GCDescs creation logic in the stress mode) + GetNonUniversalGCDescPointers(type, state, tempState); + } + } +#endif + type.ParseBaseType(context, baseTypeParser); + + // Assert that parsed base type size matches the BaseTypeSize that we calculated. + Debug.Assert(!checkBaseTypeSize || state.BaseTypeSize == baseTypeSize); + } + +#if FEATURE_UNIVERSAL_GENERICS + private void ParseVTableMethodSignatures(TypeBuilderState state, NativeLayoutInfoLoadContext nativeLayoutInfoLoadContext, NativeParser methodSignaturesParser) + { + TypeDesc type = state.TypeBeingBuilt; + if (methodSignaturesParser.IsNull) + return; + + // Processing vtable method signatures is only meaningful in the context of universal generics only + Debug.Assert(state.TemplateType != null && state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)); + + uint numSignatures = methodSignaturesParser.GetUnsigned(); + + state.VTableMethodSignatures = new TypeBuilderState.VTableLayoutInfo[numSignatures]; + + for (int i = 0; i < numSignatures; i++) + { + state.VTableMethodSignatures[i] = new TypeBuilderState.VTableLayoutInfo(); + + uint slot = methodSignaturesParser.GetUnsigned(); + state.VTableMethodSignatures[i].VTableSlot = (slot >> 1); + if ((slot & 1) == 1) + { + state.VTableMethodSignatures[i].IsSealedVTableSlot = true; + state.NumSealedVTableMethodSignatures++; + } + + NativeParser sigParser = methodSignaturesParser.GetParserFromRelativeOffset(); + state.VTableMethodSignatures[i].MethodSignature = RuntimeSignature.CreateFromNativeLayoutSignature(nativeLayoutInfoLoadContext._module.Handle, sigParser.Offset); + } + } +#endif + + private unsafe void ComputeVTableLayout(TypeDesc currentType, TypeDesc currentTemplateType, TypeBuilderState targetTypeState) + { + TypeDesc baseType = GetBaseTypeThatIsCorrectForMDArrays(currentType); + TypeDesc baseTemplateType = GetBaseTypeUsingRuntimeTypeHandle(currentTemplateType); + + Debug.Assert((baseType == null && baseTemplateType == null) || (baseType != null && baseTemplateType != null)); + + // Compute the vtable layout for the current type starting with base types first + if (baseType != null) + ComputeVTableLayout(baseType, baseTemplateType, targetTypeState); + + currentTemplateType.RetrieveRuntimeTypeHandleIfPossible(); + Debug.Assert(!currentTemplateType.RuntimeTypeHandle.IsNull()); + Debug.Assert(baseTemplateType == null || !baseTemplateType.RuntimeTypeHandle.IsNull()); + + // The m_usNumVtableSlots field on EETypes includes the count of vtable slots of the base type, + // so make sure we don't count that twice! + int currentVtableIndex = baseTemplateType == null ? 0 : baseTemplateType.RuntimeTypeHandle.GetNumVtableSlots(); + + IntPtr dictionarySlotInVtable = IntPtr.Zero; + + if (currentType.IsGeneric()) + { + if (!currentType.CanShareNormalGenericCode() && currentTemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + { + // We are building a type that cannot share code with normal canonical types, so the type has to have + // the same vtable layout as non-shared generics, meaning no dictionary pointer in the vtable. + // We use universal canonical template types to build such types. Universal canonical types have 'NULL' + // dictionary pointers in their vtables, so we'll start copying the vtable entries right after that + // dictionary slot (dictionaries are accessed/used at runtime in a different way, not through the vtable + // dictionary pointer for such types). + currentVtableIndex++; + } + else if (currentType.CanShareNormalGenericCode()) + { + // In the case of a normal canonical type in their base class hierarchy, + // we need to keep track of its dictionary slot in the vtable mapping, and try to + // copy its value values directly from its template type vtable. + // Two possible cases: + // 1) The template type is a normal canonical type. In this case, the dictionary value + // in the vtable slot of the template is NULL, but that's ok because this case is + // correctly handled anyways by the FinishBaseTypeAndDictionaries() API. + // 2) The template type is NOT a canonical type. In this case, the dictionary value + // in the vtable slot of the template is not null, and we keep track of it in the + // VTableSlotsMapping so we can copy it to the dynamic type after creation. + // This corner case is not handled by FinishBaseTypeAndDictionaries(), so we track it + // here. + // Examples: + // 1) Derived : Base, instantiated over [int,string] + // 2) Derived<__Universal> : BaseClass, and BaseClass : BaseBaseClass + // 3) Derived<__Universal> : BaseClass + Debug.Assert(currentTemplateType != null && !currentTemplateType.RuntimeTypeHandle.IsNull()); + + IntPtr* pTemplateVtable = (IntPtr*)((byte*)(currentTemplateType.RuntimeTypeHandle.ToEETypePtr()) + sizeof(MethodTable)); + dictionarySlotInVtable = pTemplateVtable[currentVtableIndex]; + } + } + else if (currentType is ArrayType) + { + if (currentTemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + { + TypeDesc canonicalElementType = currentType.Context.ConvertToCanon(((ArrayType)currentType).ElementType, CanonicalFormKind.Specific); + bool quickIsNotCanonical = canonicalElementType == ((ArrayType)currentType).ElementType; + + Debug.Assert(quickIsNotCanonical == !canonicalElementType.IsCanonicalSubtype(CanonicalFormKind.Any)); + + if (quickIsNotCanonical) + { + // We are building a type that cannot share code with normal canonical types, so the type has to have + // the same vtable layout as non-shared generics, meaning no dictionary pointer in the vtable. + // We use universal canonical template types to build such types. Universal canonical types have 'NULL' + // dictionary pointers in their vtables, so we'll start copying the vtable entries right after that + // dictionary slot (dictionaries are accessed/used at runtime in a different way, not through the vtable + // dictionary pointer for such types). + currentVtableIndex++; + } + } + } + + // Map vtable entries from target type's template type + int numVtableSlotsOnCurrentTemplateType = currentTemplateType.RuntimeTypeHandle.GetNumVtableSlots(); + for (; currentVtableIndex < numVtableSlotsOnCurrentTemplateType; currentVtableIndex++) + { + targetTypeState.VTableSlotsMapping.AddMapping( + currentVtableIndex, + targetTypeState.VTableSlotsMapping.NumSlotMappings, + dictionarySlotInVtable); + + // Reset dictionarySlotInVtable (only one dictionary slot in vtable per type) + dictionarySlotInVtable = IntPtr.Zero; + } + + // Sanity check: vtable of the dynamic type should be equal or smaller than the vtable of the template type + Debug.Assert(targetTypeState.VTableSlotsMapping.NumSlotMappings <= numVtableSlotsOnCurrentTemplateType); + } + + /// + /// Wraps information about how a type is laid out into one package. Types may have been laid out by + /// TypeBuilder (which means they have a gc bitfield), or they could be types that were laid out by NUTC + /// (which means we only have a GCDesc for them). This struct wraps both of those possibilities into + /// one package to be able to write that layout to another bitfield we are constructing. (This is for + /// struct fields.) + /// + internal unsafe struct GCLayout + { + private LowLevelList _bitfield; + private unsafe void* _gcdesc; + private int _size; + private bool _isReferenceTypeGCLayout; + + public static GCLayout None { get { return new GCLayout(); } } + public static GCLayout SingleReference { get; } = new GCLayout(new LowLevelList(new bool[1] { true }), false); + + public bool IsNone { get { return _bitfield == null && _gcdesc == null; } } + + public GCLayout(LowLevelList bitfield, bool isReferenceTypeGCLayout) + { + Debug.Assert(bitfield != null); + + _bitfield = bitfield; + _gcdesc = null; + _size = 0; + _isReferenceTypeGCLayout = isReferenceTypeGCLayout; + } + + public GCLayout(RuntimeTypeHandle rtth) + { + MethodTable* MethodTable = rtth.ToEETypePtr(); + Debug.Assert(MethodTable != null); + + _bitfield = null; + _isReferenceTypeGCLayout = false; // This field is only used for the LowLevelList path + _gcdesc = MethodTable->HasGCPointers ? (void**)MethodTable - 1 : null; + _size = (int)MethodTable->BaseSize; + } + + /// + /// Writes this layout to the given bitfield. + /// + /// The bitfield to write a layout to (may be null, at which + /// point it will be created and assigned). + /// The offset at which we need to write the bitfield. + public void WriteToBitfield(LowLevelList bitfield, int offset) + { + if (bitfield == null) + throw new ArgumentNullException(nameof(bitfield)); + + if (IsNone) + return; + + // Ensure exactly one of these two are set. + Debug.Assert(_gcdesc != null ^ _bitfield != null); + + if (_bitfield != null) + MergeBitfields(bitfield, offset); + else + WriteGCDescToBitfield(bitfield, offset); + } + + private unsafe void WriteGCDescToBitfield(LowLevelList bitfield, int offset) + { + int startIndex = offset / IntPtr.Size; + + void** ptr = (void**)_gcdesc; + Debug.Assert(_gcdesc != null); + + // Number of series + int count = (int)*ptr-- - 1; + Debug.Assert(count >= 0); + + // Ensure capacity for the values we are about to write + int capacity = startIndex + _size / IntPtr.Size - 2; + bitfield.Expand(capacity); + + while (count-- >= 0) + { + int offs = (int)*ptr-- / IntPtr.Size - 1; + int len = ((int)*ptr-- + _size) / IntPtr.Size; + + Debug.Assert(len > 0); + Debug.Assert(offs >= 0); + + for (int i = 0; i < len; i++) + bitfield[startIndex + offs + i] = true; + } + } + + private void MergeBitfields(LowLevelList outputBitfield, int offset) + { + int startIndex = offset / IntPtr.Size; + + // These routines represent the GC layout after the MethodTable pointer + // in an object, but the LowLevelList bitfield logically contains + // the EETypepointer if it is describing a reference type. So, skip the + // first value. + int itemsToSkip = _isReferenceTypeGCLayout ? 1 : 0; + + // Assert that we only skip a non-reported pointer. + Debug.Assert(itemsToSkip == 0 || _bitfield[0] == false); + + // Ensure capacity for the values we are about to write + int capacity = startIndex + _bitfield.Count - itemsToSkip; + outputBitfield.Expand(capacity); + + + for (int i = itemsToSkip; i < _bitfield.Count; i++) + { + // We should never overwrite a TRUE value in the table. + Debug.Assert(!outputBitfield[startIndex + i - itemsToSkip] || _bitfield[i]); + + outputBitfield[startIndex + i - itemsToSkip] = _bitfield[i]; + } + } + } + +#if GENERICS_FORCE_USG + private unsafe void GetNonUniversalGCDescPointers(TypeDesc type, TypeBuilderState state, TypeBuilderState tempNonUniversalState) + { + NativeParser nonUniversalTypeInfoParser = GetNativeLayoutInfoParser(type, ref tempNonUniversalState.NativeLayoutInfo); + NativeLayoutInfoLoadContext context = tempNonUniversalState.NativeLayoutInfo.LoadContext; + + uint beginOffset = nonUniversalTypeInfoParser.Offset; + uint? staticGCDescId = nonUniversalTypeInfoParser.GetUnsignedForBagElementKind(BagElementKind.GcStaticDesc); + + nonUniversalTypeInfoParser.Offset = beginOffset; + uint? threadStaticGCDescId = nonUniversalTypeInfoParser.GetUnsignedForBagElementKind(BagElementKind.ThreadStaticDesc); + + if(staticGCDescId.HasValue) + state.NonUniversalStaticGCDesc = context.GetStaticInfo(staticGCDescId.Value); + + if (threadStaticGCDescId.HasValue) + state.NonUniversalThreadStaticGCDesc = context.GetStaticInfo(threadStaticGCDescId.Value); + + state.NonUniversalInstanceGCDescSize = RuntimeAugments.GetGCDescSize(tempNonUniversalState.TemplateType.RuntimeTypeHandle); + if (state.NonUniversalInstanceGCDescSize > 0) + state.NonUniversalInstanceGCDesc = new IntPtr(((byte*)tempNonUniversalState.TemplateType.RuntimeTypeHandle.ToIntPtr().ToPointer()) - 1); + } +#endif + + private unsafe void AllocateRuntimeType(TypeDesc type) + { + TypeBuilderState state = type.GetTypeBuilderState(); + + Debug.Assert(type is DefType || type is ArrayType || type is PointerType || type is ByRefType); + + if (state.ThreadDataSize != 0) + state.ThreadStaticOffset = TypeLoaderEnvironment.Instance.GetNextThreadStaticsOffsetValue(); + + RuntimeTypeHandle rtt = EETypeCreator.CreateEEType(type, state); + + if (state.ThreadDataSize != 0) + TypeLoaderEnvironment.Instance.RegisterDynamicThreadStaticsInfo(state.HalfBakedRuntimeTypeHandle, state.ThreadStaticOffset, state.ThreadDataSize); + + TypeLoaderLogger.WriteLine("Allocated new type " + type.ToString() + " with hashcode value = 0x" + type.GetHashCode().LowLevelToString() + " with MethodTable = " + rtt.ToIntPtr().LowLevelToString() + " of size " + rtt.ToEETypePtr()->BaseSize.LowLevelToString()); + } + + private void AllocateRuntimeMethodDictionary(InstantiatedMethod method) + { + Debug.Assert(method.RuntimeMethodDictionary == IntPtr.Zero && method.Dictionary != null); + + IntPtr rmd = method.Dictionary.Allocate(); + method.AssociateWithRuntimeMethodDictionary(rmd); + + TypeLoaderLogger.WriteLine("Allocated new method dictionary for method " + method.ToString() + " @ " + rmd.LowLevelToString()); + } + + private RuntimeTypeHandle[] GetGenericContextOfBaseType(DefType type, int vtableMethodSlot) + { + DefType baseType = type.BaseType; + Debug.Assert(baseType == null || !GetRuntimeTypeHandle(baseType).IsNull()); + Debug.Assert(vtableMethodSlot < GetRuntimeTypeHandle(type).GetNumVtableSlots()); + + int numBaseTypeVtableSlots = baseType == null ? 0 : GetRuntimeTypeHandle(baseType).GetNumVtableSlots(); + + if (vtableMethodSlot < numBaseTypeVtableSlots) + return GetGenericContextOfBaseType(baseType, vtableMethodSlot); + else + return GetRuntimeTypeHandles(type.Instantiation); + } + +#if FEATURE_UNIVERSAL_GENERICS + private unsafe void FinishVTableCallingConverterThunks(TypeDesc type, TypeBuilderState state) + { + Debug.Assert(state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)); + + if (state.VTableMethodSignatures == null || state.VTableMethodSignatures.Length == 0) + return; + + int numVtableSlots = GetRuntimeTypeHandle(type).GetNumVtableSlots(); + IntPtr* vtableCells = (IntPtr*)((byte*)GetRuntimeTypeHandle(type).ToIntPtr() + sizeof(MethodTable)); + Debug.Assert((state.VTableMethodSignatures.Length - state.NumSealedVTableMethodSignatures) <= numVtableSlots); + + TypeDesc baseType = type.BaseType; + int numBaseTypeVtableSlots = GetRuntimeTypeHandle(baseType).GetNumVtableSlots(); + + // Generic context + RuntimeTypeHandle[] typeArgs = Empty.Array; + + if (type is DefType) + typeArgs = GetRuntimeTypeHandles(((DefType)type).Instantiation); + else if (type is ArrayType) + typeArgs = GetRuntimeTypeHandles(new Instantiation(new TypeDesc[] { ((ArrayType)type).ElementType })); + + for (int i = 0; i < state.VTableMethodSignatures.Length; i++) + { + RuntimeTypeHandle[] typeArgsToUse = typeArgs; + + int vtableSlotInDynamicType = -1; + if (!state.VTableMethodSignatures[i].IsSealedVTableSlot) + { + vtableSlotInDynamicType = state.VTableSlotsMapping.GetVTableSlotInTargetType((int)state.VTableMethodSignatures[i].VTableSlot); + Debug.Assert(vtableSlotInDynamicType != -1); + + if (vtableSlotInDynamicType < numBaseTypeVtableSlots) + { + // Vtable method from the vtable portion of a base type. Use generic context of the basetype defining the vtable slot. + // We should never reach here for array types (the vtable entries of the System.Array basetype should never need a converter). + Debug.Assert(type is DefType); + typeArgsToUse = GetGenericContextOfBaseType((DefType)type, vtableSlotInDynamicType); + } + } + + IntPtr originalFunctionPointerFromVTable = state.VTableMethodSignatures[i].IsSealedVTableSlot ? + ((IntPtr*)state.HalfBakedSealedVTable)[state.VTableMethodSignatures[i].VTableSlot] : + vtableCells[vtableSlotInDynamicType]; + + IntPtr thunkPtr = CallConverterThunk.MakeThunk( + ThunkKind.StandardToGeneric, + originalFunctionPointerFromVTable, + state.VTableMethodSignatures[i].MethodSignature, + IntPtr.Zero, // No instantiating arg for non-generic instance methods + typeArgsToUse, + Empty.Array); // No GVMs in vtables, no no method args + + if (state.VTableMethodSignatures[i].IsSealedVTableSlot) + { + // Patch the sealed vtable entry to point to the calling converter thunk + Debug.Assert(state.VTableMethodSignatures[i].VTableSlot < state.NumSealedVTableEntries && state.HalfBakedSealedVTable != IntPtr.Zero); + ((IntPtr*)state.HalfBakedSealedVTable)[state.VTableMethodSignatures[i].VTableSlot] = thunkPtr; + } + else + { + // Patch the vtable entry to point to the calling converter thunk + Debug.Assert(vtableSlotInDynamicType < numVtableSlots && vtableCells != null); + vtableCells[vtableSlotInDynamicType] = thunkPtr; + } + } + } +#endif + + // + // Returns either the registered type handle or half-baked type handle. This method should be only called + // during final phase of type building. + // + public RuntimeTypeHandle GetRuntimeTypeHandle(TypeDesc type) + { +#if DEBUG + Debug.Assert(_finalTypeBuilding); +#endif + + var rtth = type.RuntimeTypeHandle; + if (!rtth.IsNull()) + return rtth; + + rtth = type.GetTypeBuilderState().HalfBakedRuntimeTypeHandle; + Debug.Assert(!rtth.IsNull()); + return rtth; + } + + public RuntimeTypeHandle[] GetRuntimeTypeHandles(Instantiation types) + { + if (types.Length == 0) + return Array.Empty(); + + RuntimeTypeHandle[] result = new RuntimeTypeHandle[types.Length]; + for (int i = 0; i < types.Length; i++) + result[i] = GetRuntimeTypeHandle(types[i]); + return result; + } + + public static DefType GetBaseTypeUsingRuntimeTypeHandle(TypeDesc type) + { + type.RetrieveRuntimeTypeHandleIfPossible(); + unsafe + { + RuntimeTypeHandle thBaseTypeTemplate = type.RuntimeTypeHandle.ToEETypePtr()->BaseType->ToRuntimeTypeHandle(); + if (thBaseTypeTemplate.IsNull()) + return null; + + return (DefType)type.Context.ResolveRuntimeTypeHandle(thBaseTypeTemplate); + } + } + + public static DefType GetBaseTypeThatIsCorrectForMDArrays(TypeDesc type) + { + if (type.BaseType == type.Context.GetWellKnownType(WellKnownType.Array)) + { + // Use the type from the template, the metadata we have will be inaccurate for multidimensional + // arrays, as we hide the MDArray infrastructure from the metadata. + TypeDesc template = type.ComputeTemplate(false); + return GetBaseTypeUsingRuntimeTypeHandle(template ?? type); + } + + return type.BaseType; + } + + private void FinishInterfaces(TypeDesc type, TypeBuilderState state) + { + DefType[] interfaces = state.RuntimeInterfaces; + if (interfaces != null) + { + for (int i = 0; i < interfaces.Length; i++) + { + state.HalfBakedRuntimeTypeHandle.SetInterface(i, GetRuntimeTypeHandle(interfaces[i])); + } + } + } + + private unsafe void FinishTypeDictionary(TypeDesc type, TypeBuilderState state) + { + if (state.Dictionary != null) + { + // First, update the dictionary slot in the type's vtable to point to the created dictionary when applicable + Debug.Assert(state.HalfBakedDictionary != IntPtr.Zero); + + int dictionarySlot = EETypeCreator.GetDictionarySlotInVTable(type); + if (dictionarySlot >= 0) + { + state.HalfBakedRuntimeTypeHandle.SetDictionary(dictionarySlot, state.HalfBakedDictionary); + } + else + { + // Dictionary shouldn't be in the vtable of the type + Debug.Assert(!type.CanShareNormalGenericCode()); + } + + TypeLoaderLogger.WriteLine("Setting dictionary entries for type " + type.ToString() + " @ " + state.HalfBakedDictionary.LowLevelToString()); + state.Dictionary.Finish(this); + } + } + + private unsafe void FinishMethodDictionary(InstantiatedMethod method) + { + Debug.Assert(method.Dictionary != null); + + TypeLoaderLogger.WriteLine("Setting dictionary entries for method " + method.ToString() + " @ " + method.RuntimeMethodDictionary.LowLevelToString()); + method.Dictionary.Finish(this); + } + + private unsafe void FinishClassConstructor(TypeDesc type, TypeBuilderState state) + { + if (!state.HasStaticConstructor) + return; + + IntPtr canonicalClassConstructorFunctionPointer = IntPtr.Zero; // Pointer to canonical static method to serve as cctor + IntPtr exactClassConstructorFunctionPointer = IntPtr.Zero; // Exact pointer. Takes priority over canonical pointer + + if (state.TemplateType == null) + { + if (!type.HasInstantiation) + { + // Non-Generic ReadyToRun types in their current state already have their static field region setup + // with the class constructor initialized. + return; + } + else + { + // For generic types, we need to do the metadata lookup and then resolve to a function pointer. + MethodDesc staticConstructor = type.GetStaticConstructor(); + IntPtr staticCctor; + IntPtr unused1; + TypeLoaderEnvironment.MethodAddressType addressType; + if (!TypeLoaderEnvironment.TryGetMethodAddressFromMethodDesc(staticConstructor, out staticCctor, out unused1, out addressType)) + { + Environment.FailFast("Unable to find class constructor method address for type:" + type.ToString()); + } + Debug.Assert(unused1 == IntPtr.Zero); + + switch (addressType) + { + case TypeLoaderEnvironment.MethodAddressType.Exact: + // If we have an exact match, put it in the slot directly + // and return as we don't want to make this into a fat function pointer + exactClassConstructorFunctionPointer = staticCctor; + break; + + case TypeLoaderEnvironment.MethodAddressType.Canonical: + case TypeLoaderEnvironment.MethodAddressType.UniversalCanonical: + // If we have a canonical method, setup for generating a fat function pointer + canonicalClassConstructorFunctionPointer = staticCctor; + break; + + default: + Environment.FailFast("Invalid MethodAddressType during ClassConstructor discovery"); + return; + } + } + } + else if (state.ClassConstructorPointer.HasValue) + { + canonicalClassConstructorFunctionPointer = state.ClassConstructorPointer.Value; + } + else + { + // Lookup the non-GC static data for the template type, and use the class constructor context offset to locate the class constructor's + // fat pointer within the non-GC static data. + IntPtr templateTypeStaticData = TypeLoaderEnvironment.Instance.TryGetNonGcStaticFieldData(GetRuntimeTypeHandle(state.TemplateType)); + Debug.Assert(templateTypeStaticData != IntPtr.Zero); + IntPtr* templateTypeClassConstructorSlotPointer = (IntPtr*)((byte*)templateTypeStaticData + ClassConstructorOffset); + IntPtr templateTypeClassConstructorFatFunctionPointer = templateTypeClassConstructorFatFunctionPointer = *templateTypeClassConstructorSlotPointer; + + // Crack the fat function pointer into the raw class constructor method pointer and the generic type dictionary. + Debug.Assert(FunctionPointerOps.IsGenericMethodPointer(templateTypeClassConstructorFatFunctionPointer)); + GenericMethodDescriptor* templateTypeGenericMethodDescriptor = FunctionPointerOps.ConvertToGenericDescriptor(templateTypeClassConstructorFatFunctionPointer); + Debug.Assert(templateTypeGenericMethodDescriptor != null); + canonicalClassConstructorFunctionPointer = templateTypeGenericMethodDescriptor->MethodFunctionPointer; + } + + IntPtr generatedTypeStaticData = GetRuntimeTypeHandle(type).ToEETypePtr()->DynamicNonGcStaticsData; + IntPtr* generatedTypeClassConstructorSlotPointer = (IntPtr*)((byte*)generatedTypeStaticData + ClassConstructorOffset); + + if (exactClassConstructorFunctionPointer != IntPtr.Zero) + { + // We have an exact pointer, not a canonical match + // Just set the pointer and return. No need for a fat pointer + *generatedTypeClassConstructorSlotPointer = exactClassConstructorFunctionPointer; + return; + } + + // If we reach here, classConstructorFunctionPointer points at a canonical method, that needs to be converted into + // a fat function pointer so that the calli in the ClassConstructorRunner will work properly + Debug.Assert(canonicalClassConstructorFunctionPointer != IntPtr.Zero); + + // Use the template type's class constructor method pointer and this type's generic type dictionary to generate a new fat pointer, + // and save that fat pointer back to this type's class constructor context offset within the non-GC static data. + IntPtr instantiationArgument = GetRuntimeTypeHandle(type).ToIntPtr(); + IntPtr generatedTypeClassConstructorFatFunctionPointer = FunctionPointerOps.GetGenericMethodFunctionPointer(canonicalClassConstructorFunctionPointer, instantiationArgument); + *generatedTypeClassConstructorSlotPointer = generatedTypeClassConstructorFatFunctionPointer; + } + + private void CopyDictionaryFromTypeToAppropriateSlotInDerivedType(TypeDesc baseType, TypeBuilderState derivedTypeState) + { + var baseTypeState = baseType.GetOrCreateTypeBuilderState(); + + if (baseTypeState.HasDictionaryInVTable) + { + RuntimeTypeHandle baseTypeHandle = GetRuntimeTypeHandle(baseType); + + // If the basetype is currently being created by the TypeBuilder, we need to get its dictionary pointer from the + // TypeBuilder state (at this point, the dictionary has not yet been set on the baseTypeHandle). If + // the basetype is not a dynamic type, or has previously been dynamically allocated in the past, the TypeBuilder + // state will have a null dictionary pointer, in which case we need to read it directly from the basetype's vtable + IntPtr dictionaryEntry = baseTypeState.HalfBakedDictionary; + if (dictionaryEntry == IntPtr.Zero) + dictionaryEntry = baseTypeHandle.GetDictionary(); + Debug.Assert(dictionaryEntry != IntPtr.Zero); + + // Compute the vtable slot for the dictionary entry to set + int dictionarySlot = EETypeCreator.GetDictionarySlotInVTable(baseType); + Debug.Assert(dictionarySlot >= 0); + + derivedTypeState.HalfBakedRuntimeTypeHandle.SetDictionary(dictionarySlot, dictionaryEntry); + TypeLoaderLogger.WriteLine("Setting basetype " + baseType.ToString() + " dictionary on type " + derivedTypeState.TypeBeingBuilt.ToString()); + } + } + + private void FinishBaseTypeAndDictionaries(TypeDesc type, TypeBuilderState state) + { + DefType baseType = GetBaseTypeThatIsCorrectForMDArrays(type); + state.HalfBakedRuntimeTypeHandle.SetBaseType(baseType == null ? default(RuntimeTypeHandle) : GetRuntimeTypeHandle(baseType)); + + if (baseType == null) + return; + + // Update every dictionary in type hierarchy with copy from base type + while (baseType != null) + { + CopyDictionaryFromTypeToAppropriateSlotInDerivedType(baseType, state); + baseType = baseType.BaseType; + } + } + + private void FinishRuntimeType(TypeDesc type) + { + TypeLoaderLogger.WriteLine("Finishing type " + type.ToString() + " ..."); + + var state = type.GetTypeBuilderState(); + + if (type is DefType) + { + DefType typeAsDefType = (DefType)type; + + if (type.HasInstantiation) + { + // Type definitions don't need any further finishing once created by the EETypeCreator + if (type.IsTypeDefinition) + return; + + state.HalfBakedRuntimeTypeHandle.SetGenericDefinition(GetRuntimeTypeHandle(typeAsDefType.GetTypeDefinition())); + Instantiation instantiation = typeAsDefType.Instantiation; + state.HalfBakedRuntimeTypeHandle.SetGenericArity((uint)instantiation.Length); + for (int argIndex = 0; argIndex < instantiation.Length; argIndex++) + { + state.HalfBakedRuntimeTypeHandle.SetGenericArgument(argIndex, GetRuntimeTypeHandle(instantiation[argIndex])); + if (state.GenericVarianceFlags != null) + { + Debug.Assert(state.GenericVarianceFlags.Length == instantiation.Length); + state.HalfBakedRuntimeTypeHandle.SetGenericVariance(argIndex, state.GenericVarianceFlags[argIndex]); + } + } + } + + FinishBaseTypeAndDictionaries(type, state); + + FinishInterfaces(type, state); + + FinishTypeDictionary(type, state); + + FinishClassConstructor(type, state); + +#if FEATURE_UNIVERSAL_GENERICS + // For types that were allocated from universal canonical templates, patch their vtables with + // pointers to calling convention conversion thunks + if (state.TemplateType != null && state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + FinishVTableCallingConverterThunks(type, state); +#endif + } + else if (type is ParameterizedType) + { + if (type is ArrayType) + { + ArrayType typeAsSzArrayType = (ArrayType)type; + + state.HalfBakedRuntimeTypeHandle.SetRelatedParameterType(GetRuntimeTypeHandle(typeAsSzArrayType.ElementType)); + + state.HalfBakedRuntimeTypeHandle.SetComponentSize(state.ComponentSize.Value); + + FinishInterfaces(type, state); + + if (typeAsSzArrayType.IsSzArray && !typeAsSzArrayType.ElementType.IsPointer) + { + FinishTypeDictionary(type, state); + +#if FEATURE_UNIVERSAL_GENERICS + // For types that were allocated from universal canonical templates, patch their vtables with + // pointers to calling convention conversion thunks + if (state.TemplateType != null && state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + FinishVTableCallingConverterThunks(type, state); +#endif + } + } + else if (type is PointerType) + { + state.HalfBakedRuntimeTypeHandle.SetRelatedParameterType(GetRuntimeTypeHandle(((PointerType)type).ParameterType)); + + // Nothing else to do for pointer types + } + else if (type is ByRefType) + { + state.HalfBakedRuntimeTypeHandle.SetRelatedParameterType(GetRuntimeTypeHandle(((ByRefType)type).ParameterType)); + + // We used a pointer type for the template because they're similar enough. Adjust this to be a ByRef. + unsafe + { + Debug.Assert(state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->ParameterizedTypeShape == ParameterizedTypeShapeConstants.Pointer); + state.HalfBakedRuntimeTypeHandle.SetParameterizedTypeShape(ParameterizedTypeShapeConstants.ByRef); + Debug.Assert(state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->ElementType == EETypeElementType.Pointer); + state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->Flags = EETypeBuilderHelpers.ComputeFlags(type); + Debug.Assert(state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->ElementType == EETypeElementType.ByRef); + } + } + } + else + { + Debug.Assert(false); + } + } + + private IEnumerable TypesToRegister() + { + for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) + { + DefType typeAsDefType = _typesThatNeedTypeHandles[i] as DefType; + if (typeAsDefType == null) + continue; + + if (typeAsDefType.HasInstantiation && !typeAsDefType.IsTypeDefinition) + { + yield return new TypeEntryToRegister + { + GenericTypeEntry = new GenericTypeEntry + { + _genericTypeDefinitionHandle = GetRuntimeTypeHandle(typeAsDefType.GetTypeDefinition()), + _genericTypeArgumentHandles = GetRuntimeTypeHandles(typeAsDefType.Instantiation), + _instantiatedTypeHandle = typeAsDefType.GetTypeBuilderState().HalfBakedRuntimeTypeHandle + } + }; + } + else + { + yield return new TypeEntryToRegister + { + MetadataDefinitionType = (MetadataType)typeAsDefType + }; + } + } + } + + private IEnumerable MethodsToRegister() + { + for (int i = 0; i < _methodsThatNeedDictionaries.Count; i++) + { + InstantiatedMethod method = _methodsThatNeedDictionaries[i]; + yield return new GenericMethodEntry + { + _declaringTypeHandle = GetRuntimeTypeHandle(method.OwningType), + _genericMethodArgumentHandles = GetRuntimeTypeHandles(method.Instantiation), + _methodNameAndSignature = method.NameAndSignature, + _methodDictionary = method.RuntimeMethodDictionary + }; + } + } + + private void RegisterGenericTypesAndMethods() + { + int typesToRegisterCount = 0; + for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) + { + DefType typeAsDefType; + if ((typeAsDefType = _typesThatNeedTypeHandles[i] as DefType) != null) + typesToRegisterCount++; + } + + DynamicGenericsRegistrationData registrationData = new DynamicGenericsRegistrationData + { + TypesToRegisterCount = typesToRegisterCount, + TypesToRegister = (typesToRegisterCount != 0) ? TypesToRegister() : null, + MethodsToRegisterCount = _methodsThatNeedDictionaries.Count, + MethodsToRegister = (_methodsThatNeedDictionaries.Count != 0) ? MethodsToRegister() : null, + }; + TypeLoaderEnvironment.Instance.RegisterDynamicGenericTypesAndMethods(registrationData); + } + + /// + /// Publish generic type / method information to the data buffer read by the debugger. This supports + /// debugging dynamically created types / methods + /// + private void RegisterDebugDataForTypesAndMethods() + { + for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) + { + DefType typeAsDefType; + if ((typeAsDefType = _typesThatNeedTypeHandles[i] as DefType) != null) + { + SerializedDebugData.RegisterDebugDataForType(this, typeAsDefType, typeAsDefType.GetTypeBuilderState()); + } + } + + for (int i = 0; i < _methodsThatNeedDictionaries.Count; i++) + { + SerializedDebugData.RegisterDebugDataForMethod(this, _methodsThatNeedDictionaries[i]); + } + } + + private void FinishTypeAndMethodBuilding() + { + // Once we start allocating EETypes and dictionaries, the only accepted failure is OOM. + // TODO: Error handling - on retry, restart where we failed last time? The current implementation is leaking on OOM. + +#if DEBUG + _finalTypeBuilding = true; +#endif + + // At this point we know all types that need EETypes. Allocate all EETypes so that we can start building + // their contents. + for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) + { + AllocateRuntimeType(_typesThatNeedTypeHandles[i]); + } + + for (int i = 0; i < _methodsThatNeedDictionaries.Count; i++) + { + AllocateRuntimeMethodDictionary(_methodsThatNeedDictionaries[i]); + } + + // Do not add more type phases here. Instead, read the required information from the TypeDesc or TypeBuilderState. + + // Fill in content of all EETypes + for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) + { + FinishRuntimeType(_typesThatNeedTypeHandles[i]); + } + + for (int i = 0; i < _methodsThatNeedDictionaries.Count; i++) + { + FinishMethodDictionary(_methodsThatNeedDictionaries[i]); + } + + RegisterDebugDataForTypesAndMethods(); + + int newArrayTypesCount = 0; + int newPointerTypesCount = 0; + int newByRefTypesCount = 0; + int[] mdArrayNewTypesCount = null; + + for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) + { + ParameterizedType typeAsParameterizedType = _typesThatNeedTypeHandles[i] as ParameterizedType; + if (typeAsParameterizedType == null) + continue; + + if (typeAsParameterizedType.IsSzArray) + newArrayTypesCount++; + else if (typeAsParameterizedType.IsPointer) + newPointerTypesCount++; + else if (typeAsParameterizedType.IsByRef) + newByRefTypesCount++; + else if (typeAsParameterizedType.IsMdArray) + { + if (mdArrayNewTypesCount == null) + mdArrayNewTypesCount = new int[MDArray.MaxRank + 1]; + mdArrayNewTypesCount[((ArrayType)typeAsParameterizedType).Rank]++; + } + } + // Reserve space in array/pointer cache's so that the actual adding can be fault-free. + var szArrayCache = TypeSystemContext.GetArrayTypesCache(false, -1); + szArrayCache.Reserve(szArrayCache.Count + newArrayTypesCount); + + // + if (mdArrayNewTypesCount != null) + { + for (int i = 0; i < mdArrayNewTypesCount.Length; i++) + { + if (mdArrayNewTypesCount[i] == 0) + continue; + + var mdArrayCache = TypeSystemContext.GetArrayTypesCache(true, i); + mdArrayCache.Reserve(mdArrayCache.Count + mdArrayNewTypesCount[i]); + } + } + + TypeSystemContext.PointerTypesCache.Reserve(TypeSystemContext.PointerTypesCache.Count + newPointerTypesCount); + TypeSystemContext.ByRefTypesCache.Reserve(TypeSystemContext.ByRefTypesCache.Count + newByRefTypesCount); + + // Finally, register all generic types and methods atomically with the runtime + RegisterGenericTypesAndMethods(); + + + for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) + { + _typesThatNeedTypeHandles[i].SetRuntimeTypeHandleUnsafe(_typesThatNeedTypeHandles[i].GetTypeBuilderState().HalfBakedRuntimeTypeHandle); + + TypeLoaderLogger.WriteLine("Successfully Registered type " + _typesThatNeedTypeHandles[i].ToString() + "."); + } + + // Save all constructed array and pointer types to the types cache + for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) + { + ParameterizedType typeAsParameterizedType = _typesThatNeedTypeHandles[i] as ParameterizedType; + if (typeAsParameterizedType == null) + continue; + + Debug.Assert(!typeAsParameterizedType.RuntimeTypeHandle.IsNull()); + Debug.Assert(!typeAsParameterizedType.ParameterType.RuntimeTypeHandle.IsNull()); + + if (typeAsParameterizedType.IsMdArray) + TypeSystemContext.GetArrayTypesCache(true, ((ArrayType)typeAsParameterizedType).Rank).AddOrGetExisting(typeAsParameterizedType.RuntimeTypeHandle); + else if (typeAsParameterizedType.IsSzArray) + TypeSystemContext.GetArrayTypesCache(false, -1).AddOrGetExisting(typeAsParameterizedType.RuntimeTypeHandle); + else if (typeAsParameterizedType.IsByRef) + { + unsafe + { + Debug.Assert(typeAsParameterizedType.RuntimeTypeHandle.ToEETypePtr()->IsByRefType); + } + TypeSystemContext.ByRefTypesCache.AddOrGetExisting(typeAsParameterizedType.RuntimeTypeHandle); + } + else + { + Debug.Assert(typeAsParameterizedType is PointerType); + unsafe + { + Debug.Assert(typeAsParameterizedType.RuntimeTypeHandle.ToEETypePtr()->IsPointerType); + } + TypeSystemContext.PointerTypesCache.AddOrGetExisting(typeAsParameterizedType.RuntimeTypeHandle); + } + } + } + + internal void BuildType(TypeDesc type) + { + TypeLoaderLogger.WriteLine("Dynamically allocating new type for " + type.ToString()); + + // Construct a new type along with all the dependencies that are needed to create interface lists, + // generic dictionaries, etc. + + // Start by collecting all dependencies we need to create in order to create this type. + PrepareType(type); + + // Process the pending types + ProcessTypesNeedingPreparation(); + + FinishTypeAndMethodBuilding(); + } + + internal bool TryComputeFieldOffset(DefType declaringType, uint fieldOrdinal, out int fieldOffset) + { + fieldOffset = int.MinValue; + + TypeLoaderLogger.WriteLine("Computing offset of field #" + fieldOrdinal.LowLevelToString() + " on type " + declaringType.ToString()); + + // Get the computed field offset result + LayoutInt layoutFieldOffset = declaringType.GetFieldByNativeLayoutOrdinal(fieldOrdinal).Offset; + if (layoutFieldOffset.IsIndeterminate) + { + fieldOffset = 0; + return false; + } + fieldOffset = layoutFieldOffset.AsInt; + return true; + } + + private void BuildMethod(InstantiatedMethod method) + { + TypeLoaderLogger.WriteLine("Dynamically allocating new method instantiation for " + method.ToString()); + + // Start by collecting all dependencies we need to create in order to create this method. + PrepareMethod(method); + + // Process the pending types + ProcessTypesNeedingPreparation(); + + FinishTypeAndMethodBuilding(); + } + + private static DefType GetExactDeclaringType(DefType srcDefType, DefType dstDefType) + { + while (srcDefType != null) + { + if (srcDefType.HasSameTypeDefinition(dstDefType)) + return srcDefType; + + srcDefType = srcDefType.BaseType; + } + + Debug.Assert(false); + return null; + } + + // + // This method is used by the lazy generic lookup. It resolves the signature of the runtime artifact in the given instantiation context. + // + private unsafe IntPtr BuildGenericLookupTarget(TypeSystemContext typeSystemContext, IntPtr context, IntPtr signature, out IntPtr auxResult) + { + TypeLoaderLogger.WriteLine("BuildGenericLookupTarget for " + context.LowLevelToString() + "/" + signature.LowLevelToString()); + + TypeManagerHandle typeManager; + NativeReader reader; + uint offset; + + // The first is a pointer that points to the TypeManager indirection cell. + // The second is the offset into the native layout info blob in that TypeManager, where the native signature is encoded. + IntPtr** lazySignature = (IntPtr**)signature.ToPointer(); + typeManager = new TypeManagerHandle(lazySignature[0][0]); + offset = checked((uint)new IntPtr(lazySignature[1]).ToInt32()); + reader = TypeLoaderEnvironment.Instance.GetNativeLayoutInfoReader(typeManager); + + NativeParser parser = new NativeParser(reader, offset); + + GenericContextKind contextKind = (GenericContextKind)parser.GetUnsigned(); + + NativeFormatModuleInfo moduleInfo = ModuleList.Instance.GetModuleInfoByHandle(typeManager); + + NativeLayoutInfoLoadContext nlilContext = new NativeLayoutInfoLoadContext(); + nlilContext._module = moduleInfo; + nlilContext._typeSystemContext = typeSystemContext; + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + NativeFormatMetadataUnit metadataUnit = null; + + if (moduleInfo.ModuleType == ModuleType.ReadyToRun) + metadataUnit = typeSystemContext.ResolveMetadataUnit(moduleInfo); +#endif + + if ((contextKind & GenericContextKind.FromMethodHiddenArg) != 0) + { + RuntimeTypeHandle declaringTypeHandle; + MethodNameAndSignature nameAndSignature; + RuntimeTypeHandle[] genericMethodArgHandles; + bool success = TypeLoaderEnvironment.Instance.TryGetGenericMethodComponents(context, out declaringTypeHandle, out nameAndSignature, out genericMethodArgHandles); + Debug.Assert(success); + + if (RuntimeAugments.IsGenericType(declaringTypeHandle)) + { + DefType declaringType = (DefType)typeSystemContext.ResolveRuntimeTypeHandle(declaringTypeHandle); + nlilContext._typeArgumentHandles = declaringType.Instantiation; + } + + nlilContext._methodArgumentHandles = typeSystemContext.ResolveRuntimeTypeHandles(genericMethodArgHandles); + } + else + { + TypeDesc typeContext = typeSystemContext.ResolveRuntimeTypeHandle(RuntimeAugments.CreateRuntimeTypeHandle(context)); + + if (typeContext is DefType) + { + nlilContext._typeArgumentHandles = ((DefType)typeContext).Instantiation; + } + else if (typeContext is ArrayType) + { + nlilContext._typeArgumentHandles = new Instantiation(new TypeDesc[] { ((ArrayType)typeContext).ElementType }); + } + else + { + Debug.Assert(false); + } + + if ((contextKind & GenericContextKind.HasDeclaringType) != 0) + { + // No need to deal with arrays - arrays can't have declaring type + + TypeDesc declaringType; + + if (moduleInfo.ModuleType == ModuleType.Eager) + { + declaringType = nlilContext.GetType(ref parser); + } + else + { + Debug.Assert(moduleInfo.ModuleType == ModuleType.ReadyToRun); +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + uint typeToken = parser.GetUnsigned(); + declaringType = metadataUnit.GetType(((int)typeToken).AsHandle()); +#else + Environment.FailFast("Ready to Run module type?"); + declaringType = null; +#endif + } + + DefType actualContext = GetExactDeclaringType((DefType)typeContext, (DefType)declaringType); + + nlilContext._typeArgumentHandles = actualContext.Instantiation; + } + } + + if ((contextKind & GenericContextKind.NeedsUSGContext) != 0) + { + IntPtr genericDictionary; + auxResult = IntPtr.Zero; + + // There is a cache in place so that this function doesn't get called much, but we still need a registration store, + // so we don't leak allocated contexts + if (TypeLoaderEnvironment.Instance.TryLookupConstructedLazyDictionaryForContext(context, signature, out genericDictionary)) + { + return genericDictionary; + } + + GenericTypeDictionary ucgDict; + + if (moduleInfo.ModuleType == ModuleType.Eager) + { + ucgDict = new GenericTypeDictionary(GenericDictionaryCell.BuildDictionary(this, nlilContext, parser)); + } + else + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + Debug.Assert(moduleInfo.ModuleType == ModuleType.ReadyToRun); + FixupCellMetadataResolver metadataResolver = new FixupCellMetadataResolver(metadataUnit, nlilContext); + ucgDict = new GenericTypeDictionary(GenericDictionaryCell.BuildDictionaryFromMetadataTokensAndContext(this, parser, metadataUnit, metadataResolver)); +#else + Environment.FailFast("Ready to Run module type?"); + ucgDict = null; +#endif + } + genericDictionary = ucgDict.Allocate(); + + // Process the pending types + ProcessTypesNeedingPreparation(); + + FinishTypeAndMethodBuilding(); + + ucgDict.Finish(this); + + TypeLoaderEnvironment.Instance.RegisterConstructedLazyDictionaryForContext(context, signature, genericDictionary); + return genericDictionary; + } + else + { + GenericDictionaryCell cell; + + if (moduleInfo.ModuleType == ModuleType.Eager) + { + cell = GenericDictionaryCell.ParseAndCreateCell( + nlilContext, + ref parser); + } + else + { + Debug.Assert(moduleInfo.ModuleType == ModuleType.ReadyToRun); +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + MetadataFixupKind fixupKind = (MetadataFixupKind)parser.GetUInt8(); + Internal.Metadata.NativeFormat.Handle token = parser.GetUnsigned().AsHandle(); + Internal.Metadata.NativeFormat.Handle token2 = default(Internal.Metadata.NativeFormat.Handle); + + switch (fixupKind) + { + case MetadataFixupKind.GenericConstrainedMethod: + case MetadataFixupKind.NonGenericConstrainedMethod: + case MetadataFixupKind.NonGenericDirectConstrainedMethod: + token2 = parser.GetUnsigned().AsHandle(); + break; + } + + FixupCellMetadataResolver resolver = new FixupCellMetadataResolver(metadataUnit, nlilContext); + cell = GenericDictionaryCell.CreateCellFromFixupKindAndToken(fixupKind, resolver, token, token2); +#else + Environment.FailFast("Ready to Run module type?"); + cell = null; +#endif + } + + cell.Prepare(this); + + // Process the pending types + ProcessTypesNeedingPreparation(); + + FinishTypeAndMethodBuilding(); + + IntPtr dictionaryCell = cell.CreateLazyLookupCell(this, out auxResult); + + return dictionaryCell; + } + } + + // + // This method is used to build the floating portion of a generic dictionary. + // + private unsafe IntPtr BuildFloatingDictionary(TypeSystemContext typeSystemContext, IntPtr context, bool isTypeContext, IntPtr fixedDictionary, out bool isNewlyAllocatedDictionary) + { + isNewlyAllocatedDictionary = true; + + NativeParser nativeLayoutParser; + NativeLayoutInfoLoadContext nlilContext; + + if (isTypeContext) + { + TypeDesc typeContext = typeSystemContext.ResolveRuntimeTypeHandle(*(RuntimeTypeHandle*)&context); + + TypeLoaderLogger.WriteLine("Building floating dictionary layout for type " + typeContext.ToString() + "..."); + + // We should only perform updates to floating dictionaries for types that share normal canonical code + Debug.Assert(typeContext.CanShareNormalGenericCode()); + + // Computing the template will throw if no template is found. + typeContext.ComputeTemplate(); + + TypeBuilderState state = typeContext.GetOrCreateTypeBuilderState(); + nativeLayoutParser = state.GetParserForNativeLayoutInfo(); + nlilContext = state.NativeLayoutInfo.LoadContext; + } + else + { + RuntimeTypeHandle declaringTypeHandle; + MethodNameAndSignature nameAndSignature; + RuntimeTypeHandle[] genericMethodArgHandles; + bool success = TypeLoaderEnvironment.Instance.TryGetGenericMethodComponents(context, out declaringTypeHandle, out nameAndSignature, out genericMethodArgHandles); + Debug.Assert(success); + + DefType declaringType = (DefType)typeSystemContext.ResolveRuntimeTypeHandle(declaringTypeHandle); + InstantiatedMethod methodContext = (InstantiatedMethod)typeSystemContext.ResolveGenericMethodInstantiation( + false, + declaringType, + nameAndSignature, + typeSystemContext.ResolveRuntimeTypeHandles(genericMethodArgHandles), + IntPtr.Zero, + false); + + TypeLoaderLogger.WriteLine("Building floating dictionary layout for method " + methodContext.ToString() + "..."); + + // We should only perform updates to floating dictionaries for gemeric methods that share normal canonical code + Debug.Assert(!methodContext.IsNonSharableMethod); + + uint nativeLayoutInfoToken; + NativeFormatModuleInfo nativeLayoutModule; + MethodDesc templateMethod = (new TemplateLocator()).TryGetGenericMethodTemplate(methodContext, out nativeLayoutModule, out nativeLayoutInfoToken); + if (templateMethod == null) + throw new TypeBuilder.MissingTemplateException(); + + NativeReader nativeLayoutInfoReader = TypeLoaderEnvironment.Instance.GetNativeLayoutInfoReader(nativeLayoutModule.Handle); + + nativeLayoutParser = new NativeParser(nativeLayoutInfoReader, nativeLayoutInfoToken); + nlilContext = new NativeLayoutInfoLoadContext + { + _typeSystemContext = methodContext.Context, + _typeArgumentHandles = methodContext.OwningType.Instantiation, + _methodArgumentHandles = methodContext.Instantiation, + _module = nativeLayoutModule + }; + } + + NativeParser dictionaryLayoutParser = nativeLayoutParser.GetParserForBagElementKind(BagElementKind.DictionaryLayout); + if (dictionaryLayoutParser.IsNull) + return IntPtr.Zero; + + int floatingVersionCellIndex, floatingVersionInLayout; + GenericDictionaryCell[] floatingCells = GenericDictionaryCell.BuildFloatingDictionary(this, nlilContext, dictionaryLayoutParser, out floatingVersionCellIndex, out floatingVersionInLayout); + if (floatingCells == null) + return IntPtr.Zero; + + // If the floating section is already constructed, then return. This means we are beaten by another thread. + if (*((IntPtr*)fixedDictionary) != IntPtr.Zero) + { + isNewlyAllocatedDictionary = false; + return *((IntPtr*)fixedDictionary); + } + + GenericTypeDictionary floatingDict = new GenericTypeDictionary(floatingCells); + + IntPtr result = floatingDict.Allocate(); + + ProcessTypesNeedingPreparation(); + + FinishTypeAndMethodBuilding(); + + floatingDict.Finish(this); + + return result; + } + + public static bool TryBuildGenericType(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle) + { + Debug.Assert(!genericTypeDefinitionHandle.IsNull() && genericTypeArgumentHandles != null && genericTypeArgumentHandles.Length > 0); + + try + { + TypeSystemContext context = TypeSystemContextFactory.Create(); + + DefType genericDef = (DefType)context.ResolveRuntimeTypeHandle(genericTypeDefinitionHandle); + Instantiation genericArgs = context.ResolveRuntimeTypeHandles(genericTypeArgumentHandles); + DefType typeBeingLoaded = context.ResolveGenericInstantiation(genericDef, genericArgs); + + new TypeBuilder().BuildType(typeBeingLoaded); + + runtimeTypeHandle = typeBeingLoaded.RuntimeTypeHandle; + Debug.Assert(!runtimeTypeHandle.IsNull()); + + // Recycle the context only if we succesfully built the type. The state may be partially initialized otherwise. + TypeSystemContextFactory.Recycle(context); + + return true; + } + catch (MissingTemplateException) + { + runtimeTypeHandle = default(RuntimeTypeHandle); + return false; + } + } + + public static bool TryBuildArrayType(RuntimeTypeHandle elementTypeHandle, bool isMdArray, int rank, out RuntimeTypeHandle arrayTypeHandle) + { + try + { + TypeSystemContext context = TypeSystemContextFactory.Create(); + + TypeDesc elementType = context.ResolveRuntimeTypeHandle(elementTypeHandle); + ArrayType arrayType = (ArrayType)context.GetArrayType(elementType, !isMdArray ? -1 : rank); + + new TypeBuilder().BuildType(arrayType); + + arrayTypeHandle = arrayType.RuntimeTypeHandle; + Debug.Assert(!arrayTypeHandle.IsNull()); + + // Recycle the context only if we succesfully built the type. The state may be partially initialized otherwise. + TypeSystemContextFactory.Recycle(context); + + return true; + } + catch (MissingTemplateException) + { + arrayTypeHandle = default(RuntimeTypeHandle); + return false; + } + } + + public static bool TryBuildPointerType(RuntimeTypeHandle pointeeTypeHandle, out RuntimeTypeHandle pointerTypeHandle) + { + if (!TypeSystemContext.PointerTypesCache.TryGetValue(pointeeTypeHandle, out pointerTypeHandle)) + { + TypeSystemContext context = TypeSystemContextFactory.Create(); + TypeDesc pointerType = context.GetPointerType(context.ResolveRuntimeTypeHandle(pointeeTypeHandle)); + pointerTypeHandle = EETypeCreator.CreatePointerEEType((uint)pointerType.GetHashCode(), pointeeTypeHandle, pointerType); + unsafe + { + Debug.Assert(pointerTypeHandle.ToEETypePtr()->IsPointerType); + } + TypeSystemContext.PointerTypesCache.AddOrGetExisting(pointerTypeHandle); + + // Recycle the context only if we succesfully built the type. The state may be partially initialized otherwise. + TypeSystemContextFactory.Recycle(context); + } + + return true; + } + + public static bool TryBuildByRefType(RuntimeTypeHandle pointeeTypeHandle, out RuntimeTypeHandle byRefTypeHandle) + { + if (!TypeSystemContext.ByRefTypesCache.TryGetValue(pointeeTypeHandle, out byRefTypeHandle)) + { + TypeSystemContext context = TypeSystemContextFactory.Create(); + TypeDesc byRefType = context.GetByRefType(context.ResolveRuntimeTypeHandle(pointeeTypeHandle)); + byRefTypeHandle = EETypeCreator.CreateByRefEEType((uint)byRefType.GetHashCode(), pointeeTypeHandle, byRefType); + unsafe + { + Debug.Assert(byRefTypeHandle.ToEETypePtr()->IsByRefType); + } + TypeSystemContext.ByRefTypesCache.AddOrGetExisting(byRefTypeHandle); + + // Recycle the context only if we succesfully built the type. The state may be partially initialized otherwise. + TypeSystemContextFactory.Recycle(context); + } + + return true; + } + + public static bool TryBuildGenericMethod(RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle[] genericMethodArgHandles, MethodNameAndSignature methodNameAndSignature, out IntPtr methodDictionary) + { + TypeSystemContext context = TypeSystemContextFactory.Create(); + + DefType declaringType = (DefType)context.ResolveRuntimeTypeHandle(declaringTypeHandle); + InstantiatedMethod methodBeingLoaded = (InstantiatedMethod)context.ResolveGenericMethodInstantiation(false, declaringType, methodNameAndSignature, context.ResolveRuntimeTypeHandles(genericMethodArgHandles), IntPtr.Zero, false); + + bool success = TryBuildGenericMethod(methodBeingLoaded, out methodDictionary); + + // Recycle the context only if we succesfully built the method. The state may be partially initialized otherwise. + if (success) + TypeSystemContextFactory.Recycle(context); + + return success; + } + + internal static bool TryBuildGenericMethod(InstantiatedMethod methodBeingLoaded, out IntPtr methodDictionary) + { + try + { + new TypeBuilder().BuildMethod(methodBeingLoaded); + + methodDictionary = methodBeingLoaded.RuntimeMethodDictionary; + Debug.Assert(methodDictionary != IntPtr.Zero); + + return true; + } + catch (MissingTemplateException) + { + methodDictionary = IntPtr.Zero; + return false; + } + } + + private void ResolveSingleCell_Worker(GenericDictionaryCell cell, out IntPtr fixupResolution) + { + cell.Prepare(this); + + // Process the pending types + ProcessTypesNeedingPreparation(); + FinishTypeAndMethodBuilding(); + + // At this stage the pointer we need is accessible via a call to Create on the prepared cell + fixupResolution = cell.Create(this); + } + + private void ResolveMultipleCells_Worker(GenericDictionaryCell[] cells, out IntPtr[] fixups) + { + foreach (var cell in cells) + { + cell.Prepare(this); + } + + // Process the pending types + ProcessTypesNeedingPreparation(); + FinishTypeAndMethodBuilding(); + + // At this stage the pointer we need is accessible via a call to Create on the prepared cell + fixups = new IntPtr[cells.Length]; + for (int i = 0; i < fixups.Length; i++) + fixups[i] = cells[i].Create(this); + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + private void ResolveSingleMetadataFixup(NativeFormatMetadataUnit module, Handle token, MetadataFixupKind fixupKind, out IntPtr fixupResolution) + { + FixupCellMetadataResolver metadata = new FixupCellMetadataResolver(module); + + // Allocate a cell object to represent the fixup, and prepare it + GenericDictionaryCell cell = GenericDictionaryCell.CreateCellFromFixupKindAndToken(fixupKind, metadata, token, default(Handle)); + ResolveSingleCell_Worker(cell, out fixupResolution); + } + + public static bool TryResolveSingleMetadataFixup(NativeFormatModuleInfo module, int metadataToken, MetadataFixupKind fixupKind, out IntPtr fixupResolution) + { + TypeSystemContext context = TypeSystemContextFactory.Create(); + + NativeFormatMetadataUnit metadataUnit = context.ResolveMetadataUnit(module); + new TypeBuilder().ResolveSingleMetadataFixup(metadataUnit, metadataToken.AsHandle(), fixupKind, out fixupResolution); + + TypeSystemContextFactory.Recycle(context); + + return true; + } + + public static void ResolveSingleTypeDefinition(QTypeDefinition qTypeDefinition, out IntPtr typeHandle) + { + TypeSystemContext context = TypeSystemContextFactory.Create(); + + TypeDesc type = context.GetTypeDescFromQHandle(qTypeDefinition); + GenericDictionaryCell cell = GenericDictionaryCell.CreateTypeHandleCell(type); + + new TypeBuilder().ResolveSingleCell_Worker(cell, out typeHandle); + + TypeSystemContextFactory.Recycle(context); + } +#endif + + internal static void ResolveSingleCell(GenericDictionaryCell cell, out IntPtr fixupResolution) + { + new TypeBuilder().ResolveSingleCell_Worker(cell, out fixupResolution); + } + + public static void ResolveMultipleCells(GenericDictionaryCell [] cells, out IntPtr[] fixups) + { + new TypeBuilder().ResolveMultipleCells_Worker(cells, out fixups); + } + + public static IntPtr BuildGenericLookupTarget(IntPtr typeContext, IntPtr signature, out IntPtr auxResult) + { + try + { + TypeSystemContext context = TypeSystemContextFactory.Create(); + + IntPtr ret = new TypeBuilder().BuildGenericLookupTarget(context, typeContext, signature, out auxResult); + + TypeSystemContextFactory.Recycle(context); + + return ret; + } + catch (MissingTemplateException e) + { + // This should not ever happen. The static compiler should ensure that the templates are always + // available for types and methods referenced by lazy dictionary lookups + Environment.FailFast("MissingTemplateException thrown during lazy generic lookup", e); + + auxResult = IntPtr.Zero; + return IntPtr.Zero; + } + } + + public static bool TryGetFieldOffset(RuntimeTypeHandle declaringTypeHandle, uint fieldOrdinal, out int fieldOffset) + { + try + { + TypeSystemContext context = TypeSystemContextFactory.Create(); + + DefType declaringType = (DefType)context.ResolveRuntimeTypeHandle(declaringTypeHandle); + Debug.Assert(declaringType.HasInstantiation); + + bool success = new TypeBuilder().TryComputeFieldOffset(declaringType, fieldOrdinal, out fieldOffset); + + TypeSystemContextFactory.Recycle(context); + + return success; + } + catch (MissingTemplateException) + { + fieldOffset = int.MinValue; + return false; + } + } + + internal static bool TryGetDelegateInvokeMethodSignature(RuntimeTypeHandle delegateTypeHandle, out RuntimeSignature signature) + { + signature = default(RuntimeSignature); + bool success = false; + + TypeSystemContext context = TypeSystemContextFactory.Create(); + + DefType delegateType = (DefType)context.ResolveRuntimeTypeHandle(delegateTypeHandle); + Debug.Assert(delegateType.HasInstantiation); + + NativeLayoutInfoLoadContext loadContext; + NativeLayoutInfo universalLayoutInfo; + NativeParser parser = delegateType.GetOrCreateTypeBuilderState().GetParserForUniversalNativeLayoutInfo(out loadContext, out universalLayoutInfo); + if (!parser.IsNull) + { + NativeParser sigParser = parser.GetParserForBagElementKind(BagElementKind.DelegateInvokeSignature); + if (!sigParser.IsNull) + { + signature = RuntimeSignature.CreateFromNativeLayoutSignature(universalLayoutInfo.Module.Handle, sigParser.Offset); + success = true; + } + } + + TypeSystemContextFactory.Recycle(context); + + return success; + } + + // + // This method is used to build the floating portion of a generic dictionary. + // + internal static IntPtr TryBuildFloatingDictionary(IntPtr context, bool isTypeContext, IntPtr fixedDictionary, out bool isNewlyAllocatedDictionary) + { + isNewlyAllocatedDictionary = true; + + try + { + TypeSystemContext typeSystemContext = TypeSystemContextFactory.Create(); + + IntPtr ret = new TypeBuilder().BuildFloatingDictionary(typeSystemContext, context, isTypeContext, fixedDictionary, out isNewlyAllocatedDictionary); + + TypeSystemContextFactory.Recycle(typeSystemContext); + + return ret; + } + catch (MissingTemplateException e) + { + // This should not ever happen. The static compiler should ensure that the templates are always + // available for types and methods that have floating dictionaries + Environment.FailFast("MissingTemplateException thrown during dictionary update", e); + + return IntPtr.Zero; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs new file mode 100644 index 00000000000000..0aa63813f9a8a0 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs @@ -0,0 +1,1100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Text; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.Metadata.NativeFormat; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.TypeSystem.NativeFormat; +using Internal.TypeSystem.NoMetadata; + +namespace Internal.Runtime.TypeLoader +{ + internal struct NativeLayoutInfo + { + public uint Offset; + public NativeFormatModuleInfo Module; + public NativeReader Reader; + public NativeLayoutInfoLoadContext LoadContext; + } + + // + // TypeBuilder per-Type state. It is attached to each TypeDesc that gets involved in type building. + // + internal class TypeBuilderState + { + internal class VTableLayoutInfo + { + public uint VTableSlot; + public RuntimeSignature MethodSignature; + public bool IsSealedVTableSlot; + } + + internal class VTableSlotMapper + { + private int[] _slotMap; + private IntPtr[] _dictionarySlots; + private int _numMappingsAssigned; + + public VTableSlotMapper(int numVtableSlotsInTemplateType) + { + _slotMap = new int[numVtableSlotsInTemplateType]; + _dictionarySlots = new IntPtr[numVtableSlotsInTemplateType]; + _numMappingsAssigned = 0; + for (int i = 0; i < numVtableSlotsInTemplateType; i++) + { + _slotMap[i] = -1; + _dictionarySlots[i] = IntPtr.Zero; + } + } + public void AddMapping(int vtableSlotInTemplateType, int vtableSlotInTargetType, IntPtr dictionaryValueInSlot) + { + Debug.Assert(_numMappingsAssigned < _slotMap.Length); + _slotMap[vtableSlotInTemplateType] = vtableSlotInTargetType; + _dictionarySlots[vtableSlotInTemplateType] = dictionaryValueInSlot; + _numMappingsAssigned++; + } + public int GetVTableSlotInTargetType(int vtableSlotInTemplateType) + { + Debug.Assert((uint)vtableSlotInTemplateType < (uint)_slotMap.Length); + return _slotMap[vtableSlotInTemplateType]; + } + public bool IsDictionarySlot(int vtableSlotInTemplateType, out IntPtr dictionaryPtrValue) + { + Debug.Assert((uint)vtableSlotInTemplateType < (uint)_dictionarySlots.Length); + dictionaryPtrValue = _dictionarySlots[vtableSlotInTemplateType]; + return _dictionarySlots[vtableSlotInTemplateType] != IntPtr.Zero; + } + public int NumSlotMappings + { + get { return _numMappingsAssigned; } + } + } + + public TypeBuilderState(TypeDesc typeBeingBuilt) + { + TypeBeingBuilt = typeBeingBuilt; + } + + public readonly TypeDesc TypeBeingBuilt; + + // + // We cache and try to reuse the most recently used TypeSystemContext. The TypeSystemContext is used by not just type builder itself, + // but also in several random other places in reflection. There can be multiple ResolutionContexts in flight at any given moment. + // This check ensures that the RuntimeTypeHandle cache in the current resolution context is refreshed in case there were new + // types built using a different TypeSystemContext in the meantime. + // NOTE: For correctness, this value must be recomputed every time the context is recycled. This requires flushing the TypeBuilderState + // from each type that has one if context is recycled. + // + public bool AttemptedAndFailedToRetrieveTypeHandle; + + public bool NeedsTypeHandle; + public bool HasBeenPrepared; + + public RuntimeTypeHandle HalfBakedRuntimeTypeHandle; + public IntPtr HalfBakedDictionary; + public IntPtr HalfBakedSealedVTable; + + private bool _templateComputed; + private bool _nativeLayoutTokenComputed; + private TypeDesc _templateType; + + public TypeDesc TemplateType + { + get + { + if (!_templateComputed) + { + // Multidimensional arrays and szarrays of pointers don't implement generic interfaces and are special cases. They use + // typeof(object[,]) as their template. + if (TypeBeingBuilt.IsMdArray || (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType.IsPointer)) + { + _templateType = TypeBeingBuilt.Context.ResolveRuntimeTypeHandle(typeof(object[,]).TypeHandle); + _templateTypeLoaderNativeLayout = false; + _nativeLayoutComputed = _nativeLayoutTokenComputed = _templateComputed = true; + + return _templateType; + } + + // Locate the template type and native layout info + _templateType = TypeBeingBuilt.Context.TemplateLookup.TryGetTypeTemplate(TypeBeingBuilt, ref _nativeLayoutInfo); + Debug.Assert(_templateType == null || !_templateType.RuntimeTypeHandle.IsNull()); + + _templateTypeLoaderNativeLayout = true; + _templateComputed = true; + if ((_templateType != null) && !_templateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + _nativeLayoutTokenComputed = true; + } + + return _templateType; + } + } + + private bool _nativeLayoutComputed; + private bool _templateTypeLoaderNativeLayout; + private bool _readyToRunNativeLayout; + + private NativeLayoutInfo _nativeLayoutInfo; + private NativeLayoutInfo _r2rnativeLayoutInfo; + + private void EnsureNativeLayoutInfoComputed() + { + if (!_nativeLayoutComputed) + { + if (!_nativeLayoutTokenComputed) + { + if (!_templateComputed) + { + // Attempt to compute native layout through as a non-ReadyToRun template + object temp = this.TemplateType; + } + if (!_nativeLayoutTokenComputed) + { + TypeBeingBuilt.Context.TemplateLookup.TryGetMetadataNativeLayout(TypeBeingBuilt, out _r2rnativeLayoutInfo.Module, out _r2rnativeLayoutInfo.Offset); + + if (_r2rnativeLayoutInfo.Module != null) + _readyToRunNativeLayout = true; + } + _nativeLayoutTokenComputed = true; + } + + if (_nativeLayoutInfo.Module != null) + { + FinishInitNativeLayoutInfo(TypeBeingBuilt, ref _nativeLayoutInfo); + } + + if (_r2rnativeLayoutInfo.Module != null) + { + FinishInitNativeLayoutInfo(TypeBeingBuilt, ref _r2rnativeLayoutInfo); + } + + _nativeLayoutComputed = true; + } + } + + /// + /// Initialize the Reader and LoadContext fields of the native layout info + /// + /// + /// + private static void FinishInitNativeLayoutInfo(TypeDesc type, ref NativeLayoutInfo nativeLayoutInfo) + { + var nativeLayoutInfoLoadContext = new NativeLayoutInfoLoadContext(); + + nativeLayoutInfoLoadContext._typeSystemContext = type.Context; + nativeLayoutInfoLoadContext._module = nativeLayoutInfo.Module; + + if (type is DefType) + { + nativeLayoutInfoLoadContext._typeArgumentHandles = ((DefType)type).Instantiation; + } + else if (type is ArrayType) + { + nativeLayoutInfoLoadContext._typeArgumentHandles = new Instantiation(new TypeDesc[] { ((ArrayType)type).ElementType }); + } + else + { + Debug.Assert(false); + } + + nativeLayoutInfoLoadContext._methodArgumentHandles = new Instantiation(null); + + nativeLayoutInfo.Reader = TypeLoaderEnvironment.Instance.GetNativeLayoutInfoReader(nativeLayoutInfo.Module.Handle); + nativeLayoutInfo.LoadContext = nativeLayoutInfoLoadContext; + } + + public NativeLayoutInfo NativeLayoutInfo + { + get + { + EnsureNativeLayoutInfoComputed(); + return _nativeLayoutInfo; + } + } + + public NativeLayoutInfo R2RNativeLayoutInfo + { + get + { + EnsureNativeLayoutInfoComputed(); + return _r2rnativeLayoutInfo; + } + } + + public NativeParser GetParserForNativeLayoutInfo() + { + EnsureNativeLayoutInfoComputed(); + if (_templateTypeLoaderNativeLayout) + return new NativeParser(_nativeLayoutInfo.Reader, _nativeLayoutInfo.Offset); + else + return default(NativeParser); + } + + public NativeParser GetParserForReadyToRunNativeLayoutInfo() + { + EnsureNativeLayoutInfoComputed(); + if (_readyToRunNativeLayout) + return new NativeParser(_r2rnativeLayoutInfo.Reader, _r2rnativeLayoutInfo.Offset); + else + return default(NativeParser); + } + + public NativeParser GetParserForUniversalNativeLayoutInfo(out NativeLayoutInfoLoadContext universalLayoutLoadContext, out NativeLayoutInfo universalLayoutInfo) + { + universalLayoutInfo = new NativeLayoutInfo(); + universalLayoutLoadContext = null; + TypeDesc universalTemplate = TypeBeingBuilt.Context.TemplateLookup.TryGetUniversalTypeTemplate(TypeBeingBuilt, ref universalLayoutInfo); + if (universalTemplate == null) + return new NativeParser(); + + FinishInitNativeLayoutInfo(TypeBeingBuilt, ref universalLayoutInfo); + universalLayoutLoadContext = universalLayoutInfo.LoadContext; + return new NativeParser(universalLayoutInfo.Reader, universalLayoutInfo.Offset); + } + + // RuntimeInterfaces is the full list of interfaces that the type implements. It can include private internal implementation + // detail interfaces that nothing is known about. + public DefType[] RuntimeInterfaces + { + get + { + // Generic Type Definitions have no runtime interfaces + if (TypeBeingBuilt.IsGenericDefinition) + return null; + + return TypeBeingBuilt.RuntimeInterfaces; + } + } + + private bool? _hasDictionarySlotInVTable; + private bool ComputeHasDictionarySlotInVTable() + { + if (!TypeBeingBuilt.IsGeneric() && !(TypeBeingBuilt is ArrayType)) + return false; + + // Generic interfaces always have a dictionary slot + if (TypeBeingBuilt.IsInterface) + return true; + + return TypeBeingBuilt.CanShareNormalGenericCode(); + } + + public bool HasDictionarySlotInVTable + { + get + { + if (_hasDictionarySlotInVTable == null) + { + _hasDictionarySlotInVTable = ComputeHasDictionarySlotInVTable(); + } + return _hasDictionarySlotInVTable.Value; + } + } + + private bool? _hasDictionaryInVTable; + private bool ComputeHasDictionaryInVTable() + { + if (!HasDictionarySlotInVTable) + return false; + + if (TypeBeingBuilt.RetrieveRuntimeTypeHandleIfPossible()) + { + // Type was already constructed + return TypeBeingBuilt.RuntimeTypeHandle.GetDictionary() != IntPtr.Zero; + } + else + { + // Type is being newly constructed + if (TemplateType != null) + { + NativeParser parser = GetParserForNativeLayoutInfo(); + // Template type loader case +#if GENERICS_FORCE_USG + bool isTemplateUniversalCanon = state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.UniversalCanonLookup); + if (isTemplateUniversalCanon && type.CanShareNormalGenericCode()) + { + TypeBuilderState tempState = new TypeBuilderState(); + tempState.NativeLayoutInfo = new NativeLayoutInfo(); + tempState.TemplateType = type.Context.TemplateLookup.TryGetNonUniversalTypeTemplate(type, ref tempState.NativeLayoutInfo); + if (tempState.TemplateType != null) + { + Debug.Assert(!tempState.TemplateType.IsCanonicalSubtype(CanonicalFormKind.UniversalCanonLookup)); + parser = GetNativeLayoutInfoParser(type, ref tempState.NativeLayoutInfo); + } + } +#endif + var dictionaryLayoutParser = parser.GetParserForBagElementKind(BagElementKind.DictionaryLayout); + + return !dictionaryLayoutParser.IsNull; + } + else + { + NativeParser parser = GetParserForReadyToRunNativeLayoutInfo(); + // ReadyToRun case + // Dictionary is directly encoded instead of the NativeLayout being a collection of bags + if (parser.IsNull) + return false; + + // First unsigned value in the native layout is the number of dictionary entries + return parser.GetUnsigned() != 0; + } + } + } + + public bool HasDictionaryInVTable + { + get + { + if (_hasDictionaryInVTable == null) + _hasDictionaryInVTable = ComputeHasDictionaryInVTable(); + return _hasDictionaryInVTable.Value; + } + } + + private ushort? _numVTableSlots; + private ushort ComputeNumVTableSlots() + { + if (TypeBeingBuilt.RetrieveRuntimeTypeHandleIfPossible()) + { + unsafe + { + return TypeBeingBuilt.RuntimeTypeHandle.ToEETypePtr()->NumVtableSlots; + } + } + else + { + TypeDesc templateType = TypeBeingBuilt.ComputeTemplate(false); + if (templateType != null) + { + // Template type loader case + if (VTableSlotsMapping != null) + { + return checked((ushort)VTableSlotsMapping.NumSlotMappings); + } + else + { + unsafe + { + if (TypeBeingBuilt.IsMdArray || (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType.IsPointer)) + { + // MDArray types and pointer arrays have the same vtable as the System.Array type they "derive" from. + // They do not implement the generic interfaces that make this interesting for normal arrays. + return TypeBeingBuilt.BaseType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; + } + else + { + // This should only happen for non-universal templates + Debug.Assert(TypeBeingBuilt.IsTemplateCanonical()); + + // Canonical template type loader case + return templateType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; + } + } + } + } + else + { + // Metadata based type loading. + + // Generic Type Definitions have no actual vtable entries + if (TypeBeingBuilt.IsGenericDefinition) + return 0; + + // We have at least as many slots as exist on the base type. + + ushort numVTableSlots = 0; + checked + { + if (TypeBeingBuilt.BaseType != null) + { + numVTableSlots = TypeBeingBuilt.BaseType.GetOrCreateTypeBuilderState().NumVTableSlots; + } + else + { + // Generic interfaces have a dictionary slot + if (TypeBeingBuilt.IsInterface && TypeBeingBuilt.HasInstantiation) + numVTableSlots = 1; + } + + // Interfaces have actual vtable slots + if (TypeBeingBuilt.IsInterface) + return numVTableSlots; + + foreach (MethodDesc method in TypeBeingBuilt.GetMethods()) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + if (LazyVTableResolver.MethodDefinesVTableSlot(method)) + numVTableSlots++; +#else + Environment.FailFast("metadata type loader required"); +#endif + } + + if (HasDictionarySlotInVTable) + numVTableSlots++; + } + + return numVTableSlots; + } + } + } + + public ushort NumVTableSlots + { + get + { + if (_numVTableSlots == null) + _numVTableSlots = ComputeNumVTableSlots(); + + return _numVTableSlots.Value; + } + } + + + public GenericTypeDictionary Dictionary; + + public int NonGcDataSize + { + get + { + DefType defType = TypeBeingBuilt as DefType; + + // The NonGCStatic fields hold the class constructor data if it exists in the negative space + // of the memory region. The ClassConstructorOffset is negative, so it must be negated to + // determine the extra space that is used. + + if (defType != null) + { + return defType.NonGCStaticFieldSize.AsInt - (HasStaticConstructor ? TypeBuilder.ClassConstructorOffset : 0); + } + else + { + return -(HasStaticConstructor ? TypeBuilder.ClassConstructorOffset : 0); + } + } + } + + public int GcDataSize + { + get + { + DefType defType = TypeBeingBuilt as DefType; + if (defType != null) + { + return defType.GCStaticFieldSize.AsInt; + } + else + { + return 0; + } + } + } + + public int ThreadDataSize + { + get + { + DefType defType = TypeBeingBuilt as DefType; + if (defType != null && !defType.IsGenericDefinition) + { + return defType.ThreadGcStaticFieldSize.AsInt; + } + else + { + // Non-DefType's and GenericEETypeDefinitions do not have static fields of any form + return 0; + } + } + } + + public bool HasStaticConstructor + { + get { return TypeBeingBuilt.HasStaticConstructor; } + } + + public IntPtr? ClassConstructorPointer; + public IntPtr GcStaticDesc; + public IntPtr GcStaticEEType; + public IntPtr ThreadStaticDesc; + public bool AllocatedStaticGCDesc; + public bool AllocatedThreadStaticGCDesc; + public uint ThreadStaticOffset; + public uint NumSealedVTableEntries; + public GenericVariance[] GenericVarianceFlags; + + // Sentinel static to allow us to initialize _instanceLayout to something + // and then detect that InstanceGCLayout should return null + private static LowLevelList s_emptyLayout = new LowLevelList(); + + private LowLevelList _instanceGCLayout; + + /// + /// The instance gc layout of a dynamically laid out type. + /// null if one of the following is true + /// 1) For an array type: + /// - the type is a reference array + /// 2) For a generic type: + /// - the type has no GC instance fields + /// - the type already has a type handle + /// - the type has a non-universal canonical template + /// - the type has already been constructed + /// + /// If the type is a valuetype array, this is the layout of the valuetype held in the array if the type has GC reference fields + /// Otherwise, it is the layout of the fields in the type. + /// + public LowLevelList InstanceGCLayout + { + get + { + if (_instanceGCLayout == null) + { + LowLevelList instanceGCLayout = null; + + if (TypeBeingBuilt is ArrayType) + { + if (!IsArrayOfReferenceTypes) + { + ArrayType arrayType = (ArrayType)TypeBeingBuilt; + TypeBuilder.GCLayout elementGcLayout = GetFieldGCLayout(arrayType.ElementType); + if (!elementGcLayout.IsNone) + { + instanceGCLayout = new LowLevelList(); + elementGcLayout.WriteToBitfield(instanceGCLayout, 0); + _instanceGCLayout = instanceGCLayout; + } + } + else + { + // Array of reference type returns null + _instanceGCLayout = s_emptyLayout; + } + } + else if (TypeBeingBuilt.RetrieveRuntimeTypeHandleIfPossible() || + TypeBeingBuilt.IsTemplateCanonical() || + (TypeBeingBuilt is PointerType) || + (TypeBeingBuilt is ByRefType)) + { + _instanceGCLayout = s_emptyLayout; + } + else + { + // Generic Type Definitions have no gc layout + if (!(TypeBeingBuilt.IsGenericDefinition)) + { + // Copy in from base type + if (!TypeBeingBuilt.IsValueType && (TypeBeingBuilt.BaseType != null)) + { + DefType baseType = TypeBeingBuilt.BaseType; + + // Capture the gc layout from the base type + TypeBuilder.GCLayout baseTypeLayout = GetInstanceGCLayout(baseType); + if (!baseTypeLayout.IsNone) + { + instanceGCLayout = new LowLevelList(); + baseTypeLayout.WriteToBitfield(instanceGCLayout, IntPtr.Size /* account for the MethodTable pointer */); + } + } + + foreach (FieldDesc field in GetFieldsForGCLayout()) + { + if (field.IsStatic) + continue; + + if (field.IsLiteral) + continue; + + TypeBuilder.GCLayout fieldGcLayout = GetFieldGCLayout(field.FieldType); + if (!fieldGcLayout.IsNone) + { + if (instanceGCLayout == null) + instanceGCLayout = new LowLevelList(); + + fieldGcLayout.WriteToBitfield(instanceGCLayout, field.Offset.AsInt); + } + } + + if ((instanceGCLayout != null) && instanceGCLayout.HasSetBits()) + { + // When bits are set in the instance GC layout, it implies that the type contains GC refs, + // which implies that the type size is pointer-aligned. In this case consumers assume that + // the type size can be computed by multiplying the bitfield size by the pointer size. If + // necessary, expand the bitfield to ensure that this invariant holds. + + // Valuetypes with gc fields must be aligned on at least pointer boundaries + Debug.Assert(!TypeBeingBuilt.IsValueType || (FieldAlignment.Value >= TypeBeingBuilt.Context.Target.PointerSize)); + // Valuetypes with gc fields must have a type size which is aligned on an IntPtr boundary. + Debug.Assert(!TypeBeingBuilt.IsValueType || ((TypeSize.Value & (IntPtr.Size - 1)) == 0)); + + int impliedBitCount = (TypeSize.Value + IntPtr.Size - 1) / IntPtr.Size; + Debug.Assert(instanceGCLayout.Count <= impliedBitCount); + instanceGCLayout.Expand(impliedBitCount); + Debug.Assert(instanceGCLayout.Count == impliedBitCount); + } + } + + if (instanceGCLayout == null) + _instanceGCLayout = s_emptyLayout; + else + _instanceGCLayout = instanceGCLayout; + } + } + + if (_instanceGCLayout == s_emptyLayout) + return null; + else + return _instanceGCLayout; + } + } + + + public LowLevelList StaticGCLayout; + public LowLevelList ThreadStaticGCLayout; + + private bool _staticGCLayoutPrepared; + + /// + /// Prepare the StaticGCLayout/ThreadStaticGCLayout/GcStaticDesc/ThreadStaticDesc fields by + /// reading native layout or metadata as appropriate. This method should only be called for types which + /// are actually to be created. + /// + public void PrepareStaticGCLayout() + { + if (!_staticGCLayoutPrepared) + { + _staticGCLayoutPrepared = true; + DefType defType = TypeBeingBuilt as DefType; + + if (defType == null) + { + // Array/pointer types do not have static fields + } + else if (defType.IsTemplateCanonical()) + { + // Canonical templates get their layout directly from the NativeLayoutInfo. + // Parse it and pull that info out here. + + NativeParser typeInfoParser = GetParserForNativeLayoutInfo(); + + BagElementKind kind; + while ((kind = typeInfoParser.GetBagElementKind()) != BagElementKind.End) + { + switch (kind) + { + case BagElementKind.GcStaticDesc: + GcStaticDesc = NativeLayoutInfo.LoadContext.GetGCStaticInfo(typeInfoParser.GetUnsigned()); + break; + + case BagElementKind.ThreadStaticDesc: + ThreadStaticDesc = NativeLayoutInfo.LoadContext.GetGCStaticInfo(typeInfoParser.GetUnsigned()); + break; + + case BagElementKind.GcStaticEEType: + GcStaticEEType = NativeLayoutInfo.LoadContext.GetGCStaticInfo(typeInfoParser.GetUnsigned()); + break; + + default: + typeInfoParser.SkipInteger(); + break; + } + } + } + else + { + // Compute GC layout boolean array from field information. + IEnumerable fields = GetFieldsForGCLayout(); + LowLevelList threadStaticLayout = null; + LowLevelList gcStaticLayout = null; + + foreach (FieldDesc field in fields) + { + if (!field.IsStatic) + continue; + + if (field.IsLiteral) + continue; + + LowLevelList gcLayoutInfo = null; + if (field.IsThreadStatic) + { + if (threadStaticLayout == null) + threadStaticLayout = new LowLevelList(); + gcLayoutInfo = threadStaticLayout; + } + else if (field.HasGCStaticBase) + { + if (gcStaticLayout == null) + gcStaticLayout = new LowLevelList(); + gcLayoutInfo = gcStaticLayout; + } + else + { + // Non-GC static no need to record information + continue; + } + + TypeBuilder.GCLayout fieldGcLayout = GetFieldGCLayout(field.FieldType); + fieldGcLayout.WriteToBitfield(gcLayoutInfo, field.Offset.AsInt); + } + + if (gcStaticLayout != null && gcStaticLayout.Count > 0) + StaticGCLayout = gcStaticLayout; + + if (threadStaticLayout != null && threadStaticLayout.Count > 0) + ThreadStaticGCLayout = threadStaticLayout; + } + } + } + + /// + /// Get an enumerable list of the fields used for dynamic gc layout calculation. + /// + private IEnumerable GetFieldsForGCLayout() + { + DefType defType = (DefType)TypeBeingBuilt; + + IEnumerable fields; + + if (defType.ComputeTemplate(false) != null) + { + // we have native layout and a template. Use the NativeLayoutFields as that is the only complete + // description of the fields available. (There may be metadata fields, but those aren't guaranteed + // to be a complete set of fields due to reflection reduction. + NativeLayoutFieldAlgorithm.EnsureFieldLayoutLoadedForGenericType(defType); + fields = defType.NativeLayoutFields; + } + else + { + // The metadata case. We're loading the type from regular metadata, so use the regular metadata fields + fields = defType.GetFields(); + } + + return fields; + } + + // Get the GC layout of a type. Handles pre-created, universal template, and non-universal template cases + // Only to be used for getting the instance layout of non-valuetypes. + /// + /// Get the GC layout of a type. Handles pre-created, universal template, and non-universal template cases + /// Only to be used for getting the instance layout of non-valuetypes that are used as base types + /// + /// + /// + private unsafe TypeBuilder.GCLayout GetInstanceGCLayout(TypeDesc type) + { + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any)); + Debug.Assert(!type.IsValueType); + + if (type.RetrieveRuntimeTypeHandleIfPossible()) + { + return new TypeBuilder.GCLayout(type.RuntimeTypeHandle); + } + + if (type.IsTemplateCanonical()) + { + var templateType = type.ComputeTemplate(); + bool success = templateType.RetrieveRuntimeTypeHandleIfPossible(); + Debug.Assert(success && !templateType.RuntimeTypeHandle.IsNull()); + + return new TypeBuilder.GCLayout(templateType.RuntimeTypeHandle); + } + else + { + TypeBuilderState state = type.GetOrCreateTypeBuilderState(); + if (state.InstanceGCLayout == null) + return TypeBuilder.GCLayout.None; + else + return new TypeBuilder.GCLayout(state.InstanceGCLayout, true); + } + } + + /// + /// Get the GC layout of a type when used as a field. + /// NOTE: if the fieldtype is a reference type, this function will return GCLayout.None + /// Consumers of the api must handle that special case. + /// + private unsafe TypeBuilder.GCLayout GetFieldGCLayout(TypeDesc fieldType) + { + if (!fieldType.IsValueType) + { + if (fieldType.IsPointer) + return TypeBuilder.GCLayout.None; + else + return TypeBuilder.GCLayout.SingleReference; + } + + // Is this a type that already exists? If so, get its gclayout from the MethodTable directly + if (fieldType.RetrieveRuntimeTypeHandleIfPossible()) + { + return new TypeBuilder.GCLayout(fieldType.RuntimeTypeHandle); + } + + // The type of the field must be a valuetype that is dynamically being constructed + + if (fieldType.IsTemplateCanonical()) + { + // Pull the GC Desc from the canonical instantiation + TypeDesc templateType = fieldType.ComputeTemplate(); + bool success = templateType.RetrieveRuntimeTypeHandleIfPossible(); + Debug.Assert(success); + return new TypeBuilder.GCLayout(templateType.RuntimeTypeHandle); + } + else + { + // Use the type builder state's computed InstanceGCLayout + var instanceGCLayout = fieldType.GetOrCreateTypeBuilderState().InstanceGCLayout; + if (instanceGCLayout == null) + return TypeBuilder.GCLayout.None; + + return new TypeBuilder.GCLayout(instanceGCLayout, false /* Always represents a valuetype as the reference type case + is handled above with the GCLayout.SingleReference return */); + } + } + + + public bool IsArrayOfReferenceTypes + { + get + { + ArrayType typeAsArrayType = TypeBeingBuilt as ArrayType; + if (typeAsArrayType != null) + return !typeAsArrayType.ParameterType.IsValueType && !typeAsArrayType.ParameterType.IsPointer; + else + return false; + } + } + + // Rank for arrays, -1 is used for an SzArray, and a positive number for a multidimensional array. + public int? ArrayRank + { + get + { + if (!TypeBeingBuilt.IsArray) + return null; + else if (TypeBeingBuilt.IsSzArray) + return -1; + else + { + Debug.Assert(TypeBeingBuilt.IsMdArray); + return ((ArrayType)TypeBeingBuilt).Rank; + } + } + } + + public int? BaseTypeSize + { + get + { + if (TypeBeingBuilt.BaseType == null) + { + return null; + } + else + { + return TypeBeingBuilt.BaseType.InstanceByteCountUnaligned.AsInt; + } + } + } + + public int? TypeSize + { + get + { + DefType defType = TypeBeingBuilt as DefType; + if (defType != null) + { + // Generic Type Definition EETypes do not have size + if (defType.IsGenericDefinition) + return null; + + if (defType.IsValueType) + { + return defType.InstanceFieldSize.AsInt; + } + else + { + if (defType.IsInterface) + return IntPtr.Size; + + return defType.InstanceByteCountUnaligned.AsInt; + } + } + else if (TypeBeingBuilt is ArrayType) + { + int basicArraySize = 2 * IntPtr.Size; // EETypePtr + Length + if (TypeBeingBuilt.IsMdArray) + { + // MD Arrays are arranged like normal arrays, but they also have 2 int's per rank for the individual dimension loBounds and range. + basicArraySize += ((ArrayType)TypeBeingBuilt).Rank * sizeof(int) * 2; + } + return basicArraySize; + } + else + { + return null; + } + } + } + + public int? UnalignedTypeSize + { + get + { + DefType defType = TypeBeingBuilt as DefType; + if (defType != null) + { + return defType.InstanceByteCountUnaligned.AsInt; + } + else if (TypeBeingBuilt is ArrayType) + { + // Arrays use the same algorithm for TypeSize as for UnalignedTypeSize + return TypeSize; + } + else + { + return 0; + } + } + } + + public int? FieldAlignment + { + get + { + if (TypeBeingBuilt is DefType) + { + return checked((ushort)((DefType)TypeBeingBuilt).InstanceFieldAlignment.AsInt); + } + else if (TypeBeingBuilt is ArrayType) + { + ArrayType arrayType = (ArrayType)TypeBeingBuilt; + + if (arrayType.ElementType is DefType) + { + return checked((ushort)((DefType)arrayType.ElementType).InstanceFieldAlignment.AsInt); + } + else + { + return (ushort)arrayType.Context.Target.PointerSize; + } + } + else if (TypeBeingBuilt is PointerType || TypeBeingBuilt is ByRefType) + { + return (ushort)TypeBeingBuilt.Context.Target.PointerSize; + } + else + { + return null; + } + } + } + + public ushort? ComponentSize + { + get + { + ArrayType arrayType = TypeBeingBuilt as ArrayType; + if (arrayType != null) + { + if (arrayType.ElementType is DefType) + { + uint size = (uint)((DefType)arrayType.ElementType).InstanceFieldSize.AsInt; + + if (size > ArrayTypesConstants.MaxSizeForValueClassInArray && arrayType.ElementType.IsValueType) + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadValueClassTooLarge, arrayType.ElementType); + + return checked((ushort)size); + } + else + { + return (ushort)arrayType.Context.Target.PointerSize; + } + } + else + { + return null; + } + } + } + + public uint NullableValueOffset + { + get + { + if (!TypeBeingBuilt.IsNullable) + return 0; + + if (TypeBeingBuilt.IsTemplateCanonical()) + { + // Pull the GC Desc from the canonical instantiation + TypeDesc templateType = TypeBeingBuilt.ComputeTemplate(); + bool success = templateType.RetrieveRuntimeTypeHandleIfPossible(); + Debug.Assert(success); + unsafe + { + return templateType.RuntimeTypeHandle.ToEETypePtr()->NullableValueOffset; + } + } + else + { + int fieldCount = 0; + uint nullableValueOffset = 0; + + foreach (FieldDesc f in GetFieldsForGCLayout()) + { + if (fieldCount == 1) + { + nullableValueOffset = checked((uint)f.Offset.AsInt); + } + fieldCount++; + } + + // Nullable only has two fields. HasValue and Value + Debug.Assert(fieldCount == 2); + return nullableValueOffset; + } + } + } + + public bool IsHFA + { + get + { +#if TARGET_ARM + if (TypeBeingBuilt.IsValueType && TypeBeingBuilt is DefType) + { + return ((DefType)TypeBeingBuilt).IsHomogeneousAggregate; + } + else + { + return false; + } +#else + // On Non-ARM platforms, HFA'ness is not encoded in the MethodTable as it doesn't effect ABI + return false; +#endif + } + } + + public VTableLayoutInfo[] VTableMethodSignatures; + public int NumSealedVTableMethodSignatures; + + public VTableSlotMapper VTableSlotsMapping; + +#if GENERICS_FORCE_USG + public TypeDesc NonUniversalTemplateType; + public int NonUniversalInstanceGCDescSize; + public IntPtr NonUniversalInstanceGCDesc; + public IntPtr NonUniversalStaticGCDesc; + public IntPtr NonUniversalThreadStaticGCDesc; +#endif + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericMethodsLookup.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericMethodsLookup.cs new file mode 100644 index 00000000000000..b1f003ed8dab4c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericMethodsLookup.cs @@ -0,0 +1,555 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.NativeFormat; +using Internal.TypeSystem; + +namespace Internal.Runtime.TypeLoader +{ + public sealed partial class TypeLoaderEnvironment + { + internal class GenericMethodEntry + { + private int? _hashCode; + public bool _isRegisteredSuccessfully; + public IntPtr _methodDictionary; + public RuntimeTypeHandle _declaringTypeHandle; + public MethodNameAndSignature _methodNameAndSignature; + public RuntimeTypeHandle[] _genericMethodArgumentHandles; + + public override int GetHashCode() + { + if (!_hashCode.HasValue) + { + _hashCode = _declaringTypeHandle.GetHashCode() ^ TypeHashingAlgorithms.ComputeGenericInstanceHashCode(TypeHashingAlgorithms.ComputeNameHashCode(_methodNameAndSignature.Name), _genericMethodArgumentHandles); + } + return _hashCode.Value; + } + + public override bool Equals(object obj) + { + // There are no scenarios where we call .Equals to check the componentized equality (we explicitly use IsEqualToEntryByComponentsComparison for that), + // so making sure we revert to the reference equality in case someone calls here. + return base.Equals(obj); + } + + public virtual bool IsEqualToEntryByComponentsComparison(GenericMethodEntry other) + { + if (!other._declaringTypeHandle.Equals(_declaringTypeHandle)) + return false; + + if (!other._methodNameAndSignature.Equals(_methodNameAndSignature)) + return false; + + if (other._genericMethodArgumentHandles == null) + return false; + + if (other._genericMethodArgumentHandles.Length != _genericMethodArgumentHandles.Length) + return false; + + for (int i = 0; i < _genericMethodArgumentHandles.Length; i++) + if (!other._genericMethodArgumentHandles[i].Equals(_genericMethodArgumentHandles[i])) + return false; + + return true; + } + } + + internal class DynamicGenericMethodsHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(GenericMethodLookupData key) + { + return key.LookupHashCode(); + } + protected override bool CompareKeyToValue(GenericMethodLookupData key, GenericMethodEntry value) + { + return key.MatchGenericMethodEntry(value); + } + + protected override int GetValueHashCode(GenericMethodEntry value) + { + return value.GetHashCode(); + } + + protected override bool CompareValueToValue(GenericMethodEntry value1, GenericMethodEntry value2) + { + // Comparisons should *only* be done using the generic method components + return value1.IsEqualToEntryByComponentsComparison(value2); + } + + protected override GenericMethodEntry CreateValueFromKey(GenericMethodLookupData key) + { + // Feature not used by the TypeBuilder + throw NotImplemented.ByDesign; + } + } + + internal class DynamicGenericMethodComponentsHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(IntPtr key) + { + return key.GetHashCode(); + } + protected override bool CompareKeyToValue(IntPtr key, GenericMethodEntry value) + { + return key.Equals(value._methodDictionary); + } + + protected override int GetValueHashCode(GenericMethodEntry value) + { + Debug.Assert(value._methodDictionary != IntPtr.Zero); + return value._methodDictionary.GetHashCode(); + } + + protected override bool CompareValueToValue(GenericMethodEntry value1, GenericMethodEntry value2) + { + // Comparisons should *only* be done using the generic method components + return value1.IsEqualToEntryByComponentsComparison(value2); + } + + protected override GenericMethodEntry CreateValueFromKey(IntPtr key) + { + // Feature not used by the TypeBuilder + throw NotImplemented.ByDesign; + } + } + + internal abstract class GenericMethodLookupData + { + internal abstract int LookupHashCode(); + internal abstract bool MatchParsedEntry(ref NativeParser entryParser, ref ExternalReferencesTable externalReferencesLookup, TypeManagerHandle moduleHandle); + internal abstract bool MatchGenericMethodEntry(GenericMethodEntry entry); + } + internal class MethodDescBasedGenericMethodLookup : GenericMethodLookupData + { + protected InstantiatedMethod _methodToLookup; + + internal MethodDescBasedGenericMethodLookup(InstantiatedMethod methodToLookup) { _methodToLookup = methodToLookup; } + + internal override int LookupHashCode() { return _methodToLookup.GetHashCode(); } + + internal override bool MatchParsedEntry(ref NativeParser entryParser, ref ExternalReferencesTable externalReferencesLookup, TypeManagerHandle moduleHandle) + { + // + // Entries read from the hashtable are loaded as GenericMethodDescs, and compared to the input. + // This lookup is slower than the lookups using RuntimeTypeHandles, but can handle cases where we don't have + // RuntimeTypeHandle values for all of the components of the input GenericMethodDesc, but still need to look it up in case the + // method dictionary statically really exists + // + TypeSystemContext context = _methodToLookup.Context; + + RuntimeTypeHandle parsedDeclaringTypeHandle = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + + // Hash table names / sigs are indirected through to the native layout info + MethodNameAndSignature nameAndSignature; + if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(moduleHandle, entryParser.GetUnsigned(), out nameAndSignature)) + return false; + + RuntimeTypeHandle[] parsedArgsHandles = GetTypeSequence(ref externalReferencesLookup, ref entryParser); + + DefType parsedDeclaringType = context.ResolveRuntimeTypeHandle(parsedDeclaringTypeHandle) as DefType; + Instantiation parsedArgs = context.ResolveRuntimeTypeHandles(parsedArgsHandles); + InstantiatedMethod parsedGenericMethod = (InstantiatedMethod)context.ResolveGenericMethodInstantiation(false, parsedDeclaringType, nameAndSignature, parsedArgs, IntPtr.Zero, false); + + return parsedGenericMethod == _methodToLookup; + } + + internal override bool MatchGenericMethodEntry(GenericMethodEntry entry) + { + TypeSystemContext context = _methodToLookup.Context; + + DefType parsedDeclaringType = context.ResolveRuntimeTypeHandle(entry._declaringTypeHandle) as DefType; + Instantiation parsedArgs = context.ResolveRuntimeTypeHandles(entry._genericMethodArgumentHandles); + InstantiatedMethod parsedGenericMethod = (InstantiatedMethod)context.ResolveGenericMethodInstantiation(false, parsedDeclaringType, entry._methodNameAndSignature, parsedArgs, IntPtr.Zero, false); + + return parsedGenericMethod == _methodToLookup; + } + } + internal class HandleBasedGenericMethodLookup : MethodDescBasedGenericMethodLookup + { + private RuntimeTypeHandle _declaringType; + private MethodNameAndSignature _nameAndSignature; + private RuntimeTypeHandle[] _genericMethodArgumentHandles; + + internal HandleBasedGenericMethodLookup(InstantiatedMethod methodToLookup) : base(methodToLookup) + { + Debug.Assert(methodToLookup != null); + _declaringType = _methodToLookup.OwningType.RuntimeTypeHandle; + _nameAndSignature = _methodToLookup.NameAndSignature; + // _genericMethodArgumentHandles not initialized here to avoid allocation of a new array (and it's not used if we initialize _typeToLookup). + } + + internal HandleBasedGenericMethodLookup(RuntimeTypeHandle declaringType, MethodNameAndSignature nameAndSignature, RuntimeTypeHandle[] genericMethodArgumentHandles) : base(null) + { + Debug.Assert(!declaringType.IsNull() && nameAndSignature != null && genericMethodArgumentHandles != null); + _declaringType = declaringType; + _nameAndSignature = nameAndSignature; + _genericMethodArgumentHandles = genericMethodArgumentHandles; + } + + internal override int LookupHashCode() + { + // Todo: Signatures in the hash code. + return _methodToLookup != null ? _methodToLookup.GetHashCode() : (_declaringType.GetHashCode() ^ TypeHashingAlgorithms.ComputeGenericInstanceHashCode(TypeHashingAlgorithms.ComputeNameHashCode(_nameAndSignature.Name), _genericMethodArgumentHandles)); + } + + internal override bool MatchParsedEntry(ref NativeParser entryParser, ref ExternalReferencesTable externalReferencesLookup, TypeManagerHandle moduleHandle) + { + // Compare entry with inputs as we parse it. If we get a mismatch, stop parsing and move to the next entry... + RuntimeTypeHandle parsedDeclaringTypeHandle = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!parsedDeclaringTypeHandle.Equals(_declaringType)) + return false; + + // Hash table names / sigs are indirected through to the native layout info + MethodNameAndSignature nameAndSignature; + if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(moduleHandle, entryParser.GetUnsigned(), out nameAndSignature)) + return false; + + if (!nameAndSignature.Equals(_nameAndSignature)) + return false; + + int parsedArity = (int)entryParser.GetSequenceCount(); + int lookupArity = (_methodToLookup != null ? _methodToLookup.Instantiation.Length : _genericMethodArgumentHandles.Length); + if (parsedArity != lookupArity) + return false; + + for (int i = 0; i < parsedArity; i++) + { + RuntimeTypeHandle parsedArg = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + RuntimeTypeHandle lookupArg = (_methodToLookup != null ? _methodToLookup.Instantiation[i].RuntimeTypeHandle : _genericMethodArgumentHandles[i]); + if (!parsedArg.Equals(lookupArg)) + return false; + } + + return true; + } + + internal override bool MatchGenericMethodEntry(GenericMethodEntry entry) + { + if (!entry._declaringTypeHandle.Equals(_declaringType)) + return false; + + if (!entry._methodNameAndSignature.Equals(_nameAndSignature)) + return false; + + if (entry._genericMethodArgumentHandles == null) + return false; + + if (_methodToLookup != null) + { + int expectedArity = _methodToLookup.Instantiation.Length; + + if (entry._genericMethodArgumentHandles.Length != expectedArity) + return false; + + for (int i = 0; i < expectedArity; i++) + if (!entry._genericMethodArgumentHandles[i].Equals(_methodToLookup.Instantiation[i].RuntimeTypeHandle)) + return false; + } + else + { + if (entry._genericMethodArgumentHandles.Length != _genericMethodArgumentHandles.Length) + return false; + + for (int i = 0; i < _genericMethodArgumentHandles.Length; i++) + if (!entry._genericMethodArgumentHandles[i].Equals(_genericMethodArgumentHandles[i])) + return false; + } + + return true; + } + } + + private DynamicGenericMethodsHashtable _dynamicGenericMethods = new DynamicGenericMethodsHashtable(); + private DynamicGenericMethodComponentsHashtable _dynamicGenericMethodComponents = new DynamicGenericMethodComponentsHashtable(); + + internal bool TryLookupGenericMethodDictionaryForComponents(GenericMethodLookupData lookupData, out IntPtr result) + { + if (!TryGetStaticGenericMethodDictionaryForComponents(lookupData, out result)) + if (!TryGetDynamicGenericMethodDictionaryForComponents(lookupData, out result)) + return false; + + return true; + } + + public bool TryLookupGenericMethodDictionaryForComponents(RuntimeTypeHandle declaringType, MethodNameAndSignature nameAndSignature, RuntimeTypeHandle[] genericMethodArgumentHandles, out IntPtr result) + { + return TryLookupGenericMethodDictionaryForComponents(new HandleBasedGenericMethodLookup(declaringType, nameAndSignature, genericMethodArgumentHandles), out result); + } + + public bool TryGetGenericMethodComponents(IntPtr methodDictionary, out RuntimeTypeHandle declaringType, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodArgumentHandles) + { + nameAndSignature = null; + + if (!TryGetDynamicGenericMethodComponents(methodDictionary, out declaringType, out nameAndSignature, out genericMethodArgumentHandles)) + if (!TryGetStaticGenericMethodComponents(methodDictionary, out declaringType, out nameAndSignature, out genericMethodArgumentHandles)) + return false; + + return true; + } + + public bool TryLookupExactMethodPointerForComponents(RuntimeTypeHandle declaringType, MethodNameAndSignature nameAndSignature, RuntimeTypeHandle[] genericMethodArgumentHandles, out IntPtr result) + { + int lookupHashcode = declaringType.GetHashCode(); + + NativeHashtable hashtable; + ExternalReferencesTable externalReferencesLookup; + + HandleBasedGenericMethodLookup lookupData = new HandleBasedGenericMethodLookup(declaringType, nameAndSignature, genericMethodArgumentHandles); + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + if (!GetHashtableFromBlob(module, ReflectionMapBlob.ExactMethodInstantiationsHashtable, out hashtable, out externalReferencesLookup)) + continue; + + var enumerator = hashtable.Lookup(lookupHashcode); + + NativeParser entryParser; + while (!(entryParser = enumerator.GetNext()).IsNull) + { + if (!lookupData.MatchParsedEntry(ref entryParser, ref externalReferencesLookup, module.Handle)) + continue; + + // We found a match + result = externalReferencesLookup.GetIntPtrFromIndex(entryParser.GetUnsigned()); + return true; + } + } + + result = IntPtr.Zero; + return false; + } + + // This method computes the method pointer and dictionary pointer for a GVM. + // Inputs: + // - targetTypeHanlde: target type on which the GVM is implemented + // - nameAndSignature: name and signature of the GVM method + // - genericMethodArgumentHandles: GVM instantiation arguments + // Outputs: + // - methodPointer: pointer to the GVM's implementation + // - dictionaryPointer: (if applicable) pointer to the dictionary to be used with the GVM call + public bool TryGetGenericVirtualMethodPointer(RuntimeTypeHandle targetTypeHandle, MethodNameAndSignature nameAndSignature, RuntimeTypeHandle[] genericMethodArgumentHandles, out IntPtr methodPointer, out IntPtr dictionaryPointer) + { + methodPointer = dictionaryPointer = IntPtr.Zero; + + TypeSystemContext context = TypeSystemContextFactory.Create(); + + DefType targetType = (DefType)context.ResolveRuntimeTypeHandle(targetTypeHandle); + Instantiation methodInstantiation = context.ResolveRuntimeTypeHandles(genericMethodArgumentHandles); + InstantiatedMethod method = (InstantiatedMethod)context.ResolveGenericMethodInstantiation(false, targetType, nameAndSignature, methodInstantiation, IntPtr.Zero, false); + + if (!method.CanShareNormalGenericCode()) + { + // First see if we can find an exact method implementation for the GVM (avoid using USG implementations if we can, + // because USG code is much slower). + if (TryLookupExactMethodPointerForComponents(targetTypeHandle, nameAndSignature, genericMethodArgumentHandles, out methodPointer)) + { + Debug.Assert(methodPointer != IntPtr.Zero); + TypeSystemContextFactory.Recycle(context); + return true; + } + } + + // If we cannot find an exact method entry point, look for an equivalent template and compute the generic dictinoary + TemplateLocator templateLocator = new TemplateLocator(); + NativeLayoutInfo nativeLayoutInfo = new NativeLayoutInfo(); + InstantiatedMethod templateMethod = templateLocator.TryGetGenericMethodTemplate(method, out nativeLayoutInfo.Module, out nativeLayoutInfo.Offset); + if (templateMethod == null) + return false; + + methodPointer = templateMethod.IsCanonicalMethod(CanonicalFormKind.Universal) ? + templateMethod.UsgFunctionPointer : + templateMethod.FunctionPointer; + + if (!TryLookupGenericMethodDictionaryForComponents(targetTypeHandle, nameAndSignature, genericMethodArgumentHandles, out dictionaryPointer)) + { + using (LockHolder.Hold(_typeLoaderLock)) + { + // Now that we hold the lock, we may find that existing types can now find + // their associated RuntimeTypeHandle. Flush the type builder states as a way + // to force the reresolution of RuntimeTypeHandles which couldn't be found before. + context.FlushTypeBuilderStates(); + + if (!TypeBuilder.TryBuildGenericMethod(method, out dictionaryPointer)) + return false; + } + } + + Debug.Assert(methodPointer != IntPtr.Zero && dictionaryPointer != IntPtr.Zero); + + if (templateMethod.IsCanonicalMethod(CanonicalFormKind.Universal)) + { + // Check if we need to wrap the method pointer into a calling convention converter thunk + if (!TypeLoaderEnvironment.Instance.MethodSignatureHasVarsNeedingCallingConventionConverter(context, nameAndSignature.Signature)) + { + TypeSystemContextFactory.Recycle(context); + return true; + } + + RuntimeTypeHandle[] typeArgs = Array.Empty(); + + if (RuntimeAugments.IsGenericType(targetTypeHandle)) + { + RuntimeAugments.GetGenericInstantiation(targetTypeHandle, out typeArgs); + } + + // Create a CallingConventionConverter to call the method correctly + IntPtr thunkPtr = CallConverterThunk.MakeThunk( + CallConverterThunk.ThunkKind.StandardToGenericInstantiating, + methodPointer, + nameAndSignature.Signature, + dictionaryPointer, + typeArgs, + genericMethodArgumentHandles); + + Debug.Assert(thunkPtr != IntPtr.Zero); + + methodPointer = thunkPtr; + // Set dictionaryPointer to null so we don't make a fat function pointer around the whole thing. + dictionaryPointer = IntPtr.Zero; + + // TODO! add a new call converter thunk that will pass the instantiating arg through and use a fat function pointer. + // should allow us to make fewer thunks. + } + + TypeSystemContextFactory.Recycle(context); + return true; + } + + #region Privates + private bool TryGetDynamicGenericMethodDictionaryForComponents(GenericMethodLookupData lookupData, out IntPtr result) + { + result = IntPtr.Zero; + + using (LockHolder.Hold(_dynamicGenericsLock)) + { + GenericMethodEntry entry; + if (!_dynamicGenericMethods.TryGetValue(lookupData, out entry)) + return false; + + if (!entry._isRegisteredSuccessfully) + return false; + + result = entry._methodDictionary; + return true; + } + } + private bool TryGetStaticGenericMethodDictionaryForComponents(GenericMethodLookupData lookupData, out IntPtr result) + { + // Search the hashtable for a generic instantiation match + + ExternalReferencesTable externalReferencesLookup; + NativeHashtable genericMethodsHashtable; + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + if (!GetHashtableFromBlob(module, ReflectionMapBlob.GenericMethodsHashtable, out genericMethodsHashtable, out externalReferencesLookup)) + continue; + + int lookupHashcode = lookupData.LookupHashCode(); + var enumerator = genericMethodsHashtable.Lookup(lookupHashcode); + + NativeParser entryParser; + while (!(entryParser = enumerator.GetNext()).IsNull) + { + uint dictionaryIndex = entryParser.GetUnsigned(); + + if (!lookupData.MatchParsedEntry(ref entryParser, ref externalReferencesLookup, module.Handle)) + continue; + + // Current entry matched all inputs, return success + result = externalReferencesLookup.GetIntPtrFromIndex(dictionaryIndex); + return true; + } + } + + result = IntPtr.Zero; + return false; + } + + private bool TryGetDynamicGenericMethodComponents(IntPtr methodDictionary, out RuntimeTypeHandle declaringType, out MethodNameAndSignature methodNameAndSignature, out RuntimeTypeHandle[] genericMethodArgumentHandles) + { + declaringType = default(RuntimeTypeHandle); + methodNameAndSignature = null; + genericMethodArgumentHandles = null; + + using (LockHolder.Hold(_dynamicGenericsLock)) + { + GenericMethodEntry entry; + if (!_dynamicGenericMethodComponents.TryGetValue(methodDictionary, out entry)) + return false; + + if (!entry._isRegisteredSuccessfully) + return false; + + declaringType = entry._declaringTypeHandle; + methodNameAndSignature = entry._methodNameAndSignature; + genericMethodArgumentHandles = entry._genericMethodArgumentHandles; + return true; + } + } + private unsafe bool TryGetStaticGenericMethodComponents(IntPtr methodDictionary, out RuntimeTypeHandle declaringType, out MethodNameAndSignature methodNameAndSignature, out RuntimeTypeHandle[] genericMethodArgumentHandles) + { + // Generic method dictionaries have a header that has the hash code in it. Locate the header + IntPtr dictionaryHeader = IntPtr.Subtract(methodDictionary, IntPtr.Size); + int lookupHashcode = *(int*)dictionaryHeader; + + ExternalReferencesTable externalReferencesLookup; + NativeHashtable genericMethodsHashtable; + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + if (!GetHashtableFromBlob(module, ReflectionMapBlob.GenericMethodsHashtable, out genericMethodsHashtable, out externalReferencesLookup)) + continue; + + var enumerator = genericMethodsHashtable.Lookup(lookupHashcode); + + NativeParser entryParser; + while (!(entryParser = enumerator.GetNext()).IsNull) + { + // Is this entry the dictionary we are looking for? + uint dictionaryIndex = entryParser.GetUnsigned(); + IntPtr parsedMethodDictionary = externalReferencesLookup.GetIntPtrFromIndex(dictionaryIndex); + if (parsedMethodDictionary != methodDictionary) + continue; + + // We have a match - fill in the results + declaringType = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + + // Hash table names / sigs are indirected through to the native layout info + if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(module.Handle, entryParser.GetUnsigned(), out methodNameAndSignature)) + continue; + + uint arity = entryParser.GetSequenceCount(); + genericMethodArgumentHandles = new RuntimeTypeHandle[arity]; + + for (int i = 0; i < arity; i++) + { + genericMethodArgumentHandles[i] = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + } + + return true; + } + } + + declaringType = default(RuntimeTypeHandle); + methodNameAndSignature = null; + genericMethodArgumentHandles = null; + return false; + } + + #endregion + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericTypesLookup.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericTypesLookup.cs new file mode 100644 index 00000000000000..4ebc79b2189558 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericTypesLookup.cs @@ -0,0 +1,329 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +using Internal.Runtime; +using Internal.Runtime.Augments; + +using Internal.NativeFormat; +using Internal.TypeSystem; + +namespace Internal.Runtime.TypeLoader +{ + public sealed partial class TypeLoaderEnvironment + { + internal struct TypeEntryToRegister + { + public GenericTypeEntry GenericTypeEntry; + public MetadataType MetadataDefinitionType; + } + + internal class GenericTypeEntry + { + private int? _hashCode; + public bool _isRegisteredSuccessfully; + public RuntimeTypeHandle _instantiatedTypeHandle; + public RuntimeTypeHandle _genericTypeDefinitionHandle; + public RuntimeTypeHandle[] _genericTypeArgumentHandles; + + public override int GetHashCode() + { + if (!_hashCode.HasValue) + { + _hashCode = TypeHashingAlgorithms.ComputeGenericInstanceHashCode(_genericTypeDefinitionHandle.GetHashCode(), _genericTypeArgumentHandles); + } + return _hashCode.Value; + } + + public override bool Equals(object obj) + { + // There are no scenarios where we call .Equals to check the componentized equality (we explicitly use IsEqualToEntryByComponentsComparison for that), + // so making sure we revert to the reference equality in case someone calls here. + return base.Equals(obj); + } + + public virtual bool IsEqualToEntryByComponentsComparison(GenericTypeEntry other) + { + if (!other._genericTypeDefinitionHandle.Equals(_genericTypeDefinitionHandle)) + return false; + + if (other._genericTypeArgumentHandles == null) + return false; + + if (other._genericTypeArgumentHandles.Length != _genericTypeArgumentHandles.Length) + return false; + + for (int i = 0; i < _genericTypeArgumentHandles.Length; i++) + if (!other._genericTypeArgumentHandles[i].Equals(_genericTypeArgumentHandles[i])) + return false; + + return true; + } + } + + internal class DynamicGenericTypesHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(GenericTypeLookupData key) + { + return key.LookupHashCode(); + } + protected override bool CompareKeyToValue(GenericTypeLookupData key, GenericTypeEntry value) + { + return key.MatchGenericTypeEntry(value); + } + + protected override int GetValueHashCode(GenericTypeEntry value) + { + return value.GetHashCode(); + } + + protected override bool CompareValueToValue(GenericTypeEntry value1, GenericTypeEntry value2) + { + // Comparisons should *only* be done using the generic type components + return value1.IsEqualToEntryByComponentsComparison(value2); + } + + protected override GenericTypeEntry CreateValueFromKey(GenericTypeLookupData key) + { + // Feature not used by the TypeBuilder + throw NotImplemented.ByDesign; + } + } + + internal abstract class GenericTypeLookupData + { + internal abstract int LookupHashCode(); + internal abstract bool MatchParsedEntry(RuntimeTypeHandle tentativeType); + internal abstract bool MatchGenericTypeEntry(GenericTypeEntry entry); + } + internal class DefTypeBasedGenericTypeLookup : GenericTypeLookupData + { + protected DefType _typeToLookup; + + internal DefTypeBasedGenericTypeLookup(DefType typeToLookup) { _typeToLookup = typeToLookup; } + + internal override int LookupHashCode() { return _typeToLookup.GetHashCode(); } + + internal override bool MatchParsedEntry(RuntimeTypeHandle tentativeType) + { + // + // Entries read from the hashtable are loaded as DefTypes, and compared to the input. + // This lookup is slower than the lookups using RuntimeTypeHandles, but can handle cases where we don't have + // RuntimeTypeHandle values for all of the components of the input DefType, but still need to look it up in case the type + // statically exists and has an existing RuntimeTypeHandle value. + // + TypeSystemContext context = _typeToLookup.Context; + + RuntimeTypeHandle[] parsedArgsHandles; + RuntimeTypeHandle parsedTypeDefinitionHandle = RuntimeAugments.GetGenericInstantiation(tentativeType, out parsedArgsHandles); + + DefType parsedTypeDefinition = (DefType)context.ResolveRuntimeTypeHandle(parsedTypeDefinitionHandle); + Instantiation parsedArgs = context.ResolveRuntimeTypeHandles(parsedArgsHandles); + DefType parsedGenericType = context.ResolveGenericInstantiation(parsedTypeDefinition, parsedArgs); + + return parsedGenericType == _typeToLookup; + } + + internal override bool MatchGenericTypeEntry(GenericTypeEntry entry) + { + TypeSystemContext context = _typeToLookup.Context; + + DefType parsedTypeDefinition = (DefType)context.ResolveRuntimeTypeHandle(entry._genericTypeDefinitionHandle); + Instantiation parsedArgs = context.ResolveRuntimeTypeHandles(entry._genericTypeArgumentHandles); + DefType parsedGenericType = context.ResolveGenericInstantiation(parsedTypeDefinition, parsedArgs); + + return parsedGenericType == _typeToLookup; + } + } + internal class HandleBasedGenericTypeLookup : DefTypeBasedGenericTypeLookup + { + private RuntimeTypeHandle _genericTypeDefinitionHandle; + private RuntimeTypeHandle[] _genericTypeArgumentHandles; + + internal HandleBasedGenericTypeLookup(DefType typeToLookup) : base(typeToLookup) + { + Debug.Assert(typeToLookup != null); + _genericTypeDefinitionHandle = _typeToLookup.GetTypeDefinition().RuntimeTypeHandle; + // _genericTypeArgumentHandles not initialized here to avoid allocation of new array (and it's not used if we initialize _typeToLookup). + } + + internal HandleBasedGenericTypeLookup(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles) : base(null) + { + Debug.Assert(genericTypeArgumentHandles != null); + _genericTypeDefinitionHandle = genericTypeDefinitionHandle; + _genericTypeArgumentHandles = genericTypeArgumentHandles; + } + + internal override int LookupHashCode() + { + return _typeToLookup != null ? _typeToLookup.GetHashCode() : TypeHashingAlgorithms.ComputeGenericInstanceHashCode(_genericTypeDefinitionHandle.GetHashCode(), _genericTypeArgumentHandles); + } + + internal override bool MatchParsedEntry(RuntimeTypeHandle tentativeType) + { + RuntimeTypeHandle parsedTypeDefinitionHandle = RuntimeAugments.GetGenericDefinition(tentativeType); + if (!parsedTypeDefinitionHandle.Equals(_genericTypeDefinitionHandle)) + return false; + + int lookupArity = (_typeToLookup != null ? _typeToLookup.Instantiation.Length : _genericTypeArgumentHandles.Length); + + for (int i = 0; i < lookupArity; i++) + { + RuntimeTypeHandle parsedArg = RuntimeAugments.GetGenericArgument(tentativeType, i); + RuntimeTypeHandle lookupArg = (_typeToLookup != null ? _typeToLookup.Instantiation[i].RuntimeTypeHandle : _genericTypeArgumentHandles[i]); + if (!parsedArg.Equals(lookupArg)) + return false; + } + + return true; + } + + internal override bool MatchGenericTypeEntry(GenericTypeEntry entry) + { + if (!entry._genericTypeDefinitionHandle.Equals(_genericTypeDefinitionHandle)) + return false; + + if (entry._genericTypeArgumentHandles == null) + return false; + + if (_typeToLookup != null) + { + int expectedArity = _typeToLookup.Instantiation.Length; + + if (entry._genericTypeArgumentHandles.Length != expectedArity) + return false; + + for (int i = 0; i < expectedArity; i++) + if (!entry._genericTypeArgumentHandles[i].Equals(_typeToLookup.Instantiation[i].RuntimeTypeHandle)) + return false; + } + else + { + if (entry._genericTypeArgumentHandles.Length != _genericTypeArgumentHandles.Length) + return false; + + for (int i = 0; i < _genericTypeArgumentHandles.Length; i++) + if (!entry._genericTypeArgumentHandles[i].Equals(_genericTypeArgumentHandles[i])) + return false; + } + + return true; + } + } + + internal struct LazyDictionaryContext : IEquatable + { + public IntPtr _context; + public IntPtr _signature; + + public override bool Equals(object obj) + { + if (!(obj is LazyDictionaryContext)) + return false; + return Equals((LazyDictionaryContext)obj); + } + + public bool Equals(LazyDictionaryContext other) + { + return _context == other._context && _signature == other._signature; + } + + public override int GetHashCode() + { + return _context.GetHashCode() ^ _signature.GetHashCode(); + } + } + + private DynamicGenericTypesHashtable _dynamicGenericTypes = new DynamicGenericTypesHashtable(); + private LowLevelDictionary _lazyGenericDictionaries = new LowLevelDictionary(); + + + // + // Return a generic type instantiation using the runtime type system. If the underlying runtime type system does not support + // this operation, return false. + // + internal bool TryLookupConstructedGenericTypeForComponents(GenericTypeLookupData lookupData, out RuntimeTypeHandle runtimeTypeHandle) + { + if (!TryGetStaticGenericTypeForComponents(lookupData, out runtimeTypeHandle)) + if (!TryGetDynamicGenericTypeForComponents(lookupData, out runtimeTypeHandle)) + return false; + + return true; + } + + public bool TryLookupConstructedGenericTypeForComponents(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle) + { + return TryLookupConstructedGenericTypeForComponents(new HandleBasedGenericTypeLookup(genericTypeDefinitionHandle, genericTypeArgumentHandles), out runtimeTypeHandle); + } + + public bool TryLookupConstructedLazyDictionaryForContext(IntPtr context, IntPtr signature, out IntPtr dictionary) + { + Debug.Assert(_typeLoaderLock.IsAcquired); + return _lazyGenericDictionaries.TryGetValue(new LazyDictionaryContext { _context = context, _signature = signature }, out dictionary); + } + + #region Privates + private unsafe bool TryGetDynamicGenericTypeForComponents(GenericTypeLookupData lookupData, out RuntimeTypeHandle runtimeTypeHandle) + { + runtimeTypeHandle = default(RuntimeTypeHandle); + + using (LockHolder.Hold(_dynamicGenericsLock)) + { + GenericTypeEntry entry; + if (!_dynamicGenericTypes.TryGetValue(lookupData, out entry)) + return false; + + if (!entry._isRegisteredSuccessfully) + return false; + + runtimeTypeHandle = entry._instantiatedTypeHandle; + return true; + } + } + + internal unsafe bool TryGetStaticGenericTypeForComponents(GenericTypeLookupData lookupData, out RuntimeTypeHandle runtimeTypeHandle) + { + // Search the hashtable for a generic instantiation match + // TODO multi-file: consider whether we can limit the search somehow, + // i.e. not look at all the modules + + runtimeTypeHandle = default(RuntimeTypeHandle); + + NativeHashtable genericsHashtable; + ExternalReferencesTable externalReferencesLookup; + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + if (!GetHashtableFromBlob(module, ReflectionMapBlob.GenericsHashtable, out genericsHashtable, out externalReferencesLookup)) + continue; + + int lookupHashcode = lookupData.LookupHashCode(); + var enumerator = genericsHashtable.Lookup(lookupHashcode); + + NativeParser entryParser; + while (!(entryParser = enumerator.GetNext()).IsNull) + { + RuntimeTypeHandle tentativeType = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + + if (!lookupData.MatchParsedEntry(tentativeType)) + continue; + + runtimeTypeHandle = tentativeType; + Debug.Assert(RuntimeAugments.IsGenericType(runtimeTypeHandle)); + + return true; + } + } + + return false; + } + #endregion + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericsRegistration.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericsRegistration.cs new file mode 100644 index 00000000000000..1eea5e42b1b543 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericsRegistration.cs @@ -0,0 +1,232 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.NativeFormat; +using Internal.TypeSystem; +using System.Reflection.Runtime.General; + +namespace Internal.Runtime.TypeLoader +{ + public sealed partial class TypeLoaderEnvironment + { + // Container data structures with info on dynamically created method and type instantiations that need registration. + // More convinient to pass around in calls, instead of passing the individual components + internal struct DynamicGenericsRegistrationData + { + public int TypesToRegisterCount; + public IEnumerable TypesToRegister; + public int MethodsToRegisterCount; + public IEnumerable MethodsToRegister; + } + + // To keep the synchronization simple, we execute all dynamic generic type registration/lookups under a global lock + private Lock _dynamicGenericsLock = new Lock(); + + internal void RegisterDynamicGenericTypesAndMethods(DynamicGenericsRegistrationData registrationData) + { + using (LockHolder.Hold(_dynamicGenericsLock)) + { + int registeredTypesCount = 0; + int registeredMethodsCount = 0; + int nativeFormatTypesRegisteredCount = 0; + TypeEntryToRegister[] registeredTypes = null; + GenericMethodEntry[] registeredMethods = null; + + try + { + if (registrationData.TypesToRegister != null) + { + registeredTypes = new TypeEntryToRegister[registrationData.TypesToRegisterCount]; + + foreach (TypeEntryToRegister typeEntry in registrationData.TypesToRegister) + { + // Keep track of registered type handles so that that we can rollback the registration on exception + registeredTypes[registeredTypesCount++] = typeEntry; + + // Information tracked in these dictionaries is (partially) redundant with information tracked by MRT. + // We can save a bit of memory by avoiding the redundancy where possible. For now, we are keeping it simple. + + // Register type -> components mapping first so that we can use it during rollback below + if (typeEntry.GenericTypeEntry != null) + { + GenericTypeEntry registeredTypeEntry = _dynamicGenericTypes.AddOrGetExisting(typeEntry.GenericTypeEntry); + if (registeredTypeEntry != typeEntry.GenericTypeEntry && registeredTypeEntry._isRegisteredSuccessfully) + throw new ArgumentException(SR.Argument_AddingDuplicate); + + registeredTypeEntry._instantiatedTypeHandle = typeEntry.GenericTypeEntry._instantiatedTypeHandle; + registeredTypeEntry._isRegisteredSuccessfully = true; + } + else + { + MetadataType metadataType = typeEntry.MetadataDefinitionType; + IntPtr nonGcStaticFields = IntPtr.Zero; + IntPtr gcStaticFields = IntPtr.Zero; +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING +#if SUPPORTS_R2R_LOADING + uint nonGcStaticsRva = 0; + uint gcStaticsRva = 0; + + // For images where statics are directly embedded in the image, store the information about where + // to find statics info + if (TypeLoaderEnvironment.TryGetStaticsTableEntry(metadataType, out nonGcStaticsRva, out gcStaticsRva)) + { + ModuleInfo moduleInfo = TypeLoaderEnvironment.GetModuleInfoForType(metadataType); + + if (nonGcStaticsRva == 0) + nonGcStaticFields = TypeLoaderEnvironment.NoStaticsData; + else + nonGcStaticFields = moduleInfo.Handle + checked((int)nonGcStaticsRva); + + if (gcStaticsRva == 0) + gcStaticFields = TypeLoaderEnvironment.NoStaticsData; + else + gcStaticFields = moduleInfo.Handle + checked((int)gcStaticsRva); + } +#endif + + TypeSystem.NativeFormat.NativeFormatType nativeFormatType = metadataType as TypeSystem.NativeFormat.NativeFormatType; + if (nativeFormatType != null) + { + RegisterNewNamedTypeRuntimeTypeHandle(new QTypeDefinition(nativeFormatType.MetadataReader, + nativeFormatType.Handle), + nativeFormatType.GetTypeBuilderState().HalfBakedRuntimeTypeHandle, + nonGcStaticFields, + gcStaticFields); + } +#if ECMA_METADATA_SUPPORT + TypeSystem.Ecma.EcmaType ecmaFormatType = metadataType as TypeSystem.Ecma.EcmaType; + if (ecmaFormatType != null) + { + RegisterNewNamedTypeRuntimeTypeHandle(new QTypeDefinition(ecmaFormatType.MetadataReader, + ecmaFormatType.Handle), + ecmaFormatType.GetTypeBuilderState().HalfBakedRuntimeTypeHandle, + nonGcStaticFields, + gcStaticFields); + } +#endif + + nativeFormatTypesRegisteredCount++; +#else + Environment.FailFast("Ready to Run module type?"); +#endif + } + } + } + Debug.Assert(registeredTypesCount == registrationData.TypesToRegisterCount); + + if (registrationData.MethodsToRegister != null) + { + registeredMethods = new GenericMethodEntry[registrationData.MethodsToRegisterCount]; + + foreach (GenericMethodEntry methodEntry in registrationData.MethodsToRegister) + { + Debug.Assert(methodEntry._methodDictionary != IntPtr.Zero); + + // Keep track of registered method dictionaries so that that we can rollback the registration on exception + registeredMethods[registeredMethodsCount++] = methodEntry; + + // Register method dictionary -> components mapping first so that we can use it during rollback below + GenericMethodEntry registeredMethodComponentsEntry = _dynamicGenericMethodComponents.AddOrGetExisting(methodEntry); + if (registeredMethodComponentsEntry != methodEntry && registeredMethodComponentsEntry._isRegisteredSuccessfully) + throw new ArgumentException(SR.Argument_AddingDuplicate); + + GenericMethodEntry registeredMethodEntry = _dynamicGenericMethods.AddOrGetExisting(methodEntry); + if (registeredMethodEntry != methodEntry && registeredMethodEntry._isRegisteredSuccessfully) + throw new ArgumentException(SR.Argument_AddingDuplicate); + + Debug.Assert(registeredMethodComponentsEntry == registeredMethodEntry); + registeredMethodEntry._methodDictionary = methodEntry._methodDictionary; + registeredMethodEntry._isRegisteredSuccessfully = true; + } + } + Debug.Assert(registeredMethodsCount == registrationData.MethodsToRegisterCount); + } + catch + { + // Catch and rethrow any exceptions instead of using finally block. Otherwise, filters that are run during + // the first pass of exception unwind may see partially registered types. + + // TODO: Convert this to filter for better diagnostics once we switch to Roslyn + + // Undo types that were registered. There should be no memory allocations or other failure points. + try + { + for (int i = 0; i < registeredTypesCount; i++) + { + var typeEntry = registeredTypes[i]; + // There is no Remove feature in the LockFreeReaderHashtable... + if (typeEntry.GenericTypeEntry != null) + { + GenericTypeEntry failedEntry = _dynamicGenericTypes.GetValueIfExists(typeEntry.GenericTypeEntry); + if (failedEntry != null) + failedEntry._isRegisteredSuccessfully = false; + } + else + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + TypeSystem.NativeFormat.NativeFormatType nativeFormatType = typeEntry.MetadataDefinitionType as TypeSystem.NativeFormat.NativeFormatType; + if (nativeFormatType != null) + { + UnregisterNewNamedTypeRuntimeTypeHandle(new QTypeDefinition(nativeFormatType.MetadataReader, + nativeFormatType.Handle), + nativeFormatType.GetTypeBuilderState().HalfBakedRuntimeTypeHandle); + } +#if ECMA_METADATA_SUPPORT + TypeSystem.Ecma.EcmaType ecmaFormatType = typeEntry.MetadataDefinitionType as TypeSystem.Ecma.EcmaType; + if (ecmaFormatType != null) + { + UnregisterNewNamedTypeRuntimeTypeHandle(new QTypeDefinition(ecmaFormatType.MetadataReader, + ecmaFormatType.Handle), + ecmaFormatType.GetTypeBuilderState().HalfBakedRuntimeTypeHandle); + } +#endif +#else + Environment.FailFast("Ready to Run module type?"); +#endif + } + } + for (int i = 0; i < registeredMethodsCount; i++) + { + // There is no Remove feature in the LockFreeReaderHashtable... + GenericMethodEntry failedEntry = _dynamicGenericMethods.GetValueIfExists(registeredMethods[i]); + if (failedEntry != null) + failedEntry._isRegisteredSuccessfully = false; + + failedEntry = _dynamicGenericMethodComponents.GetValueIfExists(registeredMethods[i]); + if (failedEntry != null) + failedEntry._isRegisteredSuccessfully = false; + } + } + catch (Exception e) + { + // Catch any exceptions and fail fast just in case + Environment.FailFast("Exception during registration rollback", e); + } + + throw; + } + + if (nativeFormatTypesRegisteredCount > 0) + FinishAddingNewNamedTypes(); + } + } + + public void RegisterConstructedLazyDictionaryForContext(IntPtr context, IntPtr signature, IntPtr dictionary) + { + Debug.Assert(_typeLoaderLock.IsAcquired); + _lazyGenericDictionaries.Add(new LazyDictionaryContext { _context = context, _signature = signature }, dictionary); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs new file mode 100644 index 00000000000000..97d5c73f6741ed --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs @@ -0,0 +1,519 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +using System.Reflection.Runtime.General; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.Metadata.NativeFormat; +using Internal.NativeFormat; +using Internal.TypeSystem; + +namespace Internal.Runtime.TypeLoader +{ + /// + /// Helper structure describing all info needed to construct dynamic field accessors. + /// + public struct FieldAccessMetadata + { + /// + /// Module containing the relevant metadata, null when not found + /// + public TypeManagerHandle MappingTableModule; + + /// + /// Cookie for field access. This field is set to IntPtr.Zero when the value is not available. + /// + public IntPtr Cookie; + + /// + /// Field access and characteristics bitmask. + /// + public FieldTableFlags Flags; + + /// + /// Field offset, address or cookie based on field access type. + /// + public int Offset; + } + + [StructLayout(LayoutKind.Sequential)] + struct ThreadStaticFieldOffsets + { + public uint StartingOffsetInTlsBlock; // Offset in the TLS block containing the thread static fields of a given type + public uint FieldOffset; // Offset of a thread static field from the start of its containing type's TLS fields block + // (in other words, the address of a field is 'TLS block + StartingOffsetInTlsBlock + FieldOffset') + } + + public sealed partial class TypeLoaderEnvironment + { + /// + /// Try to look up field access info for given canon in metadata blobs for all available modules. + /// + /// Metadata reader for the declaring type + /// Declaring type for the method + /// Field handle + /// Output - metadata information for field accessor construction + /// true when found, false otherwise + public static bool TryGetFieldAccessMetadata( + MetadataReader metadataReader, + RuntimeTypeHandle runtimeTypeHandle, + FieldHandle fieldHandle, + out FieldAccessMetadata fieldAccessMetadata) + { + fieldAccessMetadata = default(FieldAccessMetadata); + + if (TryGetFieldAccessMetadataFromFieldAccessMap( + metadataReader, + runtimeTypeHandle, + fieldHandle, + CanonicalFormKind.Specific, + ref fieldAccessMetadata)) + { + return true; + } + + if (TryGetFieldAccessMetadataFromFieldAccessMap( + metadataReader, + runtimeTypeHandle, + fieldHandle, + CanonicalFormKind.Universal, + ref fieldAccessMetadata)) + { + return true; + } + + TypeSystemContext context = TypeSystemContextFactory.Create(); + + bool success = TryGetFieldAccessMetadataFromNativeFormatMetadata( + metadataReader, + runtimeTypeHandle, + fieldHandle, + context, + ref fieldAccessMetadata); + + TypeSystemContextFactory.Recycle(context); + + return success; + } + + /// + /// Try to look up field access info for given canon in metadata blobs for all available modules. + /// + /// Metadata reader for the declaring type + /// Declaring type for the method + /// Field handle + /// Canonical form to use + /// Output - metadata information for field accessor construction + /// true when found, false otherwise + private static unsafe bool TryGetFieldAccessMetadataFromFieldAccessMap( + MetadataReader metadataReader, + RuntimeTypeHandle declaringTypeHandle, + FieldHandle fieldHandle, + CanonicalFormKind canonFormKind, + ref FieldAccessMetadata fieldAccessMetadata) + { + CanonicallyEquivalentEntryLocator canonWrapper = new CanonicallyEquivalentEntryLocator(declaringTypeHandle, canonFormKind); + TypeManagerHandle fieldHandleModule = ModuleList.Instance.GetModuleForMetadataReader(metadataReader); + bool isDynamicType = RuntimeAugments.IsDynamicType(declaringTypeHandle); + string fieldName = null; + RuntimeTypeHandle declaringTypeHandleDefinition = Instance.GetTypeDefinition(declaringTypeHandle); + + foreach (NativeFormatModuleInfo mappingTableModule in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(declaringTypeHandle))) + { + NativeReader fieldMapReader; + if (!TryGetNativeReaderForBlob(mappingTableModule, ReflectionMapBlob.FieldAccessMap, out fieldMapReader)) + continue; + + NativeParser fieldMapParser = new NativeParser(fieldMapReader, 0); + NativeHashtable fieldHashtable = new NativeHashtable(fieldMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + if (!externalReferences.InitializeCommonFixupsTable(mappingTableModule)) + { + continue; + } + + var lookup = fieldHashtable.Lookup(canonWrapper.LookupHashCode); + + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + // Grammar of a hash table entry: + // Flags + DeclaringType + MdHandle or Name + Cookie or Ordinal or Offset + + FieldTableFlags entryFlags = (FieldTableFlags)entryParser.GetUnsigned(); + + if ((canonFormKind == CanonicalFormKind.Universal) != ((entryFlags & FieldTableFlags.IsUniversalCanonicalEntry) != 0)) + continue; + + RuntimeTypeHandle entryDeclaringTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!entryDeclaringTypeHandle.Equals(declaringTypeHandle) + && !canonWrapper.IsCanonicallyEquivalent(entryDeclaringTypeHandle)) + continue; + + if ((entryFlags & FieldTableFlags.HasMetadataHandle) != 0) + { + Handle entryFieldHandle = (((int)HandleType.Field << 24) | (int)entryParser.GetUnsigned()).AsHandle(); + if (!fieldHandle.Equals(entryFieldHandle)) + continue; + } + else + { + if (fieldName == null) + { + QTypeDefinition qTypeDefinition; + + bool success = Instance.TryGetMetadataForNamedType( + declaringTypeHandleDefinition, + out qTypeDefinition); + Debug.Assert(success); + + MetadataReader nativeFormatMetadataReader = qTypeDefinition.NativeFormatReader; + + fieldName = nativeFormatMetadataReader.GetString(fieldHandle.GetField(nativeFormatMetadataReader).Name); + } + + string entryFieldName = entryParser.GetString(); + + if (fieldName != entryFieldName) + continue; + } + + int fieldOffset = -1; + IntPtr fieldAddressCookie = IntPtr.Zero; + + if (canonFormKind == CanonicalFormKind.Universal) + { + if (!TypeLoaderEnvironment.Instance.TryGetFieldOffset(declaringTypeHandle, entryParser.GetUnsigned() /* field ordinal */, out fieldOffset)) + { + Debug.Assert(false); + return false; + } + } + else + { + if ((entryFlags & FieldTableFlags.FieldOffsetEncodedDirectly) != 0) + { + fieldOffset = (int)entryParser.GetUnsigned(); + } + else + { + fieldOffset = 0; + fieldAddressCookie = externalReferences.GetAddressFromIndex(entryParser.GetUnsigned()); + + FieldTableFlags storageClass = entryFlags & FieldTableFlags.StorageClass; + if (storageClass == FieldTableFlags.GCStatic || storageClass == FieldTableFlags.ThreadStatic) + fieldOffset = (int)entryParser.GetUnsigned(); + } + } + + fieldAccessMetadata.MappingTableModule = mappingTableModule.Handle; + fieldAccessMetadata.Cookie = fieldAddressCookie; + fieldAccessMetadata.Flags = entryFlags; + fieldAccessMetadata.Offset = fieldOffset; + return true; + } + } + + return false; + } + + private enum FieldAccessStaticDataKind + { + NonGC, + GC, + TLS + } + + private static class FieldAccessFlags + { + public const int RemoteStaticFieldRVA = unchecked((int)0x80000000); + } + + /// + /// This structure describes one static field in an external module. It is represented + /// by an indirection cell pointer and an offset within the cell - the final address + /// of the static field is essentially *IndirectionCell + Offset. + /// + [StructLayout(LayoutKind.Sequential)] + private struct RemoteStaticFieldDescriptor + { + public unsafe IntPtr* IndirectionCell; + public int Offset; + } + + /// + /// Resolve a given 32-bit integer (staticFieldRVA) representing a static field address. + /// For "local" static fields residing in the module given by moduleHandle, staticFieldRVA + /// directly contains the RVA of the static field. For remote static fields residing in other + /// modules, staticFieldRVA has the highest bit set (FieldAccessFlags.RemoteStaticFieldRVA) + /// and it contains the RVA of a RemoteStaticFieldDescriptor structure residing in the module + /// given by moduleHandle that holds a pointer to the indirection cell + /// of the remote static field and its offset within the cell. + /// + /// Reference module handle used for static field lookup + /// + /// RVA of static field for local fields; for remote fields, RVA of a RemoteStaticFieldDescriptor + /// structure for the field or-ed with the FieldAccessFlags.RemoteStaticFieldRVA bit + /// + public static unsafe IntPtr RvaToNonGenericStaticFieldAddress(TypeManagerHandle moduleHandle, int staticFieldRVA) + { + // TODO: implement for CoreRT + throw new NotImplementedException(); + } + + /// + /// Try to look up non-gc/gc static effective field bases for a non-generic non-dynamic type. + /// + /// Declaring type for the method + /// type of static base to find + /// Output - statics region address info + /// true when found, false otherwise + private static unsafe bool TryGetStaticFieldBaseFromFieldAccessMap( + RuntimeTypeHandle declaringTypeHandle, + FieldAccessStaticDataKind fieldAccessKind, + out IntPtr staticsRegionAddress) + { + staticsRegionAddress = IntPtr.Zero; + byte* comparableStaticRegionAddress = null; + + CanonicallyEquivalentEntryLocator canonWrapper = new CanonicallyEquivalentEntryLocator(declaringTypeHandle, CanonicalFormKind.Specific); + + // This function only finds results for non-dynamic, non-generic types + if (RuntimeAugments.IsDynamicType(declaringTypeHandle) || RuntimeAugments.IsGenericType(declaringTypeHandle)) + return false; + + foreach (NativeFormatModuleInfo mappingTableModule in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(declaringTypeHandle))) + { + NativeReader fieldMapReader; + if (!TryGetNativeReaderForBlob(mappingTableModule, ReflectionMapBlob.FieldAccessMap, out fieldMapReader)) + continue; + + NativeParser fieldMapParser = new NativeParser(fieldMapReader, 0); + NativeHashtable fieldHashtable = new NativeHashtable(fieldMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + if (!externalReferences.InitializeCommonFixupsTable(mappingTableModule)) + { + continue; + } + + var lookup = fieldHashtable.Lookup(canonWrapper.LookupHashCode); + + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + // Grammar of a hash table entry: + // Flags + DeclaringType + MdHandle or Name + Cookie or Ordinal or Offset + + FieldTableFlags entryFlags = (FieldTableFlags)entryParser.GetUnsigned(); + FieldTableFlags storageClass = entryFlags & FieldTableFlags.StorageClass; + + Debug.Assert((entryFlags & FieldTableFlags.IsUniversalCanonicalEntry) == 0); + + if (storageClass == FieldTableFlags.Instance) + continue; + + switch (fieldAccessKind) + { + case FieldAccessStaticDataKind.NonGC: + if (storageClass != FieldTableFlags.NonGCStatic) + continue; + break; + case FieldAccessStaticDataKind.GC: + if (storageClass != FieldTableFlags.GCStatic) + continue; + break; + + case FieldAccessStaticDataKind.TLS: + default: + // TODO! TLS statics + Environment.FailFast("TLS static field access not yet supported"); + return false; + } + + RuntimeTypeHandle entryDeclaringTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!entryDeclaringTypeHandle.Equals(declaringTypeHandle)) + continue; + + if ((entryFlags & FieldTableFlags.HasMetadataHandle) != 0) + { + // skip metadata handle + entryParser.GetUnsigned(); + } + else + { + // skip field name + entryParser.SkipString(); + } + + int cookieOrOffsetOrOrdinal = (int)entryParser.GetUnsigned(); + int fieldOffset = (int)externalReferences.GetRvaFromIndex((uint)cookieOrOffsetOrOrdinal); + + IntPtr fieldAddress = RvaToNonGenericStaticFieldAddress( + mappingTableModule.Handle, fieldOffset); + + if ((comparableStaticRegionAddress == null) || (comparableStaticRegionAddress > fieldAddress.ToPointer())) + { + comparableStaticRegionAddress = (byte*)fieldAddress.ToPointer(); + } + } + + // Static fields for a type can only be found in at most one module + if (comparableStaticRegionAddress != null) + break; + } + + if (comparableStaticRegionAddress != null) + { + staticsRegionAddress = new IntPtr(comparableStaticRegionAddress); + return true; + } + else + { + return false; + } + } + + /// + /// Try to figure out field access information based on type metadata for native format types. + /// + /// Metadata reader for the declaring type + /// Declaring type for the method + /// Field handle + /// Type system context + /// Output - metadata information for field accessor construction + /// true when found, false otherwise + private static bool TryGetFieldAccessMetadataFromNativeFormatMetadata( + MetadataReader metadataReader, + RuntimeTypeHandle declaringTypeHandle, + FieldHandle fieldHandle, + TypeSystemContext context, + ref FieldAccessMetadata fieldAccessMetadata) + { + Field field = metadataReader.GetField(fieldHandle); + string fieldName = metadataReader.GetString(field.Name); + + TypeDesc declaringType = context.ResolveRuntimeTypeHandle(declaringTypeHandle); + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + if (declaringType is MetadataType) + { + return TryGetFieldAccessMetadataForNativeFormatType(declaringType, fieldName, ref fieldAccessMetadata); + } +#endif + + return false; + } + + /// + /// Locate field on native format type and fill in the field access flags and offset. + /// + /// Metadata reader for the declaring type + /// Field name + /// Output - metadata information for field accessor construction + /// true when found, false otherwise + private static bool TryGetFieldAccessMetadataForNativeFormatType( + TypeDesc type, + string fieldName, + ref FieldAccessMetadata fieldAccessMetadata) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + FieldDesc fieldDesc = type.GetField(fieldName); + if (fieldDesc == null) + { + return false; + } + + fieldAccessMetadata.MappingTableModule = default(TypeManagerHandle); + +#if SUPPORTS_R2R_LOADING + fieldAccessMetadata.MappingTableModule = ModuleList.Instance.GetModuleForMetadataReader(((NativeFormatType)type.GetTypeDefinition()).MetadataReader); +#endif + fieldAccessMetadata.Offset = fieldDesc.Offset.AsInt; + fieldAccessMetadata.Flags = FieldTableFlags.HasMetadataHandle; + + if (fieldDesc.IsThreadStatic) + { + // Specify that the data is thread local + fieldAccessMetadata.Flags |= FieldTableFlags.ThreadStatic; + + // Specify that the general purpose field access routine that only relies on offset should be used. + fieldAccessMetadata.Flags |= FieldTableFlags.IsUniversalCanonicalEntry; + } + else if (fieldDesc.IsStatic) + { + uint nonGcStaticsRVA = 0; + uint gcStaticsRVA = 0; + bool nonGenericCase = false; + + if (type is MetadataType) + { + // Static fields on Non-Generic types are contained within the module, and their offsets + // are adjusted by their static rva base. + nonGenericCase = true; + +#if SUPPORTS_R2R_LOADING + if (!TryGetStaticsTableEntry((MetadataType)type, nonGcStaticsRVA: out nonGcStaticsRVA, gcStaticsRVA: out gcStaticsRVA)) +#endif + { + Environment.FailFast( + "Failed to locate statics table entry for for field '" + + fieldName + + "' on type " + + type.ToString()); + } + } + + if (fieldDesc.HasGCStaticBase) + { + if ((gcStaticsRVA == 0) && nonGenericCase) + { + Environment.FailFast( + "GC statics region was not found for field '" + + fieldName + + "' on type " + + type.ToString()); + } + fieldAccessMetadata.Offset += (int)gcStaticsRVA; + fieldAccessMetadata.Flags |= FieldTableFlags.GCStatic; + } + else + { + if ((nonGcStaticsRVA == 0) && nonGenericCase) + { + Environment.FailFast( + "Non-GC statics region was not found for field '" + + fieldName + + "' on type " + + type.ToString()); + } + fieldAccessMetadata.Offset += (int)nonGcStaticsRVA; + fieldAccessMetadata.Flags |= FieldTableFlags.NonGCStatic; + } + return true; + } + else + { + // Instance field + fieldAccessMetadata.Flags |= FieldTableFlags.Instance; + } + + return true; +#else + return false; +#endif + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs new file mode 100644 index 00000000000000..c586cccadc9b6b --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs @@ -0,0 +1,638 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.NativeFormat; +using Internal.TypeSystem; + +namespace Internal.Runtime.TypeLoader +{ + public sealed partial class TypeLoaderEnvironment + { +#if GVM_RESOLUTION_TRACE + private string GetTypeNameDebug(RuntimeTypeHandle rtth) + { + string result; + + if (RuntimeAugments.IsGenericType(rtth)) + { + RuntimeTypeHandle[] typeArgumentsHandles; + RuntimeTypeHandle openTypeDef = RuntimeAugments.GetGenericInstantiation(rtth, out typeArgumentsHandles); ; + result = GetTypeNameDebug(openTypeDef) + "<"; + for (int i = 0; i < typeArgumentsHandles.Length; i++) + result += (i == 0 ? "" : ",") + GetTypeNameDebug(typeArgumentsHandles[i]); + return result + ">"; + } + else + { + System.Reflection.Runtime.General.QTypeDefinition qTypeDefinition; + + // Check if we have metadata. + if (Instance.TryGetMetadataForNamedType(rtth, out qTypeDefinition)) + return qTypeDefinition.NativeFormatHandle.GetFullName(qTypeDefinition.NativeFormatReader); + } + + return rtth.LowLevelToStringRawEETypeAddress(); + } +#endif + + public bool TryGetGenericVirtualTargetForTypeAndSlot(RuntimeTypeHandle targetHandle, ref RuntimeTypeHandle declaringType, RuntimeTypeHandle[] genericArguments, ref string methodName, ref RuntimeSignature methodSignature, bool lookForDefaultImplementation, out IntPtr methodPointer, out IntPtr dictionaryPointer, out bool slotUpdated) + { + MethodNameAndSignature methodNameAndSignature = new MethodNameAndSignature(methodName, methodSignature); + +#if GVM_RESOLUTION_TRACE + Debug.WriteLine("GVM resolution starting for " + GetTypeNameDebug(declaringType) + "." + methodNameAndSignature.Name + "(...) on a target of type " + GetTypeNameDebug(targetHandle) + " ..."); +#endif + + if (RuntimeAugments.IsInterface(declaringType)) + { + if (!ResolveInterfaceGenericVirtualMethodSlot(targetHandle, lookForDefaultImplementation, ref declaringType, ref methodNameAndSignature)) + { + methodPointer = dictionaryPointer = IntPtr.Zero; + slotUpdated = false; + return false; + } + + if (RuntimeAugments.IsInterface(declaringType)) + { + slotUpdated = false; + if (!TryGetGenericVirtualMethodPointer(declaringType, methodNameAndSignature, genericArguments, out methodPointer, out dictionaryPointer)) + { + var sb = new System.Text.StringBuilder(); + sb.AppendLine("Generic virtual method pointer lookup failure."); + sb.AppendLine(); + sb.AppendLine("Declaring type handle: " + declaringType.LowLevelToStringRawEETypeAddress()); + sb.AppendLine("Target type handle: " + targetHandle.LowLevelToStringRawEETypeAddress()); + sb.AppendLine("Method name: " + methodNameAndSignature.Name); + sb.AppendLine("Instantiation:"); + for (int i = 0; i < genericArguments.Length; i++) + { + sb.AppendLine(" Argument " + i.LowLevelToString() + ": " + genericArguments[i].LowLevelToStringRawEETypeAddress()); + } + + Environment.FailFast(sb.ToString()); + } + } + else + { + methodPointer = IntPtr.Zero; + dictionaryPointer = IntPtr.Zero; + slotUpdated = true; + methodName = methodNameAndSignature.Name; + methodSignature = methodNameAndSignature.Signature; + } + return true; + } + else + { + slotUpdated = false; + return ResolveGenericVirtualMethodTarget(targetHandle, declaringType, genericArguments, methodNameAndSignature, out methodPointer, out dictionaryPointer); + } + } + + private MethodNameAndSignature GetMethodNameAndSignatureFromNativeReader(NativeReader nativeLayoutReader, TypeManagerHandle moduleHandle, uint nativeLayoutOffset) + { + NativeParser parser = new NativeParser(nativeLayoutReader, nativeLayoutOffset); + + string methodName = parser.GetString(); + + // Signatures are indirected to through a relative offset so that we don't have to parse them + // when not comparing signatures (parsing them requires resolving types and is tremendously + // expensive). + NativeParser sigParser = parser.GetParserFromRelativeOffset(); + RuntimeSignature methodSig = RuntimeSignature.CreateFromNativeLayoutSignature(moduleHandle, sigParser.Offset); + + return new MethodNameAndSignature(methodName, methodSig); + } + + private RuntimeTypeHandle GetOpenTypeDefinition(RuntimeTypeHandle typeHandle, out RuntimeTypeHandle[] typeArgumentsHandles) + { + if (RuntimeAugments.IsGenericType(typeHandle)) + return RuntimeAugments.GetGenericInstantiation(typeHandle, out typeArgumentsHandles); + + typeArgumentsHandles = null; + return typeHandle; + } + + private RuntimeTypeHandle GetTypeDefinition(RuntimeTypeHandle typeHandle) + { + if (RuntimeAugments.IsGenericType(typeHandle)) + return RuntimeAugments.GetGenericDefinition(typeHandle); + + return typeHandle; + } + + private bool FindMatchingInterfaceSlot(NativeFormatModuleInfo module, NativeReader nativeLayoutReader, ref NativeParser entryParser, ref ExternalReferencesTable extRefs, ref RuntimeTypeHandle declaringType, ref MethodNameAndSignature methodNameAndSignature, RuntimeTypeHandle instanceTypeHandle, RuntimeTypeHandle openTargetTypeHandle, RuntimeTypeHandle[] targetTypeInstantiation, bool variantDispatch, bool defaultMethods) + { + uint numTargetImplementations = entryParser.GetUnsigned(); + +#if GVM_RESOLUTION_TRACE + Debug.WriteLine(" :: Declaring type = " + GetTypeNameDebug(declaringType)); + Debug.WriteLine(" :: Target type = " + GetTypeNameDebug(openTargetTypeHandle)); +#endif + + for (uint j = 0; j < numTargetImplementations; j++) + { + uint nameAndSigToken = entryParser.GetUnsigned(); + + MethodNameAndSignature targetMethodNameAndSignature = null; + RuntimeTypeHandle targetTypeHandle = default; + bool isDefaultInterfaceMethodImplementation; + + if (nameAndSigToken != SpecialGVMInterfaceEntry.Diamond && nameAndSigToken != SpecialGVMInterfaceEntry.Reabstraction) + { + targetMethodNameAndSignature = GetMethodNameAndSignatureFromNativeReader(nativeLayoutReader, module.Handle, nameAndSigToken); + targetTypeHandle = extRefs.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + isDefaultInterfaceMethodImplementation = RuntimeAugments.IsInterface(targetTypeHandle); +#if GVM_RESOLUTION_TRACE + Debug.WriteLine(" Searching for GVM implementation on targe type = " + GetTypeNameDebug(targetTypeHandle)); +#endif + } + else + { + isDefaultInterfaceMethodImplementation = true; + } + + uint numIfaceImpls = entryParser.GetUnsigned(); + + for (uint k = 0; k < numIfaceImpls; k++) + { + RuntimeTypeHandle implementingTypeHandle = extRefs.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + +#if GVM_RESOLUTION_TRACE + Debug.WriteLine(" -> Current implementing type = " + GetTypeNameDebug(implementingTypeHandle)); +#endif + + uint numIfaceSigs = entryParser.GetUnsigned(); + + if (!openTargetTypeHandle.Equals(implementingTypeHandle) + || defaultMethods != isDefaultInterfaceMethodImplementation) + { + // Skip over signatures data + for (uint l = 0; l < numIfaceSigs; l++) + entryParser.GetUnsigned(); + + continue; + } + + for (uint l = 0; l < numIfaceSigs; l++) + { + RuntimeTypeHandle currentIfaceTypeHandle = default(RuntimeTypeHandle); + + NativeParser ifaceSigParser = new NativeParser(nativeLayoutReader, entryParser.GetUnsigned()); + + if (TypeLoaderEnvironment.Instance.GetTypeFromSignatureAndContext(ref ifaceSigParser, module.Handle, targetTypeInstantiation, null, out currentIfaceTypeHandle)) + { +#if GVM_RESOLUTION_TRACE + Debug.WriteLine(" -> Current interface on type = " + GetTypeNameDebug(currentIfaceTypeHandle)); +#endif + Debug.Assert(!currentIfaceTypeHandle.IsNull()); + + if ((!variantDispatch && declaringType.Equals(currentIfaceTypeHandle)) || + (variantDispatch && RuntimeAugments.IsAssignableFrom(declaringType, currentIfaceTypeHandle))) + { +#if GVM_RESOLUTION_TRACE + Debug.WriteLine(" " + (declaringType.Equals(currentIfaceTypeHandle) ? "Exact" : "Variant-compatible") + " match found on this target type!"); +#endif + if (targetMethodNameAndSignature == null) + { + if (nameAndSigToken == SpecialGVMInterfaceEntry.Diamond) + { + throw new AmbiguousImplementationException(); + } + else + { + Debug.Assert(nameAndSigToken == SpecialGVMInterfaceEntry.Reabstraction); + throw new EntryPointNotFoundException(); + } + } + + // We found the GVM slot target for the input interface GVM call, so let's update the interface GVM slot and return success to the caller + if (!RuntimeAugments.IsInterface(targetTypeHandle) || !RuntimeAugments.IsGenericTypeDefinition(targetTypeHandle)) + { + // Not a default interface method or default interface method on a non-generic type. + // We have a usable type handle. + declaringType = targetTypeHandle; + } + else if (RuntimeAugments.IsGenericType(currentIfaceTypeHandle) && RuntimeAugments.GetGenericDefinition(currentIfaceTypeHandle).Equals(targetTypeHandle)) + { + // Default interface method implemented on the same type that declared the slot. + // Use the instantiation as-is from what we found. + declaringType = currentIfaceTypeHandle; + } + else + { + declaringType = default; + + // Default interface method implemented on a different generic interface. + // We need to find a usable instantiation. There should be only one match because we + // would be dealing with a diamond otherwise. + int numInstanceInterfaces = RuntimeAugments.GetInterfaceCount(instanceTypeHandle); + for (int instIntfIndex = 0; instIntfIndex < numInstanceInterfaces; instIntfIndex++) + { + RuntimeTypeHandle instIntf = RuntimeAugments.GetInterface(instanceTypeHandle, instIntfIndex); + if (RuntimeAugments.IsGenericType(instIntf) + && RuntimeAugments.GetGenericDefinition(instIntf).Equals(targetTypeHandle)) + { + // Got a potential interface. Check if the implementing interface is in the interface + // list. We don't want IsAssignableFrom because we need an exact match. + int numIntInterfaces = RuntimeAugments.GetInterfaceCount(instIntf); + for (int intIntfIndex = 0; intIntfIndex < numIntInterfaces; intIntfIndex++) + { + if (RuntimeAugments.GetInterface(instIntf, intIntfIndex).Equals(currentIfaceTypeHandle)) + { + Debug.Assert(declaringType.IsNull()); + declaringType = instIntf; +#if !DEBUG + break; +#endif + } + } +#if !DEBUG + if (!declaringType.IsNull()) + break; +#endif + } + } + + Debug.Assert(!declaringType.IsNull()); + } + + methodNameAndSignature = targetMethodNameAndSignature; + return true; + } + } + } + } + } + + return false; + } + + private bool ResolveInterfaceGenericVirtualMethodSlot(RuntimeTypeHandle targetTypeHandle, bool lookForDefaultImplementation, ref RuntimeTypeHandle declaringType, ref MethodNameAndSignature methodNameAndSignature) + { + if (IsPregeneratedOrTemplateRuntimeTypeHandle(targetTypeHandle)) + { + // If the target type isn't dynamic, or at least is template type generated, the static lookup logic is what we want. + return ResolveInterfaceGenericVirtualMethodSlot_Static(targetTypeHandle, lookForDefaultImplementation, ref declaringType, ref methodNameAndSignature); + } + else + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + TypeSystemContext context = TypeSystemContextFactory.Create(); + DefType targetType = (DefType)context.ResolveRuntimeTypeHandle(targetTypeHandle); + + // Method being called... + MethodDesc targetVirtualMethod = ResolveTypeHandleAndMethodNameAndSigToVirtualMethodDesc(context, declaringType, methodNameAndSignature); + + if (targetVirtualMethod == null) + { + // If we can't find the method in the type system, it must only be present in the static environment. Search there instead. + TypeSystemContextFactory.Recycle(context); + return ResolveInterfaceGenericVirtualMethodSlot_Static(targetTypeHandle, ref declaringType, ref methodNameAndSignature); + } + + TypeDesc instanceDefTypeToExamine; + MethodDesc newlyFoundVirtualMethod = LazyVTableResolver.ResolveInterfaceMethodToVirtualMethod(targetType, out instanceDefTypeToExamine, targetVirtualMethod); + + targetVirtualMethod = newlyFoundVirtualMethod; + + // The pregenerated base type must be the one that implements the interface method + // Call into Redhawk to deal with this. + if ((newlyFoundVirtualMethod == null) && (instanceDefTypeToExamine != null)) + { + TypeSystemContextFactory.Recycle(context); + // If we can't find the method in the type system, the overload must be defined in the static environment. Search there instead. + return ResolveInterfaceGenericVirtualMethodSlot_Static(instanceDefTypeToExamine.GetRuntimeTypeHandle(), ref declaringType, ref methodNameAndSignature); + } + + declaringType = targetVirtualMethod.OwningType.GetRuntimeTypeHandle(); + methodNameAndSignature = targetVirtualMethod.NameAndSignature; + TypeSystemContextFactory.Recycle(context); + return true; +#else + Environment.FailFast("GVM Resolution for non template or pregenerated type"); + return false; +#endif + } + } + + private bool ResolveInterfaceGenericVirtualMethodSlot_Static(RuntimeTypeHandle targetTypeHandle, bool lookForDefaultImplementation, ref RuntimeTypeHandle declaringType, ref MethodNameAndSignature methodNameAndSignature) + { + // Get the open type definition of the containing type of the generic virtual method being resolved + RuntimeTypeHandle openCallingTypeHandle = GetTypeDefinition(declaringType); + + // Get the open type definition of the current type of the object instance on which the GVM is being resolved + RuntimeTypeHandle openTargetTypeHandle; + RuntimeTypeHandle[] targetTypeInstantiation; + openTargetTypeHandle = GetOpenTypeDefinition(targetTypeHandle, out targetTypeInstantiation); + +#if GVM_RESOLUTION_TRACE + Debug.WriteLine("INTERFACE GVM call = " + GetTypeNameDebug(declaringType) + "." + methodNameAndSignature.Name); +#endif + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(openTargetTypeHandle))) + { + NativeReader gvmTableReader; + if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.InterfaceGenericVirtualMethodTable, out gvmTableReader)) + continue; + + NativeReader nativeLayoutReader; + if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.NativeLayoutInfo, out nativeLayoutReader)) + continue; + + NativeParser gvmTableParser = new NativeParser(gvmTableReader, 0); + NativeHashtable gvmHashtable = new NativeHashtable(gvmTableParser); + + ExternalReferencesTable extRefs = default(ExternalReferencesTable); + extRefs.InitializeCommonFixupsTable(module); + + var lookup = gvmHashtable.Lookup(openCallingTypeHandle.GetHashCode()); + + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle interfaceTypeHandle = extRefs.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!openCallingTypeHandle.Equals(interfaceTypeHandle)) + continue; + + uint nameAndSigToken = entryParser.GetUnsigned(); + MethodNameAndSignature interfaceMethodNameAndSignature = GetMethodNameAndSignatureFromNativeReader(nativeLayoutReader, module.Handle, nameAndSigToken); + + if (!interfaceMethodNameAndSignature.Equals(methodNameAndSignature)) + continue; + + // For each of the possible GVM slot targets for the current interface call, we will do the following: + // + // Step 1: Scan the types that currently provide implementations for the current GVM slot target, and look + // for ones that match the target object's type. + // + // Step 2: For each type that we find in step #1, get a list of all the interfaces that the current GVM target + // provides an implementation for + // + // Step 3: For each interface in the list in step #2, parse the signature of that interface, do the generic argument + // substitution (in case of a generic interface), and check if this interface signature is assignable from the + // calling interface signature (from the name and sig input). if there is an exact match based on + // interface type, then we've found the right slot. Otherwise, re-scan the entry again and see if some interface + // type is compatible with the initial slots interface by means of variance. + // This is done by calling the TypeLoaderEnvironment helper function. + // + // Example: + // public interface IFoo + // { + // string M1(); + // } + // public class Foo1 : IFoo, IFoo, U> + // { + // string IFoo.M1() { ... } + // public virtual string M1() { ... } + // } + // public class Foo2 : Foo1, IFoo + // { + // string IFoo.M1() { ... } + // } + // + // GVM Table layout for IFoo.M1: + // { + // InterfaceTypeHandle = IFoo + // InterfaceMethodNameAndSignature = { "M1", SigOf(string M1) } + // GVMTargetSlots[] = + // { + // { + // TargetMethodNameAndSignature = { "M1", SigOf(M1) } + // TargetTypeHandle = Foo1 + // ImplementingTypes[] = { + // ImplementingTypeHandle = Foo1 + // ImplementedInterfacesSignatures[] = { SigOf(IFoo) } + // } + // }, + // + // { + // TargetMethodNameAndSignature = { "M1", SigOf(M1) } + // TargetTypeHandle = Foo1 + // ImplementingTypes[] = { + // ImplementingTypeHandle = Foo1 + // ImplementedInterfacesSignatures[] = { SigOf(IFoo, !1>) } + // } + // }, + // + // { + // TargetMethodNameAndSignature = { "M1", SigOf(M1) } + // TargetTypeHandle = Foo2 + // ImplementingTypes = { + // ImplementingTypeHandle = Foo2 + // ImplementedInterfacesSignatures[] = { SigOf(IFoo) } + // } + // }, + // } + // } + // + + uint currentOffset = entryParser.Offset; + + // Non-variant dispatch of a variant generic interface generic virtual method. + if (FindMatchingInterfaceSlot(module, nativeLayoutReader, ref entryParser, ref extRefs, ref declaringType, ref methodNameAndSignature, targetTypeHandle, openTargetTypeHandle, targetTypeInstantiation, false, lookForDefaultImplementation)) + { + return true; + } + + entryParser.Offset = currentOffset; + + // Variant dispatch of a variant generic interface generic virtual method. + if (FindMatchingInterfaceSlot(module, nativeLayoutReader, ref entryParser, ref extRefs, ref declaringType, ref methodNameAndSignature, targetTypeHandle, openTargetTypeHandle, targetTypeInstantiation, true, lookForDefaultImplementation)) + { + return true; + } + } + } + + return false; + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + public MethodDesc ResolveTypeHandleAndMethodNameAndSigToVirtualMethodDesc(TypeSystemContext context, RuntimeTypeHandle declaringTypeHandle, MethodNameAndSignature methodNameAndSignature) + { + TypeDesc declaringType = context.ResolveRuntimeTypeHandle(declaringTypeHandle); + MethodDesc targetVirtualMethod = null; + foreach (MethodDesc m in declaringType.GetAllMethods()) + { + if (!m.IsVirtual) + continue; + + if (m.NameAndSignature.Equals(methodNameAndSignature)) + { + targetVirtualMethod = m; + } + } + + return targetVirtualMethod; + } +#endif + + public static unsafe bool IsPregeneratedOrTemplateRuntimeTypeHandle(RuntimeTypeHandle rtth) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + if (!rtth.IsDynamicType()) + return true; + + if (rtth.ToEETypePtr()->DynamicModule == null) + return true; + + return rtth.ToEETypePtr()->DynamicModule->DynamicTypeSlotDispatchResolve == null; +#else + return true; +#endif + } + + private bool ResolveGenericVirtualMethodTarget(RuntimeTypeHandle targetTypeHandle, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle[] genericArguments, MethodNameAndSignature callingMethodNameAndSignature, out IntPtr methodPointer, out IntPtr dictionaryPointer) + { + if (IsPregeneratedOrTemplateRuntimeTypeHandle(targetTypeHandle)) + { + // If the target type isn't dynamic, or at least is template type generated, the static lookup logic is what we want. + return ResolveGenericVirtualMethodTarget_Static(targetTypeHandle, declaringTypeHandle, genericArguments, callingMethodNameAndSignature, out methodPointer, out dictionaryPointer); + } + else + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + methodPointer = IntPtr.Zero; + dictionaryPointer = IntPtr.Zero; + + TypeSystemContext context = TypeSystemContextFactory.Create(); + DefType targetType = (DefType)context.ResolveRuntimeTypeHandle(targetTypeHandle); + + // Method being called... + MethodDesc targetVirtualMethod = ResolveTypeHandleAndMethodNameAndSigToVirtualMethodDesc(context, declaringTypeHandle, callingMethodNameAndSignature); + + if (targetVirtualMethod == null) + { + // If we can't find the method in the type system, it must only be present in the static environment. Search there instead. + TypeSystemContextFactory.Recycle(context); + return ResolveGenericVirtualMethodTarget_Static(targetTypeHandle, declaringTypeHandle, genericArguments, callingMethodNameAndSignature, out methodPointer, out dictionaryPointer); + } + + MethodDesc dispatchMethod = targetType.FindVirtualFunctionTargetMethodOnObjectType(targetVirtualMethod); + + if (dispatchMethod == null) + return false; + + Instantiation targetMethodInstantiation = context.ResolveRuntimeTypeHandles(genericArguments); + MethodDesc instantiatedDispatchMethod = dispatchMethod.Context.ResolveGenericMethodInstantiation(dispatchMethod.OwningType.IsValueType/* get the unboxing stub */, + dispatchMethod.OwningType.GetClosestDefType(), + dispatchMethod.NameAndSignature, + targetMethodInstantiation, IntPtr.Zero, false); + + GenericDictionaryCell cell = GenericDictionaryCell.CreateMethodCell(instantiatedDispatchMethod, false); + using (LockHolder.Hold(_typeLoaderLock)) + { + // Now that we hold the lock, we may find that existing types can now find + // their associated RuntimeTypeHandle. Flush the type builder states as a way + // to force the reresolution of RuntimeTypeHandles which couldn't be found before. + context.FlushTypeBuilderStates(); + + TypeBuilder.ResolveSingleCell(cell, out methodPointer); + } + + TypeSystemContextFactory.Recycle(context); + + return true; +#else + methodPointer = IntPtr.Zero; + dictionaryPointer = IntPtr.Zero; + Environment.FailFast("GVM Resolution for non template or pregenerated type"); + return false; +#endif + } + } + + private unsafe bool ResolveGenericVirtualMethodTarget_Static(RuntimeTypeHandle targetTypeHandle, RuntimeTypeHandle declaringType, RuntimeTypeHandle[] genericArguments, MethodNameAndSignature callingMethodNameAndSignature, out IntPtr methodPointer, out IntPtr dictionaryPointer) + { + methodPointer = dictionaryPointer = IntPtr.Zero; + + // Get the open type definition of the containing type of the generic virtual method being resolved + RuntimeTypeHandle openCallingTypeHandle = GetTypeDefinition(declaringType); + + // Get the open type definition of the current type of the object instance on which the GVM is being resolved + RuntimeTypeHandle openTargetTypeHandle = GetTypeDefinition(targetTypeHandle); + + int hashCode = openCallingTypeHandle.GetHashCode(); + hashCode = ((hashCode << 13) ^ hashCode) ^ openTargetTypeHandle.GetHashCode(); + +#if GVM_RESOLUTION_TRACE + Debug.WriteLine("GVM Target Resolution = " + GetTypeNameDebug(targetTypeHandle) + "." + callingMethodNameAndSignature.Name); +#endif + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(openTargetTypeHandle))) + { + NativeReader gvmTableReader; + if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.GenericVirtualMethodTable, out gvmTableReader)) + continue; + + NativeReader nativeLayoutReader; + if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.NativeLayoutInfo, out nativeLayoutReader)) + continue; + + NativeParser gvmTableParser = new NativeParser(gvmTableReader, 0); + NativeHashtable gvmHashtable = new NativeHashtable(gvmTableParser); + ExternalReferencesTable extRefs = default(ExternalReferencesTable); + extRefs.InitializeCommonFixupsTable(module); + + var lookup = gvmHashtable.Lookup(hashCode); + + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle parsedCallingTypeHandle = extRefs.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!parsedCallingTypeHandle.Equals(openCallingTypeHandle)) + continue; + + RuntimeTypeHandle parsedTargetTypeHandle = extRefs.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!parsedTargetTypeHandle.Equals(openTargetTypeHandle)) + continue; + + uint parsedCallingNameAndSigToken = entryParser.GetUnsigned(); + MethodNameAndSignature parsedCallingNameAndSignature = GetMethodNameAndSignatureFromNativeReader(nativeLayoutReader, module.Handle, parsedCallingNameAndSigToken); + + if (!parsedCallingNameAndSignature.Equals(callingMethodNameAndSignature)) + continue; + + uint parsedTargetMethodNameAndSigToken = entryParser.GetUnsigned(); + MethodNameAndSignature targetMethodNameAndSignature = GetMethodNameAndSignatureFromNativeReader(nativeLayoutReader, module.Handle, parsedTargetMethodNameAndSigToken); + + Debug.Assert(targetMethodNameAndSignature != null); + + if (!TryGetGenericVirtualMethodPointer(targetTypeHandle, targetMethodNameAndSignature, genericArguments, out methodPointer, out dictionaryPointer)) + { + var sb = new System.Text.StringBuilder(); + sb.AppendLine("Generic virtual method pointer lookup failure."); + sb.AppendLine(); + sb.AppendLine("Declaring type handle: " + declaringType.LowLevelToStringRawEETypeAddress()); + sb.AppendLine("Target type handle: " + targetTypeHandle.LowLevelToStringRawEETypeAddress()); + sb.AppendLine("Method name: " + targetMethodNameAndSignature.Name); + sb.AppendLine("Instantiation:"); + for (int i = 0; i < genericArguments.Length; i++) + { + sb.AppendLine(" Argument " + i.LowLevelToString() + ": " + genericArguments[i].LowLevelToStringRawEETypeAddress()); + } + + Environment.FailFast(sb.ToString()); + } + + return true; + } + } + + return false; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs new file mode 100644 index 00000000000000..1c1da35553b880 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs @@ -0,0 +1,426 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Text; +using System.Reflection; +using System.Runtime; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; +using System.Reflection.Runtime.General; + +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.NativeFormat; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime.TypeLoader +{ + public sealed partial class TypeLoaderEnvironment + { + [StructLayout(LayoutKind.Sequential)] + private struct DynamicFieldHandleInfo + { + public IntPtr DeclaringType; + public IntPtr FieldName; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct DynamicMethodHandleInfo + { + public IntPtr DeclaringType; + public IntPtr MethodName; + public RuntimeSignature MethodSignature; + public int NumGenericArgs; + public IntPtr GenericArgsArray; + } + + + #region String conversions + private static unsafe string GetStringFromMemoryInNativeFormat(IntPtr pointerToDataStream) + { + byte* dataStream = (byte*)pointerToDataStream.ToPointer(); + uint stringLen = NativePrimitiveDecoder.DecodeUnsigned(ref dataStream); + return Encoding.UTF8.GetString(dataStream, checked((int)stringLen)); + } + + /// + /// From a string, get a pointer to an allocated memory location that holds a NativeFormat encoded string. + /// This is used for the creation of RuntimeFieldHandles from metadata. + /// + /// + /// + public IntPtr GetNativeFormatStringForString(string str) + { + using (LockHolder.Hold(_typeLoaderLock)) + { + IntPtr result; + if (_nativeFormatStrings.TryGetValue(str, out result)) + return result; + + NativePrimitiveEncoder stringEncoder = new NativePrimitiveEncoder(); + stringEncoder.Init(); + byte[] utf8Bytes = Encoding.UTF8.GetBytes(str); + stringEncoder.WriteUnsigned(checked((uint)utf8Bytes.Length)); + foreach (byte b in utf8Bytes) + stringEncoder.WriteByte(b); + + IntPtr allocatedNativeFormatString = MemoryHelpers.AllocateMemory(stringEncoder.Size); + unsafe + { + stringEncoder.Save((byte*)allocatedNativeFormatString.ToPointer(), stringEncoder.Size); + } + _nativeFormatStrings.Add(str, allocatedNativeFormatString); + return allocatedNativeFormatString; + } + } + + private LowLevelDictionary _nativeFormatStrings = new LowLevelDictionary(); + #endregion + + + #region Ldtoken Hashtables + private struct RuntimeFieldHandleKey : IEquatable + { + private RuntimeTypeHandle _declaringType; + private string _fieldName; + private int _hashcode; + + public RuntimeFieldHandleKey(RuntimeTypeHandle declaringType, string fieldName) + { + _declaringType = declaringType; + _fieldName = fieldName; + _hashcode = declaringType.GetHashCode() ^ fieldName.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (obj is RuntimeFieldHandleKey) + { + RuntimeFieldHandleKey other = (RuntimeFieldHandleKey)obj; + return Equals(other); + } + return false; + } + + public bool Equals(RuntimeFieldHandleKey other) + { + return other._declaringType.Equals(_declaringType) && other._fieldName == _fieldName; + } + + public override int GetHashCode() { return _hashcode; } + } + + private struct RuntimeMethodHandleKey : IEquatable + { + private RuntimeTypeHandle _declaringType; + private string _methodName; + private RuntimeSignature _signature; + private RuntimeTypeHandle[] _genericArgs; + private int _hashcode; + + public RuntimeMethodHandleKey(RuntimeTypeHandle declaringType, string methodName, RuntimeSignature signature, RuntimeTypeHandle[] genericArgs) + { + // genericArgs will be null if this is a (typical or not) method definition + // genericArgs are non-null only for instantiated generic methods. + Debug.Assert(genericArgs == null || genericArgs.Length > 0); + + _declaringType = declaringType; + _methodName = methodName; + _signature = signature; + _genericArgs = genericArgs; + int methodNameHashCode = methodName == null ? 0 : methodName.GetHashCode(); + _hashcode = methodNameHashCode ^ signature.GetHashCode(); + + if (genericArgs != null) + _hashcode ^= TypeHashingAlgorithms.ComputeGenericInstanceHashCode(declaringType.GetHashCode(), genericArgs); + else + _hashcode ^= declaringType.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (obj is RuntimeMethodHandleKey) + { + RuntimeMethodHandleKey other = (RuntimeMethodHandleKey)obj; + return Equals(other); + } + return false; + } + + public bool Equals(RuntimeMethodHandleKey other) + { + if (!_declaringType.Equals(other._declaringType) || _methodName != other._methodName || !_signature.Equals(other._signature)) + return false; + + if ((_genericArgs == null) != (other._genericArgs == null)) + return false; + + if (_genericArgs != null) + { + if (_genericArgs.Length != other._genericArgs.Length) + return false; + + for (int i = 0; i < _genericArgs.Length; i++) + if (!_genericArgs[i].Equals(other._genericArgs[i])) + return false; + } + + return true; + } + + public override int GetHashCode() { return _hashcode; } + } + + private LowLevelDictionary _runtimeFieldHandles = new LowLevelDictionary(); + private LowLevelDictionary _runtimeMethodHandles = new LowLevelDictionary(); + #endregion + + + #region Field Ldtoken Functions + public RuntimeFieldHandle GetRuntimeFieldHandleForComponents(RuntimeTypeHandle declaringTypeHandle, string fieldName) + { + IntPtr nameAsIntPtr = GetNativeFormatStringForString(fieldName); + return GetRuntimeFieldHandleForComponents(declaringTypeHandle, nameAsIntPtr); + } + + public unsafe RuntimeFieldHandle GetRuntimeFieldHandleForComponents(RuntimeTypeHandle declaringTypeHandle, IntPtr fieldName) + { + string fieldNameStr = GetStringFromMemoryInNativeFormat(fieldName); + + RuntimeFieldHandleKey key = new RuntimeFieldHandleKey(declaringTypeHandle, fieldNameStr); + RuntimeFieldHandle runtimeFieldHandle = default(RuntimeFieldHandle); + + lock (_runtimeFieldHandles) + { + if (!_runtimeFieldHandles.TryGetValue(key, out runtimeFieldHandle)) + { + IntPtr runtimeFieldHandleValue = MemoryHelpers.AllocateMemory(sizeof(DynamicFieldHandleInfo)); + if (runtimeFieldHandleValue == IntPtr.Zero) + throw new OutOfMemoryException(); + + DynamicFieldHandleInfo* fieldData = (DynamicFieldHandleInfo*)runtimeFieldHandleValue.ToPointer(); + fieldData->DeclaringType = *(IntPtr*)&declaringTypeHandle; + fieldData->FieldName = fieldName; + + // Special flag (lowest bit set) in the handle value to indicate it was dynamically allocated + runtimeFieldHandleValue = runtimeFieldHandleValue + 1; + runtimeFieldHandle = *(RuntimeFieldHandle*)&runtimeFieldHandleValue; + + _runtimeFieldHandles.Add(key, runtimeFieldHandle); + } + + return runtimeFieldHandle; + } + } + + public bool TryGetRuntimeFieldHandleComponents(RuntimeFieldHandle runtimeFieldHandle, out RuntimeTypeHandle declaringTypeHandle, out string fieldName) + { + return runtimeFieldHandle.IsDynamic() ? + TryGetDynamicRuntimeFieldHandleComponents(runtimeFieldHandle, out declaringTypeHandle, out fieldName) : + TryGetStaticRuntimeFieldHandleComponents(runtimeFieldHandle, out declaringTypeHandle, out fieldName); + } + + private unsafe bool TryGetDynamicRuntimeFieldHandleComponents(RuntimeFieldHandle runtimeFieldHandle, out RuntimeTypeHandle declaringTypeHandle, out string fieldName) + { + IntPtr runtimeFieldHandleValue = *(IntPtr*)&runtimeFieldHandle; + + // Special flag in the handle value to indicate it was dynamically allocated + Debug.Assert((runtimeFieldHandleValue.ToInt64() & 0x1) == 0x1); + runtimeFieldHandleValue = runtimeFieldHandleValue - 1; + + DynamicFieldHandleInfo* fieldData = (DynamicFieldHandleInfo*)runtimeFieldHandleValue.ToPointer(); + declaringTypeHandle = *(RuntimeTypeHandle*)&(fieldData->DeclaringType); + + // FieldName points to the field name in NativeLayout format, so we parse it using a NativeParser + IntPtr fieldNamePtr = fieldData->FieldName; + fieldName = GetStringFromMemoryInNativeFormat(fieldNamePtr); + + return true; + } + + private unsafe bool TryGetStaticRuntimeFieldHandleComponents(RuntimeFieldHandle runtimeFieldHandle, out RuntimeTypeHandle declaringTypeHandle, out string fieldName) + { + fieldName = null; + declaringTypeHandle = default(RuntimeTypeHandle); + + // Make sure it's not a dynamically allocated RuntimeFieldHandle before we attempt to use it to parse native layout data + Debug.Assert(((*(IntPtr*)&runtimeFieldHandle).ToInt64() & 0x1) == 0); + + RuntimeFieldHandleInfo* fieldData = *(RuntimeFieldHandleInfo**)&runtimeFieldHandle; + RuntimeSignature signature; + + // The native layout info signature is a pair. + // The first is a pointer that points to the TypeManager indirection cell. + // The second is the offset into the native layout info blob in that TypeManager, where the native signature is encoded. + IntPtr* nativeLayoutInfoSignatureData = (IntPtr*)fieldData->NativeLayoutInfoSignature; + + signature = RuntimeSignature.CreateFromNativeLayoutSignature( + new TypeManagerHandle(*(IntPtr*)nativeLayoutInfoSignatureData[0]), + (uint)nativeLayoutInfoSignatureData[1].ToInt32()); + + RuntimeSignature remainingSignature; + if (!GetTypeFromSignatureAndContext(signature, null, null, out declaringTypeHandle, out remainingSignature)) + return false; + + // GetTypeFromSignatureAndContext parses the type from the signature and returns a pointer to the next + // part of the native layout signature to read which we get the field name from + var reader = GetNativeLayoutInfoReader(remainingSignature); + var parser = new NativeParser(reader, remainingSignature.NativeLayoutOffset); + fieldName = parser.GetString(); + + return true; + } + #endregion + + + #region Method Ldtoken Functions + /// + /// Create a runtime method handle from name, signature and generic arguments. If the methodSignature + /// is constructed from a metadata token, the methodName should be IntPtr.Zero, as it already encodes the method + /// name. + /// + public unsafe RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTypeHandle declaringTypeHandle, IntPtr methodName, RuntimeSignature methodSignature, RuntimeTypeHandle[] genericMethodArgs) + { + string methodNameStr = methodName == IntPtr.Zero ? null : GetStringFromMemoryInNativeFormat(methodName); + + RuntimeMethodHandleKey key = new RuntimeMethodHandleKey(declaringTypeHandle, methodNameStr, methodSignature, genericMethodArgs); + RuntimeMethodHandle runtimeMethodHandle = default(RuntimeMethodHandle); + + lock (_runtimeMethodHandles) + { + if (!_runtimeMethodHandles.TryGetValue(key, out runtimeMethodHandle)) + { + int sizeToAllocate = sizeof(DynamicMethodHandleInfo); + int numGenericMethodArgs = genericMethodArgs == null ? 0 : genericMethodArgs.Length; + // Use checked arithmetics to ensure there aren't any overflows/truncations + sizeToAllocate = checked(sizeToAllocate + (numGenericMethodArgs > 0 ? sizeof(IntPtr) * (numGenericMethodArgs - 1) : 0)); + IntPtr runtimeMethodHandleValue = MemoryHelpers.AllocateMemory(sizeToAllocate); + if (runtimeMethodHandleValue == IntPtr.Zero) + throw new OutOfMemoryException(); + + DynamicMethodHandleInfo* methodData = (DynamicMethodHandleInfo*)runtimeMethodHandleValue.ToPointer(); + methodData->DeclaringType = *(IntPtr*)&declaringTypeHandle; + methodData->MethodName = methodName; + methodData->MethodSignature = methodSignature; + methodData->NumGenericArgs = numGenericMethodArgs; + IntPtr* genericArgPtr = &(methodData->GenericArgsArray); + for (int i = 0; i < numGenericMethodArgs; i++) + { + RuntimeTypeHandle currentArg = genericMethodArgs[i]; + genericArgPtr[i] = *(IntPtr*)¤tArg; + } + + // Special flag in the handle value to indicate it was dynamically allocated, and doesn't point into the InvokeMap blob + runtimeMethodHandleValue = runtimeMethodHandleValue + 1; + runtimeMethodHandle = * (RuntimeMethodHandle*)&runtimeMethodHandleValue; + + _runtimeMethodHandles.Add(key, runtimeMethodHandle); + } + + return runtimeMethodHandle; + } + } + public RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTypeHandle declaringTypeHandle, string methodName, RuntimeSignature methodSignature, RuntimeTypeHandle[] genericMethodArgs) + { + IntPtr nameAsIntPtr = GetNativeFormatStringForString(methodName); + return GetRuntimeMethodHandleForComponents(declaringTypeHandle, nameAsIntPtr, methodSignature, genericMethodArgs); + } + + public bool TryGetRuntimeMethodHandleComponents(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodArgs) + { + return runtimeMethodHandle.IsDynamic() ? + TryGetDynamicRuntimeMethodHandleComponents(runtimeMethodHandle, out declaringTypeHandle, out nameAndSignature, out genericMethodArgs) : + TryGetStaticRuntimeMethodHandleComponents(runtimeMethodHandle, out declaringTypeHandle, out nameAndSignature, out genericMethodArgs); + } + private unsafe bool TryGetDynamicRuntimeMethodHandleComponents(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodArgs) + { + IntPtr runtimeMethodHandleValue = *(IntPtr*)&runtimeMethodHandle; + Debug.Assert((runtimeMethodHandleValue.ToInt64() & 0x1) == 0x1); + + // Special flag in the handle value to indicate it was dynamically allocated, and doesn't point into the InvokeMap blob + runtimeMethodHandleValue = runtimeMethodHandleValue - 1; + + DynamicMethodHandleInfo* methodData = (DynamicMethodHandleInfo*)runtimeMethodHandleValue.ToPointer(); + declaringTypeHandle = *(RuntimeTypeHandle*)&(methodData->DeclaringType); + genericMethodArgs = null; + + if (methodData->NumGenericArgs > 0) + { + IntPtr* genericArgPtr = &(methodData->GenericArgsArray); + genericMethodArgs = new RuntimeTypeHandle[methodData->NumGenericArgs]; + for (int i = 0; i < methodData->NumGenericArgs; i++) + { + genericMethodArgs[i] = *(RuntimeTypeHandle*)&(genericArgPtr[i]); + } + } + + if (methodData->MethodSignature.IsNativeLayoutSignature) + { + // MethodName points to the method name in NativeLayout format, so we parse it using a NativeParser + IntPtr methodNamePtr = methodData->MethodName; + string name = GetStringFromMemoryInNativeFormat(methodNamePtr); + + nameAndSignature = new MethodNameAndSignature(name, methodData->MethodSignature); + } + else + { + ModuleInfo moduleInfo = methodData->MethodSignature.GetModuleInfo(); + + string name; +#if ECMA_METADATA_SUPPORT + if (moduleInfo is NativeFormatModuleInfo) +#endif + { + var metadataReader = ((NativeFormatModuleInfo)moduleInfo).MetadataReader; + var methodHandle = methodData->MethodSignature.Token.AsHandle().ToMethodHandle(metadataReader); + var method = methodHandle.GetMethod(metadataReader); + name = metadataReader.GetConstantStringValue(method.Name).Value; + } +#if ECMA_METADATA_SUPPORT + else + { + var ecmaReader = ((EcmaModuleInfo)moduleInfo).MetadataReader; + var ecmaHandle = System.Reflection.Metadata.Ecma335.MetadataTokens.Handle(methodData->MethodSignature.Token); + var ecmaMethodHandle = (System.Reflection.Metadata.MethodDefinitionHandle)ecmaHandle; + var ecmaMethod = ecmaReader.GetMethodDefinition(ecmaMethodHandle); + name = ecmaReader.GetString(ecmaMethod.Name); + } +#endif + nameAndSignature = new MethodNameAndSignature(name, methodData->MethodSignature); + } + + return true; + } + private unsafe bool TryGetStaticRuntimeMethodHandleComponents(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodArgs) + { + declaringTypeHandle = default(RuntimeTypeHandle); + nameAndSignature = null; + genericMethodArgs = null; + + // Make sure it's not a dynamically allocated RuntimeMethodHandle before we attempt to use it to parse native layout data + Debug.Assert(((*(IntPtr*)&runtimeMethodHandle).ToInt64() & 0x1) == 0); + + RuntimeMethodHandleInfo* methodData = *(RuntimeMethodHandleInfo**)&runtimeMethodHandle; + RuntimeSignature signature; + + // The native layout info signature is a pair. + // The first is a pointer that points to the TypeManager indirection cell. + // The second is the offset into the native layout info blob in that TypeManager, where the native signature is encoded. + IntPtr* nativeLayoutInfoSignatureData = (IntPtr*)methodData->NativeLayoutInfoSignature; + + signature = RuntimeSignature.CreateFromNativeLayoutSignature( + new TypeManagerHandle(*(IntPtr*)nativeLayoutInfoSignatureData[0]), + (uint)nativeLayoutInfoSignatureData[1].ToInt32()); + + RuntimeSignature remainingSignature; + return GetMethodFromSignatureAndContext(signature, null, null, out declaringTypeHandle, out nameAndSignature, out genericMethodArgs, out remainingSignature); + } + #endregion + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs new file mode 100644 index 00000000000000..37874b76b74579 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs @@ -0,0 +1,1767 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +using System.Reflection.Runtime.General; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.Metadata.NativeFormat; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.TypeSystem.NativeFormat; +#if ECMA_METADATA_SUPPORT +using Internal.TypeSystem.Ecma; +#endif + +namespace Internal.Runtime.TypeLoader +{ + /// + /// This structure represents metadata-based information used to construct method invokers. + /// TypeLoaderEnvironment.TryGetMethodInvokeMetadata fills in this structure based on metadata lookup across + /// all currently registered binary modules. + /// + public struct MethodInvokeMetadata + { + /// + /// module containing the relevant metadata, null when not found + /// + public NativeFormatModuleInfo MappingTableModule; + + /// + /// Method entrypoint + /// + public IntPtr MethodEntryPoint; + + /// + /// Raw method entrypoint + /// + public IntPtr RawMethodEntryPoint; + + /// + /// Method dictionary for components + /// + public IntPtr DictionaryComponent; + + /// + /// Dynamic invoke cookie + /// + public uint DynamicInvokeCookie; + + /// + /// Invoke flags + /// + public InvokeTableFlags InvokeTableFlags; + } + + public sealed partial class TypeLoaderEnvironment + { + /// + /// Compare two arrays sequentially. + /// + /// First array to compare + /// Second array to compare + /// + /// true = arrays have the same values and Equals holds for all pairs of elements + /// with the same indices + /// + private static bool SequenceEqual(T[] seq1, T[] seq2) + { + if (seq1.Length != seq2.Length) + return false; + for (int i = 0; i < seq1.Length; i++) + if (!seq1[i].Equals(seq2[i])) + return false; + return true; + } + + /// + /// Locate blob with given ID and create native reader on it. + /// + /// Address of module to search for the blob + /// Blob ID within blob map for the module + /// Native reader for the blob (asserts and returns an empty native reader when not found) + internal static unsafe NativeReader GetNativeReaderForBlob(NativeFormatModuleInfo module, ReflectionMapBlob blob) + { + NativeReader reader; + if (TryGetNativeReaderForBlob(module, blob, out reader)) + { + return reader; + } + + Debug.Assert(false); + return default(NativeReader); + } + + /// + /// Return the metadata handle for a TypeRef if this type was referenced indirectly by other type that pay-for-play has denoted as browsable + /// (for example, as part of a method signature.) + /// + /// This is only used in "debug" builds to provide better MissingMetadataException diagnostics. + /// + /// Preconditions: + /// runtimeTypeHandle is a typedef (not a constructed type such as an array or generic instance.) + /// + /// MethodTable of the type in question + /// Metadata reader for the type + /// Located TypeRef handle + public static unsafe bool TryGetTypeReferenceForNamedType(RuntimeTypeHandle runtimeTypeHandle, out MetadataReader metadataReader, out TypeReferenceHandle typeRefHandle) + { + int hashCode = runtimeTypeHandle.GetHashCode(); + + // Iterate over all modules, starting with the module that defines the MethodTable + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(runtimeTypeHandle))) + { + NativeReader typeMapReader; + if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.TypeMap, out typeMapReader)) + { + NativeParser typeMapParser = new NativeParser(typeMapReader, 0); + NativeHashtable typeHashtable = new NativeHashtable(typeMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = typeHashtable.Lookup(hashCode); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle foundType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (foundType.Equals(runtimeTypeHandle)) + { + Handle entryMetadataHandle = entryParser.GetUnsigned().AsHandle(); + if (entryMetadataHandle.HandleType == HandleType.TypeReference) + { + metadataReader = module.MetadataReader; + typeRefHandle = entryMetadataHandle.ToTypeReferenceHandle(metadataReader); + return true; + } + } + } + } + } + + metadataReader = null; + typeRefHandle = default(TypeReferenceHandle); + + return false; + } + + /// + /// Return the RuntimeTypeHandle for the named type referenced by another type that pay-for-play denotes as browsable (for example, + /// in a member signature.) This will only find the typehandle if it is not defined in the current module, and is primarily used + /// to find non-browsable types. + /// + /// This is used to ensure that we can produce a Type object if requested and that it match up with the analogous + /// Type obtained via typeof(). + /// + /// + /// Preconditions: + /// metadataReader + typeRefHandle - a valid metadata reader + typeReferenceHandle where "metadataReader" is one + /// of the metadata readers returned by ExecutionEnvironment.MetadataReaders. + /// + /// Note: Although this method has a "bool" return value like the other mapping table accessors, the Project N pay-for-play design + /// guarantees that any type that has a metadata TypeReference to it also has a RuntimeTypeHandle underneath. + /// + /// Metadata reader for module containing the type reference + /// TypeRef handle to look up + /// Resolved MethodTable for the type reference + /// Search all modules + public static unsafe bool TryGetNamedTypeForTypeReference(MetadataReader metadataReader, TypeReferenceHandle typeRefHandle, out RuntimeTypeHandle runtimeTypeHandle, bool searchAllModules = false) + { + int hashCode = typeRefHandle.ComputeHashCode(metadataReader); + NativeFormatModuleInfo typeRefModule = ModuleList.Instance.GetModuleInfoForMetadataReader(metadataReader); + return TryGetNamedTypeForTypeReference_Inner(metadataReader, typeRefModule, typeRefHandle, hashCode, typeRefModule, out runtimeTypeHandle); + } + + /// + /// Return the RuntimeTypeHandle for the named type referenced by another type that pay-for-play denotes as browsable (for example, + /// in a member signature.) This lookup will attempt to resolve to an MethodTable in any module to cover situations where the type + /// does not have a TypeDefinition (non-browsable type) as well as cases where it does. + /// + /// Preconditions: + /// metadataReader + typeRefHandle - a valid metadata reader + typeReferenceHandle where "metadataReader" is one + /// of the metadata readers returned by ExecutionEnvironment.MetadataReaders. + /// + /// Note: Although this method has a "bool" return value like the other mapping table accessors, the Project N pay-for-play design + /// guarantees that any type that has a metadata TypeReference to it also has a RuntimeTypeHandle underneath. + /// + /// Metadata reader for module containing the type reference + /// TypeRef handle to look up + /// Resolved MethodTable for the type reference + public static unsafe bool TryResolveNamedTypeForTypeReference(MetadataReader metadataReader, TypeReferenceHandle typeRefHandle, out RuntimeTypeHandle runtimeTypeHandle) + { + int hashCode = typeRefHandle.ComputeHashCode(metadataReader); + NativeFormatModuleInfo typeRefModule = ModuleList.Instance.GetModuleInfoForMetadataReader(metadataReader); + runtimeTypeHandle = default(RuntimeTypeHandle); + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(typeRefModule.Handle)) + { + if (TryGetNamedTypeForTypeReference_Inner(metadataReader, typeRefModule, typeRefHandle, hashCode, module, out runtimeTypeHandle)) + return true; + } + + return false; + } + + private static unsafe bool TryGetNamedTypeForTypeReference_Inner(MetadataReader metadataReader, + NativeFormatModuleInfo typeRefModule, + TypeReferenceHandle typeRefHandle, + int hashCode, + NativeFormatModuleInfo module, + out RuntimeTypeHandle runtimeTypeHandle) + { + Debug.Assert(typeRefModule == ModuleList.Instance.GetModuleInfoForMetadataReader(metadataReader)); + + NativeReader typeMapReader; + if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.TypeMap, out typeMapReader)) + { + NativeParser typeMapParser = new NativeParser(typeMapReader, 0); + NativeHashtable typeHashtable = new NativeHashtable(typeMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = typeHashtable.Lookup(hashCode); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + var foundTypeIndex = entryParser.GetUnsigned(); + var handle = entryParser.GetUnsigned().AsHandle(); + + if (module == typeRefModule) + { + if (handle.Equals(typeRefHandle)) + { + runtimeTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(foundTypeIndex); + return true; + } + } + else if (handle.HandleType == HandleType.TypeReference) + { + MetadataReader mrFoundHandle = module.MetadataReader; + // We found a type reference handle in another module.. see if it matches + if (MetadataReaderHelpers.CompareTypeReferenceAcrossModules(typeRefHandle, metadataReader, handle.ToTypeReferenceHandle(mrFoundHandle), mrFoundHandle)) + { + runtimeTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(foundTypeIndex); + return true; + } + } + else if (handle.HandleType == HandleType.TypeDefinition) + { + // We found a type definition handle in another module. See if it matches + MetadataReader mrFoundHandle = module.MetadataReader; + // We found a type definition handle in another module.. see if it matches + if (MetadataReaderHelpers.CompareTypeReferenceToDefinition(typeRefHandle, metadataReader, handle.ToTypeDefinitionHandle(mrFoundHandle), mrFoundHandle)) + { + runtimeTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(foundTypeIndex); + return true; + } + } + } + } + + runtimeTypeHandle = default(RuntimeTypeHandle); + return false; + } + + /// + /// Given a RuntimeTypeHandle for any non-dynamic type E, return a RuntimeTypeHandle for type E[] + /// if the pay for play policy denotes E[] as browsable. This is used to implement Array.CreateInstance(). + /// This is not equivalent to calling TryGetMultiDimTypeForElementType() with a rank of 1! + /// + /// Preconditions: + /// elementTypeHandle is a valid RuntimeTypeHandle. + /// + /// MethodTable of the array element type + /// Resolved MethodTable of the array type + public static unsafe bool TryGetArrayTypeForNonDynamicElementType(RuntimeTypeHandle elementTypeHandle, out RuntimeTypeHandle arrayTypeHandle) + { + arrayTypeHandle = new RuntimeTypeHandle(); + + int arrayHashcode = TypeHashingAlgorithms.ComputeArrayTypeHashCode(elementTypeHandle.GetHashCode(), -1); + + // Note: ReflectionMapBlob.ArrayMap may not exist in the module that contains the element type. + // So we must enumerate all loaded modules in order to find ArrayMap and the array type for + // the given element. + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + NativeReader arrayMapReader; + if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.ArrayMap, out arrayMapReader)) + { + NativeParser arrayMapParser = new NativeParser(arrayMapReader, 0); + NativeHashtable arrayHashtable = new NativeHashtable(arrayMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = arrayHashtable.Lookup(arrayHashcode); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle foundArrayType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + RuntimeTypeHandle foundArrayElementType = RuntimeAugments.GetRelatedParameterTypeHandle(foundArrayType); + if (foundArrayElementType.Equals(elementTypeHandle)) + { + arrayTypeHandle = foundArrayType; + return true; + } + } + } + } + + return false; + } + + /// + /// The array table only holds some of the precomputed array types, others may be found in the template type. + /// As our system requires us to find the RuntimeTypeHandle of templates we must not be in a situation where we fail to find + /// a RuntimeTypeHandle for a type which is actually in the template table. This function fixes that problem for arrays. + /// + /// + /// + /// + public bool TryGetArrayTypeHandleForNonDynamicArrayTypeFromTemplateTable(ArrayType arrayType, out RuntimeTypeHandle arrayTypeHandle) + { + arrayTypeHandle = default(RuntimeTypeHandle); + + // Only SzArray types have templates. + if (!arrayType.IsSzArray) + return false; + + // If we can't find a RuntimeTypeHandle for the element type, we can't find the array in the template table. + if (!arrayType.ParameterType.RetrieveRuntimeTypeHandleIfPossible()) + return false; + + unsafe + { + // If the elementType is a dynamic type it cannot exist in the template table. + if (arrayType.ParameterType.RuntimeTypeHandle.ToEETypePtr()->IsDynamicType) + return false; + } + + // Try to find out if the type exists as a template + var canonForm = arrayType.ConvertToCanonForm(CanonicalFormKind.Specific); + var hashCode = canonForm.GetHashCode(); + foreach (var module in ModuleList.EnumerateModules()) + { + ExternalReferencesTable externalFixupsTable; + + NativeHashtable typeTemplatesHashtable = LoadHashtable(module, ReflectionMapBlob.TypeTemplateMap, out externalFixupsTable); + + if (typeTemplatesHashtable.IsNull) + continue; + + var enumerator = typeTemplatesHashtable.Lookup(hashCode); + + NativeParser entryParser; + while (!(entryParser = enumerator.GetNext()).IsNull) + { + RuntimeTypeHandle candidateTemplateTypeHandle = externalFixupsTable.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + TypeDesc foundType = arrayType.Context.ResolveRuntimeTypeHandle(candidateTemplateTypeHandle); + if (foundType == arrayType) + { + arrayTypeHandle = candidateTemplateTypeHandle; + + // This lookup in the template table is fairly slow, so if we find the array here, add it to the dynamic array cache, so that + // we can find it faster in the future. + if (arrayType.IsSzArray) + TypeSystemContext.GetArrayTypesCache(false, -1).AddOrGetExisting(arrayTypeHandle); + return true; + } + } + } + + return false; + } + + // Lazy loadings of hashtables (load on-demand only) + private unsafe NativeHashtable LoadHashtable(NativeFormatModuleInfo module, ReflectionMapBlob hashtableBlobId, out ExternalReferencesTable externalFixupsTable) + { + // Load the common fixups table + externalFixupsTable = default(ExternalReferencesTable); + if (!externalFixupsTable.InitializeCommonFixupsTable(module)) + return default(NativeHashtable); + + // Load the hashtable + byte* pBlob; + uint cbBlob; + if (!module.TryFindBlob(hashtableBlobId, out pBlob, out cbBlob)) + return default(NativeHashtable); + + NativeReader reader = new NativeReader(pBlob, cbBlob); + NativeParser parser = new NativeParser(reader, 0); + return new NativeHashtable(parser); + } + + /// + /// Locate the static constructor context given the runtime type handle (MethodTable) for the type in question. + /// + /// MethodTable of the type to look up + public static unsafe IntPtr TryGetStaticClassConstructionContext(RuntimeTypeHandle typeHandle) + { + if (RuntimeAugments.HasCctor(typeHandle)) + { + if (RuntimeAugments.IsDynamicType(typeHandle)) + { + // For dynamic types, its always possible to get the non-gc static data section directly. + byte* ptr = (byte*)Instance.TryGetNonGcStaticFieldData(typeHandle); + + // what we have now is the base address of the non-gc statics of the type + // what we need is the cctor context, which is just before that + ptr = ptr - sizeof(System.Runtime.CompilerServices.StaticClassConstructionContext); + + return (IntPtr)ptr; + } + else + { + // Non-dynamic types do not provide a way to directly get at the non-gc static region. + // Use the CctorContextMap instead. + + var moduleHandle = RuntimeAugments.GetModuleFromTypeHandle(typeHandle); + NativeFormatModuleInfo module = ModuleList.Instance.GetModuleInfoByHandle(moduleHandle); + Debug.Assert(!moduleHandle.IsNull); + + NativeReader typeMapReader; + if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.CCtorContextMap, out typeMapReader)) + { + NativeParser typeMapParser = new NativeParser(typeMapReader, 0); + NativeHashtable typeHashtable = new NativeHashtable(typeMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = typeHashtable.Lookup(typeHandle.GetHashCode()); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle foundType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (foundType.Equals(typeHandle)) + { + byte* pNonGcStaticBase = (byte*)externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + + // cctor context is located before the non-GC static base + return (IntPtr)(pNonGcStaticBase - sizeof(System.Runtime.CompilerServices.StaticClassConstructionContext)); + } + } + } + } + + // If the type has a lazy/deferred Cctor, the compiler must have missed emitting + // a data structure if we reach this. + Debug.Assert(false); + } + + return IntPtr.Zero; + } + + /// + /// Construct the native reader for a given blob in a specified module. + /// + /// Containing binary module for the blob + /// Blob ID to fetch from the module + /// Native reader created for the module blob + /// true when the blob was found in the module, false when not + private static unsafe bool TryGetNativeReaderForBlob(NativeFormatModuleInfo module, ReflectionMapBlob blob, out NativeReader reader) + { + byte* pBlob; + uint cbBlob; + + if (module.TryFindBlob(blob, out pBlob, out cbBlob)) + { + reader = new NativeReader(pBlob, cbBlob); + return true; + } + + reader = default(NativeReader); + return false; + } + + /// + /// Look up the default constructor for a given type. Should not be called by code which has already initialized + /// the type system. + /// + /// TypeDesc for the type in question + /// Function pointer representing the constructor, IntPtr.Zero when not found + internal IntPtr TryGetDefaultConstructorForType(TypeDesc type) + { + // Try to find the default constructor in metadata first + IntPtr result = IntPtr.Zero; + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + result = TryGetDefaultConstructorForTypeViaMetadata_Inner(type); +#endif + + DefType defType = type as DefType; + if ((result == IntPtr.Zero) && (defType != null)) + { +#if GENERICS_FORCE_USG + // In force USG mode, prefer universal matches over canon specific matches. + CanonicalFormKind firstCanonFormKind = CanonicalFormKind.Universal; + CanonicalFormKind secondCanonFormKind = CanonicalFormKind.Specific; +#else + CanonicalFormKind firstCanonFormKind = CanonicalFormKind.Specific; + CanonicalFormKind secondCanonFormKind = CanonicalFormKind.Universal; +#endif + + CanonicallyEquivalentEntryLocator canonHelperSpecific = new CanonicallyEquivalentEntryLocator(defType, firstCanonFormKind); + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + result = TryGetDefaultConstructorForType_Inner(module, ref canonHelperSpecific); + + if (result != IntPtr.Zero) + break; + } + + if (result == IntPtr.Zero) + { + CanonicallyEquivalentEntryLocator canonHelperUniversal = new CanonicallyEquivalentEntryLocator(defType, secondCanonFormKind); + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + result = TryGetDefaultConstructorForType_Inner(module, ref canonHelperUniversal); + + if (result != IntPtr.Zero) + break; + } + } + } + + return result; + } + + /// + /// Look up the default constructor for a given type. Should not be called by code which has already initialized + /// the type system. + /// + /// Type handle (MethodTable) for the type in question + /// Function pointer representing the constructor, IntPtr.Zero when not found + public IntPtr TryGetDefaultConstructorForType(RuntimeTypeHandle runtimeTypeHandle) + { + CanonicallyEquivalentEntryLocator canonHelperSpecific = new CanonicallyEquivalentEntryLocator(runtimeTypeHandle, CanonicalFormKind.Specific); + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(runtimeTypeHandle))) + { + IntPtr result = TryGetDefaultConstructorForType_Inner(module, ref canonHelperSpecific); + + if (result != IntPtr.Zero) + return result; + } + + CanonicallyEquivalentEntryLocator canonHelperUniversal = new CanonicallyEquivalentEntryLocator(runtimeTypeHandle, CanonicalFormKind.Universal); + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(runtimeTypeHandle))) + { + IntPtr result = TryGetDefaultConstructorForType_Inner(module, ref canonHelperUniversal); + + if (result != IntPtr.Zero) + return result; + } + + // Try to find the default constructor in metadata last (this is costly as it requires spinning up a TypeLoaderContext, and + // currently also the _typeLoaderLock) (TODO when the _typeLoaderLock is no longer necessary to correctly use the type system + // context, remove the use of the lock here.) + using (LockHolder.Hold(_typeLoaderLock)) + { + TypeSystemContext context = TypeSystemContextFactory.Create(); + + TypeDesc type = context.ResolveRuntimeTypeHandle(runtimeTypeHandle); + IntPtr result = TryGetDefaultConstructorForTypeViaMetadata_Inner(type); + + TypeSystemContextFactory.Recycle(context); + + if (result != IntPtr.Zero) + return result; + } + + return IntPtr.Zero; + } + + /// + /// Lookup default constructor via the typesystem api surface and such + /// + private IntPtr TryGetDefaultConstructorForTypeViaMetadata_Inner(TypeDesc type) + { + IntPtr metadataLookupResult = IntPtr.Zero; + + DefType defType = type as DefType; + + if (defType != null) + { + if (!defType.IsValueType && defType is MetadataType) + { + MethodDesc defaultConstructor = ((MetadataType)defType).GetDefaultConstructor(); + if (defaultConstructor != null) + { + IntPtr dummyUnboxingStub; + TypeLoaderEnvironment.MethodAddressType foundAddressType; + TypeLoaderEnvironment.TryGetMethodAddressFromMethodDesc(defaultConstructor, out metadataLookupResult, out dummyUnboxingStub, out foundAddressType); + } + } + } + + return metadataLookupResult; + } + + /// + /// Attempt to locate the default type constructor in a given module. + /// + /// Module to search for the constructor + /// Canonically equivalent entry locator representing the type + /// Function pointer representing the constructor, IntPtr.Zero when not found + internal unsafe IntPtr TryGetDefaultConstructorForType_Inner(NativeFormatModuleInfo mappingTableModule, ref CanonicallyEquivalentEntryLocator canonHelper) + { + NativeReader invokeMapReader; + if (TryGetNativeReaderForBlob(mappingTableModule, ReflectionMapBlob.InvokeMap, out invokeMapReader)) + { + NativeParser invokeMapParser = new NativeParser(invokeMapReader, 0); + NativeHashtable invokeHashtable = new NativeHashtable(invokeMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(mappingTableModule); + + var lookup = invokeHashtable.Lookup(canonHelper.LookupHashCode); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + InvokeTableFlags entryFlags = (InvokeTableFlags)entryParser.GetUnsigned(); + if ((entryFlags & InvokeTableFlags.IsDefaultConstructor) == 0) + continue; + + entryParser.GetUnsigned(); // Skip method handle or the NameAndSig cookie + + RuntimeTypeHandle entryType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!canonHelper.IsCanonicallyEquivalent(entryType)) + continue; + + return externalReferences.GetFunctionPointerFromIndex(entryParser.GetUnsigned()); + } + } + + // If not found in the invoke map, try the default constructor map + NativeReader defaultCtorMapReader; + if (TryGetNativeReaderForBlob(mappingTableModule, ReflectionMapBlob.DefaultConstructorMap, out defaultCtorMapReader)) + { + NativeParser defaultCtorMapParser = new NativeParser(defaultCtorMapReader, 0); + NativeHashtable defaultCtorHashtable = new NativeHashtable(defaultCtorMapParser); + + ExternalReferencesTable externalReferencesForDefaultCtorMap = default(ExternalReferencesTable); + externalReferencesForDefaultCtorMap.InitializeCommonFixupsTable(mappingTableModule); + var lookup = defaultCtorHashtable.Lookup(canonHelper.LookupHashCode); + NativeParser defaultCtorParser; + while (!(defaultCtorParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle entryType = externalReferencesForDefaultCtorMap.GetRuntimeTypeHandleFromIndex(defaultCtorParser.GetUnsigned()); + if (!canonHelper.IsCanonicallyEquivalent(entryType)) + continue; + + return externalReferencesForDefaultCtorMap.GetFunctionPointerFromIndex(defaultCtorParser.GetUnsigned()); + } + } + return IntPtr.Zero; + } + + /// + /// Try to resolve a member reference in all registered binary modules containing metadata. + /// + /// Metadata reader for the member reference + /// Member reference handle (method, field, property, event) to resolve + /// Metadata reader for the resolved reference + /// Resolved runtime handle to the containing type + /// Resolved handle to the referenced member + /// true when the lookup was successful; false when not + public static bool TryResolveMemberReference( + MetadataReader metadataReader, + MemberReferenceHandle memberReferenceHandle, + out MetadataReader resolvedMetadataReader, + out RuntimeTypeHandle resolvedContainingTypeHandle, + out Handle resolvedMemberHandle) + { + // TODO + resolvedMetadataReader = null; + resolvedContainingTypeHandle = default(RuntimeTypeHandle); + resolvedMemberHandle = default(Handle); + return false; + } + + /// + /// Get the information necessary to resolve to metadata given a vtable slot and a type that defines that vtable slot + /// + /// type context to use. + /// Type that defines the vtable slot. (Derived types are not valid here) + /// vtable slot index + /// output name/sig of method + /// + public static unsafe bool TryGetMethodMethodNameAndSigFromVTableSlotForPregeneratedOrTemplateType(TypeSystemContext context, RuntimeTypeHandle type, int vtableSlot, out MethodNameAndSignature methodNameAndSig) + { + // + // NOTE: The semantics of the vtable slot and method declaring type in the VirtualInvokeMap table have slight differences between ProjectN and CoreRT ABIs. + // See comment in TryGetVirtualResolveData for more details. + // + + int logicalSlot = vtableSlot; + MethodTable* ptrType = type.ToEETypePtr(); + RuntimeTypeHandle openOrNonGenericTypeDefinition = default(RuntimeTypeHandle); + + // Compute the logical slot by removing space reserved for generic dictionary pointers + if (ptrType->IsInterface && ptrType->IsGeneric) + { + openOrNonGenericTypeDefinition = RuntimeAugments.GetGenericDefinition(type); + logicalSlot--; + } + else + { + MethodTable* searchForSharedGenericTypesInParentHierarchy = ptrType; + while (searchForSharedGenericTypesInParentHierarchy != null) + { + // See if this type is shared generic. If so, adjust the slot by 1. + if (searchForSharedGenericTypesInParentHierarchy->IsGeneric) + { + RuntimeTypeHandle[] genericTypeArgs; + RuntimeTypeHandle genericDefinition = RuntimeAugments.GetGenericInstantiation(searchForSharedGenericTypesInParentHierarchy->ToRuntimeTypeHandle(), + out genericTypeArgs); + + if (Instance.ConversionToCanonFormIsAChange(genericTypeArgs, CanonicalFormKind.Specific)) + { + // Shared generic types have a slot dedicated to holding the generic dictionary. + logicalSlot--; + } + if (openOrNonGenericTypeDefinition.IsNull()) + openOrNonGenericTypeDefinition = genericDefinition; + } + else if (searchForSharedGenericTypesInParentHierarchy->IsArray) + { + // Arrays are like shared generics + RuntimeTypeHandle arrayElementTypeHandle = searchForSharedGenericTypesInParentHierarchy->RelatedParameterType->ToRuntimeTypeHandle(); + + TypeDesc arrayElementType = context.ResolveRuntimeTypeHandle(arrayElementTypeHandle); + TypeDesc canonFormOfArrayElementType = context.ConvertToCanon(arrayElementType, CanonicalFormKind.Specific); + + if (canonFormOfArrayElementType != arrayElementType) + { + logicalSlot--; + } + } + + // Walk to parent + searchForSharedGenericTypesInParentHierarchy = searchForSharedGenericTypesInParentHierarchy->BaseType; + } + } + + if (openOrNonGenericTypeDefinition.IsNull()) + openOrNonGenericTypeDefinition = type; + + TypeManagerHandle moduleHandle = RuntimeAugments.GetModuleFromTypeHandle(openOrNonGenericTypeDefinition); + NativeFormatModuleInfo module = ModuleList.Instance.GetModuleInfoByHandle(moduleHandle); + + return TryGetMethodNameAndSigFromVirtualResolveData(module, openOrNonGenericTypeDefinition, logicalSlot, out methodNameAndSig); + } + + public struct VirtualResolveDataResult + { + public RuntimeTypeHandle DeclaringInvokeType; + public ushort SlotIndex; + public RuntimeMethodHandle GVMHandle; + public bool IsGVM; + } + + public static bool TryGetVirtualResolveData(NativeFormatModuleInfo module, + RuntimeTypeHandle methodHandleDeclaringType, RuntimeTypeHandle[] genericArgs, + ref MethodSignatureComparer methodSignatureComparer, + out VirtualResolveDataResult lookupResult) + { + lookupResult = default(VirtualResolveDataResult); + NativeReader invokeMapReader = GetNativeReaderForBlob(module, ReflectionMapBlob.VirtualInvokeMap); + NativeParser invokeMapParser = new NativeParser(invokeMapReader, 0); + NativeHashtable invokeHashtable = new NativeHashtable(invokeMapParser); + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + // + // On CoreRT, the vtable entries for each instantiated type might not necessarily exist. + // Example 1: + // If there's a call to Foo.Method1 and a call to Foo.Method2, Foo will + // not have Method2 in its vtable and Foo will not have Method1. + // Example 2: + // If there's a call to Foo.Method1 and a call to Foo.Method2, given that both + // of these instantiations share the same canonical form, Foo<__Canon> will have both method + // entries, and therefore Foo and Foo will have both entries too. + // For this reason, the entries that we write to the map in CoreRT will be based on the canonical form + // of the method's containing type instead of the open type definition. + // + + CanonicallyEquivalentEntryLocator canonHelper = new CanonicallyEquivalentEntryLocator( + methodHandleDeclaringType, + CanonicalFormKind.Specific); + + var lookup = invokeHashtable.Lookup(canonHelper.LookupHashCode); + + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + // Grammar of an entry in the hash table: + // Virtual Method uses a normal slot + // TypeKey + NameAndSig metadata offset into the native layout metadata + (NumberOfStepsUpParentHierarchyToType << 1) + slot + // OR + // Generic Virtual Method + // TypeKey + NameAndSig metadata offset into the native layout metadata + (NumberOfStepsUpParentHierarchyToType << 1 + 1) + + RuntimeTypeHandle entryType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!canonHelper.IsCanonicallyEquivalent(entryType)) + continue; + + uint nameAndSigPointerToken = entryParser.GetUnsigned(); + + MethodNameAndSignature nameAndSig; + if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(module.Handle, nameAndSigPointerToken, out nameAndSig)) + { + Debug.Assert(false); + continue; + } + + if (!methodSignatureComparer.IsMatchingNativeLayoutMethodNameAndSignature(nameAndSig.Name, nameAndSig.Signature)) + { + continue; + } + + uint parentHierarchyAndFlag = entryParser.GetUnsigned(); + uint parentHierarchy = parentHierarchyAndFlag >> 1; + RuntimeTypeHandle declaringTypeOfVirtualInvoke = methodHandleDeclaringType; + for (uint iType = 0; iType < parentHierarchy; iType++) + { + if (!RuntimeAugments.TryGetBaseType(declaringTypeOfVirtualInvoke, out declaringTypeOfVirtualInvoke)) + { + Debug.Assert(false); // This will only fail if the virtual invoke data is malformed as specifies that a type + // has a deeper inheritance hierarchy than it actually does. + return false; + } + } + + bool isGenericVirtualMethod = ((parentHierarchyAndFlag & VirtualInvokeTableEntry.FlagsMask) == VirtualInvokeTableEntry.GenericVirtualMethod); + + Debug.Assert(isGenericVirtualMethod == ((genericArgs != null) && genericArgs.Length > 0)); + + if (isGenericVirtualMethod) + { + RuntimeSignature methodName; + RuntimeSignature methodSignature; + + if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignaturePointersFromNativeLayoutSignature(module.Handle, nameAndSigPointerToken, out methodName, out methodSignature)) + { + Debug.Assert(false); + return false; + } + + RuntimeMethodHandle gvmSlot = TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents(declaringTypeOfVirtualInvoke, methodName.NativeLayoutSignature(), methodSignature, genericArgs); + + lookupResult = new VirtualResolveDataResult + { + DeclaringInvokeType = declaringTypeOfVirtualInvoke, + SlotIndex = 0, + GVMHandle = gvmSlot, + IsGVM = true + }; + return true; + } + else + { + uint slot = entryParser.GetUnsigned(); + + lookupResult = new VirtualResolveDataResult + { + DeclaringInvokeType = declaringTypeOfVirtualInvoke, + SlotIndex = checked((ushort)slot), + GVMHandle = default(RuntimeMethodHandle), + IsGVM = false + }; + return true; + } + } + return false; + } + + /// + /// Given a virtual logical slot and its open defining type, get information necessary to acquire the associated metadata from the mapping tables. + /// + /// Module to look in + /// Declaring type that is known to define the slot + /// The logical slot that the method goes in. For this method, the logical + /// slot is defined as the nth virtual method defined in order on the type (including base types). + /// VTable slots reserved for dictionary pointers are ignored. + /// The name and signature of the method + /// true if a definition is found, false if not + private static unsafe bool TryGetMethodNameAndSigFromVirtualResolveData(NativeFormatModuleInfo module, + RuntimeTypeHandle declaringType, int logicalSlot, out MethodNameAndSignature methodNameAndSig) + { + // + // NOTE: The semantics of the vtable slot and method declaring type in the VirtualInvokeMap table have slight differences between ProjectN and CoreRT ABIs. + // See comment in TryGetVirtualResolveData for more details. + // + + NativeReader invokeMapReader = GetNativeReaderForBlob(module, ReflectionMapBlob.VirtualInvokeMap); + NativeParser invokeMapParser = new NativeParser(invokeMapReader, 0); + NativeHashtable invokeHashtable = new NativeHashtable(invokeMapParser); + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + CanonicallyEquivalentEntryLocator canonHelper = new CanonicallyEquivalentEntryLocator(declaringType, CanonicalFormKind.Specific); + + methodNameAndSig = default(MethodNameAndSignature); + + var lookup = invokeHashtable.Lookup(canonHelper.LookupHashCode); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + // Grammar of an entry in the hash table: + // Virtual Method uses a normal slot + // TypeKey + NameAndSig metadata offset into the native layout metadata + (NumberOfStepsUpParentHierarchyToType << 1) + slot + // OR + // Generic Virtual Method + // TypeKey + NameAndSig metadata offset into the native layout metadata + (NumberOfStepsUpParentHierarchyToType << 1 + 1) + + RuntimeTypeHandle entryType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!canonHelper.IsCanonicallyEquivalent(entryType)) + continue; + + uint nameAndSigPointerToken = entryParser.GetUnsigned(); + + uint parentHierarchyAndFlag = entryParser.GetUnsigned(); + bool isGenericVirtualMethod = ((parentHierarchyAndFlag & VirtualInvokeTableEntry.FlagsMask) == VirtualInvokeTableEntry.GenericVirtualMethod); + + // We're looking for a method with a specific slot. By definition, it isn't a GVM as we define GVM as not having slots in the vtable + if (isGenericVirtualMethod) + continue; + + uint mappingTableSlot = entryParser.GetUnsigned(); + + // Slot doesn't match + if (logicalSlot != mappingTableSlot) + continue; + + if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(module.Handle, nameAndSigPointerToken, out methodNameAndSig)) + { + Debug.Assert(false); + continue; + } + + return true; + } + return false; + } + + /// + /// Try to look up method invoke info for given canon. + /// + /// Declaring type for the method + /// Method handle + /// Handles of generic argument types + /// Helper class used to compare method signatures + /// Canonical form to use + /// Output - metadata information for method invoker construction + /// true when found, false otherwise + public static bool TryGetMethodInvokeMetadata( + RuntimeTypeHandle declaringTypeHandle, + QMethodDefinition methodHandle, + RuntimeTypeHandle[] genericMethodTypeArgumentHandles, + ref MethodSignatureComparer methodSignatureComparer, + CanonicalFormKind canonFormKind, + out MethodInvokeMetadata methodInvokeMetadata) + { + if (methodHandle.IsNativeFormatMetadataBased) + { + if (TryGetMethodInvokeMetadataFromInvokeMap( + methodHandle.NativeFormatReader, + declaringTypeHandle, + methodHandle.NativeFormatHandle, + genericMethodTypeArgumentHandles, + ref methodSignatureComparer, + canonFormKind, + out methodInvokeMetadata)) + { + return true; + } + } + + TypeSystemContext context = TypeSystemContextFactory.Create(); + + bool success = TryGetMethodInvokeMetadataFromNativeFormatMetadata( + declaringTypeHandle, + methodHandle, + genericMethodTypeArgumentHandles, + ref methodSignatureComparer, + context, + canonFormKind, + out methodInvokeMetadata); + + TypeSystemContextFactory.Recycle(context); + + return success; + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + /// + /// Try to look up method invoke info for given canon in InvokeMap blobs for all available modules. + /// + /// Metadata MethodDesc to look for + /// method to search for + /// Helper class used to compare method signatures + /// Canonical form to use + /// Output - Output code address + /// Output - The type of method address match found. A canonical address may require extra parameters to call. + /// true when found, false otherwise + private static bool TryGetMethodInvokeDataFromInvokeMap( + NativeFormatMethod typicalMethodDesc, + MethodDesc method, + ref MethodSignatureComparer methodSignatureComparer, + CanonicalFormKind canonFormKind, + out IntPtr methodEntryPoint, + out MethodAddressType foundAddressType) + { + methodEntryPoint = IntPtr.Zero; + foundAddressType = MethodAddressType.None; + + CanonicallyEquivalentEntryLocator canonHelper = new CanonicallyEquivalentEntryLocator(method.OwningType.GetClosestDefType(), canonFormKind); + + TypeManagerHandle methodHandleModule = typicalMethodDesc.MetadataUnit.RuntimeModule; + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(methodHandleModule)) + { + NativeReader invokeMapReader; + if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.InvokeMap, out invokeMapReader)) + { + continue; + } + + NativeParser invokeMapParser = new NativeParser(invokeMapReader, 0); + NativeHashtable invokeHashtable = new NativeHashtable(invokeMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = invokeHashtable.Lookup(canonHelper.LookupHashCode); + var entryData = new InvokeMapEntryDataEnumerator( + new TypeSystemTypeComparator(method), + canonFormKind, + module.Handle, + typicalMethodDesc.Handle, + methodHandleModule); + + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + entryData.GetNext(ref entryParser, ref externalReferences, ref methodSignatureComparer, canonHelper); + + if (!entryData.IsMatchingOrCompatibleEntry()) + continue; + + IntPtr rawMethodEntryPoint; + bool needsDictionaryForCall; + + if (entryData.GetMethodEntryPoint( + out methodEntryPoint, + out needsDictionaryForCall, + out rawMethodEntryPoint)) + { + // At this time, because we don't have any logic which generates a true fat function pointer + // in the TypeSystemTypeComparator, rawMethodEntryPoint should always be the same as methodEntryPoint + Debug.Assert(rawMethodEntryPoint == methodEntryPoint); + + if (canonFormKind == CanonicalFormKind.Universal) + { + foundAddressType = MethodAddressType.UniversalCanonical; + } + else + { + Debug.Assert(canonFormKind == CanonicalFormKind.Specific); + + if (needsDictionaryForCall) + { + foundAddressType = MethodAddressType.Canonical; + } + else + { + if (method.OwningType.IsValueType && method.OwningType != method.OwningType.ConvertToCanonForm(canonFormKind) && !method.Signature.IsStatic) + { + // The entrypoint found is the unboxing stub for a non-generic instance method on a structure + foundAddressType = MethodAddressType.Canonical; + } + else + { + foundAddressType = MethodAddressType.Exact; // We may or may not have found a canonical method here, but if its exactly callable... its close enough + } + } + } + } + + return true; + } + } + + return false; + } +#endif + + /// + /// Try to look up method invoke info for given canon in InvokeMap blobs for all available modules. + /// + /// Metadata reader for the declaring type + /// Declaring type for the method + /// Method handle + /// Handles of generic argument types + /// Helper class used to compare method signatures + /// Canonical form to use + /// Output - metadata information for method invoker construction + /// true when found, false otherwise + private static bool TryGetMethodInvokeMetadataFromInvokeMap( + MetadataReader metadataReader, + RuntimeTypeHandle declaringTypeHandle, + MethodHandle methodHandle, + RuntimeTypeHandle[] genericMethodTypeArgumentHandles, + ref MethodSignatureComparer methodSignatureComparer, + CanonicalFormKind canonFormKind, + out MethodInvokeMetadata methodInvokeMetadata) + { + CanonicallyEquivalentEntryLocator canonHelper = new CanonicallyEquivalentEntryLocator(declaringTypeHandle, canonFormKind); + TypeManagerHandle methodHandleModule = ModuleList.Instance.GetModuleForMetadataReader(metadataReader); + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(declaringTypeHandle))) + { + NativeReader invokeMapReader; + if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.InvokeMap, out invokeMapReader)) + { + continue; + } + + NativeParser invokeMapParser = new NativeParser(invokeMapReader, 0); + NativeHashtable invokeHashtable = new NativeHashtable(invokeMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = invokeHashtable.Lookup(canonHelper.LookupHashCode); + var entryData = new InvokeMapEntryDataEnumerator( + new PreloadedTypeComparator(declaringTypeHandle, genericMethodTypeArgumentHandles), + canonFormKind, + module.Handle, + methodHandle, + methodHandleModule); + + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + entryData.GetNext(ref entryParser, ref externalReferences, ref methodSignatureComparer, canonHelper); + + if (!entryData.IsMatchingOrCompatibleEntry()) + continue; + + if (entryData.GetMethodEntryPoint( + out methodInvokeMetadata.MethodEntryPoint, + out methodInvokeMetadata.DictionaryComponent, + out methodInvokeMetadata.RawMethodEntryPoint)) + { + methodInvokeMetadata.MappingTableModule = module; + methodInvokeMetadata.DynamicInvokeCookie = entryData._dynamicInvokeCookie; + methodInvokeMetadata.InvokeTableFlags = entryData._flags; + + return true; + } + } + } + + methodInvokeMetadata = default(MethodInvokeMetadata); + return false; + } + + /// + /// Look up method entry point based on native format metadata information. + /// + /// Declaring type for the method + /// Method handle + /// Handles of generic argument types + /// Helper class used to compare method signatures + /// Type system context to use + /// Canonical form to use + /// Output - metadata information for method invoker construction + /// true when found, false otherwise + private static bool TryGetMethodInvokeMetadataFromNativeFormatMetadata( + RuntimeTypeHandle declaringTypeHandle, + QMethodDefinition methodHandle, + RuntimeTypeHandle[] genericMethodTypeArgumentHandles, + ref MethodSignatureComparer methodSignatureComparer, + TypeSystemContext typeSystemContext, + CanonicalFormKind canonFormKind, + out MethodInvokeMetadata methodInvokeMetadata) + { + methodInvokeMetadata = default(MethodInvokeMetadata); + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + TypeDesc declaringType = typeSystemContext.ResolveRuntimeTypeHandle(declaringTypeHandle); + TypeDesc declaringTypeDefinition = declaringType.GetTypeDefinition(); + + if (declaringTypeDefinition == null) + return false; + + + MethodDesc methodOnType = null; + + if (declaringTypeDefinition is NativeFormatType) + { + NativeFormatType nativeFormatType = ((NativeFormatType)declaringTypeDefinition); + Debug.Assert(methodHandle.NativeFormatReader == nativeFormatType.MetadataReader); + methodOnType = nativeFormatType.MetadataUnit.GetMethod(methodHandle.NativeFormatHandle, nativeFormatType); + } + else + { + EcmaType ecmaType = ((EcmaType)declaringTypeDefinition); + Debug.Assert(methodHandle.EcmaFormatReader == ecmaType.MetadataReader); + methodOnType = ecmaType.EcmaModule.GetMethod(methodHandle.EcmaFormatHandle); + } + + if (methodOnType == null) + { + return false; + } + + if (declaringTypeDefinition != declaringType) + { + // If we reach here, then the method is on a generic type, and we just found the uninstantiated form + // Get the method on the instantiated type and continue + methodOnType = typeSystemContext.GetMethodForInstantiatedType(methodOnType, (InstantiatedType)declaringType); + } + + if (genericMethodTypeArgumentHandles.Length > 0) + { + // If we reach here, this is a generic method, instantiate and continue + methodOnType = typeSystemContext.GetInstantiatedMethod(methodOnType, typeSystemContext.ResolveRuntimeTypeHandles(genericMethodTypeArgumentHandles)); + } + + IntPtr entryPoint = IntPtr.Zero; + IntPtr unboxingStubAddress = IntPtr.Zero; + MethodAddressType foundAddressType = MethodAddressType.None; +#if SUPPORT_DYNAMIC_CODE + if (foundAddressType == MethodAddressType.None) + MethodEntrypointStubs.TryGetMethodEntrypoint(methodOnType, out entryPoint, out unboxingStubAddress, out foundAddressType); +#endif + if (foundAddressType == MethodAddressType.None) + return false; + + // Only find a universal canon implementation if searching for one + if (foundAddressType == MethodAddressType.UniversalCanonical && + !((canonFormKind == CanonicalFormKind.Universal) || (canonFormKind == CanonicalFormKind.Any))) + { + return false; + } + + // TODO: This will probably require additional work to smoothly use unboxing stubs + // in vtables - for plain reflection invoke everything seems to work + // without additional changes thanks to the "NeedsParameterInterpretation" flag. + if (methodHandle.IsNativeFormatMetadataBased) + methodInvokeMetadata.MappingTableModule = ((NativeFormatType)declaringTypeDefinition).MetadataUnit.RuntimeModuleInfo; + else + methodInvokeMetadata.MappingTableModule = null; // MappingTableModule is only used if NeedsParameterInterpretation isn't set + methodInvokeMetadata.MethodEntryPoint = entryPoint; + methodInvokeMetadata.RawMethodEntryPoint = entryPoint; + // TODO: methodInvokeMetadata.DictionaryComponent + // TODO: methodInvokeMetadata.DefaultValueString + // TODO: methodInvokeMetadata.DynamicInvokeCookie + + methodInvokeMetadata.InvokeTableFlags = + InvokeTableFlags.HasMetadataHandle | + InvokeTableFlags.HasEntrypoint | + InvokeTableFlags.NeedsParameterInterpretation; + if (methodOnType.Signature.GenericParameterCount != 0) + { + methodInvokeMetadata.InvokeTableFlags |= InvokeTableFlags.IsGenericMethod; + } + if (canonFormKind == CanonicalFormKind.Universal) + { + methodInvokeMetadata.InvokeTableFlags |= InvokeTableFlags.IsUniversalCanonicalEntry; + } + /* TODO + if (methodOnType.HasDefaultParameters) + { + methodInvokeMetadata.InvokeTableFlags |= InvokeTableFlags.HasDefaultParameters; + } + */ + + return true; +#else + return false; +#endif + } + + // Api surface for controlling invoke map enumeration. + private interface IInvokeMapEntryDataDeclaringTypeAndGenericMethodParameterHandling + { + bool GetTypeDictionary(out TDictionaryComponentType dictionary); + bool GetMethodDictionary(MethodNameAndSignature nameAndSignature, out TDictionaryComponentType dictionary); + bool IsUninterestingDictionaryComponent(TDictionaryComponentType dictionary); + bool CompareMethodInstantiation(RuntimeTypeHandle[] methodInstantiation); + bool CanInstantiationsShareCode(RuntimeTypeHandle[] methodInstantiation, CanonicalFormKind canonFormKind); + IntPtr ProduceFatFunctionPointerMethodEntryPoint(IntPtr methodEntrypoint, TDictionaryComponentType dictionary); + } + + // Comparator for invoke map when used to find an invoke map entry and the search data is a set of + // pre-loaded types, and metadata handles. + private struct PreloadedTypeComparator : IInvokeMapEntryDataDeclaringTypeAndGenericMethodParameterHandling + { + private readonly RuntimeTypeHandle _declaringTypeHandle; + private readonly RuntimeTypeHandle[] _genericMethodTypeArgumentHandles; + + public PreloadedTypeComparator(RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + _declaringTypeHandle = declaringTypeHandle; + _genericMethodTypeArgumentHandles = genericMethodTypeArgumentHandles; + } + + public bool GetTypeDictionary(out IntPtr dictionary) + { + dictionary = RuntimeAugments.GetPointerFromTypeHandle(_declaringTypeHandle); + Debug.Assert(dictionary != IntPtr.Zero); + return true; + } + + public bool GetMethodDictionary(MethodNameAndSignature nameAndSignature, out IntPtr dictionary) + { + return TypeLoaderEnvironment.Instance.TryGetGenericMethodDictionaryForComponents(_declaringTypeHandle, + _genericMethodTypeArgumentHandles, + nameAndSignature, + out dictionary); + } + + public bool IsUninterestingDictionaryComponent(IntPtr dictionary) + { + return dictionary == IntPtr.Zero; + } + + public IntPtr ProduceFatFunctionPointerMethodEntryPoint(IntPtr methodEntrypoint, IntPtr dictionary) + { + return FunctionPointerOps.GetGenericMethodFunctionPointer(methodEntrypoint, dictionary); + } + + public bool CompareMethodInstantiation(RuntimeTypeHandle[] methodInstantiation) + { + return SequenceEqual(_genericMethodTypeArgumentHandles, methodInstantiation); + } + + public bool CanInstantiationsShareCode(RuntimeTypeHandle[] methodInstantiation, CanonicalFormKind canonFormKind) + { + return TypeLoaderEnvironment.Instance.CanInstantiationsShareCode(methodInstantiation, _genericMethodTypeArgumentHandles, canonFormKind); + } + } + + // Comparator for invoke map when used to find an invoke map entry and the search data is + // a type system object with Metadata + private struct TypeSystemTypeComparator : IInvokeMapEntryDataDeclaringTypeAndGenericMethodParameterHandling + { + private readonly MethodDesc _targetMethod; + + public TypeSystemTypeComparator(MethodDesc targetMethod) + { + _targetMethod = targetMethod; + } + + public bool GetTypeDictionary(out bool dictionary) + { + // The true is to indicate a dictionary is necessary + dictionary = true; + return true; + } + + public bool GetMethodDictionary(MethodNameAndSignature nameAndSignature, out bool dictionary) + { + // The true is to indicate a dictionary is necessary + dictionary = true; + return true; + } + + public bool IsUninterestingDictionaryComponent(bool dictionary) + { + return dictionary == false; + } + + public IntPtr ProduceFatFunctionPointerMethodEntryPoint(IntPtr methodEntrypoint, bool dictionary) + { + // We don't actually want to produce the fat function pointer here. We want to delay until its actually needed + return methodEntrypoint; + } + + public bool CompareMethodInstantiation(RuntimeTypeHandle[] methodInstantiation) + { + if (!_targetMethod.HasInstantiation) + return false; + + if (_targetMethod.Instantiation.Length != methodInstantiation.Length) + return false; + + int i = 0; + foreach (TypeDesc instantiationType in _targetMethod.Instantiation) + { + TypeDesc genericArg2 = _targetMethod.Context.ResolveRuntimeTypeHandle(methodInstantiation[i]); + if (instantiationType != genericArg2) + { + return false; + } + i++; + } + + return true; + } + + public bool CanInstantiationsShareCode(RuntimeTypeHandle[] methodInstantiation, CanonicalFormKind canonFormKind) + { + if (!_targetMethod.HasInstantiation) + return false; + + if (_targetMethod.Instantiation.Length != methodInstantiation.Length) + return false; + + int i = 0; + foreach (TypeDesc instantiationType in _targetMethod.Instantiation) + { + TypeSystemContext context = _targetMethod.Context; + TypeDesc genericArg2 = context.ResolveRuntimeTypeHandle(methodInstantiation[i]); + if (context.ConvertToCanon(instantiationType, canonFormKind) != context.ConvertToCanon(genericArg2, canonFormKind)) + { + return false; + } + i++; + } + + return true; + } + } + + // Enumerator for discovering methods in the InvokeMap. This is generic to allow highly efficient + // searching of this table with multiple different input data formats. + private struct InvokeMapEntryDataEnumerator where TLookupMethodInfo : IInvokeMapEntryDataDeclaringTypeAndGenericMethodParameterHandling + { + // Read-only inputs + private TLookupMethodInfo _lookupMethodInfo; + private readonly CanonicalFormKind _canonFormKind; + private readonly TypeManagerHandle _moduleHandle; + private readonly TypeManagerHandle _moduleForMethodHandle; + private readonly MethodHandle _methodHandle; + + // Parsed data from entry in the hashtable + public InvokeTableFlags _flags; + public RuntimeTypeHandle _entryType; + public IntPtr _methodEntrypoint; + public uint _dynamicInvokeCookie; + public IntPtr _entryDictionary; + public RuntimeTypeHandle[] _methodInstantiation; + + // Computed data + private bool _hasEntryPoint; + private bool _isMatchingMethodHandleAndDeclaringType; + private MethodNameAndSignature _nameAndSignature; + private RuntimeTypeHandle[] _entryMethodInstantiation; + + public InvokeMapEntryDataEnumerator( + TLookupMethodInfo lookupMethodInfo, + CanonicalFormKind canonFormKind, + TypeManagerHandle moduleHandle, + MethodHandle methodHandle, + TypeManagerHandle moduleForMethodHandle) + { + _lookupMethodInfo = lookupMethodInfo; + _canonFormKind = canonFormKind; + _moduleHandle = moduleHandle; + _methodHandle = methodHandle; + _moduleForMethodHandle = moduleForMethodHandle; + + _flags = 0; + _entryType = default(RuntimeTypeHandle); + _methodEntrypoint = IntPtr.Zero; + _dynamicInvokeCookie = 0xffffffff; + _hasEntryPoint = false; + _isMatchingMethodHandleAndDeclaringType = false; + _entryDictionary = IntPtr.Zero; + _methodInstantiation = null; + _nameAndSignature = null; + _entryMethodInstantiation = null; + } + + public void GetNext( + ref NativeParser entryParser, + ref ExternalReferencesTable extRefTable, + ref MethodSignatureComparer methodSignatureComparer, + CanonicallyEquivalentEntryLocator canonHelper) + { + // Read flags and reset members data + _flags = (InvokeTableFlags)entryParser.GetUnsigned(); + _hasEntryPoint = ((_flags & InvokeTableFlags.HasEntrypoint) != 0); + _isMatchingMethodHandleAndDeclaringType = false; + _entryType = default(RuntimeTypeHandle); + _methodEntrypoint = IntPtr.Zero; + _dynamicInvokeCookie = 0xffffffff; + _entryDictionary = IntPtr.Zero; + _methodInstantiation = null; + _nameAndSignature = null; + _entryMethodInstantiation = null; + + // If the current entry is not a canonical entry of the same canonical form kind we are looking for, then this cannot be a match + if (((_flags & InvokeTableFlags.IsUniversalCanonicalEntry) != 0) != (_canonFormKind == CanonicalFormKind.Universal)) + return; + + if ((_flags & InvokeTableFlags.HasMetadataHandle) != 0) + { + // Metadata handles are not known cross module, and cannot be compared across modules. + if (_moduleHandle != _moduleForMethodHandle) + return; + + Handle entryMethodHandle = (((uint)HandleType.Method << 24) | entryParser.GetUnsigned()).AsHandle(); + if (!_methodHandle.Equals(entryMethodHandle)) + return; + } + else + { + uint nameAndSigToken = entryParser.GetUnsigned(); + MethodNameAndSignature nameAndSig; + if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(_moduleHandle, nameAndSigToken, out nameAndSig)) + { + Debug.Assert(false); + return; + } + Debug.Assert(nameAndSig.Signature.IsNativeLayoutSignature); + if (!methodSignatureComparer.IsMatchingNativeLayoutMethodNameAndSignature(nameAndSig.Name, nameAndSig.Signature)) + return; + } + + _entryType = extRefTable.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!canonHelper.IsCanonicallyEquivalent(_entryType)) + return; + + // Method handle and entry type match at this point. Continue reading data from the entry... + _isMatchingMethodHandleAndDeclaringType = true; + + if (_hasEntryPoint) + _methodEntrypoint = extRefTable.GetFunctionPointerFromIndex(entryParser.GetUnsigned()); + + if ((_flags & InvokeTableFlags.NeedsParameterInterpretation) == 0) + _dynamicInvokeCookie = entryParser.GetUnsigned(); + + if ((_flags & InvokeTableFlags.IsGenericMethod) == 0) + return; + + if ((_flags & InvokeTableFlags.IsUniversalCanonicalEntry) != 0) + { + Debug.Assert((_hasEntryPoint || ((_flags & InvokeTableFlags.HasVirtualInvoke) != 0)) && ((_flags & InvokeTableFlags.RequiresInstArg) != 0)); + + uint nameAndSigPointerToken = entryParser.GetUnsigned(); + if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(_moduleHandle, nameAndSigPointerToken, out _nameAndSignature)) + { + Debug.Assert(false); //Error + _isMatchingMethodHandleAndDeclaringType = false; + } + } + else if (((_flags & InvokeTableFlags.RequiresInstArg) != 0) && _hasEntryPoint) + _entryDictionary = extRefTable.GetGenericDictionaryFromIndex(entryParser.GetUnsigned()); + else + _methodInstantiation = GetTypeSequence(ref extRefTable, ref entryParser); + } + + public bool IsMatchingOrCompatibleEntry() + { + // Check if method handle and entry type were matching or compatible + if (!_isMatchingMethodHandleAndDeclaringType) + return false; + + // Nothing special about non-generic methods. + if ((_flags & InvokeTableFlags.IsGenericMethod) == 0) + return true; + + // A universal canonical method entry can share code with any method instantiation (no need to call CanInstantiationsShareCode()) + if ((_flags & InvokeTableFlags.IsUniversalCanonicalEntry) != 0) + { + Debug.Assert(_canonFormKind == CanonicalFormKind.Universal); + return true; + } + + // Generic non-shareable method or abstract methods: check for the canonical equivalency of the method + // instantiation arguments that we read from the entry + if (((_flags & InvokeTableFlags.RequiresInstArg) == 0) || !_hasEntryPoint) + return _lookupMethodInfo.CanInstantiationsShareCode(_methodInstantiation, _canonFormKind); + + // Generic shareable method: check for canonical equivalency of the method instantiation arguments. + // The method instantiation arguments are extracted from the generic dictionary pointer that we read from the entry. + Debug.Assert(_entryDictionary != IntPtr.Zero); + return GetNameAndSignatureAndMethodInstantiation() && _lookupMethodInfo.CanInstantiationsShareCode(_entryMethodInstantiation, _canonFormKind); + } + + public bool GetMethodEntryPoint(out IntPtr methodEntrypoint, out TDictionaryComponentType dictionaryComponent, out IntPtr rawMethodEntrypoint) + { + // Debug-only sanity check before proceeding (IsMatchingOrCompatibleEntry is called from TryGetDynamicMethodInvokeInfo) + Debug.Assert(IsMatchingOrCompatibleEntry()); + + rawMethodEntrypoint = _methodEntrypoint; + methodEntrypoint = IntPtr.Zero; + dictionaryComponent = default(TDictionaryComponentType); + + if (!GetDictionaryComponent(out dictionaryComponent) || !GetMethodEntryPointComponent(dictionaryComponent, out methodEntrypoint)) + return false; + + return true; + } + + private bool GetDictionaryComponent(out TDictionaryComponentType dictionaryComponent) + { + dictionaryComponent = default(TDictionaryComponentType); + + if (((_flags & InvokeTableFlags.RequiresInstArg) == 0) || !_hasEntryPoint) + return true; + + // Dictionary for non-generic method is the type handle of the declaring type + if ((_flags & InvokeTableFlags.IsGenericMethod) == 0) + { + return _lookupMethodInfo.GetTypeDictionary(out dictionaryComponent); + } + + // Dictionary for generic method (either found statically or constructed dynamically) + return GetNameAndSignatureAndMethodInstantiation() && _lookupMethodInfo.GetMethodDictionary(_nameAndSignature, out dictionaryComponent); + } + + private bool GetMethodEntryPointComponent(TDictionaryComponentType dictionaryComponent, out IntPtr methodEntrypoint) + { + methodEntrypoint = _methodEntrypoint; + + if (_lookupMethodInfo.IsUninterestingDictionaryComponent(dictionaryComponent)) + return true; + + // Do not use a fat function-pointer for universal canonical methods because the converter data block already holds the + // dictionary pointer so it serves as its own instantiating stub + if ((_flags & InvokeTableFlags.IsUniversalCanonicalEntry) == 0) + methodEntrypoint = _lookupMethodInfo.ProduceFatFunctionPointerMethodEntryPoint(_methodEntrypoint, dictionaryComponent); + + return true; + } + + private bool GetNameAndSignatureAndMethodInstantiation() + { + if (_nameAndSignature != null) + { + Debug.Assert(((_flags & InvokeTableFlags.IsUniversalCanonicalEntry) != 0) || (_entryMethodInstantiation != null && _entryMethodInstantiation.Length > 0)); + return true; + } + + if ((_flags & InvokeTableFlags.IsUniversalCanonicalEntry) != 0) + { + // _nameAndSignature should have been read from the InvokeMap entry directly! + Debug.Fail("Universal canonical entries do NOT have dictionary entries!"); + return false; + } + + RuntimeTypeHandle dummy1; + bool success = TypeLoaderEnvironment.Instance.TryGetGenericMethodComponents(_entryDictionary, out dummy1, out _nameAndSignature, out _entryMethodInstantiation); + Debug.Assert(success && dummy1.Equals(_entryType) && _nameAndSignature != null && _entryMethodInstantiation != null && _entryMethodInstantiation.Length > 0); + return success; + } + } + + public static ModuleInfo GetModuleInfoForType(TypeDesc type) + { + for (;;) + { + type = type.GetTypeDefinition(); +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + NativeFormatType nativeType = type as NativeFormatType; + if (nativeType != null) + { + MetadataReader metadataReader = nativeType.MetadataReader; + ModuleInfo moduleInfo = ModuleList.Instance.GetModuleInfoForMetadataReader(metadataReader); + + return moduleInfo; + } +#endif +#if ECMA_METADATA_SUPPORT + Internal.TypeSystem.Ecma.EcmaType ecmaType = type as Internal.TypeSystem.Ecma.EcmaType; + if (ecmaType != null) + { + return ecmaType.EcmaModule.RuntimeModuleInfo; + } +#endif + ArrayType arrayType = type as ArrayType; + if (arrayType != null) + { + // Arrays are defined in the core shared library + return ModuleList.Instance.SystemModule; + } + InstantiatedType instantiatedType = type as InstantiatedType; + if (instantiatedType != null) + { + type = instantiatedType.GetTypeDefinition(); + } + ParameterizedType parameterizedType = type as ParameterizedType; + if (parameterizedType != null) + { + type = parameterizedType.ParameterType; + continue; + } + // Unable to resolve the native type + return ModuleList.Instance.SystemModule; + } + } + + public bool TryGetMetadataForTypeMethodNameAndSignature(RuntimeTypeHandle declaringTypeHandle, MethodNameAndSignature nameAndSignature, out QMethodDefinition methodHandle) + { + if (!nameAndSignature.Signature.IsNativeLayoutSignature) + { + ModuleInfo moduleInfo = nameAndSignature.Signature.GetModuleInfo(); + +#if ECMA_METADATA_SUPPORT + if (moduleInfo is NativeFormatModuleInfo) +#endif + { + methodHandle = new QMethodDefinition(((NativeFormatModuleInfo)moduleInfo).MetadataReader, nameAndSignature.Signature.Token.AsHandle().ToMethodHandle(null)); + } +#if ECMA_METADATA_SUPPORT + else + { + methodHandle = new QMethodDefinition(((EcmaModuleInfo)moduleInfo).MetadataReader, (System.Reflection.Metadata.MethodDefinitionHandle)System.Reflection.Metadata.Ecma335.MetadataTokens.Handle(nameAndSignature.Signature.Token)); + } +#endif + // When working with method signature that draw directly from metadata, just return the metadata token + return true; + } + + QTypeDefinition qTypeDefinition; + RuntimeTypeHandle metadataLookupTypeHandle = GetTypeDefinition(declaringTypeHandle); + methodHandle = default(QMethodDefinition); + + if (!TryGetMetadataForNamedType(metadataLookupTypeHandle, out qTypeDefinition)) + return false; + + MetadataReader reader = qTypeDefinition.NativeFormatReader; + TypeDefinitionHandle typeDefinitionHandle = qTypeDefinition.NativeFormatHandle; + + TypeDefinition typeDefinition = typeDefinitionHandle.GetTypeDefinition(reader); + + Debug.Assert(nameAndSignature.Signature.IsNativeLayoutSignature); + + foreach (MethodHandle mh in typeDefinition.Methods) + { + Method method = mh.GetMethod(reader); + if (method.Name.StringEquals(nameAndSignature.Name, reader)) + { + MethodSignatureComparer methodSignatureComparer = new MethodSignatureComparer(reader, mh); + if (methodSignatureComparer.IsMatchingNativeLayoutMethodSignature(nameAndSignature.Signature)) + { + methodHandle = new QMethodDefinition(reader, mh); + return true; + } + } + } + + return false; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.MetadataSignatureParsing.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.MetadataSignatureParsing.cs new file mode 100644 index 00000000000000..cebdf43051c454 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.MetadataSignatureParsing.cs @@ -0,0 +1,424 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Runtime; + +using Internal.Runtime.CompilerServices; +using Internal.Metadata.NativeFormat; +using Internal.NativeFormat; +using Internal.Runtime.TypeLoader; +using Internal.Runtime.Augments; + +using System.Reflection.Runtime.General; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime.TypeLoader +{ + internal static class SigParsing + { + public static RuntimeTypeHandle GetTypeFromNativeLayoutSignature(ref NativeParser parser, TypeManagerHandle moduleHandle, uint offset) + { + RuntimeTypeHandle typeHandle; + + parser.Offset = offset; + TypeLoaderEnvironment.Instance.GetTypeFromSignatureAndContext(ref parser, moduleHandle, null, null, out typeHandle); + + return typeHandle; + } + } + + public struct MethodSignatureComparer + { + /// + /// Metadata reader corresponding to the method declaring type + /// + private readonly MetadataReader _metadataReader; + + /// + /// Method handle + /// + private readonly MethodHandle _methodHandle; + + /// + /// Method instance obtained from the method handle + /// + private readonly Method _method; + + /// + /// Method signature + /// + private readonly MethodSignature _methodSignature; + + /// + /// true = this is a static method + /// + private readonly bool _isStatic; + + /// + /// true = this is a generic method + /// + private readonly bool _isGeneric; + + public MethodSignatureComparer( + QMethodDefinition methodHandle) + { + if (methodHandle.IsNativeFormatMetadataBased) + { + _metadataReader = methodHandle.NativeFormatReader; + _methodHandle = methodHandle.NativeFormatHandle; + + _method = _methodHandle.GetMethod(_metadataReader); + + _methodSignature = _method.Signature.GetMethodSignature(_metadataReader); + _isGeneric = (_methodSignature.GenericParameterCount != 0); + + // Precalculate initial method attributes used in signature queries + _isStatic = (_method.Flags & MethodAttributes.Static) != 0; + } + else + { + _metadataReader = null; + _methodHandle = default(MethodHandle); + _method = default(Method); + _methodSignature = default(MethodSignature); + _isGeneric = false; + _isStatic = false; + } + } + + /// + /// Construct a comparer between NativeFormat metadata methods and native layouts + /// + /// Metadata reader for the method declaring type + /// Handle of method to compare + public MethodSignatureComparer( + MetadataReader metadataReader, + MethodHandle methodHandle) + { + _metadataReader = metadataReader; + _methodHandle = methodHandle; + + _method = methodHandle.GetMethod(metadataReader); + + _methodSignature = _method.Signature.GetMethodSignature(_metadataReader); + _isGeneric = (_methodSignature.GenericParameterCount != 0); + + // Precalculate initial method attributes used in signature queries + _isStatic = (_method.Flags & MethodAttributes.Static) != 0; + } + + public bool IsMatchingNativeLayoutMethodNameAndSignature(string name, RuntimeSignature signature) + { + return _method.Name.StringEquals(name, _metadataReader) && + IsMatchingNativeLayoutMethodSignature(signature); + } + + public bool IsMatchingNativeLayoutMethodSignature(RuntimeSignature signature) + { + NativeParser parser = GetNativeParserForSignature(signature); + + if (!CompareCallingConventions((MethodCallingConvention)parser.GetUnsigned())) + return false; + + if (_isGeneric) + { + uint genericParamCount1 = parser.GetUnsigned(); + int genericParamCount2 = _methodSignature.GenericParameterCount; + + if (genericParamCount1 != genericParamCount2) + return false; + } + + uint parameterCount = parser.GetUnsigned(); + + if (!CompareTypeSigWithType(ref parser, new TypeManagerHandle(signature.ModuleHandle), _methodSignature.ReturnType)) + { + return false; + } + + uint parameterIndexToMatch = 0; + foreach (Handle parameterSignature in _methodSignature.Parameters) + { + if (parameterIndexToMatch >= parameterCount) + { + // The metadata-defined _method has more parameters than the native layout + return false; + } + if (!CompareTypeSigWithType(ref parser, new TypeManagerHandle(signature.ModuleHandle), parameterSignature)) + return false; + parameterIndexToMatch++; + } + + // Make sure that all native layout parameters have been matched + return parameterIndexToMatch == parameterCount; + } + + /// + /// Look up module containing given nativesignature and return the appropriate native parser. + /// + /// Signature to look up + /// Native parser for the signature + internal static NativeParser GetNativeParserForSignature(RuntimeSignature signature) + { + Debug.Assert(signature.IsNativeLayoutSignature); + NativeFormatModuleInfo module = ModuleList.Instance.GetModuleInfoByHandle(new TypeManagerHandle(signature.ModuleHandle)); + + NativeReader reader = TypeLoaderEnvironment.GetNativeReaderForBlob(module, ReflectionMapBlob.NativeLayoutInfo); + return new NativeParser(reader, signature.NativeLayoutOffset); + } + + private bool CompareTypeSigWithType(ref NativeParser parser, TypeManagerHandle moduleHandle, Handle typeHandle) + { + while (typeHandle.HandleType == HandleType.TypeSpecification) + { + typeHandle = typeHandle + .ToTypeSpecificationHandle(_metadataReader) + .GetTypeSpecification(_metadataReader) + .Signature; + } + + // startOffset lets us backtrack to the TypeSignatureKind for external types since the TypeLoader + // expects to read it in. + uint startOffset = parser.Offset; + + uint data; + var typeSignatureKind = parser.GetTypeSignatureKind(out data); + + switch (typeSignatureKind) + { + case TypeSignatureKind.Lookback: + { + NativeParser lookbackParser = parser.GetLookbackParser(data); + return CompareTypeSigWithType(ref lookbackParser, moduleHandle, typeHandle); + } + + case TypeSignatureKind.Modifier: + { + // Ensure the modifier kind (vector, pointer, byref) is the same + TypeModifierKind modifierKind = (TypeModifierKind)data; + switch (modifierKind) + { + case TypeModifierKind.Array: + if (typeHandle.HandleType == HandleType.SZArraySignature) + { + return CompareTypeSigWithType(ref parser, moduleHandle, typeHandle + .ToSZArraySignatureHandle(_metadataReader) + .GetSZArraySignature(_metadataReader) + .ElementType); + } + return false; + + case TypeModifierKind.ByRef: + if (typeHandle.HandleType == HandleType.ByReferenceSignature) + { + return CompareTypeSigWithType(ref parser, moduleHandle, typeHandle + .ToByReferenceSignatureHandle(_metadataReader) + .GetByReferenceSignature(_metadataReader) + .Type); + } + return false; + + case TypeModifierKind.Pointer: + if (typeHandle.HandleType == HandleType.PointerSignature) + { + return CompareTypeSigWithType(ref parser, moduleHandle, typeHandle + .ToPointerSignatureHandle(_metadataReader) + .GetPointerSignature(_metadataReader) + .Type); + } + return false; + + default: + Debug.Assert(null == "invalid type modifier kind"); + return false; + } + } + + case TypeSignatureKind.Variable: + { + bool isMethodVar = (data & 0x1) == 1; + uint index = data >> 1; + + if (isMethodVar) + { + if (typeHandle.HandleType == HandleType.MethodTypeVariableSignature) + { + return index == typeHandle + .ToMethodTypeVariableSignatureHandle(_metadataReader) + .GetMethodTypeVariableSignature(_metadataReader) + .Number; + } + } + else + { + if (typeHandle.HandleType == HandleType.TypeVariableSignature) + { + return index == typeHandle + .ToTypeVariableSignatureHandle(_metadataReader) + .GetTypeVariableSignature(_metadataReader) + .Number; + } + } + + return false; + } + + case TypeSignatureKind.MultiDimArray: + { + if (typeHandle.HandleType != HandleType.ArraySignature) + { + return false; + } + + ArraySignature sig = typeHandle + .ToArraySignatureHandle(_metadataReader) + .GetArraySignature(_metadataReader); + + if (data != sig.Rank) + return false; + + if (!CompareTypeSigWithType(ref parser, moduleHandle, sig.ElementType)) + return false; + + uint boundCount1 = parser.GetUnsigned(); + for (uint i = 0; i < boundCount1; i++) + { + parser.GetUnsigned(); + } + + uint lowerBoundCount1 = parser.GetUnsigned(); + + for (uint i = 0; i < lowerBoundCount1; i++) + { + parser.GetUnsigned(); + } + break; + } + + case TypeSignatureKind.FunctionPointer: + { + // callingConvention is in data + uint argCount1 = parser.GetUnsigned(); + + for (uint i = 0; i < argCount1; i++) + { + if (!CompareTypeSigWithType(ref parser, moduleHandle, typeHandle)) + return false; + } + return false; + } + + case TypeSignatureKind.Instantiation: + { + if (typeHandle.HandleType != HandleType.TypeInstantiationSignature) + { + return false; + } + + TypeInstantiationSignature sig = typeHandle + .ToTypeInstantiationSignatureHandle(_metadataReader) + .GetTypeInstantiationSignature(_metadataReader); + + if (!CompareTypeSigWithType(ref parser, moduleHandle, sig.GenericType)) + { + return false; + } + + uint genericArgIndex = 0; + foreach (Handle genericArgumentTypeHandle in sig.GenericTypeArguments) + { + if (genericArgIndex >= data) + { + // The metadata generic has more parameters than the native layour + return false; + } + if (!CompareTypeSigWithType(ref parser, moduleHandle, genericArgumentTypeHandle)) + { + return false; + } + genericArgIndex++; + } + // Make sure all generic parameters have been matched + return genericArgIndex == data; + } + + case TypeSignatureKind.BuiltIn: + case TypeSignatureKind.External: + { + RuntimeTypeHandle type2; + switch (typeHandle.HandleType) + { + case HandleType.TypeDefinition: + if (!TypeLoaderEnvironment.Instance.TryGetNamedTypeForMetadata( + new QTypeDefinition(_metadataReader, typeHandle.ToTypeDefinitionHandle(_metadataReader)), out type2)) + { + return false; + } + break; + + case HandleType.TypeReference: + if (!TypeLoaderEnvironment.TryResolveNamedTypeForTypeReference( + _metadataReader, typeHandle.ToTypeReferenceHandle(_metadataReader), out type2)) + { + return false; + } + break; + + default: + return false; + } + + RuntimeTypeHandle type1 = default(RuntimeTypeHandle); + if (typeSignatureKind == TypeSignatureKind.External) + { + type1 = SigParsing.GetTypeFromNativeLayoutSignature(ref parser, moduleHandle, startOffset); + } + else + { + type1 = ((Internal.TypeSystem.WellKnownType)data).GetRuntimeTypeHandle(); + } + + return type1.Equals(type2); + } + + default: + return false; + } + return true; + } + + private bool CompareCallingConventions(MethodCallingConvention callingConvention) + { + return (callingConvention.HasFlag(MethodCallingConvention.Static) == _isStatic) && + (callingConvention.HasFlag(MethodCallingConvention.Generic) == _isGeneric); + } + + private static bool CanGetTypeHandle(Type type) + { + if (type.HasElementType) + { + return CanGetTypeHandle(type.GetElementType()); + } + else if (type.IsConstructedGenericType) + { + foreach (var typeArg in type.GenericTypeArguments) + { + if (!CanGetTypeHandle(typeArg)) + { + return false; + } + } + } + else if (type.IsGenericParameter) + { + return false; + } + + return true; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.MethodAddress.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.MethodAddress.cs new file mode 100644 index 00000000000000..f91e640f6f6a29 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.MethodAddress.cs @@ -0,0 +1,273 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Reflection.Runtime.General; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.Metadata.NativeFormat; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.TypeSystem.NativeFormat; + +namespace Internal.Runtime.TypeLoader +{ + public sealed partial class TypeLoaderEnvironment + { + public enum MethodAddressType + { + None, + Exact, + Canonical, + UniversalCanonical + } + + /// + /// Resolve a MethodDesc to a callable method address and unboxing stub address. + /// + /// Native metadata method description object + /// Resolved method address + /// Resolved unboxing stub address + /// Output - The type of method address match found. A canonical address may require extra parameters to call. + /// true when the resolution succeeded, false when not + public static bool TryGetMethodAddressFromMethodDesc( + MethodDesc method, + out IntPtr methodAddress, + out IntPtr unboxingStubAddress, + out MethodAddressType foundAddressType) + { + methodAddress = IntPtr.Zero; + unboxingStubAddress = IntPtr.Zero; + foundAddressType = MethodAddressType.None; + +#if SUPPORT_DYNAMIC_CODE + if (foundAddressType == MethodAddressType.None) + MethodEntrypointStubs.TryGetMethodEntrypoint(method, out methodAddress, out unboxingStubAddress, out foundAddressType); +#endif + if (foundAddressType != MethodAddressType.None) + return true; + + // Otherwise try to find it via an invoke map + return TryGetMethodAddressFromTypeSystemMethodViaInvokeMap(method, out methodAddress, out unboxingStubAddress, out foundAddressType); + } + + /// + /// Resolve a MethodDesc to a callable method address and unboxing stub address by searching + /// by searching in the InvokeMaps. This function is a wrapper around TryGetMethodInvokeDataFromInvokeMap + /// that produces output in the format which matches the code table system. + /// + /// Native metadata method description object + /// Resolved method address + /// Resolved unboxing stub address + /// Output - The type of method address match found. A canonical address may require extra parameters to call. + /// true when the resolution succeeded, false when not + private static bool TryGetMethodAddressFromTypeSystemMethodViaInvokeMap( + MethodDesc method, + out IntPtr methodAddress, + out IntPtr unboxingStubAddress, + out MethodAddressType foundAddressType) + { + methodAddress = IntPtr.Zero; + unboxingStubAddress = IntPtr.Zero; + foundAddressType = MethodAddressType.None; +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + NativeFormatMethod nativeFormatMethod = method.GetTypicalMethodDefinition() as NativeFormatMethod; + if (nativeFormatMethod == null) + return false; + + MethodSignatureComparer methodSignatureComparer = new MethodSignatureComparer( + nativeFormatMethod.MetadataReader, nativeFormatMethod.Handle); + + // Try to find a specific canonical match, or if that fails, a universal match + if (TryGetMethodInvokeDataFromInvokeMap( + nativeFormatMethod, + method, + ref methodSignatureComparer, + CanonicalFormKind.Specific, + out methodAddress, + out foundAddressType) || + + TryGetMethodInvokeDataFromInvokeMap( + nativeFormatMethod, + method, + ref methodSignatureComparer, + CanonicalFormKind.Universal, + out methodAddress, + out foundAddressType)) + { + if (method.OwningType.IsValueType && !method.Signature.IsStatic) + { + // In this case the invoke map found an unboxing stub, and we should pull the method address out as well + unboxingStubAddress = methodAddress; + methodAddress = RuntimeAugments.GetCodeTarget(unboxingStubAddress); + + if (!method.HasInstantiation && ((foundAddressType != MethodAddressType.Exact) || method.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any))) + { + IntPtr underlyingTarget; // unboxing and instantiating stub handling + if (!TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(methodAddress, out underlyingTarget)) + { + Environment.FailFast("Expected this to be an unboxing and instantiating stub."); + } + methodAddress = underlyingTarget; + } + } + + return true; + } + +#endif + return false; + } + + /// + /// Attempt a virtual dispatch on a given instanceType based on the method found via a metadata token + /// + private static bool TryDispatchMethodOnTarget_Inner(NativeFormatModuleInfo module, int metadataToken, RuntimeTypeHandle targetInstanceType, out IntPtr methodAddress) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + TypeSystemContext context = TypeSystemContextFactory.Create(); + + NativeFormatMetadataUnit metadataUnit = context.ResolveMetadataUnit(module); + MethodDesc targetMethod = metadataUnit.GetMethod(metadataToken.AsHandle(), null); + TypeDesc instanceType = context.ResolveRuntimeTypeHandle(targetInstanceType); + + MethodDesc realTargetMethod = targetMethod; + + // For non-interface methods we support the target method not being the exact target. (This allows + // a canonical method to be passed in and work for any generic type instantiation.) + if (!targetMethod.OwningType.IsInterface) + realTargetMethod = instanceType.FindMethodOnTypeWithMatchingTypicalMethod(targetMethod); + + bool success = LazyVTableResolver.TryDispatchMethodOnTarget(instanceType, realTargetMethod, out methodAddress); + + TypeSystemContextFactory.Recycle(context); + return success; +#else + methodAddress = IntPtr.Zero; + return false; +#endif + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING +#if DEBUG + private static int s_ConvertDispatchCellInfoCounter; +#endif + + /// + /// Attempt to convert the dispatch cell to a metadata token to a more efficient vtable dispatch or interface/slot dispatch. + /// Failure to convert is not a correctness issue. We also support performing a dispatch based on metadata token alone. + /// + private static DispatchCellInfo ConvertDispatchCellInfo_Inner(NativeFormatModuleInfo module, DispatchCellInfo cellInfo) + { + Debug.Assert(cellInfo.CellType == DispatchCellType.MetadataToken); + + TypeSystemContext context = TypeSystemContextFactory.Create(); + + MethodDesc targetMethod = context.ResolveMetadataUnit(module).GetMethod(cellInfo.MetadataToken.AsHandle(), null); + Debug.Assert(!targetMethod.HasInstantiation); // At this time we do not support generic virtuals through the dispatch mechanism + Debug.Assert(targetMethod.IsVirtual); + if (targetMethod.OwningType.IsInterface) + { + if (!LazyVTableResolver.TryGetInterfaceSlotNumberFromMethod(targetMethod, out cellInfo.InterfaceSlot)) + { + // Unable to resolve interface method. Fail, by not mutating cellInfo + return cellInfo; + } + + if (!targetMethod.OwningType.RetrieveRuntimeTypeHandleIfPossible()) + { + new TypeBuilder().BuildType(targetMethod.OwningType); + } + + cellInfo.CellType = DispatchCellType.InterfaceAndSlot; + cellInfo.InterfaceType = targetMethod.OwningType.RuntimeTypeHandle.ToIntPtr(); + cellInfo.MetadataToken = 0; + } + else + { + // Virtual function case, attempt to resolve to a VTable slot offset. + // If the offset is less than 4096 update the cellInfo +#if DEBUG + // The path of resolving a metadata token at dispatch time is relatively rare in practice. + // Force it to occur in debug builds with much more regularity + if ((s_ConvertDispatchCellInfoCounter % 16) == 0) + { + s_ConvertDispatchCellInfoCounter++; + TypeSystemContextFactory.Recycle(context); + return cellInfo; + } + s_ConvertDispatchCellInfoCounter++; +#endif + + int slotIndexOfMethod = LazyVTableResolver.VirtualMethodToSlotIndex(targetMethod); + int vtableOffset = -1; + if (slotIndexOfMethod >= 0) + vtableOffset = LazyVTableResolver.SlotIndexToEETypeVTableOffset(slotIndexOfMethod); + if ((vtableOffset < 4096) && (vtableOffset != -1)) + { + cellInfo.CellType = DispatchCellType.VTableOffset; + cellInfo.VTableOffset = checked((uint)vtableOffset); + cellInfo.MetadataToken = 0; + } + // Otherwise, do nothing, and resolve with a metadata dispatch later + } + + TypeSystemContextFactory.Recycle(context); + return cellInfo; + } +#endif + + /// + /// Resolve a dispatch on an interface MethodTable/slot index pair to a function pointer + /// + private unsafe bool TryResolveTypeSlotDispatch_Inner(MethodTable* pTargetType, MethodTable* pInterfaceType, ushort slot, out IntPtr methodAddress) + { + methodAddress = IntPtr.Zero; + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + TypeSystemContext context = TypeSystemContextFactory.Create(); + + TypeDesc targetType; + TypeDesc interfaceType; + + targetType = context.ResolveRuntimeTypeHandle(pTargetType->ToRuntimeTypeHandle()); + interfaceType = context.ResolveRuntimeTypeHandle(pInterfaceType->ToRuntimeTypeHandle()); + + if (!(interfaceType.GetTypeDefinition() is MetadataType)) + { + // If the interface open type is not a metadata type, this must be an interface not known in the metadata world. + // Use the redhawk resolver for this directly. + TypeDesc pregeneratedType = LazyVTableResolver.GetMostDerivedPregeneratedOrTemplateLoadedType(targetType); + pregeneratedType.RetrieveRuntimeTypeHandleIfPossible(); + interfaceType.RetrieveRuntimeTypeHandleIfPossible(); + methodAddress = RuntimeAugments.ResolveDispatchOnType(pregeneratedType.RuntimeTypeHandle, interfaceType.RuntimeTypeHandle, slot); + } + else + { + MethodDesc interfaceMethod; + + if (!LazyVTableResolver.TryGetMethodFromInterfaceSlot(interfaceType, slot, out interfaceMethod)) + return false; + + if (!LazyVTableResolver.TryDispatchMethodOnTarget(targetType, interfaceMethod, out methodAddress)) + return false; + } + + TypeSystemContextFactory.Recycle(context); + + return true; +#else + return false; +#endif + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.NamedTypeLookup.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.NamedTypeLookup.cs new file mode 100644 index 00000000000000..697b6c27214987 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.NamedTypeLookup.cs @@ -0,0 +1,311 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +using System.Reflection.Runtime.General; + +using Internal.Runtime; +using Internal.Runtime.Augments; + +using Internal.Metadata.NativeFormat; +using Internal.NativeFormat; +using Internal.TypeSystem; + +namespace Internal.Runtime.TypeLoader +{ + public sealed partial class TypeLoaderEnvironment + { + private class NamedTypeLookupResult + { + public int RuntimeTypeHandleHashcode; + public RuntimeTypeHandle RuntimeTypeHandle; + public QTypeDefinition QualifiedTypeDefinition; + public IntPtr GcStaticFields; + public IntPtr NonGcStaticFields; + public volatile int VersionNumber; + } + + private volatile int _namedTypeLookupLiveVersion; + + private NamedTypeRuntimeTypeHandleToMetadataHashtable _runtimeTypeHandleToMetadataHashtable = new NamedTypeRuntimeTypeHandleToMetadataHashtable(); + + public static IntPtr NoStaticsData { get; } = (IntPtr)1; + + private class NamedTypeRuntimeTypeHandleToMetadataHashtable : LockFreeReaderHashtable + { + protected unsafe override int GetKeyHashCode(RuntimeTypeHandle key) + { + return (int)key.ToEETypePtr()->HashCode; + } + protected override bool CompareKeyToValue(RuntimeTypeHandle key, NamedTypeLookupResult value) + { + return key.Equals(value.RuntimeTypeHandle); + } + + protected unsafe override int GetValueHashCode(NamedTypeLookupResult value) + { + return value.RuntimeTypeHandleHashcode; + } + + protected override bool CompareValueToValue(NamedTypeLookupResult value1, NamedTypeLookupResult value2) + { + if (value1.RuntimeTypeHandle.IsNull() || value2.RuntimeTypeHandle.IsNull()) + { + return value1.QualifiedTypeDefinition.Token.Equals(value2.QualifiedTypeDefinition.Token) && + value1.QualifiedTypeDefinition.Reader.Equals(value2.QualifiedTypeDefinition.Reader); + } + return value1.RuntimeTypeHandle.Equals(value2.RuntimeTypeHandle); + } + + protected override NamedTypeLookupResult CreateValueFromKey(RuntimeTypeHandle key) + { + int hashCode = GetKeyHashCode(key); + + // Iterate over all modules, starting with the module that defines the MethodTable + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(key))) + { + NativeReader typeMapReader; + if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.TypeMap, out typeMapReader)) + { + NativeParser typeMapParser = new NativeParser(typeMapReader, 0); + NativeHashtable typeHashtable = new NativeHashtable(typeMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = typeHashtable.Lookup(hashCode); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle foundType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (foundType.Equals(key)) + { + Handle entryMetadataHandle = entryParser.GetUnsigned().AsHandle(); + if (entryMetadataHandle.HandleType == HandleType.TypeDefinition) + { + MetadataReader metadataReader = module.MetadataReader; + return new NamedTypeLookupResult() + { + QualifiedTypeDefinition = new QTypeDefinition(metadataReader, entryMetadataHandle.ToTypeDefinitionHandle(metadataReader)), + RuntimeTypeHandle = key, + RuntimeTypeHandleHashcode = hashCode + }; + } + } + } + } + } + + return new NamedTypeLookupResult() + { + RuntimeTypeHandle = key, + RuntimeTypeHandleHashcode = hashCode + }; + } + } + + private QTypeDefinitionToRuntimeTypeHandleHashtable _metadataToRuntimeTypeHandleHashtable = new QTypeDefinitionToRuntimeTypeHandleHashtable(); + + private class QTypeDefinitionToRuntimeTypeHandleHashtable : LockFreeReaderHashtable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int _rotl(int value, int shift) + { + return (int)(((uint)value << shift) | ((uint)value >> (32 - shift))); + } + + protected unsafe override int GetKeyHashCode(QTypeDefinition key) + { + return key.Token.GetHashCode() ^ _rotl(key.Reader.GetHashCode(), 8); + } + protected override bool CompareKeyToValue(QTypeDefinition key, NamedTypeLookupResult value) + { + return key.Token.Equals(value.QualifiedTypeDefinition.Token) && + key.Reader.Equals(value.QualifiedTypeDefinition.Reader); + } + + protected unsafe override int GetValueHashCode(NamedTypeLookupResult value) + { + return value.QualifiedTypeDefinition.Token.GetHashCode() ^ _rotl(value.QualifiedTypeDefinition.Reader.GetHashCode(), 8); + } + + protected override bool CompareValueToValue(NamedTypeLookupResult value1, NamedTypeLookupResult value2) + { + return value1.QualifiedTypeDefinition.Token.Equals(value2.QualifiedTypeDefinition.Token) && + value1.QualifiedTypeDefinition.Reader.Equals(value2.QualifiedTypeDefinition.Reader); + } + + protected override NamedTypeLookupResult CreateValueFromKey(QTypeDefinition key) + { + RuntimeTypeHandle foundRuntimeTypeHandle = default(RuntimeTypeHandle); + + if (key.IsNativeFormatMetadataBased) + { + MetadataReader metadataReader = key.NativeFormatReader; + TypeDefinitionHandle typeDefHandle = key.NativeFormatHandle; + int hashCode = typeDefHandle.ComputeHashCode(metadataReader); + + NativeFormatModuleInfo module = ModuleList.Instance.GetModuleInfoForMetadataReader(metadataReader); + + NativeReader typeMapReader; + if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.TypeMap, out typeMapReader)) + { + NativeParser typeMapParser = new NativeParser(typeMapReader, 0); + NativeHashtable typeHashtable = new NativeHashtable(typeMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = typeHashtable.Lookup(hashCode); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + var foundTypeIndex = entryParser.GetUnsigned(); + if (entryParser.GetUnsigned().AsHandle().Equals(typeDefHandle)) + { + foundRuntimeTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(foundTypeIndex); + break; + } + } + } + } + + return new NamedTypeLookupResult() + { + QualifiedTypeDefinition = key, + RuntimeTypeHandle = foundRuntimeTypeHandle, + VersionNumber = TypeLoaderEnvironment.Instance._namedTypeLookupLiveVersion + }; + } + } + + /// + /// Return the metadata handle for a TypeDef if the pay-for-policy enabled this type as browsable. This is used to obtain name and other information for types + /// obtained via typeof() or Object.GetType(). This can include generic types (not to be confused with generic instances). + /// + /// Preconditions: + /// runtimeTypeHandle is a typedef (not a constructed type such as an array or generic instance.) + /// + /// Runtime handle of the type in question + /// TypeDef handle for the type + public unsafe bool TryGetMetadataForNamedType(RuntimeTypeHandle runtimeTypeHandle, out QTypeDefinition qTypeDefinition) + { + NamedTypeLookupResult result = _runtimeTypeHandleToMetadataHashtable.GetOrCreateValue(runtimeTypeHandle); + qTypeDefinition = result.QualifiedTypeDefinition; + return qTypeDefinition.Reader != null; + } + + /// + /// Get the static addresses of a type if it is in the table + /// + /// Runtime handle of the type in question + /// non-gc static field address + /// gc static field address + /// true if nonGcStaticsData/gcStaticsData are valid, false if not + public unsafe bool TryGetStaticsInfoForNamedType(RuntimeTypeHandle runtimeTypeHandle, out IntPtr nonGcStaticsData, out IntPtr gcStaticsData) + { + NamedTypeLookupResult result; + + if (!_runtimeTypeHandleToMetadataHashtable.TryGetValue(runtimeTypeHandle, out result)) + { + gcStaticsData = IntPtr.Zero; + nonGcStaticsData = IntPtr.Zero; + return false; + } + + gcStaticsData = result.GcStaticFields; + nonGcStaticsData = result.NonGcStaticFields; + + bool noResults = gcStaticsData == IntPtr.Zero || gcStaticsData == IntPtr.Zero; + + if (gcStaticsData == (IntPtr)1) + gcStaticsData = IntPtr.Zero; + + if (nonGcStaticsData == (IntPtr)1) + nonGcStaticsData = IntPtr.Zero; + + return result.QualifiedTypeDefinition.Reader != null && !noResults; + } + + /// + /// Return the RuntimeTypeHandle for the named type described in metadata. This is used to implement the Create and Invoke + /// apis for types. + /// + /// Preconditions: + /// metadataReader + typeDefHandle - a valid metadata reader + typeDefinitionHandle where "metadataReader" is one + /// of the metadata readers returned by ExecutionEnvironment.MetadataReaders. + /// + /// Note: Although this method has a "bool" return value like the other mapping table accessors, the Project N pay-for-play design + /// guarantees that any type enabled for metadata also has a RuntimeTypeHandle underneath. + /// + /// TypeDef handle for the type to look up + /// Runtime type handle (MethodTable) for the given type + public unsafe bool TryGetNamedTypeForMetadata(QTypeDefinition qTypeDefinition, out RuntimeTypeHandle runtimeTypeHandle) + { + runtimeTypeHandle = default(RuntimeTypeHandle); + NamedTypeLookupResult result = _metadataToRuntimeTypeHandleHashtable.GetOrCreateValue(qTypeDefinition); + + if (result.VersionNumber <= _namedTypeLookupLiveVersion) + runtimeTypeHandle = result.RuntimeTypeHandle; + + return !runtimeTypeHandle.IsNull(); + } + + public void RegisterNewNamedTypeRuntimeTypeHandle(QTypeDefinition qTypeDefinition, RuntimeTypeHandle runtimeTypeHandle, IntPtr nonGcStaticFields, IntPtr gcStaticFields) + { + TypeLoaderLogger.WriteLine("Register new type with MethodTable = " + runtimeTypeHandle.ToIntPtr().LowLevelToString() + " nonGcStaticFields " + nonGcStaticFields.LowLevelToString() + " gcStaticFields " + gcStaticFields.LowLevelToString()); + NamedTypeLookupResult result = _metadataToRuntimeTypeHandleHashtable.GetOrCreateValue(qTypeDefinition); + + result.VersionNumber = _namedTypeLookupLiveVersion + 1; + result.RuntimeTypeHandle = runtimeTypeHandle; + result.GcStaticFields = gcStaticFields; + result.NonGcStaticFields = nonGcStaticFields; + unsafe + { + result.RuntimeTypeHandleHashcode = (int)runtimeTypeHandle.ToEETypePtr()->HashCode; + } + + NamedTypeLookupResult rthToMetadataResult = _runtimeTypeHandleToMetadataHashtable.AddOrGetExisting(result); + + if (!object.ReferenceEquals(rthToMetadataResult, result)) + { + rthToMetadataResult.QualifiedTypeDefinition = qTypeDefinition; + rthToMetadataResult.GcStaticFields = gcStaticFields; + rthToMetadataResult.NonGcStaticFields = nonGcStaticFields; + } + } + + public void UnregisterNewNamedTypeRuntimeTypeHandle(QTypeDefinition qTypeDefinition, RuntimeTypeHandle runtimeTypeHandle) + { + NamedTypeLookupResult metadataLookupResult; + if (_metadataToRuntimeTypeHandleHashtable.TryGetValue(qTypeDefinition, out metadataLookupResult)) + { + metadataLookupResult.RuntimeTypeHandle = default(RuntimeTypeHandle); + metadataLookupResult.VersionNumber = -1; + } + + NamedTypeLookupResult runtimeTypeHandleResult; + if (_runtimeTypeHandleToMetadataHashtable.TryGetValue(runtimeTypeHandle, out runtimeTypeHandleResult)) + { + metadataLookupResult.GcStaticFields = IntPtr.Zero; + metadataLookupResult.NonGcStaticFields = IntPtr.Zero; + metadataLookupResult.RuntimeTypeHandle = default(RuntimeTypeHandle); + } + } + + public void FinishAddingNewNamedTypes() + { + _namedTypeLookupLiveVersion++; + if (_namedTypeLookupLiveVersion == int.MaxValue) + Environment.FailFast("Too many types loaded"); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs new file mode 100644 index 00000000000000..3820ce7b8cd5d6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs @@ -0,0 +1,774 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Runtime; +using System.Reflection.Runtime.General; + +using Internal.Runtime; +using Internal.Runtime.TypeLoader; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.Metadata.NativeFormat; +using Internal.NativeFormat; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime.TypeLoader +{ + public sealed partial class TypeLoaderEnvironment + { + public bool CompareMethodSignatures(RuntimeSignature signature1, RuntimeSignature signature2) + { + if (signature1.IsNativeLayoutSignature && signature2.IsNativeLayoutSignature) + { + if (signature1.StructuralEquals(signature2)) + return true; + + NativeFormatModuleInfo module1 = ModuleList.GetModuleInfoByHandle(new TypeManagerHandle(signature1.ModuleHandle)); + NativeReader reader1 = GetNativeLayoutInfoReader(signature1); + NativeParser parser1 = new NativeParser(reader1, signature1.NativeLayoutOffset); + + NativeFormatModuleInfo module2 = ModuleList.GetModuleInfoByHandle(new TypeManagerHandle(signature2.ModuleHandle)); + NativeReader reader2 = GetNativeLayoutInfoReader(signature2); + NativeParser parser2 = new NativeParser(reader2, signature2.NativeLayoutOffset); + + return CompareMethodSigs(parser1, module1, parser2, module2); + } + else if (signature1.IsNativeLayoutSignature) + { + int token = signature2.Token; + MetadataReader metadataReader = ModuleList.Instance.GetMetadataReaderForModule(new TypeManagerHandle(signature2.ModuleHandle)); + + MethodSignatureComparer comparer = new MethodSignatureComparer(metadataReader, token.AsHandle().ToMethodHandle(metadataReader)); + return comparer.IsMatchingNativeLayoutMethodSignature(signature1); + } + else if (signature2.IsNativeLayoutSignature) + { + int token = signature1.Token; + MetadataReader metadataReader = ModuleList.Instance.GetMetadataReaderForModule(new TypeManagerHandle(signature1.ModuleHandle)); + + MethodSignatureComparer comparer = new MethodSignatureComparer(metadataReader, token.AsHandle().ToMethodHandle(metadataReader)); + return comparer.IsMatchingNativeLayoutMethodSignature(signature2); + } + else + { + // For now, RuntimeSignatures are only used to compare for method signature equality (along with their Name) + // So we can implement this with the simple equals check + if (signature1.Token != signature2.Token) + return false; + + if (signature1.ModuleHandle != signature2.ModuleHandle) + return false; + + return true; + } + } + + public uint GetGenericArgumentCountFromMethodNameAndSignature(MethodNameAndSignature signature) + { + if (signature.Signature.IsNativeLayoutSignature) + { + NativeReader reader = GetNativeLayoutInfoReader(signature.Signature); + NativeParser parser = new NativeParser(reader, signature.Signature.NativeLayoutOffset); + + return GetGenericArgCountFromSig(parser); + } + else + { + ModuleInfo module = signature.Signature.GetModuleInfo(); + +#if ECMA_METADATA_SUPPORT + if (module is NativeFormatModuleInfo) +#endif + { + NativeFormatModuleInfo nativeFormatModule = (NativeFormatModuleInfo)module; + var metadataReader = nativeFormatModule.MetadataReader; + var methodHandle = signature.Signature.Token.AsHandle().ToMethodHandle(metadataReader); + + var method = methodHandle.GetMethod(metadataReader); + var methodSignature = method.Signature.GetMethodSignature(metadataReader); + return checked((uint)methodSignature.GenericParameterCount); + } +#if ECMA_METADATA_SUPPORT + else + { + EcmaModuleInfo ecmaModuleInfo = (EcmaModuleInfo)module; + var metadataReader = ecmaModuleInfo.MetadataReader; + var ecmaHandle = (System.Reflection.Metadata.MethodDefinitionHandle)System.Reflection.Metadata.Ecma335.MetadataTokens.Handle(signature.Signature.Token); + var method = metadataReader.GetMethodDefinition(ecmaHandle); + var blobHandle = method.Signature; + var blobReader = metadataReader.GetBlobReader(blobHandle); + byte sigByte = blobReader.ReadByte(); + if ((sigByte & (byte)System.Reflection.Metadata.SignatureAttributes.Generic) == 0) + return 0; + uint genArgCount = checked((uint)blobReader.ReadCompressedInteger()); + return genArgCount; + } +#endif + } + } + + public bool TryGetMethodNameAndSignatureFromNativeLayoutSignature(RuntimeSignature signature, out MethodNameAndSignature nameAndSignature) + { + nameAndSignature = null; + + NativeReader reader = GetNativeLayoutInfoReader(signature); + NativeParser parser = new NativeParser(reader, signature.NativeLayoutOffset); + if (parser.IsNull) + return false; + + RuntimeSignature methodSig; + RuntimeSignature methodNameSig; + nameAndSignature = GetMethodNameAndSignature(ref parser, new TypeManagerHandle(signature.ModuleHandle), out methodNameSig, out methodSig); + + return true; + } + + public bool TryGetMethodNameAndSignaturePointersFromNativeLayoutSignature(TypeManagerHandle module, uint methodNameAndSigToken, out RuntimeSignature methodNameSig, out RuntimeSignature methodSig) + { + methodNameSig = default(RuntimeSignature); + methodSig = default(RuntimeSignature); + + NativeReader reader = GetNativeLayoutInfoReader(module); + NativeParser parser = new NativeParser(reader, methodNameAndSigToken); + if (parser.IsNull) + return false; + + methodNameSig = RuntimeSignature.CreateFromNativeLayoutSignature(module, parser.Offset); + string methodName = parser.GetString(); + + // Signatures are indirected to through a relative offset so that we don't have to parse them + // when not comparing signatures (parsing them requires resolving types and is tremendously + // expensive). + NativeParser sigParser = parser.GetParserFromRelativeOffset(); + methodSig = RuntimeSignature.CreateFromNativeLayoutSignature(module, sigParser.Offset); + + return true; + } + + public bool TryGetMethodNameAndSignatureFromNativeLayoutOffset(TypeManagerHandle moduleHandle, uint nativeLayoutOffset, out MethodNameAndSignature nameAndSignature) + { + nameAndSignature = null; + + NativeReader reader = GetNativeLayoutInfoReader(moduleHandle); + NativeParser parser = new NativeParser(reader, nativeLayoutOffset); + if (parser.IsNull) + return false; + + RuntimeSignature methodSig; + RuntimeSignature methodNameSig; + nameAndSignature = GetMethodNameAndSignature(ref parser, moduleHandle, out methodNameSig, out methodSig); + return true; + } + + internal MethodNameAndSignature GetMethodNameAndSignature(ref NativeParser parser, TypeManagerHandle moduleHandle, out RuntimeSignature methodNameSig, out RuntimeSignature methodSig) + { + methodNameSig = RuntimeSignature.CreateFromNativeLayoutSignature(moduleHandle, parser.Offset); + string methodName = parser.GetString(); + + // Signatures are indirected to through a relative offset so that we don't have to parse them + // when not comparing signatures (parsing them requires resolving types and is tremendously + // expensive). + NativeParser sigParser = parser.GetParserFromRelativeOffset(); + methodSig = RuntimeSignature.CreateFromNativeLayoutSignature(moduleHandle, sigParser.Offset); + + return new MethodNameAndSignature(methodName, methodSig); + } + + internal bool IsStaticMethodSignature(RuntimeSignature methodSig) + { + if (methodSig.IsNativeLayoutSignature) + { + NativeReader reader = GetNativeLayoutInfoReader(methodSig); + NativeParser parser = new NativeParser(reader, methodSig.NativeLayoutOffset); + + MethodCallingConvention callingConvention = (MethodCallingConvention)parser.GetUnsigned(); + return callingConvention.HasFlag(MethodCallingConvention.Static); + } + else + { + ModuleInfo module = methodSig.GetModuleInfo(); + +#if ECMA_METADATA_SUPPORT + if (module is NativeFormatModuleInfo) +#endif + { + NativeFormatModuleInfo nativeFormatModule = (NativeFormatModuleInfo)module; + var metadataReader = nativeFormatModule.MetadataReader; + var methodHandle = methodSig.Token.AsHandle().ToMethodHandle(metadataReader); + + var method = methodHandle.GetMethod(metadataReader); + return (method.Flags & MethodAttributes.Static) != 0; + } +#if ECMA_METADATA_SUPPORT + else + { + EcmaModuleInfo ecmaModuleInfo = (EcmaModuleInfo)module; + var metadataReader = ecmaModuleInfo.MetadataReader; + var ecmaHandle = (System.Reflection.Metadata.MethodDefinitionHandle)System.Reflection.Metadata.Ecma335.MetadataTokens.Handle(methodSig.Token); + var method = metadataReader.GetMethodDefinition(ecmaHandle); + var blobHandle = method.Signature; + var blobReader = metadataReader.GetBlobReader(blobHandle); + byte sigByte = blobReader.ReadByte(); + return ((sigByte & (byte)System.Reflection.Metadata.SignatureAttributes.Instance) == 0); + } +#endif + } + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + // Create a TypeSystem.MethodSignature object from a RuntimeSignature that isn't a NativeLayoutSignature + private TypeSystem.MethodSignature TypeSystemSigFromRuntimeSignature(TypeSystemContext context, RuntimeSignature signature) + { + Debug.Assert(!signature.IsNativeLayoutSignature); + + ModuleInfo module = signature.GetModuleInfo(); + +#if ECMA_METADATA_SUPPORT + if (module is NativeFormatModuleInfo) +#endif + { + NativeFormatModuleInfo nativeFormatModule = (NativeFormatModuleInfo)module; + var metadataReader = nativeFormatModule.MetadataReader; + var methodHandle = signature.Token.AsHandle().ToMethodHandle(metadataReader); + var metadataUnit = ((TypeLoaderTypeSystemContext)context).ResolveMetadataUnit(nativeFormatModule); + var parser = new Internal.TypeSystem.NativeFormat.NativeFormatSignatureParser(metadataUnit, metadataReader.GetMethod(methodHandle).Signature, metadataReader); + return parser.ParseMethodSignature(); + } +#if ECMA_METADATA_SUPPORT + else + { + EcmaModuleInfo ecmaModuleInfo = (EcmaModuleInfo)module; + TypeSystem.Ecma.EcmaModule ecmaModule = context.ResolveEcmaModule(ecmaModuleInfo); + var ecmaHandle = System.Reflection.Metadata.Ecma335.MetadataTokens.EntityHandle(signature.Token); + MethodDesc ecmaMethod = ecmaModule.GetMethod(ecmaHandle); + return ecmaMethod.Signature; + } +#endif + } +#endif + + internal bool GetCallingConverterDataFromMethodSignature(TypeSystemContext context, RuntimeSignature methodSig, Instantiation typeInstantiation, Instantiation methodInstantiation, out bool hasThis, out TypeDesc[] parameters, out bool[] parametersWithGenericDependentLayout) + { + if (methodSig.IsNativeLayoutSignature) + return GetCallingConverterDataFromMethodSignature_NativeLayout(context, methodSig, typeInstantiation, methodInstantiation, out hasThis, out parameters, out parametersWithGenericDependentLayout); + else + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + var sig = TypeSystemSigFromRuntimeSignature(context, methodSig); + return GetCallingConverterDataFromMethodSignature_MethodSignature(sig, typeInstantiation, methodInstantiation, out hasThis, out parameters, out parametersWithGenericDependentLayout); +#else + parametersWithGenericDependentLayout = null; + hasThis = false; + parameters = null; + return false; +#endif + } + } + + internal bool GetCallingConverterDataFromMethodSignature_NativeLayout(TypeSystemContext context, RuntimeSignature methodSig, Instantiation typeInstantiation, Instantiation methodInstantiation, out bool hasThis, out TypeDesc[] parameters, out bool[] parametersWithGenericDependentLayout) + { + return GetCallingConverterDataFromMethodSignature_NativeLayout_Common( + context, + methodSig, + typeInstantiation, + methodInstantiation, + out hasThis, + out parameters, + out parametersWithGenericDependentLayout, + null); + } + + internal bool GetCallingConverterDataFromMethodSignature_NativeLayout_Common( + TypeSystemContext context, + RuntimeSignature methodSig, + Instantiation typeInstantiation, + Instantiation methodInstantiation, + out bool hasThis, + out TypeDesc[] parameters, + out bool[] parametersWithGenericDependentLayout, + NativeReader nativeReader) + { + hasThis = false; + parameters = null; + + NativeLayoutInfoLoadContext nativeLayoutContext = new NativeLayoutInfoLoadContext(); + + nativeLayoutContext._module = (NativeFormatModuleInfo)methodSig.GetModuleInfo(); + nativeLayoutContext._typeSystemContext = context; + nativeLayoutContext._typeArgumentHandles = typeInstantiation; + nativeLayoutContext._methodArgumentHandles = methodInstantiation; + + NativeFormatModuleInfo module = ModuleList.Instance.GetModuleInfoByHandle(new TypeManagerHandle(methodSig.ModuleHandle)); + NativeReader reader = GetNativeLayoutInfoReader(methodSig); + NativeParser parser = new NativeParser(reader, methodSig.NativeLayoutOffset); + + MethodCallingConvention callingConvention = (MethodCallingConvention)parser.GetUnsigned(); + hasThis = !callingConvention.HasFlag(MethodCallingConvention.Static); + + uint numGenArgs = callingConvention.HasFlag(MethodCallingConvention.Generic) ? parser.GetUnsigned() : 0; + + uint parameterCount = parser.GetUnsigned(); + parameters = new TypeDesc[parameterCount + 1]; + parametersWithGenericDependentLayout = new bool[parameterCount + 1]; + + // One extra parameter to account for the return type + for (uint i = 0; i <= parameterCount; i++) + { + // NativeParser is a struct, so it can be copied. + NativeParser parserCopy = parser; + + // Parse the signature twice. The first time to find out the exact type of the signature + // The second time to identify if the parameter loaded via the signature should be forced to be + // passed byref as part of the universal generic calling convention. + parameters[i] = GetConstructedTypeFromParserAndNativeLayoutContext(ref parser, nativeLayoutContext); + parametersWithGenericDependentLayout[i] = TypeSignatureHasVarsNeedingCallingConventionConverter(ref parserCopy, module, context, HasVarsInvestigationLevel.Parameter); + if (parameters[i] == null) + return false; + } + + return true; + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + private static bool GetCallingConverterDataFromMethodSignature_MethodSignature(TypeSystem.MethodSignature methodSignature, Instantiation typeInstantiation, Instantiation methodInstantiation, out bool hasThis, out TypeDesc[] parameters, out bool[] parametersWithGenericDependentLayout) + { + // Compute parameters dependent on generic instantiation for their layout + parametersWithGenericDependentLayout = new bool[methodSignature.Length + 1]; + parametersWithGenericDependentLayout[0] = UniversalGenericParameterLayout.IsLayoutDependentOnGenericInstantiation(methodSignature.ReturnType); + for (int i = 0; i < methodSignature.Length; i++) + { + parametersWithGenericDependentLayout[i + 1] = UniversalGenericParameterLayout.IsLayoutDependentOnGenericInstantiation(methodSignature[i]); + } + + // Compute hasThis-ness + hasThis = !methodSignature.IsStatic; + + // Compute parameter exact types + parameters = new TypeDesc[methodSignature.Length + 1]; + + parameters[0] = methodSignature.ReturnType.InstantiateSignature(typeInstantiation, methodInstantiation); + for (int i = 0; i < methodSignature.Length; i++) + { + parameters[i + 1] = methodSignature[i].InstantiateSignature(typeInstantiation, methodInstantiation); + } + + return true; + } +#endif + + internal bool MethodSignatureHasVarsNeedingCallingConventionConverter(TypeSystemContext context, RuntimeSignature methodSig) + { + if (methodSig.IsNativeLayoutSignature) + return MethodSignatureHasVarsNeedingCallingConventionConverter_NativeLayout(context, methodSig); + else + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + var sig = TypeSystemSigFromRuntimeSignature(context, methodSig); + return UniversalGenericParameterLayout.MethodSignatureHasVarsNeedingCallingConventionConverter(sig); +#else + Environment.FailFast("Cannot parse signature"); + return false; +#endif + } + } + + private bool MethodSignatureHasVarsNeedingCallingConventionConverter_NativeLayout(TypeSystemContext context, RuntimeSignature methodSig) + { + NativeReader reader = GetNativeLayoutInfoReader(methodSig); + NativeParser parser = new NativeParser(reader, methodSig.NativeLayoutOffset); + NativeFormatModuleInfo module = ModuleList.Instance.GetModuleInfoByHandle(new TypeManagerHandle(methodSig.ModuleHandle)); + + MethodCallingConvention callingConvention = (MethodCallingConvention)parser.GetUnsigned(); + uint numGenArgs = callingConvention.HasFlag(MethodCallingConvention.Generic) ? parser.GetUnsigned() : 0; + uint parameterCount = parser.GetUnsigned(); + + // Check the return type of the method + if (TypeSignatureHasVarsNeedingCallingConventionConverter(ref parser, module, context, HasVarsInvestigationLevel.Parameter)) + return true; + + // Check the parameters of the method + for (uint i = 0; i < parameterCount; i++) + { + if (TypeSignatureHasVarsNeedingCallingConventionConverter(ref parser, module, context, HasVarsInvestigationLevel.Parameter)) + return true; + } + + return false; + } + + #region Private Helpers + private enum HasVarsInvestigationLevel + { + Parameter, + NotParameter, + Ignore + } + + /// + /// IF THESE SEMANTICS EVER CHANGE UPDATE THE LOGIC WHICH DEFINES THIS BEHAVIOR IN + /// THE DYNAMIC TYPE LOADER AS WELL AS THE COMPILER. + /// (There is a version of this in UniversalGenericParameterLayout.cs that must be kept in sync with this.) + /// + /// Parameter's are considered to have type layout dependent on their generic instantiation + /// if the type of the parameter in its signature is a type variable, or if the type is a generic + /// structure which meets 2 characteristics: + /// 1. Structure size/layout is affected by the size/layout of one or more of its generic parameters + /// 2. One or more of the generic parameters is a type variable, or a generic structure which also recursively + /// would satisfy constraint 2. (Note, that in the recursion case, whether or not the structure is affected + /// by the size/layout of its generic parameters is not investigated.) + /// + /// Examples parameter types, and behavior. + /// + /// T = true + /// List[T] = false + /// StructNotDependentOnArgsForSize[T] = false + /// GenStructDependencyOnArgsForSize[T] = true + /// StructNotDependentOnArgsForSize[GenStructDependencyOnArgsForSize[T]] = true + /// StructNotDependentOnArgsForSize[GenStructDependencyOnArgsForSize[List[T]]]] = false + /// + /// Example non-parameter type behavior + /// T = true + /// List[T] = false + /// StructNotDependentOnArgsForSize[T] = *true* + /// GenStructDependencyOnArgsForSize[T] = true + /// StructNotDependentOnArgsForSize[GenStructDependencyOnArgsForSize[T]] = true + /// StructNotDependentOnArgsForSize[GenStructDependencyOnArgsForSize[List[T]]]] = false + /// + private bool TypeSignatureHasVarsNeedingCallingConventionConverter(ref NativeParser parser, NativeFormatModuleInfo moduleHandle, TypeSystemContext context, HasVarsInvestigationLevel investigationLevel) + { + uint data; + var kind = parser.GetTypeSignatureKind(out data); + + switch (kind) + { + case TypeSignatureKind.External: return false; + case TypeSignatureKind.Variable: return true; + case TypeSignatureKind.BuiltIn: return false; + + case TypeSignatureKind.Lookback: + { + var lookbackParser = parser.GetLookbackParser(data); + return TypeSignatureHasVarsNeedingCallingConventionConverter(ref lookbackParser, moduleHandle, context, investigationLevel); + } + + case TypeSignatureKind.Instantiation: + { + RuntimeTypeHandle genericTypeDef; + if (!TryGetTypeFromSimpleTypeSignature(ref parser, moduleHandle, out genericTypeDef)) + { + Debug.Assert(false); + return true; // Returning true will prevent further reading from the native parser + } + + if (!RuntimeAugments.IsValueType(genericTypeDef)) + { + // Reference types are treated like pointers. No calling convention conversion needed. Just consume the rest of the signature. + for (uint i = 0; i < data; i++) + TypeSignatureHasVarsNeedingCallingConventionConverter(ref parser, moduleHandle, context, HasVarsInvestigationLevel.Ignore); + return false; + } + else + { + bool result = false; + for (uint i = 0; i < data; i++) + result = TypeSignatureHasVarsNeedingCallingConventionConverter(ref parser, moduleHandle, context, HasVarsInvestigationLevel.NotParameter) || result; + + if ((result == true) && (investigationLevel == HasVarsInvestigationLevel.Parameter)) + { + if (!TryComputeHasInstantiationDeterminedSize(genericTypeDef, context, out result)) + Environment.FailFast("Unable to setup calling convention converter correctly"); + + return result; + } + + return result; + } + } + + case TypeSignatureKind.Modifier: + { + // Arrays, pointers and byref types signatures are treated as pointers, not requiring calling convention conversion. + // Just consume the parameter type from the stream and return false; + TypeSignatureHasVarsNeedingCallingConventionConverter(ref parser, moduleHandle, context, HasVarsInvestigationLevel.Ignore); + return false; + } + + case TypeSignatureKind.MultiDimArray: + { + // No need for a calling convention converter for this case. Just consume the signature from the stream. + + TypeSignatureHasVarsNeedingCallingConventionConverter(ref parser, moduleHandle, context, HasVarsInvestigationLevel.Ignore); + + uint boundCount = parser.GetUnsigned(); + for (uint i = 0; i < boundCount; i++) + parser.GetUnsigned(); + + uint lowerBoundCount = parser.GetUnsigned(); + for (uint i = 0; i < lowerBoundCount; i++) + parser.GetUnsigned(); + } + return false; + + case TypeSignatureKind.FunctionPointer: + { + // No need for a calling convention converter for this case. Just consume the signature from the stream. + + uint argCount = parser.GetUnsigned(); + for (uint i = 0; i < argCount; i++) + TypeSignatureHasVarsNeedingCallingConventionConverter(ref parser, moduleHandle, context, HasVarsInvestigationLevel.Ignore); + } + return false; + + default: + parser.ThrowBadImageFormatException(); + return true; + } + } + + private bool TryGetTypeFromSimpleTypeSignature(ref NativeParser parser, NativeFormatModuleInfo moduleHandle, out RuntimeTypeHandle typeHandle) + { + uint data; + TypeSignatureKind kind = parser.GetTypeSignatureKind(out data); + + if (kind == TypeSignatureKind.Lookback) + { + var lookbackParser = parser.GetLookbackParser(data); + return TryGetTypeFromSimpleTypeSignature(ref lookbackParser, moduleHandle, out typeHandle); + } + else if (kind == TypeSignatureKind.External) + { + typeHandle = GetExternalTypeHandle(moduleHandle, data); + return true; + } + else if (kind == TypeSignatureKind.BuiltIn) + { + typeHandle = ((WellKnownType)data).GetRuntimeTypeHandle(); + return true; + } + + // Not a simple type signature... requires more work to skip + typeHandle = default(RuntimeTypeHandle); + return false; + } + + private RuntimeTypeHandle GetExternalTypeHandle(NativeFormatModuleInfo moduleHandle, uint typeIndex) + { + Debug.Assert(moduleHandle != null); + + RuntimeTypeHandle result; + + TypeSystemContext context = TypeSystemContextFactory.Create(); + { + NativeLayoutInfoLoadContext nativeLayoutContext = new NativeLayoutInfoLoadContext(); + nativeLayoutContext._module = moduleHandle; + nativeLayoutContext._typeSystemContext = context; + + TypeDesc type = nativeLayoutContext.GetExternalType(typeIndex); + result = type.RuntimeTypeHandle; + } + TypeSystemContextFactory.Recycle(context); + + Debug.Assert(!result.IsNull()); + return result; + } + + private uint GetGenericArgCountFromSig(NativeParser parser) + { + MethodCallingConvention callingConvention = (MethodCallingConvention)parser.GetUnsigned(); + + if ((callingConvention & MethodCallingConvention.Generic) == MethodCallingConvention.Generic) + { + return parser.GetUnsigned(); + } + else + { + return 0; + } + } + + private bool CompareMethodSigs(NativeParser parser1, NativeFormatModuleInfo moduleHandle1, NativeParser parser2, NativeFormatModuleInfo moduleHandle2) + { + MethodCallingConvention callingConvention1 = (MethodCallingConvention)parser1.GetUnsigned(); + MethodCallingConvention callingConvention2 = (MethodCallingConvention)parser2.GetUnsigned(); + + if (callingConvention1 != callingConvention2) + return false; + + if ((callingConvention1 & MethodCallingConvention.Generic) == MethodCallingConvention.Generic) + { + if (parser1.GetUnsigned() != parser2.GetUnsigned()) + return false; + } + + uint parameterCount1 = parser1.GetUnsigned(); + uint parameterCount2 = parser2.GetUnsigned(); + if (parameterCount1 != parameterCount2) + return false; + + // Compare one extra parameter to account for the return type + for (uint i = 0; i <= parameterCount1; i++) + { + if (!CompareTypeSigs(ref parser1, moduleHandle1, ref parser2, moduleHandle2)) + return false; + } + + return true; + } + + private bool CompareTypeSigs(ref NativeParser parser1, NativeFormatModuleInfo moduleHandle1, ref NativeParser parser2, NativeFormatModuleInfo moduleHandle2) + { + // startOffset lets us backtrack to the TypeSignatureKind for external types since the TypeLoader + // expects to read it in. + uint data1; + uint startOffset1 = parser1.Offset; + var typeSignatureKind1 = parser1.GetTypeSignatureKind(out data1); + + // If the parser is at a lookback type, get a new parser for it and recurse. + // Since we haven't read the element type of parser2 yet, we just pass it in unchanged + if (typeSignatureKind1 == TypeSignatureKind.Lookback) + { + NativeParser lookbackParser1 = parser1.GetLookbackParser(data1); + return CompareTypeSigs(ref lookbackParser1, moduleHandle1, ref parser2, moduleHandle2); + } + + uint data2; + uint startOffset2 = parser2.Offset; + var typeSignatureKind2 = parser2.GetTypeSignatureKind(out data2); + + // If parser2 is a lookback type, we need to rewind parser1 to its startOffset1 + // before recursing. + if (typeSignatureKind2 == TypeSignatureKind.Lookback) + { + NativeParser lookbackParser2 = parser2.GetLookbackParser(data2); + parser1 = new NativeParser(parser1.Reader, startOffset1); + return CompareTypeSigs(ref parser1, moduleHandle1, ref lookbackParser2, moduleHandle2); + } + + if (typeSignatureKind1 != typeSignatureKind2) + return false; + + switch (typeSignatureKind1) + { + case TypeSignatureKind.Lookback: + { + // Recursion above better have removed all lookbacks + Debug.Fail("Unexpected lookback type"); + return false; + } + + case TypeSignatureKind.Modifier: + { + // Ensure the modifier kind (vector, pointer, byref) is the same + if (data1 != data2) + return false; + return CompareTypeSigs(ref parser1, moduleHandle1, ref parser2, moduleHandle2); + } + + case TypeSignatureKind.Variable: + { + // variable index is in data + if (data1 != data2) + return false; + break; + } + + case TypeSignatureKind.MultiDimArray: + { + // rank is in data + if (data1 != data2) + return false; + + if (!CompareTypeSigs(ref parser1, moduleHandle1, ref parser2, moduleHandle2)) + return false; + + uint boundCount1 = parser1.GetUnsigned(); + uint boundCount2 = parser2.GetUnsigned(); + if (boundCount1 != boundCount2) + return false; + + for (uint i = 0; i < boundCount1; i++) + { + if (parser1.GetUnsigned() != parser2.GetUnsigned()) + return false; + } + + uint lowerBoundCount1 = parser1.GetUnsigned(); + uint lowerBoundCount2 = parser2.GetUnsigned(); + if (lowerBoundCount1 != lowerBoundCount2) + return false; + + for (uint i = 0; i < lowerBoundCount1; i++) + { + if (parser1.GetUnsigned() != parser2.GetUnsigned()) + return false; + } + break; + } + + case TypeSignatureKind.FunctionPointer: + { + // callingConvention is in data + if (data1 != data2) + return false; + uint argCount1 = parser1.GetUnsigned(); + uint argCount2 = parser2.GetUnsigned(); + if (argCount1 != argCount2) + return false; + for (uint i = 0; i < argCount1; i++) + { + if (!CompareTypeSigs(ref parser1, moduleHandle1, ref parser2, moduleHandle2)) + return false; + } + break; + } + + case TypeSignatureKind.Instantiation: + { + // Type parameter count is in data + if (data1 != data2) + return false; + + if (!CompareTypeSigs(ref parser1, moduleHandle1, ref parser2, moduleHandle2)) + return false; + + for (uint i = 0; i < data1; i++) + { + if (!CompareTypeSigs(ref parser1, moduleHandle1, ref parser2, moduleHandle2)) + return false; + } + break; + } + + case TypeSignatureKind.BuiltIn: + RuntimeTypeHandle typeHandle3 = ((WellKnownType)data1).GetRuntimeTypeHandle(); + RuntimeTypeHandle typeHandle4 = ((WellKnownType)data2).GetRuntimeTypeHandle(); + if (!typeHandle3.Equals(typeHandle4)) + return false; + + break; + + case TypeSignatureKind.External: + { + RuntimeTypeHandle typeHandle1 = GetExternalTypeHandle(moduleHandle1, data1); + RuntimeTypeHandle typeHandle2 = GetExternalTypeHandle(moduleHandle2, data2); + if (!typeHandle1.Equals(typeHandle2)) + return false; + + break; + } + + default: + return false; + } + return true; + } + #endregion + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.StaticsLookup.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.StaticsLookup.cs new file mode 100644 index 00000000000000..db22977baba40f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.StaticsLookup.cs @@ -0,0 +1,325 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Runtime; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +using Internal.Runtime; +using Internal.Runtime.Augments; + +using Internal.NativeFormat; + +namespace Internal.Runtime.TypeLoader +{ + public sealed partial class TypeLoaderEnvironment + { + private const int DynamicTypeTlsOffsetFlag = unchecked((int)0x80000000); + + // To keep the synchronization simple, we execute all TLS registration/lookups under a global lock + private Lock _threadStaticsLock = new Lock(); + + // Counter to keep track of generated offsets for TLS cells of dynamic types; + private int _maxTlsCells; + private LowLevelDictionary _dynamicGenericsThreadStatics = new LowLevelDictionary(); + private LowLevelDictionary _dynamicGenericsThreadStaticSizes = new LowLevelDictionary(); + + // Various functions in static access need to create permanent pointers for use by thread static lookup. + #region GC/Non-GC Statics + /// + /// Get a pointer to the nongc static field data of a type. This function works for dynamic + /// types, reflectable types, and for all generic types + /// + public IntPtr TryGetNonGcStaticFieldData(RuntimeTypeHandle runtimeTypeHandle) + { + unsafe + { + MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr(); + // Non-generic, non-dynamic types need special handling. + if (!typeAsEEType->IsDynamicType && !typeAsEEType->IsGeneric) + { + if (typeAsEEType->HasCctor) + { + // The non-gc area for a type is immediately following its cctor context if it has one + IntPtr dataAddress = TryGetStaticClassConstructionContext(runtimeTypeHandle); + if (dataAddress != IntPtr.Zero) + { + return (IntPtr)(((byte*)dataAddress.ToPointer()) + sizeof(System.Runtime.CompilerServices.StaticClassConstructionContext)); + } + } + else + { + // If the type does not have a Cctor context, search for the field on the type in the field map which has the lowest offset, + // yet has the the correct type of storage. + IntPtr staticAddress; + if (TryGetStaticFieldBaseFromFieldAccessMap(runtimeTypeHandle, FieldAccessStaticDataKind.NonGC, out staticAddress)) + { + return staticAddress; + } + } + } + } + + IntPtr nonGcStaticsAddress; + IntPtr gcStaticsAddress; + if (TryGetStaticsInfoForNamedType(runtimeTypeHandle, out nonGcStaticsAddress, out gcStaticsAddress)) + { + return nonGcStaticsAddress; + } + + unsafe + { + // Non-generic, non-dynamic static data is found via the FieldAccessMap + MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr(); + // Non-generic, non-dynamic types need special handling. + Debug.Assert(typeAsEEType->IsDynamicType || typeAsEEType->IsGeneric); + } + + // Search hashtable for static entry + ExternalReferencesTable staticInfoLookup; + var parser = GetStaticInfo(runtimeTypeHandle, out staticInfoLookup); + if (!parser.IsNull) + { + var index = parser.GetUnsignedForBagElementKind(BagElementKind.NonGcStaticData); + + return index.HasValue ? staticInfoLookup.GetIntPtrFromIndex(index.Value) : IntPtr.Zero; + } + + // Not found in hashtable... must be a dynamically created type + Debug.Assert(runtimeTypeHandle.IsDynamicType()); + unsafe + { + MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr(); + if ((typeAsEEType->RareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0) + { + return typeAsEEType->DynamicNonGcStaticsData; + } + } + + // Type has no non-GC statics + return IntPtr.Zero; + } + + /// + /// Get a pointer to the gc static field data of a type. This function works for dynamic + /// types, reflectable types, and for all generic types + /// + public IntPtr TryGetGcStaticFieldData(RuntimeTypeHandle runtimeTypeHandle) + { + unsafe + { + // Non-generic, non-dynamic static data is found via the FieldAccessMap + MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr(); + // Non-generic, non-dynamic types need special handling. + if (!typeAsEEType->IsDynamicType && !typeAsEEType->IsGeneric) + { + //search for the field on the type in the field map which has the lowest offset, + // yet has the the correct type of storage. + IntPtr staticAddress; + if (TryGetStaticFieldBaseFromFieldAccessMap(runtimeTypeHandle, FieldAccessStaticDataKind.GC, out staticAddress)) + { + return staticAddress; + } + else + { + return IntPtr.Zero; + } + } + } + + IntPtr nonGcStaticsAddress; + IntPtr gcStaticsAddress; + if (TryGetStaticsInfoForNamedType(runtimeTypeHandle, out nonGcStaticsAddress, out gcStaticsAddress)) + { + return gcStaticsAddress; + } + + unsafe + { + // Non-generic, non-dynamic static data is found via the FieldAccessMap + MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr(); + // Non-generic, non-dynamic types need special handling. + Debug.Assert(typeAsEEType->IsDynamicType || typeAsEEType->IsGeneric); + } + + // Search hashtable for static entry + ExternalReferencesTable staticInfoLookup; + var parser = GetStaticInfo(runtimeTypeHandle, out staticInfoLookup); + if (!parser.IsNull) + { + var index = parser.GetUnsignedForBagElementKind(BagElementKind.GcStaticData); + + return index.HasValue ? staticInfoLookup.GetIntPtrFromIndex(index.Value) : IntPtr.Zero; + } + + // Not found in hashtable... must be a dynamically created type + Debug.Assert(runtimeTypeHandle.IsDynamicType()); + unsafe + { + MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr(); + if ((typeAsEEType->RareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0) + { + return typeAsEEType->DynamicGcStaticsData; + } + } + + // Type has no GC statics + return IntPtr.Zero; + } + #endregion + + + #region Thread Statics + /// + /// Get a pointer to a pointer to the thread static field data of a type. This function works for all generic types + /// + public IntPtr TryGetThreadStaticFieldData(RuntimeTypeHandle runtimeTypeHandle) + { + unsafe + { + // Non-generic, non-dynamic static data is found via the FieldAccessMap + MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr(); + // Non-generic, non-dynamic types need special handling. + Debug.Assert(typeAsEEType->IsDynamicType || typeAsEEType->IsGeneric); + } + + // Search hashtable for static entry + ExternalReferencesTable staticInfoLookup; + var parser = GetStaticInfo(runtimeTypeHandle, out staticInfoLookup); + if (!parser.IsNull) + { + var index = parser.GetUnsignedForBagElementKind(BagElementKind.ThreadStaticIndex); + + return index.HasValue ? staticInfoLookup.GetIntPtrFromIndex(index.Value) : IntPtr.Zero; + } + + // Not found in hashtable... must be a dynamically created type + Debug.Assert(!runtimeTypeHandle.IsDynamicType()); + // Not yet implemented... + + // Type has no GC statics + return IntPtr.Zero; + } + + public int TryGetThreadStaticsSizeForDynamicType(int index, out int numTlsCells) + { + Debug.Assert((index & DynamicTypeTlsOffsetFlag) == DynamicTypeTlsOffsetFlag); + + numTlsCells = _maxTlsCells; + + using (LockHolder.Hold(_threadStaticsLock)) + { + int storageSize; + if (_dynamicGenericsThreadStaticSizes.TryGetValue((uint)index, out storageSize)) + return storageSize; + } + + Debug.Assert(false); + return 0; + } + + public uint GetNextThreadStaticsOffsetValue() + { + // Highest bit of the TLS offset used as a flag to indicate that it's a special TLS offset of a dynamic type + var result = 0x80000000 | (uint)_maxTlsCells; + // Use checked arithmetics to ensure there aren't any overflows/truncations + _maxTlsCells = checked(_maxTlsCells + 1); + return result; + } + + public void RegisterDynamicThreadStaticsInfo(RuntimeTypeHandle runtimeTypeHandle, uint offsetValue, int storageSize) + { + bool registered = false; + Debug.Assert(offsetValue != 0 && storageSize > 0 && runtimeTypeHandle.IsDynamicType()); + + _threadStaticsLock.Acquire(); + try + { + // Sanity check to make sure we do not register thread statics for the same type more than once + uint temp; + Debug.Assert(!_dynamicGenericsThreadStatics.TryGetValue(runtimeTypeHandle, out temp) && storageSize > 0); + + _dynamicGenericsThreadStatics.Add(runtimeTypeHandle, offsetValue); + _dynamicGenericsThreadStaticSizes.Add(offsetValue, storageSize); + registered = true; + } + finally + { + if (!registered) + { + _dynamicGenericsThreadStatics.Remove(runtimeTypeHandle); + _dynamicGenericsThreadStaticSizes.Remove(offsetValue); + } + + _threadStaticsLock.Release(); + } + } + #endregion + + + #region Privates + // get the statics hash table, external references, and static info table for a module + // TODO multi-file: consider whether we want to cache this info + private unsafe bool GetStaticsInfoHashtable(NativeFormatModuleInfo module, out NativeHashtable staticsInfoHashtable, out ExternalReferencesTable externalReferencesLookup, out ExternalReferencesTable staticInfoLookup) + { + byte* pBlob; + uint cbBlob; + + staticsInfoHashtable = default(NativeHashtable); + externalReferencesLookup = default(ExternalReferencesTable); + staticInfoLookup = default(ExternalReferencesTable); + + // Load statics info hashtable + if (!module.TryFindBlob(ReflectionMapBlob.StaticsInfoHashtable, out pBlob, out cbBlob)) + return false; + NativeReader reader = new NativeReader(pBlob, cbBlob); + NativeParser parser = new NativeParser(reader, 0); + + if (!externalReferencesLookup.InitializeNativeReferences(module)) + return false; + + if (!staticInfoLookup.InitializeNativeStatics(module)) + return false; + + staticsInfoHashtable = new NativeHashtable(parser); + + return true; + } + + private NativeParser GetStaticInfo(RuntimeTypeHandle instantiatedType, out ExternalReferencesTable staticsInfoLookup) + { + TypeManagerHandle moduleHandle = RuntimeAugments.GetModuleFromTypeHandle(instantiatedType); + NativeFormatModuleInfo module = ModuleList.Instance.GetModuleInfoByHandle(moduleHandle); + NativeHashtable staticsInfoHashtable; + ExternalReferencesTable externalReferencesLookup; + if (!GetStaticsInfoHashtable(module, out staticsInfoHashtable, out externalReferencesLookup, out staticsInfoLookup)) + return new NativeParser(); + + int lookupHashcode = instantiatedType.GetHashCode(); + var enumerator = staticsInfoHashtable.Lookup(lookupHashcode); + + NativeParser entryParser; + while (!(entryParser = enumerator.GetNext()).IsNull) + { + RuntimeTypeHandle parsedInstantiatedType = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + + if (!parsedInstantiatedType.Equals(instantiatedType)) + continue; + + return entryParser; + } + + return new NativeParser(); + } + + private unsafe IntPtr TryCreateDictionaryCellWithValue(uint value) + { + return PermanentAllocatedMemoryBlobs.GetPointerToUInt(value); + } + #endregion + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs new file mode 100644 index 00000000000000..9004db49a1397c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs @@ -0,0 +1,853 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Threading; +using System.Collections.Generic; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Reflection.Runtime.General; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + +using Internal.Metadata.NativeFormat; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.TypeSystem.NativeFormat; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime.TypeLoader +{ + internal class Callbacks : TypeLoaderCallbacks + { + public override bool TryGetConstructedGenericTypeForComponents(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle) + { + return TypeLoaderEnvironment.Instance.TryGetConstructedGenericTypeForComponents(genericTypeDefinitionHandle, genericTypeArgumentHandles, out runtimeTypeHandle); + } + + public override int GetThreadStaticsSizeForDynamicType(int index, out int numTlsCells) + { + return TypeLoaderEnvironment.Instance.TryGetThreadStaticsSizeForDynamicType(index, out numTlsCells); + } + + public override IntPtr GenericLookupFromContextAndSignature(IntPtr context, IntPtr signature, out IntPtr auxResult) + { + return TypeLoaderEnvironment.Instance.GenericLookupFromContextAndSignature(context, signature, out auxResult); + } + + public override bool GetRuntimeMethodHandleComponents(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodArgs) + { + return TypeLoaderEnvironment.Instance.TryGetRuntimeMethodHandleComponents(runtimeMethodHandle, out declaringTypeHandle, out nameAndSignature, out genericMethodArgs); + } + + public override bool CompareMethodSignatures(RuntimeSignature signature1, RuntimeSignature signature2) + { + return TypeLoaderEnvironment.Instance.CompareMethodSignatures(signature1, signature2); + } + + public override IntPtr TryGetDefaultConstructorForType(RuntimeTypeHandle runtimeTypeHandle) + { + return TypeLoaderEnvironment.Instance.TryGetDefaultConstructorForType(runtimeTypeHandle); + } + + public override IntPtr GetDelegateThunk(Delegate delegateObject, int thunkKind) + { + return CallConverterThunk.GetDelegateThunk(delegateObject, thunkKind); + } + + public override bool TryGetGenericVirtualTargetForTypeAndSlot(RuntimeTypeHandle targetHandle, ref RuntimeTypeHandle declaringType, RuntimeTypeHandle[] genericArguments, ref string methodName, ref RuntimeSignature methodSignature, bool lookForDefaultImplementation, out IntPtr methodPointer, out IntPtr dictionaryPointer, out bool slotUpdated) + { + return TypeLoaderEnvironment.Instance.TryGetGenericVirtualTargetForTypeAndSlot(targetHandle, ref declaringType, genericArguments, ref methodName, ref methodSignature, lookForDefaultImplementation, out methodPointer, out dictionaryPointer, out slotUpdated); + } + + public override bool GetRuntimeFieldHandleComponents(RuntimeFieldHandle runtimeFieldHandle, out RuntimeTypeHandle declaringTypeHandle, out string fieldName) + { + return TypeLoaderEnvironment.Instance.TryGetRuntimeFieldHandleComponents(runtimeFieldHandle, out declaringTypeHandle, out fieldName); + } + + public override IntPtr ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(IntPtr unboxingFunctionPointer, RuntimeTypeHandle declaringType) + { + return TypeLoaderEnvironment.ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(unboxingFunctionPointer, declaringType); + } + + public override bool TryGetPointerTypeForTargetType(RuntimeTypeHandle pointeeTypeHandle, out RuntimeTypeHandle pointerTypeHandle) + { + return TypeLoaderEnvironment.Instance.TryGetPointerTypeForTargetType(pointeeTypeHandle, out pointerTypeHandle); + } + + public override bool TryGetArrayTypeForElementType(RuntimeTypeHandle elementTypeHandle, bool isMdArray, int rank, out RuntimeTypeHandle arrayTypeHandle) + { + return TypeLoaderEnvironment.Instance.TryGetArrayTypeForElementType(elementTypeHandle, isMdArray, rank, out arrayTypeHandle); + } + + public override IntPtr UpdateFloatingDictionary(IntPtr context, IntPtr dictionaryPtr) + { + return TypeLoaderEnvironment.Instance.UpdateFloatingDictionary(context, dictionaryPtr); + } + + /// + /// Register a new runtime-allocated code thunk in the diagnostic stream. + /// + /// Address of thunk to register + public override void RegisterThunk(IntPtr thunkAddress) + { + SerializedDebugData.RegisterTailCallThunk(thunkAddress); + } + } + + public static class RuntimeSignatureExtensions + { + public static IntPtr NativeLayoutSignature(this RuntimeSignature signature) + { + if (!signature.IsNativeLayoutSignature) + Environment.FailFast("Not a valid native layout signature"); + + NativeReader reader = TypeLoaderEnvironment.Instance.GetNativeLayoutInfoReader(signature); + return reader.OffsetToAddress(signature.NativeLayoutOffset); + } + } + + public sealed partial class TypeLoaderEnvironment + { + [ThreadStatic] + private static bool t_isReentrant; + + public static TypeLoaderEnvironment Instance { get; } = new TypeLoaderEnvironment(); + + /// + /// List of loaded binary modules is typically used to locate / process various metadata blobs + /// and other per-module information. + /// + public readonly ModuleList ModuleList; + + // Cache the NativeReader in each module to avoid looking up the NativeLayoutInfo blob each + // time we call GetNativeLayoutInfoReader(). The dictionary is a thread static variable to ensure + // thread safety. Using ThreadStatic instead of a lock is ok as long as the NativeReader class is + // small enough in size (which is the case today). + [ThreadStatic] + private static LowLevelDictionary t_moduleNativeReaders; + + // Eager initialization called from LibraryInitializer for the assembly. + internal static void Initialize() + { + RuntimeAugments.InitializeLookups(new Callbacks()); + } + + public TypeLoaderEnvironment() + { + ModuleList = new ModuleList(); + } + + // To keep the synchronization simple, we execute all type loading under a global lock + private Lock _typeLoaderLock = new Lock(); + + public void VerifyTypeLoaderLockHeld() + { + if (!_typeLoaderLock.IsAcquired) + Environment.FailFast("TypeLoaderLock not held"); + } + + public void RunUnderTypeLoaderLock(Action action) + { + using (LockHolder.Hold(_typeLoaderLock)) + { + action(); + } + } + + public IntPtr GenericLookupFromContextAndSignature(IntPtr context, IntPtr signature, out IntPtr auxResult) + { + IntPtr result; + + using (LockHolder.Hold(_typeLoaderLock)) + { + try + { + if (t_isReentrant) + Environment.FailFast("Reentrant lazy generic lookup"); + t_isReentrant = true; + + result = TypeBuilder.BuildGenericLookupTarget(context, signature, out auxResult); + + t_isReentrant = false; + } + catch + { + // Catch and rethrow any exceptions instead of using finally block. Otherwise, filters that are run during + // the first pass of exception unwind may hit the re-entrancy fail fast above. + + // TODO: Convert this to filter for better diagnostics once we switch to Roslyn + + t_isReentrant = false; + throw; + } + } + + return result; + } + + private bool EnsureTypeHandleForType(TypeDesc type) + { + if (type.RuntimeTypeHandle.IsNull()) + { + using (LockHolder.Hold(_typeLoaderLock)) + { + // Now that we hold the lock, we may find that existing types can now find + // their associated RuntimeTypeHandle. Flush the type builder states as a way + // to force the reresolution of RuntimeTypeHandles which couldn't be found before. + type.Context.FlushTypeBuilderStates(); + try + { + new TypeBuilder().BuildType(type); + } + catch (TypeBuilder.MissingTemplateException) + { + return false; + } + } + } + + // Returned type has to have a valid type handle value + Debug.Assert(!type.RuntimeTypeHandle.IsNull()); + return !type.RuntimeTypeHandle.IsNull(); + } + + internal TypeDesc GetConstructedTypeFromParserAndNativeLayoutContext(ref NativeParser parser, NativeLayoutInfoLoadContext nativeLayoutContext) + { + TypeDesc parsedType = nativeLayoutContext.GetType(ref parser); + if (parsedType == null) + return null; + + if (!EnsureTypeHandleForType(parsedType)) + return null; + + return parsedType; + } + + // + // Parse a native layout signature pointed to by "signature" in the executable image, optionally using + // "typeArgs" and "methodArgs" for generic type parameter substitution. The first field in "signature" + // must be an encoded type but any data beyond that is user-defined and returned in "remainingSignature" + // + internal bool GetTypeFromSignatureAndContext(RuntimeSignature signature, RuntimeTypeHandle[] typeArgs, RuntimeTypeHandle[] methodArgs, out RuntimeTypeHandle createdType, out RuntimeSignature remainingSignature) + { + NativeReader reader = GetNativeLayoutInfoReader(signature); + NativeParser parser = new NativeParser(reader, signature.NativeLayoutOffset); + + bool result = GetTypeFromSignatureAndContext(ref parser, new TypeManagerHandle(signature.ModuleHandle), typeArgs, methodArgs, out createdType); + + remainingSignature = RuntimeSignature.CreateFromNativeLayoutSignature(signature, parser.Offset); + + return result; + } + + internal bool GetTypeFromSignatureAndContext(ref NativeParser parser, TypeManagerHandle moduleHandle, RuntimeTypeHandle[] typeArgs, RuntimeTypeHandle[] methodArgs, out RuntimeTypeHandle createdType) + { + createdType = default(RuntimeTypeHandle); + TypeSystemContext context = TypeSystemContextFactory.Create(); + + TypeDesc parsedType = TryParseNativeSignatureWorker(context, moduleHandle, ref parser, typeArgs, methodArgs, false) as TypeDesc; + if (parsedType == null) + return false; + + if (!EnsureTypeHandleForType(parsedType)) + return false; + + createdType = parsedType.RuntimeTypeHandle; + + TypeSystemContextFactory.Recycle(context); + return true; + } + + // + // Parse a native layout signature pointed to by "signature" in the executable image, optionally using + // "typeArgs" and "methodArgs" for generic type parameter substitution. The first field in "signature" + // must be an encoded method but any data beyond that is user-defined and returned in "remainingSignature" + // + public bool GetMethodFromSignatureAndContext(RuntimeSignature signature, RuntimeTypeHandle[] typeArgs, RuntimeTypeHandle[] methodArgs, out RuntimeTypeHandle createdType, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles, out RuntimeSignature remainingSignature) + { + NativeReader reader = GetNativeLayoutInfoReader(signature); + NativeParser parser = new NativeParser(reader, signature.NativeLayoutOffset); + + bool result = GetMethodFromSignatureAndContext(ref parser, new TypeManagerHandle(signature.ModuleHandle), typeArgs, methodArgs, out createdType, out nameAndSignature, out genericMethodTypeArgumentHandles); + + remainingSignature = RuntimeSignature.CreateFromNativeLayoutSignature(signature, parser.Offset); + + return result; + } + + internal bool GetMethodFromSignatureAndContext(ref NativeParser parser, TypeManagerHandle moduleHandle, RuntimeTypeHandle[] typeArgs, RuntimeTypeHandle[] methodArgs, out RuntimeTypeHandle createdType, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + createdType = default(RuntimeTypeHandle); + nameAndSignature = null; + genericMethodTypeArgumentHandles = null; + + TypeSystemContext context = TypeSystemContextFactory.Create(); + + MethodDesc parsedMethod = TryParseNativeSignatureWorker(context, moduleHandle, ref parser, typeArgs, methodArgs, true) as MethodDesc; + if (parsedMethod == null) + return false; + + if (!EnsureTypeHandleForType(parsedMethod.OwningType)) + return false; + + createdType = parsedMethod.OwningType.RuntimeTypeHandle; + nameAndSignature = parsedMethod.NameAndSignature; + if (!parsedMethod.IsMethodDefinition && parsedMethod.Instantiation.Length > 0) + { + genericMethodTypeArgumentHandles = new RuntimeTypeHandle[parsedMethod.Instantiation.Length]; + for (int i = 0; i < parsedMethod.Instantiation.Length; ++i) + { + if (!EnsureTypeHandleForType(parsedMethod.Instantiation[i])) + return false; + + genericMethodTypeArgumentHandles[i] = parsedMethod.Instantiation[i].RuntimeTypeHandle; + } + } + + TypeSystemContextFactory.Recycle(context); + + return true; + } + + // + // Returns the native layout info reader + // + internal unsafe NativeReader GetNativeLayoutInfoReader(NativeFormatModuleInfo module) + { + return GetNativeLayoutInfoReader(module.Handle); + } + + // + // Returns the native layout info reader + // + internal unsafe NativeReader GetNativeLayoutInfoReader(RuntimeSignature signature) + { + Debug.Assert(signature.IsNativeLayoutSignature); + return GetNativeLayoutInfoReader(new TypeManagerHandle(signature.ModuleHandle)); + } + + // + // Returns the native layout info reader + // + internal unsafe NativeReader GetNativeLayoutInfoReader(TypeManagerHandle moduleHandle) + { + Debug.Assert(!moduleHandle.IsNull); + + if (t_moduleNativeReaders == null) + t_moduleNativeReaders = new LowLevelDictionary(); + + NativeReader result = null; + if (t_moduleNativeReaders.TryGetValue(moduleHandle, out result)) + return result; + + byte* pBlob; + uint cbBlob; + if (RuntimeAugments.FindBlob(moduleHandle, (int)ReflectionMapBlob.NativeLayoutInfo, new IntPtr(&pBlob), new IntPtr(&cbBlob))) + result = new NativeReader(pBlob, cbBlob); + + t_moduleNativeReaders.Add(moduleHandle, result); + return result; + } + + private static RuntimeTypeHandle[] GetTypeSequence(ref ExternalReferencesTable extRefs, ref NativeParser parser) + { + uint count = parser.GetUnsigned(); + RuntimeTypeHandle[] result = new RuntimeTypeHandle[count]; + for (uint i = 0; i < count; i++) + result[i] = extRefs.GetRuntimeTypeHandleFromIndex(parser.GetUnsigned()); + + return result; + } + + private static RuntimeTypeHandle[] TypeDescsToRuntimeHandles(Instantiation types) + { + var result = new RuntimeTypeHandle[types.Length]; + for (int i = 0; i < types.Length; i++) + result[i] = types[i].RuntimeTypeHandle; + + return result; + } + + public bool TryGetConstructedGenericTypeForComponents(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle) + { + if (TryLookupConstructedGenericTypeForComponents(genericTypeDefinitionHandle, genericTypeArgumentHandles, out runtimeTypeHandle)) + return true; + + using (LockHolder.Hold(_typeLoaderLock)) + { + return TypeBuilder.TryBuildGenericType(genericTypeDefinitionHandle, genericTypeArgumentHandles, out runtimeTypeHandle); + } + } + + // Get an array RuntimeTypeHandle given an element's RuntimeTypeHandle and rank. Pass false for isMdArray, and rank == -1 for SzArrays + public bool TryGetArrayTypeForElementType(RuntimeTypeHandle elementTypeHandle, bool isMdArray, int rank, out RuntimeTypeHandle arrayTypeHandle) + { + if (TryGetArrayTypeForElementType_LookupOnly(elementTypeHandle, isMdArray, rank, out arrayTypeHandle)) + { + return true; + } + + using (LockHolder.Hold(_typeLoaderLock)) + { + if (isMdArray && (rank < MDArray.MinRank) && (rank > MDArray.MaxRank)) + { + arrayTypeHandle = default(RuntimeTypeHandle); + return false; + } + + if (TypeSystemContext.GetArrayTypesCache(isMdArray, rank).TryGetValue(elementTypeHandle, out arrayTypeHandle)) + return true; + + return TypeBuilder.TryBuildArrayType(elementTypeHandle, isMdArray, rank, out arrayTypeHandle); + } + } + + // Looks up an array RuntimeTypeHandle given an element's RuntimeTypeHandle and rank. A rank of -1 indicates SzArray + internal bool TryGetArrayTypeForElementType_LookupOnly(RuntimeTypeHandle elementTypeHandle, bool isMdArray, int rank, out RuntimeTypeHandle arrayTypeHandle) + { + if (isMdArray && (rank < MDArray.MinRank) && (rank > MDArray.MaxRank)) + { + arrayTypeHandle = default(RuntimeTypeHandle); + return false; + } + + if (TypeSystemContext.GetArrayTypesCache(isMdArray, rank).TryGetValue(elementTypeHandle, out arrayTypeHandle)) + return true; + + if (!isMdArray && + !RuntimeAugments.IsDynamicType(elementTypeHandle) && + TryGetArrayTypeForNonDynamicElementType(elementTypeHandle, out arrayTypeHandle)) + { + TypeSystemContext.GetArrayTypesCache(isMdArray, rank).AddOrGetExisting(arrayTypeHandle); + return true; + } + + return false; + } + + public bool TryGetPointerTypeForTargetType(RuntimeTypeHandle pointeeTypeHandle, out RuntimeTypeHandle pointerTypeHandle) + { + // There are no lookups for pointers in static modules. All pointer EETypes will be created at this level. + // It's possible to have multiple pointer EETypes representing the same pointer type with the same element type + // The caching of pointer types is done at the reflection layer (in the RuntimeTypeUnifier) and + // here in the TypeSystemContext layer + + if (TypeSystemContext.PointerTypesCache.TryGetValue(pointeeTypeHandle, out pointerTypeHandle)) + return true; + + using (LockHolder.Hold(_typeLoaderLock)) + { + return TypeBuilder.TryBuildPointerType(pointeeTypeHandle, out pointerTypeHandle); + } + } + + public bool TryGetByRefTypeForTargetType(RuntimeTypeHandle pointeeTypeHandle, out RuntimeTypeHandle byRefTypeHandle) + { + // There are no lookups for ByRefs in static modules. All ByRef EETypes will be created at this level. + // It's possible to have multiple ByRef EETypes representing the same ByRef type with the same element type + // The caching of ByRef types is done at the reflection layer (in the RuntimeTypeUnifier) and + // here in the TypeSystemContext layer + + if (TypeSystemContext.ByRefTypesCache.TryGetValue(pointeeTypeHandle, out byRefTypeHandle)) + return true; + + using (LockHolder.Hold(_typeLoaderLock)) + { + return TypeBuilder.TryBuildByRefType(pointeeTypeHandle, out byRefTypeHandle); + } + } + + public int GetCanonicalHashCode(RuntimeTypeHandle typeHandle, CanonicalFormKind kind) + { + TypeSystemContext context = TypeSystemContextFactory.Create(); + TypeDesc type = context.ResolveRuntimeTypeHandle(typeHandle); + int hashCode = type.ConvertToCanonForm(kind).GetHashCode(); + TypeSystemContextFactory.Recycle(context); + + return hashCode; + } + + private object TryParseNativeSignatureWorker(TypeSystemContext typeSystemContext, TypeManagerHandle moduleHandle, ref NativeParser parser, RuntimeTypeHandle[] typeGenericArgumentHandles, RuntimeTypeHandle[] methodGenericArgumentHandles, bool isMethodSignature) + { + Instantiation typeGenericArguments = typeSystemContext.ResolveRuntimeTypeHandles(typeGenericArgumentHandles ?? Array.Empty()); + Instantiation methodGenericArguments = typeSystemContext.ResolveRuntimeTypeHandles(methodGenericArgumentHandles ?? Array.Empty()); + + NativeLayoutInfoLoadContext nativeLayoutContext = new NativeLayoutInfoLoadContext(); + nativeLayoutContext._module = ModuleList.GetModuleInfoByHandle(moduleHandle); + nativeLayoutContext._typeSystemContext = typeSystemContext; + nativeLayoutContext._typeArgumentHandles = typeGenericArguments; + nativeLayoutContext._methodArgumentHandles = methodGenericArguments; + + if (isMethodSignature) + return nativeLayoutContext.GetMethod(ref parser); + else + return nativeLayoutContext.GetType(ref parser); + } + + public bool TryGetGenericMethodDictionaryForComponents(RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle[] genericMethodArgHandles, MethodNameAndSignature nameAndSignature, out IntPtr methodDictionary) + { + if (TryLookupGenericMethodDictionaryForComponents(declaringTypeHandle, nameAndSignature, genericMethodArgHandles, out methodDictionary)) + return true; + + using (LockHolder.Hold(_typeLoaderLock)) + { + return TypeBuilder.TryBuildGenericMethod(declaringTypeHandle, genericMethodArgHandles, nameAndSignature, out methodDictionary); + } + } + + public bool TryGetFieldOffset(RuntimeTypeHandle declaringTypeHandle, uint fieldOrdinal, out int fieldOffset) + { + fieldOffset = int.MinValue; + + // No use going further for non-generic types... TypeLoader doesn't have offset answers for non-generic types! + if (!declaringTypeHandle.IsGenericType()) + return false; + + using (LockHolder.Hold(_typeLoaderLock)) + { + return TypeBuilder.TryGetFieldOffset(declaringTypeHandle, fieldOrdinal, out fieldOffset); + } + } + + public unsafe IntPtr UpdateFloatingDictionary(IntPtr context, IntPtr dictionaryPtr) + { + IntPtr newFloatingDictionary; + bool isNewlyAllocatedDictionary; + bool isTypeContext = context != dictionaryPtr; + + if (isTypeContext) + { + // Look for the exact base type that owns the dictionary. We may be having + // a virtual method run on a derived type and the generic lookup are performed + // on the base type's dictionary. + MethodTable* pEEType = (MethodTable*)context.ToPointer(); + context = (IntPtr)EETypeCreator.GetBaseEETypeForDictionaryPtr(pEEType, dictionaryPtr); + } + + using (LockHolder.Hold(_typeLoaderLock)) + { + // Check if some other thread already allocated a floating dictionary and updated the fixed portion + if (*(IntPtr*)dictionaryPtr != IntPtr.Zero) + return *(IntPtr*)dictionaryPtr; + + try + { + if (t_isReentrant) + Environment.FailFast("Reentrant update to floating dictionary"); + t_isReentrant = true; + + newFloatingDictionary = TypeBuilder.TryBuildFloatingDictionary(context, isTypeContext, dictionaryPtr, out isNewlyAllocatedDictionary); + + t_isReentrant = false; + } + catch + { + // Catch and rethrow any exceptions instead of using finally block. Otherwise, filters that are run during + // the first pass of exception unwind may hit the re-entrancy fail fast above. + + // TODO: Convert this to filter for better diagnostics once we switch to Roslyn + + t_isReentrant = false; + throw; + } + } + + if (newFloatingDictionary == IntPtr.Zero) + { + Environment.FailFast("Unable to update floating dictionary"); + return IntPtr.Zero; + } + + // The pointer to the floating dictionary is the first slot of the fixed dictionary. + if (Interlocked.CompareExchange(ref *(IntPtr*)dictionaryPtr, newFloatingDictionary, IntPtr.Zero) != IntPtr.Zero) + { + // Some other thread beat us and updated the pointer to the floating dictionary. + // Free the one allocated by the current thread + if (isNewlyAllocatedDictionary) + MemoryHelpers.FreeMemory(newFloatingDictionary); + } + + return *(IntPtr*)dictionaryPtr; + } + + public bool CanInstantiationsShareCode(RuntimeTypeHandle[] genericArgHandles1, RuntimeTypeHandle[] genericArgHandles2, CanonicalFormKind kind) + { + if (genericArgHandles1.Length != genericArgHandles2.Length) + return false; + + bool match = true; + + TypeSystemContext context = TypeSystemContextFactory.Create(); + + for (int i = 0; i < genericArgHandles1.Length; i++) + { + TypeDesc genericArg1 = context.ResolveRuntimeTypeHandle(genericArgHandles1[i]); + TypeDesc genericArg2 = context.ResolveRuntimeTypeHandle(genericArgHandles2[i]); + + if (context.ConvertToCanon(genericArg1, kind) != context.ConvertToCanon(genericArg2, kind)) + { + match = false; + break; + } + } + + TypeSystemContextFactory.Recycle(context); + + return match; + } + + public bool ConversionToCanonFormIsAChange(RuntimeTypeHandle[] genericArgHandles, CanonicalFormKind kind) + { + // Todo: support for universal canon type? + + TypeSystemContext context = TypeSystemContextFactory.Create(); + + Instantiation genericArgs = context.ResolveRuntimeTypeHandles(genericArgHandles); + bool result; + context.ConvertInstantiationToCanonForm(genericArgs, kind, out result); + + TypeSystemContextFactory.Recycle(context); + + return result; + } + + // get the generics hash table and external references table for a module + // TODO multi-file: consider whether we want to cache this info + private unsafe bool GetHashtableFromBlob(NativeFormatModuleInfo module, ReflectionMapBlob blobId, out NativeHashtable hashtable, out ExternalReferencesTable externalReferencesLookup) + { + byte* pBlob; + uint cbBlob; + + hashtable = default(NativeHashtable); + externalReferencesLookup = default(ExternalReferencesTable); + + if (!module.TryFindBlob(blobId, out pBlob, out cbBlob)) + return false; + + NativeReader reader = new NativeReader(pBlob, cbBlob); + NativeParser parser = new NativeParser(reader, 0); + + hashtable = new NativeHashtable(parser); + + return externalReferencesLookup.InitializeNativeReferences(module); + } + + public static unsafe void GetFieldAlignmentAndSize(RuntimeTypeHandle fieldType, out int alignment, out int size) + { + MethodTable* typePtr = fieldType.ToEETypePtr(); + if (typePtr->IsValueType) + { + size = (int)typePtr->ValueTypeSize; + } + else + { + size = IntPtr.Size; + } + + alignment = (int)typePtr->FieldAlignmentRequirement; + } + + [StructLayout(LayoutKind.Sequential)] + private struct UnboxingAndInstantiatingStubMapEntry + { + public uint StubMethodRva; + public uint MethodRva; + } + + public static unsafe bool TryGetTargetOfUnboxingAndInstantiatingStub(IntPtr maybeInstantiatingAndUnboxingStub, out IntPtr targetMethod) + { + targetMethod = RuntimeAugments.GetTargetOfUnboxingAndInstantiatingStub(maybeInstantiatingAndUnboxingStub); + return (targetMethod != IntPtr.Zero); + } + + public bool TryComputeHasInstantiationDeterminedSize(RuntimeTypeHandle typeHandle, out bool hasInstantiationDeterminedSize) + { + TypeSystemContext context = TypeSystemContextFactory.Create(); + bool success = TryComputeHasInstantiationDeterminedSize(typeHandle, context, out hasInstantiationDeterminedSize); + TypeSystemContextFactory.Recycle(context); + + return success; + } + + public bool TryComputeHasInstantiationDeterminedSize(RuntimeTypeHandle typeHandle, TypeSystemContext context, out bool hasInstantiationDeterminedSize) + { + Debug.Assert(RuntimeAugments.IsGenericType(typeHandle) || RuntimeAugments.IsGenericTypeDefinition(typeHandle)); + DefType type = (DefType)context.ResolveRuntimeTypeHandle(typeHandle); + + return TryComputeHasInstantiationDeterminedSize(type, out hasInstantiationDeterminedSize); + } + + internal bool TryComputeHasInstantiationDeterminedSize(DefType type, out bool hasInstantiationDeterminedSize) + { + Debug.Assert(type.HasInstantiation); + + NativeLayoutInfoLoadContext loadContextUniversal; + NativeLayoutInfo universalLayoutInfo; + NativeParser parser = type.GetOrCreateTypeBuilderState().GetParserForUniversalNativeLayoutInfo(out loadContextUniversal, out universalLayoutInfo); + if (parser.IsNull) + { + hasInstantiationDeterminedSize = false; +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + MetadataType typeDefinition = type.GetTypeDefinition() as MetadataType; + if (typeDefinition != null) + { + TypeDesc [] universalCanonInstantiation = new TypeDesc[type.Instantiation.Length]; + TypeSystemContext context = type.Context; + TypeDesc universalCanonType = context.UniversalCanonType; + for (int i = 0 ; i < universalCanonInstantiation.Length; i++) + universalCanonInstantiation[i] = universalCanonType; + + DefType universalCanonForm = typeDefinition.MakeInstantiatedType(universalCanonInstantiation); + hasInstantiationDeterminedSize = universalCanonForm.InstanceFieldSize.IsIndeterminate; + return true; + } +#endif + return false; + } + + int? flags = (int?)parser.GetUnsignedForBagElementKind(BagElementKind.TypeFlags); + + hasInstantiationDeterminedSize = flags.HasValue ? + (((NativeFormat.TypeFlags)flags) & NativeFormat.TypeFlags.HasInstantiationDeterminedSize) != 0 : + false; + + return true; + } + + public bool TryResolveSingleMetadataFixup(ModuleInfo module, int metadataToken, MetadataFixupKind fixupKind, out IntPtr fixupResolution) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + using (LockHolder.Hold(_typeLoaderLock)) + { + try + { + return TypeBuilder.TryResolveSingleMetadataFixup((NativeFormatModuleInfo)module, metadataToken, fixupKind, out fixupResolution); + } + catch (Exception ex) + { + Environment.FailFast("Failed to resolve metadata token " + + ((uint)metadataToken).LowLevelToString() + ": " + ex.Message); +#else + Environment.FailFast("Failed to resolve metadata token " + + ((uint)metadataToken).LowLevelToString()); +#endif + fixupResolution = IntPtr.Zero; + return false; +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + } + } +#endif + } + + public bool TryDispatchMethodOnTarget(NativeFormatModuleInfo module, int metadataToken, RuntimeTypeHandle targetInstanceType, out IntPtr methodAddress) + { + using (LockHolder.Hold(_typeLoaderLock)) + { + return TryDispatchMethodOnTarget_Inner( + module, + metadataToken, + targetInstanceType, + out methodAddress); + } + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + internal DispatchCellInfo ConvertDispatchCellInfo(NativeFormatModuleInfo module, DispatchCellInfo cellInfo) + { + using (LockHolder.Hold(_typeLoaderLock)) + { + return ConvertDispatchCellInfo_Inner( + module, + cellInfo); + } + } +#endif + + internal unsafe bool TryResolveTypeSlotDispatch(MethodTable* targetType, MethodTable* interfaceType, ushort slot, out IntPtr methodAddress) + { + using (LockHolder.Hold(_typeLoaderLock)) + { + return TryResolveTypeSlotDispatch_Inner(targetType, interfaceType, slot, out methodAddress); + } + } + + public unsafe bool TryGetOrCreateNamedTypeForMetadata( + QTypeDefinition qTypeDefinition, + out RuntimeTypeHandle runtimeTypeHandle) + { + if (TryGetNamedTypeForMetadata(qTypeDefinition, out runtimeTypeHandle)) + { + return true; + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + using (LockHolder.Hold(_typeLoaderLock)) + { + IntPtr runtimeTypeHandleAsIntPtr; + TypeBuilder.ResolveSingleTypeDefinition(qTypeDefinition, out runtimeTypeHandleAsIntPtr); + runtimeTypeHandle = *(RuntimeTypeHandle*)&runtimeTypeHandleAsIntPtr; + return true; + } +#else + return false; +#endif + } + + public static IntPtr ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(IntPtr unboxingFunctionPointer, RuntimeTypeHandle declaringType) + { + if (FunctionPointerOps.IsGenericMethodPointer(unboxingFunctionPointer)) + { + // Handle shared generic methods + unsafe + { + GenericMethodDescriptor* functionPointerDescriptor = FunctionPointerOps.ConvertToGenericDescriptor(unboxingFunctionPointer); + IntPtr nonUnboxingTarget = RuntimeAugments.GetCodeTarget(functionPointerDescriptor->MethodFunctionPointer); + Debug.Assert(nonUnboxingTarget != functionPointerDescriptor->MethodFunctionPointer); + Debug.Assert(nonUnboxingTarget == RuntimeAugments.GetCodeTarget(nonUnboxingTarget)); + return FunctionPointerOps.GetGenericMethodFunctionPointer(nonUnboxingTarget, functionPointerDescriptor->InstantiationArgument); + } + } + + // GetCodeTarget will look through simple unboxing stubs (ones that consist of adjusting the this pointer and then + // jumping to the target. + IntPtr exactTarget = RuntimeAugments.GetCodeTarget(unboxingFunctionPointer); + if (RuntimeAugments.IsGenericType(declaringType)) + { + IntPtr fatFunctionPointerTarget; + + // This check looks for unboxing and instantiating stubs generated via the compiler backend + if (TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(exactTarget, out fatFunctionPointerTarget)) + { + // If this is an unboxing and instantiating stub, use seperate table, find target, and create fat function pointer + exactTarget = FunctionPointerOps.GetGenericMethodFunctionPointer(fatFunctionPointerTarget, + declaringType.ToIntPtr()); + } + else + { + IntPtr newExactTarget; + // This check looks for unboxing and instantiating stubs generated dynamically as thunks in the calling convention converter + if (CallConverterThunk.TryGetNonUnboxingFunctionPointerFromUnboxingAndInstantiatingStub(exactTarget, + declaringType, out newExactTarget)) + { + // CallingConventionConverter determined non-unboxing stub + exactTarget = newExactTarget; + } + else + { + // Target method was a method on a generic, but it wasn't a shared generic, and thus none of the above + // complex unboxing stub digging logic was necessary. Do nothing, and use exactTarget as discovered + // from GetCodeTarget + } + } + } + + return exactTarget; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderLogger.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderLogger.cs new file mode 100644 index 00000000000000..7ae917146bdd63 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderLogger.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Internal.Runtime.TypeLoader +{ + internal static class TypeLoaderLogger + { + /// + /// Variable used to pause the runtime when a given message appears. To use this feature + /// attach a debugger to the process and set s_pauseHash to the hash code early in process + /// execution + /// + internal static int s_pauseHash; + + [Conditional("TYPE_LOADER_TRACE")] + public static void WriteLine(string message) + { + int hash = message.GetHashCode(); + + if (s_pauseHash != 0) + { + if (s_pauseHash == message.GetHashCode()) + Debugger.Break(); + } + Debug.WriteLine("[" + hash.LowLevelToString() + "] " + message); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs new file mode 100644 index 00000000000000..e28f575ca841f7 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs @@ -0,0 +1,318 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Diagnostics; + +using Internal.Metadata.NativeFormat; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; +using Internal.TypeSystem; +using Internal.TypeSystem.NativeFormat; +using Internal.TypeSystem.NoMetadata; +using Internal.Reflection.Core; +using Internal.Reflection.Execution; + +namespace Internal.Runtime.TypeLoader +{ + /// + /// TypeSystemContext that can interfact with the + /// Redhawk runtime type system and native metadata + /// + public partial class TypeLoaderTypeSystemContext : TypeSystemContext + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + private static readonly MetadataFieldLayoutAlgorithm s_metadataFieldLayoutAlgorithm = new MetadataFieldLayoutAlgorithm(); + private static readonly MetadataVirtualMethodAlgorithm s_metadataVirtualMethodAlgorithm = new MetadataVirtualMethodAlgorithm(); + private static readonly MetadataRuntimeInterfacesAlgorithm s_metadataRuntimeInterfacesAlgorithm = new MetadataRuntimeInterfacesAlgorithm(); +#endif + private static readonly NoMetadataFieldLayoutAlgorithm s_noMetadataFieldLayoutAlgorithm = new NoMetadataFieldLayoutAlgorithm(); + private static readonly NoMetadataRuntimeInterfacesAlgorithm s_noMetadataRuntimeInterfacesAlgorithm = new NoMetadataRuntimeInterfacesAlgorithm(); + private static readonly NativeLayoutFieldAlgorithm s_nativeLayoutFieldAlgorithm = new NativeLayoutFieldAlgorithm(); + private static readonly NativeLayoutInterfacesAlgorithm s_nativeLayoutInterfacesAlgorithm = new NativeLayoutInterfacesAlgorithm(); + + public TypeLoaderTypeSystemContext(TargetDetails targetDetails) : base(targetDetails) + { + ModuleDesc systemModule = null; + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + systemModule = ((MetadataType)GetWellKnownType(WellKnownType.Object)).Module; +#endif + + InitializeSystemModule(systemModule); + } + + public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type) + { + if ((type == UniversalCanonType) +#if SUPPORT_DYNAMIC_CODE + || (type.IsRuntimeDeterminedType && (((RuntimeDeterminedType)type).CanonicalType == UniversalCanonType))) +#else + ) +#endif + { + return UniversalCanonLayoutAlgorithm.Instance; + } + else if (type.RetrieveRuntimeTypeHandleIfPossible()) + { + // If the type is already constructed, use the NoMetadataFieldLayoutAlgorithm. + // its more efficient than loading from native layout or metadata. + return s_noMetadataFieldLayoutAlgorithm; + } + if (type.HasNativeLayout) + { + return s_nativeLayoutFieldAlgorithm; + } + else if (type is NoMetadataType) + { + return s_noMetadataFieldLayoutAlgorithm; + } + else + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + return s_metadataFieldLayoutAlgorithm; +#else + Debug.Assert(false); + return null; +#endif + } + } + + protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForDefType(DefType type) + { + if (type.RetrieveRuntimeTypeHandleIfPossible() && !type.IsGenericDefinition) + { + // If the type is already constructed, use the NoMetadataRuntimeInterfacesAlgorithm. + // its more efficient than loading from native layout or metadata. + return s_noMetadataRuntimeInterfacesAlgorithm; + } + else if (type.HasNativeLayout) + { + return s_nativeLayoutInterfacesAlgorithm; + } + else if (type is NoMetadataType) + { + return s_noMetadataRuntimeInterfacesAlgorithm; + } +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + else if (type is MetadataType) + { + return s_metadataRuntimeInterfacesAlgorithm; + } +#endif + else + { + Debug.Assert(false); + return null; + } + } + + protected internal sealed override bool IsIDynamicInterfaceCastableInterface(DefType type) + { + throw new NotImplementedException(); + } + + protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForNonPointerArrayType(ArrayType type) + { + // At runtime, we're instantiating an Array instantiation as the template, so we know we'll always have + // a NativeLayoutInterfacesAlgorithm to work with + return s_nativeLayoutInterfacesAlgorithm; + } + + public override DefType GetWellKnownType(WellKnownType wellKnownType, bool throwIfNotFound = true) + { + switch (wellKnownType) + { + case WellKnownType.Void: + return (DefType)ResolveRuntimeTypeHandle(typeof(void).TypeHandle); + + case WellKnownType.Boolean: + return (DefType)ResolveRuntimeTypeHandle(typeof(bool).TypeHandle); + + case WellKnownType.Char: + return (DefType)ResolveRuntimeTypeHandle(typeof(char).TypeHandle); + + case WellKnownType.SByte: + return (DefType)ResolveRuntimeTypeHandle(typeof(sbyte).TypeHandle); + + case WellKnownType.Byte: + return (DefType)ResolveRuntimeTypeHandle(typeof(byte).TypeHandle); + + case WellKnownType.Int16: + return (DefType)ResolveRuntimeTypeHandle(typeof(short).TypeHandle); + + case WellKnownType.UInt16: + return (DefType)ResolveRuntimeTypeHandle(typeof(ushort).TypeHandle); + + case WellKnownType.Int32: + return (DefType)ResolveRuntimeTypeHandle(typeof(int).TypeHandle); + + case WellKnownType.UInt32: + return (DefType)ResolveRuntimeTypeHandle(typeof(uint).TypeHandle); + + case WellKnownType.Int64: + return (DefType)ResolveRuntimeTypeHandle(typeof(long).TypeHandle); + + case WellKnownType.UInt64: + return (DefType)ResolveRuntimeTypeHandle(typeof(ulong).TypeHandle); + + case WellKnownType.IntPtr: + return (DefType)ResolveRuntimeTypeHandle(typeof(IntPtr).TypeHandle); + + case WellKnownType.UIntPtr: + return (DefType)ResolveRuntimeTypeHandle(typeof(UIntPtr).TypeHandle); + + case WellKnownType.Single: + return (DefType)ResolveRuntimeTypeHandle(typeof(float).TypeHandle); + + case WellKnownType.Double: + return (DefType)ResolveRuntimeTypeHandle(typeof(double).TypeHandle); + + case WellKnownType.ValueType: + return (DefType)ResolveRuntimeTypeHandle(typeof(ValueType).TypeHandle); + + case WellKnownType.Enum: + return (DefType)ResolveRuntimeTypeHandle(typeof(Enum).TypeHandle); + + case WellKnownType.Nullable: + return (DefType)ResolveRuntimeTypeHandle(typeof(Nullable<>).TypeHandle); + + case WellKnownType.Object: + return (DefType)ResolveRuntimeTypeHandle(typeof(object).TypeHandle); + + case WellKnownType.String: + return (DefType)ResolveRuntimeTypeHandle(typeof(string).TypeHandle); + + case WellKnownType.Array: + return (DefType)ResolveRuntimeTypeHandle(typeof(Array).TypeHandle); + + case WellKnownType.MulticastDelegate: + return (DefType)ResolveRuntimeTypeHandle(typeof(MulticastDelegate).TypeHandle); + + case WellKnownType.RuntimeTypeHandle: + return (DefType)ResolveRuntimeTypeHandle(typeof(RuntimeTypeHandle).TypeHandle); + + case WellKnownType.RuntimeMethodHandle: + return (DefType)ResolveRuntimeTypeHandle(typeof(RuntimeMethodHandle).TypeHandle); + + case WellKnownType.RuntimeFieldHandle: + return (DefType)ResolveRuntimeTypeHandle(typeof(RuntimeFieldHandle).TypeHandle); + + case WellKnownType.Exception: + return (DefType)ResolveRuntimeTypeHandle(typeof(Exception).TypeHandle); + + default: + if (throwIfNotFound) + throw new TypeLoadException(); + else + return null; + } + } + + public override ModuleDesc ResolveAssembly(AssemblyName name, bool throwErrorIfNotFound) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + AssemblyBindResult bindResult; + Exception failureException; + if (!AssemblyBinderImplementation.Instance.Bind(name.ToRuntimeAssemblyName(), cacheMissedLookups: true, out bindResult, out failureException)) + { + if (throwErrorIfNotFound) + throw failureException; + return null; + } + + var moduleList = Internal.Runtime.TypeLoader.ModuleList.Instance; + + if (bindResult.Reader != null) + { + NativeFormatModuleInfo primaryModule = moduleList.GetModuleInfoForMetadataReader(bindResult.Reader); + NativeFormatMetadataUnit metadataUnit = ResolveMetadataUnit(primaryModule); + return metadataUnit.GetModule(bindResult.ScopeDefinitionHandle); + } +#if ECMA_METADATA_SUPPORT + else if (bindResult.EcmaMetadataReader != null) + { + EcmaModuleInfo ecmaModule = moduleList.GetModuleInfoForMetadataReader(bindResult.EcmaMetadataReader); + return ResolveEcmaModule(ecmaModule); + } +#endif + else + { + // Should not be possible to reach here + throw new Exception(); + } +#else + return null; +#endif + } + + public override VirtualMethodAlgorithm GetVirtualMethodAlgorithmForType(TypeDesc type) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + Debug.Assert(!type.IsArray, "Wanted to call GetClosestMetadataType?"); + + return s_metadataVirtualMethodAlgorithm; +#else + Debug.Assert(false); + return null; +#endif + } + + protected internal override Instantiation ConvertInstantiationToCanonForm(Instantiation instantiation, CanonicalFormKind kind, out bool changed) + { + return StandardCanonicalizationAlgorithm.ConvertInstantiationToCanonForm(instantiation, kind, out changed); + } + + protected internal override TypeDesc ConvertToCanon(TypeDesc typeToConvert, CanonicalFormKind kind) + { + return StandardCanonicalizationAlgorithm.ConvertToCanon(typeToConvert, kind); + } + + protected internal override bool ComputeHasGCStaticBase(FieldDesc field) + { + Debug.Assert(field.IsStatic); + + if (field is NativeLayoutFieldDesc) + { + return ((NativeLayoutFieldDesc)field).FieldStorage == Internal.NativeFormat.FieldStorage.GCStatic; + } + + TypeDesc fieldType = field.FieldType; + if (fieldType.IsValueType) + { + FieldDesc typicalField = field.GetTypicalFieldDefinition(); + + if (field != typicalField) + { + if (typicalField.FieldType.IsSignatureVariable) + return true; + } + if (fieldType.IsEnum || fieldType.IsPrimitive) + return false; + return true; + } + else + return fieldType.IsGCPointer; + } + + protected internal override bool ComputeHasStaticConstructor(TypeDesc type) + { + if (type.RetrieveRuntimeTypeHandleIfPossible()) + { + unsafe + { + return type.RuntimeTypeHandle.ToEETypePtr()->HasCctor; + } + } + else if (type is MetadataType) + { + return ((MetadataType)type).GetStaticConstructor() != null; + } + return false; + } + + public override bool SupportsUniversalCanon => true; + public override bool SupportsCanon => true; + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs new file mode 100644 index 00000000000000..4d3fa768fdd2d6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Runtime.InteropServices; + +using Internal.TypeSystem; + +namespace Internal.Runtime.TypeLoader +{ + public static class TypeSystemContextFactory + { + // Cache the most recent instance of TypeSystemContext in a weak handle, and reuse it if possible + // This allows us to avoid recreating the type resolution context again and again, but still allows it to go away once the types are no longer being built + private static GCHandle s_cachedContext = GCHandle.Alloc(null, GCHandleType.Weak); + + private static Lock s_lock = new Lock(); + + public static TypeSystemContext Create() + { + using (LockHolder.Hold(s_lock)) + { + TypeSystemContext context = (TypeSystemContext)s_cachedContext.Target; + if (context != null) + { + s_cachedContext.Target = null; + return context; + } + } + return new TypeLoaderTypeSystemContext(new TargetDetails( +#if TARGET_ARM + TargetArchitecture.ARM, +#elif TARGET_ARM64 + TargetArchitecture.ARM64, +#elif TARGET_X86 + TargetArchitecture.X86, +#elif TARGET_AMD64 + TargetArchitecture.X64, +#elif TARGET_WASM + TargetArchitecture.Wasm32, +#else +#error Unknown architecture +#endif + TargetOS.Windows, + TargetAbi.Unknown)); + } + + public static void Recycle(TypeSystemContext context) + { + // Only cache a reasonably small context that is still in Gen0 + if (context.LoadFactor > 200 || GC.GetGeneration(context) > 0) + return; + + // Flush the type system context from all types being recycled + context.FlushTypeBuilderStates(); + + // No lock needed here - the reference assignment is atomic + s_cachedContext.Target = context; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemExtensions.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemExtensions.cs new file mode 100644 index 00000000000000..a97b75c0c150a3 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemExtensions.cs @@ -0,0 +1,158 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.NativeFormat; +using Internal.TypeSystem.NativeFormat; +#if ECMA_METADATA_SUPPORT +using Internal.TypeSystem.Ecma; +#endif +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; +using Internal.TypeSystem; +using Internal.TypeSystem.NoMetadata; +using System.Reflection.Runtime.General; + +namespace Internal.TypeSystem.NativeFormat +{ + // When SUPPORTS_NATIVE_METADATA_TYPE_LOADING is not set we may see compile errors from using statements. + // Add a namespace definition for Internal.TypeSystem.NativeFormat +} + +namespace Internal.Runtime.TypeLoader +{ + internal static class TypeDescExtensions + { + public static bool CanShareNormalGenericCode(this TypeDesc type) + { + return (type != type.ConvertToCanonForm(CanonicalFormKind.Specific)); + } + + public static bool IsGeneric(this TypeDesc type) + { + DefType typeAsDefType = type as DefType; + return typeAsDefType != null && typeAsDefType.HasInstantiation; + } + + public static DefType GetClosestDefType(this TypeDesc type) + { + if (type is DefType) + return (DefType)type; + else + return type.BaseType; + } + } + + internal static class MethodDescExtensions + { + public static bool CanShareNormalGenericCode(this InstantiatedMethod method) + { + return (method != method.GetCanonMethodTarget(CanonicalFormKind.Specific)); + } + } + + internal static class RuntimeHandleExtensions + { + public static bool IsNull(this RuntimeTypeHandle rtth) + { + return RuntimeAugments.GetRuntimeTypeHandleRawValue(rtth) == IntPtr.Zero; + } + + public static unsafe bool IsDynamic(this RuntimeFieldHandle rtfh) + { + IntPtr rtfhValue = *(IntPtr*)&rtfh; + return (rtfhValue.ToInt64() & 0x1) == 0x1; + } + + public static unsafe bool IsDynamic(this RuntimeMethodHandle rtfh) + { + IntPtr rtfhValue = *(IntPtr*)&rtfh; + return (rtfhValue.ToInt64() & 0x1) == 0x1; + } + } + + public static partial class RuntimeSignatureHelper + { + public static bool TryCreate(MethodDesc method, out RuntimeSignature methodSignature) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + + if (typicalMethod is TypeSystem.NativeFormat.NativeFormatMethod) + { + TypeSystem.NativeFormat.NativeFormatMethod nativeFormatMethod = (TypeSystem.NativeFormat.NativeFormatMethod)typicalMethod; + methodSignature = RuntimeSignature.CreateFromMethodHandle(nativeFormatMethod.MetadataUnit.RuntimeModule, nativeFormatMethod.Handle.ToInt()); + return true; + } +#if ECMA_METADATA_SUPPORT + if (typicalMethod is TypeSystem.Ecma.EcmaMethod) + { + unsafe + { + TypeSystem.Ecma.EcmaMethod ecmaMethod = (TypeSystem.Ecma.EcmaMethod)typicalMethod; + methodSignature = RuntimeSignature.CreateFromMethodHandle(new IntPtr(ecmaMethod.Module.RuntimeModuleInfo.DynamicModulePtr), System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(ecmaMethod.Handle)); + } + return true; + } +#endif +#endif + methodSignature = default(RuntimeSignature); + return false; + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + public static MethodDesc ToMethodDesc(this RuntimeMethodHandle rmh, TypeSystemContext typeSystemContext) + { + RuntimeTypeHandle declaringTypeHandle; + MethodNameAndSignature nameAndSignature; + RuntimeTypeHandle[] genericMethodArgs; + + if (!TypeLoaderEnvironment.Instance.TryGetRuntimeMethodHandleComponents(rmh, out declaringTypeHandle, out nameAndSignature, out genericMethodArgs)) + { + return null; + } + + QMethodDefinition methodHandle; + if (!TypeLoaderEnvironment.Instance.TryGetMetadataForTypeMethodNameAndSignature(declaringTypeHandle, nameAndSignature, out methodHandle)) + { + return null; + } + + TypeDesc declaringType = typeSystemContext.ResolveRuntimeTypeHandle(declaringTypeHandle); + + TypeDesc declaringTypeDefinition = declaringType.GetTypeDefinition(); + MethodDesc typicalMethod = null; + if (methodHandle.IsNativeFormatMetadataBased) + { + var nativeFormatType = (NativeFormatType)declaringTypeDefinition; + typicalMethod = nativeFormatType.MetadataUnit.GetMethod(methodHandle.NativeFormatHandle, nativeFormatType); + } + else if (methodHandle.IsEcmaFormatMetadataBased) + { + var ecmaFormatType = (EcmaType)declaringTypeDefinition; + typicalMethod = ecmaFormatType.EcmaModule.GetMethod(methodHandle.EcmaFormatHandle); + } + Debug.Assert(typicalMethod != null); + + MethodDesc methodOnInstantiatedType = typicalMethod; + if (declaringType != declaringTypeDefinition) + methodOnInstantiatedType = typeSystemContext.GetMethodForInstantiatedType(typicalMethod, (InstantiatedType)declaringType); + + MethodDesc instantiatedMethod = methodOnInstantiatedType; + if (genericMethodArgs != null) + { + Debug.Assert(genericMethodArgs.Length > 0); + Instantiation genericMethodInstantiation = typeSystemContext.ResolveRuntimeTypeHandles(genericMethodArgs); + typeSystemContext.GetInstantiatedMethod(methodOnInstantiatedType, genericMethodInstantiation); + } + + return instantiatedMethod; + } +#endif + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/WellKnownTypeExtensions.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/WellKnownTypeExtensions.cs new file mode 100644 index 00000000000000..4db542dcc1bbaa --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/WellKnownTypeExtensions.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.TypeSystem; + +namespace Internal.Runtime.TypeLoader +{ + public static class WellKnownTypeExtensions + { + public static RuntimeTypeHandle GetRuntimeTypeHandle(this WellKnownType wkt) + { + switch (wkt) + { + case WellKnownType.Void: + return typeof(void).TypeHandle; + case WellKnownType.Boolean: + return typeof(bool).TypeHandle; + case WellKnownType.Char: + return typeof(char).TypeHandle; + case WellKnownType.SByte: + return typeof(sbyte).TypeHandle; + case WellKnownType.Byte: + return typeof(byte).TypeHandle; + case WellKnownType.Int16: + return typeof(short).TypeHandle; + case WellKnownType.UInt16: + return typeof(ushort).TypeHandle; + case WellKnownType.Int32: + return typeof(int).TypeHandle; + case WellKnownType.UInt32: + return typeof(uint).TypeHandle; + case WellKnownType.Int64: + return typeof(long).TypeHandle; + case WellKnownType.UInt64: + return typeof(ulong).TypeHandle; + case WellKnownType.IntPtr: + return typeof(IntPtr).TypeHandle; + case WellKnownType.UIntPtr: + return typeof(UIntPtr).TypeHandle; + case WellKnownType.Single: + return typeof(float).TypeHandle; + case WellKnownType.Double: + return typeof(double).TypeHandle; + case WellKnownType.String: + return typeof(string).TypeHandle; + default: + Debug.Assert(false); + return default(RuntimeTypeHandle); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/ArrayMethod.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/ArrayMethod.Runtime.cs new file mode 100644 index 00000000000000..e29806ef48b988 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/ArrayMethod.Runtime.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using Internal.Runtime.CompilerServices; + +namespace Internal.TypeSystem +{ + public partial class ArrayMethod : MethodDesc + { + public override MethodNameAndSignature NameAndSignature + { + get + { + // TODO Eventually implement via working with a RuntimeMethod that refers to the actual implementation. + // https://github.com/dotnet/corert/issues/3772 + throw new NotImplementedException(); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/CanonTypes.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/CanonTypes.Runtime.cs new file mode 100644 index 00000000000000..1c21486f7c38a1 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/CanonTypes.Runtime.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; + +using Internal.Runtime; +using Internal.Runtime.Augments; + +namespace Internal.TypeSystem +{ + internal partial class CanonType + { + partial void Initialize() + { + SetRuntimeTypeHandleUnsafe(RuntimeAugments.GetCanonType(CanonTypeKind.NormalCanon)); + } + } + + internal partial class UniversalCanonType + { + partial void Initialize() + { + SetRuntimeTypeHandleUnsafe(RuntimeAugments.GetCanonType(CanonTypeKind.UniversalCanon)); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/DefType.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/DefType.Runtime.cs new file mode 100644 index 00000000000000..679fae52014d60 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/DefType.Runtime.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.NativeFormat; +using Internal.Runtime.TypeLoader; +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Internal.TypeSystem +{ + // Includes functionality for runtime type loading + public partial class DefType + { + internal static readonly LayoutInt MaximumAlignmentPossible = new LayoutInt(8); + + internal IEnumerable GetDiagnosticFields() + { + if (HasNativeLayout) + { + // Universal template fields get diagnostic info, but normal canon templates do not + if (IsTemplateUniversal()) + { + NativeLayoutFieldAlgorithm.EnsureFieldLayoutLoadedForGenericType(this); + return NativeLayoutFields; + } + return FieldDesc.EmptyFields; + } + else + { + // This will only happen for fully formed metadata based loads... + return GetFields(); + } + } + + public FieldDesc GetFieldByNativeLayoutOrdinal(uint ordinal) + { + NativeLayoutFieldAlgorithm.EnsureFieldLayoutLoadedForGenericType(this); + return NativeLayoutFields[ordinal]; + } + + public virtual bool HasNativeLayout + { + get + { + // Attempt to compute the template, if there isn't one, then there isn't native layout + return ComputeTemplate(false) != null; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/ExceptionTypeNameFormatter.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/ExceptionTypeNameFormatter.Runtime.cs new file mode 100644 index 00000000000000..9cdd08202ab238 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/ExceptionTypeNameFormatter.Runtime.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem +{ + partial class ExceptionTypeNameFormatter + { + private string GetTypeName(DefType type) + { + if (type is NoMetadata.NoMetadataType) + return ((NoMetadata.NoMetadataType)type).NameForDiagnostics; + + return type.Name; + } + + private string GetTypeNamespace(DefType type) + { + if (type is NoMetadata.NoMetadataType) + return ((NoMetadata.NoMetadataType)type).NamespaceForDiagnostics; + + return type.Namespace; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/InstantiatedMethod.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/InstantiatedMethod.Runtime.cs new file mode 100644 index 00000000000000..f87b5d5607586c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/InstantiatedMethod.Runtime.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using Internal.NativeFormat; +using Internal.Runtime.CompilerServices; +using Internal.Runtime.TypeLoader; +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem +{ + public sealed partial class InstantiatedMethod : MethodDesc + { + public override MethodNameAndSignature NameAndSignature + { + get + { + return _methodDef.NameAndSignature; + } + } + + + protected override bool ComputeIsNonSharableMethod() + { + return !IsCanonicalMethod(CanonicalFormKind.Any) && + this == GetCanonMethodTarget(CanonicalFormKind.Specific); + } + + + /// + /// Does this method need a dictionary? + /// + public bool NeedsDictionary { get; set; } + + /// + /// IntPtr pointing at allocated method generic dictionary. + /// + public IntPtr RuntimeMethodDictionary { get; private set; } + internal GenericDictionary Dictionary { get; private set; } + + // Set the Dictionary property. This is a helper function so that setting is easy to search for + internal void SetGenericDictionary(GenericDictionary dictionary) + { + Dictionary = dictionary; + } + + /// + /// Attach a generic dictionary to this method + /// + /// + public void AssociateWithRuntimeMethodDictionary(IntPtr rmd) + { + Debug.Assert(rmd != IntPtr.Zero); + RuntimeMethodDictionary = rmd; + } + + public override bool UnboxingStub + { + get + { + return _methodDef.UnboxingStub; + } + } + +#if DEBUG + public override string ToString() + { + var sb = new System.Text.StringBuilder(_methodDef.ToString()); + sb.Append('<'); + sb.Append(_instantiation.ToString()); + sb.Append('>'); + return sb.ToString(); + } +#endif + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/MethodDesc.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/MethodDesc.Runtime.cs new file mode 100644 index 00000000000000..270499eb05982d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/MethodDesc.Runtime.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using Internal.Runtime.CompilerServices; + +namespace Internal.TypeSystem +{ + public partial class MethodDesc + { + private IntPtr _functionPointer; + private IntPtr _usgFunctionPointer; + + public void SetFunctionPointer(IntPtr functionPointer, bool isFunctionPointerUSG) + { + if (isFunctionPointerUSG) + _usgFunctionPointer = functionPointer; + else + _functionPointer = functionPointer; + } + + /// + /// Pointer to function's code. May be IntPtr.Zero + /// + public IntPtr FunctionPointer + { + get + { + return _functionPointer; + } + } + + /// + /// Pointer to function's universal shared generics code. May be IntPtr.Zero + /// + public IntPtr UsgFunctionPointer + { + get + { + return _usgFunctionPointer; + } + } + + public abstract MethodNameAndSignature NameAndSignature { get; } + + private bool? _isNonSharableCache; + public virtual bool IsNonSharableMethod + { + get + { + if (!_isNonSharableCache.HasValue) + { + _isNonSharableCache = ComputeIsNonSharableMethod(); + } + return _isNonSharableCache.Value; + } + } + + protected virtual bool ComputeIsNonSharableMethod() + { + return !OwningType.IsCanonicalSubtype(CanonicalFormKind.Any) && + OwningType == (OwningType.ConvertToCanonForm(CanonicalFormKind.Specific) as DefType); + } + + public virtual bool UnboxingStub + { + get + { + return false; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/MethodForInstantiatedType.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/MethodForInstantiatedType.Runtime.cs new file mode 100644 index 00000000000000..b6fcc2796a67ee --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/MethodForInstantiatedType.Runtime.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using Internal.Runtime.CompilerServices; + +namespace Internal.TypeSystem +{ + public sealed partial class MethodForInstantiatedType : MethodDesc + { + public override MethodNameAndSignature NameAndSignature + { + get + { + return _typicalMethodDef.NameAndSignature; + } + } + +#if DEBUG + public override string ToString() + { + return OwningType.ToString() + "." + Name; + } +#endif + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/NoMetadataMethodDesc.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/NoMetadataMethodDesc.cs new file mode 100644 index 00000000000000..cbc1b2811b0eb9 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/NoMetadataMethodDesc.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using Internal.Runtime.CompilerServices; + +namespace Internal.TypeSystem.NoMetadata +{ + /// + /// Represents a method that does not have metadata + /// + internal abstract class NoMetadataMethodDesc : MethodDesc + { + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.Canon.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.Canon.cs new file mode 100644 index 00000000000000..956dcdcafb9761 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.Canon.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; + +namespace Internal.TypeSystem.NoMetadata +{ + // Implements runtime method canonicalization + internal partial class RuntimeMethodDesc + { + public override bool IsCanonicalMethod(CanonicalFormKind policy) + { + return OwningType.HasInstantiation && OwningType.IsCanonicalSubtype(policy); + } + + public override MethodDesc GetCanonMethodTarget(CanonicalFormKind kind) + { + if (!OwningType.HasInstantiation) + return this; + + DefType canonicalizedTypeOfTargetMethod = (DefType)OwningType.ConvertToCanonForm(kind); + if (canonicalizedTypeOfTargetMethod == OwningType) + return this; + + return Context.ResolveRuntimeMethod(this.UnboxingStub, canonicalizedTypeOfTargetMethod, this.NameAndSignature, IntPtr.Zero, false); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.cs new file mode 100644 index 00000000000000..9cf5c49c1d4013 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.cs @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using Internal.Runtime.CompilerServices; +using Internal.Runtime.TypeLoader; + +namespace Internal.TypeSystem.NoMetadata +{ + /// + /// Represents a method within the Redhawk runtime + /// + internal sealed partial class RuntimeMethodDesc : NoMetadataMethodDesc + { + public RuntimeMethodDesc(bool unboxingStub, DefType owningType, + MethodNameAndSignature nameAndSignature, int hashcode) + { + _owningType = owningType; + _nameAndSignature = nameAndSignature; + _unboxingStub = unboxingStub; + SetHashCode(hashcode); + +#if DEBUG + DebugName = this.ToString(); +#endif + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + private Instantiation _instantiation; + + public override Instantiation Instantiation + { + get + { + if (_instantiation.IsNull) + { + uint genericArgCount = TypeLoaderEnvironment.Instance.GetGenericArgumentCountFromMethodNameAndSignature(_nameAndSignature); + if (genericArgCount == 0) + { + _instantiation = Instantiation.Empty; + } + else + { + TypeDesc[] genericParameters = new TypeDesc[genericArgCount]; + for (int i = 0; i < genericParameters.Length; i++) + { + genericParameters[i] = Context.GetSignatureVariable(i, true); + } + _instantiation = new Instantiation(genericParameters); + } + } + return _instantiation; + } + } + + private TypeDesc _owningType; + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + public override MethodSignature Signature + { + get + { + throw new NotSupportedException(); + } + } + + private MethodNameAndSignature _nameAndSignature; + public override MethodNameAndSignature NameAndSignature + { + get + { + return _nameAndSignature; + } + } + + public override string Name + { + get + { + return _nameAndSignature.Name; + } + } + + private bool _unboxingStub; + public override bool UnboxingStub + { + get + { + return _unboxingStub; + } + } + + public override MethodDesc GetTypicalMethodDefinition() + { + TypeDesc owningTypeDefinition = OwningType.GetTypeDefinition(); + + // If this method is on a type that is its own type definition, this it is the type method + if (owningTypeDefinition == OwningType) + { + return this; + } + + // Otherwise, find its equivalent on the type definition of the owning type + return Context.ResolveRuntimeMethod(UnboxingStub, (DefType)owningTypeDefinition, _nameAndSignature, IntPtr.Zero, false); + } + + public override MethodDesc InstantiateSignature(Instantiation typeInstantiation, Instantiation methodInstantiation) + { + MethodDesc method = this; + + TypeDesc owningType = method.OwningType; + TypeDesc instantiatedOwningType = owningType.InstantiateSignature(typeInstantiation, methodInstantiation); + if (owningType != instantiatedOwningType) + method = instantiatedOwningType.Context.ResolveRuntimeMethod(UnboxingStub, (DefType)instantiatedOwningType, _nameAndSignature, IntPtr.Zero, false); + + Instantiation instantiation = method.Instantiation; + TypeDesc[] clone = null; + + for (int i = 0; i < instantiation.Length; i++) + { + TypeDesc uninst = instantiation[i]; + TypeDesc inst = uninst.InstantiateSignature(typeInstantiation, methodInstantiation); + if (inst != uninst) + { + if (clone == null) + { + clone = new TypeDesc[instantiation.Length]; + for (int j = 0; j < clone.Length; j++) + { + clone[j] = instantiation[j]; + } + } + clone[i] = inst; + } + } + + return (clone == null) ? method : method.Context.GetInstantiatedMethod(method.GetMethodDefinition(), new Instantiation(clone)); + } + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) + { + throw new PlatformNotSupportedException(); + } + +#if DEBUG + public string DebugName; + + public override string ToString() + { + string result = OwningType.ToString() + ".Method(" + NameAndSignature.Name + ")"; + return result; + } +#endif + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeNoMetadataType.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeNoMetadataType.cs new file mode 100644 index 00000000000000..86dc00e75efc7c --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeNoMetadataType.cs @@ -0,0 +1,378 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Text; +using System.Reflection.Runtime.General; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.TypeLoader; +using Internal.Metadata.NativeFormat; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem.NoMetadata +{ + /// + /// Type that once had metadata, but that metadata is not available + /// for the lifetime of the TypeSystemContext. Directly correlates + /// to a RuntimeTypeHandle useable in the current environment. + /// This type replaces the placeholder NoMetadataType that comes + /// with the common type system codebase + /// + internal class NoMetadataType : DefType + { + private TypeSystemContext _context; + private int _hashcode; + private RuntimeTypeHandle _genericTypeDefinition; + private DefType _genericTypeDefinitionAsDefType; + private Instantiation _instantiation; + + // "_baseType == this" means "base type was not initialized yet" + private DefType _baseType; + + public NoMetadataType(TypeSystemContext context, RuntimeTypeHandle genericTypeDefinition, DefType genericTypeDefinitionAsDefType, Instantiation instantiation, int hashcode) + { + _hashcode = hashcode; + _context = context; + _genericTypeDefinition = genericTypeDefinition; + _genericTypeDefinitionAsDefType = genericTypeDefinitionAsDefType; + if (_genericTypeDefinitionAsDefType == null) + _genericTypeDefinitionAsDefType = this; + _instantiation = instantiation; + + // Instantiation must either be: + // Something valid (if the type is generic, or a generic type definition) + // or Empty (if the type isn't a generic of any form) + unsafe + { + Debug.Assert(((_instantiation.Length > 0) && _genericTypeDefinition.ToEETypePtr()->IsGenericTypeDefinition) || + ((_instantiation.Length == 0) && !_genericTypeDefinition.ToEETypePtr()->IsGenericTypeDefinition)); + } + + // Base type is not initialized + _baseType = this; + } + + public override int GetHashCode() + { + return _hashcode; + } + + public override TypeSystemContext Context + { + get + { + return _context; + } + } + + public override DefType BaseType + { + get + { + // _baseType == this means we didn't initialize it yet + if (_baseType != this) + return _baseType; + + if (RetrieveRuntimeTypeHandleIfPossible()) + { + RuntimeTypeHandle baseTypeHandle; + if (!RuntimeAugments.TryGetBaseType(RuntimeTypeHandle, out baseTypeHandle)) + { + Debug.Assert(false); + } + + DefType baseType = !baseTypeHandle.IsNull() ? (DefType)Context.ResolveRuntimeTypeHandle(baseTypeHandle) : null; + SetBaseType(baseType); + + return baseType; + } + else + { + // Parsing of the base type has not yet happened. Perform that part of native layout parsing + // just-in-time + TypeBuilderState state = GetOrCreateTypeBuilderState(); + + ComputeTemplate(); + NativeParser typeInfoParser = state.GetParserForNativeLayoutInfo(); + NativeParser baseTypeParser = typeInfoParser.GetParserForBagElementKind(BagElementKind.BaseType); + + ParseBaseType(state.NativeLayoutInfo.LoadContext, baseTypeParser); + Debug.Assert(_baseType != this); + return _baseType; + } + } + } + + internal override void ParseBaseType(NativeLayoutInfoLoadContext nativeLayoutInfoLoadContext, NativeParser baseTypeParser) + { + if (!baseTypeParser.IsNull) + { + // If the base type is available from the native layout info use it if the type we have is a NoMetadataType + SetBaseType((DefType)nativeLayoutInfoLoadContext.GetType(ref baseTypeParser)); + } + else + { + // Set the base type for no metadata types, if we reach this point, and there isn't a parser, then we simply use the value from the template + SetBaseType(ComputeTemplate().BaseType); + } + } + + /// + /// This is used to set base type for generic types without metadata + /// + public void SetBaseType(DefType baseType) + { + Debug.Assert(_baseType == this || _baseType == baseType); + _baseType = baseType; + } + + protected override TypeFlags ComputeTypeFlags(TypeFlags mask) + { + TypeFlags flags = 0; + + if ((mask & TypeFlags.CategoryMask) != 0) + { + unsafe + { + MethodTable* MethodTable = _genericTypeDefinition.ToEETypePtr(); + EETypeElementType elementType = MethodTable->ElementType; + if (elementType == EETypeElementType.SystemArray) + { + // System.Array is a regular class in the type system + flags |= TypeFlags.Class; + } + else if (elementType <= EETypeElementType.Double && + (MethodTable->IsGenericTypeDefinition || MethodTable->BaseType == typeof(System.Enum).TypeHandle.ToEETypePtr())) + { + // Enums are represented as their underlying type in the runtime type system + // Note: we check for IsGenericDefinition above to cover generic enums (base types are not set + // on generic definition MethodTable) + flags |= TypeFlags.Enum; + } + else + { + // Paranoid check that we handled enums above + Debug.Assert(MethodTable->IsGenericTypeDefinition || + MethodTable->BaseType != typeof(System.Enum).TypeHandle.ToEETypePtr()); + + // The rest of values should be directly castable to TypeFlags + Debug.Assert((int)EETypeElementType.Void == (int)TypeFlags.Void); + Debug.Assert((int)EETypeElementType.Int32 == (int)TypeFlags.Int32); + Debug.Assert((int)EETypeElementType.IntPtr == (int)TypeFlags.IntPtr); + Debug.Assert((int)EETypeElementType.Double == (int)TypeFlags.Double); + Debug.Assert((int)EETypeElementType.Pointer == (int)TypeFlags.Pointer); + Debug.Assert((int)EETypeElementType.Class == (int)TypeFlags.Class); + Debug.Assert((int)EETypeElementType.Nullable == (int)TypeFlags.Nullable); + + flags |= (TypeFlags)elementType; + } + } + } + + if ((mask & TypeFlags.AttributeCacheComputed) != 0) + { + flags |= TypeFlags.AttributeCacheComputed; + + unsafe + { + MethodTable* MethodTable = _genericTypeDefinition.ToEETypePtr(); + if (MethodTable->IsByRefLike) + { + flags |= TypeFlags.IsByRefLike; + } + } + } + + return flags; + } + + // Canonicalization handling + + public override bool IsCanonicalSubtype(CanonicalFormKind policy) + { + foreach (TypeDesc t in Instantiation) + { + if (t.IsCanonicalSubtype(policy)) + { + return true; + } + } + + return false; + } + + protected override TypeDesc ConvertToCanonFormImpl(CanonicalFormKind kind) + { + bool needsChange; + Instantiation canonInstantiation = Context.ConvertInstantiationToCanonForm(Instantiation, kind, out needsChange); + if (needsChange) + { + TypeDesc openType = GetTypeDefinition(); + return openType.InstantiateSignature(canonInstantiation, new Instantiation()); + } + + return this; + } + + public override TypeDesc GetTypeDefinition() + { + if (_genericTypeDefinitionAsDefType != null) + return _genericTypeDefinitionAsDefType; + else + return this; + } + + public override TypeDesc InstantiateSignature(Instantiation typeInstantiation, Instantiation methodInstantiation) + { + TypeDesc[] clone = null; + + for (int i = 0; i < _instantiation.Length; i++) + { + TypeDesc uninst = _instantiation[i]; + TypeDesc inst = uninst.InstantiateSignature(typeInstantiation, methodInstantiation); + if (inst != uninst) + { + if (clone == null) + { + clone = new TypeDesc[_instantiation.Length]; + for (int j = 0; j < clone.Length; j++) + { + clone[j] = _instantiation[j]; + } + } + clone[i] = inst; + } + } + + return (clone == null) ? this : _genericTypeDefinitionAsDefType.Context.ResolveGenericInstantiation(_genericTypeDefinitionAsDefType, new Instantiation(clone)); + } + + public override Instantiation Instantiation + { + get + { + return _instantiation; + } + } + + public override TypeDesc UnderlyingType + { + get + { + if (!this.IsEnum) + return this; + + unsafe + { + EETypeElementType elementType = RuntimeTypeHandle.ToEETypePtr()->ElementType; + Debug.Assert((int)EETypeElementType.Void == (int)WellKnownType.Void); + Debug.Assert((int)EETypeElementType.Int32 == (int)WellKnownType.Int32); + Debug.Assert((int)EETypeElementType.IntPtr == (int)WellKnownType.IntPtr); + Debug.Assert((int)EETypeElementType.Double == (int)WellKnownType.Double); + Debug.Assert(elementType <= EETypeElementType.Double); + return Context.GetWellKnownType((WellKnownType)elementType); + } + } + } + + private void GetTypeNameHelper(out string name, out string nsName, out string assemblyName) + { + RuntimeTypeHandle genericDefinitionHandle = GetTypeDefinition().GetRuntimeTypeHandle(); + Debug.Assert(!genericDefinitionHandle.IsNull()); + +#if DEBUG + TypeReferenceHandle typeRefHandle; + QTypeDefinition qTypeDefinition; + MetadataReader reader; + + string enclosingDummy; + + // Try to get the name from metadata + if (TypeLoaderEnvironment.Instance.TryGetMetadataForNamedType(genericDefinitionHandle, out qTypeDefinition)) + { + TypeDefinitionHandle typeDefHandle = qTypeDefinition.NativeFormatHandle; + typeDefHandle.GetFullName(qTypeDefinition.NativeFormatReader, out name, out enclosingDummy, out nsName); + assemblyName = typeDefHandle.GetContainingModuleName(qTypeDefinition.NativeFormatReader); + } + // Try to get the name from diagnostic metadata + else if (TypeLoaderEnvironment.TryGetTypeReferenceForNamedType(genericDefinitionHandle, out reader, out typeRefHandle)) + { + typeRefHandle.GetFullName(reader, out name, out enclosingDummy, out nsName); + assemblyName = typeRefHandle.GetContainingModuleName(reader); + } + else +#endif + { + name = genericDefinitionHandle.LowLevelToStringRawEETypeAddress(); + nsName = ""; + assemblyName = "?"; + } + } + + public string NamespaceForDiagnostics + { + get + { + string name, nsName, assemblyName; + GetTypeNameHelper(out name, out nsName, out assemblyName); + return nsName; + } + } + + public string NameForDiagnostics + { + get + { + string name, nsName, assemblyName; + GetTypeNameHelper(out name, out nsName, out assemblyName); + return name; + } + } + + public string DiagnosticModuleName + { + get + { + string name, nsName, assemblyName; + GetTypeNameHelper(out name, out nsName, out assemblyName); + return assemblyName; + } + } + +#if DEBUG + private string _cachedToString; + + public override string ToString() + { + if (_cachedToString != null) + return _cachedToString; + + StringBuilder sb = new StringBuilder(); + + if (!_genericTypeDefinition.IsNull()) + sb.Append(_genericTypeDefinition.LowLevelToString()); + else if (!RuntimeTypeHandle.IsNull()) + sb.Append(RuntimeTypeHandle.LowLevelToString()); + + if (!Instantiation.IsNull) + { + for (int i = 0; i < Instantiation.Length; i++) + { + sb.Append(i == 0 ? "[" : ", "); + sb.Append(Instantiation[i].ToString()); + } + if (Instantiation.Length > 0) sb.Append(']'); + } + + _cachedToString = sb.ToString(); + + return _cachedToString; + } +#endif + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/ThrowHelper.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/ThrowHelper.cs new file mode 100644 index 00000000000000..9188b7f7703bc7 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/ThrowHelper.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using CoreLibThrow = Internal.Runtime.CompilerHelpers.ThrowHelpers; + +namespace Internal.TypeSystem +{ + // This implementation forwards to the throw helpers targeted by the compiler in CoreLib. + // That way we can share the exception string resources. + public static partial class ThrowHelper + { + private static void ThrowTypeLoadException(ExceptionStringID id, string typeName, string assemblyName, string messageArg) + { + CoreLibThrow.ThrowTypeLoadExceptionWithArgument(id, typeName, assemblyName, messageArg); + } + + private static void ThrowTypeLoadException(ExceptionStringID id, string typeName, string assemblyName) + { + CoreLibThrow.ThrowTypeLoadException(id, typeName, assemblyName); + } + + public static void ThrowMissingMethodException(TypeDesc owningType, string methodName, MethodSignature signature) + { + CoreLibThrow.ThrowMissingMethodException(ExceptionStringID.MissingMethod, Format.Method(owningType, methodName, signature)); + } + + public static void ThrowMissingFieldException(TypeDesc owningType, string fieldName) + { + CoreLibThrow.ThrowMissingFieldException(ExceptionStringID.MissingField, Format.Field(owningType, fieldName)); + } + + public static void ThrowFileNotFoundException(ExceptionStringID id, string fileName) + { + CoreLibThrow.ThrowFileNotFoundException(id, fileName); + } + + public static void ThrowInvalidProgramException() + { + CoreLibThrow.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramDefault); + } + + public static void ThrowInvalidProgramException(ExceptionStringID id, MethodDesc method) + { + CoreLibThrow.ThrowInvalidProgramExceptionWithArgument(id, Format.Method(method)); + } + + public static void ThrowBadImageFormatException() + { + CoreLibThrow.ThrowBadImageFormatException(ExceptionStringID.BadImageFormatGeneric); + } + + private static partial class Format + { + public static string OwningModule(TypeDesc type) + { + if (type is NoMetadata.NoMetadataType) + return ((NoMetadata.NoMetadataType)type).DiagnosticModuleName; + + return Module((type as MetadataType)?.Module); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeDesc.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeDesc.Runtime.cs new file mode 100644 index 00000000000000..8d92aaafbf825a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeDesc.Runtime.cs @@ -0,0 +1,265 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using Internal.TypeSystem; +using Internal.Runtime.Augments; +using Internal.Runtime.TypeLoader; +using Debug = System.Diagnostics.Debug; +using Internal.NativeFormat; +using System.Collections.Generic; +using Internal.TypeSystem.NoMetadata; +using System.Reflection.Runtime.General; + +namespace Internal.TypeSystem +{ + public abstract partial class TypeDesc + { + private RuntimeTypeHandle _runtimeTypeHandle; + public RuntimeTypeHandle RuntimeTypeHandle + { + get + { + return _runtimeTypeHandle; + } + } + + /// + /// Setter for RuntimeTypeHandle. Seperate from normal property as all uses should be done with great care. + /// Must not be set with partially constructed type handles + /// + public void SetRuntimeTypeHandleUnsafe(RuntimeTypeHandle runtimeTypeHandle) + { + Debug.Assert(!runtimeTypeHandle.IsNull()); + Debug.Assert(_runtimeTypeHandle.IsNull() || runtimeTypeHandle.Equals(_runtimeTypeHandle)); + Debug.Assert(runtimeTypeHandle.GetHashCode() == GetHashCode()); + _runtimeTypeHandle = runtimeTypeHandle; + } + + /// + /// Get the RuntimeTypeHandle if possible and return it. Otherwise, return a null RuntimeTypeHandle + /// + public RuntimeTypeHandle GetRuntimeTypeHandle() + { + RetrieveRuntimeTypeHandleIfPossible(); + return RuntimeTypeHandle; + } + + private NativeLayoutFieldDesc[] _nativeLayoutFields; + /// + /// The native layout fields of a type. This property is for the use of the NativeLayoutFieldAlgorithm, + /// DefType.GetFieldByNativeLayoutOrdinal, TypeBuilderState.PrepareStaticGCLayout and DefType.GetDiagnosticFields + /// only. Other uses should use the more general purpose GetFields api or similar. + /// + internal NativeLayoutFieldDesc[] NativeLayoutFields + { + get + { + return _nativeLayoutFields; + } + set + { + Debug.Assert(_nativeLayoutFields == null); + Debug.Assert(value != null); + _nativeLayoutFields = value; + } + } + + internal TypeBuilderState TypeBuilderState { get; set; } + +#if DEBUG + public string DebugName { get; set; } +#endif + + // Todo: This is looking up the hierarchy to DefType and ParameterizedType. It should really + // call a virtual or an outside type to handle those parts + internal bool RetrieveRuntimeTypeHandleIfPossible() + { + TypeDesc type = this; + if (!type.RuntimeTypeHandle.IsNull()) + return true; + + TypeBuilderState state = GetTypeBuilderStateIfExist(); + if (state != null && state.AttemptedAndFailedToRetrieveTypeHandle) + return false; + + if (type is DefType) + { + DefType typeAsDefType = (DefType)type; + + TypeDesc typeDefinition = typeAsDefType.GetTypeDefinition(); + RuntimeTypeHandle typeDefHandle = typeDefinition.RuntimeTypeHandle; + if (typeDefHandle.IsNull()) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + NativeFormat.NativeFormatType mdType = typeDefinition as NativeFormat.NativeFormatType; + if (mdType != null) + { + // Look up the runtime type handle in the module metadata + if (TypeLoaderEnvironment.Instance.TryGetNamedTypeForMetadata(new QTypeDefinition(mdType.MetadataReader, mdType.Handle), out typeDefHandle)) + { + typeDefinition.SetRuntimeTypeHandleUnsafe(typeDefHandle); + } + } +#endif +#if ECMA_METADATA_SUPPORT + Ecma.EcmaType ecmaType = typeDefinition as Ecma.EcmaType; + if (ecmaType != null) + { + // Look up the runtime type handle in the module metadata + if (TypeLoaderEnvironment.Instance.TryGetNamedTypeForMetadata(new QTypeDefinition(ecmaType.MetadataReader, ecmaType.Handle), out typeDefHandle)) + { + typeDefinition.SetRuntimeTypeHandleUnsafe(typeDefHandle); + } + } +#endif + } + + if (!typeDefHandle.IsNull()) + { + Instantiation instantiation = typeAsDefType.Instantiation; + + if ((instantiation.Length > 0) && !typeAsDefType.IsGenericDefinition) + { + // Generic type. First make sure we have type handles for the arguments, then check + // the instantiation. + bool argumentsRegistered = true; + bool arrayArgumentsFound = false; + for (int i = 0; i < instantiation.Length; i++) + { + if (!instantiation[i].RetrieveRuntimeTypeHandleIfPossible()) + { + argumentsRegistered = false; + arrayArgumentsFound = arrayArgumentsFound || (instantiation[i] is ArrayType); + } + } + + RuntimeTypeHandle rtth; + + // If at least one of the arguments is not known to the runtime, we take a slower + // path to compare the current type we need a handle for to the list of generic + // types statically available, by loading them as DefTypes and doing a DefType comparaison + if ((argumentsRegistered && TypeLoaderEnvironment.Instance.TryLookupConstructedGenericTypeForComponents(new TypeLoaderEnvironment.HandleBasedGenericTypeLookup(typeAsDefType), out rtth)) || + (arrayArgumentsFound && TypeLoaderEnvironment.Instance.TryLookupConstructedGenericTypeForComponents(new TypeLoaderEnvironment.DefTypeBasedGenericTypeLookup(typeAsDefType), out rtth))) + { + typeAsDefType.SetRuntimeTypeHandleUnsafe(rtth); + return true; + } + } + else + { + // Nongeneric, or generic type def types are just the type handle of the type definition as found above + type.SetRuntimeTypeHandleUnsafe(typeDefHandle); + return true; + } + } + } + else if (type is ParameterizedType) + { + ParameterizedType typeAsParameterType = (ParameterizedType)type; + + if (typeAsParameterType.ParameterType.RetrieveRuntimeTypeHandleIfPossible()) + { + RuntimeTypeHandle rtth; + if ((type is ArrayType && + (TypeLoaderEnvironment.Instance.TryGetArrayTypeForElementType_LookupOnly(typeAsParameterType.ParameterType.RuntimeTypeHandle, type.IsMdArray, type.IsMdArray ? ((ArrayType)type).Rank : -1, out rtth) || + TypeLoaderEnvironment.Instance.TryGetArrayTypeHandleForNonDynamicArrayTypeFromTemplateTable(type as ArrayType, out rtth))) + || + (type is PointerType && TypeSystemContext.PointerTypesCache.TryGetValue(typeAsParameterType.ParameterType.RuntimeTypeHandle, out rtth)) + || + (type is ByRefType && TypeSystemContext.ByRefTypesCache.TryGetValue(typeAsParameterType.ParameterType.RuntimeTypeHandle, out rtth))) + { + typeAsParameterType.SetRuntimeTypeHandleUnsafe(rtth); + return true; + } + } + } + else if (type is SignatureVariable) + { + // SignatureVariables do not have RuntimeTypeHandles + } + else + { + Debug.Assert(false); + } + + // Make a note on the type build state that we have attempted to retrieve RuntimeTypeHandle but there is not one + GetOrCreateTypeBuilderState().AttemptedAndFailedToRetrieveTypeHandle = true; + + return false; + } + + internal TypeBuilderState GetTypeBuilderStateIfExist() + { + return (TypeBuilderState)TypeBuilderState; + } + + // + // Get existing type builder state. This method should be only called during final phase of type building. + // + internal TypeBuilderState GetTypeBuilderState() + { + TypeBuilderState state = (TypeBuilderState)TypeBuilderState; + Debug.Assert(state != null); + return state; + } + + // + // Get or create existing type builder state. This method should not be called during final phase of type building. + // + internal TypeBuilderState GetOrCreateTypeBuilderState() + { + TypeBuilderState state = (TypeBuilderState)TypeBuilderState; + if (state == null) + { + state = new TypeBuilderState(this); + TypeBuilderState = state; + Context.RegisterTypeForTypeSystemStateFlushing(this); + } + return state; + } + + /// Parse the native layout to ensure that the type has proper base type setup. + /// This is used to generalize out some behavior of NoMetadataTypes which actually use this information + internal virtual void ParseBaseType(NativeLayoutInfoLoadContext nativeLayoutInfoLoadContext, NativeParser baseTypeParser) + { + return; + } + + internal TypeDesc ComputeTemplate(bool templateRequired = true) + { + return ComputeTemplate(GetOrCreateTypeBuilderState(), templateRequired); + } + + internal TypeDesc ComputeTemplate(TypeBuilderState state, bool templateRequired = true) + { + TypeDesc templateType = state.TemplateType; + + if (templateRequired && (templateType == null)) + { + throw new TypeBuilder.MissingTemplateException(); + } + + return templateType; + } + + internal bool IsTemplateUniversal() + { + TypeDesc templateType = ComputeTemplate(false); + if (templateType == null) + return false; + else + return templateType.IsCanonicalSubtype(CanonicalFormKind.Universal); + } + + internal bool IsTemplateCanonical() + { + TypeDesc templateType = ComputeTemplate(false); + if (templateType == null) + return false; + else + return !templateType.IsCanonicalSubtype(CanonicalFormKind.Universal); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs new file mode 100644 index 00000000000000..6667763770f481 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs @@ -0,0 +1,656 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; + +using System.Reflection.Runtime.General; + +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; +using Internal.Runtime.TypeLoader; +using Internal.TypeSystem.NativeFormat; +using Internal.TypeSystem.NoMetadata; +using Internal.Metadata.NativeFormat; +using Internal.NativeFormat; + +namespace Internal.TypeSystem +{ + public abstract partial class TypeSystemContext + { + internal TemplateLocator TemplateLookup => new TemplateLocator(); + + internal class RuntimeTypeHandleToParameterTypeRuntimeTypeHandleHashtable : LockFreeReaderHashtableOfPointers + { + protected override bool CompareKeyToValue(RuntimeTypeHandle key, RuntimeTypeHandle value) + { + unsafe + { + return value.ToEETypePtr()->RelatedParameterType->ToRuntimeTypeHandle().Equals(key); + } + } + + protected override bool CompareValueToValue(RuntimeTypeHandle value1, RuntimeTypeHandle value2) + { + return value1.Equals(value2); + } + + protected override RuntimeTypeHandle ConvertIntPtrToValue(IntPtr pointer) + { + unsafe + { + return ((MethodTable*)pointer.ToPointer())->ToRuntimeTypeHandle(); + } + } + + protected override IntPtr ConvertValueToIntPtr(RuntimeTypeHandle value) + { + return value.ToIntPtr(); + } + + protected override RuntimeTypeHandle CreateValueFromKey(RuntimeTypeHandle key) + { + throw new NotSupportedException(); + } + + protected override int GetKeyHashCode(RuntimeTypeHandle key) + { + return key.GetHashCode(); + } + + protected override int GetValueHashCode(RuntimeTypeHandle value) + { + unsafe + { + return (int)value.ToEETypePtr()->RelatedParameterType->HashCode; + } + } + } + + internal static RuntimeTypeHandleToParameterTypeRuntimeTypeHandleHashtable[] s_ArrayTypesCaches = new RuntimeTypeHandleToParameterTypeRuntimeTypeHandleHashtable[MDArray.MaxRank + 1]; + /// + /// Cache of array types created by the builder to prevent duplication + /// + internal static RuntimeTypeHandleToParameterTypeRuntimeTypeHandleHashtable GetArrayTypesCache(bool isMdArray, int rank) + { + if (isMdArray && (rank < MDArray.MinRank || rank > MDArray.MaxRank)) + throw new PlatformNotSupportedException(); + + if (!isMdArray) + rank = 0; + + if (s_ArrayTypesCaches[rank] == null) + s_ArrayTypesCaches[rank] = new RuntimeTypeHandleToParameterTypeRuntimeTypeHandleHashtable(); + return s_ArrayTypesCaches[rank]; + } + + /// + /// Cache of pointer types created by the builder to prevent duplication + /// + internal static RuntimeTypeHandleToParameterTypeRuntimeTypeHandleHashtable PointerTypesCache { get; } = + new RuntimeTypeHandleToParameterTypeRuntimeTypeHandleHashtable(); + + /// + /// Cache of ByRef types created by the builder to prevent duplication + /// + internal static RuntimeTypeHandleToParameterTypeRuntimeTypeHandleHashtable ByRefTypesCache { get; } = + new RuntimeTypeHandleToParameterTypeRuntimeTypeHandleHashtable(); + + public Instantiation ResolveRuntimeTypeHandles(RuntimeTypeHandle[] runtimeTypeHandles) + { + TypeDesc[] TypeDescs = new TypeDesc[runtimeTypeHandles.Length]; + for (int i = 0; i < runtimeTypeHandles.Length; i++) + TypeDescs[i] = ResolveRuntimeTypeHandle(runtimeTypeHandles[i]); + return new Instantiation(TypeDescs); + } + + // This dictionary is in every scenario - create it eagerly + private LowLevelDictionary _runtimeTypeHandleResolutionCache = + new LowLevelDictionary(); + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + internal TypeDesc GetTypeDescFromQHandle(QTypeDefinition qTypeDefinition) + { +#if ECMA_METADATA_SUPPORT + if (qTypeDefinition.IsNativeFormatMetadataBased) +#endif + { + MetadataReader nativeFormatMetadataReader = qTypeDefinition.NativeFormatReader; + TypeDefinitionHandle typeDefinitionHandle = qTypeDefinition.NativeFormatHandle; + NativeFormatModuleInfo module = ModuleList.Instance.GetModuleInfoForMetadataReader(nativeFormatMetadataReader); + NativeFormatMetadataUnit metadataUnit = ResolveMetadataUnit(module); + NativeFormatType nativeFormatType = (NativeFormatType)metadataUnit.GetType(typeDefinitionHandle); + return nativeFormatType; + } +#if ECMA_METADATA_SUPPORT + else if (qTypeDefinition.IsEcmaFormatMetadataBased) + { + EcmaModuleInfo module = ModuleList.Instance.GetModuleInfoForMetadataReader(qTypeDefinition.EcmaFormatReader); + Ecma.EcmaModule ecmaModule = ResolveEcmaModule(module); + Ecma.EcmaType ecmaType = (Ecma.EcmaType)ecmaModule.GetType(qTypeDefinition.EcmaFormatHandle); + return ecmaType; + } +#endif + return null; + } +#endif + + // Helper routine for ResolveRuntimeTypeHandle, used to handle lookups which may result in a metadata based type. + private TypeDesc TryGetMetadataBasedTypeFromRuntimeTypeHandle_Uncached(RuntimeTypeHandle rtth) + { +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + QTypeDefinition qTypeDefinition; + if (TypeLoaderEnvironment.Instance.TryGetMetadataForNamedType(rtth, out qTypeDefinition)) + { + return GetTypeDescFromQHandle(qTypeDefinition); + } +#endif + return null; + } + + public TypeDesc ResolveRuntimeTypeHandle(RuntimeTypeHandle rtth) + { + TypeDesc returnedType; + if (_runtimeTypeHandleResolutionCache.TryGetValue(rtth, out returnedType)) + return returnedType; + + if (rtth.Equals(CanonType.RuntimeTypeHandle)) + { + returnedType = CanonType; + } + else if (rtth.Equals(UniversalCanonType.RuntimeTypeHandle)) + { + returnedType = UniversalCanonType; + } + else if (RuntimeAugments.IsGenericTypeDefinition(rtth)) + { + returnedType = TryGetMetadataBasedTypeFromRuntimeTypeHandle_Uncached(rtth); + if (returnedType == null) + { + unsafe + { + TypeDesc[] genericParameters = new TypeDesc[rtth.ToEETypePtr()->GenericArgumentCount]; + for (int i = 0; i < genericParameters.Length; i++) + { + genericParameters[i] = GetSignatureVariable(i, false); + } + + returnedType = new NoMetadataType(this, rtth, null, new Instantiation(genericParameters), rtth.GetHashCode()); + } + } + } + else if (RuntimeAugments.IsGenericType(rtth)) + { + RuntimeTypeHandle typeDefRuntimeTypeHandle; + RuntimeTypeHandle[] genericArgRuntimeTypeHandles; + typeDefRuntimeTypeHandle = RuntimeAugments.GetGenericInstantiation(rtth, out genericArgRuntimeTypeHandles); + + DefType typeDef = (DefType)ResolveRuntimeTypeHandle(typeDefRuntimeTypeHandle); + Instantiation genericArgs = ResolveRuntimeTypeHandles(genericArgRuntimeTypeHandles); + returnedType = ResolveGenericInstantiation(typeDef, genericArgs); + } + else if (RuntimeAugments.IsArrayType(rtth)) + { + RuntimeTypeHandle elementTypeHandle = RuntimeAugments.GetRelatedParameterTypeHandle(rtth); + TypeDesc elementType = ResolveRuntimeTypeHandle(elementTypeHandle); + unsafe + { + if (rtth.ToEETypePtr()->IsSzArray) + returnedType = GetArrayType(elementType); + else + returnedType = GetArrayType(elementType, rtth.ToEETypePtr()->ArrayRank); + } + } + else if (RuntimeAugments.IsUnmanagedPointerType(rtth)) + { + RuntimeTypeHandle targetTypeHandle = RuntimeAugments.GetRelatedParameterTypeHandle(rtth); + TypeDesc targetType = ResolveRuntimeTypeHandle(targetTypeHandle); + returnedType = GetPointerType(targetType); + } + else if (RuntimeAugments.IsByRefType(rtth)) + { + RuntimeTypeHandle targetTypeHandle = RuntimeAugments.GetRelatedParameterTypeHandle(rtth); + TypeDesc targetType = ResolveRuntimeTypeHandle(targetTypeHandle); + returnedType = GetByRefType(targetType); + } + else + { + returnedType = TryGetMetadataBasedTypeFromRuntimeTypeHandle_Uncached(rtth); + if (returnedType == null) + { + returnedType = new NoMetadataType(this, rtth, null, Instantiation.Empty, rtth.GetHashCode()); + } + } + + // We either retrieved an existing DefType that is already registered with the runtime + // or one that is not associated with an MethodTable yet. If it's not associated, associate it. + if (returnedType.RuntimeTypeHandle.IsNull()) + { + TypeBuilderState state = returnedType.GetTypeBuilderStateIfExist(); + bool skipStoringRuntimeTypeHandle = false; + + // If we've already attempted to lookup and failed to retrieve this type handle, we + // may have already decided to create a new one. In that case, do not attempt to abort + // that creation process as it may have already begun the process of type creation + if (state != null && state.AttemptedAndFailedToRetrieveTypeHandle) + skipStoringRuntimeTypeHandle = true; + + if (!skipStoringRuntimeTypeHandle) + returnedType.SetRuntimeTypeHandleUnsafe(rtth); + } + + _runtimeTypeHandleResolutionCache.Add(rtth, returnedType); + + return returnedType.WithDebugName(); + } + + private struct GenericTypeInstanceKey : IEquatable + { + private DefType _typeDefinition; + private Instantiation _instantiation; + private int _hashCode; + + public GenericTypeInstanceKey(DefType typeDefinition, Instantiation instantiation) + { + _typeDefinition = typeDefinition; + _instantiation = instantiation; + + _hashCode = instantiation.ComputeGenericInstanceHashCode(typeDefinition.GetHashCode()); + } + + public bool Equals(GenericTypeInstanceKey other) + { + if (_typeDefinition != other._typeDefinition) + return false; + + Debug.Assert(_instantiation.Length == other._instantiation.Length); + + for (int i = 0; i < _instantiation.Length; i++) + if (_instantiation[i] != other._instantiation[i]) + return false; + + return true; + } + + public override bool Equals(object obj) + { + if (!(obj is GenericTypeInstanceKey)) + return false; + + return Equals((GenericTypeInstanceKey)obj); + } + + public override int GetHashCode() + { + return _hashCode; + } + } + + private struct RuntimeMethodKey + { + private bool _unboxingStub; + private DefType _owningType; + private MethodNameAndSignature _methodNameAndSignature; + private int _hashCode; + + public RuntimeMethodKey(bool unboxingStub, DefType owningType, MethodNameAndSignature nameAndSignature) + { + _unboxingStub = unboxingStub; + _owningType = owningType; + _methodNameAndSignature = nameAndSignature; + + _hashCode = TypeHashingAlgorithms.ComputeMethodHashCode(owningType.GetHashCode(), TypeHashingAlgorithms.ComputeNameHashCode(nameAndSignature.Name)); + } + + public class RuntimeMethodKeyHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(RuntimeMethodKey key) + { + return key._hashCode; + } + + protected override int GetValueHashCode(MethodDesc value) + { + return value.GetHashCode(); + } + + protected override bool CompareKeyToValue(RuntimeMethodKey key, MethodDesc value) + { + if (value is RuntimeMethodDesc) + { + RuntimeMethodDesc runtimeMethod = (RuntimeMethodDesc)value; + + if (key._unboxingStub != runtimeMethod.UnboxingStub) + return false; + + if (!key._owningType.Equals(runtimeMethod.OwningType)) + return false; + + if (!key._methodNameAndSignature.Equals(runtimeMethod.NameAndSignature)) + return false; + + return true; + } + else + { + // Only RuntimeMethodDesc can be an unboxing stub + if (key._unboxingStub) + return false; + + if (!key._owningType.Equals(value.OwningType)) + return false; + + if (!key._methodNameAndSignature.Equals(value.NameAndSignature)) + return false; + + return true; + } + } + + protected override bool CompareValueToValue(MethodDesc value1, MethodDesc value2) + { + if (value1 is RuntimeMethodDesc || value2 is RuntimeMethodDesc) + { + // If one is a RuntimeMethodDesc, they must both be to be equals + if (!(value1 is RuntimeMethodDesc) || !(value2 is RuntimeMethodDesc)) + { + return false; + } + if (((RuntimeMethodDesc)value1).UnboxingStub != ((RuntimeMethodDesc)value2).UnboxingStub) + return false; + + if (!value1.OwningType.Equals(value2.OwningType)) + return false; + + if (!value1.NameAndSignature.Equals(value2.NameAndSignature)) + return false; + + return true; + } + else + { + // Allocation of non RuntimeMethodDescs is not handled by this context, so we can just + // do a reference equality check here. + return value1 == value2; + } + } + + protected override MethodDesc CreateValueFromKey(RuntimeMethodKey key) + { + // unboxing stubs are always RuntimeMethodDesc + if (!key._unboxingStub) + { + // Instantiated Types always get their methods through GetMethodForInstantiatedType + if (key._owningType is InstantiatedType) + { + MethodDesc typicalMethod = key._owningType.Context.ResolveRuntimeMethod(key._unboxingStub, (DefType)key._owningType.GetTypeDefinition(), key._methodNameAndSignature, IntPtr.Zero, false); + return typicalMethod.Context.GetMethodForInstantiatedType(typicalMethod, (InstantiatedType)key._owningType); + } + + // Otherwise, just check to see if there is a method discoverable via GetMethods + foreach (MethodDesc potentialMethod in key._owningType.GetMethods()) + { + if (CompareKeyToValue(key, potentialMethod)) + { + return potentialMethod; + } + } + } + else + { + // We should only have unboxing stubs on value types + Debug.Assert(key._owningType.IsValueType); + } + + return new RuntimeMethodDesc(key._unboxingStub, key._owningType, key._methodNameAndSignature, key._hashCode); + } + } + } + + private RuntimeMethodKey.RuntimeMethodKeyHashtable _runtimeMethods; + + internal MethodDesc ResolveRuntimeMethod(bool unboxingStub, DefType owningType, MethodNameAndSignature nameAndSignature, IntPtr functionPointer, bool usgFunctionPointer) + { + if (_runtimeMethods == null) + _runtimeMethods = new RuntimeMethodKey.RuntimeMethodKeyHashtable(); + + MethodDesc retVal = _runtimeMethods.GetOrCreateValue(new RuntimeMethodKey(unboxingStub, owningType, nameAndSignature)); + + if (functionPointer != IntPtr.Zero) + { + retVal.SetFunctionPointer(functionPointer, usgFunctionPointer); + } + + return retVal; + } + + private LowLevelDictionary _genericTypeInstances; + + /// + /// Get a DefType that is the generic instantiation of an open generic type over instantiation arguments + /// This looks like a rename of GetInstantiatedType, but isn't because the corert GetInstantiatedType + /// relies on typeDef being a MetadataType, whereas this permits non-metadata types. + /// + public DefType ResolveGenericInstantiation(DefType typeDef, Instantiation arguments) + { + Debug.Assert(typeDef.Instantiation.IsNull || typeDef.Instantiation.Length == arguments.Length); + + MetadataType typeAsMetadataType = typeDef as MetadataType; + + if (typeAsMetadataType != null) + return GetInstantiatedType(typeAsMetadataType, arguments); + + if (_genericTypeInstances == null) + _genericTypeInstances = new LowLevelDictionary(); + + GenericTypeInstanceKey key = new GenericTypeInstanceKey(typeDef, arguments); + + DefType result; + if (!_genericTypeInstances.TryGetValue(key, out result)) + { + NoMetadataType nmTypeDef = (NoMetadataType)typeDef; + Debug.Assert(RuntimeAugments.IsGenericTypeDefinition(nmTypeDef.RuntimeTypeHandle)); + result = new NoMetadataType(this, nmTypeDef.RuntimeTypeHandle, nmTypeDef, arguments, key.GetHashCode()); + + _genericTypeInstances.Add(key, result); + } + + return result.WithDebugName(); + } + + /// + /// Find a method based on owner type and nativelayout name, method instantiation, and signature. + /// + public MethodDesc ResolveGenericMethodInstantiation(bool unboxingStub, DefType owningType, MethodNameAndSignature nameAndSignature, Instantiation methodInstantiation, IntPtr functionPointer, bool usgFunctionPointer) + { + var uninstantiatedMethod = ResolveRuntimeMethod(unboxingStub, owningType, nameAndSignature, IntPtr.Zero, false); + + MethodDesc returnedMethod; + if (methodInstantiation.IsNull || (methodInstantiation.Length == 0)) + { + returnedMethod = uninstantiatedMethod; + } + else + { + returnedMethod = GetInstantiatedMethod(uninstantiatedMethod, methodInstantiation); + } + + if (functionPointer != IntPtr.Zero) + { + returnedMethod.SetFunctionPointer(functionPointer, usgFunctionPointer); + } + + return returnedMethod; + } + +#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING + public class ModuleToMetadataUnitHashtable : LockFreeReaderHashtable + { + private TypeSystemContext _context; + + public ModuleToMetadataUnitHashtable(TypeSystemContext context) + { + _context = context; + } + + protected override int GetKeyHashCode(NativeFormatModuleInfo key) + { + return key.GetHashCode(); + } + + protected override int GetValueHashCode(NativeFormat.NativeFormatMetadataUnit value) + { + return value.RuntimeModuleInfo.GetHashCode(); + } + + protected override bool CompareKeyToValue(NativeFormatModuleInfo key, NativeFormat.NativeFormatMetadataUnit value) + { + return key == value.RuntimeModuleInfo; + } + + protected override bool CompareValueToValue(NativeFormat.NativeFormatMetadataUnit value1, NativeFormat.NativeFormatMetadataUnit value2) + { + return value1.RuntimeModuleInfo == value2.RuntimeModuleInfo; + } + + protected override NativeFormat.NativeFormatMetadataUnit CreateValueFromKey(NativeFormatModuleInfo key) + { + return new NativeFormat.NativeFormatMetadataUnit(_context, key, key.MetadataReader); + } + } + + private ModuleToMetadataUnitHashtable _metadataUnits = null; + + internal NativeFormat.NativeFormatMetadataUnit ResolveMetadataUnit(NativeFormatModuleInfo module) + { + if (_metadataUnits == null) + _metadataUnits = new ModuleToMetadataUnitHashtable(this); + + return _metadataUnits.GetOrCreateValue(module); + } +#endif + + /// + /// Returns an estimate of the number of objects tracked by this context + /// + public virtual int LoadFactor + { + get + { + int loadFactor = _arrayTypes.Count; + loadFactor += _byRefTypes.Count; + loadFactor += _fieldForInstantiatedTypes.Count; + loadFactor += _instantiatedMethods.Count; + loadFactor += _instantiatedTypes.Count; + loadFactor += _methodForInstantiatedTypes.Count; + loadFactor += _pointerTypes.Count; + loadFactor += _signatureVariables.Count; + + if (_runtimeMethods != null) + loadFactor += _runtimeMethods.Count; + + if (_genericTypeInstances != null) + loadFactor += _genericTypeInstances.Count; + + // TODO: This doesn't track nongeneric types and members. Does that significantly affect the results? + + return loadFactor; + } + } + + private LowLevelList _typesToFlushTypeSystemStateFrom; + + /// + /// Register the types that will get their attached TypeSystemState flushed if the + /// type system context is recycled + /// + internal void RegisterTypeForTypeSystemStateFlushing(TypeDesc type) + { + if (_typesToFlushTypeSystemStateFrom == null) + _typesToFlushTypeSystemStateFrom = new LowLevelList(); + _typesToFlushTypeSystemStateFrom.Add(type); + } + + /// + /// Remove the type system contexts from every type in this context that has one. + /// This function must be called before a TypeSystemContext is recycled + /// + internal void FlushTypeBuilderStates() + { + if (_typesToFlushTypeSystemStateFrom != null) + { + for (int i = 0; i < _typesToFlushTypeSystemStateFrom.Count; i++) + { + _typesToFlushTypeSystemStateFrom[i].TypeBuilderState = null; + } + } + _typesToFlushTypeSystemStateFrom = null; + } + +#if ECMA_METADATA_SUPPORT + public class ModuleToEcmaModuleHashtable : LockFreeReaderHashtable + { + private TypeSystemContext _context; + + public ModuleToEcmaModuleHashtable(TypeSystemContext context) + { + _context = context; + } + + protected override int GetKeyHashCode(EcmaModuleInfo key) + { + return key.GetHashCode(); + } + + protected override int GetValueHashCode(Internal.TypeSystem.Ecma.EcmaModule value) + { + return value.RuntimeModuleInfo.GetHashCode(); + } + + protected override bool CompareKeyToValue(EcmaModuleInfo key, Internal.TypeSystem.Ecma.EcmaModule value) + { + return key == value.RuntimeModuleInfo; + } + + protected override bool CompareValueToValue(Internal.TypeSystem.Ecma.EcmaModule value1, Internal.TypeSystem.Ecma.EcmaModule value2) + { + return value1.RuntimeModuleInfo == value2.RuntimeModuleInfo; + } + + protected override Internal.TypeSystem.Ecma.EcmaModule CreateValueFromKey(EcmaModuleInfo key) + { + Internal.TypeSystem.Ecma.EcmaModule result = new Internal.TypeSystem.Ecma.EcmaModule(_context, key.PE, key.MetadataReader, null, null); + result.SetRuntimeModuleInfoUNSAFE(key); + return result; + } + } + + private ModuleToEcmaModuleHashtable _ecmaModules = null; + + internal Internal.TypeSystem.Ecma.EcmaModule ResolveEcmaModule(EcmaModuleInfo module) + { + if (_ecmaModules == null) + _ecmaModules = new ModuleToEcmaModuleHashtable(this); + + return _ecmaModules.GetOrCreateValue(module); + } +#endif // ECMA_METADATA_SUPPORT + } + + internal static partial class TypeNameHelper + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T WithDebugName(this T type) where T : TypeDesc + { +#if DEBUG + if (type.DebugName == null) type.DebugName = type.ToString(); +#endif + return type; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Resources/Strings.resx b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Resources/Strings.resx new file mode 100644 index 00000000000000..949e8adc2fbadc --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Resources/Strings.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Hashtable's capacity overflowed and went negative. Check load factor, capacity and the current size of the table. + + + An item with the same key has already been added. + + + Cannot load assembly '{0}'. No metadata found for this assembly. + + + Cannot load assembly '{0}'. The assembly exists but its version {1} is lower than the requested version {2}. + + diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj new file mode 100644 index 00000000000000..e41867788ef5a2 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -0,0 +1,376 @@ + + + $(NoWarn),CS1574 + TYPE_LOADER_IMPLEMENTATION;$(DefineConstants) + TYPE_LOADER_TRACE;$(DefineConstants) + GVM_RESOLUTION_TRACE;$(DefineConstants) + CCCONVERTER_TRACE;$(DefineConstants) + GENERICS_FORCE_USG;$(DefineConstants) + + + + + + + $(CompilerCommonPath)\Internal\NativeFormat + + + + + + + + + + MethodTable.cs + + + MethodTable.Constants.cs + + + MappingTableFlags.cs + + + EETypeBuilderHelpers.cs + + + RuntimeConstants.cs + + + TransitionBlock.cs + + + LowLevelList.cs + + + MetadataBlob.cs + + + ArrayBuilder.cs + + + LowLevelDictionary.cs + + + NotImplemented.cs + + + + IntrinsicAttribute.cs + + + Internal\TypeSystem\ArrayType.Canon.cs + + + Internal\TypeSystem\ByRefType.Canon.cs + + + Internal\TypeSystem\CanonTypes.cs + + + Internal\TypeSystem\DefType.Canon.cs + + + Internal\TypeSystem\FunctionPointerType.Canon.cs + + + Internal\TypeSystem\GenericParameterDesc.Canon.cs + + + Internal\TypeSystem\InstantiatedMethod.Canon.cs + + + Internal\TypeSystem\InstantiatedType.Canon.cs + + + Internal\TypeSystem\MetadataType.Canon.cs + + + Internal\TypeSystem\MethodDesc.Canon.cs + + + Internal\TypeSystem\MethodForInstantiatedType.Canon.cs + + + Internal\TypeSystem\ParameterizedType.Canon.cs + + + Internal\TypeSystem\PointerType.Canon.cs + + + Internal\TypeSystem\SignatureVariable.Canon.cs + + + Internal\TypeSystem\StandardCanonicalizationAlgorithm.cs + + + Internal\TypeSystem\TypeDesc.Canon.cs + + + Internal\TypeSystem\TypeSystemContext.Canon.cs + + + Internal\TypeSystem\AlignmentHelper.cs + + + Internal\TypeSystem\ArrayOfTRuntimeInterfacesAlgorithm.cs + + + Internal\TypeSystem\ArrayType.cs + + + Internal\TypeSystem\BaseTypeRuntimeInterfacesAlgorithm.cs + + + Internal\TypeSystem\ByRefType.cs + + + Internal\TypeSystem\CastingHelper.cs + + + Internal\TypeSystem\ConstructedTypeRewritingHelpers.cs + + + Internal\TypeSystem\DefType.cs + + + Internal\TypeSystem\DefType.FieldLayout.cs + + + Internal\TypeSystem\FieldDesc.cs + + + Internal\TypeSystem\FieldDesc.FieldLayout.cs + + + Internal\TypeSystem\FieldForInstantiatedType.cs + + + Internal\TypeSystem\FieldLayoutAlgorithm.cs + + + Internal\TypeSystem\FunctionPointerType.cs + + + Internal\TypeSystem\GenericParameterDesc.cs + + + Internal\TypeSystem\IAssemblyDesc.cs + + + Internal\TypeSystem\IModuleResolver.cs + + + Internal\TypeSystem\InstantiatedMethod.cs + + + Internal\TypeSystem\InstantiatedType.cs + + + Internal\TypeSystem\InstantiatedType.Interfaces.cs + + + Internal\TypeSystem\InstantiatedType.MethodImpls.cs + + + Internal\TypeSystem\Instantiation.cs + + + Internal\TypeSystem\LayoutInt.cs + + + Internal\TypeSystem\Common\ExplicitLayoutValidator.cs + + + Internal\TypeSystem\MetadataType.cs + + + Internal\TypeSystem\MetadataType.Interfaces.cs + + + Internal\TypeSystem\MetadataType.MethodImpls.cs + + + Internal\TypeSystem\MethodDesc.cs + + + Internal\TypeSystem\MethodForInstantiatedType.cs + + + Internal\TypeSystem\ModuleDesc.cs + + + TypeSystem\Common\NotFoundBehavior.cs + + + TypeSystem\Common\ResolutionFailure.cs + + + Internal\TypeSystem\ParameterizedType.cs + + + Internal\TypeSystem\PointerType.cs + + + Internal\TypeSystem\RuntimeInterfacesAlgorithm.cs + + + Internal\TypeSystem\SignatureVariable.cs + + + Internal\TypeSystem\TargetArchitecture.cs + + + Internal\TypeSystem\TargetDetails.cs + + + Internal\TypeSystem\ThreadSafeFlags.cs + + + Internal\TypeSystem\TypeDesc.cs + + + Internal\TypeSystem\TypeDesc.Interfaces.cs + + + Internal\TypeSystem\TypeFlags.cs + + + Internal\TypeSystem\TypeSystemContext.cs + + + Internal\TypeSystem\TypeSystemEntity.cs + + + Internal\TypeSystem\ThrowHelper.Common.cs + + + Internal\TypeSystem\TypeSystemHelpers.cs + + + UniversalCanonLayoutAlgorithm.cs + + + LockFreeReaderHashtableOfPointers.cs + + + Internal\TypeSystem\Utilities\ExceptionTypeNameFormatter.cs + + + TypeNameFormatter.cs + + + Internal\TypeSystem\VirtualMethodAlgorithm.cs + + + Internal\TypeSystem\WellKnownType.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System\Runtime\CompilerServices\__BlockAllReflectionAttribute.cs + + + Internal\TypeSystem\Interop\MetadataType.Interop.cs + + + Internal\TypeSystem\Interop\InstantiatedType.Interop.cs + + + Internal\TypeSystem\Canon\CanonTypes.Interop.cs + + + Internal\TypeSystem\Interop\MarshalAsDescriptor.cs + + + + + Internal\TypeSystem\TypeDesc.ToString.cs + + + Internal\TypeSystem\FieldDesc.ToString.cs + + + Utilities\DebugNameFormatter.cs + + + Internal\TypeSystem\Common\GenericParameterDesc.Dummy.Diagnostic.cs + + + Internal\TypeSystem\Common\MethodDesc.Dummy.Diagnostic.cs + + + Internal\TypeSystem\Common\DefType.Dummy.Diagnostic.cs + + + diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System/Reflection/Runtime/General/QHandles.NativeFormat.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System/Reflection/Runtime/General/QHandles.NativeFormat.cs new file mode 100644 index 00000000000000..08ad466e41a0c5 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System/Reflection/Runtime/General/QHandles.NativeFormat.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Collection of "qualified handle" tuples. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Metadata.NativeFormat; +using Internal.Runtime.TypeLoader; + +namespace System.Reflection.Runtime.General +{ + + public partial struct QMethodDefinition + { + public QMethodDefinition(MetadataReader reader, MethodHandle handle) + { + _reader = reader; + _handle = ((Handle)handle).AsInt(); + } + + public MetadataReader NativeFormatReader { get { Debug.Assert(IsNativeFormatMetadataBased); return _reader as MetadataReader; } } + public MethodHandle NativeFormatHandle { get { Debug.Assert(IsNativeFormatMetadataBased); return _handle.AsHandle().ToMethodHandle(NativeFormatReader); } } + + public bool IsNativeFormatMetadataBased + { + get + { + return (_reader != null) && _reader is global::Internal.Metadata.NativeFormat.MetadataReader; + } + } + } + + public partial struct QTypeDefinition + { + public QTypeDefinition(MetadataReader reader, TypeDefinitionHandle handle) + { + _reader = reader; + _handle = ((Handle)handle).AsInt(); + } + + public MetadataReader NativeFormatReader { get { Debug.Assert(IsNativeFormatMetadataBased); return _reader as MetadataReader; } } + public TypeDefinitionHandle NativeFormatHandle { get { Debug.Assert(IsNativeFormatMetadataBased); return _handle.AsHandle().ToTypeDefinitionHandle(NativeFormatReader); } } + + public bool IsNativeFormatMetadataBased + { + get + { + return (_reader != null) && _reader is global::Internal.Metadata.NativeFormat.MetadataReader; + } + } + } + + public partial struct QTypeDefRefOrSpec + { + public QTypeDefRefOrSpec(MetadataReader reader, Handle handle, bool skipCheck = false) + { + if (!skipCheck) + { + if (!handle.IsTypeDefRefOrSpecHandle(reader)) + throw new BadImageFormatException(); + } + Debug.Assert(handle.IsTypeDefRefOrSpecHandle(reader)); + _reader = reader; + _handle = handle.ToIntToken(); + } + + public bool IsNativeFormatMetadataBased + { + get + { + return (_reader != null) && Reader is global::Internal.Metadata.NativeFormat.MetadataReader; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System/Reflection/Runtime/General/QHandles.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System/Reflection/Runtime/General/QHandles.cs new file mode 100644 index 00000000000000..2f47be124a0a28 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System/Reflection/Runtime/General/QHandles.cs @@ -0,0 +1,185 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Collection of "qualified handle" tuples. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Metadata.NativeFormat; +using Internal.Runtime.TypeLoader; + +namespace Internal.Reflection.Core +{ + public struct QScopeDefinition : IEquatable + { + public QScopeDefinition(MetadataReader reader, ScopeDefinitionHandle handle) + { + _reader = reader; + _handle = handle; + } + + public MetadataReader Reader { get { return _reader; } } + public ScopeDefinitionHandle Handle { get { return _handle; } } + public ScopeDefinition ScopeDefinition + { + get + { + return _handle.GetScopeDefinition(_reader); + } + } + + public override bool Equals(object obj) + { + if (!(obj is QScopeDefinition)) + return false; + return Equals((QScopeDefinition)obj); + } + + public bool Equals(QScopeDefinition other) + { + if (!(_reader == other._reader)) + return false; + if (!(_handle.Equals(other._handle))) + return false; + return true; + } + + public override int GetHashCode() + { + return _handle.GetHashCode(); + } + + private readonly MetadataReader _reader; + private readonly ScopeDefinitionHandle _handle; + } +} + +namespace System.Reflection.Runtime.General +{ + public struct QHandle : IEquatable + { + public QHandle(MetadataReader reader, Handle handle) + { + _reader = reader; + _handle = handle; + } + + public MetadataReader Reader { get { return _reader; } } + public Handle Handle { get { return _handle; } } + + public override bool Equals(object obj) + { + if (!(obj is QHandle)) + return false; + return Equals((QHandle)obj); + } + + public bool Equals(QHandle other) + { + if (!(_reader == other._reader)) + return false; + if (!(_handle.Equals(other._handle))) + return false; + return true; + } + + public override int GetHashCode() + { + return _handle.GetHashCode(); + } + + private readonly MetadataReader _reader; + private readonly Handle _handle; + } + + public partial struct QMethodDefinition + { + private QMethodDefinition(object reader, int token) + { + _reader = reader; + _handle = token; + } + + public static QMethodDefinition FromObjectAndInt(object reader, int token) + { + return new QMethodDefinition(reader, token); + } + + public object Reader { get { return _reader; } } + public int Token { get { return _handle; } } + + public bool IsValid { get { return _reader == null; } } + + public static QMethodDefinition Null => default; + + private readonly object _reader; + private readonly int _handle; + } + + public partial struct QTypeDefinition + { + public object Reader { get { return _reader; } } + public int Token { get { return _handle; } } + + public bool IsValid { get { return _reader == null; } } + + public static QTypeDefinition Null => default; + + private readonly object _reader; + private readonly int _handle; + } + + + public partial struct QTypeDefRefOrSpec + { + public object Reader { get { return _reader; } } + public int Handle { get { return _handle; } } + + public bool IsValid { get { return _reader == null; } } + + public static QTypeDefRefOrSpec Null => default; + + private readonly object _reader; + private readonly int _handle; + } + + public struct QGenericParameter : IEquatable + { + public QGenericParameter(MetadataReader reader, GenericParameterHandle handle) + { + _reader = reader; + _handle = handle; + } + + public MetadataReader Reader { get { return _reader; } } + public GenericParameterHandle Handle { get { return _handle; } } + + public override bool Equals(object obj) + { + if (!(obj is QGenericParameter)) + return false; + return Equals((QGenericParameter)obj); + } + + public bool Equals(QGenericParameter other) + { + if (!(_reader == other._reader)) + return false; + if (!(_handle.Equals(other._handle))) + return false; + return true; + } + + public override int GetHashCode() + { + return _handle.GetHashCode(); + } + + private readonly MetadataReader _reader; + private readonly GenericParameterHandle _handle; + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/README.md b/src/coreclr/nativeaot/Test.CoreLib/README.md new file mode 100644 index 00000000000000..878302f8092acb --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/README.md @@ -0,0 +1,23 @@ +# Test.CoreLib + +This is a minimum viable core library for test purposes. + +## How to use this + +Test.CoreLib gets built as part of the repo. After you build the repo: + +1. Compile your test program against Test.CoreLib + +``` +csc /noconfig /nostdlib Program.cs /r:\bin\Product\Windows_NT.x64.Debug\Test.CoreLib\Test.CoreLib.dll /out:repro.exe +``` + +2. Compile the IL with ILC + +Use ilc.dll that was built with the repo to compile the program. + +``` +ilc repro.exe -o:repro.obj -r:\bin\Product\Windows_NT.x64.Debug\Test.CoreLib\Test.CoreLib.dll --systemmodule Test.CoreLib +``` + +3. Use native linker to link diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs b/src/coreclr/nativeaot/Test.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs new file mode 100644 index 00000000000000..c4b3a714299bd7 --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; + +namespace Internal.Runtime +{ + static unsafe class IDynamicCastableSupport + { + [RuntimeExport("IDynamicCastableIsInterfaceImplemented")] + internal static bool IDynamicCastableIsInterfaceImplemented(object instance, MethodTable* interfaceType, bool throwIfNotImplemented) + { + return false; + } + + [RuntimeExport("IDynamicCastableGetInterfaceImplementation")] + internal static IntPtr IDynamicCastableGetInterfaceImplementation(object instance, MethodTable* interfaceType, ushort slot) + { + RuntimeImports.RhpFallbackFailFast(); + return default; + } + } +} \ No newline at end of file diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Array.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Array.cs new file mode 100644 index 00000000000000..edf23307c5498e --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Array.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; + +using MethodTable = Internal.Runtime.MethodTable; + +namespace System +{ + public partial class Array + { + // This is the classlib-provided "get array MethodTable" function that will be invoked whenever the runtime + // needs to know the base type of an array. + [RuntimeExport("GetSystemArrayEEType")] + private static unsafe MethodTable* GetSystemArrayEEType() + { + return EETypePtr.EETypePtrOf().ToPointer(); + } + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Object.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Object.cs new file mode 100644 index 00000000000000..faa855843fc855 --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Object.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +using Internal.Runtime; +using Internal.Runtime.CompilerServices; + +namespace System +{ + // CONTRACT with Runtime + // The Object type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type MethodTable* + // VTable Contract: The first vtable slot should be the finalizer for object => The first virtual method in the object class should be the Finalizer + + public unsafe class Object + { + // CS0649: Field '{blah}' is never assigned to, and will always have its default value +#pragma warning disable 649 + private MethodTable* m_pEEType; +#pragma warning restore + + // Creates a new instance of an Object. + public Object() + { + } + + // Allow an object to free resources before the object is reclaimed by the GC. + // CONTRACT with runtime: This method's virtual slot number is hardcoded in the binder. It is an + // implementation detail where it winds up at runtime. + // **** Do not add any virtual methods in this class ahead of this **** +#pragma warning disable CA1821 // Remove empty Finalizers + ~Object() + { + } +#pragma warning restore CA1821 + + public virtual bool Equals(object o) + { + return false; + } + + public virtual int GetHashCode() + { + return 0; + } + + internal MethodTable* MethodTable + { + get + { + // NOTE: if managed code can be run when the GC has objects marked, then this method is + // unsafe. But, generically, we don't expect managed code such as this to be allowed + // to run while the GC is running. + return m_pEEType; + } + } + + [Runtime.CompilerServices.Intrinsic] + internal static extern MethodTable* MethodTableOf(); + + [StructLayout(LayoutKind.Sequential)] + private class RawData + { + public byte Data; + } + + internal ref byte GetRawData() + { + return ref Unsafe.As(this).Data; + } + + /// + /// Return size of all data (excluding ObjHeader and MethodTable*). + /// Note that for strings/arrays this would include the Length as well. + /// + internal uint GetRawDataSize() + { + return MethodTable->BaseSize - (uint)sizeof(ObjHeader) - (uint)sizeof(MethodTable*); + } + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs new file mode 100644 index 00000000000000..2a034b6bb7485f --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using System.Runtime; +using System.Runtime.InteropServices; + +namespace System.Runtime.CompilerServices +{ + internal static class ClassConstructorRunner + { + private static unsafe object CheckStaticClassConstructionReturnGCStaticBase(ref StaticClassConstructionContext context, object gcStaticBase) + { + CheckStaticClassConstruction(ref context); + return gcStaticBase; + } + + private static unsafe IntPtr CheckStaticClassConstructionReturnNonGCStaticBase(ref StaticClassConstructionContext context, IntPtr nonGcStaticBase) + { + CheckStaticClassConstruction(ref context); + return nonGcStaticBase; + } + + // Called by the runtime when it finds a class whose static class constructor has probably not run + // (probably because it checks in the initialized flag without thread synchronization). + // + // This method should synchronize with other threads, recheck the initialized flag and execute the + // cctor method (whose address is given in the context structure) before setting the initialized flag + // to 1. Once in this state the runtime will not call this method again for the same type (barring + // race conditions). + // + // The context structure passed by reference lives in the image of one of the application's modules. + // The contents are thus fixed (do not require pinning) and the address can be used as a unique + // identifier for the context. + private static unsafe void CheckStaticClassConstruction(ref StaticClassConstructionContext context) + { + // This is a simplistic placeholder implementation. For instance it uses a busy wait spinlock and + // does not handle recursion. + + while (true) + { + // Read the current state of the cctor. + int oldInitializationState = context.initialized; + + // Once it transitions to 1 then the cctor has been run (another thread got there first) and + // we can simply return. + if (oldInitializationState == 1) + return; + + // If the state is anything other than 0 (the initial state) then another thread is currently + // running this cctor. We must wait for it to complete doing so before continuing, so loop + // again. + if (oldInitializationState != 0) + continue; + + // C# warns that passing a volatile field to a method via a reference loses the volatility of the field. + // However the method in question is Interlocked.CompareExchange so the volatility in this case is + // unimportant. +#pragma warning disable 420 + + // We read a state of 0 (the initial state: not initialized and not being initialized). Try to + // transition this to 2 which will let other threads know we're going to run the cctor here. + if (Interlocked.CompareExchange(ref context.initialized, 2, 0) == 0) + { + // We won the race to transition the state from 0 -> 2. So we can now run the cctor. Other + // threads trying to do the same thing will spin waiting for us to transition the state to + // 1. + + ((delegate*)context.cctorMethodAddress)(); + + // Insert a memory barrier here to order any writes executed as part of static class + // construction above with respect to the initialized flag update we're about to make + // below. This is important since the fast path for checking the cctor uses a normal read + // and doesn't come here so without the barrier it could observe initialized == 1 but + // still see uninitialized static fields on the class. + Interlocked.MemoryBarrier(); + + // Set the state to 1 to indicate to the runtime and other threads that this cctor has now + // been run. + context.initialized = 1; + } + + // If we get here some other thread changed the initialization state to a non-zero value + // before we could. Loop at try again. + } + } + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/CompilerServices/StaticClassConstructionContext.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/CompilerServices/StaticClassConstructionContext.cs new file mode 100644 index 00000000000000..5cc04d3f013abf --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/CompilerServices/StaticClassConstructionContext.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Runtime.CompilerServices +{ + // This structure is used to pass context about a type's static class construction state from the runtime + // to the classlibrary via the CheckStaticClassConstruction callback. The runtime knows about the first + // two fields (cctorMethodAddress and initialized) and thus these must remain the first two fields in the + // same order and at the same offset (hence the sequential layout attribute). It is permissable for the + // classlibrary to add its own fields after these for its own use however. These must not contain GC + // references and will be zero initialized. + [StructLayout(LayoutKind.Sequential)] + public struct StaticClassConstructionContext + { + // Pointer to the code for the static class constructor method. This is initialized by the + // binder/runtime. + public IntPtr cctorMethodAddress; + + // Initialization state of the class. This is initialized to 0. Every time managed code checks the + // cctor state the runtime will call the classlibrary's CheckStaticClassConstruction with this context + // structure unless initialized == 1. This check is specific to allow the classlibrary to store more + // than a binary state for each cctor if it so desires. + public volatile int initialized; + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/InitializeFinalizerThread.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/InitializeFinalizerThread.cs new file mode 100644 index 00000000000000..08ca09dd867bbc --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/InitializeFinalizerThread.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; + +namespace System.Runtime +{ + internal static class FinalizerInitRunner + { + // Here, we are subscribing to a callback from the runtime. This callback is made from the finalizer + // thread before any objects are finalized. + public static void DoInitialize() + { + } + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/InteropServices/InAttribute.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/InteropServices/InAttribute.cs new file mode 100644 index 00000000000000..41d2656af1ecb3 --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/InteropServices/InAttribute.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InAttribute : Attribute + { + public InAttribute() + { + } + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeHelpers.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeHelpers.cs new file mode 100644 index 00000000000000..6c91965f67a1bd --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeHelpers.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + public static class RuntimeHelpers + { + public static int OffsetToStringData + { + get + { + // Number of bytes from the address pointed to by a reference to + // a String to the first 16-bit character in the String. + // This property allows C#'s fixed statement to work on Strings. + return string.FIRST_CHAR_OFFSET; + } + } + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs new file mode 100644 index 00000000000000..bcf47e47fc5ca2 --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -0,0 +1,113 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using Internal.Runtime; + +namespace System.Runtime +{ + // CONTRACT with Runtime + // This class lists all the static methods that the redhawk runtime exports to a class library + // These are not expected to change much but are needed by the class library to implement its functionality + // + // The contents of this file can be modified if needed by the class library + // E.g., the class and methods are marked internal assuming that only the base class library needs them + // but if a class library wants to factor differently (such as putting the GCHandle methods in an + // optional library, those methods can be moved to a different file/namespace/dll + + public static class RuntimeImports + { + private const string RuntimeLibrary = "*"; + + // + // calls for GCHandle. + // These methods are needed to implement GCHandle class like functionality (optional) + // + + // Allocate handle. + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpHandleAlloc")] + private static extern IntPtr RhpHandleAlloc(object value, GCHandleType type); + + internal static IntPtr RhHandleAlloc(object value, GCHandleType type) + { + IntPtr h = RhpHandleAlloc(value, type); + if (h == IntPtr.Zero) + throw new OutOfMemoryException(); + return h; + } + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpRegisterFrozenSegment")] + internal static extern IntPtr RhpRegisterFrozenSegment(IntPtr pSegmentStart, IntPtr length); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpUnregisterFrozenSegment")] + internal static extern void RhpUnregisterFrozenSegment(IntPtr pSegmentHandle); + + [RuntimeImport(RuntimeLibrary, "RhpGetModuleSection")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern IntPtr RhGetModuleSection(ref TypeManagerHandle module, ReadyToRunSectionType section, out int length); + + internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSectionType section, out int length) + { + return RhGetModuleSection(ref module, section, out length); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpCreateTypeManager")] + internal static extern unsafe TypeManagerHandle RhpCreateTypeManager(IntPtr osModule, IntPtr moduleHeader, IntPtr* pClasslibFunctions, int nClasslibFunctions); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpRegisterOsModule")] + internal static extern unsafe IntPtr RhpRegisterOsModule(IntPtr osModule); + + // + // calls to runtime for allocation + // These calls are needed in types which cannot use "new" to allocate and need to do it manually + // + // calls to runtime for allocation + // + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhNewObject")] + private static unsafe extern object RhNewObject(MethodTable* pEEType); + + internal static unsafe object RhNewObject(EETypePtr pEEType) + => RhNewObject(pEEType.ToPointer()); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhNewArray")] + private static unsafe extern Array RhNewArray(MethodTable* pEEType, int length); + + internal static unsafe Array RhNewArray(EETypePtr pEEType, int length) + => RhNewArray(pEEType.ToPointer(), length); + + [DllImport(RuntimeLibrary, ExactSpelling = true)] + internal static unsafe extern void RhAllocateNewObject(IntPtr pEEType, uint flags, void* pResult); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpFallbackFailFast")] + internal static extern unsafe void RhpFallbackFailFast(); + + // + // Interlocked helpers + // + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpLockCmpXchg32")] + internal static extern int InterlockedCompareExchange(ref int location1, int value, int comparand); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpMemoryBarrier")] + internal static extern void MemoryBarrier(); + + // Moves memory from smem to dmem. Size must be a positive value. + // This copy uses an intrinsic to be safe for copying arbitrary bits of + // heap memory + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhBulkMoveWithWriteBarrier")] + internal static extern unsafe void RhBulkMoveWithWriteBarrier(ref byte dmem, ref byte smem, nuint size); + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/RuntimeExceptionHelpers.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/RuntimeExceptionHelpers.cs new file mode 100644 index 00000000000000..777b5790d145b6 --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -0,0 +1,128 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; +using System.Runtime.CompilerServices; + +using Debug = System.Diagnostics.Debug; + +namespace System +{ + // Eagerly preallocate instance of out of memory exception to avoid infinite recursion once we run out of memory + [EagerStaticClassConstruction] + internal static class PreallocatedOutOfMemoryException + { + public static readonly OutOfMemoryException Instance = new OutOfMemoryException(); + } + + internal static class RuntimeExceptionHelpers + { + //------------------------------------------------------------------------------------------------------------ + // @TODO: this function is related to throwing exceptions out of Rtm. If we did not have to throw + // out of Rtm, then we would note have to have the code below to get a classlib exception object given + // an exception id, or the special functions to back up the MDIL THROW_* instructions, or the allocation + // failure helper. If we could move to a world where we never throw out of Rtm, perhaps by moving parts + // of Rtm that do need to throw out to Bartok- or Binder-generated functions, then we could remove all of this. + //------------------------------------------------------------------------------------------------------------ + + // This is the classlib-provided "get exception" function that will be invoked whenever the runtime + // needs to throw an exception back to a method in a non-runtime module. The classlib is expected + // to convert every code in the ExceptionIDs enum to an exception object. + [RuntimeExport("GetRuntimeException")] + public static Exception GetRuntimeException(ExceptionIDs id) + { + // This method is called by the runtime's EH dispatch code and is not allowed to leak exceptions + // back into the dispatcher. + try + { + // @TODO: this function should return pre-allocated exception objects, either frozen in the image + // or preallocated during DllMain(). In particular, this function will be called when out of memory, + // and failure to create an exception will result in infinite recursion and therefore a stack overflow. + switch (id) + { + case ExceptionIDs.OutOfMemory: + return PreallocatedOutOfMemoryException.Instance; + + case ExceptionIDs.Arithmetic: + return new ArithmeticException(); + + case ExceptionIDs.ArrayTypeMismatch: + return new ArrayTypeMismatchException(); + + case ExceptionIDs.DivideByZero: + return new DivideByZeroException(); + + case ExceptionIDs.IndexOutOfRange: + return new IndexOutOfRangeException(); + + case ExceptionIDs.InvalidCast: + return new InvalidCastException(); + + case ExceptionIDs.Overflow: + return new OverflowException(); + + case ExceptionIDs.NullReference: + return new NullReferenceException(); + + case ExceptionIDs.DataMisaligned: + // We don't have this in Test.CoreLib + return new PlatformNotSupportedException(); + + default: + Debug.Assert(false, "unexpected ExceptionID"); + RuntimeImports.RhpFallbackFailFast(); + return null; + } + } + catch + { + return null; // returning null will cause the runtime to FailFast via the class library. + } + } + + public enum RhFailFastReason + { + Unknown = 0, + InternalError = 1, // "Runtime internal error" + UnhandledException_ExceptionDispatchNotAllowed = 2, // "Unhandled exception: no handler found before escaping a finally clause or other fail-fast scope." + UnhandledException_CallerDidNotHandle = 3, // "Unhandled exception: no handler found in calling method." + ClassLibDidNotTranslateExceptionID = 4, // "Unable to translate failure into a classlib-specific exception object." + IllegalUnmanagedCallersOnlyEntry = 5, // "Invalid Program: attempted to call a UnmanagedCallersOnly method from runtime-typesafe code." + PN_UnhandledException = 6, // ProjectN: "Unhandled exception: a managed exception was not handled before reaching unmanaged code" + PN_UnhandledExceptionFromPInvoke = 7, // ProjectN: "Unhandled exception: an unmanaged exception was thrown out of a managed-to-native transition." + Max + } + + // This is the classlib-provided fail-fast function that will be invoked whenever the runtime + // needs to cause the process to exit. It is the classlib's opprotunity to customize the + // termination behavior in whatever way necessary. + [RuntimeExport("FailFast")] + public static void RuntimeFailFast(RhFailFastReason reason, Exception exception, IntPtr pExAddress, IntPtr pExContext) + { + RuntimeImports.RhpFallbackFailFast(); + } + + public static void FailFast(string message) + { + RuntimeImports.RhpFallbackFailFast(); + } + + [RuntimeExport("AppendExceptionStackFrame")] + private static void AppendExceptionStackFrame(object exceptionObj, IntPtr IP, int flags) + { + Exception ex = exceptionObj as Exception; + if (ex == null) + FailFast("Exceptions must derive from the System.Exception class"); + } + + [RuntimeExport("OnFirstChanceException")] + internal static void OnFirstChanceException(object e) + { + } + + [RuntimeExport("OnUnhandledException")] + internal static void OnUnhandledException(object e) + { + } + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/RuntimeTypeHandle.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/RuntimeTypeHandle.cs new file mode 100644 index 00000000000000..45193214f819bd --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/RuntimeTypeHandle.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + [StructLayout(LayoutKind.Sequential)] + public struct RuntimeTypeHandle + { + private EETypePtr _pEEType; + + internal RuntimeTypeHandle(EETypePtr pEEType) + { + _pEEType = pEEType; + } + + [Intrinsic] + internal static unsafe IntPtr GetValueInternal(RuntimeTypeHandle handle) + { + return (IntPtr)handle._pEEType.ToPointer(); + } + } +} + +namespace Internal.Runtime.CompilerHelpers +{ + // Needed by the compiler to lower LDTOKEN + internal static class LdTokenHelpers + { + private static RuntimeTypeHandle GetRuntimeTypeHandle(IntPtr pEEType) + { + return new RuntimeTypeHandle(new EETypePtr(pEEType)); + } + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Threading/Interlocked.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Threading/Interlocked.cs new file mode 100644 index 00000000000000..b3cbdc65794740 --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Threading/Interlocked.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System.Threading +{ + public static class Interlocked + { + [Intrinsic] + public static int CompareExchange(ref int location1, int value, int comparand) + { + return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); + } + + [Intrinsic] + public static void MemoryBarrier() + { + RuntimeImports.MemoryBarrier(); + } + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Type.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Type.cs new file mode 100644 index 00000000000000..f038c88a7e052c --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Type.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// System.Type is only defined to support C# typeof. We shouldn't have it here since the semantic +// is not very compatible. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public class Type + { + private readonly RuntimeTypeHandle _typeHandle; + + private Type(RuntimeTypeHandle typeHandle) + { + _typeHandle = typeHandle; + } + + public RuntimeTypeHandle TypeHandle => _typeHandle; + + public static Type GetTypeFromHandle(RuntimeTypeHandle rh) + { + return new Type(rh); + } + + [Intrinsic] + public static bool operator ==(Type left, Type right) + { + return RuntimeTypeHandle.GetValueInternal(left._typeHandle) == RuntimeTypeHandle.GetValueInternal(right._typeHandle); + } + + [Intrinsic] + public static bool operator !=(Type left, Type right) => !(left == right); + + public override bool Equals(object o) => o is Type && this == (Type)o; + + public override int GetHashCode() => 0; + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj new file mode 100644 index 00000000000000..c95cc9c17b8a39 --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj @@ -0,0 +1,230 @@ + + + false + false + + + FEATURE_GC_STRESS;$(DefineConstants) + + + FEATURE_64BIT_ALIGNMENT;$(DefineConstants) + + + FEATURE_64BIT_ALIGNMENT;$(DefineConstants) + + + FEATURE_64BIT_ALIGNMENT;$(DefineConstants) + + + + true + + + INPLACE_RUNTIME;$(DefineConstants) + FEATURE_64BIT_ALIGNMENT;$(DefineConstants) + FEATURE_64BIT_ALIGNMENT;$(DefineConstants) + + $(ArtifactsObjDir)\coreclr\$(TargetOS).$(TargetArchitecture).$(CoreCLRConfiguration) + $(IntermediatesDir)\ide + + + + Runtime.Base\src\System\Runtime\CachedInterfaceDispatch.cs + + + Runtime.Base\src\System\Runtime\DispatchResolve.cs + + + Runtime.Base\src\System\Runtime\GCStress.cs + + + Runtime.Base\src\System\Runtime\__Finalizer.cs + + + Runtime.Base\src\System\Runtime\MethodTable.Runtime.cs + + + Runtime.Base\src\System\Runtime\ExceptionHandling.cs + + + Runtime.Base\src\System\Runtime\InternalCalls.cs + + + Runtime.Base\src\System\Runtime\RuntimeExports.cs + + + Runtime.Base\src\System\Runtime\StackFrameIterator.cs + + + Runtime.Base\src\System\Runtime\ThunkPool.cs + + + Runtime.Base\src\System\Runtime\TypeCast.cs + + + Runtime.Base\src\System\Runtime\InteropServices\UnsafeGCHandle.cs + + + Runtime.Base\src\System\Runtime\InteropServices\UnmanagedType.cs + + + Runtime.Base\src\RhBaseName.cs + + + Common\TransitionBlock.cs + + + + + + + + Internal\NativeFormat\NativeFormatReader.Primitives.cs + + + Internal\Runtime\CompilerHelpers\StartupCodeHelpers.cs + + + Internal\Runtime\CompilerHelpers\StartupDebug.cs + + + System\Runtime\MethodTable.Constants.cs + + + System\Runtime\MethodTable.cs + + + Internal\Runtime\ModuleHeaders.cs + + + Internal\Runtime\RuntimeConstants.cs + + + System\Array.cs + + + System\Attribute.cs + + + System\AttributeTargets.cs + + + System\AttributeUsageAttribute.cs + + + System\Delegate.cs + + + System\Diagnostics\ConditionalAttribute.cs + + + System\Diagnostics\Debug.cs + + + System\Exception.cs + + + System\FlagsAttribute.cs + + + System\GC.cs + + + System\MulticastDelegate.cs + + + System\Nullable.cs + + + System\ParamArrayAttribute.cs + + + System\Primitives.cs + + + System\RuntimeHandles.cs + + + System\Runtime\CompilerServices\EagerStaticClassConstructionAttribute.cs + + + System\Runtime\CompilerServices\IntrinsicAttribute.cs + + + System\Runtime\CompilerServices\IsVolatile.cs + + + System\Runtime\CompilerServices\MethodImplAttribute.cs + + + Runtime.Base\src\System\Runtime\CompilerServices\IsByRefLikeAttribute.cs + + + System\EETypePtr.cs + + + System\Runtime\ExceptionIDs.cs + + + System\Runtime\InteropServices\CallingConvention.cs + + + System\Runtime\InteropServices\CharSet.cs + + + System\Runtime\InteropServices\DllImportAttribute.cs + + + System\Runtime\InteropServices\FieldOffsetAttribute.cs + + + System\Runtime\InteropServices\GCHandleType.cs + + + System\Runtime\InteropServices\LayoutKind.cs + + + System\Runtime\InteropServices\UnmanagedCallersOnlyAttribute.cs + + + System\Runtime\InteropServices\OutAttribute.cs + + + System\Runtime\InteropServices\StructLayoutAttribute.cs + + + System\Runtime\RuntimeExportAttribute.cs + + + System\Runtime\RuntimeImportAttribute.cs + + + System\String.cs + + + System\ThrowHelpers.cs + + + System\Void.cs + + + Internal\Runtime\CompilerServices\Unsafe.cs + + + + + + + + + + + + + + + + Internal\Runtime\TypeManagerHandle.cs + + + diff --git a/src/coreclr/nativeaot/libunwind/.arcconfig b/src/coreclr/nativeaot/libunwind/.arcconfig new file mode 100644 index 00000000000000..78ee8d358cded2 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/.arcconfig @@ -0,0 +1,4 @@ +{ + "repository.callsign" : "UNW", + "conduit_uri" : "https://reviews.llvm.org/" +} diff --git a/src/coreclr/nativeaot/libunwind/.clang-format b/src/coreclr/nativeaot/libunwind/.clang-format new file mode 100644 index 00000000000000..5bead5f39dd3c5 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM + diff --git a/src/coreclr/nativeaot/libunwind/CMakeLists.txt b/src/coreclr/nativeaot/libunwind/CMakeLists.txt new file mode 100644 index 00000000000000..b51922a48fe288 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/CMakeLists.txt @@ -0,0 +1,383 @@ +#=============================================================================== +# Setup Project +#=============================================================================== + +cmake_minimum_required(VERSION 3.4.3) + +if (POLICY CMP0042) + cmake_policy(SET CMP0042 NEW) # Set MACOSX_RPATH=YES by default +endif() + +# Add path for custom modules +set(CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" + ${CMAKE_MODULE_PATH} + ) + +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR OR LIBUNWIND_STANDALONE_BUILD) + project(libunwind) + + # Rely on llvm-config. + set(CONFIG_OUTPUT) + if(NOT LLVM_CONFIG_PATH) + find_program(LLVM_CONFIG_PATH "llvm-config") + endif() + if (DEFINED LLVM_PATH) + set(LLVM_INCLUDE_DIR ${LLVM_INCLUDE_DIR} CACHE PATH "Path to llvm/include") + set(LLVM_PATH ${LLVM_PATH} CACHE PATH "Path to LLVM source tree") + set(LLVM_MAIN_SRC_DIR ${LLVM_PATH}) + set(LLVM_CMAKE_PATH "${LLVM_PATH}/cmake/modules") + elseif(LLVM_CONFIG_PATH) + message(STATUS "Found LLVM_CONFIG_PATH as ${LLVM_CONFIG_PATH}") + set(CONFIG_COMMAND ${LLVM_CONFIG_PATH} "--includedir" "--prefix" "--src-root") + execute_process(COMMAND ${CONFIG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE CONFIG_OUTPUT) + if (NOT HAD_ERROR) + string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" + CONFIG_OUTPUT ${CONFIG_OUTPUT}) + else() + string(REPLACE ";" " " CONFIG_COMMAND_STR "${CONFIG_COMMAND}") + message(STATUS "${CONFIG_COMMAND_STR}") + message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}") + endif() + + list(GET CONFIG_OUTPUT 0 INCLUDE_DIR) + list(GET CONFIG_OUTPUT 1 LLVM_OBJ_ROOT) + list(GET CONFIG_OUTPUT 2 MAIN_SRC_DIR) + + set(LLVM_INCLUDE_DIR ${INCLUDE_DIR} CACHE PATH "Path to llvm/include") + set(LLVM_BINARY_DIR ${LLVM_OBJ_ROOT} CACHE PATH "Path to LLVM build tree") + set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") + set(LLVM_LIT_PATH "${LLVM_PATH}/utils/lit/lit.py") + + # --cmakedir is supported since llvm r291218 (4.0 release) + execute_process( + COMMAND ${LLVM_CONFIG_PATH} --cmakedir + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE CONFIG_OUTPUT + ERROR_QUIET) + if(NOT HAD_ERROR) + string(STRIP "${CONFIG_OUTPUT}" LLVM_CMAKE_PATH_FROM_LLVM_CONFIG) + file(TO_CMAKE_PATH "${LLVM_CMAKE_PATH_FROM_LLVM_CONFIG}" LLVM_CMAKE_PATH) + else() + file(TO_CMAKE_PATH "${LLVM_BINARY_DIR}" LLVM_BINARY_DIR_CMAKE_STYLE) + set(LLVM_CMAKE_PATH "${LLVM_BINARY_DIR_CMAKE_STYLE}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm") + endif() + else() + message(WARNING "UNSUPPORTED LIBUNWIND CONFIGURATION DETECTED: " + "llvm-config not found and LLVM_MAIN_SRC_DIR not defined. " + "Reconfigure with -DLLVM_CONFIG=path/to/llvm-config " + "or -DLLVM_PATH=path/to/llvm-source-root.") + endif() + + if (EXISTS ${LLVM_CMAKE_PATH}) + # Enable warnings, otherwise -w gets added to the cflags by HandleLLVMOptions. + set(LLVM_ENABLE_WARNINGS ON) + list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}") + include("${LLVM_CMAKE_PATH}/AddLLVM.cmake") + include("${LLVM_CMAKE_PATH}/HandleLLVMOptions.cmake") + else() + message(WARNING "Not found: ${LLVM_CMAKE_PATH}") + endif() + + set(PACKAGE_NAME libunwind) + set(PACKAGE_VERSION 9.0.0svn) + set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") + set(PACKAGE_BUGREPORT "llvm-bugs@lists.llvm.org") + + if (EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) + set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) + else() + # Seek installed Lit. + find_program(LLVM_LIT "lit.py" ${LLVM_MAIN_SRC_DIR}/utils/lit + DOC "Path to lit.py") + endif() + + if (LLVM_LIT) + # Define the default arguments to use with 'lit', and an option for the user + # to override. + set(LIT_ARGS_DEFAULT "-sv") + if (MSVC OR XCODE) + set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar") + endif() + set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit") + + # On Win32 hosts, provide an option to specify the path to the GnuWin32 tools. + if (WIN32 AND NOT CYGWIN) + set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools") + endif() + else() + set(LLVM_INCLUDE_TESTS OFF) + endif() + + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) +else() + set(LLVM_LIT "${CMAKE_SOURCE_DIR}/utils/lit/lit.py") +endif() + +#=============================================================================== +# Setup CMake Options +#=============================================================================== +include(CMakeDependentOption) +include(HandleCompilerRT) + +# Define options. +option(LIBUNWIND_BUILD_32_BITS "Build 32 bit libunwind" ${LLVM_BUILD_32_BITS}) +option(LIBUNWIND_ENABLE_ASSERTIONS "Enable assertions independent of build mode." ON) +option(LIBUNWIND_ENABLE_PEDANTIC "Compile with pedantic enabled." ON) +option(LIBUNWIND_ENABLE_WERROR "Fail and stop if a warning is triggered." OFF) +option(LIBUNWIND_ENABLE_SHARED "Build libunwind as a shared library." ON) +option(LIBUNWIND_ENABLE_STATIC "Build libunwind as a static library." ON) +option(LIBUNWIND_ENABLE_CROSS_UNWINDING "Enable cross-platform unwinding support." OFF) +option(LIBUNWIND_ENABLE_ARM_WMMX "Enable unwinding support for ARM WMMX registers." OFF) +option(LIBUNWIND_ENABLE_THREADS "Build libunwind with threading support." ON) +option(LIBUNWIND_WEAK_PTHREAD_LIB "Use weak references to refer to pthread functions." OFF) +option(LIBUNWIND_USE_COMPILER_RT "Use compiler-rt instead of libgcc" OFF) +option(LIBUNWIND_INCLUDE_DOCS "Build the libunwind documentation." ${LLVM_INCLUDE_DOCS}) + +set(LIBUNWIND_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}" CACHE STRING + "Define suffix of library directory name (32/64)") +option(LIBUNWIND_INSTALL_LIBRARY "Install the libunwind library." ON) +cmake_dependent_option(LIBUNWIND_INSTALL_STATIC_LIBRARY + "Install the static libunwind library." ON + "LIBUNWIND_ENABLE_STATIC;LIBUNWIND_INSTALL_LIBRARY" OFF) +cmake_dependent_option(LIBUNWIND_INSTALL_SHARED_LIBRARY + "Install the shared libunwind library." ON + "LIBUNWIND_ENABLE_SHARED;LIBUNWIND_INSTALL_LIBRARY" OFF) +set(LIBUNWIND_TARGET_TRIPLE "" CACHE STRING "Target triple for cross compiling.") +set(LIBUNWIND_GCC_TOOLCHAIN "" CACHE PATH "GCC toolchain for cross compiling.") +set(LIBUNWIND_SYSROOT "" CACHE PATH "Sysroot for cross compiling.") +set(LIBUNWIND_TEST_LINKER_FLAGS "" CACHE STRING + "Additional linker flags for test programs.") +set(LIBUNWIND_TEST_COMPILER_FLAGS "" CACHE STRING + "Additional compiler flags for test programs.") + +if (NOT LIBUNWIND_ENABLE_SHARED AND NOT LIBUNWIND_ENABLE_STATIC) + message(FATAL_ERROR "libunwind must be built as either a shared or static library.") +endif() + +# Check that we can build with 32 bits if requested. +if (CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT WIN32) + if (LIBUNWIND_BUILD_32_BITS AND NOT LLVM_BUILD_32_BITS) # Don't duplicate the output from LLVM + message(STATUS "Building 32 bits executables and libraries.") + endif() +elseif(LIBUNWIND_BUILD_32_BITS) + message(FATAL_ERROR "LIBUNWIND_BUILD_32_BITS=ON is not supported on this platform.") +endif() + +option(LIBUNWIND_HERMETIC_STATIC_LIBRARY + "Do not export any symbols from the static library." OFF) + +#=============================================================================== +# Configure System +#=============================================================================== + +# Add path for custom modules +set(CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" + ${CMAKE_MODULE_PATH}) + +set(LIBUNWIND_COMPILER ${CMAKE_CXX_COMPILER}) +set(LIBUNWIND_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(LIBUNWIND_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" CLANG_VERSION + ${PACKAGE_VERSION}) + +if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE) + set(LIBUNWIND_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}/${LLVM_DEFAULT_TARGET_TRIPLE}/c++) + set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LLVM_LIBDIR_SUFFIX}/${LLVM_DEFAULT_TARGET_TRIPLE}/c++) + if(LIBCXX_LIBDIR_SUBDIR) + string(APPEND LIBUNWIND_LIBRARY_DIR /${LIBUNWIND_LIBDIR_SUBDIR}) + string(APPEND LIBUNWIND_INSTALL_LIBRARY_DIR /${LIBUNWIND_LIBDIR_SUBDIR}) + endif() +elseif(LLVM_LIBRARY_OUTPUT_INTDIR) + set(LIBUNWIND_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}) + set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LIBUNWIND_LIBDIR_SUFFIX}) +else() + set(LIBUNWIND_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib${LIBUNWIND_LIBDIR_SUFFIX}) + set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LIBUNWIND_LIBDIR_SUFFIX}) +endif() + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIBUNWIND_LIBRARY_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIBUNWIND_LIBRARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${LIBUNWIND_LIBRARY_DIR}) + +set(LIBUNWIND_INSTALL_PREFIX "" CACHE STRING "Define libunwind destination prefix.") + +set(LIBUNWIND_C_FLAGS "") +set(LIBUNWIND_CXX_FLAGS "") +set(LIBUNWIND_COMPILE_FLAGS "") +set(LIBUNWIND_LINK_FLAGS "") + +# Get required flags. +macro(unwind_append_if list condition var) + if (${condition}) + list(APPEND ${list} ${var}) + endif() +endmacro() + +macro(add_target_flags) + foreach(value ${ARGN}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${value}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${value}") + list(APPEND LIBUNWIND_COMPILE_FLAGS ${value}) + list(APPEND LIBUNWIND_LINK_FLAGS ${value}) + endforeach() +endmacro() + +macro(add_target_flags_if condition) + if (${condition}) + add_target_flags(${ARGN}) + endif() +endmacro() + +add_target_flags_if(LIBUNWIND_BUILD_32_BITS "-m32") + +if(LIBUNWIND_TARGET_TRIPLE) + add_target_flags("--target=${LIBUNWIND_TARGET_TRIPLE}") +elseif(CMAKE_CXX_COMPILER_TARGET) + set(LIBUNWIND_TARGET_TRIPLE "${CMAKE_CXX_COMPILER_TARGET}") +endif() +if(LIBUNWIND_GCC_TOOLCHAIN) + add_target_flags("--gcc-toolchain=${LIBUNWIND_GCC_TOOLCHAIN}") +elseif(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN) + set(LIBUNWIND_GCC_TOOLCHAIN "${CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN}") +endif() +if(LIBUNWIND_SYSROOT) + add_target_flags("--sysroot=${LIBUNWIND_SYSROOT}") +elseif(CMAKE_SYSROOT) + set(LIBUNWIND_SYSROOT "${CMAKE_SYSROOT}") +endif() + +if (LIBUNWIND_TARGET_TRIPLE) + set(TARGET_TRIPLE "${LIBUNWIND_TARGET_TRIPLE}") +endif() + +# Configure compiler. +include(config-ix) + +if (LIBUNWIND_USE_COMPILER_RT AND NOT LIBUNWIND_HAS_NODEFAULTLIBS_FLAG) + list(APPEND LIBUNWIND_LINK_FLAGS "-rtlib=compiler-rt") +endif() + +#=============================================================================== +# Setup Compiler Flags +#=============================================================================== + +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WERROR_FLAG -Werror=return-type) + +# Get warning flags +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_W_FLAG -W) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WALL_FLAG -Wall) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WCHAR_SUBSCRIPTS_FLAG -Wchar-subscripts) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WCONVERSION_FLAG -Wconversion) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WMISMATCHED_TAGS_FLAG -Wmismatched-tags) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WMISSING_BRACES_FLAG -Wmissing-braces) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WNEWLINE_EOF_FLAG -Wnewline-eof) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WNO_UNUSED_FUNCTION_FLAG -Wno-unused-function) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WSHADOW_FLAG -Wshadow) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WSHORTEN_64_TO_32_FLAG -Wshorten-64-to-32) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WSIGN_COMPARE_FLAG -Wsign-compare) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WSIGN_CONVERSION_FLAG -Wsign-conversion) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WSTRICT_ALIASING_FLAG -Wstrict-aliasing=2) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WSTRICT_OVERFLOW_FLAG -Wstrict-overflow=4) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WUNUSED_PARAMETER_FLAG -Wunused-parameter) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WUNUSED_VARIABLE_FLAG -Wunused-variable) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WWRITE_STRINGS_FLAG -Wwrite-strings) +unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WUNDEF_FLAG -Wundef) + +if (LIBUNWIND_ENABLE_WERROR) + unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WERROR_FLAG -Werror) + unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WX_FLAG -WX) +else() + unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_WNO_ERROR_FLAG -Wno-error) + unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_NO_WX_FLAG -WX-) +endif() + +if (LIBUNWIND_ENABLE_PEDANTIC) + unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_HAS_PEDANTIC_FLAG -pedantic) +endif() + +# Get feature flags. +# Exceptions +# Catches C++ exceptions only and tells the compiler to assume that extern C +# functions never throw a C++ exception. +unwind_append_if(LIBUNWIND_CXX_FLAGS LIBUNWIND_HAS_FSTRICT_ALIASING_FLAG -fstrict-aliasing) +unwind_append_if(LIBUNWIND_CXX_FLAGS LIBUNWIND_HAS_EHSC_FLAG -EHsc) + +unwind_append_if(LIBUNWIND_C_FLAGS LIBUNWIND_HAS_FUNWIND_TABLES -funwind-tables) + +# Ensure that we don't depend on C++ standard library. +unwind_append_if(LIBUNWIND_CXX_FLAGS LIBUNWIND_HAS_NOSTDINCXX_FLAG -nostdinc++) + +# Assert +string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) +if (LIBUNWIND_ENABLE_ASSERTIONS) + # MSVC doesn't like _DEBUG on release builds. See PR 4379. + if (NOT MSVC) + list(APPEND LIBUNWIND_COMPILE_FLAGS -D_DEBUG) + endif() + + # On Release builds cmake automatically defines NDEBUG, so we + # explicitly undefine it: + if (uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE") + list(APPEND LIBUNWIND_COMPILE_FLAGS -UNDEBUG) + endif() +else() + if (NOT uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE") + list(APPEND LIBUNWIND_COMPILE_FLAGS -DNDEBUG) + endif() +endif() + +# Cross-unwinding +if (NOT LIBUNWIND_ENABLE_CROSS_UNWINDING) + list(APPEND LIBUNWIND_COMPILE_FLAGS -D_LIBUNWIND_IS_NATIVE_ONLY) +endif() + +# Threading-support +if (NOT LIBUNWIND_ENABLE_THREADS) + list(APPEND LIBUNWIND_COMPILE_FLAGS -D_LIBUNWIND_HAS_NO_THREADS) +endif() + +# ARM WMMX register support +if (LIBUNWIND_ENABLE_ARM_WMMX) + # __ARM_WMMX is a compiler pre-define (as per the ACLE 2.0). Clang does not + # define this macro for any supported target at present. Therefore, here we + # provide the option to explicitly enable support for WMMX registers in the + # unwinder. + list(APPEND LIBUNWIND_COMPILE_FLAGS -D__ARM_WMMX) +endif() + +# This is the _ONLY_ place where add_definitions is called. +if (MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +# Disable DLL annotations on Windows for static builds. +if (WIN32 AND LIBUNWIND_ENABLE_STATIC AND NOT LIBUNWIND_ENABLE_SHARED) + add_definitions(-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS) +endif() + +if (LIBUNWIND_HAS_COMMENT_LIB_PRAGMA) + add_definitions(-D_LIBUNWIND_HAS_COMMENT_LIB_PRAGMA) +endif() + +#=============================================================================== +# Setup Source Code +#=============================================================================== + +include_directories(include) + +add_subdirectory(src) + +if (LIBUNWIND_INCLUDE_DOCS) + add_subdirectory(docs) +endif() + +if (EXISTS ${LLVM_CMAKE_PATH}) + add_subdirectory(test) +endif() diff --git a/src/coreclr/nativeaot/libunwind/cmake/Modules/HandleCompilerRT.cmake b/src/coreclr/nativeaot/libunwind/cmake/Modules/HandleCompilerRT.cmake new file mode 100644 index 00000000000000..77168e599466ed --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/cmake/Modules/HandleCompilerRT.cmake @@ -0,0 +1,64 @@ +function(find_compiler_rt_library name dest) + if (NOT DEFINED LIBUNWIND_COMPILE_FLAGS) + message(FATAL_ERROR "LIBUNWIND_COMPILE_FLAGS must be defined when using this function") + endif() + set(dest "" PARENT_SCOPE) + set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBUNWIND_COMPILE_FLAGS} + "--rtlib=compiler-rt" "--print-libgcc-file-name") + if (CMAKE_CXX_COMPILER_ID MATCHES Clang AND CMAKE_CXX_COMPILER_TARGET) + list(APPEND CLANG_COMMAND "--target=${CMAKE_CXX_COMPILER_TARGET}") + endif() + get_property(LIBUNWIND_CXX_FLAGS CACHE CMAKE_CXX_FLAGS PROPERTY VALUE) + string(REPLACE " " ";" LIBUNWIND_CXX_FLAGS "${LIBUNWIND_CXX_FLAGS}") + list(APPEND CLANG_COMMAND ${LIBUNWIND_CXX_FLAGS}) + execute_process( + COMMAND ${CLANG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LIBRARY_FILE + ) + string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE) + file(TO_CMAKE_PATH "${LIBRARY_FILE}" LIBRARY_FILE) + string(REPLACE "builtins" "${name}" LIBRARY_FILE "${LIBRARY_FILE}") + if (NOT HAD_ERROR AND EXISTS "${LIBRARY_FILE}") + message(STATUS "Found compiler-rt library: ${LIBRARY_FILE}") + set(${dest} "${LIBRARY_FILE}" PARENT_SCOPE) + else() + message(STATUS "Failed to find compiler-rt library") + endif() +endfunction() + +function(find_compiler_rt_dir dest) + if (NOT DEFINED LIBUNWIND_COMPILE_FLAGS) + message(FATAL_ERROR "LIBUNWIND_COMPILE_FLAGS must be defined when using this function") + endif() + set(dest "" PARENT_SCOPE) + if (APPLE) + set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBUNWIND_COMPILE_FLAGS} + "-print-file-name=lib") + execute_process( + COMMAND ${CLANG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LIBRARY_DIR + ) + string(STRIP "${LIBRARY_DIR}" LIBRARY_DIR) + file(TO_CMAKE_PATH "${LIBRARY_DIR}" LIBRARY_DIR) + set(LIBRARY_DIR "${LIBRARY_DIR}/darwin") + else() + set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBUNWIND_COMPILE_FLAGS} + "--rtlib=compiler-rt" "--print-libgcc-file-name") + execute_process( + COMMAND ${CLANG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LIBRARY_FILE + ) + string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE) + file(TO_CMAKE_PATH "${LIBRARY_FILE}" LIBRARY_FILE) + get_filename_component(LIBRARY_DIR "${LIBRARY_FILE}" DIRECTORY) + endif() + if (NOT HAD_ERROR AND EXISTS "${LIBRARY_DIR}") + message(STATUS "Found compiler-rt directory: ${LIBRARY_DIR}") + set(${dest} "${LIBRARY_DIR}" PARENT_SCOPE) + else() + message(STATUS "Failed to find compiler-rt directory") + endif() +endfunction() diff --git a/src/coreclr/nativeaot/libunwind/cmake/config-ix.cmake b/src/coreclr/nativeaot/libunwind/cmake/config-ix.cmake new file mode 100644 index 00000000000000..07a95ce1a46a8a --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/cmake/config-ix.cmake @@ -0,0 +1,110 @@ +include(CMakePushCheckState) +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) +include(CheckLibraryExists) +include(CheckCSourceCompiles) + +check_library_exists(c fopen "" LIBUNWIND_HAS_C_LIB) + +if (NOT LIBUNWIND_USE_COMPILER_RT) + check_library_exists(gcc_s __gcc_personality_v0 "" LIBUNWIND_HAS_GCC_S_LIB) + check_library_exists(gcc __absvdi2 "" LIBUNWIND_HAS_GCC_LIB) +endif() + +# libunwind is built with -nodefaultlibs, so we want all our checks to also +# use this option, otherwise we may end up with an inconsistency between +# the flags we think we require during configuration (if the checks are +# performed without -nodefaultlibs) and the flags that are actually +# required during compilation (which has the -nodefaultlibs). libc is +# required for the link to go through. We remove sanitizers from the +# configuration checks to avoid spurious link errors. +check_c_compiler_flag(-nodefaultlibs LIBUNWIND_HAS_NODEFAULTLIBS_FLAG) +if (LIBUNWIND_HAS_NODEFAULTLIBS_FLAG) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -nodefaultlibs") + if (LIBUNWIND_HAS_C_LIB) + list(APPEND CMAKE_REQUIRED_LIBRARIES c) + endif () + if (LIBUNWIND_USE_COMPILER_RT) + find_compiler_rt_library(builtins LIBUNWIND_BUILTINS_LIBRARY) + list(APPEND CMAKE_REQUIRED_LIBRARIES "${LIBUNWIND_BUILTINS_LIBRARY}") + else () + if (LIBUNWIND_HAS_GCC_S_LIB) + list(APPEND CMAKE_REQUIRED_LIBRARIES gcc_s) + endif () + if (LIBUNWIND_HAS_GCC_LIB) + list(APPEND CMAKE_REQUIRED_LIBRARIES gcc) + endif () + endif () + if (MINGW) + # Mingw64 requires quite a few "C" runtime libraries in order for basic + # programs to link successfully with -nodefaultlibs. + if (LIBUNWIND_USE_COMPILER_RT) + set(MINGW_RUNTIME ${LIBUNWIND_BUILTINS_LIBRARY}) + else () + set(MINGW_RUNTIME gcc_s gcc) + endif() + set(MINGW_LIBRARIES mingw32 ${MINGW_RUNTIME} moldname mingwex msvcrt advapi32 + shell32 user32 kernel32 mingw32 ${MINGW_RUNTIME} + moldname mingwex msvcrt) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${MINGW_LIBRARIES}) + endif() + if (CMAKE_C_FLAGS MATCHES -fsanitize OR CMAKE_CXX_FLAGS MATCHES -fsanitize) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-sanitize=all") + endif () + if (CMAKE_C_FLAGS MATCHES -fsanitize-coverage OR CMAKE_CXX_FLAGS MATCHES -fsanitize-coverage) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters") + endif () +endif () + +# Check compiler pragmas +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror=unknown-pragmas") + check_c_source_compiles(" +#pragma comment(lib, \"c\") +int main() { return 0; } +" LIBUNWIND_HAS_COMMENT_LIB_PRAGMA) + cmake_pop_check_state() +endif() + +# Check compiler flags +check_c_compiler_flag(-funwind-tables LIBUNWIND_HAS_FUNWIND_TABLES) +check_cxx_compiler_flag(-fno-exceptions LIBUNWIND_HAS_NO_EXCEPTIONS_FLAG) +check_cxx_compiler_flag(-fno-rtti LIBUNWIND_HAS_NO_RTTI_FLAG) +check_cxx_compiler_flag(-fstrict-aliasing LIBUNWIND_HAS_FSTRICT_ALIASING_FLAG) +check_cxx_compiler_flag(-nostdinc++ LIBUNWIND_HAS_NOSTDINCXX_FLAG) +check_cxx_compiler_flag(-Wall LIBUNWIND_HAS_WALL_FLAG) +check_cxx_compiler_flag(-W LIBUNWIND_HAS_W_FLAG) +check_cxx_compiler_flag(-Wno-unused-function LIBUNWIND_HAS_WNO_UNUSED_FUNCTION_FLAG) +check_cxx_compiler_flag(-Wunused-variable LIBUNWIND_HAS_WUNUSED_VARIABLE_FLAG) +check_cxx_compiler_flag(-Wunused-parameter LIBUNWIND_HAS_WUNUSED_PARAMETER_FLAG) +check_cxx_compiler_flag(-Wstrict-aliasing LIBUNWIND_HAS_WSTRICT_ALIASING_FLAG) +check_cxx_compiler_flag(-Wstrict-overflow LIBUNWIND_HAS_WSTRICT_OVERFLOW_FLAG) +check_cxx_compiler_flag(-Wwrite-strings LIBUNWIND_HAS_WWRITE_STRINGS_FLAG) +check_cxx_compiler_flag(-Wchar-subscripts LIBUNWIND_HAS_WCHAR_SUBSCRIPTS_FLAG) +check_cxx_compiler_flag(-Wmismatched-tags LIBUNWIND_HAS_WMISMATCHED_TAGS_FLAG) +check_cxx_compiler_flag(-Wmissing-braces LIBUNWIND_HAS_WMISSING_BRACES_FLAG) +check_cxx_compiler_flag(-Wshorten-64-to-32 LIBUNWIND_HAS_WSHORTEN_64_TO_32_FLAG) +check_cxx_compiler_flag(-Wsign-conversion LIBUNWIND_HAS_WSIGN_CONVERSION_FLAG) +check_cxx_compiler_flag(-Wsign-compare LIBUNWIND_HAS_WSIGN_COMPARE_FLAG) +check_cxx_compiler_flag(-Wshadow LIBUNWIND_HAS_WSHADOW_FLAG) +check_cxx_compiler_flag(-Wconversion LIBUNWIND_HAS_WCONVERSION_FLAG) +check_cxx_compiler_flag(-Wnewline-eof LIBUNWIND_HAS_WNEWLINE_EOF_FLAG) +check_cxx_compiler_flag(-Wundef LIBUNWIND_HAS_WUNDEF_FLAG) +check_cxx_compiler_flag(-pedantic LIBUNWIND_HAS_PEDANTIC_FLAG) +check_cxx_compiler_flag(-Werror LIBUNWIND_HAS_WERROR_FLAG) +check_cxx_compiler_flag(-Wno-error LIBUNWIND_HAS_WNO_ERROR_FLAG) +check_cxx_compiler_flag(/WX LIBUNWIND_HAS_WX_FLAG) +check_cxx_compiler_flag(/WX- LIBUNWIND_HAS_NO_WX_FLAG) +check_cxx_compiler_flag(/EHsc LIBUNWIND_HAS_EHSC_FLAG) +check_cxx_compiler_flag(/EHs- LIBUNWIND_HAS_NO_EHS_FLAG) +check_cxx_compiler_flag(/EHa- LIBUNWIND_HAS_NO_EHA_FLAG) +check_cxx_compiler_flag(/GR- LIBUNWIND_HAS_NO_GR_FLAG) +check_cxx_compiler_flag(-std=c++11 LIBUNWIND_HAS_STD_CXX11) + +if(LIBUNWIND_HAS_STD_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +endif() + +check_library_exists(dl dladdr "" LIBUNWIND_HAS_DL_LIB) +check_library_exists(pthread pthread_once "" LIBUNWIND_HAS_PTHREAD_LIB) diff --git a/src/coreclr/nativeaot/libunwind/docs/BuildingLibunwind.rst b/src/coreclr/nativeaot/libunwind/docs/BuildingLibunwind.rst new file mode 100644 index 00000000000000..7f42133a8a50e0 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/docs/BuildingLibunwind.rst @@ -0,0 +1,161 @@ +.. _BuildingLibunwind: + +================== +Building libunwind +================== + +.. contents:: + :local: + +.. _build instructions: + +Getting Started +=============== + +On Mac OS, the easiest way to get this library is to link with -lSystem. +However if you want to build tip-of-trunk from here (getting the bleeding +edge), read on. + +The basic steps needed to build libc++ are: + +#. Checkout LLVM, libunwind, and related projects: + + * ``cd where-you-want-llvm-to-live`` + * ``git clone https://github.com/llvm/llvm-project.git`` + +#. Configure and build libunwind: + + CMake is the only supported configuration system. + + Clang is the preferred compiler when building and using libunwind. + + * ``cd where you want to build llvm`` + * ``mkdir build`` + * ``cd build`` + * ``cmake -G -DLLVM_ENABLE_PROJECTS=libunwind [options] `` + + For more information about configuring libunwind see :ref:`CMake Options`. + + * ``make unwind`` --- will build libunwind. + * ``make check-unwind`` --- will run the test suite. + + Shared and static libraries for libunwind should now be present in llvm/build/lib. + +#. **Optional**: Install libunwind + + If your system already provides an unwinder, it is important to be careful + not to replace it. Remember Use the CMake option ``CMAKE_INSTALL_PREFIX`` to + select a safe place to install libunwind. + + * ``make install-unwind`` --- Will install the libraries and the headers + + +It is sometimes beneficial to build outside of the LLVM tree. An out-of-tree +build would look like this: + +.. code-block:: bash + + $ cd where-you-want-libunwind-to-live + $ # Check out llvm, and libunwind + $ ``svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm`` + $ ``svn co http://llvm.org/svn/llvm-project/libunwind/trunk libunwind`` + $ cd where-you-want-to-build + $ mkdir build && cd build + $ export CC=clang CXX=clang++ + $ cmake -DLLVM_PATH=path/to/llvm \ + path/to/libunwind + $ make + + +.. _CMake Options: + +CMake Options +============= + +Here are some of the CMake variables that are used often, along with a +brief explanation and LLVM-specific notes. For full documentation, check the +CMake docs or execute ``cmake --help-variable VARIABLE_NAME``. + +**CMAKE_BUILD_TYPE**:STRING + Sets the build type for ``make`` based generators. Possible values are + Release, Debug, RelWithDebInfo and MinSizeRel. On systems like Visual Studio + the user sets the build type with the IDE settings. + +**CMAKE_INSTALL_PREFIX**:PATH + Path where LLVM will be installed if "make install" is invoked or the + "INSTALL" target is built. + +**CMAKE_CXX_COMPILER**:STRING + The C++ compiler to use when building and testing libunwind. + + +.. _libunwind-specific options: + +libunwind specific options +-------------------------- + +.. option:: LIBUNWIND_BUILD_32_BITS:BOOL + + **Default**: Same as LLVM_BUILD_32_BITS + + Toggle whether libunwind should be built with -m32. + +.. option:: LIBUNWIND_ENABLE_ASSERTIONS:BOOL + + **Default**: ``ON`` + + Toggle assertions independent of the build mode. + +.. option:: LIBUNWIND_ENABLE_PEDANTIC:BOOL + + **Default**: ``ON`` + + Compile with -Wpedantic. + +.. option:: LIBUNWIND_ENABLE_WERROR:BOOL + + **Default**: ``ON`` + + Compile with -Werror + +.. option:: LIBUNWIND_ENABLE_SHARED:BOOL + + **Default**: ``ON`` + + Build libunwind as a shared library. + +.. option:: LIBUNWIND_ENABLE_STATIC:BOOL + + **Default**: ``ON`` + + Build libunwind as a static archive. + +.. option:: LIBUNWIND_ENABLE_CROSS_UNWINDING:BOOL + + **Default**: ``OFF`` + + Enable cross-platform unwinding support. + +.. option:: LIBUNWIND_ENABLE_ARM_WMMX:BOOL + + **Default**: ``OFF`` + + Enable unwinding support for ARM WMMX registers. + +.. option:: LIBUNWIND_ENABLE_THREADS:BOOL + + **Default**: ``ON`` + + Build libunwind with threading support. + +.. option:: LIBUNWIND_TARGET_TRIPLE:STRING + + Target triple for cross compiling + +.. option:: LIBUNWIND_GCC_TOOLCHAIN:PATH + + GCC toolchain for cross compiling + +.. option:: LIBUNWIND_SYSROOT + + Sysroot for cross compiling diff --git a/src/coreclr/nativeaot/libunwind/docs/CMakeLists.txt b/src/coreclr/nativeaot/libunwind/docs/CMakeLists.txt new file mode 100644 index 00000000000000..c226f2f5b8e8d3 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/docs/CMakeLists.txt @@ -0,0 +1,7 @@ +include(FindSphinx) +if (SPHINX_FOUND) + include(AddSphinxTarget) + if (${SPHINX_OUTPUT_HTML}) + add_sphinx_target(html libunwind) + endif() +endif() diff --git a/src/coreclr/nativeaot/libunwind/docs/README.txt b/src/coreclr/nativeaot/libunwind/docs/README.txt new file mode 100644 index 00000000000000..968982fce5e076 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/docs/README.txt @@ -0,0 +1,13 @@ +libunwind Documentation +==================== + +The libunwind documentation is written using the Sphinx documentation generator. It is +currently tested with Sphinx 1.1.3. + +To build the documents into html configure libunwind with the following cmake options: + + * -DLLVM_ENABLE_SPHINX=ON + * -DLIBUNWIND_INCLUDE_DOCS=ON + +After configuring libunwind with these options the make rule `docs-libunwind-html` +should be available. diff --git a/src/coreclr/nativeaot/libunwind/docs/conf.py b/src/coreclr/nativeaot/libunwind/docs/conf.py new file mode 100644 index 00000000000000..704a1d0a12da46 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/docs/conf.py @@ -0,0 +1,252 @@ +# -*- coding: utf-8 -*- +# +# libunwind documentation build configuration file. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os +from datetime import date + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'libunwind' +copyright = u'2011-%d, LLVM Project' % date.today().year + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '9.0' +# The full version, including alpha/beta/rc tags. +release = '9.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +today_fmt = '%Y-%m-%d' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'friendly' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'haiku' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'libunwinddoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('contents', 'libunwind.tex', u'libunwind Documentation', + u'LLVM project', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('contents', 'libunwind', u'libunwind Documentation', + [u'LLVM project'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('contents', 'libunwind', u'libunwind Documentation', + u'LLVM project', 'libunwind', 'LLVM Unwinder', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + + +# FIXME: Define intersphinx configration. +intersphinx_mapping = {} + + +# -- Options for extensions ---------------------------------------------------- + +# Enable this if you want TODOs to show up in the generated documentation. +todo_include_todos = True diff --git a/src/coreclr/nativeaot/libunwind/docs/index.rst b/src/coreclr/nativeaot/libunwind/docs/index.rst new file mode 100644 index 00000000000000..a4e21bb3c336ce --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/docs/index.rst @@ -0,0 +1,104 @@ +.. _index: + +======================= +libunwind LLVM Unwinder +======================= + +Overview +======== + +libunwind is an implementation of the interface defined by the HP libunwind +project. It was contributed by Apple as a way to enable clang++ to port to +platforms that do not have a system unwinder. It is intended to be a small and +fast implementation of the ABI, leaving off some features of HP's libunwind +that never materialized (e.g. remote unwinding). + +The unwinder has two levels of API. The high level APIs are the `_Unwind_*` +functions which implement functionality required by `__cxa_*` exception +functions. The low level APIs are the `unw_*` functions which are an interface +defined by the old HP libunwind project. + +Getting Started with libunwind +------------------------------ + +.. toctree:: + :maxdepth: 2 + + BuildingLibunwind + +Current Status +-------------- + +libunwind is a production-quality unwinder, with platform support for DWARF +unwind info, SjLj, and ARM EHABI. + +The low level libunwind API was designed to work either in-process (aka local) +or to operate on another process (aka remote), but only the local path has been +implemented. Remote unwinding remains as future work. + +Platform and Compiler Support +----------------------------- + +libunwind is known to work on the following platforms: + +============ ======================== ============ ======================== +OS Arch Compilers Unwind Info +============ ======================== ============ ======================== +Any i386, x86_64, ARM Clang SjLj +Bare Metal ARM Clang, GCC EHABI +FreeBSD i386, x86_64, ARM64 Clang DWARF CFI +iOS ARM Clang SjLj +Linux ARM Clang, GCC EHABI +Linux i386, x86_64, ARM64 Clang, GCC DWARF CFI +macOS i386, x86_64 Clang, GCC DWARF CFI +NetBSD x86_64 Clang, GCC DWARF CFI +Windows i386, x86_64, ARM, ARM64 Clang DWARF CFI +============ ======================== ============ ======================== + +The following minimum compiler versions are strongly recommended. + +* Clang 3.5 and above +* GCC 4.7 and above. + +Anything older *may* work. + +Notes and Known Issues +---------------------- + +* TODO + + +Getting Involved +================ + +First please review our `Developer's Policy `__ +and `Getting started with LLVM `__. + +**Bug Reports** + +If you think you've found a bug in libunwind, please report it using +the `LLVM Bugzilla`_. If you're not sure, you +can post a message to the `cfe-dev mailing list`_ or on IRC. +Please include "libunwind" in your subject. + +**Patches** + +If you want to contribute a patch to libunwind, the best place for that is +`Phabricator `_. Please include [libunwind] in the subject and +add `cfe-commits` as a subscriber. Also make sure you are subscribed to the +`cfe-commits mailing list `_. + +**Discussion and Questions** + +Send discussions and questions to the +`cfe-dev mailing list `_. +Please include [libunwind] in the subject. + + +Quick Links +=========== +* `LLVM Homepage `_ +* `LLVM Bugzilla `_ +* `cfe-commits Mailing List`_ +* `cfe-dev Mailing List`_ +* `Browse libunwind Sources `_ diff --git a/src/coreclr/nativeaot/libunwind/include/__libunwind_config.h b/src/coreclr/nativeaot/libunwind/include/__libunwind_config.h new file mode 100644 index 00000000000000..753085c7fe0c0b --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/include/__libunwind_config.h @@ -0,0 +1,145 @@ +//===------------------------- __libunwind_config.h -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef ____LIBUNWIND_CONFIG_H__ +#define ____LIBUNWIND_CONFIG_H__ + +#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ + !defined(__ARM_DWARF_EH__) +#define _LIBUNWIND_ARM_EHABI +#endif + +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86 8 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64 32 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC 112 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64 116 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64 95 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM 287 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K 32 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS 65 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC 31 + +#if defined(_LIBUNWIND_IS_NATIVE_ONLY) +# if defined(__i386__) +# define _LIBUNWIND_TARGET_I386 +# define _LIBUNWIND_CONTEXT_SIZE 13 +# define _LIBUNWIND_CURSOR_SIZE 19 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86 +# elif defined(__x86_64__) +# define _LIBUNWIND_TARGET_X86_64 1 +# if defined(_WIN64) +# define _LIBUNWIND_CONTEXT_SIZE 54 +# ifdef __SEH__ +# define _LIBUNWIND_CURSOR_SIZE 204 +# else +# define _LIBUNWIND_CURSOR_SIZE 66 +# endif +# else +# define _LIBUNWIND_CONTEXT_SIZE 38 +# define _LIBUNWIND_CURSOR_SIZE 50 +# endif +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64 +# elif defined(__powerpc64__) +# define _LIBUNWIND_TARGET_PPC64 1 +# define _LIBUNWIND_CONTEXT_SIZE 167 +# define _LIBUNWIND_CURSOR_SIZE 179 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64 +# elif defined(__ppc__) +# define _LIBUNWIND_TARGET_PPC 1 +# define _LIBUNWIND_CONTEXT_SIZE 117 +# define _LIBUNWIND_CURSOR_SIZE 124 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC +# elif defined(__aarch64__) +# define _LIBUNWIND_TARGET_AARCH64 1 +# define _LIBUNWIND_CONTEXT_SIZE 100 +# if defined(__SEH__) +# define _LIBUNWIND_CURSOR_SIZE 198 +# else +# define _LIBUNWIND_CURSOR_SIZE 112 +# endif +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64 +# elif defined(__arm__) +# define _LIBUNWIND_TARGET_ARM 1 +# if defined(__SEH__) +# define _LIBUNWIND_CONTEXT_SIZE 42 +# define _LIBUNWIND_CURSOR_SIZE 80 +# elif defined(__ARM_WMMX) +# define _LIBUNWIND_CONTEXT_SIZE 61 +# define _LIBUNWIND_CURSOR_SIZE 68 +# else +# define _LIBUNWIND_CONTEXT_SIZE 50 +# define _LIBUNWIND_CURSOR_SIZE 57 +# endif +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM +# elif defined(__or1k__) +# define _LIBUNWIND_TARGET_OR1K 1 +# define _LIBUNWIND_CONTEXT_SIZE 16 +# define _LIBUNWIND_CURSOR_SIZE 24 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K +# elif defined(__mips__) +# if defined(_ABIO32) && _MIPS_SIM == _ABIO32 +# define _LIBUNWIND_TARGET_MIPS_O32 1 +# if defined(__mips_hard_float) +# define _LIBUNWIND_CONTEXT_SIZE 50 +# define _LIBUNWIND_CURSOR_SIZE 57 +# else +# define _LIBUNWIND_CONTEXT_SIZE 18 +# define _LIBUNWIND_CURSOR_SIZE 24 +# endif +# elif defined(_ABIN32) && _MIPS_SIM == _ABIN32 +# define _LIBUNWIND_TARGET_MIPS_NEWABI 1 +# if defined(__mips_hard_float) +# define _LIBUNWIND_CONTEXT_SIZE 67 +# define _LIBUNWIND_CURSOR_SIZE 74 +# else +# define _LIBUNWIND_CONTEXT_SIZE 35 +# define _LIBUNWIND_CURSOR_SIZE 42 +# endif +# elif defined(_ABI64) && _MIPS_SIM == _ABI64 +# define _LIBUNWIND_TARGET_MIPS_NEWABI 1 +# if defined(__mips_hard_float) +# define _LIBUNWIND_CONTEXT_SIZE 67 +# define _LIBUNWIND_CURSOR_SIZE 79 +# else +# define _LIBUNWIND_CONTEXT_SIZE 35 +# define _LIBUNWIND_CURSOR_SIZE 47 +# endif +# else +# error "Unsupported MIPS ABI and/or environment" +# endif +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS +# elif defined(__sparc__) + #define _LIBUNWIND_TARGET_SPARC 1 + #define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC + #define _LIBUNWIND_CONTEXT_SIZE 16 + #define _LIBUNWIND_CURSOR_SIZE 23 +#elif defined(HOST_WASM) +#define _LIBUNWIND_TARGET_WASM 1 +// TODO: Determine the right values +#define _LIBUNWIND_CONTEXT_SIZE 0xbadf00d +#define _LIBUNWIND_CURSOR_SIZE 0xbadf00d +#else +# error "Unsupported architecture." +# endif +#else // !_LIBUNWIND_IS_NATIVE_ONLY +# define _LIBUNWIND_TARGET_I386 +# define _LIBUNWIND_TARGET_X86_64 1 +# define _LIBUNWIND_TARGET_PPC 1 +# define _LIBUNWIND_TARGET_PPC64 1 +# define _LIBUNWIND_TARGET_AARCH64 1 +# define _LIBUNWIND_TARGET_ARM 1 +# define _LIBUNWIND_TARGET_OR1K 1 +# define _LIBUNWIND_TARGET_MIPS_O32 1 +# define _LIBUNWIND_TARGET_MIPS_NEWABI 1 +# define _LIBUNWIND_TARGET_SPARC 1 +# define _LIBUNWIND_CONTEXT_SIZE 167 +# define _LIBUNWIND_CURSOR_SIZE 179 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER 287 +#endif // _LIBUNWIND_IS_NATIVE_ONLY + +#endif // ____LIBUNWIND_CONFIG_H__ diff --git a/src/coreclr/nativeaot/libunwind/include/libunwind.h b/src/coreclr/nativeaot/libunwind/include/libunwind.h new file mode 100644 index 00000000000000..6e70f264f9f361 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/include/libunwind.h @@ -0,0 +1,855 @@ +//===---------------------------- libunwind.h -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Compatible with libunwind API documented at: +// http://www.nongnu.org/libunwind/man/libunwind(3).html +// +//===----------------------------------------------------------------------===// + +#ifndef __LIBUNWIND__ +#define __LIBUNWIND__ + +#include <__libunwind_config.h> + +#include +#include + +#ifdef __APPLE__ + #if __clang__ + #if __has_include() + #include + #endif + #elif __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 + #include + #endif + + #ifdef __arm__ + #define LIBUNWIND_AVAIL __attribute__((unavailable)) + #elif defined(__OSX_AVAILABLE_STARTING) + #define LIBUNWIND_AVAIL __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_5_0) + #else + #include + #ifdef AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER + #define LIBUNWIND_AVAIL AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER + #else + #define LIBUNWIND_AVAIL __attribute__((unavailable)) + #endif + #endif +#else + #define LIBUNWIND_AVAIL +#endif + +/* error codes */ +enum { + UNW_ESUCCESS = 0, /* no error */ + UNW_EUNSPEC = -6540, /* unspecified (general) error */ + UNW_ENOMEM = -6541, /* out of memory */ + UNW_EBADREG = -6542, /* bad register number */ + UNW_EREADONLYREG = -6543, /* attempt to write read-only register */ + UNW_ESTOPUNWIND = -6544, /* stop unwinding */ + UNW_EINVALIDIP = -6545, /* invalid IP */ + UNW_EBADFRAME = -6546, /* bad frame */ + UNW_EINVAL = -6547, /* unsupported operation or bad value */ + UNW_EBADVERSION = -6548, /* unwind info has unsupported version */ + UNW_ENOINFO = -6549 /* no unwind info found */ +#if defined(_LIBUNWIND_TARGET_AARCH64) && !defined(_LIBUNWIND_IS_NATIVE_ONLY) + , UNW_ECROSSRASIGNING = -6550 /* cross unwind with return address signing */ +#endif +}; + +struct unw_context_t { + uint64_t data[_LIBUNWIND_CONTEXT_SIZE]; +}; +typedef struct unw_context_t unw_context_t; + +struct unw_cursor_t { + uint64_t data[_LIBUNWIND_CURSOR_SIZE]; +}; +typedef struct unw_cursor_t unw_cursor_t; + +typedef struct unw_addr_space *unw_addr_space_t; + +typedef int unw_regnum_t; +typedef uintptr_t unw_word_t; +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) +typedef uint64_t unw_fpreg_t; +#else +typedef double unw_fpreg_t; +#endif + +struct unw_proc_info_t { + unw_word_t start_ip; /* start address of function */ + unw_word_t end_ip; /* address after end of function */ + unw_word_t lsda; /* address of language specific data area, */ + /* or zero if not used */ + unw_word_t handler; /* personality routine, or zero if not used */ + unw_word_t gp; /* not used */ + unw_word_t flags; /* not used */ + uint32_t format; /* compact unwind encoding, or zero if none */ + uint32_t unwind_info_size; /* size of DWARF unwind info, or zero if none */ + unw_word_t unwind_info; /* address of DWARF unwind info, or zero */ + unw_word_t extra; /* mach_header of mach-o image containing func */ +}; +typedef struct unw_proc_info_t unw_proc_info_t; + +enum unw_save_loc_type_t +{ + UNW_SLT_NONE, /* register is not saved ("not an l-value") */ + UNW_SLT_MEMORY, /* register has been saved in memory */ + UNW_SLT_REG /* register has been saved in (another) register */ +}; +typedef enum unw_save_loc_type_t unw_save_loc_type_t; + +struct unw_save_loc_t +{ + unw_save_loc_type_t type; + union + { + unw_word_t addr; /* valid if type==UNW_SLT_MEMORY */ + unw_regnum_t regnum; /* valid if type==UNW_SLT_REG */ + } + u; +}; +typedef struct unw_save_loc_t unw_save_loc_t; + +#ifdef __cplusplus +extern "C" { +#endif + +extern int unw_getcontext(unw_context_t *) LIBUNWIND_AVAIL; +extern int unw_init_local(unw_cursor_t *, unw_context_t *) LIBUNWIND_AVAIL; +extern int unw_step(unw_cursor_t *) LIBUNWIND_AVAIL; +extern int unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *) LIBUNWIND_AVAIL; +extern int unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *) LIBUNWIND_AVAIL; +extern int unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t, unw_word_t *) LIBUNWIND_AVAIL; +extern int unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t) LIBUNWIND_AVAIL; +extern int unw_resume(unw_cursor_t *) LIBUNWIND_AVAIL; + +#ifdef __arm__ +/* Save VFP registers in FSTMX format (instead of FSTMD). */ +extern void unw_save_vfp_as_X(unw_cursor_t *) LIBUNWIND_AVAIL; +#endif + + +extern const char *unw_regname(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL; +extern int unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *) LIBUNWIND_AVAIL; +extern int unw_is_fpreg(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL; +extern int unw_is_signal_frame(unw_cursor_t *) LIBUNWIND_AVAIL; +extern int unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *) LIBUNWIND_AVAIL; +extern int unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*) LIBUNWIND_AVAIL; + +extern unw_addr_space_t unw_local_addr_space; + +#ifdef __cplusplus +} +#endif + +// architecture independent register numbers +enum { + UNW_REG_IP = -1, // instruction pointer + UNW_REG_SP = -2, // stack pointer +}; + +// 32-bit x86 registers +enum { + UNW_X86_EAX = 0, + UNW_X86_ECX = 1, + UNW_X86_EDX = 2, + UNW_X86_EBX = 3, + UNW_X86_EBP = 4, + UNW_X86_ESP = 5, + UNW_X86_ESI = 6, + UNW_X86_EDI = 7 +}; + +// 64-bit x86_64 registers +enum { + UNW_X86_64_RAX = 0, + UNW_X86_64_RDX = 1, + UNW_X86_64_RCX = 2, + UNW_X86_64_RBX = 3, + UNW_X86_64_RSI = 4, + UNW_X86_64_RDI = 5, + UNW_X86_64_RBP = 6, + UNW_X86_64_RSP = 7, + UNW_X86_64_R8 = 8, + UNW_X86_64_R9 = 9, + UNW_X86_64_R10 = 10, + UNW_X86_64_R11 = 11, + UNW_X86_64_R12 = 12, + UNW_X86_64_R13 = 13, + UNW_X86_64_R14 = 14, + UNW_X86_64_R15 = 15, + UNW_X86_64_RIP = 16, + UNW_X86_64_XMM0 = 17, + UNW_X86_64_XMM1 = 18, + UNW_X86_64_XMM2 = 19, + UNW_X86_64_XMM3 = 20, + UNW_X86_64_XMM4 = 21, + UNW_X86_64_XMM5 = 22, + UNW_X86_64_XMM6 = 23, + UNW_X86_64_XMM7 = 24, + UNW_X86_64_XMM8 = 25, + UNW_X86_64_XMM9 = 26, + UNW_X86_64_XMM10 = 27, + UNW_X86_64_XMM11 = 28, + UNW_X86_64_XMM12 = 29, + UNW_X86_64_XMM13 = 30, + UNW_X86_64_XMM14 = 31, + UNW_X86_64_XMM15 = 32, +}; + + +// 32-bit ppc register numbers +enum { + UNW_PPC_R0 = 0, + UNW_PPC_R1 = 1, + UNW_PPC_R2 = 2, + UNW_PPC_R3 = 3, + UNW_PPC_R4 = 4, + UNW_PPC_R5 = 5, + UNW_PPC_R6 = 6, + UNW_PPC_R7 = 7, + UNW_PPC_R8 = 8, + UNW_PPC_R9 = 9, + UNW_PPC_R10 = 10, + UNW_PPC_R11 = 11, + UNW_PPC_R12 = 12, + UNW_PPC_R13 = 13, + UNW_PPC_R14 = 14, + UNW_PPC_R15 = 15, + UNW_PPC_R16 = 16, + UNW_PPC_R17 = 17, + UNW_PPC_R18 = 18, + UNW_PPC_R19 = 19, + UNW_PPC_R20 = 20, + UNW_PPC_R21 = 21, + UNW_PPC_R22 = 22, + UNW_PPC_R23 = 23, + UNW_PPC_R24 = 24, + UNW_PPC_R25 = 25, + UNW_PPC_R26 = 26, + UNW_PPC_R27 = 27, + UNW_PPC_R28 = 28, + UNW_PPC_R29 = 29, + UNW_PPC_R30 = 30, + UNW_PPC_R31 = 31, + UNW_PPC_F0 = 32, + UNW_PPC_F1 = 33, + UNW_PPC_F2 = 34, + UNW_PPC_F3 = 35, + UNW_PPC_F4 = 36, + UNW_PPC_F5 = 37, + UNW_PPC_F6 = 38, + UNW_PPC_F7 = 39, + UNW_PPC_F8 = 40, + UNW_PPC_F9 = 41, + UNW_PPC_F10 = 42, + UNW_PPC_F11 = 43, + UNW_PPC_F12 = 44, + UNW_PPC_F13 = 45, + UNW_PPC_F14 = 46, + UNW_PPC_F15 = 47, + UNW_PPC_F16 = 48, + UNW_PPC_F17 = 49, + UNW_PPC_F18 = 50, + UNW_PPC_F19 = 51, + UNW_PPC_F20 = 52, + UNW_PPC_F21 = 53, + UNW_PPC_F22 = 54, + UNW_PPC_F23 = 55, + UNW_PPC_F24 = 56, + UNW_PPC_F25 = 57, + UNW_PPC_F26 = 58, + UNW_PPC_F27 = 59, + UNW_PPC_F28 = 60, + UNW_PPC_F29 = 61, + UNW_PPC_F30 = 62, + UNW_PPC_F31 = 63, + UNW_PPC_MQ = 64, + UNW_PPC_LR = 65, + UNW_PPC_CTR = 66, + UNW_PPC_AP = 67, + UNW_PPC_CR0 = 68, + UNW_PPC_CR1 = 69, + UNW_PPC_CR2 = 70, + UNW_PPC_CR3 = 71, + UNW_PPC_CR4 = 72, + UNW_PPC_CR5 = 73, + UNW_PPC_CR6 = 74, + UNW_PPC_CR7 = 75, + UNW_PPC_XER = 76, + UNW_PPC_V0 = 77, + UNW_PPC_V1 = 78, + UNW_PPC_V2 = 79, + UNW_PPC_V3 = 80, + UNW_PPC_V4 = 81, + UNW_PPC_V5 = 82, + UNW_PPC_V6 = 83, + UNW_PPC_V7 = 84, + UNW_PPC_V8 = 85, + UNW_PPC_V9 = 86, + UNW_PPC_V10 = 87, + UNW_PPC_V11 = 88, + UNW_PPC_V12 = 89, + UNW_PPC_V13 = 90, + UNW_PPC_V14 = 91, + UNW_PPC_V15 = 92, + UNW_PPC_V16 = 93, + UNW_PPC_V17 = 94, + UNW_PPC_V18 = 95, + UNW_PPC_V19 = 96, + UNW_PPC_V20 = 97, + UNW_PPC_V21 = 98, + UNW_PPC_V22 = 99, + UNW_PPC_V23 = 100, + UNW_PPC_V24 = 101, + UNW_PPC_V25 = 102, + UNW_PPC_V26 = 103, + UNW_PPC_V27 = 104, + UNW_PPC_V28 = 105, + UNW_PPC_V29 = 106, + UNW_PPC_V30 = 107, + UNW_PPC_V31 = 108, + UNW_PPC_VRSAVE = 109, + UNW_PPC_VSCR = 110, + UNW_PPC_SPE_ACC = 111, + UNW_PPC_SPEFSCR = 112 +}; + +// 64-bit ppc register numbers +enum { + UNW_PPC64_R0 = 0, + UNW_PPC64_R1 = 1, + UNW_PPC64_R2 = 2, + UNW_PPC64_R3 = 3, + UNW_PPC64_R4 = 4, + UNW_PPC64_R5 = 5, + UNW_PPC64_R6 = 6, + UNW_PPC64_R7 = 7, + UNW_PPC64_R8 = 8, + UNW_PPC64_R9 = 9, + UNW_PPC64_R10 = 10, + UNW_PPC64_R11 = 11, + UNW_PPC64_R12 = 12, + UNW_PPC64_R13 = 13, + UNW_PPC64_R14 = 14, + UNW_PPC64_R15 = 15, + UNW_PPC64_R16 = 16, + UNW_PPC64_R17 = 17, + UNW_PPC64_R18 = 18, + UNW_PPC64_R19 = 19, + UNW_PPC64_R20 = 20, + UNW_PPC64_R21 = 21, + UNW_PPC64_R22 = 22, + UNW_PPC64_R23 = 23, + UNW_PPC64_R24 = 24, + UNW_PPC64_R25 = 25, + UNW_PPC64_R26 = 26, + UNW_PPC64_R27 = 27, + UNW_PPC64_R28 = 28, + UNW_PPC64_R29 = 29, + UNW_PPC64_R30 = 30, + UNW_PPC64_R31 = 31, + UNW_PPC64_F0 = 32, + UNW_PPC64_F1 = 33, + UNW_PPC64_F2 = 34, + UNW_PPC64_F3 = 35, + UNW_PPC64_F4 = 36, + UNW_PPC64_F5 = 37, + UNW_PPC64_F6 = 38, + UNW_PPC64_F7 = 39, + UNW_PPC64_F8 = 40, + UNW_PPC64_F9 = 41, + UNW_PPC64_F10 = 42, + UNW_PPC64_F11 = 43, + UNW_PPC64_F12 = 44, + UNW_PPC64_F13 = 45, + UNW_PPC64_F14 = 46, + UNW_PPC64_F15 = 47, + UNW_PPC64_F16 = 48, + UNW_PPC64_F17 = 49, + UNW_PPC64_F18 = 50, + UNW_PPC64_F19 = 51, + UNW_PPC64_F20 = 52, + UNW_PPC64_F21 = 53, + UNW_PPC64_F22 = 54, + UNW_PPC64_F23 = 55, + UNW_PPC64_F24 = 56, + UNW_PPC64_F25 = 57, + UNW_PPC64_F26 = 58, + UNW_PPC64_F27 = 59, + UNW_PPC64_F28 = 60, + UNW_PPC64_F29 = 61, + UNW_PPC64_F30 = 62, + UNW_PPC64_F31 = 63, + // 64: reserved + UNW_PPC64_LR = 65, + UNW_PPC64_CTR = 66, + // 67: reserved + UNW_PPC64_CR0 = 68, + UNW_PPC64_CR1 = 69, + UNW_PPC64_CR2 = 70, + UNW_PPC64_CR3 = 71, + UNW_PPC64_CR4 = 72, + UNW_PPC64_CR5 = 73, + UNW_PPC64_CR6 = 74, + UNW_PPC64_CR7 = 75, + UNW_PPC64_XER = 76, + UNW_PPC64_V0 = 77, + UNW_PPC64_V1 = 78, + UNW_PPC64_V2 = 79, + UNW_PPC64_V3 = 80, + UNW_PPC64_V4 = 81, + UNW_PPC64_V5 = 82, + UNW_PPC64_V6 = 83, + UNW_PPC64_V7 = 84, + UNW_PPC64_V8 = 85, + UNW_PPC64_V9 = 86, + UNW_PPC64_V10 = 87, + UNW_PPC64_V11 = 88, + UNW_PPC64_V12 = 89, + UNW_PPC64_V13 = 90, + UNW_PPC64_V14 = 91, + UNW_PPC64_V15 = 92, + UNW_PPC64_V16 = 93, + UNW_PPC64_V17 = 94, + UNW_PPC64_V18 = 95, + UNW_PPC64_V19 = 96, + UNW_PPC64_V20 = 97, + UNW_PPC64_V21 = 98, + UNW_PPC64_V22 = 99, + UNW_PPC64_V23 = 100, + UNW_PPC64_V24 = 101, + UNW_PPC64_V25 = 102, + UNW_PPC64_V26 = 103, + UNW_PPC64_V27 = 104, + UNW_PPC64_V28 = 105, + UNW_PPC64_V29 = 106, + UNW_PPC64_V30 = 107, + UNW_PPC64_V31 = 108, + // 109, 111-113: OpenPOWER ELF V2 ABI: reserved + // Borrowing VRSAVE number from PPC32. + UNW_PPC64_VRSAVE = 109, + UNW_PPC64_VSCR = 110, + UNW_PPC64_TFHAR = 114, + UNW_PPC64_TFIAR = 115, + UNW_PPC64_TEXASR = 116, + UNW_PPC64_VS0 = UNW_PPC64_F0, + UNW_PPC64_VS1 = UNW_PPC64_F1, + UNW_PPC64_VS2 = UNW_PPC64_F2, + UNW_PPC64_VS3 = UNW_PPC64_F3, + UNW_PPC64_VS4 = UNW_PPC64_F4, + UNW_PPC64_VS5 = UNW_PPC64_F5, + UNW_PPC64_VS6 = UNW_PPC64_F6, + UNW_PPC64_VS7 = UNW_PPC64_F7, + UNW_PPC64_VS8 = UNW_PPC64_F8, + UNW_PPC64_VS9 = UNW_PPC64_F9, + UNW_PPC64_VS10 = UNW_PPC64_F10, + UNW_PPC64_VS11 = UNW_PPC64_F11, + UNW_PPC64_VS12 = UNW_PPC64_F12, + UNW_PPC64_VS13 = UNW_PPC64_F13, + UNW_PPC64_VS14 = UNW_PPC64_F14, + UNW_PPC64_VS15 = UNW_PPC64_F15, + UNW_PPC64_VS16 = UNW_PPC64_F16, + UNW_PPC64_VS17 = UNW_PPC64_F17, + UNW_PPC64_VS18 = UNW_PPC64_F18, + UNW_PPC64_VS19 = UNW_PPC64_F19, + UNW_PPC64_VS20 = UNW_PPC64_F20, + UNW_PPC64_VS21 = UNW_PPC64_F21, + UNW_PPC64_VS22 = UNW_PPC64_F22, + UNW_PPC64_VS23 = UNW_PPC64_F23, + UNW_PPC64_VS24 = UNW_PPC64_F24, + UNW_PPC64_VS25 = UNW_PPC64_F25, + UNW_PPC64_VS26 = UNW_PPC64_F26, + UNW_PPC64_VS27 = UNW_PPC64_F27, + UNW_PPC64_VS28 = UNW_PPC64_F28, + UNW_PPC64_VS29 = UNW_PPC64_F29, + UNW_PPC64_VS30 = UNW_PPC64_F30, + UNW_PPC64_VS31 = UNW_PPC64_F31, + UNW_PPC64_VS32 = UNW_PPC64_V0, + UNW_PPC64_VS33 = UNW_PPC64_V1, + UNW_PPC64_VS34 = UNW_PPC64_V2, + UNW_PPC64_VS35 = UNW_PPC64_V3, + UNW_PPC64_VS36 = UNW_PPC64_V4, + UNW_PPC64_VS37 = UNW_PPC64_V5, + UNW_PPC64_VS38 = UNW_PPC64_V6, + UNW_PPC64_VS39 = UNW_PPC64_V7, + UNW_PPC64_VS40 = UNW_PPC64_V8, + UNW_PPC64_VS41 = UNW_PPC64_V9, + UNW_PPC64_VS42 = UNW_PPC64_V10, + UNW_PPC64_VS43 = UNW_PPC64_V11, + UNW_PPC64_VS44 = UNW_PPC64_V12, + UNW_PPC64_VS45 = UNW_PPC64_V13, + UNW_PPC64_VS46 = UNW_PPC64_V14, + UNW_PPC64_VS47 = UNW_PPC64_V15, + UNW_PPC64_VS48 = UNW_PPC64_V16, + UNW_PPC64_VS49 = UNW_PPC64_V17, + UNW_PPC64_VS50 = UNW_PPC64_V18, + UNW_PPC64_VS51 = UNW_PPC64_V19, + UNW_PPC64_VS52 = UNW_PPC64_V20, + UNW_PPC64_VS53 = UNW_PPC64_V21, + UNW_PPC64_VS54 = UNW_PPC64_V22, + UNW_PPC64_VS55 = UNW_PPC64_V23, + UNW_PPC64_VS56 = UNW_PPC64_V24, + UNW_PPC64_VS57 = UNW_PPC64_V25, + UNW_PPC64_VS58 = UNW_PPC64_V26, + UNW_PPC64_VS59 = UNW_PPC64_V27, + UNW_PPC64_VS60 = UNW_PPC64_V28, + UNW_PPC64_VS61 = UNW_PPC64_V29, + UNW_PPC64_VS62 = UNW_PPC64_V30, + UNW_PPC64_VS63 = UNW_PPC64_V31 +}; + +// 64-bit ARM64 registers +enum { + UNW_ARM64_X0 = 0, + UNW_ARM64_X1 = 1, + UNW_ARM64_X2 = 2, + UNW_ARM64_X3 = 3, + UNW_ARM64_X4 = 4, + UNW_ARM64_X5 = 5, + UNW_ARM64_X6 = 6, + UNW_ARM64_X7 = 7, + UNW_ARM64_X8 = 8, + UNW_ARM64_X9 = 9, + UNW_ARM64_X10 = 10, + UNW_ARM64_X11 = 11, + UNW_ARM64_X12 = 12, + UNW_ARM64_X13 = 13, + UNW_ARM64_X14 = 14, + UNW_ARM64_X15 = 15, + UNW_ARM64_X16 = 16, + UNW_ARM64_X17 = 17, + UNW_ARM64_X18 = 18, + UNW_ARM64_X19 = 19, + UNW_ARM64_X20 = 20, + UNW_ARM64_X21 = 21, + UNW_ARM64_X22 = 22, + UNW_ARM64_X23 = 23, + UNW_ARM64_X24 = 24, + UNW_ARM64_X25 = 25, + UNW_ARM64_X26 = 26, + UNW_ARM64_X27 = 27, + UNW_ARM64_X28 = 28, + UNW_ARM64_X29 = 29, + UNW_ARM64_FP = 29, + UNW_ARM64_X30 = 30, + UNW_ARM64_LR = 30, + UNW_ARM64_X31 = 31, + UNW_ARM64_SP = 31, + // reserved block + UNW_ARM64_RA_SIGN_STATE = 34, + // reserved block + UNW_ARM64_D0 = 64, + UNW_ARM64_D1 = 65, + UNW_ARM64_D2 = 66, + UNW_ARM64_D3 = 67, + UNW_ARM64_D4 = 68, + UNW_ARM64_D5 = 69, + UNW_ARM64_D6 = 70, + UNW_ARM64_D7 = 71, + UNW_ARM64_D8 = 72, + UNW_ARM64_D9 = 73, + UNW_ARM64_D10 = 74, + UNW_ARM64_D11 = 75, + UNW_ARM64_D12 = 76, + UNW_ARM64_D13 = 77, + UNW_ARM64_D14 = 78, + UNW_ARM64_D15 = 79, + UNW_ARM64_D16 = 80, + UNW_ARM64_D17 = 81, + UNW_ARM64_D18 = 82, + UNW_ARM64_D19 = 83, + UNW_ARM64_D20 = 84, + UNW_ARM64_D21 = 85, + UNW_ARM64_D22 = 86, + UNW_ARM64_D23 = 87, + UNW_ARM64_D24 = 88, + UNW_ARM64_D25 = 89, + UNW_ARM64_D26 = 90, + UNW_ARM64_D27 = 91, + UNW_ARM64_D28 = 92, + UNW_ARM64_D29 = 93, + UNW_ARM64_D30 = 94, + UNW_ARM64_D31 = 95, +}; + +// 32-bit ARM registers. Numbers match DWARF for ARM spec #3.1 Table 1. +// Naming scheme uses recommendations given in Note 4 for VFP-v2 and VFP-v3. +// In this scheme, even though the 64-bit floating point registers D0-D31 +// overlap physically with the 32-bit floating pointer registers S0-S31, +// they are given a non-overlapping range of register numbers. +// +// Commented out ranges are not preserved during unwinding. +enum { + UNW_ARM_R0 = 0, + UNW_ARM_R1 = 1, + UNW_ARM_R2 = 2, + UNW_ARM_R3 = 3, + UNW_ARM_R4 = 4, + UNW_ARM_R5 = 5, + UNW_ARM_R6 = 6, + UNW_ARM_R7 = 7, + UNW_ARM_R8 = 8, + UNW_ARM_R9 = 9, + UNW_ARM_R10 = 10, + UNW_ARM_R11 = 11, + UNW_ARM_R12 = 12, + UNW_ARM_SP = 13, // Logical alias for UNW_REG_SP + UNW_ARM_R13 = 13, + UNW_ARM_LR = 14, + UNW_ARM_R14 = 14, + UNW_ARM_IP = 15, // Logical alias for UNW_REG_IP + UNW_ARM_R15 = 15, + // 16-63 -- OBSOLETE. Used in VFP1 to represent both S0-S31 and D0-D31. + UNW_ARM_S0 = 64, + UNW_ARM_S1 = 65, + UNW_ARM_S2 = 66, + UNW_ARM_S3 = 67, + UNW_ARM_S4 = 68, + UNW_ARM_S5 = 69, + UNW_ARM_S6 = 70, + UNW_ARM_S7 = 71, + UNW_ARM_S8 = 72, + UNW_ARM_S9 = 73, + UNW_ARM_S10 = 74, + UNW_ARM_S11 = 75, + UNW_ARM_S12 = 76, + UNW_ARM_S13 = 77, + UNW_ARM_S14 = 78, + UNW_ARM_S15 = 79, + UNW_ARM_S16 = 80, + UNW_ARM_S17 = 81, + UNW_ARM_S18 = 82, + UNW_ARM_S19 = 83, + UNW_ARM_S20 = 84, + UNW_ARM_S21 = 85, + UNW_ARM_S22 = 86, + UNW_ARM_S23 = 87, + UNW_ARM_S24 = 88, + UNW_ARM_S25 = 89, + UNW_ARM_S26 = 90, + UNW_ARM_S27 = 91, + UNW_ARM_S28 = 92, + UNW_ARM_S29 = 93, + UNW_ARM_S30 = 94, + UNW_ARM_S31 = 95, + // 96-103 -- OBSOLETE. F0-F7. Used by the FPA system. Superseded by VFP. + // 104-111 -- wCGR0-wCGR7, ACC0-ACC7 (Intel wireless MMX) + UNW_ARM_WR0 = 112, + UNW_ARM_WR1 = 113, + UNW_ARM_WR2 = 114, + UNW_ARM_WR3 = 115, + UNW_ARM_WR4 = 116, + UNW_ARM_WR5 = 117, + UNW_ARM_WR6 = 118, + UNW_ARM_WR7 = 119, + UNW_ARM_WR8 = 120, + UNW_ARM_WR9 = 121, + UNW_ARM_WR10 = 122, + UNW_ARM_WR11 = 123, + UNW_ARM_WR12 = 124, + UNW_ARM_WR13 = 125, + UNW_ARM_WR14 = 126, + UNW_ARM_WR15 = 127, + // 128-133 -- SPSR, SPSR_{FIQ|IRQ|ABT|UND|SVC} + // 134-143 -- Reserved + // 144-150 -- R8_USR-R14_USR + // 151-157 -- R8_FIQ-R14_FIQ + // 158-159 -- R13_IRQ-R14_IRQ + // 160-161 -- R13_ABT-R14_ABT + // 162-163 -- R13_UND-R14_UND + // 164-165 -- R13_SVC-R14_SVC + // 166-191 -- Reserved + UNW_ARM_WC0 = 192, + UNW_ARM_WC1 = 193, + UNW_ARM_WC2 = 194, + UNW_ARM_WC3 = 195, + // 196-199 -- wC4-wC7 (Intel wireless MMX control) + // 200-255 -- Reserved + UNW_ARM_D0 = 256, + UNW_ARM_D1 = 257, + UNW_ARM_D2 = 258, + UNW_ARM_D3 = 259, + UNW_ARM_D4 = 260, + UNW_ARM_D5 = 261, + UNW_ARM_D6 = 262, + UNW_ARM_D7 = 263, + UNW_ARM_D8 = 264, + UNW_ARM_D9 = 265, + UNW_ARM_D10 = 266, + UNW_ARM_D11 = 267, + UNW_ARM_D12 = 268, + UNW_ARM_D13 = 269, + UNW_ARM_D14 = 270, + UNW_ARM_D15 = 271, + UNW_ARM_D16 = 272, + UNW_ARM_D17 = 273, + UNW_ARM_D18 = 274, + UNW_ARM_D19 = 275, + UNW_ARM_D20 = 276, + UNW_ARM_D21 = 277, + UNW_ARM_D22 = 278, + UNW_ARM_D23 = 279, + UNW_ARM_D24 = 280, + UNW_ARM_D25 = 281, + UNW_ARM_D26 = 282, + UNW_ARM_D27 = 283, + UNW_ARM_D28 = 284, + UNW_ARM_D29 = 285, + UNW_ARM_D30 = 286, + UNW_ARM_D31 = 287, + // 288-319 -- Reserved for VFP/Neon + // 320-8191 -- Reserved + // 8192-16383 -- Unspecified vendor co-processor register. +}; + +// OpenRISC1000 register numbers +enum { + UNW_OR1K_R0 = 0, + UNW_OR1K_R1 = 1, + UNW_OR1K_R2 = 2, + UNW_OR1K_R3 = 3, + UNW_OR1K_R4 = 4, + UNW_OR1K_R5 = 5, + UNW_OR1K_R6 = 6, + UNW_OR1K_R7 = 7, + UNW_OR1K_R8 = 8, + UNW_OR1K_R9 = 9, + UNW_OR1K_R10 = 10, + UNW_OR1K_R11 = 11, + UNW_OR1K_R12 = 12, + UNW_OR1K_R13 = 13, + UNW_OR1K_R14 = 14, + UNW_OR1K_R15 = 15, + UNW_OR1K_R16 = 16, + UNW_OR1K_R17 = 17, + UNW_OR1K_R18 = 18, + UNW_OR1K_R19 = 19, + UNW_OR1K_R20 = 20, + UNW_OR1K_R21 = 21, + UNW_OR1K_R22 = 22, + UNW_OR1K_R23 = 23, + UNW_OR1K_R24 = 24, + UNW_OR1K_R25 = 25, + UNW_OR1K_R26 = 26, + UNW_OR1K_R27 = 27, + UNW_OR1K_R28 = 28, + UNW_OR1K_R29 = 29, + UNW_OR1K_R30 = 30, + UNW_OR1K_R31 = 31, + UNW_OR1K_EPCR = 32, +}; + +// MIPS registers +enum { + UNW_MIPS_R0 = 0, + UNW_MIPS_R1 = 1, + UNW_MIPS_R2 = 2, + UNW_MIPS_R3 = 3, + UNW_MIPS_R4 = 4, + UNW_MIPS_R5 = 5, + UNW_MIPS_R6 = 6, + UNW_MIPS_R7 = 7, + UNW_MIPS_R8 = 8, + UNW_MIPS_R9 = 9, + UNW_MIPS_R10 = 10, + UNW_MIPS_R11 = 11, + UNW_MIPS_R12 = 12, + UNW_MIPS_R13 = 13, + UNW_MIPS_R14 = 14, + UNW_MIPS_R15 = 15, + UNW_MIPS_R16 = 16, + UNW_MIPS_R17 = 17, + UNW_MIPS_R18 = 18, + UNW_MIPS_R19 = 19, + UNW_MIPS_R20 = 20, + UNW_MIPS_R21 = 21, + UNW_MIPS_R22 = 22, + UNW_MIPS_R23 = 23, + UNW_MIPS_R24 = 24, + UNW_MIPS_R25 = 25, + UNW_MIPS_R26 = 26, + UNW_MIPS_R27 = 27, + UNW_MIPS_R28 = 28, + UNW_MIPS_R29 = 29, + UNW_MIPS_R30 = 30, + UNW_MIPS_R31 = 31, + UNW_MIPS_F0 = 32, + UNW_MIPS_F1 = 33, + UNW_MIPS_F2 = 34, + UNW_MIPS_F3 = 35, + UNW_MIPS_F4 = 36, + UNW_MIPS_F5 = 37, + UNW_MIPS_F6 = 38, + UNW_MIPS_F7 = 39, + UNW_MIPS_F8 = 40, + UNW_MIPS_F9 = 41, + UNW_MIPS_F10 = 42, + UNW_MIPS_F11 = 43, + UNW_MIPS_F12 = 44, + UNW_MIPS_F13 = 45, + UNW_MIPS_F14 = 46, + UNW_MIPS_F15 = 47, + UNW_MIPS_F16 = 48, + UNW_MIPS_F17 = 49, + UNW_MIPS_F18 = 50, + UNW_MIPS_F19 = 51, + UNW_MIPS_F20 = 52, + UNW_MIPS_F21 = 53, + UNW_MIPS_F22 = 54, + UNW_MIPS_F23 = 55, + UNW_MIPS_F24 = 56, + UNW_MIPS_F25 = 57, + UNW_MIPS_F26 = 58, + UNW_MIPS_F27 = 59, + UNW_MIPS_F28 = 60, + UNW_MIPS_F29 = 61, + UNW_MIPS_F30 = 62, + UNW_MIPS_F31 = 63, + UNW_MIPS_HI = 64, + UNW_MIPS_LO = 65, +}; + +// SPARC registers +enum { + UNW_SPARC_G0 = 0, + UNW_SPARC_G1 = 1, + UNW_SPARC_G2 = 2, + UNW_SPARC_G3 = 3, + UNW_SPARC_G4 = 4, + UNW_SPARC_G5 = 5, + UNW_SPARC_G6 = 6, + UNW_SPARC_G7 = 7, + UNW_SPARC_O0 = 8, + UNW_SPARC_O1 = 9, + UNW_SPARC_O2 = 10, + UNW_SPARC_O3 = 11, + UNW_SPARC_O4 = 12, + UNW_SPARC_O5 = 13, + UNW_SPARC_O6 = 14, + UNW_SPARC_O7 = 15, + UNW_SPARC_L0 = 16, + UNW_SPARC_L1 = 17, + UNW_SPARC_L2 = 18, + UNW_SPARC_L3 = 19, + UNW_SPARC_L4 = 20, + UNW_SPARC_L5 = 21, + UNW_SPARC_L6 = 22, + UNW_SPARC_L7 = 23, + UNW_SPARC_I0 = 24, + UNW_SPARC_I1 = 25, + UNW_SPARC_I2 = 26, + UNW_SPARC_I3 = 27, + UNW_SPARC_I4 = 28, + UNW_SPARC_I5 = 29, + UNW_SPARC_I6 = 30, + UNW_SPARC_I7 = 31, +}; + +#endif diff --git a/src/coreclr/nativeaot/libunwind/include/mach-o/compact_unwind_encoding.h b/src/coreclr/nativeaot/libunwind/include/mach-o/compact_unwind_encoding.h new file mode 100644 index 00000000000000..5301b1055ef930 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/include/mach-o/compact_unwind_encoding.h @@ -0,0 +1,477 @@ +//===------------------ mach-o/compact_unwind_encoding.h ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Darwin's alternative to DWARF based unwind encodings. +// +//===----------------------------------------------------------------------===// + + +#ifndef __COMPACT_UNWIND_ENCODING__ +#define __COMPACT_UNWIND_ENCODING__ + +#include + +// +// Compilers can emit standard DWARF FDEs in the __TEXT,__eh_frame section +// of object files. Or compilers can emit compact unwind information in +// the __LD,__compact_unwind section. +// +// When the linker creates a final linked image, it will create a +// __TEXT,__unwind_info section. This section is a small and fast way for the +// runtime to access unwind info for any given function. If the compiler +// emitted compact unwind info for the function, that compact unwind info will +// be encoded in the __TEXT,__unwind_info section. If the compiler emitted +// DWARF unwind info, the __TEXT,__unwind_info section will contain the offset +// of the FDE in the __TEXT,__eh_frame section in the final linked image. +// +// Note: Previously, the linker would transform some DWARF unwind infos into +// compact unwind info. But that is fragile and no longer done. + + +// +// The compact unwind endoding is a 32-bit value which encoded in an +// architecture specific way, which registers to restore from where, and how +// to unwind out of the function. +// +typedef uint32_t compact_unwind_encoding_t; + + +// architecture independent bits +enum { + UNWIND_IS_NOT_FUNCTION_START = 0x80000000, + UNWIND_HAS_LSDA = 0x40000000, + UNWIND_PERSONALITY_MASK = 0x30000000, +}; + + + + +// +// x86 +// +// 1-bit: start +// 1-bit: has lsda +// 2-bit: personality index +// +// 4-bits: 0=old, 1=ebp based, 2=stack-imm, 3=stack-ind, 4=DWARF +// ebp based: +// 15-bits (5*3-bits per reg) register permutation +// 8-bits for stack offset +// frameless: +// 8-bits stack size +// 3-bits stack adjust +// 3-bits register count +// 10-bits register permutation +// +enum { + UNWIND_X86_MODE_MASK = 0x0F000000, + UNWIND_X86_MODE_EBP_FRAME = 0x01000000, + UNWIND_X86_MODE_STACK_IMMD = 0x02000000, + UNWIND_X86_MODE_STACK_IND = 0x03000000, + UNWIND_X86_MODE_DWARF = 0x04000000, + + UNWIND_X86_EBP_FRAME_REGISTERS = 0x00007FFF, + UNWIND_X86_EBP_FRAME_OFFSET = 0x00FF0000, + + UNWIND_X86_FRAMELESS_STACK_SIZE = 0x00FF0000, + UNWIND_X86_FRAMELESS_STACK_ADJUST = 0x0000E000, + UNWIND_X86_FRAMELESS_STACK_REG_COUNT = 0x00001C00, + UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF, + + UNWIND_X86_DWARF_SECTION_OFFSET = 0x00FFFFFF, +}; + +enum { + UNWIND_X86_REG_NONE = 0, + UNWIND_X86_REG_EBX = 1, + UNWIND_X86_REG_ECX = 2, + UNWIND_X86_REG_EDX = 3, + UNWIND_X86_REG_EDI = 4, + UNWIND_X86_REG_ESI = 5, + UNWIND_X86_REG_EBP = 6, +}; + +// +// For x86 there are four modes for the compact unwind encoding: +// UNWIND_X86_MODE_EBP_FRAME: +// EBP based frame where EBP is push on stack immediately after return address, +// then ESP is moved to EBP. Thus, to unwind ESP is restored with the current +// EPB value, then EBP is restored by popping off the stack, and the return +// is done by popping the stack once more into the pc. +// All non-volatile registers that need to be restored must have been saved +// in a small range in the stack that starts EBP-4 to EBP-1020. The offset/4 +// is encoded in the UNWIND_X86_EBP_FRAME_OFFSET bits. The registers saved +// are encoded in the UNWIND_X86_EBP_FRAME_REGISTERS bits as five 3-bit entries. +// Each entry contains which register to restore. +// UNWIND_X86_MODE_STACK_IMMD: +// A "frameless" (EBP not used as frame pointer) function with a small +// constant stack size. To return, a constant (encoded in the compact +// unwind encoding) is added to the ESP. Then the return is done by +// popping the stack into the pc. +// All non-volatile registers that need to be restored must have been saved +// on the stack immediately after the return address. The stack_size/4 is +// encoded in the UNWIND_X86_FRAMELESS_STACK_SIZE (max stack size is 1024). +// The number of registers saved is encoded in UNWIND_X86_FRAMELESS_STACK_REG_COUNT. +// UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION constains which registers were +// saved and their order. +// UNWIND_X86_MODE_STACK_IND: +// A "frameless" (EBP not used as frame pointer) function large constant +// stack size. This case is like the previous, except the stack size is too +// large to encode in the compact unwind encoding. Instead it requires that +// the function contains "subl $nnnnnnnn,ESP" in its prolog. The compact +// encoding contains the offset to the nnnnnnnn value in the function in +// UNWIND_X86_FRAMELESS_STACK_SIZE. +// UNWIND_X86_MODE_DWARF: +// No compact unwind encoding is available. Instead the low 24-bits of the +// compact encoding is the offset of the DWARF FDE in the __eh_frame section. +// This mode is never used in object files. It is only generated by the +// linker in final linked images which have only DWARF unwind info for a +// function. +// +// The permutation encoding is a Lehmer code sequence encoded into a +// single variable-base number so we can encode the ordering of up to +// six registers in a 10-bit space. +// +// The following is the algorithm used to create the permutation encoding used +// with frameless stacks. It is passed the number of registers to be saved and +// an array of the register numbers saved. +// +//uint32_t permute_encode(uint32_t registerCount, const uint32_t registers[6]) +//{ +// uint32_t renumregs[6]; +// for (int i=6-registerCount; i < 6; ++i) { +// int countless = 0; +// for (int j=6-registerCount; j < i; ++j) { +// if ( registers[j] < registers[i] ) +// ++countless; +// } +// renumregs[i] = registers[i] - countless -1; +// } +// uint32_t permutationEncoding = 0; +// switch ( registerCount ) { +// case 6: +// permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] +// + 6*renumregs[2] + 2*renumregs[3] +// + renumregs[4]); +// break; +// case 5: +// permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] +// + 6*renumregs[3] + 2*renumregs[4] +// + renumregs[5]); +// break; +// case 4: +// permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] +// + 3*renumregs[4] + renumregs[5]); +// break; +// case 3: +// permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] +// + renumregs[5]); +// break; +// case 2: +// permutationEncoding |= (5*renumregs[4] + renumregs[5]); +// break; +// case 1: +// permutationEncoding |= (renumregs[5]); +// break; +// } +// return permutationEncoding; +//} +// + + + + +// +// x86_64 +// +// 1-bit: start +// 1-bit: has lsda +// 2-bit: personality index +// +// 4-bits: 0=old, 1=rbp based, 2=stack-imm, 3=stack-ind, 4=DWARF +// rbp based: +// 15-bits (5*3-bits per reg) register permutation +// 8-bits for stack offset +// frameless: +// 8-bits stack size +// 3-bits stack adjust +// 3-bits register count +// 10-bits register permutation +// +enum { + UNWIND_X86_64_MODE_MASK = 0x0F000000, + UNWIND_X86_64_MODE_RBP_FRAME = 0x01000000, + UNWIND_X86_64_MODE_STACK_IMMD = 0x02000000, + UNWIND_X86_64_MODE_STACK_IND = 0x03000000, + UNWIND_X86_64_MODE_DWARF = 0x04000000, + + UNWIND_X86_64_RBP_FRAME_REGISTERS = 0x00007FFF, + UNWIND_X86_64_RBP_FRAME_OFFSET = 0x00FF0000, + + UNWIND_X86_64_FRAMELESS_STACK_SIZE = 0x00FF0000, + UNWIND_X86_64_FRAMELESS_STACK_ADJUST = 0x0000E000, + UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT = 0x00001C00, + UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF, + + UNWIND_X86_64_DWARF_SECTION_OFFSET = 0x00FFFFFF, +}; + +enum { + UNWIND_X86_64_REG_NONE = 0, + UNWIND_X86_64_REG_RBX = 1, + UNWIND_X86_64_REG_R12 = 2, + UNWIND_X86_64_REG_R13 = 3, + UNWIND_X86_64_REG_R14 = 4, + UNWIND_X86_64_REG_R15 = 5, + UNWIND_X86_64_REG_RBP = 6, +}; +// +// For x86_64 there are four modes for the compact unwind encoding: +// UNWIND_X86_64_MODE_RBP_FRAME: +// RBP based frame where RBP is push on stack immediately after return address, +// then RSP is moved to RBP. Thus, to unwind RSP is restored with the current +// EPB value, then RBP is restored by popping off the stack, and the return +// is done by popping the stack once more into the pc. +// All non-volatile registers that need to be restored must have been saved +// in a small range in the stack that starts RBP-8 to RBP-2040. The offset/8 +// is encoded in the UNWIND_X86_64_RBP_FRAME_OFFSET bits. The registers saved +// are encoded in the UNWIND_X86_64_RBP_FRAME_REGISTERS bits as five 3-bit entries. +// Each entry contains which register to restore. +// UNWIND_X86_64_MODE_STACK_IMMD: +// A "frameless" (RBP not used as frame pointer) function with a small +// constant stack size. To return, a constant (encoded in the compact +// unwind encoding) is added to the RSP. Then the return is done by +// popping the stack into the pc. +// All non-volatile registers that need to be restored must have been saved +// on the stack immediately after the return address. The stack_size/8 is +// encoded in the UNWIND_X86_64_FRAMELESS_STACK_SIZE (max stack size is 2048). +// The number of registers saved is encoded in UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT. +// UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION constains which registers were +// saved and their order. +// UNWIND_X86_64_MODE_STACK_IND: +// A "frameless" (RBP not used as frame pointer) function large constant +// stack size. This case is like the previous, except the stack size is too +// large to encode in the compact unwind encoding. Instead it requires that +// the function contains "subq $nnnnnnnn,RSP" in its prolog. The compact +// encoding contains the offset to the nnnnnnnn value in the function in +// UNWIND_X86_64_FRAMELESS_STACK_SIZE. +// UNWIND_X86_64_MODE_DWARF: +// No compact unwind encoding is available. Instead the low 24-bits of the +// compact encoding is the offset of the DWARF FDE in the __eh_frame section. +// This mode is never used in object files. It is only generated by the +// linker in final linked images which have only DWARF unwind info for a +// function. +// + + +// ARM64 +// +// 1-bit: start +// 1-bit: has lsda +// 2-bit: personality index +// +// 4-bits: 4=frame-based, 3=DWARF, 2=frameless +// frameless: +// 12-bits of stack size +// frame-based: +// 4-bits D reg pairs saved +// 5-bits X reg pairs saved +// DWARF: +// 24-bits offset of DWARF FDE in __eh_frame section +// +enum { + UNWIND_ARM64_MODE_MASK = 0x0F000000, + UNWIND_ARM64_MODE_FRAMELESS = 0x02000000, + UNWIND_ARM64_MODE_DWARF = 0x03000000, + UNWIND_ARM64_MODE_FRAME = 0x04000000, + + UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001, + UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002, + UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004, + UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008, + UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010, + UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100, + UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200, + UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400, + UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800, + + UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000, + UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF, +}; +// For arm64 there are three modes for the compact unwind encoding: +// UNWIND_ARM64_MODE_FRAME: +// This is a standard arm64 prolog where FP/LR are immediately pushed on the +// stack, then SP is copied to FP. If there are any non-volatile registers +// saved, then are copied into the stack frame in pairs in a contiguous +// range right below the saved FP/LR pair. Any subset of the five X pairs +// and four D pairs can be saved, but the memory layout must be in register +// number order. +// UNWIND_ARM64_MODE_FRAMELESS: +// A "frameless" leaf function, where FP/LR are not saved. The return address +// remains in LR throughout the function. If any non-volatile registers +// are saved, they must be pushed onto the stack before any stack space is +// allocated for local variables. The stack sized (including any saved +// non-volatile registers) divided by 16 is encoded in the bits +// UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK. +// UNWIND_ARM64_MODE_DWARF: +// No compact unwind encoding is available. Instead the low 24-bits of the +// compact encoding is the offset of the DWARF FDE in the __eh_frame section. +// This mode is never used in object files. It is only generated by the +// linker in final linked images which have only DWARF unwind info for a +// function. +// + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// Relocatable Object Files: __LD,__compact_unwind +// +//////////////////////////////////////////////////////////////////////////////// + +// +// A compiler can generated compact unwind information for a function by adding +// a "row" to the __LD,__compact_unwind section. This section has the +// S_ATTR_DEBUG bit set, so the section will be ignored by older linkers. +// It is removed by the new linker, so never ends up in final executables. +// This section is a table, initially with one row per function (that needs +// unwind info). The table columns and some conceptual entries are: +// +// range-start pointer to start of function/range +// range-length +// compact-unwind-encoding 32-bit encoding +// personality-function or zero if no personality function +// lsda or zero if no LSDA data +// +// The length and encoding fields are 32-bits. The other are all pointer sized. +// +// In x86_64 assembly, these entry would look like: +// +// .section __LD,__compact_unwind,regular,debug +// +// #compact unwind for _foo +// .quad _foo +// .set L1,LfooEnd-_foo +// .long L1 +// .long 0x01010001 +// .quad 0 +// .quad 0 +// +// #compact unwind for _bar +// .quad _bar +// .set L2,LbarEnd-_bar +// .long L2 +// .long 0x01020011 +// .quad __gxx_personality +// .quad except_tab1 +// +// +// Notes: There is no need for any labels in the the __compact_unwind section. +// The use of the .set directive is to force the evaluation of the +// range-length at assembly time, instead of generating relocations. +// +// To support future compiler optimizations where which non-volatile registers +// are saved changes within a function (e.g. delay saving non-volatiles until +// necessary), there can by multiple lines in the __compact_unwind table for one +// function, each with a different (non-overlapping) range and each with +// different compact unwind encodings that correspond to the non-volatiles +// saved at that range of the function. +// +// If a particular function is so wacky that there is no compact unwind way +// to encode it, then the compiler can emit traditional DWARF unwind info. +// The runtime will use which ever is available. +// +// Runtime support for compact unwind encodings are only available on 10.6 +// and later. So, the compiler should not generate it when targeting pre-10.6. + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// Final Linked Images: __TEXT,__unwind_info +// +//////////////////////////////////////////////////////////////////////////////// + +// +// The __TEXT,__unwind_info section is laid out for an efficient two level lookup. +// The header of the section contains a coarse index that maps function address +// to the page (4096 byte block) containing the unwind info for that function. +// + +#define UNWIND_SECTION_VERSION 1 +struct unwind_info_section_header +{ + uint32_t version; // UNWIND_SECTION_VERSION + uint32_t commonEncodingsArraySectionOffset; + uint32_t commonEncodingsArrayCount; + uint32_t personalityArraySectionOffset; + uint32_t personalityArrayCount; + uint32_t indexSectionOffset; + uint32_t indexCount; + // compact_unwind_encoding_t[] + // uint32_t personalities[] + // unwind_info_section_header_index_entry[] + // unwind_info_section_header_lsda_index_entry[] +}; + +struct unwind_info_section_header_index_entry +{ + uint32_t functionOffset; + uint32_t secondLevelPagesSectionOffset; // section offset to start of regular or compress page + uint32_t lsdaIndexArraySectionOffset; // section offset to start of lsda_index array for this range +}; + +struct unwind_info_section_header_lsda_index_entry +{ + uint32_t functionOffset; + uint32_t lsdaOffset; +}; + +// +// There are two kinds of second level index pages: regular and compressed. +// A compressed page can hold up to 1021 entries, but it cannot be used +// if too many different encoding types are used. The regular page holds +// 511 entries. +// + +struct unwind_info_regular_second_level_entry +{ + uint32_t functionOffset; + compact_unwind_encoding_t encoding; +}; + +#define UNWIND_SECOND_LEVEL_REGULAR 2 +struct unwind_info_regular_second_level_page_header +{ + uint32_t kind; // UNWIND_SECOND_LEVEL_REGULAR + uint16_t entryPageOffset; + uint16_t entryCount; + // entry array +}; + +#define UNWIND_SECOND_LEVEL_COMPRESSED 3 +struct unwind_info_compressed_second_level_page_header +{ + uint32_t kind; // UNWIND_SECOND_LEVEL_COMPRESSED + uint16_t entryPageOffset; + uint16_t entryCount; + uint16_t encodingsPageOffset; + uint16_t encodingsCount; + // 32-bit entry array + // encodings array +}; + +#define UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) (entry & 0x00FFFFFF) +#define UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry) ((entry >> 24) & 0xFF) + + + +#endif + diff --git a/src/coreclr/nativeaot/libunwind/include/unwind.h b/src/coreclr/nativeaot/libunwind/include/unwind.h new file mode 100644 index 00000000000000..47d303c3f095a5 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/include/unwind.h @@ -0,0 +1,400 @@ +//===------------------------------- unwind.h -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// C++ ABI Level 1 ABI documented at: +// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +#ifndef __UNWIND_H__ +#define __UNWIND_H__ + +#include <__libunwind_config.h> + +#include +#include + +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) && defined(_WIN32) +#include +#include +#endif + +#if defined(__APPLE__) +#define LIBUNWIND_UNAVAIL __attribute__ (( unavailable )) +#else +#define LIBUNWIND_UNAVAIL +#endif + +typedef enum { + _URC_NO_REASON = 0, + _URC_OK = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, +#if defined(_LIBUNWIND_ARM_EHABI) + _URC_FAILURE = 9 +#endif +} _Unwind_Reason_Code; + +typedef enum { + _UA_SEARCH_PHASE = 1, + _UA_CLEANUP_PHASE = 2, + _UA_HANDLER_FRAME = 4, + _UA_FORCE_UNWIND = 8, + _UA_END_OF_STACK = 16 // gcc extension to C++ ABI +} _Unwind_Action; + +typedef struct _Unwind_Context _Unwind_Context; // opaque + +#if defined(_LIBUNWIND_ARM_EHABI) +typedef uint32_t _Unwind_State; + +static const _Unwind_State _US_VIRTUAL_UNWIND_FRAME = 0; +static const _Unwind_State _US_UNWIND_FRAME_STARTING = 1; +static const _Unwind_State _US_UNWIND_FRAME_RESUME = 2; +static const _Unwind_State _US_ACTION_MASK = 3; +/* Undocumented flag for force unwinding. */ +static const _Unwind_State _US_FORCE_UNWIND = 8; + +typedef uint32_t _Unwind_EHT_Header; + +struct _Unwind_Control_Block; +typedef struct _Unwind_Control_Block _Unwind_Control_Block; +typedef struct _Unwind_Control_Block _Unwind_Exception; /* Alias */ + +struct _Unwind_Control_Block { + uint64_t exception_class; + void (*exception_cleanup)(_Unwind_Reason_Code, _Unwind_Control_Block*); + + /* Unwinder cache, private fields for the unwinder's use */ + struct { + uint32_t reserved1; /* init reserved1 to 0, then don't touch */ + uint32_t reserved2; + uint32_t reserved3; + uint32_t reserved4; + uint32_t reserved5; + } unwinder_cache; + + /* Propagation barrier cache (valid after phase 1): */ + struct { + uint32_t sp; + uint32_t bitpattern[5]; + } barrier_cache; + + /* Cleanup cache (preserved over cleanup): */ + struct { + uint32_t bitpattern[4]; + } cleanup_cache; + + /* Pr cache (for pr's benefit): */ + struct { + uint32_t fnstart; /* function start address */ + _Unwind_EHT_Header* ehtp; /* pointer to EHT entry header word */ + uint32_t additional; + uint32_t reserved1; + } pr_cache; + + long long int :0; /* Enforce the 8-byte alignment */ +} __attribute__((__aligned__(8))); + +typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) + (_Unwind_State state, + _Unwind_Exception* exceptionObject, + struct _Unwind_Context* context); + +typedef _Unwind_Reason_Code (*__personality_routine) + (_Unwind_State state, + _Unwind_Exception* exceptionObject, + struct _Unwind_Context* context); +#else +struct _Unwind_Context; // opaque +struct _Unwind_Exception; // forward declaration +typedef struct _Unwind_Exception _Unwind_Exception; + +struct _Unwind_Exception { + uint64_t exception_class; + void (*exception_cleanup)(_Unwind_Reason_Code reason, + _Unwind_Exception *exc); +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) + uintptr_t private_[6]; +#else + uintptr_t private_1; // non-zero means forced unwind + uintptr_t private_2; // holds sp that phase1 found for phase2 to use +#endif +#if __SIZEOF_POINTER__ == 4 + // The implementation of _Unwind_Exception uses an attribute mode on the + // above fields which has the side effect of causing this whole struct to + // round up to 32 bytes in size (48 with SEH). To be more explicit, we add + // pad fields added for binary compatibility. + uint32_t reserved[3]; +#endif + // The Itanium ABI requires that _Unwind_Exception objects are "double-word + // aligned". GCC has interpreted this to mean "use the maximum useful + // alignment for the target"; so do we. +} __attribute__((__aligned__)); + +typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) + (int version, + _Unwind_Action actions, + uint64_t exceptionClass, + _Unwind_Exception* exceptionObject, + struct _Unwind_Context* context, + void* stop_parameter ); + +typedef _Unwind_Reason_Code (*__personality_routine) + (int version, + _Unwind_Action actions, + uint64_t exceptionClass, + _Unwind_Exception* exceptionObject, + struct _Unwind_Context* context); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// +// The following are the base functions documented by the C++ ABI +// +#ifdef __USING_SJLJ_EXCEPTIONS__ +extern _Unwind_Reason_Code + _Unwind_SjLj_RaiseException(_Unwind_Exception *exception_object); +extern void _Unwind_SjLj_Resume(_Unwind_Exception *exception_object); +#else +extern _Unwind_Reason_Code + _Unwind_RaiseException(_Unwind_Exception *exception_object); +extern void _Unwind_Resume(_Unwind_Exception *exception_object); +#endif +extern void _Unwind_DeleteException(_Unwind_Exception *exception_object); + +#if defined(_LIBUNWIND_ARM_EHABI) +typedef enum { + _UVRSC_CORE = 0, /* integer register */ + _UVRSC_VFP = 1, /* vfp */ + _UVRSC_WMMXD = 3, /* Intel WMMX data register */ + _UVRSC_WMMXC = 4 /* Intel WMMX control register */ +} _Unwind_VRS_RegClass; + +typedef enum { + _UVRSD_UINT32 = 0, + _UVRSD_VFPX = 1, + _UVRSD_UINT64 = 3, + _UVRSD_FLOAT = 4, + _UVRSD_DOUBLE = 5 +} _Unwind_VRS_DataRepresentation; + +typedef enum { + _UVRSR_OK = 0, + _UVRSR_NOT_IMPLEMENTED = 1, + _UVRSR_FAILED = 2 +} _Unwind_VRS_Result; + +extern void _Unwind_Complete(_Unwind_Exception* exception_object); + +extern _Unwind_VRS_Result +_Unwind_VRS_Get(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t regno, _Unwind_VRS_DataRepresentation representation, + void *valuep); + +extern _Unwind_VRS_Result +_Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t regno, _Unwind_VRS_DataRepresentation representation, + void *valuep, uint32_t *pos); + +extern _Unwind_VRS_Result +_Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t discriminator, + _Unwind_VRS_DataRepresentation representation); +#endif + +#if !defined(_LIBUNWIND_ARM_EHABI) + +extern uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index); +extern void _Unwind_SetGR(struct _Unwind_Context *context, int index, + uintptr_t new_value, uintptr_t *pos); +extern uintptr_t _Unwind_GetIP(struct _Unwind_Context *context); +extern void _Unwind_SetIP(struct _Unwind_Context *, uintptr_t new_value); + +#else // defined(_LIBUNWIND_ARM_EHABI) + +#if defined(_LIBUNWIND_UNWIND_LEVEL1_EXTERNAL_LINKAGE) +#define _LIBUNWIND_EXPORT_UNWIND_LEVEL1 extern +#else +#define _LIBUNWIND_EXPORT_UNWIND_LEVEL1 static __inline__ +#endif + +// These are de facto helper functions for ARM, which delegate the function +// calls to _Unwind_VRS_Get/Set(). These are not a part of ARM EHABI +// specification, thus these function MUST be inlined. Please don't replace +// these with the "extern" function declaration; otherwise, the program +// including this header won't be ABI compatible and will result in +// link error when we are linking the program with libgcc. + +_LIBUNWIND_EXPORT_UNWIND_LEVEL1 +uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index) { + uintptr_t value = 0; + _Unwind_VRS_Get(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value); + return value; +} + +_LIBUNWIND_EXPORT_UNWIND_LEVEL1 +void _Unwind_SetGR(struct _Unwind_Context *context, int index, + uintptr_t value,uintptr_t *pos) { + _Unwind_VRS_Set(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value, pos); +} + +_LIBUNWIND_EXPORT_UNWIND_LEVEL1 +uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { + // remove the thumb-bit before returning + return _Unwind_GetGR(context, 15) & (~(uintptr_t)0x1); +} + +_LIBUNWIND_EXPORT_UNWIND_LEVEL1 +void _Unwind_SetIP(struct _Unwind_Context *context, uintptr_t value) { + uintptr_t thumb_bit = _Unwind_GetGR(context, 15) & ((uintptr_t)0x1); + _Unwind_SetGR(context, 15, value | thumb_bit, NULL); +} +#endif // defined(_LIBUNWIND_ARM_EHABI) + +extern uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context *context); +extern uintptr_t + _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context); +#ifdef __USING_SJLJ_EXCEPTIONS__ +extern _Unwind_Reason_Code + _Unwind_SjLj_ForcedUnwind(_Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter); +#else +extern _Unwind_Reason_Code + _Unwind_ForcedUnwind(_Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter); +#endif + +#ifdef __USING_SJLJ_EXCEPTIONS__ +typedef struct _Unwind_FunctionContext *_Unwind_FunctionContext_t; +extern void _Unwind_SjLj_Register(_Unwind_FunctionContext_t fc); +extern void _Unwind_SjLj_Unregister(_Unwind_FunctionContext_t fc); +#endif + +// +// The following are semi-suppoted extensions to the C++ ABI +// + +// +// called by __cxa_rethrow(). +// +#ifdef __USING_SJLJ_EXCEPTIONS__ +extern _Unwind_Reason_Code + _Unwind_SjLj_Resume_or_Rethrow(_Unwind_Exception *exception_object); +#else +extern _Unwind_Reason_Code + _Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object); +#endif + +// _Unwind_Backtrace() is a gcc extension that walks the stack and calls the +// _Unwind_Trace_Fn once per frame until it reaches the bottom of the stack +// or the _Unwind_Trace_Fn function returns something other than _URC_NO_REASON. +typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *, + void *); +extern _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *); + +// _Unwind_GetCFA is a gcc extension that can be called from within a +// personality handler to get the CFA (stack pointer before call) of +// current frame. +extern uintptr_t _Unwind_GetCFA(struct _Unwind_Context *); + + +// _Unwind_GetIPInfo is a gcc extension that can be called from within a +// personality handler. Similar to _Unwind_GetIP() but also returns in +// *ipBefore a non-zero value if the instruction pointer is at or before the +// instruction causing the unwind. Normally, in a function call, the IP returned +// is the return address which is after the call instruction and may be past the +// end of the function containing the call instruction. +extern uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context, + int *ipBefore); + + +// __register_frame() is used with dynamically generated code to register the +// FDE for a generated (JIT) code. The FDE must use pc-rel addressing to point +// to its function and optional LSDA. +// __register_frame() has existed in all versions of Mac OS X, but in 10.4 and +// 10.5 it was buggy and did not actually register the FDE with the unwinder. +// In 10.6 and later it does register properly. +extern void __register_frame(const void *fde); +extern void __deregister_frame(const void *fde); + +// _Unwind_Find_FDE() will locate the FDE if the pc is in some function that has +// an associated FDE. Note, Mac OS X 10.6 and later, introduces "compact unwind +// info" which the runtime uses in preference to DWARF unwind info. This +// function will only work if the target function has an FDE but no compact +// unwind info. +struct dwarf_eh_bases { + uintptr_t tbase; + uintptr_t dbase; + uintptr_t func; +}; +extern const void *_Unwind_Find_FDE(const void *pc, struct dwarf_eh_bases *); + + +// This function attempts to find the start (address of first instruction) of +// a function given an address inside the function. It only works if the +// function has an FDE (DWARF unwind info). +// This function is unimplemented on Mac OS X 10.6 and later. Instead, use +// _Unwind_Find_FDE() and look at the dwarf_eh_bases.func result. +extern void *_Unwind_FindEnclosingFunction(void *pc); + +// Mac OS X does not support text-rel and data-rel addressing so these functions +// are unimplemented +extern uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context *context) + LIBUNWIND_UNAVAIL; +extern uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context *context) + LIBUNWIND_UNAVAIL; + +// Mac OS X 10.4 and 10.5 had implementations of these functions in +// libgcc_s.dylib, but they never worked. +/// These functions are no longer available on Mac OS X. +extern void __register_frame_info_bases(const void *fde, void *ob, void *tb, + void *db) LIBUNWIND_UNAVAIL; +extern void __register_frame_info(const void *fde, void *ob) + LIBUNWIND_UNAVAIL; +extern void __register_frame_info_table_bases(const void *fde, void *ob, + void *tb, void *db) + LIBUNWIND_UNAVAIL; +extern void __register_frame_info_table(const void *fde, void *ob) + LIBUNWIND_UNAVAIL; +extern void __register_frame_table(const void *fde) + LIBUNWIND_UNAVAIL; +extern void *__deregister_frame_info(const void *fde) + LIBUNWIND_UNAVAIL; +extern void *__deregister_frame_info_bases(const void *fde) + LIBUNWIND_UNAVAIL; + +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) +#ifndef _WIN32 +typedef struct _EXCEPTION_RECORD EXCEPTION_RECORD; +typedef struct _CONTEXT CONTEXT; +typedef struct _DISPATCHER_CONTEXT DISPATCHER_CONTEXT; +#elif !defined(__MINGW32__) && VER_PRODUCTBUILD < 8000 +typedef struct _DISPATCHER_CONTEXT DISPATCHER_CONTEXT; +#endif +// This is the common wrapper for GCC-style personality functions with SEH. +extern EXCEPTION_DISPOSITION _GCC_specific_handler(EXCEPTION_RECORD *exc, + void *frame, + CONTEXT *ctx, + DISPATCHER_CONTEXT *disp, + __personality_routine pers); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __UNWIND_H__ diff --git a/src/coreclr/nativeaot/libunwind/src/AddressSpace.hpp b/src/coreclr/nativeaot/libunwind/src/AddressSpace.hpp new file mode 100644 index 00000000000000..389be0a506672f --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/AddressSpace.hpp @@ -0,0 +1,641 @@ +//===------------------------- AddressSpace.hpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Abstracts accessing local vs remote address spaces. +// +//===----------------------------------------------------------------------===// + +#ifndef __ADDRESSSPACE_HPP__ +#define __ADDRESSSPACE_HPP__ + +#include +#include +#include +#include + +#ifndef _LIBUNWIND_USE_DLADDR + #if !defined(_LIBUNWIND_IS_BAREMETAL) && !defined(_WIN32) + #define _LIBUNWIND_USE_DLADDR 1 + #else + #define _LIBUNWIND_USE_DLADDR 0 + #endif +#endif + +#if _LIBUNWIND_USE_DLADDR +#include +#if defined(__unix__) && defined(__ELF__) && defined(_LIBUNWIND_HAS_COMMENT_LIB_PRAGMA) +#pragma comment(lib, "dl") +#endif +#endif + +#ifdef __APPLE__ +#include +namespace libunwind { + bool checkKeyMgrRegisteredFDEs(uintptr_t targetAddr, void *&fde); +} +#endif + +#include "libunwind.h" +#include "config.h" +#include "dwarf2.h" +#include "EHHeaderParser.hpp" +#include "Registers.hpp" + +#ifdef __APPLE__ + + struct dyld_unwind_sections + { + const struct mach_header* mh; + const void* dwarf_section; + uintptr_t dwarf_section_length; + const void* compact_unwind_section; + uintptr_t compact_unwind_section_length; + }; + #if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \ + && (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)) \ + || defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + // In 10.7.0 or later, libSystem.dylib implements this function. + extern "C" bool _dyld_find_unwind_sections(void *, dyld_unwind_sections *); + #else + // In 10.6.x and earlier, we need to implement this functionality. Note + // that this requires a newer version of libmacho (from cctools) than is + // present in libSystem on 10.6.x (for getsectiondata). + static inline bool _dyld_find_unwind_sections(void* addr, + dyld_unwind_sections* info) { + // Find mach-o image containing address. + Dl_info dlinfo; + if (!dladdr(addr, &dlinfo)) + return false; +#if __LP64__ + const struct mach_header_64 *mh = (const struct mach_header_64 *)dlinfo.dli_fbase; +#else + const struct mach_header *mh = (const struct mach_header *)dlinfo.dli_fbase; +#endif + + // Initialize the return struct + info->mh = (const struct mach_header *)mh; + info->dwarf_section = getsectiondata(mh, "__TEXT", "__eh_frame", &info->dwarf_section_length); + info->compact_unwind_section = getsectiondata(mh, "__TEXT", "__unwind_info", &info->compact_unwind_section_length); + + if (!info->dwarf_section) { + info->dwarf_section_length = 0; + } + + if (!info->compact_unwind_section) { + info->compact_unwind_section_length = 0; + } + + return true; + } + #endif + +#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL) + +// When statically linked on bare-metal, the symbols for the EH table are looked +// up without going through the dynamic loader. + +// The following linker script may be used to produce the necessary sections and symbols. +// Unless the --eh-frame-hdr linker option is provided, the section is not generated +// and does not take space in the output file. +// +// .eh_frame : +// { +// __eh_frame_start = .; +// KEEP(*(.eh_frame)) +// __eh_frame_end = .; +// } +// +// .eh_frame_hdr : +// { +// KEEP(*(.eh_frame_hdr)) +// } +// +// __eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0; +// __eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0; + +#ifndef _LIBUNWIND_USE_ONLY_DWARF_INDEX +extern char __eh_frame_start; +extern char __eh_frame_end; +#endif + +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + +#ifndef _LIBUNWIND_BAREMETAL_DWARF_INDEX_SEC_START +#define _LIBUNWIND_BAREMETAL_DWARF_INDEX_SEC_START __eh_frame_hdr_start +#endif + +#ifndef _LIBUNWIND_BAREMETAL_DWARF_INDEX_SEC_END +#define _LIBUNWIND_BAREMETAL_DWARF_INDEX_SEC_END __eh_frame_hdr_end +#endif + +extern char _LIBUNWIND_BAREMETAL_DWARF_INDEX_SEC_START; +extern char _LIBUNWIND_BAREMETAL_DWARF_INDEX_SEC_END; +#endif + +#elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL) + +// When statically linked on bare-metal, the symbols for the EH table are looked +// up without going through the dynamic loader. +extern char __exidx_start; +extern char __exidx_end; + +#elif defined(_LIBUNWIND_ARM_EHABI) || defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + +// ELF-based systems may use dl_iterate_phdr() to access sections +// containing unwinding information. The ElfW() macro for pointer-size +// independent ELF header traversal is not provided by on some +// systems (e.g., FreeBSD). On these systems the data structures are +// just called Elf_XXX. Define ElfW() locally. +#ifndef _WIN32 +#include +#else +#include +#include +#endif +#if !defined(ElfW) +#define ElfW(type) Elf_##type +#endif + +#endif + +namespace libunwind { + +/// Used by findUnwindSections() to return info about needed sections. +struct UnwindInfoSections { +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) || defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) || \ + defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + // No dso_base for SEH or ARM EHABI. + uintptr_t dso_base; +#endif +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +#ifndef _LIBUNWIND_USE_ONLY_DWARF_INDEX + uintptr_t dwarf_section; + uintptr_t dwarf_section_length; +#endif +#endif +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + uintptr_t dwarf_index_section; + uintptr_t dwarf_index_section_length; +#endif +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + uintptr_t compact_unwind_section; + uintptr_t compact_unwind_section_length; +#endif +#if defined(_LIBUNWIND_ARM_EHABI) + uintptr_t arm_section; + uintptr_t arm_section_length; +#endif +}; + + +/// LocalAddressSpace is used as a template parameter to UnwindCursor when +/// unwinding a thread in the same process. The wrappers compile away, +/// making local unwinds fast. +class _LIBUNWIND_HIDDEN LocalAddressSpace { +public: + typedef uintptr_t pint_t; + typedef intptr_t sint_t; + uint8_t get8(pint_t addr) { + uint8_t val; + memcpy(&val, (void *)addr, sizeof(val)); + return val; + } + uint16_t get16(pint_t addr) { + uint16_t val; + memcpy(&val, (void *)addr, sizeof(val)); + return val; + } + uint32_t get32(pint_t addr) { + uint32_t val; + memcpy(&val, (void *)addr, sizeof(val)); + return val; + } + uint64_t get64(pint_t addr) { + uint64_t val; + memcpy(&val, (void *)addr, sizeof(val)); + return val; + } + double getDouble(pint_t addr) { + double val; + memcpy(&val, (void *)addr, sizeof(val)); + return val; + } + v128 getVector(pint_t addr) { + v128 val; + memcpy(&val, (void *)addr, sizeof(val)); + return val; + } + uintptr_t getP(pint_t addr); + uint64_t getRegister(pint_t addr); + static uint64_t getULEB128(pint_t &addr, pint_t end); + static int64_t getSLEB128(pint_t &addr, pint_t end); + + pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, + pint_t datarelBase = 0); + bool findFunctionName(pint_t addr, char *buf, size_t bufLen, + unw_word_t *offset); + bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info); + bool findOtherFDE(pint_t targetAddr, pint_t &fde); + + static LocalAddressSpace sThisAddressSpace; +}; + +inline uintptr_t LocalAddressSpace::getP(pint_t addr) { +#if __SIZEOF_POINTER__ == 8 + return get64(addr); +#else + return get32(addr); +#endif +} + +inline uint64_t LocalAddressSpace::getRegister(pint_t addr) { +#if __SIZEOF_POINTER__ == 8 || defined(__mips64) + return get64(addr); +#else + return get32(addr); +#endif +} + +/// Read a ULEB128 into a 64-bit word. +inline uint64_t LocalAddressSpace::getULEB128(pint_t &addr, pint_t end) { + const uint8_t *p = (uint8_t *)addr; + const uint8_t *pend = (uint8_t *)end; + uint64_t result = 0; + int bit = 0; + do { + uint64_t b; + + if (p == pend) + _LIBUNWIND_ABORT("truncated uleb128 expression"); + + b = *p & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) { + _LIBUNWIND_ABORT("malformed uleb128 expression"); + } else { + result |= b << bit; + bit += 7; + } + } while (*p++ >= 0x80); + addr = (pint_t) p; + return result; +} + +/// Read a SLEB128 into a 64-bit word. +inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) { + const uint8_t *p = (uint8_t *)addr; + const uint8_t *pend = (uint8_t *)end; + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == pend) + _LIBUNWIND_ABORT("truncated sleb128 expression"); + byte = *p++; + result |= ((byte & 0x7f) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ((byte & 0x40) != 0) + result |= (-1ULL) << bit; + addr = (pint_t) p; + return result; +} + +inline LocalAddressSpace::pint_t +LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, + pint_t datarelBase) { + pint_t startAddr = addr; + const uint8_t *p = (uint8_t *)addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = getP(addr); + p += sizeof(pint_t); + addr = (pint_t) p; + break; + case DW_EH_PE_uleb128: + result = (pint_t)getULEB128(addr, end); + break; + case DW_EH_PE_udata2: + result = get16(addr); + p += 2; + addr = (pint_t) p; + break; + case DW_EH_PE_udata4: + result = get32(addr); + p += 4; + addr = (pint_t) p; + break; + case DW_EH_PE_udata8: + result = (pint_t)get64(addr); + p += 8; + addr = (pint_t) p; + break; + case DW_EH_PE_sleb128: + result = (pint_t)getSLEB128(addr, end); + break; + case DW_EH_PE_sdata2: + // Sign extend from signed 16-bit value. + result = (pint_t)(int16_t)get16(addr); + p += 2; + addr = (pint_t) p; + break; + case DW_EH_PE_sdata4: + // Sign extend from signed 32-bit value. + result = (pint_t)(int32_t)get32(addr); + p += 4; + addr = (pint_t) p; + break; + case DW_EH_PE_sdata8: + result = (pint_t)get64(addr); + p += 8; + addr = (pint_t) p; + break; + default: + _LIBUNWIND_ABORT("unknown pointer encoding"); + } + + // then add relative offset + switch (encoding & 0x70) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + _LIBUNWIND_ABORT("DW_EH_PE_textrel pointer encoding not supported"); + break; + case DW_EH_PE_datarel: + // DW_EH_PE_datarel is only valid in a few places, so the parameter has a + // default value of 0, and we abort in the event that someone calls this + // function with a datarelBase of 0 and DW_EH_PE_datarel encoding. + if (datarelBase == 0) + _LIBUNWIND_ABORT("DW_EH_PE_datarel is invalid with a datarelBase of 0"); + result += datarelBase; + break; + case DW_EH_PE_funcrel: + _LIBUNWIND_ABORT("DW_EH_PE_funcrel pointer encoding not supported"); + break; + case DW_EH_PE_aligned: + _LIBUNWIND_ABORT("DW_EH_PE_aligned pointer encoding not supported"); + break; + default: + _LIBUNWIND_ABORT("unknown pointer encoding"); + break; + } + + if (encoding & DW_EH_PE_indirect) + result = getP(result); + + return result; +} + +inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, + UnwindInfoSections &info) { +#ifdef __APPLE__ + dyld_unwind_sections dyldInfo; + if (_dyld_find_unwind_sections((void *)targetAddr, &dyldInfo)) { + info.dso_base = (uintptr_t)dyldInfo.mh; + #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + info.dwarf_section = (uintptr_t)dyldInfo.dwarf_section; + info.dwarf_section_length = dyldInfo.dwarf_section_length; + #endif + info.compact_unwind_section = (uintptr_t)dyldInfo.compact_unwind_section; + info.compact_unwind_section_length = dyldInfo.compact_unwind_section_length; + return true; + } +#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL) + // Bare metal is statically linked, so no need to ask the dynamic loader +#ifndef _LIBUNWIND_USE_ONLY_DWARF_INDEX + info.dwarf_section_length = (uintptr_t)(&__eh_frame_end - &__eh_frame_start); + info.dwarf_section = (uintptr_t)(&__eh_frame_start); + _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p", + (void *)info.dwarf_section, + (void *)info.dwarf_section_length); +#endif +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + info.dwarf_index_section = + (uintptr_t)(&(_LIBUNWIND_BAREMETAL_DWARF_INDEX_SEC_START)); + info.dwarf_index_section_length = + (uintptr_t)(&(_LIBUNWIND_BAREMETAL_DWARF_INDEX_SEC_END) - + &(_LIBUNWIND_BAREMETAL_DWARF_INDEX_SEC_START)); + _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: index section %p length %p", + (void *)info.dwarf_index_section, + (void *)info.dwarf_index_section_length); +#endif + +#ifndef _LIBUNWIND_USE_ONLY_DWARF_INDEX + if (info.dwarf_section_length) + return true; +#else + if (info.dwarf_index_section_length > 0) + return true; +#endif +#elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL) + // Bare metal is statically linked, so no need to ask the dynamic loader + info.arm_section = (uintptr_t)(&__exidx_start); + info.arm_section_length = (uintptr_t)(&__exidx_end - &__exidx_start); + _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p", + (void *)info.arm_section, (void *)info.arm_section_length); + if (info.arm_section && info.arm_section_length) + return true; +#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_WIN32) + HMODULE mods[1024]; + HANDLE process = GetCurrentProcess(); + DWORD needed; + + if (!EnumProcessModules(process, mods, sizeof(mods), &needed)) + return false; + + for (unsigned i = 0; i < (needed / sizeof(HMODULE)); i++) { + PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER)mods[i]; + PIMAGE_NT_HEADERS pinh = (PIMAGE_NT_HEADERS)((BYTE *)pidh + pidh->e_lfanew); + PIMAGE_FILE_HEADER pifh = (PIMAGE_FILE_HEADER)&pinh->FileHeader; + PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinh); + bool found_obj = false; + bool found_hdr = false; + + info.dso_base = (uintptr_t)mods[i]; + for (unsigned j = 0; j < pifh->NumberOfSections; j++, pish++) { + uintptr_t begin = pish->VirtualAddress + (uintptr_t)mods[i]; + uintptr_t end = begin + pish->Misc.VirtualSize; + if (!strncmp((const char *)pish->Name, ".text", + IMAGE_SIZEOF_SHORT_NAME)) { + if (targetAddr >= begin && targetAddr < end) + found_obj = true; + } else if (!strncmp((const char *)pish->Name, ".eh_frame", + IMAGE_SIZEOF_SHORT_NAME)) { + info.dwarf_section = begin; + info.dwarf_section_length = pish->Misc.VirtualSize; + found_hdr = true; + } + if (found_obj && found_hdr) + return true; + } + } + return false; +#elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32) + // Don't even bother, since Windows has functions that do all this stuff + // for us. + (void)targetAddr; + (void)info; + return true; +#elif defined(_LIBUNWIND_ARM_EHABI) && defined(__BIONIC__) && \ + (__ANDROID_API__ < 21) + int length = 0; + info.arm_section = + (uintptr_t)dl_unwind_find_exidx((_Unwind_Ptr)targetAddr, &length); + info.arm_section_length = (uintptr_t)length; + if (info.arm_section && info.arm_section_length) + return true; +#elif defined(_LIBUNWIND_ARM_EHABI) || defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + struct dl_iterate_cb_data { + LocalAddressSpace *addressSpace; + UnwindInfoSections *sects; + uintptr_t targetAddr; + }; + + dl_iterate_cb_data cb_data = {this, &info, targetAddr}; + int found = dl_iterate_phdr( + [](struct dl_phdr_info *pinfo, size_t, void *data) -> int { + auto cbdata = static_cast(data); + bool found_obj = false; + bool found_hdr = false; + + assert(cbdata); + assert(cbdata->sects); + + if (cbdata->targetAddr < pinfo->dlpi_addr) { + return false; + } + +#if !defined(Elf_Half) + typedef ElfW(Half) Elf_Half; +#endif +#if !defined(Elf_Phdr) + typedef ElfW(Phdr) Elf_Phdr; +#endif +#if !defined(Elf_Addr) && defined(__ANDROID__) + typedef ElfW(Addr) Elf_Addr; +#endif + + #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + #if !defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + #error "_LIBUNWIND_SUPPORT_DWARF_UNWIND requires _LIBUNWIND_SUPPORT_DWARF_INDEX on this platform." + #endif + size_t object_length; +#if defined(__ANDROID__) + Elf_Addr image_base = + pinfo->dlpi_phnum + ? reinterpret_cast(pinfo->dlpi_phdr) - + reinterpret_cast(pinfo->dlpi_phdr) + ->p_offset + : 0; +#endif + + for (Elf_Half i = 0; i < pinfo->dlpi_phnum; i++) { + const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i]; + if (phdr->p_type == PT_LOAD) { + uintptr_t begin = pinfo->dlpi_addr + phdr->p_vaddr; +#if defined(__ANDROID__) + if (pinfo->dlpi_addr == 0 && phdr->p_vaddr < image_base) + begin = begin + image_base; +#endif + uintptr_t end = begin + phdr->p_memsz; + if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) { + cbdata->sects->dso_base = begin; + object_length = phdr->p_memsz; + found_obj = true; + } + } else if (phdr->p_type == PT_GNU_EH_FRAME) { + EHHeaderParser::EHHeaderInfo hdrInfo; + uintptr_t eh_frame_hdr_start = pinfo->dlpi_addr + phdr->p_vaddr; +#if defined(__ANDROID__) + if (pinfo->dlpi_addr == 0 && phdr->p_vaddr < image_base) + eh_frame_hdr_start = eh_frame_hdr_start + image_base; +#endif + cbdata->sects->dwarf_index_section = eh_frame_hdr_start; + cbdata->sects->dwarf_index_section_length = phdr->p_memsz; + found_hdr = EHHeaderParser::decodeEHHdr( + *cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz, + hdrInfo); + if (found_hdr) + cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr; + } + } + + if (found_obj && found_hdr) { + cbdata->sects->dwarf_section_length = object_length; + return true; + } else { + return false; + } + #else // defined(_LIBUNWIND_ARM_EHABI) + for (Elf_Half i = 0; i < pinfo->dlpi_phnum; i++) { + const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i]; + if (phdr->p_type == PT_LOAD) { + uintptr_t begin = pinfo->dlpi_addr + phdr->p_vaddr; + uintptr_t end = begin + phdr->p_memsz; + if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) + found_obj = true; + } else if (phdr->p_type == PT_ARM_EXIDX) { + uintptr_t exidx_start = pinfo->dlpi_addr + phdr->p_vaddr; + cbdata->sects->arm_section = exidx_start; + cbdata->sects->arm_section_length = phdr->p_memsz; + found_hdr = true; + } + } + return found_obj && found_hdr; + #endif + }, + &cb_data); + return static_cast(found); +#endif + + return false; +} + + +inline bool LocalAddressSpace::findOtherFDE(pint_t targetAddr, pint_t &fde) { +#ifdef __APPLE__ + return checkKeyMgrRegisteredFDEs(targetAddr, *((void**)&fde)); +#else + // TO DO: if OS has way to dynamically register FDEs, check that. + (void)targetAddr; + (void)fde; + return false; +#endif +} + +inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf, + size_t bufLen, + unw_word_t *offset) { +#if _LIBUNWIND_USE_DLADDR + Dl_info dyldInfo; + if (dladdr((void *)addr, &dyldInfo)) { + if (dyldInfo.dli_sname != NULL) { + snprintf(buf, bufLen, "%s", dyldInfo.dli_sname); + *offset = (addr - (pint_t) dyldInfo.dli_saddr); + return true; + } + } +#else + (void)addr; + (void)buf; + (void)bufLen; + (void)offset; +#endif + return false; +} + +} // namespace libunwind + +#endif // __ADDRESSSPACE_HPP__ diff --git a/src/coreclr/nativeaot/libunwind/src/CMakeLists.txt b/src/coreclr/nativeaot/libunwind/src/CMakeLists.txt new file mode 100644 index 00000000000000..572c82396bfa4b --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/CMakeLists.txt @@ -0,0 +1,178 @@ +# Get sources + +set(LIBUNWIND_CXX_SOURCES + libunwind.cpp + Unwind-EHABI.cpp + Unwind-seh.cpp + ) +unwind_append_if(LIBUNWIND_CXX_SOURCES APPLE Unwind_AppleExtras.cpp) + +set(LIBUNWIND_C_SOURCES + UnwindLevel1.c + UnwindLevel1-gcc-ext.c + Unwind-sjlj.c + ) +set_source_files_properties(${LIBUNWIND_C_SOURCES} + PROPERTIES + COMPILE_FLAGS "-std=c99") + +set(LIBUNWIND_ASM_SOURCES + UnwindRegistersRestore.S + UnwindRegistersSave.S + ) +set_source_files_properties(${LIBUNWIND_ASM_SOURCES} + PROPERTIES + LANGUAGE C) + +set(LIBUNWIND_HEADERS + AddressSpace.hpp + assembly.h + CompactUnwinder.hpp + config.h + dwarf2.h + DwarfInstructions.hpp + DwarfParser.hpp + libunwind_ext.h + Registers.hpp + RWMutex.hpp + UnwindCursor.hpp + ../include/libunwind.h + ../include/unwind.h + ) + +unwind_append_if(LIBUNWIND_HEADERS APPLE + ../include/mach-o/compact_unwind_encoding.h + ) + +if (MSVC_IDE) + # Force them all into the headers dir on MSVC, otherwise they end up at + # project scope because they don't have extensions. + source_group("Header Files" FILES ${LIBUNWIND_HEADERS}) +endif() + +set(LIBUNWIND_SOURCES + ${LIBUNWIND_CXX_SOURCES} + ${LIBUNWIND_C_SOURCES} + ${LIBUNWIND_ASM_SOURCES}) + +# Generate library list. +set(libraries) +unwind_append_if(libraries LIBUNWIND_HAS_C_LIB c) +if (LIBUNWIND_USE_COMPILER_RT) + list(APPEND libraries "${LIBUNWIND_BUILTINS_LIBRARY}") +else() + unwind_append_if(libraries LIBUNWIND_HAS_GCC_S_LIB gcc_s) + unwind_append_if(libraries LIBUNWIND_HAS_GCC_LIB gcc) +endif() +unwind_append_if(libraries LIBUNWIND_HAS_DL_LIB dl) +if (LIBUNWIND_ENABLE_THREADS) + unwind_append_if(libraries LIBUNWIND_HAS_PTHREAD_LIB pthread) + unwind_append_if(LIBUNWIND_COMPILE_FLAGS LIBUNWIND_WEAK_PTHREAD_LIB -DLIBUNWIND_USE_WEAK_PTHREAD=1) +endif() + +# Setup flags. +unwind_append_if(LIBUNWIND_CXX_FLAGS LIBUNWIND_HAS_NO_RTTI_FLAG -fno-rtti) + +unwind_append_if(LIBUNWIND_LINK_FLAGS LIBUNWIND_HAS_NODEFAULTLIBS_FLAG -nodefaultlibs) + +# MINGW_LIBRARIES is defined in config-ix.cmake +unwind_append_if(libraries MINGW "${MINGW_LIBRARIES}") + +if (LIBUNWIND_HAS_NO_EXCEPTIONS_FLAG AND LIBUNWIND_HAS_FUNWIND_TABLES) + list(APPEND LIBUNWIND_COMPILE_FLAGS -fno-exceptions) + list(APPEND LIBUNWIND_COMPILE_FLAGS -funwind-tables) +elseif (LIBUNWIND_ENABLE_SHARED) + message(FATAL_ERROR + "Compiler doesn't support generation of unwind tables if exception " + "support is disabled. Building libunwind DSO with runtime dependency " + "on C++ ABI library is not supported.") +endif() + +if (APPLE) + list(APPEND LIBUNWIND_COMPILE_FLAGS "-U__STRICT_ANSI__") + list(APPEND LIBUNWIND_LINK_FLAGS + "-compatibility_version 1" + "-install_name /usr/lib/libunwind.1.dylib") + + if (CMAKE_OSX_DEPLOYMENT_TARGET STREQUAL "10.6") + list(APPEND LIBUNWIND_LINK_FLAGS + "-current_version ${LIBUNWIND_VERSION}" + "/usr/lib/libSystem.B.dylib") + endif () +endif () + +string(REPLACE ";" " " LIBUNWIND_COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}") +string(REPLACE ";" " " LIBUNWIND_CXX_FLAGS "${LIBUNWIND_CXX_FLAGS}") +string(REPLACE ";" " " LIBUNWIND_C_FLAGS "${LIBUNWIND_C_FLAGS}") +string(REPLACE ";" " " LIBUNWIND_LINK_FLAGS "${LIBUNWIND_LINK_FLAGS}") + +set_property(SOURCE ${LIBUNWIND_CXX_SOURCES} + APPEND_STRING PROPERTY COMPILE_FLAGS " ${LIBUNWIND_CXX_FLAGS}") +set_property(SOURCE ${LIBUNWIND_C_SOURCES} + APPEND_STRING PROPERTY COMPILE_FLAGS " ${LIBUNWIND_C_FLAGS}") + +# Build the shared library. +if (LIBUNWIND_ENABLE_SHARED) + add_library(unwind_shared SHARED ${LIBUNWIND_SOURCES} ${LIBUNWIND_HEADERS}) + if(COMMAND llvm_setup_rpath) + llvm_setup_rpath(unwind_shared) + endif() + target_link_libraries(unwind_shared PRIVATE ${libraries}) + set_target_properties(unwind_shared + PROPERTIES + COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}" + LINK_FLAGS "${LIBUNWIND_LINK_FLAGS}" + OUTPUT_NAME "unwind" + VERSION "1.0" + SOVERSION "1") + list(APPEND LIBUNWIND_BUILD_TARGETS "unwind_shared") + if (LIBUNWIND_INSTALL_SHARED_LIBRARY) + list(APPEND LIBUNWIND_INSTALL_TARGETS "unwind_shared") + endif() +endif() + +# Build the static library. +if (LIBUNWIND_ENABLE_STATIC) + add_library(unwind_static STATIC ${LIBUNWIND_SOURCES} ${LIBUNWIND_HEADERS}) + target_link_libraries(unwind_static PRIVATE ${libraries}) + set_target_properties(unwind_static + PROPERTIES + COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}" + LINK_FLAGS "${LIBUNWIND_LINK_FLAGS}" + OUTPUT_NAME "unwind") + + if(LIBUNWIND_HERMETIC_STATIC_LIBRARY) + append_flags_if_supported(UNWIND_STATIC_LIBRARY_FLAGS -fvisibility=hidden) + append_flags_if_supported(UNWIND_STATIC_LIBRARY_FLAGS -fvisibility-global-new-delete-hidden) + target_compile_options(unwind_static PRIVATE ${UNWIND_STATIC_LIBRARY_FLAGS}) + target_compile_definitions(unwind_static PRIVATE _LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS) + endif() + + list(APPEND LIBUNWIND_BUILD_TARGETS "unwind_static") + if (LIBUNWIND_INSTALL_STATIC_LIBRARY) + list(APPEND LIBUNWIND_INSTALL_TARGETS "unwind_static") + endif() +endif() + +# Add a meta-target for both libraries. +add_custom_target(unwind DEPENDS ${LIBUNWIND_BUILD_TARGETS}) + +if (LIBUNWIND_INSTALL_LIBRARY) + install(TARGETS ${LIBUNWIND_INSTALL_TARGETS} + LIBRARY DESTINATION ${LIBUNWIND_INSTALL_PREFIX}${LIBUNWIND_INSTALL_LIBRARY_DIR} COMPONENT unwind + ARCHIVE DESTINATION ${LIBUNWIND_INSTALL_PREFIX}${LIBUNWIND_INSTALL_LIBRARY_DIR} COMPONENT unwind) +endif() + +if (NOT CMAKE_CONFIGURATION_TYPES AND LIBUNWIND_INSTALL_LIBRARY) + add_custom_target(install-unwind + DEPENDS unwind + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=unwind + -P "${LIBUNWIND_BINARY_DIR}/cmake_install.cmake") + add_custom_target(install-unwind-stripped + DEPENDS unwind + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=unwind + -DCMAKE_INSTALL_DO_STRIP=1 + -P "${LIBUNWIND_BINARY_DIR}/cmake_install.cmake") +endif() diff --git a/src/coreclr/nativeaot/libunwind/src/CompactUnwinder.hpp b/src/coreclr/nativeaot/libunwind/src/CompactUnwinder.hpp new file mode 100644 index 00000000000000..3546f195120a15 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/CompactUnwinder.hpp @@ -0,0 +1,697 @@ +//===-------------------------- CompactUnwinder.hpp -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Does runtime stack unwinding using compact unwind encodings. +// +//===----------------------------------------------------------------------===// + +#ifndef __COMPACT_UNWINDER_HPP__ +#define __COMPACT_UNWINDER_HPP__ + +#include +#include + +#include +#include + +#include "Registers.hpp" + +#define EXTRACT_BITS(value, mask) \ + ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1)) + +namespace libunwind { + +#if defined(_LIBUNWIND_TARGET_I386) +/// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka +/// unwind) by modifying a Registers_x86 register set +template +class CompactUnwinder_x86 { +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t info, + uint32_t functionStart, A &addressSpace, + Registers_x86 ®isters); + +private: + typename A::pint_t pint_t; + + static void frameUnwind(A &addressSpace, Registers_x86 ®isters); + static void framelessUnwind(A &addressSpace, + typename A::pint_t returnAddressLocation, + Registers_x86 ®isters); + static int + stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, + uint32_t functionStart, A &addressSpace, + Registers_x86 ®isters); + static int stepWithCompactEncodingFrameless( + compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters, bool indirectStackSize); +}; + +template +int CompactUnwinder_x86::stepWithCompactEncoding( + compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters) { + switch (compactEncoding & UNWIND_X86_MODE_MASK) { + case UNWIND_X86_MODE_EBP_FRAME: + return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart, + addressSpace, registers); + case UNWIND_X86_MODE_STACK_IMMD: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, false); + case UNWIND_X86_MODE_STACK_IND: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, true); + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +template +int CompactUnwinder_x86::stepWithCompactEncodingEBPFrame( + compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters) { + uint32_t savedRegistersOffset = + EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = + EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS); + + uint32_t savedRegisters = registers.getEBP() - 4 * savedRegistersOffset; + for (int i = 0; i < 5; ++i) { + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_REG_NONE: + // no register saved in this slot + break; + case UNWIND_X86_REG_EBX: + registers.setEBX(addressSpace.get32(savedRegisters), savedRegisters); + break; + case UNWIND_X86_REG_ECX: + registers.setECX(addressSpace.get32(savedRegisters), savedRegisters); + break; + case UNWIND_X86_REG_EDX: + registers.setEDX(addressSpace.get32(savedRegisters), savedRegisters); + break; + case UNWIND_X86_REG_EDI: + registers.setEDI(addressSpace.get32(savedRegisters), savedRegisters); + break; + case UNWIND_X86_REG_ESI: + registers.setESI(addressSpace.get32(savedRegisters), savedRegisters); + break; + default: + (void)functionStart; + _LIBUNWIND_DEBUG_LOG("bad register for EBP frame, encoding=%08X for " + "function starting at 0x%X", + compactEncoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 4; + savedRegistersLocations = (savedRegistersLocations >> 3); + } + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; +} + +template +int CompactUnwinder_x86::stepWithCompactEncodingFrameless( + compact_unwind_encoding_t encoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters, bool indirectStackSize) { + uint32_t stackSizeEncoded = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); + uint32_t regCount = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); + uint32_t stackSize = stackSizeEncoded * 4; + if (indirectStackSize) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded); + stackSize = subl + 4 * stackAdjust; + } + // decompress permutation + uint32_t permunreg[6]; + switch (regCount) { + case 6: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // re-number registers back to standard numbers + int registersSaved[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i = 0; i < regCount; ++i) { + uint32_t renum = 0; + for (int u = 1; u < 7; ++u) { + if (!used[u]) { + if (renum == permunreg[i]) { + registersSaved[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + uint32_t savedRegisters = registers.getSP() + stackSize - 4 - 4 * regCount; + for (uint32_t i = 0; i < regCount; ++i) { + switch (registersSaved[i]) { + case UNWIND_X86_REG_EBX: + registers.setEBX(addressSpace.get32(savedRegisters), savedRegisters); + break; + case UNWIND_X86_REG_ECX: + registers.setECX(addressSpace.get32(savedRegisters), savedRegisters); + break; + case UNWIND_X86_REG_EDX: + registers.setEDX(addressSpace.get32(savedRegisters), savedRegisters); + break; + case UNWIND_X86_REG_EDI: + registers.setEDI(addressSpace.get32(savedRegisters), savedRegisters); + break; + case UNWIND_X86_REG_ESI: + registers.setESI(addressSpace.get32(savedRegisters), savedRegisters); + break; + case UNWIND_X86_REG_EBP: + registers.setEBP(addressSpace.get32(savedRegisters), savedRegisters); + break; + default: + _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for " + "function starting at 0x%X", + encoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 4; + } + framelessUnwind(addressSpace, savedRegisters, registers); + return UNW_STEP_SUCCESS; +} + + +template +void CompactUnwinder_x86::frameUnwind(A &addressSpace, + Registers_x86 ®isters) { + typename A::pint_t bp = registers.getEBP(); + // ebp points to old ebp + registers.setEBP(addressSpace.get32(bp), bp); + // old esp is ebp less saved ebp and return address + registers.setSP((uint32_t)bp + 8, 0); + // pop return address into eip + registers.setIP(addressSpace.get32(bp + 4), bp + 4); +} + +template +void CompactUnwinder_x86::framelessUnwind( + A &addressSpace, typename A::pint_t returnAddressLocation, + Registers_x86 ®isters) { + // return address is on stack after last saved register + registers.setIP(addressSpace.get32(returnAddressLocation), returnAddressLocation); + // old esp is before return address + registers.setSP((uint32_t)returnAddressLocation + 4, 0); +} +#endif // _LIBUNWIND_TARGET_I386 + + +#if defined(_LIBUNWIND_TARGET_X86_64) +/// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka +/// unwind) by modifying a Registers_x86_64 register set +template +class CompactUnwinder_x86_64 { +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_x86_64 ®isters); + +private: + typename A::pint_t pint_t; + + static void frameUnwind(A &addressSpace, Registers_x86_64 ®isters); + static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation, + Registers_x86_64 ®isters); + static int + stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_x86_64 ®isters); + static int stepWithCompactEncodingFrameless( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_x86_64 ®isters, bool indirectStackSize); +}; + +template +int CompactUnwinder_x86_64::stepWithCompactEncoding( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_x86_64 ®isters) { + switch (compactEncoding & UNWIND_X86_64_MODE_MASK) { + case UNWIND_X86_64_MODE_RBP_FRAME: + return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart, + addressSpace, registers); + case UNWIND_X86_64_MODE_STACK_IMMD: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, false); + case UNWIND_X86_64_MODE_STACK_IND: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, true); + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +template +int CompactUnwinder_x86_64::stepWithCompactEncodingRBPFrame( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_x86_64 ®isters) { + uint32_t savedRegistersOffset = + EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = + EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); + + uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset; + for (int i = 0; i < 5; ++i) { + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_64_REG_NONE: + // no register saved in this slot + break; + case UNWIND_X86_64_REG_RBX: + registers.setRBX(addressSpace.get64(savedRegisters), savedRegisters); + break; + case UNWIND_X86_64_REG_R12: + registers.setR12(addressSpace.get64(savedRegisters), savedRegisters); + break; + case UNWIND_X86_64_REG_R13: + registers.setR13(addressSpace.get64(savedRegisters), savedRegisters); + break; + case UNWIND_X86_64_REG_R14: + registers.setR14(addressSpace.get64(savedRegisters), savedRegisters); + break; + case UNWIND_X86_64_REG_R15: + registers.setR15(addressSpace.get64(savedRegisters), savedRegisters); + break; + default: + (void)functionStart; + _LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for " + "function starting at 0x%llX", + compactEncoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 8; + savedRegistersLocations = (savedRegistersLocations >> 3); + } + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; +} + +template +int CompactUnwinder_x86_64::stepWithCompactEncodingFrameless( + compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace, + Registers_x86_64 ®isters, bool indirectStackSize) { + uint32_t stackSizeEncoded = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); + uint32_t regCount = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); + uint32_t stackSize = stackSizeEncoded * 8; + if (indirectStackSize) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded); + stackSize = subl + 8 * stackAdjust; + } + // decompress permutation + uint32_t permunreg[6]; + switch (regCount) { + case 6: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // re-number registers back to standard numbers + int registersSaved[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i = 0; i < regCount; ++i) { + uint32_t renum = 0; + for (int u = 1; u < 7; ++u) { + if (!used[u]) { + if (renum == permunreg[i]) { + registersSaved[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount; + for (uint32_t i = 0; i < regCount; ++i) { + switch (registersSaved[i]) { + case UNWIND_X86_64_REG_RBX: + registers.setRBX(addressSpace.get64(savedRegisters), savedRegisters); + break; + case UNWIND_X86_64_REG_R12: + registers.setR12(addressSpace.get64(savedRegisters), savedRegisters); + break; + case UNWIND_X86_64_REG_R13: + registers.setR13(addressSpace.get64(savedRegisters), savedRegisters); + break; + case UNWIND_X86_64_REG_R14: + registers.setR14(addressSpace.get64(savedRegisters), savedRegisters); + break; + case UNWIND_X86_64_REG_R15: + registers.setR15(addressSpace.get64(savedRegisters), savedRegisters); + break; + case UNWIND_X86_64_REG_RBP: + registers.setRBP(addressSpace.get64(savedRegisters), savedRegisters); + break; + default: + _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for " + "function starting at 0x%llX", + encoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 8; + } + framelessUnwind(addressSpace, savedRegisters, registers); + return UNW_STEP_SUCCESS; +} + + +template +void CompactUnwinder_x86_64::frameUnwind(A &addressSpace, + Registers_x86_64 ®isters) { + uint64_t rbp = registers.getRBP(); + // ebp points to old ebp + registers.setRBP(addressSpace.get64(rbp), rbp); + // old esp is ebp less saved ebp and return address + registers.setSP(rbp + 16, 0); + // pop return address into eip + registers.setIP(addressSpace.get64(rbp + 8), rbp + 8); +} + +template +void CompactUnwinder_x86_64::framelessUnwind(A &addressSpace, + uint64_t returnAddressLocation, + Registers_x86_64 ®isters) { + // return address is on stack after last saved register + registers.setIP(addressSpace.get64(returnAddressLocation), returnAddressLocation); + // old esp is before return address + registers.setSP(returnAddressLocation + 8, 0); +} +#endif // _LIBUNWIND_TARGET_X86_64 + + + +#if defined(_LIBUNWIND_TARGET_AARCH64) +/// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka +/// unwind) by modifying a Registers_arm64 register set +template +class CompactUnwinder_arm64 { +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_arm64 ®isters); + +private: + typename A::pint_t pint_t; + + static int + stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_arm64 ®isters); + static int stepWithCompactEncodingFrameless( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_arm64 ®isters); +}; + +template +int CompactUnwinder_arm64::stepWithCompactEncoding( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_arm64 ®isters) { + switch (compactEncoding & UNWIND_ARM64_MODE_MASK) { + case UNWIND_ARM64_MODE_FRAME: + return stepWithCompactEncodingFrame(compactEncoding, functionStart, + addressSpace, registers); + case UNWIND_ARM64_MODE_FRAMELESS: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers); + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +template +int CompactUnwinder_arm64::stepWithCompactEncodingFrameless( + compact_unwind_encoding_t encoding, uint64_t, A &addressSpace, + Registers_arm64 ®isters) { + uint32_t stackSize = + 16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK); + + uint64_t savedRegisterLoc = registers.getSP() + stackSize; + + if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) { + registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) { + registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) { + registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) { + registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) { + registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + + if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) { + registers.setFloatRegister(UNW_ARM64_D8, + addressSpace.getDouble(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D9, + addressSpace.getDouble(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) { + registers.setFloatRegister(UNW_ARM64_D10, + addressSpace.getDouble(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D11, + addressSpace.getDouble(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) { + registers.setFloatRegister(UNW_ARM64_D12, + addressSpace.getDouble(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D13, + addressSpace.getDouble(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) { + registers.setFloatRegister(UNW_ARM64_D14, + addressSpace.getDouble(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D15, + addressSpace.getDouble(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + + // subtract stack size off of sp + registers.setSP(savedRegisterLoc, 0); + + // set pc to be value in lr + registers.setIP(registers.getRegister(UNW_ARM64_LR), 0); + + return UNW_STEP_SUCCESS; +} + +template +int CompactUnwinder_arm64::stepWithCompactEncodingFrame( + compact_unwind_encoding_t encoding, uint64_t, A &addressSpace, + Registers_arm64 ®isters) { + uint64_t savedRegisterLoc = registers.getFP() - 8; + + if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) { + registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) { + registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) { + registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) { + registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) { + registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc), savedRegisterLoc); + savedRegisterLoc -= 8; + } + + if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) { + registers.setFloatRegister(UNW_ARM64_D8, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D9, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) { + registers.setFloatRegister(UNW_ARM64_D10, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D11, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) { + registers.setFloatRegister(UNW_ARM64_D12, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D13, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) { + registers.setFloatRegister(UNW_ARM64_D14, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D15, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + uint64_t fp = registers.getFP(); + // fp points to old fp + registers.setFP(addressSpace.get64(fp), fp); + // old sp is fp less saved fp and lr + registers.setSP(fp + 16, 0); + // pop return address into pc + registers.setIP(addressSpace.get64(fp + 8), fp + 8); + + return UNW_STEP_SUCCESS; +} +#endif // _LIBUNWIND_TARGET_AARCH64 + + +} // namespace libunwind + +#endif // __COMPACT_UNWINDER_HPP__ diff --git a/src/coreclr/nativeaot/libunwind/src/DwarfInstructions.hpp b/src/coreclr/nativeaot/libunwind/src/DwarfInstructions.hpp new file mode 100644 index 00000000000000..1eb9c472c58e23 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/DwarfInstructions.hpp @@ -0,0 +1,834 @@ +//===-------------------------- DwarfInstructions.hpp ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Processor specific interpretation of DWARF unwind info. +// +//===----------------------------------------------------------------------===// + +#ifndef __DWARF_INSTRUCTIONS_HPP__ +#define __DWARF_INSTRUCTIONS_HPP__ + +#include +#include +#include + +#include "dwarf2.h" +#include "Registers.hpp" +#include "DwarfParser.hpp" +#include "config.h" + + +namespace libunwind { + + +/// DwarfInstructions maps abtract DWARF unwind instructions to a particular +/// architecture +template +class DwarfInstructions { +public: + typedef typename A::pint_t pint_t; + typedef typename A::sint_t sint_t; + + static int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart, + R ®isters); + +private: + + enum { + DW_X86_64_RET_ADDR = 16 + }; + + enum { + DW_X86_RET_ADDR = 8 + }; + + typedef typename CFI_Parser::RegisterLocation RegisterLocation; + typedef typename CFI_Parser::PrologInfo PrologInfo; + typedef typename CFI_Parser::FDE_Info FDE_Info; + typedef typename CFI_Parser::CIE_Info CIE_Info; + + static pint_t evaluateExpression(pint_t expression, A &addressSpace, + const R ®isters, + pint_t initialStackValue); + static pint_t getSavedRegister(A &addressSpace, const R ®isters, + pint_t cfa, const RegisterLocation &savedReg, + pint_t& location); + static double getSavedFloatRegister(A &addressSpace, const R ®isters, + pint_t cfa, const RegisterLocation &savedReg); + static v128 getSavedVectorRegister(A &addressSpace, const R ®isters, + pint_t cfa, const RegisterLocation &savedReg); + + static pint_t getCFA(A &addressSpace, const PrologInfo &prolog, + const R ®isters) { + if (prolog.cfaRegister != 0) + return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) + + prolog.cfaRegisterOffset); + if (prolog.cfaExpression != 0) + return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, + registers, 0); + assert(0 && "getCFA(): unknown location"); + __builtin_unreachable(); + } +}; + + +template +typename A::pint_t DwarfInstructions::getSavedRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const RegisterLocation &savedReg, + typename A::pint_t& location) { + switch (savedReg.location) { + case CFI_Parser::kRegisterInCFA: + location = cfa + (pint_t)savedReg.value; + return (pint_t)addressSpace.getP(location); + + case CFI_Parser::kRegisterAtExpression: + location = evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa); + return (pint_t)addressSpace.getP(location); + + case CFI_Parser::kRegisterIsExpression: + location = 0; + return evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa); + case CFI_Parser::kRegisterInRegister: + location = 0; + return registers.getRegister((int)savedReg.value); + + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + // FIX ME + break; + } + _LIBUNWIND_ABORT("unsupported restore location for register"); +} + +template +double DwarfInstructions::getSavedFloatRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getDouble(cfa + (pint_t)savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getDouble( + evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + case CFI_Parser::kRegisterInRegister: + // FIX ME + break; + } + _LIBUNWIND_ABORT("unsupported restore location for float register"); +} + +template +v128 DwarfInstructions::getSavedVectorRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getVector(cfa + (pint_t)savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getVector( + evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + case CFI_Parser::kRegisterInRegister: + // FIX ME + break; + } + _LIBUNWIND_ABORT("unsupported restore location for vector register"); +} + +template +int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, + pint_t fdeStart, R ®isters) { + FDE_Info fdeInfo; + CIE_Info cieInfo; + if (CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, + &cieInfo) == NULL) { + PrologInfo prolog; + if (CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc, + R::getArch(), &prolog)) { + // get pointer to cfa (architecture specific) + pint_t cfa = getCFA(addressSpace, prolog, registers); + + // restore registers that DWARF says were saved + R newRegisters = registers; + pint_t returnAddress = 0; + pint_t returnAddressLocation = 0; + const int lastReg = R::lastDwarfRegNum(); + assert(static_cast(CFI_Parser::kMaxRegisterNumber) >= lastReg && + "register range too large"); + assert(lastReg >= (int)cieInfo.returnAddressRegister && + "register range does not contain return address register"); + for (int i = 0; i <= lastReg; ++i) { + if (prolog.savedRegisters[i].location != + CFI_Parser::kRegisterUnused) { + if (registers.validFloatRegister(i)) + newRegisters.setFloatRegister( + i, getSavedFloatRegister(addressSpace, registers, cfa, + prolog.savedRegisters[i])); + else if (registers.validVectorRegister(i)) + newRegisters.setVectorRegister( + i, getSavedVectorRegister(addressSpace, registers, cfa, + prolog.savedRegisters[i])); + else if (i == (int)cieInfo.returnAddressRegister) { + returnAddress = getSavedRegister(addressSpace, registers, cfa, + prolog.savedRegisters[i], + returnAddressLocation); + if (registers.validRegister(i)) { + newRegisters.setRegister(i, returnAddress, returnAddressLocation); + } + } + else if (registers.validRegister(i)) { + pint_t value; + pint_t location; + value = getSavedRegister(addressSpace, registers, cfa, + prolog.savedRegisters[i], + location); + + newRegisters.setRegister(i, value, location); + } + else + return UNW_EBADREG; + } + } + + // By definition, the CFA is the stack pointer at the call site, so + // restoring SP means setting it to CFA. + newRegisters.setSP(cfa, 0); + +#if defined(_LIBUNWIND_TARGET_AARCH64) + // If the target is aarch64 then the return address may have been signed + // using the v8.3 pointer authentication extensions. The original + // return address needs to be authenticated before the return address is + // restored. autia1716 is used instead of autia as autia1716 assembles + // to a NOP on pre-v8.3a architectures. + if ((R::getArch() == REGISTERS_ARM64) && + prolog.savedRegisters[UNW_ARM64_RA_SIGN_STATE].value) { +#if !defined(_LIBUNWIND_IS_NATIVE_ONLY) + return UNW_ECROSSRASIGNING; +#else + register unsigned long long x17 __asm("x17") = returnAddress; + register unsigned long long x16 __asm("x16") = cfa; + + // These are the autia1716/autib1716 instructions. The hint instructions + // are used here as gcc does not assemble autia1716/autib1716 for pre + // armv8.3a targets. + if (cieInfo.addressesSignedWithBKey) + asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716 + else + asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716 + returnAddress = x17; +#endif + } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC) + if (R::getArch() == REGISTERS_SPARC) { + // Skip call site instruction and delay slot + returnAddress += 8; + // Skip unimp instruction if function returns a struct + if ((addressSpace.get32(returnAddress) & 0xC1C00000) == 0) + returnAddress += 4; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC64) +#define PPC64_ELFV1_R2_LOAD_INST_ENCODING 0xe8410028u // ld r2,40(r1) +#define PPC64_ELFV1_R2_OFFSET 40 +#define PPC64_ELFV2_R2_LOAD_INST_ENCODING 0xe8410018u // ld r2,24(r1) +#define PPC64_ELFV2_R2_OFFSET 24 + // If the instruction at return address is a TOC (r2) restore, + // then r2 was saved and needs to be restored. + // ELFv2 ABI specifies that the TOC Pointer must be saved at SP + 24, + // while in ELFv1 ABI it is saved at SP + 40. + if (R::getArch() == REGISTERS_PPC64 && returnAddress != 0) { + pint_t sp = newRegisters.getRegister(UNW_REG_SP); + pint_t r2 = 0; + switch (addressSpace.get32(returnAddress)) { + case PPC64_ELFV1_R2_LOAD_INST_ENCODING: + r2 = addressSpace.get64(sp + PPC64_ELFV1_R2_OFFSET); + break; + case PPC64_ELFV2_R2_LOAD_INST_ENCODING: + r2 = addressSpace.get64(sp + PPC64_ELFV2_R2_OFFSET); + break; + } + if (r2) + newRegisters.setRegister(UNW_PPC64_R2, r2); + } +#endif + + // Return address is address after call site instruction, so setting IP to + // that does simualates a return. + newRegisters.setIP(returnAddress, returnAddressLocation); + + // Simulate the step by replacing the register set with the new ones. + registers = newRegisters; + + return UNW_STEP_SUCCESS; + } + } + return UNW_EBADFRAME; +} + +template +typename A::pint_t +DwarfInstructions::evaluateExpression(pint_t expression, A &addressSpace, + const R ®isters, + pint_t initialStackValue) { + const bool log = false; + pint_t p = expression; + pint_t expressionEnd = expression + 20; // temp, until len read + pint_t length = (pint_t)addressSpace.getULEB128(p, expressionEnd); + expressionEnd = p + length; + if (log) + fprintf(stderr, "evaluateExpression(): length=%" PRIu64 "\n", + (uint64_t)length); + pint_t stack[100]; + pint_t *sp = stack; + *(++sp) = initialStackValue; + + while (p < expressionEnd) { + if (log) { + for (pint_t *t = sp; t > stack; --t) { + fprintf(stderr, "sp[] = 0x%" PRIx64 "\n", (uint64_t)(*t)); + } + } + uint8_t opcode = addressSpace.get8(p++); + sint_t svalue, svalue2; + pint_t value; + uint32_t reg; + switch (opcode) { + case DW_OP_addr: + // push immediate address sized value + value = addressSpace.getP(p); + p += sizeof(pint_t); + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_deref: + // pop stack, dereference, push result + value = *sp--; + *(++sp) = addressSpace.getP(value); + if (log) + fprintf(stderr, "dereference 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const1u: + // push immediate 1 byte value + value = addressSpace.get8(p); + p += 1; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const1s: + // push immediate 1 byte signed value + svalue = (int8_t) addressSpace.get8(p); + p += 1; + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); + break; + + case DW_OP_const2u: + // push immediate 2 byte value + value = addressSpace.get16(p); + p += 2; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const2s: + // push immediate 2 byte signed value + svalue = (int16_t) addressSpace.get16(p); + p += 2; + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); + break; + + case DW_OP_const4u: + // push immediate 4 byte value + value = addressSpace.get32(p); + p += 4; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const4s: + // push immediate 4 byte signed value + svalue = (int32_t)addressSpace.get32(p); + p += 4; + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); + break; + + case DW_OP_const8u: + // push immediate 8 byte value + value = (pint_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const8s: + // push immediate 8 byte signed value + value = (pint_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_constu: + // push immediate ULEB128 value + value = (pint_t)addressSpace.getULEB128(p, expressionEnd); + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_consts: + // push immediate SLEB128 value + svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); + break; + + case DW_OP_dup: + // push top of stack + value = *sp; + *(++sp) = value; + if (log) + fprintf(stderr, "duplicate top of stack\n"); + break; + + case DW_OP_drop: + // pop + --sp; + if (log) + fprintf(stderr, "pop top of stack\n"); + break; + + case DW_OP_over: + // dup second + value = sp[-1]; + *(++sp) = value; + if (log) + fprintf(stderr, "duplicate second in stack\n"); + break; + + case DW_OP_pick: + // pick from + reg = addressSpace.get8(p); + p += 1; + value = sp[-reg]; + *(++sp) = value; + if (log) + fprintf(stderr, "duplicate %d in stack\n", reg); + break; + + case DW_OP_swap: + // swap top two + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = value; + if (log) + fprintf(stderr, "swap top of stack\n"); + break; + + case DW_OP_rot: + // rotate top three + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = value; + if (log) + fprintf(stderr, "rotate top three of stack\n"); + break; + + case DW_OP_xderef: + // pop stack, dereference, push result + value = *sp--; + *sp = *((pint_t*)value); + if (log) + fprintf(stderr, "x-dereference 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_abs: + svalue = (sint_t)*sp; + if (svalue < 0) + *sp = (pint_t)(-svalue); + if (log) + fprintf(stderr, "abs\n"); + break; + + case DW_OP_and: + value = *sp--; + *sp &= value; + if (log) + fprintf(stderr, "and\n"); + break; + + case DW_OP_div: + svalue = (sint_t)(*sp--); + svalue2 = (sint_t)*sp; + *sp = (pint_t)(svalue2 / svalue); + if (log) + fprintf(stderr, "div\n"); + break; + + case DW_OP_minus: + value = *sp--; + *sp = *sp - value; + if (log) + fprintf(stderr, "minus\n"); + break; + + case DW_OP_mod: + svalue = (sint_t)(*sp--); + svalue2 = (sint_t)*sp; + *sp = (pint_t)(svalue2 % svalue); + if (log) + fprintf(stderr, "module\n"); + break; + + case DW_OP_mul: + svalue = (sint_t)(*sp--); + svalue2 = (sint_t)*sp; + *sp = (pint_t)(svalue2 * svalue); + if (log) + fprintf(stderr, "mul\n"); + break; + + case DW_OP_neg: + *sp = 0 - *sp; + if (log) + fprintf(stderr, "neg\n"); + break; + + case DW_OP_not: + svalue = (sint_t)(*sp); + *sp = (pint_t)(~svalue); + if (log) + fprintf(stderr, "not\n"); + break; + + case DW_OP_or: + value = *sp--; + *sp |= value; + if (log) + fprintf(stderr, "or\n"); + break; + + case DW_OP_plus: + value = *sp--; + *sp += value; + if (log) + fprintf(stderr, "plus\n"); + break; + + case DW_OP_plus_uconst: + // pop stack, add uelb128 constant, push result + *sp += static_cast(addressSpace.getULEB128(p, expressionEnd)); + if (log) + fprintf(stderr, "add constant\n"); + break; + + case DW_OP_shl: + value = *sp--; + *sp = *sp << value; + if (log) + fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shr: + value = *sp--; + *sp = *sp >> value; + if (log) + fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shra: + value = *sp--; + svalue = (sint_t)*sp; + *sp = (pint_t)(svalue >> value); + if (log) + fprintf(stderr, "shift left arithmetric\n"); + break; + + case DW_OP_xor: + value = *sp--; + *sp ^= value; + if (log) + fprintf(stderr, "xor\n"); + break; + + case DW_OP_skip: + svalue = (int16_t) addressSpace.get16(p); + p += 2; + p = (pint_t)((sint_t)p + svalue); + if (log) + fprintf(stderr, "skip %" PRIu64 "\n", (uint64_t)svalue); + break; + + case DW_OP_bra: + svalue = (int16_t) addressSpace.get16(p); + p += 2; + if (*sp--) + p = (pint_t)((sint_t)p + svalue); + if (log) + fprintf(stderr, "bra %" PRIu64 "\n", (uint64_t)svalue); + break; + + case DW_OP_eq: + value = *sp--; + *sp = (*sp == value); + if (log) + fprintf(stderr, "eq\n"); + break; + + case DW_OP_ge: + value = *sp--; + *sp = (*sp >= value); + if (log) + fprintf(stderr, "ge\n"); + break; + + case DW_OP_gt: + value = *sp--; + *sp = (*sp > value); + if (log) + fprintf(stderr, "gt\n"); + break; + + case DW_OP_le: + value = *sp--; + *sp = (*sp <= value); + if (log) + fprintf(stderr, "le\n"); + break; + + case DW_OP_lt: + value = *sp--; + *sp = (*sp < value); + if (log) + fprintf(stderr, "lt\n"); + break; + + case DW_OP_ne: + value = *sp--; + *sp = (*sp != value); + if (log) + fprintf(stderr, "ne\n"); + break; + + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + value = static_cast(opcode - DW_OP_lit0); + *(++sp) = value; + if (log) + fprintf(stderr, "push literal 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + reg = static_cast(opcode - DW_OP_reg0); + *(++sp) = registers.getRegister((int)reg); + if (log) + fprintf(stderr, "push reg %d\n", reg); + break; + + case DW_OP_regx: + reg = static_cast(addressSpace.getULEB128(p, expressionEnd)); + *(++sp) = registers.getRegister((int)reg); + if (log) + fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue); + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + reg = static_cast(opcode - DW_OP_breg0); + svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); + svalue += static_cast(registers.getRegister((int)reg)); + *(++sp) = (pint_t)(svalue); + if (log) + fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue); + break; + + case DW_OP_bregx: + reg = static_cast(addressSpace.getULEB128(p, expressionEnd)); + svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); + svalue += static_cast(registers.getRegister((int)reg)); + *(++sp) = (pint_t)(svalue); + if (log) + fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue); + break; + + case DW_OP_fbreg: + _LIBUNWIND_ABORT("DW_OP_fbreg not implemented"); + break; + + case DW_OP_piece: + _LIBUNWIND_ABORT("DW_OP_piece not implemented"); + break; + + case DW_OP_deref_size: + // pop stack, dereference, push result + value = *sp--; + switch (addressSpace.get8(p++)) { + case 1: + value = addressSpace.get8(value); + break; + case 2: + value = addressSpace.get16(value); + break; + case 4: + value = addressSpace.get32(value); + break; + case 8: + value = (pint_t)addressSpace.get64(value); + break; + default: + _LIBUNWIND_ABORT("DW_OP_deref_size with bad size"); + } + *(++sp) = value; + if (log) + fprintf(stderr, "sized dereference 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_xderef_size: + case DW_OP_nop: + case DW_OP_push_object_addres: + case DW_OP_call2: + case DW_OP_call4: + case DW_OP_call_ref: + default: + _LIBUNWIND_ABORT("DWARF opcode not implemented"); + } + + } + if (log) + fprintf(stderr, "expression evaluates to 0x%" PRIx64 "\n", (uint64_t)*sp); + return *sp; +} + + + +} // namespace libunwind + +#endif // __DWARF_INSTRUCTIONS_HPP__ diff --git a/src/coreclr/nativeaot/libunwind/src/DwarfParser.hpp b/src/coreclr/nativeaot/libunwind/src/DwarfParser.hpp new file mode 100644 index 00000000000000..a2ebf3bb0e189b --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/DwarfParser.hpp @@ -0,0 +1,766 @@ +//===--------------------------- DwarfParser.hpp --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Parses DWARF CFIs (FDEs and CIEs). +// +//===----------------------------------------------------------------------===// + +#ifndef __DWARF_PARSER_HPP__ +#define __DWARF_PARSER_HPP__ +#define __STDC_FORMAT_MACROS + +#include +#include +#include +#include + +#include "libunwind.h" +#include "dwarf2.h" +#include "Registers.hpp" + +#include "config.h" + +namespace libunwind { + +/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records. +/// See DWARF Spec for details: +/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +/// +template +class CFI_Parser { +public: + typedef typename A::pint_t pint_t; + + /// Information encoded in a CIE (Common Information Entry) + struct CIE_Info { + pint_t cieStart; + pint_t cieLength; + pint_t cieInstructions; + uint8_t pointerEncoding; + uint8_t lsdaEncoding; + uint8_t personalityEncoding; + uint8_t personalityOffsetInCIE; + pint_t personality; + uint32_t codeAlignFactor; + int dataAlignFactor; + bool isSignalFrame; + bool fdesHaveAugmentationData; + uint8_t returnAddressRegister; +#if defined(_LIBUNWIND_TARGET_AARCH64) + bool addressesSignedWithBKey; +#endif + }; + + /// Information about an FDE (Frame Description Entry) + struct FDE_Info { + pint_t fdeStart; + pint_t fdeLength; + pint_t fdeInstructions; + pint_t pcStart; + pint_t pcEnd; + pint_t lsda; + }; + + enum { + kMaxRegisterNumber = _LIBUNWIND_HIGHEST_DWARF_REGISTER + }; + enum RegisterSavedWhere { + kRegisterUnused, + kRegisterInCFA, + kRegisterOffsetFromCFA, + kRegisterInRegister, + kRegisterAtExpression, + kRegisterIsExpression + }; + struct RegisterLocation { + RegisterSavedWhere location; + int64_t value; + }; + /// Information about a frame layout and registers saved determined + /// by "running" the DWARF FDE "instructions" + struct PrologInfo { + uint32_t cfaRegister; + int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset + int64_t cfaExpression; // CFA = expression + uint32_t spExtraArgSize; + uint32_t codeOffsetAtStackDecrement; + bool registersInOtherRegisters; + bool sameValueUsed; + RegisterLocation savedRegisters[kMaxRegisterNumber + 1]; + }; + + struct PrologInfoStackEntry { + PrologInfoStackEntry(PrologInfoStackEntry *n, const PrologInfo &i) + : next(n), info(i) {} + PrologInfoStackEntry *next; + PrologInfo info; + }; + + static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, + uint32_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo, + CIE_Info *cieInfo); + static const char *decodeFDE(A &addressSpace, pint_t fdeStart, + FDE_Info *fdeInfo, CIE_Info *cieInfo); + static bool parseFDEInstructions(A &addressSpace, const FDE_Info &fdeInfo, + const CIE_Info &cieInfo, pint_t upToPC, + int arch, PrologInfo *results); + + static const char *parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo); + +private: + static bool parseInstructions(A &addressSpace, pint_t instructions, + pint_t instructionsEnd, const CIE_Info &cieInfo, + pint_t pcoffset, + PrologInfoStackEntry *&rememberStack, int arch, + PrologInfo *results); +}; + +/// Parse a FDE into a CIE_Info and an FDE_Info +template +const char *CFI_Parser::decodeFDE(A &addressSpace, pint_t fdeStart, + FDE_Info *fdeInfo, CIE_Info *cieInfo) { + pint_t p = fdeStart; + pint_t cfiLength = (pint_t)addressSpace.get32(p); + p += 4; + if (cfiLength == 0xffffffff) { + // 0xffffffff means length is really next 8 bytes + cfiLength = (pint_t)addressSpace.get64(p); + p += 8; + } + if (cfiLength == 0) + return "FDE has zero length"; // end marker + uint32_t ciePointer = addressSpace.get32(p); + if (ciePointer == 0) + return "FDE is really a CIE"; // this is a CIE not an FDE + pint_t nextCFI = p + cfiLength; + pint_t cieStart = p - ciePointer; + const char *err = parseCIE(addressSpace, cieStart, cieInfo); + if (err != NULL) + return err; + p += 4; + // Parse pc begin and range. + pint_t pcStart = + addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); + pint_t pcRange = + addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F); + // Parse rest of info. + fdeInfo->lsda = 0; + // Check for augmentation length. + if (cieInfo->fdesHaveAugmentationData) { + pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if (cieInfo->lsdaEncoding != DW_EH_PE_omit) { + // Peek at value (without indirection). Zero means no LSDA. + pint_t lsdaStart = p; + if (addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != + 0) { + // Reset pointer and re-parse LSDA address. + p = lsdaStart; + fdeInfo->lsda = + addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = fdeStart; + fdeInfo->fdeLength = nextCFI - fdeStart; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart + pcRange; + return NULL; // success +} + +/// Scan an eh_frame section to find an FDE for a pc +template +bool CFI_Parser::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, + uint32_t sectionLength, pint_t fdeHint, + FDE_Info *fdeInfo, CIE_Info *cieInfo) { + //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc); + pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart; + const pint_t ehSectionEnd = p + sectionLength; + while (p < ehSectionEnd) { + pint_t currentCFI = p; + //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p); + pint_t cfiLength = addressSpace.get32(p); + p += 4; + if (cfiLength == 0xffffffff) { + // 0xffffffff means length is really next 8 bytes + cfiLength = (pint_t)addressSpace.get64(p); + p += 8; + } + if (cfiLength == 0) + return false; // end marker + uint32_t id = addressSpace.get32(p); + if (id == 0) { + // Skip over CIEs. + p += cfiLength; + } else { + // Process FDE to see if it covers pc. + pint_t nextCFI = p + cfiLength; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p - ciePointer; + // Validate pointer to CIE is within section. + if ((ehSectionStart <= cieStart) && (cieStart < ehSectionEnd)) { + if (parseCIE(addressSpace, cieStart, cieInfo) == NULL) { + p += 4; + // Parse pc begin and range. + pint_t pcStart = + addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP( + p, nextCFI, cieInfo->pointerEncoding & 0x0F); + // Test if pc is within the function this FDE covers. + if ((pcStart < pc) && (pc <= pcStart + pcRange)) { + // parse rest of info + fdeInfo->lsda = 0; + // check for augmentation length + if (cieInfo->fdesHaveAugmentationData) { + pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if (cieInfo->lsdaEncoding != DW_EH_PE_omit) { + // Peek at value (without indirection). Zero means no LSDA. + pint_t lsdaStart = p; + if (addressSpace.getEncodedP( + p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0) { + // Reset pointer and re-parse LSDA address. + p = lsdaStart; + fdeInfo->lsda = addressSpace + .getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = currentCFI; + fdeInfo->fdeLength = nextCFI - currentCFI; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart + pcRange; + return true; + } else { + // pc is not in begin/range, skip this FDE + } + } else { + // Malformed CIE, now augmentation describing pc range encoding. + } + } else { + // malformed FDE. CIE is bad + } + p = nextCFI; + } + } + return false; +} + +/// Extract info from a CIE +template +const char *CFI_Parser::parseCIE(A &addressSpace, pint_t cie, + CIE_Info *cieInfo) { + cieInfo->pointerEncoding = 0; + cieInfo->lsdaEncoding = DW_EH_PE_omit; + cieInfo->personalityEncoding = 0; + cieInfo->personalityOffsetInCIE = 0; + cieInfo->personality = 0; + cieInfo->codeAlignFactor = 0; + cieInfo->dataAlignFactor = 0; + cieInfo->isSignalFrame = false; + cieInfo->fdesHaveAugmentationData = false; +#if defined(_LIBUNWIND_TARGET_AARCH64) + cieInfo->addressesSignedWithBKey = false; +#endif + cieInfo->cieStart = cie; + pint_t p = cie; + pint_t cieLength = (pint_t)addressSpace.get32(p); + p += 4; + pint_t cieContentEnd = p + cieLength; + if (cieLength == 0xffffffff) { + // 0xffffffff means length is really next 8 bytes + cieLength = (pint_t)addressSpace.get64(p); + p += 8; + cieContentEnd = p + cieLength; + } + if (cieLength == 0) + return NULL; + // CIE ID is always 0 + if (addressSpace.get32(p) != 0) + return "CIE ID is not zero"; + p += 4; + // Version is always 1 or 3 + uint8_t version = addressSpace.get8(p); + if ((version != 1) && (version != 3)) + return "CIE version is not 1 or 3"; + ++p; + // save start of augmentation string and find end + pint_t strStart = p; + while (addressSpace.get8(p) != 0) + ++p; + ++p; + // parse code aligment factor + cieInfo->codeAlignFactor = (uint32_t)addressSpace.getULEB128(p, cieContentEnd); + // parse data alignment factor + cieInfo->dataAlignFactor = (int)addressSpace.getSLEB128(p, cieContentEnd); + // parse return address register + uint64_t raReg = addressSpace.getULEB128(p, cieContentEnd); + assert(raReg < 255 && "return address register too large"); + cieInfo->returnAddressRegister = (uint8_t)raReg; + // parse augmentation data based on augmentation string + const char *result = NULL; + if (addressSpace.get8(strStart) == 'z') { + // parse augmentation data length + addressSpace.getULEB128(p, cieContentEnd); + for (pint_t s = strStart; addressSpace.get8(s) != '\0'; ++s) { + switch (addressSpace.get8(s)) { + case 'z': + cieInfo->fdesHaveAugmentationData = true; + break; + case 'P': + cieInfo->personalityEncoding = addressSpace.get8(p); + ++p; + cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie); + cieInfo->personality = addressSpace + .getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding); + break; + case 'L': + cieInfo->lsdaEncoding = addressSpace.get8(p); + ++p; + break; + case 'R': + cieInfo->pointerEncoding = addressSpace.get8(p); + ++p; + break; + case 'S': + cieInfo->isSignalFrame = true; + break; +#if defined(_LIBUNWIND_TARGET_AARCH64) + case 'B': + cieInfo->addressesSignedWithBKey = true; + break; +#endif + default: + // ignore unknown letters + break; + } + } + } + cieInfo->cieLength = cieContentEnd - cieInfo->cieStart; + cieInfo->cieInstructions = p; + return result; +} + + +/// "run" the DWARF instructions and create the abstact PrologInfo for an FDE +template +bool CFI_Parser::parseFDEInstructions(A &addressSpace, + const FDE_Info &fdeInfo, + const CIE_Info &cieInfo, pint_t upToPC, + int arch, PrologInfo *results) { + // clear results + memset(results, '\0', sizeof(PrologInfo)); + PrologInfoStackEntry *rememberStack = NULL; + + // parse CIE then FDE instructions + return parseInstructions(addressSpace, cieInfo.cieInstructions, + cieInfo.cieStart + cieInfo.cieLength, cieInfo, + (pint_t)(-1), rememberStack, arch, results) && + parseInstructions(addressSpace, fdeInfo.fdeInstructions, + fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo, + upToPC - fdeInfo.pcStart, rememberStack, arch, + results); +} + +/// "run" the DWARF instructions +template +bool CFI_Parser::parseInstructions(A &addressSpace, pint_t instructions, + pint_t instructionsEnd, + const CIE_Info &cieInfo, pint_t pcoffset, + PrologInfoStackEntry *&rememberStack, + int arch, PrologInfo *results) { + pint_t p = instructions; + pint_t codeOffset = 0; + PrologInfo initialState = *results; + + _LIBUNWIND_TRACE_DWARF("parseInstructions(instructions=0x%0" PRIx64 ")\n", + static_cast(instructionsEnd)); + + // see DWARF Spec, section 6.4.2 for details on unwind opcodes + while ((p < instructionsEnd) && (codeOffset < pcoffset)) { + uint64_t reg; + uint64_t reg2; + int64_t offset; + uint64_t length; + uint8_t opcode = addressSpace.get8(p); + uint8_t operand; +#if !defined(_LIBUNWIND_NO_HEAP) + PrologInfoStackEntry *entry; +#endif + ++p; + switch (opcode) { + case DW_CFA_nop: + _LIBUNWIND_TRACE_DWARF("DW_CFA_nop\n"); + break; + case DW_CFA_set_loc: + codeOffset = + addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding); + _LIBUNWIND_TRACE_DWARF("DW_CFA_set_loc\n"); + break; + case DW_CFA_advance_loc1: + codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor); + p += 1; + _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc1: new offset=%" PRIu64 "\n", + static_cast(codeOffset)); + break; + case DW_CFA_advance_loc2: + codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor); + p += 2; + _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc2: new offset=%" PRIu64 "\n", + static_cast(codeOffset)); + break; + case DW_CFA_advance_loc4: + codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor); + p += 4; + _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc4: new offset=%" PRIu64 "\n", + static_cast(codeOffset)); + break; + case DW_CFA_offset_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) + * cieInfo.dataAlignFactor; + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_offset_extended DWARF unwind, reg too big"); + return false; + } + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended(reg=%" PRIu64 ", " + "offset=%" PRId64 ")\n", + reg, offset); + break; + case DW_CFA_restore_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_restore_extended DWARF unwind, reg too big"); + return false; + } + results->savedRegisters[reg] = initialState.savedRegisters[reg]; + _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_extended(reg=%" PRIu64 ")\n", reg); + break; + case DW_CFA_undefined: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_undefined DWARF unwind, reg too big"); + return false; + } + results->savedRegisters[reg].location = kRegisterUnused; + _LIBUNWIND_TRACE_DWARF("DW_CFA_undefined(reg=%" PRIu64 ")\n", reg); + break; + case DW_CFA_same_value: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_same_value DWARF unwind, reg too big"); + return false; + } + // DW_CFA_same_value unsupported + // "same value" means register was stored in frame, but its current + // value has not changed, so no need to restore from frame. + // We model this as if the register was never saved. + results->savedRegisters[reg].location = kRegisterUnused; + // set flag to disable conversion to compact unwind + results->sameValueUsed = true; + _LIBUNWIND_TRACE_DWARF("DW_CFA_same_value(reg=%" PRIu64 ")\n", reg); + break; + case DW_CFA_register: + reg = addressSpace.getULEB128(p, instructionsEnd); + reg2 = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_register DWARF unwind, reg too big"); + return false; + } + if (reg2 > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_register DWARF unwind, reg2 too big"); + return false; + } + results->savedRegisters[reg].location = kRegisterInRegister; + results->savedRegisters[reg].value = (int64_t)reg2; + // set flag to disable conversion to compact unwind + results->registersInOtherRegisters = true; + _LIBUNWIND_TRACE_DWARF( + "DW_CFA_register(reg=%" PRIu64 ", reg2=%" PRIu64 ")\n", reg, reg2); + break; +#if !defined(_LIBUNWIND_NO_HEAP) + case DW_CFA_remember_state: + // avoid operator new, because that would be an upward dependency + entry = (PrologInfoStackEntry *)malloc(sizeof(PrologInfoStackEntry)); + if (entry != NULL) { + entry->next = rememberStack; + entry->info = *results; + rememberStack = entry; + } else { + return false; + } + _LIBUNWIND_TRACE_DWARF("DW_CFA_remember_state\n"); + break; + case DW_CFA_restore_state: + if (rememberStack != NULL) { + PrologInfoStackEntry *top = rememberStack; + *results = top->info; + rememberStack = top->next; + free((char *)top); + } else { + return false; + } + _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_state\n"); + break; +#endif + case DW_CFA_def_cfa: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0("malformed DW_CFA_def_cfa DWARF unwind, reg too big"); + return false; + } + results->cfaRegister = (uint32_t)reg; + results->cfaRegisterOffset = (int32_t)offset; + _LIBUNWIND_TRACE_DWARF( + "DW_CFA_def_cfa(reg=%" PRIu64 ", offset=%" PRIu64 ")\n", reg, offset); + break; + case DW_CFA_def_cfa_register: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_def_cfa_register DWARF unwind, reg too big"); + return false; + } + results->cfaRegister = (uint32_t)reg; + _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_register(%" PRIu64 ")\n", reg); + break; + case DW_CFA_def_cfa_offset: + results->cfaRegisterOffset = (int32_t) + addressSpace.getULEB128(p, instructionsEnd); + results->codeOffsetAtStackDecrement = (uint32_t)codeOffset; + _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset(%d)\n", + results->cfaRegisterOffset); + break; + case DW_CFA_def_cfa_expression: + results->cfaRegister = 0; + results->cfaExpression = (int64_t)p; + length = addressSpace.getULEB128(p, instructionsEnd); + assert(length < static_cast(~0) && "pointer overflow"); + p += static_cast(length); + _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_expression(expression=0x%" PRIx64 + ", length=%" PRIu64 ")\n", + results->cfaExpression, length); + break; + case DW_CFA_expression: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_expression DWARF unwind, reg too big"); + return false; + } + results->savedRegisters[reg].location = kRegisterAtExpression; + results->savedRegisters[reg].value = (int64_t)p; + length = addressSpace.getULEB128(p, instructionsEnd); + assert(length < static_cast(~0) && "pointer overflow"); + p += static_cast(length); + _LIBUNWIND_TRACE_DWARF("DW_CFA_expression(reg=%" PRIu64 ", " + "expression=0x%" PRIx64 ", " + "length=%" PRIu64 ")\n", + reg, results->savedRegisters[reg].value, length); + break; + case DW_CFA_offset_extended_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_offset_extended_sf DWARF unwind, reg too big"); + return false; + } + offset = + addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended_sf(reg=%" PRIu64 ", " + "offset=%" PRId64 ")\n", + reg, offset); + break; + case DW_CFA_def_cfa_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = + addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_def_cfa_sf DWARF unwind, reg too big"); + return false; + } + results->cfaRegister = (uint32_t)reg; + results->cfaRegisterOffset = (int32_t)offset; + _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_sf(reg=%" PRIu64 ", " + "offset=%" PRId64 ")\n", + reg, offset); + break; + case DW_CFA_def_cfa_offset_sf: + results->cfaRegisterOffset = (int32_t) + (addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor); + results->codeOffsetAtStackDecrement = (uint32_t)codeOffset; + _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset_sf(%d)\n", + results->cfaRegisterOffset); + break; + case DW_CFA_val_offset: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG( + "malformed DW_CFA_val_offset DWARF unwind, reg (%" PRIu64 + ") out of range\n", + reg); + return false; + } + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) + * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterOffsetFromCFA; + results->savedRegisters[reg].value = offset; + _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset(reg=%" PRIu64 ", " + "offset=%" PRId64 "\n", + reg, offset); + break; + case DW_CFA_val_offset_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_val_offset_sf DWARF unwind, reg too big"); + return false; + } + offset = + addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterOffsetFromCFA; + results->savedRegisters[reg].value = offset; + _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset_sf(reg=%" PRIu64 ", " + "offset=%" PRId64 "\n", + reg, offset); + break; + case DW_CFA_val_expression: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_val_expression DWARF unwind, reg too big"); + return false; + } + results->savedRegisters[reg].location = kRegisterIsExpression; + results->savedRegisters[reg].value = (int64_t)p; + length = addressSpace.getULEB128(p, instructionsEnd); + assert(length < static_cast(~0) && "pointer overflow"); + p += static_cast(length); + _LIBUNWIND_TRACE_DWARF("DW_CFA_val_expression(reg=%" PRIu64 ", " + "expression=0x%" PRIx64 ", length=%" PRIu64 ")\n", + reg, results->savedRegisters[reg].value, length); + break; + case DW_CFA_GNU_args_size: + length = addressSpace.getULEB128(p, instructionsEnd); + results->spExtraArgSize = (uint32_t)length; + _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_args_size(%" PRIu64 ")\n", length); + break; + case DW_CFA_GNU_negative_offset_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0("malformed DW_CFA_GNU_negative_offset_extended DWARF " + "unwind, reg too big"); + return false; + } + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) + * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = -offset; + _LIBUNWIND_TRACE_DWARF( + "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset); + break; + +#if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC) + // The same constant is used to represent different instructions on + // AArch64 (negate_ra_state) and SPARC (window_save). + static_assert(DW_CFA_AARCH64_negate_ra_state == DW_CFA_GNU_window_save, + "uses the same constant"); + case DW_CFA_AARCH64_negate_ra_state: + switch (arch) { +#if defined(_LIBUNWIND_TARGET_AARCH64) + case REGISTERS_ARM64: + results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^= 0x1; + _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n"); + break; +#endif +#if defined(_LIBUNWIND_TARGET_SPARC) + // case DW_CFA_GNU_window_save: + case REGISTERS_SPARC: + _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save()\n"); + for (reg = UNW_SPARC_O0; reg <= UNW_SPARC_O7; reg++) { + results->savedRegisters[reg].location = kRegisterInRegister; + results->savedRegisters[reg].value = + ((int64_t)reg - UNW_SPARC_O0) + UNW_SPARC_I0; + } + + for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) { + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = + ((int64_t)reg - UNW_SPARC_L0) * 4; + } + break; +#endif + } + break; +#else + (void)arch; +#endif + + default: + operand = opcode & 0x3F; + switch (opcode & 0xC0) { + case DW_CFA_offset: + reg = operand; + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG("malformed DW_CFA_offset DWARF unwind, reg (%" PRIu64 + ") out of range", + reg); + return false; + } + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) + * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + _LIBUNWIND_TRACE_DWARF("DW_CFA_offset(reg=%d, offset=%" PRId64 ")\n", + operand, offset); + break; + case DW_CFA_advance_loc: + codeOffset += operand * cieInfo.codeAlignFactor; + _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc: new offset=%" PRIu64 "\n", + static_cast(codeOffset)); + break; + case DW_CFA_restore: + reg = operand; + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG("malformed DW_CFA_restore DWARF unwind, reg (%" PRIu64 + ") out of range", + reg); + return false; + } + results->savedRegisters[reg] = initialState.savedRegisters[reg]; + _LIBUNWIND_TRACE_DWARF("DW_CFA_restore(reg=%" PRIu64 ")\n", + static_cast(operand)); + break; + default: + _LIBUNWIND_TRACE_DWARF("unknown CFA opcode 0x%02X\n", opcode); + return false; + } + } + } + + return true; +} + +} // namespace libunwind + +#endif // __DWARF_PARSER_HPP__ diff --git a/src/coreclr/nativeaot/libunwind/src/EHHeaderParser.hpp b/src/coreclr/nativeaot/libunwind/src/EHHeaderParser.hpp new file mode 100644 index 00000000000000..0101835b8e63d4 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/EHHeaderParser.hpp @@ -0,0 +1,167 @@ +//===------------------------- EHHeaderParser.hpp -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Parses ELF .eh_frame_hdr sections. +// +//===----------------------------------------------------------------------===// + +#ifndef __EHHEADERPARSER_HPP__ +#define __EHHEADERPARSER_HPP__ + +#include "libunwind.h" + +#include "DwarfParser.hpp" + +namespace libunwind { + +/// \brief EHHeaderParser does basic parsing of an ELF .eh_frame_hdr section. +/// +/// See DWARF spec for details: +/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +/// +template class EHHeaderParser { +public: + typedef typename A::pint_t pint_t; + + /// Information encoded in the EH frame header. + struct EHHeaderInfo { + pint_t eh_frame_ptr; + size_t fde_count; + pint_t table; + uint8_t table_enc; + }; + + static bool decodeEHHdr(A &addressSpace, pint_t ehHdrStart, pint_t ehHdrEnd, + EHHeaderInfo &ehHdrInfo); + static bool findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart, + uint32_t sectionLength, + typename CFI_Parser::FDE_Info *fdeInfo, + typename CFI_Parser::CIE_Info *cieInfo); + +private: + static bool decodeTableEntry(A &addressSpace, pint_t &tableEntry, + pint_t ehHdrStart, pint_t ehHdrEnd, + uint8_t tableEnc, + typename CFI_Parser::FDE_Info *fdeInfo, + typename CFI_Parser::CIE_Info *cieInfo); + static size_t getTableEntrySize(uint8_t tableEnc); +}; + +template +bool EHHeaderParser::decodeEHHdr(A &addressSpace, pint_t ehHdrStart, + pint_t ehHdrEnd, EHHeaderInfo &ehHdrInfo) { + pint_t p = ehHdrStart; + uint8_t version = addressSpace.get8(p++); + if (version != 1) { + _LIBUNWIND_LOG0("Unsupported .eh_frame_hdr version"); + return false; + } + + uint8_t eh_frame_ptr_enc = addressSpace.get8(p++); + uint8_t fde_count_enc = addressSpace.get8(p++); + ehHdrInfo.table_enc = addressSpace.get8(p++); + + ehHdrInfo.eh_frame_ptr = + addressSpace.getEncodedP(p, ehHdrEnd, eh_frame_ptr_enc, ehHdrStart); + ehHdrInfo.fde_count = + fde_count_enc == DW_EH_PE_omit + ? 0 + : addressSpace.getEncodedP(p, ehHdrEnd, fde_count_enc, ehHdrStart); + ehHdrInfo.table = p; + + return true; +} + +template +bool EHHeaderParser::decodeTableEntry( + A &addressSpace, pint_t &tableEntry, pint_t ehHdrStart, pint_t ehHdrEnd, + uint8_t tableEnc, typename CFI_Parser::FDE_Info *fdeInfo, + typename CFI_Parser::CIE_Info *cieInfo) { + // Have to decode the whole FDE for the PC range anyway, so just throw away + // the PC start. + addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart); + pint_t fde = + addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart); + const char *message = + CFI_Parser::decodeFDE(addressSpace, fde, fdeInfo, cieInfo); + if (message != NULL) { + _LIBUNWIND_DEBUG_LOG("EHHeaderParser::decodeTableEntry: bad fde: %s", + message); + return false; + } + + return true; +} + +template +bool EHHeaderParser::findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart, + uint32_t sectionLength, + typename CFI_Parser::FDE_Info *fdeInfo, + typename CFI_Parser::CIE_Info *cieInfo) { + pint_t ehHdrEnd = ehHdrStart + sectionLength; + + EHHeaderParser::EHHeaderInfo hdrInfo; + if (!EHHeaderParser::decodeEHHdr(addressSpace, ehHdrStart, ehHdrEnd, + hdrInfo)) + return false; + + size_t tableEntrySize = getTableEntrySize(hdrInfo.table_enc); + pint_t tableEntry; + + size_t low = 0; + for (size_t len = hdrInfo.fde_count; len > 1;) { + size_t mid = low + (len / 2); + tableEntry = hdrInfo.table + mid * tableEntrySize; + pint_t start = addressSpace.getEncodedP(tableEntry, ehHdrEnd, + hdrInfo.table_enc, ehHdrStart); + + if (start == pc) { + low = mid; + break; + } else if (start < pc) { + low = mid; + len -= (len / 2); + } else { + len /= 2; + } + } + + tableEntry = hdrInfo.table + low * tableEntrySize; + if (decodeTableEntry(addressSpace, tableEntry, ehHdrStart, ehHdrEnd, + hdrInfo.table_enc, fdeInfo, cieInfo)) { + if (pc >= fdeInfo->pcStart && pc < fdeInfo->pcEnd) + return true; + } + + return false; +} + +template +size_t EHHeaderParser::getTableEntrySize(uint8_t tableEnc) { + switch (tableEnc & 0x0f) { + case DW_EH_PE_sdata2: + case DW_EH_PE_udata2: + return 4; + case DW_EH_PE_sdata4: + case DW_EH_PE_udata4: + return 8; + case DW_EH_PE_sdata8: + case DW_EH_PE_udata8: + return 16; + case DW_EH_PE_sleb128: + case DW_EH_PE_uleb128: + _LIBUNWIND_ABORT("Can't binary search on variable length encoded data."); + case DW_EH_PE_omit: + return 0; + default: + _LIBUNWIND_ABORT("Unknown DWARF encoding for search table."); + } +} + +} + +#endif diff --git a/src/coreclr/nativeaot/libunwind/src/RWMutex.hpp b/src/coreclr/nativeaot/libunwind/src/RWMutex.hpp new file mode 100644 index 00000000000000..a37ac77144f389 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/RWMutex.hpp @@ -0,0 +1,114 @@ +//===----------------------------- Registers.hpp --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Abstract interface to shared reader/writer log, hiding platform and +// configuration differences. +// +//===----------------------------------------------------------------------===// + +#ifndef __RWMUTEX_HPP__ +#define __RWMUTEX_HPP__ + +#if defined(_WIN32) +#include +#elif !defined(_LIBUNWIND_HAS_NO_THREADS) +#include +#if defined(__unix__) && defined(__ELF__) && defined(_LIBUNWIND_HAS_COMMENT_LIB_PRAGMA) +#pragma comment(lib, "pthread") +#endif +#endif + +namespace libunwind { + +#if defined(_LIBUNWIND_HAS_NO_THREADS) + +class _LIBUNWIND_HIDDEN RWMutex { +public: + bool lock_shared() { return true; } + bool unlock_shared() { return true; } + bool lock() { return true; } + bool unlock() { return true; } +}; + +#elif defined(_WIN32) + +class _LIBUNWIND_HIDDEN RWMutex { +public: + bool lock_shared() { + AcquireSRWLockShared(&_lock); + return true; + } + bool unlock_shared() { + ReleaseSRWLockShared(&_lock); + return true; + } + bool lock() { + AcquireSRWLockExclusive(&_lock); + return true; + } + bool unlock() { + ReleaseSRWLockExclusive(&_lock); + return true; + } + +private: + SRWLOCK _lock = SRWLOCK_INIT; +}; + +#elif !defined(LIBUNWIND_USE_WEAK_PTHREAD) + +class _LIBUNWIND_HIDDEN RWMutex { +public: + bool lock_shared() { return pthread_rwlock_rdlock(&_lock) == 0; } + bool unlock_shared() { return pthread_rwlock_unlock(&_lock) == 0; } + bool lock() { return pthread_rwlock_wrlock(&_lock) == 0; } + bool unlock() { return pthread_rwlock_unlock(&_lock) == 0; } + +private: + pthread_rwlock_t _lock = PTHREAD_RWLOCK_INITIALIZER; +}; + +#else + +extern "C" int __attribute__((weak)) +pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); +extern "C" int __attribute__((weak)) +pthread_rwlock_rdlock(pthread_rwlock_t *lock); +extern "C" int __attribute__((weak)) +pthread_rwlock_wrlock(pthread_rwlock_t *lock); +extern "C" int __attribute__((weak)) +pthread_rwlock_unlock(pthread_rwlock_t *lock); + +// Calls to the locking functions are gated on pthread_create, and not the +// functions themselves, because the data structure should only be locked if +// another thread has been created. This is what similar libraries do. + +class _LIBUNWIND_HIDDEN RWMutex { +public: + bool lock_shared() { + return !pthread_create || (pthread_rwlock_rdlock(&_lock) == 0); + } + bool unlock_shared() { + return !pthread_create || (pthread_rwlock_unlock(&_lock) == 0); + } + bool lock() { + return !pthread_create || (pthread_rwlock_wrlock(&_lock) == 0); + } + bool unlock() { + return !pthread_create || (pthread_rwlock_unlock(&_lock) == 0); + } + +private: + pthread_rwlock_t _lock = PTHREAD_RWLOCK_INITIALIZER; +}; + +#endif + +} // namespace libunwind + +#endif // __RWMUTEX_HPP__ diff --git a/src/coreclr/nativeaot/libunwind/src/Registers.hpp b/src/coreclr/nativeaot/libunwind/src/Registers.hpp new file mode 100644 index 00000000000000..e2f05fb0990d0d --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/Registers.hpp @@ -0,0 +1,3720 @@ +//===----------------------------- Registers.hpp --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Models register sets for supported processors. +// +//===----------------------------------------------------------------------===// + +#ifndef __REGISTERS_HPP__ +#define __REGISTERS_HPP__ + +#include +#include + +#include "libunwind.h" +#include "config.h" + +namespace libunwind { + +// For emulating 128-bit registers +struct v128 { uint32_t vec[4]; }; + +enum { + REGISTERS_X86, + REGISTERS_X86_64, + REGISTERS_PPC, + REGISTERS_PPC64, + REGISTERS_ARM64, + REGISTERS_ARM, + REGISTERS_OR1K, + REGISTERS_MIPS_O32, + REGISTERS_MIPS_NEWABI, + REGISTERS_SPARC, +}; + +#if defined(_LIBUNWIND_TARGET_I386) +/// Registers_x86 holds the register state of a thread in a 32-bit intel +/// process. +class _LIBUNWIND_HIDDEN Registers_x86 { +public: + Registers_x86(); + Registers_x86(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value, uint32_t location); + uint32_t getRegisterLocation(int num) const; + bool validFloatRegister(int) const { return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86; } + static int getArch() { return REGISTERS_X86; } + + uint32_t getSP() const { return _registers.__esp; } + void setSP(uint32_t value, uint32_t location) { _registers.__esp = value; _registerLocations.__esp = location; } + uint32_t getIP() const { return _registers.__eip; } + void setIP(uint32_t value, uint32_t location) { _registers.__eip = value; _registerLocations.__eip = location; } + uint32_t getEBP() const { return _registers.__ebp; } + void setEBP(uint32_t value, uint32_t location) { _registers.__ebp = value; _registerLocations.__ebp = location; } + uint32_t getEBX() const { return _registers.__ebx; } + void setEBX(uint32_t value, uint32_t location) { _registers.__ebx = value; _registerLocations.__ebx = location; } + uint32_t getECX() const { return _registers.__ecx; } + void setECX(uint32_t value, uint32_t location) { _registers.__ecx = value; _registerLocations.__ecx = location; } + uint32_t getEDX() const { return _registers.__edx; } + void setEDX(uint32_t value, uint32_t location) { _registers.__edx = value; _registerLocations.__edx = location; } + uint32_t getESI() const { return _registers.__esi; } + void setESI(uint32_t value, uint32_t location) { _registers.__esi = value; _registerLocations.__esi = location; } + uint32_t getEDI() const { return _registers.__edi; } + void setEDI(uint32_t value, uint32_t location) { _registers.__edi = value; _registerLocations.__edi = location; } + +private: + struct GPRs { + unsigned int __eax; + unsigned int __ebx; + unsigned int __ecx; + unsigned int __edx; + unsigned int __edi; + unsigned int __esi; + unsigned int __ebp; + unsigned int __esp; + unsigned int __ss; + unsigned int __eflags; + unsigned int __eip; + unsigned int __cs; + unsigned int __ds; + unsigned int __es; + unsigned int __fs; + unsigned int __gs; + }; + struct GPRLocations { + unsigned int __eax; + unsigned int __ebx; + unsigned int __ecx; + unsigned int __edx; + unsigned int __edi; + unsigned int __esi; + unsigned int __ebp; + unsigned int __esp; + unsigned int __eip; + }; + + GPRs _registers; + GPRLocations _registerLocations; +}; + +inline Registers_x86::Registers_x86(const void *registers) { + static_assert((check_fit::does_fit), + "x86 registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); + memset(&_registerLocations, 0, sizeof(_registerLocations)); +} + +inline Registers_x86::Registers_x86() { + memset(&_registers, 0, sizeof(_registers)); + memset(&_registerLocations, 0, sizeof(_registerLocations)); +} + +inline bool Registers_x86::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 7) + return false; + return true; +} + +inline uint32_t Registers_x86::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__eip; + case UNW_REG_SP: + return _registers.__esp; + case UNW_X86_EAX: + return _registers.__eax; + case UNW_X86_ECX: + return _registers.__ecx; + case UNW_X86_EDX: + return _registers.__edx; + case UNW_X86_EBX: + return _registers.__ebx; +#if !defined(__APPLE__) + case UNW_X86_ESP: +#else + case UNW_X86_EBP: +#endif + return _registers.__ebp; +#if !defined(__APPLE__) + case UNW_X86_EBP: +#else + case UNW_X86_ESP: +#endif + return _registers.__esp; + case UNW_X86_ESI: + return _registers.__esi; + case UNW_X86_EDI: + return _registers.__edi; + } + _LIBUNWIND_ABORT("unsupported x86 register"); +} + +inline void Registers_x86::setRegister(int regNum, uint32_t value, uint32_t location) { + switch (regNum) { + case UNW_REG_IP: + _registers.__eip = value; + _registerLocations.__eip = location; + return; + case UNW_REG_SP: + _registers.__esp = value; + _registerLocations.__esp = location; + return; + case UNW_X86_EAX: + _registers.__eax = value; + _registerLocations.__eax = location; + return; + case UNW_X86_ECX: + _registers.__ecx = value; + _registerLocations.__ecx = location; + return; + case UNW_X86_EDX: + _registers.__edx = value; + _registerLocations.__edx = location; + return; + case UNW_X86_EBX: + _registers.__ebx = value; + _registerLocations.__ebx = location; + return; +#if !defined(__APPLE__) + case UNW_X86_ESP: +#else + case UNW_X86_EBP: +#endif + _registers.__ebp = value; + _registerLocations.__ebp = location; + return; +#if !defined(__APPLE__) + case UNW_X86_EBP: +#else + case UNW_X86_ESP: +#endif + _registers.__esp = value; + _registerLocations.__esp = location; + return; + case UNW_X86_ESI: + _registers.__esi = value; + _registerLocations.__esi = location; + return; + case UNW_X86_EDI: + _registers.__edi = value; + _registerLocations.__edi = location; + return; + } + _LIBUNWIND_ABORT("unsupported x86 register"); +} + +inline uint32_t Registers_x86::getRegisterLocation(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registerLocations.__eip; + case UNW_REG_SP: + return _registerLocations.__esp; + case UNW_X86_EAX: + return _registerLocations.__eax; + case UNW_X86_ECX: + return _registerLocations.__ecx; + case UNW_X86_EDX: + return _registerLocations.__edx; + case UNW_X86_EBX: + return _registerLocations.__ebx; + case UNW_X86_EBP: + return _registerLocations.__ebp; + case UNW_X86_ESP: + return _registerLocations.__esp; + case UNW_X86_ESI: + return _registerLocations.__esi; + case UNW_X86_EDI: + return _registerLocations.__edi; + } + _LIBUNWIND_ABORT("unsupported x86 register"); +} + +inline const char *Registers_x86::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "esp"; + case UNW_X86_EAX: + return "eax"; + case UNW_X86_ECX: + return "ecx"; + case UNW_X86_EDX: + return "edx"; + case UNW_X86_EBX: + return "ebx"; + case UNW_X86_EBP: + return "ebp"; + case UNW_X86_ESP: + return "esp"; + case UNW_X86_ESI: + return "esi"; + case UNW_X86_EDI: + return "edi"; + default: + return "unknown register"; + } +} + +inline double Registers_x86::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no x86 float registers"); +} + +inline void Registers_x86::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no x86 float registers"); +} + +inline v128 Registers_x86::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no x86 vector registers"); +} + +inline void Registers_x86::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no x86 vector registers"); +} +#endif // _LIBUNWIND_TARGET_I386 + + +#if defined(_LIBUNWIND_TARGET_X86_64) +/// Registers_x86_64 holds the register state of a thread in a 64-bit intel +/// process. +class _LIBUNWIND_HIDDEN Registers_x86_64 { +public: + Registers_x86_64(); + Registers_x86_64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value, uint64_t location); + uint64_t getRegisterLocation(int num) const; + bool validFloatRegister(int) const { return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64; } + static int getArch() { return REGISTERS_X86_64; } + + uint64_t getSP() const { return _registers.__rsp; } + void setSP(uint64_t value, uint64_t location) { _registers.__rsp = value; _registerLocations.__rsp = location;} + uint64_t getIP() const { return _registers.__rip; } + void setIP(uint64_t value, uint64_t location) { _registers.__rip = value; _registerLocations.__rip = location; } + uint64_t getRBP() const { return _registers.__rbp; } + void setRBP(uint64_t value, uint64_t location) { _registers.__rbp = value; _registerLocations.__rbp = location; } + uint64_t getRBX() const { return _registers.__rbx; } + void setRBX(uint64_t value, uint64_t location) { _registers.__rbx = value; _registerLocations.__rbx = location; } + uint64_t getR12() const { return _registers.__r12; } + void setR12(uint64_t value, uint64_t location) { _registers.__r12 = value; _registerLocations.__r12 = location; } + uint64_t getR13() const { return _registers.__r13; } + void setR13(uint64_t value, uint64_t location) { _registers.__r13 = value; _registerLocations.__r13 = location; } + uint64_t getR14() const { return _registers.__r14; } + void setR14(uint64_t value, uint64_t location) { _registers.__r14 = value; _registerLocations.__r14 = location; } + uint64_t getR15() const { return _registers.__r15; } + void setR15(uint64_t value, uint64_t location) { _registers.__r15 = value; _registerLocations.__r15 = location; } + +private: + struct GPRs { + uint64_t __rax; + uint64_t __rbx; + uint64_t __rcx; + uint64_t __rdx; + uint64_t __rdi; + uint64_t __rsi; + uint64_t __rbp; + uint64_t __rsp; + uint64_t __r8; + uint64_t __r9; + uint64_t __r10; + uint64_t __r11; + uint64_t __r12; + uint64_t __r13; + uint64_t __r14; + uint64_t __r15; + uint64_t __rip; + uint64_t __rflags; + uint64_t __cs; + uint64_t __fs; + uint64_t __gs; +#if defined(_WIN64) + uint64_t __padding; // 16-byte align +#endif + }; + struct GPRLocations { + uint64_t __rax; + uint64_t __rbx; + uint64_t __rcx; + uint64_t __rdx; + uint64_t __rdi; + uint64_t __rsi; + uint64_t __rbp; + uint64_t __rsp; + uint64_t __r8; + uint64_t __r9; + uint64_t __r10; + uint64_t __r11; + uint64_t __r12; + uint64_t __r13; + uint64_t __r14; + uint64_t __r15; + uint64_t __rip; + }; + GPRs _registers; + GPRLocations _registerLocations; +#if defined(_WIN64) + v128 _xmm[16]; +#endif +}; + +inline Registers_x86_64::Registers_x86_64(const void *registers) { + static_assert((check_fit::does_fit), + "x86_64 registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); + memset(&_registerLocations, 0, sizeof(_registerLocations)); +} + +inline Registers_x86_64::Registers_x86_64() { + memset(&_registers, 0, sizeof(_registers)); + memset(&_registerLocations, 0, sizeof(_registerLocations)); +} + +inline bool Registers_x86_64::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 15) + return false; + return true; +} + +inline uint64_t Registers_x86_64::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__rip; + case UNW_REG_SP: + return _registers.__rsp; + case UNW_X86_64_RAX: + return _registers.__rax; + case UNW_X86_64_RDX: + return _registers.__rdx; + case UNW_X86_64_RCX: + return _registers.__rcx; + case UNW_X86_64_RBX: + return _registers.__rbx; + case UNW_X86_64_RSI: + return _registers.__rsi; + case UNW_X86_64_RDI: + return _registers.__rdi; + case UNW_X86_64_RBP: + return _registers.__rbp; + case UNW_X86_64_RSP: + return _registers.__rsp; + case UNW_X86_64_R8: + return _registers.__r8; + case UNW_X86_64_R9: + return _registers.__r9; + case UNW_X86_64_R10: + return _registers.__r10; + case UNW_X86_64_R11: + return _registers.__r11; + case UNW_X86_64_R12: + return _registers.__r12; + case UNW_X86_64_R13: + return _registers.__r13; + case UNW_X86_64_R14: + return _registers.__r14; + case UNW_X86_64_R15: + return _registers.__r15; + } + _LIBUNWIND_ABORT("unsupported x86_64 register"); +} + +inline uint64_t Registers_x86_64::getRegisterLocation(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registerLocations.__rip; + case UNW_REG_SP: + return _registerLocations.__rsp; + case UNW_X86_64_RAX: + return _registerLocations.__rax; + case UNW_X86_64_RDX: + return _registerLocations.__rdx; + case UNW_X86_64_RCX: + return _registerLocations.__rcx; + case UNW_X86_64_RBX: + return _registerLocations.__rbx; + case UNW_X86_64_RSI: + return _registerLocations.__rsi; + case UNW_X86_64_RDI: + return _registerLocations.__rdi; + case UNW_X86_64_RBP: + return _registerLocations.__rbp; + case UNW_X86_64_RSP: + return _registerLocations.__rsp; + case UNW_X86_64_R8: + return _registerLocations.__r8; + case UNW_X86_64_R9: + return _registerLocations.__r9; + case UNW_X86_64_R10: + return _registerLocations.__r10; + case UNW_X86_64_R11: + return _registerLocations.__r11; + case UNW_X86_64_R12: + return _registerLocations.__r12; + case UNW_X86_64_R13: + return _registerLocations.__r13; + case UNW_X86_64_R14: + return _registerLocations.__r14; + case UNW_X86_64_R15: + return _registerLocations.__r15; + } + _LIBUNWIND_ABORT("unsupported x86_64 register"); +} + +inline void Registers_x86_64::setRegister(int regNum, uint64_t value, uint64_t location) { + switch (regNum) { + case UNW_REG_IP: + _registers.__rip = value; + _registerLocations.__rip = location; + return; + case UNW_REG_SP: + _registers.__rsp = value; + _registerLocations.__rsp = location; + return; + case UNW_X86_64_RAX: + _registers.__rax = value; + _registerLocations.__rax = location; + return; + case UNW_X86_64_RDX: + _registers.__rdx = value; + _registerLocations.__rdx = location; + return; + case UNW_X86_64_RCX: + _registers.__rcx = value; + _registerLocations.__rcx = location; + return; + case UNW_X86_64_RBX: + _registers.__rbx = value; + _registerLocations.__rbx = location; + return; + case UNW_X86_64_RSI: + _registers.__rsi = value; + _registerLocations.__rsi = location; + return; + case UNW_X86_64_RDI: + _registers.__rdi = value; + _registerLocations.__rdi = location; + return; + case UNW_X86_64_RBP: + _registers.__rbp = value; + _registerLocations.__rbp = location; + return; + case UNW_X86_64_RSP: + _registers.__rsp = value; + _registerLocations.__rsp = location; + return; + case UNW_X86_64_R8: + _registers.__r8 = value; + _registerLocations.__r8 = location; + return; + case UNW_X86_64_R9: + _registers.__r9 = value; + _registerLocations.__r9 = location; + return; + case UNW_X86_64_R10: + _registers.__r10 = value; + _registerLocations.__r10 = location; + return; + case UNW_X86_64_R11: + _registers.__r11 = value; + _registerLocations.__r11 = location; + return; + case UNW_X86_64_R12: + _registers.__r12 = value; + _registerLocations.__r12 = location; + return; + case UNW_X86_64_R13: + _registers.__r13 = value; + _registerLocations.__r13 = location; + return; + case UNW_X86_64_R14: + _registers.__r14 = value; + _registerLocations.__r14 = location; + return; + case UNW_X86_64_R15: + _registers.__r15 = value; + _registerLocations.__r15 = location; + return; + } + _LIBUNWIND_ABORT("unsupported x86_64 register"); +} + +inline const char *Registers_x86_64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "rip"; + case UNW_REG_SP: + return "rsp"; + case UNW_X86_64_RAX: + return "rax"; + case UNW_X86_64_RDX: + return "rdx"; + case UNW_X86_64_RCX: + return "rcx"; + case UNW_X86_64_RBX: + return "rbx"; + case UNW_X86_64_RSI: + return "rsi"; + case UNW_X86_64_RDI: + return "rdi"; + case UNW_X86_64_RBP: + return "rbp"; + case UNW_X86_64_RSP: + return "rsp"; + case UNW_X86_64_R8: + return "r8"; + case UNW_X86_64_R9: + return "r9"; + case UNW_X86_64_R10: + return "r10"; + case UNW_X86_64_R11: + return "r11"; + case UNW_X86_64_R12: + return "r12"; + case UNW_X86_64_R13: + return "r13"; + case UNW_X86_64_R14: + return "r14"; + case UNW_X86_64_R15: + return "r15"; + case UNW_X86_64_XMM0: + return "xmm0"; + case UNW_X86_64_XMM1: + return "xmm1"; + case UNW_X86_64_XMM2: + return "xmm2"; + case UNW_X86_64_XMM3: + return "xmm3"; + case UNW_X86_64_XMM4: + return "xmm4"; + case UNW_X86_64_XMM5: + return "xmm5"; + case UNW_X86_64_XMM6: + return "xmm6"; + case UNW_X86_64_XMM7: + return "xmm7"; + case UNW_X86_64_XMM8: + return "xmm8"; + case UNW_X86_64_XMM9: + return "xmm9"; + case UNW_X86_64_XMM10: + return "xmm10"; + case UNW_X86_64_XMM11: + return "xmm11"; + case UNW_X86_64_XMM12: + return "xmm12"; + case UNW_X86_64_XMM13: + return "xmm13"; + case UNW_X86_64_XMM14: + return "xmm14"; + case UNW_X86_64_XMM15: + return "xmm15"; + default: + return "unknown register"; + } +} + +inline double Registers_x86_64::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no x86_64 float registers"); +} + +inline void Registers_x86_64::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no x86_64 float registers"); +} + +inline bool Registers_x86_64::validVectorRegister(int regNum) const { +#if defined(_WIN64) + if (regNum < UNW_X86_64_XMM0) + return false; + if (regNum > UNW_X86_64_XMM15) + return false; + return true; +#else + (void)regNum; // suppress unused parameter warning + return false; +#endif +} + +inline v128 Registers_x86_64::getVectorRegister(int regNum) const { +#if defined(_WIN64) + assert(validVectorRegister(regNum)); + return _xmm[regNum - UNW_X86_64_XMM0]; +#else + (void)regNum; // suppress unused parameter warning + _LIBUNWIND_ABORT("no x86_64 vector registers"); +#endif +} + +inline void Registers_x86_64::setVectorRegister(int regNum, v128 value) { +#if defined(_WIN64) + assert(validVectorRegister(regNum)); + _xmm[regNum - UNW_X86_64_XMM0] = value; +#else + (void)regNum; (void)value; // suppress unused parameter warnings + _LIBUNWIND_ABORT("no x86_64 vector registers"); +#endif +} +#endif // _LIBUNWIND_TARGET_X86_64 + + +#if defined(_LIBUNWIND_TARGET_PPC) +/// Registers_ppc holds the register state of a thread in a 32-bit PowerPC +/// process. +class _LIBUNWIND_HIDDEN Registers_ppc { +public: + Registers_ppc(); + Registers_ppc(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC; } + static int getArch() { return REGISTERS_PPC; } + + uint64_t getSP() const { return _registers.__r1; } + void setSP(uint32_t value) { _registers.__r1 = value; } + uint64_t getIP() const { return _registers.__srr0; } + void setIP(uint32_t value) { _registers.__srr0 = value; } + +private: + struct ppc_thread_state_t { + unsigned int __srr0; /* Instruction address register (PC) */ + unsigned int __srr1; /* Machine state register (supervisor) */ + unsigned int __r0; + unsigned int __r1; + unsigned int __r2; + unsigned int __r3; + unsigned int __r4; + unsigned int __r5; + unsigned int __r6; + unsigned int __r7; + unsigned int __r8; + unsigned int __r9; + unsigned int __r10; + unsigned int __r11; + unsigned int __r12; + unsigned int __r13; + unsigned int __r14; + unsigned int __r15; + unsigned int __r16; + unsigned int __r17; + unsigned int __r18; + unsigned int __r19; + unsigned int __r20; + unsigned int __r21; + unsigned int __r22; + unsigned int __r23; + unsigned int __r24; + unsigned int __r25; + unsigned int __r26; + unsigned int __r27; + unsigned int __r28; + unsigned int __r29; + unsigned int __r30; + unsigned int __r31; + unsigned int __cr; /* Condition register */ + unsigned int __xer; /* User's integer exception register */ + unsigned int __lr; /* Link register */ + unsigned int __ctr; /* Count register */ + unsigned int __mq; /* MQ register (601 only) */ + unsigned int __vrsave; /* Vector Save Register */ + }; + + struct ppc_float_state_t { + double __fpregs[32]; + + unsigned int __fpscr_pad; /* fpscr is 64 bits, 32 bits of rubbish */ + unsigned int __fpscr; /* floating point status register */ + }; + + ppc_thread_state_t _registers; + ppc_float_state_t _floatRegisters; + v128 _vectorRegisters[32]; // offset 424 +}; + +inline Registers_ppc::Registers_ppc(const void *registers) { + static_assert((check_fit::does_fit), + "ppc registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); + static_assert(sizeof(ppc_thread_state_t) == 160, + "expected float register offset to be 160"); + memcpy(&_floatRegisters, + static_cast(registers) + sizeof(ppc_thread_state_t), + sizeof(_floatRegisters)); + static_assert(sizeof(ppc_thread_state_t) + sizeof(ppc_float_state_t) == 424, + "expected vector register offset to be 424 bytes"); + memcpy(_vectorRegisters, + static_cast(registers) + sizeof(ppc_thread_state_t) + + sizeof(ppc_float_state_t), + sizeof(_vectorRegisters)); +} + +inline Registers_ppc::Registers_ppc() { + memset(&_registers, 0, sizeof(_registers)); + memset(&_floatRegisters, 0, sizeof(_floatRegisters)); + memset(&_vectorRegisters, 0, sizeof(_vectorRegisters)); +} + +inline bool Registers_ppc::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum == UNW_PPC_VRSAVE) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_PPC_R31) + return true; + if (regNum == UNW_PPC_MQ) + return true; + if (regNum == UNW_PPC_LR) + return true; + if (regNum == UNW_PPC_CTR) + return true; + if ((UNW_PPC_CR0 <= regNum) && (regNum <= UNW_PPC_CR7)) + return true; + return false; +} + +inline uint32_t Registers_ppc::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__srr0; + case UNW_REG_SP: + return _registers.__r1; + case UNW_PPC_R0: + return _registers.__r0; + case UNW_PPC_R1: + return _registers.__r1; + case UNW_PPC_R2: + return _registers.__r2; + case UNW_PPC_R3: + return _registers.__r3; + case UNW_PPC_R4: + return _registers.__r4; + case UNW_PPC_R5: + return _registers.__r5; + case UNW_PPC_R6: + return _registers.__r6; + case UNW_PPC_R7: + return _registers.__r7; + case UNW_PPC_R8: + return _registers.__r8; + case UNW_PPC_R9: + return _registers.__r9; + case UNW_PPC_R10: + return _registers.__r10; + case UNW_PPC_R11: + return _registers.__r11; + case UNW_PPC_R12: + return _registers.__r12; + case UNW_PPC_R13: + return _registers.__r13; + case UNW_PPC_R14: + return _registers.__r14; + case UNW_PPC_R15: + return _registers.__r15; + case UNW_PPC_R16: + return _registers.__r16; + case UNW_PPC_R17: + return _registers.__r17; + case UNW_PPC_R18: + return _registers.__r18; + case UNW_PPC_R19: + return _registers.__r19; + case UNW_PPC_R20: + return _registers.__r20; + case UNW_PPC_R21: + return _registers.__r21; + case UNW_PPC_R22: + return _registers.__r22; + case UNW_PPC_R23: + return _registers.__r23; + case UNW_PPC_R24: + return _registers.__r24; + case UNW_PPC_R25: + return _registers.__r25; + case UNW_PPC_R26: + return _registers.__r26; + case UNW_PPC_R27: + return _registers.__r27; + case UNW_PPC_R28: + return _registers.__r28; + case UNW_PPC_R29: + return _registers.__r29; + case UNW_PPC_R30: + return _registers.__r30; + case UNW_PPC_R31: + return _registers.__r31; + case UNW_PPC_LR: + return _registers.__lr; + case UNW_PPC_CR0: + return (_registers.__cr & 0xF0000000); + case UNW_PPC_CR1: + return (_registers.__cr & 0x0F000000); + case UNW_PPC_CR2: + return (_registers.__cr & 0x00F00000); + case UNW_PPC_CR3: + return (_registers.__cr & 0x000F0000); + case UNW_PPC_CR4: + return (_registers.__cr & 0x0000F000); + case UNW_PPC_CR5: + return (_registers.__cr & 0x00000F00); + case UNW_PPC_CR6: + return (_registers.__cr & 0x000000F0); + case UNW_PPC_CR7: + return (_registers.__cr & 0x0000000F); + case UNW_PPC_VRSAVE: + return _registers.__vrsave; + } + _LIBUNWIND_ABORT("unsupported ppc register"); +} + +inline void Registers_ppc::setRegister(int regNum, uint32_t value) { + //fprintf(stderr, "Registers_ppc::setRegister(%d, 0x%08X)\n", regNum, value); + switch (regNum) { + case UNW_REG_IP: + _registers.__srr0 = value; + return; + case UNW_REG_SP: + _registers.__r1 = value; + return; + case UNW_PPC_R0: + _registers.__r0 = value; + return; + case UNW_PPC_R1: + _registers.__r1 = value; + return; + case UNW_PPC_R2: + _registers.__r2 = value; + return; + case UNW_PPC_R3: + _registers.__r3 = value; + return; + case UNW_PPC_R4: + _registers.__r4 = value; + return; + case UNW_PPC_R5: + _registers.__r5 = value; + return; + case UNW_PPC_R6: + _registers.__r6 = value; + return; + case UNW_PPC_R7: + _registers.__r7 = value; + return; + case UNW_PPC_R8: + _registers.__r8 = value; + return; + case UNW_PPC_R9: + _registers.__r9 = value; + return; + case UNW_PPC_R10: + _registers.__r10 = value; + return; + case UNW_PPC_R11: + _registers.__r11 = value; + return; + case UNW_PPC_R12: + _registers.__r12 = value; + return; + case UNW_PPC_R13: + _registers.__r13 = value; + return; + case UNW_PPC_R14: + _registers.__r14 = value; + return; + case UNW_PPC_R15: + _registers.__r15 = value; + return; + case UNW_PPC_R16: + _registers.__r16 = value; + return; + case UNW_PPC_R17: + _registers.__r17 = value; + return; + case UNW_PPC_R18: + _registers.__r18 = value; + return; + case UNW_PPC_R19: + _registers.__r19 = value; + return; + case UNW_PPC_R20: + _registers.__r20 = value; + return; + case UNW_PPC_R21: + _registers.__r21 = value; + return; + case UNW_PPC_R22: + _registers.__r22 = value; + return; + case UNW_PPC_R23: + _registers.__r23 = value; + return; + case UNW_PPC_R24: + _registers.__r24 = value; + return; + case UNW_PPC_R25: + _registers.__r25 = value; + return; + case UNW_PPC_R26: + _registers.__r26 = value; + return; + case UNW_PPC_R27: + _registers.__r27 = value; + return; + case UNW_PPC_R28: + _registers.__r28 = value; + return; + case UNW_PPC_R29: + _registers.__r29 = value; + return; + case UNW_PPC_R30: + _registers.__r30 = value; + return; + case UNW_PPC_R31: + _registers.__r31 = value; + return; + case UNW_PPC_MQ: + _registers.__mq = value; + return; + case UNW_PPC_LR: + _registers.__lr = value; + return; + case UNW_PPC_CTR: + _registers.__ctr = value; + return; + case UNW_PPC_CR0: + _registers.__cr &= 0x0FFFFFFF; + _registers.__cr |= (value & 0xF0000000); + return; + case UNW_PPC_CR1: + _registers.__cr &= 0xF0FFFFFF; + _registers.__cr |= (value & 0x0F000000); + return; + case UNW_PPC_CR2: + _registers.__cr &= 0xFF0FFFFF; + _registers.__cr |= (value & 0x00F00000); + return; + case UNW_PPC_CR3: + _registers.__cr &= 0xFFF0FFFF; + _registers.__cr |= (value & 0x000F0000); + return; + case UNW_PPC_CR4: + _registers.__cr &= 0xFFFF0FFF; + _registers.__cr |= (value & 0x0000F000); + return; + case UNW_PPC_CR5: + _registers.__cr &= 0xFFFFF0FF; + _registers.__cr |= (value & 0x00000F00); + return; + case UNW_PPC_CR6: + _registers.__cr &= 0xFFFFFF0F; + _registers.__cr |= (value & 0x000000F0); + return; + case UNW_PPC_CR7: + _registers.__cr &= 0xFFFFFFF0; + _registers.__cr |= (value & 0x0000000F); + return; + case UNW_PPC_VRSAVE: + _registers.__vrsave = value; + return; + // not saved + return; + case UNW_PPC_XER: + _registers.__xer = value; + return; + case UNW_PPC_AP: + case UNW_PPC_VSCR: + case UNW_PPC_SPEFSCR: + // not saved + return; + } + _LIBUNWIND_ABORT("unsupported ppc register"); +} + +inline bool Registers_ppc::validFloatRegister(int regNum) const { + if (regNum < UNW_PPC_F0) + return false; + if (regNum > UNW_PPC_F31) + return false; + return true; +} + +inline double Registers_ppc::getFloatRegister(int regNum) const { + assert(validFloatRegister(regNum)); + return _floatRegisters.__fpregs[regNum - UNW_PPC_F0]; +} + +inline void Registers_ppc::setFloatRegister(int regNum, double value) { + assert(validFloatRegister(regNum)); + _floatRegisters.__fpregs[regNum - UNW_PPC_F0] = value; +} + +inline bool Registers_ppc::validVectorRegister(int regNum) const { + if (regNum < UNW_PPC_V0) + return false; + if (regNum > UNW_PPC_V31) + return false; + return true; +} + +inline v128 Registers_ppc::getVectorRegister(int regNum) const { + assert(validVectorRegister(regNum)); + v128 result = _vectorRegisters[regNum - UNW_PPC_V0]; + return result; +} + +inline void Registers_ppc::setVectorRegister(int regNum, v128 value) { + assert(validVectorRegister(regNum)); + _vectorRegisters[regNum - UNW_PPC_V0] = value; +} + +inline const char *Registers_ppc::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_PPC_R0: + return "r0"; + case UNW_PPC_R1: + return "r1"; + case UNW_PPC_R2: + return "r2"; + case UNW_PPC_R3: + return "r3"; + case UNW_PPC_R4: + return "r4"; + case UNW_PPC_R5: + return "r5"; + case UNW_PPC_R6: + return "r6"; + case UNW_PPC_R7: + return "r7"; + case UNW_PPC_R8: + return "r8"; + case UNW_PPC_R9: + return "r9"; + case UNW_PPC_R10: + return "r10"; + case UNW_PPC_R11: + return "r11"; + case UNW_PPC_R12: + return "r12"; + case UNW_PPC_R13: + return "r13"; + case UNW_PPC_R14: + return "r14"; + case UNW_PPC_R15: + return "r15"; + case UNW_PPC_R16: + return "r16"; + case UNW_PPC_R17: + return "r17"; + case UNW_PPC_R18: + return "r18"; + case UNW_PPC_R19: + return "r19"; + case UNW_PPC_R20: + return "r20"; + case UNW_PPC_R21: + return "r21"; + case UNW_PPC_R22: + return "r22"; + case UNW_PPC_R23: + return "r23"; + case UNW_PPC_R24: + return "r24"; + case UNW_PPC_R25: + return "r25"; + case UNW_PPC_R26: + return "r26"; + case UNW_PPC_R27: + return "r27"; + case UNW_PPC_R28: + return "r28"; + case UNW_PPC_R29: + return "r29"; + case UNW_PPC_R30: + return "r30"; + case UNW_PPC_R31: + return "r31"; + case UNW_PPC_F0: + return "fp0"; + case UNW_PPC_F1: + return "fp1"; + case UNW_PPC_F2: + return "fp2"; + case UNW_PPC_F3: + return "fp3"; + case UNW_PPC_F4: + return "fp4"; + case UNW_PPC_F5: + return "fp5"; + case UNW_PPC_F6: + return "fp6"; + case UNW_PPC_F7: + return "fp7"; + case UNW_PPC_F8: + return "fp8"; + case UNW_PPC_F9: + return "fp9"; + case UNW_PPC_F10: + return "fp10"; + case UNW_PPC_F11: + return "fp11"; + case UNW_PPC_F12: + return "fp12"; + case UNW_PPC_F13: + return "fp13"; + case UNW_PPC_F14: + return "fp14"; + case UNW_PPC_F15: + return "fp15"; + case UNW_PPC_F16: + return "fp16"; + case UNW_PPC_F17: + return "fp17"; + case UNW_PPC_F18: + return "fp18"; + case UNW_PPC_F19: + return "fp19"; + case UNW_PPC_F20: + return "fp20"; + case UNW_PPC_F21: + return "fp21"; + case UNW_PPC_F22: + return "fp22"; + case UNW_PPC_F23: + return "fp23"; + case UNW_PPC_F24: + return "fp24"; + case UNW_PPC_F25: + return "fp25"; + case UNW_PPC_F26: + return "fp26"; + case UNW_PPC_F27: + return "fp27"; + case UNW_PPC_F28: + return "fp28"; + case UNW_PPC_F29: + return "fp29"; + case UNW_PPC_F30: + return "fp30"; + case UNW_PPC_F31: + return "fp31"; + case UNW_PPC_LR: + return "lr"; + default: + return "unknown register"; + } + +} +#endif // _LIBUNWIND_TARGET_PPC + +#if defined(_LIBUNWIND_TARGET_PPC64) +/// Registers_ppc64 holds the register state of a thread in a 64-bit PowerPC +/// process. +class _LIBUNWIND_HIDDEN Registers_ppc64 { +public: + Registers_ppc64(); + Registers_ppc64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value, uint64_t location); + uint64_t getRegisterLocation(int num) const; + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64; } + static int getArch() { return REGISTERS_PPC64; } + + uint64_t getSP() const { return _registers.__r1; } + void setSP(uint64_t value) { _registers.__r1 = value; } + uint64_t getIP() const { return _registers.__srr0; } + void setIP(uint64_t value) { _registers.__srr0 = value; } + +private: + struct ppc64_thread_state_t { + uint64_t __srr0; // Instruction address register (PC) + uint64_t __srr1; // Machine state register (supervisor) + uint64_t __r0; + uint64_t __r1; + uint64_t __r2; + uint64_t __r3; + uint64_t __r4; + uint64_t __r5; + uint64_t __r6; + uint64_t __r7; + uint64_t __r8; + uint64_t __r9; + uint64_t __r10; + uint64_t __r11; + uint64_t __r12; + uint64_t __r13; + uint64_t __r14; + uint64_t __r15; + uint64_t __r16; + uint64_t __r17; + uint64_t __r18; + uint64_t __r19; + uint64_t __r20; + uint64_t __r21; + uint64_t __r22; + uint64_t __r23; + uint64_t __r24; + uint64_t __r25; + uint64_t __r26; + uint64_t __r27; + uint64_t __r28; + uint64_t __r29; + uint64_t __r30; + uint64_t __r31; + uint64_t __cr; // Condition register + uint64_t __xer; // User's integer exception register + uint64_t __lr; // Link register + uint64_t __ctr; // Count register + uint64_t __vrsave; // Vector Save Register + }; + + union ppc64_vsr_t { + struct asfloat_s { + double f; + uint64_t v2; + } asfloat; + v128 v; + }; + + ppc64_thread_state_t _registers; + ppc64_vsr_t _vectorScalarRegisters[64]; + + static int getVectorRegNum(int num); +}; + +inline Registers_ppc64::Registers_ppc64(const void *registers) { + static_assert((check_fit::does_fit), + "ppc64 registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); + static_assert(sizeof(_registers) == 312, + "expected vector scalar register offset to be 312"); + memcpy(&_vectorScalarRegisters, + static_cast(registers) + sizeof(_registers), + sizeof(_vectorScalarRegisters)); + static_assert(sizeof(_registers) + + sizeof(_vectorScalarRegisters) == 1336, + "expected vector register offset to be 1336 bytes"); +} + +inline Registers_ppc64::Registers_ppc64() { + memset(&_registers, 0, sizeof(_registers)); + memset(&_vectorScalarRegisters, 0, sizeof(_vectorScalarRegisters)); +} + +inline bool Registers_ppc64::validRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + case UNW_REG_SP: + case UNW_PPC64_XER: + case UNW_PPC64_LR: + case UNW_PPC64_CTR: + case UNW_PPC64_VRSAVE: + return true; + } + + if (regNum >= UNW_PPC64_R0 && regNum <= UNW_PPC64_R31) + return true; + if (regNum >= UNW_PPC64_CR0 && regNum <= UNW_PPC64_CR7) + return true; + + return false; +} + +inline uint64_t Registers_ppc64::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__srr0; + case UNW_PPC64_R0: + return _registers.__r0; + case UNW_PPC64_R1: + case UNW_REG_SP: + return _registers.__r1; + case UNW_PPC64_R2: + return _registers.__r2; + case UNW_PPC64_R3: + return _registers.__r3; + case UNW_PPC64_R4: + return _registers.__r4; + case UNW_PPC64_R5: + return _registers.__r5; + case UNW_PPC64_R6: + return _registers.__r6; + case UNW_PPC64_R7: + return _registers.__r7; + case UNW_PPC64_R8: + return _registers.__r8; + case UNW_PPC64_R9: + return _registers.__r9; + case UNW_PPC64_R10: + return _registers.__r10; + case UNW_PPC64_R11: + return _registers.__r11; + case UNW_PPC64_R12: + return _registers.__r12; + case UNW_PPC64_R13: + return _registers.__r13; + case UNW_PPC64_R14: + return _registers.__r14; + case UNW_PPC64_R15: + return _registers.__r15; + case UNW_PPC64_R16: + return _registers.__r16; + case UNW_PPC64_R17: + return _registers.__r17; + case UNW_PPC64_R18: + return _registers.__r18; + case UNW_PPC64_R19: + return _registers.__r19; + case UNW_PPC64_R20: + return _registers.__r20; + case UNW_PPC64_R21: + return _registers.__r21; + case UNW_PPC64_R22: + return _registers.__r22; + case UNW_PPC64_R23: + return _registers.__r23; + case UNW_PPC64_R24: + return _registers.__r24; + case UNW_PPC64_R25: + return _registers.__r25; + case UNW_PPC64_R26: + return _registers.__r26; + case UNW_PPC64_R27: + return _registers.__r27; + case UNW_PPC64_R28: + return _registers.__r28; + case UNW_PPC64_R29: + return _registers.__r29; + case UNW_PPC64_R30: + return _registers.__r30; + case UNW_PPC64_R31: + return _registers.__r31; + case UNW_PPC64_CR0: + return (_registers.__cr & 0xF0000000); + case UNW_PPC64_CR1: + return (_registers.__cr & 0x0F000000); + case UNW_PPC64_CR2: + return (_registers.__cr & 0x00F00000); + case UNW_PPC64_CR3: + return (_registers.__cr & 0x000F0000); + case UNW_PPC64_CR4: + return (_registers.__cr & 0x0000F000); + case UNW_PPC64_CR5: + return (_registers.__cr & 0x00000F00); + case UNW_PPC64_CR6: + return (_registers.__cr & 0x000000F0); + case UNW_PPC64_CR7: + return (_registers.__cr & 0x0000000F); + case UNW_PPC64_XER: + return _registers.__xer; + case UNW_PPC64_LR: + return _registers.__lr; + case UNW_PPC64_CTR: + return _registers.__ctr; + case UNW_PPC64_VRSAVE: + return _registers.__vrsave; + } + _LIBUNWIND_ABORT("unsupported ppc64 register"); +} + +inline void Registers_ppc64::setRegister(int regNum, uint64_t value) { + switch (regNum) { + case UNW_REG_IP: + _registers.__srr0 = value; + return; + case UNW_PPC64_R0: + _registers.__r0 = value; + return; + case UNW_PPC64_R1: + case UNW_REG_SP: + _registers.__r1 = value; + return; + case UNW_PPC64_R2: + _registers.__r2 = value; + return; + case UNW_PPC64_R3: + _registers.__r3 = value; + return; + case UNW_PPC64_R4: + _registers.__r4 = value; + return; + case UNW_PPC64_R5: + _registers.__r5 = value; + return; + case UNW_PPC64_R6: + _registers.__r6 = value; + return; + case UNW_PPC64_R7: + _registers.__r7 = value; + return; + case UNW_PPC64_R8: + _registers.__r8 = value; + return; + case UNW_PPC64_R9: + _registers.__r9 = value; + return; + case UNW_PPC64_R10: + _registers.__r10 = value; + return; + case UNW_PPC64_R11: + _registers.__r11 = value; + return; + case UNW_PPC64_R12: + _registers.__r12 = value; + return; + case UNW_PPC64_R13: + _registers.__r13 = value; + return; + case UNW_PPC64_R14: + _registers.__r14 = value; + return; + case UNW_PPC64_R15: + _registers.__r15 = value; + return; + case UNW_PPC64_R16: + _registers.__r16 = value; + return; + case UNW_PPC64_R17: + _registers.__r17 = value; + return; + case UNW_PPC64_R18: + _registers.__r18 = value; + return; + case UNW_PPC64_R19: + _registers.__r19 = value; + return; + case UNW_PPC64_R20: + _registers.__r20 = value; + return; + case UNW_PPC64_R21: + _registers.__r21 = value; + return; + case UNW_PPC64_R22: + _registers.__r22 = value; + return; + case UNW_PPC64_R23: + _registers.__r23 = value; + return; + case UNW_PPC64_R24: + _registers.__r24 = value; + return; + case UNW_PPC64_R25: + _registers.__r25 = value; + return; + case UNW_PPC64_R26: + _registers.__r26 = value; + return; + case UNW_PPC64_R27: + _registers.__r27 = value; + return; + case UNW_PPC64_R28: + _registers.__r28 = value; + return; + case UNW_PPC64_R29: + _registers.__r29 = value; + return; + case UNW_PPC64_R30: + _registers.__r30 = value; + return; + case UNW_PPC64_R31: + _registers.__r31 = value; + return; + case UNW_PPC64_CR0: + _registers.__cr &= 0x0FFFFFFF; + _registers.__cr |= (value & 0xF0000000); + return; + case UNW_PPC64_CR1: + _registers.__cr &= 0xF0FFFFFF; + _registers.__cr |= (value & 0x0F000000); + return; + case UNW_PPC64_CR2: + _registers.__cr &= 0xFF0FFFFF; + _registers.__cr |= (value & 0x00F00000); + return; + case UNW_PPC64_CR3: + _registers.__cr &= 0xFFF0FFFF; + _registers.__cr |= (value & 0x000F0000); + return; + case UNW_PPC64_CR4: + _registers.__cr &= 0xFFFF0FFF; + _registers.__cr |= (value & 0x0000F000); + return; + case UNW_PPC64_CR5: + _registers.__cr &= 0xFFFFF0FF; + _registers.__cr |= (value & 0x00000F00); + return; + case UNW_PPC64_CR6: + _registers.__cr &= 0xFFFFFF0F; + _registers.__cr |= (value & 0x000000F0); + return; + case UNW_PPC64_CR7: + _registers.__cr &= 0xFFFFFFF0; + _registers.__cr |= (value & 0x0000000F); + return; + case UNW_PPC64_XER: + _registers.__xer = value; + return; + case UNW_PPC64_LR: + _registers.__lr = value; + return; + case UNW_PPC64_CTR: + _registers.__ctr = value; + return; + case UNW_PPC64_VRSAVE: + _registers.__vrsave = value; + return; + } + _LIBUNWIND_ABORT("unsupported ppc64 register"); +} + +inline bool Registers_ppc64::validFloatRegister(int regNum) const { + return regNum >= UNW_PPC64_F0 && regNum <= UNW_PPC64_F31; +} + +inline double Registers_ppc64::getFloatRegister(int regNum) const { + assert(validFloatRegister(regNum)); + return _vectorScalarRegisters[regNum - UNW_PPC64_F0].asfloat.f; +} + +inline void Registers_ppc64::setFloatRegister(int regNum, double value) { + assert(validFloatRegister(regNum)); + _vectorScalarRegisters[regNum - UNW_PPC64_F0].asfloat.f = value; +} + +inline bool Registers_ppc64::validVectorRegister(int regNum) const { +#ifdef PPC64_HAS_VMX + if (regNum >= UNW_PPC64_VS0 && regNum <= UNW_PPC64_VS31) + return true; + if (regNum >= UNW_PPC64_VS32 && regNum <= UNW_PPC64_VS63) + return true; +#else + if (regNum >= UNW_PPC64_V0 && regNum <= UNW_PPC64_V31) + return true; +#endif + return false; +} + +inline int Registers_ppc64::getVectorRegNum(int num) +{ + if (num >= UNW_PPC64_VS0 && num <= UNW_PPC64_VS31) + return num - UNW_PPC64_VS0; + else + return num - UNW_PPC64_VS32 + 32; +} + +inline v128 Registers_ppc64::getVectorRegister(int regNum) const { + assert(validVectorRegister(regNum)); + return _vectorScalarRegisters[getVectorRegNum(regNum)].v; +} + +inline void Registers_ppc64::setVectorRegister(int regNum, v128 value) { + assert(validVectorRegister(regNum)); + _vectorScalarRegisters[getVectorRegNum(regNum)].v = value; +} + +inline const char *Registers_ppc64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_PPC64_R0: + return "r0"; + case UNW_PPC64_R1: + return "r1"; + case UNW_PPC64_R2: + return "r2"; + case UNW_PPC64_R3: + return "r3"; + case UNW_PPC64_R4: + return "r4"; + case UNW_PPC64_R5: + return "r5"; + case UNW_PPC64_R6: + return "r6"; + case UNW_PPC64_R7: + return "r7"; + case UNW_PPC64_R8: + return "r8"; + case UNW_PPC64_R9: + return "r9"; + case UNW_PPC64_R10: + return "r10"; + case UNW_PPC64_R11: + return "r11"; + case UNW_PPC64_R12: + return "r12"; + case UNW_PPC64_R13: + return "r13"; + case UNW_PPC64_R14: + return "r14"; + case UNW_PPC64_R15: + return "r15"; + case UNW_PPC64_R16: + return "r16"; + case UNW_PPC64_R17: + return "r17"; + case UNW_PPC64_R18: + return "r18"; + case UNW_PPC64_R19: + return "r19"; + case UNW_PPC64_R20: + return "r20"; + case UNW_PPC64_R21: + return "r21"; + case UNW_PPC64_R22: + return "r22"; + case UNW_PPC64_R23: + return "r23"; + case UNW_PPC64_R24: + return "r24"; + case UNW_PPC64_R25: + return "r25"; + case UNW_PPC64_R26: + return "r26"; + case UNW_PPC64_R27: + return "r27"; + case UNW_PPC64_R28: + return "r28"; + case UNW_PPC64_R29: + return "r29"; + case UNW_PPC64_R30: + return "r30"; + case UNW_PPC64_R31: + return "r31"; + case UNW_PPC64_CR0: + return "cr0"; + case UNW_PPC64_CR1: + return "cr1"; + case UNW_PPC64_CR2: + return "cr2"; + case UNW_PPC64_CR3: + return "cr3"; + case UNW_PPC64_CR4: + return "cr4"; + case UNW_PPC64_CR5: + return "cr5"; + case UNW_PPC64_CR6: + return "cr6"; + case UNW_PPC64_CR7: + return "cr7"; + case UNW_PPC64_XER: + return "xer"; + case UNW_PPC64_LR: + return "lr"; + case UNW_PPC64_CTR: + return "ctr"; + case UNW_PPC64_VRSAVE: + return "vrsave"; + case UNW_PPC64_F0: + return "fp0"; + case UNW_PPC64_F1: + return "fp1"; + case UNW_PPC64_F2: + return "fp2"; + case UNW_PPC64_F3: + return "fp3"; + case UNW_PPC64_F4: + return "fp4"; + case UNW_PPC64_F5: + return "fp5"; + case UNW_PPC64_F6: + return "fp6"; + case UNW_PPC64_F7: + return "fp7"; + case UNW_PPC64_F8: + return "fp8"; + case UNW_PPC64_F9: + return "fp9"; + case UNW_PPC64_F10: + return "fp10"; + case UNW_PPC64_F11: + return "fp11"; + case UNW_PPC64_F12: + return "fp12"; + case UNW_PPC64_F13: + return "fp13"; + case UNW_PPC64_F14: + return "fp14"; + case UNW_PPC64_F15: + return "fp15"; + case UNW_PPC64_F16: + return "fp16"; + case UNW_PPC64_F17: + return "fp17"; + case UNW_PPC64_F18: + return "fp18"; + case UNW_PPC64_F19: + return "fp19"; + case UNW_PPC64_F20: + return "fp20"; + case UNW_PPC64_F21: + return "fp21"; + case UNW_PPC64_F22: + return "fp22"; + case UNW_PPC64_F23: + return "fp23"; + case UNW_PPC64_F24: + return "fp24"; + case UNW_PPC64_F25: + return "fp25"; + case UNW_PPC64_F26: + return "fp26"; + case UNW_PPC64_F27: + return "fp27"; + case UNW_PPC64_F28: + return "fp28"; + case UNW_PPC64_F29: + return "fp29"; + case UNW_PPC64_F30: + return "fp30"; + case UNW_PPC64_F31: + return "fp31"; + case UNW_PPC64_V0: + return "v0"; + case UNW_PPC64_V1: + return "v1"; + case UNW_PPC64_V2: + return "v2"; + case UNW_PPC64_V3: + return "v3"; + case UNW_PPC64_V4: + return "v4"; + case UNW_PPC64_V5: + return "v5"; + case UNW_PPC64_V6: + return "v6"; + case UNW_PPC64_V7: + return "v7"; + case UNW_PPC64_V8: + return "v8"; + case UNW_PPC64_V9: + return "v9"; + case UNW_PPC64_V10: + return "v10"; + case UNW_PPC64_V11: + return "v11"; + case UNW_PPC64_V12: + return "v12"; + case UNW_PPC64_V13: + return "v13"; + case UNW_PPC64_V14: + return "v14"; + case UNW_PPC64_V15: + return "v15"; + case UNW_PPC64_V16: + return "v16"; + case UNW_PPC64_V17: + return "v17"; + case UNW_PPC64_V18: + return "v18"; + case UNW_PPC64_V19: + return "v19"; + case UNW_PPC64_V20: + return "v20"; + case UNW_PPC64_V21: + return "v21"; + case UNW_PPC64_V22: + return "v22"; + case UNW_PPC64_V23: + return "v23"; + case UNW_PPC64_V24: + return "v24"; + case UNW_PPC64_V25: + return "v25"; + case UNW_PPC64_V26: + return "v26"; + case UNW_PPC64_V27: + return "v27"; + case UNW_PPC64_V28: + return "v28"; + case UNW_PPC64_V29: + return "v29"; + case UNW_PPC64_V30: + return "v30"; + case UNW_PPC64_V31: + return "v31"; + } + return "unknown register"; +} +#endif // _LIBUNWIND_TARGET_PPC64 + + +#if defined(_LIBUNWIND_TARGET_AARCH64) +/// Registers_arm64 holds the register state of a thread in a 64-bit arm +/// process. +class _LIBUNWIND_HIDDEN Registers_arm64 { +public: + Registers_arm64(); + Registers_arm64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value, uint64_t location); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + uint64_t getRegisterLocation(int regNum) const; + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64; } + static int getArch() { return REGISTERS_ARM64; } + + uint64_t getSP() const { return _registers.__sp; } + void setSP(uint64_t value, uint64_t location) { _registers.__sp = value; } + uint64_t getIP() const { return _registers.__pc; } + void setIP(uint64_t value, uint64_t location) { _registers.__pc = value; } + uint64_t getFP() const { return _registers.__fp; } + void setFP(uint64_t value, uint64_t location) { _registers.__fp = value; } + +private: + struct GPRs { + uint64_t __x[29]; // x0-x28 + uint64_t __fp; // Frame pointer x29 + uint64_t __lr; // Link register x30 + uint64_t __sp; // Stack pointer x31 + uint64_t __pc; // Program counter + uint64_t __ra_sign_state; // RA sign state register + }; + + struct GPRLocations { + uint64_t __x[29]; // x0-x28 + uint64_t __fp; // Frame pointer x29 + uint64_t __lr; // Link register x30 + uint64_t __sp; // Stack pointer x31 + uint64_t __pc; // Program counter + uint64_t padding; // 16-byte align + }; + + GPRs _registers; + GPRLocations _registerLocations; + double _vectorHalfRegisters[32]; + // Currently only the lower double in 128-bit vectore registers + // is perserved during unwinding. We could define new register + // numbers (> 96) which mean whole vector registers, then this + // struct would need to change to contain whole vector registers. +}; + +inline Registers_arm64::Registers_arm64(const void *registers) { + static_assert((check_fit::does_fit), + "arm64 registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); + memset(&_registerLocations, 0, sizeof(_registerLocations)); + static_assert( + sizeof(GPRs) == 0x110, + "expected VFP registers to be at offset 272"); + memcpy(_vectorHalfRegisters, + static_cast(registers) + sizeof(GPRs), + sizeof(_vectorHalfRegisters)); +} + +inline Registers_arm64::Registers_arm64() { + memset(&_registers, 0, sizeof(_registers)); + memset(&_registerLocations, 0, sizeof(_registerLocations)); + memset(&_vectorHalfRegisters, 0, sizeof(_vectorHalfRegisters)); +} + +inline bool Registers_arm64::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 95) + return false; + if (regNum == UNW_ARM64_RA_SIGN_STATE) + return true; + if ((regNum > 31) && (regNum < 64)) + return false; + return true; +} + +inline uint64_t Registers_arm64::getRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return _registers.__pc; + if (regNum == UNW_REG_SP) + return _registers.__sp; + if (regNum == UNW_ARM64_RA_SIGN_STATE) + return _registers.__ra_sign_state; + if ((regNum >= 0) && (regNum < 32)) + return _registers.__x[regNum]; + _LIBUNWIND_ABORT("unsupported arm64 register"); +} + +inline void Registers_arm64::setRegister(int regNum, uint64_t value, uint64_t location) { + if (regNum == UNW_REG_IP) { + _registers.__pc = value; + _registerLocations.__pc = location; + } + else if (regNum == UNW_REG_SP) { + _registers.__sp = value; + _registerLocations.__sp = location; + } + else if (regNum == UNW_ARM64_RA_SIGN_STATE) + _registers.__ra_sign_state = value; + else if ((regNum >= 0) && (regNum < 32)) { + _registers.__x[regNum] = value; + _registerLocations.__x[regNum] = location; + } + else + _LIBUNWIND_ABORT("unsupported arm64 register"); +} + +inline uint64_t Registers_arm64::getRegisterLocation(int regNum) const { + if (regNum == UNW_REG_IP) + return _registerLocations.__pc; + if (regNum == UNW_REG_SP) + return _registerLocations.__sp; + if ((regNum >= 0) && (regNum < 32)) + return _registerLocations.__x[regNum]; + _LIBUNWIND_ABORT("unsupported arm64 register"); +} + +inline const char *Registers_arm64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_REG_SP: + return "sp"; + case UNW_ARM64_X0: + return "x0"; + case UNW_ARM64_X1: + return "x1"; + case UNW_ARM64_X2: + return "x2"; + case UNW_ARM64_X3: + return "x3"; + case UNW_ARM64_X4: + return "x4"; + case UNW_ARM64_X5: + return "x5"; + case UNW_ARM64_X6: + return "x6"; + case UNW_ARM64_X7: + return "x7"; + case UNW_ARM64_X8: + return "x8"; + case UNW_ARM64_X9: + return "x9"; + case UNW_ARM64_X10: + return "x10"; + case UNW_ARM64_X11: + return "x11"; + case UNW_ARM64_X12: + return "x12"; + case UNW_ARM64_X13: + return "x13"; + case UNW_ARM64_X14: + return "x14"; + case UNW_ARM64_X15: + return "x15"; + case UNW_ARM64_X16: + return "x16"; + case UNW_ARM64_X17: + return "x17"; + case UNW_ARM64_X18: + return "x18"; + case UNW_ARM64_X19: + return "x19"; + case UNW_ARM64_X20: + return "x20"; + case UNW_ARM64_X21: + return "x21"; + case UNW_ARM64_X22: + return "x22"; + case UNW_ARM64_X23: + return "x23"; + case UNW_ARM64_X24: + return "x24"; + case UNW_ARM64_X25: + return "x25"; + case UNW_ARM64_X26: + return "x26"; + case UNW_ARM64_X27: + return "x27"; + case UNW_ARM64_X28: + return "x28"; + case UNW_ARM64_X29: + return "fp"; + case UNW_ARM64_X30: + return "lr"; + case UNW_ARM64_X31: + return "sp"; + case UNW_ARM64_D0: + return "d0"; + case UNW_ARM64_D1: + return "d1"; + case UNW_ARM64_D2: + return "d2"; + case UNW_ARM64_D3: + return "d3"; + case UNW_ARM64_D4: + return "d4"; + case UNW_ARM64_D5: + return "d5"; + case UNW_ARM64_D6: + return "d6"; + case UNW_ARM64_D7: + return "d7"; + case UNW_ARM64_D8: + return "d8"; + case UNW_ARM64_D9: + return "d9"; + case UNW_ARM64_D10: + return "d10"; + case UNW_ARM64_D11: + return "d11"; + case UNW_ARM64_D12: + return "d12"; + case UNW_ARM64_D13: + return "d13"; + case UNW_ARM64_D14: + return "d14"; + case UNW_ARM64_D15: + return "d15"; + case UNW_ARM64_D16: + return "d16"; + case UNW_ARM64_D17: + return "d17"; + case UNW_ARM64_D18: + return "d18"; + case UNW_ARM64_D19: + return "d19"; + case UNW_ARM64_D20: + return "d20"; + case UNW_ARM64_D21: + return "d21"; + case UNW_ARM64_D22: + return "d22"; + case UNW_ARM64_D23: + return "d23"; + case UNW_ARM64_D24: + return "d24"; + case UNW_ARM64_D25: + return "d25"; + case UNW_ARM64_D26: + return "d26"; + case UNW_ARM64_D27: + return "d27"; + case UNW_ARM64_D28: + return "d28"; + case UNW_ARM64_D29: + return "d29"; + case UNW_ARM64_D30: + return "d30"; + case UNW_ARM64_D31: + return "d31"; + default: + return "unknown register"; + } +} + +inline void Registers_arm64::jumpto() {} + +inline bool Registers_arm64::validFloatRegister(int regNum) const { + if (regNum < UNW_ARM64_D0) + return false; + if (regNum > UNW_ARM64_D31) + return false; + return true; +} + +inline double Registers_arm64::getFloatRegister(int regNum) const { + assert(validFloatRegister(regNum)); + return _vectorHalfRegisters[regNum - UNW_ARM64_D0]; +} + +inline void Registers_arm64::setFloatRegister(int regNum, double value) { + assert(validFloatRegister(regNum)); + _vectorHalfRegisters[regNum - UNW_ARM64_D0] = value; +} + +inline bool Registers_arm64::validVectorRegister(int) const { + return false; +} + +inline v128 Registers_arm64::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no arm64 vector register support yet"); +} + +inline void Registers_arm64::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no arm64 vector register support yet"); +} +#endif // _LIBUNWIND_TARGET_AARCH64 + +#if defined(_LIBUNWIND_TARGET_ARM) +/// Registers_arm holds the register state of a thread in a 32-bit arm +/// process. +/// +/// NOTE: Assumes VFPv3. On ARM processors without a floating point unit, +/// this uses more memory than required. +class _LIBUNWIND_HIDDEN Registers_arm { +public: + Registers_arm(); + Registers_arm(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value, uint32_t location); + uint32_t getRegisterLocation(int num) const; + bool validFloatRegister(int num) const; + unw_fpreg_t getFloatRegister(int num); + void setFloatRegister(int num, unw_fpreg_t value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto() { + restoreSavedFloatRegisters(); + restoreCoreAndJumpTo(); + } + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM; } + static int getArch() { return REGISTERS_ARM; } + + uint32_t getSP() const { return _registers.__sp; } + void setSP(uint32_t value, uint32_t location) { _registers.__sp = value; _registerLocations.__sp = location; } + uint32_t getIP() const { return _registers.__pc; } + void setIP(uint32_t value, uint32_t location) { _registers.__pc = value; _registerLocations.__pc = location; } + + void saveVFPAsX() { + assert(_use_X_for_vfp_save || !_saved_vfp_d0_d15); + _use_X_for_vfp_save = true; + } + + void restoreSavedFloatRegisters() { + if (_saved_vfp_d0_d15) { + if (_use_X_for_vfp_save) + restoreVFPWithFLDMX(_vfp_d0_d15_pad); + else + restoreVFPWithFLDMD(_vfp_d0_d15_pad); + } + if (_saved_vfp_d16_d31) + restoreVFPv3(_vfp_d16_d31); +#if defined(__ARM_WMMX) + if (_saved_iwmmx) + restoreiWMMX(_iwmmx); + if (_saved_iwmmx_control) + restoreiWMMXControl(_iwmmx_control); +#endif + } + +private: + struct GPRs { + uint32_t __r[13]; // r0-r12 + uint32_t __sp; // Stack pointer r13 + uint32_t __lr; // Link register r14 + uint32_t __pc; // Program counter r15 + }; + + struct GPRLocations { + uint32_t __r[13]; // r0-r12 + uint32_t __sp; // Stack pointer r13 + uint32_t __lr; // Link register r14 + uint32_t __pc; // Program counter r15 + }; + + static void saveVFPWithFSTMD(void *); + static void saveVFPWithFSTMX(void*); + static void saveVFPv3(void*); + static void restoreVFPWithFLDMD(void*); + static void restoreVFPWithFLDMX(void*); + static void restoreVFPv3(void*); +#if defined(__ARM_WMMX) + static void saveiWMMX(void*); + static void saveiWMMXControl(uint32_t*); + static void restoreiWMMX(void*); + static void restoreiWMMXControl(uint32_t*); +#endif + void restoreCoreAndJumpTo(); + + // ARM registers + GPRs _registers; + GPRLocations _registerLocations; + + // We save floating point registers lazily because we can't know ahead of + // time which ones are used. See EHABI #4.7. + + // Whether D0-D15 are saved in the FTSMX instead of FSTMD format. + // + // See EHABI #7.5 that explains how matching instruction sequences for load + // and store need to be used to correctly restore the exact register bits. + bool _use_X_for_vfp_save; + // Whether VFP D0-D15 are saved. + bool _saved_vfp_d0_d15; + // Whether VFPv3 D16-D31 are saved. + bool _saved_vfp_d16_d31; + // VFP registers D0-D15, + padding if saved using FSTMX + unw_fpreg_t _vfp_d0_d15_pad[17]; + // VFPv3 registers D16-D31, always saved using FSTMD + unw_fpreg_t _vfp_d16_d31[16]; +#if defined(__ARM_WMMX) + // Whether iWMMX data registers are saved. + bool _saved_iwmmx; + // Whether iWMMX control registers are saved. + mutable bool _saved_iwmmx_control; + // iWMMX registers + unw_fpreg_t _iwmmx[16]; + // iWMMX control registers + mutable uint32_t _iwmmx_control[4]; +#endif +}; + +inline Registers_arm::Registers_arm(const void *registers) + : _use_X_for_vfp_save(false), + _saved_vfp_d0_d15(false), + _saved_vfp_d16_d31(false) { + static_assert((check_fit::does_fit), + "arm registers do not fit into unw_context_t"); + // See __unw_getcontext() note about data. + memcpy(&_registers, registers, sizeof(_registers)); + memset(&_registerLocations, 0, sizeof(_registerLocations)); + memset(&_vfp_d0_d15_pad, 0, sizeof(_vfp_d0_d15_pad)); + memset(&_vfp_d16_d31, 0, sizeof(_vfp_d16_d31)); +#if defined(__ARM_WMMX) + _saved_iwmmx = false; + _saved_iwmmx_control = false; + memset(&_iwmmx, 0, sizeof(_iwmmx)); + memset(&_iwmmx_control, 0, sizeof(_iwmmx_control)); +#endif +} + +inline Registers_arm::Registers_arm() + : _use_X_for_vfp_save(false), + _saved_vfp_d0_d15(false), + _saved_vfp_d16_d31(false) { + memset(&_registers, 0, sizeof(_registers)); + memset(&_registerLocations, 0, sizeof(_registerLocations)); + memset(&_vfp_d0_d15_pad, 0, sizeof(_vfp_d0_d15_pad)); + memset(&_vfp_d16_d31, 0, sizeof(_vfp_d16_d31)); +#if defined(__ARM_WMMX) + _saved_iwmmx = false; + _saved_iwmmx_control = false; + memset(&_iwmmx, 0, sizeof(_iwmmx)); + memset(&_iwmmx_control, 0, sizeof(_iwmmx_control)); +#endif +} + +inline bool Registers_arm::validRegister(int regNum) const { + // Returns true for all non-VFP registers supported by the EHABI + // virtual register set (VRS). + if (regNum == UNW_REG_IP) + return true; + + if (regNum == UNW_REG_SP) + return true; + + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) + return true; + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WC0 && regNum <= UNW_ARM_WC3) + return true; +#endif + + return false; +} + +inline uint32_t Registers_arm::getRegister(int regNum) const { + if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) + return _registers.__sp; + + if (regNum == UNW_ARM_LR) + return _registers.__lr; + + if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) + return _registers.__pc; + + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R12) + return _registers.__r[regNum]; + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WC0 && regNum <= UNW_ARM_WC3) { + if (!_saved_iwmmx_control) { + _saved_iwmmx_control = true; + saveiWMMXControl(_iwmmx_control); + } + return _iwmmx_control[regNum - UNW_ARM_WC0]; + } +#endif + + _LIBUNWIND_ABORT("unsupported arm register"); +} + +inline void Registers_arm::setRegister(int regNum, uint32_t value, uint32_t location) { + if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) { + _registers.__sp = value; + _registerLocations.__sp = location; + return; + } + + if (regNum == UNW_ARM_LR) { + _registers.__lr = value; + _registerLocations.__lr = location; + return; + } + + if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) { + _registers.__pc = value; + _registerLocations.__pc = location; + return; + } + + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R12) { + _registers.__r[regNum] = value; + _registerLocations.__r[regNum] = location; + return; + } + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WC0 && regNum <= UNW_ARM_WC3) { + if (!_saved_iwmmx_control) { + _saved_iwmmx_control = true; + saveiWMMXControl(_iwmmx_control); + } + _iwmmx_control[regNum - UNW_ARM_WC0] = value; + return; + } +#endif + + _LIBUNWIND_ABORT("unsupported arm register"); +} + +inline uint32_t Registers_arm::getRegisterLocation(int regNum) const { + if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) + return _registerLocations.__sp; + + if (regNum == UNW_ARM_LR) + return _registerLocations.__lr; + + if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) + return _registerLocations.__pc; + + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R12) + return _registerLocations.__r[regNum]; + + _LIBUNWIND_ABORT("unsupported arm register"); +} + +inline const char *Registers_arm::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + case UNW_ARM_IP: // UNW_ARM_R15 is alias + return "pc"; + case UNW_ARM_LR: // UNW_ARM_R14 is alias + return "lr"; + case UNW_REG_SP: + case UNW_ARM_SP: // UNW_ARM_R13 is alias + return "sp"; + case UNW_ARM_R0: + return "r0"; + case UNW_ARM_R1: + return "r1"; + case UNW_ARM_R2: + return "r2"; + case UNW_ARM_R3: + return "r3"; + case UNW_ARM_R4: + return "r4"; + case UNW_ARM_R5: + return "r5"; + case UNW_ARM_R6: + return "r6"; + case UNW_ARM_R7: + return "r7"; + case UNW_ARM_R8: + return "r8"; + case UNW_ARM_R9: + return "r9"; + case UNW_ARM_R10: + return "r10"; + case UNW_ARM_R11: + return "r11"; + case UNW_ARM_R12: + return "r12"; + case UNW_ARM_S0: + return "s0"; + case UNW_ARM_S1: + return "s1"; + case UNW_ARM_S2: + return "s2"; + case UNW_ARM_S3: + return "s3"; + case UNW_ARM_S4: + return "s4"; + case UNW_ARM_S5: + return "s5"; + case UNW_ARM_S6: + return "s6"; + case UNW_ARM_S7: + return "s7"; + case UNW_ARM_S8: + return "s8"; + case UNW_ARM_S9: + return "s9"; + case UNW_ARM_S10: + return "s10"; + case UNW_ARM_S11: + return "s11"; + case UNW_ARM_S12: + return "s12"; + case UNW_ARM_S13: + return "s13"; + case UNW_ARM_S14: + return "s14"; + case UNW_ARM_S15: + return "s15"; + case UNW_ARM_S16: + return "s16"; + case UNW_ARM_S17: + return "s17"; + case UNW_ARM_S18: + return "s18"; + case UNW_ARM_S19: + return "s19"; + case UNW_ARM_S20: + return "s20"; + case UNW_ARM_S21: + return "s21"; + case UNW_ARM_S22: + return "s22"; + case UNW_ARM_S23: + return "s23"; + case UNW_ARM_S24: + return "s24"; + case UNW_ARM_S25: + return "s25"; + case UNW_ARM_S26: + return "s26"; + case UNW_ARM_S27: + return "s27"; + case UNW_ARM_S28: + return "s28"; + case UNW_ARM_S29: + return "s29"; + case UNW_ARM_S30: + return "s30"; + case UNW_ARM_S31: + return "s31"; + case UNW_ARM_D0: + return "d0"; + case UNW_ARM_D1: + return "d1"; + case UNW_ARM_D2: + return "d2"; + case UNW_ARM_D3: + return "d3"; + case UNW_ARM_D4: + return "d4"; + case UNW_ARM_D5: + return "d5"; + case UNW_ARM_D6: + return "d6"; + case UNW_ARM_D7: + return "d7"; + case UNW_ARM_D8: + return "d8"; + case UNW_ARM_D9: + return "d9"; + case UNW_ARM_D10: + return "d10"; + case UNW_ARM_D11: + return "d11"; + case UNW_ARM_D12: + return "d12"; + case UNW_ARM_D13: + return "d13"; + case UNW_ARM_D14: + return "d14"; + case UNW_ARM_D15: + return "d15"; + case UNW_ARM_D16: + return "d16"; + case UNW_ARM_D17: + return "d17"; + case UNW_ARM_D18: + return "d18"; + case UNW_ARM_D19: + return "d19"; + case UNW_ARM_D20: + return "d20"; + case UNW_ARM_D21: + return "d21"; + case UNW_ARM_D22: + return "d22"; + case UNW_ARM_D23: + return "d23"; + case UNW_ARM_D24: + return "d24"; + case UNW_ARM_D25: + return "d25"; + case UNW_ARM_D26: + return "d26"; + case UNW_ARM_D27: + return "d27"; + case UNW_ARM_D28: + return "d28"; + case UNW_ARM_D29: + return "d29"; + case UNW_ARM_D30: + return "d30"; + case UNW_ARM_D31: + return "d31"; + default: + return "unknown register"; + } +} + +inline bool Registers_arm::validFloatRegister(int regNum) const { + // NOTE: Consider the intel MMX registers floating points so the + // __unw_get_fpreg can be used to transmit the 64-bit data back. + return ((regNum >= UNW_ARM_D0) && (regNum <= UNW_ARM_D31)) +#if defined(__ARM_WMMX) + || ((regNum >= UNW_ARM_WR0) && (regNum <= UNW_ARM_WR15)) +#endif + ; +} + +inline unw_fpreg_t Registers_arm::getFloatRegister(int regNum) { + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D15) { + if (!_saved_vfp_d0_d15) { + _saved_vfp_d0_d15 = true; + if (_use_X_for_vfp_save) + saveVFPWithFSTMX(_vfp_d0_d15_pad); + else + saveVFPWithFSTMD(_vfp_d0_d15_pad); + } + return _vfp_d0_d15_pad[regNum - UNW_ARM_D0]; + } + + if (regNum >= UNW_ARM_D16 && regNum <= UNW_ARM_D31) { + if (!_saved_vfp_d16_d31) { + _saved_vfp_d16_d31 = true; + saveVFPv3(_vfp_d16_d31); + } + return _vfp_d16_d31[regNum - UNW_ARM_D16]; + } + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WR0 && regNum <= UNW_ARM_WR15) { + if (!_saved_iwmmx) { + _saved_iwmmx = true; + saveiWMMX(_iwmmx); + } + return _iwmmx[regNum - UNW_ARM_WR0]; + } +#endif + + _LIBUNWIND_ABORT("Unknown ARM float register"); +} + +inline void Registers_arm::setFloatRegister(int regNum, unw_fpreg_t value) { + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D15) { + if (!_saved_vfp_d0_d15) { + _saved_vfp_d0_d15 = true; + if (_use_X_for_vfp_save) + saveVFPWithFSTMX(_vfp_d0_d15_pad); + else + saveVFPWithFSTMD(_vfp_d0_d15_pad); + } + _vfp_d0_d15_pad[regNum - UNW_ARM_D0] = value; + return; + } + + if (regNum >= UNW_ARM_D16 && regNum <= UNW_ARM_D31) { + if (!_saved_vfp_d16_d31) { + _saved_vfp_d16_d31 = true; + saveVFPv3(_vfp_d16_d31); + } + _vfp_d16_d31[regNum - UNW_ARM_D16] = value; + return; + } + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WR0 && regNum <= UNW_ARM_WR15) { + if (!_saved_iwmmx) { + _saved_iwmmx = true; + saveiWMMX(_iwmmx); + } + _iwmmx[regNum - UNW_ARM_WR0] = value; + return; + } +#endif + + _LIBUNWIND_ABORT("Unknown ARM float register"); +} + +inline bool Registers_arm::validVectorRegister(int) const { + return false; +} + +inline v128 Registers_arm::getVectorRegister(int) const { + _LIBUNWIND_ABORT("ARM vector support not implemented"); +} + +inline void Registers_arm::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("ARM vector support not implemented"); +} +#endif // _LIBUNWIND_TARGET_ARM + + +#if defined(_LIBUNWIND_TARGET_OR1K) +/// Registers_or1k holds the register state of a thread in an OpenRISC1000 +/// process. +class _LIBUNWIND_HIDDEN Registers_or1k { +public: + Registers_or1k(); + Registers_or1k(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K; } + static int getArch() { return REGISTERS_OR1K; } + + uint64_t getSP() const { return _registers.__r[1]; } + void setSP(uint32_t value) { _registers.__r[1] = value; } + uint64_t getIP() const { return _registers.__pc; } + void setIP(uint32_t value) { _registers.__pc = value; } + +private: + struct or1k_thread_state_t { + unsigned int __r[32]; // r0-r31 + unsigned int __pc; // Program counter + unsigned int __epcr; // Program counter at exception + }; + + or1k_thread_state_t _registers; +}; + +inline Registers_or1k::Registers_or1k(const void *registers) { + static_assert((check_fit::does_fit), + "or1k registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_or1k::Registers_or1k() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_or1k::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_OR1K_R31) + return true; + if (regNum == UNW_OR1K_EPCR) + return true; + return false; +} + +inline uint32_t Registers_or1k::getRegister(int regNum) const { + if (regNum >= UNW_OR1K_R0 && regNum <= UNW_OR1K_R31) + return _registers.__r[regNum - UNW_OR1K_R0]; + + switch (regNum) { + case UNW_REG_IP: + return _registers.__pc; + case UNW_REG_SP: + return _registers.__r[1]; + case UNW_OR1K_EPCR: + return _registers.__epcr; + } + _LIBUNWIND_ABORT("unsupported or1k register"); +} + +inline void Registers_or1k::setRegister(int regNum, uint32_t value) { + if (regNum >= UNW_OR1K_R0 && regNum <= UNW_OR1K_R31) { + _registers.__r[regNum - UNW_OR1K_R0] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__pc = value; + return; + case UNW_REG_SP: + _registers.__r[1] = value; + return; + case UNW_OR1K_EPCR: + _registers.__epcr = value; + return; + } + _LIBUNWIND_ABORT("unsupported or1k register"); +} + +inline bool Registers_or1k::validFloatRegister(int /* regNum */) const { + return false; +} + +inline double Registers_or1k::getFloatRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("or1k float support not implemented"); +} + +inline void Registers_or1k::setFloatRegister(int /* regNum */, + double /* value */) { + _LIBUNWIND_ABORT("or1k float support not implemented"); +} + +inline bool Registers_or1k::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_or1k::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("or1k vector support not implemented"); +} + +inline void Registers_or1k::setVectorRegister(int /* regNum */, v128 /* value */) { + _LIBUNWIND_ABORT("or1k vector support not implemented"); +} + +inline const char *Registers_or1k::getRegisterName(int regNum) { + switch (regNum) { + case UNW_OR1K_R0: + return "r0"; + case UNW_OR1K_R1: + return "r1"; + case UNW_OR1K_R2: + return "r2"; + case UNW_OR1K_R3: + return "r3"; + case UNW_OR1K_R4: + return "r4"; + case UNW_OR1K_R5: + return "r5"; + case UNW_OR1K_R6: + return "r6"; + case UNW_OR1K_R7: + return "r7"; + case UNW_OR1K_R8: + return "r8"; + case UNW_OR1K_R9: + return "r9"; + case UNW_OR1K_R10: + return "r10"; + case UNW_OR1K_R11: + return "r11"; + case UNW_OR1K_R12: + return "r12"; + case UNW_OR1K_R13: + return "r13"; + case UNW_OR1K_R14: + return "r14"; + case UNW_OR1K_R15: + return "r15"; + case UNW_OR1K_R16: + return "r16"; + case UNW_OR1K_R17: + return "r17"; + case UNW_OR1K_R18: + return "r18"; + case UNW_OR1K_R19: + return "r19"; + case UNW_OR1K_R20: + return "r20"; + case UNW_OR1K_R21: + return "r21"; + case UNW_OR1K_R22: + return "r22"; + case UNW_OR1K_R23: + return "r23"; + case UNW_OR1K_R24: + return "r24"; + case UNW_OR1K_R25: + return "r25"; + case UNW_OR1K_R26: + return "r26"; + case UNW_OR1K_R27: + return "r27"; + case UNW_OR1K_R28: + return "r28"; + case UNW_OR1K_R29: + return "r29"; + case UNW_OR1K_R30: + return "r30"; + case UNW_OR1K_R31: + return "r31"; + case UNW_OR1K_EPCR: + return "EPCR"; + default: + return "unknown register"; + } + +} +#endif // _LIBUNWIND_TARGET_OR1K + +#if defined(_LIBUNWIND_TARGET_MIPS_O32) +/// Registers_mips_o32 holds the register state of a thread in a 32-bit MIPS +/// process. +class _LIBUNWIND_HIDDEN Registers_mips_o32 { +public: + Registers_mips_o32(); + Registers_mips_o32(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS; } + static int getArch() { return REGISTERS_MIPS_O32; } + + uint32_t getSP() const { return _registers.__r[29]; } + void setSP(uint32_t value) { _registers.__r[29] = value; } + uint32_t getIP() const { return _registers.__pc; } + void setIP(uint32_t value) { _registers.__pc = value; } + +private: + struct mips_o32_thread_state_t { + uint32_t __r[32]; + uint32_t __pc; + uint32_t __hi; + uint32_t __lo; + }; + + mips_o32_thread_state_t _registers; +#ifdef __mips_hard_float + /// O32 with 32-bit floating point registers only uses half of this + /// space. However, using the same layout for 32-bit vs 64-bit + /// floating point registers results in a single context size for + /// O32 with hard float. + uint32_t _padding; + double _floats[32]; +#endif +}; + +inline Registers_mips_o32::Registers_mips_o32(const void *registers) { + static_assert((check_fit::does_fit), + "mips_o32 registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_mips_o32::Registers_mips_o32() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_mips_o32::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_MIPS_R31) + return true; +#if __mips_isa_rev != 6 + if (regNum == UNW_MIPS_HI) + return true; + if (regNum == UNW_MIPS_LO) + return true; +#endif +#if defined(__mips_hard_float) && __mips_fpr == 32 + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) + return true; +#endif + // FIXME: DSP accumulator registers, MSA registers + return false; +} + +inline uint32_t Registers_mips_o32::getRegister(int regNum) const { + if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) + return _registers.__r[regNum - UNW_MIPS_R0]; +#if defined(__mips_hard_float) && __mips_fpr == 32 + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) { + uint32_t *p; + + if (regNum % 2 == 0) + p = (uint32_t *)&_floats[regNum - UNW_MIPS_F0]; + else + p = (uint32_t *)&_floats[(regNum - 1) - UNW_MIPS_F0] + 1; + return *p; + } +#endif + + switch (regNum) { + case UNW_REG_IP: + return _registers.__pc; + case UNW_REG_SP: + return _registers.__r[29]; + case UNW_MIPS_HI: + return _registers.__hi; + case UNW_MIPS_LO: + return _registers.__lo; + } + _LIBUNWIND_ABORT("unsupported mips_o32 register"); +} + +inline void Registers_mips_o32::setRegister(int regNum, uint32_t value) { + if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) { + _registers.__r[regNum - UNW_MIPS_R0] = value; + return; + } +#if defined(__mips_hard_float) && __mips_fpr == 32 + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) { + uint32_t *p; + + if (regNum % 2 == 0) + p = (uint32_t *)&_floats[regNum - UNW_MIPS_F0]; + else + p = (uint32_t *)&_floats[(regNum - 1) - UNW_MIPS_F0] + 1; + *p = value; + return; + } +#endif + + switch (regNum) { + case UNW_REG_IP: + _registers.__pc = value; + return; + case UNW_REG_SP: + _registers.__r[29] = value; + return; + case UNW_MIPS_HI: + _registers.__hi = value; + return; + case UNW_MIPS_LO: + _registers.__lo = value; + return; + } + _LIBUNWIND_ABORT("unsupported mips_o32 register"); +} + +inline bool Registers_mips_o32::validFloatRegister(int regNum) const { +#if defined(__mips_hard_float) && __mips_fpr == 64 + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) + return true; +#endif + return false; +} + +inline double Registers_mips_o32::getFloatRegister(int regNum) const { +#if defined(__mips_hard_float) && __mips_fpr == 64 + assert(validFloatRegister(regNum)); + return _floats[regNum - UNW_MIPS_F0]; +#else + _LIBUNWIND_ABORT("mips_o32 float support not implemented"); +#endif +} + +inline void Registers_mips_o32::setFloatRegister(int regNum, + double value) { +#if defined(__mips_hard_float) && __mips_fpr == 64 + assert(validFloatRegister(regNum)); + _floats[regNum - UNW_MIPS_F0] = value; +#else + _LIBUNWIND_ABORT("mips_o32 float support not implemented"); +#endif +} + +inline bool Registers_mips_o32::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_mips_o32::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("mips_o32 vector support not implemented"); +} + +inline void Registers_mips_o32::setVectorRegister(int /* regNum */, v128 /* value */) { + _LIBUNWIND_ABORT("mips_o32 vector support not implemented"); +} + +inline const char *Registers_mips_o32::getRegisterName(int regNum) { + switch (regNum) { + case UNW_MIPS_R0: + return "$0"; + case UNW_MIPS_R1: + return "$1"; + case UNW_MIPS_R2: + return "$2"; + case UNW_MIPS_R3: + return "$3"; + case UNW_MIPS_R4: + return "$4"; + case UNW_MIPS_R5: + return "$5"; + case UNW_MIPS_R6: + return "$6"; + case UNW_MIPS_R7: + return "$7"; + case UNW_MIPS_R8: + return "$8"; + case UNW_MIPS_R9: + return "$9"; + case UNW_MIPS_R10: + return "$10"; + case UNW_MIPS_R11: + return "$11"; + case UNW_MIPS_R12: + return "$12"; + case UNW_MIPS_R13: + return "$13"; + case UNW_MIPS_R14: + return "$14"; + case UNW_MIPS_R15: + return "$15"; + case UNW_MIPS_R16: + return "$16"; + case UNW_MIPS_R17: + return "$17"; + case UNW_MIPS_R18: + return "$18"; + case UNW_MIPS_R19: + return "$19"; + case UNW_MIPS_R20: + return "$20"; + case UNW_MIPS_R21: + return "$21"; + case UNW_MIPS_R22: + return "$22"; + case UNW_MIPS_R23: + return "$23"; + case UNW_MIPS_R24: + return "$24"; + case UNW_MIPS_R25: + return "$25"; + case UNW_MIPS_R26: + return "$26"; + case UNW_MIPS_R27: + return "$27"; + case UNW_MIPS_R28: + return "$28"; + case UNW_MIPS_R29: + return "$29"; + case UNW_MIPS_R30: + return "$30"; + case UNW_MIPS_R31: + return "$31"; + case UNW_MIPS_F0: + return "$f0"; + case UNW_MIPS_F1: + return "$f1"; + case UNW_MIPS_F2: + return "$f2"; + case UNW_MIPS_F3: + return "$f3"; + case UNW_MIPS_F4: + return "$f4"; + case UNW_MIPS_F5: + return "$f5"; + case UNW_MIPS_F6: + return "$f6"; + case UNW_MIPS_F7: + return "$f7"; + case UNW_MIPS_F8: + return "$f8"; + case UNW_MIPS_F9: + return "$f9"; + case UNW_MIPS_F10: + return "$f10"; + case UNW_MIPS_F11: + return "$f11"; + case UNW_MIPS_F12: + return "$f12"; + case UNW_MIPS_F13: + return "$f13"; + case UNW_MIPS_F14: + return "$f14"; + case UNW_MIPS_F15: + return "$f15"; + case UNW_MIPS_F16: + return "$f16"; + case UNW_MIPS_F17: + return "$f17"; + case UNW_MIPS_F18: + return "$f18"; + case UNW_MIPS_F19: + return "$f19"; + case UNW_MIPS_F20: + return "$f20"; + case UNW_MIPS_F21: + return "$f21"; + case UNW_MIPS_F22: + return "$f22"; + case UNW_MIPS_F23: + return "$f23"; + case UNW_MIPS_F24: + return "$f24"; + case UNW_MIPS_F25: + return "$f25"; + case UNW_MIPS_F26: + return "$f26"; + case UNW_MIPS_F27: + return "$f27"; + case UNW_MIPS_F28: + return "$f28"; + case UNW_MIPS_F29: + return "$f29"; + case UNW_MIPS_F30: + return "$f30"; + case UNW_MIPS_F31: + return "$f31"; + case UNW_MIPS_HI: + return "$hi"; + case UNW_MIPS_LO: + return "$lo"; + default: + return "unknown register"; + } +} +#endif // _LIBUNWIND_TARGET_MIPS_O32 + +#if defined(_LIBUNWIND_TARGET_MIPS_NEWABI) +/// Registers_mips_newabi holds the register state of a thread in a +/// MIPS process using NEWABI (the N32 or N64 ABIs). +class _LIBUNWIND_HIDDEN Registers_mips_newabi { +public: + Registers_mips_newabi(); + Registers_mips_newabi(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS; } + static int getArch() { return REGISTERS_MIPS_NEWABI; } + + uint64_t getSP() const { return _registers.__r[29]; } + void setSP(uint64_t value) { _registers.__r[29] = value; } + uint64_t getIP() const { return _registers.__pc; } + void setIP(uint64_t value) { _registers.__pc = value; } + +private: + struct mips_newabi_thread_state_t { + uint64_t __r[32]; + uint64_t __pc; + uint64_t __hi; + uint64_t __lo; + }; + + mips_newabi_thread_state_t _registers; +#ifdef __mips_hard_float + double _floats[32]; +#endif +}; + +inline Registers_mips_newabi::Registers_mips_newabi(const void *registers) { + static_assert((check_fit::does_fit), + "mips_newabi registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_mips_newabi::Registers_mips_newabi() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_mips_newabi::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_MIPS_R31) + return true; +#if __mips_isa_rev != 6 + if (regNum == UNW_MIPS_HI) + return true; + if (regNum == UNW_MIPS_LO) + return true; +#endif + // FIXME: Hard float, DSP accumulator registers, MSA registers + return false; +} + +inline uint64_t Registers_mips_newabi::getRegister(int regNum) const { + if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) + return _registers.__r[regNum - UNW_MIPS_R0]; + + switch (regNum) { + case UNW_REG_IP: + return _registers.__pc; + case UNW_REG_SP: + return _registers.__r[29]; + case UNW_MIPS_HI: + return _registers.__hi; + case UNW_MIPS_LO: + return _registers.__lo; + } + _LIBUNWIND_ABORT("unsupported mips_newabi register"); +} + +inline void Registers_mips_newabi::setRegister(int regNum, uint64_t value) { + if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) { + _registers.__r[regNum - UNW_MIPS_R0] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__pc = value; + return; + case UNW_REG_SP: + _registers.__r[29] = value; + return; + case UNW_MIPS_HI: + _registers.__hi = value; + return; + case UNW_MIPS_LO: + _registers.__lo = value; + return; + } + _LIBUNWIND_ABORT("unsupported mips_newabi register"); +} + +inline bool Registers_mips_newabi::validFloatRegister(int regNum) const { +#ifdef __mips_hard_float + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) + return true; +#endif + return false; +} + +inline double Registers_mips_newabi::getFloatRegister(int regNum) const { +#ifdef __mips_hard_float + assert(validFloatRegister(regNum)); + return _floats[regNum - UNW_MIPS_F0]; +#else + _LIBUNWIND_ABORT("mips_newabi float support not implemented"); +#endif +} + +inline void Registers_mips_newabi::setFloatRegister(int regNum, + double value) { +#ifdef __mips_hard_float + assert(validFloatRegister(regNum)); + _floats[regNum - UNW_MIPS_F0] = value; +#else + _LIBUNWIND_ABORT("mips_newabi float support not implemented"); +#endif +} + +inline bool Registers_mips_newabi::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_mips_newabi::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("mips_newabi vector support not implemented"); +} + +inline void Registers_mips_newabi::setVectorRegister(int /* regNum */, v128 /* value */) { + _LIBUNWIND_ABORT("mips_newabi vector support not implemented"); +} + +inline const char *Registers_mips_newabi::getRegisterName(int regNum) { + switch (regNum) { + case UNW_MIPS_R0: + return "$0"; + case UNW_MIPS_R1: + return "$1"; + case UNW_MIPS_R2: + return "$2"; + case UNW_MIPS_R3: + return "$3"; + case UNW_MIPS_R4: + return "$4"; + case UNW_MIPS_R5: + return "$5"; + case UNW_MIPS_R6: + return "$6"; + case UNW_MIPS_R7: + return "$7"; + case UNW_MIPS_R8: + return "$8"; + case UNW_MIPS_R9: + return "$9"; + case UNW_MIPS_R10: + return "$10"; + case UNW_MIPS_R11: + return "$11"; + case UNW_MIPS_R12: + return "$12"; + case UNW_MIPS_R13: + return "$13"; + case UNW_MIPS_R14: + return "$14"; + case UNW_MIPS_R15: + return "$15"; + case UNW_MIPS_R16: + return "$16"; + case UNW_MIPS_R17: + return "$17"; + case UNW_MIPS_R18: + return "$18"; + case UNW_MIPS_R19: + return "$19"; + case UNW_MIPS_R20: + return "$20"; + case UNW_MIPS_R21: + return "$21"; + case UNW_MIPS_R22: + return "$22"; + case UNW_MIPS_R23: + return "$23"; + case UNW_MIPS_R24: + return "$24"; + case UNW_MIPS_R25: + return "$25"; + case UNW_MIPS_R26: + return "$26"; + case UNW_MIPS_R27: + return "$27"; + case UNW_MIPS_R28: + return "$28"; + case UNW_MIPS_R29: + return "$29"; + case UNW_MIPS_R30: + return "$30"; + case UNW_MIPS_R31: + return "$31"; + case UNW_MIPS_F0: + return "$f0"; + case UNW_MIPS_F1: + return "$f1"; + case UNW_MIPS_F2: + return "$f2"; + case UNW_MIPS_F3: + return "$f3"; + case UNW_MIPS_F4: + return "$f4"; + case UNW_MIPS_F5: + return "$f5"; + case UNW_MIPS_F6: + return "$f6"; + case UNW_MIPS_F7: + return "$f7"; + case UNW_MIPS_F8: + return "$f8"; + case UNW_MIPS_F9: + return "$f9"; + case UNW_MIPS_F10: + return "$f10"; + case UNW_MIPS_F11: + return "$f11"; + case UNW_MIPS_F12: + return "$f12"; + case UNW_MIPS_F13: + return "$f13"; + case UNW_MIPS_F14: + return "$f14"; + case UNW_MIPS_F15: + return "$f15"; + case UNW_MIPS_F16: + return "$f16"; + case UNW_MIPS_F17: + return "$f17"; + case UNW_MIPS_F18: + return "$f18"; + case UNW_MIPS_F19: + return "$f19"; + case UNW_MIPS_F20: + return "$f20"; + case UNW_MIPS_F21: + return "$f21"; + case UNW_MIPS_F22: + return "$f22"; + case UNW_MIPS_F23: + return "$f23"; + case UNW_MIPS_F24: + return "$f24"; + case UNW_MIPS_F25: + return "$f25"; + case UNW_MIPS_F26: + return "$f26"; + case UNW_MIPS_F27: + return "$f27"; + case UNW_MIPS_F28: + return "$f28"; + case UNW_MIPS_F29: + return "$f29"; + case UNW_MIPS_F30: + return "$f30"; + case UNW_MIPS_F31: + return "$f31"; + case UNW_MIPS_HI: + return "$hi"; + case UNW_MIPS_LO: + return "$lo"; + default: + return "unknown register"; + } +} +#endif // _LIBUNWIND_TARGET_MIPS_NEWABI + +#if defined(_LIBUNWIND_TARGET_SPARC) +/// Registers_sparc holds the register state of a thread in a 32-bit Sparc +/// process. +class _LIBUNWIND_HIDDEN Registers_sparc { +public: + Registers_sparc(); + Registers_sparc(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC; } + static int getArch() { return REGISTERS_SPARC; } + + uint64_t getSP() const { return _registers.__regs[UNW_SPARC_O6]; } + void setSP(uint32_t value) { _registers.__regs[UNW_SPARC_O6] = value; } + uint64_t getIP() const { return _registers.__regs[UNW_SPARC_O7]; } + void setIP(uint32_t value) { _registers.__regs[UNW_SPARC_O7] = value; } + +private: + struct sparc_thread_state_t { + unsigned int __regs[32]; + }; + + sparc_thread_state_t _registers; +}; + +inline Registers_sparc::Registers_sparc(const void *registers) { + static_assert((check_fit::does_fit), + "sparc registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_sparc::Registers_sparc() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_sparc::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_SPARC_I7) + return true; + return false; +} + +inline uint32_t Registers_sparc::getRegister(int regNum) const { + if ((UNW_SPARC_G0 <= regNum) && (regNum <= UNW_SPARC_I7)) { + return _registers.__regs[regNum]; + } + + switch (regNum) { + case UNW_REG_IP: + return _registers.__regs[UNW_SPARC_O7]; + case UNW_REG_SP: + return _registers.__regs[UNW_SPARC_O6]; + } + _LIBUNWIND_ABORT("unsupported sparc register"); +} + +inline void Registers_sparc::setRegister(int regNum, uint32_t value) { + if ((UNW_SPARC_G0 <= regNum) && (regNum <= UNW_SPARC_I7)) { + _registers.__regs[regNum] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__regs[UNW_SPARC_O7] = value; + return; + case UNW_REG_SP: + _registers.__regs[UNW_SPARC_O6] = value; + return; + } + _LIBUNWIND_ABORT("unsupported sparc register"); +} + +inline bool Registers_sparc::validFloatRegister(int) const { return false; } + +inline double Registers_sparc::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no Sparc float registers"); +} + +inline void Registers_sparc::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no Sparc float registers"); +} + +inline bool Registers_sparc::validVectorRegister(int) const { return false; } + +inline v128 Registers_sparc::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no Sparc vector registers"); +} + +inline void Registers_sparc::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no Sparc vector registers"); +} + +inline const char *Registers_sparc::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_SPARC_G0: + return "g0"; + case UNW_SPARC_G1: + return "g1"; + case UNW_SPARC_G2: + return "g2"; + case UNW_SPARC_G3: + return "g3"; + case UNW_SPARC_G4: + return "g4"; + case UNW_SPARC_G5: + return "g5"; + case UNW_SPARC_G6: + return "g6"; + case UNW_SPARC_G7: + return "g7"; + case UNW_SPARC_O0: + return "o0"; + case UNW_SPARC_O1: + return "o1"; + case UNW_SPARC_O2: + return "o2"; + case UNW_SPARC_O3: + return "o3"; + case UNW_SPARC_O4: + return "o4"; + case UNW_SPARC_O5: + return "o5"; + case UNW_REG_SP: + case UNW_SPARC_O6: + return "sp"; + case UNW_SPARC_O7: + return "o7"; + case UNW_SPARC_L0: + return "l0"; + case UNW_SPARC_L1: + return "l1"; + case UNW_SPARC_L2: + return "l2"; + case UNW_SPARC_L3: + return "l3"; + case UNW_SPARC_L4: + return "l4"; + case UNW_SPARC_L5: + return "l5"; + case UNW_SPARC_L6: + return "l6"; + case UNW_SPARC_L7: + return "l7"; + case UNW_SPARC_I0: + return "i0"; + case UNW_SPARC_I1: + return "i1"; + case UNW_SPARC_I2: + return "i2"; + case UNW_SPARC_I3: + return "i3"; + case UNW_SPARC_I4: + return "i4"; + case UNW_SPARC_I5: + return "i5"; + case UNW_SPARC_I6: + return "fp"; + case UNW_SPARC_I7: + return "i7"; + default: + return "unknown register"; + } +} +#endif // _LIBUNWIND_TARGET_SPARC + +} // namespace libunwind + +#endif // __REGISTERS_HPP__ diff --git a/src/coreclr/nativeaot/libunwind/src/Unwind-EHABI.cpp b/src/coreclr/nativeaot/libunwind/src/Unwind-EHABI.cpp new file mode 100644 index 00000000000000..90acd50e395f4f --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/Unwind-EHABI.cpp @@ -0,0 +1,994 @@ +//===--------------------------- Unwind-EHABI.cpp -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Implements ARM zero-cost C++ exceptions +// +//===----------------------------------------------------------------------===// + +#include "Unwind-EHABI.h" + +#if defined(_LIBUNWIND_ARM_EHABI) + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "libunwind.h" +#include "libunwind_ext.h" +#include "unwind.h" + +namespace { + +// Strange order: take words in order, but inside word, take from most to least +// signinficant byte. +uint8_t getByte(const uint32_t* data, size_t offset) { + const uint8_t* byteData = reinterpret_cast(data); + return byteData[(offset & ~(size_t)0x03) + (3 - (offset & (size_t)0x03))]; +} + +const char* getNextWord(const char* data, uint32_t* out) { + *out = *reinterpret_cast(data); + return data + 4; +} + +const char* getNextNibble(const char* data, uint32_t* out) { + *out = *reinterpret_cast(data); + return data + 2; +} + +struct Descriptor { + // See # 9.2 + typedef enum { + SU16 = 0, // Short descriptor, 16-bit entries + LU16 = 1, // Long descriptor, 16-bit entries + LU32 = 3, // Long descriptor, 32-bit entries + RESERVED0 = 4, RESERVED1 = 5, RESERVED2 = 6, RESERVED3 = 7, + RESERVED4 = 8, RESERVED5 = 9, RESERVED6 = 10, RESERVED7 = 11, + RESERVED8 = 12, RESERVED9 = 13, RESERVED10 = 14, RESERVED11 = 15 + } Format; + + // See # 9.2 + typedef enum { + CLEANUP = 0x0, + FUNC = 0x1, + CATCH = 0x2, + INVALID = 0x4 + } Kind; +}; + +_Unwind_Reason_Code ProcessDescriptors( + _Unwind_State state, + _Unwind_Control_Block* ucbp, + struct _Unwind_Context* context, + Descriptor::Format format, + const char* descriptorStart, + uint32_t flags) { + + // EHT is inlined in the index using compact form. No descriptors. #5 + if (flags & 0x1) + return _URC_CONTINUE_UNWIND; + + // TODO: We should check the state here, and determine whether we need to + // perform phase1 or phase2 unwinding. + (void)state; + + const char* descriptor = descriptorStart; + uint32_t descriptorWord; + getNextWord(descriptor, &descriptorWord); + while (descriptorWord) { + // Read descriptor based on # 9.2. + uint32_t length; + uint32_t offset; + switch (format) { + case Descriptor::LU32: + descriptor = getNextWord(descriptor, &length); + descriptor = getNextWord(descriptor, &offset); + break; + case Descriptor::LU16: + descriptor = getNextNibble(descriptor, &length); + descriptor = getNextNibble(descriptor, &offset); + break; + default: + assert(false); + return _URC_FAILURE; + } + + // See # 9.2 table for decoding the kind of descriptor. It's a 2-bit value. + Descriptor::Kind kind = + static_cast((length & 0x1) | ((offset & 0x1) << 1)); + + // Clear off flag from last bit. + length &= ~1u; + offset &= ~1u; + uintptr_t scopeStart = ucbp->pr_cache.fnstart + offset; + uintptr_t scopeEnd = scopeStart + length; + uintptr_t pc = _Unwind_GetIP(context); + bool isInScope = (scopeStart <= pc) && (pc < scopeEnd); + + switch (kind) { + case Descriptor::CLEANUP: { + // TODO(ajwong): Handle cleanup descriptors. + break; + } + case Descriptor::FUNC: { + // TODO(ajwong): Handle function descriptors. + break; + } + case Descriptor::CATCH: { + // Catch descriptors require gobbling one more word. + uint32_t landing_pad; + descriptor = getNextWord(descriptor, &landing_pad); + + if (isInScope) { + // TODO(ajwong): This is only phase1 compatible logic. Implement + // phase2. + landing_pad = signExtendPrel31(landing_pad & ~0x80000000); + if (landing_pad == 0xffffffff) { + return _URC_HANDLER_FOUND; + } else if (landing_pad == 0xfffffffe) { + return _URC_FAILURE; + } else { + /* + bool is_reference_type = landing_pad & 0x80000000; + void* matched_object; + if (__cxxabiv1::__cxa_type_match( + ucbp, reinterpret_cast(landing_pad), + is_reference_type, + &matched_object) != __cxxabiv1::ctm_failed) + return _URC_HANDLER_FOUND; + */ + _LIBUNWIND_ABORT("Type matching not implemented"); + } + } + break; + } + default: + _LIBUNWIND_ABORT("Invalid descriptor kind found."); + } + + getNextWord(descriptor, &descriptorWord); + } + + return _URC_CONTINUE_UNWIND; +} + +static _Unwind_Reason_Code unwindOneFrame(_Unwind_State state, + _Unwind_Control_Block* ucbp, + struct _Unwind_Context* context) { + // Read the compact model EHT entry's header # 6.3 + const uint32_t* unwindingData = ucbp->pr_cache.ehtp; + assert((*unwindingData & 0xf0000000) == 0x80000000 && "Must be a compact entry"); + Descriptor::Format format = + static_cast((*unwindingData & 0x0f000000) >> 24); + + const char *lsda = + reinterpret_cast(_Unwind_GetLanguageSpecificData(context)); + + // Handle descriptors before unwinding so they are processed in the context + // of the correct stack frame. + _Unwind_Reason_Code result = + ProcessDescriptors(state, ucbp, context, format, lsda, + ucbp->pr_cache.additional); + + if (result != _URC_CONTINUE_UNWIND) + return result; + + if (__unw_step(reinterpret_cast(context)) != UNW_STEP_SUCCESS) + return _URC_FAILURE; + return _URC_CONTINUE_UNWIND; +} + +// Generates mask discriminator for _Unwind_VRS_Pop, e.g. for _UVRSC_CORE / +// _UVRSD_UINT32. +uint32_t RegisterMask(uint8_t start, uint8_t count_minus_one) { + return ((1U << (count_minus_one + 1)) - 1) << start; +} + +// Generates mask discriminator for _Unwind_VRS_Pop, e.g. for _UVRSC_VFP / +// _UVRSD_DOUBLE. +uint32_t RegisterRange(uint8_t start, uint8_t count_minus_one) { + return ((uint32_t)start << 16) | ((uint32_t)count_minus_one + 1); +} + +} // end anonymous namespace + +/** + * Decodes an EHT entry. + * + * @param data Pointer to EHT. + * @param[out] off Offset from return value (in bytes) to begin interpretation. + * @param[out] len Number of bytes in unwind code. + * @return Pointer to beginning of unwind code. + */ +extern "C" const uint32_t* +decode_eht_entry(const uint32_t* data, size_t* off, size_t* len) { + if ((*data & 0x80000000) == 0) { + // 6.2: Generic Model + // + // EHT entry is a prel31 pointing to the PR, followed by data understood + // only by the personality routine. Fortunately, all existing assembler + // implementations, including GNU assembler, LLVM integrated assembler, + // and ARM assembler, assume that the unwind opcodes come after the + // personality routine address. + *off = 1; // First byte is size data. + *len = (((data[1] >> 24) & 0xff) + 1) * 4; + data++; // Skip the first word, which is the prel31 offset. + } else { + // 6.3: ARM Compact Model + // + // EHT entries here correspond to the __aeabi_unwind_cpp_pr[012] PRs indeded + // by format: + Descriptor::Format format = + static_cast((*data & 0x0f000000) >> 24); + switch (format) { + case Descriptor::SU16: + *len = 4; + *off = 1; + break; + case Descriptor::LU16: + case Descriptor::LU32: + *len = 4 + 4 * ((*data & 0x00ff0000) >> 16); + *off = 2; + break; + default: + return nullptr; + } + } + return data; +} + +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data, + size_t offset, size_t len) { + bool wrotePC = false; + bool finish = false; + while (offset < len && !finish) { + uint8_t byte = getByte(data, offset++); + if ((byte & 0x80) == 0) { + uint32_t sp; + _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp); + if (byte & 0x40) + sp -= (((uint32_t)byte & 0x3f) << 2) + 4; + else + sp += ((uint32_t)byte << 2) + 4; + _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp, NULL); + } else { + switch (byte & 0xf0) { + case 0x80: { + if (offset >= len) + return _URC_FAILURE; + uint32_t registers = + (((uint32_t)byte & 0x0f) << 12) | + (((uint32_t)getByte(data, offset++)) << 4); + if (!registers) + return _URC_FAILURE; + if (registers & (1 << 15)) + wrotePC = true; + _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32); + break; + } + case 0x90: { + uint8_t reg = byte & 0x0f; + if (reg == 13 || reg == 15) + return _URC_FAILURE; + uint32_t sp; + _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_R0 + reg, + _UVRSD_UINT32, &sp); + _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, + &sp, NULL); + break; + } + case 0xa0: { + uint32_t registers = RegisterMask(4, byte & 0x07); + if (byte & 0x08) + registers |= 1 << 14; + _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32); + break; + } + case 0xb0: { + switch (byte) { + case 0xb0: + finish = true; + break; + case 0xb1: { + if (offset >= len) + return _URC_FAILURE; + uint8_t registers = getByte(data, offset++); + if (registers & 0xf0 || !registers) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32); + break; + } + case 0xb2: { + uint32_t addend = 0; + uint32_t shift = 0; + // This decodes a uleb128 value. + while (true) { + if (offset >= len) + return _URC_FAILURE; + uint32_t v = getByte(data, offset++); + addend |= (v & 0x7f) << shift; + if ((v & 0x80) == 0) + break; + shift += 7; + } + uint32_t sp; + _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, + &sp); + sp += 0x204 + (addend << 2); + _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, + &sp, NULL); + break; + } + case 0xb3: { + uint8_t v = getByte(data, offset++); + _Unwind_VRS_Pop(context, _UVRSC_VFP, + RegisterRange(static_cast(v >> 4), + v & 0x0f), _UVRSD_VFPX); + break; + } + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + return _URC_FAILURE; + default: + _Unwind_VRS_Pop(context, _UVRSC_VFP, + RegisterRange(8, byte & 0x07), _UVRSD_VFPX); + break; + } + break; + } + case 0xc0: { + switch (byte) { +#if defined(__ARM_WMMX) + case 0xc0: + case 0xc1: + case 0xc2: + case 0xc3: + case 0xc4: + case 0xc5: + _Unwind_VRS_Pop(context, _UVRSC_WMMXD, + RegisterRange(10, byte & 0x7), _UVRSD_DOUBLE); + break; + case 0xc6: { + uint8_t v = getByte(data, offset++); + uint8_t start = static_cast(v >> 4); + uint8_t count_minus_one = v & 0xf; + if (start + count_minus_one >= 16) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_WMMXD, + RegisterRange(start, count_minus_one), + _UVRSD_DOUBLE); + break; + } + case 0xc7: { + uint8_t v = getByte(data, offset++); + if (!v || v & 0xf0) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_WMMXC, v, _UVRSD_DOUBLE); + break; + } +#endif + case 0xc8: + case 0xc9: { + uint8_t v = getByte(data, offset++); + uint8_t start = + static_cast(((byte == 0xc8) ? 16 : 0) + (v >> 4)); + uint8_t count_minus_one = v & 0xf; + if (start + count_minus_one >= 32) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_VFP, + RegisterRange(start, count_minus_one), + _UVRSD_DOUBLE); + break; + } + default: + return _URC_FAILURE; + } + break; + } + case 0xd0: { + if (byte & 0x08) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_VFP, RegisterRange(8, byte & 0x7), + _UVRSD_DOUBLE); + break; + } + default: + return _URC_FAILURE; + } + } + } + if (!wrotePC) { + uint32_t lr; + _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_LR, _UVRSD_UINT32, &lr); + _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_IP, _UVRSD_UINT32, &lr, NULL); + } + return _URC_CONTINUE_UNWIND; +} + +extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code +__aeabi_unwind_cpp_pr0(_Unwind_State state, _Unwind_Control_Block *ucbp, + _Unwind_Context *context) { + return unwindOneFrame(state, ucbp, context); +} + +extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code +__aeabi_unwind_cpp_pr1(_Unwind_State state, _Unwind_Control_Block *ucbp, + _Unwind_Context *context) { + return unwindOneFrame(state, ucbp, context); +} + +extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code +__aeabi_unwind_cpp_pr2(_Unwind_State state, _Unwind_Control_Block *ucbp, + _Unwind_Context *context) { + return unwindOneFrame(state, ucbp, context); +} + +static _Unwind_Reason_Code +unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) { + // EHABI #7.3 discusses preserving the VRS in a "temporary VRS" during + // phase 1 and then restoring it to the "primary VRS" for phase 2. The + // effect is phase 2 doesn't see any of the VRS manipulations from phase 1. + // In this implementation, the phases don't share the VRS backing store. + // Instead, they are passed the original |uc| and they create a new VRS + // from scratch thus achieving the same effect. + __unw_init_local(cursor, uc); + + // Walk each frame looking for a place to stop. + for (bool handlerNotFound = true; handlerNotFound;) { + + // See if frame has code to run (has personality routine). + unw_proc_info_t frameInfo; + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): __unw_get_proc_info " + "failed => _URC_FATAL_PHASE1_ERROR", + static_cast(exception_object)); + return _URC_FATAL_PHASE1_ERROR; + } + + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + unw_word_t pc; + __unw_get_reg(cursor, UNW_REG_IP, &pc); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): pc=0x%" PRIxPTR ", start_ip=0x%" PRIxPTR ", func=%s, " + "lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR, + static_cast(exception_object), pc, + frameInfo.start_ip, functionName, + frameInfo.lsda, frameInfo.handler); + } + + // If there is a personality routine, ask it if it will want to stop at + // this frame. + if (frameInfo.handler != 0) { + __personality_routine p = + (__personality_routine)(long)(frameInfo.handler); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): calling personality function %p", + static_cast(exception_object), + reinterpret_cast(reinterpret_cast(p))); + struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor); + exception_object->pr_cache.fnstart = frameInfo.start_ip; + exception_object->pr_cache.ehtp = + (_Unwind_EHT_Header *)frameInfo.unwind_info; + exception_object->pr_cache.additional = frameInfo.flags; + _Unwind_Reason_Code personalityResult = + (*p)(_US_VIRTUAL_UNWIND_FRAME, exception_object, context); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): personality result %d start_ip %x ehtp %p " + "additional %x", + static_cast(exception_object), personalityResult, + exception_object->pr_cache.fnstart, + static_cast(exception_object->pr_cache.ehtp), + exception_object->pr_cache.additional); + switch (personalityResult) { + case _URC_HANDLER_FOUND: + // found a catch clause or locals that need destructing in this frame + // stop search and remember stack pointer at the frame + handlerNotFound = false; + // p should have initialized barrier_cache. EHABI #7.3.5 + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND", + static_cast(exception_object)); + return _URC_NO_REASON; + + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND", + static_cast(exception_object)); + // continue unwinding + break; + + // EHABI #7.3.3 + case _URC_FAILURE: + return _URC_FAILURE; + + default: + // something went wrong + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR", + static_cast(exception_object)); + return _URC_FATAL_PHASE1_ERROR; + } + } + } + return _URC_NO_REASON; +} + +static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, + _Unwind_Exception *exception_object, + bool resume) { + // See comment at the start of unwind_phase1 regarding VRS integrity. + __unw_init_local(cursor, uc); + + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)", + static_cast(exception_object)); + int frame_count = 0; + + // Walk each frame until we reach where search phase said to stop. + while (true) { + // Ask libunwind to get next frame (skip over first which is + // _Unwind_RaiseException or _Unwind_Resume). + // + // Resume only ever makes sense for 1 frame. + _Unwind_State state = + resume ? _US_UNWIND_FRAME_RESUME : _US_UNWIND_FRAME_STARTING; + if (resume && frame_count == 1) { + // On a resume, first unwind the _Unwind_Resume() frame. The next frame + // is now the landing pad for the cleanup from a previous execution of + // phase2. To continue unwindingly correctly, replace VRS[15] with the + // IP of the frame that the previous run of phase2 installed the context + // for. After this, continue unwinding as if normal. + // + // See #7.4.6 for details. + __unw_set_reg(cursor, UNW_REG_IP, + exception_object->unwinder_cache.reserved2, NULL); + resume = false; + } + + // Get info about this frame. + unw_word_t sp; + unw_proc_info_t frameInfo; + __unw_get_reg(cursor, UNW_REG_SP, &sp); + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): __unw_get_proc_info " + "failed => _URC_FATAL_PHASE2_ERROR", + static_cast(exception_object)); + return _URC_FATAL_PHASE2_ERROR; + } + + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): start_ip=0x%" PRIxPTR ", func=%s, sp=0x%" PRIxPTR ", " + "lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR "", + static_cast(exception_object), frameInfo.start_ip, + functionName, sp, frameInfo.lsda, + frameInfo.handler); + } + + // If there is a personality routine, tell it we are unwinding. + if (frameInfo.handler != 0) { + __personality_routine p = + (__personality_routine)(long)(frameInfo.handler); + struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor); + // EHABI #7.2 + exception_object->pr_cache.fnstart = frameInfo.start_ip; + exception_object->pr_cache.ehtp = + (_Unwind_EHT_Header *)frameInfo.unwind_info; + exception_object->pr_cache.additional = frameInfo.flags; + _Unwind_Reason_Code personalityResult = + (*p)(state, exception_object, context); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + // Continue unwinding + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND", + static_cast(exception_object)); + // EHABI #7.2 + if (sp == exception_object->barrier_cache.sp) { + // Phase 1 said we would stop at this frame, but we did not... + _LIBUNWIND_ABORT("during phase1 personality function said it would " + "stop here, but now in phase2 it did not stop here"); + } + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT", + static_cast(exception_object)); + // Personality routine says to transfer control to landing pad. + // We may get control back if landing pad calls _Unwind_Resume(). + if (_LIBUNWIND_TRACING_UNWINDING) { + unw_word_t pc; + __unw_get_reg(cursor, UNW_REG_IP, &pc); + __unw_get_reg(cursor, UNW_REG_SP, &sp); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering " + "user code with ip=0x%" PRIxPTR ", sp=0x%" PRIxPTR, + static_cast(exception_object), + pc, sp); + } + + { + // EHABI #7.4.1 says we need to preserve pc for when _Unwind_Resume + // is called back, to find this same frame. + unw_word_t pc; + __unw_get_reg(cursor, UNW_REG_IP, &pc); + exception_object->unwinder_cache.reserved2 = (uint32_t)pc; + } + __unw_resume(cursor); + // __unw_resume() only returns if there was an error. + return _URC_FATAL_PHASE2_ERROR; + + // # EHABI #7.4.3 + case _URC_FAILURE: + abort(); + + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", + personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + frame_count++; + } + + // Clean up phase did not resume at the frame that the search phase + // said it would... + return _URC_FATAL_PHASE2_ERROR; +} + +/// Called by __cxa_throw. Only returns if there is a fatal error. +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_RaiseException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)", + static_cast(exception_object)); + unw_context_t uc; + unw_cursor_t cursor; + __unw_getcontext(&uc); + + // This field for is for compatibility with GCC to say this isn't a forced + // unwind. EHABI #7.2 + exception_object->unwinder_cache.reserved1 = 0; + + // phase 1: the search phase + _Unwind_Reason_Code phase1 = unwind_phase1(&uc, &cursor, exception_object); + if (phase1 != _URC_NO_REASON) + return phase1; + + // phase 2: the clean up phase + return unwind_phase2(&uc, &cursor, exception_object, false); +} + +_LIBUNWIND_EXPORT void _Unwind_Complete(_Unwind_Exception* exception_object) { + // This is to be called when exception handling completes to give us a chance + // to perform any housekeeping. EHABI #7.2. But we have nothing to do here. + (void)exception_object; +} + +/// When _Unwind_RaiseException() is in phase2, it hands control +/// to the personality function at each frame. The personality +/// may force a jump to a landing pad in that function, the landing +/// pad code may then call _Unwind_Resume() to continue with the +/// unwinding. Note: the call to _Unwind_Resume() is from compiler +/// generated user code. All other _Unwind_* routines are called +/// by the C++ runtime __cxa_* routines. +/// +/// Note: re-throwing an exception (as opposed to continuing the unwind) +/// is implemented by having the code call __cxa_rethrow() which +/// in turn calls _Unwind_Resume_or_Rethrow(). +_LIBUNWIND_EXPORT void +_Unwind_Resume(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", + static_cast(exception_object)); + unw_context_t uc; + unw_cursor_t cursor; + __unw_getcontext(&uc); + + // _Unwind_RaiseException on EHABI will always set the reserved1 field to 0, + // which is in the same position as private_1 below. + // TODO(ajwong): Who wronte the above? Why is it true? + unwind_phase2(&uc, &cursor, exception_object, true); + + // Clients assume _Unwind_Resume() does not return, so all we can do is abort. + _LIBUNWIND_ABORT("_Unwind_Resume() can't return"); +} + +/// Called by personality handler during phase 2 to get LSDA for current frame. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) + result = (uintptr_t)frameInfo.lsda; + _LIBUNWIND_TRACE_API( + "_Unwind_GetLanguageSpecificData(context=%p) => 0x%llx", + static_cast(context), (long long)result); + return result; +} + +static uint64_t ValueAsBitPattern(_Unwind_VRS_DataRepresentation representation, + void* valuep) { + uint64_t value = 0; + switch (representation) { + case _UVRSD_UINT32: + case _UVRSD_FLOAT: + memcpy(&value, valuep, sizeof(uint32_t)); + break; + + case _UVRSD_VFPX: + case _UVRSD_UINT64: + case _UVRSD_DOUBLE: + memcpy(&value, valuep, sizeof(uint64_t)); + break; + } + return value; +} + +_LIBUNWIND_EXPORT _Unwind_VRS_Result +_Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t regno, _Unwind_VRS_DataRepresentation representation, + void *valuep, unw_word_t *pos) { + _LIBUNWIND_TRACE_API("_Unwind_VRS_Set(context=%p, regclass=%d, reg=%d, " + "rep=%d, value=0x%llX)", + static_cast(context), regclass, regno, + representation, + ValueAsBitPattern(representation, valuep)); + unw_cursor_t *cursor = (unw_cursor_t *)context; + switch (regclass) { + case _UVRSC_CORE: + if (representation != _UVRSD_UINT32 || regno > 15) + return _UVRSR_FAILED; + return __unw_set_reg(cursor, (unw_regnum_t)(UNW_ARM_R0 + regno), + *(unw_word_t *)valuep,(unw_word_t *)pos) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_VFP: + if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) + return _UVRSR_FAILED; + if (representation == _UVRSD_VFPX) { + // Can only touch d0-15 with FSTMFDX. + if (regno > 15) + return _UVRSR_FAILED; + __unw_save_vfp_as_X(cursor); + } else { + if (regno > 31) + return _UVRSR_FAILED; + } + return __unw_set_fpreg(cursor, (unw_regnum_t)(UNW_ARM_D0 + regno), + *(unw_fpreg_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; +#if defined(__ARM_WMMX) + case _UVRSC_WMMXC: + if (representation != _UVRSD_UINT32 || regno > 3) + return _UVRSR_FAILED; + return __unw_set_reg(cursor, (unw_regnum_t)(UNW_ARM_WC0 + regno), + *(unw_word_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_WMMXD: + if (representation != _UVRSD_DOUBLE || regno > 31) + return _UVRSR_FAILED; + return __unw_set_fpreg(cursor, (unw_regnum_t)(UNW_ARM_WR0 + regno), + *(unw_fpreg_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; +#else + case _UVRSC_WMMXC: + case _UVRSC_WMMXD: + break; +#endif + } + _LIBUNWIND_ABORT("unsupported register class"); +} + +static _Unwind_VRS_Result +_Unwind_VRS_Get_Internal(_Unwind_Context *context, + _Unwind_VRS_RegClass regclass, uint32_t regno, + _Unwind_VRS_DataRepresentation representation, + void *valuep) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + switch (regclass) { + case _UVRSC_CORE: + if (representation != _UVRSD_UINT32 || regno > 15) + return _UVRSR_FAILED; + return __unw_get_reg(cursor, (unw_regnum_t)(UNW_ARM_R0 + regno), + (unw_word_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_VFP: + if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) + return _UVRSR_FAILED; + if (representation == _UVRSD_VFPX) { + // Can only touch d0-15 with FSTMFDX. + if (regno > 15) + return _UVRSR_FAILED; + __unw_save_vfp_as_X(cursor); + } else { + if (regno > 31) + return _UVRSR_FAILED; + } + return __unw_get_fpreg(cursor, (unw_regnum_t)(UNW_ARM_D0 + regno), + (unw_fpreg_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; +#if defined(__ARM_WMMX) + case _UVRSC_WMMXC: + if (representation != _UVRSD_UINT32 || regno > 3) + return _UVRSR_FAILED; + return __unw_get_reg(cursor, (unw_regnum_t)(UNW_ARM_WC0 + regno), + (unw_word_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_WMMXD: + if (representation != _UVRSD_DOUBLE || regno > 31) + return _UVRSR_FAILED; + return __unw_get_fpreg(cursor, (unw_regnum_t)(UNW_ARM_WR0 + regno), + (unw_fpreg_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; +#else + case _UVRSC_WMMXC: + case _UVRSC_WMMXD: + break; +#endif + } + _LIBUNWIND_ABORT("unsupported register class"); +} + +_LIBUNWIND_EXPORT _Unwind_VRS_Result +_Unwind_VRS_Get(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t regno, _Unwind_VRS_DataRepresentation representation, + void *valuep) { + _Unwind_VRS_Result result = + _Unwind_VRS_Get_Internal(context, regclass, regno, representation, + valuep); + _LIBUNWIND_TRACE_API("_Unwind_VRS_Get(context=%p, regclass=%d, reg=%d, " + "rep=%d, value=0x%llX, result = %d)", + static_cast(context), regclass, regno, + representation, + ValueAsBitPattern(representation, valuep), result); + return result; +} + +_Unwind_VRS_Result +_Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t discriminator, + _Unwind_VRS_DataRepresentation representation) { + _LIBUNWIND_TRACE_API("_Unwind_VRS_Pop(context=%p, regclass=%d, " + "discriminator=%d, representation=%d)", + static_cast(context), regclass, discriminator, + representation); + switch (regclass) { + case _UVRSC_WMMXC: +#if !defined(__ARM_WMMX) + break; +#endif + case _UVRSC_CORE: { + if (representation != _UVRSD_UINT32) + return _UVRSR_FAILED; + // When popping SP from the stack, we don't want to override it from the + // computed new stack location. See EHABI #7.5.4 table 3. + bool poppedSP = false; + uint32_t* sp; + uint32_t* pos; + if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, + _UVRSD_UINT32, &sp) != _UVRSR_OK) { + return _UVRSR_FAILED; + } + for (uint32_t i = 0; i < 16; ++i) { + if (!(discriminator & static_cast(1 << i))) + continue; + pos = sp; + uint32_t value = *sp++; + if (regclass == _UVRSC_CORE && i == 13) + poppedSP = true; + if (_Unwind_VRS_Set(context, regclass, i, + _UVRSD_UINT32, &value, pos) != _UVRSR_OK) { + return _UVRSR_FAILED; + } + } + if (!poppedSP) { + return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, + _UVRSD_UINT32, &sp, NULL); + } + return _UVRSR_OK; + } + case _UVRSC_WMMXD: +#if !defined(__ARM_WMMX) + break; +#endif + case _UVRSC_VFP: { + if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) + return _UVRSR_FAILED; + uint32_t first = discriminator >> 16; + uint32_t count = discriminator & 0xffff; + uint32_t end = first+count; + uint32_t* sp; + if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, + _UVRSD_UINT32, &sp) != _UVRSR_OK) { + return _UVRSR_FAILED; + } + // For _UVRSD_VFPX, we're assuming the data is stored in FSTMX "standard + // format 1", which is equivalent to FSTMD + a padding word. + for (uint32_t i = first; i < end; ++i) { + // SP is only 32-bit aligned so don't copy 64-bit at a time. + uint64_t value = *sp++; + value |= ((uint64_t)(*sp++)) << 32; + if (_Unwind_VRS_Set(context, regclass, i, representation, &value, NULL) != + _UVRSR_OK) + return _UVRSR_FAILED; + } + if (representation == _UVRSD_VFPX) + ++sp; + return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, + &sp, NULL); + } + } + _LIBUNWIND_ABORT("unsupported register class"); +} + +/// Called by personality handler during phase 2 to find the start of the +/// function. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetRegionStart(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) + result = (uintptr_t)frameInfo.start_ip; + _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%llX", + static_cast(context), (long long)result); + return result; +} + + +/// Called by personality handler during phase 2 if a foreign exception +// is caught. +_LIBUNWIND_EXPORT void +_Unwind_DeleteException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)", + static_cast(exception_object)); + if (exception_object->exception_cleanup != NULL) + (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, + exception_object); +} + +extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code +__gnu_unwind_frame(_Unwind_Exception *exception_object, + struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + if (__unw_step(cursor) != UNW_STEP_SUCCESS) + return _URC_FAILURE; + return _URC_OK; +} + +#endif // defined(_LIBUNWIND_ARM_EHABI) diff --git a/src/coreclr/nativeaot/libunwind/src/Unwind-EHABI.h b/src/coreclr/nativeaot/libunwind/src/Unwind-EHABI.h new file mode 100644 index 00000000000000..6897082a337f35 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/Unwind-EHABI.h @@ -0,0 +1,50 @@ +//===------------------------- Unwind-EHABI.hpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +//===----------------------------------------------------------------------===// + +#ifndef __UNWIND_EHABI_H__ +#define __UNWIND_EHABI_H__ + +#include <__libunwind_config.h> + +#if defined(_LIBUNWIND_ARM_EHABI) + +#include +#include + +// Unable to unwind in the ARM index table (section 5 EHABI). +#define UNW_EXIDX_CANTUNWIND 0x1 + +static inline uint32_t signExtendPrel31(uint32_t data) { + return data | ((data & 0x40000000u) << 1); +} + +static inline uint32_t readPrel31(const uint32_t *data) { + return (((uint32_t)(uintptr_t)data) + signExtendPrel31(*data)); +} + +#if defined(__cplusplus) +extern "C" { +#endif + +extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr0( + _Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context); + +extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr1( + _Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context); + +extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr2( + _Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // defined(_LIBUNWIND_ARM_EHABI) + +#endif // __UNWIND_EHABI_H__ diff --git a/src/coreclr/nativeaot/libunwind/src/Unwind-seh.cpp b/src/coreclr/nativeaot/libunwind/src/Unwind-seh.cpp new file mode 100644 index 00000000000000..7647f2e0db0bfe --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/Unwind-seh.cpp @@ -0,0 +1,501 @@ +//===--------------------------- Unwind-seh.cpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements SEH-based Itanium C++ exceptions. +// +//===----------------------------------------------------------------------===// + +#include "config.h" + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "libunwind_ext.h" +#include "UnwindCursor.hpp" + +using namespace libunwind; + +#define STATUS_USER_DEFINED (1u << 29) + +#define STATUS_GCC_MAGIC (('G' << 16) | ('C' << 8) | 'C') + +#define MAKE_CUSTOM_STATUS(s, c) \ + ((NTSTATUS)(((s) << 30) | STATUS_USER_DEFINED | (c))) +#define MAKE_GCC_EXCEPTION(c) \ + MAKE_CUSTOM_STATUS(STATUS_SEVERITY_SUCCESS, STATUS_GCC_MAGIC | ((c) << 24)) + +/// SEH exception raised by libunwind when the program calls +/// \c _Unwind_RaiseException. +#define STATUS_GCC_THROW MAKE_GCC_EXCEPTION(0) // 0x20474343 +/// SEH exception raised by libunwind to initiate phase 2 of exception +/// handling. +#define STATUS_GCC_UNWIND MAKE_GCC_EXCEPTION(1) // 0x21474343 + +/// Class of foreign exceptions based on unrecognized SEH exceptions. +static const uint64_t kSEHExceptionClass = 0x434C4E4753454800; // CLNGSEH\0 + +/// Exception cleanup routine used by \c _GCC_specific_handler to +/// free foreign exceptions. +static void seh_exc_cleanup(_Unwind_Reason_Code urc, _Unwind_Exception *exc) { + (void)urc; + if (exc->exception_class != kSEHExceptionClass) + _LIBUNWIND_ABORT("SEH cleanup called on non-SEH exception"); + free(exc); +} + +static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *ctx); +static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor); +static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor, + DISPATCHER_CONTEXT *disp); + +/// Common implementation of SEH-style handler functions used by Itanium- +/// style frames. Depending on how and why it was called, it may do one of: +/// a) Delegate to the given Itanium-style personality function; or +/// b) Initiate a collided unwind to halt unwinding. +_LIBUNWIND_EXPORT EXCEPTION_DISPOSITION +_GCC_specific_handler(PEXCEPTION_RECORD ms_exc, PVOID frame, PCONTEXT ms_ctx, + DISPATCHER_CONTEXT *disp, __personality_routine pers) { + unw_cursor_t cursor; + _Unwind_Exception *exc; + _Unwind_Action action; + struct _Unwind_Context *ctx = nullptr; + _Unwind_Reason_Code urc; + uintptr_t retval, target; + bool ours = false; + + _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler(%#010lx(%lx), %p)", + ms_exc->ExceptionCode, ms_exc->ExceptionFlags, + (void *)frame); + if (ms_exc->ExceptionCode == STATUS_GCC_UNWIND) { + if (IS_TARGET_UNWIND(ms_exc->ExceptionFlags)) { + // Set up the upper return value (the lower one and the target PC + // were set in the call to RtlUnwindEx()) for the landing pad. +#ifdef __x86_64__ + disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3]; +#elif defined(__arm__) + disp->ContextRecord->R1 = ms_exc->ExceptionInformation[3]; +#elif defined(__aarch64__) + disp->ContextRecord->X1 = ms_exc->ExceptionInformation[3]; +#endif + } + // This is the collided unwind to the landing pad. Nothing to do. + return ExceptionContinueSearch; + } + + if (ms_exc->ExceptionCode == STATUS_GCC_THROW) { + // This is (probably) a libunwind-controlled exception/unwind. Recover the + // parameters which we set below, and pass them to the personality function. + ours = true; + exc = (_Unwind_Exception *)ms_exc->ExceptionInformation[0]; + if (!IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1) { + ctx = (struct _Unwind_Context *)ms_exc->ExceptionInformation[1]; + action = (_Unwind_Action)ms_exc->ExceptionInformation[2]; + } + } else { + // Foreign exception. + exc = (_Unwind_Exception *)malloc(sizeof(_Unwind_Exception)); + exc->exception_class = kSEHExceptionClass; + exc->exception_cleanup = seh_exc_cleanup; + memset(exc->private_, 0, sizeof(exc->private_)); + } + if (!ctx) { + __unw_init_seh(&cursor, disp->ContextRecord); + __unw_seh_set_disp_ctx(&cursor, disp); + __unw_set_reg(&cursor, UNW_REG_IP, disp->ControlPc - 1); + ctx = (struct _Unwind_Context *)&cursor; + + if (!IS_UNWINDING(ms_exc->ExceptionFlags)) { + if (ours && ms_exc->NumberParameters > 1) + action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_FORCE_UNWIND); + else + action = _UA_SEARCH_PHASE; + } else { + if (ours && ms_exc->ExceptionInformation[1] == (ULONG_PTR)frame) + action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME); + else + action = _UA_CLEANUP_PHASE; + } + } + + _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() calling personality " + "function %p(1, %d, %llx, %p, %p)", + (void *)pers, action, exc->exception_class, + (void *)exc, (void *)ctx); + urc = pers(1, action, exc->exception_class, exc, ctx); + _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() personality returned %d", urc); + switch (urc) { + case _URC_CONTINUE_UNWIND: + // If we're in phase 2, and the personality routine said to continue + // at the target frame, we're in real trouble. + if (action & _UA_HANDLER_FRAME) + _LIBUNWIND_ABORT("Personality continued unwind at the target frame!"); + return ExceptionContinueSearch; + case _URC_HANDLER_FOUND: + // If we were called by __libunwind_seh_personality(), indicate that + // a handler was found; otherwise, initiate phase 2 by unwinding. + if (ours && ms_exc->NumberParameters > 1) + return 4 /* ExecptionExecuteHandler in mingw */; + // This should never happen in phase 2. + if (IS_UNWINDING(ms_exc->ExceptionFlags)) + _LIBUNWIND_ABORT("Personality indicated exception handler in phase 2!"); + exc->private_[1] = (ULONG_PTR)frame; + if (ours) { + ms_exc->NumberParameters = 4; + ms_exc->ExceptionInformation[1] = (ULONG_PTR)frame; + } + // FIXME: Indicate target frame in foreign case! + // phase 2: the clean up phase + RtlUnwindEx(frame, (PVOID)disp->ControlPc, ms_exc, exc, ms_ctx, disp->HistoryTable); + _LIBUNWIND_ABORT("RtlUnwindEx() failed"); + case _URC_INSTALL_CONTEXT: { + // If we were called by __libunwind_seh_personality(), indicate that + // a handler was found; otherwise, it's time to initiate a collided + // unwind to the target. + if (ours && !IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1) + return 4 /* ExecptionExecuteHandler in mingw */; + // This should never happen in phase 1. + if (!IS_UNWINDING(ms_exc->ExceptionFlags)) + _LIBUNWIND_ABORT("Personality installed context during phase 1!"); +#ifdef __x86_64__ + exc->private_[2] = disp->TargetIp; + __unw_get_reg(&cursor, UNW_X86_64_RAX, &retval); + __unw_get_reg(&cursor, UNW_X86_64_RDX, &exc->private_[3]); +#elif defined(__arm__) + exc->private_[2] = disp->TargetPc; + __unw_get_reg(&cursor, UNW_ARM_R0, &retval); + __unw_get_reg(&cursor, UNW_ARM_R1, &exc->private_[3]); +#elif defined(__aarch64__) + exc->private_[2] = disp->TargetPc; + __unw_get_reg(&cursor, UNW_ARM64_X0, &retval); + __unw_get_reg(&cursor, UNW_ARM64_X1, &exc->private_[3]); +#endif + __unw_get_reg(&cursor, UNW_REG_IP, &target); + ms_exc->ExceptionCode = STATUS_GCC_UNWIND; +#ifdef __x86_64__ + ms_exc->ExceptionInformation[2] = disp->TargetIp; +#elif defined(__arm__) || defined(__aarch64__) + ms_exc->ExceptionInformation[2] = disp->TargetPc; +#endif + ms_exc->ExceptionInformation[3] = exc->private_[3]; + // Give NTRTL some scratch space to keep track of the collided unwind. + // Don't use the one that was passed in; we don't want to overwrite the + // context in the DISPATCHER_CONTEXT. + CONTEXT new_ctx; + RtlUnwindEx(frame, (PVOID)target, ms_exc, (PVOID)retval, &new_ctx, disp->HistoryTable); + _LIBUNWIND_ABORT("RtlUnwindEx() failed"); + } + // Anything else indicates a serious problem. + default: return ExceptionContinueExecution; + } +} + +/// Personality function returned by \c __unw_get_proc_info() in SEH contexts. +/// This is a wrapper that calls the real SEH handler function, which in +/// turn (at least, for Itanium-style frames) calls the real Itanium +/// personality function (see \c _GCC_specific_handler()). +extern "C" _Unwind_Reason_Code +__libunwind_seh_personality(int version, _Unwind_Action state, + uint64_t klass, _Unwind_Exception *exc, + struct _Unwind_Context *context) { + (void)version; + (void)klass; + EXCEPTION_RECORD ms_exc; + bool phase2 = (state & (_UA_SEARCH_PHASE|_UA_CLEANUP_PHASE)) == _UA_CLEANUP_PHASE; + ms_exc.ExceptionCode = STATUS_GCC_THROW; + ms_exc.ExceptionFlags = 0; + ms_exc.NumberParameters = 3; + ms_exc.ExceptionInformation[0] = (ULONG_PTR)exc; + ms_exc.ExceptionInformation[1] = (ULONG_PTR)context; + ms_exc.ExceptionInformation[2] = state; + DISPATCHER_CONTEXT *disp_ctx = + __unw_seh_get_disp_ctx((unw_cursor_t *)context); + EXCEPTION_DISPOSITION ms_act = disp_ctx->LanguageHandler(&ms_exc, + (PVOID)disp_ctx->EstablisherFrame, + disp_ctx->ContextRecord, + disp_ctx); + switch (ms_act) { + case ExceptionContinueSearch: return _URC_CONTINUE_UNWIND; + case 4 /*ExceptionExecuteHandler*/: + return phase2 ? _URC_INSTALL_CONTEXT : _URC_HANDLER_FOUND; + default: + return phase2 ? _URC_FATAL_PHASE2_ERROR : _URC_FATAL_PHASE1_ERROR; + } +} + +static _Unwind_Reason_Code +unwind_phase2_forced(unw_context_t *uc, + _Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter) { + unw_cursor_t cursor2; + __unw_init_local(&cursor2, uc); + + // Walk each frame until we reach where search phase said to stop + while (__unw_step(&cursor2) > 0) { + + // Update info about this frame. + unw_proc_info_t frameInfo; + if (__unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step " + "failed => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(&cursor2, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIx64 + ", func=%s, lsda=0x%" PRIx64 ", personality=0x%" PRIx64, + (void *)exception_object, frameInfo.start_ip, functionName, + frameInfo.lsda, frameInfo.handler); + } + + // Call stop function at each frame. + _Unwind_Action action = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = + (*stop)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(&cursor2), stop_parameter); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stop function returned %d", + (void *)exception_object, stopResult); + if (stopResult != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stopped by stop function", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // If there is a personality routine, tell it we are unwinding. + if (frameInfo.handler != 0) { + __personality_routine p = + (__personality_routine)(intptr_t)(frameInfo.handler); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): calling personality function %p", + (void *)exception_object, (void *)(uintptr_t)p); + _Unwind_Reason_Code personalityResult = + (*p)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(&cursor2)); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned " + "_URC_CONTINUE_UNWIND", + (void *)exception_object); + // Destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned " + "_URC_INSTALL_CONTEXT", + (void *)exception_object); + // We may get control back if landing pad calls _Unwind_Resume(). + __unw_resume(&cursor2); + break; + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned %d, " + "_URC_FATAL_PHASE2_ERROR", + (void *)exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // Call stop function one last time and tell it we've reached the end + // of the stack. + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " + "function with _UA_END_OF_STACK", + (void *)exception_object); + _Unwind_Action lastAction = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(&cursor2), stop_parameter); + + // Clean up phase did not resume at the frame that the search phase said it + // would. + return _URC_FATAL_PHASE2_ERROR; +} + +/// Called by \c __cxa_throw(). Only returns if there is a fatal error. +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_RaiseException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)", + (void *)exception_object); + + // Mark that this is a non-forced unwind, so _Unwind_Resume() + // can do the right thing. + memset(exception_object->private_, 0, sizeof(exception_object->private_)); + + // phase 1: the search phase + // We'll let the system do that for us. + RaiseException(STATUS_GCC_THROW, 0, 1, (ULONG_PTR *)&exception_object); + + // If we get here, either something went horribly wrong or we reached the + // top of the stack. Either way, let libc++abi call std::terminate(). + return _URC_END_OF_STACK; +} + +/// When \c _Unwind_RaiseException() is in phase2, it hands control +/// to the personality function at each frame. The personality +/// may force a jump to a landing pad in that function; the landing +/// pad code may then call \c _Unwind_Resume() to continue with the +/// unwinding. Note: the call to \c _Unwind_Resume() is from compiler +/// geneated user code. All other \c _Unwind_* routines are called +/// by the C++ runtime \c __cxa_* routines. +/// +/// Note: re-throwing an exception (as opposed to continuing the unwind) +/// is implemented by having the code call \c __cxa_rethrow() which +/// in turn calls \c _Unwind_Resume_or_Rethrow(). +_LIBUNWIND_EXPORT void +_Unwind_Resume(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", (void *)exception_object); + + if (exception_object->private_[0] != 0) { + unw_context_t uc; + + __unw_getcontext(&uc); + unwind_phase2_forced(&uc, exception_object, + (_Unwind_Stop_Fn) exception_object->private_[0], + (void *)exception_object->private_[4]); + } else { + // Recover the parameters for the unwind from the exception object + // so we can start unwinding again. + EXCEPTION_RECORD ms_exc; + CONTEXT ms_ctx; + UNWIND_HISTORY_TABLE hist; + + memset(&ms_exc, 0, sizeof(ms_exc)); + memset(&hist, 0, sizeof(hist)); + ms_exc.ExceptionCode = STATUS_GCC_THROW; + ms_exc.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + ms_exc.NumberParameters = 4; + ms_exc.ExceptionInformation[0] = (ULONG_PTR)exception_object; + ms_exc.ExceptionInformation[1] = exception_object->private_[1]; + ms_exc.ExceptionInformation[2] = exception_object->private_[2]; + ms_exc.ExceptionInformation[3] = exception_object->private_[3]; + RtlUnwindEx((PVOID)exception_object->private_[1], + (PVOID)exception_object->private_[2], &ms_exc, + exception_object, &ms_ctx, &hist); + } + + // Clients assume _Unwind_Resume() does not return, so all we can do is abort. + _LIBUNWIND_ABORT("_Unwind_Resume() can't return"); +} + +/// Not used by C++. +/// Unwinds stack, calling "stop" function at each frame. +/// Could be used to implement \c longjmp(). +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_ForcedUnwind(_Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter) { + _LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)", + (void *)exception_object, (void *)(uintptr_t)stop); + unw_context_t uc; + __unw_getcontext(&uc); + + // Mark that this is a forced unwind, so _Unwind_Resume() can do + // the right thing. + exception_object->private_[0] = (uintptr_t) stop; + exception_object->private_[4] = (uintptr_t) stop_parameter; + + // do it + return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter); +} + +/// Called by personality handler during phase 2 to get LSDA for current frame. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { + uintptr_t result = + (uintptr_t)__unw_seh_get_disp_ctx((unw_cursor_t *)context)->HandlerData; + _LIBUNWIND_TRACE_API( + "_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR, + (void *)context, result); + return result; +} + +/// Called by personality handler during phase 2 to find the start of the +/// function. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetRegionStart(struct _Unwind_Context *context) { + DISPATCHER_CONTEXT *disp = __unw_seh_get_disp_ctx((unw_cursor_t *)context); + uintptr_t result = (uintptr_t)disp->FunctionEntry->BeginAddress + disp->ImageBase; + _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIxPTR, + (void *)context, result); + return result; +} + +static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *context) { +#ifdef _LIBUNWIND_TARGET_X86_64 + new (reinterpret_cast *>(cursor)) + UnwindCursor( + context, LocalAddressSpace::sThisAddressSpace); + auto *co = reinterpret_cast(cursor); + co->setInfoBasedOnIPRegister(); + return UNW_ESUCCESS; +#elif defined(_LIBUNWIND_TARGET_ARM) + new (reinterpret_cast *>(cursor)) + UnwindCursor( + context, LocalAddressSpace::sThisAddressSpace); + auto *co = reinterpret_cast(cursor); + co->setInfoBasedOnIPRegister(); + return UNW_ESUCCESS; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + new (reinterpret_cast *>(cursor)) + UnwindCursor( + context, LocalAddressSpace::sThisAddressSpace); + auto *co = reinterpret_cast(cursor); + co->setInfoBasedOnIPRegister(); + return UNW_ESUCCESS; +#else + return UNW_EINVAL; +#endif +} + +static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor) { +#ifdef _LIBUNWIND_TARGET_X86_64 + return reinterpret_cast *>(cursor)->getDispatcherContext(); +#elif defined(_LIBUNWIND_TARGET_ARM) + return reinterpret_cast *>(cursor)->getDispatcherContext(); +#elif defined(_LIBUNWIND_TARGET_AARCH64) + return reinterpret_cast *>(cursor)->getDispatcherContext(); +#else + return nullptr; +#endif +} + +static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor, + DISPATCHER_CONTEXT *disp) { +#ifdef _LIBUNWIND_TARGET_X86_64 + reinterpret_cast *>(cursor)->setDispatcherContext(disp); +#elif defined(_LIBUNWIND_TARGET_ARM) + reinterpret_cast *>(cursor)->setDispatcherContext(disp); +#elif defined(_LIBUNWIND_TARGET_AARCH64) + reinterpret_cast *>(cursor)->setDispatcherContext(disp); +#endif +} + +#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) diff --git a/src/coreclr/nativeaot/libunwind/src/Unwind-sjlj.c b/src/coreclr/nativeaot/libunwind/src/Unwind-sjlj.c new file mode 100644 index 00000000000000..b8bb7c83bdff7a --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/Unwind-sjlj.c @@ -0,0 +1,516 @@ +//===--------------------------- Unwind-sjlj.c ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Implements setjump-longjump based C++ exceptions +// +//===----------------------------------------------------------------------===// + +#include + +#include +#include +#include +#include + +#include "config.h" + +/// With SJLJ based exceptions, any function that has a catch clause or needs to +/// do any clean up when an exception propagates through it, needs to call +/// \c _Unwind_SjLj_Register at the start of the function and +/// \c _Unwind_SjLj_Unregister at the end. The register function is called with +/// the address of a block of memory in the function's stack frame. The runtime +/// keeps a linked list (stack) of these blocks - one per thread. The calling +/// function also sets the personality and lsda fields of the block. + +#if defined(_LIBUNWIND_BUILD_SJLJ_APIS) + +struct _Unwind_FunctionContext { + // next function in stack of handlers + struct _Unwind_FunctionContext *prev; + + // set by calling function before registering to be the landing pad + uint32_t resumeLocation; + + // set by personality handler to be parameters passed to landing pad function + uint32_t resumeParameters[4]; + + // set by calling function before registering + __personality_routine personality; // arm offset=24 + uintptr_t lsda; // arm offset=28 + + // variable length array, contains registers to restore + // 0 = r7, 1 = pc, 2 = sp + void *jbuf[]; +}; + +#if defined(_LIBUNWIND_HAS_NO_THREADS) +# define _LIBUNWIND_THREAD_LOCAL +#else +# if __STDC_VERSION__ >= 201112L +# define _LIBUNWIND_THREAD_LOCAL _Thread_local +# elif defined(_MSC_VER) +# define _LIBUNWIND_THREAD_LOCAL __declspec(thread) +# elif defined(__GNUC__) || defined(__clang__) +# define _LIBUNWIND_THREAD_LOCAL __thread +# else +# error Unable to create thread local storage +# endif +#endif + + +#if !defined(FOR_DYLD) + +#if defined(__APPLE__) +#include +#else +static _LIBUNWIND_THREAD_LOCAL struct _Unwind_FunctionContext *stack = NULL; +#endif + +static struct _Unwind_FunctionContext *__Unwind_SjLj_GetTopOfFunctionStack() { +#if defined(__APPLE__) + return _pthread_getspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key); +#else + return stack; +#endif +} + +static void +__Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext *fc) { +#if defined(__APPLE__) + _pthread_setspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key, fc); +#else + stack = fc; +#endif +} + +#endif + + +/// Called at start of each function that catches exceptions +_LIBUNWIND_EXPORT void +_Unwind_SjLj_Register(struct _Unwind_FunctionContext *fc) { + fc->prev = __Unwind_SjLj_GetTopOfFunctionStack(); + __Unwind_SjLj_SetTopOfFunctionStack(fc); +} + + +/// Called at end of each function that catches exceptions +_LIBUNWIND_EXPORT void +_Unwind_SjLj_Unregister(struct _Unwind_FunctionContext *fc) { + __Unwind_SjLj_SetTopOfFunctionStack(fc->prev); +} + + +static _Unwind_Reason_Code +unwind_phase1(struct _Unwind_Exception *exception_object) { + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1: initial function-context=%p", + (void *)c); + + // walk each frame looking for a place to stop + for (bool handlerNotFound = true; handlerNotFound; c = c->prev) { + + // check for no more frames + if (c == NULL) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): reached " + "bottom => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_END_OF_STACK; + } + + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1: function-context=%p", (void *)c); + // if there is a personality routine, ask it if it will want to stop at this + // frame + if (c->personality != NULL) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): calling " + "personality function %p", + (void *)exception_object, + (void *)c->personality); + _Unwind_Reason_Code personalityResult = (*c->personality)( + 1, _UA_SEARCH_PHASE, exception_object->exception_class, + exception_object, (struct _Unwind_Context *)c); + switch (personalityResult) { + case _URC_HANDLER_FOUND: + // found a catch clause or locals that need destructing in this frame + // stop search and remember function context + handlerNotFound = false; + exception_object->private_2 = (uintptr_t) c; + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): " + "_URC_HANDLER_FOUND", + (void *)exception_object); + return _URC_NO_REASON; + + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): " + "_URC_CONTINUE_UNWIND", + (void *)exception_object); + // continue unwinding + break; + + default: + // something went wrong + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR", + (void *)exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + } + } + return _URC_NO_REASON; +} + + +static _Unwind_Reason_Code +unwind_phase2(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)", + (void *)exception_object); + + // walk each frame until we reach where search phase said to stop + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + while (true) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2s(ex_ojb=%p): context=%p", + (void *)exception_object, (void *)c); + + // check for no more frames + if (c == NULL) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): __unw_step() reached " + "bottom => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_END_OF_STACK; + } + + // if there is a personality routine, tell it we are unwinding + if (c->personality != NULL) { + _Unwind_Action action = _UA_CLEANUP_PHASE; + if ((uintptr_t) c == exception_object->private_2) + action = (_Unwind_Action)( + _UA_CLEANUP_PHASE | + _UA_HANDLER_FRAME); // tell personality this was the frame it marked + // in phase 1 + _Unwind_Reason_Code personalityResult = + (*c->personality)(1, action, exception_object->exception_class, + exception_object, (struct _Unwind_Context *)c); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + // continue unwinding + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND", + (void *)exception_object); + if ((uintptr_t) c == exception_object->private_2) { + // phase 1 said we would stop at this frame, but we did not... + _LIBUNWIND_ABORT("during phase1 personality function said it would " + "stop here, but now if phase2 it did not stop here"); + } + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): " + "_URC_INSTALL_CONTEXT, will resume at " + "landing pad %p", + (void *)exception_object, c->jbuf[1]); + // personality routine says to transfer control to landing pad + // we may get control back if landing pad calls _Unwind_Resume() + __Unwind_SjLj_SetTopOfFunctionStack(c); + __builtin_longjmp(c->jbuf, 1); + // __unw_resume() only returns if there was an error + return _URC_FATAL_PHASE2_ERROR; + default: + // something went wrong + _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", + personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + c = c->prev; + } + + // clean up phase did not resume at the frame that the search phase said it + // would + return _URC_FATAL_PHASE2_ERROR; +} + + +static _Unwind_Reason_Code +unwind_phase2_forced(struct _Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter) { + // walk each frame until we reach where search phase said to stop + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + while (true) { + + // get next frame (skip over first which is _Unwind_RaiseException) + if (c == NULL) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): __unw_step() reached " + "bottom => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_END_OF_STACK; + } + + // call stop function at each frame + _Unwind_Action action = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = + (*stop)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)c, stop_parameter); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "stop function returned %d", + (void *)exception_object, stopResult); + if (stopResult != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "stopped by stop function", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // if there is a personality routine, tell it we are unwinding + if (c->personality != NULL) { + __personality_routine p = (__personality_routine) c->personality; + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "calling personality function %p", + (void *)exception_object, (void *)p); + _Unwind_Reason_Code personalityResult = + (*p)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)c); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned _URC_CONTINUE_UNWIND", + (void *)exception_object); + // destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned _URC_INSTALL_CONTEXT", + (void *)exception_object); + // we may get control back if landing pad calls _Unwind_Resume() + __Unwind_SjLj_SetTopOfFunctionStack(c); + __builtin_longjmp(c->jbuf, 1); + break; + default: + // something went wrong + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned %d, " + "_URC_FATAL_PHASE2_ERROR", + (void *)exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + c = c->prev; + } + + // call stop function one last time and tell it we've reached the end of the + // stack + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " + "function with _UA_END_OF_STACK", + (void *)exception_object); + _Unwind_Action lastAction = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)c, stop_parameter); + + // clean up phase did not resume at the frame that the search phase said it + // would + return _URC_FATAL_PHASE2_ERROR; +} + + +/// Called by __cxa_throw. Only returns if there is a fatal error +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_SjLj_RaiseException(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_SjLj_RaiseException(ex_obj=%p)", + (void *)exception_object); + + // mark that this is a non-forced unwind, so _Unwind_Resume() can do the right + // thing + exception_object->private_1 = 0; + exception_object->private_2 = 0; + + // phase 1: the search phase + _Unwind_Reason_Code phase1 = unwind_phase1(exception_object); + if (phase1 != _URC_NO_REASON) + return phase1; + + // phase 2: the clean up phase + return unwind_phase2(exception_object); +} + + + +/// When _Unwind_RaiseException() is in phase2, it hands control +/// to the personality function at each frame. The personality +/// may force a jump to a landing pad in that function, the landing +/// pad code may then call _Unwind_Resume() to continue with the +/// unwinding. Note: the call to _Unwind_Resume() is from compiler +/// geneated user code. All other _Unwind_* routines are called +/// by the C++ runtime __cxa_* routines. +/// +/// Re-throwing an exception is implemented by having the code call +/// __cxa_rethrow() which in turn calls _Unwind_Resume_or_Rethrow() +_LIBUNWIND_EXPORT void +_Unwind_SjLj_Resume(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_SjLj_Resume(ex_obj=%p)", + (void *)exception_object); + + if (exception_object->private_1 != 0) + unwind_phase2_forced(exception_object, + (_Unwind_Stop_Fn) exception_object->private_1, + (void *)exception_object->private_2); + else + unwind_phase2(exception_object); + + // clients assume _Unwind_Resume() does not return, so all we can do is abort. + _LIBUNWIND_ABORT("_Unwind_SjLj_Resume() can't return"); +} + + +/// Called by __cxa_rethrow(). +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("__Unwind_SjLj_Resume_or_Rethrow(ex_obj=%p), " + "private_1=%" PRIuPTR, + (void *)exception_object, exception_object->private_1); + // If this is non-forced and a stopping place was found, then this is a + // re-throw. + // Call _Unwind_RaiseException() as if this was a new exception. + if (exception_object->private_1 == 0) { + return _Unwind_SjLj_RaiseException(exception_object); + // should return if there is no catch clause, so that __cxa_rethrow can call + // std::terminate() + } + + // Call through to _Unwind_Resume() which distiguishes between forced and + // regular exceptions. + _Unwind_SjLj_Resume(exception_object); + _LIBUNWIND_ABORT("__Unwind_SjLj_Resume_or_Rethrow() called " + "_Unwind_SjLj_Resume() which unexpectedly returned"); +} + + +/// Called by personality handler during phase 2 to get LSDA for current frame. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + _LIBUNWIND_TRACE_API("_Unwind_GetLanguageSpecificData(context=%p) " + "=> 0x%" PRIuPTR, + (void *)context, ufc->lsda); + return ufc->lsda; +} + + +/// Called by personality handler during phase 2 to get register values. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, + int index) { + _LIBUNWIND_TRACE_API("_Unwind_GetGR(context=%p, reg=%d)", (void *)context, + index); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + return ufc->resumeParameters[index]; +} + + +/// Called by personality handler during phase 2 to alter register values. +_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index, + uintptr_t new_value) { + _LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%" PRIuPTR + ")", + (void *)context, index, new_value); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + ufc->resumeParameters[index] = new_value; +} + + +/// Called by personality handler during phase 2 to get instruction pointer. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIu32, + (void *)context, ufc->resumeLocation + 1); + return ufc->resumeLocation + 1; +} + + +/// Called by personality handler during phase 2 to get instruction pointer. +/// ipBefore is a boolean that says if IP is already adjusted to be the call +/// site address. Normally IP is the return address. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context, + int *ipBefore) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + *ipBefore = 0; + _LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p, %p) => 0x%" PRIu32, + (void *)context, (void *)ipBefore, + ufc->resumeLocation + 1); + return ufc->resumeLocation + 1; +} + + +/// Called by personality handler during phase 2 to alter instruction pointer. +_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context, + uintptr_t new_value) { + _LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%" PRIuPTR ")", + (void *)context, new_value); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + ufc->resumeLocation = new_value - 1; +} + + +/// Called by personality handler during phase 2 to find the start of the +/// function. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetRegionStart(struct _Unwind_Context *context) { + // Not supported or needed for sjlj based unwinding + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p)", (void *)context); + return 0; +} + + +/// Called by personality handler during phase 2 if a foreign exception +/// is caught. +_LIBUNWIND_EXPORT void +_Unwind_DeleteException(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)", + (void *)exception_object); + if (exception_object->exception_cleanup != NULL) + (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, + exception_object); +} + + + +/// Called by personality handler during phase 2 to get base address for data +/// relative encodings. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetDataRelBase(struct _Unwind_Context *context) { + // Not supported or needed for sjlj based unwinding + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)", (void *)context); + _LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented"); +} + + +/// Called by personality handler during phase 2 to get base address for text +/// relative encodings. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetTextRelBase(struct _Unwind_Context *context) { + // Not supported or needed for sjlj based unwinding + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)", (void *)context); + _LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented"); +} + + +/// Called by personality handler to get "Call Frame Area" for current frame. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) { + _LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p)", (void *)context); + if (context != NULL) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + // Setjmp/longjmp based exceptions don't have a true CFA. + // Instead, the SP in the jmpbuf is the closest approximation. + return (uintptr_t) ufc->jbuf[2]; + } + return 0; +} + +#endif // defined(_LIBUNWIND_BUILD_SJLJ_APIS) diff --git a/src/coreclr/nativeaot/libunwind/src/UnwindCursor.hpp b/src/coreclr/nativeaot/libunwind/src/UnwindCursor.hpp new file mode 100644 index 00000000000000..3a6c5131c93bc2 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/UnwindCursor.hpp @@ -0,0 +1,2037 @@ +//===------------------------- UnwindCursor.hpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// C++ interface to lower levels of libunwind +//===----------------------------------------------------------------------===// + +#ifndef __UNWINDCURSOR_HPP__ +#define __UNWINDCURSOR_HPP__ + +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include +#endif +#ifdef __APPLE__ + #include +#endif + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) +// Provide a definition for the DISPATCHER_CONTEXT struct for old (Win7 and +// earlier) SDKs. +// MinGW-w64 has always provided this struct. + #if defined(_WIN32) && defined(_LIBUNWIND_TARGET_X86_64) && \ + !defined(__MINGW32__) && VER_PRODUCTBUILD < 8000 +struct _DISPATCHER_CONTEXT { + ULONG64 ControlPc; + ULONG64 ImageBase; + PRUNTIME_FUNCTION FunctionEntry; + ULONG64 EstablisherFrame; + ULONG64 TargetIp; + PCONTEXT ContextRecord; + PEXCEPTION_ROUTINE LanguageHandler; + PVOID HandlerData; + PUNWIND_HISTORY_TABLE HistoryTable; + ULONG ScopeIndex; + ULONG Fill0; +}; + #endif + +struct UNWIND_INFO { + uint8_t Version : 3; + uint8_t Flags : 5; + uint8_t SizeOfProlog; + uint8_t CountOfCodes; + uint8_t FrameRegister : 4; + uint8_t FrameOffset : 4; + uint16_t UnwindCodes[2]; +}; + +extern "C" _Unwind_Reason_Code __libunwind_seh_personality( + int, _Unwind_Action, uint64_t, _Unwind_Exception *, + struct _Unwind_Context *); + +#endif + +#include "config.h" + +#include "AddressSpace.hpp" +#include "CompactUnwinder.hpp" +#include "config.h" +#include "DwarfInstructions.hpp" +#include "EHHeaderParser.hpp" +#include "libunwind.h" +#include "Registers.hpp" +#include "RWMutex.hpp" +#include "Unwind-EHABI.h" + +namespace libunwind { + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +/// Cache of recently found FDEs. +template +class _LIBUNWIND_HIDDEN DwarfFDECache { + typedef typename A::pint_t pint_t; +public: + static pint_t findFDE(pint_t mh, pint_t pc); + static void add(pint_t mh, pint_t ip_start, pint_t ip_end, pint_t fde); + static void removeAllIn(pint_t mh); + static void iterateCacheEntries(void (*func)(unw_word_t ip_start, + unw_word_t ip_end, + unw_word_t fde, unw_word_t mh)); + +private: + + struct entry { + pint_t mh; + pint_t ip_start; + pint_t ip_end; + pint_t fde; + }; + + // These fields are all static to avoid needing an initializer. + // There is only one instance of this class per process. + static RWMutex _lock; +#ifdef __APPLE__ + static void dyldUnloadHook(const struct mach_header *mh, intptr_t slide); + static bool _registeredForDyldUnloads; +#endif + static entry *_buffer; + static entry *_bufferUsed; + static entry *_bufferEnd; + static entry _initialBuffer[64]; +}; + +template +typename DwarfFDECache::entry * +DwarfFDECache::_buffer = _initialBuffer; + +template +typename DwarfFDECache::entry * +DwarfFDECache::_bufferUsed = _initialBuffer; + +template +typename DwarfFDECache::entry * +DwarfFDECache::_bufferEnd = &_initialBuffer[64]; + +template +typename DwarfFDECache::entry DwarfFDECache::_initialBuffer[64]; + +template +RWMutex DwarfFDECache::_lock; + +#ifdef __APPLE__ +template +bool DwarfFDECache::_registeredForDyldUnloads = false; +#endif + +template +typename A::pint_t DwarfFDECache::findFDE(pint_t mh, pint_t pc) { + pint_t result = 0; + _LIBUNWIND_LOG_IF_FALSE(_lock.lock_shared()); + for (entry *p = _buffer; p < _bufferUsed; ++p) { + if ((mh == p->mh) || (mh == 0)) { + if ((p->ip_start <= pc) && (pc < p->ip_end)) { + result = p->fde; + break; + } + } + } + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock_shared()); + return result; +} + +template +void DwarfFDECache::add(pint_t mh, pint_t ip_start, pint_t ip_end, + pint_t fde) { +#if !defined(_LIBUNWIND_NO_HEAP) + _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); + if (_bufferUsed >= _bufferEnd) { + size_t oldSize = (size_t)(_bufferEnd - _buffer); + size_t newSize = oldSize * 4; + // Can't use operator new (we are below it). + entry *newBuffer = (entry *)malloc(newSize * sizeof(entry)); + memcpy(newBuffer, _buffer, oldSize * sizeof(entry)); + if (_buffer != _initialBuffer) + free(_buffer); + _buffer = newBuffer; + _bufferUsed = &newBuffer[oldSize]; + _bufferEnd = &newBuffer[newSize]; + } + _bufferUsed->mh = mh; + _bufferUsed->ip_start = ip_start; + _bufferUsed->ip_end = ip_end; + _bufferUsed->fde = fde; + ++_bufferUsed; +#ifdef __APPLE__ + if (!_registeredForDyldUnloads) { + _dyld_register_func_for_remove_image(&dyldUnloadHook); + _registeredForDyldUnloads = true; + } +#endif + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +#endif +} + +template +void DwarfFDECache::removeAllIn(pint_t mh) { + _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); + entry *d = _buffer; + for (const entry *s = _buffer; s < _bufferUsed; ++s) { + if (s->mh != mh) { + if (d != s) + *d = *s; + ++d; + } + } + _bufferUsed = d; + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +} + +#ifdef __APPLE__ +template +void DwarfFDECache::dyldUnloadHook(const struct mach_header *mh, intptr_t ) { + removeAllIn((pint_t) mh); +} +#endif + +template +void DwarfFDECache::iterateCacheEntries(void (*func)( + unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) { + _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); + for (entry *p = _buffer; p < _bufferUsed; ++p) { + (*func)(p->ip_start, p->ip_end, p->fde, p->mh); + } + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +} +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + + +#define arrayoffsetof(type, index, field) ((size_t)(&((type *)0)[index].field)) + +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) +template class UnwindSectionHeader { +public: + UnwindSectionHeader(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t version() const { + return _addressSpace.get32(_addr + + offsetof(unwind_info_section_header, version)); + } + uint32_t commonEncodingsArraySectionOffset() const { + return _addressSpace.get32(_addr + + offsetof(unwind_info_section_header, + commonEncodingsArraySectionOffset)); + } + uint32_t commonEncodingsArrayCount() const { + return _addressSpace.get32(_addr + offsetof(unwind_info_section_header, + commonEncodingsArrayCount)); + } + uint32_t personalityArraySectionOffset() const { + return _addressSpace.get32(_addr + offsetof(unwind_info_section_header, + personalityArraySectionOffset)); + } + uint32_t personalityArrayCount() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_section_header, personalityArrayCount)); + } + uint32_t indexSectionOffset() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_section_header, indexSectionOffset)); + } + uint32_t indexCount() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_section_header, indexCount)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionIndexArray { +public: + UnwindSectionIndexArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_index_entry, index, + functionOffset)); + } + uint32_t secondLevelPagesSectionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_index_entry, index, + secondLevelPagesSectionOffset)); + } + uint32_t lsdaIndexArraySectionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_index_entry, index, + lsdaIndexArraySectionOffset)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionRegularPageHeader { +public: + UnwindSectionRegularPageHeader(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t kind() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_regular_second_level_page_header, kind)); + } + uint16_t entryPageOffset() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_regular_second_level_page_header, + entryPageOffset)); + } + uint16_t entryCount() const { + return _addressSpace.get16( + _addr + + offsetof(unwind_info_regular_second_level_page_header, entryCount)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionRegularArray { +public: + UnwindSectionRegularArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_regular_second_level_entry, index, + functionOffset)); + } + uint32_t encoding(uint32_t index) const { + return _addressSpace.get32( + _addr + + arrayoffsetof(unwind_info_regular_second_level_entry, index, encoding)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionCompressedPageHeader { +public: + UnwindSectionCompressedPageHeader(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t kind() const { + return _addressSpace.get32( + _addr + + offsetof(unwind_info_compressed_second_level_page_header, kind)); + } + uint16_t entryPageOffset() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_compressed_second_level_page_header, + entryPageOffset)); + } + uint16_t entryCount() const { + return _addressSpace.get16( + _addr + + offsetof(unwind_info_compressed_second_level_page_header, entryCount)); + } + uint16_t encodingsPageOffset() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_compressed_second_level_page_header, + encodingsPageOffset)); + } + uint16_t encodingsCount() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_compressed_second_level_page_header, + encodingsCount)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionCompressedArray { +public: + UnwindSectionCompressedArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET( + _addressSpace.get32(_addr + index * sizeof(uint32_t))); + } + uint16_t encodingIndex(uint32_t index) const { + return UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX( + _addressSpace.get32(_addr + index * sizeof(uint32_t))); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionLsdaArray { +public: + UnwindSectionLsdaArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, + index, functionOffset)); + } + uint32_t lsdaOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, + index, lsdaOffset)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; +#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + +class _LIBUNWIND_HIDDEN AbstractUnwindCursor { +public: + // NOTE: provide a class specific placement deallocation function (S5.3.4 p20) + // This avoids an unnecessary dependency to libc++abi. + void operator delete(void *, size_t) {} + + virtual ~AbstractUnwindCursor() {} + virtual bool validReg(int) { _LIBUNWIND_ABORT("validReg not implemented"); } + virtual unw_word_t getReg(int) { _LIBUNWIND_ABORT("getReg not implemented"); } + virtual void setReg(int, unw_word_t, unw_word_t) { + _LIBUNWIND_ABORT("setReg not implemented"); + } + virtual unw_word_t getRegLocation(int) { + _LIBUNWIND_ABORT("getRegLocation not implemented"); + } + virtual bool validFloatReg(int) { + _LIBUNWIND_ABORT("validFloatReg not implemented"); + } + virtual unw_fpreg_t getFloatReg(int) { + _LIBUNWIND_ABORT("getFloatReg not implemented"); + } + virtual void setFloatReg(int, unw_fpreg_t) { + _LIBUNWIND_ABORT("setFloatReg not implemented"); + } + virtual int step() { _LIBUNWIND_ABORT("step not implemented"); } + virtual void getInfo(unw_proc_info_t *) { + _LIBUNWIND_ABORT("getInfo not implemented"); + } + virtual void jumpto() { _LIBUNWIND_ABORT("jumpto not implemented"); } + virtual bool isSignalFrame() { + _LIBUNWIND_ABORT("isSignalFrame not implemented"); + } + virtual bool getFunctionName(char *, size_t, unw_word_t *) { + _LIBUNWIND_ABORT("getFunctionName not implemented"); + } + virtual void setInfoBasedOnIPRegister(bool = false) { + _LIBUNWIND_ABORT("setInfoBasedOnIPRegister not implemented"); + } + virtual const char *getRegisterName(int) { + _LIBUNWIND_ABORT("getRegisterName not implemented"); + } +#ifdef __arm__ + virtual void saveVFPAsX() { _LIBUNWIND_ABORT("saveVFPAsX not implemented"); } +#endif +}; + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32) + +/// \c UnwindCursor contains all state (including all register values) during +/// an unwind. This is normally stack-allocated inside a unw_cursor_t. +template +class UnwindCursor : public AbstractUnwindCursor { + typedef typename A::pint_t pint_t; +public: + UnwindCursor(unw_context_t *context, A &as); + UnwindCursor(CONTEXT *context, A &as); + UnwindCursor(A &as, void *threadArg); + virtual ~UnwindCursor() {} + virtual bool validReg(int); + virtual unw_word_t getReg(int); + virtual void setReg(int, unw_word_t); + virtual bool validFloatReg(int); + virtual unw_fpreg_t getFloatReg(int); + virtual void setFloatReg(int, unw_fpreg_t); + virtual int step(); + virtual void getInfo(unw_proc_info_t *); + virtual void jumpto(); + virtual bool isSignalFrame(); + virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off); + virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false); + virtual const char *getRegisterName(int num); +#ifdef __arm__ + virtual void saveVFPAsX(); +#endif + + DISPATCHER_CONTEXT *getDispatcherContext() { return &_dispContext; } + void setDispatcherContext(DISPATCHER_CONTEXT *disp) { _dispContext = *disp; } + + // libunwind does not and should not depend on C++ library which means that we + // need our own defition of inline placement new. + static void *operator new(size_t, UnwindCursor *p) { return p; } + +private: + + pint_t getLastPC() const { return _dispContext.ControlPc; } + void setLastPC(pint_t pc) { _dispContext.ControlPc = pc; } + RUNTIME_FUNCTION *lookUpSEHUnwindInfo(pint_t pc, pint_t *base) { + _dispContext.FunctionEntry = RtlLookupFunctionEntry(pc, + &_dispContext.ImageBase, + _dispContext.HistoryTable); + *base = _dispContext.ImageBase; + return _dispContext.FunctionEntry; + } + bool getInfoFromSEH(pint_t pc); + int stepWithSEHData() { + _dispContext.LanguageHandler = RtlVirtualUnwind(UNW_FLAG_UHANDLER, + _dispContext.ImageBase, + _dispContext.ControlPc, + _dispContext.FunctionEntry, + _dispContext.ContextRecord, + &_dispContext.HandlerData, + &_dispContext.EstablisherFrame, + NULL); + // Update some fields of the unwind info now, since we have them. + _info.lsda = reinterpret_cast(_dispContext.HandlerData); + if (_dispContext.LanguageHandler) { + _info.handler = reinterpret_cast(__libunwind_seh_personality); + } else + _info.handler = 0; + return UNW_STEP_SUCCESS; + } + + A &_addressSpace; + unw_proc_info_t _info; + DISPATCHER_CONTEXT _dispContext; + CONTEXT _msContext; + UNWIND_HISTORY_TABLE _histTable; + bool _unwindInfoMissing; +}; + + +template +UnwindCursor::UnwindCursor(unw_context_t *context, A &as) + : _addressSpace(as), _unwindInfoMissing(false) { + static_assert((check_fit, unw_cursor_t>::does_fit), + "UnwindCursor<> does not fit in unw_cursor_t"); + memset(&_info, 0, sizeof(_info)); + memset(&_histTable, 0, sizeof(_histTable)); + _dispContext.ContextRecord = &_msContext; + _dispContext.HistoryTable = &_histTable; + // Initialize MS context from ours. + R r(context); + _msContext.ContextFlags = CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_FLOATING_POINT; +#if defined(_LIBUNWIND_TARGET_X86_64) + _msContext.Rax = r.getRegister(UNW_X86_64_RAX); + _msContext.Rcx = r.getRegister(UNW_X86_64_RCX); + _msContext.Rdx = r.getRegister(UNW_X86_64_RDX); + _msContext.Rbx = r.getRegister(UNW_X86_64_RBX); + _msContext.Rsp = r.getRegister(UNW_X86_64_RSP); + _msContext.Rbp = r.getRegister(UNW_X86_64_RBP); + _msContext.Rsi = r.getRegister(UNW_X86_64_RSI); + _msContext.Rdi = r.getRegister(UNW_X86_64_RDI); + _msContext.R8 = r.getRegister(UNW_X86_64_R8); + _msContext.R9 = r.getRegister(UNW_X86_64_R9); + _msContext.R10 = r.getRegister(UNW_X86_64_R10); + _msContext.R11 = r.getRegister(UNW_X86_64_R11); + _msContext.R12 = r.getRegister(UNW_X86_64_R12); + _msContext.R13 = r.getRegister(UNW_X86_64_R13); + _msContext.R14 = r.getRegister(UNW_X86_64_R14); + _msContext.R15 = r.getRegister(UNW_X86_64_R15); + _msContext.Rip = r.getRegister(UNW_REG_IP); + union { + v128 v; + M128A m; + } t; + t.v = r.getVectorRegister(UNW_X86_64_XMM0); + _msContext.Xmm0 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM1); + _msContext.Xmm1 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM2); + _msContext.Xmm2 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM3); + _msContext.Xmm3 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM4); + _msContext.Xmm4 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM5); + _msContext.Xmm5 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM6); + _msContext.Xmm6 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM7); + _msContext.Xmm7 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM8); + _msContext.Xmm8 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM9); + _msContext.Xmm9 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM10); + _msContext.Xmm10 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM11); + _msContext.Xmm11 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM12); + _msContext.Xmm12 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM13); + _msContext.Xmm13 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM14); + _msContext.Xmm14 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM15); + _msContext.Xmm15 = t.m; +#elif defined(_LIBUNWIND_TARGET_ARM) + _msContext.R0 = r.getRegister(UNW_ARM_R0); + _msContext.R1 = r.getRegister(UNW_ARM_R1); + _msContext.R2 = r.getRegister(UNW_ARM_R2); + _msContext.R3 = r.getRegister(UNW_ARM_R3); + _msContext.R4 = r.getRegister(UNW_ARM_R4); + _msContext.R5 = r.getRegister(UNW_ARM_R5); + _msContext.R6 = r.getRegister(UNW_ARM_R6); + _msContext.R7 = r.getRegister(UNW_ARM_R7); + _msContext.R8 = r.getRegister(UNW_ARM_R8); + _msContext.R9 = r.getRegister(UNW_ARM_R9); + _msContext.R10 = r.getRegister(UNW_ARM_R10); + _msContext.R11 = r.getRegister(UNW_ARM_R11); + _msContext.R12 = r.getRegister(UNW_ARM_R12); + _msContext.Sp = r.getRegister(UNW_ARM_SP); + _msContext.Lr = r.getRegister(UNW_ARM_LR); + _msContext.Pc = r.getRegister(UNW_ARM_IP); + for (int i = UNW_ARM_D0; i <= UNW_ARM_D31; ++i) { + union { + uint64_t w; + double d; + } d; + d.d = r.getFloatRegister(i); + _msContext.D[i - UNW_ARM_D0] = d.w; + } +#elif defined(_LIBUNWIND_TARGET_AARCH64) + for (int i = UNW_ARM64_X0; i <= UNW_ARM64_X30; ++i) + _msContext.X[i - UNW_ARM64_X0] = r.getRegister(i); + _msContext.Sp = r.getRegister(UNW_REG_SP); + _msContext.Pc = r.getRegister(UNW_REG_IP); + for (int i = UNW_ARM64_D0; i <= UNW_ARM64_D31; ++i) + _msContext.V[i - UNW_ARM64_D0].D[0] = r.getFloatRegister(i); +#endif +} + +template +UnwindCursor::UnwindCursor(CONTEXT *context, A &as) + : _addressSpace(as), _unwindInfoMissing(false) { + static_assert((check_fit, unw_cursor_t>::does_fit), + "UnwindCursor<> does not fit in unw_cursor_t"); + memset(&_info, 0, sizeof(_info)); + memset(&_histTable, 0, sizeof(_histTable)); + _dispContext.ContextRecord = &_msContext; + _dispContext.HistoryTable = &_histTable; + _msContext = *context; +} + + +template +bool UnwindCursor::validReg(int regNum) { + if (regNum == UNW_REG_IP || regNum == UNW_REG_SP) return true; +#if defined(_LIBUNWIND_TARGET_X86_64) + if (regNum >= UNW_X86_64_RAX && regNum <= UNW_X86_64_R15) return true; +#elif defined(_LIBUNWIND_TARGET_ARM) + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) return true; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + if (regNum >= UNW_ARM64_X0 && regNum <= UNW_ARM64_X30) return true; +#endif + return false; +} + +template +unw_word_t UnwindCursor::getReg(int regNum) { + switch (regNum) { +#if defined(_LIBUNWIND_TARGET_X86_64) + case UNW_REG_IP: return _msContext.Rip; + case UNW_X86_64_RAX: return _msContext.Rax; + case UNW_X86_64_RDX: return _msContext.Rdx; + case UNW_X86_64_RCX: return _msContext.Rcx; + case UNW_X86_64_RBX: return _msContext.Rbx; + case UNW_REG_SP: + case UNW_X86_64_RSP: return _msContext.Rsp; + case UNW_X86_64_RBP: return _msContext.Rbp; + case UNW_X86_64_RSI: return _msContext.Rsi; + case UNW_X86_64_RDI: return _msContext.Rdi; + case UNW_X86_64_R8: return _msContext.R8; + case UNW_X86_64_R9: return _msContext.R9; + case UNW_X86_64_R10: return _msContext.R10; + case UNW_X86_64_R11: return _msContext.R11; + case UNW_X86_64_R12: return _msContext.R12; + case UNW_X86_64_R13: return _msContext.R13; + case UNW_X86_64_R14: return _msContext.R14; + case UNW_X86_64_R15: return _msContext.R15; +#elif defined(_LIBUNWIND_TARGET_ARM) + case UNW_ARM_R0: return _msContext.R0; + case UNW_ARM_R1: return _msContext.R1; + case UNW_ARM_R2: return _msContext.R2; + case UNW_ARM_R3: return _msContext.R3; + case UNW_ARM_R4: return _msContext.R4; + case UNW_ARM_R5: return _msContext.R5; + case UNW_ARM_R6: return _msContext.R6; + case UNW_ARM_R7: return _msContext.R7; + case UNW_ARM_R8: return _msContext.R8; + case UNW_ARM_R9: return _msContext.R9; + case UNW_ARM_R10: return _msContext.R10; + case UNW_ARM_R11: return _msContext.R11; + case UNW_ARM_R12: return _msContext.R12; + case UNW_REG_SP: + case UNW_ARM_SP: return _msContext.Sp; + case UNW_ARM_LR: return _msContext.Lr; + case UNW_REG_IP: + case UNW_ARM_IP: return _msContext.Pc; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + case UNW_REG_SP: return _msContext.Sp; + case UNW_REG_IP: return _msContext.Pc; + default: return _msContext.X[regNum - UNW_ARM64_X0]; +#endif + } + _LIBUNWIND_ABORT("unsupported register"); +} + +template +void UnwindCursor::setReg(int regNum, unw_word_t value) { + switch (regNum) { +#if defined(_LIBUNWIND_TARGET_X86_64) + case UNW_REG_IP: _msContext.Rip = value; break; + case UNW_X86_64_RAX: _msContext.Rax = value; break; + case UNW_X86_64_RDX: _msContext.Rdx = value; break; + case UNW_X86_64_RCX: _msContext.Rcx = value; break; + case UNW_X86_64_RBX: _msContext.Rbx = value; break; + case UNW_REG_SP: + case UNW_X86_64_RSP: _msContext.Rsp = value; break; + case UNW_X86_64_RBP: _msContext.Rbp = value; break; + case UNW_X86_64_RSI: _msContext.Rsi = value; break; + case UNW_X86_64_RDI: _msContext.Rdi = value; break; + case UNW_X86_64_R8: _msContext.R8 = value; break; + case UNW_X86_64_R9: _msContext.R9 = value; break; + case UNW_X86_64_R10: _msContext.R10 = value; break; + case UNW_X86_64_R11: _msContext.R11 = value; break; + case UNW_X86_64_R12: _msContext.R12 = value; break; + case UNW_X86_64_R13: _msContext.R13 = value; break; + case UNW_X86_64_R14: _msContext.R14 = value; break; + case UNW_X86_64_R15: _msContext.R15 = value; break; +#elif defined(_LIBUNWIND_TARGET_ARM) + case UNW_ARM_R0: _msContext.R0 = value; break; + case UNW_ARM_R1: _msContext.R1 = value; break; + case UNW_ARM_R2: _msContext.R2 = value; break; + case UNW_ARM_R3: _msContext.R3 = value; break; + case UNW_ARM_R4: _msContext.R4 = value; break; + case UNW_ARM_R5: _msContext.R5 = value; break; + case UNW_ARM_R6: _msContext.R6 = value; break; + case UNW_ARM_R7: _msContext.R7 = value; break; + case UNW_ARM_R8: _msContext.R8 = value; break; + case UNW_ARM_R9: _msContext.R9 = value; break; + case UNW_ARM_R10: _msContext.R10 = value; break; + case UNW_ARM_R11: _msContext.R11 = value; break; + case UNW_ARM_R12: _msContext.R12 = value; break; + case UNW_REG_SP: + case UNW_ARM_SP: _msContext.Sp = value; break; + case UNW_ARM_LR: _msContext.Lr = value; break; + case UNW_REG_IP: + case UNW_ARM_IP: _msContext.Pc = value; break; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + case UNW_REG_SP: _msContext.Sp = value; break; + case UNW_REG_IP: _msContext.Pc = value; break; + case UNW_ARM64_X0: + case UNW_ARM64_X1: + case UNW_ARM64_X2: + case UNW_ARM64_X3: + case UNW_ARM64_X4: + case UNW_ARM64_X5: + case UNW_ARM64_X6: + case UNW_ARM64_X7: + case UNW_ARM64_X8: + case UNW_ARM64_X9: + case UNW_ARM64_X10: + case UNW_ARM64_X11: + case UNW_ARM64_X12: + case UNW_ARM64_X13: + case UNW_ARM64_X14: + case UNW_ARM64_X15: + case UNW_ARM64_X16: + case UNW_ARM64_X17: + case UNW_ARM64_X18: + case UNW_ARM64_X19: + case UNW_ARM64_X20: + case UNW_ARM64_X21: + case UNW_ARM64_X22: + case UNW_ARM64_X23: + case UNW_ARM64_X24: + case UNW_ARM64_X25: + case UNW_ARM64_X26: + case UNW_ARM64_X27: + case UNW_ARM64_X28: + case UNW_ARM64_FP: + case UNW_ARM64_LR: _msContext.X[regNum - UNW_ARM64_X0] = value; break; +#endif + default: + _LIBUNWIND_ABORT("unsupported register"); + } +} + +template +bool UnwindCursor::validFloatReg(int regNum) { +#if defined(_LIBUNWIND_TARGET_ARM) + if (regNum >= UNW_ARM_S0 && regNum <= UNW_ARM_S31) return true; + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) return true; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + if (regNum >= UNW_ARM64_D0 && regNum <= UNW_ARM64_D31) return true; +#else + (void)regNum; +#endif + return false; +} + +template +unw_fpreg_t UnwindCursor::getFloatReg(int regNum) { +#if defined(_LIBUNWIND_TARGET_ARM) + if (regNum >= UNW_ARM_S0 && regNum <= UNW_ARM_S31) { + union { + uint32_t w; + float f; + } d; + d.w = _msContext.S[regNum - UNW_ARM_S0]; + return d.f; + } + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) { + union { + uint64_t w; + double d; + } d; + d.w = _msContext.D[regNum - UNW_ARM_D0]; + return d.d; + } + _LIBUNWIND_ABORT("unsupported float register"); +#elif defined(_LIBUNWIND_TARGET_AARCH64) + return _msContext.V[regNum - UNW_ARM64_D0].D[0]; +#else + (void)regNum; + _LIBUNWIND_ABORT("float registers unimplemented"); +#endif +} + +template +void UnwindCursor::setFloatReg(int regNum, unw_fpreg_t value) { +#if defined(_LIBUNWIND_TARGET_ARM) + if (regNum >= UNW_ARM_S0 && regNum <= UNW_ARM_S31) { + union { + uint32_t w; + float f; + } d; + d.f = value; + _msContext.S[regNum - UNW_ARM_S0] = d.w; + } + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) { + union { + uint64_t w; + double d; + } d; + d.d = value; + _msContext.D[regNum - UNW_ARM_D0] = d.w; + } + _LIBUNWIND_ABORT("unsupported float register"); +#elif defined(_LIBUNWIND_TARGET_AARCH64) + _msContext.V[regNum - UNW_ARM64_D0].D[0] = value; +#else + (void)regNum; + (void)value; + _LIBUNWIND_ABORT("float registers unimplemented"); +#endif +} + +template void UnwindCursor::jumpto() { + RtlRestoreContext(&_msContext, nullptr); +} + +#ifdef __arm__ +template void UnwindCursor::saveVFPAsX() {} +#endif + +template +const char *UnwindCursor::getRegisterName(int regNum) { + return R::getRegisterName(regNum); +} + +template bool UnwindCursor::isSignalFrame() { + return false; +} + +#else // !defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) || !defined(_WIN32) + +/// UnwindCursor contains all state (including all register values) during +/// an unwind. This is normally stack allocated inside a unw_cursor_t. +template +class UnwindCursor : public AbstractUnwindCursor{ + typedef typename A::pint_t pint_t; +public: + UnwindCursor(A &as); + UnwindCursor(unw_context_t *context, A &as); + UnwindCursor(A &as, void *threadArg); + virtual ~UnwindCursor() {} + virtual bool validReg(int); + virtual unw_word_t getReg(int); + virtual void setReg(int, unw_word_t, unw_word_t); + virtual unw_word_t getRegLocation(int); + virtual bool validFloatReg(int); + virtual unw_fpreg_t getFloatReg(int); + virtual void setFloatReg(int, unw_fpreg_t); + virtual int step(); + virtual void getInfo(unw_proc_info_t *); + virtual void jumpto(); + virtual bool isSignalFrame(); + virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off); + virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false); + virtual const char *getRegisterName(int num); +#ifdef __arm__ + virtual void saveVFPAsX(); +#endif + + // libunwind does not and should not depend on C++ library which means that we + // need our own defition of inline placement new. + static void *operator new(size_t, UnwindCursor *p) { return p; } + +private: + +#if defined(_LIBUNWIND_ARM_EHABI) + bool getInfoFromEHABISection(pint_t pc, const UnwindInfoSections §s); + + int stepWithEHABI() { + size_t len = 0; + size_t off = 0; + // FIXME: Calling decode_eht_entry() here is violating the libunwind + // abstraction layer. + const uint32_t *ehtp = + decode_eht_entry(reinterpret_cast(_info.unwind_info), + &off, &len); + if (_Unwind_VRS_Interpret((_Unwind_Context *)this, ehtp, off, len) != + _URC_CONTINUE_UNWIND) + return UNW_STEP_END; + return UNW_STEP_SUCCESS; + } +#endif + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +public: + bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections §s, + uint32_t fdeSectionOffsetHint=0); + int stepWithDwarfFDE() { + return DwarfInstructions::stepWithDwarf(_addressSpace, + (pint_t)this->getReg(UNW_REG_IP), + (pint_t)_info.unwind_info, + _registers); + } +#endif + +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + bool getInfoFromCompactEncodingSection(pint_t pc, + const UnwindInfoSections §s); + int stepWithCompactEncoding() { + #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + if ( compactSaysUseDwarf() ) + return stepWithDwarfFDE(); + #endif + R dummy; + return stepWithCompactEncoding(dummy); + } + +#if defined(_LIBUNWIND_TARGET_X86_64) + int stepWithCompactEncoding(Registers_x86_64 &) { + return CompactUnwinder_x86_64::stepWithCompactEncoding( + _info.format, _info.start_ip, _addressSpace, _registers); + } +#endif + +#if defined(_LIBUNWIND_TARGET_I386) + int stepWithCompactEncoding(Registers_x86 &) { + return CompactUnwinder_x86::stepWithCompactEncoding( + _info.format, (uint32_t)_info.start_ip, _addressSpace, _registers); + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC) + int stepWithCompactEncoding(Registers_ppc &) { + return UNW_EINVAL; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC64) + int stepWithCompactEncoding(Registers_ppc64 &) { + return UNW_EINVAL; + } +#endif + + +#if defined(_LIBUNWIND_TARGET_AARCH64) + int stepWithCompactEncoding(Registers_arm64 &) { + return CompactUnwinder_arm64::stepWithCompactEncoding( + _info.format, _info.start_ip, _addressSpace, _registers); + } +#endif + +#if defined(_LIBUNWIND_TARGET_MIPS_O32) + int stepWithCompactEncoding(Registers_mips_o32 &) { + return UNW_EINVAL; + } +#endif + +#if defined(_LIBUNWIND_TARGET_MIPS_NEWABI) + int stepWithCompactEncoding(Registers_mips_newabi &) { + return UNW_EINVAL; + } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC) + int stepWithCompactEncoding(Registers_sparc &) { return UNW_EINVAL; } +#endif + + bool compactSaysUseDwarf(uint32_t *offset=NULL) const { + R dummy; + return compactSaysUseDwarf(dummy, offset); + } + +#if defined(_LIBUNWIND_TARGET_X86_64) + bool compactSaysUseDwarf(Registers_x86_64 &, uint32_t *offset) const { + if ((_info.format & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF) { + if (offset) + *offset = (_info.format & UNWIND_X86_64_DWARF_SECTION_OFFSET); + return true; + } + return false; + } +#endif + +#if defined(_LIBUNWIND_TARGET_I386) + bool compactSaysUseDwarf(Registers_x86 &, uint32_t *offset) const { + if ((_info.format & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF) { + if (offset) + *offset = (_info.format & UNWIND_X86_DWARF_SECTION_OFFSET); + return true; + } + return false; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC) + bool compactSaysUseDwarf(Registers_ppc &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC64) + bool compactSaysUseDwarf(Registers_ppc64 &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_AARCH64) + bool compactSaysUseDwarf(Registers_arm64 &, uint32_t *offset) const { + if ((_info.format & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_DWARF) { + if (offset) + *offset = (_info.format & UNWIND_ARM64_DWARF_SECTION_OFFSET); + return true; + } + return false; + } +#endif + +#if defined(_LIBUNWIND_TARGET_MIPS_O32) + bool compactSaysUseDwarf(Registers_mips_o32 &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_MIPS_NEWABI) + bool compactSaysUseDwarf(Registers_mips_newabi &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC) + bool compactSaysUseDwarf(Registers_sparc &, uint32_t *) const { return true; } +#endif + +#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + compact_unwind_encoding_t dwarfEncoding() const { + R dummy; + return dwarfEncoding(dummy); + } + +#if defined(_LIBUNWIND_TARGET_X86_64) + compact_unwind_encoding_t dwarfEncoding(Registers_x86_64 &) const { + return UNWIND_X86_64_MODE_DWARF; + } +#endif + +#if defined(_LIBUNWIND_TARGET_I386) + compact_unwind_encoding_t dwarfEncoding(Registers_x86 &) const { + return UNWIND_X86_MODE_DWARF; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC) + compact_unwind_encoding_t dwarfEncoding(Registers_ppc &) const { + return 0; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC64) + compact_unwind_encoding_t dwarfEncoding(Registers_ppc64 &) const { + return 0; + } +#endif + +#if defined(_LIBUNWIND_TARGET_AARCH64) + compact_unwind_encoding_t dwarfEncoding(Registers_arm64 &) const { + return UNWIND_ARM64_MODE_DWARF; + } +#endif + +#if defined(_LIBUNWIND_TARGET_ARM) + compact_unwind_encoding_t dwarfEncoding(Registers_arm &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_OR1K) + compact_unwind_encoding_t dwarfEncoding(Registers_or1k &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_MIPS_O32) + compact_unwind_encoding_t dwarfEncoding(Registers_mips_o32 &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_MIPS_NEWABI) + compact_unwind_encoding_t dwarfEncoding(Registers_mips_newabi &) const { + return 0; + } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC) + compact_unwind_encoding_t dwarfEncoding(Registers_sparc &) const { return 0; } +#endif + +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + // For runtime environments using SEH unwind data without Windows runtime + // support. + pint_t getLastPC() const { /* FIXME: Implement */ return 0; } + void setLastPC(pint_t pc) { /* FIXME: Implement */ } + RUNTIME_FUNCTION *lookUpSEHUnwindInfo(pint_t pc, pint_t *base) { + /* FIXME: Implement */ + *base = 0; + return nullptr; + } + bool getInfoFromSEH(pint_t pc); + int stepWithSEHData() { /* FIXME: Implement */ return 0; } +#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + + + A &_addressSpace; + R _registers; + unw_proc_info_t _info; + bool _unwindInfoMissing; + bool _isSignalFrame; +}; + +template +UnwindCursor::UnwindCursor(A &as) + : _addressSpace(as) + , _unwindInfoMissing(false) + , _isSignalFrame(false) { + memset(&_info, 0, sizeof(_info)); +} + +template +UnwindCursor::UnwindCursor(unw_context_t *context, A &as) + : _addressSpace(as), _registers(context), _unwindInfoMissing(false), + _isSignalFrame(false) { + static_assert((check_fit, unw_cursor_t>::does_fit), + "UnwindCursor<> does not fit in unw_cursor_t"); + memset(&_info, 0, sizeof(_info)); +} + +template +UnwindCursor::UnwindCursor(A &as, void *arg) + : _addressSpace(as),_registers(arg), _unwindInfoMissing(false), + _isSignalFrame(false) { + memset(&_info, 0, sizeof(_info)); + + // FIXME + // fill in _registers from thread arg +} + + +template +bool UnwindCursor::validReg(int regNum) { + return _registers.validRegister(regNum); +} + +template +unw_word_t UnwindCursor::getReg(int regNum) { + return _registers.getRegister(regNum); +} + +template +void UnwindCursor::setReg(int regNum, unw_word_t value, unw_word_t location) { + _registers.setRegister(regNum, (typename A::pint_t)value, (typename A::pint_t)location); +} + +template +unw_word_t UnwindCursor::getRegLocation(int regNum) { + return _registers.getRegisterLocation(regNum); +} + +template +bool UnwindCursor::validFloatReg(int regNum) { + return _registers.validFloatRegister(regNum); +} + +template +unw_fpreg_t UnwindCursor::getFloatReg(int regNum) { + return _registers.getFloatRegister(regNum); +} + +template +void UnwindCursor::setFloatReg(int regNum, unw_fpreg_t value) { + _registers.setFloatRegister(regNum, value); +} + +template void UnwindCursor::jumpto() { + _registers.jumpto(); +} + +#ifdef __arm__ +template void UnwindCursor::saveVFPAsX() { + _registers.saveVFPAsX(); +} +#endif + +template +const char *UnwindCursor::getRegisterName(int regNum) { + return _registers.getRegisterName(regNum); +} + +template bool UnwindCursor::isSignalFrame() { + return _isSignalFrame; +} + +#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + +#if defined(_LIBUNWIND_ARM_EHABI) +struct EHABIIndexEntry { + uint32_t functionOffset; + uint32_t data; +}; + +template +struct EHABISectionIterator { + typedef EHABISectionIterator _Self; + + typedef typename A::pint_t value_type; + typedef typename A::pint_t* pointer; + typedef typename A::pint_t& reference; + typedef size_t size_type; + typedef size_t difference_type; + + static _Self begin(A& addressSpace, const UnwindInfoSections& sects) { + return _Self(addressSpace, sects, 0); + } + static _Self end(A& addressSpace, const UnwindInfoSections& sects) { + return _Self(addressSpace, sects, + sects.arm_section_length / sizeof(EHABIIndexEntry)); + } + + EHABISectionIterator(A& addressSpace, const UnwindInfoSections& sects, size_t i) + : _i(i), _addressSpace(&addressSpace), _sects(§s) {} + + _Self& operator++() { ++_i; return *this; } + _Self& operator+=(size_t a) { _i += a; return *this; } + _Self& operator--() { assert(_i > 0); --_i; return *this; } + _Self& operator-=(size_t a) { assert(_i >= a); _i -= a; return *this; } + + _Self operator+(size_t a) { _Self out = *this; out._i += a; return out; } + _Self operator-(size_t a) { assert(_i >= a); _Self out = *this; out._i -= a; return out; } + + size_t operator-(const _Self& other) { return _i - other._i; } + + bool operator==(const _Self& other) const { + assert(_addressSpace == other._addressSpace); + assert(_sects == other._sects); + return _i == other._i; + } + + typename A::pint_t operator*() const { return functionAddress(); } + + typename A::pint_t functionAddress() const { + typename A::pint_t indexAddr = _sects->arm_section + arrayoffsetof( + EHABIIndexEntry, _i, functionOffset); + return indexAddr + signExtendPrel31(_addressSpace->get32(indexAddr)); + } + + typename A::pint_t dataAddress() { + typename A::pint_t indexAddr = _sects->arm_section + arrayoffsetof( + EHABIIndexEntry, _i, data); + return indexAddr; + } + + private: + size_t _i; + A* _addressSpace; + const UnwindInfoSections* _sects; +}; + +namespace { + +template +EHABISectionIterator EHABISectionUpperBound( + EHABISectionIterator first, + EHABISectionIterator last, + typename A::pint_t value) { + size_t len = last - first; + while (len > 0) { + size_t l2 = len / 2; + EHABISectionIterator m = first + l2; + if (value < *m) { + len = l2; + } else { + first = ++m; + len -= l2 + 1; + } + } + return first; +} + +} + +template +bool UnwindCursor::getInfoFromEHABISection( + pint_t pc, + const UnwindInfoSections §s) { + EHABISectionIterator begin = + EHABISectionIterator::begin(_addressSpace, sects); + EHABISectionIterator end = + EHABISectionIterator::end(_addressSpace, sects); + if (begin == end) + return false; + + EHABISectionIterator itNextPC = EHABISectionUpperBound(begin, end, pc); + if (itNextPC == begin) + return false; + EHABISectionIterator itThisPC = itNextPC - 1; + + pint_t thisPC = itThisPC.functionAddress(); + // If an exception is thrown from a function, corresponding to the last entry + // in the table, we don't really know the function extent and have to choose a + // value for nextPC. Choosing max() will allow the range check during trace to + // succeed. + pint_t nextPC = (itNextPC == end) ? UINTPTR_MAX : itNextPC.functionAddress(); + pint_t indexDataAddr = itThisPC.dataAddress(); + + if (indexDataAddr == 0) + return false; + + uint32_t indexData = _addressSpace.get32(indexDataAddr); + if (indexData == UNW_EXIDX_CANTUNWIND) + return false; + + // If the high bit is set, the exception handling table entry is inline inside + // the index table entry on the second word (aka |indexDataAddr|). Otherwise, + // the table points at an offset in the exception handling table (section 5 EHABI). + pint_t exceptionTableAddr; + uint32_t exceptionTableData; + bool isSingleWordEHT; + if (indexData & 0x80000000) { + exceptionTableAddr = indexDataAddr; + // TODO(ajwong): Should this data be 0? + exceptionTableData = indexData; + isSingleWordEHT = true; + } else { + exceptionTableAddr = indexDataAddr + signExtendPrel31(indexData); + exceptionTableData = _addressSpace.get32(exceptionTableAddr); + isSingleWordEHT = false; + } + + // Now we know the 3 things: + // exceptionTableAddr -- exception handler table entry. + // exceptionTableData -- the data inside the first word of the eht entry. + // isSingleWordEHT -- whether the entry is in the index. + unw_word_t personalityRoutine = 0xbadf00d; + bool scope32 = false; + uintptr_t lsda; + + // If the high bit in the exception handling table entry is set, the entry is + // in compact form (section 6.3 EHABI). + if (exceptionTableData & 0x80000000) { + // Grab the index of the personality routine from the compact form. + uint32_t choice = (exceptionTableData & 0x0f000000) >> 24; + uint32_t extraWords = 0; + switch (choice) { + case 0: + personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr0; + extraWords = 0; + scope32 = false; + lsda = isSingleWordEHT ? 0 : (exceptionTableAddr + 4); + break; + case 1: + personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr1; + extraWords = (exceptionTableData & 0x00ff0000) >> 16; + scope32 = false; + lsda = exceptionTableAddr + (extraWords + 1) * 4; + break; + case 2: + personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr2; + extraWords = (exceptionTableData & 0x00ff0000) >> 16; + scope32 = true; + lsda = exceptionTableAddr + (extraWords + 1) * 4; + break; + default: + _LIBUNWIND_ABORT("unknown personality routine"); + return false; + } + + if (isSingleWordEHT) { + if (extraWords != 0) { + _LIBUNWIND_ABORT("index inlined table detected but pr function " + "requires extra words"); + return false; + } + } + } else { + pint_t personalityAddr = + exceptionTableAddr + signExtendPrel31(exceptionTableData); + personalityRoutine = personalityAddr; + + // ARM EHABI # 6.2, # 9.2 + // + // +---- ehtp + // v + // +--------------------------------------+ + // | +--------+--------+--------+-------+ | + // | |0| prel31 to personalityRoutine | | + // | +--------+--------+--------+-------+ | + // | | N | unwind opcodes | | <-- UnwindData + // | +--------+--------+--------+-------+ | + // | | Word 2 unwind opcodes | | + // | +--------+--------+--------+-------+ | + // | ... | + // | +--------+--------+--------+-------+ | + // | | Word N unwind opcodes | | + // | +--------+--------+--------+-------+ | + // | | LSDA | | <-- lsda + // | | ... | | + // | +--------+--------+--------+-------+ | + // +--------------------------------------+ + + uint32_t *UnwindData = reinterpret_cast(exceptionTableAddr) + 1; + uint32_t FirstDataWord = *UnwindData; + size_t N = ((FirstDataWord >> 24) & 0xff); + size_t NDataWords = N + 1; + lsda = reinterpret_cast(UnwindData + NDataWords); + } + + _info.start_ip = thisPC; + _info.end_ip = nextPC; + _info.handler = personalityRoutine; + _info.unwind_info = exceptionTableAddr; + _info.lsda = lsda; + // flags is pr_cache.additional. See EHABI #7.2 for definition of bit 0. + _info.flags = (isSingleWordEHT ? 1 : 0) | (scope32 ? 0x2 : 0); // Use enum? + + return true; +} +#endif + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +template +bool UnwindCursor::getInfoFromDwarfSection(pint_t pc, + const UnwindInfoSections §s, + uint32_t fdeSectionOffsetHint) { + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + bool foundFDE = false; + bool foundInCache = false; + +#ifndef _LIBUNWIND_USE_ONLY_DWARF_INDEX + // If compact encoding table gave offset into dwarf section, go directly there + if (fdeSectionOffsetHint != 0) { + foundFDE = CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, + (uint32_t)sects.dwarf_section_length, + sects.dwarf_section + fdeSectionOffsetHint, + &fdeInfo, &cieInfo); + } +#endif + +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + if (!foundFDE && (sects.dwarf_index_section != 0)) { + foundFDE = EHHeaderParser::findFDE( + _addressSpace, pc, sects.dwarf_index_section, + (uint32_t)sects.dwarf_index_section_length, &fdeInfo, &cieInfo); + } +#endif + +#ifndef _LIBUNWIND_USE_ONLY_DWARF_INDEX + if (!foundFDE) { + // otherwise, search cache of previously found FDEs. + pint_t cachedFDE = DwarfFDECache::findFDE(sects.dso_base, pc); + if (cachedFDE != 0) { + foundFDE = + CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, + (uint32_t)sects.dwarf_section_length, + cachedFDE, &fdeInfo, &cieInfo); + foundInCache = foundFDE; + } + } + if (!foundFDE) { + // Still not found, do full scan of __eh_frame section. + foundFDE = CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, + (uint32_t)sects.dwarf_section_length, 0, + &fdeInfo, &cieInfo); + } +#endif + if (foundFDE) { + typename CFI_Parser::PrologInfo prolog; + if (CFI_Parser::parseFDEInstructions(_addressSpace, fdeInfo, cieInfo, pc, + R::getArch(), &prolog)) { + // Save off parsed FDE info + _info.start_ip = fdeInfo.pcStart; + _info.end_ip = fdeInfo.pcEnd; + _info.lsda = fdeInfo.lsda; + _info.handler = cieInfo.personality; + _info.gp = prolog.spExtraArgSize; + _info.flags = 0; + _info.format = dwarfEncoding(); + _info.unwind_info = fdeInfo.fdeStart; + _info.unwind_info_size = (uint32_t)fdeInfo.fdeLength; + _info.extra = (unw_word_t) sects.dso_base; + + // Add to cache (to make next lookup faster) if we had no hint + // and there was no index. + if (!foundInCache && (fdeSectionOffsetHint == 0)) { + #if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + if (sects.dwarf_index_section == 0) + #endif + DwarfFDECache::add(sects.dso_base, fdeInfo.pcStart, fdeInfo.pcEnd, + fdeInfo.fdeStart); + } + return true; + } + } + //_LIBUNWIND_DEBUG_LOG("can't find/use FDE for pc=0x%llX", (uint64_t)pc); + return false; +} +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + + +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) +template +bool UnwindCursor::getInfoFromCompactEncodingSection(pint_t pc, + const UnwindInfoSections §s) { + const bool log = false; + if (log) + fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX, mh=0x%llX)\n", + (uint64_t)pc, (uint64_t)sects.dso_base); + + const UnwindSectionHeader sectionHeader(_addressSpace, + sects.compact_unwind_section); + if (sectionHeader.version() != UNWIND_SECTION_VERSION) + return false; + + // do a binary search of top level index to find page with unwind info + pint_t targetFunctionOffset = pc - sects.dso_base; + const UnwindSectionIndexArray topIndex(_addressSpace, + sects.compact_unwind_section + + sectionHeader.indexSectionOffset()); + uint32_t low = 0; + uint32_t high = sectionHeader.indexCount(); + uint32_t last = high - 1; + while (low < high) { + uint32_t mid = (low + high) / 2; + //if ( log ) fprintf(stderr, "\tmid=%d, low=%d, high=%d, *mid=0x%08X\n", + //mid, low, high, topIndex.functionOffset(mid)); + if (topIndex.functionOffset(mid) <= targetFunctionOffset) { + if ((mid == last) || + (topIndex.functionOffset(mid + 1) > targetFunctionOffset)) { + low = mid; + break; + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + const uint32_t firstLevelFunctionOffset = topIndex.functionOffset(low); + const uint32_t firstLevelNextPageFunctionOffset = + topIndex.functionOffset(low + 1); + const pint_t secondLevelAddr = + sects.compact_unwind_section + topIndex.secondLevelPagesSectionOffset(low); + const pint_t lsdaArrayStartAddr = + sects.compact_unwind_section + topIndex.lsdaIndexArraySectionOffset(low); + const pint_t lsdaArrayEndAddr = + sects.compact_unwind_section + topIndex.lsdaIndexArraySectionOffset(low+1); + if (log) + fprintf(stderr, "\tfirst level search for result index=%d " + "to secondLevelAddr=0x%llX\n", + low, (uint64_t) secondLevelAddr); + // do a binary search of second level page index + uint32_t encoding = 0; + pint_t funcStart = 0; + pint_t funcEnd = 0; + pint_t lsda = 0; + pint_t personality = 0; + uint32_t pageKind = _addressSpace.get32(secondLevelAddr); + if (pageKind == UNWIND_SECOND_LEVEL_REGULAR) { + // regular page + UnwindSectionRegularPageHeader pageHeader(_addressSpace, + secondLevelAddr); + UnwindSectionRegularArray pageIndex( + _addressSpace, secondLevelAddr + pageHeader.entryPageOffset()); + // binary search looks for entry with e where index[e].offset <= pc < + // index[e+1].offset + if (log) + fprintf(stderr, "\tbinary search for targetFunctionOffset=0x%08llX in " + "regular page starting at secondLevelAddr=0x%llX\n", + (uint64_t) targetFunctionOffset, (uint64_t) secondLevelAddr); + low = 0; + high = pageHeader.entryCount(); + while (low < high) { + uint32_t mid = (low + high) / 2; + if (pageIndex.functionOffset(mid) <= targetFunctionOffset) { + if (mid == (uint32_t)(pageHeader.entryCount() - 1)) { + // at end of table + low = mid; + funcEnd = firstLevelNextPageFunctionOffset + sects.dso_base; + break; + } else if (pageIndex.functionOffset(mid + 1) > targetFunctionOffset) { + // next is too big, so we found it + low = mid; + funcEnd = pageIndex.functionOffset(low + 1) + sects.dso_base; + break; + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + encoding = pageIndex.encoding(low); + funcStart = pageIndex.functionOffset(low) + sects.dso_base; + if (pc < funcStart) { + if (log) + fprintf( + stderr, + "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", + (uint64_t) pc, (uint64_t) funcStart, (uint64_t) funcEnd); + return false; + } + if (pc > funcEnd) { + if (log) + fprintf( + stderr, + "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", + (uint64_t) pc, (uint64_t) funcStart, (uint64_t) funcEnd); + return false; + } + } else if (pageKind == UNWIND_SECOND_LEVEL_COMPRESSED) { + // compressed page + UnwindSectionCompressedPageHeader pageHeader(_addressSpace, + secondLevelAddr); + UnwindSectionCompressedArray pageIndex( + _addressSpace, secondLevelAddr + pageHeader.entryPageOffset()); + const uint32_t targetFunctionPageOffset = + (uint32_t)(targetFunctionOffset - firstLevelFunctionOffset); + // binary search looks for entry with e where index[e].offset <= pc < + // index[e+1].offset + if (log) + fprintf(stderr, "\tbinary search of compressed page starting at " + "secondLevelAddr=0x%llX\n", + (uint64_t) secondLevelAddr); + low = 0; + last = pageHeader.entryCount() - 1; + high = pageHeader.entryCount(); + while (low < high) { + uint32_t mid = (low + high) / 2; + if (pageIndex.functionOffset(mid) <= targetFunctionPageOffset) { + if ((mid == last) || + (pageIndex.functionOffset(mid + 1) > targetFunctionPageOffset)) { + low = mid; + break; + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + funcStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset + + sects.dso_base; + if (low < last) + funcEnd = + pageIndex.functionOffset(low + 1) + firstLevelFunctionOffset + + sects.dso_base; + else + funcEnd = firstLevelNextPageFunctionOffset + sects.dso_base; + if (pc < funcStart) { + _LIBUNWIND_DEBUG_LOG("malformed __unwind_info, pc=0x%llX not in second " + "level compressed unwind table. funcStart=0x%llX", + (uint64_t) pc, (uint64_t) funcStart); + return false; + } + if (pc > funcEnd) { + _LIBUNWIND_DEBUG_LOG("malformed __unwind_info, pc=0x%llX not in second " + "level compressed unwind table. funcEnd=0x%llX", + (uint64_t) pc, (uint64_t) funcEnd); + return false; + } + uint16_t encodingIndex = pageIndex.encodingIndex(low); + if (encodingIndex < sectionHeader.commonEncodingsArrayCount()) { + // encoding is in common table in section header + encoding = _addressSpace.get32( + sects.compact_unwind_section + + sectionHeader.commonEncodingsArraySectionOffset() + + encodingIndex * sizeof(uint32_t)); + } else { + // encoding is in page specific table + uint16_t pageEncodingIndex = + encodingIndex - (uint16_t)sectionHeader.commonEncodingsArrayCount(); + encoding = _addressSpace.get32(secondLevelAddr + + pageHeader.encodingsPageOffset() + + pageEncodingIndex * sizeof(uint32_t)); + } + } else { + _LIBUNWIND_DEBUG_LOG("malformed __unwind_info at 0x%0llX bad second " + "level page", + (uint64_t) sects.compact_unwind_section); + return false; + } + + // look up LSDA, if encoding says function has one + if (encoding & UNWIND_HAS_LSDA) { + UnwindSectionLsdaArray lsdaIndex(_addressSpace, lsdaArrayStartAddr); + uint32_t funcStartOffset = (uint32_t)(funcStart - sects.dso_base); + low = 0; + high = (uint32_t)(lsdaArrayEndAddr - lsdaArrayStartAddr) / + sizeof(unwind_info_section_header_lsda_index_entry); + // binary search looks for entry with exact match for functionOffset + if (log) + fprintf(stderr, + "\tbinary search of lsda table for targetFunctionOffset=0x%08X\n", + funcStartOffset); + while (low < high) { + uint32_t mid = (low + high) / 2; + if (lsdaIndex.functionOffset(mid) == funcStartOffset) { + lsda = lsdaIndex.lsdaOffset(mid) + sects.dso_base; + break; + } else if (lsdaIndex.functionOffset(mid) < funcStartOffset) { + low = mid + 1; + } else { + high = mid; + } + } + if (lsda == 0) { + _LIBUNWIND_DEBUG_LOG("found encoding 0x%08X with HAS_LSDA bit set for " + "pc=0x%0llX, but lsda table has no entry", + encoding, (uint64_t) pc); + return false; + } + } + + // extact personality routine, if encoding says function has one + uint32_t personalityIndex = (encoding & UNWIND_PERSONALITY_MASK) >> + (__builtin_ctz(UNWIND_PERSONALITY_MASK)); + if (personalityIndex != 0) { + --personalityIndex; // change 1-based to zero-based index + if (personalityIndex > sectionHeader.personalityArrayCount()) { + _LIBUNWIND_DEBUG_LOG("found encoding 0x%08X with personality index %d, " + "but personality table has only %d entries", + encoding, personalityIndex, + sectionHeader.personalityArrayCount()); + return false; + } + int32_t personalityDelta = (int32_t)_addressSpace.get32( + sects.compact_unwind_section + + sectionHeader.personalityArraySectionOffset() + + personalityIndex * sizeof(uint32_t)); + pint_t personalityPointer = sects.dso_base + (pint_t)personalityDelta; + personality = _addressSpace.getP(personalityPointer); + if (log) + fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), " + "personalityDelta=0x%08X, personality=0x%08llX\n", + (uint64_t) pc, personalityDelta, (uint64_t) personality); + } + + if (log) + fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), " + "encoding=0x%08X, lsda=0x%08llX for funcStart=0x%llX\n", + (uint64_t) pc, encoding, (uint64_t) lsda, (uint64_t) funcStart); + _info.start_ip = funcStart; + _info.end_ip = funcEnd; + _info.lsda = lsda; + _info.handler = personality; + _info.gp = 0; + _info.flags = 0; + _info.format = encoding; + _info.unwind_info = 0; + _info.unwind_info_size = 0; + _info.extra = sects.dso_base; + return true; +} +#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) +template +bool UnwindCursor::getInfoFromSEH(pint_t pc) { + pint_t base; + RUNTIME_FUNCTION *unwindEntry = lookUpSEHUnwindInfo(pc, &base); + if (!unwindEntry) { + _LIBUNWIND_DEBUG_LOG("\tpc not in table, pc=0x%llX", (uint64_t) pc); + return false; + } + _info.gp = 0; + _info.flags = 0; + _info.format = 0; + _info.unwind_info_size = sizeof(RUNTIME_FUNCTION); + _info.unwind_info = reinterpret_cast(unwindEntry); + _info.extra = base; + _info.start_ip = base + unwindEntry->BeginAddress; +#ifdef _LIBUNWIND_TARGET_X86_64 + _info.end_ip = base + unwindEntry->EndAddress; + // Only fill in the handler and LSDA if they're stale. + if (pc != getLastPC()) { + UNWIND_INFO *xdata = reinterpret_cast(base + unwindEntry->UnwindData); + if (xdata->Flags & (UNW_FLAG_EHANDLER|UNW_FLAG_UHANDLER)) { + // The personality is given in the UNWIND_INFO itself. The LSDA immediately + // follows the UNWIND_INFO. (This follows how both Clang and MSVC emit + // these structures.) + // N.B. UNWIND_INFO structs are DWORD-aligned. + uint32_t lastcode = (xdata->CountOfCodes + 1) & ~1; + const uint32_t *handler = reinterpret_cast(&xdata->UnwindCodes[lastcode]); + _info.lsda = reinterpret_cast(handler+1); + if (*handler) { + _info.handler = reinterpret_cast(__libunwind_seh_personality); + } else + _info.handler = 0; + } else { + _info.lsda = 0; + _info.handler = 0; + } + } +#elif defined(_LIBUNWIND_TARGET_ARM) + _info.end_ip = _info.start_ip + unwindEntry->FunctionLength; + _info.lsda = 0; // FIXME + _info.handler = 0; // FIXME +#endif + setLastPC(pc); + return true; +} +#endif + + +template +void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { + pint_t pc = (pint_t)this->getReg(UNW_REG_IP); +#if defined(_LIBUNWIND_ARM_EHABI) + // Remove the thumb bit so the IP represents the actual instruction address. + // This matches the behaviour of _Unwind_GetIP on arm. + pc &= (pint_t)~0x1; +#endif + + // If the last line of a function is a "throw" the compiler sometimes + // emits no instructions after the call to __cxa_throw. This means + // the return address is actually the start of the next function. + // To disambiguate this, back up the pc when we know it is a return + // address. + if (isReturnAddress) + --pc; + + // Ask address space object to find unwind sections for this pc. + UnwindInfoSections sects; + if (_addressSpace.findUnwindSections(pc, sects)) { +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + // If there is a compact unwind encoding table, look there first. + if (sects.compact_unwind_section != 0) { + if (this->getInfoFromCompactEncodingSection(pc, sects)) { + #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + // Found info in table, done unless encoding says to use dwarf. + uint32_t dwarfOffset; + if ((sects.dwarf_section != 0) && compactSaysUseDwarf(&dwarfOffset)) { + if (this->getInfoFromDwarfSection(pc, sects, dwarfOffset)) { + // found info in dwarf, done + return; + } + } + #endif + // If unwind table has entry, but entry says there is no unwind info, + // record that we have no unwind info. + if (_info.format == 0) + _unwindInfoMissing = true; + return; + } + } +#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + // If there is SEH unwind info, look there next. + if (this->getInfoFromSEH(pc)) + return; +#endif + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + // If there is dwarf unwind info, look there next. +#if defined(_LIBUNWIND_USE_ONLY_DWARF_INDEX) + if (sects.dwarf_index_section != 0) { +#else + if (sects.dwarf_section != 0) { +#endif + if (this->getInfoFromDwarfSection(pc, sects)) { + // found info in dwarf, done + return; + } + } +#endif + +#if defined(_LIBUNWIND_ARM_EHABI) + // If there is ARM EHABI unwind info, look there next. + if (sects.arm_section != 0 && this->getInfoFromEHABISection(pc, sects)) + return; +#endif + } + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + // There is no static unwind info for this pc. Look to see if an FDE was + // dynamically registered for it. + pint_t cachedFDE = DwarfFDECache::findFDE(0, pc); + if (cachedFDE != 0) { + CFI_Parser::FDE_Info fdeInfo; + CFI_Parser::CIE_Info cieInfo; + const char *msg = CFI_Parser::decodeFDE(_addressSpace, + cachedFDE, &fdeInfo, &cieInfo); + if (msg == NULL) { + typename CFI_Parser::PrologInfo prolog; + if (CFI_Parser::parseFDEInstructions(_addressSpace, fdeInfo, cieInfo, + pc, R::getArch(), &prolog)) { + // save off parsed FDE info + _info.start_ip = fdeInfo.pcStart; + _info.end_ip = fdeInfo.pcEnd; + _info.lsda = fdeInfo.lsda; + _info.handler = cieInfo.personality; + _info.gp = prolog.spExtraArgSize; + // Some frameless functions need SP + // altered when resuming in function. + _info.flags = 0; + _info.format = dwarfEncoding(); + _info.unwind_info = fdeInfo.fdeStart; + _info.unwind_info_size = (uint32_t)fdeInfo.fdeLength; + _info.extra = 0; + return; + } + } + } + + // Lastly, ask AddressSpace object about platform specific ways to locate + // other FDEs. + pint_t fde; + if (_addressSpace.findOtherFDE(pc, fde)) { + CFI_Parser::FDE_Info fdeInfo; + CFI_Parser::CIE_Info cieInfo; + if (!CFI_Parser::decodeFDE(_addressSpace, fde, &fdeInfo, &cieInfo)) { + // Double check this FDE is for a function that includes the pc. + if ((fdeInfo.pcStart <= pc) && (pc < fdeInfo.pcEnd)) { + typename CFI_Parser::PrologInfo prolog; + if (CFI_Parser::parseFDEInstructions(_addressSpace, fdeInfo, cieInfo, + pc, R::getArch(), &prolog)) { + // save off parsed FDE info + _info.start_ip = fdeInfo.pcStart; + _info.end_ip = fdeInfo.pcEnd; + _info.lsda = fdeInfo.lsda; + _info.handler = cieInfo.personality; + _info.gp = prolog.spExtraArgSize; + _info.flags = 0; + _info.format = dwarfEncoding(); + _info.unwind_info = fdeInfo.fdeStart; + _info.unwind_info_size = (uint32_t)fdeInfo.fdeLength; + _info.extra = 0; + return; + } + } + } + } +#endif // #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + + // no unwind info, flag that we can't reliably unwind + _unwindInfoMissing = true; +} + +template +int UnwindCursor::step() { + // Bottom of stack is defined is when unwind info cannot be found. + if (_unwindInfoMissing) + return UNW_STEP_END; + + // Use unwinding info to modify register set as if function returned. + int result; +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + result = this->stepWithCompactEncoding(); +#elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + result = this->stepWithSEHData(); +#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + result = this->stepWithDwarfFDE(); +#elif defined(_LIBUNWIND_ARM_EHABI) + result = this->stepWithEHABI(); +#else + #error Need _LIBUNWIND_SUPPORT_COMPACT_UNWIND or \ + _LIBUNWIND_SUPPORT_SEH_UNWIND or \ + _LIBUNWIND_SUPPORT_DWARF_UNWIND or \ + _LIBUNWIND_ARM_EHABI +#endif + + // update info based on new PC + if (result == UNW_STEP_SUCCESS) { + this->setInfoBasedOnIPRegister(true); + if (_unwindInfoMissing) + return UNW_STEP_END; + } + + return result; +} + +template +void UnwindCursor::getInfo(unw_proc_info_t *info) { + *info = _info; +} + +template +bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, + unw_word_t *offset) { + return _addressSpace.findFunctionName((pint_t)this->getReg(UNW_REG_IP), + buf, bufLen, offset); +} + +} // namespace libunwind + +#endif // __UNWINDCURSOR_HPP__ diff --git a/src/coreclr/nativeaot/libunwind/src/UnwindLevel1-gcc-ext.c b/src/coreclr/nativeaot/libunwind/src/UnwindLevel1-gcc-ext.c new file mode 100644 index 00000000000000..63e4083a45794a --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/UnwindLevel1-gcc-ext.c @@ -0,0 +1,319 @@ +//===--------------------- UnwindLevel1-gcc-ext.c -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Implements gcc extensions to the C++ ABI Exception Handling Level 1. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "libunwind_ext.h" +#include "libunwind.h" +#include "Unwind-EHABI.h" +#include "unwind.h" + +#if defined(_LIBUNWIND_BUILD_ZERO_COST_APIS) + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) +#define private_1 private_[0] +#endif + +/// Called by __cxa_rethrow(). +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object) { +#if defined(_LIBUNWIND_ARM_EHABI) + _LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%ld", + (void *)exception_object, + (long)exception_object->unwinder_cache.reserved1); +#else + _LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%" PRIdPTR, + (void *)exception_object, + (intptr_t)exception_object->private_1); +#endif + +#if defined(_LIBUNWIND_ARM_EHABI) + // _Unwind_RaiseException on EHABI will always set the reserved1 field to 0, + // which is in the same position as private_1 below. + return _Unwind_RaiseException(exception_object); +#else + // If this is non-forced and a stopping place was found, then this is a + // re-throw. + // Call _Unwind_RaiseException() as if this was a new exception + if (exception_object->private_1 == 0) { + return _Unwind_RaiseException(exception_object); + // Will return if there is no catch clause, so that __cxa_rethrow can call + // std::terminate(). + } + + // Call through to _Unwind_Resume() which distiguishes between forced and + // regular exceptions. + _Unwind_Resume(exception_object); + _LIBUNWIND_ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException()" + " which unexpectedly returned"); +#endif +} + + +/// Called by personality handler during phase 2 to get base address for data +/// relative encodings. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetDataRelBase(struct _Unwind_Context *context) { + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)", (void *)context); + _LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented"); +} + + +/// Called by personality handler during phase 2 to get base address for text +/// relative encodings. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetTextRelBase(struct _Unwind_Context *context) { + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)", (void *)context); + _LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented"); +} + + +/// Scans unwind information to find the function that contains the +/// specified code address "pc". +_LIBUNWIND_EXPORT void *_Unwind_FindEnclosingFunction(void *pc) { + _LIBUNWIND_TRACE_API("_Unwind_FindEnclosingFunction(pc=%p)", pc); + // This is slow, but works. + // We create an unwind cursor then alter the IP to be pc + unw_cursor_t cursor; + unw_context_t uc; + unw_proc_info_t info; + __unw_getcontext(&uc); + __unw_init_local(&cursor, &uc); + __unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(intptr_t)pc); + if (__unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS) + return (void *)(intptr_t) info.start_ip; + else + return NULL; +} + +/// Walk every frame and call trace function at each one. If trace function +/// returns anything other than _URC_NO_REASON, then walk is terminated. +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { + unw_cursor_t cursor; + unw_context_t uc; + __unw_getcontext(&uc); + __unw_init_local(&cursor, &uc); + + _LIBUNWIND_TRACE_API("_Unwind_Backtrace(callback=%p)", + (void *)(uintptr_t)callback); + +#if defined(_LIBUNWIND_ARM_EHABI) + // Create a mock exception object for force unwinding. + _Unwind_Exception ex; + memset(&ex, '\0', sizeof(ex)); + ex.exception_class = 0x434C4E47554E5700; // CLNGUNW\0 +#endif + + // walk each frame + while (true) { + _Unwind_Reason_Code result; + +#if !defined(_LIBUNWIND_ARM_EHABI) + // ask libunwind to get next frame (skip over first frame which is + // _Unwind_Backtrace()) + if (__unw_step(&cursor) <= 0) { + _LIBUNWIND_TRACE_UNWINDING(" _backtrace: ended because cursor reached " + "bottom of stack, returning %d", + _URC_END_OF_STACK); + return _URC_END_OF_STACK; + } +#else + // Get the information for this frame. + unw_proc_info_t frameInfo; + if (__unw_get_proc_info(&cursor, &frameInfo) != UNW_ESUCCESS) { + return _URC_END_OF_STACK; + } + + // Update the pr_cache in the mock exception object. + const uint32_t* unwindInfo = (uint32_t *) frameInfo.unwind_info; + ex.pr_cache.fnstart = frameInfo.start_ip; + ex.pr_cache.ehtp = (_Unwind_EHT_Header *) unwindInfo; + ex.pr_cache.additional= frameInfo.flags; + + struct _Unwind_Context *context = (struct _Unwind_Context *)&cursor; + // Get and call the personality function to unwind the frame. + __personality_routine handler = (__personality_routine) frameInfo.handler; + if (handler == NULL) { + return _URC_END_OF_STACK; + } + if (handler(_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, &ex, context) != + _URC_CONTINUE_UNWIND) { + return _URC_END_OF_STACK; + } +#endif // defined(_LIBUNWIND_ARM_EHABI) + + // debugging + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionName[512]; + unw_proc_info_t frame; + unw_word_t offset; + __unw_get_proc_name(&cursor, functionName, 512, &offset); + __unw_get_proc_info(&cursor, &frame); + _LIBUNWIND_TRACE_UNWINDING( + " _backtrace: start_ip=0x%" PRIxPTR ", func=%s, lsda=0x%" PRIxPTR ", context=%p", + frame.start_ip, functionName, frame.lsda, + (void *)&cursor); + } + + // call trace function with this frame + result = (*callback)((struct _Unwind_Context *)(&cursor), ref); + if (result != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING( + " _backtrace: ended because callback returned %d", result); + return result; + } + } +} + + +/// Find DWARF unwind info for an address 'pc' in some function. +_LIBUNWIND_EXPORT const void *_Unwind_Find_FDE(const void *pc, + struct dwarf_eh_bases *bases) { + // This is slow, but works. + // We create an unwind cursor then alter the IP to be pc + unw_cursor_t cursor; + unw_context_t uc; + unw_proc_info_t info; + __unw_getcontext(&uc); + __unw_init_local(&cursor, &uc); + __unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(intptr_t)pc); + __unw_get_proc_info(&cursor, &info); + bases->tbase = (uintptr_t)info.extra; + bases->dbase = 0; // dbase not used on Mac OS X + bases->func = (uintptr_t)info.start_ip; + _LIBUNWIND_TRACE_API("_Unwind_Find_FDE(pc=%p) => %p", pc, + (void *)(intptr_t) info.unwind_info); + return (void *)(intptr_t) info.unwind_info; +} + +/// Returns the CFA (call frame area, or stack pointer at start of function) +/// for the current context. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_word_t result; + __unw_get_reg(cursor, UNW_REG_SP, &result); + _LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p) => 0x%" PRIxPTR, + (void *)context, result); + return (uintptr_t)result; +} + + +/// Called by personality handler during phase 2 to get instruction pointer. +/// ipBefore is a boolean that says if IP is already adjusted to be the call +/// site address. Normally IP is the return address. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context, + int *ipBefore) { + _LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p)", (void *)context); + *ipBefore = 0; + return _Unwind_GetIP(context); +} + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + +/// Called by programs with dynamic code generators that want +/// to register a dynamically generated FDE. +/// This function has existed on Mac OS X since 10.4, but +/// was broken until 10.6. +_LIBUNWIND_EXPORT void __register_frame(const void *fde) { + _LIBUNWIND_TRACE_API("__register_frame(%p)", fde); + __unw_add_dynamic_fde((unw_word_t)(uintptr_t)fde); +} + + +/// Called by programs with dynamic code generators that want +/// to unregister a dynamically generated FDE. +/// This function has existed on Mac OS X since 10.4, but +/// was broken until 10.6. +_LIBUNWIND_EXPORT void __deregister_frame(const void *fde) { + _LIBUNWIND_TRACE_API("__deregister_frame(%p)", fde); + __unw_remove_dynamic_fde((unw_word_t)(uintptr_t)fde); +} + + +// The following register/deregister functions are gcc extensions. +// They have existed on Mac OS X, but have never worked because Mac OS X +// before 10.6 used keymgr to track known FDEs, but these functions +// never got updated to use keymgr. +// For now, we implement these as do-nothing functions to keep any existing +// applications working. We also add the not in 10.6 symbol so that nwe +// application won't be able to use them. + +#if defined(_LIBUNWIND_SUPPORT_FRAME_APIS) +_LIBUNWIND_EXPORT void __register_frame_info_bases(const void *fde, void *ob, + void *tb, void *db) { + (void)fde; + (void)ob; + (void)tb; + (void)db; + _LIBUNWIND_TRACE_API("__register_frame_info_bases(%p,%p, %p, %p)", + fde, ob, tb, db); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void __register_frame_info(const void *fde, void *ob) { + (void)fde; + (void)ob; + _LIBUNWIND_TRACE_API("__register_frame_info(%p, %p)", fde, ob); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void __register_frame_info_table_bases(const void *fde, + void *ob, void *tb, + void *db) { + (void)fde; + (void)ob; + (void)tb; + (void)db; + _LIBUNWIND_TRACE_API("__register_frame_info_table_bases" + "(%p,%p, %p, %p)", fde, ob, tb, db); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void __register_frame_info_table(const void *fde, void *ob) { + (void)fde; + (void)ob; + _LIBUNWIND_TRACE_API("__register_frame_info_table(%p, %p)", fde, ob); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void __register_frame_table(const void *fde) { + (void)fde; + _LIBUNWIND_TRACE_API("__register_frame_table(%p)", fde); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void *__deregister_frame_info(const void *fde) { + (void)fde; + _LIBUNWIND_TRACE_API("__deregister_frame_info(%p)", fde); + // do nothing, this function never worked in Mac OS X + return NULL; +} + +_LIBUNWIND_EXPORT void *__deregister_frame_info_bases(const void *fde) { + (void)fde; + _LIBUNWIND_TRACE_API("__deregister_frame_info_bases(%p)", fde); + // do nothing, this function never worked in Mac OS X + return NULL; +} +#endif // defined(_LIBUNWIND_SUPPORT_FRAME_APIS) + +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + +#endif // defined(_LIBUNWIND_BUILD_ZERO_COST_APIS) diff --git a/src/coreclr/nativeaot/libunwind/src/UnwindLevel1.c b/src/coreclr/nativeaot/libunwind/src/UnwindLevel1.c new file mode 100644 index 00000000000000..bcb1a7fbec2a11 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/UnwindLevel1.c @@ -0,0 +1,515 @@ +//===------------------------- UnwindLevel1.c -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Implements C++ ABI Exception Handling Level 1 as documented at: +// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html +// using libunwind +// +//===----------------------------------------------------------------------===// + +// ARM EHABI does not specify _Unwind_{Get,Set}{GR,IP}(). Thus, we are +// defining inline functions to delegate the function calls to +// _Unwind_VRS_{Get,Set}(). However, some applications might declare the +// function protetype directly (instead of including ), thus we need +// to export these functions from libunwind.so as well. +#define _LIBUNWIND_UNWIND_LEVEL1_EXTERNAL_LINKAGE 1 + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "libunwind.h" +#include "libunwind_ext.h" +#include "unwind.h" + +#if !defined(_LIBUNWIND_ARM_EHABI) && !defined(__USING_SJLJ_EXCEPTIONS__) + +#ifndef _LIBUNWIND_SUPPORT_SEH_UNWIND + +static _Unwind_Reason_Code +unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) { + __unw_init_local(cursor, uc); + + // Walk each frame looking for a place to stop. + bool handlerNotFound = true; + while (handlerNotFound) { + // Ask libunwind to get next frame (skip over first which is + // _Unwind_RaiseException). + int stepResult = __unw_step(cursor); + if (stepResult == 0) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): __unw_step() reached " + "bottom => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_END_OF_STACK; + } else if (stepResult < 0) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): __unw_step failed => " + "_URC_FATAL_PHASE1_ERROR", + (void *)exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + + // See if frame has code to run (has personality routine). + unw_proc_info_t frameInfo; + unw_word_t sp; + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): __unw_get_proc_info " + "failed => _URC_FATAL_PHASE1_ERROR", + (void *)exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + unw_word_t pc; + __unw_get_reg(cursor, UNW_REG_IP, &pc); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): pc=0x%" PRIxPTR ", start_ip=0x%" PRIxPTR + ", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR "", + (void *)exception_object, pc, frameInfo.start_ip, functionName, + frameInfo.lsda, frameInfo.handler); + } + + // If there is a personality routine, ask it if it will want to stop at + // this frame. + if (frameInfo.handler != 0) { + __personality_routine p = + (__personality_routine)(uintptr_t)(frameInfo.handler); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): calling personality function %p", + (void *)exception_object, (void *)(uintptr_t)p); + _Unwind_Reason_Code personalityResult = + (*p)(1, _UA_SEARCH_PHASE, exception_object->exception_class, + exception_object, (struct _Unwind_Context *)(cursor)); + switch (personalityResult) { + case _URC_HANDLER_FOUND: + // found a catch clause or locals that need destructing in this frame + // stop search and remember stack pointer at the frame + handlerNotFound = false; + __unw_get_reg(cursor, UNW_REG_SP, &sp); + exception_object->private_2 = (uintptr_t)sp; + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND", + (void *)exception_object); + return _URC_NO_REASON; + + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND", + (void *)exception_object); + // continue unwinding + break; + + default: + // something went wrong + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR", + (void *)exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + } + } + return _URC_NO_REASON; +} + + +static _Unwind_Reason_Code +unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) { + __unw_init_local(cursor, uc); + + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)", + (void *)exception_object); + + // Walk each frame until we reach where search phase said to stop. + while (true) { + + // Ask libunwind to get next frame (skip over first which is + // _Unwind_RaiseException). + int stepResult = __unw_step(cursor); + if (stepResult == 0) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): __unw_step() reached " + "bottom => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_END_OF_STACK; + } else if (stepResult < 0) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): __unw_step failed => " + "_URC_FATAL_PHASE1_ERROR", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // Get info about this frame. + unw_word_t sp; + unw_proc_info_t frameInfo; + __unw_get_reg(cursor, UNW_REG_SP, &sp); + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): __unw_get_proc_info " + "failed => _URC_FATAL_PHASE1_ERROR", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): start_ip=0x%" PRIxPTR + ", func=%s, sp=0x%" PRIxPTR ", lsda=0x%" PRIxPTR + ", personality=0x%" PRIxPTR, + (void *)exception_object, frameInfo.start_ip, + functionName, sp, frameInfo.lsda, + frameInfo.handler); + } + + // If there is a personality routine, tell it we are unwinding. + if (frameInfo.handler != 0) { + __personality_routine p = + (__personality_routine)(uintptr_t)(frameInfo.handler); + _Unwind_Action action = _UA_CLEANUP_PHASE; + if (sp == exception_object->private_2) { + // Tell personality this was the frame it marked in phase 1. + action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME); + } + _Unwind_Reason_Code personalityResult = + (*p)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(cursor)); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + // Continue unwinding + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND", + (void *)exception_object); + if (sp == exception_object->private_2) { + // Phase 1 said we would stop at this frame, but we did not... + _LIBUNWIND_ABORT("during phase1 personality function said it would " + "stop here, but now in phase2 it did not stop here"); + } + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT", + (void *)exception_object); + // Personality routine says to transfer control to landing pad. + // We may get control back if landing pad calls _Unwind_Resume(). + if (_LIBUNWIND_TRACING_UNWINDING) { + unw_word_t pc; + __unw_get_reg(cursor, UNW_REG_IP, &pc); + __unw_get_reg(cursor, UNW_REG_SP, &sp); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering " + "user code with ip=0x%" PRIxPTR + ", sp=0x%" PRIxPTR, + (void *)exception_object, pc, sp); + } + __unw_resume(cursor); + // __unw_resume() only returns if there was an error. + return _URC_FATAL_PHASE2_ERROR; + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", + personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // Clean up phase did not resume at the frame that the search phase + // said it would... + return _URC_FATAL_PHASE2_ERROR; +} + +static _Unwind_Reason_Code +unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor, + _Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter) { + __unw_init_local(cursor, uc); + + // Walk each frame until we reach where search phase said to stop + while (__unw_step(cursor) > 0) { + + // Update info about this frame. + unw_proc_info_t frameInfo; + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step " + "failed => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIxPTR + ", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR, + (void *)exception_object, frameInfo.start_ip, functionName, + frameInfo.lsda, frameInfo.handler); + } + + // Call stop function at each frame. + _Unwind_Action action = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = + (*stop)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(cursor), stop_parameter); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stop function returned %d", + (void *)exception_object, stopResult); + if (stopResult != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stopped by stop function", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // If there is a personality routine, tell it we are unwinding. + if (frameInfo.handler != 0) { + __personality_routine p = + (__personality_routine)(intptr_t)(frameInfo.handler); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): calling personality function %p", + (void *)exception_object, (void *)(uintptr_t)p); + _Unwind_Reason_Code personalityResult = + (*p)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(cursor)); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned " + "_URC_CONTINUE_UNWIND", + (void *)exception_object); + // Destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned " + "_URC_INSTALL_CONTEXT", + (void *)exception_object); + // We may get control back if landing pad calls _Unwind_Resume(). + __unw_resume(cursor); + break; + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned %d, " + "_URC_FATAL_PHASE2_ERROR", + (void *)exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // Call stop function one last time and tell it we've reached the end + // of the stack. + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " + "function with _UA_END_OF_STACK", + (void *)exception_object); + _Unwind_Action lastAction = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(cursor), stop_parameter); + + // Clean up phase did not resume at the frame that the search phase said it + // would. + return _URC_FATAL_PHASE2_ERROR; +} + + +/// Called by __cxa_throw. Only returns if there is a fatal error. +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_RaiseException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)", + (void *)exception_object); + unw_context_t uc; + unw_cursor_t cursor; + __unw_getcontext(&uc); + + // Mark that this is a non-forced unwind, so _Unwind_Resume() + // can do the right thing. + exception_object->private_1 = 0; + exception_object->private_2 = 0; + + // phase 1: the search phase + _Unwind_Reason_Code phase1 = unwind_phase1(&uc, &cursor, exception_object); + if (phase1 != _URC_NO_REASON) + return phase1; + + // phase 2: the clean up phase + return unwind_phase2(&uc, &cursor, exception_object); +} + + + +/// When _Unwind_RaiseException() is in phase2, it hands control +/// to the personality function at each frame. The personality +/// may force a jump to a landing pad in that function, the landing +/// pad code may then call _Unwind_Resume() to continue with the +/// unwinding. Note: the call to _Unwind_Resume() is from compiler +/// geneated user code. All other _Unwind_* routines are called +/// by the C++ runtime __cxa_* routines. +/// +/// Note: re-throwing an exception (as opposed to continuing the unwind) +/// is implemented by having the code call __cxa_rethrow() which +/// in turn calls _Unwind_Resume_or_Rethrow(). +_LIBUNWIND_EXPORT void +_Unwind_Resume(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", (void *)exception_object); + unw_context_t uc; + unw_cursor_t cursor; + __unw_getcontext(&uc); + + if (exception_object->private_1 != 0) + unwind_phase2_forced(&uc, &cursor, exception_object, + (_Unwind_Stop_Fn) exception_object->private_1, + (void *)exception_object->private_2); + else + unwind_phase2(&uc, &cursor, exception_object); + + // Clients assume _Unwind_Resume() does not return, so all we can do is abort. + _LIBUNWIND_ABORT("_Unwind_Resume() can't return"); +} + + + +/// Not used by C++. +/// Unwinds stack, calling "stop" function at each frame. +/// Could be used to implement longjmp(). +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_ForcedUnwind(_Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter) { + _LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)", + (void *)exception_object, (void *)(uintptr_t)stop); + unw_context_t uc; + unw_cursor_t cursor; + __unw_getcontext(&uc); + + // Mark that this is a forced unwind, so _Unwind_Resume() can do + // the right thing. + exception_object->private_1 = (uintptr_t) stop; + exception_object->private_2 = (uintptr_t) stop_parameter; + + // do it + return unwind_phase2_forced(&uc, &cursor, exception_object, stop, stop_parameter); +} + + +/// Called by personality handler during phase 2 to get LSDA for current frame. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) + result = (uintptr_t)frameInfo.lsda; + _LIBUNWIND_TRACE_API( + "_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR, + (void *)context, result); + if (result != 0) { + if (*((uint8_t *)result) != 0xFF) + _LIBUNWIND_DEBUG_LOG("lsda at 0x%" PRIxPTR " does not start with 0xFF", + result); + } + return result; +} + + +/// Called by personality handler during phase 2 to find the start of the +/// function. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetRegionStart(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) + result = (uintptr_t)frameInfo.start_ip; + _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIxPTR, + (void *)context, result); + return result; +} + +#endif // !_LIBUNWIND_SUPPORT_SEH_UNWIND + +/// Called by personality handler during phase 2 if a foreign exception +// is caught. +_LIBUNWIND_EXPORT void +_Unwind_DeleteException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)", + (void *)exception_object); + if (exception_object->exception_cleanup != NULL) + (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, + exception_object); +} + +/// Called by personality handler during phase 2 to get register values. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetGR(struct _Unwind_Context *context, int index) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_word_t result; + __unw_get_reg(cursor, index, &result); + _LIBUNWIND_TRACE_API("_Unwind_GetGR(context=%p, reg=%d) => 0x%" PRIxPTR, + (void *)context, index, result); + return (uintptr_t)result; +} + +/// Called by personality handler during phase 2 to alter register values. +_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index, + uintptr_t value) { + _LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0" PRIxPTR + ")", + (void *)context, index, value); + unw_cursor_t *cursor = (unw_cursor_t *)context; + __unw_set_reg(cursor, index, value); +} + +/// Called by personality handler during phase 2 to get instruction pointer. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_word_t result; + __unw_get_reg(cursor, UNW_REG_IP, &result); + _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIxPTR, + (void *)context, result); + return (uintptr_t)result; +} + +/// Called by personality handler during phase 2 to alter instruction pointer, +/// such as setting where the landing pad is, so _Unwind_Resume() will +/// start executing in the landing pad. +_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context, + uintptr_t value) { + _LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%0" PRIxPTR ")", + (void *)context, value); + unw_cursor_t *cursor = (unw_cursor_t *)context; + __unw_set_reg(cursor, UNW_REG_IP, value); +} + +#endif // !defined(_LIBUNWIND_ARM_EHABI) && !defined(__USING_SJLJ_EXCEPTIONS__) diff --git a/src/coreclr/nativeaot/libunwind/src/UnwindRegistersRestore.S b/src/coreclr/nativeaot/libunwind/src/UnwindRegistersRestore.S new file mode 100644 index 00000000000000..01113565e8e003 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/UnwindRegistersRestore.S @@ -0,0 +1,1032 @@ +//===-------------------- UnwindRegistersRestore.S ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "assembly.h" + + .text + +#if !defined(__USING_SJLJ_EXCEPTIONS__) + +#if defined(__i386__) +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_x866jumptoEv) +# +# void libunwind::Registers_x86::jumpto() +# +#if defined(_WIN32) +# On windows, the 'this' pointer is passed in ecx instead of on the stack + movl %ecx, %eax +#else +# On entry: +# + + +# +-----------------------+ +# + thread_state pointer + +# +-----------------------+ +# + return address + +# +-----------------------+ <-- SP +# + + + movl 4(%esp), %eax +#endif + # set up eax and ret on new stack location + movl 28(%eax), %edx # edx holds new stack pointer + subl $8,%edx + movl %edx, 28(%eax) + movl 0(%eax), %ebx + movl %ebx, 0(%edx) + movl 40(%eax), %ebx + movl %ebx, 4(%edx) + # we now have ret and eax pushed onto where new stack will be + # restore all registers + movl 4(%eax), %ebx + movl 8(%eax), %ecx + movl 12(%eax), %edx + movl 16(%eax), %edi + movl 20(%eax), %esi + movl 24(%eax), %ebp + movl 28(%eax), %esp + # skip ss + # skip eflags + pop %eax # eax was already pushed on new stack + ret # eip was already pushed on new stack + # skip cs + # skip ds + # skip es + # skip fs + # skip gs + +#elif defined(__x86_64__) + +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind16Registers_x86_646jumptoEv) +# +# void libunwind::Registers_x86_64::jumpto() +# +#if defined(_WIN64) +# On entry, thread_state pointer is in rcx; move it into rdi +# to share restore code below. Since this routine restores and +# overwrites all registers, we can use the same registers for +# pointers and temporaries as on unix even though win64 normally +# mustn't clobber some of them. + movq %rcx, %rdi +#else +# On entry, thread_state pointer is in rdi +#endif + + movq 56(%rdi), %rax # rax holds new stack pointer + subq $16, %rax + movq %rax, 56(%rdi) + movq 32(%rdi), %rbx # store new rdi on new stack + movq %rbx, 0(%rax) + movq 128(%rdi), %rbx # store new rip on new stack + movq %rbx, 8(%rax) + # restore all registers + movq 0(%rdi), %rax + movq 8(%rdi), %rbx + movq 16(%rdi), %rcx + movq 24(%rdi), %rdx + # restore rdi later + movq 40(%rdi), %rsi + movq 48(%rdi), %rbp + # restore rsp later + movq 64(%rdi), %r8 + movq 72(%rdi), %r9 + movq 80(%rdi), %r10 + movq 88(%rdi), %r11 + movq 96(%rdi), %r12 + movq 104(%rdi), %r13 + movq 112(%rdi), %r14 + movq 120(%rdi), %r15 + # skip rflags + # skip cs + # skip fs + # skip gs + +#if defined(_WIN64) + movdqu 176(%rdi),%xmm0 + movdqu 192(%rdi),%xmm1 + movdqu 208(%rdi),%xmm2 + movdqu 224(%rdi),%xmm3 + movdqu 240(%rdi),%xmm4 + movdqu 256(%rdi),%xmm5 + movdqu 272(%rdi),%xmm6 + movdqu 288(%rdi),%xmm7 + movdqu 304(%rdi),%xmm8 + movdqu 320(%rdi),%xmm9 + movdqu 336(%rdi),%xmm10 + movdqu 352(%rdi),%xmm11 + movdqu 368(%rdi),%xmm12 + movdqu 384(%rdi),%xmm13 + movdqu 400(%rdi),%xmm14 + movdqu 416(%rdi),%xmm15 +#endif + movq 56(%rdi), %rsp # cut back rsp to new location + pop %rdi # rdi was saved here earlier + ret # rip was saved here + + +#elif defined(__powerpc64__) + +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_ppc646jumptoEv) +// +// void libunwind::Registers_ppc64::jumpto() +// +// On entry: +// thread_state pointer is in r3 +// + +// load register (GPR) +#define PPC64_LR(n) \ + ld %r##n, (8 * (n + 2))(%r3) + + // restore integral registers + // skip r0 for now + // skip r1 for now + PPC64_LR(2) + // skip r3 for now + // skip r4 for now + // skip r5 for now + PPC64_LR(6) + PPC64_LR(7) + PPC64_LR(8) + PPC64_LR(9) + PPC64_LR(10) + PPC64_LR(11) + PPC64_LR(12) + PPC64_LR(13) + PPC64_LR(14) + PPC64_LR(15) + PPC64_LR(16) + PPC64_LR(17) + PPC64_LR(18) + PPC64_LR(19) + PPC64_LR(20) + PPC64_LR(21) + PPC64_LR(22) + PPC64_LR(23) + PPC64_LR(24) + PPC64_LR(25) + PPC64_LR(26) + PPC64_LR(27) + PPC64_LR(28) + PPC64_LR(29) + PPC64_LR(30) + PPC64_LR(31) + +#ifdef PPC64_HAS_VMX + + // restore VS registers + // (note that this also restores floating point registers and V registers, + // because part of VS is mapped to these registers) + + addi %r4, %r3, PPC64_OFFS_FP + +// load VS register +#define PPC64_LVS(n) \ + lxvd2x %vs##n, 0, %r4 ;\ + addi %r4, %r4, 16 + + // restore the first 32 VS regs (and also all floating point regs) + PPC64_LVS(0) + PPC64_LVS(1) + PPC64_LVS(2) + PPC64_LVS(3) + PPC64_LVS(4) + PPC64_LVS(5) + PPC64_LVS(6) + PPC64_LVS(7) + PPC64_LVS(8) + PPC64_LVS(9) + PPC64_LVS(10) + PPC64_LVS(11) + PPC64_LVS(12) + PPC64_LVS(13) + PPC64_LVS(14) + PPC64_LVS(15) + PPC64_LVS(16) + PPC64_LVS(17) + PPC64_LVS(18) + PPC64_LVS(19) + PPC64_LVS(20) + PPC64_LVS(21) + PPC64_LVS(22) + PPC64_LVS(23) + PPC64_LVS(24) + PPC64_LVS(25) + PPC64_LVS(26) + PPC64_LVS(27) + PPC64_LVS(28) + PPC64_LVS(29) + PPC64_LVS(30) + PPC64_LVS(31) + + // use VRSAVE to conditionally restore the remaining VS regs, + // that are where the V regs are mapped + + ld %r5, PPC64_OFFS_VRSAVE(%r3) // test VRsave + cmpwi %r5, 0 + beq Lnovec + +// conditionally load VS +#define PPC64_CLVS_BOTTOM(n) \ + beq Ldone##n ;\ + addi %r4, %r3, PPC64_OFFS_FP + n * 16 ;\ + lxvd2x %vs##n, 0, %r4 ;\ +Ldone##n: + +#define PPC64_CLVSl(n) \ + andis. %r0, %r5, (1<<(47-n)) ;\ +PPC64_CLVS_BOTTOM(n) + +#define PPC64_CLVSh(n) \ + andi. %r0, %r5, (1<<(63-n)) ;\ +PPC64_CLVS_BOTTOM(n) + + PPC64_CLVSl(32) + PPC64_CLVSl(33) + PPC64_CLVSl(34) + PPC64_CLVSl(35) + PPC64_CLVSl(36) + PPC64_CLVSl(37) + PPC64_CLVSl(38) + PPC64_CLVSl(39) + PPC64_CLVSl(40) + PPC64_CLVSl(41) + PPC64_CLVSl(42) + PPC64_CLVSl(43) + PPC64_CLVSl(44) + PPC64_CLVSl(45) + PPC64_CLVSl(46) + PPC64_CLVSl(47) + PPC64_CLVSh(48) + PPC64_CLVSh(49) + PPC64_CLVSh(50) + PPC64_CLVSh(51) + PPC64_CLVSh(52) + PPC64_CLVSh(53) + PPC64_CLVSh(54) + PPC64_CLVSh(55) + PPC64_CLVSh(56) + PPC64_CLVSh(57) + PPC64_CLVSh(58) + PPC64_CLVSh(59) + PPC64_CLVSh(60) + PPC64_CLVSh(61) + PPC64_CLVSh(62) + PPC64_CLVSh(63) + +#else + +// load FP register +#define PPC64_LF(n) \ + lfd %f##n, (PPC64_OFFS_FP + n * 16)(%r3) + + // restore float registers + PPC64_LF(0) + PPC64_LF(1) + PPC64_LF(2) + PPC64_LF(3) + PPC64_LF(4) + PPC64_LF(5) + PPC64_LF(6) + PPC64_LF(7) + PPC64_LF(8) + PPC64_LF(9) + PPC64_LF(10) + PPC64_LF(11) + PPC64_LF(12) + PPC64_LF(13) + PPC64_LF(14) + PPC64_LF(15) + PPC64_LF(16) + PPC64_LF(17) + PPC64_LF(18) + PPC64_LF(19) + PPC64_LF(20) + PPC64_LF(21) + PPC64_LF(22) + PPC64_LF(23) + PPC64_LF(24) + PPC64_LF(25) + PPC64_LF(26) + PPC64_LF(27) + PPC64_LF(28) + PPC64_LF(29) + PPC64_LF(30) + PPC64_LF(31) + + // restore vector registers if any are in use + ld %r5, PPC64_OFFS_VRSAVE(%r3) // test VRsave + cmpwi %r5, 0 + beq Lnovec + + subi %r4, %r1, 16 + // r4 is now a 16-byte aligned pointer into the red zone + // the _vectorScalarRegisters may not be 16-byte aligned + // so copy via red zone temp buffer + +#define PPC64_CLV_UNALIGNED_BOTTOM(n) \ + beq Ldone##n ;\ + ld %r0, (PPC64_OFFS_V + n * 16)(%r3) ;\ + std %r0, 0(%r4) ;\ + ld %r0, (PPC64_OFFS_V + n * 16 + 8)(%r3) ;\ + std %r0, 8(%r4) ;\ + lvx %v##n, 0, %r4 ;\ +Ldone ## n: + +#define PPC64_CLV_UNALIGNEDl(n) \ + andis. %r0, %r5, (1<<(15-n)) ;\ +PPC64_CLV_UNALIGNED_BOTTOM(n) + +#define PPC64_CLV_UNALIGNEDh(n) \ + andi. %r0, %r5, (1<<(31-n)) ;\ +PPC64_CLV_UNALIGNED_BOTTOM(n) + + PPC64_CLV_UNALIGNEDl(0) + PPC64_CLV_UNALIGNEDl(1) + PPC64_CLV_UNALIGNEDl(2) + PPC64_CLV_UNALIGNEDl(3) + PPC64_CLV_UNALIGNEDl(4) + PPC64_CLV_UNALIGNEDl(5) + PPC64_CLV_UNALIGNEDl(6) + PPC64_CLV_UNALIGNEDl(7) + PPC64_CLV_UNALIGNEDl(8) + PPC64_CLV_UNALIGNEDl(9) + PPC64_CLV_UNALIGNEDl(10) + PPC64_CLV_UNALIGNEDl(11) + PPC64_CLV_UNALIGNEDl(12) + PPC64_CLV_UNALIGNEDl(13) + PPC64_CLV_UNALIGNEDl(14) + PPC64_CLV_UNALIGNEDl(15) + PPC64_CLV_UNALIGNEDh(16) + PPC64_CLV_UNALIGNEDh(17) + PPC64_CLV_UNALIGNEDh(18) + PPC64_CLV_UNALIGNEDh(19) + PPC64_CLV_UNALIGNEDh(20) + PPC64_CLV_UNALIGNEDh(21) + PPC64_CLV_UNALIGNEDh(22) + PPC64_CLV_UNALIGNEDh(23) + PPC64_CLV_UNALIGNEDh(24) + PPC64_CLV_UNALIGNEDh(25) + PPC64_CLV_UNALIGNEDh(26) + PPC64_CLV_UNALIGNEDh(27) + PPC64_CLV_UNALIGNEDh(28) + PPC64_CLV_UNALIGNEDh(29) + PPC64_CLV_UNALIGNEDh(30) + PPC64_CLV_UNALIGNEDh(31) + +#endif + +Lnovec: + ld %r0, PPC64_OFFS_CR(%r3) + mtcr %r0 + ld %r0, PPC64_OFFS_SRR0(%r3) + mtctr %r0 + + PPC64_LR(0) + PPC64_LR(5) + PPC64_LR(4) + PPC64_LR(1) + PPC64_LR(3) + bctr + +#elif defined(__ppc__) + +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_ppc6jumptoEv) +// +// void libunwind::Registers_ppc::jumpto() +// +// On entry: +// thread_state pointer is in r3 +// + + // restore integral registerrs + // skip r0 for now + // skip r1 for now + lwz %r2, 16(%r3) + // skip r3 for now + // skip r4 for now + // skip r5 for now + lwz %r6, 32(%r3) + lwz %r7, 36(%r3) + lwz %r8, 40(%r3) + lwz %r9, 44(%r3) + lwz %r10, 48(%r3) + lwz %r11, 52(%r3) + lwz %r12, 56(%r3) + lwz %r13, 60(%r3) + lwz %r14, 64(%r3) + lwz %r15, 68(%r3) + lwz %r16, 72(%r3) + lwz %r17, 76(%r3) + lwz %r18, 80(%r3) + lwz %r19, 84(%r3) + lwz %r20, 88(%r3) + lwz %r21, 92(%r3) + lwz %r22, 96(%r3) + lwz %r23,100(%r3) + lwz %r24,104(%r3) + lwz %r25,108(%r3) + lwz %r26,112(%r3) + lwz %r27,116(%r3) + lwz %r28,120(%r3) + lwz %r29,124(%r3) + lwz %r30,128(%r3) + lwz %r31,132(%r3) + + // restore float registers + lfd %f0, 160(%r3) + lfd %f1, 168(%r3) + lfd %f2, 176(%r3) + lfd %f3, 184(%r3) + lfd %f4, 192(%r3) + lfd %f5, 200(%r3) + lfd %f6, 208(%r3) + lfd %f7, 216(%r3) + lfd %f8, 224(%r3) + lfd %f9, 232(%r3) + lfd %f10,240(%r3) + lfd %f11,248(%r3) + lfd %f12,256(%r3) + lfd %f13,264(%r3) + lfd %f14,272(%r3) + lfd %f15,280(%r3) + lfd %f16,288(%r3) + lfd %f17,296(%r3) + lfd %f18,304(%r3) + lfd %f19,312(%r3) + lfd %f20,320(%r3) + lfd %f21,328(%r3) + lfd %f22,336(%r3) + lfd %f23,344(%r3) + lfd %f24,352(%r3) + lfd %f25,360(%r3) + lfd %f26,368(%r3) + lfd %f27,376(%r3) + lfd %f28,384(%r3) + lfd %f29,392(%r3) + lfd %f30,400(%r3) + lfd %f31,408(%r3) + + // restore vector registers if any are in use + lwz %r5, 156(%r3) // test VRsave + cmpwi %r5, 0 + beq Lnovec + + subi %r4, %r1, 16 + rlwinm %r4, %r4, 0, 0, 27 // mask low 4-bits + // r4 is now a 16-byte aligned pointer into the red zone + // the _vectorRegisters may not be 16-byte aligned so copy via red zone temp buffer + + +#define LOAD_VECTOR_UNALIGNEDl(_index) \ + andis. %r0, %r5, (1<<(15-_index)) SEPARATOR \ + beq Ldone ## _index SEPARATOR \ + lwz %r0, 424+_index*16(%r3) SEPARATOR \ + stw %r0, 0(%r4) SEPARATOR \ + lwz %r0, 424+_index*16+4(%r3) SEPARATOR \ + stw %r0, 4(%r4) SEPARATOR \ + lwz %r0, 424+_index*16+8(%r3) SEPARATOR \ + stw %r0, 8(%r4) SEPARATOR \ + lwz %r0, 424+_index*16+12(%r3) SEPARATOR \ + stw %r0, 12(%r4) SEPARATOR \ + lvx %v ## _index, 0, %r4 SEPARATOR \ + Ldone ## _index: + +#define LOAD_VECTOR_UNALIGNEDh(_index) \ + andi. %r0, %r5, (1<<(31-_index)) SEPARATOR \ + beq Ldone ## _index SEPARATOR \ + lwz %r0, 424+_index*16(%r3) SEPARATOR \ + stw %r0, 0(%r4) SEPARATOR \ + lwz %r0, 424+_index*16+4(%r3) SEPARATOR \ + stw %r0, 4(%r4) SEPARATOR \ + lwz %r0, 424+_index*16+8(%r3) SEPARATOR \ + stw %r0, 8(%r4) SEPARATOR \ + lwz %r0, 424+_index*16+12(%r3) SEPARATOR \ + stw %r0, 12(%r4) SEPARATOR \ + lvx %v ## _index, 0, %r4 SEPARATOR \ + Ldone ## _index: + + + LOAD_VECTOR_UNALIGNEDl(0) + LOAD_VECTOR_UNALIGNEDl(1) + LOAD_VECTOR_UNALIGNEDl(2) + LOAD_VECTOR_UNALIGNEDl(3) + LOAD_VECTOR_UNALIGNEDl(4) + LOAD_VECTOR_UNALIGNEDl(5) + LOAD_VECTOR_UNALIGNEDl(6) + LOAD_VECTOR_UNALIGNEDl(7) + LOAD_VECTOR_UNALIGNEDl(8) + LOAD_VECTOR_UNALIGNEDl(9) + LOAD_VECTOR_UNALIGNEDl(10) + LOAD_VECTOR_UNALIGNEDl(11) + LOAD_VECTOR_UNALIGNEDl(12) + LOAD_VECTOR_UNALIGNEDl(13) + LOAD_VECTOR_UNALIGNEDl(14) + LOAD_VECTOR_UNALIGNEDl(15) + LOAD_VECTOR_UNALIGNEDh(16) + LOAD_VECTOR_UNALIGNEDh(17) + LOAD_VECTOR_UNALIGNEDh(18) + LOAD_VECTOR_UNALIGNEDh(19) + LOAD_VECTOR_UNALIGNEDh(20) + LOAD_VECTOR_UNALIGNEDh(21) + LOAD_VECTOR_UNALIGNEDh(22) + LOAD_VECTOR_UNALIGNEDh(23) + LOAD_VECTOR_UNALIGNEDh(24) + LOAD_VECTOR_UNALIGNEDh(25) + LOAD_VECTOR_UNALIGNEDh(26) + LOAD_VECTOR_UNALIGNEDh(27) + LOAD_VECTOR_UNALIGNEDh(28) + LOAD_VECTOR_UNALIGNEDh(29) + LOAD_VECTOR_UNALIGNEDh(30) + LOAD_VECTOR_UNALIGNEDh(31) + +Lnovec: + lwz %r0, 136(%r3) // __cr + mtcr %r0 + lwz %r0, 148(%r3) // __ctr + mtctr %r0 + lwz %r0, 0(%r3) // __ssr0 + mtctr %r0 + lwz %r0, 8(%r3) // do r0 now + lwz %r5, 28(%r3) // do r5 now + lwz %r4, 24(%r3) // do r4 now + lwz %r1, 12(%r3) // do sp now + lwz %r3, 20(%r3) // do r3 last + bctr + +#elif defined(__arm64__) || defined(__aarch64__) + +// +// void libunwind::Registers_arm64::jumpto() +// +// On entry: +// thread_state pointer is in x0 +// + .p2align 2 +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_arm646jumptoEv) + // skip restore of x0,x1 for now + ldp x2, x3, [x0, #0x010] + ldp x4, x5, [x0, #0x020] + ldp x6, x7, [x0, #0x030] + ldp x8, x9, [x0, #0x040] + ldp x10,x11, [x0, #0x050] + ldp x12,x13, [x0, #0x060] + ldp x14,x15, [x0, #0x070] + ldp x16,x17, [x0, #0x080] + ldp x18,x19, [x0, #0x090] + ldp x20,x21, [x0, #0x0A0] + ldp x22,x23, [x0, #0x0B0] + ldp x24,x25, [x0, #0x0C0] + ldp x26,x27, [x0, #0x0D0] + ldp x28,x29, [x0, #0x0E0] + ldr x30, [x0, #0x100] // restore pc into lr + ldr x1, [x0, #0x0F8] + mov sp,x1 // restore sp + + ldp d0, d1, [x0, #0x110] + ldp d2, d3, [x0, #0x120] + ldp d4, d5, [x0, #0x130] + ldp d6, d7, [x0, #0x140] + ldp d8, d9, [x0, #0x150] + ldp d10,d11, [x0, #0x160] + ldp d12,d13, [x0, #0x170] + ldp d14,d15, [x0, #0x180] + ldp d16,d17, [x0, #0x190] + ldp d18,d19, [x0, #0x1A0] + ldp d20,d21, [x0, #0x1B0] + ldp d22,d23, [x0, #0x1C0] + ldp d24,d25, [x0, #0x1D0] + ldp d26,d27, [x0, #0x1E0] + ldp d28,d29, [x0, #0x1F0] + ldr d30, [x0, #0x200] + ldr d31, [x0, #0x208] + + ldp x0, x1, [x0, #0x000] // restore x0,x1 + ret x30 // jump to pc + +#elif defined(__arm__) && !defined(__APPLE__) + +#if !defined(__ARM_ARCH_ISA_ARM) +#if (__ARM_ARCH_ISA_THUMB == 2) + .syntax unified +#endif + .thumb +#endif + +@ +@ void libunwind::Registers_arm::restoreCoreAndJumpTo() +@ +@ On entry: +@ thread_state pointer is in r0 +@ + .p2align 2 +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm20restoreCoreAndJumpToEv) +#if !defined(__ARM_ARCH_ISA_ARM) && __ARM_ARCH_ISA_THUMB == 1 + @ r8-r11: ldm into r1-r4, then mov to r8-r11 + adds r0, #0x20 + ldm r0!, {r1-r4} + subs r0, #0x30 + mov r8, r1 + mov r9, r2 + mov r10, r3 + mov r11, r4 + @ r12 does not need loading, it it the intra-procedure-call scratch register + ldr r2, [r0, #0x34] + ldr r3, [r0, #0x3c] + mov sp, r2 + mov lr, r3 @ restore pc into lr + ldm r0, {r0-r7} +#else + @ Use lr as base so that r0 can be restored. + mov lr, r0 + @ 32bit thumb-2 restrictions for ldm: + @ . the sp (r13) cannot be in the list + @ . the pc (r15) and lr (r14) cannot both be in the list in an LDM instruction + ldm lr, {r0-r12} + ldr sp, [lr, #52] + ldr lr, [lr, #60] @ restore pc into lr +#endif + JMP(lr) + +@ +@ static void libunwind::Registers_arm::restoreVFPWithFLDMD(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .fpu vfpv3-d16 +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm19restoreVFPWithFLDMDEPv) + @ VFP and iwMMX instructions are only available when compiling with the flags + @ that enable them. We do not want to do that in the library (because we do not + @ want the compiler to generate instructions that access those) but this is + @ only accessed if the personality routine needs these registers. Use of + @ these registers implies they are, actually, available on the target, so + @ it's ok to execute. + @ So, generate the instruction using the corresponding coprocessor mnemonic. + vldmia r0, {d0-d15} + JMP(lr) + +@ +@ static void libunwind::Registers_arm::restoreVFPWithFLDMX(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .fpu vfpv3-d16 +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm19restoreVFPWithFLDMXEPv) + vldmia r0, {d0-d15} @ fldmiax is deprecated in ARMv7+ and now behaves like vldmia + JMP(lr) + +@ +@ static void libunwind::Registers_arm::restoreVFPv3(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .fpu vfpv3 +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm12restoreVFPv3EPv) + vldmia r0, {d16-d31} + JMP(lr) + +#if defined(__ARM_WMMX) + +@ +@ static void libunwind::Registers_arm::restoreiWMMX(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .arch armv5te +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm12restoreiWMMXEPv) + ldcl p1, cr0, [r0], #8 @ wldrd wR0, [r0], #8 + ldcl p1, cr1, [r0], #8 @ wldrd wR1, [r0], #8 + ldcl p1, cr2, [r0], #8 @ wldrd wR2, [r0], #8 + ldcl p1, cr3, [r0], #8 @ wldrd wR3, [r0], #8 + ldcl p1, cr4, [r0], #8 @ wldrd wR4, [r0], #8 + ldcl p1, cr5, [r0], #8 @ wldrd wR5, [r0], #8 + ldcl p1, cr6, [r0], #8 @ wldrd wR6, [r0], #8 + ldcl p1, cr7, [r0], #8 @ wldrd wR7, [r0], #8 + ldcl p1, cr8, [r0], #8 @ wldrd wR8, [r0], #8 + ldcl p1, cr9, [r0], #8 @ wldrd wR9, [r0], #8 + ldcl p1, cr10, [r0], #8 @ wldrd wR10, [r0], #8 + ldcl p1, cr11, [r0], #8 @ wldrd wR11, [r0], #8 + ldcl p1, cr12, [r0], #8 @ wldrd wR12, [r0], #8 + ldcl p1, cr13, [r0], #8 @ wldrd wR13, [r0], #8 + ldcl p1, cr14, [r0], #8 @ wldrd wR14, [r0], #8 + ldcl p1, cr15, [r0], #8 @ wldrd wR15, [r0], #8 + JMP(lr) + +@ +@ static void libunwind::Registers_arm::restoreiWMMXControl(unw_uint32_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .arch armv5te +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm19restoreiWMMXControlEPj) + ldc2 p1, cr8, [r0], #4 @ wldrw wCGR0, [r0], #4 + ldc2 p1, cr9, [r0], #4 @ wldrw wCGR1, [r0], #4 + ldc2 p1, cr10, [r0], #4 @ wldrw wCGR2, [r0], #4 + ldc2 p1, cr11, [r0], #4 @ wldrw wCGR3, [r0], #4 + JMP(lr) + +#endif + +#elif defined(__or1k__) + +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind14Registers_or1k6jumptoEv) +# +# void libunwind::Registers_or1k::jumpto() +# +# On entry: +# thread_state pointer is in r3 +# + + # restore integral registers + l.lwz r0, 0(r3) + l.lwz r1, 4(r3) + l.lwz r2, 8(r3) + # skip r3 for now + l.lwz r4, 16(r3) + l.lwz r5, 20(r3) + l.lwz r6, 24(r3) + l.lwz r7, 28(r3) + l.lwz r8, 32(r3) + # skip r9 + l.lwz r10, 40(r3) + l.lwz r11, 44(r3) + l.lwz r12, 48(r3) + l.lwz r13, 52(r3) + l.lwz r14, 56(r3) + l.lwz r15, 60(r3) + l.lwz r16, 64(r3) + l.lwz r17, 68(r3) + l.lwz r18, 72(r3) + l.lwz r19, 76(r3) + l.lwz r20, 80(r3) + l.lwz r21, 84(r3) + l.lwz r22, 88(r3) + l.lwz r23, 92(r3) + l.lwz r24, 96(r3) + l.lwz r25,100(r3) + l.lwz r26,104(r3) + l.lwz r27,108(r3) + l.lwz r28,112(r3) + l.lwz r29,116(r3) + l.lwz r30,120(r3) + l.lwz r31,124(r3) + + # at last, restore r3 + l.lwz r3, 12(r3) + + # load new pc into ra + l.lwz r9, 128(r3) + # jump to pc + l.jr r9 + l.nop + +#elif defined(__mips__) && defined(_ABIO32) && _MIPS_SIM == _ABIO32 + +// +// void libunwind::Registers_mips_o32::jumpto() +// +// On entry: +// thread state pointer is in a0 ($4) +// +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind18Registers_mips_o326jumptoEv) + .set push + .set noat + .set noreorder + .set nomacro +#ifdef __mips_hard_float +#if __mips_fpr != 64 + ldc1 $f0, (4 * 36 + 8 * 0)($4) + ldc1 $f2, (4 * 36 + 8 * 2)($4) + ldc1 $f4, (4 * 36 + 8 * 4)($4) + ldc1 $f6, (4 * 36 + 8 * 6)($4) + ldc1 $f8, (4 * 36 + 8 * 8)($4) + ldc1 $f10, (4 * 36 + 8 * 10)($4) + ldc1 $f12, (4 * 36 + 8 * 12)($4) + ldc1 $f14, (4 * 36 + 8 * 14)($4) + ldc1 $f16, (4 * 36 + 8 * 16)($4) + ldc1 $f18, (4 * 36 + 8 * 18)($4) + ldc1 $f20, (4 * 36 + 8 * 20)($4) + ldc1 $f22, (4 * 36 + 8 * 22)($4) + ldc1 $f24, (4 * 36 + 8 * 24)($4) + ldc1 $f26, (4 * 36 + 8 * 26)($4) + ldc1 $f28, (4 * 36 + 8 * 28)($4) + ldc1 $f30, (4 * 36 + 8 * 30)($4) +#else + ldc1 $f0, (4 * 36 + 8 * 0)($4) + ldc1 $f1, (4 * 36 + 8 * 1)($4) + ldc1 $f2, (4 * 36 + 8 * 2)($4) + ldc1 $f3, (4 * 36 + 8 * 3)($4) + ldc1 $f4, (4 * 36 + 8 * 4)($4) + ldc1 $f5, (4 * 36 + 8 * 5)($4) + ldc1 $f6, (4 * 36 + 8 * 6)($4) + ldc1 $f7, (4 * 36 + 8 * 7)($4) + ldc1 $f8, (4 * 36 + 8 * 8)($4) + ldc1 $f9, (4 * 36 + 8 * 9)($4) + ldc1 $f10, (4 * 36 + 8 * 10)($4) + ldc1 $f11, (4 * 36 + 8 * 11)($4) + ldc1 $f12, (4 * 36 + 8 * 12)($4) + ldc1 $f13, (4 * 36 + 8 * 13)($4) + ldc1 $f14, (4 * 36 + 8 * 14)($4) + ldc1 $f15, (4 * 36 + 8 * 15)($4) + ldc1 $f16, (4 * 36 + 8 * 16)($4) + ldc1 $f17, (4 * 36 + 8 * 17)($4) + ldc1 $f18, (4 * 36 + 8 * 18)($4) + ldc1 $f19, (4 * 36 + 8 * 19)($4) + ldc1 $f20, (4 * 36 + 8 * 20)($4) + ldc1 $f21, (4 * 36 + 8 * 21)($4) + ldc1 $f22, (4 * 36 + 8 * 22)($4) + ldc1 $f23, (4 * 36 + 8 * 23)($4) + ldc1 $f24, (4 * 36 + 8 * 24)($4) + ldc1 $f25, (4 * 36 + 8 * 25)($4) + ldc1 $f26, (4 * 36 + 8 * 26)($4) + ldc1 $f27, (4 * 36 + 8 * 27)($4) + ldc1 $f28, (4 * 36 + 8 * 28)($4) + ldc1 $f29, (4 * 36 + 8 * 29)($4) + ldc1 $f30, (4 * 36 + 8 * 30)($4) + ldc1 $f31, (4 * 36 + 8 * 31)($4) +#endif +#endif + // restore hi and lo + lw $8, (4 * 33)($4) + mthi $8 + lw $8, (4 * 34)($4) + mtlo $8 + // r0 is zero + lw $1, (4 * 1)($4) + lw $2, (4 * 2)($4) + lw $3, (4 * 3)($4) + // skip a0 for now + lw $5, (4 * 5)($4) + lw $6, (4 * 6)($4) + lw $7, (4 * 7)($4) + lw $8, (4 * 8)($4) + lw $9, (4 * 9)($4) + lw $10, (4 * 10)($4) + lw $11, (4 * 11)($4) + lw $12, (4 * 12)($4) + lw $13, (4 * 13)($4) + lw $14, (4 * 14)($4) + lw $15, (4 * 15)($4) + lw $16, (4 * 16)($4) + lw $17, (4 * 17)($4) + lw $18, (4 * 18)($4) + lw $19, (4 * 19)($4) + lw $20, (4 * 20)($4) + lw $21, (4 * 21)($4) + lw $22, (4 * 22)($4) + lw $23, (4 * 23)($4) + lw $24, (4 * 24)($4) + lw $25, (4 * 25)($4) + lw $26, (4 * 26)($4) + lw $27, (4 * 27)($4) + lw $28, (4 * 28)($4) + lw $29, (4 * 29)($4) + lw $30, (4 * 30)($4) + // load new pc into ra + lw $31, (4 * 32)($4) + // jump to ra, load a0 in the delay slot + jr $31 + lw $4, (4 * 4)($4) + .set pop + +#elif defined(__mips64) + +// +// void libunwind::Registers_mips_newabi::jumpto() +// +// On entry: +// thread state pointer is in a0 ($4) +// +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind21Registers_mips_newabi6jumptoEv) + .set push + .set noat + .set noreorder + .set nomacro +#ifdef __mips_hard_float + ldc1 $f0, (8 * 35)($4) + ldc1 $f1, (8 * 36)($4) + ldc1 $f2, (8 * 37)($4) + ldc1 $f3, (8 * 38)($4) + ldc1 $f4, (8 * 39)($4) + ldc1 $f5, (8 * 40)($4) + ldc1 $f6, (8 * 41)($4) + ldc1 $f7, (8 * 42)($4) + ldc1 $f8, (8 * 43)($4) + ldc1 $f9, (8 * 44)($4) + ldc1 $f10, (8 * 45)($4) + ldc1 $f11, (8 * 46)($4) + ldc1 $f12, (8 * 47)($4) + ldc1 $f13, (8 * 48)($4) + ldc1 $f14, (8 * 49)($4) + ldc1 $f15, (8 * 50)($4) + ldc1 $f16, (8 * 51)($4) + ldc1 $f17, (8 * 52)($4) + ldc1 $f18, (8 * 53)($4) + ldc1 $f19, (8 * 54)($4) + ldc1 $f20, (8 * 55)($4) + ldc1 $f21, (8 * 56)($4) + ldc1 $f22, (8 * 57)($4) + ldc1 $f23, (8 * 58)($4) + ldc1 $f24, (8 * 59)($4) + ldc1 $f25, (8 * 60)($4) + ldc1 $f26, (8 * 61)($4) + ldc1 $f27, (8 * 62)($4) + ldc1 $f28, (8 * 63)($4) + ldc1 $f29, (8 * 64)($4) + ldc1 $f30, (8 * 65)($4) + ldc1 $f31, (8 * 66)($4) +#endif + // restore hi and lo + ld $8, (8 * 33)($4) + mthi $8 + ld $8, (8 * 34)($4) + mtlo $8 + // r0 is zero + ld $1, (8 * 1)($4) + ld $2, (8 * 2)($4) + ld $3, (8 * 3)($4) + // skip a0 for now + ld $5, (8 * 5)($4) + ld $6, (8 * 6)($4) + ld $7, (8 * 7)($4) + ld $8, (8 * 8)($4) + ld $9, (8 * 9)($4) + ld $10, (8 * 10)($4) + ld $11, (8 * 11)($4) + ld $12, (8 * 12)($4) + ld $13, (8 * 13)($4) + ld $14, (8 * 14)($4) + ld $15, (8 * 15)($4) + ld $16, (8 * 16)($4) + ld $17, (8 * 17)($4) + ld $18, (8 * 18)($4) + ld $19, (8 * 19)($4) + ld $20, (8 * 20)($4) + ld $21, (8 * 21)($4) + ld $22, (8 * 22)($4) + ld $23, (8 * 23)($4) + ld $24, (8 * 24)($4) + ld $25, (8 * 25)($4) + ld $26, (8 * 26)($4) + ld $27, (8 * 27)($4) + ld $28, (8 * 28)($4) + ld $29, (8 * 29)($4) + ld $30, (8 * 30)($4) + // load new pc into ra + ld $31, (8 * 32)($4) + // jump to ra, load a0 in the delay slot + jr $31 + ld $4, (8 * 4)($4) + .set pop + +#elif defined(__sparc__) + +// +// void libunwind::Registers_sparc_o32::jumpto() +// +// On entry: +// thread_state pointer is in o0 +// +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_sparc6jumptoEv) + ta 3 + ldd [%o0 + 64], %l0 + ldd [%o0 + 72], %l2 + ldd [%o0 + 80], %l4 + ldd [%o0 + 88], %l6 + ldd [%o0 + 96], %i0 + ldd [%o0 + 104], %i2 + ldd [%o0 + 112], %i4 + ldd [%o0 + 120], %i6 + ld [%o0 + 60], %o7 + jmp %o7 + nop + +#endif + +#endif /* !defined(__USING_SJLJ_EXCEPTIONS__) */ + +NO_EXEC_STACK_DIRECTIVE + diff --git a/src/coreclr/nativeaot/libunwind/src/UnwindRegistersSave.S b/src/coreclr/nativeaot/libunwind/src/UnwindRegistersSave.S new file mode 100644 index 00000000000000..54505e53bac702 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/UnwindRegistersSave.S @@ -0,0 +1,983 @@ +//===------------------------ UnwindRegistersSave.S -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "assembly.h" + + .text + +#if !defined(__USING_SJLJ_EXCEPTIONS__) + +#if defined(__i386__) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# + + +# +-----------------------+ +# + thread_state pointer + +# +-----------------------+ +# + return address + +# +-----------------------+ <-- SP +# + + +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + push %eax + movl 8(%esp), %eax + movl %ebx, 4(%eax) + movl %ecx, 8(%eax) + movl %edx, 12(%eax) + movl %edi, 16(%eax) + movl %esi, 20(%eax) + movl %ebp, 24(%eax) + movl %esp, %edx + addl $8, %edx + movl %edx, 28(%eax) # store what sp was at call site as esp + # skip ss + # skip eflags + movl 4(%esp), %edx + movl %edx, 40(%eax) # store return address as eip + # skip cs + # skip ds + # skip es + # skip fs + # skip gs + movl (%esp), %edx + movl %edx, (%eax) # store original eax + popl %eax + xorl %eax, %eax # return UNW_ESUCCESS + ret + +#elif defined(__x86_64__) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in rdi +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) +#if defined(_WIN64) +#define PTR %rcx +#define TMP %rdx +#else +#define PTR %rdi +#define TMP %rsi +#endif + + movq %rax, (PTR) + movq %rbx, 8(PTR) + movq %rcx, 16(PTR) + movq %rdx, 24(PTR) + movq %rdi, 32(PTR) + movq %rsi, 40(PTR) + movq %rbp, 48(PTR) + movq %rsp, 56(PTR) + addq $8, 56(PTR) + movq %r8, 64(PTR) + movq %r9, 72(PTR) + movq %r10, 80(PTR) + movq %r11, 88(PTR) + movq %r12, 96(PTR) + movq %r13,104(PTR) + movq %r14,112(PTR) + movq %r15,120(PTR) + movq (%rsp),TMP + movq TMP,128(PTR) # store return address as rip + # skip rflags + # skip cs + # skip fs + # skip gs + +#if defined(_WIN64) + movdqu %xmm0,176(PTR) + movdqu %xmm1,192(PTR) + movdqu %xmm2,208(PTR) + movdqu %xmm3,224(PTR) + movdqu %xmm4,240(PTR) + movdqu %xmm5,256(PTR) + movdqu %xmm6,272(PTR) + movdqu %xmm7,288(PTR) + movdqu %xmm8,304(PTR) + movdqu %xmm9,320(PTR) + movdqu %xmm10,336(PTR) + movdqu %xmm11,352(PTR) + movdqu %xmm12,368(PTR) + movdqu %xmm13,384(PTR) + movdqu %xmm14,400(PTR) + movdqu %xmm15,416(PTR) +#endif + xorl %eax, %eax # return UNW_ESUCCESS + ret + +#elif defined(__mips__) && defined(_ABIO32) && _MIPS_SIM == _ABIO32 + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in a0 ($4) +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + .set push + .set noat + .set noreorder + .set nomacro + sw $1, (4 * 1)($4) + sw $2, (4 * 2)($4) + sw $3, (4 * 3)($4) + sw $4, (4 * 4)($4) + sw $5, (4 * 5)($4) + sw $6, (4 * 6)($4) + sw $7, (4 * 7)($4) + sw $8, (4 * 8)($4) + sw $9, (4 * 9)($4) + sw $10, (4 * 10)($4) + sw $11, (4 * 11)($4) + sw $12, (4 * 12)($4) + sw $13, (4 * 13)($4) + sw $14, (4 * 14)($4) + sw $15, (4 * 15)($4) + sw $16, (4 * 16)($4) + sw $17, (4 * 17)($4) + sw $18, (4 * 18)($4) + sw $19, (4 * 19)($4) + sw $20, (4 * 20)($4) + sw $21, (4 * 21)($4) + sw $22, (4 * 22)($4) + sw $23, (4 * 23)($4) + sw $24, (4 * 24)($4) + sw $25, (4 * 25)($4) + sw $26, (4 * 26)($4) + sw $27, (4 * 27)($4) + sw $28, (4 * 28)($4) + sw $29, (4 * 29)($4) + sw $30, (4 * 30)($4) + sw $31, (4 * 31)($4) + # Store return address to pc + sw $31, (4 * 32)($4) + # hi and lo + mfhi $8 + sw $8, (4 * 33)($4) + mflo $8 + sw $8, (4 * 34)($4) +#ifdef __mips_hard_float +#if __mips_fpr != 64 + sdc1 $f0, (4 * 36 + 8 * 0)($4) + sdc1 $f2, (4 * 36 + 8 * 2)($4) + sdc1 $f4, (4 * 36 + 8 * 4)($4) + sdc1 $f6, (4 * 36 + 8 * 6)($4) + sdc1 $f8, (4 * 36 + 8 * 8)($4) + sdc1 $f10, (4 * 36 + 8 * 10)($4) + sdc1 $f12, (4 * 36 + 8 * 12)($4) + sdc1 $f14, (4 * 36 + 8 * 14)($4) + sdc1 $f16, (4 * 36 + 8 * 16)($4) + sdc1 $f18, (4 * 36 + 8 * 18)($4) + sdc1 $f20, (4 * 36 + 8 * 20)($4) + sdc1 $f22, (4 * 36 + 8 * 22)($4) + sdc1 $f24, (4 * 36 + 8 * 24)($4) + sdc1 $f26, (4 * 36 + 8 * 26)($4) + sdc1 $f28, (4 * 36 + 8 * 28)($4) + sdc1 $f30, (4 * 36 + 8 * 30)($4) +#else + sdc1 $f0, (4 * 36 + 8 * 0)($4) + sdc1 $f1, (4 * 36 + 8 * 1)($4) + sdc1 $f2, (4 * 36 + 8 * 2)($4) + sdc1 $f3, (4 * 36 + 8 * 3)($4) + sdc1 $f4, (4 * 36 + 8 * 4)($4) + sdc1 $f5, (4 * 36 + 8 * 5)($4) + sdc1 $f6, (4 * 36 + 8 * 6)($4) + sdc1 $f7, (4 * 36 + 8 * 7)($4) + sdc1 $f8, (4 * 36 + 8 * 8)($4) + sdc1 $f9, (4 * 36 + 8 * 9)($4) + sdc1 $f10, (4 * 36 + 8 * 10)($4) + sdc1 $f11, (4 * 36 + 8 * 11)($4) + sdc1 $f12, (4 * 36 + 8 * 12)($4) + sdc1 $f13, (4 * 36 + 8 * 13)($4) + sdc1 $f14, (4 * 36 + 8 * 14)($4) + sdc1 $f15, (4 * 36 + 8 * 15)($4) + sdc1 $f16, (4 * 36 + 8 * 16)($4) + sdc1 $f17, (4 * 36 + 8 * 17)($4) + sdc1 $f18, (4 * 36 + 8 * 18)($4) + sdc1 $f19, (4 * 36 + 8 * 19)($4) + sdc1 $f20, (4 * 36 + 8 * 20)($4) + sdc1 $f21, (4 * 36 + 8 * 21)($4) + sdc1 $f22, (4 * 36 + 8 * 22)($4) + sdc1 $f23, (4 * 36 + 8 * 23)($4) + sdc1 $f24, (4 * 36 + 8 * 24)($4) + sdc1 $f25, (4 * 36 + 8 * 25)($4) + sdc1 $f26, (4 * 36 + 8 * 26)($4) + sdc1 $f27, (4 * 36 + 8 * 27)($4) + sdc1 $f28, (4 * 36 + 8 * 28)($4) + sdc1 $f29, (4 * 36 + 8 * 29)($4) + sdc1 $f30, (4 * 36 + 8 * 30)($4) + sdc1 $f31, (4 * 36 + 8 * 31)($4) +#endif +#endif + jr $31 + # return UNW_ESUCCESS + or $2, $0, $0 + .set pop + +#elif defined(__mips64) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in a0 ($4) +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + .set push + .set noat + .set noreorder + .set nomacro + sd $1, (8 * 1)($4) + sd $2, (8 * 2)($4) + sd $3, (8 * 3)($4) + sd $4, (8 * 4)($4) + sd $5, (8 * 5)($4) + sd $6, (8 * 6)($4) + sd $7, (8 * 7)($4) + sd $8, (8 * 8)($4) + sd $9, (8 * 9)($4) + sd $10, (8 * 10)($4) + sd $11, (8 * 11)($4) + sd $12, (8 * 12)($4) + sd $13, (8 * 13)($4) + sd $14, (8 * 14)($4) + sd $15, (8 * 15)($4) + sd $16, (8 * 16)($4) + sd $17, (8 * 17)($4) + sd $18, (8 * 18)($4) + sd $19, (8 * 19)($4) + sd $20, (8 * 20)($4) + sd $21, (8 * 21)($4) + sd $22, (8 * 22)($4) + sd $23, (8 * 23)($4) + sd $24, (8 * 24)($4) + sd $25, (8 * 25)($4) + sd $26, (8 * 26)($4) + sd $27, (8 * 27)($4) + sd $28, (8 * 28)($4) + sd $29, (8 * 29)($4) + sd $30, (8 * 30)($4) + sd $31, (8 * 31)($4) + # Store return address to pc + sd $31, (8 * 32)($4) + # hi and lo + mfhi $8 + sd $8, (8 * 33)($4) + mflo $8 + sd $8, (8 * 34)($4) +#ifdef __mips_hard_float + sdc1 $f0, (8 * 35)($4) + sdc1 $f1, (8 * 36)($4) + sdc1 $f2, (8 * 37)($4) + sdc1 $f3, (8 * 38)($4) + sdc1 $f4, (8 * 39)($4) + sdc1 $f5, (8 * 40)($4) + sdc1 $f6, (8 * 41)($4) + sdc1 $f7, (8 * 42)($4) + sdc1 $f8, (8 * 43)($4) + sdc1 $f9, (8 * 44)($4) + sdc1 $f10, (8 * 45)($4) + sdc1 $f11, (8 * 46)($4) + sdc1 $f12, (8 * 47)($4) + sdc1 $f13, (8 * 48)($4) + sdc1 $f14, (8 * 49)($4) + sdc1 $f15, (8 * 50)($4) + sdc1 $f16, (8 * 51)($4) + sdc1 $f17, (8 * 52)($4) + sdc1 $f18, (8 * 53)($4) + sdc1 $f19, (8 * 54)($4) + sdc1 $f20, (8 * 55)($4) + sdc1 $f21, (8 * 56)($4) + sdc1 $f22, (8 * 57)($4) + sdc1 $f23, (8 * 58)($4) + sdc1 $f24, (8 * 59)($4) + sdc1 $f25, (8 * 60)($4) + sdc1 $f26, (8 * 61)($4) + sdc1 $f27, (8 * 62)($4) + sdc1 $f28, (8 * 63)($4) + sdc1 $f29, (8 * 64)($4) + sdc1 $f30, (8 * 65)($4) + sdc1 $f31, (8 * 66)($4) +#endif + jr $31 + # return UNW_ESUCCESS + or $2, $0, $0 + .set pop + +# elif defined(__mips__) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# Just trap for the time being. +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + teq $0, $0 + +#elif defined(__powerpc64__) + +// +// extern int __unw_getcontext(unw_context_t* thread_state) +// +// On entry: +// thread_state pointer is in r3 +// +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + +// store register (GPR) +#define PPC64_STR(n) \ + std %r##n, (8 * (n + 2))(%r3) + + // save GPRs + PPC64_STR(0) + mflr %r0 + std %r0, PPC64_OFFS_SRR0(%r3) // store lr as ssr0 + PPC64_STR(1) + PPC64_STR(2) + PPC64_STR(3) + PPC64_STR(4) + PPC64_STR(5) + PPC64_STR(6) + PPC64_STR(7) + PPC64_STR(8) + PPC64_STR(9) + PPC64_STR(10) + PPC64_STR(11) + PPC64_STR(12) + PPC64_STR(13) + PPC64_STR(14) + PPC64_STR(15) + PPC64_STR(16) + PPC64_STR(17) + PPC64_STR(18) + PPC64_STR(19) + PPC64_STR(20) + PPC64_STR(21) + PPC64_STR(22) + PPC64_STR(23) + PPC64_STR(24) + PPC64_STR(25) + PPC64_STR(26) + PPC64_STR(27) + PPC64_STR(28) + PPC64_STR(29) + PPC64_STR(30) + PPC64_STR(31) + + mfcr %r0 + std %r0, PPC64_OFFS_CR(%r3) + mfxer %r0 + std %r0, PPC64_OFFS_XER(%r3) + mflr %r0 + std %r0, PPC64_OFFS_LR(%r3) + mfctr %r0 + std %r0, PPC64_OFFS_CTR(%r3) + mfvrsave %r0 + std %r0, PPC64_OFFS_VRSAVE(%r3) + +#ifdef PPC64_HAS_VMX + // save VS registers + // (note that this also saves floating point registers and V registers, + // because part of VS is mapped to these registers) + + addi %r4, %r3, PPC64_OFFS_FP + +// store VS register +#define PPC64_STVS(n) \ + stxvd2x %vs##n, 0, %r4 ;\ + addi %r4, %r4, 16 + + PPC64_STVS(0) + PPC64_STVS(1) + PPC64_STVS(2) + PPC64_STVS(3) + PPC64_STVS(4) + PPC64_STVS(5) + PPC64_STVS(6) + PPC64_STVS(7) + PPC64_STVS(8) + PPC64_STVS(9) + PPC64_STVS(10) + PPC64_STVS(11) + PPC64_STVS(12) + PPC64_STVS(13) + PPC64_STVS(14) + PPC64_STVS(15) + PPC64_STVS(16) + PPC64_STVS(17) + PPC64_STVS(18) + PPC64_STVS(19) + PPC64_STVS(20) + PPC64_STVS(21) + PPC64_STVS(22) + PPC64_STVS(23) + PPC64_STVS(24) + PPC64_STVS(25) + PPC64_STVS(26) + PPC64_STVS(27) + PPC64_STVS(28) + PPC64_STVS(29) + PPC64_STVS(30) + PPC64_STVS(31) + PPC64_STVS(32) + PPC64_STVS(33) + PPC64_STVS(34) + PPC64_STVS(35) + PPC64_STVS(36) + PPC64_STVS(37) + PPC64_STVS(38) + PPC64_STVS(39) + PPC64_STVS(40) + PPC64_STVS(41) + PPC64_STVS(42) + PPC64_STVS(43) + PPC64_STVS(44) + PPC64_STVS(45) + PPC64_STVS(46) + PPC64_STVS(47) + PPC64_STVS(48) + PPC64_STVS(49) + PPC64_STVS(50) + PPC64_STVS(51) + PPC64_STVS(52) + PPC64_STVS(53) + PPC64_STVS(54) + PPC64_STVS(55) + PPC64_STVS(56) + PPC64_STVS(57) + PPC64_STVS(58) + PPC64_STVS(59) + PPC64_STVS(60) + PPC64_STVS(61) + PPC64_STVS(62) + PPC64_STVS(63) + +#else + +// store FP register +#define PPC64_STF(n) \ + stfd %f##n, (PPC64_OFFS_FP + n * 16)(%r3) + + // save float registers + PPC64_STF(0) + PPC64_STF(1) + PPC64_STF(2) + PPC64_STF(3) + PPC64_STF(4) + PPC64_STF(5) + PPC64_STF(6) + PPC64_STF(7) + PPC64_STF(8) + PPC64_STF(9) + PPC64_STF(10) + PPC64_STF(11) + PPC64_STF(12) + PPC64_STF(13) + PPC64_STF(14) + PPC64_STF(15) + PPC64_STF(16) + PPC64_STF(17) + PPC64_STF(18) + PPC64_STF(19) + PPC64_STF(20) + PPC64_STF(21) + PPC64_STF(22) + PPC64_STF(23) + PPC64_STF(24) + PPC64_STF(25) + PPC64_STF(26) + PPC64_STF(27) + PPC64_STF(28) + PPC64_STF(29) + PPC64_STF(30) + PPC64_STF(31) + + // save vector registers + + // Use 16-bytes below the stack pointer as an + // aligned buffer to save each vector register. + // Note that the stack pointer is always 16-byte aligned. + subi %r4, %r1, 16 + +#define PPC64_STV_UNALIGNED(n) \ + stvx %v##n, 0, %r4 ;\ + ld %r5, 0(%r4) ;\ + std %r5, (PPC64_OFFS_V + n * 16)(%r3) ;\ + ld %r5, 8(%r4) ;\ + std %r5, (PPC64_OFFS_V + n * 16 + 8)(%r3) + + PPC64_STV_UNALIGNED(0) + PPC64_STV_UNALIGNED(1) + PPC64_STV_UNALIGNED(2) + PPC64_STV_UNALIGNED(3) + PPC64_STV_UNALIGNED(4) + PPC64_STV_UNALIGNED(5) + PPC64_STV_UNALIGNED(6) + PPC64_STV_UNALIGNED(7) + PPC64_STV_UNALIGNED(8) + PPC64_STV_UNALIGNED(9) + PPC64_STV_UNALIGNED(10) + PPC64_STV_UNALIGNED(11) + PPC64_STV_UNALIGNED(12) + PPC64_STV_UNALIGNED(13) + PPC64_STV_UNALIGNED(14) + PPC64_STV_UNALIGNED(15) + PPC64_STV_UNALIGNED(16) + PPC64_STV_UNALIGNED(17) + PPC64_STV_UNALIGNED(18) + PPC64_STV_UNALIGNED(19) + PPC64_STV_UNALIGNED(20) + PPC64_STV_UNALIGNED(21) + PPC64_STV_UNALIGNED(22) + PPC64_STV_UNALIGNED(23) + PPC64_STV_UNALIGNED(24) + PPC64_STV_UNALIGNED(25) + PPC64_STV_UNALIGNED(26) + PPC64_STV_UNALIGNED(27) + PPC64_STV_UNALIGNED(28) + PPC64_STV_UNALIGNED(29) + PPC64_STV_UNALIGNED(30) + PPC64_STV_UNALIGNED(31) + +#endif + + li %r3, 0 // return UNW_ESUCCESS + blr + + +#elif defined(__ppc__) + +// +// extern int unw_getcontext(unw_context_t* thread_state) +// +// On entry: +// thread_state pointer is in r3 +// +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + stw %r0, 8(%r3) + mflr %r0 + stw %r0, 0(%r3) // store lr as ssr0 + stw %r1, 12(%r3) + stw %r2, 16(%r3) + stw %r3, 20(%r3) + stw %r4, 24(%r3) + stw %r5, 28(%r3) + stw %r6, 32(%r3) + stw %r7, 36(%r3) + stw %r8, 40(%r3) + stw %r9, 44(%r3) + stw %r10, 48(%r3) + stw %r11, 52(%r3) + stw %r12, 56(%r3) + stw %r13, 60(%r3) + stw %r14, 64(%r3) + stw %r15, 68(%r3) + stw %r16, 72(%r3) + stw %r17, 76(%r3) + stw %r18, 80(%r3) + stw %r19, 84(%r3) + stw %r20, 88(%r3) + stw %r21, 92(%r3) + stw %r22, 96(%r3) + stw %r23,100(%r3) + stw %r24,104(%r3) + stw %r25,108(%r3) + stw %r26,112(%r3) + stw %r27,116(%r3) + stw %r28,120(%r3) + stw %r29,124(%r3) + stw %r30,128(%r3) + stw %r31,132(%r3) + + // save VRSave register + mfspr %r0, 256 + stw %r0, 156(%r3) + // save CR registers + mfcr %r0 + stw %r0, 136(%r3) + // save CTR register + mfctr %r0 + stw %r0, 148(%r3) + + // save float registers + stfd %f0, 160(%r3) + stfd %f1, 168(%r3) + stfd %f2, 176(%r3) + stfd %f3, 184(%r3) + stfd %f4, 192(%r3) + stfd %f5, 200(%r3) + stfd %f6, 208(%r3) + stfd %f7, 216(%r3) + stfd %f8, 224(%r3) + stfd %f9, 232(%r3) + stfd %f10,240(%r3) + stfd %f11,248(%r3) + stfd %f12,256(%r3) + stfd %f13,264(%r3) + stfd %f14,272(%r3) + stfd %f15,280(%r3) + stfd %f16,288(%r3) + stfd %f17,296(%r3) + stfd %f18,304(%r3) + stfd %f19,312(%r3) + stfd %f20,320(%r3) + stfd %f21,328(%r3) + stfd %f22,336(%r3) + stfd %f23,344(%r3) + stfd %f24,352(%r3) + stfd %f25,360(%r3) + stfd %f26,368(%r3) + stfd %f27,376(%r3) + stfd %f28,384(%r3) + stfd %f29,392(%r3) + stfd %f30,400(%r3) + stfd %f31,408(%r3) + + + // save vector registers + + subi %r4, %r1, 16 + rlwinm %r4, %r4, 0, 0, 27 // mask low 4-bits + // r4 is now a 16-byte aligned pointer into the red zone + +#define SAVE_VECTOR_UNALIGNED(_vec, _offset) \ + stvx _vec, 0, %r4 SEPARATOR \ + lwz %r5, 0(%r4) SEPARATOR \ + stw %r5, _offset(%r3) SEPARATOR \ + lwz %r5, 4(%r4) SEPARATOR \ + stw %r5, _offset+4(%r3) SEPARATOR \ + lwz %r5, 8(%r4) SEPARATOR \ + stw %r5, _offset+8(%r3) SEPARATOR \ + lwz %r5, 12(%r4) SEPARATOR \ + stw %r5, _offset+12(%r3) + + SAVE_VECTOR_UNALIGNED( %v0, 424+0x000) + SAVE_VECTOR_UNALIGNED( %v1, 424+0x010) + SAVE_VECTOR_UNALIGNED( %v2, 424+0x020) + SAVE_VECTOR_UNALIGNED( %v3, 424+0x030) + SAVE_VECTOR_UNALIGNED( %v4, 424+0x040) + SAVE_VECTOR_UNALIGNED( %v5, 424+0x050) + SAVE_VECTOR_UNALIGNED( %v6, 424+0x060) + SAVE_VECTOR_UNALIGNED( %v7, 424+0x070) + SAVE_VECTOR_UNALIGNED( %v8, 424+0x080) + SAVE_VECTOR_UNALIGNED( %v9, 424+0x090) + SAVE_VECTOR_UNALIGNED(%v10, 424+0x0A0) + SAVE_VECTOR_UNALIGNED(%v11, 424+0x0B0) + SAVE_VECTOR_UNALIGNED(%v12, 424+0x0C0) + SAVE_VECTOR_UNALIGNED(%v13, 424+0x0D0) + SAVE_VECTOR_UNALIGNED(%v14, 424+0x0E0) + SAVE_VECTOR_UNALIGNED(%v15, 424+0x0F0) + SAVE_VECTOR_UNALIGNED(%v16, 424+0x100) + SAVE_VECTOR_UNALIGNED(%v17, 424+0x110) + SAVE_VECTOR_UNALIGNED(%v18, 424+0x120) + SAVE_VECTOR_UNALIGNED(%v19, 424+0x130) + SAVE_VECTOR_UNALIGNED(%v20, 424+0x140) + SAVE_VECTOR_UNALIGNED(%v21, 424+0x150) + SAVE_VECTOR_UNALIGNED(%v22, 424+0x160) + SAVE_VECTOR_UNALIGNED(%v23, 424+0x170) + SAVE_VECTOR_UNALIGNED(%v24, 424+0x180) + SAVE_VECTOR_UNALIGNED(%v25, 424+0x190) + SAVE_VECTOR_UNALIGNED(%v26, 424+0x1A0) + SAVE_VECTOR_UNALIGNED(%v27, 424+0x1B0) + SAVE_VECTOR_UNALIGNED(%v28, 424+0x1C0) + SAVE_VECTOR_UNALIGNED(%v29, 424+0x1D0) + SAVE_VECTOR_UNALIGNED(%v30, 424+0x1E0) + SAVE_VECTOR_UNALIGNED(%v31, 424+0x1F0) + + li %r3, 0 // return UNW_ESUCCESS + blr + + +#elif defined(__arm64__) || defined(__aarch64__) + +// +// extern int __unw_getcontext(unw_context_t* thread_state) +// +// On entry: +// thread_state pointer is in x0 +// + .p2align 2 +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + stp x0, x1, [x0, #0x000] + stp x2, x3, [x0, #0x010] + stp x4, x5, [x0, #0x020] + stp x6, x7, [x0, #0x030] + stp x8, x9, [x0, #0x040] + stp x10,x11, [x0, #0x050] + stp x12,x13, [x0, #0x060] + stp x14,x15, [x0, #0x070] + stp x16,x17, [x0, #0x080] + stp x18,x19, [x0, #0x090] + stp x20,x21, [x0, #0x0A0] + stp x22,x23, [x0, #0x0B0] + stp x24,x25, [x0, #0x0C0] + stp x26,x27, [x0, #0x0D0] + stp x28,x29, [x0, #0x0E0] + str x30, [x0, #0x0F0] + mov x1,sp + str x1, [x0, #0x0F8] + str x30, [x0, #0x100] // store return address as pc + // skip cpsr + stp d0, d1, [x0, #0x110] + stp d2, d3, [x0, #0x120] + stp d4, d5, [x0, #0x130] + stp d6, d7, [x0, #0x140] + stp d8, d9, [x0, #0x150] + stp d10,d11, [x0, #0x160] + stp d12,d13, [x0, #0x170] + stp d14,d15, [x0, #0x180] + stp d16,d17, [x0, #0x190] + stp d18,d19, [x0, #0x1A0] + stp d20,d21, [x0, #0x1B0] + stp d22,d23, [x0, #0x1C0] + stp d24,d25, [x0, #0x1D0] + stp d26,d27, [x0, #0x1E0] + stp d28,d29, [x0, #0x1F0] + str d30, [x0, #0x200] + str d31, [x0, #0x208] + mov x0, #0 // return UNW_ESUCCESS + ret + +#elif defined(__arm__) && !defined(__APPLE__) + +#if !defined(__ARM_ARCH_ISA_ARM) +#if (__ARM_ARCH_ISA_THUMB == 2) + .syntax unified +#endif + .thumb +#endif + +@ +@ extern int __unw_getcontext(unw_context_t* thread_state) +@ +@ On entry: +@ thread_state pointer is in r0 +@ +@ Per EHABI #4.7 this only saves the core integer registers. +@ EHABI #7.4.5 notes that in general all VRS registers should be restored +@ however this is very hard to do for VFP registers because it is unknown +@ to the library how many registers are implemented by the architecture. +@ Instead, VFP registers are demand saved by logic external to __unw_getcontext. +@ + .p2align 2 +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) +#if !defined(__ARM_ARCH_ISA_ARM) && __ARM_ARCH_ISA_THUMB == 1 + stm r0!, {r0-r7} + mov r1, r8 + mov r2, r9 + mov r3, r10 + stm r0!, {r1-r3} + mov r1, r11 + mov r2, sp + mov r3, lr + str r1, [r0, #0] @ r11 + @ r12 does not need storing, it it the intra-procedure-call scratch register + str r2, [r0, #8] @ sp + str r3, [r0, #12] @ lr + str r3, [r0, #16] @ store return address as pc + @ T1 does not have a non-cpsr-clobbering register-zeroing instruction. + @ It is safe to use here though because we are about to return, and cpsr is + @ not expected to be preserved. + movs r0, #0 @ return UNW_ESUCCESS +#else + @ 32bit thumb-2 restrictions for stm: + @ . the sp (r13) cannot be in the list + @ . the pc (r15) cannot be in the list in an STM instruction + stm r0, {r0-r12} + str sp, [r0, #52] + str lr, [r0, #56] + str lr, [r0, #60] @ store return address as pc + mov r0, #0 @ return UNW_ESUCCESS +#endif + JMP(lr) + +@ +@ static void libunwind::Registers_arm::saveVFPWithFSTMD(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .fpu vfpv3-d16 +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm16saveVFPWithFSTMDEPv) + vstmia r0, {d0-d15} + JMP(lr) + +@ +@ static void libunwind::Registers_arm::saveVFPWithFSTMX(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .fpu vfpv3-d16 +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm16saveVFPWithFSTMXEPv) + vstmia r0, {d0-d15} @ fstmiax is deprecated in ARMv7+ and now behaves like vstmia + JMP(lr) + +@ +@ static void libunwind::Registers_arm::saveVFPv3(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .fpu vfpv3 +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm9saveVFPv3EPv) + @ VFP and iwMMX instructions are only available when compiling with the flags + @ that enable them. We do not want to do that in the library (because we do not + @ want the compiler to generate instructions that access those) but this is + @ only accessed if the personality routine needs these registers. Use of + @ these registers implies they are, actually, available on the target, so + @ it's ok to execute. + @ So, generate the instructions using the corresponding coprocessor mnemonic. + vstmia r0, {d16-d31} + JMP(lr) + +#if defined(_LIBUNWIND_ARM_WMMX) + +@ +@ static void libunwind::Registers_arm::saveiWMMX(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .arch armv5te +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm9saveiWMMXEPv) + stcl p1, cr0, [r0], #8 @ wstrd wR0, [r0], #8 + stcl p1, cr1, [r0], #8 @ wstrd wR1, [r0], #8 + stcl p1, cr2, [r0], #8 @ wstrd wR2, [r0], #8 + stcl p1, cr3, [r0], #8 @ wstrd wR3, [r0], #8 + stcl p1, cr4, [r0], #8 @ wstrd wR4, [r0], #8 + stcl p1, cr5, [r0], #8 @ wstrd wR5, [r0], #8 + stcl p1, cr6, [r0], #8 @ wstrd wR6, [r0], #8 + stcl p1, cr7, [r0], #8 @ wstrd wR7, [r0], #8 + stcl p1, cr8, [r0], #8 @ wstrd wR8, [r0], #8 + stcl p1, cr9, [r0], #8 @ wstrd wR9, [r0], #8 + stcl p1, cr10, [r0], #8 @ wstrd wR10, [r0], #8 + stcl p1, cr11, [r0], #8 @ wstrd wR11, [r0], #8 + stcl p1, cr12, [r0], #8 @ wstrd wR12, [r0], #8 + stcl p1, cr13, [r0], #8 @ wstrd wR13, [r0], #8 + stcl p1, cr14, [r0], #8 @ wstrd wR14, [r0], #8 + stcl p1, cr15, [r0], #8 @ wstrd wR15, [r0], #8 + JMP(lr) + +@ +@ static void libunwind::Registers_arm::saveiWMMXControl(unw_uint32_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .arch armv5te +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm16saveiWMMXControlEPj) + stc2 p1, cr8, [r0], #4 @ wstrw wCGR0, [r0], #4 + stc2 p1, cr9, [r0], #4 @ wstrw wCGR1, [r0], #4 + stc2 p1, cr10, [r0], #4 @ wstrw wCGR2, [r0], #4 + stc2 p1, cr11, [r0], #4 @ wstrw wCGR3, [r0], #4 + JMP(lr) + +#endif + +#elif defined(__or1k__) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in r3 +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + l.sw 0(r3), r0 + l.sw 4(r3), r1 + l.sw 8(r3), r2 + l.sw 12(r3), r3 + l.sw 16(r3), r4 + l.sw 20(r3), r5 + l.sw 24(r3), r6 + l.sw 28(r3), r7 + l.sw 32(r3), r8 + l.sw 36(r3), r9 + l.sw 40(r3), r10 + l.sw 44(r3), r11 + l.sw 48(r3), r12 + l.sw 52(r3), r13 + l.sw 56(r3), r14 + l.sw 60(r3), r15 + l.sw 64(r3), r16 + l.sw 68(r3), r17 + l.sw 72(r3), r18 + l.sw 76(r3), r19 + l.sw 80(r3), r20 + l.sw 84(r3), r21 + l.sw 88(r3), r22 + l.sw 92(r3), r23 + l.sw 96(r3), r24 + l.sw 100(r3), r25 + l.sw 104(r3), r26 + l.sw 108(r3), r27 + l.sw 112(r3), r28 + l.sw 116(r3), r29 + l.sw 120(r3), r30 + l.sw 124(r3), r31 + # store ra to pc + l.sw 128(r3), r9 + # zero epcr + l.sw 132(r3), r0 + +#elif defined(__sparc__) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in o0 +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + ta 3 + add %o7, 8, %o7 + std %g0, [%o0 + 0] + std %g2, [%o0 + 8] + std %g4, [%o0 + 16] + std %g6, [%o0 + 24] + std %o0, [%o0 + 32] + std %o2, [%o0 + 40] + std %o4, [%o0 + 48] + std %o6, [%o0 + 56] + std %l0, [%o0 + 64] + std %l2, [%o0 + 72] + std %l4, [%o0 + 80] + std %l6, [%o0 + 88] + std %i0, [%o0 + 96] + std %i2, [%o0 + 104] + std %i4, [%o0 + 112] + std %i6, [%o0 + 120] + jmp %o7 + clr %o0 // return UNW_ESUCCESS +#endif + + WEAK_ALIAS(__unw_getcontext, unw_getcontext) + +#endif /* !defined(__USING_SJLJ_EXCEPTIONS__) */ + +NO_EXEC_STACK_DIRECTIVE diff --git a/src/coreclr/nativeaot/libunwind/src/Unwind_AppleExtras.cpp b/src/coreclr/nativeaot/libunwind/src/Unwind_AppleExtras.cpp new file mode 100644 index 00000000000000..248d99570e94ab --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/Unwind_AppleExtras.cpp @@ -0,0 +1,183 @@ +//===--------------------- Unwind_AppleExtras.cpp -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +//===----------------------------------------------------------------------===// + +#include "config.h" +#include "AddressSpace.hpp" +#include "DwarfParser.hpp" + + +// private keymgr stuff +#define KEYMGR_GCC3_DW2_OBJ_LIST 302 +extern "C" { + extern void _keymgr_set_and_unlock_processwide_ptr(int key, void *ptr); + extern void *_keymgr_get_and_lock_processwide_ptr(int key); +} + +// undocumented libgcc "struct object" +struct libgcc_object { + void *start; + void *unused1; + void *unused2; + void *fde; + unsigned long encoding; + void *fde_end; + libgcc_object *next; +}; + +// undocumented libgcc "struct km_object_info" referenced by +// KEYMGR_GCC3_DW2_OBJ_LIST +struct libgcc_object_info { + libgcc_object *seen_objects; + libgcc_object *unseen_objects; + unsigned spare[2]; +}; + + +// static linker symbols to prevent wrong two level namespace for _Unwind symbols +#if defined(__arm__) + #define NOT_HERE_BEFORE_5_0(sym) \ + extern const char sym##_tmp30 __asm("$ld$hide$os3.0$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp30 = 0; \ + extern const char sym##_tmp31 __asm("$ld$hide$os3.1$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp31 = 0; \ + extern const char sym##_tmp32 __asm("$ld$hide$os3.2$_" #sym );\ + __attribute__((visibility("default"))) const char sym##_tmp32 = 0; \ + extern const char sym##_tmp40 __asm("$ld$hide$os4.0$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp40 = 0; \ + extern const char sym##_tmp41 __asm("$ld$hide$os4.1$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp41 = 0; \ + extern const char sym##_tmp42 __asm("$ld$hide$os4.2$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp42 = 0; \ + extern const char sym##_tmp43 __asm("$ld$hide$os4.3$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp43 = 0; +#elif defined(__arm64__) + #define NOT_HERE_BEFORE_10_6(sym) + #define NEVER_HERE(sym) +#else + #define NOT_HERE_BEFORE_10_6(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NEVER_HERE(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp6 = 0; +#endif + + +#if defined(_LIBUNWIND_BUILD_ZERO_COST_APIS) + +// +// symbols in libSystem.dylib in 10.6 and later, but are in libgcc_s.dylib in +// earlier versions +// +NOT_HERE_BEFORE_10_6(_Unwind_DeleteException) +NOT_HERE_BEFORE_10_6(_Unwind_Find_FDE) +NOT_HERE_BEFORE_10_6(_Unwind_ForcedUnwind) +NOT_HERE_BEFORE_10_6(_Unwind_GetGR) +NOT_HERE_BEFORE_10_6(_Unwind_GetIP) +NOT_HERE_BEFORE_10_6(_Unwind_GetLanguageSpecificData) +NOT_HERE_BEFORE_10_6(_Unwind_GetRegionStart) +NOT_HERE_BEFORE_10_6(_Unwind_RaiseException) +NOT_HERE_BEFORE_10_6(_Unwind_Resume) +NOT_HERE_BEFORE_10_6(_Unwind_SetGR) +NOT_HERE_BEFORE_10_6(_Unwind_SetIP) +NOT_HERE_BEFORE_10_6(_Unwind_Backtrace) +NOT_HERE_BEFORE_10_6(_Unwind_FindEnclosingFunction) +NOT_HERE_BEFORE_10_6(_Unwind_GetCFA) +NOT_HERE_BEFORE_10_6(_Unwind_GetDataRelBase) +NOT_HERE_BEFORE_10_6(_Unwind_GetTextRelBase) +NOT_HERE_BEFORE_10_6(_Unwind_Resume_or_Rethrow) +NOT_HERE_BEFORE_10_6(_Unwind_GetIPInfo) +NOT_HERE_BEFORE_10_6(__register_frame) +NOT_HERE_BEFORE_10_6(__deregister_frame) + +// +// symbols in libSystem.dylib for compatibility, but we don't want any new code +// using them +// +NEVER_HERE(__register_frame_info_bases) +NEVER_HERE(__register_frame_info) +NEVER_HERE(__register_frame_info_table_bases) +NEVER_HERE(__register_frame_info_table) +NEVER_HERE(__register_frame_table) +NEVER_HERE(__deregister_frame_info) +NEVER_HERE(__deregister_frame_info_bases) + +#endif // defined(_LIBUNWIND_BUILD_ZERO_COST_APIS) + + + + +#if defined(_LIBUNWIND_BUILD_SJLJ_APIS) +// +// symbols in libSystem.dylib in iOS 5.0 and later, but are in libgcc_s.dylib in +// earlier versions +// +NOT_HERE_BEFORE_5_0(_Unwind_GetLanguageSpecificData) +NOT_HERE_BEFORE_5_0(_Unwind_GetRegionStart) +NOT_HERE_BEFORE_5_0(_Unwind_GetIP) +NOT_HERE_BEFORE_5_0(_Unwind_SetGR) +NOT_HERE_BEFORE_5_0(_Unwind_SetIP) +NOT_HERE_BEFORE_5_0(_Unwind_DeleteException) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Register) +NOT_HERE_BEFORE_5_0(_Unwind_GetGR) +NOT_HERE_BEFORE_5_0(_Unwind_GetIPInfo) +NOT_HERE_BEFORE_5_0(_Unwind_GetCFA) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Resume) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_RaiseException) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Resume_or_Rethrow) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Unregister) + +#endif // defined(_LIBUNWIND_BUILD_SJLJ_APIS) + + +namespace libunwind { + +_LIBUNWIND_HIDDEN +bool checkKeyMgrRegisteredFDEs(uintptr_t pc, void *&fde) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // lastly check for old style keymgr registration of dynamically generated + // FDEs acquire exclusive access to libgcc_object_info + libgcc_object_info *head = (libgcc_object_info *) + _keymgr_get_and_lock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST); + if (head != NULL) { + // look at each FDE in keymgr + for (libgcc_object *ob = head->unseen_objects; ob != NULL; ob = ob->next) { + CFI_Parser::FDE_Info fdeInfo; + CFI_Parser::CIE_Info cieInfo; + const char *msg = CFI_Parser::decodeFDE( + LocalAddressSpace::sThisAddressSpace, + (uintptr_t)ob->fde, &fdeInfo, &cieInfo); + if (msg == NULL) { + // Check if this FDE is for a function that includes the pc + if ((fdeInfo.pcStart <= pc) && (pc < fdeInfo.pcEnd)) { + fde = (void*)fdeInfo.pcStart; + _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, + head); + return true; + } + } + } + } + // release libgcc_object_info + _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, head); +#else + (void)pc; + (void)fde; +#endif + return false; +} + +} + diff --git a/src/coreclr/nativeaot/libunwind/src/assembly.h b/src/coreclr/nativeaot/libunwind/src/assembly.h new file mode 100644 index 00000000000000..7132b6c561b0db --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/assembly.h @@ -0,0 +1,158 @@ +/* ===-- assembly.h - libUnwind assembler support macros -------------------=== + * + * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://llvm.org/LICENSE.txt for license information. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + * ===----------------------------------------------------------------------=== + * + * This file defines macros for use in libUnwind assembler source. + * This file is not part of the interface of this library. + * + * ===----------------------------------------------------------------------=== + */ + +#ifndef UNWIND_ASSEMBLY_H +#define UNWIND_ASSEMBLY_H + +#if defined(__powerpc64__) +#define SEPARATOR ; +#define PPC64_OFFS_SRR0 0 +#define PPC64_OFFS_CR 272 +#define PPC64_OFFS_XER 280 +#define PPC64_OFFS_LR 288 +#define PPC64_OFFS_CTR 296 +#define PPC64_OFFS_VRSAVE 304 +#define PPC64_OFFS_FP 312 +#define PPC64_OFFS_V 824 +#ifdef _ARCH_PWR8 +#define PPC64_HAS_VMX +#endif +#elif defined(__arm64__) +#define SEPARATOR %% +#else +#define SEPARATOR ; +#endif + +#if defined(__powerpc64__) && (!defined(_CALL_ELF) || _CALL_ELF == 1) +#define PPC64_OPD1 .section .opd,"aw",@progbits SEPARATOR +#define PPC64_OPD2 SEPARATOR \ + .p2align 3 SEPARATOR \ + .quad .Lfunc_begin0 SEPARATOR \ + .quad .TOC.@tocbase SEPARATOR \ + .quad 0 SEPARATOR \ + .text SEPARATOR \ +.Lfunc_begin0: +#else +#define PPC64_OPD1 +#define PPC64_OPD2 +#endif + +#define GLUE2(a, b) a ## b +#define GLUE(a, b) GLUE2(a, b) +#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name) + +#if defined(__APPLE__) + +#define SYMBOL_IS_FUNC(name) +#define EXPORT_SYMBOL(name) +#define HIDDEN_SYMBOL(name) .private_extern name +#define WEAK_SYMBOL(name) .weak_reference name +#define WEAK_ALIAS(name, aliasname) \ + .globl SYMBOL_NAME(aliasname) SEPARATOR \ + WEAK_SYMBOL(aliasname) SEPARATOR \ + SYMBOL_NAME(aliasname) = SYMBOL_NAME(name) + +#define NO_EXEC_STACK_DIRECTIVE + +#elif defined(__ELF__) + +#if defined(__arm__) +#define SYMBOL_IS_FUNC(name) .type name,%function +#else +#define SYMBOL_IS_FUNC(name) .type name,@function +#endif +#define EXPORT_SYMBOL(name) +#define HIDDEN_SYMBOL(name) .hidden name +#define WEAK_SYMBOL(name) .weak name +#define WEAK_ALIAS(name, aliasname) \ + WEAK_SYMBOL(aliasname) SEPARATOR \ + SYMBOL_NAME(aliasname) = SYMBOL_NAME(name) + +#if defined(__GNU__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \ + defined(__linux__) +#define NO_EXEC_STACK_DIRECTIVE .section .note.GNU-stack,"",%progbits +#else +#define NO_EXEC_STACK_DIRECTIVE +#endif + +#elif defined(_WIN32) + +#define SYMBOL_IS_FUNC(name) \ + .def name SEPARATOR \ + .scl 2 SEPARATOR \ + .type 32 SEPARATOR \ + .endef +#define EXPORT_SYMBOL2(name) \ + .section .drectve,"yn" SEPARATOR \ + .ascii "-export:", #name, "\0" SEPARATOR \ + .text +#if defined(_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS) +#define EXPORT_SYMBOL(name) +#else +#define EXPORT_SYMBOL(name) EXPORT_SYMBOL2(name) +#endif +#define HIDDEN_SYMBOL(name) + +#if defined(__MINGW32__) +#define WEAK_ALIAS(name, aliasname) \ + .globl SYMBOL_NAME(aliasname) SEPARATOR \ + EXPORT_SYMBOL(aliasname) SEPARATOR \ + SYMBOL_NAME(aliasname) = SYMBOL_NAME(name) +#else +#define WEAK_ALIAS3(name, aliasname) \ + .section .drectve,"yn" SEPARATOR \ + .ascii "-alternatename:", #aliasname, "=", #name, "\0" SEPARATOR \ + .text +#define WEAK_ALIAS2(name, aliasname) \ + WEAK_ALIAS3(name, aliasname) +#define WEAK_ALIAS(name, aliasname) \ + EXPORT_SYMBOL(SYMBOL_NAME(aliasname)) SEPARATOR \ + WEAK_ALIAS2(SYMBOL_NAME(name), SYMBOL_NAME(aliasname)) +#endif + +#define NO_EXEC_STACK_DIRECTIVE + +#elif defined(__sparc__) + +#else + +#error Unsupported target + +#endif + +#define DEFINE_LIBUNWIND_FUNCTION(name) \ + .globl SYMBOL_NAME(name) SEPARATOR \ + HIDDEN_SYMBOL(SYMBOL_NAME(name)) SEPARATOR \ + SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \ + PPC64_OPD1 \ + SYMBOL_NAME(name): \ + PPC64_OPD2 + +#if defined(__arm__) +#if !defined(__ARM_ARCH) +#define __ARM_ARCH 4 +#endif + +#if defined(__ARM_ARCH_4T__) || __ARM_ARCH >= 5 +#define ARM_HAS_BX +#endif + +#ifdef ARM_HAS_BX +#define JMP(r) bx r +#else +#define JMP(r) mov pc, r +#endif +#endif /* __arm__ */ + +#endif /* UNWIND_ASSEMBLY_H */ diff --git a/src/coreclr/nativeaot/libunwind/src/config.h b/src/coreclr/nativeaot/libunwind/src/config.h new file mode 100644 index 00000000000000..09bb261647ca5d --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/config.h @@ -0,0 +1,211 @@ +//===----------------------------- config.h -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Defines macros used within libunwind project. +// +//===----------------------------------------------------------------------===// + + +#ifndef LIBUNWIND_CONFIG_H +#define LIBUNWIND_CONFIG_H + +#include +#include +#include +#include + +// Define static_assert() unless already defined by compiler. +#ifndef __has_feature + #define __has_feature(__x) 0 +#endif +#if !(__has_feature(cxx_static_assert)) && !defined(static_assert) + #define static_assert(__b, __m) \ + extern int compile_time_assert_failed[ ( __b ) ? 1 : -1 ] \ + __attribute__( ( unused ) ); +#endif + +// Platform specific configuration defines. +#ifdef __APPLE__ + #if defined(FOR_DYLD) + #define _LIBUNWIND_SUPPORT_COMPACT_UNWIND + #else + #define _LIBUNWIND_SUPPORT_COMPACT_UNWIND + #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 + #endif +#elif defined(_WIN32) + #ifdef __SEH__ + #define _LIBUNWIND_SUPPORT_SEH_UNWIND 1 + #else + #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 + #endif +#else + #if defined(__ARM_DWARF_EH__) || !defined(__arm__) + #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 + #define _LIBUNWIND_SUPPORT_DWARF_INDEX 1 + #endif +#endif + +#if defined(_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS) + #define _LIBUNWIND_EXPORT + #define _LIBUNWIND_HIDDEN +#else + #if !defined(__ELF__) && !defined(__MACH__) + #define _LIBUNWIND_EXPORT __declspec(dllexport) + #define _LIBUNWIND_HIDDEN + #else + #define _LIBUNWIND_EXPORT __attribute__((visibility("default"))) + #define _LIBUNWIND_HIDDEN __attribute__((visibility("hidden"))) + #endif +#endif + +#define STR(a) #a +#define XSTR(a) STR(a) +#define SYMBOL_NAME(name) XSTR(__USER_LABEL_PREFIX__) #name + +#if defined(__APPLE__) +#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \ + __asm__(".globl " SYMBOL_NAME(aliasname)); \ + __asm__(SYMBOL_NAME(aliasname) " = " SYMBOL_NAME(name)); \ + extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \ + __attribute__((weak_import)); +#elif defined(__ELF__) +#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \ + extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \ + __attribute__((weak, alias(#name))); +#elif defined(_WIN32) +#if defined(__MINGW32__) +#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \ + extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \ + __attribute__((alias(#name))); +#else +#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \ + __pragma(comment(linker, "/alternatename:" SYMBOL_NAME(aliasname) "=" \ + SYMBOL_NAME(name))) \ + extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname; +#endif +#else +#error Unsupported target +#endif + +#if (defined(__APPLE__) && defined(__arm__)) || defined(__USING_SJLJ_EXCEPTIONS__) +#define _LIBUNWIND_BUILD_SJLJ_APIS +#endif + +#if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || defined(__ppc64__) || defined(__powerpc64__) +#define _LIBUNWIND_SUPPORT_FRAME_APIS +#endif + +#if defined(__i386__) || defined(__x86_64__) || \ + defined(__ppc__) || defined(__ppc64__) || defined(__powerpc64__) || \ + (!defined(__APPLE__) && defined(__arm__)) || \ + (defined(__arm64__) || defined(__aarch64__)) || \ + defined(__mips__) +#if !defined(_LIBUNWIND_BUILD_SJLJ_APIS) +#define _LIBUNWIND_BUILD_ZERO_COST_APIS +#endif +#endif + +#if defined(__powerpc64__) && defined(_ARCH_PWR8) +#define PPC64_HAS_VMX +#endif + +#if defined(NDEBUG) && defined(_LIBUNWIND_IS_BAREMETAL) +#define _LIBUNWIND_ABORT(msg) \ + do { \ + abort(); \ + } while (0) +#else +#define _LIBUNWIND_ABORT(msg) \ + do { \ + fprintf(stderr, "libunwind: %s %s:%d - %s\n", __func__, __FILE__, \ + __LINE__, msg); \ + fflush(stderr); \ + abort(); \ + } while (0) +#endif + +#if defined(NDEBUG) && defined(_LIBUNWIND_IS_BAREMETAL) +#define _LIBUNWIND_LOG0(msg) +#define _LIBUNWIND_LOG(msg, ...) +#else +#define _LIBUNWIND_LOG0(msg) \ + fprintf(stderr, "libunwind: " msg "\n") +#define _LIBUNWIND_LOG(msg, ...) \ + fprintf(stderr, "libunwind: " msg "\n", __VA_ARGS__) +#endif + +#if defined(NDEBUG) + #define _LIBUNWIND_LOG_IF_FALSE(x) x +#else + #define _LIBUNWIND_LOG_IF_FALSE(x) \ + do { \ + bool _ret = x; \ + if (!_ret) \ + _LIBUNWIND_LOG("" #x " failed in %s", __FUNCTION__); \ + } while (0) +#endif + +// Macros that define away in non-Debug builds +#ifdef NDEBUG + #define _LIBUNWIND_DEBUG_LOG(msg, ...) + #define _LIBUNWIND_TRACE_API(msg, ...) + #define _LIBUNWIND_TRACING_UNWINDING (0) + #define _LIBUNWIND_TRACING_DWARF (0) + #define _LIBUNWIND_TRACE_UNWINDING(msg, ...) + #define _LIBUNWIND_TRACE_DWARF(...) +#else + #ifdef __cplusplus + extern "C" { + #endif + extern bool logAPIs(); + extern bool logUnwinding(); + extern bool logDWARF(); + #ifdef __cplusplus + } + #endif + #define _LIBUNWIND_DEBUG_LOG(msg, ...) _LIBUNWIND_LOG(msg, __VA_ARGS__) + #define _LIBUNWIND_TRACE_API(msg, ...) \ + do { \ + if (logAPIs()) \ + _LIBUNWIND_LOG(msg, __VA_ARGS__); \ + } while (0) + #define _LIBUNWIND_TRACING_UNWINDING logUnwinding() + #define _LIBUNWIND_TRACING_DWARF logDWARF() + #define _LIBUNWIND_TRACE_UNWINDING(msg, ...) \ + do { \ + if (logUnwinding()) \ + _LIBUNWIND_LOG(msg, __VA_ARGS__); \ + } while (0) + #define _LIBUNWIND_TRACE_DWARF(...) \ + do { \ + if (logDWARF()) \ + fprintf(stderr, __VA_ARGS__); \ + } while (0) +#endif + +#ifdef __cplusplus +// Used to fit UnwindCursor and Registers_xxx types against unw_context_t / +// unw_cursor_t sized memory blocks. +#if defined(_LIBUNWIND_IS_NATIVE_ONLY) +# define COMP_OP == +#else +# define COMP_OP <= +#endif +template +struct check_fit { + template + struct blk_count { + static const size_t count = + (sizeof(T) + sizeof(uint64_t) - 1) / sizeof(uint64_t); + }; + static const bool does_fit = + (blk_count<_Type>::count COMP_OP blk_count<_Mem>::count); +}; +#undef COMP_OP +#endif // __cplusplus + +#endif // LIBUNWIND_CONFIG_H diff --git a/src/coreclr/nativeaot/libunwind/src/dwarf2.h b/src/coreclr/nativeaot/libunwind/src/dwarf2.h new file mode 100644 index 00000000000000..40f0daf4680592 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/dwarf2.h @@ -0,0 +1,239 @@ +//===------------------------------- dwarf2.h -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + +/* + These constants were taken from version 3 of the DWARF standard, + which is Copyright (c) 2005 Free Standards Group, and + Copyright (c) 1992, 1993 UNIX International, Inc. +*/ + +#ifndef __DWARF2__ +#define __DWARF2__ + +// DWARF unwind instructions +enum { + DW_CFA_nop = 0x0, + DW_CFA_set_loc = 0x1, + DW_CFA_advance_loc1 = 0x2, + DW_CFA_advance_loc2 = 0x3, + DW_CFA_advance_loc4 = 0x4, + DW_CFA_offset_extended = 0x5, + DW_CFA_restore_extended = 0x6, + DW_CFA_undefined = 0x7, + DW_CFA_same_value = 0x8, + DW_CFA_register = 0x9, + DW_CFA_remember_state = 0xA, + DW_CFA_restore_state = 0xB, + DW_CFA_def_cfa = 0xC, + DW_CFA_def_cfa_register = 0xD, + DW_CFA_def_cfa_offset = 0xE, + DW_CFA_def_cfa_expression = 0xF, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta + DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register + DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register + + // GNU extensions + DW_CFA_GNU_window_save = 0x2D, + DW_CFA_GNU_args_size = 0x2E, + DW_CFA_GNU_negative_offset_extended = 0x2F, + + // AARCH64 extensions + DW_CFA_AARCH64_negate_ra_state = 0x2D +}; + + +// FSF exception handling Pointer-Encoding constants +// Used in CFI augmentation by GCC +enum { + DW_EH_PE_ptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_signed = 0x08, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_absptr = 0x00, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + + +// DWARF expressions +enum { + DW_OP_addr = 0x03, // constant address (size target specific) + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, // 1-byte constant + DW_OP_const1s = 0x09, // 1-byte constant + DW_OP_const2u = 0x0A, // 2-byte constant + DW_OP_const2s = 0x0B, // 2-byte constant + DW_OP_const4u = 0x0C, // 4-byte constant + DW_OP_const4s = 0x0D, // 4-byte constant + DW_OP_const8u = 0x0E, // 8-byte constant + DW_OP_const8s = 0x0F, // 8-byte constant + DW_OP_constu = 0x10, // ULEB128 constant + DW_OP_consts = 0x11, // SLEB128 constant + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, // 1-byte stack index + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1A, + DW_OP_div = 0x1B, + DW_OP_minus = 0x1C, + DW_OP_mod = 0x1D, + DW_OP_mul = 0x1E, + DW_OP_neg = 0x1F, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, // ULEB128 addend + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_skip = 0x2F, // signed 2-byte constant + DW_OP_bra = 0x28, // signed 2-byte constant + DW_OP_eq = 0x29, + DW_OP_ge = 0x2A, + DW_OP_gt = 0x2B, + DW_OP_le = 0x2C, + DW_OP_lt = 0x2D, + DW_OP_ne = 0x2E, + DW_OP_lit0 = 0x30, // Literal 0 + DW_OP_lit1 = 0x31, // Literal 1 + DW_OP_lit2 = 0x32, // Literal 2 + DW_OP_lit3 = 0x33, // Literal 3 + DW_OP_lit4 = 0x34, // Literal 4 + DW_OP_lit5 = 0x35, // Literal 5 + DW_OP_lit6 = 0x36, // Literal 6 + DW_OP_lit7 = 0x37, // Literal 7 + DW_OP_lit8 = 0x38, // Literal 8 + DW_OP_lit9 = 0x39, // Literal 9 + DW_OP_lit10 = 0x3A, // Literal 10 + DW_OP_lit11 = 0x3B, // Literal 11 + DW_OP_lit12 = 0x3C, // Literal 12 + DW_OP_lit13 = 0x3D, // Literal 13 + DW_OP_lit14 = 0x3E, // Literal 14 + DW_OP_lit15 = 0x3F, // Literal 15 + DW_OP_lit16 = 0x40, // Literal 16 + DW_OP_lit17 = 0x41, // Literal 17 + DW_OP_lit18 = 0x42, // Literal 18 + DW_OP_lit19 = 0x43, // Literal 19 + DW_OP_lit20 = 0x44, // Literal 20 + DW_OP_lit21 = 0x45, // Literal 21 + DW_OP_lit22 = 0x46, // Literal 22 + DW_OP_lit23 = 0x47, // Literal 23 + DW_OP_lit24 = 0x48, // Literal 24 + DW_OP_lit25 = 0x49, // Literal 25 + DW_OP_lit26 = 0x4A, // Literal 26 + DW_OP_lit27 = 0x4B, // Literal 27 + DW_OP_lit28 = 0x4C, // Literal 28 + DW_OP_lit29 = 0x4D, // Literal 29 + DW_OP_lit30 = 0x4E, // Literal 30 + DW_OP_lit31 = 0x4F, // Literal 31 + DW_OP_reg0 = 0x50, // Contents of reg0 + DW_OP_reg1 = 0x51, // Contents of reg1 + DW_OP_reg2 = 0x52, // Contents of reg2 + DW_OP_reg3 = 0x53, // Contents of reg3 + DW_OP_reg4 = 0x54, // Contents of reg4 + DW_OP_reg5 = 0x55, // Contents of reg5 + DW_OP_reg6 = 0x56, // Contents of reg6 + DW_OP_reg7 = 0x57, // Contents of reg7 + DW_OP_reg8 = 0x58, // Contents of reg8 + DW_OP_reg9 = 0x59, // Contents of reg9 + DW_OP_reg10 = 0x5A, // Contents of reg10 + DW_OP_reg11 = 0x5B, // Contents of reg11 + DW_OP_reg12 = 0x5C, // Contents of reg12 + DW_OP_reg13 = 0x5D, // Contents of reg13 + DW_OP_reg14 = 0x5E, // Contents of reg14 + DW_OP_reg15 = 0x5F, // Contents of reg15 + DW_OP_reg16 = 0x60, // Contents of reg16 + DW_OP_reg17 = 0x61, // Contents of reg17 + DW_OP_reg18 = 0x62, // Contents of reg18 + DW_OP_reg19 = 0x63, // Contents of reg19 + DW_OP_reg20 = 0x64, // Contents of reg20 + DW_OP_reg21 = 0x65, // Contents of reg21 + DW_OP_reg22 = 0x66, // Contents of reg22 + DW_OP_reg23 = 0x67, // Contents of reg23 + DW_OP_reg24 = 0x68, // Contents of reg24 + DW_OP_reg25 = 0x69, // Contents of reg25 + DW_OP_reg26 = 0x6A, // Contents of reg26 + DW_OP_reg27 = 0x6B, // Contents of reg27 + DW_OP_reg28 = 0x6C, // Contents of reg28 + DW_OP_reg29 = 0x6D, // Contents of reg29 + DW_OP_reg30 = 0x6E, // Contents of reg30 + DW_OP_reg31 = 0x6F, // Contents of reg31 + DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset + DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset + DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset + DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset + DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset + DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset + DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset + DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset + DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset + DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset + DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset + DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset + DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset + DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset + DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset + DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset + DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset + DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset + DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset + DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset + DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset + DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset + DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset + DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset + DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset + DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset + DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset + DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset + DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset + DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset + DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset + DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset + DW_OP_regx = 0x90, // ULEB128 register + DW_OP_fbreg = 0x91, // SLEB128 offset + DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset + DW_OP_piece = 0x93, // ULEB128 size of piece addressed + DW_OP_deref_size = 0x94, // 1-byte size of data retrieved + DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved + DW_OP_nop = 0x96, + DW_OP_push_object_addres = 0x97, + DW_OP_call2 = 0x98, // 2-byte offset of DIE + DW_OP_call4 = 0x99, // 4-byte offset of DIE + DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE + DW_OP_lo_user = 0xE0, + DW_OP_APPLE_uninit = 0xF0, + DW_OP_hi_user = 0xFF +}; + + +#endif diff --git a/src/coreclr/nativeaot/libunwind/src/libunwind.cpp b/src/coreclr/nativeaot/libunwind/src/libunwind.cpp new file mode 100644 index 00000000000000..387ad0fe1616f4 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/libunwind.cpp @@ -0,0 +1,339 @@ +//===--------------------------- libunwind.cpp ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Implements unw_* functions from +// +//===----------------------------------------------------------------------===// + +#include + +#include "libunwind_ext.h" +#include "config.h" + +#include + + +#if !defined(__USING_SJLJ_EXCEPTIONS__) +#include "AddressSpace.hpp" +#include "UnwindCursor.hpp" + +using namespace libunwind; + +/// internal object to represent this processes address space +LocalAddressSpace LocalAddressSpace::sThisAddressSpace; + +_LIBUNWIND_EXPORT unw_addr_space_t unw_local_addr_space = + (unw_addr_space_t)&LocalAddressSpace::sThisAddressSpace; + +/// Create a cursor of a thread in this process given 'context' recorded by +/// __unw_getcontext(). +_LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor, + unw_context_t *context) { + _LIBUNWIND_TRACE_API("__unw_init_local(cursor=%p, context=%p)", + static_cast(cursor), + static_cast(context)); +#if defined(__i386__) +# define REGISTER_KIND Registers_x86 +#elif defined(__x86_64__) +# define REGISTER_KIND Registers_x86_64 +#elif defined(__powerpc64__) +# define REGISTER_KIND Registers_ppc64 +#elif defined(__ppc__) +# define REGISTER_KIND Registers_ppc +#elif defined(__aarch64__) +# define REGISTER_KIND Registers_arm64 +#elif defined(__arm__) +# define REGISTER_KIND Registers_arm +#elif defined(__or1k__) +# define REGISTER_KIND Registers_or1k +#elif defined(__mips__) && defined(_ABIO32) && _MIPS_SIM == _ABIO32 +# define REGISTER_KIND Registers_mips_o32 +#elif defined(__mips64) +# define REGISTER_KIND Registers_mips_newabi +#elif defined(__mips__) +# warning The MIPS architecture is not supported with this ABI and environment! +#elif defined(__sparc__) +# define REGISTER_KIND Registers_sparc +#else +# error Architecture not supported +#endif + // Use "placement new" to allocate UnwindCursor in the cursor buffer. + new (reinterpret_cast *>(cursor)) + UnwindCursor( + context, LocalAddressSpace::sThisAddressSpace); +#undef REGISTER_KIND + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + co->setInfoBasedOnIPRegister(); + + return UNW_ESUCCESS; +} +_LIBUNWIND_WEAK_ALIAS(__unw_init_local, unw_init_local) + +/// Get value of specified register at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum, + unw_word_t *value) { + _LIBUNWIND_TRACE_API("__unw_get_reg(cursor=%p, regNum=%d, &value=%p)", + static_cast(cursor), regNum, + static_cast(value)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validReg(regNum)) { + *value = co->getReg(regNum); + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} +_LIBUNWIND_WEAK_ALIAS(__unw_get_reg, unw_get_reg) + +/// Set value of specified register at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum, + unw_word_t value, unw_word_t *pos) { + _LIBUNWIND_TRACE_API("__unw_set_reg(cursor=%p, regNum=%d, value=0x%" PRIxPTR + ")", + static_cast(cursor), regNum, value); + typedef LocalAddressSpace::pint_t pint_t; + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validReg(regNum)) { + co->setReg(regNum, (pint_t)value, (pint_t)pos); + // special case altering IP to re-find info (being called by personality + // function) + if (regNum == UNW_REG_IP) { + unw_proc_info_t info; + // First, get the FDE for the old location and then update it. + co->getInfo(&info); + co->setInfoBasedOnIPRegister(false); + // If the original call expects stack adjustment, perform this now. + // Normal frame unwinding would have included the offset already in the + // CFA computation. + // Note: for PA-RISC and other platforms where the stack grows up, + // this should actually be - info.gp. LLVM doesn't currently support + // any such platforms and Clang doesn't export a macro for them. + if (info.gp) + co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + info.gp, 0); + } + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} +_LIBUNWIND_WEAK_ALIAS(__unw_set_reg, unw_set_reg) + +/// Get value of specified float register at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_get_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum, + unw_fpreg_t *value) { + _LIBUNWIND_TRACE_API("__unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)", + static_cast(cursor), regNum, + static_cast(value)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validFloatReg(regNum)) { + *value = co->getFloatReg(regNum); + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} +_LIBUNWIND_WEAK_ALIAS(__unw_get_fpreg, unw_get_fpreg) + +/// Set value of specified float register at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_set_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum, + unw_fpreg_t value) { +#if defined(_LIBUNWIND_ARM_EHABI) + _LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%llX)", + static_cast(cursor), regNum, value); +#else + _LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%g)", + static_cast(cursor), regNum, value); +#endif + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validFloatReg(regNum)) { + co->setFloatReg(regNum, value); + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} +_LIBUNWIND_WEAK_ALIAS(__unw_set_fpreg, unw_set_fpreg) + +/// Get location of specified register at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_get_save_loc(unw_cursor_t *cursor, int regNum, + unw_save_loc_t* location) +{ + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validReg(regNum)) { + // We only support memory locations, not register locations + location->u.addr = co->getRegLocation(regNum); + location->type = (location->u.addr == 0) ? UNW_SLT_NONE : UNW_SLT_MEMORY; + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} +_LIBUNWIND_WEAK_ALIAS(__unw_get_save_loc, unw_get_save_loc) + +/// Move cursor to next frame. +_LIBUNWIND_HIDDEN int __unw_step(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("__unw_step(cursor=%p)", static_cast(cursor)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->step(); +} +_LIBUNWIND_WEAK_ALIAS(__unw_step, unw_step) + +/// Get unwind info at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_get_proc_info(unw_cursor_t *cursor, + unw_proc_info_t *info) { + _LIBUNWIND_TRACE_API("__unw_get_proc_info(cursor=%p, &info=%p)", + static_cast(cursor), static_cast(info)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + co->getInfo(info); + if (info->end_ip == 0) + return UNW_ENOINFO; + else + return UNW_ESUCCESS; +} +_LIBUNWIND_WEAK_ALIAS(__unw_get_proc_info, unw_get_proc_info) + +/// Resume execution at cursor position (aka longjump). +_LIBUNWIND_HIDDEN int __unw_resume(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("__unw_resume(cursor=%p)", static_cast(cursor)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + co->jumpto(); + return UNW_EUNSPEC; +} +_LIBUNWIND_WEAK_ALIAS(__unw_resume, unw_resume) + +/// Get name of function at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_get_proc_name(unw_cursor_t *cursor, char *buf, + size_t bufLen, unw_word_t *offset) { + _LIBUNWIND_TRACE_API("__unw_get_proc_name(cursor=%p, &buf=%p, bufLen=%lu)", + static_cast(cursor), static_cast(buf), + static_cast(bufLen)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->getFunctionName(buf, bufLen, offset)) + return UNW_ESUCCESS; + else + return UNW_EUNSPEC; +} +_LIBUNWIND_WEAK_ALIAS(__unw_get_proc_name, unw_get_proc_name) + +/// Checks if a register is a floating-point register. +_LIBUNWIND_HIDDEN int __unw_is_fpreg(unw_cursor_t *cursor, + unw_regnum_t regNum) { + _LIBUNWIND_TRACE_API("__unw_is_fpreg(cursor=%p, regNum=%d)", + static_cast(cursor), regNum); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->validFloatReg(regNum); +} +_LIBUNWIND_WEAK_ALIAS(__unw_is_fpreg, unw_is_fpreg) + +/// Checks if a register is a floating-point register. +_LIBUNWIND_HIDDEN const char *__unw_regname(unw_cursor_t *cursor, + unw_regnum_t regNum) { + _LIBUNWIND_TRACE_API("__unw_regname(cursor=%p, regNum=%d)", + static_cast(cursor), regNum); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->getRegisterName(regNum); +} +_LIBUNWIND_WEAK_ALIAS(__unw_regname, unw_regname) + +/// Checks if current frame is signal trampoline. +_LIBUNWIND_HIDDEN int __unw_is_signal_frame(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("__unw_is_signal_frame(cursor=%p)", + static_cast(cursor)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->isSignalFrame(); +} +_LIBUNWIND_WEAK_ALIAS(__unw_is_signal_frame, unw_is_signal_frame) + +#ifdef __arm__ +// Save VFP registers d0-d15 using FSTMIADX instead of FSTMIADD +_LIBUNWIND_HIDDEN void __unw_save_vfp_as_X(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("__unw_get_fpreg_save_vfp_as_X(cursor=%p)", + static_cast(cursor)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->saveVFPAsX(); +} +_LIBUNWIND_WEAK_ALIAS(__unw_save_vfp_as_X, unw_save_vfp_as_X) +#endif + + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +/// SPI: walks cached DWARF entries +_LIBUNWIND_HIDDEN void __unw_iterate_dwarf_unwind_cache(void (*func)( + unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) { + _LIBUNWIND_TRACE_API("__unw_iterate_dwarf_unwind_cache(func=%p)", + reinterpret_cast(func)); + DwarfFDECache::iterateCacheEntries(func); +} +_LIBUNWIND_WEAK_ALIAS(__unw_iterate_dwarf_unwind_cache, + unw_iterate_dwarf_unwind_cache) + +/// IPI: for __register_frame() +void __unw_add_dynamic_fde(unw_word_t fde) { + CFI_Parser::FDE_Info fdeInfo; + CFI_Parser::CIE_Info cieInfo; + const char *message = CFI_Parser::decodeFDE( + LocalAddressSpace::sThisAddressSpace, + (LocalAddressSpace::pint_t) fde, &fdeInfo, &cieInfo); + if (message == NULL) { + // dynamically registered FDEs don't have a mach_header group they are in. + // Use fde as mh_group + unw_word_t mh_group = fdeInfo.fdeStart; + DwarfFDECache::add((LocalAddressSpace::pint_t)mh_group, + fdeInfo.pcStart, fdeInfo.pcEnd, + fdeInfo.fdeStart); + } else { + _LIBUNWIND_DEBUG_LOG("__unw_add_dynamic_fde: bad fde: %s", message); + } +} + +/// IPI: for __deregister_frame() +void __unw_remove_dynamic_fde(unw_word_t fde) { + // fde is own mh_group + DwarfFDECache::removeAllIn((LocalAddressSpace::pint_t)fde); +} +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +#endif // !defined(__USING_SJLJ_EXCEPTIONS__) + + + +// Add logging hooks in Debug builds only +#ifndef NDEBUG +#include + +_LIBUNWIND_HIDDEN +bool logAPIs() { + // do manual lock to avoid use of _cxa_guard_acquire or initializers + static bool checked = false; + static bool log = false; + if (!checked) { + log = (getenv("LIBUNWIND_PRINT_APIS") != NULL); + checked = true; + } + return log; +} + +_LIBUNWIND_HIDDEN +bool logUnwinding() { + // do manual lock to avoid use of _cxa_guard_acquire or initializers + static bool checked = false; + static bool log = false; + if (!checked) { + log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL); + checked = true; + } + return log; +} + +_LIBUNWIND_HIDDEN +bool logDWARF() { + // do manual lock to avoid use of _cxa_guard_acquire or initializers + static bool checked = false; + static bool log = false; + if (!checked) { + log = (getenv("LIBUNWIND_PRINT_DWARF") != NULL); + checked = true; + } + return log; +} + +#endif // NDEBUG + diff --git a/src/coreclr/nativeaot/libunwind/src/libunwind_ext.h b/src/coreclr/nativeaot/libunwind/src/libunwind_ext.h new file mode 100644 index 00000000000000..b240ba7fbcacda --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/libunwind_ext.h @@ -0,0 +1,66 @@ +//===------------------------ libunwind_ext.h -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Extensions to libunwind API. +// +//===----------------------------------------------------------------------===// + +#ifndef __LIBUNWIND_EXT__ +#define __LIBUNWIND_EXT__ + +#include "config.h" +#include +#include + +#define UNW_STEP_SUCCESS 1 +#define UNW_STEP_END 0 + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __unw_getcontext(unw_context_t *); +extern int __unw_init_local(unw_cursor_t *, unw_context_t *); +extern int __unw_step(unw_cursor_t *); +extern int __unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *); +extern int __unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *); +extern int __unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t, unw_word_t *); +extern int __unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t); +extern int __unw_resume(unw_cursor_t *); + +#ifdef __arm__ +/* Save VFP registers in FSTMX format (instead of FSTMD). */ +extern void __unw_save_vfp_as_X(unw_cursor_t *); +#endif + +extern const char *__unw_regname(unw_cursor_t *, unw_regnum_t); +extern int __unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *); +extern int __unw_is_fpreg(unw_cursor_t *, unw_regnum_t); +extern int __unw_is_signal_frame(unw_cursor_t *); +extern int __unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *); +extern int __unw_get_save_loc(unw_cursor_t *, int, unw_save_loc_t *); + +// SPI +extern void __unw_iterate_dwarf_unwind_cache(void (*func)( + unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)); + +// IPI +extern void __unw_add_dynamic_fde(unw_word_t fde); +extern void __unw_remove_dynamic_fde(unw_word_t fde); + +#if defined(_LIBUNWIND_ARM_EHABI) +extern const uint32_t* decode_eht_entry(const uint32_t*, size_t*, size_t*); +extern _Unwind_Reason_Code _Unwind_VRS_Interpret(_Unwind_Context *context, + const uint32_t *data, + size_t offset, size_t len); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __LIBUNWIND_EXT__ diff --git a/src/coreclr/nativeaot/libunwind/src/unwind_ext.h b/src/coreclr/nativeaot/libunwind/src/unwind_ext.h new file mode 100644 index 00000000000000..c40ce6a1610f4f --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/src/unwind_ext.h @@ -0,0 +1,37 @@ +//===-------------------------- unwind_ext.h ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// Extensions to unwind API. +// +//===----------------------------------------------------------------------===// + +#ifndef __UNWIND_EXT__ +#define __UNWIND_EXT__ + +#include "unwind.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// These platform specific functions to get and set the top context are +// implemented elsewhere. + +extern struct _Unwind_FunctionContext * +__Unwind_SjLj_GetTopOfFunctionStack(); + +extern void +__Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext *fc); + +#ifdef __cplusplus +} +#endif + +#endif // __UNWIND_EXT__ + + diff --git a/src/coreclr/nativeaot/libunwind/test/CMakeLists.txt b/src/coreclr/nativeaot/libunwind/test/CMakeLists.txt new file mode 100644 index 00000000000000..d902e3e829410d --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/test/CMakeLists.txt @@ -0,0 +1,35 @@ +include(AddLLVM) # for add_lit_testsuite +macro(pythonize_bool var) + if (${var}) + set(${var} True) + else() + set(${var} False) + endif() +endmacro() + +if (NOT DEFINED LIBCXX_ENABLE_SHARED) + set(LIBCXX_ENABLE_SHARED ON) +endif() + +pythonize_bool(LIBUNWIND_BUILD_32_BITS) +pythonize_bool(LIBCXX_ENABLE_SHARED) +pythonize_bool(LIBUNWIND_ENABLE_SHARED) +pythonize_bool(LIBUNWIND_ENABLE_THREADS) +pythonize_bool(LIBUNWIND_ENABLE_EXCEPTIONS) +pythonize_bool(LIBUNWIND_USE_COMPILER_RT) +pythonize_bool(LIBUNWIND_BUILD_EXTERNAL_THREAD_LIBRARY) +set(LIBUNWIND_TARGET_INFO "libcxx.test.target_info.LocalTI" CACHE STRING + "TargetInfo to use when setting up test environment.") +set(LIBUNWIND_EXECUTOR "None" CACHE STRING + "Executor to use when running tests.") + +set(AUTO_GEN_COMMENT "## Autogenerated by libunwind configuration.\n# Do not edit!") +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + @ONLY) + +add_lit_testsuite(check-unwind "Running libunwind tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${LIBUNWIND_TEST_DEPS} + ) diff --git a/src/coreclr/nativeaot/libunwind/test/alignment.pass.cpp b/src/coreclr/nativeaot/libunwind/test/alignment.pass.cpp new file mode 100644 index 00000000000000..b0da7f15513462 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/test/alignment.pass.cpp @@ -0,0 +1,28 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// The Itanium ABI requires that _Unwind_Exception objects are "double-word +// aligned". + +#include + +// EHABI : 8-byte aligned +// itanium: largest supported alignment for the system +#if defined(_LIBUNWIND_ARM_EHABI) +static_assert(alignof(_Unwind_Control_Block) == 8, + "_Unwind_Control_Block must be double-word aligned"); +#else +struct MaxAligned {} __attribute__((__aligned__)); +static_assert(alignof(_Unwind_Exception) == alignof(MaxAligned), + "_Unwind_Exception must be maximally aligned"); +#endif + +int main() +{ +} diff --git a/src/coreclr/nativeaot/libunwind/test/libunwind/__init__.py b/src/coreclr/nativeaot/libunwind/test/libunwind/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/src/coreclr/nativeaot/libunwind/test/libunwind/test/__init__.py b/src/coreclr/nativeaot/libunwind/test/libunwind/test/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/src/coreclr/nativeaot/libunwind/test/libunwind/test/config.py b/src/coreclr/nativeaot/libunwind/test/libunwind/test/config.py new file mode 100644 index 00000000000000..05e3f3cc21f31c --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/test/libunwind/test/config.py @@ -0,0 +1,68 @@ +#===----------------------------------------------------------------------===## +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===----------------------------------------------------------------------===## +import os +import sys + +from libcxx.test.config import Configuration as LibcxxConfiguration + + +class Configuration(LibcxxConfiguration): + # pylint: disable=redefined-outer-name + def __init__(self, lit_config, config): + super(Configuration, self).__init__(lit_config, config) + self.libunwind_src_root = None + self.libunwind_obj_root = None + self.abi_library_path = None + self.libcxx_src_root = None + + def configure_src_root(self): + self.libunwind_src_root = (self.get_lit_conf('libunwind_src_root') + or os.path.dirname(self.config.test_source_root)) + self.libcxx_src_root = (self.get_lit_conf('libcxx_src_root') + or os.path.join(self.libunwind_src_root, '..', 'libcxx')) + + def configure_obj_root(self): + self.libunwind_obj_root = self.get_lit_conf('libunwind_obj_root') + super(Configuration, self).configure_obj_root() + + def has_cpp_feature(self, feature, required_value): + return int(self.cxx.dumpMacros().get('__cpp_' + feature, 0)) >= required_value + + def configure_features(self): + super(Configuration, self).configure_features() + if not self.get_lit_bool('enable_exceptions', True): + self.config.available_features.add('libcxxabi-no-exceptions') + + def configure_compile_flags(self): + self.cxx.compile_flags += ['-DLIBUNWIND_NO_TIMER'] + if not self.get_lit_bool('enable_exceptions', True): + self.cxx.compile_flags += ['-fno-exceptions', '-DLIBUNWIND_HAS_NO_EXCEPTIONS'] + # Stack unwinding tests need unwinding tables and these are not + # generated by default on all Targets. + self.cxx.compile_flags += ['-funwind-tables'] + if not self.get_lit_bool('enable_threads', True): + self.cxx.compile_flags += ['-D_LIBUNWIND_HAS_NO_THREADS'] + self.config.available_features.add('libunwind-no-threads') + super(Configuration, self).configure_compile_flags() + + def configure_compile_flags_header_includes(self): + self.configure_config_site_header() + + libunwind_headers = self.get_lit_conf( + 'libunwind_headers', + os.path.join(self.libunwind_src_root, 'include')) + if not os.path.isdir(libunwind_headers): + self.lit_config.fatal("libunwind_headers='%s' is not a directory." + % libunwind_headers) + self.cxx.compile_flags += ['-I' + libunwind_headers] + + def configure_compile_flags_exceptions(self): + pass + + def configure_compile_flags_rtti(self): + pass diff --git a/src/coreclr/nativeaot/libunwind/test/libunwind_01.pass.cpp b/src/coreclr/nativeaot/libunwind/test/libunwind_01.pass.cpp new file mode 100644 index 00000000000000..6957d98f956d75 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/test/libunwind_01.pass.cpp @@ -0,0 +1,42 @@ +#include +#include + +void backtrace(int lower_bound) { + unw_context_t context; + unw_getcontext(&context); + + unw_cursor_t cursor; + unw_init_local(&cursor, &context); + + int n = 0; + do { + ++n; + if (n > 100) { + abort(); + } + } while (unw_step(&cursor) > 0); + + if (n < lower_bound) { + abort(); + } +} + +void test1(int i) { + backtrace(i); +} + +void test2(int i, int j) { + backtrace(i); + test1(j); +} + +void test3(int i, int j, int k) { + backtrace(i); + test2(j, k); +} + +int main() { + test1(1); + test2(1, 2); + test3(1, 2, 3); +} diff --git a/src/coreclr/nativeaot/libunwind/test/libunwind_02.pass.cpp b/src/coreclr/nativeaot/libunwind/test/libunwind_02.pass.cpp new file mode 100644 index 00000000000000..a0efd1df79fa45 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/test/libunwind_02.pass.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +#define EXPECTED_NUM_FRAMES 50 +#define NUM_FRAMES_UPPER_BOUND 100 + +_Unwind_Reason_Code callback(_Unwind_Context *context, void *cnt) { + (void)context; + int *i = (int *)cnt; + ++*i; + if (*i > NUM_FRAMES_UPPER_BOUND) { + abort(); + } + return _URC_NO_REASON; +} + +void test_backtrace() { + int n = 0; + _Unwind_Backtrace(&callback, &n); + if (n < EXPECTED_NUM_FRAMES) { + abort(); + } +} + +int test(int i) { + if (i == 0) { + test_backtrace(); + return 0; + } else { + return i + test(i - 1); + } +} + +int main() { + int total = test(50); + assert(total == 1275); +} diff --git a/src/coreclr/nativeaot/libunwind/test/lit.cfg b/src/coreclr/nativeaot/libunwind/test/lit.cfg new file mode 100644 index 00000000000000..1d284bdfd771a8 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/test/lit.cfg @@ -0,0 +1,70 @@ +# -*- Python -*- vim: set ft=python ts=4 sw=4 expandtab tw=79: + +# Configuration file for the 'lit' test runner. + + +import os +import site + +site.addsitedir(os.path.dirname(__file__)) + + +# Tell pylint that we know config and lit_config exist somewhere. +if 'PYLINT_IMPORT' in os.environ: + config = object() + lit_config = object() + +# name: The name of this test suite. +config.name = 'libunwind' + +# suffixes: A list of file extensions to treat as test files. +config.suffixes = ['.cpp', '.s'] + +# test_source_root: The root path where tests are located. +config.test_source_root = os.path.dirname(__file__) + +# needed to test libunwind with code that throws exceptions +config.enable_exceptions = True + +# Infer the libcxx_test_source_root for configuration import. +# If libcxx_source_root isn't specified in the config, assume that the libcxx +# and libunwind source directories are sibling directories. +libcxx_src_root = getattr(config, 'libcxx_src_root', None) +if not libcxx_src_root: + libcxx_src_root = os.path.join(config.test_source_root, '../../libcxx') +libcxx_test_src_root = os.path.join(libcxx_src_root, 'utils') +if os.path.isfile(os.path.join(libcxx_test_src_root, 'libcxx', '__init__.py')): + site.addsitedir(libcxx_test_src_root) +else: + lit_config.fatal('Could not find libcxx test directory for test imports' + ' in: %s' % libcxx_test_src_root) + +# Infer the test_exec_root from the libcxx_object root. +obj_root = getattr(config, 'libunwind_obj_root', None) + +# Check that the test exec root is known. +if obj_root is None: + import libcxx.test.config + libcxx.test.config.loadSiteConfig( + lit_config, config, 'libunwind_site_config', 'LIBUNWIND_SITE_CONFIG') + obj_root = getattr(config, 'libunwind_obj_root', None) + if obj_root is None: + import tempfile + obj_root = tempfile.mkdtemp(prefix='libunwind-testsuite-') + lit_config.warning('Creating temporary directory for object root: %s' % + obj_root) + +config.test_exec_root = os.path.join(obj_root, 'test') + +cfg_variant = getattr(config, 'configuration_variant', 'libunwind') +if cfg_variant: + lit_config.note('Using configuration variant: %s' % cfg_variant) + +# Load the Configuration class from the module name .test.config. +config_module_name = '.'.join([cfg_variant, 'test', 'config']) +config_module = __import__(config_module_name, fromlist=['Configuration']) + +configuration = config_module.Configuration(lit_config, config) +configuration.configure() +configuration.print_config_info() +config.test_format = configuration.get_test_format() diff --git a/src/coreclr/nativeaot/libunwind/test/lit.site.cfg.in b/src/coreclr/nativeaot/libunwind/test/lit.site.cfg.in new file mode 100644 index 00000000000000..34da72ac106848 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/test/lit.site.cfg.in @@ -0,0 +1,30 @@ +@AUTO_GEN_COMMENT@ +config.cxx_under_test = "@LIBUNWIND_COMPILER@" +config.project_obj_root = "@CMAKE_BINARY_DIR@" +config.libunwind_src_root = "@LIBUNWIND_SOURCE_DIR@" +config.libunwind_obj_root = "@LIBUNWIND_BINARY_DIR@" +config.abi_library_path = "@LIBUNWIND_LIBRARY_DIR@" +config.libcxx_src_root = "@LIBUNWIND_LIBCXX_PATH@" +config.libunwind_headers = "@LIBUNWIND_SOURCE_DIR@/include" +config.cxx_library_root = "@LIBUNWIND_LIBCXX_LIBRARY_PATH@" +config.llvm_unwinder = True +config.builtins_library = "@LIBUNWIND_BUILTINS_LIBRARY@" +config.enable_threads = @LIBUNWIND_ENABLE_THREADS@ +config.use_sanitizer = "@LLVM_USE_SANITIZER@" +config.enable_32bit = @LIBUNWIND_BUILD_32_BITS@ +config.target_info = "@LIBUNWIND_TARGET_INFO@" +config.test_linker_flags = "@LIBUNWIND_TEST_LINKER_FLAGS@" +config.test_compiler_flags = "@LIBUNWIND_TEST_COMPILER_FLAGS@" +config.executor = "@LIBUNWIND_EXECUTOR@" +config.libunwind_shared = @LIBUNWIND_ENABLE_SHARED@ +config.enable_shared = @LIBCXX_ENABLE_SHARED@ +config.enable_exceptions = @LIBUNWIND_ENABLE_EXCEPTIONS@ +config.host_triple = "@LLVM_HOST_TRIPLE@" +config.target_triple = "@TARGET_TRIPLE@" +config.use_target = bool("@LIBUNWIND_TARGET_TRIPLE@") +config.sysroot = "@LIBUNWIND_SYSROOT@" +config.gcc_toolchain = "@LIBUNWIND_GCC_TOOLCHAIN@" +config.cxx_ext_threads = @LIBUNWIND_BUILD_EXTERNAL_THREAD_LIBRARY@ + +# Let the main config do the real work. +lit_config.load_config(config, "@LIBUNWIND_SOURCE_DIR@/test/lit.cfg") diff --git a/src/coreclr/nativeaot/libunwind/test/unw_getcontext.pass.cpp b/src/coreclr/nativeaot/libunwind/test/unw_getcontext.pass.cpp new file mode 100644 index 00000000000000..b012706a0bf925 --- /dev/null +++ b/src/coreclr/nativeaot/libunwind/test/unw_getcontext.pass.cpp @@ -0,0 +1,8 @@ +#include +#include + +int main() { + unw_context_t context; + int ret = unw_getcontext(&context); + assert(ret == UNW_ESUCCESS); +} diff --git a/src/coreclr/nativeaot/nativeaot.sln b/src/coreclr/nativeaot/nativeaot.sln new file mode 100644 index 00000000000000..72d2ecf74b7edb --- /dev/null +++ b/src/coreclr/nativeaot/nativeaot.sln @@ -0,0 +1,269 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30421.15 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib", "System.Private.CoreLib\src\System.Private.CoreLib.csproj", "{E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.DisabledReflection", "System.Private.DisabledReflection\src\System.Private.DisabledReflection.csproj", "{ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.Interop", "System.Private.Interop\src\System.Private.Interop.csproj", "{BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.Reflection.Core", "System.Private.Reflection.Core\src\System.Private.Reflection.Core.csproj", "{6147AF1A-5054-492A-9309-FA868A184414}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.Reflection.Execution", "System.Private.Reflection.Execution\src\System.Private.Reflection.Execution.csproj", "{7498DD7C-76C1-4912-AF72-DA84E05B568F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.Reflection.Metadata", "System.Private.Reflection.Metadata\src\System.Private.Reflection.Metadata.csproj", "{C0245BD9-6AE2-47A5-BC41-DB6F777423AF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.StackTraceMetadata", "System.Private.StackTraceMetadata\src\System.Private.StackTraceMetadata.csproj", "{33CAE331-16EE-443C-A0CC-4337B94A02AD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.TypeLoader", "System.Private.TypeLoader\src\System.Private.TypeLoader.csproj", "{3E43ACA2-073E-4A66-BA9C-417C5F83D430}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.CoreLib", "Test.CoreLib\src\Test.CoreLib.csproj", "{C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "System.Private.CoreLib.Shared", "..\..\libraries\System.Private.CoreLib\src\System.Private.CoreLib.Shared.shproj", "{977524B8-92D8-4DFC-91E4-11A0582B81BF}" +EndProject +Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\..\libraries\System.Private.CoreLib\src\System.Private.CoreLib.Shared.projitems*{977524b8-92d8-4dfc-91e4-11a0582b81bf}*SharedItemsImports = 13 + ..\..\libraries\System.Private.CoreLib\src\System.Private.CoreLib.Shared.projitems*{e4bc768b-f97d-4a8f-9391-b65df3eb47c6}*SharedItemsImports = 5 + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Checked|arm = Checked|arm + Checked|arm64 = Checked|arm64 + Checked|x64 = Checked|x64 + Checked|x86 = Checked|x86 + Debug|arm = Debug|arm + Debug|arm64 = Debug|arm64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|arm = Release|arm + Release|arm64 = Release|arm64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Checked|arm.ActiveCfg = Checked|arm + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Checked|arm.Build.0 = Checked|arm + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Checked|arm64.ActiveCfg = Checked|arm64 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Checked|arm64.Build.0 = Checked|arm64 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Checked|x64.ActiveCfg = Checked|x64 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Checked|x64.Build.0 = Checked|x64 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Checked|x86.ActiveCfg = Checked|x86 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Checked|x86.Build.0 = Checked|x86 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Debug|arm.ActiveCfg = Debug|arm + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Debug|arm.Build.0 = Debug|arm + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Debug|arm64.ActiveCfg = Debug|arm64 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Debug|arm64.Build.0 = Debug|arm64 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Debug|x64.ActiveCfg = Debug|x64 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Debug|x64.Build.0 = Debug|x64 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Debug|x86.ActiveCfg = Debug|x86 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Debug|x86.Build.0 = Debug|x86 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Release|arm.ActiveCfg = Release|arm + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Release|arm.Build.0 = Release|arm + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Release|arm64.ActiveCfg = Release|arm64 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Release|arm64.Build.0 = Release|arm64 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Release|x64.ActiveCfg = Release|x64 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Release|x64.Build.0 = Release|x64 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Release|x86.ActiveCfg = Release|x86 + {E4BC768B-F97D-4A8F-9391-B65DF3EB47C6}.Release|x86.Build.0 = Release|x86 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Checked|arm.ActiveCfg = Checked|arm + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Checked|arm.Build.0 = Checked|arm + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Checked|arm64.ActiveCfg = Checked|arm64 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Checked|arm64.Build.0 = Checked|arm64 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Checked|x64.ActiveCfg = Checked|x64 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Checked|x64.Build.0 = Checked|x64 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Checked|x86.ActiveCfg = Checked|x86 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Checked|x86.Build.0 = Checked|x86 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Debug|arm.ActiveCfg = Debug|arm + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Debug|arm.Build.0 = Debug|arm + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Debug|arm64.ActiveCfg = Debug|arm64 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Debug|arm64.Build.0 = Debug|arm64 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Debug|x64.ActiveCfg = Debug|x64 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Debug|x64.Build.0 = Debug|x64 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Debug|x86.ActiveCfg = Debug|x86 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Debug|x86.Build.0 = Debug|x86 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Release|arm.ActiveCfg = Release|arm + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Release|arm.Build.0 = Release|arm + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Release|arm64.ActiveCfg = Release|arm64 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Release|arm64.Build.0 = Release|arm64 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Release|x64.ActiveCfg = Release|x64 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Release|x64.Build.0 = Release|x64 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Release|x86.ActiveCfg = Release|x86 + {ADA691AE-4E1F-4212-97E6-51A27EFCE7E4}.Release|x86.Build.0 = Release|x86 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Checked|arm.ActiveCfg = Checked|arm + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Checked|arm.Build.0 = Checked|arm + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Checked|arm64.ActiveCfg = Checked|arm64 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Checked|arm64.Build.0 = Checked|arm64 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Checked|x64.ActiveCfg = Checked|x64 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Checked|x64.Build.0 = Checked|x64 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Checked|x86.ActiveCfg = Checked|x86 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Checked|x86.Build.0 = Checked|x86 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Debug|arm.ActiveCfg = Debug|arm + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Debug|arm.Build.0 = Debug|arm + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Debug|arm64.ActiveCfg = Debug|arm64 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Debug|arm64.Build.0 = Debug|arm64 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Debug|x64.ActiveCfg = Debug|x64 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Debug|x64.Build.0 = Debug|x64 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Debug|x86.ActiveCfg = Debug|x86 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Debug|x86.Build.0 = Debug|x86 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Release|arm.ActiveCfg = Release|arm + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Release|arm.Build.0 = Release|arm + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Release|arm64.ActiveCfg = Release|arm64 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Release|arm64.Build.0 = Release|arm64 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Release|x64.ActiveCfg = Release|x64 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Release|x64.Build.0 = Release|x64 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Release|x86.ActiveCfg = Release|x86 + {BAF9BBDF-0DFA-4E0D-AB3C-F07657B9EBB0}.Release|x86.Build.0 = Release|x86 + {6147AF1A-5054-492A-9309-FA868A184414}.Checked|arm.ActiveCfg = Checked|arm + {6147AF1A-5054-492A-9309-FA868A184414}.Checked|arm.Build.0 = Checked|arm + {6147AF1A-5054-492A-9309-FA868A184414}.Checked|arm64.ActiveCfg = Checked|arm64 + {6147AF1A-5054-492A-9309-FA868A184414}.Checked|arm64.Build.0 = Checked|arm64 + {6147AF1A-5054-492A-9309-FA868A184414}.Checked|x64.ActiveCfg = Checked|x64 + {6147AF1A-5054-492A-9309-FA868A184414}.Checked|x64.Build.0 = Checked|x64 + {6147AF1A-5054-492A-9309-FA868A184414}.Checked|x86.ActiveCfg = Checked|x86 + {6147AF1A-5054-492A-9309-FA868A184414}.Checked|x86.Build.0 = Checked|x86 + {6147AF1A-5054-492A-9309-FA868A184414}.Debug|arm.ActiveCfg = Debug|arm + {6147AF1A-5054-492A-9309-FA868A184414}.Debug|arm.Build.0 = Debug|arm + {6147AF1A-5054-492A-9309-FA868A184414}.Debug|arm64.ActiveCfg = Debug|arm64 + {6147AF1A-5054-492A-9309-FA868A184414}.Debug|arm64.Build.0 = Debug|arm64 + {6147AF1A-5054-492A-9309-FA868A184414}.Debug|x64.ActiveCfg = Debug|x64 + {6147AF1A-5054-492A-9309-FA868A184414}.Debug|x64.Build.0 = Debug|x64 + {6147AF1A-5054-492A-9309-FA868A184414}.Debug|x86.ActiveCfg = Debug|x86 + {6147AF1A-5054-492A-9309-FA868A184414}.Debug|x86.Build.0 = Debug|x86 + {6147AF1A-5054-492A-9309-FA868A184414}.Release|arm.ActiveCfg = Release|arm + {6147AF1A-5054-492A-9309-FA868A184414}.Release|arm.Build.0 = Release|arm + {6147AF1A-5054-492A-9309-FA868A184414}.Release|arm64.ActiveCfg = Release|arm64 + {6147AF1A-5054-492A-9309-FA868A184414}.Release|arm64.Build.0 = Release|arm64 + {6147AF1A-5054-492A-9309-FA868A184414}.Release|x64.ActiveCfg = Release|x64 + {6147AF1A-5054-492A-9309-FA868A184414}.Release|x64.Build.0 = Release|x64 + {6147AF1A-5054-492A-9309-FA868A184414}.Release|x86.ActiveCfg = Release|x86 + {6147AF1A-5054-492A-9309-FA868A184414}.Release|x86.Build.0 = Release|x86 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Checked|arm.ActiveCfg = Checked|arm + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Checked|arm.Build.0 = Checked|arm + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Checked|arm64.ActiveCfg = Checked|arm64 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Checked|arm64.Build.0 = Checked|arm64 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Checked|x64.ActiveCfg = Checked|x64 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Checked|x64.Build.0 = Checked|x64 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Checked|x86.ActiveCfg = Checked|x86 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Checked|x86.Build.0 = Checked|x86 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Debug|arm.ActiveCfg = Debug|arm + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Debug|arm.Build.0 = Debug|arm + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Debug|arm64.ActiveCfg = Debug|arm64 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Debug|arm64.Build.0 = Debug|arm64 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Debug|x64.ActiveCfg = Debug|x64 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Debug|x64.Build.0 = Debug|x64 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Debug|x86.ActiveCfg = Debug|x86 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Debug|x86.Build.0 = Debug|x86 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Release|arm.ActiveCfg = Release|arm + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Release|arm.Build.0 = Release|arm + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Release|arm64.ActiveCfg = Release|arm64 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Release|arm64.Build.0 = Release|arm64 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Release|x64.ActiveCfg = Release|x64 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Release|x64.Build.0 = Release|x64 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Release|x86.ActiveCfg = Release|x86 + {7498DD7C-76C1-4912-AF72-DA84E05B568F}.Release|x86.Build.0 = Release|x86 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Checked|arm.ActiveCfg = Checked|arm + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Checked|arm.Build.0 = Checked|arm + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Checked|arm64.ActiveCfg = Checked|arm64 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Checked|arm64.Build.0 = Checked|arm64 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Checked|x64.ActiveCfg = Checked|x64 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Checked|x64.Build.0 = Checked|x64 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Checked|x86.ActiveCfg = Checked|x86 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Checked|x86.Build.0 = Checked|x86 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Debug|arm.ActiveCfg = Debug|arm + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Debug|arm.Build.0 = Debug|arm + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Debug|arm64.ActiveCfg = Debug|arm64 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Debug|arm64.Build.0 = Debug|arm64 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Debug|x64.ActiveCfg = Debug|x64 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Debug|x64.Build.0 = Debug|x64 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Debug|x86.ActiveCfg = Debug|x86 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Debug|x86.Build.0 = Debug|x86 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Release|arm.ActiveCfg = Release|arm + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Release|arm.Build.0 = Release|arm + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Release|arm64.ActiveCfg = Release|arm64 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Release|arm64.Build.0 = Release|arm64 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Release|x64.ActiveCfg = Release|x64 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Release|x64.Build.0 = Release|x64 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Release|x86.ActiveCfg = Release|x86 + {C0245BD9-6AE2-47A5-BC41-DB6F777423AF}.Release|x86.Build.0 = Release|x86 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Checked|arm.ActiveCfg = Checked|arm + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Checked|arm.Build.0 = Checked|arm + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Checked|arm64.ActiveCfg = Checked|arm64 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Checked|arm64.Build.0 = Checked|arm64 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Checked|x64.ActiveCfg = Checked|x64 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Checked|x64.Build.0 = Checked|x64 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Checked|x86.ActiveCfg = Checked|x86 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Checked|x86.Build.0 = Checked|x86 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Debug|arm.ActiveCfg = Debug|arm + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Debug|arm.Build.0 = Debug|arm + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Debug|arm64.ActiveCfg = Debug|arm64 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Debug|arm64.Build.0 = Debug|arm64 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Debug|x64.ActiveCfg = Debug|x64 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Debug|x64.Build.0 = Debug|x64 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Debug|x86.ActiveCfg = Debug|x86 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Debug|x86.Build.0 = Debug|x86 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Release|arm.ActiveCfg = Release|arm + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Release|arm.Build.0 = Release|arm + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Release|arm64.ActiveCfg = Release|arm64 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Release|arm64.Build.0 = Release|arm64 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Release|x64.ActiveCfg = Release|x64 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Release|x64.Build.0 = Release|x64 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Release|x86.ActiveCfg = Release|x86 + {33CAE331-16EE-443C-A0CC-4337B94A02AD}.Release|x86.Build.0 = Release|x86 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Checked|arm.ActiveCfg = Checked|arm + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Checked|arm.Build.0 = Checked|arm + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Checked|arm64.ActiveCfg = Checked|arm64 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Checked|arm64.Build.0 = Checked|arm64 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Checked|x64.ActiveCfg = Checked|x64 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Checked|x64.Build.0 = Checked|x64 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Checked|x86.ActiveCfg = Checked|x86 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Checked|x86.Build.0 = Checked|x86 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Debug|arm.ActiveCfg = Debug|arm + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Debug|arm.Build.0 = Debug|arm + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Debug|arm64.ActiveCfg = Debug|arm64 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Debug|arm64.Build.0 = Debug|arm64 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Debug|x64.ActiveCfg = Debug|x64 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Debug|x64.Build.0 = Debug|x64 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Debug|x86.ActiveCfg = Debug|x86 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Debug|x86.Build.0 = Debug|x86 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Release|arm.ActiveCfg = Release|arm + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Release|arm.Build.0 = Release|arm + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Release|arm64.ActiveCfg = Release|arm64 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Release|arm64.Build.0 = Release|arm64 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Release|x64.ActiveCfg = Release|x64 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Release|x64.Build.0 = Release|x64 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Release|x86.ActiveCfg = Release|x86 + {3E43ACA2-073E-4A66-BA9C-417C5F83D430}.Release|x86.Build.0 = Release|x86 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Checked|arm.ActiveCfg = Checked|arm + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Checked|arm.Build.0 = Checked|arm + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Checked|arm64.ActiveCfg = Checked|arm64 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Checked|arm64.Build.0 = Checked|arm64 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Checked|x64.ActiveCfg = Checked|x64 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Checked|x64.Build.0 = Checked|x64 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Checked|x86.ActiveCfg = Checked|x86 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Checked|x86.Build.0 = Checked|x86 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Debug|arm.ActiveCfg = Debug|arm + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Debug|arm.Build.0 = Debug|arm + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Debug|arm64.ActiveCfg = Debug|arm64 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Debug|arm64.Build.0 = Debug|arm64 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Debug|x64.ActiveCfg = Debug|x64 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Debug|x64.Build.0 = Debug|x64 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Debug|x86.ActiveCfg = Debug|x86 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Debug|x86.Build.0 = Debug|x86 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Release|arm.ActiveCfg = Release|arm + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Release|arm.Build.0 = Release|arm + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Release|arm64.ActiveCfg = Release|arm64 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Release|arm64.Build.0 = Release|arm64 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Release|x64.ActiveCfg = Release|x64 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Release|x64.Build.0 = Release|x64 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Release|x86.ActiveCfg = Release|x86 + {C3371E09-E8A6-4F9E-B4CB-B1CE3F4FCC51}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {81B16D59-928B-49C1-839D-10E4747B0DC0} + EndGlobalSection +EndGlobal diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index ede5efe550d8de..237b75275cec96 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -2739,6 +2739,11 @@ LPCVOID PALAPI PAL_GetSymbolModuleBase(PVOID symbol); +PALIMPORT +int +PALAPI +PAL_CopyModuleData(PVOID moduleBase, PVOID destinationBufferStart, PVOID destinationBufferEnd);; + PALIMPORT LPCSTR PALAPI @@ -3252,6 +3257,54 @@ FORCEINLINE void PAL_ArmInterlockedOperationBarrier() #endif // HOST_ARM64 } +/*++ +Function: +InterlockedAdd + +The InterlockedAdd function adds the value of the specified variable +with another specified value. The function prevents more than one thread +from using the same variable simultaneously. + +Parameters + +lpAddend +[in/out] Pointer to the variable to add. + +lpAddend +[in] The value to add. + +Return Values + +The return value is the resulting added value. +--*/ +EXTERN_C +PALIMPORT +inline +LONG +PALAPI +InterlockedAdd( + IN OUT LONG volatile *lpAddend, + IN LONG value) +{ + LONG result = __sync_add_and_fetch(lpAddend, value); + PAL_ArmInterlockedOperationBarrier(); + return result; +} + +EXTERN_C +PALIMPORT +inline +LONGLONG +PALAPI +InterlockedAdd64( + IN OUT LONGLONG volatile *lpAddend, + IN LONGLONG value) +{ + LONGLONG result = __sync_add_and_fetch(lpAddend, value); + PAL_ArmInterlockedOperationBarrier(); + return result; +} + /*++ Function: InterlockedIncrement diff --git a/src/coreclr/pal/src/loader/module.cpp b/src/coreclr/pal/src/loader/module.cpp index 7d15fa4c496fea..e792f75a2b22e1 100644 --- a/src/coreclr/pal/src/loader/module.cpp +++ b/src/coreclr/pal/src/loader/module.cpp @@ -46,6 +46,8 @@ SET_DEFAULT_DEBUG_CHANNEL(LOADER); // some headers have code with asserts, so do #ifdef __APPLE__ #include #include +#else +#include #endif // __APPLE__ #include @@ -55,6 +57,8 @@ SET_DEFAULT_DEBUG_CHANNEL(LOADER); // some headers have code with asserts, so do #include #endif +#include + using namespace CorUnix; // In safemath.h, Template SafeInt uses macro _ASSERTE, which need to use variable @@ -913,6 +917,112 @@ PAL_GetSymbolModuleBase(PVOID symbol) return retval; } +struct CopyModuleDataParam +{ + uint8_t* destination_buffer_start; + uint8_t* destination_buffer_end; + uint8_t* module_base; + int result; +}; + +void handle_image_range(uint8_t* source_start, size_t size, struct CopyModuleDataParam* param) +{ + uint8_t* source_end = source_start + size; + if (param->destination_buffer_start != NULL) + { + size_t offset = source_start - param->module_base; + uint8_t* destination_start = ((uint8_t*)(param->destination_buffer_start)) + offset; + uint8_t* destination_end = destination_start + size; + _ASSERTE(destination_end <= param->destination_buffer_end); + if (destination_end <= param->destination_buffer_end) + { + memcpy(destination_start, source_start, size); + } + } + param->result = std::max(param->result, (int)(source_end - param->module_base)); +} + +#ifdef __APPLE__ +PALIMPORT +int +PALAPI +PAL_CopyModuleData(PVOID moduleBase, PVOID destinationBufferStart, PVOID destinationBufferEnd) +{ + CopyModuleDataParam param; + param.module_base = (uint8_t*)moduleBase; + param.destination_buffer_start = (uint8_t*)destinationBufferStart; + param.destination_buffer_end = (uint8_t*)destinationBufferEnd; + uint32_t count = _dyld_image_count(); + for (uint32_t i = 0; i < count; i++) + { + const struct mach_header * header = _dyld_get_image_header(i); + if (PAL_GetSymbolModuleBase((void*)header) == moduleBase) + { + intptr_t slide = _dyld_get_image_vmaddr_slide(i); + + struct load_command * cmd = (struct load_command * )((char * ) header + sizeof(struct mach_header)); + if(header->magic == MH_MAGIC_64) + { + cmd = (struct load_command*)((char *)header + sizeof(struct mach_header_64)); + } + + for (uint32_t j = 0; j < header -> ncmds; j++) + { + if (cmd -> cmd == LC_SEGMENT) + { + struct segment_command * seg = (struct segment_command *) cmd; + size_t size = seg->vmsize; + uint8_t* source_start = (uint8_t*)(seg->vmaddr + slide); + handle_image_range(source_start, size, ¶m); + } + else if (cmd -> cmd == LC_SEGMENT_64) + { + struct segment_command_64 * seg = (struct segment_command_64 * ) cmd; + size_t size = seg->vmsize; + uint8_t* source_start = (uint8_t*)(seg->vmaddr + slide); + handle_image_range(source_start, size, ¶m); + } + cmd = (struct load_command*)((uint8_t*)cmd + cmd->cmdsize); + } + } + } + return param.result; +} +#else +static int CopyModuleDataCallback(struct dl_phdr_info *info, size_t size, void *data) +{ + CopyModuleDataParam* param = (CopyModuleDataParam*)data; + if (info->dlpi_addr == (size_t)param->module_base) + { + for (int j = 0; j < info->dlpi_phnum; j++) + { + if (info->dlpi_phdr[j].p_type == PT_LOAD) + { + Elf32_Word size = info->dlpi_phdr[j].p_memsz; + uint8_t* source_start = (uint8_t*)(info->dlpi_addr + info->dlpi_phdr[j].p_vaddr); + handle_image_range(source_start, size, param); + } + } + return 1; + } + return 0; +} + +PALIMPORT +int +PALAPI +PAL_CopyModuleData(PVOID moduleBase, PVOID destinationBufferStart, PVOID destinationBufferEnd) +{ + CopyModuleDataParam param; + param.destination_buffer_start = (uint8_t*)destinationBufferStart; + param.destination_buffer_end = (uint8_t*)destinationBufferEnd; + param.module_base = (uint8_t*)moduleBase; + param.result = 0; + dl_iterate_phdr(CopyModuleDataCallback, ¶m); + return param.result; +} +#endif + /*++ PAL_GetLoadLibraryError diff --git a/src/coreclr/pal/src/map/map.cpp b/src/coreclr/pal/src/map/map.cpp index af97934f7ca845..cf93acfac201e3 100644 --- a/src/coreclr/pal/src/map/map.cpp +++ b/src/coreclr/pal/src/map/map.cpp @@ -61,7 +61,7 @@ LIST_ENTRY MappedViewList; #ifndef CORECLR static PAL_ERROR MAPCreateTempFile(CPalThread *, PINT, PSZ); #endif // !CORECLR -static PAL_ERROR MAPGrowLocalFile(INT, UINT); +static PAL_ERROR MAPGrowLocalFile(INT, off_t); static PMAPPED_VIEW_LIST MAPGetViewForAddress( LPCVOID ); static PAL_ERROR MAPDesiredAccessAllowed( DWORD, DWORD, DWORD ); @@ -389,6 +389,7 @@ CorUnix::InternalCreateFileMapping( INT UnixFd = -1; BOOL bPALCreatedTempFile = FALSE; UINT nFileSize = 0; + off_t maximumSize; // // Validate parameters @@ -401,13 +402,6 @@ CorUnix::InternalCreateFileMapping( goto ExitInternalCreateFileMapping; } - if (0 != dwMaximumSizeHigh) - { - ASSERT("dwMaximumSizeHigh is always 0.\n"); - palError = ERROR_INVALID_PARAMETER; - goto ExitInternalCreateFileMapping; - } - if (PAGE_READWRITE != flProtect && PAGE_READONLY != flProtect && PAGE_WRITECOPY != flProtect) @@ -419,7 +413,8 @@ CorUnix::InternalCreateFileMapping( goto ExitInternalCreateFileMapping; } - if (hFile == INVALID_HANDLE_VALUE && 0 == dwMaximumSizeLow) + if (hFile == INVALID_HANDLE_VALUE && + 0 == dwMaximumSizeLow && 0 == dwMaximumSizeHigh) { ERROR( "If hFile is INVALID_HANDLE_VALUE, then you must specify a size.\n" ); palError = ERROR_INVALID_PARAMETER; @@ -432,6 +427,8 @@ CorUnix::InternalCreateFileMapping( palError = ERROR_INVALID_PARAMETER; goto ExitInternalCreateFileMapping; } + + maximumSize = ((off_t)dwMaximumSizeHigh << 32) | (off_t)dwMaximumSizeLow; palError = g_pObjectManager->AllocateObject( pThread, @@ -597,8 +594,7 @@ CorUnix::InternalCreateFileMapping( goto ExitInternalCreateFileMapping; } - if ( 0 == UnixFileInformation.st_size && - 0 == dwMaximumSizeHigh && 0 == dwMaximumSizeLow ) + if ( 0 == UnixFileInformation.st_size && 0 == maximumSize ) { ERROR( "The file cannot be a zero length file.\n" ); palError = ERROR_FILE_INVALID; @@ -606,7 +602,7 @@ CorUnix::InternalCreateFileMapping( } if ( INVALID_HANDLE_VALUE != hFile && - dwMaximumSizeLow > (DWORD) UnixFileInformation.st_size && + maximumSize > UnixFileInformation.st_size && ( PAGE_READONLY == flProtect || PAGE_WRITECOPY == flProtect ) ) { /* In this situation, Windows returns an error, because the @@ -616,12 +612,12 @@ CorUnix::InternalCreateFileMapping( goto ExitInternalCreateFileMapping; } - if ( (DWORD) UnixFileInformation.st_size < dwMaximumSizeLow ) + if ( UnixFileInformation.st_size < maximumSize ) { TRACE( "Growing the size of file on disk to match requested size.\n" ); /* Need to grow the file on disk to match size. */ - palError = MAPGrowLocalFile(UnixFd, dwMaximumSizeLow); + palError = MAPGrowLocalFile(UnixFd, maximumSize); if (NO_ERROR != palError) { ERROR( "Unable to grow the file on disk.\n" ); @@ -630,8 +626,8 @@ CorUnix::InternalCreateFileMapping( } } - nFileSize = ( 0 == dwMaximumSizeLow && 0 == dwMaximumSizeHigh ) ? - UnixFileInformation.st_size : dwMaximumSizeLow; + nFileSize = ( 0 == maximumSize ) ? + UnixFileInformation.st_size : maximumSize; pImmutableData->MaxSize = nFileSize; pImmutableData->flProtect = flProtect; @@ -1589,7 +1585,7 @@ Function : Grows the file on disk to match the specified size. --*/ -static PAL_ERROR MAPGrowLocalFile( INT UnixFD, UINT NewSize ) +static PAL_ERROR MAPGrowLocalFile( INT UnixFD, off_t NewSize ) { PAL_ERROR palError = NO_ERROR; INT TruncateRetVal = -1; diff --git a/src/coreclr/runtime.proj b/src/coreclr/runtime.proj index 39b207417d3573..7c8a68a580b6f3 100644 --- a/src/coreclr/runtime.proj +++ b/src/coreclr/runtime.proj @@ -41,6 +41,7 @@ + <_CoreClrBuildArg Condition="'$(ClrHostsSubset)' == 'true'" Include="-component hosts" /> <_CoreClrBuildArg Condition="'$(ClrRuntimeSubset)' == 'true'" Include="-component runtime" /> <_CoreClrBuildArg Condition="'$(ClrJitSubset)' == 'true'" Include="-component jit" /> <_CoreClrBuildArg Condition="'$(ClrPalTestsSubset)' == 'true'" Include="-component paltests" /> diff --git a/src/coreclr/scripts/superpmi_aspnet.py b/src/coreclr/scripts/superpmi_aspnet.py index f2e01ad6e2ae5e..e2aeecf2a478d8 100644 --- a/src/coreclr/scripts/superpmi_aspnet.py +++ b/src/coreclr/scripts/superpmi_aspnet.py @@ -174,7 +174,9 @@ def build_and_run(coreclr_args): # note tricks to get one element tuples - runtime_options_list = [("Dummy=0",), ("TieredCompilation=0", ), ("TieredPGO=1", "TC_QuickJitForLoops=1"), ("TieredPGO=1", "TC_QuickJitForLoops=1", "ReadyToRun=0")] + runtime_options_list = [("Dummy=0",), ("TieredCompilation=0", ), ("TieredPGO=1", "TC_QuickJitForLoops=1"), ("TieredPGO=1", "TC_QuickJitForLoops=1", "ReadyToRun=0"), + ("TC_QuickJitForLoops=1", "ReadyToRun=0", "TC_OnStackReplacement=1", "OSR_HitLimit=0", "TC_OnStackReplacement_InitialCounter=0"), + ("TieredPGO=1", "TC_QuickJitForLoops=1", "ReadyToRun=0", "TC_OnStackReplacement=1", "OSR_HitLimit=0", "TC_OnStackReplacement_InitialCounter=100")] # runtime_options_list = [("TieredCompilation=0", )] diff --git a/src/coreclr/tools/CMakeLists.txt b/src/coreclr/tools/CMakeLists.txt index 8bc7696b332ddc..43fc4d02f88b1e 100644 --- a/src/coreclr/tools/CMakeLists.txt +++ b/src/coreclr/tools/CMakeLists.txt @@ -4,3 +4,6 @@ if (CLR_CMAKE_TARGET_WIN32 AND NOT CLR_CMAKE_CROSS_ARCH) install(EXPORT dactabletools DESTINATION dactabletools COMPONENT crosscomponents) endif() +if ((NOT CLR_CMAKE_TARGET_ALPINE_LINUX) AND (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64)) + add_subdirectory(StressLogAnalyzer) +endif() \ No newline at end of file diff --git a/src/coreclr/tools/Common/Compiler/Logger.cs b/src/coreclr/tools/Common/Compiler/Logger.cs index af84d21ab3b4bb..eb77c7cd681e12 100644 --- a/src/coreclr/tools/Common/Compiler/Logger.cs +++ b/src/coreclr/tools/Common/Compiler/Logger.cs @@ -177,7 +177,7 @@ internal bool IsSingleWarn(ModuleDesc owningModule, string messageSubcategory) { if (_aotWarnedAssemblies.Add(assemblyName)) { - LogWarning($"Assembly '{assemblyName}' produced AOT analysis warnings.", 9702, GetModuleFileName(owningModule)); + LogWarning($"Assembly '{assemblyName}' produced AOT analysis warnings.", 3053, GetModuleFileName(owningModule)); } } } diff --git a/src/coreclr/tools/Common/Compiler/Logging/DocumentationSignatureGenerator.PartVisitor.cs b/src/coreclr/tools/Common/Compiler/Logging/DocumentationSignatureGenerator.PartVisitor.cs new file mode 100644 index 00000000000000..fbb5ec5749ce20 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/Logging/DocumentationSignatureGenerator.PartVisitor.cs @@ -0,0 +1,224 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Text; + +using Internal.TypeSystem; + +namespace ILCompiler.Logging +{ + + public sealed partial class DocumentationSignatureGenerator + { + /// + /// A visitor that generates the part of the documentation comment after the initial type + /// and colon. + /// Adapted from Roslyn's DocumentattionCommentIDVisitor.PartVisitor: + /// https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs + /// + internal sealed class PartVisitor : TypeNameFormatter + { + internal static readonly PartVisitor Instance = new PartVisitor(); + + private PartVisitor() + { + } + + public override void AppendName(StringBuilder builder, ArrayType arrayType) + { + AppendName(builder, arrayType.ElementType); + + // Rank-one arrays are displayed different than rectangular arrays + if (arrayType.IsSzArray) + { + builder.Append("[]"); + } + else + { + // C# arrays only support zero lower bounds + builder.Append("[0:"); + for (int i = 1; i < arrayType.Rank; i++) + { + //if (arrayType.Dimensions[0].LowerBound != 0) + // throw new NotImplementedException(); + builder.Append(",0:"); + } + + builder.Append(']'); + } + } + +#if false + public void VisitField(FieldDefinition field, StringBuilder builder) + { + VisitTypeReference(field.DeclaringType, builder); + builder.Append('.').Append(field.Name); + } + + private void VisitParameters(IEnumerable parameters, bool isVararg, StringBuilder builder) + { + builder.Append('('); + bool needsComma = false; + + foreach (var parameter in parameters) + { + if (needsComma) + builder.Append(','); + + // byrefs are tracked on the parameter type, not the parameter, + // so we don't have VisitParameter that Roslyn uses. + VisitTypeReference(parameter.ParameterType, builder); + needsComma = true; + } + + // note: the C# doc comment generator outputs an extra comma for varargs + // methods that also have fixed parameters + if (isVararg && needsComma) + builder.Append(','); + + builder.Append(')'); + } + + public void VisitMethodDefinition(MethodDefinition method, StringBuilder builder) + { + VisitTypeReference(method.DeclaringType, builder); + builder.Append('.').Append(GetEscapedMetadataName(method)); + + if (method.HasGenericParameters) + builder.Append("``").Append(method.GenericParameters.Count); + + if (method.HasParameters || (method.CallingConvention == MethodCallingConvention.VarArg)) + VisitParameters(method.Parameters, method.CallingConvention == MethodCallingConvention.VarArg, builder); + + if (method.Name == "op_Implicit" || method.Name == "op_Explicit") + { + builder.Append('~'); + VisitTypeReference(method.ReturnType, builder); + } + } + + public void VisitProperty(PropertyDefinition property, StringBuilder builder) + { + VisitTypeReference(property.DeclaringType, builder); + builder.Append('.').Append(GetEscapedMetadataName(property)); + + if (property.Parameters.Count > 0) + VisitParameters(property.Parameters, false, builder); + } + + public void VisitEvent(EventDefinition evt, StringBuilder builder) + { + VisitTypeReference(evt.DeclaringType, builder); + builder.Append('.').Append(GetEscapedMetadataName(evt)); + } +#endif + + public override void AppendName(StringBuilder builder, FunctionPointerType type) + { + // Not defined how this should look like + // https://github.com/dotnet/roslyn/issues/48363 + } + + public override void AppendName(StringBuilder builder, GenericParameterDesc genericParameter) + { + // Is this a type parameter on a type? + if (genericParameter.Kind == GenericParameterKind.Method) + { + builder.Append("``"); + } + else + { + Debug.Assert(genericParameter.Kind == GenericParameterKind.Type); + + // If the containing type is nested within other types. + // e.g. A.B.M(T t, U u, V v) should be M(`0, `1, ``0). + // Roslyn needs to add generic arities of parents, but the innermost type redeclares + // all generic parameters so we don't need to add them. + builder.Append('`'); + } + + builder.Append(genericParameter.Index); + } + + public override void AppendName(StringBuilder builder, SignatureMethodVariable type) => builder.Append("``").Append(type.Index); + public override void AppendName(StringBuilder builder, SignatureTypeVariable type) => builder.Append('`').Append(type.Index); + + protected override void AppendNameForNestedType(StringBuilder sb, DefType nestedType, DefType containingType) + { + AppendName(sb, containingType); + sb.Append('.'); + sb.Append(nestedType.Name); + } + + protected override void AppendNameForNamespaceType(StringBuilder sb, DefType type) + { + string @namespace = type.Namespace; + if (!string.IsNullOrEmpty(@namespace)) + sb.Append(@namespace).Append('.'); + sb.Append(type.Name); + } + + protected override void AppendNameForInstantiatedType(StringBuilder builder, DefType type) + { + int containingArity = 0; + DefType containingType = type.ContainingType; + if (containingType != null) + { + AppendName(builder, containingType); + containingArity = containingType.Instantiation.Length; + } + else + { + string @namespace = type.Namespace; + if (!string.IsNullOrEmpty(@namespace)) + builder.Append(@namespace).Append('.'); + } + + string unmangledName = type.Name; + int totalArity = type.Instantiation.Length; + string expectedSuffix = $"`{totalArity.ToString()}"; + if (unmangledName.EndsWith(expectedSuffix)) + unmangledName = unmangledName.Substring(0, unmangledName.Length - expectedSuffix.Length); + + builder.Append(unmangledName); + + // Append type arguments excluding arguments for re-declared parent generic parameters + builder.Append('{'); + bool needsComma = false; + for (int i = containingArity; i < totalArity; ++i) + { + if (needsComma) + builder.Append(','); + var typeArgument = type.Instantiation[i]; + AppendName(builder, typeArgument); + needsComma = true; + } + builder.Append('}'); + } + + public override void AppendName(StringBuilder builder, PointerType type) + { + AppendName(builder, type.ParameterType); + builder.Append('*'); + } + + public override void AppendName(StringBuilder builder, ByRefType type) + { + AppendName(builder, type.ParameterType); + builder.Append('@'); + } + +#if false + private static string GetEscapedMetadataName(IMemberDefinition member) + { + var name = member.Name.Replace('.', '#'); + // Not sure if the following replacements are necessary, but + // they are included to match Roslyn. + return name.Replace('<', '{').Replace('>', '}'); + } +#endif + } + } +} diff --git a/src/coreclr/tools/Common/Compiler/Logging/DocumentationSignatureParser.cs b/src/coreclr/tools/Common/Compiler/Logging/DocumentationSignatureParser.cs new file mode 100644 index 00000000000000..bc19279765e4ec --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/Logging/DocumentationSignatureParser.cs @@ -0,0 +1,805 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; + +using Internal.TypeSystem; + +namespace ILCompiler.Logging +{ + /// + /// Parses a signature for a member, in the format used for C# Documentation Comments: + /// https://github.com/dotnet/csharplang/blob/master/spec/documentation-comments.md#id-string-format + /// Adapted from Roslyn's DocumentationCommentId: + /// https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/DocumentationCommentId.cs + /// + /// + /// Roslyn's API works with ISymbol, which represents a symbol exposed by the compiler. + /// a Symbol has information about the source language, name, metadata name, + /// containing scopes, visibility/accessibility, attributes, etc. + /// This API instead works with the Cecil OM. It can be used to refer to IL definitions + /// where the signature of a member can contain references to instantiated generics. + /// + public static class DocumentationSignatureParser + { + [Flags] + public enum MemberType + { + Method = 0x0001, + Field = 0x0002, + Type = 0x0004, + Property = 0x0008, + Event = 0x0010, + All = Method | Field | Type | Property | Event + } + + public static IEnumerable GetMembersForDocumentationSignature(string id, ModuleDesc module) + { + var results = new List(); + if (id == null || module == null) + return results; + + ParseDocumentationSignature(id, module, results); + return results; + } + + // Takes a documentation signature (not including the documentation member type prefix) and resolves it to a type + // in the assembly. + public static TypeDesc GetTypeByDocumentationSignature(IAssemblyDesc assembly, string signature) + { + int index = 0; + var results = new List(); + DocumentationSignatureParser.ParseSignaturePart(signature, ref index, (ModuleDesc)assembly, DocumentationSignatureParser.MemberType.Type, results); + Debug.Assert(results.Count <= 1); + return results.Count == 0 ? null : (TypeDesc)results[0]; + } + + // Takes a member signature (not including the declaring type) and returns the matching members on the type. + public static IEnumerable GetMembersByDocumentationSignature(MetadataType type, string signature, bool acceptName = false) + { + int index = 0; + var results = new List(); + var nameBuilder = new StringBuilder(); + var (name, arity) = DocumentationSignatureParser.ParseTypeOrNamespaceName(signature, ref index, nameBuilder); + DocumentationSignatureParser.GetMatchingMembers(signature, ref index, type.Module, type, name, arity, DocumentationSignatureParser.MemberType.All, results, acceptName); + return results; + } + + static string GetSignaturePart(TypeDesc type) + { + var builder = new StringBuilder(); + DocumentationSignatureGenerator.PartVisitor.Instance.AppendName(builder, type); + return builder.ToString(); + } + + static bool ParseDocumentationSignature(string id, ModuleDesc module, List results) + { + if (id == null) + return false; + + if (id.Length < 2) + return false; + + int index = 0; + results.Clear(); + ParseSignature(id, ref index, module, results); + return results.Count > 0; + } + + static void ParseSignature(string id, ref int index, ModuleDesc module, List results) + { + Debug.Assert(results.Count == 0); + var memberTypeChar = PeekNextChar(id, index); + MemberType memberType; + + switch (memberTypeChar) + { + case 'E': + memberType = MemberType.Event; + break; + case 'F': + memberType = MemberType.Field; + break; + case 'M': + memberType = MemberType.Method; + break; + case 'N': + // We do not support namespaces, which do not exist in IL. + return; + case 'P': + memberType = MemberType.Property; + break; + case 'T': + memberType = MemberType.Type; + break; + default: + // Documentation comment id must start with E, F, M, P, or T + return; + } + + index++; + // Note: this allows leaving out the ':'. + if (PeekNextChar(id, index) == ':') + index++; + + ParseSignaturePart(id, ref index, module, memberType, results); + } + + // Parses and resolves a fully-qualified (namespace and nested types but no assembly) member signature, + // without the member type prefix. The results include all members matching the specified member types. + public static void ParseSignaturePart(string id, ref int index, ModuleDesc module, MemberType memberTypes, List results) + { + // Roslyn resolves types by searching namespaces top-down. + // We don't have namespace info. Instead try treating each part of a + // dotted name as a type first, then as a namespace if it fails + // to resolve to a type. + TypeDesc containingType = null; + var nameBuilder = new StringBuilder(); + + string name; + int arity; + + // process dotted names + while (true) + { + (name, arity) = ParseTypeOrNamespaceName(id, ref index, nameBuilder); + // if we are at the end of the dotted name and still haven't resolved it to + // a type, there are no results. + if (String.IsNullOrEmpty(name)) + return; + + // no more dots, so don't loop any more + if (PeekNextChar(id, index) != '.') + break; + + // must be a namespace or type since name continues after dot + index++; + + // try to resolve it as a type + var typeOrNamespaceName = nameBuilder.ToString(); + GetMatchingTypes(module, declaringType: containingType, name: typeOrNamespaceName, arity: arity, results: results); + Debug.Assert(results.Count <= 1); + if (results.Any()) + { + // the name resolved to a type + var result = results.Single(); + Debug.Assert(result is TypeDesc); + // result becomes the new container + containingType = result as TypeDesc; + nameBuilder.Clear(); + results.Clear(); + continue; + } + + // it didn't resolve as a type. + + // only types have arity. + if (arity > 0) + return; + + // treat it as a namespace and continue building up the type name + nameBuilder.Append('.'); + } + + var memberName = nameBuilder.ToString(); + GetMatchingMembers(id, ref index, module, containingType, memberName, arity, memberTypes, results); + } + + // Gets all members of the specified member kinds of the containing type, with + // mathing name, arity, and signature at the current index (for methods and properties). + // This will also resolve types from the given module if no containing type is given. + public static void GetMatchingMembers(string id, ref int index, ModuleDesc module, TypeDesc containingType, string memberName, int arity, MemberType memberTypes, List results, bool acceptName = false) + { + if (memberTypes.HasFlag(MemberType.Type)) + GetMatchingTypes(module, containingType, memberName, arity, results); + + if (containingType == null) + return; + + int startIndex = index; + int endIndex = index; + + if (memberTypes.HasFlag(MemberType.Method)) + { + GetMatchingMethods(id, ref index, containingType, memberName, arity, results, acceptName); + endIndex = index; + index = startIndex; + } + +#if false + if (memberTypes.HasFlag(MemberType.Property)) + { + GetMatchingProperties(id, ref index, containingType, memberName, results, acceptName); + endIndex = index; + index = startIndex; + } +#endif + + index = endIndex; + +#if false + if (memberTypes.HasFlag(MemberType.Event)) + GetMatchingEvents(containingType, memberName, results); +#endif + + if (memberTypes.HasFlag(MemberType.Field)) + GetMatchingFields(containingType, memberName, results); + } + + // Parses a part of a dotted declaration name, including generic definitions. + // Returns the name (either a namespace or the unmangled name of a C# type) and an arity + // which may be non-zero for generic types. + public static (string name, int arity) ParseTypeOrNamespaceName(string id, ref int index, StringBuilder nameBuilder) + { + var name = ParseName(id, ref index); + // don't parse ` after an empty name + if (string.IsNullOrEmpty(name)) + return (name, 0); + + nameBuilder.Append(name); + var arity = 0; + + // has type parameters? + if (PeekNextChar(id, index) == '`') + { + index++; + + bool genericType = true; + + // method type parameters? + // note: this allows `` for type parameters + if (PeekNextChar(id, index) == '`') + { + index++; + genericType = false; + } + + arity = ReadNextInteger(id, ref index); + + if (genericType) + { + // We need to mangle generic type names but not generic method names. + nameBuilder.Append('`'); + nameBuilder.Append(arity); + } + } + + return (name, arity); + } + + // Roslyn resolves types in a signature to their declaration by searching through namespaces. + // To avoid looking for types by name in all referenced assemblies, we just represent types + // that are part of a signature by their doc comment strings, and we check for matching + // strings when looking for matching member signatures. + static string ParseTypeSymbol(string id, ref int index, TypeSystemEntity typeParameterContext) + { + var results = new List(); + ParseTypeSymbol(id, ref index, typeParameterContext, results); + if (results.Count == 1) + return results[0]; + + Debug.Assert(results.Count == 0); + return null; + } + + static void ParseTypeSymbol(string id, ref int index, TypeSystemEntity typeParameterContext, List results) + { + // Note: Roslyn has a special case that deviates from the language spec, which + // allows context expressions embedded in a type reference => : + // We do not support this special format. + + Debug.Assert(results.Count == 0); + + if (PeekNextChar(id, index) == '`') + ParseTypeParameterSymbol(id, ref index, typeParameterContext, results); + else + ParseNamedTypeSymbol(id, ref index, typeParameterContext, results); + + // apply any array or pointer constructions to results + var startIndex = index; + var endIndex = index; + + for (int i = 0; i < results.Count; i++) + { + index = startIndex; + var typeReference = results[i]; + + while (true) + { + if (PeekNextChar(id, index) == '[') + { + var boundsStartIndex = index; + var bounds = ParseArrayBounds(id, ref index); + var boundsEndIndex = index; + Debug.Assert(bounds > 0); + // Instead of constructing a representation of the array bounds, we + // use the original input to represent the bounds, and later match it + // against the generated strings for types in signatures. + // This ensures that we will only resolve members with supported array bounds. + typeReference += id.Substring(boundsStartIndex, boundsEndIndex - boundsStartIndex); + continue; + } + + if (PeekNextChar(id, index) == '*') + { + index++; + typeReference += '*'; + continue; + } + + break; + } + + if (PeekNextChar(id, index) == '@') + { + index++; + typeReference += '@'; + } + + results[i] = typeReference; + endIndex = index; + } + + index = endIndex; + } + + static void ParseTypeParameterSymbol(string id, ref int index, TypeSystemEntity typeParameterContext, List results) + { + // skip the first ` + Debug.Assert(PeekNextChar(id, index) == '`'); + index++; + + Debug.Assert( + typeParameterContext == null || + typeParameterContext is MethodDesc || + typeParameterContext is TypeDesc + ); + + if (PeekNextChar(id, index) == '`') + { + // `` means this is a method type parameter + index++; + var methodTypeParameterIndex = ReadNextInteger(id, ref index); + + if (typeParameterContext is MethodDesc methodContext) + { + var count = methodContext.Instantiation.Length; + if (count > 0 && methodTypeParameterIndex < count) + { + results.Add("``" + methodTypeParameterIndex); + } + } + } + else + { + // regular type parameter + var typeParameterIndex = ReadNextInteger(id, ref index); + + var typeContext = typeParameterContext is MethodDesc methodContext + ? methodContext.OwningType + : typeParameterContext as TypeDesc; + + if (typeParameterIndex >= 0 || + typeParameterIndex < typeContext?.Instantiation.Length) + { + // No need to look at declaring types like Roslyn, because type parameters are redeclared. + results.Add("`" + typeParameterIndex); + } + } + } + + static void ParseNamedTypeSymbol(string id, ref int index, TypeSystemEntity typeParameterContext, List results) + { + Debug.Assert(results.Count == 0); + var nameBuilder = new StringBuilder(); + // loop for dotted names + while (true) + { + var name = ParseName(id, ref index); + if (String.IsNullOrEmpty(name)) + return; + + nameBuilder.Append(name); + + List typeArguments = null; + int arity = 0; + + // type arguments + if (PeekNextChar(id, index) == '{') + { + typeArguments = new List(); + if (!ParseTypeArguments(id, ref index, typeParameterContext, typeArguments)) + { + continue; + } + + arity = typeArguments.Count; + } + + if (arity != 0) + { + Debug.Assert(typeArguments != null && typeArguments.Count != 0); + nameBuilder.Append('{'); + bool needsComma = false; + foreach (var typeArg in typeArguments) + { + if (needsComma) + { + nameBuilder.Append(','); + } + nameBuilder.Append(typeArg); + needsComma = true; + } + nameBuilder.Append('}'); + } + + if (PeekNextChar(id, index) != '.') + break; + + index++; + nameBuilder.Append('.'); + } + + results.Add(nameBuilder.ToString()); + } + + static int ParseArrayBounds(string id, ref int index) + { + index++; // skip '[' + + int bounds = 0; + + while (true) + { + // note: the actual bounds are ignored. + // C# only supports arrays with lower bound zero. + // size is not known. + + if (char.IsDigit(PeekNextChar(id, index))) + ReadNextInteger(id, ref index); + + if (PeekNextChar(id, index) == ':') + { + index++; + + // note: the spec says that omitting both the lower bounds and the size + // should omit the ':' as well, but this allows for it in the input. + if (char.IsDigit(PeekNextChar(id, index))) + ReadNextInteger(id, ref index); + } + + bounds++; + + if (PeekNextChar(id, index) == ',') + { + index++; + continue; + } + + break; + } + + // note: this allows leaving out the closing ']' + if (PeekNextChar(id, index) == ']') + index++; + + return bounds; + } + + static bool ParseTypeArguments(string id, ref int index, TypeSystemEntity typeParameterContext, List typeArguments) + { + index++; // skip over { + + while (true) + { + var type = ParseTypeSymbol(id, ref index, typeParameterContext); + + if (type == null) + { + // if a type argument cannot be identified, argument list is no good + return false; + } + + // add first one + typeArguments.Add(type); + + if (PeekNextChar(id, index) == ',') + { + index++; + continue; + } + + break; + } + + // note: this doesn't require closing } + if (PeekNextChar(id, index) == '}') + { + index++; + } + + return true; + } + + static void GetMatchingTypes(ModuleDesc module, TypeDesc declaringType, string name, int arity, List results) + { + Debug.Assert(module != null); + + if (declaringType == null) + { + int indexOfLastDot = name.LastIndexOf('.'); + string namespacepart; + string namepart; + if (indexOfLastDot > 0 && indexOfLastDot < name.Length - 1) + { + namespacepart = name.Substring(indexOfLastDot - 1); + namepart = name.Substring(indexOfLastDot + 1, name.Length - indexOfLastDot - 1); + } + else + { + namespacepart = ""; + namepart = name; + } + + var type = module.GetType(namespacepart, namepart, throwIfNotFound: false); + if (type != null) + { + results.Add(type); + } + return; + } + + if (declaringType is not MetadataType mdDeclaringType) + return; + + foreach (var nestedType in mdDeclaringType.GetNestedTypes()) + { + Debug.Assert(String.IsNullOrEmpty(nestedType.Namespace)); + if (nestedType.Name != name) + continue; + + // Compute arity counting only the newly-introduced generic parameters + var declaringArity = declaringType.Instantiation.Length; + int totalArity = nestedType.Instantiation.Length; + var nestedTypeArity = totalArity - declaringArity; + if (nestedTypeArity != arity) + continue; + + results.Add(nestedType); + return; + } + } + + static void GetMatchingMethods(string id, ref int index, TypeDesc type, string memberName, int arity, List results, bool acceptName = false) + { + if (type == null) + return; + + var parameters = new List(); + var startIndex = index; + var endIndex = index; + + foreach (var method in type.GetMethods()) + { + index = startIndex; + if (method.Name != memberName) + continue; + + var methodArity = method.Instantiation.Length; + if (methodArity != arity) + continue; + + parameters.Clear(); + bool isNameOnly = true; + if (PeekNextChar(id, index) == '(') + { + isNameOnly = false; + // if the parameters cannot be identified (some error), then the symbol cannot match, try next method symbol + if (!ParseParameterList(id, ref index, method, parameters)) + continue; + } + + // note: this allows extra characters at the end + + if (PeekNextChar(id, index) == '~') + { + isNameOnly = false; + index++; + string returnType = ParseTypeSymbol(id, ref index, method); + if (returnType == null) + continue; + + // if return type is specified, then it must match + if (GetSignaturePart(method.Signature.ReturnType) != returnType) + continue; + } + + if (!isNameOnly || !acceptName) + { + // check parameters unless we are matching a name only + if (!AllParametersMatch(method.Signature, parameters)) + continue; + } + + results.Add(method); + endIndex = index; + } + index = endIndex; + } + +#if false + static void GetMatchingProperties(string id, ref int index, TypeDesc type, string memberName, List results, bool acceptName = false) + { + if (type == null) + return; + + int startIndex = index; + int endIndex = index; + + List parameters = null; + // Unlike Roslyn, we don't need to decode property names because we are working + // directly with IL. + foreach (var property in type.Properties) + { + index = startIndex; + if (property.Name != memberName) + continue; + if (PeekNextChar(id, index) == '(') + { + if (parameters == null) + { + parameters = new List(); + } + else + { + parameters.Clear(); + } + if (!ParseParameterList(id, ref index, property.DeclaringType, parameters)) + continue; + if (!AllParametersMatch(property.Parameters, parameters)) + continue; + } + else + { + if (!acceptName && property.Parameters.Count != 0) + continue; + } + results.Add(property); + endIndex = index; + } + + index = endIndex; + } +#endif + + static void GetMatchingFields(TypeDesc type, string memberName, List results) + { + if (type == null) + return; + foreach (var field in type.GetFields()) + { + if (field.Name != memberName) + continue; + results.Add(field); + } + } + +#if false + static void GetMatchingEvents(TypeDesc type, string memberName, List results) + { + if (type == null) + return; + foreach (var evt in type.Events) + { + if (evt.Name != memberName) + continue; + results.Add(evt); + } + } +#endif + + static bool AllParametersMatch(MethodSignature methodParameters, List expectedParameters) + { + if (methodParameters.Length != expectedParameters.Count) + return false; + + for (int i = 0; i < expectedParameters.Count; i++) + { + if (GetSignaturePart(methodParameters[i]) != expectedParameters[i]) + return false; + } + + return true; + } + + static bool ParseParameterList(string id, ref int index, TypeSystemEntity typeParameterContext, List parameters) + { + System.Diagnostics.Debug.Assert(typeParameterContext != null); + + index++; // skip over '(' + + if (PeekNextChar(id, index) == ')') + { + // note: this will match parameterless methods, or methods with only varargs parameters + index++; + return true; + } + + string parameter = ParseTypeSymbol(id, ref index, typeParameterContext); + if (parameter == null) + return false; + + parameters.Add(parameter); + + while (PeekNextChar(id, index) == ',') + { + index++; + + parameter = ParseTypeSymbol(id, ref index, typeParameterContext); + if (parameter == null) + return false; + + parameters.Add(parameter); + } + + // note: this doesn't require the trailing ')' + if (PeekNextChar(id, index) == ')') + { + index++; + } + + return true; + } + + static char PeekNextChar(string id, int index) + { + return index >= id.Length ? '\0' : id[index]; + } + + static readonly char[] s_nameDelimiters = { ':', '.', '(', ')', '{', '}', '[', ']', ',', '\'', '@', '*', '`', '~' }; + + static string ParseName(string id, ref int index) + { + string name; + + int delimiterOffset = id.IndexOfAny(s_nameDelimiters, index); + if (delimiterOffset >= 0) + { + name = id.Substring(index, delimiterOffset - index); + index = delimiterOffset; + } + else + { + name = id.Substring(index); + index = id.Length; + } + + return DecodeName(name); + } + + // undoes dot encodings within names... + static string DecodeName(string name) + { + return name.Replace('#', '.'); + } + + static int ReadNextInteger(string id, ref int index) + { + int n = 0; + + // note: this can overflow + while (index < id.Length && char.IsDigit(id[index])) + { + n = n * 10 + (id[index] - '0'); + index++; + } + + return n; + } + } +} diff --git a/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/DocumentationSignatureGenerator.PartVisitor.cs b/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/DocumentationSignatureGenerator.PartVisitor.cs new file mode 100644 index 00000000000000..ed4763b75cda81 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/DocumentationSignatureGenerator.PartVisitor.cs @@ -0,0 +1,211 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using Mono.Cecil; + +namespace Mono.Linker +{ + + public sealed partial class DocumentationSignatureGenerator + { + /// + /// A visitor that generates the part of the documentation comment after the initial type + /// and colon. + /// Adapted from Roslyn's DocumentattionCommentIDVisitor.PartVisitor: + /// https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs + /// + internal sealed class PartVisitor + { + internal static readonly PartVisitor Instance = new PartVisitor (); + + private PartVisitor () + { + } + + public void VisitArrayType (ArrayType arrayType, StringBuilder builder, ITryResolveMetadata resolver) + { + VisitTypeReference (arrayType.ElementType, builder, resolver); + + // Rank-one arrays are displayed different than rectangular arrays + if (arrayType.IsVector) { + builder.Append ("[]"); + } else { + // C# arrays only support zero lower bounds + if (arrayType.Dimensions[0].LowerBound != 0) + throw new NotImplementedException (); + builder.Append ("[0:"); + for (int i = 1; i < arrayType.Rank; i++) { + if (arrayType.Dimensions[0].LowerBound != 0) + throw new NotImplementedException (); + builder.Append (",0:"); + } + + builder.Append (']'); + } + } + + public void VisitField (FieldDefinition field, StringBuilder builder, ITryResolveMetadata resolver) + { + VisitTypeReference (field.DeclaringType, builder, resolver); + builder.Append ('.').Append (field.Name); + } + + private void VisitParameters (IEnumerable parameters, bool isVararg, StringBuilder builder, ITryResolveMetadata resolver) + { + builder.Append ('('); + bool needsComma = false; + + foreach (var parameter in parameters) { + if (needsComma) + builder.Append (','); + + // byrefs are tracked on the parameter type, not the parameter, + // so we don't have VisitParameter that Roslyn uses. + VisitTypeReference (parameter.ParameterType, builder, resolver); + needsComma = true; + } + + // note: the C# doc comment generator outputs an extra comma for varargs + // methods that also have fixed parameters + if (isVararg && needsComma) + builder.Append (','); + + builder.Append (')'); + } + + public void VisitMethodDefinition (MethodDefinition method, StringBuilder builder, ITryResolveMetadata resolver) + { + VisitTypeReference (method.DeclaringType, builder, resolver); + builder.Append ('.').Append (GetEscapedMetadataName (method)); + + if (method.HasGenericParameters) + builder.Append ("``").Append (method.GenericParameters.Count); + + if (method.HasParameters || (method.CallingConvention == MethodCallingConvention.VarArg)) + VisitParameters (method.Parameters, method.CallingConvention == MethodCallingConvention.VarArg, builder, resolver); + + if (method.Name == "op_Implicit" || method.Name == "op_Explicit") { + builder.Append ('~'); + VisitTypeReference (method.ReturnType, builder, resolver); + } + } + + public void VisitProperty (PropertyDefinition property, StringBuilder builder, ITryResolveMetadata resolver) + { + VisitTypeReference (property.DeclaringType, builder, resolver); + builder.Append ('.').Append (GetEscapedMetadataName (property)); + + if (property.Parameters.Count > 0) + VisitParameters (property.Parameters, false, builder, resolver); + } + + public void VisitEvent (EventDefinition evt, StringBuilder builder, ITryResolveMetadata resolver) + { + VisitTypeReference (evt.DeclaringType, builder, resolver); + builder.Append ('.').Append (GetEscapedMetadataName (evt)); + } + + public static void VisitGenericParameter (GenericParameter genericParameter, StringBuilder builder) + { + Debug.Assert (genericParameter.DeclaringMethod == null ^ genericParameter.DeclaringType == null); + // Is this a type parameter on a type? + if (genericParameter.DeclaringMethod != null) { + builder.Append ("``"); + } else { + Debug.Assert (genericParameter.DeclaringType != null); + + // If the containing type is nested within other types. + // e.g. A.B.M(T t, U u, V v) should be M(`0, `1, ``0). + // Roslyn needs to add generic arities of parents, but the innermost type redeclares + // all generic parameters so we don't need to add them. + builder.Append ('`'); + } + + builder.Append (genericParameter.Position); + } + + public void VisitTypeReference (TypeReference typeReference, StringBuilder builder, ITryResolveMetadata resolver) + { + switch (typeReference) { + case ByReferenceType byReferenceType: + VisitByReferenceType (byReferenceType, builder, resolver); + return; + case PointerType pointerType: + VisitPointerType (pointerType, builder, resolver); + return; + case ArrayType arrayType: + VisitArrayType (arrayType, builder, resolver); + return; + case GenericParameter genericParameter: + VisitGenericParameter (genericParameter, builder); + return; + } + + if (typeReference.IsNested) { + VisitTypeReference (typeReference.GetInflatedDeclaringType (resolver), builder, resolver); + builder.Append ('.'); + } + + if (!String.IsNullOrEmpty (typeReference.Namespace)) + builder.Append (typeReference.Namespace).Append ('.'); + + // This includes '`n' for mangled generic types + builder.Append (typeReference.Name); + + // For uninstantiated generic types (we already built the mangled name) + // or non-generic types, we are done. + if (typeReference.HasGenericParameters || typeReference is not GenericInstanceType genericInstance) + return; + + // Compute arity counting only the newly-introduced generic parameters + var declaringType = genericInstance.DeclaringType; + var declaringArity = 0; + if (declaringType != null && declaringType.HasGenericParameters) + declaringArity = declaringType.GenericParameters.Count; + var totalArity = genericInstance.GenericArguments.Count; + var arity = totalArity - declaringArity; + + // Un-mangle the generic type name + var suffixLength = arity.ToString ().Length + 1; + builder.Remove (builder.Length - suffixLength, suffixLength); + + // Append type arguments excluding arguments for re-declared parent generic parameters + builder.Append ('{'); + bool needsComma = false; + for (int i = totalArity - arity; i < totalArity; ++i) { + if (needsComma) + builder.Append (','); + var typeArgument = genericInstance.GenericArguments[i]; + VisitTypeReference (typeArgument, builder, resolver); + needsComma = true; + } + builder.Append ('}'); + } + + public void VisitPointerType (PointerType pointerType, StringBuilder builder, ITryResolveMetadata resolver) + { + VisitTypeReference (pointerType.ElementType, builder, resolver); + builder.Append ('*'); + } + + public void VisitByReferenceType (ByReferenceType byReferenceType, StringBuilder builder, ITryResolveMetadata resolver) + { + VisitTypeReference (byReferenceType.ElementType, builder, resolver); + builder.Append ('@'); + } + + private static string GetEscapedMetadataName (IMemberDefinition member) + { + var name = member.Name.Replace ('.', '#'); + // Not sure if the following replacements are necessary, but + // they are included to match Roslyn. + return name.Replace ('<', '{').Replace ('>', '}'); + } + } + } +} \ No newline at end of file diff --git a/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/DocumentationSignatureParser.cs b/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/DocumentationSignatureParser.cs new file mode 100644 index 00000000000000..923197f58b6bf0 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/DocumentationSignatureParser.cs @@ -0,0 +1,729 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Mono.Cecil; +using Mono.Collections.Generic; + +namespace Mono.Linker +{ + /// + /// Parses a signature for a member, in the format used for C# Documentation Comments: + /// https://github.com/dotnet/csharplang/blob/master/spec/documentation-comments.md#id-string-format + /// Adapted from Roslyn's DocumentationCommentId: + /// https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/DocumentationCommentId.cs + /// + /// + /// Roslyn's API works with ISymbol, which represents a symbol exposed by the compiler. + /// a Symbol has information about the source language, name, metadata name, + /// containing scopes, visibility/accessibility, attributes, etc. + /// This API instead works with the Cecil OM. It can be used to refer to IL definitions + /// where the signature of a member can contain references to instantiated generics. + /// + public static class DocumentationSignatureParser + { + [Flags] + public enum MemberType + { + Method = 0x0001, + Field = 0x0002, + Type = 0x0004, + Property = 0x0008, + Event = 0x0010, + All = Method | Field | Type | Property | Event + } + + public static IEnumerable GetMembersForDocumentationSignature (string id, ModuleDefinition module, ITryResolveMetadata resolver) + { + var results = new List (); + if (id == null || module == null) + return results; + + ParseDocumentationSignature (id, module, results, resolver); + return results; + } + + // Takes a documentation signature (not including the documentation member type prefix) and resolves it to a type + // in the assembly. + public static TypeDefinition? GetTypeByDocumentationSignature (AssemblyDefinition assembly, string signature, ITryResolveMetadata resolver) + { + int index = 0; + var results = new List (); + DocumentationSignatureParser.ParseSignaturePart (signature, ref index, assembly.MainModule, DocumentationSignatureParser.MemberType.Type, results, resolver); + Debug.Assert (results.Count <= 1); + return results.Count == 0 ? null : (TypeDefinition) results[0]; + } + + // Takes a member signature (not including the declaring type) and returns the matching members on the type. + public static IEnumerable GetMembersByDocumentationSignature (TypeDefinition type, string signature, ITryResolveMetadata resolver, bool acceptName = false) + { + int index = 0; + var results = new List (); + var nameBuilder = new StringBuilder (); + var (name, arity) = DocumentationSignatureParser.ParseTypeOrNamespaceName (signature, ref index, nameBuilder); + DocumentationSignatureParser.GetMatchingMembers (signature, ref index, type.Module, type, name, arity, DocumentationSignatureParser.MemberType.All, results, resolver, acceptName); + return results; + } + + static string GetSignaturePart (TypeReference type, ITryResolveMetadata resolver) + { + var builder = new StringBuilder (); + DocumentationSignatureGenerator.PartVisitor.Instance.VisitTypeReference (type, builder, resolver); + return builder.ToString (); + } + + static bool ParseDocumentationSignature (string id, ModuleDefinition module, List results, ITryResolveMetadata resolver) + { + if (id == null) + return false; + + if (id.Length < 2) + return false; + + int index = 0; + results.Clear (); + ParseSignature (id, ref index, module, results, resolver); + return results.Count > 0; + } + + static void ParseSignature (string id, ref int index, ModuleDefinition module, List results, ITryResolveMetadata resolver) + { + Debug.Assert (results.Count == 0); + var memberTypeChar = PeekNextChar (id, index); + MemberType memberType; + + switch (memberTypeChar) { + case 'E': + memberType = MemberType.Event; + break; + case 'F': + memberType = MemberType.Field; + break; + case 'M': + memberType = MemberType.Method; + break; + case 'N': + // We do not support namespaces, which do not exist in IL. + return; + case 'P': + memberType = MemberType.Property; + break; + case 'T': + memberType = MemberType.Type; + break; + default: + // Documentation comment id must start with E, F, M, P, or T + return; + } + + index++; + // Note: this allows leaving out the ':'. + if (PeekNextChar (id, index) == ':') + index++; + + ParseSignaturePart (id, ref index, module, memberType, results, resolver); + } + + // Parses and resolves a fully-qualified (namespace and nested types but no assembly) member signature, + // without the member type prefix. The results include all members matching the specified member types. + public static void ParseSignaturePart (string id, ref int index, ModuleDefinition module, MemberType memberTypes, List results, ITryResolveMetadata resolver) + { + // Roslyn resolves types by searching namespaces top-down. + // We don't have namespace info. Instead try treating each part of a + // dotted name as a type first, then as a namespace if it fails + // to resolve to a type. + TypeDefinition? containingType = null; + var nameBuilder = new StringBuilder (); + + string name; + int arity; + + // process dotted names + while (true) { + (name, arity) = ParseTypeOrNamespaceName (id, ref index, nameBuilder); + // if we are at the end of the dotted name and still haven't resolved it to + // a type, there are no results. + if (String.IsNullOrEmpty (name)) + return; + + // no more dots, so don't loop any more + if (PeekNextChar (id, index) != '.') + break; + + // must be a namespace or type since name continues after dot + index++; + + // try to resolve it as a type + var typeOrNamespaceName = nameBuilder.ToString (); + GetMatchingTypes (module, declaringType: containingType, name: typeOrNamespaceName, arity: arity, results: results, resolver); + Debug.Assert (results.Count <= 1); + if (results.Any ()) { + // the name resolved to a type + var result = results.Single (); + Debug.Assert (result is TypeDefinition); + // result becomes the new container + containingType = result as TypeDefinition; + nameBuilder.Clear (); + results.Clear (); + continue; + } + + // it didn't resolve as a type. + + // only types have arity. + if (arity > 0) + return; + + // treat it as a namespace and continue building up the type name + nameBuilder.Append ('.'); + } + + var memberName = nameBuilder.ToString (); + GetMatchingMembers (id, ref index, module, containingType, memberName, arity, memberTypes, results, resolver); + } + + // Gets all members of the specified member kinds of the containing type, with + // mathing name, arity, and signature at the current index (for methods and properties). + // This will also resolve types from the given module if no containing type is given. + public static void GetMatchingMembers (string id, ref int index, ModuleDefinition module, TypeDefinition? containingType, string memberName, int arity, MemberType memberTypes, List results, ITryResolveMetadata resolver, bool acceptName = false) + { + if (memberTypes.HasFlag (MemberType.Type)) + GetMatchingTypes (module, containingType, memberName, arity, results, resolver); + + if (containingType == null) + return; + + int startIndex = index; + int endIndex = index; + + if (memberTypes.HasFlag (MemberType.Method)) { + GetMatchingMethods (id, ref index, containingType, memberName, arity, results, resolver, acceptName); + endIndex = index; + index = startIndex; + } + + if (memberTypes.HasFlag (MemberType.Property)) { + GetMatchingProperties (id, ref index, containingType, memberName, results, resolver, acceptName); + endIndex = index; + index = startIndex; + } + + index = endIndex; + + if (memberTypes.HasFlag (MemberType.Event)) + GetMatchingEvents (containingType, memberName, results); + + if (memberTypes.HasFlag (MemberType.Field)) + GetMatchingFields (containingType, memberName, results); + } + + // Parses a part of a dotted declaration name, including generic definitions. + // Returns the name (either a namespace or the unmangled name of a C# type) and an arity + // which may be non-zero for generic types. + public static (string name, int arity) ParseTypeOrNamespaceName (string id, ref int index, StringBuilder nameBuilder) + { + var name = ParseName (id, ref index); + // don't parse ` after an empty name + if (string.IsNullOrEmpty (name)) + return (name, 0); + + nameBuilder.Append (name); + var arity = 0; + + // has type parameters? + if (PeekNextChar (id, index) == '`') { + index++; + + bool genericType = true; + + // method type parameters? + // note: this allows `` for type parameters + if (PeekNextChar (id, index) == '`') { + index++; + genericType = false; + } + + arity = ReadNextInteger (id, ref index); + + if (genericType) { + // We need to mangle generic type names but not generic method names. + nameBuilder.Append ('`'); + nameBuilder.Append (arity); + } + } + + return (name, arity); + } + + // Roslyn resolves types in a signature to their declaration by searching through namespaces. + // To avoid looking for types by name in all referenced assemblies, we just represent types + // that are part of a signature by their doc comment strings, and we check for matching + // strings when looking for matching member signatures. + static string? ParseTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext) + { + var results = new List (); + ParseTypeSymbol (id, ref index, typeParameterContext, results); + if (results.Count == 1) + return results[0]; + + Debug.Assert (results.Count == 0); + return null; + } + + static void ParseTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) + { + // Note: Roslyn has a special case that deviates from the language spec, which + // allows context expressions embedded in a type reference => : + // We do not support this special format. + + Debug.Assert (results.Count == 0); + + if (PeekNextChar (id, index) == '`') + ParseTypeParameterSymbol (id, ref index, typeParameterContext, results); + else + ParseNamedTypeSymbol (id, ref index, typeParameterContext, results); + + // apply any array or pointer constructions to results + var startIndex = index; + var endIndex = index; + + for (int i = 0; i < results.Count; i++) { + index = startIndex; + var typeReference = results[i]; + + while (true) { + if (PeekNextChar (id, index) == '[') { + var boundsStartIndex = index; + var bounds = ParseArrayBounds (id, ref index); + var boundsEndIndex = index; + Debug.Assert (bounds > 0); + // Instead of constructing a representation of the array bounds, we + // use the original input to represent the bounds, and later match it + // against the generated strings for types in signatures. + // This ensures that we will only resolve members with supported array bounds. + typeReference += id.Substring (boundsStartIndex, boundsEndIndex - boundsStartIndex); + continue; + } + + if (PeekNextChar (id, index) == '*') { + index++; + typeReference += '*'; + continue; + } + + break; + } + + if (PeekNextChar (id, index) == '@') { + index++; + typeReference += '@'; + } + + results[i] = typeReference; + endIndex = index; + } + + index = endIndex; + } + + static void ParseTypeParameterSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) + { + // skip the first ` + Debug.Assert (PeekNextChar (id, index) == '`'); + index++; + + Debug.Assert ( + typeParameterContext == null || + (typeParameterContext is MethodDefinition && typeParameterContext.GenericParameterType == GenericParameterType.Method) || + (typeParameterContext is TypeDefinition && typeParameterContext.GenericParameterType == GenericParameterType.Type) + ); + + if (PeekNextChar (id, index) == '`') { + // `` means this is a method type parameter + index++; + var methodTypeParameterIndex = ReadNextInteger (id, ref index); + + if (typeParameterContext is MethodDefinition methodContext) { + var count = methodContext.HasGenericParameters ? methodContext.GenericParameters.Count : 0; + if (count > 0 && methodTypeParameterIndex < count) { + results.Add ("``" + methodTypeParameterIndex); + } + } + } else { + // regular type parameter + var typeParameterIndex = ReadNextInteger (id, ref index); + + var typeContext = typeParameterContext is MethodDefinition methodContext + ? methodContext.DeclaringType + : typeParameterContext as TypeDefinition; + + if (typeParameterIndex >= 0 || + typeParameterIndex < typeContext?.GenericParameters.Count) { + // No need to look at declaring types like Roslyn, because type parameters are redeclared. + results.Add ("`" + typeParameterIndex); + } + } + } + + static void ParseNamedTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) + { + Debug.Assert (results.Count == 0); + var nameBuilder = new StringBuilder (); + // loop for dotted names + while (true) { + var name = ParseName (id, ref index); + if (String.IsNullOrEmpty (name)) + return; + + nameBuilder.Append (name); + + List? typeArguments = null; + int arity = 0; + + // type arguments + if (PeekNextChar (id, index) == '{') { + typeArguments = new List (); + if (!ParseTypeArguments (id, ref index, typeParameterContext, typeArguments)) { + continue; + } + + arity = typeArguments.Count; + } + + if (arity != 0) { + Debug.Assert (typeArguments != null && typeArguments.Count != 0); + nameBuilder.Append ('{'); + bool needsComma = false; + foreach (var typeArg in typeArguments) { + if (needsComma) { + nameBuilder.Append (','); + } + nameBuilder.Append (typeArg); + needsComma = true; + } + nameBuilder.Append ('}'); + } + + if (PeekNextChar (id, index) != '.') + break; + + index++; + nameBuilder.Append ('.'); + } + + results.Add (nameBuilder.ToString ()); + } + + static int ParseArrayBounds (string id, ref int index) + { + index++; // skip '[' + + int bounds = 0; + + while (true) { + // note: the actual bounds are ignored. + // C# only supports arrays with lower bound zero. + // size is not known. + + if (char.IsDigit (PeekNextChar (id, index))) + ReadNextInteger (id, ref index); + + if (PeekNextChar (id, index) == ':') { + index++; + + // note: the spec says that omitting both the lower bounds and the size + // should omit the ':' as well, but this allows for it in the input. + if (char.IsDigit (PeekNextChar (id, index))) + ReadNextInteger (id, ref index); + } + + bounds++; + + if (PeekNextChar (id, index) == ',') { + index++; + continue; + } + + break; + } + + // note: this allows leaving out the closing ']' + if (PeekNextChar (id, index) == ']') + index++; + + return bounds; + } + + static bool ParseTypeArguments (string id, ref int index, IGenericParameterProvider? typeParameterContext, List typeArguments) + { + index++; // skip over { + + while (true) { + var type = ParseTypeSymbol (id, ref index, typeParameterContext); + + if (type == null) { + // if a type argument cannot be identified, argument list is no good + return false; + } + + // add first one + typeArguments.Add (type); + + if (PeekNextChar (id, index) == ',') { + index++; + continue; + } + + break; + } + + // note: this doesn't require closing } + if (PeekNextChar (id, index) == '}') { + index++; + } + + return true; + } + + static void GetMatchingTypes (ModuleDefinition module, TypeDefinition? declaringType, string name, int arity, List results, ITryResolveMetadata resolver) + { + Debug.Assert (module != null); + + if (declaringType == null) { + var type = module.ResolveType (name, resolver); + if (type != null) { + results.Add (type); + } + return; + } + + if (!declaringType.HasNestedTypes) + return; + + foreach (var nestedType in declaringType.NestedTypes) { + Debug.Assert (String.IsNullOrEmpty (nestedType.Namespace)); + if (nestedType.Name != name) + continue; + + // Compute arity counting only the newly-introduced generic parameters + var declaringArity = declaringType.HasGenericParameters ? declaringType.GenericParameters.Count : 0; + int totalArity = nestedType.HasGenericParameters ? nestedType.GenericParameters.Count : 0; + var nestedTypeArity = totalArity - declaringArity; + if (nestedTypeArity != arity) + continue; + + results.Add (nestedType); + return; + } + } + + static void GetMatchingMethods (string id, ref int index, TypeDefinition? type, string memberName, int arity, List results, ITryResolveMetadata resolver, bool acceptName = false) + { + if (type == null) + return; + + var parameters = new List (); + var startIndex = index; + var endIndex = index; + + foreach (var method in type.Methods) { + index = startIndex; + if (method.Name != memberName) + continue; + + var methodArity = method.HasGenericParameters ? method.GenericParameters.Count : 0; + if (methodArity != arity) + continue; + + parameters.Clear (); + bool isNameOnly = true; + if (PeekNextChar (id, index) == '(') { + isNameOnly = false; + // if the parameters cannot be identified (some error), then the symbol cannot match, try next method symbol + if (!ParseParameterList (id, ref index, method, parameters)) + continue; + } + + // note: this allows extra characters at the end + + if (PeekNextChar (id, index) == '~') { + isNameOnly = false; + index++; + string? returnType = ParseTypeSymbol (id, ref index, method); + if (returnType == null) + continue; + + // if return type is specified, then it must match + if (GetSignaturePart (method.ReturnType, resolver) != returnType) + continue; + } + + if (!isNameOnly || !acceptName) { + // check parameters unless we are matching a name only + if (!AllParametersMatch (method.Parameters, parameters, resolver)) + continue; + } + + results.Add (method); + endIndex = index; + } + index = endIndex; + } + + static void GetMatchingProperties (string id, ref int index, TypeDefinition? type, string memberName, List results, ITryResolveMetadata resolver, bool acceptName = false) + { + if (type == null) + return; + + int startIndex = index; + int endIndex = index; + + List? parameters = null; + // Unlike Roslyn, we don't need to decode property names because we are working + // directly with IL. + foreach (var property in type.Properties) { + index = startIndex; + if (property.Name != memberName) + continue; + if (PeekNextChar (id, index) == '(') { + if (parameters == null) { + parameters = new List (); + } else { + parameters.Clear (); + } + if (!ParseParameterList (id, ref index, property.DeclaringType, parameters)) + continue; + if (!AllParametersMatch (property.Parameters, parameters, resolver)) + continue; + } else { + if (!acceptName && property.Parameters.Count != 0) + continue; + } + results.Add (property); + endIndex = index; + } + + index = endIndex; + } + + static void GetMatchingFields (TypeDefinition? type, string memberName, List results) + { + if (type == null) + return; + foreach (var field in type.Fields) { + if (field.Name != memberName) + continue; + results.Add (field); + } + } + + static void GetMatchingEvents (TypeDefinition? type, string memberName, List results) + { + if (type == null) + return; + foreach (var evt in type.Events) { + if (evt.Name != memberName) + continue; + results.Add (evt); + } + } + + static bool AllParametersMatch (Collection methodParameters, List expectedParameters, ITryResolveMetadata resolver) + { + if (methodParameters.Count != expectedParameters.Count) + return false; + + for (int i = 0; i < expectedParameters.Count; i++) { + if (GetSignaturePart (methodParameters[i].ParameterType, resolver) != expectedParameters[i]) + return false; + } + + return true; + } + + static bool ParseParameterList (string id, ref int index, IGenericParameterProvider typeParameterContext, List parameters) + { + System.Diagnostics.Debug.Assert (typeParameterContext != null); + + index++; // skip over '(' + + if (PeekNextChar (id, index) == ')') { + // note: this will match parameterless methods, or methods with only varargs parameters + index++; + return true; + } + + string? parameter = ParseTypeSymbol (id, ref index, typeParameterContext); + if (parameter == null) + return false; + + parameters.Add (parameter); + + while (PeekNextChar (id, index) == ',') { + index++; + + parameter = ParseTypeSymbol (id, ref index, typeParameterContext); + if (parameter == null) + return false; + + parameters.Add (parameter); + } + + // note: this doesn't require the trailing ')' + if (PeekNextChar (id, index) == ')') { + index++; + } + + return true; + } + + static char PeekNextChar (string id, int index) + { + return index >= id.Length ? '\0' : id[index]; + } + + static readonly char[] s_nameDelimiters = { ':', '.', '(', ')', '{', '}', '[', ']', ',', '\'', '@', '*', '`', '~' }; + + static string ParseName (string id, ref int index) + { + string name; + + int delimiterOffset = id.IndexOfAny (s_nameDelimiters, index); + if (delimiterOffset >= 0) { + name = id.Substring (index, delimiterOffset - index); + index = delimiterOffset; + } else { + name = id.Substring (index); + index = id.Length; + } + + return DecodeName (name); + } + + // undoes dot encodings within names... + static string DecodeName (string name) + { + return name.Replace ('#', '.'); + } + + static int ReadNextInteger (string id, ref int index) + { + int n = 0; + + // note: this can overflow + while (index < id.Length && char.IsDigit (id[index])) { + n = n * 10 + (id[index] - '0'); + index++; + } + + return n; + } + } +} \ No newline at end of file diff --git a/src/coreclr/tools/Common/Internal/LowLevelLinq/LowLevelEnumerable.ToArray.cs b/src/coreclr/tools/Common/Internal/LowLevelLinq/LowLevelEnumerable.ToArray.cs new file mode 100644 index 00000000000000..68d4ce19fc0113 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/LowLevelLinq/LowLevelEnumerable.ToArray.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +namespace Internal.LowLevelLinq +{ + internal static partial class LowLevelEnumerable + { + public static T[] ToArray(this IEnumerable values) + { + Debug.Assert(values != null); + + LowLevelList list = new LowLevelList(); + foreach (T value in values) + { + list.Add(value); + } + return list.ToArray(); + } + } +} diff --git a/src/coreclr/tools/Common/Internal/LowLevelLinq/LowLevelEnumerable.ToList.cs b/src/coreclr/tools/Common/Internal/LowLevelLinq/LowLevelEnumerable.ToList.cs new file mode 100644 index 00000000000000..8ab46e8b8caead --- /dev/null +++ b/src/coreclr/tools/Common/Internal/LowLevelLinq/LowLevelEnumerable.ToList.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +namespace Internal.LowLevelLinq +{ + internal static partial class LowLevelEnumerable + { + public static List ToList(this IEnumerable source) + { + List result; + + var collection = source as ICollection; + if (collection != null) + result = new List(collection.Count); + else + result = new List(); + + foreach (var element in source) + result.Add(element); + + return result; + } + } +} diff --git a/src/coreclr/tools/Common/Internal/LowLevelLinq/LowLevelEnumerable.cs b/src/coreclr/tools/Common/Internal/LowLevelLinq/LowLevelEnumerable.cs new file mode 100644 index 00000000000000..d3d2fdeb17e736 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/LowLevelLinq/LowLevelEnumerable.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +namespace Internal.LowLevelLinq +{ + internal static partial class LowLevelEnumerable + { + public static bool Any(this IEnumerable values) + { + Debug.Assert(values != null); + + IEnumerator enumerator = values.GetEnumerator(); + return enumerator.MoveNext(); + } + + public static bool Any(this IEnumerable values, Func predicate) + { + Debug.Assert(values != null); + Debug.Assert(predicate != null); + foreach (T value in values) + { + if (predicate(value)) + return true; + } + return false; + } + + public static IEnumerable Select(this IEnumerable values, Func func) + { + Debug.Assert(values != null); + + foreach (T value in values) + { + yield return func(value); + } + } + + public static IEnumerable Select(this T[] values, Func func) + { + Debug.Assert(values != null); + + foreach (T value in values) + { + yield return func(value); + } + } + + public static IEnumerable Where(this IEnumerable source, Func filter) + { + Debug.Assert(source != null); + Debug.Assert(filter != null); + + foreach (T value in source) + { + if (filter(value)) + yield return value; + } + } + + public static IEnumerable AsEnumerable(this IEnumerable source) + { + Debug.Assert(source != null); + return source; + } + + public static int Count(this IEnumerable enumeration) + { + Debug.Assert(enumeration != null); + + var collection = enumeration as ICollection; + if (collection != null) + return collection.Count; + + int i = 0; + foreach (T element in enumeration) + { + i++; + } + + return i; + } + } +} diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/CsWriter.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/CsWriter.cs new file mode 100644 index 00000000000000..48af0fc72a1b2b --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/CsWriter.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; + +class CsWriter : IDisposable +{ + TextWriter _writer; + + int _indent; + bool _emptyLineAssumed; + + public CsWriter(string filename) + { + _writer = new StreamWriter(filename); + + _writer.WriteLine("// Licensed to the .NET Foundation under one or more agreements."); + _writer.WriteLine("// The .NET Foundation licenses this file to you under the MIT license."); + _writer.WriteLine(); + _writer.WriteLine("// NOTE: This is a generated file - do not manually edit!"); + _writer.WriteLine(); + } + + public void Dispose() + { + _writer.Dispose(); + } + + public void Indent() + { + _indent++; + } + + public void Exdent() + { + _indent--; + } + + public void WriteLine(string s) + { + for (int i = 0; i < 4 * _indent; i++) + _writer.Write(' '); + _writer.WriteLine(s); + + _emptyLineAssumed = false; + } + + public void WriteLine() + { + _writer.WriteLine(); + + _emptyLineAssumed = true; + } + + public void WriteLineIfNeeded() + { + if (!_emptyLineAssumed) + WriteLine(); + } + + public void OpenScope(string s) + { + WriteLineIfNeeded(); + + WriteLine(s); + WriteLine("{"); + Indent(); + + _emptyLineAssumed = true; + } + + public void CloseScope(string s = null) + { + Exdent(); + WriteLine((s != null) ? ("} // " + s) : "}"); + } + + public void WriteScopeAttribute(string s) + { + WriteLineIfNeeded(); + WriteLine(s); + + _emptyLineAssumed = true; + } + + public void WriteDocComment(string s) + { + WriteLine("/// " + s); + } +} diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/MdBinaryReaderGen.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/MdBinaryReaderGen.cs new file mode 100644 index 00000000000000..e641c76a161150 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/MdBinaryReaderGen.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Generates the C# MdBinaryReader class. This classes is responsible for correctly decoding +// data members in the .metadata file. See NativeFormatReaderGen.cs for how the MetadataReader +// use this class. +// + +class MdBinaryReaderGen : CsWriter +{ + public MdBinaryReaderGen(string fileName) + : base(fileName) + { + } + + public void EmitSource() + { + WriteLine("#pragma warning disable 649"); + WriteLine(); + + WriteLine("using System;"); + WriteLine("using System.IO;"); + WriteLine("using System.Collections.Generic;"); + WriteLine("using System.Reflection;"); + WriteLine("using Internal.NativeFormat;"); + WriteLine("using Debug = System.Diagnostics.Debug;"); + WriteLine(); + + OpenScope("namespace Internal.Metadata.NativeFormat"); + + OpenScope("internal static partial class MdBinaryReader"); + + foreach (var primitiveType in SchemaDef.PrimitiveTypes) + { + EmitReadPrimitiveCollection(primitiveType.TypeName, primitiveType.Name); + } + + foreach (var enumType in SchemaDef.EnumTypes) + { + EmitReadEnum(enumType); + } + + EmitReadCollection($"Handle"); + + foreach (var typeName in SchemaDef.HandleSchema) + { + EmitRead($"{typeName}Handle"); + } + + foreach (var typeName in SchemaDef.TypeNamesWithCollectionTypes) + { + EmitReadCollection($"{typeName}Handle"); + } + + CloseScope("MdBinaryReader"); + CloseScope("Internal.Metadata.NativeFormat"); + } + + private void EmitReadCollection(string typeName) + { + string collectionTypeName = $"{typeName}Collection"; + + OpenScope($"public static uint Read(this NativeReader reader, uint offset, out {collectionTypeName} values)"); + WriteLine($"values = new {collectionTypeName}(reader, offset);"); + WriteLine("uint count;"); + WriteLine("offset = reader.DecodeUnsigned(offset, out count);"); + WriteLine("for (uint i = 0; i < count; ++i)"); + WriteLine("{"); + WriteLine(" offset = reader.SkipInteger(offset);"); + WriteLine("}"); + WriteLine("return offset;"); + CloseScope("Read"); + } + + private void EmitReadPrimitiveCollection(string typeName, string shortName) + { + string collectionTypeName = $"{typeName}Collection"; + + OpenScope($"public static unsafe uint Read(this NativeReader reader, uint offset, out {collectionTypeName} values)"); + WriteLine($"values = new {collectionTypeName}(reader, offset);"); + WriteLine("uint count;"); + WriteLine("offset = reader.DecodeUnsigned(offset, out count);"); + WriteLine($"offset = checked(offset + count * sizeof({shortName}));"); + WriteLine("return offset;"); + CloseScope("Read"); + } + + private void EmitReadEnum(EnumType enumType) + { + OpenScope($"public static uint Read(this NativeReader reader, uint offset, out {enumType.Name} value)"); + WriteLine($"uint ivalue;"); + WriteLine("offset = reader.DecodeUnsigned(offset, out ivalue);"); + WriteLine($"value = ({enumType.Name})ivalue;"); + WriteLine("return offset;"); + CloseScope("Read"); + } + + private void EmitRead(string typeName) + { + OpenScope($"public static uint Read(this NativeReader reader, uint offset, out {typeName} handle)"); + WriteLine("uint value;"); + WriteLine("offset = reader.DecodeUnsigned(offset, out value);"); + WriteLine($"handle = new {typeName}((int)value);"); + WriteLine("handle._Validate();"); + WriteLine("return offset;"); + CloseScope("Read"); + } +} diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/MdBinaryWriterGen.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/MdBinaryWriterGen.cs new file mode 100644 index 00000000000000..19e870feb39183 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/MdBinaryWriterGen.cs @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// Generates the C# MdBinaryWriter class. This classes is responsible for correctly decoding +// data members in the .metadata file. See NativeFormatWriterGen.cs for how the MetadataWriter +// use this class. +// + +class MdBinaryWriterGen : CsWriter +{ + public MdBinaryWriterGen(string fileName) + : base(fileName) + { + } + + public void EmitSource() + { + WriteLine("#pragma warning disable 649"); + WriteLine(); + + WriteLine("using System;"); + WriteLine("using System.IO;"); + WriteLine("using System.Collections.Generic;"); + WriteLine("using System.Reflection;"); + WriteLine("using Internal.LowLevelLinq;"); + WriteLine("using Internal.NativeFormat;"); + WriteLine("using Debug = System.Diagnostics.Debug;"); + WriteLine(); + + OpenScope("namespace Internal.Metadata.NativeFormat.Writer"); + + OpenScope("internal static partial class MdBinaryWriter"); + + foreach (var primitiveType in SchemaDef.PrimitiveTypes) + { + EmitWritePrimitiveArray(primitiveType.Name); + } + + foreach (var enumType in SchemaDef.EnumTypes) + { + EmitWriteEnum(enumType); + } + + EmitWriteArray($"MetadataRecord"); + + foreach (var typeName in SchemaDef.HandleSchema) + { + EmitWrite(typeName); + EmitWriteArray(typeName); + } + + CloseScope("MdBinaryWriter"); + CloseScope("Internal.Metadata.NativeFormat.Writer"); + } + + private void EmitWritePrimitiveArray(string typeName) + { + OpenScope($"public static void Write(this NativeWriter writer, {typeName}[] values)"); + WriteLine("if (values == null)"); + WriteLine("{"); + WriteLine(" writer.WriteUnsigned(0);"); + WriteLine(" return;"); + WriteLine("}"); + WriteLine("writer.WriteUnsigned((uint)values.Length);"); + WriteLine($"foreach ({typeName} value in values)"); + WriteLine("{"); + WriteLine(" writer.Write(value);"); + WriteLine("}"); + CloseScope("Write"); + } + + private void EmitWriteEnum(EnumType enumType) + { + OpenScope($"public static void Write(this NativeWriter writer, {enumType.Name} value)"); + WriteLine($"writer.WriteUnsigned((uint)value);"); + CloseScope("Write"); + } + + private void EmitWrite(string typeName) + { + OpenScope($"public static void Write(this NativeWriter writer, {typeName} record)"); + WriteLine("if (record != null)"); + WriteLine(" writer.WriteUnsigned((uint)record.Handle.Offset);"); + WriteLine("else"); + WriteLine(" writer.WriteUnsigned(0);"); + CloseScope("Write"); + } + + private void EmitWriteArray(string typeName) + { + OpenScope($"public static void Write(this NativeWriter writer, List<{typeName}> values)"); + WriteLine("if (values == null)"); + WriteLine("{"); + WriteLine(" writer.WriteUnsigned(0);"); + WriteLine(" return;"); + WriteLine("}"); + WriteLine("writer.WriteUnsigned((uint)values.Count);"); + WriteLine($"foreach ({typeName} value in values)"); + WriteLine("{"); + WriteLine(" writer.Write(value);"); + WriteLine("}"); + CloseScope("Write"); + } +} diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/NativeFormatGen.csproj b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/NativeFormatGen.csproj new file mode 100644 index 00000000000000..f9927e4661d9ab --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/NativeFormatGen.csproj @@ -0,0 +1,53 @@ + + + + Exe + Properties + NativeFormatGen + NativeFormatGen + v4.6 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/Program.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/Program.cs new file mode 100644 index 00000000000000..1123043b453040 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/Program.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +class Program +{ + static void Main(string[] args) + { + using (var writer = new PublicGen(@"NativeFormatReaderCommonGen.cs")) + { + writer.EmitSource(); + } + + using (var writer = new ReaderGen(@"NativeFormatReaderGen.cs")) + { + writer.EmitSource(); + } + + using (var writer = new WriterGen(@"..\..\..\..\aot\ILCompiler.MetadataTransform\Internal\Metadata\NativeFormat\Writer\NativeFormatWriterGen.cs")) + { + writer.EmitSource(); + } + + using (var writer = new MdBinaryReaderGen(@"MdBinaryReaderGen.cs")) + { + writer.EmitSource(); + } + + using (var writer = new MdBinaryWriterGen(@"..\..\..\..\aot\ILCompiler.MetadataTransform\Internal\Metadata\NativeFormat\Writer\MdBinaryWriterGen.cs")) + { + writer.EmitSource(); + } + } +} diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/PublicGen.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/PublicGen.cs new file mode 100644 index 00000000000000..779be88aef37be --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/PublicGen.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; + +// +// This class generates the common API declarations that any metadata reader must implement. +// In general, this script consumes the metadata record schema defined in SchemaDef.cs and +// generates two interfaces and two structs for each - one(interface, struct); pair corresponding +// to the metadata record itself, and one(interface, struct); pair corresponding to the "Handle" +// used to reference the specific record type.The interfaces are used as a way of +// enforcing that the structs implement all required public members, but are not publicly consumed +// and are declared as internal. The use of structs instead of classes for each record was driven +// by a requirement from the Roslyn team that a metadata reader must minimize as much as possible +// the number of allocations made; thus, structs are allocated on the stack and exist only as +// long as the declaring scope remains on the stack. +// +// Each record interface simply declares as properties the members declared in the schema definition, +// and each struct is declared as partial and as implmenting the interface, thus requiring all +// interface properties to be supplied by the metadata reader implementation. +// +// Each handle interface requires type-specific equality functionality by itself implementing +// IEquatable, and the handle structs similarly declare this interface to require that +// the implementation be supplied by the reader. +// + +class PublicGen : CsWriter +{ + public PublicGen(string fileName) + : base(fileName) + { + } + + public void EmitSource() + { + WriteLine("using System;"); + WriteLine("using System.Reflection;"); + WriteLine("using System.Collections.Generic;"); + WriteLine(); + + WriteLine("#pragma warning disable 108 // base type 'uint' is not CLS-compliant"); + WriteLine("#pragma warning disable 3009 // base type 'uint' is not CLS-compliant"); + WriteLine("#pragma warning disable 282 // There is no defined ordering between fields in multiple declarations of partial class or struct"); + WriteLine(); + + OpenScope("namespace Internal.Metadata.NativeFormat"); + + EmitEnums(); + + CloseScope("Internal.Metadata.NativeFormat"); + } + + private void EmitEnums() + { + foreach (var record in SchemaDef.EnumSchema) + { + EmitEnum(record); + } + + // + // HandleType enum is not the the schema + // + EmitEnum( + new RecordDef( + name: "HandleType", + baseTypeName: "byte", + members: + new MemberDef[] { + new MemberDef(name: "Null", value: "0x0") + } + .Concat( + SchemaDef.HandleSchema.Select((name, index) => + new MemberDef(name: name, value: $"0x{index + 1:x}")) + ) + .ToArray() + ) + ); + } + + private void EmitEnum(RecordDef record) + { + if ((record.Flags & RecordDefFlags.Flags) != 0) + WriteScopeAttribute("[Flags]"); + OpenScope($"public enum {record.Name} : {record.BaseTypeName}"); + + foreach (var member in record.Members) + { + if (member.Comment != null) + { + WriteLineIfNeeded(); + WriteDocComment(member.Comment); + } + WriteLine($"{member.Name} = {member.Value},"); + } + + CloseScope(record.Name); + } +} diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/ReaderGen.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/ReaderGen.cs new file mode 100644 index 00000000000000..d068f7d047704a --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/ReaderGen.cs @@ -0,0 +1,300 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This class generates most of the implementation of the MetadataReader for the ProjectN format, +// ensuring that the contract defined by CsPublicGen2 is implemented.The generated file is +// 'NativeFormatReaderGen.cs', and any missing implementation is the supplied in the human-authored +// source counterpart 'NativeFormatReader.cs'. +// + +class ReaderGen : CsWriter +{ + public ReaderGen(string fileName) + : base(fileName) + { + } + + public void EmitSource() + { + WriteLine("#pragma warning disable 649"); + WriteLine("#pragma warning disable 169"); + WriteLine("#pragma warning disable 282 // There is no defined ordering between fields in multiple declarations of partial class or struct"); + WriteLine(); + + WriteLine("using System;"); + WriteLine("using System.Reflection;"); + WriteLine("using System.Collections.Generic;"); + WriteLine("using Internal.NativeFormat;"); + WriteLine(); + + OpenScope("namespace Internal.Metadata.NativeFormat"); + + foreach (var record in SchemaDef.RecordSchema) + { + EmitRecord(record); + EmitHandle(record); + } + + foreach (var typeName in SchemaDef.TypeNamesWithCollectionTypes) + { + EmitCollection(typeName + "HandleCollection", typeName + "Handle"); + } + + foreach (var primitiveType in SchemaDef.PrimitiveTypes) + { + EmitCollection(primitiveType.TypeName + "Collection", primitiveType.Name); + } + + EmitOpaqueHandle(); + EmitCollection("HandleCollection", "Handle"); + EmitMetadataReader(); + + CloseScope("Internal.Metadata.NativeFormat"); + } + + private void EmitRecord(RecordDef record) + { + OpenScope($"public partial struct {record.Name}"); + + WriteLine("internal MetadataReader _reader;"); + WriteLine($"internal {record.Name}Handle _handle;"); + + OpenScope($"public {record.Name}Handle Handle"); + OpenScope("get"); + WriteLine("return _handle;"); + CloseScope(); + CloseScope("Handle"); + + foreach (var member in record.Members) + { + if ((member.Flags & MemberDefFlags.NotPersisted) != 0) + continue; + + string memberType = member.GetMemberType(); + string fieldType = member.GetMemberType(MemberTypeKind.ReaderField); + + string fieldName = member.GetMemberFieldName(); + + string description = member.GetMemberDescription(); + if (description != null) + WriteDocComment(description); + OpenScope($"public {memberType} {member.Name}"); + OpenScope("get"); + if (fieldType != memberType) + WriteLine($"return ({memberType}){fieldName};"); + else + WriteLine($"return {fieldName};"); + CloseScope(); + CloseScope(member.Name); + + WriteLineIfNeeded(); + WriteLine($"internal {fieldType} {fieldName};"); + } + + CloseScope(record.Name); + } + + private void EmitHandle(RecordDef record) + { + string handleName = $"{record.Name}Handle"; + + OpenScope($"public partial struct {handleName}"); + + OpenScope("public override bool Equals(object obj)"); + WriteLine($"if (obj is {handleName})"); + WriteLine($" return _value == (({handleName})obj)._value;"); + WriteLine("else if (obj is Handle)"); + WriteLine(" return _value == ((Handle)obj)._value;"); + WriteLine("else"); + WriteLine(" return false;"); + CloseScope("Equals"); + + OpenScope($"public bool Equals({handleName} handle)"); + WriteLine("return _value == handle._value;"); + CloseScope("Equals"); + + OpenScope("public bool Equals(Handle handle)"); + WriteLine("return _value == handle._value;"); + CloseScope("Equals"); + + OpenScope("public override int GetHashCode()"); + WriteLine("return (int)_value;"); + CloseScope("GetHashCode"); + + WriteLineIfNeeded(); + WriteLine("internal int _value;"); + + OpenScope($"internal {handleName}(Handle handle) : this(handle._value)"); + CloseScope(); + + OpenScope($"internal {handleName}(int value)"); + WriteLine("HandleType hType = (HandleType)(value >> 24);"); + WriteLine($"if (!(hType == 0 || hType == HandleType.{record.Name} || hType == HandleType.Null))"); + WriteLine(" throw new ArgumentException();"); + WriteLine($"_value = (value & 0x00FFFFFF) | (((int)HandleType.{record.Name}) << 24);"); + WriteLine("_Validate();"); + CloseScope(); + + OpenScope($"public static implicit operator Handle({handleName} handle)"); + WriteLine("return new Handle(handle._value);"); + CloseScope("Handle"); + + OpenScope("internal int Offset"); + OpenScope("get"); + WriteLine("return (this._value & 0x00FFFFFF);"); + CloseScope(); + CloseScope("Offset"); + + OpenScope($"public {record.Name} Get{record.Name}(MetadataReader reader)"); + WriteLine($"return reader.Get{record.Name}(this);"); + CloseScope($"Get{record.Name}"); + + OpenScope("public bool IsNull(MetadataReader reader)"); + WriteLine("return reader.IsNull(this);"); + CloseScope("IsNull"); + + OpenScope("public Handle ToHandle(MetadataReader reader)"); + WriteLine("return reader.ToHandle(this);"); + CloseScope("ToHandle"); + + WriteScopeAttribute("[System.Diagnostics.Conditional(\"DEBUG\")]"); + OpenScope("internal void _Validate()"); + WriteLine($"if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.{record.Name})"); + WriteLine(" throw new ArgumentException();"); + CloseScope("_Validate"); + + OpenScope("public override string ToString()"); + WriteLine("return string.Format(\"{0:X8}\", _value);"); + CloseScope("ToString"); + + CloseScope(handleName); + } + + private void EmitCollection(string collectionTypeName, string elementTypeName) + { + OpenScope($"public partial struct {collectionTypeName}"); + + WriteLine("private NativeReader _reader;"); + WriteLine("private uint _offset;"); + + OpenScope($"internal {collectionTypeName}(NativeReader reader, uint offset)"); + WriteLine("_offset = offset;"); + WriteLine("_reader = reader;"); + CloseScope(); + + OpenScope("public int Count"); + OpenScope("get"); + WriteLine("uint count;"); + WriteLine("_reader.DecodeUnsigned(_offset, out count);"); + WriteLine("return (int)count;"); + CloseScope(); + CloseScope("Count"); + + OpenScope($"public Enumerator GetEnumerator()"); + WriteLine($"return new Enumerator(_reader, _offset);"); + CloseScope("GetEnumerator"); + + OpenScope($"public struct Enumerator"); + + WriteLine("private NativeReader _reader;"); + WriteLine("private uint _offset;"); + WriteLine("private uint _remaining;"); + WriteLine($"private {elementTypeName} _current;"); + + OpenScope($"internal Enumerator(NativeReader reader, uint offset)"); + WriteLine("_reader = reader;"); + WriteLine("_offset = reader.DecodeUnsigned(offset, out _remaining);"); + WriteLine($"_current = default({elementTypeName});"); + CloseScope(); + + OpenScope($"public {elementTypeName} Current"); + OpenScope("get"); + WriteLine("return _current;"); + CloseScope(); + CloseScope("Current"); + + OpenScope("public bool MoveNext()"); + WriteLine("if (_remaining == 0)"); + WriteLine(" return false;"); + WriteLine("_remaining--;"); + WriteLine("_offset = _reader.Read(_offset, out _current);"); + WriteLine("return true;"); + CloseScope("MoveNext"); + + OpenScope("public void Dispose()"); + CloseScope("Dispose"); + + CloseScope("Enumerator"); + + CloseScope(collectionTypeName); + } + + private void EmitOpaqueHandle() + { + OpenScope("public partial struct Handle"); + + foreach (var record in SchemaDef.RecordSchema) + { + string handleName = $"{record.Name}Handle"; + + OpenScope($"public {handleName} To{handleName}(MetadataReader reader)"); + WriteLine($"return new {handleName}(this);"); + CloseScope($"To{handleName}"); + } + + CloseScope("Handle"); + } + + private void EmitMetadataReader() + { + OpenScope("public partial class MetadataReader"); + + foreach (var record in SchemaDef.RecordSchema) + { + OpenScope($"public {record.Name} Get{record.Name}({record.Name}Handle handle)"); + if (record.Name == "ConstantStringValue") + { + WriteLine("if (IsNull(handle))"); + WriteLine(" return new ConstantStringValue();"); + } + WriteLine($"{record.Name} record;"); + WriteLine("record._reader = this;"); + WriteLine("record._handle = handle;"); + WriteLine("var offset = (uint)handle.Offset;"); + foreach (var member in record.Members) + { + if ((member.Flags & MemberDefFlags.NotPersisted) != 0) + continue; + WriteLine($"offset = _streamReader.Read(offset, out record.{member.GetMemberFieldName()});"); + } + WriteLine("return record;"); + CloseScope($"Get{record.Name}"); + } + + foreach (var record in SchemaDef.RecordSchema) + { + OpenScope($"internal Handle ToHandle({record.Name}Handle handle)"); + WriteLine("return new Handle(handle._value);"); + CloseScope("ToHandle"); + } + + foreach (var record in SchemaDef.RecordSchema) + { + string handleName = $"{record.Name}Handle"; + + OpenScope($"internal {handleName} To{handleName}(Handle handle)"); + WriteLine($"return new {handleName}(handle._value);"); + CloseScope($"To{handleName}"); + } + + foreach (var record in SchemaDef.RecordSchema) + { + OpenScope($"internal bool IsNull({record.Name}Handle handle)"); + WriteLine("return (handle._value & 0x00FFFFFF) == 0;"); + CloseScope("IsNull"); + } + + CloseScope("MetadataReader"); + } +} diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/SchemaDef.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/SchemaDef.cs new file mode 100644 index 00000000000000..6936fb23923487 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/SchemaDef.cs @@ -0,0 +1,768 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; + +/// +/// Defines the set of flags that may be applied to a schema record definition. +/// +[Flags] +public enum RecordDefFlags +{ + Enum = 0x01, // Indicates that the record is actually an enum. + Flags = 0x02, // Indicates that [Flags] should be applied to enum definition. + CustomCompare = 0x04, // Indicates a specific set of members have been flagged for use in + // implementing equality functionality; else all members are used. + ReentrantEquals = 0x08, // The generated Equals method is potentially reentrant on the same instance + // and should have a fast exit path to protect from infinite recursion. +} + +/// +/// Defines the set of flags that may be applied to the member definition of a schema record. +/// +[Flags] +public enum MemberDefFlags +{ + Map = 0x0001, // => List for writer + List = 0x0002, // => List for writer + Array = 0x0004, // => RecordType[] for writer + + Collection = MemberDefFlags.Map | MemberDefFlags.List | MemberDefFlags.Array, + Sequence = MemberDefFlags.List | MemberDefFlags.Array, + + RecordRef = 0x0008, + + Child = 0x0010, // Member instance is logically defined and owned by record; + // otherwise instance may be shared (such as a TypeRef). + Name = 0x0020, // May be used as the member's simple name for diagnostics. + NotPersisted = 0x0040, // Indicates member is not written to or read from metadata. + Compare = 0x0080, // Indicates member should be used for equality functionality. + EnumerateForHashCode = 0x0100, // Indicates that the collection is safe to be enumerated in GetHashCode + // without causing reentrancy + CustomCompare = 0x0200, // Indicates that this member uses a custom comparer +} + +public enum MemberTypeKind +{ + Accessor, + ReaderField, + WriterField +}; + +/// +/// Encapsulates definition of member in schema record definition. +/// +public class MemberDef +{ + public MemberDef(string name, object typeName = null, MemberDefFlags flags = 0, string value = null, string comment = null) + { + Name = name; + TypeName = typeName; + Flags = flags; + Value = value; + Comment = comment; + } + + readonly public string Name; + readonly public object TypeName; + readonly public MemberDefFlags Flags; + readonly public string Value; + readonly public string Comment; + + public string GetMemberType(MemberTypeKind kind = MemberTypeKind.Accessor) + { + string typeName; + if ((Flags & MemberDefFlags.RecordRef) != 0) + { + if (TypeName is String[]) + { + typeName = (kind == MemberTypeKind.WriterField) ? "MetadataRecord" : "Handle"; + } + else + { + typeName = (kind == MemberTypeKind.WriterField) ? + (TypeName != null ? (string)TypeName : "MetadataRecord"): $"{TypeName}Handle"; + } + } + else + { + typeName = (string)TypeName; + } + if ((Flags & MemberDefFlags.Collection) != 0) + { + if (kind == MemberTypeKind.WriterField) + { + if ((Flags & (MemberDefFlags.List | MemberDefFlags.Map)) != 0) + return $"List<{typeName}>"; + else + return $"{typeName}[]"; + } + + return $"{typeName}Collection"; + } + return typeName; + } + + public string GetMemberFieldName() + { + return "_" + Char.ToLower(Name[0], System.Globalization.CultureInfo.InvariantCulture) + Name.Substring(1); + } + + public string GetMemberDescription() + { + var typeSet = TypeName as String[]; + if (typeSet == null) + return null; + + return "One of: " + String.Join(", ", typeSet); + } +} + +/// +/// Encapsulates definition of schema record. +/// +public class RecordDef +{ + public RecordDef(string name, string baseTypeName = null, RecordDefFlags flags = 0, string comment = null, MemberDef[] members = null) + { + Name = name; + BaseTypeName = baseTypeName; + Flags = flags; + Comment = comment; + Members = members; + } + + readonly public string Name; + readonly public string BaseTypeName; + readonly public RecordDefFlags Flags; + readonly public string Comment; + readonly public MemberDef[] Members; +} + +public class EnumType +{ + public EnumType(string name, string underlyingType) + { + Name = name; + UnderlyingType = underlyingType; + } + + readonly public string Name; + readonly public string UnderlyingType; +} + +public class PrimitiveType +{ + public PrimitiveType(string name, string typeName, bool customCompare = false) + { + Name = name; + TypeName = typeName; + CustomCompare = customCompare; + } + + readonly public string Name; + readonly public string TypeName; + readonly public bool CustomCompare; +} + +/// +/// This class defines the metadata schema that is consumed by all generators. +/// +class SchemaDef +{ + public static readonly EnumType[] EnumTypes = new EnumType[] + { + new EnumType("AssemblyFlags", "uint"), + new EnumType("AssemblyHashAlgorithm", "uint"), + new EnumType("CallingConventions", "ushort"), + new EnumType("EventAttributes", "ushort"), + new EnumType("FieldAttributes", "ushort"), + new EnumType("GenericParameterAttributes", "ushort"), + new EnumType("GenericParameterKind", "byte"), + new EnumType("MethodAttributes", "ushort"), + new EnumType("MethodImplAttributes", "ushort"), + new EnumType("MethodSemanticsAttributes", "ushort"), + new EnumType("NamedArgumentMemberKind", "byte"), + new EnumType("ParameterAttributes", "ushort"), + new EnumType("PInvokeAttributes", "ushort"), + new EnumType("PropertyAttributes", "ushort"), + new EnumType("TypeAttributes", "uint"), + }; + + public static readonly PrimitiveType[] PrimitiveTypes = new PrimitiveType[] + { + new PrimitiveType("bool", "Boolean"), + new PrimitiveType("char", "Char"), + new PrimitiveType("byte", "Byte"), + new PrimitiveType("sbyte", "SByte"), + new PrimitiveType("short", "Int16"), + new PrimitiveType("ushort", "UInt16"), + new PrimitiveType("int", "Int32"), + new PrimitiveType("uint", "UInt32"), + new PrimitiveType("long", "Int64"), + new PrimitiveType("ulong", "UInt64"), + new PrimitiveType("float", "Single", customCompare: true), + new PrimitiveType("double", "Double", customCompare: true), + }; + + // These enums supplement those defined by System.Reflection.Primitives. + public static readonly RecordDef[] EnumSchema = new RecordDef[] + { + // AssemblyFlags - as defined in ECMA + new RecordDef( + name: "AssemblyFlags", + baseTypeName: "uint", + flags: RecordDefFlags.Enum | RecordDefFlags.Flags, + members: new MemberDef[] { + new MemberDef(name: "PublicKey", value: "0x1", comment: "The assembly reference holds the full (unhashed) public key."), + new MemberDef(name: "Retargetable", value: "0x100", comment: "The implementation of this assembly used at runtime is not expected to match the version seen at compile time."), + new MemberDef(name: "DisableJITcompileOptimizer", value: "0x4000", comment: "Reserved."), + new MemberDef(name: "EnableJITcompileTracking", value: "0x8000", comment: "Reserved."), + } + ), + // AssemblyHashAlgorithm - as defined in ECMA + new RecordDef( + name: "AssemblyHashAlgorithm", + baseTypeName: "uint", + flags: RecordDefFlags.Enum, + members: new MemberDef[] { + new MemberDef(name: "None", value: "0x0"), + new MemberDef(name: "Reserved", value: "0x8003"), + new MemberDef(name: "SHA1", value: "0x8004"), + } + ), + // NamedArgumentMemberKind - used to disambiguate the referenced members of the named + // arguments to a custom attribute instance. + new RecordDef( + name: "NamedArgumentMemberKind", + baseTypeName: "byte", + flags: RecordDefFlags.Enum, + members: new MemberDef[] { + new MemberDef(name: "Property", value: "0x0", comment: "Specifies the name of a property"), + new MemberDef(name: "Field", value: "0x1", comment: "Specifies the name of a field"), + } + ), + // GenericParameterKind - used to distinguish between generic type and generic method type parameters. + new RecordDef( + name: "GenericParameterKind", + baseTypeName: "byte", + flags: RecordDefFlags.Enum, + members: new MemberDef[] { + new MemberDef(name: "GenericTypeParameter", value: "0x0", comment: "Represents a type parameter for a generic type."), + new MemberDef(name: "GenericMethodParameter", value: "0x1", comment: "Represents a type parameter from a generic method."), + } + ) + } + .OrderBy(record => record.Name, StringComparer.Ordinal) + .ToArray(); + + // + // Record schema definition + // + + // + // ConstantXXXValue, ConstantXXXArray, and corresponding Handle types + // + + // Set of record schema definitions (see format description in "Metadata records" section below) + // that represent contant primitive type values. Adds concept of constant managed reference, which + // must always have a null value (thus the use of the NotPersisted flag). + + private static readonly RecordDef[] ConstantValueRecordSchema = + ( + from primitiveType in PrimitiveTypes select + new RecordDef( + name: "Constant" + primitiveType.TypeName + "Value", + members: new MemberDef[] { + new MemberDef(name: "Value", typeName: primitiveType.Name, + flags: primitiveType.CustomCompare ? MemberDefFlags.CustomCompare : 0) + } + ) + ) + .Concat + ( + new RecordDef[] { + new RecordDef( + name: "ConstantReferenceValue", + members: new MemberDef[] { + new MemberDef(name: "Value", typeName: "Object", flags: MemberDefFlags.NotPersisted) + } + ), + new RecordDef( + name: "ConstantStringValue", + members: new MemberDef[] { + new MemberDef(name: "Value", typeName: "string") + } + ) + } + ) + .ToArray(); + + // Set of record schema definitions (see format description in "Metadata records" section below) + // that represent contant arrays primitive type values. Adds concept of a constant array of handle values (currently used to store + // an array TypeDefOrRefOrSpec handles corresponding to System.Type arguments to the instantiation of a custom attribute, or to store + // custom initialized object[] arrays in custom attributes). + + private static readonly RecordDef[] ConstantArrayRecordSchema = + ( + from primitiveType in PrimitiveTypes select + new RecordDef( + name: "Constant" + primitiveType.TypeName + "Array", + members: new MemberDef[] { + new MemberDef(name: "Value", typeName: primitiveType.TypeName, + flags: MemberDefFlags.Array | (primitiveType.CustomCompare ? MemberDefFlags.CustomCompare : 0)) + } + ) + ) + .Concat + ( + new RecordDef[] { + new RecordDef( + name: "ConstantHandleArray", + members: new MemberDef[] { + new MemberDef(name: "Value", flags: MemberDefFlags.RecordRef | MemberDefFlags.List) + } + ), + new RecordDef( + name: "ConstantStringArray", + members: new MemberDef[] { + new MemberDef(name: "Value", typeName: new string[] { "ConstantStringValue", "ConstantReferenceValue" }, flags: MemberDefFlags.RecordRef | MemberDefFlags.List) + } + ), + new RecordDef( + name: "ConstantEnumArray", + members: new MemberDef[] { + new MemberDef("ElementType", TypeDefOrRefOrSpec, MemberDefFlags.RecordRef), + new MemberDef("Value", ConstantEnumArrayValue, MemberDefFlags.RecordRef) + } + ), + } + ) + .ToArray(); + + private static readonly RecordDef[] ConstantRecordSchema = + ConstantValueRecordSchema.Concat(ConstantArrayRecordSchema) + .OrderBy(record => record.Name, StringComparer.Ordinal) + .ToArray(); + + // + // Common tuple definitions + // + + private static readonly string[] EnumConstantValue = new string[] + { + "ConstantByteValue", + "ConstantSByteValue", + "ConstantInt16Value", + "ConstantUInt16Value", + "ConstantInt32Value", + "ConstantUInt32Value", + "ConstantInt64Value", + "ConstantUInt64Value", + }; + + private static readonly string[] ConstantEnumArrayValue = new string[] + { + "ConstantByteArray", + "ConstantSByteArray", + "ConstantInt16Array", + "ConstantUInt16Array", + "ConstantInt32Array", + "ConstantUInt32Array", + "ConstantInt64Array", + "ConstantUInt64Array", + }; + + private static readonly string[] TypeDefOrRef = new string[] + { + "TypeDefinition", + "TypeReference", + }; + + private static readonly string[] TypeDefOrRefOrSpec = new string[] + { + "TypeDefinition", + "TypeReference", + "TypeSpecification", + }; + + private static readonly string[] TypeDefOrRefOrSpecOrMod = new string[] + { + "TypeDefinition", + "TypeReference", + "TypeSpecification", + "ModifiedType", + }; + + private static readonly string[] TypeSig = new string[] + { + "TypeInstantiationSignature", + "SZArraySignature", + "ArraySignature", + "PointerSignature", + "FunctionPointerSignature", + "ByReferenceSignature", + "TypeVariableSignature", + "MethodTypeVariableSignature", + }; + + private static readonly string[] TypeDefOrRefOrSpecOrConstant = + TypeDefOrRefOrSpec.Concat(from constantRecord in ConstantRecordSchema select constantRecord.Name).ToArray(); + + private static readonly string[] MethodDefOrRef = new string[] + { + "QualifiedMethod", + "MemberReference", + }; + + // + // Metadata records + // The record schema is defined as a list of tuples, one for each record type. + // Record tuple format: (name, base type (not currently used), flags, [members]) + // Member tuple format: (name, type, flags) + // These are largely based on the definitions in ECMA335. + // + public static readonly RecordDef[] RecordSchema = new RecordDef[] + { + new RecordDef( + name: "TypeDefinition", + flags: RecordDefFlags.CustomCompare, + members: new MemberDef[] { + new MemberDef("Flags", "TypeAttributes"), + new MemberDef("BaseType", TypeDefOrRefOrSpec, MemberDefFlags.RecordRef), + new MemberDef("NamespaceDefinition", "NamespaceDefinition", MemberDefFlags.RecordRef | MemberDefFlags.Compare), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.Compare), + new MemberDef("Size", "uint"), + new MemberDef("PackingSize", "ushort"), + new MemberDef("EnclosingType", "TypeDefinition", MemberDefFlags.RecordRef | MemberDefFlags.Compare), + new MemberDef("NestedTypes", "TypeDefinition", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Methods", "Method", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Fields", "Field", MemberDefFlags.Map | MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Properties", "Property", MemberDefFlags.Map | MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Events", "Event", MemberDefFlags.Map | MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("GenericParameters", "GenericParameter", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Interfaces", TypeDefOrRefOrSpec, MemberDefFlags.List | MemberDefFlags.RecordRef), + // COMPLETENESS: new MemberDef("MethodImpls", "MethodImpl", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + new RecordDef( + name: "TypeReference", + members: new MemberDef[] { + new MemberDef("ParentNamespaceOrType", new string[] { "NamespaceReference", "TypeReference" }, MemberDefFlags.RecordRef), + new MemberDef("TypeName", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.Name), + // COMPLETENESS: new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + new RecordDef( + name: "TypeSpecification", + members: new MemberDef[] { + new MemberDef("Signature", TypeDefOrRef.Concat(TypeSig).ToArray(), MemberDefFlags.RecordRef | MemberDefFlags.Child), + // COMPLETENESS: new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + new RecordDef( + name: "ScopeDefinition", + flags: RecordDefFlags.CustomCompare, + members: new MemberDef[] { + new MemberDef("Flags", "AssemblyFlags", MemberDefFlags.Compare), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.Compare), + new MemberDef("HashAlgorithm", "AssemblyHashAlgorithm", MemberDefFlags.Compare), + new MemberDef("MajorVersion", "ushort", MemberDefFlags.Compare), + new MemberDef("MinorVersion", "ushort", MemberDefFlags.Compare), + new MemberDef("BuildNumber", "ushort", MemberDefFlags.Compare), + new MemberDef("RevisionNumber", "ushort", MemberDefFlags.Compare), + new MemberDef("PublicKey", "Byte", MemberDefFlags.Array | MemberDefFlags.Compare), + new MemberDef("Culture", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.Compare), + new MemberDef("RootNamespaceDefinition", "NamespaceDefinition", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("EntryPoint", "QualifiedMethod", MemberDefFlags.RecordRef), + new MemberDef("GlobalModuleType", "TypeDefinition", MemberDefFlags.RecordRef), + new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("ModuleName", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.Compare), + new MemberDef("Mvid", "Byte", MemberDefFlags.Array | MemberDefFlags.Compare), + new MemberDef("ModuleCustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + new RecordDef( + name: "ScopeReference", + members: new MemberDef[] { + new MemberDef("Flags", "AssemblyFlags"), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("MajorVersion", "ushort"), + new MemberDef("MinorVersion", "ushort"), + new MemberDef("BuildNumber", "ushort"), + new MemberDef("RevisionNumber", "ushort"), + new MemberDef("PublicKeyOrToken", "Byte", MemberDefFlags.Array), + new MemberDef("Culture", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child), + // COMPLETENESS: new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + new RecordDef( + name: "NamespaceDefinition", + flags: RecordDefFlags.CustomCompare, + members: new MemberDef[] { + new MemberDef("ParentScopeOrNamespace", new string[] { "NamespaceDefinition", "ScopeDefinition" }, MemberDefFlags.RecordRef | MemberDefFlags.Compare), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.Compare), + new MemberDef("TypeDefinitions", "TypeDefinition", MemberDefFlags.Map | MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("TypeForwarders", "TypeForwarder", MemberDefFlags.Map | MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("NamespaceDefinitions", "NamespaceDefinition", MemberDefFlags.Map | MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + new RecordDef( + name: "NamespaceReference", + members: new MemberDef[] { + new MemberDef("ParentScopeOrNamespace", new string[] { "NamespaceReference", "ScopeReference" }, MemberDefFlags.RecordRef), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + new RecordDef( + name: "Method", + members: new MemberDef[] { + new MemberDef("Flags", "MethodAttributes"), + new MemberDef("ImplFlags", "MethodImplAttributes"), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Signature", "MethodSignature", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Parameters", "Parameter", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.EnumerateForHashCode), + new MemberDef("GenericParameters", "GenericParameter", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.EnumerateForHashCode), + new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + new RecordDef( + name: "QualifiedMethod", + members: new MemberDef[] { + new MemberDef("Method", "Method", MemberDefFlags.RecordRef), + new MemberDef("EnclosingType", "TypeDefinition", MemberDefFlags.RecordRef), + } + ), + new RecordDef( + name: "MethodInstantiation", + members: new MemberDef[] { + new MemberDef("Method", MethodDefOrRef, MemberDefFlags.RecordRef), + new MemberDef("GenericTypeArguments", TypeDefOrRefOrSpec, MemberDefFlags.List | MemberDefFlags.RecordRef), + // COMPLETENESS: new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + new RecordDef( + name: "MemberReference", + members: new MemberDef[] { + new MemberDef("Parent", TypeDefOrRefOrSpec, MemberDefFlags.RecordRef), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Signature", new string[] { "MethodSignature", "FieldSignature" }, MemberDefFlags.RecordRef | MemberDefFlags.Child), + // COMPLETENESS: new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + new RecordDef( + name: "Field", + members: new MemberDef[] { + new MemberDef("Flags", "FieldAttributes"), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Signature", "FieldSignature", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("DefaultValue", TypeDefOrRefOrSpecOrConstant, MemberDefFlags.RecordRef), + new MemberDef("Offset", "uint"), + new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.EnumerateForHashCode), + } + ), + new RecordDef( + name: "QualifiedField", + members: new MemberDef[] { + new MemberDef("Field", "Field", MemberDefFlags.RecordRef), + new MemberDef("EnclosingType", "TypeDefinition", MemberDefFlags.RecordRef), + } + ), + new RecordDef( + name: "Property", + members: new MemberDef[] { + new MemberDef("Flags", "PropertyAttributes"), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Signature", "PropertySignature", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("MethodSemantics", "MethodSemantics", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.EnumerateForHashCode), + new MemberDef("DefaultValue", TypeDefOrRefOrSpecOrConstant, MemberDefFlags.RecordRef), + new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.EnumerateForHashCode), + } + ), + new RecordDef( + name: "Event", + members: new MemberDef[] { + new MemberDef("Flags", "EventAttributes"), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Type", TypeDefOrRefOrSpec, MemberDefFlags.RecordRef), + new MemberDef("MethodSemantics", "MethodSemantics", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.EnumerateForHashCode), + new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.EnumerateForHashCode), + } + ), + new RecordDef( + name: "CustomAttribute", + flags: RecordDefFlags.ReentrantEquals, + members: new MemberDef[] { + new MemberDef("Constructor", MethodDefOrRef, MemberDefFlags.RecordRef), + new MemberDef("FixedArguments", TypeDefOrRefOrSpecOrConstant, MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.EnumerateForHashCode), + new MemberDef("NamedArguments", "NamedArgument", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.EnumerateForHashCode), + } + ), + new RecordDef( + name: "NamedArgument", + members: new MemberDef[] { + new MemberDef("Flags", "NamedArgumentMemberKind"), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Type", TypeDefOrRefOrSpec, MemberDefFlags.RecordRef), + new MemberDef("Value", TypeDefOrRefOrSpecOrConstant, MemberDefFlags.RecordRef), + } + ), + new RecordDef( + name: "ConstantBoxedEnumValue", + members: new MemberDef[] { + new MemberDef("Value", EnumConstantValue, MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Type", TypeDefOrRefOrSpec, MemberDefFlags.RecordRef) + } + ), + new RecordDef( + name: "GenericParameter", + members: new MemberDef[] { + new MemberDef("Number", "ushort"), + new MemberDef("Flags", "GenericParameterAttributes"), + new MemberDef("Kind", "GenericParameterKind"), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("Constraints", TypeDefOrRefOrSpecOrMod, MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.EnumerateForHashCode), + new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + /* COMPLETENESS new RecordDef( + name: "MethodImpl", + members: new MemberDef[] { + new MemberDef("MethodBody", MethodDefOrRef, MemberDefFlags.RecordRef), + new MemberDef("MethodDeclaration", MethodDefOrRef, MemberDefFlags.RecordRef), + } + ),*/ + new RecordDef( + name: "Parameter", + members: new MemberDef[] { + new MemberDef("Flags", "ParameterAttributes"), + new MemberDef("Sequence", "ushort"), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child), + new MemberDef("DefaultValue", TypeDefOrRefOrSpecOrConstant, MemberDefFlags.RecordRef), + new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + new RecordDef( + name: "MethodSemantics", + members: new MemberDef[] { + new MemberDef("Attributes", "MethodSemanticsAttributes"), + new MemberDef("Method", "Method", MemberDefFlags.RecordRef), + } + ), + new RecordDef( + name: "TypeInstantiationSignature", + members: new MemberDef[] { + new MemberDef("GenericType", TypeDefOrRefOrSpec, MemberDefFlags.RecordRef), + new MemberDef("GenericTypeArguments", TypeDefOrRefOrSpec, MemberDefFlags.List | MemberDefFlags.RecordRef), + } + ), + new RecordDef( + name: "SZArraySignature", + members: new MemberDef[] { + new MemberDef("ElementType", TypeDefOrRefOrSpecOrMod, MemberDefFlags.RecordRef), + } + ), + new RecordDef( + name: "ArraySignature", + members: new MemberDef[] { + new MemberDef("ElementType", TypeDefOrRefOrSpec, MemberDefFlags.RecordRef), + new MemberDef("Rank", "int"), + new MemberDef("Sizes", "Int32", MemberDefFlags.Array), + new MemberDef("LowerBounds", "Int32", MemberDefFlags.Array), + } + ), + new RecordDef( + name: "ByReferenceSignature", + members: new MemberDef[] { + new MemberDef("Type", TypeDefOrRefOrSpec, MemberDefFlags.RecordRef), + } + ), + new RecordDef( + name: "PointerSignature", + members: new MemberDef[] { + new MemberDef("Type", TypeDefOrRefOrSpecOrMod, MemberDefFlags.RecordRef), + } + ), + new RecordDef( + name: "FunctionPointerSignature", + members: new MemberDef[] { + new MemberDef("Signature", "MethodSignature", MemberDefFlags.RecordRef), + } + ), + new RecordDef( + name: "TypeVariableSignature", + members: new MemberDef[] { + new MemberDef("Number", "int"), + } + ), + new RecordDef( + name: "MethodTypeVariableSignature", + members: new MemberDef[] { + new MemberDef("Number", "int"), + } + ), + new RecordDef( + name: "FieldSignature", + members: new MemberDef[] { + new MemberDef("Type", TypeDefOrRefOrSpecOrMod, MemberDefFlags.RecordRef), + } + ), + new RecordDef( + name: "PropertySignature", + members: new MemberDef[] { + new MemberDef("CallingConvention", "CallingConventions"), + new MemberDef("Type", TypeDefOrRefOrSpecOrMod, MemberDefFlags.RecordRef), + new MemberDef("Parameters", TypeDefOrRefOrSpecOrMod, MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.EnumerateForHashCode), + } + ), + new RecordDef( + name: "MethodSignature", + members: new MemberDef[] { + new MemberDef("CallingConvention", "CallingConventions"), + new MemberDef("GenericParameterCount", "int"), + new MemberDef("ReturnType", TypeDefOrRefOrSpecOrMod, MemberDefFlags.RecordRef), + new MemberDef("Parameters", TypeDefOrRefOrSpecOrMod, MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.EnumerateForHashCode), + new MemberDef("VarArgParameters", TypeDefOrRefOrSpecOrMod, MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.EnumerateForHashCode), + } + ), + new RecordDef( + name: "TypeForwarder", + members: new MemberDef[] { + new MemberDef("Scope", "ScopeReference", MemberDefFlags.RecordRef), + new MemberDef("Name", "ConstantStringValue", MemberDefFlags.RecordRef | MemberDefFlags.Child | MemberDefFlags.Name), + new MemberDef("NestedTypes", "TypeForwarder", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + // COMPLETENESS: new MemberDef("CustomAttributes", "CustomAttribute", MemberDefFlags.List | MemberDefFlags.RecordRef | MemberDefFlags.Child), + } + ), + new RecordDef( + name: "ModifiedType", + members: new MemberDef[] { + new MemberDef("IsOptional", "bool"), + new MemberDef("ModifierType", TypeDefOrRefOrSpec, MemberDefFlags.RecordRef), + new MemberDef("Type", TypeDefOrRefOrSpecOrMod, MemberDefFlags.RecordRef), + } + ) + } + // ConstantXXXValue provided in ConstantRecordSchema and appended to end of this list. + .Concat(ConstantRecordSchema) + .OrderBy(record => record.Name, StringComparer.Ordinal) + .ToArray(); + + /// + // Contains a list of records with corresponding Handle types (currently all of them). + /// + public static readonly string[] HandleSchema = (from record in RecordSchema select record.Name).ToArray(); + + public static readonly string[] TypeNamesWithCollectionTypes = + RecordSchema.SelectMany(r => + from member in r.Members + let memberTypeName = member.TypeName as string + where memberTypeName != null && + (member.Flags & MemberDefFlags.Collection) != 0 && + !PrimitiveTypes.Any(pt => pt.TypeName == memberTypeName) + select memberTypeName + ).Concat(new[] { "ScopeDefinition" }).Distinct().ToArray(); +} diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/WriterGen.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/WriterGen.cs new file mode 100644 index 00000000000000..6b03f4d8c78698 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/WriterGen.cs @@ -0,0 +1,267 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +class WriterGen : CsWriter +{ + public WriterGen(string fileName) + : base(fileName) + { + } + + public void EmitSource() + { + WriteLine("#pragma warning disable 649"); + WriteLine(); + + WriteLine("using System;"); + WriteLine("using System.IO;"); + WriteLine("using System.Collections.Generic;"); + WriteLine("using System.Reflection;"); + WriteLine("using System.Threading;"); + WriteLine("using Internal.LowLevelLinq;"); + WriteLine("using Internal.Metadata.NativeFormat.Writer;"); + WriteLine("using Internal.NativeFormat;"); + WriteLine("using HandleType = Internal.Metadata.NativeFormat.HandleType;"); + WriteLine("using Debug = System.Diagnostics.Debug;"); + WriteLine(); + + OpenScope("namespace Internal.Metadata.NativeFormat.Writer"); + + foreach (var record in SchemaDef.RecordSchema) + { + EmitRecord(record); + } + + CloseScope("Internal.Metadata.NativeFormat.Writer"); + } + + private void EmitRecord(RecordDef record) + { + bool isConstantStringValue = record.Name == "ConstantStringValue"; + + OpenScope($"public partial class {record.Name} : MetadataRecord"); + + if ((record.Flags & RecordDefFlags.ReentrantEquals) != 0) + { + OpenScope($"public {record.Name}()"); + WriteLine("_equalsReentrancyGuard = new ThreadLocal(() => new ReentrancyGuardStack());"); + CloseScope(); + } + + OpenScope("public override HandleType HandleType"); + OpenScope("get"); + WriteLine($"return HandleType.{record.Name};"); + CloseScope(); + CloseScope("HandleType"); + + OpenScope("internal override void Visit(IRecordVisitor visitor)"); + foreach (var member in record.Members) + { + if ((member.Flags & MemberDefFlags.RecordRef) == 0) + continue; + + WriteLine($"{member.Name} = visitor.Visit(this, {member.Name});"); + } + CloseScope("Visit"); + + OpenScope("public override sealed bool Equals(Object obj)"); + WriteLine("if (Object.ReferenceEquals(this, obj)) return true;"); + WriteLine($"var other = obj as {record.Name};"); + WriteLine("if (other == null) return false;"); + if ((record.Flags & RecordDefFlags.ReentrantEquals) != 0) + { + WriteLine("if (_equalsReentrancyGuard.Value.Contains(other))"); + WriteLine(" return true;"); + WriteLine("_equalsReentrancyGuard.Value.Push(other);"); + WriteLine("try"); + WriteLine("{"); + } + foreach (var member in record.Members) + { + if ((member.Flags & MemberDefFlags.NotPersisted) != 0) + continue; + + if ((record.Flags & RecordDefFlags.CustomCompare) != 0 && (member.Flags & MemberDefFlags.Compare) == 0) + continue; + + if ((member.Flags & MemberDefFlags.Sequence) != 0) + { + if ((member.Flags & MemberDefFlags.CustomCompare) != 0) + WriteLine($"if (!{member.Name}.SequenceEqual(other.{member.Name}, {member.TypeName}Comparer.Instance)) return false;"); + else + WriteLine($"if (!{member.Name}.SequenceEqual(other.{member.Name})) return false;"); + } + else + if ((member.Flags & (MemberDefFlags.Map | MemberDefFlags.RecordRef)) != 0) + { + WriteLine($"if (!Object.Equals({member.Name}, other.{member.Name})) return false;"); + } + else + if ((member.Flags & MemberDefFlags.CustomCompare) != 0) + { + WriteLine($"if (!CustomComparer.Equals({member.Name}, other.{member.Name})) return false;"); + } + else + { + WriteLine($"if ({member.Name} != other.{member.Name}) return false;"); + } + } + if ((record.Flags & RecordDefFlags.ReentrantEquals) != 0) + { + WriteLine("}"); + + WriteLine("finally"); + WriteLine("{"); + WriteLine(" var popped = _equalsReentrancyGuard.Value.Pop();"); + WriteLine(" Debug.Assert(Object.ReferenceEquals(other, popped));"); + WriteLine("}"); + } + WriteLine("return true;"); + CloseScope("Equals"); + if ((record.Flags & RecordDefFlags.ReentrantEquals) != 0) + WriteLine("private ThreadLocal _equalsReentrancyGuard;"); + + OpenScope("public override sealed int GetHashCode()"); + WriteLine("if (_hash != 0)"); + WriteLine(" return _hash;"); + WriteLine("EnterGetHashCode();"); + + // Compute hash seed using stable hashcode + byte[] nameBytes = System.Text.Encoding.UTF8.GetBytes(record.Name); + byte[] hashBytes = System.Security.Cryptography.SHA256.Create().ComputeHash(nameBytes); + int hashSeed = System.BitConverter.ToInt32(hashBytes, 0); + WriteLine($"int hash = {hashSeed};"); + + foreach (var member in record.Members) + { + if ((member.Flags & MemberDefFlags.NotPersisted) != 0) + continue; + + if ((record.Flags & RecordDefFlags.CustomCompare) != 0 && (member.Flags & MemberDefFlags.Compare) == 0) + continue; + + if (member.TypeName as string == "ConstantStringValue") + { + WriteLine($"hash = ((hash << 13) - (hash >> 19)) ^ ({member.Name} == null ? 0 : {member.Name}.GetHashCode());"); + } + else + if ((member.Flags & MemberDefFlags.Array) != 0) + { + WriteLine($"if ({member.Name} != null)"); + WriteLine("{"); + WriteLine($" for (int i = 0; i < {member.Name}.Length; i++)"); + WriteLine(" {"); + WriteLine($" hash = ((hash << 13) - (hash >> 19)) ^ {member.Name}[i].GetHashCode();"); + WriteLine(" }"); + WriteLine("}"); + } + else + if ((member.Flags & (MemberDefFlags.List | MemberDefFlags.Map)) != 0) + { + if ((member.Flags & MemberDefFlags.EnumerateForHashCode) == 0) + continue; + + WriteLine($"if ({member.Name} != null)"); + WriteLine("{"); + WriteLine($"for (int i = 0; i < {member.Name}.Count; i++)"); + WriteLine(" {"); + WriteLine($" hash = ((hash << 13) - (hash >> 19)) ^ ({member.Name}[i] == null ? 0 : {member.Name}[i].GetHashCode());"); + WriteLine(" }"); + WriteLine("}"); + } + else + if ((member.Flags & MemberDefFlags.RecordRef) != 0 || isConstantStringValue) + { + WriteLine($"hash = ((hash << 13) - (hash >> 19)) ^ ({member.Name} == null ? 0 : {member.Name}.GetHashCode());"); + } + else + { + WriteLine($"hash = ((hash << 13) - (hash >> 19)) ^ {member.Name}.GetHashCode();"); + } + } + WriteLine("LeaveGetHashCode();"); + WriteLine("_hash = hash;"); + WriteLine("return _hash;"); + CloseScope("GetHashCode"); + + OpenScope("internal override void Save(NativeWriter writer)"); + if (isConstantStringValue) + { + WriteLine("if (Value == null)"); + WriteLine(" return;"); + WriteLine(); + } + foreach (var member in record.Members) + { + if ((member.Flags & MemberDefFlags.NotPersisted) != 0) + continue; + + var typeSet = member.TypeName as string[]; + if (typeSet != null) + { + if ((member.Flags & (MemberDefFlags.List | MemberDefFlags.Map)) != 0) + { + WriteLine($"Debug.Assert({member.Name}.TrueForAll(handle => handle == null ||"); + for (int i = 0; i < typeSet.Length; i++) + WriteLine($" handle.HandleType == HandleType.{typeSet[i]}" + + ((i == typeSet.Length - 1) ? "));" : " ||")); + } + else + { + WriteLine($"Debug.Assert({member.Name} == null ||"); + for (int i = 0; i < typeSet.Length; i++) + WriteLine($" {member.Name}.HandleType == HandleType.{typeSet[i]}" + + ((i == typeSet.Length - 1) ? ");" : " ||")); + } + } + WriteLine($"writer.Write({member.Name});"); + } + CloseScope("Save"); + + OpenScope($"internal static {record.Name}Handle AsHandle({record.Name} record)"); + WriteLine("if (record == null)"); + WriteLine("{"); + WriteLine($" return new {record.Name}Handle(0);"); + WriteLine("}"); + WriteLine("else"); + WriteLine("{"); + WriteLine(" return record.Handle;"); + WriteLine("}"); + CloseScope("AsHandle"); + + OpenScope($"internal new {record.Name}Handle Handle"); + OpenScope("get"); + if (isConstantStringValue) + { + WriteLine("if (Value == null)"); + WriteLine(" return new ConstantStringValueHandle(0);"); + WriteLine("else"); + WriteLine(" return new ConstantStringValueHandle(HandleOffset);"); + } + else + { + WriteLine($"return new {record.Name}Handle(HandleOffset);"); + } + CloseScope(); + CloseScope("Handle"); + + WriteLineIfNeeded(); + foreach (var member in record.Members) + { + if ((member.Flags & MemberDefFlags.NotPersisted) != 0) + continue; + + string fieldType = member.GetMemberType(MemberTypeKind.WriterField); + if ((member.Flags & (MemberDefFlags.List | MemberDefFlags.Map)) != 0) + { + WriteLine($"public {fieldType} {member.Name} = new {fieldType}();"); + } + else + { + WriteLine($"public {fieldType} {member.Name};"); + } + } + + CloseScope(record.Name); + } +} diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/MdBinaryReader.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/MdBinaryReader.cs new file mode 100644 index 00000000000000..660c2f5d20d363 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/MdBinaryReader.cs @@ -0,0 +1,104 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Collections.Generic; +using System.Reflection; +using Debug = System.Diagnostics.Debug; +using System.Threading; +using System.Text; +using Internal.NativeFormat; + +namespace Internal.Metadata.NativeFormat +{ + internal static partial class MdBinaryReader + { + public static uint Read(this NativeReader reader, uint offset, out bool value) + { + value = (reader.ReadUInt8(offset) != 0) ? true : false; + return offset + 1; + } + + public static uint Read(this NativeReader reader, uint offset, out string value) + { + return reader.DecodeString(offset, out value); + } + + public static uint Read(this NativeReader reader, uint offset, out char value) + { + uint val; + offset = reader.DecodeUnsigned(offset, out val); + value = (char)val; + return offset; + } + + public static uint Read(this NativeReader reader, uint offset, out short value) + { + int val; + offset = reader.DecodeSigned(offset, out val); + value = (short)val; + return offset; + } + + public static uint Read(this NativeReader reader, uint offset, out sbyte value) + { + value = (sbyte)reader.ReadUInt8(offset); + return offset + 1; + } + + public static uint Read(this NativeReader reader, uint offset, out ulong value) + { + return reader.DecodeUnsignedLong(offset, out value); + } + + public static uint Read(this NativeReader reader, uint offset, out int value) + { + return reader.DecodeSigned(offset, out value); + } + + public static uint Read(this NativeReader reader, uint offset, out uint value) + { + return reader.DecodeUnsigned(offset, out value); + } + + public static uint Read(this NativeReader reader, uint offset, out byte value) + { + value = reader.ReadUInt8(offset); + return offset + 1; + } + + public static uint Read(this NativeReader reader, uint offset, out ushort value) + { + uint val; + offset = reader.DecodeUnsigned(offset, out val); + value = (ushort)val; + return offset; + } + + public static uint Read(this NativeReader reader, uint offset, out long value) + { + return reader.DecodeSignedLong(offset, out value); + } + + public static uint Read(this NativeReader reader, uint offset, out Handle handle) + { + uint rawValue; + offset = reader.DecodeUnsigned(offset, out rawValue); + handle = new Handle((HandleType)(byte)rawValue, (int)(rawValue >> 8)); + return offset; + } + + public static uint Read(this NativeReader reader, uint offset, out float value) + { + value = reader.ReadFloat(offset); + return offset + 4; + } + + public static uint Read(this NativeReader reader, uint offset, out double value) + { + value = reader.ReadDouble(offset); + return offset + 8; + } + } +} diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/MdBinaryReaderGen.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/MdBinaryReaderGen.cs new file mode 100644 index 00000000000000..d0e425b36790b7 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/MdBinaryReaderGen.cs @@ -0,0 +1,982 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// NOTE: This is a generated file - do not manually edit! + +#pragma warning disable 649 + +using System; +using System.IO; +using System.Collections.Generic; +using System.Reflection; +using Internal.NativeFormat; +using Debug = System.Diagnostics.Debug; + +namespace Internal.Metadata.NativeFormat +{ + internal static partial class MdBinaryReader + { + public static unsafe uint Read(this NativeReader reader, uint offset, out BooleanCollection values) + { + values = new BooleanCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + offset = checked(offset + count * sizeof(bool)); + return offset; + } // Read + + public static unsafe uint Read(this NativeReader reader, uint offset, out CharCollection values) + { + values = new CharCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + offset = checked(offset + count * sizeof(char)); + return offset; + } // Read + + public static unsafe uint Read(this NativeReader reader, uint offset, out ByteCollection values) + { + values = new ByteCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + offset = checked(offset + count * sizeof(byte)); + return offset; + } // Read + + public static unsafe uint Read(this NativeReader reader, uint offset, out SByteCollection values) + { + values = new SByteCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + offset = checked(offset + count * sizeof(sbyte)); + return offset; + } // Read + + public static unsafe uint Read(this NativeReader reader, uint offset, out Int16Collection values) + { + values = new Int16Collection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + offset = checked(offset + count * sizeof(short)); + return offset; + } // Read + + public static unsafe uint Read(this NativeReader reader, uint offset, out UInt16Collection values) + { + values = new UInt16Collection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + offset = checked(offset + count * sizeof(ushort)); + return offset; + } // Read + + public static unsafe uint Read(this NativeReader reader, uint offset, out Int32Collection values) + { + values = new Int32Collection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + offset = checked(offset + count * sizeof(int)); + return offset; + } // Read + + public static unsafe uint Read(this NativeReader reader, uint offset, out UInt32Collection values) + { + values = new UInt32Collection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + offset = checked(offset + count * sizeof(uint)); + return offset; + } // Read + + public static unsafe uint Read(this NativeReader reader, uint offset, out Int64Collection values) + { + values = new Int64Collection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + offset = checked(offset + count * sizeof(long)); + return offset; + } // Read + + public static unsafe uint Read(this NativeReader reader, uint offset, out UInt64Collection values) + { + values = new UInt64Collection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + offset = checked(offset + count * sizeof(ulong)); + return offset; + } // Read + + public static unsafe uint Read(this NativeReader reader, uint offset, out SingleCollection values) + { + values = new SingleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + offset = checked(offset + count * sizeof(float)); + return offset; + } // Read + + public static unsafe uint Read(this NativeReader reader, uint offset, out DoubleCollection values) + { + values = new DoubleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + offset = checked(offset + count * sizeof(double)); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out AssemblyFlags value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (AssemblyFlags)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out AssemblyHashAlgorithm value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (AssemblyHashAlgorithm)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out CallingConventions value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (CallingConventions)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out EventAttributes value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (EventAttributes)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out FieldAttributes value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (FieldAttributes)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out GenericParameterAttributes value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (GenericParameterAttributes)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out GenericParameterKind value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (GenericParameterKind)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out MethodAttributes value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (MethodAttributes)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out MethodImplAttributes value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (MethodImplAttributes)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out MethodSemanticsAttributes value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (MethodSemanticsAttributes)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out NamedArgumentMemberKind value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (NamedArgumentMemberKind)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ParameterAttributes value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (ParameterAttributes)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out PInvokeAttributes value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (PInvokeAttributes)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out PropertyAttributes value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (PropertyAttributes)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out TypeAttributes value) + { + uint ivalue; + offset = reader.DecodeUnsigned(offset, out ivalue); + value = (TypeAttributes)ivalue; + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out HandleCollection values) + { + values = new HandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ArraySignatureHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ArraySignatureHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ByReferenceSignatureHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ByReferenceSignatureHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantBooleanArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantBooleanArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantBooleanValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantBooleanValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantBoxedEnumValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantBoxedEnumValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantByteArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantByteArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantByteValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantByteValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantCharArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantCharArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantCharValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantCharValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantDoubleArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantDoubleArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantDoubleValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantDoubleValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantEnumArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantEnumArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantHandleArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantHandleArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantInt16ArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantInt16ArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantInt16ValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantInt16ValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantInt32ArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantInt32ArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantInt32ValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantInt32ValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantInt64ArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantInt64ArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantInt64ValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantInt64ValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantReferenceValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantReferenceValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantSByteArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantSByteArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantSByteValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantSByteValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantSingleArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantSingleArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantSingleValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantSingleValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantStringArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantStringArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantStringValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantStringValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantUInt16ArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantUInt16ArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantUInt16ValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantUInt16ValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantUInt32ArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantUInt32ArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantUInt32ValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantUInt32ValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantUInt64ArrayHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantUInt64ArrayHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ConstantUInt64ValueHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ConstantUInt64ValueHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out CustomAttributeHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new CustomAttributeHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out EventHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new EventHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out FieldHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new FieldHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out FieldSignatureHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new FieldSignatureHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out FunctionPointerSignatureHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new FunctionPointerSignatureHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out GenericParameterHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new GenericParameterHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out MemberReferenceHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new MemberReferenceHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out MethodHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new MethodHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out MethodInstantiationHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new MethodInstantiationHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out MethodSemanticsHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new MethodSemanticsHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out MethodSignatureHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new MethodSignatureHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out MethodTypeVariableSignatureHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new MethodTypeVariableSignatureHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ModifiedTypeHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ModifiedTypeHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out NamedArgumentHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new NamedArgumentHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out NamespaceDefinitionHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new NamespaceDefinitionHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out NamespaceReferenceHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new NamespaceReferenceHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ParameterHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ParameterHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out PointerSignatureHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new PointerSignatureHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out PropertyHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new PropertyHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out PropertySignatureHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new PropertySignatureHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out QualifiedFieldHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new QualifiedFieldHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out QualifiedMethodHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new QualifiedMethodHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out SZArraySignatureHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new SZArraySignatureHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ScopeDefinitionHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ScopeDefinitionHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ScopeReferenceHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new ScopeReferenceHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out TypeDefinitionHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new TypeDefinitionHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out TypeForwarderHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new TypeForwarderHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out TypeInstantiationSignatureHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new TypeInstantiationSignatureHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out TypeReferenceHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new TypeReferenceHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out TypeSpecificationHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new TypeSpecificationHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out TypeVariableSignatureHandle handle) + { + uint value; + offset = reader.DecodeUnsigned(offset, out value); + handle = new TypeVariableSignatureHandle((int)value); + handle._Validate(); + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out NamedArgumentHandleCollection values) + { + values = new NamedArgumentHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out MethodSemanticsHandleCollection values) + { + values = new MethodSemanticsHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out CustomAttributeHandleCollection values) + { + values = new CustomAttributeHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ParameterHandleCollection values) + { + values = new ParameterHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out GenericParameterHandleCollection values) + { + values = new GenericParameterHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out TypeDefinitionHandleCollection values) + { + values = new TypeDefinitionHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out TypeForwarderHandleCollection values) + { + values = new TypeForwarderHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out NamespaceDefinitionHandleCollection values) + { + values = new NamespaceDefinitionHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out MethodHandleCollection values) + { + values = new MethodHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out FieldHandleCollection values) + { + values = new FieldHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out PropertyHandleCollection values) + { + values = new PropertyHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out EventHandleCollection values) + { + values = new EventHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + + public static uint Read(this NativeReader reader, uint offset, out ScopeDefinitionHandleCollection values) + { + values = new ScopeDefinitionHandleCollection(reader, offset); + uint count; + offset = reader.DecodeUnsigned(offset, out count); + for (uint i = 0; i < count; ++i) + { + offset = reader.SkipInteger(offset); + } + return offset; + } // Read + } // MdBinaryReader +} // Internal.Metadata.NativeFormat diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/MetadataTypeHashingAlgorithms.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/MetadataTypeHashingAlgorithms.cs new file mode 100644 index 00000000000000..af2cd760f72366 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/MetadataTypeHashingAlgorithms.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using TypeAttributes = System.Reflection.TypeAttributes; +using Debug = System.Diagnostics.Debug; +using HashCodeBuilder = Internal.NativeFormat.TypeHashingAlgorithms.HashCodeBuilder; +using TypeHashingAlgorithms = Internal.NativeFormat.TypeHashingAlgorithms; + +namespace Internal.Metadata.NativeFormat +{ + internal static class MetadataTypeHashingAlgorithms + { + private static void AppendNamespaceHashCode(ref HashCodeBuilder builder, NamespaceDefinitionHandle namespaceDefHandle, MetadataReader reader) + { + NamespaceDefinition namespaceDefinition = reader.GetNamespaceDefinition(namespaceDefHandle); + + Handle parentHandle = namespaceDefinition.ParentScopeOrNamespace; + HandleType parentHandleType = parentHandle.HandleType; + if (parentHandleType == HandleType.NamespaceDefinition) + { + AppendNamespaceHashCode(ref builder, parentHandle.ToNamespaceDefinitionHandle(reader), reader); + string namespaceNamePart = reader.GetString(namespaceDefinition.Name); + builder.Append(namespaceNamePart); + builder.Append("."); + } + else + { + Debug.Assert(parentHandleType == HandleType.ScopeDefinition); + Debug.Assert(string.IsNullOrEmpty(reader.GetString(namespaceDefinition.Name)), "Root namespace with a name?"); + } + } + + private static void AppendNamespaceHashCode(ref HashCodeBuilder builder, NamespaceReferenceHandle namespaceRefHandle, MetadataReader reader) + { + NamespaceReference namespaceReference = reader.GetNamespaceReference(namespaceRefHandle); + + Handle parentHandle = namespaceReference.ParentScopeOrNamespace; + HandleType parentHandleType = parentHandle.HandleType; + if (parentHandleType == HandleType.NamespaceReference) + { + AppendNamespaceHashCode(ref builder, parentHandle.ToNamespaceReferenceHandle(reader), reader); + string namespaceNamePart = reader.GetString(namespaceReference.Name); + builder.Append(namespaceNamePart); + builder.Append("."); + } + else + { + Debug.Assert(parentHandleType == HandleType.ScopeReference); + Debug.Assert(string.IsNullOrEmpty(reader.GetString(namespaceReference.Name)), "Root namespace with a name?"); + } + } + + public static int ComputeHashCode(this TypeDefinitionHandle typeDefHandle, MetadataReader reader) + { + HashCodeBuilder builder = new HashCodeBuilder(""); + + TypeDefinition typeDef = reader.GetTypeDefinition(typeDefHandle); + bool isNested = typeDef.Flags.IsNested(); + if (!isNested) + { + AppendNamespaceHashCode(ref builder, typeDef.NamespaceDefinition, reader); + } + + string typeName = reader.GetString(typeDef.Name); + builder.Append(typeName); + + if (isNested) + { + int enclosingTypeHashCode = typeDef.EnclosingType.ComputeHashCode(reader); + return TypeHashingAlgorithms.ComputeNestedTypeHashCode(enclosingTypeHashCode, builder.ToHashCode()); + } + + return builder.ToHashCode(); + } + + public static int ComputeHashCode(this TypeReferenceHandle typeRefHandle, MetadataReader reader) + { + HashCodeBuilder builder = new HashCodeBuilder(""); + + TypeReference typeRef = reader.GetTypeReference(typeRefHandle); + HandleType parentHandleType = typeRef.ParentNamespaceOrType.HandleType; + bool isNested = parentHandleType == HandleType.TypeReference; + if (!isNested) + { + Debug.Assert(parentHandleType == HandleType.NamespaceReference); + AppendNamespaceHashCode(ref builder, typeRef.ParentNamespaceOrType.ToNamespaceReferenceHandle(reader), reader); + } + + string typeName = reader.GetString(typeRef.TypeName); + builder.Append(typeName); + + if (isNested) + { + int enclosingTypeHashCode = typeRef.ParentNamespaceOrType.ToTypeReferenceHandle(reader).ComputeHashCode(reader); + return TypeHashingAlgorithms.ComputeNestedTypeHashCode(enclosingTypeHashCode, builder.ToHashCode()); + } + + return builder.ToHashCode(); + } + + // This mask is the fastest way to check if a type is nested from its flags, + // but it should not be added to the BCL enum as its semantics can be misleading. + // Consider, for example, that (NestedFamANDAssem & NestedMask) == NestedFamORAssem. + // Only comparison of the masked value to 0 is meaningful, which is different from + // the other masks in the enum. + private const TypeAttributes NestedMask = (TypeAttributes)0x00000006; + + private static bool IsNested(this TypeAttributes flags) + { + return (flags & NestedMask) != 0; + } + } +} diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeFormatReaderCommonGen.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeFormatReaderCommonGen.cs new file mode 100644 index 00000000000000..d7a28a874cc271 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeFormatReaderCommonGen.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// NOTE: This is a generated file - do not manually edit! + +using System; +using System.Reflection; +using System.Collections.Generic; + +#pragma warning disable 108 // base type 'uint' is not CLS-compliant +#pragma warning disable 3009 // base type 'uint' is not CLS-compliant +#pragma warning disable 282 // There is no defined ordering between fields in multiple declarations of partial class or struct + +namespace Internal.Metadata.NativeFormat +{ + [Flags] + public enum AssemblyFlags : uint + { + /// The assembly reference holds the full (unhashed) public key. + PublicKey = 0x1, + + /// The implementation of this assembly used at runtime is not expected to match the version seen at compile time. + Retargetable = 0x100, + + /// Reserved. + DisableJITcompileOptimizer = 0x4000, + + /// Reserved. + EnableJITcompileTracking = 0x8000, + } // AssemblyFlags + + public enum AssemblyHashAlgorithm : uint + { + None = 0x0, + Reserved = 0x8003, + SHA1 = 0x8004, + } // AssemblyHashAlgorithm + + public enum GenericParameterKind : byte + { + /// Represents a type parameter for a generic type. + GenericTypeParameter = 0x0, + + /// Represents a type parameter from a generic method. + GenericMethodParameter = 0x1, + } // GenericParameterKind + + public enum NamedArgumentMemberKind : byte + { + /// Specifies the name of a property + Property = 0x0, + + /// Specifies the name of a field + Field = 0x1, + } // NamedArgumentMemberKind + + public enum HandleType : byte + { + Null = 0x0, + ArraySignature = 0x1, + ByReferenceSignature = 0x2, + ConstantBooleanArray = 0x3, + ConstantBooleanValue = 0x4, + ConstantBoxedEnumValue = 0x5, + ConstantByteArray = 0x6, + ConstantByteValue = 0x7, + ConstantCharArray = 0x8, + ConstantCharValue = 0x9, + ConstantDoubleArray = 0xa, + ConstantDoubleValue = 0xb, + ConstantEnumArray = 0xc, + ConstantHandleArray = 0xd, + ConstantInt16Array = 0xe, + ConstantInt16Value = 0xf, + ConstantInt32Array = 0x10, + ConstantInt32Value = 0x11, + ConstantInt64Array = 0x12, + ConstantInt64Value = 0x13, + ConstantReferenceValue = 0x14, + ConstantSByteArray = 0x15, + ConstantSByteValue = 0x16, + ConstantSingleArray = 0x17, + ConstantSingleValue = 0x18, + ConstantStringArray = 0x19, + ConstantStringValue = 0x1a, + ConstantUInt16Array = 0x1b, + ConstantUInt16Value = 0x1c, + ConstantUInt32Array = 0x1d, + ConstantUInt32Value = 0x1e, + ConstantUInt64Array = 0x1f, + ConstantUInt64Value = 0x20, + CustomAttribute = 0x21, + Event = 0x22, + Field = 0x23, + FieldSignature = 0x24, + FunctionPointerSignature = 0x25, + GenericParameter = 0x26, + MemberReference = 0x27, + Method = 0x28, + MethodInstantiation = 0x29, + MethodSemantics = 0x2a, + MethodSignature = 0x2b, + MethodTypeVariableSignature = 0x2c, + ModifiedType = 0x2d, + NamedArgument = 0x2e, + NamespaceDefinition = 0x2f, + NamespaceReference = 0x30, + Parameter = 0x31, + PointerSignature = 0x32, + Property = 0x33, + PropertySignature = 0x34, + QualifiedField = 0x35, + QualifiedMethod = 0x36, + SZArraySignature = 0x37, + ScopeDefinition = 0x38, + ScopeReference = 0x39, + TypeDefinition = 0x3a, + TypeForwarder = 0x3b, + TypeInstantiationSignature = 0x3c, + TypeReference = 0x3d, + TypeSpecification = 0x3e, + TypeVariableSignature = 0x3f, + } // HandleType +} // Internal.Metadata.NativeFormat diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeFormatReaderGen.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeFormatReaderGen.cs new file mode 100644 index 00000000000000..523fc0d172d521 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeFormatReaderGen.cs @@ -0,0 +1,11396 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// NOTE: This is a generated file - do not manually edit! + +#pragma warning disable 649 +#pragma warning disable 169 +#pragma warning disable 282 // There is no defined ordering between fields in multiple declarations of partial class or struct + +using System; +using System.Reflection; +using System.Collections.Generic; +using Internal.NativeFormat; + +namespace Internal.Metadata.NativeFormat +{ + public partial struct ArraySignature + { + internal MetadataReader _reader; + internal ArraySignatureHandle _handle; + + public ArraySignatureHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: TypeDefinition, TypeReference, TypeSpecification + + public Handle ElementType + { + get + { + return _elementType; + } + } // ElementType + + internal Handle _elementType; + + public int Rank + { + get + { + return _rank; + } + } // Rank + + internal int _rank; + + public Int32Collection Sizes + { + get + { + return _sizes; + } + } // Sizes + + internal Int32Collection _sizes; + + public Int32Collection LowerBounds + { + get + { + return _lowerBounds; + } + } // LowerBounds + + internal Int32Collection _lowerBounds; + } // ArraySignature + + public partial struct ArraySignatureHandle + { + public override bool Equals(object obj) + { + if (obj is ArraySignatureHandle) + return _value == ((ArraySignatureHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ArraySignatureHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ArraySignatureHandle(Handle handle) : this(handle._value) + { + } + + internal ArraySignatureHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ArraySignature || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ArraySignature) << 24); + _Validate(); + } + + public static implicit operator Handle(ArraySignatureHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ArraySignature GetArraySignature(MetadataReader reader) + { + return reader.GetArraySignature(this); + } // GetArraySignature + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ArraySignature) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ArraySignatureHandle + + public partial struct ByReferenceSignature + { + internal MetadataReader _reader; + internal ByReferenceSignatureHandle _handle; + + public ByReferenceSignatureHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: TypeDefinition, TypeReference, TypeSpecification + + public Handle Type + { + get + { + return _type; + } + } // Type + + internal Handle _type; + } // ByReferenceSignature + + public partial struct ByReferenceSignatureHandle + { + public override bool Equals(object obj) + { + if (obj is ByReferenceSignatureHandle) + return _value == ((ByReferenceSignatureHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ByReferenceSignatureHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ByReferenceSignatureHandle(Handle handle) : this(handle._value) + { + } + + internal ByReferenceSignatureHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ByReferenceSignature || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ByReferenceSignature) << 24); + _Validate(); + } + + public static implicit operator Handle(ByReferenceSignatureHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ByReferenceSignature GetByReferenceSignature(MetadataReader reader) + { + return reader.GetByReferenceSignature(this); + } // GetByReferenceSignature + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ByReferenceSignature) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ByReferenceSignatureHandle + + public partial struct ConstantBooleanArray + { + internal MetadataReader _reader; + internal ConstantBooleanArrayHandle _handle; + + public ConstantBooleanArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public BooleanCollection Value + { + get + { + return _value; + } + } // Value + + internal BooleanCollection _value; + } // ConstantBooleanArray + + public partial struct ConstantBooleanArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantBooleanArrayHandle) + return _value == ((ConstantBooleanArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantBooleanArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantBooleanArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantBooleanArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantBooleanArray || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantBooleanArray) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantBooleanArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantBooleanArray GetConstantBooleanArray(MetadataReader reader) + { + return reader.GetConstantBooleanArray(this); + } // GetConstantBooleanArray + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantBooleanArray) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantBooleanArrayHandle + + public partial struct ConstantBooleanValue + { + internal MetadataReader _reader; + internal ConstantBooleanValueHandle _handle; + + public ConstantBooleanValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public bool Value + { + get + { + return _value; + } + } // Value + + internal bool _value; + } // ConstantBooleanValue + + public partial struct ConstantBooleanValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantBooleanValueHandle) + return _value == ((ConstantBooleanValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantBooleanValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantBooleanValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantBooleanValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantBooleanValue || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantBooleanValue) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantBooleanValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantBooleanValue GetConstantBooleanValue(MetadataReader reader) + { + return reader.GetConstantBooleanValue(this); + } // GetConstantBooleanValue + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantBooleanValue) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantBooleanValueHandle + + public partial struct ConstantBoxedEnumValue + { + internal MetadataReader _reader; + internal ConstantBoxedEnumValueHandle _handle; + + public ConstantBoxedEnumValueHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: ConstantByteValue, ConstantSByteValue, ConstantInt16Value, ConstantUInt16Value, ConstantInt32Value, ConstantUInt32Value, ConstantInt64Value, ConstantUInt64Value + + public Handle Value + { + get + { + return _value; + } + } // Value + + internal Handle _value; + /// One of: TypeDefinition, TypeReference, TypeSpecification + + public Handle Type + { + get + { + return _type; + } + } // Type + + internal Handle _type; + } // ConstantBoxedEnumValue + + public partial struct ConstantBoxedEnumValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantBoxedEnumValueHandle) + return _value == ((ConstantBoxedEnumValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantBoxedEnumValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantBoxedEnumValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantBoxedEnumValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantBoxedEnumValue || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantBoxedEnumValue) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantBoxedEnumValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantBoxedEnumValue GetConstantBoxedEnumValue(MetadataReader reader) + { + return reader.GetConstantBoxedEnumValue(this); + } // GetConstantBoxedEnumValue + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantBoxedEnumValue) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantBoxedEnumValueHandle + + public partial struct ConstantByteArray + { + internal MetadataReader _reader; + internal ConstantByteArrayHandle _handle; + + public ConstantByteArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public ByteCollection Value + { + get + { + return _value; + } + } // Value + + internal ByteCollection _value; + } // ConstantByteArray + + public partial struct ConstantByteArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantByteArrayHandle) + return _value == ((ConstantByteArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantByteArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantByteArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantByteArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantByteArray || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantByteArray) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantByteArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantByteArray GetConstantByteArray(MetadataReader reader) + { + return reader.GetConstantByteArray(this); + } // GetConstantByteArray + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantByteArray) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantByteArrayHandle + + public partial struct ConstantByteValue + { + internal MetadataReader _reader; + internal ConstantByteValueHandle _handle; + + public ConstantByteValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public byte Value + { + get + { + return _value; + } + } // Value + + internal byte _value; + } // ConstantByteValue + + public partial struct ConstantByteValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantByteValueHandle) + return _value == ((ConstantByteValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantByteValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantByteValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantByteValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantByteValue || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantByteValue) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantByteValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantByteValue GetConstantByteValue(MetadataReader reader) + { + return reader.GetConstantByteValue(this); + } // GetConstantByteValue + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantByteValue) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantByteValueHandle + + public partial struct ConstantCharArray + { + internal MetadataReader _reader; + internal ConstantCharArrayHandle _handle; + + public ConstantCharArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public CharCollection Value + { + get + { + return _value; + } + } // Value + + internal CharCollection _value; + } // ConstantCharArray + + public partial struct ConstantCharArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantCharArrayHandle) + return _value == ((ConstantCharArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantCharArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantCharArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantCharArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantCharArray || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantCharArray) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantCharArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantCharArray GetConstantCharArray(MetadataReader reader) + { + return reader.GetConstantCharArray(this); + } // GetConstantCharArray + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantCharArray) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantCharArrayHandle + + public partial struct ConstantCharValue + { + internal MetadataReader _reader; + internal ConstantCharValueHandle _handle; + + public ConstantCharValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public char Value + { + get + { + return _value; + } + } // Value + + internal char _value; + } // ConstantCharValue + + public partial struct ConstantCharValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantCharValueHandle) + return _value == ((ConstantCharValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantCharValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantCharValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantCharValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantCharValue || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantCharValue) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantCharValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantCharValue GetConstantCharValue(MetadataReader reader) + { + return reader.GetConstantCharValue(this); + } // GetConstantCharValue + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantCharValue) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantCharValueHandle + + public partial struct ConstantDoubleArray + { + internal MetadataReader _reader; + internal ConstantDoubleArrayHandle _handle; + + public ConstantDoubleArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public DoubleCollection Value + { + get + { + return _value; + } + } // Value + + internal DoubleCollection _value; + } // ConstantDoubleArray + + public partial struct ConstantDoubleArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantDoubleArrayHandle) + return _value == ((ConstantDoubleArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantDoubleArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantDoubleArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantDoubleArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantDoubleArray || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantDoubleArray) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantDoubleArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantDoubleArray GetConstantDoubleArray(MetadataReader reader) + { + return reader.GetConstantDoubleArray(this); + } // GetConstantDoubleArray + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantDoubleArray) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantDoubleArrayHandle + + public partial struct ConstantDoubleValue + { + internal MetadataReader _reader; + internal ConstantDoubleValueHandle _handle; + + public ConstantDoubleValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public double Value + { + get + { + return _value; + } + } // Value + + internal double _value; + } // ConstantDoubleValue + + public partial struct ConstantDoubleValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantDoubleValueHandle) + return _value == ((ConstantDoubleValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantDoubleValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantDoubleValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantDoubleValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantDoubleValue || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantDoubleValue) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantDoubleValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantDoubleValue GetConstantDoubleValue(MetadataReader reader) + { + return reader.GetConstantDoubleValue(this); + } // GetConstantDoubleValue + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantDoubleValue) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantDoubleValueHandle + + public partial struct ConstantEnumArray + { + internal MetadataReader _reader; + internal ConstantEnumArrayHandle _handle; + + public ConstantEnumArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public Handle ElementType + { + get + { + return _elementType; + } + } // ElementType + + internal Handle _elementType; + + public Handle Value + { + get + { + return _value; + } + } // Value + + internal Handle _value; + } // ConstantEnumArray + + public partial struct ConstantEnumArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantEnumArrayHandle) + return _value == ((ConstantEnumArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantEnumArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantEnumArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantEnumArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantEnumArray || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantEnumArray) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantEnumArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantEnumArray GetConstantEnumArray(MetadataReader reader) + { + return reader.GetConstantEnumArray(this); + } // GetConstantEnumArray + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantEnumArray) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantEnumArrayHandle + + public partial struct ConstantHandleArray + { + internal MetadataReader _reader; + internal ConstantHandleArrayHandle _handle; + + public ConstantHandleArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public HandleCollection Value + { + get + { + return _value; + } + } // Value + + internal HandleCollection _value; + } // ConstantHandleArray + + public partial struct ConstantHandleArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantHandleArrayHandle) + return _value == ((ConstantHandleArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantHandleArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantHandleArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantHandleArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantHandleArray || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantHandleArray) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantHandleArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantHandleArray GetConstantHandleArray(MetadataReader reader) + { + return reader.GetConstantHandleArray(this); + } // GetConstantHandleArray + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantHandleArray) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantHandleArrayHandle + + public partial struct ConstantInt16Array + { + internal MetadataReader _reader; + internal ConstantInt16ArrayHandle _handle; + + public ConstantInt16ArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public Int16Collection Value + { + get + { + return _value; + } + } // Value + + internal Int16Collection _value; + } // ConstantInt16Array + + public partial struct ConstantInt16ArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantInt16ArrayHandle) + return _value == ((ConstantInt16ArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantInt16ArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantInt16ArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantInt16ArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantInt16Array || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantInt16Array) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantInt16ArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantInt16Array GetConstantInt16Array(MetadataReader reader) + { + return reader.GetConstantInt16Array(this); + } // GetConstantInt16Array + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantInt16Array) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantInt16ArrayHandle + + public partial struct ConstantInt16Value + { + internal MetadataReader _reader; + internal ConstantInt16ValueHandle _handle; + + public ConstantInt16ValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public short Value + { + get + { + return _value; + } + } // Value + + internal short _value; + } // ConstantInt16Value + + public partial struct ConstantInt16ValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantInt16ValueHandle) + return _value == ((ConstantInt16ValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantInt16ValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantInt16ValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantInt16ValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantInt16Value || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantInt16Value) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantInt16ValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantInt16Value GetConstantInt16Value(MetadataReader reader) + { + return reader.GetConstantInt16Value(this); + } // GetConstantInt16Value + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantInt16Value) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantInt16ValueHandle + + public partial struct ConstantInt32Array + { + internal MetadataReader _reader; + internal ConstantInt32ArrayHandle _handle; + + public ConstantInt32ArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public Int32Collection Value + { + get + { + return _value; + } + } // Value + + internal Int32Collection _value; + } // ConstantInt32Array + + public partial struct ConstantInt32ArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantInt32ArrayHandle) + return _value == ((ConstantInt32ArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantInt32ArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantInt32ArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantInt32ArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantInt32Array || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantInt32Array) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantInt32ArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantInt32Array GetConstantInt32Array(MetadataReader reader) + { + return reader.GetConstantInt32Array(this); + } // GetConstantInt32Array + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantInt32Array) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantInt32ArrayHandle + + public partial struct ConstantInt32Value + { + internal MetadataReader _reader; + internal ConstantInt32ValueHandle _handle; + + public ConstantInt32ValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public int Value + { + get + { + return _value; + } + } // Value + + internal int _value; + } // ConstantInt32Value + + public partial struct ConstantInt32ValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantInt32ValueHandle) + return _value == ((ConstantInt32ValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantInt32ValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantInt32ValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantInt32ValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantInt32Value || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantInt32Value) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantInt32ValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantInt32Value GetConstantInt32Value(MetadataReader reader) + { + return reader.GetConstantInt32Value(this); + } // GetConstantInt32Value + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantInt32Value) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantInt32ValueHandle + + public partial struct ConstantInt64Array + { + internal MetadataReader _reader; + internal ConstantInt64ArrayHandle _handle; + + public ConstantInt64ArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public Int64Collection Value + { + get + { + return _value; + } + } // Value + + internal Int64Collection _value; + } // ConstantInt64Array + + public partial struct ConstantInt64ArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantInt64ArrayHandle) + return _value == ((ConstantInt64ArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantInt64ArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantInt64ArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantInt64ArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantInt64Array || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantInt64Array) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantInt64ArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantInt64Array GetConstantInt64Array(MetadataReader reader) + { + return reader.GetConstantInt64Array(this); + } // GetConstantInt64Array + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantInt64Array) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantInt64ArrayHandle + + public partial struct ConstantInt64Value + { + internal MetadataReader _reader; + internal ConstantInt64ValueHandle _handle; + + public ConstantInt64ValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public long Value + { + get + { + return _value; + } + } // Value + + internal long _value; + } // ConstantInt64Value + + public partial struct ConstantInt64ValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantInt64ValueHandle) + return _value == ((ConstantInt64ValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantInt64ValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantInt64ValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantInt64ValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantInt64Value || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantInt64Value) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantInt64ValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantInt64Value GetConstantInt64Value(MetadataReader reader) + { + return reader.GetConstantInt64Value(this); + } // GetConstantInt64Value + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantInt64Value) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantInt64ValueHandle + + public partial struct ConstantReferenceValue + { + internal MetadataReader _reader; + internal ConstantReferenceValueHandle _handle; + + public ConstantReferenceValueHandle Handle + { + get + { + return _handle; + } + } // Handle + } // ConstantReferenceValue + + public partial struct ConstantReferenceValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantReferenceValueHandle) + return _value == ((ConstantReferenceValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantReferenceValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantReferenceValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantReferenceValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantReferenceValue || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantReferenceValue) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantReferenceValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantReferenceValue GetConstantReferenceValue(MetadataReader reader) + { + return reader.GetConstantReferenceValue(this); + } // GetConstantReferenceValue + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantReferenceValue) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantReferenceValueHandle + + public partial struct ConstantSByteArray + { + internal MetadataReader _reader; + internal ConstantSByteArrayHandle _handle; + + public ConstantSByteArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public SByteCollection Value + { + get + { + return _value; + } + } // Value + + internal SByteCollection _value; + } // ConstantSByteArray + + public partial struct ConstantSByteArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantSByteArrayHandle) + return _value == ((ConstantSByteArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantSByteArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantSByteArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantSByteArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantSByteArray || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantSByteArray) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantSByteArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantSByteArray GetConstantSByteArray(MetadataReader reader) + { + return reader.GetConstantSByteArray(this); + } // GetConstantSByteArray + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantSByteArray) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantSByteArrayHandle + + public partial struct ConstantSByteValue + { + internal MetadataReader _reader; + internal ConstantSByteValueHandle _handle; + + public ConstantSByteValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public sbyte Value + { + get + { + return _value; + } + } // Value + + internal sbyte _value; + } // ConstantSByteValue + + public partial struct ConstantSByteValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantSByteValueHandle) + return _value == ((ConstantSByteValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantSByteValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantSByteValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantSByteValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantSByteValue || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantSByteValue) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantSByteValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantSByteValue GetConstantSByteValue(MetadataReader reader) + { + return reader.GetConstantSByteValue(this); + } // GetConstantSByteValue + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantSByteValue) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantSByteValueHandle + + public partial struct ConstantSingleArray + { + internal MetadataReader _reader; + internal ConstantSingleArrayHandle _handle; + + public ConstantSingleArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public SingleCollection Value + { + get + { + return _value; + } + } // Value + + internal SingleCollection _value; + } // ConstantSingleArray + + public partial struct ConstantSingleArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantSingleArrayHandle) + return _value == ((ConstantSingleArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantSingleArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantSingleArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantSingleArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantSingleArray || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantSingleArray) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantSingleArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantSingleArray GetConstantSingleArray(MetadataReader reader) + { + return reader.GetConstantSingleArray(this); + } // GetConstantSingleArray + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantSingleArray) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantSingleArrayHandle + + public partial struct ConstantSingleValue + { + internal MetadataReader _reader; + internal ConstantSingleValueHandle _handle; + + public ConstantSingleValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public float Value + { + get + { + return _value; + } + } // Value + + internal float _value; + } // ConstantSingleValue + + public partial struct ConstantSingleValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantSingleValueHandle) + return _value == ((ConstantSingleValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantSingleValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantSingleValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantSingleValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantSingleValue || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantSingleValue) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantSingleValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantSingleValue GetConstantSingleValue(MetadataReader reader) + { + return reader.GetConstantSingleValue(this); + } // GetConstantSingleValue + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantSingleValue) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantSingleValueHandle + + public partial struct ConstantStringArray + { + internal MetadataReader _reader; + internal ConstantStringArrayHandle _handle; + + public ConstantStringArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: ConstantStringValue, ConstantReferenceValue + + public HandleCollection Value + { + get + { + return _value; + } + } // Value + + internal HandleCollection _value; + } // ConstantStringArray + + public partial struct ConstantStringArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantStringArrayHandle) + return _value == ((ConstantStringArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantStringArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantStringArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantStringArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantStringArray || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantStringArray) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantStringArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantStringArray GetConstantStringArray(MetadataReader reader) + { + return reader.GetConstantStringArray(this); + } // GetConstantStringArray + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantStringArray) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantStringArrayHandle + + public partial struct ConstantStringValue + { + internal MetadataReader _reader; + internal ConstantStringValueHandle _handle; + + public ConstantStringValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public string Value + { + get + { + return _value; + } + } // Value + + internal string _value; + } // ConstantStringValue + + public partial struct ConstantStringValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantStringValueHandle) + return _value == ((ConstantStringValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantStringValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantStringValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantStringValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantStringValue || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantStringValue) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantStringValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantStringValue GetConstantStringValue(MetadataReader reader) + { + return reader.GetConstantStringValue(this); + } // GetConstantStringValue + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantStringValue) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantStringValueHandle + + public partial struct ConstantUInt16Array + { + internal MetadataReader _reader; + internal ConstantUInt16ArrayHandle _handle; + + public ConstantUInt16ArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public UInt16Collection Value + { + get + { + return _value; + } + } // Value + + internal UInt16Collection _value; + } // ConstantUInt16Array + + public partial struct ConstantUInt16ArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantUInt16ArrayHandle) + return _value == ((ConstantUInt16ArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantUInt16ArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantUInt16ArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantUInt16ArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantUInt16Array || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantUInt16Array) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantUInt16ArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantUInt16Array GetConstantUInt16Array(MetadataReader reader) + { + return reader.GetConstantUInt16Array(this); + } // GetConstantUInt16Array + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantUInt16Array) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantUInt16ArrayHandle + + public partial struct ConstantUInt16Value + { + internal MetadataReader _reader; + internal ConstantUInt16ValueHandle _handle; + + public ConstantUInt16ValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public ushort Value + { + get + { + return _value; + } + } // Value + + internal ushort _value; + } // ConstantUInt16Value + + public partial struct ConstantUInt16ValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantUInt16ValueHandle) + return _value == ((ConstantUInt16ValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantUInt16ValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantUInt16ValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantUInt16ValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantUInt16Value || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantUInt16Value) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantUInt16ValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantUInt16Value GetConstantUInt16Value(MetadataReader reader) + { + return reader.GetConstantUInt16Value(this); + } // GetConstantUInt16Value + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantUInt16Value) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantUInt16ValueHandle + + public partial struct ConstantUInt32Array + { + internal MetadataReader _reader; + internal ConstantUInt32ArrayHandle _handle; + + public ConstantUInt32ArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public UInt32Collection Value + { + get + { + return _value; + } + } // Value + + internal UInt32Collection _value; + } // ConstantUInt32Array + + public partial struct ConstantUInt32ArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantUInt32ArrayHandle) + return _value == ((ConstantUInt32ArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantUInt32ArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantUInt32ArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantUInt32ArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantUInt32Array || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantUInt32Array) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantUInt32ArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantUInt32Array GetConstantUInt32Array(MetadataReader reader) + { + return reader.GetConstantUInt32Array(this); + } // GetConstantUInt32Array + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantUInt32Array) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantUInt32ArrayHandle + + public partial struct ConstantUInt32Value + { + internal MetadataReader _reader; + internal ConstantUInt32ValueHandle _handle; + + public ConstantUInt32ValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public uint Value + { + get + { + return _value; + } + } // Value + + internal uint _value; + } // ConstantUInt32Value + + public partial struct ConstantUInt32ValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantUInt32ValueHandle) + return _value == ((ConstantUInt32ValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantUInt32ValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantUInt32ValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantUInt32ValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantUInt32Value || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantUInt32Value) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantUInt32ValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantUInt32Value GetConstantUInt32Value(MetadataReader reader) + { + return reader.GetConstantUInt32Value(this); + } // GetConstantUInt32Value + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantUInt32Value) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantUInt32ValueHandle + + public partial struct ConstantUInt64Array + { + internal MetadataReader _reader; + internal ConstantUInt64ArrayHandle _handle; + + public ConstantUInt64ArrayHandle Handle + { + get + { + return _handle; + } + } // Handle + + public UInt64Collection Value + { + get + { + return _value; + } + } // Value + + internal UInt64Collection _value; + } // ConstantUInt64Array + + public partial struct ConstantUInt64ArrayHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantUInt64ArrayHandle) + return _value == ((ConstantUInt64ArrayHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantUInt64ArrayHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantUInt64ArrayHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantUInt64ArrayHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantUInt64Array || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantUInt64Array) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantUInt64ArrayHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantUInt64Array GetConstantUInt64Array(MetadataReader reader) + { + return reader.GetConstantUInt64Array(this); + } // GetConstantUInt64Array + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantUInt64Array) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantUInt64ArrayHandle + + public partial struct ConstantUInt64Value + { + internal MetadataReader _reader; + internal ConstantUInt64ValueHandle _handle; + + public ConstantUInt64ValueHandle Handle + { + get + { + return _handle; + } + } // Handle + + public ulong Value + { + get + { + return _value; + } + } // Value + + internal ulong _value; + } // ConstantUInt64Value + + public partial struct ConstantUInt64ValueHandle + { + public override bool Equals(object obj) + { + if (obj is ConstantUInt64ValueHandle) + return _value == ((ConstantUInt64ValueHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ConstantUInt64ValueHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ConstantUInt64ValueHandle(Handle handle) : this(handle._value) + { + } + + internal ConstantUInt64ValueHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ConstantUInt64Value || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ConstantUInt64Value) << 24); + _Validate(); + } + + public static implicit operator Handle(ConstantUInt64ValueHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ConstantUInt64Value GetConstantUInt64Value(MetadataReader reader) + { + return reader.GetConstantUInt64Value(this); + } // GetConstantUInt64Value + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ConstantUInt64Value) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ConstantUInt64ValueHandle + + public partial struct CustomAttribute + { + internal MetadataReader _reader; + internal CustomAttributeHandle _handle; + + public CustomAttributeHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: QualifiedMethod, MemberReference + + public Handle Constructor + { + get + { + return _constructor; + } + } // Constructor + + internal Handle _constructor; + /// One of: TypeDefinition, TypeReference, TypeSpecification, ConstantBooleanArray, ConstantBooleanValue, ConstantByteArray, ConstantByteValue, ConstantCharArray, ConstantCharValue, ConstantDoubleArray, ConstantDoubleValue, ConstantEnumArray, ConstantHandleArray, ConstantInt16Array, ConstantInt16Value, ConstantInt32Array, ConstantInt32Value, ConstantInt64Array, ConstantInt64Value, ConstantReferenceValue, ConstantSByteArray, ConstantSByteValue, ConstantSingleArray, ConstantSingleValue, ConstantStringArray, ConstantStringValue, ConstantUInt16Array, ConstantUInt16Value, ConstantUInt32Array, ConstantUInt32Value, ConstantUInt64Array, ConstantUInt64Value + + public HandleCollection FixedArguments + { + get + { + return _fixedArguments; + } + } // FixedArguments + + internal HandleCollection _fixedArguments; + + public NamedArgumentHandleCollection NamedArguments + { + get + { + return _namedArguments; + } + } // NamedArguments + + internal NamedArgumentHandleCollection _namedArguments; + } // CustomAttribute + + public partial struct CustomAttributeHandle + { + public override bool Equals(object obj) + { + if (obj is CustomAttributeHandle) + return _value == ((CustomAttributeHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(CustomAttributeHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal CustomAttributeHandle(Handle handle) : this(handle._value) + { + } + + internal CustomAttributeHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.CustomAttribute || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.CustomAttribute) << 24); + _Validate(); + } + + public static implicit operator Handle(CustomAttributeHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public CustomAttribute GetCustomAttribute(MetadataReader reader) + { + return reader.GetCustomAttribute(this); + } // GetCustomAttribute + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.CustomAttribute) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // CustomAttributeHandle + + public partial struct Event + { + internal MetadataReader _reader; + internal EventHandle _handle; + + public EventHandle Handle + { + get + { + return _handle; + } + } // Handle + + public EventAttributes Flags + { + get + { + return _flags; + } + } // Flags + + internal EventAttributes _flags; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + /// One of: TypeDefinition, TypeReference, TypeSpecification + + public Handle Type + { + get + { + return _type; + } + } // Type + + internal Handle _type; + + public MethodSemanticsHandleCollection MethodSemantics + { + get + { + return _methodSemantics; + } + } // MethodSemantics + + internal MethodSemanticsHandleCollection _methodSemantics; + + public CustomAttributeHandleCollection CustomAttributes + { + get + { + return _customAttributes; + } + } // CustomAttributes + + internal CustomAttributeHandleCollection _customAttributes; + } // Event + + public partial struct EventHandle + { + public override bool Equals(object obj) + { + if (obj is EventHandle) + return _value == ((EventHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(EventHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal EventHandle(Handle handle) : this(handle._value) + { + } + + internal EventHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.Event || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.Event) << 24); + _Validate(); + } + + public static implicit operator Handle(EventHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public Event GetEvent(MetadataReader reader) + { + return reader.GetEvent(this); + } // GetEvent + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.Event) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // EventHandle + + public partial struct Field + { + internal MetadataReader _reader; + internal FieldHandle _handle; + + public FieldHandle Handle + { + get + { + return _handle; + } + } // Handle + + public FieldAttributes Flags + { + get + { + return _flags; + } + } // Flags + + internal FieldAttributes _flags; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + + public FieldSignatureHandle Signature + { + get + { + return _signature; + } + } // Signature + + internal FieldSignatureHandle _signature; + /// One of: TypeDefinition, TypeReference, TypeSpecification, ConstantBooleanArray, ConstantBooleanValue, ConstantByteArray, ConstantByteValue, ConstantCharArray, ConstantCharValue, ConstantDoubleArray, ConstantDoubleValue, ConstantEnumArray, ConstantHandleArray, ConstantInt16Array, ConstantInt16Value, ConstantInt32Array, ConstantInt32Value, ConstantInt64Array, ConstantInt64Value, ConstantReferenceValue, ConstantSByteArray, ConstantSByteValue, ConstantSingleArray, ConstantSingleValue, ConstantStringArray, ConstantStringValue, ConstantUInt16Array, ConstantUInt16Value, ConstantUInt32Array, ConstantUInt32Value, ConstantUInt64Array, ConstantUInt64Value + + public Handle DefaultValue + { + get + { + return _defaultValue; + } + } // DefaultValue + + internal Handle _defaultValue; + + public uint Offset + { + get + { + return _offset; + } + } // Offset + + internal uint _offset; + + public CustomAttributeHandleCollection CustomAttributes + { + get + { + return _customAttributes; + } + } // CustomAttributes + + internal CustomAttributeHandleCollection _customAttributes; + } // Field + + public partial struct FieldHandle + { + public override bool Equals(object obj) + { + if (obj is FieldHandle) + return _value == ((FieldHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(FieldHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal FieldHandle(Handle handle) : this(handle._value) + { + } + + internal FieldHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.Field || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.Field) << 24); + _Validate(); + } + + public static implicit operator Handle(FieldHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public Field GetField(MetadataReader reader) + { + return reader.GetField(this); + } // GetField + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.Field) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // FieldHandle + + public partial struct FieldSignature + { + internal MetadataReader _reader; + internal FieldSignatureHandle _handle; + + public FieldSignatureHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: TypeDefinition, TypeReference, TypeSpecification, ModifiedType + + public Handle Type + { + get + { + return _type; + } + } // Type + + internal Handle _type; + } // FieldSignature + + public partial struct FieldSignatureHandle + { + public override bool Equals(object obj) + { + if (obj is FieldSignatureHandle) + return _value == ((FieldSignatureHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(FieldSignatureHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal FieldSignatureHandle(Handle handle) : this(handle._value) + { + } + + internal FieldSignatureHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.FieldSignature || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.FieldSignature) << 24); + _Validate(); + } + + public static implicit operator Handle(FieldSignatureHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public FieldSignature GetFieldSignature(MetadataReader reader) + { + return reader.GetFieldSignature(this); + } // GetFieldSignature + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.FieldSignature) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // FieldSignatureHandle + + public partial struct FunctionPointerSignature + { + internal MetadataReader _reader; + internal FunctionPointerSignatureHandle _handle; + + public FunctionPointerSignatureHandle Handle + { + get + { + return _handle; + } + } // Handle + + public MethodSignatureHandle Signature + { + get + { + return _signature; + } + } // Signature + + internal MethodSignatureHandle _signature; + } // FunctionPointerSignature + + public partial struct FunctionPointerSignatureHandle + { + public override bool Equals(object obj) + { + if (obj is FunctionPointerSignatureHandle) + return _value == ((FunctionPointerSignatureHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(FunctionPointerSignatureHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal FunctionPointerSignatureHandle(Handle handle) : this(handle._value) + { + } + + internal FunctionPointerSignatureHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.FunctionPointerSignature || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.FunctionPointerSignature) << 24); + _Validate(); + } + + public static implicit operator Handle(FunctionPointerSignatureHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public FunctionPointerSignature GetFunctionPointerSignature(MetadataReader reader) + { + return reader.GetFunctionPointerSignature(this); + } // GetFunctionPointerSignature + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.FunctionPointerSignature) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // FunctionPointerSignatureHandle + + public partial struct GenericParameter + { + internal MetadataReader _reader; + internal GenericParameterHandle _handle; + + public GenericParameterHandle Handle + { + get + { + return _handle; + } + } // Handle + + public ushort Number + { + get + { + return _number; + } + } // Number + + internal ushort _number; + + public GenericParameterAttributes Flags + { + get + { + return _flags; + } + } // Flags + + internal GenericParameterAttributes _flags; + + public GenericParameterKind Kind + { + get + { + return _kind; + } + } // Kind + + internal GenericParameterKind _kind; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + /// One of: TypeDefinition, TypeReference, TypeSpecification, ModifiedType + + public HandleCollection Constraints + { + get + { + return _constraints; + } + } // Constraints + + internal HandleCollection _constraints; + + public CustomAttributeHandleCollection CustomAttributes + { + get + { + return _customAttributes; + } + } // CustomAttributes + + internal CustomAttributeHandleCollection _customAttributes; + } // GenericParameter + + public partial struct GenericParameterHandle + { + public override bool Equals(object obj) + { + if (obj is GenericParameterHandle) + return _value == ((GenericParameterHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(GenericParameterHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal GenericParameterHandle(Handle handle) : this(handle._value) + { + } + + internal GenericParameterHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.GenericParameter || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.GenericParameter) << 24); + _Validate(); + } + + public static implicit operator Handle(GenericParameterHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public GenericParameter GetGenericParameter(MetadataReader reader) + { + return reader.GetGenericParameter(this); + } // GetGenericParameter + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.GenericParameter) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // GenericParameterHandle + + public partial struct MemberReference + { + internal MetadataReader _reader; + internal MemberReferenceHandle _handle; + + public MemberReferenceHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: TypeDefinition, TypeReference, TypeSpecification + + public Handle Parent + { + get + { + return _parent; + } + } // Parent + + internal Handle _parent; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + /// One of: MethodSignature, FieldSignature + + public Handle Signature + { + get + { + return _signature; + } + } // Signature + + internal Handle _signature; + } // MemberReference + + public partial struct MemberReferenceHandle + { + public override bool Equals(object obj) + { + if (obj is MemberReferenceHandle) + return _value == ((MemberReferenceHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(MemberReferenceHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal MemberReferenceHandle(Handle handle) : this(handle._value) + { + } + + internal MemberReferenceHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.MemberReference || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.MemberReference) << 24); + _Validate(); + } + + public static implicit operator Handle(MemberReferenceHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public MemberReference GetMemberReference(MetadataReader reader) + { + return reader.GetMemberReference(this); + } // GetMemberReference + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.MemberReference) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // MemberReferenceHandle + + public partial struct Method + { + internal MetadataReader _reader; + internal MethodHandle _handle; + + public MethodHandle Handle + { + get + { + return _handle; + } + } // Handle + + public MethodAttributes Flags + { + get + { + return _flags; + } + } // Flags + + internal MethodAttributes _flags; + + public MethodImplAttributes ImplFlags + { + get + { + return _implFlags; + } + } // ImplFlags + + internal MethodImplAttributes _implFlags; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + + public MethodSignatureHandle Signature + { + get + { + return _signature; + } + } // Signature + + internal MethodSignatureHandle _signature; + + public ParameterHandleCollection Parameters + { + get + { + return _parameters; + } + } // Parameters + + internal ParameterHandleCollection _parameters; + + public GenericParameterHandleCollection GenericParameters + { + get + { + return _genericParameters; + } + } // GenericParameters + + internal GenericParameterHandleCollection _genericParameters; + + public CustomAttributeHandleCollection CustomAttributes + { + get + { + return _customAttributes; + } + } // CustomAttributes + + internal CustomAttributeHandleCollection _customAttributes; + } // Method + + public partial struct MethodHandle + { + public override bool Equals(object obj) + { + if (obj is MethodHandle) + return _value == ((MethodHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(MethodHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal MethodHandle(Handle handle) : this(handle._value) + { + } + + internal MethodHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.Method || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.Method) << 24); + _Validate(); + } + + public static implicit operator Handle(MethodHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public Method GetMethod(MetadataReader reader) + { + return reader.GetMethod(this); + } // GetMethod + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.Method) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // MethodHandle + + public partial struct MethodInstantiation + { + internal MetadataReader _reader; + internal MethodInstantiationHandle _handle; + + public MethodInstantiationHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: QualifiedMethod, MemberReference + + public Handle Method + { + get + { + return _method; + } + } // Method + + internal Handle _method; + /// One of: TypeDefinition, TypeReference, TypeSpecification + + public HandleCollection GenericTypeArguments + { + get + { + return _genericTypeArguments; + } + } // GenericTypeArguments + + internal HandleCollection _genericTypeArguments; + } // MethodInstantiation + + public partial struct MethodInstantiationHandle + { + public override bool Equals(object obj) + { + if (obj is MethodInstantiationHandle) + return _value == ((MethodInstantiationHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(MethodInstantiationHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal MethodInstantiationHandle(Handle handle) : this(handle._value) + { + } + + internal MethodInstantiationHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.MethodInstantiation || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.MethodInstantiation) << 24); + _Validate(); + } + + public static implicit operator Handle(MethodInstantiationHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public MethodInstantiation GetMethodInstantiation(MetadataReader reader) + { + return reader.GetMethodInstantiation(this); + } // GetMethodInstantiation + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.MethodInstantiation) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // MethodInstantiationHandle + + public partial struct MethodSemantics + { + internal MetadataReader _reader; + internal MethodSemanticsHandle _handle; + + public MethodSemanticsHandle Handle + { + get + { + return _handle; + } + } // Handle + + public MethodSemanticsAttributes Attributes + { + get + { + return _attributes; + } + } // Attributes + + internal MethodSemanticsAttributes _attributes; + + public MethodHandle Method + { + get + { + return _method; + } + } // Method + + internal MethodHandle _method; + } // MethodSemantics + + public partial struct MethodSemanticsHandle + { + public override bool Equals(object obj) + { + if (obj is MethodSemanticsHandle) + return _value == ((MethodSemanticsHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(MethodSemanticsHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal MethodSemanticsHandle(Handle handle) : this(handle._value) + { + } + + internal MethodSemanticsHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.MethodSemantics || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.MethodSemantics) << 24); + _Validate(); + } + + public static implicit operator Handle(MethodSemanticsHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public MethodSemantics GetMethodSemantics(MetadataReader reader) + { + return reader.GetMethodSemantics(this); + } // GetMethodSemantics + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.MethodSemantics) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // MethodSemanticsHandle + + public partial struct MethodSignature + { + internal MetadataReader _reader; + internal MethodSignatureHandle _handle; + + public MethodSignatureHandle Handle + { + get + { + return _handle; + } + } // Handle + + public CallingConventions CallingConvention + { + get + { + return _callingConvention; + } + } // CallingConvention + + internal CallingConventions _callingConvention; + + public int GenericParameterCount + { + get + { + return _genericParameterCount; + } + } // GenericParameterCount + + internal int _genericParameterCount; + /// One of: TypeDefinition, TypeReference, TypeSpecification, ModifiedType + + public Handle ReturnType + { + get + { + return _returnType; + } + } // ReturnType + + internal Handle _returnType; + /// One of: TypeDefinition, TypeReference, TypeSpecification, ModifiedType + + public HandleCollection Parameters + { + get + { + return _parameters; + } + } // Parameters + + internal HandleCollection _parameters; + /// One of: TypeDefinition, TypeReference, TypeSpecification, ModifiedType + + public HandleCollection VarArgParameters + { + get + { + return _varArgParameters; + } + } // VarArgParameters + + internal HandleCollection _varArgParameters; + } // MethodSignature + + public partial struct MethodSignatureHandle + { + public override bool Equals(object obj) + { + if (obj is MethodSignatureHandle) + return _value == ((MethodSignatureHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(MethodSignatureHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal MethodSignatureHandle(Handle handle) : this(handle._value) + { + } + + internal MethodSignatureHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.MethodSignature || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.MethodSignature) << 24); + _Validate(); + } + + public static implicit operator Handle(MethodSignatureHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public MethodSignature GetMethodSignature(MetadataReader reader) + { + return reader.GetMethodSignature(this); + } // GetMethodSignature + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.MethodSignature) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // MethodSignatureHandle + + public partial struct MethodTypeVariableSignature + { + internal MetadataReader _reader; + internal MethodTypeVariableSignatureHandle _handle; + + public MethodTypeVariableSignatureHandle Handle + { + get + { + return _handle; + } + } // Handle + + public int Number + { + get + { + return _number; + } + } // Number + + internal int _number; + } // MethodTypeVariableSignature + + public partial struct MethodTypeVariableSignatureHandle + { + public override bool Equals(object obj) + { + if (obj is MethodTypeVariableSignatureHandle) + return _value == ((MethodTypeVariableSignatureHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(MethodTypeVariableSignatureHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal MethodTypeVariableSignatureHandle(Handle handle) : this(handle._value) + { + } + + internal MethodTypeVariableSignatureHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.MethodTypeVariableSignature || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.MethodTypeVariableSignature) << 24); + _Validate(); + } + + public static implicit operator Handle(MethodTypeVariableSignatureHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public MethodTypeVariableSignature GetMethodTypeVariableSignature(MetadataReader reader) + { + return reader.GetMethodTypeVariableSignature(this); + } // GetMethodTypeVariableSignature + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.MethodTypeVariableSignature) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // MethodTypeVariableSignatureHandle + + public partial struct ModifiedType + { + internal MetadataReader _reader; + internal ModifiedTypeHandle _handle; + + public ModifiedTypeHandle Handle + { + get + { + return _handle; + } + } // Handle + + public bool IsOptional + { + get + { + return _isOptional; + } + } // IsOptional + + internal bool _isOptional; + /// One of: TypeDefinition, TypeReference, TypeSpecification + + public Handle ModifierType + { + get + { + return _modifierType; + } + } // ModifierType + + internal Handle _modifierType; + /// One of: TypeDefinition, TypeReference, TypeSpecification, ModifiedType + + public Handle Type + { + get + { + return _type; + } + } // Type + + internal Handle _type; + } // ModifiedType + + public partial struct ModifiedTypeHandle + { + public override bool Equals(object obj) + { + if (obj is ModifiedTypeHandle) + return _value == ((ModifiedTypeHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ModifiedTypeHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ModifiedTypeHandle(Handle handle) : this(handle._value) + { + } + + internal ModifiedTypeHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ModifiedType || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ModifiedType) << 24); + _Validate(); + } + + public static implicit operator Handle(ModifiedTypeHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ModifiedType GetModifiedType(MetadataReader reader) + { + return reader.GetModifiedType(this); + } // GetModifiedType + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ModifiedType) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ModifiedTypeHandle + + public partial struct NamedArgument + { + internal MetadataReader _reader; + internal NamedArgumentHandle _handle; + + public NamedArgumentHandle Handle + { + get + { + return _handle; + } + } // Handle + + public NamedArgumentMemberKind Flags + { + get + { + return _flags; + } + } // Flags + + internal NamedArgumentMemberKind _flags; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + /// One of: TypeDefinition, TypeReference, TypeSpecification + + public Handle Type + { + get + { + return _type; + } + } // Type + + internal Handle _type; + /// One of: TypeDefinition, TypeReference, TypeSpecification, ConstantBooleanArray, ConstantBooleanValue, ConstantByteArray, ConstantByteValue, ConstantCharArray, ConstantCharValue, ConstantDoubleArray, ConstantDoubleValue, ConstantEnumArray, ConstantHandleArray, ConstantInt16Array, ConstantInt16Value, ConstantInt32Array, ConstantInt32Value, ConstantInt64Array, ConstantInt64Value, ConstantReferenceValue, ConstantSByteArray, ConstantSByteValue, ConstantSingleArray, ConstantSingleValue, ConstantStringArray, ConstantStringValue, ConstantUInt16Array, ConstantUInt16Value, ConstantUInt32Array, ConstantUInt32Value, ConstantUInt64Array, ConstantUInt64Value + + public Handle Value + { + get + { + return _value; + } + } // Value + + internal Handle _value; + } // NamedArgument + + public partial struct NamedArgumentHandle + { + public override bool Equals(object obj) + { + if (obj is NamedArgumentHandle) + return _value == ((NamedArgumentHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(NamedArgumentHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal NamedArgumentHandle(Handle handle) : this(handle._value) + { + } + + internal NamedArgumentHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.NamedArgument || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.NamedArgument) << 24); + _Validate(); + } + + public static implicit operator Handle(NamedArgumentHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public NamedArgument GetNamedArgument(MetadataReader reader) + { + return reader.GetNamedArgument(this); + } // GetNamedArgument + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.NamedArgument) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // NamedArgumentHandle + + public partial struct NamespaceDefinition + { + internal MetadataReader _reader; + internal NamespaceDefinitionHandle _handle; + + public NamespaceDefinitionHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: NamespaceDefinition, ScopeDefinition + + public Handle ParentScopeOrNamespace + { + get + { + return _parentScopeOrNamespace; + } + } // ParentScopeOrNamespace + + internal Handle _parentScopeOrNamespace; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + + public TypeDefinitionHandleCollection TypeDefinitions + { + get + { + return _typeDefinitions; + } + } // TypeDefinitions + + internal TypeDefinitionHandleCollection _typeDefinitions; + + public TypeForwarderHandleCollection TypeForwarders + { + get + { + return _typeForwarders; + } + } // TypeForwarders + + internal TypeForwarderHandleCollection _typeForwarders; + + public NamespaceDefinitionHandleCollection NamespaceDefinitions + { + get + { + return _namespaceDefinitions; + } + } // NamespaceDefinitions + + internal NamespaceDefinitionHandleCollection _namespaceDefinitions; + } // NamespaceDefinition + + public partial struct NamespaceDefinitionHandle + { + public override bool Equals(object obj) + { + if (obj is NamespaceDefinitionHandle) + return _value == ((NamespaceDefinitionHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(NamespaceDefinitionHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal NamespaceDefinitionHandle(Handle handle) : this(handle._value) + { + } + + internal NamespaceDefinitionHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.NamespaceDefinition || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.NamespaceDefinition) << 24); + _Validate(); + } + + public static implicit operator Handle(NamespaceDefinitionHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public NamespaceDefinition GetNamespaceDefinition(MetadataReader reader) + { + return reader.GetNamespaceDefinition(this); + } // GetNamespaceDefinition + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.NamespaceDefinition) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // NamespaceDefinitionHandle + + public partial struct NamespaceReference + { + internal MetadataReader _reader; + internal NamespaceReferenceHandle _handle; + + public NamespaceReferenceHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: NamespaceReference, ScopeReference + + public Handle ParentScopeOrNamespace + { + get + { + return _parentScopeOrNamespace; + } + } // ParentScopeOrNamespace + + internal Handle _parentScopeOrNamespace; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + } // NamespaceReference + + public partial struct NamespaceReferenceHandle + { + public override bool Equals(object obj) + { + if (obj is NamespaceReferenceHandle) + return _value == ((NamespaceReferenceHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(NamespaceReferenceHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal NamespaceReferenceHandle(Handle handle) : this(handle._value) + { + } + + internal NamespaceReferenceHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.NamespaceReference || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.NamespaceReference) << 24); + _Validate(); + } + + public static implicit operator Handle(NamespaceReferenceHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public NamespaceReference GetNamespaceReference(MetadataReader reader) + { + return reader.GetNamespaceReference(this); + } // GetNamespaceReference + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.NamespaceReference) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // NamespaceReferenceHandle + + public partial struct Parameter + { + internal MetadataReader _reader; + internal ParameterHandle _handle; + + public ParameterHandle Handle + { + get + { + return _handle; + } + } // Handle + + public ParameterAttributes Flags + { + get + { + return _flags; + } + } // Flags + + internal ParameterAttributes _flags; + + public ushort Sequence + { + get + { + return _sequence; + } + } // Sequence + + internal ushort _sequence; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + /// One of: TypeDefinition, TypeReference, TypeSpecification, ConstantBooleanArray, ConstantBooleanValue, ConstantByteArray, ConstantByteValue, ConstantCharArray, ConstantCharValue, ConstantDoubleArray, ConstantDoubleValue, ConstantEnumArray, ConstantHandleArray, ConstantInt16Array, ConstantInt16Value, ConstantInt32Array, ConstantInt32Value, ConstantInt64Array, ConstantInt64Value, ConstantReferenceValue, ConstantSByteArray, ConstantSByteValue, ConstantSingleArray, ConstantSingleValue, ConstantStringArray, ConstantStringValue, ConstantUInt16Array, ConstantUInt16Value, ConstantUInt32Array, ConstantUInt32Value, ConstantUInt64Array, ConstantUInt64Value + + public Handle DefaultValue + { + get + { + return _defaultValue; + } + } // DefaultValue + + internal Handle _defaultValue; + + public CustomAttributeHandleCollection CustomAttributes + { + get + { + return _customAttributes; + } + } // CustomAttributes + + internal CustomAttributeHandleCollection _customAttributes; + } // Parameter + + public partial struct ParameterHandle + { + public override bool Equals(object obj) + { + if (obj is ParameterHandle) + return _value == ((ParameterHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ParameterHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ParameterHandle(Handle handle) : this(handle._value) + { + } + + internal ParameterHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.Parameter || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.Parameter) << 24); + _Validate(); + } + + public static implicit operator Handle(ParameterHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public Parameter GetParameter(MetadataReader reader) + { + return reader.GetParameter(this); + } // GetParameter + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.Parameter) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ParameterHandle + + public partial struct PointerSignature + { + internal MetadataReader _reader; + internal PointerSignatureHandle _handle; + + public PointerSignatureHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: TypeDefinition, TypeReference, TypeSpecification, ModifiedType + + public Handle Type + { + get + { + return _type; + } + } // Type + + internal Handle _type; + } // PointerSignature + + public partial struct PointerSignatureHandle + { + public override bool Equals(object obj) + { + if (obj is PointerSignatureHandle) + return _value == ((PointerSignatureHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(PointerSignatureHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal PointerSignatureHandle(Handle handle) : this(handle._value) + { + } + + internal PointerSignatureHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.PointerSignature || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.PointerSignature) << 24); + _Validate(); + } + + public static implicit operator Handle(PointerSignatureHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public PointerSignature GetPointerSignature(MetadataReader reader) + { + return reader.GetPointerSignature(this); + } // GetPointerSignature + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.PointerSignature) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // PointerSignatureHandle + + public partial struct Property + { + internal MetadataReader _reader; + internal PropertyHandle _handle; + + public PropertyHandle Handle + { + get + { + return _handle; + } + } // Handle + + public PropertyAttributes Flags + { + get + { + return _flags; + } + } // Flags + + internal PropertyAttributes _flags; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + + public PropertySignatureHandle Signature + { + get + { + return _signature; + } + } // Signature + + internal PropertySignatureHandle _signature; + + public MethodSemanticsHandleCollection MethodSemantics + { + get + { + return _methodSemantics; + } + } // MethodSemantics + + internal MethodSemanticsHandleCollection _methodSemantics; + /// One of: TypeDefinition, TypeReference, TypeSpecification, ConstantBooleanArray, ConstantBooleanValue, ConstantByteArray, ConstantByteValue, ConstantCharArray, ConstantCharValue, ConstantDoubleArray, ConstantDoubleValue, ConstantEnumArray, ConstantHandleArray, ConstantInt16Array, ConstantInt16Value, ConstantInt32Array, ConstantInt32Value, ConstantInt64Array, ConstantInt64Value, ConstantReferenceValue, ConstantSByteArray, ConstantSByteValue, ConstantSingleArray, ConstantSingleValue, ConstantStringArray, ConstantStringValue, ConstantUInt16Array, ConstantUInt16Value, ConstantUInt32Array, ConstantUInt32Value, ConstantUInt64Array, ConstantUInt64Value + + public Handle DefaultValue + { + get + { + return _defaultValue; + } + } // DefaultValue + + internal Handle _defaultValue; + + public CustomAttributeHandleCollection CustomAttributes + { + get + { + return _customAttributes; + } + } // CustomAttributes + + internal CustomAttributeHandleCollection _customAttributes; + } // Property + + public partial struct PropertyHandle + { + public override bool Equals(object obj) + { + if (obj is PropertyHandle) + return _value == ((PropertyHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(PropertyHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal PropertyHandle(Handle handle) : this(handle._value) + { + } + + internal PropertyHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.Property || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.Property) << 24); + _Validate(); + } + + public static implicit operator Handle(PropertyHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public Property GetProperty(MetadataReader reader) + { + return reader.GetProperty(this); + } // GetProperty + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.Property) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // PropertyHandle + + public partial struct PropertySignature + { + internal MetadataReader _reader; + internal PropertySignatureHandle _handle; + + public PropertySignatureHandle Handle + { + get + { + return _handle; + } + } // Handle + + public CallingConventions CallingConvention + { + get + { + return _callingConvention; + } + } // CallingConvention + + internal CallingConventions _callingConvention; + /// One of: TypeDefinition, TypeReference, TypeSpecification, ModifiedType + + public Handle Type + { + get + { + return _type; + } + } // Type + + internal Handle _type; + /// One of: TypeDefinition, TypeReference, TypeSpecification, ModifiedType + + public HandleCollection Parameters + { + get + { + return _parameters; + } + } // Parameters + + internal HandleCollection _parameters; + } // PropertySignature + + public partial struct PropertySignatureHandle + { + public override bool Equals(object obj) + { + if (obj is PropertySignatureHandle) + return _value == ((PropertySignatureHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(PropertySignatureHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal PropertySignatureHandle(Handle handle) : this(handle._value) + { + } + + internal PropertySignatureHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.PropertySignature || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.PropertySignature) << 24); + _Validate(); + } + + public static implicit operator Handle(PropertySignatureHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public PropertySignature GetPropertySignature(MetadataReader reader) + { + return reader.GetPropertySignature(this); + } // GetPropertySignature + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.PropertySignature) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // PropertySignatureHandle + + public partial struct QualifiedField + { + internal MetadataReader _reader; + internal QualifiedFieldHandle _handle; + + public QualifiedFieldHandle Handle + { + get + { + return _handle; + } + } // Handle + + public FieldHandle Field + { + get + { + return _field; + } + } // Field + + internal FieldHandle _field; + + public TypeDefinitionHandle EnclosingType + { + get + { + return _enclosingType; + } + } // EnclosingType + + internal TypeDefinitionHandle _enclosingType; + } // QualifiedField + + public partial struct QualifiedFieldHandle + { + public override bool Equals(object obj) + { + if (obj is QualifiedFieldHandle) + return _value == ((QualifiedFieldHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(QualifiedFieldHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal QualifiedFieldHandle(Handle handle) : this(handle._value) + { + } + + internal QualifiedFieldHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.QualifiedField || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.QualifiedField) << 24); + _Validate(); + } + + public static implicit operator Handle(QualifiedFieldHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public QualifiedField GetQualifiedField(MetadataReader reader) + { + return reader.GetQualifiedField(this); + } // GetQualifiedField + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.QualifiedField) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // QualifiedFieldHandle + + public partial struct QualifiedMethod + { + internal MetadataReader _reader; + internal QualifiedMethodHandle _handle; + + public QualifiedMethodHandle Handle + { + get + { + return _handle; + } + } // Handle + + public MethodHandle Method + { + get + { + return _method; + } + } // Method + + internal MethodHandle _method; + + public TypeDefinitionHandle EnclosingType + { + get + { + return _enclosingType; + } + } // EnclosingType + + internal TypeDefinitionHandle _enclosingType; + } // QualifiedMethod + + public partial struct QualifiedMethodHandle + { + public override bool Equals(object obj) + { + if (obj is QualifiedMethodHandle) + return _value == ((QualifiedMethodHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(QualifiedMethodHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal QualifiedMethodHandle(Handle handle) : this(handle._value) + { + } + + internal QualifiedMethodHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.QualifiedMethod || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.QualifiedMethod) << 24); + _Validate(); + } + + public static implicit operator Handle(QualifiedMethodHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public QualifiedMethod GetQualifiedMethod(MetadataReader reader) + { + return reader.GetQualifiedMethod(this); + } // GetQualifiedMethod + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.QualifiedMethod) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // QualifiedMethodHandle + + public partial struct SZArraySignature + { + internal MetadataReader _reader; + internal SZArraySignatureHandle _handle; + + public SZArraySignatureHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: TypeDefinition, TypeReference, TypeSpecification, ModifiedType + + public Handle ElementType + { + get + { + return _elementType; + } + } // ElementType + + internal Handle _elementType; + } // SZArraySignature + + public partial struct SZArraySignatureHandle + { + public override bool Equals(object obj) + { + if (obj is SZArraySignatureHandle) + return _value == ((SZArraySignatureHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(SZArraySignatureHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal SZArraySignatureHandle(Handle handle) : this(handle._value) + { + } + + internal SZArraySignatureHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.SZArraySignature || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.SZArraySignature) << 24); + _Validate(); + } + + public static implicit operator Handle(SZArraySignatureHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public SZArraySignature GetSZArraySignature(MetadataReader reader) + { + return reader.GetSZArraySignature(this); + } // GetSZArraySignature + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.SZArraySignature) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // SZArraySignatureHandle + + public partial struct ScopeDefinition + { + internal MetadataReader _reader; + internal ScopeDefinitionHandle _handle; + + public ScopeDefinitionHandle Handle + { + get + { + return _handle; + } + } // Handle + + public AssemblyFlags Flags + { + get + { + return _flags; + } + } // Flags + + internal AssemblyFlags _flags; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + + public AssemblyHashAlgorithm HashAlgorithm + { + get + { + return _hashAlgorithm; + } + } // HashAlgorithm + + internal AssemblyHashAlgorithm _hashAlgorithm; + + public ushort MajorVersion + { + get + { + return _majorVersion; + } + } // MajorVersion + + internal ushort _majorVersion; + + public ushort MinorVersion + { + get + { + return _minorVersion; + } + } // MinorVersion + + internal ushort _minorVersion; + + public ushort BuildNumber + { + get + { + return _buildNumber; + } + } // BuildNumber + + internal ushort _buildNumber; + + public ushort RevisionNumber + { + get + { + return _revisionNumber; + } + } // RevisionNumber + + internal ushort _revisionNumber; + + public ByteCollection PublicKey + { + get + { + return _publicKey; + } + } // PublicKey + + internal ByteCollection _publicKey; + + public ConstantStringValueHandle Culture + { + get + { + return _culture; + } + } // Culture + + internal ConstantStringValueHandle _culture; + + public NamespaceDefinitionHandle RootNamespaceDefinition + { + get + { + return _rootNamespaceDefinition; + } + } // RootNamespaceDefinition + + internal NamespaceDefinitionHandle _rootNamespaceDefinition; + + public QualifiedMethodHandle EntryPoint + { + get + { + return _entryPoint; + } + } // EntryPoint + + internal QualifiedMethodHandle _entryPoint; + + public TypeDefinitionHandle GlobalModuleType + { + get + { + return _globalModuleType; + } + } // GlobalModuleType + + internal TypeDefinitionHandle _globalModuleType; + + public CustomAttributeHandleCollection CustomAttributes + { + get + { + return _customAttributes; + } + } // CustomAttributes + + internal CustomAttributeHandleCollection _customAttributes; + + public ConstantStringValueHandle ModuleName + { + get + { + return _moduleName; + } + } // ModuleName + + internal ConstantStringValueHandle _moduleName; + + public ByteCollection Mvid + { + get + { + return _mvid; + } + } // Mvid + + internal ByteCollection _mvid; + + public CustomAttributeHandleCollection ModuleCustomAttributes + { + get + { + return _moduleCustomAttributes; + } + } // ModuleCustomAttributes + + internal CustomAttributeHandleCollection _moduleCustomAttributes; + } // ScopeDefinition + + public partial struct ScopeDefinitionHandle + { + public override bool Equals(object obj) + { + if (obj is ScopeDefinitionHandle) + return _value == ((ScopeDefinitionHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ScopeDefinitionHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ScopeDefinitionHandle(Handle handle) : this(handle._value) + { + } + + internal ScopeDefinitionHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ScopeDefinition || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ScopeDefinition) << 24); + _Validate(); + } + + public static implicit operator Handle(ScopeDefinitionHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ScopeDefinition GetScopeDefinition(MetadataReader reader) + { + return reader.GetScopeDefinition(this); + } // GetScopeDefinition + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ScopeDefinition) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ScopeDefinitionHandle + + public partial struct ScopeReference + { + internal MetadataReader _reader; + internal ScopeReferenceHandle _handle; + + public ScopeReferenceHandle Handle + { + get + { + return _handle; + } + } // Handle + + public AssemblyFlags Flags + { + get + { + return _flags; + } + } // Flags + + internal AssemblyFlags _flags; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + + public ushort MajorVersion + { + get + { + return _majorVersion; + } + } // MajorVersion + + internal ushort _majorVersion; + + public ushort MinorVersion + { + get + { + return _minorVersion; + } + } // MinorVersion + + internal ushort _minorVersion; + + public ushort BuildNumber + { + get + { + return _buildNumber; + } + } // BuildNumber + + internal ushort _buildNumber; + + public ushort RevisionNumber + { + get + { + return _revisionNumber; + } + } // RevisionNumber + + internal ushort _revisionNumber; + + public ByteCollection PublicKeyOrToken + { + get + { + return _publicKeyOrToken; + } + } // PublicKeyOrToken + + internal ByteCollection _publicKeyOrToken; + + public ConstantStringValueHandle Culture + { + get + { + return _culture; + } + } // Culture + + internal ConstantStringValueHandle _culture; + } // ScopeReference + + public partial struct ScopeReferenceHandle + { + public override bool Equals(object obj) + { + if (obj is ScopeReferenceHandle) + return _value == ((ScopeReferenceHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(ScopeReferenceHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal ScopeReferenceHandle(Handle handle) : this(handle._value) + { + } + + internal ScopeReferenceHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.ScopeReference || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.ScopeReference) << 24); + _Validate(); + } + + public static implicit operator Handle(ScopeReferenceHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public ScopeReference GetScopeReference(MetadataReader reader) + { + return reader.GetScopeReference(this); + } // GetScopeReference + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.ScopeReference) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // ScopeReferenceHandle + + public partial struct TypeDefinition + { + internal MetadataReader _reader; + internal TypeDefinitionHandle _handle; + + public TypeDefinitionHandle Handle + { + get + { + return _handle; + } + } // Handle + + public TypeAttributes Flags + { + get + { + return _flags; + } + } // Flags + + internal TypeAttributes _flags; + /// One of: TypeDefinition, TypeReference, TypeSpecification + + public Handle BaseType + { + get + { + return _baseType; + } + } // BaseType + + internal Handle _baseType; + + public NamespaceDefinitionHandle NamespaceDefinition + { + get + { + return _namespaceDefinition; + } + } // NamespaceDefinition + + internal NamespaceDefinitionHandle _namespaceDefinition; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + + public uint Size + { + get + { + return _size; + } + } // Size + + internal uint _size; + + public ushort PackingSize + { + get + { + return _packingSize; + } + } // PackingSize + + internal ushort _packingSize; + + public TypeDefinitionHandle EnclosingType + { + get + { + return _enclosingType; + } + } // EnclosingType + + internal TypeDefinitionHandle _enclosingType; + + public TypeDefinitionHandleCollection NestedTypes + { + get + { + return _nestedTypes; + } + } // NestedTypes + + internal TypeDefinitionHandleCollection _nestedTypes; + + public MethodHandleCollection Methods + { + get + { + return _methods; + } + } // Methods + + internal MethodHandleCollection _methods; + + public FieldHandleCollection Fields + { + get + { + return _fields; + } + } // Fields + + internal FieldHandleCollection _fields; + + public PropertyHandleCollection Properties + { + get + { + return _properties; + } + } // Properties + + internal PropertyHandleCollection _properties; + + public EventHandleCollection Events + { + get + { + return _events; + } + } // Events + + internal EventHandleCollection _events; + + public GenericParameterHandleCollection GenericParameters + { + get + { + return _genericParameters; + } + } // GenericParameters + + internal GenericParameterHandleCollection _genericParameters; + /// One of: TypeDefinition, TypeReference, TypeSpecification + + public HandleCollection Interfaces + { + get + { + return _interfaces; + } + } // Interfaces + + internal HandleCollection _interfaces; + + public CustomAttributeHandleCollection CustomAttributes + { + get + { + return _customAttributes; + } + } // CustomAttributes + + internal CustomAttributeHandleCollection _customAttributes; + } // TypeDefinition + + public partial struct TypeDefinitionHandle + { + public override bool Equals(object obj) + { + if (obj is TypeDefinitionHandle) + return _value == ((TypeDefinitionHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(TypeDefinitionHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal TypeDefinitionHandle(Handle handle) : this(handle._value) + { + } + + internal TypeDefinitionHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.TypeDefinition || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.TypeDefinition) << 24); + _Validate(); + } + + public static implicit operator Handle(TypeDefinitionHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public TypeDefinition GetTypeDefinition(MetadataReader reader) + { + return reader.GetTypeDefinition(this); + } // GetTypeDefinition + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.TypeDefinition) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // TypeDefinitionHandle + + public partial struct TypeForwarder + { + internal MetadataReader _reader; + internal TypeForwarderHandle _handle; + + public TypeForwarderHandle Handle + { + get + { + return _handle; + } + } // Handle + + public ScopeReferenceHandle Scope + { + get + { + return _scope; + } + } // Scope + + internal ScopeReferenceHandle _scope; + + public ConstantStringValueHandle Name + { + get + { + return _name; + } + } // Name + + internal ConstantStringValueHandle _name; + + public TypeForwarderHandleCollection NestedTypes + { + get + { + return _nestedTypes; + } + } // NestedTypes + + internal TypeForwarderHandleCollection _nestedTypes; + } // TypeForwarder + + public partial struct TypeForwarderHandle + { + public override bool Equals(object obj) + { + if (obj is TypeForwarderHandle) + return _value == ((TypeForwarderHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(TypeForwarderHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal TypeForwarderHandle(Handle handle) : this(handle._value) + { + } + + internal TypeForwarderHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.TypeForwarder || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.TypeForwarder) << 24); + _Validate(); + } + + public static implicit operator Handle(TypeForwarderHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public TypeForwarder GetTypeForwarder(MetadataReader reader) + { + return reader.GetTypeForwarder(this); + } // GetTypeForwarder + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.TypeForwarder) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // TypeForwarderHandle + + public partial struct TypeInstantiationSignature + { + internal MetadataReader _reader; + internal TypeInstantiationSignatureHandle _handle; + + public TypeInstantiationSignatureHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: TypeDefinition, TypeReference, TypeSpecification + + public Handle GenericType + { + get + { + return _genericType; + } + } // GenericType + + internal Handle _genericType; + /// One of: TypeDefinition, TypeReference, TypeSpecification + + public HandleCollection GenericTypeArguments + { + get + { + return _genericTypeArguments; + } + } // GenericTypeArguments + + internal HandleCollection _genericTypeArguments; + } // TypeInstantiationSignature + + public partial struct TypeInstantiationSignatureHandle + { + public override bool Equals(object obj) + { + if (obj is TypeInstantiationSignatureHandle) + return _value == ((TypeInstantiationSignatureHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(TypeInstantiationSignatureHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal TypeInstantiationSignatureHandle(Handle handle) : this(handle._value) + { + } + + internal TypeInstantiationSignatureHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.TypeInstantiationSignature || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.TypeInstantiationSignature) << 24); + _Validate(); + } + + public static implicit operator Handle(TypeInstantiationSignatureHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public TypeInstantiationSignature GetTypeInstantiationSignature(MetadataReader reader) + { + return reader.GetTypeInstantiationSignature(this); + } // GetTypeInstantiationSignature + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.TypeInstantiationSignature) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // TypeInstantiationSignatureHandle + + public partial struct TypeReference + { + internal MetadataReader _reader; + internal TypeReferenceHandle _handle; + + public TypeReferenceHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: NamespaceReference, TypeReference + + public Handle ParentNamespaceOrType + { + get + { + return _parentNamespaceOrType; + } + } // ParentNamespaceOrType + + internal Handle _parentNamespaceOrType; + + public ConstantStringValueHandle TypeName + { + get + { + return _typeName; + } + } // TypeName + + internal ConstantStringValueHandle _typeName; + } // TypeReference + + public partial struct TypeReferenceHandle + { + public override bool Equals(object obj) + { + if (obj is TypeReferenceHandle) + return _value == ((TypeReferenceHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(TypeReferenceHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal TypeReferenceHandle(Handle handle) : this(handle._value) + { + } + + internal TypeReferenceHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.TypeReference || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.TypeReference) << 24); + _Validate(); + } + + public static implicit operator Handle(TypeReferenceHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public TypeReference GetTypeReference(MetadataReader reader) + { + return reader.GetTypeReference(this); + } // GetTypeReference + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.TypeReference) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // TypeReferenceHandle + + public partial struct TypeSpecification + { + internal MetadataReader _reader; + internal TypeSpecificationHandle _handle; + + public TypeSpecificationHandle Handle + { + get + { + return _handle; + } + } // Handle + /// One of: TypeDefinition, TypeReference, TypeInstantiationSignature, SZArraySignature, ArraySignature, PointerSignature, FunctionPointerSignature, ByReferenceSignature, TypeVariableSignature, MethodTypeVariableSignature + + public Handle Signature + { + get + { + return _signature; + } + } // Signature + + internal Handle _signature; + } // TypeSpecification + + public partial struct TypeSpecificationHandle + { + public override bool Equals(object obj) + { + if (obj is TypeSpecificationHandle) + return _value == ((TypeSpecificationHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(TypeSpecificationHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal TypeSpecificationHandle(Handle handle) : this(handle._value) + { + } + + internal TypeSpecificationHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.TypeSpecification || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.TypeSpecification) << 24); + _Validate(); + } + + public static implicit operator Handle(TypeSpecificationHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public TypeSpecification GetTypeSpecification(MetadataReader reader) + { + return reader.GetTypeSpecification(this); + } // GetTypeSpecification + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.TypeSpecification) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // TypeSpecificationHandle + + public partial struct TypeVariableSignature + { + internal MetadataReader _reader; + internal TypeVariableSignatureHandle _handle; + + public TypeVariableSignatureHandle Handle + { + get + { + return _handle; + } + } // Handle + + public int Number + { + get + { + return _number; + } + } // Number + + internal int _number; + } // TypeVariableSignature + + public partial struct TypeVariableSignatureHandle + { + public override bool Equals(object obj) + { + if (obj is TypeVariableSignatureHandle) + return _value == ((TypeVariableSignatureHandle)obj)._value; + else if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } // Equals + + public bool Equals(TypeVariableSignatureHandle handle) + { + return _value == handle._value; + } // Equals + + public bool Equals(Handle handle) + { + return _value == handle._value; + } // Equals + + public override int GetHashCode() + { + return (int)_value; + } // GetHashCode + + internal int _value; + + internal TypeVariableSignatureHandle(Handle handle) : this(handle._value) + { + } + + internal TypeVariableSignatureHandle(int value) + { + HandleType hType = (HandleType)(value >> 24); + if (!(hType == 0 || hType == HandleType.TypeVariableSignature || hType == HandleType.Null)) + throw new ArgumentException(); + _value = (value & 0x00FFFFFF) | (((int)HandleType.TypeVariableSignature) << 24); + _Validate(); + } + + public static implicit operator Handle(TypeVariableSignatureHandle handle) + { + return new Handle(handle._value); + } // Handle + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } // Offset + + public TypeVariableSignature GetTypeVariableSignature(MetadataReader reader) + { + return reader.GetTypeVariableSignature(this); + } // GetTypeVariableSignature + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } // IsNull + + public Handle ToHandle(MetadataReader reader) + { + return reader.ToHandle(this); + } // ToHandle + + [System.Diagnostics.Conditional("DEBUG")] + internal void _Validate() + { + if ((HandleType)((_value & 0xFF000000) >> 24) != HandleType.TypeVariableSignature) + throw new ArgumentException(); + } // _Validate + + public override string ToString() + { + return string.Format("{0:X8}", _value); + } // ToString + } // TypeVariableSignatureHandle + + public partial struct NamedArgumentHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal NamedArgumentHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private NamedArgumentHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(NamedArgumentHandle); + } + + public NamedArgumentHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // NamedArgumentHandleCollection + + public partial struct MethodSemanticsHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal MethodSemanticsHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private MethodSemanticsHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(MethodSemanticsHandle); + } + + public MethodSemanticsHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // MethodSemanticsHandleCollection + + public partial struct CustomAttributeHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal CustomAttributeHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private CustomAttributeHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(CustomAttributeHandle); + } + + public CustomAttributeHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // CustomAttributeHandleCollection + + public partial struct ParameterHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal ParameterHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private ParameterHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(ParameterHandle); + } + + public ParameterHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // ParameterHandleCollection + + public partial struct GenericParameterHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal GenericParameterHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private GenericParameterHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(GenericParameterHandle); + } + + public GenericParameterHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // GenericParameterHandleCollection + + public partial struct TypeDefinitionHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal TypeDefinitionHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private TypeDefinitionHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(TypeDefinitionHandle); + } + + public TypeDefinitionHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // TypeDefinitionHandleCollection + + public partial struct TypeForwarderHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal TypeForwarderHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private TypeForwarderHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(TypeForwarderHandle); + } + + public TypeForwarderHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // TypeForwarderHandleCollection + + public partial struct NamespaceDefinitionHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal NamespaceDefinitionHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private NamespaceDefinitionHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(NamespaceDefinitionHandle); + } + + public NamespaceDefinitionHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // NamespaceDefinitionHandleCollection + + public partial struct MethodHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal MethodHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private MethodHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(MethodHandle); + } + + public MethodHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // MethodHandleCollection + + public partial struct FieldHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal FieldHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private FieldHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(FieldHandle); + } + + public FieldHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // FieldHandleCollection + + public partial struct PropertyHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal PropertyHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private PropertyHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(PropertyHandle); + } + + public PropertyHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // PropertyHandleCollection + + public partial struct EventHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal EventHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private EventHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(EventHandle); + } + + public EventHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // EventHandleCollection + + public partial struct ScopeDefinitionHandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal ScopeDefinitionHandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private ScopeDefinitionHandle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(ScopeDefinitionHandle); + } + + public ScopeDefinitionHandle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // ScopeDefinitionHandleCollection + + public partial struct BooleanCollection + { + private NativeReader _reader; + private uint _offset; + + internal BooleanCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private bool _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(bool); + } + + public bool Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // BooleanCollection + + public partial struct CharCollection + { + private NativeReader _reader; + private uint _offset; + + internal CharCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private char _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(char); + } + + public char Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // CharCollection + + public partial struct ByteCollection + { + private NativeReader _reader; + private uint _offset; + + internal ByteCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private byte _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(byte); + } + + public byte Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // ByteCollection + + public partial struct SByteCollection + { + private NativeReader _reader; + private uint _offset; + + internal SByteCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private sbyte _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(sbyte); + } + + public sbyte Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // SByteCollection + + public partial struct Int16Collection + { + private NativeReader _reader; + private uint _offset; + + internal Int16Collection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private short _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(short); + } + + public short Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // Int16Collection + + public partial struct UInt16Collection + { + private NativeReader _reader; + private uint _offset; + + internal UInt16Collection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private ushort _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(ushort); + } + + public ushort Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // UInt16Collection + + public partial struct Int32Collection + { + private NativeReader _reader; + private uint _offset; + + internal Int32Collection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private int _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(int); + } + + public int Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // Int32Collection + + public partial struct UInt32Collection + { + private NativeReader _reader; + private uint _offset; + + internal UInt32Collection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private uint _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(uint); + } + + public uint Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // UInt32Collection + + public partial struct Int64Collection + { + private NativeReader _reader; + private uint _offset; + + internal Int64Collection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private long _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(long); + } + + public long Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // Int64Collection + + public partial struct UInt64Collection + { + private NativeReader _reader; + private uint _offset; + + internal UInt64Collection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private ulong _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(ulong); + } + + public ulong Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // UInt64Collection + + public partial struct SingleCollection + { + private NativeReader _reader; + private uint _offset; + + internal SingleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private float _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(float); + } + + public float Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // SingleCollection + + public partial struct DoubleCollection + { + private NativeReader _reader; + private uint _offset; + + internal DoubleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private double _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(double); + } + + public double Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // DoubleCollection + + public partial struct Handle + { + public ArraySignatureHandle ToArraySignatureHandle(MetadataReader reader) + { + return new ArraySignatureHandle(this); + } // ToArraySignatureHandle + + public ByReferenceSignatureHandle ToByReferenceSignatureHandle(MetadataReader reader) + { + return new ByReferenceSignatureHandle(this); + } // ToByReferenceSignatureHandle + + public ConstantBooleanArrayHandle ToConstantBooleanArrayHandle(MetadataReader reader) + { + return new ConstantBooleanArrayHandle(this); + } // ToConstantBooleanArrayHandle + + public ConstantBooleanValueHandle ToConstantBooleanValueHandle(MetadataReader reader) + { + return new ConstantBooleanValueHandle(this); + } // ToConstantBooleanValueHandle + + public ConstantBoxedEnumValueHandle ToConstantBoxedEnumValueHandle(MetadataReader reader) + { + return new ConstantBoxedEnumValueHandle(this); + } // ToConstantBoxedEnumValueHandle + + public ConstantByteArrayHandle ToConstantByteArrayHandle(MetadataReader reader) + { + return new ConstantByteArrayHandle(this); + } // ToConstantByteArrayHandle + + public ConstantByteValueHandle ToConstantByteValueHandle(MetadataReader reader) + { + return new ConstantByteValueHandle(this); + } // ToConstantByteValueHandle + + public ConstantCharArrayHandle ToConstantCharArrayHandle(MetadataReader reader) + { + return new ConstantCharArrayHandle(this); + } // ToConstantCharArrayHandle + + public ConstantCharValueHandle ToConstantCharValueHandle(MetadataReader reader) + { + return new ConstantCharValueHandle(this); + } // ToConstantCharValueHandle + + public ConstantDoubleArrayHandle ToConstantDoubleArrayHandle(MetadataReader reader) + { + return new ConstantDoubleArrayHandle(this); + } // ToConstantDoubleArrayHandle + + public ConstantDoubleValueHandle ToConstantDoubleValueHandle(MetadataReader reader) + { + return new ConstantDoubleValueHandle(this); + } // ToConstantDoubleValueHandle + + public ConstantEnumArrayHandle ToConstantEnumArrayHandle(MetadataReader reader) + { + return new ConstantEnumArrayHandle(this); + } // ToConstantEnumArrayHandle + + public ConstantHandleArrayHandle ToConstantHandleArrayHandle(MetadataReader reader) + { + return new ConstantHandleArrayHandle(this); + } // ToConstantHandleArrayHandle + + public ConstantInt16ArrayHandle ToConstantInt16ArrayHandle(MetadataReader reader) + { + return new ConstantInt16ArrayHandle(this); + } // ToConstantInt16ArrayHandle + + public ConstantInt16ValueHandle ToConstantInt16ValueHandle(MetadataReader reader) + { + return new ConstantInt16ValueHandle(this); + } // ToConstantInt16ValueHandle + + public ConstantInt32ArrayHandle ToConstantInt32ArrayHandle(MetadataReader reader) + { + return new ConstantInt32ArrayHandle(this); + } // ToConstantInt32ArrayHandle + + public ConstantInt32ValueHandle ToConstantInt32ValueHandle(MetadataReader reader) + { + return new ConstantInt32ValueHandle(this); + } // ToConstantInt32ValueHandle + + public ConstantInt64ArrayHandle ToConstantInt64ArrayHandle(MetadataReader reader) + { + return new ConstantInt64ArrayHandle(this); + } // ToConstantInt64ArrayHandle + + public ConstantInt64ValueHandle ToConstantInt64ValueHandle(MetadataReader reader) + { + return new ConstantInt64ValueHandle(this); + } // ToConstantInt64ValueHandle + + public ConstantReferenceValueHandle ToConstantReferenceValueHandle(MetadataReader reader) + { + return new ConstantReferenceValueHandle(this); + } // ToConstantReferenceValueHandle + + public ConstantSByteArrayHandle ToConstantSByteArrayHandle(MetadataReader reader) + { + return new ConstantSByteArrayHandle(this); + } // ToConstantSByteArrayHandle + + public ConstantSByteValueHandle ToConstantSByteValueHandle(MetadataReader reader) + { + return new ConstantSByteValueHandle(this); + } // ToConstantSByteValueHandle + + public ConstantSingleArrayHandle ToConstantSingleArrayHandle(MetadataReader reader) + { + return new ConstantSingleArrayHandle(this); + } // ToConstantSingleArrayHandle + + public ConstantSingleValueHandle ToConstantSingleValueHandle(MetadataReader reader) + { + return new ConstantSingleValueHandle(this); + } // ToConstantSingleValueHandle + + public ConstantStringArrayHandle ToConstantStringArrayHandle(MetadataReader reader) + { + return new ConstantStringArrayHandle(this); + } // ToConstantStringArrayHandle + + public ConstantStringValueHandle ToConstantStringValueHandle(MetadataReader reader) + { + return new ConstantStringValueHandle(this); + } // ToConstantStringValueHandle + + public ConstantUInt16ArrayHandle ToConstantUInt16ArrayHandle(MetadataReader reader) + { + return new ConstantUInt16ArrayHandle(this); + } // ToConstantUInt16ArrayHandle + + public ConstantUInt16ValueHandle ToConstantUInt16ValueHandle(MetadataReader reader) + { + return new ConstantUInt16ValueHandle(this); + } // ToConstantUInt16ValueHandle + + public ConstantUInt32ArrayHandle ToConstantUInt32ArrayHandle(MetadataReader reader) + { + return new ConstantUInt32ArrayHandle(this); + } // ToConstantUInt32ArrayHandle + + public ConstantUInt32ValueHandle ToConstantUInt32ValueHandle(MetadataReader reader) + { + return new ConstantUInt32ValueHandle(this); + } // ToConstantUInt32ValueHandle + + public ConstantUInt64ArrayHandle ToConstantUInt64ArrayHandle(MetadataReader reader) + { + return new ConstantUInt64ArrayHandle(this); + } // ToConstantUInt64ArrayHandle + + public ConstantUInt64ValueHandle ToConstantUInt64ValueHandle(MetadataReader reader) + { + return new ConstantUInt64ValueHandle(this); + } // ToConstantUInt64ValueHandle + + public CustomAttributeHandle ToCustomAttributeHandle(MetadataReader reader) + { + return new CustomAttributeHandle(this); + } // ToCustomAttributeHandle + + public EventHandle ToEventHandle(MetadataReader reader) + { + return new EventHandle(this); + } // ToEventHandle + + public FieldHandle ToFieldHandle(MetadataReader reader) + { + return new FieldHandle(this); + } // ToFieldHandle + + public FieldSignatureHandle ToFieldSignatureHandle(MetadataReader reader) + { + return new FieldSignatureHandle(this); + } // ToFieldSignatureHandle + + public FunctionPointerSignatureHandle ToFunctionPointerSignatureHandle(MetadataReader reader) + { + return new FunctionPointerSignatureHandle(this); + } // ToFunctionPointerSignatureHandle + + public GenericParameterHandle ToGenericParameterHandle(MetadataReader reader) + { + return new GenericParameterHandle(this); + } // ToGenericParameterHandle + + public MemberReferenceHandle ToMemberReferenceHandle(MetadataReader reader) + { + return new MemberReferenceHandle(this); + } // ToMemberReferenceHandle + + public MethodHandle ToMethodHandle(MetadataReader reader) + { + return new MethodHandle(this); + } // ToMethodHandle + + public MethodInstantiationHandle ToMethodInstantiationHandle(MetadataReader reader) + { + return new MethodInstantiationHandle(this); + } // ToMethodInstantiationHandle + + public MethodSemanticsHandle ToMethodSemanticsHandle(MetadataReader reader) + { + return new MethodSemanticsHandle(this); + } // ToMethodSemanticsHandle + + public MethodSignatureHandle ToMethodSignatureHandle(MetadataReader reader) + { + return new MethodSignatureHandle(this); + } // ToMethodSignatureHandle + + public MethodTypeVariableSignatureHandle ToMethodTypeVariableSignatureHandle(MetadataReader reader) + { + return new MethodTypeVariableSignatureHandle(this); + } // ToMethodTypeVariableSignatureHandle + + public ModifiedTypeHandle ToModifiedTypeHandle(MetadataReader reader) + { + return new ModifiedTypeHandle(this); + } // ToModifiedTypeHandle + + public NamedArgumentHandle ToNamedArgumentHandle(MetadataReader reader) + { + return new NamedArgumentHandle(this); + } // ToNamedArgumentHandle + + public NamespaceDefinitionHandle ToNamespaceDefinitionHandle(MetadataReader reader) + { + return new NamespaceDefinitionHandle(this); + } // ToNamespaceDefinitionHandle + + public NamespaceReferenceHandle ToNamespaceReferenceHandle(MetadataReader reader) + { + return new NamespaceReferenceHandle(this); + } // ToNamespaceReferenceHandle + + public ParameterHandle ToParameterHandle(MetadataReader reader) + { + return new ParameterHandle(this); + } // ToParameterHandle + + public PointerSignatureHandle ToPointerSignatureHandle(MetadataReader reader) + { + return new PointerSignatureHandle(this); + } // ToPointerSignatureHandle + + public PropertyHandle ToPropertyHandle(MetadataReader reader) + { + return new PropertyHandle(this); + } // ToPropertyHandle + + public PropertySignatureHandle ToPropertySignatureHandle(MetadataReader reader) + { + return new PropertySignatureHandle(this); + } // ToPropertySignatureHandle + + public QualifiedFieldHandle ToQualifiedFieldHandle(MetadataReader reader) + { + return new QualifiedFieldHandle(this); + } // ToQualifiedFieldHandle + + public QualifiedMethodHandle ToQualifiedMethodHandle(MetadataReader reader) + { + return new QualifiedMethodHandle(this); + } // ToQualifiedMethodHandle + + public SZArraySignatureHandle ToSZArraySignatureHandle(MetadataReader reader) + { + return new SZArraySignatureHandle(this); + } // ToSZArraySignatureHandle + + public ScopeDefinitionHandle ToScopeDefinitionHandle(MetadataReader reader) + { + return new ScopeDefinitionHandle(this); + } // ToScopeDefinitionHandle + + public ScopeReferenceHandle ToScopeReferenceHandle(MetadataReader reader) + { + return new ScopeReferenceHandle(this); + } // ToScopeReferenceHandle + + public TypeDefinitionHandle ToTypeDefinitionHandle(MetadataReader reader) + { + return new TypeDefinitionHandle(this); + } // ToTypeDefinitionHandle + + public TypeForwarderHandle ToTypeForwarderHandle(MetadataReader reader) + { + return new TypeForwarderHandle(this); + } // ToTypeForwarderHandle + + public TypeInstantiationSignatureHandle ToTypeInstantiationSignatureHandle(MetadataReader reader) + { + return new TypeInstantiationSignatureHandle(this); + } // ToTypeInstantiationSignatureHandle + + public TypeReferenceHandle ToTypeReferenceHandle(MetadataReader reader) + { + return new TypeReferenceHandle(this); + } // ToTypeReferenceHandle + + public TypeSpecificationHandle ToTypeSpecificationHandle(MetadataReader reader) + { + return new TypeSpecificationHandle(this); + } // ToTypeSpecificationHandle + + public TypeVariableSignatureHandle ToTypeVariableSignatureHandle(MetadataReader reader) + { + return new TypeVariableSignatureHandle(this); + } // ToTypeVariableSignatureHandle + } // Handle + + public partial struct HandleCollection + { + private NativeReader _reader; + private uint _offset; + + internal HandleCollection(NativeReader reader, uint offset) + { + _offset = offset; + _reader = reader; + } + + public int Count + { + get + { + uint count; + _reader.DecodeUnsigned(_offset, out count); + return (int)count; + } + } // Count + + public Enumerator GetEnumerator() + { + return new Enumerator(_reader, _offset); + } // GetEnumerator + + public struct Enumerator + { + private NativeReader _reader; + private uint _offset; + private uint _remaining; + private Handle _current; + + internal Enumerator(NativeReader reader, uint offset) + { + _reader = reader; + _offset = reader.DecodeUnsigned(offset, out _remaining); + _current = default(Handle); + } + + public Handle Current + { + get + { + return _current; + } + } // Current + + public bool MoveNext() + { + if (_remaining == 0) + return false; + _remaining--; + _offset = _reader.Read(_offset, out _current); + return true; + } // MoveNext + + public void Dispose() + { + } // Dispose + } // Enumerator + } // HandleCollection + + public partial class MetadataReader + { + public ArraySignature GetArraySignature(ArraySignatureHandle handle) + { + ArraySignature record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._elementType); + offset = _streamReader.Read(offset, out record._rank); + offset = _streamReader.Read(offset, out record._sizes); + offset = _streamReader.Read(offset, out record._lowerBounds); + return record; + } // GetArraySignature + + public ByReferenceSignature GetByReferenceSignature(ByReferenceSignatureHandle handle) + { + ByReferenceSignature record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._type); + return record; + } // GetByReferenceSignature + + public ConstantBooleanArray GetConstantBooleanArray(ConstantBooleanArrayHandle handle) + { + ConstantBooleanArray record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantBooleanArray + + public ConstantBooleanValue GetConstantBooleanValue(ConstantBooleanValueHandle handle) + { + ConstantBooleanValue record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantBooleanValue + + public ConstantBoxedEnumValue GetConstantBoxedEnumValue(ConstantBoxedEnumValueHandle handle) + { + ConstantBoxedEnumValue record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + offset = _streamReader.Read(offset, out record._type); + return record; + } // GetConstantBoxedEnumValue + + public ConstantByteArray GetConstantByteArray(ConstantByteArrayHandle handle) + { + ConstantByteArray record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantByteArray + + public ConstantByteValue GetConstantByteValue(ConstantByteValueHandle handle) + { + ConstantByteValue record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantByteValue + + public ConstantCharArray GetConstantCharArray(ConstantCharArrayHandle handle) + { + ConstantCharArray record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantCharArray + + public ConstantCharValue GetConstantCharValue(ConstantCharValueHandle handle) + { + ConstantCharValue record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantCharValue + + public ConstantDoubleArray GetConstantDoubleArray(ConstantDoubleArrayHandle handle) + { + ConstantDoubleArray record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantDoubleArray + + public ConstantDoubleValue GetConstantDoubleValue(ConstantDoubleValueHandle handle) + { + ConstantDoubleValue record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantDoubleValue + + public ConstantEnumArray GetConstantEnumArray(ConstantEnumArrayHandle handle) + { + ConstantEnumArray record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._elementType); + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantEnumArray + + public ConstantHandleArray GetConstantHandleArray(ConstantHandleArrayHandle handle) + { + ConstantHandleArray record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantHandleArray + + public ConstantInt16Array GetConstantInt16Array(ConstantInt16ArrayHandle handle) + { + ConstantInt16Array record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantInt16Array + + public ConstantInt16Value GetConstantInt16Value(ConstantInt16ValueHandle handle) + { + ConstantInt16Value record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantInt16Value + + public ConstantInt32Array GetConstantInt32Array(ConstantInt32ArrayHandle handle) + { + ConstantInt32Array record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantInt32Array + + public ConstantInt32Value GetConstantInt32Value(ConstantInt32ValueHandle handle) + { + ConstantInt32Value record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantInt32Value + + public ConstantInt64Array GetConstantInt64Array(ConstantInt64ArrayHandle handle) + { + ConstantInt64Array record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantInt64Array + + public ConstantInt64Value GetConstantInt64Value(ConstantInt64ValueHandle handle) + { + ConstantInt64Value record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantInt64Value + + public ConstantReferenceValue GetConstantReferenceValue(ConstantReferenceValueHandle handle) + { + ConstantReferenceValue record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + return record; + } // GetConstantReferenceValue + + public ConstantSByteArray GetConstantSByteArray(ConstantSByteArrayHandle handle) + { + ConstantSByteArray record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantSByteArray + + public ConstantSByteValue GetConstantSByteValue(ConstantSByteValueHandle handle) + { + ConstantSByteValue record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantSByteValue + + public ConstantSingleArray GetConstantSingleArray(ConstantSingleArrayHandle handle) + { + ConstantSingleArray record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantSingleArray + + public ConstantSingleValue GetConstantSingleValue(ConstantSingleValueHandle handle) + { + ConstantSingleValue record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantSingleValue + + public ConstantStringArray GetConstantStringArray(ConstantStringArrayHandle handle) + { + ConstantStringArray record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantStringArray + + public ConstantStringValue GetConstantStringValue(ConstantStringValueHandle handle) + { + if (IsNull(handle)) + return new ConstantStringValue(); + ConstantStringValue record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantStringValue + + public ConstantUInt16Array GetConstantUInt16Array(ConstantUInt16ArrayHandle handle) + { + ConstantUInt16Array record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantUInt16Array + + public ConstantUInt16Value GetConstantUInt16Value(ConstantUInt16ValueHandle handle) + { + ConstantUInt16Value record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantUInt16Value + + public ConstantUInt32Array GetConstantUInt32Array(ConstantUInt32ArrayHandle handle) + { + ConstantUInt32Array record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantUInt32Array + + public ConstantUInt32Value GetConstantUInt32Value(ConstantUInt32ValueHandle handle) + { + ConstantUInt32Value record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantUInt32Value + + public ConstantUInt64Array GetConstantUInt64Array(ConstantUInt64ArrayHandle handle) + { + ConstantUInt64Array record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantUInt64Array + + public ConstantUInt64Value GetConstantUInt64Value(ConstantUInt64ValueHandle handle) + { + ConstantUInt64Value record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetConstantUInt64Value + + public CustomAttribute GetCustomAttribute(CustomAttributeHandle handle) + { + CustomAttribute record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._constructor); + offset = _streamReader.Read(offset, out record._fixedArguments); + offset = _streamReader.Read(offset, out record._namedArguments); + return record; + } // GetCustomAttribute + + public Event GetEvent(EventHandle handle) + { + Event record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._flags); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._type); + offset = _streamReader.Read(offset, out record._methodSemantics); + offset = _streamReader.Read(offset, out record._customAttributes); + return record; + } // GetEvent + + public Field GetField(FieldHandle handle) + { + Field record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._flags); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._signature); + offset = _streamReader.Read(offset, out record._defaultValue); + offset = _streamReader.Read(offset, out record._offset); + offset = _streamReader.Read(offset, out record._customAttributes); + return record; + } // GetField + + public FieldSignature GetFieldSignature(FieldSignatureHandle handle) + { + FieldSignature record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._type); + return record; + } // GetFieldSignature + + public FunctionPointerSignature GetFunctionPointerSignature(FunctionPointerSignatureHandle handle) + { + FunctionPointerSignature record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._signature); + return record; + } // GetFunctionPointerSignature + + public GenericParameter GetGenericParameter(GenericParameterHandle handle) + { + GenericParameter record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._number); + offset = _streamReader.Read(offset, out record._flags); + offset = _streamReader.Read(offset, out record._kind); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._constraints); + offset = _streamReader.Read(offset, out record._customAttributes); + return record; + } // GetGenericParameter + + public MemberReference GetMemberReference(MemberReferenceHandle handle) + { + MemberReference record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._parent); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._signature); + return record; + } // GetMemberReference + + public Method GetMethod(MethodHandle handle) + { + Method record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._flags); + offset = _streamReader.Read(offset, out record._implFlags); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._signature); + offset = _streamReader.Read(offset, out record._parameters); + offset = _streamReader.Read(offset, out record._genericParameters); + offset = _streamReader.Read(offset, out record._customAttributes); + return record; + } // GetMethod + + public MethodInstantiation GetMethodInstantiation(MethodInstantiationHandle handle) + { + MethodInstantiation record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._method); + offset = _streamReader.Read(offset, out record._genericTypeArguments); + return record; + } // GetMethodInstantiation + + public MethodSemantics GetMethodSemantics(MethodSemanticsHandle handle) + { + MethodSemantics record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._attributes); + offset = _streamReader.Read(offset, out record._method); + return record; + } // GetMethodSemantics + + public MethodSignature GetMethodSignature(MethodSignatureHandle handle) + { + MethodSignature record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._callingConvention); + offset = _streamReader.Read(offset, out record._genericParameterCount); + offset = _streamReader.Read(offset, out record._returnType); + offset = _streamReader.Read(offset, out record._parameters); + offset = _streamReader.Read(offset, out record._varArgParameters); + return record; + } // GetMethodSignature + + public MethodTypeVariableSignature GetMethodTypeVariableSignature(MethodTypeVariableSignatureHandle handle) + { + MethodTypeVariableSignature record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._number); + return record; + } // GetMethodTypeVariableSignature + + public ModifiedType GetModifiedType(ModifiedTypeHandle handle) + { + ModifiedType record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._isOptional); + offset = _streamReader.Read(offset, out record._modifierType); + offset = _streamReader.Read(offset, out record._type); + return record; + } // GetModifiedType + + public NamedArgument GetNamedArgument(NamedArgumentHandle handle) + { + NamedArgument record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._flags); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._type); + offset = _streamReader.Read(offset, out record._value); + return record; + } // GetNamedArgument + + public NamespaceDefinition GetNamespaceDefinition(NamespaceDefinitionHandle handle) + { + NamespaceDefinition record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._parentScopeOrNamespace); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._typeDefinitions); + offset = _streamReader.Read(offset, out record._typeForwarders); + offset = _streamReader.Read(offset, out record._namespaceDefinitions); + return record; + } // GetNamespaceDefinition + + public NamespaceReference GetNamespaceReference(NamespaceReferenceHandle handle) + { + NamespaceReference record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._parentScopeOrNamespace); + offset = _streamReader.Read(offset, out record._name); + return record; + } // GetNamespaceReference + + public Parameter GetParameter(ParameterHandle handle) + { + Parameter record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._flags); + offset = _streamReader.Read(offset, out record._sequence); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._defaultValue); + offset = _streamReader.Read(offset, out record._customAttributes); + return record; + } // GetParameter + + public PointerSignature GetPointerSignature(PointerSignatureHandle handle) + { + PointerSignature record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._type); + return record; + } // GetPointerSignature + + public Property GetProperty(PropertyHandle handle) + { + Property record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._flags); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._signature); + offset = _streamReader.Read(offset, out record._methodSemantics); + offset = _streamReader.Read(offset, out record._defaultValue); + offset = _streamReader.Read(offset, out record._customAttributes); + return record; + } // GetProperty + + public PropertySignature GetPropertySignature(PropertySignatureHandle handle) + { + PropertySignature record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._callingConvention); + offset = _streamReader.Read(offset, out record._type); + offset = _streamReader.Read(offset, out record._parameters); + return record; + } // GetPropertySignature + + public QualifiedField GetQualifiedField(QualifiedFieldHandle handle) + { + QualifiedField record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._field); + offset = _streamReader.Read(offset, out record._enclosingType); + return record; + } // GetQualifiedField + + public QualifiedMethod GetQualifiedMethod(QualifiedMethodHandle handle) + { + QualifiedMethod record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._method); + offset = _streamReader.Read(offset, out record._enclosingType); + return record; + } // GetQualifiedMethod + + public SZArraySignature GetSZArraySignature(SZArraySignatureHandle handle) + { + SZArraySignature record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._elementType); + return record; + } // GetSZArraySignature + + public ScopeDefinition GetScopeDefinition(ScopeDefinitionHandle handle) + { + ScopeDefinition record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._flags); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._hashAlgorithm); + offset = _streamReader.Read(offset, out record._majorVersion); + offset = _streamReader.Read(offset, out record._minorVersion); + offset = _streamReader.Read(offset, out record._buildNumber); + offset = _streamReader.Read(offset, out record._revisionNumber); + offset = _streamReader.Read(offset, out record._publicKey); + offset = _streamReader.Read(offset, out record._culture); + offset = _streamReader.Read(offset, out record._rootNamespaceDefinition); + offset = _streamReader.Read(offset, out record._entryPoint); + offset = _streamReader.Read(offset, out record._globalModuleType); + offset = _streamReader.Read(offset, out record._customAttributes); + offset = _streamReader.Read(offset, out record._moduleName); + offset = _streamReader.Read(offset, out record._mvid); + offset = _streamReader.Read(offset, out record._moduleCustomAttributes); + return record; + } // GetScopeDefinition + + public ScopeReference GetScopeReference(ScopeReferenceHandle handle) + { + ScopeReference record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._flags); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._majorVersion); + offset = _streamReader.Read(offset, out record._minorVersion); + offset = _streamReader.Read(offset, out record._buildNumber); + offset = _streamReader.Read(offset, out record._revisionNumber); + offset = _streamReader.Read(offset, out record._publicKeyOrToken); + offset = _streamReader.Read(offset, out record._culture); + return record; + } // GetScopeReference + + public TypeDefinition GetTypeDefinition(TypeDefinitionHandle handle) + { + TypeDefinition record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._flags); + offset = _streamReader.Read(offset, out record._baseType); + offset = _streamReader.Read(offset, out record._namespaceDefinition); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._size); + offset = _streamReader.Read(offset, out record._packingSize); + offset = _streamReader.Read(offset, out record._enclosingType); + offset = _streamReader.Read(offset, out record._nestedTypes); + offset = _streamReader.Read(offset, out record._methods); + offset = _streamReader.Read(offset, out record._fields); + offset = _streamReader.Read(offset, out record._properties); + offset = _streamReader.Read(offset, out record._events); + offset = _streamReader.Read(offset, out record._genericParameters); + offset = _streamReader.Read(offset, out record._interfaces); + offset = _streamReader.Read(offset, out record._customAttributes); + return record; + } // GetTypeDefinition + + public TypeForwarder GetTypeForwarder(TypeForwarderHandle handle) + { + TypeForwarder record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._scope); + offset = _streamReader.Read(offset, out record._name); + offset = _streamReader.Read(offset, out record._nestedTypes); + return record; + } // GetTypeForwarder + + public TypeInstantiationSignature GetTypeInstantiationSignature(TypeInstantiationSignatureHandle handle) + { + TypeInstantiationSignature record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._genericType); + offset = _streamReader.Read(offset, out record._genericTypeArguments); + return record; + } // GetTypeInstantiationSignature + + public TypeReference GetTypeReference(TypeReferenceHandle handle) + { + TypeReference record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._parentNamespaceOrType); + offset = _streamReader.Read(offset, out record._typeName); + return record; + } // GetTypeReference + + public TypeSpecification GetTypeSpecification(TypeSpecificationHandle handle) + { + TypeSpecification record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._signature); + return record; + } // GetTypeSpecification + + public TypeVariableSignature GetTypeVariableSignature(TypeVariableSignatureHandle handle) + { + TypeVariableSignature record; + record._reader = this; + record._handle = handle; + var offset = (uint)handle.Offset; + offset = _streamReader.Read(offset, out record._number); + return record; + } // GetTypeVariableSignature + + internal Handle ToHandle(ArraySignatureHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ByReferenceSignatureHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantBooleanArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantBooleanValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantBoxedEnumValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantByteArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantByteValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantCharArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantCharValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantDoubleArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantDoubleValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantEnumArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantHandleArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantInt16ArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantInt16ValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantInt32ArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantInt32ValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantInt64ArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantInt64ValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantReferenceValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantSByteArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantSByteValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantSingleArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantSingleValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantStringArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantStringValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantUInt16ArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantUInt16ValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantUInt32ArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantUInt32ValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantUInt64ArrayHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ConstantUInt64ValueHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(CustomAttributeHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(EventHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(FieldHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(FieldSignatureHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(FunctionPointerSignatureHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(GenericParameterHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(MemberReferenceHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(MethodHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(MethodInstantiationHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(MethodSemanticsHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(MethodSignatureHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(MethodTypeVariableSignatureHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ModifiedTypeHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(NamedArgumentHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(NamespaceDefinitionHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(NamespaceReferenceHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ParameterHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(PointerSignatureHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(PropertyHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(PropertySignatureHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(QualifiedFieldHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(QualifiedMethodHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(SZArraySignatureHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ScopeDefinitionHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(ScopeReferenceHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(TypeDefinitionHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(TypeForwarderHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(TypeInstantiationSignatureHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(TypeReferenceHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(TypeSpecificationHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal Handle ToHandle(TypeVariableSignatureHandle handle) + { + return new Handle(handle._value); + } // ToHandle + + internal ArraySignatureHandle ToArraySignatureHandle(Handle handle) + { + return new ArraySignatureHandle(handle._value); + } // ToArraySignatureHandle + + internal ByReferenceSignatureHandle ToByReferenceSignatureHandle(Handle handle) + { + return new ByReferenceSignatureHandle(handle._value); + } // ToByReferenceSignatureHandle + + internal ConstantBooleanArrayHandle ToConstantBooleanArrayHandle(Handle handle) + { + return new ConstantBooleanArrayHandle(handle._value); + } // ToConstantBooleanArrayHandle + + internal ConstantBooleanValueHandle ToConstantBooleanValueHandle(Handle handle) + { + return new ConstantBooleanValueHandle(handle._value); + } // ToConstantBooleanValueHandle + + internal ConstantBoxedEnumValueHandle ToConstantBoxedEnumValueHandle(Handle handle) + { + return new ConstantBoxedEnumValueHandle(handle._value); + } // ToConstantBoxedEnumValueHandle + + internal ConstantByteArrayHandle ToConstantByteArrayHandle(Handle handle) + { + return new ConstantByteArrayHandle(handle._value); + } // ToConstantByteArrayHandle + + internal ConstantByteValueHandle ToConstantByteValueHandle(Handle handle) + { + return new ConstantByteValueHandle(handle._value); + } // ToConstantByteValueHandle + + internal ConstantCharArrayHandle ToConstantCharArrayHandle(Handle handle) + { + return new ConstantCharArrayHandle(handle._value); + } // ToConstantCharArrayHandle + + internal ConstantCharValueHandle ToConstantCharValueHandle(Handle handle) + { + return new ConstantCharValueHandle(handle._value); + } // ToConstantCharValueHandle + + internal ConstantDoubleArrayHandle ToConstantDoubleArrayHandle(Handle handle) + { + return new ConstantDoubleArrayHandle(handle._value); + } // ToConstantDoubleArrayHandle + + internal ConstantDoubleValueHandle ToConstantDoubleValueHandle(Handle handle) + { + return new ConstantDoubleValueHandle(handle._value); + } // ToConstantDoubleValueHandle + + internal ConstantEnumArrayHandle ToConstantEnumArrayHandle(Handle handle) + { + return new ConstantEnumArrayHandle(handle._value); + } // ToConstantEnumArrayHandle + + internal ConstantHandleArrayHandle ToConstantHandleArrayHandle(Handle handle) + { + return new ConstantHandleArrayHandle(handle._value); + } // ToConstantHandleArrayHandle + + internal ConstantInt16ArrayHandle ToConstantInt16ArrayHandle(Handle handle) + { + return new ConstantInt16ArrayHandle(handle._value); + } // ToConstantInt16ArrayHandle + + internal ConstantInt16ValueHandle ToConstantInt16ValueHandle(Handle handle) + { + return new ConstantInt16ValueHandle(handle._value); + } // ToConstantInt16ValueHandle + + internal ConstantInt32ArrayHandle ToConstantInt32ArrayHandle(Handle handle) + { + return new ConstantInt32ArrayHandle(handle._value); + } // ToConstantInt32ArrayHandle + + internal ConstantInt32ValueHandle ToConstantInt32ValueHandle(Handle handle) + { + return new ConstantInt32ValueHandle(handle._value); + } // ToConstantInt32ValueHandle + + internal ConstantInt64ArrayHandle ToConstantInt64ArrayHandle(Handle handle) + { + return new ConstantInt64ArrayHandle(handle._value); + } // ToConstantInt64ArrayHandle + + internal ConstantInt64ValueHandle ToConstantInt64ValueHandle(Handle handle) + { + return new ConstantInt64ValueHandle(handle._value); + } // ToConstantInt64ValueHandle + + internal ConstantReferenceValueHandle ToConstantReferenceValueHandle(Handle handle) + { + return new ConstantReferenceValueHandle(handle._value); + } // ToConstantReferenceValueHandle + + internal ConstantSByteArrayHandle ToConstantSByteArrayHandle(Handle handle) + { + return new ConstantSByteArrayHandle(handle._value); + } // ToConstantSByteArrayHandle + + internal ConstantSByteValueHandle ToConstantSByteValueHandle(Handle handle) + { + return new ConstantSByteValueHandle(handle._value); + } // ToConstantSByteValueHandle + + internal ConstantSingleArrayHandle ToConstantSingleArrayHandle(Handle handle) + { + return new ConstantSingleArrayHandle(handle._value); + } // ToConstantSingleArrayHandle + + internal ConstantSingleValueHandle ToConstantSingleValueHandle(Handle handle) + { + return new ConstantSingleValueHandle(handle._value); + } // ToConstantSingleValueHandle + + internal ConstantStringArrayHandle ToConstantStringArrayHandle(Handle handle) + { + return new ConstantStringArrayHandle(handle._value); + } // ToConstantStringArrayHandle + + internal ConstantStringValueHandle ToConstantStringValueHandle(Handle handle) + { + return new ConstantStringValueHandle(handle._value); + } // ToConstantStringValueHandle + + internal ConstantUInt16ArrayHandle ToConstantUInt16ArrayHandle(Handle handle) + { + return new ConstantUInt16ArrayHandle(handle._value); + } // ToConstantUInt16ArrayHandle + + internal ConstantUInt16ValueHandle ToConstantUInt16ValueHandle(Handle handle) + { + return new ConstantUInt16ValueHandle(handle._value); + } // ToConstantUInt16ValueHandle + + internal ConstantUInt32ArrayHandle ToConstantUInt32ArrayHandle(Handle handle) + { + return new ConstantUInt32ArrayHandle(handle._value); + } // ToConstantUInt32ArrayHandle + + internal ConstantUInt32ValueHandle ToConstantUInt32ValueHandle(Handle handle) + { + return new ConstantUInt32ValueHandle(handle._value); + } // ToConstantUInt32ValueHandle + + internal ConstantUInt64ArrayHandle ToConstantUInt64ArrayHandle(Handle handle) + { + return new ConstantUInt64ArrayHandle(handle._value); + } // ToConstantUInt64ArrayHandle + + internal ConstantUInt64ValueHandle ToConstantUInt64ValueHandle(Handle handle) + { + return new ConstantUInt64ValueHandle(handle._value); + } // ToConstantUInt64ValueHandle + + internal CustomAttributeHandle ToCustomAttributeHandle(Handle handle) + { + return new CustomAttributeHandle(handle._value); + } // ToCustomAttributeHandle + + internal EventHandle ToEventHandle(Handle handle) + { + return new EventHandle(handle._value); + } // ToEventHandle + + internal FieldHandle ToFieldHandle(Handle handle) + { + return new FieldHandle(handle._value); + } // ToFieldHandle + + internal FieldSignatureHandle ToFieldSignatureHandle(Handle handle) + { + return new FieldSignatureHandle(handle._value); + } // ToFieldSignatureHandle + + internal FunctionPointerSignatureHandle ToFunctionPointerSignatureHandle(Handle handle) + { + return new FunctionPointerSignatureHandle(handle._value); + } // ToFunctionPointerSignatureHandle + + internal GenericParameterHandle ToGenericParameterHandle(Handle handle) + { + return new GenericParameterHandle(handle._value); + } // ToGenericParameterHandle + + internal MemberReferenceHandle ToMemberReferenceHandle(Handle handle) + { + return new MemberReferenceHandle(handle._value); + } // ToMemberReferenceHandle + + internal MethodHandle ToMethodHandle(Handle handle) + { + return new MethodHandle(handle._value); + } // ToMethodHandle + + internal MethodInstantiationHandle ToMethodInstantiationHandle(Handle handle) + { + return new MethodInstantiationHandle(handle._value); + } // ToMethodInstantiationHandle + + internal MethodSemanticsHandle ToMethodSemanticsHandle(Handle handle) + { + return new MethodSemanticsHandle(handle._value); + } // ToMethodSemanticsHandle + + internal MethodSignatureHandle ToMethodSignatureHandle(Handle handle) + { + return new MethodSignatureHandle(handle._value); + } // ToMethodSignatureHandle + + internal MethodTypeVariableSignatureHandle ToMethodTypeVariableSignatureHandle(Handle handle) + { + return new MethodTypeVariableSignatureHandle(handle._value); + } // ToMethodTypeVariableSignatureHandle + + internal ModifiedTypeHandle ToModifiedTypeHandle(Handle handle) + { + return new ModifiedTypeHandle(handle._value); + } // ToModifiedTypeHandle + + internal NamedArgumentHandle ToNamedArgumentHandle(Handle handle) + { + return new NamedArgumentHandle(handle._value); + } // ToNamedArgumentHandle + + internal NamespaceDefinitionHandle ToNamespaceDefinitionHandle(Handle handle) + { + return new NamespaceDefinitionHandle(handle._value); + } // ToNamespaceDefinitionHandle + + internal NamespaceReferenceHandle ToNamespaceReferenceHandle(Handle handle) + { + return new NamespaceReferenceHandle(handle._value); + } // ToNamespaceReferenceHandle + + internal ParameterHandle ToParameterHandle(Handle handle) + { + return new ParameterHandle(handle._value); + } // ToParameterHandle + + internal PointerSignatureHandle ToPointerSignatureHandle(Handle handle) + { + return new PointerSignatureHandle(handle._value); + } // ToPointerSignatureHandle + + internal PropertyHandle ToPropertyHandle(Handle handle) + { + return new PropertyHandle(handle._value); + } // ToPropertyHandle + + internal PropertySignatureHandle ToPropertySignatureHandle(Handle handle) + { + return new PropertySignatureHandle(handle._value); + } // ToPropertySignatureHandle + + internal QualifiedFieldHandle ToQualifiedFieldHandle(Handle handle) + { + return new QualifiedFieldHandle(handle._value); + } // ToQualifiedFieldHandle + + internal QualifiedMethodHandle ToQualifiedMethodHandle(Handle handle) + { + return new QualifiedMethodHandle(handle._value); + } // ToQualifiedMethodHandle + + internal SZArraySignatureHandle ToSZArraySignatureHandle(Handle handle) + { + return new SZArraySignatureHandle(handle._value); + } // ToSZArraySignatureHandle + + internal ScopeDefinitionHandle ToScopeDefinitionHandle(Handle handle) + { + return new ScopeDefinitionHandle(handle._value); + } // ToScopeDefinitionHandle + + internal ScopeReferenceHandle ToScopeReferenceHandle(Handle handle) + { + return new ScopeReferenceHandle(handle._value); + } // ToScopeReferenceHandle + + internal TypeDefinitionHandle ToTypeDefinitionHandle(Handle handle) + { + return new TypeDefinitionHandle(handle._value); + } // ToTypeDefinitionHandle + + internal TypeForwarderHandle ToTypeForwarderHandle(Handle handle) + { + return new TypeForwarderHandle(handle._value); + } // ToTypeForwarderHandle + + internal TypeInstantiationSignatureHandle ToTypeInstantiationSignatureHandle(Handle handle) + { + return new TypeInstantiationSignatureHandle(handle._value); + } // ToTypeInstantiationSignatureHandle + + internal TypeReferenceHandle ToTypeReferenceHandle(Handle handle) + { + return new TypeReferenceHandle(handle._value); + } // ToTypeReferenceHandle + + internal TypeSpecificationHandle ToTypeSpecificationHandle(Handle handle) + { + return new TypeSpecificationHandle(handle._value); + } // ToTypeSpecificationHandle + + internal TypeVariableSignatureHandle ToTypeVariableSignatureHandle(Handle handle) + { + return new TypeVariableSignatureHandle(handle._value); + } // ToTypeVariableSignatureHandle + + internal bool IsNull(ArraySignatureHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ByReferenceSignatureHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantBooleanArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantBooleanValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantBoxedEnumValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantByteArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantByteValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantCharArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantCharValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantDoubleArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantDoubleValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantEnumArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantHandleArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantInt16ArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantInt16ValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantInt32ArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantInt32ValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantInt64ArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantInt64ValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantReferenceValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantSByteArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantSByteValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantSingleArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantSingleValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantStringArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantStringValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantUInt16ArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantUInt16ValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantUInt32ArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantUInt32ValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantUInt64ArrayHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ConstantUInt64ValueHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(CustomAttributeHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(EventHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(FieldHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(FieldSignatureHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(FunctionPointerSignatureHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(GenericParameterHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(MemberReferenceHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(MethodHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(MethodInstantiationHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(MethodSemanticsHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(MethodSignatureHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(MethodTypeVariableSignatureHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ModifiedTypeHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(NamedArgumentHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(NamespaceDefinitionHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(NamespaceReferenceHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ParameterHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(PointerSignatureHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(PropertyHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(PropertySignatureHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(QualifiedFieldHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(QualifiedMethodHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(SZArraySignatureHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ScopeDefinitionHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(ScopeReferenceHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(TypeDefinitionHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(TypeForwarderHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(TypeInstantiationSignatureHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(TypeReferenceHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(TypeSpecificationHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + + internal bool IsNull(TypeVariableSignatureHandle handle) + { + return (handle._value & 0x00FFFFFF) == 0; + } // IsNull + } // MetadataReader +} // Internal.Metadata.NativeFormat diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeMetadataReader.cs b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeMetadataReader.cs new file mode 100644 index 00000000000000..8eba905400e423 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeMetadataReader.cs @@ -0,0 +1,263 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +#pragma warning disable 169 + +// There is no defined ordering between fields in multiple declarations of partial class or struct +#pragma warning disable 282 + + +using System; +using System.IO; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using Internal.NativeFormat; + +namespace Internal.Metadata.NativeFormat +{ + // This Enum matches CorMethodSemanticsAttr defined in CorHdr.h + [Flags] + public enum MethodSemanticsAttributes + { + Setter = 0x0001, + Getter = 0x0002, + Other = 0x0004, + AddOn = 0x0008, + RemoveOn = 0x0010, + Fire = 0x0020, + } + + // This Enum matches CorPInvokeMap defined in CorHdr.h + [Flags] + public enum PInvokeAttributes + { + NoMangle = 0x0001, + + CharSetMask = 0x0006, + CharSetNotSpec = 0x0000, + CharSetAnsi = 0x0002, + CharSetUnicode = 0x0004, + CharSetAuto = 0x0006, + + BestFitUseAssem = 0x0000, + BestFitEnabled = 0x0010, + BestFitDisabled = 0x0020, + BestFitMask = 0x0030, + + ThrowOnUnmappableCharUseAssem = 0x0000, + ThrowOnUnmappableCharEnabled = 0x1000, + ThrowOnUnmappableCharDisabled = 0x2000, + ThrowOnUnmappableCharMask = 0x3000, + + SupportsLastError = 0x0040, + + CallConvMask = 0x0700, + CallConvWinapi = 0x0100, + CallConvCdecl = 0x0200, + CallConvStdcall = 0x0300, + CallConvThiscall = 0x0400, + CallConvFastcall = 0x0500, + + MaxValue = 0xFFFF, + } + + public partial struct Handle + { + public override bool Equals(object obj) + { + if (obj is Handle) + return _value == ((Handle)obj)._value; + else + return false; + } + + public bool Equals(Handle handle) + { + return _value == handle._value; + } + + public override int GetHashCode() + { + return (int)_value; + } + + internal Handle(int value) + { + _value = value; + } + + internal void Validate(params HandleType[] permittedTypes) + { + var myHandleType = (HandleType)(_value >> 24); + foreach (var hType in permittedTypes) + { + if (myHandleType == hType) + { + return; + } + } + if (myHandleType == HandleType.Null) + { + return; + } + throw new ArgumentException("Invalid handle type"); + } + + public Handle(HandleType type, int offset) + { + _value = (int)type << 24 | (int)offset; + } + + public HandleType HandleType + { + get + { + return (HandleType)(_value >> 24); + } + } + + internal int Offset + { + get + { + return (this._value & 0x00FFFFFF); + } + } + + public bool IsNull(MetadataReader reader) + { + return reader.IsNull(this); + } + + public int ToIntToken() + { + return _value; + } + + public static Handle FromIntToken(int value) + { + return new Handle(value); + } + + internal int _value; + +#if DEBUG + public override string ToString() + { + return string.Format("{1} : {0,8:X8}", _value, Enum.GetName(typeof(HandleType), this.HandleType)); + } +#endif + } + + public static class NativeFormatReaderExtensions + { + public static string GetString(this MetadataReader reader, ConstantStringValueHandle handle) + { + return reader.GetConstantStringValue(handle).Value; + } + } + + /// + /// ConstantReferenceValue can only be used to encapsulate null reference values, + /// and therefore does not actually store the value. + /// + public partial struct ConstantReferenceValue + { + /// Always returns null value. + public object Value + { get { return null; } } + } // ConstantReferenceValue + + public partial struct ConstantStringValueHandle + { + public bool StringEquals(string value, MetadataReader reader) + { + return reader.StringEquals(this, value); + } + } + + public sealed partial class MetadataReader + { + private MetadataHeader _header; + + internal NativeReader _streamReader; + + // Creates a metadata reader on a memory-mapped file block + public unsafe MetadataReader(IntPtr pBuffer, int cbBuffer) + { + _streamReader = new NativeReader((byte*)pBuffer, (uint)cbBuffer); + + _header = new MetadataHeader(); + _header.Decode(_streamReader); + } + + /// + /// Used as the root entrypoint for metadata, this is where all top-down + /// structural walks of metadata must start. + /// + public ScopeDefinitionHandleCollection ScopeDefinitions + { + get + { + return _header.ScopeDefinitions; + } + } + + /// + /// Returns a Handle value representing the null value. Can be used + /// to test handle values of all types for null. + /// + public Handle NullHandle + { + get + { + return new Handle() { _value = ((int)HandleType.Null) << 24 }; + } + } + + /// + /// Returns true if handle is null. + /// + public bool IsNull(Handle handle) + { + return handle._value == NullHandle._value; + } + + /// + /// Idempotent - simply returns the provided handle value. Exists for + /// consistency so that generated code does not need to handle this + /// as a special case. + /// + public Handle ToHandle(Handle handle) + { + return handle; + } + + internal bool StringEquals(ConstantStringValueHandle handle, string value) + { + return _streamReader.StringEquals((uint)handle.Offset, value); + } + } + + internal partial class MetadataHeader + { + /// + /// Signature should be updated every time the metadata schema changes. + /// + public const uint Signature = 0xDEADDFFD; + + /// + /// The set of ScopeDefinitions contained within this metadata resource. + /// + public ScopeDefinitionHandleCollection ScopeDefinitions; + + public void Decode(NativeReader reader) + { + if (reader.ReadUInt32(0) != Signature) + reader.ThrowBadImageFormatException(); + reader.Read(4, out ScopeDefinitions); + } + } +} diff --git a/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/UpdateNativeFormatSources.cmd b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/UpdateNativeFormatSources.cmd new file mode 100644 index 00000000000000..b74e787cc77e2f --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/UpdateNativeFormatSources.cmd @@ -0,0 +1,2 @@ +csc Generator\*.cs /out:Generator.exe +Generator.exe diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.Metadata.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.Metadata.cs new file mode 100644 index 00000000000000..97bb5cfcace37a --- /dev/null +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.Metadata.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// --------------------------------------------------------------------------- +// Native Format Reader +// +// Metadata / NativeLayoutInfo reading methods +// --------------------------------------------------------------------------- + +using System; +using System.Diagnostics; + +namespace Internal.NativeFormat +{ + internal partial struct NativeParser + { + public BagElementKind GetBagElementKind() + { + return (BagElementKind)GetUnsigned(); + } + + public FixupSignatureKind GetFixupSignatureKind() + { + return (FixupSignatureKind)GetUnsigned(); + } + + public TypeSignatureKind GetTypeSignatureKind(out uint data) + { + uint val = GetUnsigned(); + data = (val >> 4); + return (TypeSignatureKind)(val & 0xF); + } + + public NativeParser GetLookbackParser(uint lookback) + { + // Adjust the lookback by the size of the TypeSignatureKind element and the minimum lookback size + uint adjustedLookback = lookback + NativePrimitiveDecoder.GetUnsignedEncodingSize(lookback << 4) + 2; + return new NativeParser(_reader, _offset - adjustedLookback); + } + + public uint? GetUnsignedForBagElementKind(BagElementKind kindToFind) + { + var parser = this; + + BagElementKind kind; + while ((kind = parser.GetBagElementKind()) != BagElementKind.End) + { + if (kind == kindToFind) + return parser.GetUnsigned(); + + parser.SkipInteger(); + } + + return null; + } + + public NativeParser GetParserForBagElementKind(BagElementKind kindToFind) + { + var parser = this; + + BagElementKind kind; + while ((kind = parser.GetBagElementKind()) != BagElementKind.End) + { + if (kind == kindToFind) + return parser.GetParserFromRelativeOffset(); + + parser.SkipInteger(); + } + + return new NativeParser(); + } + } +} diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.Primitives.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.Primitives.cs new file mode 100644 index 00000000000000..e30810799e605f --- /dev/null +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.Primitives.cs @@ -0,0 +1,204 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// --------------------------------------------------------------------------- +// Native Format Reader +// +// Utilities to read native data from images, that are written by the NativeFormatWriter engine +// --------------------------------------------------------------------------- + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Internal.NativeFormat +{ + // Minimal functionality that is low level enough for use in the managed runtime. + internal unsafe partial struct NativePrimitiveDecoder + { + public static byte ReadUInt8(ref byte* stream) + { + byte result = *(stream); // Assumes little endian and unaligned access + stream++; + return result; + } + + public static ushort ReadUInt16(ref byte* stream) + { + ushort result = *(ushort*)(stream); // Assumes little endian and unaligned access + stream += 2; + return result; + } + + public static uint ReadUInt32(ref byte* stream) + { + uint result = *(uint*)(stream); // Assumes little endian and unaligned access + stream += 4; + return result; + } + + public static ulong ReadUInt64(ref byte* stream) + { + ulong result = *(ulong*)(stream); // Assumes little endian and unaligned access + stream += 8; + return result; + } + + public static unsafe float ReadFloat(ref byte* stream) + { + uint value = ReadUInt32(ref stream); + return *(float*)(&value); + } + + public static double ReadDouble(ref byte* stream) + { + ulong value = ReadUInt64(ref stream); + return *(double*)(&value); + } + + public static uint GetUnsignedEncodingSize(uint value) + { + if (value < 128) return 1; + if (value < 128 * 128) return 2; + if (value < 128 * 128 * 128) return 3; + if (value < 128 * 128 * 128 * 128) return 4; + return 5; + } + + public static uint DecodeUnsigned(ref byte* stream) + { + uint value = 0; + + uint val = *stream; + if ((val & 1) == 0) + { + value = (val >> 1); + stream += 1; + } + else if ((val & 2) == 0) + { + value = (val >> 2) | + (((uint)*(stream + 1)) << 6); + stream += 2; + } + else if ((val & 4) == 0) + { + value = (val >> 3) | + (((uint)*(stream + 1)) << 5) | + (((uint)*(stream + 2)) << 13); + stream += 3; + } + else if ((val & 8) == 0) + { + value = (val >> 4) | + (((uint)*(stream + 1)) << 4) | + (((uint)*(stream + 2)) << 12) | + (((uint)*(stream + 3)) << 20); + stream += 4; + } + else if ((val & 16) == 0) + { + stream += 1; + value = ReadUInt32(ref stream); + } + else + { + Debug.Assert(false); + return 0; + } + + return value; + } + + public static int DecodeSigned(ref byte* stream) + { + int value = 0; + + int val = *(stream); + if ((val & 1) == 0) + { + value = ((sbyte)val) >> 1; + stream += 1; + } + else if ((val & 2) == 0) + { + value = (val >> 2) | + (((int)*(sbyte*)(stream + 1)) << 6); + stream += 2; + } + else if ((val & 4) == 0) + { + value = (val >> 3) | + (((int)*(stream + 1)) << 5) | + (((int)*(sbyte*)(stream + 2)) << 13); + stream += 3; + } + else if ((val & 8) == 0) + { + value = (val >> 4) | + (((int)*(stream + 1)) << 4) | + (((int)*(stream + 2)) << 12) | + (((int)*(sbyte*)(stream + 3)) << 20); + stream += 4; + } + else if ((val & 16) == 0) + { + stream += 1; + value = (int)ReadUInt32(ref stream); + } + else + { + Debug.Assert(false); + return 0; + } + + return value; + } + + public static ulong DecodeUnsignedLong(ref byte* stream) + { + ulong value = 0; + + byte val = *stream; + if ((val & 31) != 31) + { + value = DecodeUnsigned(ref stream); + } + else if ((val & 32) == 0) + { + stream += 1; + value = ReadUInt64(ref stream); + } + else + { + Debug.Assert(false); + return 0; + } + + return value; + } + + public static long DecodeSignedLong(ref byte* stream) + { + long value = 0; + + byte val = *stream; + if ((val & 31) != 31) + { + value = DecodeSigned(ref stream); + } + else if ((val & 32) == 0) + { + stream += 1; + value = (long)ReadUInt64(ref stream); + } + else + { + Debug.Assert(false); + return 0; + } + + return value; + } + } +} diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.String.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.String.cs new file mode 100644 index 00000000000000..a044494d834326 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.String.cs @@ -0,0 +1,113 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// --------------------------------------------------------------------------- +// Native Format Reader +// +// UTF8 string reading methods +// --------------------------------------------------------------------------- + +using System; +using System.Text; + +namespace Internal.NativeFormat +{ + internal partial struct NativeParser + { + public string GetString() + { + string value; + _offset = _reader.DecodeString(_offset, out value); + return value; + } + + public void SkipString() + { + _offset = _reader.SkipString(_offset); + } + } + + internal partial class NativeReader + { + public string ReadString(uint offset) + { + string value; + DecodeString(offset, out value); + return value; + } + + public unsafe uint DecodeString(uint offset, out string value) + { + uint numBytes; + offset = DecodeUnsigned(offset, out numBytes); + + if (numBytes == 0) + { + value = string.Empty; + return offset; + } + + uint endOffset = offset + numBytes; + if (endOffset < numBytes || endOffset > _size) + ThrowBadImageFormatException(); + +#if NETFX_45 + byte[] bytes = new byte[numBytes]; + for (int i = 0; i < bytes.Length; i++) + bytes[i] = *(_base + offset + i); + + value = Encoding.UTF8.GetString(bytes, 0, bytes.Length); +#else + value = Encoding.UTF8.GetString(_base + offset, (int)numBytes); +#endif + + return endOffset; + } + + // Decode a string, but just skip it instead of returning it + public uint SkipString(uint offset) + { + uint numBytes; + offset = DecodeUnsigned(offset, out numBytes); + + if (numBytes == 0) + { + return offset; + } + + uint endOffset = offset + numBytes; + if (endOffset < numBytes || endOffset > _size) + ThrowBadImageFormatException(); + + return endOffset; + } + + public unsafe bool StringEquals(uint offset, string value) + { + uint originalOffset = offset; + + uint numBytes; + offset = DecodeUnsigned(offset, out numBytes); + + uint endOffset = offset + numBytes; + if (endOffset < numBytes || offset > _size) + ThrowBadImageFormatException(); + + if (numBytes < value.Length) + return false; + + for (int i = 0; i < value.Length; i++) + { + int ch = *(_base + offset + i); + if (ch > 0x7F) + return ReadString(originalOffset) == value; + + // We are assuming here that valid UTF8 encoded byte > 0x7F cannot map to a character with code point <= 0x7F + if (ch != value[i]) + return false; + } + + return numBytes == value.Length; // All char ANSI, all matching + } + } +} diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.cs new file mode 100644 index 00000000000000..6063683a7d62e2 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.cs @@ -0,0 +1,611 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// --------------------------------------------------------------------------- +// Native Format Reader +// +// Utilities to read native data from images, that are written by the NativeFormatWriter engine +// --------------------------------------------------------------------------- + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Internal.NativeFormat +{ + internal unsafe partial struct NativePrimitiveDecoder + { + public static void ThrowBadImageFormatException() + { + Debug.Assert(false); + throw new BadImageFormatException(); + } + + public static uint DecodeUnsigned(ref byte* stream, byte* streamEnd) + { + if (stream >= streamEnd) + ThrowBadImageFormatException(); + + uint value = 0; + + uint val = *stream; + if ((val & 1) == 0) + { + value = (val >> 1); + stream += 1; + } + else if ((val & 2) == 0) + { + if (stream + 1 >= streamEnd) + ThrowBadImageFormatException(); + value = (val >> 2) | + (((uint)*(stream + 1)) << 6); + stream += 2; + } + else if ((val & 4) == 0) + { + if (stream + 2 >= streamEnd) + ThrowBadImageFormatException(); + value = (val >> 3) | + (((uint)*(stream + 1)) << 5) | + (((uint)*(stream + 2)) << 13); + stream += 3; + } + else if ((val & 8) == 0) + { + if (stream + 3 >= streamEnd) + ThrowBadImageFormatException(); + value = (val >> 4) | + (((uint)*(stream + 1)) << 4) | + (((uint)*(stream + 2)) << 12) | + (((uint)*(stream + 3)) << 20); + stream += 4; + } + else if ((val & 16) == 0) + { + stream += 1; + value = ReadUInt32(ref stream); + } + else + { + ThrowBadImageFormatException(); + return 0; + } + + return value; + } + + public static int DecodeSigned(ref byte* stream, byte* streamEnd) + { + if (stream >= streamEnd) + ThrowBadImageFormatException(); + + int value = 0; + + int val = *(stream); + if ((val & 1) == 0) + { + value = ((sbyte)val) >> 1; + stream += 1; + } + else if ((val & 2) == 0) + { + if (stream + 1 >= streamEnd) + ThrowBadImageFormatException(); + value = (val >> 2) | + (((int)*(sbyte*)(stream + 1)) << 6); + stream += 2; + } + else if ((val & 4) == 0) + { + if (stream + 2 >= streamEnd) + ThrowBadImageFormatException(); + value = (val >> 3) | + (((int)*(stream + 1)) << 5) | + (((int)*(sbyte*)(stream + 2)) << 13); + stream += 3; + } + else if ((val & 8) == 0) + { + if (stream + 3 >= streamEnd) + ThrowBadImageFormatException(); + value = (val >> 4) | + (((int)*(stream + 1)) << 4) | + (((int)*(stream + 2)) << 12) | + (((int)*(sbyte*)(stream + 3)) << 20); + stream += 4; + } + else if ((val & 16) == 0) + { + stream += 1; + value = (int)ReadUInt32(ref stream); + } + else + { + ThrowBadImageFormatException(); + return 0; + } + + return value; + } + + public static ulong DecodeUnsignedLong(ref byte* stream, byte* streamEnd) + { + if (stream >= streamEnd) + ThrowBadImageFormatException(); + + ulong value = 0; + + byte val = *stream; + if ((val & 31) != 31) + { + value = DecodeUnsigned(ref stream, streamEnd); + } + else if ((val & 32) == 0) + { + stream += 1; + value = ReadUInt64(ref stream); + } + else + { + ThrowBadImageFormatException(); + return 0; + } + + return value; + } + + public static long DecodeSignedLong(ref byte* stream, byte* streamEnd) + { + if (stream >= streamEnd) + ThrowBadImageFormatException(); + + long value = 0; + + byte val = *stream; + if ((val & 31) != 31) + { + value = DecodeSigned(ref stream, streamEnd); + } + else if ((val & 32) == 0) + { + stream += 1; + value = (long)ReadUInt64(ref stream); + } + else + { + ThrowBadImageFormatException(); + return 0; + } + + return value; + } + + public static void SkipInteger(ref byte* stream) + { + byte val = *stream; + if ((val & 1) == 0) + { + stream += 1; + } + else if ((val & 2) == 0) + { + stream += 2; + } + else if ((val & 4) == 0) + { + stream += 3; + } + else if ((val & 8) == 0) + { + stream += 4; + } + else if ((val & 16) == 0) + { + stream += 5; + } + else if ((val & 32) == 0) + { + stream += 9; + } + else + { + ThrowBadImageFormatException(); + } + } + } + + internal unsafe partial class NativeReader + { + private readonly byte* _base; + private readonly uint _size; + + public NativeReader(byte* base_, uint size) + { + // Limit the maximum blob size to prevent buffer overruns triggered by boundary integer overflows + if (size >= uint.MaxValue / 4) + ThrowBadImageFormatException(); + + Debug.Assert(base_ <= base_ + size); + + _base = base_; + _size = size; + } + + public uint Size + { + get + { + return _size; + } + } + + public uint AddressToOffset(IntPtr address) + { + Debug.Assert((byte*)address >= _base); + Debug.Assert((byte*)address <= _base + _size); + return (uint)((byte*)address - _base); + } + + public IntPtr OffsetToAddress(uint offset) + { + Debug.Assert(offset < _size); + + return new IntPtr(_base + offset); + } + + public void ThrowBadImageFormatException() + { + Debug.Assert(false); + throw new BadImageFormatException(); + } + + private uint EnsureOffsetInRange(uint offset, uint lookAhead) + { + if ((int)offset < 0 || offset + lookAhead >= _size) + ThrowBadImageFormatException(); + return offset; + } + + public byte ReadUInt8(uint offset) + { + EnsureOffsetInRange(offset, 0); + byte* data = _base + offset; + return NativePrimitiveDecoder.ReadUInt8(ref data); + } + + public ushort ReadUInt16(uint offset) + { + EnsureOffsetInRange(offset, 1); + byte* data = _base + offset; + return NativePrimitiveDecoder.ReadUInt16(ref data); + } + + public uint ReadUInt32(uint offset) + { + EnsureOffsetInRange(offset, 3); + byte* data = _base + offset; + return NativePrimitiveDecoder.ReadUInt32(ref data); + } + + public ulong ReadUInt64(uint offset) + { + EnsureOffsetInRange(offset, 7); + byte* data = _base + offset; + return NativePrimitiveDecoder.ReadUInt64(ref data); + } + + public unsafe float ReadFloat(uint offset) + { + EnsureOffsetInRange(offset, 3); + byte* data = _base + offset; + return NativePrimitiveDecoder.ReadFloat(ref data); + } + + public double ReadDouble(uint offset) + { + EnsureOffsetInRange(offset, 7); + byte* data = _base + offset; + return NativePrimitiveDecoder.ReadDouble(ref data); + } + + public uint DecodeUnsigned(uint offset, out uint value) + { + EnsureOffsetInRange(offset, 0); + + byte* data = _base + offset; + value = NativePrimitiveDecoder.DecodeUnsigned(ref data, _base + _size); + return (uint)(data - _base); + } + + public uint DecodeSigned(uint offset, out int value) + { + EnsureOffsetInRange(offset, 0); + + byte* data = _base + offset; + value = NativePrimitiveDecoder.DecodeSigned(ref data, _base + _size); + return (uint)(data - _base); + } + + public uint DecodeUnsignedLong(uint offset, out ulong value) + { + EnsureOffsetInRange(offset, 0); + + byte* data = _base + offset; + value = NativePrimitiveDecoder.DecodeUnsignedLong(ref data, _base + _size); + return (uint)(data - _base); + } + + public uint DecodeSignedLong(uint offset, out long value) + { + EnsureOffsetInRange(offset, 0); + + byte* data = _base + offset; + value = NativePrimitiveDecoder.DecodeSignedLong(ref data, _base + _size); + return (uint)(data - _base); + } + + public uint SkipInteger(uint offset) + { + EnsureOffsetInRange(offset, 0); + + byte* data = _base + offset; + NativePrimitiveDecoder.SkipInteger(ref data); + return (uint)(data - _base); + } + } + + internal partial struct NativeParser + { + private readonly NativeReader _reader; + private uint _offset; + + public NativeParser(NativeReader reader, uint offset) + { + _reader = reader; + _offset = offset; + } + + public bool IsNull + { + get + { + return _reader == null; + } + } + + public NativeReader Reader + { + get + { + return _reader; + } + } + + public uint Offset + { + get + { + return _offset; + } + set + { + Debug.Assert(value < _reader.Size); + _offset = value; + } + } + + public void ThrowBadImageFormatException() + { + _reader.ThrowBadImageFormatException(); + } + + public byte GetUInt8() + { + byte val = _reader.ReadUInt8(_offset); + _offset += 1; + return val; + } + + public uint GetUnsigned() + { + uint value; + _offset = _reader.DecodeUnsigned(_offset, out value); + return value; + } + + public ulong GetUnsignedLong() + { + ulong value; + _offset = _reader.DecodeUnsignedLong(_offset, out value); + return value; + } + + public int GetSigned() + { + int value; + _offset = _reader.DecodeSigned(_offset, out value); + return value; + } + + public uint GetRelativeOffset() + { + uint pos = _offset; + + int delta; + _offset = _reader.DecodeSigned(_offset, out delta); + + return pos + (uint)delta; + } + + public void SkipInteger() + { + _offset = _reader.SkipInteger(_offset); + } + + public NativeParser GetParserFromRelativeOffset() + { + return new NativeParser(_reader, GetRelativeOffset()); + } + + public uint GetSequenceCount() + { + return GetUnsigned(); + } + } + + internal struct NativeHashtable + { + private NativeReader _reader; + private uint _baseOffset; + private uint _bucketMask; + private byte _entryIndexSize; + + public NativeHashtable(NativeParser parser) + { + uint header = parser.GetUInt8(); + + _reader = parser.Reader; + _baseOffset = parser.Offset; + + int numberOfBucketsShift = (int)(header >> 2); + if (numberOfBucketsShift > 31) + _reader.ThrowBadImageFormatException(); + _bucketMask = (uint)((1 << numberOfBucketsShift) - 1); + + byte entryIndexSize = (byte)(header & 3); + if (entryIndexSize > 2) + _reader.ThrowBadImageFormatException(); + _entryIndexSize = entryIndexSize; + } + + public bool IsNull { get { return _reader == null; } } + + // + // The enumerator does not conform to the regular C# enumerator pattern to avoid paying + // its performance penalty (allocation, multiple calls per iteration) + // + public struct Enumerator + { + private NativeParser _parser; + private uint _endOffset; + private byte _lowHashcode; + + internal Enumerator(NativeParser parser, uint endOffset, byte lowHashcode) + { + _parser = parser; + _endOffset = endOffset; + _lowHashcode = lowHashcode; + } + + public NativeParser GetNext() + { + while (_parser.Offset < _endOffset) + { + byte lowHashcode = _parser.GetUInt8(); + + if (lowHashcode == _lowHashcode) + { + return _parser.GetParserFromRelativeOffset(); + } + + // The entries are sorted by hashcode within the bucket. It allows us to terminate the lookup prematurely. + if (lowHashcode > _lowHashcode) + { + _endOffset = _parser.Offset; // Ensure that extra call to GetNext returns null parser again + break; + } + + _parser.SkipInteger(); + } + + return default; + } + } + + public struct AllEntriesEnumerator + { + private NativeHashtable _table; + private NativeParser _parser; + private uint _currentBucket; + private uint _endOffset; + + internal AllEntriesEnumerator(NativeHashtable table) + { + _table = table; + _currentBucket = 0; + _parser = _table.GetParserForBucket(_currentBucket, out _endOffset); + } + + public NativeParser GetNext() + { + for (;;) + { + while (_parser.Offset < _endOffset) + { + byte lowHashcode = _parser.GetUInt8(); + return _parser.GetParserFromRelativeOffset(); + } + + if (_currentBucket >= _table._bucketMask) + return default; + + _currentBucket++; + _parser = _table.GetParserForBucket(_currentBucket, out _endOffset); + } + } + } + + private NativeParser GetParserForBucket(uint bucket, out uint endOffset) + { + uint start, end; + + if (_entryIndexSize == 0) + { + uint bucketOffset = _baseOffset + bucket; + start = _reader.ReadUInt8(bucketOffset); + end = _reader.ReadUInt8(bucketOffset + 1); + } + else if (_entryIndexSize == 1) + { + uint bucketOffset = _baseOffset + 2 * bucket; + start = _reader.ReadUInt16(bucketOffset); + end = _reader.ReadUInt16(bucketOffset + 2); + } + else + { + uint bucketOffset = _baseOffset + 4 * bucket; + start = _reader.ReadUInt32(bucketOffset); + end = _reader.ReadUInt32(bucketOffset + 4); + } + + endOffset = end + _baseOffset; + return new NativeParser(_reader, _baseOffset + start); + } + + // The recommended code pattern to perform lookup is: + // + // var lookup = t.Lookup(TypeHashingAlgorithms.ComputeGenericInstanceHashCode(genericTypeDefinitionHandle, genericTypeArgumentHandles)); + // NativeParser typeParser; + // while (!(typeParser = lookup.GetNext()).IsNull) + // { + // typeParser.GetTypeSignatureKind(out index); + // ... create RuntimeTypeHandle from the external reference RVAs at [index] + // ... compare if RuntimeTypeHandle is an instance of pair (genericTypeDefinitionHandle, genericTypeArgumentHandles) + // } + // + public Enumerator Lookup(int hashcode) + { + uint endOffset; + uint bucket = ((uint)hashcode >> 8) & _bucketMask; + NativeParser parser = GetParserForBucket(bucket, out endOffset); + + return new Enumerator(parser, endOffset, (byte)hashcode); + } + + public AllEntriesEnumerator EnumerateAllEntries() + { + return new AllEntriesEnumerator(this); + } + } +} diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriterExtensions.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriterExtensions.cs new file mode 100644 index 00000000000000..6741cdb12e2f68 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriterExtensions.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; + +namespace Internal.NativeFormat +{ +#if NATIVEFORMAT_PUBLICWRITER + public +#else + internal +#endif + static class NativeFormatWriterExtensions + { + public static byte[] Save(this NativeWriter writer) + { + MemoryStream ms = new MemoryStream(); + writer.Save(ms); + return ms.ToArray(); + } + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/CanonTypeKind.cs b/src/coreclr/tools/Common/Internal/Runtime/CanonTypeKind.cs new file mode 100644 index 00000000000000..661725af693987 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/CanonTypeKind.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.Runtime +{ + public enum CanonTypeKind + { + NormalCanon, + UniversalCanon, + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/EETypeBuilderHelpers.cs b/src/coreclr/tools/Common/Internal/Runtime/EETypeBuilderHelpers.cs new file mode 100644 index 00000000000000..dd34d85aaa5697 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/EETypeBuilderHelpers.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.TypeSystem; + +namespace Internal.Runtime +{ + internal static class EETypeBuilderHelpers + { + private static EETypeElementType ComputeEETypeElementType(TypeDesc type) + { + // Enums are represented as their underlying type + type = type.UnderlyingType; + + if (type.IsWellKnownType(WellKnownType.Array)) + { + // SystemArray is a special EETypeElementType that doesn't exist in TypeFlags + return EETypeElementType.SystemArray; + } + else + { + // The rest of TypeFlags should be directly castable to EETypeElementType. + // Spot check the enums match. + Debug.Assert((int)TypeFlags.Void == (int)EETypeElementType.Void); + Debug.Assert((int)TypeFlags.IntPtr == (int)EETypeElementType.IntPtr); + Debug.Assert((int)TypeFlags.Single == (int)EETypeElementType.Single); + Debug.Assert((int)TypeFlags.UInt32 == (int)EETypeElementType.UInt32); + Debug.Assert((int)TypeFlags.Pointer == (int)EETypeElementType.Pointer); + Debug.Assert((int)TypeFlags.Array == (int)EETypeElementType.Array); + + EETypeElementType elementType = (EETypeElementType)type.Category; + + // Would be surprising to get these here though. + Debug.Assert(elementType != EETypeElementType.SystemArray); + Debug.Assert(elementType <= EETypeElementType.Pointer); + + return elementType; + + } + } + + public static ushort ComputeFlags(TypeDesc type) + { + ushort flags = type.IsParameterizedType ? + (ushort)EETypeKind.ParameterizedEEType : (ushort)EETypeKind.CanonicalEEType; + + // The top 5 bits of flags are used to convey enum underlying type, primitive type, or mark the type as being System.Array + EETypeElementType elementType = ComputeEETypeElementType(type); + flags |= (ushort)((ushort)elementType << (ushort)EETypeFlags.ElementTypeShift); + + if (type.IsGenericDefinition) + { + flags |= (ushort)EETypeKind.GenericTypeDefEEType; + + // Generic type definition EETypes don't set the other flags. + return flags; + } + + if (type.HasFinalizer) + { + flags |= (ushort)EETypeFlags.HasFinalizerFlag; + } + + if (type.IsDefType + && !type.IsCanonicalSubtype(CanonicalFormKind.Universal) + && ((DefType)type).ContainsGCPointers) + { + flags |= (ushort)EETypeFlags.HasPointersFlag; + } + else if (type.IsArray && !type.IsCanonicalSubtype(CanonicalFormKind.Universal)) + { + var arrayElementType = ((ArrayType)type).ElementType; + if ((arrayElementType.IsValueType && ((DefType)arrayElementType).ContainsGCPointers) || arrayElementType.IsGCPointer) + { + flags |= (ushort)EETypeFlags.HasPointersFlag; + } + } + + if (type.HasInstantiation) + { + flags |= (ushort)EETypeFlags.IsGenericFlag; + + if (type.HasVariance) + { + flags |= (ushort)EETypeFlags.GenericVarianceFlag; + } + } + + return flags; + } + + // These masks and paddings have been chosen so that the ValueTypePadding field can always fit in a byte of data + // if the alignment is 8 bytes or less. If the alignment is higher then there may be a need for more bits to hold + // the rest of the padding data. + // If paddings of greater than 7 bytes are necessary, then the high bits of the field represent that padding + private const uint ValueTypePaddingLowMask = 0x7; + private const uint ValueTypePaddingHighMask = 0xFFFFFF00; + private const uint ValueTypePaddingMax = 0x07FFFFFF; + private const int ValueTypePaddingHighShift = 8; + private const uint ValueTypePaddingAlignmentMask = 0xF8; + private const int ValueTypePaddingAlignmentShift = 3; + + /// + /// Compute the encoded value type padding and alignment that are stored as optional fields on an + /// MethodTable. This padding as added to naturally align value types when laid out as fields + /// of objects on the GCHeap. The amount of padding is recorded to allow unboxing to locals / + /// arrays of value types which don't need it. + /// + internal static uint ComputeValueTypeFieldPaddingFieldValue(uint padding, uint alignment, int targetPointerSize) + { + // For the default case, return 0 + if ((padding == 0) && (alignment == targetPointerSize)) + return 0; + + uint alignmentLog2 = 0; + Debug.Assert(alignment != 0); + + while ((alignment & 1) == 0) + { + alignmentLog2++; + alignment = alignment >> 1; + } + Debug.Assert(alignment == 1); + + Debug.Assert(ValueTypePaddingMax >= padding); + + // Our alignment values here are adjusted by one to allow for a default of 0 (which represents pointer alignment) + alignmentLog2++; + + uint paddingLowBits = padding & ValueTypePaddingLowMask; + uint paddingHighBits = ((padding & ~ValueTypePaddingLowMask) >> ValueTypePaddingAlignmentShift) << ValueTypePaddingHighShift; + uint alignmentLog2Bits = alignmentLog2 << ValueTypePaddingAlignmentShift; + Debug.Assert((alignmentLog2Bits & ~ValueTypePaddingAlignmentMask) == 0); + return paddingLowBits | paddingHighBits | alignmentLog2Bits; + } + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/EETypeOptionalFieldsBuilder.cs b/src/coreclr/tools/Common/Internal/Runtime/EETypeOptionalFieldsBuilder.cs new file mode 100644 index 00000000000000..6481e6ef32cf50 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/EETypeOptionalFieldsBuilder.cs @@ -0,0 +1,150 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using Internal.Runtime; +using Internal.NativeFormat; +using System.Diagnostics; +using System.Text; + +namespace Internal.Runtime +{ + internal unsafe partial class EETypeOptionalFieldsBuilder + { + private NativePrimitiveEncoder _encoder; + private OptionalField[] _rgFields = new OptionalField[(int)EETypeOptionalFieldTag.Count]; + + private struct OptionalField + { + internal bool _fieldPresent; + internal UInt32 _value; + } + + internal EETypeOptionalFieldsBuilder() { } + + internal UInt32 GetFieldValue(EETypeOptionalFieldTag eTag, UInt32 defaultValueIfNotFound) + { + return _rgFields[(int)eTag]._fieldPresent ? _rgFields[(int)eTag]._value : defaultValueIfNotFound; + } + + internal void SetFieldValue(EETypeOptionalFieldTag eTag, UInt32 value) + { + _rgFields[(int)eTag]._fieldPresent = true; + _rgFields[(int)eTag]._value = value; + } + + internal void ClearField(EETypeOptionalFieldTag eTag) + { + _rgFields[(int)eTag]._fieldPresent = false; + } + + private int Encode() + { + EETypeOptionalFieldTag eLastTag = EETypeOptionalFieldTag.Count; + + for (EETypeOptionalFieldTag eTag = 0; eTag < EETypeOptionalFieldTag.Count; eTag++) + eLastTag = _rgFields[(int)eTag]._fieldPresent ? eTag : eLastTag; + + if (eLastTag == EETypeOptionalFieldTag.Count) + return 0; + + _encoder = new NativePrimitiveEncoder(); + _encoder.Init(); + + for (EETypeOptionalFieldTag eTag = 0; eTag < EETypeOptionalFieldTag.Count; eTag++) + { + if (!_rgFields[(int)eTag]._fieldPresent) + continue; + + _encoder.WriteByte((byte)((byte)eTag | (eTag == eLastTag ? 0x80 : 0))); + _encoder.WriteUnsigned(_rgFields[(int)eTag]._value); + } + + return _encoder.Size; + } + + public byte[] GetBytes() + { + Debug.Assert(IsAtLeastOneFieldUsed()); + if (_encoder.Size == 0) + { + Encode(); + } + + return _encoder.GetBytes(); + } + + public bool IsAtLeastOneFieldUsed() + { + for (int i = 0; i < (int)EETypeOptionalFieldTag.Count; i++) + { + if (_rgFields[i]._fieldPresent) + return true; + } + + return false; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < (int)EETypeOptionalFieldTag.Count; i++) + { + if (_rgFields[i]._fieldPresent) + { + sb.Append(_rgFields[i]._value.ToStringInvariant()); + } + else + { + sb.Append("x"); + } + + + if (i != (int)EETypeOptionalFieldTag.Count - 1) + { + sb.Append("_"); + } + } + + return sb.ToString(); + } + + public override bool Equals(object obj) + { + if (obj == null) + return false; + + if (!(obj is EETypeOptionalFieldsBuilder)) + return false; + + EETypeOptionalFieldsBuilder other = obj as EETypeOptionalFieldsBuilder; + + if (ReferenceEquals(this, other)) + return true; + + for (EETypeOptionalFieldTag eTag = 0; eTag < EETypeOptionalFieldTag.Count; eTag++) + { + int index = (int)eTag; + if (_rgFields[index]._fieldPresent != other._rgFields[index]._fieldPresent || + (_rgFields[index]._fieldPresent && _rgFields[index]._value != other._rgFields[index]._value)) + return false; + } + + return true; + } + + public override int GetHashCode() + { + int hash = 31; + + for (EETypeOptionalFieldTag eTag = 0; eTag < EETypeOptionalFieldTag.Count; eTag++) + { + hash = hash * 486187739 + (int)GetFieldValue(eTag, 0); + } + + return hash; + } + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/GCDescEncoder.cs b/src/coreclr/tools/Common/Internal/Runtime/GCDescEncoder.cs new file mode 100644 index 00000000000000..c842e61164ccf3 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/GCDescEncoder.cs @@ -0,0 +1,219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime +{ + /// + /// Utility class for encoding GCDescs. GCDesc is a runtime structure used by the + /// garbage collector that describes the GC layout of a memory region. + /// + public struct GCDescEncoder + { + /// + /// Retrieves size of the GCDesc that describes the instance GC layout for the given type. + /// + public static int GetGCDescSize(TypeDesc type) + { + if (type.IsArray) + { + TypeDesc elementType = ((ArrayType)type).ElementType; + if (elementType.IsGCPointer) + { + // For efficiency this is special cased and encoded as one serie. + return 3 * type.Context.Target.PointerSize; + } + else if (elementType.IsDefType) + { + var defType = (DefType)elementType; + if (defType.ContainsGCPointers) + { + GCPointerMap pointerMap = GCPointerMap.FromInstanceLayout(defType); + if (pointerMap.IsAllGCPointers) + { + // For efficiency this is special cased and encoded as one serie. + return 3 * type.Context.Target.PointerSize; + } + else + { + int numSeries = pointerMap.NumSeries; + Debug.Assert(numSeries > 0); + return (numSeries + 2) * type.Context.Target.PointerSize; + } + } + } + } + else + { + var defType = (DefType)type; + if (defType.ContainsGCPointers) + { + int numSeries = GCPointerMap.FromInstanceLayout(defType).NumSeries; + Debug.Assert(numSeries > 0); + return (numSeries * 2 + 1) * type.Context.Target.PointerSize; + } + } + + return 0; + } + + public static void EncodeGCDesc(ref T builder, TypeDesc type) + where T : struct, ITargetBinaryWriter + { + int initialBuilderPosition = builder.CountBytes; + + if (type.IsArray) + { + TypeDesc elementType = ((ArrayType)type).ElementType; + + // 2 means m_pEEType and _numComponents. Syncblock is sort of appended at the end of the object layout in this case. + int baseSize = 2 * builder.TargetPointerSize; + + if (type.IsMdArray) + { + // Multi-dim arrays include upper and lower bounds for each rank + baseSize += 2 * sizeof(int) * ((ArrayType)type).Rank; + } + + if (elementType.IsGCPointer) + { + EncodeAllGCPointersArrayGCDesc(ref builder, baseSize); + } + else if (elementType.IsDefType) + { + var elementDefType = (DefType)elementType; + if (elementDefType.ContainsGCPointers) + { + GCPointerMap pointerMap = GCPointerMap.FromInstanceLayout(elementDefType); + if (pointerMap.IsAllGCPointers) + { + EncodeAllGCPointersArrayGCDesc(ref builder, baseSize); + } + else + { + EncodeArrayGCDesc(ref builder, pointerMap, baseSize); + } + } + } + } + else + { + var defType = (DefType)type; + if (defType.ContainsGCPointers) + { + // Computing the layout for the boxed version if this is a value type. + int offs = defType.IsValueType ? builder.TargetPointerSize : 0; + + // Include syncblock + int objectSize = defType.InstanceByteCount.AsInt + offs + builder.TargetPointerSize; + + EncodeStandardGCDesc(ref builder, GCPointerMap.FromInstanceLayout(defType), objectSize, offs); + } + } + + Debug.Assert(initialBuilderPosition + GetGCDescSize(type) == builder.CountBytes); + } + + public static void EncodeStandardGCDesc(ref T builder, GCPointerMap map, int size, int delta) + where T : struct, ITargetBinaryWriter + { + Debug.Assert(size >= map.Size); + + int pointerSize = builder.TargetPointerSize; + + int numSeries = 0; + + for (int cellIndex = map.Size - 1; cellIndex >= 0; cellIndex--) + { + if (map[cellIndex]) + { + numSeries++; + + int seriesSize = pointerSize; + + while (cellIndex > 0 && map[cellIndex - 1]) + { + seriesSize += pointerSize; + cellIndex--; + } + + builder.EmitNaturalInt(seriesSize - size); + builder.EmitNaturalInt(cellIndex * pointerSize + delta); + } + } + + Debug.Assert(numSeries > 0); + builder.EmitNaturalInt(numSeries); + } + + // Arrays of all GC references are encoded as special kind of GC desc for efficiency + private static void EncodeAllGCPointersArrayGCDesc(ref T builder, int baseSize) + where T : struct, ITargetBinaryWriter + { + // Construct the gc info as if this array contains exactly one pointer + // - the encoding trick where the size of the series is measured as a difference from + // total object size will make this work for arbitrary array lengths + + // Series size + builder.EmitNaturalInt(-(baseSize + builder.TargetPointerSize)); + // Series offset + builder.EmitNaturalInt(baseSize); + // NumSeries + builder.EmitNaturalInt(1); + } + + private static void EncodeArrayGCDesc(ref T builder, GCPointerMap map, int baseSize) + where T : struct, ITargetBinaryWriter + { + // NOTE: This format cannot properly represent element types with sizes >= 64k bytes. + // We guard it with an assert, but it is the responsibility of the code using this + // to cap array component sizes. MethodTable only has a UInt16 field for component sizes too. + + int numSeries = 0; + int leadingNonPointerCount = 0; + + int pointerSize = builder.TargetPointerSize; + + for (int cellIndex = 0; cellIndex < map.Size && !map[cellIndex]; cellIndex++) + { + leadingNonPointerCount++; + } + + int nonPointerCount = leadingNonPointerCount; + + for (int cellIndex = map.Size - 1; cellIndex >= leadingNonPointerCount; cellIndex--) + { + if (map[cellIndex]) + { + numSeries++; + + int pointerCount = 1; + while (cellIndex > leadingNonPointerCount && map[cellIndex - 1]) + { + cellIndex--; + pointerCount++; + } + + Debug.Assert(pointerCount < 64 * 1024); + builder.EmitHalfNaturalInt((short)pointerCount); + + Debug.Assert(nonPointerCount * pointerSize < 64 * 1024); + builder.EmitHalfNaturalInt((short)(nonPointerCount * pointerSize)); + + nonPointerCount = 0; + } + else + { + nonPointerCount++; + } + } + + Debug.Assert(numSeries > 0); + builder.EmitNaturalInt(baseSize + leadingNonPointerCount * pointerSize); + builder.EmitNaturalInt(-numSeries); + } + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/ITargetBinaryWriter.cs b/src/coreclr/tools/Common/Internal/Runtime/ITargetBinaryWriter.cs new file mode 100644 index 00000000000000..58920e42ec17ae --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/ITargetBinaryWriter.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.Runtime +{ + /// + /// Abstraction to emit byte streams of data in a format expected by the target platform. + /// + public interface ITargetBinaryWriter + { + /// + /// Gets the number of bytes written. + /// + int CountBytes { get; } + + /// + /// Gets the size, in bytes, of a pointer on the target architecture. + /// + int TargetPointerSize { get; } + + /// + /// Emits an integer that has a natural size on the target platform (e.g. 64 bits on 64 bit platforms). + /// + void EmitNaturalInt(int emit); + + /// + /// Emits an integer that has half of the natural size on the target platform (e.g. 32 bits on 64 bit platforms). + /// + void EmitHalfNaturalInt(short emit); + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/InterfaceDispatchCellCachePointerFlags.cs b/src/coreclr/tools/Common/Internal/Runtime/InterfaceDispatchCellCachePointerFlags.cs new file mode 100644 index 00000000000000..a8fb3254fc2c00 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/InterfaceDispatchCellCachePointerFlags.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Internal.Runtime +{ + // + // The low 2 bits of the interface dispatch cell's cache pointer are treated specially so that we can avoid the + // need for extra fields on the type. + // + // Keep these in sync with the native copy in src\Native\Runtime\inc\rhbinder.h + // + public enum InterfaceDispatchCellCachePointerFlags + { + CachePointerPointsAtCache = 0x0, + CachePointerIsInterfacePointerOrMetadataToken = 0x1, + CachePointerIsIndirectedInterfaceRelativePointer = 0x2, + CachePointerIsInterfaceRelativePointer = 0x3, + CachePointerMask = 0x3, + CachePointerMaskShift = 0x2, + MaxVTableOffsetPlusOne = 0x1000 + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/InteropConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/InteropConstants.cs new file mode 100644 index 00000000000000..1cddd12859e629 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/InteropConstants.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.Runtime +{ + internal static class InteropDataConstants + { + /// + /// Flag set in RuntimeInteropData if the entry is for type with marshallers + /// + public const int HasMarshallers = 0x1; + + /// + /// Flag set in RuntimeInteropData if the entry is for type with invalid interop layout + /// + public const int HasInvalidLayout = 0x2; + + /// + /// Shift used to encode field count + /// + public const int FieldCountShift = 2; + + /// + /// Flag set in ModuleFixupCell if DllImportSearchPath is specified for the DllImport. + /// + public const uint HasDllImportSearchPath = 0x80000000; + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/MappingTableFlags.cs b/src/coreclr/tools/Common/Internal/Runtime/MappingTableFlags.cs new file mode 100644 index 00000000000000..189be63698bcd1 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/MappingTableFlags.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Internal.Runtime +{ + internal struct DynamicInvokeMapEntry + { + public const uint IsImportMethodFlag = 0x40000000; + public const uint InstantiationDetailIndexMask = 0x3FFFFFFF; + } + + internal struct VirtualInvokeTableEntry + { + public const int GenericVirtualMethod = 1; + public const int FlagsMask = 1; + } + + [Flags] + public enum InvokeTableFlags : uint + { + HasVirtualInvoke = 0x00000001, + IsGenericMethod = 0x00000002, + HasMetadataHandle = 0x00000004, + IsDefaultConstructor = 0x00000008, + RequiresInstArg = 0x00000010, + HasEntrypoint = 0x00000020, + IsUniversalCanonicalEntry = 0x00000040, + NeedsParameterInterpretation = 0x00000080, + CallingConventionDefault = 0x00000000, + Cdecl = 0x00001000, + Winapi = 0x00002000, + StdCall = 0x00003000, + ThisCall = 0x00004000, + FastCall = 0x00005000, + CallingConventionMask = 0x00007000, + } + + [Flags] + public enum FieldTableFlags : uint + { + Instance = 0x00, + NonGCStatic = 0x01, + GCStatic = 0x02, + ThreadStatic = 0x03, + + StorageClass = 0x03, + + IsUniversalCanonicalEntry = 0x04, + HasMetadataHandle = 0x08, + FieldOffsetEncodedDirectly = 0x20, + IsAnyCanonicalEntry = 0x40, + IsInitOnly = 0x80 + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs b/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs new file mode 100644 index 00000000000000..e324885edee59a --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.Runtime +{ + internal enum ReflectionMapBlob + { + TypeMap = 1, + ArrayMap = 2, + GenericInstanceMap = 3, // unused + GenericParameterMap = 4, // unused + BlockReflectionTypeMap = 5, + InvokeMap = 6, + VirtualInvokeMap = 7, + CommonFixupsTable = 8, + FieldAccessMap = 9, + CCtorContextMap = 10, + DiagGenericInstanceMap = 11, // unused + DiagGenericParameterMap = 12, // unused + EmbeddedMetadata = 13, + DefaultConstructorMap = 14, + UnboxingAndInstantiatingStubMap = 15, + StructMarshallingStubMap = 16, + DelegateMarshallingStubMap = 17, + GenericVirtualMethodTable = 18, + InterfaceGenericVirtualMethodTable = 19, + + // Reflection template types/methods blobs: + TypeTemplateMap = 21, + GenericMethodsTemplateMap = 22, + DynamicInvokeTemplateData = 23, + BlobIdResourceIndex = 24, + BlobIdResourceData = 25, + BlobIdStackTraceEmbeddedMetadata = 26, + BlobIdStackTraceMethodRvaToTokenMapping = 27, + + //Native layout blobs: + NativeLayoutInfo = 30, + NativeReferences = 31, + GenericsHashtable = 32, + NativeStatics = 33, + StaticsInfoHashtable = 34, + GenericMethodsHashtable = 35, + ExactMethodInstantiationsHashtable = 36, + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs b/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs new file mode 100644 index 00000000000000..1798b7ad1a04fd --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs @@ -0,0 +1,289 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Internal.Runtime +{ + /// + /// Represents the flags stored in the _usFlags field of a System.Runtime.MethodTable. + /// + [Flags] + internal enum EETypeFlags : ushort + { + /// + /// There are four kinds of EETypes, defined in Kinds. + /// + EETypeKindMask = 0x0003, + + /// + /// This flag is set when m_RelatedType is in a different module. In that case, _pRelatedType + /// actually points to an IAT slot in this module, which then points to the desired MethodTable in the + /// other module. In other words, there is an extra indirection through m_RelatedType to get to + /// the related type in the other module. When this flag is set, it is expected that you use the + /// "_ppXxxxViaIAT" member of the RelatedTypeUnion for the particular related type you're + /// accessing. + /// + RelatedTypeViaIATFlag = 0x0004, + + /// + /// This type was dynamically allocated at runtime. + /// + IsDynamicTypeFlag = 0x0008, + + /// + /// This MethodTable represents a type which requires finalization. + /// + HasFinalizerFlag = 0x0010, + + /// + /// This type contain GC pointers. + /// + HasPointersFlag = 0x0020, + + /// + /// This type implements IDynamicInterfaceCastable to allow dynamic resolution of interface casts. + /// + IDynamicInterfaceCastableFlag = 0x0040, + + /// + /// This type is generic and one or more of its type parameters is co- or contra-variant. This + /// only applies to interface and delegate types. + /// + GenericVarianceFlag = 0x0080, + + /// + /// This type has optional fields present. + /// + OptionalFieldsFlag = 0x0100, + + // Unused = 0x0200, + + /// + /// This type is generic. + /// + IsGenericFlag = 0x0400, + + /// + /// We are storing a EETypeElementType in the upper bits for unboxing enums. + /// + ElementTypeMask = 0xf800, + ElementTypeShift = 11, + + /// + /// Single mark to check TypeKind and two flags. When non-zero, casting is more complicated. + /// + ComplexCastingMask = EETypeKindMask | RelatedTypeViaIATFlag | GenericVarianceFlag + }; + + internal enum EETypeKind : ushort + { + /// + /// Represents a standard ECMA type + /// + CanonicalEEType = 0x0000, + + /// + /// Represents a type cloned from another MethodTable + /// + ClonedEEType = 0x0001, + + /// + /// Represents a parameterized type. For example a single dimensional array or pointer type + /// + ParameterizedEEType = 0x0002, + + /// + /// Represents an uninstantiated generic type definition + /// + GenericTypeDefEEType = 0x0003, + } + + /// + /// These are flag values that are rarely set for types. If any of them are set then an optional field will + /// be associated with the MethodTable to represent them. + /// + [Flags] + internal enum EETypeRareFlags : int + { + /// + /// This type requires 8-byte alignment for its fields on certain platforms (only ARM currently). + /// + RequiresAlign8Flag = 0x00000001, + + // UNUSED1 = 0x00000002, + + // UNUSED = 0x00000004, + + // UNUSED = 0x00000008, + + // UNUSED = 0x00000010, + + /// + /// This MethodTable has a Class Constructor + /// + HasCctorFlag = 0x0000020, + + // UNUSED2 = 0x00000040, + + /// + /// This MethodTable was constructed from a universal canonical template, and has + /// its own dynamically created DispatchMap (does not use the DispatchMap of its template type) + /// + HasDynamicallyAllocatedDispatchMapFlag = 0x00000080, + + /// + /// This MethodTable represents a structure that is an HFA + /// + IsHFAFlag = 0x00000100, + + /// + /// This MethodTable has sealed vtable entries + /// + HasSealedVTableEntriesFlag = 0x00000200, + + /// + /// This dynamically created types has gc statics + /// + IsDynamicTypeWithGcStatics = 0x00000400, + + /// + /// This dynamically created types has non gc statics + /// + IsDynamicTypeWithNonGcStatics = 0x00000800, + + /// + /// This dynamically created types has thread statics + /// + IsDynamicTypeWithThreadStatics = 0x00001000, + + /// + /// This MethodTable contains a pointer to dynamic module information + /// + HasDynamicModuleFlag = 0x00002000, + + /// + /// This MethodTable is an abstract class (but not an interface). + /// + IsAbstractClassFlag = 0x00004000, + + /// + /// This MethodTable is for a Byref-like class (TypedReference, Span<T>,...) + /// + IsByRefLikeFlag = 0x00008000, + } + + internal enum EETypeField + { + ETF_InterfaceMap, + ETF_TypeManagerIndirection, + ETF_WritableData, + ETF_Finalizer, + ETF_OptionalFieldsPtr, + ETF_SealedVirtualSlots, + ETF_DynamicTemplateType, + ETF_DynamicDispatchMap, + ETF_DynamicModule, + ETF_GenericDefinition, + ETF_GenericComposition, + ETF_DynamicGcStatics, + ETF_DynamicNonGcStatics, + ETF_DynamicThreadStaticOffset, + } + + // Subset of the managed TypeFlags enum understood by Redhawk. + // This should match the values in the TypeFlags enum except for the special + // entry that marks System.Array specifically. + internal enum EETypeElementType + { + // Primitive + Unknown = 0x00, + Void = 0x01, + Boolean = 0x02, + Char = 0x03, + SByte = 0x04, + Byte = 0x05, + Int16 = 0x06, + UInt16 = 0x07, + Int32 = 0x08, + UInt32 = 0x09, + Int64 = 0x0A, + UInt64 = 0x0B, + IntPtr = 0x0C, + UIntPtr = 0x0D, + Single = 0x0E, + Double = 0x0F, + + ValueType = 0x10, + // Enum = 0x11, // EETypes store enums as their underlying type + Nullable = 0x12, + // Unused 0x13, + + Class = 0x14, + Interface = 0x15, + + SystemArray = 0x16, // System.Array type + + Array = 0x17, + SzArray = 0x18, + ByRef = 0x19, + Pointer = 0x1A, + } + + internal enum EETypeOptionalFieldTag : byte + { + /// + /// Extra MethodTable flags not commonly used such as HasClassConstructor + /// + RareFlags, + + /// + /// Index of the dispatch map pointer in the DispathMap table + /// + DispatchMap, + + /// + /// Padding added to a value type when allocated on the GC heap + /// + ValueTypeFieldPadding, + + /// + /// Offset in Nullable<T> of the value field + /// + NullableValueOffset, + + // Number of field types we support + Count + } + + // Keep this synchronized with GenericVarianceType in rhbinder.h. + internal enum GenericVariance : byte + { + NonVariant = 0, + Covariant = 1, + Contravariant = 2, + ArrayCovariant = 0x20, + } + + internal static class ParameterizedTypeShapeConstants + { + // NOTE: Parameterized type kind is stored in the BaseSize field of the MethodTable. + // Array types use their actual base size. Pointer and ByRef types are never boxed, + // so we can reuse the MethodTable BaseSize field to indicate the kind. + // It's important that these values always stay lower than any valid value of a base + // size for an actual array. + public const int Pointer = 0; + public const int ByRef = 1; + } + + internal static class StringComponentSize + { + public const int Value = sizeof(char); + } + + internal static class WritableData + { + public static int GetSize(int pointerSize) => pointerSize; + public static int GetAlignment(int pointerSize) => pointerSize; + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/RuntimeConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/RuntimeConstants.cs new file mode 100644 index 00000000000000..fc05a42e6435eb --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/RuntimeConstants.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.Runtime +{ + internal static class IndirectionConstants + { + /// + /// Flag set on pointers to indirection cells to distinguish them + /// from pointers to the object directly + /// + public const int IndirectionCellPointer = 0x1; + + /// + /// Flag set on RVAs to indirection cells to distinguish them + /// from RVAs to the object directly + /// + public const uint RVAPointsToIndirection = 0x80000000u; + } + + internal static class GCStaticRegionConstants + { + /// + /// Flag set if the corresponding GCStatic entry has not yet been initialized and + /// the corresponding MethodTable pointer has been changed into a instance pointer of + /// that MethodTable. + /// + public const int Uninitialized = 0x1; + + /// + /// Flag set if the next pointer loc points to GCStaticsPreInitDataNode. + /// Otherise it is the next GCStatic entry. + /// + public const int HasPreInitializedData = 0x2; + + public const int Mask = Uninitialized | HasPreInitializedData; + } + + internal static class ArrayTypesConstants + { + /// + /// Maximum allowable size for array element types. + /// + public const int MaxSizeForValueClassInArray = 0xFFFF; + } + + // keep in sync with GC_ALLOC_FLAGS in gcinterface.h + internal enum GC_ALLOC_FLAGS + { + GC_ALLOC_NO_FLAGS = 0, + GC_ALLOC_ZEROING_OPTIONAL = 16, + GC_ALLOC_PINNED_OBJECT_HEAP = 64, + } + + internal static class SpecialDispatchMapSlot + { + public const ushort Diamond = 0xFFFE; + public const ushort Reabstraction = 0xFFFF; + } + + internal static class SpecialGVMInterfaceEntry + { + public const uint Diamond = 0xFFFFFFFF; + public const uint Reabstraction = 0xFFFFFFFE; + } + + internal enum RuntimeHelperKind + { + AllocateObject, + IsInst, + CastClass, + AllocateArray, + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/UniversalGenericParameterLayout.cs b/src/coreclr/tools/Common/Internal/Runtime/UniversalGenericParameterLayout.cs new file mode 100644 index 00000000000000..d3d576972e6c40 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/UniversalGenericParameterLayout.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.TypeSystem; + +namespace Internal.Runtime +{ + internal static class UniversalGenericParameterLayout + { + private enum HasVarsInvestigationLevel + { + Parameter, + NotParameter + } + + /// + /// IF THESE SEMANTICS EVER CHANGE UPDATE THE LOGIC WHICH DEFINES THIS BEHAVIOR IN + /// THE DYNAMIC TYPE LOADER AS WELL AS THE COMPILER. + /// (There is a version of this in TypeLoaderEnvironment.SignatureParsing.cs that must be kept in sync with this.) + /// + /// Parameter's are considered to have type layout dependent on their generic instantiation + /// if the type of the parameter in its signature is a type variable, or if the type is a generic + /// structure which meets 2 characteristics: + /// 1. Structure size/layout is affected by the size/layout of one or more of its generic parameters + /// 2. One or more of the generic parameters is a type variable, or a generic structure which also recursively + /// would satisfy constraint 2. (Note, that in the recursion case, whether or not the structure is affected + /// by the size/layout of its generic parameters is not investigated.) + /// + /// Examples parameter types, and behavior. + /// + /// T = true + /// List[T] = false + /// StructNotDependentOnArgsForSize[T] = false + /// GenStructDependencyOnArgsForSize[T] = true + /// StructNotDependentOnArgsForSize[GenStructDependencyOnArgsForSize[T]] = true + /// StructNotDependentOnArgsForSize[GenStructDependencyOnArgsForSize[List[T]]]] = false + /// + /// Example non-parameter type behavior + /// T = true + /// List[T] = false + /// StructNotDependentOnArgsForSize[T] = *true* + /// GenStructDependencyOnArgsForSize[T] = true + /// StructNotDependentOnArgsForSize[GenStructDependencyOnArgsForSize[T]] = true + /// StructNotDependentOnArgsForSize[GenStructDependencyOnArgsForSize[List[T]]]] = false + /// + public static bool IsLayoutDependentOnGenericInstantiation(TypeDesc type) + { + return IsLayoutDependentOnGenericInstantiation(type, HasVarsInvestigationLevel.Parameter); + } + + private static bool IsLayoutDependentOnGenericInstantiation(TypeDesc type, HasVarsInvestigationLevel investigationLevel) + { + if (type.IsSignatureVariable) + { + return true; + } + else if (type.HasInstantiation && type.IsValueType) + { + foreach (TypeDesc valueTypeInstantiationParam in type.Instantiation) + { + if (IsLayoutDependentOnGenericInstantiation(valueTypeInstantiationParam, HasVarsInvestigationLevel.NotParameter)) + { + if (investigationLevel == HasVarsInvestigationLevel.Parameter) + { + DefType universalCanonForm = (DefType)type.ConvertToCanonForm(CanonicalFormKind.Universal); + return universalCanonForm.InstanceFieldSize.IsIndeterminate; + } + else + { + return true; + } + } + } + return false; + } + else + { + // All other forms of type do not change their shape dependent on signature variables. + return false; + } + } + + public static bool MethodSignatureHasVarsNeedingCallingConventionConverter(TypeSystem.MethodSignature methodSignature) + { + if (IsLayoutDependentOnGenericInstantiation(methodSignature.ReturnType, HasVarsInvestigationLevel.Parameter)) + return true; + + for (int i = 0; i < methodSignature.Length; i++) + { + if (IsLayoutDependentOnGenericInstantiation(methodSignature[i], HasVarsInvestigationLevel.Parameter)) + return true; + } + + return false; + } + + public static bool VTableMethodRequiresCallingConventionConverter(MethodDesc method) + { + if (!MethodSignatureHasVarsNeedingCallingConventionConverter(method.GetTypicalMethodDefinition().Signature)) + return false; + + MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method).GetCanonMethodTarget(CanonicalFormKind.Specific); + return slotDecl.IsCanonicalMethod(CanonicalFormKind.Universal); + } + } +} diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs index 8c552704c7080f..9ef70518bd0c31 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs @@ -1832,12 +1832,12 @@ static void _getFunctionEntryPoint(IntPtr thisHandle, IntPtr* ppException, CORIN } [UnmanagedCallersOnly] - static void _getFunctionFixedEntryPoint(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* ftn, CORINFO_CONST_LOOKUP* pResult) + static void _getFunctionFixedEntryPoint(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* ftn, byte isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP* pResult) { var _this = GetThis(thisHandle); try { - _this.getFunctionFixedEntryPoint(ftn, ref *pResult); + _this.getFunctionFixedEntryPoint(ftn, isUnsafeFunctionPointer != 0, ref *pResult); } catch (Exception ex) { @@ -2691,7 +2691,7 @@ static IntPtr GetUnmanagedCallbacks() callbacks[120] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; callbacks[121] = (delegate* unmanaged)&_getHelperFtn; callbacks[122] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[123] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[123] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; callbacks[124] = (delegate* unmanaged)&_getMethodSync; callbacks[125] = (delegate* unmanaged)&_getLazyStringLiteralHelper; callbacks[126] = (delegate* unmanaged)&_embedModuleHandle; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs index 50f7783be86547..364b0c1384232e 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs @@ -79,11 +79,6 @@ static IntrinsicHashtable InitializeIntrinsicHashtable() table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Array_Get, "Get", null, null); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Array_Address, "Address", null, null); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Array_Set, "Set", null, null); - table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_RTH_GetValueInternal, "GetValueInternal", "System", "RuntimeTypeHandle"); - table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Object_GetType, "GetType", "System", "Object"); - table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_StubHelpers_GetStubContext, "GetStubContext", "System.StubHelpers", "StubHelpers"); // interop-specific - // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr, "GetStubContextAddr", "System.StubHelpers", "StubHelpers"); // interop-specific - table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_StubHelpers_NextCallReturnAddress, "NextCallReturnAddress", "System.StubHelpers", "StubHelpers"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Ctor, ".ctor", "System", "ByReference`1"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Value, "get_Value", "System", "ByReference`1"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_GetRawHandle, "EETypePtrOf", "System", "EETypePtr"); @@ -134,7 +129,6 @@ private CorInfoIntrinsics getIntrinsicID(MethodDesc method, byte* pMustExpand) return CorInfoIntrinsics.CORINFO_INTRINSIC_Illegal; break; - case CorInfoIntrinsics.CORINFO_INTRINSIC_RTH_GetValueInternal: case CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Ctor: case CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Value: if (pMustExpand != null) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 7cb086495a6108..4377fb6f784b96 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2981,7 +2981,7 @@ private void ThrowExceptionForHelper(ref CORINFO_HELPER_DESC throwHelper) public static CORINFO_OS TargetToOs(TargetDetails target) { - return target.IsWindows ? CORINFO_OS.CORINFO_WINNT : + return target.IsWindows ? CORINFO_OS.CORINFO_WINNT : target.IsOSX ? CORINFO_OS.CORINFO_MACOS : CORINFO_OS.CORINFO_UNIX; } @@ -3156,7 +3156,7 @@ private uint getThreadTLSIndex(ref void* ppIndirection) } } - private void getFunctionFixedEntryPoint(CORINFO_METHOD_STRUCT_* ftn, ref CORINFO_CONST_LOOKUP pResult) + private void getFunctionFixedEntryPoint(CORINFO_METHOD_STRUCT_* ftn, bool isUnsafeFunctionPointer, ref CORINFO_CONST_LOOKUP pResult) { throw new NotImplementedException("getFunctionFixedEntryPoint"); } private CorInfoHelpFunc getLazyStringLiteralHelper(CORINFO_MODULE_STRUCT_* handle) @@ -3380,6 +3380,15 @@ private void allocUnwindInfo(byte* pHotCode, byte* pColdCode, uint startOffset, blobData[i] = pUnwindBlock[i]; } +#if !READYTORUN + var target = _compilation.TypeSystemContext.Target; + + if (target.Architecture == TargetArchitecture.ARM64 && target.OperatingSystem == TargetOS.Linux) + { + blobData = CompressARM64CFI(blobData); + } +#endif + _frameInfos[_usedFrameInfos++] = new FrameInfo(flags, (int)startOffset, (int)endOffset, blobData); } @@ -3820,7 +3829,7 @@ public static void ComputeJitPgoInstrumentationSchema(Func objec instrumentationData = msInstrumentationData.ToArray(); } - private HRESULT getPgoInstrumentationResults(CORINFO_METHOD_STRUCT_* ftnHnd, ref PgoInstrumentationSchema* pSchema, ref uint countSchemaItems, byte** pInstrumentationData, + private HRESULT getPgoInstrumentationResults(CORINFO_METHOD_STRUCT_* ftnHnd, ref PgoInstrumentationSchema* pSchema, ref uint countSchemaItems, byte** pInstrumentationData, ref PgoSource pPgoSource) { MethodDesc methodDesc = HandleToObject(ftnHnd); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index bc6d53514df1d8..4d59d99a771d35 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -442,11 +442,6 @@ public enum CorInfoIntrinsics CORINFO_INTRINSIC_Array_Get, // Get the value of an element in an array CORINFO_INTRINSIC_Array_Address, // Get the address of an element in an array CORINFO_INTRINSIC_Array_Set, // Set the value of an element in an array - CORINFO_INTRINSIC_RTH_GetValueInternal, - CORINFO_INTRINSIC_Object_GetType, - CORINFO_INTRINSIC_StubHelpers_GetStubContext, - CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr, - CORINFO_INTRINSIC_StubHelpers_NextCallReturnAddress, CORINFO_INTRINSIC_ByReference_Ctor, CORINFO_INTRINSIC_ByReference_Value, CORINFO_INTRINSIC_GetRawHandle, diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 76982cd7aaff93..2e62a489400f7b 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -276,7 +276,7 @@ FUNCTIONS int32_t * getAddrOfCaptureThreadGlobal(void **ppIndirection); void* getHelperFtn (CorInfoHelpFunc ftnNum, void **ppIndirection); void getFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP * pResult, CORINFO_ACCESS_FLAGS accessFlags); - void getFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP * pResult); + void getFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, bool isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP * pResult); void* getMethodSync(CORINFO_METHOD_HANDLE ftn, void **ppIndirection); CorInfoHelpFunc getLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle); CORINFO_MODULE_HANDLE embedModuleHandle(CORINFO_MODULE_HANDLE handle, void **ppIndirection); diff --git a/src/coreclr/tools/Common/JitInterface/UnboxingMethodDescFactory.cs b/src/coreclr/tools/Common/JitInterface/UnboxingMethodDescFactory.cs new file mode 100644 index 00000000000000..4f38998f5cb5f0 --- /dev/null +++ b/src/coreclr/tools/Common/JitInterface/UnboxingMethodDescFactory.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Internal.TypeSystem; + +namespace Internal.JitInterface +{ + internal class UnboxingMethodDescFactory : Dictionary + { + public UnboxingMethodDesc GetUnboxingMethod(MethodDesc method) + { + if (!TryGetValue(method, out UnboxingMethodDesc result)) + { + result = new UnboxingMethodDesc(method, this); + Add(method, result); + } + + return result; + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Aot/TargetDetails.Aot.cs b/src/coreclr/tools/Common/TypeSystem/Aot/TargetDetails.Aot.cs new file mode 100644 index 00000000000000..9e9704555724d4 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Aot/TargetDetails.Aot.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem +{ + // Extension to TargetDetails related to Aot + partial class TargetDetails + { + /// + /// Offset by which fat function pointers are shifted to distinguish them + /// from real function pointers. + /// WebAssembly uses index tables, not addresses for function pointers, so the lower bits are not free to use. + /// + public int FatFunctionPointerOffset => Architecture == TargetArchitecture.Wasm32 ? 1 << 31 : 2; + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/CodeGen/INonEmittableType.cs b/src/coreclr/tools/Common/TypeSystem/CodeGen/INonEmittableType.cs new file mode 100644 index 00000000000000..cf302f1fcf1744 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/CodeGen/INonEmittableType.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem +{ + /// + /// Used to mark TypeDesc types that are not part of the core type system + /// that should never be turned into an MethodTable. + /// + public interface INonEmittableType + { } +} diff --git a/src/coreclr/tools/Common/TypeSystem/CodeGen/NativeStructType.CodeGen.cs b/src/coreclr/tools/Common/TypeSystem/CodeGen/NativeStructType.CodeGen.cs new file mode 100644 index 00000000000000..320b8de11b09fc --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/CodeGen/NativeStructType.CodeGen.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem.Interop +{ + // Implements INonEmittableType + partial class NativeStructType : INonEmittableType + { + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Common/Utilities/GCPointerMap.Aot.cs b/src/coreclr/tools/Common/TypeSystem/Common/Utilities/GCPointerMap.Aot.cs new file mode 100644 index 00000000000000..4738cde90c6921 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Common/Utilities/GCPointerMap.Aot.cs @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem +{ + partial struct GCPointerMap + { + /// + /// Computes the GC pointer map of the GC static region of the type. + /// + public static GCPointerMap FromStaticLayout(DefType type) + { + GCPointerMapBuilder builder = new GCPointerMapBuilder(type.GCStaticFieldSize.AsInt, type.Context.Target.PointerSize); + + foreach (FieldDesc field in type.GetFields()) + { + if (!field.IsStatic || field.HasRva || field.IsLiteral + || field.IsThreadStatic || !field.HasGCStaticBase) + continue; + + TypeDesc fieldType = field.FieldType; + if (fieldType.IsGCPointer) + { + builder.MarkGCPointer(field.Offset.AsInt); + } + else + { + Debug.Assert(fieldType.IsValueType); + var fieldDefType = (DefType)fieldType; + if (fieldDefType.ContainsGCPointers) + { + GCPointerMapBuilder innerBuilder = + builder.GetInnerBuilder(field.Offset.AsInt, fieldDefType.InstanceByteCount.AsInt); + FromInstanceLayoutHelper(ref innerBuilder, fieldDefType); + } + } + } + + Debug.Assert(builder.ToGCMap().Size * type.Context.Target.PointerSize >= type.GCStaticFieldSize.AsInt); + return builder.ToGCMap(); + } + + /// + /// Computes the GC pointer map of the thread static region of the type. + /// + public static GCPointerMap FromThreadStaticLayout(DefType type) + { + GCPointerMapBuilder builder = new GCPointerMapBuilder(type.ThreadGcStaticFieldSize.AsInt, type.Context.Target.PointerSize); + + foreach (FieldDesc field in type.GetFields()) + { + if (!field.IsStatic || field.HasRva || field.IsLiteral || !field.IsThreadStatic || !field.HasGCStaticBase) + continue; + + TypeDesc fieldType = field.FieldType; + if (fieldType.IsGCPointer) + { + builder.MarkGCPointer(field.Offset.AsInt); + } + else if (fieldType.IsValueType) + { + var fieldDefType = (DefType)fieldType; + if (fieldDefType.ContainsGCPointers) + { + GCPointerMapBuilder innerBuilder = + builder.GetInnerBuilder(field.Offset.AsInt, fieldDefType.InstanceByteCount.AsInt); + FromInstanceLayoutHelper(ref innerBuilder, fieldDefType); + } + } + } + + Debug.Assert(builder.ToGCMap().Size * type.Context.Target.PointerSize >= type.ThreadGcStaticFieldSize.AsInt); + return builder.ToGCMap(); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Common/Utilities/LockFreeReaderHashtableOfPointers.cs b/src/coreclr/tools/Common/TypeSystem/Common/Utilities/LockFreeReaderHashtableOfPointers.cs new file mode 100644 index 00000000000000..42b62fc6bb0738 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Common/Utilities/LockFreeReaderHashtableOfPointers.cs @@ -0,0 +1,699 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem +{ + /// + /// A hash table which is lock free for readers and up to 1 writer at a time. + /// It must be possible to compute the key's hashcode from a value. + /// All values must convertable to/from an IntPtr. + /// It must be possible to perform an equality check between a key and a value. + /// It must be possible to perform an equality check between a value and a value. + /// A LockFreeReaderKeyValueComparer must be provided to perform these operations. + /// This hashtable may not store a pointer to null or (void*)1 + /// + public abstract class LockFreeReaderHashtableOfPointers + { + private const int _initialSize = 16; + private const int _fillPercentageBeforeResize = 60; + + /// + /// _hashtable is the currently visible underlying array for the hashtable + /// Any modifications to this array must be additive only, and there must + /// never be a situation where the visible _hashtable has less data than + /// it did at an earlier time. This value is initialized to an array of size + /// 1. (That array is never mutated as any additions will trigger an Expand + /// operation, but we don't use an empty array as the + /// initial step, as this approach allows the TryGetValue logic to always + /// succeed without needing any length or null checks.) + /// + private volatile IntPtr[] _hashtable = new IntPtr[_initialSize]; + + /// + /// Tracks the hashtable being used by expansion. Used as a sentinel + /// to threads trying to add to the old hashtable that an expansion is + /// in progress. + /// + private volatile IntPtr[] _newHashTable; + + /// + /// _count represents the current count of elements in the hashtable + /// _count is used in combination with _resizeCount to control when the + /// hashtable should expand + /// + private volatile int _count; + + /// + /// Represents _count plus the number of potential adds currently happening. + /// If this reaches _hashTable.Length-1, an expansion is required (because + /// one slot must always be null for seeks to complete). + /// + private int _reserve; + + /// + /// _resizeCount represents the size at which the hashtable should resize. + /// While this doesn't strictly need to be volatile, having threads read stale values + /// triggers a lot of unneeded attempts to expand. + /// + private volatile int _resizeCount = _initialSize * _fillPercentageBeforeResize / 100; + + /// + /// Get the underlying array for the hashtable at this time. + /// + private IntPtr[] GetCurrentHashtable() + { + return _hashtable; + } + + /// + /// Set the newly visible hashtable underlying array. Used by writers after + /// the new array is fully constructed. The volatile write is used to ensure + /// that all writes to the contents of hashtable are completed before _hashtable + /// is visible to readers. + /// + private void SetCurrentHashtable(IntPtr[] hashtable) + { + _hashtable = hashtable; + } + + /// + /// Used to ensure that the hashtable can function with + /// fairly poor initial hash codes. + /// + public static int HashInt1(int key) + { + unchecked + { + int a = (int)0x9e3779b9 + key; + int b = (int)0x9e3779b9; + int c = 16777619; + a -= b; a -= c; a ^= (c >> 13); + b -= c; b -= a; b ^= (a << 8); + c -= a; c -= b; c ^= (b >> 13); + a -= b; a -= c; a ^= (c >> 12); + b -= c; b -= a; b ^= (a << 16); + c -= a; c -= b; c ^= (b >> 5); + a -= b; a -= c; a ^= (c >> 3); + b -= c; b -= a; b ^= (a << 10); + c -= a; c -= b; c ^= (b >> 15); + return c; + } + } + + /// + /// Generate a somewhat independent hash value from another integer. This is used + /// as part of a double hashing scheme. By being relatively prime with powers of 2 + /// this hash function can be reliably used as part of a double hashing scheme as it + /// is guaranteed to eventually probe every slot in the table. (Table sizes are + /// constrained to be a power of two) + /// + public static int HashInt2(int key) + { + unchecked + { + int hash = unchecked((int)0xB1635D64) + key; + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + hash |= 0x00000001; // To make sure that this is relatively prime with power of 2 + return hash; + } + } + + /// + /// Create the LockFreeReaderHashtable. This hash table is designed for GetOrCreateValue + /// to be a generally lock free api (unless an add is necessary) + /// + public LockFreeReaderHashtableOfPointers() + { +#if DEBUG + // Ensure the initial value is a power of 2 + bool foundAOne = false; + for (int i = 0; i < 32; i++) + { + int lastBit = _initialSize >> i; + if ((lastBit & 0x1) == 0x1) + { + Debug.Assert(!foundAOne); + foundAOne = true; + } + } +#endif // DEBUG + _newHashTable = _hashtable; + } + + /// + /// The current count of elements in the hashtable + /// + public int Count { get { return _count; } } + + /// + /// Gets the value associated with the specified key. + /// + /// The key of the value to get. + /// When this method returns, contains the value associated with + /// the specified key, if the key is found; otherwise, the default value for the type + /// of the value parameter. This parameter is passed uninitialized. This function is threadsafe, + /// and wait-free + /// true if a value was found + public bool TryGetValue(TKey key, out TValue value) + { + IntPtr[] hashTableLocal = GetCurrentHashtable(); + Debug.Assert(hashTableLocal.Length > 0); + int mask = hashTableLocal.Length - 1; + int hashCode = GetKeyHashCode(key); + int tableIndex = HashInt1(hashCode) & mask; + + IntPtr examineEntry = hashTableLocal[tableIndex]; + if ((examineEntry == IntPtr.Zero) || (examineEntry == new IntPtr(1))) + { + value = default(TValue); + return false; + } + + TValue valTemp = ConvertIntPtrToValue(hashTableLocal[tableIndex]); + if (CompareKeyToValue(key, valTemp)) + { + value = valTemp; + return true; + } + + int hash2 = HashInt2(hashCode); + tableIndex = (tableIndex + hash2) & mask; + examineEntry = hashTableLocal[tableIndex]; + + while ((examineEntry != IntPtr.Zero) && (examineEntry != new IntPtr(1))) + { + valTemp = ConvertIntPtrToValue(hashTableLocal[tableIndex]); + if (CompareKeyToValue(key, valTemp)) + { + value = valTemp; + return true; + } + tableIndex = (tableIndex + hash2) & mask; + examineEntry = hashTableLocal[tableIndex]; + } + value = default(TValue); + return false; + } + + /// + /// Spin and wait for a sentinel to disappear. + /// + /// + /// + /// The value that replaced the sentinel, or null + IntPtr WaitForSentinelInHashtableToDisappear(IntPtr[] hashtable, int tableIndex) + { + var sw = new SpinWait(); + while (true) + { + IntPtr value = Volatile.Read(ref hashtable[tableIndex]); + if (value != new IntPtr(1)) + return value; + sw.SpinOnce(); + } + } + + /// + /// Make the underlying array of the hashtable bigger. This function + /// does not change the contents of the hashtable. This entire function locks. + /// + private void Expand(IntPtr[] oldHashtable) + { + lock (this) + { + // If somebody else already resized, don't try to do it based on an old table + if (oldHashtable != _hashtable) + { + return; + } + + // The checked statement here protects against both the hashTable size and _reserve overflowing. That does mean + // the maximum size of _hashTable is 0x70000000 + int newSize = checked(oldHashtable.Length * 2); + + // The hashtable only functions well when it has a certain minimum size + const int minimumUsefulSize = 16; + if (newSize < minimumUsefulSize) + newSize = minimumUsefulSize; + + IntPtr[] newHashTable = new IntPtr[newSize]; + Interlocked.Exchange(ref _newHashTable, newHashTable); + + int mask = newHashTable.Length - 1; + for (int iEntry = 0; iEntry < _hashtable.Length; iEntry++) + { + IntPtr ptrValue = _hashtable[iEntry]; + if (ptrValue == new IntPtr(1)) + { + // Entry is in the process of writing a value + ptrValue = WaitForSentinelInHashtableToDisappear(oldHashtable, iEntry); + } + if (ptrValue == IntPtr.Zero) + continue; + + TValue value = ConvertIntPtrToValue(ptrValue); + // If there's a deadlock at this point, GetValueHashCode is re-entering Add, which it must not do. + int hashCode = GetValueHashCode(value); + int tableIndex = HashInt1(hashCode) & mask; + + // Initial probe into hashtable found empty spot + if (newHashTable[tableIndex] == IntPtr.Zero) + { + // Add to hash + newHashTable[tableIndex] = ptrValue; + continue; + } + + int hash2 = HashInt2(hashCode); + tableIndex = (tableIndex + hash2) & mask; + + while (newHashTable[tableIndex] != IntPtr.Zero) + { + tableIndex = (tableIndex + hash2) & mask; + } + + // We've probed to find an empty spot + // Add to hash + newHashTable[tableIndex] = ptrValue; + } + + _resizeCount = checked((newSize * _fillPercentageBeforeResize) / 100); + SetCurrentHashtable(newHashTable); + } + } + + /// + /// Grow the hashtable so that storing into the hashtable does not require any allocation + /// operations. + /// + /// + public void Reserve(int size) + { + while (size >= _resizeCount) + Expand(_hashtable); + } + + /// + /// Adds a value to the hashtable if it is not already present. + /// Note that the key is not specified as it is implicit in the value. This function is thread-safe, + /// but must only take locks around internal operations and GetValueHashCode. + /// + /// Value to attempt to add to the hashtable, must not be null + /// True if the value was added. False if it was already present. + public bool TryAdd(TValue value) + { + bool addedValue; + AddOrGetExistingInner(value, out addedValue); + return addedValue; + } + + /// + /// Add a value to the hashtable, or find a value which is already present in the hashtable. + /// Note that the key is not specified as it is implicit in the value. This function is thread-safe, + /// but must only take locks around internal operations and GetValueHashCode. + /// + /// Value to attempt to add to the hashtable, its conversion to IntPtr must not result in IntPtr.Zero + /// Newly added value, or a value which was already present in the hashtable which is equal to it. + public TValue AddOrGetExisting(TValue value) + { + bool unused; + return AddOrGetExistingInner(value, out unused); + } + + private TValue AddOrGetExistingInner(TValue value, out bool addedValue) + { + IntPtr ptrValue = ConvertValueToIntPtr(value); + + if (ptrValue == IntPtr.Zero) + throw new ArgumentNullException(); + + // Optimistically check to see if adding this value may require an expansion. If so, expand + // the table now. This isn't required to ensure space for the write, but helps keep + // the ratio in a good range. + if (_count >= _resizeCount) + { + Expand(_hashtable); + } + + TValue result = default(TValue); + bool success; + do + { + success = TryAddOrGetExisting(value, out addedValue, ref result); + } while (!success); + return result; + } + + IntPtr VolatileReadNonSentinelFromHashtable(IntPtr[] hashTable, int tableIndex) + { + IntPtr examineEntry = Volatile.Read(ref hashTable[tableIndex]); + + if (examineEntry == new IntPtr(1)) + examineEntry = WaitForSentinelInHashtableToDisappear(hashTable, tableIndex); + + return examineEntry; + } + + /// + /// Attemps to add a value to the hashtable, or find a value which is already present in the hashtable. + /// In some cases, this will fail due to contention with other additions and must be retried. + /// Note that the key is not specified as it is implicit in the value. This function is thread-safe, + /// but must only take locks around internal operations and GetValueHashCode. + /// + /// Value to attempt to add to the hashtable, must not be null + /// Set to true if was added to the table. False if the value + /// was already present. Not defined if adding was attempted but failed. + /// Newly added value if adding succeds, a value which was already present in the hashtable which is equal to it, + /// or an undefined value if adding fails and must be retried. + /// True if the operation succeeded (either because the value was added or the value was already present). + private bool TryAddOrGetExisting(TValue value, out bool addedValue, ref TValue valueInHashtable) + { + // The table must be captured into a local to ensure reads/writes + // don't get torn by expansions + IntPtr[] hashTableLocal = _hashtable; + + addedValue = true; + int mask = hashTableLocal.Length - 1; + int hashCode = GetValueHashCode(value); + int tableIndex = HashInt1(hashCode) & mask; + + // Find an empty spot, starting with the initial tableIndex + IntPtr examineEntry = VolatileReadNonSentinelFromHashtable(hashTableLocal, tableIndex); + if (examineEntry != IntPtr.Zero) + { + TValue valTmp = ConvertIntPtrToValue(examineEntry); + if (CompareValueToValue(value, valTmp)) + { + // Value is already present in hash, do not add + addedValue = false; + valueInHashtable = valTmp; + return true; + } + + int hash2 = HashInt2(hashCode); + tableIndex = (tableIndex + hash2) & mask; + examineEntry = VolatileReadNonSentinelFromHashtable(hashTableLocal, tableIndex); + while (examineEntry != IntPtr.Zero) + { + valTmp = ConvertIntPtrToValue(examineEntry); + if (CompareValueToValue(value, valTmp)) + { + // Value is already present in hash, do not add + addedValue = false; + valueInHashtable = valTmp; + return true; + } + tableIndex = (tableIndex + hash2) & mask; + examineEntry = VolatileReadNonSentinelFromHashtable(hashTableLocal, tableIndex); + } + } + + // Ensure there's enough space for at least one null slot after this write + if (Interlocked.Increment(ref _reserve) >= hashTableLocal.Length - 1) + { + Interlocked.Decrement(ref _reserve); + Expand(hashTableLocal); + + // Since we expanded, our index won't work, restart + return false; + } + + // We've probed to find an empty spot, add to hash + IntPtr ptrValue = ConvertValueToIntPtr(value); + if (!TryWriteSentinelToLocation(hashTableLocal, tableIndex)) + { + Interlocked.Decrement(ref _reserve); + return false; + } + + // Now that we've written to the local array, find out if that array has been + // replaced by expansion. If it has, we need to restart and write to the new array. + if (_newHashTable != hashTableLocal) + { + WriteAbortNullToLocation(hashTableLocal, tableIndex); + // Pulse the lock so we don't spin during an expansion + lock (this) { } + Interlocked.Decrement(ref _reserve); + return false; + } + + WriteValueToLocation(ptrValue, hashTableLocal, tableIndex); + + // If the write succeeded, increment _count + Interlocked.Increment(ref _count); + valueInHashtable = value; + return true; + } + + /// + /// Attampts to write the Sentinel into the table. May fail if another value has been added. + /// + /// True if the value was successfully written + private bool TryWriteSentinelToLocation(IntPtr[] hashTableLocal, int tableIndex) + { + // Add to hash, use a CompareExchange to ensure that + // the sentinel is are fully communicated to all threads + if (Interlocked.CompareExchange(ref hashTableLocal[tableIndex], new IntPtr(1), IntPtr.Zero) == IntPtr.Zero) + { + return true; + } + + return false; + } + + /// + /// Writes the value into the table. Must only be used to overwrite a sentinel. + /// + private void WriteValueToLocation(IntPtr value, IntPtr[] hashTableLocal, int tableIndex) + { + // Add to hash, use a volatile write to ensure that + // the contents of the value are fully published to all + // threads before adding to the hashtable + Volatile.Write(ref hashTableLocal[tableIndex], value); + } + + /// + /// Abandons the sentinel. Must only be used to overwrite a sentinel. + /// + private void WriteAbortNullToLocation(IntPtr[] hashTableLocal, int tableIndex) + { + // Abandon sentinel, use a volatile write to ensure that + // the contents of the value are fully published to all + // threads before continuing. + Volatile.Write(ref hashTableLocal[tableIndex], IntPtr.Zero); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private TValue CreateValueAndEnsureValueIsInTable(TKey key) + { + TValue newValue = CreateValueFromKey(key); + Debug.Assert(GetValueHashCode(newValue) == GetKeyHashCode(key)); + + return AddOrGetExisting(newValue); + } + + /// + /// Get the value associated with a key. If value is not present in dictionary, use the creator delegate passed in + /// at object construction time to create the value, and attempt to add it to the table. (Create the value while not + /// under the lock, but add it to the table while under the lock. This may result in a throw away object being constructed) + /// This function is thread-safe, but will take a lock to perform its operations. + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TValue GetOrCreateValue(TKey key) + { + TValue existingValue; + if (TryGetValue(key, out existingValue)) + return existingValue; + + return CreateValueAndEnsureValueIsInTable(key); + } + + /// + /// Determine if this collection contains a value associated with a key. This function is thread-safe, and wait-free. + /// + public bool Contains(TKey key) + { + TValue dummyExistingValue; + return TryGetValue(key, out dummyExistingValue); + } + + /// + /// Determine if this collection contains a given value, and returns the value in the hashtable if found. This function is thread-safe, and wait-free. + /// + /// Value to search for in the hashtable, must not be null + /// Value from the hashtable if found, otherwise null. + public TValue GetValueIfExists(TValue value) + { + if (value == null) + throw new ArgumentNullException(); + + IntPtr[] hashTableLocal = GetCurrentHashtable(); + Debug.Assert(hashTableLocal.Length > 0); + int mask = hashTableLocal.Length - 1; + int hashCode = GetValueHashCode(value); + int tableIndex = HashInt1(hashCode) & mask; + + IntPtr examineEntry = hashTableLocal[tableIndex]; + if ((examineEntry == IntPtr.Zero) || (examineEntry == new IntPtr(1))) + return default(TValue); + + TValue valTmp = ConvertIntPtrToValue(examineEntry); + if (CompareValueToValue(value, valTmp)) + return valTmp; + + int hash2 = HashInt2(hashCode); + tableIndex = (tableIndex + hash2) & mask; + examineEntry = hashTableLocal[tableIndex]; + while (examineEntry != IntPtr.Zero) + { + valTmp = ConvertIntPtrToValue(examineEntry); + if (CompareValueToValue(value, valTmp)) + return valTmp; + + tableIndex = (tableIndex + hash2) & mask; + examineEntry = hashTableLocal[tableIndex]; + } + + return default(TValue); + } + + /// + /// Enumerator type for the LockFreeReaderHashtable + /// This is threadsafe, but is not garaunteed to avoid torn state. + /// In particular, the enumerator may report some newly added values + /// but not others. All values in the hashtable as of enumerator + /// creation will always be enumerated. + /// + public struct Enumerator : IEnumerator + { + LockFreeReaderHashtableOfPointers _hashtable; + private IntPtr[] _hashtableContentsToEnumerate; + private int _index; + private TValue _current; + + /// + /// Use this to get an enumerable collection from a LockFreeReaderHashtable. + /// Used instead of a GetEnumerator method on the LockFreeReaderHashtable to + /// reduce excess type creation. (By moving the method here, the generic dictionary for + /// LockFreeReaderHashtable does not need to contain a reference to the + /// enumerator type. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Enumerator Get(LockFreeReaderHashtableOfPointers hashtable) + { + return new Enumerator(hashtable); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + return this; + } + + internal Enumerator(LockFreeReaderHashtableOfPointers hashtable) + { + _hashtable = hashtable; + _hashtableContentsToEnumerate = hashtable._hashtable; + _index = 0; + _current = default(TValue); + } + + public bool MoveNext() + { + if ((_hashtableContentsToEnumerate != null) && (_index < _hashtableContentsToEnumerate.Length)) + { + for (; _index < _hashtableContentsToEnumerate.Length; _index++) + { + IntPtr examineEntry = Volatile.Read(ref _hashtableContentsToEnumerate[_index]); + if ((examineEntry != IntPtr.Zero) && (examineEntry != new IntPtr(1))) + { + _current = _hashtable.ConvertIntPtrToValue(examineEntry); + _index++; + return true; + } + } + } + + _current = default(TValue); + return false; + } + + public void Dispose() + { + } + + public void Reset() + { + throw new NotSupportedException(); + } + + public TValue Current + { + get + { + return _current; + } + } + + object IEnumerator.Current + { + get + { + throw new NotSupportedException(); + } + } + } + + /// + /// Given a key, compute a hash code. This function must be thread safe. + /// + protected abstract int GetKeyHashCode(TKey key); + + /// + /// Given a value, compute a hash code which would be identical to the hash code + /// for a key which should look up this value. This function must be thread safe. + /// This function must also not cause additional hashtable adds. + /// + protected abstract int GetValueHashCode(TValue value); + + /// + /// Compare a key and value. If the key refers to this value, return true. + /// This function must be thread safe. + /// + protected abstract bool CompareKeyToValue(TKey key, TValue value); + + /// + /// Compare a value with another value. Return true if values are equal. + /// This function must be thread safe. + /// + protected abstract bool CompareValueToValue(TValue value1, TValue value2); + + /// + /// Create a new value from a key. Must be threadsafe. Value may or may not be added + /// to collection. Return value must not be null. + /// + protected abstract TValue CreateValueFromKey(TKey key); + + /// + /// Convert a value to an IntPtr for storage into the hashtable + /// + protected abstract IntPtr ConvertValueToIntPtr(TValue value); + + /// + /// Convert an IntPtr into a value for comparisions, or for returning. + /// + protected abstract TValue ConvertIntPtrToValue(IntPtr pointer); + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureEncoder.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureEncoder.cs new file mode 100644 index 00000000000000..68110e076d3cea --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureEncoder.cs @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Immutable; +using System.Reflection; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.Metadata; + +using Internal.TypeSystem; + +namespace Internal.TypeSystem.Ecma +{ + public interface IEntityHandleProvider + { + /// Implement to allow EcmaSignatureEncoder to encode types that need metadata references to be resolved. + /// only used to express non-generic references + EntityHandle GetTypeDefOrRefHandleForTypeDesc(TypeDesc type); + } + + public class EcmaSignatureEncoder where TEntityHandleProvider : IEntityHandleProvider + { + TEntityHandleProvider _entityHandleProvider; + + public EcmaSignatureEncoder(TEntityHandleProvider entityHandleProvider) + { + _entityHandleProvider = entityHandleProvider; + } + + public void EncodeMethodSignature(BlobBuilder methodSignatureBlob, MethodSignature signature) + { + BlobEncoder encoder = new BlobEncoder(methodSignatureBlob); + + MethodSignatureEncoder methodSigEncoder = encoder.MethodSignature( + SignatureCallingConvention.Default, signature.GenericParameterCount, !signature.IsStatic); + + ReturnTypeEncoder returnTypeEncoder; + ParametersEncoder parametersEncoder; + methodSigEncoder.Parameters(signature.Length, out returnTypeEncoder, out parametersEncoder); + + // Return Type Sig + EncodeTypeSignature(returnTypeEncoder.Type(), signature.ReturnType); + + // Parameter Types Sig + for (int i = 0; i < signature.Length; i++) + EncodeTypeSignature(parametersEncoder.AddParameter().Type(), signature[i]); + } + + public void EncodeTypeSignature(SignatureTypeEncoder encoder, TypeDesc type) + { + if (type is RuntimeDeterminedType) + { + EncodeTypeSignature(encoder, ((RuntimeDeterminedType)type).RuntimeDeterminedDetailsType); + return; + } + + switch (type.Category) + { + case TypeFlags.Boolean: + encoder.Boolean(); break; + case TypeFlags.Byte: + encoder.Byte(); break; + case TypeFlags.SByte: + encoder.SByte(); break; + case TypeFlags.Char: + encoder.Char(); break; + case TypeFlags.Int16: + encoder.Int16(); break; + case TypeFlags.UInt16: + encoder.UInt16(); break; + case TypeFlags.Int32: + encoder.Int32(); break; + case TypeFlags.UInt32: + encoder.UInt32(); break; + case TypeFlags.Int64: + encoder.Int64(); break; + case TypeFlags.UInt64: + encoder.UInt64(); break; + case TypeFlags.Single: + encoder.Single(); break; + case TypeFlags.Double: + encoder.Double(); break; + case TypeFlags.IntPtr: + encoder.IntPtr(); break; + case TypeFlags.UIntPtr: + encoder.UIntPtr(); break; + case TypeFlags.Void: + encoder.Builder.WriteByte((byte)PrimitiveTypeCode.Void); + break; + + case TypeFlags.SignatureTypeVariable: + encoder.GenericTypeParameter(((SignatureVariable)type).Index); + break; + + case TypeFlags.SignatureMethodVariable: + encoder.GenericMethodTypeParameter(((SignatureMethodVariable)type).Index); + break; + + case TypeFlags.GenericParameter: + { + var genericTypeDesc = (GenericParameterDesc)type; + if (genericTypeDesc.Kind == GenericParameterKind.Type) + encoder.GenericTypeParameter(genericTypeDesc.Index); + else + encoder.GenericMethodTypeParameter(genericTypeDesc.Index); + } + break; + + case TypeFlags.FunctionPointer: + { + FunctionPointerType fptrType = (FunctionPointerType)type; + encoder.FunctionPointer( + SignatureCallingConvention.Default, + fptrType.Signature.IsStatic ? default(FunctionPointerAttributes) : FunctionPointerAttributes.HasThis, + fptrType.Signature.GenericParameterCount); + + // Return Type Sig + EncodeTypeSignature(encoder, fptrType.Signature.ReturnType); + + // Parameter Types Sig + for (int i = 0; i < fptrType.Signature.Length; i++) + EncodeTypeSignature(encoder, fptrType.Signature[i]); + } + break; + + case TypeFlags.Array: + { + // Skip bounds and lobounds (TODO) + ImmutableArray bounds = ImmutableArray.Create(); + ImmutableArray lowerBounds = ImmutableArray.Create(); + encoder.Array( + elementType => EncodeTypeSignature(elementType, ((ArrayType)type).ElementType), + arrayShape => arrayShape.Shape(((ArrayType)type).Rank, bounds, lowerBounds)); + } + break; + + case TypeFlags.SzArray: + encoder.SZArray(); + EncodeTypeSignature(encoder, ((ParameterizedType)type).ParameterType); + break; + + case TypeFlags.ByRef: + encoder.Builder.WriteByte((byte)SignatureTypeCode.ByReference); + EncodeTypeSignature(encoder, ((ParameterizedType)type).ParameterType); + break; + + case TypeFlags.Pointer: + encoder.Builder.WriteByte((byte)SignatureTypeCode.Pointer); + EncodeTypeSignature(encoder, ((ParameterizedType)type).ParameterType); + break; + + case TypeFlags.Enum: + case TypeFlags.Class: + case TypeFlags.ValueType: + case TypeFlags.Interface: + case TypeFlags.Nullable: + { + if (type == type.Context.GetWellKnownType(WellKnownType.TypedReference)) + encoder.Builder.WriteByte((byte)PrimitiveTypeCode.TypedReference); + else if (type == type.Context.GetWellKnownType(WellKnownType.Object)) + encoder.PrimitiveType(PrimitiveTypeCode.Object); + else if (type == type.Context.GetWellKnownType(WellKnownType.String)) + encoder.PrimitiveType(PrimitiveTypeCode.String); + else if (type.HasInstantiation && !type.IsGenericDefinition) + { + encoder.GenericInstantiation(_entityHandleProvider.GetTypeDefOrRefHandleForTypeDesc(type.GetTypeDefinition()), type.Instantiation.Length, type.IsValueType); + + for (int i = 0; i < type.Instantiation.Length; i++) + EncodeTypeSignature(encoder, type.Instantiation[i]); + } + else + { + encoder.Type(_entityHandleProvider.GetTypeDefOrRefHandleForTypeDesc(type), type.IsValueType); + } + } + break; + + default: + throw new InvalidOperationException("Attempting to encode an invalid type signature."); + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/CoreRTILProvider.cs b/src/coreclr/tools/Common/TypeSystem/IL/CoreRTILProvider.cs new file mode 100644 index 00000000000000..caebd36dd4c35c --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/CoreRTILProvider.cs @@ -0,0 +1,349 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Internal.IL.Stubs; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL +{ + public sealed class CoreRTILProvider : ILProvider + { + private MethodIL TryGetRuntimeImplementedMethodIL(MethodDesc method) + { + // Provides method bodies for runtime implemented methods. It can return null for + // methods that are treated specially by the codegen. + + Debug.Assert(method.IsRuntimeImplemented); + + TypeDesc owningType = method.OwningType; + + if (owningType.IsDelegate) + { + return DelegateMethodILEmitter.EmitIL(method); + } + + return null; + } + + /// + /// Provides method bodies for intrinsics recognized by the compiler. + /// It can return null if it's not an intrinsic recognized by the compiler, + /// but an intrinsic e.g. recognized by codegen. + /// + private MethodIL TryGetIntrinsicMethodIL(MethodDesc method) + { + Debug.Assert(method.IsIntrinsic); + + MetadataType owningType = method.OwningType as MetadataType; + if (owningType == null) + return null; + + switch (owningType.Name) + { + case "Interlocked": + { + if (owningType.Namespace == "System.Threading") + return InterlockedIntrinsics.EmitIL(method); + } + break; + case "Unsafe": + { + if (owningType.Namespace == "Internal.Runtime.CompilerServices") + return UnsafeIntrinsics.EmitIL(method); + } + break; + case "MemoryMarshal": + { + if (owningType.Namespace == "System.Runtime.InteropServices") + return MemoryMarshalIntrinsics.EmitIL(method); + } + break; + case "Volatile": + { + if (owningType.Namespace == "System.Threading") + return VolatileIntrinsics.EmitIL(method); + } + break; + case "Debug": + { + if (owningType.Namespace == "System.Diagnostics" && method.Name == "DebugBreak") + return new ILStubMethodIL(method, new byte[] { (byte)ILOpcode.break_, (byte)ILOpcode.ret }, Array.Empty(), null); + } + break; + case "RuntimeAugments": + { + if (owningType.Namespace == "Internal.Runtime.Augments" && method.Name == "GetCanonType") + return GetCanonTypeIntrinsic.EmitIL(method); + } + break; + case "MethodTable": + { + if (owningType.Namespace == "Internal.Runtime" && method.Name == "get_SupportsRelativePointers") + { + ILOpcode value = method.Context.Target.SupportsRelativePointers ? + ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0; + return new ILStubMethodIL(method, new byte[] { (byte)value, (byte)ILOpcode.ret }, Array.Empty(), null); + } + } + break; + } + + return null; + } + + /// + /// Provides method bodies for intrinsics recognized by the compiler that + /// are specialized per instantiation. It can return null if the intrinsic + /// is not recognized. + /// + private MethodIL TryGetPerInstantiationIntrinsicMethodIL(MethodDesc method) + { + Debug.Assert(method.IsIntrinsic); + + MetadataType owningType = method.OwningType.GetTypeDefinition() as MetadataType; + if (owningType == null) + return null; + + string methodName = method.Name; + + switch (owningType.Name) + { + case "Activator": + { + TypeSystemContext context = owningType.Context; + if (methodName == "CreateInstance" && method.Signature.Length == 0 && method.HasInstantiation + && method.Instantiation[0] is TypeDesc activatedType + && activatedType != context.UniversalCanonType + && activatedType.IsValueType + && activatedType.GetParameterlessConstructor() == null) + { + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + + var t = emit.NewLocal(context.GetSignatureVariable(0, method: true)); + codeStream.EmitLdLoca(t); + codeStream.Emit(ILOpcode.initobj, emit.NewToken(context.GetSignatureVariable(0, method: true))); + codeStream.EmitLdLoc(t); + codeStream.Emit(ILOpcode.ret); + + return new InstantiatedMethodIL(method, emit.Link(method.GetMethodDefinition())); + } + } + break; + case "RuntimeHelpers": + { + if (owningType.Namespace == "System.Runtime.CompilerServices") + return RuntimeHelpersIntrinsics.EmitIL(method); + } + break; + case "Comparer`1": + { + if (methodName == "Create" && owningType.Namespace == "System.Collections.Generic") + return ComparerIntrinsics.EmitComparerCreate(method); + } + break; + case "EqualityComparer`1": + { + if (methodName == "Create" && owningType.Namespace == "System.Collections.Generic") + return ComparerIntrinsics.EmitEqualityComparerCreate(method); + } + break; + case "ComparerHelpers": + { + if (owningType.Namespace != "Internal.IntrinsicSupport") + return null; + + if (methodName == "EnumOnlyCompare") + { + //calls CompareTo for underlyingType to avoid boxing + + TypeDesc elementType = method.Instantiation[0]; + if (!elementType.IsEnum) + return null; + + TypeDesc underlyingType = elementType.UnderlyingType; + TypeDesc returnType = method.Context.GetWellKnownType(WellKnownType.Int32); + MethodDesc underlyingCompareToMethod = underlyingType.GetKnownMethod("CompareTo", + new MethodSignature( + MethodSignatureFlags.None, + genericParameterCount: 0, + returnType: returnType, + parameters: new TypeDesc[] {underlyingType})); + + ILEmitter emitter = new ILEmitter(); + var codeStream = emitter.NewCodeStream(); + + codeStream.EmitLdArga(0); + codeStream.EmitLdArg(1); + codeStream.Emit(ILOpcode.call, emitter.NewToken(underlyingCompareToMethod)); + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(method); + } + } + break; + case "EqualityComparerHelpers": + { + if (owningType.Namespace != "Internal.IntrinsicSupport") + return null; + + if (methodName == "EnumOnlyEquals") + { + // EnumOnlyEquals would basically like to do this: + // static bool EnumOnlyEquals(T x, T y) where T: struct => x == y; + // This is not legal though. + // We don't want to do this: + // static bool EnumOnlyEquals(T x, T y) where T: struct => x.Equals(y); + // Because it would box y. + // So we resort to some per-instantiation magic. + + TypeDesc elementType = method.Instantiation[0]; + if (!elementType.IsEnum) + return null; + + ILOpcode convInstruction; + if (((DefType)elementType).InstanceFieldSize.AsInt <= 4) + { + convInstruction = ILOpcode.conv_i4; + } + else + { + Debug.Assert(((DefType)elementType).InstanceFieldSize.AsInt == 8); + convInstruction = ILOpcode.conv_i8; + } + + return new ILStubMethodIL(method, new byte[] { + (byte)ILOpcode.ldarg_0, + (byte)convInstruction, + (byte)ILOpcode.ldarg_1, + (byte)convInstruction, + (byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.ceq), + (byte)ILOpcode.ret, + }, + Array.Empty(), null); + } + else if (methodName == "GetComparerForReferenceTypesOnly") + { + TypeDesc elementType = method.Instantiation[0]; + if (!elementType.IsRuntimeDeterminedSubtype + && !elementType.IsCanonicalSubtype(CanonicalFormKind.Any) + && !elementType.IsGCPointer) + { + return new ILStubMethodIL(method, new byte[] { + (byte)ILOpcode.ldnull, + (byte)ILOpcode.ret + }, + Array.Empty(), null); + } + } + else if (methodName == "StructOnlyEquals") + { + TypeDesc elementType = method.Instantiation[0]; + if (!elementType.IsRuntimeDeterminedSubtype + && !elementType.IsCanonicalSubtype(CanonicalFormKind.Any) + && !elementType.IsGCPointer) + { + Debug.Assert(elementType.IsValueType); + + TypeSystemContext context = elementType.Context; + MetadataType helperType = context.SystemModule.GetKnownType("Internal.IntrinsicSupport", "EqualityComparerHelpers"); + + MethodDesc methodToCall; + if (elementType.IsEnum) + { + methodToCall = helperType.GetKnownMethod("EnumOnlyEquals", null).MakeInstantiatedMethod(elementType); + } + else if (elementType.IsNullable && ComparerIntrinsics.ImplementsIEquatable(elementType.Instantiation[0])) + { + methodToCall = helperType.GetKnownMethod("StructOnlyEqualsNullable", null).MakeInstantiatedMethod(elementType.Instantiation[0]); + } + else if (ComparerIntrinsics.ImplementsIEquatable(elementType)) + { + methodToCall = helperType.GetKnownMethod("StructOnlyEqualsIEquatable", null).MakeInstantiatedMethod(elementType); + } + else + { + methodToCall = helperType.GetKnownMethod("StructOnlyNormalEquals", null).MakeInstantiatedMethod(elementType); + } + + return new ILStubMethodIL(method, new byte[] + { + (byte)ILOpcode.ldarg_0, + (byte)ILOpcode.ldarg_1, + (byte)ILOpcode.call, 1, 0, 0, 0, + (byte)ILOpcode.ret + }, + Array.Empty(), new object[] { methodToCall }); + } + } + } + break; + } + + return null; + } + + public override MethodIL GetMethodIL(MethodDesc method) + { + if (method is EcmaMethod ecmaMethod) + { + if (ecmaMethod.IsIntrinsic) + { + MethodIL result = TryGetIntrinsicMethodIL(ecmaMethod); + if (result != null) + return result; + } + + if (ecmaMethod.IsRuntimeImplemented) + { + MethodIL result = TryGetRuntimeImplementedMethodIL(ecmaMethod); + if (result != null) + return result; + } + + MethodIL methodIL = EcmaMethodIL.Create(ecmaMethod); + if (methodIL != null) + return methodIL; + + return null; + } + else + if (method is MethodForInstantiatedType || method is InstantiatedMethod) + { + // Intrinsics specialized per instantiation + if (method.IsIntrinsic) + { + MethodIL methodIL = TryGetPerInstantiationIntrinsicMethodIL(method); + if (methodIL != null) + return methodIL; + } + + var methodDefinitionIL = GetMethodIL(method.GetTypicalMethodDefinition()); + if (methodDefinitionIL == null) + return null; + return new InstantiatedMethodIL(method, methodDefinitionIL); + } + else + if (method is ILStubMethod) + { + return ((ILStubMethod)method).EmitIL(); + } + else + if (method is ArrayMethod) + { + return ArrayMethodILEmitter.EmitIL((ArrayMethod)method); + } + else + { + Debug.Assert(!(method is PInvokeTargetNativeMethod), "Who is asking for IL of PInvokeTargetNativeMethod?"); + return null; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/DelegateInfo.cs b/src/coreclr/tools/Common/TypeSystem/IL/DelegateInfo.cs new file mode 100644 index 00000000000000..d6993f05d3dad9 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/DelegateInfo.cs @@ -0,0 +1,251 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.IL.Stubs; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; +using Interlocked = System.Threading.Interlocked; + +namespace Internal.IL +{ + /// + /// Represents a delegate and provides access to compiler-generated methods on the delegate type. + /// + public class DelegateInfo + { + private readonly TypeDesc _delegateType; + private readonly DelegateFeature _supportedFeatures; + + private MethodSignature _signature; + + private MethodDesc _getThunkMethod; + private DelegateThunkCollection _thunks; + + /// + /// Gets the synthetic methods that support this delegate type. + /// + public IEnumerable Methods + { + get + { + if (_getThunkMethod == null) + { + Interlocked.CompareExchange(ref _getThunkMethod, new DelegateGetThunkMethodOverride(this), null); + } + + yield return _getThunkMethod; + + DelegateThunkCollection thunks = Thunks; + for (DelegateThunkKind kind = 0; kind < DelegateThunkCollection.MaxThunkKind; kind++) + { + MethodDesc thunk = thunks[kind]; + if (thunk != null) + yield return thunk; + } + } + } + + /// + /// Gets the collection of delegate invocation thunks. + /// + public DelegateThunkCollection Thunks + { + get + { + if (_thunks == null) + { + Interlocked.CompareExchange(ref _thunks, new DelegateThunkCollection(this), null); + } + return _thunks; + } + } + + /// + /// Gets the signature of the delegate type. + /// + public MethodSignature Signature + { + get + { + if (_signature == null) + { + _signature = _delegateType.GetKnownMethod("Invoke", null).Signature; + } + return _signature; + } + } + + public DelegateFeature SupportedFeatures + { + get + { + return _supportedFeatures; + } + } + + /// + /// Gets the type of the delegate. + /// + public TypeDesc Type + { + get + { + return _delegateType; + } + } + + public DelegateInfo(TypeDesc delegateType, DelegateFeature features) + { + Debug.Assert(delegateType.IsDelegate); + Debug.Assert(delegateType.IsTypeDefinition); + + _delegateType = delegateType; + _supportedFeatures = features; + } + } + + /// + /// Represents a collection of delegate invocation thunks. + /// + public class DelegateThunkCollection + { + public const DelegateThunkKind MaxThunkKind = DelegateThunkKind.ObjectArrayThunk + 1; + + private MethodDesc _openStaticThunk; + private MethodDesc _multicastThunk; + private MethodDesc _closedStaticThunk; + private MethodDesc _closedInstanceOverGeneric; + private MethodDesc _invokeObjectArrayThunk; + private MethodDesc _openInstanceThunk; + + internal DelegateThunkCollection(DelegateInfo owningDelegate) + { + _openStaticThunk = new DelegateInvokeOpenStaticThunk(owningDelegate); + _multicastThunk = new DelegateInvokeMulticastThunk(owningDelegate); + _closedStaticThunk = new DelegateInvokeClosedStaticThunk(owningDelegate); + _closedInstanceOverGeneric = new DelegateInvokeInstanceClosedOverGenericMethodThunk(owningDelegate); + + // Methods that have a byref-like type in the signature cannot be invoked with the object array thunk. + // We would need to box the parameter and these can't be boxed. + // Neither can be methods that have pointers in the signature. + MethodSignature delegateSignature = owningDelegate.Signature; + bool generateObjectArrayThunk = true; + for (int i = 0; i < delegateSignature.Length; i++) + { + TypeDesc paramType = delegateSignature[i]; + if (paramType.IsByRef) + paramType = ((ByRefType)paramType).ParameterType; + if (!paramType.IsSignatureVariable && paramType.IsByRefLike) + { + generateObjectArrayThunk = false; + break; + } + if (paramType.IsPointer || paramType.IsFunctionPointer) + { + generateObjectArrayThunk = false; + break; + } + } + TypeDesc returnType = delegateSignature.ReturnType; + if (returnType.IsByRef) + generateObjectArrayThunk = false; + if (!returnType.IsSignatureVariable && returnType.IsByRefLike) + generateObjectArrayThunk = false; + if (returnType.IsPointer || returnType.IsFunctionPointer) + generateObjectArrayThunk = false; + + if ((owningDelegate.SupportedFeatures & DelegateFeature.ObjectArrayThunk) != 0 && generateObjectArrayThunk) + _invokeObjectArrayThunk = new DelegateInvokeObjectArrayThunk(owningDelegate); + + // + // Check whether we have an open instance thunk + // + + if ((owningDelegate.SupportedFeatures & DelegateFeature.OpenInstanceThunk) != 0 && delegateSignature.Length > 0) + { + TypeDesc firstParam = delegateSignature[0]; + + bool generateOpenInstanceMethod; + + switch (firstParam.Category) + { + case TypeFlags.Pointer: + case TypeFlags.FunctionPointer: + generateOpenInstanceMethod = false; + break; + + case TypeFlags.ByRef: + firstParam = ((ByRefType)firstParam).ParameterType; + generateOpenInstanceMethod = firstParam.IsSignatureVariable || firstParam.IsValueType; + break; + + case TypeFlags.Array: + case TypeFlags.SzArray: + case TypeFlags.SignatureTypeVariable: + generateOpenInstanceMethod = true; + break; + + default: + Debug.Assert(firstParam.IsDefType); + generateOpenInstanceMethod = !firstParam.IsValueType; + break; + } + + if (generateOpenInstanceMethod) + { + _openInstanceThunk = new DelegateInvokeOpenInstanceThunk(owningDelegate); + } + } + } + + public MethodDesc this[DelegateThunkKind kind] + { + get + { + switch (kind) + { + case DelegateThunkKind.OpenStaticThunk: + return _openStaticThunk; + case DelegateThunkKind.MulticastThunk: + return _multicastThunk; + case DelegateThunkKind.ClosedStaticThunk: + return _closedStaticThunk; + case DelegateThunkKind.ClosedInstanceThunkOverGenericMethod: + return _closedInstanceOverGeneric; + case DelegateThunkKind.ObjectArrayThunk: + return _invokeObjectArrayThunk; + case DelegateThunkKind.OpenInstanceThunk: + return _openInstanceThunk; + default: + return null; + } + } + } + } + + // TODO: Unify with the consts used in Delegate.cs within the class library. + public enum DelegateThunkKind + { + MulticastThunk = 0, + ClosedStaticThunk = 1, + OpenStaticThunk = 2, + ClosedInstanceThunkOverGenericMethod = 3, // This may not exist + DelegateInvokeThunk = 4, + OpenInstanceThunk = 5, // This may not exist + ObjectArrayThunk = 6, // This may not exist + } + + [Flags] + public enum DelegateFeature + { + DynamicInvoke = 0x1, + ObjectArrayThunk = 0x2, + OpenInstanceThunk = 0x4, + + All = 0x7, + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs new file mode 100644 index 00000000000000..0f15befa918038 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs @@ -0,0 +1,251 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + internal struct ArrayMethodILEmitter + { + private ArrayMethod _method; + private TypeDesc _elementType; + private int _rank; + + private ILToken _helperFieldToken; + private ILEmitter _emitter; + + private ArrayMethodILEmitter(ArrayMethod method) + { + Debug.Assert(method.Kind != ArrayMethodKind.Address, "Should be " + nameof(ArrayMethodKind.AddressWithHiddenArg)); + + _method = method; + + ArrayType arrayType = (ArrayType)method.OwningType; + _rank = arrayType.Rank; + _elementType = arrayType.ElementType; + _emitter = new ILEmitter(); + + // This helper field is needed to generate proper GC tracking. There is no direct way + // to create interior pointer. + _helperFieldToken = _emitter.NewToken(_method.Context.GetWellKnownType(WellKnownType.Object).GetKnownField("m_pEEType")); + } + + private void EmitLoadInteriorAddress(ILCodeStream codeStream, int offset) + { + codeStream.EmitLdArg(0); // this + codeStream.Emit(ILOpcode.ldflda, _helperFieldToken); + codeStream.EmitLdc(offset); + codeStream.Emit(ILOpcode.add); + } + + private MethodIL EmitIL() + { + switch (_method.Kind) + { + case ArrayMethodKind.Get: + case ArrayMethodKind.Set: + case ArrayMethodKind.AddressWithHiddenArg: + EmitILForAccessor(); + break; + + case ArrayMethodKind.Ctor: + // .ctor is implemented as a JIT helper and the JIT shouldn't be asking for the IL. + default: + // Asking for anything else is invalid. + throw new InvalidOperationException(); + } + + return _emitter.Link(_method); + } + + public static MethodIL EmitIL(ArrayMethod arrayMethod) + { + return new ArrayMethodILEmitter(arrayMethod).EmitIL(); + } + + private void EmitILForAccessor() + { + Debug.Assert(_method.OwningType.IsMdArray); + + var codeStream = _emitter.NewCodeStream(); + var context = _method.Context; + + var int32Type = context.GetWellKnownType(WellKnownType.Int32); + + var totalLocalNum = _emitter.NewLocal(int32Type); + var lengthLocalNum = _emitter.NewLocal(int32Type); + + int pointerSize = context.Target.PointerSize; + + int argStartOffset = _method.Kind == ArrayMethodKind.AddressWithHiddenArg ? 2 : 1; + + var rangeExceptionLabel = _emitter.NewCodeLabel(); + ILCodeLabel typeMismatchExceptionLabel = null; + + if (_elementType.IsGCPointer) + { + // Type check + if (_method.Kind == ArrayMethodKind.Set) + { + MethodDesc checkArrayStore = + context.SystemModule.GetKnownType("System.Runtime", "RuntimeImports").GetKnownMethod("RhCheckArrayStore", null); + + codeStream.EmitLdArg(0); + codeStream.EmitLdArg(_rank + argStartOffset); + + codeStream.Emit(ILOpcode.call, _emitter.NewToken(checkArrayStore)); + } + else if (_method.Kind == ArrayMethodKind.AddressWithHiddenArg) + { + TypeDesc objectType = context.GetWellKnownType(WellKnownType.Object); + TypeDesc eetypeType = context.SystemModule.GetKnownType("Internal.Runtime", "MethodTable"); + + typeMismatchExceptionLabel = _emitter.NewCodeLabel(); + + ILCodeLabel typeCheckPassedLabel = _emitter.NewCodeLabel(); + + // Codegen will pass a null hidden argument if this is a `constrained.` call to the Address method. + // As per ECMA-335 III.2.3, the prefix suppresses the type check. + // if (hiddenArg == IntPtr.Zero) + // goto TypeCheckPassed; + codeStream.EmitLdArg(1); + codeStream.Emit(ILOpcode.brfalse, typeCheckPassedLabel); + + // MethodTable* actualElementType = this.MethodTable.RelatedParameterType; // ArrayElementType + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.call, _emitter.NewToken(objectType.GetKnownMethod("get_MethodTable", null))); + codeStream.Emit(ILOpcode.call, + _emitter.NewToken(eetypeType.GetKnownMethod("get_RelatedParameterType", null))); + + // MethodTable* expectedElementType = hiddenArg->RelatedParameterType; // ArrayElementType + codeStream.EmitLdArg(1); + codeStream.Emit(ILOpcode.call, + _emitter.NewToken(eetypeType.GetKnownMethod("get_RelatedParameterType", null))); + + // if (TypeCast.AreTypesEquivalent(expectedElementType, actualElementType)) + // ThrowHelpers.ThrowArrayTypeMismatchException(); + codeStream.Emit(ILOpcode.call, _emitter.NewToken( + context.SystemModule.GetKnownType("System.Runtime", "TypeCast").GetKnownMethod("AreTypesEquivalent", null))); + codeStream.Emit(ILOpcode.brfalse, typeMismatchExceptionLabel); + + codeStream.EmitLabel(typeCheckPassedLabel); + } + } + + // Methods on Rank 1 MdArray need to be able to handle `this` that is an SzArray + // because SzArray is castable to Rank 1 MdArray (but not the other way around). + + ILCodeLabel rangeCheckDoneLabel = null; + if (_rank == 1) + { + TypeDesc objectType = context.GetWellKnownType(WellKnownType.Object); + TypeDesc eetypeType = context.SystemModule.GetKnownType("Internal.Runtime", "MethodTable"); + + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.call, _emitter.NewToken(objectType.GetKnownMethod("get_MethodTable", null))); + codeStream.Emit(ILOpcode.call, + _emitter.NewToken(eetypeType.GetKnownMethod("get_IsSzArray", null))); + + ILCodeLabel notSzArrayLabel = _emitter.NewCodeLabel(); + codeStream.Emit(ILOpcode.brfalse, notSzArrayLabel); + + // We have an SzArray - do the bounds check differently + EmitLoadInteriorAddress(codeStream, pointerSize); + codeStream.Emit(ILOpcode.dup); + codeStream.Emit(ILOpcode.ldind_i4); + codeStream.EmitLdArg(argStartOffset); + codeStream.EmitStLoc(totalLocalNum); + codeStream.EmitLdLoc(totalLocalNum); + codeStream.Emit(ILOpcode.ble_un, rangeExceptionLabel); + + codeStream.EmitLdc(pointerSize); + codeStream.Emit(ILOpcode.add); + + rangeCheckDoneLabel = _emitter.NewCodeLabel(); + codeStream.Emit(ILOpcode.br, rangeCheckDoneLabel); + + codeStream.EmitLabel(notSzArrayLabel); + } + + for (int i = 0; i < _rank; i++) + { + // The first two fields are MethodTable pointer and total length. Lengths for each dimension follows. + int lengthOffset = (2 * pointerSize + i * sizeof(int)); + + EmitLoadInteriorAddress(codeStream, lengthOffset); + codeStream.Emit(ILOpcode.ldind_i4); + codeStream.EmitStLoc(lengthLocalNum); + + codeStream.EmitLdArg(i + argStartOffset); + + // Compare with length + codeStream.Emit(ILOpcode.dup); + codeStream.EmitLdLoc(lengthLocalNum); + codeStream.Emit(ILOpcode.bge_un, rangeExceptionLabel); + + // Add to the running total if we have one already + if (i > 0) + { + codeStream.EmitLdLoc(totalLocalNum); + codeStream.EmitLdLoc(lengthLocalNum); + codeStream.Emit(ILOpcode.mul); + codeStream.Emit(ILOpcode.add); + } + codeStream.EmitStLoc(totalLocalNum); + } + + // Compute element offset + // TODO: This leaves unused space for lower bounds to match CoreCLR... + int firstElementOffset = (2 * pointerSize + 2 * _rank * sizeof(int)); + EmitLoadInteriorAddress(codeStream, firstElementOffset); + + if (rangeCheckDoneLabel != null) + codeStream.EmitLabel(rangeCheckDoneLabel); + + codeStream.EmitLdLoc(totalLocalNum); + codeStream.Emit(ILOpcode.conv_u); + + int elementSize = _elementType.GetElementSize().AsInt; + if (elementSize != 1) + { + codeStream.EmitLdc(elementSize); + codeStream.Emit(ILOpcode.mul); + } + codeStream.Emit(ILOpcode.add); + + switch (_method.Kind) + { + case ArrayMethodKind.Get: + codeStream.Emit(ILOpcode.ldobj, _emitter.NewToken(_elementType)); + break; + + case ArrayMethodKind.Set: + codeStream.EmitLdArg(_rank + argStartOffset); + codeStream.Emit(ILOpcode.stobj, _emitter.NewToken(_elementType)); + break; + + case ArrayMethodKind.AddressWithHiddenArg: + break; + } + + codeStream.Emit(ILOpcode.ret); + + codeStream.EmitLdc(0); + codeStream.EmitLabel(rangeExceptionLabel); // Assumes that there is one "int" pushed on the stack + codeStream.Emit(ILOpcode.pop); + + MethodDesc throwHelper = context.GetHelperEntryPoint("ThrowHelpers", "ThrowIndexOutOfRangeException"); + codeStream.EmitCallThrowHelper(_emitter, throwHelper); + + if (typeMismatchExceptionLabel != null) + { + codeStream.EmitLabel(typeMismatchExceptionLabel); + codeStream.EmitCallThrowHelper(_emitter, context.GetHelperEntryPoint("ThrowHelpers", "ThrowArrayTypeMismatchException")); + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AssemblyGetExecutingAssemblyMethodThunk.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AssemblyGetExecutingAssemblyMethodThunk.Sorting.cs new file mode 100644 index 00000000000000..004388db62c15a --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AssemblyGetExecutingAssemblyMethodThunk.Sorting.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + // Functionality related to deterministic ordering of types + partial class AssemblyGetExecutingAssemblyMethodThunk + { + protected override int ClassCode => 1459986716; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (AssemblyGetExecutingAssemblyMethodThunk)other; + return StringComparer.Ordinal.Compare(ExecutingAssembly.GetName().Name, otherMethod.ExecutingAssembly.GetName().Name); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AssemblyGetExecutingAssemblyMethodThunk.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AssemblyGetExecutingAssemblyMethodThunk.cs new file mode 100644 index 00000000000000..ab0e8eee978581 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AssemblyGetExecutingAssemblyMethodThunk.cs @@ -0,0 +1,123 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + /// + /// Thunk that replaces calls to Assembly.GetExecutingAssembly in user code. The purpose of the thunk + /// is to load something that will let us identify the current assembly and call a class library + /// helper that will let us get the Assembly. + /// + internal partial class AssemblyGetExecutingAssemblyMethodThunk : ILStubMethod + { + public AssemblyGetExecutingAssemblyMethodThunk(TypeDesc owningType, IAssemblyDesc executingAssembly) + { + OwningType = owningType; + ExecutingAssembly = executingAssembly; + + TypeSystemContext context = owningType.Context; + + Signature = new MethodSignature(MethodSignatureFlags.Static, 0, + context.SystemModule.GetKnownType("System.Reflection", "Assembly"), TypeDesc.EmptyTypes); + } + + public override TypeSystemContext Context + { + get + { + return OwningType.Context; + } + } + + public IAssemblyDesc ExecutingAssembly + { + get; + } + + public override string Name + { + get + { + return $"GetExecutingAssembly_{ExecutingAssembly.GetName().Name}"; + } + } + + public override TypeDesc OwningType + { + get; + } + + public override MethodSignature Signature + { + get; + } + + public override MethodIL EmitIL() + { + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + + MethodDesc classlibHelper = Context.GetHelperEntryPoint("ReflectionHelpers", "GetExecutingAssembly"); + + // Use the global module type as "a type from the assembly that has metadata" + // Our reflection policy always makes sure this has metadata. + MetadataType moduleType = ((ModuleDesc)ExecutingAssembly).GetGlobalModuleType(); + + codeStream.Emit(ILOpcode.ldtoken, emit.NewToken(moduleType)); + codeStream.Emit(ILOpcode.call, emit.NewToken(classlibHelper)); + codeStream.Emit(ILOpcode.ret); + + return emit.Link(this); + } + } + + internal class AssemblyGetExecutingAssemblyMethodThunkCache + { + private TypeDesc _owningTypeForThunks; + private Unifier _cache; + + public AssemblyGetExecutingAssemblyMethodThunkCache(TypeDesc owningTypeForThunks) + { + _owningTypeForThunks = owningTypeForThunks; + _cache = new Unifier(this); + } + + public MethodDesc GetHelper(IAssemblyDesc executingAssembly) + { + return _cache.GetOrCreateValue(executingAssembly); + } + + private class Unifier : LockFreeReaderHashtable + { + private AssemblyGetExecutingAssemblyMethodThunkCache _parent; + + public Unifier(AssemblyGetExecutingAssemblyMethodThunkCache parent) + { + _parent = parent; + } + + protected override int GetKeyHashCode(IAssemblyDesc key) + { + return key.GetHashCode(); + } + protected override int GetValueHashCode(AssemblyGetExecutingAssemblyMethodThunk value) + { + return value.ExecutingAssembly.GetHashCode(); + } + protected override bool CompareKeyToValue(IAssemblyDesc key, AssemblyGetExecutingAssemblyMethodThunk value) + { + return key == value.ExecutingAssembly; + } + protected override bool CompareValueToValue(AssemblyGetExecutingAssemblyMethodThunk value1, AssemblyGetExecutingAssemblyMethodThunk value2) + { + return value1.ExecutingAssembly == value2.ExecutingAssembly; + } + protected override AssemblyGetExecutingAssemblyMethodThunk CreateValueFromKey(IAssemblyDesc key) + { + return new AssemblyGetExecutingAssemblyMethodThunk(_parent._owningTypeForThunks, key); + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Mangling.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Mangling.cs new file mode 100644 index 00000000000000..c68f9a10fe2fa6 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Mangling.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + public partial class CalliMarshallingMethodThunk : IPrefixMangledSignature + { + MethodSignature IPrefixMangledSignature.BaseSignature + { + get + { + return _targetSignature; + } + } + + string IPrefixMangledSignature.Prefix + { + get + { + return "Calli"; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Sorting.cs new file mode 100644 index 00000000000000..1c48c00b406078 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.Sorting.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + // Functionality related to deterministic ordering of methods + partial class CalliMarshallingMethodThunk + { + protected internal override int ClassCode => 1594107963; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (CalliMarshallingMethodThunk)other; + return comparer.Compare(_targetSignature, otherMethod._targetSignature); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.cs new file mode 100644 index 00000000000000..73b8645e855947 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/CalliMarshallingMethodThunk.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; +using Debug = System.Diagnostics.Debug; +using Internal.TypeSystem.Ecma; + +namespace Internal.IL.Stubs +{ + /// + /// Thunk to marshal calli PInvoke parameters and invoke the appropriate function pointer + /// + public partial class CalliMarshallingMethodThunk : ILStubMethod + { + private readonly MethodSignature _targetSignature; + private readonly InteropStateManager _interopStateManager; + private readonly TypeDesc _owningType; + + private MethodSignature _signature; + + public CalliMarshallingMethodThunk(MethodSignature targetSignature, TypeDesc owningType, + InteropStateManager interopStateManager) + { + _targetSignature = targetSignature; + _owningType = owningType; + _interopStateManager = interopStateManager; + } + + public MethodSignature TargetSignature + { + get + { + return _targetSignature; + } + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + // Prepend fnptr argument to the signature + TypeDesc[] parameterTypes = new TypeDesc[_targetSignature.Length + 1]; + + for (int i = 0; i < _targetSignature.Length; i++) + parameterTypes[i] = _targetSignature[i]; + parameterTypes[parameterTypes.Length - 1] = Context.GetWellKnownType(WellKnownType.IntPtr); + + _signature = new MethodSignature(MethodSignatureFlags.Static, 0, _targetSignature.ReturnType, parameterTypes); + } + return _signature; + } + } + + public override string Name + { + get + { + return "CalliMarshallingMethodThunk"; + } + } + + public override PInvokeMetadata GetPInvokeMethodMetadata() + { + // Return PInvokeAttributes.PreserveSig to circumvent marshalling required checks + return new PInvokeMetadata(null, null, PInvokeAttributes.PreserveSig); + } + + public override MethodIL EmitIL() + { + return PInvokeILEmitter.EmitIL(this, default(PInvokeILEmitterConfiguration), _interopStateManager); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DebuggerSteppingHelpers.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DebuggerSteppingHelpers.cs new file mode 100644 index 00000000000000..df2d5dba546791 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DebuggerSteppingHelpers.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.IL.Stubs +{ + public static class DebuggerSteppingHelpers + { + public static void MarkDebuggerStepThroughPoint(this ILCodeStream codeStream) + { + codeStream.DefineSequencePoint("", 0xF00F00); + } + + public static void MarkDebuggerStepInPoint(this ILCodeStream codeStream) + { + codeStream.DefineSequencePoint("", 0xFEEFEE); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.Mangling.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.Mangling.cs new file mode 100644 index 00000000000000..0f8d28eb9e8f27 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.Mangling.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + public partial class DelegateMarshallingMethodThunk : IPrefixMangledType + { + TypeDesc IPrefixMangledType.BaseType + { + get + { + return _delegateType; + } + } + + string IPrefixMangledType.Prefix + { + get + { + return NamePrefix; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.Sorting.cs new file mode 100644 index 00000000000000..c0a4887211c179 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.Sorting.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + // Functionality related to deterministic ordering of methods + partial class DelegateMarshallingMethodThunk + { + protected internal override int ClassCode => 1018037605; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (DelegateMarshallingMethodThunk)other; + int result = (int)Kind - (int)otherMethod.Kind; + if (result != 0) + return result; + + return comparer.Compare(_delegateType, otherMethod._delegateType); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.cs new file mode 100644 index 00000000000000..a81809c4193030 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.cs @@ -0,0 +1,245 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; +using Debug = System.Diagnostics.Debug; +using Internal.TypeSystem.Ecma; + +namespace Internal.IL.Stubs +{ + public enum DelegateMarshallingMethodThunkKind : byte + { + ReverseOpenStatic, + ReverseClosed, + ForwardNativeFunctionWrapper + } + /// + /// Thunk to marshal delegate parameters and invoke the appropriate delegate function pointer + /// + public partial class DelegateMarshallingMethodThunk : ILStubMethod + { + private readonly TypeDesc _owningType; + private readonly MetadataType _delegateType; + private readonly InteropStateManager _interopStateManager; + private readonly MethodDesc _invokeMethod; + private MethodSignature _signature; // signature of the native callable marshalling stub + + public DelegateMarshallingMethodThunkKind Kind + { + get; + } + + public override bool IsPInvoke + { + get + { + return Kind == DelegateMarshallingMethodThunkKind.ForwardNativeFunctionWrapper; + } + } + + public MarshalDirection Direction + { + get + { + if (Kind == DelegateMarshallingMethodThunkKind.ForwardNativeFunctionWrapper) + return MarshalDirection.Forward; + else + return MarshalDirection.Reverse; + } + } + + public DelegateMarshallingMethodThunk(MetadataType delegateType, TypeDesc owningType, + InteropStateManager interopStateManager, DelegateMarshallingMethodThunkKind kind) + { + _owningType = owningType; + _delegateType = delegateType; + _invokeMethod = delegateType.GetMethod("Invoke", null); + _interopStateManager = interopStateManager; + Kind = kind; + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override bool IsUnmanagedCallersOnly + { + get + { + return Direction == MarshalDirection.Reverse; + } + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + public MetadataType DelegateType + { + get + { + return _delegateType; + } + } + + private TypeDesc GetNativeMethodParameterType(TypeDesc managedType, MarshalAsDescriptor marshalAs, InteropStateManager interopStateManager, bool isReturn, bool isAnsi) + { + TypeDesc nativeType; + try + { + nativeType = MarshalHelpers.GetNativeMethodParameterType(managedType, marshalAs, interopStateManager, isReturn, isAnsi); + } + catch (NotSupportedException) + { + // if marshalling is not supported for this type the generated stubs will emit appropriate + // error message. We just set native type to be same as managedtype + nativeType = managedType; + } + return nativeType; + } + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + if (Kind == DelegateMarshallingMethodThunkKind.ForwardNativeFunctionWrapper) + { + _signature = _invokeMethod.Signature; + } + else + { + PInvokeFlags flags = default; + + if (_delegateType is EcmaType ecmaType) + { + flags = ecmaType.GetDelegatePInvokeFlags(); + } + + // Mirror CharSet normalization from Marshaller.CreateMarshaller + bool isAnsi = flags.CharSet switch + { + CharSet.Ansi => true, + CharSet.Unicode => false, + CharSet.Auto => !_delegateType.Context.Target.IsWindows, + _ => true + }; + + MethodSignature delegateSignature = _invokeMethod.Signature; + TypeDesc[] nativeParameterTypes = new TypeDesc[delegateSignature.Length]; + ParameterMetadata[] parameterMetadataArray = _invokeMethod.GetParameterMetadata(); + int parameterIndex = 0; + + MarshalAsDescriptor marshalAs = null; + if (parameterMetadataArray != null && parameterMetadataArray.Length > 0 && parameterMetadataArray[0].Index == 0) + { + marshalAs = parameterMetadataArray[parameterIndex++].MarshalAsDescriptor; + } + + TypeDesc nativeReturnType = GetNativeMethodParameterType(delegateSignature.ReturnType, + marshalAs, + _interopStateManager, + isReturn:true, + isAnsi:isAnsi); + + for (int i = 0; i < delegateSignature.Length; i++) + { + int sequence = i + 1; + Debug.Assert(parameterIndex == parameterMetadataArray.Length || sequence <= parameterMetadataArray[parameterIndex].Index); + if (parameterIndex == parameterMetadataArray.Length || sequence < parameterMetadataArray[parameterIndex].Index) + { + // if we don't have metadata for the parameter, marshalAs is null + marshalAs = null; + } + else + { + Debug.Assert(sequence == parameterMetadataArray[parameterIndex].Index); + marshalAs = parameterMetadataArray[parameterIndex++].MarshalAsDescriptor; + } + bool isByRefType = delegateSignature[i].IsByRef; + + var managedType = isByRefType ? delegateSignature[i].GetParameterType() : delegateSignature[i]; + + var nativeType = GetNativeMethodParameterType(managedType, + marshalAs, + _interopStateManager, + isReturn:false, + isAnsi:isAnsi); + + nativeParameterTypes[i] = isByRefType ? nativeType.MakePointerType() : nativeType; + } + + MethodSignatureFlags unmanagedCallingConvention = flags.UnmanagedCallingConvention; + if (unmanagedCallingConvention == MethodSignatureFlags.None) + unmanagedCallingConvention = MethodSignatureFlags.UnmanagedCallingConvention; + + _signature = new MethodSignature(MethodSignatureFlags.Static | unmanagedCallingConvention, 0, nativeReturnType, nativeParameterTypes); + } + } + return _signature; + } + } + + public override ParameterMetadata[] GetParameterMetadata() + { + return _invokeMethod.GetParameterMetadata(); + } + + public override PInvokeMetadata GetPInvokeMethodMetadata() + { + return _invokeMethod.GetPInvokeMethodMetadata(); + } + + public MethodSignature DelegateSignature + { + get + { + return _invokeMethod.Signature; + } + } + + private string NamePrefix + { + get + { + switch (Kind) + { + case DelegateMarshallingMethodThunkKind.ReverseOpenStatic: + return "ReverseOpenStaticDelegateStub"; + case DelegateMarshallingMethodThunkKind.ReverseClosed: + return "ReverseDelegateStub"; + case DelegateMarshallingMethodThunkKind.ForwardNativeFunctionWrapper: + return "ForwardNativeFunctionWrapper"; + default: + System.Diagnostics.Debug.Fail("Unexpected DelegateMarshallingMethodThunkKind."); + return String.Empty; + } + } + } + + public override string Name + { + get + { + return NamePrefix + "__" + DelegateType.Name; + } + } + + public override MethodIL EmitIL() + { + return PInvokeILEmitter.EmitIL(this, default(PInvokeILEmitterConfiguration), _interopStateManager); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMethodILEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMethodILEmitter.cs new file mode 100644 index 00000000000000..905d40fd2828ea --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMethodILEmitter.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + public static class DelegateMethodILEmitter + { + public static MethodIL EmitIL(MethodDesc method) + { + Debug.Assert(method.OwningType.IsDelegate); + Debug.Assert(method.OwningType.IsTypeDefinition); + Debug.Assert(method.IsRuntimeImplemented); + + if (method.Name == "BeginInvoke" || method.Name == "EndInvoke") + { + // BeginInvoke and EndInvoke are not supported on .NET Core + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + MethodDesc notSupportedExceptionHelper = method.Context.GetHelperEntryPoint("ThrowHelpers", "ThrowPlatformNotSupportedException"); + codeStream.EmitCallThrowHelper(emit, notSupportedExceptionHelper); + return emit.Link(method); + } + + if (method.Name == ".ctor") + { + // We only support delegate creation if the IL follows the delegate creation verifiability requirements + // described in ECMA-335 III.4.21 (newobj - create a new object). The codegen is expected to + // intrinsically expand the pattern. + // If the delegate creation doesn't follow the pattern, we generate code that throws at runtime. + // We could potentially implement this (unreliably) through the use of reflection metadata, + // but it remains to be proven that this is an actual customer scenario. + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + MethodDesc notSupportedExceptionHelper = method.Context.GetHelperEntryPoint("ThrowHelpers", "ThrowPlatformNotSupportedException"); + codeStream.EmitCallThrowHelper(emit, notSupportedExceptionHelper); + return emit.Link(method); + } + + if (method.Name == "Invoke") + { + TypeSystemContext context = method.Context; + + ILEmitter emit = new ILEmitter(); + TypeDesc delegateType = context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType; + FieldDesc firstParameterField = delegateType.GetKnownField("m_firstParameter"); + FieldDesc functionPointerField = delegateType.GetKnownField("m_functionPointer"); + ILCodeStream codeStream = emit.NewCodeStream(); + + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emit.NewToken(firstParameterField.InstantiateAsOpen())); + for (int i = 0; i < method.Signature.Length; i++) + { + codeStream.EmitLdArg(i + 1); + } + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emit.NewToken(functionPointerField.InstantiateAsOpen())); + + MethodSignature signature = method.Signature; + if (method.OwningType.HasInstantiation) + { + // If the owning type is generic, the signature will contain T's and U's. + // We need !0's and !1's. + TypeDesc[] typesToReplace = new TypeDesc[method.OwningType.Instantiation.Length]; + TypeDesc[] replacementTypes = new TypeDesc[typesToReplace.Length]; + for (int i = 0; i < typesToReplace.Length; i++) + { + typesToReplace[i] = method.OwningType.Instantiation[i]; + replacementTypes[i] = context.GetSignatureVariable(i, method: false); + } + TypeDesc[] parameters = new TypeDesc[method.Signature.Length]; + for (int i = 0; i < parameters.Length; i++) + { + parameters[i] = method.Signature[i].ReplaceTypesInConstructionOfType(typesToReplace, replacementTypes); + } + TypeDesc returnType = method.Signature.ReturnType.ReplaceTypesInConstructionOfType(typesToReplace, replacementTypes); + signature = new MethodSignature(signature.Flags, signature.GenericParameterCount, returnType, parameters); + } + + codeStream.Emit(ILOpcode.calli, emit.NewToken(signature)); + + codeStream.Emit(ILOpcode.ret); + + return emit.Link(method); + } + + return null; + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.Sorting.cs new file mode 100644 index 00000000000000..a5aece70b22371 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.Sorting.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + // Functionality related to deterministic ordering of types + partial class DelegateThunk + { + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (DelegateThunk)other; + return comparer.Compare(_delegateInfo.Type, otherMethod._delegateInfo.Type); + } + } + + partial class DelegateInvokeOpenStaticThunk + { + protected internal override int ClassCode => 386356101; + } + + public sealed partial class DelegateInvokeOpenInstanceThunk + { + protected internal override int ClassCode => -1787190244; + } + + partial class DelegateInvokeClosedStaticThunk + { + protected internal override int ClassCode => 28195375; + } + + partial class DelegateInvokeMulticastThunk + { + protected internal override int ClassCode => 639863471; + } + + partial class DelegateInvokeInstanceClosedOverGenericMethodThunk + { + protected internal override int ClassCode => -354480633; + } + + partial class DelegateInvokeObjectArrayThunk + { + protected internal override int ClassCode => 1993292344; + } + + partial class DelegateGetThunkMethodOverride + { + protected internal override int ClassCode => -321263379; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (DelegateGetThunkMethodOverride)other; + return comparer.Compare(_delegateInfo.Type, otherMethod._delegateInfo.Type); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs new file mode 100644 index 00000000000000..5725a062a086e2 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs @@ -0,0 +1,783 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + /// + /// Base class for all delegate invocation thunks. + /// + public abstract partial class DelegateThunk : ILStubMethod + { + private DelegateInfo _delegateInfo; + + public DelegateThunk(DelegateInfo delegateInfo) + { + _delegateInfo = delegateInfo; + } + + public sealed override TypeSystemContext Context + { + get + { + return _delegateInfo.Type.Context; + } + } + + public sealed override TypeDesc OwningType + { + get + { + return _delegateInfo.Type; + } + } + + public sealed override MethodSignature Signature + { + get + { + return _delegateInfo.Signature; + } + } + + public sealed override Instantiation Instantiation + { + get + { + return Instantiation.Empty; + } + } + + protected TypeDesc SystemDelegateType + { + get + { + return Context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType; + } + } + + protected FieldDesc ExtraFunctionPointerOrDataField + { + get + { + return SystemDelegateType.GetKnownField("m_extraFunctionPointerOrData"); + } + } + + protected FieldDesc HelperObjectField + { + get + { + return SystemDelegateType.GetKnownField("m_helperObject"); + } + } + + protected FieldDesc FirstParameterField + { + get + { + return SystemDelegateType.GetKnownField("m_firstParameter"); + } + } + + protected FieldDesc FunctionPointerField + { + get + { + return SystemDelegateType.GetKnownField("m_functionPointer"); + } + } + } + + /// + /// Invoke thunk for open delegates to static methods. Loads all arguments except + /// the 'this' pointer and performs an indirect call to the delegate target. + /// This method is injected into delegate types. + /// + public sealed partial class DelegateInvokeOpenStaticThunk : DelegateThunk + { + internal DelegateInvokeOpenStaticThunk(DelegateInfo delegateInfo) + : base(delegateInfo) + { + } + + public override MethodIL EmitIL() + { + // Target has the same signature as the Invoke method, except it's static. + MethodSignatureBuilder builder = new MethodSignatureBuilder(Signature); + builder.Flags = Signature.Flags | MethodSignatureFlags.Static; + + var emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + // Load all arguments except 'this' + for (int i = 0; i < Signature.Length; i++) + { + codeStream.EmitLdArg(i + 1); + } + + // Indirectly call the delegate target static method. + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(ExtraFunctionPointerOrDataField)); + + codeStream.Emit(ILOpcode.calli, emitter.NewToken(builder.ToSignature())); + + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + + public override string Name + { + get + { + return "InvokeOpenStaticThunk"; + } + } + } + + /// + /// Invoke thunk for open delegates to instance methods. This kind of thunk + /// uses the first parameter as `this` that gets passed to the target instance method. + /// The thunk also performs virtual resolution if necessary. + /// This kind of delegates is typically created with Delegate.CreateDelegate + /// and MethodInfo.CreateDelegate at runtime. + /// + public sealed partial class DelegateInvokeOpenInstanceThunk : DelegateThunk + { + internal DelegateInvokeOpenInstanceThunk(DelegateInfo delegateInfo) + : base(delegateInfo) + { + } + + public override MethodIL EmitIL() + { + Debug.Assert(Signature.Length > 0); + + var emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + // Load all arguments except delegate's 'this' + TypeDesc boxThisType = null; + TypeDesc[] parameters = new TypeDesc[Signature.Length - 1]; + for (int i = 0; i < Signature.Length; i++) + { + codeStream.EmitLdArg(i + 1); + + if (i == 0) + { + // Ensure that we're working with an object type by boxing it here. + // This is to allow delegates which are generic over thier first parameter + // to have valid code in their thunk. + if (Signature[i].IsSignatureVariable) + { + boxThisType = Signature[i]; + codeStream.Emit(ILOpcode.box, emitter.NewToken(boxThisType)); + } + } + else + { + parameters[i - 1] = Signature[i]; + } + } + + // Call a helper to get the actual method target + codeStream.EmitLdArg(0); + + if (Signature[0].IsByRef) + { + codeStream.Emit(ILOpcode.ldnull); + } + else + { + codeStream.EmitLdArg(1); + if (boxThisType != null) + { + codeStream.Emit(ILOpcode.box, emitter.NewToken(boxThisType)); + } + } + + codeStream.Emit(ILOpcode.call, emitter.NewToken(SystemDelegateType.GetKnownMethod("GetActualTargetFunctionPointer", null))); + + MethodSignature targetSignature = new MethodSignature(0, 0, Signature.ReturnType, parameters); + codeStream.Emit(ILOpcode.calli, emitter.NewToken(targetSignature)); + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + + public override string Name + { + get + { + return "InvokeOpenInstanceThunk"; + } + } + } + + /// + /// Invoke thunk for closed delegates to static methods. The target + /// is a static method, but the first argument is captured by the delegate. + /// The signature of the target has an extra object-typed argument, followed + /// by the arguments that are delegate-compatible with the thunk signature. + /// This method is injected into delegate types. + /// + public sealed partial class DelegateInvokeClosedStaticThunk : DelegateThunk + { + internal DelegateInvokeClosedStaticThunk(DelegateInfo delegateInfo) + : base(delegateInfo) + { + } + + public override MethodIL EmitIL() + { + TypeDesc[] targetMethodParameters = new TypeDesc[Signature.Length + 1]; + targetMethodParameters[0] = Context.GetWellKnownType(WellKnownType.Object); + + for (int i = 0; i < Signature.Length; i++) + { + targetMethodParameters[i + 1] = Signature[i]; + } + + var targetMethodSignature = new MethodSignature( + Signature.Flags | MethodSignatureFlags.Static, 0, Signature.ReturnType, targetMethodParameters); + + var emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + // Load the stored 'this' + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(HelperObjectField)); + + // Load all arguments except 'this' + for (int i = 0; i < Signature.Length; i++) + { + codeStream.EmitLdArg(i + 1); + } + + // Indirectly call the delegate target static method. + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(ExtraFunctionPointerOrDataField)); + + codeStream.Emit(ILOpcode.calli, emitter.NewToken(targetMethodSignature)); + + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + + public override string Name + { + get + { + return "InvokeClosedStaticThunk"; + } + } + } + + /// + /// Multicast invoke thunk for delegates that are a result of Delegate.Combine. + /// Passes it's arguments to each of the delegates that got combined and calls them + /// one by one. Returns the value of the last delegate executed. + /// This method is injected into delegate types. + /// + public sealed partial class DelegateInvokeMulticastThunk : DelegateThunk + { + internal DelegateInvokeMulticastThunk(DelegateInfo delegateInfo) + : base(delegateInfo) + { + } + + public override MethodIL EmitIL() + { + ILEmitter emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + ArrayType invocationListArrayType = SystemDelegateType.MakeArrayType(); + + ILLocalVariable delegateArrayLocal = emitter.NewLocal(invocationListArrayType); + ILLocalVariable invocationCountLocal = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); + ILLocalVariable iteratorLocal = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); + ILLocalVariable delegateToCallLocal = emitter.NewLocal(SystemDelegateType); + + ILLocalVariable returnValueLocal = 0; + if (!Signature.ReturnType.IsVoid) + { + returnValueLocal = emitter.NewLocal(Signature.ReturnType); + } + + // Fill in delegateArrayLocal + // Delegate[] delegateArrayLocal = (Delegate[])this.m_helperObject + + // ldarg.0 (this pointer) + // ldfld Delegate.HelperObjectField + // castclass Delegate[] + // stloc delegateArrayLocal + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(HelperObjectField)); + codeStream.Emit(ILOpcode.castclass, emitter.NewToken(invocationListArrayType)); + codeStream.EmitStLoc(delegateArrayLocal); + + // Fill in invocationCountLocal + // int invocationCountLocal = this.m_extraFunctionPointerOrData + // ldarg.0 (this pointer) + // ldfld Delegate.m_extraFunctionPointerOrData + // stloc invocationCountLocal + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(ExtraFunctionPointerOrDataField)); + codeStream.EmitStLoc(invocationCountLocal); + + // Fill in iteratorLocal + // int iteratorLocal = 0; + + // ldc.0 + // stloc iteratorLocal + codeStream.EmitLdc(0); + codeStream.EmitStLoc(iteratorLocal); + + // Loop across every element of the array. + ILCodeLabel startOfLoopLabel = emitter.NewCodeLabel(); + codeStream.EmitLabel(startOfLoopLabel); + + // Implement as do/while loop. We only have this stub in play if we're in the multicast situation + // Find the delegate to call + // Delegate = delegateToCallLocal = delegateArrayLocal[iteratorLocal]; + + // ldloc delegateArrayLocal + // ldloc iteratorLocal + // ldelem System.Delegate + // stloc delegateToCallLocal + codeStream.EmitLdLoc(delegateArrayLocal); + codeStream.EmitLdLoc(iteratorLocal); + codeStream.Emit(ILOpcode.ldelem, emitter.NewToken(SystemDelegateType)); + codeStream.EmitStLoc(delegateToCallLocal); + + // Call the delegate + // returnValueLocal = delegateToCallLocal(...); + + // ldloc delegateToCallLocal + // ldfld System.Delegate.m_firstParameter + // ldarg 1, n + // ldloc delegateToCallLocal + // ldfld System.Delegate.m_functionPointer + // calli returnValueType thiscall (all the params) + // IF there is a return value + // stloc returnValueLocal + + codeStream.EmitLdLoc(delegateToCallLocal); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(FirstParameterField)); + + for (int i = 0; i < Signature.Length; i++) + { + codeStream.EmitLdArg(i + 1); + } + + codeStream.EmitLdLoc(delegateToCallLocal); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(FunctionPointerField)); + + codeStream.Emit(ILOpcode.calli, emitter.NewToken(Signature)); + + if (returnValueLocal != 0) + codeStream.EmitStLoc(returnValueLocal); + + // Increment iteratorLocal + // ++iteratorLocal; + + // ldloc iteratorLocal + // ldc.i4.1 + // add + // stloc iteratorLocal + codeStream.EmitLdLoc(iteratorLocal); + codeStream.EmitLdc(1); + codeStream.Emit(ILOpcode.add); + codeStream.EmitStLoc(iteratorLocal); + + // Check to see if the loop is done + codeStream.EmitLdLoc(invocationCountLocal); + codeStream.EmitLdLoc(iteratorLocal); + codeStream.Emit(ILOpcode.bne_un, startOfLoopLabel); + + // Return to caller. If the delegate has a return value, be certain to return that. + // return returnValueLocal; + + // ldloc returnValueLocal + // ret + if (returnValueLocal != 0) + codeStream.EmitLdLoc(returnValueLocal); + + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + + public override string Name + { + get + { + return "InvokeMulticastThunk"; + } + } + } + + /// + /// Invoke thunk for delegates that point to closed instance generic methods. + /// These need a thunk because the function pointer to invoke might be a fat function + /// pointer and we need a calli to unwrap it, inject the hidden argument, shuffle the + /// rest of the arguments, and call the unwrapped function pointer. + /// + public sealed partial class DelegateInvokeInstanceClosedOverGenericMethodThunk : DelegateThunk + { + internal DelegateInvokeInstanceClosedOverGenericMethodThunk(DelegateInfo delegateInfo) + : base(delegateInfo) + { + } + + public override MethodIL EmitIL() + { + var emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + // Load the stored 'this' + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(HelperObjectField)); + + // Load all arguments except 'this' + for (int i = 0; i < Signature.Length; i++) + { + codeStream.EmitLdArg(i + 1); + } + + // Indirectly call the delegate target + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(ExtraFunctionPointerOrDataField)); + + codeStream.Emit(ILOpcode.calli, emitter.NewToken(Signature)); + + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + + public override string Name + { + get + { + return "InvokeInstanceClosedOverGenericMethodThunk"; + } + } + } + + /// + /// Reverse invocation stub which goes from the strongly typed parameters the delegate + /// accepts, converts them into an object array, and invokes a delegate with the + /// object array, and then casts and returns the result back. + /// This is used to support delegates pointing to the LINQ expression interpreter. + /// + public sealed partial class DelegateInvokeObjectArrayThunk : DelegateThunk + { + internal DelegateInvokeObjectArrayThunk(DelegateInfo delegateInfo) + : base(delegateInfo) + { + } + + public override MethodIL EmitIL() + { + // We will generate the following code: + // + // object ret; + // object[] args = new object[parameterCount]; + // args[0] = param0; + // args[1] = param1; + // ... + // try { + // ret = ((Func)dlg.m_helperObject)(args); + // } finally { + // param0 = (T0)args[0]; // only generated for each byref argument + // } + // return (TRet)ret; + + ILEmitter emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + TypeDesc objectType = Context.GetWellKnownType(WellKnownType.Object); + TypeDesc objectArrayType = objectType.MakeArrayType(); + + ILLocalVariable argsLocal = emitter.NewLocal(objectArrayType); + + bool hasReturnValue = !Signature.ReturnType.IsVoid; + + bool hasRefArgs = false; + if (Signature.Length > 0) + { + codeStream.EmitLdc(Signature.Length); + codeStream.Emit(ILOpcode.newarr, emitter.NewToken(objectType)); + codeStream.EmitStLoc(argsLocal); + + for (int i = 0; i < Signature.Length; i++) + { + TypeDesc paramType = Signature[i]; + bool paramIsByRef = false; + + if (paramType.IsByRef) + { + hasRefArgs |= paramType.IsByRef; + paramIsByRef = true; + paramType = ((ByRefType)paramType).ParameterType; + } + + hasRefArgs |= paramType.IsByRef; + + codeStream.EmitLdLoc(argsLocal); + codeStream.EmitLdc(i); + codeStream.EmitLdArg(i + 1); + + ILToken paramToken = emitter.NewToken(paramType); + + if (paramIsByRef) + { + codeStream.Emit(ILOpcode.ldobj, paramToken); + } + codeStream.Emit(ILOpcode.box, paramToken); + codeStream.Emit(ILOpcode.stelem_ref); + } + } + else + { + MethodDesc emptyObjectArrayMethod = Context.GetHelperEntryPoint("DelegateHelpers", "GetEmptyObjectArray"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(emptyObjectArrayMethod)); + codeStream.EmitStLoc(argsLocal); + } + + ILExceptionRegionBuilder tryFinallyRegion = null; + if (hasRefArgs) + { + // we emit a try/finally to update the args array even if an exception is thrown + tryFinallyRegion = emitter.NewFinallyRegion(); + codeStream.BeginTry(tryFinallyRegion); + } + + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(HelperObjectField)); + + MetadataType funcType = Context.SystemModule.GetKnownType("System", "Func`2"); + TypeDesc instantiatedFunc = funcType.MakeInstantiatedType(objectArrayType, objectType); + + codeStream.Emit(ILOpcode.castclass, emitter.NewToken(instantiatedFunc)); + + codeStream.EmitLdLoc(argsLocal); + + MethodDesc invokeMethod = instantiatedFunc.GetKnownMethod("Invoke", null); + codeStream.Emit(ILOpcode.callvirt, emitter.NewToken(invokeMethod)); + + ILLocalVariable retLocal = (ILLocalVariable)(-1); + if (hasReturnValue) + { + retLocal = emitter.NewLocal(objectType); + codeStream.EmitStLoc(retLocal); + } + else + { + codeStream.Emit(ILOpcode.pop); + } + + if (hasRefArgs) + { + ILCodeLabel returnLabel = emitter.NewCodeLabel(); + codeStream.Emit(ILOpcode.leave, returnLabel); + codeStream.EndTry(tryFinallyRegion); + + // copy back ref/out args + codeStream.BeginHandler(tryFinallyRegion); + for (int i = 0; i < Signature.Length; i++) + { + TypeDesc paramType = Signature[i]; + if (paramType.IsByRef) + { + paramType = ((ByRefType)paramType).ParameterType; + ILToken paramToken = emitter.NewToken(paramType); + + // Update parameter + codeStream.EmitLdArg(i + 1); + codeStream.EmitLdLoc(argsLocal); + codeStream.EmitLdc(i); + codeStream.Emit(ILOpcode.ldelem_ref); + codeStream.Emit(ILOpcode.unbox_any, paramToken); + codeStream.Emit(ILOpcode.stobj, paramToken); + } + } + codeStream.Emit(ILOpcode.endfinally); + codeStream.EndHandler(tryFinallyRegion); + codeStream.EmitLabel(returnLabel); + } + + if (hasReturnValue) + { + codeStream.EmitLdLoc(retLocal); + codeStream.Emit(ILOpcode.unbox_any, emitter.NewToken(Signature.ReturnType)); + } + + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + + public override string Name + { + get + { + return "InvokeObjectArrayThunk"; + } + } + } + + /// + /// Synthetic method override of "IntPtr Delegate.GetThunk(Int32)". This method is injected + /// into all delegate types and provides means for System.Delegate to access the various thunks + /// generated by the compiler. + /// + public sealed partial class DelegateGetThunkMethodOverride : ILStubMethod + { + private DelegateInfo _delegateInfo; + private MethodSignature _signature; + + internal DelegateGetThunkMethodOverride(DelegateInfo delegateInfo) + { + _delegateInfo = delegateInfo; + } + + public override TypeSystemContext Context + { + get + { + return _delegateInfo.Type.Context; + } + } + + public override TypeDesc OwningType + { + get + { + return _delegateInfo.Type; + } + } + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + TypeSystemContext context = _delegateInfo.Type.Context; + TypeDesc intPtrType = context.GetWellKnownType(WellKnownType.IntPtr); + TypeDesc int32Type = context.GetWellKnownType(WellKnownType.Int32); + + _signature = new MethodSignature(0, 0, intPtrType, new[] { int32Type }); + } + + return _signature; + } + } + + public override MethodIL EmitIL() + { + ILEmitter emitter = new ILEmitter(); + + var codeStream = emitter.NewCodeStream(); + + ILCodeLabel returnNullLabel = emitter.NewCodeLabel(); + + bool hasDynamicInvokeThunk = (_delegateInfo.SupportedFeatures & DelegateFeature.DynamicInvoke) != 0 && + DynamicInvokeMethodThunk.SupportsSignature(_delegateInfo.Signature); + + ILCodeLabel[] labels = new ILCodeLabel[(int)DelegateThunkCollection.MaxThunkKind]; + for (DelegateThunkKind i = 0; i < DelegateThunkCollection.MaxThunkKind; i++) + { + MethodDesc thunk = _delegateInfo.Thunks[i]; + if (thunk != null || + (i == DelegateThunkKind.DelegateInvokeThunk && hasDynamicInvokeThunk)) + labels[(int)i] = emitter.NewCodeLabel(); + else + labels[(int)i] = returnNullLabel; + } + + codeStream.EmitLdArg(1); + codeStream.EmitSwitch(labels); + + codeStream.Emit(ILOpcode.br, returnNullLabel); + + for (DelegateThunkKind i = 0; i < DelegateThunkCollection.MaxThunkKind; i++) + { + MethodDesc targetMethod = null; + + // Dynamic invoke thunk is special since we're calling into a shared helper + if (i == DelegateThunkKind.DelegateInvokeThunk && hasDynamicInvokeThunk) + { + Debug.Assert(_delegateInfo.Thunks[i] == null); + + var sig = new DynamicInvokeMethodSignature(_delegateInfo.Signature); + MethodDesc thunk = Context.GetDynamicInvokeThunk(sig); + + if (thunk.HasInstantiation) + { + TypeDesc[] inst = DynamicInvokeMethodThunk.GetThunkInstantiationForMethod(_delegateInfo.Type.InstantiateAsOpen().GetMethod("Invoke", null)); + targetMethod = Context.GetInstantiatedMethod(thunk, new Instantiation(inst)); + } + else + { + targetMethod = thunk; + } + } + else + { + MethodDesc thunk = _delegateInfo.Thunks[i]; + + if (thunk != null) + { + targetMethod = thunk.InstantiateAsOpen(); + } + } + + if (targetMethod != null) + { + codeStream.EmitLabel(labels[(int)i]); + codeStream.Emit(ILOpcode.ldftn, emitter.NewToken(targetMethod)); + codeStream.Emit(ILOpcode.ret); + } + } + + codeStream.EmitLabel(returnNullLabel); + codeStream.EmitLdc(0); + codeStream.Emit(ILOpcode.conv_i); + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + + public override Instantiation Instantiation + { + get + { + return Instantiation.Empty; + } + } + + public override bool IsVirtual + { + get + { + return true; + } + } + + public override string Name + { + get + { + return "GetThunk"; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.Sorting.cs new file mode 100644 index 00000000000000..43da587d086346 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.Sorting.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + // Functionality related to determinstic ordering of types + partial class DynamicInvokeMethodThunk + { + protected internal override int ClassCode => -1980933220; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + return CompareTo((DynamicInvokeMethodThunk)other); + } + + private int CompareTo(DynamicInvokeMethodThunk otherMethod) + { + int result = _targetSignature.Length - otherMethod._targetSignature.Length; + if (result != 0) + return result; + + DynamicInvokeMethodParameterKind thisReturnType = _targetSignature.ReturnType; + result = (int)thisReturnType - (int)otherMethod._targetSignature.ReturnType; + if (result != 0) + return result; + + result = _targetSignature.GetNumerOfReturnTypePointerIndirections() - otherMethod._targetSignature.GetNumerOfReturnTypePointerIndirections(); + if (result != 0) + return result; + + for (int i = 0; i < _targetSignature.Length; i++) + { + DynamicInvokeMethodParameterKind thisParamType = _targetSignature[i]; + result = (int)thisParamType - (int)otherMethod._targetSignature[i]; + if (result != 0) + return result; + + result = _targetSignature.GetNumberOfParameterPointerIndirections(i) - otherMethod._targetSignature.GetNumberOfParameterPointerIndirections(i); + if (result != 0) + return result; + } + + Debug.Assert(this == otherMethod); + return 0; + } + + partial class DynamicInvokeThunkGenericParameter + { + protected internal override int ClassCode => -234393261; + + protected internal override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) + { + var otherType = (DynamicInvokeThunkGenericParameter)other; + int result = Index - otherType.Index; + if (result != 0) + return result; + + return _owningMethod.CompareTo(otherType._owningMethod); + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.cs new file mode 100644 index 00000000000000..90ce50f8425ca4 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.cs @@ -0,0 +1,687 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; +using Interlocked = System.Threading.Interlocked; + +namespace Internal.IL.Stubs +{ + /// + /// Thunk to dynamically invoke a method using reflection. The method accepts an object[] of parameters + /// to target method, lays them out on the stack, and calls the target method. This thunk has heavy + /// dependencies on the general dynamic invocation infrastructure in System.InvokeUtils and gets called from there + /// at runtime. See comments in System.InvokeUtils for a more thorough explanation. + /// + public partial class DynamicInvokeMethodThunk : ILStubMethod + { + private TypeDesc _owningType; + private DynamicInvokeMethodSignature _targetSignature; + + private TypeDesc[] _instantiation; + private MethodSignature _signature; + + public DynamicInvokeMethodThunk(TypeDesc owningType, DynamicInvokeMethodSignature signature) + { + _owningType = owningType; + _targetSignature = signature; + } + + internal static bool SupportsDynamicInvoke(TypeSystemContext context) + { + return context.SystemModule.GetType("System", "InvokeUtils", throwIfNotFound: false) != null; + } + + private static TypeDesc UnwrapByRef(TypeDesc type) + { + if (type.IsByRef) + return ((ByRefType)type).ParameterType; + return type; + } + + private static bool ContainsFunctionPointer(TypeDesc type) + { + if (type.IsFunctionPointer) + return true; + else if (type.IsParameterizedType) + return ContainsFunctionPointer(((ParameterizedType)type).ParameterType); + else + return false; + } + + public static bool SupportsSignature(MethodSignature signature) + { + // ---------------------------------------------------------------- + // TODO: function pointer types are odd: https://github.com/dotnet/corert/issues/1929 + // ---------------------------------------------------------------- + + // ---------------------------------------------------------------- + // Methods that take or return ByRef-like types can't be reflection invoked + // + // TODO: CoreCLR allows invoking methods that take ByRef-like types by value when the argument has + // the default null value. It is a corner case that is unlikely to be exercised in practice. + // ---------------------------------------------------------------- + + TypeDesc unwrappedReturnType = UnwrapByRef(signature.ReturnType); + if (ContainsFunctionPointer(unwrappedReturnType)) + return false; + if (!unwrappedReturnType.IsSignatureVariable && unwrappedReturnType.IsByRefLike) + return false; + + for (int i = 0; i < signature.Length; i++) + { + TypeDesc unwrappedParameterType = UnwrapByRef(signature[i]); + if (ContainsFunctionPointer(unwrappedParameterType)) + return false; + if (!unwrappedParameterType.IsSignatureVariable && unwrappedParameterType.IsByRefLike) + return false; + } + + return true; + } + + public static TypeDesc[] GetThunkInstantiationForMethod(MethodDesc method) + { + MethodSignature sig = method.Signature; + + ParameterMetadata[] paramMetadata = null; + TypeDesc[] instantiation = new TypeDesc[sig.ReturnType.IsVoid ? sig.Length : sig.Length + 1]; + + for (int i = 0; i < sig.Length; i++) + { + TypeDesc parameterType = sig[i]; + if (parameterType.IsByRef) + { + // strip ByRefType off the parameter (the method already has ByRef in the signature) + parameterType = ((ByRefType)parameterType).ParameterType; + + // Strip off all the pointers. Pointers are not valid instantiation arguments and the thunk compensates for that + // by being specialized for the specific pointer depth. + while (parameterType.IsPointer) + parameterType = ((PointerType)parameterType).ParameterType; + } + else if (parameterType.IsPointer) + { + // Strip off all the pointers. Pointers are not valid instantiation arguments and the thunk compensates for that + // by being specialized for the specific pointer depth. + while (parameterType.IsPointer) + parameterType = ((PointerType)parameterType).ParameterType; + } + else if (parameterType.IsEnum) + { + // If the invoke method takes an enum as an input parameter and there is no default value for + // that paramter, we don't need to specialize on the exact enum type (we only need to specialize + // on the underlying integral type of the enum.) + if (paramMetadata == null) + paramMetadata = method.GetParameterMetadata(); + + bool hasDefaultValue = false; + foreach (var p in paramMetadata) + { + // Parameter metadata indexes are 1-based (0 is reserved for return "parameter") + if (p.Index == (i + 1) && p.HasDefault) + { + hasDefaultValue = true; + break; + } + } + + if (!hasDefaultValue) + parameterType = parameterType.UnderlyingType; + } + + instantiation[i] = parameterType; + } + + if (!sig.ReturnType.IsVoid) + { + TypeDesc returnType = sig.ReturnType; + + // strip ByRefType off the return type (the method already has ByRef in the signature) + if (returnType.IsByRef) + returnType = ((ByRefType)returnType).ParameterType; + + // If the invoke method return an object reference, we don't need to specialize on the + // exact type of the object reference, as the behavior is not different. + if ((returnType.IsDefType && !returnType.IsValueType) || returnType.IsArray) + { + returnType = method.Context.GetWellKnownType(WellKnownType.Object); + } + + // Strip off all the pointers. Pointers are not valid instantiation arguments and the thunk compensates for that + // by being specialized for the specific pointer depth. + while (returnType.IsPointer) + returnType = ((PointerType)returnType).ParameterType; + + instantiation[sig.Length] = returnType; + } + + return instantiation; + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + private MetadataType InvokeUtilsType + { + get + { + return Context.SystemModule.GetKnownType("System", "InvokeUtils"); + } + } + + private MetadataType ArgSetupStateType + { + get + { + return InvokeUtilsType.GetNestedType("ArgSetupState"); + } + } + + public DynamicInvokeMethodSignature TargetSignature + { + get + { + return _targetSignature; + } + } + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + _signature = new MethodSignature( + MethodSignatureFlags.Static, + Instantiation.Length, + Context.GetWellKnownType(WellKnownType.Object), + new TypeDesc[] + { + Context.GetWellKnownType(WellKnownType.Object), // thisPtr + Context.GetWellKnownType(WellKnownType.IntPtr), // methodToCall + ArgSetupStateType.MakeByRefType(), // argSetupState + Context.GetWellKnownType(WellKnownType.Boolean), // targetIsThisCall + }); + } + + return _signature; + } + } + + public override Instantiation Instantiation + { + get + { + if (_instantiation == null) + { + TypeDesc[] instantiation = + new TypeDesc[_targetSignature.HasReturnValue ? _targetSignature.Length + 1 : _targetSignature.Length]; + + for (int i = 0; i < _targetSignature.Length; i++) + instantiation[i] = new DynamicInvokeThunkGenericParameter(this, i); + + if (_targetSignature.HasReturnValue) + instantiation[_targetSignature.Length] = + new DynamicInvokeThunkGenericParameter(this, _targetSignature.Length); + + Interlocked.CompareExchange(ref _instantiation, instantiation, null); + } + + return new Instantiation(_instantiation); + } + } + + public override string Name + { + get + { + StringBuilder sb = new StringBuilder("InvokeRet"); + + switch (_targetSignature.ReturnType) + { + case DynamicInvokeMethodParameterKind.None: + sb.Append('V'); + break; + case DynamicInvokeMethodParameterKind.Pointer: + sb.Append('P'); + for (int i = 0; i < _targetSignature.GetNumerOfReturnTypePointerIndirections() - 1; i++) + sb.Append('p'); + break; + case DynamicInvokeMethodParameterKind.Reference: + sb.Append("R"); + for (int i = 0; i < _targetSignature.GetNumerOfReturnTypePointerIndirections(); i++) + sb.Append('p'); + break; + case DynamicInvokeMethodParameterKind.Value: + sb.Append('O'); + break; + default: + Debug.Fail("Unreachable"); + break; + } + + for (int i = 0; i < _targetSignature.Length; i++) + { + switch (_targetSignature[i]) + { + case DynamicInvokeMethodParameterKind.Pointer: + sb.Append('P'); + + for (int j = 0; j < _targetSignature.GetNumberOfParameterPointerIndirections(i) - 1; j++) + sb.Append('p'); + + break; + case DynamicInvokeMethodParameterKind.Reference: + sb.Append("R"); + + for (int j = 0; j < _targetSignature.GetNumberOfParameterPointerIndirections(i); j++) + sb.Append('p'); + break; + case DynamicInvokeMethodParameterKind.Value: + sb.Append("I"); + break; + default: + Debug.Fail("Unreachable"); + break; + } + } + + return sb.ToString(); + } + } + + public override MethodIL EmitIL() + { + ILEmitter emitter = new ILEmitter(); + ILCodeStream argSetupStream = emitter.NewCodeStream(); + ILCodeStream thisCallSiteSetupStream = emitter.NewCodeStream(); + ILCodeStream staticCallSiteSetupStream = emitter.NewCodeStream(); + ILCodeStream returnCodeStream = emitter.NewCodeStream(); + + // This function will look like + // + // !For each parameter to the method + // !if (parameter is In Parameter) + // localX is TypeOfParameterX& + // ldarg.2 + // ldtoken TypeOfParameterX + // call DynamicInvokeParamHelperIn(ref ArgSetupState, RuntimeTypeHandle) + // stloc localX + // !else + // localX is TypeOfParameter + // ldarg.2 + // ldtoken TypeOfParameterX + // call DynamicInvokeParamHelperRef(ref ArgSetupState, RuntimeTypeHandle) + // stloc localX + + // ldarg.2 + // call DynamicInvokeArgSetupComplete(ref ArgSetupState) + + // *** Thiscall instruction stream starts here *** + + // ldarg.3 // Load targetIsThisCall + // brfalse Not_this_call + + // ldarg.0 // Load this pointer + // !For each parameter + // !if (parameter is In Parameter) + // ldloc localX + // ldobj TypeOfParameterX + // !else + // ldloc localX + // ldarg.1 + // calli ReturnType thiscall(TypeOfParameter1, ...) + // br Process_return + + // *** Static call instruction stream starts here *** + + // Not_this_call: + // !For each parameter + // !if (parameter is In Parameter) + // ldloc localX + // ldobj TypeOfParameterX + // !else + // ldloc localX + // ldarg.1 + // calli ReturnType (TypeOfParameter1, ...) + + // *** Return code stream starts here *** + + // Process_return: + // !if (ReturnType is Byref) + // dup + // brfalse ByRefNull + // ldobj ReturnType + // !if ((ReturnType == void) + // ldnull + // !elif (ReturnType is pointer) + // System.Reflection.Pointer.Box(ReturnType) + // !else + // box ReturnType + // ret + // + // !if (ReturnType is ByRef) + // ByRefNull: + // pop + // call InvokeUtils.get_NullByRefValueSentinel + // ret + + ILCodeLabel lStaticCall = emitter.NewCodeLabel(); + ILCodeLabel lProcessReturn = emitter.NewCodeLabel(); + thisCallSiteSetupStream.EmitLdArg(3); // targetIsThisCall + thisCallSiteSetupStream.Emit(ILOpcode.brfalse, lStaticCall); + staticCallSiteSetupStream.EmitLabel(lStaticCall); + + thisCallSiteSetupStream.EmitLdArg(0); // thisPtr + + ILToken tokDynamicInvokeParamHelperRef = + emitter.NewToken(InvokeUtilsType.GetKnownMethod("DynamicInvokeParamHelperRef", null)); + ILToken tokDynamicInvokeParamHelperIn = + emitter.NewToken(InvokeUtilsType.GetKnownMethod("DynamicInvokeParamHelperIn", null)); + + TypeDesc[] targetMethodSignature = new TypeDesc[_targetSignature.Length]; + + for (int paramIndex = 0; paramIndex < _targetSignature.Length; paramIndex++) + { + TypeDesc paramType = Context.GetSignatureVariable(paramIndex, true); + DynamicInvokeMethodParameterKind paramKind = _targetSignature[paramIndex]; + + for (int i = 0; i < _targetSignature.GetNumberOfParameterPointerIndirections(paramIndex); i++) + paramType = paramType.MakePointerType(); + + ILToken tokParamType = emitter.NewToken(paramType); + ILLocalVariable local = emitter.NewLocal(paramType.MakeByRefType()); + + thisCallSiteSetupStream.EmitLdLoc(local); + staticCallSiteSetupStream.EmitLdLoc(local); + + argSetupStream.EmitLdArg(2); // argSetupState + argSetupStream.Emit(ILOpcode.ldtoken, tokParamType); + + if (paramKind == DynamicInvokeMethodParameterKind.Reference) + { + argSetupStream.Emit(ILOpcode.call, tokDynamicInvokeParamHelperRef); + + targetMethodSignature[paramIndex] = paramType.MakeByRefType(); + } + else + { + argSetupStream.Emit(ILOpcode.call, tokDynamicInvokeParamHelperIn); + + thisCallSiteSetupStream.EmitLdInd(paramType); + staticCallSiteSetupStream.EmitLdInd(paramType); + + targetMethodSignature[paramIndex] = paramType; + } + argSetupStream.EmitStLoc(local); + } + + argSetupStream.EmitLdArg(2); // argSetupState + argSetupStream.Emit(ILOpcode.call, emitter.NewToken(InvokeUtilsType.GetKnownMethod("DynamicInvokeArgSetupComplete", null))); + + thisCallSiteSetupStream.EmitLdArg(1); // methodToCall + staticCallSiteSetupStream.EmitLdArg(1); // methodToCall + + DynamicInvokeMethodParameterKind returnKind = _targetSignature.ReturnType; + TypeDesc returnType = returnKind != DynamicInvokeMethodParameterKind.None ? + Context.GetSignatureVariable(_targetSignature.Length, true) : + Context.GetWellKnownType(WellKnownType.Void); + + for (int i = 0; i < _targetSignature.GetNumerOfReturnTypePointerIndirections(); i++) + returnType = returnType.MakePointerType(); + + if (returnKind == DynamicInvokeMethodParameterKind.Reference) + returnType = returnType.MakeByRefType(); + + MethodSignature thisCallMethodSig = new MethodSignature(0, 0, returnType, targetMethodSignature); + thisCallSiteSetupStream.Emit(ILOpcode.calli, emitter.NewToken(thisCallMethodSig)); + thisCallSiteSetupStream.Emit(ILOpcode.br, lProcessReturn); + + MethodSignature staticCallMethodSig = new MethodSignature(MethodSignatureFlags.Static, 0, returnType, targetMethodSignature); + staticCallSiteSetupStream.Emit(ILOpcode.calli, emitter.NewToken(staticCallMethodSig)); + + returnCodeStream.EmitLabel(lProcessReturn); + + ILCodeLabel lByRefReturnNull = null; + + if (returnKind == DynamicInvokeMethodParameterKind.None) + { + returnCodeStream.Emit(ILOpcode.ldnull); + } + else + { + TypeDesc returnTypeForBoxing = returnType; + + if (returnType.IsByRef) + { + // If this is a byref return, we need to dereference first + returnTypeForBoxing = ((ByRefType)returnType).ParameterType; + lByRefReturnNull = emitter.NewCodeLabel(); + returnCodeStream.Emit(ILOpcode.dup); + returnCodeStream.Emit(ILOpcode.brfalse, lByRefReturnNull); + returnCodeStream.EmitLdInd(returnTypeForBoxing); + } + + if (returnTypeForBoxing.IsPointer) + { + // Pointers box differently + returnCodeStream.Emit(ILOpcode.ldtoken, emitter.NewToken(returnTypeForBoxing)); + MethodDesc getTypeFromHandleMethod = + Context.SystemModule.GetKnownType("System", "Type").GetKnownMethod("GetTypeFromHandle", null); + returnCodeStream.Emit(ILOpcode.call, emitter.NewToken(getTypeFromHandleMethod)); + + MethodDesc pointerBoxMethod = + Context.SystemModule.GetKnownType("System.Reflection", "Pointer").GetKnownMethod("Box", null); + returnCodeStream.Emit(ILOpcode.call, emitter.NewToken(pointerBoxMethod)); + } + else + { + ILToken tokReturnType = emitter.NewToken(returnTypeForBoxing); + returnCodeStream.Emit(ILOpcode.box, tokReturnType); + } + } + + returnCodeStream.Emit(ILOpcode.ret); + + if (lByRefReturnNull != null) + { + returnCodeStream.EmitLabel(lByRefReturnNull); + returnCodeStream.Emit(ILOpcode.pop); + returnCodeStream.Emit(ILOpcode.call, emitter.NewToken(InvokeUtilsType.GetKnownMethod("get_NullByRefValueSentinel", null))); + returnCodeStream.Emit(ILOpcode.ret); + } + + return emitter.Link(this); + } + + private partial class DynamicInvokeThunkGenericParameter : GenericParameterDesc + { + private DynamicInvokeMethodThunk _owningMethod; + + public DynamicInvokeThunkGenericParameter(DynamicInvokeMethodThunk owningMethod, int index) + { + _owningMethod = owningMethod; + Index = index; + } + + public override TypeSystemContext Context + { + get + { + return _owningMethod.Context; + } + } + + public override int Index + { + get; + } + + public override GenericParameterKind Kind + { + get + { + return GenericParameterKind.Method; + } + } + } + } + + internal enum DynamicInvokeMethodParameterKind + { + None, + Value, + Reference, + Pointer, + } + + /// + /// Wraps a to reduce it's fidelity. + /// + public struct DynamicInvokeMethodSignature : IEquatable + { + private MethodSignature _signature; + + public TypeSystemContext Context => _signature.ReturnType.Context; + + public bool HasReturnValue + { + get + { + return !_signature.ReturnType.IsVoid; + } + } + + public int Length + { + get + { + return _signature.Length; + } + } + + internal DynamicInvokeMethodParameterKind this[int index] + { + get + { + TypeDesc type = _signature[index]; + + if (type.IsByRef) + return DynamicInvokeMethodParameterKind.Reference; + else if (type.IsPointer) + return DynamicInvokeMethodParameterKind.Pointer; + else + return DynamicInvokeMethodParameterKind.Value; + } + } + + public static int GetNumberOfIndirections(TypeDesc type) + { + // Strip byrefness off. This is to support "ref void**"-style signatures. + if (type.IsByRef) + type = ((ByRefType)type).ParameterType; + + int result = 0; + while (type.IsPointer) + { + result++; + type = ((PointerType)type).ParameterType; + } + + return result; + } + + public int GetNumberOfParameterPointerIndirections(int paramIndex) + { + return GetNumberOfIndirections(_signature[paramIndex]); + } + + public int GetNumerOfReturnTypePointerIndirections() + { + return GetNumberOfIndirections(_signature.ReturnType); + } + + internal DynamicInvokeMethodParameterKind ReturnType + { + get + { + TypeDesc type = _signature.ReturnType; + if (type.IsPointer) + return DynamicInvokeMethodParameterKind.Pointer; + else if (type.IsVoid) + return DynamicInvokeMethodParameterKind.None; + else if (type.IsByRef) + return DynamicInvokeMethodParameterKind.Reference; + else + return DynamicInvokeMethodParameterKind.Value; + } + } + + public DynamicInvokeMethodSignature(MethodSignature concreteSignature) + { + Debug.Assert(DynamicInvokeMethodThunk.SupportsSignature(concreteSignature)); + _signature = concreteSignature; + } + + public override bool Equals(object obj) + { + return obj is DynamicInvokeMethodSignature && Equals((DynamicInvokeMethodSignature)obj); + } + + public override int GetHashCode() + { + int hashCode = (int)this.ReturnType * 0x5498341 + 0x832424; + + for (int i = 0; i < Length; i++) + { + int value = (int)this[i] * 0x5498341 + 0x832424; + hashCode = hashCode * 31 + value; + } + + return hashCode; + } + + public bool Equals(DynamicInvokeMethodSignature other) + { + DynamicInvokeMethodParameterKind thisReturnKind = ReturnType; + if (thisReturnKind != other.ReturnType) + return false; + + if (GetNumerOfReturnTypePointerIndirections() != other.GetNumerOfReturnTypePointerIndirections()) + return false; + + if (Length != other.Length) + return false; + + for (int i = 0; i < Length; i++) + { + DynamicInvokeMethodParameterKind thisParamKind = this[i]; + if (thisParamKind != other[i]) + return false; + + if (GetNumberOfParameterPointerIndirections(i) != other.GetNumberOfParameterPointerIndirections(i)) + return false; + } + + return true; + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/EnumThunks.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/EnumThunks.Sorting.cs new file mode 100644 index 00000000000000..d6ae00c139863f --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/EnumThunks.Sorting.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + // Functionality related to deterministic ordering of types + partial class EnumGetHashCodeThunk + { + protected internal override int ClassCode => 261739662; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (EnumGetHashCodeThunk)other; + return comparer.Compare(_owningType, otherMethod._owningType); + } + } + + partial class EnumEqualsThunk + { + protected internal override int ClassCode => -1774524780; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (EnumEqualsThunk)other; + return comparer.Compare(_owningType, otherMethod._owningType); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/EnumThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/EnumThunks.cs new file mode 100644 index 00000000000000..a44161e6fa354b --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/EnumThunks.cs @@ -0,0 +1,203 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + /// + /// Thunk to call the underlying type's GetHashCode method on enum types. + /// This method prevents boxing of 'this' that would be required before a call to + /// the System.Enum's default implementation. + /// + internal partial class EnumGetHashCodeThunk : ILStubMethod + { + private TypeDesc _owningType; + private MethodSignature _signature; + + public EnumGetHashCodeThunk(TypeDesc owningType) + { + Debug.Assert(owningType.IsEnum); + _owningType = owningType; + _signature = ObjectGetHashCodeMethod.Signature; + } + + private MethodDesc ObjectGetHashCodeMethod + { + get + { + return Context.GetWellKnownType(WellKnownType.Object).GetKnownMethod("GetHashCode", null); + } + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + public override MethodSignature Signature + { + get + { + return _signature; + } + } + + public override string Name + { + get + { + return "GetHashCode"; + } + } + + public override bool IsVirtual + { + get + { + // This would be implicit (false is the default), but let's be very explicit. + // Making this an actual override would cause size bloat with very little benefit. + // The usefulness of this method lies in it's ability to prevent boxing of 'this'. + // The base implementation on System.Enum is adequate for everything else. + return false; + } + } + + public override MethodIL EmitIL() + { + ILEmitter emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.constrained, emitter.NewToken(_owningType.UnderlyingType)); + codeStream.Emit(ILOpcode.callvirt, emitter.NewToken(ObjectGetHashCodeMethod)); + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + } + + /// + /// Thunk to compare underlying values of enums in the Equals method. + /// This method prevents boxing of 'this' that would be required before a call to + /// the System.Enum's default implementation. + /// + internal partial class EnumEqualsThunk : ILStubMethod + { + private TypeDesc _owningType; + private MethodSignature _signature; + + public EnumEqualsThunk(TypeDesc owningType) + { + Debug.Assert(owningType.IsEnum); + _owningType = owningType; + _signature = ObjectEqualsMethod.Signature; + } + + private MethodDesc ObjectEqualsMethod + { + get + { + return Context.GetWellKnownType(WellKnownType.Object).GetKnownMethod("Equals", null); + } + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + public override MethodSignature Signature + { + get + { + return _signature; + } + } + + public override string Name + { + get + { + return "Equals"; + } + } + + public override bool IsVirtual + { + get + { + // This would be implicit (false is the default), but let's be very explicit. + // Making this an actual override would cause size bloat with very little benefit. + // The usefulness of this method lies in it's ability to prevent boxing of 'this'. + // The base implementation on System.Enum is adequate for everything else. + return false; + } + } + + public override MethodIL EmitIL() + { + ILEmitter emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + // InstantiateAsOpen covers the weird case of generic enums + TypeDesc owningTypeAsOpen = _owningType.InstantiateAsOpen(); + + ILCodeLabel lNotEqual = emitter.NewCodeLabel(); + + // if (!(obj is {enumtype})) + // return false; + + codeStream.EmitLdArg(1); + codeStream.Emit(ILOpcode.isinst, emitter.NewToken(owningTypeAsOpen)); + codeStream.Emit(ILOpcode.dup); + codeStream.Emit(ILOpcode.brfalse, lNotEqual); + + // return ({underlyingtype})this == ({underlyingtype})obj; + + // PREFER: ILOpcode.unbox, but the codegen for that is pretty bad + codeStream.Emit(ILOpcode.ldflda, emitter.NewToken(Context.GetWellKnownType(WellKnownType.Object).GetKnownField("m_pEEType"))); + codeStream.EmitLdc(Context.Target.PointerSize); + codeStream.Emit(ILOpcode.add); + codeStream.EmitLdInd(owningTypeAsOpen); + + codeStream.EmitLdArg(0); + codeStream.EmitLdInd(owningTypeAsOpen); + + codeStream.Emit(ILOpcode.ceq); + + codeStream.Emit(ILOpcode.ret); + + codeStream.EmitLabel(lNotEqual); + codeStream.Emit(ILOpcode.pop); + codeStream.EmitLdc(0); + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ForwardDelegateCreationThunk.Mangling.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ForwardDelegateCreationThunk.Mangling.cs new file mode 100644 index 00000000000000..c6d9f6e71862ba --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ForwardDelegateCreationThunk.Mangling.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + /// + /// contains functionality related to name mangling + /// + public partial class ForwardDelegateCreationThunk : IPrefixMangledType + { + TypeDesc IPrefixMangledType.BaseType + { + get + { + return _delegateType; + } + } + + string IPrefixMangledType.Prefix + { + get + { + return "ForwardDelegateCreationStub"; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ForwardDelegateCreationThunk.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ForwardDelegateCreationThunk.Sorting.cs new file mode 100644 index 00000000000000..7bdbbadd0d00fc --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ForwardDelegateCreationThunk.Sorting.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + // Functionality related to deterministic ordering of types + partial class ForwardDelegateCreationThunk + { + protected internal override int ClassCode => 1026039617; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (ForwardDelegateCreationThunk)other; + + return comparer.Compare(DelegateType, otherMethod.DelegateType); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ForwardDelegateCreationThunk.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ForwardDelegateCreationThunk.cs new file mode 100644 index 00000000000000..e93d385a0ffcf6 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ForwardDelegateCreationThunk.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; +using Debug = System.Diagnostics.Debug; +using Internal.TypeSystem.Ecma; + +namespace Internal.IL.Stubs +{ + /// + /// Thunk to create Delegates from native function pointer + /// + public partial class ForwardDelegateCreationThunk : ILStubMethod + { + private readonly TypeDesc _owningType; + private readonly MetadataType _delegateType; + private readonly InteropStateManager _interopStateManager; + private MethodSignature _signature; + + + public ForwardDelegateCreationThunk(MetadataType delegateType, TypeDesc owningType, InteropStateManager interopStateManager) + { + _owningType = owningType; + _delegateType = delegateType; + _interopStateManager = interopStateManager; + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + public MetadataType DelegateType + { + get + { + return _delegateType; + } + } + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + _signature = new MethodSignature(MethodSignatureFlags.Static, 0, + DelegateType, + new TypeDesc[] { + Context.GetWellKnownType(WellKnownType.IntPtr) + }); + } + return _signature; + } + } + + public override string Name + { + get + { + return "ForwardDelegateCreationStub__" + DelegateType.Name; + } + } + + /// + /// This thunk creates a delegate from a native function pointer + /// by first creating a PInvokeDelegateWrapper from the function pointer + /// and then creating the delegate from the Invoke method of the wrapper + /// + /// Generated IL: + /// ldarg 0 + /// newobj PInvokeDelegateWrapper.ctor + /// dup + /// ldvirtftn PInvokeDelegateWrapper.Invoke + /// newobj DelegateType.ctor + /// ret + /// + /// Equivalent C# + /// return new DelegateType(new PInvokeDelegateWrapper(functionPointer).Invoke) + /// + public override MethodIL EmitIL() + { + var emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + codeStream.EmitLdArg(0); + + codeStream.Emit(ILOpcode.newobj, emitter.NewToken( + _interopStateManager.GetPInvokeDelegateWrapper(DelegateType) + .GetPInvokeDelegateWrapperMethod(PInvokeDelegateWrapperMethodKind.Constructor))); + + codeStream.Emit(ILOpcode.dup); + + codeStream.Emit(ILOpcode.ldvirtftn, emitter.NewToken( + _interopStateManager.GetPInvokeDelegateWrapper(DelegateType) + .GetPInvokeDelegateWrapperMethod(PInvokeDelegateWrapperMethodKind.Invoke))); + + codeStream.Emit(ILOpcode.newobj, emitter.NewToken( + _delegateType.GetMethod(".ctor", + new MethodSignature(MethodSignatureFlags.None, + genericParameterCount: 0, + returnType: Context.GetWellKnownType(WellKnownType.Void), + parameters: new TypeDesc[] { Context.GetWellKnownType(WellKnownType.Object), + Context.GetWellKnownType(WellKnownType.IntPtr)} + )))); + + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/GetCanonTypeIntrinsic.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/GetCanonTypeIntrinsic.cs new file mode 100644 index 00000000000000..5989185359f412 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/GetCanonTypeIntrinsic.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Runtime; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + /// + /// Provides method body for the GetCanonType intrinsic. + /// + public static class GetCanonTypeIntrinsic + { + public static MethodIL EmitIL(MethodDesc target) + { + Debug.Assert(target.Signature.Length == 1); + + ILEmitter emitter = new ILEmitter(); + var codeStream = emitter.NewCodeStream(); + + TypeSystemContext context = target.Context; + TypeDesc runtimeTypeHandleType = context.GetWellKnownType(WellKnownType.RuntimeTypeHandle); + Debug.Assert(target.Signature.ReturnType == runtimeTypeHandleType); + + if (context.SupportsCanon) + { + ILCodeLabel lNotCanon = emitter.NewCodeLabel(); + codeStream.Emit(ILOpcode.ldarg_0); + codeStream.EmitLdc((int)CanonTypeKind.NormalCanon); + codeStream.Emit(ILOpcode.bne_un, lNotCanon); + codeStream.Emit(ILOpcode.ldtoken, emitter.NewToken(context.CanonType)); + codeStream.Emit(ILOpcode.ret); + codeStream.EmitLabel(lNotCanon); + + // We're not conditioning this on SupportsUniversalCanon because the runtime type loader + // does a lot of comparisons against UniversalCanon and not having a RuntimeTypeHandle + // for it makes these checks awkward. + // Would be nice if we didn't have to emit the MethodTable if universal canonical code wasn't enabled + // at the time of compilation. + ILCodeLabel lNotUniversalCanon = emitter.NewCodeLabel(); + codeStream.Emit(ILOpcode.ldarg_0); + codeStream.EmitLdc((int)CanonTypeKind.UniversalCanon); + codeStream.Emit(ILOpcode.bne_un, lNotUniversalCanon); + codeStream.Emit(ILOpcode.ldtoken, emitter.NewToken(context.UniversalCanonType)); + codeStream.Emit(ILOpcode.ret); + codeStream.EmitLabel(lNotUniversalCanon); + } + + ILLocalVariable vNullTypeHandle = emitter.NewLocal(runtimeTypeHandleType); + codeStream.EmitLdLoca(vNullTypeHandle); + codeStream.Emit(ILOpcode.initobj, emitter.NewToken(runtimeTypeHandleType)); + codeStream.EmitLdLoc(vNullTypeHandle); + + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(target); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MethodBaseGetCurrentMethodThunk.Mangling.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MethodBaseGetCurrentMethodThunk.Mangling.cs new file mode 100644 index 00000000000000..868ffc7c7596f5 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MethodBaseGetCurrentMethodThunk.Mangling.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + partial class MethodBaseGetCurrentMethodThunk : IPrefixMangledMethod + { + MethodDesc IPrefixMangledMethod.BaseMethod + { + get + { + return Method; + } + } + + string IPrefixMangledMethod.Prefix + { + get + { + return "GetCurrentMethod"; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MethodBaseGetCurrentMethodThunk.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MethodBaseGetCurrentMethodThunk.Sorting.cs new file mode 100644 index 00000000000000..d3afe93c396600 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MethodBaseGetCurrentMethodThunk.Sorting.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + // Functionality related to deterministic ordering of types + partial class MethodBaseGetCurrentMethodThunk + { + protected override int ClassCode => 1889524798; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (MethodBaseGetCurrentMethodThunk)other; + return comparer.Compare(Method, otherMethod.Method); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MethodBaseGetCurrentMethodThunk.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MethodBaseGetCurrentMethodThunk.cs new file mode 100644 index 00000000000000..d75feac0fe9bdc --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MethodBaseGetCurrentMethodThunk.cs @@ -0,0 +1,123 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + /// + /// Thunk that replaces calls to MethodBase.GetCurrentMethod in user code. The purpose of the thunk + /// is to LDTOKEN the method considered "current method" and call into the class library to + /// retrieve the associated MethodBase object instance. + /// + internal partial class MethodBaseGetCurrentMethodThunk : ILStubMethod + { + public MethodBaseGetCurrentMethodThunk(MethodDesc method) + { + Debug.Assert(method.IsTypicalMethodDefinition); + + Method = method; + Signature = new MethodSignature(MethodSignatureFlags.Static, 0, + Context.SystemModule.GetKnownType("System.Reflection", "MethodBase"), TypeDesc.EmptyTypes); + } + + public override TypeSystemContext Context + { + get + { + return Method.Context; + } + } + + public MethodDesc Method + { + get; + } + + public override string Name + { + get + { + return Method.Name; + } + } + + public override TypeDesc OwningType + { + get + { + return Method.OwningType; + } + } + + public override MethodSignature Signature + { + get; + } + + public override MethodIL EmitIL() + { + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + + codeStream.Emit(ILOpcode.ldtoken, emit.NewToken(Method)); + + string helperName; + if (Method.OwningType.HasInstantiation) + { + codeStream.Emit(ILOpcode.ldtoken, emit.NewToken(Method.OwningType)); + helperName = "GetCurrentMethodGeneric"; + } + else + helperName = "GetCurrentMethodNonGeneric"; + + MethodDesc classlibHelper = Context.GetHelperEntryPoint("ReflectionHelpers", helperName); + + codeStream.Emit(ILOpcode.call, emit.NewToken(classlibHelper)); + codeStream.Emit(ILOpcode.ret); + + return emit.Link(this); + } + } + + internal class MethodBaseGetCurrentMethodThunkCache + { + private Unifier _cache; + + public MethodBaseGetCurrentMethodThunkCache() + { + _cache = new Unifier(); + } + + public MethodDesc GetHelper(MethodDesc currentMethod) + { + return _cache.GetOrCreateValue(currentMethod.GetTypicalMethodDefinition()); + } + + private class Unifier : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(MethodDesc key) + { + return key.GetHashCode(); + } + protected override int GetValueHashCode(MethodBaseGetCurrentMethodThunk value) + { + return value.Method.GetHashCode(); + } + protected override bool CompareKeyToValue(MethodDesc key, MethodBaseGetCurrentMethodThunk value) + { + return key == value.Method; + } + protected override bool CompareValueToValue(MethodBaseGetCurrentMethodThunk value1, MethodBaseGetCurrentMethodThunk value2) + { + return value1.Method == value2.Method; + } + protected override MethodBaseGetCurrentMethodThunk CreateValueFromKey(MethodDesc key) + { + return new MethodBaseGetCurrentMethodThunk(key); + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeILEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeILEmitter.cs new file mode 100644 index 00000000000000..d77bb5994ec0e5 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeILEmitter.cs @@ -0,0 +1,487 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; +using Debug = System.Diagnostics.Debug; +using Internal.TypeSystem.Ecma; + +namespace Internal.IL.Stubs +{ + /// + /// Provides method bodies for PInvoke methods + /// + /// This by no means intends to provide full PInvoke support. The intended use of this is to + /// a) prevent calls getting generated to targets that require a full marshaller + /// (this compiler doesn't provide that), and b) offer a hand in some very simple marshalling + /// situations (but support for this part might go away as the product matures). + /// + public struct PInvokeILEmitter + { + private readonly MethodDesc _targetMethod; + private readonly Marshaller[] _marshallers; + private readonly PInvokeILEmitterConfiguration _pInvokeILEmitterConfiguration; + private readonly PInvokeMetadata _pInvokeMetadata; + private readonly PInvokeFlags _flags; + private readonly InteropStateManager _interopStateManager; + + private PInvokeILEmitter(MethodDesc targetMethod, PInvokeILEmitterConfiguration pinvokeILEmitterConfiguration, InteropStateManager interopStateManager) + { + Debug.Assert(targetMethod.IsPInvoke || targetMethod is DelegateMarshallingMethodThunk || targetMethod is CalliMarshallingMethodThunk); + _targetMethod = targetMethod; + _pInvokeILEmitterConfiguration = pinvokeILEmitterConfiguration; + _pInvokeMetadata = targetMethod.GetPInvokeMethodMetadata(); + _interopStateManager = interopStateManager; + + // + // targetMethod could be either a PInvoke or a DelegateMarshallingMethodThunk + // ForwardNativeFunctionWrapper method thunks are marked as PInvokes, so it is + // important to check them first here so that we get the right flags. + // + if (_targetMethod is DelegateMarshallingMethodThunk delegateMethod) + { + _flags = ((EcmaType)delegateMethod.DelegateType.GetTypeDefinition()).GetDelegatePInvokeFlags(); + } + else + { + _flags = _pInvokeMetadata.Flags; + } + _marshallers = InitializeMarshallers(targetMethod, interopStateManager, _flags); + } + + private static Marshaller[] InitializeMarshallers(MethodDesc targetMethod, InteropStateManager interopStateManager, PInvokeFlags flags) + { + MarshalDirection direction = MarshalDirection.Forward; + MethodSignature methodSig; + switch (targetMethod) + { + case DelegateMarshallingMethodThunk delegateMethod: + methodSig = delegateMethod.DelegateSignature; + direction = delegateMethod.Direction; + break; + case CalliMarshallingMethodThunk calliMethod: + methodSig = calliMethod.TargetSignature; + break; + default: + methodSig = targetMethod.Signature; + break; + } + int indexOffset = 0; + if (!methodSig.IsStatic && direction == MarshalDirection.Forward) + { + // For instance methods(eg. Forward delegate marshalling thunk), first argument is + // the instance + indexOffset = 1; + } + ParameterMetadata[] parameterMetadataArray = targetMethod.GetParameterMetadata(); + Marshaller[] marshallers = new Marshaller[methodSig.Length + 1]; + int parameterIndex = 0; + ParameterMetadata parameterMetadata; + + for (int i = 0; i < marshallers.Length; i++) + { + Debug.Assert(parameterIndex == parameterMetadataArray.Length || i <= parameterMetadataArray[parameterIndex].Index); + if (parameterIndex == parameterMetadataArray.Length || i < parameterMetadataArray[parameterIndex].Index) + { + // if we don't have metadata for the parameter, create a dummy one + parameterMetadata = new ParameterMetadata(i, ParameterMetadataAttributes.None, null); + } + else + { + Debug.Assert(i == parameterMetadataArray[parameterIndex].Index); + parameterMetadata = parameterMetadataArray[parameterIndex++]; + } + + TypeDesc parameterType; + bool isHRSwappedRetVal = false; + if (i == 0) + { + // First item is the return type + parameterType = methodSig.ReturnType; + if (!flags.PreserveSig && !parameterType.IsVoid) + { + // PreserveSig = false can only show up an regular forward PInvokes + Debug.Assert(direction == MarshalDirection.Forward); + + parameterType = methodSig.Context.GetByRefType(parameterType); + isHRSwappedRetVal = true; + } + } + else + { + parameterType = methodSig[i - 1]; + } + + marshallers[i] = Marshaller.CreateMarshaller(parameterType, + parameterIndex, + methodSig.GetEmbeddedSignatureData(), + MarshallerType.Argument, + parameterMetadata.MarshalAsDescriptor, + direction, + marshallers, + interopStateManager, + indexOffset + parameterMetadata.Index, + flags, + parameterMetadata.In, + isHRSwappedRetVal ? true : parameterMetadata.Out, + isHRSwappedRetVal ? false : parameterMetadata.Return + ); + } + + return marshallers; + } + + private void EmitDelegateCall(DelegateMarshallingMethodThunk delegateMethod, PInvokeILCodeStreams ilCodeStreams) + { + ILEmitter emitter = ilCodeStreams.Emitter; + ILCodeStream fnptrLoadStream = ilCodeStreams.FunctionPointerLoadStream; + ILCodeStream marshallingCodeStream = ilCodeStreams.MarshallingCodeStream; + ILCodeStream callsiteSetupCodeStream = ilCodeStreams.CallsiteSetupCodeStream; + TypeSystemContext context = _targetMethod.Context; + + Debug.Assert(delegateMethod != null); + + if (delegateMethod.Kind == DelegateMarshallingMethodThunkKind.ReverseOpenStatic) + { + // + // For Open static delegates call + // InteropHelpers.GetCurrentCalleeOpenStaticDelegateFunctionPointer() + // which returns a function pointer. Just call the function pointer and we are done. + // + TypeDesc[] parameters = new TypeDesc[_marshallers.Length - 1]; + for (int i = 1; i < _marshallers.Length; i++) + { + parameters[i - 1] = _marshallers[i].ManagedParameterType; + } + + MethodSignature managedSignature = new MethodSignature( + MethodSignatureFlags.Static, 0, _marshallers[0].ManagedParameterType, parameters); + + fnptrLoadStream.Emit(ILOpcode.call, emitter.NewToken( + delegateMethod.Context.GetHelperType("InteropHelpers").GetKnownMethod( + "GetCurrentCalleeOpenStaticDelegateFunctionPointer", null))); + + ILLocalVariable vDelegateStub = emitter.NewLocal( + delegateMethod.Context.GetWellKnownType(WellKnownType.IntPtr)); + + fnptrLoadStream.EmitStLoc(vDelegateStub); + callsiteSetupCodeStream.EmitLdLoc(vDelegateStub); + callsiteSetupCodeStream.Emit(ILOpcode.calli, emitter.NewToken(managedSignature)); + } + else if (delegateMethod.Kind == DelegateMarshallingMethodThunkKind.ReverseClosed) + { + // + // For closed delegates call + // InteropHelpers.GetCurrentCalleeDelegate + // which returns the delegate. Do a CallVirt on the invoke method. + // + MethodDesc instantiatedHelper = delegateMethod.Context.GetInstantiatedMethod( + delegateMethod.Context.GetHelperType("InteropHelpers") + .GetKnownMethod("GetCurrentCalleeDelegate", null), + new Instantiation((delegateMethod.DelegateType))); + + fnptrLoadStream.Emit(ILOpcode.call, emitter.NewToken(instantiatedHelper)); + + ILLocalVariable vDelegateStub = emitter.NewLocal(delegateMethod.DelegateType); + fnptrLoadStream.EmitStLoc(vDelegateStub); + marshallingCodeStream.EmitLdLoc(vDelegateStub); + MethodDesc invokeMethod = delegateMethod.DelegateType.GetKnownMethod("Invoke", null); + callsiteSetupCodeStream.Emit(ILOpcode.callvirt, emitter.NewToken(invokeMethod)); + } + else if (delegateMethod.Kind == DelegateMarshallingMethodThunkKind + .ForwardNativeFunctionWrapper) + { + // if the SetLastError flag is set in UnmanagedFunctionPointerAttribute, clear the error code before doing P/Invoke + if (_flags.SetLastError) + { + callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( + InteropTypes.GetPInvokeMarshal(context).GetKnownMethod("ClearLastError", null))); + } + + // + // For NativeFunctionWrapper we need to load the native function and call it + // + fnptrLoadStream.EmitLdArg(0); + fnptrLoadStream.Emit(ILOpcode.call, emitter.NewToken(InteropTypes + .GetNativeFunctionPointerWrapper(context) + .GetMethod("get_NativeFunctionPointer", null))); + + var fnPtr = emitter.NewLocal( + context.GetWellKnownType(WellKnownType.IntPtr)); + + fnptrLoadStream.EmitStLoc(fnPtr); + callsiteSetupCodeStream.EmitLdLoc(fnPtr); + + TypeDesc nativeReturnType = _marshallers[0].NativeParameterType; + TypeDesc[] nativeParameterTypes = new TypeDesc[_marshallers.Length - 1]; + + for (int i = 1; i < _marshallers.Length; i++) + { + nativeParameterTypes[i - 1] = _marshallers[i].NativeParameterType; + } + + MethodSignatureFlags unmanagedCallingConvention = _flags.UnmanagedCallingConvention; + if (unmanagedCallingConvention == MethodSignatureFlags.None) + unmanagedCallingConvention = MethodSignatureFlags.UnmanagedCallingConvention; + + MethodSignature nativeSig = new MethodSignature( + MethodSignatureFlags.Static | unmanagedCallingConvention, 0, nativeReturnType, nativeParameterTypes); + + callsiteSetupCodeStream.Emit(ILOpcode.calli, emitter.NewToken(nativeSig)); + + // if the SetLastError flag is set in UnmanagedFunctionPointerAttribute, call the PInvokeMarshal.SaveLastError + // so that last error can be used later by calling Marshal.GetLastPInvokeError + if (_flags.SetLastError) + { + callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( + InteropTypes.GetPInvokeMarshal(context) + .GetKnownMethod("SaveLastError", null))); + } + } + else + { + Debug.Fail("Unexpected DelegateMarshallingMethodThunkKind"); + } + } + + private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams) + { + ILEmitter emitter = ilCodeStreams.Emitter; + ILCodeStream fnptrLoadStream = ilCodeStreams.FunctionPointerLoadStream; + ILCodeStream callsiteSetupCodeStream = ilCodeStreams.CallsiteSetupCodeStream; + TypeSystemContext context = _targetMethod.Context; + + bool isHRSwappedRetVal = !_flags.PreserveSig && !_targetMethod.Signature.ReturnType.IsVoid; + TypeDesc nativeReturnType = _flags.PreserveSig ? _marshallers[0].NativeParameterType : context.GetWellKnownType(WellKnownType.Int32); + TypeDesc[] nativeParameterTypes = new TypeDesc[isHRSwappedRetVal ? _marshallers.Length : _marshallers.Length - 1]; + + // if the SetLastError flag is set in DllImport, clear the error code before doing P/Invoke + if (_flags.SetLastError) + { + callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( + InteropTypes.GetPInvokeMarshal(context).GetKnownMethod("ClearLastError", null))); + } + + for (int i = 1; i < _marshallers.Length; i++) + { + nativeParameterTypes[i - 1] = _marshallers[i].NativeParameterType; + } + + if (isHRSwappedRetVal) + { + nativeParameterTypes[_marshallers.Length - 1] = _marshallers[0].NativeParameterType; + } + + if (!_pInvokeILEmitterConfiguration.GenerateDirectCall(_targetMethod, out _)) + { + MetadataType lazyHelperType = context.GetHelperType("InteropHelpers"); + FieldDesc lazyDispatchCell = _interopStateManager.GetPInvokeLazyFixupField(_targetMethod); + + fnptrLoadStream.Emit(ILOpcode.ldsflda, emitter.NewToken(lazyDispatchCell)); + fnptrLoadStream.Emit(ILOpcode.call, emitter.NewToken(lazyHelperType + .GetKnownMethod("ResolvePInvoke", null))); + + MethodSignatureFlags unmanagedCallingConvention = _flags.UnmanagedCallingConvention; + if (unmanagedCallingConvention == MethodSignatureFlags.None) + unmanagedCallingConvention = MethodSignatureFlags.UnmanagedCallingConvention; + + EmbeddedSignatureData[] embeddedSignatureData = null; + if (_targetMethod.HasCustomAttribute("System.Runtime.InteropServices", "SuppressGCTransitionAttribute")) + { + embeddedSignatureData = new EmbeddedSignatureData[] + { + new EmbeddedSignatureData() + { + index = MethodSignature.IndexOfCustomModifiersOnReturnType, + kind = EmbeddedSignatureDataKind.OptionalCustomModifier, + type = context.SystemModule.GetKnownType("System.Runtime.CompilerServices", "CallConvSuppressGCTransition") + } + }; + } + + MethodSignature nativeSig = new MethodSignature( + _targetMethod.Signature.Flags | unmanagedCallingConvention, 0, nativeReturnType, + nativeParameterTypes, embeddedSignatureData); + + ILLocalVariable vNativeFunctionPointer = emitter.NewLocal(context + .GetWellKnownType(WellKnownType.IntPtr)); + + fnptrLoadStream.EmitStLoc(vNativeFunctionPointer); + callsiteSetupCodeStream.EmitLdLoc(vNativeFunctionPointer); + callsiteSetupCodeStream.Emit(ILOpcode.calli, emitter.NewToken(nativeSig)); + } + else + { + // Eager call + MethodSignature nativeSig = new MethodSignature( + _targetMethod.Signature.Flags, 0, nativeReturnType, nativeParameterTypes); + + MethodDesc nativeMethod = + new PInvokeTargetNativeMethod(_targetMethod, nativeSig); + + callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken(nativeMethod)); + } + + if (!_flags.PreserveSig) + { + callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( + InteropTypes.GetMarshal(context) + .GetKnownMethod("ThrowExceptionForHR", null))); + } + + // if the SetLastError flag is set in DllImport, call the PInvokeMarshal.SaveLastError + // so that last error can be used later by calling Marshal.GetLastPInvokeError + if (_flags.SetLastError) + { + callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( + InteropTypes.GetPInvokeMarshal(context) + .GetKnownMethod("SaveLastError", null))); + } + } + + private void EmitCalli(PInvokeILCodeStreams ilCodeStreams, CalliMarshallingMethodThunk calliThunk) + { + ILEmitter emitter = ilCodeStreams.Emitter; + ILCodeStream callsiteSetupCodeStream = ilCodeStreams.CallsiteSetupCodeStream; + + TypeDesc nativeReturnType = _marshallers[0].NativeParameterType; + TypeDesc[] nativeParameterTypes = new TypeDesc[_marshallers.Length - 1]; + + for (int i = 1; i < _marshallers.Length; i++) + { + nativeParameterTypes[i - 1] = _marshallers[i].NativeParameterType; + } + + MethodSignature nativeSig = new MethodSignature( + calliThunk.TargetSignature.Flags, 0, nativeReturnType, + nativeParameterTypes, calliThunk.TargetSignature.GetEmbeddedSignatureData()); + + callsiteSetupCodeStream.EmitLdArg(calliThunk.TargetSignature.Length); + callsiteSetupCodeStream.Emit(ILOpcode.calli, emitter.NewToken(nativeSig)); + } + + private MethodIL EmitIL() + { + PInvokeILCodeStreams pInvokeILCodeStreams = new PInvokeILCodeStreams(); + ILEmitter emitter = pInvokeILCodeStreams.Emitter; + ILCodeStream marshallingCodestream = pInvokeILCodeStreams.MarshallingCodeStream; + ILCodeStream unmarshallingCodestream = pInvokeILCodeStreams.UnmarshallingCodestream; + ILCodeStream cleanupCodestream = pInvokeILCodeStreams.CleanupCodeStream; + + // Marshalling is wrapped in a finally block to guarantee cleanup + ILExceptionRegionBuilder tryFinally = emitter.NewFinallyRegion(); + + marshallingCodestream.BeginTry(tryFinally); + cleanupCodestream.BeginHandler(tryFinally); + + // Marshal the arguments + bool isHRSwappedRetVal = !_flags.PreserveSig && !_targetMethod.Signature.ReturnType.IsVoid; + for (int i = isHRSwappedRetVal ? 1 : 0; i < _marshallers.Length; i++) + { + _marshallers[i].EmitMarshallingIL(pInvokeILCodeStreams); + } + + if (isHRSwappedRetVal) + { + _marshallers[0].EmitMarshallingIL(pInvokeILCodeStreams); + } + + // make the call + switch (_targetMethod) + { + case DelegateMarshallingMethodThunk delegateMethod: + EmitDelegateCall(delegateMethod, pInvokeILCodeStreams); + break; + case CalliMarshallingMethodThunk calliMethod: + EmitCalli(pInvokeILCodeStreams, calliMethod); + break; + default: + EmitPInvokeCall(pInvokeILCodeStreams); + break; + } + + ILCodeLabel lReturn = emitter.NewCodeLabel(); + unmarshallingCodestream.Emit(ILOpcode.leave, lReturn); + unmarshallingCodestream.EndTry(tryFinally); + + cleanupCodestream.Emit(ILOpcode.endfinally); + cleanupCodestream.EndHandler(tryFinally); + + cleanupCodestream.EmitLabel(lReturn); + + _marshallers[0].LoadReturnValue(cleanupCodestream); + cleanupCodestream.Emit(ILOpcode.ret); + + return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(_targetMethod), IsStubRequired()); + } + + public static MethodIL EmitIL(MethodDesc method, + PInvokeILEmitterConfiguration pinvokeILEmitterConfiguration, + InteropStateManager interopStateManager) + { + try + { + return new PInvokeILEmitter(method, pinvokeILEmitterConfiguration, interopStateManager) + .EmitIL(); + } + catch (NotSupportedException) + { + string message = "Method '" + method.ToString() + + "' requires marshalling that is not yet supported by this compiler."; + return MarshalHelpers.EmitExceptionBody(message, method); + } + catch (InvalidProgramException ex) + { + Debug.Assert(!String.IsNullOrEmpty(ex.Message)); + return MarshalHelpers.EmitExceptionBody(ex.Message, method); + } + } + + private bool IsStubRequired() + { + Debug.Assert(_targetMethod.IsPInvoke || _targetMethod is DelegateMarshallingMethodThunk || _targetMethod is CalliMarshallingMethodThunk); + + if (_targetMethod is DelegateMarshallingMethodThunk) + { + return true; + } + + // The configuration can be null if this is delegate or calli marshalling + if (_pInvokeILEmitterConfiguration != null) + { + if (!_pInvokeILEmitterConfiguration.GenerateDirectCall(_targetMethod, out _)) + { + return true; + } + } + + if (_flags.SetLastError) + { + return true; + } + + if (!_flags.PreserveSig) + { + return true; + } + + for (int i = 0; i < _marshallers.Length; i++) + { + if (_marshallers[i].IsMarshallingRequired()) + return true; + } + return false; + } + } + + public sealed class PInvokeILStubMethodIL : ILStubMethodIL + { + public bool IsStubRequired { get; } + public PInvokeILStubMethodIL(ILStubMethodIL methodIL, bool isStubRequired) : base(methodIL) + { + IsStubRequired = isStubRequired; + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeLazyFixupField.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeLazyFixupField.Sorting.cs new file mode 100644 index 00000000000000..edfccf89890e67 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeLazyFixupField.Sorting.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + partial class PInvokeLazyFixupField + { + protected internal override int ClassCode => -1784477702; + + protected internal override int CompareToImpl(FieldDesc other, TypeSystemComparer comparer) + { + return comparer.Compare(_targetMethod, ((PInvokeLazyFixupField)other)._targetMethod); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeLazyFixupField.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeLazyFixupField.cs new file mode 100644 index 00000000000000..c2a44b01653a59 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeLazyFixupField.cs @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + /// + /// Synthetic RVA static field that represents PInvoke fixup cell. The RVA data is + /// backed by a small data structure generated on the fly from the + /// carried by the instance of this class. + /// + public sealed partial class PInvokeLazyFixupField : FieldDesc + { + private readonly DefType _owningType; + private readonly MethodDesc _targetMethod; + + public PInvokeLazyFixupField(DefType owningType, MethodDesc targetMethod) + { + Debug.Assert(targetMethod.IsPInvoke); + _owningType = owningType; + _targetMethod = targetMethod; + } + + public MethodDesc TargetMethod + { + get + { + return _targetMethod; + } + } + + public PInvokeMetadata PInvokeMetadata + { + get + { + return _targetMethod.GetPInvokeMethodMetadata(); + } + } + + public override TypeSystemContext Context + { + get + { + return _targetMethod.Context; + } + } + + public override TypeDesc FieldType + { + get + { + return Context.GetHelperType("InteropHelpers").GetNestedType("MethodFixupCell"); + } + } + + public override bool HasRva + { + get + { + return true; + } + } + + public override bool IsInitOnly + { + get + { + return false; + } + } + + public override bool IsLiteral + { + get + { + return false; + } + } + + public override bool IsStatic + { + get + { + return true; + } + } + + public override bool IsThreadStatic + { + get + { + return false; + } + } + + public override DefType OwningType + { + get + { + return _owningType; + } + } + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) + { + return false; + } + + public override string Name + { + get + { + return _targetMethod.Name; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/StructMarshallingThunk.Mangling.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/StructMarshallingThunk.Mangling.cs new file mode 100644 index 00000000000000..68506e92117f40 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/StructMarshallingThunk.Mangling.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + public partial class StructMarshallingThunk : IPrefixMangledType + { + TypeDesc IPrefixMangledType.BaseType + { + get + { + return ManagedType; + } + } + + string IPrefixMangledType.Prefix + { + get + { + return NamePrefix; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/StructMarshallingThunk.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/StructMarshallingThunk.Sorting.cs new file mode 100644 index 00000000000000..f2eee55f961086 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/StructMarshallingThunk.Sorting.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + // Functionality related to deterministic ordering of types + partial class StructMarshallingThunk + { + protected internal override int ClassCode => 340834018; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (StructMarshallingThunk)other; + + int result = ThunkType - otherMethod.ThunkType; + if (result != 0) + return result; + + return comparer.Compare(ManagedType, otherMethod.ManagedType); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/StructMarshallingThunk.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/StructMarshallingThunk.cs new file mode 100644 index 00000000000000..cf346c06852173 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/StructMarshallingThunk.cs @@ -0,0 +1,334 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + public enum StructMarshallingThunkType : byte + { + ManagedToNative = 1, + NativeToManaged = 2, + Cleanup = 4 + } + + public struct InlineArrayCandidate + { + public readonly MetadataType ElementType; + public readonly uint Length; + + public InlineArrayCandidate(MetadataType type, uint length) + { + ElementType = type; + Length = length; + } + } + + public partial class StructMarshallingThunk : ILStubMethod + { + internal readonly MetadataType ManagedType; + internal readonly NativeStructType NativeType; + internal readonly StructMarshallingThunkType ThunkType; + private InteropStateManager _interopStateManager; + private TypeDesc _owningType; + + public StructMarshallingThunk(TypeDesc owningType, MetadataType managedType, StructMarshallingThunkType thunkType, InteropStateManager interopStateManager) + { + _owningType = owningType; + ManagedType = managedType; + _interopStateManager = interopStateManager; + NativeType = _interopStateManager.GetStructMarshallingNativeType(managedType); + ThunkType = thunkType; + } + + public override TypeSystemContext Context + { + get + { + return ManagedType.Context; + } + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + private MethodSignature _signature; + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + TypeDesc[] parameters = null; + switch (ThunkType) + { + case StructMarshallingThunkType.ManagedToNative: + parameters = new TypeDesc[] { + ManagedType.IsValueType ? (TypeDesc)ManagedType.MakeByRefType() : ManagedType, + NativeType.MakeByRefType() + }; + break; + case StructMarshallingThunkType.NativeToManaged: + parameters = new TypeDesc[] { + NativeType.MakeByRefType(), + ManagedType.IsValueType ? (TypeDesc)ManagedType.MakeByRefType() : ManagedType + }; + break; + case StructMarshallingThunkType.Cleanup: + parameters = new TypeDesc[] { + NativeType.MakeByRefType() + }; + break; + default: + System.Diagnostics.Debug.Fail("Unexpected Struct marshalling thunk type"); + break; + } + _signature = new MethodSignature(MethodSignatureFlags.Static, 0, Context.GetWellKnownType(WellKnownType.Void), parameters); + } + return _signature; + } + } + + private string NamePrefix + { + get + { + switch (ThunkType) + { + case StructMarshallingThunkType.ManagedToNative: + return "ManagedToNative"; + case StructMarshallingThunkType.NativeToManaged: + return "NativeToManaged"; + case StructMarshallingThunkType.Cleanup: + return "Cleanup"; + default: + System.Diagnostics.Debug.Fail("Unexpected Struct marshalling thunk type"); + return string.Empty; + } + } + } + + public override string Name + { + get + { + return NamePrefix + "__" + ((MetadataType)ManagedType).Name; + } + } + + private Marshaller[] InitializeMarshallers() + { + Debug.Assert(_interopStateManager != null); + + int numInstanceFields = 0; + foreach (var field in ManagedType.GetFields()) + { + if (field.IsStatic) + continue; + numInstanceFields++; + } + + Marshaller[] marshallers = new Marshaller[numInstanceFields]; + + PInvokeFlags flags = new PInvokeFlags(); + if (ManagedType.PInvokeStringFormat == PInvokeStringFormat.UnicodeClass || ManagedType.PInvokeStringFormat == PInvokeStringFormat.AutoClass) + { + flags.CharSet = CharSet.Unicode; + } + else + { + flags.CharSet = CharSet.Ansi; + } + + int index = 0; + + foreach (FieldDesc field in ManagedType.GetFields()) + { + if (field.IsStatic) + { + continue; + } + + marshallers[index] = Marshaller.CreateMarshaller(field.FieldType, + null, /* parameterIndex */ + null, /* customModifierData */ + MarshallerType.Field, + field.GetMarshalAsDescriptor(), + (ThunkType == StructMarshallingThunkType.NativeToManaged) ? MarshalDirection.Reverse : MarshalDirection.Forward, + marshallers, + _interopStateManager, + index, + flags, + isIn: true, /* Struct fields are considered as IN within the helper*/ + isOut: false, + isReturn: false); + index++; + } + + return marshallers; + } + + private MethodIL EmitMarshallingIL(PInvokeILCodeStreams pInvokeILCodeStreams) + { + Marshaller[] marshallers = InitializeMarshallers(); + + ILEmitter emitter = pInvokeILCodeStreams.Emitter; + + IEnumerator nativeEnumerator = NativeType.GetFields().GetEnumerator(); + + int index = 0; + foreach (var managedField in ManagedType.GetFields()) + { + if (managedField.IsStatic) + { + continue; + } + + bool notEmpty = nativeEnumerator.MoveNext(); + Debug.Assert(notEmpty == true); + + var nativeField = nativeEnumerator.Current; + Debug.Assert(nativeField != null); + bool isInlineArray = nativeField.FieldType is InlineArrayType; + // + // Field marshallers expects the value of the fields to be + // loaded on the stack. We load the value on the stack + // before calling the marshallers. + // Only exception is ByValArray marshallers. Since they can + // only be used for field marshalling, they load/store values + // directly from arguments. + // + + if (isInlineArray) + { + var byValMarshaller = marshallers[index++] as ByValArrayMarshaller; + + Debug.Assert(byValMarshaller != null); + + byValMarshaller.EmitMarshallingIL(pInvokeILCodeStreams, managedField, nativeField); + } + else + { + if (ThunkType == StructMarshallingThunkType.ManagedToNative) + { + LoadFieldValueFromArg(0, managedField, pInvokeILCodeStreams); + } + else if (ThunkType == StructMarshallingThunkType.NativeToManaged) + { + LoadFieldValueFromArg(0, nativeField, pInvokeILCodeStreams); + } + + marshallers[index++].EmitMarshallingIL(pInvokeILCodeStreams); + + if (ThunkType == StructMarshallingThunkType.ManagedToNative) + { + StoreFieldValueFromArg(1, nativeField, pInvokeILCodeStreams); + } + else if (ThunkType == StructMarshallingThunkType.NativeToManaged) + { + StoreFieldValueFromArg(1, managedField, pInvokeILCodeStreams); + } + } + } + + Debug.Assert(!nativeEnumerator.MoveNext()); + + pInvokeILCodeStreams.UnmarshallingCodestream.Emit(ILOpcode.ret); + return emitter.Link(this); + } + + private MethodIL EmitCleanupIL(PInvokeILCodeStreams pInvokeILCodeStreams) + { + Marshaller[] marshallers = InitializeMarshallers(); + ILEmitter emitter = pInvokeILCodeStreams.Emitter; + ILCodeStream codeStream = pInvokeILCodeStreams.MarshallingCodeStream; + IEnumerator nativeEnumerator = NativeType.GetFields().GetEnumerator(); + int index = 0; + foreach (var managedField in ManagedType.GetFields()) + { + if (managedField.IsStatic) + { + continue; + } + + bool notEmpty = nativeEnumerator.MoveNext(); + Debug.Assert(notEmpty == true); + + var nativeField = nativeEnumerator.Current; + Debug.Assert(nativeField != null); + + if (marshallers[index].CleanupRequired) + { + LoadFieldValueFromArg(0, nativeField, pInvokeILCodeStreams); + marshallers[index].EmitElementCleanup(codeStream, emitter); + } + index++; + } + + pInvokeILCodeStreams.UnmarshallingCodestream.Emit(ILOpcode.ret); + return emitter.Link(this); + } + + public override MethodIL EmitIL() + { + try + { + PInvokeILCodeStreams pInvokeILCodeStreams = new PInvokeILCodeStreams(); + + if (ThunkType == StructMarshallingThunkType.Cleanup) + { + return EmitCleanupIL(pInvokeILCodeStreams); + } + else + { + return EmitMarshallingIL(pInvokeILCodeStreams); + } + } + catch (NotSupportedException) + { + string message = "Struct '" + ((MetadataType)ManagedType).Name + + "' requires marshalling that is not yet supported by this compiler."; + return MarshalHelpers.EmitExceptionBody(message, this); + } + catch (InvalidProgramException ex) + { + Debug.Assert(!String.IsNullOrEmpty(ex.Message)); + return MarshalHelpers.EmitExceptionBody(ex.Message, this); + } + } + + /// + /// Loads the value of field of a struct at argument index argIndex to stack + /// + private void LoadFieldValueFromArg(int argIndex, FieldDesc field, PInvokeILCodeStreams pInvokeILCodeStreams) + { + ILCodeStream stream = pInvokeILCodeStreams.MarshallingCodeStream; + ILEmitter emitter = pInvokeILCodeStreams.Emitter; + stream.EmitLdArg(argIndex); + stream.Emit(ILOpcode.ldfld, emitter.NewToken(field)); + } + + private void StoreFieldValueFromArg(int argIndex, FieldDesc field, PInvokeILCodeStreams pInvokeILCodeStreams) + { + ILCodeStream stream = pInvokeILCodeStreams.MarshallingCodeStream; + ILEmitter emitter = pInvokeILCodeStreams.Emitter; + Internal.IL.Stubs.ILLocalVariable var = emitter.NewLocal(field.FieldType); + + stream.EmitStLoc(var); + + stream.EmitLdArg(argIndex); + stream.EmitLdLoc(var); + stream.Emit(ILOpcode.stfld, emitter.NewToken(field)); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/TypeGetTypeMethodThunk.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/TypeGetTypeMethodThunk.Sorting.cs new file mode 100644 index 00000000000000..c99a3ff3b40fa8 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/TypeGetTypeMethodThunk.Sorting.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + // Functionality related to deterministic ordering of types + partial class TypeGetTypeMethodThunk + { + protected override int ClassCode => -949164050; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (TypeGetTypeMethodThunk)other; + int result = StringComparer.Ordinal.Compare(DefaultAssemblyName, otherMethod.DefaultAssemblyName); + if (result != 0) + return result; + + return comparer.Compare(Signature, otherMethod.Signature); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/TypeGetTypeMethodThunk.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/TypeGetTypeMethodThunk.cs new file mode 100644 index 00000000000000..be09098c5dab6a --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/TypeGetTypeMethodThunk.cs @@ -0,0 +1,177 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + /// + /// Helps model the behavior of calls to various Type.GetType overloads. Type.GetType is required to search + /// the calling assembly for a matching type if the type name supplied by the user code was not assembly qualified. + /// This thunk calls a helper method, passing it a string for what should be considered the "calling assembly". + /// + internal partial class TypeGetTypeMethodThunk : ILStubMethod + { + private readonly MethodDesc _helperMethod; + + public TypeGetTypeMethodThunk(TypeDesc owningType, MethodSignature signature, MethodDesc helperMethod, string defaultAssemblyName) + { + OwningType = owningType; + Signature = signature; + DefaultAssemblyName = defaultAssemblyName; + + _helperMethod = helperMethod; + } + + public override TypeSystemContext Context + { + get + { + return OwningType.Context; + } + } + + public override string Name + { + get + { + return $"{_helperMethod.Name}_{Signature.Length}_{DefaultAssemblyName}"; + } + } + + public override TypeDesc OwningType + { + get; + } + + public override MethodSignature Signature + { + get; + } + + public string DefaultAssemblyName + { + get; + } + + public override MethodIL EmitIL() + { + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + + Debug.Assert(Signature[0] == _helperMethod.Signature[0]); + codeStream.EmitLdArg(0); + + Debug.Assert(_helperMethod.Signature[1].IsString); + codeStream.Emit(ILOpcode.ldstr, emit.NewToken(DefaultAssemblyName)); + + for (int i = 2; i < _helperMethod.Signature.Length; i++) + { + // The helper method could be expecting more arguments than what we have - check for that + // The thunk represents one of the 6 possible overloads: + // (String), (String, bool), (String, bool, bool) + // (String, Func<...>, Func<...>), (String, Func<...>, Func<...>, bool), (String, Func<...>, Func<...>, bool, bool) + // We only need 2 helpers to support all 6 overloads. The default value for the bools is false. + + if (i - 1 < Signature.Length) + { + // Pass user's parameter + Debug.Assert(_helperMethod.Signature[i] == Signature[i - 1]); + codeStream.EmitLdArg(i - 1); + } + else + { + // Pass a default value + Debug.Assert(_helperMethod.Signature[i].IsWellKnownType(WellKnownType.Boolean)); + codeStream.EmitLdc(0); + } + } + + codeStream.Emit(ILOpcode.call, emit.NewToken(_helperMethod)); + codeStream.Emit(ILOpcode.ret); + + return emit.Link(this); + } + } + + internal class TypeGetTypeMethodThunkCache + { + private TypeDesc _owningTypeForThunks; + private Unifier _cache; + + public TypeGetTypeMethodThunkCache(TypeDesc owningTypeForThunks) + { + _owningTypeForThunks = owningTypeForThunks; + _cache = new Unifier(this); + } + + public MethodDesc GetHelper(MethodDesc getTypeOverload, string defaultAssemblyName) + { + return _cache.GetOrCreateValue(new Key(defaultAssemblyName, getTypeOverload)); + } + + private struct Key + { + public readonly string DefaultAssemblyName; + public readonly MethodDesc GetTypeOverload; + + public Key(string defaultAssemblyName, MethodDesc getTypeOverload) + { + DefaultAssemblyName = defaultAssemblyName; + GetTypeOverload = getTypeOverload; + } + } + + private class Unifier : LockFreeReaderHashtable + { + private TypeGetTypeMethodThunkCache _parent; + + public Unifier(TypeGetTypeMethodThunkCache parent) + { + _parent = parent; + } + + protected override int GetKeyHashCode(Key key) + { + return key.DefaultAssemblyName.GetHashCode() ^ key.GetTypeOverload.Signature.GetHashCode(); + } + protected override int GetValueHashCode(TypeGetTypeMethodThunk value) + { + return value.DefaultAssemblyName.GetHashCode() ^ value.Signature.GetHashCode(); + } + protected override bool CompareKeyToValue(Key key, TypeGetTypeMethodThunk value) + { + return key.DefaultAssemblyName == value.DefaultAssemblyName && + key.GetTypeOverload.Signature.Equals(value.Signature); + } + protected override bool CompareValueToValue(TypeGetTypeMethodThunk value1, TypeGetTypeMethodThunk value2) + { + return value1.DefaultAssemblyName == value2.DefaultAssemblyName && + value1.Signature.Equals(value2.Signature); + } + protected override TypeGetTypeMethodThunk CreateValueFromKey(Key key) + { + TypeSystemContext contex = key.GetTypeOverload.Context; + + // This will be one of the 6 possible overloads: + // (String), (String, bool), (String, bool, bool) + // (String, Func<...>, Func<...>), (String, Func<...>, Func<...>, bool), (String, Func<...>, Func<...>, bool, bool) + + // We only need 2 helpers to support this. Use the second parameter to pick the right one. + + string helperName; + MethodSignature signature = key.GetTypeOverload.Signature; + if (signature.Length > 1 && signature[1].HasInstantiation) + helperName = "ExtensibleGetType"; + else + helperName = "GetType"; + + MethodDesc helper = contex.GetHelperEntryPoint("ReflectionHelpers", helperName); + + return new TypeGetTypeMethodThunk(_parent._owningTypeForThunks, signature, helper, key.DefaultAssemblyName); + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/TypeSystemThrowingILEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/TypeSystemThrowingILEmitter.cs new file mode 100644 index 00000000000000..ca24e143856b32 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/TypeSystemThrowingILEmitter.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + /// + /// Helper class to generate method bodies that throw exceptions for methods that can't be compiled + /// for various reasons. The exception thrown is same (or similar) to the one an attempt to JIT-compile + /// (or intepret) the method on a just-in-time compiled runtime would generate. + /// + internal static class TypeSystemThrowingILEmitter + { + public static MethodIL EmitIL(MethodDesc methodThatShouldThrow, TypeSystemException exception) + { + TypeSystemContext context = methodThatShouldThrow.Context; + + MethodDesc helper; + + Type exceptionType = exception.GetType(); + if (exceptionType == typeof(TypeSystemException.TypeLoadException)) + { + // + // There are two ThrowTypeLoadException helpers. Find the one which matches the number of + // arguments "exception" was initialized with. + // + helper = context.GetHelperEntryPoint("ThrowHelpers", "ThrowTypeLoadException"); + + if (helper.Signature.Length != exception.Arguments.Count + 1) + { + helper = context.GetHelperEntryPoint("ThrowHelpers", "ThrowTypeLoadExceptionWithArgument"); + } + } + else if (exceptionType == typeof(TypeSystemException.MissingFieldException)) + { + helper = context.GetHelperEntryPoint("ThrowHelpers", "ThrowMissingFieldException"); + } + else if (exceptionType == typeof(TypeSystemException.MissingMethodException)) + { + helper = context.GetHelperEntryPoint("ThrowHelpers", "ThrowMissingMethodException"); + } + else if (exceptionType == typeof(TypeSystemException.FileNotFoundException)) + { + helper = context.GetHelperEntryPoint("ThrowHelpers", "ThrowFileNotFoundException"); + } + else if (exceptionType == typeof(TypeSystemException.InvalidProgramException)) + { + // + // There are two ThrowInvalidProgramException helpers. Find the one which matches the number of + // arguments "exception" was initialized with. + // + + helper = context.GetHelperEntryPoint("ThrowHelpers", "ThrowInvalidProgramException"); + + if (helper.Signature.Length != exception.Arguments.Count + 1) + { + helper = context.GetHelperEntryPoint("ThrowHelpers", "ThrowInvalidProgramExceptionWithArgument"); + } + } + else if (exceptionType == typeof(TypeSystemException.BadImageFormatException)) + { + helper = context.GetHelperEntryPoint("ThrowHelpers", "ThrowBadImageFormatException"); + } + else if (exceptionType == typeof(TypeSystemException.MarshalDirectiveException)) + { + helper = context.GetHelperEntryPoint("ThrowHelpers", "ThrowMarshalDirectiveException"); + } + else + { + throw new NotImplementedException(); + } + + Debug.Assert(helper.Signature.Length == exception.Arguments.Count + 1); + + var emitter = new ILEmitter(); + var codeStream = emitter.NewCodeStream(); + + var infinityLabel = emitter.NewCodeLabel(); + codeStream.EmitLabel(infinityLabel); + + codeStream.EmitLdc((int)exception.StringID); + + foreach (var arg in exception.Arguments) + { + codeStream.Emit(ILOpcode.ldstr, emitter.NewToken(arg)); + } + + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + // The call will never return, but we still need to emit something. Emit a jump so that + // we don't have to bother balancing the stack if the method returns something. + codeStream.Emit(ILOpcode.br, infinityLabel); + + return emitter.Link(methodThatShouldThrow); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.Sorting.cs new file mode 100644 index 00000000000000..882a4419e2e242 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.Sorting.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + partial class ValueTypeGetFieldHelperMethodOverride + { + protected internal override int ClassCode => 2036839816; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (ValueTypeGetFieldHelperMethodOverride)other; + + return comparer.Compare(_owningType, otherMethod._owningType); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs new file mode 100644 index 00000000000000..a324de7bb222be --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs @@ -0,0 +1,153 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + /// + /// Synthetic method override of "int ValueType.__GetFieldHelper(Int32, out EETypePtr)". This method is injected + /// into all value types that cannot have their Equals(object) and GetHashCode() methods operate on individual + /// bytes. The purpose of the override is to provide access to the value types' fields and their types. + /// + public sealed partial class ValueTypeGetFieldHelperMethodOverride : ILStubMethod + { + private DefType _owningType; + private MethodSignature _signature; + + internal ValueTypeGetFieldHelperMethodOverride(DefType owningType) + { + _owningType = owningType; + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + TypeSystemContext context = _owningType.Context; + TypeDesc int32Type = context.GetWellKnownType(WellKnownType.Int32); + TypeDesc eeTypePtrType = context.SystemModule.GetKnownType("System", "EETypePtr"); + + _signature = new MethodSignature(0, 0, int32Type, new[] { + int32Type, + eeTypePtrType.MakeByRefType() + }); + } + + return _signature; + } + } + + public override MethodIL EmitIL() + { + TypeDesc owningType = _owningType.InstantiateAsOpen(); + + ILEmitter emitter = new ILEmitter(); + + TypeDesc eeTypePtrType = Context.SystemModule.GetKnownType("System", "EETypePtr"); + MethodDesc eeTypePtrOfMethod = eeTypePtrType.GetKnownMethod("EETypePtrOf", null); + ILToken eeTypePtrToken = emitter.NewToken(eeTypePtrType); + + var switchStream = emitter.NewCodeStream(); + var getFieldStream = emitter.NewCodeStream(); + + ArrayBuilder fieldGetters = new ArrayBuilder(); + foreach (FieldDesc field in owningType.GetFields()) + { + if (field.IsStatic) + continue; + + ILCodeLabel label = emitter.NewCodeLabel(); + fieldGetters.Add(label); + + getFieldStream.EmitLabel(label); + getFieldStream.EmitLdArg(2); + + // We need something we can instantiate EETypePtrOf over. Also, the classlib + // code doesn't handle pointers. + TypeDesc boxableFieldType = field.FieldType; + if (boxableFieldType.IsPointer || boxableFieldType.IsFunctionPointer) + boxableFieldType = Context.GetWellKnownType(WellKnownType.IntPtr); + + // The fact that the type is a reference type is sufficient for the callers. + // Don't unnecessarily create an MethodTable for the field type. + if (!boxableFieldType.IsSignatureVariable && !boxableFieldType.IsValueType) + boxableFieldType = Context.GetWellKnownType(WellKnownType.Object); + + // If this is an enum, it's okay to Equals/GetHashCode the underlying type. + // Don't unnecessarily create an MethodTable for the enum. + boxableFieldType = boxableFieldType.UnderlyingType; + + MethodDesc ptrOfField = eeTypePtrOfMethod.MakeInstantiatedMethod(boxableFieldType); + getFieldStream.Emit(ILOpcode.call, emitter.NewToken(ptrOfField)); + + getFieldStream.Emit(ILOpcode.stobj, eeTypePtrToken); + + getFieldStream.EmitLdArg(0); + getFieldStream.Emit(ILOpcode.ldflda, emitter.NewToken(field)); + + getFieldStream.EmitLdArg(0); + + getFieldStream.Emit(ILOpcode.sub); + + getFieldStream.Emit(ILOpcode.ret); + } + + if (fieldGetters.Count > 0) + { + switchStream.EmitLdArg(1); + switchStream.EmitSwitch(fieldGetters.ToArray()); + } + + switchStream.EmitLdc(fieldGetters.Count); + + switchStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + + public override Instantiation Instantiation + { + get + { + return Instantiation.Empty; + } + } + + public override bool IsVirtual + { + get + { + return true; + } + } + + public override string Name + { + get + { + return "__GetFieldHelper"; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.DelegateInfo.cs b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.DelegateInfo.cs new file mode 100644 index 00000000000000..b9a3a3e1fdbbb2 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.DelegateInfo.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.IL; + +namespace Internal.TypeSystem +{ + public abstract partial class TypeSystemContext + { + private class DelegateInfoHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(TypeDesc key) + { + return key.GetHashCode(); + } + protected override int GetValueHashCode(DelegateInfo value) + { + return value.Type.GetHashCode(); + } + protected override bool CompareKeyToValue(TypeDesc key, DelegateInfo value) + { + return Object.ReferenceEquals(key, value.Type); + } + protected override bool CompareValueToValue(DelegateInfo value1, DelegateInfo value2) + { + return Object.ReferenceEquals(value1.Type, value2.Type); + } + protected override DelegateInfo CreateValueFromKey(TypeDesc key) + { + return key.Context.CreateDelegateInfo(key); + } + } + + private DelegateInfoHashtable _delegateInfoHashtable = new DelegateInfoHashtable(); + + public DelegateInfo GetDelegateInfo(TypeDesc delegateType) + { + return _delegateInfoHashtable.GetOrCreateValue(delegateType); + } + + /// + /// Creates a for a given delegate type. + /// + protected virtual DelegateInfo CreateDelegateInfo(TypeDesc key) + { + // Type system contexts that support creating delegate infos need to override. + throw new NotSupportedException(); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.DynamicInvoke.cs b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.DynamicInvoke.cs new file mode 100644 index 00000000000000..290f11dd90a508 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.DynamicInvoke.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.IL.Stubs; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem +{ + partial class TypeSystemContext + { + private class DynamicInvokeThunkHashtable : LockFreeReaderHashtable + { + protected override bool CompareKeyToValue(DynamicInvokeMethodSignature key, DynamicInvokeMethodThunk value) => key.Equals(value.TargetSignature); + protected override bool CompareValueToValue(DynamicInvokeMethodThunk value1, DynamicInvokeMethodThunk value2) => value1.TargetSignature.Equals(value2.TargetSignature) && value1.OwningType == value2.OwningType; + protected override int GetKeyHashCode(DynamicInvokeMethodSignature key) => key.GetHashCode(); + protected override int GetValueHashCode(DynamicInvokeMethodThunk value) => value.TargetSignature.GetHashCode(); + protected override DynamicInvokeMethodThunk CreateValueFromKey(DynamicInvokeMethodSignature key) + { + return new DynamicInvokeMethodThunk(key.Context.GeneratedAssembly.GetGlobalModuleType(), key); + } + } + DynamicInvokeThunkHashtable _dynamicInvokeThunks = new DynamicInvokeThunkHashtable(); + + public MethodDesc GetDynamicInvokeThunk(DynamicInvokeMethodSignature signature) + { + return _dynamicInvokeThunks.GetOrCreateValue(signature); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.EnumMethods.cs b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.EnumMethods.cs new file mode 100644 index 00000000000000..0be009f53c7bb3 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.EnumMethods.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.IL.Stubs; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem +{ + public abstract partial class TypeSystemContext + { + private class EnumInfo + { + public TypeDesc Type => EqualsMethod.OwningType; + + public MethodDesc EqualsMethod { get; } + + public MethodDesc GetHashCodeMethod { get; } + + public EnumInfo(TypeDesc enumType) + { + Debug.Assert(enumType.IsEnum && enumType.IsTypeDefinition); + EqualsMethod = new EnumEqualsThunk(enumType); + GetHashCodeMethod = new EnumGetHashCodeThunk(enumType); + } + } + + private class EnumInfoHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(TypeDesc key) => key.GetHashCode(); + protected override int GetValueHashCode(EnumInfo value) => value.Type.GetHashCode(); + protected override bool CompareKeyToValue(TypeDesc key, EnumInfo value) => key == value.Type; + protected override bool CompareValueToValue(EnumInfo v1, EnumInfo v2) => v1.Type == v2.Type; + + protected override EnumInfo CreateValueFromKey(TypeDesc key) + { + return new EnumInfo(key); + } + } + + private EnumInfoHashtable _enumInfoHashtable = new EnumInfoHashtable(); + + public MethodDesc TryResolveConstrainedEnumMethod(TypeDesc enumType, MethodDesc virtualMethod) + { + Debug.Assert(enumType.IsEnum); + + if (!virtualMethod.OwningType.IsObject) + return null; + + // Also handle the odd case of generic enums + + TypeDesc enumTypeDefinition = enumType.GetTypeDefinition(); + EnumInfo info = _enumInfoHashtable.GetOrCreateValue(enumTypeDefinition); + + MethodDesc resolvedMethod; + if (virtualMethod.Name == "Equals") + resolvedMethod = info.EqualsMethod; + else if (virtualMethod.Name == "GetHashCode") + resolvedMethod = info.GetHashCodeMethod; + else + return null; + + if (enumType != enumTypeDefinition) + return GetMethodForInstantiatedType(resolvedMethod, (InstantiatedType)enumType); + + return resolvedMethod; + } + + protected virtual IEnumerable GetAllMethodsForEnum(TypeDesc enumType, bool virtualOnly) + { + if (virtualOnly) + { + // We devirtualize these, but they're not actually virtual. We don't want standalone method bodies + // referenced from vtables. The base implementation on System.Enum is perflectly adequate. + yield break; + } + + if (_objectEqualsMethod == null) + _objectEqualsMethod = GetWellKnownType(WellKnownType.Object).GetMethod("Equals", null); + + // If the classlib doesn't have Object.Equals, we don't need this. + if (_objectEqualsMethod == null) + yield break; + + TypeDesc enumTypeDefinition = enumType.GetTypeDefinition(); + EnumInfo info = _enumInfoHashtable.GetOrCreateValue(enumTypeDefinition); + + if (enumType != enumTypeDefinition) + { + yield return GetMethodForInstantiatedType(info.GetHashCodeMethod, (InstantiatedType)enumType); + yield return GetMethodForInstantiatedType(info.EqualsMethod, (InstantiatedType)enumType); + } + else + { + yield return info.GetHashCodeMethod; + yield return info.EqualsMethod; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.GeneratedAssembly.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.GeneratedAssembly.Sorting.cs new file mode 100644 index 00000000000000..a2490a4cb196e7 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.GeneratedAssembly.Sorting.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Internal.TypeSystem +{ + partial class TypeSystemContext + { + // Functionality related to determinstic ordering of types and members + partial class CompilerGeneratedType : MetadataType + { + protected internal override int ClassCode => -1036681447; + + protected internal override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) + { + // Should be a singleton + throw new NotSupportedException(); + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.GeneratedAssembly.cs b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.GeneratedAssembly.cs new file mode 100644 index 00000000000000..a01beba3cea869 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.GeneratedAssembly.cs @@ -0,0 +1,259 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using TypeHashingAlgorithms = Internal.NativeFormat.TypeHashingAlgorithms; +using Interlocked = System.Threading.Interlocked; +using AssemblyName = System.Reflection.AssemblyName; +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem +{ + partial class TypeSystemContext + { + private ModuleDesc _generatedAssembly; + + public ModuleDesc GeneratedAssembly + { + get + { + if (_generatedAssembly == null) + { + Interlocked.CompareExchange(ref _generatedAssembly, new CompilerGeneratedAssembly(this), null); + } + + return _generatedAssembly; + } + } + + private class CompilerGeneratedAssembly : ModuleDesc, IAssemblyDesc + { + private MetadataType _globalModuleType; + + public override IAssemblyDesc Assembly => this; + + public CompilerGeneratedAssembly(TypeSystemContext context) + : base(context, null) + { + _globalModuleType = new CompilerGeneratedType(this, ""); + } + + public override IEnumerable GetAllTypes() + { + return Array.Empty(); + } + + public override MetadataType GetGlobalModuleType() + { + return _globalModuleType; + } + + public AssemblyName GetName() + { + return new AssemblyName("System.Private.CompilerGenerated"); + } + + public override object GetType(string nameSpace, string name, NotFoundBehavior notFoundBehavior) + { + Debug.Fail("Resolving a TypeRef in the compiler generated assembly?"); + throw new NotImplementedException(); + } + } + + /// + /// A pseudo-type that owns helper methods generated by the compiler. + /// This type should never be allocated (we should never see an MethodTable for it). + /// + internal sealed partial class CompilerGeneratedType : MetadataType + { + private int _hashcode; + + public CompilerGeneratedType(ModuleDesc module, string name) + { + Module = module; + Name = name; + } + + public override TypeSystemContext Context + { + get + { + return Module.Context; + } + } + + public override string Name + { + get; + } + + public override string Namespace + { + get + { + return "Internal.CompilerGenerated"; + } + } + + public override int GetHashCode() + { + if (_hashcode != 0) + return _hashcode; + return InitializeHashCode(); + } + + private int InitializeHashCode() + { + string ns = Namespace; + var hashCodeBuilder = new TypeHashingAlgorithms.HashCodeBuilder(ns); + if (ns.Length > 0) + hashCodeBuilder.Append("."); + hashCodeBuilder.Append(Name); + _hashcode = hashCodeBuilder.ToHashCode(); + + return _hashcode; + } + + public override bool IsCanonicalSubtype(CanonicalFormKind policy) + { + Debug.Assert(!HasInstantiation, "Why is this generic?"); + return false; + } + + protected override TypeFlags ComputeTypeFlags(TypeFlags mask) + { + return TypeFlags.Class | + TypeFlags.HasGenericVarianceComputed | + TypeFlags.HasStaticConstructorComputed | + TypeFlags.HasFinalizerComputed | + TypeFlags.AttributeCacheComputed; + } + + public override ClassLayoutMetadata GetClassLayout() + { + return new ClassLayoutMetadata + { + Offsets = null, + PackingSize = 0, + Size = 0, + }; + } + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) + { + return false; + } + + public override IEnumerable GetNestedTypes() + { + return Array.Empty(); + } + + public override MetadataType GetNestedType(string name) + { + return null; + } + + protected override MethodImplRecord[] ComputeVirtualMethodImplsForType() + { + return Array.Empty(); + } + + public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name) + { + return Array.Empty(); + } + + public override ModuleDesc Module + { + get; + } + + public override PInvokeStringFormat PInvokeStringFormat + { + get + { + return PInvokeStringFormat.AutoClass; + } + } + + public override bool IsExplicitLayout + { + get + { + return false; + } + } + + public override bool IsSequentialLayout + { + get + { + return false; + } + } + + public override bool IsBeforeFieldInit + { + get + { + return false; + } + } + + + public override DefType BaseType + { + get + { + // See below + return null; + } + } + + public override MetadataType MetadataBaseType + { + get + { + // Since this type should never be allocated and only serves the purpose of grouping things, + // it can act like a type and have no base type. + return null; + } + } + + public override bool IsSealed + { + get + { + return true; + } + } + + public override bool IsAbstract + { + get + { + return false; + } + } + + public override DefType ContainingType + { + get + { + return null; + } + } + + public override DefType[] ExplicitlyImplementedInterfaces + { + get + { + return Array.Empty(); + } + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.ValueTypeMethods.cs b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.ValueTypeMethods.cs new file mode 100644 index 00000000000000..f44bf4b11cf8e2 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.ValueTypeMethods.cs @@ -0,0 +1,245 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.IL.Stubs; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem +{ + public abstract partial class TypeSystemContext + { + private MethodDesc _objectEqualsMethod; + + private class ValueTypeMethodHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(DefType key) => key.GetHashCode(); + protected override int GetValueHashCode(MethodDesc value) => value.OwningType.GetHashCode(); + protected override bool CompareKeyToValue(DefType key, MethodDesc value) => key == value.OwningType; + protected override bool CompareValueToValue(MethodDesc v1, MethodDesc v2) => v1.OwningType == v2.OwningType; + + protected override MethodDesc CreateValueFromKey(DefType key) + { + return new ValueTypeGetFieldHelperMethodOverride(key); + } + } + + private ValueTypeMethodHashtable _valueTypeMethodHashtable = new ValueTypeMethodHashtable(); + + protected virtual IEnumerable GetAllMethodsForValueType(TypeDesc valueType, bool virtualOnly) + { + TypeDesc valueTypeDefinition = valueType.GetTypeDefinition(); + + if (RequiresGetFieldHelperMethod((MetadataType)valueTypeDefinition)) + { + MethodDesc getFieldHelperMethod = _valueTypeMethodHashtable.GetOrCreateValue((DefType)valueTypeDefinition); + + if (valueType != valueTypeDefinition) + { + yield return GetMethodForInstantiatedType(getFieldHelperMethod, (InstantiatedType)valueType); + } + else + { + yield return getFieldHelperMethod; + } + } + + IEnumerable metadataMethods = virtualOnly ? valueType.GetVirtualMethods() : valueType.GetMethods(); + foreach (MethodDesc method in metadataMethods) + yield return method; + } + + private bool RequiresGetFieldHelperMethod(MetadataType valueType) + { + if (_objectEqualsMethod == null) + _objectEqualsMethod = GetWellKnownType(WellKnownType.Object).GetMethod("Equals", null); + + // If the classlib doesn't have Object.Equals, we don't need this. + if (_objectEqualsMethod == null) + return false; + + // Byref-like valuetypes cannot be boxed. + if (valueType.IsByRefLike) + return false; + + // Enums get their overrides from System.Enum. + if (valueType.IsEnum) + return false; + + // These need to provide an implementation of Equals/GetHashCode because of NaN handling. + // The helper would be useless. + if (valueType.IsWellKnownType(WellKnownType.Double) || valueType.IsWellKnownType(WellKnownType.Single)) + return false; + + return !_typeStateHashtable.GetOrCreateValue(valueType).CanCompareValueTypeBits; + } + + private class TypeState + { + private enum Flags + { + CanCompareValueTypeBits = 0x0000_0001, + CanCompareValueTypeBitsComputed = 0x0000_0002, + } + + private volatile Flags _flags; + private readonly TypeStateHashtable _hashtable; + + public TypeDesc Type { get; } + + public bool CanCompareValueTypeBits + { + get + { + Flags flags = _flags; + if ((flags & Flags.CanCompareValueTypeBitsComputed) == 0) + { + Debug.Assert(Type.IsValueType); + if (ComputeCanCompareValueTypeBits((MetadataType)Type)) + flags |= Flags.CanCompareValueTypeBits; + flags |= Flags.CanCompareValueTypeBitsComputed; + + _flags = flags; + } + return (flags & Flags.CanCompareValueTypeBits) != 0; + } + } + + public TypeState(TypeDesc type, TypeStateHashtable hashtable) + { + Type = type; + _hashtable = hashtable; + } + + private bool ComputeCanCompareValueTypeBits(MetadataType type) + { + Debug.Assert(type.IsValueType); + + if (type.ContainsGCPointers) + return false; + + if (type.IsGenericDefinition) + return false; + + OverlappingFieldTracker overlappingFieldTracker = new OverlappingFieldTracker(type); + + bool result = true; + foreach (var field in type.GetFields()) + { + if (field.IsStatic) + continue; + + if (!overlappingFieldTracker.TrackField(field)) + { + // This field overlaps with another field - can't compare memory + result = false; + break; + } + + TypeDesc fieldType = field.FieldType; + if (fieldType.IsPrimitive || fieldType.IsEnum || fieldType.IsPointer || fieldType.IsFunctionPointer) + { + TypeFlags category = fieldType.UnderlyingType.Category; + if (category == TypeFlags.Single || category == TypeFlags.Double) + { + // Double/Single have weird behaviors around negative/positive zero + result = false; + break; + } + } + else + { + // Would be a suprise if this wasn't a valuetype. We checked ContainsGCPointers above. + Debug.Assert(fieldType.IsValueType); + + MethodDesc objectEqualsMethod = fieldType.Context._objectEqualsMethod; + + // If the field overrides Equals, we can't use the fast helper because we need to call the method. + if (fieldType.FindVirtualFunctionTargetMethodOnObjectType(objectEqualsMethod).OwningType == fieldType) + { + result = false; + break; + } + + if (!_hashtable.GetOrCreateValue((MetadataType)fieldType).CanCompareValueTypeBits) + { + result = false; + break; + } + } + } + + // If there are gaps, we can't memcompare + if (result && overlappingFieldTracker.HasGaps) + result = false; + + return result; + } + } + + private class TypeStateHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(TypeDesc key) => key.GetHashCode(); + protected override int GetValueHashCode(TypeState value) => value.Type.GetHashCode(); + protected override bool CompareKeyToValue(TypeDesc key, TypeState value) => key == value.Type; + protected override bool CompareValueToValue(TypeState v1, TypeState v2) => v1.Type == v2.Type; + + protected override TypeState CreateValueFromKey(TypeDesc key) + { + return new TypeState(key, this); + } + } + private TypeStateHashtable _typeStateHashtable = new TypeStateHashtable(); + + private struct OverlappingFieldTracker + { + private bool[] _usedBytes; + + public OverlappingFieldTracker(MetadataType type) + { + _usedBytes = new bool[type.InstanceFieldSize.AsInt]; + } + + public bool TrackField(FieldDesc field) + { + int fieldBegin = field.Offset.AsInt; + + TypeDesc fieldType = field.FieldType; + + int fieldEnd; + if (fieldType.IsPointer || fieldType.IsFunctionPointer) + { + fieldEnd = fieldBegin + field.Context.Target.PointerSize; + } + else + { + Debug.Assert(fieldType.IsValueType); + fieldEnd = fieldBegin + ((DefType)fieldType).InstanceFieldSize.AsInt; + } + + for (int i = fieldBegin; i < fieldEnd; i++) + { + if (_usedBytes[i]) + return false; + _usedBytes[i] = true; + } + + return true; + } + + public bool HasGaps + { + get + { + for (int i = 0; i < _usedBytes.Length; i++) + if (!_usedBytes[i]) + return true; + + return false; + } + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/InlineArrayType.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/InlineArrayType.Sorting.cs new file mode 100644 index 00000000000000..2bcee373962bea --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/InlineArrayType.Sorting.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem.Interop +{ + // Functionality related to determinstic ordering of types + partial class InlineArrayType + { + protected internal override int ClassCode => 226817075; + + protected internal override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) + { + var otherType = (InlineArrayType)other; + int result = (int)Length - (int)otherType.Length; + if (result != 0) + return result; + + return comparer.Compare(ElementType, otherType.ElementType); + } + + partial class InlineArrayMethod + { + protected internal override int ClassCode => -1303220581; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (InlineArrayMethod)other; + + int result = _kind - otherMethod._kind; + if (result != 0) + return result; + + return comparer.CompareWithinClass(OwningType, otherMethod.OwningType); + } + } + + partial class InlineArrayField + { + protected internal override int ClassCode => 1542668652; + + protected internal override int CompareToImpl(FieldDesc other, TypeSystemComparer comparer) + { + var otherField = (InlineArrayField)other; + + return comparer.CompareWithinClass(OwningType, otherField.OwningType); + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/InlineArrayType.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/InlineArrayType.cs new file mode 100644 index 00000000000000..6462c6ee2c59c7 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/InlineArrayType.cs @@ -0,0 +1,495 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using Debug = System.Diagnostics.Debug; +using Internal.IL.Stubs; +using Internal.IL; +using System.Threading; + +namespace Internal.TypeSystem.Interop +{ + internal sealed partial class InlineArrayType : MetadataType + { + public MetadataType ElementType + { + get; + } + + public uint Length + { + get; + } + + public override ModuleDesc Module + { + get; + } + + public override string Name + { + get + { + return "_InlineArray__" + ElementType.Name + "__"+ Length; + } + } + + public override string Namespace + { + get + { + return "Internal.CompilerGenerated"; + } + } + + public override Instantiation Instantiation + { + get + { + return Instantiation.Empty; + } + } + + public override PInvokeStringFormat PInvokeStringFormat + { + get + { + return PInvokeStringFormat.AnsiClass; + } + } + + public override bool IsExplicitLayout + { + get + { + return false; + } + } + + public override bool IsSequentialLayout + { + get + { + return true; + } + } + + public override bool IsBeforeFieldInit + { + get + { + return false; + } + } + + public override DefType BaseType + { + get + { + return (DefType)Context.GetWellKnownType(WellKnownType.ValueType); + } + } + + public override MetadataType MetadataBaseType + { + get + { + return (MetadataType)Context.GetWellKnownType(WellKnownType.ValueType); + } + } + + public override bool IsSealed + { + get + { + return true; + } + } + + public override bool IsAbstract + { + get + { + return false; + } + } + + public override DefType ContainingType + { + get + { + return null; + } + } + + public override DefType[] ExplicitlyImplementedInterfaces + { + get + { + return Array.Empty(); + } + } + + public override TypeSystemContext Context + { + get + { + return ElementType.Context; + } + } + + private InteropStateManager _interopStateManager; + private MethodDesc[] _methods; + private FieldDesc[] _fields; + + public InlineArrayType(ModuleDesc owningModule, MetadataType elementType, uint length, InteropStateManager interopStateManager) + { + Debug.Assert(elementType.IsTypeDefinition); + Debug.Assert(elementType.IsValueType); + Debug.Assert(!elementType.IsGenericDefinition); + + Module = owningModule; + ElementType = elementType; + Length = length; + _interopStateManager = interopStateManager; + } + + public override ClassLayoutMetadata GetClassLayout() + { + ClassLayoutMetadata result = new ClassLayoutMetadata(); + result.PackingSize = 0; + result.Size = checked((int)Length * ElementType.GetElementSize().AsInt); + return result; + } + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) + { + return false; + } + + public override IEnumerable GetNestedTypes() + { + return Array.Empty(); + } + + public override MetadataType GetNestedType(string name) + { + return null; + } + + protected override MethodImplRecord[] ComputeVirtualMethodImplsForType() + { + return Array.Empty(); + } + + public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name) + { + return Array.Empty(); + } + + private int _hashCode; + + private void InitializeHashCode() + { + var hashCodeBuilder = new Internal.NativeFormat.TypeHashingAlgorithms.HashCodeBuilder(Namespace); + + if (Namespace.Length > 0) + { + hashCodeBuilder.Append("."); + } + + hashCodeBuilder.Append(Name); + _hashCode = hashCodeBuilder.ToHashCode(); + } + + public override int GetHashCode() + { + if (_hashCode == 0) + { + InitializeHashCode(); + } + return _hashCode; + } + + protected override TypeFlags ComputeTypeFlags(TypeFlags mask) + { + TypeFlags flags = 0; + + if ((mask & TypeFlags.HasGenericVarianceComputed) != 0) + { + flags |= TypeFlags.HasGenericVarianceComputed; + } + + if ((mask & TypeFlags.CategoryMask) != 0) + { + flags |= TypeFlags.ValueType; + } + + flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.AttributeCacheComputed; + + return flags; + } + + private void InitializeMethods() + { + MethodDesc[] methods = new MethodDesc[] { + new InlineArrayMethod(this, InlineArrayMethodKind.Getter), + new InlineArrayMethod(this, InlineArrayMethodKind.Setter), + }; + + Interlocked.CompareExchange(ref _methods, methods, null); + } + public override IEnumerable GetMethods() + { + if (_methods == null) + { + InitializeMethods(); + } + return _methods; + } + + public MethodDesc GetInlineArrayMethod(InlineArrayMethodKind kind) + { + if (_methods == null) + { + InitializeMethods(); + } + return _methods[(int)kind]; + } + + private void InitializeFields() + { + // The inline array will inherit alignment from the dummy field + FieldDesc[] fields = new FieldDesc[] { + new InlineArrayField(this) + }; + + Interlocked.CompareExchange(ref _fields, fields, null); + } + public override IEnumerable GetFields() + { + if (_fields == null) + { + InitializeFields(); + } + return _fields; + } + + private sealed partial class InlineArrayMethod : ILStubMethod + { + private InlineArrayType _owningType; + private InlineArrayMethodKind _kind; + private MethodSignature _signature; + + public InlineArrayMethod(InlineArrayType owningType, InlineArrayMethodKind kind) + { + _owningType = owningType; + _kind = kind; + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override string Name + { + get + { + if (_kind == InlineArrayMethodKind.Getter) + { + return "get_Item"; + } + else + { + return "set_Item"; + } + } + } + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + if (_kind == InlineArrayMethodKind.Getter) + { + _signature = new MethodSignature(MethodSignatureFlags.None, + genericParameterCount: 0, + returnType: _owningType.ElementType, + parameters: new TypeDesc[] { Context.GetWellKnownType(WellKnownType.Int32) }); + } + else + { + _signature = new MethodSignature(MethodSignatureFlags.None, + genericParameterCount: 0, + returnType: Context.GetWellKnownType(WellKnownType.Void), + parameters: new TypeDesc[] { Context.GetWellKnownType(WellKnownType.Int32), _owningType.ElementType }); + } + } + return _signature; + } + } + + public override MethodIL EmitIL() + { + var emitter = new ILEmitter(); + var codeStream = emitter.NewCodeStream(); + var lIntermediate = emitter.NewCodeLabel(); + var lCheck = emitter.NewCodeLabel(); + var lValid = emitter.NewCodeLabel(); + var vFlag = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Boolean)); + var elementType = _owningType.ElementType; + + // Getter: + // return ((ElementType*)(&this))[index]; + // + // Setter: + // fixed (InlineArray* pThis = &this) + //{ + // ((ElementType*)pThis)[(ulong)index] = (ElementType)value; + //} + + var vThis = emitter.NewLocal(_owningType.MakeByRefType()); + codeStream.EmitLdArg(0); + codeStream.EmitStLoc(vThis); + codeStream.EmitLdLoc(vThis); + + codeStream.EmitLdArg(1); + codeStream.Emit(ILOpcode.conv_i4); + + codeStream.Emit(ILOpcode.sizeof_, emitter.NewToken(elementType)); + + codeStream.Emit(ILOpcode.conv_i4); + codeStream.Emit(ILOpcode.mul); + codeStream.Emit(ILOpcode.conv_i); + codeStream.Emit(ILOpcode.add); + + if (_kind == InlineArrayMethodKind.Getter) + { + codeStream.EmitLdInd(elementType); + } + else + { + codeStream.EmitLdArg(2); + + codeStream.EmitStInd(elementType); + codeStream.EmitLdc(0); + codeStream.Emit(ILOpcode.conv_u); + codeStream.EmitStLoc(vThis); + } + + codeStream.Emit(ILOpcode.ret); + return emitter.Link(this); + } + } + + private sealed partial class InlineArrayField : FieldDesc + { + private InlineArrayType _owningType; + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc FieldType + { + get + { + return _owningType.ElementType; + } + } + + public override bool HasRva + { + get + { + return false; + } + } + + public override bool IsInitOnly + { + get + { + return false; + } + } + + public override bool IsLiteral + { + get + { + return false; + } + } + + public override bool IsStatic + { + get + { + return false; + } + } + + public override bool IsThreadStatic + { + get + { + return false; + } + } + + public override DefType OwningType + { + get + { + return _owningType; + } + } + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) + { + return false; + } + + public override string Name + { + get + { + return "InlineArrayField"; + } + } + + public InlineArrayField(InlineArrayType owningType) + { + _owningType = owningType; + } + } + } + + public enum InlineArrayMethodKind : byte + { + Getter = 0, + Setter = 1 + } + +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.Aot.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.Aot.cs new file mode 100644 index 00000000000000..8925f9964b093c --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.Aot.cs @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.IL; +using Internal.IL.Stubs; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem.Interop +{ + public static partial class MarshalHelpers + { + public static bool IsStructMarshallingRequired(TypeDesc typeDesc) + { + if (typeDesc is ByRefType) + { + typeDesc = typeDesc.GetParameterType(); + } + + typeDesc = typeDesc.UnderlyingType; + + // TODO: There are primitive types which require marshalling, such as bool, char. + if (typeDesc.IsPrimitive) + { + return false; + } + + MetadataType type = typeDesc as MetadataType; + if (type == null) + { + return false; + } + + // + // For struct marshalling it is required to have either Sequential + // or Explicit layout. For Auto layout the P/Invoke marshalling code + // will throw appropriate error message. + // + if (!type.HasLayout()) + return false; + + if (!type.IsValueType) + return true; + + // If it is not blittable we will need struct marshalling + return !MarshalUtils.IsBlittableType(type); + } + + internal static TypeDesc GetNativeMethodParameterType(TypeDesc type, MarshalAsDescriptor marshalAs, InteropStateManager interopStateManager, bool isReturn, bool isAnsi) + { + MarshallerKind elementMarshallerKind; + MarshallerKind marshallerKind = MarshalHelpers.GetMarshallerKind(type, + null, /* parameterIndex */ + null, /* customModifierData */ + marshalAs, + isReturn, + isAnsi, + MarshallerType.Argument, + out elementMarshallerKind); + + return GetNativeTypeFromMarshallerKind(type, + marshallerKind, + elementMarshallerKind, + interopStateManager, + marshalAs); + } + + internal static TypeDesc GetNativeStructFieldType(TypeDesc type, MarshalAsDescriptor marshalAs, InteropStateManager interopStateManager, bool isAnsi) + { + MarshallerKind elementMarshallerKind; + MarshallerKind marshallerKind = MarshalHelpers.GetMarshallerKind(type, + null, /* parameterIndex */ + null, /* customModifierData */ + marshalAs, + false, /* isReturn */ + isAnsi, /* isAnsi */ + MarshallerType.Field, + out elementMarshallerKind); + + return GetNativeTypeFromMarshallerKind(type, + marshallerKind, + elementMarshallerKind, + interopStateManager, + marshalAs); + } + + internal static InlineArrayCandidate GetInlineArrayCandidate(TypeDesc managedElementType, MarshallerKind elementMarshallerKind, InteropStateManager interopStateManager, MarshalAsDescriptor marshalAs) + { + TypeDesc nativeType = GetNativeTypeFromMarshallerKind( + managedElementType, + elementMarshallerKind, + MarshallerKind.Unknown, + interopStateManager, + null); + + var elementNativeType = nativeType as MetadataType; + if (elementNativeType == null) + { + Debug.Assert(nativeType.IsPointer || nativeType.IsFunctionPointer); + + // If it is a pointer type we will create InlineArray for IntPtr + elementNativeType = (MetadataType)managedElementType.Context.GetWellKnownType(WellKnownType.IntPtr); + } + Debug.Assert(marshalAs != null && marshalAs.SizeConst.HasValue); + + // if SizeConst is not specified, we will default to 1. + // the marshaller will throw appropriate exception + uint size = 1; + if (marshalAs.SizeConst.HasValue) + { + size = marshalAs.SizeConst.Value; + } + return new InlineArrayCandidate(elementNativeType, size); + + } + + //TODO: https://github.com/dotnet/corert/issues/2675 + // This exception messages need to localized + // TODO: Log as warning + public static MethodIL EmitExceptionBody(string message, MethodDesc method) + { + ILEmitter emitter = new ILEmitter(); + + TypeSystemContext context = method.Context; + MethodSignature ctorSignature = new MethodSignature(0, 0, context.GetWellKnownType(WellKnownType.Void), + new TypeDesc[] { context.GetWellKnownType(WellKnownType.String) }); + MethodDesc exceptionCtor = method.Context.GetWellKnownType(WellKnownType.Exception).GetKnownMethod(".ctor", ctorSignature); + + ILCodeStream codeStream = emitter.NewCodeStream(); + codeStream.Emit(ILOpcode.ldstr, emitter.NewToken(message)); + codeStream.Emit(ILOpcode.newobj, emitter.NewToken(exceptionCtor)); + codeStream.Emit(ILOpcode.throw_); + + return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(method), isStubRequired: true); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.Aot.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.Aot.cs new file mode 100644 index 00000000000000..4244a04ab1810c --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.Aot.cs @@ -0,0 +1,1132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers.Binary; +using System.Runtime.InteropServices; +using Internal.IL.Stubs; +using Internal.IL; + +using Debug = System.Diagnostics.Debug; +using ILLocalVariable = Internal.IL.Stubs.ILLocalVariable; +using Internal.TypeSystem.Ecma; +using System.Reflection.Metadata; + +namespace Internal.TypeSystem.Interop +{ + partial class Marshaller + { + protected static Marshaller CreateMarshaller(MarshallerKind kind) + { + switch (kind) + { + case MarshallerKind.Enum: + case MarshallerKind.BlittableValue: + case MarshallerKind.BlittableStruct: + case MarshallerKind.UnicodeChar: + return new BlittableValueMarshaller(); + case MarshallerKind.BlittableStructPtr: + return new BlittableStructPtrMarshaller(); + case MarshallerKind.AnsiChar: + return new AnsiCharMarshaller(); + case MarshallerKind.Array: + return new ArrayMarshaller(); + case MarshallerKind.BlittableArray: + return new BlittableArrayMarshaller(); + case MarshallerKind.Bool: + case MarshallerKind.CBool: + return new BooleanMarshaller(); + case MarshallerKind.VariantBool: + return new BooleanMarshaller((short)-1); + case MarshallerKind.AnsiString: + return new AnsiStringMarshaller(); + case MarshallerKind.UTF8String: + return new UTF8StringMarshaller(); + case MarshallerKind.UnicodeString: + return new UnicodeStringMarshaller(); + case MarshallerKind.AnsiBSTRString: + return new AnsiBSTRStringMarshaller(); + case MarshallerKind.BSTRString: + return new BSTRStringMarshaller(); + case MarshallerKind.SafeHandle: + return new SafeHandleMarshaller(); + case MarshallerKind.UnicodeStringBuilder: + return new StringBuilderMarshaller(isAnsi: false); + case MarshallerKind.AnsiStringBuilder: + return new StringBuilderMarshaller(isAnsi: true); + case MarshallerKind.VoidReturn: + return new VoidReturnMarshaller(); + case MarshallerKind.FunctionPointer: + return new DelegateMarshaller(); + case MarshallerKind.Struct: + case MarshallerKind.Decimal: + return new StructMarshaller(); + case MarshallerKind.ByValAnsiString: + return new ByValAnsiStringMarshaller(); + case MarshallerKind.ByValUnicodeString: + return new ByValUnicodeStringMarshaller(); + case MarshallerKind.ByValAnsiCharArray: + case MarshallerKind.ByValArray: + return new ByValArrayMarshaller(); + case MarshallerKind.AnsiCharArray: + return new AnsiCharArrayMarshaller(); + case MarshallerKind.HandleRef: + return new HandleRefMarshaller(); + case MarshallerKind.LayoutClass: + return new LayoutClassMarshaler(); + case MarshallerKind.LayoutClassPtr: + return new LayoutClassPtrMarshaller(); + case MarshallerKind.AsAnyA: + return new AsAnyMarshaller(isAnsi: true); + case MarshallerKind.AsAnyW: + return new AsAnyMarshaller(isAnsi: false); + case MarshallerKind.ComInterface: + return new ComInterfaceMarshaller(); + case MarshallerKind.OleDateTime: + return new OleDateTimeMarshaller(); + case MarshallerKind.OleCurrency: + return new OleCurrencyMarshaller(); + case MarshallerKind.Variant: + return new VariantMarshaller(); + default: + // ensures we don't throw during create marshaller. We will throw NSE + // during EmitIL which will be handled and an Exception method body + // will be emitted. + return new NotSupportedMarshaller(); + } + } + } + + class AnsiCharArrayMarshaller : ArrayMarshaller + { + protected override void AllocManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + var helper = Context.GetHelperEntryPoint("InteropHelpers", "AllocMemoryForAnsiCharArray"); + LoadManagedValue(codeStream); + + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + StoreNativeValue(codeStream); + } + + + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + var helper = Context.GetHelperEntryPoint("InteropHelpers", "WideCharArrayToAnsiCharArray"); + + LoadManagedValue(codeStream); + LoadNativeValue(codeStream); + codeStream.Emit(PInvokeFlags.BestFitMapping ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); + codeStream.Emit(PInvokeFlags.ThrowOnUnmappableChar ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + var helper = Context.GetHelperEntryPoint("InteropHelpers", "AnsiCharArrayToWideCharArray"); + + LoadNativeValue(codeStream); + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + } + } + + class AnsiCharMarshaller : Marshaller + { + protected override void AllocAndTransformManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + var helper = Context.GetHelperEntryPoint("InteropHelpers", "WideCharToAnsiChar"); + + LoadManagedValue(codeStream); + codeStream.Emit(PInvokeFlags.BestFitMapping ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); + codeStream.Emit(PInvokeFlags.ThrowOnUnmappableChar ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + StoreNativeValue(codeStream); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + var helper = Context.GetHelperEntryPoint("InteropHelpers", "AnsiCharToWideChar"); + + LoadNativeValue(codeStream); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + StoreManagedValue(codeStream); + } + } + + class StringBuilderMarshaller : Marshaller + { + private bool _isAnsi; + public StringBuilderMarshaller(bool isAnsi) + { + _isAnsi = isAnsi; + } + + internal override bool CleanupRequired + { + get + { + return true; + } + } + + internal override void EmitElementCleanup(ILCodeStream codeStream, ILEmitter emitter) + { + codeStream.Emit(ILOpcode.call, emitter.NewToken( + Context.GetHelperEntryPoint("InteropHelpers", "CoTaskMemFree"))); + } + + protected override void AllocNativeToManaged(ILCodeStream codeStream) + { + var emitter = _ilCodeStreams.Emitter; + var lNull = emitter.NewCodeLabel(); + + // Check for null + LoadNativeValue(codeStream); + codeStream.Emit(ILOpcode.brfalse, lNull); + + codeStream.Emit(ILOpcode.newobj, emitter.NewToken( + ManagedType.GetParameterlessConstructor())); + StoreManagedValue(codeStream); + codeStream.EmitLabel(lNull); + } + + protected override void AllocManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + string helperMethodName = _isAnsi ? "AllocMemoryForAnsiStringBuilder" : "AllocMemoryForUnicodeStringBuilder"; + var helper = Context.GetHelperEntryPoint("InteropHelpers", helperMethodName); + LoadManagedValue(codeStream); + + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + StoreNativeValue(codeStream); + } + + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + string helperMethodName = _isAnsi ? "StringBuilderToAnsiString" : "StringBuilderToUnicodeString"; + var helper = Context.GetHelperEntryPoint("InteropHelpers", helperMethodName); + + LoadManagedValue(codeStream); + LoadNativeValue(codeStream); + if (_isAnsi) + { + codeStream.Emit(PInvokeFlags.BestFitMapping ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); + codeStream.Emit(PInvokeFlags.ThrowOnUnmappableChar ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); + } + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + string helperMethodName = _isAnsi ? "AnsiStringToStringBuilder" : "UnicodeStringToStringBuilder"; + var helper = Context.GetHelperEntryPoint("InteropHelpers", helperMethodName); + LoadNativeValue(codeStream); + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken(helper)); + } + + + protected override void EmitCleanupManaged(ILCodeStream codeStream) + { + LoadNativeValue(codeStream); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( + Context.GetHelperEntryPoint("InteropHelpers", "CoTaskMemFree"))); + } + } + + class HandleRefMarshaller : Marshaller + { + protected override void AllocAndTransformManagedToNative(ILCodeStream codeStream) + { + LoadManagedAddr(codeStream); + codeStream.Emit(ILOpcode.ldfld, _ilCodeStreams.Emitter.NewToken(InteropTypes.GetHandleRef(Context).GetKnownField("_handle"))); + StoreNativeValue(codeStream); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + ThrowHelper.ThrowMarshalDirectiveException(); + } + + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + ThrowHelper.ThrowMarshalDirectiveException(); + } + + protected override void EmitCleanupManaged(ILCodeStream codeStream) + { + LoadManagedAddr(codeStream); + codeStream.Emit(ILOpcode.ldfld, _ilCodeStreams.Emitter.NewToken(InteropTypes.GetHandleRef(Context).GetKnownField("_wrapper"))); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken(InteropTypes.GetGC(Context).GetKnownMethod("KeepAlive", null))); + } + } + + class StructMarshaller : Marshaller + { + protected override void AllocManagedToNative(ILCodeStream codeStream) + { + LoadNativeAddr(codeStream); + codeStream.Emit(ILOpcode.initobj, _ilCodeStreams.Emitter.NewToken(NativeType)); + } + + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + LoadManagedAddr(codeStream); + LoadNativeAddr(codeStream); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( + InteropStateManager.GetStructMarshallingManagedToNativeThunk(ManagedType))); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + LoadNativeAddr(codeStream); + LoadManagedAddr(codeStream); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( + InteropStateManager.GetStructMarshallingNativeToManagedThunk(ManagedType))); + } + + protected override void EmitCleanupManaged(ILCodeStream codeStream) + { + // Only do cleanup if it is IN + if (!In) + { + return; + } + + LoadNativeAddr(codeStream); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( + InteropStateManager.GetStructMarshallingCleanupThunk(ManagedType))); + } + } + + class ByValArrayMarshaller : ArrayMarshaller + { + protected FieldDesc _managedField; + protected FieldDesc _nativeField; + + public void EmitMarshallingIL(PInvokeILCodeStreams codeStreams, FieldDesc managedField, FieldDesc nativeField) + { + _managedField = managedField; + _nativeField = nativeField; + EmitMarshallingIL(codeStreams); + } + + protected override void EmitElementCount(ILCodeStream codeStream, MarshalDirection direction) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + if (MarshalAsDescriptor == null || !MarshalAsDescriptor.SizeConst.HasValue) + { + throw new InvalidProgramException("SizeConst is required for ByValArray."); + } + + if (direction == MarshalDirection.Forward) + { + // In forward direction ElementCount = Min(managed.length, SizeConst); + + var vLength = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); + var lSmaller = emitter.NewCodeLabel(); + var lDone = emitter.NewCodeLabel(); + + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); + + var lNullCheck = emitter.NewCodeLabel(); + codeStream.Emit(ILOpcode.brfalse, lNullCheck); + + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); + + codeStream.Emit(ILOpcode.ldlen); + codeStream.Emit(ILOpcode.conv_i4); + codeStream.EmitStLoc(vLength); + + codeStream.EmitLabel(lNullCheck); + + Debug.Assert(MarshalAsDescriptor.SizeConst.HasValue); + int sizeConst = (int)MarshalAsDescriptor.SizeConst.Value; + + codeStream.EmitLdc(sizeConst); + codeStream.EmitLdLoc(vLength); + codeStream.Emit(ILOpcode.blt, lSmaller); + + codeStream.EmitLdLoc(vLength); + codeStream.Emit(ILOpcode.br, lDone); + + codeStream.EmitLabel(lSmaller); + codeStream.EmitLdc(sizeConst); + + codeStream.EmitLabel(lDone); + } + else + { + // In reverse direction ElementCount = SizeConst; + Debug.Assert(MarshalAsDescriptor.SizeConst.HasValue); + int sizeConst = (int)MarshalAsDescriptor.SizeConst.Value; + + codeStream.EmitLdc(sizeConst); + } + } + + protected override void EmitMarshalFieldManagedToNative() + { + + // It generates the following code + //if (ManagedArg.Field != null) + //{ + // + // fixed (InlineArray* pUnsafe = &NativeArg.Field) + // { + // uint index = 0u; + // while ((ulong)index < (ulong)((long)ManagedArg.Field.Length)) + // { + // NativeArg.s[index] = ManagedArg.Field[(int)index]; + // index += 1u; + // } + // } + //} + + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeStream codeStream = _ilCodeStreams.MarshallingCodeStream; + var nativeArrayType = NativeType as InlineArrayType; + Debug.Assert(nativeArrayType != null); + Debug.Assert(ManagedType is ArrayType); + + var managedElementType = ((ArrayType)ManagedType).ElementType; + + ILCodeLabel lDone = emitter.NewCodeLabel(); + ILCodeLabel lRangeCheck = emitter.NewCodeLabel(); + ILCodeLabel lLoopHeader = emitter.NewCodeLabel(); + + ILLocalVariable vIndex = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); + ILLocalVariable vLength = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); + ILLocalVariable vNative = emitter.NewLocal(NativeType.MakeByRefType(), isPinned: true); + + // check if ManagedType == null, then return + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); + codeStream.Emit(ILOpcode.brfalse, lDone); + + codeStream.EmitLdArg(1); + codeStream.Emit(ILOpcode.ldflda, emitter.NewToken(_nativeField)); + codeStream.EmitStLoc(vNative); + + EmitElementCount(codeStream, MarshalDirection.Forward); + codeStream.EmitStLoc(vLength); + + codeStream.EmitLdc(0); + codeStream.EmitStLoc(vIndex); + codeStream.Emit(ILOpcode.br, lRangeCheck); + + codeStream.EmitLabel(lLoopHeader); + codeStream.EmitLdArg(1); + codeStream.Emit(ILOpcode.ldflda, emitter.NewToken(_nativeField)); + codeStream.EmitLdLoc(vIndex); + + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); + codeStream.EmitLdLoc(vIndex); + + codeStream.EmitLdElem(managedElementType); + + // generate marshalling IL for the element + GetElementMarshaller(MarshalDirection.Forward) + .EmitMarshallingIL(new PInvokeILCodeStreams(_ilCodeStreams.Emitter, codeStream)); + + codeStream.Emit(ILOpcode.call, emitter.NewToken( + nativeArrayType.GetInlineArrayMethod(InlineArrayMethodKind.Setter))); + + codeStream.EmitLdLoc(vIndex); + codeStream.EmitLdc(1); + codeStream.Emit(ILOpcode.add); + codeStream.EmitStLoc(vIndex); + + codeStream.EmitLabel(lRangeCheck); + codeStream.EmitLdLoc(vIndex); + + codeStream.EmitLdLoc(vLength); + codeStream.Emit(ILOpcode.blt, lLoopHeader); + + codeStream.EmitLabel(lDone); + } + + protected override void EmitMarshalFieldNativeToManaged() + { + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeStream codeStream = _ilCodeStreams.UnmarshallingCodestream; + + // It generates the following IL: + // ManagedArg.s = new ElementType[Length]; + // + // for (uint index = 0u; index < Length; index += 1u) + // { + // ManagedArg.s[index] = NativeArg.s[index]; + // } + // + + ILCodeLabel lRangeCheck = emitter.NewCodeLabel(); + ILCodeLabel lLoopHeader = emitter.NewCodeLabel(); + + Debug.Assert(ManagedType is ArrayType); + + var nativeArrayType = NativeType as InlineArrayType; + Debug.Assert(nativeArrayType != null); + + var managedElementType = ((ArrayType)ManagedType).ElementType; + + ILLocalVariable vLength = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); + codeStream.EmitLdArg(1); + // load the length + EmitElementCount(codeStream, MarshalDirection.Reverse); + codeStream.EmitStLoc(vLength); + + codeStream.EmitLdLoc(vLength); + codeStream.Emit(ILOpcode.newarr, emitter.NewToken(managedElementType)); + codeStream.Emit(ILOpcode.stfld, emitter.NewToken(_managedField)); + + + var vIndex = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); + + // index = 0 + codeStream.EmitLdc(0); + codeStream.EmitStLoc(vIndex); + codeStream.Emit(ILOpcode.br, lRangeCheck); + + codeStream.EmitLabel(lLoopHeader); + + // load managed type + codeStream.EmitLdArg(1); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); + + codeStream.EmitLdLoc(vIndex); + + // load native type + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldflda, emitter.NewToken(_nativeField)); + codeStream.EmitLdLoc(vIndex); + + codeStream.Emit(ILOpcode.call, emitter.NewToken( + nativeArrayType.GetInlineArrayMethod(InlineArrayMethodKind.Getter))); + + // generate marshalling IL for the element + GetElementMarshaller(MarshalDirection.Reverse) + .EmitMarshallingIL(new PInvokeILCodeStreams(_ilCodeStreams.Emitter, codeStream)); + + codeStream.EmitStElem(managedElementType); + + codeStream.EmitLdLoc(vIndex); + codeStream.EmitLdc(1); + codeStream.Emit(ILOpcode.add); + codeStream.EmitStLoc(vIndex); + + codeStream.EmitLabel(lRangeCheck); + + codeStream.EmitLdLoc(vIndex); + codeStream.EmitLdLoc(vLength); + codeStream.Emit(ILOpcode.blt, lLoopHeader); + } + } + + abstract class ByValStringMarshaller : ByValArrayMarshaller + { + protected override void EmitElementCount(ILCodeStream codeStream, MarshalDirection direction) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + if (MarshalAsDescriptor == null || !MarshalAsDescriptor.SizeConst.HasValue) + { + throw new InvalidProgramException("SizeConst is required for ByValString."); + } + codeStream.EmitLdc((int)MarshalAsDescriptor.SizeConst.Value); + } + + protected abstract bool IsAnsi + { + get; + } + + protected abstract MethodDesc GetManagedToNativeHelper(); + + protected abstract MethodDesc GetNativeToManagedHelper(); + + protected override void EmitMarshalFieldManagedToNative() + { + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeStream codeStream = _ilCodeStreams.MarshallingCodeStream; + + var nativeArrayType = NativeType as InlineArrayType; + Debug.Assert(nativeArrayType != null); + + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); + + codeStream.EmitLdArg(1); + codeStream.Emit(ILOpcode.ldflda, emitter.NewToken(_nativeField)); + codeStream.Emit(ILOpcode.conv_u); + + EmitElementCount(codeStream, MarshalDirection.Forward); + + if (IsAnsi) + { + codeStream.Emit(PInvokeFlags.BestFitMapping ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); + codeStream.Emit(PInvokeFlags.ThrowOnUnmappableChar ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0); + } + + codeStream.Emit(ILOpcode.call, emitter.NewToken(GetManagedToNativeHelper())); + } + + protected override void EmitMarshalFieldNativeToManaged() + { + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeStream codeStream = _ilCodeStreams.UnmarshallingCodestream; + + codeStream.EmitLdArg(1); + + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldflda, emitter.NewToken(_nativeField)); + codeStream.Emit(ILOpcode.conv_u); + + EmitElementCount(codeStream, MarshalDirection.Reverse); + + codeStream.Emit(ILOpcode.call, emitter.NewToken(GetNativeToManagedHelper())); + + codeStream.Emit(ILOpcode.stfld, emitter.NewToken(_managedField)); + } + } + + class ByValAnsiStringMarshaller : ByValStringMarshaller + { + protected override bool IsAnsi + { + get + { + return true; + } + } + + protected override MethodDesc GetManagedToNativeHelper() + { + return Context.GetHelperEntryPoint("InteropHelpers", "StringToByValAnsiString"); + } + + protected override MethodDesc GetNativeToManagedHelper() + { + return Context.GetHelperEntryPoint("InteropHelpers", "ByValAnsiStringToString"); + } + } + + class ByValUnicodeStringMarshaller : ByValStringMarshaller + { + protected override bool IsAnsi + { + get + { + return false; + } + } + + protected override MethodDesc GetManagedToNativeHelper() + { + return Context.GetHelperEntryPoint("InteropHelpers", "StringToUnicodeFixedArray"); + } + + protected override MethodDesc GetNativeToManagedHelper() + { + return Context.GetHelperEntryPoint("InteropHelpers", "UnicodeToStringFixedArray"); + } + } + + class LayoutClassMarshaler : Marshaller + { + protected override void AllocManagedToNative(ILCodeStream codeStream) + { + LoadNativeAddr(codeStream); + codeStream.Emit(ILOpcode.initobj, _ilCodeStreams.Emitter.NewToken(NativeType)); + } + + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeLabel lNull = emitter.NewCodeLabel(); + + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.brfalse, lNull); + + LoadManagedValue(codeStream); + LoadNativeAddr(codeStream); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( + InteropStateManager.GetStructMarshallingManagedToNativeThunk(ManagedType))); + + codeStream.EmitLabel(lNull); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeLabel lNonNull = emitter.NewCodeLabel(); + + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.brtrue, lNonNull); + + MethodDesc ctor = ManagedType.GetParameterlessConstructor(); + if (ctor == null) + throw new InvalidProgramException(); + + codeStream.Emit(ILOpcode.newobj, emitter.NewToken(ctor)); + StoreManagedValue(codeStream); + + codeStream.EmitLabel(lNonNull); + LoadNativeAddr(codeStream); + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( + InteropStateManager.GetStructMarshallingNativeToManagedThunk(ManagedType))); + } + + protected override void EmitCleanupManaged(ILCodeStream codeStream) + { + // Only do cleanup if it is IN + if (!In) + { + return; + } + + LoadNativeAddr(codeStream); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( + InteropStateManager.GetStructMarshallingCleanupThunk(ManagedType))); + } + } + + class LayoutClassPtrMarshaller : Marshaller + { + protected override void AllocManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeLabel lNull = emitter.NewCodeLabel(); + + codeStream.EmitLdc(0); + codeStream.Emit(ILOpcode.conv_i); + StoreNativeValue(codeStream); + + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.brfalse, lNull); + + TypeDesc nativeStructType = InteropStateManager.GetStructMarshallingNativeType(ManagedType); + + ILLocalVariable lNativeType = emitter.NewLocal(nativeStructType); + codeStream.EmitLdLoca(lNativeType); + codeStream.Emit(ILOpcode.initobj, emitter.NewToken(nativeStructType)); + codeStream.EmitLdLoca(lNativeType); + StoreNativeValue(codeStream); + + codeStream.EmitLabel(lNull); + } + + protected override void AllocNativeToManaged(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + + MethodDesc ctor = ManagedType.GetParameterlessConstructor(); + if (ctor == null) + throw new InvalidProgramException(); + + codeStream.Emit(ILOpcode.newobj, emitter.NewToken(ctor)); + StoreManagedValue(codeStream); + } + + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeLabel lNull = emitter.NewCodeLabel(); + + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.brfalse, lNull); + + LoadManagedValue(codeStream); + LoadNativeValue(codeStream); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( + InteropStateManager.GetStructMarshallingManagedToNativeThunk(ManagedType))); + + codeStream.EmitLabel(lNull); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeLabel lNull = emitter.NewCodeLabel(); + + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.brfalse, lNull); + + LoadNativeValue(codeStream); + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( + InteropStateManager.GetStructMarshallingNativeToManagedThunk(ManagedType))); + + codeStream.EmitLabel(lNull); + } + + protected override void EmitCleanupManaged(ILCodeStream codeStream) + { + // Only do cleanup if it is IN + if (!In) + { + return; + } + + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeLabel lNull = emitter.NewCodeLabel(); + + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.brfalse, lNull); + + LoadNativeValue(codeStream); + codeStream.Emit(ILOpcode.call, emitter.NewToken( + InteropStateManager.GetStructMarshallingCleanupThunk(ManagedType))); + + codeStream.EmitLabel(lNull); + } + } + + class AsAnyMarshaller : Marshaller + { + // This flag affects encoding of string, StringBuilder and Char array marshalling. + // It does not affect LayoutClass marshalling. + // Note that the CoreLib portion of the marshaller currently only supports LayoutClass. + private readonly bool _isAnsi; + + public AsAnyMarshaller(bool isAnsi) + { + _isAnsi = isAnsi; + } + + protected override void AllocManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeLabel lNull = emitter.NewCodeLabel(); + ILLocalVariable lSize = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); + + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.brfalse, lNull); + + MethodDesc getNativeSizeHelper = Context.GetHelperEntryPoint("InteropHelpers", "AsAnyGetNativeSize"); + + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.call, emitter.NewToken(getNativeSizeHelper)); + codeStream.Emit(ILOpcode.dup); + codeStream.EmitStLoc(lSize); + codeStream.Emit(ILOpcode.localloc); + codeStream.Emit(ILOpcode.dup); + StoreNativeValue(codeStream); + codeStream.EmitLdc(0); + codeStream.EmitLdLoc(lSize); + codeStream.Emit(ILOpcode.initblk); + + codeStream.EmitLabel(lNull); + } + + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeLabel lNull = emitter.NewCodeLabel(); + + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.brfalse, lNull); + + LoadManagedValue(codeStream); + LoadNativeValue(codeStream); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( + Context.GetHelperEntryPoint("InteropHelpers", "AsAnyMarshalManagedToNative"))); + + codeStream.EmitLabel(lNull); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeLabel lNull = emitter.NewCodeLabel(); + + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.brfalse, lNull); + + LoadNativeValue(codeStream); + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( + Context.GetHelperEntryPoint("InteropHelpers", "AsAnyMarshalNativeToManaged"))); + + codeStream.EmitLabel(lNull); + } + + protected override void EmitCleanupManaged(ILCodeStream codeStream) + { + // Only do cleanup if it is IN + if (!In) + { + return; + } + + ILEmitter emitter = _ilCodeStreams.Emitter; + ILCodeLabel lNull = emitter.NewCodeLabel(); + + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.brfalse, lNull); + + LoadNativeValue(codeStream); + LoadManagedValue(codeStream); + codeStream.Emit(ILOpcode.call, emitter.NewToken( + Context.GetHelperEntryPoint("InteropHelpers", "AsAnyCleanupNative"))); + + codeStream.EmitLabel(lNull); + } + } + + class ComInterfaceMarshaller : Marshaller + { + protected override void AllocAndTransformManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + + LoadManagedValue(codeStream); + var parameterType = this.ManagedParameterType; + if (parameterType.IsByRef) + { + parameterType = ((ByRefType)this.ManagedParameterType).ParameterType; + } + + CustomAttributeValue? guidAttributeValue = (parameterType as EcmaType)? + .GetDecodedCustomAttribute("System.Runtime.InteropServices", "GuidAttribute"); + if (guidAttributeValue != null) + { + var guidValue = (string)guidAttributeValue.Value.FixedArguments[0].Value; + Span bytes = Guid.Parse(guidValue).ToByteArray(); + codeStream.EmitLdc(BinaryPrimitives.ReadInt32LittleEndian(bytes)); + codeStream.EmitLdc(BinaryPrimitives.ReadInt16LittleEndian(bytes.Slice(4))); + codeStream.EmitLdc(BinaryPrimitives.ReadInt16LittleEndian(bytes.Slice(6))); + for (int i = 8; i < 16; i++) + codeStream.EmitLdc(bytes[i]); + + MetadataType guidType = Context.SystemModule.GetKnownType("System", "Guid"); + var int32Type = Context.GetWellKnownType(WellKnownType.Int32); + var int16Type = Context.GetWellKnownType(WellKnownType.Int16); + var byteType = Context.GetWellKnownType(WellKnownType.Byte); + var sig = new MethodSignature( + MethodSignatureFlags.None, + genericParameterCount: 0, + returnType: Context.GetWellKnownType(WellKnownType.Void), + parameters: new TypeDesc[] { int32Type, int16Type, int16Type, byteType, byteType, byteType, byteType, byteType, byteType, byteType, byteType }); + MethodDesc guidCtorHandleMethod = + guidType.GetKnownMethod(".ctor", sig); + codeStream.Emit(ILOpcode.newobj, emitter.NewToken(guidCtorHandleMethod)); + + MethodDesc helper = Context.GetHelperEntryPoint("InteropHelpers", "ConvertManagedComInterfaceToNative"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + } + else + { + if (!parameterType.IsObject) + { + throw new NotSupportedException(); + } + + MethodDesc helper = Context.GetHelperEntryPoint("InteropHelpers", "ConvertManagedComInterfaceToIUnknown"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + } + + StoreNativeValue(codeStream); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + + LoadNativeValue(codeStream); + MethodDesc helper = Context.GetHelperEntryPoint("InteropHelpers", "ConvertNativeComInterfaceToManaged"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + StoreManagedValue(codeStream); + } + + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + throw new NotSupportedException(); + } + } + + class AnsiBSTRStringMarshaller : Marshaller + { + + internal override bool CleanupRequired => true; + + internal override void EmitElementCleanup(ILCodeStream codeStream, ILEmitter emitter) + { + var helper = InteropTypes.GetMarshal(Context).GetKnownMethod("FreeBSTR", null); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + } + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + LoadManagedValue(codeStream); + + var helper = Context.GetHelperEntryPoint("InteropHelpers", "StringToAnsiBstrBuffer"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + StoreNativeValue(codeStream); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + LoadNativeValue(codeStream); + + var helper = Context.GetHelperEntryPoint("InteropHelpers", "AnsiBstrBufferToString"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + StoreManagedValue(codeStream); + } + } + + class BSTRStringMarshaller : Marshaller + { + + internal override bool CleanupRequired => true; + + internal override void EmitElementCleanup(ILCodeStream codeStream, ILEmitter emitter) + { + var helper = InteropTypes.GetMarshal(Context).GetKnownMethod("FreeBSTR", null); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + } + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + LoadManagedValue(codeStream); + + var helper = InteropTypes.GetMarshal(Context).GetKnownMethod("StringToBSTR", null); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + StoreNativeValue(codeStream); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + LoadNativeValue(codeStream); + + var helper = Context.GetHelperEntryPoint("InteropHelpers", "BstrBufferToString"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + StoreManagedValue(codeStream); + } + } + + class OleDateTimeMarshaller : Marshaller + { + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + LoadManagedValue(codeStream); + + var helper = Context.GetHelperEntryPoint("InteropHelpers", "DateTimeToOleDateTime"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + StoreNativeValue(codeStream); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + LoadNativeValue(codeStream); + + var helper = Context.GetHelperEntryPoint("InteropHelpers", "OleDateTimeToDateTime"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + StoreManagedValue(codeStream); + } + } + + class OleCurrencyMarshaller : Marshaller + { + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + LoadManagedValue(codeStream); + + var helper = Context.GetHelperEntryPoint("InteropHelpers", "DecimalToOleCurrency"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + StoreNativeValue(codeStream); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + ILEmitter emitter = _ilCodeStreams.Emitter; + LoadNativeValue(codeStream); + + var helper = Context.GetHelperEntryPoint("InteropHelpers", "OleCurrencyToDecimal"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + StoreManagedValue(codeStream); + } + } + + class VariantMarshaller : Marshaller + { + protected override void AllocManagedToNative(ILCodeStream codeStream) + { + LoadNativeAddr(codeStream); + codeStream.Emit(ILOpcode.initobj, _ilCodeStreams.Emitter.NewToken(NativeType)); + } + + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + if (this.MarshalDirection == MarshalDirection.Reverse) + { + throw new NotSupportedException(); + } + + ILEmitter emitter = _ilCodeStreams.Emitter; + + LoadManagedValue(codeStream); + LoadNativeAddr(codeStream); + + var helper = Context.GetHelperEntryPoint("InteropHelpers", "ConvertObjectToVariant"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + if (this.MarshalDirection == MarshalDirection.Reverse) + { + throw new NotSupportedException(); + } + + ILEmitter emitter = _ilCodeStreams.Emitter; + + LoadNativeAddr(codeStream); + + var helper = Context.GetHelperEntryPoint("InteropHelpers", "VariantToObject"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + + StoreManagedValue(codeStream); + } + + protected override void EmitCleanupManaged(ILCodeStream codeStream) + { + // Only do cleanup if it is IN + if (!In) + { + return; + } + + ILEmitter emitter = _ilCodeStreams.Emitter; + + LoadNativeAddr(codeStream); + var helper = Context.GetHelperEntryPoint("InteropHelpers", "CleanupVariant"); + codeStream.Emit(ILOpcode.call, emitter.NewToken(helper)); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/NativeStructType.Mangling.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/NativeStructType.Mangling.cs new file mode 100644 index 00000000000000..fe3edc156824a6 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/NativeStructType.Mangling.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem.Interop +{ + partial class NativeStructType : IPrefixMangledType + { + TypeDesc IPrefixMangledType.BaseType => ManagedStructType; + + string IPrefixMangledType.Prefix => "NativeStructType"; + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/NativeStructType.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/NativeStructType.Sorting.cs new file mode 100644 index 00000000000000..0da2d42007d56b --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/NativeStructType.Sorting.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem.Interop +{ + // Functionality related to determinstic ordering of types + partial class NativeStructType + { + protected internal override int ClassCode => -377751537; + + protected internal override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) + { + return comparer.Compare(ManagedStructType, ((NativeStructType)other).ManagedStructType); + } + + partial class NativeStructField + { + protected internal override int ClassCode => 1580219745; + + protected internal override int CompareToImpl(FieldDesc other, TypeSystemComparer comparer) + { + return comparer.Compare(_managedField, ((NativeStructField)other)._managedField); + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/NativeStructType.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/NativeStructType.cs new file mode 100644 index 00000000000000..d20d6fafcc0dc9 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/NativeStructType.cs @@ -0,0 +1,406 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem.Interop +{ + public partial class NativeStructType : MetadataType + { + // The managed struct that this type will imitate + public MetadataType ManagedStructType + { + get; + } + + public override ModuleDesc Module + { + get; + } + + public override string Name + { + get + { + return "__NativeType__" + ManagedStructType.Name; + } + } + + public override string Namespace + { + get + { + return "Internal.CompilerGenerated"; + } + } + + public override PInvokeStringFormat PInvokeStringFormat + { + get + { + return ManagedStructType.PInvokeStringFormat; + } + } + + public override bool IsExplicitLayout + { + get + { + return ManagedStructType.IsExplicitLayout; + } + } + + public override bool IsSequentialLayout + { + get + { + return ManagedStructType.IsSequentialLayout; + } + } + + public override bool IsBeforeFieldInit + { + get + { + return ManagedStructType.IsBeforeFieldInit; + } + } + + public override DefType BaseType + { + get + { + return (DefType)Context.GetWellKnownType(WellKnownType.ValueType); + } + } + + public override MetadataType MetadataBaseType + { + get + { + return (MetadataType)Context.GetWellKnownType(WellKnownType.ValueType); + } + } + + public override bool IsSealed + { + get + { + return true; + } + } + + public override bool IsAbstract + { + get + { + return false; + } + } + + public override DefType ContainingType + { + get + { + return null; + } + } + + public override DefType[] ExplicitlyImplementedInterfaces + { + get + { + return Array.Empty(); + } + } + + public override TypeSystemContext Context + { + get + { + return ManagedStructType.Context; + } + } + + private NativeStructField[] _fields; + private InteropStateManager _interopStateManager; + private bool _hasInvalidLayout; + + public bool HasInvalidLayout + { + get + { + return _hasInvalidLayout; + } + } + + public FieldDesc[] Fields + { + get + { + return _fields; + } + } + + public NativeStructType(ModuleDesc owningModule, MetadataType managedStructType, InteropStateManager interopStateManager) + { + Debug.Assert(!managedStructType.IsGenericDefinition); + + Module = owningModule; + ManagedStructType = managedStructType; + _interopStateManager = interopStateManager; + _hasInvalidLayout = false; + CalculateFields(); + } + + private void CalculateFields() + { + bool isSequential = ManagedStructType.IsSequentialLayout; + bool isAnsi = ManagedStructType.PInvokeStringFormat == PInvokeStringFormat.AnsiClass; + + int numFields = 0; + foreach (FieldDesc field in ManagedStructType.GetFields()) + { + if (field.IsStatic) + { + continue; + } + numFields++; + } + + _fields = new NativeStructField[numFields]; + + int index = 0; + foreach (FieldDesc field in ManagedStructType.GetFields()) + { + if (field.IsStatic) + { + continue; + } + + var managedType = field.FieldType; + + TypeDesc nativeType; + try + { + nativeType = MarshalHelpers.GetNativeStructFieldType(managedType, field.GetMarshalAsDescriptor(), _interopStateManager, isAnsi); + } + catch (NotSupportedException) + { + // if marshalling is not supported for this type the generated stubs will emit appropriate + // error message. We just set native type to be same as managedtype + nativeType = managedType; + _hasInvalidLayout = true; + } + + _fields[index++] = new NativeStructField(nativeType, this, field); + } + } + + public override ClassLayoutMetadata GetClassLayout() + { + ClassLayoutMetadata layout = ManagedStructType.GetClassLayout(); + + ClassLayoutMetadata result; + result.PackingSize = layout.PackingSize; + result.Size = layout.Size; + + if (IsExplicitLayout) + { + result.Offsets = new FieldAndOffset[layout.Offsets.Length]; + + Debug.Assert(layout.Offsets.Length <= _fields.Length); + + int layoutIndex = 0; + for (int index = 0; index < _fields.Length; index++) + { + if (_fields[index].Name == layout.Offsets[layoutIndex].Field.Name) + { + result.Offsets[layoutIndex] = new FieldAndOffset(_fields[index], layout.Offsets[layoutIndex].Offset); + layoutIndex++; + } + } + + Debug.Assert(layoutIndex == layout.Offsets.Length); + } + else + { + result.Offsets = null; + } + + return result; + } + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) + { + return false; + } + + public override IEnumerable GetNestedTypes() + { + return Array.Empty(); + } + + public override MetadataType GetNestedType(string name) + { + return null; + } + + protected override MethodImplRecord[] ComputeVirtualMethodImplsForType() + { + return Array.Empty(); + } + + public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name) + { + return Array.Empty(); + } + + private int _hashCode; + + private void InitializeHashCode() + { + var hashCodeBuilder = new Internal.NativeFormat.TypeHashingAlgorithms.HashCodeBuilder(Namespace); + + if (Namespace.Length > 0) + { + hashCodeBuilder.Append("."); + } + + hashCodeBuilder.Append(Name); + _hashCode = hashCodeBuilder.ToHashCode(); + } + + public override int GetHashCode() + { + if (_hashCode == 0) + { + InitializeHashCode(); + } + return _hashCode; + } + + protected override TypeFlags ComputeTypeFlags(TypeFlags mask) + { + TypeFlags flags = 0; + + if ((mask & TypeFlags.HasGenericVarianceComputed) != 0) + { + flags |= TypeFlags.HasGenericVarianceComputed; + } + + if ((mask & TypeFlags.CategoryMask) != 0) + { + flags |= TypeFlags.ValueType; + } + + flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.AttributeCacheComputed; + + return flags; + } + + public override IEnumerable GetFields() + { + return _fields; + } + + /// + /// Synthetic field on . + /// + private partial class NativeStructField : FieldDesc + { + private TypeDesc _fieldType; + private MetadataType _owningType; + private FieldDesc _managedField; + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc FieldType + { + get + { + return _fieldType; + } + } + + public override bool HasRva + { + get + { + return false; + } + } + + + public override bool IsInitOnly + { + get + { + return false; + } + } + + public override bool IsLiteral + { + get + { + return false; + } + } + + public override bool IsStatic + { + get + { + return false; + } + } + + public override bool IsThreadStatic + { + get + { + return false; + } + } + + public override DefType OwningType + { + get + { + return _owningType; + } + } + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) + { + return false; + } + + public override string Name + { + get + { + return _managedField.Name; + } + } + + public NativeStructField(TypeDesc nativeType, MetadataType owningType, FieldDesc managedField) + { + _fieldType = nativeType; + _owningType = owningType; + _managedField = managedField; + } + } + + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapper.Mangling.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapper.Mangling.cs new file mode 100644 index 00000000000000..ab66c754f58b08 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapper.Mangling.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem.Interop +{ + partial class PInvokeDelegateWrapper : IPrefixMangledType + { + TypeDesc IPrefixMangledType.BaseType + { + get + { + return DelegateType; + } + } + + string IPrefixMangledType.Prefix + { + get + { + return "PInvokeDelegateWrapper"; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapper.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapper.Sorting.cs new file mode 100644 index 00000000000000..6b4f6b9ca06206 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapper.Sorting.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem.Interop +{ + // Functionality related to determinstic ordering of types + partial class PInvokeDelegateWrapper + { + protected internal override int ClassCode => -262930217; + + protected internal override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) + { + return comparer.Compare(DelegateType, ((PInvokeDelegateWrapper)other).DelegateType); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapper.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapper.cs new file mode 100644 index 00000000000000..78cbe796e397f1 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapper.cs @@ -0,0 +1,260 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using Internal.IL; +using Internal.IL.Stubs; +using Debug = System.Diagnostics.Debug; +using System.Threading; + +namespace Internal.TypeSystem.Interop +{ + /// + /// This generates a class which inherits from System.Runtime.InteropServices.NativeFunctionPointerWrapper. It has a constructror and + /// a second instance method which marshalls arguments in the forward direction in order to call the native function pointer. + /// + public partial class PInvokeDelegateWrapper : MetadataType + { + // The type of delegate that will be created from the native function pointer + public MetadataType DelegateType + { + get; + } + + public override ModuleDesc Module + { + get; + } + + public override string Name + { + get + { + return "PInvokeDelegateWrapper__" + DelegateType.Name; + } + } + + public override string Namespace + { + get + { + return "Internal.CompilerGenerated"; + } + } + + public override bool IsExplicitLayout + { + get + { + return false; + } + } + + public override PInvokeStringFormat PInvokeStringFormat + { + get + { + return PInvokeStringFormat.AnsiClass; + } + } + + public override bool IsSequentialLayout + { + get + { + return false; + } + } + + public override bool IsBeforeFieldInit + { + get + { + return false; + } + } + + public override MetadataType MetadataBaseType + { + get + { + return InteropTypes.GetNativeFunctionPointerWrapper(Context); + } + } + + public override DefType BaseType + { + get + { + return InteropTypes.GetNativeFunctionPointerWrapper(Context); + } + } + + public override bool IsSealed + { + get + { + return true; + } + } + + public override bool IsAbstract + { + get + { + return false; + } + } + + public override DefType ContainingType + { + get + { + return null; + } + } + + public override DefType[] ExplicitlyImplementedInterfaces + { + get + { + return Array.Empty(); + } + } + + public override TypeSystemContext Context + { + get + { + return DelegateType.Context; + } + } + + private InteropStateManager _interopStateManager; + + public PInvokeDelegateWrapper(ModuleDesc owningModule, MetadataType delegateType, InteropStateManager interopStateManager) + { + Debug.Assert(delegateType.IsDelegate); + + Module = owningModule; + DelegateType = delegateType; + _interopStateManager = interopStateManager; + } + + public override ClassLayoutMetadata GetClassLayout() + { + return default(ClassLayoutMetadata); + } + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) + { + return false; + } + + public override IEnumerable GetNestedTypes() + { + return Array.Empty(); + } + + public override MetadataType GetNestedType(string name) + { + return null; + } + + protected override MethodImplRecord[] ComputeVirtualMethodImplsForType() + { + return Array.Empty(); + } + + public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name) + { + return Array.Empty(); + } + + private int _hashCode; + + private void InitializeHashCode() + { + var hashCodeBuilder = new Internal.NativeFormat.TypeHashingAlgorithms.HashCodeBuilder(Namespace); + + if (Namespace.Length > 0) + { + hashCodeBuilder.Append("."); + } + + hashCodeBuilder.Append(Name); + _hashCode = hashCodeBuilder.ToHashCode(); + } + + public override int GetHashCode() + { + if (_hashCode == 0) + { + InitializeHashCode(); + } + return _hashCode; + } + + protected override TypeFlags ComputeTypeFlags(TypeFlags mask) + { + TypeFlags flags = 0; + + if ((mask & TypeFlags.HasGenericVarianceComputed) != 0) + { + flags |= TypeFlags.HasGenericVarianceComputed; + } + + if ((mask & TypeFlags.CategoryMask) != 0) + { + flags |= TypeFlags.Class; + } + + flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.AttributeCacheComputed; + + return flags; + } + + private MethodDesc[] _methods; + + private void InitializeMethods() + { + MethodDesc[] methods = new MethodDesc[] { + new PInvokeDelegateWrapperConstructor(this), // Constructor + new DelegateMarshallingMethodThunk(DelegateType, this, _interopStateManager, + DelegateMarshallingMethodThunkKind.ForwardNativeFunctionWrapper) // a forward marshalling instance method + }; + + Interlocked.CompareExchange(ref _methods, methods, null); + } + + public override IEnumerable GetMethods() + { + if (_methods == null) + { + InitializeMethods(); + } + return _methods; + } + + public MethodDesc GetPInvokeDelegateWrapperMethod(PInvokeDelegateWrapperMethodKind kind) + { + if (_methods == null) + { + InitializeMethods(); + } + + Debug.Assert((int)kind < _methods.Length); + + return _methods[(int)kind]; + } + } + + public enum PInvokeDelegateWrapperMethodKind : byte + { + Constructor = 0, + Invoke = 1 + } + +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapperConstructor.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapperConstructor.Sorting.cs new file mode 100644 index 00000000000000..2509b265ea71e8 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapperConstructor.Sorting.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem.Interop +{ + // Functionality related to deterministic ordering of methods + partial class PInvokeDelegateWrapperConstructor + { + protected internal override int ClassCode => 1000342011; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var owningType = (PInvokeDelegateWrapper)OwningType; + var otherOwningType = (PInvokeDelegateWrapper)other.OwningType; + return comparer.Compare(owningType.DelegateType, otherOwningType.DelegateType); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapperConstructor.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapperConstructor.cs new file mode 100644 index 00000000000000..6917c71f656e42 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeDelegateWrapperConstructor.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using Internal.IL; +using Internal.IL.Stubs; +using Debug = System.Diagnostics.Debug; +using System.Threading; + +namespace Internal.TypeSystem.Interop +{ + /// + /// Constructor for PInvokeDelegateWrapper which calls into the base class constructor + /// + public partial class PInvokeDelegateWrapperConstructor : ILStubMethod + { + public PInvokeDelegateWrapperConstructor(PInvokeDelegateWrapper owningType) + { + OwningType = owningType; + } + + public override TypeDesc OwningType + { + get; + } + + public override string Name + { + get + { + return ".ctor"; + } + } + + private MethodSignature _signature; + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + _signature = new MethodSignature(MethodSignatureFlags.None, + genericParameterCount: 0, + returnType: Context.GetWellKnownType(WellKnownType.Void), + parameters: new TypeDesc[] { + Context.GetWellKnownType(WellKnownType.IntPtr) + }); + } + return _signature; + } + } + + public override TypeSystemContext Context + { + get + { + return OwningType.Context; + } + } + + public override MethodIL EmitIL() + { + var emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + codeStream.EmitLdArg(0); + codeStream.EmitLdArg(1); + codeStream.Emit(ILOpcode.call, emitter.NewToken( + InteropTypes.GetNativeFunctionPointerWrapper(Context).GetMethod(".ctor", Signature))); + codeStream.Emit(ILOpcode.ret); + return emitter.Link(this); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeILEmitterConfiguration.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeILEmitterConfiguration.cs new file mode 100644 index 00000000000000..c75e59d033facb --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/PInvokeILEmitterConfiguration.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace Internal.IL +{ + public abstract class PInvokeILEmitterConfiguration + { + public abstract bool GenerateDirectCall(MethodDesc method, out string externName); + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/InteropStateManager.cs b/src/coreclr/tools/Common/TypeSystem/Interop/InteropStateManager.cs new file mode 100644 index 00000000000000..202fcce90becef --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Interop/InteropStateManager.cs @@ -0,0 +1,518 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.IL.Stubs; +using Internal.TypeSystem.Interop; +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem +{ + /// + /// This class manages and caches interop state information + /// + public sealed class InteropStateManager + { + private readonly ModuleDesc _generatedAssembly; + private readonly NativeStructTypeHashtable _nativeStructHashtable; + private readonly StructMarshallingThunkHashTable _structMarshallingThunkHashtable; + private readonly DelegateMarshallingStubHashtable _delegateMarshallingThunkHashtable; + private readonly ForwardDelegateCreationStubHashtable _forwardDelegateCreationStubHashtable; + private readonly PInvokeDelegateWrapperHashtable _pInvokeDelegateWrapperHashtable; + private readonly InlineArrayHashTable _inlineArrayHashtable; + private readonly PInvokeLazyFixupFieldHashtable _pInvokeLazyFixupFieldHashtable; + private readonly PInvokeCalliHashtable _pInvokeCalliHashtable; + + public InteropStateManager(ModuleDesc generatedAssembly) + { + _generatedAssembly = generatedAssembly; + _structMarshallingThunkHashtable = new StructMarshallingThunkHashTable(this, _generatedAssembly.GetGlobalModuleType()); + _nativeStructHashtable = new NativeStructTypeHashtable(this, _generatedAssembly); + _delegateMarshallingThunkHashtable = new DelegateMarshallingStubHashtable(this, _generatedAssembly.GetGlobalModuleType()); + _forwardDelegateCreationStubHashtable = new ForwardDelegateCreationStubHashtable(this, _generatedAssembly.GetGlobalModuleType()); + _pInvokeDelegateWrapperHashtable = new PInvokeDelegateWrapperHashtable(this, _generatedAssembly); + _inlineArrayHashtable = new InlineArrayHashTable(this, _generatedAssembly); + _pInvokeLazyFixupFieldHashtable = new PInvokeLazyFixupFieldHashtable(_generatedAssembly.GetGlobalModuleType()); + _pInvokeCalliHashtable = new PInvokeCalliHashtable(this, _generatedAssembly.GetGlobalModuleType()); + } + // + // Delegate Marshalling Stubs + // + + /// + /// Generates marshalling stubs for open static delegates + /// + public DelegateMarshallingMethodThunk GetOpenStaticDelegateMarshallingThunk(TypeDesc delegateType) + { + if (delegateType is ByRefType) + { + delegateType = delegateType.GetParameterType(); + } + + Debug.Assert(delegateType is MetadataType); + + + // Get the stub for marshalling open static delegate + var stubKey = new DelegateMarshallingStubHashtableKey((MetadataType)delegateType, DelegateMarshallingMethodThunkKind.ReverseOpenStatic); + return _delegateMarshallingThunkHashtable.GetOrCreateValue(stubKey); + } + + /// + /// Generates marshalling stubs for closed instance delegates + /// + public DelegateMarshallingMethodThunk GetClosedDelegateMarshallingThunk(TypeDesc delegateType) + { + if (delegateType is ByRefType) + { + delegateType = delegateType.GetParameterType(); + } + + Debug.Assert(delegateType is MetadataType); + + + // Get the stub for marshalling open static delegate + var stubKey = new DelegateMarshallingStubHashtableKey((MetadataType)delegateType, DelegateMarshallingMethodThunkKind.ReverseClosed); + return _delegateMarshallingThunkHashtable.GetOrCreateValue(stubKey); + } + + /// + /// Generates thunk for creating delegate + /// + public ForwardDelegateCreationThunk GetForwardDelegateCreationThunk(TypeDesc delegateType) + { + if (delegateType is ByRefType) + { + delegateType = delegateType.GetParameterType(); + } + + Debug.Assert(delegateType is MetadataType); + + // Get the stub for creating delegate + return _forwardDelegateCreationStubHashtable.GetOrCreateValue((MetadataType)delegateType); + } + + public PInvokeDelegateWrapper GetPInvokeDelegateWrapper(TypeDesc delegateType) + { + if (delegateType is ByRefType) + { + delegateType = delegateType.GetParameterType(); + } + + Debug.Assert(delegateType is MetadataType); + + // Get the Type that wraps the native function + return _pInvokeDelegateWrapperHashtable.GetOrCreateValue((MetadataType)delegateType); + } + + // + // Struct Marshalling + // To support struct marshalling compiler needs to generate a native type which + // imitates the original struct being passed to managed side with corresponding + // fields of marshalled types. Additionally it needs to generate three thunks + // 1. Managed to Native Thunk: For forward marshalling + // 2. Native to Managed Thunk: For reverse marshalling + // 3. Cleanup Thunk: for cleaning up any allocated resources + // + /// + /// Generates a Native struct type which imitates the managed struct + /// + public NativeStructType GetStructMarshallingNativeType(TypeDesc managedType) + { + if (managedType is ByRefType) + { + managedType = managedType.GetParameterType(); + } + + Debug.Assert(managedType is MetadataType); + + return _nativeStructHashtable.GetOrCreateValue((MetadataType)managedType); + } + + /// + /// Generates a thunk to marshal the fields of the struct from managed to native + /// + public MethodDesc GetStructMarshallingManagedToNativeThunk(TypeDesc managedType) + { + if (managedType is ByRefType) + { + managedType = managedType.GetParameterType(); + } + + Debug.Assert(managedType is MetadataType); + + var methodKey = new StructMarshallingThunkKey((MetadataType)managedType, StructMarshallingThunkType.ManagedToNative); + return _structMarshallingThunkHashtable.GetOrCreateValue(methodKey); + } + + /// + /// Generates a thunk to marshal the fields of the struct from native to managed + /// + public MethodDesc GetStructMarshallingNativeToManagedThunk(TypeDesc managedType) + { + if (managedType is ByRefType) + { + managedType = managedType.GetParameterType(); + } + + Debug.Assert(managedType is MetadataType); + + var methodKey = new StructMarshallingThunkKey((MetadataType)managedType, StructMarshallingThunkType.NativeToManaged); + return _structMarshallingThunkHashtable.GetOrCreateValue(methodKey); + } + + /// + /// Generates a thunk to cleanup any allocated resources during marshalling + /// + public MethodDesc GetStructMarshallingCleanupThunk(TypeDesc managedType) + { + if (managedType is ByRefType) + { + managedType = ((ByRefType)managedType).GetParameterType(); + } + + Debug.Assert(managedType is MetadataType); + + var methodKey = new StructMarshallingThunkKey((MetadataType)managedType, StructMarshallingThunkType.Cleanup); + return _structMarshallingThunkHashtable.GetOrCreateValue(methodKey); + } + + public TypeDesc GetInlineArrayType(InlineArrayCandidate candidate) + { + return _inlineArrayHashtable.GetOrCreateValue(candidate); + } + + public FieldDesc GetPInvokeLazyFixupField(MethodDesc method) + { + return _pInvokeLazyFixupFieldHashtable.GetOrCreateValue(method); + } + + public MethodDesc GetPInvokeCalliStub(MethodSignature signature) + { + return _pInvokeCalliHashtable.GetOrCreateValue(signature); + } + + private class NativeStructTypeHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(MetadataType key) + { + return key.GetHashCode(); + } + + protected override int GetValueHashCode(NativeStructType value) + { + return value.ManagedStructType.GetHashCode(); + } + + protected override bool CompareKeyToValue(MetadataType key, NativeStructType value) + { + return Object.ReferenceEquals(key, value.ManagedStructType); + } + + protected override bool CompareValueToValue(NativeStructType value1, NativeStructType value2) + { + return Object.ReferenceEquals(value1.ManagedStructType, value2.ManagedStructType); + } + + protected override NativeStructType CreateValueFromKey(MetadataType key) + { + return new NativeStructType(_owningModule, key, _interopStateManager); + } + + private readonly InteropStateManager _interopStateManager; + private readonly ModuleDesc _owningModule; + + public NativeStructTypeHashtable(InteropStateManager interopStateManager, ModuleDesc owningModule) + { + _interopStateManager = interopStateManager; + _owningModule = owningModule; + } + } + + private struct StructMarshallingThunkKey + { + public readonly MetadataType ManagedType; + public readonly StructMarshallingThunkType ThunkType; + + public StructMarshallingThunkKey(MetadataType type, StructMarshallingThunkType thunkType) + { + ManagedType = type; + ThunkType = thunkType; + } + } + + private class StructMarshallingThunkHashTable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(StructMarshallingThunkKey key) + { + return key.ManagedType.GetHashCode() ^ (int)key.ThunkType; + } + + protected override int GetValueHashCode(StructMarshallingThunk value) + { + return value.ManagedType.GetHashCode() ^ (int)value.ThunkType; + } + + protected override bool CompareKeyToValue(StructMarshallingThunkKey key, StructMarshallingThunk value) + { + return Object.ReferenceEquals(key.ManagedType, value.ManagedType) && + key.ThunkType == value.ThunkType; + } + + protected override bool CompareValueToValue(StructMarshallingThunk value1, StructMarshallingThunk value2) + { + return Object.ReferenceEquals(value1.ManagedType, value2.ManagedType) && + value1.ThunkType == value2.ThunkType; + } + + protected override StructMarshallingThunk CreateValueFromKey(StructMarshallingThunkKey key) + { + return new StructMarshallingThunk(_owningType, key.ManagedType, key.ThunkType, _interopStateManager); + } + + private readonly InteropStateManager _interopStateManager; + private readonly TypeDesc _owningType; + + public StructMarshallingThunkHashTable(InteropStateManager interopStateManager, TypeDesc owningType) + { + _interopStateManager = interopStateManager; + _owningType = owningType; + } + } + + private class InlineArrayHashTable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(InlineArrayCandidate key) + { + return key.ElementType.GetHashCode() ^ (int)key.Length; + } + + protected override int GetValueHashCode(InlineArrayType value) + { + return value.ElementType.GetHashCode() ^ (int)value.Length; + } + + protected override bool CompareKeyToValue(InlineArrayCandidate key, InlineArrayType value) + { + return Object.ReferenceEquals(key.ElementType, value.ElementType) && + key.Length == value.Length; + } + + protected override bool CompareValueToValue(InlineArrayType value1, InlineArrayType value2) + { + return Object.ReferenceEquals(value1.ElementType, value2.ElementType) && + value1.Length == value2.Length; + } + + protected override InlineArrayType CreateValueFromKey(InlineArrayCandidate key) + { + return new InlineArrayType(_owningModule, key.ElementType, key.Length, _interopStateManager); + } + + private readonly InteropStateManager _interopStateManager; + private readonly ModuleDesc _owningModule; + + public InlineArrayHashTable(InteropStateManager interopStateManager, ModuleDesc owningModule) + { + _interopStateManager = interopStateManager; + _owningModule = owningModule; + } + } + + private struct DelegateMarshallingStubHashtableKey + { + public readonly MetadataType DelegateType; + public readonly DelegateMarshallingMethodThunkKind Kind; + + public DelegateMarshallingStubHashtableKey(MetadataType type, DelegateMarshallingMethodThunkKind kind) + { + DelegateType = type; + Kind = kind; + } + } + private class DelegateMarshallingStubHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(DelegateMarshallingStubHashtableKey key) + { + return key.DelegateType.GetHashCode() ^ (int)key.Kind; + } + + protected override int GetValueHashCode(DelegateMarshallingMethodThunk value) + { + return value.DelegateType.GetHashCode() ^ (int)value.Kind; + } + + protected override bool CompareKeyToValue(DelegateMarshallingStubHashtableKey key, DelegateMarshallingMethodThunk value) + { + return Object.ReferenceEquals(key.DelegateType, value.DelegateType) && + key.Kind== value.Kind; + } + + protected override bool CompareValueToValue(DelegateMarshallingMethodThunk value1, DelegateMarshallingMethodThunk value2) + { + return Object.ReferenceEquals(value1.DelegateType, value2.DelegateType) && + value1.Kind== value2.Kind; + } + + protected override DelegateMarshallingMethodThunk CreateValueFromKey(DelegateMarshallingStubHashtableKey key) + { + return new DelegateMarshallingMethodThunk(key.DelegateType, _owningType, + _interopStateManager, key.Kind); + } + + private TypeDesc _owningType; + private InteropStateManager _interopStateManager; + + public DelegateMarshallingStubHashtable(InteropStateManager interopStateManager, TypeDesc owningType) + { + _interopStateManager = interopStateManager; + _owningType = owningType; + } + } + + private class ForwardDelegateCreationStubHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(MetadataType key) + { + return key.GetHashCode(); + } + + protected override int GetValueHashCode(ForwardDelegateCreationThunk value) + { + return value.DelegateType.GetHashCode(); + } + + protected override bool CompareKeyToValue(MetadataType key, ForwardDelegateCreationThunk value) + { + return Object.ReferenceEquals(key, value.DelegateType); + } + + protected override bool CompareValueToValue(ForwardDelegateCreationThunk value1, ForwardDelegateCreationThunk value2) + { + return Object.ReferenceEquals(value1.DelegateType, value2.DelegateType); + } + + protected override ForwardDelegateCreationThunk CreateValueFromKey(MetadataType key) + { + return new ForwardDelegateCreationThunk(key, _owningType, _interopStateManager); + } + + private TypeDesc _owningType; + private InteropStateManager _interopStateManager; + + public ForwardDelegateCreationStubHashtable(InteropStateManager interopStateManager, TypeDesc owningType) + { + _interopStateManager = interopStateManager; + _owningType = owningType; + } + } + + private class PInvokeDelegateWrapperHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(MetadataType key) + { + return key.GetHashCode(); + } + + protected override int GetValueHashCode(PInvokeDelegateWrapper value) + { + return value.DelegateType.GetHashCode(); + } + + protected override bool CompareKeyToValue(MetadataType key, PInvokeDelegateWrapper value) + { + return Object.ReferenceEquals(key, value.DelegateType); + } + + protected override bool CompareValueToValue(PInvokeDelegateWrapper value1, PInvokeDelegateWrapper value2) + { + return Object.ReferenceEquals(value1.DelegateType, value2.DelegateType); + } + + protected override PInvokeDelegateWrapper CreateValueFromKey(MetadataType key) + { + return new PInvokeDelegateWrapper(_owningModule, key, _interopStateManager); + } + + private readonly InteropStateManager _interopStateManager; + private readonly ModuleDesc _owningModule; + + public PInvokeDelegateWrapperHashtable(InteropStateManager interopStateManager, ModuleDesc owningModule) + { + _interopStateManager = interopStateManager; + _owningModule = owningModule; + } + } + + private class PInvokeLazyFixupFieldHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(MethodDesc key) + { + return key.GetHashCode(); + } + + protected override int GetValueHashCode(PInvokeLazyFixupField value) + { + return value.TargetMethod.GetHashCode(); + } + + protected override bool CompareKeyToValue(MethodDesc key, PInvokeLazyFixupField value) + { + return key == value.TargetMethod; + } + + protected override bool CompareValueToValue(PInvokeLazyFixupField value1, PInvokeLazyFixupField value2) + { + return value1.TargetMethod == value2.TargetMethod; + } + + protected override PInvokeLazyFixupField CreateValueFromKey(MethodDesc key) + { + return new PInvokeLazyFixupField(_owningType, key); + } + + private readonly DefType _owningType; + + public PInvokeLazyFixupFieldHashtable(DefType owningType) + { + _owningType = owningType; + } + } + + private class PInvokeCalliHashtable : LockFreeReaderHashtable + { + private readonly InteropStateManager _interopStateManager; + private readonly TypeDesc _owningType; + + protected override int GetKeyHashCode(MethodSignature key) + { + return key.GetHashCode(); + } + + protected override int GetValueHashCode(CalliMarshallingMethodThunk value) + { + return value.TargetSignature.GetHashCode(); + } + + protected override bool CompareKeyToValue(MethodSignature key, CalliMarshallingMethodThunk value) + { + return key.Equals(value.TargetSignature); + } + + protected override bool CompareValueToValue(CalliMarshallingMethodThunk value1, CalliMarshallingMethodThunk value2) + { + return value1.TargetSignature.Equals(value2.TargetSignature); + } + + protected override CalliMarshallingMethodThunk CreateValueFromKey(MethodSignature key) + { + return new CalliMarshallingMethodThunk(key, _owningType, _interopStateManager); + } + + public PInvokeCalliHashtable(InteropStateManager interopStateManager, TypeDesc owningType) + { + _interopStateManager = interopStateManager; + _owningType = owningType; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/TypesDebugInfoWriter/TypesDebugInfoWriter.cs b/src/coreclr/tools/Common/TypeSystem/TypesDebugInfoWriter/TypesDebugInfoWriter.cs new file mode 100644 index 00000000000000..ed387a866bc552 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/TypesDebugInfoWriter/TypesDebugInfoWriter.cs @@ -0,0 +1,113 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace Internal.TypeSystem.TypesDebugInfo +{ + public interface ITypesDebugInfoWriter + { + uint GetEnumTypeIndex(EnumTypeDescriptor enumTypeDescriptor, EnumRecordTypeDescriptor[] typeRecords); + + uint GetClassTypeIndex(ClassTypeDescriptor classTypeDescriptor); + + uint GetCompleteClassTypeIndex(ClassTypeDescriptor classTypeDescriptor, ClassFieldsTypeDescriptor classFieldsTypeDescriptor, + DataFieldDescriptor[] fields, StaticDataFieldDescriptor[] statics); + + uint GetArrayTypeIndex(ClassTypeDescriptor classDescriptor, ArrayTypeDescriptor arrayTypeDescriprtor); + + uint GetPointerTypeIndex(PointerTypeDescriptor pointerDescriptor); + + uint GetMemberFunctionTypeIndex(MemberFunctionTypeDescriptor memberDescriptor, uint[] argumentTypes); + + uint GetMemberFunctionId(MemberFunctionIdTypeDescriptor memberIdDescriptor); + + uint GetPrimitiveTypeIndex(TypeDesc type); + + string GetMangledName(TypeDesc type); + } + + [StructLayout(LayoutKind.Sequential)] + public struct EnumRecordTypeDescriptor + { + public ulong Value; + public string Name; + } + + [StructLayout(LayoutKind.Sequential)] + public struct EnumTypeDescriptor + { + public uint ElementType; + public ulong ElementCount; + public string Name; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ClassTypeDescriptor + { + public int IsStruct; + public string Name; + public uint BaseClassId; + public ulong InstanceSize; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DataFieldDescriptor + { + public uint FieldTypeIndex; + public ulong Offset; + public string Name; + } + + [StructLayout(LayoutKind.Sequential)] + public struct StaticDataFieldDescriptor + { + public string StaticDataName; + public ulong StaticOffset; + public int IsStaticDataInObject; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ClassFieldsTypeDescriptor + { + public ulong Size; + public int FieldsCount; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ArrayTypeDescriptor + { + public uint Rank; + public uint ElementType; + public uint Size; + public int IsMultiDimensional; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PointerTypeDescriptor + { + public uint ElementType; + public int IsReference; + public int IsConst; + public int Is64Bit; // Otherwise, 32 bit + } + + [StructLayout(LayoutKind.Sequential)] + public struct MemberFunctionTypeDescriptor + { + public uint ReturnType; + public uint ContainingClass; + public uint TypeIndexOfThisPointer; + public uint ThisAdjust; + public uint CallingConvention; + public ushort NumberOfArguments; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MemberFunctionIdTypeDescriptor + { + public uint MemberFunction; + public uint ParentClass; + public string Name; + } +} diff --git a/src/coreclr/tools/StressLogAnalyzer/CMakeLists.txt b/src/coreclr/tools/StressLogAnalyzer/CMakeLists.txt new file mode 100644 index 00000000000000..7654f027435513 --- /dev/null +++ b/src/coreclr/tools/StressLogAnalyzer/CMakeLists.txt @@ -0,0 +1,15 @@ +set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded) +add_executable_clr(StressLogAnalyzer StressLogAnalyzer.cpp StressLogDump.cpp StressLogPlugin.cpp) + +if(CLR_CMAKE_TARGET_WIN32) + target_link_libraries(StressLogAnalyzer + ${STATIC_MT_CRT_LIB} + ${STATIC_MT_VCRT_LIB} + ) +else() + target_link_libraries(StressLogAnalyzer + coreclrpal + ) +endif(CLR_CMAKE_TARGET_WIN32) + +install_clr(TARGETS StressLogAnalyzer DESTINATIONS . COMPONENT runtime) diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.cpp b/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.cpp index 42ee883921ed32..8b5523299762e9 100644 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.cpp +++ b/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.cpp @@ -8,9 +8,27 @@ #include "assert.h" +#include + #define MEMORY_MAPPED_STRESSLOG -int ParseCommandLine(wchar_t* s, wchar_t** argv, int maxArgc) +#ifdef HOST_WINDOWS +#define MEMORY_MAPPED_STRESSLOG_BASE_ADDRESS (void*)0x400000000000 +#else +#define MEMORY_MAPPED_STRESSLOG_BASE_ADDRESS nullptr +#endif + +// This macro is used to standardize the wide character string literals between UNIX and Windows. +// Unix L"" is UTF32, and on windows it's UTF16. Because of built-in assumptions on the size +// of string literals, it's important to match behaviour between Unix and Windows. Unix will be defined +// as u"" (char16_t) +#ifdef TARGET_UNIX +#define W(str) u##str +#else // TARGET_UNIX +#define W(str) L##str +#endif // TARGET_UNIX + +int ParseCommandLine(char* s, char** argv, int maxArgc) { int argc = 0; bool prevWasSpace = true; @@ -51,16 +69,30 @@ int ParseCommandLine(wchar_t* s, wchar_t** argv, int maxArgc) return argc; } -int wmain(int argc, wchar_t *argv[]) +int ProcessStressLog(void* baseAddress, int argc, char* argv[]); + +int main(int argc, char *argv[]) { - if (argc < 2 || wcscmp(argv[1], L"-?") == 0) +#ifdef HOST_UNIX + int exitCode = PAL_Initialize(argc, argv); + if (exitCode != 0) + { + fprintf(stderr, "PAL initialization FAILED %d\n", exitCode); + return exitCode; + } +#endif + + if (argc < 2 || strcmp(argv[1], "-?") == 0) { printf("Usage: StressLog \n"); printf(" StressLog -? for list of options\n"); return 1; } + WCHAR filename[MAX_PATH]; + if (MultiByteToWideChar(CP_ACP, 0, argv[1], -1, filename, MAX_PATH) == 0) + return 1; - HANDLE file = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE file = CreateFile(filename, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (file == INVALID_HANDLE_VALUE) { printf("file not found\n"); @@ -81,7 +113,7 @@ int wmain(int argc, wchar_t *argv[]) printf("could not create file mapping\n"); return 1; } - void* baseAddress = MapViewOfFileEx(map, FILE_MAP_READ, 0, 0, size, (void*)0x400000000000); + void* baseAddress = MapViewOfFileEx(map, FILE_MAP_READ, 0, 0, size, MEMORY_MAPPED_STRESSLOG_BASE_ADDRESS); if (baseAddress == nullptr) { printf("could not map view of file\n"); @@ -117,30 +149,11 @@ int wmain(int argc, wchar_t *argv[]) #endif argc -= 2; argv += 2; - wchar_t* largv[128]; + char* largv[128]; memset(largv, 0, sizeof(largv)); while (true) { - typedef int ProcessStresslog(void* baseAddress, int argc, wchar_t* argv[]); - - HMODULE plugin = LoadLibrary(L"StressLogPlugin.dll"); - - if (plugin == nullptr) - { - printf("could not load StressLogPlugin.dll"); - return 1; - } - - ProcessStresslog* processStressLog = (ProcessStresslog*)GetProcAddress(plugin, "ProcessStresslog"); - if (processStressLog == nullptr) - { - printf("could not find entry point ProcessStresslog in StressLogPlugin.dll"); - return 1; - } - - int error = processStressLog(baseAddress, argc, argv); - - FreeLibrary(plugin); + int error = ProcessStressLog(baseAddress, argc, argv); if (error != 0) { @@ -148,11 +161,11 @@ int wmain(int argc, wchar_t *argv[]) } bool runAgain = false; - wchar_t s[1024]; + char s[1024]; while (true) { printf("'q' to quit, 'r' to run again\n>"); - if (fgetws(s, 1023, stdin) == nullptr) + if (fgets(s, 1023, stdin) == nullptr) continue; switch (s[0]) { @@ -184,4 +197,6 @@ int wmain(int argc, wchar_t *argv[]) break; } } + + return 0; } diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.sln b/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.sln deleted file mode 100644 index 2ba0ed163aa436..00000000000000 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.sln +++ /dev/null @@ -1,44 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30717.126 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StressLogAnalyzer", "StressLogAnalyzer.vcxproj", "{C6955B43-4176-4259-8831-9A60596E373D}" - ProjectSection(ProjectDependencies) = postProject - {6BADDA84-5ED3-40EF-B4A5-553C446D43AA} = {6BADDA84-5ED3-40EF-B4A5-553C446D43AA} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StressLogPlugin", "StressLogPlugin\StressLogPlugin.vcxproj", "{6BADDA84-5ED3-40EF-B4A5-553C446D43AA}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C6955B43-4176-4259-8831-9A60596E373D}.Debug|x64.ActiveCfg = Debug|x64 - {C6955B43-4176-4259-8831-9A60596E373D}.Debug|x64.Build.0 = Debug|x64 - {C6955B43-4176-4259-8831-9A60596E373D}.Debug|x86.ActiveCfg = Debug|Win32 - {C6955B43-4176-4259-8831-9A60596E373D}.Debug|x86.Build.0 = Debug|Win32 - {C6955B43-4176-4259-8831-9A60596E373D}.Release|x64.ActiveCfg = Release|x64 - {C6955B43-4176-4259-8831-9A60596E373D}.Release|x64.Build.0 = Release|x64 - {C6955B43-4176-4259-8831-9A60596E373D}.Release|x86.ActiveCfg = Release|Win32 - {C6955B43-4176-4259-8831-9A60596E373D}.Release|x86.Build.0 = Release|Win32 - {6BADDA84-5ED3-40EF-B4A5-553C446D43AA}.Debug|x64.ActiveCfg = Debug|x64 - {6BADDA84-5ED3-40EF-B4A5-553C446D43AA}.Debug|x64.Build.0 = Debug|x64 - {6BADDA84-5ED3-40EF-B4A5-553C446D43AA}.Debug|x86.ActiveCfg = Debug|Win32 - {6BADDA84-5ED3-40EF-B4A5-553C446D43AA}.Debug|x86.Build.0 = Debug|Win32 - {6BADDA84-5ED3-40EF-B4A5-553C446D43AA}.Release|x64.ActiveCfg = Release|x64 - {6BADDA84-5ED3-40EF-B4A5-553C446D43AA}.Release|x64.Build.0 = Release|x64 - {6BADDA84-5ED3-40EF-B4A5-553C446D43AA}.Release|x86.ActiveCfg = Release|Win32 - {6BADDA84-5ED3-40EF-B4A5-553C446D43AA}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {BB9CFD6B-BEF2-43F8-A459-AD8B42CF37A9} - EndGlobalSection -EndGlobal diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.vcxproj.filters b/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.vcxproj.filters deleted file mode 100644 index 81f2a77fb8cb5b..00000000000000 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.vcxproj.filters +++ /dev/null @@ -1,22 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Quelldateien - - - \ No newline at end of file diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.vcxproj.users b/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.vcxproj.users deleted file mode 100644 index 0568ca62bf941f..00000000000000 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.vcxproj.users +++ /dev/null @@ -1,11 +0,0 @@ - - - - I:\Issue_41928\Fix_2\Stresslog1749.log -e - WindowsLocalDebugger - - - i:\gcperfsim\gcperfsimstresslog.log - WindowsLocalDebugger - - \ No newline at end of file diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/stressLogDump.cpp b/src/coreclr/tools/StressLogAnalyzer/StressLogDump.cpp similarity index 95% rename from src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/stressLogDump.cpp rename to src/coreclr/tools/StressLogAnalyzer/StressLogDump.cpp index 2b3c1f884a3a22..a0e8c877be52f2 100644 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/stressLogDump.cpp +++ b/src/coreclr/tools/StressLogAnalyzer/StressLogDump.cpp @@ -6,15 +6,29 @@ // // ==--== -#include "pch.h" #include "strike.h" #include "util.h" #include #include +#include #ifndef STRESS_LOG #define STRESS_LOG + +class MapViewHolder +{ + void* whatever; +}; + +typedef unsigned char uint8_t; +typedef unsigned int uint32_t; +#ifdef HOST_WINDOWS +typedef long long int64_t; +#else +#define FEATURE_PAL +#endif +typedef size_t uint64_t; #endif // STRESS_LOG #define STRESS_LOG_READONLY #include "../../../inc/stresslog.h" @@ -87,7 +101,7 @@ const char *getFacilityName(DWORD_PTR lf) } else if ((((DWORD)lf) & (LF_ALWAYS | 0xfffe | LF_GC)) == (LF_ALWAYS | LF_GC)) { - sprintf_s(buff, "`GC l=%d`", (lf >> 16) & 0x7fff); + sprintf_s(buff, ARRAY_SIZE(buff), "`GC l=%d`", (int)((lf >> 16) & 0x7fff)); return buff; } else @@ -119,7 +133,7 @@ void formatOutput(struct IDebugDataSpaces* memCallBack, ___in FILE* file, __inou else if (threadId & 0x4000000000000000) fprintf(file, "BG%2d %13.9f : ", (unsigned)threadId, timeStamp); else - fprintf(file, "%4x %13.9f : ", threadId, timeStamp); + fprintf(file, "%4x %13.9f : ", (int)threadId, timeStamp); fprintf(file, "%-20s ", getFacilityName ( facility )); if (fPrintFormatString) @@ -162,7 +176,7 @@ void formatOutput(struct IDebugDataSpaces* memCallBack, ___in FILE* file, __inou // Print the string up to that point c = *ptr; *ptr = 0; // Terminate the string temporarily - vfprintf(file, format, (va_list)args); + fprintf(file, format, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]); *ptr = c; // Put it back // move the argument pointers past the part the was printed @@ -176,7 +190,7 @@ void formatOutput(struct IDebugDataSpaces* memCallBack, ___in FILE* file, __inou case 'M': // format as a method Desc if (g_bDacBroken) { - fprintf(file," (MethodDesc: %p)",arg); + fprintf(file," (MethodDesc: %p)", (void*)arg); } else { @@ -206,7 +220,7 @@ void formatOutput(struct IDebugDataSpaces* memCallBack, ___in FILE* file, __inou case 'T': // format as a MethodTable if (g_bDacBroken) { - fprintf(file, "(MethodTable: %p)",arg); + fprintf(file, "(MethodTable: %p)", (void*)arg); } else { @@ -248,7 +262,7 @@ void formatOutput(struct IDebugDataSpaces* memCallBack, ___in FILE* file, __inou fprintf (file, " (%s", Symbol); if (Displacement) { - fprintf (file, "+%#x", Displacement); + fprintf (file, "+%#llx", Displacement); } fprintf (file, ")"); } @@ -298,8 +312,9 @@ void formatOutput(struct IDebugDataSpaces* memCallBack, ___in FILE* file, __inou iArgCount++; } } - // Print anything after the last special format instruction. - vfprintf(file, format, (va_list)args); + + // Print anything after the last special format instruction. + fprintf(file, format, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]); fprintf(file, "\n"); } @@ -532,7 +547,7 @@ HRESULT StressLog::Dump(ULONG64 outProcLog, const char* fileName, struct IDebugD latestLog->readPtr = NULL; if (!bDoGcHist) { - fprintf(file, "------------ Last message from thread %x -----------\n", latestLog->threadId); + fprintf(file, "------------ Last message from thread %llx -----------\n", latestLog->threadId); } } diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.cpp b/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin.cpp similarity index 87% rename from src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.cpp rename to src/coreclr/tools/StressLogAnalyzer/StressLogPlugin.cpp index 54a54bb72cfcfd..2b8b08588236e3 100644 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.cpp +++ b/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin.cpp @@ -1,13 +1,23 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#include "pch.h" - #include #include #include #include +#ifndef INFINITY +#define INFINITY 1e300 // Practically good enough - not sure why we miss this in our Linux build. +#endif + +#ifndef DLLEXPORT +#ifdef _MSC_VER +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT __attribute__ ((visibility ("default"))) +#endif // _MSC_VER +#endif // DLLEXPORT + #include "strike.h" #include "util.h" @@ -24,7 +34,9 @@ class MapViewHolder typedef unsigned char uint8_t; typedef unsigned int uint32_t; +#ifdef HOST_WINDOWS typedef long long int64_t; +#endif typedef size_t uint64_t; bool IsInCantAllocStressLogRegion() @@ -32,8 +44,12 @@ bool IsInCantAllocStressLogRegion() return true; } +#include #include "../../../inc/stresslog.h" +size_t StressLog::writing_base_address; +size_t StressLog::reading_base_address; + BOOL g_bDacBroken; WCHAR g_mdName[1]; SYMBOLS* g_ExtSymbols; @@ -68,7 +84,7 @@ struct CorClrData : IDebugDataSpaces virtual HRESULT ReadVirtual(void* src, void* dest, size_t size, int) { size_t cumSize = 0; - for (int moduleIndex = 0; moduleIndex < StressLog::MAX_MODULES; moduleIndex++) + for (size_t moduleIndex = 0; moduleIndex < StressLog::MAX_MODULES; moduleIndex++) { ptrdiff_t offs = (uint8_t*)src - hdr->modules[moduleIndex].baseAddress; if ((size_t)offs < hdr->modules[moduleIndex].size && (size_t)offs + size < hdr->modules[moduleIndex].size) @@ -355,6 +371,9 @@ bool FilterMessage(StressLog::StressLogHeader* hdr, ThreadStressLog* tsl, uint32 InterestingStringId isd = FindStringId(hdr, format); switch (isd) { + case IS_UNINTERESTING: + case IS_UNKNOWN: + break; case IS_THREAD_WAIT: case IS_THREAD_WAIT_DONE: RememberThreadForHeap(tsl->threadId, (int64_t)args[0], GC_THREAD_FG); @@ -527,8 +546,8 @@ int CmpMsg(const void* p1, const void* p2) struct ThreadStressLogDesc { - volatile unsigned workStarted; - volatile unsigned workFinished; + volatile LONG workStarted; + volatile LONG workFinished; ThreadStressLog* tsl; StressMsg* earliestMessage; @@ -540,15 +559,15 @@ struct ThreadStressLogDesc static const int MAX_THREADSTRESSLOGS = 64 * 1024; static ThreadStressLogDesc s_threadStressLogDesc[MAX_THREADSTRESSLOGS]; static int s_threadStressLogCount; -static LONG s_wrappedWriteThreadCount; +static LONG64 s_wrappedWriteThreadCount; static const LONG MAX_MESSAGE_COUNT = 64 * 1024 * 1024; static StressThreadAndMsg* s_threadMsgBuf; -static volatile LONGLONG s_msgCount = 0; -static volatile LONGLONG s_totalMsgCount = 0; +static volatile LONG64 s_msgCount = 0; +static volatile LONG64 s_totalMsgCount = 0; static double s_timeFilterStart = 0; static double s_timeFilterEnd = 0; -static wchar_t* s_outputFileName = nullptr; +static WCHAR* s_outputFileName = nullptr; static StressLog::StressLogHeader* s_hdr; @@ -625,12 +644,12 @@ static void InterpretEscapeSequences(char* s) *d = '\0'; } -bool ParseOptions(int argc, wchar_t* argv[]) +bool ParseOptions(int argc, char* argv[]) { int i = 0; while (i < argc) { - wchar_t* arg = argv[i]; + char* arg = argv[i]; if (arg[0] == '-') { switch (arg[1]) @@ -645,15 +664,15 @@ bool ParseOptions(int argc, wchar_t* argv[]) if (arg[2] == ':') { int i = s_valueFilterCount++; - wchar_t* end = nullptr; - s_valueFilter[i].start = _wcstoui64(&arg[3], &end, 16); + char* end = nullptr; + s_valueFilter[i].start = strtoul(&arg[3], &end, 16); if (*end == '-') { - s_valueFilter[i].end = _wcstoui64(end + 1, &end, 16); + s_valueFilter[i].end = strtoul(end + 1, &end, 16); } else if (*end == '+') { - s_valueFilter[i].end = s_valueFilter[i].start + _wcstoui64(end + 1, &end, 16); + s_valueFilter[i].end = s_valueFilter[i].start + strtoul(end + 1, &end, 16); } else if (*end != '\0') { @@ -666,7 +685,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) } if (*end != '\0') { - printf("could not parse option %S\n", arg); + printf("could not parse option %s\n", arg); return false; } } @@ -681,15 +700,15 @@ bool ParseOptions(int argc, wchar_t* argv[]) case 'T': if (arg[2] == ':') { - wchar_t* end = nullptr; - s_timeFilterStart = wcstod(&arg[3], &end); + char* end = nullptr; + s_timeFilterStart = strtod(&arg[3], &end); if (*end == '-') { - s_timeFilterEnd = wcstod(end + 1, &end); + s_timeFilterEnd = strtod(end + 1, &end); } else if (*end == '+') { - s_timeFilterEnd = s_timeFilterStart + wcstod(end + 1, &end); + s_timeFilterEnd = s_timeFilterStart + strtod(end + 1, &end); } else { @@ -697,11 +716,11 @@ bool ParseOptions(int argc, wchar_t* argv[]) } if (*end != '\0') { - printf("could not parse option %S\n", arg); + printf("could not parse option %s\n", arg); return false; } } - else if (_wcsnicmp(arg, L"-tid:", 5) == 0) + else if (_strnicmp(arg, "-tid:", 5) == 0) { arg = arg + 5; while (true) @@ -711,11 +730,11 @@ bool ParseOptions(int argc, wchar_t* argv[]) printf("too many thread filters - max is %d\n", MAX_THREAD_FILTERS); return false; } - wchar_t* end = nullptr; - if (_wcsnicmp(arg, L"gc", 2) == 0 || _wcsnicmp(arg, L"bg", 2) == 0) + char* end = nullptr; + if (_strnicmp(arg, "gc", 2) == 0 || _strnicmp(arg, "bg", 2) == 0) { - int gcHeapNumber = wcstol(arg+2, &end, 10); - GcThreadKind kind = _wcsnicmp(arg, L"gc", 2) == 0 ? GC_THREAD_FG : GC_THREAD_BG; + int gcHeapNumber = strtoul(arg+2, &end, 10); + GcThreadKind kind = _strnicmp(arg, "gc", 2) == 0 ? GC_THREAD_FG : GC_THREAD_BG; if (gcHeapNumber < MAX_NUMBER_OF_HEAPS) { s_gcThreadFilter[gcHeapNumber][kind] = true; @@ -730,7 +749,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) else { int i = s_threadFilterCount++; - s_threadFilter[i] = _wcstoui64(arg, &end, 16); + s_threadFilter[i] = strtoul(arg, &end, 16); } if (*end == ',') { @@ -738,7 +757,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) } else if (*end != '\0') { - printf("could not parse %S\n", arg); + printf("could not parse %s\n", arg); return false; } else @@ -747,7 +766,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) } } } - else if (_wcsicmp(arg, L"-tid") == 0) + else if (_stricmp(arg, "-tid") == 0) { s_printHexTidForGcThreads = true; } @@ -763,7 +782,10 @@ bool ParseOptions(int argc, wchar_t* argv[]) case 'O': if (arg[2] == ':') { - s_outputFileName = &arg[3]; + WCHAR* buffer = new WCHAR[1000]; + if (MultiByteToWideChar(CP_ACP, 0, &arg[3], -1, buffer, 1000) == 0) + return false; + s_outputFileName = buffer; } else { @@ -785,7 +807,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) return false; } int i = s_levelFilterCount++; - wchar_t* end = nullptr; + char* end = nullptr; if (*arg == '*') { s_levelFilter[i].minLevel = 0; @@ -794,10 +816,10 @@ bool ParseOptions(int argc, wchar_t* argv[]) } else { - s_levelFilter[i].minLevel = wcstol(arg, &end, 10); + s_levelFilter[i].minLevel = strtoul(arg, &end, 10); if (*end == '-') { - s_levelFilter[i].maxLevel = wcstol(end + 1, &end, 10); + s_levelFilter[i].maxLevel = strtoul(end + 1, &end, 10); } else { @@ -810,7 +832,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) } else if (*end != '\0') { - printf("could not parse option %S\n", arg); + printf("could not parse option %s\n", arg); return false; } else @@ -840,23 +862,18 @@ bool ParseOptions(int argc, wchar_t* argv[]) return false; } arg = &arg[3]; - size_t requiredSize = 0; - if (wcstombs_s(&requiredSize, nullptr, 0, arg, 0) != 0) - return false; - char* buf = new char[requiredSize]; - size_t actualSize = 0; - if (wcstombs_s(&actualSize, buf, requiredSize, arg, requiredSize) != 0) - return false; + char* buf = arg; + size_t actualSize = strlen(buf); if (actualSize <= 1) { printf("-f: expected\n"); return false; } - assert(actualSize == requiredSize); + // remove double quotes around the string, if given - if (actualSize >= 2 && buf[0] == '"' && buf[actualSize - 2] == '"') + if (actualSize >= 2 && buf[0] == '"' && buf[actualSize - 1] == '"') { - buf[actualSize - 2] = '\0'; + buf[actualSize - 1] = '\0'; buf++; } InterpretEscapeSequences(buf); @@ -868,11 +885,11 @@ bool ParseOptions(int argc, wchar_t* argv[]) case 'G': if (arg[2] == ':') { - wchar_t* end = nullptr; - s_gcFilterStart = wcstol(arg+3, &end, 10); + char* end = nullptr; + s_gcFilterStart = strtoul(arg+3, &end, 10); if (*end == '-') { - s_gcFilterEnd = wcstol(end+1, &end, 10); + s_gcFilterEnd = strtoul(end+1, &end, 10); } else { @@ -880,7 +897,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) } if (*end != '\0') { - printf("could not parse option %S\n", arg); + printf("could not parse option %s\n", arg); return false; } } @@ -895,11 +912,11 @@ bool ParseOptions(int argc, wchar_t* argv[]) case 'I': if (arg[2] == ':') { - wchar_t* end = nullptr; - s_facilityIgnore = wcstoul(arg + 3, &end, 16); + char* end = nullptr; + s_facilityIgnore = strtoul(arg + 3, &end, 16); if (*end != '\0') { - printf("could not parse option %S\n", arg); + printf("could not parse option %s\n", arg); return false; } } @@ -926,11 +943,11 @@ bool ParseOptions(int argc, wchar_t* argv[]) printf("too many threads - max is %d\n", MAX_THREAD_FILTERS); return false; } - wchar_t* end = nullptr; - if (_wcsnicmp(arg, L"gc", 2) == 0 || _wcsnicmp(arg, L"bg", 2) == 0) + char* end = nullptr; + if (_strnicmp(arg, "gc", 2) == 0 || _strnicmp(arg, "bg", 2) == 0) { - int gcHeapNumber = wcstol(arg + 2, &end, 10); - GcThreadKind kind = _wcsnicmp(arg, L"gc", 2) == 0 ? GC_THREAD_FG : GC_THREAD_BG; + int gcHeapNumber = strtoul(arg + 2, &end, 10); + GcThreadKind kind = _strnicmp(arg, "gc", 2) == 0 ? GC_THREAD_FG : GC_THREAD_BG; if (gcHeapNumber < MAX_NUMBER_OF_HEAPS) { s_printEarliestMessageFromGcThread[gcHeapNumber][kind] = true; @@ -944,7 +961,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) else { int i = s_printEarliestMessageFromThreadCount++; - s_printEarliestMessageFromThread[i] = _wcstoui64(arg, &end, 16); + s_printEarliestMessageFromThread[i] = strtoul(arg, &end, 16); } if (*end == ',') { @@ -952,7 +969,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) } else if (*end != '\0') { - printf("could not parse %S\n", arg); + printf("could not parse %s\n", arg); return false; } else @@ -963,7 +980,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) } else { - printf("could not parse option %S\n", arg); + printf("could not parse option %s\n", arg); return false; } break; @@ -973,7 +990,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) return false; default: - printf("unrecognized option %S\n", arg); + printf("unrecognized option %s\n", arg); return false; } } @@ -988,7 +1005,7 @@ bool ParseOptions(int argc, wchar_t* argv[]) static void IncludeMessage(uint64_t threadId, StressMsg* msg) { - LONGLONG msgCount = _InterlockedIncrement64(&s_msgCount) - 1; + LONGLONG msgCount = InterlockedIncrement64(&s_msgCount) - 1; if (msgCount < MAX_MESSAGE_COUNT) { s_threadMsgBuf[msgCount].threadId = threadId; @@ -1018,14 +1035,14 @@ DWORD WINAPI ProcessStresslogWorker(LPVOID) { wrappedWriteThreadCount++; } - // printf("thread: %Ix\n", tsl->threadId); - StressMsg* msg = tsl->curPtr; - StressLogChunk* slc = tsl->curWriteChunk; + // printf("thread: %Ix\n", tsl->threadId); + StressMsg* msg = StressLog::TranslateMemoryMappedPointer(tsl->curPtr); + StressLogChunk* slc = StressLog::TranslateMemoryMappedPointer(tsl->curWriteChunk); int chunkCount = 0; StressMsg* prevMsg = nullptr; while (true) { - // printf("stress log chunk %Ix\n", (size_t)slc); + // printf("stress log chunk %Ix\n", (size_t)slc); if (!slc->IsValid()) { printf("oops, invalid stress log chunk\n"); @@ -1040,11 +1057,11 @@ DWORD WINAPI ProcessStresslogWorker(LPVOID) } msg = nullptr; } - assert(slc->next->prev == slc); - assert(slc->prev->next == slc); + assert(StressLog::TranslateMemoryMappedPointer(StressLog::TranslateMemoryMappedPointer(slc->next)->prev) == slc); + assert(StressLog::TranslateMemoryMappedPointer(StressLog::TranslateMemoryMappedPointer(slc->prev)->next) == slc); #ifdef _DEBUG int chunkCount1 = 0; - for (StressLogChunk* slc1 = tsl->curWriteChunk; slc1 != slc; slc1 = slc1->next) + for (StressLogChunk* slc1 = StressLog::TranslateMemoryMappedPointer(tsl->curWriteChunk); slc1 != slc; slc1 = StressLog::TranslateMemoryMappedPointer(slc1->next)) { chunkCount1++; } @@ -1114,10 +1131,10 @@ DWORD WINAPI ProcessStresslogWorker(LPVOID) prevMsg = msg; msg = (StressMsg*)&msg->args[numberOfArgs]; } - if (slc == tsl->chunkListTail && !tsl->writeHasWrapped) + if (slc == StressLog::TranslateMemoryMappedPointer(tsl->chunkListTail) && !tsl->writeHasWrapped) break; - slc = slc->next; - if (slc == tsl->curWriteChunk) + slc = StressLog::TranslateMemoryMappedPointer(slc->next); + if (slc == StressLog::TranslateMemoryMappedPointer(tsl->curWriteChunk)) break; if (s_hadGcThreadFilters && !FilterThread(tsl)) break; @@ -1134,7 +1151,7 @@ DWORD WINAPI ProcessStresslogWorker(LPVOID) } InterlockedAdd64(&s_totalMsgCount, totalMsgCount); - InterlockedAdd(&s_wrappedWriteThreadCount, wrappedWriteThreadCount); + InterlockedAdd64(&s_wrappedWriteThreadCount, wrappedWriteThreadCount); return 0; } @@ -1142,9 +1159,9 @@ DWORD WINAPI ProcessStresslogWorker(LPVOID) static double FindLatestTime(StressLog::StressLogHeader* hdr) { double latestTime = 0.0; - for (ThreadStressLog* tsl = hdr->logs.t; tsl != nullptr; tsl = tsl->next) + for (ThreadStressLog* tsl = StressLog::TranslateMemoryMappedPointer(hdr->logs.t); tsl != nullptr; tsl = StressLog::TranslateMemoryMappedPointer(tsl->next)) { - StressMsg* msg = tsl->curPtr; + StressMsg* msg = StressLog::TranslateMemoryMappedPointer(tsl->curPtr); double deltaTime = ((double)(msg->timeStamp - hdr->startTimeStamp)) / hdr->tickFrequency; latestTime = max(latestTime, deltaTime); } @@ -1154,7 +1171,7 @@ static double FindLatestTime(StressLog::StressLogHeader* hdr) static void PrintFriendlyNumber(LONGLONG n) { if (n < 1000) - printf("%d", n); + printf("%lld", n); else if (n < 1000 * 1000) printf("%5.3f thousand", n / 1000.0); else if (n < 1000 * 1000 * 1000) @@ -1188,19 +1205,34 @@ static void PrintMessage(CorClrData& corClrData, FILE *outputFile, uint64_t thre formatOutput(&corClrData, outputFile, format, threadId, deltaTime, msg->facility, argBuffer, s_fPrintFormatStrings); } -extern "C" int __declspec(dllexport) ProcessStresslog(void* baseAddress, int argc, wchar_t* argv[]) +int ProcessStressLog(void* baseAddress, int argc, char* argv[]) { + for (int threadStressLogIndex = 0; threadStressLogIndex < s_threadStressLogCount; threadStressLogIndex++) + { + s_threadStressLogDesc[threadStressLogIndex].workStarted = 0; + s_threadStressLogDesc[threadStressLogIndex].workFinished = 0; + } + s_msgCount = 0; + s_totalMsgCount = 0; + s_timeFilterStart = 0; + s_timeFilterEnd = 0; + s_outputFileName = nullptr; + s_fPrintFormatStrings = false; + memset(&mapImageToStringId, 0, sizeof(mapImageToStringId)); + if (!ParseOptions(argc, argv)) return 1; StressLog::StressLogHeader* hdr = (StressLog::StressLogHeader*)baseAddress; if (hdr->headerSize != sizeof(*hdr) || - hdr->magic != 'STRL' || + hdr->magic != *(uint32_t*)"LRTS" || hdr->version != 0x00010001) { printf("Unrecognized file format\n"); return 1; } + StressLog::writing_base_address = (size_t)hdr->memoryBase; + StressLog::reading_base_address = (size_t)baseAddress; s_hdr = hdr; s_threadMsgBuf = new StressThreadAndMsg[MAX_MESSAGE_COUNT]; int threadStressLogIndex = 0; @@ -1210,7 +1242,7 @@ extern "C" int __declspec(dllexport) ProcessStresslog(void* baseAddress, int arg s_timeFilterStart = max(latestTime + s_timeFilterStart, 0); s_timeFilterEnd = latestTime; } - for (ThreadStressLog* tsl = hdr->logs.t; tsl != nullptr; tsl = tsl->next) + for (ThreadStressLog* tsl = StressLog::TranslateMemoryMappedPointer(hdr->logs.t); tsl != nullptr; tsl = StressLog::TranslateMemoryMappedPointer(tsl->next)) { if (!tsl->IsValid()) continue; @@ -1340,14 +1372,14 @@ extern "C" int __declspec(dllexport) ProcessStresslog(void* baseAddress, int arg FILE* outputFile = stdout; if (s_outputFileName != nullptr) { - if (_wfopen_s(&outputFile, s_outputFileName, L"w") != 0) + if (_wfopen_s(&outputFile, s_outputFileName, W("w")) != 0) { printf("could not create output file %S\n", s_outputFileName); outputFile = stdout; } } - for (size_t i = 0; i < s_msgCount; i++) + for (LONGLONG i = 0; i < s_msgCount; i++) { uint64_t threadId = (unsigned)s_threadMsgBuf[i].threadId; StressMsg* msg = s_threadMsgBuf[i].msg; @@ -1384,7 +1416,7 @@ extern "C" int __declspec(dllexport) ProcessStresslog(void* baseAddress, int arg if (s_printEarliestMessages || s_printEarliestMessageFromThreadCount > 0) { fprintf(outputFile, "\nEarliestMessages:\n"); - LONG earliestStartCount = s_msgCount; + LONGLONG earliestStartCount = s_msgCount; for (int threadStressLogIndex = 0; threadStressLogIndex < s_threadStressLogCount; threadStressLogIndex++) { StressMsg* msg = s_threadStressLogDesc[threadStressLogIndex].earliestMessage; @@ -1409,7 +1441,7 @@ extern "C" int __declspec(dllexport) ProcessStresslog(void* baseAddress, int arg } } qsort(&s_threadMsgBuf[earliestStartCount], s_msgCount - earliestStartCount, sizeof(s_threadMsgBuf[0]), CmpMsg); - for (size_t i = earliestStartCount; i < s_msgCount; i++) + for (LONGLONG i = earliestStartCount; i < s_msgCount; i++) { uint64_t threadId = (unsigned)s_threadMsgBuf[i].threadId; StressMsg* msg = s_threadMsgBuf[i].msg; @@ -1424,7 +1456,7 @@ extern "C" int __declspec(dllexport) ProcessStresslog(void* baseAddress, int arg ptrdiff_t availSize = hdr->memoryLimit - hdr->memoryCur; printf("Used file size: %6.3f GB, still available: %6.3f GB, %d threads total, %d overwrote earlier messages\n", (double)usedSize / (1024 * 1024 * 1024), (double)availSize/ (1024 * 1024 * 1024), - s_threadStressLogCount, s_wrappedWriteThreadCount); + s_threadStressLogCount, (int)s_wrappedWriteThreadCount); if (hdr->threadsWithNoLog != 0) printf("%Id threads did not get a log!\n", hdr->threadsWithNoLog); printf("Number of messages examined: "); PrintFriendlyNumber(s_totalMsgCount); printf(", printed: "); PrintFriendlyNumber(s_msgCount); printf("\n"); @@ -1433,20 +1465,3 @@ extern "C" int __declspec(dllexport) ProcessStresslog(void* baseAddress, int arg return 0; } - -BOOL APIENTRY DllMain( HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} - diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.vcxproj b/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.vcxproj deleted file mode 100644 index 1fe578f3b7536b..00000000000000 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.vcxproj +++ /dev/null @@ -1,174 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {6badda84-5ed3-40ef-b4a5-553c446d43aa} - StressLogPlugin - 10.0 - - - - DynamicLibrary - true - v142 - Unicode - - - DynamicLibrary - false - v142 - true - Unicode - - - DynamicLibrary - true - v142 - Unicode - - - DynamicLibrary - false - v142 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - - - false - - - true - - - false - true - - - $(BaseOutputPath)..\StressLogAnalyzer\$(Platform)\$(Configuration)\ - - - - Level3 - true - WIN32;_DEBUG;STRESSLOGPLUGIN_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - Use - pch.h - - - Windows - true - false - - - - - Level3 - true - true - true - WIN32;NDEBUG;STRESSLOGPLUGIN_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - Use - pch.h - - - Windows - true - true - true - false - - - - - Level3 - true - _DEBUG;STRESSLOGPLUGIN_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - Use - pch.h - - - Windows - true - false - - - - - Level3 - true - true - true - NDEBUG;STRESSLOGPLUGIN_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - Use - pch.h - - - Windows - true - true - true - false - - - - - - - - - - - Create - Create - Create - Create - - - - - - \ No newline at end of file diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.vcxproj.filters b/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.vcxproj.filters deleted file mode 100644 index e2dabc4a63863a..00000000000000 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/StressLogPlugin.vcxproj.filters +++ /dev/null @@ -1,36 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Headerdateien - - - Headerdateien - - - - - Quelldateien - - - Quelldateien - - - Quelldateien - - - \ No newline at end of file diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/framework.h b/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/framework.h deleted file mode 100644 index 8f61743a51dae5..00000000000000 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/framework.h +++ /dev/null @@ -1,8 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#pragma once - -#define WIN32_LEAN_AND_MEAN // Selten verwendete Komponenten aus Windows-Headern ausschließen -// Windows-Headerdateien -#include diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/staticcontract.h b/src/coreclr/tools/StressLogAnalyzer/staticcontract.h similarity index 89% rename from src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/staticcontract.h rename to src/coreclr/tools/StressLogAnalyzer/staticcontract.h index 31fa681126cc45..d3ef6a4ab9bc48 100644 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/staticcontract.h +++ b/src/coreclr/tools/StressLogAnalyzer/staticcontract.h @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#ifndef _ASSERTE #define _ASSERTE(a) +#endif + #define STATIC_CONTRACT_LEAF #define STATIC_CONTRACT_WRAPPER diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/strike.h b/src/coreclr/tools/StressLogAnalyzer/strike.h similarity index 98% rename from src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/strike.h rename to src/coreclr/tools/StressLogAnalyzer/strike.h index c627b7a1148931..5b59ac738f3020 100644 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/strike.h +++ b/src/coreclr/tools/StressLogAnalyzer/strike.h @@ -4,8 +4,6 @@ #include #include -#define HOST_64BIT - typedef unsigned char uint8_t; typedef unsigned int uint32_t; typedef size_t uint64_t; diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/util.h b/src/coreclr/tools/StressLogAnalyzer/util.h similarity index 100% rename from src/coreclr/tools/StressLogAnalyzer/StressLogPlugin/util.h rename to src/coreclr/tools/StressLogAnalyzer/util.h diff --git a/src/coreclr/tools/aot/ILCompiler.Build.Tasks/ComputeManagedAssembliesToCompileToNative.cs b/src/coreclr/tools/aot/ILCompiler.Build.Tasks/ComputeManagedAssembliesToCompileToNative.cs new file mode 100644 index 00000000000000..741a322594a084 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Build.Tasks/ComputeManagedAssembliesToCompileToNative.cs @@ -0,0 +1,187 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; + + + +namespace Build.Tasks +{ + public class ComputeManagedAssembliesToCompileToNative : DesktopCompatibleTask + { + [Required] + public ITaskItem[] Assemblies + { + get; + set; + } + + /// + /// The CoreRT-specific System.Private.* assemblies that must be used instead of the netcoreapp2.1 versions. + /// + [Required] + public ITaskItem[] SdkAssemblies + { + get; + set; + } + + /// + /// The set of AOT-specific framework assemblies we currently need to use which will replace the same-named ones + /// in the app's closure. + /// + [Required] + public ITaskItem[] FrameworkAssemblies + { + get; + set; + } + + /// + /// The native apphost (whose name ends up colliding with the CoreRT output binary) + /// + [Required] + public string DotNetAppHostExecutableName + { + get; + set; + } + + /// + /// The CoreCLR dotnet host fixer library that can be skipped during publish + /// + [Required] + public string DotNetHostFxrLibraryName + { + get; + set; + } + + /// + /// The CoreCLR dotnet host policy library that can be skipped during publish + /// + [Required] + public string DotNetHostPolicyLibraryName + { + get; + set; + } + + [Output] + public ITaskItem[] ManagedAssemblies + { + get; + set; + } + + [Output] + public ITaskItem[] AssembliesToSkipPublish + { + get; + set; + } + + public override bool Execute() + { + var list = new List(); + var assembliesToSkipPublish = new List(); + var coreRTFrameworkAssembliesToUse = new HashSet(); + + foreach (ITaskItem taskItem in SdkAssemblies) + { + coreRTFrameworkAssembliesToUse.Add(Path.GetFileName(taskItem.ItemSpec)); + } + + foreach (ITaskItem taskItem in FrameworkAssemblies) + { + coreRTFrameworkAssembliesToUse.Add(Path.GetFileName(taskItem.ItemSpec)); + } + + foreach (ITaskItem taskItem in Assemblies) + { + // In the case of disk-based assemblies, this holds the file path + string itemSpec = taskItem.ItemSpec; + + // Skip the native apphost (whose name ends up colliding with the CoreRT output binary) and supporting libraries + if (itemSpec.EndsWith(DotNetAppHostExecutableName, StringComparison.OrdinalIgnoreCase) || itemSpec.Contains(DotNetHostFxrLibraryName) || itemSpec.Contains(DotNetHostPolicyLibraryName)) + { + assembliesToSkipPublish.Add(taskItem); + continue; + } + + // Prototype aid - remove the native CoreCLR runtime pieces from the publish folder + if (itemSpec.Contains("microsoft.netcore.app") && (itemSpec.Contains("\\native\\") || itemSpec.Contains("/native/"))) + { + assembliesToSkipPublish.Add(taskItem); + continue; + } + + var assemblyFileName = Path.GetFileName(itemSpec); + + if (assemblyFileName == "WindowsBase.dll") + { + // There are two instances of WindowsBase.dll, one small one, in the NativeAOT framework + // and real one in WindowsDesktop SDK. We want to make sure that if both are present, + // we will use the one from WindowsDesktop SDK, and not from NativeAOT framework. + foreach (ITaskItem taskItemToSkip in FrameworkAssemblies) + { + if (Path.GetFileName(taskItemToSkip.ItemSpec) == assemblyFileName) + { + assembliesToSkipPublish.Add(taskItemToSkip); + break; + } + } + + assembliesToSkipPublish.Add(taskItem); + list.Add(taskItem); + continue; + } + + // Remove any assemblies whose implementation we want to come from CoreRT's package. + // Currently that's System.Private.* SDK assemblies and a bunch of framework assemblies. + if (coreRTFrameworkAssembliesToUse.Contains(assemblyFileName)) + { + assembliesToSkipPublish.Add(taskItem); + continue; + } + + try + { + using (FileStream moduleStream = File.OpenRead(itemSpec)) + using (var module = new PEReader(moduleStream)) + { + if (module.HasMetadata) + { + MetadataReader moduleMetadataReader = module.GetMetadataReader(); + if (moduleMetadataReader.IsAssembly) + { + string culture = moduleMetadataReader.GetString(moduleMetadataReader.GetAssemblyDefinition().Culture); + + assembliesToSkipPublish.Add(taskItem); + if (culture == "" || culture.Equals("neutral", StringComparison.OrdinalIgnoreCase)) + { + // CoreRT doesn't consume resource assemblies yet so skip them + list.Add(taskItem); + } + } + } + } + } + catch (BadImageFormatException) + { + } + } + + ManagedAssemblies = list.ToArray(); + AssembliesToSkipPublish = assembliesToSkipPublish.ToArray(); + + return true; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Build.Tasks/DesktopCompatibleTask.cs b/src/coreclr/tools/aot/ILCompiler.Build.Tasks/DesktopCompatibleTask.cs new file mode 100644 index 00000000000000..6f0b36bea4559d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Build.Tasks/DesktopCompatibleTask.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; + +using Microsoft.Build.Utilities; + +namespace Build.Tasks +{ + public abstract class DesktopCompatibleTask : Task + { + static DesktopCompatibleTask() + { + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + } + + private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + // apply any existing policy + AssemblyName referenceName = new AssemblyName(AppDomain.CurrentDomain.ApplyPolicy(args.Name)); + + string fileName = referenceName.Name + ".dll"; + string assemblyPath = null; + string probingPath = null; + Assembly assm = null; + + // look next to requesting assembly + assemblyPath = args.RequestingAssembly?.Location; + if (!String.IsNullOrEmpty(assemblyPath)) + { + probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName); + Debug.WriteLine($"Considering {probingPath} based on RequestingAssembly"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + } + + // look next to the executing assembly + assemblyPath = Assembly.GetExecutingAssembly().Location; + if (!String.IsNullOrEmpty(assemblyPath)) + { + probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName); + + Debug.WriteLine($"Considering {probingPath} based on ExecutingAssembly"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + } + + // look in AppDomain base directory + probingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + Debug.WriteLine($"Considering {probingPath} based on BaseDirectory"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + + // look in current directory + Debug.WriteLine($"Considering {fileName}"); + if (Probe(fileName, referenceName.Version, out assm)) + { + return assm; + } + + return null; + } + + /// + /// Considers a path to load for satisfying an assembly ref and loads it + /// if the file exists and version is sufficient. + /// + /// Path to consider for load + /// Minimum version to consider + /// loaded assembly + /// true if assembly was loaded + private static bool Probe(string filePath, Version minimumVersion, out Assembly assembly) + { + if (File.Exists(filePath)) + { + AssemblyName name = AssemblyName.GetAssemblyName(filePath); + + if (name.Version >= minimumVersion) + { + assembly = Assembly.Load(name); + return true; + } + } + + assembly = null; + return false; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Build.Tasks/DumpNativeResources.cs b/src/coreclr/tools/aot/ILCompiler.Build.Tasks/DumpNativeResources.cs new file mode 100644 index 00000000000000..f683710eb0723d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Build.Tasks/DumpNativeResources.cs @@ -0,0 +1,255 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using System.Text; + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Build.Tasks +{ + /// + /// Dumps native Win32 resources in the given assembly into a specified *.res file. + /// + public class DumpNativeResources : DesktopCompatibleTask + { + /// + /// File name of the assembly with Win32 resources to be dumped. + /// + [Required] + public string MainAssembly + { + get; + set; + } + + /// + /// File name into which to dump the Win32 resources. + /// + [Required] + public string ResourceFile + { + get; + set; + } + + public override bool Execute() + { + using (FileStream fs = File.OpenRead(MainAssembly)) + using (PEReader peFile = new PEReader(fs)) + { + DirectoryEntry resourceDirectory = peFile.PEHeaders.PEHeader.ResourceTableDirectory; + if (resourceDirectory.Size != 0 + && peFile.PEHeaders.TryGetDirectoryOffset(resourceDirectory, out int rsrcOffset)) + { + using (var bw = new BinaryWriter(File.OpenWrite(ResourceFile))) + { + ResWriter.WriteResources(peFile, rsrcOffset, resourceDirectory.Size, bw); + } + } + else + { + if (File.Exists(ResourceFile)) + { + try + { + File.Delete(ResourceFile); + } + catch { } + } + } + } + + return true; + } + } + + /// + /// Helper class that converts from a Win32 PE resource directory format to + /// a set of RESOURCEHEADER structures (https://docs.microsoft.com/en-us/windows/desktop/menurc/resourceheader) + /// that form the basis of Win32 RES files. + /// + class ResWriter + { + private readonly PEMemoryBlock _memoryBlock; + private readonly PEReader _peReader; + private readonly int _rsrcOffset; + private readonly int _rsrcSize; + private readonly BinaryWriter _bw; + + private object _typeIdOrName; + private object _resourceIdOrName; + private int _languageId; + + private ResWriter(PEMemoryBlock memoryBlock, PEReader peReader, int rsrcOffset, int rsrcSize, BinaryWriter bw) + { + _memoryBlock = memoryBlock; + _peReader = peReader; + _rsrcOffset = rsrcOffset; + _rsrcSize = rsrcSize; + _bw = bw; + } + + public static void WriteResources(PEReader reader, int rsrcOffset, int rsrcSize, BinaryWriter bw) + { + var rw = new ResWriter(reader.GetEntireImage(), reader, rsrcOffset, rsrcSize, bw); + + // First entry is a null resource entry + + bw.Write(new byte[] { + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }); + + rw.DumpDirectory(reader.GetEntireImage().GetReader(rsrcOffset, rsrcSize), 0); + } + + private void DumpDirectory(BlobReader br, int level) + { + // Skip characteristics + br.ReadInt32(); + + // Skip time/date stamp + br.ReadInt32(); + + // Skip time/date stamp + br.ReadInt32(); + + ushort numNamed = br.ReadUInt16(); + ushort numId = br.ReadUInt16(); + + for (int i = 0; i < numNamed + numId; i++) + { + int nameOffsetOrId = br.ReadInt32(); + uint entryTableOrSubdirectoryOffset = br.ReadUInt32(); + + if (i < numNamed) + { + nameOffsetOrId = nameOffsetOrId &= 0x7FFFFFFF; + BlobReader nameReader = _memoryBlock.GetReader(_rsrcOffset + nameOffsetOrId, _rsrcSize - nameOffsetOrId); + ushort nameLength = nameReader.ReadUInt16(); + StringBuilder sb = new StringBuilder(nameLength); + for (int charIndex = 0; charIndex < nameLength; charIndex++) + sb.Append((char)nameReader.ReadUInt16()); + string name = sb.ToString(); + + if (level == 0) + { + _typeIdOrName = name; + } + else if (level == 1) + { + _resourceIdOrName = name; + } + else + throw new BadImageFormatException(); + } + else + { + if (level == 0) + { + _typeIdOrName = nameOffsetOrId; + } + else if (level == 1) + { + _resourceIdOrName = nameOffsetOrId; + } + else if (level == 2) + { + _languageId = nameOffsetOrId; + } + else + throw new BadImageFormatException(); + } + + if (level < 2) + { + if ((entryTableOrSubdirectoryOffset & 0x80000000) == 0) + throw new BadImageFormatException(); + entryTableOrSubdirectoryOffset &= 0x7FFFFFFF; + DumpDirectory(_memoryBlock.GetReader(_rsrcOffset + (int)entryTableOrSubdirectoryOffset, _rsrcSize - (int)entryTableOrSubdirectoryOffset), level + 1); + } + else + { + DumpEntry(_memoryBlock.GetReader(_rsrcOffset + (int)entryTableOrSubdirectoryOffset, _rsrcSize - (int)entryTableOrSubdirectoryOffset)); + } + } + } + + private void DumpEntry(BlobReader br) + { + int dataRva = br.ReadInt32(); + int size = br.ReadInt32(); + + // Skip codepage + br.ReadInt32(); + + // Skip reserved + br.ReadInt32(); + + var ms = new MemoryStream(); + var hdr = new BinaryWriter(ms, System.Text.Encoding.Unicode); + + hdr.Write(size); // DataSize + hdr.Write(0); // HeaderSize + hdr.WriteNameOrId(_typeIdOrName); // TYPE + hdr.WriteNameOrId(_resourceIdOrName); // NAME + + // round up to "DWORD" offset + long curHeaderSize = hdr.BaseStream.Position; + while (curHeaderSize % 4 != 0) + { + hdr.Write((byte)0); + curHeaderSize++; + } + + hdr.Write(0); // DataVersion + hdr.Write((short)0); // MemoryFlags + hdr.Write((ushort)_languageId); // LanguageId + hdr.Write(0); // Version + hdr.Write(0); // Characteristics + + // Patch up HeaderSize + var headerSize = hdr.Seek(0, SeekOrigin.Current); + hdr.Seek(4, SeekOrigin.Begin); + hdr.Write((int)headerSize); + + var hdrData = ms.ToArray(); + + _bw.Write(hdrData); + _bw.Write(_peReader.GetSectionData(dataRva).GetReader().ReadBytes(size)); + + // Make sure we are DWORD aligned + var totalSize = hdrData.Length + size; + while (totalSize % 4 != 0) + { + _bw.Write((byte)0); + totalSize++; + } + + } + } + + static class ResourceHelper + { + public static void WriteNameOrId(this BinaryWriter bw, object nameOrId) + { + if (nameOrId is string s) + { + // String + bw.Write(s.ToCharArray()); + bw.Write((short)0); + } + else + { + // Integer + bw.Write((short)-1); + bw.Write((short)(int)nameOrId); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Build.Tasks/ILCompiler.Build.Tasks.csproj b/src/coreclr/tools/aot/ILCompiler.Build.Tasks/ILCompiler.Build.Tasks.csproj new file mode 100644 index 00000000000000..8cad68f2ff2146 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Build.Tasks/ILCompiler.Build.Tasks.csproj @@ -0,0 +1,37 @@ + + + Library + ILCompiler + ILCompiler.Build.Tasks + netstandard2.0 + false + $(RuntimeBinDir)/ilc/netstandard + Debug;Release;Checked + AnyCPU + + + true + + + + + 15.3.409 + + + 15.3.409 + + + $(SystemReflectionMetadataVersion) + + + + + + + PreserveNewest + + + PreserveNewest + + + diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedInteropStubManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedInteropStubManager.cs new file mode 100644 index 00000000000000..415dca084fbfa7 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedInteropStubManager.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.IL; +using Internal.TypeSystem; + +using ILCompiler.DependencyAnalysis; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; + +namespace ILCompiler +{ + /// + /// Represents an interop stub manager whose list of stubs has been determined ahead of time. + /// + public class AnalysisBasedInteropStubManager : CompilerGeneratedInteropStubManager + { + private readonly IEnumerable _typesWithStructMarshalling; + private readonly IEnumerable _typesWithDelegateMarshalling; + + public AnalysisBasedInteropStubManager(InteropStateManager interopStateManager, PInvokeILEmitterConfiguration pInvokeILEmitterConfiguration, IEnumerable typesWithStructMarshalling, IEnumerable typesWithDelegateMarshalling) + : base(interopStateManager, pInvokeILEmitterConfiguration) + { + _typesWithStructMarshalling = typesWithStructMarshalling; + _typesWithDelegateMarshalling = typesWithDelegateMarshalling; + } + + public override void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + foreach (DefType type in _typesWithStructMarshalling) + { + rootProvider.RootStructMarshallingData(type, "Analysis based interop root"); + } + + foreach (DefType type in _typesWithDelegateMarshalling) + { + rootProvider.RootDelegateMarshallingData(type, "Analysis based interop root"); + } + } + + public override void AddDependeciesDueToPInvoke(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + } + + public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + } + + public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs new file mode 100644 index 00000000000000..6ba6cc54c5122f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs @@ -0,0 +1,303 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +using ILCompiler.Metadata; +using ILCompiler.DependencyAnalysis; + +using Debug = System.Diagnostics.Debug; +using EcmaModule = Internal.TypeSystem.Ecma.EcmaModule; +using CustomAttributeHandle = System.Reflection.Metadata.CustomAttributeHandle; +using ExportedTypeHandle = System.Reflection.Metadata.ExportedTypeHandle; + +namespace ILCompiler +{ + /// + /// A metadata manager that knows the full set of metadata ahead of time. + /// + public sealed class AnalysisBasedMetadataManager : GeneratingMetadataManager, ICompilationRootProvider + { + private readonly List _modulesWithMetadata; + + private readonly Dictionary _reflectableTypes = new Dictionary(); + private readonly Dictionary _reflectableMethods = new Dictionary(); + private readonly Dictionary _reflectableFields = new Dictionary(); + private readonly HashSet _reflectableAttributes = new HashSet(); + + public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) + : this(typeSystemContext, new FullyBlockedMetadataBlockingPolicy(), + new FullyBlockedManifestResourceBlockingPolicy(), null, new NoStackTraceEmissionPolicy(), + new NoDynamicInvokeThunkGenerationPolicy(), Array.Empty(), + Array.Empty>(), Array.Empty>(), + Array.Empty>(), Array.Empty()) + { + } + + public AnalysisBasedMetadataManager( + CompilerTypeSystemContext typeSystemContext, + MetadataBlockingPolicy blockingPolicy, + ManifestResourceBlockingPolicy resourceBlockingPolicy, + string logFile, + StackTraceEmissionPolicy stackTracePolicy, + DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy, + IEnumerable modulesWithMetadata, + IEnumerable> reflectableTypes, + IEnumerable> reflectableMethods, + IEnumerable> reflectableFields, + IEnumerable reflectableAttributes) + : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy) + { + _modulesWithMetadata = new List(modulesWithMetadata); + + foreach (var refType in reflectableTypes) + { + _reflectableTypes.Add(refType.Entity, refType.Category); + } + + foreach (var refMethod in reflectableMethods) + { + // Asking for description or runtime mapping for a member without asking + // for the owning type would mean we can't actually satisfy the request. + Debug.Assert((refMethod.Category & MetadataCategory.Description) == 0 + || (_reflectableTypes[refMethod.Entity.OwningType] & MetadataCategory.Description) != 0); + Debug.Assert((refMethod.Category & MetadataCategory.RuntimeMapping) == 0 + || (_reflectableTypes[refMethod.Entity.OwningType] & MetadataCategory.RuntimeMapping) != 0); + _reflectableMethods.Add(refMethod.Entity, refMethod.Category); + } + + foreach (var refField in reflectableFields) + { + // Asking for description or runtime mapping for a member without asking + // for the owning type would mean we can't actually satisfy the request. + Debug.Assert((refField.Category & MetadataCategory.Description) == 0 + || (_reflectableTypes[refField.Entity.OwningType] & MetadataCategory.Description) != 0); + Debug.Assert((refField.Category & MetadataCategory.RuntimeMapping) == 0 + || (_reflectableTypes[refField.Entity.OwningType] & MetadataCategory.RuntimeMapping) != 0); + _reflectableFields.Add(refField.Entity, refField.Category); + } + + foreach (var refAttribute in reflectableAttributes) + { + _reflectableAttributes.Add(refAttribute); + } + +#if DEBUG + HashSet moduleHash = new HashSet(_modulesWithMetadata); + foreach (var refType in reflectableTypes) + { + // The instantiated types need to agree on the Description bit with the definition. + // GetMetadataCategory relies on that. + Debug.Assert((GetMetadataCategory(refType.Entity.GetTypeDefinition()) & MetadataCategory.Description) + == (GetMetadataCategory(refType.Entity) & MetadataCategory.Description)); + + Debug.Assert(!(refType.Entity is MetadataType) || moduleHash.Contains(((MetadataType)refType.Entity).Module)); + } + + foreach (var refMethod in reflectableMethods) + { + // The instantiated methods need to agree on the Description bit with the definition. + // GetMetadataCategory relies on that. + Debug.Assert((GetMetadataCategory(refMethod.Entity.GetTypicalMethodDefinition()) & MetadataCategory.Description) + == (GetMetadataCategory(refMethod.Entity) & MetadataCategory.Description)); + + // Canonical form of the method needs to agree with the logical form + Debug.Assert(GetMetadataCategory(refMethod.Entity) == GetMetadataCategory(refMethod.Entity.GetCanonMethodTarget(CanonicalFormKind.Specific))); + } + + foreach (var refField in reflectableFields) + { + // The instantiated fields need to agree on the Description bit with the definition. + // GetMetadataCategory relies on that. + Debug.Assert((GetMetadataCategory(refField.Entity.GetTypicalFieldDefinition()) & MetadataCategory.Description) + == (GetMetadataCategory(refField.Entity) & MetadataCategory.Description)); + } +#endif + } + + public override IEnumerable GetCompilationModulesWithMetadata() + { + return _modulesWithMetadata; + } + + protected override void ComputeMetadata(NodeFactory factory, + out byte[] metadataBlob, + out List> typeMappings, + out List> methodMappings, + out List> fieldMappings, + out List> stackTraceMapping) + { + ComputeMetadata(new Policy(_blockingPolicy, this), factory, + out metadataBlob, + out typeMappings, + out methodMappings, + out fieldMappings, + out stackTraceMapping); + } + + protected sealed override MetadataCategory GetMetadataCategory(MethodDesc method) + { + if (_reflectableMethods.TryGetValue(method, out MetadataCategory value)) + return value; + return 0; + } + + protected sealed override MetadataCategory GetMetadataCategory(TypeDesc type) + { + if (_reflectableTypes.TryGetValue(type, out MetadataCategory value)) + return value; + return 0; + } + + protected sealed override MetadataCategory GetMetadataCategory(FieldDesc field) + { + if (_reflectableFields.TryGetValue(field, out MetadataCategory value)) + return value; + return 0; + } + + protected override IEnumerable GetFieldsWithRuntimeMapping() + { + foreach (var pair in _reflectableFields) + { + if ((pair.Value & MetadataCategory.RuntimeMapping) != 0) + yield return pair.Key; + } + } + + void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider) + { + // We go over all the types and members that need a runtime artifact present in the + // compiled executable and root it. + + const string reason = "Reflection"; + + foreach (var pair in _reflectableTypes) + { + if ((pair.Value & MetadataCategory.RuntimeMapping) != 0) + rootProvider.AddCompilationRoot(pair.Key, reason); + } + + foreach (var pair in _reflectableMethods) + { + if ((pair.Value & MetadataCategory.RuntimeMapping) != 0) + { + MethodDesc method = pair.Key; + rootProvider.AddReflectionRoot(method, reason); + } + } + + foreach (var pair in _reflectableFields) + { + if ((pair.Value & MetadataCategory.RuntimeMapping) != 0) + { + FieldDesc field = pair.Key; + + // We only care about static fields at this point. Instance fields don't need + // runtime artifacts generated in the image. + if (field.IsStatic && !field.IsLiteral) + { + if (field.IsThreadStatic) + rootProvider.RootThreadStaticBaseForType(field.OwningType, reason); + else if (field.HasGCStaticBase) + rootProvider.RootGCStaticBaseForType(field.OwningType, reason); + else + rootProvider.RootNonGCStaticBaseForType(field.OwningType, reason); + } + } + } + } + + private struct Policy : IMetadataPolicy + { + private readonly MetadataBlockingPolicy _blockingPolicy; + private readonly AnalysisBasedMetadataManager _parent; + + public Policy(MetadataBlockingPolicy blockingPolicy, + AnalysisBasedMetadataManager parent) + { + _blockingPolicy = blockingPolicy; + _parent = parent; + } + + public bool GeneratesMetadata(FieldDesc fieldDef) + { + return (_parent.GetMetadataCategory(fieldDef) & MetadataCategory.Description) != 0; + } + + public bool GeneratesMetadata(MethodDesc methodDef) + { + return (_parent.GetMetadataCategory(methodDef) & MetadataCategory.Description) != 0; + } + + public bool GeneratesMetadata(MetadataType typeDef) + { + return (_parent.GetMetadataCategory(typeDef) & MetadataCategory.Description) != 0; + } + + public bool GeneratesMetadata(EcmaModule module, CustomAttributeHandle caHandle) + { + return _parent._reflectableAttributes.Contains(new ReflectableCustomAttribute(module, caHandle)); + } + + public bool GeneratesMetadata(EcmaModule module, ExportedTypeHandle exportedTypeHandle) + { + try + { + // We'll possibly need to do something else here if we ever use this MetadataManager + // with compilation modes that generate multiple metadata blobs. + // (Multi-module or .NET Native style shared library.) + // We are currently missing type forwarders pointing to the other blobs. + var targetType = (MetadataType)module.GetObject(exportedTypeHandle); + return GeneratesMetadata(targetType); + } + catch (TypeSystemException) + { + // No harm in generating a forwarder that didn't resolve. + // We'll get matching behavior at runtime. + return true; + } + } + + public bool IsBlocked(MetadataType typeDef) + { + return _blockingPolicy.IsBlocked(typeDef); + } + + public bool IsBlocked(MethodDesc methodDef) + { + return _blockingPolicy.IsBlocked(methodDef); + } + } + } + + public struct ReflectableEntity + { + public readonly TEntity Entity; + public readonly MetadataCategory Category; + + public ReflectableEntity(TEntity entity, MetadataCategory category) + { + Entity = entity; + Category = category; + } + } + + public struct ReflectableCustomAttribute : IEquatable + { + public readonly EcmaModule Module; + public readonly CustomAttributeHandle CustomAttributeHandle; + + public ReflectableCustomAttribute(EcmaModule module, CustomAttributeHandle caHandle) + => (Module, CustomAttributeHandle) = (module, caHandle); + + public bool Equals(ReflectableCustomAttribute other) + => other.Module == Module && other.CustomAttributeHandle == CustomAttributeHandle; + public override bool Equals(object obj) + => obj is ReflectableCustomAttribute other && Equals(other); + public override int GetHashCode() => Module.GetHashCode() ^ CustomAttributeHandle.GetHashCode(); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BlockedInternalsBlockingPolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BlockedInternalsBlockingPolicy.cs new file mode 100644 index 00000000000000..8d07a9363e2499 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BlockedInternalsBlockingPolicy.cs @@ -0,0 +1,253 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Debug = System.Diagnostics.Debug; +using TypeAttributes = System.Reflection.TypeAttributes; +using MethodAttributes = System.Reflection.MethodAttributes; +using FieldAttributes = System.Reflection.FieldAttributes; + +namespace ILCompiler +{ + /// + /// Represents a metadata policy that blocks implementations details. + /// + public sealed class BlockedInternalsBlockingPolicy : MetadataBlockingPolicy + { + private enum ModuleBlockingMode + { + None, + BlockedInternals, + FullyBlocked, + } + + private class ModuleBlockingState + { + public ModuleDesc Module { get; } + public ModuleBlockingMode BlockingMode { get; } + public ModuleBlockingState(ModuleDesc module, ModuleBlockingMode mode) + { + Module = module; + BlockingMode = mode; + } + } + + private class BlockedModulesHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(ModuleDesc key) => key.GetHashCode(); + protected override int GetValueHashCode(ModuleBlockingState value) => value.Module.GetHashCode(); + protected override bool CompareKeyToValue(ModuleDesc key, ModuleBlockingState value) => Object.ReferenceEquals(key, value.Module); + protected override bool CompareValueToValue(ModuleBlockingState value1, ModuleBlockingState value2) => Object.ReferenceEquals(value1.Module, value2.Module); + protected override ModuleBlockingState CreateValueFromKey(ModuleDesc module) + { + ModuleBlockingMode blockingMode = ModuleBlockingMode.None; + + if (module.GetType("System.Runtime.CompilerServices", "__BlockAllReflectionAttribute", throwIfNotFound: false) != null) + { + blockingMode = ModuleBlockingMode.FullyBlocked; + } + else if (module.GetType("System.Runtime.CompilerServices", "__BlockReflectionAttribute", throwIfNotFound: false) != null) + { + blockingMode = ModuleBlockingMode.BlockedInternals; + } + + return new ModuleBlockingState(module, blockingMode); + } + } + private BlockedModulesHashtable _blockedModules = new BlockedModulesHashtable(); + + private class BlockingState + { + public EcmaType Type { get; } + public bool IsBlocked { get; } + public BlockingState(EcmaType type, bool isBlocked) + { + Type = type; + IsBlocked = isBlocked; + } + } + + private class BlockedTypeHashtable : LockFreeReaderHashtable + { + private readonly BlockedModulesHashtable _blockedModules; + + public BlockedTypeHashtable(BlockedModulesHashtable blockedModules) + { + _blockedModules = blockedModules; + } + + protected override int GetKeyHashCode(EcmaType key) => key.GetHashCode(); + protected override int GetValueHashCode(BlockingState value) => value.Type.GetHashCode(); + protected override bool CompareKeyToValue(EcmaType key, BlockingState value) => Object.ReferenceEquals(key, value.Type); + protected override bool CompareValueToValue(BlockingState value1, BlockingState value2) => Object.ReferenceEquals(value1.Type, value2.Type); + protected override BlockingState CreateValueFromKey(EcmaType type) + { + ModuleBlockingMode moduleBlockingMode = _blockedModules.GetOrCreateValue(type.EcmaModule).BlockingMode; + bool isBlocked = ComputeIsBlocked(type, moduleBlockingMode); + return new BlockingState(type, isBlocked); + } + + private bool ComputeIsBlocked(EcmaType type, ModuleBlockingMode blockingMode) + { + // If the type is explicitly blocked, it's always blocked. + if (type.HasCustomAttribute("System.Runtime.CompilerServices", "ReflectionBlockedAttribute")) + return true; + + // If no blocking is applied to the module, the type is not blocked + if (blockingMode == ModuleBlockingMode.None) + return false; + + // type always gets metadata + if (type.IsModuleType) + return false; + + // The various SR types used in Resource Manager always get metadata + if ((type.Name == "Strings" || type.Name == "SR") && + type.Namespace.Contains(type.Module.Assembly.GetName().Name)) + return false; + + // Event sources are not blocked + if (type.HasCustomAttribute("System.Diagnostics.Tracing", "EventSourceAttribute")) + return false; + + // We block everything else if the module is blocked + if (blockingMode == ModuleBlockingMode.FullyBlocked) + return true; + + DefType containingType = type.ContainingType; + var typeDefinition = type.MetadataReader.GetTypeDefinition(type.Handle); + + if (containingType == null) + { + if ((typeDefinition.Attributes & TypeAttributes.Public) == 0) + { + return true; + } + } + else + { + if ((typeDefinition.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic) + { + return ComputeIsBlocked((EcmaType)containingType, blockingMode); + } + else + { + return true; + } + } + + return false; + } + } + private BlockedTypeHashtable _blockedTypes; + + private MetadataType ArrayOfTType { get; } + private MetadataType AttributeType { get; } + + public BlockedInternalsBlockingPolicy(TypeSystemContext context) + { + _blockedTypes = new BlockedTypeHashtable(_blockedModules); + + ArrayOfTType = context.SystemModule.GetType("System", "Array`1", throwIfNotFound: false); + AttributeType = context.SystemModule.GetType("System", "Attribute", throwIfNotFound: false); + } + + public override bool IsBlocked(MetadataType type) + { + Debug.Assert(type.IsTypeDefinition); + + var ecmaType = type as EcmaType; + if (ecmaType == null) + return true; + + return _blockedTypes.GetOrCreateValue(ecmaType).IsBlocked; + } + + public override bool IsBlocked(MethodDesc method) + { + Debug.Assert(method.IsTypicalMethodDefinition); + + var ecmaMethod = method as EcmaMethod; + if (ecmaMethod == null) + return true; + + ModuleBlockingMode moduleBlockingMode = _blockedModules.GetOrCreateValue(ecmaMethod.Module).BlockingMode; + if (moduleBlockingMode == ModuleBlockingMode.None) + return false; + else if (moduleBlockingMode == ModuleBlockingMode.FullyBlocked) + return true; + + // We are blocking internal implementation details + Debug.Assert(moduleBlockingMode == ModuleBlockingMode.BlockedInternals); + + var owningType = (EcmaType)ecmaMethod.OwningType; + if (_blockedTypes.GetOrCreateValue(owningType).IsBlocked) + return true; + + MethodAttributes accessibility = ecmaMethod.Attributes & MethodAttributes.Public; + if (accessibility != MethodAttributes.Family + && accessibility != MethodAttributes.FamORAssem + && accessibility != MethodAttributes.Public) + { + return true; + } + + // Methods on Array`1 are implementation details that implement the generic interfaces on + // arrays. They should not generate metadata or be reflection invokable. + // We could get rid of this special casing two ways: + // * Make these method stop being regular EcmaMethods with Array as their owning type, or + // * Make these methods implement the interfaces explicitly (they would become private and naturally blocked) + if (ecmaMethod.OwningType == ArrayOfTType) + return true; + + return false; + } + + public override bool IsBlocked(FieldDesc field) + { + Debug.Assert(field.IsTypicalFieldDefinition); + + var ecmaField = field as EcmaField; + if (ecmaField == null) + return true; + + ModuleBlockingMode moduleBlockingMode = _blockedModules.GetOrCreateValue(ecmaField.Module).BlockingMode; + if (moduleBlockingMode == ModuleBlockingMode.None) + return false; + else if (moduleBlockingMode == ModuleBlockingMode.FullyBlocked) + return true; + + // We are blocking internal implementation details + Debug.Assert(moduleBlockingMode == ModuleBlockingMode.BlockedInternals); + + var owningType = (EcmaType)ecmaField.OwningType; + if (_blockedTypes.GetOrCreateValue(owningType).IsBlocked) + return true; + + FieldAttributes accessibility = ecmaField.Attributes & FieldAttributes.FieldAccessMask; + if (accessibility != FieldAttributes.Family + && accessibility != FieldAttributes.FamORAssem + && accessibility != FieldAttributes.Public) + { + // Exempt fields on custom attributes from blocking. + // Attribute.Equals and Attribute.GetHashCode depends on being able to + // walk all fields on custom attributes using reflection. + // We're opening this hole in hopes that the fields won't have any "interesting" + // types (that would be themselves blocked). Doing that could be problematic. + if (AttributeType != null + && owningType.CanCastTo(AttributeType)) + { + return false; + } + + return true; + } + + return false; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs new file mode 100644 index 00000000000000..f3f780e87e060b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs @@ -0,0 +1,645 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Runtime.InteropServices; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +using Internal.IL; +using Internal.IL.Stubs; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using CORINFO_DEVIRTUALIZATION_DETAIL = Internal.JitInterface.CORINFO_DEVIRTUALIZATION_DETAIL; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + public abstract class Compilation : ICompilation + { + protected readonly DependencyAnalyzerBase _dependencyGraph; + protected readonly NodeFactory _nodeFactory; + protected readonly Logger _logger; + protected readonly DebugInformationProvider _debugInformationProvider; + protected readonly DevirtualizationManager _devirtualizationManager; + private readonly IInliningPolicy _inliningPolicy; + + public NameMangler NameMangler => _nodeFactory.NameMangler; + public NodeFactory NodeFactory => _nodeFactory; + public CompilerTypeSystemContext TypeSystemContext => NodeFactory.TypeSystemContext; + public Logger Logger => _logger; + public PInvokeILProvider PInvokeILProvider { get; } + + private readonly TypeGetTypeMethodThunkCache _typeGetTypeMethodThunks; + private readonly AssemblyGetExecutingAssemblyMethodThunkCache _assemblyGetExecutingAssemblyMethodThunks; + private readonly MethodBaseGetCurrentMethodThunkCache _methodBaseGetCurrentMethodThunks; + + protected Compilation( + DependencyAnalyzerBase dependencyGraph, + NodeFactory nodeFactory, + IEnumerable compilationRoots, + ILProvider ilProvider, + DebugInformationProvider debugInformationProvider, + DevirtualizationManager devirtualizationManager, + IInliningPolicy inliningPolicy, + Logger logger) + { + _dependencyGraph = dependencyGraph; + _nodeFactory = nodeFactory; + _logger = logger; + _debugInformationProvider = debugInformationProvider; + _devirtualizationManager = devirtualizationManager; + _inliningPolicy = inliningPolicy; + + _dependencyGraph.ComputeDependencyRoutine += ComputeDependencyNodeDependencies; + NodeFactory.AttachToDependencyGraph(_dependencyGraph); + + var rootingService = new RootingServiceProvider(nodeFactory, _dependencyGraph.AddRoot); + foreach (var rootProvider in compilationRoots) + rootProvider.AddCompilationRoots(rootingService); + + MetadataType globalModuleGeneratedType = nodeFactory.TypeSystemContext.GeneratedAssembly.GetGlobalModuleType(); + _typeGetTypeMethodThunks = new TypeGetTypeMethodThunkCache(globalModuleGeneratedType); + _assemblyGetExecutingAssemblyMethodThunks = new AssemblyGetExecutingAssemblyMethodThunkCache(globalModuleGeneratedType); + _methodBaseGetCurrentMethodThunks = new MethodBaseGetCurrentMethodThunkCache(); + + PInvokeILProvider = _nodeFactory.InteropStubManager.CreatePInvokeILProvider(); + if (PInvokeILProvider != null) + { + ilProvider = new CombinedILProvider(ilProvider, PInvokeILProvider); + } + + _methodILCache = new ILCache(ilProvider); + } + + private ILCache _methodILCache; + + public virtual MethodIL GetMethodIL(MethodDesc method) + { + // Flush the cache when it grows too big + if (_methodILCache.Count > 1000) + _methodILCache = new ILCache(_methodILCache.ILProvider); + + return _methodILCache.GetOrCreateValue(method).MethodIL; + } + + protected abstract void ComputeDependencyNodeDependencies(List> obj); + + protected abstract void CompileInternal(string outputFile, ObjectDumper dumper); + + public void DetectGenericCycles(MethodDesc caller, MethodDesc callee) + { + _nodeFactory.TypeSystemContext.DetectGenericCycles(caller, callee); + } + + public virtual IEETypeNode NecessaryTypeSymbolIfPossible(TypeDesc type) + { + return _nodeFactory.NecessaryTypeSymbol(type); + } + + public bool CanInline(MethodDesc caller, MethodDesc callee) + { + return _inliningPolicy.CanInline(caller, callee); + } + + public bool CanConstructType(TypeDesc type) + { + return _devirtualizationManager.CanConstructType(type); + } + + public DelegateCreationInfo GetDelegateCtor(TypeDesc delegateType, MethodDesc target, bool followVirtualDispatch) + { + // If we're creating a delegate to a virtual method that cannot be overriden, devirtualize. + // This is not just an optimization - it's required for correctness in the presence of sealed + // vtable slots. + if (followVirtualDispatch && (target.IsFinal || target.OwningType.IsSealed())) + followVirtualDispatch = false; + + if (followVirtualDispatch) + target = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(target); + + return DelegateCreationInfo.Create(delegateType, target, NodeFactory, followVirtualDispatch); + } + + /// + /// Gets an object representing the static data for RVA mapped fields from the PE image. + /// + public virtual ISymbolNode GetFieldRvaData(FieldDesc field) + { + if (field.GetType() == typeof(PInvokeLazyFixupField)) + { + return NodeFactory.PInvokeMethodFixup(new PInvokeMethodData((PInvokeLazyFixupField)field)); + } + else if (field is ExternSymbolMappedField externField) + { + return NodeFactory.ExternSymbol(externField.SymbolName); + } + else + { + // Use the typical field definition in case this is an instantiated generic type + field = field.GetTypicalFieldDefinition(); + return NodeFactory.ReadOnlyDataBlob(NameMangler.GetMangledFieldName(field), + ((EcmaField)field).GetFieldRvaData(), NodeFactory.Target.PointerSize); + } + } + + public bool HasLazyStaticConstructor(TypeDesc type) + { + return NodeFactory.PreinitializationManager.HasLazyStaticConstructor(type); + } + + public MethodDebugInformation GetDebugInfo(MethodIL methodIL) + { + return _debugInformationProvider.GetDebugInfo(methodIL); + } + + /// + /// Resolves a reference to an intrinsic method to a new method that takes it's place in the compilation. + /// This is used for intrinsics where the intrinsic expansion depends on the callsite. + /// + /// The intrinsic method called. + /// The callsite that calls the intrinsic. + /// The intrinsic implementation to be called for this specific callsite. + public MethodDesc ExpandIntrinsicForCallsite(MethodDesc intrinsicMethod, MethodDesc callsiteMethod) + { + Debug.Assert(intrinsicMethod.IsIntrinsic); + + var intrinsicOwningType = intrinsicMethod.OwningType as MetadataType; + if (intrinsicOwningType == null) + return intrinsicMethod; + + if (intrinsicOwningType.Module != TypeSystemContext.SystemModule) + return intrinsicMethod; + + if (intrinsicOwningType.Name == "Type" && intrinsicOwningType.Namespace == "System") + { + if (intrinsicMethod.Signature.IsStatic && intrinsicMethod.Name == "GetType") + { + ModuleDesc callsiteModule = (callsiteMethod.OwningType as MetadataType)?.Module; + if (callsiteModule != null) + { + Debug.Assert(callsiteModule is IAssemblyDesc, "Multi-module assemblies"); + return _typeGetTypeMethodThunks.GetHelper(intrinsicMethod, ((IAssemblyDesc)callsiteModule).GetName().FullName); + } + } + } + else if (intrinsicOwningType.Name == "Assembly" && intrinsicOwningType.Namespace == "System.Reflection") + { + if (intrinsicMethod.Signature.IsStatic && intrinsicMethod.Name == "GetExecutingAssembly") + { + ModuleDesc callsiteModule = (callsiteMethod.OwningType as MetadataType)?.Module; + if (callsiteModule != null) + { + Debug.Assert(callsiteModule is IAssemblyDesc, "Multi-module assemblies"); + return _assemblyGetExecutingAssemblyMethodThunks.GetHelper((IAssemblyDesc)callsiteModule); + } + } + } + else if (intrinsicOwningType.Name == "MethodBase" && intrinsicOwningType.Namespace == "System.Reflection") + { + if (intrinsicMethod.Signature.IsStatic && intrinsicMethod.Name == "GetCurrentMethod") + { + return _methodBaseGetCurrentMethodThunks.GetHelper(callsiteMethod).InstantiateAsOpen(); + } + } + + return intrinsicMethod; + } + + public bool HasFixedSlotVTable(TypeDesc type) + { + return NodeFactory.VTable(type).HasFixedSlots; + } + + public bool IsEffectivelySealed(TypeDesc type) + { + return _devirtualizationManager.IsEffectivelySealed(type); + } + + public bool IsEffectivelySealed(MethodDesc method) + { + return _devirtualizationManager.IsEffectivelySealed(method); + } + + public MethodDesc ResolveVirtualMethod(MethodDesc declMethod, TypeDesc implType, out CORINFO_DEVIRTUALIZATION_DETAIL devirtualizationDetail) + { + return _devirtualizationManager.ResolveVirtualMethod(declMethod, implType, out devirtualizationDetail); + } + + public bool NeedsRuntimeLookup(ReadyToRunHelperId lookupKind, object targetOfLookup) + { + switch (lookupKind) + { + case ReadyToRunHelperId.TypeHandle: + case ReadyToRunHelperId.NecessaryTypeHandle: + case ReadyToRunHelperId.DefaultConstructor: + case ReadyToRunHelperId.TypeHandleForCasting: + case ReadyToRunHelperId.ObjectAllocator: + return ((TypeDesc)targetOfLookup).IsRuntimeDeterminedSubtype; + + case ReadyToRunHelperId.MethodDictionary: + case ReadyToRunHelperId.MethodEntry: + case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.MethodHandle: + return ((MethodDesc)targetOfLookup).IsRuntimeDeterminedExactMethod; + + case ReadyToRunHelperId.FieldHandle: + return ((FieldDesc)targetOfLookup).OwningType.IsRuntimeDeterminedSubtype; + + default: + throw new NotImplementedException(); + } + } + + public ReadyToRunHelperId GetLdTokenHelperForType(TypeDesc type) + { + bool canConstructPerWholeProgramAnalysis = _devirtualizationManager == null ? true : _devirtualizationManager.CanConstructType(type); + return canConstructPerWholeProgramAnalysis & DependencyAnalysis.ConstructedEETypeNode.CreationAllowed(type) + ? ReadyToRunHelperId.TypeHandle + : ReadyToRunHelperId.NecessaryTypeHandle; + } + + public static MethodDesc GetConstructorForCreateInstanceIntrinsic(TypeDesc type) + { + MethodDesc ctor = type.GetDefaultConstructor(); + if (ctor == null) + { + MetadataType activatorType = type.Context.SystemModule.GetKnownType("System", "Activator"); + if (type.IsValueType && type.GetParameterlessConstructor() == null) + { + ctor = activatorType.GetKnownNestedType("StructWithNoConstructor").GetKnownMethod(".ctor", null); + } + else + { + ctor = activatorType.GetKnownMethod("MissingConstructorMethod", null); + } + } + + return ctor; + } + + public ISymbolNode ComputeConstantLookup(ReadyToRunHelperId lookupKind, object targetOfLookup) + { + switch (lookupKind) + { + case ReadyToRunHelperId.TypeHandle: + return NodeFactory.ConstructedTypeSymbol((TypeDesc)targetOfLookup); + case ReadyToRunHelperId.NecessaryTypeHandle: + return NecessaryTypeSymbolIfPossible((TypeDesc)targetOfLookup); + case ReadyToRunHelperId.TypeHandleForCasting: + { + var type = (TypeDesc)targetOfLookup; + if (type.IsNullable) + targetOfLookup = type.Instantiation[0]; + return NecessaryTypeSymbolIfPossible((TypeDesc)targetOfLookup); + } + case ReadyToRunHelperId.MethodDictionary: + return NodeFactory.MethodGenericDictionary((MethodDesc)targetOfLookup); + case ReadyToRunHelperId.MethodEntry: + return NodeFactory.FatFunctionPointer((MethodDesc)targetOfLookup); + case ReadyToRunHelperId.MethodHandle: + return NodeFactory.RuntimeMethodHandle((MethodDesc)targetOfLookup); + case ReadyToRunHelperId.FieldHandle: + return NodeFactory.RuntimeFieldHandle((FieldDesc)targetOfLookup); + case ReadyToRunHelperId.DefaultConstructor: + { + var type = (TypeDesc)targetOfLookup; + MethodDesc ctor = GetConstructorForCreateInstanceIntrinsic(type); + return NodeFactory.CanonicalEntrypoint(ctor); + } + case ReadyToRunHelperId.ObjectAllocator: + { + var type = (TypeDesc)targetOfLookup; + return NodeFactory.ExternSymbol(JitHelper.GetNewObjectHelperForType(type)); + } + + default: + throw new NotImplementedException(); + } + } + + public GenericDictionaryLookup ComputeGenericLookup(MethodDesc contextMethod, ReadyToRunHelperId lookupKind, object targetOfLookup) + { + if (targetOfLookup is TypeSystemEntity typeSystemEntity) + { + _nodeFactory.TypeSystemContext.DetectGenericCycles(contextMethod, typeSystemEntity); + } + + GenericContextSource contextSource; + + if (contextMethod.RequiresInstMethodDescArg()) + { + contextSource = GenericContextSource.MethodParameter; + } + else if (contextMethod.RequiresInstMethodTableArg()) + { + contextSource = GenericContextSource.TypeParameter; + } + else + { + Debug.Assert(contextMethod.AcquiresInstMethodTableFromThis()); + contextSource = GenericContextSource.ThisObject; + } + + // + // Some helpers represent logical concepts that might not be something that can be looked up in a dictionary + // + + // Downgrade type handle for casting to a normal type handle if possible + if (lookupKind == ReadyToRunHelperId.TypeHandleForCasting) + { + var type = (TypeDesc)targetOfLookup; + if (!type.IsRuntimeDeterminedType || + (!((RuntimeDeterminedType)type).CanonicalType.IsCanonicalDefinitionType(CanonicalFormKind.Universal) && + !((RuntimeDeterminedType)type).CanonicalType.IsNullable)) + { + if (type.IsNullable) + { + targetOfLookup = type.Instantiation[0]; + } + lookupKind = ReadyToRunHelperId.NecessaryTypeHandle; + } + } + + // We don't have separate entries for necessary type handles to avoid possible duplication + if (lookupKind == ReadyToRunHelperId.NecessaryTypeHandle) + { + lookupKind = ReadyToRunHelperId.TypeHandle; + } + + // Can we do a fixed lookup? Start by checking if we can get to the dictionary. + // Context source having a vtable with fixed slots is a prerequisite. + if (contextSource == GenericContextSource.MethodParameter + || HasFixedSlotVTable(contextMethod.OwningType)) + { + DictionaryLayoutNode dictionaryLayout; + if (contextSource == GenericContextSource.MethodParameter) + dictionaryLayout = _nodeFactory.GenericDictionaryLayout(contextMethod); + else + dictionaryLayout = _nodeFactory.GenericDictionaryLayout(contextMethod.OwningType); + + // If the dictionary layout has fixed slots, we can compute the lookup now. Otherwise defer to helper. + if (dictionaryLayout.HasFixedSlots) + { + int pointerSize = _nodeFactory.Target.PointerSize; + + GenericLookupResult lookup = ReadyToRunGenericHelperNode.GetLookupSignature(_nodeFactory, lookupKind, targetOfLookup); + int dictionarySlot = dictionaryLayout.GetSlotForFixedEntry(lookup); + if (dictionarySlot != -1) + { + int dictionaryOffset = dictionarySlot * pointerSize; + + bool indirectLastOffset = lookup.LookupResultReferenceType(_nodeFactory) == GenericLookupResultReferenceType.Indirect; + + if (contextSource == GenericContextSource.MethodParameter) + { + return GenericDictionaryLookup.CreateFixedLookup(contextSource, dictionaryOffset, indirectLastOffset: indirectLastOffset); + } + else + { + int vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(_nodeFactory, contextMethod.OwningType); + int vtableOffset = EETypeNode.GetVTableOffset(pointerSize) + vtableSlot * pointerSize; + return GenericDictionaryLookup.CreateFixedLookup(contextSource, vtableOffset, dictionaryOffset, indirectLastOffset: indirectLastOffset); + } + } + } + } + + // Fixed lookup not possible - use helper. + return GenericDictionaryLookup.CreateHelperLookup(contextSource, lookupKind, targetOfLookup); + } + + public bool IsFatPointerCandidate(MethodDesc containingMethod, MethodSignature signature) + { + // Unmanaged calls are never fat pointers + if ((signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) != 0) + return false; + + if (containingMethod.OwningType is MetadataType owningType) + { + // RawCalliHelper is a way for the class library to opt out of fat calls + if (owningType.Name == "RawCalliHelper") + return false; + + // Delegate invocation never needs fat calls + if (owningType.IsDelegate && containingMethod.Name == "Invoke") + return false; + } + + return true; + } + + /// + /// Retreives method whose runtime handle is suitable for use with GVMLookupForSlot. + /// + public MethodDesc GetTargetOfGenericVirtualMethodCall(MethodDesc calledMethod) + { + // Should be a generic virtual method + Debug.Assert(calledMethod.HasInstantiation && calledMethod.IsVirtual); + + // Needs to be either a concrete method, or a runtime determined form. + Debug.Assert(!calledMethod.IsCanonicalMethod(CanonicalFormKind.Specific)); + + MethodDesc targetMethod = calledMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + MethodDesc targetMethodDefinition = targetMethod.GetMethodDefinition(); + + MethodDesc slotNormalizedMethodDefinition = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethodDefinition); + + // If the method defines the slot, we can use that. + if (slotNormalizedMethodDefinition == targetMethodDefinition) + { + return calledMethod; + } + + // Normalize to the slot defining method + MethodDesc slotNormalizedMethod = TypeSystemContext.GetInstantiatedMethod( + slotNormalizedMethodDefinition, + targetMethod.Instantiation); + + // Since the slot normalization logic modified what method we're looking at, we need to compute the new target of lookup. + // + // If we could use virtual method resolution logic with runtime determined methods, we wouldn't need what we're going + // to do below. + MethodDesc runtimeDeterminedSlotNormalizedMethod; + if (!slotNormalizedMethod.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + // If the owning type is not generic, we can use it as-is, potentially only replacing the runtime-determined + // method instantiation part. + runtimeDeterminedSlotNormalizedMethod = slotNormalizedMethod.GetMethodDefinition(); + } + else + { + // If we need a runtime lookup but a normalization to the slot defining method happened above, we need to compute + // the runtime lookup in terms of the base type that introduced the slot. + // + // To do that, we walk the base hierarchy of the runtime determined thing, looking for a type definition that matches + // the slot-normalized virtual method. We then find the method on that type. + TypeDesc runtimeDeterminedOwningType = calledMethod.OwningType; + + Debug.Assert(!runtimeDeterminedOwningType.IsInterface); + + while (!slotNormalizedMethod.OwningType.HasSameTypeDefinition(runtimeDeterminedOwningType)) + { + TypeDesc runtimeDeterminedBaseTypeDefinition = runtimeDeterminedOwningType.GetTypeDefinition().BaseType; + if (runtimeDeterminedBaseTypeDefinition.HasInstantiation) + { + runtimeDeterminedOwningType = runtimeDeterminedBaseTypeDefinition.InstantiateSignature(runtimeDeterminedOwningType.Instantiation, default); + } + else + { + runtimeDeterminedOwningType = runtimeDeterminedBaseTypeDefinition; + } + } + + // Now get the method on the newly found type + Debug.Assert(runtimeDeterminedOwningType.HasInstantiation); + runtimeDeterminedSlotNormalizedMethod = TypeSystemContext.GetMethodForInstantiatedType( + slotNormalizedMethod.GetTypicalMethodDefinition(), + (InstantiatedType)runtimeDeterminedOwningType); + } + + return TypeSystemContext.GetInstantiatedMethod(runtimeDeterminedSlotNormalizedMethod, calledMethod.Instantiation); + } + + CompilationResults ICompilation.Compile(string outputFile, ObjectDumper dumper) + { + if (dumper != null) + { + dumper.Begin(); + } + + CompileInternal(outputFile, dumper); + + if (dumper != null) + { + dumper.End(); + } + + return new CompilationResults(_dependencyGraph, _nodeFactory); + } + + private sealed class ILCache : LockFreeReaderHashtable + { + public ILProvider ILProvider { get; } + + public ILCache(ILProvider provider) + { + ILProvider = provider; + } + + protected override int GetKeyHashCode(MethodDesc key) + { + return key.GetHashCode(); + } + protected override int GetValueHashCode(MethodILData value) + { + return value.Method.GetHashCode(); + } + protected override bool CompareKeyToValue(MethodDesc key, MethodILData value) + { + return Object.ReferenceEquals(key, value.Method); + } + protected override bool CompareValueToValue(MethodILData value1, MethodILData value2) + { + return Object.ReferenceEquals(value1.Method, value2.Method); + } + protected override MethodILData CreateValueFromKey(MethodDesc key) + { + return new MethodILData() { Method = key, MethodIL = ILProvider.GetMethodIL(key) }; + } + + internal class MethodILData + { + public MethodDesc Method; + public MethodIL MethodIL; + } + } + + private sealed class CombinedILProvider : ILProvider + { + private readonly ILProvider _primaryILProvider; + private readonly PInvokeILProvider _pinvokeProvider; + + public CombinedILProvider(ILProvider primaryILProvider, PInvokeILProvider pinvokeILProvider) + { + _primaryILProvider = primaryILProvider; + _pinvokeProvider = pinvokeILProvider; + } + + public override MethodIL GetMethodIL(MethodDesc method) + { + MethodIL result = _primaryILProvider.GetMethodIL(method); + if (result == null && method.IsPInvoke) + result = _pinvokeProvider.GetMethodIL(method); + + return result; + } + } + } + + // Interface under which Compilation is exposed externally. + public interface ICompilation + { + CompilationResults Compile(string outputFileName, ObjectDumper dumper); + } + + public class CompilationResults + { + private readonly DependencyAnalyzerBase _graph; + protected readonly NodeFactory _factory; + + protected ImmutableArray> MarkedNodes + { + get + { + return _graph.MarkedNodeList; + } + } + + internal CompilationResults(DependencyAnalyzerBase graph, NodeFactory factory) + { + _graph = graph; + _factory = factory; + } + + public void WriteDependencyLog(string fileName) + { + using (FileStream dgmlOutput = new FileStream(fileName, FileMode.Create)) + { + DgmlWriter.WriteDependencyGraphToStream(dgmlOutput, _graph, _factory); + dgmlOutput.Flush(); + } + } + + public IEnumerable CompiledMethodBodies + { + get + { + foreach (var node in MarkedNodes) + { + if (node is IMethodBodyNode) + yield return ((IMethodBodyNode)node).Method; + } + } + } + + public IEnumerable ConstructedEETypes + { + get + { + foreach (var node in MarkedNodes) + { + if (node is ConstructedEETypeNode || node is CanonicalEETypeNode) + { + yield return ((IEETypeNode)node).Type; + } + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs new file mode 100644 index 00000000000000..25cd0bc9b85a6b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.JitInterface; + +namespace ILCompiler +{ + partial class CompilationBuilder + { + private PreinitializationManager _preinitializationManager; + + // These need to provide reasonable defaults so that the user can optionally skip + // calling the Use/Configure methods and still get something reasonable back. + protected MetadataManager _metadataManager; + protected InteropStubManager _interopStubManager = new EmptyInteropStubManager(); + protected VTableSliceProvider _vtableSliceProvider = new LazyVTableSliceProvider(); + protected DictionaryLayoutProvider _dictionaryLayoutProvider = new LazyDictionaryLayoutProvider(); + protected DebugInformationProvider _debugInformationProvider = new DebugInformationProvider(); + protected DevirtualizationManager _devirtualizationManager = new DevirtualizationManager(); + protected MethodImportationErrorProvider _methodImportationErrorProvider = new MethodImportationErrorProvider(); + protected IInliningPolicy _inliningPolicy; + protected bool _methodBodyFolding; + protected bool _singleThreaded; + protected InstructionSetSupport _instructionSetSupport; + protected SecurityMitigationOptions _mitigationOptions; + + partial void InitializePartial() + { + _metadataManager = new AnalysisBasedMetadataManager(_context); + _instructionSetSupport = new InstructionSetSupport(default, default, _context.Target.Architecture); + } + + public CompilationBuilder UseInstructionSetSupport(InstructionSetSupport support) + { + _instructionSetSupport = support; + return this; + } + + public CompilationBuilder UseInliningPolicy(IInliningPolicy policy) + { + _inliningPolicy = policy; + return this; + } + + public CompilationBuilder UseMetadataManager(MetadataManager metadataManager) + { + _metadataManager = metadataManager; + return this; + } + + public CompilationBuilder UseInteropStubManager(InteropStubManager interopStubManager) + { + _interopStubManager = interopStubManager; + return this; + } + + public CompilationBuilder UseVTableSliceProvider(VTableSliceProvider provider) + { + _vtableSliceProvider = provider; + return this; + } + + public CompilationBuilder UseGenericDictionaryLayoutProvider(DictionaryLayoutProvider provider) + { + _dictionaryLayoutProvider = provider; + return this; + } + + public CompilationBuilder UseDevirtualizationManager(DevirtualizationManager manager) + { + _devirtualizationManager = manager; + return this; + } + + public CompilationBuilder UseDebugInfoProvider(DebugInformationProvider provider) + { + _debugInformationProvider = provider; + return this; + } + + public CompilationBuilder UseSecurityMitigationOptions(SecurityMitigationOptions options) + { + _mitigationOptions = options; + return this; + } + + public CompilationBuilder UseMethodBodyFolding(bool enable) + { + _methodBodyFolding = enable; + return this; + } + + public CompilationBuilder UseSingleThread(bool enable) + { + _singleThreaded = enable; + return this; + } + + public CompilationBuilder UsePreinitializationManager(PreinitializationManager manager) + { + _preinitializationManager = manager; + return this; + } + + public CompilationBuilder UseMethodImportationErrorProvider(MethodImportationErrorProvider errorProvider) + { + _methodImportationErrorProvider = errorProvider; + return this; + } + + protected PreinitializationManager GetPreinitializationManager() + { + if (_preinitializationManager == null) + return new PreinitializationManager(_context, _compilationGroup, GetILProvider(), enableInterpreter: false); + return _preinitializationManager; + } + + public ILScannerBuilder GetILScannerBuilder(CompilationModuleGroup compilationGroup = null) + { + return new ILScannerBuilder(_context, compilationGroup ?? _compilationGroup, _nameMangler, GetILProvider(), GetPreinitializationManager()); + } + } + + [Flags] + public enum SecurityMitigationOptions + { + ControlFlowGuardAnnotations = 0x0001, + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationModuleGroup.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationModuleGroup.Aot.cs new file mode 100644 index 00000000000000..4a062192ecaca0 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationModuleGroup.Aot.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler +{ + partial class CompilationModuleGroup : IInliningPolicy + { + /// + /// If true, type dictionary of "type" is in the module to be compiled + /// + public abstract bool ContainsTypeDictionary(TypeDesc type); + + /// + /// If true, the generic dictionary of "method" is in the set of input assemblies being compiled + /// + public abstract bool ContainsMethodDictionary(MethodDesc method); + /// + /// If true, "method" is imported from the set of reference assemblies + /// + public abstract bool ImportsMethod(MethodDesc method, bool unboxingStub); + /// + /// If true, all code is compiled into a single module + /// + public abstract bool IsSingleFileCompilation { get; } + /// + /// If true, the full type should be generated. This occurs in situations where the type is + /// shared between modules (generics, parameterized types), or the type lives in a different module + /// and therefore needs a full VTable + /// + public abstract bool ShouldProduceFullVTable(TypeDesc type); + /// + /// If true, the necessary type should be promoted to a full type should be generated. + /// + public abstract bool ShouldPromoteToFullType(TypeDesc type); + /// + /// If true, if a type is in the dependency graph, its non-generic methods that can be transformed + /// into code must be. + /// + public abstract bool PresenceOfEETypeImpliesAllMethodsOnType(TypeDesc type); + /// + /// If true, the type will not be linked into the same module as the current compilation and therefore + /// accessed through the target platform's import mechanism (ie, Import Address Table on Windows) + /// + public abstract bool ShouldReferenceThroughImportTable(TypeDesc type); + + /// + /// If true, there may be type system constructs that will not be linked into the same module as the current compilation and therefore + /// accessed through the target platform's import mechanism (ie, Import Address Table on Windows) + /// + public abstract bool CanHaveReferenceThroughImportTable { get; } + + /// + /// If true, instance methods will only be generated once their owning type is created. + /// + public abstract bool AllowInstanceMethodOptimization(MethodDesc method); + + /// + /// If true, virtual methods on abstract types will only be generated once a non-abstract derived + /// type that doesn't override the virtual method is created. + /// + public abstract bool AllowVirtualMethodOnAbstractTypeOptimization(MethodDesc method); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerGeneratedInteropStubManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerGeneratedInteropStubManager.cs new file mode 100644 index 00000000000000..5c83e2211c9c5c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerGeneratedInteropStubManager.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.IL; +using Internal.TypeSystem; + +using ILCompiler.DependencyAnalysis; + +using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; + +namespace ILCompiler +{ + /// + /// This class is responsible for managing stub methods for interop + /// + public abstract class CompilerGeneratedInteropStubManager : InteropStubManager + { + private readonly PInvokeILEmitterConfiguration _pInvokeILEmitterConfiguration; + internal readonly InteropStateManager _interopStateManager; + + public CompilerGeneratedInteropStubManager(InteropStateManager interopStateManager, PInvokeILEmitterConfiguration pInvokeILEmitterConfiguration) + { + _interopStateManager = interopStateManager; + _pInvokeILEmitterConfiguration = pInvokeILEmitterConfiguration; + } + + public sealed override PInvokeILProvider CreatePInvokeILProvider() + { + return new PInvokeILProvider(_pInvokeILEmitterConfiguration, _interopStateManager); + } + + public sealed override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) + { + var delegateMapNode = new DelegateMarshallingStubMapNode(commonFixupsTableNode, _interopStateManager); + header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.DelegateMarshallingStubMap), delegateMapNode, delegateMapNode, delegateMapNode.EndSymbol); + + var structMapNode = new StructMarshallingStubMapNode(commonFixupsTableNode, _interopStateManager); + header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.StructMarshallingStubMap), structMapNode, structMapNode, structMapNode.EndSymbol); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerMetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerMetadataFieldLayoutAlgorithm.cs new file mode 100644 index 00000000000000..99df8e77fc5a1a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerMetadataFieldLayoutAlgorithm.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + internal class CompilerMetadataFieldLayoutAlgorithm : MetadataFieldLayoutAlgorithm + { + // GC statics start with a pointer to the "MethodTable" that signals the size and GCDesc to the GC + public static LayoutInt GetGCStaticFieldOffset(TypeSystemContext context) => context.Target.LayoutPointerSize; + + protected override void PrepareRuntimeSpecificStaticFieldLayout(TypeSystemContext context, ref ComputedStaticFieldLayout layout) + { + LayoutInt offset = GetGCStaticFieldOffset(context); + layout.GcStatics.Size = offset; + layout.ThreadGcStatics.Size = offset; + } + + protected override void FinalizeRuntimeSpecificStaticFieldLayout(TypeSystemContext context, ref ComputedStaticFieldLayout layout) + { + LayoutInt offset = GetGCStaticFieldOffset(context); + + // If the size of GCStatics is equal to the size set in PrepareRuntimeSpecificStaticFieldLayout, we + // don't have any GC statics + if (layout.GcStatics.Size == offset) + { + layout.GcStatics.Size = LayoutInt.Zero; + } + if (layout.ThreadGcStatics.Size == offset) + { + layout.ThreadGcStatics.Size = LayoutInt.Zero; + } + + // CoreRT makes no distinction between Gc / non-Gc thread statics. All are placed into ThreadGcStatics since thread statics + // are typically rare. + Debug.Assert(layout.ThreadNonGcStatics.Size == LayoutInt.Zero); + } + + protected override ComputedInstanceFieldLayout ComputeInstanceFieldLayout(MetadataType type, int numInstanceFields) + { + if (type.IsExplicitLayout) + { + return ComputeExplicitFieldLayout(type, numInstanceFields); + } + // Sequential layout has to be respected for blittable types only. We use approximation and respect it for + // all types without GC references (ie C# unmanaged types). + else if (type.IsSequentialLayout && !type.ContainsGCPointers) + { + return ComputeSequentialFieldLayout(type, numInstanceFields); + } + else + { + return ComputeAutoFieldLayout(type, numInstanceFields); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs new file mode 100644 index 00000000000000..24c076777fe2a6 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs @@ -0,0 +1,218 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +using Internal.TypeSystem; +using Internal.IL; + +using Interlocked = System.Threading.Interlocked; + +namespace ILCompiler +{ + partial class CompilerTypeSystemContext + { + public SharedGenericsConfiguration GenericsConfig + { + get; + } + + private readonly DelegateFeature _delegateFeatures; + private readonly MetadataFieldLayoutAlgorithm _metadataFieldLayoutAlgorithm = new CompilerMetadataFieldLayoutAlgorithm(); + private readonly RuntimeDeterminedFieldLayoutAlgorithm _runtimeDeterminedFieldLayoutAlgorithm = new RuntimeDeterminedFieldLayoutAlgorithm(); + private readonly VectorOfTFieldLayoutAlgorithm _vectorOfTFieldLayoutAlgorithm; + private readonly VectorFieldLayoutAlgorithm _vectorFieldLayoutAlgorithm; + + private TypeDesc[] _arrayOfTInterfaces; + private ArrayOfTRuntimeInterfacesAlgorithm _arrayOfTRuntimeInterfacesAlgorithm; + + public CompilerTypeSystemContext(TargetDetails details, SharedGenericsMode genericsMode, DelegateFeature delegateFeatures) + : base(details) + { + _genericsMode = genericsMode; + + _vectorOfTFieldLayoutAlgorithm = new VectorOfTFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm); + _vectorFieldLayoutAlgorithm = new VectorFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm); + + _delegateFeatures = delegateFeatures; + + GenericsConfig = new SharedGenericsConfiguration(); + } + + protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForNonPointerArrayType(ArrayType type) + { + if (_arrayOfTRuntimeInterfacesAlgorithm == null) + { + _arrayOfTRuntimeInterfacesAlgorithm = new ArrayOfTRuntimeInterfacesAlgorithm(SystemModule.GetKnownType("System", "Array`1")); + } + return _arrayOfTRuntimeInterfacesAlgorithm; + } + + public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type) + { + if (type == UniversalCanonType) + return UniversalCanonLayoutAlgorithm.Instance; + else if (type.IsRuntimeDeterminedType) + return _runtimeDeterminedFieldLayoutAlgorithm; + else if (VectorOfTFieldLayoutAlgorithm.IsVectorOfTType(type)) + return _vectorOfTFieldLayoutAlgorithm; + else if (VectorFieldLayoutAlgorithm.IsVectorType(type)) + return _vectorFieldLayoutAlgorithm; + else + return _metadataFieldLayoutAlgorithm; + } + + protected override bool ComputeHasGCStaticBase(FieldDesc field) + { + Debug.Assert(field.IsStatic); + + if (field.IsThreadStatic) + return true; + + TypeDesc fieldType = field.FieldType; + if (fieldType.IsValueType) + return ((DefType)fieldType).ContainsGCPointers; + else + return fieldType.IsGCPointer; + } + + /// + /// Returns true if is a generic interface type implemented by arrays. + /// + public bool IsGenericArrayInterfaceType(TypeDesc type) + { + // Hardcode the fact that all generic interfaces on array types have arity 1 + if (!type.IsInterface || type.Instantiation.Length != 1) + return false; + + if (_arrayOfTInterfaces == null) + { + DefType[] implementedInterfaces = SystemModule.GetKnownType("System", "Array`1").ExplicitlyImplementedInterfaces; + TypeDesc[] interfaceDefinitions = new TypeDesc[implementedInterfaces.Length]; + for (int i = 0; i < interfaceDefinitions.Length; i++) + interfaceDefinitions[i] = implementedInterfaces[i].GetTypeDefinition(); + Interlocked.CompareExchange(ref _arrayOfTInterfaces, interfaceDefinitions, null); + } + + TypeDesc interfaceDefinition = type.GetTypeDefinition(); + foreach (var arrayInterfaceDefinition in _arrayOfTInterfaces) + { + if (interfaceDefinition == arrayInterfaceDefinition) + return true; + } + + return false; + } + + protected override IEnumerable GetAllMethods(TypeDesc type) + { + return GetAllMethods(type, virtualOnly: false); + } + + protected override IEnumerable GetAllVirtualMethods(TypeDesc type) + { + return GetAllMethods(type, virtualOnly: true); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private IEnumerable GetAllMethods(TypeDesc type, bool virtualOnly) + { + if (type.IsDelegate) + { + return GetAllMethodsForDelegate(type, virtualOnly); + } + else if (type.IsEnum) + { + return GetAllMethodsForEnum(type, virtualOnly); + } + else if (type.IsValueType) + { + return GetAllMethodsForValueType(type, virtualOnly); + } + + return virtualOnly ? type.GetVirtualMethods() : type.GetMethods(); + } + + protected virtual IEnumerable GetAllMethodsForDelegate(TypeDesc type, bool virtualOnly) + { + // Inject the synthetic methods that support the implementation of the delegate. + InstantiatedType instantiatedType = type as InstantiatedType; + if (instantiatedType != null) + { + DelegateInfo info = GetDelegateInfo(type.GetTypeDefinition()); + foreach (MethodDesc syntheticMethod in info.Methods) + { + if (!virtualOnly || syntheticMethod.IsVirtual) + yield return GetMethodForInstantiatedType(syntheticMethod, instantiatedType); + } + } + else + { + DelegateInfo info = GetDelegateInfo(type); + foreach (MethodDesc syntheticMethod in info.Methods) + { + if (!virtualOnly || syntheticMethod.IsVirtual) + yield return syntheticMethod; + } + } + + // Append all the methods defined in metadata + IEnumerable metadataMethods = virtualOnly ? type.GetVirtualMethods() : type.GetMethods(); + foreach (var m in metadataMethods) + yield return m; + } + + protected override DelegateInfo CreateDelegateInfo(TypeDesc delegateType) + { + return new DelegateInfo(delegateType, _delegateFeatures); + } + + private readonly LazyGenericsSupport.GenericCycleDetector _genericCycleDetector = new LazyGenericsSupport.GenericCycleDetector(); + + public void DetectGenericCycles(TypeSystemEntity owner, TypeSystemEntity referent) + { + _genericCycleDetector.DetectCycle(owner, referent); + } + + public void LogWarnings(Logger logger) + { + _genericCycleDetector.LogWarnings(logger); + } + } + + public class SharedGenericsConfiguration + { + // + // Universal Shared Generics heuristics magic values determined empirically + // + public long UniversalCanonGVMReflectionRootHeuristic_InstantiationCount { get; } + public long UniversalCanonGVMDepthHeuristic_NonCanonDepth { get; } + public long UniversalCanonGVMDepthHeuristic_CanonDepth { get; } + + // Controls how many different instantiations of a generic method, or method on generic type + // should be allowed before trying to fall back to only supplying USG in the reflection + // method table. + public long UniversalCanonReflectionMethodRootHeuristic_InstantiationCount { get; } + + // To avoid infinite generic recursion issues during debug type record generation, attempt to + // use canonical form for types with high generic complexity. + public long MaxGenericDepthOfDebugRecord { get; } + + public SharedGenericsConfiguration() + { + UniversalCanonGVMReflectionRootHeuristic_InstantiationCount = 4; + UniversalCanonGVMDepthHeuristic_NonCanonDepth = 2; + UniversalCanonGVMDepthHeuristic_CanonDepth = 1; + + // Unlike the GVM heuristics which are intended to kick in aggressively + // this heuristic exists to make it so that a fair amount of generic + // expansion is allowed. Numbers are chosen to allow a fairly large + // amount of generic expansion before trimming. + UniversalCanonReflectionMethodRootHeuristic_InstantiationCount = 1024; + + MaxGenericDepthOfDebugRecord = 15; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.BoxedTypes.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.BoxedTypes.cs new file mode 100644 index 00000000000000..8be62c3d9950db --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.BoxedTypes.cs @@ -0,0 +1,605 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Text; + +using Internal.TypeSystem; +using Internal.IL; +using Internal.IL.Stubs; + +using Debug = System.Diagnostics.Debug; + +// +// Functionality related to instantiating unboxing thunks +// +// To support calling canonical interface methods on generic valuetypes, +// the compiler needs to generate unboxing+instantiating thunks that bridge +// the difference between the two calling conventions. +// +// As a refresher: +// * Instance methods on shared generic valuetypes expect two arguments +// (aside from the arguments declared in the signature): a ByRef to the +// first byte of the value of the valuetype (this), and a generic context +// argument (MethodTable) +// * Interface calls expect 'this' to be a reference type (with the generic +// context to be inferred from 'this' by the callee). +// +// Instantiating and unboxing stubs bridge this by extracting a managed +// pointer out of a boxed valuetype, along with the MethodTable of the boxed +// valuetype (to provide the generic context) before dispatching to the +// instance method with the different calling convention. +// +// We compile them by: +// * Pretending the unboxing stub is an instance method on a reference type +// with the same layout as a boxed valuetype (this matches the calling +// convention expected by the caller). +// * Having the unboxing stub load the m_pEEType field (to get generic +// context) and a byref to the actual value (to get a 'this' expected by +// valuetype methods) +// * Generating a call to a fake instance method on the valuetype that has +// the hidden (generic context) argument explicitly present in the +// signature. We need a fake method to be able to refer to the hidden parameter +// from IL. +// +// At a later stage (once codegen is done), we replace the references to the +// fake instance method with the real instance method. Their signatures after +// compilation is identical. +// + +namespace ILCompiler +{ + // Contains functionality related to pseudotypes representing boxed instances of value types + partial class CompilerTypeSystemContext + { + /// + /// For a shared (canonical) instance method on a generic valuetype, gets a method that can be used to call the + /// method given a boxed version of the generic valuetype as 'this' pointer. + /// + public MethodDesc GetSpecialUnboxingThunk(MethodDesc targetMethod, ModuleDesc ownerModuleOfThunk) + { + Debug.Assert(targetMethod.IsSharedByGenericInstantiations); + Debug.Assert(!targetMethod.Signature.IsStatic); + Debug.Assert(!targetMethod.HasInstantiation); + + TypeDesc owningType = targetMethod.OwningType; + Debug.Assert(owningType.IsValueType); + + var owningTypeDefinition = (MetadataType)owningType.GetTypeDefinition(); + + // Get a reference type that has the same layout as the boxed valuetype. + var typeKey = new BoxedValuetypeHashtableKey(owningTypeDefinition, ownerModuleOfThunk); + BoxedValueType boxedTypeDefinition = _boxedValuetypeHashtable.GetOrCreateValue(typeKey); + + // Get a method on the reference type with the same signature as the target method (but different + // calling convention, since 'this' will be a reference type). + var targetMethodDefinition = targetMethod.GetTypicalMethodDefinition(); + var methodKey = new UnboxingThunkHashtableKey(targetMethodDefinition, boxedTypeDefinition); + GenericUnboxingThunk thunkDefinition = _unboxingThunkHashtable.GetOrCreateValue(methodKey); + + // Find the thunk on the instantiated version of the reference type. + Debug.Assert(owningType != owningTypeDefinition); + InstantiatedType boxedType = boxedTypeDefinition.MakeInstantiatedType(owningType.Instantiation); + + MethodDesc thunk = GetMethodForInstantiatedType(thunkDefinition, boxedType); + Debug.Assert(!thunk.HasInstantiation); + + return thunk; + } + + public MethodDesc GetUnboxingThunk(MethodDesc targetMethod, ModuleDesc ownerModuleOfThunk) + { + TypeDesc owningType = targetMethod.OwningType; + Debug.Assert(owningType.IsValueType); + + var owningTypeDefinition = (MetadataType)owningType.GetTypeDefinition(); + + // Get a reference type that has the same layout as the boxed valuetype. + var typeKey = new BoxedValuetypeHashtableKey(owningTypeDefinition, ownerModuleOfThunk); + BoxedValueType boxedTypeDefinition = _boxedValuetypeHashtable.GetOrCreateValue(typeKey); + + // Get a method on the reference type with the same signature as the target method (but different + // calling convention, since 'this' will be a reference type). + var targetMethodDefinition = targetMethod.GetTypicalMethodDefinition(); + var methodKey = new UnboxingThunkHashtableKey(targetMethodDefinition, boxedTypeDefinition); + UnboxingThunk thunkDefinition = _nonGenericUnboxingThunkHashtable.GetOrCreateValue(methodKey); + + // Find the thunk on the instantiated version of the reference type. + MethodDesc thunk; + if (owningType != owningTypeDefinition) + { + InstantiatedType boxedType = boxedTypeDefinition.MakeInstantiatedType(owningType.Instantiation); + thunk = GetMethodForInstantiatedType(thunkDefinition, boxedType); + } + else + { + thunk = thunkDefinition; + } + if (thunk.HasInstantiation) + thunk = thunk.MakeInstantiatedMethod(targetMethod.Instantiation); + + return thunk; + } + + /// + /// Returns true of is a standin method for unboxing thunk target. + /// + public bool IsSpecialUnboxingThunkTargetMethod(MethodDesc method) + { + return method.GetTypicalMethodDefinition().GetType() == typeof(ValueTypeInstanceMethodWithHiddenParameter); + } + + /// + /// Returns the real target method of an unboxing stub. + /// + public MethodDesc GetRealSpecialUnboxingThunkTargetMethod(MethodDesc method) + { + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + MethodDesc methodDefinitionRepresented = ((ValueTypeInstanceMethodWithHiddenParameter)typicalMethod).MethodRepresented; + return GetMethodForInstantiatedType(methodDefinitionRepresented, (InstantiatedType)method.OwningType); + } + + private struct BoxedValuetypeHashtableKey + { + public readonly MetadataType ValueType; + public readonly ModuleDesc OwningModule; + + public BoxedValuetypeHashtableKey(MetadataType valueType, ModuleDesc owningModule) + { + ValueType = valueType; + OwningModule = owningModule; + } + } + + private class BoxedValuetypeHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(BoxedValuetypeHashtableKey key) + { + return key.ValueType.GetHashCode(); + } + protected override int GetValueHashCode(BoxedValueType value) + { + return value.ValueTypeRepresented.GetHashCode(); + } + protected override bool CompareKeyToValue(BoxedValuetypeHashtableKey key, BoxedValueType value) + { + return Object.ReferenceEquals(key.ValueType, value.ValueTypeRepresented) && + Object.ReferenceEquals(key.OwningModule, value.Module); + } + protected override bool CompareValueToValue(BoxedValueType value1, BoxedValueType value2) + { + return Object.ReferenceEquals(value1.ValueTypeRepresented, value2.ValueTypeRepresented) && + Object.ReferenceEquals(value1.Module, value2.Module); + } + protected override BoxedValueType CreateValueFromKey(BoxedValuetypeHashtableKey key) + { + return new BoxedValueType(key.OwningModule, key.ValueType); + } + } + private BoxedValuetypeHashtable _boxedValuetypeHashtable = new BoxedValuetypeHashtable(); + + private struct UnboxingThunkHashtableKey + { + public readonly MethodDesc TargetMethod; + public readonly BoxedValueType OwningType; + + public UnboxingThunkHashtableKey(MethodDesc targetMethod, BoxedValueType owningType) + { + TargetMethod = targetMethod; + OwningType = owningType; + } + } + + private class UnboxingThunkHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(UnboxingThunkHashtableKey key) + { + return key.TargetMethod.GetHashCode(); + } + protected override int GetValueHashCode(GenericUnboxingThunk value) + { + return value.TargetMethod.GetHashCode(); + } + protected override bool CompareKeyToValue(UnboxingThunkHashtableKey key, GenericUnboxingThunk value) + { + return Object.ReferenceEquals(key.TargetMethod, value.TargetMethod) && + Object.ReferenceEquals(key.OwningType, value.OwningType); + } + protected override bool CompareValueToValue(GenericUnboxingThunk value1, GenericUnboxingThunk value2) + { + return Object.ReferenceEquals(value1.TargetMethod, value2.TargetMethod) && + Object.ReferenceEquals(value1.OwningType, value2.OwningType); + } + protected override GenericUnboxingThunk CreateValueFromKey(UnboxingThunkHashtableKey key) + { + return new GenericUnboxingThunk(key.OwningType, key.TargetMethod); + } + } + private UnboxingThunkHashtable _unboxingThunkHashtable = new UnboxingThunkHashtable(); + + private class NonGenericUnboxingThunkHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(UnboxingThunkHashtableKey key) + { + return key.TargetMethod.GetHashCode(); + } + protected override int GetValueHashCode(UnboxingThunk value) + { + return value.TargetMethod.GetHashCode(); + } + protected override bool CompareKeyToValue(UnboxingThunkHashtableKey key, UnboxingThunk value) + { + return Object.ReferenceEquals(key.TargetMethod, value.TargetMethod) && + Object.ReferenceEquals(key.OwningType, value.OwningType); + } + protected override bool CompareValueToValue(UnboxingThunk value1, UnboxingThunk value2) + { + return Object.ReferenceEquals(value1.TargetMethod, value2.TargetMethod) && + Object.ReferenceEquals(value1.OwningType, value2.OwningType); + } + protected override UnboxingThunk CreateValueFromKey(UnboxingThunkHashtableKey key) + { + return new UnboxingThunk(key.OwningType, key.TargetMethod); + } + } + + private NonGenericUnboxingThunkHashtable _nonGenericUnboxingThunkHashtable = new NonGenericUnboxingThunkHashtable(); + + /// + /// A type with an identical layout to the layout of a boxed value type. + /// The type has a single field of the type of the valuetype it represents. + /// + private partial class BoxedValueType : MetadataType, INonEmittableType + { + public MetadataType ValueTypeRepresented { get; } + + public override ModuleDesc Module { get; } + + public override string Name => "Boxed_" + ValueTypeRepresented.Name; + + public override string Namespace => ValueTypeRepresented.Namespace; + + public override Instantiation Instantiation => ValueTypeRepresented.Instantiation; + public override PInvokeStringFormat PInvokeStringFormat => PInvokeStringFormat.AutoClass; + public override bool IsExplicitLayout => false; + public override bool IsSequentialLayout => true; + public override bool IsBeforeFieldInit => false; + public override MetadataType MetadataBaseType => (MetadataType)Context.GetWellKnownType(WellKnownType.Object); + public override DefType BaseType => MetadataBaseType; + public override bool IsSealed => true; + public override bool IsAbstract => false; + public override DefType ContainingType => null; + public override DefType[] ExplicitlyImplementedInterfaces => Array.Empty(); + public override TypeSystemContext Context => ValueTypeRepresented.Context; + + public BoxedValueType(ModuleDesc owningModule, MetadataType valuetype) + { + // BoxedValueType has the same genericness as the valuetype it's wrapping. + // Making BoxedValueType wrap the genericness (and be itself nongeneric) would + // require a crazy name mangling scheme to allow generating stable and unique names + // for the wrappers. + Debug.Assert(valuetype.IsTypeDefinition); + + Debug.Assert(valuetype.IsValueType); + + Module = owningModule; + ValueTypeRepresented = valuetype; + + // Unboxing thunks for byref-like types don't make sense. Byref-like types cannot be boxed. + // We still allow these to exist in the system, because it's easier than trying to prevent + // their creation. We create them as if they existed (in lieu of e.g. pointing all of them + // to the same __unreachable method body) so that the various places that store pointers to + // them because they want to be able to extract the target instance method can use the same + // mechanism they use for everything else at runtime. + } + + public override ClassLayoutMetadata GetClassLayout() => default(ClassLayoutMetadata); + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) => false; + public override IEnumerable GetNestedTypes() => Array.Empty(); + public override MetadataType GetNestedType(string name) => null; + protected override MethodImplRecord[] ComputeVirtualMethodImplsForType() => Array.Empty(); + public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name) => Array.Empty(); + + public override int GetHashCode() + { + string ns = Namespace; + var hashCodeBuilder = new Internal.NativeFormat.TypeHashingAlgorithms.HashCodeBuilder(ns); + if (ns.Length > 0) + hashCodeBuilder.Append("."); + hashCodeBuilder.Append(Name); + return hashCodeBuilder.ToHashCode(); + } + + protected override TypeFlags ComputeTypeFlags(TypeFlags mask) + { + TypeFlags flags = 0; + + if ((mask & TypeFlags.HasGenericVarianceComputed) != 0) + { + flags |= TypeFlags.HasGenericVarianceComputed; + } + + if ((mask & TypeFlags.CategoryMask) != 0) + { + flags |= TypeFlags.Class; + } + + flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.AttributeCacheComputed; + + return flags; + } + + public override FieldDesc GetField(string name) + { + return null; + } + + public override IEnumerable GetFields() + { + return Array.Empty(); + } + } + + /// + /// Does a method represent an unboxing stub + /// + public bool IsSpecialUnboxingThunk(MethodDesc method) + { + if (method.GetTypicalMethodDefinition().GetType() == typeof(GenericUnboxingThunk)) + return true; + + return false; + } + + /// + /// Convert from an unboxing stub to the actual target method + /// + public MethodDesc GetTargetOfSpecialUnboxingThunk(MethodDesc method) + { + MethodDesc typicalMethodTarget = ((GenericUnboxingThunk)method.GetTypicalMethodDefinition()).TargetMethod; + + MethodDesc methodOnInstantiatedType = typicalMethodTarget; + if (method.OwningType.HasInstantiation) + { + InstantiatedType instantiatedType = GetInstantiatedType((MetadataType)typicalMethodTarget.OwningType, method.OwningType.Instantiation); + methodOnInstantiatedType = GetMethodForInstantiatedType(typicalMethodTarget, instantiatedType); + } + + MethodDesc instantiatedMethod = methodOnInstantiatedType; + if (method.HasInstantiation) + { + instantiatedMethod = GetInstantiatedMethod(methodOnInstantiatedType, method.Instantiation); + } + + return instantiatedMethod; + } + + /// + /// Represents a thunk to call shared instance method on boxed valuetypes. + /// + private partial class GenericUnboxingThunk : ILStubMethod + { + private MethodDesc _targetMethod; + private ValueTypeInstanceMethodWithHiddenParameter _nakedTargetMethod; + private BoxedValueType _owningType; + + public GenericUnboxingThunk(BoxedValueType owningType, MethodDesc targetMethod) + { + Debug.Assert(targetMethod.OwningType.IsValueType); + Debug.Assert(!targetMethod.Signature.IsStatic); + + _owningType = owningType; + _targetMethod = targetMethod; + _nakedTargetMethod = new ValueTypeInstanceMethodWithHiddenParameter(targetMethod); + } + + public override TypeSystemContext Context => _targetMethod.Context; + + public override TypeDesc OwningType => _owningType; + + public override MethodSignature Signature => _targetMethod.Signature; + + public MethodDesc TargetMethod => _targetMethod; + + public override string Name + { + get + { + return _targetMethod.Name + "_Unbox"; + } + } + + public override MethodIL EmitIL() + { + if (_owningType.ValueTypeRepresented.IsByRefLike) + { + // If this is the fake unboxing thunk for ByRef-like types, just make a method that throws. + return new ILStubMethodIL(this, + new byte[] { (byte)ILOpcode.ldnull, (byte)ILOpcode.throw_ }, + Array.Empty(), + Array.Empty()); + } + + // Generate the unboxing stub. This loosely corresponds to following C#: + // return BoxedValue.InstanceMethod(this.m_pEEType, [rest of parameters]) + + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + + FieldDesc eeTypeField = Context.GetWellKnownType(WellKnownType.Object).GetKnownField("m_pEEType"); + + // Load ByRef to the field with the value of the boxed valuetype + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldflda, emit.NewToken(Context.SystemModule.GetKnownType("System.Runtime.CompilerServices", "RawData").GetField("Data"))); + + // Load the MethodTable of the boxed valuetype (this is the hidden generic context parameter expected + // by the (canonical) instance method, but normally not part of the signature in IL). + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emit.NewToken(eeTypeField)); + + // Load rest of the arguments + for (int i = 0; i < _targetMethod.Signature.Length; i++) + { + codeStream.EmitLdArg(i + 1); + } + + // Call an instance method on the target valuetype that has a fake instantiation parameter + // in it's signature. This will be swapped by the actual instance method after codegen is done. + codeStream.Emit(ILOpcode.call, emit.NewToken(_nakedTargetMethod.InstantiateAsOpen())); + codeStream.Emit(ILOpcode.ret); + + return emit.Link(this); + } + } + + /// + /// Represents a thunk to call instance method on boxed valuetypes. + /// + private partial class UnboxingThunk : ILStubMethod + { + private MethodDesc _targetMethod; + private BoxedValueType _owningType; + + public UnboxingThunk(BoxedValueType owningType, MethodDesc targetMethod) + { + Debug.Assert(targetMethod.OwningType.IsValueType); + Debug.Assert(!targetMethod.Signature.IsStatic); + + _owningType = owningType; + _targetMethod = targetMethod; + } + + public override TypeSystemContext Context => _targetMethod.Context; + + public override TypeDesc OwningType => _owningType; + + public override MethodSignature Signature => _targetMethod.Signature; + + public MethodDesc TargetMethod => _targetMethod; + + public override string Name + { + get + { + return _targetMethod.Name + "_Unbox"; + } + } + + public override MethodIL EmitIL() + { + if (_owningType.ValueTypeRepresented.IsByRefLike) + { + // If this is the fake unboxing thunk for ByRef-like types, just make a method that throws. + return new ILStubMethodIL(this, + new byte[] { (byte)ILOpcode.ldnull, (byte)ILOpcode.throw_ }, + Array.Empty(), + Array.Empty()); + } + + // Generate the unboxing stub. This loosely corresponds to following C#: + // return BoxedValue.InstanceMethod([rest of parameters]) + + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + + // unbox to get a pointer to the value type + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldflda, emit.NewToken(Context.SystemModule.GetKnownType("System.Runtime.CompilerServices", "RawData").GetField("Data"))); + + // Load rest of the arguments + for (int i = 0; i < _targetMethod.Signature.Length; i++) + { + codeStream.EmitLdArg(i + 1); + } + + TypeDesc owner = _targetMethod.OwningType; + MethodDesc methodToInstantiate = _targetMethod; + if (owner.HasInstantiation) + { + MetadataType instantiatedOwner = (MetadataType)owner.InstantiateAsOpen(); + methodToInstantiate = _targetMethod.Context.GetMethodForInstantiatedType(_targetMethod, (InstantiatedType)instantiatedOwner); + } + if (methodToInstantiate.HasInstantiation) + { + TypeSystemContext context = methodToInstantiate.Context; + + var inst = new TypeDesc[methodToInstantiate.Instantiation.Length]; + for (int i = 0; i < inst.Length; i++) + { + inst[i] = context.GetSignatureVariable(i, true); + } + + methodToInstantiate = context.GetInstantiatedMethod(methodToInstantiate, new Instantiation(inst)); + } + + codeStream.Emit(ILOpcode.call, emit.NewToken(methodToInstantiate)); + codeStream.Emit(ILOpcode.ret); + + return emit.Link(this); + } + + public override Instantiation Instantiation => _targetMethod.Instantiation; + } + + /// + /// Represents an instance method on a generic valuetype with an explicit instantiation parameter in the + /// signature. This is so that we can refer to the parameter from IL. References to this method will + /// be replaced by the actual instance method after codegen is done. + /// + internal partial class ValueTypeInstanceMethodWithHiddenParameter : MethodDesc + { + private MethodDesc _methodRepresented; + private MethodSignature _signature; + + public ValueTypeInstanceMethodWithHiddenParameter(MethodDesc methodRepresented) + { + Debug.Assert(methodRepresented.OwningType.IsValueType); + Debug.Assert(!methodRepresented.Signature.IsStatic); + + _methodRepresented = methodRepresented; + } + + public MethodDesc MethodRepresented => _methodRepresented; + + // We really don't want this method to be inlined. + public override bool IsNoInlining => true; + + public override bool IsInternalCall => true; + + public override bool IsIntrinsic => true; + + public override TypeSystemContext Context => _methodRepresented.Context; + public override TypeDesc OwningType => _methodRepresented.OwningType; + + public override string Name => _methodRepresented.Name; + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + TypeDesc[] parameters = new TypeDesc[_methodRepresented.Signature.Length + 1]; + + // Shared instance methods on generic valuetypes have a hidden parameter with the generic context. + // We add it to the signature so that we can refer to it from IL. + parameters[0] = Context.GetWellKnownType(WellKnownType.IntPtr); + for (int i = 0; i < _methodRepresented.Signature.Length; i++) + parameters[i + 1] = _methodRepresented.Signature[i]; + + _signature = new MethodSignature(_methodRepresented.Signature.Flags, + _methodRepresented.Signature.GenericParameterCount, + _methodRepresented.Signature.ReturnType, + parameters); + } + + return _signature; + } + } + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) => false; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.IntefaceThunks.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.IntefaceThunks.cs new file mode 100644 index 00000000000000..ad558ae43bf51b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.IntefaceThunks.cs @@ -0,0 +1,305 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Text; + +using Internal.TypeSystem; +using Internal.IL; +using Internal.IL.Stubs; + +using Debug = System.Diagnostics.Debug; + +// Default interface method implementation thunks +// +// The problem with default interface methods and shared generic code is that for: +// +// interface IFoo +// { +// Type GetTheType() => typeof(T); +// } +// +// The actual generated code when instantiated over a shareable instance (like object) is +// just IFoo<__Canon>.GetTheType, for any shareable argument (there's no unique code +// generated for IFoo/IFoo/... - we just have IFoo<__Canon>). +// +// For the canonical code to know what the actual T is, we need to provide the instantiation +// context somehow. We can't easily get it from `this` like we do for reference types +// since the type might implement multiple instantiations of IFoo (`class Abc : IFoo, IFoo { }`) +// and we wouldn't know which one we are executing for within the method body. +// +// So we end up passing the context same as for shared valuetype code (that also cannot +// determine context from just `this`) - by adding an extra instantiation +// argument. The actual code for IFoo<__Canon>.GetTheType looks something like this: +// +// Type IFoo__Canon__GetTheType(IFoo<__Canon> instance, MethodTable* context) +// { +// return Type.GetTypeFromHandle(GetTypeHandleOfTInIFooCanon(context)); +// } +// +// Now we have a problem because this method expects an extra `context` argument +// that will not be provided at the callsite, since the callsite doesn't know +// where it will dispatch to (could be a non-default-interface-method). +// +// We solve this with an instantiating thunk. The instantiating thunk is the thing +// we place in the vtable of the implementing type. The thunk looks like this: +// +// Type Abc_IFoo__Canon__GetTheType_Thunk(IFoo<__Canon> instance) +// { +// return IFoo__Canon__GetTheType(instance, GetOrdinalInterface(instance.m_pEEType, 0)); +// } +// +// Notice the thunk now has the expected signature, and some code to compute the context. +// +// The GetOrdinalInterface method retrieves the specified interface MethodTable off the MethodTable's interface list. +// The thunks are per-type (since the position in the inteface list is different). +// +// We hardcode the position in the interface list instead of just hardcoding the interface type +// itself so that we don't require runtime code generation when a new type is loaded +// (e.g. "class Abc : IFoo { }" and we MakeGenericType's a new Abc at runtime) - +// the instantiating thunk in this shape can be shared. +namespace ILCompiler +{ + // Contains functionality related to instantiating thunks for default interface methods + partial class CompilerTypeSystemContext + { + private const int UseContextFromRuntime = -1; + + /// + /// For a shared (canonical) default interface method, gets a method that can be used to call the + /// method on a specific implementing class. + /// + public MethodDesc GetDefaultInterfaceMethodImplementationThunk(MethodDesc targetMethod, TypeDesc implementingClass, DefType interfaceOnDefinition) + { + Debug.Assert(targetMethod.IsSharedByGenericInstantiations); + Debug.Assert(!targetMethod.Signature.IsStatic); + Debug.Assert(!targetMethod.HasInstantiation); + Debug.Assert(interfaceOnDefinition.GetTypeDefinition() == targetMethod.OwningType.GetTypeDefinition()); + Debug.Assert(targetMethod.OwningType.IsInterface); + + int interfaceIndex; + if (implementingClass.IsInterface) + { + Debug.Assert(((MetadataType)implementingClass).IsDynamicInterfaceCastableImplementation()); + interfaceIndex = UseContextFromRuntime; + } + else + { + interfaceIndex = Array.IndexOf(implementingClass.GetTypeDefinition().RuntimeInterfaces, interfaceOnDefinition); + Debug.Assert(interfaceIndex >= 0); + } + + // Get a method that will inject the appropriate instantiation context to the + // target default interface method. + var methodKey = new DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey(targetMethod, interfaceIndex); + MethodDesc thunk = _dimThunkHashtable.GetOrCreateValue(methodKey); + + return thunk; + } + + /// + /// Returns true of is a standin method for instantiating thunk target. + /// + public bool IsDefaultInterfaceMethodImplementationThunkTargetMethod(MethodDesc method) + { + return method.GetTypicalMethodDefinition().GetType() == typeof(DefaultInterfaceMethodImplementationWithHiddenParameter); + } + + /// + /// Returns the real target method of an instantiating thunk. + /// + public MethodDesc GetRealDefaultInterfaceMethodImplementationThunkTargetMethod(MethodDesc method) + { + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + return ((DefaultInterfaceMethodImplementationWithHiddenParameter)typicalMethod).MethodRepresented; + } + + private struct DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey + { + public readonly MethodDesc TargetMethod; + public readonly int InterfaceIndex; + + public DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey(MethodDesc targetMethod, int interfaceIndex) + { + TargetMethod = targetMethod; + InterfaceIndex = interfaceIndex; + } + } + + private class DefaultInterfaceMethodImplementationInstantiationThunkHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey key) + { + return key.TargetMethod.GetHashCode() ^ key.InterfaceIndex; + } + protected override int GetValueHashCode(DefaultInterfaceMethodImplementationInstantiationThunk value) + { + return value.TargetMethod.GetHashCode() ^ value.InterfaceIndex; + } + protected override bool CompareKeyToValue(DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey key, DefaultInterfaceMethodImplementationInstantiationThunk value) + { + return Object.ReferenceEquals(key.TargetMethod, value.TargetMethod) && + key.InterfaceIndex == value.InterfaceIndex; + } + protected override bool CompareValueToValue(DefaultInterfaceMethodImplementationInstantiationThunk value1, DefaultInterfaceMethodImplementationInstantiationThunk value2) + { + return Object.ReferenceEquals(value1.TargetMethod, value2.TargetMethod) && + value1.InterfaceIndex == value2.InterfaceIndex; + } + protected override DefaultInterfaceMethodImplementationInstantiationThunk CreateValueFromKey(DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey key) + { + TypeDesc owningTypeOfThunks = key.TargetMethod.Context.GeneratedAssembly.GetGlobalModuleType(); + return new DefaultInterfaceMethodImplementationInstantiationThunk(owningTypeOfThunks, key.TargetMethod, key.InterfaceIndex); + } + } + private DefaultInterfaceMethodImplementationInstantiationThunkHashtable _dimThunkHashtable = new DefaultInterfaceMethodImplementationInstantiationThunkHashtable(); + + /// + /// Represents a thunk to call shared instance method on generic interfaces. + /// + private partial class DefaultInterfaceMethodImplementationInstantiationThunk : ILStubMethod, IPrefixMangledMethod + { + private readonly MethodDesc _targetMethod; + private readonly DefaultInterfaceMethodImplementationWithHiddenParameter _nakedTargetMethod; + private readonly TypeDesc _owningType; + private readonly int _interfaceIndex; + + public DefaultInterfaceMethodImplementationInstantiationThunk(TypeDesc owningType, MethodDesc targetMethod, int interfaceIndex) + { + Debug.Assert(targetMethod.OwningType.IsInterface); + Debug.Assert(!targetMethod.Signature.IsStatic); + + _owningType = owningType; + _targetMethod = targetMethod; + _nakedTargetMethod = new DefaultInterfaceMethodImplementationWithHiddenParameter(targetMethod, owningType); + _interfaceIndex = interfaceIndex; + } + + public override TypeSystemContext Context => _targetMethod.Context; + + public override TypeDesc OwningType => _owningType; + + public int InterfaceIndex => _interfaceIndex; + + public override MethodSignature Signature => _targetMethod.Signature; + + public MethodDesc TargetMethod => _targetMethod; + + public override string Name + { + get + { + return _targetMethod.Name; + } + } + + public MethodDesc BaseMethod => _targetMethod; + + public string Prefix => $"__InstantiatingStub_{_interfaceIndex}_"; + + public override MethodIL EmitIL() + { + // Generate the instantiating stub. This loosely corresponds to following C#: + // return Interface.Method(this, GetOrdinalInterface(this.m_pEEType, Index), [rest of parameters]) + + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + + FieldDesc eeTypeField = Context.GetWellKnownType(WellKnownType.Object).GetKnownField("m_pEEType"); + MethodDesc getOrdinalInterfaceMethod = Context.GetHelperEntryPoint("SharedCodeHelpers", "GetOrdinalInterface"); + MethodDesc getCurrentContext = Context.GetHelperEntryPoint("SharedCodeHelpers", "GetCurrentSharedThunkContext"); + + // Load "this" + codeStream.EmitLdArg(0); + + // Load the instantiating argument. + if (_interfaceIndex == UseContextFromRuntime) + { + codeStream.Emit(ILOpcode.call, emit.NewToken(getCurrentContext)); + } + else + { + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emit.NewToken(eeTypeField)); + codeStream.EmitLdc(_interfaceIndex); + codeStream.Emit(ILOpcode.call, emit.NewToken(getOrdinalInterfaceMethod)); + } + + // Load rest of the arguments + for (int i = 0; i < _targetMethod.Signature.Length; i++) + { + codeStream.EmitLdArg(i + 1); + } + + // Call an instance method on the target interface that has a fake instantiation parameter + // in it's signature. This will be swapped by the actual instance method after codegen is done. + codeStream.Emit(ILOpcode.call, emit.NewToken(_nakedTargetMethod)); + codeStream.Emit(ILOpcode.ret); + + return emit.Link(this); + } + } + + /// + /// Represents an instance method on a generic interface with an explicit instantiation parameter in the + /// signature. This is so that we can refer to the parameter from IL. References to this method will + /// be replaced by the actual instance method after codegen is done. + /// + internal partial class DefaultInterfaceMethodImplementationWithHiddenParameter : MethodDesc + { + private readonly MethodDesc _methodRepresented; + private readonly TypeDesc _owningType; + private MethodSignature _signature; + + public DefaultInterfaceMethodImplementationWithHiddenParameter(MethodDesc methodRepresented, TypeDesc owningType) + { + Debug.Assert(methodRepresented.OwningType.IsInterface); + Debug.Assert(!methodRepresented.Signature.IsStatic); + + _methodRepresented = methodRepresented; + _owningType = owningType; + } + + public MethodDesc MethodRepresented => _methodRepresented; + + // We really don't want this method to be inlined. + public override bool IsNoInlining => true; + + public override bool IsInternalCall => true; + + public override bool IsIntrinsic => true; + + public override TypeSystemContext Context => _methodRepresented.Context; + public override TypeDesc OwningType => _owningType; + + public override string Name => _methodRepresented.Name; + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + TypeDesc[] parameters = new TypeDesc[_methodRepresented.Signature.Length + 1]; + + // Shared instance methods on generic interfaces have a hidden parameter with the generic context. + // We add it to the signature so that we can refer to it from IL. + parameters[0] = Context.GetWellKnownType(WellKnownType.IntPtr); + for (int i = 0; i < _methodRepresented.Signature.Length; i++) + parameters[i + 1] = _methodRepresented.Signature[i]; + + _signature = new MethodSignature(_methodRepresented.Signature.Flags, + _methodRepresented.Signature.GenericParameterCount, + _methodRepresented.Signature.ReturnType, + parameters); + } + + return _signature; + } + } + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) => false; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Mangling.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Mangling.cs new file mode 100644 index 00000000000000..edbb37fedb4c29 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Mangling.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; + +namespace ILCompiler +{ + // Contains functionality related to name mangling + partial class CompilerTypeSystemContext + { + partial class BoxedValueType : IPrefixMangledType + { + TypeDesc IPrefixMangledType.BaseType + { + get + { + return ValueTypeRepresented; + } + } + + string IPrefixMangledType.Prefix + { + get + { + return "Boxed"; + } + } + } + + partial class GenericUnboxingThunk : IPrefixMangledMethod + { + MethodDesc IPrefixMangledMethod.BaseMethod + { + get + { + return _targetMethod; + } + } + + string IPrefixMangledMethod.Prefix + { + get + { + return "unbox"; + } + } + } + + partial class UnboxingThunk : IPrefixMangledMethod + { + MethodDesc IPrefixMangledMethod.BaseMethod + { + get + { + return _targetMethod; + } + } + + string IPrefixMangledMethod.Prefix + { + get + { + return "unbox"; + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Sorting.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Sorting.cs new file mode 100644 index 00000000000000..26226baab4e720 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Sorting.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler +{ + // Functionality related to determinstic ordering of types and members + partial class CompilerTypeSystemContext + { + partial class BoxedValueType + { + protected override int ClassCode => 1062019524; + + protected override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) + { + return comparer.Compare(ValueTypeRepresented, ((BoxedValueType)other).ValueTypeRepresented); + } + } + + partial class GenericUnboxingThunk + { + protected override int ClassCode => -247515475; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (GenericUnboxingThunk)other; + return comparer.Compare(_targetMethod, otherMethod._targetMethod); + } + } + + partial class UnboxingThunk + { + protected override int ClassCode => 446545583; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (UnboxingThunk)other; + return comparer.Compare(_targetMethod, otherMethod._targetMethod); + } + } + + partial class ValueTypeInstanceMethodWithHiddenParameter + { + protected override int ClassCode => 2131875345; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (ValueTypeInstanceMethodWithHiddenParameter)other; + return comparer.Compare(_methodRepresented, otherMethod._methodRepresented); + } + } + + partial class DefaultInterfaceMethodImplementationInstantiationThunk + { + protected override int ClassCode => -789598; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (DefaultInterfaceMethodImplementationInstantiationThunk)other; + + int result = System.Collections.Generic.Comparer.Default.Compare(_interfaceIndex, otherMethod._interfaceIndex); + if (result != 0) + return result; + + return comparer.Compare(_targetMethod, otherMethod._targetMethod); + } + } + + partial class DefaultInterfaceMethodImplementationWithHiddenParameter + { + protected override int ClassCode => 4903209; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (DefaultInterfaceMethodImplementationWithHiddenParameter)other; + return comparer.Compare(_methodRepresented, otherMethod._methodRepresented); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs new file mode 100644 index 00000000000000..895c3433c5b816 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.Dataflow +{ + static class DiagnosticUtilities + { + internal static Origin GetMethodParameterFromIndex(MethodDesc method, int parameterIndex) + { + int declaredParameterIndex; + if (!method.Signature.IsStatic) + { + if (parameterIndex == 0) + return new MethodOrigin(method); + + declaredParameterIndex = parameterIndex - 1; + } + else + declaredParameterIndex = parameterIndex; + + return new ParameterOrigin(method, declaredParameterIndex); + } + + internal static string GetParameterNameForErrorMessage(ParameterOrigin origin) + { + return $"#{origin.Index}"; + } + + internal static string GetMethodSignatureDisplayName(MethodDesc method) + { + return method.GetDisplayName(); + } + + internal static string GetGenericParameterDeclaringMemberDisplayName(GenericParameterOrigin origin) + { + var param = (EcmaGenericParameter)origin.GenericParameter; + var parent = param.Module.GetObject(param.MetadataReader.GetGenericParameter(param.Handle).Parent); + if (parent is MethodDesc m) + return m.GetDisplayName(); + else + return ((TypeDesc)parent).GetDisplayName(); + } + + internal static string GetRequiresUnreferencedCodeAttributeMessage(MethodDesc method) + { + var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod; + if (ecmaMethod == null) + return null; + + var decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresUnreferencedCodeAttribute"); + if (decoded == null) + return null; + + var decodedValue = decoded.Value; + + if (decodedValue.FixedArguments.Length != 0) + return (string)decodedValue.FixedArguments[0].Value; + + return null; + } + + internal static string GetRequiresDynamicCodeAttributeMessage(MethodDesc method) + { + var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod; + if (ecmaMethod == null) + return null; + + var decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresDynamicCodeAttribute"); + if (decoded == null) + return null; + + var decodedValue = decoded.Value; + + if (decodedValue.FixedArguments.Length != 0) + return (string)decodedValue.FixedArguments[0].Value; + + return null; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DynamicallyAccessedMembersBinder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DynamicallyAccessedMembersBinder.cs new file mode 100644 index 00000000000000..9eabda7ff273ca --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DynamicallyAccessedMembersBinder.cs @@ -0,0 +1,538 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.Dataflow +{ + internal static class DynamicallyAccessedMembersBinder + { + // Returns the members of the type bound by memberTypes. For DynamicallyAccessedMemberTypes.All, this returns all members of the type and its + // nested types, including interface implementations, plus the same or any base types or implemented interfaces. + // DynamicallyAccessedMemberTypes.PublicNestedTypes and NonPublicNestedTypes do the same for members of the selected nested types. + public static IEnumerable GetDynamicallyAccessedMembers(this TypeDesc typeDefinition, DynamicallyAccessedMemberTypes memberTypes, bool declaredOnly = false) + { + if (memberTypes == DynamicallyAccessedMemberTypes.None) + yield break; + + if (memberTypes == DynamicallyAccessedMemberTypes.All) + { + var members = new List(); + typeDefinition.GetAllOnType(declaredOnly, members); + foreach (var m in members) + yield return m; + yield break; + } + + var declaredOnlyFlags = declaredOnly ? BindingFlags.DeclaredOnly : BindingFlags.Default; + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.NonPublicConstructors)) + { + foreach (var c in typeDefinition.GetConstructorsOnType(filter: null, bindingFlags: BindingFlags.NonPublic)) + yield return c; + } + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors)) + { + foreach (var c in typeDefinition.GetConstructorsOnType(filter: null, bindingFlags: BindingFlags.Public)) + yield return c; + } + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)) + { + foreach (var c in typeDefinition.GetConstructorsOnType(filter: m => m.IsPublic() && m.Signature.Length == 0)) + yield return c; + } + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.NonPublicMethods)) + { + foreach (var m in typeDefinition.GetMethodsOnTypeHierarchy(filter: null, bindingFlags: BindingFlags.NonPublic | declaredOnlyFlags)) + yield return m; + } + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicMethods)) + { + foreach (var m in typeDefinition.GetMethodsOnTypeHierarchy(filter: null, bindingFlags: BindingFlags.Public | declaredOnlyFlags)) + yield return m; + } + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.NonPublicFields)) + { + foreach (var f in typeDefinition.GetFieldsOnTypeHierarchy(filter: null, bindingFlags: BindingFlags.NonPublic | declaredOnlyFlags)) + yield return f; + } + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicFields)) + { + foreach (var f in typeDefinition.GetFieldsOnTypeHierarchy(filter: null, bindingFlags: BindingFlags.Public | declaredOnlyFlags)) + yield return f; + } + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.NonPublicNestedTypes)) + { + foreach (var t in typeDefinition.GetNestedTypesOnType(filter: null, bindingFlags: BindingFlags.NonPublic)) + { + yield return t; + var members = new List(); + t.GetAllOnType(declaredOnly: false, members); + foreach (var m in members) + yield return m; + } + } + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicNestedTypes)) + { + foreach (var t in typeDefinition.GetNestedTypesOnType(filter: null, bindingFlags: BindingFlags.Public)) + { + yield return t; + var members = new List(); + t.GetAllOnType(declaredOnly: false, members); + foreach (var m in members) + yield return m; + } + } + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.NonPublicProperties)) + { + foreach (var p in typeDefinition.GetPropertiesOnTypeHierarchy(filter: null, bindingFlags: BindingFlags.NonPublic | declaredOnlyFlags)) + yield return p; + } + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicProperties)) + { + foreach (var p in typeDefinition.GetPropertiesOnTypeHierarchy(filter: null, bindingFlags: BindingFlags.Public | declaredOnlyFlags)) + yield return p; + } + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.NonPublicEvents)) + { + foreach (var e in typeDefinition.GetEventsOnTypeHierarchy(filter: null, bindingFlags: BindingFlags.NonPublic | declaredOnlyFlags)) + yield return e; + } + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicEvents)) + { + foreach (var e in typeDefinition.GetEventsOnTypeHierarchy(filter: null, bindingFlags: BindingFlags.Public | declaredOnlyFlags)) + yield return e; + } + } + + public static IEnumerable GetConstructorsOnType(this TypeDesc type, Func filter, BindingFlags? bindingFlags = null) + { + if (type.IsArray) + { + // Constructors on arrays are special magic that the reflection stack special cases at runtime anyway. + yield break; + } + + foreach (var method in type.GetMethods()) + { + if (!method.IsConstructor) + continue; + + if (filter != null && !filter(method)) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static && !method.Signature.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance && method.Signature.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public && !method.IsPublic()) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic && method.IsPublic()) + continue; + + yield return method; + } + } + + public static IEnumerable GetMethodsOnTypeHierarchy(this TypeDesc type, Func filter, BindingFlags? bindingFlags = null) + { + bool onBaseType = false; + + if (type.IsArray) + { + // Methods on arrays are special magic that the reflection stack special cases at runtime anyway. + type = type.BaseType; + onBaseType = true; + } + + while (type != null) + { + foreach (var method in type.GetMethods()) + { + // Ignore constructors as those are not considered methods from a reflection's point of view + if (method.IsConstructor) + continue; + + // Ignore private methods on a base type - those are completely ignored by reflection + // (anything private on the base type is not visible via the derived type) + if (onBaseType && method.IsPrivate()) + continue; + + // Note that special methods like property getter/setter, event adder/remover will still get through and will be marked. + // This is intentional as reflection treats these as methods as well. + + if (filter != null && !filter(method)) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static && !method.Signature.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance && method.Signature.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public && !method.IsPublic()) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic && method.IsPublic()) + continue; + + yield return method; + } + + if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) + yield break; + + type = type.TryGetBaseType(); + onBaseType = true; + } + } + + public static IEnumerable GetFieldsOnTypeHierarchy(this TypeDesc type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + bool onBaseType = false; + while (type != null) + { + foreach (var field in type.GetFields()) + { + // Ignore private fields on a base type - those are completely ignored by reflection + // (anything private on the base type is not visible via the derived type) + if (onBaseType && field.IsPrivate()) + continue; + + // Note that compiler generated fields backing some properties and events will get through here. + // This is intentional as reflection treats these as fields as well. + + if (filter != null && !filter(field)) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static && !field.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance && field.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public && !field.IsPublic()) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic && field.IsPublic()) + continue; + + yield return field; + } + + if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) + yield break; + + type = type.TryGetBaseType(); + onBaseType = true; + } + } + + public static IEnumerable GetNestedTypesOnType(this TypeDesc type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + if (type is not MetadataType mdType) + yield break; + + foreach (var nestedType in mdType.GetNestedTypes()) + { + if (filter != null && !filter(nestedType)) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public) + { + if (!nestedType.IsNestedPublic()) + continue; + } + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic) + { + if (nestedType.IsNestedPublic()) + continue; + } + + yield return nestedType; + } + } + + public static IEnumerable GetPropertiesOnTypeHierarchy(this TypeDesc type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + bool onBaseType = false; + + if (type.IsArray) + { + type = type.BaseType; + onBaseType = true; + } + + while (type != null) + { + if (type.GetTypeDefinition() is not EcmaType ecmaType) + { + yield break; + } + + foreach (var propertyHandle in ecmaType.MetadataReader.GetTypeDefinition(ecmaType.Handle).GetProperties()) + { + var property = new PropertyPseudoDesc(ecmaType, propertyHandle); + + // Ignore private properties on a base type - those are completely ignored by reflection + // (anything private on the base type is not visible via the derived type) + // Note that properties themselves are not actually private, their accessors are + if (onBaseType && + (property.GetMethod == null || property.GetMethod.IsPrivate()) && + (property.SetMethod == null || property.SetMethod.IsPrivate())) + continue; + + if (filter != null && !filter(property)) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static) + { + if ((property.GetMethod != null) && !property.GetMethod.Signature.IsStatic) continue; + if ((property.SetMethod != null) && !property.SetMethod.Signature.IsStatic) continue; + } + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance) + { + if ((property.GetMethod != null) && property.GetMethod.Signature.IsStatic) continue; + if ((property.SetMethod != null) && property.SetMethod.Signature.IsStatic) continue; + } + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public) + { + if ((property.GetMethod == null || !property.GetMethod.IsPublic()) + && (property.SetMethod == null || !property.SetMethod.IsPublic())) + continue; + } + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic) + { + if ((property.GetMethod != null) && property.GetMethod.IsPublic()) continue; + if ((property.SetMethod != null) && property.SetMethod.IsPublic()) continue; + } + + yield return property; + } + + if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) + yield break; + + type = type.TryGetBaseType(); + onBaseType = true; + } + } + + public static IEnumerable GetEventsOnTypeHierarchy(this TypeDesc type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + bool onBaseType = false; + + if (type.IsArray) + { + type = type.BaseType; + onBaseType = true; + } + + while (type != null) + { + if (type.GetTypeDefinition() is not EcmaType ecmaType) + { + yield break; + } + + foreach (var eventHandle in ecmaType.MetadataReader.GetTypeDefinition(ecmaType.Handle).GetEvents()) + { + var @event = new EventPseudoDesc(ecmaType, eventHandle); + + // Ignore private properties on a base type - those are completely ignored by reflection + // (anything private on the base type is not visible via the derived type) + // Note that properties themselves are not actually private, their accessors are + if (onBaseType && + (@event.AddMethod == null || @event.AddMethod.IsPrivate()) && + (@event.RemoveMethod == null || @event.RemoveMethod.IsPrivate())) + continue; + + if (filter != null && !filter(@event)) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static) + { + if ((@event.AddMethod != null) && !@event.AddMethod.Signature.IsStatic) continue; + if ((@event.RemoveMethod != null) && !@event.RemoveMethod.Signature.IsStatic) continue; + } + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance) + { + if ((@event.AddMethod != null) && @event.AddMethod.Signature.IsStatic) continue; + if ((@event.RemoveMethod != null) && @event.RemoveMethod.Signature.IsStatic) continue; + } + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public) + { + if ((@event.AddMethod == null || !@event.AddMethod.IsPublic()) + && (@event.RemoveMethod == null || !@event.RemoveMethod.IsPublic())) + continue; + } + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic) + { + if ((@event.AddMethod != null) && @event.AddMethod.IsPublic()) continue; + if ((@event.RemoveMethod != null) && @event.RemoveMethod.IsPublic()) continue; + } + + yield return @event; + } + + if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) + yield break; + + type = type.TryGetBaseType(); + onBaseType = true; + } + } + + // declaredOnly will cause this to retrieve interfaces recursively required by the type, but doesn't necessarily + // include interfaces required by any base types. + public static IEnumerable GetAllInterfaceImplementations(this TypeDesc type, bool declaredOnly) + { + while (type != null) + { + foreach (DefType i in type.TryGetExplicitlyImplementedInterfaces()) + { + yield return i; + + // declaredOnly here doesn't matter since interfaces don't have base types + foreach (DefType innerInterface in i.GetAllInterfaceImplementations(declaredOnly: true)) + yield return innerInterface; + } + + if (declaredOnly) + yield break; + + type = type.TryGetBaseType(); + } + } + + // declaredOnly will cause this to retrieve only members of the type, not of its base types. This includes interfaces recursively + // required by this type (but not members of these interfaces, or interfaces required only by base types). + public static void GetAllOnType(this TypeDesc type, bool declaredOnly, List members) => GetAllOnType(type, declaredOnly, members, new HashSet()); + + private static void GetAllOnType(TypeDesc type, bool declaredOnly, List members, HashSet types) + { + if (!types.Add(type)) + return; + + if (type is MetadataType mdType) + { + foreach (MetadataType nested in mdType.GetNestedTypes()) + { + members.Add(nested); + // Base types and interfaces of nested types are always included. + GetAllOnType(nested, declaredOnly: false, members, types); + } + } + + if (!declaredOnly) + { + DefType baseType = type.TryGetBaseType(); + if (baseType != null) + GetAllOnType(baseType, declaredOnly: false, members, types); + } + + if (declaredOnly) + { + foreach (DefType iface in type.GetAllInterfaceImplementations(declaredOnly: true)) + members.Add(iface); + } + else + { + foreach (DefType interfaceType in type.TryGetExplicitlyImplementedInterfaces()) + { + members.Add(interfaceType); + GetAllOnType(interfaceType, declaredOnly: false, members, types); + } + } + + foreach (var f in type.GetFields()) + members.Add(f); + + foreach (var m in type.GetMethods()) + members.Add(m); + + foreach (var p in type.GetPropertiesOnTypeHierarchy(filter: null, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly)) + members.Add(p); + + foreach (var e in type.GetEventsOnTypeHierarchy(filter: null, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly)) + members.Add(e); + } + + private static DefType TryGetBaseType(this TypeDesc type) + { + try + { + return type.BaseType; + } + catch (TypeSystemException) + { + return null; + } + } + + private static DefType[] TryGetExplicitlyImplementedInterfaces(this TypeDesc type) + { + if (type is MetadataType mdType) + { + try + { + return mdType.ExplicitlyImplementedInterfaces; + } + catch (TypeSystemException) + { + } + } + return Array.Empty(); + } + } + + // Temporary local copy of the enum because the enum we're compiling against + // doesn't define all the values. Can be removed once we update to .NET 6. + public enum DynamicallyAccessedMemberTypes + { + None = 0, + PublicParameterlessConstructor = 0x0001, + PublicConstructors = 0x0002 | PublicParameterlessConstructor, + NonPublicConstructors = 0x0004, + PublicMethods = 0x0008, + NonPublicMethods = 0x0010, + PublicFields = 0x0020, + NonPublicFields = 0x0040, + PublicNestedTypes = 0x0080, + NonPublicNestedTypes = 0x0100, + PublicProperties = 0x0200, + NonPublicProperties = 0x0400, + PublicEvents = 0x0800, + NonPublicEvents = 0x1000, + Interfaces = 0x2000, + All = ~None + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/EcmaExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/EcmaExtensions.cs new file mode 100644 index 00000000000000..48502b786adbd5 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/EcmaExtensions.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using MethodAttributes = System.Reflection.MethodAttributes; +using FieldAttributes = System.Reflection.FieldAttributes; +using TypeAttributes = System.Reflection.TypeAttributes; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.Dataflow +{ + static class EcmaExtensions + { + public static bool IsPublic(this MethodDesc method) + { + return method.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod + && (ecmaMethod.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public; + } + + public static bool IsPublic(this FieldDesc field) + { + return field.GetTypicalFieldDefinition() is EcmaField ecmaField + && (ecmaField.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public; + } + + public static bool IsPrivate(this MethodDesc method) + { + return method.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod + && (ecmaMethod.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private; + } + + public static bool IsPrivate(this FieldDesc field) + { + return field.GetTypicalFieldDefinition() is EcmaField ecmaField + && (ecmaField.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Private; + } + + public static bool IsNestedPublic(this MetadataType mdType) + { + return mdType.GetTypeDefinition() is EcmaType ecmaType + && (ecmaType.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic; + } + + public static PropertyPseudoDesc GetProperty(this MetadataType mdType, string name, PropertySignature? signature) + { + Debug.Assert(signature == null); + + var type = (EcmaType)mdType.GetTypeDefinition(); + var reader = type.MetadataReader; + foreach (var propertyHandle in reader.GetTypeDefinition(type.Handle).GetProperties()) + { + if (reader.StringComparer.Equals(reader.GetPropertyDefinition(propertyHandle).Name, name)) + { + return new PropertyPseudoDesc(type, propertyHandle); + } + } + + mdType = mdType.MetadataBaseType; + if (mdType != null) + return GetProperty(mdType, name, signature); + + return null; + } + + public static PropertyPseudoDesc GetPropertyForAccessor(this MethodDesc accessor) + { + var ecmaAccessor = (EcmaMethod)accessor.GetTypicalMethodDefinition(); + var type = (EcmaType)ecmaAccessor.OwningType; + var reader = type.MetadataReader; + var module = type.EcmaModule; + foreach (var propertyHandle in reader.GetTypeDefinition(type.Handle).GetProperties()) + { + var accessors = reader.GetPropertyDefinition(propertyHandle).GetAccessors(); + if (ecmaAccessor.Handle == accessors.Getter + || ecmaAccessor.Handle == accessors.Setter) + { + return new PropertyPseudoDesc(type, propertyHandle); + } + } + + return null; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs new file mode 100644 index 00000000000000..f3585e0e098f06 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs @@ -0,0 +1,890 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Metadata; + +using Internal.IL; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.Dataflow +{ + /// + /// Caches dataflow annotations for type members. + /// + public class FlowAnnotations + { + private readonly TypeAnnotationsHashtable _hashtable; + private readonly Logger _logger; + + public FlowAnnotations(Logger logger, ILProvider ilProvider) + { + _hashtable = new TypeAnnotationsHashtable(logger, ilProvider); + _logger = logger; + } + + public bool RequiresDataflowAnalysis(MethodDesc method) + { + try + { + method = method.GetTypicalMethodDefinition(); + return GetAnnotations(method.OwningType).TryGetAnnotation(method, out _); + } + catch (TypeSystemException) + { + return false; + } + } + + public bool RequiresDataflowAnalysis(FieldDesc field) + { + try + { + field = field.GetTypicalFieldDefinition(); + return GetAnnotations(field.OwningType).TryGetAnnotation(field, out _); + } + catch (TypeSystemException) + { + return false; + } + } + + public bool HasAnyAnnotations(TypeDesc type) + { + try + { + return !GetAnnotations(type.GetTypeDefinition()).IsDefault; + } + catch (TypeSystemException) + { + return false; + } + } + + /// + /// Retrieves the annotations for the given parameter. + /// + /// Parameter index in the IL sense. Parameter 0 on instance methods is `this`. + public DynamicallyAccessedMemberTypes GetParameterAnnotation(MethodDesc method, int parameterIndex) + { + method = method.GetTypicalMethodDefinition(); + + if (GetAnnotations(method.OwningType).TryGetAnnotation(method, out var annotation) && annotation.ParameterAnnotations != null) + { + return annotation.ParameterAnnotations[parameterIndex]; + } + + return DynamicallyAccessedMemberTypes.None; + } + + public DynamicallyAccessedMemberTypes GetReturnParameterAnnotation(MethodDesc method) + { + method = method.GetTypicalMethodDefinition(); + + if (GetAnnotations(method.OwningType).TryGetAnnotation(method, out var annotation)) + { + return annotation.ReturnParameterAnnotation; + } + + return DynamicallyAccessedMemberTypes.None; + } + + public DynamicallyAccessedMemberTypes GetFieldAnnotation(FieldDesc field) + { + field = field.GetTypicalFieldDefinition(); + + if (GetAnnotations(field.OwningType).TryGetAnnotation(field, out var annotation)) + { + return annotation.Annotation; + } + + return DynamicallyAccessedMemberTypes.None; + } + + public DynamicallyAccessedMemberTypes GetTypeAnnotation(TypeDesc type) + { + return GetAnnotations(type.GetTypeDefinition()).TypeAnnotation; + } + + public DynamicallyAccessedMemberTypes GetGenericParameterAnnotation(GenericParameterDesc genericParameter) + { + if (genericParameter is not EcmaGenericParameter ecmaGenericParameter) + return DynamicallyAccessedMemberTypes.None; + + GenericParameter paramDef = ecmaGenericParameter.MetadataReader.GetGenericParameter(ecmaGenericParameter.Handle); + + if (ecmaGenericParameter.Kind == GenericParameterKind.Type) + { + TypeDesc parent = ecmaGenericParameter.Module.GetType(paramDef.Parent); + if (GetAnnotations(parent).TryGetAnnotation(ecmaGenericParameter, out var annotation)) + { + return annotation; + } + } + else + { + Debug.Assert(ecmaGenericParameter.Kind == GenericParameterKind.Method); + MethodDesc parent = ecmaGenericParameter.Module.GetMethod(paramDef.Parent); + if (GetAnnotations(parent.OwningType).TryGetAnnotation(parent, out var methodAnnotation) + && methodAnnotation.TryGetAnnotation(genericParameter, out var annotation)) + { + return annotation; + } + } + + return DynamicallyAccessedMemberTypes.None; + } + + public bool ShouldWarnWhenAccessedForReflection(MethodDesc method) + { + method = method.GetTypicalMethodDefinition(); + + if (!GetAnnotations(method.OwningType).TryGetAnnotation(method, out var annotation)) + return false; + + if (annotation.ParameterAnnotations == null && annotation.ReturnParameterAnnotation == DynamicallyAccessedMemberTypes.None) + return false; + + // If the method only has annotation on the return value and it's not virtual avoid warning. + // Return value annotations are "consumed" by the caller of a method, and as such there is nothing + // wrong calling these dynamically. The only problem can happen if something overrides a virtual + // method with annotated return value at runtime - in this case the trimmer can't validate + // that the method will return only types which fulfill the annotation's requirements. + // For example: + // class BaseWithAnnotation + // { + // [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] + // public abstract Type GetTypeWithFields(); + // } + // + // class UsingTheBase + // { + // public void PrintFields(Base base) + // { + // // No warning here - GetTypeWithFields is correctly annotated to allow GetFields on the return value. + // Console.WriteLine(string.Join(" ", base.GetTypeWithFields().GetFields().Select(f => f.Name))); + // } + // } + // + // If at runtime (through ref emit) something generates code like this: + // class DerivedAtRuntimeFromBase + // { + // // No point in adding annotation on the return value - nothing will look at it anyway + // // Linker will not see this code, so there are no checks + // public override Type GetTypeWithFields() { return typeof(TestType); } + // } + // + // If TestType from above is trimmed, it may note have all its fields, and there would be no warnings generated. + // But there has to be code like this somewhere in the app, in order to generate the override: + // class RuntimeTypeGenerator + // { + // public MethodInfo GetBaseMethod() + // { + // // This must warn - that the GetTypeWithFields has annotation on the return value + // return typeof(BaseWithAnnotation).GetMethod("GetTypeWithFields"); + // } + // } + return method.IsVirtual || annotation.ParameterAnnotations != null; + } + + public bool ShouldWarnWhenAccessedForReflection(FieldDesc field) + { + field = field.GetTypicalFieldDefinition(); + return GetAnnotations(field.OwningType).TryGetAnnotation(field, out _); + } + + private TypeAnnotations GetAnnotations(TypeDesc type) + { + return _hashtable.GetOrCreateValue(type); + } + + private class TypeAnnotationsHashtable : LockFreeReaderHashtable + { + private readonly ILProvider _ilProvider; + private readonly Logger _logger; + + public TypeAnnotationsHashtable(Logger logger, ILProvider ilProvider) => (_logger, _ilProvider) = (logger, ilProvider); + + private static DynamicallyAccessedMemberTypes GetMemberTypesForDynamicallyAccessedMembersAttribute(MetadataReader reader, CustomAttributeHandleCollection customAttributeHandles) + { + CustomAttributeHandle ca = reader.GetCustomAttributeHandle(customAttributeHandles, "System.Diagnostics.CodeAnalysis", "DynamicallyAccessedMembersAttribute"); + if (ca.IsNil) + return DynamicallyAccessedMemberTypes.None; + + BlobReader blobReader = reader.GetBlobReader(reader.GetCustomAttribute(ca).Value); + Debug.Assert(blobReader.Length == 8); + if (blobReader.Length != 8) + return DynamicallyAccessedMemberTypes.None; + + blobReader.ReadUInt16(); // Prolog + return (DynamicallyAccessedMemberTypes)blobReader.ReadUInt32(); + } + + protected override bool CompareKeyToValue(TypeDesc key, TypeAnnotations value) => key == value.Type; + protected override bool CompareValueToValue(TypeAnnotations value1, TypeAnnotations value2) => value1.Type == value2.Type; + protected override int GetKeyHashCode(TypeDesc key) => key.GetHashCode(); + protected override int GetValueHashCode(TypeAnnotations value) => value.Type.GetHashCode(); + + protected override TypeAnnotations CreateValueFromKey(TypeDesc key) + { + // We scan the entire type at this point; the reason for doing that is properties. + // + // We allow annotating properties, but those annotations need to flow onto individual get/set methods + // and backing fields. Without scanning all properties, we can't answer questions about fields/methods. + // And if we're going over all properties, we might as well go over everything else to keep things simple. + + Debug.Assert(key.IsTypeDefinition); + if (key is not EcmaType ecmaType) + return new TypeAnnotations(key, DynamicallyAccessedMemberTypes.None, null, null, null); + + MetadataReader reader = ecmaType.MetadataReader; + + // class, interface, struct can have annotations + TypeDefinition typeDef = reader.GetTypeDefinition(ecmaType.Handle); + DynamicallyAccessedMemberTypes typeAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, typeDef.GetCustomAttributes()); + + try + { + // Also inherit annotation from bases + TypeDesc baseType = key.BaseType; + while (baseType != null) + { + TypeDefinition baseTypeDef = reader.GetTypeDefinition(((EcmaType)baseType.GetTypeDefinition()).Handle); + typeAnnotation |= GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, baseTypeDef.GetCustomAttributes()); + baseType = baseType.BaseType; + } + + // And inherit them from interfaces + foreach (DefType runtimeInterface in key.RuntimeInterfaces) + { + TypeDefinition interfaceTypeDef = reader.GetTypeDefinition(((EcmaType)runtimeInterface.GetTypeDefinition()).Handle); + typeAnnotation |= GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, interfaceTypeDef.GetCustomAttributes()); + } + } + catch (TypeSystemException) + { + // If the class hierarchy is not walkable, just stop collecting the annotations. + } + + var annotatedFields = new ArrayBuilder(); + + // First go over all fields with an explicit annotation + foreach (EcmaField field in ecmaType.GetFields()) + { + FieldDefinition fieldDef = reader.GetFieldDefinition(field.Handle); + DynamicallyAccessedMemberTypes annotation = + GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, fieldDef.GetCustomAttributes()); + if (annotation == DynamicallyAccessedMemberTypes.None) + { + continue; + } + + if (!IsTypeInterestingForDataflow(field.FieldType)) + { + // Already know that there's a non-empty annotation on a field which is not System.Type/String and we're about to ignore it + _logger.LogWarning( + $"Field '{field.GetDisplayName()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to fields of type 'System.Type' or 'System.String'.", + 2097, field, subcategory: MessageSubCategory.TrimAnalysis); + continue; + } + + annotatedFields.Add(new FieldAnnotation(field, annotation)); + } + + var annotatedMethods = new ArrayBuilder(); + + // Next go over all methods with an explicit annotation + foreach (EcmaMethod method in ecmaType.GetMethods()) + { + DynamicallyAccessedMemberTypes[] paramAnnotations = null; + + // We convert indices from metadata space to IL space here. + // IL space assigns index 0 to the `this` parameter on instance methods. + + DynamicallyAccessedMemberTypes methodMemberTypes = + GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, reader.GetMethodDefinition(method.Handle).GetCustomAttributes()); + + MethodSignature signature; + try + { + signature = method.Signature; + } + catch (TypeSystemException) + { + // If we cannot resolve things in the signature, just move along. + continue; + } + + int offset; + if (!signature.IsStatic) + { + offset = 1; + } + else + { + offset = 0; + } + + // If there's an annotation on the method itself and it's one of the special types (System.Type for example) + // treat that annotation as annotating the "this" parameter. + if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) + { + if (IsTypeInterestingForDataflow(method.OwningType) && !signature.IsStatic) + { + paramAnnotations = new DynamicallyAccessedMemberTypes[signature.Length + offset]; + paramAnnotations[0] = methodMemberTypes; + } + else + { + _logger.LogWarning( + $"The 'DynamicallyAccessedMembersAttribute' is not allowed on methods. It is allowed on method return value or method parameters though.", + 2041, method, subcategory: MessageSubCategory.TrimAnalysis); + } + } + + MethodDefinition methodDef = reader.GetMethodDefinition(method.Handle); + ParameterHandleCollection parameterHandles = methodDef.GetParameters(); + + DynamicallyAccessedMemberTypes returnAnnotation = DynamicallyAccessedMemberTypes.None; + + foreach (ParameterHandle parameterHandle in parameterHandles) + { + Parameter parameter = reader.GetParameter(parameterHandle); + + if (parameter.SequenceNumber == 0) + { + // this is the return parameter + returnAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, parameter.GetCustomAttributes()); + if (returnAnnotation != DynamicallyAccessedMemberTypes.None && !IsTypeInterestingForDataflow(signature.ReturnType)) + { + _logger.LogWarning( + $"Return type of method '{method.GetDisplayName()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to parameters of type 'System.Type' or 'System.String'.", + 2106, method, subcategory: MessageSubCategory.TrimAnalysis); + } + } + else + { + DynamicallyAccessedMemberTypes pa = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, parameter.GetCustomAttributes()); + if (pa == DynamicallyAccessedMemberTypes.None) + continue; + + if (!IsTypeInterestingForDataflow(signature[parameter.SequenceNumber - 1])) + { + _logger.LogWarning( + $"Parameter #{parameter.SequenceNumber} of method '{method.GetDisplayName()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to parameters of type 'System.Type' or 'System.String'.", + 2098, method, subcategory: MessageSubCategory.TrimAnalysis); + continue; + } + + if (paramAnnotations == null) + { + paramAnnotations = new DynamicallyAccessedMemberTypes[signature.Length + offset]; + } + paramAnnotations[parameter.SequenceNumber - 1 + offset] = pa; + } + } + + DynamicallyAccessedMemberTypes[] genericParameterAnnotations = null; + foreach (EcmaGenericParameter genericParameter in method.Instantiation) + { + GenericParameter genericParameterDef = reader.GetGenericParameter(genericParameter.Handle); + var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, genericParameterDef.GetCustomAttributes()); + if (annotation != DynamicallyAccessedMemberTypes.None) + { + if (genericParameterAnnotations == null) + genericParameterAnnotations = new DynamicallyAccessedMemberTypes[method.Instantiation.Length]; + genericParameterAnnotations[genericParameter.Index] = annotation; + } + } + + if (returnAnnotation != DynamicallyAccessedMemberTypes.None || paramAnnotations != null || genericParameterAnnotations != null) + { + annotatedMethods.Add(new MethodAnnotations(method, paramAnnotations, returnAnnotation, genericParameterAnnotations)); + } + } + + // Next up are properties. Annotations on properties are kind of meta because we need to + // map them to annotations on methods/fields. They're syntactic sugar - what they do is expressible + // by placing attribute on the accessor/backing field. For complex properties, that's what people + // will need to do anyway. Like so: + // + // [field: Attribute] + // Type MyProperty + // { + // [return: Attribute] + // get; + // [value: Attribute] + // set; + // } + + foreach (PropertyDefinitionHandle propertyHandle in reader.GetTypeDefinition(ecmaType.Handle).GetProperties()) + { + DynamicallyAccessedMemberTypes annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute( + reader, reader.GetPropertyDefinition(propertyHandle).GetCustomAttributes()); + if (annotation == DynamicallyAccessedMemberTypes.None) + continue; + + PropertyPseudoDesc property = new PropertyPseudoDesc(ecmaType, propertyHandle); + + if (!IsTypeInterestingForDataflow(property.Signature.ReturnType)) + { + _logger.LogWarning( + $"Property '{property.GetDisplayName()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to properties of type 'System.Type' or 'System.String'.", + 2099, property, subcategory: MessageSubCategory.TrimAnalysis); + continue; + } + + FieldDesc backingFieldFromSetter = null; + + // Propagate the annotation to the setter method + MethodDesc setMethod = property.SetMethod; + if (setMethod != null) + { + + // Abstract property backing field propagation doesn't make sense, and any derived property will be validated + // to have the exact same annotations on getter/setter, and thus if it has a detectable backing field that will be validated as well. + MethodIL methodBody = _ilProvider.GetMethodIL(setMethod); + if (methodBody != null) + { + // Look for the compiler generated backing field. If it doesn't work out simply move on. In such case we would still + // propagate the annotation to the setter/getter and later on when analyzing the setter/getter we will warn + // that the field (which ever it is) must be annotated as well. + ScanMethodBodyForFieldAccess(methodBody, write: true, out backingFieldFromSetter); + } + + if (annotatedMethods.Any(a => a.Method == setMethod)) + { + + _logger.LogWarning( + $"'DynamicallyAccessedMembersAttribute' on property '{property.GetDisplayName()}' conflicts with the same attribute on its accessor '{setMethod.GetDisplayName()}'.", + 2043, setMethod, subcategory: MessageSubCategory.TrimAnalysis); + } + else + { + int offset = setMethod.Signature.IsStatic ? 0 : 1; + if (setMethod.Signature.Length > 0) + { + DynamicallyAccessedMemberTypes[] paramAnnotations = new DynamicallyAccessedMemberTypes[setMethod.Signature.Length + offset]; + paramAnnotations[paramAnnotations.Length - 1] = annotation; + annotatedMethods.Add(new MethodAnnotations(setMethod, paramAnnotations, DynamicallyAccessedMemberTypes.None, null)); + } + } + } + + FieldDesc backingFieldFromGetter = null; + + // Propagate the annotation to the getter method + MethodDesc getMethod = property.GetMethod; + if (getMethod != null) + { + + // Abstract property backing field propagation doesn't make sense, and any derived property will be validated + // to have the exact same annotations on getter/setter, and thus if it has a detectable backing field that will be validated as well. + MethodIL methodBody = _ilProvider.GetMethodIL(getMethod); + if (methodBody != null) + { + // Look for the compiler generated backing field. If it doesn't work out simply move on. In such case we would still + // propagate the annotation to the setter/getter and later on when analyzing the setter/getter we will warn + // that the field (which ever it is) must be annotated as well. + ScanMethodBodyForFieldAccess(methodBody, write: false, out backingFieldFromGetter); + } + + if (annotatedMethods.Any(a => a.Method == getMethod)) + { + _logger.LogWarning( + $"'DynamicallyAccessedMembersAttribute' on property '{property.GetDisplayName()}' conflicts with the same attribute on its accessor '{getMethod.GetDisplayName()}'.", + 2043, getMethod, subcategory: MessageSubCategory.TrimAnalysis); + } + else + { + annotatedMethods.Add(new MethodAnnotations(getMethod, null, annotation, null)); + } + } + + FieldDesc backingField; + if (backingFieldFromGetter != null && backingFieldFromSetter != null && + backingFieldFromGetter != backingFieldFromSetter) + { + _logger.LogWarning( + $"Could not find a unique backing field for property '{property.GetDisplayName()}' to propagate 'DynamicallyAccessedMembersAttribute'.", + 2042, property, subcategory: MessageSubCategory.TrimAnalysis); + backingField = null; + } + else + { + backingField = backingFieldFromGetter ?? backingFieldFromSetter; + } + + if (backingField != null) + { + if (annotatedFields.Any(a => a.Field == backingField)) + { + _logger.LogWarning( + $"'DynamicallyAccessedMemberAttribute' on property '{property.GetDisplayName()}' conflicts with the same attribute on its backing field '{backingField.GetDisplayName()}'.", + 2056, backingField, subcategory: MessageSubCategory.TrimAnalysis); + } + else + { + annotatedFields.Add(new FieldAnnotation(backingField, annotation)); + } + } + } + + DynamicallyAccessedMemberTypes[] typeGenericParameterAnnotations = null; + foreach (EcmaGenericParameter genericParameter in ecmaType.Instantiation) + { + GenericParameter genericParameterDef = reader.GetGenericParameter(genericParameter.Handle); + + var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, genericParameterDef.GetCustomAttributes()); + if (annotation != DynamicallyAccessedMemberTypes.None) + { + if (typeGenericParameterAnnotations == null) + typeGenericParameterAnnotations = new DynamicallyAccessedMemberTypes[ecmaType.Instantiation.Length]; + typeGenericParameterAnnotations[genericParameter.Index] = annotation; + } + } + + return new TypeAnnotations(ecmaType, typeAnnotation, annotatedMethods.ToArray(), annotatedFields.ToArray(), typeGenericParameterAnnotations); + } + + private static bool ScanMethodBodyForFieldAccess(MethodIL body, bool write, out FieldDesc found) + { + // Tries to find the backing field for a property getter/setter. + // Returns true if this is a method body that we can unambiguously analyze. + // The found field could still be null if there's no backing store. + found = null; + + ILReader ilReader = new ILReader(body.GetILBytes()); + + while (ilReader.HasNext) + { + ILOpcode opcode = ilReader.ReadILOpcode(); + switch (opcode) + { + case ILOpcode.ldsfld when !write: + case ILOpcode.ldfld when !write: + case ILOpcode.stsfld when write: + case ILOpcode.stfld when write: + { + // This writes/reads multiple fields - can't guess which one is the backing store. + // Return failure. + if (found != null) + { + found = null; + return false; + } + found = (FieldDesc)body.GetObject(ilReader.ReadILToken()); + } + break; + default: + ilReader.Skip(opcode); + break; + } + } + + if (found == null) + { + // Doesn't access any fields. Could be e.g. "Type Foo => typeof(Bar);" + // Return success. + return true; + } + + if (found.OwningType != body.OwningMethod.OwningType || + found.IsStatic != body.OwningMethod.Signature.IsStatic || + !found.HasCustomAttribute("System.Runtime.CompilerServices", "CompilerGeneratedAttribute")) + { + // A couple heuristics to make sure we got the right field. + // Return failure. + found = null; + return false; + } + + return true; + } + + private bool IsTypeInterestingForDataflow(TypeDesc type) + { + // NOTE: this method is not particulary fast. It's assumed that the caller limits + // calls to this method as much as possible. + + if (type.IsWellKnownType(WellKnownType.String)) + return true; + + if (!type.IsDefType) + return false; + + var metadataType = (MetadataType)type; + + foreach (var intf in type.RuntimeInterfaces) + { + if (intf.Name == "IReflect" && intf.Namespace == "System.Reflection") + return true; + } + + if (metadataType.Name == "IReflect" && metadataType.Namespace == "System.Reflection") + return true; + + do + { + if (metadataType.Name == "Type" && metadataType.Namespace == "System") + return true; + } while ((metadataType = metadataType.MetadataBaseType) != null); + + return false; + } + } + + internal void ValidateMethodAnnotationsAreSame(MethodDesc method, MethodDesc baseMethod) + { + method = method.GetTypicalMethodDefinition(); + baseMethod = baseMethod.GetTypicalMethodDefinition(); + + GetAnnotations(method.OwningType).TryGetAnnotation(method, out var methodAnnotations); + GetAnnotations(baseMethod.OwningType).TryGetAnnotation(baseMethod, out var baseMethodAnnotations); + + if (methodAnnotations.ReturnParameterAnnotation != baseMethodAnnotations.ReturnParameterAnnotation) + LogValidationWarning(method.Signature.ReturnType, baseMethod, method); + + if (methodAnnotations.ParameterAnnotations != null || baseMethodAnnotations.ParameterAnnotations != null) + { + if (methodAnnotations.ParameterAnnotations == null) + ValidateMethodParametersHaveNoAnnotations(baseMethodAnnotations.ParameterAnnotations, method, baseMethod, method); + else if (baseMethodAnnotations.ParameterAnnotations == null) + ValidateMethodParametersHaveNoAnnotations(methodAnnotations.ParameterAnnotations, method, baseMethod, method); + else + { + if (methodAnnotations.ParameterAnnotations.Length != baseMethodAnnotations.ParameterAnnotations.Length) + return; + + for (int parameterIndex = 0; parameterIndex < methodAnnotations.ParameterAnnotations.Length; parameterIndex++) + { + if (methodAnnotations.ParameterAnnotations[parameterIndex] != baseMethodAnnotations.ParameterAnnotations[parameterIndex]) + LogValidationWarning( + DiagnosticUtilities.GetMethodParameterFromIndex(method, parameterIndex), + DiagnosticUtilities.GetMethodParameterFromIndex(baseMethod, parameterIndex), + method); + } + } + } + + if (methodAnnotations.GenericParameterAnnotations != null || baseMethodAnnotations.GenericParameterAnnotations != null) + { + if (methodAnnotations.GenericParameterAnnotations == null) + ValidateMethodGenericParametersHaveNoAnnotations(baseMethodAnnotations.GenericParameterAnnotations, method, baseMethod, method); + else if (baseMethodAnnotations.GenericParameterAnnotations == null) + ValidateMethodGenericParametersHaveNoAnnotations(methodAnnotations.GenericParameterAnnotations, method, baseMethod, method); + else + { + if (methodAnnotations.GenericParameterAnnotations.Length != baseMethodAnnotations.GenericParameterAnnotations.Length) + return; + + for (int genericParameterIndex = 0; genericParameterIndex < methodAnnotations.GenericParameterAnnotations.Length; genericParameterIndex++) + { + if (methodAnnotations.GenericParameterAnnotations[genericParameterIndex] != baseMethodAnnotations.GenericParameterAnnotations[genericParameterIndex]) + { + LogValidationWarning( + method.Instantiation[genericParameterIndex], + baseMethod.Instantiation[genericParameterIndex], + method); + } + } + } + } + } + + void ValidateMethodParametersHaveNoAnnotations(DynamicallyAccessedMemberTypes[] parameterAnnotations, MethodDesc method, MethodDesc baseMethod, MethodDesc origin) + { + for (int parameterIndex = 0; parameterIndex < parameterAnnotations.Length; parameterIndex++) + { + var annotation = parameterAnnotations[parameterIndex]; + if (annotation != DynamicallyAccessedMemberTypes.None) + LogValidationWarning( + parameterIndex, + baseMethod, + origin); + } + } + + void ValidateMethodGenericParametersHaveNoAnnotations(DynamicallyAccessedMemberTypes[] genericParameterAnnotations, MethodDesc method, MethodDesc baseMethod, MethodDesc origin) + { + for (int genericParameterIndex = 0; genericParameterIndex < genericParameterAnnotations.Length; genericParameterIndex++) + { + if (genericParameterAnnotations[genericParameterIndex] != DynamicallyAccessedMemberTypes.None) + { + LogValidationWarning( + method.Instantiation[genericParameterIndex], + baseMethod.Instantiation[genericParameterIndex], + origin); + } + } + } + + void LogValidationWarning(object provider, object baseProvider, MethodDesc origin) + { + switch (provider) + { + case int parameterNumber: + _logger.LogWarning( + $"'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the parameter #{parameterNumber} of method '{DiagnosticUtilities.GetMethodSignatureDisplayName(origin)}' " + + $"don't match overridden parameter #{parameterNumber} of method '{DiagnosticUtilities.GetMethodSignatureDisplayName((MethodDesc)baseProvider)}'. " + + $"All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.", + 2092, origin, subcategory: MessageSubCategory.TrimAnalysis); + break; + case GenericParameterDesc genericParameterOverride: + _logger.LogWarning( + $"'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the generic parameter '{genericParameterOverride.Name}' of '{DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(new GenericParameterOrigin(genericParameterOverride))}' " + + $"don't match overridden generic parameter '{((GenericParameterDesc)baseProvider).Name}' of '{DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(new GenericParameterOrigin((GenericParameterDesc)baseProvider))}'. " + + $"All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.", + 2095, origin, subcategory: MessageSubCategory.TrimAnalysis); + break; + case TypeDesc methodReturnType: + _logger.LogWarning( + $"'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the return value of method '{DiagnosticUtilities.GetMethodSignatureDisplayName(origin)}' " + + $"don't match overridden return value of method '{DiagnosticUtilities.GetMethodSignatureDisplayName((MethodDesc)baseProvider)}'. " + + $"All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.", + 2093, origin, subcategory: MessageSubCategory.TrimAnalysis); + break; + // No fields - it's not possible to have a virtual field and override it + case MethodDesc methodDefinition: + _logger.LogWarning( + $"'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the implicit 'this' parameter of method '{DiagnosticUtilities.GetMethodSignatureDisplayName(methodDefinition)}' " + + $"don't match overridden implicit 'this' parameter of method '{DiagnosticUtilities.GetMethodSignatureDisplayName((MethodDesc)baseProvider)}'. " + + $"All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.", + 2094, origin, subcategory: MessageSubCategory.TrimAnalysis); + break; + default: + throw new NotImplementedException($"Unsupported provider type {provider.GetType()}"); + } + } + + private class TypeAnnotations + { + public readonly TypeDesc Type; + public readonly DynamicallyAccessedMemberTypes TypeAnnotation; + private readonly MethodAnnotations[] _annotatedMethods; + private readonly FieldAnnotation[] _annotatedFields; + private readonly DynamicallyAccessedMemberTypes[] _genericParameterAnnotations; + + public bool IsDefault => _annotatedMethods == null && _annotatedFields == null && _genericParameterAnnotations == null; + + public TypeAnnotations( + TypeDesc type, + DynamicallyAccessedMemberTypes typeAnnotations, + MethodAnnotations[] annotatedMethods, + FieldAnnotation[] annotatedFields, + DynamicallyAccessedMemberTypes[] genericParameterAnnotations) + => (Type, TypeAnnotation, _annotatedMethods, _annotatedFields, _genericParameterAnnotations) + = (type, typeAnnotations, annotatedMethods, annotatedFields, genericParameterAnnotations); + + public bool TryGetAnnotation(MethodDesc method, out MethodAnnotations annotations) + { + annotations = default; + + if (_annotatedMethods == null) + { + return false; + } + + foreach (var m in _annotatedMethods) + { + if (m.Method == method) + { + annotations = m; + return true; + } + } + + return false; + } + + public bool TryGetAnnotation(FieldDesc field, out FieldAnnotation annotation) + { + annotation = default; + + if (_annotatedFields == null) + { + return false; + } + + foreach (var f in _annotatedFields) + { + if (f.Field == field) + { + annotation = f; + return true; + } + } + + return false; + } + + public bool TryGetAnnotation(GenericParameterDesc genericParameter, out DynamicallyAccessedMemberTypes annotation) + { + annotation = default; + + if (_genericParameterAnnotations == null) + return false; + + for (int genericParameterIndex = 0; genericParameterIndex < _genericParameterAnnotations.Length; genericParameterIndex++) + { + if (Type.Instantiation[genericParameterIndex] == genericParameter) + { + annotation = _genericParameterAnnotations[genericParameterIndex]; + return true; + } + } + + return false; + } + } + + private readonly struct MethodAnnotations + { + public readonly MethodDesc Method; + public readonly DynamicallyAccessedMemberTypes[] ParameterAnnotations; + public readonly DynamicallyAccessedMemberTypes ReturnParameterAnnotation; + public readonly DynamicallyAccessedMemberTypes[] GenericParameterAnnotations; + + public MethodAnnotations( + MethodDesc method, + DynamicallyAccessedMemberTypes[] paramAnnotations, + DynamicallyAccessedMemberTypes returnParamAnnotations, + DynamicallyAccessedMemberTypes[] genericParameterAnnotations) + => (Method, ParameterAnnotations, ReturnParameterAnnotation, GenericParameterAnnotations) = + (method, paramAnnotations, returnParamAnnotations, genericParameterAnnotations); + + public bool TryGetAnnotation(GenericParameterDesc genericParameter, out DynamicallyAccessedMemberTypes annotation) + { + annotation = default; + + if (GenericParameterAnnotations == null) + return false; + + for (int genericParameterIndex = 0; genericParameterIndex < GenericParameterAnnotations.Length; genericParameterIndex++) + { + if (Method.Instantiation[genericParameterIndex] == genericParameter) + { + annotation = GenericParameterAnnotations[genericParameterIndex]; + return true; + } + } + + return false; + } + } + + private readonly struct FieldAnnotation + { + public readonly FieldDesc Field; + public readonly DynamicallyAccessedMemberTypes Annotation; + + public FieldAnnotation(FieldDesc field, DynamicallyAccessedMemberTypes annotation) + => (Field, Annotation) = (field, annotation); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs new file mode 100644 index 00000000000000..f07166e28d80c9 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs @@ -0,0 +1,1075 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; + +using Internal.IL; +using Internal.TypeSystem; + +namespace ILCompiler.Dataflow +{ + /// + /// Tracks information about the contents of a stack slot + /// + readonly struct StackSlot + { + public ValueNode Value { get; } + + /// + /// True if the value is on the stack as a byref + /// + public bool IsByRef { get; } + + public StackSlot(ValueNode value, bool isByRef = false) + { + Value = value; + IsByRef = isByRef; + } + } + + abstract partial class MethodBodyScanner + { + internal ValueNode MethodReturnValue { private set; get; } + + protected virtual void WarnAboutInvalidILInMethod(MethodIL method, int ilOffset) + { + } + + private void CheckForInvalidStack(Stack stack, int depthRequired, MethodIL method, int ilOffset) + { + if (stack.Count < depthRequired) + { + WarnAboutInvalidILInMethod(method, ilOffset); + while (stack.Count < depthRequired) + stack.Push(new StackSlot()); // Push dummy values to avoid crashes. + // Analysis of this method will be incorrect. + } + } + + private static void PushUnknown(Stack stack) + { + stack.Push(new StackSlot()); + } + + private void PushUnknownAndWarnAboutInvalidIL(Stack stack, MethodIL methodBody, int offset) + { + WarnAboutInvalidILInMethod(methodBody, offset); + PushUnknown(stack); + } + + private StackSlot PopUnknown(Stack stack, int count, MethodIL method, int ilOffset) + { + if (count < 1) + throw new InvalidOperationException(); + + StackSlot topOfStack = default; + CheckForInvalidStack(stack, count, method, ilOffset); + + for (int i = 0; i < count; ++i) + { + StackSlot slot = stack.Pop(); + if (i == 0) + topOfStack = slot; + } + return topOfStack; + } + + private static StackSlot MergeStackElement(StackSlot a, StackSlot b) + { + StackSlot mergedSlot; + if (b.Value == null) + { + mergedSlot = a; + } + else if (a.Value == null) + { + mergedSlot = b; + } + else + { + mergedSlot = new StackSlot(MergePointValue.MergeValues(a.Value, b.Value)); + } + + return mergedSlot; + } + + // Merge stacks together. This may return the first stack, the stack length must be the same for the two stacks. + private static Stack MergeStack(Stack a, Stack b) + { + if (a.Count != b.Count) + { + // Force stacks to be of equal size to avoid crashes. + // Analysis of this method will be incorrect. + while (a.Count < b.Count) + a.Push(new StackSlot()); + + while (b.Count < a.Count) + b.Push(new StackSlot()); + } + + Stack newStack = new Stack(a.Count); + IEnumerator aEnum = a.GetEnumerator(); + IEnumerator bEnum = b.GetEnumerator(); + while (aEnum.MoveNext() && bEnum.MoveNext()) + { + newStack.Push(MergeStackElement(aEnum.Current, bEnum.Current)); + } + + // The new stack is reversed. Use the copy constructor to reverse it back + return new Stack(newStack); + } + + private static void ClearStack(ref Stack stack) + { + stack = null; + } + + private static void NewKnownStack(Dictionary> knownStacks, int newOffset, Stack newStack) + { + // No need to merge in empty stacks + if (newStack.Count == 0) + { + return; + } + + if (knownStacks.ContainsKey(newOffset)) + { + knownStacks[newOffset] = MergeStack(knownStacks[newOffset], newStack); + } + else + { + knownStacks.Add(newOffset, new Stack(newStack.Reverse())); + } + } + + private struct BasicBlockIterator + { + readonly HashSet _methodBranchTargets; + int _currentBlockIndex; + bool _foundEndOfPrevBlock; + MethodIL _methodBody; + + public BasicBlockIterator(MethodIL methodBody) + { + _methodBranchTargets = methodBody.ComputeBranchTargets(); + _currentBlockIndex = -1; + _foundEndOfPrevBlock = true; + _methodBody = methodBody; + } + + public int CurrentBlockIndex + { + get + { + return _currentBlockIndex; + } + } + + public int MoveNext(int offset) + { + if (_foundEndOfPrevBlock || _methodBranchTargets.Contains(offset)) + { + _currentBlockIndex++; + _foundEndOfPrevBlock = false; + } + + var reader = new ILReader(_methodBody.GetILBytes()); + reader.Seek(offset); + ILOpcode opcode = reader.ReadILOpcode(); + if (opcode.IsControlFlowInstruction()) + { + _foundEndOfPrevBlock = true; + } + + return CurrentBlockIndex; + } + } + + private static void StoreMethodLocalValue( + ValueBasicBlockPair[] valueCollection, + ValueNode valueToStore, + int index, + int curBasicBlock) + { + ValueBasicBlockPair newValue = new ValueBasicBlockPair { BasicBlockIndex = curBasicBlock }; + + ValueBasicBlockPair existingValue = valueCollection[index]; + if (existingValue.Value != null + && existingValue.BasicBlockIndex == curBasicBlock) + { + // If the previous value was stored in the current basic block, then we can safely + // overwrite the previous value with the new one. + newValue.Value = valueToStore; + } + else + { + // If the previous value came from a previous basic block, then some other use of + // the local could see the previous value, so we must merge the new value with the + // old value. + newValue.Value = MergePointValue.MergeValues(existingValue.Value, valueToStore); + } + valueCollection[index] = newValue; + } + + private static void StoreMethodLocalValue( + Dictionary valueCollection, + ValueNode valueToStore, + KeyType collectionKey, + int curBasicBlock, + int? maxTrackedValues = null) + { + ValueBasicBlockPair newValue = new ValueBasicBlockPair { BasicBlockIndex = curBasicBlock }; + + ValueBasicBlockPair existingValue; + if (valueCollection.TryGetValue(collectionKey, out existingValue)) + { + if (existingValue.BasicBlockIndex == curBasicBlock) + { + // If the previous value was stored in the current basic block, then we can safely + // overwrite the previous value with the new one. + newValue.Value = valueToStore; + } + else + { + // If the previous value came from a previous basic block, then some other use of + // the local could see the previous value, so we must merge the new value with the + // old value. + newValue.Value = MergePointValue.MergeValues(existingValue.Value, valueToStore); + } + valueCollection[collectionKey] = newValue; + } + else if (maxTrackedValues == null || valueCollection.Count < maxTrackedValues) + { + // We're not currently tracking a value a this index, so store the value now. + newValue.Value = valueToStore; + valueCollection[collectionKey] = newValue; + } + } + + public void Scan(MethodIL methodBody) + { + MethodDesc thisMethod = methodBody.OwningMethod; + + ValueBasicBlockPair[] locals = new ValueBasicBlockPair[methodBody.GetLocals().Length]; + + Dictionary> knownStacks = new Dictionary>(); + Stack currentStack = new Stack(methodBody.MaxStack); + + ScanExceptionInformation(knownStacks, methodBody); + + BasicBlockIterator blockIterator = new BasicBlockIterator(methodBody); + + MethodReturnValue = null; + ILReader reader = new ILReader(methodBody.GetILBytes()); + while (reader.HasNext) + { + int curBasicBlock = blockIterator.MoveNext(reader.Offset); + + if (knownStacks.ContainsKey(reader.Offset)) + { + if (currentStack == null) + { + // The stack copy constructor reverses the stack + currentStack = new Stack(knownStacks[reader.Offset].Reverse()); + } + else + { + currentStack = MergeStack(currentStack, knownStacks[reader.Offset]); + } + } + + if (currentStack == null) + { + currentStack = new Stack(methodBody.MaxStack); + } + + int offset = reader.Offset; + ILOpcode opcode = reader.ReadILOpcode(); + + switch (opcode) + { + case ILOpcode.add: + case ILOpcode.add_ovf: + case ILOpcode.add_ovf_un: + case ILOpcode.and: + case ILOpcode.div: + case ILOpcode.div_un: + case ILOpcode.mul: + case ILOpcode.mul_ovf: + case ILOpcode.mul_ovf_un: + case ILOpcode.or: + case ILOpcode.rem: + case ILOpcode.rem_un: + case ILOpcode.sub: + case ILOpcode.sub_ovf: + case ILOpcode.sub_ovf_un: + case ILOpcode.xor: + case ILOpcode.cgt: + case ILOpcode.cgt_un: + case ILOpcode.clt: + case ILOpcode.clt_un: + case ILOpcode.shl: + case ILOpcode.shr: + case ILOpcode.shr_un: + case ILOpcode.ceq: + PopUnknown(currentStack, 2, methodBody, offset); + PushUnknown(currentStack); + reader.Skip(opcode); + break; + + case ILOpcode.dup: + currentStack.Push(currentStack.Peek()); + break; + + case ILOpcode.ldnull: + currentStack.Push(new StackSlot(NullValue.Instance)); + break; + + + case ILOpcode.ldc_i4_0: + case ILOpcode.ldc_i4_1: + case ILOpcode.ldc_i4_2: + case ILOpcode.ldc_i4_3: + case ILOpcode.ldc_i4_4: + case ILOpcode.ldc_i4_5: + case ILOpcode.ldc_i4_6: + case ILOpcode.ldc_i4_7: + case ILOpcode.ldc_i4_8: + { + int value = opcode - ILOpcode.ldc_i4_0; + ConstIntValue civ = new ConstIntValue(value); + StackSlot slot = new StackSlot(civ); + currentStack.Push(slot); + } + break; + + case ILOpcode.ldc_i4_m1: + { + ConstIntValue civ = new ConstIntValue(-1); + StackSlot slot = new StackSlot(civ); + currentStack.Push(slot); + } + break; + + case ILOpcode.ldc_i4: + { + int value = (int)reader.ReadILUInt32(); + ConstIntValue civ = new ConstIntValue(value); + StackSlot slot = new StackSlot(civ); + currentStack.Push(slot); + } + break; + + case ILOpcode.ldc_i4_s: + { + int value = (sbyte)reader.ReadILByte(); + ConstIntValue civ = new ConstIntValue(value); + StackSlot slot = new StackSlot(civ); + currentStack.Push(slot); + } + break; + + case ILOpcode.arglist: + case ILOpcode.ldftn: + case ILOpcode.sizeof_: + case ILOpcode.ldc_i8: + case ILOpcode.ldc_r4: + case ILOpcode.ldc_r8: + PushUnknown(currentStack); + reader.Skip(opcode); + break; + + case ILOpcode.ldarg: + case ILOpcode.ldarg_0: + case ILOpcode.ldarg_1: + case ILOpcode.ldarg_2: + case ILOpcode.ldarg_3: + case ILOpcode.ldarg_s: + case ILOpcode.ldarga: + case ILOpcode.ldarga_s: + ScanLdarg(opcode, opcode switch + { + ILOpcode.ldarg => reader.ReadILUInt16(), + ILOpcode.ldarga => reader.ReadILUInt16(), + ILOpcode.ldarg_s => reader.ReadILByte(), + ILOpcode.ldarga_s => reader.ReadILByte(), + _ => opcode - ILOpcode.ldarg_0 + }, currentStack, thisMethod); + break; + + case ILOpcode.ldloc: + case ILOpcode.ldloc_0: + case ILOpcode.ldloc_1: + case ILOpcode.ldloc_2: + case ILOpcode.ldloc_3: + case ILOpcode.ldloc_s: + case ILOpcode.ldloca: + case ILOpcode.ldloca_s: + ScanLdloc(methodBody, offset, opcode, opcode switch + { + ILOpcode.ldloc => reader.ReadILUInt16(), + ILOpcode.ldloca => reader.ReadILUInt16(), + ILOpcode.ldloc_s => reader.ReadILByte(), + ILOpcode.ldloca_s => reader.ReadILByte(), + _ => opcode - ILOpcode.ldloc_0 + }, currentStack, locals); + break; + + case ILOpcode.ldstr: + { + StackSlot slot = new StackSlot(new KnownStringValue((string)methodBody.GetObject(reader.ReadILToken()))); + currentStack.Push(slot); + } + break; + + case ILOpcode.ldtoken: + object obj = methodBody.GetObject(reader.ReadILToken()); + ScanLdtoken(methodBody, obj, currentStack); + break; + + case ILOpcode.ldind_i: + case ILOpcode.ldind_i1: + case ILOpcode.ldind_i2: + case ILOpcode.ldind_i4: + case ILOpcode.ldind_i8: + case ILOpcode.ldind_r4: + case ILOpcode.ldind_r8: + case ILOpcode.ldind_u1: + case ILOpcode.ldind_u2: + case ILOpcode.ldind_u4: + case ILOpcode.ldlen: + case ILOpcode.ldvirtftn: + case ILOpcode.localloc: + case ILOpcode.refanytype: + case ILOpcode.refanyval: + case ILOpcode.conv_i1: + case ILOpcode.conv_i2: + case ILOpcode.conv_i4: + case ILOpcode.conv_ovf_i1: + case ILOpcode.conv_ovf_i1_un: + case ILOpcode.conv_ovf_i2: + case ILOpcode.conv_ovf_i2_un: + case ILOpcode.conv_ovf_i4: + case ILOpcode.conv_ovf_i4_un: + case ILOpcode.conv_ovf_u: + case ILOpcode.conv_ovf_u_un: + case ILOpcode.conv_ovf_u1: + case ILOpcode.conv_ovf_u1_un: + case ILOpcode.conv_ovf_u2: + case ILOpcode.conv_ovf_u2_un: + case ILOpcode.conv_ovf_u4: + case ILOpcode.conv_ovf_u4_un: + case ILOpcode.conv_u1: + case ILOpcode.conv_u2: + case ILOpcode.conv_u4: + case ILOpcode.conv_i8: + case ILOpcode.conv_ovf_i8: + case ILOpcode.conv_ovf_i8_un: + case ILOpcode.conv_ovf_u8: + case ILOpcode.conv_ovf_u8_un: + case ILOpcode.conv_u8: + case ILOpcode.conv_i: + case ILOpcode.conv_ovf_i: + case ILOpcode.conv_ovf_i_un: + case ILOpcode.conv_u: + case ILOpcode.conv_r_un: + case ILOpcode.conv_r4: + case ILOpcode.conv_r8: + case ILOpcode.ldind_ref: + case ILOpcode.ldobj: + case ILOpcode.mkrefany: + case ILOpcode.unbox: + case ILOpcode.unbox_any: + case ILOpcode.box: + case ILOpcode.neg: + case ILOpcode.not: + PopUnknown(currentStack, 1, methodBody, offset); + PushUnknown(currentStack); + reader.Skip(opcode); + break; + + case ILOpcode.isinst: + case ILOpcode.castclass: + // We can consider a NOP because the value doesn't change. + // It might change to NULL, but for the purposes of dataflow analysis + // it doesn't hurt much. + reader.Skip(opcode); + break; + + case ILOpcode.ldfld: + case ILOpcode.ldsfld: + case ILOpcode.ldflda: + case ILOpcode.ldsflda: + ScanLdfld(methodBody, offset, opcode, (FieldDesc)methodBody.GetObject(reader.ReadILToken()), currentStack); + break; + + case ILOpcode.newarr: + { + StackSlot count = PopUnknown(currentStack, 1, methodBody, offset); + var arrayElement = (TypeDesc)methodBody.GetObject(reader.ReadILToken()); + currentStack.Push(new StackSlot(new ArrayValue(count.Value, arrayElement))); + } + break; + + case ILOpcode.stelem_i: + case ILOpcode.stelem_i1: + case ILOpcode.stelem_i2: + case ILOpcode.stelem_i4: + case ILOpcode.stelem_i8: + case ILOpcode.stelem_r4: + case ILOpcode.stelem_r8: + case ILOpcode.stelem: + case ILOpcode.stelem_ref: + ScanStelem(offset, currentStack, methodBody, curBasicBlock); + reader.Skip(opcode); + break; + + case ILOpcode.ldelem_i: + case ILOpcode.ldelem_i1: + case ILOpcode.ldelem_i2: + case ILOpcode.ldelem_i4: + case ILOpcode.ldelem_i8: + case ILOpcode.ldelem_r4: + case ILOpcode.ldelem_r8: + case ILOpcode.ldelem_u1: + case ILOpcode.ldelem_u2: + case ILOpcode.ldelem_u4: + case ILOpcode.ldelem: + case ILOpcode.ldelem_ref: + case ILOpcode.ldelema: + ScanLdelem(opcode, offset, currentStack, methodBody, curBasicBlock); + reader.Skip(opcode); + break; + + case ILOpcode.cpblk: + case ILOpcode.initblk: + PopUnknown(currentStack, 3, methodBody, offset); + reader.Skip(opcode); + break; + + case ILOpcode.stfld: + case ILOpcode.stsfld: + ScanStfld(methodBody, offset, opcode, (FieldDesc)methodBody.GetObject(reader.ReadILToken()), currentStack); + break; + + case ILOpcode.cpobj: + PopUnknown(currentStack, 2, methodBody, offset); + reader.Skip(opcode); + break; + + case ILOpcode.stind_i: + case ILOpcode.stind_i1: + case ILOpcode.stind_i2: + case ILOpcode.stind_i4: + case ILOpcode.stind_i8: + case ILOpcode.stind_r4: + case ILOpcode.stind_r8: + case ILOpcode.stind_ref: + case ILOpcode.stobj: + ScanIndirectStore(methodBody, offset, currentStack); + reader.Skip(opcode); + break; + + case ILOpcode.initobj: + case ILOpcode.pop: + PopUnknown(currentStack, 1, methodBody, offset); + reader.Skip(opcode); + break; + + case ILOpcode.starg: + case ILOpcode.starg_s: + ScanStarg(methodBody, offset, opcode == ILOpcode.starg ? reader.ReadILUInt16() : reader.ReadILByte(), currentStack); + break; + + case ILOpcode.stloc: + case ILOpcode.stloc_s: + case ILOpcode.stloc_0: + case ILOpcode.stloc_1: + case ILOpcode.stloc_2: + case ILOpcode.stloc_3: + ScanStloc(methodBody, offset, opcode switch { + ILOpcode.stloc => reader.ReadILUInt16(), + ILOpcode.stloc_s => reader.ReadILByte(), + _ => opcode - ILOpcode.stloc_0, + }, currentStack, locals, curBasicBlock); + break; + + case ILOpcode.constrained: + case ILOpcode.no: + case ILOpcode.readonly_: + case ILOpcode.tail: + case ILOpcode.unaligned: + case ILOpcode.volatile_: + reader.Skip(opcode); + break; + + case ILOpcode.brfalse: + case ILOpcode.brfalse_s: + case ILOpcode.brtrue: + case ILOpcode.brtrue_s: + PopUnknown(currentStack, 1, methodBody, offset); + NewKnownStack(knownStacks, reader.ReadBranchDestination(opcode), currentStack); + break; + + case ILOpcode.calli: + { + var signature = (MethodSignature)methodBody.GetObject(reader.ReadILToken()); + if (!signature.IsStatic) + { + PopUnknown(currentStack, 1, methodBody, offset); + } + + // Pop arguments + if (signature.Length > 0) + PopUnknown(currentStack, signature.Length, methodBody, offset); + + // Pop function pointer + PopUnknown(currentStack, 1, methodBody, offset); + + // Push return value + if (!signature.ReturnType.IsVoid) + PushUnknown(currentStack); + } + break; + + case ILOpcode.call: + case ILOpcode.callvirt: + case ILOpcode.newobj: + HandleCall(methodBody, opcode, offset, (MethodDesc)methodBody.GetObject(reader.ReadILToken()), currentStack, curBasicBlock); + break; + + case ILOpcode.jmp: + // Not generated by mainstream compilers + reader.Skip(opcode); + break; + + case ILOpcode.br: + case ILOpcode.br_s: + NewKnownStack(knownStacks, reader.ReadBranchDestination(opcode), currentStack); + ClearStack(ref currentStack); + break; + + case ILOpcode.leave: + case ILOpcode.leave_s: + ClearStack(ref currentStack); + NewKnownStack(knownStacks, reader.ReadBranchDestination(opcode), new Stack(methodBody.MaxStack)); + break; + + case ILOpcode.endfilter: + case ILOpcode.endfinally: + case ILOpcode.rethrow: + case ILOpcode.throw_: + ClearStack(ref currentStack); + break; + + case ILOpcode.ret: + { + bool hasReturnValue = !methodBody.OwningMethod.Signature.ReturnType.IsVoid; + if (currentStack.Count != (hasReturnValue ? 1 : 0)) + { + WarnAboutInvalidILInMethod(methodBody, offset); + } + if (hasReturnValue) + { + StackSlot retValue = PopUnknown(currentStack, 1, methodBody, offset); + MethodReturnValue = MergePointValue.MergeValues(MethodReturnValue, retValue.Value); + } + ClearStack(ref currentStack); + break; + } + + case ILOpcode.switch_: + { + PopUnknown(currentStack, 1, methodBody, offset); + + uint count = reader.ReadILUInt32(); + int jmpBase = reader.Offset + (int)(4 * count); + for (uint i = 0; i < count; i++) + { + NewKnownStack(knownStacks, (int)reader.ReadILUInt32() + jmpBase, currentStack); + } + break; + } + + case ILOpcode.beq: + case ILOpcode.beq_s: + case ILOpcode.bne_un: + case ILOpcode.bne_un_s: + case ILOpcode.bge: + case ILOpcode.bge_s: + case ILOpcode.bge_un: + case ILOpcode.bge_un_s: + case ILOpcode.bgt: + case ILOpcode.bgt_s: + case ILOpcode.bgt_un: + case ILOpcode.bgt_un_s: + case ILOpcode.ble: + case ILOpcode.ble_s: + case ILOpcode.ble_un: + case ILOpcode.ble_un_s: + case ILOpcode.blt: + case ILOpcode.blt_s: + case ILOpcode.blt_un: + case ILOpcode.blt_un_s: + PopUnknown(currentStack, 2, methodBody, offset); + NewKnownStack(knownStacks, reader.ReadBranchDestination(opcode), currentStack); + break; + default: + reader.Skip(opcode); + break; + } + } + } + + private static void ScanExceptionInformation(Dictionary> knownStacks, MethodIL methodBody) + { + foreach (ILExceptionRegion exceptionClause in methodBody.GetExceptionRegions()) + { + Stack catchStack = new Stack(1); + catchStack.Push(new StackSlot()); + + if (exceptionClause.Kind == ILExceptionRegionKind.Filter) + { + NewKnownStack(knownStacks, exceptionClause.FilterOffset, catchStack); + NewKnownStack(knownStacks, exceptionClause.HandlerOffset, catchStack); + } + if (exceptionClause.Kind == ILExceptionRegionKind.Catch) + { + NewKnownStack(knownStacks, exceptionClause.HandlerOffset, catchStack); + } + } + } + + protected abstract ValueNode GetMethodParameterValue(MethodDesc method, int parameterIndex); + + private void ScanLdarg(ILOpcode opcode, int paramNum, Stack currentStack, MethodDesc thisMethod) + { + bool isByRef; + + if (!thisMethod.Signature.IsStatic && paramNum == 0) + { + isByRef = thisMethod.OwningType.IsValueType; + } + else + { + isByRef = thisMethod.Signature[paramNum - (thisMethod.Signature.IsStatic ? 0 : 1)].IsByRefOrPointer(); + } + + isByRef |= opcode == ILOpcode.ldarga || opcode == ILOpcode.ldarga_s; + + StackSlot slot = new StackSlot(GetMethodParameterValue(thisMethod, paramNum), isByRef); + currentStack.Push(slot); + } + + private void ScanStarg( + MethodIL methodBody, + int offset, + int index, + Stack currentStack + ) + { + var valueToStore = PopUnknown(currentStack, 1, methodBody, offset); + HandleStoreParameter(methodBody, offset, index, valueToStore.Value); + } + + private void ScanLdloc( + MethodIL methodBody, + int offset, + ILOpcode operation, + int index, + Stack currentStack, + ValueBasicBlockPair[] locals) + { + bool isByRef = operation == ILOpcode.ldloca || operation == ILOpcode.ldloca_s + || methodBody.GetLocals()[index].Type.IsByRefOrPointer(); + + ValueBasicBlockPair localValue = locals[index]; + if (localValue.Value != null) + { + ValueNode valueToPush = localValue.Value; + currentStack.Push(new StackSlot(valueToPush, isByRef)); + } + else + { + currentStack.Push(new StackSlot(null, isByRef)); + } + } + + private static void ScanLdtoken(MethodIL methodBody, object operand, Stack currentStack) + { + if (operand is TypeDesc type) + { + if (type.IsGenericParameter) + { + StackSlot slot = new StackSlot(new RuntimeTypeHandleForGenericParameterValue((GenericParameterDesc)type)); + currentStack.Push(slot); + } + else + { + StackSlot slot = new StackSlot(new RuntimeTypeHandleValue(type)); + currentStack.Push(slot); + } + } + else if (operand is MethodDesc method) + { + StackSlot slot = new StackSlot(new RuntimeMethodHandleValue(method)); + currentStack.Push(slot); + } + else + { + PushUnknown(currentStack); + } + } + + private void ScanStloc( + MethodIL methodBody, + int offset, + int index, + Stack currentStack, + ValueBasicBlockPair[] locals, + int curBasicBlock) + { + StackSlot valueToStore = PopUnknown(currentStack, 1, methodBody, offset); + StoreMethodLocalValue(locals, valueToStore.Value, index, curBasicBlock); + } + + private void ScanIndirectStore( + MethodIL methodBody, + int offset, + Stack currentStack) + { + StackSlot valueToStore = PopUnknown(currentStack, 1, methodBody, offset); + StackSlot destination = PopUnknown(currentStack, 1, methodBody, offset); + + foreach (var uniqueDestination in destination.Value.UniqueValues()) + { + if (uniqueDestination.Kind == ValueNodeKind.LoadField) + { + HandleStoreField(methodBody, offset, ((LoadFieldValue)uniqueDestination).Field, valueToStore.Value); + } + else if (uniqueDestination.Kind == ValueNodeKind.MethodParameter) + { + HandleStoreParameter(methodBody, offset, ((MethodParameterValue)uniqueDestination).ParameterIndex, valueToStore.Value); + } + } + + } + + protected abstract ValueNode GetFieldValue(MethodIL method, FieldDesc field); + + private void ScanLdfld( + MethodIL methodBody, + int offset, + ILOpcode opcode, + FieldDesc field, + Stack currentStack + ) + { + if (opcode == ILOpcode.ldfld || opcode == ILOpcode.ldflda) + PopUnknown(currentStack, 1, methodBody, offset); + + bool isByRef = opcode == ILOpcode.ldflda || opcode == ILOpcode.ldsflda; + + StackSlot slot = new StackSlot(GetFieldValue(methodBody, field), isByRef); + currentStack.Push(slot); + } + + protected virtual void HandleStoreField(MethodIL method, int offset, FieldDesc field, ValueNode valueToStore) + { + } + + protected virtual void HandleStoreParameter(MethodIL method, int offset, int index, ValueNode valueToStore) + { + } + + private void ScanStfld( + MethodIL methodBody, + int offset, + ILOpcode opcode, + FieldDesc field, + Stack currentStack) + { + StackSlot valueToStoreSlot = PopUnknown(currentStack, 1, methodBody, offset); + if (opcode == ILOpcode.stfld) + PopUnknown(currentStack, 1, methodBody, offset); + + HandleStoreField(methodBody, offset, field, valueToStoreSlot.Value); + } + + private ValueNodeList PopCallArguments( + Stack currentStack, + MethodDesc methodCalled, + MethodIL containingMethodBody, + bool isNewObj, int ilOffset, + out ValueNode newObjValue) + { + newObjValue = null; + + int countToPop = 0; + if (!isNewObj && !methodCalled.Signature.IsStatic) + countToPop++; + countToPop += methodCalled.Signature.Length; + + ValueNodeList methodParams = new ValueNodeList(countToPop); + for (int iParam = 0; iParam < countToPop; ++iParam) + { + StackSlot slot = PopUnknown(currentStack, 1, containingMethodBody, ilOffset); + methodParams.Add(slot.Value); + } + + if (isNewObj) + { + newObjValue = UnknownValue.Instance; + methodParams.Add(newObjValue); + } + methodParams.Reverse(); + return methodParams; + } + + private void HandleCall( + MethodIL callingMethodBody, + ILOpcode opcode, + int offset, + MethodDesc calledMethod, + Stack currentStack, + int curBasicBlock) + { + bool isNewObj = opcode == ILOpcode.newobj; + + ValueNode newObjValue; + ValueNodeList methodParams = PopCallArguments(currentStack, calledMethod, callingMethodBody, isNewObj, + offset, out newObjValue); + + ValueNode methodReturnValue; + bool handledFunction = HandleCall( + callingMethodBody, + calledMethod, + opcode, + offset, + methodParams, + out methodReturnValue); + + // Handle the return value or newobj result + if (!handledFunction) + { + if (isNewObj) + { + if (newObjValue == null) + PushUnknown(currentStack); + else + methodReturnValue = newObjValue; + } + else + { + if (!calledMethod.Signature.ReturnType.IsVoid) + { + methodReturnValue = UnknownValue.Instance; + } + } + } + + if (methodReturnValue != null) + currentStack.Push(new StackSlot(methodReturnValue, calledMethod.Signature.ReturnType.IsByRefOrPointer())); + + foreach (var param in methodParams) + { + if (param is ArrayValue arr) + { + MarkArrayValuesAsUnknown(arr, curBasicBlock); + } + } + } + + public abstract bool HandleCall( + MethodIL callingMethodBody, + MethodDesc calledMethod, + ILOpcode operation, + int offset, + ValueNodeList methodParams, + out ValueNode methodReturnValue); + + // Limit tracking array values to 32 values for performance reasons. There are many arrays much longer than 32 elements in .NET, but the interesting ones for the linker are nearly always less than 32 elements. + private const int MaxTrackedArrayValues = 32; + + private static void MarkArrayValuesAsUnknown(ArrayValue arrValue, int curBasicBlock) + { + // Since we can't know the current index we're storing the value at, clear all indices. + // That way we won't accidentally think we know the value at a given index when we cannot. + foreach (var knownIndex in arrValue.IndexValues.Keys) + { + // Don't pass MaxTrackedArrayValues since we are only looking at keys we've already seen. + StoreMethodLocalValue(arrValue.IndexValues, UnknownValue.Instance, knownIndex, curBasicBlock); + } + } + + private void ScanStelem( + int offset, + Stack currentStack, + MethodIL methodBody, + int curBasicBlock) + { + StackSlot valueToStore = PopUnknown(currentStack, 1, methodBody, offset); + StackSlot indexToStoreAt = PopUnknown(currentStack, 1, methodBody, offset); + StackSlot arrayToStoreIn = PopUnknown(currentStack, 1, methodBody, offset); + int? indexToStoreAtInt = indexToStoreAt.Value.AsConstInt(); + foreach (var array in arrayToStoreIn.Value.UniqueValues()) + { + if (array is ArrayValue arrValue) + { + if (indexToStoreAtInt == null) + { + MarkArrayValuesAsUnknown(arrValue, curBasicBlock); + } + else + { + // When we know the index, we can record the value at that index. + StoreMethodLocalValue(arrValue.IndexValues, valueToStore.Value, indexToStoreAtInt.Value, curBasicBlock, MaxTrackedArrayValues); + } + } + } + } + + private void ScanLdelem( + ILOpcode opcode, + int offset, + Stack currentStack, + MethodIL methodBody, + int curBasicBlock) + { + StackSlot indexToLoadFrom = PopUnknown(currentStack, 1, methodBody, offset); + StackSlot arrayToLoadFrom = PopUnknown(currentStack, 1, methodBody, offset); + if (arrayToLoadFrom.Value is not ArrayValue arr) + { + PushUnknown(currentStack); + return; + } + bool isByRef = opcode == ILOpcode.ldelema; + + int? index = indexToLoadFrom.Value.AsConstInt(); + if (index == null) + { + PushUnknown(currentStack); + if (isByRef) + { + MarkArrayValuesAsUnknown(arr, curBasicBlock); + } + return; + } + + + ValueBasicBlockPair arrayIndexValue; + arr.IndexValues.TryGetValue(index.Value, out arrayIndexValue); + if (arrayIndexValue.Value != null) + { + ValueNode valueToPush = arrayIndexValue.Value; + currentStack.Push(new StackSlot(valueToPush, isByRef)); + } + else + { + currentStack.Push(new StackSlot(null, isByRef)); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/Origin.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/Origin.cs new file mode 100644 index 00000000000000..2668a4696cc5eb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/Origin.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.Dataflow +{ + internal abstract class Origin + { + } + + class MethodReturnOrigin : Origin + { + public MethodDesc Method { get; } + + public MethodReturnOrigin(MethodDesc method) + { + Method = method; + } + + public string GetDisplayName() => Method.GetDisplayName(); + } + + class ParameterOrigin : Origin + { + public MethodDesc Method { get; } + public int Index { get; } + + public ParameterOrigin(MethodDesc method, int index) + { + Method = method; + Index = index; + } + + public string GetDisplayName() => Method.GetDisplayName(); + } + + class MethodOrigin : Origin + { + public MethodDesc Method { get; } + + public MethodOrigin(MethodDesc method) + { + Method = method; + } + + public string GetDisplayName() => Method.GetDisplayName(); + } + + class FieldOrigin : Origin + { + public FieldDesc Field { get; } + + public FieldOrigin(FieldDesc field) + { + Field = field; + } + + public string GetDisplayName() => Field.GetDisplayName(); + } + + class TypeOrigin : Origin + { + public MetadataType Type { get; } + + public TypeOrigin(MetadataType type) + { + Type = type; + } + + public string GetDisplayName() => Type.GetDisplayName(); + } + + class GenericParameterOrigin : Origin + { + public GenericParameterDesc GenericParameter { get; } + + public string Name => GenericParameter.Name; + + public GenericParameterOrigin(GenericParameterDesc genericParam) + { + GenericParameter = genericParam; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/README.md b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/README.md new file mode 100644 index 00000000000000..6388ad638a5b63 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/README.md @@ -0,0 +1,7 @@ +The dataflow analysis logic originates from IL Linker: http://github.com/mono/linker. + +The purpose of this logic is to analyze dynamic behavior of the compiled code to make things like reflection work. This is done by analyzing the IL and reading dataflow annotations. + +Let's try to keep this in sync. The ReferenceSource contains sources at the time of porting. + +It should be updated whenever we take fixes from IL linker. \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DynamicallyAccessedMembersBinder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DynamicallyAccessedMembersBinder.cs new file mode 100644 index 00000000000000..3f92f0516eec6d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DynamicallyAccessedMembersBinder.cs @@ -0,0 +1,433 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using Mono.Cecil; + +#nullable enable + +namespace Mono.Linker +{ + // Temporary workaround - should be removed once linker can be upgraded to build against + // high enough version of the framework which has this enum value. + internal static class DynamicallyAccessedMemberTypesOverlay + { + public const DynamicallyAccessedMemberTypes Interfaces = (DynamicallyAccessedMemberTypes) 0x2000; + } + + internal static class DynamicallyAccessedMembersBinder + { + // Returns the members of the type bound by memberTypes. For DynamicallyAccessedMemberTypes.All, this returns all members of the type and its + // nested types, including interface implementations, plus the same or any base types or implemented interfaces. + // DynamicallyAccessedMemberTypes.PublicNestedTypes and NonPublicNestedTypes do the same for members of the selected nested types. + public static IEnumerable GetDynamicallyAccessedMembers (this TypeDefinition typeDefinition, LinkContext context, DynamicallyAccessedMemberTypes memberTypes, bool declaredOnly = false) + { + if (memberTypes == DynamicallyAccessedMemberTypes.None) + yield break; + + if (memberTypes == DynamicallyAccessedMemberTypes.All) { + var members = new List (); + typeDefinition.GetAllOnType (context, declaredOnly, members); + foreach (var m in members) + yield return m; + yield break; + } + + var declaredOnlyFlags = declaredOnly ? BindingFlags.DeclaredOnly : BindingFlags.Default; + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.NonPublicConstructors)) { + foreach (var c in typeDefinition.GetConstructorsOnType (filter: null, bindingFlags: BindingFlags.NonPublic)) + yield return c; + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors)) { + foreach (var c in typeDefinition.GetConstructorsOnType (filter: null, bindingFlags: BindingFlags.Public)) + yield return c; + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)) { + foreach (var c in typeDefinition.GetConstructorsOnType (filter: m => m.IsPublic && m.Parameters.Count == 0)) + yield return c; + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.NonPublicMethods)) { + foreach (var m in typeDefinition.GetMethodsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.NonPublic | declaredOnlyFlags)) + yield return m; + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicMethods)) { + foreach (var m in typeDefinition.GetMethodsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.Public | declaredOnlyFlags)) + yield return m; + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.NonPublicFields)) { + foreach (var f in typeDefinition.GetFieldsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.NonPublic | declaredOnlyFlags)) + yield return f; + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicFields)) { + foreach (var f in typeDefinition.GetFieldsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.Public | declaredOnlyFlags)) + yield return f; + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.NonPublicNestedTypes)) { + foreach (var nested in typeDefinition.GetNestedTypesOnType (filter: null, bindingFlags: BindingFlags.NonPublic)) { + yield return nested; + var members = new List (); + nested.GetAllOnType (context, declaredOnly: false, members); + foreach (var m in members) + yield return m; + } + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicNestedTypes)) { + foreach (var nested in typeDefinition.GetNestedTypesOnType (filter: null, bindingFlags: BindingFlags.Public)) { + yield return nested; + var members = new List (); + nested.GetAllOnType (context, declaredOnly: false, members); + foreach (var m in members) + yield return m; + } + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.NonPublicProperties)) { + foreach (var p in typeDefinition.GetPropertiesOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.NonPublic | declaredOnlyFlags)) + yield return p; + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicProperties)) { + foreach (var p in typeDefinition.GetPropertiesOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.Public | declaredOnlyFlags)) + yield return p; + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.NonPublicEvents)) { + foreach (var e in typeDefinition.GetEventsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.NonPublic | declaredOnlyFlags)) + yield return e; + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicEvents)) { + foreach (var e in typeDefinition.GetEventsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.Public | declaredOnlyFlags)) + yield return e; + } + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypesOverlay.Interfaces)) { + foreach (var i in typeDefinition.GetAllInterfaceImplementations (context, declaredOnly)) + yield return i; + } + } + + public static IEnumerable GetConstructorsOnType (this TypeDefinition type, Func? filter, BindingFlags? bindingFlags = null) + { + foreach (var method in type.Methods) { + if (!method.IsConstructor) + continue; + + if (filter != null && !filter (method)) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static && !method.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance && method.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public && !method.IsPublic) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic && method.IsPublic) + continue; + + yield return method; + } + } + + public static IEnumerable GetMethodsOnTypeHierarchy (this TypeDefinition thisType, LinkContext context, Func? filter, BindingFlags? bindingFlags = null) + { + TypeDefinition? type = thisType; + bool onBaseType = false; + while (type != null) { + foreach (var method in type.Methods) { + // Ignore constructors as those are not considered methods from a reflection's point of view + if (method.IsConstructor) + continue; + + // Ignore private methods on a base type - those are completely ignored by reflection + // (anything private on the base type is not visible via the derived type) + if (onBaseType && method.IsPrivate) + continue; + + // Note that special methods like property getter/setter, event adder/remover will still get through and will be marked. + // This is intentional as reflection treats these as methods as well. + + if (filter != null && !filter (method)) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static && !method.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance && method.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public && !method.IsPublic) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic && method.IsPublic) + continue; + + yield return method; + } + + if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) + yield break; + + type = context.TryResolve (type.BaseType); + onBaseType = true; + } + } + + public static IEnumerable GetFieldsOnTypeHierarchy (this TypeDefinition thisType, LinkContext context, Func? filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + TypeDefinition? type = thisType; + bool onBaseType = false; + while (type != null) { + foreach (var field in type.Fields) { + // Ignore private fields on a base type - those are completely ignored by reflection + // (anything private on the base type is not visible via the derived type) + if (onBaseType && field.IsPrivate) + continue; + + // Note that compiler generated fields backing some properties and events will get through here. + // This is intentional as reflection treats these as fields as well. + + if (filter != null && !filter (field)) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static && !field.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance && field.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public && !field.IsPublic) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic && field.IsPublic) + continue; + + yield return field; + } + + if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) + yield break; + + type = context.TryResolve (type.BaseType); + onBaseType = true; + } + } + + public static IEnumerable GetNestedTypesOnType (this TypeDefinition type, Func? filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var nestedType in type.NestedTypes) { + if (filter != null && !filter (nestedType)) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public) { + if (!nestedType.IsNestedPublic) + continue; + } + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic) { + if (nestedType.IsNestedPublic) + continue; + } + + yield return nestedType; + } + } + + public static IEnumerable GetPropertiesOnTypeHierarchy (this TypeDefinition thisType, LinkContext context, Func? filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + TypeDefinition? type = thisType; + bool onBaseType = false; + while (type != null) { + foreach (var property in type.Properties) { + // Ignore private properties on a base type - those are completely ignored by reflection + // (anything private on the base type is not visible via the derived type) + // Note that properties themselves are not actually private, their accessors are + if (onBaseType && + (property.GetMethod == null || property.GetMethod.IsPrivate) && + (property.SetMethod == null || property.SetMethod.IsPrivate)) + continue; + + if (filter != null && !filter (property)) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static) { + if ((property.GetMethod != null) && !property.GetMethod.IsStatic) continue; + if ((property.SetMethod != null) && !property.SetMethod.IsStatic) continue; + } + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance) { + if ((property.GetMethod != null) && property.GetMethod.IsStatic) continue; + if ((property.SetMethod != null) && property.SetMethod.IsStatic) continue; + } + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public) { + if ((property.GetMethod == null || !property.GetMethod.IsPublic) + && (property.SetMethod == null || !property.SetMethod.IsPublic)) + continue; + } + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic) { + if ((property.GetMethod != null) && property.GetMethod.IsPublic) continue; + if ((property.SetMethod != null) && property.SetMethod.IsPublic) continue; + } + + yield return property; + } + + if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) + yield break; + + type = context.TryResolve (type.BaseType); + onBaseType = true; + } + } + + public static IEnumerable GetEventsOnTypeHierarchy (this TypeDefinition thisType, LinkContext context, Func? filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + TypeDefinition? type = thisType; + bool onBaseType = false; + while (type != null) { + foreach (var @event in type.Events) { + // Ignore private properties on a base type - those are completely ignored by reflection + // (anything private on the base type is not visible via the derived type) + // Note that properties themselves are not actually private, their accessors are + if (onBaseType && + (@event.AddMethod == null || @event.AddMethod.IsPrivate) && + (@event.RemoveMethod == null || @event.RemoveMethod.IsPrivate)) + continue; + + if (filter != null && !filter (@event)) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static) { + if ((@event.AddMethod != null) && !@event.AddMethod.IsStatic) continue; + if ((@event.RemoveMethod != null) && !@event.RemoveMethod.IsStatic) continue; + } + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance) { + if ((@event.AddMethod != null) && @event.AddMethod.IsStatic) continue; + if ((@event.RemoveMethod != null) && @event.RemoveMethod.IsStatic) continue; + } + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public) { + if ((@event.AddMethod == null || !@event.AddMethod.IsPublic) + && (@event.RemoveMethod == null || !@event.RemoveMethod.IsPublic)) + continue; + } + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic) { + if ((@event.AddMethod != null) && @event.AddMethod.IsPublic) continue; + if ((@event.RemoveMethod != null) && @event.RemoveMethod.IsPublic) continue; + } + + yield return @event; + } + + if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) + yield break; + + type = context.TryResolve (type.BaseType); + onBaseType = true; + } + } + + // declaredOnly will cause this to retrieve interfaces recursively required by the type, but doesn't necessarily + // include interfaces required by any base types. + public static IEnumerable GetAllInterfaceImplementations (this TypeDefinition thisType, LinkContext context, bool declaredOnly) + { + TypeDefinition? type = thisType; + while (type != null) { + foreach (var i in type.Interfaces) { + yield return i; + + TypeDefinition? interfaceType = context.TryResolve (i.InterfaceType); + if (interfaceType != null) { + // declaredOnly here doesn't matter since interfaces don't have base types + foreach (var innerInterface in interfaceType.GetAllInterfaceImplementations (context, declaredOnly: true)) + yield return innerInterface; + } + } + + if (declaredOnly) + yield break; + + type = context.TryResolve (type.BaseType); + } + } + + // declaredOnly will cause this to retrieve only members of the type, not of its base types. This includes interfaces recursively + // required by this type (but not members of these interfaces, or interfaces required only by base types). + public static void GetAllOnType (this TypeDefinition type, LinkContext context, bool declaredOnly, List members) => GetAllOnType (type, context, declaredOnly, members, new HashSet ()); + + static void GetAllOnType (TypeDefinition type, LinkContext context, bool declaredOnly, List members, HashSet types) + { + if (!types.Add (type)) + return; + + if (type.HasNestedTypes) { + foreach (var nested in type.NestedTypes) { + members.Add (nested); + // Base types and interfaces of nested types are always included. + GetAllOnType (nested, context, declaredOnly: false, members, types); + } + } + + if (!declaredOnly) { + var baseType = context.TryResolve (type.BaseType); + if (baseType != null) + GetAllOnType (baseType, context, declaredOnly: false, members, types); + } + + if (type.HasInterfaces) { + if (declaredOnly) { + foreach (var iface in type.GetAllInterfaceImplementations (context, declaredOnly: true)) + members.Add (iface); + } else { + foreach (var iface in type.Interfaces) { + members.Add (iface); + var interfaceType = context.TryResolve (iface.InterfaceType); + if (interfaceType == null) + continue; + GetAllOnType (interfaceType, context, declaredOnly: false, members, types); + } + } + } + + if (type.HasFields) { + foreach (var f in type.Fields) + members.Add (f); + } + + if (type.HasMethods) { + foreach (var m in type.Methods) + members.Add (m); + } + + if (type.HasProperties) { + foreach (var p in type.Properties) + members.Add (p); + } + + if (type.HasEvents) { + foreach (var e in type.Events) + members.Add (e); + } + } + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/FlowAnnotations.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/FlowAnnotations.cs new file mode 100644 index 00000000000000..5a7af5a04962a2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/FlowAnnotations.cs @@ -0,0 +1,703 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Mono.Cecil; +using Mono.Cecil.Cil; + +#nullable enable + +namespace Mono.Linker.Dataflow +{ + class FlowAnnotations + { + readonly LinkContext _context; + readonly Dictionary _annotations = new Dictionary (); + readonly TypeHierarchyCache _hierarchyInfo; + + public FlowAnnotations (LinkContext context) + { + _context = context; + _hierarchyInfo = new TypeHierarchyCache (context); + } + + public bool RequiresDataFlowAnalysis (MethodDefinition method) => + GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out _); + + public bool RequiresDataFlowAnalysis (FieldDefinition field) => + GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out _); + + public bool RequiresDataFlowAnalysis (GenericParameter genericParameter) => + GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None; + + /// + /// Retrieves the annotations for the given parameter. + /// + /// Parameter index in the IL sense. Parameter 0 on instance methods is `this`. + /// + public DynamicallyAccessedMemberTypes GetParameterAnnotation (MethodDefinition method, int parameterIndex) + { + if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) && + annotation.ParameterAnnotations != null) + return annotation.ParameterAnnotations[parameterIndex]; + + return DynamicallyAccessedMemberTypes.None; + } + + public DynamicallyAccessedMemberTypes GetReturnParameterAnnotation (MethodDefinition method) + { + if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation)) + return annotation.ReturnParameterAnnotation; + + return DynamicallyAccessedMemberTypes.None; + } + + public DynamicallyAccessedMemberTypes GetFieldAnnotation (FieldDefinition field) + { + if (GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out var annotation)) + return annotation.Annotation; + + return DynamicallyAccessedMemberTypes.None; + } + + public DynamicallyAccessedMemberTypes GetTypeAnnotation (TypeDefinition type) => + GetAnnotations (type).TypeAnnotation; + + public bool ShouldWarnWhenAccessedForReflection (IMemberDefinition provider) => + provider switch { + MethodDefinition method => ShouldWarnWhenAccessedForReflection (method), + FieldDefinition field => ShouldWarnWhenAccessedForReflection (field), + _ => false + }; + + public DynamicallyAccessedMemberTypes GetGenericParameterAnnotation (GenericParameter genericParameter) + { + TypeDefinition? declaringType = _context.Resolve (genericParameter.DeclaringType); + if (declaringType != null) { + if (GetAnnotations (declaringType).TryGetAnnotation (genericParameter, out var annotation)) + return annotation; + + return DynamicallyAccessedMemberTypes.None; + } + + MethodDefinition? declaringMethod = _context.Resolve (genericParameter.DeclaringMethod); + if (declaringMethod != null && GetAnnotations (declaringMethod.DeclaringType).TryGetAnnotation (declaringMethod, out var methodTypeAnnotations) && + methodTypeAnnotations.TryGetAnnotation (genericParameter, out var methodAnnotation)) + return methodAnnotation; + + return DynamicallyAccessedMemberTypes.None; + } + + public bool ShouldWarnWhenAccessedForReflection (MethodDefinition method) + { + if (!GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation)) + return false; + + if (annotation.ParameterAnnotations == null && annotation.ReturnParameterAnnotation == DynamicallyAccessedMemberTypes.None) + return false; + + // If the method only has annotation on the return value and it's not virtual avoid warning. + // Return value annotations are "consumed" by the caller of a method, and as such there is nothing + // wrong calling these dynamically. The only problem can happen if something overrides a virtual + // method with annotated return value at runtime - in this case the trimmer can't validate + // that the method will return only types which fulfill the annotation's requirements. + // For example: + // class BaseWithAnnotation + // { + // [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] + // public abstract Type GetTypeWithFields(); + // } + // + // class UsingTheBase + // { + // public void PrintFields(Base base) + // { + // // No warning here - GetTypeWithFields is correctly annotated to allow GetFields on the return value. + // Console.WriteLine(string.Join(" ", base.GetTypeWithFields().GetFields().Select(f => f.Name))); + // } + // } + // + // If at runtime (through ref emit) something generates code like this: + // class DerivedAtRuntimeFromBase + // { + // // No point in adding annotation on the return value - nothing will look at it anyway + // // Linker will not see this code, so there are no checks + // public override Type GetTypeWithFields() { return typeof(TestType); } + // } + // + // If TestType from above is trimmed, it may note have all its fields, and there would be no warnings generated. + // But there has to be code like this somewhere in the app, in order to generate the override: + // class RuntimeTypeGenerator + // { + // public MethodInfo GetBaseMethod() + // { + // // This must warn - that the GetTypeWithFields has annotation on the return value + // return typeof(BaseWithAnnotation).GetMethod("GetTypeWithFields"); + // } + // } + return method.IsVirtual || annotation.ParameterAnnotations != null; + } + + public bool ShouldWarnWhenAccessedForReflection (FieldDefinition field) => + GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out _); + + TypeAnnotations GetAnnotations (TypeDefinition type) + { + if (!_annotations.TryGetValue (type, out TypeAnnotations value)) { + value = BuildTypeAnnotations (type); + _annotations.Add (type, value); + } + + return value; + } + + static bool IsDynamicallyAccessedMembersAttribute (CustomAttribute attribute) + { + var attributeType = attribute.AttributeType; + return attributeType.Name == "DynamicallyAccessedMembersAttribute" && attributeType.Namespace == "System.Diagnostics.CodeAnalysis"; + } + + DynamicallyAccessedMemberTypes GetMemberTypesForDynamicallyAccessedMembersAttribute (IMemberDefinition member, ICustomAttributeProvider? providerIfNotMember = null) + { + ICustomAttributeProvider provider = providerIfNotMember ?? member; + if (!_context.CustomAttributes.HasAny (provider)) + return DynamicallyAccessedMemberTypes.None; + foreach (var attribute in _context.CustomAttributes.GetCustomAttributes (provider)) { + if (!IsDynamicallyAccessedMembersAttribute (attribute)) + continue; + if (attribute.ConstructorArguments.Count == 1) + return (DynamicallyAccessedMemberTypes) (int) attribute.ConstructorArguments[0].Value; + else + _context.LogWarning ( + $"Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' doesn't have the required number of parameters specified.", + 2028, member); + } + return DynamicallyAccessedMemberTypes.None; + } + + TypeAnnotations BuildTypeAnnotations (TypeDefinition type) + { + // class, interface, struct can have annotations + DynamicallyAccessedMemberTypes typeAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (type); + + var annotatedFields = new ArrayBuilder (); + + // First go over all fields with an explicit annotation + if (type.HasFields) { + foreach (FieldDefinition field in type.Fields) { + DynamicallyAccessedMemberTypes annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (field); + if (annotation == DynamicallyAccessedMemberTypes.None) { + continue; + } + + if (!IsTypeInterestingForDataflow (field.FieldType)) { + // Already know that there's a non-empty annotation on a field which is not System.Type/String and we're about to ignore it + _context.LogWarning ( + $"Field '{field.GetDisplayName ()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to fields of type 'System.Type' or 'System.String'.", + 2097, field, subcategory: MessageSubCategory.TrimAnalysis); + continue; + } + + annotatedFields.Add (new FieldAnnotation (field, annotation)); + } + } + + var annotatedMethods = new ArrayBuilder (); + + // Next go over all methods with an explicit annotation + if (type.HasMethods) { + foreach (MethodDefinition method in type.Methods) { + DynamicallyAccessedMemberTypes[]? paramAnnotations = null; + + // We convert indices from metadata space to IL space here. + // IL space assigns index 0 to the `this` parameter on instance methods. + + + DynamicallyAccessedMemberTypes methodMemberTypes = GetMemberTypesForDynamicallyAccessedMembersAttribute (method); + + int offset; + if (method.HasImplicitThis ()) { + offset = 1; + if (IsTypeInterestingForDataflow (method.DeclaringType)) { + // If there's an annotation on the method itself and it's one of the special types (System.Type for example) + // treat that annotation as annotating the "this" parameter. + if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { + paramAnnotations = new DynamicallyAccessedMemberTypes[method.Parameters.Count + offset]; + paramAnnotations[0] = methodMemberTypes; + } + } else if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { + _context.LogWarning ( + $"The 'DynamicallyAccessedMembersAttribute' is not allowed on methods. It is allowed on method return value or method parameters though.", + 2041, method, subcategory: MessageSubCategory.TrimAnalysis); + } + } else { + offset = 0; + if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { + _context.LogWarning ( + $"The 'DynamicallyAccessedMembersAttribute' is not allowed on methods. It is allowed on method return value or method parameters though.", + 2041, method, subcategory: MessageSubCategory.TrimAnalysis); + } + } + + for (int i = 0; i < method.Parameters.Count; i++) { + var methodParameter = method.Parameters[i]; + DynamicallyAccessedMemberTypes pa = GetMemberTypesForDynamicallyAccessedMembersAttribute (method, providerIfNotMember: methodParameter); + if (pa == DynamicallyAccessedMemberTypes.None) + continue; + + if (!IsTypeInterestingForDataflow (methodParameter.ParameterType)) { + _context.LogWarning ( + $"Parameter '{DiagnosticUtilities.GetParameterNameForErrorMessage (methodParameter)}' of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (methodParameter.Method)}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to parameters of type 'System.Type' or 'System.String'.", + 2098, method, subcategory: MessageSubCategory.TrimAnalysis); + continue; + } + + if (paramAnnotations == null) { + paramAnnotations = new DynamicallyAccessedMemberTypes[method.Parameters.Count + offset]; + } + paramAnnotations[i + offset] = pa; + } + + DynamicallyAccessedMemberTypes returnAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (method, providerIfNotMember: method.MethodReturnType); + if (returnAnnotation != DynamicallyAccessedMemberTypes.None && !IsTypeInterestingForDataflow (method.ReturnType)) { + _context.LogWarning ( + $"Return type of method '{method.GetDisplayName ()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to properties of type 'System.Type' or 'System.String'.", + 2106, method, subcategory: MessageSubCategory.TrimAnalysis); + } + + DynamicallyAccessedMemberTypes[]? genericParameterAnnotations = null; + if (method.HasGenericParameters) { + for (int genericParameterIndex = 0; genericParameterIndex < method.GenericParameters.Count; genericParameterIndex++) { + var genericParameter = method.GenericParameters[genericParameterIndex]; + var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (method, providerIfNotMember: genericParameter); + if (annotation != DynamicallyAccessedMemberTypes.None) { + if (genericParameterAnnotations == null) + genericParameterAnnotations = new DynamicallyAccessedMemberTypes[method.GenericParameters.Count]; + genericParameterAnnotations[genericParameterIndex] = annotation; + } + } + } + + if (returnAnnotation != DynamicallyAccessedMemberTypes.None || paramAnnotations != null || genericParameterAnnotations != null) { + annotatedMethods.Add (new MethodAnnotations (method, paramAnnotations, returnAnnotation, genericParameterAnnotations)); + } + } + } + + // Next up are properties. Annotations on properties are kind of meta because we need to + // map them to annotations on methods/fields. They're syntactic sugar - what they do is expressible + // by placing attribute on the accessor/backing field. For complex properties, that's what people + // will need to do anyway. Like so: + // + // [field: Attribute] + // Type MyProperty { + // [return: Attribute] + // get; + // [value: Attribute] + // set; + // } + // + + if (type.HasProperties) { + foreach (PropertyDefinition property in type.Properties) { + DynamicallyAccessedMemberTypes annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (property); + if (annotation == DynamicallyAccessedMemberTypes.None) + continue; + + if (!IsTypeInterestingForDataflow (property.PropertyType)) { + _context.LogWarning ( + $"Property '{property.GetDisplayName ()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to properties of type 'System.Type' or 'System.String'.", + 2099, property, subcategory: MessageSubCategory.TrimAnalysis); + continue; + } + + FieldDefinition? backingFieldFromSetter = null; + + // Propagate the annotation to the setter method + MethodDefinition setMethod = property.SetMethod; + if (setMethod != null) { + + // Abstract property backing field propagation doesn't make sense, and any derived property will be validated + // to have the exact same annotations on getter/setter, and thus if it has a detectable backing field that will be validated as well. + if (setMethod.HasBody) { + // Look for the compiler generated backing field. If it doesn't work out simply move on. In such case we would still + // propagate the annotation to the setter/getter and later on when analyzing the setter/getter we will warn + // that the field (which ever it is) must be annotated as well. + ScanMethodBodyForFieldAccess (setMethod.Body, write: true, out backingFieldFromSetter); + } + + if (annotatedMethods.Any (a => a.Method == setMethod)) { + _context.LogWarning ( + $"'DynamicallyAccessedMembersAttribute' on property '{property.GetDisplayName ()}' conflicts with the same attribute on its accessor '{setMethod.GetDisplayName ()}'.", + 2043, setMethod, subcategory: MessageSubCategory.TrimAnalysis); + } else { + int offset = setMethod.HasImplicitThis () ? 1 : 0; + if (setMethod.Parameters.Count > 0) { + DynamicallyAccessedMemberTypes[] paramAnnotations = new DynamicallyAccessedMemberTypes[setMethod.Parameters.Count + offset]; + paramAnnotations[paramAnnotations.Length - 1] = annotation; + annotatedMethods.Add (new MethodAnnotations (setMethod, paramAnnotations, DynamicallyAccessedMemberTypes.None, null)); + } + } + } + + FieldDefinition? backingFieldFromGetter = null; + + // Propagate the annotation to the getter method + MethodDefinition getMethod = property.GetMethod; + if (getMethod != null) { + + // Abstract property backing field propagation doesn't make sense, and any derived property will be validated + // to have the exact same annotations on getter/setter, and thus if it has a detectable backing field that will be validated as well. + if (getMethod.HasBody) { + // Look for the compiler generated backing field. If it doesn't work out simply move on. In such case we would still + // propagate the annotation to the setter/getter and later on when analyzing the setter/getter we will warn + // that the field (which ever it is) must be annotated as well. + ScanMethodBodyForFieldAccess (getMethod.Body, write: false, out backingFieldFromGetter); + } + + if (annotatedMethods.Any (a => a.Method == getMethod)) { + _context.LogWarning ( + $"'DynamicallyAccessedMembersAttribute' on property '{property.GetDisplayName ()}' conflicts with the same attribute on its accessor '{getMethod.GetDisplayName ()}'.", + 2043, getMethod, subcategory: MessageSubCategory.TrimAnalysis); + } else { + annotatedMethods.Add (new MethodAnnotations (getMethod, null, annotation, null)); + } + } + + FieldDefinition? backingField; + if (backingFieldFromGetter != null && backingFieldFromSetter != null && + backingFieldFromGetter != backingFieldFromSetter) { + _context.LogWarning ( + $"Could not find a unique backing field for property '{property.GetDisplayName ()}' to propagate 'DynamicallyAccessedMembersAttribute'.", + 2042, property, subcategory: MessageSubCategory.TrimAnalysis); + backingField = null; + } else { + backingField = backingFieldFromGetter ?? backingFieldFromSetter; + } + + if (backingField != null) { + if (annotatedFields.Any (a => a.Field == backingField)) { + _context.LogWarning ( + $"'DynamicallyAccessedMemberAttribute' on property '{property.GetDisplayName ()}' conflicts with the same attribute on its backing field '{backingField.GetDisplayName ()}'.", + 2056, backingField, subcategory: MessageSubCategory.TrimAnalysis); + } else { + annotatedFields.Add (new FieldAnnotation (backingField, annotation)); + } + } + } + } + + DynamicallyAccessedMemberTypes[]? typeGenericParameterAnnotations = null; + if (type.HasGenericParameters) { + for (int genericParameterIndex = 0; genericParameterIndex < type.GenericParameters.Count; genericParameterIndex++) { + var genericParameter = type.GenericParameters[genericParameterIndex]; + var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (type, providerIfNotMember: genericParameter); + if (annotation != DynamicallyAccessedMemberTypes.None) { + if (typeGenericParameterAnnotations == null) + typeGenericParameterAnnotations = new DynamicallyAccessedMemberTypes[type.GenericParameters.Count]; + typeGenericParameterAnnotations[genericParameterIndex] = annotation; + } + } + } + + return new TypeAnnotations (type, typeAnnotation, annotatedMethods.ToArray (), annotatedFields.ToArray (), typeGenericParameterAnnotations); + } + + bool ScanMethodBodyForFieldAccess (MethodBody body, bool write, out FieldDefinition? found) + { + // Tries to find the backing field for a property getter/setter. + // Returns true if this is a method body that we can unambiguously analyze. + // The found field could still be null if there's no backing store. + + FieldReference? foundReference = null; + + foreach (Instruction instruction in body.Instructions) { + switch (instruction.OpCode.Code) { + case Code.Ldsfld when !write: + case Code.Ldfld when !write: + case Code.Stsfld when write: + case Code.Stfld when write: + + if (foundReference != null) { + // This writes/reads multiple fields - can't guess which one is the backing store. + // Return failure. + found = null; + return false; + } + + foundReference = (FieldReference) instruction.Operand; + break; + } + } + + if (foundReference == null) { + // Doesn't access any fields. Could be e.g. "Type Foo => typeof(Bar);" + // Return success. + found = null; + return true; + } + + found = _context.Resolve (foundReference); + + if (found == null) { + // If the field doesn't resolve, it can't be a field on the current type + // anyway. Return failure. + return false; + } + + if (found.DeclaringType != body.Method.DeclaringType || + found.IsStatic != body.Method.IsStatic || + !found.IsCompilerGenerated ()) { + // A couple heuristics to make sure we got the right field. + // Return failure. + found = null; + return false; + } + + return true; + } + + bool IsTypeInterestingForDataflow (TypeReference typeReference) + { + if (typeReference.MetadataType == MetadataType.String) + return true; + + TypeDefinition? type = _context.TryResolve (typeReference); + return type != null && ( + _hierarchyInfo.IsSystemType (type) || + _hierarchyInfo.IsSystemReflectionIReflect (type)); + } + + internal void ValidateMethodAnnotationsAreSame (MethodDefinition method, MethodDefinition baseMethod) + { + GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var methodAnnotations); + GetAnnotations (baseMethod.DeclaringType).TryGetAnnotation (baseMethod, out var baseMethodAnnotations); + + if (methodAnnotations.ReturnParameterAnnotation != baseMethodAnnotations.ReturnParameterAnnotation) + LogValidationWarning (method.MethodReturnType, baseMethod.MethodReturnType, method); + + if (methodAnnotations.ParameterAnnotations != null || baseMethodAnnotations.ParameterAnnotations != null) { + if (methodAnnotations.ParameterAnnotations == null) + ValidateMethodParametersHaveNoAnnotations (baseMethodAnnotations.ParameterAnnotations!, method, baseMethod, method); + else if (baseMethodAnnotations.ParameterAnnotations == null) + ValidateMethodParametersHaveNoAnnotations (methodAnnotations.ParameterAnnotations, method, baseMethod, method); + else { + if (methodAnnotations.ParameterAnnotations.Length != baseMethodAnnotations.ParameterAnnotations.Length) + return; + + for (int parameterIndex = 0; parameterIndex < methodAnnotations.ParameterAnnotations.Length; parameterIndex++) { + if (methodAnnotations.ParameterAnnotations[parameterIndex] != baseMethodAnnotations.ParameterAnnotations[parameterIndex]) + LogValidationWarning ( + DiagnosticUtilities.GetMethodParameterFromIndex (method, parameterIndex), + DiagnosticUtilities.GetMethodParameterFromIndex (baseMethod, parameterIndex), + method); + } + } + } + + if (methodAnnotations.GenericParameterAnnotations != null || baseMethodAnnotations.GenericParameterAnnotations != null) { + if (methodAnnotations.GenericParameterAnnotations == null) + ValidateMethodGenericParametersHaveNoAnnotations (baseMethodAnnotations.GenericParameterAnnotations!, method, baseMethod, method); + else if (baseMethodAnnotations.GenericParameterAnnotations == null) + ValidateMethodGenericParametersHaveNoAnnotations (methodAnnotations.GenericParameterAnnotations, method, baseMethod, method); + else { + if (methodAnnotations.GenericParameterAnnotations.Length != baseMethodAnnotations.GenericParameterAnnotations.Length) + return; + + for (int genericParameterIndex = 0; genericParameterIndex < methodAnnotations.GenericParameterAnnotations.Length; genericParameterIndex++) { + if (methodAnnotations.GenericParameterAnnotations[genericParameterIndex] != baseMethodAnnotations.GenericParameterAnnotations[genericParameterIndex]) { + LogValidationWarning ( + method.GenericParameters[genericParameterIndex], + baseMethod.GenericParameters[genericParameterIndex], + method); + } + } + } + } + } + + void ValidateMethodParametersHaveNoAnnotations (DynamicallyAccessedMemberTypes[] parameterAnnotations, MethodDefinition method, MethodDefinition baseMethod, IMemberDefinition origin) + { + for (int parameterIndex = 0; parameterIndex < parameterAnnotations.Length; parameterIndex++) { + var annotation = parameterAnnotations[parameterIndex]; + if (annotation != DynamicallyAccessedMemberTypes.None) + LogValidationWarning ( + DiagnosticUtilities.GetMethodParameterFromIndex (method, parameterIndex), + DiagnosticUtilities.GetMethodParameterFromIndex (baseMethod, parameterIndex), + origin); + } + } + + void ValidateMethodGenericParametersHaveNoAnnotations (DynamicallyAccessedMemberTypes[] genericParameterAnnotations, MethodDefinition method, MethodDefinition baseMethod, IMemberDefinition origin) + { + for (int genericParameterIndex = 0; genericParameterIndex < genericParameterAnnotations.Length; genericParameterIndex++) { + if (genericParameterAnnotations[genericParameterIndex] != DynamicallyAccessedMemberTypes.None) { + LogValidationWarning ( + method.GenericParameters[genericParameterIndex], + baseMethod.GenericParameters[genericParameterIndex], + origin); + } + } + } + + void LogValidationWarning (IMetadataTokenProvider provider, IMetadataTokenProvider baseProvider, IMemberDefinition origin) + { + Debug.Assert (provider.GetType () == baseProvider.GetType ()); + Debug.Assert (!(provider is GenericParameter genericParameter) || genericParameter.DeclaringMethod != null); + switch (provider) { + case ParameterDefinition parameterDefinition: + var baseParameterDefinition = (ParameterDefinition) baseProvider; + _context.LogWarning ( + $"'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the parameter '{DiagnosticUtilities.GetParameterNameForErrorMessage (parameterDefinition)}' of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (parameterDefinition.Method)}' " + + $"don't match overridden parameter '{DiagnosticUtilities.GetParameterNameForErrorMessage (baseParameterDefinition)}' of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (baseParameterDefinition.Method)}'. " + + $"All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.", + 2092, origin, subcategory: MessageSubCategory.TrimAnalysis); + break; + case MethodReturnType methodReturnType: + _context.LogWarning ( + $"'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the return value of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (methodReturnType.Method)}' " + + $"don't match overridden return value of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (((MethodReturnType) baseProvider).Method)}'. " + + $"All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.", + 2093, origin, subcategory: MessageSubCategory.TrimAnalysis); + break; + // No fields - it's not possible to have a virtual field and override it + case MethodDefinition methodDefinition: + _context.LogWarning ( + $"'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the implicit 'this' parameter of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (methodDefinition)}' " + + $"don't match overridden implicit 'this' parameter of method '{DiagnosticUtilities.GetMethodSignatureDisplayName ((MethodDefinition) baseProvider)}'. " + + $"All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.", + 2094, origin, subcategory: MessageSubCategory.TrimAnalysis); + break; + case GenericParameter genericParameterOverride: + var genericParameterBase = (GenericParameter) baseProvider; + _context.LogWarning ( + $"'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the generic parameter '{genericParameterOverride.Name}' of '{DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (genericParameterOverride)}' " + + $"don't match overridden generic parameter '{genericParameterBase.Name}' of '{DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (genericParameterBase)}'. " + + $"All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.", + 2095, origin, subcategory: MessageSubCategory.TrimAnalysis); + break; + default: + throw new NotImplementedException ($"Unsupported provider type '{provider.GetType ()}'."); + } + } + + readonly struct TypeAnnotations + { + readonly TypeDefinition _type; + readonly DynamicallyAccessedMemberTypes _typeAnnotation; + readonly MethodAnnotations[] _annotatedMethods; + readonly FieldAnnotation[] _annotatedFields; + readonly DynamicallyAccessedMemberTypes[]? _genericParameterAnnotations; + + public TypeAnnotations ( + TypeDefinition type, + DynamicallyAccessedMemberTypes typeAnnotation, + MethodAnnotations[] annotatedMethods, + FieldAnnotation[] annotatedFields, + DynamicallyAccessedMemberTypes[]? genericParameterAnnotations) + => (_type, _typeAnnotation, _annotatedMethods, _annotatedFields, _genericParameterAnnotations) + = (type, typeAnnotation, annotatedMethods, annotatedFields, genericParameterAnnotations); + + public DynamicallyAccessedMemberTypes TypeAnnotation { get => _typeAnnotation; } + + public bool TryGetAnnotation (MethodDefinition method, out MethodAnnotations annotations) + { + annotations = default; + + if (_annotatedMethods == null) { + return false; + } + + foreach (var m in _annotatedMethods) { + if (m.Method == method) { + annotations = m; + return true; + } + } + + return false; + } + + public bool TryGetAnnotation (FieldDefinition field, out FieldAnnotation annotation) + { + annotation = default; + + if (_annotatedFields == null) { + return false; + } + + foreach (var f in _annotatedFields) { + if (f.Field == field) { + annotation = f; + return true; + } + } + + return false; + } + + public bool TryGetAnnotation (GenericParameter genericParameter, out DynamicallyAccessedMemberTypes annotation) + { + annotation = default; + + if (_genericParameterAnnotations == null) + return false; + + for (int genericParameterIndex = 0; genericParameterIndex < _genericParameterAnnotations.Length; genericParameterIndex++) { + if (_type.GenericParameters[genericParameterIndex] == genericParameter) { + annotation = _genericParameterAnnotations[genericParameterIndex]; + return true; + } + } + + return false; + } + } + + readonly struct MethodAnnotations + { + public readonly MethodDefinition Method; + public readonly DynamicallyAccessedMemberTypes[]? ParameterAnnotations; + public readonly DynamicallyAccessedMemberTypes ReturnParameterAnnotation; + public readonly DynamicallyAccessedMemberTypes[]? GenericParameterAnnotations; + + public MethodAnnotations ( + MethodDefinition method, + DynamicallyAccessedMemberTypes[]? paramAnnotations, + DynamicallyAccessedMemberTypes returnParamAnnotations, + DynamicallyAccessedMemberTypes[]? genericParameterAnnotations) + => (Method, ParameterAnnotations, ReturnParameterAnnotation, GenericParameterAnnotations) = + (method, paramAnnotations, returnParamAnnotations, genericParameterAnnotations); + + public bool TryGetAnnotation (GenericParameter genericParameter, out DynamicallyAccessedMemberTypes annotation) + { + annotation = default; + + if (GenericParameterAnnotations == null) + return false; + + for (int genericParameterIndex = 0; genericParameterIndex < GenericParameterAnnotations.Length; genericParameterIndex++) { + if (Method.GenericParameters[genericParameterIndex] == genericParameter) { + annotation = GenericParameterAnnotations[genericParameterIndex]; + return true; + } + } + + return false; + } + } + + readonly struct FieldAnnotation + { + public readonly FieldDefinition Field; + public readonly DynamicallyAccessedMemberTypes Annotation; + + public FieldAnnotation (FieldDefinition field, DynamicallyAccessedMemberTypes annotation) + => (Field, Annotation) = (field, annotation); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodBodyScanner.cs new file mode 100644 index 00000000000000..6fc44bc4deb5b2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodBodyScanner.cs @@ -0,0 +1,1011 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Collections.Generic; + +#nullable enable + +namespace Mono.Linker.Dataflow +{ + /// + /// Tracks information about the contents of a stack slot + /// + readonly struct StackSlot + { + public ValueNode? Value { get; } + + /// + /// True if the value is on the stack as a byref + /// + public bool IsByRef { get; } + + public StackSlot (ValueNode? value, bool isByRef = false) + { + Value = value; + IsByRef = isByRef; + } + } + + abstract partial class MethodBodyScanner + { + protected readonly LinkContext _context; + + protected MethodBodyScanner (LinkContext context) + { + this._context = context; + } + + internal ValueNode? MethodReturnValue { private set; get; } + + protected virtual void WarnAboutInvalidILInMethod (MethodBody method, int ilOffset) + { + } + + private void CheckForInvalidStack (Stack stack, int depthRequired, MethodBody method, int ilOffset) + { + if (stack.Count < depthRequired) { + WarnAboutInvalidILInMethod (method, ilOffset); + while (stack.Count < depthRequired) + stack.Push (new StackSlot ()); // Push dummy values to avoid crashes. + // Analysis of this method will be incorrect. + } + } + + private static void PushUnknown (Stack stack) + { + stack.Push (new StackSlot ()); + } + + private void PushUnknownAndWarnAboutInvalidIL (Stack stack, MethodBody methodBody, int offset) + { + WarnAboutInvalidILInMethod (methodBody, offset); + PushUnknown (stack); + } + + private StackSlot PopUnknown (Stack stack, int count, MethodBody method, int ilOffset) + { + if (count < 1) + throw new InvalidOperationException (); + + StackSlot topOfStack = default; + CheckForInvalidStack (stack, count, method, ilOffset); + + for (int i = 0; i < count; ++i) { + StackSlot slot = stack.Pop (); + if (i == 0) + topOfStack = slot; + } + return topOfStack; + } + + private static StackSlot MergeStackElement (StackSlot a, StackSlot b) + { + StackSlot mergedSlot; + if (b.Value == null) { + mergedSlot = a; + } else if (a.Value == null) { + mergedSlot = b; + } else { + mergedSlot = new StackSlot (MergePointValue.MergeValues (a.Value, b.Value)); + } + + return mergedSlot; + } + + // Merge stacks together. This may return the first stack, the stack length must be the same for the two stacks. + private static Stack MergeStack (Stack a, Stack b) + { + if (a.Count != b.Count) { + // Force stacks to be of equal size to avoid crashes. + // Analysis of this method will be incorrect. + while (a.Count < b.Count) + a.Push (new StackSlot ()); + + while (b.Count < a.Count) + b.Push (new StackSlot ()); + } + + Stack newStack = new Stack (a.Count); + IEnumerator aEnum = a.GetEnumerator (); + IEnumerator bEnum = b.GetEnumerator (); + while (aEnum.MoveNext () && bEnum.MoveNext ()) { + newStack.Push (MergeStackElement (aEnum.Current, bEnum.Current)); + } + + // The new stack is reversed. Use the copy constructor to reverse it back + return new Stack (newStack); + } + + private static void ClearStack (ref Stack? stack) + { + stack = null; + } + + private static void NewKnownStack (Dictionary> knownStacks, int newOffset, Stack newStack) + { + // No need to merge in empty stacks + if (newStack.Count == 0) { + return; + } + + if (knownStacks.ContainsKey (newOffset)) { + knownStacks[newOffset] = MergeStack (knownStacks[newOffset], newStack); + } else { + knownStacks.Add (newOffset, new Stack (newStack.Reverse ())); + } + } + + private struct BasicBlockIterator + { + readonly HashSet _methodBranchTargets; + int _currentBlockIndex; + bool _foundEndOfPrevBlock; + + public BasicBlockIterator (MethodBody methodBody) + { + _methodBranchTargets = methodBody.ComputeBranchTargets (); + _currentBlockIndex = -1; + _foundEndOfPrevBlock = true; + } + + public int CurrentBlockIndex { + get { + return _currentBlockIndex; + } + } + + public int MoveNext (Instruction op) + { + if (_foundEndOfPrevBlock || _methodBranchTargets.Contains (op.Offset)) { + _currentBlockIndex++; + _foundEndOfPrevBlock = false; + } + + if (op.OpCode.IsControlFlowInstruction ()) { + _foundEndOfPrevBlock = true; + } + + return CurrentBlockIndex; + } + } + + private static void StoreMethodLocalValue ( + Dictionary valueCollection, + ValueNode? valueToStore, + KeyType collectionKey, + int curBasicBlock, + int? maxTrackedValues = null) + where KeyType : notnull + { + ValueBasicBlockPair newValue = new ValueBasicBlockPair { BasicBlockIndex = curBasicBlock }; + + ValueBasicBlockPair existingValue; + if (valueCollection.TryGetValue (collectionKey, out existingValue)) { + if (existingValue.BasicBlockIndex == curBasicBlock) { + // If the previous value was stored in the current basic block, then we can safely + // overwrite the previous value with the new one. + newValue.Value = valueToStore; + } else { + // If the previous value came from a previous basic block, then some other use of + // the local could see the previous value, so we must merge the new value with the + // old value. + newValue.Value = MergePointValue.MergeValues (existingValue.Value, valueToStore); + } + valueCollection[collectionKey] = newValue; + } else if (maxTrackedValues == null || valueCollection.Count < maxTrackedValues) { + // We're not currently tracking a value a this index, so store the value now. + newValue.Value = valueToStore; + valueCollection[collectionKey] = newValue; + } + } + + public void Scan (MethodBody methodBody) + { + MethodDefinition thisMethod = methodBody.Method; + + Dictionary locals = new Dictionary (methodBody.Variables.Count); + + Dictionary> knownStacks = new Dictionary> (); + Stack? currentStack = new Stack (methodBody.MaxStackSize); + + ScanExceptionInformation (knownStacks, methodBody); + + BasicBlockIterator blockIterator = new BasicBlockIterator (methodBody); + + MethodReturnValue = null; + foreach (Instruction operation in methodBody.Instructions) { + int curBasicBlock = blockIterator.MoveNext (operation); + + if (knownStacks.ContainsKey (operation.Offset)) { + if (currentStack == null) { + // The stack copy constructor reverses the stack + currentStack = new Stack (knownStacks[operation.Offset].Reverse ()); + } else { + currentStack = MergeStack (currentStack, knownStacks[operation.Offset]); + } + } + + if (currentStack == null) { + currentStack = new Stack (methodBody.MaxStackSize); + } + + switch (operation.OpCode.Code) { + case Code.Add: + case Code.Add_Ovf: + case Code.Add_Ovf_Un: + case Code.And: + case Code.Div: + case Code.Div_Un: + case Code.Mul: + case Code.Mul_Ovf: + case Code.Mul_Ovf_Un: + case Code.Or: + case Code.Rem: + case Code.Rem_Un: + case Code.Sub: + case Code.Sub_Ovf: + case Code.Sub_Ovf_Un: + case Code.Xor: + case Code.Cgt: + case Code.Cgt_Un: + case Code.Clt: + case Code.Clt_Un: + case Code.Shl: + case Code.Shr: + case Code.Shr_Un: + case Code.Ceq: + PopUnknown (currentStack, 2, methodBody, operation.Offset); + PushUnknown (currentStack); + break; + + case Code.Dup: + currentStack.Push (currentStack.Peek ()); + break; + + case Code.Ldnull: + currentStack.Push (new StackSlot (NullValue.Instance)); + break; + + + case Code.Ldc_I4_0: + case Code.Ldc_I4_1: + case Code.Ldc_I4_2: + case Code.Ldc_I4_3: + case Code.Ldc_I4_4: + case Code.Ldc_I4_5: + case Code.Ldc_I4_6: + case Code.Ldc_I4_7: + case Code.Ldc_I4_8: { + int value = operation.OpCode.Code - Code.Ldc_I4_0; + ConstIntValue civ = new ConstIntValue (value); + StackSlot slot = new StackSlot (civ); + currentStack.Push (slot); + } + break; + + case Code.Ldc_I4_M1: { + ConstIntValue civ = new ConstIntValue (-1); + StackSlot slot = new StackSlot (civ); + currentStack.Push (slot); + } + break; + + case Code.Ldc_I4: { + int value = (int) operation.Operand; + ConstIntValue civ = new ConstIntValue (value); + StackSlot slot = new StackSlot (civ); + currentStack.Push (slot); + } + break; + + case Code.Ldc_I4_S: { + int value = (sbyte) operation.Operand; + ConstIntValue civ = new ConstIntValue (value); + StackSlot slot = new StackSlot (civ); + currentStack.Push (slot); + } + break; + + case Code.Arglist: + case Code.Ldftn: + case Code.Sizeof: + case Code.Ldc_I8: + case Code.Ldc_R4: + case Code.Ldc_R8: + PushUnknown (currentStack); + break; + + case Code.Ldarg: + case Code.Ldarg_0: + case Code.Ldarg_1: + case Code.Ldarg_2: + case Code.Ldarg_3: + case Code.Ldarg_S: + case Code.Ldarga: + case Code.Ldarga_S: + ScanLdarg (operation, currentStack, thisMethod, methodBody); + break; + + case Code.Ldloc: + case Code.Ldloc_0: + case Code.Ldloc_1: + case Code.Ldloc_2: + case Code.Ldloc_3: + case Code.Ldloc_S: + case Code.Ldloca: + case Code.Ldloca_S: + ScanLdloc (operation, currentStack, methodBody, locals); + break; + + case Code.Ldstr: { + StackSlot slot = new StackSlot (new KnownStringValue ((string) operation.Operand)); + currentStack.Push (slot); + } + break; + + case Code.Ldtoken: + ScanLdtoken (operation, currentStack); + break; + + case Code.Ldind_I: + case Code.Ldind_I1: + case Code.Ldind_I2: + case Code.Ldind_I4: + case Code.Ldind_I8: + case Code.Ldind_R4: + case Code.Ldind_R8: + case Code.Ldind_U1: + case Code.Ldind_U2: + case Code.Ldind_U4: + case Code.Ldlen: + case Code.Ldvirtftn: + case Code.Localloc: + case Code.Refanytype: + case Code.Refanyval: + case Code.Conv_I1: + case Code.Conv_I2: + case Code.Conv_I4: + case Code.Conv_Ovf_I1: + case Code.Conv_Ovf_I1_Un: + case Code.Conv_Ovf_I2: + case Code.Conv_Ovf_I2_Un: + case Code.Conv_Ovf_I4: + case Code.Conv_Ovf_I4_Un: + case Code.Conv_Ovf_U: + case Code.Conv_Ovf_U_Un: + case Code.Conv_Ovf_U1: + case Code.Conv_Ovf_U1_Un: + case Code.Conv_Ovf_U2: + case Code.Conv_Ovf_U2_Un: + case Code.Conv_Ovf_U4: + case Code.Conv_Ovf_U4_Un: + case Code.Conv_U1: + case Code.Conv_U2: + case Code.Conv_U4: + case Code.Conv_I8: + case Code.Conv_Ovf_I8: + case Code.Conv_Ovf_I8_Un: + case Code.Conv_Ovf_U8: + case Code.Conv_Ovf_U8_Un: + case Code.Conv_U8: + case Code.Conv_I: + case Code.Conv_Ovf_I: + case Code.Conv_Ovf_I_Un: + case Code.Conv_U: + case Code.Conv_R_Un: + case Code.Conv_R4: + case Code.Conv_R8: + case Code.Ldind_Ref: + case Code.Ldobj: + case Code.Mkrefany: + case Code.Unbox: + case Code.Unbox_Any: + case Code.Box: + case Code.Neg: + case Code.Not: + PopUnknown (currentStack, 1, methodBody, operation.Offset); + PushUnknown (currentStack); + break; + + case Code.Isinst: + case Code.Castclass: + // We can consider a NOP because the value doesn't change. + // It might change to NULL, but for the purposes of dataflow analysis + // it doesn't hurt much. + break; + + case Code.Ldfld: + case Code.Ldsfld: + case Code.Ldflda: + case Code.Ldsflda: + ScanLdfld (operation, currentStack, thisMethod, methodBody); + break; + + case Code.Newarr: { + StackSlot count = PopUnknown (currentStack, 1, methodBody, operation.Offset); + currentStack.Push (new StackSlot (new ArrayValue (count.Value, (TypeReference) operation.Operand))); + } + break; + + case Code.Stelem_I: + case Code.Stelem_I1: + case Code.Stelem_I2: + case Code.Stelem_I4: + case Code.Stelem_I8: + case Code.Stelem_R4: + case Code.Stelem_R8: + case Code.Stelem_Any: + case Code.Stelem_Ref: + ScanStelem (operation, currentStack, methodBody, curBasicBlock); + break; + + case Code.Ldelem_I: + case Code.Ldelem_I1: + case Code.Ldelem_I2: + case Code.Ldelem_I4: + case Code.Ldelem_I8: + case Code.Ldelem_R4: + case Code.Ldelem_R8: + case Code.Ldelem_U1: + case Code.Ldelem_U2: + case Code.Ldelem_U4: + case Code.Ldelem_Any: + case Code.Ldelem_Ref: + case Code.Ldelema: + ScanLdelem (operation, currentStack, methodBody, curBasicBlock); + break; + + case Code.Cpblk: + case Code.Initblk: + PopUnknown (currentStack, 3, methodBody, operation.Offset); + break; + + case Code.Stfld: + case Code.Stsfld: + ScanStfld (operation, currentStack, thisMethod, methodBody); + break; + + case Code.Cpobj: + PopUnknown (currentStack, 2, methodBody, operation.Offset); + break; + + case Code.Stind_I: + case Code.Stind_I1: + case Code.Stind_I2: + case Code.Stind_I4: + case Code.Stind_I8: + case Code.Stind_R4: + case Code.Stind_R8: + case Code.Stind_Ref: + case Code.Stobj: + ScanIndirectStore (operation, currentStack, methodBody); + break; + + case Code.Initobj: + case Code.Pop: + PopUnknown (currentStack, 1, methodBody, operation.Offset); + break; + + case Code.Starg: + case Code.Starg_S: + ScanStarg (operation, currentStack, thisMethod, methodBody); + break; + + case Code.Stloc: + case Code.Stloc_S: + case Code.Stloc_0: + case Code.Stloc_1: + case Code.Stloc_2: + case Code.Stloc_3: + ScanStloc (operation, currentStack, methodBody, locals, curBasicBlock); + break; + + case Code.Constrained: + case Code.No: + case Code.Readonly: + case Code.Tail: + case Code.Unaligned: + case Code.Volatile: + break; + + case Code.Brfalse: + case Code.Brfalse_S: + case Code.Brtrue: + case Code.Brtrue_S: + PopUnknown (currentStack, 1, methodBody, operation.Offset); + NewKnownStack (knownStacks, ((Instruction) operation.Operand).Offset, currentStack); + break; + + case Code.Calli: { + var signature = (CallSite) operation.Operand; + if (signature.HasThis && !signature.ExplicitThis) { + PopUnknown (currentStack, 1, methodBody, operation.Offset); + } + + // Pop arguments + if (signature.Parameters.Count > 0) + PopUnknown (currentStack, signature.Parameters.Count, methodBody, operation.Offset); + + // Pop function pointer + PopUnknown (currentStack, 1, methodBody, operation.Offset); + + if (GetReturnTypeWithoutModifiers (signature.ReturnType).MetadataType != MetadataType.Void) + PushUnknown (currentStack); + } + break; + + case Code.Call: + case Code.Callvirt: + case Code.Newobj: + HandleCall (methodBody, operation, currentStack, curBasicBlock); + break; + + case Code.Jmp: + // Not generated by mainstream compilers + break; + + case Code.Br: + case Code.Br_S: + NewKnownStack (knownStacks, ((Instruction) operation.Operand).Offset, currentStack); + ClearStack (ref currentStack); + break; + + case Code.Leave: + case Code.Leave_S: + ClearStack (ref currentStack); + NewKnownStack (knownStacks, ((Instruction) operation.Operand).Offset, new Stack (methodBody.MaxStackSize)); + break; + + case Code.Endfilter: + case Code.Endfinally: + case Code.Rethrow: + case Code.Throw: + ClearStack (ref currentStack); + break; + + case Code.Ret: { + + bool hasReturnValue = GetReturnTypeWithoutModifiers (methodBody.Method.ReturnType).MetadataType != MetadataType.Void; + + if (currentStack.Count != (hasReturnValue ? 1 : 0)) { + WarnAboutInvalidILInMethod (methodBody, operation.Offset); + } + if (hasReturnValue) { + StackSlot retValue = PopUnknown (currentStack, 1, methodBody, operation.Offset); + MethodReturnValue = MergePointValue.MergeValues (MethodReturnValue, retValue.Value); + } + ClearStack (ref currentStack); + break; + } + + case Code.Switch: { + PopUnknown (currentStack, 1, methodBody, operation.Offset); + Instruction[] targets = (Instruction[]) operation.Operand; + foreach (Instruction target in targets) { + NewKnownStack (knownStacks, target.Offset, currentStack); + } + break; + } + + case Code.Beq: + case Code.Beq_S: + case Code.Bne_Un: + case Code.Bne_Un_S: + case Code.Bge: + case Code.Bge_S: + case Code.Bge_Un: + case Code.Bge_Un_S: + case Code.Bgt: + case Code.Bgt_S: + case Code.Bgt_Un: + case Code.Bgt_Un_S: + case Code.Ble: + case Code.Ble_S: + case Code.Ble_Un: + case Code.Ble_Un_S: + case Code.Blt: + case Code.Blt_S: + case Code.Blt_Un: + case Code.Blt_Un_S: + PopUnknown (currentStack, 2, methodBody, operation.Offset); + NewKnownStack (knownStacks, ((Instruction) operation.Operand).Offset, currentStack); + break; + } + } + } + + private static void ScanExceptionInformation (Dictionary> knownStacks, MethodBody methodBody) + { + foreach (ExceptionHandler exceptionClause in methodBody.ExceptionHandlers) { + Stack catchStack = new Stack (1); + catchStack.Push (new StackSlot ()); + + if (exceptionClause.HandlerType == ExceptionHandlerType.Filter) { + NewKnownStack (knownStacks, exceptionClause.FilterStart.Offset, catchStack); + NewKnownStack (knownStacks, exceptionClause.HandlerStart.Offset, catchStack); + } + if (exceptionClause.HandlerType == ExceptionHandlerType.Catch) { + NewKnownStack (knownStacks, exceptionClause.HandlerStart.Offset, catchStack); + } + } + } + + protected abstract ValueNode GetMethodParameterValue (MethodDefinition method, int parameterIndex); + + private void ScanLdarg (Instruction operation, Stack currentStack, MethodDefinition thisMethod, MethodBody methodBody) + { + Code code = operation.OpCode.Code; + + bool isByRef; + + // Thank you Cecil, Operand being a ParameterDefinition instead of an integer, + // (except for Ldarg_0 - Ldarg_3, where it's null) makes all of this really convenient... + // NOT. + int paramNum; + if (code >= Code.Ldarg_0 && + code <= Code.Ldarg_3) { + paramNum = code - Code.Ldarg_0; + + if (thisMethod.HasImplicitThis ()) { + if (paramNum == 0) { + isByRef = thisMethod.DeclaringType.IsValueType; + } else { + isByRef = thisMethod.Parameters[paramNum - 1].ParameterType.IsByRefOrPointer (); + } + } else { + isByRef = thisMethod.Parameters[paramNum].ParameterType.IsByRefOrPointer (); + } + } else { + var paramDefinition = (ParameterDefinition) operation.Operand; + if (thisMethod.HasImplicitThis ()) { + if (paramDefinition == methodBody.ThisParameter) { + paramNum = 0; + } else { + paramNum = paramDefinition.Index + 1; + } + } else { + paramNum = paramDefinition.Index; + } + + isByRef = paramDefinition.ParameterType.IsByRefOrPointer (); + } + + isByRef |= code == Code.Ldarga || code == Code.Ldarga_S; + + StackSlot slot = new StackSlot (GetMethodParameterValue (thisMethod, paramNum), isByRef); + currentStack.Push (slot); + } + + private void ScanStarg ( + Instruction operation, + Stack currentStack, + MethodDefinition thisMethod, + MethodBody methodBody) + { + ParameterDefinition param = (ParameterDefinition) operation.Operand; + var valueToStore = PopUnknown (currentStack, 1, methodBody, operation.Offset); + HandleStoreParameter (thisMethod, param.Sequence, operation, valueToStore.Value); + } + + private void ScanLdloc ( + Instruction operation, + Stack currentStack, + MethodBody methodBody, + Dictionary locals) + { + VariableDefinition localDef = GetLocalDef (operation, methodBody.Variables); + if (localDef == null) { + PushUnknownAndWarnAboutInvalidIL (currentStack, methodBody, operation.Offset); + return; + } + + bool isByRef = operation.OpCode.Code == Code.Ldloca || operation.OpCode.Code == Code.Ldloca_S + || localDef.VariableType.IsByRefOrPointer (); + + ValueBasicBlockPair localValue; + locals.TryGetValue (localDef, out localValue); + if (localValue.Value != null) { + ValueNode valueToPush = localValue.Value; + currentStack.Push (new StackSlot (valueToPush, isByRef)); + } else { + currentStack.Push (new StackSlot (null, isByRef)); + } + } + + void ScanLdtoken (Instruction operation, Stack currentStack) + { + if (operation.Operand is GenericParameter genericParameter) { + StackSlot slot = new StackSlot (new RuntimeTypeHandleForGenericParameterValue (genericParameter)); + currentStack.Push (slot); + return; + } + + if (operation.Operand is TypeReference typeReference) { + var resolvedReference = ResolveToTypeDefinition (typeReference); + if (resolvedReference != null) { + StackSlot slot = new StackSlot (new RuntimeTypeHandleValue (resolvedReference)); + currentStack.Push (slot); + return; + } + } else if (operation.Operand is MethodReference methodReference) { + var resolvedMethod = _context.TryResolve (methodReference); + if (resolvedMethod != null) { + StackSlot slot = new StackSlot (new RuntimeMethodHandleValue (resolvedMethod)); + currentStack.Push (slot); + return; + } + } + + PushUnknown (currentStack); + } + + private void ScanStloc ( + Instruction operation, + Stack currentStack, + MethodBody methodBody, + Dictionary locals, + int curBasicBlock) + { + StackSlot valueToStore = PopUnknown (currentStack, 1, methodBody, operation.Offset); + VariableDefinition localDef = GetLocalDef (operation, methodBody.Variables); + if (localDef == null) { + WarnAboutInvalidILInMethod (methodBody, operation.Offset); + return; + } + + StoreMethodLocalValue (locals, valueToStore.Value, localDef, curBasicBlock); + } + + private void ScanIndirectStore ( + Instruction operation, + Stack currentStack, + MethodBody methodBody) + { + StackSlot valueToStore = PopUnknown (currentStack, 1, methodBody, operation.Offset); + StackSlot destination = PopUnknown (currentStack, 1, methodBody, operation.Offset); + + foreach (var uniqueDestination in destination.Value.UniqueValues ()) { + if (uniqueDestination.Kind == ValueNodeKind.LoadField) { + HandleStoreField (methodBody.Method, ((LoadFieldValue) uniqueDestination).Field, operation, valueToStore.Value); + } else if (uniqueDestination.Kind == ValueNodeKind.MethodParameter) { + HandleStoreParameter (methodBody.Method, ((MethodParameterValue) uniqueDestination).ParameterIndex, operation, valueToStore.Value); + } + } + + } + + protected abstract ValueNode GetFieldValue (MethodDefinition method, FieldDefinition field); + + private void ScanLdfld ( + Instruction operation, + Stack currentStack, + MethodDefinition thisMethod, + MethodBody methodBody) + { + Code code = operation.OpCode.Code; + if (code == Code.Ldfld || code == Code.Ldflda) + PopUnknown (currentStack, 1, methodBody, operation.Offset); + + bool isByRef = code == Code.Ldflda || code == Code.Ldsflda; + + FieldDefinition? field = _context.TryResolve ((FieldReference) operation.Operand); + if (field != null) { + StackSlot slot = new StackSlot (GetFieldValue (thisMethod, field), isByRef); + currentStack.Push (slot); + return; + } + + PushUnknown (currentStack); + } + + protected virtual void HandleStoreField (MethodDefinition method, FieldDefinition field, Instruction operation, ValueNode? valueToStore) + { + } + + protected virtual void HandleStoreParameter (MethodDefinition method, int index, Instruction operation, ValueNode? valueToStore) + { + } + + private void ScanStfld ( + Instruction operation, + Stack currentStack, + MethodDefinition thisMethod, + MethodBody methodBody) + { + StackSlot valueToStoreSlot = PopUnknown (currentStack, 1, methodBody, operation.Offset); + if (operation.OpCode.Code == Code.Stfld) + PopUnknown (currentStack, 1, methodBody, operation.Offset); + + FieldDefinition? field = _context.TryResolve ((FieldReference) operation.Operand); + if (field != null) { + HandleStoreField (thisMethod, field, operation, valueToStoreSlot.Value); + } + } + + private static VariableDefinition GetLocalDef (Instruction operation, Collection localVariables) + { + Code code = operation.OpCode.Code; + if (code >= Code.Ldloc_0 && code <= Code.Ldloc_3) + return localVariables[code - Code.Ldloc_0]; + if (code >= Code.Stloc_0 && code <= Code.Stloc_3) + return localVariables[code - Code.Stloc_0]; + + return (VariableDefinition) operation.Operand; + } + + private ValueNodeList PopCallArguments ( + Stack currentStack, + MethodReference methodCalled, + MethodBody containingMethodBody, + bool isNewObj, int ilOffset, + out ValueNode? newObjValue) + { + newObjValue = null; + + int countToPop = 0; + if (!isNewObj && methodCalled.HasThis && !methodCalled.ExplicitThis) + countToPop++; + countToPop += methodCalled.Parameters.Count; + + ValueNodeList methodParams = new ValueNodeList (countToPop); + for (int iParam = 0; iParam < countToPop; ++iParam) { + StackSlot slot = PopUnknown (currentStack, 1, containingMethodBody, ilOffset); + methodParams.Add (slot.Value); + } + + if (isNewObj) { + newObjValue = UnknownValue.Instance; + methodParams.Add (newObjValue); + } + methodParams.Reverse (); + return methodParams; + } + + private void HandleCall ( + MethodBody callingMethodBody, + Instruction operation, + Stack currentStack, + int curBasicBlock) + { + MethodReference calledMethod = (MethodReference) operation.Operand; + + bool isNewObj = operation.OpCode.Code == Code.Newobj; + + ValueNode? newObjValue; + ValueNodeList methodParams = PopCallArguments (currentStack, calledMethod, callingMethodBody, isNewObj, + operation.Offset, out newObjValue); + + ValueNode? methodReturnValue; + bool handledFunction = HandleCall ( + callingMethodBody, + calledMethod, + operation, + methodParams, + out methodReturnValue); + + // Handle the return value or newobj result + if (!handledFunction) { + if (isNewObj) { + if (newObjValue == null) + PushUnknown (currentStack); + else + methodReturnValue = newObjValue; + } else { + if (GetReturnTypeWithoutModifiers (calledMethod.ReturnType).MetadataType != MetadataType.Void) { + methodReturnValue = UnknownValue.Instance; + } + } + } + + if (methodReturnValue != null) + currentStack.Push (new StackSlot (methodReturnValue, calledMethod.ReturnType.IsByRefOrPointer ())); + + foreach (var param in methodParams) { + if (param is ArrayValue arr) { + MarkArrayValuesAsUnknown (arr, curBasicBlock); + } + } + } + + protected static TypeReference GetReturnTypeWithoutModifiers (TypeReference returnType) + { + while (returnType is IModifierType) { + returnType = ((IModifierType) returnType).ElementType; + } + return returnType; + } + + // Array types that are dynamically accessed should resolve to System.Array instead of its element type - which is what Cecil resolves to. + // Any data flow annotations placed on a type parameter which receives an array type apply to the array itself. None of the members in its + // element type should be marked. + public TypeDefinition? ResolveToTypeDefinition (TypeReference typeReference) + { + if (typeReference is ArrayType) + return BCL.FindPredefinedType ("System", "Array", _context); + + return _context.TryResolve (typeReference); + } + + public abstract bool HandleCall ( + MethodBody callingMethodBody, + MethodReference calledMethod, + Instruction operation, + ValueNodeList methodParams, + out ValueNode? methodReturnValue); + + // Limit tracking array values to 32 values for performance reasons. There are many arrays much longer than 32 elements in .NET, but the interesting ones for the linker are nearly always less than 32 elements. + private const int MaxTrackedArrayValues = 32; + + private static void MarkArrayValuesAsUnknown (ArrayValue arrValue, int curBasicBlock) + { + // Since we can't know the current index we're storing the value at, clear all indices. + // That way we won't accidentally think we know the value at a given index when we cannot. + foreach (var knownIndex in arrValue.IndexValues.Keys) { + // Don't pass MaxTrackedArrayValues since we are only looking at keys we've already seen. + StoreMethodLocalValue (arrValue.IndexValues, UnknownValue.Instance, knownIndex, curBasicBlock); + } + } + + private void ScanStelem ( + Instruction operation, + Stack currentStack, + MethodBody methodBody, + int curBasicBlock) + { + StackSlot valueToStore = PopUnknown (currentStack, 1, methodBody, operation.Offset); + StackSlot indexToStoreAt = PopUnknown (currentStack, 1, methodBody, operation.Offset); + StackSlot arrayToStoreIn = PopUnknown (currentStack, 1, methodBody, operation.Offset); + int? indexToStoreAtInt = indexToStoreAt.Value.AsConstInt (); + foreach (var array in arrayToStoreIn.Value.UniqueValues ()) { + if (array is ArrayValue arrValue) { + if (indexToStoreAtInt == null) { + MarkArrayValuesAsUnknown (arrValue, curBasicBlock); + } else { + // When we know the index, we can record the value at that index. + StoreMethodLocalValue (arrValue.IndexValues, valueToStore.Value, indexToStoreAtInt.Value, curBasicBlock, MaxTrackedArrayValues); + } + } + } + } + + private void ScanLdelem ( + Instruction operation, + Stack currentStack, + MethodBody methodBody, + int curBasicBlock) + { + StackSlot indexToLoadFrom = PopUnknown (currentStack, 1, methodBody, operation.Offset); + StackSlot arrayToLoadFrom = PopUnknown (currentStack, 1, methodBody, operation.Offset); + if (arrayToLoadFrom.Value is not ArrayValue arr) { + PushUnknown (currentStack); + return; + } + bool isByRef = operation.OpCode.Code == Code.Ldelema; + + int? index = indexToLoadFrom.Value.AsConstInt (); + if (index == null) { + PushUnknown (currentStack); + if (isByRef) { + MarkArrayValuesAsUnknown (arr, curBasicBlock); + } + return; + } + + + ValueBasicBlockPair arrayIndexValue; + arr.IndexValues.TryGetValue (index.Value, out arrayIndexValue); + if (arrayIndexValue.Value != null) { + ValueNode valueToPush = arrayIndexValue.Value; + currentStack.Push (new StackSlot (valueToPush, isByRef)); + } else { + currentStack.Push (new StackSlot (null, isByRef)); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/README.md b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/README.md new file mode 100644 index 00000000000000..bc461d7424f012 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/README.md @@ -0,0 +1 @@ +Sources taken from https://github.com/dotnet/linker/tree/640878adf1e27cb79bea0a894033f85a84db208d/src/linker/Linker.Dataflow. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionMethodBodyScanner.cs new file mode 100644 index 00000000000000..c25f40433b8910 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionMethodBodyScanner.cs @@ -0,0 +1,2470 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using ILLink.Shared; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Linker.Steps; + +using BindingFlags = System.Reflection.BindingFlags; + +#nullable enable + +namespace Mono.Linker.Dataflow +{ + class ReflectionMethodBodyScanner : MethodBodyScanner + { + readonly MarkStep _markStep; + readonly MarkScopeStack _scopeStack; + static readonly DynamicallyAccessedMemberTypes[] AllDynamicallyAccessedMemberTypes = GetAllDynamicallyAccessedMemberTypes (); + + static DynamicallyAccessedMemberTypes[] GetAllDynamicallyAccessedMemberTypes () + { + HashSet values = Enum.GetValues (typeof (DynamicallyAccessedMemberTypes)) + .Cast () + .ToHashSet (); + if (!values.Contains (DynamicallyAccessedMemberTypesOverlay.Interfaces)) + values.Add (DynamicallyAccessedMemberTypesOverlay.Interfaces); + return values.ToArray (); + } + + public static bool RequiresReflectionMethodBodyScannerForCallSite (LinkContext context, MethodReference calledMethod) + { + MethodDefinition? methodDefinition = context.TryResolve (calledMethod); + if (methodDefinition == null) + return false; + + return GetIntrinsicIdForMethod (methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || + context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (methodDefinition) || + context.Annotations.DoesMethodRequireUnreferencedCode (methodDefinition, out _) || + methodDefinition.IsPInvokeImpl; + } + + public static bool RequiresReflectionMethodBodyScannerForMethodBody (FlowAnnotations flowAnnotations, MethodDefinition methodDefinition) + { + return + GetIntrinsicIdForMethod (methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || + flowAnnotations.RequiresDataFlowAnalysis (methodDefinition); + } + + public static bool RequiresReflectionMethodBodyScannerForAccess (LinkContext context, FieldReference field) + { + FieldDefinition? fieldDefinition = context.TryResolve (field); + if (fieldDefinition == null) + return false; + + return context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (fieldDefinition); + } + + bool ShouldEnableReflectionPatternReporting () + { + if (_markStep.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode ()) + return false; + + return true; + } + + public ReflectionMethodBodyScanner (LinkContext context, MarkStep parent, MarkScopeStack scopeStack) + : base (context) + { + _markStep = parent; + _scopeStack = scopeStack; + } + + public void ScanAndProcessReturnValue (MethodBody methodBody) + { + Scan (methodBody); + + if (GetReturnTypeWithoutModifiers (methodBody.Method.ReturnType).MetadataType != MetadataType.Void) { + var method = methodBody.Method; + var requiredMemberTypes = _context.Annotations.FlowAnnotations.GetReturnParameterAnnotation (method); + if (requiredMemberTypes != 0) { + var reflectionContext = new ReflectionPatternContext (_context, ShouldEnableReflectionPatternReporting (), _scopeStack.CurrentScope.Origin, method.MethodReturnType); + reflectionContext.AnalyzingPattern (); + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, MethodReturnValue, method.MethodReturnType); + reflectionContext.Dispose (); + } + } + } + + public void ProcessAttributeDataflow (MethodDefinition method, IList arguments) + { + int paramOffset = method.HasImplicitThis () ? 1 : 0; + + for (int i = 0; i < method.Parameters.Count; i++) { + var annotation = _context.Annotations.FlowAnnotations.GetParameterAnnotation (method, i + paramOffset); + if (annotation != DynamicallyAccessedMemberTypes.None) { + ValueNode valueNode = GetValueNodeForCustomAttributeArgument (arguments[i]); + var methodParameter = method.Parameters[i]; + var reflectionContext = new ReflectionPatternContext (_context, true, _scopeStack.CurrentScope.Origin, methodParameter); + reflectionContext.AnalyzingPattern (); + RequireDynamicallyAccessedMembers (ref reflectionContext, annotation, valueNode, methodParameter); + reflectionContext.Dispose (); + } + } + } + + public void ProcessAttributeDataflow (FieldDefinition field, CustomAttributeArgument value) + { + var annotation = _context.Annotations.FlowAnnotations.GetFieldAnnotation (field); + Debug.Assert (annotation != DynamicallyAccessedMemberTypes.None); + + ValueNode valueNode = GetValueNodeForCustomAttributeArgument (value); + var reflectionContext = new ReflectionPatternContext (_context, true, _scopeStack.CurrentScope.Origin, field); + reflectionContext.AnalyzingPattern (); + RequireDynamicallyAccessedMembers (ref reflectionContext, annotation, valueNode, field); + reflectionContext.Dispose (); + } + + ValueNode GetValueNodeForCustomAttributeArgument (CustomAttributeArgument argument) + { + ValueNode valueNode; + if (argument.Type.Name == "Type") { + TypeDefinition? referencedType = ResolveToTypeDefinition ((TypeReference) argument.Value); + if (referencedType == null) + valueNode = UnknownValue.Instance; + else + valueNode = new SystemTypeValue (referencedType); + } else if (argument.Type.MetadataType == MetadataType.String) { + valueNode = new KnownStringValue ((string) argument.Value); + } else { + // We shouldn't have gotten a non-null annotation for this from GetParameterAnnotation + throw new InvalidOperationException (); + } + + Debug.Assert (valueNode != null); + return valueNode; + } + + public void ProcessGenericArgumentDataFlow (GenericParameter genericParameter, TypeReference genericArgument) + { + var annotation = _context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (genericParameter); + Debug.Assert (annotation != DynamicallyAccessedMemberTypes.None); + + ValueNode valueNode = GetTypeValueNodeFromGenericArgument (genericArgument); + var currentScopeOrigin = _scopeStack.CurrentScope.Origin; + + var reflectionContext = new ReflectionPatternContext (_context, ShouldEnableReflectionPatternReporting (), currentScopeOrigin, genericParameter); + reflectionContext.AnalyzingPattern (); + RequireDynamicallyAccessedMembers (ref reflectionContext, annotation, valueNode, genericParameter); + reflectionContext.Dispose (); + } + + ValueNode GetTypeValueNodeFromGenericArgument (TypeReference genericArgument) + { + if (genericArgument is GenericParameter inputGenericParameter) { + // Technically this should be a new value node type as it's not a System.Type instance representation, but just the generic parameter + // That said we only use it to perform the dynamically accessed members checks and for that purpose treating it as System.Type is perfectly valid. + return new SystemTypeForGenericParameterValue (inputGenericParameter, _context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (inputGenericParameter)); + } else { + TypeDefinition? genericArgumentTypeDef = ResolveToTypeDefinition (genericArgument); + if (genericArgumentTypeDef != null) { + return new SystemTypeValue (genericArgumentTypeDef); + } else { + // If we can't resolve the generic argument, it means we can't apply potential requirements on it + // so track it as unknown value. If we later on hit this unknown value as being used somewhere + // where we need to apply requirements on it, it will generate a warning. + return UnknownValue.Instance; + } + } + } + + protected override void WarnAboutInvalidILInMethod (MethodBody method, int ilOffset) + { + // Serves as a debug helper to make sure valid IL is not considered invalid. + // + // The .NET Native compiler used to warn if it detected invalid IL during treeshaking, + // but the warnings were often triggered in autogenerated dead code of a major game engine + // and resulted in support calls. No point in warning. If the code gets exercised at runtime, + // an InvalidProgramException will likely be raised. + Debug.Fail ("Invalid IL or a bug in the scanner"); + } + + MethodReturnValue CreateMethodReturnValue (MethodReference method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes = DynamicallyAccessedMemberTypes.None) + { + return new MethodReturnValue (ResolveToTypeDefinition (method.ReturnType), dynamicallyAccessedMemberTypes, method.MethodReturnType); + } + + protected override ValueNode GetMethodParameterValue (MethodDefinition method, int parameterIndex) + { + DynamicallyAccessedMemberTypes memberTypes = _context.Annotations.FlowAnnotations.GetParameterAnnotation (method, parameterIndex); + + var staticType = + !method.HasImplicitThis () ? ResolveToTypeDefinition (method.Parameters[parameterIndex].ParameterType) : + parameterIndex == 0 ? method.DeclaringType : + ResolveToTypeDefinition (method.Parameters[parameterIndex - 1].ParameterType); + + return new MethodParameterValue (staticType, parameterIndex, memberTypes, DiagnosticUtilities.GetMethodParameterFromIndex (method, parameterIndex)); + } + + protected override ValueNode GetFieldValue (MethodDefinition method, FieldDefinition field) + { + switch (field.Name) { + case "EmptyTypes" when field.DeclaringType.IsTypeOf ("System", "Type"): { + return new ArrayValue (new ConstIntValue (0), field.DeclaringType); + } + case "Empty" when field.DeclaringType.IsTypeOf ("System", "String"): { + return new KnownStringValue (string.Empty); + } + + default: { + DynamicallyAccessedMemberTypes memberTypes = _context.Annotations.FlowAnnotations.GetFieldAnnotation (field); + return new LoadFieldValue (ResolveToTypeDefinition (field.FieldType), field, memberTypes); + } + } + } + + protected override void HandleStoreField (MethodDefinition method, FieldDefinition field, Instruction operation, ValueNode? valueToStore) + { + var requiredMemberTypes = _context.Annotations.FlowAnnotations.GetFieldAnnotation (field); + if (requiredMemberTypes != 0) { + _scopeStack.UpdateCurrentScopeInstructionOffset (operation.Offset); + var reflectionContext = new ReflectionPatternContext (_context, ShouldEnableReflectionPatternReporting (), _scopeStack.CurrentScope.Origin, field, operation); + reflectionContext.AnalyzingPattern (); + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, valueToStore, field); + reflectionContext.Dispose (); + } + } + + protected override void HandleStoreParameter (MethodDefinition method, int index, Instruction operation, ValueNode? valueToStore) + { + var requiredMemberTypes = _context.Annotations.FlowAnnotations.GetParameterAnnotation (method, index); + if (requiredMemberTypes != 0) { + ParameterDefinition parameter = method.Parameters[index - (method.HasImplicitThis () ? 1 : 0)]; + _scopeStack.UpdateCurrentScopeInstructionOffset (operation.Offset); + var reflectionContext = new ReflectionPatternContext (_context, ShouldEnableReflectionPatternReporting (), _scopeStack.CurrentScope.Origin, parameter, operation); + reflectionContext.AnalyzingPattern (); + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, valueToStore, parameter); + reflectionContext.Dispose (); + } + } + + enum IntrinsicId + { + None = 0, + IntrospectionExtensions_GetTypeInfo, + Type_GetTypeFromHandle, + Type_get_TypeHandle, + Object_GetType, + TypeDelegator_Ctor, + Array_Empty, + TypeInfo_AsType, + MethodBase_GetMethodFromHandle, + + // Anything above this marker will require the method to be run through + // the reflection body scanner. + RequiresReflectionBodyScanner_Sentinel = 1000, + Type_MakeGenericType, + Type_GetType, + Type_GetConstructor, + Type_GetConstructors, + Type_GetMethod, + Type_GetMethods, + Type_GetField, + Type_GetFields, + Type_GetProperty, + Type_GetProperties, + Type_GetEvent, + Type_GetEvents, + Type_GetNestedType, + Type_GetNestedTypes, + Type_GetMember, + Type_GetMembers, + Type_GetInterface, + Type_get_AssemblyQualifiedName, + Type_get_UnderlyingSystemType, + Type_get_BaseType, + Expression_Call, + Expression_Field, + Expression_Property, + Expression_New, + Activator_CreateInstance_Type, + Activator_CreateInstance_AssemblyName_TypeName, + Activator_CreateInstanceFrom, + Activator_CreateInstanceOfT, + AppDomain_CreateInstance, + AppDomain_CreateInstanceAndUnwrap, + AppDomain_CreateInstanceFrom, + AppDomain_CreateInstanceFromAndUnwrap, + Assembly_CreateInstance, + RuntimeReflectionExtensions_GetRuntimeEvent, + RuntimeReflectionExtensions_GetRuntimeField, + RuntimeReflectionExtensions_GetRuntimeMethod, + RuntimeReflectionExtensions_GetRuntimeProperty, + RuntimeHelpers_RunClassConstructor, + MethodInfo_MakeGenericMethod, + } + + static IntrinsicId GetIntrinsicIdForMethod (MethodDefinition calledMethod) + { + return calledMethod.Name switch { + // static System.Reflection.IntrospectionExtensions.GetTypeInfo (Type type) + "GetTypeInfo" when calledMethod.IsDeclaredOnType ("System.Reflection", "IntrospectionExtensions") => IntrinsicId.IntrospectionExtensions_GetTypeInfo, + + // System.Reflection.TypeInfo.AsType () + "AsType" when calledMethod.IsDeclaredOnType ("System.Reflection", "TypeInfo") => IntrinsicId.TypeInfo_AsType, + + // System.Type.GetTypeInfo (Type type) + "GetTypeFromHandle" when calledMethod.IsDeclaredOnType ("System", "Type") => IntrinsicId.Type_GetTypeFromHandle, + + // System.Type.GetTypeHandle (Type type) + "get_TypeHandle" when calledMethod.IsDeclaredOnType ("System", "Type") => IntrinsicId.Type_get_TypeHandle, + + // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle) + // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) + "GetMethodFromHandle" when calledMethod.IsDeclaredOnType ("System.Reflection", "MethodBase") + && calledMethod.HasParameterOfType (0, "System", "RuntimeMethodHandle") + && (calledMethod.Parameters.Count == 1 || calledMethod.Parameters.Count == 2) + => IntrinsicId.MethodBase_GetMethodFromHandle, + + // static System.Type.MakeGenericType (Type [] typeArguments) + "MakeGenericType" when calledMethod.IsDeclaredOnType ("System", "Type") => IntrinsicId.Type_MakeGenericType, + + // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeEvent (this Type type, string name) + "GetRuntimeEvent" when calledMethod.IsDeclaredOnType ("System.Reflection", "RuntimeReflectionExtensions") + && calledMethod.HasParameterOfType (0, "System", "Type") + && calledMethod.HasParameterOfType (1, "System", "String") + => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent, + + // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeField (this Type type, string name) + "GetRuntimeField" when calledMethod.IsDeclaredOnType ("System.Reflection", "RuntimeReflectionExtensions") + && calledMethod.HasParameterOfType (0, "System", "Type") + && calledMethod.HasParameterOfType (1, "System", "String") + => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField, + + // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeMethod (this Type type, string name, Type[] parameters) + "GetRuntimeMethod" when calledMethod.IsDeclaredOnType ("System.Reflection", "RuntimeReflectionExtensions") + && calledMethod.HasParameterOfType (0, "System", "Type") + && calledMethod.HasParameterOfType (1, "System", "String") + => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod, + + // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeProperty (this Type type, string name) + "GetRuntimeProperty" when calledMethod.IsDeclaredOnType ("System.Reflection", "RuntimeReflectionExtensions") + && calledMethod.HasParameterOfType (0, "System", "Type") + && calledMethod.HasParameterOfType (1, "System", "String") + => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty, + + // static System.Linq.Expressions.Expression.Call (Type, String, Type[], Expression[]) + "Call" when calledMethod.IsDeclaredOnType ("System.Linq.Expressions", "Expression") + && calledMethod.HasParameterOfType (0, "System", "Type") + && calledMethod.Parameters.Count == 4 + => IntrinsicId.Expression_Call, + + // static System.Linq.Expressions.Expression.Field (Expression, Type, String) + "Field" when calledMethod.IsDeclaredOnType ("System.Linq.Expressions", "Expression") + && calledMethod.HasParameterOfType (1, "System", "Type") + && calledMethod.Parameters.Count == 3 + => IntrinsicId.Expression_Field, + + // static System.Linq.Expressions.Expression.Property (Expression, Type, String) + // static System.Linq.Expressions.Expression.Property (Expression, MethodInfo) + "Property" when calledMethod.IsDeclaredOnType ("System.Linq.Expressions", "Expression") + && ((calledMethod.HasParameterOfType (1, "System", "Type") && calledMethod.Parameters.Count == 3) + || (calledMethod.HasParameterOfType (1, "System.Reflection", "MethodInfo") && calledMethod.Parameters.Count == 2)) + => IntrinsicId.Expression_Property, + + // static System.Linq.Expressions.Expression.New (Type) + "New" when calledMethod.IsDeclaredOnType ("System.Linq.Expressions", "Expression") + && calledMethod.HasParameterOfType (0, "System", "Type") + && calledMethod.Parameters.Count == 1 + => IntrinsicId.Expression_New, + + // static System.Type.GetType (string) + // static System.Type.GetType (string, Boolean) + // static System.Type.GetType (string, Boolean, Boolean) + // static System.Type.GetType (string, Func, Func) + // static System.Type.GetType (string, Func, Func, Boolean) + // static System.Type.GetType (string, Func, Func, Boolean, Boolean) + "GetType" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System", "String") + => IntrinsicId.Type_GetType, + + // System.Type.GetConstructor (Type[]) + // System.Type.GetConstructor (BindingFlags, Type[]) + // System.Type.GetConstructor (BindingFlags, Binder, Type[], ParameterModifier []) + // System.Type.GetConstructor (BindingFlags, Binder, CallingConventions, Type[], ParameterModifier []) + "GetConstructor" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasThis + => IntrinsicId.Type_GetConstructor, + + // System.Type.GetConstructors (BindingFlags) + "GetConstructors" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") + && calledMethod.Parameters.Count == 1 + && calledMethod.HasThis + => IntrinsicId.Type_GetConstructors, + + // System.Type.GetMethod (string) + // System.Type.GetMethod (string, BindingFlags) + // System.Type.GetMethod (string, Type[]) + // System.Type.GetMethod (string, Type[], ParameterModifier[]) + // System.Type.GetMethod (string, BindingFlags, Type[]) + // System.Type.GetMethod (string, BindingFlags, Binder, Type[], ParameterModifier[]) + // System.Type.GetMethod (string, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[]) + // System.Type.GetMethod (string, int, Type[]) + // System.Type.GetMethod (string, int, Type[], ParameterModifier[]?) + // System.Type.GetMethod (string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?) + // System.Type.GetMethod (string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?) + "GetMethod" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasThis + => IntrinsicId.Type_GetMethod, + + // System.Type.GetMethods (BindingFlags) + "GetMethods" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") + && calledMethod.Parameters.Count == 1 + && calledMethod.HasThis + => IntrinsicId.Type_GetMethods, + + // System.Type.GetField (string) + // System.Type.GetField (string, BindingFlags) + "GetField" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasThis + => IntrinsicId.Type_GetField, + + // System.Type.GetFields (BindingFlags) + "GetFields" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") + && calledMethod.Parameters.Count == 1 + && calledMethod.HasThis + => IntrinsicId.Type_GetFields, + + // System.Type.GetEvent (string) + // System.Type.GetEvent (string, BindingFlags) + "GetEvent" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasThis + => IntrinsicId.Type_GetEvent, + + // System.Type.GetEvents (BindingFlags) + "GetEvents" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") + && calledMethod.Parameters.Count == 1 + && calledMethod.HasThis + => IntrinsicId.Type_GetEvents, + + // System.Type.GetNestedType (string) + // System.Type.GetNestedType (string, BindingFlags) + "GetNestedType" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasThis + => IntrinsicId.Type_GetNestedType, + + // System.Type.GetNestedTypes (BindingFlags) + "GetNestedTypes" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") + && calledMethod.Parameters.Count == 1 + && calledMethod.HasThis + => IntrinsicId.Type_GetNestedTypes, + + // System.Type.GetMember (String) + // System.Type.GetMember (String, BindingFlags) + // System.Type.GetMember (String, MemberTypes, BindingFlags) + "GetMember" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasThis + && (calledMethod.Parameters.Count == 1 || + (calledMethod.Parameters.Count == 2 && calledMethod.HasParameterOfType (1, "System.Reflection", "BindingFlags")) || + (calledMethod.Parameters.Count == 3 && calledMethod.HasParameterOfType (2, "System.Reflection", "BindingFlags"))) + => IntrinsicId.Type_GetMember, + + // System.Type.GetMembers (BindingFlags) + "GetMembers" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") + && calledMethod.Parameters.Count == 1 + && calledMethod.HasThis + => IntrinsicId.Type_GetMembers, + + // System.Type.GetInterface (string) + // System.Type.GetInterface (string, bool) + "GetInterface" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasThis + && (calledMethod.Parameters.Count == 1 || + (calledMethod.Parameters.Count == 2 && calledMethod.HasParameterOfType (1, "System", "Boolean"))) + => IntrinsicId.Type_GetInterface, + + // System.Type.AssemblyQualifiedName + "get_AssemblyQualifiedName" when calledMethod.IsDeclaredOnType ("System", "Type") + && !calledMethod.HasParameters + && calledMethod.HasThis + => IntrinsicId.Type_get_AssemblyQualifiedName, + + // System.Type.UnderlyingSystemType + "get_UnderlyingSystemType" when calledMethod.IsDeclaredOnType ("System", "Type") + && !calledMethod.HasParameters + && calledMethod.HasThis + => IntrinsicId.Type_get_UnderlyingSystemType, + + // System.Type.BaseType + "get_BaseType" when calledMethod.IsDeclaredOnType ("System", "Type") + && !calledMethod.HasParameters + && calledMethod.HasThis + => IntrinsicId.Type_get_BaseType, + + // System.Type.GetProperty (string) + // System.Type.GetProperty (string, BindingFlags) + // System.Type.GetProperty (string, Type) + // System.Type.GetProperty (string, Type[]) + // System.Type.GetProperty (string, Type, Type[]) + // System.Type.GetProperty (string, Type, Type[], ParameterModifier[]) + // System.Type.GetProperty (string, BindingFlags, Binder, Type, Type[], ParameterModifier[]) + "GetProperty" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasThis + => IntrinsicId.Type_GetProperty, + + // System.Type.GetProperties (BindingFlags) + "GetProperties" when calledMethod.IsDeclaredOnType ("System", "Type") + && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") + && calledMethod.Parameters.Count == 1 + && calledMethod.HasThis + => IntrinsicId.Type_GetProperties, + + // static System.Object.GetType () + "GetType" when calledMethod.IsDeclaredOnType ("System", "Object") + => IntrinsicId.Object_GetType, + + ".ctor" when calledMethod.IsDeclaredOnType ("System.Reflection", "TypeDelegator") + && calledMethod.HasParameterOfType (0, "System", "Type") + => IntrinsicId.TypeDelegator_Ctor, + + "Empty" when calledMethod.IsDeclaredOnType ("System", "Array") + => IntrinsicId.Array_Empty, + + // static System.Activator.CreateInstance (System.Type type) + // static System.Activator.CreateInstance (System.Type type, bool nonPublic) + // static System.Activator.CreateInstance (System.Type type, params object?[]? args) + // static System.Activator.CreateInstance (System.Type type, object?[]? args, object?[]? activationAttributes) + // static System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture) + // static System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } + "CreateInstance" when calledMethod.IsDeclaredOnType ("System", "Activator") + && !calledMethod.ContainsGenericParameter + && calledMethod.HasParameterOfType (0, "System", "Type") + => IntrinsicId.Activator_CreateInstance_Type, + + // static System.Activator.CreateInstance (string assemblyName, string typeName) + // static System.Activator.CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) + // static System.Activator.CreateInstance (string assemblyName, string typeName, object?[]? activationAttributes) + "CreateInstance" when calledMethod.IsDeclaredOnType ("System", "Activator") + && !calledMethod.ContainsGenericParameter + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasParameterOfType (1, "System", "String") + => IntrinsicId.Activator_CreateInstance_AssemblyName_TypeName, + + // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName) + // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) + "CreateInstanceFrom" when calledMethod.IsDeclaredOnType ("System", "Activator") + && !calledMethod.ContainsGenericParameter + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasParameterOfType (1, "System", "String") + => IntrinsicId.Activator_CreateInstanceFrom, + + // static T System.Activator.CreateInstance () + "CreateInstance" when calledMethod.IsDeclaredOnType ("System", "Activator") + && calledMethod.ContainsGenericParameter + && calledMethod.GenericParameters.Count == 1 + && calledMethod.Parameters.Count == 0 + => IntrinsicId.Activator_CreateInstanceOfT, + + // System.AppDomain.CreateInstance (string assemblyName, string typeName) + // System.AppDomain.CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // System.AppDomain.CreateInstance (string assemblyName, string typeName, object? []? activationAttributes) + "CreateInstance" when calledMethod.IsDeclaredOnType ("System", "AppDomain") + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasParameterOfType (1, "System", "String") + => IntrinsicId.AppDomain_CreateInstance, + + // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName) + // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName, object? []? activationAttributes) + "CreateInstanceAndUnwrap" when calledMethod.IsDeclaredOnType ("System", "AppDomain") + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasParameterOfType (1, "System", "String") + => IntrinsicId.AppDomain_CreateInstanceAndUnwrap, + + // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName) + // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) + "CreateInstanceFrom" when calledMethod.IsDeclaredOnType ("System", "AppDomain") + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasParameterOfType (1, "System", "String") + => IntrinsicId.AppDomain_CreateInstanceFrom, + + // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName) + // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, object? []? activationAttributes) + "CreateInstanceFromAndUnwrap" when calledMethod.IsDeclaredOnType ("System", "AppDomain") + && calledMethod.HasParameterOfType (0, "System", "String") + && calledMethod.HasParameterOfType (1, "System", "String") + => IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap, + + // System.Reflection.Assembly.CreateInstance (string typeName) + // System.Reflection.Assembly.CreateInstance (string typeName, bool ignoreCase) + // System.Reflection.Assembly.CreateInstance (string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder? binder, object []? args, CultureInfo? culture, object []? activationAttributes) + "CreateInstance" when calledMethod.IsDeclaredOnType ("System.Reflection", "Assembly") + && calledMethod.HasParameterOfType (0, "System", "String") + => IntrinsicId.Assembly_CreateInstance, + + // System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) + "RunClassConstructor" when calledMethod.IsDeclaredOnType ("System.Runtime.CompilerServices", "RuntimeHelpers") + && calledMethod.HasParameterOfType (0, "System", "RuntimeTypeHandle") + => IntrinsicId.RuntimeHelpers_RunClassConstructor, + + // System.Reflection.MethodInfo.MakeGenericMethod (Type[] typeArguments) + "MakeGenericMethod" when calledMethod.IsDeclaredOnType ("System.Reflection", "MethodInfo") + && calledMethod.HasThis + && calledMethod.Parameters.Count == 1 + => IntrinsicId.MethodInfo_MakeGenericMethod, + + _ => IntrinsicId.None, + }; + } + + public override bool HandleCall (MethodBody callingMethodBody, MethodReference calledMethod, Instruction operation, ValueNodeList methodParams, out ValueNode? methodReturnValue) + { + methodReturnValue = null; + + var reflectionProcessed = _markStep.ProcessReflectionDependency (callingMethodBody, operation); + if (reflectionProcessed) + return false; + + var callingMethodDefinition = callingMethodBody.Method; + var calledMethodDefinition = _context.TryResolve (calledMethod); + if (calledMethodDefinition == null) + return false; + + _scopeStack.UpdateCurrentScopeInstructionOffset (operation.Offset); + var reflectionContext = new ReflectionPatternContext ( + _context, + ShouldEnableReflectionPatternReporting (), + _scopeStack.CurrentScope.Origin, + calledMethodDefinition, + operation); + + DynamicallyAccessedMemberTypes returnValueDynamicallyAccessedMemberTypes = 0; + + try { + + bool requiresDataFlowAnalysis = _context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (calledMethodDefinition); + returnValueDynamicallyAccessedMemberTypes = requiresDataFlowAnalysis ? + _context.Annotations.FlowAnnotations.GetReturnParameterAnnotation (calledMethodDefinition) : 0; + + switch (GetIntrinsicIdForMethod (calledMethodDefinition)) { + case IntrinsicId.IntrospectionExtensions_GetTypeInfo: { + // typeof(Foo).GetTypeInfo()... will be commonly present in code targeting + // the dead-end reflection refactoring. The call doesn't do anything and we + // don't want to lose the annotation. + methodReturnValue = methodParams[0]; + } + break; + + case IntrinsicId.TypeInfo_AsType: { + // someType.AsType()... will be commonly present in code targeting + // the dead-end reflection refactoring. The call doesn't do anything and we + // don't want to lose the annotation. + methodReturnValue = methodParams[0]; + } + break; + + case IntrinsicId.TypeDelegator_Ctor: { + // This is an identity function for analysis purposes + if (operation.OpCode == OpCodes.Newobj) + methodReturnValue = methodParams[1]; + } + break; + + case IntrinsicId.Array_Empty: { + methodReturnValue = new ArrayValue (new ConstIntValue (0), ((GenericInstanceMethod) calledMethod).GenericArguments[0]); + } + break; + + case IntrinsicId.Type_GetTypeFromHandle: { + // Infrastructure piece to support "typeof(Foo)" + if (methodParams[0] is RuntimeTypeHandleValue typeHandle) + methodReturnValue = new SystemTypeValue (typeHandle.TypeRepresented); + else if (methodParams[0] is RuntimeTypeHandleForGenericParameterValue typeHandleForGenericParameter) { + methodReturnValue = new SystemTypeForGenericParameterValue ( + typeHandleForGenericParameter.GenericParameter, + _context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (typeHandleForGenericParameter.GenericParameter)); + } + } + break; + + case IntrinsicId.Type_get_TypeHandle: { + foreach (var value in methodParams[0].UniqueValues ()) { + if (value is SystemTypeValue typeValue) + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new RuntimeTypeHandleValue (typeValue.TypeRepresented)); + else if (value == NullValue.Instance) + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, value); + else + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, UnknownValue.Instance); + } + } + break; + + // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle) + // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) + case IntrinsicId.MethodBase_GetMethodFromHandle: { + // Infrastructure piece to support "ldtoken method -> GetMethodFromHandle" + if (methodParams[0] is RuntimeMethodHandleValue methodHandle) + methodReturnValue = new SystemReflectionMethodBaseValue (methodHandle.MethodRepresented); + } + break; + + // + // System.Type + // + // Type MakeGenericType (params Type[] typeArguments) + // + case IntrinsicId.Type_MakeGenericType: { + reflectionContext.AnalyzingPattern (); + foreach (var value in methodParams[0].UniqueValues ()) { + if (value is SystemTypeValue typeValue) { + if (AnalyzeGenericInstantiationTypeArray (methodParams[1], ref reflectionContext, calledMethodDefinition, typeValue.TypeRepresented.GenericParameters)) { + reflectionContext.RecordHandledPattern (); + } else { + bool hasUncheckedAnnotation = false; + foreach (var genericParameter in typeValue.TypeRepresented.GenericParameters) { + if (_context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None || + (genericParameter.HasDefaultConstructorConstraint && !typeValue.TypeRepresented.IsTypeOf ("System", "Nullable`1"))) { + // If we failed to analyze the array, we go through the analyses again + // and intentionally ignore one particular annotation: + // Special case: Nullable where T : struct + // The struct constraint in C# implies new() constraints, but Nullable doesn't make a use of that part. + // There are several places even in the framework where typeof(Nullable<>).MakeGenericType would warn + // without any good reason to do so. + hasUncheckedAnnotation = true; + break; + } + } + if (hasUncheckedAnnotation) { + reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericType, + new DiagnosticString (DiagnosticId.MakeGenericType).GetMessage (calledMethodDefinition.GetDisplayName ())); + } + } + + // We haven't found any generic parameters with annotations, so there's nothing to validate. + reflectionContext.RecordHandledPattern (); + } else if (value == NullValue.Instance) + reflectionContext.RecordHandledPattern (); + else { + // We have no way to "include more" to fix this if we don't know, so we have to warn + reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericType, + new DiagnosticString (DiagnosticId.MakeGenericType).GetMessage (calledMethodDefinition.GetDisplayName ())); + } + } + + // We don't want to lose track of the type + // in case this is e.g. Activator.CreateInstance(typeof(Foo<>).MakeGenericType(...)); + methodReturnValue = methodParams[0]; + } + break; + + // + // System.Reflection.RuntimeReflectionExtensions + // + // static GetRuntimeEvent (this Type type, string name) + // static GetRuntimeField (this Type type, string name) + // static GetRuntimeMethod (this Type type, string name, Type[] parameters) + // static GetRuntimeProperty (this Type type, string name) + // + case var getRuntimeMember when getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: { + + reflectionContext.AnalyzingPattern (); + BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + DynamicallyAccessedMemberTypes requiredMemberTypes = getRuntimeMember switch { + IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent => DynamicallyAccessedMemberTypes.PublicEvents, + IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField => DynamicallyAccessedMemberTypes.PublicFields, + IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod => DynamicallyAccessedMemberTypes.PublicMethods, + IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty => DynamicallyAccessedMemberTypes.PublicProperties, + _ => throw new InternalErrorException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is of unexpected member type."), + }; + + foreach (var value in methodParams[0].UniqueValues ()) { + if (value is SystemTypeValue systemTypeValue) { + foreach (var stringParam in methodParams[1].UniqueValues ()) { + if (stringParam is KnownStringValue stringValue) { + switch (getRuntimeMember) { + case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent: + MarkEventsOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, e => e.Name == stringValue.Contents, bindingFlags); + reflectionContext.RecordHandledPattern (); + break; + case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField: + MarkFieldsOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, f => f.Name == stringValue.Contents, bindingFlags); + reflectionContext.RecordHandledPattern (); + break; + case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod: + ProcessGetMethodByName (ref reflectionContext, systemTypeValue.TypeRepresented, stringValue.Contents, bindingFlags, ref methodReturnValue); + reflectionContext.RecordHandledPattern (); + break; + case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: + MarkPropertiesOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, p => p.Name == stringValue.Contents, bindingFlags); + reflectionContext.RecordHandledPattern (); + break; + default: + throw new InternalErrorException ($"Error processing reflection call '{calledMethod.GetDisplayName ()}' inside {callingMethodDefinition.GetDisplayName ()}. Unexpected member kind."); + } + } else { + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethod.Parameters[0]); + } + } + } else { + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethod.Parameters[0]); + } + } + } + break; + + // + // System.Linq.Expressions.Expression + // + // static Call (Type, String, Type[], Expression[]) + // + case IntrinsicId.Expression_Call: { + reflectionContext.AnalyzingPattern (); + BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; + + bool hasTypeArguments = (methodParams[2] as ArrayValue)?.Size.AsConstInt () != 0; + foreach (var value in methodParams[0].UniqueValues ()) { + if (value is SystemTypeValue systemTypeValue) { + foreach (var stringParam in methodParams[1].UniqueValues ()) { + if (stringParam is KnownStringValue stringValue) { + foreach (var method in systemTypeValue.TypeRepresented.GetMethodsOnTypeHierarchy (_context, m => m.Name == stringValue.Contents, bindingFlags)) { + ValidateGenericMethodInstantiation (ref reflectionContext, method, methodParams[2], calledMethod); + MarkMethod (ref reflectionContext, method); + } + + reflectionContext.RecordHandledPattern (); + } else { + if (hasTypeArguments) { + // We don't know what method the `MakeGenericMethod` was called on, so we have to assume + // that the method may have requirements which we can't fullfil -> warn. + reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericMethod, + new DiagnosticString (DiagnosticId.MakeGenericMethod).GetMessage (DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod))); + } + + RequireDynamicallyAccessedMembers ( + ref reflectionContext, + GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags), + value, + calledMethod.Parameters[0]); + } + } + } else { + if (hasTypeArguments) { + // We don't know what method the `MakeGenericMethod` was called on, so we have to assume + // that the method may have requirements which we can't fullfil -> warn. + reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericMethod, + new DiagnosticString (DiagnosticId.MakeGenericMethod).GetMessage (DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod))); + } + + RequireDynamicallyAccessedMembers ( + ref reflectionContext, + GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags), + value, + calledMethod.Parameters[0]); + } + } + } + break; + + // + // System.Linq.Expressions.Expression + // + // static Property (Expression, MethodInfo) + // + case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType (1, "System.Reflection", "MethodInfo"): { + reflectionContext.AnalyzingPattern (); + + foreach (var value in methodParams[1].UniqueValues ()) { + if (value is SystemReflectionMethodBaseValue methodBaseValue) { + // We have one of the accessors for the property. The Expression.Property will in this case search + // for the matching PropertyInfo and store that. So to be perfectly correct we need to mark the + // respective PropertyInfo as "accessed via reflection". + var propertyDefinition = methodBaseValue.MethodRepresented.GetProperty (); + if (propertyDefinition != null) { + MarkProperty (ref reflectionContext, propertyDefinition); + continue; + } + } else if (value == NullValue.Instance) { + reflectionContext.RecordHandledPattern (); + continue; + } + + // In all other cases we may not even know which type this is about, so there's nothing we can do + // report it as a warning. + reflectionContext.RecordUnrecognizedPattern ( + 2103, string.Format (Resources.Strings.IL2103, + DiagnosticUtilities.GetParameterNameForErrorMessage (calledMethod.Parameters[1]), + DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod))); + } + } + break; + + // + // System.Linq.Expressions.Expression + // + // static Field (Expression, Type, String) + // static Property (Expression, Type, String) + // + case var fieldOrPropertyInstrinsic when fieldOrPropertyInstrinsic == IntrinsicId.Expression_Field || fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property: { + reflectionContext.AnalyzingPattern (); + DynamicallyAccessedMemberTypes memberTypes = fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property + ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties + : DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields; + + foreach (var value in methodParams[1].UniqueValues ()) { + if (value is SystemTypeValue systemTypeValue) { + foreach (var stringParam in methodParams[2].UniqueValues ()) { + if (stringParam is KnownStringValue stringValue) { + BindingFlags bindingFlags = methodParams[0]?.Kind == ValueNodeKind.Null ? BindingFlags.Static : BindingFlags.Default; + if (fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property) { + MarkPropertiesOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, filter: p => p.Name == stringValue.Contents, bindingFlags); + } else { + MarkFieldsOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, filter: f => f.Name == stringValue.Contents, bindingFlags); + } + + reflectionContext.RecordHandledPattern (); + } else { + RequireDynamicallyAccessedMembers (ref reflectionContext, memberTypes, value, calledMethod.Parameters[2]); + } + } + } else { + RequireDynamicallyAccessedMembers (ref reflectionContext, memberTypes, value, calledMethod.Parameters[1]); + } + } + } + break; + + // + // System.Linq.Expressions.Expression + // + // static New (Type) + // + case IntrinsicId.Expression_New: { + reflectionContext.AnalyzingPattern (); + + foreach (var value in methodParams[0].UniqueValues ()) { + if (value is SystemTypeValue systemTypeValue) { + MarkConstructorsOnType (ref reflectionContext, systemTypeValue.TypeRepresented, null, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + reflectionContext.RecordHandledPattern (); + } else { + RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, value, calledMethod.Parameters[0]); + } + } + } + break; + + // + // System.Object + // + // GetType() + // + case IntrinsicId.Object_GetType: { + foreach (var valueNode in methodParams[0].UniqueValues ()) { + // Note that valueNode can be statically typed in IL as some generic argument type. + // For example: + // void Method(T instance) { instance.GetType().... } + // Currently this case will end up with null StaticType - since there's no typedef for the generic argument type. + // But it could be that T is annotated with for example PublicMethods: + // void Method<[DAM(PublicMethods)] T>(T instance) { instance.GetType().GetMethod("Test"); } + // In this case it's in theory possible to handle it, by treating the T basically as a base class + // for the actual type of "instance". But the analysis for this would be pretty complicated (as the marking + // has to happen on the callsite, which doesn't know that GetType() will be used...). + // For now we're intentionally ignoring this case - it will produce a warning. + // The counter example is: + // Method(new Derived); + // In this case to get correct results, trimmer would have to mark all public methods on Derived. Which + // currently it won't do. + + TypeDefinition? staticType = valueNode.StaticType; + if (staticType is null) { + // We don't know anything about the type GetType was called on. Track this as a usual result of a method call without any annotations + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod)); + } else if (staticType.IsSealed || staticType.IsTypeOf ("System", "Delegate")) { + // We can treat this one the same as if it was a typeof() expression + + // We can allow Object.GetType to be modeled as System.Delegate because we keep all methods + // on delegates anyway so reflection on something this approximation would miss is actually safe. + + // We ignore the fact that the type can be annotated (see below for handling of annotated types) + // This means the annotations (if any) won't be applied - instead we rely on the exact knowledge + // of the type. So for example even if the type is annotated with PublicMethods + // but the code calls GetProperties on it - it will work - mark properties, don't mark methods + // since we ignored the fact that it's annotated. + // This can be seen a little bit as a violation of the annotation, but we already have similar cases + // where a parameter is annotated and if something in the method sets a specific known type to it + // we will also make it just work, even if the annotation doesn't match the usage. + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (staticType)); + } else { + reflectionContext.AnalyzingPattern (); + + // Make sure the type is marked (this will mark it as used via reflection, which is sort of true) + // This should already be true for most cases (method params, fields, ...), but just in case + MarkType (ref reflectionContext, staticType); + + var annotation = _markStep.DynamicallyAccessedMembersTypeHierarchy + .ApplyDynamicallyAccessedMembersToTypeHierarchy (this, staticType); + + reflectionContext.RecordHandledPattern (); + + // Return a value which is "unknown type" with annotation. For now we'll use the return value node + // for the method, which means we're loosing the information about which staticType this + // started with. For now we don't need it, but we can add it later on. + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod, annotation)); + } + } + } + break; + + // + // System.Type + // + // GetType (string) + // GetType (string, Boolean) + // GetType (string, Boolean, Boolean) + // GetType (string, Func, Func) + // GetType (string, Func, Func, Boolean) + // GetType (string, Func, Func, Boolean, Boolean) + // + case IntrinsicId.Type_GetType: { + reflectionContext.AnalyzingPattern (); + + var parameters = calledMethod.Parameters; + if ((parameters.Count == 3 && parameters[2].ParameterType.MetadataType == MetadataType.Boolean && methodParams[2].AsConstInt () != 0) || + (parameters.Count == 5 && methodParams[4].AsConstInt () != 0)) { + reflectionContext.RecordUnrecognizedPattern (2096, $"Call to '{calledMethod.GetDisplayName ()}' can perform case insensitive lookup of the type, currently ILLink can not guarantee presence of all the matching types"); + break; + } + foreach (var typeNameValue in methodParams[0].UniqueValues ()) { + if (typeNameValue is KnownStringValue knownStringValue) { + TypeReference foundTypeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents, callingMethodDefinition, out AssemblyDefinition typeAssembly, false); + TypeDefinition? foundType = ResolveToTypeDefinition (foundTypeRef); + if (foundType == null) { + // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. + reflectionContext.RecordHandledPattern (); + } else { + reflectionContext.RecordRecognizedPattern (foundType, () => _markStep.MarkTypeVisibleToReflection (foundTypeRef, foundType, new DependencyInfo (DependencyKind.AccessedViaReflection, callingMethodDefinition))); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (foundType)); + _context.MarkingHelpers.MarkMatchingExportedType (foundType, typeAssembly, new DependencyInfo (DependencyKind.AccessedViaReflection, foundType)); + } + } else if (typeNameValue == NullValue.Instance) { + reflectionContext.RecordHandledPattern (); + } else if (typeNameValue is LeafValueWithDynamicallyAccessedMemberNode valueWithDynamicallyAccessedMember && valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes != 0) { + // Propagate the annotation from the type name to the return value. Annotation on a string value will be fullfilled whenever a value is assigned to the string with annotation. + // So while we don't know which type it is, we can guarantee that it will fulfill the annotation. + reflectionContext.RecordHandledPattern (); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethodDefinition, valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes)); + } else { + reflectionContext.RecordUnrecognizedPattern (2057, $"Unrecognized value passed to the parameter 'typeName' of method '{calledMethod.GetDisplayName ()}'. It's not possible to guarantee the availability of the target type."); + } + } + + } + break; + + // + // GetConstructor (Type[]) + // GetConstructor (BindingFlags, Type[]) + // GetConstructor (BindingFlags, Binder, Type[], ParameterModifier []) + // GetConstructor (BindingFlags, Binder, CallingConventions, Type[], ParameterModifier []) + // + case IntrinsicId.Type_GetConstructor: { + reflectionContext.AnalyzingPattern (); + + var parameters = calledMethod.Parameters; + BindingFlags? bindingFlags; + if (parameters.Count > 1 && calledMethod.Parameters[0].ParameterType.Name == "BindingFlags") + bindingFlags = GetBindingFlagsFromValue (methodParams[1]); + else + // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter + bindingFlags = BindingFlags.Public | BindingFlags.Instance; + + int? ctorParameterCount = parameters.Count switch { + 1 => (methodParams[1] as ArrayValue)?.Size.AsConstInt (), + 2 => (methodParams[2] as ArrayValue)?.Size.AsConstInt (), + 4 => (methodParams[3] as ArrayValue)?.Size.AsConstInt (), + 5 => (methodParams[4] as ArrayValue)?.Size.AsConstInt (), + _ => null, + }; + + // Go over all types we've seen + foreach (var value in methodParams[0].UniqueValues ()) { + if (value is SystemTypeValue systemTypeValue) { + if (BindingFlagsAreUnsupported (bindingFlags)) { + RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors, value, calledMethodDefinition); + } else { + if (HasBindingFlag (bindingFlags, BindingFlags.Public) && !HasBindingFlag (bindingFlags, BindingFlags.NonPublic) + && ctorParameterCount == 0) { + MarkConstructorsOnType (ref reflectionContext, systemTypeValue.TypeRepresented, m => m.IsPublic && m.Parameters.Count == 0, bindingFlags); + } else { + MarkConstructorsOnType (ref reflectionContext, systemTypeValue.TypeRepresented, null, bindingFlags); + } + } + reflectionContext.RecordHandledPattern (); + } else { + // Otherwise fall back to the bitfield requirements + var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags); + // We can scope down the public constructors requirement if we know the number of parameters is 0 + if (requiredMemberTypes == DynamicallyAccessedMemberTypes.PublicConstructors && ctorParameterCount == 0) + requiredMemberTypes = DynamicallyAccessedMemberTypes.PublicParameterlessConstructor; + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethodDefinition); + } + } + } + break; + + // + // GetMethod (string) + // GetMethod (string, BindingFlags) + // GetMethod (string, Type[]) + // GetMethod (string, Type[], ParameterModifier[]) + // GetMethod (string, BindingFlags, Type[]) + // GetMethod (string, BindingFlags, Binder, Type[], ParameterModifier[]) + // GetMethod (string, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[]) + // GetMethod (string, int, Type[]) + // GetMethod (string, int, Type[], ParameterModifier[]?) + // GetMethod (string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?) + // GetMethod (string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?) + // + case IntrinsicId.Type_GetMethod: { + reflectionContext.AnalyzingPattern (); + + BindingFlags? bindingFlags; + if (calledMethod.Parameters.Count > 1 && calledMethod.Parameters[1].ParameterType.Name == "BindingFlags") + bindingFlags = GetBindingFlagsFromValue (methodParams[2]); + else if (calledMethod.Parameters.Count > 2 && calledMethod.Parameters[2].ParameterType.Name == "BindingFlags") + bindingFlags = GetBindingFlagsFromValue (methodParams[3]); + else + // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter + bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags); + foreach (var value in methodParams[0].UniqueValues ()) { + if (value is SystemTypeValue systemTypeValue) { + foreach (var stringParam in methodParams[1].UniqueValues ()) { + if (stringParam is KnownStringValue stringValue) { + if (BindingFlagsAreUnsupported (bindingFlags)) { + RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods, value, calledMethodDefinition); + } else { + ProcessGetMethodByName (ref reflectionContext, systemTypeValue.TypeRepresented, stringValue.Contents, bindingFlags, ref methodReturnValue); + } + + reflectionContext.RecordHandledPattern (); + } else { + // Otherwise fall back to the bitfield requirements + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethodDefinition); + } + } + } else { + // Otherwise fall back to the bitfield requirements + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethodDefinition); + } + } + } + break; + + // + // GetNestedType (string) + // GetNestedType (string, BindingFlags) + // + case IntrinsicId.Type_GetNestedType: { + reflectionContext.AnalyzingPattern (); + + BindingFlags? bindingFlags; + if (calledMethod.Parameters.Count > 1 && calledMethod.Parameters[1].ParameterType.Name == "BindingFlags") + bindingFlags = GetBindingFlagsFromValue (methodParams[2]); + else + // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter + bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + + var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (bindingFlags); + bool everyParentTypeHasAll = true; + foreach (var value in methodParams[0].UniqueValues ()) { + if (value is SystemTypeValue systemTypeValue) { + foreach (var stringParam in methodParams[1].UniqueValues ()) { + if (stringParam is KnownStringValue stringValue) { + if (BindingFlagsAreUnsupported (bindingFlags)) + // We have chosen not to populate the methodReturnValue for now + RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, value, calledMethodDefinition); + else { + TypeDefinition[] matchingNestedTypes = MarkNestedTypesOnType (ref reflectionContext, systemTypeValue.TypeRepresented, m => m.Name == stringValue.Contents, bindingFlags); + + if (matchingNestedTypes != null) { + for (int i = 0; i < matchingNestedTypes.Length; i++) + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (matchingNestedTypes[i])); + } + } + reflectionContext.RecordHandledPattern (); + } else { + // Otherwise fall back to the bitfield requirements + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethodDefinition); + } + } + } else { + // Otherwise fall back to the bitfield requirements + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethodDefinition); + } + + if (value is LeafValueWithDynamicallyAccessedMemberNode leafValueWithDynamicallyAccessedMember) { + if (leafValueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.All) + everyParentTypeHasAll = false; + } else if (!(value is NullValue || value is SystemTypeValue)) { + // Known Type values are always OK - either they're fully resolved above and thus the return value + // is set to the known resolved type, or if they're not resolved, they won't exist at runtime + // and will cause exceptions - and thus don't introduce new requirements on marking. + // nulls are intentionally ignored as they will lead to exceptions at runtime + // and thus don't introduce new requirements on marking. + everyParentTypeHasAll = false; + } + } + + // If the parent type (all the possible values) has DynamicallyAccessedMemberTypes.All it means its nested types are also fully marked + // (see MarkStep.MarkEntireType - it will recursively mark entire type on nested types). In that case we can annotate + // the returned type (the nested type) with DynamicallyAccessedMemberTypes.All as well. + // Note it's OK to blindly overwrite any potential annotation on the return value from the method definition + // since DynamicallyAccessedMemberTypes.All is a superset of any other annotation. + if (everyParentTypeHasAll && methodReturnValue == null) + methodReturnValue = CreateMethodReturnValue (calledMethodDefinition, DynamicallyAccessedMemberTypes.All); + } + break; + + // + // AssemblyQualifiedName + // + case IntrinsicId.Type_get_AssemblyQualifiedName: { + ValueNode? transformedResult = null; + foreach (var value in methodParams[0].UniqueValues ()) { + if (value is LeafValueWithDynamicallyAccessedMemberNode dynamicallyAccessedThing) { + var annotatedString = new AnnotatedStringValue (dynamicallyAccessedThing.SourceContext, dynamicallyAccessedThing.DynamicallyAccessedMemberTypes); + transformedResult = MergePointValue.MergeValues (transformedResult, annotatedString); + } else { + transformedResult = null; + break; + } + } + + if (transformedResult != null) { + methodReturnValue = transformedResult; + } + } + break; + + // + // UnderlyingSystemType + // + case IntrinsicId.Type_get_UnderlyingSystemType: { + // This is identity for the purposes of the analysis. + methodReturnValue = methodParams[0]; + } + break; + + // + // Type.BaseType + // + case IntrinsicId.Type_get_BaseType: { + foreach (var value in methodParams[0].UniqueValues ()) { + if (value is LeafValueWithDynamicallyAccessedMemberNode dynamicallyAccessedMemberNode) { + DynamicallyAccessedMemberTypes propagatedMemberTypes = DynamicallyAccessedMemberTypes.None; + if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) + propagatedMemberTypes = DynamicallyAccessedMemberTypes.All; + else { + // PublicConstructors are not propagated to base type + + if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicEvents)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicEvents; + + if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicFields)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicFields; + + if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicMethods)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicMethods; + + // PublicNestedTypes are not propagated to base type + + // PublicParameterlessConstructor is not propagated to base type + + if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicProperties)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicProperties; + } + + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod, propagatedMemberTypes)); + } else if (value is SystemTypeValue systemTypeValue) { + if (systemTypeValue.TypeRepresented.BaseType is TypeReference baseTypeRef && _context.TryResolve (baseTypeRef) is TypeDefinition baseTypeDefinition) + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (baseTypeDefinition)); + else + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod)); + } else if (value == NullValue.Instance) { + // Ignore nulls - null.BaseType will fail at runtime, but it has no effect on static analysis + continue; + } else { + // Unknown input - propagate a return value without any annotation - we know it's a Type but we know nothing about it + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod)); + } + } + } + break; + + // + // GetField (string) + // GetField (string, BindingFlags) + // GetEvent (string) + // GetEvent (string, BindingFlags) + // GetProperty (string) + // GetProperty (string, BindingFlags) + // GetProperty (string, Type) + // GetProperty (string, Type[]) + // GetProperty (string, Type, Type[]) + // GetProperty (string, Type, Type[], ParameterModifier[]) + // GetProperty (string, BindingFlags, Binder, Type, Type[], ParameterModifier[]) + // + case var fieldPropertyOrEvent when (fieldPropertyOrEvent == IntrinsicId.Type_GetField || fieldPropertyOrEvent == IntrinsicId.Type_GetProperty || fieldPropertyOrEvent == IntrinsicId.Type_GetEvent) + && calledMethod.DeclaringType.Namespace == "System" + && calledMethod.DeclaringType.Name == "Type" + && calledMethod.Parameters[0].ParameterType.FullName == "System.String" + && calledMethod.HasThis: { + + reflectionContext.AnalyzingPattern (); + BindingFlags? bindingFlags; + if (calledMethod.Parameters.Count > 1 && calledMethod.Parameters[1].ParameterType.Name == "BindingFlags") + bindingFlags = GetBindingFlagsFromValue (methodParams[2]); + else + // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter + bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + + DynamicallyAccessedMemberTypes memberTypes = fieldPropertyOrEvent switch { + IntrinsicId.Type_GetEvent => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents (bindingFlags), + IntrinsicId.Type_GetField => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields (bindingFlags), + IntrinsicId.Type_GetProperty => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (bindingFlags), + _ => throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is of unexpected member type."), + }; + + foreach (var value in methodParams[0].UniqueValues ()) { + if (value is SystemTypeValue systemTypeValue) { + foreach (var stringParam in methodParams[1].UniqueValues ()) { + if (stringParam is KnownStringValue stringValue) { + switch (fieldPropertyOrEvent) { + case IntrinsicId.Type_GetEvent: + if (BindingFlagsAreUnsupported (bindingFlags)) + RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents, value, calledMethodDefinition); + else + MarkEventsOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, filter: e => e.Name == stringValue.Contents, bindingFlags); + break; + case IntrinsicId.Type_GetField: + if (BindingFlagsAreUnsupported (bindingFlags)) + RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields, value, calledMethodDefinition); + else + MarkFieldsOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, filter: f => f.Name == stringValue.Contents, bindingFlags); + break; + case IntrinsicId.Type_GetProperty: + if (BindingFlagsAreUnsupported (bindingFlags)) + RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties, value, calledMethodDefinition); + else + MarkPropertiesOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, filter: p => p.Name == stringValue.Contents, bindingFlags); + break; + default: + Debug.Fail ("Unreachable."); + break; + } + reflectionContext.RecordHandledPattern (); + } else { + RequireDynamicallyAccessedMembers (ref reflectionContext, memberTypes, value, calledMethodDefinition); + } + } + } else { + RequireDynamicallyAccessedMembers (ref reflectionContext, memberTypes, value, calledMethodDefinition); + } + } + } + break; + + // + // GetConstructors (BindingFlags) + // GetMethods (BindingFlags) + // GetFields (BindingFlags) + // GetEvents (BindingFlags) + // GetProperties (BindingFlags) + // GetNestedTypes (BindingFlags) + // GetMembers (BindingFlags) + // + case var callType when (callType == IntrinsicId.Type_GetConstructors || callType == IntrinsicId.Type_GetMethods || callType == IntrinsicId.Type_GetFields || + callType == IntrinsicId.Type_GetProperties || callType == IntrinsicId.Type_GetEvents || callType == IntrinsicId.Type_GetNestedTypes || callType == IntrinsicId.Type_GetMembers) + && calledMethod.DeclaringType.Namespace == "System" + && calledMethod.DeclaringType.Name == "Type" + && calledMethod.Parameters[0].ParameterType.FullName == "System.Reflection.BindingFlags" + && calledMethod.HasThis: { + + reflectionContext.AnalyzingPattern (); + BindingFlags? bindingFlags; + bindingFlags = GetBindingFlagsFromValue (methodParams[1]); + DynamicallyAccessedMemberTypes memberTypes = DynamicallyAccessedMemberTypes.None; + if (BindingFlagsAreUnsupported (bindingFlags)) { + memberTypes = callType switch { + IntrinsicId.Type_GetConstructors => DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors, + IntrinsicId.Type_GetMethods => DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods, + IntrinsicId.Type_GetEvents => DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents, + IntrinsicId.Type_GetFields => DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields, + IntrinsicId.Type_GetProperties => DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties, + IntrinsicId.Type_GetNestedTypes => DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, + IntrinsicId.Type_GetMembers => DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | + DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, + _ => throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is of unexpected member type."), + }; + } else { + memberTypes = callType switch { + IntrinsicId.Type_GetConstructors => GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags), + IntrinsicId.Type_GetMethods => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags), + IntrinsicId.Type_GetEvents => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents (bindingFlags), + IntrinsicId.Type_GetFields => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields (bindingFlags), + IntrinsicId.Type_GetProperties => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (bindingFlags), + IntrinsicId.Type_GetNestedTypes => GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (bindingFlags), + IntrinsicId.Type_GetMembers => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers (bindingFlags), + _ => throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is of unexpected member type."), + }; + } + + foreach (var value in methodParams[0].UniqueValues ()) { + RequireDynamicallyAccessedMembers (ref reflectionContext, memberTypes, value, calledMethodDefinition); + } + } + break; + + + // + // GetMember (String) + // GetMember (String, BindingFlags) + // GetMember (String, MemberTypes, BindingFlags) + // + case IntrinsicId.Type_GetMember: { + reflectionContext.AnalyzingPattern (); + var parameters = calledMethod.Parameters; + BindingFlags? bindingFlags; + if (parameters.Count == 1) { + // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter + bindingFlags = BindingFlags.Public | BindingFlags.Instance; + } else if (parameters.Count == 2 && calledMethod.Parameters[1].ParameterType.Name == "BindingFlags") + bindingFlags = GetBindingFlagsFromValue (methodParams[2]); + else if (parameters.Count == 3 && calledMethod.Parameters[2].ParameterType.Name == "BindingFlags") { + bindingFlags = GetBindingFlagsFromValue (methodParams[3]); + } else // Non recognized intrinsic + throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is an unexpected intrinsic."); + + DynamicallyAccessedMemberTypes requiredMemberTypes = DynamicallyAccessedMemberTypes.None; + if (BindingFlagsAreUnsupported (bindingFlags)) { + requiredMemberTypes = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | + DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; + } else { + requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers (bindingFlags); + } + // Go over all types we've seen + foreach (var value in methodParams[0].UniqueValues ()) { + // Mark based on bitfield requirements + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethodDefinition); + } + } + break; + + // + // GetInterface (String) + // GetInterface (String, bool) + // + case IntrinsicId.Type_GetInterface: { + reflectionContext.AnalyzingPattern (); + + foreach (var value in methodParams[0].UniqueValues ()) { + // For now no support for marking a single interface by name. We would have to correctly support + // mangled names for generics to do that correctly. Simply mark all interfaces on the type for now. + + // Require Interfaces annotation + RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypesOverlay.Interfaces, value, calledMethodDefinition); + + // Interfaces is transitive, so the return values will always have at least Interfaces annotation + DynamicallyAccessedMemberTypes returnMemberTypes = DynamicallyAccessedMemberTypesOverlay.Interfaces; + + // Propagate All annotation across the call - All is a superset of Interfaces + if (value is LeafValueWithDynamicallyAccessedMemberNode annotatedNode + && annotatedNode.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) + returnMemberTypes = DynamicallyAccessedMemberTypes.All; + + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod, returnMemberTypes)); + } + } + break; + + // + // System.Activator + // + // static CreateInstance (System.Type type) + // static CreateInstance (System.Type type, bool nonPublic) + // static CreateInstance (System.Type type, params object?[]? args) + // static CreateInstance (System.Type type, object?[]? args, object?[]? activationAttributes) + // static CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture) + // static CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } + // + case IntrinsicId.Activator_CreateInstance_Type: { + var parameters = calledMethod.Parameters; + + reflectionContext.AnalyzingPattern (); + + int? ctorParameterCount = null; + BindingFlags bindingFlags = BindingFlags.Instance; + if (parameters.Count > 1) { + if (parameters[1].ParameterType.MetadataType == MetadataType.Boolean) { + // The overload that takes a "nonPublic" bool + bool nonPublic = true; + if (methodParams[1] is ConstIntValue constInt) { + nonPublic = constInt.Value != 0; + } + + if (nonPublic) + bindingFlags |= BindingFlags.NonPublic | BindingFlags.Public; + else + bindingFlags |= BindingFlags.Public; + ctorParameterCount = 0; + } else { + // Overload that has the parameters as the second or fourth argument + int argsParam = parameters.Count == 2 || parameters.Count == 3 ? 1 : 3; + + if (methodParams.Count > argsParam) { + if (methodParams[argsParam] is ArrayValue arrayValue && + arrayValue.Size.AsConstInt () != null) + ctorParameterCount = arrayValue.Size.AsConstInt (); + else if (methodParams[argsParam] is NullValue) + ctorParameterCount = 0; + } + + if (parameters.Count > 3) { + if (methodParams[1].AsConstInt () is int constInt) + bindingFlags |= (BindingFlags) constInt; + else + bindingFlags |= BindingFlags.NonPublic | BindingFlags.Public; + } else { + bindingFlags |= BindingFlags.Public; + } + } + } else { + // The overload with a single System.Type argument + ctorParameterCount = 0; + bindingFlags |= BindingFlags.Public; + } + + // Go over all types we've seen + foreach (var value in methodParams[0].UniqueValues ()) { + if (value is SystemTypeValue systemTypeValue) { + // Special case known type values as we can do better by applying exact binding flags and parameter count. + MarkConstructorsOnType (ref reflectionContext, systemTypeValue.TypeRepresented, + ctorParameterCount == null ? null : m => m.Parameters.Count == ctorParameterCount, bindingFlags); + reflectionContext.RecordHandledPattern (); + } else { + // Otherwise fall back to the bitfield requirements + var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags); + + // Special case the public parameterless constructor if we know that there are 0 args passed in + if (ctorParameterCount == 0 && requiredMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors)) { + requiredMemberTypes &= ~DynamicallyAccessedMemberTypes.PublicConstructors; + requiredMemberTypes |= DynamicallyAccessedMemberTypes.PublicParameterlessConstructor; + } + + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethod.Parameters[0]); + } + } + } + break; + + // + // System.Activator + // + // static CreateInstance (string assemblyName, string typeName) + // static CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) + // static CreateInstance (string assemblyName, string typeName, object?[]? activationAttributes) + // + case IntrinsicId.Activator_CreateInstance_AssemblyName_TypeName: + ProcessCreateInstanceByName (ref reflectionContext, calledMethodDefinition, methodParams); + break; + + // + // System.Activator + // + // static CreateInstanceFrom (string assemblyFile, string typeName) + // static CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // static CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) + // + case IntrinsicId.Activator_CreateInstanceFrom: + ProcessCreateInstanceByName (ref reflectionContext, calledMethodDefinition, methodParams); + break; + + // + // System.Activator + // + // static T CreateInstance () + // + // Note: If the when condition returns false it would be an overload which we don't recognize, so just fall through to the default case + case IntrinsicId.Activator_CreateInstanceOfT when + calledMethod is GenericInstanceMethod genericCalledMethod && genericCalledMethod.GenericArguments.Count == 1: { + reflectionContext.AnalyzingPattern (); + + if (genericCalledMethod.GenericArguments[0] is GenericParameter genericParameter && + genericParameter.HasDefaultConstructorConstraint) { + // This is safe, the linker would have marked the default .ctor already + reflectionContext.RecordHandledPattern (); + break; + } + + RequireDynamicallyAccessedMembers ( + ref reflectionContext, + DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, + GetTypeValueNodeFromGenericArgument (genericCalledMethod.GenericArguments[0]), + calledMethodDefinition.GenericParameters[0]); + } + break; + + // + // System.AppDomain + // + // CreateInstance (string assemblyName, string typeName) + // CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // CreateInstance (string assemblyName, string typeName, object? []? activationAttributes) + // + // CreateInstanceAndUnwrap (string assemblyName, string typeName) + // CreateInstanceAndUnwrap (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // CreateInstanceAndUnwrap (string assemblyName, string typeName, object? []? activationAttributes) + // + // CreateInstanceFrom (string assemblyFile, string typeName) + // CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) + // + // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName) + // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, object? []? activationAttributes) + // + case var appDomainCreateInstance when appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstance + || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceAndUnwrap + || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFrom + || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap: + ProcessCreateInstanceByName (ref reflectionContext, calledMethodDefinition, methodParams); + break; + + // + // System.Reflection.Assembly + // + // CreateInstance (string typeName) + // CreateInstance (string typeName, bool ignoreCase) + // CreateInstance (string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder? binder, object []? args, CultureInfo? culture, object []? activationAttributes) + // + case IntrinsicId.Assembly_CreateInstance: + // For now always fail since we don't track assemblies (dotnet/linker/issues/1947) + reflectionContext.AnalyzingPattern (); + reflectionContext.RecordUnrecognizedPattern (2058, $"Parameters passed to method '{calledMethodDefinition.GetDisplayName ()}' cannot be analyzed. Consider using methods 'System.Type.GetType' and `System.Activator.CreateInstance` instead."); + break; + + // + // System.Runtime.CompilerServices.RuntimeHelpers + // + // RunClassConstructor (RuntimeTypeHandle type) + // + case IntrinsicId.RuntimeHelpers_RunClassConstructor: { + reflectionContext.AnalyzingPattern (); + foreach (var typeHandleValue in methodParams[0].UniqueValues ()) { + if (typeHandleValue is RuntimeTypeHandleValue runtimeTypeHandleValue) { + _markStep.MarkStaticConstructorVisibleToReflection (runtimeTypeHandleValue.TypeRepresented, new DependencyInfo (DependencyKind.AccessedViaReflection, reflectionContext.Source)); + reflectionContext.RecordHandledPattern (); + } else if (typeHandleValue == NullValue.Instance) + reflectionContext.RecordHandledPattern (); + else { + reflectionContext.RecordUnrecognizedPattern (2059, $"Unrecognized value passed to the parameter 'type' of method '{calledMethodDefinition.GetDisplayName ()}'. It's not possible to guarantee the availability of the target static constructor."); + } + } + } + break; + + // + // System.Reflection.MethodInfo + // + // MakeGenericMethod (Type[] typeArguments) + // + case IntrinsicId.MethodInfo_MakeGenericMethod: { + reflectionContext.AnalyzingPattern (); + + foreach (var methodValue in methodParams[0].UniqueValues ()) { + if (methodValue is SystemReflectionMethodBaseValue methodBaseValue) { + ValidateGenericMethodInstantiation (ref reflectionContext, methodBaseValue.MethodRepresented, methodParams[1], calledMethod); + } else if (methodValue == NullValue.Instance) { + reflectionContext.RecordHandledPattern (); + } else { + // We don't know what method the `MakeGenericMethod` was called on, so we have to assume + // that the method may have requirements which we can't fullfil -> warn. + reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericMethod, + new DiagnosticString (DiagnosticId.MakeGenericMethod).GetMessage (DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod))); + } + } + + // MakeGenericMethod doesn't change the identity of the MethodBase we're tracking so propagate to the return value + methodReturnValue = methodParams[0]; + } + break; + + default: + + if (calledMethodDefinition.IsPInvokeImpl) { + // Is the PInvoke dangerous? + bool comDangerousMethod = IsComInterop (calledMethodDefinition.MethodReturnType, calledMethodDefinition.ReturnType); + foreach (ParameterDefinition pd in calledMethodDefinition.Parameters) { + comDangerousMethod |= IsComInterop (pd, pd.ParameterType); + } + + if (comDangerousMethod) { + reflectionContext.AnalyzingPattern (); + reflectionContext.RecordUnrecognizedPattern (2050, $"P/invoke method '{calledMethodDefinition.GetDisplayName ()}' declares a parameter with COM marshalling. Correctness of COM interop cannot be guaranteed after trimming. Interfaces and interface members might be removed."); + } + } + + if (requiresDataFlowAnalysis) { + reflectionContext.AnalyzingPattern (); + for (int parameterIndex = 0; parameterIndex < methodParams.Count; parameterIndex++) { + var requiredMemberTypes = _context.Annotations.FlowAnnotations.GetParameterAnnotation (calledMethodDefinition, parameterIndex); + if (requiredMemberTypes != 0) { + IMetadataTokenProvider targetContext; + if (calledMethodDefinition.HasImplicitThis ()) { + if (parameterIndex == 0) + targetContext = calledMethodDefinition; + else + targetContext = calledMethodDefinition.Parameters[parameterIndex - 1]; + } else { + targetContext = calledMethodDefinition.Parameters[parameterIndex]; + } + + RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, methodParams[parameterIndex], targetContext); + } + } + + reflectionContext.RecordHandledPattern (); + } + + _markStep.CheckAndReportRequiresUnreferencedCode (calledMethodDefinition); + + // To get good reporting of errors we need to track the origin of the value for all method calls + // but except Newobj as those are special. + if (GetReturnTypeWithoutModifiers (calledMethodDefinition.ReturnType).MetadataType != MetadataType.Void) { + methodReturnValue = CreateMethodReturnValue (calledMethodDefinition, returnValueDynamicallyAccessedMemberTypes); + + return true; + } + + return false; + } + } finally { + reflectionContext.Dispose (); + } + + // If we get here, we handled this as an intrinsic. As a convenience, if the code above + // didn't set the return value (and the method has a return value), we will set it to be an + // unknown value with the return type of the method. + if (methodReturnValue == null) { + if (GetReturnTypeWithoutModifiers (calledMethod.ReturnType).MetadataType != MetadataType.Void) { + methodReturnValue = CreateMethodReturnValue (calledMethodDefinition, returnValueDynamicallyAccessedMemberTypes); + } + } + + // Validate that the return value has the correct annotations as per the method return value annotations + if (returnValueDynamicallyAccessedMemberTypes != 0) { + foreach (var uniqueValue in methodReturnValue.UniqueValues ()) { + if (uniqueValue is LeafValueWithDynamicallyAccessedMemberNode methodReturnValueWithMemberTypes) { + if (!methodReturnValueWithMemberTypes.DynamicallyAccessedMemberTypes.HasFlag (returnValueDynamicallyAccessedMemberTypes)) + throw new InvalidOperationException ($"Internal linker error: processing of call from {callingMethodDefinition.GetDisplayName ()} to {calledMethod.GetDisplayName ()} returned value which is not correctly annotated with the expected dynamic member access kinds."); + } else if (uniqueValue is SystemTypeValue) { + // SystemTypeValue can fullfill any requirement, so it's always valid + // The requirements will be applied at the point where it's consumed (passed as a method parameter, set as field value, returned from the method) + } else { + throw new InvalidOperationException ($"Internal linker error: processing of call from {callingMethodDefinition.GetDisplayName ()} to {calledMethod.GetDisplayName ()} returned value which is not correctly annotated with the expected dynamic member access kinds."); + } + } + } + + return true; + } + + bool IsComInterop (IMarshalInfoProvider marshalInfoProvider, TypeReference parameterType) + { + // This is best effort. One can likely find ways how to get COM without triggering these alarms. + // AsAny marshalling of a struct with an object-typed field would be one, for example. + + // This logic roughly corresponds to MarshalInfo::MarshalInfo in CoreCLR, + // not trying to handle invalid cases and distinctions that are not interesting wrt + // "is this COM?" question. + + NativeType nativeType = NativeType.None; + if (marshalInfoProvider.HasMarshalInfo) { + nativeType = marshalInfoProvider.MarshalInfo.NativeType; + } + + if (nativeType == NativeType.IUnknown || nativeType == NativeType.IDispatch || nativeType == NativeType.IntF) { + // This is COM by definition + return true; + } + + if (nativeType == NativeType.None) { + // Resolve will look at the element type + var parameterTypeDef = _context.TryResolve (parameterType); + + if (parameterTypeDef != null) { + if (parameterTypeDef.IsTypeOf ("System", "Array")) { + // System.Array marshals as IUnknown by default + return true; + } else if (parameterTypeDef.IsTypeOf ("System", "String") || + parameterTypeDef.IsTypeOf ("System.Text", "StringBuilder")) { + // String and StringBuilder are special cased by interop + return false; + } + + if (parameterTypeDef.IsValueType) { + // Value types don't marshal as COM + return false; + } else if (parameterTypeDef.IsInterface) { + // Interface types marshal as COM by default + return true; + } else if (parameterTypeDef.IsMulticastDelegate ()) { + // Delegates are special cased by interop + return false; + } else if (parameterTypeDef.IsSubclassOf ("System.Runtime.InteropServices", "CriticalHandle", _context)) { + // Subclasses of CriticalHandle are special cased by interop + return false; + } else if (parameterTypeDef.IsSubclassOf ("System.Runtime.InteropServices", "SafeHandle", _context)) { + // Subclasses of SafeHandle are special cased by interop + return false; + } else if (!parameterTypeDef.IsSequentialLayout && !parameterTypeDef.IsExplicitLayout) { + // Rest of classes that don't have layout marshal as COM + return true; + } + } + } + + return false; + } + + bool AnalyzeGenericInstantiationTypeArray (ValueNode? arrayParam, ref ReflectionPatternContext reflectionContext, MethodReference calledMethod, IList genericParameters) + { + bool hasRequirements = false; + foreach (var genericParameter in genericParameters) { + if (_context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None) { + hasRequirements = true; + break; + } + } + + // If there are no requirements, then there's no point in warning + if (!hasRequirements) + return true; + + foreach (var typesValue in arrayParam.UniqueValues ()) { + if (typesValue.Kind != ValueNodeKind.Array) { + return false; + } + ArrayValue array = (ArrayValue) typesValue; + int? size = array.Size.AsConstInt (); + if (size == null || size != genericParameters.Count) { + return false; + } + bool allIndicesKnown = true; + for (int i = 0; i < size.Value; i++) { + if (!array.IndexValues.TryGetValue (i, out ValueBasicBlockPair value) || value.Value is null or { Kind: ValueNodeKind.Unknown }) { + allIndicesKnown = false; + break; + } + } + + if (!allIndicesKnown) { + return false; + } + + for (int i = 0; i < size.Value; i++) { + if (array.IndexValues.TryGetValue (i, out ValueBasicBlockPair value)) { + RequireDynamicallyAccessedMembers ( + ref reflectionContext, + _context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (genericParameters[i]), + value.Value, + calledMethod.Resolve ()); + } + } + } + return true; + } + + void ProcessCreateInstanceByName (ref ReflectionPatternContext reflectionContext, MethodDefinition calledMethod, ValueNodeList methodParams) + { + reflectionContext.AnalyzingPattern (); + + BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; + bool parameterlessConstructor = true; + if (calledMethod.Parameters.Count == 8 && calledMethod.Parameters[2].ParameterType.MetadataType == MetadataType.Boolean) { + parameterlessConstructor = false; + bindingFlags = BindingFlags.Instance; + if (methodParams[3].AsConstInt () is int bindingFlagsInt) + bindingFlags |= (BindingFlags) bindingFlagsInt; + else + bindingFlags |= BindingFlags.Public | BindingFlags.NonPublic; + } + + int methodParamsOffset = calledMethod.HasImplicitThis () ? 1 : 0; + + foreach (var assemblyNameValue in methodParams[methodParamsOffset].UniqueValues ()) { + if (assemblyNameValue is KnownStringValue assemblyNameStringValue) { + foreach (var typeNameValue in methodParams[methodParamsOffset + 1].UniqueValues ()) { + if (typeNameValue is KnownStringValue typeNameStringValue) { + var resolvedAssembly = _context.TryResolve (assemblyNameStringValue.Contents); + if (resolvedAssembly == null) { + reflectionContext.RecordUnrecognizedPattern (2061, $"The assembly name '{assemblyNameStringValue.Contents}' passed to method '{calledMethod.GetDisplayName ()}' references assembly which is not available."); + continue; + } + + var typeRef = _context.TypeNameResolver.ResolveTypeName (resolvedAssembly, typeNameStringValue.Contents); + var resolvedType = _context.TryResolve (typeRef); + if (resolvedType == null || typeRef is ArrayType) { + // It's not wrong to have a reference to non-existing type - the code may well expect to get an exception in this case + // Note that we did find the assembly, so it's not a linker config problem, it's either intentional, or wrong versions of assemblies + // but linker can't know that. In case a user tries to create an array using System.Activator we should simply ignore it, the user + // might expect an exception to be thrown. + reflectionContext.RecordHandledPattern (); + continue; + } + + MarkConstructorsOnType (ref reflectionContext, resolvedType, parameterlessConstructor ? m => m.Parameters.Count == 0 : null, bindingFlags); + } else { + reflectionContext.RecordUnrecognizedPattern (2032, $"Unrecognized value passed to the parameter '{calledMethod.Parameters[1].Name}' of method '{calledMethod.GetDisplayName ()}'. It's not possible to guarantee the availability of the target type."); + } + } + } else { + reflectionContext.RecordUnrecognizedPattern (2032, $"Unrecognized value passed to the parameter '{calledMethod.Parameters[0].Name}' of method '{calledMethod.GetDisplayName ()}'. It's not possible to guarantee the availability of the target type."); + } + } + } + + void ProcessGetMethodByName ( + ref ReflectionPatternContext reflectionContext, + TypeDefinition typeDefinition, + string methodName, + BindingFlags? bindingFlags, + ref ValueNode? methodReturnValue) + { + bool foundAny = false; + foreach (var method in typeDefinition.GetMethodsOnTypeHierarchy (_context, m => m.Name == methodName, bindingFlags)) { + MarkMethod (ref reflectionContext, method); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemReflectionMethodBaseValue (method)); + foundAny = true; + } + + // If there were no methods found the API will return null at runtime, so we should + // track the null as a return value as well. + // This also prevents warnings in such case, since if we don't set the return value it will be + // "unknown" and consumers may warn. + if (!foundAny) + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, NullValue.Instance); + } + + public static DynamicallyAccessedMemberTypes GetMissingMemberTypes (DynamicallyAccessedMemberTypes requiredMemberTypes, DynamicallyAccessedMemberTypes availableMemberTypes) + { + if (availableMemberTypes.HasFlag (requiredMemberTypes)) + return DynamicallyAccessedMemberTypes.None; + + if (requiredMemberTypes == DynamicallyAccessedMemberTypes.All) + return DynamicallyAccessedMemberTypes.All; + + var missingMemberTypes = requiredMemberTypes & ~availableMemberTypes; + + // PublicConstructors is a special case since its value is 3 - so PublicParameterlessConstructor (1) | _PublicConstructor_WithMoreThanOneParameter_ (2) + // The above bit logic only works for value with single bit set. + if (requiredMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors) && + !availableMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors)) + missingMemberTypes |= DynamicallyAccessedMemberTypes.PublicConstructors; + + return missingMemberTypes; + } + + string GetMemberTypesString (DynamicallyAccessedMemberTypes memberTypes) + { + Debug.Assert (memberTypes != DynamicallyAccessedMemberTypes.None); + + if (memberTypes == DynamicallyAccessedMemberTypes.All) + return $"'{nameof (DynamicallyAccessedMemberTypes)}.{nameof (DynamicallyAccessedMemberTypes.All)}'"; + + var memberTypesList = AllDynamicallyAccessedMemberTypes + .Where (damt => (memberTypes & damt) == damt && damt != DynamicallyAccessedMemberTypes.None) + .ToList (); + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors)) + memberTypesList.Remove (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor); + + return string.Join (", ", memberTypesList.Select (mt => { + string mtName = mt == DynamicallyAccessedMemberTypesOverlay.Interfaces + ? nameof (DynamicallyAccessedMemberTypesOverlay.Interfaces) + : mt.ToString (); + + return $"'{nameof (DynamicallyAccessedMemberTypes)}.{mtName}'"; + })); + } + + void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionContext, DynamicallyAccessedMemberTypes requiredMemberTypes, ValueNode? value, IMetadataTokenProvider targetContext) + { + foreach (var uniqueValue in value.UniqueValues ()) { + if (requiredMemberTypes == DynamicallyAccessedMemberTypes.PublicParameterlessConstructor + && uniqueValue is SystemTypeForGenericParameterValue genericParam + && genericParam.GenericParameter.HasDefaultConstructorConstraint) { + // We allow a new() constraint on a generic parameter to satisfy DynamicallyAccessedMemberTypes.PublicParameterlessConstructor + reflectionContext.RecordHandledPattern (); + } else if (uniqueValue is LeafValueWithDynamicallyAccessedMemberNode valueWithDynamicallyAccessedMember) { + var availableMemberTypes = valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes; + var missingMemberTypesValue = GetMissingMemberTypes (requiredMemberTypes, availableMemberTypes); + if (missingMemberTypesValue != DynamicallyAccessedMemberTypes.None) { + var missingMemberTypes = GetMemberTypesString (missingMemberTypesValue); + + switch ((valueWithDynamicallyAccessedMember.SourceContext, targetContext)) { + case (ParameterDefinition sourceParameter, ParameterDefinition targetParameter): + reflectionContext.RecordUnrecognizedPattern (2067, string.Format (Resources.Strings.IL2067, + DiagnosticUtilities.GetParameterNameForErrorMessage (targetParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName (targetParameter.Method), + DiagnosticUtilities.GetParameterNameForErrorMessage (sourceParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName (sourceParameter.Method), + missingMemberTypes)); + break; + case (ParameterDefinition sourceParameter, MethodReturnType targetMethodReturnType): + reflectionContext.RecordUnrecognizedPattern (2068, string.Format (Resources.Strings.IL2068, + DiagnosticUtilities.GetMethodSignatureDisplayName (targetMethodReturnType.Method), + DiagnosticUtilities.GetParameterNameForErrorMessage (sourceParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName (sourceParameter.Method), + missingMemberTypes)); + break; + case (ParameterDefinition sourceParameter, FieldDefinition targetField): + reflectionContext.RecordUnrecognizedPattern (2069, string.Format (Resources.Strings.IL2069, + targetField.GetDisplayName (), + DiagnosticUtilities.GetParameterNameForErrorMessage (sourceParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName (sourceParameter.Method), + missingMemberTypes)); + break; + case (ParameterDefinition sourceParameter, MethodDefinition targetMethod): + reflectionContext.RecordUnrecognizedPattern (2070, string.Format (Resources.Strings.IL2070, + targetMethod.GetDisplayName (), + DiagnosticUtilities.GetParameterNameForErrorMessage (sourceParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName (sourceParameter.Method), + missingMemberTypes)); + break; + case (ParameterDefinition sourceParameter, GenericParameter targetGenericParameter): + // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used + reflectionContext.RecordUnrecognizedPattern (2071, string.Format (Resources.Strings.IL2071, + targetGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (targetGenericParameter), + DiagnosticUtilities.GetParameterNameForErrorMessage (sourceParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName (sourceParameter.Method), + missingMemberTypes)); + break; + + case (MethodReturnType sourceMethodReturnType, ParameterDefinition targetParameter): + reflectionContext.RecordUnrecognizedPattern (2072, string.Format (Resources.Strings.IL2072, + DiagnosticUtilities.GetParameterNameForErrorMessage (targetParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName (targetParameter.Method), + DiagnosticUtilities.GetMethodSignatureDisplayName (sourceMethodReturnType.Method), + missingMemberTypes)); + break; + case (MethodReturnType sourceMethodReturnType, MethodReturnType targetMethodReturnType): + reflectionContext.RecordUnrecognizedPattern (2073, string.Format (Resources.Strings.IL2073, + DiagnosticUtilities.GetMethodSignatureDisplayName (targetMethodReturnType.Method), + DiagnosticUtilities.GetMethodSignatureDisplayName (sourceMethodReturnType.Method), + missingMemberTypes)); + break; + case (MethodReturnType sourceMethodReturnType, FieldDefinition targetField): + reflectionContext.RecordUnrecognizedPattern (2074, string.Format (Resources.Strings.IL2074, + targetField.GetDisplayName (), + DiagnosticUtilities.GetMethodSignatureDisplayName (sourceMethodReturnType.Method), + missingMemberTypes)); + break; + case (MethodReturnType sourceMethodReturnType, MethodDefinition targetMethod): + reflectionContext.RecordUnrecognizedPattern (2075, string.Format (Resources.Strings.IL2075, + targetMethod.GetDisplayName (), + DiagnosticUtilities.GetMethodSignatureDisplayName (sourceMethodReturnType.Method), + missingMemberTypes)); + break; + case (MethodReturnType sourceMethodReturnType, GenericParameter targetGenericParameter): + // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used + reflectionContext.RecordUnrecognizedPattern (2076, string.Format (Resources.Strings.IL2076, + targetGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (targetGenericParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName (sourceMethodReturnType.Method), + missingMemberTypes)); + break; + + case (FieldDefinition sourceField, ParameterDefinition targetParameter): + reflectionContext.RecordUnrecognizedPattern (2077, string.Format (Resources.Strings.IL2077, + DiagnosticUtilities.GetParameterNameForErrorMessage (targetParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName (targetParameter.Method), + sourceField.GetDisplayName (), + missingMemberTypes)); + break; + case (FieldDefinition sourceField, MethodReturnType targetMethodReturnType): + reflectionContext.RecordUnrecognizedPattern (2078, string.Format (Resources.Strings.IL2078, + DiagnosticUtilities.GetMethodSignatureDisplayName (targetMethodReturnType.Method), + sourceField.GetDisplayName (), + missingMemberTypes)); + break; + case (FieldDefinition sourceField, FieldDefinition targetField): + reflectionContext.RecordUnrecognizedPattern (2079, string.Format (Resources.Strings.IL2079, + targetField.GetDisplayName (), + sourceField.GetDisplayName (), + missingMemberTypes)); + break; + case (FieldDefinition sourceField, MethodDefinition targetMethod): + reflectionContext.RecordUnrecognizedPattern (2080, string.Format (Resources.Strings.IL2080, + targetMethod.GetDisplayName (), + sourceField.GetDisplayName (), + missingMemberTypes)); + break; + case (FieldDefinition sourceField, GenericParameter targetGenericParameter): + // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used + reflectionContext.RecordUnrecognizedPattern (2081, string.Format (Resources.Strings.IL2081, + targetGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (targetGenericParameter), + sourceField.GetDisplayName (), + missingMemberTypes)); + break; + + case (MethodDefinition sourceMethod, ParameterDefinition targetParameter): + reflectionContext.RecordUnrecognizedPattern (2082, string.Format (Resources.Strings.IL2082, + DiagnosticUtilities.GetParameterNameForErrorMessage (targetParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName (targetParameter.Method), + sourceMethod.GetDisplayName (), + missingMemberTypes)); + break; + case (MethodDefinition sourceMethod, MethodReturnType targetMethodReturnType): + reflectionContext.RecordUnrecognizedPattern (2083, string.Format (Resources.Strings.IL2083, + DiagnosticUtilities.GetMethodSignatureDisplayName (targetMethodReturnType.Method), + sourceMethod.GetDisplayName (), + missingMemberTypes)); + break; + case (MethodDefinition sourceMethod, FieldDefinition targetField): + reflectionContext.RecordUnrecognizedPattern (2084, string.Format (Resources.Strings.IL2084, + targetField.GetDisplayName (), + sourceMethod.GetDisplayName (), + missingMemberTypes)); + break; + case (MethodDefinition sourceMethod, MethodDefinition targetMethod): + reflectionContext.RecordUnrecognizedPattern (2085, string.Format (Resources.Strings.IL2085, + targetMethod.GetDisplayName (), + sourceMethod.GetDisplayName (), + missingMemberTypes)); + break; + case (MethodDefinition sourceMethod, GenericParameter targetGenericParameter): + // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used + reflectionContext.RecordUnrecognizedPattern (2086, string.Format (Resources.Strings.IL2086, + targetGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (targetGenericParameter), + sourceMethod.GetDisplayName (), + missingMemberTypes)); + break; + + case (GenericParameter sourceGenericParameter, ParameterDefinition targetParameter): + reflectionContext.RecordUnrecognizedPattern (2087, string.Format (Resources.Strings.IL2087, + DiagnosticUtilities.GetParameterNameForErrorMessage (targetParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName (targetParameter.Method), + sourceGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (sourceGenericParameter), + missingMemberTypes)); + break; + case (GenericParameter sourceGenericParameter, MethodReturnType targetMethodReturnType): + reflectionContext.RecordUnrecognizedPattern (2088, string.Format (Resources.Strings.IL2088, + DiagnosticUtilities.GetMethodSignatureDisplayName (targetMethodReturnType.Method), + sourceGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (sourceGenericParameter), + missingMemberTypes)); + break; + case (GenericParameter sourceGenericParameter, FieldDefinition targetField): + reflectionContext.RecordUnrecognizedPattern (2089, string.Format (Resources.Strings.IL2089, + targetField.GetDisplayName (), + sourceGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (sourceGenericParameter), + missingMemberTypes)); + break; + case (GenericParameter sourceGenericParameter, MethodDefinition targetMethod): + // Currently this is never generated, it might be possible one day if we try to validate annotations on results of reflection + // For example code like this should ideally one day generate the warning + // void TestMethod() + // { + // // This passes the T as the "this" parameter to Type.GetMethods() + // typeof(Type).GetMethod("GetMethods").Invoke(typeof(T), new object[] {}); + // } + reflectionContext.RecordUnrecognizedPattern (2090, string.Format (Resources.Strings.IL2090, + targetMethod.GetDisplayName (), + sourceGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (sourceGenericParameter), + missingMemberTypes)); + break; + case (GenericParameter sourceGenericParameter, GenericParameter targetGenericParameter): + reflectionContext.RecordUnrecognizedPattern (2091, string.Format (Resources.Strings.IL2091, + targetGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (targetGenericParameter), + sourceGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (sourceGenericParameter), + missingMemberTypes)); + break; + + default: + throw new NotImplementedException ($"unsupported source context {valueWithDynamicallyAccessedMember.SourceContext} or target context {targetContext}"); + }; + } else { + reflectionContext.RecordHandledPattern (); + } + } else if (uniqueValue is SystemTypeValue systemTypeValue) { + MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, systemTypeValue.TypeRepresented, requiredMemberTypes, DependencyKind.DynamicallyAccessedMember); + } else if (uniqueValue is KnownStringValue knownStringValue) { + TypeReference typeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents, reflectionContext.Source, out AssemblyDefinition typeAssembly); + TypeDefinition? foundType = ResolveToTypeDefinition (typeRef); + if (foundType == null) { + // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. + reflectionContext.RecordHandledPattern (); + } else { + MarkType (ref reflectionContext, typeRef); + MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, foundType, requiredMemberTypes, DependencyKind.DynamicallyAccessedMember); + _context.MarkingHelpers.MarkMatchingExportedType (foundType, typeAssembly, new DependencyInfo (DependencyKind.DynamicallyAccessedMember, foundType)); + } + } else if (uniqueValue == NullValue.Instance) { + // Ignore - probably unreachable path as it would fail at runtime anyway. + } else { + switch (targetContext) { + case ParameterDefinition parameterDefinition: + reflectionContext.RecordUnrecognizedPattern ( + 2062, + $"Value passed to parameter '{DiagnosticUtilities.GetParameterNameForErrorMessage (parameterDefinition)}' of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (parameterDefinition.Method)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); + break; + case MethodReturnType methodReturnType: + reflectionContext.RecordUnrecognizedPattern ( + 2063, + $"Value returned from method '{DiagnosticUtilities.GetMethodSignatureDisplayName (methodReturnType.Method)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); + break; + case FieldDefinition fieldDefinition: + reflectionContext.RecordUnrecognizedPattern ( + 2064, + $"Value assigned to {fieldDefinition.GetDisplayName ()} can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); + break; + case MethodDefinition methodDefinition: + reflectionContext.RecordUnrecognizedPattern ( + 2065, + $"Value passed to implicit 'this' parameter of method '{methodDefinition.GetDisplayName ()}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); + break; + case GenericParameter genericParameter: + // Unknown value to generic parameter - this is possible if the generic argument fails to resolve + reflectionContext.RecordUnrecognizedPattern ( + 2066, + $"Type passed to generic parameter '{genericParameter.Name}' of '{DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (genericParameter)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); + break; + default: throw new NotImplementedException ($"unsupported target context {targetContext.GetType ()}"); + }; + } + } + + reflectionContext.RecordHandledPattern (); + } + + static BindingFlags? GetBindingFlagsFromValue (ValueNode? parameter) => (BindingFlags?) parameter.AsConstInt (); + + static bool BindingFlagsAreUnsupported (BindingFlags? bindingFlags) + { + if (bindingFlags == null) + return true; + + // Binding flags we understand + const BindingFlags UnderstoodBindingFlags = + BindingFlags.DeclaredOnly | + BindingFlags.Instance | + BindingFlags.Static | + BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.FlattenHierarchy | + BindingFlags.ExactBinding; + + // Binding flags that don't affect binding outside InvokeMember (that we don't analyze). + const BindingFlags IgnorableBindingFlags = + BindingFlags.InvokeMethod | + BindingFlags.CreateInstance | + BindingFlags.GetField | + BindingFlags.SetField | + BindingFlags.GetProperty | + BindingFlags.SetProperty; + + BindingFlags flags = bindingFlags.Value; + return (flags & ~(UnderstoodBindingFlags | IgnorableBindingFlags)) != 0; + } + + static bool HasBindingFlag (BindingFlags? bindingFlags, BindingFlags? search) => bindingFlags != null && (bindingFlags & search) == search; + + public void MarkTypeForDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionContext, TypeDefinition typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes, DependencyKind dependencyKind, bool declaredOnly = false) + { + foreach (var member in typeDefinition.GetDynamicallyAccessedMembers (_context, requiredMemberTypes, declaredOnly)) { + switch (member) { + case MethodDefinition method: + MarkMethod (ref reflectionContext, method, dependencyKind); + break; + case FieldDefinition field: + MarkField (ref reflectionContext, field, dependencyKind); + break; + case TypeDefinition nestedType: + MarkType (ref reflectionContext, nestedType, dependencyKind); + break; + case PropertyDefinition property: + MarkProperty (ref reflectionContext, property, dependencyKind); + break; + case EventDefinition @event: + MarkEvent (ref reflectionContext, @event, dependencyKind); + break; + case InterfaceImplementation interfaceImplementation: + MarkInterfaceImplementation (ref reflectionContext, interfaceImplementation, dependencyKind); + break; + } + } + } + + void MarkType (ref ReflectionPatternContext reflectionContext, TypeReference typeReference, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + var dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); + if (_context.TryResolve (typeReference) is TypeDefinition type) + reflectionContext.RecordRecognizedPattern (type, () => _markStep.MarkTypeVisibleToReflection (typeReference, type, dependencyInfo)); + } + + void MarkMethod (ref ReflectionPatternContext reflectionContext, MethodDefinition method, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + var dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); + reflectionContext.RecordRecognizedPattern (method, () => _markStep.MarkMethodVisibleToReflection (method, dependencyInfo)); + } + + void MarkField (ref ReflectionPatternContext reflectionContext, FieldDefinition field, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + var dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); + reflectionContext.RecordRecognizedPattern (field, () => _markStep.MarkFieldVisibleToReflection (field, dependencyInfo)); + } + + void MarkProperty (ref ReflectionPatternContext reflectionContext, PropertyDefinition property, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + var dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); + reflectionContext.RecordRecognizedPattern (property, () => { _markStep.MarkPropertyVisibleToReflection (property, dependencyInfo); }); + } + + void MarkEvent (ref ReflectionPatternContext reflectionContext, EventDefinition @event, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + var dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); + reflectionContext.RecordRecognizedPattern (@event, () => { _markStep.MarkEventVisibleToReflection (@event, dependencyInfo); }); + } + + void MarkInterfaceImplementation (ref ReflectionPatternContext reflectionContext, InterfaceImplementation interfaceImplementation, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + var dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); + reflectionContext.RecordRecognizedPattern (interfaceImplementation, () => _markStep.MarkInterfaceImplementation (interfaceImplementation, null, dependencyInfo)); + } + + void MarkConstructorsOnType (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func? filter, BindingFlags? bindingFlags = null) + { + foreach (var ctor in type.GetConstructorsOnType (filter, bindingFlags)) + MarkMethod (ref reflectionContext, ctor); + } + + void MarkFieldsOnTypeHierarchy (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var field in type.GetFieldsOnTypeHierarchy (_context, filter, bindingFlags)) + MarkField (ref reflectionContext, field); + } + + TypeDefinition[] MarkNestedTypesOnType (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + var result = new ArrayBuilder (); + + foreach (var nestedType in type.GetNestedTypesOnType (filter, bindingFlags)) { + result.Add (nestedType); + MarkType (ref reflectionContext, nestedType); + } + + return result.ToArray (); + } + + void MarkPropertiesOnTypeHierarchy (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var property in type.GetPropertiesOnTypeHierarchy (_context, filter, bindingFlags)) + MarkProperty (ref reflectionContext, property); + } + + void MarkEventsOnTypeHierarchy (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var @event in type.GetEventsOnTypeHierarchy (_context, filter, bindingFlags)) + MarkEvent (ref reflectionContext, @event); + } + + void ValidateGenericMethodInstantiation ( + ref ReflectionPatternContext reflectionContext, + MethodDefinition genericMethod, + ValueNode? genericParametersArray, + MethodReference reflectionMethod) + { + if (!genericMethod.HasGenericParameters) { + reflectionContext.RecordHandledPattern (); + return; + } + + if (!AnalyzeGenericInstantiationTypeArray (genericParametersArray, ref reflectionContext, reflectionMethod, genericMethod.GenericParameters)) { + reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericMethod, + new DiagnosticString (DiagnosticId.MakeGenericMethod).GetMessage (DiagnosticUtilities.GetMethodSignatureDisplayName (reflectionMethod))); + } else { + reflectionContext.RecordHandledPattern (); + } + } + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (BindingFlags? bindingFlags) => + (HasBindingFlag (bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicNestedTypes : DynamicallyAccessedMemberTypes.None) | + (HasBindingFlag (bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None) | + (BindingFlagsAreUnsupported (bindingFlags) ? DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None); + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (BindingFlags? bindingFlags) => + (HasBindingFlag (bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicConstructors : DynamicallyAccessedMemberTypes.None) | + (HasBindingFlag (bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None) | + (BindingFlagsAreUnsupported (bindingFlags) ? DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None); + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (BindingFlags? bindingFlags) => + (HasBindingFlag (bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicMethods : DynamicallyAccessedMemberTypes.None) | + (HasBindingFlag (bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None) | + (BindingFlagsAreUnsupported (bindingFlags) ? DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None); + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields (BindingFlags? bindingFlags) => + (HasBindingFlag (bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicFields : DynamicallyAccessedMemberTypes.None) | + (HasBindingFlag (bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None) | + (BindingFlagsAreUnsupported (bindingFlags) ? DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None); + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (BindingFlags? bindingFlags) => + (HasBindingFlag (bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicProperties : DynamicallyAccessedMemberTypes.None) | + (HasBindingFlag (bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None) | + (BindingFlagsAreUnsupported (bindingFlags) ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None); + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents (BindingFlags? bindingFlags) => + (HasBindingFlag (bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicEvents : DynamicallyAccessedMemberTypes.None) | + (HasBindingFlag (bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None) | + (BindingFlagsAreUnsupported (bindingFlags) ? DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None); + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers (BindingFlags? bindingFlags) => + GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags) | + GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents (bindingFlags) | + GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields (bindingFlags) | + GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags) | + GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (bindingFlags) | + GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (bindingFlags); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionPatternContext.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionPatternContext.cs new file mode 100644 index 00000000000000..870a302c4dddda --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionPatternContext.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Mono.Linker.Dataflow +{ + /// + /// Helper struct to pass around context information about reflection pattern + /// as a single parameter (and have a way to extend this in the future if we need to easily). + /// Also implements a simple validation mechanism to check that the code does report patter recognition + /// results for all methods it works on. + /// The promise of the pattern recorder is that for a given reflection method, it will either not talk + /// about it ever, or it will always report recognized/unrecognized. + /// + struct ReflectionPatternContext : IDisposable + { + readonly LinkContext _context; +#if DEBUG + bool _patternAnalysisAttempted; + bool _patternReported; +#endif + + public MessageOrigin Origin { get; init; } + public ICustomAttributeProvider Source { get => Origin.Provider; } + public IMetadataTokenProvider MemberWithRequirements { get; init; } + public Instruction Instruction { get; init; } + public bool ReportingEnabled { get; init; } + + public ReflectionPatternContext ( + LinkContext context, + bool reportingEnabled, + in MessageOrigin origin, + IMetadataTokenProvider memberWithRequirements, + Instruction instruction = null) + { + _context = context; + ReportingEnabled = reportingEnabled; + Origin = origin; + MemberWithRequirements = memberWithRequirements; + Instruction = instruction; + +#if DEBUG + _patternAnalysisAttempted = false; + _patternReported = false; +#endif + } + +#pragma warning disable CA1822 + [Conditional ("DEBUG")] + public void AnalyzingPattern () + { +#if DEBUG + _patternAnalysisAttempted = true; +#endif + } + + [Conditional ("DEBUG")] + public void RecordHandledPattern () + { +#if DEBUG + _patternReported = true; +#endif + } +#pragma warning restore CA1822 + + public void RecordRecognizedPattern (IMetadataTokenProvider accessedItem, Action mark) + { +#if DEBUG + if (!_patternAnalysisAttempted) + throw new InvalidOperationException ($"Internal error: To correctly report all patterns, when starting to analyze a pattern the AnalyzingPattern must be called first. {Source} -> {MemberWithRequirements}"); + + _patternReported = true; +#endif + + mark (); + + if (ReportingEnabled) + _context.ReflectionPatternRecorder.RecognizedReflectionAccessPattern (Source, Instruction, accessedItem); + } + + public void RecordUnrecognizedPattern (int messageCode, string message) + { +#if DEBUG + if (!_patternAnalysisAttempted) + throw new InvalidOperationException ($"Internal error: To correctly report all patterns, when starting to analyze a pattern the AnalyzingPattern must be called first. {Source} -> {MemberWithRequirements}"); + + _patternReported = true; +#endif + + if (ReportingEnabled) + _context.ReflectionPatternRecorder.UnrecognizedReflectionAccessPattern (Origin, Source, Instruction, MemberWithRequirements, message, messageCode); + } + + public void Dispose () + { +#if DEBUG + if (_patternAnalysisAttempted && !_patternReported) + throw new InvalidOperationException ($"Internal error: A reflection pattern was analyzed, but no result was reported. {Source} -> {MemberWithRequirements}"); +#endif + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ScannerExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ScannerExtensions.cs new file mode 100644 index 00000000000000..aa7b3f05e146dc --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ScannerExtensions.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Mono.Linker.Dataflow +{ + static class ScannerExtensions + { + public static bool IsControlFlowInstruction (in this OpCode opcode) + { + return opcode.FlowControl == FlowControl.Branch + || opcode.FlowControl == FlowControl.Cond_Branch + || (opcode.FlowControl == FlowControl.Return && opcode.Code != Code.Ret); + } + + public static HashSet ComputeBranchTargets (this MethodBody methodBody) + { + HashSet branchTargets = new HashSet (); + foreach (Instruction operation in methodBody.Instructions) { + if (!operation.OpCode.IsControlFlowInstruction ()) + continue; + Object value = operation.Operand; + if (value is Instruction inst) { + branchTargets.Add (inst.Offset); + } else if (value is Instruction[] instructions) { + foreach (Instruction switchLabel in instructions) { + branchTargets.Add (switchLabel.Offset); + } + } + } + foreach (ExceptionHandler einfo in methodBody.ExceptionHandlers) { + if (einfo.HandlerType == ExceptionHandlerType.Filter) { + branchTargets.Add (einfo.FilterStart.Offset); + } + branchTargets.Add (einfo.HandlerStart.Offset); + } + return branchTargets; + } + + public static bool IsByRefOrPointer (this TypeReference typeRef) + { + return typeRef.IsByReference || typeRef.IsPointer; + } + } + +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ValueNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ValueNode.cs new file mode 100644 index 00000000000000..0afda819cd506d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ValueNode.cs @@ -0,0 +1,1377 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using Mono.Cecil; +using FieldDefinition = Mono.Cecil.FieldDefinition; +using GenericParameter = Mono.Cecil.GenericParameter; +using TypeDefinition = Mono.Cecil.TypeDefinition; + +#nullable enable + +namespace Mono.Linker.Dataflow +{ + public enum ValueNodeKind + { + Invalid, // in case the Kind field is not initialized properly + + Unknown, // unknown value, has StaticType from context + + Null, // known value + SystemType, // known value - TypeRepresented + RuntimeTypeHandle, // known value - TypeRepresented + KnownString, // known value - Contents + ConstInt, // known value - Int32 + AnnotatedString, // string with known annotation + + MethodParameter, // symbolic placeholder + MethodReturn, // symbolic placeholder + + RuntimeMethodHandle, // known value - MethodRepresented + SystemReflectionMethodBase, // known value - MethodRepresented + + RuntimeTypeHandleForGenericParameter, // symbolic placeholder for generic parameter + SystemTypeForGenericParameter, // symbolic placeholder for generic parameter + + MergePoint, // structural, multiplexer - Values + GetTypeFromString, // structural, could be known value - KnownString + Array, // structural, could be known value - Array + + LoadField, // structural, could be known value - InstanceValue + } + + /// + /// A ValueNode represents a value in the IL dataflow analysis. It may not contain complete information as it is a + /// best-effort representation. Additionally, as the analysis is linear and does not account for control flow, any + /// given ValueNode may represent multiple values simultaneously. (This occurs, for example, at control flow join + /// points when both paths yield values on the IL stack or in a local.) + /// + public abstract class ValueNode : IEquatable + { + public ValueNode () + { +#if false // Helpful for debugging a cycle that has inadvertently crept into the graph + if (this.DetectCycle(new HashSet())) + { + throw new Exception("Found a cycle"); + } +#endif + } + + /// + /// The 'kind' of value node -- this represents the most-derived type and allows us to switch over and do + /// equality checks without the cost of casting. Intermediate non-leaf types in the ValueNode hierarchy should + /// be abstract. + /// + public ValueNodeKind Kind { get; protected set; } + + /// + /// The IL type of the value, represented as closely as possible, but not always exact. It can be null, for + /// example, when the analysis is imprecise or operating on malformed IL. + /// + public TypeDefinition? StaticType { get; protected set; } + + /// + /// Allows the enumeration of the direct children of this node. The ChildCollection struct returned here + /// supports 'foreach' without allocation. + /// + public ChildCollection Children { get { return new ChildCollection (this); } } + + /// + /// This property allows you to enumerate all 'unique values' represented by a given ValueNode. The basic idea + /// is that there will be no MergePointValues in the returned ValueNodes and all structural operations will be + /// applied so that each 'unique value' can be considered on its own without regard to the structure that led to + /// it. + /// + public UniqueValueCollection UniqueValuesInternal { + get { + return new UniqueValueCollection (this); + } + } + + /// + /// This protected method is how nodes implement the UniqueValues property. It is protected because it returns + /// an IEnumerable and we want to avoid allocating an enumerator for the exceedingly common case of there being + /// only one value in the enumeration. The UniqueValueCollection returned by the UniqueValues property handles + /// this detail. + /// + protected abstract IEnumerable EvaluateUniqueValues (); + + /// + /// RepresentsExactlyOneValue is used by the UniqueValues property to allow us to bypass allocating an + /// enumerator to return just one value. If a node returns 'true' from RepresentsExactlyOneValue, it must also + /// return that one value from GetSingleUniqueValue. If it always returns 'false', it doesn't need to implement + /// GetSingleUniqueValue. + /// + protected virtual bool RepresentsExactlyOneValue { get { return false; } } + + /// + /// GetSingleUniqueValue is called if, and only if, RepresentsExactlyOneValue returns true. It allows us to + /// bypass the allocation of an enumerator for the common case of returning exactly one value. + /// + protected virtual ValueNode GetSingleUniqueValue () + { + // Not implemented because RepresentsExactlyOneValue returns false and, therefore, this method should be + // unreachable. + throw new NotImplementedException (); + } + + protected abstract int NumChildren { get; } + protected abstract ValueNode ChildAt (int index); + + public virtual bool Equals (ValueNode? other) + { + return other != null && this.Kind == other.Kind && this.StaticType == other.StaticType; + } + + public abstract override int GetHashCode (); + + /// + /// Each node type must implement this to stringize itself. The expectation is that it is implemented using + /// ValueNodeDump.ValueNodeToString(), passing any non-ValueNode properties of interest (e.g. + /// SystemTypeValue.TypeRepresented). Properties that are invariant on a particular node type + /// should be omitted for clarity. + /// + protected abstract string NodeToString (); + + public override string ToString () + { + return NodeToString (); + } + + public override bool Equals (object? other) + { + if (!(other is ValueNode)) + return false; + + return this.Equals ((ValueNode) other); + } + + #region Specialized Collection Nested Types + /// + /// ChildCollection struct is used to wrap the operations on a node involving its children. In particular, the + /// struct implements a GetEnumerator method that is used to allow "foreach (ValueNode node in myNode.Children)" + /// without heap allocations. + /// + public struct ChildCollection : IEnumerable + { + /// + /// Enumerator for children of a ValueNode. Allows foreach(var child in node.Children) to work without + /// allocating a heap-based enumerator. + /// + public struct Enumerator : IEnumerator + { + int _index; + readonly ValueNode _parent; + + public Enumerator (ValueNode parent) + { + _parent = parent; + _index = -1; + } + + public ValueNode Current { get { return _parent.ChildAt (_index); } } + + object System.Collections.IEnumerator.Current { get { return Current; } } + + public bool MoveNext () + { + _index++; + return (_parent != null) ? (_index < _parent.NumChildren) : false; + } + + public void Reset () + { + _index = -1; + } + + public void Dispose () + { + } + } + + readonly ValueNode _parentNode; + + public ChildCollection (ValueNode parentNode) { _parentNode = parentNode; } + + // Used by C# 'foreach', when strongly typed, to avoid allocation. + public Enumerator GetEnumerator () + { + return new Enumerator (_parentNode); + } + + IEnumerator IEnumerable.GetEnumerator () + { + // note the boxing! + return new Enumerator (_parentNode); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () + { + // note the boxing! + return new Enumerator (_parentNode); + } + + public int Count { get { return (_parentNode != null) ? _parentNode.NumChildren : 0; } } + } + + /// + /// UniqueValueCollection is used to wrap calls to ValueNode.EvaluateUniqueValues. If a ValueNode represents + /// only one value, then foreach(ValueNode value in node.UniqueValues) will not allocate a heap-based enumerator. + /// + /// This is implented by having each ValueNode tell us whether or not it represents exactly one value or not. + /// If it does, we fetch it with ValueNode.GetSingleUniqueValue(), otherwise, we fall back to the usual heap- + /// based IEnumerable returned by ValueNode.EvaluateUniqueValues. + /// + public struct UniqueValueCollection : IEnumerable + { + readonly IEnumerable? _multiValueEnumerable; + readonly ValueNode? _treeNode; + + public UniqueValueCollection (ValueNode node) + { + if (node.RepresentsExactlyOneValue) { + _multiValueEnumerable = null; + _treeNode = node; + } else { + _multiValueEnumerable = node.EvaluateUniqueValues (); + _treeNode = null; + } + } + + public Enumerator GetEnumerator () + { + return new Enumerator (_treeNode, _multiValueEnumerable); + } + + IEnumerator IEnumerable.GetEnumerator () + { + if (_multiValueEnumerable != null) { + return _multiValueEnumerable.GetEnumerator (); + } + + // note the boxing! + return GetEnumerator (); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () + { + if (_multiValueEnumerable != null) { + return _multiValueEnumerable.GetEnumerator (); + } + + // note the boxing! + return GetEnumerator (); + } + + + public struct Enumerator : IEnumerator + { + readonly IEnumerator? _multiValueEnumerator; + readonly ValueNode? _singleValueNode; + int _index; + + public Enumerator (ValueNode? treeNode, IEnumerable? multiValueEnumerable) + { + Debug.Assert (treeNode != null || multiValueEnumerable != null); + _singleValueNode = treeNode?.GetSingleUniqueValue (); + _multiValueEnumerator = multiValueEnumerable?.GetEnumerator (); + _index = -1; + } + + public void Reset () + { + if (_multiValueEnumerator != null) { + _multiValueEnumerator.Reset (); + return; + } + + _index = -1; + } + + public bool MoveNext () + { + if (_multiValueEnumerator != null) + return _multiValueEnumerator.MoveNext (); + + _index++; + return _index == 0; + } + + public ValueNode Current { + get { + if (_multiValueEnumerator != null) + return _multiValueEnumerator.Current; + + if (_index == 0) + return _singleValueNode!; + + throw new InvalidOperationException (); + } + } + + object System.Collections.IEnumerator.Current { get { return Current; } } + + public void Dispose () + { + } + } + } + #endregion + } + + /// + /// LeafValueNode represents a 'leaf' in the expression tree. In other words, the node has no ValueNode children. + /// It *may* still have non-ValueNode 'properties' that are interesting. This class serves, primarily, as a way to + /// collect up the very common implmentation of NumChildren/ChildAt for leaf nodes and the "represents exactly one + /// value" optimization. These things aren't on the ValueNode base class because, otherwise, new node types + /// deriving from ValueNode may 'forget' to implement these things. So this class allows them to remain abstract in + /// ValueNode while still having a common implementation for all the leaf nodes. + /// + public abstract class LeafValueNode : ValueNode + { + protected override int NumChildren { get { return 0; } } + protected override ValueNode ChildAt (int index) { throw new InvalidOperationException (); } + + protected override bool RepresentsExactlyOneValue { get { return true; } } + + protected override ValueNode GetSingleUniqueValue () { return this; } + + + protected override IEnumerable EvaluateUniqueValues () + { + // Leaf values should not represent more than one value. This method should be unreachable as long as + // RepresentsExactlyOneValue returns true. + throw new NotImplementedException (); + } + } + + // These are extension methods because we want to allow the use of them on null 'this' pointers. + internal static class ValueNodeExtensions + { + /// + /// Returns true if a ValueNode graph contains a cycle + /// + /// Node to evaluate + /// Set of nodes previously seen on the current arc. Callers may pass a non-empty set + /// to test whether adding that set to this node would create a cycle. Contents will be modified by the walk + /// and should not be used by the caller after returning + /// Optional. The set of all nodes encountered during a walk after DetectCycle returns + /// + public static bool DetectCycle (this ValueNode? node, HashSet seenNodes, HashSet? allNodesSeen) + { + if (node == null) + return false; + + if (seenNodes.Contains (node)) + return true; + + seenNodes.Add (node); + + if (allNodesSeen != null) { + allNodesSeen.Add (node); + } + + bool foundCycle = false; + switch (node.Kind) { + // + // Leaf nodes + // + case ValueNodeKind.Unknown: + case ValueNodeKind.Null: + case ValueNodeKind.SystemType: + case ValueNodeKind.RuntimeTypeHandle: + case ValueNodeKind.KnownString: + case ValueNodeKind.AnnotatedString: + case ValueNodeKind.ConstInt: + case ValueNodeKind.MethodParameter: + case ValueNodeKind.MethodReturn: + case ValueNodeKind.SystemTypeForGenericParameter: + case ValueNodeKind.RuntimeTypeHandleForGenericParameter: + case ValueNodeKind.SystemReflectionMethodBase: + case ValueNodeKind.RuntimeMethodHandle: + case ValueNodeKind.LoadField: + break; + + // + // Nodes with children + // + case ValueNodeKind.MergePoint: + foreach (ValueNode val in ((MergePointValue) node).Values) { + if (val.DetectCycle (seenNodes, allNodesSeen)) { + foundCycle = true; + } + } + break; + + case ValueNodeKind.GetTypeFromString: + GetTypeFromStringValue gtfsv = (GetTypeFromStringValue) node; + foundCycle = gtfsv.AssemblyIdentity.DetectCycle (seenNodes, allNodesSeen); + foundCycle |= gtfsv.NameString.DetectCycle (seenNodes, allNodesSeen); + break; + + case ValueNodeKind.Array: + ArrayValue av = (ArrayValue) node; + foundCycle = av.Size.DetectCycle (seenNodes, allNodesSeen); + foreach (ValueBasicBlockPair pair in av.IndexValues.Values) { + foundCycle |= pair.Value.DetectCycle (seenNodes, allNodesSeen); + } + break; + + default: + throw new Exception (String.Format ("Unknown node kind: {0}", node.Kind)); + } + seenNodes.Remove (node); + + return foundCycle; + } + + public static ValueNode.UniqueValueCollection UniqueValues (this ValueNode? node) + { + if (node == null) + return new ValueNode.UniqueValueCollection (UnknownValue.Instance); + + return node.UniqueValuesInternal; + } + + public static int? AsConstInt (this ValueNode? node) + { + if (node is ConstIntValue constInt) + return constInt.Value; + return null; + } + } + + internal static class ValueNodeDump + { + internal static string ValueNodeToString (ValueNode? node, params object[] args) + { + if (node == null) + return ""; + + StringBuilder sb = new StringBuilder (); + sb.Append (node.Kind.ToString ()); + sb.Append ("("); + if (args != null) { + for (int i = 0; i < args.Length; i++) { + if (i > 0) + sb.Append (","); + sb.Append (args[i] == null ? "" : args[i].ToString ()); + } + } + sb.Append (")"); + return sb.ToString (); + } + + static string GetIndent (int level) + { + StringBuilder sb = new StringBuilder (level * 2); + for (int i = 0; i < level; i++) + sb.Append (" "); + return sb.ToString (); + } + + public static void DumpTree (this ValueNode node, System.IO.TextWriter? writer = null, int indentLevel = 0) + { + if (writer == null) + writer = Console.Out; + + writer.Write (GetIndent (indentLevel)); + if (node == null) { + writer.WriteLine (""); + return; + } + + writer.WriteLine (node); + foreach (ValueNode child in node.Children) { + child.DumpTree (writer, indentLevel + 1); + } + } + } + + /// + /// Represents an unknown value. + /// + class UnknownValue : LeafValueNode + { + private UnknownValue () + { + Kind = ValueNodeKind.Unknown; + StaticType = null; + } + + public static UnknownValue Instance { get; } = new UnknownValue (); + + public override bool Equals (ValueNode? other) + { + return base.Equals (other); + } + + public override int GetHashCode () + { + // All instances of UnknownValue are equivalent, so they all hash to the same hashcode. This one was + // chosen for no particular reason at all. + return 0x98052; + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this); + } + } + + class NullValue : LeafValueNode + { + private NullValue () + { + Kind = ValueNodeKind.Null; + StaticType = null; + } + + public override bool Equals (ValueNode? other) + { + return base.Equals (other); + } + + public static NullValue Instance { get; } = new NullValue (); + + public override int GetHashCode () + { + // All instances of NullValue are equivalent, so they all hash to the same hashcode. This one was + // chosen for no particular reason at all. + return 0x90210; + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this); + } + } + + /// + /// This is a known System.Type value. TypeRepresented is the 'value' of the System.Type. + /// + class SystemTypeValue : LeafValueNode + { + public SystemTypeValue (TypeDefinition typeRepresented) + { + Kind = ValueNodeKind.SystemType; + + // Should be System.Type - but we don't have any use case where tracking it like that would matter + StaticType = null; + + TypeRepresented = typeRepresented; + } + + public TypeDefinition TypeRepresented { get; private set; } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + return Equals (this.TypeRepresented, ((SystemTypeValue) other).TypeRepresented); + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, TypeRepresented); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, TypeRepresented); + } + } + + /// + /// This is the System.RuntimeTypeHandle equivalent to a node. + /// + class RuntimeTypeHandleValue : LeafValueNode + { + public RuntimeTypeHandleValue (TypeDefinition typeRepresented) + { + Kind = ValueNodeKind.RuntimeTypeHandle; + + // Should be System.RuntimeTypeHandle, but we don't have a use case for it like that + StaticType = null; + + TypeRepresented = typeRepresented; + } + + public TypeDefinition TypeRepresented { get; } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + return Equals (this.TypeRepresented, ((RuntimeTypeHandleValue) other).TypeRepresented); + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, TypeRepresented); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, TypeRepresented); + } + } + + /// + /// This is a System.Type value which represents generic parameter (basically result of typeof(T)) + /// Its actual type is unknown, but it can have annotations. + /// + class SystemTypeForGenericParameterValue : LeafValueWithDynamicallyAccessedMemberNode + { + public SystemTypeForGenericParameterValue (GenericParameter genericParameter, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + : base (genericParameter) + { + Kind = ValueNodeKind.SystemTypeForGenericParameter; + + // Should be System.Type, but we don't have a use case for it + StaticType = null; + + GenericParameter = genericParameter; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public GenericParameter GenericParameter { get; } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + var otherValue = (SystemTypeForGenericParameterValue) other; + return this.GenericParameter == otherValue.GenericParameter && this.DynamicallyAccessedMemberTypes == otherValue.DynamicallyAccessedMemberTypes; + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, GenericParameter, DynamicallyAccessedMemberTypes); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, GenericParameter, DynamicallyAccessedMemberTypes); + } + } + + /// + /// This is the System.RuntimeTypeHandle equivalent to a node. + /// + class RuntimeTypeHandleForGenericParameterValue : LeafValueNode + { + public RuntimeTypeHandleForGenericParameterValue (GenericParameter genericParameter) + { + Kind = ValueNodeKind.RuntimeTypeHandleForGenericParameter; + + // Should be System.RuntimeTypeHandle, but we don't have a use case for it + StaticType = null; + + GenericParameter = genericParameter; + } + + public GenericParameter GenericParameter { get; } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + return Equals (this.GenericParameter, ((RuntimeTypeHandleForGenericParameterValue) other).GenericParameter); + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, GenericParameter); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, GenericParameter); + } + } + + /// + /// This is the System.RuntimeMethodHandle equivalent to a node. + /// + class RuntimeMethodHandleValue : LeafValueNode + { + public RuntimeMethodHandleValue (MethodDefinition methodRepresented) + { + Kind = ValueNodeKind.RuntimeMethodHandle; + + // Should be System.RuntimeMethodHandle, but we don't have a use case for it + StaticType = null; + + MethodRepresented = methodRepresented; + } + + public MethodDefinition MethodRepresented { get; } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + return Equals (this.MethodRepresented, ((RuntimeMethodHandleValue) other).MethodRepresented); + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, MethodRepresented); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, MethodRepresented); + } + } + + /// + /// This is a known System.Reflection.MethodBase value. MethodRepresented is the 'value' of the MethodBase. + /// + class SystemReflectionMethodBaseValue : LeafValueNode + { + public SystemReflectionMethodBaseValue (MethodDefinition methodRepresented) + { + Kind = ValueNodeKind.SystemReflectionMethodBase; + + // Should be System.Reflection.MethodBase, but we don't have a use case for it + StaticType = null; + + MethodRepresented = methodRepresented; + } + + public MethodDefinition MethodRepresented { get; private set; } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + return Equals (this.MethodRepresented, ((SystemReflectionMethodBaseValue) other).MethodRepresented); + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, MethodRepresented); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, MethodRepresented); + } + } + + /// + /// A known string - such as the result of a ldstr. + /// + class KnownStringValue : LeafValueNode + { + public KnownStringValue (string contents) + { + Kind = ValueNodeKind.KnownString; + + // Should be System.String, but we don't have a use case for it + StaticType = null; + + Contents = contents; + } + + public string Contents { get; private set; } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + return this.Contents == ((KnownStringValue) other).Contents; + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, Contents); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, "\"" + Contents + "\""); + } + } + + /// + /// Base class for all nodes which can have dynamically accessed member annotation. + /// + abstract class LeafValueWithDynamicallyAccessedMemberNode : LeafValueNode + { + public LeafValueWithDynamicallyAccessedMemberNode (IMetadataTokenProvider sourceContext) + { + SourceContext = sourceContext; + } + + public IMetadataTokenProvider SourceContext { get; private set; } + + /// + /// The bitfield of dynamically accessed member types the node guarantees + /// + public DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; protected set; } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + var otherValue = (LeafValueWithDynamicallyAccessedMemberNode) other; + return SourceContext == otherValue.SourceContext + && DynamicallyAccessedMemberTypes == otherValue.DynamicallyAccessedMemberTypes; + } + } + + /// + /// A value that came from a method parameter - such as the result of a ldarg. + /// + class MethodParameterValue : LeafValueWithDynamicallyAccessedMemberNode + { + public MethodParameterValue (TypeDefinition? staticType, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, IMetadataTokenProvider sourceContext) + : base (sourceContext) + { + Kind = ValueNodeKind.MethodParameter; + StaticType = staticType; + ParameterIndex = parameterIndex; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public int ParameterIndex { get; } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + var otherValue = (MethodParameterValue) other; + return this.ParameterIndex == otherValue.ParameterIndex; + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, ParameterIndex, DynamicallyAccessedMemberTypes); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, ParameterIndex, DynamicallyAccessedMemberTypes); + } + } + + /// + /// String with a known annotation. + /// + class AnnotatedStringValue : LeafValueWithDynamicallyAccessedMemberNode + { + public AnnotatedStringValue (IMetadataTokenProvider sourceContext, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + : base (sourceContext) + { + Kind = ValueNodeKind.AnnotatedString; + + // Should be System.String, but we don't have a use case for it + StaticType = null; + + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public override bool Equals (ValueNode? other) + { + return base.Equals (other); + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, DynamicallyAccessedMemberTypes); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, DynamicallyAccessedMemberTypes); + } + } + + /// + /// Return value from a method + /// + class MethodReturnValue : LeafValueWithDynamicallyAccessedMemberNode + { + public MethodReturnValue (TypeDefinition? staticType, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, IMetadataTokenProvider sourceContext) + : base (sourceContext) + { + Kind = ValueNodeKind.MethodReturn; + StaticType = staticType; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public override bool Equals (ValueNode? other) + { + return base.Equals (other); + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, DynamicallyAccessedMemberTypes); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, DynamicallyAccessedMemberTypes); + } + } + + /// + /// A merge point commonly occurs due to control flow in a method body. It represents a set of values + /// from different paths through the method. It is the reason for EvaluateUniqueValues, which essentially + /// provides an enumeration over all the concrete values represented by a given ValueNode after 'erasing' + /// the merge point nodes. + /// + class MergePointValue : ValueNode + { + private MergePointValue (ValueNode one, ValueNode two) + { + Kind = ValueNodeKind.MergePoint; + StaticType = null; + m_values = new ValueNodeHashSet (); + + if (one.Kind == ValueNodeKind.MergePoint) { + MergePointValue mpvOne = (MergePointValue) one; + foreach (ValueNode value in mpvOne.Values) + m_values.Add (value); + } else + m_values.Add (one); + + if (two.Kind == ValueNodeKind.MergePoint) { + MergePointValue mpvTwo = (MergePointValue) two; + foreach (ValueNode value in mpvTwo.Values) + m_values.Add (value); + } else + m_values.Add (two); + } + + public MergePointValue () + { + Kind = ValueNodeKind.MergePoint; + m_values = new ValueNodeHashSet (); + } + + public void AddValue (ValueNode node) + { + // we are mutating our state, so we must invalidate any cached knowledge + //InvalidateIsOpen (); + + if (node.Kind == ValueNodeKind.MergePoint) { + foreach (ValueNode value in ((MergePointValue) node).Values) + m_values.Add (value); + } else + m_values.Add (node); + +#if false + if (this.DetectCycle(new HashSet())) + { + throw new Exception("Found a cycle"); + } +#endif + } + + readonly ValueNodeHashSet m_values; + + public ValueNodeHashSet Values { get { return m_values; } } + + protected override int NumChildren { get { return Values.Count; } } + protected override ValueNode ChildAt (int index) + { + if (index < NumChildren) + return Values.ElementAt (index); + throw new InvalidOperationException (); + } + + public static ValueNode? MergeValues (ValueNode? one, ValueNode? two) + { + if (one == null) + return two; + else if (two == null) + return one; + else if (one.Equals (two)) + return one; + else + return new MergePointValue (one, two); + } + + protected override IEnumerable EvaluateUniqueValues () + { + foreach (ValueNode value in Values) { + foreach (ValueNode uniqueValue in value.UniqueValuesInternal) { + yield return uniqueValue; + } + } + } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + MergePointValue otherMpv = (MergePointValue) other; + if (this.Values.Count != otherMpv.Values.Count) + return false; + + foreach (ValueNode value in this.Values) { + if (!otherMpv.Values.Contains (value)) + return false; + } + return true; + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, Values); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this); + } + } + + delegate TypeDefinition TypeResolver (string assemblyString, string typeString); + + /// + /// The result of a Type.GetType. + /// AssemblyIdentity is the scope in which to resolve if the type name string is not assembly-qualified. + /// + +#pragma warning disable CA1812 // GetTypeFromStringValue is never instantiated + class GetTypeFromStringValue : ValueNode + { + private readonly TypeResolver _resolver; + + public GetTypeFromStringValue (TypeResolver resolver, ValueNode assemblyIdentity, ValueNode nameString) + { + _resolver = resolver; + Kind = ValueNodeKind.GetTypeFromString; + + // Should be System.Type, but we don't have a use case for it + StaticType = null; + + AssemblyIdentity = assemblyIdentity; + NameString = nameString; + } + + public ValueNode AssemblyIdentity { get; private set; } + + public ValueNode NameString { get; private set; } + + protected override int NumChildren { get { return 2; } } + protected override ValueNode ChildAt (int index) + { + if (index == 0) return AssemblyIdentity; + if (index == 1) return NameString; + throw new InvalidOperationException (); + } + + protected override IEnumerable EvaluateUniqueValues () + { + HashSet? names = null; + + foreach (ValueNode nameStringValue in NameString.UniqueValuesInternal) { + if (nameStringValue.Kind == ValueNodeKind.KnownString) { + if (names == null) { + names = new HashSet (); + } + + string typeName = ((KnownStringValue) nameStringValue).Contents; + names.Add (typeName); + } + } + + bool foundAtLeastOne = false; + + if (names != null) { + foreach (ValueNode assemblyValue in AssemblyIdentity.UniqueValuesInternal) { + if (assemblyValue.Kind == ValueNodeKind.KnownString) { + string assemblyName = ((KnownStringValue) assemblyValue).Contents; + + foreach (string name in names) { + TypeDefinition typeDefinition = _resolver (assemblyName, name); + if (typeDefinition != null) { + foundAtLeastOne = true; + yield return new SystemTypeValue (typeDefinition); + } + } + } + } + } + + if (!foundAtLeastOne) + yield return UnknownValue.Instance; + } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + GetTypeFromStringValue otherGtfs = (GetTypeFromStringValue) other; + + return this.AssemblyIdentity.Equals (otherGtfs.AssemblyIdentity) && + this.NameString.Equals (otherGtfs.NameString); + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, AssemblyIdentity, NameString); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, NameString); + } + } + + /// + /// A representation of a ldfld. Note that we don't have a representation of objects containing fields + /// so there isn't much that can be done with this node type yet. + /// + class LoadFieldValue : LeafValueWithDynamicallyAccessedMemberNode + { + public LoadFieldValue (TypeDefinition? staticType, FieldDefinition fieldToLoad, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + : base (fieldToLoad) + { + Kind = ValueNodeKind.LoadField; + StaticType = staticType; + Field = fieldToLoad; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public FieldDefinition Field { get; private set; } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + LoadFieldValue otherLfv = (LoadFieldValue) other; + return Equals (this.Field, otherLfv.Field); + } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, Field, DynamicallyAccessedMemberTypes); + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, Field, DynamicallyAccessedMemberTypes); + } + } + + /// + /// Represents a ldc on an int32. + /// + class ConstIntValue : LeafValueNode + { + public ConstIntValue (int value) + { + Kind = ValueNodeKind.ConstInt; + + // Should be System.Int32, but we don't have a usecase for it right now + StaticType = null; + + Value = value; + } + + public int Value { get; private set; } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, Value); + } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + ConstIntValue otherCiv = (ConstIntValue) other; + return Value == otherCiv.Value; + } + + protected override string NodeToString () + { + return ValueNodeDump.ValueNodeToString (this, Value); + } + } + + class ArrayValue : ValueNode + { + protected override int NumChildren => 1 + IndexValues.Count; + + /// + /// Constructs an array value of the given size + /// + public ArrayValue (ValueNode? size, TypeReference elementType) + { + Kind = ValueNodeKind.Array; + + // Should be System.Array (or similar), but we don't have a use case for it + StaticType = null; + + Size = size ?? UnknownValue.Instance; + ElementType = elementType; + IndexValues = new Dictionary (); + } + + private ArrayValue (ValueNode size, TypeReference elementType, Dictionary indexValues) + : this (size, elementType) + { + IndexValues = indexValues; + } + + public ValueNode Size { get; } + public TypeReference ElementType { get; } + public Dictionary IndexValues { get; } + + public override int GetHashCode () + { + return HashCode.Combine (Kind, Size); + } + + public override bool Equals (ValueNode? other) + { + if (!base.Equals (other)) + return false; + + ArrayValue otherArr = (ArrayValue) other; + bool equals = Size.Equals (otherArr.Size); + equals &= IndexValues.Count == otherArr.IndexValues.Count; + if (!equals) + return false; + + // If both sets T and O are the same size and "T intersect O" is empty, then T == O. + HashSet> thisValueSet = new (IndexValues); + HashSet> otherValueSet = new (otherArr.IndexValues); + thisValueSet.ExceptWith (otherValueSet); + return thisValueSet.Count == 0; + } + + protected override string NodeToString () + { + // TODO: Use StringBuilder and remove Linq usage. + return $"(Array Size:{ValueNodeDump.ValueNodeToString (this, Size)}, Values:({string.Join (',', IndexValues.Select (v => $"({v.Key},{ValueNodeDump.ValueNodeToString (v.Value.Value)})"))})"; + } + + protected override IEnumerable EvaluateUniqueValues () + { + foreach (var sizeConst in Size.UniqueValuesInternal) + yield return new ArrayValue (sizeConst, ElementType, IndexValues); + } + + protected override ValueNode ChildAt (int index) + { + if (index == 0) return Size; + if (index - 1 <= IndexValues.Count) + return IndexValues.Values.ElementAt (index - 1).Value!; + + throw new InvalidOperationException (); + } + } + + #region ValueNode Collections + public class ValueNodeList : List + { + public ValueNodeList () + { + } + + public ValueNodeList (int capacity) + : base (capacity) + { + } + + public ValueNodeList (List other) + : base (other) + { + } + + public override int GetHashCode () + { + return HashUtils.CalcHashCodeEnumerable (this); + } + + public override bool Equals (object? other) + { + if (!(other is ValueNodeList otherList)) + return false; + + if (otherList.Count != Count) + return false; + + for (int i = 0; i < Count; i++) { + if (!(otherList[i]?.Equals (this[i]) ?? (this[i] is null))) + return false; + } + return true; + } + } + + class ValueNodeHashSet : HashSet + { + public override int GetHashCode () + { + return HashUtils.CalcHashCodeEnumerable (this); + } + + public override bool Equals (object? other) + { + if (!(other is ValueNodeHashSet otherSet)) + return false; + + if (otherSet.Count != Count) + return false; + + IEnumerator thisEnumerator = this.GetEnumerator (); + IEnumerator otherEnumerator = otherSet.GetEnumerator (); + + for (int i = 0; i < Count; i++) { + thisEnumerator.MoveNext (); + otherEnumerator.MoveNext (); + if (!thisEnumerator.Current.Equals (otherEnumerator.Current)) + return false; + } + return true; + } + } + #endregion + + static class HashUtils + { + public static int CalcHashCodeEnumerable (IEnumerable list) where T : class? + { + HashCode hashCode = new HashCode (); + foreach (var item in list) + hashCode.Add (item); + return hashCode.ToHashCode (); + } + } + + public struct ValueBasicBlockPair + { + public ValueNode? Value; + public int BasicBlockIndex; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs new file mode 100644 index 00000000000000..5f4e52e7e28181 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -0,0 +1,3151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +using Internal.IL; +using Internal.TypeSystem; + +using BindingFlags = System.Reflection.BindingFlags; +using NodeFactory = ILCompiler.DependencyAnalysis.NodeFactory; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue; +using CustomAttributeTypedArgument = System.Reflection.Metadata.CustomAttributeTypedArgument; +using CustomAttributeNamedArgumentKind = System.Reflection.Metadata.CustomAttributeNamedArgumentKind; +using InteropTypes = Internal.TypeSystem.Interop.InteropTypes; + +namespace ILCompiler.Dataflow +{ + class ReflectionMethodBodyScanner : MethodBodyScanner + { + private readonly FlowAnnotations _flowAnnotations; + private readonly Logger _logger; + private readonly NodeFactory _factory; + private DependencyList _dependencies = new DependencyList(); + + public static bool RequiresReflectionMethodBodyScannerForCallSite(FlowAnnotations flowAnnotations, MethodDesc methodDefinition) + { + return + GetIntrinsicIdForMethod(methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || + flowAnnotations.RequiresDataflowAnalysis(methodDefinition) || + methodDefinition.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresUnreferencedCodeAttribute") || + methodDefinition.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresDynamicCodeAttribute") || + methodDefinition.IsPInvoke; + } + + public static bool RequiresReflectionMethodBodyScannerForMethodBody(FlowAnnotations flowAnnotations, MethodDesc methodDefinition) + { + return + GetIntrinsicIdForMethod(methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || + flowAnnotations.RequiresDataflowAnalysis(methodDefinition); + } + + public static bool RequiresReflectionMethodBodyScannerForAccess(FlowAnnotations flowAnnotations, FieldDesc fieldDefinition) + { + return flowAnnotations.RequiresDataflowAnalysis(fieldDefinition); + } + + private bool ShouldEnablePatternReporting(MethodDesc method, string attributeName) + { + if (method.HasCustomAttribute("System.Diagnostics.CodeAnalysis", attributeName)) + return false; + + MethodDesc userMethod = ILCompiler.Logging.CompilerGeneratedState.GetUserDefinedMethodForCompilerGeneratedMember(method); + if (userMethod != null && + userMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", attributeName)) + return false; + + return true; + } + + bool ShouldEnableReflectionPatternReporting(MethodDesc method) + { + return ShouldEnablePatternReporting(method, "RequiresUnreferencedCodeAttribute"); + } + + bool ShouldEnableAotPatternReporting(MethodDesc method) + { + return ShouldEnablePatternReporting(method, "RequiresDynamicCodeAttribute"); + } + + private enum ScanningPurpose + { + Default, + GetTypeDataflow, + } + + private ScanningPurpose _purpose; + + private ReflectionMethodBodyScanner(NodeFactory factory, FlowAnnotations flowAnnotations, Logger logger, ScanningPurpose purpose = ScanningPurpose.Default) + { + _flowAnnotations = flowAnnotations; + _logger = logger; + _factory = factory; + _purpose = purpose; + } + + public static DependencyList ScanAndProcessReturnValue(NodeFactory factory, FlowAnnotations flowAnnotations, Logger logger, MethodIL methodBody) + { + var scanner = new ReflectionMethodBodyScanner(factory, flowAnnotations, logger); + + Debug.Assert(methodBody.GetMethodILDefinition() == methodBody); + if (methodBody.OwningMethod.HasInstantiation || methodBody.OwningMethod.OwningType.HasInstantiation) + { + // We instantiate the body over the generic parameters. + // + // This will transform references like "call Foo.Method(!0 arg)" into + // "call Foo.Method(T arg)". We do this to avoid getting confused about what + // context the generic variables refer to - in the above example, we would see + // two !0's - one refers to the generic parameter of the type that owns the method with + // the call, but the other one (in the signature of "Method") actually refers to + // the generic parameter of Foo. + // + // If we don't do this translation, retrieving the signature of the called method + // would attempt to do bogus substitutions. + // + // By doing the following transformation, we ensure we don't see the generic variables + // that need to be bound to the context of the currently analyzed method. + methodBody = new InstantiatedMethodIL(methodBody.OwningMethod, methodBody); + } + + scanner.Scan(methodBody); + + if (!methodBody.OwningMethod.Signature.ReturnType.IsVoid) + { + var method = methodBody.OwningMethod; + var requiredMemberTypes = scanner._flowAnnotations.GetReturnParameterAnnotation(method); + if (requiredMemberTypes != 0) + { + var targetContext = new MethodReturnOrigin(method); + var reflectionContext = new ReflectionPatternContext(scanner._logger, scanner.ShouldEnableReflectionPatternReporting(method), method, targetContext); + reflectionContext.AnalyzingPattern(); + scanner.RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, scanner.MethodReturnValue, targetContext); + reflectionContext.Dispose(); + } + } + + return scanner._dependencies; + } + + public static DependencyList ProcessAttributeDataflow(NodeFactory factory, FlowAnnotations flowAnnotations, Logger logger, MethodDesc method, CustomAttributeValue arguments) + { + DependencyList result = null; + + // First do the dataflow for the constructor parameters if necessary. + if (flowAnnotations.RequiresDataflowAnalysis(method)) + { + for (int i = 0; i < method.Signature.Length; i++) + { + DynamicallyAccessedMemberTypes annotation = flowAnnotations.GetParameterAnnotation(method, i + 1); + if (annotation != DynamicallyAccessedMemberTypes.None) + { + ValueNode valueNode = GetValueNodeForCustomAttributeArgument(arguments.FixedArguments[i].Value); + if (valueNode != null) + { + var targetContext = new ParameterOrigin(method, i); + var reflectionContext = new ReflectionPatternContext(logger, true, method, targetContext); + try + { + reflectionContext.AnalyzingPattern(); + var scanner = new ReflectionMethodBodyScanner(factory, flowAnnotations, logger); + scanner.RequireDynamicallyAccessedMembers(ref reflectionContext, annotation, valueNode, targetContext); + result = scanner._dependencies; + } + finally + { + reflectionContext.Dispose(); + } + } + } + } + } + + // Named arguments next + TypeDesc attributeType = method.OwningType; + foreach (var namedArgument in arguments.NamedArguments) + { + TypeSystemEntity entity = null; + DynamicallyAccessedMemberTypes annotation = DynamicallyAccessedMemberTypes.None; + Origin targetContext = null; + if (namedArgument.Kind == CustomAttributeNamedArgumentKind.Field) + { + FieldDesc field = attributeType.GetField(namedArgument.Name); + if (field != null) + { + annotation = flowAnnotations.GetFieldAnnotation(field); + entity = field; + targetContext = new FieldOrigin(field); + } + } + else + { + Debug.Assert(namedArgument.Kind == CustomAttributeNamedArgumentKind.Property); + PropertyPseudoDesc property = ((MetadataType)attributeType).GetProperty(namedArgument.Name, null); + MethodDesc setter = property.SetMethod; + if (setter != null && setter.Signature.Length > 0 && !setter.Signature.IsStatic) + { + annotation = flowAnnotations.GetParameterAnnotation(setter, 1); + entity = property; + targetContext = new ParameterOrigin(setter, 1); + } + } + + if (annotation != DynamicallyAccessedMemberTypes.None) + { + ValueNode valueNode = GetValueNodeForCustomAttributeArgument(namedArgument.Value); + if (valueNode != null) + { + var reflectionContext = new ReflectionPatternContext(logger, true, method, targetContext); + try + { + reflectionContext.AnalyzingPattern(); + var scanner = new ReflectionMethodBodyScanner(factory, flowAnnotations, logger); + scanner.RequireDynamicallyAccessedMembers(ref reflectionContext, annotation, valueNode, targetContext); + if (result == null) + { + result = scanner._dependencies; + } + else + { + result.AddRange(scanner._dependencies); + } + } + finally + { + reflectionContext.Dispose(); + } + } + } + } + + return result; + } + + public static DependencyList ProcessTypeGetTypeDataflow(NodeFactory factory, FlowAnnotations flowAnnotations, Logger logger, MetadataType type) + { + DynamicallyAccessedMemberTypes annotation = flowAnnotations.GetTypeAnnotation(type); + Debug.Assert(annotation != DynamicallyAccessedMemberTypes.None); + var scanner = new ReflectionMethodBodyScanner(factory, flowAnnotations, logger, ScanningPurpose.GetTypeDataflow); + ReflectionPatternContext reflectionPatternContext = new ReflectionPatternContext(logger, reportingEnabled: true, type, new TypeOrigin(type)); + reflectionPatternContext.AnalyzingPattern(); + scanner.MarkTypeForDynamicallyAccessedMembers(ref reflectionPatternContext, type, annotation); + reflectionPatternContext.RecordHandledPattern(); + reflectionPatternContext.Dispose(); + return scanner._dependencies; + } + + static ValueNode GetValueNodeForCustomAttributeArgument(object argument) + { + ValueNode result = null; + if (argument is TypeDesc td) + { + result = new SystemTypeValue(td); + } + else if (argument is string str) + { + result = new KnownStringValue(str); + } + else + { + Debug.Assert(argument is null); + result = NullValue.Instance; + } + + return result; + } + + public static DependencyList ProcessGenericArgumentDataFlow(NodeFactory factory, FlowAnnotations flowAnnotations, Logger logger, GenericParameterDesc genericParameter, TypeDesc genericArgument, TypeSystemEntity source) + { + var scanner = new ReflectionMethodBodyScanner(factory, flowAnnotations, logger); + + var annotation = flowAnnotations.GetGenericParameterAnnotation(genericParameter); + Debug.Assert(annotation != DynamicallyAccessedMemberTypes.None); + + ValueNode valueNode = new SystemTypeValue(genericArgument); + + var origin = new GenericParameterOrigin(genericParameter); + var reflectionContext = new ReflectionPatternContext(logger, reportingEnabled: true, source, origin); + reflectionContext.AnalyzingPattern(); + scanner.RequireDynamicallyAccessedMembers(ref reflectionContext, annotation, valueNode, origin); + reflectionContext.Dispose(); + + return scanner._dependencies; + } + + protected override void WarnAboutInvalidILInMethod(MethodIL method, int ilOffset) + { + // Serves as a debug helper to make sure valid IL is not considered invalid. + // + // The .NET Native compiler used to warn if it detected invalid IL during treeshaking, + // but the warnings were often triggered in autogenerated dead code of a major game engine + // and resulted in support calls. No point in warning. If the code gets exercised at runtime, + // an InvalidProgramException will likely be raised. + Debug.Fail("Invalid IL or a bug in the scanner"); + } + + protected override ValueNode GetMethodParameterValue(MethodDesc method, int parameterIndex) + { + DynamicallyAccessedMemberTypes memberTypes = _flowAnnotations.GetParameterAnnotation(method, parameterIndex); + return new MethodParameterValue(method, parameterIndex, memberTypes); + } + + protected override ValueNode GetFieldValue(MethodIL method, FieldDesc field) + { + switch (field.Name) + { + case "EmptyTypes" when field.OwningType.IsTypeOf("System", "Type"): + { + return new ArrayValue(new ConstIntValue(0), field.OwningType); + } + case "Empty" when field.OwningType.IsTypeOf("System", "String"): + { + return new KnownStringValue(string.Empty); + } + + default: + { + DynamicallyAccessedMemberTypes memberTypes = _flowAnnotations.GetFieldAnnotation(field); + return new LoadFieldValue(field, memberTypes); + } + } + } + + protected override void HandleStoreField(MethodIL methodBody, int offset, FieldDesc field, ValueNode valueToStore) + { + var requiredMemberTypes = _flowAnnotations.GetFieldAnnotation(field); + if (requiredMemberTypes != 0) + { + var origin = new FieldOrigin(field); + var reflectionContext = new ReflectionPatternContext(_logger, ShouldEnableReflectionPatternReporting(methodBody.OwningMethod), methodBody, offset, origin); + reflectionContext.AnalyzingPattern(); + RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, valueToStore, origin); + reflectionContext.Dispose(); + } + } + + protected override void HandleStoreParameter(MethodIL method, int offset, int index, ValueNode valueToStore) + { + var requiredMemberTypes = _flowAnnotations.GetParameterAnnotation(method.OwningMethod, index); + if (requiredMemberTypes != 0) + { + Origin parameter = DiagnosticUtilities.GetMethodParameterFromIndex(method.OwningMethod, index); + var reflectionContext = new ReflectionPatternContext(_logger, ShouldEnableReflectionPatternReporting(method.OwningMethod), method, offset, parameter); + reflectionContext.AnalyzingPattern(); + RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, valueToStore, parameter); + reflectionContext.Dispose(); + } + } + + enum IntrinsicId + { + None = 0, + IntrospectionExtensions_GetTypeInfo, + Type_GetTypeFromHandle, + Type_get_TypeHandle, + Object_GetType, + TypeDelegator_Ctor, + Array_Empty, + TypeInfo_AsType, + MethodBase_GetMethodFromHandle, + + // Anything above this marker will require the method to be run through + // the reflection body scanner. + RequiresReflectionBodyScanner_Sentinel = 1000, + Type_MakeGenericType, + Type_GetType, + Type_GetConstructor, + Type_GetConstructors, + Type_GetMethod, + Type_GetMethods, + Type_GetField, + Type_GetFields, + Type_GetProperty, + Type_GetProperties, + Type_GetEvent, + Type_GetEvents, + Type_GetNestedType, + Type_GetNestedTypes, + Type_GetMember, + Type_GetMembers, + Type_GetInterface, + Type_get_AssemblyQualifiedName, + Type_get_UnderlyingSystemType, + Type_get_BaseType, + Expression_Call, + Expression_Field, + Expression_Property, + Expression_New, + Enum_GetValues, + Marshal_SizeOf, + Marshal_OffsetOf, + Marshal_PtrToStructure, + Marshal_DestroyStructure, + Marshal_GetDelegateForFunctionPointer, + Activator_CreateInstance_Type, + Activator_CreateInstance_AssemblyName_TypeName, + Activator_CreateInstanceFrom, + Activator_CreateInstanceOfT, + AppDomain_CreateInstance, + AppDomain_CreateInstanceAndUnwrap, + AppDomain_CreateInstanceFrom, + AppDomain_CreateInstanceFromAndUnwrap, + Assembly_CreateInstance, + RuntimeReflectionExtensions_GetRuntimeEvent, + RuntimeReflectionExtensions_GetRuntimeField, + RuntimeReflectionExtensions_GetRuntimeMethod, + RuntimeReflectionExtensions_GetRuntimeProperty, + RuntimeHelpers_RunClassConstructor, + MethodInfo_MakeGenericMethod, + } + + static IntrinsicId GetIntrinsicIdForMethod(MethodDesc calledMethod) + { + return calledMethod.Name switch + { + // static System.Reflection.IntrospectionExtensions.GetTypeInfo (Type type) + "GetTypeInfo" when calledMethod.IsDeclaredOnType("System.Reflection", "IntrospectionExtensions") => IntrinsicId.IntrospectionExtensions_GetTypeInfo, + + // System.Reflection.TypeInfo.AsType () + "AsType" when calledMethod.IsDeclaredOnType("System.Reflection", "TypeInfo") => IntrinsicId.TypeInfo_AsType, + + // System.Type.GetTypeInfo (Type type) + "GetTypeFromHandle" when calledMethod.IsDeclaredOnType("System", "Type") => IntrinsicId.Type_GetTypeFromHandle, + + // System.Type.GetTypeHandle (Type type) + "get_TypeHandle" when calledMethod.IsDeclaredOnType("System", "Type") => IntrinsicId.Type_get_TypeHandle, + + // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle) + // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) + "GetMethodFromHandle" when calledMethod.IsDeclaredOnType("System.Reflection", "MethodBase") + && calledMethod.HasParameterOfType(0, "System", "RuntimeMethodHandle") + && (calledMethod.Signature.Length == 1 || calledMethod.Signature.Length == 2) + => IntrinsicId.MethodBase_GetMethodFromHandle, + + // static System.Type.MakeGenericType (Type [] typeArguments) + "MakeGenericType" when calledMethod.IsDeclaredOnType("System", "Type") => IntrinsicId.Type_MakeGenericType, + + // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeEvent (this Type type, string name) + "GetRuntimeEvent" when calledMethod.IsDeclaredOnType("System.Reflection", "RuntimeReflectionExtensions") + && calledMethod.HasParameterOfType(0, "System", "Type") + && calledMethod.HasParameterOfType(1, "System", "String") + => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent, + + // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeField (this Type type, string name) + "GetRuntimeField" when calledMethod.IsDeclaredOnType("System.Reflection", "RuntimeReflectionExtensions") + && calledMethod.HasParameterOfType(0, "System", "Type") + && calledMethod.HasParameterOfType(1, "System", "String") + => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField, + + // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeMethod (this Type type, string name, Type[] parameters) + "GetRuntimeMethod" when calledMethod.IsDeclaredOnType("System.Reflection", "RuntimeReflectionExtensions") + && calledMethod.HasParameterOfType(0, "System", "Type") + && calledMethod.HasParameterOfType(1, "System", "String") + => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod, + + // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeProperty (this Type type, string name) + "GetRuntimeProperty" when calledMethod.IsDeclaredOnType("System.Reflection", "RuntimeReflectionExtensions") + && calledMethod.HasParameterOfType(0, "System", "Type") + && calledMethod.HasParameterOfType(1, "System", "String") + => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty, + + // static System.Linq.Expressions.Expression.Call (Type, String, Type[], Expression[]) + "Call" when calledMethod.IsDeclaredOnType("System.Linq.Expressions", "Expression") + && calledMethod.HasParameterOfType(0, "System", "Type") + && calledMethod.Signature.Length == 4 + => IntrinsicId.Expression_Call, + + // static System.Linq.Expressions.Expression.Field (Expression, Type, String) + "Field" when calledMethod.IsDeclaredOnType("System.Linq.Expressions", "Expression") + && calledMethod.HasParameterOfType(1, "System", "Type") + && calledMethod.Signature.Length == 3 + => IntrinsicId.Expression_Field, + + // static System.Linq.Expressions.Expression.Property (Expression, Type, String) + // static System.Linq.Expressions.Expression.Property (Expression, MethodInfo) + "Property" when calledMethod.IsDeclaredOnType("System.Linq.Expressions", "Expression") + && ((calledMethod.HasParameterOfType(1, "System", "Type") && calledMethod.Signature.Length == 3) + || (calledMethod.HasParameterOfType(1, "System.Reflection", "MethodInfo") && calledMethod.Signature.Length == 2)) + => IntrinsicId.Expression_Property, + + // static System.Linq.Expressions.Expression.New (Type) + "New" when calledMethod.IsDeclaredOnType("System.Linq.Expressions", "Expression") + && calledMethod.HasParameterOfType(0, "System", "Type") + && calledMethod.Signature.Length == 1 + => IntrinsicId.Expression_New, + + // static Array System.Enum.GetValues (Type) + "GetValues" when calledMethod.IsDeclaredOnType("System", "Enum") + && calledMethod.HasParameterOfType(0, "System", "Type") + && calledMethod.Signature.Length == 1 + => IntrinsicId.Enum_GetValues, + + // static int System.Runtime.InteropServices.Marshal.SizeOf (Type) + "SizeOf" when calledMethod.IsDeclaredOnType("System.Runtime.InteropServices", "Marshal") + && calledMethod.HasParameterOfType(0, "System", "Type") + && calledMethod.Signature.Length == 1 + => IntrinsicId.Marshal_SizeOf, + + // static int System.Runtime.InteropServices.Marshal.OffsetOf (Type, string) + "OffsetOf" when calledMethod.IsDeclaredOnType("System.Runtime.InteropServices", "Marshal") + && calledMethod.HasParameterOfType(0, "System", "Type") + && calledMethod.Signature.Length == 2 + => IntrinsicId.Marshal_OffsetOf, + + // static object System.Runtime.InteropServices.Marshal.PtrToStructure (IntPtr, Type) + "PtrToStructure" when calledMethod.IsDeclaredOnType("System.Runtime.InteropServices", "Marshal") + && calledMethod.HasParameterOfType(1, "System", "Type") + && calledMethod.Signature.Length == 2 + => IntrinsicId.Marshal_PtrToStructure, + + // static void System.Runtime.InteropServices.Marshal.DestroyStructure (IntPtr, Type) + "DestroyStructure" when calledMethod.IsDeclaredOnType("System.Runtime.InteropServices", "Marshal") + && calledMethod.HasParameterOfType(1, "System", "Type") + && calledMethod.Signature.Length == 2 + => IntrinsicId.Marshal_DestroyStructure, + + // static Delegate System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer (IntPtr, Type) + "GetDelegateForFunctionPointer" when calledMethod.IsDeclaredOnType("System.Runtime.InteropServices", "Marshal") + && calledMethod.HasParameterOfType(1, "System", "Type") + && calledMethod.Signature.Length == 2 + => IntrinsicId.Marshal_GetDelegateForFunctionPointer, + + // static System.Type.GetType (string) + // static System.Type.GetType (string, Boolean) + // static System.Type.GetType (string, Boolean, Boolean) + // static System.Type.GetType (string, Func, Func) + // static System.Type.GetType (string, Func, Func, Boolean) + // static System.Type.GetType (string, Func, Func, Boolean, Boolean) + "GetType" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System", "String") + => IntrinsicId.Type_GetType, + + // System.Type.GetConstructor (Type[]) + // System.Type.GetConstructor (BindingFlags, Type[]) + // System.Type.GetConstructor (BindingFlags, Binder, Type[], ParameterModifier []) + // System.Type.GetConstructor (BindingFlags, Binder, CallingConventions, Type[], ParameterModifier []) + "GetConstructor" when calledMethod.IsDeclaredOnType("System", "Type") + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetConstructor, + + // System.Type.GetConstructors (BindingFlags) + "GetConstructors" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") + && calledMethod.Signature.Length == 1 + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetConstructors, + + // System.Type.GetMethod (string) + // System.Type.GetMethod (string, BindingFlags) + // System.Type.GetMethod (string, Type[]) + // System.Type.GetMethod (string, Type[], ParameterModifier[]) + // System.Type.GetMethod (string, BindingFlags, Type[]) + // System.Type.GetMethod (string, BindingFlags, Binder, Type[], ParameterModifier[]) + // System.Type.GetMethod (string, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[]) + // System.Type.GetMethod (string, int, Type[]) + // System.Type.GetMethod (string, int, Type[], ParameterModifier[]?) + // System.Type.GetMethod (string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?) + // System.Type.GetMethod (string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?) + "GetMethod" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System", "String") + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetMethod, + + // System.Type.GetMethods (BindingFlags) + "GetMethods" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") + && calledMethod.Signature.Length == 1 + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetMethods, + + // System.Type.GetField (string) + // System.Type.GetField (string, BindingFlags) + "GetField" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System", "String") + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetField, + + // System.Type.GetFields (BindingFlags) + "GetFields" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") + && calledMethod.Signature.Length == 1 + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetFields, + + // System.Type.GetEvent (string) + // System.Type.GetEvent (string, BindingFlags) + "GetEvent" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System", "String") + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetEvent, + + // System.Type.GetEvents (BindingFlags) + "GetEvents" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") + && calledMethod.Signature.Length == 1 + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetEvents, + + // System.Type.GetNestedType (string) + // System.Type.GetNestedType (string, BindingFlags) + "GetNestedType" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System", "String") + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetNestedType, + + // System.Type.GetNestedTypes (BindingFlags) + "GetNestedTypes" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") + && calledMethod.Signature.Length == 1 + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetNestedTypes, + + // System.Type.GetMember (String) + // System.Type.GetMember (String, BindingFlags) + // System.Type.GetMember (String, MemberTypes, BindingFlags) + "GetMember" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System", "String") + && !calledMethod.Signature.IsStatic + && (calledMethod.Signature.Length == 1 || + (calledMethod.Signature.Length == 2 && calledMethod.HasParameterOfType(1, "System.Reflection", "BindingFlags")) || + (calledMethod.Signature.Length == 3 && calledMethod.HasParameterOfType(2, "System.Reflection", "BindingFlags"))) + => IntrinsicId.Type_GetMember, + + // System.Type.GetMembers (BindingFlags) + "GetMembers" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") + && calledMethod.Signature.Length == 1 + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetMembers, + + // System.Type.GetInterface (string) + // System.Type.GetInterface (string, bool) + "GetInterface" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System", "String") + && !calledMethod.Signature.IsStatic + && (calledMethod.Signature.Length == 1 || + (calledMethod.Signature.Length == 2 && calledMethod.Signature[1].IsWellKnownType(WellKnownType.Boolean))) + => IntrinsicId.Type_GetInterface, + + // System.Type.AssemblyQualifiedName + "get_AssemblyQualifiedName" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.Signature.Length == 0 + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_get_AssemblyQualifiedName, + + // System.Type.UnderlyingSystemType + "get_UnderlyingSystemType" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.Signature.Length == 0 + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_get_UnderlyingSystemType, + + // System.Type.BaseType + "get_BaseType" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.Signature.Length == 0 + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_get_BaseType, + + // System.Type.GetProperty (string) + // System.Type.GetProperty (string, BindingFlags) + // System.Type.GetProperty (string, Type) + // System.Type.GetProperty (string, Type[]) + // System.Type.GetProperty (string, Type, Type[]) + // System.Type.GetProperty (string, Type, Type[], ParameterModifier[]) + // System.Type.GetProperty (string, BindingFlags, Binder, Type, Type[], ParameterModifier[]) + "GetProperty" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System", "String") + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetProperty, + + // System.Type.GetProperties (BindingFlags) + "GetProperties" when calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") + && calledMethod.Signature.Length == 1 + && !calledMethod.Signature.IsStatic + => IntrinsicId.Type_GetProperties, + + // static System.Object.GetType () + "GetType" when calledMethod.IsDeclaredOnType("System", "Object") + => IntrinsicId.Object_GetType, + + ".ctor" when calledMethod.IsDeclaredOnType("System.Reflection", "TypeDelegator") + && calledMethod.HasParameterOfType(0, "System", "Type") + => IntrinsicId.TypeDelegator_Ctor, + + "Empty" when calledMethod.IsDeclaredOnType("System", "Array") + => IntrinsicId.Array_Empty, + + // static System.Activator.CreateInstance (System.Type type) + // static System.Activator.CreateInstance (System.Type type, bool nonPublic) + // static System.Activator.CreateInstance (System.Type type, params object?[]? args) + // static System.Activator.CreateInstance (System.Type type, object?[]? args, object?[]? activationAttributes) + // static System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture) + // static System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } + "CreateInstance" when calledMethod.IsDeclaredOnType("System", "Activator") + && !calledMethod.HasInstantiation + && calledMethod.HasParameterOfType(0, "System", "Type") + => IntrinsicId.Activator_CreateInstance_Type, + + // static System.Activator.CreateInstance (string assemblyName, string typeName) + // static System.Activator.CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) + // static System.Activator.CreateInstance (string assemblyName, string typeName, object?[]? activationAttributes) + "CreateInstance" when calledMethod.IsDeclaredOnType("System", "Activator") + && !calledMethod.HasInstantiation + && calledMethod.HasParameterOfType(0, "System", "String") + && calledMethod.HasParameterOfType(1, "System", "String") + => IntrinsicId.Activator_CreateInstance_AssemblyName_TypeName, + + // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName) + // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) + "CreateInstanceFrom" when calledMethod.IsDeclaredOnType("System", "Activator") + && !calledMethod.HasInstantiation + && calledMethod.HasParameterOfType(0, "System", "String") + && calledMethod.HasParameterOfType(1, "System", "String") + => IntrinsicId.Activator_CreateInstanceFrom, + + // static T System.Activator.CreateInstance () + "CreateInstance" when calledMethod.IsDeclaredOnType("System", "Activator") + && calledMethod.HasInstantiation + && calledMethod.Instantiation.Length == 1 + && calledMethod.Signature.Length == 0 + => IntrinsicId.Activator_CreateInstanceOfT, + + // System.AppDomain.CreateInstance (string assemblyName, string typeName) + // System.AppDomain.CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // System.AppDomain.CreateInstance (string assemblyName, string typeName, object? []? activationAttributes) + "CreateInstance" when calledMethod.IsDeclaredOnType("System", "AppDomain") + && calledMethod.HasParameterOfType(0, "System", "String") + && calledMethod.HasParameterOfType(1, "System", "String") + => IntrinsicId.AppDomain_CreateInstance, + + // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName) + // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName, object? []? activationAttributes) + "CreateInstanceAndUnwrap" when calledMethod.IsDeclaredOnType("System", "AppDomain") + && calledMethod.HasParameterOfType(0, "System", "String") + && calledMethod.HasParameterOfType(1, "System", "String") + => IntrinsicId.AppDomain_CreateInstanceAndUnwrap, + + // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName) + // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) + "CreateInstanceFrom" when calledMethod.IsDeclaredOnType("System", "AppDomain") + && calledMethod.HasParameterOfType(0, "System", "String") + && calledMethod.HasParameterOfType(1, "System", "String") + => IntrinsicId.AppDomain_CreateInstanceFrom, + + // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName) + // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, object? []? activationAttributes) + "CreateInstanceFromAndUnwrap" when calledMethod.IsDeclaredOnType("System", "AppDomain") + && calledMethod.HasParameterOfType(0, "System", "String") + && calledMethod.HasParameterOfType(1, "System", "String") + => IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap, + + // System.Reflection.Assembly.CreateInstance (string typeName) + // System.Reflection.Assembly.CreateInstance (string typeName, bool ignoreCase) + // System.Reflection.Assembly.CreateInstance (string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder? binder, object []? args, CultureInfo? culture, object []? activationAttributes) + "CreateInstance" when calledMethod.IsDeclaredOnType("System.Reflection", "Assembly") + && calledMethod.HasParameterOfType(0, "System", "String") + => IntrinsicId.Assembly_CreateInstance, + + // System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) + "RunClassConstructor" when calledMethod.IsDeclaredOnType("System.Runtime.CompilerServices", "RuntimeHelpers") + && calledMethod.HasParameterOfType(0, "System", "RuntimeTypeHandle") + => IntrinsicId.RuntimeHelpers_RunClassConstructor, + + // System.Reflection.MethodInfo.MakeGenericMethod (Type[] typeArguments) + "MakeGenericMethod" when calledMethod.IsDeclaredOnType("System.Reflection", "MethodInfo") + && !calledMethod.Signature.IsStatic + && calledMethod.Signature.Length == 1 + => IntrinsicId.MethodInfo_MakeGenericMethod, + + _ => IntrinsicId.None, + }; + } + + public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMethod, ILOpcode operation, int offset, ValueNodeList methodParams, out ValueNode methodReturnValue) + { + methodReturnValue = null; + + var callingMethodDefinition = callingMethodBody.OwningMethod; + bool shouldEnableReflectionWarnings = ShouldEnableReflectionPatternReporting(callingMethodDefinition); + bool shouldEnableAotWarnings = ShouldEnableAotPatternReporting(callingMethodDefinition); + var reflectionContext = new ReflectionPatternContext(_logger, shouldEnableReflectionWarnings, callingMethodBody, offset, new MethodOrigin(calledMethod)); + + DynamicallyAccessedMemberTypes returnValueDynamicallyAccessedMemberTypes = 0; + + try + { + + bool requiresDataFlowAnalysis = _flowAnnotations.RequiresDataflowAnalysis(calledMethod); + returnValueDynamicallyAccessedMemberTypes = requiresDataFlowAnalysis ? + _flowAnnotations.GetReturnParameterAnnotation(calledMethod) : 0; + + var intrinsicId = GetIntrinsicIdForMethod(calledMethod); + switch (intrinsicId) + { + case IntrinsicId.IntrospectionExtensions_GetTypeInfo: + { + // typeof(Foo).GetTypeInfo()... will be commonly present in code targeting + // the dead-end reflection refactoring. The call doesn't do anything and we + // don't want to lose the annotation. + methodReturnValue = methodParams[0]; + } + break; + + case IntrinsicId.TypeInfo_AsType: + { + // someType.AsType()... will be commonly present in code targeting + // the dead-end reflection refactoring. The call doesn't do anything and we + // don't want to lose the annotation. + methodReturnValue = methodParams[0]; + } + break; + + case IntrinsicId.TypeDelegator_Ctor: + { + // This is an identity function for analysis purposes + if (operation == ILOpcode.newobj) + methodReturnValue = methodParams[1]; + } + break; + + case IntrinsicId.Array_Empty: + { + methodReturnValue = new ArrayValue(new ConstIntValue(0), calledMethod.Instantiation[0]); + } + break; + + case IntrinsicId.Type_GetTypeFromHandle: + { + // Infrastructure piece to support "typeof(Foo)" + if (methodParams[0] is RuntimeTypeHandleValue typeHandle) + methodReturnValue = new SystemTypeValue(typeHandle.TypeRepresented); + else if (methodParams[0] is RuntimeTypeHandleForGenericParameterValue typeHandleForGenericParameter) + { + methodReturnValue = new SystemTypeForGenericParameterValue( + typeHandleForGenericParameter.GenericParameter, + _flowAnnotations.GetGenericParameterAnnotation(typeHandleForGenericParameter.GenericParameter)); + } + } + break; + + case IntrinsicId.Type_get_TypeHandle: + { + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is SystemTypeValue typeValue) + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new RuntimeTypeHandleValue(typeValue.TypeRepresented)); + else if (value == NullValue.Instance) + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, value); + else + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, UnknownValue.Instance); + } + } + break; + + // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle) + // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) + case IntrinsicId.MethodBase_GetMethodFromHandle: + { + // Infrastructure piece to support "ldtoken method -> GetMethodFromHandle" + if (methodParams[0] is RuntimeMethodHandleValue methodHandle) + methodReturnValue = new SystemReflectionMethodBaseValue(methodHandle.MethodRepresented); + } + break; + + // + // System.Type + // + // Type MakeGenericType (params Type[] typeArguments) + // + case IntrinsicId.Type_MakeGenericType: + { + reflectionContext.AnalyzingPattern(); + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is SystemTypeValue typeValue) + { + if (AnalyzeGenericInstantiationTypeArray(methodParams[1], ref reflectionContext, calledMethod, typeValue.TypeRepresented.GetTypeDefinition().Instantiation)) + { + reflectionContext.RecordHandledPattern(); + } + else + { + bool hasUncheckedAnnotation = false; + foreach (GenericParameterDesc genericParameter in typeValue.TypeRepresented.GetTypeDefinition().Instantiation) + { + if (_flowAnnotations.GetGenericParameterAnnotation(genericParameter) != DynamicallyAccessedMemberTypes.None || + (genericParameter.HasDefaultConstructorConstraint && !typeValue.TypeRepresented.IsNullable)) + { + // If we failed to analyze the array, we go through the analyses again + // and intentionally ignore one particular annotation: + // Special case: Nullable where T : struct + // The struct constraint in C# implies new() constraints, but Nullable doesn't make a use of that part. + // There are several places even in the framework where typeof(Nullable<>).MakeGenericType would warn + // without any good reason to do so. + hasUncheckedAnnotation = true; + break; + } + } + if (hasUncheckedAnnotation) + { + reflectionContext.RecordUnrecognizedPattern( + 2055, + $"Call to '{calledMethod.GetDisplayName()}' can not be statically analyzed. " + + $"It's not possible to guarantee the availability of requirements of the generic type."); + } + } + + // We haven't found any generic parameters with annotations, so there's nothing to validate. + reflectionContext.RecordHandledPattern(); + } + else if (value == NullValue.Instance) + reflectionContext.RecordHandledPattern(); + else + { + // We have no way to "include more" to fix this if we don't know, so we have to warn + reflectionContext.RecordUnrecognizedPattern( + 2055, + $"Call to '{calledMethod.GetDisplayName()}' can not be statically analyzed. " + + $"It's not possible to guarantee the availability of requirements of the generic type."); + } + } + + if (shouldEnableAotWarnings) + LogDynamicCodeWarning(_logger, callingMethodBody, offset, calledMethod); + + // We don't want to lose track of the type + // in case this is e.g. Activator.CreateInstance(typeof(Foo<>).MakeGenericType(...)); + methodReturnValue = methodParams[0]; + } + break; + + // + // System.Reflection.RuntimeReflectionExtensions + // + // static GetRuntimeEvent (this Type type, string name) + // static GetRuntimeField (this Type type, string name) + // static GetRuntimeMethod (this Type type, string name, Type[] parameters) + // static GetRuntimeProperty (this Type type, string name) + // + case var getRuntimeMember when getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: + { + + reflectionContext.AnalyzingPattern(); + BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + DynamicallyAccessedMemberTypes requiredMemberTypes = getRuntimeMember switch + { + IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent => DynamicallyAccessedMemberTypes.PublicEvents, + IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField => DynamicallyAccessedMemberTypes.PublicFields, + IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod => DynamicallyAccessedMemberTypes.PublicMethods, + IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty => DynamicallyAccessedMemberTypes.PublicProperties, + _ => throw new Exception($"Reflection call '{calledMethod.GetDisplayName()}' inside '{callingMethodDefinition.GetDisplayName()}' is of unexpected member type."), + }; + + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is SystemTypeValue systemTypeValue) + { + foreach (var stringParam in methodParams[1].UniqueValues()) + { + if (stringParam is KnownStringValue stringValue) + { + switch (getRuntimeMember) + { + case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent: + MarkEventsOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, e => e.Name == stringValue.Contents, bindingFlags); + reflectionContext.RecordHandledPattern(); + break; + case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField: + MarkFieldsOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, f => f.Name == stringValue.Contents, bindingFlags); + reflectionContext.RecordHandledPattern(); + break; + case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod: + ProcessGetMethodByName(ref reflectionContext, systemTypeValue.TypeRepresented, stringValue.Contents, bindingFlags, ref methodReturnValue); + reflectionContext.RecordHandledPattern(); + break; + case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: + MarkPropertiesOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, p => p.Name == stringValue.Contents, bindingFlags); + reflectionContext.RecordHandledPattern(); + break; + default: + throw new Exception($"Error processing reflection call '{calledMethod.GetDisplayName()}' inside {callingMethodDefinition.GetDisplayName()}. Unexpected member kind."); + } + } + else + { + RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new ParameterOrigin(calledMethod, 0)); + } + } + } + else + { + RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new ParameterOrigin(calledMethod, 0)); + } + } + } + break; + + // + // System.Linq.Expressions.Expression + // + // static Call (Type, String, Type[], Expression[]) + // + case IntrinsicId.Expression_Call: + { + reflectionContext.AnalyzingPattern(); + BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; + + bool hasTypeArguments = (methodParams[2] as ArrayValue)?.Size.AsConstInt() != 0; + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is SystemTypeValue systemTypeValue) + { + foreach (var stringParam in methodParams[1].UniqueValues()) + { + if (stringParam is KnownStringValue stringValue) + { + foreach (var method in systemTypeValue.TypeRepresented.GetMethodsOnTypeHierarchy(m => m.Name == stringValue.Contents, bindingFlags)) + { + ValidateGenericMethodInstantiation(ref reflectionContext, method, methodParams[2], calledMethod); + MarkMethod(ref reflectionContext, method); + } + + reflectionContext.RecordHandledPattern(); + } + else + { + if (hasTypeArguments) + { + // We don't know what method the `MakeGenericMethod` was called on, so we have to assume + // that the method may have requirements which we can't fullfil -> warn. + reflectionContext.RecordUnrecognizedPattern( + 2060, string.Format(Resources.Strings.IL2060, + DiagnosticUtilities.GetMethodSignatureDisplayName(calledMethod))); + } + + RequireDynamicallyAccessedMembers( + ref reflectionContext, + GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags), + value, + new ParameterOrigin(calledMethod, 0)); + } + } + } + else + { + if (hasTypeArguments) + { + // We don't know what method the `MakeGenericMethod` was called on, so we have to assume + // that the method may have requirements which we can't fullfil -> warn. + reflectionContext.RecordUnrecognizedPattern( + 2060, string.Format(Resources.Strings.IL2060, + DiagnosticUtilities.GetMethodSignatureDisplayName(calledMethod))); + } + + RequireDynamicallyAccessedMembers( + ref reflectionContext, + GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags), + value, + new ParameterOrigin(calledMethod, 0)); + } + } + } + break; + + // + // System.Linq.Expressions.Expression + // + // static Property (Expression, MethodInfo) + // + case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType(1, "System.Reflection", "MethodInfo"): + { + reflectionContext.AnalyzingPattern(); + foreach (var value in methodParams[1].UniqueValues()) + { + if (value is SystemReflectionMethodBaseValue methodBaseValue) + { + // We have one of the accessors for the property. The Expression.Property will in this case search + // for the matching PropertyInfo and store that. So to be perfectly correct we need to mark the + // respective PropertyInfo as "accessed via reflection". + var propertyDefinition = methodBaseValue.MethodRepresented.GetPropertyForAccessor(); + if (propertyDefinition is not null) + { + MarkProperty(ref reflectionContext, propertyDefinition); + continue; + } + } + else if (value == NullValue.Instance) + { + reflectionContext.RecordHandledPattern(); + continue; + } + // In all other cases we may not even know which type this is about, so there's nothing we can do + // report it as a warning. + reflectionContext.RecordUnrecognizedPattern( + 2103, string.Format(Resources.Strings.IL2103, + DiagnosticUtilities.GetParameterNameForErrorMessage(new ParameterOrigin(calledMethod, 1)), + DiagnosticUtilities.GetMethodSignatureDisplayName(calledMethod))); + } + } + break; + + // + // System.Linq.Expressions.Expression + // + // static Field (Expression, Type, String) + // static Property (Expression, Type, String) + // + case var fieldOrPropertyInstrinsic when fieldOrPropertyInstrinsic == IntrinsicId.Expression_Field || fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property: + { + reflectionContext.AnalyzingPattern(); + DynamicallyAccessedMemberTypes memberTypes = fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property + ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties + : DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields; + + foreach (var value in methodParams[1].UniqueValues()) + { + if (value is SystemTypeValue systemTypeValue) + { + foreach (var stringParam in methodParams[2].UniqueValues()) + { + if (stringParam is KnownStringValue stringValue) + { + BindingFlags bindingFlags = methodParams[0]?.Kind == ValueNodeKind.Null ? BindingFlags.Static : BindingFlags.Default; + if (fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property) + { + MarkPropertiesOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, filter: p => p.Name == stringValue.Contents, bindingFlags); + } + else + { + MarkFieldsOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, filter: f => f.Name == stringValue.Contents, bindingFlags); + } + + reflectionContext.RecordHandledPattern(); + } + else + { + RequireDynamicallyAccessedMembers(ref reflectionContext, memberTypes, value, new ParameterOrigin(calledMethod, 2)); + } + } + } + else + { + RequireDynamicallyAccessedMembers(ref reflectionContext, memberTypes, value, new ParameterOrigin(calledMethod, 1)); + } + } + } + break; + + // + // System.Linq.Expressions.Expression + // + // static New (Type) + // + case IntrinsicId.Expression_New: + { + reflectionContext.AnalyzingPattern(); + + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is SystemTypeValue systemTypeValue) + { + MarkConstructorsOnType(ref reflectionContext, systemTypeValue.TypeRepresented, null, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + reflectionContext.RecordHandledPattern(); + } + else + { + RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, value, new ParameterOrigin(calledMethod, 0)); + } + } + } + break; + + // + // System.Enum + // + // static GetValues (Type) + // + case IntrinsicId.Enum_GetValues: + { + // Enum.GetValues returns System.Array, but it's the array of the enum type under the hood + // and people depend on this undocumented detail (could have returned enum of the underlying + // type instead). + // + // At least until we have shared enum code, this needs extra handling to get it right. + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is SystemTypeValue systemTypeValue + && !systemTypeValue.TypeRepresented.IsGenericDefinition + && !systemTypeValue.TypeRepresented.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) + { + if (systemTypeValue.TypeRepresented.IsEnum) + { + _dependencies.Add(_factory.ConstructedTypeSymbol(systemTypeValue.TypeRepresented.MakeArrayType()), "Enum.GetValues"); + } + } + else if (shouldEnableAotWarnings) + { + LogDynamicCodeWarning(_logger, callingMethodBody, offset, calledMethod); + } + } + } + break; + + // + // System.Runtime.InteropServices.Marshal + // + // static SizeOf (Type) + // static PtrToStructure (IntPtr, Type) + // static DestroyStructure (IntPtr, Type) + // static OffsetOf (Type, string) + // + case IntrinsicId.Marshal_SizeOf: + case IntrinsicId.Marshal_PtrToStructure: + case IntrinsicId.Marshal_DestroyStructure: + case IntrinsicId.Marshal_OffsetOf: + { + int paramIndex = intrinsicId == IntrinsicId.Marshal_SizeOf + || intrinsicId == IntrinsicId.Marshal_OffsetOf + ? 0 : 1; + + // We need the data to do struct marshalling. + foreach (var value in methodParams[paramIndex].UniqueValues()) + { + if (value is SystemTypeValue systemTypeValue + && !systemTypeValue.TypeRepresented.IsGenericDefinition + && !systemTypeValue.TypeRepresented.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) + { + if (systemTypeValue.TypeRepresented.IsDefType) + { + _dependencies.Add(_factory.StructMarshallingData((DefType)systemTypeValue.TypeRepresented), "Marshal API"); + } + } + else if (shouldEnableAotWarnings) + { + LogDynamicCodeWarning(_logger, callingMethodBody, offset, calledMethod); + } + } + } + break; + + // + // System.Runtime.InteropServices.Marshal + // + // static GetDelegateForFunctionPointer (IntPtr, Type) + // + case IntrinsicId.Marshal_GetDelegateForFunctionPointer: + { + // We need the data to do delegate marshalling. + foreach (var value in methodParams[1].UniqueValues()) + { + if (value is SystemTypeValue systemTypeValue + && !systemTypeValue.TypeRepresented.IsGenericDefinition + && !systemTypeValue.TypeRepresented.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) + { + if (systemTypeValue.TypeRepresented.IsDefType) + { + _dependencies.Add(_factory.DelegateMarshallingData((DefType)systemTypeValue.TypeRepresented), "Marshal API"); + } + } + else if (shouldEnableAotWarnings) + { + LogDynamicCodeWarning(_logger, callingMethodBody, offset, calledMethod); + } + } + } + break; + + // + // System.Object + // + // GetType() + // + case IntrinsicId.Object_GetType: + { + foreach (var valueNode in methodParams[0].UniqueValues()) + { + // Note that valueNode can be statically typed in IL as some generic argument type. + // For example: + // void Method(T instance) { instance.GetType().... } + // Currently this case will end up with null StaticType - since there's no typedef for the generic argument type. + // But it could be that T is annotated with for example PublicMethods: + // void Method<[DAM(PublicMethods)] T>(T instance) { instance.GetType().GetMethod("Test"); } + // In this case it's in theory possible to handle it, by treating the T basically as a base class + // for the actual type of "instance". But the analysis for this would be pretty complicated (as the marking + // has to happen on the callsite, which doesn't know that GetType() will be used...). + // For now we're intentionally ignoring this case - it will produce a warning. + // The counter example is: + // Method(new Derived); + // In this case to get correct results, trimmer would have to mark all public methods on Derived. Which + // currently it won't do. + + TypeDesc staticType = valueNode.StaticType; + if (staticType is null || (!staticType.IsDefType && !staticType.IsArray)) + { + // We don't know anything about the type GetType was called on. Track this as a usual "result of a method call without any annotations" + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, DynamicallyAccessedMemberTypes.None)); + } + else if (staticType.IsSealed() || staticType.IsTypeOf("System", "Delegate")) + { + // We can treat this one the same as if it was a typeof() expression + + // We can allow Object.GetType to be modeled as System.Delegate because we keep all methods + // on delegates anyway so reflection on something this approximation would miss is actually safe. + + // We ignore the fact that the type can be annotated (see below for handling of annotated types) + // This means the annotations (if any) won't be applied - instead we rely on the exact knowledge + // of the type. So for example even if the type is annotated with PublicMethods + // but the code calls GetProperties on it - it will work - mark properties, don't mark methods + // since we ignored the fact that it's annotated. + // This can be seen a little bit as a violation of the annotation, but we already have similar cases + // where a parameter is annotated and if something in the method sets a specific known type to it + // we will also make it just work, even if the annotation doesn't match the usage. + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new SystemTypeValue(staticType)); + } + else + { + reflectionContext.AnalyzingPattern(); + + Debug.Assert(staticType is MetadataType || staticType.IsArray); + MetadataType closestMetadataType = staticType is MetadataType mdType ? + mdType : (MetadataType)_factory.TypeSystemContext.GetWellKnownType(WellKnownType.Array); + + var annotation = _flowAnnotations.GetTypeAnnotation(staticType); + + if (annotation != default) + { + _dependencies.Add(_factory.ObjectGetTypeFlowDependencies(closestMetadataType), "GetType called on this type"); + } + + reflectionContext.RecordHandledPattern(); + + // Return a value which is "unknown type" with annotation. For now we'll use the return value node + // for the method, which means we're loosing the information about which staticType this + // started with. For now we don't need it, but we can add it later on. + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, annotation)); + } + } + } + break; + + // + // System.Type + // + // GetType (string) + // GetType (string, Boolean) + // GetType (string, Boolean, Boolean) + // GetType (string, Func, Func) + // GetType (string, Func, Func, Boolean) + // GetType (string, Func, Func, Boolean, Boolean) + // + case IntrinsicId.Type_GetType: + { + reflectionContext.AnalyzingPattern(); + + var parameters = calledMethod.Signature; + if ((parameters.Length == 3 && parameters[2].IsWellKnownType(WellKnownType.Boolean) && methodParams[2].AsConstInt() != 0) || + (parameters.Length == 5 && methodParams[4].AsConstInt() != 0)) + { + reflectionContext.RecordUnrecognizedPattern(2096, $"Call to '{calledMethod.GetDisplayName()}' can perform case insensitive lookup of the type, currently ILLink can not guarantee presence of all the matching types"); + break; + } + foreach (var typeNameValue in methodParams[0].UniqueValues()) + { + if (typeNameValue is KnownStringValue knownStringValue) + { + bool found = ILCompiler.DependencyAnalysis.ReflectionMethodBodyScanner.ResolveType(knownStringValue.Contents, ((MetadataType)callingMethodDefinition.OwningType).Module, + callingMethodDefinition.Context, + out TypeDesc foundType, out ModuleDesc referenceModule); + if (!found) + { + // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. + reflectionContext.RecordHandledPattern(); + } + else + { + // Also add module metadata in case this reference was through a type forward + if (_factory.MetadataManager.CanGenerateMetadata(referenceModule.GetGlobalModuleType())) + _dependencies.Add(_factory.ModuleMetadata(referenceModule), reflectionContext.MemberWithRequirements.ToString()); + + reflectionContext.RecordRecognizedPattern(() => _dependencies.Add(_factory.MaximallyConstructableType(foundType), "Type.GetType reference")); + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new SystemTypeValue(foundType)); + } + } + else if (typeNameValue == NullValue.Instance) + { + reflectionContext.RecordHandledPattern(); + } + else if (typeNameValue is LeafValueWithDynamicallyAccessedMemberNode valueWithDynamicallyAccessedMember && valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes != 0) + { + // Propagate the annotation from the type name to the return value. Annotation on a string value will be fullfilled whenever a value is assigned to the string with annotation. + // So while we don't know which type it is, we can guarantee that it will fulfill the annotation. + reflectionContext.RecordHandledPattern(); + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes)); + } + else + { + reflectionContext.RecordUnrecognizedPattern(2057, $"Unrecognized value passed to the parameter 'typeName' of method '{calledMethod.GetDisplayName()}'. It's not possible to guarantee the availability of the target type."); + } + } + + } + break; + + // + // GetConstructor (Type[]) + // GetConstructor (BindingFlags, Type[]) + // GetConstructor (BindingFlags, Binder, Type[], ParameterModifier []) + // GetConstructor (BindingFlags, Binder, CallingConventions, Type[], ParameterModifier []) + // + case IntrinsicId.Type_GetConstructor: + { + reflectionContext.AnalyzingPattern(); + + var parameters = calledMethod.Signature; + BindingFlags? bindingFlags; + if (parameters.Length > 1 && calledMethod.Signature[0].IsTypeOf("System.Reflection", "BindingFlags")) + bindingFlags = GetBindingFlagsFromValue(methodParams[1]); + else + // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter + bindingFlags = BindingFlags.Public | BindingFlags.Instance; + + int? ctorParameterCount = parameters.Length switch + { + 1 => (methodParams[1] as ArrayValue)?.Size.AsConstInt(), + 2 => (methodParams[2] as ArrayValue)?.Size.AsConstInt(), + 4 => (methodParams[3] as ArrayValue)?.Size.AsConstInt(), + 5 => (methodParams[4] as ArrayValue)?.Size.AsConstInt(), + _ => null, + }; + + // Go over all types we've seen + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is SystemTypeValue systemTypeValue) + { + if (BindingFlagsAreUnsupported(bindingFlags)) + { + RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors, value, new MethodOrigin(calledMethod)); + } + else + { + if (HasBindingFlag(bindingFlags, BindingFlags.Public) && !HasBindingFlag(bindingFlags, BindingFlags.NonPublic) + && ctorParameterCount == 0) + { + MarkConstructorsOnType(ref reflectionContext, systemTypeValue.TypeRepresented, m => m.IsPublic() && m.Signature.Length == 0, bindingFlags); + } + else + { + MarkConstructorsOnType(ref reflectionContext, systemTypeValue.TypeRepresented, null, bindingFlags); + } + } + reflectionContext.RecordHandledPattern(); + } + else + { + // Otherwise fall back to the bitfield requirements + var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(bindingFlags); + // We can scope down the public constructors requirement if we know the number of parameters is 0 + if (requiredMemberTypes == DynamicallyAccessedMemberTypes.PublicConstructors && ctorParameterCount == 0) + requiredMemberTypes = DynamicallyAccessedMemberTypes.PublicParameterlessConstructor; + RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new MethodOrigin(calledMethod)); + } + } + } + break; + + // + // GetMethod (string) + // GetMethod (string, BindingFlags) + // GetMethod (string, Type[]) + // GetMethod (string, Type[], ParameterModifier[]) + // GetMethod (string, BindingFlags, Type[]) + // GetMethod (string, BindingFlags, Binder, Type[], ParameterModifier[]) + // GetMethod (string, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[]) + // GetMethod (string, int, Type[]) + // GetMethod (string, int, Type[], ParameterModifier[]?) + // GetMethod (string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?) + // GetMethod (string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?) + // + case IntrinsicId.Type_GetMethod: + { + reflectionContext.AnalyzingPattern(); + + BindingFlags? bindingFlags; + if (calledMethod.Signature.Length > 1 && calledMethod.Signature[1].IsTypeOf("System.Reflection", "BindingFlags")) + bindingFlags = GetBindingFlagsFromValue(methodParams[2]); + else if (calledMethod.Signature.Length > 2 && calledMethod.Signature[2].IsTypeOf("System.Reflection", "BindingFlags")) + bindingFlags = GetBindingFlagsFromValue(methodParams[3]); + else + // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter + bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + + var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags); + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is SystemTypeValue systemTypeValue) + { + foreach (var stringParam in methodParams[1].UniqueValues()) + { + if (stringParam is KnownStringValue stringValue) + { + if (BindingFlagsAreUnsupported(bindingFlags)) + { + RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods, value, new MethodOrigin(calledMethod)); + } + else + { + ProcessGetMethodByName(ref reflectionContext, systemTypeValue.TypeRepresented, stringValue.Contents, bindingFlags, ref methodReturnValue); + } + reflectionContext.RecordHandledPattern(); + } + else + { + // Otherwise fall back to the bitfield requirements + RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new MethodOrigin(calledMethod)); + } + } + } + else + { + // Otherwise fall back to the bitfield requirements + RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new MethodOrigin(calledMethod)); + } + } + } + break; + + // + // GetNestedType (string) + // GetNestedType (string, BindingFlags) + // + case IntrinsicId.Type_GetNestedType: + { + reflectionContext.AnalyzingPattern(); + + BindingFlags? bindingFlags; + if (calledMethod.Signature.Length > 1 && calledMethod.Signature[1].IsTypeOf("System.Reflection", "BindingFlags")) + bindingFlags = GetBindingFlagsFromValue(methodParams[2]); + else + // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter + bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + + var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes(bindingFlags); + bool everyParentTypeHasAll = true; + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is SystemTypeValue systemTypeValue) + { + foreach (var stringParam in methodParams[1].UniqueValues()) + { + if (stringParam is KnownStringValue stringValue) + { + if (BindingFlagsAreUnsupported(bindingFlags)) + // We have chosen not to populate the methodReturnValue for now + RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, value, new MethodOrigin(calledMethod)); + else + { + MetadataType[] matchingNestedTypes = MarkNestedTypesOnType(ref reflectionContext, systemTypeValue.TypeRepresented, m => m.Name == stringValue.Contents, bindingFlags); + + if (matchingNestedTypes != null) + { + for (int i = 0; i < matchingNestedTypes.Length; i++) + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new SystemTypeValue(matchingNestedTypes[i])); + } + } + reflectionContext.RecordHandledPattern(); + } + else + { + // Otherwise fall back to the bitfield requirements + RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new MethodOrigin(calledMethod)); + } + } + } + else + { + // Otherwise fall back to the bitfield requirements + RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new MethodOrigin(calledMethod)); + } + + if (value is LeafValueWithDynamicallyAccessedMemberNode leafValueWithDynamicallyAccessedMember) + { + if (leafValueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.All) + everyParentTypeHasAll = false; + } + else if (!(value is NullValue || value is SystemTypeValue)) + { + // Known Type values are always OK - either they're fully resolved above and thus the return value + // is set to the known resolved type, or if they're not resolved, they won't exist at runtime + // and will cause exceptions - and thus don't introduce new requirements on marking. + // nulls are intentionally ignored as they will lead to exceptions at runtime + // and thus don't introduce new requirements on marking. + everyParentTypeHasAll = false; + } + } + + // If the parent type (all the possible values) has DynamicallyAccessedMemberTypes.All it means its nested types are also fully marked + // (see MarkStep.MarkEntireType - it will recursively mark entire type on nested types). In that case we can annotate + // the returned type (the nested type) with DynamicallyAccessedMemberTypes.All as well. + // Note it's OK to blindly overwrite any potential annotation on the return value from the method definition + // since DynamicallyAccessedMemberTypes.All is a superset of any other annotation. + if (everyParentTypeHasAll && methodReturnValue == null) + methodReturnValue = new MethodReturnValue(calledMethod, DynamicallyAccessedMemberTypes.All); + } + break; + + // + // AssemblyQualifiedName + // + case IntrinsicId.Type_get_AssemblyQualifiedName: + { + + ValueNode transformedResult = null; + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is LeafValueWithDynamicallyAccessedMemberNode dynamicallyAccessedThing) + { + var annotatedString = new AnnotatedStringValue(dynamicallyAccessedThing.SourceContext, dynamicallyAccessedThing.DynamicallyAccessedMemberTypes); + transformedResult = MergePointValue.MergeValues(transformedResult, annotatedString); + } + else + { + transformedResult = null; + break; + } + } + + if (transformedResult != null) + { + methodReturnValue = transformedResult; + } + } + break; + + // + // UnderlyingSystemType + // + case IntrinsicId.Type_get_UnderlyingSystemType: + { + // This is identity for the purposes of the analysis. + methodReturnValue = methodParams[0]; + } + break; + + // + // Type.BaseType + // + case IntrinsicId.Type_get_BaseType: + { + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is LeafValueWithDynamicallyAccessedMemberNode dynamicallyAccessedMemberNode) + { + DynamicallyAccessedMemberTypes propagatedMemberTypes = DynamicallyAccessedMemberTypes.None; + if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) + propagatedMemberTypes = DynamicallyAccessedMemberTypes.All; + else + { + // PublicConstructors are not propagated to base type + + if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicEvents)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicEvents; + + if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicFields)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicFields; + + if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicMethods)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicMethods; + + // PublicNestedTypes are not propagated to base type + + // PublicParameterlessConstructor is not propagated to base type + + if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicProperties)) + propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicProperties; + } + + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, propagatedMemberTypes)); + } + else if (value is SystemTypeValue systemTypeValue) + { + DefType baseTypeDefinition = systemTypeValue.TypeRepresented.BaseType; + if (baseTypeDefinition != null) + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new SystemTypeValue(baseTypeDefinition)); + else + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, DynamicallyAccessedMemberTypes.None)); + } + else if (value == NullValue.Instance) + { + // Ignore nulls - null.BaseType will fail at runtime, but it has no effect on static analysis + continue; + } + else + { + // Unknown input - propagate a return value without any annotation - we know it's a Type but we know nothing about it + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, DynamicallyAccessedMemberTypes.None)); + } + } + } + break; + + // + // GetField (string) + // GetField (string, BindingFlags) + // GetEvent (string) + // GetEvent (string, BindingFlags) + // GetProperty (string) + // GetProperty (string, BindingFlags) + // GetProperty (string, Type) + // GetProperty (string, Type[]) + // GetProperty (string, Type, Type[]) + // GetProperty (string, Type, Type[], ParameterModifier[]) + // GetProperty (string, BindingFlags, Binder, Type, Type[], ParameterModifier[]) + // + case var fieldPropertyOrEvent when (fieldPropertyOrEvent == IntrinsicId.Type_GetField || fieldPropertyOrEvent == IntrinsicId.Type_GetProperty || fieldPropertyOrEvent == IntrinsicId.Type_GetEvent) + && calledMethod.IsDeclaredOnType("System", "Type") + && !calledMethod.Signature.IsStatic + && calledMethod.Signature[0].IsString: + { + + reflectionContext.AnalyzingPattern(); + BindingFlags? bindingFlags; + if (calledMethod.Signature.Length > 1 && calledMethod.Signature[1].IsTypeOf("System.Reflection", "BindingFlags")) + bindingFlags = GetBindingFlagsFromValue(methodParams[2]); + else + // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter + bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + + DynamicallyAccessedMemberTypes memberTypes = fieldPropertyOrEvent switch + { + IntrinsicId.Type_GetEvent => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents(bindingFlags), + IntrinsicId.Type_GetField => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields(bindingFlags), + IntrinsicId.Type_GetProperty => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties(bindingFlags), + _ => throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{callingMethodDefinition.GetDisplayName()}' is of unexpected member type."), + }; + + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is SystemTypeValue systemTypeValue) + { + foreach (var stringParam in methodParams[1].UniqueValues()) + { + if (stringParam is KnownStringValue stringValue) + { + switch (fieldPropertyOrEvent) + { + case IntrinsicId.Type_GetEvent: + if (BindingFlagsAreUnsupported(bindingFlags)) + RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents, value, new MethodOrigin(calledMethod)); + else + MarkEventsOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, filter: e => e.Name == stringValue.Contents, bindingFlags); + break; + case IntrinsicId.Type_GetField: + if (BindingFlagsAreUnsupported(bindingFlags)) + RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields, value, new MethodOrigin(calledMethod)); + else + MarkFieldsOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, filter: f => f.Name == stringValue.Contents, bindingFlags); + break; + case IntrinsicId.Type_GetProperty: + if (BindingFlagsAreUnsupported(bindingFlags)) + RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties, value, new MethodOrigin(calledMethod)); + else + MarkPropertiesOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, filter: p => p.Name == stringValue.Contents, bindingFlags); + break; + default: + Debug.Fail("Unreachable."); + break; + } + reflectionContext.RecordHandledPattern(); + } + else + { + RequireDynamicallyAccessedMembers(ref reflectionContext, memberTypes, value, new MethodOrigin(calledMethod)); + } + } + } + else + { + RequireDynamicallyAccessedMembers(ref reflectionContext, memberTypes, value, new MethodOrigin(calledMethod)); + } + } + } + break; + + // + // GetConstructors (BindingFlags) + // GetMethods (BindingFlags) + // GetFields (BindingFlags) + // GetEvents (BindingFlags) + // GetProperties (BindingFlags) + // GetNestedTypes (BindingFlags) + // GetMembers (BindingFlags) + // + case var callType when (callType == IntrinsicId.Type_GetConstructors || callType == IntrinsicId.Type_GetMethods || callType == IntrinsicId.Type_GetFields || + callType == IntrinsicId.Type_GetProperties || callType == IntrinsicId.Type_GetEvents || callType == IntrinsicId.Type_GetNestedTypes || callType == IntrinsicId.Type_GetMembers) + && calledMethod.IsDeclaredOnType("System", "Type") + && calledMethod.Signature[0].IsTypeOf("System.Reflection", "BindingFlags") + && !calledMethod.Signature.IsStatic: + { + reflectionContext.AnalyzingPattern(); + BindingFlags? bindingFlags; + bindingFlags = GetBindingFlagsFromValue(methodParams[1]); + DynamicallyAccessedMemberTypes memberTypes = DynamicallyAccessedMemberTypes.None; + if (BindingFlagsAreUnsupported(bindingFlags)) + { + memberTypes = callType switch + { + IntrinsicId.Type_GetConstructors => DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors, + IntrinsicId.Type_GetMethods => DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods, + IntrinsicId.Type_GetEvents => DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents, + IntrinsicId.Type_GetFields => DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields, + IntrinsicId.Type_GetProperties => DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties, + IntrinsicId.Type_GetNestedTypes => DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, + IntrinsicId.Type_GetMembers => DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | + DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, + _ => throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{callingMethodDefinition.GetDisplayName()}' is of unexpected member type."), + }; + } + else + { + memberTypes = callType switch + { + IntrinsicId.Type_GetConstructors => GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(bindingFlags), + IntrinsicId.Type_GetMethods => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags), + IntrinsicId.Type_GetEvents => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents(bindingFlags), + IntrinsicId.Type_GetFields => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields(bindingFlags), + IntrinsicId.Type_GetProperties => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties(bindingFlags), + IntrinsicId.Type_GetNestedTypes => GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes(bindingFlags), + IntrinsicId.Type_GetMembers => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers(bindingFlags), + _ => throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{callingMethodDefinition.GetDisplayName()}' is of unexpected member type."), + }; + } + + foreach (var value in methodParams[0].UniqueValues()) + { + RequireDynamicallyAccessedMembers(ref reflectionContext, memberTypes, value, new MethodOrigin(calledMethod)); + } + } + break; + + + // + // GetMember (String) + // GetMember (String, BindingFlags) + // GetMember (String, MemberTypes, BindingFlags) + // + case IntrinsicId.Type_GetMember: + { + reflectionContext.AnalyzingPattern(); + var signature = calledMethod.Signature; + BindingFlags? bindingFlags; + if (signature.Length == 1) + { + // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter + bindingFlags = BindingFlags.Public | BindingFlags.Instance; + } + else if (signature.Length == 2 && calledMethod.HasParameterOfType(1, "System.Reflection", "BindingFlags")) + bindingFlags = GetBindingFlagsFromValue(methodParams[2]); + else if (signature.Length == 3 && calledMethod.HasParameterOfType(2, "System.Reflection", "BindingFlags")) + { + bindingFlags = GetBindingFlagsFromValue(methodParams[3]); + } + else // Non recognized intrinsic + throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{callingMethodDefinition.GetDisplayName()}' is an unexpected intrinsic."); + + DynamicallyAccessedMemberTypes requiredMemberTypes = DynamicallyAccessedMemberTypes.None; + if (BindingFlagsAreUnsupported(bindingFlags)) + { + requiredMemberTypes = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | + DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; + } + else + { + requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers(bindingFlags); + } + // Go over all types we've seen + foreach (var value in methodParams[0].UniqueValues()) + { + // Mark based on bitfield requirements + RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new MethodOrigin(calledMethod)); + } + } + break; + + // + // GetInterface (String) + // GetInterface (String, bool) + // + case IntrinsicId.Type_GetInterface: + { + reflectionContext.AnalyzingPattern(); + foreach (var value in methodParams[0].UniqueValues()) + { + // For now no support for marking a single interface by name. We would have to correctly support + // mangled names for generics to do that correctly. Simply mark all interfaces on the type for now. + // Require Interfaces annotation + RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.Interfaces, value, new MethodOrigin(calledMethod)); + // Interfaces is transitive, so the return values will always have at least Interfaces annotation + DynamicallyAccessedMemberTypes returnMemberTypes = DynamicallyAccessedMemberTypes.Interfaces; + // Propagate All annotation across the call - All is a superset of Interfaces + if (value is LeafValueWithDynamicallyAccessedMemberNode annotatedNode + && annotatedNode.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) + returnMemberTypes = DynamicallyAccessedMemberTypes.All; + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, returnMemberTypes)); + } + } + break; + + // + // System.Activator + // + // static CreateInstance (System.Type type) + // static CreateInstance (System.Type type, bool nonPublic) + // static CreateInstance (System.Type type, params object?[]? args) + // static CreateInstance (System.Type type, object?[]? args, object?[]? activationAttributes) + // static CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture) + // static CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } + // + case IntrinsicId.Activator_CreateInstance_Type: + { + var parameters = calledMethod.Signature; + + reflectionContext.AnalyzingPattern(); + + int? ctorParameterCount = null; + BindingFlags bindingFlags = BindingFlags.Instance; + if (parameters.Length > 1) + { + if (parameters[1].IsWellKnownType(WellKnownType.Boolean)) + { + // The overload that takes a "nonPublic" bool + bool nonPublic = true; + if (methodParams[1] is ConstIntValue constInt) + { + nonPublic = constInt.Value != 0; + } + + if (nonPublic) + bindingFlags |= BindingFlags.NonPublic | BindingFlags.Public; + else + bindingFlags |= BindingFlags.Public; + ctorParameterCount = 0; + } + else + { + // Overload that has the parameters as the second or fourth argument + int argsParam = parameters.Length == 2 || parameters.Length == 3 ? 1 : 3; + + if (methodParams.Count > argsParam) + { + if (methodParams[argsParam] is ArrayValue arrayValue && + arrayValue.Size.AsConstInt() != null) + ctorParameterCount = arrayValue.Size.AsConstInt(); + else if (methodParams[argsParam] is NullValue) + ctorParameterCount = 0; + } + + if (parameters.Length > 3) + { + if (methodParams[1].AsConstInt() is int constInt) + bindingFlags |= (BindingFlags)constInt; + else + bindingFlags |= BindingFlags.NonPublic | BindingFlags.Public; + } + else + { + bindingFlags |= BindingFlags.Public; + } + } + } + else + { + // The overload with a single System.Type argument + ctorParameterCount = 0; + bindingFlags |= BindingFlags.Public; + } + + // Go over all types we've seen + foreach (var value in methodParams[0].UniqueValues()) + { + if (value is SystemTypeValue systemTypeValue) + { + // Special case known type values as we can do better by applying exact binding flags and parameter count. + MarkConstructorsOnType(ref reflectionContext, systemTypeValue.TypeRepresented, + ctorParameterCount == null ? null : m => m.Signature.Length == ctorParameterCount, bindingFlags); + reflectionContext.RecordHandledPattern(); + } + else + { + // Otherwise fall back to the bitfield requirements + var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(bindingFlags); + + // Special case the public parameterless constructor if we know that there are 0 args passed in + if (ctorParameterCount == 0 && requiredMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors)) + { + requiredMemberTypes &= ~DynamicallyAccessedMemberTypes.PublicConstructors; + requiredMemberTypes |= DynamicallyAccessedMemberTypes.PublicParameterlessConstructor; + } + RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new ParameterOrigin(calledMethod, 0)); + } + } + } + break; + +#if false + // TODO: niche APIs that we probably shouldn't even have added + // + // System.Activator + // + // static CreateInstance (string assemblyName, string typeName) + // static CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) + // static CreateInstance (string assemblyName, string typeName, object?[]? activationAttributes) + // + case IntrinsicId.Activator_CreateInstance_AssemblyName_TypeName: + ProcessCreateInstanceByName(ref reflectionContext, calledMethod, methodParams); + break; + + // + // System.Activator + // + // static CreateInstanceFrom (string assemblyFile, string typeName) + // static CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // static CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) + // + case IntrinsicId.Activator_CreateInstanceFrom: + ProcessCreateInstanceByName(ref reflectionContext, calledMethod, methodParams); + break; +#endif + +#if false + // We probably don't need this because there's other places within the compiler that ensure this works. + // + // System.Activator + // + // static T CreateInstance () + // + // Note: If the when condition returns false it would be an overload which we don't recognize, so just fall through to the default case + case IntrinsicId.Activator_CreateInstanceOfT when + calledMethod.Instantiation.Length == 1: + { + reflectionContext.AnalyzingPattern(); + + if (genericCalledMethod.GenericArguments[0] is GenericParameter genericParameter && + genericParameter.HasDefaultConstructorConstraint) + { + // This is safe, the linker would have marked the default .ctor already + reflectionContext.RecordHandledPattern(); + break; + } + + RequireDynamicallyAccessedMembers( + ref reflectionContext, + DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, + GetTypeValueNodeFromGenericArgument(genericCalledMethod.GenericArguments[0]), + calledMethodDefinition.GenericParameters[0]); + } + break; +#endif + +#if false + // TODO: niche APIs that we probably shouldn't even have added + // + // System.AppDomain + // + // CreateInstance (string assemblyName, string typeName) + // CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // CreateInstance (string assemblyName, string typeName, object? []? activationAttributes) + // + // CreateInstanceAndUnwrap (string assemblyName, string typeName) + // CreateInstanceAndUnwrap (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // CreateInstanceAndUnwrap (string assemblyName, string typeName, object? []? activationAttributes) + // + // CreateInstanceFrom (string assemblyFile, string typeName) + // CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) + // + // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName) + // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, object? []? activationAttributes) + // + case var appDomainCreateInstance when appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstance + || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceAndUnwrap + || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFrom + || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap: + ProcessCreateInstanceByName(ref reflectionContext, calledMethod, methodParams); + break; +#endif + + // + // System.Reflection.Assembly + // + // CreateInstance (string typeName) + // CreateInstance (string typeName, bool ignoreCase) + // CreateInstance (string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder? binder, object []? args, CultureInfo? culture, object []? activationAttributes) + // + case IntrinsicId.Assembly_CreateInstance: + // For now always fail since we don't track assemblies (dotnet/linker/issues/1947) + reflectionContext.AnalyzingPattern(); + reflectionContext.RecordUnrecognizedPattern(2058, $"Parameters passed to method '{calledMethod.GetDisplayName()}' cannot be analyzed. Consider using methods 'System.Type.GetType' and `System.Activator.CreateInstance` instead."); + break; + + // + // System.Runtime.CompilerServices.RuntimeHelpers + // + // RunClassConstructor (RuntimeTypeHandle type) + // + case IntrinsicId.RuntimeHelpers_RunClassConstructor: + { + reflectionContext.AnalyzingPattern(); + foreach (var typeHandleValue in methodParams[0].UniqueValues()) + { + if (typeHandleValue is RuntimeTypeHandleValue runtimeTypeHandleValue) + { + TypeDesc typeRepresented = runtimeTypeHandleValue.TypeRepresented; + if (!typeRepresented.IsGenericDefinition && !typeRepresented.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true) && typeRepresented.HasStaticConstructor) + { + _dependencies.Add(_factory.CanonicalEntrypoint(typeRepresented.GetStaticConstructor()), "RunClassConstructor reference"); + } + + reflectionContext.RecordHandledPattern(); + } + else if (typeHandleValue == NullValue.Instance) + reflectionContext.RecordHandledPattern(); + else + { + reflectionContext.RecordUnrecognizedPattern(2059, $"Unrecognized value passed to the parameter 'type' of method '{calledMethod.GetDisplayName()}'. It's not possible to guarantee the availability of the target static constructor."); + } + } + } + break; + + // + // System.Reflection.MethodInfo + // + // MakeGenericMethod (Type[] typeArguments) + // + case IntrinsicId.MethodInfo_MakeGenericMethod: + { + reflectionContext.AnalyzingPattern(); + + foreach (var methodValue in methodParams[0].UniqueValues()) + { + if (methodValue is SystemReflectionMethodBaseValue methodBaseValue) + { + ValidateGenericMethodInstantiation(ref reflectionContext, methodBaseValue.MethodRepresented, methodParams[1], calledMethod); + } + else if (methodValue == NullValue.Instance) + { + reflectionContext.RecordHandledPattern(); + } + else + { + // We don't know what method the `MakeGenericMethod` was called on, so we have to assume + // that the method may have requirements which we can't fullfil -> warn. + reflectionContext.RecordUnrecognizedPattern( + 2060, string.Format(Resources.Strings.IL2060, + DiagnosticUtilities.GetMethodSignatureDisplayName(calledMethod))); + } + } + // MakeGenericMethod doesn't change the identity of the MethodBase we're tracking so propagate to the return value + methodReturnValue = methodParams[0]; + + if (shouldEnableAotWarnings) + LogDynamicCodeWarning(_logger, callingMethodBody, offset, calledMethod); + } + break; + + default: + if (calledMethod.IsPInvoke) + { + // Is the PInvoke dangerous? + ParameterMetadata[] paramMetadata = calledMethod.GetParameterMetadata(); + + ParameterMetadata returnParamMetadata = Array.Find(paramMetadata, m => m.Index == 0); + + bool comDangerousMethod = IsComInterop(returnParamMetadata.MarshalAsDescriptor, calledMethod.Signature.ReturnType); + for (int paramIndex = 0; paramIndex < calledMethod.Signature.Length; paramIndex++) + { + MarshalAsDescriptor marshalAsDescriptor = null; + for (int metadataIndex = 0; metadataIndex < paramMetadata.Length; metadataIndex++) + { + if (paramMetadata[metadataIndex].Index == paramIndex + 1) + marshalAsDescriptor = paramMetadata[metadataIndex].MarshalAsDescriptor; + } + + comDangerousMethod |= IsComInterop(marshalAsDescriptor, calledMethod.Signature[paramIndex]); + } + + if (comDangerousMethod) + { + reflectionContext.AnalyzingPattern(); + reflectionContext.RecordUnrecognizedPattern(2050, $"P/invoke method '{calledMethod.GetDisplayName()}' declares a parameter with COM marshalling. Correctness of COM interop cannot be guaranteed after trimming. Interfaces and interface members might be removed."); + } + } + + if (requiresDataFlowAnalysis) + { + reflectionContext.AnalyzingPattern(); + for (int parameterIndex = 0; parameterIndex < methodParams.Count; parameterIndex++) + { + var requiredMemberTypes = _flowAnnotations.GetParameterAnnotation(calledMethod, parameterIndex); + if (requiredMemberTypes != 0) + { + Origin targetContext = DiagnosticUtilities.GetMethodParameterFromIndex(calledMethod, parameterIndex); + RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, methodParams[parameterIndex], targetContext); + } + } + + reflectionContext.RecordHandledPattern(); + } + + if (shouldEnableReflectionWarnings && + calledMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresUnreferencedCodeAttribute")) + { + string attributeMessage = DiagnosticUtilities.GetRequiresUnreferencedCodeAttributeMessage(calledMethod); + + if (attributeMessage != null && attributeMessage.Length > 0 && !attributeMessage.EndsWith('.')) + attributeMessage += '.'; + + string message = + $"Using method '{calledMethod.GetDisplayName()}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {attributeMessage}"; + + //if (requiresUnreferencedCode.Url != null) + //{ + // message += " " + requiresUnreferencedCode.Url; + //} + + _logger.LogWarning(message, 2026, callingMethodBody, offset, MessageSubCategory.TrimAnalysis); + } + + + + if (shouldEnableAotWarnings && + calledMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresDynamicCodeAttribute")) + { + LogDynamicCodeWarning(_logger, callingMethodBody, offset, calledMethod); + } + + static void LogDynamicCodeWarning(Logger logger, MethodIL callingMethodBody, int offset, MethodDesc calledMethod) + { + string attributeMessage = DiagnosticUtilities.GetRequiresDynamicCodeAttributeMessage(calledMethod); + + if (attributeMessage != null && attributeMessage.Length > 0 && !attributeMessage.EndsWith('.')) + attributeMessage += '.'; + + string message = $"{String.Format(Resources.Strings.IL3050, calledMethod.GetDisplayName())} {attributeMessage}"; + + //if (requiresUnreferencedCode.Url != null) + //{ + // message += " " + requiresUnreferencedCode.Url; + //} + + logger.LogWarning(message, 3050, callingMethodBody, offset, MessageSubCategory.AotAnalysis); + } + + // To get good reporting of errors we need to track the origin of the value for all method calls + // but except Newobj as those are special. + if (!calledMethod.Signature.ReturnType.IsVoid) + { + methodReturnValue = new MethodReturnValue(calledMethod, returnValueDynamicallyAccessedMemberTypes); + + return true; + } + + return false; + } + } + finally + { + reflectionContext.Dispose(); + } + + // If we get here, we handled this as an intrinsic. As a convenience, if the code above + // didn't set the return value (and the method has a return value), we will set it to be an + // unknown value with the return type of the method. + if (methodReturnValue == null) + { + if (!calledMethod.Signature.ReturnType.IsVoid) + { + methodReturnValue = new MethodReturnValue(calledMethod, returnValueDynamicallyAccessedMemberTypes); + } + } + + // Validate that the return value has the correct annotations as per the method return value annotations + if (returnValueDynamicallyAccessedMemberTypes != 0) + { + foreach (var uniqueValue in methodReturnValue.UniqueValues()) + { + if (uniqueValue is LeafValueWithDynamicallyAccessedMemberNode methodReturnValueWithMemberTypes) + { + if (!methodReturnValueWithMemberTypes.DynamicallyAccessedMemberTypes.HasFlag(returnValueDynamicallyAccessedMemberTypes)) + throw new InvalidOperationException($"Internal linker error: processing of call from {callingMethodDefinition.GetDisplayName()} to {calledMethod.GetDisplayName()} returned value which is not correctly annotated with the expected dynamic member access kinds."); + } + else if (uniqueValue is SystemTypeValue) + { + // SystemTypeValue can fullfill any requirement, so it's always valid + // The requirements will be applied at the point where it's consumed (passed as a method parameter, set as field value, returned from the method) + } + else + { + throw new InvalidOperationException($"Internal linker error: processing of call from {callingMethodDefinition.GetDisplayName()} to {calledMethod.GetDisplayName()} returned value which is not correctly annotated with the expected dynamic member access kinds."); + } + } + } + + return true; + } + + bool IsComInterop(MarshalAsDescriptor marshalInfoProvider, TypeDesc parameterType) + { + // This is best effort. One can likely find ways how to get COM without triggering these alarms. + // AsAny marshalling of a struct with an object-typed field would be one, for example. + + // This logic roughly corresponds to MarshalInfo::MarshalInfo in CoreCLR, + // not trying to handle invalid cases and distinctions that are not interesting wrt + // "is this COM?" question. + + NativeTypeKind nativeType = NativeTypeKind.Default; + if (marshalInfoProvider != null) + { + nativeType = marshalInfoProvider.Type; + } + + if (nativeType == NativeTypeKind.IUnknown || nativeType == NativeTypeKind.IDispatch || nativeType == NativeTypeKind.Intf) + { + // This is COM by definition + return true; + } + + if (nativeType == NativeTypeKind.Default) + { + TypeSystemContext context = parameterType.Context; + + if (parameterType.IsPointer) + return false; + + while (parameterType.IsParameterizedType) + parameterType = ((ParameterizedType)parameterType).ParameterType; + + if (parameterType.IsWellKnownType(WellKnownType.Array)) + { + // System.Array marshals as IUnknown by default + return true; + } + else if (parameterType.IsWellKnownType(WellKnownType.String) || + InteropTypes.IsStringBuilder(context, parameterType)) + { + // String and StringBuilder are special cased by interop + return false; + } + + if (parameterType.IsValueType) + { + // Value types don't marshal as COM + return false; + } + else if (parameterType.IsInterface) + { + // Interface types marshal as COM by default + return true; + } + else if (parameterType.IsDelegate || parameterType.IsWellKnownType(WellKnownType.MulticastDelegate) + || parameterType == context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType) + { + // Delegates are special cased by interop + return false; + } + else if (InteropTypes.IsCriticalHandle(context, parameterType)) + { + // Subclasses of CriticalHandle are special cased by interop + return false; + } + else if (InteropTypes.IsSafeHandle(context, parameterType)) + { + // Subclasses of SafeHandle are special cased by interop + return false; + } + else if (parameterType is MetadataType mdType && !mdType.IsSequentialLayout && !mdType.IsExplicitLayout) + { + // Rest of classes that don't have layout marshal as COM + return true; + } + } + + return false; + } + + private bool AnalyzeGenericInstantiationTypeArray(ValueNode arrayParam, ref ReflectionPatternContext reflectionContext, MethodDesc calledMethod, Instantiation genericParameters) + { + bool hasRequirements = false; + foreach (GenericParameterDesc genericParameter in genericParameters) + { + if (_flowAnnotations.GetGenericParameterAnnotation(genericParameter) != DynamicallyAccessedMemberTypes.None) + { + hasRequirements = true; + break; + } + } + + // If there are no requirements, then there's no point in warning + if (!hasRequirements) + return true; + + foreach (var typesValue in arrayParam.UniqueValues()) + { + if (typesValue.Kind != ValueNodeKind.Array) + { + return false; + } + ArrayValue array = (ArrayValue)typesValue; + int? size = array.Size.AsConstInt(); + if (size == null || size != genericParameters.Length) + { + return false; + } + bool allIndicesKnown = true; + for (int i = 0; i < size.Value; i++) + { + if (!array.IndexValues.TryGetValue(i, out ValueBasicBlockPair value) || value.Value is null or { Kind: ValueNodeKind.Unknown }) + { + allIndicesKnown = false; + break; + } + } + + if (!allIndicesKnown) + { + return false; + } + + for (int i = 0; i < size.Value; i++) + { + if (array.IndexValues.TryGetValue(i, out ValueBasicBlockPair value)) + { + RequireDynamicallyAccessedMembers( + ref reflectionContext, + _flowAnnotations.GetGenericParameterAnnotation((GenericParameterDesc)genericParameters[i]), + value.Value, + new MethodOrigin(calledMethod)); + } + } + } + return true; + } + +#if false + void ProcessCreateInstanceByName(ref ReflectionPatternContext reflectionContext, MethodDesc calledMethod, ValueNodeList methodParams) + { + reflectionContext.AnalyzingPattern(); + + BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; + bool parameterlessConstructor = true; + if (calledMethod.Parameters.Count == 8 && calledMethod.Parameters[2].ParameterType.MetadataType == MetadataType.Boolean) + { + parameterlessConstructor = false; + bindingFlags = BindingFlags.Instance; + if (methodParams[3].AsConstInt() is int bindingFlagsInt) + bindingFlags |= (BindingFlags)bindingFlagsInt; + else + bindingFlags |= BindingFlags.Public | BindingFlags.NonPublic; + } + + int methodParamsOffset = !calledMethod.Signature.IsStatic ? 1 : 0; + + foreach (var assemblyNameValue in methodParams[methodParamsOffset].UniqueValues()) + { + if (assemblyNameValue is KnownStringValue assemblyNameStringValue) + { + foreach (var typeNameValue in methodParams[methodParamsOffset + 1].UniqueValues()) + { + if (typeNameValue is KnownStringValue typeNameStringValue) + { + var resolvedAssembly = _context.GetLoadedAssembly(assemblyNameStringValue.Contents); + if (resolvedAssembly == null) + { + reflectionContext.RecordUnrecognizedPattern(2061, $"The assembly name '{assemblyNameStringValue.Contents}' passed to method '{calledMethod.GetDisplayName()}' references assembly which is not available."); + continue; + } + + var resolvedType = _context.TypeNameResolver.ResolveTypeName(resolvedAssembly, typeNameStringValue.Contents)?.Resolve(); + if (resolvedType == null) + { + // It's not wrong to have a reference to non-existing type - the code may well expect to get an exception in this case + // Note that we did find the assembly, so it's not a linker config problem, it's either intentional, or wrong versions of assemblies + // but linker can't know that. + reflectionContext.RecordHandledPattern(); + continue; + } + + MarkConstructorsOnType(ref reflectionContext, resolvedType, parameterlessConstructor ? m => m.Parameters.Count == 0 : null, bindingFlags); + } + else + { + reflectionContext.RecordUnrecognizedPattern(2032, $"Unrecognized value passed to the parameter '{calledMethod.Parameters[1].Name}' of method '{calledMethod.GetDisplayName()}'. It's not possible to guarantee the availability of the target type."); + } + } + } + else + { + reflectionContext.RecordUnrecognizedPattern(2032, $"Unrecognized value passed to the parameter '{calledMethod.Parameters[0].Name}' of method '{calledMethod.GetDisplayName()}'. It's not possible to guarantee the availability of the target type."); + } + } + } +#endif + + void ProcessGetMethodByName( + ref ReflectionPatternContext reflectionContext, + TypeDesc typeDefinition, + string methodName, + BindingFlags? bindingFlags, + ref ValueNode methodReturnValue) + { + bool foundAny = false; + foreach (var method in typeDefinition.GetMethodsOnTypeHierarchy(m => m.Name == methodName, bindingFlags)) + { + MarkMethod(ref reflectionContext, method); + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new SystemReflectionMethodBaseValue(method)); + foundAny = true; + } + // If there were no methods found the API will return null at runtime, so we should + // track the null as a return value as well. + // This also prevents warnings in such case, since if we don't set the return value it will be + // "unknown" and consumers may warn. + if (!foundAny) + methodReturnValue = MergePointValue.MergeValues(methodReturnValue, NullValue.Instance); + } + + public static DynamicallyAccessedMemberTypes GetMissingMemberTypes(DynamicallyAccessedMemberTypes requiredMemberTypes, DynamicallyAccessedMemberTypes availableMemberTypes) + { + if (availableMemberTypes.HasFlag(requiredMemberTypes)) + return DynamicallyAccessedMemberTypes.None; + + if (requiredMemberTypes == DynamicallyAccessedMemberTypes.All) + return DynamicallyAccessedMemberTypes.All; + + var missingMemberTypes = requiredMemberTypes & ~availableMemberTypes; + + // PublicConstructors is a special case since its value is 3 - so PublicParameterlessConstructor (1) | _PublicConstructor_WithMoreThanOneParameter_ (2) + // The above bit logic only works for value with single bit set. + if (requiredMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors) && + !availableMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors)) + missingMemberTypes |= DynamicallyAccessedMemberTypes.PublicConstructors; + + return missingMemberTypes; + } + + private string GetMemberTypesString(DynamicallyAccessedMemberTypes memberTypes) + { + Debug.Assert(memberTypes != DynamicallyAccessedMemberTypes.None); + + if (memberTypes == DynamicallyAccessedMemberTypes.All) + return $"'{nameof(DynamicallyAccessedMemberTypes)}.{nameof(DynamicallyAccessedMemberTypes.All)}'"; + + var memberTypesList = Enum.GetValues() + .Where(damt => (memberTypes & damt) == damt && damt != DynamicallyAccessedMemberTypes.None) + .ToList(); + + if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors)) + memberTypesList.Remove(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor); + + return string.Join(", ", memberTypesList.Select(mt => $"'{nameof(DynamicallyAccessedMemberTypes)}.{mt}'")); + } + + void RequireDynamicallyAccessedMembers(ref ReflectionPatternContext reflectionContext, DynamicallyAccessedMemberTypes requiredMemberTypes, ValueNode value, Origin targetContext) + { + foreach (var uniqueValue in value.UniqueValues()) + { + if (requiredMemberTypes == DynamicallyAccessedMemberTypes.PublicParameterlessConstructor + && uniqueValue is SystemTypeForGenericParameterValue genericParam + && genericParam.GenericParameter.HasDefaultConstructorConstraint) + { + // We allow a new() constraint on a generic parameter to satisfy DynamicallyAccessedMemberTypes.PublicParameterlessConstructor + reflectionContext.RecordHandledPattern(); + } + else if (uniqueValue is LeafValueWithDynamicallyAccessedMemberNode valueWithDynamicallyAccessedMember) + { + var availableMemberTypes = valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes; + var missingMemberTypesValue = GetMissingMemberTypes(requiredMemberTypes, availableMemberTypes); + if (missingMemberTypesValue != DynamicallyAccessedMemberTypes.None) + { + var missingMemberTypes = GetMemberTypesString(missingMemberTypesValue); + switch ((valueWithDynamicallyAccessedMember.SourceContext, targetContext)) + { + case (ParameterOrigin sourceParameter, ParameterOrigin targetParameter): + reflectionContext.RecordUnrecognizedPattern(2067, string.Format(Resources.Strings.IL2067, + DiagnosticUtilities.GetParameterNameForErrorMessage(targetParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName(targetParameter.Method), + DiagnosticUtilities.GetParameterNameForErrorMessage(sourceParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName(sourceParameter.Method), + missingMemberTypes)); + break; + case (ParameterOrigin sourceParameter, MethodReturnOrigin targetMethodReturnType): + reflectionContext.RecordUnrecognizedPattern(2068, string.Format(Resources.Strings.IL2068, + DiagnosticUtilities.GetMethodSignatureDisplayName(targetMethodReturnType.Method), + DiagnosticUtilities.GetParameterNameForErrorMessage(sourceParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName(sourceParameter.Method), + missingMemberTypes)); + break; + case (ParameterOrigin sourceParameter, FieldOrigin targetField): + reflectionContext.RecordUnrecognizedPattern(2069, string.Format(Resources.Strings.IL2069, + targetField.GetDisplayName(), + DiagnosticUtilities.GetParameterNameForErrorMessage(sourceParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName(sourceParameter.Method), + missingMemberTypes)); + break; + case (ParameterOrigin sourceParameter, MethodOrigin targetMethod): + reflectionContext.RecordUnrecognizedPattern(2070, string.Format(Resources.Strings.IL2070, + targetMethod.GetDisplayName(), + DiagnosticUtilities.GetParameterNameForErrorMessage(sourceParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName(sourceParameter.Method), + missingMemberTypes)); + break; + case (ParameterOrigin sourceParameter, GenericParameterOrigin targetGenericParameter): + // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used + reflectionContext.RecordUnrecognizedPattern(2071, string.Format(Resources.Strings.IL2071, + targetGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(targetGenericParameter), + DiagnosticUtilities.GetParameterNameForErrorMessage(sourceParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName(sourceParameter.Method), + missingMemberTypes)); + break; + + case (MethodReturnOrigin sourceMethodReturnType, ParameterOrigin targetParameter): + reflectionContext.RecordUnrecognizedPattern(2072, string.Format(Resources.Strings.IL2072, + DiagnosticUtilities.GetParameterNameForErrorMessage(targetParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName(targetParameter.Method), + DiagnosticUtilities.GetMethodSignatureDisplayName(sourceMethodReturnType.Method), + missingMemberTypes)); + break; + case (MethodReturnOrigin sourceMethodReturnType, MethodReturnOrigin targetMethodReturnType): + reflectionContext.RecordUnrecognizedPattern(2073, string.Format(Resources.Strings.IL2073, + DiagnosticUtilities.GetMethodSignatureDisplayName(targetMethodReturnType.Method), + DiagnosticUtilities.GetMethodSignatureDisplayName(sourceMethodReturnType.Method), + missingMemberTypes)); + break; + case (MethodReturnOrigin sourceMethodReturnType, FieldOrigin targetField): + reflectionContext.RecordUnrecognizedPattern(2074, string.Format(Resources.Strings.IL2074, + targetField.GetDisplayName(), + DiagnosticUtilities.GetMethodSignatureDisplayName(sourceMethodReturnType.Method), + missingMemberTypes)); + break; + case (MethodReturnOrigin sourceMethodReturnType, MethodOrigin targetMethod): + reflectionContext.RecordUnrecognizedPattern(2075, string.Format(Resources.Strings.IL2075, + targetMethod.GetDisplayName(), + DiagnosticUtilities.GetMethodSignatureDisplayName(sourceMethodReturnType.Method), + missingMemberTypes)); + break; + case (MethodReturnOrigin sourceMethodReturnType, GenericParameterOrigin targetGenericParameter): + // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used + reflectionContext.RecordUnrecognizedPattern(2076, string.Format(Resources.Strings.IL2076, + targetGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(targetGenericParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName(sourceMethodReturnType.Method), + missingMemberTypes)); + break; + + case (FieldOrigin sourceField, ParameterOrigin targetParameter): + reflectionContext.RecordUnrecognizedPattern(2077, string.Format(Resources.Strings.IL2077, + DiagnosticUtilities.GetParameterNameForErrorMessage(targetParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName(targetParameter.Method), + sourceField.GetDisplayName(), + missingMemberTypes)); + break; + case (FieldOrigin sourceField, MethodReturnOrigin targetMethodReturnType): + reflectionContext.RecordUnrecognizedPattern(2078, string.Format(Resources.Strings.IL2078, + DiagnosticUtilities.GetMethodSignatureDisplayName(targetMethodReturnType.Method), + sourceField.GetDisplayName(), + missingMemberTypes)); + break; + case (FieldOrigin sourceField, FieldOrigin targetField): + reflectionContext.RecordUnrecognizedPattern(2079, string.Format(Resources.Strings.IL2079, + targetField.GetDisplayName(), + sourceField.GetDisplayName(), + missingMemberTypes)); + break; + case (FieldOrigin sourceField, MethodOrigin targetMethod): + reflectionContext.RecordUnrecognizedPattern(2080, string.Format(Resources.Strings.IL2080, + targetMethod.GetDisplayName(), + sourceField.GetDisplayName(), + missingMemberTypes)); + break; + case (FieldOrigin sourceField, GenericParameterOrigin targetGenericParameter): + // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used + reflectionContext.RecordUnrecognizedPattern(2081, string.Format(Resources.Strings.IL2081, + targetGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(targetGenericParameter), + sourceField.GetDisplayName(), + missingMemberTypes)); + break; + + case (MethodOrigin sourceMethod, ParameterOrigin targetParameter): + reflectionContext.RecordUnrecognizedPattern(2082, string.Format(Resources.Strings.IL2082, + DiagnosticUtilities.GetParameterNameForErrorMessage(targetParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName(targetParameter.Method), + sourceMethod.GetDisplayName(), + missingMemberTypes)); + break; + case (MethodOrigin sourceMethod, MethodReturnOrigin targetMethodReturnType): + reflectionContext.RecordUnrecognizedPattern(2083, string.Format(Resources.Strings.IL2083, + DiagnosticUtilities.GetMethodSignatureDisplayName(targetMethodReturnType.Method), + sourceMethod.GetDisplayName(), + missingMemberTypes)); + break; + case (MethodOrigin sourceMethod, FieldOrigin targetField): + reflectionContext.RecordUnrecognizedPattern(2084, string.Format(Resources.Strings.IL2084, + targetField.GetDisplayName(), + sourceMethod.GetDisplayName(), + missingMemberTypes)); + break; + case (MethodOrigin sourceMethod, MethodOrigin targetMethod): + reflectionContext.RecordUnrecognizedPattern(2085, string.Format(Resources.Strings.IL2085, + targetMethod.GetDisplayName(), + sourceMethod.GetDisplayName(), + missingMemberTypes)); + break; + case (MethodOrigin sourceMethod, GenericParameterOrigin targetGenericParameter): + // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used + reflectionContext.RecordUnrecognizedPattern(2086, string.Format(Resources.Strings.IL2086, + targetGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(targetGenericParameter), + sourceMethod.GetDisplayName(), + missingMemberTypes)); + break; + + case (GenericParameterOrigin sourceGenericParameter, ParameterOrigin targetParameter): + reflectionContext.RecordUnrecognizedPattern(2087, string.Format(Resources.Strings.IL2087, + DiagnosticUtilities.GetParameterNameForErrorMessage(targetParameter), + DiagnosticUtilities.GetMethodSignatureDisplayName(targetParameter.Method), + sourceGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(sourceGenericParameter), + missingMemberTypes)); + break; + case (GenericParameterOrigin sourceGenericParameter, MethodReturnOrigin targetMethodReturnType): + reflectionContext.RecordUnrecognizedPattern(2088, string.Format(Resources.Strings.IL2088, + DiagnosticUtilities.GetMethodSignatureDisplayName(targetMethodReturnType.Method), + sourceGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(sourceGenericParameter), + missingMemberTypes)); + break; + case (GenericParameterOrigin sourceGenericParameter, FieldOrigin targetField): + reflectionContext.RecordUnrecognizedPattern(2089, string.Format(Resources.Strings.IL2089, + targetField.GetDisplayName(), + sourceGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(sourceGenericParameter), + missingMemberTypes)); + break; + case (GenericParameterOrigin sourceGenericParameter, MethodOrigin targetMethod): + // Currently this is never generated, it might be possible one day if we try to validate annotations on results of reflection + // For example code like this should ideally one day generate the warning + // void TestMethod() + // { + // // This passes the T as the "this" parameter to Type.GetMethods() + // typeof(Type).GetMethod("GetMethods").Invoke(typeof(T), new object[] {}); + // } + reflectionContext.RecordUnrecognizedPattern(2090, string.Format(Resources.Strings.IL2090, + targetMethod.GetDisplayName(), + sourceGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(sourceGenericParameter), + missingMemberTypes)); + break; + case (GenericParameterOrigin sourceGenericParameter, GenericParameterOrigin targetGenericParameter): + reflectionContext.RecordUnrecognizedPattern(2091, string.Format(Resources.Strings.IL2091, + targetGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(targetGenericParameter), + sourceGenericParameter.Name, + DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(sourceGenericParameter), + missingMemberTypes)); + break; + + default: + throw new NotImplementedException($"unsupported source context {valueWithDynamicallyAccessedMember.SourceContext} or target context {targetContext}"); + }; + } + else + { + reflectionContext.RecordHandledPattern(); + } + } + else if (uniqueValue is SystemTypeValue systemTypeValue) + { + MarkTypeForDynamicallyAccessedMembers(ref reflectionContext, systemTypeValue.TypeRepresented, requiredMemberTypes); + } + else if (uniqueValue is KnownStringValue knownStringValue) + { + ModuleDesc callingModule = ((reflectionContext.Source as MethodDesc)?.OwningType as MetadataType)?.Module; + + if (!ILCompiler.DependencyAnalysis.ReflectionMethodBodyScanner.ResolveType(knownStringValue.Contents, callingModule, reflectionContext.Source.Context, out TypeDesc foundType, out ModuleDesc referenceModule)) + { + // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. + reflectionContext.RecordHandledPattern(); + } + else + { + // Also add module metadata in case this reference was through a type forward + if (_factory.MetadataManager.CanGenerateMetadata(referenceModule.GetGlobalModuleType())) + _dependencies.Add(_factory.ModuleMetadata(referenceModule), reflectionContext.MemberWithRequirements.ToString()); + + MarkType(ref reflectionContext, foundType); + MarkTypeForDynamicallyAccessedMembers(ref reflectionContext, foundType, requiredMemberTypes); + } + } + else if (uniqueValue == NullValue.Instance) + { + // Ignore - probably unreachable path as it would fail at runtime anyway. + } + else + { + switch (targetContext) + { + case ParameterOrigin parameterDefinition: + reflectionContext.RecordUnrecognizedPattern( + 2062, + $"Value passed to parameter '{DiagnosticUtilities.GetParameterNameForErrorMessage(parameterDefinition)}' of method '{DiagnosticUtilities.GetMethodSignatureDisplayName(parameterDefinition.Method)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); + break; + case MethodReturnOrigin methodReturnType: + reflectionContext.RecordUnrecognizedPattern( + 2063, + $"Value returned from method '{DiagnosticUtilities.GetMethodSignatureDisplayName(methodReturnType.Method)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); + break; + case FieldOrigin fieldDefinition: + reflectionContext.RecordUnrecognizedPattern( + 2064, + $"Value assigned to {fieldDefinition.GetDisplayName()} can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); + break; + case MethodOrigin methodDefinition: + reflectionContext.RecordUnrecognizedPattern( + 2065, + $"Value passed to implicit 'this' parameter of method '{methodDefinition.GetDisplayName()}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); + break; + case GenericParameterOrigin genericParameter: + // Unknown value to generic parameter - this is possible if the generic argumnet fails to resolve + reflectionContext.RecordUnrecognizedPattern( + 2066, + $"Type passed to generic parameter '{genericParameter.Name}' of '{DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(genericParameter)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); + break; + default: throw new NotImplementedException($"unsupported target context {targetContext.GetType()}"); + }; + } + } + + reflectionContext.RecordHandledPattern(); + } + + static BindingFlags? GetBindingFlagsFromValue(ValueNode parameter) => (BindingFlags?)parameter.AsConstInt(); + + static bool BindingFlagsAreUnsupported(BindingFlags? bindingFlags) + { + if (bindingFlags == null) + return true; + + // Binding flags we understand + const BindingFlags UnderstoodBindingFlags = + BindingFlags.DeclaredOnly | + BindingFlags.Instance | + BindingFlags.Static | + BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.FlattenHierarchy | + BindingFlags.ExactBinding; + + // Binding flags that don't affect binding outside InvokeMember (that we don't analyze). + const BindingFlags IgnorableBindingFlags = + BindingFlags.InvokeMethod | + BindingFlags.CreateInstance | + BindingFlags.GetField | + BindingFlags.SetField | + BindingFlags.GetProperty | + BindingFlags.SetProperty; + + BindingFlags flags = bindingFlags.Value; + return (flags & ~(UnderstoodBindingFlags | IgnorableBindingFlags)) != 0; + } + + static bool HasBindingFlag(BindingFlags? bindingFlags, BindingFlags? search) => bindingFlags != null && (bindingFlags & search) == search; + + void MarkTypeForDynamicallyAccessedMembers(ref ReflectionPatternContext reflectionContext, TypeDesc typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes, bool declaredOnly = false) + { + foreach (var member in typeDefinition.GetDynamicallyAccessedMembers(requiredMemberTypes, declaredOnly)) + { + switch (member) + { + case MethodDesc method: + MarkMethod(ref reflectionContext, method); + break; + case FieldDesc field: + MarkField(ref reflectionContext, field); + break; + case MetadataType type: + MarkType(ref reflectionContext, type); + break; + case PropertyPseudoDesc property: + MarkProperty(ref reflectionContext, property); + break; + case EventPseudoDesc @event: + MarkEvent(ref reflectionContext, @event); + break; + default: + Debug.Fail(member.GetType().ToString()); + break; + } + } + } + + void MarkType(ref ReflectionPatternContext reflectionContext, TypeDesc type) + { + RootingHelpers.TryGetDependenciesForReflectedType(ref _dependencies, _factory, type, reflectionContext.MemberWithRequirements.ToString()); + reflectionContext.RecordHandledPattern(); + } + + void WarnOnReflectionAccess(ref ReflectionPatternContext context, TypeSystemEntity entity) + { + if (_purpose == ScanningPurpose.GetTypeDataflow) + { + // Don't check whether the current scope is a RUC type or RUC method because these warnings + // are not suppressed in RUC scopes. Here the scope represents the DynamicallyAccessedMembers + // annotation on a type, not a callsite which uses the annotation. We always want to warn about + // possible reflection access indicated by these annotations. + + var message = string.Format( + "'DynamicallyAccessedMembersAttribute' on '{0}' or one of its base types references '{1}' which has 'DynamicallyAccessedMembersAttribute' requirements.", + ((TypeOrigin)context.MemberWithRequirements).GetDisplayName(), + entity.GetDisplayName()); + _logger.LogWarning(message, 2115, context.Source, MessageSubCategory.TrimAnalysis); + } + else + { + if (entity is FieldDesc && context.ReportingEnabled) + { + _logger.LogWarning( + $"Field '{entity.GetDisplayName()}' with 'DynamicallyAccessedMembersAttribute' is accessed via reflection. Trimmer can't guarantee availability of the requirements of the field.", + 2110, + context.Source, + MessageSubCategory.TrimAnalysis); + } + else + { + Debug.Assert(entity is MethodDesc); + + _logger.LogWarning( + $"Method '{entity.GetDisplayName()}' with parameters or return value with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can't guarantee availability of the requirements of the method.", + 2111, + context.Source, + MessageSubCategory.TrimAnalysis); + } + } + } + + void MarkMethod(ref ReflectionPatternContext reflectionContext, MethodDesc method) + { + if (method.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresUnreferencedCodeAttribute")) + { + if (_purpose == ScanningPurpose.GetTypeDataflow) + { + var message = string.Format( + "'DynamicallyAccessedMembersAttribute' on '{0}' or one of its base types references '{1}' which requires unreferenced code.", + ((TypeOrigin)reflectionContext.MemberWithRequirements).GetDisplayName(), + method.GetDisplayName()); + _logger.LogWarning(message, 2113, reflectionContext.Source, MessageSubCategory.TrimAnalysis); + } + } + + if (_flowAnnotations.ShouldWarnWhenAccessedForReflection(method)) + { + WarnOnReflectionAccess(ref reflectionContext, method); + } + + RootingHelpers.TryGetDependenciesForReflectedMethod(ref _dependencies, _factory, method, reflectionContext.MemberWithRequirements.ToString()); + reflectionContext.RecordHandledPattern(); + } + + void MarkField(ref ReflectionPatternContext reflectionContext, FieldDesc field) + { + if (_flowAnnotations.ShouldWarnWhenAccessedForReflection(field)) + { + WarnOnReflectionAccess(ref reflectionContext, field); + } + + RootingHelpers.TryGetDependenciesForReflectedField(ref _dependencies, _factory, field, reflectionContext.MemberWithRequirements.ToString()); + reflectionContext.RecordHandledPattern(); + } + + void MarkProperty(ref ReflectionPatternContext reflectionContext, PropertyPseudoDesc property) + { + if (property.GetMethod != null) + MarkMethod(ref reflectionContext, property.GetMethod); + if (property.SetMethod != null) + MarkMethod(ref reflectionContext, property.SetMethod); + reflectionContext.RecordHandledPattern(); + } + + void MarkEvent(ref ReflectionPatternContext reflectionContext, EventPseudoDesc @event) + { + if (@event.AddMethod != null) + MarkMethod(ref reflectionContext, @event.AddMethod); + if (@event.RemoveMethod != null) + MarkMethod(ref reflectionContext, @event.RemoveMethod); + reflectionContext.RecordHandledPattern(); + } + + void MarkConstructorsOnType(ref ReflectionPatternContext reflectionContext, TypeDesc type, Func filter, BindingFlags? bindingFlags = null) + { + foreach (var ctor in type.GetConstructorsOnType(filter, bindingFlags)) + MarkMethod(ref reflectionContext, ctor); + } + + void MarkFieldsOnTypeHierarchy(ref ReflectionPatternContext reflectionContext, TypeDesc type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var field in type.GetFieldsOnTypeHierarchy(filter, bindingFlags)) + MarkField(ref reflectionContext, field); + } + + MetadataType[] MarkNestedTypesOnType(ref ReflectionPatternContext reflectionContext, TypeDesc type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + var result = new ArrayBuilder(); + + foreach (var nestedType in type.GetNestedTypesOnType(filter, bindingFlags)) + { + result.Add(nestedType); + MarkTypeForDynamicallyAccessedMembers(ref reflectionContext, nestedType, DynamicallyAccessedMemberTypes.All); + } + + return result.ToArray(); + } + + void MarkPropertiesOnTypeHierarchy(ref ReflectionPatternContext reflectionContext, TypeDesc type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var property in type.GetPropertiesOnTypeHierarchy(filter, bindingFlags)) + MarkProperty(ref reflectionContext, property); + } + + void MarkEventsOnTypeHierarchy(ref ReflectionPatternContext reflectionContext, TypeDesc type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var @event in type.GetEventsOnTypeHierarchy(filter, bindingFlags)) + MarkEvent(ref reflectionContext, @event); + } + + void ValidateGenericMethodInstantiation( + ref ReflectionPatternContext reflectionContext, + MethodDesc genericMethod, + ValueNode genericParametersArray, + MethodDesc reflectionMethod) + { + if (!genericMethod.HasInstantiation) + { + reflectionContext.RecordHandledPattern(); + return; + } + if (!AnalyzeGenericInstantiationTypeArray(genericParametersArray, ref reflectionContext, reflectionMethod, genericMethod.GetMethodDefinition().Instantiation)) + { + reflectionContext.RecordUnrecognizedPattern( + 2060, + string.Format(Resources.Strings.IL2060, DiagnosticUtilities.GetMethodSignatureDisplayName(reflectionMethod))); + } + else + { + reflectionContext.RecordHandledPattern(); + } + } + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes(BindingFlags? bindingFlags) => + (HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicNestedTypes : DynamicallyAccessedMemberTypes.None) | + (HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None) | + (BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None); + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(BindingFlags? bindingFlags) => + (HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicConstructors : DynamicallyAccessedMemberTypes.None) | + (HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None) | + (BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None); + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(BindingFlags? bindingFlags) => + (HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicMethods : DynamicallyAccessedMemberTypes.None) | + (HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None) | + (BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None); + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields(BindingFlags? bindingFlags) => + (HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicFields : DynamicallyAccessedMemberTypes.None) | + (HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None) | + (BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None); + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties(BindingFlags? bindingFlags) => + (HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicProperties : DynamicallyAccessedMemberTypes.None) | + (HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None) | + (BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None); + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents(BindingFlags? bindingFlags) => + (HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicEvents : DynamicallyAccessedMemberTypes.None) | + (HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None) | + (BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None); + + static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers(BindingFlags? bindingFlags) => + GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(bindingFlags) | + GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents(bindingFlags) | + GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields(bindingFlags) | + GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags) | + GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties(bindingFlags) | + GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes(bindingFlags); + + private static class Resources + { + public static class Strings + { + public const string IL2060 = "Call to '{0}' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic method."; + public const string IL2067 = "'{0}' argument does not satisfy {4} in call to '{1}'. The parameter '{2}' of method '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2068 = "'{0}' method return value does not satisfy {3} requirements. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2069 = "value stored in field '{0}' does not satisfy {3} requirements. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2070 = "'this' argument does not satisfy {3} in call to '{0}'. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2071 = "'{0}' generic argument does not satisfy {4} in '{1}'. The parameter '{2}' of method '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2072 = "'{0}' argument does not satisfy {3} in call to '{1}'. The return value of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2073 = "'{0}' method return value does not satisfy {2} requirements. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2074 = "value stored in field '{0}' does not satisfy {2} requirements. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2075 = "'this' argument does not satisfy {2} in call to '{0}'. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2076 = "'{0}' generic argument does not satisfy {3} in '{1}'. The return value of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2077 = "'{0}' argument does not satisfy {3} in call to '{1}'. The field '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2078 = "'{0}' method return value does not satisfy {2} requirements. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2079 = "value stored in field '{0}' does not satisfy {2} requirements. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2080 = "'this' argument does not satisfy {2} in call to '{0}'. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2081 = "'{0}' generic argument does not satisfy {3} in '{1}'. The field '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2082 = "'{0}' argument does not satisfy {3} in call to '{1}'. The implicit 'this' argument of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2083 = "'{0}' method return value does not satisfy {2} requirements. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2084 = "value stored in field '{0}' does not satisfy {2} requirements. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2085 = "'this' argument does not satisfy {2} in call to '{0}'. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2086 = "'{0}' generic argument does not satisfy {3} in '{1}'. The implicit 'this' argument of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2087 = "'{0}' argument does not satisfy {4} in call to '{1}'. The generic parameter '{2}' of '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2088 = "'{0}' method return value does not satisfy {3} requirements. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2089 = "value stored in field '{0}' does not satisfy {3} requirements. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2090 = "'this' argument does not satisfy {3} in call to '{0}'. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2091 = "'{0}' generic argument does not satisfy {4} in '{1}'. The generic parameter '{2}' of '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to."; + public const string IL2103 = "Value passed to the '{0}' parameter of method '{1}' cannot be statically determined as a property accessor."; + + // Error codes > 6000 are reserved for custom steps and illink doesn't claim ownership of them + + + public const string IL3050 = "Using member '{0}' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling."; + // IL3051 - mismatched RequiresDynamicCode on virtuals + // TODO: these are all unique to NativeAOT - mono/linker repo is not aware this error code is used. + // IL3052 - COM + // IL3053 - AOT analysis warnings + // IL3054 - Generic cycle + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionPatternContext.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionPatternContext.cs new file mode 100644 index 00000000000000..99d97784e78119 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionPatternContext.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using Internal.IL; +using Internal.TypeSystem; + +namespace ILCompiler.Dataflow +{ + /// + /// Helper struct to pass around context information about reflection pattern + /// as a single parameter (and have a way to extend this in the future if we need to easily). + /// Also implements a simple validation mechanism to check that the code does report patter recognition + /// results for all methods it works on. + /// The promise of the pattern recorder is that for a given reflection method, it will either not talk + /// about it ever, or it will always report recognized/unrecognized. + /// + struct ReflectionPatternContext : IDisposable + { + readonly Logger _logger; + readonly int _ilOffset; + readonly MethodIL _sourceIL; + +#if DEBUG + bool _patternAnalysisAttempted; + bool _patternReported; +#endif + + public TypeSystemEntity Source { get; private set; } + public Origin MemberWithRequirements { get; private set; } + public bool ReportingEnabled { get; private set; } + + public ReflectionPatternContext( + Logger logger, + bool reportingEnabled, + TypeSystemEntity source, + Origin memberWithRequirements) + { + _logger = logger; + ReportingEnabled = reportingEnabled; + Source = source; + MemberWithRequirements = memberWithRequirements; + _ilOffset = 0; + _sourceIL = null; + +#if DEBUG + _patternAnalysisAttempted = false; + _patternReported = false; +#endif + } + + public ReflectionPatternContext( + Logger logger, + bool reportingEnabled, + MethodIL source, + int offset, + Origin memberWithRequirements) + : this(logger, reportingEnabled, source.OwningMethod, memberWithRequirements) + { + _sourceIL = source; + _ilOffset = offset; + } + +#pragma warning disable CA1822 + [Conditional("DEBUG")] + public void AnalyzingPattern() + { +#if DEBUG + _patternAnalysisAttempted = true; +#endif + } + + [Conditional("DEBUG")] + public void RecordHandledPattern() + { +#if DEBUG + _patternReported = true; +#endif + } +#pragma warning restore CA1822 + + public void RecordRecognizedPattern(Action mark) + { +#if DEBUG + if (!_patternAnalysisAttempted) + throw new InvalidOperationException($"Internal error: To correctly report all patterns, when starting to analyze a pattern the AnalyzingPattern must be called first. {Source} -> {MemberWithRequirements}"); + + _patternReported = true; +#endif + + mark(); + } + + public void RecordUnrecognizedPattern(int messageCode, string message) + { +#if DEBUG + if (!_patternAnalysisAttempted) + throw new InvalidOperationException($"Internal error: To correctly report all patterns, when starting to analyze a pattern the AnalyzingPattern must be called first. {Source} -> {MemberWithRequirements}"); + + _patternReported = true; +#endif + + if (ReportingEnabled) + { + if (_sourceIL != null) + _logger.LogWarning(message, messageCode, _sourceIL, _ilOffset, MessageSubCategory.TrimAnalysis); + else + _logger.LogWarning(message, messageCode, Source, MessageSubCategory.TrimAnalysis); + } + } + + public void Dispose() + { +#if DEBUG + if (_patternAnalysisAttempted && !_patternReported) + throw new InvalidOperationException($"Internal error: A reflection pattern was analyzed, but no result was reported. {Source} -> {MemberWithRequirements}"); +#endif + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ScannerExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ScannerExtensions.cs new file mode 100644 index 00000000000000..bfb7999e2b47e5 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ScannerExtensions.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.IL; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.Dataflow +{ + internal static class ScannerExtensions + { + public static bool IsControlFlowInstruction(this ILOpcode opcode) + { + switch (opcode) + { + case ILOpcode.br_s: + case ILOpcode.br: + case ILOpcode.brfalse_s: + case ILOpcode.brtrue_s: + case ILOpcode.beq_s: + case ILOpcode.bge_s: + case ILOpcode.bgt_s: + case ILOpcode.ble_s: + case ILOpcode.blt_s: + case ILOpcode.bne_un_s: + case ILOpcode.bge_un_s: + case ILOpcode.bgt_un_s: + case ILOpcode.ble_un_s: + case ILOpcode.blt_un_s: + case ILOpcode.brfalse: + case ILOpcode.brtrue: + case ILOpcode.beq: + case ILOpcode.bge: + case ILOpcode.bgt: + case ILOpcode.ble: + case ILOpcode.blt: + case ILOpcode.bne_un: + case ILOpcode.bge_un: + case ILOpcode.bgt_un: + case ILOpcode.ble_un: + case ILOpcode.blt_un: + case ILOpcode.switch_: + case ILOpcode.leave: + case ILOpcode.leave_s: + case ILOpcode.endfilter: + case ILOpcode.endfinally: + case ILOpcode.throw_: + case ILOpcode.rethrow: + return true; + } + return false; + } + + public static HashSet ComputeBranchTargets(this MethodIL methodBody) + { + HashSet branchTargets = new HashSet(); + var reader = new ILReader(methodBody.GetILBytes()); + while (reader.HasNext) + { + ILOpcode opcode = reader.ReadILOpcode(); + if (opcode >= ILOpcode.br_s && opcode <= ILOpcode.blt_un) + { + branchTargets.Add(reader.ReadBranchDestination(opcode)); + } + else if (opcode == ILOpcode.switch_) + { + uint count = reader.ReadILUInt32(); + int jmpBase = reader.Offset + (int)(4 * count); + for (uint i = 0; i < count; i++) + { + branchTargets.Add((int)reader.ReadILUInt32() + jmpBase); + } + } + else + { + reader.Skip(opcode); + } + } + foreach (ILExceptionRegion einfo in methodBody.GetExceptionRegions()) + { + if (einfo.Kind == ILExceptionRegionKind.Filter) + { + branchTargets.Add(einfo.FilterOffset); + } + branchTargets.Add(einfo.HandlerOffset); + } + return branchTargets; + } + + public static bool IsByRefOrPointer(this TypeDesc type) + { + return type.IsByRef || type.IsPointer; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TypeExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TypeExtensions.cs new file mode 100644 index 00000000000000..89b9cd764a2e34 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TypeExtensions.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.Dataflow +{ + static class TypeExtensions + { + public static bool IsTypeOf(this TypeDesc type, string ns, string name) + { + return type is MetadataType mdType && mdType.Name == name && mdType.Namespace == ns; + } + + public static bool IsDeclaredOnType(this MethodDesc method, string ns, string name) + { + return method.OwningType.IsTypeOf(ns, name); + } + + public static bool HasParameterOfType(this MethodDesc method, int index, string ns, string name) + { + return index < method.Signature.Length && method.Signature[index].IsTypeOf(ns, name); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ValueNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ValueNode.cs new file mode 100644 index 00000000000000..e348eec7c2f64e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ValueNode.cs @@ -0,0 +1,1408 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; + +using Internal.IL; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.Dataflow +{ + public enum ValueNodeKind + { + Invalid, // in case the Kind field is not initialized properly + + Unknown, // unknown value, has StaticType from context + + Null, // known value + SystemType, // known value - TypeRepresented + RuntimeTypeHandle, // known value - TypeRepresented + KnownString, // known value - Contents + ConstInt, // known value - Int32 + AnnotatedString, // string with known annotation + + MethodParameter, // symbolic placeholder + MethodReturn, // symbolic placeholder + + RuntimeMethodHandle, // known value - MethodRepresented + SystemReflectionMethodBase, // known value - MethodRepresented + + RuntimeTypeHandleForGenericParameter, // symbolic placeholder for generic parameter + SystemTypeForGenericParameter, // symbolic placeholder for generic parameter + + MergePoint, // structural, multiplexer - Values + GetTypeFromString, // structural, could be known value - KnownString + Array, // structural, could be known value - Array + + LoadField, // structural, could be known value - InstanceValue + } + + /// + /// A ValueNode represents a value in the IL dataflow analysis. It may not contain complete information as it is a + /// best-effort representation. Additionally, as the analysis is linear and does not account for control flow, any + /// given ValueNode may represent multiple values simultaneously. (This occurs, for example, at control flow join + /// points when both paths yield values on the IL stack or in a local.) + /// + public abstract class ValueNode : IEquatable + { + public ValueNode() + { +#if false // Helpful for debugging a cycle that has inadvertently crept into the graph + if (this.DetectCycle(new HashSet())) + { + throw new Exception("Found a cycle"); + } +#endif + } + + /// + /// The 'kind' of value node -- this represents the most-derived type and allows us to switch over and do + /// equality checks without the cost of casting. Intermediate non-leaf types in the ValueNode hierarchy should + /// be abstract. + /// + public ValueNodeKind Kind { get; protected set; } + + /// + /// The IL type of the value, represented as closely as possible, but not always exact. It can be null, for + /// example, when the analysis is imprecise or operating on malformed IL. + /// + public TypeDesc StaticType { get; protected set; } + + /// + /// Allows the enumeration of the direct children of this node. The ChildCollection struct returned here + /// supports 'foreach' without allocation. + /// + public ChildCollection Children { get { return new ChildCollection(this); } } + + /// + /// This property allows you to enumerate all 'unique values' represented by a given ValueNode. The basic idea + /// is that there will be no MergePointValues in the returned ValueNodes and all structural operations will be + /// applied so that each 'unique value' can be considered on its own without regard to the structure that led to + /// it. + /// + public UniqueValueCollection UniqueValuesInternal + { + get + { + return new UniqueValueCollection(this); + } + } + + /// + /// This protected method is how nodes implement the UniqueValues property. It is protected because it returns + /// an IEnumerable and we want to avoid allocating an enumerator for the exceedingly common case of there being + /// only one value in the enumeration. The UniqueValueCollection returned by the UniqueValues property handles + /// this detail. + /// + protected abstract IEnumerable EvaluateUniqueValues(); + + /// + /// RepresentsExactlyOneValue is used by the UniqueValues property to allow us to bypass allocating an + /// enumerator to return just one value. If a node returns 'true' from RepresentsExactlyOneValue, it must also + /// return that one value from GetSingleUniqueValue. If it always returns 'false', it doesn't need to implement + /// GetSingleUniqueValue. + /// + protected virtual bool RepresentsExactlyOneValue { get { return false; } } + + /// + /// GetSingleUniqueValue is called if, and only if, RepresentsExactlyOneValue returns true. It allows us to + /// bypass the allocation of an enumerator for the common case of returning exactly one value. + /// + protected virtual ValueNode GetSingleUniqueValue() + { + // Not implemented because RepresentsExactlyOneValue returns false and, therefore, this method should be + // unreachable. + throw new NotImplementedException(); + } + + protected abstract int NumChildren { get; } + protected abstract ValueNode ChildAt(int index); + + public virtual bool Equals(ValueNode other) + { + return other != null && this.Kind == other.Kind && this.StaticType == other.StaticType; + } + + public abstract override int GetHashCode(); + + /// + /// Each node type must implement this to stringize itself. The expectation is that it is implemented using + /// ValueNodeDump.ValueNodeToString(), passing any non-ValueNode properties of interest (e.g. + /// SystemTypeValue.TypeRepresented). Properties that are invariant on a particular node type + /// should be omitted for clarity. + /// + protected abstract string NodeToString(); + + public override string ToString() + { + return NodeToString(); + } + + public override bool Equals(object other) + { + if (!(other is ValueNode)) + return false; + + return this.Equals((ValueNode)other); + } + + #region Specialized Collection Nested Types + /// + /// ChildCollection struct is used to wrap the operations on a node involving its children. In particular, the + /// struct implements a GetEnumerator method that is used to allow "foreach (ValueNode node in myNode.Children)" + /// without heap allocations. + /// + public struct ChildCollection : IEnumerable + { + /// + /// Enumerator for children of a ValueNode. Allows foreach(var child in node.Children) to work without + /// allocating a heap-based enumerator. + /// + public struct Enumerator : IEnumerator + { + int _index; + readonly ValueNode _parent; + + public Enumerator(ValueNode parent) + { + _parent = parent; + _index = -1; + } + + public ValueNode Current { get { return _parent?.ChildAt(_index); } } + + object System.Collections.IEnumerator.Current { get { return Current; } } + + public bool MoveNext() + { + _index++; + return (_parent != null) ? (_index < _parent.NumChildren) : false; + } + + public void Reset() + { + _index = -1; + } + + public void Dispose() + { + } + } + + readonly ValueNode _parentNode; + + public ChildCollection(ValueNode parentNode) { _parentNode = parentNode; } + + // Used by C# 'foreach', when strongly typed, to avoid allocation. + public Enumerator GetEnumerator() + { + return new Enumerator(_parentNode); + } + + IEnumerator IEnumerable.GetEnumerator() + { + // note the boxing! + return new Enumerator(_parentNode); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + // note the boxing! + return new Enumerator(_parentNode); + } + + public int Count { get { return (_parentNode != null) ? _parentNode.NumChildren : 0; } } + } + + /// + /// UniqueValueCollection is used to wrap calls to ValueNode.EvaluateUniqueValues. If a ValueNode represents + /// only one value, then foreach(ValueNode value in node.UniqueValues) will not allocate a heap-based enumerator. + /// + /// This is implented by having each ValueNode tell us whether or not it represents exactly one value or not. + /// If it does, we fetch it with ValueNode.GetSingleUniqueValue(), otherwise, we fall back to the usual heap- + /// based IEnumerable returned by ValueNode.EvaluateUniqueValues. + /// + public struct UniqueValueCollection : IEnumerable + { + readonly IEnumerable _multiValueEnumerable; + readonly ValueNode _treeNode; + + public UniqueValueCollection(ValueNode node) + { + if (node.RepresentsExactlyOneValue) + { + _multiValueEnumerable = null; + _treeNode = node; + } + else + { + _multiValueEnumerable = node.EvaluateUniqueValues(); + _treeNode = null; + } + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_treeNode, _multiValueEnumerable); + } + + IEnumerator IEnumerable.GetEnumerator() + { + if (_multiValueEnumerable != null) + { + return _multiValueEnumerable.GetEnumerator(); + } + + // note the boxing! + return GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + if (_multiValueEnumerable != null) + { + return _multiValueEnumerable.GetEnumerator(); + } + + // note the boxing! + return GetEnumerator(); + } + + + public struct Enumerator : IEnumerator + { + readonly IEnumerator _multiValueEnumerator; + readonly ValueNode _singleValueNode; + int _index; + + public Enumerator(ValueNode treeNode, IEnumerable mulitValueEnumerable) + { + _singleValueNode = treeNode?.GetSingleUniqueValue(); + _multiValueEnumerator = mulitValueEnumerable?.GetEnumerator(); + _index = -1; + } + + public void Reset() + { + if (_multiValueEnumerator != null) + { + _multiValueEnumerator.Reset(); + return; + } + + _index = -1; + } + + public bool MoveNext() + { + if (_multiValueEnumerator != null) + return _multiValueEnumerator.MoveNext(); + + _index++; + return _index == 0; + } + + public ValueNode Current + { + get + { + if (_multiValueEnumerator != null) + return _multiValueEnumerator.Current; + + if (_index == 0) + return _singleValueNode; + + throw new InvalidOperationException(); + } + } + + object System.Collections.IEnumerator.Current { get { return Current; } } + + public void Dispose() + { + } + } + } + #endregion + } + + /// + /// LeafValueNode represents a 'leaf' in the expression tree. In other words, the node has no ValueNode children. + /// It *may* still have non-ValueNode 'properties' that are interesting. This class serves, primarily, as a way to + /// collect up the very common implmentation of NumChildren/ChildAt for leaf nodes and the "represents exactly one + /// value" optimization. These things aren't on the ValueNode base class because, otherwise, new node types + /// deriving from ValueNode may 'forget' to implement these things. So this class allows them to remain abstract in + /// ValueNode while still having a common implementation for all the leaf nodes. + /// + public abstract class LeafValueNode : ValueNode + { + protected override int NumChildren { get { return 0; } } + protected override ValueNode ChildAt(int index) { throw new InvalidOperationException(); } + + protected override bool RepresentsExactlyOneValue { get { return true; } } + + protected override ValueNode GetSingleUniqueValue() { return this; } + + + protected override IEnumerable EvaluateUniqueValues() + { + // Leaf values should not represent more than one value. This method should be unreachable as long as + // RepresentsExactlyOneValue returns true. + throw new NotImplementedException(); + } + } + + // These are extension methods because we want to allow the use of them on null 'this' pointers. + internal static class ValueNodeExtensions + { + /// + /// Returns true if a ValueNode graph contains a cycle + /// + /// Node to evaluate + /// Set of nodes previously seen on the current arc. Callers may pass a non-empty set + /// to test whether adding that set to this node would create a cycle. Contents will be modified by the walk + /// and should not be used by the caller after returning + /// Optional. The set of all nodes encountered during a walk after DetectCycle returns + /// + public static bool DetectCycle(this ValueNode node, HashSet seenNodes, HashSet allNodesSeen) + { + if (node == null) + return false; + + if (seenNodes.Contains(node)) + return true; + + seenNodes.Add(node); + + if (allNodesSeen != null) + { + allNodesSeen.Add(node); + } + + bool foundCycle = false; + switch (node.Kind) + { + // + // Leaf nodes + // + case ValueNodeKind.Unknown: + case ValueNodeKind.Null: + case ValueNodeKind.SystemType: + case ValueNodeKind.RuntimeTypeHandle: + case ValueNodeKind.KnownString: + case ValueNodeKind.AnnotatedString: + case ValueNodeKind.ConstInt: + case ValueNodeKind.MethodParameter: + case ValueNodeKind.MethodReturn: + case ValueNodeKind.SystemTypeForGenericParameter: + case ValueNodeKind.RuntimeTypeHandleForGenericParameter: + case ValueNodeKind.SystemReflectionMethodBase: + case ValueNodeKind.RuntimeMethodHandle: + case ValueNodeKind.LoadField: + break; + + // + // Nodes with children + // + case ValueNodeKind.MergePoint: + foreach (ValueNode val in ((MergePointValue)node).Values) + { + if (val.DetectCycle(seenNodes, allNodesSeen)) + { + foundCycle = true; + } + } + break; + + case ValueNodeKind.GetTypeFromString: + GetTypeFromStringValue gtfsv = (GetTypeFromStringValue)node; + foundCycle = gtfsv.AssemblyIdentity.DetectCycle(seenNodes, allNodesSeen); + foundCycle |= gtfsv.NameString.DetectCycle(seenNodes, allNodesSeen); + break; + + case ValueNodeKind.Array: + ArrayValue av = (ArrayValue)node; + foundCycle = av.Size.DetectCycle(seenNodes, allNodesSeen); + foreach (ValueBasicBlockPair pair in av.IndexValues.Values) + { + foundCycle |= pair.Value.DetectCycle(seenNodes, allNodesSeen); + } + break; + + default: + throw new Exception(String.Format("Unknown node kind: {0}", node.Kind)); + } + seenNodes.Remove(node); + + return foundCycle; + } + + public static ValueNode.UniqueValueCollection UniqueValues(this ValueNode node) + { + if (node == null) + return new ValueNode.UniqueValueCollection(UnknownValue.Instance); + + return node.UniqueValuesInternal; + } + + public static int? AsConstInt(this ValueNode node) + { + if (node is ConstIntValue constInt) + return constInt.Value; + return null; + } + } + + internal static class ValueNodeDump + { + internal static string ValueNodeToString(ValueNode node, params object[] args) + { + if (node == null) + return ""; + + StringBuilder sb = new StringBuilder(); + sb.Append(node.Kind.ToString()); + sb.Append("("); + if (args != null) + { + for (int i = 0; i < args.Length; i++) + { + if (i > 0) + sb.Append(","); + sb.Append(args[i] == null ? "" : args[i].ToString()); + } + } + sb.Append(")"); + return sb.ToString(); + } + + static string GetIndent(int level) + { + StringBuilder sb = new StringBuilder(level * 2); + for (int i = 0; i < level; i++) + sb.Append(" "); + return sb.ToString(); + } + + public static void DumpTree(this ValueNode node, System.IO.TextWriter writer = null, int indentLevel = 0) + { + if (writer == null) + writer = Console.Out; + + writer.Write(GetIndent(indentLevel)); + if (node == null) + { + writer.WriteLine(""); + return; + } + + writer.WriteLine(node); + foreach (ValueNode child in node.Children) + { + child.DumpTree(writer, indentLevel + 1); + } + } + } + + /// + /// Represents an unknown value. + /// + class UnknownValue : LeafValueNode + { + private UnknownValue() + { + Kind = ValueNodeKind.Unknown; + StaticType = null; + } + + public static UnknownValue Instance { get; } = new UnknownValue(); + + public override bool Equals(ValueNode other) + { + return base.Equals(other); + } + + public override int GetHashCode() + { + // All instances of UnknownValue are equivalent, so they all hash to the same hashcode. This one was + // chosen for no particular reason at all. + return 0x98052; + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this); + } + } + + class NullValue : LeafValueNode + { + private NullValue() + { + Kind = ValueNodeKind.Null; + StaticType = null; + } + + public override bool Equals(ValueNode other) + { + return base.Equals(other); + } + + public static NullValue Instance { get; } = new NullValue(); + + public override int GetHashCode() + { + // All instances of NullValue are equivalent, so they all hash to the same hashcode. This one was + // chosen for no particular reason at all. + return 0x90210; + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this); + } + } + + /// + /// This is a known System.Type value. TypeRepresented is the 'value' of the System.Type. + /// + class SystemTypeValue : LeafValueNode + { + public SystemTypeValue(TypeDesc typeRepresented) + { + Kind = ValueNodeKind.SystemType; + + // Should be System.Type - but we don't have any use case where tracking it like that would matter + StaticType = null; + + TypeRepresented = typeRepresented; + } + + public TypeDesc TypeRepresented { get; private set; } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + return Equals(this.TypeRepresented, ((SystemTypeValue)other).TypeRepresented); + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, TypeRepresented); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, TypeRepresented); + } + } + + /// + /// This is the System.RuntimeTypeHandle equivalent to a node. + /// + class RuntimeTypeHandleValue : LeafValueNode + { + public RuntimeTypeHandleValue(TypeDesc typeRepresented) + { + Kind = ValueNodeKind.RuntimeTypeHandle; + + // Should be System.RuntimeTypeHandle, but we don't have a use case for it like that + StaticType = null; + + TypeRepresented = typeRepresented; + } + + public TypeDesc TypeRepresented { get; } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + return Equals(this.TypeRepresented, ((RuntimeTypeHandleValue)other).TypeRepresented); + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, TypeRepresented); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, TypeRepresented); + } + } + + /// + /// This is a System.Type value which represents generic parameter (basically result of typeof(T)) + /// Its actual type is unknown, but it can have annotations. + /// + class SystemTypeForGenericParameterValue : LeafValueWithDynamicallyAccessedMemberNode + { + public SystemTypeForGenericParameterValue(GenericParameterDesc genericParameter, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + Kind = ValueNodeKind.SystemTypeForGenericParameter; + + // Should be System.Type, but we don't have a use case for it + StaticType = null; + + GenericParameter = genericParameter; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + SourceContext = new GenericParameterOrigin(genericParameter); + } + + public GenericParameterDesc GenericParameter { get; } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + var otherValue = (SystemTypeForGenericParameterValue)other; + return this.GenericParameter == otherValue.GenericParameter && this.DynamicallyAccessedMemberTypes == otherValue.DynamicallyAccessedMemberTypes; + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, GenericParameter, DynamicallyAccessedMemberTypes); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, GenericParameter, DynamicallyAccessedMemberTypes); + } + } + + /// + /// This is the System.RuntimeTypeHandle equivalent to a node. + /// + class RuntimeTypeHandleForGenericParameterValue : LeafValueNode + { + public RuntimeTypeHandleForGenericParameterValue(GenericParameterDesc genericParameter) + { + Kind = ValueNodeKind.RuntimeTypeHandleForGenericParameter; + + // Should be System.RuntimeTypeHandle, but we don't have a use case for it + StaticType = null; + + GenericParameter = genericParameter; + } + + public GenericParameterDesc GenericParameter { get; } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + return Equals(this.GenericParameter, ((RuntimeTypeHandleForGenericParameterValue)other).GenericParameter); + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, GenericParameter); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, GenericParameter); + } + } + + /// + /// This is the System.RuntimeMethodHandle equivalent to a node. + /// + class RuntimeMethodHandleValue : LeafValueNode + { + public RuntimeMethodHandleValue(MethodDesc methodRepresented) + { + Kind = ValueNodeKind.RuntimeMethodHandle; + + // Should be System.RuntimeMethodHandle, but we don't have a use case for it + StaticType = null; + + MethodRepresented = methodRepresented; + } + + public MethodDesc MethodRepresented { get; } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + return Equals(this.MethodRepresented, ((RuntimeMethodHandleValue)other).MethodRepresented); + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, MethodRepresented); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, MethodRepresented); + } + } + + /// + /// This is a known System.Reflection.MethodBase value. MethodRepresented is the 'value' of the MethodBase. + /// + class SystemReflectionMethodBaseValue : LeafValueNode + { + public SystemReflectionMethodBaseValue(MethodDesc methodRepresented) + { + Kind = ValueNodeKind.SystemReflectionMethodBase; + + // Should be System.Reflection.MethodBase, but we don't have a use case for it + StaticType = null; + + MethodRepresented = methodRepresented; + } + + public MethodDesc MethodRepresented { get; private set; } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + return Equals(this.MethodRepresented, ((SystemReflectionMethodBaseValue)other).MethodRepresented); + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, MethodRepresented); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, MethodRepresented); + } + } + + /// + /// A known string - such as the result of a ldstr. + /// + class KnownStringValue : LeafValueNode + { + public KnownStringValue(string contents) + { + Kind = ValueNodeKind.KnownString; + + // Should be System.String, but we don't have a use case for it + StaticType = null; + + Contents = contents; + } + + public string Contents { get; private set; } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + return this.Contents == ((KnownStringValue)other).Contents; + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, Contents); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, "\"" + Contents + "\""); + } + } + + /// + /// Base class for all nodes which can have dynamically accessed member annotation. + /// + abstract class LeafValueWithDynamicallyAccessedMemberNode : LeafValueNode + { + public Origin SourceContext { get; protected set; } + + /// + /// The bitfield of dynamically accessed member types the node guarantees + /// + public DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; protected set; } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + var otherValue = (LeafValueWithDynamicallyAccessedMemberNode)other; + return SourceContext == otherValue.SourceContext + && DynamicallyAccessedMemberTypes == otherValue.DynamicallyAccessedMemberTypes; + } + } + + /// + /// A value that came from a method parameter - such as the result of a ldarg. + /// + class MethodParameterValue : LeafValueWithDynamicallyAccessedMemberNode + { + public MethodParameterValue(MethodDesc method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + Kind = ValueNodeKind.MethodParameter; + StaticType = !method.Signature.IsStatic + ? (parameterIndex == 0 + ? method.OwningType + : method.Signature[parameterIndex - 1]) + : method.Signature[parameterIndex]; + ParameterIndex = parameterIndex; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + SourceContext = !method.Signature.IsStatic && parameterIndex == 0 ? + new MethodOrigin(method) : + new ParameterOrigin(method, parameterIndex); + } + + public int ParameterIndex { get; } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + var otherValue = (MethodParameterValue)other; + return this.ParameterIndex == otherValue.ParameterIndex; + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, ParameterIndex, DynamicallyAccessedMemberTypes); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, ParameterIndex, DynamicallyAccessedMemberTypes); + } + } + + /// + /// String with a known annotation. + /// + class AnnotatedStringValue : LeafValueWithDynamicallyAccessedMemberNode + { + public AnnotatedStringValue(Origin sourceContext, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + Kind = ValueNodeKind.AnnotatedString; + + // Should be System.String, but we don't have a use case for it + StaticType = null; + + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + SourceContext = sourceContext; + } + + public override bool Equals(ValueNode other) + { + return base.Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, DynamicallyAccessedMemberTypes); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, DynamicallyAccessedMemberTypes); + } + } + + /// + /// Return value from a method + /// + class MethodReturnValue : LeafValueWithDynamicallyAccessedMemberNode + { + public MethodReturnValue(MethodDesc method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + Kind = ValueNodeKind.MethodReturn; + StaticType = method.Signature.ReturnType; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + SourceContext = new MethodReturnOrigin(method); + } + + public override bool Equals(ValueNode other) + { + return base.Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, DynamicallyAccessedMemberTypes); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, DynamicallyAccessedMemberTypes); + } + } + + /// + /// A merge point commonly occurs due to control flow in a method body. It represents a set of values + /// from different paths through the method. It is the reason for EvaluateUniqueValues, which essentially + /// provides an enumeration over all the concrete values represented by a given ValueNode after 'erasing' + /// the merge point nodes. + /// + class MergePointValue : ValueNode + { + private MergePointValue(ValueNode one, ValueNode two) + { + Kind = ValueNodeKind.MergePoint; + StaticType = null; + m_values = new ValueNodeHashSet(); + + if (one.Kind == ValueNodeKind.MergePoint) + { + MergePointValue mpvOne = (MergePointValue)one; + foreach (ValueNode value in mpvOne.Values) + m_values.Add(value); + } + else + m_values.Add(one); + + if (two.Kind == ValueNodeKind.MergePoint) + { + MergePointValue mpvTwo = (MergePointValue)two; + foreach (ValueNode value in mpvTwo.Values) + m_values.Add(value); + } + else + m_values.Add(two); + } + + public MergePointValue() + { + Kind = ValueNodeKind.MergePoint; + m_values = new ValueNodeHashSet(); + } + + public void AddValue(ValueNode node) + { + // we are mutating our state, so we must invalidate any cached knowledge + //InvalidateIsOpen (); + + if (node.Kind == ValueNodeKind.MergePoint) + { + foreach (ValueNode value in ((MergePointValue)node).Values) + m_values.Add(value); + } + else + m_values.Add(node); + +#if false + if (this.DetectCycle(new HashSet())) + { + throw new Exception("Found a cycle"); + } +#endif + } + + readonly ValueNodeHashSet m_values; + + public ValueNodeHashSet Values { get { return m_values; } } + + protected override int NumChildren { get { return Values.Count; } } + protected override ValueNode ChildAt(int index) + { + if (index < NumChildren) + return Values.ElementAt(index); + throw new InvalidOperationException(); + } + + public static ValueNode MergeValues(ValueNode one, ValueNode two) + { + if (one == null) + return two; + else if (two == null) + return one; + else if (one.Equals(two)) + return one; + else + return new MergePointValue(one, two); + } + + protected override IEnumerable EvaluateUniqueValues() + { + foreach (ValueNode value in Values) + { + foreach (ValueNode uniqueValue in value.UniqueValuesInternal) + { + yield return uniqueValue; + } + } + } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + MergePointValue otherMpv = (MergePointValue)other; + if (this.Values.Count != otherMpv.Values.Count) + return false; + + foreach (ValueNode value in this.Values) + { + if (!otherMpv.Values.Contains(value)) + return false; + } + return true; + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, Values); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this); + } + } + + delegate TypeDesc TypeResolver(string assemblyString, string typeString); + + /// + /// The result of a Type.GetType. + /// AssemblyIdentity is the scope in which to resolve if the type name string is not assembly-qualified. + /// + +#pragma warning disable CA1812 // GetTypeFromStringValue is never instantiated + class GetTypeFromStringValue : ValueNode + { + private readonly TypeResolver _resolver; + + public GetTypeFromStringValue(TypeResolver resolver, ValueNode assemblyIdentity, ValueNode nameString) + { + _resolver = resolver; + Kind = ValueNodeKind.GetTypeFromString; + + // Should be System.Type, but we don't have a use case for it + StaticType = null; + + AssemblyIdentity = assemblyIdentity; + NameString = nameString; + } + + public ValueNode AssemblyIdentity { get; private set; } + + public ValueNode NameString { get; private set; } + + protected override int NumChildren { get { return 2; } } + protected override ValueNode ChildAt(int index) + { + if (index == 0) return AssemblyIdentity; + if (index == 1) return NameString; + throw new InvalidOperationException(); + } + + protected override IEnumerable EvaluateUniqueValues() + { + HashSet names = null; + + foreach (ValueNode nameStringValue in NameString.UniqueValuesInternal) + { + if (nameStringValue.Kind == ValueNodeKind.KnownString) + { + if (names == null) + { + names = new HashSet(); + } + + string typeName = ((KnownStringValue)nameStringValue).Contents; + names.Add(typeName); + } + } + + bool foundAtLeastOne = false; + + if (names != null) + { + foreach (ValueNode assemblyValue in AssemblyIdentity.UniqueValuesInternal) + { + if (assemblyValue.Kind == ValueNodeKind.KnownString) + { + string assemblyName = ((KnownStringValue)assemblyValue).Contents; + + foreach (string name in names) + { + TypeDesc typeDefinition = _resolver(assemblyName, name); + if (typeDefinition != null) + { + foundAtLeastOne = true; + yield return new SystemTypeValue(typeDefinition); + } + } + } + } + } + + if (!foundAtLeastOne) + yield return UnknownValue.Instance; + } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + GetTypeFromStringValue otherGtfs = (GetTypeFromStringValue)other; + + return this.AssemblyIdentity.Equals(otherGtfs.AssemblyIdentity) && + this.NameString.Equals(otherGtfs.NameString); + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, AssemblyIdentity, NameString); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, NameString); + } + } + + /// + /// A representation of a ldfld. Note that we don't have a representation of objects containing fields + /// so there isn't much that can be done with this node type yet. + /// + class LoadFieldValue : LeafValueWithDynamicallyAccessedMemberNode + { + public LoadFieldValue(FieldDesc fieldToLoad, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + Kind = ValueNodeKind.LoadField; + StaticType = fieldToLoad.FieldType; + Field = fieldToLoad; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + SourceContext = new FieldOrigin(fieldToLoad); + } + + public FieldDesc Field { get; private set; } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + LoadFieldValue otherLfv = (LoadFieldValue)other; + return Equals(this.Field, otherLfv.Field); + } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, Field, DynamicallyAccessedMemberTypes); + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, Field, DynamicallyAccessedMemberTypes); + } + } + + /// + /// Represents a ldc on an int32. + /// + class ConstIntValue : LeafValueNode + { + public ConstIntValue(int value) + { + Kind = ValueNodeKind.ConstInt; + + // Should be System.Int32, but we don't have a usecase for it right now + StaticType = null; + + Value = value; + } + + public int Value { get; private set; } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, Value); + } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + ConstIntValue otherCiv = (ConstIntValue)other; + return Value == otherCiv.Value; + } + + protected override string NodeToString() + { + return ValueNodeDump.ValueNodeToString(this, Value); + } + } + + class ArrayValue : ValueNode + { + protected override int NumChildren => 1 + IndexValues.Count; + + /// + /// Constructs an array value of the given size + /// + public ArrayValue(ValueNode size, TypeDesc elementType) + { + Kind = ValueNodeKind.Array; + // Should be System.Array (or similar), but we don't have a use case for it + StaticType = null; + Size = size ?? UnknownValue.Instance; + ElementType = elementType; + IndexValues = new Dictionary(); + } + private ArrayValue(ValueNode size, TypeDesc elementType, Dictionary indexValues) + : this(size, elementType) + { + IndexValues = indexValues; + } + + public ValueNode Size { get; } + public TypeDesc ElementType { get; } + public Dictionary IndexValues { get; } + + public override int GetHashCode() + { + return HashCode.Combine(Kind, Size); + } + + public override bool Equals(ValueNode other) + { + if (!base.Equals(other)) + return false; + + ArrayValue otherArr = (ArrayValue)other; + bool equals = Size.Equals(otherArr.Size); + equals &= IndexValues.Count == otherArr.IndexValues.Count; + if (!equals) + return false; + // If both sets T and O are the same size and "T intersect O" is empty, then T == O. + HashSet> thisValueSet = new(IndexValues); + HashSet> otherValueSet = new(otherArr.IndexValues); + thisValueSet.ExceptWith(otherValueSet); + return thisValueSet.Count == 0; + } + + protected override string NodeToString() + { + // TODO: Use StringBuilder and remove Linq usage. + return $"(Array Size:{ValueNodeDump.ValueNodeToString(this, Size)}, Values:({string.Join(',', IndexValues.Select(v => $"({v.Key},{ValueNodeDump.ValueNodeToString(v.Value.Value)})"))})"; + } + + protected override IEnumerable EvaluateUniqueValues() + { + foreach (var sizeConst in Size.UniqueValuesInternal) + yield return new ArrayValue(sizeConst, ElementType, IndexValues); + } + + protected override ValueNode ChildAt(int index) + { + if (index == 0) return Size; + if (index - 1 <= IndexValues.Count) + return IndexValues.Values.ElementAt(index - 1).Value; + + throw new InvalidOperationException(); + } + } + + #region ValueNode Collections + public class ValueNodeList : List + { + public ValueNodeList() + { + } + + public ValueNodeList(int capacity) + : base(capacity) + { + } + + public ValueNodeList(List other) + : base(other) + { + } + + public override int GetHashCode() + { + return HashUtils.CalcHashCodeEnumerable(this); + } + + public override bool Equals(object other) + { + if (!(other is ValueNodeList otherList)) + return false; + + if (otherList.Count != Count) + return false; + + for (int i = 0; i < Count; i++) + { + if (!(otherList[i]?.Equals(this[i]) ?? (this[i] is null))) + return false; + } + return true; + } + } + + class ValueNodeHashSet : HashSet + { + public override int GetHashCode() + { + return HashUtils.CalcHashCodeEnumerable(this); + } + + public override bool Equals(object other) + { + if (!(other is ValueNodeHashSet otherSet)) + return false; + + if (otherSet.Count != Count) + return false; + + IEnumerator thisEnumerator = this.GetEnumerator(); + IEnumerator otherEnumerator = otherSet.GetEnumerator(); + + for (int i = 0; i < Count; i++) + { + thisEnumerator.MoveNext(); + otherEnumerator.MoveNext(); + if (!thisEnumerator.Current.Equals(otherEnumerator.Current)) + return false; + } + return true; + } + } + #endregion + + static class HashUtils + { + public static int CalcHashCodeEnumerable(IEnumerable list) where T : class + { + HashCode hashCode = new HashCode(); + foreach (var item in list) + hashCode.Add(item); + return hashCode.ToHashCode(); + } + } + + public struct ValueBasicBlockPair + { + public ValueNode Value; + public int BasicBlockIndex; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DebugInformationProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DebugInformationProvider.cs new file mode 100644 index 00000000000000..2f2064da1d74cf --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DebugInformationProvider.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.IL; + +namespace ILCompiler +{ + /// + /// Provides debug information by delegating to the . + /// + public class DebugInformationProvider + { + public virtual MethodDebugInformation GetDebugInfo(MethodIL methodIL) + { + return methodIL.GetDebugInfo(); + } + } + + /// + /// Provides empty debug information. + /// + public sealed class NullDebugInformationProvider : DebugInformationProvider + { + public override MethodDebugInformation GetDebugInfo(MethodIL methodIL) + { + return MethodDebugInformation.None; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DelegateCreationInfo.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DelegateCreationInfo.cs new file mode 100644 index 00000000000000..0195f82d6c3692 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DelegateCreationInfo.cs @@ -0,0 +1,328 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.IL; +using Internal.TypeSystem; +using Internal.Text; +using ILCompiler.DependencyAnalysis; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// Captures information required to generate a ReadyToRun helper to create a delegate type instance + /// pointing to a specific target method. + /// + public sealed class DelegateCreationInfo + { + private enum TargetKind + { + CanonicalEntrypoint, + ExactCallableAddress, + InterfaceDispatch, + VTableLookup, + MethodHandle, + } + + private TargetKind _targetKind; + + /// + /// Gets the node corresponding to the method that initializes the delegate. + /// + public IMethodNode Constructor + { + get; + } + + public MethodDesc TargetMethod + { + get; + } + + private bool TargetMethodIsUnboxingThunk + { + get + { + return TargetMethod.OwningType.IsValueType && !TargetMethod.Signature.IsStatic; + } + } + + public bool TargetNeedsVTableLookup => _targetKind == TargetKind.VTableLookup; + + public bool NeedsVirtualMethodUseTracking + { + get + { + return _targetKind == TargetKind.VTableLookup || _targetKind == TargetKind.InterfaceDispatch; + } + } + + public bool NeedsRuntimeLookup + { + get + { + switch (_targetKind) + { + case TargetKind.VTableLookup: + return false; + + case TargetKind.CanonicalEntrypoint: + case TargetKind.ExactCallableAddress: + case TargetKind.InterfaceDispatch: + case TargetKind.MethodHandle: + return TargetMethod.IsRuntimeDeterminedExactMethod; + + default: + Debug.Assert(false); + return false; + } + } + } + + // None of the data structures that support shared generics have been ported to the JIT + // codebase which makes this a huge PITA. Not including the method for JIT since nobody + // uses it in that mode anyway. +#if !SUPPORT_JIT + public GenericLookupResult GetLookupKind(NodeFactory factory) + { + Debug.Assert(NeedsRuntimeLookup); + switch (_targetKind) + { + case TargetKind.ExactCallableAddress: + return factory.GenericLookup.MethodEntry(TargetMethod, TargetMethodIsUnboxingThunk); + + case TargetKind.InterfaceDispatch: + return factory.GenericLookup.VirtualDispatchCell(TargetMethod); + + case TargetKind.MethodHandle: + return factory.GenericLookup.MethodHandle(TargetMethod); + + default: + Debug.Assert(false); + return null; + } + } +#endif + + /// + /// Gets the node representing the target method of the delegate if no runtime lookup is needed. + /// + public ISymbolNode GetTargetNode(NodeFactory factory) + { + Debug.Assert(!NeedsRuntimeLookup); + switch (_targetKind) + { + case TargetKind.CanonicalEntrypoint: + return factory.CanonicalEntrypoint(TargetMethod, TargetMethodIsUnboxingThunk); + + case TargetKind.ExactCallableAddress: + return factory.ExactCallableAddress(TargetMethod, TargetMethodIsUnboxingThunk); + + case TargetKind.InterfaceDispatch: + return factory.InterfaceDispatchCell(TargetMethod); + + case TargetKind.MethodHandle: + return factory.RuntimeMethodHandle(TargetMethod); + + case TargetKind.VTableLookup: + Debug.Fail("Need to do runtime lookup"); + return null; + + default: + Debug.Assert(false); + return null; + } + } + + /// + /// Gets an optional node passed as an additional argument to the constructor. + /// + public IMethodNode Thunk + { + get; + } + + private DelegateCreationInfo(IMethodNode constructor, MethodDesc targetMethod, TargetKind targetKind, IMethodNode thunk = null) + { + Debug.Assert(targetKind != TargetKind.VTableLookup + || MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod) == targetMethod); + Constructor = constructor; + TargetMethod = targetMethod; + _targetKind = targetKind; + Thunk = thunk; + } + + /// + /// Constructs a new instance of set up to construct a delegate of type + /// '' pointing to ''. + /// + public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targetMethod, NodeFactory factory, bool followVirtualDispatch) + { + TypeSystemContext context = delegateType.Context; + DefType systemDelegate = context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType; + + int paramCountTargetMethod = targetMethod.Signature.Length; + if (!targetMethod.Signature.IsStatic) + { + paramCountTargetMethod++; + } + + DelegateInfo delegateInfo = context.GetDelegateInfo(delegateType.GetTypeDefinition()); + int paramCountDelegateClosed = delegateInfo.Signature.Length + 1; + bool closed = false; + if (paramCountDelegateClosed == paramCountTargetMethod) + { + closed = true; + } + else + { + Debug.Assert(paramCountDelegateClosed == paramCountTargetMethod + 1); + } + + if (targetMethod.Signature.IsStatic) + { + MethodDesc invokeThunk; + MethodDesc initMethod; + + if (!closed) + { + initMethod = systemDelegate.GetKnownMethod("InitializeOpenStaticThunk", null); + invokeThunk = delegateInfo.Thunks[DelegateThunkKind.OpenStaticThunk]; + } + else + { + // Closed delegate to a static method (i.e. delegate to an extension method that locks the first parameter) + invokeThunk = delegateInfo.Thunks[DelegateThunkKind.ClosedStaticThunk]; + initMethod = systemDelegate.GetKnownMethod("InitializeClosedStaticThunk", null); + } + + var instantiatedDelegateType = delegateType as InstantiatedType; + if (instantiatedDelegateType != null) + invokeThunk = context.GetMethodForInstantiatedType(invokeThunk, instantiatedDelegateType); + + return new DelegateCreationInfo( + factory.MethodEntrypoint(initMethod), + targetMethod, + TargetKind.ExactCallableAddress, + factory.MethodEntrypoint(invokeThunk)); + } + else + { + if (!closed) + throw new NotImplementedException("Open instance delegates"); + + string initializeMethodName = "InitializeClosedInstance"; + MethodDesc targetCanonMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + TargetKind kind; + if (targetMethod.HasInstantiation) + { + if (followVirtualDispatch && targetMethod.IsVirtual) + { + initializeMethodName = "InitializeClosedInstanceWithGVMResolution"; + kind = TargetKind.MethodHandle; + } + else + { + if (targetMethod != targetCanonMethod) + { + // Closed delegates to generic instance methods need to be constructed through a slow helper that + // checks for the fat function pointer case (function pointer + instantiation argument in a single + // pointer) and injects an invocation thunk to unwrap the fat function pointer as part of + // the invocation if necessary. + initializeMethodName = "InitializeClosedInstanceSlow"; + } + + kind = TargetKind.ExactCallableAddress; + } + } + else + { + if (followVirtualDispatch && targetMethod.IsVirtual) + { + if (targetMethod.OwningType.IsInterface) + { + kind = TargetKind.InterfaceDispatch; + initializeMethodName = "InitializeClosedInstanceToInterface"; + } + else + { + kind = TargetKind.VTableLookup; + targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + } + } + else + { + kind = TargetKind.CanonicalEntrypoint; + targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + } + } + + return new DelegateCreationInfo( + factory.MethodEntrypoint(systemDelegate.GetKnownMethod(initializeMethodName, null)), + targetMethod, + kind); + } + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__DelegateCtor_"); + if (TargetNeedsVTableLookup) + sb.Append("FromVtbl_"); + Constructor.AppendMangledName(nameMangler, sb); + sb.Append("__"); + sb.Append(nameMangler.GetMangledMethodName(TargetMethod)); + if (Thunk != null) + { + sb.Append("__"); + Thunk.AppendMangledName(nameMangler, sb); + } + } + + public override bool Equals(object obj) + { + var other = obj as DelegateCreationInfo; + return other != null + && Constructor == other.Constructor + && TargetMethod == other.TargetMethod + && _targetKind == other._targetKind + && Thunk == other.Thunk; + } + + public override int GetHashCode() + { + return Constructor.GetHashCode() ^ TargetMethod.GetHashCode(); + } + +#if !SUPPORT_JIT + internal int CompareTo(DelegateCreationInfo other, TypeSystemComparer comparer) + { + var compare = _targetKind - other._targetKind; + if (compare != 0) + return compare; + + compare = comparer.Compare(TargetMethod, other.TargetMethod); + if (compare != 0) + return compare; + + compare = comparer.Compare(Constructor.Method, other.Constructor.Method); + if (compare != 0) + return compare; + + if (Thunk == other.Thunk) + return 0; + + if (Thunk == null) + return -1; + + if (other.Thunk == null) + return 1; + + return comparer.Compare(Thunk.Method, other.Thunk.Method); + } +#endif + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayMapNode.cs new file mode 100644 index 00000000000000..02bcfd36914d58 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayMapNode.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.NativeFormat; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a hash table of array types generated into the image. + /// + internal sealed class ArrayMapNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public ArrayMapNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__array_type_map_End", true); + _externalReferences = externalReferences; + } + + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__array_type_map"); + } + public int Offset => 0; + public override bool IsShareable => false; + + public override ObjectNodeSection Section => _externalReferences.Section; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + var writer = new NativeWriter(); + var typeMapHashTable = new VertexHashtable(); + + Section hashTableSection = writer.NewSection(); + hashTableSection.Place(typeMapHashTable); + + foreach (var type in factory.MetadataManager.GetTypesWithConstructedEETypes()) + { + if (!type.IsSzArray) + continue; + + var arrayType = (ArrayType)type; + + // This optimization is not compatible with canInlineTypeCheck on JIT/EE interface returning + // CORINFO_INLINE_TYPECHECK_PASS unconditionally. + // + // If we're generating a template for this type, we can skip generating the hashtable entry + // since the type loader can just create this type at runtime if something needs it. It's + // okay to have multiple EETypes for the same array type. + // var canonArrayType = arrayType.ConvertToCanonForm(CanonicalFormKind.Specific); + // if (arrayType != canonArrayType && factory.NativeLayout.TemplateTypeLayout(canonArrayType).Marked) + // continue; + + // Look at the constructed type symbol. If a constructed type wasn't emitted, then the array map entry isn't valid for use + IEETypeNode arrayTypeSymbol = factory.ConstructedTypeSymbol(arrayType); + + Vertex vertex = writer.GetUnsignedConstant(_externalReferences.GetIndex(arrayTypeSymbol)); + + int hashCode = arrayType.GetHashCode(); + typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.ArrayMapNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfEmbeddedDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfEmbeddedDataNode.cs new file mode 100644 index 00000000000000..bc6a93c776aefc --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfEmbeddedDataNode.cs @@ -0,0 +1,121 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public interface IHasStartSymbol + { + ObjectAndOffsetSymbolNode StartSymbol { get; } + } + + /// + /// Represents an array of nodes. The contents of this node will be emitted + /// by placing a starting symbol, followed by contents of nodes (optionally + /// sorted using provided comparer), followed by ending symbol. + /// + public class ArrayOfEmbeddedDataNode : EmbeddedDataContainerNode, IHasStartSymbol + where TEmbedded : EmbeddedObjectNode + { + private HashSet _nestedNodes = new HashSet(); + private List _nestedNodesList = new List(); + private IComparer _sorter; + + public ArrayOfEmbeddedDataNode(string startSymbolMangledName, string endSymbolMangledName, IComparer nodeSorter) : base(startSymbolMangledName, endSymbolMangledName) + { + _sorter = nodeSorter; + } + + public void AddEmbeddedObject(TEmbedded symbol) + { + lock (_nestedNodes) + { + if (_nestedNodes.Add(symbol)) + { + _nestedNodesList.Add(symbol); + } + symbol.ContainingNode = this; + } + } + + protected override string GetName(NodeFactory factory) => $"Region {StartSymbol.GetMangledName(factory.NameMangler)}"; + + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + protected IEnumerable NodesList => _nestedNodesList; + private TEmbedded _nextElementToEncode; + public TEmbedded NextElementToEncode => _nextElementToEncode; + + protected virtual void GetElementDataForNodes(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly) + { + int index = 0; + _nextElementToEncode = null; + for (int i = 0; i < _nestedNodesList.Count; i++) + { + TEmbedded node = _nestedNodesList[i]; + if ((i + 1) < _nestedNodesList.Count) + _nextElementToEncode = _nestedNodesList[i + 1]; + else + _nextElementToEncode = null; + + if (!relocsOnly) + { + node.InitializeOffsetFromBeginningOfArray(builder.CountBytes); + node.InitializeIndexFromBeginningOfArray(index++); + } + + node.EncodeData(ref builder, factory, relocsOnly); + if (node is ISymbolDefinitionNode symbolDef) + { + builder.AddSymbol(symbolDef); + } + } + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.RequireInitialPointerAlignment(); + + if (_sorter != null) + _nestedNodesList.Sort(_sorter); + + builder.AddSymbol(StartSymbol); + + GetElementDataForNodes(ref builder, factory, relocsOnly); + + EndSymbol.SetSymbolOffset(builder.CountBytes); + builder.AddSymbol(EndSymbol); + + ObjectData objData = builder.ToObjectData(); + return objData; + } + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) + { + return _nestedNodesList.Count == 0; + } + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + dependencies.Add(StartSymbol, "StartSymbol"); + dependencies.Add(EndSymbol, "EndSymbol"); + + return dependencies; + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + public override int ClassCode => (int)ObjectNodeOrder.ArrayOfEmbeddedDataNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfEmbeddedPointersNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfEmbeddedPointersNode.cs new file mode 100644 index 00000000000000..2d426c40a62962 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfEmbeddedPointersNode.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents an array of pointers to symbols. is the type + /// of node each pointer within the vector points to. + /// + public sealed class ArrayOfEmbeddedPointersNode : ArrayOfEmbeddedDataNode> + where TTarget : ISortableSymbolNode + { + private int _nextId; + private string _startSymbolMangledName; + + /// + /// Provides a callback mechanism for notification when an EmbeddedPointerIndirectionNode is marked and added to the + /// parent ArrayOfEmbeddedPointersNode's internal list + /// + public delegate void OnMarkedDelegate(EmbeddedPointerIndirectionNode embeddedObject); + + public ArrayOfEmbeddedPointersNode(string startSymbolMangledName, string endSymbolMangledName, IComparer nodeSorter) + : base( + startSymbolMangledName, + endSymbolMangledName, + nodeSorter != null ? new PointerIndirectionNodeComparer(nodeSorter) : null) + { + _startSymbolMangledName = startSymbolMangledName; + } + + public EmbeddedObjectNode NewNode(TTarget target) + { + return new SimpleEmbeddedPointerIndirectionNode(this, target); + } + + public EmbeddedObjectNode NewNodeWithSymbol(TTarget target) + { + return new EmbeddedPointerIndirectionWithSymbolNode(this, target, GetNextId()); + } + + int GetNextId() + { + return System.Threading.Interlocked.Increment(ref _nextId); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + public override int ClassCode => (int)ObjectNodeOrder.ArrayOfEmbeddedPointersNode; + + private class PointerIndirectionNodeComparer : IComparer> + { + private IComparer _innerComparer; + + public PointerIndirectionNodeComparer(IComparer innerComparer) + { + _innerComparer = innerComparer; + } + + public int Compare(EmbeddedPointerIndirectionNode x, EmbeddedPointerIndirectionNode y) + { + return _innerComparer.Compare(x.Target, y.Target); + } + } + + private class SimpleEmbeddedPointerIndirectionNode : EmbeddedPointerIndirectionNode + { + protected ArrayOfEmbeddedPointersNode _parentNode; + + public SimpleEmbeddedPointerIndirectionNode(ArrayOfEmbeddedPointersNode futureParent, TTarget target) + : base(target) + { + _parentNode = futureParent; + } + + protected override string GetName(NodeFactory factory) => $"Embedded pointer to {Target.GetMangledName(factory.NameMangler)}"; + + protected override void OnMarked(NodeFactory factory) + { + // We don't want the child in the parent collection unless it's necessary. + // Only when this node gets marked, the parent node becomes the actual parent. + _parentNode.AddEmbeddedObject(this); + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + return new[] + { + new DependencyListEntry(Target, "reloc"), + new DependencyListEntry(_parentNode, "Pointer region") + }; + } + + public override int ClassCode => -66002498; + } + + private class EmbeddedPointerIndirectionWithSymbolNode : SimpleEmbeddedPointerIndirectionNode, ISymbolDefinitionNode + { + private int _id; + + public EmbeddedPointerIndirectionWithSymbolNode(ArrayOfEmbeddedPointersNode futureParent, TTarget target, int id) + : base(futureParent, target) + { + _id = id; + } + + + int ISymbolNode.Offset => 0; + + int ISymbolDefinitionNode.Offset => OffsetFromBeginningOfArray; + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append(_parentNode._startSymbolMangledName).Append("_").Append(_id.ToStringInvariant()); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs new file mode 100644 index 00000000000000..e2bc6c4b70ec8e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class ArrayOfFrozenObjectsNode : ArrayOfEmbeddedDataNode + where TEmbedded : EmbeddedObjectNode + { + public ArrayOfFrozenObjectsNode(string startSymbolMangledName, string endSymbolMangledName, IComparer nodeSorter) : base(startSymbolMangledName, endSymbolMangledName, nodeSorter) + { + } + + private void AlignNextObject(ref ObjectDataBuilder builder, NodeFactory factory) + { + builder.EmitZeros(AlignmentHelper.AlignUp(builder.CountBytes, factory.Target.PointerSize) - builder.CountBytes); + } + + protected override void GetElementDataForNodes(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly) + { + foreach (EmbeddedObjectNode node in NodesList) + { + AlignNextObject(ref builder, factory); + + if (!relocsOnly) + node.InitializeOffsetFromBeginningOfArray(builder.CountBytes); + + node.EncodeData(ref builder, factory, relocsOnly); + if (node is ISymbolDefinitionNode) + { + builder.AddSymbol((ISymbolDefinitionNode)node); + } + } + + // Terminate with a null pointer as expected by the GC + AlignNextObject(ref builder, factory); + builder.EmitZeroPointer(); + } + + public override int ClassCode => -1771336339; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/BlobNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/BlobNode.cs new file mode 100644 index 00000000000000..9e9a393c8f82d2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/BlobNode.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class BlobNode : ObjectNode, ISymbolDefinitionNode + { + private Utf8String _name; + private ObjectNodeSection _section; + private byte[] _data; + private int _alignment; + + public BlobNode(Utf8String name, ObjectNodeSection section, byte[] data, int alignment) + { + _name = name; + _section = section; + _data = data; + _alignment = alignment; + } + + public override ObjectNodeSection Section => _section; + public override bool StaticDependenciesAreComputed => true; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(_name); + } + public int Offset => 0; + public override bool IsShareable => true; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + return new ObjectData(_data, Array.Empty(), _alignment, new ISymbolDefinitionNode[] { this }); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + +#if !SUPPORT_JIT + public override int ClassCode => -470351029; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return _name.CompareTo(((BlobNode)other)._name); + } +#endif + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/BlockReflectionTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/BlockReflectionTypeMapNode.cs new file mode 100644 index 00000000000000..0020d0581f243d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/BlockReflectionTypeMapNode.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; +using Internal.NativeFormat; +using Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a hashtable of all type blocked from reflection. + /// + public sealed class BlockReflectionTypeMapNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public BlockReflectionTypeMapNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__block_reflection_type_map_End", true); + _externalReferences = externalReferences; + } + + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__block_reflection_type_map"); + } + public int Offset => 0; + public override bool IsShareable => false; + + public override ObjectNodeSection Section => _externalReferences.Section; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + var writer = new NativeWriter(); + var reflectionBlockTypeMapHashTable = new VertexHashtable(); + + Section hashTableSection = writer.NewSection(); + hashTableSection.Place(reflectionBlockTypeMapHashTable); + + foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) + { + if (!type.IsTypeDefinition) + continue; + + var mdType = type as MetadataType; + if (mdType == null) + continue; + + if (!factory.MetadataManager.IsReflectionBlocked(mdType)) + continue; + + if (!factory.CompilationModuleGroup.ContainsType(mdType)) + continue; + + // Go with a necessary type symbol. It will be upgraded to a constructed one if a constructed was emitted. + IEETypeNode typeSymbol = factory.NecessaryTypeSymbol(type); + + Vertex vertex = writer.GetUnsignedConstant(_externalReferences.GetIndex(typeSymbol)); + + int hashCode = typeSymbol.Type.GetHashCode(); + reflectionBlockTypeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.BlockReflectionTypeMapNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CallingConventionConverterKey.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CallingConventionConverterKey.cs new file mode 100644 index 00000000000000..7c94539fb6bb42 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CallingConventionConverterKey.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public struct CallingConventionConverterKey : IEquatable + { + public CallingConventionConverterKey(Internal.NativeFormat.CallingConventionConverterKind converterKind, + MethodSignature signature) + { + ConverterKind = converterKind; + Signature = signature; + } + + public Internal.NativeFormat.CallingConventionConverterKind ConverterKind { get; } + public MethodSignature Signature { get; } + + public override bool Equals(object obj) + { + return obj is CallingConventionConverterKey && Equals((CallingConventionConverterKey)obj); + } + + public bool Equals(CallingConventionConverterKey other) + { + if (ConverterKind != other.ConverterKind) + return false; + + if (!Signature.Equals(other.Signature)) + return false; + + return true; + } + + public override int GetHashCode() + { + return Signature.GetHashCode() ^ (int)ConverterKind; + } + + public string GetName() + { + return ConverterKind.ToString() + Signature.GetName(); + } + } + + public static class MethodSignatureExtensions + { + public static void AppendName(this MethodSignature signature, StringBuilder nameBuilder, UniqueTypeNameFormatter typeNameFormatter) + { + if (signature.GenericParameterCount > 0) + { + nameBuilder.Append("GenParams:"); + nameBuilder.Append(signature.GenericParameterCount); + nameBuilder.Append(' '); + } + + if (signature.IsStatic) + nameBuilder.Append("Static "); + + typeNameFormatter.AppendName(nameBuilder, signature.ReturnType); + nameBuilder.Append('('); + for (int i = 0; i < signature.Length; i++) + { + if (i != 0) + nameBuilder.Append(','); + typeNameFormatter.AppendName(nameBuilder, signature[i]); + } + nameBuilder.Append(')'); + } + + public static string GetName(this MethodSignature signature) + { + StringBuilder nameBuilder = new StringBuilder(); + signature.AppendName(nameBuilder, UniqueTypeNameFormatter.Instance); + return nameBuilder.ToString(); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CanonicalDefinitionEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CanonicalDefinitionEETypeNode.cs new file mode 100644 index 00000000000000..a598bfe9961f1b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CanonicalDefinitionEETypeNode.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.TypeSystem; +using Internal.Runtime; + +namespace ILCompiler.DependencyAnalysis +{ + internal sealed class CanonicalDefinitionEETypeNode : EETypeNode + { + public CanonicalDefinitionEETypeNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + Debug.Assert(type.IsCanonicalDefinitionType(CanonicalFormKind.Any)); + } + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => false; + public override bool StaticDependenciesAreComputed => true; + public override bool IsShareable => true; + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) => null; + protected override int GCDescSize => 0; + + protected internal override void ComputeOptionalEETypeFields(NodeFactory factory, bool relocsOnly) + { + if (_type.IsCanonicalDefinitionType(CanonicalFormKind.Universal)) + { + // Value types should have at least 1 byte of size to avoid zero-length structures + // Add pointer-size to the number of instance field bytes to consistently represents the boxed size. + uint numInstanceFieldBytes = 1 + (uint)factory.Target.PointerSize; + + uint valueTypeFieldPadding = (uint)(MinimumObjectSize - factory.Target.PointerSize) - numInstanceFieldBytes; + uint valueTypeFieldPaddingEncoded = EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue(valueTypeFieldPadding, 1, _type.Context.Target.PointerSize); + Debug.Assert(valueTypeFieldPaddingEncoded != 0); + + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.ValueTypeFieldPadding, valueTypeFieldPaddingEncoded); + } + } + + // Canonical definition types will have their base size set to the minimum + protected override int BaseSize => MinimumObjectSize; + + public override int ClassCode => -1851030036; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs new file mode 100644 index 00000000000000..845fe201486d34 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs @@ -0,0 +1,148 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.TypeSystem; +using Internal.Runtime; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Canonical type instantiations are emitted, not because they are used directly by the user code, but because + /// they are used by the dynamic type loader when dynamically instantiating types at runtime. + /// The data that we emit on canonical type instantiations should just be the minimum that is needed by the template + /// type loader. + /// Similarly, the dependencies that we track for canonical type instantiations are minimal, and are just the ones used + /// by the dynamic type loader + /// + public sealed class CanonicalEETypeNode : EETypeNode + { + public CanonicalEETypeNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + Debug.Assert(!type.IsCanonicalDefinitionType(CanonicalFormKind.Any)); + Debug.Assert(type.IsCanonicalSubtype(CanonicalFormKind.Any)); + Debug.Assert(type == type.ConvertToCanonForm(CanonicalFormKind.Specific)); + Debug.Assert(!type.IsMdArray || factory.Target.Abi == TargetAbi.CppCodegen); + } + + public override bool StaticDependenciesAreComputed => true; + public override bool IsShareable => IsTypeNodeShareable(_type); + protected override bool EmitVirtualSlotsAndInterfaces => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => false; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencyList = base.ComputeNonRelocationBasedDependencies(factory); + + // Ensure that we track the necessary type symbol if we are working with a constructed type symbol. + // The emitter will ensure we don't emit both, but this allows us assert that we only generate + // relocs to nodes we emit. + dependencyList.Add(factory.NecessaryTypeSymbol(_type), "Necessary type symbol related to CanonicalEETypeNode"); + + DefType closestDefType = _type.GetClosestDefType(); + + if (MightHaveInterfaceDispatchMap(factory)) + dependencyList.Add(factory.InterfaceDispatchMap(_type), "Canonical interface dispatch map"); + + dependencyList.Add(factory.VTable(closestDefType), "VTable"); + + if (_type.IsCanonicalSubtype(CanonicalFormKind.Universal)) + dependencyList.Add(factory.NativeLayout.TemplateTypeLayout(_type), "Universal generic types always have template layout"); + + // Track generic virtual methods that will get added to the GVM tables + if (TypeGVMEntriesNode.TypeNeedsGVMTableEntries(_type)) + { + dependencyList.Add(new DependencyListEntry(factory.TypeGVMEntries(_type.GetTypeDefinition()), "Type with generic virtual methods")); + + AddDependenciesForUniversalGVMSupport(factory, _type, ref dependencyList); + } + + // Keep track of the default constructor map dependency for this type if it has a default constructor + MethodDesc defaultCtor = closestDefType.GetDefaultConstructor(); + if (defaultCtor != null) + { + dependencyList.Add(new DependencyListEntry( + factory.CanonicalEntrypoint(defaultCtor), + "DefaultConstructorNode")); + } + + return dependencyList; + } + + protected override ISymbolNode GetBaseTypeNode(NodeFactory factory) + { + return _type.BaseType != null ? factory.NecessaryTypeSymbol(_type.BaseType.NormalizeInstantiation()) : null; + } + + protected override int GCDescSize + { + get + { + // No GCDescs for universal canonical types + if (_type.IsCanonicalSubtype(CanonicalFormKind.Universal)) + return 0; + + Debug.Assert(_type.IsCanonicalSubtype(CanonicalFormKind.Specific)); + return GCDescEncoder.GetGCDescSize(_type); + } + } + + protected override void OutputGCDesc(ref ObjectDataBuilder builder) + { + // No GCDescs for universal canonical types + if (_type.IsCanonicalSubtype(CanonicalFormKind.Universal)) + return; + + Debug.Assert(_type.IsCanonicalSubtype(CanonicalFormKind.Specific)); + GCDescEncoder.EncodeGCDesc(ref builder, _type); + } + + protected override void OutputInterfaceMap(NodeFactory factory, ref ObjectDataBuilder objData) + { + foreach (var itf in _type.RuntimeInterfaces) + { + // Interface omitted for canonical instantiations (constructed at runtime for dynamic types from the native layout info) + objData.EmitZeroPointer(); + } + } + + protected override int BaseSize + { + get + { + if (_type.IsCanonicalSubtype(CanonicalFormKind.Universal) && _type.IsDefType) + { + LayoutInt instanceByteCount = ((DefType)_type).InstanceByteCount; + + if (instanceByteCount.IsIndeterminate) + { + // For USG types, they may be of indeterminate size, and the size of the type may be meaningless. + // In that case emit a fixed constant. + return MinimumObjectSize; + } + } + + return base.BaseSize; + } + } + + protected override void ComputeValueTypeFieldPadding() + { + DefType defType = _type as DefType; + + // Types of indeterminate sizes don't have computed ValueTypeFieldPadding + if (defType != null && defType.InstanceByteCount.IsIndeterminate) + { + Debug.Assert(_type.IsCanonicalSubtype(CanonicalFormKind.Universal)); + return; + } + + base.ComputeValueTypeFieldPadding(); + } + + public override int ClassCode => -1798018602; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ClassConstructorContextMap.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ClassConstructorContextMap.cs new file mode 100644 index 00000000000000..db7e7b2c353781 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ClassConstructorContextMap.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.NativeFormat; +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + class ClassConstructorContextMap : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public ClassConstructorContextMap(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__type_to_cctorContext_map_End", true); + _externalReferences = externalReferences; + } + + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__type_to_cctorContext_map"); + } + public int Offset => 0; + + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + var writer = new NativeWriter(); + var typeMapHashTable = new VertexHashtable(); + + Section hashTableSection = writer.NewSection(); + hashTableSection.Place(typeMapHashTable); + + foreach (var node in factory.MetadataManager.GetCctorContextMapping()) + { + MetadataType type = node.Type; + + Debug.Assert(factory.PreinitializationManager.HasLazyStaticConstructor(type)); + + // If this type doesn't generate an MethodTable in the current compilation, don't report it in the table. + // If nobody can get to the MethodTable, they can't ask to run the cctor. We don't need to force generate it. + if (!factory.MetadataManager.TypeGeneratesEEType(type)) + continue; + + // Hash table is hashed by the hashcode of the owning type. + // Each entry has: the MethodTable of the type, followed by the non-GC static base. + // The non-GC static base is prefixed by the class constructor context. + Vertex vertex = writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.NecessaryTypeSymbol(type))), + writer.GetUnsignedConstant(_externalReferences.GetIndex(node)) + ); + + int hashCode = type.GetHashCode(); + typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.ClassConstructorContextMap; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ClonedConstructedEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ClonedConstructedEETypeNode.cs new file mode 100644 index 00000000000000..4656e567a4df2d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ClonedConstructedEETypeNode.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal sealed class ClonedConstructedEETypeNode : ConstructedEETypeNode, ISymbolDefinitionNode + { + public ClonedConstructedEETypeNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler) + " cloned"; + + public override ISymbolNode NodeForLinkage(NodeFactory factory) => this; + + // + // A cloned type must be named differently than the type it is a clone of so the linker + // will have an unambiguous symbol to resolve + // + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__Cloned_EEType_").Append(nameMangler.GetMangledTypeName(_type)); + } + public override bool IsShareable => true; + + protected override void OutputRelatedType(NodeFactory factory, ref ObjectDataBuilder objData) + { + // + // Cloned types use the related type field to point via an IAT slot at their true implementation + // + objData.EmitPointerReloc(factory.NecessaryTypeSymbol(_type)); + } + + public override int ClassCode => -288888778; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs new file mode 100644 index 00000000000000..96dfdf19104bf3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +using Internal.IL; +using Internal.Text; +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.DependencyAnalysis; + +using DependencyList=ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using CombinedDependencyList=System.Collections.Generic.List.CombinedDependencyListEntry>; +using DependencyListEntry=ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyListEntry; + +namespace ILCompiler.DependencyAnalysis +{ + public static class CodeBasedDependencyAlgorithm + { + public static void AddDependenciesDueToMethodCodePresence(ref DependencyList dependencies, NodeFactory factory, MethodDesc method, MethodIL methodIL) + { + factory.MetadataManager.GetDependenciesDueToMethodCodePresence(ref dependencies, factory, method, methodIL); + + factory.InteropStubManager.AddDependeciesDueToPInvoke(ref dependencies, factory, method); + + if (method.OwningType is MetadataType mdOwningType + && mdOwningType.Module.GetGlobalModuleType().GetStaticConstructor() is MethodDesc moduleCctor) + { + dependencies ??= new DependencyList(); + dependencies.Add(factory.MethodEntrypoint(moduleCctor), "Method in a module with initializer"); + } + + if (method.IsIntrinsic) + { + if (method.OwningType is MetadataType owningType) + { + string name = method.Name; + + switch (name) + { + // The general purpose code in Comparer/EqualityComparer Create method depends on the template + // type loader being able to load the necessary types at runtime. + case "Create": + if (method.IsSharedByGenericInstantiations + && owningType.Module == factory.TypeSystemContext.SystemModule + && owningType.Namespace == "System.Collections.Generic") + { + TypeDesc[] templateDependencies = null; + + if (owningType.Name == "Comparer`1") + { + templateDependencies = Internal.IL.Stubs.ComparerIntrinsics.GetPotentialComparersForType( + owningType.Instantiation[0]); + } + else if (owningType.Name == "EqualityComparer`1") + { + templateDependencies = Internal.IL.Stubs.ComparerIntrinsics.GetPotentialEqualityComparersForType( + owningType.Instantiation[0]); + } + + if (templateDependencies != null) + { + dependencies = dependencies ?? new DependencyList(); + foreach (TypeDesc templateType in templateDependencies) + { + dependencies.Add(factory.NativeLayout.TemplateTypeLayout(templateType), "Generic comparer"); + } + } + } + break; + } + } + } + } + + public static bool HasConditionalDependenciesDueToMethodCodePresence(MethodDesc method) + { + // NICE: would be nice if the metadata managed could decide this but we don't have a way to get at it + return method.HasInstantiation || method.OwningType.HasInstantiation; + } + + public static void AddConditionalDependenciesDueToMethodCodePresence(ref CombinedDependencyList dependencies, NodeFactory factory, MethodDesc method) + { + factory.MetadataManager.GetConditionalDependenciesDueToMethodCodePresence(ref dependencies, factory, method); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs new file mode 100644 index 00000000000000..4c5d630437a3b2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs @@ -0,0 +1,158 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Runtime; +using Internal.TypeSystem; +using Internal.IL; + +namespace ILCompiler.DependencyAnalysis +{ + public class ConstructedEETypeNode : EETypeNode + { + public ConstructedEETypeNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + Debug.Assert(!type.IsCanonicalDefinitionType(CanonicalFormKind.Any)); + CheckCanGenerateConstructedEEType(factory, type); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler) + " constructed"; + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => false; + + protected override bool EmitVirtualSlotsAndInterfaces => true; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencyList = base.ComputeNonRelocationBasedDependencies(factory); + + // Ensure that we track the necessary type symbol if we are working with a constructed type symbol. + // The emitter will ensure we don't emit both, but this allows us assert that we only generate + // relocs to nodes we emit. + dependencyList.Add(factory.NecessaryTypeSymbol(_type), "NecessaryType for constructed type"); + + if (_type is MetadataType mdType + && mdType.Module.GetGlobalModuleType().GetStaticConstructor() is MethodDesc moduleCctor) + { + dependencyList.Add(factory.MethodEntrypoint(moduleCctor), "Type in a module with initializer"); + } + + DefType closestDefType = _type.GetClosestDefType(); + + if (MightHaveInterfaceDispatchMap(factory)) + { + dependencyList.Add(factory.InterfaceDispatchMap(_type), "Interface dispatch map"); + } + + if (_type.IsArray) + { + // Array MethodTable depends on System.Array's virtuals. Array EETypes don't point to + // their base type (i.e. there's no reloc based dependency making this "just work"). + dependencyList.Add(factory.ConstructedTypeSymbol(_type.BaseType), "Array base type"); + + ArrayType arrayType = (ArrayType)_type; + if (arrayType.IsMdArray && arrayType.Rank == 1) + { + // Allocating an MDArray of Rank 1 with zero lower bounds results in allocating + // an SzArray instead. Make sure the type loader can find the SzArray type. + dependencyList.Add(factory.ConstructedTypeSymbol(arrayType.ElementType.MakeArrayType()), "Rank 1 array"); + } + } + + dependencyList.Add(factory.VTable(closestDefType), "VTable"); + + if (factory.TypeSystemContext.SupportsUniversalCanon) + { + foreach (var instantiationType in _type.Instantiation) + { + if (instantiationType.IsValueType) + { + // All valuetype generic parameters of a constructed type may be effectively constructed. This is generally not that + // critical, but in the presence of universal generics the compiler may generate a Box followed by calls to ToString, + // GetHashcode or Equals in ways that cannot otherwise be detected by dependency analysis. Thus force all struct type + // generic parameters to be considered constructed when walking dependencies of a constructed generic + dependencyList.Add(factory.ConstructedTypeSymbol(instantiationType.ConvertToCanonForm(CanonicalFormKind.Specific)), + "Struct generic parameters in constructed types may be assumed to be used as constructed in constructed generic types"); + } + } + } + + // Ask the metadata manager if we have any dependencies due to reflectability. + factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencyList, factory, _type); + + factory.InteropStubManager.AddInterestingInteropConstructedTypeDependencies(ref dependencyList, factory, _type); + + // Keep track of the default constructor map dependency for this type if it has a default constructor + MethodDesc defaultCtor = closestDefType.GetDefaultConstructor(); + if (defaultCtor != null) + { + dependencyList.Add(new DependencyListEntry( + factory.CanonicalEntrypoint(defaultCtor), + "DefaultConstructorNode")); + } + + return dependencyList; + } + + protected override ISymbolNode GetBaseTypeNode(NodeFactory factory) + { + return _type.BaseType != null ? factory.ConstructedTypeSymbol(_type.BaseType) : null; + } + + protected override IEETypeNode GetInterfaceTypeNode(NodeFactory factory, TypeDesc interfaceType) + { + // The interface type will be visible to reflection and should be considered constructed. + return factory.ConstructedTypeSymbol(interfaceType); + } + + protected override int GCDescSize => GCDescEncoder.GetGCDescSize(_type); + + protected override void OutputGCDesc(ref ObjectDataBuilder builder) + { + GCDescEncoder.EncodeGCDesc(ref builder, _type); + } + + public static bool CreationAllowed(TypeDesc type) + { + switch (type.Category) + { + case TypeFlags.Pointer: + case TypeFlags.FunctionPointer: + case TypeFlags.ByRef: + // Pointers and byrefs are not boxable + return false; + case TypeFlags.Array: + case TypeFlags.SzArray: + // TODO: any validation for arrays? + break; + + default: + // Generic definition EETypes can't be allocated + if (type.IsGenericDefinition) + return false; + + // Full MethodTable of System.Canon should never be used. + if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) + return false; + + // The global "" type can never be allocated. + if (((MetadataType)type).IsModuleType) + return false; + + break; + } + + return true; + } + + public static void CheckCanGenerateConstructedEEType(NodeFactory factory, TypeDesc type) + { + if (!CreationAllowed(type)) + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + } + + public override int ClassCode => 590142654; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs new file mode 100644 index 00000000000000..09111876ceca61 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs @@ -0,0 +1,274 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Immutable; +using System.Diagnostics; +using System.Reflection.Metadata; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using MethodAttributes = System.Reflection.MethodAttributes; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Computes the list of dependencies that are necessary to generate metadata for a custom attribute, but also the dependencies to + /// make the custom attributes usable by the reflection stack at runtime. + /// + internal class CustomAttributeBasedDependencyAlgorithm + { + public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaMethod method) + { + MetadataReader reader = method.MetadataReader; + MethodDefinitionHandle methodHandle = method.Handle; + MethodDefinition methodDef = reader.GetMethodDefinition(methodHandle); + + // Handle custom attributes on the method + AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, methodDef.GetCustomAttributes()); + + // Handle custom attributes on method parameters + foreach (ParameterHandle parameterHandle in methodDef.GetParameters()) + { + Parameter parameter = reader.GetParameter(parameterHandle); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes()); + } + + // Handle custom attributes on generic method parameters + foreach (GenericParameterHandle genericParameterHandle in methodDef.GetGenericParameters()) + { + GenericParameter parameter = reader.GetGenericParameter(genericParameterHandle); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes()); + } + + // We don't model properties and events as separate entities within the compiler, so ensuring + // we can generate custom attributes for the associated events and properties from here + // is as good as any other place. + // + // As a performance optimization, we look for associated events and properties only + // if the method is SpecialName. This is required for CLS compliance and compilers we + // care about emit accessors like this. + if ((methodDef.Attributes & MethodAttributes.SpecialName) != 0) + { + TypeDefinition declaringType = reader.GetTypeDefinition(methodDef.GetDeclaringType()); + + foreach (PropertyDefinitionHandle propertyHandle in declaringType.GetProperties()) + { + PropertyDefinition property = reader.GetPropertyDefinition(propertyHandle); + PropertyAccessors accessors = property.GetAccessors(); + + if (accessors.Getter == methodHandle || accessors.Setter == methodHandle) + AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, property.GetCustomAttributes()); + } + + foreach (EventDefinitionHandle eventHandle in declaringType.GetEvents()) + { + EventDefinition @event = reader.GetEventDefinition(eventHandle); + EventAccessors accessors = @event.GetAccessors(); + + if (accessors.Adder == methodHandle || accessors.Remover == methodHandle || accessors.Raiser == methodHandle) + AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, @event.GetCustomAttributes()); + } + } + } + + public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaType type) + { + MetadataReader reader = type.MetadataReader; + TypeDefinition typeDef = reader.GetTypeDefinition(type.Handle); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, type.EcmaModule, typeDef.GetCustomAttributes()); + + // Handle custom attributes on generic type parameters + foreach (GenericParameterHandle genericParameterHandle in typeDef.GetGenericParameters()) + { + GenericParameter parameter = reader.GetGenericParameter(genericParameterHandle); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, type.EcmaModule, parameter.GetCustomAttributes()); + } + } + + public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaField field) + { + FieldDefinition fieldDef = field.MetadataReader.GetFieldDefinition(field.Handle); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, field.Module, fieldDef.GetCustomAttributes()); + } + + public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaAssembly assembly) + { + AssemblyDefinition asmDef = assembly.MetadataReader.GetAssemblyDefinition(); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, assembly, asmDef.GetCustomAttributes()); + + ModuleDefinition moduleDef = assembly.MetadataReader.GetModuleDefinition(); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, assembly, moduleDef.GetCustomAttributes()); + } + + private static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaModule module, CustomAttributeHandleCollection attributeHandles) + { + MetadataReader reader = module.MetadataReader; + var mdManager = (UsageBasedMetadataManager)factory.MetadataManager; + var attributeTypeProvider = new CustomAttributeTypeProvider(module); + + + foreach (CustomAttributeHandle caHandle in attributeHandles) + { + CustomAttribute attribute = reader.GetCustomAttribute(caHandle); + + try + { + MethodDesc constructor = module.GetMethod(attribute.Constructor); + + if (!mdManager.GeneratesAttributeMetadata(constructor.OwningType)) + continue; + + if (mdManager.IsReflectionBlocked(constructor)) + continue; + + CustomAttributeValue decodedValue = attribute.DecodeValue(attributeTypeProvider); + + // Make a new list in case we need to abort. + var caDependencies = factory.MetadataManager.GetDependenciesForCustomAttribute(factory, constructor, decodedValue) ?? new DependencyList(); + + caDependencies.Add(factory.ReflectableMethod(constructor), "Attribute constructor"); + caDependencies.Add(factory.ConstructedTypeSymbol(constructor.OwningType), "Attribute type"); + + if (AddDependenciesFromCustomAttributeBlob(caDependencies, factory, constructor.OwningType, decodedValue)) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.AddRange(caDependencies); + dependencies.Add(factory.CustomAttributeMetadata(new ReflectableCustomAttribute(module, caHandle)), "Attribute metadata"); + } + } + catch (TypeSystemException) + { + // We could end up seeing an exception here for a multitude of reasons: + // * Attribute ctor doesn't resolve + // * There's a typeof() that refers to something that can't be loaded + // * Attribute refers to a non-existing field + // * Etc. + // + // If we really wanted to, we could probably come up with a way to still make this + // work with the same failure modes at runtime as the CLR, but it might not be + // worth the hassle: the input was invalid. The most important thing is that we + // don't crash the compilation. + } + } + } + + private static bool AddDependenciesFromCustomAttributeBlob(DependencyList dependencies, NodeFactory factory, TypeDesc attributeType, CustomAttributeValue value) + { + MetadataManager mdManager = factory.MetadataManager; + + foreach (CustomAttributeTypedArgument decodedArgument in value.FixedArguments) + { + if (!AddDependenciesFromCustomAttributeArgument(dependencies, factory, decodedArgument.Type, decodedArgument.Value)) + return false; + } + + foreach (CustomAttributeNamedArgument decodedArgument in value.NamedArguments) + { + if (decodedArgument.Kind == CustomAttributeNamedArgumentKind.Field) + { + // This is an instance field. We don't track them right now. + } + else + { + Debug.Assert(decodedArgument.Kind == CustomAttributeNamedArgumentKind.Property); + + // Reflection will need to reflection-invoke the setter at runtime. + if (!AddDependenciesFromPropertySetter(dependencies, factory, attributeType, decodedArgument.Name)) + return false; + } + + if (!AddDependenciesFromCustomAttributeArgument(dependencies, factory, decodedArgument.Type, decodedArgument.Value)) + return false; + } + + return true; + } + + private static bool AddDependenciesFromPropertySetter(DependencyList dependencies, NodeFactory factory, TypeDesc attributeType, string propertyName) + { + EcmaType attributeTypeDefinition = (EcmaType)attributeType.GetTypeDefinition(); + + MetadataReader reader = attributeTypeDefinition.MetadataReader; + var typeDefinition = reader.GetTypeDefinition(attributeTypeDefinition.Handle); + + foreach (PropertyDefinitionHandle propDefHandle in typeDefinition.GetProperties()) + { + PropertyDefinition propDef = reader.GetPropertyDefinition(propDefHandle); + if (reader.StringComparer.Equals(propDef.Name, propertyName)) + { + PropertyAccessors accessors = propDef.GetAccessors(); + + if (!accessors.Setter.IsNil) + { + MethodDesc setterMethod = (MethodDesc)attributeTypeDefinition.EcmaModule.GetObject(accessors.Setter); + if (factory.MetadataManager.IsReflectionBlocked(setterMethod)) + return false; + + // Method on a generic attribute + if (attributeType != attributeTypeDefinition) + { + setterMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(setterMethod, (InstantiatedType)attributeType); + } + + dependencies.Add(factory.ReflectableMethod(setterMethod), "Custom attribute blob"); + } + + return true; + } + } + + // Haven't found it in current type. Check the base type. + TypeDesc baseType = attributeType.BaseType; + + if (baseType != null) + return AddDependenciesFromPropertySetter(dependencies, factory, baseType, propertyName); + + // Not found. This is bad metadata that will result in a runtime failure, but we shouldn't fail the compilation. + return true; + } + + private static bool AddDependenciesFromCustomAttributeArgument(DependencyList dependencies, NodeFactory factory, TypeDesc type, object value) + { + // If this is an initializer that refers to e.g. a blocked enum, we can't encode this attribute. + if (factory.MetadataManager.IsReflectionBlocked(type)) + return false; + + // Reflection will need to be able to allocate this type at runtime + // (e.g. this could be an array that needs to be allocated, or an enum that needs to be boxed). + dependencies.Add(factory.ConstructedTypeSymbol(type), "Custom attribute blob"); + + if (type.UnderlyingType.IsPrimitive || type.IsString || value == null) + return true; + + if (type.IsSzArray) + { + TypeDesc elementType = ((ArrayType)type).ElementType; + if (elementType.UnderlyingType.IsPrimitive || elementType.IsString) + return true; + + foreach (CustomAttributeTypedArgument arrayElement in (ImmutableArray>)value) + { + if (!AddDependenciesFromCustomAttributeArgument(dependencies, factory, arrayElement.Type, arrayElement.Value)) + return false; + } + + return true; + } + + // typeof() should be the only remaining option. + + Debug.Assert(value is TypeDesc); + + TypeDesc typeofType = (TypeDesc)value; + + if (factory.MetadataManager.IsReflectionBlocked(typeofType)) + return false; + + // Grab the metadata nodes that will be necessary to represent the typeof in the metadata blob + TypeMetadataNode.GetMetadataDependencies(ref dependencies, factory, typeofType, "Custom attribute blob"); + return true; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeMetadataNode.cs new file mode 100644 index 00000000000000..9cb52e99ebb710 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeMetadataNode.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a custom attribute that has metadata generated in the current compilation. + /// + /// + /// Only expected to be used during ILScanning when scanning for reflection. + /// + internal class CustomAttributeMetadataNode : DependencyNodeCore + { + private readonly ReflectableCustomAttribute _customAttribute; + + public CustomAttributeMetadataNode(ReflectableCustomAttribute customAttribute) + { + _customAttribute = customAttribute; + } + + public ReflectableCustomAttribute CustomAttribute => _customAttribute; + + // The metadata that the attribute depends on gets injected into the entity that owns the attribute. + // This makes the dependency graph less "nice", but it avoids either having to walk the attribute + // blob twice, or wasting memory holding on to dependencies here. + // + // We need to walk the dependencies before placing the node into the graph to find out whether + // the attribute even can be generated (does it refer to blocked types or something like that?). + public override IEnumerable GetStaticDependencies(NodeFactory factory) => null; + + protected override string GetName(NodeFactory factory) + { + return $"Reflectable custom attribute {_customAttribute.CustomAttributeHandle} in {_customAttribute.Module}"; + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedMethodNode.cs new file mode 100644 index 00000000000000..bde3bc13eabb4b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedMethodNode.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.TypeSystem; +using Internal.Runtime; +using Internal.IL; + +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Node that performs dataflow analysis to find dynamic dependencies (e.g. reflection use) of the given method. + /// + public class DataflowAnalyzedMethodNode : DependencyNodeCore + { + private readonly MethodIL _methodIL; + + public DataflowAnalyzedMethodNode(MethodIL methodIL) + { + Debug.Assert(methodIL.OwningMethod.IsTypicalMethodDefinition); + _methodIL = methodIL; + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + var mdManager = (UsageBasedMetadataManager)factory.MetadataManager; + try + { + return Dataflow.ReflectionMethodBodyScanner.ScanAndProcessReturnValue(factory, mdManager.FlowAnnotations, mdManager.Logger, _methodIL); + } + catch (TypeSystemException) + { + // Something wrong with the input - missing references, etc. + // The method body likely won't compile either, so we don't care. + return Array.Empty(); + } + } + + protected override string GetName(NodeFactory factory) + { + return "Dataflow analysis for " + factory.NameMangler.GetMangledMethodName(_methodIL.OwningMethod).ToString(); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DefaultConstructorMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DefaultConstructorMapNode.cs new file mode 100644 index 00000000000000..be7a7f0d7fcdad --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DefaultConstructorMapNode.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// DefaultConstructorMap blob, containing information on default constructor entrypoints of all types used + /// by lazy generic instantiations. + /// + internal class DefaultConstructorMapNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public DefaultConstructorMapNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__DefaultConstructor_Map_End", true); + _externalReferences = externalReferences; + } + + public ISymbolNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__DefaultConstructor_Map"); + } + + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => false; + public override bool StaticDependenciesAreComputed => true; + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.DefaultConstructorMapNode; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + var writer = new NativeWriter(); + var defaultConstructorHashtable = new VertexHashtable(); + + Section defaultConstructorHashtableSection = writer.NewSection(); + defaultConstructorHashtableSection.Place(defaultConstructorHashtable); + + foreach (var type in factory.MetadataManager.GetTypesWithConstructedEETypes()) + { + MethodDesc defaultCtor = type.GetDefaultConstructor(); + if (defaultCtor == null) + continue; + + defaultCtor = defaultCtor.GetCanonMethodTarget(CanonicalFormKind.Specific); + + ISymbolNode typeNode = factory.NecessaryTypeSymbol(type); + ISymbolNode defaultCtorNode = factory.MethodEntrypoint(defaultCtor, false); + + Vertex vertex = writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(typeNode)), + writer.GetUnsignedConstant(_externalReferences.GetIndex(defaultCtorNode))); + + int hashCode = type.GetHashCode(); + defaultConstructorHashtable.Append((uint)hashCode, defaultConstructorHashtableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DelegateMarshallingDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DelegateMarshallingDataNode.cs new file mode 100644 index 00000000000000..39c2670db18804 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DelegateMarshallingDataNode.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents an entry in the table. + /// + public class DelegateMarshallingDataNode : DependencyNodeCore + { + private readonly DefType _type; + + public DefType Type => _type; + + public DelegateMarshallingDataNode(DefType type) + { + Debug.Assert(type.IsDelegate); + _type = type; + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + InteropStateManager stateManager = ((CompilerGeneratedInteropStubManager)factory.InteropStubManager)._interopStateManager; + + return new DependencyListEntry[] + { + new DependencyListEntry(factory.NecessaryTypeSymbol(_type), "Delegate Marshalling Stub"), + new DependencyListEntry(factory.MethodEntrypoint(stateManager.GetOpenStaticDelegateMarshallingThunk(_type)), "Delegate Marshalling Stub"), + new DependencyListEntry(factory.MethodEntrypoint(stateManager.GetClosedDelegateMarshallingThunk(_type)), "Delegate Marshalling Stub"), + new DependencyListEntry(factory.MethodEntrypoint(stateManager.GetForwardDelegateCreationThunk(_type)), "Delegate Marshalling Stub"), + }; + } + + protected override string GetName(NodeFactory context) + { + return $"Delegate marshaling data for {_type}"; + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DelegateMarshallingStubMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DelegateMarshallingStubMapNode.cs new file mode 100644 index 00000000000000..bfe3777676b181 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DelegateMarshallingStubMapNode.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.NativeFormat; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a hash table of delegate marshalling stub types generated into the image. + /// + internal sealed class DelegateMarshallingStubMapNode : ObjectNode, ISymbolDefinitionNode + { + private readonly ObjectAndOffsetSymbolNode _endSymbol; + private readonly ExternalReferencesTableNode _externalReferences; + private readonly InteropStateManager _interopStateManager; + + public DelegateMarshallingStubMapNode(ExternalReferencesTableNode externalReferences, InteropStateManager interopStateManager) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__delegate_marshalling_stub_map_End", true); + _externalReferences = externalReferences; + _interopStateManager = interopStateManager; + } + + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__delegate_marshalling_stub_map"); + } + public int Offset => 0; + public override bool IsShareable => false; + + public override ObjectNodeSection Section => _externalReferences.Section; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + var writer = new NativeWriter(); + var typeMapHashTable = new VertexHashtable(); + + Section hashTableSection = writer.NewSection(); + hashTableSection.Place(typeMapHashTable); + + foreach (var delegateType in factory.MetadataManager.GetTypesWithDelegateMarshalling()) + { + Vertex thunks= writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(_interopStateManager.GetOpenStaticDelegateMarshallingThunk(delegateType)))), + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(_interopStateManager.GetClosedDelegateMarshallingThunk(delegateType)))), + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(_interopStateManager.GetForwardDelegateCreationThunk(delegateType)))) + ); + + Vertex vertex = writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.NecessaryTypeSymbol(delegateType))), + thunks + ); + + int hashCode = delegateType.GetHashCode(); + typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.DelegateMarshallingStubMapNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs new file mode 100644 index 00000000000000..9653f64718c74a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs @@ -0,0 +1,313 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.TypeSystem; + +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents the layout of the generic dictionary associated with a given canonical + /// generic type or generic method. Maintains a bag of associated + /// with the canonical entity. + /// + /// + /// The generic dictionary doesn't have any dependent nodes because + /// are runtime-determined - the concrete dependency depends on the generic context the canonical + /// entity is instantiated with. + /// + public abstract class DictionaryLayoutNode : DependencyNodeCore + { + private readonly TypeSystemEntity _owningMethodOrType; + + public DictionaryLayoutNode(TypeSystemEntity owningMethodOrType) + { + _owningMethodOrType = owningMethodOrType; + Validate(); + } + + [Conditional("DEBUG")] + private void Validate() + { + TypeDesc type = _owningMethodOrType as TypeDesc; + if (type != null) + { + Debug.Assert(type.IsCanonicalSubtype(CanonicalFormKind.Any)); + Debug.Assert(type.IsDefType); + } + else + { + MethodDesc method = _owningMethodOrType as MethodDesc; + Debug.Assert(method != null && method.IsSharedByGenericInstantiations); + } + } + + public virtual ObjectNodeSection DictionarySection(NodeFactory factory) + { + if (factory.Target.IsWindows) + { + if (_owningMethodOrType is TypeDesc) + { + return ObjectNodeSection.FoldableReadOnlyDataSection; + } + else + { + // Method dictionary serves as an identity at runtime which means they are not foldable. + Debug.Assert(_owningMethodOrType is MethodDesc); + return ObjectNodeSection.ReadOnlyDataSection; + } + } + else + { + return ObjectNodeSection.DataSection; + } + } + + /// + /// Ensure that a generic lookup result can be resolved. Used to add new lookups to a dictionary which HasUnfixedSlots + /// Must not be used after any calls to GetSlotForEntry + /// + public abstract void EnsureEntry(GenericLookupResult entry); + + /// + /// Get a slot index for a given entry. Slot indices are never expected to change once given out. + /// + public abstract int GetSlotForEntry(GenericLookupResult entry); + + /// + /// Get the slot for an entry which is fixed already. Otherwise return -1 + /// + /// + /// + public virtual int GetSlotForFixedEntry(GenericLookupResult entry) + { + return GetSlotForEntry(entry); + } + + public abstract IEnumerable Entries + { + get; + } + + public virtual IEnumerable FixedEntries => Entries; + + public TypeSystemEntity OwningMethodOrType => _owningMethodOrType; + + /// + /// Gets a value indicating whether the slot assignment is determined at the node creation time. + /// + public abstract bool HasFixedSlots + { + get; + } + + /// + /// Gets a value indicating if this dictionary may have non fixed slots + /// + public virtual bool HasUnfixedSlots => !HasFixedSlots; + + public virtual ICollection GetTemplateEntries(NodeFactory factory) + { + ArrayBuilder templateEntries = new ArrayBuilder(); + foreach (var entry in Entries) + { + templateEntries.Add(entry.TemplateDictionaryNode(factory)); + } + + return templateEntries.ToArray(); + } + + public virtual void EmitDictionaryData(ref ObjectDataBuilder builder, NodeFactory factory, GenericDictionaryNode dictionary, bool fixedLayoutOnly) + { + var context = new GenericLookupResultContext(dictionary.OwningEntity, dictionary.TypeInstantiation, dictionary.MethodInstantiation); + + IEnumerable entriesToEmit = fixedLayoutOnly ? FixedEntries : Entries; + + foreach (GenericLookupResult lookupResult in entriesToEmit) + { +#if DEBUG + int offsetBefore = builder.CountBytes; +#endif + + lookupResult.EmitDictionaryEntry(ref builder, factory, context, dictionary); + +#if DEBUG + Debug.Assert(builder.CountBytes - offsetBefore == factory.Target.PointerSize); +#endif + } + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + if (HasFixedSlots) + { + foreach (GenericLookupResult lookupResult in FixedEntries) + { + foreach (DependencyNodeCore dependency in lookupResult.NonRelocDependenciesFromUsage(factory)) + { + yield return new DependencyListEntry(dependency, "GenericLookupResultDependency"); + } + } + } + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + Debug.Assert(HasFixedSlots); + + NativeLayoutSavedVertexNode templateLayout; + if (_owningMethodOrType is MethodDesc) + { + templateLayout = factory.NativeLayout.TemplateMethodLayout((MethodDesc)_owningMethodOrType); + } + else + { + templateLayout = factory.NativeLayout.TemplateTypeLayout((TypeDesc)_owningMethodOrType); + } + + List conditionalDependencies = new List(); + + foreach (var lookupSignature in FixedEntries) + { + conditionalDependencies.Add(new CombinedDependencyListEntry(lookupSignature.TemplateDictionaryNode(factory), + templateLayout, + "Type loader template")); + } + + return conditionalDependencies; + } + + protected override string GetName(NodeFactory factory) => $"Dictionary layout for {_owningMethodOrType.ToString()}"; + + public override bool HasConditionalStaticDependencies => HasFixedSlots; + public override bool HasDynamicDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } + + public class PrecomputedDictionaryLayoutNode : DictionaryLayoutNode + { + private readonly GenericLookupResult[] _layout; + + public override bool HasFixedSlots => true; + + public PrecomputedDictionaryLayoutNode(TypeSystemEntity owningMethodOrType, IEnumerable layout) + : base(owningMethodOrType) + { + ArrayBuilder l = new ArrayBuilder(); + foreach (var entry in layout) + l.Add(entry); + + _layout = l.ToArray(); + } + + public override void EnsureEntry(GenericLookupResult entry) + { + int index = Array.IndexOf(_layout, entry); + + if (index == -1) + { + // Using EnsureEntry to add a slot to a PrecomputedDictionaryLayoutNode is not supported + throw new NotSupportedException(); + } + } + + public override int GetSlotForEntry(GenericLookupResult entry) + { + int index = Array.IndexOf(_layout, entry); + + // This entry wasn't precomputed. If this is an optimized build with scanner, it might suggest + // the scanner didn't see the need for this. There is a discrepancy between scanning and compiling. + // This is a fatal error to prevent bad codegen. + if (index < 0) + throw new InvalidOperationException($"{OwningMethodOrType}: {entry}"); + + return index; + } + + public override IEnumerable Entries + { + get + { + return _layout; + } + } + } + + public sealed class LazilyBuiltDictionaryLayoutNode : DictionaryLayoutNode + { + class EntryHashTable : LockFreeReaderHashtable + { + protected override bool CompareKeyToValue(GenericLookupResult key, GenericLookupResult value) => Object.Equals(key, value); + protected override bool CompareValueToValue(GenericLookupResult value1, GenericLookupResult value2) => Object.Equals(value1, value2); + protected override GenericLookupResult CreateValueFromKey(GenericLookupResult key) => key; + protected override int GetKeyHashCode(GenericLookupResult key) => key.GetHashCode(); + protected override int GetValueHashCode(GenericLookupResult value) => value.GetHashCode(); + } + + private EntryHashTable _entries = new EntryHashTable(); + private volatile GenericLookupResult[] _layout; + + public override bool HasFixedSlots => false; + + public LazilyBuiltDictionaryLayoutNode(TypeSystemEntity owningMethodOrType) + : base(owningMethodOrType) + { + } + + public override void EnsureEntry(GenericLookupResult entry) + { + Debug.Assert(_layout == null, "Trying to add entry but layout already computed"); + _entries.AddOrGetExisting(entry); + } + + private void ComputeLayout() + { + GenericLookupResult[] layout = new GenericLookupResult[_entries.Count]; + int index = 0; + foreach (GenericLookupResult entry in EntryHashTable.Enumerator.Get(_entries)) + { + layout[index++] = entry; + } + + var comparer = new GenericLookupResult.Comparer(new TypeSystemComparer()); + Array.Sort(layout, comparer.Compare); + + // Only publish after the full layout is computed. Races are fine. + _layout = layout; + } + + public override int GetSlotForEntry(GenericLookupResult entry) + { + if (_layout == null) + ComputeLayout(); + + int index = Array.IndexOf(_layout, entry); + + // We never called EnsureEntry on this during compilation? + // This is a fatal error to prevent bad codegen. + if (index < 0) + throw new InvalidOperationException($"{OwningMethodOrType}: {entry}"); + + return index; + } + + public override IEnumerable Entries + { + get + { + if (_layout == null) + ComputeLayout(); + + return _layout; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicDependencyAttributeAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicDependencyAttributeAlgorithm.cs new file mode 100644 index 00000000000000..a6bca843c6b086 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicDependencyAttributeAlgorithm.cs @@ -0,0 +1,166 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Immutable; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Metadata; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using ILCompiler.Logging; + +using static ILCompiler.Dataflow.DynamicallyAccessedMembersBinder; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using MethodAttributes = System.Reflection.MethodAttributes; +using DynamicallyAccessedMemberTypes = ILCompiler.Dataflow.DynamicallyAccessedMemberTypes; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Computes the list of dependencies from DynamicDependencyAttribute. + /// https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.dynamicdependencyattribute + /// + internal class DynamicDependencyAttributeAlgorithm + { + public static void AddDependenciesDueToDynamicDependencyAttribute(ref DependencyList dependencies, NodeFactory factory, EcmaMethod method) + { + foreach (var attribute in method.GetDecodedCustomAttributes("System.Diagnostics.CodeAnalysis", "DynamicDependencyAttribute")) + { + IEnumerable members; + + static MetadataType Linkerify(TypeDesc type) + { + // IL Linker compatibility: illink will call Resolve() that will strip parameter types and genericness + // and operate on the definition. + while (type.IsParameterizedType) + type = ((ParameterizedType)type).ParameterType; + return (MetadataType)type.GetTypeDefinition(); + } + + // First figure out the list of members that this maps to. + // These are the ways to specify the members: + // + // * A string that contains a documentation signature + // * DynamicallyAccessedMembers enum + + var fixedArgs = attribute.FixedArguments; + TypeDesc targetType; + + if (fixedArgs.Length > 0 && fixedArgs[0].Value is string sigFromAttribute) + { + if (fixedArgs.Length == 1) + { + // DynamicDependencyAttribute(String) + targetType = method.OwningType; + } + else if (fixedArgs.Length == 2 && fixedArgs[1].Value is TypeDesc typeFromAttribute) + { + // DynamicDependencyAttribute(String, Type) + targetType = typeFromAttribute; + } + else if (fixedArgs.Length == 3 && fixedArgs[1].Value is string typeStringFromAttribute + && fixedArgs[2].Value is string assemblyStringFromAttribute) + { + // DynamicDependencyAttribute(String, String, String) + ModuleDesc asm = factory.TypeSystemContext.ResolveAssembly(new System.Reflection.AssemblyName(assemblyStringFromAttribute), throwIfNotFound: false); + if (asm == null) + { + // _context.LogWarning($"Unresolved assembly '{dynamicDependency.AssemblyName}' in 'DynamicDependencyAttribute'", 2035, context); + continue; + } + + targetType = DocumentationSignatureParser.GetTypeByDocumentationSignature((IAssemblyDesc)asm, typeStringFromAttribute); + if (targetType == null) + { + // _context.LogWarning ($"Unresolved type '{typeName}' in DynamicDependencyAttribute", 2036, context); + continue; + } + } + else + { + Debug.Fail("Did we introduce a new overload?"); + continue; + } + + members = DocumentationSignatureParser.GetMembersByDocumentationSignature(Linkerify(targetType), sigFromAttribute, acceptName: true); + } + else if (fixedArgs.Length > 0 && fixedArgs[0].Value is int memberTypesFromAttribute) + { + if (fixedArgs.Length == 2 && fixedArgs[1].Value is TypeDesc typeFromAttribute) + { + // DynamicDependencyAttribute(DynamicallyAccessedMemberTypes, Type) + targetType = typeFromAttribute; + } + else if (fixedArgs.Length == 3 && fixedArgs[1].Value is string typeStringFromAttribute + && fixedArgs[2].Value is string assemblyStringFromAttribute) + { + // DynamicDependencyAttribute(DynamicallyAccessedMemberTypes, String, String) + ModuleDesc asm = factory.TypeSystemContext.ResolveAssembly(new System.Reflection.AssemblyName(assemblyStringFromAttribute), throwIfNotFound: false); + if (asm == null) + { + // _context.LogWarning($"Unresolved assembly '{dynamicDependency.AssemblyName}' in 'DynamicDependencyAttribute'", 2035, context); + continue; + } + + targetType = DocumentationSignatureParser.GetTypeByDocumentationSignature((IAssemblyDesc)asm, typeStringFromAttribute); + if (targetType == null) + { + // _context.LogWarning ($"Unresolved type '{typeName}' in DynamicDependencyAttribute", 2036, context); + continue; + } + } + else + { + Debug.Fail("Did we introduce a new overload?"); + continue; + } + + members = Linkerify(targetType).GetDynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)memberTypesFromAttribute); + } + else + { + Debug.Fail("Did we introduce a new overload?"); + continue; + } + + const string reason = "DynamicDependencyAttribute"; + + // Now root the discovered members + foreach (var member in members) + { + switch (member) + { + case MethodDesc m: + RootingHelpers.TryGetDependenciesForReflectedMethod(ref dependencies, factory, m, reason); + break; + case FieldDesc field: + RootingHelpers.TryGetDependenciesForReflectedField(ref dependencies, factory, field, reason); + break; + case MetadataType nestedType: + RootingHelpers.TryGetDependenciesForReflectedType(ref dependencies, factory, nestedType, reason); + break; + case PropertyPseudoDesc property: + if (property.GetMethod != null) + RootingHelpers.TryGetDependenciesForReflectedMethod(ref dependencies, factory, property.GetMethod, reason); + if (property.SetMethod != null) + RootingHelpers.TryGetDependenciesForReflectedMethod(ref dependencies, factory, property.SetMethod, reason); + break; + case EventPseudoDesc @event: + if (@event.AddMethod != null) + RootingHelpers.TryGetDependenciesForReflectedMethod(ref dependencies, factory, @event.AddMethod, reason); + if (@event.RemoveMethod != null) + RootingHelpers.TryGetDependenciesForReflectedMethod(ref dependencies, factory, @event.RemoveMethod, reason); + break; + default: + Debug.Fail(member.GetType().ToString()); + break; + } + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicInvokeTemplateDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicInvokeTemplateDataNode.cs new file mode 100644 index 00000000000000..87be95102005d4 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicInvokeTemplateDataNode.cs @@ -0,0 +1,150 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a map between method name / signature and CanonicalEntryPoint for the corresponding invoke stub. + /// The first entry is the containing type of the invoke stubs. + /// + internal sealed class DynamicInvokeTemplateDataNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + private Dictionary _methodToTemplateIndex; + + public DynamicInvokeTemplateDataNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__dynamic_invoke_template_data_end", true); + _externalReferences = externalReferences; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__dynamic_invoke_template_data"); + } + + public ISymbolNode EndSymbol => _endSymbol; + public int Offset => 0; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool IsShareable => false; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.GetDynamicInvokeTemplateMethods().GetEnumerator().MoveNext(); + + public int GetIdForMethod(MethodDesc dynamicInvokeMethod, NodeFactory factory) + { + // We should only see canonical or non-shared methods here + Debug.Assert(dynamicInvokeMethod.GetCanonMethodTarget(CanonicalFormKind.Specific) == dynamicInvokeMethod); + Debug.Assert(!dynamicInvokeMethod.IsCanonicalMethod(CanonicalFormKind.Universal)); + + if (_methodToTemplateIndex == null) + { + BuildMethodToIdMap(factory); + } + + return _methodToTemplateIndex[dynamicInvokeMethod]; + } + + private void BuildMethodToIdMap(NodeFactory factory) + { + List methods = new List(factory.MetadataManager.GetDynamicInvokeTemplateMethods()); + + // Sort the stubs + var typeSystemComparer = new TypeSystemComparer(); + methods.Sort((first, second) => typeSystemComparer.Compare(first, second)); + + // Assign each stub an ID + var methodToTemplateIndex = new Dictionary(); + foreach (MethodDesc method in methods) + { + TypeDesc dynamicInvokeMethodContainingType = method.OwningType; + + int templateIndex = (2 * methodToTemplateIndex.Count) + 1; + // Add 1 to the index to account for the first blob entry being the containing MethodTable RVA + methodToTemplateIndex.Add(method, templateIndex); + } + + _methodToTemplateIndex = methodToTemplateIndex; + } + + internal static DependencyListEntry[] GetDependenciesDueToInvokeTemplatePresence(NodeFactory factory, MethodDesc method) + { + return new[] + { + new DependencyListEntry(factory.MethodEntrypoint(method), "Dynamic invoke stub"), + new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(method)), "Dynamic invoke stub"), + new DependencyListEntry(factory.NecessaryTypeSymbol(method.OwningType), "Dynamic invoke stub containing type"), + new DependencyListEntry(factory.NativeLayout.TemplateMethodLayout(method), "Template"), + new DependencyListEntry(factory.NativeLayout.TemplateMethodEntry(method), "Template"), + }; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + // Ensure the native layout blob has been saved + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialPointerAlignment(); + objData.AddSymbol(this); + + if (_methodToTemplateIndex == null) + { + BuildMethodToIdMap(factory); + } + + TypeDesc containerType = null; + foreach (var method in _methodToTemplateIndex.Keys) + { + Debug.Assert(containerType == null || containerType == method.OwningType); + containerType = method.OwningType; +#if !DEBUG + break; +#endif + } + + if (factory.Target.SupportsRelativePointers) + objData.EmitReloc(factory.NecessaryTypeSymbol(containerType), RelocType.IMAGE_REL_BASED_RELPTR32); + else + objData.EmitPointerReloc(factory.NecessaryTypeSymbol(containerType)); + + List> sortedList = new List>(_methodToTemplateIndex); + sortedList.Sort((firstEntry, secondEntry) => firstEntry.Value.CompareTo(secondEntry.Value)); + + for (int i = 0; i < sortedList.Count; i++) + { + var nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(sortedList[i].Key)); + + if (factory.Target.SupportsRelativePointers) + { + objData.EmitInt(nameAndSig.SavedVertex.VertexOffset); + objData.EmitReloc(factory.MethodEntrypoint(sortedList[i].Key), RelocType.IMAGE_REL_BASED_RELPTR32); + } + else + { + objData.EmitNaturalInt(nameAndSig.SavedVertex.VertexOffset); + objData.EmitPointerReloc(factory.MethodEntrypoint(sortedList[i].Key)); + } + } + + _endSymbol.SetSymbolOffset(objData.CountBytes); + objData.AddSymbol(_endSymbol); + + return objData.ToObjectData(); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.DynamicInvokeTemplateDataNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicInvokeTemplateNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicInvokeTemplateNode.cs new file mode 100644 index 00000000000000..aebec11310545e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicInvokeTemplateNode.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Collections.Generic; + +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Models dependencies of the dynamic invoke methods that aid in reflection invoking methods. + /// Dynamic invoke methods are shared method bodies that perform calling convention conversion + /// from object[] to the expected signature of the called method. + /// + internal class DynamicInvokeTemplateNode : DependencyNodeCore + { + public MethodDesc Method { get; } + + public DynamicInvokeTemplateNode(MethodDesc method) + { + Debug.Assert(method.IsSharedByGenericInstantiations); + Method = method; + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + return DynamicInvokeTemplateDataNode.GetDependenciesDueToInvokeTemplatePresence(factory, Method); + } + + protected override string GetName(NodeFactory factory) + { + return "DynamicInvokeTemplate: " + factory.NameMangler.GetMangledMethodName(Method); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs new file mode 100644 index 00000000000000..6aef1df4d46ff7 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -0,0 +1,1265 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.IL; +using Internal.Runtime; +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; +using GenericVariance = Internal.Runtime.GenericVariance; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Given a type, EETypeNode writes an MethodTable data structure in the format expected by the runtime. + /// + /// Format of an MethodTable: + /// + /// Field Size | Contents + /// ----------------+----------------------------------- + /// UInt16 | Component Size. For arrays this is the element type size, for strings it is 2 (.NET uses + /// | UTF16 character encoding), for generic type definitions it is the number of generic parameters, + /// | and 0 for all other types. + /// | + /// UInt16 | EETypeKind (Normal, Array, Pointer type). Flags for: IsValueType, IsCrossModule, HasPointers, + /// | HasOptionalFields, IsInterface, IsGeneric. Top 5 bits are used for enum EETypeElementType to + /// | record whether it's back by an Int32, Int16 etc + /// | + /// Uint32 | Base size. + /// | + /// [Pointer Size] | Related type. Base type for regular types. Element type for arrays / pointer types. + /// | + /// UInt16 | Number of VTable slots (X) + /// | + /// UInt16 | Number of interfaces implemented by type (Y) + /// | + /// UInt32 | Hash code + /// | + /// X * [Ptr Size] | VTable entries (optional) + /// | + /// Y * [Ptr Size] | Pointers to interface map data structures (optional) + /// | + /// [Relative ptr] | Pointer to containing TypeManager indirection cell + /// | + /// [Relative ptr] | Pointer to writable data + /// | + /// [Relative ptr] | Pointer to finalizer method (optional) + /// | + /// [Relative ptr] | Pointer to optional fields (optional) + /// | + /// [Relative ptr] | Pointer to the generic type definition MethodTable (optional) + /// | + /// [Relative ptr] | Pointer to the generic argument and variance info (optional) + /// + public partial class EETypeNode : ObjectNode, IEETypeNode, ISymbolDefinitionNode, ISymbolNodeWithLinkage + { + protected readonly TypeDesc _type; + internal readonly EETypeOptionalFieldsBuilder _optionalFieldsBuilder = new EETypeOptionalFieldsBuilder(); + internal readonly EETypeOptionalFieldsNode _optionalFieldsNode; + protected bool? _mightHaveInterfaceDispatchMap; + private bool _hasConditionalDependenciesFromMetadataManager; + + public EETypeNode(NodeFactory factory, TypeDesc type) + { + if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) + Debug.Assert(this is CanonicalDefinitionEETypeNode); + else if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + Debug.Assert((this is CanonicalEETypeNode) || (this is NecessaryCanonicalEETypeNode)); + + Debug.Assert(!type.IsRuntimeDeterminedSubtype); + _type = type; + _optionalFieldsNode = new EETypeOptionalFieldsNode(this); + _hasConditionalDependenciesFromMetadataManager = factory.MetadataManager.HasConditionalDependenciesDueToEETypePresence(type); + + factory.TypeSystemContext.EnsureLoadableType(type); + + // We don't have a representation for function pointers right now + if (WithoutParameterizeTypes(type).IsFunctionPointer) + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + + static TypeDesc WithoutParameterizeTypes(TypeDesc t) => t is ParameterizedType pt ? WithoutParameterizeTypes(pt.ParameterType) : t; + } + + protected bool MightHaveInterfaceDispatchMap(NodeFactory factory) + { + if (!_mightHaveInterfaceDispatchMap.HasValue) + { + _mightHaveInterfaceDispatchMap = EmitVirtualSlotsAndInterfaces && InterfaceDispatchMapNode.MightHaveInterfaceDispatchMap(_type, factory); + } + + return _mightHaveInterfaceDispatchMap.Value; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) + { + // If there is a constructed version of this node in the graph, emit that instead + if (ConstructedEETypeNode.CreationAllowed(_type)) + return factory.ConstructedTypeSymbol(_type).Marked; + + return false; + } + + public virtual ISymbolNode NodeForLinkage(NodeFactory factory) + { + return factory.NecessaryTypeSymbol(_type); + } + + public TypeDesc Type => _type; + + public override ObjectNodeSection Section + { + get + { + if (_type.Context.Target.IsWindows) + return ObjectNodeSection.ReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + + public int MinimumObjectSize => _type.Context.Target.PointerSize * 3; + + protected virtual bool EmitVirtualSlotsAndInterfaces => false; + + public override bool InterestingForDynamicDependencyAnalysis + { + get + { + if (!EmitVirtualSlotsAndInterfaces) + return false; + + if (_type.IsInterface) + return false; + + if (_type.IsDefType) + { + // First, check if this type has any GVM that overrides a GVM on a parent type. If that's the case, this makes + // the current type interesting for GVM analysis (i.e. instantiate its overriding GVMs for existing GVMDependenciesNodes + // of the instantiated GVM on the parent types). + foreach (var method in _type.GetAllVirtualMethods()) + { + Debug.Assert(method.IsVirtual); + + if (method.HasInstantiation) + { + MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); + if (slotDecl != method) + return true; + } + } + + // Second, check if this type has any GVMs that implement any GVM on any of the implemented interfaces. This would + // make the current type interesting for dynamic dependency analysis to that we can instantiate its GVMs. + foreach (DefType interfaceImpl in _type.RuntimeInterfaces) + { + foreach (var method in interfaceImpl.GetAllVirtualMethods()) + { + Debug.Assert(method.IsVirtual); + + if (method.HasInstantiation) + { + // We found a GVM on one of the implemented interfaces. Find if the type implements this method. + // (Note, do this comparision against the generic definition of the method, not the specific method instantiation + MethodDesc genericDefinition = method.GetMethodDefinition(); + MethodDesc slotDecl = _type.ResolveInterfaceMethodTarget(genericDefinition); + if (slotDecl != null) + { + // If the type doesn't introduce this interface method implementation (i.e. the same implementation + // already exists in the base type), do not consider this type interesting for GVM analysis just yet. + // + // We need to limit the number of types that are interesting for GVM analysis at all costs since + // these all will be looked at for every unique generic virtual method call in the program. + // Having a long list of interesting types affects the compilation throughput heavily. + if (slotDecl.OwningType == _type || + _type.BaseType.ResolveInterfaceMethodTarget(genericDefinition) != slotDecl) + { + return true; + } + } + else + { + // The method could be implemented by a default interface method + var resolution = _type.ResolveInterfaceMethodToDefaultImplementationOnType(genericDefinition, out slotDecl); + if (resolution == DefaultInterfaceMethodResolution.DefaultImplementation) + { + return true; + } + } + } + } + } + } + return false; + } + } + + internal bool HasOptionalFields + { + get { return _optionalFieldsBuilder.IsAtLeastOneFieldUsed(); } + } + + internal byte[] GetOptionalFieldsData() + { + return _optionalFieldsBuilder.GetBytes(); + } + + public override bool StaticDependenciesAreComputed => true; + + public static string GetMangledName(TypeDesc type, NameMangler nameMangler) + { + return nameMangler.NodeMangler.MethodTable(type); + } + + public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.NodeMangler.MethodTable(_type)); + } + + int ISymbolNode.Offset => 0; + int ISymbolDefinitionNode.Offset => GCDescSize; + + public override bool IsShareable => IsTypeNodeShareable(_type); + + private bool CanonFormTypeMayExist + { + get + { + if (!_type.HasInstantiation) + return false; + + if (!_type.Context.SupportsCanon) + return false; + + // If type is already in canon form, a canonically equivalent type cannot exist + if (_type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; + + // If we reach here, a universal canon variant can exist (if universal canon is supported) + if (_type.Context.SupportsUniversalCanon) + return true; + + // Attempt to convert to canon. If the type changes, then the CanonForm exists + return (_type.ConvertToCanonForm(CanonicalFormKind.Specific) != _type); + } + } + + public sealed override bool HasConditionalStaticDependencies + { + get + { + // If the type is can be converted to some interesting canon type, and this is the non-constructed variant of an MethodTable + // we may need to trigger the fully constructed type to exist to make the behavior of the type consistent + // in reflection and generic template expansion scenarios + if (CanonFormTypeMayExist) + { + return true; + } + + if (!EmitVirtualSlotsAndInterfaces) + return false; + + // Since the vtable is dependency driven, generate conditional static dependencies for + // all possible vtable entries. + // + // The conditional dependencies conditionally add the implementation of the virtual method + // if the virtual method is used. + // + // We walk the inheritance chain because abstract bases would only add a "tentative" + // method body of the implementation that can be trimmed away if no other type uses it. + DefType currentType = _type.GetClosestDefType(); + while (currentType != null) + { + if (currentType == _type || (currentType is MetadataType mdType && mdType.IsAbstract)) + { + foreach (var method in currentType.GetAllVirtualMethods()) + { + // Abstract methods don't have a body associated with it so there's no conditional + // dependency to add. + // Generic virtual methods are tracked by an orthogonal mechanism. + if (!method.IsAbstract && !method.HasInstantiation) + return true; + } + } + + currentType = currentType.BaseType; + } + + // If the type implements at least one interface, calls against that interface could result in this type's + // implementation being used. + if (_type.RuntimeInterfaces.Length > 0) + return true; + + return _hasConditionalDependenciesFromMetadataManager; + } + } + + public sealed override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + List result = new List(); + + IEETypeNode maximallyConstructableType = factory.MaximallyConstructableType(_type); + + if (maximallyConstructableType != this) + { + // MethodTable upgrading from necessary to constructed if some template instantation exists that matches up + // This ensures we don't end up having two EETypes in the system (one is this necessary type, and another one + // that was dynamically created at runtime). + if (CanonFormTypeMayExist) + { + result.Add(new CombinedDependencyListEntry(maximallyConstructableType, factory.MaximallyConstructableType(_type.ConvertToCanonForm(CanonicalFormKind.Specific)), "Trigger full type generation if canonical form exists")); + + if (_type.Context.SupportsUniversalCanon) + result.Add(new CombinedDependencyListEntry(maximallyConstructableType, factory.MaximallyConstructableType(_type.ConvertToCanonForm(CanonicalFormKind.Universal)), "Trigger full type generation if universal canonical form exists")); + } + return result; + } + + if (!EmitVirtualSlotsAndInterfaces) + return result; + + DefType defType = _type.GetClosestDefType(); + + // If we're producing a full vtable, none of the dependencies are conditional. + if (!factory.VTable(defType).HasFixedSlots) + { + bool isNonInterfaceAbstractType = !defType.IsInterface && ((MetadataType)defType).IsAbstract; + + foreach (MethodDesc decl in defType.EnumAllVirtualSlots()) + { + // Generic virtual methods are tracked by an orthogonal mechanism. + if (decl.HasInstantiation) + continue; + + MethodDesc impl = defType.FindVirtualFunctionTargetMethodOnObjectType(decl); + bool implOwnerIsAbstract = ((MetadataType)impl.OwningType).IsAbstract; + + // We add a conditional dependency in two situations: + // 1. The implementation is on this type. This is pretty obvious. + // 2. The implementation comes from an abstract base type. We do this + // because abstract types only request a TentativeMethodEntrypoint of the implementation. + // The actual method body of this entrypoint might still be trimmed away. + // We don't need to do this for implementations from non-abstract bases since + // non-abstract types will create a hard conditional reference to their virtual + // method implementations. + // + // We also skip abstract methods since they don't have a body to refer to. + if ((impl.OwningType == defType || implOwnerIsAbstract) && !impl.IsAbstract) + { + MethodDesc canonImpl = impl.GetCanonMethodTarget(CanonicalFormKind.Specific); + + // If this is an abstract type, only request a tentative entrypoint (whose body + // might just be stubbed out). This lets us avoid generating method bodies for + // virtual method on abstract types that are overriden in all their children. + // + // We don't do this if the method can be placed in the sealed vtable since + // those can never be overriden by children anyway. + bool canUseTentativeMethod = isNonInterfaceAbstractType + && !decl.CanMethodBeInSealedVTable() + && factory.CompilationModuleGroup.AllowVirtualMethodOnAbstractTypeOptimization(canonImpl); + IMethodNode implNode = canUseTentativeMethod ? + factory.TentativeMethodEntrypoint(canonImpl, impl.OwningType.IsValueType) : + factory.MethodEntrypoint(canonImpl, impl.OwningType.IsValueType); + result.Add(new CombinedDependencyListEntry(implNode, factory.VirtualMethodUse(decl), "Virtual method")); + } + + if (impl.OwningType == defType) + { + factory.MetadataManager.NoteOverridingMethod(decl, impl); + } + } + + Debug.Assert( + _type == defType || + ((System.Collections.IStructuralEquatable)defType.RuntimeInterfaces).Equals(_type.RuntimeInterfaces, + EqualityComparer.Default)); + + // Add conditional dependencies for interface methods the type implements. For example, if the type T implements + // interface IFoo which has a method M1, add a dependency on T.M1 dependent on IFoo.M1 being called, since it's + // possible for any IFoo object to actually be an instance of T. + DefType[] defTypeRuntimeInterfaces = defType.RuntimeInterfaces; + for (int interfaceIndex = 0; interfaceIndex < defTypeRuntimeInterfaces.Length; interfaceIndex++) + { + DefType interfaceType = defTypeRuntimeInterfaces[interfaceIndex]; + + Debug.Assert(interfaceType.IsInterface); + + bool isVariantInterfaceImpl = VariantInterfaceMethodUseNode.IsVariantInterfaceImplementation(factory, _type, interfaceType); + + foreach (MethodDesc interfaceMethod in interfaceType.GetAllVirtualMethods()) + { + // Generic virtual methods are tracked by an orthogonal mechanism. + if (interfaceMethod.HasInstantiation) + continue; + + // Static virtual methods are resolved at compile time + if (interfaceMethod.Signature.IsStatic) + continue; + + MethodDesc implMethod = defType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod); + if (implMethod != null) + { + result.Add(new CombinedDependencyListEntry(factory.VirtualMethodUse(implMethod), factory.VirtualMethodUse(interfaceMethod), "Interface method")); + + // If any of the implemented interfaces have variance, calls against compatible interface methods + // could result in interface methods of this type being used (e.g. IEnumerable.GetEnumerator() + // can dispatch to an implementation of IEnumerable.GetEnumerator()). + if (isVariantInterfaceImpl) + { + MethodDesc typicalInterfaceMethod = interfaceMethod.GetTypicalMethodDefinition(); + result.Add(new CombinedDependencyListEntry(factory.VirtualMethodUse(implMethod), factory.VariantInterfaceMethodUse(typicalInterfaceMethod), "Interface method")); + result.Add(new CombinedDependencyListEntry(factory.VirtualMethodUse(interfaceMethod), factory.VariantInterfaceMethodUse(typicalInterfaceMethod), "Interface method")); + } + + factory.MetadataManager.NoteOverridingMethod(interfaceMethod, implMethod); + } + else + { + // Is the implementation provided by a default interface method? + // If so, add a dependency on the entrypoint directly since nobody else is going to do that + // (interface types have an empty vtable, modulo their generic dictionary). + TypeDesc interfaceOnDefinition = defType.GetTypeDefinition().RuntimeInterfaces[interfaceIndex]; + MethodDesc interfaceMethodDefinition = interfaceMethod; + if (!interfaceType.IsTypeDefinition) + interfaceMethodDefinition = factory.TypeSystemContext.GetMethodForInstantiatedType(interfaceMethod.GetTypicalMethodDefinition(), (InstantiatedType)interfaceOnDefinition); + + var resolution = defType.GetTypeDefinition().ResolveInterfaceMethodToDefaultImplementationOnType(interfaceMethodDefinition, out implMethod); + if (resolution == DefaultInterfaceMethodResolution.DefaultImplementation) + { + DefType providingInterfaceDefinitionType = (DefType)implMethod.OwningType; + implMethod = implMethod.InstantiateSignature(defType.Instantiation, Instantiation.Empty); + + MethodDesc defaultIntfMethod = implMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (defaultIntfMethod.IsCanonicalMethod(CanonicalFormKind.Any)) + { + defaultIntfMethod = factory.TypeSystemContext.GetDefaultInterfaceMethodImplementationThunk(defaultIntfMethod, _type.ConvertToCanonForm(CanonicalFormKind.Specific), providingInterfaceDefinitionType); + } + result.Add(new CombinedDependencyListEntry(factory.MethodEntrypoint(defaultIntfMethod), factory.VirtualMethodUse(interfaceMethod), "Interface method")); + + factory.MetadataManager.NoteOverridingMethod(interfaceMethod, implMethod); + } + } + } + } + } + + factory.MetadataManager.GetConditionalDependenciesDueToEETypePresence(ref result, factory, _type); + + return result; + } + + public static bool IsTypeNodeShareable(TypeDesc type) + { + return type.IsParameterizedType || type.IsFunctionPointer || type is InstantiatedType; + } + + internal static bool MethodHasNonGenericILMethodBody(MethodDesc method) + { + // Generic methods have their own generic dictionaries + if (method.HasInstantiation) + return false; + + // Abstract methods don't have a body + if (method.IsAbstract) + return false; + + // PInvoke methods are not permitted on generic types, + // but let's not crash the compilation because of that. + if (method.IsPInvoke) + return false; + + // CoreRT can generate method bodies for these no matter what (worst case + // they'll be throwing). We don't want to take the "return false" code path on CoreRT because + // delegate methods fall into the runtime implemented category on CoreRT, but we + // just treat them like regular method bodies. + return true; + } + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + + // Include the optional fields by default. We don't know if optional fields will be needed until + // all of the interface usage has been stabilized. If we end up not needing it, the MethodTable node will not + // generate any relocs to it, and the optional fields node will instruct the object writer to skip + // emitting it. + dependencies.Add(new DependencyListEntry(_optionalFieldsNode, "Optional fields")); + + // TODO-SIZE: We probably don't need to add these for all EETypes + StaticsInfoHashtableNode.AddStaticsInfoDependencies(ref dependencies, factory, _type); + + if (EmitVirtualSlotsAndInterfaces) + { + if (!_type.IsArrayTypeWithoutGenericInterfaces()) + { + // Sealed vtables have relative pointers, so to minimize size, we build sealed vtables for the canonical types + dependencies.Add(new DependencyListEntry(factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific)), "Sealed Vtable")); + } + + // Also add the un-normalized vtable slices of implemented interfaces. + // This is important to do in the scanning phase so that the compilation phase can find + // vtable information for things like IEnumerator>. + foreach (TypeDesc intface in _type.RuntimeInterfaces) + dependencies.Add(factory.VTable(intface), "Interface vtable slice"); + + // Generated type contains generic virtual methods that will get added to the GVM tables + if (TypeGVMEntriesNode.TypeNeedsGVMTableEntries(_type)) + { + dependencies.Add(new DependencyListEntry(factory.TypeGVMEntries(_type.GetTypeDefinition()), "Type with generic virtual methods")); + + AddDependenciesForUniversalGVMSupport(factory, _type, ref dependencies); + + TypeDesc canonicalType = _type.ConvertToCanonForm(CanonicalFormKind.Specific); + if (canonicalType != _type) + dependencies.Add(factory.ConstructedTypeSymbol(canonicalType), "Type with generic virtual methods"); + } + } + + if (factory.CompilationModuleGroup.PresenceOfEETypeImpliesAllMethodsOnType(_type)) + { + if (_type.IsArray || _type.IsDefType) + { + // If the compilation group wants this type to be fully promoted, ensure that all non-generic methods of the + // type are generated. + // This may be done for several reasons: + // - The MethodTable may be going to be COMDAT folded with other EETypes generated in a different object file + // This means their generic dictionaries need to have identical contents. The only way to achieve that is + // by generating the entries for all methods that contribute to the dictionary, and sorting the dictionaries. + // - The generic type may be imported into another module, in which case the generic dictionary imported + // must represent all of the methods, as the set of used methods cannot be known at compile time + // - As a matter of policy, the type and its methods may be exported for use in another module. The policy + // may wish to specify that if a type is to be placed into a shared module, all of the methods associated with + // it should be also be exported. + foreach (var method in _type.GetClosestDefType().ConvertToCanonForm(CanonicalFormKind.Specific).GetAllMethods()) + { + if (!MethodHasNonGenericILMethodBody(method)) + continue; + + dependencies.Add(factory.MethodEntrypoint(method.GetCanonMethodTarget(CanonicalFormKind.Specific)), + "Ensure all methods on type due to CompilationModuleGroup policy"); + } + } + } + + if (!ConstructedEETypeNode.CreationAllowed(_type)) + { + // If necessary MethodTable is the highest load level for this type, ask the metadata manager + // if we have any dependencies due to reflectability. + factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencies, factory, _type); + + // If necessary MethodTable is the highest load level, consider this a module use + if (_type is MetadataType mdType + && mdType.Module.GetGlobalModuleType().GetStaticConstructor() is MethodDesc moduleCctor) + { + dependencies.Add(factory.MethodEntrypoint(moduleCctor), "Type in a module with initializer"); + } + } + + return dependencies; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialPointerAlignment(); + objData.AddSymbol(this); + + ComputeOptionalEETypeFields(factory, relocsOnly); + + OutputGCDesc(ref objData); + OutputComponentSize(ref objData); + OutputFlags(factory, ref objData); + objData.EmitInt(BaseSize); + OutputRelatedType(factory, ref objData); + + // Number of vtable slots will be only known later. Reseve the bytes for it. + var vtableSlotCountReservation = objData.ReserveShort(); + + // Number of interfaces will only be known later. Reserve the bytes for it. + var interfaceCountReservation = objData.ReserveShort(); + + objData.EmitInt(_type.GetHashCode()); + + if (EmitVirtualSlotsAndInterfaces) + { + // Emit VTable + Debug.Assert(objData.CountBytes - ((ISymbolDefinitionNode)this).Offset == GetVTableOffset(objData.TargetPointerSize)); + SlotCounter virtualSlotCounter = SlotCounter.BeginCounting(ref /* readonly */ objData); + OutputVirtualSlots(factory, ref objData, _type, _type, _type, relocsOnly); + + // Update slot count + int numberOfVtableSlots = virtualSlotCounter.CountSlots(ref /* readonly */ objData); + objData.EmitShort(vtableSlotCountReservation, checked((short)numberOfVtableSlots)); + + // Emit interface map + SlotCounter interfaceSlotCounter = SlotCounter.BeginCounting(ref /* readonly */ objData); + OutputInterfaceMap(factory, ref objData); + + // Update slot count + int numberOfInterfaceSlots = interfaceSlotCounter.CountSlots(ref /* readonly */ objData); + objData.EmitShort(interfaceCountReservation, checked((short)numberOfInterfaceSlots)); + + } + else + { + // If we're not emitting any slots, the number of slots is zero. + objData.EmitShort(vtableSlotCountReservation, 0); + objData.EmitShort(interfaceCountReservation, 0); + } + + OutputTypeManagerIndirection(factory, ref objData); + OutputWritableData(factory, ref objData); + OutputFinalizerMethod(factory, ref objData); + OutputOptionalFields(factory, ref objData); + OutputSealedVTable(factory, relocsOnly, ref objData); + OutputGenericInstantiationDetails(factory, ref objData); + + return objData.ToObjectData(); + } + + /// + /// Returns the offset within an MethodTable of the beginning of VTable entries + /// + /// The size of a pointer in bytes in the target architecture + public static int GetVTableOffset(int pointerSize) + { + return 16 + pointerSize; + } + + protected virtual int GCDescSize => 0; + + protected virtual void OutputGCDesc(ref ObjectDataBuilder builder) + { + // Non-constructed EETypeNodes get no GC Desc + Debug.Assert(GCDescSize == 0); + } + + private void OutputComponentSize(ref ObjectDataBuilder objData) + { + if (_type.IsArray) + { + TypeDesc elementType = ((ArrayType)_type).ElementType; + if (elementType == elementType.Context.UniversalCanonType) + { + objData.EmitShort(0); + } + else + { + int elementSize = elementType.GetElementSize().AsInt; + // We validated that this will fit the short when the node was constructed. No need for nice messages. + objData.EmitShort((short)checked((ushort)elementSize)); + } + } + else if (_type.IsString) + { + objData.EmitShort(StringComponentSize.Value); + } + else + { + objData.EmitShort(0); + } + } + + private void OutputFlags(NodeFactory factory, ref ObjectDataBuilder objData) + { + UInt16 flags = EETypeBuilderHelpers.ComputeFlags(_type); + + if (_type.GetTypeDefinition() == factory.ArrayOfTEnumeratorType) + { + // Generic array enumerators use special variance rules recognized by the runtime + flags |= (UInt16)EETypeFlags.GenericVarianceFlag; + } + + if (factory.TypeSystemContext.IsGenericArrayInterfaceType(_type)) + { + // Runtime casting logic relies on all interface types implemented on arrays + // to have the variant flag set (even if all the arguments are non-variant). + // This supports e.g. casting uint[] to ICollection + flags |= (UInt16)EETypeFlags.GenericVarianceFlag; + } + + if (_type.IsIDynamicInterfaceCastable) + { + flags |= (UInt16)EETypeFlags.IDynamicInterfaceCastableFlag; + } + + ISymbolNode relatedTypeNode = GetRelatedTypeNode(factory); + + // If the related type (base type / array element type / pointee type) is not part of this compilation group, and + // the output binaries will be multi-file (not multiple object files linked together), indicate to the runtime + // that it should indirect through the import address table + if (relatedTypeNode != null && relatedTypeNode.RepresentsIndirectionCell) + { + flags |= (UInt16)EETypeFlags.RelatedTypeViaIATFlag; + } + + if (HasOptionalFields) + { + flags |= (UInt16)EETypeFlags.OptionalFieldsFlag; + } + + if (this is ClonedConstructedEETypeNode) + { + flags |= (UInt16)EETypeKind.ClonedEEType; + } + + objData.EmitShort((short)flags); + } + + protected virtual int BaseSize + { + get + { + int pointerSize = _type.Context.Target.PointerSize; + int objectSize; + + if (_type.IsDefType) + { + LayoutInt instanceByteCount = ((DefType)_type).InstanceByteCount; + + if (instanceByteCount.IsIndeterminate) + { + // Some value must be put in, but the specific value doesn't matter as it + // isn't used for specific instantiations, and the universal canon MethodTable + // is never associated with an allocated object. + objectSize = pointerSize; + } + else + { + objectSize = pointerSize + + ((DefType)_type).InstanceByteCount.AsInt; // +pointerSize for SyncBlock + } + + if (_type.IsValueType) + objectSize += pointerSize; // + EETypePtr field inherited from System.Object + } + else if (_type.IsArray) + { + objectSize = 3 * pointerSize; // SyncBlock + EETypePtr + Length + if (_type.IsMdArray) + objectSize += + 2 * sizeof(int) * ((ArrayType)_type).Rank; + } + else if (_type.IsPointer) + { + // These never get boxed and don't have a base size. Use a sentinel value recognized by the runtime. + return ParameterizedTypeShapeConstants.Pointer; + } + else if (_type.IsByRef) + { + // These never get boxed and don't have a base size. Use a sentinel value recognized by the runtime. + return ParameterizedTypeShapeConstants.ByRef; + } + else + throw new NotImplementedException(); + + objectSize = AlignmentHelper.AlignUp(objectSize, pointerSize); + objectSize = Math.Max(MinimumObjectSize, objectSize); + + if (_type.IsString) + { + // If this is a string, throw away objectSize we computed so far. Strings are special. + // SyncBlock + EETypePtr + length + firstChar + objectSize = 2 * pointerSize + + sizeof(int) + + StringComponentSize.Value; + } + + return objectSize; + } + } + + protected virtual ISymbolNode GetBaseTypeNode(NodeFactory factory) + { + return _type.BaseType != null ? factory.NecessaryTypeSymbol(_type.BaseType) : null; + } + + private ISymbolNode GetRelatedTypeNode(NodeFactory factory) + { + ISymbolNode relatedTypeNode = null; + + if (_type.IsArray || _type.IsPointer || _type.IsByRef) + { + var parameterType = ((ParameterizedType)_type).ParameterType; + relatedTypeNode = factory.NecessaryTypeSymbol(parameterType); + } + else + { + TypeDesc baseType = _type.BaseType; + if (baseType != null) + { + relatedTypeNode = GetBaseTypeNode(factory); + } + } + + return relatedTypeNode; + } + + protected virtual void OutputRelatedType(NodeFactory factory, ref ObjectDataBuilder objData) + { + ISymbolNode relatedTypeNode = GetRelatedTypeNode(factory); + + if (relatedTypeNode != null) + { + objData.EmitPointerReloc(relatedTypeNode); + } + else + { + objData.EmitZeroPointer(); + } + } + + private void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objData, TypeDesc implType, TypeDesc declType, TypeDesc templateType, bool relocsOnly) + { + Debug.Assert(EmitVirtualSlotsAndInterfaces); + + declType = declType.GetClosestDefType(); + templateType = templateType.ConvertToCanonForm(CanonicalFormKind.Specific); + + var baseType = declType.BaseType; + if (baseType != null) + { + Debug.Assert(templateType.BaseType != null); + OutputVirtualSlots(factory, ref objData, implType, baseType, templateType.BaseType, relocsOnly); + } + + // + // In the universal canonical types case, we could have base types in the hierarchy that are partial universal canonical types. + // The presence of these types could cause incorrect vtable layouts, so we need to fully canonicalize them and walk the + // hierarchy of the template type of the original input type to detect these cases. + // + // Exmaple: we begin with Derived<__UniversalCanon> and walk the template hierarchy: + // + // class Derived : Middle { } // -> Template is Derived<__UniversalCanon> and needs a dictionary slot + // // -> Basetype tempalte is Middle<__UniversalCanon, MyStruct>. It's a partial + // Universal canonical type, so we need to fully canonicalize it. + // + // class Middle : Base { } // -> Template is Middle<__UniversalCanon, __UniversalCanon> and needs a dictionary slot + // // -> Basetype template is Base<__UniversalCanon> + // + // class Base { } // -> Template is Base<__UniversalCanon> and needs a dictionary slot. + // + // If we had not fully canonicalized the Middle class template, we would have ended up with Base, which does not need + // a dictionary slot, meaning we would have created a vtable layout that the runtime does not expect. + // + + // The generic dictionary pointer occupies the first slot of each type vtable slice + if (declType.HasGenericDictionarySlot() || templateType.HasGenericDictionarySlot()) + { + // All generic interface types have a dictionary slot, but only some of them have an actual dictionary. + bool isInterfaceWithAnEmptySlot = declType.IsInterface && + declType.ConvertToCanonForm(CanonicalFormKind.Specific) == declType; + + // Note: Canonical type instantiations always have a generic dictionary vtable slot, but it's empty + // Note: If the current EETypeNode represents a universal canonical type, any dictionary slot must be empty + if (declType.IsCanonicalSubtype(CanonicalFormKind.Any) + || implType.IsCanonicalSubtype(CanonicalFormKind.Universal) + || factory.LazyGenericsPolicy.UsesLazyGenerics(declType) + || isInterfaceWithAnEmptySlot) + objData.EmitZeroPointer(); + else + objData.EmitPointerReloc(factory.TypeGenericDictionary(declType)); + } + + VTableSliceNode declVTable = factory.VTable(declType); + + // It's only okay to touch the actual list of slots if we're in the final emission phase + // or the vtable is not built lazily. + if (relocsOnly && !declVTable.HasFixedSlots) + return; + + // Inteface types don't place anything else in their physical vtable. + // Interfaces have logical slots for their methods but since they're all abstract, they would be zero. + // We place default implementations of interface methods into the vtable of the interface-implementing + // type, pretending there was an extra virtual slot. + if (_type.IsInterface) + return; + + // Actual vtable slots follow + IReadOnlyList virtualSlots = declVTable.Slots; + + for (int i = 0; i < virtualSlots.Count; i++) + { + MethodDesc declMethod = virtualSlots[i]; + + // Object.Finalize shouldn't get a virtual slot. Finalizer is stored in an optional field + // instead: most MethodTable don't have a finalizer, but all EETypes contain Object's vtable. + // This lets us save a pointer (+reloc) on most EETypes. + Debug.Assert(!declType.IsObject || declMethod.Name != "Finalize"); + + // No generic virtual methods can appear in the vtable! + Debug.Assert(!declMethod.HasInstantiation); + + MethodDesc implMethod = implType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(declMethod); + + // Final NewSlot methods cannot be overridden, and therefore can be placed in the sealed-vtable to reduce the size of the vtable + // of this type and any type that inherits from it. + if (declMethod.CanMethodBeInSealedVTable() && !declType.IsArrayTypeWithoutGenericInterfaces()) + continue; + + if (!implMethod.IsAbstract) + { + MethodDesc canonImplMethod = implMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + + // If the type we're generating now is abstract, and the implementation comes from an abstract type, + // only use a tentative method entrypoint that can have its body replaced by a throwing stub + // if no "hard" reference to that entrypoint exists in the program. + // This helps us to eliminate method bodies for virtual methods on abstract types that are fully overriden + // in the children of that abstract type. + bool canUseTentativeEntrypoint = implType is MetadataType mdImplType && mdImplType.IsAbstract && !mdImplType.IsInterface + && implMethod.OwningType is MetadataType mdImplMethodType && mdImplMethodType.IsAbstract + && factory.CompilationModuleGroup.AllowVirtualMethodOnAbstractTypeOptimization(canonImplMethod); + + IMethodNode implSymbol = canUseTentativeEntrypoint ? + factory.TentativeMethodEntrypoint(canonImplMethod, implMethod.OwningType.IsValueType) : + factory.MethodEntrypoint(canonImplMethod, implMethod.OwningType.IsValueType); + objData.EmitPointerReloc(implSymbol); + } + else + { + objData.EmitZeroPointer(); + } + } + } + + protected virtual IEETypeNode GetInterfaceTypeNode(NodeFactory factory, TypeDesc interfaceType) + { + return factory.NecessaryTypeSymbol(interfaceType); + } + + protected virtual void OutputInterfaceMap(NodeFactory factory, ref ObjectDataBuilder objData) + { + Debug.Assert(EmitVirtualSlotsAndInterfaces); + + foreach (var itf in _type.RuntimeInterfaces) + { + objData.EmitPointerRelocOrIndirectionReference(GetInterfaceTypeNode(factory, itf)); + } + } + + private void OutputFinalizerMethod(NodeFactory factory, ref ObjectDataBuilder objData) + { + if (_type.HasFinalizer) + { + MethodDesc finalizerMethod = _type.GetFinalizer(); + MethodDesc canonFinalizerMethod = finalizerMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (factory.Target.SupportsRelativePointers) + objData.EmitReloc(factory.MethodEntrypoint(canonFinalizerMethod), RelocType.IMAGE_REL_BASED_RELPTR32); + else + objData.EmitPointerReloc(factory.MethodEntrypoint(canonFinalizerMethod)); + } + } + + protected void OutputTypeManagerIndirection(NodeFactory factory, ref ObjectDataBuilder objData) + { + if (factory.Target.SupportsRelativePointers) + objData.EmitReloc(factory.TypeManagerIndirection, RelocType.IMAGE_REL_BASED_RELPTR32); + else + objData.EmitPointerReloc(factory.TypeManagerIndirection); + } + + protected void OutputWritableData(NodeFactory factory, ref ObjectDataBuilder objData) + { + if (factory.Target.SupportsRelativePointers) + { + Utf8StringBuilder writableDataBlobName = new Utf8StringBuilder(); + writableDataBlobName.Append("__writableData"); + writableDataBlobName.Append(factory.NameMangler.GetMangledTypeName(_type)); + + BlobNode blob = factory.UninitializedWritableDataBlob(writableDataBlobName.ToUtf8String(), + WritableData.GetSize(factory.Target.PointerSize), WritableData.GetAlignment(factory.Target.PointerSize)); + + objData.EmitReloc(blob, RelocType.IMAGE_REL_BASED_RELPTR32); + } + } + + protected void OutputOptionalFields(NodeFactory factory, ref ObjectDataBuilder objData) + { + if (HasOptionalFields) + { + if (factory.Target.SupportsRelativePointers) + objData.EmitReloc(_optionalFieldsNode, RelocType.IMAGE_REL_BASED_RELPTR32); + else + objData.EmitPointerReloc(_optionalFieldsNode); + } + } + + private void OutputSealedVTable(NodeFactory factory, bool relocsOnly, ref ObjectDataBuilder objData) + { + if (EmitVirtualSlotsAndInterfaces && !_type.IsArrayTypeWithoutGenericInterfaces()) + { + // Sealed vtables have relative pointers, so to minimize size, we build sealed vtables for the canonical types + SealedVTableNode sealedVTable = factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific)); + + if (sealedVTable.BuildSealedVTableSlots(factory, relocsOnly) && sealedVTable.NumSealedVTableEntries > 0) + { + if (factory.Target.SupportsRelativePointers) + objData.EmitReloc(sealedVTable, RelocType.IMAGE_REL_BASED_RELPTR32); + else + objData.EmitPointerReloc(sealedVTable); + } + } + } + + private void OutputGenericInstantiationDetails(NodeFactory factory, ref ObjectDataBuilder objData) + { + if (_type.HasInstantiation && !_type.IsTypeDefinition) + { + IEETypeNode typeDefNode = factory.NecessaryTypeSymbol(_type.GetTypeDefinition()); + if (factory.Target.SupportsRelativePointers) + objData.EmitRelativeRelocOrIndirectionReference(typeDefNode); + else + objData.EmitPointerRelocOrIndirectionReference(typeDefNode); + + GenericCompositionDetails details; + if (_type.GetTypeDefinition() == factory.ArrayOfTEnumeratorType) + { + // Generic array enumerators use special variance rules recognized by the runtime + details = new GenericCompositionDetails(_type.Instantiation, new[] { GenericVariance.ArrayCovariant }); + } + else if (factory.TypeSystemContext.IsGenericArrayInterfaceType(_type)) + { + // Runtime casting logic relies on all interface types implemented on arrays + // to have the variant flag set (even if all the arguments are non-variant). + // This supports e.g. casting uint[] to ICollection + details = new GenericCompositionDetails(_type, forceVarianceInfo: true); + } + else + details = new GenericCompositionDetails(_type); + + ISymbolNode compositionNode = factory.GenericComposition(details); + if (factory.Target.SupportsRelativePointers) + objData.EmitReloc(compositionNode, RelocType.IMAGE_REL_BASED_RELPTR32); + else + objData.EmitPointerReloc(compositionNode); + } + } + + /// + /// Populate the OptionalFieldsRuntimeBuilder if any optional fields are required. + /// + protected internal virtual void ComputeOptionalEETypeFields(NodeFactory factory, bool relocsOnly) + { + if (!relocsOnly && MightHaveInterfaceDispatchMap(factory)) + { + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.DispatchMap, checked((uint)factory.InterfaceDispatchMapIndirection(Type).IndexFromBeginningOfArray)); + } + + ComputeRareFlags(factory, relocsOnly); + ComputeNullableValueOffset(); + ComputeValueTypeFieldPadding(); + } + + void ComputeRareFlags(NodeFactory factory, bool relocsOnly) + { + uint flags = 0; + + MetadataType metadataType = _type as MetadataType; + + if (factory.PreinitializationManager.HasLazyStaticConstructor(_type)) + { + flags |= (uint)EETypeRareFlags.HasCctorFlag; + } + + if (_type.RequiresAlign8()) + { + flags |= (uint)EETypeRareFlags.RequiresAlign8Flag; + } + + TargetArchitecture targetArch = _type.Context.Target.Architecture; + if (metadataType != null && + (targetArch == TargetArchitecture.ARM || + targetArch == TargetArchitecture.ARM64) && + metadataType.IsHomogeneousAggregate) + { + flags |= (uint)EETypeRareFlags.IsHFAFlag; + } + + if (metadataType != null && !_type.IsInterface && metadataType.IsAbstract) + { + flags |= (uint)EETypeRareFlags.IsAbstractClassFlag; + } + + if (_type.IsByRefLike) + { + flags |= (uint)EETypeRareFlags.IsByRefLikeFlag; + } + + if (EmitVirtualSlotsAndInterfaces && !_type.IsArrayTypeWithoutGenericInterfaces()) + { + SealedVTableNode sealedVTable = factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific)); + if (sealedVTable.BuildSealedVTableSlots(factory, relocsOnly) && sealedVTable.NumSealedVTableEntries > 0) + flags |= (uint)EETypeRareFlags.HasSealedVTableEntriesFlag; + } + + if (flags != 0) + { + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.RareFlags, flags); + } + } + + /// + /// To support boxing / unboxing, the offset of the value field of a Nullable type is recorded on the MethodTable. + /// This is variable according to the alignment requirements of the Nullable<T> type parameter. + /// + void ComputeNullableValueOffset() + { + if (!_type.IsNullable) + return; + + if (!_type.Instantiation[0].IsCanonicalSubtype(CanonicalFormKind.Universal)) + { + var field = _type.GetKnownField("value"); + + // In the definition of Nullable, the first field should be the boolean representing "hasValue" + Debug.Assert(field.Offset.AsInt > 0); + + // The contract with the runtime states the Nullable value offset is stored with the boolean "hasValue" size subtracted + // to get a small encoding size win. + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.NullableValueOffset, (uint)field.Offset.AsInt - 1); + } + } + + protected virtual void ComputeValueTypeFieldPadding() + { + // All objects that can have appreciable which can be derived from size compute ValueTypeFieldPadding. + // Unfortunately, the name ValueTypeFieldPadding is now wrong to avoid integration conflicts. + + // Interfaces, sealed types, and non-DefTypes cannot be derived from + if (_type.IsInterface || !_type.IsDefType || (_type.IsSealed() && !_type.IsValueType)) + return; + + DefType defType = _type as DefType; + Debug.Assert(defType != null); + + uint valueTypeFieldPaddingEncoded; + + if (defType.InstanceByteCount.IsIndeterminate) + { + valueTypeFieldPaddingEncoded = EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue(0, 1, _type.Context.Target.PointerSize); + } + else + { + int numInstanceFieldBytes = defType.InstanceByteCountUnaligned.AsInt; + + // Check if we have a type derived from System.ValueType or System.Enum, but not System.Enum itself + if (defType.IsValueType) + { + // Value types should have at least 1 byte of size + Debug.Assert(numInstanceFieldBytes >= 1); + + // The size doesn't currently include the MethodTable pointer size. We need to add this so that + // the number of instance field bytes consistently represents the boxed size. + numInstanceFieldBytes += _type.Context.Target.PointerSize; + } + + // For unboxing to work correctly and for supporting dynamic type loading for derived types we need + // to record the actual size of the fields of a type without any padding for GC heap allocation (since + // we can unbox into locals or arrays where this padding is not used, and because field layout for derived + // types is effected by the unaligned base size). We don't want to store this information for all EETypes + // since it's only relevant for value types, and derivable types so it's added as an optional field. It's + // also enough to simply store the size of the padding (between 0 and 4 or 8 bytes for 32-bit and 0 and 8 or 16 bytes + // for 64-bit) which cuts down our storage requirements. + + uint valueTypeFieldPadding = checked((uint)((BaseSize - _type.Context.Target.PointerSize) - numInstanceFieldBytes)); + valueTypeFieldPaddingEncoded = EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue(valueTypeFieldPadding, (uint)defType.InstanceFieldAlignment.AsInt, _type.Context.Target.PointerSize); + } + + if (valueTypeFieldPaddingEncoded != 0) + { + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.ValueTypeFieldPadding, valueTypeFieldPaddingEncoded); + } + } + + protected override void OnMarked(NodeFactory context) + { + if (!context.IsCppCodegenTemporaryWorkaround) + { + Debug.Assert(_type.IsTypeDefinition || !_type.HasSameTypeDefinition(context.ArrayOfTClass), "Asking for Array MethodTable"); + } + } + + public static void AddDependenciesForStaticsNode(NodeFactory factory, TypeDesc type, ref DependencyList dependencies) + { + // To ensure that the behvior of FieldInfo.GetValue/SetValue remains correct, + // if a type may be reflectable, and it is generic, if a canonical instantiation of reflection + // can exist which can refer to the associated type of this static base, ensure that type + // has an MethodTable. (Which will allow the static field lookup logic to find the right type) + if (type.HasInstantiation && !factory.MetadataManager.IsReflectionBlocked(type)) + { + // TODO-SIZE: This current implementation is slightly generous, as it does not attempt to restrict + // the created types to the maximum extent by investigating reflection data and such. Here we just + // check if we support use of a canonically equivalent type to perform reflection. + // We don't check to see if reflection is enabled on the type. + if (factory.TypeSystemContext.SupportsUniversalCanon + || (factory.TypeSystemContext.SupportsCanon && (type != type.ConvertToCanonForm(CanonicalFormKind.Specific)))) + { + if (dependencies == null) + dependencies = new DependencyList(); + + dependencies.Add(factory.NecessaryTypeSymbol(type), "Static block owning type is necessary for canonically equivalent reflection"); + } + } + } + + protected static void AddDependenciesForUniversalGVMSupport(NodeFactory factory, TypeDesc type, ref DependencyList dependencies) + { + if (factory.TypeSystemContext.SupportsUniversalCanon) + { + foreach (MethodDesc method in type.GetVirtualMethods()) + { + if (!method.HasInstantiation) + continue; + + if (method.IsAbstract) + continue; + + TypeDesc[] universalCanonArray = new TypeDesc[method.Instantiation.Length]; + for (int i = 0; i < universalCanonArray.Length; i++) + universalCanonArray[i] = factory.TypeSystemContext.UniversalCanonType; + + MethodDesc universalCanonMethodNonCanonicalized = method.MakeInstantiatedMethod(new Instantiation(universalCanonArray)); + MethodDesc universalCanonGVMMethod = universalCanonMethodNonCanonicalized.GetCanonMethodTarget(CanonicalFormKind.Universal); + + if (dependencies == null) + dependencies = new DependencyList(); + + dependencies.Add(new DependencyListEntry(factory.MethodEntrypoint(universalCanonGVMMethod), "USG GVM Method")); + } + } + } + + public override int ClassCode => 1521789141; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((EETypeNode)other)._type); + } + + public override string ToString() + { + return _type.ToString(); + } + + private struct SlotCounter + { + private int _startBytes; + + public static SlotCounter BeginCounting(ref /* readonly */ ObjectDataBuilder builder) + => new SlotCounter { _startBytes = builder.CountBytes }; + + public int CountSlots(ref /* readonly */ ObjectDataBuilder builder) + { + int bytesEmitted = builder.CountBytes - _startBytes; + Debug.Assert(bytesEmitted % builder.TargetPointerSize == 0); + return bytesEmitted / builder.TargetPointerSize; + } + + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs new file mode 100644 index 00000000000000..aa6d25c8c82ac2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class EETypeOptionalFieldsNode : ObjectNode, ISymbolDefinitionNode + { + private EETypeNode _owner; + + public EETypeOptionalFieldsNode(EETypeNode owner) + { + _owner = owner; + } + + public override ObjectNodeSection Section + { + get + { + if (_owner.Type.Context.Target.IsWindows) + return ObjectNodeSection.FoldableReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + + public override bool StaticDependenciesAreComputed => true; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__optionalfields_"); + _owner.AppendMangledName(nameMangler, sb); + } + public int Offset => 0; + public override bool IsShareable => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) + { + _owner.ComputeOptionalEETypeFields(factory, relocsOnly: false); + return _owner.ShouldSkipEmittingObjectNode(factory) || !_owner.HasOptionalFields; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialAlignment(1); + objData.AddSymbol(this); + + if (!relocsOnly) + { + _owner.ComputeOptionalEETypeFields(factory, relocsOnly: false); + objData.EmitBytes(_owner.GetOptionalFieldsData()); + } + + return objData.ToObjectData(); + } + + public override int ClassCode => 821718028; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return SortableDependencyNode.CompareImpl(_owner, ((EETypeOptionalFieldsNode)other)._owner, comparer); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EmbeddedObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EmbeddedObjectNode.cs new file mode 100644 index 00000000000000..511f91cbb56f22 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EmbeddedObjectNode.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + public abstract class EmbeddedObjectNode : SortableDependencyNode + { + private const int InvalidOffset = int.MinValue; + + private int _offset; + private int _index; + + public IHasStartSymbol ContainingNode { get; set; } + + public EmbeddedObjectNode() + { + _offset = InvalidOffset; + _index = InvalidOffset; + } + + public int OffsetFromBeginningOfArray + { + get + { + Debug.Assert(_offset != InvalidOffset); + return _offset; + } + } + + public int IndexFromBeginningOfArray + { + get + { + Debug.Assert(_index != InvalidOffset); + return _index; + } + } + + public void InitializeOffsetFromBeginningOfArray(int offset) + { + Debug.Assert(_offset == InvalidOffset || _offset == offset); + _offset = offset; + } + + public void InitializeIndexFromBeginningOfArray(int index) + { + Debug.Assert(_index == InvalidOffset || _index == index); + _index = index; + } + + public virtual bool IsShareable => false; + public virtual bool RepresentsIndirectionCell => false; + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + + public abstract void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EmbeddedPointerIndirectionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EmbeddedPointerIndirectionNode.cs new file mode 100644 index 00000000000000..7b3664ffbc6e69 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EmbeddedPointerIndirectionNode.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// An whose sole value is a pointer to a different . + /// represents the node type this pointer points to. + /// + public abstract class EmbeddedPointerIndirectionNode : EmbeddedObjectNode, ISortableSymbolNode + where TTarget : ISortableSymbolNode + { + private TTarget _targetNode; + + /// + /// Target symbol this node points to. + /// + public TTarget Target => _targetNode; + + protected internal EmbeddedPointerIndirectionNode(TTarget target) + { + _targetNode = target; + } + + public override bool StaticDependenciesAreComputed => true; + + public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly) + { + dataBuilder.RequireInitialPointerAlignment(); + dataBuilder.EmitPointerReloc(Target); + } + + // At minimum, Target needs to be reported as a static dependency by inheritors. + public abstract override IEnumerable GetStaticDependencies(NodeFactory factory); + + int ISymbolNode.Offset => 0; + + public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("_embedded_ptr_"); + Target.AppendMangledName(nameMangler, sb); + } + + public override int ClassCode => -2055384490; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_targetNode, ((EmbeddedPointerIndirectionNode)other)._targetNode); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs new file mode 100644 index 00000000000000..1635f4d07b560a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs @@ -0,0 +1,163 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Hashtable of all exact (non-canonical) generic method instantiations compiled in the module. + /// + public sealed class ExactMethodInstantiationsNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public ExactMethodInstantiationsNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__exact_method_instantiations_End", true); + _externalReferences = externalReferences; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__exact_method_instantiations"); + } + + public ISymbolDefinitionNode EndSymbol => _endSymbol; + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // Dependencies for this node are tracked by the method code nodes + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + // Ensure the native layout data has been saved, in order to get valid Vertex offsets for the signature Vertices + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + NativeWriter nativeWriter = new NativeWriter(); + VertexHashtable hashtable = new VertexHashtable(); + Section nativeSection = nativeWriter.NewSection(); + nativeSection.Place(hashtable); + + + foreach (MethodDesc method in factory.MetadataManager.GetCompiledMethods()) + { + if (!IsMethodEligibleForTracking(factory, method)) + continue; + + // Get the method pointer vertex + + bool getUnboxingStub = method.OwningType.IsValueType && !method.Signature.IsStatic; + IMethodNode methodEntryPointNode = factory.MethodEntrypoint(method, getUnboxingStub); + Vertex methodPointer = nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(methodEntryPointNode)); + + // Get native layout vertices for the declaring type + + ISymbolNode declaringTypeNode = factory.NecessaryTypeSymbol(method.OwningType); + Vertex declaringType = nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(declaringTypeNode)); + + // Get a vertex sequence for the method instantiation args if any + + VertexSequence arguments = new VertexSequence(); + foreach (var arg in method.Instantiation) + { + ISymbolNode argNode = factory.NecessaryTypeSymbol(arg); + arguments.Append(nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(argNode))); + } + + // Get the name and sig of the method. + // Note: the method name and signature are stored in the NativeLayoutInfo blob, not in the hashtable we build here. + + NativeLayoutMethodNameAndSignatureVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition()); + NativeLayoutPlacedSignatureVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig); + Debug.Assert(placedNameAndSig.SavedVertex != null); + Vertex placedNameAndSigOffsetSig = nativeWriter.GetOffsetSignature(placedNameAndSig.SavedVertex); + + // Get the vertex for the completed method signature + + Vertex methodSignature = nativeWriter.GetTuple(declaringType, placedNameAndSigOffsetSig, arguments); + + // Make the generic method entry vertex + + Vertex entry = nativeWriter.GetTuple(methodSignature, methodPointer); + + // Add to the hash table, hashed by the containing type's hashcode + uint hashCode = (uint)method.OwningType.GetHashCode(); + hashtable.Append(hashCode, nativeSection.Place(entry)); + } + + byte[] streamBytes = nativeWriter.Save(); + + _endSymbol.SetSymbolOffset(streamBytes.Length); + + return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + public static void GetExactMethodInstantiationDependenciesForMethod(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + if (!IsMethodEligibleForTracking(factory, method)) + return; + + dependencies = dependencies ?? new DependencyList(); + + // Method entry point dependency + bool getUnboxingStub = method.OwningType.IsValueType && !method.Signature.IsStatic; + IMethodNode methodEntryPointNode = factory.MethodEntrypoint(method, getUnboxingStub); + dependencies.Add(new DependencyListEntry(methodEntryPointNode, "Exact method instantiation entry")); + + // Get native layout dependencies for the declaring type + dependencies.Add(new DependencyListEntry(factory.NecessaryTypeSymbol(method.OwningType), "Exact method instantiation entry")); + + // Get native layout dependencies for the method instantiation args + foreach (var arg in method.Instantiation) + dependencies.Add(new DependencyListEntry(factory.NecessaryTypeSymbol(arg), "Exact method instantiation entry")); + + // Get native layout dependencies for the method signature. + NativeLayoutMethodNameAndSignatureVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition()); + dependencies.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(nameAndSig), "Exact method instantiation entry")); + } + + private static bool IsMethodEligibleForTracking(NodeFactory factory, MethodDesc method) + { + // Runtime determined methods should never show up here. + Debug.Assert(!method.IsRuntimeDeterminedExactMethod); + + if (method.IsAbstract) + return false; + + if (!method.HasInstantiation) + return false; + + // This hashtable is only for method instantiations that don't use generic dictionaries, + // so check if the given method is shared before proceeding + if (method.IsSharedByGenericInstantiations || method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method) + return false; + + // The hashtable is used to find implementations of generic virtual methods at runtime + if (method.IsVirtual) + return true; + + // The hashtable is also used for reflection + if (!factory.MetadataManager.IsReflectionBlocked(method)) + return true; + + // The rest of the entries are potentially only useful for the universal + // canonical type loader. + return factory.TypeSystemContext.SupportsUniversalCanon; + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.ExactMethodInstantiationsNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternEETypeSymbolNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternEETypeSymbolNode.cs new file mode 100644 index 00000000000000..053642f6eff505 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternEETypeSymbolNode.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a symbol that is defined externally but modelled as a type in the + /// DependencyAnalysis infrastructure during compilation. + /// + public sealed class ExternEETypeSymbolNode : ExternSymbolNode, IEETypeNode + { + private TypeDesc _type; + + public ExternEETypeSymbolNode(NodeFactory factory, TypeDesc type) + : base(factory.NameMangler.NodeMangler.MethodTable(type)) + { + _type = type; + + factory.TypeSystemContext.EnsureLoadableType(type); + } + + public TypeDesc Type => _type; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternMethodSymbolNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternMethodSymbolNode.cs new file mode 100644 index 00000000000000..0abfc47f1bff82 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternMethodSymbolNode.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a symbol that is defined externally but modelled as a method + /// in the DependencyAnalysis infrastructure during compilation + /// + public sealed class ExternMethodSymbolNode : ExternSymbolNode, IMethodNode + { + private MethodDesc _method; + + public ExternMethodSymbolNode(NodeFactory factory, MethodDesc method, bool isUnboxing = false) + : base(isUnboxing ? UnboxingStubNode.GetMangledName(factory.NameMangler, method) : + factory.NameMangler.GetMangledMethodName(method)) + { + _method = method; + } + + public MethodDesc Method + { + get + { + return _method; + } + } + + public override int ClassCode => -729061105; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternSymbolNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternSymbolNode.cs new file mode 100644 index 00000000000000..8f0e239fc1fb67 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternSymbolNode.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a symbol that is defined externally and statically linked to the output obj file. + /// + public class ExternSymbolNode : SortableDependencyNode, ISortableSymbolNode + { + private Utf8String _name; + + public ExternSymbolNode(Utf8String name) + { + _name = name; + } + + protected override string GetName(NodeFactory factory) => $"ExternSymbol {_name.ToString()}"; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(_name); + } + public int Offset => 0; + public virtual bool RepresentsIndirectionCell => false; + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) => null; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + +#if !SUPPORT_JIT + public override int ClassCode => 1092559304; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return _name.CompareTo(((ExternSymbolNode)other)._name); + } +#endif + + public override string ToString() + { + return _name.ToString(); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternSymbolsImportedNodeProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternSymbolsImportedNodeProvider.cs new file mode 100644 index 00000000000000..18668be9769bbf --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternSymbolsImportedNodeProvider.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + public class ExternSymbolsImportedNodeProvider : ImportedNodeProvider + { + public override IEETypeNode ImportedEETypeNode(NodeFactory factory, TypeDesc type) + { + return new ExternEETypeSymbolNode(factory, type); + } + + public override ISortableSymbolNode ImportedGCStaticNode(NodeFactory factory, MetadataType type) + { + return new ExternSymbolNode(GCStaticsNode.GetMangledName(type, factory.NameMangler)); + } + + public override ISortableSymbolNode ImportedNonGCStaticNode(NodeFactory factory, MetadataType type) + { + return new ExternSymbolNode(NonGCStaticsNode.GetMangledName(type, factory.NameMangler)); + } + + public override ISortableSymbolNode ImportedTypeDictionaryNode(NodeFactory factory, TypeDesc type) + { + throw new NotImplementedException(); + } + + public override ISortableSymbolNode ImportedMethodDictionaryNode(NodeFactory factory, MethodDesc method) + { + throw new NotImplementedException(); + } + + public override IMethodNode ImportedMethodCodeNode(NodeFactory factory, MethodDesc method, bool unboxingStub) + { + return new ExternMethodSymbolNode(factory, method, unboxingStub); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalReferencesTableNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalReferencesTableNode.cs new file mode 100644 index 00000000000000..1d2ab3ed66a426 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalReferencesTableNode.cs @@ -0,0 +1,152 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.Runtime; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a node that points to various symbols and can be sequentially addressed. + /// + public sealed class ExternalReferencesTableNode : ObjectNode, ISymbolDefinitionNode + { + private readonly ObjectAndOffsetSymbolNode _endSymbol; + private readonly string _blobName; + private readonly NodeFactory _nodeFactory; + + private Dictionary _insertedSymbolsDictionary = new Dictionary(); + private List _insertedSymbols = new List(); + + public ExternalReferencesTableNode(string blobName, NodeFactory nodeFactory) + { + _blobName = blobName; + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__external_" + blobName + "_references_End", true); + _nodeFactory = nodeFactory; + } + + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__external_" + _blobName + "_references"); + } + public int Offset => 0; + public override bool IsShareable => false; + + /// + /// Adds a new entry to the table. Thread safety: not thread safe. Expected to be called at the final + /// object data emission phase from a single thread. + /// + public uint GetIndex(ISymbolNode symbol, int delta = 0) + { +#if DEBUG + if (_nodeFactory.MarkingComplete) + { + var node = symbol as ILCompiler.DependencyAnalysisFramework.DependencyNodeCore; + if (node != null) + Debug.Assert(node.Marked); + } +#endif + + SymbolAndDelta key = new SymbolAndDelta(symbol, delta); + + uint index; + if (!_insertedSymbolsDictionary.TryGetValue(key, out index)) + { + index = (uint)_insertedSymbols.Count; + _insertedSymbolsDictionary[key] = index; + _insertedSymbols.Add(key); + } + + return index; + } + + public override ObjectNodeSection Section + { + get + { + if (_nodeFactory.Target.IsWindows || _nodeFactory.Target.SupportsRelativePointers) + return ObjectNodeSection.ReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + // Zero out the dictionary so that we AV if someone tries to insert after we're done. + _insertedSymbolsDictionary = null; + + var builder = new ObjectDataBuilder(factory, relocsOnly); + + foreach (SymbolAndDelta symbolAndDelta in _insertedSymbols) + { + if (factory.Target.SupportsRelativePointers) + { + // TODO: set low bit if the linkage of the symbol is IAT_PVALUE. + builder.EmitReloc(symbolAndDelta.Symbol, RelocType.IMAGE_REL_BASED_RELPTR32, symbolAndDelta.Delta); + } + else + { + builder.EmitPointerReloc(symbolAndDelta.Symbol, symbolAndDelta.Delta); + } + } + + _endSymbol.SetSymbolOffset(builder.CountBytes); + + builder.AddSymbol(this); + builder.AddSymbol(_endSymbol); + + return builder.ToObjectData(); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.ExternalReferencesTableNode; + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return string.Compare(_blobName, ((ExternalReferencesTableNode)other)._blobName); + } + + struct SymbolAndDelta : IEquatable + { + public readonly ISymbolNode Symbol; + public readonly int Delta; + + public SymbolAndDelta(ISymbolNode symbol, int delta) + { + Symbol = symbol; + Delta = delta; + } + + public bool Equals(SymbolAndDelta other) + { + return Symbol == other.Symbol && Delta == other.Delta; + } + + public override bool Equals(object obj) + { + return Equals((SymbolAndDelta)obj); + } + + public override int GetHashCode() + { + return Symbol.GetHashCode(); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs new file mode 100644 index 00000000000000..9873fb76c78370 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs @@ -0,0 +1,104 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a fat function pointer - a data structure that captures a pointer to a canonical + /// method body along with the instantiation context the canonical body requires. + /// Pointers to these structures can be created by e.g. ldftn/ldvirtftn of a method with a canonical body. + /// + public class FatFunctionPointerNode : ObjectNode, IMethodNode, ISymbolDefinitionNode + { + private bool _isUnboxingStub; + + public bool IsUnboxingStub => _isUnboxingStub; + + public FatFunctionPointerNode(MethodDesc methodRepresented, bool isUnboxingStub) + { + // We should not create these for methods that don't have a canonical method body + Debug.Assert(methodRepresented.GetCanonMethodTarget(CanonicalFormKind.Specific) != methodRepresented); + + Method = methodRepresented; + _isUnboxingStub = isUnboxingStub; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + string prefix = _isUnboxingStub ? "__fatunboxpointer_" : "__fatpointer_"; + sb.Append(prefix).Append(nameMangler.GetMangledMethodName(Method)); + } + + int ISymbolDefinitionNode.Offset => 0; + int ISymbolNode.Offset => Method.Context.Target.FatFunctionPointerOffset; + + public override bool IsShareable => true; + + public MethodDesc Method { get; } + + public override ObjectNodeSection Section + { + get + { + if (Method.Context.Target.IsWindows) + return ObjectNodeSection.ReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + var builder = new ObjectDataBuilder(factory, relocsOnly); + + // These need to be aligned the same as methods because they show up in same contexts + builder.RequireInitialAlignment(factory.Target.MinimumFunctionAlignment); + + builder.AddSymbol(this); + + MethodDesc canonMethod = Method.GetCanonMethodTarget(CanonicalFormKind.Specific); + + // Pointer to the canonical body of the method + builder.EmitPointerReloc(factory.MethodEntrypoint(canonMethod, _isUnboxingStub)); + + // Find out what's the context to use + ISortableSymbolNode contextParameter; + if (canonMethod.RequiresInstMethodDescArg()) + { + contextParameter = factory.MethodGenericDictionary(Method); + } + else + { + Debug.Assert(canonMethod.RequiresInstMethodTableArg()); + + // Ask for a constructed type symbol because we need the vtable to get to the dictionary + contextParameter = factory.ConstructedTypeSymbol(Method.OwningType); + } + + // The next entry is a pointer to the context to be used for the canonical method + builder.EmitPointerReloc(contextParameter); + + return builder.ToObjectData(); + } + + public override int ClassCode => 190463489; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + var compare = _isUnboxingStub.CompareTo(((FatFunctionPointerNode)other)._isUnboxingStub); + if (compare != 0) + return compare; + + return comparer.Compare(Method, ((FatFunctionPointerNode)other).Method); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs new file mode 100644 index 00000000000000..8f701ceb236d3e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; +using EcmaField = Internal.TypeSystem.Ecma.EcmaField; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a field that has metadata generated in the current compilation. + /// + /// + /// Only expected to be used during ILScanning when scanning for reflection. + /// + internal class FieldMetadataNode : DependencyNodeCore + { + private readonly FieldDesc _field; + + public FieldMetadataNode(FieldDesc field) + { + Debug.Assert(field.IsTypicalFieldDefinition); + _field = field; + } + + public FieldDesc Field => _field; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + dependencies.Add(factory.TypeMetadata((MetadataType)_field.OwningType), "Owning type metadata"); + + CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, ((EcmaField)_field)); + + return dependencies; + } + protected override string GetName(NodeFactory factory) + { + return "Reflectable field: " + _field.ToString(); + } + + protected override void OnMarked(NodeFactory factory) + { + Debug.Assert(!factory.MetadataManager.IsReflectionBlocked(_field)); + Debug.Assert(factory.MetadataManager.CanGenerateMetadata(_field)); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenObjectNode.cs new file mode 100644 index 00000000000000..496cb7841d6ffb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenObjectNode.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a frozen object that is statically preallocated within the data section + /// of the executable instead of on the GC heap. + /// + public class FrozenObjectNode : EmbeddedObjectNode, ISymbolDefinitionNode + { + private readonly FieldDesc _field; + private readonly TypePreinit.ISerializableReference _data; + + public FrozenObjectNode(FieldDesc field, TypePreinit.ISerializableReference data) + { + _field = field; + _data = data; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__FrozenObj_") + .Append(nameMangler.GetMangledFieldName(_field)); + } + + public override bool StaticDependenciesAreComputed => true; + + int ISymbolNode.Offset => 0; + + int ISymbolDefinitionNode.Offset + { + get + { + // The frozen object symbol points at the MethodTable portion of the object, skipping over the sync block + return OffsetFromBeginningOfArray + _field.Context.Target.PointerSize; + } + } + + public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly) + { + // Sync Block + dataBuilder.EmitZeroPointer(); + + // byte contents + _data.WriteContent(ref dataBuilder, this, factory); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, true); + EncodeData(ref builder, factory, true); + Relocation[] relocs = builder.ToObjectData().Relocs; + DependencyList dependencies = null; + + if (relocs != null) + { + dependencies = new DependencyList(); + foreach (Relocation reloc in relocs) + { + dependencies.Add(reloc.Target, "reloc"); + } + } + + return dependencies; + } + + protected override void OnMarked(NodeFactory factory) + { + factory.FrozenSegmentRegion.AddEmbeddedObject(this); + } + + public override int ClassCode => 1789429316; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(((FrozenObjectNode)other)._field, _field); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenStringNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenStringNode.cs new file mode 100644 index 00000000000000..c54aeabd5e92e9 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FrozenStringNode.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class FrozenStringNode : EmbeddedObjectNode, ISymbolDefinitionNode + { + private string _data; + private int _syncBlockSize; + + public FrozenStringNode(string data, TargetDetails target) + { + _data = data; + _syncBlockSize = target.PointerSize; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__Str_").Append(nameMangler.GetMangledStringName(_data)); + } + + public override bool StaticDependenciesAreComputed => true; + + int ISymbolNode.Offset => 0; + + int ISymbolDefinitionNode.Offset + { + get + { + // The frozen string symbol points at the MethodTable portion of the object, skipping over the sync block + return OffsetFromBeginningOfArray + _syncBlockSize; + } + } + + private static IEETypeNode GetEETypeNode(NodeFactory factory) + { + DefType systemStringType = factory.TypeSystemContext.GetWellKnownType(WellKnownType.String); + + // + // The GC requires a direct reference to frozen objects' EETypes. If System.String will be compiled into a separate + // binary, it must be cloned into this one. + // + IEETypeNode stringSymbol = factory.ConstructedTypeSymbol(systemStringType); + + if (stringSymbol.RepresentsIndirectionCell) + { + return factory.ConstructedClonedTypeSymbol(systemStringType); + } + else + { + return stringSymbol; + } + } + + public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly) + { + dataBuilder.EmitZeroPointer(); // Sync block + + dataBuilder.EmitPointerReloc(GetEETypeNode(factory)); + + dataBuilder.EmitInt(_data.Length); + + foreach (char c in _data) + { + dataBuilder.EmitShort((short)c); + } + + // Null-terminate for friendliness with interop + dataBuilder.EmitShort(0); + + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + return new DependencyListEntry[] + { + new DependencyListEntry(GetEETypeNode(factory), "Frozen string literal MethodTable"), + }; + } + + protected override void OnMarked(NodeFactory factory) + { + factory.FrozenSegmentRegion.AddEmbeddedObject(this); + } + + public override int ClassCode => -1733946122; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return string.CompareOrdinal(_data, ((FrozenStringNode)other)._data); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs new file mode 100644 index 00000000000000..fd2fc719af5a1f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; + +using Internal.Runtime; +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a subset of that is used to describe GC static field regions for + /// types. It only fills out enough pieces of the MethodTable structure so that the GC can operate on it. Runtime should + /// never see these. + /// + public class GCStaticEETypeNode : ObjectNode, ISymbolDefinitionNode + { + private GCPointerMap _gcMap; + private TargetDetails _target; + + public GCStaticEETypeNode(TargetDetails target, GCPointerMap gcMap) + { + _gcMap = gcMap; + _target = target; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectNodeSection Section + { + get + { + if (_target.IsWindows) + return ObjectNodeSection.ReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + + public override bool StaticDependenciesAreComputed => true; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__GCStaticEEType_").Append(_gcMap.ToString()); + } + + int ISymbolDefinitionNode.Offset + { + get + { + int numSeries = _gcMap.NumSeries; + return numSeries > 0 ? ((numSeries * 2) + 1) * _target.PointerSize : 0; + } + } + + int ISymbolNode.Offset => 0; + + public override bool IsShareable => true; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + ObjectDataBuilder dataBuilder = new ObjectDataBuilder(factory, relocsOnly); + dataBuilder.RequireInitialPointerAlignment(); + dataBuilder.AddSymbol(this); + + // +1 for SyncBlock (in CoreRT static size already includes MethodTable) + Debug.Assert(factory.Target.Abi == TargetAbi.CoreRT || factory.Target.Abi == TargetAbi.CppCodegen); + int totalSize = (_gcMap.Size + 1) * _target.PointerSize; + + // We only need to check for containsPointers because ThreadStatics are always allocated + // on the GC heap (no matter what "HasGCStaticBase" says). + // If that ever changes, we can assume "true" and switch this to an assert. + + bool containsPointers = _gcMap.NumSeries > 0; + if (containsPointers) + { + GCDescEncoder.EncodeStandardGCDesc(ref dataBuilder, _gcMap, totalSize, 0); + } + + Debug.Assert(dataBuilder.CountBytes == ((ISymbolDefinitionNode)this).Offset); + + dataBuilder.EmitShort(0); // ComponentSize is always 0 + + short flags = 0; + if (containsPointers) + flags |= (short)EETypeFlags.HasPointersFlag; + + dataBuilder.EmitShort(flags); + + totalSize = Math.Max(totalSize, _target.PointerSize * 3); // minimum GC MethodTable size is 3 pointers + dataBuilder.EmitInt(totalSize); + + // Related type: System.Object. This allows storing an instance of this type in an array of objects. + dataBuilder.EmitPointerReloc(factory.NecessaryTypeSymbol(factory.TypeSystemContext.GetWellKnownType(WellKnownType.Object))); + + return dataBuilder.ToObjectData(); + } + + public override int ClassCode => 1304929125; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return _gcMap.CompareTo(((GCStaticEETypeNode)other)._gcMap); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticsNode.cs new file mode 100644 index 00000000000000..0272852f07606d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticsNode.cs @@ -0,0 +1,112 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; +using GCStaticRegionConstants = Internal.Runtime.GCStaticRegionConstants; + +namespace ILCompiler.DependencyAnalysis +{ + public class GCStaticsNode : ObjectNode, ISymbolDefinitionNode, ISortableSymbolNode + { + private readonly MetadataType _type; + private readonly TypePreinit.PreinitializationInfo _preinitializationInfo; + + public GCStaticsNode(MetadataType type, PreinitializationManager preinitManager) + { + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific)); + _type = type; + + if (preinitManager.IsPreinitialized(type)) + _preinitializationInfo = preinitManager.GetPreinitializationInfo(_type); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.NodeMangler.GCStatics(_type)); + } + + public int Offset => 0; + public MetadataType Type => _type; + + public static string GetMangledName(TypeDesc type, NameMangler nameMangler) + { + return nameMangler.NodeMangler.GCStatics(type); + } + + private ISymbolNode GetGCStaticEETypeNode(NodeFactory factory) + { + GCPointerMap map = GCPointerMap.FromStaticLayout(_type); + return factory.GCStaticEEType(map); + } + + public GCStaticsPreInitDataNode NewPreInitDataNode() + { + Debug.Assert(_preinitializationInfo != null && _preinitializationInfo.IsPreinitialized); + return new GCStaticsPreInitDataNode(_preinitializationInfo); + } + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencyList = new DependencyList(); + + if (factory.PreinitializationManager.HasEagerStaticConstructor(_type)) + { + dependencyList.Add(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor"); + } + + if (_type.Module.GetGlobalModuleType().GetStaticConstructor() is MethodDesc moduleCctor) + { + dependencyList.Add(factory.MethodEntrypoint(moduleCctor), "Static base in a module with initializer"); + } + + dependencyList.Add(factory.GCStaticsRegion, "GCStatics Region"); + + dependencyList.Add(factory.GCStaticIndirection(_type), "GC statics indirection"); + EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref dependencyList); + + return dependencyList; + } + + public override bool StaticDependenciesAreComputed => true; + + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + public override bool IsShareable => EETypeNode.IsTypeNodeShareable(_type); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + + builder.RequireInitialPointerAlignment(); + + int delta = GCStaticRegionConstants.Uninitialized; + + // Set the flag that indicates next pointer following MethodTable is the preinit data + bool isPreinitialized = _preinitializationInfo != null && _preinitializationInfo.IsPreinitialized; + if (isPreinitialized) + delta |= GCStaticRegionConstants.HasPreInitializedData; + + builder.EmitPointerReloc(GetGCStaticEETypeNode(factory), delta); + + if (isPreinitialized) + builder.EmitPointerReloc(factory.GCStaticsPreInitDataNode(_type)); + + builder.AddSymbol(this); + + return builder.ToObjectData(); + } + + public override int ClassCode => -522346696; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((GCStaticsNode)other)._type); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs new file mode 100644 index 00000000000000..423854c113f93f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Contains all GC static fields for a particular MethodTable. + /// Fields that have preinitialized data are pointer reloc pointing to frozen objects. + /// Other fields are initialized with 0. + /// We simply memcpy these over the GC static MethodTable object. + /// + public class GCStaticsPreInitDataNode : ObjectNode, ISymbolDefinitionNode + { + private TypePreinit.PreinitializationInfo _preinitializationInfo; + + public GCStaticsPreInitDataNode(TypePreinit.PreinitializationInfo preinitializationInfo) + { + Debug.Assert(!preinitializationInfo.Type.IsCanonicalSubtype(CanonicalFormKind.Specific)); + _preinitializationInfo = preinitializationInfo; + } + + protected override string GetName(NodeFactory factory) => GetMangledName(_preinitializationInfo.Type, factory.NameMangler); + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetMangledName(_preinitializationInfo.Type, nameMangler)); + } + + public int Offset => 0; + public MetadataType Type => _preinitializationInfo.Type; + + public static string GetMangledName(TypeDesc type, NameMangler nameMangler) + { + return nameMangler.NodeMangler.GCStatics(type) + "__PreInitData"; + } + + public override bool StaticDependenciesAreComputed => true; + + public override ObjectNodeSection Section + { + get + { + if (Type.Context.Target.IsWindows) + return ObjectNodeSection.ReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + public override bool IsShareable => EETypeNode.IsTypeNodeShareable(_preinitializationInfo.Type); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + + MetadataType type = _preinitializationInfo.Type; + + builder.RequireInitialAlignment(factory.Target.PointerSize); + + // GC static fields don't begin at offset 0, need to subtract that. + int initialOffset = CompilerMetadataFieldLayoutAlgorithm.GetGCStaticFieldOffset(factory.TypeSystemContext).AsInt; + + foreach (FieldDesc field in type.GetFields()) + { + if (!field.IsStatic || field.HasRva || field.IsLiteral || field.IsThreadStatic || !field.HasGCStaticBase) + continue; + + int padding = field.Offset.AsInt - initialOffset - builder.CountBytes; + Debug.Assert(padding >= 0); + builder.EmitZeros(padding); + + TypePreinit.ISerializableValue val = _preinitializationInfo.GetFieldValue(field); + int currentOffset = builder.CountBytes; + if (val != null) + val.WriteFieldData(ref builder, field, factory); + else + builder.EmitZeroPointer(); + Debug.Assert(builder.CountBytes - currentOffset == field.FieldType.GetElementSize().AsInt); + } + + int pad = _preinitializationInfo.Type.GCStaticFieldSize.AsInt - builder.CountBytes - initialOffset; + Debug.Assert(pad >= 0); + builder.EmitZeros(pad); + + builder.AddSymbol(this); + + return builder.ToObjectData(); + } + + public override int ClassCode => 1148300665; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_preinitializationInfo.Type, ((GCStaticsPreInitDataNode)other)._preinitializationInfo.Type); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GVMDependenciesNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GVMDependenciesNode.cs new file mode 100644 index 00000000000000..f78bc2eb6c48b2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GVMDependenciesNode.cs @@ -0,0 +1,224 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// This analysis node is used for computing GVM dependencies for the following cases: + /// 1) Derived types where the GVM is overridden + /// 2) Variant-interfaces GVMs + /// This analysis node will ensure that the proper GVM instantiations are compiled on types. + /// We only analyze the canonical forms of generic virtual methods to limit the amount of generic + /// expansion we need to deal with in the compiler. + /// + public class GVMDependenciesNode : DependencyNodeCore + { + private const int UniversalCanonGVMDepthHeuristic_CanonDepth = 2; + private readonly MethodDesc _method; + + public GVMDependenciesNode(MethodDesc method) + { + Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method); + Debug.Assert(method.IsVirtual && method.HasInstantiation); + _method = method; + } + + public override bool HasConditionalStaticDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => "__GVMDependenciesNode_" + factory.NameMangler.GetMangledMethodName(_method); + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + DependencyList dependencies = null; + + context.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref dependencies, context, _method); + + if (!_method.IsAbstract) + { + bool validInstantiation = + _method.IsSharedByGenericInstantiations || ( // Non-exact methods are always valid instantiations (always pass constraints check) + _method.Instantiation.CheckValidInstantiationArguments() && + _method.OwningType.Instantiation.CheckValidInstantiationArguments() && + _method.CheckConstraints()); + + if (validInstantiation) + { + if (context.TypeSystemContext.SupportsUniversalCanon && _method.IsGenericDepthGreaterThan(UniversalCanonGVMDepthHeuristic_CanonDepth)) + { + // fall back to using the universal generic variant of the generic method + return dependencies; + } + + bool getUnboxingStub = _method.OwningType.IsValueType; + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(context.MethodEntrypoint(_method, getUnboxingStub), "GVM Dependency - Canon method"); + + if (_method.IsSharedByGenericInstantiations) + { + dependencies.Add(context.NativeLayout.TemplateMethodEntry(_method), "GVM Dependency - Template entry"); + dependencies.Add(context.NativeLayout.TemplateMethodLayout(_method), "GVM Dependency - Template"); + } + } + } + + return dependencies; + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => null; + + public override bool HasDynamicDependencies + { + get + { + TypeDesc methodOwningType = _method.OwningType; + + // SearchDynamicDependencies wouldn't come up with anything for these + if (!methodOwningType.IsInterface && + (methodOwningType.IsSealed() || _method.IsFinal)) + return false; + + return true; + } + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) + { + List dynamicDependencies = new List(); + + TypeDesc methodOwningType = _method.OwningType; + bool methodIsShared = _method.IsSharedByGenericInstantiations; + + TypeSystemContext context = _method.Context; + + for (int i = firstNode; i < markedNodes.Count; i++) + { + DependencyNodeCore entry = markedNodes[i]; + EETypeNode entryAsEETypeNode = entry as EETypeNode; + + if (entryAsEETypeNode == null) + continue; + + TypeDesc potentialOverrideType = entryAsEETypeNode.Type; + if (!potentialOverrideType.IsDefType || potentialOverrideType.IsInterface) + continue; + + // If method is canonical, don't allow using it with non-canonical types - we can wait until + // we see the __Canon instantiation. If there isn't one, the canonical method wouldn't be useful anyway. + if (methodIsShared && + potentialOverrideType.ConvertToCanonForm(CanonicalFormKind.Specific) != potentialOverrideType) + continue; + + // Similarly, if the type is canonical but this method instantiation isn't, don't mix them. + if (!methodIsShared && + potentialOverrideType.IsCanonicalSubtype(CanonicalFormKind.Any)) + continue; + + // If this is an interface gvm, look for types that implement the interface + // and other instantantiations that have the same canonical form. + // This ensure the various slot numbers remain equivalent across all types where there is an equivalence + // relationship in the vtable. + if (methodOwningType.IsInterface) + { + // We go over definitions because a single canonical interface method could actually be implemented + // by multiple methods - consider: + // + // class Foo : IFoo, IFoo, IFoo { } + // + // If we ask what implements IFoo<__Canon>.Method, the answer could be "three methods" + // and that's expected. We therefore resolve IFoo<__Canon>.Method for each IFoo.Method, + // IFoo.Method, and IFoo.Method, adding GVMDependencies for each. + TypeDesc potentialOverrideDefinition = potentialOverrideType.GetTypeDefinition(); + DefType[] potentialInterfaces = potentialOverrideType.RuntimeInterfaces; + DefType[] potentialDefinitionInterfaces = potentialOverrideDefinition.RuntimeInterfaces; + for (int interfaceIndex = 0; interfaceIndex < potentialInterfaces.Length; interfaceIndex++) + { + if (potentialInterfaces[interfaceIndex].ConvertToCanonForm(CanonicalFormKind.Specific) == methodOwningType) + { + MethodDesc interfaceMethod = _method.GetMethodDefinition(); + if (methodOwningType.HasInstantiation) + interfaceMethod = context.GetMethodForInstantiatedType( + _method.GetTypicalMethodDefinition(), (InstantiatedType)potentialDefinitionInterfaces[interfaceIndex]); + + MethodDesc slotDecl = potentialOverrideDefinition.InstantiateAsOpen().ResolveInterfaceMethodTarget(interfaceMethod); + if (slotDecl == null) + { + // The method might be implemented through a default interface method + var result = potentialOverrideDefinition.InstantiateAsOpen().ResolveInterfaceMethodToDefaultImplementationOnType(interfaceMethod, out slotDecl); + if (result != DefaultInterfaceMethodResolution.DefaultImplementation) + { + slotDecl = null; + } + } + + if (slotDecl != null) + { + TypeDesc[] openInstantiation = new TypeDesc[_method.Instantiation.Length]; + for (int instArg = 0; instArg < openInstantiation.Length; instArg++) + openInstantiation[instArg] = context.GetSignatureVariable(instArg, method: true); + MethodDesc implementingMethodInstantiation = slotDecl.MakeInstantiatedMethod(openInstantiation).InstantiateSignature(potentialOverrideType.Instantiation, _method.Instantiation); + dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(implementingMethodInstantiation.GetCanonMethodTarget(CanonicalFormKind.Specific)), null, "ImplementingMethodInstantiation")); + } + } + } + } + else + { + // This is not an interface GVM. Check whether the current type overrides the virtual method. + // We might need to change what virtual method we ask about - consider: + // + // class Base { virtual Method(); } + // class Derived : Base { override Method(); } + // + // We need to resolve Base<__Canon>.Method on Derived, but if we were to ask the virtual + // method resolution algorithm, the answer would be "does not override" because Base<__Canon> + // is not even in the inheritance hierarchy. + // + // So we need to modify the question to resolve Base.Method instead and then + // canonicalize the result. + + TypeDesc overrideTypeCur = potentialOverrideType; + do + { + if (overrideTypeCur.ConvertToCanonForm(CanonicalFormKind.Specific) == methodOwningType) + break; + + overrideTypeCur = overrideTypeCur.BaseType; + } + while (overrideTypeCur != null); + + if (overrideTypeCur == null) + continue; + + MethodDesc methodToResolve; + if (methodOwningType == overrideTypeCur) + { + methodToResolve = _method; + } + else + { + methodToResolve = context + .GetMethodForInstantiatedType(_method.GetTypicalMethodDefinition(), (InstantiatedType)overrideTypeCur) + .MakeInstantiatedMethod(_method.Instantiation); + } + + MethodDesc instantiatedTargetMethod = potentialOverrideType.FindVirtualFunctionTargetMethodOnObjectType(methodToResolve) + .GetCanonMethodTarget(CanonicalFormKind.Specific); + if (instantiatedTargetMethod != _method) + dynamicDependencies.Add(new CombinedDependencyListEntry( + factory.GVMDependencies(instantiatedTargetMethod), null, "DerivedMethodInstantiation")); + } + } + + return dynamicDependencies; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericCompositionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericCompositionNode.cs new file mode 100644 index 00000000000000..a4388b4eeb4a96 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericCompositionNode.cs @@ -0,0 +1,233 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; +using GenericVariance = Internal.Runtime.GenericVariance; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Describes how a generic type instance is composed - the number of generic arguments, their types, + /// and variance information. + /// + public class GenericCompositionNode : ObjectNode, ISymbolDefinitionNode + { + private GenericCompositionDetails _details; + + internal GenericCompositionNode(GenericCompositionDetails details) + { + _details = details; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__GenericInstance"); + + for (int i = 0; i < _details.Instantiation.Length; i++) + { + sb.Append('_'); + sb.Append(nameMangler.GetMangledTypeName(_details.Instantiation[i])); + } + + if (_details.Variance != null) + { + for (int i = 0; i < _details.Variance.Length; i++) + { + sb.Append('_'); + sb.Append((checked((byte)_details.Variance[i])).ToStringInvariant()); + } + } + } + + public int Offset + { + get + { + return 0; + } + } + + public override ObjectNodeSection Section + { + get + { + if (_details.Instantiation[0].Context.Target.IsWindows) + return ObjectNodeSection.FoldableReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + + public override bool IsShareable => true; + + public override bool StaticDependenciesAreComputed => true; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + bool hasVariance = _details.Variance != null; + + var builder = new ObjectDataBuilder(factory, relocsOnly); + builder.AddSymbol(this); + + builder.RequireInitialPointerAlignment(); + + builder.EmitShort((short)checked((UInt16)_details.Instantiation.Length)); + + builder.EmitByte((byte)(hasVariance ? 1 : 0)); + + // TODO: general purpose padding + builder.EmitByte(0); + if (factory.Target.PointerSize == 8) + builder.EmitInt(0); + + foreach (var typeArg in _details.Instantiation) + builder.EmitPointerRelocOrIndirectionReference(factory.NecessaryTypeSymbol(typeArg)); + + if (hasVariance) + { + foreach (var argVariance in _details.Variance) + builder.EmitByte(checked((byte)argVariance)); + } + + return builder.ToObjectData(); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override int ClassCode => -762680703; + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return _details.CompareToImpl(((GenericCompositionNode)other)._details, comparer); + } + } + + internal struct GenericCompositionDetails : IEquatable + { + public readonly Instantiation Instantiation; + + public readonly GenericVariance[] Variance; + + public GenericCompositionDetails(TypeDesc genericTypeInstance, bool forceVarianceInfo = false) + { + Debug.Assert(!genericTypeInstance.IsTypeDefinition); + + Instantiation = genericTypeInstance.Instantiation; + + bool emitVarianceInfo = forceVarianceInfo; + if (!emitVarianceInfo) + { + foreach (GenericParameterDesc param in genericTypeInstance.GetTypeDefinition().Instantiation) + { + if (param.Variance != Internal.TypeSystem.GenericVariance.None) + { + emitVarianceInfo = true; + break; + } + } + } + + if (emitVarianceInfo) + { + Debug.Assert((byte)Internal.TypeSystem.GenericVariance.Contravariant == (byte)GenericVariance.Contravariant); + Debug.Assert((byte)Internal.TypeSystem.GenericVariance.Covariant == (byte)GenericVariance.Covariant); + + Variance = new GenericVariance[Instantiation.Length]; + int i = 0; + foreach (GenericParameterDesc param in genericTypeInstance.GetTypeDefinition().Instantiation) + { + Variance[i++] = (GenericVariance)param.Variance; + } + } + else + { + Variance = null; + } + } + + public GenericCompositionDetails(Instantiation instantiation, GenericVariance[] variance) + { + Debug.Assert(variance == null || instantiation.Length == variance.Length); + Instantiation = instantiation; + Variance = variance; + } + + public bool Equals(GenericCompositionDetails other) + { + if (Instantiation.Length != other.Instantiation.Length) + return false; + + if ((Variance == null) != (other.Variance == null)) + return false; + + for (int i = 0; i < Instantiation.Length; i++) + { + if (Instantiation[i] != other.Instantiation[i]) + return false; + + if (Variance != null) + { + if (Variance[i] != other.Variance[i]) + return false; + } + } + + return true; + } + + public int CompareToImpl(GenericCompositionDetails other, TypeSystemComparer comparer) + { + var compare = Instantiation.Length.CompareTo(other.Instantiation.Length); + if (compare != 0) + return compare; + + if (Variance == null && other.Variance != null) + return -1; + + if (Variance != null && other.Variance == null) + return 1; + + for (int i = 0; i < Instantiation.Length; i++) + { + compare = comparer.Compare(Instantiation[i], other.Instantiation[i]); + if (compare != 0) + return compare; + + if (Variance != null) + { + compare = Variance[i].CompareTo(other.Variance[i]); + if (compare != 0) + return compare; + } + } + + Debug.Assert(Equals(other)); + return 0; + } + + public override bool Equals(object obj) + { + return obj is GenericCompositionDetails && Equals((GenericCompositionDetails)obj); + } + + public override int GetHashCode() + { + int hashCode = 13; + + if (Variance != null) + { + foreach (var element in Variance) + { + int value = (int)element * 0x5498341 + 0x832424; + hashCode = hashCode * 31 + value; + } + } + + return Instantiation.ComputeGenericInstanceHashCode(hashCode); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs new file mode 100644 index 00000000000000..1381cbf58fa8a3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Runtime; +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + internal sealed class GenericDefinitionEETypeNode : EETypeNode + { + public GenericDefinitionEETypeNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + Debug.Assert(type.IsGenericDefinition); + } + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) + { + return false; + } + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencyList = null; + + // Ask the metadata manager if we have any dependencies due to reflectability. + factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencyList, factory, _type); + + return dependencyList; + } + + protected internal override void ComputeOptionalEETypeFields(NodeFactory factory, bool relocsOnly) + { + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder dataBuilder = new ObjectDataBuilder(factory, relocsOnly); + + dataBuilder.RequireInitialPointerAlignment(); + dataBuilder.AddSymbol(this); + EETypeRareFlags rareFlags = 0; + + ushort flags = EETypeBuilderHelpers.ComputeFlags(_type); + if (factory.PreinitializationManager.HasLazyStaticConstructor(_type)) + rareFlags = rareFlags | EETypeRareFlags.HasCctorFlag; + if (_type.IsByRefLike) + rareFlags |= EETypeRareFlags.IsByRefLikeFlag; + + if (rareFlags != 0) + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.RareFlags, (uint)rareFlags); + + if (HasOptionalFields) + flags |= (ushort)EETypeFlags.OptionalFieldsFlag; + + dataBuilder.EmitShort((short)_type.Instantiation.Length); + dataBuilder.EmitUShort(flags); + dataBuilder.EmitInt(0); // Base size is always 0 + dataBuilder.EmitZeroPointer(); // No related type + dataBuilder.EmitShort(0); // No VTable + dataBuilder.EmitShort(0); // No interface map + dataBuilder.EmitInt(_type.GetHashCode()); + OutputTypeManagerIndirection(factory, ref dataBuilder); + OutputWritableData(factory, ref dataBuilder); + OutputOptionalFields(factory, ref dataBuilder); + + return dataBuilder.ToObjectData(); + } + + public override int ClassCode => -160325006; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs new file mode 100644 index 00000000000000..a824363f9862f1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs @@ -0,0 +1,287 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a generic dictionary for a concrete generic type instantiation + /// or generic method instantiation. The dictionary is used from canonical code + /// at runtime to look up runtime artifacts that depend on the concrete + /// context the generic type or method was instantiated with. + /// + public abstract class GenericDictionaryNode : ObjectNode, ISymbolDefinitionNode, ISortableSymbolNode + { + private readonly NodeFactory _factory; + + protected abstract TypeSystemContext Context { get; } + + public abstract TypeSystemEntity OwningEntity { get; } + + public abstract Instantiation TypeInstantiation { get; } + + public abstract Instantiation MethodInstantiation { get; } + + public abstract DictionaryLayoutNode GetDictionaryLayout(NodeFactory factory); + + public sealed override bool StaticDependenciesAreComputed => true; + + public sealed override bool IsShareable => true; + + int ISymbolNode.Offset => 0; + + public abstract void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb); + + protected abstract int HeaderSize { get; } + + int ISymbolDefinitionNode.Offset => HeaderSize; + + public override ObjectNodeSection Section => GetDictionaryLayout(_factory).DictionarySection(_factory); + + public GenericDictionaryNode(NodeFactory factory) + { + _factory = factory; + } + + public sealed override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.AddSymbol(this); + builder.RequireInitialPointerAlignment(); + + DictionaryLayoutNode layout = GetDictionaryLayout(factory); + + // Node representing the generic dictionary layout might be one of two kinds: + // With fixed slots, or where slots are added as we're expanding the graph. + // If it's the latter, we can't touch the collection of slots before the graph expansion + // is complete (relocsOnly == false). It's someone else's responsibility + // to make sure the dependencies are properly generated. + // If this is a dictionary layout with fixed slots, it's the responsibility of + // each dictionary to ensure the targets are marked. + if (layout.HasFixedSlots || !relocsOnly) + { + // TODO: pass the layout we already have to EmitDataInternal + EmitDataInternal(ref builder, factory, relocsOnly); + } + + return builder.ToObjectData(); + } + + protected virtual void EmitDataInternal(ref ObjectDataBuilder builder, NodeFactory factory, bool fixedLayoutOnly) + { + DictionaryLayoutNode layout = GetDictionaryLayout(factory); + layout.EmitDictionaryData(ref builder, factory, this, fixedLayoutOnly: fixedLayoutOnly); + } + + protected sealed override string GetName(NodeFactory factory) + { + return this.GetMangledName(factory.NameMangler); + } + + public override int ClassCode => ClassCode; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return CompareToImpl((ObjectNode)other, comparer); + } + } + + public sealed class TypeGenericDictionaryNode : GenericDictionaryNode + { + private TypeDesc _owningType; + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.NodeMangler.TypeGenericDictionary(_owningType)); + } + + protected override int HeaderSize => 0; + public override Instantiation TypeInstantiation => _owningType.Instantiation; + public override Instantiation MethodInstantiation => new Instantiation(); + protected override TypeSystemContext Context => _owningType.Context; + public override TypeSystemEntity OwningEntity => _owningType; + public TypeDesc OwningType => _owningType; + + public override DictionaryLayoutNode GetDictionaryLayout(NodeFactory factory) + { + return factory.GenericDictionaryLayout(_owningType.ConvertToCanonForm(CanonicalFormKind.Specific)); + } + + public override bool HasConditionalStaticDependencies => true; + + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList result = new DependencyList(); + + // Include the layout as a dependency if the canonical type isn't imported + TypeDesc canonicalOwningType = _owningType.ConvertToCanonForm(CanonicalFormKind.Specific); + if (factory.CompilationModuleGroup.ContainsType(canonicalOwningType) || !factory.CompilationModuleGroup.ShouldReferenceThroughImportTable(canonicalOwningType)) + result.Add(GetDictionaryLayout(factory), "Layout"); + + // Lazy generic use of the Activator.CreateInstance heuristic requires tracking type parameters that are used in lazy generics. + if (factory.LazyGenericsPolicy.UsesLazyGenerics(_owningType)) + { + foreach (var arg in _owningType.Instantiation) + { + // Skip types that do not have a default constructor (not interesting). + if (arg.IsValueType || arg.GetDefaultConstructor() == null || !ConstructedEETypeNode.CreationAllowed(arg)) + continue; + + result.Add(new DependencyListEntry( + factory.ConstructedTypeSymbol(arg.ConvertToCanonForm(CanonicalFormKind.Specific)), + "Default constructor for lazy generics")); + } + } + + factory.MetadataManager.GetDependenciesForGenericDictionary(ref result, factory, _owningType); + + return result; + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + // The generic dictionary layout is shared between all the canonically equivalent + // instantiations. We need to track the dependencies of all canonical method bodies + // that use the same dictionary layout. + foreach (var method in _owningType.GetAllMethods()) + { + if (!EETypeNode.MethodHasNonGenericILMethodBody(method)) + continue; + + // If a canonical method body was compiled, we need to track the dictionary + // dependencies in the context of the concrete type that owns this dictionary. + yield return new CombinedDependencyListEntry( + factory.ShadowConcreteMethod(method), + factory.MethodEntrypoint(method.GetCanonMethodTarget(CanonicalFormKind.Specific)), + "Generic dictionary dependency"); + } + } + + public TypeGenericDictionaryNode(TypeDesc owningType, NodeFactory factory) + : base(factory) + { + Debug.Assert(!owningType.IsCanonicalSubtype(CanonicalFormKind.Any)); + Debug.Assert(!owningType.IsRuntimeDeterminedSubtype); + Debug.Assert(owningType.HasInstantiation); + Debug.Assert(owningType.ConvertToCanonForm(CanonicalFormKind.Specific) != owningType); + + _owningType = owningType; + } + + public override int ClassCode => 889700584; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_owningType, ((TypeGenericDictionaryNode)other)._owningType); + } + } + + public sealed class MethodGenericDictionaryNode : GenericDictionaryNode + { + private MethodDesc _owningMethod; + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.NodeMangler.MethodGenericDictionary(_owningMethod)); + } + protected override int HeaderSize => _owningMethod.Context.Target.PointerSize; + public override Instantiation TypeInstantiation => _owningMethod.OwningType.Instantiation; + public override Instantiation MethodInstantiation => _owningMethod.Instantiation; + protected override TypeSystemContext Context => _owningMethod.Context; + public override TypeSystemEntity OwningEntity => _owningMethod; + public MethodDesc OwningMethod => _owningMethod; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + + MethodDesc canonicalTarget = _owningMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (factory.CompilationModuleGroup.ContainsMethodBody(canonicalTarget, false)) + dependencies.Add(GetDictionaryLayout(factory), "Layout"); + + // TODO-SIZE: We probably don't need to add these for all dictionaries + GenericMethodsHashtableNode.GetGenericMethodsHashtableDependenciesForMethod(ref dependencies, factory, _owningMethod); + + factory.InteropStubManager.AddMarshalAPIsGenericDependencies(ref dependencies, factory, _owningMethod); + + // Lazy generic use of the Activator.CreateInstance heuristic requires tracking type parameters that are used in lazy generics. + if (factory.LazyGenericsPolicy.UsesLazyGenerics(_owningMethod)) + { + foreach (var arg in _owningMethod.OwningType.Instantiation) + { + // Skip types that do not have a default constructor (not interesting). + if (arg.IsValueType || arg.GetDefaultConstructor() == null || !ConstructedEETypeNode.CreationAllowed(arg)) + continue; + + dependencies.Add(new DependencyListEntry( + factory.ConstructedTypeSymbol(arg.ConvertToCanonForm(CanonicalFormKind.Specific)), + "Default constructor for lazy generics")); + } + foreach (var arg in _owningMethod.Instantiation) + { + // Skip types that do not have a default constructor (not interesting). + if (arg.IsValueType || arg.GetDefaultConstructor() == null || !ConstructedEETypeNode.CreationAllowed(arg)) + continue; + + dependencies.Add(new DependencyListEntry( + factory.ConstructedTypeSymbol(arg.ConvertToCanonForm(CanonicalFormKind.Specific)), + "Default constructor for lazy generics")); + } + } + + // Make sure the dictionary can also be populated + dependencies.Add(factory.ShadowConcreteMethod(_owningMethod), "Dictionary contents"); + + factory.MetadataManager.GetDependenciesForGenericDictionary(ref dependencies, factory, _owningMethod); + + return dependencies; + } + + public override DictionaryLayoutNode GetDictionaryLayout(NodeFactory factory) + { + return factory.GenericDictionaryLayout(_owningMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)); + } + + protected override void EmitDataInternal(ref ObjectDataBuilder builder, NodeFactory factory, bool fixedLayoutOnly) + { + // Method generic dictionaries get prefixed by the hash code of the owning method + // to allow quick lookups of additional details by the type loader. + + builder.EmitInt(_owningMethod.GetHashCode()); + if (builder.TargetPointerSize == 8) + builder.EmitInt(0); + + Debug.Assert(builder.CountBytes == ((ISymbolDefinitionNode)this).Offset); + + // Lazy method dictionaries are generated by the compiler, but they have no entries within them. (They are used solely to identify the exact method) + // The dictionary layout may be filled in by various needs for generic lookups, but those are handled in a lazy fashion. + if (factory.LazyGenericsPolicy.UsesLazyGenerics(OwningMethod)) + return; + + base.EmitDataInternal(ref builder, factory, fixedLayoutOnly); + } + + public MethodGenericDictionaryNode(MethodDesc owningMethod, NodeFactory factory) + : base(factory) + { + Debug.Assert(!owningMethod.IsSharedByGenericInstantiations); + Debug.Assert(owningMethod.HasInstantiation); + Debug.Assert(owningMethod.GetCanonMethodTarget(CanonicalFormKind.Specific) != owningMethod); + + _owningMethod = owningMethod; + } + + public override int ClassCode => -1245704203; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_owningMethod, ((MethodGenericDictionaryNode)other)._owningMethod); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs new file mode 100644 index 00000000000000..754621f41bd7ba --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs @@ -0,0 +1,1635 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.IL; +using Internal.Text; +using Internal.TypeSystem; + +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + public enum GenericLookupResultReferenceType + { + Direct, // The slot stores a direct pointer to the target + Indirect, // The slot is an indirection cell which points to the direct pointer + ConditionalIndirect, // The slot may be a direct pointer or an indirection cell, depending on the last digit + } + + // Represents a generic lookup within a canonical method body. + // TODO: unify with NativeFormat.FixupSignatureKind + public enum LookupResultType + { + Invalid, + MethodTable, // a type + UnwrapNullable, // a type (The type T described by a type spec that is generic over Nullable) + NonGcStatic, // the non-gc statics of a type + GcStatic, // the gc statics of a type + Method, // a method + InterfaceDispatchCell, // the dispatch cell for calling an interface method + MethodDictionary, // a dictionary for calling a generic method + UnboxingStub, // the unboxing stub for a method + ArrayType, // an array of type + DefaultCtor, // default ctor of a type + AllocObject, // the allocator of a type + GvmVtableOffset, // vtable offset of a generic virtual method + ProfileCounter, // profiling counter cell + MethodLdToken, // a ldtoken result for a method + FieldLdToken, // a ldtoken result for a field + Field, // a field descriptor + IsInst, // isinst helper + CastClass, // castclass helper + AllocArray, // the array allocator of a type + TypeSize, // size of the type + FieldOffset, // field offset + CallingConvention_NoInstParam, // CallingConventionConverterThunk NO_INSTANTIATING_PARAM + CallingConvention_HasInstParam, // CallingConventionConverterThunk HAS_INSTANTIATING_PARAM + CallingConvention_MaybeInstParam, // CallingConventionConverterThunk MAYBE_INSTANTIATING_PARAM + VtableOffset, // Offset of a virtual method into the type's vtable + Constrained, // ConstrainedCallDesc + ConstrainedDirect, // Direct ConstrainedCallDesc + Integer, // Integer + UnboxingMethod, // UnboxingMethod + } + + public interface IGenericLookupResultTocWriter + { + void WriteData(GenericLookupResultReferenceType referenceType, LookupResultType slotType, TypeSystemEntity context); + void WriteIntegerSlot(int value); + } + + public struct GenericLookupResultContext + { + private readonly TypeSystemEntity _canonicalOwner; + + public readonly Instantiation TypeInstantiation; + + public readonly Instantiation MethodInstantiation; + + public TypeSystemEntity Context + { + get + { + if (_canonicalOwner is TypeDesc) + { + var owningTypeDefinition = (MetadataType)((TypeDesc)_canonicalOwner).GetTypeDefinition(); + Debug.Assert(owningTypeDefinition.Instantiation.Length == TypeInstantiation.Length); + Debug.Assert(MethodInstantiation.IsNull || MethodInstantiation.Length == 0); + + return owningTypeDefinition.MakeInstantiatedType(TypeInstantiation); + } + + Debug.Assert(_canonicalOwner is MethodDesc); + MethodDesc owningMethodDefinition = ((MethodDesc)_canonicalOwner).GetTypicalMethodDefinition(); + Debug.Assert(owningMethodDefinition.Instantiation.Length == MethodInstantiation.Length); + + MethodDesc concreteMethod = owningMethodDefinition; + if (!TypeInstantiation.IsNull && TypeInstantiation.Length > 0) + { + TypeDesc owningType = owningMethodDefinition.OwningType; + Debug.Assert(owningType.Instantiation.Length == TypeInstantiation.Length); + concreteMethod = owningType.Context.GetMethodForInstantiatedType(owningMethodDefinition, ((MetadataType)owningType).MakeInstantiatedType(TypeInstantiation)); + } + else + { + Debug.Assert(owningMethodDefinition.OwningType.Instantiation.IsNull + || owningMethodDefinition.OwningType.Instantiation.Length == 0); + } + + return concreteMethod.MakeInstantiatedMethod(MethodInstantiation); + } + } + + public GenericLookupResultContext(TypeSystemEntity canonicalOwner, Instantiation typeInst, Instantiation methodInst) + { + _canonicalOwner = canonicalOwner; + TypeInstantiation = typeInst; + MethodInstantiation = methodInst; + } + } + + /// + /// Represents the result of a generic lookup within a canonical method body. + /// The concrete artifact the generic lookup will result in can only be determined after substituting + /// runtime determined types with a concrete generic context. Use + /// to obtain the concrete + /// node the result points to. + /// + public abstract class GenericLookupResult + { + protected abstract int ClassCode { get; } + public abstract ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary); + public abstract void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb); + public abstract override string ToString(); + protected abstract int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer); + protected abstract bool EqualsImpl(GenericLookupResult obj); + protected abstract int GetHashCodeImpl(); + + public sealed override bool Equals(object obj) + { + GenericLookupResult other = obj as GenericLookupResult; + if (obj == null) + return false; + + return ClassCode == other.ClassCode && EqualsImpl(other); + } + + public sealed override int GetHashCode() + { + return ClassCode * 31 + GetHashCodeImpl(); + } + + public virtual void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) + { + ISymbolNode target; + try + { + target = GetTarget(factory, dictionary); + } + catch (TypeSystemException) + { + target = null; + } + + if (target == null) + { + builder.EmitZeroPointer(); + } + else if (LookupResultReferenceType(factory) == GenericLookupResultReferenceType.ConditionalIndirect) + { + builder.EmitPointerRelocOrIndirectionReference(target); + } + else + { + builder.EmitPointerReloc(target); + } + } + + public virtual GenericLookupResultReferenceType LookupResultReferenceType(NodeFactory factory) + { + return GenericLookupResultReferenceType.Direct; + } + + public abstract NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory); + + public abstract void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer); + + // Call this api to get non-reloc dependencies that arise from use of a dictionary lookup + public virtual IEnumerable> NonRelocDependenciesFromUsage(NodeFactory factory) + { + return Array.Empty>(); + } + + public class Comparer : IComparer + { + private TypeSystemComparer _comparer; + + public Comparer(TypeSystemComparer comparer) + { + _comparer = comparer; + } + + public int Compare(GenericLookupResult x, GenericLookupResult y) + { + if (x == y) + { + return 0; + } + + int codeX = x.ClassCode; + int codeY = y.ClassCode; + if (codeX == codeY) + { + Debug.Assert(x.GetType() == y.GetType()); + + int result = x.CompareToImpl(y, _comparer); + + // We did a reference equality check above so an "Equal" result is not expected + Debug.Assert(result != 0); + + return result; + } + else + { + Debug.Assert(x.GetType() != y.GetType()); + return codeX > codeY ? -1 : 1; + } + } + } + } + + /// + /// Generic lookup result that points to an MethodTable. + /// + public sealed class TypeHandleGenericLookupResult : GenericLookupResult + { + private TypeDesc _type; + + protected override int ClassCode => 1623839081; + + public TypeHandleGenericLookupResult(TypeDesc type) + { + Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete type in a generic dictionary?"); + _type = type; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + // We are getting a maximally constructable type symbol because this might be something passed to newobj. + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + + factory.TypeSystemContext.DetectGenericCycles(dictionary.Context, instantiatedType); + + return factory.MaximallyConstructableType(instantiatedType); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("TypeHandle_"); + sb.Append(nameMangler.GetMangledTypeName(_type)); + } + + public TypeDesc Type => _type; + public override string ToString() => $"TypeHandle: {_type}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.TypeHandleDictionarySlot(_type); + } + + public override GenericLookupResultReferenceType LookupResultReferenceType(NodeFactory factory) + { + if (factory.CompilationModuleGroup.CanHaveReferenceThroughImportTable) + { + return GenericLookupResultReferenceType.ConditionalIndirect; + } + else + { + return GenericLookupResultReferenceType.Direct; + } + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.MethodTable, _type); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_type, ((TypeHandleGenericLookupResult)other)._type); + } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((TypeHandleGenericLookupResult)obj)._type == _type; + } + } + + + /// + /// Generic lookup result that points to an MethodTable where if the type is Nullable<X> the MethodTable is X + /// + public sealed class UnwrapNullableTypeHandleGenericLookupResult : GenericLookupResult + { + private TypeDesc _type; + + protected override int ClassCode => 53521918; + + public UnwrapNullableTypeHandleGenericLookupResult(TypeDesc type) + { + Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete type in a generic dictionary?"); + _type = type; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + + // Unwrap the nullable type if necessary + if (instantiatedType.IsNullable) + instantiatedType = instantiatedType.Instantiation[0]; + + // We are getting a constructed type symbol because this might be something passed to newobj. + return factory.ConstructedTypeSymbol(instantiatedType); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("UnwrapNullable_"); + sb.Append(nameMangler.GetMangledTypeName(_type)); + } + + public TypeDesc Type => _type; + public override string ToString() => $"UnwrapNullable: {_type}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.UnwrapNullableTypeDictionarySlot(_type); + } + + public override GenericLookupResultReferenceType LookupResultReferenceType(NodeFactory factory) + { + if (factory.CompilationModuleGroup.CanHaveReferenceThroughImportTable) + { + return GenericLookupResultReferenceType.ConditionalIndirect; + } + else + { + return GenericLookupResultReferenceType.Direct; + } + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.UnwrapNullable, _type); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_type, ((UnwrapNullableTypeHandleGenericLookupResult)other)._type); + } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((UnwrapNullableTypeHandleGenericLookupResult)obj)._type == _type; + } + } + + /// + /// Generic lookup result that puts a field offset into the generic dictionary. + /// + internal sealed class FieldOffsetGenericLookupResult : GenericLookupResult + { + private FieldDesc _field; + + protected override int ClassCode => -1670293557; + + public FieldOffsetGenericLookupResult(FieldDesc field) + { + Debug.Assert(field.OwningType.IsRuntimeDeterminedSubtype, "Concrete field in a generic dictionary?"); + _field = field; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + Debug.Fail("GetTarget for a FieldOffsetGenericLookupResult doesn't make sense. It isn't a pointer being emitted"); + return null; + } + + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) + { + FieldDesc instantiatedField = _field.GetNonRuntimeDeterminedFieldFromRuntimeDeterminedFieldViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + int offset = instantiatedField.Offset.AsInt; + builder.EmitNaturalInt(offset); + } + + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("FieldOffset_"); + sb.Append(nameMangler.GetMangledFieldName(_field)); + } + + public override string ToString() => $"FieldOffset: {_field}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.FieldOffsetDictionarySlot(_field); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.FieldOffset, _field); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_field, ((FieldOffsetGenericLookupResult)other)._field); + } + + protected override int GetHashCodeImpl() + { + return _field.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((FieldOffsetGenericLookupResult)obj)._field == _field; + } + } + + /// + /// Generic lookup result that puts a vtable offset into the generic dictionary. + /// + internal sealed class VTableOffsetGenericLookupResult : GenericLookupResult + { + private MethodDesc _method; + + protected override int ClassCode => 386794182; + + public VTableOffsetGenericLookupResult(MethodDesc method) + { + Debug.Assert(method.IsRuntimeDeterminedExactMethod, "Concrete method in a generic dictionary?"); + _method = method; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + Debug.Fail("GetTarget for a VTableOffsetGenericLookupResult doesn't make sense. It isn't a pointer being emitted"); + return null; + } + + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) + { + Debug.Fail("VTableOffset contents should only be generated into generic dictionaries at runtime"); + builder.EmitNaturalInt(0); + } + + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("VTableOffset_"); + sb.Append(nameMangler.GetMangledMethodName(_method)); + } + + public override string ToString() => $"VTableOffset: {_method}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.VTableOffsetDictionarySlot(_method); + } + + public override IEnumerable> NonRelocDependenciesFromUsage(NodeFactory factory) + { + MethodDesc canonMethod = _method.GetCanonMethodTarget(CanonicalFormKind.Universal); + + // If we're producing a full vtable for the type, we don't need to report virtual method use. + if (factory.VTable(canonMethod.OwningType).HasFixedSlots) + return Array.Empty>(); + + return new DependencyNodeCore[] { + factory.VirtualMethodUse(canonMethod) + }; + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.VtableOffset, _method); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_method, ((VTableOffsetGenericLookupResult)other)._method); + } + + protected override int GetHashCodeImpl() + { + return _method.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((VTableOffsetGenericLookupResult)obj)._method == _method; + } + } + + /// + /// Generic lookup result that points to a RuntimeMethodHandle. + /// + internal sealed class MethodHandleGenericLookupResult : GenericLookupResult + { + private MethodDesc _method; + + protected override int ClassCode => 394272689; + + public MethodHandleGenericLookupResult(MethodDesc method) + { + Debug.Assert(method.IsRuntimeDeterminedExactMethod, "Concrete method in a generic dictionary?"); + _method = method; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + return factory.RuntimeMethodHandle(instantiatedMethod); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("MethodHandle_"); + sb.Append(nameMangler.GetMangledMethodName(_method)); + } + + public override string ToString() => $"MethodHandle: {_method}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.MethodLdTokenDictionarySlot(_method); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.MethodLdToken, _method); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_method, ((MethodHandleGenericLookupResult)other)._method); + } + + protected override int GetHashCodeImpl() + { + return _method.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((MethodHandleGenericLookupResult)obj)._method == _method; + } + } + + /// + /// Generic lookup result that points to a RuntimeFieldHandle. + /// + internal sealed class FieldHandleGenericLookupResult : GenericLookupResult + { + private FieldDesc _field; + + protected override int ClassCode => -196995964; + + public FieldHandleGenericLookupResult(FieldDesc field) + { + Debug.Assert(field.OwningType.IsRuntimeDeterminedSubtype, "Concrete field in a generic dictionary?"); + _field = field; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + FieldDesc instantiatedField = _field.GetNonRuntimeDeterminedFieldFromRuntimeDeterminedFieldViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + return factory.RuntimeFieldHandle(instantiatedField); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("FieldHandle_"); + sb.Append(nameMangler.GetMangledFieldName(_field)); + } + + public override string ToString() => $"FieldHandle: {_field}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.FieldLdTokenDictionarySlot(_field); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.FieldLdToken, _field); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_field, ((FieldHandleGenericLookupResult)other)._field); + } + + protected override int GetHashCodeImpl() + { + return _field.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((FieldHandleGenericLookupResult)obj)._field == _field; + } + } + + /// + /// Generic lookup result that points to a method dictionary. + /// + public sealed class MethodDictionaryGenericLookupResult : GenericLookupResult + { + private MethodDesc _method; + + protected override int ClassCode => -467418176; + + public MethodDictionaryGenericLookupResult(MethodDesc method) + { + Debug.Assert(method.IsRuntimeDeterminedExactMethod, "Concrete method in a generic dictionary?"); + _method = method; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + + factory.TypeSystemContext.DetectGenericCycles(dictionary.Context, instantiatedMethod); + + return factory.MethodGenericDictionary(instantiatedMethod); + } + + public override GenericLookupResultReferenceType LookupResultReferenceType(NodeFactory factory) + { + if (factory.CompilationModuleGroup.CanHaveReferenceThroughImportTable) + { + return GenericLookupResultReferenceType.ConditionalIndirect; + } + else + { + return GenericLookupResultReferenceType.Direct; + } + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("MethodDictionary_"); + sb.Append(nameMangler.GetMangledMethodName(_method)); + } + + public MethodDesc Method => _method; + public override string ToString() => $"MethodDictionary: {_method}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.MethodDictionaryDictionarySlot(_method); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.MethodDictionary, _method); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_method, ((MethodDictionaryGenericLookupResult)other)._method); + } + + protected override int GetHashCodeImpl() + { + return _method.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((MethodDictionaryGenericLookupResult)obj)._method == _method; + } + } + + /// + /// Generic lookup result that is a function pointer. + /// + internal sealed class MethodEntryGenericLookupResult : GenericLookupResult + { + private MethodDesc _method; + private bool _isUnboxingThunk; + + protected override int ClassCode => 1572293098; + + public MethodEntryGenericLookupResult(MethodDesc method, bool isUnboxingThunk) + { + Debug.Assert(method.IsRuntimeDeterminedExactMethod); + _method = method; + _isUnboxingThunk = isUnboxingThunk; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + return factory.FatFunctionPointer(instantiatedMethod, _isUnboxingThunk); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + if (!_isUnboxingThunk) + sb.Append("MethodEntry_"); + else + sb.Append("UnboxMethodEntry_"); + + sb.Append(nameMangler.GetMangledMethodName(_method)); + } + + public override string ToString() => $"MethodEntry: {_method}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + MethodDesc canonMethod = _method.GetCanonMethodTarget(CanonicalFormKind.Specific); + + // + // For universal canonical methods, we don't need the unboxing stub really, because + // the calling convention translation thunk will handle the unboxing (and we can avoid having a double thunk here) + // We just need the flag in the native layout info signature indicating that we needed an unboxing stub + // + bool getUnboxingStubNode = _isUnboxingThunk && !canonMethod.IsCanonicalMethod(CanonicalFormKind.Universal); + + return factory.NativeLayout.MethodEntrypointDictionarySlot( + _method, + _isUnboxingThunk, + factory.MethodEntrypoint(canonMethod, getUnboxingStubNode)); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + LookupResultType lookupResult = LookupResultType.Method; + writer.WriteData(LookupResultReferenceType(factory), lookupResult, _method); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + var otherEntry = (MethodEntryGenericLookupResult)other; + int result = (_isUnboxingThunk ? 1 : 0) - (otherEntry._isUnboxingThunk ? 1 : 0); + if (result != 0) + return result; + + return comparer.Compare(_method, otherEntry._method); + } + + protected override int GetHashCodeImpl() + { + return _method.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((MethodEntryGenericLookupResult)obj)._method == _method && + ((MethodEntryGenericLookupResult)obj)._isUnboxingThunk == _isUnboxingThunk; + } + } + + /// + /// Generic lookup result that points to a dispatch cell. + /// + internal sealed class VirtualDispatchCellGenericLookupResult : GenericLookupResult + { + private MethodDesc _method; + + protected override int ClassCode => 643566930; + + public VirtualDispatchCellGenericLookupResult(MethodDesc method) + { + Debug.Assert(method.IsRuntimeDeterminedExactMethod); + Debug.Assert(method.IsVirtual); + Debug.Assert(method.OwningType.IsInterface); + + _method = method; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext context) + { + MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(context.TypeInstantiation, context.MethodInstantiation); + + TypeSystemEntity contextOwner = context.Context; + GenericDictionaryNode dictionary = + contextOwner is TypeDesc ? + (GenericDictionaryNode)factory.TypeGenericDictionary((TypeDesc)contextOwner) : + (GenericDictionaryNode)factory.MethodGenericDictionary((MethodDesc)contextOwner); + + return factory.InterfaceDispatchCell(instantiatedMethod, dictionary.GetMangledName(factory.NameMangler)); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("DispatchCell_"); + sb.Append(nameMangler.GetMangledMethodName(_method)); + } + + public override string ToString() => $"DispatchCell: {_method}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.InterfaceCellDictionarySlot(_method); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.InterfaceDispatchCell, _method); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_method, ((VirtualDispatchCellGenericLookupResult)other)._method); + } + + protected override int GetHashCodeImpl() + { + return _method.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((VirtualDispatchCellGenericLookupResult)obj)._method == _method; + } + } + + /// + /// Generic lookup result that points to the non-GC static base of a type. + /// + internal sealed class TypeNonGCStaticBaseGenericLookupResult : GenericLookupResult + { + private MetadataType _type; + + protected override int ClassCode => -328863267; + + public TypeNonGCStaticBaseGenericLookupResult(TypeDesc type) + { + Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete static base in a generic dictionary?"); + Debug.Assert(type is MetadataType); + _type = (MetadataType)type; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + var instantiatedType = (MetadataType)_type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + return factory.TypeNonGCStaticsSymbol(instantiatedType); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("NonGCStaticBase_"); + sb.Append(nameMangler.GetMangledTypeName(_type)); + } + + public override string ToString() => $"NonGCStaticBase: {_type}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.NonGcStaticDictionarySlot(_type); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.NonGcStatic, _type); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_type, ((TypeNonGCStaticBaseGenericLookupResult)other)._type); + } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((TypeNonGCStaticBaseGenericLookupResult)obj)._type == _type; + } + } + + /// + /// Generic lookup result that points to the threadstatic base index of a type. + /// + internal sealed class TypeThreadStaticBaseIndexGenericLookupResult : GenericLookupResult + { + private MetadataType _type; + + protected override int ClassCode => -177446371; + + public TypeThreadStaticBaseIndexGenericLookupResult(TypeDesc type) + { + Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete static base in a generic dictionary?"); + Debug.Assert(type is MetadataType); + _type = (MetadataType)type; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + var instantiatedType = (MetadataType)_type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + return factory.TypeThreadStaticIndex(instantiatedType); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("ThreadStaticBase_"); + sb.Append(nameMangler.GetMangledTypeName(_type)); + } + + public override string ToString() => $"ThreadStaticBase: {_type}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.ThreadStaticBaseIndexDictionarySlotNode(_type); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + // TODO + throw new NotImplementedException(); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_type, ((TypeThreadStaticBaseIndexGenericLookupResult)other)._type); + } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((TypeThreadStaticBaseIndexGenericLookupResult)obj)._type == _type; + } + } + + /// + /// Generic lookup result that points to the GC static base of a type. + /// + public sealed class TypeGCStaticBaseGenericLookupResult : GenericLookupResult + { + private MetadataType _type; + + protected override int ClassCode => 429225829; + + public TypeGCStaticBaseGenericLookupResult(TypeDesc type) + { + Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete static base in a generic dictionary?"); + Debug.Assert(type is MetadataType); + _type = (MetadataType)type; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + var instantiatedType = (MetadataType)_type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + return factory.TypeGCStaticsSymbol(instantiatedType); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("GCStaticBase_"); + sb.Append(nameMangler.GetMangledTypeName(_type)); + } + + public MetadataType Type => _type; + public override string ToString() => $"GCStaticBase: {_type}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.GcStaticDictionarySlot(_type); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.GcStatic, _type); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_type, ((TypeGCStaticBaseGenericLookupResult)other)._type); + } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((TypeGCStaticBaseGenericLookupResult)obj)._type == _type; + } + } + + /// + /// Generic lookup result that points to an object allocator. + /// + internal sealed class ObjectAllocatorGenericLookupResult : GenericLookupResult + { + private TypeDesc _type; + + protected override int ClassCode => -1671431655; + + public ObjectAllocatorGenericLookupResult(TypeDesc type) + { + Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete type in a generic dictionary?"); + _type = type; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + return factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(instantiatedType)); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("AllocObject_"); + sb.Append(nameMangler.GetMangledTypeName(_type)); + } + + public override string ToString() => $"AllocObject: {_type}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.AllocateObjectDictionarySlot(_type); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.AllocObject, _type); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_type, ((ObjectAllocatorGenericLookupResult)other)._type); + } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((ObjectAllocatorGenericLookupResult)obj)._type == _type; + } + } + + /// + /// Generic lookup result that points to an array allocator. + /// + internal sealed class ArrayAllocatorGenericLookupResult : GenericLookupResult + { + private TypeDesc _type; + + protected override int ClassCode => -927905284; + + public ArrayAllocatorGenericLookupResult(TypeDesc type) + { + Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete type in a generic dictionary?"); + _type = type; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + Debug.Assert(instantiatedType.IsArray); + return factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(instantiatedType)); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("AllocArray_"); + sb.Append(nameMangler.GetMangledTypeName(_type)); + } + + public override string ToString() => $"AllocArray: {_type}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.AllocateArrayDictionarySlot(_type); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.AllocArray, _type); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_type, ((ArrayAllocatorGenericLookupResult)other)._type); + } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((ArrayAllocatorGenericLookupResult)obj)._type == _type; + } + } + + /// + /// Generic lookup result that points to an cast helper. + /// + internal sealed class CastClassGenericLookupResult : GenericLookupResult + { + private TypeDesc _type; + + protected override int ClassCode => 1691016084; + + public CastClassGenericLookupResult(TypeDesc type) + { + Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete type in a generic dictionary?"); + _type = type; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + return factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(instantiatedType, true)); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("CastClass_"); + sb.Append(nameMangler.GetMangledTypeName(_type)); + } + + public override string ToString() => $"CastClass: {_type}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.CastClassDictionarySlot(_type); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.CastClass, _type); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_type, ((CastClassGenericLookupResult)other)._type); + } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((CastClassGenericLookupResult)obj)._type == _type; + } + } + + /// + /// Generic lookup result that points to an isInst helper. + /// + internal sealed class IsInstGenericLookupResult : GenericLookupResult + { + private TypeDesc _type; + + protected override int ClassCode => 1724059349; + + public IsInstGenericLookupResult(TypeDesc type) + { + Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete type in a generic dictionary?"); + _type = type; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + return factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(instantiatedType, false)); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("IsInst_"); + sb.Append(nameMangler.GetMangledTypeName(_type)); + } + + public override string ToString() => $"IsInst: {_type}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.IsInstDictionarySlot(_type); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.IsInst, _type); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_type, ((IsInstGenericLookupResult)other)._type); + } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((IsInstGenericLookupResult)obj)._type == _type; + } + } + + internal sealed class DefaultConstructorLookupResult : GenericLookupResult + { + private TypeDesc _type; + + protected override int ClassCode => -1391112482; + + public DefaultConstructorLookupResult(TypeDesc type) + { + Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete type in a generic dictionary?"); + _type = type; + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + MethodDesc defaultCtor = Compilation.GetConstructorForCreateInstanceIntrinsic(instantiatedType); + return factory.CanonicalEntrypoint(defaultCtor); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("DefaultCtor_"); + sb.Append(nameMangler.GetMangledTypeName(_type)); + } + + public override string ToString() => $"DefaultConstructor: {_type}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.DefaultConstructorDictionarySlot(_type); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.DefaultCtor, _type); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_type, ((DefaultConstructorLookupResult)other)._type); + } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((DefaultConstructorLookupResult)obj)._type == _type; + } + } + + internal sealed class CallingConventionConverterLookupResult : GenericLookupResult + { + private CallingConventionConverterKey _callingConventionConverter; + + protected override int ClassCode => -581806472; + + public CallingConventionConverterLookupResult(CallingConventionConverterKey callingConventionConverter) + { + _callingConventionConverter = callingConventionConverter; + Debug.Assert(Internal.Runtime.UniversalGenericParameterLayout.MethodSignatureHasVarsNeedingCallingConventionConverter(callingConventionConverter.Signature)); + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + Debug.Fail("GetTarget for a CallingConventionConverterLookupResult doesn't make sense. It isn't a pointer being emitted"); + return null; + } + + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) + { + Debug.Fail("CallingConventionConverterLookupResult contents should only be generated into generic dictionaries at runtime"); + builder.EmitNaturalInt(0); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("CallingConventionConverterLookupResult_"); + sb.Append(_callingConventionConverter.GetName()); + } + + public override string ToString() => "CallingConventionConverterLookupResult"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.CallingConventionConverter(_callingConventionConverter); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + // TODO + throw new NotImplementedException(); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + var otherEntry = (CallingConventionConverterLookupResult)other; + int result = (int)(_callingConventionConverter.ConverterKind - otherEntry._callingConventionConverter.ConverterKind); + if (result != 0) + return result; + + return comparer.Compare(_callingConventionConverter.Signature, otherEntry._callingConventionConverter.Signature); + } + + protected override int GetHashCodeImpl() + { + return _callingConventionConverter.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((CallingConventionConverterLookupResult)obj)._callingConventionConverter.Equals(_callingConventionConverter); + } + } + + internal sealed class TypeSizeLookupResult : GenericLookupResult + { + private TypeDesc _type; + + protected override int ClassCode => -367755250; + + public TypeSizeLookupResult(TypeDesc type) + { + _type = type; + Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete type in a generic dictionary?"); + } + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + Debug.Fail("GetTarget for a TypeSizeLookupResult doesn't make sense. It isn't a pointer being emitted"); + return null; + } + + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) + { + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + int typeSize; + + if (_type.IsDefType) + { + typeSize = ((DefType)_type).InstanceFieldSize.AsInt; + } + else + { + typeSize = factory.TypeSystemContext.Target.PointerSize; + } + + builder.EmitNaturalInt(typeSize); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("TypeSize_"); + sb.Append(nameMangler.GetMangledTypeName(_type)); + } + + public override string ToString() => $"TypeSize: {_type}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.TypeSizeDictionarySlot(_type); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.TypeSize, _type); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + return comparer.Compare(_type, ((TypeSizeLookupResult)other)._type); + } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((TypeSizeLookupResult)obj)._type == _type; + } + } + + internal sealed class ConstrainedMethodUseLookupResult : GenericLookupResult + { + MethodDesc _constrainedMethod; + TypeDesc _constraintType; + bool _directCall; + + protected override int ClassCode => -1525377658; + + public ConstrainedMethodUseLookupResult(MethodDesc constrainedMethod, TypeDesc constraintType, bool directCall) + { + _constrainedMethod = constrainedMethod; + _constraintType = constraintType; + _directCall = directCall; + + Debug.Assert(_constraintType.IsRuntimeDeterminedSubtype || _constrainedMethod.IsRuntimeDeterminedExactMethod, "Concrete type in a generic dictionary?"); + Debug.Assert(!_constrainedMethod.HasInstantiation || !_directCall, "Direct call to constrained generic method isn't supported"); + } + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + MethodDesc instantiatedConstrainedMethod = _constrainedMethod.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + TypeDesc instantiatedConstraintType = _constraintType.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + MethodDesc implMethod = instantiatedConstrainedMethod; + + if (implMethod.OwningType.IsInterface) + { + implMethod = instantiatedConstraintType.GetClosestDefType().ResolveVariantInterfaceMethodToVirtualMethodOnType(implMethod); + } + + implMethod = instantiatedConstraintType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(implMethod); + + // AOT use of this generic lookup is restricted to finding methods on valuetypes (runtime usage of this slot in universal generics is more flexible) + Debug.Assert(instantiatedConstraintType.IsValueType); + Debug.Assert(implMethod.OwningType == instantiatedConstraintType); + + if (implMethod.HasInstantiation) + { + return factory.ExactCallableAddress(implMethod); + } + else + { + return factory.CanonicalEntrypoint(implMethod); + } + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("ConstrainedMethodUseLookupResult_"); + sb.Append(nameMangler.GetMangledTypeName(_constraintType)); + sb.Append(nameMangler.GetMangledMethodName(_constrainedMethod)); + if (_directCall) + sb.Append("Direct"); + } + + public override string ToString() => $"ConstrainedMethodUseLookupResult: {_constraintType} {_constrainedMethod} {_directCall}"; + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.ConstrainedMethodUse(_constrainedMethod, _constraintType, _directCall); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + // TODO + throw new NotImplementedException(); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + var otherResult = (ConstrainedMethodUseLookupResult)other; + int result = (_directCall ? 1 : 0) - (otherResult._directCall ? 1 : 0); + if (result != 0) + return result; + + result = comparer.Compare(_constraintType, otherResult._constraintType); + if (result != 0) + return result; + + return comparer.Compare(_constrainedMethod, otherResult._constrainedMethod); + } + + protected override int GetHashCodeImpl() + { + return _constrainedMethod.GetHashCode() * 13 + _constraintType.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + var other = (ConstrainedMethodUseLookupResult)obj; + return _constrainedMethod == other._constrainedMethod && + _constraintType == other._constraintType && + _directCall == other._directCall; + } + } + + public sealed class IntegerLookupResult : GenericLookupResult + { + int _integerValue; + + public IntegerLookupResult(int integer) + { + _integerValue = integer; + } + + public int IntegerValue => _integerValue; + + protected override int ClassCode => 385752509; + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + return null; + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("IntegerLookupResult_").Append(_integerValue.ToString("x")); + } + + public override string ToString() + { + return "IntegerLookupResult_" + _integerValue.ToString("x"); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + IntegerLookupResult lookupResultOther = (IntegerLookupResult)other; + if (lookupResultOther._integerValue == _integerValue) + return 0; + + return _integerValue > lookupResultOther._integerValue ? 1 : -1; + } + + protected override bool EqualsImpl(GenericLookupResult other) + { + IntegerLookupResult lookupResultOther = (IntegerLookupResult)other; + return lookupResultOther._integerValue == _integerValue; + } + + protected override int GetHashCodeImpl() + { + return _integerValue; + } + + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) + { + builder.EmitNaturalInt(_integerValue); + } + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.IntegerSlot(_integerValue); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteIntegerSlot(_integerValue); + } + } + + public sealed class PointerToSlotLookupResult : GenericLookupResult + { + int _slotIndex; + + public PointerToSlotLookupResult(int slotIndex) + { + _slotIndex = slotIndex; + } + + public int SlotIndex => _slotIndex; + + protected override int ClassCode => 551050755; + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + return null; + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("PointerToSlotLookupResult_").Append(_slotIndex.ToString("x")); + } + + public override string ToString() + { + return "PointerToSlotLookupResult_" + _slotIndex.ToString("x"); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + PointerToSlotLookupResult pointerToSlotResultOther = (PointerToSlotLookupResult)other; + if (pointerToSlotResultOther._slotIndex == _slotIndex) + return 0; + + return _slotIndex > pointerToSlotResultOther._slotIndex ? 1 : -1; + } + + protected override bool EqualsImpl(GenericLookupResult other) + { + PointerToSlotLookupResult pointerToSlotResultOther = (PointerToSlotLookupResult)other; + return pointerToSlotResultOther._slotIndex == _slotIndex; + } + + protected override int GetHashCodeImpl() + { + return _slotIndex; + } + + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) + { + builder.EmitPointerReloc(dictionaryNode, _slotIndex * factory.Target.PointerSize); + } + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.PointerToOtherSlot(_slotIndex); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + // Under no circumstance should we attempt to write out a pointer to slot result + throw new InvalidProgramException(); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericMethodsHashtableNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericMethodsHashtableNode.cs new file mode 100644 index 00000000000000..85ace207c8600a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericMethodsHashtableNode.cs @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a hashtable of all compiled generic method instantiations + /// + public sealed class GenericMethodsHashtableNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public GenericMethodsHashtableNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__generic_methods_hashtable_End", true); + _externalReferences = externalReferences; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__generic_methods_hashtable"); + } + + public ISymbolNode EndSymbol => _endSymbol; + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + // Ensure the native layout data has been saved, in order to get valid Vertex offsets for the signature Vertices + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + NativeWriter nativeWriter = new NativeWriter(); + VertexHashtable hashtable = new VertexHashtable(); + Section nativeSection = nativeWriter.NewSection(); + nativeSection.Place(hashtable); + + foreach (var dictionaryNode in factory.MetadataManager.GetCompiledGenericDictionaries()) + { + MethodGenericDictionaryNode methodDictionary = dictionaryNode as MethodGenericDictionaryNode; + if (methodDictionary == null) + continue; + + MethodDesc method = methodDictionary.OwningMethod; + + Debug.Assert(method.HasInstantiation && !method.IsCanonicalMethod(CanonicalFormKind.Any)); + + Vertex fullMethodSignature; + { + // Method's containing type + IEETypeNode containingTypeNode = factory.NecessaryTypeSymbol(method.OwningType); + Vertex containingType = nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(containingTypeNode)); + + // Method's instantiation arguments + VertexSequence arguments = new VertexSequence(); + for (int i = 0; i < method.Instantiation.Length; i++) + { + IEETypeNode argNode = factory.NecessaryTypeSymbol(method.Instantiation[i]); + arguments.Append(nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(argNode))); + } + + // Method name and signature + NativeLayoutVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition()); + NativeLayoutSavedVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig); + Vertex placedNameAndSigVertexOffset = nativeWriter.GetUnsignedConstant((uint)placedNameAndSig.SavedVertex.VertexOffset); + + fullMethodSignature = nativeWriter.GetTuple(containingType, placedNameAndSigVertexOffset, arguments); + } + + // Method's dictionary pointer + Vertex dictionaryVertex = nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(dictionaryNode)); + + Vertex entry = nativeWriter.GetTuple(dictionaryVertex, fullMethodSignature); + + hashtable.Append((uint)method.GetHashCode(), nativeSection.Place(entry)); + } + + byte[] streamBytes = nativeWriter.Save(); + + _endSymbol.SetSymbolOffset(streamBytes.Length); + + return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + public static void GetGenericMethodsHashtableDependenciesForMethod(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + Debug.Assert(method.HasInstantiation && !method.IsCanonicalMethod(CanonicalFormKind.Any)); + + // Method's containing type + IEETypeNode containingTypeNode = factory.NecessaryTypeSymbol(method.OwningType); + dependencies.Add(new DependencyListEntry(containingTypeNode, "GenericMethodsHashtable entry containing type")); + + // Method's instantiation arguments + for (int i = 0; i < method.Instantiation.Length; i++) + { + IEETypeNode argNode = factory.NecessaryTypeSymbol(method.Instantiation[i]); + dependencies.Add(new DependencyListEntry(argNode, "GenericMethodsHashtable entry instantiation argument")); + } + + // Method name and signature + NativeLayoutVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition()); + NativeLayoutSavedVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig); + dependencies.Add(new DependencyListEntry(placedNameAndSig, "GenericMethodsHashtable entry signature")); + + ISymbolNode dictionaryNode = factory.MethodGenericDictionary(method); + dependencies.Add(new DependencyListEntry(dictionaryNode, "GenericMethodsHashtable entry dictionary")); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.GenericMethodsHashtableNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericMethodsTemplateMap.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericMethodsTemplateMap.cs new file mode 100644 index 00000000000000..a34b49a88151c7 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericMethodsTemplateMap.cs @@ -0,0 +1,118 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Hashtable of all generic method templates used by the TypeLoader at runtime + /// + public sealed class GenericMethodsTemplateMap : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public GenericMethodsTemplateMap(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__GenericMethodsTemplateMap_End", true); + _externalReferences = externalReferences; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__GenericMethodsTemplateMap"); + } + + public ISymbolNode EndSymbol => _endSymbol; + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // Dependencies for this node are tracked by the method code nodes + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + // Ensure the native layout data has been saved, in order to get valid Vertex offsets for the signature Vertices + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + NativeWriter nativeWriter = new NativeWriter(); + VertexHashtable hashtable = new VertexHashtable(); + Section nativeSection = nativeWriter.NewSection(); + nativeSection.Place(hashtable); + + + foreach (var methodEntryNode in factory.MetadataManager.GetTemplateMethodEntries()) + { + // Method entry + Vertex methodEntry = methodEntryNode.SavedVertex; + + // Method's native layout info + var layoutNode = factory.NativeLayout.TemplateMethodLayout(methodEntryNode.Method); + Debug.Assert(layoutNode.Marked); + Vertex nativeLayout = layoutNode.SavedVertex; + + // Hashtable Entry + Vertex entry = nativeWriter.GetTuple( + nativeWriter.GetUnsignedConstant((uint)methodEntry.VertexOffset), + nativeWriter.GetUnsignedConstant((uint)nativeLayout.VertexOffset)); + + // Add to the hash table, hashed by the containing type's hashcode + uint hashCode = (uint)methodEntryNode.Method.GetHashCode(); + hashtable.Append(hashCode, nativeSection.Place(entry)); + } + + byte[] streamBytes = nativeWriter.Save(); + + _endSymbol.SetSymbolOffset(streamBytes.Length); + + return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + public static void GetTemplateMethodDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + if (!IsEligibleToBeATemplate(method)) + return; + + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(new DependencyListEntry(factory.NativeLayout.TemplateMethodEntry(method), "Template Method Entry")); + dependencies.Add(new DependencyListEntry(factory.NativeLayout.TemplateMethodLayout(method), "Template Method Layout")); + } + + private static bool IsEligibleToBeATemplate(MethodDesc method) + { + if (!method.HasInstantiation) + return false; + + if (method.IsAbstract) + return false; + + if (method.IsCanonicalMethod(CanonicalFormKind.Specific)) + { + // Must be fully canonical + Debug.Assert(method == method.GetCanonMethodTarget(CanonicalFormKind.Specific)); + return true; + } + else if (method.IsCanonicalMethod(CanonicalFormKind.Universal)) + { + // Must be fully canonical + if (method == method.GetCanonMethodTarget(CanonicalFormKind.Universal)) + return true; + } + + return false; + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.GenericMethodsTemplateMap; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs new file mode 100644 index 00000000000000..43a6e027ba0023 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a hashtable of all compiled generic type instantiations + /// + public sealed class GenericTypesHashtableNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public GenericTypesHashtableNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__generic_types_hashtable_End", true); + _externalReferences = externalReferences; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__generic_types_hashtable"); + } + + public ISymbolNode EndSymbol => _endSymbol; + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + NativeWriter nativeWriter = new NativeWriter(); + VertexHashtable hashtable = new VertexHashtable(); + Section nativeSection = nativeWriter.NewSection(); + nativeSection.Place(hashtable); + + // We go over constructed EETypes only. The places that need to consult this hashtable at runtime + // all need constructed EETypes. Placing unconstructed EETypes into this hashtable could make us + // accidentally satisfy e.g. MakeGenericType for something that was only used in a cast. Those + // should throw MissingRuntimeArtifact instead. + // + // We already make sure "necessary" EETypes that could potentially be loaded at runtime through + // the dynamic type loader get upgraded to constructed EETypes at AOT compile time. + foreach (var type in factory.MetadataManager.GetTypesWithConstructedEETypes()) + { + // If this is an instantiated non-canonical generic type, add it to the generic instantiations hashtable + if (!type.HasInstantiation || type.IsGenericDefinition || type.IsCanonicalSubtype(CanonicalFormKind.Any)) + continue; + + var typeSymbol = factory.NecessaryTypeSymbol(type); + uint instantiationId = _externalReferences.GetIndex(typeSymbol); + Vertex hashtableEntry = nativeWriter.GetUnsignedConstant(instantiationId); + + hashtable.Append((uint)type.GetHashCode(), nativeSection.Place(hashtableEntry)); + } + + byte[] streamBytes = nativeWriter.Save(); + + _endSymbol.SetSymbolOffset(streamBytes.Length); + + return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.GenericTypesHashtableNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs new file mode 100644 index 00000000000000..150190833d2c19 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.Text; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Hashtable of all generic type templates used by the TypeLoader at runtime + /// + public sealed class GenericTypesTemplateMap : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public GenericTypesTemplateMap(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__GenericTypesTemplateMap_End", true); + _externalReferences = externalReferences; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__GenericTypesTemplateMap"); + } + + public ISymbolNode EndSymbol => _endSymbol; + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // Dependencies for this node are tracked by the method code nodes + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + // Ensure the native layout data has been saved, in order to get valid Vertex offsets for the signature Vertices + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + NativeWriter nativeWriter = new NativeWriter(); + VertexHashtable hashtable = new VertexHashtable(); + Section nativeSection = nativeWriter.NewSection(); + nativeSection.Place(hashtable); + + foreach (TypeDesc type in factory.MetadataManager.GetTypesWithConstructedEETypes()) + { + if (!IsEligibleToHaveATemplate(type)) + continue; + + // Type's native layout info + NativeLayoutTemplateTypeLayoutVertexNode templateNode = factory.NativeLayout.TemplateTypeLayout(type); + + // If this template isn't considered necessary, don't emit it. + if (!templateNode.Marked) + continue; + Vertex nativeLayout = templateNode.SavedVertex; + + // Hashtable Entry + Vertex entry = nativeWriter.GetTuple( + nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(factory.NecessaryTypeSymbol(type))), + nativeWriter.GetUnsignedConstant((uint)nativeLayout.VertexOffset)); + + // Add to the hash table, hashed by the containing type's hashcode + uint hashCode = (uint)type.GetHashCode(); + hashtable.Append(hashCode, nativeSection.Place(entry)); + } + + byte[] streamBytes = nativeWriter.Save(); + + _endSymbol.SetSymbolOffset(streamBytes.Length); + + return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + public static void GetTemplateTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + TypeDesc templateType = ConvertArrayOfTToRegularArray(factory, type); + + if (!IsEligibleToHaveATemplate(templateType)) + return; + + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(new DependencyListEntry(factory.NecessaryTypeSymbol(templateType), "Template type")); + dependencies.Add(new DependencyListEntry(factory.NativeLayout.TemplateTypeLayout(templateType), "Template Type Layout")); + } + + /// + /// Array<T> should not get an MethodTable in our system. All templates should replace + /// references to Array<T> types with regular arrays. + /// + public static TypeDesc ConvertArrayOfTToRegularArray(NodeFactory factory, TypeDesc type) + { + if (type.HasSameTypeDefinition(factory.ArrayOfTClass)) + { + Debug.Assert(type.Instantiation.Length == 1); + return factory.TypeSystemContext.GetArrayType(type.Instantiation[0]); + } + + return type; + } + + public static bool IsArrayTypeEligibleForTemplate(TypeDesc type) + { + if (!type.IsSzArray) + return false; + + // Unmanaged Pointer and Function pointer arrays can't use the array template + ArrayType arrayType = (ArrayType)type; + TypeDesc elementType = arrayType.ElementType; + + return !elementType.IsPointer && !elementType.IsFunctionPointer; + } + + public static bool IsEligibleToHaveATemplate(TypeDesc type) + { + if (!type.HasInstantiation && !IsArrayTypeEligibleForTemplate(type)) + return false; + + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + // Must be fully canonical + Debug.Assert(type == type.ConvertToCanonForm(CanonicalFormKind.Specific)); + return true; + } + + return false; + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.GenericTypesTemplateMap; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs new file mode 100644 index 00000000000000..207af80ae9bebc --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs @@ -0,0 +1,146 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a map of generic virtual method implementations. + /// + public sealed class GenericVirtualMethodTableNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + private Dictionary> _gvmImplemenations; + + public GenericVirtualMethodTableNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__gvm_table_End", true); + _externalReferences = externalReferences; + _gvmImplemenations = new Dictionary>(); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__gvm_table"); + } + + public ISymbolNode EndSymbol => _endSymbol; + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + /// + /// Helper method to compute the dependencies that would be needed by a hashtable entry for a GVM call. + /// This helper is used by the TypeGVMEntriesNode, which is used by the dependency analysis to compute the + /// GVM hashtable entries for the compiled types. + /// The dependencies returned from this function will be reported as static dependencies of the TypeGVMEntriesNode, + /// which we create for each type that has generic virtual methods. + /// + public static void GetGenericVirtualMethodImplementationDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc callingMethod, MethodDesc implementationMethod) + { + Debug.Assert(!callingMethod.OwningType.IsInterface); + + // Compute the open method signatures + MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition(); + MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition(); + + var openCallingMethodNameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(openCallingMethod); + var openImplementationMethodNameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(openImplementationMethod); + + dependencies.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openCallingMethodNameAndSig), "gvm table calling method signature")); + dependencies.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openImplementationMethodNameAndSig), "gvm table implementation method signature")); + } + + private void AddGenericVirtualMethodImplementation(NodeFactory factory, MethodDesc callingMethod, MethodDesc implementationMethod) + { + Debug.Assert(!callingMethod.OwningType.IsInterface); + + // Compute the open method signatures + MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition(); + MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition(); + + // Insert open method signatures into the GVM map + if (!_gvmImplemenations.ContainsKey(openCallingMethod)) + _gvmImplemenations[openCallingMethod] = new Dictionary(); + + _gvmImplemenations[openCallingMethod][openImplementationMethod.OwningType] = openImplementationMethod; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + // Build the GVM table entries from the list of interesting GVMTableEntryNodes + foreach (var interestingEntry in factory.MetadataManager.GetTypeGVMEntries()) + { + foreach (var typeGVMEntryInfo in interestingEntry.ScanForGenericVirtualMethodEntries()) + { + AddGenericVirtualMethodImplementation(factory, typeGVMEntryInfo.CallingMethod, typeGVMEntryInfo.ImplementationMethod); + } + } + + // Ensure the native layout blob has been saved + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + NativeWriter nativeFormatWriter = new NativeWriter(); + VertexHashtable gvmHashtable = new VertexHashtable(); + + Section gvmHashtableSection = nativeFormatWriter.NewSection(); + gvmHashtableSection.Place(gvmHashtable); + + // Emit the GVM target information entries + foreach (var gvmEntry in _gvmImplemenations) + { + Debug.Assert(!gvmEntry.Key.OwningType.IsInterface); + + foreach (var implementationEntry in gvmEntry.Value) + { + MethodDesc callingMethod = gvmEntry.Key; + TypeDesc implementationType = implementationEntry.Key; + MethodDesc implementationMethod = implementationEntry.Value; + + uint callingTypeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(callingMethod.OwningType)); + Vertex vertex = nativeFormatWriter.GetUnsignedConstant(callingTypeId); + + uint targetTypeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(implementationType)); + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant(targetTypeId)); + + var nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(callingMethod)); + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset)); + + nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(implementationMethod)); + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset)); + + int hashCode = callingMethod.OwningType.GetHashCode(); + hashCode = ((hashCode << 13) ^ hashCode) ^ implementationType.GetHashCode(); + + gvmHashtable.Append((uint)hashCode, gvmHashtableSection.Place(vertex)); + } + } + + // Zero out the dictionary so that we AV if someone tries to insert after we're done. + _gvmImplemenations = null; + + byte[] streamBytes = nativeFormatWriter.Save(); + + _endSymbol.SetSymbolOffset(streamBytes.Length); + + return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.GenericVirtualMethodTableNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/HelperEntrypoint.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/HelperEntrypoint.cs new file mode 100644 index 00000000000000..c739d0d7f0cccd --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/HelperEntrypoint.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ILCompiler.DependencyAnalysis +{ + public enum HelperEntrypoint + { + EnsureClassConstructorRunAndReturnGCStaticBase, + EnsureClassConstructorRunAndReturnNonGCStaticBase, + EnsureClassConstructorRunAndReturnThreadStaticBase, + GetThreadStaticBaseForType, + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IEETypeNode.cs new file mode 100644 index 00000000000000..583dc008d057eb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IEETypeNode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// A dependency analysis node that represents a runtime type data structure. + /// + public interface IEETypeNode : ISortableSymbolNode + { + TypeDesc Type { get; } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ILScanNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ILScanNodeFactory.cs new file mode 100644 index 00000000000000..25912caeb15df8 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ILScanNodeFactory.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Node factory to be used during IL scanning. + /// + public sealed class ILScanNodeFactory : NodeFactory + { + public ILScanNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, InteropStubManager interopStubManager, NameMangler nameMangler, PreinitializationManager preinitManager) + : base(context, compilationModuleGroup, metadataManager, interopStubManager, nameMangler, new LazyGenericsDisabledPolicy(), new LazyVTableSliceProvider(), new LazyDictionaryLayoutProvider(), new ExternSymbolsImportedNodeProvider(), preinitManager) + { + } + + protected override IMethodNode CreateMethodEntrypointNode(MethodDesc method) + { + if (method.IsInternalCall) + { + // TODO: come up with a scheme where this can be shared between codegen backends and the scanner + if (TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(method)) + { + return MethodEntrypoint(TypeSystemContext.GetRealSpecialUnboxingThunkTargetMethod(method)); + } + else if (TypeSystemContext.IsDefaultInterfaceMethodImplementationThunkTargetMethod(method)) + { + return MethodEntrypoint(TypeSystemContext.GetRealDefaultInterfaceMethodImplementationThunkTargetMethod(method)); + } + else if (method.IsArrayAddressMethod()) + { + return new ScannedMethodNode(((ArrayType)method.OwningType).GetArrayMethod(ArrayMethodKind.AddressWithHiddenArg)); + } + else if (method.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute")) + { + return new RuntimeImportMethodNode(method); + } + } + + if (CompilationModuleGroup.ContainsMethodBody(method, false)) + { + // We might be able to optimize the method body away if the owning type was never seen as allocated. + if (method.NotCallableWithoutOwningEEType() && CompilationModuleGroup.AllowInstanceMethodOptimization(method)) + return new TentativeInstanceMethodNode(new ScannedMethodNode(method)); + + return new ScannedMethodNode(method); + } + else + { + return new ExternMethodSymbolNode(this, method); + } + } + + protected override IMethodNode CreateUnboxingStubNode(MethodDesc method) + { + Debug.Assert(!method.Signature.IsStatic); + + if (method.IsCanonicalMethod(CanonicalFormKind.Specific) && !method.HasInstantiation) + { + // Unboxing stubs to canonical instance methods need a special unboxing stub that unboxes + // 'this' and also provides an instantiation argument (we do a calling convention conversion). + // We don't do this for generic instance methods though because they don't use the MethodTable + // for the generic context anyway. + return new ScannedMethodNode(TypeSystemContext.GetSpecialUnboxingThunk(method, TypeSystemContext.GeneratedAssembly)); + } + else + { + // Otherwise we just unbox 'this' and don't touch anything else. + return new UnboxingStubNode(method, Target); + } + } + + protected override ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall) + { + return new ReadyToRunHelperNode(helperCall.HelperId, helperCall.Target); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/INodeWithCodeInfo.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/INodeWithCodeInfo.Aot.cs new file mode 100644 index 00000000000000..b11a5819ca34fc --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/INodeWithCodeInfo.Aot.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace ILCompiler.DependencyAnalysis +{ + public interface INodeWithCodeInfo + { + FrameInfo[] FrameInfos + { + get; + } + + byte[] GCInfo + { + get; + } + + DebugEHClauseInfo[] DebugEHClauseInfos + { + get; + } + + MethodExceptionHandlingInfoNode EHInfo + { + get; + } + + ISymbolNode GetAssociatedDataNode(NodeFactory factory); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/INodeWithDebugInfo.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/INodeWithDebugInfo.cs new file mode 100644 index 00000000000000..bf195f0170d69b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/INodeWithDebugInfo.cs @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.JitInterface; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public readonly struct NativeSequencePoint + { + public readonly int NativeOffset; + public readonly string FileName; + public readonly int LineNumber; + public readonly int ColNumber; + + public NativeSequencePoint(int nativeOffset, string fileName, int lineNumber, int colNumber = 0) + { + NativeOffset = nativeOffset; + FileName = fileName; + LineNumber = lineNumber; + ColNumber = colNumber; + } + } + + public interface INodeWithDebugInfo + { + bool IsStateMachineMoveNextMethod { get; } + + IEnumerable GetNativeSequencePoints(); + + public IEnumerable GetDebugVars(); + } + + public readonly struct DebugVarInfoMetadata + { + public readonly string Name; + public readonly TypeDesc Type; + public readonly bool IsParameter; + public readonly DebugVarInfo DebugVarInfo; + + public DebugVarInfoMetadata(string name, TypeDesc type, bool isParameter, DebugVarInfo info) + => (Name, Type, IsParameter, DebugVarInfo) = (name, type, isParameter, info); + } + + public readonly struct DebugVarInfo + { + public readonly uint VarNumber; + public readonly DebugVarRangeInfo[] Ranges; + + public DebugVarInfo(uint varNumber, DebugVarRangeInfo[] ranges) + => (VarNumber, Ranges) = (varNumber, ranges); + } + + public readonly struct DebugVarRangeInfo + { + public readonly uint StartOffset; + public readonly uint EndOffset; + public readonly VarLoc VarLoc; + + public DebugVarRangeInfo(uint startOffset, uint endOffset, VarLoc varLoc) + => (StartOffset, EndOffset, VarLoc) = (startOffset, endOffset, varLoc); + } + + public static class WellKnownLineNumber + { + /// + /// Informs the debugger that it should step through the annotated sequence point. + /// + public const int DebuggerStepThrough = 0xF00F00; + + /// + /// Informs the debugger that it should step into the annotated sequence point. + /// + public const int DebuggerStepIn = 0xFEEFEE; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IObjectDumper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IObjectDumper.cs new file mode 100644 index 00000000000000..390351d5331e7c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IObjectDumper.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; + +namespace ILCompiler.DependencyAnalysis +{ + public interface IObjectDumper + { + void DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ISpecialUnboxThunkNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ISpecialUnboxThunkNode.cs new file mode 100644 index 00000000000000..1afba71128bc20 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ISpecialUnboxThunkNode.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// A dependency analysis node that represents a special instantiating unboxing stub. + /// + public interface ISpecialUnboxThunkNode : IMethodNode + { + bool IsSpecialUnboxingThunk { get; } + ISymbolNode GetUnboxingThunkTarget(NodeFactory factory); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ImportedNodeProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ImportedNodeProvider.cs new file mode 100644 index 00000000000000..5b2ac08a8bbd79 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ImportedNodeProvider.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Abstract api to allow creation of various different types of import nodes as might be exposed through the NodeFactory + /// + public abstract class ImportedNodeProvider + { + public abstract IEETypeNode ImportedEETypeNode(NodeFactory factory, TypeDesc type); + public abstract ISortableSymbolNode ImportedGCStaticNode(NodeFactory factory, MetadataType type); + public abstract ISortableSymbolNode ImportedNonGCStaticNode(NodeFactory factory, MetadataType type); + public abstract ISortableSymbolNode ImportedTypeDictionaryNode(NodeFactory factory, TypeDesc type); + public abstract ISortableSymbolNode ImportedMethodDictionaryNode(NodeFactory factory, MethodDesc method); + public abstract IMethodNode ImportedMethodCodeNode(NodeFactory factory, MethodDesc method, bool unboxingStub); + } + + public class ImportedNodeProviderThrowing : ImportedNodeProvider + { + public override IEETypeNode ImportedEETypeNode(NodeFactory factory, TypeDesc type) + { + throw new NotSupportedException(); + } + + public override ISortableSymbolNode ImportedGCStaticNode(NodeFactory factory, MetadataType type) + { + throw new NotSupportedException(); + } + + public override ISortableSymbolNode ImportedNonGCStaticNode(NodeFactory factory, MetadataType type) + { + throw new NotSupportedException(); + } + + public override ISortableSymbolNode ImportedTypeDictionaryNode(NodeFactory factory, TypeDesc type) + { + throw new NotSupportedException(); + } + + public override ISortableSymbolNode ImportedMethodDictionaryNode(NodeFactory factory, MethodDesc method) + { + throw new NotSupportedException(); + } + + public override IMethodNode ImportedMethodCodeNode(NodeFactory factory, MethodDesc method, bool unboxingStub) + { + throw new NotSupportedException(); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IndirectionExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IndirectionExtensions.cs new file mode 100644 index 00000000000000..c23ae00dd29d80 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IndirectionExtensions.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Runtime; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + static class IndirectionExtensions + { + /// + /// Use this api to generate a reloc to a symbol that may be an indirection cell or not as a pointer + /// + /// symbol to reference + /// value to OR in to the reloc to represent to runtime code that this pointer is an indirection. Defaults to IndirectionConstants.IndirectionCellPointer + /// Delta from symbol start for value + public static void EmitPointerRelocOrIndirectionReference(ref this ObjectDataBuilder builder, ISymbolNode symbol, int delta = 0, int indirectionBit = IndirectionConstants.IndirectionCellPointer) + { + if (symbol.RepresentsIndirectionCell) + delta |= indirectionBit; + + builder.EmitReloc(symbol, (builder.TargetPointerSize == 8) ? RelocType.IMAGE_REL_BASED_DIR64 : RelocType.IMAGE_REL_BASED_HIGHLOW, delta); + } + + public static void EmitRelativeRelocOrIndirectionReference(ref this ObjectDataBuilder builder, ISymbolNode symbol, int delta = 0, int indirectionBit = IndirectionConstants.IndirectionCellPointer) + { + if (symbol.RepresentsIndirectionCell) + delta = delta | indirectionBit; + + builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_RELPTR32, delta); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs new file mode 100644 index 00000000000000..fd0e943802f041 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs @@ -0,0 +1,129 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Runtime; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class InterfaceDispatchCellNode : EmbeddedObjectNode, ISymbolDefinitionNode + { + private readonly MethodDesc _targetMethod; + private readonly string _callSiteIdentifier; + + internal MethodDesc TargetMethod => _targetMethod; + + internal string CallSiteIdentifier => _callSiteIdentifier; + + public InterfaceDispatchCellNode(MethodDesc targetMethod, string callSiteIdentifier) + { + Debug.Assert(targetMethod.OwningType.IsInterface); + Debug.Assert(!targetMethod.IsSharedByGenericInstantiations); + _targetMethod = targetMethod; + _callSiteIdentifier = callSiteIdentifier; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetMangledName(nameMangler, _targetMethod, _callSiteIdentifier)); + } + + int ISymbolDefinitionNode.Offset => OffsetFromBeginningOfArray; + + int ISymbolNode.Offset => 0; + + public override bool IsShareable => false; + + public static string GetMangledName(NameMangler nameMangler, MethodDesc method, string callSiteIdentifier) + { + string name = nameMangler.CompilationUnitPrefix + "__InterfaceDispatchCell_" + nameMangler.GetMangledMethodName(method); + + if (!string.IsNullOrEmpty(callSiteIdentifier)) + { + name += "_" + callSiteIdentifier; + } + + return name; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyList result = new DependencyList(); + + if (!factory.VTable(_targetMethod.OwningType).HasFixedSlots) + { + result.Add(factory.VirtualMethodUse(_targetMethod), "Interface method use"); + } + + factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref result, factory, _targetMethod); + + TargetArchitecture targetArchitecture = factory.Target.Architecture; + if (targetArchitecture == TargetArchitecture.ARM) + { + result.Add(factory.InitialInterfaceDispatchStub, "Initial interface dispatch stub"); + } + else + { + result.Add(factory.ExternSymbol("RhpInitialDynamicInterfaceDispatch"), "Initial interface dispatch stub"); + } + + result.Add(factory.NecessaryTypeSymbol(_targetMethod.OwningType), "Interface type"); + + return result; + } + + public override void EncodeData(ref ObjectDataBuilder objData, NodeFactory factory, bool relocsOnly) + { + TargetArchitecture targetArchitecture = factory.Target.Architecture; + if (targetArchitecture == TargetArchitecture.ARM) + { + objData.EmitPointerReloc(factory.InitialInterfaceDispatchStub); + } + else + { + objData.EmitPointerReloc(factory.ExternSymbol("RhpInitialDynamicInterfaceDispatch")); + } + + IEETypeNode interfaceType = factory.NecessaryTypeSymbol(_targetMethod.OwningType); + if (interfaceType.RepresentsIndirectionCell) + { + objData.EmitReloc(interfaceType, RelocType.IMAGE_REL_BASED_RELPTR32, + (int)InterfaceDispatchCellCachePointerFlags.CachePointerIsIndirectedInterfaceRelativePointer); + } + else + { + objData.EmitReloc(interfaceType, RelocType.IMAGE_REL_BASED_RELPTR32, + (int)InterfaceDispatchCellCachePointerFlags.CachePointerIsInterfaceRelativePointer); + } + + if (objData.TargetPointerSize == 8) + { + // IMAGE_REL_BASED_RELPTR is a 32-bit relocation. However, the cell needs a full pointer + // width there since a pointer to the cache will be written into the cell. Emit additional + // 32 bits on targets whose pointer size is 64 bit. + objData.EmitInt(0); + } + } + + protected override void OnMarked(NodeFactory factory) + { + factory.InterfaceDispatchCellSection.AddEmbeddedObject(this); + } + + public override int ClassCode => -2023802120; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + var compare = comparer.Compare(_targetMethod, ((InterfaceDispatchCellNode)other)._targetMethod); + return compare != 0 ? compare : string.Compare(_callSiteIdentifier, ((InterfaceDispatchCellNode)other)._callSiteIdentifier); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellSectionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellSectionNode.cs new file mode 100644 index 00000000000000..da79598e1c6c2f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellSectionNode.cs @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a section of the executable where interface dispatch cells and their slot information + /// is stored. + /// + public class InterfaceDispatchCellSectionNode : ArrayOfEmbeddedDataNode + { + public InterfaceDispatchCellSectionNode(NodeFactory factory) + : base("__InterfaceDispatchCellSection_Start", "__InterfaceDispatchCellSection_End", new DispatchCellComparer(factory)) + { + } + + protected override void GetElementDataForNodes(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly) + { + if (relocsOnly) + return; + + // The interface dispatch cell has an alignment requirement of 2 * [Pointer size] as part of the + // synchronization mechanism of the two values in the runtime. + builder.RequireInitialAlignment(factory.Target.PointerSize * 2); + + // This number chosen to be high enough that the cost of recording slot numbers is cheap. + const int InterfaceDispatchCellRunLength = 32; + + const int NoSlot = -1; + + // + // We emit the individual dispatch cells in groups. The purpose of the grouping is to save + // us the number of slots we need to emit. The grouping looks like this: + // + // DispatchCell1 + // DispatchCell2 + // ... + // DispatchCellN + // Null + // Slot of the above dispatch cells + // + int runLength = 0; + int currentSlot = NoSlot; + foreach (InterfaceDispatchCellNode node in NodesList) + { + MethodDesc targetMethod = node.TargetMethod; + int targetSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); + if (currentSlot == NoSlot) + { + // This is the first dispatch cell we're emitting + currentSlot = targetSlot; + } + else if (currentSlot != targetSlot || runLength == InterfaceDispatchCellRunLength) + { + // Make sure we are sorted + Debug.Assert(targetSlot >= currentSlot); + + // End the run of dispatch cells + builder.EmitZeroPointer(); + builder.EmitNaturalInt(currentSlot); + + currentSlot = targetSlot; + runLength = 0; + } + + node.InitializeOffsetFromBeginningOfArray(builder.CountBytes); + node.EncodeData(ref builder, factory, relocsOnly); + builder.AddSymbol(node); + + runLength++; + } + + if (runLength > 0) + { + // End the run of dispatch cells + builder.EmitZeroPointer(); + builder.EmitNaturalInt(currentSlot); + } + } + + public override int ClassCode => -1389343; + + /// + /// Comparer that groups interface dispatch cells by their slot number. + /// + private class DispatchCellComparer : IComparer + { + private readonly NodeFactory _factory; + private readonly TypeSystemComparer _comparer = new TypeSystemComparer(); + + public DispatchCellComparer(NodeFactory factory) + { + _factory = factory; + } + + public int Compare(InterfaceDispatchCellNode x, InterfaceDispatchCellNode y) + { + MethodDesc methodX = x.TargetMethod; + MethodDesc methodY = y.TargetMethod; + + // The primary purpose of this comparer is to sort everything by slot + int slotX = VirtualMethodSlotHelper.GetVirtualMethodSlot(_factory, methodX, methodX.OwningType); + int slotY = VirtualMethodSlotHelper.GetVirtualMethodSlot(_factory, methodY, methodY.OwningType); + + int result = slotX - slotY; + if (result != 0) + return result; + + // If slots are the same, compare the method and callsite identifier to get + // a deterministic order within the group. + result = _comparer.Compare(methodX, methodY); + if (result != 0) + return result; + + result = StringComparer.Ordinal.Compare(x.CallSiteIdentifier, y.CallSiteIdentifier); + if (result != 0) + return result; + + Debug.Assert(x == y); + return 0; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs new file mode 100644 index 00000000000000..84688a534bf3e9 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs @@ -0,0 +1,255 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.Runtime; + +namespace ILCompiler.DependencyAnalysis +{ + public class InterfaceDispatchMapNode : ObjectNode, ISymbolDefinitionNode, ISortableSymbolNode + { + private readonly TypeDesc _type; + + public InterfaceDispatchMapNode(NodeFactory factory, TypeDesc type) + { + // Multidimensional arrays should not get a sealed vtable or a dispatch map. Runtime should use the + // sealed vtable and dispatch map of the System.Array basetype instead. + // Pointer arrays also follow the same path + Debug.Assert(!type.IsArrayTypeWithoutGenericInterfaces()); + Debug.Assert(MightHaveInterfaceDispatchMap(type, factory)); + + _type = type; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__InterfaceDispatchMap_").Append(nameMangler.SanitizeName(nameMangler.GetMangledTypeName(_type))); + } + + public int Offset => 0; + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + public override ObjectNodeSection Section + { + get + { + if (_type.Context.Target.IsWindows) + return ObjectNodeSection.FoldableReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + var result = new DependencyList(); + result.Add(factory.InterfaceDispatchMapIndirection(_type), "Interface dispatch map indirection node"); + + // VTable slots of implemented interfaces are consulted during emission + foreach (TypeDesc runtimeInterface in _type.RuntimeInterfaces) + { + result.Add(factory.VTable(runtimeInterface), "Interface for a dispatch map"); + } + + return result; + } + + /// + /// Gets a value indicating whether '' might have a non-empty dispatch map. + /// Note that this is only an approximation because we might not be able to take into account + /// whether the interface methods are actually used. + /// + public static bool MightHaveInterfaceDispatchMap(TypeDesc type, NodeFactory factory) + { + if (type.IsArrayTypeWithoutGenericInterfaces()) + return false; + + if (!type.IsArray && !type.IsDefType) + return false; + + // Interfaces don't have a dispatch map because we dispatch them based on the + // dispatch map of the implementing class. + // The only exception are IDynamicInterfaceCastable scenarios that dispatch + // using the interface dispatch map. + // We generate the dispatch map irrespective of whether the interface actually + // implements any methods (we don't run the for loop below) so that at runtime + // we can distinguish between "the interface returned by IDynamicInterfaceCastable + // wasn't marked as [DynamicInterfaceCastableImplementation]" and "we couldn't find an + // implementation". We don't want to use the custom attribute for that at runtime because + // that's reflection and this should work without reflection. + if (type.IsInterface) + return ((MetadataType)type).IsDynamicInterfaceCastableImplementation(); + + TypeDesc declType = type.GetClosestDefType(); + + for (int interfaceIndex = 0; interfaceIndex < declType.RuntimeInterfaces.Length; interfaceIndex++) + { + DefType interfaceType = declType.RuntimeInterfaces[interfaceIndex]; + InstantiatedType interfaceOnDefinitionType = interfaceType.IsTypeDefinition ? + null : + (InstantiatedType)declType.GetTypeDefinition().RuntimeInterfaces[interfaceIndex]; + + IEnumerable slots; + + // If the vtable has fixed slots, we can query it directly. + // If it's a lazily built vtable, we might not be able to query slots + // just yet, so approximate by looking at all methods. + VTableSliceNode vtableSlice = factory.VTable(interfaceType); + if (vtableSlice.HasFixedSlots) + slots = vtableSlice.Slots; + else + slots = interfaceType.GetAllVirtualMethods(); + + foreach (MethodDesc slotMethod in slots) + { + MethodDesc declMethod = slotMethod; + + Debug.Assert(!declMethod.Signature.IsStatic && declMethod.IsVirtual); + + if (interfaceOnDefinitionType != null) + declMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(declMethod.GetTypicalMethodDefinition(), interfaceOnDefinitionType); + + var implMethod = declType.GetTypeDefinition().ResolveInterfaceMethodToVirtualMethodOnType(declMethod); + if (implMethod != null) + { + return true; + } + else + { + DefaultInterfaceMethodResolution result = declType.ResolveInterfaceMethodToDefaultImplementationOnType(slotMethod, out _); + if (result != DefaultInterfaceMethodResolution.None) + return true; + } + } + } + + return false; + } + + void EmitDispatchMap(ref ObjectDataBuilder builder, NodeFactory factory) + { + var entryCountReservation = builder.ReserveShort(); + var defaultEntryCountReservation = builder.ReserveShort(); + int entryCount = 0; + + TypeDesc declType = _type.GetClosestDefType(); + TypeDesc declTypeDefinition = declType.GetTypeDefinition(); + DefType[] declTypeRuntimeInterfaces = declType.RuntimeInterfaces; + DefType[] declTypeDefinitionRuntimeInterfaces = declTypeDefinition.RuntimeInterfaces; + + // Catch any runtime interface collapsing. We shouldn't have any + Debug.Assert(declTypeRuntimeInterfaces.Length == declTypeDefinitionRuntimeInterfaces.Length); + + var defaultImplementations = new List<(int InterfaceIndex, int InterfaceMethodSlot, int ImplMethodSlot)>(); + + // Resolve all the interfaces, but only emit non-default implementations + for (int interfaceIndex = 0; interfaceIndex < declTypeRuntimeInterfaces.Length; interfaceIndex++) + { + var interfaceType = declTypeRuntimeInterfaces[interfaceIndex]; + var interfaceDefinitionType = declTypeDefinitionRuntimeInterfaces[interfaceIndex]; + Debug.Assert(interfaceType.IsInterface); + + IReadOnlyList virtualSlots = factory.VTable(interfaceType).Slots; + + for (int interfaceMethodSlot = 0; interfaceMethodSlot < virtualSlots.Count; interfaceMethodSlot++) + { + MethodDesc declMethod = virtualSlots[interfaceMethodSlot]; + if(!interfaceType.IsTypeDefinition) + declMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(declMethod.GetTypicalMethodDefinition(), (InstantiatedType)interfaceDefinitionType); + + var implMethod = declTypeDefinition.ResolveInterfaceMethodToVirtualMethodOnType(declMethod); + + // Interface methods first implemented by a base type in the hierarchy will return null for the implMethod (runtime interface + // dispatch will walk the inheritance chain). + if (implMethod != null) + { + TypeDesc implType = declType; + while (!implType.HasSameTypeDefinition(implMethod.OwningType)) + implType = implType.BaseType; + + MethodDesc targetMethod = implMethod; + if (!implType.IsTypeDefinition) + targetMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(implMethod.GetTypicalMethodDefinition(), (InstantiatedType)implType); + + builder.EmitShort((short)checked((ushort)interfaceIndex)); + builder.EmitShort((short)checked((ushort)(interfaceMethodSlot + (interfaceType.HasGenericDictionarySlot() ? 1 : 0)))); + builder.EmitShort((short)checked((ushort)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, declType))); + entryCount++; + } + else + { + // Is there a default implementation? + + int? implSlot = null; + + DefaultInterfaceMethodResolution result = declTypeDefinition.ResolveInterfaceMethodToDefaultImplementationOnType(declMethod, out implMethod); + if (result == DefaultInterfaceMethodResolution.DefaultImplementation) + { + DefType providingInterfaceDefinitionType = (DefType)implMethod.OwningType; + implMethod = implMethod.InstantiateSignature(declType.Instantiation, Instantiation.Empty); + implSlot = VirtualMethodSlotHelper.GetDefaultInterfaceMethodSlot(factory, implMethod, declType, providingInterfaceDefinitionType); + } + else if (result == DefaultInterfaceMethodResolution.Reabstraction) + { + implSlot = SpecialDispatchMapSlot.Reabstraction; + } + else if (result == DefaultInterfaceMethodResolution.Diamond) + { + implSlot = SpecialDispatchMapSlot.Diamond; + } + + if (implSlot.HasValue) + { + defaultImplementations.Add(( + interfaceIndex, + interfaceMethodSlot + (interfaceType.HasGenericDictionarySlot() ? 1 : 0), + implSlot.Value)); + } + } + } + } + + // Now emit the default implementations + foreach (var defaultImplementation in defaultImplementations) + { + builder.EmitShort((short)checked((ushort)defaultImplementation.InterfaceIndex)); + builder.EmitShort((short)checked((ushort)defaultImplementation.InterfaceMethodSlot)); + builder.EmitShort((short)checked((ushort)defaultImplementation.ImplMethodSlot)); + } + + // Update the header + builder.EmitShort(entryCountReservation, (short)checked((ushort)entryCount)); + builder.EmitShort(defaultEntryCountReservation, (short)checked((ushort)defaultImplementations.Count)); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialAlignment(16); + objData.AddSymbol(this); + + if (!relocsOnly) + { + EmitDispatchMap(ref objData, factory); + } + + return objData.ToObjectData(); + } + + public override int ClassCode => 848664602; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((InterfaceDispatchMapNode)other)._type); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs new file mode 100644 index 00000000000000..247ed24cc12cc1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs @@ -0,0 +1,236 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; +using Internal.Runtime; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a map between reflection metadata and generated method bodies. + /// + public sealed class InterfaceGenericVirtualMethodTableNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + private Dictionary> _interfaceGvmSlots; + private Dictionary>> _interfaceImpls; + + public InterfaceGenericVirtualMethodTableNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__interface_gvm_table_End", true); + _externalReferences = externalReferences; + _interfaceGvmSlots = new Dictionary>(); + _interfaceImpls = new Dictionary>>(); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__interface_gvm_table"); + } + public ISymbolNode EndSymbol => _endSymbol; + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + /// + /// Helper method to compute the dependencies that would be needed by a hashtable entry for an interface GVM call. + /// This helper is used by the TypeGVMEntriesNode, which is used by the dependency analysis to compute the + /// GVM hashtable entries for the compiled types. + /// The dependencies returned from this function will be reported as static dependencies of the TypeGVMEntriesNode, + /// which we create for each type that has generic virtual methods. + /// + public static void GetGenericVirtualMethodImplementationDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc callingMethod, TypeDesc implementationType, MethodDesc implementationMethod) + { + Debug.Assert(callingMethod.OwningType.IsInterface); + + // Compute the open method signatures + MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition(); + TypeDesc openImplementationType = implementationType.GetTypeDefinition(); + + var openCallingMethodNameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(openCallingMethod); + dependencies.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openCallingMethodNameAndSig), "interface gvm table calling method signature")); + + // Implementation could be null if this is a default interface method reabstraction or diamond. We need to record those. + if (implementationMethod != null) + { + MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition(); + var openImplementationMethodNameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(openImplementationMethod); + dependencies.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openImplementationMethodNameAndSig), "interface gvm table implementation method signature")); + } + + if (!openImplementationType.IsInterface) + { + for(int index = 0; index < openImplementationType.RuntimeInterfaces.Length; index++) + { + if (openImplementationType.RuntimeInterfaces[index] == callingMethod.OwningType) + { + TypeDesc currentInterface = openImplementationType.RuntimeInterfaces[index]; + var currentInterfaceSignature = factory.NativeLayout.TypeSignatureVertex(currentInterface); + dependencies.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(currentInterfaceSignature), "interface gvm table interface signature")); + } + } + } + } + + private void AddGenericVirtualMethodImplementation(NodeFactory factory, MethodDesc callingMethod, TypeDesc implementationType, MethodDesc implementationMethod, DefaultInterfaceMethodResolution resolution) + { + Debug.Assert(callingMethod.OwningType.IsInterface); + + // Compute the open method signatures + MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition(); + object openImplementationMethod = implementationMethod == null ? resolution : implementationMethod.GetTypicalMethodDefinition(); + TypeDesc openImplementationType = implementationType.GetTypeDefinition(); + + // Add the entry to the interface GVM slots mapping table + if (!_interfaceGvmSlots.ContainsKey(openCallingMethod)) + _interfaceGvmSlots[openCallingMethod] = new HashSet(); + _interfaceGvmSlots[openCallingMethod].Add(openImplementationMethod); + + // If the implementation method is implementing some interface method, compute which + // interface explicitly implemented on the type that the current method implements an interface method for. + // We need this because at runtime, the interfaces explicitly implemented on the type will have + // runtime-determined signatures that we can use to make generic substitutions and check for interface matching. + if (!openImplementationType.IsInterface) + { + if (!_interfaceImpls.ContainsKey(openImplementationMethod)) + _interfaceImpls[openImplementationMethod] = new Dictionary>(); + if (!_interfaceImpls[openImplementationMethod].ContainsKey(openImplementationType)) + _interfaceImpls[openImplementationMethod][openImplementationType] = new HashSet(); + + int numIfacesAdded = 0; + for (int index = 0; index < openImplementationType.RuntimeInterfaces.Length; index++) + { + if (openImplementationType.RuntimeInterfaces[index] == callingMethod.OwningType) + { + _interfaceImpls[openImplementationMethod][openImplementationType].Add(index); + numIfacesAdded++; + } + } + + Debug.Assert(numIfacesAdded > 0); + } + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + // Build the GVM table entries from the list of interesting GVMTableEntryNodes + foreach (var interestingEntry in factory.MetadataManager.GetTypeGVMEntries()) + { + foreach (var typeGVMEntryInfo in interestingEntry.ScanForInterfaceGenericVirtualMethodEntries()) + { + AddGenericVirtualMethodImplementation(factory, typeGVMEntryInfo.CallingMethod, typeGVMEntryInfo.ImplementationType, typeGVMEntryInfo.ImplementationMethod, typeGVMEntryInfo.DefaultResolution); + } + } + + // Ensure the native layout blob has been saved + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + NativeWriter nativeFormatWriter = new NativeWriter(); + VertexHashtable gvmHashtable = new VertexHashtable(); + + Section gvmHashtableSection = nativeFormatWriter.NewSection(); + gvmHashtableSection.Place(gvmHashtable); + + // Emit the interface slot resolution entries + foreach (var gvmEntry in _interfaceGvmSlots) + { + Debug.Assert(gvmEntry.Key.OwningType.IsInterface); + + MethodDesc callingMethod = gvmEntry.Key; + + // Emit the method signature and containing type of the current interface method + uint typeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(callingMethod.OwningType)); + var nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(callingMethod)); + Vertex vertex = nativeFormatWriter.GetTuple( + nativeFormatWriter.GetUnsignedConstant(typeId), + nativeFormatWriter.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset)); + + // Emit the method name / sig and containing type of each GVM target method for the current interface method entry + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)gvmEntry.Value.Count)); + foreach (object impl in gvmEntry.Value) + { + if (impl is MethodDesc implementationMethod) + { + nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(implementationMethod)); + typeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(implementationMethod.OwningType)); + vertex = nativeFormatWriter.GetTuple( + vertex, + nativeFormatWriter.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset), + nativeFormatWriter.GetUnsignedConstant(typeId)); + } + else + { + Debug.Assert(impl is DefaultInterfaceMethodResolution); + uint constant = (DefaultInterfaceMethodResolution)impl switch + { + DefaultInterfaceMethodResolution.Diamond => SpecialGVMInterfaceEntry.Diamond, + DefaultInterfaceMethodResolution.Reabstraction => SpecialGVMInterfaceEntry.Reabstraction, + _ => throw new NotImplementedException(), + }; + vertex = nativeFormatWriter.GetTuple( + vertex, + nativeFormatWriter.GetUnsignedConstant(constant)); + } + + // Emit the interface GVM slot details for each type that implements the interface methods + { + Debug.Assert(_interfaceImpls.ContainsKey(impl)); + + var ifaceImpls = _interfaceImpls[impl]; + + // First, emit how many types have method implementations for this interface method entry + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)ifaceImpls.Count)); + + // Emit each type that implements the interface method, and the interface signatures for the interfaces implemented by the type + foreach (var currentImpl in ifaceImpls) + { + TypeDesc implementationType = currentImpl.Key; + + typeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(implementationType)); + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant(typeId)); + + // Emit information on which interfaces the current method entry provides implementations for + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)currentImpl.Value.Count)); + foreach (var ifaceId in currentImpl.Value) + { + // Emit the signature of the current interface implemented by the method + Debug.Assert(((uint)ifaceId) < implementationType.RuntimeInterfaces.Length); + TypeDesc currentInterface = implementationType.RuntimeInterfaces[ifaceId]; + var typeSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(currentInterface)); + vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)typeSig.SavedVertex.VertexOffset)); + } + } + } + } + + int hashCode = callingMethod.OwningType.GetHashCode(); + gvmHashtable.Append((uint)hashCode, gvmHashtableSection.Place(vertex)); + } + + // Zero out the dictionary so that we AV if someone tries to insert after we're done. + _interfaceGvmSlots = null; + + byte[] streamBytes = nativeFormatWriter.Save(); + + _endSymbol.SetSymbolOffset(streamBytes.Length); + + return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.InterfaceGenericVirtualMethodTableNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/JumpStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/JumpStubNode.cs new file mode 100644 index 00000000000000..976f7d27e96d3f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/JumpStubNode.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ILCompiler.DependencyAnalysis +{ + public abstract partial class JumpStubNode : AssemblyStubNode + { + private ISymbolNode _target; + + public ISymbolNode Target + { + get + { + return _target; + } + } + + public JumpStubNode(ISymbolNode target) + { + _target = target; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override int ClassCode => 737788182; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MetadataNode.cs new file mode 100644 index 00000000000000..427c67f93cdbe5 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MetadataNode.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a blob of native metadata describing assemblies, the types in them, and their members. + /// The data is used at runtime to e.g. support reflection. + /// + public sealed class MetadataNode : ObjectNode, ISymbolDefinitionNode + { + ObjectAndOffsetSymbolNode _endSymbol; + + public MetadataNode() + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__embedded_metadata_End", true); + } + + public ISymbolNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__embedded_metadata"); + } + public int Offset => 0; + public override bool IsShareable => false; + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node has no relocations. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + byte[] blob = factory.MetadataManager.GetMetadataBlob(factory); + _endSymbol.SetSymbolOffset(blob.Length); + + return new ObjectData( + blob, + Array.Empty(), + 1, + new ISymbolDefinitionNode[] + { + this, + _endSymbol + }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.MetadataNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs new file mode 100644 index 00000000000000..d0f445af76419b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; + +namespace ILCompiler.DependencyAnalysis +{ + [Flags] + public enum AssociatedDataFlags : byte + { + None = 0, + HasUnboxingStubTarget = 1, + } + + /// + /// This node contains any custom data that we'd like to associated with a method. The unwind info of the method + /// will have a reloc to this custom data if it exists. Not all methods need custom data to be emitted. + /// This custom data excludes gcinfo and ehinfo (they are written by ObjectWriter during obj file emission). + /// + public class MethodAssociatedDataNode : ObjectNode, ISymbolDefinitionNode + { + private IMethodNode _methodNode; + + public MethodAssociatedDataNode(IMethodNode methodNode) + { + Debug.Assert(!methodNode.Method.IsAbstract); + Debug.Assert(methodNode.Method.GetCanonMethodTarget(CanonicalFormKind.Specific) == methodNode.Method); + _methodNode = methodNode; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + public override bool StaticDependenciesAreComputed => true; + public int Offset => 0; + public override bool IsShareable => _methodNode.Method is InstantiatedMethod || EETypeNode.IsTypeNodeShareable(_methodNode.Method.OwningType); + + public override int ClassCode => 1055183914; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_methodNode, ((MethodAssociatedDataNode)other)._methodNode); + } + + public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("_associatedData_").Append(nameMangler.GetMangledMethodName(_methodNode.Method)); + } + + public static bool MethodHasAssociatedData(NodeFactory factory, IMethodNode methodNode) + { + // Instantiating unboxing stubs. We need to store their non-unboxing target pointer (looked up by runtime) + ISpecialUnboxThunkNode unboxThunk = methodNode as ISpecialUnboxThunkNode; + if(unboxThunk != null && unboxThunk.IsSpecialUnboxingThunk) + return true; + + return false; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + Debug.Assert(MethodHasAssociatedData(factory, _methodNode)); + + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialAlignment(1); + objData.AddSymbol(this); + + AssociatedDataFlags flags = AssociatedDataFlags.None; + + var flagsReservation = objData.ReserveByte(); + + ISpecialUnboxThunkNode unboxThunkNode = _methodNode as ISpecialUnboxThunkNode; + if (unboxThunkNode != null && unboxThunkNode.IsSpecialUnboxingThunk) + { + flags |= AssociatedDataFlags.HasUnboxingStubTarget; + objData.EmitReloc(unboxThunkNode.GetUnboxingThunkTarget(factory), RelocType.IMAGE_REL_BASED_RELPTR32); + } + + objData.EmitByte(flagsReservation, (byte)flags); + + return objData.ToObjectData(); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodExceptionHandlingInfoNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodExceptionHandlingInfoNode.cs new file mode 100644 index 00000000000000..3e24857b2cd46a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodExceptionHandlingInfoNode.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class MethodExceptionHandlingInfoNode : ObjectNode, ISymbolDefinitionNode + { + private readonly MethodDesc _owningMethod; + private readonly ObjectData _data; + + public MethodExceptionHandlingInfoNode(MethodDesc owningMethod, ObjectData data) + { + _owningMethod = owningMethod; + Debug.Assert(data.DefinedSymbols == null || data.DefinedSymbols.Length == 0); + _data = new ObjectData(data.Data, data.Relocs, data.Alignment, new ISymbolDefinitionNode[] { this }); + } + + public override ObjectNodeSection Section => _owningMethod.Context.Target.IsWindows + ? ObjectNodeSection.FoldableReadOnlyDataSection + : ObjectNodeSection.DataSection; + + public override bool StaticDependenciesAreComputed => true; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__ehinfo_" + nameMangler.GetMangledMethodName(_owningMethod)); + } + public int Offset => 0; + public override bool IsShareable => true; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + return _data; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + +#if !SUPPORT_JIT + public override int ClassCode => 64872398; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_owningMethod, ((MethodExceptionHandlingInfoNode)other)._owningMethod); + } +#endif + } +} + diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs new file mode 100644 index 00000000000000..8f1269d8fdfa3e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; +using EcmaMethod = Internal.TypeSystem.Ecma.EcmaMethod; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a method that has metadata generated in the current compilation. + /// + /// + /// Only expected to be used during ILScanning when scanning for reflection. + /// + internal class MethodMetadataNode : DependencyNodeCore + { + private readonly MethodDesc _method; + + public MethodMetadataNode(MethodDesc method) + { + Debug.Assert(method.IsTypicalMethodDefinition); + _method = method; + } + + public MethodDesc Method => _method; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + dependencies.Add(factory.TypeMetadata((MetadataType)_method.OwningType), "Owning type metadata"); + + CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, ((EcmaMethod)_method)); + + MethodSignature sig = _method.Signature; + const string reason = "Method signature metadata"; + TypeMetadataNode.GetMetadataDependencies(ref dependencies, factory, sig.ReturnType, reason); + foreach (TypeDesc paramType in sig) + { + TypeMetadataNode.GetMetadataDependencies(ref dependencies, factory, paramType, reason); + } + + return dependencies; + } + protected override string GetName(NodeFactory factory) + { + return "Reflectable method: " + _method.ToString(); + } + + protected override void OnMarked(NodeFactory factory) + { + Debug.Assert(!factory.MetadataManager.IsReflectionBlocked(_method)); + Debug.Assert(factory.MetadataManager.CanGenerateMetadata(_method)); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleInitializerListNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleInitializerListNode.cs new file mode 100644 index 00000000000000..888de7a5d9ffe2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleInitializerListNode.cs @@ -0,0 +1,225 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.DependencyAnalysis +{ + internal class ModuleInitializerListNode : ObjectNode, ISymbolDefinitionNode + { + private readonly ObjectAndOffsetSymbolNode _endSymbol; + + public ModuleInitializerListNode() + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__module_initializers_End", true); + } + + public ISymbolNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__module_initializers"); + } + + public int Offset => 0; + + public override bool IsShareable => false; + + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This is a summary node that doesn't introduce dependencies. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + var modulesWithCctor = new List(); + + foreach (var methodNode in factory.MetadataManager.GetCompiledMethodBodies()) + { + MethodDesc method = methodNode.Method; + if (method.OwningType is MetadataType mdType + && mdType.IsModuleType && method.IsStaticConstructor) + { + modulesWithCctor.Add(mdType.Module); + } + } + + // We have a list of modules with a class constructor. + // Do a topological sort based on the assembly references of each module. + // This is an approximation that tries to deal with module initializers that might have + // dependencies on each other. The spec doesn't guarantee any ordering so this is best effort. + List sortedModules = new List(); + + var graphFactory = new ModuleGraphFactory(); + + // This is a list because we want to keep a stable sort order. + var allModules = new List(); + + // Seed the graph with the list of modules we're interested in + foreach (ModuleDesc module in modulesWithCctor) + allModules.Add(graphFactory.GetNode(module)); + + // Expand the graph to include all the nodes in between the interesting ones + var modulesToExpand = new Queue(allModules); + while (modulesToExpand.Count > 0) + { + ModuleGraphNode node = modulesToExpand.Dequeue(); + foreach (var reference in node.Edges) + { + if (!allModules.Contains(reference)) + { + allModules.Add(reference); + modulesToExpand.Enqueue(reference); + } + } + } + + // Now sort the nodes + // + // We start with the modules that don't reference any other modules. + // Then we add modules that reference the modules we already sorted. + // Etc. until we figured out the order of all modules with a cctor. + // + // This might appear counter intuitive (the cctor of the entrypoint module + // will likely run last), but if the entrypoint module cctor calls into another + // module that has a cctor, CoreCLR will run that cctor first. + // + // If a module doesn't call into other modules with a cctor, it doesn't matter + // when we sort it. If it does call into one, it should run before. + var markedModules = new HashSet(); + while (sortedModules.Count != modulesWithCctor.Count) + { + bool madeProgress = false; + + // Add nodes that have all their dependencies already satisfied. + foreach (var module in allModules) + { + if (!markedModules.Contains(module) && module.Satisfies(markedModules)) + { + madeProgress = true; + markedModules.Add(module); + if (modulesWithCctor.Contains(module.Module)) + sortedModules.Add(module.Module); + } + } + + // If we haven't made progress, there's a cycle. Pick the first unmarked node as victim. + if (!madeProgress) + { + foreach (var module in allModules) + { + if (!markedModules.Contains(module)) + { + markedModules.Add(module); + if (modulesWithCctor.Contains(module.Module)) + sortedModules.Add(module.Module); + break; + } + } + } + } + + // The data structure is a flat list of module constructors to call. + // This is insufficient for the purposes of ordering in the multi-object-module mode. + // If this mode ever becomes more interesting, we'll need to do the sorting at + // the time of startup. (Linker likely can't do it, unfortunately.) + + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.AddSymbol(this); + builder.AddSymbol(_endSymbol); + + foreach (var module in sortedModules) + { + builder.EmitPointerReloc(factory.MethodEntrypoint(module.GetGlobalModuleType().GetStaticConstructor())); + } + + var result = builder.ToObjectData(); + + _endSymbol.SetSymbolOffset(result.Data.Length); + + return result; + } + + public override int ClassCode => 0x4732738; + + private class ModuleGraphNode + { + private readonly ModuleGraphFactory _factory; + private readonly ModuleDesc[] _edges; + private ModuleGraphNode[] _edgeNodes; + + public ModuleDesc Module { get; } + public ModuleGraphNode[] Edges + { + get + { + if (_edgeNodes != null) + { + return _edgeNodes; + } + + var edgeNodes = new ArrayBuilder(); + foreach (var edge in _edges) + edgeNodes.Add(_factory.GetNode(edge)); + return _edgeNodes = edgeNodes.ToArray(); + } + } + public bool Satisfies(HashSet markedNodes) + { + foreach (var edge in Edges) + if (!markedNodes.Contains(edge)) + return false; + return true; + } + public ModuleGraphNode(ModuleGraphFactory factory, ModuleDesc module, ModuleDesc[] edges) + => (_factory, Module, _edges) = (factory, module, edges); + } + + private class ModuleGraphFactory + { + private readonly Dictionary _nodes = new Dictionary(); + + public ModuleGraphNode GetNode(ModuleDesc module) + { + if (_nodes.TryGetValue(module, out ModuleGraphNode result)) + return result; + + if (module is EcmaModule ecmaModule) + { + ArrayBuilder referencedAssemblies = new ArrayBuilder(); + var reader = ecmaModule.MetadataReader; + foreach (var assemblyReferenceHandle in reader.AssemblyReferences) + { + var assemblyReference = reader.GetAssemblyReference(assemblyReferenceHandle); + string assemblyName = reader.GetString(assemblyReference.Name); + + try + { + var reference = module.Context.ResolveAssembly(new System.Reflection.AssemblyName(assemblyName)); + referencedAssemblies.Add(reference); + } + catch (TypeSystemException) { } + } + result = new ModuleGraphNode(this, module, referencedAssemblies.ToArray()); + } + else + { + result = new ModuleGraphNode(this, module, Array.Empty()); + } + + _nodes.Add(module, result); + return result; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleMetadataNode.cs new file mode 100644 index 00000000000000..3f6318bae28962 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleMetadataNode.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; +using EcmaAssembly = Internal.TypeSystem.Ecma.EcmaAssembly; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a reflectable module. + /// + /// + /// Only expected to be used during ILScanning when scanning for reflection. + /// + internal class ModuleMetadataNode : DependencyNodeCore + { + private readonly ModuleDesc _module; + + public ModuleMetadataNode(ModuleDesc module) + { + Debug.Assert(module is IAssemblyDesc, "Multi-module assemblies?"); + _module = module; + } + + public ModuleDesc Module => _module; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + + // Global module type always generates metadata because it's really convenient to + // have something in an assembly that always generates metadata. + dependencies.Add(factory.TypeMetadata(_module.GetGlobalModuleType()), "Global module type"); + + CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, (EcmaAssembly)_module); + + return dependencies; + } + + protected override string GetName(NodeFactory factory) + { + return "Reflectable module: " + ((IAssemblyDesc)_module).GetName().FullName; + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModulesSectionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModulesSectionNode.cs new file mode 100644 index 00000000000000..05d83408ae4b70 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModulesSectionNode.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class ModulesSectionNode : ObjectNode, ISymbolDefinitionNode + { + // Each compilation unit produces one module. When all compilation units are linked + // together in multifile mode, the runtime needs to get list of modules present + // in the final binary. This list is created via a special .modules section that + // contains list of pointers to all module headers. + public static readonly string WindowsSectionName = ".modules$I"; + public static readonly string UnixSectionName = "__modules"; + + private TargetDetails _target; + + public ModulesSectionNode(TargetDetails target) + { + _target = target; + } + + public override ObjectNodeSection Section + { + get + { + if (_target.IsWindows) + return new ObjectNodeSection(WindowsSectionName, SectionType.ReadOnly); + else + return new ObjectNodeSection(UnixSectionName, SectionType.Writeable); + } + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override bool StaticDependenciesAreComputed => true; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__Module"); + } + public int Offset => 0; + public override bool IsShareable => false; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialPointerAlignment(); + objData.AddSymbol(this); + objData.EmitPointerReloc(factory.ReadyToRunHeader); + + return objData.ToObjectData(); + } + + public override int ClassCode => -1225116970; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs new file mode 100644 index 00000000000000..c7f4bb4ecc06a7 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.Text; +using Internal.NativeFormat; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Native layout info blob. + /// + public sealed class NativeLayoutInfoNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + private ExternalReferencesTableNode _staticsReferences; + + private NativeWriter _writer; + private byte[] _writerSavedBytes; + + private Section _signaturesSection; + private Section _ldTokenInfoSection; + private Section _templatesSection; + + private List _vertexNodesToWrite; + + public NativeLayoutInfoNode(ExternalReferencesTableNode externalReferences, ExternalReferencesTableNode staticsReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__nativelayoutinfo_End", true); + _externalReferences = externalReferences; + _staticsReferences = staticsReferences; + + _writer = new NativeWriter(); + _signaturesSection = _writer.NewSection(); + _ldTokenInfoSection = _writer.NewSection(); + _templatesSection = _writer.NewSection(); + + _vertexNodesToWrite = new List(); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__nativelayoutinfo"); + } + public ISymbolNode EndSymbol => _endSymbol; + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public Section LdTokenInfoSection => _ldTokenInfoSection; + public Section SignaturesSection => _signaturesSection; + public Section TemplatesSection => _templatesSection; + public ExternalReferencesTableNode ExternalReferences => _externalReferences; + public ExternalReferencesTableNode StaticsReferences => _staticsReferences; + public NativeWriter Writer => _writer; + + public void AddVertexNodeToNativeLayout(NativeLayoutVertexNode vertexNode) + { + _vertexNodesToWrite.Add(vertexNode); + } + + public void SaveNativeLayoutInfoWriter(NodeFactory factory) + { + if (_writerSavedBytes != null) + return; + + foreach (var vertexNode in _vertexNodesToWrite) + vertexNode.WriteVertex(factory); + + _writerSavedBytes = _writer.Save(); + + // Zero out the native writer and vertex list so that we AV if someone tries to insert after we're done. + _writer = null; + _vertexNodesToWrite = null; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // Dependencies of the NativeLayoutInfo node are tracked by the callers that emit data into the native layout writer + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + SaveNativeLayoutInfoWriter(factory); + + _endSymbol.SetSymbolOffset(_writerSavedBytes.Length); + + return new ObjectData(_writerSavedBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.NativeLayoutInfoNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs new file mode 100644 index 00000000000000..1556406c6f9866 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a native layout signature. A signature is a pair where the first item is a pointer + /// to the TypeManager that contains the native layout info blob of interest, and the second item + /// is an offset into that native layout info blob + /// + public class NativeLayoutSignatureNode : ObjectNode, ISymbolDefinitionNode + { + private TypeSystemEntity _identity; + private Utf8String _identityPrefix; + private NativeLayoutSavedVertexNode _nativeSignature; + + public TypeSystemEntity Identity => _identity; + + public NativeLayoutSignatureNode(NativeLayoutSavedVertexNode nativeSignature, TypeSystemEntity identity, Utf8String identityPrefix) + { + _nativeSignature = nativeSignature; + _identity = identity; + _identityPrefix = identityPrefix; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + Utf8String identityString; + if (_identity is MethodDesc) + { + identityString = nameMangler.GetMangledMethodName((MethodDesc)_identity); + } + else if (_identity is TypeDesc) + { + identityString = nameMangler.GetMangledTypeName((TypeDesc)_identity); + } + else if (_identity is FieldDesc) + { + identityString = nameMangler.GetMangledFieldName((FieldDesc)_identity); + } + else + { + Debug.Assert(false); + identityString = new Utf8String("unknown"); + } + + sb.Append(nameMangler.CompilationUnitPrefix).Append(_identityPrefix).Append(identityString); + } + + public int Offset => 0; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + public override ObjectNodeSection Section + { + get + { + if (_identity.Context.Target.IsWindows) + return ObjectNodeSection.ReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + public override bool IsShareable => false; + public override bool StaticDependenciesAreComputed => true; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + dependencies.Add(new DependencyListEntry(_nativeSignature, "NativeLayoutSignatureNode target vertex")); + return dependencies; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + // Ensure native layout is saved to get valid Vertex offsets + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + + objData.RequireInitialPointerAlignment(); + objData.AddSymbol(this); + + objData.EmitPointerReloc(factory.TypeManagerIndirection); + objData.EmitNaturalInt(_nativeSignature.SavedVertex.VertexOffset); + + return objData.ToObjectData(); + } + + public override int ClassCode => 1887049331; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + NativeLayoutSignatureNode otherSignature = (NativeLayoutSignatureNode)other; + if (_identity is MethodDesc) + { + if (otherSignature._identity is TypeDesc || otherSignature._identity is FieldDesc) + return -1; + return comparer.Compare((MethodDesc)_identity, (MethodDesc)((NativeLayoutSignatureNode)other)._identity); + } + else if (_identity is TypeDesc) + { + if (otherSignature._identity is MethodDesc) + return 1; + + if (otherSignature._identity is FieldDesc) + return -1; + + return comparer.Compare((TypeDesc)_identity, (TypeDesc)((NativeLayoutSignatureNode)other)._identity); + } + else if (_identity is FieldDesc) + { + if (otherSignature._identity is MethodDesc || otherSignature._identity is TypeDesc) + return 1; + return comparer.Compare((FieldDesc)_identity, (FieldDesc)((NativeLayoutSignatureNode)other)._identity); + } + else + { + throw new NotSupportedException("New type system entity needs a comparison"); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs new file mode 100644 index 00000000000000..b60559f8978d06 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -0,0 +1,2202 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.NativeFormat; +using Internal.Runtime; +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Wrapper nodes for native layout vertex structures. These wrapper nodes are "abstract" as they do not + /// generate any data. They are used to keep track of the dependency nodes required by a Vertex structure. + /// + /// Any node in the graph that references data in the native layout blob needs to create one of these + /// NativeLayoutVertexNode nodes, and track it as a dependency of itself. + /// Example: MethodCodeNodes that are saved to the table in the ExactMethodInstantiationsNode reference + /// signatures stored in the native layout blob, so a NativeLayoutPlacedSignatureVertexNode node is created + /// and returned as a static dependency of the associated MethodCodeNode (in the GetStaticDependencies API). + /// + /// Each NativeLayoutVertexNode that gets marked in the graph will register itself with the NativeLayoutInfoNode, + /// so that the NativeLayoutInfoNode can write it later to the native layout blob during the call to its GetData API. + /// + public abstract class NativeLayoutVertexNode : DependencyNodeCore + { + public override bool HasConditionalStaticDependencies => false; + public override bool HasDynamicDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + + + [Conditional("DEBUG")] + public virtual void CheckIfMarkedEnoughToWrite() + { + Debug.Assert(Marked); + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) + { + return Array.Empty(); + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) + { + return Array.Empty(); + } + + protected override void OnMarked(NodeFactory context) + { + context.MetadataManager.NativeLayoutInfo.AddVertexNodeToNativeLayout(this); + } + + public abstract Vertex WriteVertex(NodeFactory factory); + + protected NativeWriter GetNativeWriter(NodeFactory factory) + { + // There is only one native layout info blob, so only one writer for now... + return factory.MetadataManager.NativeLayoutInfo.Writer; + } + } + + /// + /// Any NativeLayoutVertexNode that needs to expose the native layout Vertex after it has been saved + /// needs to derive from this NativeLayoutSavedVertexNode class. + /// + /// A nativelayout Vertex should typically only be exposed for Vertex offset fetching purposes, after the native + /// writer is saved (Vertex offsets get generated when the native writer gets saved). + /// + /// It is important for whoever derives from this class to produce unified Vertices. Calling the WriteVertex method + /// multiple times should always produce the same exact unified Vertex each time (hence the assert in SetSavedVertex). + /// All nativewriter.Getxyz methods return unified Vertices. + /// + /// When exposing a saved Vertex that is a result of a section placement operation (Section.Place(...)), always make + /// sure a unified Vertex is being placed in the section (Section.Place creates a PlacedVertex structure that wraps the + /// Vertex to be placed, so if the Vertex to be placed is unified, there will only be a single unified PlacedVertex + /// structure created for that placed Vertex). + /// + public abstract class NativeLayoutSavedVertexNode : NativeLayoutVertexNode + { + public Vertex SavedVertex { get; private set; } + protected Vertex SetSavedVertex(Vertex value) + { + Debug.Assert(SavedVertex == null || Object.ReferenceEquals(SavedVertex, value)); + SavedVertex = value; + return value; + } + } + + internal abstract class NativeLayoutMethodEntryVertexNode : NativeLayoutSavedVertexNode + { + [Flags] + public enum MethodEntryFlags + { + CreateInstantiatedSignature = 1, + SaveEntryPoint = 2, + } + + protected readonly MethodDesc _method; + private MethodEntryFlags _flags; + private NativeLayoutTypeSignatureVertexNode _containingTypeSig; + private NativeLayoutMethodSignatureVertexNode _methodSig; + private NativeLayoutTypeSignatureVertexNode[] _instantiationArgsSig; + + public MethodDesc Method => _method; + + public NativeLayoutMethodEntryVertexNode(NodeFactory factory, MethodDesc method, MethodEntryFlags flags) + { + _method = method; + _flags = flags; + _methodSig = factory.NativeLayout.MethodSignatureVertex(method.GetTypicalMethodDefinition().Signature); + + if ((_flags & MethodEntryFlags.CreateInstantiatedSignature) == 0) + { + _containingTypeSig = factory.NativeLayout.TypeSignatureVertex(method.OwningType); + if (method.HasInstantiation && !method.IsMethodDefinition) + { + _instantiationArgsSig = new NativeLayoutTypeSignatureVertexNode[method.Instantiation.Length]; + for (int i = 0; i < _instantiationArgsSig.Length; i++) + _instantiationArgsSig[i] = factory.NativeLayout.TypeSignatureVertex(method.Instantiation[i]); + } + } + } + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + DependencyList dependencies = new DependencyList(); + + dependencies.Add(new DependencyListEntry(_methodSig, "NativeLayoutMethodEntryVertexNode method signature")); + if ((_flags & MethodEntryFlags.CreateInstantiatedSignature) != 0) + { + dependencies.Add(new DependencyListEntry(context.NecessaryTypeSymbol(_method.OwningType), "NativeLayoutMethodEntryVertexNode containing type")); + foreach (var arg in _method.Instantiation) + dependencies.Add(new DependencyListEntry(context.NecessaryTypeSymbol(arg), "NativeLayoutMethodEntryVertexNode instantiation argument type")); + } + else + { + dependencies.Add(new DependencyListEntry(_containingTypeSig, "NativeLayoutMethodEntryVertexNode containing type signature")); + if (_method.HasInstantiation && !_method.IsMethodDefinition) + { + foreach (var arg in _instantiationArgsSig) + dependencies.Add(new DependencyListEntry(arg, "NativeLayoutMethodEntryVertexNode instantiation argument signature")); + } + } + + if ((_flags & MethodEntryFlags.SaveEntryPoint) != 0) + { + bool unboxingStub; + IMethodNode methodEntryPointNode = GetMethodEntrypointNode(context, out unboxingStub); + dependencies.Add(new DependencyListEntry(methodEntryPointNode, "NativeLayoutMethodEntryVertexNode entrypoint")); + } + + return dependencies; + } + + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + Vertex containingType = GetContainingTypeVertex(factory); + Vertex methodSig = _methodSig.WriteVertex(factory); + Vertex methodNameAndSig = GetNativeWriter(factory).GetMethodNameAndSigSignature(_method.Name, methodSig); + + Vertex[] args = null; + MethodFlags flags = 0; + if (_method.HasInstantiation && !_method.IsMethodDefinition) + { + Debug.Assert(_instantiationArgsSig == null || (_instantiationArgsSig != null && _method.Instantiation.Length == _instantiationArgsSig.Length)); + + flags |= MethodFlags.HasInstantiation; + args = new Vertex[_method.Instantiation.Length]; + + for (int i = 0; i < args.Length; i++) + { + if ((_flags & MethodEntryFlags.CreateInstantiatedSignature) != 0) + { + IEETypeNode eetypeNode = factory.NecessaryTypeSymbol(_method.Instantiation[i]); + uint typeIndex = factory.MetadataManager.NativeLayoutInfo.ExternalReferences.GetIndex(eetypeNode); + args[i] = GetNativeWriter(factory).GetExternalTypeSignature(typeIndex); + } + else + { + args[i] = _instantiationArgsSig[i].WriteVertex(factory); + } + } + } + + uint fptrReferenceId = 0; + if ((_flags & MethodEntryFlags.SaveEntryPoint) != 0) + { + flags |= MethodFlags.HasFunctionPointer; + + bool unboxingStub; + IMethodNode methodEntryPointNode = GetMethodEntrypointNode(factory, out unboxingStub); + fptrReferenceId = factory.MetadataManager.NativeLayoutInfo.ExternalReferences.GetIndex(methodEntryPointNode); + + if (unboxingStub) + flags |= MethodFlags.IsUnboxingStub; + if (methodEntryPointNode.Method.IsCanonicalMethod(CanonicalFormKind.Universal)) + flags |= MethodFlags.FunctionPointerIsUSG; + } + + return GetNativeWriter(factory).GetMethodSignature((uint)flags, fptrReferenceId, containingType, methodNameAndSig, args); + } + + private Vertex GetContainingTypeVertex(NodeFactory factory) + { + if ((_flags & MethodEntryFlags.CreateInstantiatedSignature) != 0) + { + IEETypeNode eetypeNode = factory.NecessaryTypeSymbol(_method.OwningType); + uint typeIndex = factory.MetadataManager.NativeLayoutInfo.ExternalReferences.GetIndex(eetypeNode); + return GetNativeWriter(factory).GetExternalTypeSignature(typeIndex); + } + else + { + return _containingTypeSig.WriteVertex(factory); + } + } + + protected virtual IMethodNode GetMethodEntrypointNode(NodeFactory factory, out bool unboxingStub) + { + unboxingStub = _method.OwningType.IsValueType && !_method.Signature.IsStatic; + IMethodNode methodEntryPointNode = factory.MethodEntrypoint(_method, unboxingStub); + return methodEntryPointNode; + } + } + + internal sealed class NativeLayoutMethodLdTokenVertexNode : NativeLayoutMethodEntryVertexNode + { + protected override string GetName(NodeFactory factory) => "NativeLayoutMethodLdTokenVertexNode_" + factory.NameMangler.GetMangledMethodName(_method); + + public NativeLayoutMethodLdTokenVertexNode(NodeFactory factory, MethodDesc method) + : base(factory, method, 0) + { + } + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + if (_method.IsVirtual && _method.HasInstantiation) + { + return GetGenericVirtualMethodDependencies(context); + } + else + { + return base.GetStaticDependencies(context); + } + } + + private IEnumerable GetGenericVirtualMethodDependencies(NodeFactory factory) + { + var dependencies = (DependencyList)base.GetStaticDependencies(factory); + + dependencies.Add(factory.GVMDependencies(_method.GetCanonMethodTarget(CanonicalFormKind.Specific)), "Potential generic virtual method call"); + + // Variant generic virtual method calls at runtime might need to build the concrete version of the + // type we could be dispatching on to find the appropriate GVM entry. + if (_method.OwningType.HasVariance) + { + GenericTypesTemplateMap.GetTemplateTypeDependencies(ref dependencies, factory, _method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific)); + } + + return dependencies; + } + + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + Vertex methodEntryVertex = base.WriteVertex(factory); + return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.LdTokenInfoSection.Place(methodEntryVertex)); + } + } + + internal sealed class NativeLayoutFieldLdTokenVertexNode : NativeLayoutSavedVertexNode + { + private readonly FieldDesc _field; + private readonly NativeLayoutTypeSignatureVertexNode _containingTypeSig; + + public NativeLayoutFieldLdTokenVertexNode(NodeFactory factory, FieldDesc field) + { + _field = field; + _containingTypeSig = factory.NativeLayout.TypeSignatureVertex(field.OwningType); + } + + protected override string GetName(NodeFactory factory) => "NativeLayoutFieldLdTokenVertexNode_" + factory.NameMangler.GetMangledFieldName(_field); + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return new DependencyListEntry[] + { + new DependencyListEntry(_containingTypeSig, "NativeLayoutFieldLdTokenVertexNode containing type signature"), + }; + } + + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + Vertex containingType = _containingTypeSig.WriteVertex(factory); + + Vertex unplacedVertex = GetNativeWriter(factory).GetFieldSignature(containingType, _field.Name); + + return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.LdTokenInfoSection.Place(unplacedVertex)); + } + } + + internal sealed class NativeLayoutMethodSignatureVertexNode : NativeLayoutVertexNode + { + private Internal.TypeSystem.MethodSignature _signature; + private NativeLayoutTypeSignatureVertexNode _returnTypeSig; + private NativeLayoutTypeSignatureVertexNode[] _parametersSig; + + protected override string GetName(NodeFactory factory) => "NativeLayoutMethodSignatureVertexNode " + _signature.GetName(); + + public NativeLayoutMethodSignatureVertexNode(NodeFactory factory, Internal.TypeSystem.MethodSignature signature) + { + _signature = signature; + _returnTypeSig = factory.NativeLayout.TypeSignatureVertex(signature.ReturnType); + _parametersSig = new NativeLayoutTypeSignatureVertexNode[signature.Length]; + for (int i = 0; i < _parametersSig.Length; i++) + _parametersSig[i] = factory.NativeLayout.TypeSignatureVertex(signature[i]); + } + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + DependencyList dependencies = new DependencyList(); + + dependencies.Add(new DependencyListEntry(_returnTypeSig, "NativeLayoutMethodSignatureVertexNode return type signature")); + foreach (var arg in _parametersSig) + dependencies.Add(new DependencyListEntry(arg, "NativeLayoutMethodSignatureVertexNode parameter signature")); + + return dependencies; + } + + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + MethodCallingConvention methodCallingConvention = default(MethodCallingConvention); + + if (_signature.GenericParameterCount > 0) + methodCallingConvention |= MethodCallingConvention.Generic; + if (_signature.IsStatic) + methodCallingConvention |= MethodCallingConvention.Static; + + Debug.Assert(_signature.Length == _parametersSig.Length); + + Vertex returnType = _returnTypeSig.WriteVertex(factory); + Vertex[] parameters = new Vertex[_parametersSig.Length]; + for (int i = 0; i < _parametersSig.Length; i++) + parameters[i] = _parametersSig[i].WriteVertex(factory); + + Vertex signature = GetNativeWriter(factory).GetMethodSigSignature((uint)methodCallingConvention, (uint)_signature.GenericParameterCount, returnType, parameters); + return factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(signature); + } + } + + internal sealed class NativeLayoutMethodNameAndSignatureVertexNode : NativeLayoutVertexNode + { + private MethodDesc _method; + private NativeLayoutMethodSignatureVertexNode _methodSig; + + protected override string GetName(NodeFactory factory) => "NativeLayoutMethodNameAndSignatureVertexNode" + factory.NameMangler.GetMangledMethodName(_method); + + public NativeLayoutMethodNameAndSignatureVertexNode(NodeFactory factory, MethodDesc method) + { + _method = method; + _methodSig = factory.NativeLayout.MethodSignatureVertex(method.Signature); + } + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return new DependencyListEntry[] { new DependencyListEntry(_methodSig, "NativeLayoutMethodNameAndSignatureVertexNode signature vertex") }; + } + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + Vertex methodSig = _methodSig.WriteVertex(factory); + return GetNativeWriter(factory).GetMethodNameAndSigSignature(_method.Name, methodSig); + } + } + + internal abstract class NativeLayoutTypeSignatureVertexNode : NativeLayoutVertexNode + { + protected readonly TypeDesc _type; + + protected NativeLayoutTypeSignatureVertexNode(TypeDesc type) + { + _type = type; + } + + protected override string GetName(NodeFactory factory) => "NativeLayoutTypeSignatureVertexNode: " + _type.ToString(); + + public static NativeLayoutTypeSignatureVertexNode NewTypeSignatureVertexNode(NodeFactory factory, TypeDesc type) + { + switch (type.Category) + { + case Internal.TypeSystem.TypeFlags.Array: + case Internal.TypeSystem.TypeFlags.SzArray: + case Internal.TypeSystem.TypeFlags.Pointer: + case Internal.TypeSystem.TypeFlags.ByRef: + return new NativeLayoutParameterizedTypeSignatureVertexNode(factory, type); + + case Internal.TypeSystem.TypeFlags.SignatureTypeVariable: + case Internal.TypeSystem.TypeFlags.SignatureMethodVariable: + return new NativeLayoutGenericVarSignatureVertexNode(factory, type); + + // TODO Internal.TypeSystem.TypeFlags.FunctionPointer (Runtime parsing also not yet implemented) + case Internal.TypeSystem.TypeFlags.FunctionPointer: + throw new NotImplementedException("FunctionPointer signature"); + + default: + { + Debug.Assert(type.IsDefType); + + if (type.HasInstantiation && !type.IsGenericDefinition) + return new NativeLayoutInstantiatedTypeSignatureVertexNode(factory, type); + else + return new NativeLayoutEETypeSignatureVertexNode(factory, type); + } + } + } + + sealed class NativeLayoutParameterizedTypeSignatureVertexNode : NativeLayoutTypeSignatureVertexNode + { + private NativeLayoutVertexNode _parameterTypeSig; + + public NativeLayoutParameterizedTypeSignatureVertexNode(NodeFactory factory, TypeDesc type) : base(type) + { + _parameterTypeSig = factory.NativeLayout.TypeSignatureVertex(((ParameterizedType)type).ParameterType); + } + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return new DependencyListEntry[] { new DependencyListEntry(_parameterTypeSig, "NativeLayoutParameterizedTypeSignatureVertexNode parameter type signature") }; + } + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + switch (_type.Category) + { + case Internal.TypeSystem.TypeFlags.SzArray: + return GetNativeWriter(factory).GetModifierTypeSignature(TypeModifierKind.Array, _parameterTypeSig.WriteVertex(factory)); + + case Internal.TypeSystem.TypeFlags.Pointer: + return GetNativeWriter(factory).GetModifierTypeSignature(TypeModifierKind.Pointer, _parameterTypeSig.WriteVertex(factory)); + + case Internal.TypeSystem.TypeFlags.ByRef: + return GetNativeWriter(factory).GetModifierTypeSignature(TypeModifierKind.ByRef, _parameterTypeSig.WriteVertex(factory)); + + case Internal.TypeSystem.TypeFlags.Array: + { + Vertex elementType = _parameterTypeSig.WriteVertex(factory); + + // Skip bounds and lobounds (TODO) + var bounds = Array.Empty(); + var lobounds = Array.Empty(); + + return GetNativeWriter(factory).GetMDArrayTypeSignature(elementType, (uint)((ArrayType)_type).Rank, bounds, lobounds); + } + } + + Debug.Fail("UNREACHABLE"); + return null; + } + } + + sealed class NativeLayoutGenericVarSignatureVertexNode : NativeLayoutTypeSignatureVertexNode + { + public NativeLayoutGenericVarSignatureVertexNode(NodeFactory factory, TypeDesc type) : base(type) + { + } + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return Array.Empty(); + } + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + switch (_type.Category) + { + case Internal.TypeSystem.TypeFlags.SignatureTypeVariable: + return GetNativeWriter(factory).GetVariableTypeSignature((uint)((SignatureVariable)_type).Index, false); + + case Internal.TypeSystem.TypeFlags.SignatureMethodVariable: + return GetNativeWriter(factory).GetVariableTypeSignature((uint)((SignatureMethodVariable)_type).Index, true); + } + + Debug.Fail("UNREACHABLE"); + return null; + } + } + + sealed class NativeLayoutInstantiatedTypeSignatureVertexNode : NativeLayoutTypeSignatureVertexNode + { + private NativeLayoutTypeSignatureVertexNode _genericTypeDefSig; + private NativeLayoutTypeSignatureVertexNode[] _instantiationArgs; + + public NativeLayoutInstantiatedTypeSignatureVertexNode(NodeFactory factory, TypeDesc type) : base(type) + { + Debug.Assert(type.HasInstantiation && !type.IsGenericDefinition); + + _genericTypeDefSig = factory.NativeLayout.TypeSignatureVertex(type.GetTypeDefinition()); + _instantiationArgs = new NativeLayoutTypeSignatureVertexNode[type.Instantiation.Length]; + for (int i = 0; i < _instantiationArgs.Length; i++) + _instantiationArgs[i] = factory.NativeLayout.TypeSignatureVertex(type.Instantiation[i]); + + } + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + DependencyList dependencies = new DependencyList(); + + dependencies.Add(new DependencyListEntry(_genericTypeDefSig, "NativeLayoutInstantiatedTypeSignatureVertexNode generic definition signature")); + foreach (var arg in _instantiationArgs) + dependencies.Add(new DependencyListEntry(arg, "NativeLayoutInstantiatedTypeSignatureVertexNode instantiation argument signature")); + + return dependencies; + } + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + Vertex genericDefVertex = _genericTypeDefSig.WriteVertex(factory); + Vertex[] args = new Vertex[_instantiationArgs.Length]; + for (int i = 0; i < args.Length; i++) + args[i] = _instantiationArgs[i].WriteVertex(factory); + + return GetNativeWriter(factory).GetInstantiationTypeSignature(genericDefVertex, args); + } + } + + sealed class NativeLayoutEETypeSignatureVertexNode : NativeLayoutTypeSignatureVertexNode + { + public NativeLayoutEETypeSignatureVertexNode(NodeFactory factory, TypeDesc type) : base(type) + { + Debug.Assert(!type.IsRuntimeDeterminedSubtype); + Debug.Assert(!type.HasInstantiation || type.IsGenericDefinition); + } + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return new DependencyListEntry[] + { + // TODO-SIZE: this might be overly generous because we don't track what this type is used for. + // A necessary EEType might be enough for some cases. + // But we definitely need constructed if this is e.g. layout for a typehandle. + // Measurements show this doesn't amount to much (0.004% - 0.3% size cost vs Necessary). + new DependencyListEntry(context.MaximallyConstructableType(_type), "NativeLayoutEETypeVertexNode containing type signature") + }; + } + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + IEETypeNode eetypeNode = factory.NecessaryTypeSymbol(_type); + uint typeIndex = factory.MetadataManager.NativeLayoutInfo.ExternalReferences.GetIndex(eetypeNode); + return GetNativeWriter(factory).GetExternalTypeSignature(typeIndex); + } + } + } + + public sealed class NativeLayoutExternalReferenceVertexNode : NativeLayoutVertexNode + { + private ISymbolNode _symbol; + + public NativeLayoutExternalReferenceVertexNode(NodeFactory factory, ISymbolNode symbol) + { + _symbol = symbol; + } + + protected override string GetName(NodeFactory factory) => "NativeLayoutISymbolNodeReferenceVertexNode " + _symbol.GetMangledName(factory.NameMangler); + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return new DependencyListEntry[] + { + new DependencyListEntry(_symbol, "NativeLayoutISymbolNodeReferenceVertexNode containing symbol") + }; + } + + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + uint symbolIndex = factory.MetadataManager.NativeLayoutInfo.ExternalReferences.GetIndex(_symbol); + return GetNativeWriter(factory).GetUnsignedConstant(symbolIndex); + } + } + + internal sealed class NativeLayoutPlacedSignatureVertexNode : NativeLayoutSavedVertexNode + { + private NativeLayoutVertexNode _signatureToBePlaced; + + protected override string GetName(NodeFactory factory) => "NativeLayoutPlacedSignatureVertexNode"; + + public NativeLayoutPlacedSignatureVertexNode(NativeLayoutVertexNode signatureToBePlaced) + { + _signatureToBePlaced = signatureToBePlaced; + } + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return new DependencyListEntry[] { new DependencyListEntry(_signatureToBePlaced, "NativeLayoutPlacedSignatureVertexNode placed signature") }; + } + public override Vertex WriteVertex(NodeFactory factory) + { + // This vertex doesn't need to assert as marked, as it simply represents the concept of an existing vertex which has been placed. + + // Always use the NativeLayoutInfo blob for names and sigs, even if the associated types/methods are written elsewhere. + // This saves space, since we can Unify more signatures, allows optimizations in comparing sigs in the same module, and + // prevents the dynamic type loader having to know about other native layout sections (since sigs contain types). If we are + // using a non-native layout info writer, write the sig to the native layout info, and refer to it by offset in its own + // section. At runtime, we will assume all names and sigs are in the native layout and find it. + + Vertex signature = _signatureToBePlaced.WriteVertex(factory); + return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(signature)); + } + } + + internal sealed class NativeLayoutPlacedVertexSequenceOfUIntVertexNode : NativeLayoutSavedVertexNode + { + private List _uints; + + protected override string GetName(NodeFactory factory) => "NativeLayoutPlacedVertexSequenceVertexNode"; + public NativeLayoutPlacedVertexSequenceOfUIntVertexNode(List uints) + { + _uints = uints; + } + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + // There are no interesting dependencies + return null; + } + + public override Vertex WriteVertex(NodeFactory factory) + { + // Eagerly return the SavedVertex so that we can unify the VertexSequence + if (SavedVertex != null) + return SavedVertex; + + // This vertex doesn't need to assert as marked, as it simply represents the concept of an existing vertex which has been placed. + + NativeWriter writer = GetNativeWriter(factory); + + VertexSequence sequence = new VertexSequence(); + foreach (uint value in _uints) + { + sequence.Append(writer.GetUnsignedConstant(value)); + } + + return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(sequence)); + } + } + + internal sealed class NativeLayoutPlacedVertexSequenceVertexNode : NativeLayoutSavedVertexNode + { + private List _vertices; + + protected override string GetName(NodeFactory factory) => "NativeLayoutPlacedVertexSequenceVertexNode"; + public NativeLayoutPlacedVertexSequenceVertexNode(List vertices) + { + _vertices = vertices; + } + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + DependencyListEntry[] dependencies = new DependencyListEntry[_vertices.Count]; + for (int i = 0; i < _vertices.Count; i++) + { + dependencies[i] = new DependencyListEntry(_vertices[i], "NativeLayoutPlacedVertexSequenceVertexNode element"); + } + + return dependencies; + } + + public override Vertex WriteVertex(NodeFactory factory) + { + // Eagerly return the SavedVertex so that we can unify the VertexSequence + if (SavedVertex != null) + return SavedVertex; + + // This vertex doesn't need to assert as marked, as it simply represents the concept of an existing vertex which has been placed. + + VertexSequence sequence = new VertexSequence(); + foreach (NativeLayoutVertexNode vertex in _vertices) + { + sequence.Append(vertex.WriteVertex(factory)); + } + + return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(sequence)); + } + } + + internal sealed class NativeLayoutTemplateMethodSignatureVertexNode : NativeLayoutMethodEntryVertexNode + { + protected override string GetName(NodeFactory factory) => "NativeLayoutTemplateMethodSignatureVertexNode_" + factory.NameMangler.GetMangledMethodName(_method); + + public NativeLayoutTemplateMethodSignatureVertexNode(NodeFactory factory, MethodDesc method) + : base(factory, method, MethodEntryFlags.CreateInstantiatedSignature | (method.IsVirtual ? MethodEntryFlags.SaveEntryPoint : 0)) + { + } + + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + Vertex methodEntryVertex = base.WriteVertex(factory); + return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.TemplatesSection.Place(methodEntryVertex)); + } + + protected override IMethodNode GetMethodEntrypointNode(NodeFactory factory, out bool unboxingStub) + { + // Only GVM templates need entry points. + Debug.Assert(_method.IsVirtual); + unboxingStub = _method.OwningType.IsValueType; + IMethodNode methodEntryPointNode = factory.MethodEntrypoint(_method, unboxingStub); + // Note: We don't set the IsUnboxingStub flag on template methods (all template lookups performed at runtime are performed with this flag not set, + // since it can't always be conveniently computed for a concrete method before looking up its template) + unboxingStub = false; + return methodEntryPointNode; + } + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + DependencyList dependencies = (DependencyList)base.GetStaticDependencies(context); + + foreach (var arg in _method.Instantiation) + { + foreach (var dependency in context.NativeLayout.TemplateConstructableTypes(arg)) + { + dependencies.Add(new DependencyListEntry(dependency, "Dependencies to make a generic method template viable Method Instantiation")); + } + } + + + foreach (var dependency in context.NativeLayout.TemplateConstructableTypes(_method.OwningType)) + { + dependencies.Add(new DependencyListEntry(dependency, "Dependencies to make a generic method template viable OwningType")); + } + + return dependencies; + } + } + + public sealed class NativeLayoutDictionarySignatureNode : NativeLayoutSavedVertexNode + { + private TypeSystemEntity _owningMethodOrType; + public NativeLayoutDictionarySignatureNode(NodeFactory nodeFactory, TypeSystemEntity owningMethodOrType) + { + if (owningMethodOrType is MethodDesc) + { + MethodDesc owningMethod = (MethodDesc)owningMethodOrType; + Debug.Assert(owningMethod.IsCanonicalMethod(CanonicalFormKind.Universal) || nodeFactory.LazyGenericsPolicy.UsesLazyGenerics(owningMethod)); + Debug.Assert(owningMethod.IsCanonicalMethod(CanonicalFormKind.Any)); + Debug.Assert(owningMethod.HasInstantiation); + } + else + { + TypeDesc owningType = (TypeDesc)owningMethodOrType; + Debug.Assert(owningType.IsCanonicalSubtype(CanonicalFormKind.Universal) || nodeFactory.LazyGenericsPolicy.UsesLazyGenerics(owningType)); + Debug.Assert(owningType.IsCanonicalSubtype(CanonicalFormKind.Any)); + } + + _owningMethodOrType = owningMethodOrType; + } + + private GenericContextKind ContextKind(NodeFactory factory) + { + if (_owningMethodOrType is MethodDesc) + { + MethodDesc owningMethod = (MethodDesc)_owningMethodOrType; + Debug.Assert(owningMethod.HasInstantiation); + return GenericContextKind.FromMethodHiddenArg | GenericContextKind.NeedsUSGContext; + } + else + { + TypeDesc owningType = (TypeDesc)_owningMethodOrType; + if (owningType.IsSzArray || owningType.HasSameTypeDefinition(factory.ArrayOfTClass) || owningType.IsValueType || owningType.IsSealed()) + { + return GenericContextKind.FromHiddenArg | GenericContextKind.NeedsUSGContext; + } + else + { + return GenericContextKind.FromHiddenArg | GenericContextKind.NeedsUSGContext | GenericContextKind.HasDeclaringType; + } + } + } + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + if ((ContextKind(context) & GenericContextKind.HasDeclaringType) != 0) + { + return new DependencyListEntry[] + { + new DependencyListEntry(context.NativeLayout.TypeSignatureVertex((TypeDesc)_owningMethodOrType), "DeclaringType signature"), + new DependencyListEntry(context.GenericDictionaryLayout(_owningMethodOrType), "Dictionary Layout") + }; + } + else + { + return new DependencyListEntry[] + { + new DependencyListEntry(context.GenericDictionaryLayout(_owningMethodOrType), "Dictionary Layout") + }; + } + } + + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + VertexSequence sequence = new VertexSequence(); + + DictionaryLayoutNode associatedLayout = factory.GenericDictionaryLayout(_owningMethodOrType); + Debug.Assert(associatedLayout.Marked); + ICollection templateLayout = associatedLayout.GetTemplateEntries(factory); + + foreach (NativeLayoutVertexNode dictionaryEntry in templateLayout) + { + dictionaryEntry.CheckIfMarkedEnoughToWrite(); + sequence.Append(dictionaryEntry.WriteVertex(factory)); + } + + Vertex signature; + + GenericContextKind contextKind = ContextKind(factory); + NativeWriter nativeWriter = GetNativeWriter(factory); + + if ((contextKind & GenericContextKind.HasDeclaringType) != 0) + { + signature = nativeWriter.GetTuple(factory.NativeLayout.TypeSignatureVertex((TypeDesc)_owningMethodOrType).WriteVertex(factory), sequence); + } + else + { + signature = sequence; + } + + Vertex signatureWithContextKind = nativeWriter.GetTuple(nativeWriter.GetUnsignedConstant((uint)contextKind), signature); + return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(signatureWithContextKind)); + } + + protected override string GetName(NodeFactory factory) => $"Dictionary layout signature for {_owningMethodOrType.ToString()}"; + } + + public sealed class NativeLayoutTemplateMethodLayoutVertexNode : NativeLayoutSavedVertexNode + { + private MethodDesc _method; + + protected override string GetName(NodeFactory factory) => "NativeLayoutTemplateMethodLayoutVertexNode" + factory.NameMangler.GetMangledMethodName(_method); + + public NativeLayoutTemplateMethodLayoutVertexNode(NodeFactory factory, MethodDesc method) + { + _method = method; + Debug.Assert(method.HasInstantiation); + Debug.Assert(method.IsCanonicalMethod(CanonicalFormKind.Any)); + Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method, "Assert that the canonical method passed in is in standard canonical form"); + } + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + foreach (var dependency in context.NativeLayout.TemplateConstructableTypes(_method.OwningType)) + { + yield return new DependencyListEntry(dependency, "method OwningType itself must be template loadable"); + } + + foreach (var type in _method.Instantiation) + { + foreach (var dependency in context.NativeLayout.TemplateConstructableTypes(type)) + { + yield return new DependencyListEntry(dependency, "method's instantiation arguments must be template loadable"); + } + } + + yield return new DependencyListEntry(context.GenericDictionaryLayout(_method), "Dictionary layout"); + } + + private int CompareDictionaryEntries(KeyValuePair left, KeyValuePair right) + { + return left.Key - right.Key; + } + + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + VertexBag layoutInfo = new VertexBag(); + + DictionaryLayoutNode associatedLayout = factory.GenericDictionaryLayout(_method); + ICollection templateLayout = associatedLayout.GetTemplateEntries(factory); + + if (!(_method.IsCanonicalMethod(CanonicalFormKind.Universal) || (factory.LazyGenericsPolicy.UsesLazyGenerics(_method))) && (templateLayout.Count > 0)) + { + List dictionaryVertices = new List(); + + foreach (NativeLayoutVertexNode dictionaryEntry in templateLayout) + { + dictionaryEntry.CheckIfMarkedEnoughToWrite(); + dictionaryVertices.Add(dictionaryEntry); + } + NativeLayoutVertexNode dictionaryLayout = factory.NativeLayout.PlacedVertexSequence(dictionaryVertices); + + layoutInfo.Append(BagElementKind.DictionaryLayout, dictionaryLayout.WriteVertex(factory)); + } + + factory.MetadataManager.NativeLayoutInfo.TemplatesSection.Place(layoutInfo); + + return SetSavedVertex(layoutInfo); + } + } + + public sealed class NativeLayoutTemplateTypeLayoutVertexNode : NativeLayoutSavedVertexNode + { + private TypeDesc _type; + private bool _isUniversalCanon; + + protected override string GetName(NodeFactory factory) => "NativeLayoutTemplateTypeLayoutVertexNode_" + factory.NameMangler.GetMangledTypeName(_type); + + public NativeLayoutTemplateTypeLayoutVertexNode(NodeFactory factory, TypeDesc type) + { + Debug.Assert(type.IsCanonicalSubtype(CanonicalFormKind.Any)); + Debug.Assert(type.ConvertToCanonForm(CanonicalFormKind.Specific) == type, "Assert that the canonical type passed in is in standard canonical form"); + _isUniversalCanon = type.IsCanonicalSubtype(CanonicalFormKind.Universal); + + _type = GetActualTemplateTypeForType(factory, type); + } + + private static TypeDesc GetActualTemplateTypeForType(NodeFactory factory, TypeDesc type) + { + DefType defType = type as DefType; + if (defType == null) + { + Debug.Assert(GenericTypesTemplateMap.IsArrayTypeEligibleForTemplate(type)); + defType = type.GetClosestDefType().ConvertToSharedRuntimeDeterminedForm(); + Debug.Assert(defType.Instantiation.Length == 1); + return factory.TypeSystemContext.GetArrayType(defType.Instantiation[0]); + } + else + { + return defType.ConvertToSharedRuntimeDeterminedForm(); + } + } + + private ISymbolNode GetStaticsNode(NodeFactory context, out BagElementKind staticsBagKind) + { + ISymbolNode symbol = context.GCStaticEEType(GCPointerMap.FromStaticLayout(_type.GetClosestDefType())); + staticsBagKind = BagElementKind.GcStaticEEType; + + return symbol; + } + + private ISymbolNode GetThreadStaticsNode(NodeFactory context, out BagElementKind staticsBagKind) + { + ISymbolNode symbol = context.GCStaticEEType(GCPointerMap.FromThreadStaticLayout(_type.GetClosestDefType())); + staticsBagKind = BagElementKind.End; // GC static EETypes not yet implemented in type loader + + return symbol; + } + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + ISymbolNode typeNode = context.MaximallyConstructableType(_type.ConvertToCanonForm(CanonicalFormKind.Specific)); + + yield return new DependencyListEntry(typeNode, "Template MethodTable"); + + foreach (var dependency in context.NativeLayout.TemplateConstructableTypes(_type)) + { + yield return new DependencyListEntry(dependency, "type itslef must be template loadable"); + } + + yield return new DependencyListEntry(context.GenericDictionaryLayout(_type.ConvertToCanonForm(CanonicalFormKind.Specific).GetClosestDefType()), "Dictionary layout"); + + foreach (TypeDesc iface in _type.RuntimeInterfaces) + { + yield return new DependencyListEntry(context.NativeLayout.TypeSignatureVertex(iface), "template interface list"); + + foreach (var dependency in context.NativeLayout.TemplateConstructableTypes(iface)) + { + yield return new DependencyListEntry(dependency, "interface type dependency must be template loadable"); + } + } + + if (context.PreinitializationManager.HasLazyStaticConstructor(_type)) + { + yield return new DependencyListEntry(context.MethodEntrypoint(_type.GetStaticConstructor().GetCanonMethodTarget(CanonicalFormKind.Specific)), "cctor for template"); + } + + if (!_isUniversalCanon) + { + if (_type.GetClosestDefType().GCStaticFieldSize.AsInt > 0) + { + BagElementKind ignored; + yield return new DependencyListEntry(GetStaticsNode(context, out ignored), "type gc static info"); + } + + if (_type.GetClosestDefType().ThreadGcStaticFieldSize.AsInt > 0) + { + BagElementKind ignored; + yield return new DependencyListEntry(GetThreadStaticsNode(context, out ignored), "type thread static info"); + } + } + + if (_type.BaseType != null && !_type.BaseType.IsRuntimeDeterminedSubtype) + { + TypeDesc baseType = _type.BaseType; + do + { + yield return new DependencyListEntry(context.MaximallyConstructableType(baseType), "base types of canonical types must have their full vtables"); + baseType = baseType.BaseType; + } while (baseType != null); + } + + if (_type.BaseType != null && _type.BaseType.IsRuntimeDeterminedSubtype) + { + yield return new DependencyListEntry(context.NativeLayout.PlacedSignatureVertex(context.NativeLayout.TypeSignatureVertex(_type.BaseType)), "template base type"); + + foreach (var dependency in context.NativeLayout.TemplateConstructableTypes(_type.BaseType)) + { + yield return new DependencyListEntry(dependency, "base type must be template loadable"); + } + } + else if (_type.IsDelegate && _isUniversalCanon) + { + // For USG delegate, we need to write the signature of the Invoke method to the native layout. + // This signature is used by the calling convention converter to marshal parameters during delegate calls. + yield return new DependencyListEntry(context.NativeLayout.MethodSignatureVertex(_type.GetMethod("Invoke", null).GetTypicalMethodDefinition().Signature), "invoke method signature"); + } + + if (_isUniversalCanon) + { + // For universal canonical template types, we need to write out field layout information so that we + // can correctly compute the type sizes for dynamically created types at runtime, and construct + // their GCDesc info + foreach (FieldDesc field in _type.GetFields()) + { + // If this field does not contribute to layout, skip + if (field.HasRva || field.IsLiteral) + { + continue; + } + + DependencyListEntry typeForFieldLayout; + + if (field.FieldType.IsGCPointer) + { + typeForFieldLayout = new DependencyListEntry(context.NativeLayout.PlacedSignatureVertex(context.NativeLayout.TypeSignatureVertex(field.Context.GetWellKnownType(WellKnownType.Object))), "universal field layout type object sized"); + } + else if (field.FieldType.IsPointer || field.FieldType.IsFunctionPointer) + { + typeForFieldLayout = new DependencyListEntry(context.NativeLayout.PlacedSignatureVertex(context.NativeLayout.TypeSignatureVertex(field.Context.GetWellKnownType(WellKnownType.IntPtr))), "universal field layout type IntPtr sized"); + } + else + { + typeForFieldLayout = new DependencyListEntry(context.NativeLayout.PlacedSignatureVertex(context.NativeLayout.TypeSignatureVertex(field.FieldType)), "universal field layout type"); + + // And ensure the type can be properly laid out + foreach (var dependency in context.NativeLayout.TemplateConstructableTypes(field.FieldType)) + { + yield return new DependencyListEntry(dependency, "template construction dependency"); + } + } + + yield return typeForFieldLayout; + } + + // We also need to write out the signatures of interesting methods in the type's vtable, which + // will be needed by the calling convention translation logic at runtime, when the type's methods + // get invoked. This logic gathers nodes for entries *unconditionally* present. (entries may be conditionally + // present if a type has a vtable which has a size computed by usage not by IL contents) + List vtableSignatureNodeEntries = null; + int currentVTableIndexUnused = 0; + ProcessVTableEntriesForCallingConventionSignatureGeneration(context, VTableEntriesToProcess.AllOnTypesThatShouldProduceFullVTables, ref currentVTableIndexUnused, + (int vtableIndex, bool isSealedVTableSlot, MethodDesc declMethod, MethodDesc implMethod) => + { + if (implMethod.IsAbstract) + return; + + if (UniversalGenericParameterLayout.VTableMethodRequiresCallingConventionConverter(implMethod)) + { + if (vtableSignatureNodeEntries == null) + vtableSignatureNodeEntries = new List(); + + vtableSignatureNodeEntries.Add(context.NativeLayout.MethodSignatureVertex(declMethod.GetTypicalMethodDefinition().Signature)); + } + } + , _type, _type, _type); + + if (vtableSignatureNodeEntries != null) + { + foreach (NativeLayoutVertexNode node in vtableSignatureNodeEntries) + yield return new DependencyListEntry(node, "vtable cctor sig"); + } + } + } + + public override bool HasConditionalStaticDependencies => _isUniversalCanon; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) + { + List conditionalDependencies = null; + + if (_isUniversalCanon) + { + // We also need to write out the signatures of interesting methods in the type's vtable, which + // will be needed by the calling convention translation logic at runtime, when the type's methods + // get invoked. This logic gathers nodes for entries *conditionally* present. (entries may be conditionally + // present if a type has a vtable which has a size computed by usage not by IL contents) + + int currentVTableIndexUnused = 0; + ProcessVTableEntriesForCallingConventionSignatureGeneration(context, VTableEntriesToProcess.AllOnTypesThatProducePartialVTables, ref currentVTableIndexUnused, + (int vtableIndex, bool isSealedVTableSlot, MethodDesc declMethod, MethodDesc implMethod) => + { + if (implMethod.IsAbstract) + return; + + if (UniversalGenericParameterLayout.VTableMethodRequiresCallingConventionConverter(implMethod)) + { + if (conditionalDependencies == null) + conditionalDependencies = new List(); + + conditionalDependencies.Add( + new CombinedDependencyListEntry(context.NativeLayout.MethodSignatureVertex(declMethod.GetTypicalMethodDefinition().Signature), + context.VirtualMethodUse(declMethod), + "conditional vtable cctor sig")); + } + } + , _type, _type, _type); + } + + if (conditionalDependencies != null) + return conditionalDependencies; + else + return Array.Empty(); + } + + private int CompareDictionaryEntries(KeyValuePair left, KeyValuePair right) + { + return left.Key - right.Key; + } + + private bool HasInstantiationDeterminedSize() + { + Debug.Assert(_isUniversalCanon); + return _type.GetClosestDefType().InstanceFieldSize.IsIndeterminate; + } + + public override Vertex WriteVertex(NodeFactory factory) + { + Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); + + VertexBag layoutInfo = new VertexBag(); + + DictionaryLayoutNode associatedLayout = factory.GenericDictionaryLayout(_type.ConvertToCanonForm(CanonicalFormKind.Specific).GetClosestDefType()); + ICollection templateLayout = associatedLayout.GetTemplateEntries(factory); + + NativeWriter writer = GetNativeWriter(factory); + + // Interfaces + if (_type.RuntimeInterfaces.Length > 0) + { + List implementedInterfacesList = new List(); + + foreach (TypeDesc iface in _type.RuntimeInterfaces) + { + implementedInterfacesList.Add(factory.NativeLayout.TypeSignatureVertex(iface)); + } + NativeLayoutVertexNode implementedInterfaces = factory.NativeLayout.PlacedVertexSequence(implementedInterfacesList); + + layoutInfo.Append(BagElementKind.ImplementedInterfaces, implementedInterfaces.WriteVertex(factory)); + } + + if (!(_isUniversalCanon || (factory.LazyGenericsPolicy.UsesLazyGenerics(_type)) )&& (templateLayout.Count > 0)) + { + List dictionaryVertices = new List(); + + foreach (NativeLayoutVertexNode dictionaryEntry in templateLayout) + { + dictionaryEntry.CheckIfMarkedEnoughToWrite(); + dictionaryVertices.Add(dictionaryEntry); + } + NativeLayoutVertexNode dictionaryLayout = factory.NativeLayout.PlacedVertexSequence(dictionaryVertices); + + layoutInfo.Append(BagElementKind.DictionaryLayout, dictionaryLayout.WriteVertex(factory)); + } + + Internal.NativeFormat.TypeFlags typeFlags = default(Internal.NativeFormat.TypeFlags); + + if (factory.PreinitializationManager.HasLazyStaticConstructor(_type)) + { + MethodDesc cctorMethod = _type.GetStaticConstructor(); + MethodDesc canonCctorMethod = cctorMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + ISymbolNode cctorSymbol = factory.MethodEntrypoint(canonCctorMethod); + uint cctorStaticsIndex = factory.MetadataManager.NativeLayoutInfo.StaticsReferences.GetIndex(cctorSymbol); + layoutInfo.AppendUnsigned(BagElementKind.ClassConstructorPointer, cctorStaticsIndex); + + typeFlags = typeFlags | Internal.NativeFormat.TypeFlags.HasClassConstructor; + } + + if (!_isUniversalCanon) + { + DefType closestDefType = _type.GetClosestDefType(); + if (closestDefType.NonGCStaticFieldSize.AsInt != 0) + { + layoutInfo.AppendUnsigned(BagElementKind.NonGcStaticDataSize, checked((uint)closestDefType.NonGCStaticFieldSize.AsInt)); + } + + if (closestDefType.GCStaticFieldSize.AsInt != 0) + { + layoutInfo.AppendUnsigned(BagElementKind.GcStaticDataSize, checked((uint)closestDefType.GCStaticFieldSize.AsInt)); + BagElementKind staticDescBagType; + ISymbolNode staticsDescSymbol = GetStaticsNode(factory, out staticDescBagType); + uint gcStaticsSymbolIndex = factory.MetadataManager.NativeLayoutInfo.StaticsReferences.GetIndex(staticsDescSymbol); + layoutInfo.AppendUnsigned(staticDescBagType, gcStaticsSymbolIndex); + } + + if (closestDefType.ThreadGcStaticFieldSize.AsInt != 0) + { + layoutInfo.AppendUnsigned(BagElementKind.ThreadStaticDataSize, checked((uint)closestDefType.ThreadGcStaticFieldSize.AsInt)); + BagElementKind threadStaticDescBagType; + ISymbolNode threadStaticsDescSymbol = GetThreadStaticsNode(factory, out threadStaticDescBagType); + uint threadStaticsSymbolIndex = factory.MetadataManager.NativeLayoutInfo.StaticsReferences.GetIndex(threadStaticsDescSymbol); + layoutInfo.AppendUnsigned(threadStaticDescBagType, threadStaticsSymbolIndex); + } + } + else + { + Debug.Assert(_isUniversalCanon); + // Determine if type has instantiation determined size + if (!_type.IsInterface && HasInstantiationDeterminedSize()) + { + typeFlags = typeFlags | Internal.NativeFormat.TypeFlags.HasInstantiationDeterminedSize; + } + } + + if (_type.BaseType != null && _type.BaseType.IsRuntimeDeterminedSubtype) + { + layoutInfo.Append(BagElementKind.BaseType, factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(_type.BaseType)).WriteVertex(factory)); + } + else if (_type.IsDelegate && _isUniversalCanon) + { + // For USG delegate, we need to write the signature of the Invoke method to the native layout. + // This signature is used by the calling convention converter to marshal parameters during delegate calls. + MethodDesc delegateInvokeMethod = _type.GetMethod("Invoke", null).GetTypicalMethodDefinition(); + NativeLayoutMethodSignatureVertexNode invokeSignatureVertexNode = factory.NativeLayout.MethodSignatureVertex(delegateInvokeMethod.Signature); + layoutInfo.Append(BagElementKind.DelegateInvokeSignature, invokeSignatureVertexNode.WriteVertex(factory)); + } + + if (typeFlags != default(Internal.NativeFormat.TypeFlags)) + layoutInfo.AppendUnsigned(BagElementKind.TypeFlags, (uint)typeFlags); + + if (!_type.IsArrayTypeWithoutGenericInterfaces() && ConstructedEETypeNode.CreationAllowed(_type)) + { + SealedVTableNode sealedVTable = factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific)); + + sealedVTable.BuildSealedVTableSlots(factory, relocsOnly: false /* This is the final emission phase */); + + if (sealedVTable.NumSealedVTableEntries > 0) + layoutInfo.AppendUnsigned(BagElementKind.SealedVTableEntries, (uint)sealedVTable.NumSealedVTableEntries); + } + + if (_type.GetTypeDefinition().HasVariance || factory.TypeSystemContext.IsGenericArrayInterfaceType(_type)) + { + // Runtime casting logic relies on all interface types implemented on arrays + // to have the variant flag set (even if all the arguments are non-variant). + // This supports e.g. casting uint[] to ICollection + List varianceFlags = new List(); + foreach (GenericParameterDesc param in _type.GetTypeDefinition().Instantiation) + { + varianceFlags.Add((uint)param.Variance); + } + + layoutInfo.Append(BagElementKind.GenericVarianceInfo, factory.NativeLayout.PlacedUIntVertexSequence(varianceFlags).WriteVertex(factory)); + } + else if (_type.GetTypeDefinition() == factory.ArrayOfTEnumeratorType) + { + // Generic array enumerators use special variance rules recognized by the runtime + List varianceFlag = new List(); + varianceFlag.Add((uint)Internal.Runtime.GenericVariance.ArrayCovariant); + layoutInfo.Append(BagElementKind.GenericVarianceInfo, factory.NativeLayout.PlacedUIntVertexSequence(varianceFlag).WriteVertex(factory)); + } + + if (_isUniversalCanon) + { + // For universal canonical template types, we need to write out field layout information so that we + // can correctly compute the type sizes for dynamically created types at runtime, and construct + // their GCDesc info + VertexSequence fieldsSequence = null; + + foreach (FieldDesc field in _type.GetFields()) + { + // If this field does contribute to layout, skip + if (field.HasRva || field.IsLiteral) + continue; + + // NOTE: The order and contents of the signature vertices emitted here is what we consider a field ordinal for the + // purpose of NativeLayoutFieldOffsetGenericDictionarySlotNode. + + FieldStorage fieldStorage = FieldStorage.Instance; + if (field.IsStatic) + { + if (field.IsThreadStatic) + fieldStorage = FieldStorage.TLSStatic; + else if (field.HasGCStaticBase) + fieldStorage = FieldStorage.GCStatic; + else + fieldStorage = FieldStorage.NonGCStatic; + } + + + NativeLayoutVertexNode fieldTypeSignature; + if (field.FieldType.IsGCPointer) + { + fieldTypeSignature = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(field.Context.GetWellKnownType(WellKnownType.Object))); + } + else if (field.FieldType.IsPointer || field.FieldType.IsFunctionPointer) + { + fieldTypeSignature = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(field.Context.GetWellKnownType(WellKnownType.IntPtr))); + } + else + { + fieldTypeSignature = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(field.FieldType)); + } + + Vertex staticFieldVertexData = writer.GetTuple(fieldTypeSignature.WriteVertex(factory), writer.GetUnsignedConstant((uint)fieldStorage)); + + if (fieldsSequence == null) + fieldsSequence = new VertexSequence(); + fieldsSequence.Append(staticFieldVertexData); + } + + if (fieldsSequence != null) + { + Vertex placedFieldsLayout = factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(fieldsSequence); + layoutInfo.Append(BagElementKind.FieldLayout, placedFieldsLayout); + } + + // We also need to write out the signatures of interesting methods in the type's vtable, which + // will be needed by the calling convention translation logic at runtime, when the type's methods + // get invoked. + int currentVTableIndexUnused = 0; + VertexSequence vtableSignaturesSequence = null; + + ProcessVTableEntriesForCallingConventionSignatureGeneration(factory, VTableEntriesToProcess.AllInVTable, ref currentVTableIndexUnused, + (int vtableIndex, bool isSealedVTableSlot, MethodDesc declMethod, MethodDesc implMethod) => + { + if (implMethod.IsAbstract) + return; + + if (UniversalGenericParameterLayout.VTableMethodRequiresCallingConventionConverter(implMethod)) + { + if (vtableSignaturesSequence == null) + vtableSignaturesSequence = new VertexSequence(); + + NativeLayoutVertexNode methodSignature = factory.NativeLayout.MethodSignatureVertex(declMethod.GetTypicalMethodDefinition().Signature); + Vertex signatureVertex = GetNativeWriter(factory).GetRelativeOffsetSignature(methodSignature.WriteVertex(factory)); + + Vertex vtableSignatureEntry = writer.GetTuple( + writer.GetUnsignedConstant((uint)((vtableIndex << 1) | (isSealedVTableSlot ? 1 : 0))), + factory.MetadataManager.NativeLayoutInfo.TemplatesSection.Place(signatureVertex)); + + vtableSignaturesSequence.Append(vtableSignatureEntry); + } + } + , _type, _type, _type); + + if (vtableSignaturesSequence != null) + { + Vertex placedVtableSigs = factory.MetadataManager.NativeLayoutInfo.TemplatesSection.Place(vtableSignaturesSequence); + layoutInfo.Append(BagElementKind.VTableMethodSignatures, placedVtableSigs); + } + } + + factory.MetadataManager.NativeLayoutInfo.TemplatesSection.Place(layoutInfo); + + return SetSavedVertex(layoutInfo); + } + + private enum VTableEntriesToProcess + { + AllInVTable, + AllOnTypesThatShouldProduceFullVTables, + AllOnTypesThatProducePartialVTables + } + + private static IEnumerable EnumVirtualSlotsDeclaredOnType(TypeDesc declType) + { + // VirtualMethodUse of Foo.Method will bring in VirtualMethodUse + // of Foo<__Canon>.Method. This in turn should bring in Foo.Method. + DefType defType = declType.GetClosestDefType(); + + Debug.Assert(!declType.IsInterface); + + IEnumerable allSlots = defType.EnumAllVirtualSlots(); + + foreach (var method in allSlots) + { + // Generic virtual methods are tracked by an orthogonal mechanism. + if (method.HasInstantiation) + continue; + + // Current type doesn't define this slot. Another VTableSlice will take care of this. + if (method.OwningType != defType) + continue; + + yield return method; + } + } + + /// + /// Process the vtable entries of a type by calling operation with the vtable index, declaring method, and implementing method + /// Process them in order from 0th entry to last. + /// Skip generic virtual methods, as they are not present in the vtable itself + /// Do not adjust vtable index for generic dictionary slot + /// The vtable index is only actually valid if whichEntries is set to VTableEntriesToProcess.AllInVTable + /// + private void ProcessVTableEntriesForCallingConventionSignatureGeneration(NodeFactory factory, VTableEntriesToProcess whichEntries, ref int currentVTableIndex, Action operation, TypeDesc implType, TypeDesc declType, TypeDesc templateType) + { + if (implType.IsInterface) + return; + + declType = declType.GetClosestDefType(); + templateType = templateType.ConvertToCanonForm(CanonicalFormKind.Specific); + + bool canShareNormalCanonicalCode = declType != declType.ConvertToCanonForm(CanonicalFormKind.Specific); + + var baseType = declType.BaseType; + if (baseType != null) + { + Debug.Assert(templateType.BaseType != null); + ProcessVTableEntriesForCallingConventionSignatureGeneration(factory, whichEntries, ref currentVTableIndex, operation, implType, baseType, templateType.BaseType); + } + + IEnumerable vtableEntriesToProcess; + + if (ConstructedEETypeNode.CreationAllowed(declType)) + { + switch (whichEntries) + { + case VTableEntriesToProcess.AllInVTable: + vtableEntriesToProcess = factory.VTable(declType).Slots; + break; + + case VTableEntriesToProcess.AllOnTypesThatShouldProduceFullVTables: + if (factory.VTable(declType).HasFixedSlots) + { + vtableEntriesToProcess = factory.VTable(declType).Slots; + } + else + { + vtableEntriesToProcess = Array.Empty(); + } + break; + + case VTableEntriesToProcess.AllOnTypesThatProducePartialVTables: + if (factory.VTable(declType).HasFixedSlots) + { + vtableEntriesToProcess = Array.Empty(); + } + else + { + vtableEntriesToProcess = EnumVirtualSlotsDeclaredOnType(declType); + } + break; + + default: + throw new Exception(); + } + } + else + { + // If allocating an object of the MethodTable isn't permitted, don't process any vtable entries. + vtableEntriesToProcess = Array.Empty(); + } + + // Dictionary slot + if (declType.HasGenericDictionarySlot() || templateType.HasGenericDictionarySlot()) + currentVTableIndex++; + + int sealedVTableSlot = 0; + DefType closestDefType = implType.GetClosestDefType(); + + // Actual vtable slots follow + foreach (MethodDesc declMethod in vtableEntriesToProcess) + { + // No generic virtual methods can appear in the vtable! + Debug.Assert(!declMethod.HasInstantiation); + + MethodDesc implMethod = closestDefType.FindVirtualFunctionTargetMethodOnObjectType(declMethod); + + if (implMethod.CanMethodBeInSealedVTable() && !implType.IsArrayTypeWithoutGenericInterfaces()) + { + // Sealed vtable entries on other types in the hierarchy should not be reported (types read entries + // from their own sealed vtables, and not from the sealed vtables of base types). + if (implMethod.OwningType == closestDefType) + operation(sealedVTableSlot++, true, declMethod, implMethod); + } + else + { + operation(currentVTableIndex++, false, declMethod, implMethod); + } + } + } + } + + public abstract class NativeLayoutGenericDictionarySlotNode : NativeLayoutVertexNode + { + public abstract override IEnumerable GetStaticDependencies(NodeFactory context); + protected abstract Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory); + protected abstract FixupSignatureKind SignatureKind { get; } + + public override Vertex WriteVertex(NodeFactory factory) + { + CheckIfMarkedEnoughToWrite(); + + NativeWriter writer = GetNativeWriter(factory); + return writer.GetFixupSignature(SignatureKind, WriteSignatureVertex(writer, factory)); + } + } + + public abstract class NativeLayoutTypeSignatureBasedGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + NativeLayoutTypeSignatureVertexNode _signature; + TypeDesc _type; + + public NativeLayoutTypeSignatureBasedGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) + { + _signature = factory.NativeLayout.TypeSignatureVertex(type); + _type = type; + } + + protected abstract string NodeTypeName { get; } + protected sealed override string GetName(NodeFactory factory) => NodeTypeName + factory.NameMangler.GetMangledTypeName(_type); + + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + yield return new DependencyListEntry(_signature, "TypeSignature"); + + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(_type)) + { + yield return new DependencyListEntry(dependency, "template construction dependency"); + } + } + + protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + return _signature.WriteVertex(factory); + } + } + + public sealed class NativeLayoutTypeHandleGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode + { + public NativeLayoutTypeHandleGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + } + + protected override string NodeTypeName => "NativeLayoutTypeHandleGenericDictionarySlotNode_"; + + protected override FixupSignatureKind SignatureKind => FixupSignatureKind.TypeHandle; + } + + public sealed class NativeLayoutUnwrapNullableGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode + { + public NativeLayoutUnwrapNullableGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + } + + protected override string NodeTypeName => "NativeLayoutUnwrapNullableGenericDictionarySlotNode_"; + + protected override FixupSignatureKind SignatureKind => FixupSignatureKind.UnwrapNullableType; + } + + public sealed class NativeLayoutTypeSizeGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode + { + public NativeLayoutTypeSizeGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + } + + protected override string NodeTypeName => "NativeLayoutTypeSizeGenericDictionarySlotNode_"; + + protected override FixupSignatureKind SignatureKind => FixupSignatureKind.TypeSize; + } + + public sealed class NativeLayoutAllocateObjectGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode + { + public NativeLayoutAllocateObjectGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + } + + protected override string NodeTypeName => "NativeLayoutAllocateObjectGenericDictionarySlotNode_"; + + protected override FixupSignatureKind SignatureKind => FixupSignatureKind.AllocateObject; + } + + public sealed class NativeLayoutCastClassGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode + { + public NativeLayoutCastClassGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + } + + protected override string NodeTypeName => "NativeLayoutAllocateCastClassDictionarySlotNode_"; + + protected override FixupSignatureKind SignatureKind => FixupSignatureKind.CastClass; + } + + public sealed class NativeLayoutIsInstGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode + { + public NativeLayoutIsInstGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + } + + protected override string NodeTypeName => "NativeLayoutIsInstGenericDictionarySlotNode_"; + + protected override FixupSignatureKind SignatureKind => FixupSignatureKind.IsInst; + } + + public sealed class NativeLayoutThreadStaticBaseIndexDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode + { + public NativeLayoutThreadStaticBaseIndexDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + } + + protected override string NodeTypeName => "NativeLayoutThreadStaticBaseIndexDictionarySlotNode_"; + + protected override FixupSignatureKind SignatureKind => FixupSignatureKind.ThreadStaticIndex; + } + + public sealed class NativeLayoutDefaultConstructorGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode + { + public NativeLayoutDefaultConstructorGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + } + + protected override string NodeTypeName => "NativeLayoutDefaultConstructorGenericDictionarySlotNode_"; + + protected override FixupSignatureKind SignatureKind => FixupSignatureKind.DefaultConstructor; + } + + public sealed class NativeLayoutAllocateArrayGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode + { + public NativeLayoutAllocateArrayGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + Debug.Assert(type.IsArray); // TODO! Verify that the passed in type is the array type and not the element type of the array. + } + + protected override string NodeTypeName => "NativeLayoutAllocateArrayGenericDictionarySlotNode_"; + + protected override FixupSignatureKind SignatureKind => FixupSignatureKind.AllocateArray; + } + + public abstract class NativeLayoutStaticsGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + NativeLayoutTypeSignatureVertexNode _signature; + TypeDesc _type; + + public NativeLayoutStaticsGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) + { + _signature = factory.NativeLayout.TypeSignatureVertex(type); + _type = type; + } + + protected abstract StaticDataKind StaticDataKindFlag { get; } + protected abstract string NodeTypeName { get; } + + protected sealed override string GetName(NodeFactory factory) => NodeTypeName + factory.NameMangler.GetMangledTypeName(_type); + + protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.StaticData; + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + yield return new DependencyListEntry(_signature, "TypeSignature"); + + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(_type)) + { + yield return new DependencyListEntry(dependency, "template construction dependency"); + } + } + + protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + return writer.GetStaticDataSignature(_signature.WriteVertex(factory), StaticDataKindFlag); + } + } + + public sealed class NativeLayoutGcStaticsGenericDictionarySlotNode : NativeLayoutStaticsGenericDictionarySlotNode + { + public NativeLayoutGcStaticsGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { } + + protected override StaticDataKind StaticDataKindFlag => StaticDataKind.Gc; + protected override string NodeTypeName => "NativeLayoutGcStaticsGenericDictionarySlotNode_"; + } + + public sealed class NativeLayoutNonGcStaticsGenericDictionarySlotNode : NativeLayoutStaticsGenericDictionarySlotNode + { + public NativeLayoutNonGcStaticsGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { } + + protected override StaticDataKind StaticDataKindFlag => StaticDataKind.NonGc; + protected override string NodeTypeName => "NativeLayoutNonGcStaticsGenericDictionarySlotNode_"; + } + + public sealed class NativeLayoutInterfaceDispatchGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + NativeLayoutTypeSignatureVertexNode _signature; + MethodDesc _method; + + public NativeLayoutInterfaceDispatchGenericDictionarySlotNode(NodeFactory factory, MethodDesc method) + { + _signature = factory.NativeLayout.TypeSignatureVertex(method.OwningType); + _method = method; + } + + protected sealed override string GetName(NodeFactory factory) => "NativeLayoutInterfaceDispatchGenericDictionarySlotNode_" + factory.NameMangler.GetMangledMethodName(_method); + + protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.InterfaceCall; + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + yield return new DependencyListEntry(_signature, "TypeSignature"); + + MethodDesc method = _method; + if (method.IsRuntimeDeterminedExactMethod) + method = method.GetCanonMethodTarget(CanonicalFormKind.Specific); + + if (!factory.VTable(method.OwningType).HasFixedSlots) + { + yield return new DependencyListEntry(factory.VirtualMethodUse(method), "Slot number"); + } + + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(method.OwningType)) + { + yield return new DependencyListEntry(dependency, "template construction dependency"); + } + } + + protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + MethodDesc method = _method; + if (method.IsRuntimeDeterminedExactMethod) + method = method.GetCanonMethodTarget(CanonicalFormKind.Specific); + + int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, method, method.OwningType); + + return writer.GetMethodSlotSignature(_signature.WriteVertex(factory), checked((uint)slot)); + } + } + + public sealed class NativeLayoutMethodDictionaryGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + MethodDesc _method; + WrappedMethodDictionaryVertexNode _wrappedNode; + + private class WrappedMethodDictionaryVertexNode : NativeLayoutMethodEntryVertexNode + { + public WrappedMethodDictionaryVertexNode(NodeFactory factory, MethodDesc method) : + base(factory, method, default(MethodEntryFlags)) + { + } + + protected override IMethodNode GetMethodEntrypointNode(NodeFactory factory, out bool unboxingStub) + { + throw new NotSupportedException(); + } + + protected sealed override string GetName(NodeFactory factory) => "WrappedMethodEntryVertexNodeForDictionarySlot_" + factory.NameMangler.GetMangledMethodName(_method); + } + + + public NativeLayoutMethodDictionaryGenericDictionarySlotNode(NodeFactory factory, MethodDesc method) + { + Debug.Assert(method.HasInstantiation); + _method = method; + _wrappedNode = new WrappedMethodDictionaryVertexNode(factory, method); + } + + protected sealed override string GetName(NodeFactory factory) => "NativeLayoutMethodDictionaryGenericDictionarySlotNode_" + factory.NameMangler.GetMangledMethodName(_method); + protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.MethodDictionary; + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + var dependencies = new DependencyList(); + + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(_method.OwningType)) + { + dependencies.Add(dependency, "template construction dependency for method OwningType"); + } + + foreach (var type in _method.Instantiation) + { + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(type)) + dependencies.Add(dependency, "template construction dependency for method Instantiation types"); + } + + GenericMethodsTemplateMap.GetTemplateMethodDependencies(ref dependencies, factory, _method.GetCanonMethodTarget(CanonicalFormKind.Specific)); + + dependencies.Add(_wrappedNode, "wrappednode"); + + return dependencies; + } + + protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + return _wrappedNode.WriteVertex(factory); + } + } + + public sealed class NativeLayoutFieldOffsetGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + FieldDesc _field; + + public NativeLayoutFieldOffsetGenericDictionarySlotNode(FieldDesc field) + { + _field = field; + } + + protected sealed override string GetName(NodeFactory factory) => "NativeLayoutFieldOffsetGenericDictionarySlotNode_" + factory.NameMangler.GetMangledFieldName(_field); + + protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.FieldOffset; + + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + yield return new DependencyListEntry(factory.NativeLayout.TypeSignatureVertex(_field.OwningType), "Field Containing Type Signature"); + + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(_field.OwningType)) + { + yield return new DependencyListEntry(dependency, "template construction dependency"); + } + } + + protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + NativeWriter nativeWriter = GetNativeWriter(factory); + + // NOTE: The order and contents of the field ordinal emitted here is based on the order of emission for fields + // in the USG template generation. + Vertex typeVertex = factory.NativeLayout.TypeSignatureVertex(_field.OwningType).WriteVertex(factory); + return nativeWriter.GetTuple(typeVertex, nativeWriter.GetUnsignedConstant(checked((uint)_field.GetFieldOrdinal()))); + } + } + + public sealed class NativeLayoutFieldLdTokenGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + FieldDesc _field; + + public NativeLayoutFieldLdTokenGenericDictionarySlotNode(FieldDesc field) + { + _field = field; + } + + protected sealed override string GetName(NodeFactory factory) => "NativeLayoutFieldLdTokenGenericDictionarySlotNode_" + factory.NameMangler.GetMangledFieldName(_field); + + protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.FieldLdToken; + + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + yield return new DependencyListEntry(factory.NativeLayout.FieldLdTokenVertex(_field), "Field Signature"); + + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(_field.OwningType)) + { + yield return new DependencyListEntry(dependency, "template construction dependency"); + } + } + + protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + Vertex ldToken = factory.NativeLayout.FieldLdTokenVertex(_field).WriteVertex(factory); + return GetNativeWriter(factory).GetRelativeOffsetSignature(ldToken); + } + } + + public sealed class NativeLayoutVTableOffsetGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + MethodDesc _method; + MethodDesc _slotDefiningMethod; + + public NativeLayoutVTableOffsetGenericDictionarySlotNode(MethodDesc method) + { + _method = method; + MethodDesc typicalSlotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method.GetTypicalMethodDefinition()); + _slotDefiningMethod = _method.OwningType.FindMethodOnTypeWithMatchingTypicalMethod(typicalSlotDefiningMethod); + Debug.Assert(method.IsRuntimeDeterminedExactMethod); + Debug.Assert(!method.HasInstantiation); + Debug.Assert(!method.OwningType.IsInterface); + Debug.Assert(method.OwningType.IsDefType); + Debug.Assert(method.IsVirtual); + } + + protected sealed override string GetName(NodeFactory factory) => "NativeLayoutVTableOffsetGenericDictionarySlotNode_" + factory.NameMangler.GetMangledMethodName(_method); + + protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.VTableOffset; + + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + yield return new DependencyListEntry(factory.NativeLayout.TypeSignatureVertex(_slotDefiningMethod.OwningType), "Method VTableOffset Containing Type Signature"); + + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(_slotDefiningMethod.OwningType)) + { + yield return new DependencyListEntry(dependency, "template construction dependency"); + } + } + + protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + NativeWriter nativeWriter = GetNativeWriter(factory); + + int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _slotDefiningMethod, _slotDefiningMethod.OwningType); + Vertex typeVertex = factory.NativeLayout.TypeSignatureVertex(_slotDefiningMethod.OwningType).WriteVertex(factory); + return nativeWriter.GetTuple(typeVertex, nativeWriter.GetUnsignedConstant((uint)slot)); + } + } + + public sealed class NativeLayoutMethodLdTokenGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + MethodDesc _method; + + public NativeLayoutMethodLdTokenGenericDictionarySlotNode(MethodDesc method) + { + _method = method; + } + + protected sealed override string GetName(NodeFactory factory) => "NativeLayoutMethodLdTokenGenericDictionarySlotNode_" + factory.NameMangler.GetMangledMethodName(_method); + + protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.MethodLdToken; + + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + yield return new DependencyListEntry(factory.NativeLayout.MethodLdTokenVertex(_method), "Method Signature"); + + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(_method.OwningType)) + { + yield return new DependencyListEntry(dependency, "template construction dependency for method OwningType"); + } + + foreach (var type in _method.Instantiation) + { + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(type)) + yield return new DependencyListEntry(dependency, "template construction dependency for method Instantiation types"); + } + } + + protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + Vertex ldToken = factory.NativeLayout.MethodLdTokenVertex(_method).WriteVertex(factory); + return GetNativeWriter(factory).GetRelativeOffsetSignature(ldToken); + } + } + + public sealed class NativeLayoutCallingConventionConverterGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + Internal.TypeSystem.MethodSignature _signature; + CallingConventionConverterKind _converterKind; + + public NativeLayoutCallingConventionConverterGenericDictionarySlotNode(Internal.TypeSystem.MethodSignature signature, CallingConventionConverterKind converterKind) + { + _signature = signature; + _converterKind = converterKind; + } + + protected sealed override string GetName(NodeFactory factory) => + "NativeLayoutCallingConventionConverterGenericDictionarySlotNode" + _converterKind.ToString() + + _signature.GetName(); + + protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.CallingConventionConverter; + + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + yield return new DependencyListEntry(factory.NativeLayout.MethodSignatureVertex(_signature), "Method Signature"); + + for (int i = 0; i < _signature.Length; i++) + { + foreach (var dep in factory.NativeLayout.UniversalTemplateConstructableTypes(_signature[i])) + { + yield return new DependencyListEntry(dep, "template construction dependency"); + } + } + foreach (var dep in factory.NativeLayout.UniversalTemplateConstructableTypes(_signature.ReturnType)) + { + yield return new DependencyListEntry(dep, "template construction dependency"); + } + } + + protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + Vertex signatureStream = factory.NativeLayout.MethodSignatureVertex(_signature).WriteVertex(factory); + return GetNativeWriter(factory).GetCallingConventionConverterSignature((uint)_converterKind, signatureStream); + } + } + + public sealed class NativeLayoutConstrainedMethodDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + MethodDesc _constrainedMethod; + TypeDesc _constraintType; + bool _directCall; + + public NativeLayoutConstrainedMethodDictionarySlotNode(MethodDesc constrainedMethod, TypeDesc constraintType, bool directCall) + { + _constrainedMethod = constrainedMethod; + _constraintType = constraintType; + _directCall = directCall; + Debug.Assert(_constrainedMethod.OwningType.IsInterface); + Debug.Assert(!_constrainedMethod.HasInstantiation || !directCall); + } + + protected sealed override string GetName(NodeFactory factory) => + "NativeLayoutConstrainedMethodDictionarySlotNode_" + + (_directCall ? "Direct" : "") + + factory.NameMangler.GetMangledMethodName(_constrainedMethod) + + "," + + factory.NameMangler.GetMangledTypeName(_constraintType); + + protected sealed override FixupSignatureKind SignatureKind => _constrainedMethod.HasInstantiation ? FixupSignatureKind.GenericConstrainedMethod : + (_directCall ? FixupSignatureKind.NonGenericDirectConstrainedMethod : FixupSignatureKind.NonGenericConstrainedMethod); + + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyNodeCore constrainedMethodDescriptorNode; + if (_constrainedMethod.HasInstantiation) + { + constrainedMethodDescriptorNode = factory.NativeLayout.MethodLdTokenVertex(_constrainedMethod); + } + else + { + constrainedMethodDescriptorNode = factory.NativeLayout.TypeSignatureVertex(_constrainedMethod.OwningType); + } + + yield return new DependencyListEntry(factory.NativeLayout.TypeSignatureVertex(_constraintType), "ConstraintType"); + + yield return new DependencyListEntry(constrainedMethodDescriptorNode, "ConstrainedMethodType"); + + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(_constrainedMethod.OwningType)) + { + yield return new DependencyListEntry(dependency, "template construction dependency constrainedMethod OwningType"); + } + + foreach (var type in _constrainedMethod.Instantiation) + { + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(type)) + yield return new DependencyListEntry(dependency, "template construction dependency constrainedMethod Instantiation type"); + } + + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(_constraintType)) + yield return new DependencyListEntry(dependency, "template construction dependency constraintType"); + } + + protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + Vertex constraintType = factory.NativeLayout.TypeSignatureVertex(_constraintType).WriteVertex(factory); + if (_constrainedMethod.HasInstantiation) + { + Debug.Assert(SignatureKind == FixupSignatureKind.GenericConstrainedMethod); + Vertex constrainedMethodVertex = factory.NativeLayout.MethodLdTokenVertex(_constrainedMethod).WriteVertex(factory); + Vertex relativeOffsetVertex = GetNativeWriter(factory).GetRelativeOffsetSignature(constrainedMethodVertex); + return writer.GetTuple(constraintType, relativeOffsetVertex); + } + else + { + Debug.Assert((SignatureKind == FixupSignatureKind.NonGenericConstrainedMethod) || (SignatureKind == FixupSignatureKind.NonGenericDirectConstrainedMethod)); + Vertex methodType = factory.NativeLayout.TypeSignatureVertex(_constrainedMethod.OwningType).WriteVertex(factory); + var canonConstrainedMethod = _constrainedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + int interfaceSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, canonConstrainedMethod, canonConstrainedMethod.OwningType); + Vertex interfaceSlotVertex = writer.GetUnsignedConstant(checked((uint)interfaceSlot)); + return writer.GetTuple(constraintType, methodType, interfaceSlotVertex); + } + } + } + + public sealed class NativeLayoutMethodEntrypointGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + MethodDesc _method; + WrappedMethodEntryVertexNode _wrappedNode; + + private class WrappedMethodEntryVertexNode : NativeLayoutMethodEntryVertexNode + { + public bool _unboxingStub; + public IMethodNode _functionPointerNode; + + public WrappedMethodEntryVertexNode(NodeFactory factory, MethodDesc method, bool unboxingStub, IMethodNode functionPointerNode) : + base(factory, method, functionPointerNode != null ? MethodEntryFlags.SaveEntryPoint : default(MethodEntryFlags)) + { + _unboxingStub = unboxingStub; + _functionPointerNode = functionPointerNode; + } + + protected override IMethodNode GetMethodEntrypointNode(NodeFactory factory, out bool unboxingStub) + { + unboxingStub = _unboxingStub; + return _functionPointerNode; + } + + protected sealed override string GetName(NodeFactory factory) => "WrappedMethodEntryVertexNodeForDictionarySlot_" + (_unboxingStub ? "Unboxing_" : "") + factory.NameMangler.GetMangledMethodName(_method); + } + + + public NativeLayoutMethodEntrypointGenericDictionarySlotNode(NodeFactory factory, MethodDesc method, IMethodNode functionPointerNode, bool unboxingStub) + { + _method = method; + _wrappedNode = new WrappedMethodEntryVertexNode(factory, method, unboxingStub, functionPointerNode); + } + + protected sealed override string GetName(NodeFactory factory) => "NativeLayoutMethodEntrypointGenericDictionarySlotNode_" + (_wrappedNode._unboxingStub ? "Unboxing_" : "") + factory.NameMangler.GetMangledMethodName(_method); + protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.Method; + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(_method.OwningType)) + { + dependencies.Add(dependency, "template construction dependency for method OwningType"); + } + + foreach (var type in _method.Instantiation) + { + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(type)) + dependencies.Add(dependency, "template construction dependency for method Instantiation types"); + } + + GenericMethodsTemplateMap.GetTemplateMethodDependencies(ref dependencies, factory, _method.GetCanonMethodTarget(CanonicalFormKind.Specific)); + + dependencies.Add(_wrappedNode, "wrappednode"); + + return dependencies; + } + + protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + return _wrappedNode.WriteVertex(factory); + } + } + + public sealed class NativeLayoutIntegerDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + int _value; + + public NativeLayoutIntegerDictionarySlotNode(int value) + { + _value = value; + } + + protected override FixupSignatureKind SignatureKind => FixupSignatureKind.IntValue; + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return null; + } + + protected override string GetName(NodeFactory context) => "NativeLayoutIntegerDictionarySlotNode_" + _value.ToStringInvariant(); + + protected override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + return writer.GetUnsignedConstant((uint)_value); + } + + public override void CheckIfMarkedEnoughToWrite() + { + // Do nothing, this node does not need marking + } + } + + public sealed class NativeLayoutPointerToOtherSlotDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + int _otherSlotIndex; + + public NativeLayoutPointerToOtherSlotDictionarySlotNode(int otherSlotIndex) + { + _otherSlotIndex = otherSlotIndex; + } + + protected override FixupSignatureKind SignatureKind => FixupSignatureKind.PointerToOtherSlot; + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return null; + } + + protected override string GetName(NodeFactory context) => "NativeLayoutPointerToOtherSlotDictionarySlotNode_" + _otherSlotIndex.ToStringInvariant(); + + protected override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + return writer.GetUnsignedConstant((uint)_otherSlotIndex); + } + + public override void CheckIfMarkedEnoughToWrite() + { + // Do nothing, this node does not need marking + } + } + + public sealed class NativeLayoutNotSupportedDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + protected override FixupSignatureKind SignatureKind => FixupSignatureKind.NotYetSupported; + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return null; + } + + protected override string GetName(NodeFactory context) => "NativeLayoutNotSupportedDictionarySlotNode"; + + protected override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + return writer.GetUnsignedConstant(0xDEADBEEF); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NecessaryCanonicalEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NecessaryCanonicalEETypeNode.cs new file mode 100644 index 00000000000000..92cb5e195498c3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NecessaryCanonicalEETypeNode.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// The node is used in ProjectX to represent a canonical type that does not have a vtable. + /// + public sealed class NecessaryCanonicalEETypeNode : EETypeNode + { + public NecessaryCanonicalEETypeNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + Debug.Assert(!type.IsCanonicalDefinitionType(CanonicalFormKind.Any)); + Debug.Assert(type.IsCanonicalSubtype(CanonicalFormKind.Any)); + Debug.Assert(type == type.ConvertToCanonForm(CanonicalFormKind.Specific)); + Debug.Assert(!type.IsMdArray || factory.Target.Abi == TargetAbi.CppCodegen); + } + + protected override ISymbolNode GetBaseTypeNode(NodeFactory factory) + { + return _type.BaseType != null ? factory.NecessaryTypeSymbol(_type.BaseType.NormalizeInstantiation()) : null; + } + + public override int ClassCode => 1505000724; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs new file mode 100644 index 00000000000000..3ab46f5bb06681 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs @@ -0,0 +1,339 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// Part of Node factory that deals with nodes describing results of generic lookups. + /// See: . + partial class NodeFactory + { + /// + /// Helper class that provides a level of grouping for all the generic lookup result kinds. + /// + public class GenericLookupResults + { + public GenericLookupResults() + { + CreateNodeCaches(); + } + + private void CreateNodeCaches() + { + _typeSymbols = new NodeCache(type => + { + return new TypeHandleGenericLookupResult(type); + }); + + _unwrapNullableSymbols = new NodeCache(type => + { + return new UnwrapNullableTypeHandleGenericLookupResult(type); + }); + + _methodHandles = new NodeCache(method => + { + return new MethodHandleGenericLookupResult(method); + }); + + _fieldHandles = new NodeCache(field => + { + return new FieldHandleGenericLookupResult(field); + }); + + _methodDictionaries = new NodeCache(method => + { + return new MethodDictionaryGenericLookupResult(method); + }); + + _methodEntrypoints = new NodeCache(key => + { + return new MethodEntryGenericLookupResult(key.Method, key.IsUnboxingStub); + }); + + _virtualDispatchCells = new NodeCache(method => + { + return new VirtualDispatchCellGenericLookupResult(method); + }); + + _typeThreadStaticBaseIndexSymbols = new NodeCache(type => + { + return new TypeThreadStaticBaseIndexGenericLookupResult(type); + }); + + _typeGCStaticBaseSymbols = new NodeCache(type => + { + return new TypeGCStaticBaseGenericLookupResult(type); + }); + + _typeNonGCStaticBaseSymbols = new NodeCache(type => + { + return new TypeNonGCStaticBaseGenericLookupResult(type); + }); + + _objectAllocators = new NodeCache(type => + { + return new ObjectAllocatorGenericLookupResult(type); + }); + + _arrayAllocators = new NodeCache(type => + { + return new ArrayAllocatorGenericLookupResult(type); + }); + + _isInstHelpers = new NodeCache(type => + { + return new IsInstGenericLookupResult(type); + }); + + _castClassHelpers = new NodeCache(type => + { + return new CastClassGenericLookupResult(type); + }); + + _defaultCtors = new NodeCache(type => + { + return new DefaultConstructorLookupResult(type); + }); + + _fieldOffsets = new NodeCache(field => + { + return new FieldOffsetGenericLookupResult(field); + }); + + _vtableOffsets = new NodeCache(method => + { + return new VTableOffsetGenericLookupResult(method); + }); + + _callingConventionConverters = new NodeCache(key => + { + return new CallingConventionConverterLookupResult(key); + }); + + _typeSizes = new NodeCache(type => + { + return new TypeSizeLookupResult(type); + }); + + _constrainedMethodUses = new NodeCache(constrainedMethodUse => + { + return new ConstrainedMethodUseLookupResult(constrainedMethodUse.ConstrainedMethod, constrainedMethodUse.ConstraintType, constrainedMethodUse.DirectCall); + }); + } + + private NodeCache _typeSymbols; + + public GenericLookupResult Type(TypeDesc type) + { + return _typeSymbols.GetOrAdd(type); + } + + private NodeCache _unwrapNullableSymbols; + + public GenericLookupResult UnwrapNullableType(TypeDesc type) + { + // An actual unwrap nullable lookup is only required if the type is exactly a runtime + // determined type associated with System.__UniversalCanon itself, or if it's + // a runtime determined instance of Nullable. + if (type.IsRuntimeDeterminedType && ( + ((RuntimeDeterminedType)type).CanonicalType.IsCanonicalDefinitionType(CanonicalFormKind.Universal) || + ((RuntimeDeterminedType)type).CanonicalType.IsNullable)) + return _unwrapNullableSymbols.GetOrAdd(type); + else + { + // Perform the unwrap or not eagerly, and use a normal Type GenericLookupResult + if (type.IsNullable) + return Type(type.Instantiation[0]); + else + return Type(type); + } + } + + private NodeCache _methodHandles; + + public GenericLookupResult MethodHandle(MethodDesc method) + { + return _methodHandles.GetOrAdd(method); + } + + private NodeCache _fieldHandles; + + public GenericLookupResult FieldHandle(FieldDesc field) + { + return _fieldHandles.GetOrAdd(field); + } + + private NodeCache _typeThreadStaticBaseIndexSymbols; + + public GenericLookupResult TypeThreadStaticBaseIndex(TypeDesc type) + { + return _typeThreadStaticBaseIndexSymbols.GetOrAdd(type); + } + + private NodeCache _typeGCStaticBaseSymbols; + + public GenericLookupResult TypeGCStaticBase(TypeDesc type) + { + return _typeGCStaticBaseSymbols.GetOrAdd(type); + } + + private NodeCache _typeNonGCStaticBaseSymbols; + + public GenericLookupResult TypeNonGCStaticBase(TypeDesc type) + { + return _typeNonGCStaticBaseSymbols.GetOrAdd(type); + } + + private NodeCache _methodDictionaries; + + public GenericLookupResult MethodDictionary(MethodDesc method) + { + return _methodDictionaries.GetOrAdd(method); + } + + private NodeCache _virtualDispatchCells; + + public GenericLookupResult VirtualDispatchCell(MethodDesc method) + { + return _virtualDispatchCells.GetOrAdd(method); + } + + private NodeCache _methodEntrypoints; + + public GenericLookupResult MethodEntry(MethodDesc method, bool isUnboxingThunk = false) + { + return _methodEntrypoints.GetOrAdd(new MethodKey(method, isUnboxingThunk)); + } + + private NodeCache _objectAllocators; + + public GenericLookupResult ObjectAllocator(TypeDesc type) + { + return _objectAllocators.GetOrAdd(type); + } + + private NodeCache _arrayAllocators; + + public GenericLookupResult ArrayAllocator(TypeDesc type) + { + return _arrayAllocators.GetOrAdd(type); + } + + private NodeCache _isInstHelpers; + + public GenericLookupResult IsInstHelper(TypeDesc type) + { + return _isInstHelpers.GetOrAdd(type); + } + + private NodeCache _castClassHelpers; + + public GenericLookupResult CastClassHelper(TypeDesc type) + { + return _castClassHelpers.GetOrAdd(type); + } + + private NodeCache _defaultCtors; + + public GenericLookupResult DefaultCtorLookupResult(TypeDesc type) + { + return _defaultCtors.GetOrAdd(type); + } + + private NodeCache _fieldOffsets; + + public GenericLookupResult FieldOffsetLookupResult(FieldDesc field) + { + return _fieldOffsets.GetOrAdd(field); + } + + private NodeCache _vtableOffsets; + + public GenericLookupResult VTableOffsetLookupResult(MethodDesc method) + { + return _vtableOffsets.GetOrAdd(method); + } + + private NodeCache _callingConventionConverters; + + public GenericLookupResult CallingConventionConverterLookupResult(CallingConventionConverterKey key) + { + return _callingConventionConverters.GetOrAdd(key); + } + + private NodeCache _typeSizes; + + public GenericLookupResult TypeSize(TypeDesc type) + { + return _typeSizes.GetOrAdd(type); + } + + private NodeCache _constrainedMethodUses; + public GenericLookupResult ConstrainedMethodUse(MethodDesc constrainedMethod, TypeDesc constraintType, bool directCall) + { + return _constrainedMethodUses.GetOrAdd(new ConstrainedMethodUseKey(constrainedMethod, constraintType, directCall)); + } + + private static NodeCache s_integers = new NodeCache(slotIndex => + { + return new IntegerLookupResult(slotIndex); + }); + + public static GenericLookupResult Integer(int integer) + { + return s_integers.GetOrAdd(integer); + } + + private static NodeCache s_pointersToSlots = new NodeCache(slotIndex => + { + return new PointerToSlotLookupResult(slotIndex); + }); + + public static GenericLookupResult PointerToSlot(int slotIndex) + { + return s_pointersToSlots.GetOrAdd(slotIndex); + } + } + + public GenericLookupResults GenericLookup = new GenericLookupResults(); + + private struct ConstrainedMethodUseKey : IEquatable + { + public ConstrainedMethodUseKey(MethodDesc constrainedMethod, TypeDesc constraintType, bool directCall) + { + ConstrainedMethod = constrainedMethod; + ConstraintType = constraintType; + DirectCall = directCall; + } + + public readonly MethodDesc ConstrainedMethod; + public readonly TypeDesc ConstraintType; + public readonly bool DirectCall; + + public override int GetHashCode() + { + return ConstraintType.GetHashCode() ^ ConstrainedMethod.GetHashCode() ^ DirectCall.GetHashCode(); + } + + public override bool Equals(object obj) + { + return (obj is ConstrainedMethodUseKey) && Equals((ConstrainedMethodUseKey)obj); + } + + public bool Equals(ConstrainedMethodUseKey other) + { + if (ConstraintType != other.ConstraintType) + return false; + if (ConstrainedMethod != other.ConstrainedMethod) + return false; + if (DirectCall != other.DirectCall) + return false; + + return true; + } + } + + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs new file mode 100644 index 00000000000000..b81a81c3064057 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs @@ -0,0 +1,674 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using Internal.Text; +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// Part of Node factory that deals with nodes describing native layout information + partial class NodeFactory + { + /// + /// Helper class that provides a level of grouping for all the native layout lookups + /// + public class NativeLayoutHelper + { + NodeFactory _factory; + + public NativeLayoutHelper(NodeFactory factory) + { + _factory = factory; + CreateNodeCaches(); + } + + private void CreateNodeCaches() + { + _typeSignatures = new NodeCache(type => + { + return NativeLayoutTypeSignatureVertexNode.NewTypeSignatureVertexNode(_factory, type); + }); + + _methodSignatures = new NodeCache(signature => + { + return new NativeLayoutMethodSignatureVertexNode(_factory, signature); + }); + + _callingConventionSlots = new NodeCache(key => + { + return new NativeLayoutCallingConventionConverterGenericDictionarySlotNode(key.Signature, key.ConverterKind); + }); + + _methodNameAndSignatures = new NodeCache(method => + { + return new NativeLayoutMethodNameAndSignatureVertexNode(_factory, method); + }); + + _placedSignatures = new NodeCache(vertexNode => + { + return new NativeLayoutPlacedSignatureVertexNode(vertexNode); + }); + + _placedVertexSequence = new NodeCache(vertices => + { + return new NativeLayoutPlacedVertexSequenceVertexNode(vertices.Vertices); + }); + + _placedUIntVertexSequence = new NodeCache, NativeLayoutPlacedVertexSequenceOfUIntVertexNode>(uints => + { + return new NativeLayoutPlacedVertexSequenceOfUIntVertexNode(uints); + }, new UIntSequenceComparer()); + + _methodLdTokenSignatures = new NodeCache(method => + { + return new NativeLayoutMethodLdTokenVertexNode(_factory, method); + }); + + _fieldLdTokenSignatures = new NodeCache(field => + { + return new NativeLayoutFieldLdTokenVertexNode(_factory, field); + }); + + _nativeLayoutSignatureNodes = new NodeCache(key => + { + return new NativeLayoutSignatureNode(key.SignatureVertex, key.Identity, key.IdentityPrefix); + }); + + _templateMethodEntries = new NodeCache(method => + { + return new NativeLayoutTemplateMethodSignatureVertexNode(_factory, method); + }); + + _templateMethodLayouts = new NodeCache(method => + { + return new NativeLayoutTemplateMethodLayoutVertexNode(_factory, method); + }); + + _templateTypeLayouts = new NodeCache(type => + { + return new NativeLayoutTemplateTypeLayoutVertexNode(_factory, type); + }); + + _typeHandle_GenericDictionarySlots = new NodeCache(type => + { + return new NativeLayoutTypeHandleGenericDictionarySlotNode(_factory, type); + }); + + _gcStatic_GenericDictionarySlots = new NodeCache(type => + { + return new NativeLayoutGcStaticsGenericDictionarySlotNode(_factory, type); + }); + + _nonGcStatic_GenericDictionarySlots = new NodeCache(type => + { + return new NativeLayoutNonGcStaticsGenericDictionarySlotNode(_factory, type); + }); + + _unwrapNullable_GenericDictionarySlots = new NodeCache(type => + { + return new NativeLayoutUnwrapNullableGenericDictionarySlotNode(_factory, type); + }); + + _typeSize_GenericDictionarySlots = new NodeCache(type => + { + return new NativeLayoutTypeSizeGenericDictionarySlotNode(_factory, type); + }); + + _allocateObject_GenericDictionarySlots = new NodeCache(type => + { + return new NativeLayoutAllocateObjectGenericDictionarySlotNode(_factory, type); + }); + + _castClass_GenericDictionarySlots = new NodeCache(type => + { + return new NativeLayoutCastClassGenericDictionarySlotNode(_factory, type); + }); + + _isInst_GenericDictionarySlots = new NodeCache(type => + { + return new NativeLayoutIsInstGenericDictionarySlotNode(_factory, type); + }); + + _threadStaticIndex_GenericDictionarySlots = new NodeCache(type => + { + return new NativeLayoutThreadStaticBaseIndexDictionarySlotNode(_factory, type); + }); + + _defaultConstructor_GenericDictionarySlots = new NodeCache(type => + { + return new NativeLayoutDefaultConstructorGenericDictionarySlotNode(_factory, type); + }); + + _allocateArray_GenericDictionarySlots = new NodeCache(type => + { + return new NativeLayoutAllocateArrayGenericDictionarySlotNode(_factory, type); + }); + + _interfaceCell_GenericDictionarySlots = new NodeCache(method => + { + return new NativeLayoutInterfaceDispatchGenericDictionarySlotNode(_factory, method); + }); + + _methodDictionary_GenericDictionarySlots = new NodeCache(method => + { + return new NativeLayoutMethodDictionaryGenericDictionarySlotNode(_factory, method); + }); + + _methodEntrypoint_GenericDictionarySlots = new NodeCache(key => + { + return new NativeLayoutMethodEntrypointGenericDictionarySlotNode(_factory, key.Method, key.FunctionPointerTarget, key.Unboxing); + }); + + _fieldLdToken_GenericDictionarySlots = new NodeCache(field => + { + return new NativeLayoutFieldLdTokenGenericDictionarySlotNode(field); + }); + + _methodLdToken_GenericDictionarySlots = new NodeCache(method => + { + return new NativeLayoutMethodLdTokenGenericDictionarySlotNode(method); + }); + + _fieldOffset_GenericDictionaryslots = new NodeCache(field => + { + return new NativeLayoutFieldOffsetGenericDictionarySlotNode(field); + }); + + _vtableOffset_GenericDictionaryslots = new NodeCache(method => + { + return new NativeLayoutVTableOffsetGenericDictionarySlotNode(method); + }); + + _dictionarySignatures = new NodeCache(owningMethodOrType => + { + return new NativeLayoutDictionarySignatureNode(_factory, owningMethodOrType); + }); + + _integerSlots = new NodeCache(value => + { + return new NativeLayoutIntegerDictionarySlotNode(value); + }); + + _otherSlotPointerSlots = new NodeCache(otherSlotIndex => + { + return new NativeLayoutPointerToOtherSlotDictionarySlotNode(otherSlotIndex); + }); + + _constrainedMethodUseSlots = new NodeCache(constrainedMethodUse => + { + return new NativeLayoutConstrainedMethodDictionarySlotNode(constrainedMethodUse.ConstrainedMethod, constrainedMethodUse.ConstraintType, constrainedMethodUse.DirectCall); + }); + } + + // Produce a set of dependencies that is necessary such that if this type + // needs to be used referenced from a NativeLayout template, that the template + // will be properly constructable. (This is done by ensuring that all + // canonical types in the deconstruction of the type are ConstructedEEType instead + // of just necessary. (Which is what the actual templates signatures will ensure) + public IEnumerable TemplateConstructableTypes(TypeDesc type) + { + // Array types are the only parameterized types that have templates + if (type.IsSzArray && !type.IsArrayTypeWithoutGenericInterfaces()) + { + TypeDesc arrayCanonicalType = type.ConvertToCanonForm(CanonicalFormKind.Specific); + + // Add a dependency on the template for this type, if the canonical type should be generated into this binary. + if (arrayCanonicalType.IsCanonicalSubtype(CanonicalFormKind.Any) && !_factory.NecessaryTypeSymbol(arrayCanonicalType).RepresentsIndirectionCell) + { + yield return _factory.NativeLayout.TemplateTypeLayout(arrayCanonicalType); + } + } + + while (type.IsParameterizedType) + { + type = ((ParameterizedType)type).ParameterType; + } + + TypeDesc canonicalType = type.ConvertToCanonForm(CanonicalFormKind.Specific); + yield return _factory.MaximallyConstructableType(canonicalType); + + // Add a dependency on the template for this type, if the canonical type should be generated into this binary. + if (canonicalType.IsCanonicalSubtype(CanonicalFormKind.Any) && !_factory.NecessaryTypeSymbol(canonicalType).RepresentsIndirectionCell) + { + if (!_factory.TypeSystemContext.IsCanonicalDefinitionType(canonicalType, CanonicalFormKind.Any)) + yield return _factory.NativeLayout.TemplateTypeLayout(canonicalType); + } + + foreach (TypeDesc instantiationType in type.Instantiation) + { + foreach (var dependency in TemplateConstructableTypes(instantiationType)) + yield return dependency; + } + } + + // Produce a set of dependencies that is necessary such that if this type + // needs to be used referenced from a NativeLayout template and any Universal Shared + // instantiation is all that is needed, that the template + // will be properly constructable. (This is done by ensuring that all + // canonical types in the deconstruction of the type are ConstructedEEType instead + // of just necessary, and that the USG variant of the template is created + // (Which is what the actual templates signatures will ensure) + public IEnumerable UniversalTemplateConstructableTypes(TypeDesc type) + { + while (type.IsParameterizedType) + { + type = ((ParameterizedType)type).ParameterType; + } + + if (type.IsSignatureVariable) + yield break; + + TypeDesc canonicalType = type.ConvertToCanonForm(CanonicalFormKind.Universal); + yield return _factory.MaximallyConstructableType(canonicalType); + + foreach (TypeDesc instantiationType in type.Instantiation) + { + foreach (var dependency in UniversalTemplateConstructableTypes(instantiationType)) + yield return dependency; + } + } + + private NodeCache _typeSignatures; + internal NativeLayoutTypeSignatureVertexNode TypeSignatureVertex(TypeDesc type) + { + if (type.IsRuntimeDeterminedType) + { + GenericParameterDesc genericParameter = ((RuntimeDeterminedType)type).RuntimeDeterminedDetailsType; + type = _factory.TypeSystemContext.GetSignatureVariable(genericParameter.Index, method: (genericParameter.Kind == GenericParameterKind.Method)); + } + + if (type.Category == TypeFlags.FunctionPointer) + { + // Pretend for now it's an IntPtr, may need to be revisited depending on https://github.com/dotnet/runtime/issues/11354 + type = _factory.TypeSystemContext.GetWellKnownType(WellKnownType.IntPtr); + } + + return _typeSignatures.GetOrAdd(type); + } + + private NodeCache _methodSignatures; + internal NativeLayoutMethodSignatureVertexNode MethodSignatureVertex(MethodSignature signature) + { + return _methodSignatures.GetOrAdd(signature); + } + + private NodeCache _callingConventionSlots; + internal NativeLayoutCallingConventionConverterGenericDictionarySlotNode CallingConventionConverter(CallingConventionConverterKey key) + { + return _callingConventionSlots.GetOrAdd(key); + } + + private NodeCache _methodNameAndSignatures; + internal NativeLayoutMethodNameAndSignatureVertexNode MethodNameAndSignatureVertex(MethodDesc method) + { + return _methodNameAndSignatures.GetOrAdd(method); + } + + private NodeCache _placedSignatures; + internal NativeLayoutPlacedSignatureVertexNode PlacedSignatureVertex(NativeLayoutVertexNode vertexNode) + { + return _placedSignatures.GetOrAdd(vertexNode); + } + + private struct VertexSequenceKey : IEquatable + { + public readonly List Vertices; + + public VertexSequenceKey(List vertices) + { + Vertices = vertices; + } + + public override bool Equals(object obj) + { + VertexSequenceKey? other = obj as VertexSequenceKey?; + if (other.HasValue) + return Equals(other.Value); + else + return false; + } + + public bool Equals(VertexSequenceKey other) + { + if (other.Vertices.Count != Vertices.Count) + return false; + + for (int i = 0; i < Vertices.Count; i++) + { + if (other.Vertices[i] != Vertices[i]) + return false; + } + + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int _rotl(int value, int shift) + { + // This is expected to be optimized into a single rotl instruction + return (int)(((uint)value << shift) | ((uint)value >> (32 - shift))); + } + + public override int GetHashCode() + { + int hashcode = 0; + foreach (NativeLayoutVertexNode node in Vertices) + { + hashcode ^= node.GetHashCode(); + hashcode = _rotl(hashcode, 5); + } + return hashcode; + } + } + + private NodeCache _placedVertexSequence; + internal NativeLayoutPlacedVertexSequenceVertexNode PlacedVertexSequence(List vertices) + { + return _placedVertexSequence.GetOrAdd(new VertexSequenceKey(vertices)); + } + + class UIntSequenceComparer : IEqualityComparer> + { + bool IEqualityComparer>.Equals(List x, List y) + { + if (x.Count != y.Count) + return false; + + for (int i = 0; i < x.Count; i++) + { + if (x[i] != y[i]) + return false; + } + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int _rotl(int value, int shift) + { + // This is expected to be optimized into a single rotl instruction + return (int)(((uint)value << shift) | ((uint)value >> (32 - shift))); + } + + int IEqualityComparer>.GetHashCode(List obj) + { + int hashcode = 0x42284781; + foreach (uint u in obj) + { + hashcode ^= (int)u; + hashcode = _rotl(hashcode, 5); + } + + return hashcode; + } + } + private NodeCache, NativeLayoutPlacedVertexSequenceOfUIntVertexNode> _placedUIntVertexSequence; + internal NativeLayoutPlacedVertexSequenceOfUIntVertexNode PlacedUIntVertexSequence(List uints) + { + return _placedUIntVertexSequence.GetOrAdd(uints); + } + + private NodeCache _methodLdTokenSignatures; + internal NativeLayoutMethodLdTokenVertexNode MethodLdTokenVertex(MethodDesc method) + { + return _methodLdTokenSignatures.GetOrAdd(method); + } + + private NodeCache _fieldLdTokenSignatures; + internal NativeLayoutFieldLdTokenVertexNode FieldLdTokenVertex(FieldDesc field) + { + return _fieldLdTokenSignatures.GetOrAdd(field); + } + + private struct NativeLayoutSignatureKey : IEquatable + { + public NativeLayoutSignatureKey(NativeLayoutSavedVertexNode signatureVertex, Utf8String identityPrefix, TypeSystemEntity identity) + { + SignatureVertex = signatureVertex; + IdentityPrefix = identityPrefix; + Identity = identity; + } + + public NativeLayoutSavedVertexNode SignatureVertex { get; } + public Utf8String IdentityPrefix { get; } + public TypeSystemEntity Identity { get; } + + public override bool Equals(object obj) + { + if (!(obj is NativeLayoutSignatureKey)) + return false; + + return Equals((NativeLayoutSignatureKey)obj); + } + + public override int GetHashCode() + { + return SignatureVertex.GetHashCode(); + } + + public bool Equals(NativeLayoutSignatureKey other) + { + if (SignatureVertex != other.SignatureVertex) + return false; + + if (!IdentityPrefix.Equals(other.IdentityPrefix)) + return false; + + if (Identity != other.Identity) + return false; + + return true; + } + } + + private NodeCache _nativeLayoutSignatureNodes; + public NativeLayoutSignatureNode NativeLayoutSignature(NativeLayoutSavedVertexNode signature, Utf8String identityPrefix, TypeSystemEntity identity) + { + return _nativeLayoutSignatureNodes.GetOrAdd(new NativeLayoutSignatureKey(signature, identityPrefix, identity)); + } + + private NodeCache _templateMethodEntries; + internal NativeLayoutTemplateMethodSignatureVertexNode TemplateMethodEntry(MethodDesc method) + { + return _templateMethodEntries.GetOrAdd(method); + } + + private NodeCache _templateMethodLayouts; + public NativeLayoutTemplateMethodLayoutVertexNode TemplateMethodLayout(MethodDesc method) + { + return _templateMethodLayouts.GetOrAdd(method); + } + + private NodeCache _templateTypeLayouts; + public NativeLayoutTemplateTypeLayoutVertexNode TemplateTypeLayout(TypeDesc type) + { + return _templateTypeLayouts.GetOrAdd(GenericTypesTemplateMap.ConvertArrayOfTToRegularArray(_factory, type)); + } + + private NodeCache _typeHandle_GenericDictionarySlots; + public NativeLayoutTypeHandleGenericDictionarySlotNode TypeHandleDictionarySlot(TypeDesc type) + { + return _typeHandle_GenericDictionarySlots.GetOrAdd(type); + } + + private NodeCache _gcStatic_GenericDictionarySlots; + public NativeLayoutGcStaticsGenericDictionarySlotNode GcStaticDictionarySlot(TypeDesc type) + { + return _gcStatic_GenericDictionarySlots.GetOrAdd(type); + } + + private NodeCache _nonGcStatic_GenericDictionarySlots; + public NativeLayoutNonGcStaticsGenericDictionarySlotNode NonGcStaticDictionarySlot(TypeDesc type) + { + return _nonGcStatic_GenericDictionarySlots.GetOrAdd(type); + } + + private NodeCache _unwrapNullable_GenericDictionarySlots; + public NativeLayoutUnwrapNullableGenericDictionarySlotNode UnwrapNullableTypeDictionarySlot(TypeDesc type) + { + return _unwrapNullable_GenericDictionarySlots.GetOrAdd(type); + } + + private NodeCache _typeSize_GenericDictionarySlots; + public NativeLayoutTypeSizeGenericDictionarySlotNode TypeSizeDictionarySlot(TypeDesc type) + { + return _typeSize_GenericDictionarySlots.GetOrAdd(type); + } + + private NodeCache _allocateObject_GenericDictionarySlots; + public NativeLayoutAllocateObjectGenericDictionarySlotNode AllocateObjectDictionarySlot(TypeDesc type) + { + return _allocateObject_GenericDictionarySlots.GetOrAdd(type); + } + + private NodeCache _castClass_GenericDictionarySlots; + public NativeLayoutCastClassGenericDictionarySlotNode CastClassDictionarySlot(TypeDesc type) + { + return _castClass_GenericDictionarySlots.GetOrAdd(type); + } + + private NodeCache _isInst_GenericDictionarySlots; + public NativeLayoutIsInstGenericDictionarySlotNode IsInstDictionarySlot(TypeDesc type) + { + return _isInst_GenericDictionarySlots.GetOrAdd(type); + } + + private NodeCache _threadStaticIndex_GenericDictionarySlots; + public NativeLayoutThreadStaticBaseIndexDictionarySlotNode ThreadStaticBaseIndexDictionarySlotNode(TypeDesc type) + { + return _threadStaticIndex_GenericDictionarySlots.GetOrAdd(type); + } + + private NodeCache _defaultConstructor_GenericDictionarySlots; + public NativeLayoutDefaultConstructorGenericDictionarySlotNode DefaultConstructorDictionarySlot(TypeDesc type) + { + return _defaultConstructor_GenericDictionarySlots.GetOrAdd(type); + } + + private NodeCache _allocateArray_GenericDictionarySlots; + public NativeLayoutAllocateArrayGenericDictionarySlotNode AllocateArrayDictionarySlot(TypeDesc type) + { + return _allocateArray_GenericDictionarySlots.GetOrAdd(type); + } + + private NodeCache _interfaceCell_GenericDictionarySlots; + public NativeLayoutInterfaceDispatchGenericDictionarySlotNode InterfaceCellDictionarySlot(MethodDesc method) + { + return _interfaceCell_GenericDictionarySlots.GetOrAdd(method); + } + + private NodeCache _methodDictionary_GenericDictionarySlots; + public NativeLayoutMethodDictionaryGenericDictionarySlotNode MethodDictionaryDictionarySlot(MethodDesc method) + { + return _methodDictionary_GenericDictionarySlots.GetOrAdd(method); + } + + public readonly NativeLayoutNotSupportedDictionarySlotNode NotSupportedDictionarySlot = new NativeLayoutNotSupportedDictionarySlotNode(); + + private struct MethodEntrypointSlotKey : IEquatable + { + public readonly bool Unboxing; + public readonly MethodDesc Method; + public readonly IMethodNode FunctionPointerTarget; + + public MethodEntrypointSlotKey(MethodDesc method, bool unboxing, IMethodNode functionPointerTarget) + { + Unboxing = unboxing; + Method = method; + FunctionPointerTarget = functionPointerTarget; + } + + public override bool Equals(object obj) + { + MethodEntrypointSlotKey? other = obj as MethodEntrypointSlotKey?; + if (other.HasValue) + return Equals(other.Value); + else + return false; + } + + public bool Equals(MethodEntrypointSlotKey other) + { + if (other.Unboxing != Unboxing) + return false; + + if (other.Method != Method) + return false; + + if (other.FunctionPointerTarget != FunctionPointerTarget) + return false; + + return true; + } + + public override int GetHashCode() + { + int hashCode = Method.GetHashCode() ^ (Unboxing ? 1 : 0); + if (FunctionPointerTarget != null) + hashCode ^= FunctionPointerTarget.GetHashCode(); + return hashCode; + } + } + + private NodeCache _methodEntrypoint_GenericDictionarySlots; + public NativeLayoutMethodEntrypointGenericDictionarySlotNode MethodEntrypointDictionarySlot(MethodDesc method, bool unboxing, IMethodNode functionPointerTarget) + { + return _methodEntrypoint_GenericDictionarySlots.GetOrAdd(new MethodEntrypointSlotKey(method, unboxing, functionPointerTarget)); + } + + private NodeCache _fieldLdToken_GenericDictionarySlots; + public NativeLayoutFieldLdTokenGenericDictionarySlotNode FieldLdTokenDictionarySlot(FieldDesc field) + { + return _fieldLdToken_GenericDictionarySlots.GetOrAdd(field); + } + + private NodeCache _methodLdToken_GenericDictionarySlots; + public NativeLayoutMethodLdTokenGenericDictionarySlotNode MethodLdTokenDictionarySlot(MethodDesc method) + { + return _methodLdToken_GenericDictionarySlots.GetOrAdd(method); + } + + private NodeCache _fieldOffset_GenericDictionaryslots; + public NativeLayoutFieldOffsetGenericDictionarySlotNode FieldOffsetDictionarySlot(FieldDesc field) + { + return _fieldOffset_GenericDictionaryslots.GetOrAdd(field); + } + + private NodeCache _vtableOffset_GenericDictionaryslots; + public NativeLayoutVTableOffsetGenericDictionarySlotNode VTableOffsetDictionarySlot(MethodDesc method) + { + return _vtableOffset_GenericDictionaryslots.GetOrAdd(method); + } + + private NodeCache _dictionarySignatures; + public NativeLayoutDictionarySignatureNode DictionarySignature(TypeSystemEntity owningMethodOrType) + { + return _dictionarySignatures.GetOrAdd(owningMethodOrType); + } + + private NodeCache _integerSlots; + public NativeLayoutIntegerDictionarySlotNode IntegerSlot(int value) + { + return _integerSlots.GetOrAdd(value); + } + + private NodeCache _otherSlotPointerSlots; + public NativeLayoutPointerToOtherSlotDictionarySlotNode PointerToOtherSlot(int otherSlotIndex) + { + return _otherSlotPointerSlots.GetOrAdd(otherSlotIndex); + } + + private NodeCache _constrainedMethodUseSlots; + public NativeLayoutConstrainedMethodDictionarySlotNode ConstrainedMethodUse(MethodDesc constrainedMethod, TypeDesc constraintType, bool directCall) + { + return _constrainedMethodUseSlots.GetOrAdd(new ConstrainedMethodUseKey(constrainedMethod, constraintType, directCall)); + } + } + + public NativeLayoutHelper NativeLayout; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs new file mode 100644 index 00000000000000..67469a0f55cc68 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -0,0 +1,1313 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.IL; +using Internal.Runtime; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public abstract partial class NodeFactory + { + private TargetDetails _target; + private CompilerTypeSystemContext _context; + private CompilationModuleGroup _compilationModuleGroup; + private VTableSliceProvider _vtableSliceProvider; + private DictionaryLayoutProvider _dictionaryLayoutProvider; + protected readonly ImportedNodeProvider _importedNodeProvider; + private bool _markingComplete; + + public NodeFactory( + CompilerTypeSystemContext context, + CompilationModuleGroup compilationModuleGroup, + MetadataManager metadataManager, + InteropStubManager interoptStubManager, + NameMangler nameMangler, + LazyGenericsPolicy lazyGenericsPolicy, + VTableSliceProvider vtableSliceProvider, + DictionaryLayoutProvider dictionaryLayoutProvider, + ImportedNodeProvider importedNodeProvider, + PreinitializationManager preinitializationManager) + { + _target = context.Target; + _context = context; + _compilationModuleGroup = compilationModuleGroup; + _vtableSliceProvider = vtableSliceProvider; + _dictionaryLayoutProvider = dictionaryLayoutProvider; + NameMangler = nameMangler; + InteropStubManager = interoptStubManager; + CreateNodeCaches(); + MetadataManager = metadataManager; + LazyGenericsPolicy = lazyGenericsPolicy; + _importedNodeProvider = importedNodeProvider; + InterfaceDispatchCellSection = new InterfaceDispatchCellSectionNode(this); + PreinitializationManager = preinitializationManager; + } + + public void SetMarkingComplete() + { + _markingComplete = true; + } + + public bool MarkingComplete => _markingComplete; + + public TargetDetails Target + { + get + { + return _target; + } + } + + public LazyGenericsPolicy LazyGenericsPolicy { get; } + public CompilationModuleGroup CompilationModuleGroup + { + get + { + return _compilationModuleGroup; + } + } + + public CompilerTypeSystemContext TypeSystemContext + { + get + { + return _context; + } + } + + public MetadataManager MetadataManager + { + get; + } + + public NameMangler NameMangler + { + get; + } + + public PreinitializationManager PreinitializationManager + { + get; + } + + public InteropStubManager InteropStubManager + { + get; + } + + // Temporary workaround that is used to disable certain features from lighting up + // in CppCodegen because they're not fully implemented yet. + public virtual bool IsCppCodegenTemporaryWorkaround + { + get { return false; } + } + + /// + /// Return true if the type is not permitted by the rules of the runtime to have an MethodTable. + /// The implementation here is not intended to be complete, but represents many conditions + /// which make a type ineligible to be an MethodTable. (This function is intended for use in assertions only) + /// + private bool TypeCannotHaveEEType(TypeDesc type) + { + if (!IsCppCodegenTemporaryWorkaround && type.GetTypeDefinition() is INonEmittableType) + return true; + + if (type.IsRuntimeDeterminedSubtype) + return true; + + if (type.IsSignatureVariable) + return true; + + if (type.IsGenericParameter) + return true; + + return false; + } + + protected struct NodeCache + { + private Func _creator; + private ConcurrentDictionary _cache; + + public NodeCache(Func creator, IEqualityComparer comparer) + { + _creator = creator; + _cache = new ConcurrentDictionary(comparer); + } + + public NodeCache(Func creator) + { + _creator = creator; + _cache = new ConcurrentDictionary(); + } + + public TValue GetOrAdd(TKey key) + { + return _cache.GetOrAdd(key, _creator); + } + + public TValue GetOrAdd(TKey key, Func creator) + { + return _cache.GetOrAdd(key, creator); + } + } + + private void CreateNodeCaches() + { + _typeSymbols = new NodeCache(CreateNecessaryTypeNode); + + _constructedTypeSymbols = new NodeCache(CreateConstructedTypeNode); + + _clonedTypeSymbols = new NodeCache((TypeDesc type) => + { + // Only types that reside in other binaries should be cloned + Debug.Assert(_compilationModuleGroup.ShouldReferenceThroughImportTable(type)); + return new ClonedConstructedEETypeNode(this, type); + }); + + _importedTypeSymbols = new NodeCache((TypeDesc type) => + { + Debug.Assert(_compilationModuleGroup.ShouldReferenceThroughImportTable(type)); + return _importedNodeProvider.ImportedEETypeNode(this, type); + }); + + _nonGCStatics = new NodeCache((MetadataType type) => + { + if (_compilationModuleGroup.ContainsType(type) && !_compilationModuleGroup.ShouldReferenceThroughImportTable(type)) + { + return new NonGCStaticsNode(type, PreinitializationManager); + } + else + { + return _importedNodeProvider.ImportedNonGCStaticNode(this, type); + } + }); + + _GCStatics = new NodeCache((MetadataType type) => + { + if (_compilationModuleGroup.ContainsType(type) && !_compilationModuleGroup.ShouldReferenceThroughImportTable(type)) + { + return new GCStaticsNode(type, PreinitializationManager); + } + else + { + return _importedNodeProvider.ImportedGCStaticNode(this, type); + } + }); + + _GCStaticsPreInitDataNodes = new NodeCache((MetadataType type) => + { + ISymbolNode gcStaticsNode = TypeGCStaticsSymbol(type); + Debug.Assert(gcStaticsNode is GCStaticsNode); + return ((GCStaticsNode)gcStaticsNode).NewPreInitDataNode(); + }); + + _GCStaticIndirectionNodes = new NodeCache((MetadataType type) => + { + ISymbolNode gcStaticsNode = TypeGCStaticsSymbol(type); + Debug.Assert(gcStaticsNode is GCStaticsNode); + return GCStaticsRegion.NewNode((GCStaticsNode)gcStaticsNode); + }); + + _threadStatics = new NodeCache(CreateThreadStaticsNode); + + _typeThreadStaticIndices = new NodeCache(type => + { + return new TypeThreadStaticIndexNode(type); + }); + + _GCStaticEETypes = new NodeCache((GCPointerMap gcMap) => + { + return new GCStaticEETypeNode(Target, gcMap); + }); + + _readOnlyDataBlobs = new NodeCache(key => + { + return new BlobNode(key.Name, ObjectNodeSection.ReadOnlyDataSection, key.Data, key.Alignment); + }); + + _uninitializedWritableDataBlobs = new NodeCache(key => + { + return new BlobNode(key.Name, ObjectNodeSection.BssSection, new byte[key.Size], key.Alignment); + }); + + _externSymbols = new NodeCache((string name) => + { + return new ExternSymbolNode(name); + }); + + _pInvokeModuleFixups = new NodeCache((PInvokeModuleData moduleData) => + { + return new PInvokeModuleFixupNode(moduleData); + }); + + _pInvokeMethodFixups = new NodeCache((PInvokeMethodData methodData) => + { + return new PInvokeMethodFixupNode(methodData); + }); + + _methodEntrypoints = new NodeCache(CreateMethodEntrypointNode); + + _tentativeMethodEntrypoints = new NodeCache((MethodDesc method) => + { + IMethodNode entrypoint = MethodEntrypoint(method, unboxingStub: false); + return new TentativeMethodNode(entrypoint is TentativeMethodNode tentative ? + tentative.RealBody : (IMethodBodyNode)entrypoint); + }); + + _unboxingStubs = new NodeCache(CreateUnboxingStubNode); + + _methodAssociatedData = new NodeCache(methodNode => + { + return new MethodAssociatedDataNode(methodNode); + }); + + _fatFunctionPointers = new NodeCache(method => + { + return new FatFunctionPointerNode(method.Method, method.IsUnboxingStub); + }); + + _gvmDependenciesNode = new NodeCache(method => + { + return new GVMDependenciesNode(method); + }); + + _gvmTableEntries = new NodeCache(type => + { + return new TypeGVMEntriesNode(type); + }); + + _dynamicInvokeTemplates = new NodeCache(method => + { + return new DynamicInvokeTemplateNode(method); + }); + + _reflectableMethods = new NodeCache(method => + { + return new ReflectableMethodNode(method); + }); + + _objectGetTypeFlowDependencies = new NodeCache(type => + { + return new ObjectGetTypeFlowDependenciesNode(type); + }); + + _shadowConcreteMethods = new NodeCache(methodKey => + { + MethodDesc canonMethod = methodKey.Method.GetCanonMethodTarget(CanonicalFormKind.Specific); + + if (methodKey.IsUnboxingStub) + { + return new ShadowConcreteUnboxingThunkNode(methodKey.Method, MethodEntrypoint(canonMethod, true)); + } + else + { + return new ShadowConcreteMethodNode(methodKey.Method, MethodEntrypoint(canonMethod)); + } + }); + + _virtMethods = new NodeCache((MethodDesc method) => + { + // We don't need to track virtual method uses for types that have a vtable with a known layout. + // It's a waste of CPU time and memory. + Debug.Assert(!VTable(method.OwningType).HasFixedSlots); + + return new VirtualMethodUseNode(method); + }); + + _variantMethods = new NodeCache((MethodDesc method) => + { + // We don't need to track virtual method uses for types that have a vtable with a known layout. + // It's a waste of CPU time and memory. + Debug.Assert(!VTable(method.OwningType).HasFixedSlots); + + return new VariantInterfaceMethodUseNode(method); + }); + + _readyToRunHelpers = new NodeCache(CreateReadyToRunHelperNode); + + _genericReadyToRunHelpersFromDict = new NodeCache(CreateGenericLookupFromDictionaryNode); + _genericReadyToRunHelpersFromType = new NodeCache(CreateGenericLookupFromTypeNode); + + _frozenStringNodes = new NodeCache((string data) => + { + return new FrozenStringNode(data, Target); + }); + + _frozenObjectNodes = new NodeCache(key => + { + return new FrozenObjectNode(key.Owner, key.SerializableObject); + }); + + _interfaceDispatchCells = new NodeCache(callSiteCell => + { + return new InterfaceDispatchCellNode(callSiteCell.Target, callSiteCell.CallsiteId); + }); + + _interfaceDispatchMaps = new NodeCache((TypeDesc type) => + { + return new InterfaceDispatchMapNode(this, type); + }); + + _sealedVtableNodes = new NodeCache((TypeDesc type) => + { + return new SealedVTableNode(type); + }); + + _runtimeMethodHandles = new NodeCache((MethodDesc method) => + { + return new RuntimeMethodHandleNode(method); + }); + + _runtimeFieldHandles = new NodeCache((FieldDesc field) => + { + return new RuntimeFieldHandleNode(field); + }); + + _dataflowAnalyzedMethods = new NodeCache((MethodILKey il) => + { + return new DataflowAnalyzedMethodNode(il.MethodIL); + }); + + _interfaceDispatchMapIndirectionNodes = new NodeCache((TypeDesc type) => + { + return DispatchMapTable.NewNodeWithSymbol(InterfaceDispatchMap(type)); + }); + + _genericCompositions = new NodeCache((GenericCompositionDetails details) => + { + return new GenericCompositionNode(details); + }); + + _eagerCctorIndirectionNodes = new NodeCache((MethodDesc method) => + { + Debug.Assert(method.IsStaticConstructor); + Debug.Assert(PreinitializationManager.HasEagerStaticConstructor((MetadataType)method.OwningType)); + return EagerCctorTable.NewNode(MethodEntrypoint(method)); + }); + + _delegateMarshalingDataNodes = new NodeCache(type => + { + return new DelegateMarshallingDataNode(type); + }); + + _structMarshalingDataNodes = new NodeCache(type => + { + return new StructMarshallingDataNode(type); + }); + + _vTableNodes = new NodeCache((TypeDesc type ) => + { + if (CompilationModuleGroup.ShouldProduceFullVTable(type)) + return new EagerlyBuiltVTableSliceNode(type); + else + return _vtableSliceProvider.GetSlice(type); + }); + + _methodGenericDictionaries = new NodeCache(method => + { + if (CompilationModuleGroup.ContainsMethodDictionary(method)) + { + return new MethodGenericDictionaryNode(method, this); + } + else + { + return _importedNodeProvider.ImportedMethodDictionaryNode(this, method); + } + }); + + _typeGenericDictionaries = new NodeCache(type => + { + if (CompilationModuleGroup.ContainsTypeDictionary(type)) + { + Debug.Assert(!this.LazyGenericsPolicy.UsesLazyGenerics(type)); + return new TypeGenericDictionaryNode(type, this); + } + else + { + return _importedNodeProvider.ImportedTypeDictionaryNode(this, type); + } + }); + + _typesWithMetadata = new NodeCache(type => + { + return new TypeMetadataNode(type); + }); + + _methodsWithMetadata = new NodeCache(method => + { + return new MethodMetadataNode(method); + }); + + _fieldsWithMetadata = new NodeCache(field => + { + return new FieldMetadataNode(field); + }); + + _modulesWithMetadata = new NodeCache(module => + { + return new ModuleMetadataNode(module); + }); + + _customAttributesWithMetadata = new NodeCache(ca => + { + return new CustomAttributeMetadataNode(ca); + }); + + _genericDictionaryLayouts = new NodeCache(_dictionaryLayoutProvider.GetLayout); + + _stringAllocators = new NodeCache(constructor => + { + return new StringAllocatorMethodNode(constructor); + }); + + NativeLayout = new NativeLayoutHelper(this); + } + + protected virtual ISymbolNode CreateGenericLookupFromDictionaryNode(ReadyToRunGenericHelperKey helperKey) + { + return new ReadyToRunGenericLookupFromDictionaryNode(this, helperKey.HelperId, helperKey.Target, helperKey.DictionaryOwner); + } + + protected virtual ISymbolNode CreateGenericLookupFromTypeNode(ReadyToRunGenericHelperKey helperKey) + { + return new ReadyToRunGenericLookupFromTypeNode(this, helperKey.HelperId, helperKey.Target, helperKey.DictionaryOwner); + } + + protected virtual IEETypeNode CreateNecessaryTypeNode(TypeDesc type) + { + Debug.Assert(!_compilationModuleGroup.ShouldReferenceThroughImportTable(type)); + if (_compilationModuleGroup.ContainsType(type)) + { + if (type.IsGenericDefinition) + { + return new GenericDefinitionEETypeNode(this, type); + } + else if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) + { + return new CanonicalDefinitionEETypeNode(this, type); + } + else if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + return new NecessaryCanonicalEETypeNode(this, type); + } + else + { + return new EETypeNode(this, type); + } + } + else + { + return new ExternEETypeSymbolNode(this, type); + } + } + + protected virtual IEETypeNode CreateConstructedTypeNode(TypeDesc type) + { + // Canonical definition types are *not* constructed types (call NecessaryTypeSymbol to get them) + Debug.Assert(!type.IsCanonicalDefinitionType(CanonicalFormKind.Any)); + Debug.Assert(!_compilationModuleGroup.ShouldReferenceThroughImportTable(type)); + + if (_compilationModuleGroup.ContainsType(type)) + { + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + return new CanonicalEETypeNode(this, type); + } + else + { + return new ConstructedEETypeNode(this, type); + } + } + else + { + return new ExternEETypeSymbolNode(this, type); + } + } + + protected abstract IMethodNode CreateMethodEntrypointNode(MethodDesc method); + + protected abstract IMethodNode CreateUnboxingStubNode(MethodDesc method); + + protected abstract ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall); + + protected virtual ISymbolDefinitionNode CreateThreadStaticsNode(MetadataType type) + { + return new ThreadStaticsNode(type, this); + } + + private NodeCache _typeSymbols; + + public IEETypeNode NecessaryTypeSymbol(TypeDesc type) + { + if (_compilationModuleGroup.ShouldReferenceThroughImportTable(type)) + { + return ImportedEETypeSymbol(type); + } + + if (_compilationModuleGroup.ShouldPromoteToFullType(type)) + { + return ConstructedTypeSymbol(type); + } + + Debug.Assert(!TypeCannotHaveEEType(type)); + + return _typeSymbols.GetOrAdd(type); + } + + private NodeCache _constructedTypeSymbols; + + public IEETypeNode ConstructedTypeSymbol(TypeDesc type) + { + if (_compilationModuleGroup.ShouldReferenceThroughImportTable(type)) + { + return ImportedEETypeSymbol(type); + } + + Debug.Assert(!TypeCannotHaveEEType(type)); + + return _constructedTypeSymbols.GetOrAdd(type); + } + + private NodeCache _clonedTypeSymbols; + + public IEETypeNode MaximallyConstructableType(TypeDesc type) + { + if (ConstructedEETypeNode.CreationAllowed(type)) + return ConstructedTypeSymbol(type); + else + return NecessaryTypeSymbol(type); + } + + public IEETypeNode ConstructedClonedTypeSymbol(TypeDesc type) + { + Debug.Assert(!TypeCannotHaveEEType(type)); + return _clonedTypeSymbols.GetOrAdd(type); + } + + private NodeCache _importedTypeSymbols; + + private IEETypeNode ImportedEETypeSymbol(TypeDesc type) + { + Debug.Assert(_compilationModuleGroup.ShouldReferenceThroughImportTable(type)); + return _importedTypeSymbols.GetOrAdd(type); + } + + private NodeCache _nonGCStatics; + + public ISortableSymbolNode TypeNonGCStaticsSymbol(MetadataType type) + { + Debug.Assert(!TypeCannotHaveEEType(type)); + return _nonGCStatics.GetOrAdd(type); + } + + private NodeCache _GCStatics; + + public ISortableSymbolNode TypeGCStaticsSymbol(MetadataType type) + { + Debug.Assert(!TypeCannotHaveEEType(type)); + return _GCStatics.GetOrAdd(type); + } + + private NodeCache _GCStaticsPreInitDataNodes; + + public GCStaticsPreInitDataNode GCStaticsPreInitDataNode(MetadataType type) + { + return _GCStaticsPreInitDataNodes.GetOrAdd(type); + } + + private NodeCache _GCStaticIndirectionNodes; + + public EmbeddedObjectNode GCStaticIndirection(MetadataType type) + { + return _GCStaticIndirectionNodes.GetOrAdd(type); + } + + private NodeCache _threadStatics; + + public ISymbolDefinitionNode TypeThreadStaticsSymbol(MetadataType type) + { + // This node is always used in the context of its index within the region. + // We should never ask for this if the current compilation doesn't contain the + // associated type. + Debug.Assert(_compilationModuleGroup.ContainsType(type)); + return _threadStatics.GetOrAdd(type); + } + + private NodeCache _typeThreadStaticIndices; + + public ISortableSymbolNode TypeThreadStaticIndex(MetadataType type) + { + if (_compilationModuleGroup.ContainsType(type)) + { + return _typeThreadStaticIndices.GetOrAdd(type); + } + else + { + return ExternSymbol(NameMangler.NodeMangler.ThreadStaticsIndex(type)); + } + } + + private NodeCache _interfaceDispatchCells; + + public InterfaceDispatchCellNode InterfaceDispatchCell(MethodDesc method, string callSite = null) + { + return _interfaceDispatchCells.GetOrAdd(new DispatchCellKey(method, callSite)); + } + + private NodeCache _runtimeMethodHandles; + + public RuntimeMethodHandleNode RuntimeMethodHandle(MethodDesc method) + { + return _runtimeMethodHandles.GetOrAdd(method); + } + + private NodeCache _runtimeFieldHandles; + + public RuntimeFieldHandleNode RuntimeFieldHandle(FieldDesc field) + { + return _runtimeFieldHandles.GetOrAdd(field); + } + + private NodeCache _dataflowAnalyzedMethods; + + public DataflowAnalyzedMethodNode DataflowAnalyzedMethod(MethodIL methodIL) + { + return _dataflowAnalyzedMethods.GetOrAdd(new MethodILKey(methodIL)); + } + + private NodeCache _GCStaticEETypes; + + public ISymbolNode GCStaticEEType(GCPointerMap gcMap) + { + return _GCStaticEETypes.GetOrAdd(gcMap); + } + + private NodeCache _uninitializedWritableDataBlobs; + + public BlobNode UninitializedWritableDataBlob(Utf8String name, int size, int alignment) + { + return _uninitializedWritableDataBlobs.GetOrAdd(new UninitializedWritableDataBlobKey(name, size, alignment)); + } + + private NodeCache _readOnlyDataBlobs; + + public BlobNode ReadOnlyDataBlob(Utf8String name, byte[] blobData, int alignment) + { + return _readOnlyDataBlobs.GetOrAdd(new ReadOnlyDataBlobKey(name, blobData, alignment)); + } + private NodeCache _sealedVtableNodes; + + internal SealedVTableNode SealedVTable(TypeDesc type) + { + return _sealedVtableNodes.GetOrAdd(type); + } + + private NodeCache _interfaceDispatchMaps; + + internal InterfaceDispatchMapNode InterfaceDispatchMap(TypeDesc type) + { + return _interfaceDispatchMaps.GetOrAdd(type); + } + + private NodeCache _interfaceDispatchMapIndirectionNodes; + + public EmbeddedObjectNode InterfaceDispatchMapIndirection(TypeDesc type) + { + return _interfaceDispatchMapIndirectionNodes.GetOrAdd(type); + } + + private NodeCache _genericCompositions; + + internal ISymbolNode GenericComposition(GenericCompositionDetails details) + { + return _genericCompositions.GetOrAdd(details); + } + + private NodeCache _externSymbols; + + public ISortableSymbolNode ExternSymbol(string name) + { + return _externSymbols.GetOrAdd(name); + } + + private NodeCache _pInvokeModuleFixups; + + public ISymbolNode PInvokeModuleFixup(PInvokeModuleData moduleData) + { + return _pInvokeModuleFixups.GetOrAdd(moduleData); + } + + private NodeCache _pInvokeMethodFixups; + + public PInvokeMethodFixupNode PInvokeMethodFixup(PInvokeMethodData methodData) + { + return _pInvokeMethodFixups.GetOrAdd(methodData); + } + + private NodeCache _vTableNodes; + + public VTableSliceNode VTable(TypeDesc type) + { + return _vTableNodes.GetOrAdd(type); + } + + private NodeCache _methodGenericDictionaries; + public ISortableSymbolNode MethodGenericDictionary(MethodDesc method) + { + return _methodGenericDictionaries.GetOrAdd(method); + } + + private NodeCache _typeGenericDictionaries; + public ISortableSymbolNode TypeGenericDictionary(TypeDesc type) + { + return _typeGenericDictionaries.GetOrAdd(type); + } + + private NodeCache _genericDictionaryLayouts; + public DictionaryLayoutNode GenericDictionaryLayout(TypeSystemEntity methodOrType) + { + return _genericDictionaryLayouts.GetOrAdd(methodOrType); + } + + private NodeCache _stringAllocators; + public IMethodNode StringAllocator(MethodDesc stringConstructor) + { + return _stringAllocators.GetOrAdd(stringConstructor); + } + + protected NodeCache _methodEntrypoints; + private NodeCache _unboxingStubs; + private NodeCache _methodAssociatedData; + + public IMethodNode MethodEntrypoint(MethodDesc method, bool unboxingStub = false) + { + if (unboxingStub) + { + return _unboxingStubs.GetOrAdd(method); + } + + return _methodEntrypoints.GetOrAdd(method); + } + + protected NodeCache _tentativeMethodEntrypoints; + + public IMethodNode TentativeMethodEntrypoint(MethodDesc method, bool unboxingStub = false) + { + // Didn't implement unboxing stubs for now. Would need to pass down the flag. + Debug.Assert(!unboxingStub); + return _tentativeMethodEntrypoints.GetOrAdd(method); + } + + public MethodAssociatedDataNode MethodAssociatedData(IMethodNode methodNode) + { + return _methodAssociatedData.GetOrAdd(methodNode); + } + + private NodeCache _fatFunctionPointers; + + public IMethodNode FatFunctionPointer(MethodDesc method, bool isUnboxingStub = false) + { + return _fatFunctionPointers.GetOrAdd(new MethodKey(method, isUnboxingStub)); + } + + public IMethodNode ExactCallableAddress(MethodDesc method, bool isUnboxingStub = false) + { + MethodDesc canonMethod = method.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (method != canonMethod) + return FatFunctionPointer(method, isUnboxingStub); + else + return MethodEntrypoint(method, isUnboxingStub); + } + + public IMethodNode CanonicalEntrypoint(MethodDesc method, bool isUnboxingStub = false) + { + MethodDesc canonMethod = method.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (method != canonMethod) + return ShadowConcreteMethod(method, isUnboxingStub); + else + return MethodEntrypoint(method, isUnboxingStub); + } + + private NodeCache _gvmDependenciesNode; + public GVMDependenciesNode GVMDependencies(MethodDesc method) + { + return _gvmDependenciesNode.GetOrAdd(method); + } + + private NodeCache _gvmTableEntries; + internal TypeGVMEntriesNode TypeGVMEntries(TypeDesc type) + { + return _gvmTableEntries.GetOrAdd(type); + } + + private NodeCache _reflectableMethods; + public ReflectableMethodNode ReflectableMethod(MethodDesc method) + { + return _reflectableMethods.GetOrAdd(method); + } + + private NodeCache _objectGetTypeFlowDependencies; + internal ObjectGetTypeFlowDependenciesNode ObjectGetTypeFlowDependencies(MetadataType type) + { + return _objectGetTypeFlowDependencies.GetOrAdd(type); + } + + private NodeCache _dynamicInvokeTemplates; + internal DynamicInvokeTemplateNode DynamicInvokeTemplate(MethodDesc method) + { + return _dynamicInvokeTemplates.GetOrAdd(method); + } + + private NodeCache _shadowConcreteMethods; + public IMethodNode ShadowConcreteMethod(MethodDesc method, bool isUnboxingStub = false) + { + return _shadowConcreteMethods.GetOrAdd(new MethodKey(method, isUnboxingStub)); + } + + private static readonly string[][] s_helperEntrypointNames = new string[][] { + new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnGCStaticBase" }, + new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnNonGCStaticBase" }, + new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnThreadStaticBase" }, + new string[] { "Internal.Runtime", "ThreadStatics", "GetThreadStaticBaseForType" } + }; + + private ISymbolNode[] _helperEntrypointSymbols; + + public ISymbolNode HelperEntrypoint(HelperEntrypoint entrypoint) + { + if (_helperEntrypointSymbols == null) + _helperEntrypointSymbols = new ISymbolNode[s_helperEntrypointNames.Length]; + + int index = (int)entrypoint; + + ISymbolNode symbol = _helperEntrypointSymbols[index]; + if (symbol == null) + { + var entry = s_helperEntrypointNames[index]; + + var type = _context.SystemModule.GetKnownType(entry[0], entry[1]); + var method = type.GetKnownMethod(entry[2], null); + + symbol = MethodEntrypoint(method); + + _helperEntrypointSymbols[index] = symbol; + } + return symbol; + } + + private MetadataType _systemArrayOfTClass; + public MetadataType ArrayOfTClass + { + get + { + if (_systemArrayOfTClass == null) + { + _systemArrayOfTClass = _context.SystemModule.GetKnownType("System", "Array`1"); + } + return _systemArrayOfTClass; + } + } + + private TypeDesc _systemArrayOfTEnumeratorType; + public TypeDesc ArrayOfTEnumeratorType + { + get + { + if (_systemArrayOfTEnumeratorType == null) + { + _systemArrayOfTEnumeratorType = ArrayOfTClass.GetNestedType("ArrayEnumerator"); + } + return _systemArrayOfTEnumeratorType; + } + } + + private MethodDesc _instanceMethodRemovedHelper; + public MethodDesc InstanceMethodRemovedHelper + { + get + { + if (_instanceMethodRemovedHelper == null) + { + // This helper is optional, but it's fine for this cache to be ineffective if that happens. + // Those scenarios are rare and typically deal with small compilations. + _instanceMethodRemovedHelper = TypeSystemContext.GetOptionalHelperEntryPoint("ThrowHelpers", "ThrowInstanceBodyRemoved"); + } + + return _instanceMethodRemovedHelper; + } + } + + private NodeCache _virtMethods; + + public DependencyNodeCore VirtualMethodUse(MethodDesc decl) + { + return _virtMethods.GetOrAdd(decl); + } + + private NodeCache _variantMethods; + + public DependencyNodeCore VariantInterfaceMethodUse(MethodDesc decl) + { + return _variantMethods.GetOrAdd(decl); + } + + private NodeCache _readyToRunHelpers; + + public ISymbolNode ReadyToRunHelper(ReadyToRunHelperId id, Object target) + { + return _readyToRunHelpers.GetOrAdd(new ReadyToRunHelperKey(id, target)); + } + + private NodeCache _genericReadyToRunHelpersFromDict; + + public ISymbolNode ReadyToRunHelperFromDictionaryLookup(ReadyToRunHelperId id, Object target, TypeSystemEntity dictionaryOwner) + { + return _genericReadyToRunHelpersFromDict.GetOrAdd(new ReadyToRunGenericHelperKey(id, target, dictionaryOwner)); + } + + private NodeCache _genericReadyToRunHelpersFromType; + + public ISymbolNode ReadyToRunHelperFromTypeLookup(ReadyToRunHelperId id, Object target, TypeSystemEntity dictionaryOwner) + { + return _genericReadyToRunHelpersFromType.GetOrAdd(new ReadyToRunGenericHelperKey(id, target, dictionaryOwner)); + } + + private NodeCache _typesWithMetadata; + + internal TypeMetadataNode TypeMetadata(MetadataType type) + { + // These are only meaningful for UsageBasedMetadataManager. We should not have them + // in the dependency graph otherwise. + Debug.Assert(MetadataManager is UsageBasedMetadataManager); + return _typesWithMetadata.GetOrAdd(type); + } + + private NodeCache _methodsWithMetadata; + + internal MethodMetadataNode MethodMetadata(MethodDesc method) + { + // These are only meaningful for UsageBasedMetadataManager. We should not have them + // in the dependency graph otherwise. + Debug.Assert(MetadataManager is UsageBasedMetadataManager); + return _methodsWithMetadata.GetOrAdd(method); + } + + private NodeCache _fieldsWithMetadata; + + internal FieldMetadataNode FieldMetadata(FieldDesc field) + { + // These are only meaningful for UsageBasedMetadataManager. We should not have them + // in the dependency graph otherwise. + Debug.Assert(MetadataManager is UsageBasedMetadataManager); + return _fieldsWithMetadata.GetOrAdd(field); + } + + private NodeCache _modulesWithMetadata; + + internal ModuleMetadataNode ModuleMetadata(ModuleDesc module) + { + // These are only meaningful for UsageBasedMetadataManager. We should not have them + // in the dependency graph otherwise. + Debug.Assert(MetadataManager is UsageBasedMetadataManager); + return _modulesWithMetadata.GetOrAdd(module); + } + + private NodeCache _customAttributesWithMetadata; + + internal CustomAttributeMetadataNode CustomAttributeMetadata(ReflectableCustomAttribute ca) + { + // These are only meaningful for UsageBasedMetadataManager. We should not have them + // in the dependency graph otherwise. + Debug.Assert(MetadataManager is UsageBasedMetadataManager); + return _customAttributesWithMetadata.GetOrAdd(ca); + } + + private NodeCache _frozenStringNodes; + + public FrozenStringNode SerializedStringObject(string data) + { + return _frozenStringNodes.GetOrAdd(data); + } + + private NodeCache _frozenObjectNodes; + + public FrozenObjectNode SerializedFrozenObject(FieldDesc owningField, TypePreinit.ISerializableReference data) + { + return _frozenObjectNodes.GetOrAdd(new SerializedFrozenObjectKey(owningField, data)); + } + + private NodeCache _eagerCctorIndirectionNodes; + + public EmbeddedObjectNode EagerCctorIndirection(MethodDesc cctorMethod) + { + return _eagerCctorIndirectionNodes.GetOrAdd(cctorMethod); + } + + public ISymbolNode ConstantUtf8String(string str) + { + int stringBytesCount = Encoding.UTF8.GetByteCount(str); + byte[] stringBytes = new byte[stringBytesCount + 1]; + Encoding.UTF8.GetBytes(str, 0, str.Length, stringBytes, 0); + + string symbolName = "__utf8str_" + NameMangler.GetMangledStringName(str); + + return ReadOnlyDataBlob(symbolName, stringBytes, 1); + } + + private NodeCache _delegateMarshalingDataNodes; + + public DelegateMarshallingDataNode DelegateMarshallingData(DefType type) + { + return _delegateMarshalingDataNodes.GetOrAdd(type); + } + + private NodeCache _structMarshalingDataNodes; + + public StructMarshallingDataNode StructMarshallingData(DefType type) + { + return _structMarshalingDataNodes.GetOrAdd(type); + } + + /// + /// Returns alternative symbol name that object writer should produce for given symbols + /// in addition to the regular one. + /// + public string GetSymbolAlternateName(ISymbolNode node) + { + string value; + if (!NodeAliases.TryGetValue(node, out value)) + return null; + return value; + } + + public ArrayOfEmbeddedPointersNode GCStaticsRegion = new ArrayOfEmbeddedPointersNode( + "__GCStaticRegionStart", + "__GCStaticRegionEnd", + new SortableDependencyNode.ObjectNodeComparer(new CompilerComparer())); + + public ArrayOfEmbeddedDataNode ThreadStaticsRegion = new ArrayOfEmbeddedDataNode( + "__ThreadStaticRegionStart", + "__ThreadStaticRegionEnd", + new SortableDependencyNode.EmbeddedObjectNodeComparer(new CompilerComparer())); + + public ArrayOfEmbeddedPointersNode EagerCctorTable = new ArrayOfEmbeddedPointersNode( + "__EagerCctorStart", + "__EagerCctorEnd", + null); + + public ArrayOfEmbeddedPointersNode DispatchMapTable = new ArrayOfEmbeddedPointersNode( + "__DispatchMapTableStart", + "__DispatchMapTableEnd", + new SortableDependencyNode.ObjectNodeComparer(new CompilerComparer())); + + public ArrayOfEmbeddedDataNode FrozenSegmentRegion = new ArrayOfFrozenObjectsNode( + "__FrozenSegmentRegionStart", + "__FrozenSegmentRegionEnd", + new SortableDependencyNode.EmbeddedObjectNodeComparer(new CompilerComparer())); + + internal ModuleInitializerListNode ModuleInitializerList = new ModuleInitializerListNode(); + + public InterfaceDispatchCellSectionNode InterfaceDispatchCellSection { get; } + + public ReadyToRunHeaderNode ReadyToRunHeader; + + public Dictionary NodeAliases = new Dictionary(); + + protected internal TypeManagerIndirectionNode TypeManagerIndirection = new TypeManagerIndirectionNode(); + + public virtual void AttachToDependencyGraph(DependencyAnalyzerBase graph) + { + ReadyToRunHeader = new ReadyToRunHeaderNode(Target); + + graph.AddRoot(ReadyToRunHeader, "ReadyToRunHeader is always generated"); + graph.AddRoot(new ModulesSectionNode(Target), "ModulesSection is always generated"); + + graph.AddRoot(GCStaticsRegion, "GC StaticsRegion is always generated"); + graph.AddRoot(ThreadStaticsRegion, "ThreadStaticsRegion is always generated"); + graph.AddRoot(EagerCctorTable, "EagerCctorTable is always generated"); + graph.AddRoot(TypeManagerIndirection, "TypeManagerIndirection is always generated"); + graph.AddRoot(DispatchMapTable, "DispatchMapTable is always generated"); + graph.AddRoot(FrozenSegmentRegion, "FrozenSegmentRegion is always generated"); + graph.AddRoot(InterfaceDispatchCellSection, "Interface dispatch cell section is always generated"); + graph.AddRoot(ModuleInitializerList, "Module initializer list is always generated"); + + ReadyToRunHeader.Add(ReadyToRunSectionType.GCStaticRegion, GCStaticsRegion, GCStaticsRegion.StartSymbol, GCStaticsRegion.EndSymbol); + ReadyToRunHeader.Add(ReadyToRunSectionType.ThreadStaticRegion, ThreadStaticsRegion, ThreadStaticsRegion.StartSymbol, ThreadStaticsRegion.EndSymbol); + ReadyToRunHeader.Add(ReadyToRunSectionType.EagerCctor, EagerCctorTable, EagerCctorTable.StartSymbol, EagerCctorTable.EndSymbol); + ReadyToRunHeader.Add(ReadyToRunSectionType.TypeManagerIndirection, TypeManagerIndirection, TypeManagerIndirection); + ReadyToRunHeader.Add(ReadyToRunSectionType.InterfaceDispatchTable, DispatchMapTable, DispatchMapTable.StartSymbol); + ReadyToRunHeader.Add(ReadyToRunSectionType.FrozenObjectRegion, FrozenSegmentRegion, FrozenSegmentRegion.StartSymbol, FrozenSegmentRegion.EndSymbol); + ReadyToRunHeader.Add(ReadyToRunSectionType.ModuleInitializerList, ModuleInitializerList, ModuleInitializerList, ModuleInitializerList.EndSymbol); + + var commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable", this); + InteropStubManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); + MetadataManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); + MetadataManager.AttachToDependencyGraph(graph); + ReadyToRunHeader.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode, commonFixupsTableNode, commonFixupsTableNode.EndSymbol); + } + + protected struct MethodKey : IEquatable + { + public readonly MethodDesc Method; + public readonly bool IsUnboxingStub; + + public MethodKey(MethodDesc method, bool isUnboxingStub) + { + Method = method; + IsUnboxingStub = isUnboxingStub; + } + + public bool Equals(MethodKey other) => Method == other.Method && IsUnboxingStub == other.IsUnboxingStub; + public override bool Equals(object obj) => obj is MethodKey && Equals((MethodKey)obj); + public override int GetHashCode() => Method.GetHashCode(); + } + + protected struct ReadyToRunHelperKey : IEquatable + { + public readonly object Target; + public readonly ReadyToRunHelperId HelperId; + + public ReadyToRunHelperKey(ReadyToRunHelperId helperId, object target) + { + HelperId = helperId; + Target = target; + } + + public bool Equals(ReadyToRunHelperKey other) => HelperId == other.HelperId && Target.Equals(other.Target); + public override bool Equals(object obj) => obj is ReadyToRunHelperKey && Equals((ReadyToRunHelperKey)obj); + public override int GetHashCode() + { + int hashCode = (int)HelperId * 0x5498341 + 0x832424; + hashCode = hashCode * 23 + Target.GetHashCode(); + return hashCode; + } + } + + protected struct ReadyToRunGenericHelperKey : IEquatable + { + public readonly object Target; + public readonly TypeSystemEntity DictionaryOwner; + public readonly ReadyToRunHelperId HelperId; + + public ReadyToRunGenericHelperKey(ReadyToRunHelperId helperId, object target, TypeSystemEntity dictionaryOwner) + { + HelperId = helperId; + Target = target; + DictionaryOwner = dictionaryOwner; + } + + public bool Equals(ReadyToRunGenericHelperKey other) + => HelperId == other.HelperId && DictionaryOwner == other.DictionaryOwner && Target.Equals(other.Target); + public override bool Equals(object obj) => obj is ReadyToRunGenericHelperKey && Equals((ReadyToRunGenericHelperKey)obj); + public override int GetHashCode() + { + int hashCode = (int)HelperId * 0x5498341 + 0x832424; + hashCode = hashCode * 23 + Target.GetHashCode(); + hashCode = hashCode * 23 + DictionaryOwner.GetHashCode(); + return hashCode; + } + } + + protected struct DispatchCellKey : IEquatable + { + public readonly MethodDesc Target; + public readonly string CallsiteId; + + public DispatchCellKey(MethodDesc target, string callsiteId) + { + Target = target; + CallsiteId = callsiteId; + } + + public bool Equals(DispatchCellKey other) => Target == other.Target && CallsiteId == other.CallsiteId; + public override bool Equals(object obj) => obj is DispatchCellKey && Equals((DispatchCellKey)obj); + public override int GetHashCode() + { + int hashCode = Target.GetHashCode(); + if (CallsiteId != null) + hashCode = hashCode * 23 + CallsiteId.GetHashCode(); + return hashCode; + } + } + + protected struct ReadOnlyDataBlobKey : IEquatable + { + public readonly Utf8String Name; + public readonly byte[] Data; + public readonly int Alignment; + + public ReadOnlyDataBlobKey(Utf8String name, byte[] data, int alignment) + { + Name = name; + Data = data; + Alignment = alignment; + } + + // The assumption here is that the name of the blob is unique. + // We can't emit two blobs with the same name and different contents. + // The name is part of the symbolic name and we don't do any mangling on it. + public bool Equals(ReadOnlyDataBlobKey other) => Name.Equals(other.Name); + public override bool Equals(object obj) => obj is ReadOnlyDataBlobKey && Equals((ReadOnlyDataBlobKey)obj); + public override int GetHashCode() => Name.GetHashCode(); + } + + protected struct UninitializedWritableDataBlobKey : IEquatable + { + public readonly Utf8String Name; + public readonly int Size; + public readonly int Alignment; + + public UninitializedWritableDataBlobKey(Utf8String name, int size, int alignment) + { + Name = name; + Size = size; + Alignment = alignment; + } + + // The assumption here is that the name of the blob is unique. + // We can't emit two blobs with the same name and different contents. + // The name is part of the symbolic name and we don't do any mangling on it. + public bool Equals(UninitializedWritableDataBlobKey other) => Name.Equals(other.Name); + public override bool Equals(object obj) => obj is UninitializedWritableDataBlobKey && Equals((UninitializedWritableDataBlobKey)obj); + public override int GetHashCode() => Name.GetHashCode(); + } + + protected struct SerializedFrozenObjectKey : IEquatable + { + public readonly FieldDesc Owner; + public readonly TypePreinit.ISerializableReference SerializableObject; + + public SerializedFrozenObjectKey(FieldDesc owner, TypePreinit.ISerializableReference obj) + { + Owner = owner; + SerializableObject = obj; + } + + public override bool Equals(object obj) => obj is SerializedFrozenObjectKey && Equals((SerializedFrozenObjectKey)obj); + public bool Equals(SerializedFrozenObjectKey other) => Owner == other.Owner; + public override int GetHashCode() => Owner.GetHashCode(); + } + + private struct MethodILKey : IEquatable + { + public readonly MethodIL MethodIL; + + public MethodILKey(MethodIL methodIL) => MethodIL = methodIL; + public override bool Equals(object obj) => obj is MethodILKey other && Equals(other); + public bool Equals(MethodILKey other) => other.MethodIL.OwningMethod == this.MethodIL.OwningMethod; + public override int GetHashCode() => MethodIL.OwningMethod.GetHashCode(); + + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NonGCStaticsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NonGCStaticsNode.cs new file mode 100644 index 00000000000000..2e52019a1e1532 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NonGCStaticsNode.cs @@ -0,0 +1,194 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a node with non-GC static data associated with a type, along + /// with it's class constructor context. The non-GC static data region shall be prefixed + /// with the class constructor context if the type has a class constructor that + /// needs to be triggered before the type members can be accessed. + /// + public class NonGCStaticsNode : ObjectNode, ISymbolDefinitionNode, ISortableSymbolNode + { + private readonly MetadataType _type; + private readonly PreinitializationManager _preinitializationManager; + + public NonGCStaticsNode(MetadataType type, PreinitializationManager preinitializationManager) + { + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific)); + _type = type; + _preinitializationManager = preinitializationManager; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectNodeSection Section + { + get + { + if (_preinitializationManager.HasLazyStaticConstructor(_type) + || _preinitializationManager.IsPreinitialized(_type)) + { + // We have data to be emitted so this needs to be in an initialized data section + return ObjectNodeSection.DataSection; + } + else + { + // This is all zeros; place this to the BSS section + return ObjectNodeSection.BssSection; + } + } + } + + public static string GetMangledName(TypeDesc type, NameMangler nameMangler) + { + return nameMangler.NodeMangler.NonGCStatics(type); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.NodeMangler.NonGCStatics(_type)); + } + + int ISymbolNode.Offset => 0; + + int ISymbolDefinitionNode.Offset + { + get + { + // Make sure the NonGCStatics symbol always points to the beginning of the data. + if (_preinitializationManager.HasLazyStaticConstructor(_type)) + { + return GetClassConstructorContextStorageSize(_type.Context.Target, _type); + } + else + { + return 0; + } + } + } + + public bool HasCCtorContext => _preinitializationManager.HasLazyStaticConstructor(_type); + + public override bool IsShareable => EETypeNode.IsTypeNodeShareable(_type); + + public MetadataType Type => _type; + + public static int GetClassConstructorContextSize(TargetDetails target) + { + // TODO: Assert that StaticClassConstructionContext type has the expected size + // (need to make it a well known type?) + return target.PointerSize * 2; + } + + private static int GetClassConstructorContextStorageSize(TargetDetails target, MetadataType type) + { + int alignmentRequired = Math.Max(type.NonGCStaticFieldAlignment.AsInt, GetClassConstructorContextAlignment(target)); + return AlignmentHelper.AlignUp(GetClassConstructorContextSize(type.Context.Target), alignmentRequired); + } + + private static int GetClassConstructorContextAlignment(TargetDetails target) + { + // TODO: Assert that StaticClassConstructionContext type has the expected alignment + // (need to make it a well known type?) + return target.PointerSize; + } + + public override bool StaticDependenciesAreComputed => true; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencyList = new DependencyList(); + + if (factory.PreinitializationManager.HasEagerStaticConstructor(_type)) + { + dependencyList.Add(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor"); + } + + if (_type.Module.GetGlobalModuleType().GetStaticConstructor() is MethodDesc moduleCctor) + { + dependencyList.Add(factory.MethodEntrypoint(moduleCctor), "Static base in a module with initializer"); + } + + EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref dependencyList); + + return dependencyList; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + + // If the type has a class constructor, its non-GC statics section is prefixed + // by System.Runtime.CompilerServices.StaticClassConstructionContext struct. + if (factory.PreinitializationManager.HasLazyStaticConstructor(_type)) + { + int alignmentRequired = Math.Max(_type.NonGCStaticFieldAlignment.AsInt, GetClassConstructorContextAlignment(_type.Context.Target)); + int classConstructorContextStorageSize = GetClassConstructorContextStorageSize(factory.Target, _type); + builder.RequireInitialAlignment(alignmentRequired); + + Debug.Assert(classConstructorContextStorageSize >= GetClassConstructorContextSize(_type.Context.Target)); + + // Add padding before the context if alignment forces us to do so + builder.EmitZeros(classConstructorContextStorageSize - GetClassConstructorContextSize(_type.Context.Target)); + + // Emit the actual StaticClassConstructionContext + MethodDesc cctorMethod = _type.GetStaticConstructor(); + builder.EmitPointerReloc(factory.ExactCallableAddress(cctorMethod)); + builder.EmitZeroPointer(); + } + else + { + builder.RequireInitialAlignment(_type.NonGCStaticFieldAlignment.AsInt); + } + + if (_preinitializationManager.IsPreinitialized(_type)) + { + TypePreinit.PreinitializationInfo preinitInfo = _preinitializationManager.GetPreinitializationInfo(_type); + int initialOffset = builder.CountBytes; + foreach (FieldDesc field in _type.GetFields()) + { + if (!field.IsStatic || field.HasRva || field.IsLiteral || field.IsThreadStatic || field.HasGCStaticBase) + continue; + + int padding = field.Offset.AsInt - builder.CountBytes + initialOffset; + Debug.Assert(padding >= 0); + builder.EmitZeros(padding); + + TypePreinit.ISerializableValue val = preinitInfo.GetFieldValue(field); + int currentOffset = builder.CountBytes; + val.WriteFieldData(ref builder, field, factory); + Debug.Assert(builder.CountBytes - currentOffset == field.FieldType.GetElementSize().AsInt); + } + + int pad = _type.NonGCStaticFieldSize.AsInt - builder.CountBytes + initialOffset; + Debug.Assert(pad >= 0); + builder.EmitZeros(pad); + } + else + { + builder.EmitZeros(_type.NonGCStaticFieldSize.AsInt); + } + + builder.AddSymbol(this); + + return builder.ToObjectData(); + } + + public override int ClassCode => -1173104872; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((NonGCStaticsNode)other)._type); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectGetTypeFlowDependenciesNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectGetTypeFlowDependenciesNode.cs new file mode 100644 index 00000000000000..e179848db092d3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectGetTypeFlowDependenciesNode.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents dataflow dependencies from a call to Object.GetType on an instance statically + /// typed as the given type. + /// + internal class ObjectGetTypeFlowDependenciesNode : DependencyNodeCore + { + private readonly MetadataType _type; + + public ObjectGetTypeFlowDependenciesNode(MetadataType type) + { + _type = type; + } + + protected override string GetName(NodeFactory factory) + { + return $"Object.GetType dependencies called on {_type}"; + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + var mdManager = (UsageBasedMetadataManager)factory.MetadataManager; + + // We don't mark any members on interfaces - these nodes are only used as conditional dependencies + // of other nodes. Calling `object.GetType()` on something typed as an interface will return + // something that implements the interface, not the interface itself. We're not reflecting on the + // interface. + if (_type.IsInterface) + return Array.Empty(); + + return Dataflow.ReflectionMethodBodyScanner.ProcessTypeGetTypeDataflow(factory, mdManager.FlowAnnotations, mdManager.Logger, _type); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs new file mode 100644 index 00000000000000..e690b5d420078e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs @@ -0,0 +1,1323 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Diagnostics; +using System.Runtime.InteropServices; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; +using Internal.JitInterface; +using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Object writer using src/Native/ObjWriter + /// + public class ObjectWriter : IDisposable, ITypesDebugInfoWriter + { + private readonly ObjectWritingOptions _options; + + // This is used to build mangled names + private Utf8StringBuilder _sb = new Utf8StringBuilder(); + + // This is used to look up file id for the given file name. + // This is a global table across nodes. + private Dictionary _debugFileToId = new Dictionary(); + + // Track offsets in node data that prevent writing all bytes in one single blob. This includes + // relocs, symbol definitions, debug data that must be streamed out using the existing LLVM API + private bool[] _byteInterruptionOffsets; + // This is used to look up DebugLocInfo for the given native offset. + // This is for individual node and should be flushed once node is emitted. + private Dictionary _offsetToDebugLoc = new Dictionary(); + + // Code offset to defined names + private Dictionary> _offsetToDefName = new Dictionary>(); + + // Code offset to Cfi blobs + private Dictionary> _offsetToCfis = new Dictionary>(); + // Code offset to Lsda label index + private Dictionary _offsetToCfiLsdaBlobName = new Dictionary(); + // Code offsets that starts a frame + private HashSet _offsetToCfiStart = new HashSet(); + // Code offsets that ends a frame + private HashSet _offsetToCfiEnd = new HashSet(); + // Used to assert whether frames are not overlapped. + private bool _frameOpened; + + // The size of CFI_CODE that RyuJit passes. + private const int CfiCodeSize = 8; + + // The section for the current node being processed. + private ObjectNodeSection _currentSection; + + // The first defined symbol name of the current node being processed. + private Utf8String _currentNodeZeroTerminatedName; + + private const string NativeObjectWriterFileName = "objwriter"; + + // Target platform ObjectWriter is instantiated for. + private TargetDetails _targetPlatform; + + // Nodefactory for which ObjectWriter is instantiated for. + private NodeFactory _nodeFactory; + private readonly bool _isSingleFileCompilation; + + // Unix section containing LSDA data, like EH Info and GC Info + public static readonly ObjectNodeSection LsdaSection = new ObjectNodeSection(".corert_eh_table", SectionType.ReadOnly); + + private UserDefinedTypeDescriptor _userDefinedTypeDescriptor; + +#if DEBUG + static Dictionary _previouslyWrittenNodeNames = new Dictionary(); +#endif + + [DllImport(NativeObjectWriterFileName)] + private static extern IntPtr InitObjWriter(string objectFilePath, string triple = null); + + [DllImport(NativeObjectWriterFileName)] + private static extern void FinishObjWriter(IntPtr objWriter); + + [DllImport(NativeObjectWriterFileName)] + private static extern void SwitchSection(IntPtr objWriter, string sectionName, CustomSectionAttributes attributes = 0, string comdatName = null); + + public void SetSection(ObjectNodeSection section) + { + if (!section.IsStandardSection) + { + SwitchSection(_nativeObjectWriter, section.Name, GetCustomSectionAttributes(section), section.ComdatName); + } + else + { + SwitchSection(_nativeObjectWriter, section.Name); + } + + _currentSection = section; + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void SetCodeSectionAttribute(IntPtr objWriter, string sectionName, CustomSectionAttributes attributes = 0, string comdatName = null); + + public void SetCodeSectionAttribute(ObjectNodeSection section) + { + if (!section.IsStandardSection) + { + SetCodeSectionAttribute(_nativeObjectWriter, section.Name, GetCustomSectionAttributes(section), section.ComdatName); + } + else + { + SetCodeSectionAttribute(_nativeObjectWriter, section.Name); + } + } + + public void EnsureCurrentSection() + { + SetSection(_currentSection); + } + + [Flags] + public enum CustomSectionAttributes + { + ReadOnly = 0x0000, + Writeable = 0x0001, + Executable = 0x0002, + }; + + /// + /// Builds a set of CustomSectionAttributes flags from an ObjectNodeSection. + /// + private CustomSectionAttributes GetCustomSectionAttributes(ObjectNodeSection section) + { + CustomSectionAttributes attributes = 0; + + switch (section.Type) + { + case SectionType.Executable: + attributes |= CustomSectionAttributes.Executable; + break; + case SectionType.ReadOnly: + attributes |= CustomSectionAttributes.ReadOnly; + break; + case SectionType.Writeable: + attributes |= CustomSectionAttributes.Writeable; + break; + } + + return attributes; + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitAlignment(IntPtr objWriter, int byteAlignment); + public void EmitAlignment(int byteAlignment) + { + EmitAlignment(_nativeObjectWriter, byteAlignment); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitBlob(IntPtr objWriter, int blobSize, byte[] blob); + public void EmitBlob(byte[] blob) + { + EmitBlob(_nativeObjectWriter, blob.Length, blob); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitIntValue(IntPtr objWriter, ulong value, int size); + public void EmitIntValue(ulong value, int size) + { + EmitIntValue(_nativeObjectWriter, value, size); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitBlob(IntPtr objWriter, int blobSize, IntPtr blob); + public void EmitBytes(IntPtr pArray, int length) + { + EmitBlob(_nativeObjectWriter, length, pArray); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitSymbolDef(IntPtr objWriter, byte[] symbolName, int global); + public void EmitSymbolDef(byte[] symbolName, bool global = false) + { + EmitSymbolDef(_nativeObjectWriter, symbolName, global ? 1 : 0); + } + public void EmitSymbolDef(Utf8StringBuilder symbolName, bool global = false) + { + EmitSymbolDef(_nativeObjectWriter, symbolName.Append('\0').UnderlyingArray, global ? 1 : 0); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern int EmitSymbolRef(IntPtr objWriter, byte[] symbolName, RelocType relocType, int delta, SymbolRefFlags flags); + private int EmitSymbolRef(Utf8StringBuilder symbolName, RelocType relocType, int delta = 0, SymbolRefFlags flags = 0) + { + return EmitSymbolRef(_nativeObjectWriter, symbolName.Append('\0').UnderlyingArray, relocType, delta, flags); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitWinFrameInfo(IntPtr objWriter, byte[] methodName, int startOffset, int endOffset, + byte[] blobSymbolName); + public void EmitWinFrameInfo(int startOffset, int endOffset, int blobSize, byte[] blobSymbolName) + { + EmitWinFrameInfo(_nativeObjectWriter, _currentNodeZeroTerminatedName.UnderlyingArray, startOffset, endOffset, blobSymbolName); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitCFIStart(IntPtr objWriter, int nativeOffset); + public void EmitCFIStart(int nativeOffset) + { + Debug.Assert(!_frameOpened); + EmitCFIStart(_nativeObjectWriter, nativeOffset); + _frameOpened = true; + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitCFIEnd(IntPtr objWriter, int nativeOffset); + public void EmitCFIEnd(int nativeOffset) + { + Debug.Assert(_frameOpened); + EmitCFIEnd(_nativeObjectWriter, nativeOffset); + _frameOpened = false; + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitCFILsda(IntPtr objWriter, byte[] blobSymbolName); + public void EmitCFILsda(byte[] blobSymbolName) + { + Debug.Assert(_frameOpened); + EmitCFILsda(_nativeObjectWriter, blobSymbolName); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitCFICode(IntPtr objWriter, int nativeOffset, byte[] blob); + public void EmitCFICode(int nativeOffset, byte[] blob) + { + Debug.Assert(_frameOpened); + EmitCFICode(_nativeObjectWriter, nativeOffset, blob); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitDebugFileInfo(IntPtr objWriter, int fileId, string fileName); + public void EmitDebugFileInfo(int fileId, string fileName) + { + EmitDebugFileInfo(_nativeObjectWriter, fileId, fileName); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitDebugLoc(IntPtr objWriter, int nativeOffset, int fileId, int linueNumber, int colNumber); + public void EmitDebugLoc(int nativeOffset, int fileId, int linueNumber, int colNumber) + { + EmitDebugLoc(_nativeObjectWriter, nativeOffset, fileId, linueNumber, colNumber); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern uint GetEnumTypeIndex(IntPtr objWriter, EnumTypeDescriptor enumTypeDescriptor, EnumRecordTypeDescriptor[] typeRecords); + + public uint GetEnumTypeIndex(EnumTypeDescriptor enumTypeDescriptor, EnumRecordTypeDescriptor[] typeRecords) + { + return GetEnumTypeIndex(_nativeObjectWriter, enumTypeDescriptor, typeRecords); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern uint GetClassTypeIndex(IntPtr objWriter, ClassTypeDescriptor classTypeDescriptor); + + [DllImport(NativeObjectWriterFileName)] + private static extern uint GetCompleteClassTypeIndex(IntPtr objWriter, ClassTypeDescriptor classTypeDescriptor, + ClassFieldsTypeDescriptor classFieldsTypeDescriptior, DataFieldDescriptor[] fields, + StaticDataFieldDescriptor[] statics); + + [DllImport(NativeObjectWriterFileName)] + private static extern uint GetPrimitiveTypeIndex(IntPtr objWriter, int type); + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitARMFnStart(IntPtr objWriter); + public void EmitARMFnStart() + { + Debug.Assert(!_frameOpened); + EmitARMFnStart(_nativeObjectWriter); + _frameOpened = true; + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitARMFnEnd(IntPtr objWriter); + public void EmitARMFnEnd() + { + Debug.Assert(_frameOpened); + EmitARMFnEnd(_nativeObjectWriter); + _frameOpened = false; + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitARMExIdxCode(IntPtr objWriter, int nativeOffset, byte[] blob); + public void EmitARMExIdxCode(int nativeOffset, byte[] blob) + { + Debug.Assert(_frameOpened); + EmitARMExIdxCode(_nativeObjectWriter, nativeOffset, blob); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitARMExIdxLsda(IntPtr objWriter, byte[] blob); + public void EmitARMExIdxLsda(byte[] blob) + { + Debug.Assert(_frameOpened); + EmitARMExIdxLsda(_nativeObjectWriter, blob); + } + + public uint GetClassTypeIndex(ClassTypeDescriptor classTypeDescriptor) + { + return GetClassTypeIndex(_nativeObjectWriter, classTypeDescriptor); + } + + public uint GetCompleteClassTypeIndex(ClassTypeDescriptor classTypeDescriptor, ClassFieldsTypeDescriptor classFieldsTypeDescriptior, + DataFieldDescriptor[] fields, StaticDataFieldDescriptor[] statics) + { + return GetCompleteClassTypeIndex(_nativeObjectWriter, classTypeDescriptor, classFieldsTypeDescriptior, fields, statics); + } + + public uint GetPrimitiveTypeIndex(TypeDesc type) + { + Debug.Assert(type.IsPrimitive, "it is not a primitive type"); + return GetPrimitiveTypeIndex(_nativeObjectWriter, (int)type.Category); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern uint GetArrayTypeIndex(IntPtr objWriter, ClassTypeDescriptor classDescriptor, ArrayTypeDescriptor arrayTypeDescriptor); + + public uint GetArrayTypeIndex(ClassTypeDescriptor classDescriptor, ArrayTypeDescriptor arrayTypeDescriptor) + { + return GetArrayTypeIndex(_nativeObjectWriter, classDescriptor, arrayTypeDescriptor); + } + + public string GetMangledName(TypeDesc type) + { + return _nodeFactory.NameMangler.GetMangledTypeName(type); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitDebugVar(IntPtr objWriter, string name, UInt32 typeIndex, bool isParam, Int32 rangeCount, ref NativeVarInfo range); + + public void EmitDebugVar(INodeWithDebugInfo owningNode, in DebugVarInfoMetadata debugVar) + { + uint typeIndex; + string varName = debugVar.Name; + try + { + if (owningNode.IsStateMachineMoveNextMethod && debugVar.DebugVarInfo.VarNumber == 0) + { + typeIndex = _userDefinedTypeDescriptor.GetStateMachineThisVariableTypeIndex(debugVar.Type); + varName = "locals"; + } + else + { + typeIndex = _userDefinedTypeDescriptor.GetVariableTypeIndex(debugVar.Type); + } + } + catch (TypeSystemException) + { + typeIndex = 0; // T_NOTYPE + } + + DebugVarRangeInfo[] rangeInfos = debugVar.DebugVarInfo.Ranges; + Span varInfos = rangeInfos.Length < 128 ? + stackalloc NativeVarInfo[rangeInfos.Length] : + new NativeVarInfo[rangeInfos.Length]; + + for (int i = 0; i < rangeInfos.Length; i++) + { + varInfos[i] = new NativeVarInfo + { + endOffset = rangeInfos[i].EndOffset, + startOffset = rangeInfos[i].StartOffset, + varLoc = rangeInfos[i].VarLoc, + varNumber = debugVar.DebugVarInfo.VarNumber, + }; + } + + EmitDebugVar(_nativeObjectWriter, varName, typeIndex, debugVar.IsParameter, varInfos.Length, ref varInfos[0]); + } + + public void EmitDebugVarInfo(ObjectNode node) + { + // No interest if it's not a debug node. + var nodeWithDebugInfo = node as INodeWithDebugInfo; + if (nodeWithDebugInfo != null) + { + foreach (var debugVar in nodeWithDebugInfo.GetDebugVars()) + { + EmitDebugVar(nodeWithDebugInfo, debugVar); + } + } + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitDebugEHClause(IntPtr objWriter, UInt32 TryOffset, UInt32 TryLength, UInt32 HandlerOffset, UInt32 HandlerLength); + + public void EmitDebugEHClause(DebugEHClauseInfo ehClause) + { + EmitDebugEHClause(_nativeObjectWriter, ehClause.TryOffset, ehClause.TryLength, ehClause.HandlerOffset, ehClause.HandlerLength); + } + + public void EmitDebugEHClauseInfo(ObjectNode node) + { + var nodeWithCodeInfo = node as INodeWithCodeInfo; + if (nodeWithCodeInfo != null) + { + DebugEHClauseInfo[] clauses = nodeWithCodeInfo.DebugEHClauseInfos; + if (clauses != null) + { + foreach (var clause in clauses) + { + EmitDebugEHClause(clause); + } + } + } + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitDebugFunctionInfo(IntPtr objWriter, byte[] methodName, int methodSize, UInt32 methodTypeIndex); + public void EmitDebugFunctionInfo(ObjectNode node, int methodSize) + { + uint methodTypeIndex = 0; + + var methodNode = node as IMethodNode; + if (methodNode != null) + { + methodTypeIndex = _userDefinedTypeDescriptor.GetMethodFunctionIdTypeIndex(methodNode.Method); + } + + EmitDebugFunctionInfo(_nativeObjectWriter, _currentNodeZeroTerminatedName.UnderlyingArray, methodSize, methodTypeIndex); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern void EmitDebugModuleInfo(IntPtr objWriter); + public void EmitDebugModuleInfo() + { + if (HasModuleDebugInfo()) + { + EmitDebugModuleInfo(_nativeObjectWriter); + } + } + + public bool HasModuleDebugInfo() + { + return (_options & ObjectWritingOptions.GenerateDebugInfo) != 0; + } + + public bool HasFunctionDebugInfo() + { + if (_offsetToDebugLoc.Count > 0) + { + Debug.Assert(HasModuleDebugInfo()); + return true; + } + + return false; + } + + private int GetDocumentId(string document) + { + if (_debugFileToId.TryGetValue(document, out int result)) + { + return result; + } + + result = _debugFileToId.Count + 1; + _debugFileToId.Add(document, result); + this.EmitDebugFileInfo(result, document); + return result; + } + + public void BuildDebugLocInfoMap(ObjectNode node) + { + if (!HasModuleDebugInfo()) + { + return; + } + + _offsetToDebugLoc.Clear(); + INodeWithDebugInfo debugNode = node as INodeWithDebugInfo; + if (debugNode != null) + { + IEnumerable locs = debugNode.GetNativeSequencePoints(); + foreach (var loc in locs) + { + Debug.Assert(!_offsetToDebugLoc.ContainsKey(loc.NativeOffset)); + _offsetToDebugLoc[loc.NativeOffset] = loc; + _byteInterruptionOffsets[loc.NativeOffset] = true; + } + } + } + + + public void PublishUnwindInfo(ObjectNode node) + { + INodeWithCodeInfo nodeWithCodeInfo = node as INodeWithCodeInfo; + if (nodeWithCodeInfo == null) + { + return; + } + + FrameInfo[] frameInfos = nodeWithCodeInfo.FrameInfos; + if (frameInfos == null) + { + // Data should only be present if the method has unwind info + Debug.Assert(nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory) == null); + + return; + } + + byte[] gcInfo = nodeWithCodeInfo.GCInfo; + MethodExceptionHandlingInfoNode ehInfo = nodeWithCodeInfo.EHInfo; + ISymbolNode associatedDataNode = nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory); + + for (int i = 0; i < frameInfos.Length; i++) + { + FrameInfo frameInfo = frameInfos[i]; + + int start = frameInfo.StartOffset; + int end = frameInfo.EndOffset; + int len = frameInfo.BlobData.Length; + byte[] blob = frameInfo.BlobData; + + _sb.Clear().Append(_nodeFactory.NameMangler.CompilationUnitPrefix).Append("_unwind").Append(i.ToStringInvariant()); + + byte[] blobSymbolName = _sb.Append(_currentNodeZeroTerminatedName).ToUtf8String().UnderlyingArray; + + ObjectNodeSection section = ObjectNodeSection.XDataSection; + if (ShouldShareSymbol(node)) + section = GetSharedSection(section, _sb.ToString()); + SwitchSection(_nativeObjectWriter, section.Name, GetCustomSectionAttributes(section), section.ComdatName); + + EmitAlignment(4); + EmitSymbolDef(blobSymbolName); + + FrameInfoFlags flags = frameInfo.Flags; + flags |= ehInfo != null ? FrameInfoFlags.HasEHInfo : 0; + flags |= associatedDataNode != null ? FrameInfoFlags.HasAssociatedData : 0; + + EmitBlob(blob); + + EmitIntValue((byte)flags, 1); + + if (associatedDataNode != null) + { + EmitSymbolReference(associatedDataNode, 0, RelocType.IMAGE_REL_BASED_ABSOLUTE); + associatedDataNode = null; + } + + if (ehInfo != null) + { + EmitSymbolReference(ehInfo, 0, RelocType.IMAGE_REL_BASED_ABSOLUTE); + ehInfo = null; + } + + if (gcInfo != null) + { + EmitBlob(gcInfo); + gcInfo = null; + } + + // For window, just emit the frame blob (UNWIND_INFO) as a whole. + EmitWinFrameInfo(start, end, len, blobSymbolName); + + EnsureCurrentSection(); + } + } + + public void BuildCFIMap(NodeFactory factory, ObjectNode node) + { + _offsetToCfis.Clear(); + _offsetToCfiStart.Clear(); + _offsetToCfiEnd.Clear(); + _offsetToCfiLsdaBlobName.Clear(); + _frameOpened = false; + + INodeWithCodeInfo nodeWithCodeInfo = node as INodeWithCodeInfo; + if (nodeWithCodeInfo == null) + { + return; + } + + FrameInfo[] frameInfos = nodeWithCodeInfo.FrameInfos; + if (frameInfos == null) + { + // Data should only be present if the method has unwind info + Debug.Assert(nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory) == null); + + return; + } + + byte[] gcInfo = nodeWithCodeInfo.GCInfo; + MethodExceptionHandlingInfoNode ehInfo = nodeWithCodeInfo.EHInfo; + ISymbolNode associatedDataNode = nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory); + + for (int i = 0; i < frameInfos.Length; i++) + { + FrameInfo frameInfo = frameInfos[i]; + + int start = frameInfo.StartOffset; + int end = frameInfo.EndOffset; + int len = frameInfo.BlobData.Length; + byte[] blob = frameInfo.BlobData; + + ObjectNodeSection lsdaSection = LsdaSection; + if (ShouldShareSymbol(node)) + { + lsdaSection = GetSharedSection(lsdaSection, ((ISymbolNode)node).GetMangledName(_nodeFactory.NameMangler)); + } + SwitchSection(_nativeObjectWriter, lsdaSection.Name, GetCustomSectionAttributes(lsdaSection), lsdaSection.ComdatName); + + _sb.Clear().Append("_lsda").Append(i.ToStringInvariant()).Append(_currentNodeZeroTerminatedName); + byte[] blobSymbolName = _sb.ToUtf8String().UnderlyingArray; + EmitSymbolDef(blobSymbolName); + + FrameInfoFlags flags = frameInfo.Flags; + flags |= ehInfo != null ? FrameInfoFlags.HasEHInfo : 0; + flags |= associatedDataNode != null ? FrameInfoFlags.HasAssociatedData : 0; + + EmitIntValue((byte)flags, 1); + + if (i != 0) + { + EmitSymbolRef(_sb.Clear().Append("_lsda0").Append(_currentNodeZeroTerminatedName), RelocType.IMAGE_REL_BASED_RELPTR32); + + // emit relative offset from the main function + EmitIntValue((ulong)(start - frameInfos[0].StartOffset), 4); + } + + if (associatedDataNode != null) + { + EmitSymbolReference(associatedDataNode, 0, RelocType.IMAGE_REL_BASED_RELPTR32); + associatedDataNode = null; + } + + if (ehInfo != null) + { + EmitSymbolReference(ehInfo, 0, RelocType.IMAGE_REL_BASED_RELPTR32); + ehInfo = null; + } + + if (gcInfo != null) + { + EmitBlob(gcInfo); + gcInfo = null; + } + + // For Unix, we build CFI blob map for each offset. + Debug.Assert(len % CfiCodeSize == 0); + + // Record start/end of frames which shouldn't be overlapped. + _offsetToCfiStart.Add(start); + _offsetToCfiEnd.Add(end); + _byteInterruptionOffsets[start] = true; + _byteInterruptionOffsets[end] = true; + _offsetToCfiLsdaBlobName.Add(start, blobSymbolName); + for (int j = 0; j < len; j += CfiCodeSize) + { + // The first byte of CFI_CODE is offset from the range the frame covers. + // Compute code offset from the root method. + int codeOffset = blob[j] + start; + List cfis; + if (!_offsetToCfis.TryGetValue(codeOffset, out cfis)) + { + cfis = new List(); + _offsetToCfis.Add(codeOffset, cfis); + _byteInterruptionOffsets[codeOffset] = true; + } + byte[] cfi = new byte[CfiCodeSize]; + Array.Copy(blob, j, cfi, 0, CfiCodeSize); + cfis.Add(cfi); + } + } + + EnsureCurrentSection(); + } + + public void EmitCFICodes(int offset) + { + // Emit end the old frame before start a frame. + if (_offsetToCfiEnd.Contains(offset)) + { + if (_targetPlatform.Architecture == TargetArchitecture.ARM) + EmitARMFnEnd(); + else + EmitCFIEnd(offset); + } + + if (_offsetToCfiStart.Contains(offset)) + { + if (_targetPlatform.Architecture == TargetArchitecture.ARM) + EmitARMFnStart(); + else + EmitCFIStart(offset); + + byte[] blobSymbolName; + if (_offsetToCfiLsdaBlobName.TryGetValue(offset, out blobSymbolName)) + { + if (_targetPlatform.Architecture == TargetArchitecture.ARM) + EmitARMExIdxLsda(blobSymbolName); + else + EmitCFILsda(blobSymbolName); + + } + else + { + // Internal compiler error + Debug.Assert(false); + } + } + + // Emit individual cfi blob for the given offset + List cfis; + if (_offsetToCfis.TryGetValue(offset, out cfis)) + { + foreach (byte[] cfi in cfis) + { + if (_targetPlatform.Architecture == TargetArchitecture.ARM) + { + EmitARMExIdxCode(offset, cfi); + } + else + { + EmitCFICode(offset, cfi); + } + } + } + } + + public void EmitDebugLocInfo(int offset) + { + NativeSequencePoint loc; + if (_offsetToDebugLoc.TryGetValue(offset, out loc)) + { + EmitDebugLoc(offset, + GetDocumentId(loc.FileName), + loc.LineNumber, + loc.ColNumber); + } + } + + public void BuildSymbolDefinitionMap(ObjectNode node, ISymbolDefinitionNode[] definedSymbols) + { + _offsetToDefName.Clear(); + foreach (ISymbolDefinitionNode n in definedSymbols) + { + if (!_offsetToDefName.ContainsKey(n.Offset)) + { + _offsetToDefName[n.Offset] = new List(); + } + + _offsetToDefName[n.Offset].Add(n); + _byteInterruptionOffsets[n.Offset] = true; + } + + var symbolNode = node as ISymbolDefinitionNode; + if (symbolNode != null) + { + _sb.Clear(); + AppendExternCPrefix(_sb); + symbolNode.AppendMangledName(_nodeFactory.NameMangler, _sb); + _currentNodeZeroTerminatedName = _sb.Append('\0').ToUtf8String(); + } + else + { + _currentNodeZeroTerminatedName = default(Utf8String); + } + } + + private void AppendExternCPrefix(Utf8StringBuilder sb) + { + if (_targetPlatform.OperatingSystem == TargetOS.OSX) + { + // On OSX, we need to prefix an extra underscore to account for correct linkage of + // extern "C" functions. + sb.Append('_'); + } + } + + // Returns size of the emitted symbol reference + public int EmitSymbolReference(ISymbolNode target, int delta, RelocType relocType) + { + _sb.Clear(); + AppendExternCPrefix(_sb); + target.AppendMangledName(_nodeFactory.NameMangler, _sb); + + SymbolRefFlags flags = 0; + + // For now consider all method symbols address taken. + // We could restrict this in the future to those that are referenced from + // reflection tables, EH tables, were actually address taken in code, or are referenced from vtables. + if ((_options & ObjectWritingOptions.ControlFlowGuard) != 0 && target is IMethodNode) + { + flags |= SymbolRefFlags.AddressTakenFunction; + } + + return EmitSymbolRef(_sb, relocType, checked(delta + target.Offset), flags); + } + + public void EmitBlobWithRelocs(byte[] blob, Relocation[] relocs) + { + int nextRelocOffset = -1; + int nextRelocIndex = -1; + if (relocs.Length > 0) + { + nextRelocOffset = relocs[0].Offset; + nextRelocIndex = 0; + } + + int i = 0; + while (i < blob.Length) + { + if (i == nextRelocOffset) + { + Relocation reloc = relocs[nextRelocIndex]; + + long delta; + unsafe + { + fixed (void* location = &blob[i]) + { + delta = Relocation.ReadValue(reloc.RelocType, location); + } + } + int size = EmitSymbolReference(reloc.Target, (int)delta, reloc.RelocType); + + // Update nextRelocIndex/Offset + if (++nextRelocIndex < relocs.Length) + { + nextRelocOffset = relocs[nextRelocIndex].Offset; + } + i += size; + } + else + { + EmitIntValue(blob[i], 1); + i++; + } + } + } + + public void EmitSymbolDefinition(int currentOffset) + { + List nodes; + if (_offsetToDefName.TryGetValue(currentOffset, out nodes)) + { + foreach (var name in nodes) + { + _sb.Clear(); + AppendExternCPrefix(_sb); + name.AppendMangledName(_nodeFactory.NameMangler, _sb); + + EmitSymbolDef(_sb); + + string alternateName = _nodeFactory.GetSymbolAlternateName(name); + if (alternateName != null) + { + _sb.Clear(); + AppendExternCPrefix(_sb); + _sb.Append(alternateName); + + EmitSymbolDef(_sb, global: true); + } + } + } + } + + private IntPtr _nativeObjectWriter = IntPtr.Zero; + + public ObjectWriter(string objectFilePath, NodeFactory factory, ObjectWritingOptions options) + { + var triple = GetLLVMTripleFromTarget(factory.Target); + + _nativeObjectWriter = InitObjWriter(objectFilePath, triple); + if (_nativeObjectWriter == IntPtr.Zero) + { + throw new IOException("Fail to initialize Native Object Writer"); + } + _nodeFactory = factory; + _targetPlatform = _nodeFactory.Target; + _isSingleFileCompilation = _nodeFactory.CompilationModuleGroup.IsSingleFileCompilation; + _userDefinedTypeDescriptor = new UserDefinedTypeDescriptor(this, factory); + _options = options; + } + + public void Dispose() + { + Dispose(true); + } + + public virtual void Dispose(bool bDisposing) + { + if (_nativeObjectWriter != IntPtr.Zero) + { + // Finalize object emission. + FinishObjWriter(_nativeObjectWriter); + _nativeObjectWriter = IntPtr.Zero; + } + + _nodeFactory = null; + + if (bDisposing) + { + GC.SuppressFinalize(this); + } + } + + ~ObjectWriter() + { + Dispose(false); + } + + private bool ShouldShareSymbol(ObjectNode node) + { + // Foldable sections are always COMDATs + ObjectNodeSection section = node.Section; + if (section == ObjectNodeSection.FoldableManagedCodeUnixContentSection || + section == ObjectNodeSection.FoldableManagedCodeWindowsContentSection || + section == ObjectNodeSection.FoldableReadOnlyDataSection) + return true; + + if (_isSingleFileCompilation) + return false; + + if (_targetPlatform.OperatingSystem == TargetOS.OSX) + return false; + + if (!(node is ISymbolNode)) + return false; + + // These intentionally clash with one another, but are merged with linker directives so should not be Comdat folded + if (node is ModulesSectionNode) + return false; + + return true; + } + + private ObjectNodeSection GetSharedSection(ObjectNodeSection section, string key) + { + string standardSectionPrefix = ""; + if (section.IsStandardSection) + standardSectionPrefix = "."; + + return new ObjectNodeSection(standardSectionPrefix + section.Name, section.Type, key); + } + + public void ResetByteRunInterruptionOffsets(ObjectData nodeContents) + { + int neededInterruptionsBytes = nodeContents.Data.Length + 1; + if (_byteInterruptionOffsets == null || _byteInterruptionOffsets.Length < neededInterruptionsBytes) + _byteInterruptionOffsets = new bool[neededInterruptionsBytes]; + else + Array.Clear(_byteInterruptionOffsets, 0, neededInterruptionsBytes); + + foreach (var reloc in nodeContents.Relocs) + { + _byteInterruptionOffsets[reloc.Offset] = true; + } + } + + public static void EmitObject(string objectFilePath, IReadOnlyCollection nodes, NodeFactory factory, ObjectWritingOptions options, IObjectDumper dumper, Logger logger) + { + ObjectWriter objectWriter = new ObjectWriter(objectFilePath, factory, options); + bool succeeded = false; + + try + { + ObjectNodeSection managedCodeSection; + if (factory.Target.OperatingSystem == TargetOS.Windows) + { + managedCodeSection = ObjectNodeSection.ManagedCodeWindowsContentSection; + } + else + { + managedCodeSection = ObjectNodeSection.ManagedCodeUnixContentSection; + // TODO 2916: managed code section has to be created here, switch is not necessary. + objectWriter.SetSection(ObjectNodeSection.ManagedCodeUnixContentSection); + objectWriter.SetSection(LsdaSection); + } + objectWriter.SetCodeSectionAttribute(managedCodeSection); + + ProgressReporter progressReporter = default; + if (logger.IsVerbose) + { + int count = 0; + foreach (var node in nodes) + if (node is ObjectNode) + count++; + + logger.Writer.WriteLine($"Writing {count} object nodes..."); + + progressReporter = new ProgressReporter(logger, count); + } + + foreach (DependencyNode depNode in nodes) + { + ObjectNode node = depNode as ObjectNode; + if (node == null) + continue; + + if (logger.IsVerbose) + progressReporter.LogProgress(); + + if (node.ShouldSkipEmittingObjectNode(factory)) + continue; + + ObjectData nodeContents = node.GetData(factory); + + if (dumper != null) + dumper.DumpObjectNode(factory.NameMangler, node, nodeContents); + +#if DEBUG + foreach (ISymbolNode definedSymbol in nodeContents.DefinedSymbols) + { + try + { + _previouslyWrittenNodeNames.Add(definedSymbol.GetMangledName(factory.NameMangler), definedSymbol); + } + catch (ArgumentException) + { + ISymbolNode alreadyWrittenSymbol = _previouslyWrittenNodeNames[definedSymbol.GetMangledName(factory.NameMangler)]; + Debug.Fail("Duplicate node name emitted to file", + $"Symbol {definedSymbol.GetMangledName(factory.NameMangler)} has already been written to the output object file {objectFilePath} with symbol {alreadyWrittenSymbol}"); + } + } +#endif + + + ObjectNodeSection section = node.Section; + if (objectWriter.ShouldShareSymbol(node)) + { + section = objectWriter.GetSharedSection(section, ((ISymbolNode)node).GetMangledName(factory.NameMangler)); + } + + // Ensure section and alignment for the node. + objectWriter.SetSection(section); + objectWriter.EmitAlignment(nodeContents.Alignment); + + objectWriter.ResetByteRunInterruptionOffsets(nodeContents); + + // Build symbol definition map. + objectWriter.BuildSymbolDefinitionMap(node, nodeContents.DefinedSymbols); + + // The DWARF CFI unwind is only implemented for some architectures. + TargetArchitecture tarch = factory.Target.Architecture; + if (!factory.Target.IsWindows && + (tarch == TargetArchitecture.X64 || tarch == TargetArchitecture.ARM || tarch == TargetArchitecture.ARM64)) + objectWriter.BuildCFIMap(factory, node); + + // Build debug location map + objectWriter.BuildDebugLocInfoMap(node); + + Relocation[] relocs = nodeContents.Relocs; + int nextRelocOffset = -1; + int nextRelocIndex = -1; + if (relocs.Length > 0) + { + nextRelocOffset = relocs[0].Offset; + nextRelocIndex = 0; + } + + int i = 0; + + while (i < nodeContents.Data.Length) + { + // Emit symbol definitions if necessary + objectWriter.EmitSymbolDefinition(i); + + // Emit CFI codes for the given offset. + objectWriter.EmitCFICodes(i); + + // Emit debug loc info if needed. + objectWriter.EmitDebugLocInfo(i); + + if (i == nextRelocOffset) + { + Relocation reloc = relocs[nextRelocIndex]; + + long delta; + unsafe + { + fixed (void* location = &nodeContents.Data[i]) + { + delta = Relocation.ReadValue(reloc.RelocType, location); + } + } + int size = objectWriter.EmitSymbolReference(reloc.Target, (int)delta, reloc.RelocType); + + // Emit a copy of original Thumb2/ARM64 instruction that came from RyuJIT + + switch (reloc.RelocType) + { + case RelocType.IMAGE_REL_BASED_THUMB_MOV32: + case RelocType.IMAGE_REL_BASED_THUMB_BRANCH24: + case RelocType.IMAGE_REL_BASED_ARM64_BRANCH26: + case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: + case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: + case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L: + unsafe + { + fixed (void* location = &nodeContents.Data[i]) + { + objectWriter.EmitBytes((IntPtr)location, size); + } + } + break; + } + + // Update nextRelocIndex/Offset + if (++nextRelocIndex < relocs.Length) + { + nextRelocOffset = relocs[nextRelocIndex].Offset; + } + else + { + // This is the last reloc. Set the next reloc offset to -1 in case the last reloc has a zero size, + // which means the reloc does not have vacant bytes corresponding to in the data buffer. E.g, + // IMAGE_REL_THUMB_BRANCH24 is a kind of 24-bit reloc whose bits scatte over the instruction that + // references it. We do not vacate extra bytes in the data buffer for this kind of reloc. + nextRelocOffset = -1; + } + i += size; + } + else + { + int offsetIndex = Array.IndexOf(objectWriter._byteInterruptionOffsets, true, i + 1, nodeContents.Data.Length - i - 1); + + int nextOffset = offsetIndex == -1 ? nodeContents.Data.Length : offsetIndex; + + unsafe + { + // Todo: Use Span instead once it's available to us in this repo + fixed (byte* pContents = &nodeContents.Data[i]) + { + objectWriter.EmitBytes((IntPtr)(pContents), nextOffset - i); + i += nextOffset - i; + } + } + + } + } + Debug.Assert(i == nodeContents.Data.Length); + + // It is possible to have a symbol just after all of the data. + objectWriter.EmitSymbolDefinition(nodeContents.Data.Length); + + // Publish Windows unwind info. + if (factory.Target.IsWindows) + objectWriter.PublishUnwindInfo(node); + + // Emit the last CFI to close the frame. + objectWriter.EmitCFICodes(nodeContents.Data.Length); + + if (objectWriter.HasFunctionDebugInfo()) + { + objectWriter.EmitDebugVarInfo(node); + objectWriter.EmitDebugEHClauseInfo(node); + objectWriter.EmitDebugFunctionInfo(node, nodeContents.Data.Length); + } + + if (node is ConstructedEETypeNode MethodTable) + { + objectWriter._userDefinedTypeDescriptor.GetTypeIndex(MethodTable.Type, needsCompleteType: true); + } + } + + if (logger.IsVerbose) + logger.Writer.WriteLine($"Finalizing output to '{objectFilePath}'..."); + + objectWriter.EmitDebugModuleInfo(); + + succeeded = true; + } + finally + { + objectWriter.Dispose(); + + if (!succeeded) + { + // If there was an exception while generating the OBJ file, make sure we don't leave the unfinished + // object file around. + try + { + File.Delete(objectFilePath); + } + catch + { + } + } + } + } + + [DllImport(NativeObjectWriterFileName)] + private static extern uint GetPointerTypeIndex(IntPtr objWriter, PointerTypeDescriptor pointerDescriptor); + + uint ITypesDebugInfoWriter.GetPointerTypeIndex(PointerTypeDescriptor pointerDescriptor) + { + return GetPointerTypeIndex(_nativeObjectWriter, pointerDescriptor); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern uint GetMemberFunctionTypeIndex(IntPtr objWriter, MemberFunctionTypeDescriptor memberDescriptor, uint[] argumentTypes); + + uint ITypesDebugInfoWriter.GetMemberFunctionTypeIndex(MemberFunctionTypeDescriptor memberDescriptor, uint[] argumentTypes) + { + return GetMemberFunctionTypeIndex(_nativeObjectWriter, memberDescriptor, argumentTypes); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern uint GetMemberFunctionIdTypeIndex(IntPtr objWriter, MemberFunctionIdTypeDescriptor memberIdDescriptor); + + uint ITypesDebugInfoWriter.GetMemberFunctionId(MemberFunctionIdTypeDescriptor memberIdDescriptor) + { + return GetMemberFunctionIdTypeIndex(_nativeObjectWriter, memberIdDescriptor); + } + + private static string GetLLVMTripleFromTarget(TargetDetails target) + { + // We create a triple based on the Target + // See https://clang.llvm.org/docs/CrossCompilation.html#target-triple + // Detect the LLVM arch + string arch; + // Not used + string sub = string.Empty; + switch (target.Architecture) + { + case TargetArchitecture.ARM: + arch = "thumbv7"; + break; + case TargetArchitecture.ARM64: + arch = "aarch64"; + break; + case TargetArchitecture.X64: + arch = "x86_64"; + break; + case TargetArchitecture.X86: + arch = "i686"; + break; + case TargetArchitecture.Wasm32: + arch = "wasm32"; + break; + default: + throw new InvalidOperationException($"The architecture `{target.Architecture}` is not supported by ObjectWriter"); + } + + string vendor; + string sys; + string abi; + switch (target.OperatingSystem) + { + case TargetOS.Windows: + vendor = "pc"; + sys = "win32"; + abi = "windows"; + break; + case TargetOS.Linux: + case TargetOS.FreeBSD: + case TargetOS.NetBSD: + vendor = "pc"; + sys = "linux"; + abi = "elf"; + break; + case TargetOS.OSX: + vendor = "apple"; + sys = "darwin"; + abi = "macho"; + break; + case TargetOS.WebAssembly: + vendor = "unknown"; + sys = "unknown"; + abi = "wasm"; + break; + default: + throw new InvalidOperationException($"The operating system `{target.OperatingSystem}` is not supported by ObjectWriter"); + } + + return $"{arch}{sub}-{vendor}-{sys}-{abi}"; + } + + private enum SymbolRefFlags + { + AddressTakenFunction = 0x0001, + } + + private struct ProgressReporter + { + private readonly Logger _logger; + private readonly int _increment; + private int _current; + + // Will report progress every (100 / 10) = 10% + private const int Steps = 10; + + public ProgressReporter(Logger logger, int total) + { + _logger = logger; + _increment = total / Steps; + _current = 0; + } + + public void LogProgress() + { + _current++; + + int adjusted = _current + Steps - 1; + if ((adjusted % _increment) == 0) + { + _logger.Writer.WriteLine($"{(adjusted / _increment) * (100 / Steps)}%..."); + } + } + } + } + + [Flags] + public enum ObjectWritingOptions + { + GenerateDebugInfo = 0x01, + ControlFlowGuard = 0x02, + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PInvokeMethodFixupNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PInvokeMethodFixupNode.cs new file mode 100644 index 00000000000000..4971ca48763fe9 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PInvokeMethodFixupNode.cs @@ -0,0 +1,180 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +using Internal.IL.Stubs; +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a single PInvoke MethodFixupCell as defined in the core library. + /// + public class PInvokeMethodFixupNode : ObjectNode, ISymbolDefinitionNode + { + private readonly PInvokeMethodData _pInvokeMethodData; + + public PInvokeMethodFixupNode(PInvokeMethodData pInvokeMethodData) + { + _pInvokeMethodData = pInvokeMethodData; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__pinvoke_"); + _pInvokeMethodData.AppendMangledName(nameMangler, sb); + } + public int Offset => 0; + public override bool IsShareable => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + + public override bool StaticDependenciesAreComputed => true; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.RequireInitialPointerAlignment(); + + builder.AddSymbol(this); + + // + // Emit a MethodFixupCell struct + // + + // Address (to be fixed up at runtime) + builder.EmitZeroPointer(); + + // Entry point name + string entryPointName = _pInvokeMethodData.EntryPointName; + if (factory.Target.IsWindows && entryPointName.StartsWith("#", StringComparison.OrdinalIgnoreCase)) + { + // Windows-specific ordinal import + // CLR-compatible behavior: Strings that can't be parsed as a signed integer are treated as zero. + int entrypointOrdinal; + if (!int.TryParse(entryPointName.Substring(1), out entrypointOrdinal)) + entrypointOrdinal = 0; + + // CLR-compatible behavior: Ordinal imports are 16-bit on Windows. Discard rest of the bits. + builder.EmitNaturalInt((ushort)entrypointOrdinal); + } + else + { + // Import by name + builder.EmitPointerReloc(factory.ConstantUtf8String(entryPointName)); + } + + // Module fixup cell + builder.EmitPointerReloc(factory.PInvokeModuleFixup(_pInvokeMethodData.ModuleData)); + + builder.EmitInt((int)_pInvokeMethodData.CharSetMangling); + + return builder.ToObjectData(); + } + + public override int ClassCode => -1592006940; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return _pInvokeMethodData.CompareTo(((PInvokeMethodFixupNode)other)._pInvokeMethodData, comparer); + } + } + + public readonly struct PInvokeMethodData : IEquatable + { + public readonly PInvokeModuleData ModuleData; + public readonly string EntryPointName; + public readonly CharSet CharSetMangling; + + public PInvokeMethodData(PInvokeLazyFixupField pInvokeLazyFixupField) + { + PInvokeMetadata metadata = pInvokeLazyFixupField.PInvokeMetadata; + ModuleDesc declaringModule = ((MetadataType)pInvokeLazyFixupField.TargetMethod.OwningType).Module; + + DllImportSearchPath? dllImportSearchPath = default; + if (declaringModule.Assembly is EcmaAssembly asm) + { + // We look for [assembly:DefaultDllImportSearchPaths(...)] + var attrHandle = asm.MetadataReader.GetCustomAttributeHandle(asm.AssemblyDefinition.GetCustomAttributes(), + "System.Runtime.InteropServices", "DefaultDllImportSearchPathsAttribute"); + if (!attrHandle.IsNil) + { + var attr = asm.MetadataReader.GetCustomAttribute(attrHandle); + var decoded = attr.DecodeValue(new CustomAttributeTypeProvider(asm)); + if (decoded.FixedArguments.Length == 1 && + decoded.FixedArguments[0].Value is int searchPath) + { + dllImportSearchPath = (DllImportSearchPath)searchPath; + } + } + } + ModuleData = new PInvokeModuleData(metadata.Module, dllImportSearchPath, declaringModule); + + EntryPointName = metadata.Name; + + CharSet charSetMangling = default; + if (declaringModule.Context.Target.IsWindows && !metadata.Flags.ExactSpelling) + { + // Mirror CharSet normalization from Marshaller.CreateMarshaller + bool isAnsi = metadata.Flags.CharSet switch + { + CharSet.Ansi => true, + CharSet.Unicode => false, + CharSet.Auto => false, + _ => true + }; + + charSetMangling = isAnsi ? CharSet.Ansi : CharSet.Unicode; + } + CharSetMangling = charSetMangling; + } + + public bool Equals(PInvokeMethodData other) + { + return ModuleData.Equals(other.ModuleData) && + EntryPointName == other.EntryPointName && + CharSetMangling == other.CharSetMangling; + } + + public override bool Equals(object obj) + { + return obj is PInvokeMethodData other && Equals(other); + } + + public override int GetHashCode() + { + return ModuleData.GetHashCode() ^ EntryPointName.GetHashCode(); + } + + public int CompareTo(PInvokeMethodData other, CompilerComparer comparer) + { + var entryPointCompare = StringComparer.Ordinal.Compare(EntryPointName, other.EntryPointName); + if (entryPointCompare != 0) + return entryPointCompare; + + var moduleCompare = ModuleData.CompareTo(other.ModuleData, comparer); + if (moduleCompare != 0) + return moduleCompare; + + return CharSetMangling.CompareTo(other.CharSetMangling); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + ModuleData.AppendMangledName(nameMangler, sb); + sb.Append("__"); + sb.Append(EntryPointName); + if (CharSetMangling != default) + { + sb.Append("__"); + sb.Append(CharSetMangling.ToString()); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PInvokeModuleFixupNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PInvokeModuleFixupNode.cs new file mode 100644 index 00000000000000..d027f3e599068b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PInvokeModuleFixupNode.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; +using InteropDataConstants = Internal.Runtime.InteropDataConstants; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a single PInvoke ModuleFixupCell as defined in the core library. + /// + public class PInvokeModuleFixupNode : ObjectNode, ISymbolDefinitionNode + { + public readonly PInvokeModuleData _pInvokeModuleData; + + public PInvokeModuleFixupNode(PInvokeModuleData pInvokeModuleData) + { + _pInvokeModuleData = pInvokeModuleData; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__nativemodule_"); + _pInvokeModuleData.AppendMangledName(nameMangler, sb); + } + public int Offset => 0; + public override bool IsShareable => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + + public override bool StaticDependenciesAreComputed => true; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.RequireInitialPointerAlignment(); + + builder.AddSymbol(this); + + ISymbolNode nameSymbol = factory.ConstantUtf8String(_pInvokeModuleData.ModuleName); + ISymbolNode moduleTypeSymbol = factory.NecessaryTypeSymbol(_pInvokeModuleData.DeclaringModule.GetGlobalModuleType()); + + // + // Emit a ModuleFixupCell struct + // + + builder.EmitZeroPointer(); + builder.EmitPointerReloc(nameSymbol); + builder.EmitPointerReloc(moduleTypeSymbol); + + uint dllImportSearchPath = 0; + if (_pInvokeModuleData.DllImportSearchPath.HasValue) + { + dllImportSearchPath = (uint)_pInvokeModuleData.DllImportSearchPath.Value; + Debug.Assert((dllImportSearchPath & InteropDataConstants.HasDllImportSearchPath) == 0); + dllImportSearchPath |= InteropDataConstants.HasDllImportSearchPath; + } + builder.EmitInt((int)dllImportSearchPath); + + return builder.ToObjectData(); + } + + public override int ClassCode => 159930099; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return _pInvokeModuleData.CompareTo(((PInvokeModuleFixupNode)other)._pInvokeModuleData, comparer); + } + } + + public readonly struct PInvokeModuleData : IEquatable + { + public readonly string ModuleName; + public readonly DllImportSearchPath? DllImportSearchPath; + public readonly ModuleDesc DeclaringModule; + + public PInvokeModuleData(string moduleName, DllImportSearchPath? dllImportSearchPath, ModuleDesc declaringModule) + { + ModuleName = moduleName; + DllImportSearchPath = dllImportSearchPath; + DeclaringModule = declaringModule; + } + + public bool Equals(PInvokeModuleData other) + { + return DeclaringModule == other.DeclaringModule && + DllImportSearchPath == other.DllImportSearchPath && + ModuleName == other.ModuleName; + } + + public override bool Equals(object obj) + { + return obj is PInvokeModuleData other && Equals(other); + } + + public override int GetHashCode() + { + return ModuleName.GetHashCode() ^ DeclaringModule.GetHashCode(); + } + + public int CompareTo(PInvokeModuleData other, CompilerComparer comparer) + { + int result = StringComparer.Ordinal.Compare(ModuleName, other.ModuleName); + if (result != 0) + return result; + + result = comparer.Compare(DeclaringModule.GetGlobalModuleType(), + other.DeclaringModule.GetGlobalModuleType()); + if (result != 0) + return result; + + return Nullable.Compare(DllImportSearchPath, other.DllImportSearchPath); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.GetMangledTypeName(DeclaringModule.GetGlobalModuleType())); + sb.Append('_'); + sb.Append(nameMangler.SanitizeName(ModuleName)); + if (DllImportSearchPath.HasValue) + { + sb.Append('_'); + sb.Append(((int)DllImportSearchPath.Value).ToString()); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs new file mode 100644 index 00000000000000..829a26e212a0c4 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs @@ -0,0 +1,345 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.IL; +using Internal.Text; +using Internal.TypeSystem; + +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + public abstract partial class ReadyToRunGenericHelperNode : AssemblyStubNode, INodeWithRuntimeDeterminedDependencies + { + private readonly ReadyToRunHelperId _id; + private readonly object _target; + protected readonly TypeSystemEntity _dictionaryOwner; + protected readonly GenericLookupResult _lookupSignature; + + // True if any of slots in dictionaries associated with this layout could not be filled + // at compile time due to a TypeSystemException. Only query through HandlesInvalidEntries + // below so that we can assert this is not queried at an inappropriate time before + // the whole program view has been established. + private bool _hasInvalidEntries; + + public ReadyToRunHelperId Id => _id; + public Object Target => _target; + public TypeSystemEntity DictionaryOwner => _dictionaryOwner; + public GenericLookupResult LookupSignature => _lookupSignature; + + public bool HandlesInvalidEntries(NodeFactory factory) + { + Debug.Assert(factory.MarkingComplete); + return _hasInvalidEntries; + } + + public ReadyToRunGenericHelperNode(NodeFactory factory, ReadyToRunHelperId helperId, object target, TypeSystemEntity dictionaryOwner) + { + _id = helperId; + _dictionaryOwner = dictionaryOwner; + _target = target; + + _lookupSignature = GetLookupSignature(factory, helperId, target); + } + + public static GenericLookupResult GetLookupSignature(NodeFactory factory, ReadyToRunHelperId id, object target) + { + // Necessary type handle is not something you can put in a dictionary - someone should have normalized to TypeHandle + Debug.Assert(id != ReadyToRunHelperId.NecessaryTypeHandle); + + switch (id) + { + case ReadyToRunHelperId.TypeHandle: + return factory.GenericLookup.Type((TypeDesc)target); + case ReadyToRunHelperId.TypeHandleForCasting: + // Check that we unwrapped the cases that could be unwrapped to prevent duplicate entries + Debug.Assert(factory.GenericLookup.Type((TypeDesc)target) != factory.GenericLookup.UnwrapNullableType((TypeDesc)target)); + return factory.GenericLookup.UnwrapNullableType((TypeDesc)target); + case ReadyToRunHelperId.MethodHandle: + return factory.GenericLookup.MethodHandle((MethodDesc)target); + case ReadyToRunHelperId.FieldHandle: + return factory.GenericLookup.FieldHandle((FieldDesc)target); + case ReadyToRunHelperId.GetGCStaticBase: + return factory.GenericLookup.TypeGCStaticBase((TypeDesc)target); + case ReadyToRunHelperId.GetNonGCStaticBase: + return factory.GenericLookup.TypeNonGCStaticBase((TypeDesc)target); + case ReadyToRunHelperId.GetThreadStaticBase: + return factory.GenericLookup.TypeThreadStaticBaseIndex((TypeDesc)target); + case ReadyToRunHelperId.MethodDictionary: + return factory.GenericLookup.MethodDictionary((MethodDesc)target); + case ReadyToRunHelperId.VirtualDispatchCell: + return factory.GenericLookup.VirtualDispatchCell((MethodDesc)target); + case ReadyToRunHelperId.MethodEntry: + return factory.GenericLookup.MethodEntry((MethodDesc)target); + case ReadyToRunHelperId.DelegateCtor: + return ((DelegateCreationInfo)target).GetLookupKind(factory); + case ReadyToRunHelperId.DefaultConstructor: + return factory.GenericLookup.DefaultCtorLookupResult((TypeDesc)target); + case ReadyToRunHelperId.ObjectAllocator: + return factory.GenericLookup.ObjectAllocator((TypeDesc)target); + default: + throw new NotImplementedException(); + } + } + + protected override bool IsVisibleFromManagedCode => false; + + protected sealed override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + public override bool IsShareable => true; + + protected sealed override void OnMarked(NodeFactory factory) + { + DictionaryLayoutNode layout = factory.GenericDictionaryLayout(_dictionaryOwner); + + if (layout.HasUnfixedSlots) + { + // When the helper call gets marked, ensure the generic layout for the associated dictionaries + // includes the signature. + layout.EnsureEntry(_lookupSignature); + + if ((_id == ReadyToRunHelperId.GetGCStaticBase || _id == ReadyToRunHelperId.GetThreadStaticBase) && + factory.PreinitializationManager.HasLazyStaticConstructor((TypeDesc)_target)) + { + // If the type has a lazy static constructor, we also need the non-GC static base + // because that's where the class constructor context is. + layout.EnsureEntry(factory.GenericLookup.TypeNonGCStaticBase((TypeDesc)_target)); + } + } + } + + public IEnumerable InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) + { + DependencyList result = new DependencyList(); + + var lookupContext = new GenericLookupResultContext(_dictionaryOwner, typeInstantiation, methodInstantiation); + + switch (_id) + { + case ReadyToRunHelperId.GetGCStaticBase: + case ReadyToRunHelperId.GetThreadStaticBase: + { + // If the type has a lazy static constructor, we also need the non-GC static base + // because that's where the class constructor context is. + TypeDesc type = (TypeDesc)_target; + + if (factory.PreinitializationManager.HasLazyStaticConstructor(type)) + { + result.Add( + new DependencyListEntry( + factory.GenericLookup.TypeNonGCStaticBase(type).GetTarget(factory, lookupContext), + "Dictionary dependency")); + } + } + break; + + case ReadyToRunHelperId.DelegateCtor: + { + DelegateCreationInfo createInfo = (DelegateCreationInfo)_target; + if (createInfo.NeedsVirtualMethodUseTracking) + { + MethodDesc instantiatedTargetMethod = createInfo.TargetMethod.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation); + if (!factory.VTable(instantiatedTargetMethod.OwningType).HasFixedSlots) + { + result.Add( + new DependencyListEntry( + factory.VirtualMethodUse(instantiatedTargetMethod), + "Dictionary dependency")); + } + + factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref result, factory, instantiatedTargetMethod); + } + } + break; + } + + try + { + // All generic lookups depend on the thing they point to + result.Add(new DependencyListEntry( + _lookupSignature.GetTarget(factory, lookupContext), + "Dictionary dependency")); + } + catch (TypeSystemException) + { + // If there was an exception, we're going to generate a null slot in the associated + // dictionary. The helper needs to be able to handle a null slot and tailcall + // and exception throwing helper instead of returning a result. + _hasInvalidEntries = true; + result.Add(GetBadSlotHelper(factory), "Failure to build dictionary slot"); + } + + return result.ToArray(); + } + + private static IMethodNode GetBadSlotHelper(NodeFactory factory) + { + return factory.MethodEntrypoint(factory.TypeSystemContext.GetHelperEntryPoint("ThrowHelpers", "ThrowUnavailableType")); + } + + protected void AppendLookupSignatureMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + if (_id != ReadyToRunHelperId.DelegateCtor) + { + _lookupSignature.AppendMangledName(nameMangler, sb); + } + else + { + ((DelegateCreationInfo)_target).AppendMangledName(nameMangler, sb); + } + } + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + + if (_dictionaryOwner is TypeDesc type) + { + // The generic lookup will need to consult the vtable of the owning type to find the + // vtable slot where the generic dictionary is placed - report the dependency. + dependencies.Add(factory.VTable(type), "Owning type vtable"); + } + + dependencies.Add(factory.GenericDictionaryLayout(_dictionaryOwner), "Layout"); + + foreach (DependencyNodeCore dependency in _lookupSignature.NonRelocDependenciesFromUsage(factory)) + { + dependencies.Add(new DependencyListEntry(dependency, "GenericLookupResultDependency")); + } + + return dependencies; + } + + public override bool HasConditionalStaticDependencies => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + List conditionalDependencies = new List(); + NativeLayoutSavedVertexNode templateLayout; + if (_dictionaryOwner is MethodDesc) + { + templateLayout = factory.NativeLayout.TemplateMethodLayout((MethodDesc)_dictionaryOwner); + conditionalDependencies.Add(new CombinedDependencyListEntry(_lookupSignature.TemplateDictionaryNode(factory), + templateLayout, + "Type loader template")); + } + else + { + templateLayout = factory.NativeLayout.TemplateTypeLayout((TypeDesc)_dictionaryOwner); + conditionalDependencies.Add(new CombinedDependencyListEntry(_lookupSignature.TemplateDictionaryNode(factory), + templateLayout, + "Type loader template")); + } + + if (_id == ReadyToRunHelperId.GetGCStaticBase || _id == ReadyToRunHelperId.GetThreadStaticBase) + { + // If the type has a lazy static constructor, we also need the non-GC static base to be available as + // a template dictionary node. + TypeDesc type = (TypeDesc)_target; + Debug.Assert(templateLayout != null); + if (factory.PreinitializationManager.HasLazyStaticConstructor(type)) + { + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(type); + conditionalDependencies.Add(new CombinedDependencyListEntry(nonGcRegionLookup.TemplateDictionaryNode(factory), + templateLayout, + "Type loader template")); + } + } + + return conditionalDependencies; + } + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + var compare = _id.CompareTo(((ReadyToRunGenericHelperNode)other)._id); + if (compare != 0) + return compare; + + if (_dictionaryOwner is MethodDesc) + { + if (((ReadyToRunGenericHelperNode)other)._dictionaryOwner is TypeDesc) + return -1; + + compare = comparer.Compare((MethodDesc)_dictionaryOwner, (MethodDesc)((ReadyToRunGenericHelperNode)other)._dictionaryOwner); + } + else + { + if (((ReadyToRunGenericHelperNode)other)._dictionaryOwner is MethodDesc) + return 1; + + compare = comparer.Compare((TypeDesc)_dictionaryOwner, (TypeDesc)((ReadyToRunGenericHelperNode)other)._dictionaryOwner); + } + + if (compare != 0) + return compare; + + switch (_id) + { + case ReadyToRunHelperId.TypeHandle: + case ReadyToRunHelperId.GetGCStaticBase: + case ReadyToRunHelperId.GetNonGCStaticBase: + case ReadyToRunHelperId.GetThreadStaticBase: + case ReadyToRunHelperId.DefaultConstructor: + case ReadyToRunHelperId.ObjectAllocator: + return comparer.Compare((TypeDesc)_target, (TypeDesc)((ReadyToRunGenericHelperNode)other)._target); + case ReadyToRunHelperId.MethodHandle: + case ReadyToRunHelperId.MethodDictionary: + case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.MethodEntry: + return comparer.Compare((MethodDesc)_target, (MethodDesc)((ReadyToRunGenericHelperNode)other)._target); + case ReadyToRunHelperId.FieldHandle: + return comparer.Compare((FieldDesc)_target, (FieldDesc)((ReadyToRunGenericHelperNode)other)._target); + case ReadyToRunHelperId.DelegateCtor: + return ((DelegateCreationInfo)_target).CompareTo((DelegateCreationInfo)((ReadyToRunGenericHelperNode)other)._target, comparer); + default: + throw new NotImplementedException(); + } + } + } + + public partial class ReadyToRunGenericLookupFromDictionaryNode : ReadyToRunGenericHelperNode + { + public ReadyToRunGenericLookupFromDictionaryNode(NodeFactory factory, ReadyToRunHelperId helperId, object target, TypeSystemEntity dictionaryOwner) + : base(factory, helperId, target, dictionaryOwner) + { + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + Utf8String mangledContextName; + if (_dictionaryOwner is MethodDesc) + mangledContextName = nameMangler.GetMangledMethodName((MethodDesc)_dictionaryOwner); + else + mangledContextName = nameMangler.GetMangledTypeName((TypeDesc)_dictionaryOwner); + + sb.Append("__GenericLookupFromDict_").Append(mangledContextName).Append("_"); + AppendLookupSignatureMangledName(nameMangler, sb); + } + + public override int ClassCode => 1055354299; + } + + public partial class ReadyToRunGenericLookupFromTypeNode : ReadyToRunGenericHelperNode + { + public ReadyToRunGenericLookupFromTypeNode(NodeFactory factory, ReadyToRunHelperId helperId, object target, TypeSystemEntity dictionaryOwner) + : base(factory, helperId, target, dictionaryOwner) + { + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + Utf8String mangledContextName; + if (_dictionaryOwner is MethodDesc) + mangledContextName = nameMangler.GetMangledMethodName((MethodDesc)_dictionaryOwner); + else + mangledContextName = nameMangler.GetMangledTypeName((TypeDesc)_dictionaryOwner); + + sb.Append("__GenericLookupFromType_").Append(mangledContextName).Append("_"); + AppendLookupSignatureMangledName(nameMangler, sb); + } + + public override int ClassCode => 913214059; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHeaderNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHeaderNode.cs new file mode 100644 index 00000000000000..49f9bafc032c12 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHeaderNode.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.Runtime; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class ReadyToRunHeaderNode : ObjectNode, ISymbolDefinitionNode + { + struct HeaderItem + { + public HeaderItem(ReadyToRunSectionType id, ObjectNode node, ISymbolNode startSymbol, ISymbolNode endSymbol) + { + Id = id; + Node = node; + StartSymbol = startSymbol; + EndSymbol = endSymbol; + } + + readonly public ReadyToRunSectionType Id; + readonly public ObjectNode Node; + readonly public ISymbolNode StartSymbol; + readonly public ISymbolNode EndSymbol; + } + + List _items = new List(); + TargetDetails _target; + + public ReadyToRunHeaderNode(TargetDetails target) + { + _target = target; + } + + public void Add(ReadyToRunSectionType id, ObjectNode node, ISymbolNode startSymbol, ISymbolNode endSymbol = null) + { + _items.Add(new HeaderItem(id, node, startSymbol, endSymbol)); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix); + sb.Append("__ReadyToRunHeader"); + } + public int Offset => 0; + public override bool IsShareable => false; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override bool StaticDependenciesAreComputed => true; + + public override ObjectNodeSection Section + { + get + { + if (_target.IsWindows) + return ObjectNodeSection.ReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.RequireInitialPointerAlignment(); + builder.AddSymbol(this); + + // Don't bother sorting if we're not emitting the contents + if (!relocsOnly) + _items.Sort((x, y) => Comparer.Default.Compare((int)x.Id, (int)y.Id)); + + // ReadyToRunHeader.Magic + builder.EmitInt((int)(ReadyToRunHeaderConstants.Signature)); + + // ReadyToRunHeader.MajorVersion + builder.EmitShort((short)(ReadyToRunHeaderConstants.CurrentMajorVersion)); + builder.EmitShort((short)(ReadyToRunHeaderConstants.CurrentMinorVersion)); + + // ReadyToRunHeader.Flags + builder.EmitInt(0); + + // ReadyToRunHeader.NumberOfSections + var sectionCountReservation = builder.ReserveShort(); + + // ReadyToRunHeader.EntrySize + builder.EmitByte((byte)(8 + 2 * factory.Target.PointerSize)); + + // ReadyToRunHeader.EntryType + builder.EmitByte(1); + + int count = 0; + foreach (var item in _items) + { + // Skip empty entries + if (!relocsOnly && item.Node.ShouldSkipEmittingObjectNode(factory)) + continue; + + builder.EmitInt((int)item.Id); + + ModuleInfoFlags flags = 0; + if (item.EndSymbol != null) + { + flags |= ModuleInfoFlags.HasEndPointer; + } + builder.EmitInt((int)flags); + + builder.EmitPointerReloc(item.StartSymbol); + + if (item.EndSymbol != null) + { + builder.EmitPointerReloc(item.EndSymbol); + } + else + { + builder.EmitZeroPointer(); + } + + count++; + } + builder.EmitShort(sectionCountReservation, checked((short)count)); + + return builder.ToObjectData(); + } + + public override int ClassCode => -534800244; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs new file mode 100644 index 00000000000000..77c17f1804ca5e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs @@ -0,0 +1,226 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + public enum ReadyToRunHelperId + { + Invalid, + NewHelper, + NewArr1, + VirtualCall, + IsInstanceOf, + CastClass, + GetNonGCStaticBase, + GetGCStaticBase, + GetThreadStaticBase, + GetThreadNonGcStaticBase, + DelegateCtor, + ResolveVirtualFunction, + CctorTrigger, + + // The following helpers are used for generic lookups only + TypeHandle, + NecessaryTypeHandle, + DeclaringTypeHandle, + MethodHandle, + FieldHandle, + MethodDictionary, + TypeDictionary, + MethodEntry, + VirtualDispatchCell, + DefaultConstructor, + TypeHandleForCasting, + ObjectAllocator, + } + + public partial class ReadyToRunHelperNode : AssemblyStubNode, INodeWithDebugInfo + { + private readonly ReadyToRunHelperId _id; + private readonly Object _target; + + public ReadyToRunHelperNode(ReadyToRunHelperId id, Object target) + { + _id = id; + _target = target; + + switch (id) + { + case ReadyToRunHelperId.GetNonGCStaticBase: + case ReadyToRunHelperId.GetGCStaticBase: + case ReadyToRunHelperId.GetThreadStaticBase: + { + // Make sure we can compute static field layout now so we can fail early + DefType defType = (DefType)target; + defType.ComputeStaticFieldLayout(StaticLayoutKind.StaticRegionSizesAndFields); + } + break; + case ReadyToRunHelperId.VirtualCall: + case ReadyToRunHelperId.ResolveVirtualFunction: + { + // Make sure we aren't trying to callvirt Object.Finalize + MethodDesc method = (MethodDesc)target; + if (method.IsFinalizer) + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramCallVirtFinalize, method); + + // Method should be in fully canonical form. Otherwise we're being wasteful and generate more + // helpers than needed. + Debug.Assert(!method.IsCanonicalMethod(CanonicalFormKind.Any) || + method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method); + } + break; + } + } + + protected override bool IsVisibleFromManagedCode => false; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public ReadyToRunHelperId Id => _id; + public Object Target => _target; + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + switch (_id) + { + case ReadyToRunHelperId.VirtualCall: + sb.Append("__VirtualCall_").Append(nameMangler.GetMangledMethodName((MethodDesc)_target)); + break; + case ReadyToRunHelperId.GetNonGCStaticBase: + sb.Append("__GetNonGCStaticBase_").Append(nameMangler.GetMangledTypeName((TypeDesc)_target)); + break; + case ReadyToRunHelperId.GetGCStaticBase: + sb.Append("__GetGCStaticBase_").Append(nameMangler.GetMangledTypeName((TypeDesc)_target)); + break; + case ReadyToRunHelperId.GetThreadStaticBase: + sb.Append("__GetThreadStaticBase_").Append(nameMangler.GetMangledTypeName((TypeDesc)_target)); + break; + case ReadyToRunHelperId.DelegateCtor: + ((DelegateCreationInfo)_target).AppendMangledName(nameMangler, sb); + break; + case ReadyToRunHelperId.ResolveVirtualFunction: + sb.Append("__ResolveVirtualFunction_"); + sb.Append(nameMangler.GetMangledMethodName((MethodDesc)_target)); + break; + default: + throw new NotImplementedException(); + } + } + + public bool IsStateMachineMoveNextMethod => false; + + public override bool IsShareable => true; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + if (_id == ReadyToRunHelperId.VirtualCall || _id == ReadyToRunHelperId.ResolveVirtualFunction) + { + var targetMethod = (MethodDesc)_target; + + DependencyList dependencyList = new DependencyList(); + +#if !SUPPORT_JIT + factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref dependencyList, factory, targetMethod); + + if (!factory.VTable(targetMethod.OwningType).HasFixedSlots) + + { + dependencyList.Add(factory.VirtualMethodUse((MethodDesc)_target), "ReadyToRun Virtual Method Call"); + } +#endif + + return dependencyList; + } + else if (_id == ReadyToRunHelperId.DelegateCtor) + { + var info = (DelegateCreationInfo)_target; + if (info.NeedsVirtualMethodUseTracking) + { + MethodDesc targetMethod = info.TargetMethod; + + DependencyList dependencyList = new DependencyList(); +#if !SUPPORT_JIT + factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref dependencyList, factory, targetMethod); + + if (!factory.VTable(info.TargetMethod.OwningType).HasFixedSlots) + { + dependencyList.Add(factory.VirtualMethodUse(info.TargetMethod), "ReadyToRun Delegate to virtual method"); + } +#endif + + return dependencyList; + } + } + + return null; + } + + IEnumerable INodeWithDebugInfo.GetNativeSequencePoints() + { + if (_id == ReadyToRunHelperId.VirtualCall) + { + // Generate debug information that lets debuggers step into the virtual calls. + // We generate a step into sequence point at the point where the helper jumps to + // the target of the virtual call. + TargetDetails target = ((MethodDesc)_target).Context.Target; + int debuggerStepInOffset = -1; + switch (target.Architecture) + { + case TargetArchitecture.X64: + debuggerStepInOffset = 3; + break; + } + if (debuggerStepInOffset != -1) + { + return new NativeSequencePoint[] + { + new NativeSequencePoint(0, String.Empty, WellKnownLineNumber.DebuggerStepThrough), + new NativeSequencePoint(debuggerStepInOffset, String.Empty, WellKnownLineNumber.DebuggerStepIn) + }; + } + } + + return Array.Empty(); + } + + IEnumerable INodeWithDebugInfo.GetDebugVars() + { + return Array.Empty(); + } + +#if !SUPPORT_JIT + public override int ClassCode => -911637948; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + var compare = _id.CompareTo(((ReadyToRunHelperNode)other)._id); + if (compare != 0) + return compare; + + switch (_id) + { + case ReadyToRunHelperId.GetNonGCStaticBase: + case ReadyToRunHelperId.GetGCStaticBase: + case ReadyToRunHelperId.GetThreadStaticBase: + return comparer.Compare((TypeDesc)_target, (TypeDesc)((ReadyToRunHelperNode)other)._target); + case ReadyToRunHelperId.VirtualCall: + case ReadyToRunHelperId.ResolveVirtualFunction: + return comparer.Compare((MethodDesc)_target, (MethodDesc)((ReadyToRunHelperNode)other)._target); + case ReadyToRunHelperId.DelegateCtor: + return ((DelegateCreationInfo)_target).CompareTo((DelegateCreationInfo)((ReadyToRunHelperNode)other)._target, comparer); + default: + throw new NotImplementedException(); + } + + } +#endif + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectableMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectableMethodNode.cs new file mode 100644 index 00000000000000..33ede223246121 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectableMethodNode.cs @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a method that is visible to reflection. + /// + public class ReflectableMethodNode : DependencyNodeCore + { + private readonly MethodDesc _method; + + public ReflectableMethodNode(MethodDesc method) + { + Debug.Assert(!method.IsCanonicalMethod(CanonicalFormKind.Any) || + method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method); + _method = method; + } + + public MethodDesc Method => _method; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + Debug.Assert(!factory.MetadataManager.IsReflectionBlocked(_method.GetTypicalMethodDefinition())); + + // Depends on static virtual method support. Turning off reflection for now. + if (_method.IsVirtual && _method.Signature.IsStatic) + return null; + + DependencyList dependencies = new DependencyList(); + factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencies, factory, _method); + + // No runtime artifacts needed if this is a generic definition + if (_method.IsGenericMethodDefinition || _method.OwningType.IsGenericDefinition) + { + return dependencies; + } + + // Ensure we consistently apply reflectability to all methods sharing the same definition. + // Different instantiations of the method have a conditional dependency on the definition node that + // brings a ReflectableMethod of the instantiated method if it's necessary for it to be reflectable. + MethodDesc typicalMethod = _method.GetTypicalMethodDefinition(); + if (typicalMethod != _method) + { + dependencies.Add(factory.ReflectableMethod(typicalMethod), "Definition of the reflectable method"); + } + + MethodDesc canonMethod = _method.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (canonMethod != _method) + { + dependencies.Add(factory.ReflectableMethod(canonMethod), "Canonical version of the reflectable method"); + } + + // Make sure we generate the method body and other artifacts. + if (MetadataManager.IsMethodSupportedInReflectionInvoke(_method)) + { + if (_method.IsVirtual) + { + if (_method.HasInstantiation) + { + dependencies.Add(factory.GVMDependencies(_method.GetCanonMethodTarget(CanonicalFormKind.Specific)), "GVM callable reflectable method"); + } + else + { + // Virtual method use is tracked on the slot defining method only. + MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(_method); + if (!factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots) + dependencies.Add(factory.VirtualMethodUse(slotDefiningMethod), "Virtually callable reflectable method"); + } + } + + if (!_method.IsAbstract) + { + dependencies.Add(factory.MethodEntrypoint(canonMethod), "Body of a reflectable method"); + + if (_method.HasInstantiation + && _method != canonMethod) + dependencies.Add(factory.MethodGenericDictionary(_method), "Dictionary of a reflectable method"); + } + } + + return dependencies; + } + protected override string GetName(NodeFactory factory) + { + return "Reflectable method: " + _method.ToString(); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs new file mode 100644 index 00000000000000..4c88bd1ffa4022 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +using FieldTableFlags = Internal.Runtime.FieldTableFlags; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a map between reflection metadata and native field offsets. + /// + internal sealed class ReflectionFieldMapNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public ReflectionFieldMapNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__field_to_offset_map_End", true); + _externalReferences = externalReferences; + } + + public ISymbolNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__field_to_offset_map"); + } + + public int Offset => 0; + public override bool IsShareable => false; + + public override ObjectNodeSection Section => _externalReferences.Section; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + var writer = new NativeWriter(); + var fieldMapHashTable = new VertexHashtable(); + + Section hashTableSection = writer.NewSection(); + hashTableSection.Place(fieldMapHashTable); + + foreach (var fieldMapping in factory.MetadataManager.GetFieldMapping(factory)) + { + FieldDesc field = fieldMapping.Entity; + + if (field.IsLiteral || field.HasRva) + continue; + + // CppCodegen: implement thread statics + if (factory.Target.Abi == TargetAbi.CppCodegen && field.IsThreadStatic) + continue; + + FieldTableFlags flags; + if (field.IsStatic) + { + if (field.IsThreadStatic) + flags = FieldTableFlags.ThreadStatic; + else if (field.HasGCStaticBase) + flags = FieldTableFlags.GCStatic; + else + flags = FieldTableFlags.NonGCStatic; + + if (field.OwningType.HasInstantiation) + flags |= FieldTableFlags.FieldOffsetEncodedDirectly; + } + else + { + flags = FieldTableFlags.Instance | FieldTableFlags.FieldOffsetEncodedDirectly; + } + + if (fieldMapping.MetadataHandle != 0) + flags |= FieldTableFlags.HasMetadataHandle; + + if (field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) + flags |= FieldTableFlags.IsAnyCanonicalEntry; + + if (field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + flags |= FieldTableFlags.IsUniversalCanonicalEntry; + + if (field.IsInitOnly) + flags |= FieldTableFlags.IsInitOnly; + + // Grammar of a hash table entry: + // Flags + DeclaringType + MdHandle or Name + Cookie or Ordinal or Offset + + Vertex vertex = writer.GetUnsignedConstant((uint)flags); + + uint declaringTypeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(field.OwningType)); + vertex = writer.GetTuple(vertex, + writer.GetUnsignedConstant(declaringTypeId)); + + if ((flags & FieldTableFlags.HasMetadataHandle) != 0) + { + // Only store the offset portion of the metadata handle to get better integer compression + vertex = writer.GetTuple(vertex, + writer.GetUnsignedConstant((uint)(fieldMapping.MetadataHandle & MetadataManager.MetadataOffsetMask))); + } + else + { + // No metadata handle means we need to store name + vertex = writer.GetTuple(vertex, + writer.GetStringConstant(field.Name)); + } + + if ((flags & FieldTableFlags.IsUniversalCanonicalEntry) != 0) + { + vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant(checked((uint)field.GetFieldOrdinal()))); + } + else + { + switch (flags & FieldTableFlags.StorageClass) + { + case FieldTableFlags.ThreadStatic: + case FieldTableFlags.GCStatic: + case FieldTableFlags.NonGCStatic: + { + if (field.OwningType.HasInstantiation) + { + vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant((uint)(field.Offset.AsInt))); + } + else + { + MetadataType metadataType = (MetadataType)field.OwningType; + + ISymbolNode staticsNode; + if (field.IsThreadStatic) + staticsNode = factory.TypeThreadStaticIndex(metadataType); + else if (field.HasGCStaticBase) + staticsNode = factory.TypeGCStaticsSymbol(metadataType); + else + staticsNode = factory.TypeNonGCStaticsSymbol(metadataType); + + if (!field.IsThreadStatic && !field.HasGCStaticBase) + { + uint index = _externalReferences.GetIndex(staticsNode, field.Offset.AsInt); + vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant(index)); + } + else + { + uint index = _externalReferences.GetIndex(staticsNode); + vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant(index)); + vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant((uint)(field.Offset.AsInt))); + } + } + } + break; + + case FieldTableFlags.Instance: + vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant((uint)field.Offset.AsInt)); + break; + } + } + + int hashCode = field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific).GetHashCode(); + fieldMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.ReflectionFieldMapNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs new file mode 100644 index 00000000000000..8939430acd0c6a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs @@ -0,0 +1,243 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +using Debug = System.Diagnostics.Debug; +using InvokeTableFlags = Internal.Runtime.InvokeTableFlags; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a map between reflection metadata and generated method bodies. + /// + internal sealed class ReflectionInvokeMapNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public ReflectionInvokeMapNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__method_to_entrypoint_map_End", true); + _externalReferences = externalReferences; + } + + public ISymbolNode EndSymbol + { + get + { + return _endSymbol; + } + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__method_to_entrypoint_map"); + } + public int Offset => 0; + public override bool IsShareable => false; + + public override ObjectNodeSection Section => _externalReferences.Section; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public static void AddDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + Debug.Assert(factory.MetadataManager.IsReflectionInvokable(method)); + + if (dependencies == null) + dependencies = new DependencyList(); + + dependencies.Add(factory.MaximallyConstructableType(method.OwningType), "Reflection invoke"); + + if (factory.MetadataManager.HasReflectionInvokeStubForInvokableMethod(method)) + { + MethodDesc canonInvokeStub = factory.MetadataManager.GetCanonicalReflectionInvokeStub(method); + if (canonInvokeStub.IsSharedByGenericInstantiations) + { + dependencies.Add(new DependencyListEntry(factory.DynamicInvokeTemplate(canonInvokeStub), "Reflection invoke")); + } + else + dependencies.Add(new DependencyListEntry(factory.MethodEntrypoint(canonInvokeStub), "Reflection invoke")); + } + + if (method.OwningType.IsValueType && !method.Signature.IsStatic) + dependencies.Add(new DependencyListEntry(factory.ExactCallableAddress(method, isUnboxingStub: true), "Reflection unboxing stub")); + + // If the method is defined in a different module than this one, a metadata token isn't known for performing the reference + // Use a name/sig reference instead. + if (!factory.MetadataManager.WillUseMetadataTokenToReferenceMethod(method)) + { + dependencies.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition())), + "Non metadata-local method reference")); + } + + if (method.HasInstantiation) + { + bool useUnboxingStub = method.OwningType.IsValueType && !method.Signature.IsStatic; + + if (method.IsCanonicalMethod(CanonicalFormKind.Universal)) + { + dependencies.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(method)), + "UniversalCanon signature of method")); + } + else if (!method.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg() || method.IsAbstract) + { + foreach (var instArg in method.Instantiation) + { + dependencies.Add(factory.NecessaryTypeSymbol(instArg), "Reflectable generic method inst arg"); + } + } + } + + ReflectionVirtualInvokeMapNode.GetVirtualInvokeMapDependencies(ref dependencies, factory, method); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + // Ensure the native layout blob has been saved + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + var writer = new NativeWriter(); + var typeMapHashTable = new VertexHashtable(); + + Section hashTableSection = writer.NewSection(); + hashTableSection.Place(typeMapHashTable); + + // Get a list of all methods that have a method body and metadata from the metadata manager. + foreach (var mappingEntry in factory.MetadataManager.GetMethodMapping(factory)) + { + MethodDesc method = mappingEntry.Entity; + + if (!factory.MetadataManager.ShouldMethodBeInInvokeMap(method)) + continue; + + bool useUnboxingStub = method.OwningType.IsValueType && !method.Signature.IsStatic; + + InvokeTableFlags flags = 0; + + if (method.HasInstantiation) + flags |= InvokeTableFlags.IsGenericMethod; + + if (method.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg()) + { + bool goesThroughInstantiatingUnboxingThunk = method.OwningType.IsValueType && !method.Signature.IsStatic && !method.HasInstantiation; + if (!goesThroughInstantiatingUnboxingThunk) + flags |= InvokeTableFlags.RequiresInstArg; + } + + if (method.IsDefaultConstructor) + flags |= InvokeTableFlags.IsDefaultConstructor; + + if (ReflectionVirtualInvokeMapNode.NeedsVirtualInvokeInfo(method)) + flags |= InvokeTableFlags.HasVirtualInvoke; + + if (!method.IsAbstract) + flags |= InvokeTableFlags.HasEntrypoint; + + if (mappingEntry.MetadataHandle != 0) + flags |= InvokeTableFlags.HasMetadataHandle; + + if (!factory.MetadataManager.HasReflectionInvokeStubForInvokableMethod(method)) + flags |= InvokeTableFlags.NeedsParameterInterpretation; + + if (method.IsCanonicalMethod(CanonicalFormKind.Universal)) + flags |= InvokeTableFlags.IsUniversalCanonicalEntry; + + // TODO: native signature for P/Invokes and UnmanagedCallersOnly methods + if (method.IsRawPInvoke() || method.IsUnmanagedCallersOnly) + continue; + + // Grammar of an entry in the hash table: + // Flags + DeclaringType + MetadataHandle/NameAndSig + Entrypoint + DynamicInvokeMethod + [NumGenericArgs + GenericArgs] + + Vertex vertex = writer.GetUnsignedConstant((uint)flags); + + if ((flags & InvokeTableFlags.HasMetadataHandle) != 0) + { + // Only store the offset portion of the metadata handle to get better integer compression + vertex = writer.GetTuple(vertex, + writer.GetUnsignedConstant((uint)(mappingEntry.MetadataHandle & MetadataManager.MetadataOffsetMask))); + } + else + { + var nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition())); + vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset)); + } + + // Go with a necessary type symbol. It will be upgraded to a constructed one if a constructed was emitted. + IEETypeNode owningTypeSymbol = factory.NecessaryTypeSymbol(method.OwningType); + vertex = writer.GetTuple(vertex, + writer.GetUnsignedConstant(_externalReferences.GetIndex(owningTypeSymbol))); + + if ((flags & InvokeTableFlags.HasEntrypoint) != 0) + { + vertex = writer.GetTuple(vertex, + writer.GetUnsignedConstant(_externalReferences.GetIndex( + factory.MethodEntrypoint(method.GetCanonMethodTarget(CanonicalFormKind.Specific), useUnboxingStub)))); + } + + if ((flags & InvokeTableFlags.NeedsParameterInterpretation) == 0) + { + MethodDesc canonInvokeStubMethod = factory.MetadataManager.GetCanonicalReflectionInvokeStub(method); + if (canonInvokeStubMethod.IsSharedByGenericInstantiations) + { + vertex = writer.GetTuple(vertex, + writer.GetUnsignedConstant(((uint)factory.MetadataManager.DynamicInvokeTemplateData.GetIdForMethod(canonInvokeStubMethod, factory) << 1) | 1)); + } + else + { + vertex = writer.GetTuple(vertex, + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(canonInvokeStubMethod)) << 1)); + } + } + + if ((flags & InvokeTableFlags.IsGenericMethod) != 0) + { + if ((flags & InvokeTableFlags.IsUniversalCanonicalEntry) != 0) + { + var nameAndSigGenericMethod = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(method)); + vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant((uint)nameAndSigGenericMethod.SavedVertex.VertexOffset)); + } + else if ((flags & InvokeTableFlags.RequiresInstArg) == 0 || (flags & InvokeTableFlags.HasEntrypoint) == 0) + { + VertexSequence args = new VertexSequence(); + for (int i = 0; i < method.Instantiation.Length; i++) + { + uint argId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(method.Instantiation[i])); + args.Append(writer.GetUnsignedConstant(argId)); + } + vertex = writer.GetTuple(vertex, args); + } + else + { + uint dictionaryId = _externalReferences.GetIndex(factory.MethodGenericDictionary(method)); + vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant(dictionaryId)); + } + } + + int hashCode = method.GetCanonMethodTarget(CanonicalFormKind.Specific).OwningType.GetHashCode(); + typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.ReflectionInvokeMapNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionMethodBodyScanner.cs new file mode 100644 index 00000000000000..6c663d149b3283 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionMethodBodyScanner.cs @@ -0,0 +1,103 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; + +using AssemblyName = System.Reflection.AssemblyName; +using StringBuilder = System.Text.StringBuilder; + +namespace ILCompiler.DependencyAnalysis +{ + internal static class ReflectionMethodBodyScanner + { + public static bool ResolveType(string name, ModuleDesc callingModule, TypeSystemContext context, out TypeDesc type, out ModuleDesc referenceModule) + { + // This can do enough resolution to resolve "Foo" or "Foo, Assembly, PublicKeyToken=...". + // The reflection resolution rules are complicated. This is only needed for a heuristic, + // not for correctness, so this shortcut is okay. + + type = null; + referenceModule = null; + + int i = 0; + + // Consume type name part + StringBuilder typeName = new StringBuilder(); + StringBuilder typeNamespace = new StringBuilder(); + string containingTypeName = null; + while (i < name.Length && (char.IsLetterOrDigit(name[i]) || name[i] == '.' || name[i] == '`' || name[i] == '+')) + { + if (name[i] == '.') + { + if (typeNamespace.Length > 0) + typeNamespace.Append('.'); + typeNamespace.Append(typeName); + typeName.Clear(); + } + else if (name[i] == '+') + { + containingTypeName = typeName.ToString(); + typeName.Clear(); + } + else + { + typeName.Append(name[i]); + } + i++; + } + + string nestedTypeName = null; + if (containingTypeName != null) + { + nestedTypeName = typeName.ToString(); + typeName = new StringBuilder(containingTypeName); + } + + // Consume any comma or white space + while (i < name.Length && (name[i] == ' ' || name[i] == ',')) + { + i++; + } + + // Consume assembly name + StringBuilder assemblyName = new StringBuilder(); + while (i < name.Length && (char.IsLetterOrDigit(name[i]) || name[i] == '.')) + { + assemblyName.Append(name[i]); + i++; + } + + // If the name was assembly-qualified, resolve the assembly + // If it wasn't qualified, we resolve in the calling assembly + + referenceModule = callingModule; + if (assemblyName.Length > 0) + { + referenceModule = context.ResolveAssembly(new AssemblyName(assemblyName.ToString()), false); + } + + if (referenceModule == null) + return false; + + // Resolve type in the assembly + MetadataType mdType = referenceModule.GetType(typeNamespace.ToString(), typeName.ToString(), throwIfNotFound: false); + if (mdType != null && nestedTypeName != null) + mdType = mdType.GetNestedType(nestedTypeName); + + // If it didn't resolve and wasn't assembly-qualified, we also try core library + if (mdType == null && assemblyName.Length == 0) + { + referenceModule = context.SystemModule; + mdType = referenceModule.GetType(typeNamespace.ToString(), typeName.ToString(), throwIfNotFound: false); + if (mdType != null && nestedTypeName != null) + mdType = mdType.GetNestedType(nestedTypeName); + } + + type = mdType; + + return type != null; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs new file mode 100644 index 00000000000000..d724d02eda742d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs @@ -0,0 +1,219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +using VirtualInvokeTableEntry = Internal.Runtime.VirtualInvokeTableEntry; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a map containing the necessary information needed to resolve + /// a virtual method target called through reflection. + /// + internal sealed class ReflectionVirtualInvokeMapNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public ReflectionVirtualInvokeMapNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__VirtualInvokeMap_End", true); + _externalReferences = externalReferences; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__VirtualInvokeMap"); + } + + public ISymbolNode EndSymbol => _endSymbol; + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public static bool NeedsVirtualInvokeInfo(MethodDesc method) + { + if (!method.IsVirtual) + return false; + + if (method.IsFinal) + return false; + + if (method.OwningType.IsSealed()) + return false; + + return true; + } + + public static MethodDesc GetDeclaringVirtualMethodAndHierarchyDistance(MethodDesc method, out int parentHierarchyDistance) + { + parentHierarchyDistance = 0; + + MethodDesc declaringMethodForSlot = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method.GetTypicalMethodDefinition()); + TypeDesc typeOfDeclaringMethodForSlot = declaringMethodForSlot.OwningType.GetTypeDefinition(); + TypeDesc currentType = method.OwningType.GetTypeDefinition(); + TypeDesc containingTypeOfDeclaringMethodForSlot = method.OwningType; + + while (typeOfDeclaringMethodForSlot != currentType) + { + parentHierarchyDistance++; + currentType = currentType.BaseType.GetTypeDefinition(); + containingTypeOfDeclaringMethodForSlot = containingTypeOfDeclaringMethodForSlot.BaseType; + } + + if (containingTypeOfDeclaringMethodForSlot.HasInstantiation) + { + declaringMethodForSlot = method.Context.GetMethodForInstantiatedType( + declaringMethodForSlot.GetTypicalMethodDefinition(), + (InstantiatedType)containingTypeOfDeclaringMethodForSlot); + } + + Debug.Assert(declaringMethodForSlot != null); + + return declaringMethodForSlot; + } + + public static void GetVirtualInvokeMapDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + if (NeedsVirtualInvokeInfo(method)) + { + dependencies = dependencies ?? new DependencyList(); + + dependencies.Add( + factory.NecessaryTypeSymbol(method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific)), + "Reflection virtual invoke owning type"); + + NativeLayoutMethodNameAndSignatureVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition()); + NativeLayoutPlacedSignatureVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig); + dependencies.Add(placedNameAndSig, "Reflection virtual invoke method signature"); + + if (!method.HasInstantiation) + { + MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); + if (!factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots) + { + dependencies.Add(factory.VirtualMethodUse(slotDefiningMethod), "Reflection virtual invoke method"); + } + } + } + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + // Ensure the native layout blob has been saved + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + var writer = new NativeWriter(); + var typeMapHashTable = new VertexHashtable(); + + Section hashTableSection = writer.NewSection(); + hashTableSection.Place(typeMapHashTable); + + Dictionary> methodsEmitted = new Dictionary>(); + + // Get a list of all methods that have a method body and metadata from the metadata manager. + foreach (var mappingEntry in factory.MetadataManager.GetMethodMapping(factory)) + { + MethodDesc method = mappingEntry.Entity; + + // The current format requires us to have an MethodTable for the owning type. We might want to lift this. + if (!factory.MetadataManager.TypeGeneratesEEType(method.OwningType)) + continue; + + // We have a method body, we have a metadata token, but we can't get an invoke stub. Bail. + if (!factory.MetadataManager.IsReflectionInvokable(method)) + continue; + + // Only virtual methods are interesting + if (!NeedsVirtualInvokeInfo(method)) + continue; + + // + // The vtable entries for each instantiated type might not necessarily exist. + // Example 1: + // If there's a call to Foo.Method1 and a call to Foo.Method2, Foo will + // not have Method2 in its vtable and Foo will not have Method1. + // Example 2: + // If there's a call to Foo.Method1 and a call to Foo.Method2, given that both + // of these instantiations share the same canonical form, Foo<__Canon> will have both method + // entries, and therefore Foo and Foo will have both entries too. + // For this reason, the entries that we write to the map in CoreRT will be based on the canonical form + // of the method's containing type instead of the open type definition. + // + + TypeDesc containingTypeKey = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); + + HashSet cache; + if (!methodsEmitted.TryGetValue(mappingEntry.MetadataHandle, out cache)) + methodsEmitted[mappingEntry.MetadataHandle] = cache = new HashSet(); + + // Only one record is needed for any instantiation. + if (!cache.Add(containingTypeKey)) + continue; + + // Grammar of an entry in the hash table: + // Virtual Method uses a normal slot + // TypeKey + NameAndSig metadata offset into the native layout metadata + (NumberOfStepsUpParentHierarchyToType << 1) + slot + // OR + // Generic Virtual Method + // TypeKey + NameAndSig metadata offset into the native layout metadata + (NumberOfStepsUpParentHierarchyToType << 1 + 1) + + int parentHierarchyDistance; + MethodDesc declaringMethodForSlot = GetDeclaringVirtualMethodAndHierarchyDistance(method, out parentHierarchyDistance); + + Vertex vertex = null; + + ISymbolNode containingTypeKeyNode = factory.NecessaryTypeSymbol(containingTypeKey); + NativeLayoutMethodNameAndSignatureVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition()); + NativeLayoutPlacedSignatureVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig); + + if (method.HasInstantiation) + { + vertex = writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(containingTypeKeyNode)), + writer.GetUnsignedConstant((uint)placedNameAndSig.SavedVertex.VertexOffset), + writer.GetUnsignedConstant(((uint)parentHierarchyDistance << 1) + VirtualInvokeTableEntry.GenericVirtualMethod)); + } + else + { + // Get the declaring method for slot on the instantiated declaring type + int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot, declaringMethodForSlot.OwningType, true); + Debug.Assert(slot != -1); + + vertex = writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(containingTypeKeyNode)), + writer.GetUnsignedConstant((uint)placedNameAndSig.SavedVertex.VertexOffset)); + + vertex = writer.GetTuple(vertex, + writer.GetUnsignedConstant((uint)parentHierarchyDistance << 1), + writer.GetUnsignedConstant((uint)slot)); + } + + int hashCode = containingTypeKey.GetHashCode(); + typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.ReflectionVirtualInvokeMapNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ResourceDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ResourceDataNode.cs new file mode 100644 index 00000000000000..3b2e7577f4aac5 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ResourceDataNode.cs @@ -0,0 +1,198 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Blob of data containing resources for all assemblies generated into the image. + /// Resources are simply copied from the inputs and concatenated into this blob. + /// All format information is provided by + /// + internal class ResourceDataNode : ObjectNode, ISymbolDefinitionNode + { + /// + /// Resource index information generated while extracting resources into the data blob + /// + private List _indexData; + private int _totalLength; + + public ResourceDataNode() + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__embedded_resourcedata_End", true); + } + + private ObjectAndOffsetSymbolNode _endSymbol; + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public override bool IsShareable => false; + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__embedded_resourcedata"); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node has no relocations. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + byte[] blob = GenerateResourceBlob(factory); + return new ObjectData( + blob, + Array.Empty(), + 1, + new ISymbolDefinitionNode[] + { + this, + EndSymbol + }); + } + + public IReadOnlyList GetOrCreateIndexData(NodeFactory factory) + { + if (_indexData != null) + { + return _indexData; + } + + _totalLength = 0; + _indexData = new List(); + // Build up index information + foreach (EcmaAssembly module in factory.MetadataManager.GetCompilationModulesWithMetadata().OfType()) + { + PEMemoryBlock resourceDirectory = module.PEReader.GetSectionData(module.PEReader.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress); + + try + { + checked + { + foreach (var resourceHandle in module.MetadataReader.ManifestResources) + { + ManifestResource resource = module.MetadataReader.GetManifestResource(resourceHandle); + + // Don't try to embed linked resources or resources in other assemblies + if (!resource.Implementation.IsNil) + { + continue; + } + + string resourceName = module.MetadataReader.GetString(resource.Name); + + // Check if emitting the manifest resource is blocked by policy. + if (factory.MetadataManager.IsManifestResourceBlocked(module, resourceName)) + continue; + + string assemblyName = module.GetName().FullName; + BlobReader reader = resourceDirectory.GetReader((int)resource.Offset, resourceDirectory.Length - (int)resource.Offset); + int length = (int)reader.ReadUInt32(); + ResourceIndexData indexData = new ResourceIndexData(assemblyName, resourceName, _totalLength, (int)resource.Offset + sizeof(Int32), module, length); + _indexData.Add(indexData); + _totalLength += length; + } + } + } + catch (OverflowException) + { + throw new BadImageFormatException(); + } + } + + return _indexData; + } + + /// + /// Extracts resources from all modules being compiled into a single blob and saves + /// the information needed to create an index into that blob. + /// + private byte[] GenerateResourceBlob(NodeFactory factory) + { + GetOrCreateIndexData(factory); + + // Read resources into the blob + byte[] resourceBlob = new byte[_totalLength]; + int currentPos = 0; + foreach (ResourceIndexData indexData in _indexData) + { + EcmaModule module = indexData.EcmaModule; + PEMemoryBlock resourceDirectory = module.PEReader.GetSectionData(module.PEReader.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress); + Debug.Assert(currentPos == indexData.NativeOffset); + BlobReader reader = resourceDirectory.GetReader(indexData.EcmaOffset, indexData.Length); + byte[] resourceData = reader.ReadBytes(indexData.Length); + Buffer.BlockCopy(resourceData, 0, resourceBlob, currentPos, resourceData.Length); + currentPos += resourceData.Length; + } + + _endSymbol.SetSymbolOffset(resourceBlob.Length); + return resourceBlob; + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.ResourceDataNode; + } + + /// + /// Data about individual manifest resources + /// + internal class ResourceIndexData + { + public ResourceIndexData(string assemblyName, string resourceName, int nativeOffset, int ecmaOffset, EcmaModule ecmaModule, int length) + { + AssemblyName = assemblyName; + ResourceName = resourceName; + NativeOffset = nativeOffset; + EcmaOffset = ecmaOffset; + EcmaModule = ecmaModule; + Length = length; + } + + /// + /// Full name of the assembly that contains the resource + /// + public string AssemblyName { get; } + + /// + /// Name of the resource + /// + public string ResourceName { get; } + + /// + /// Offset of the resource within the native resource blob + /// + public int NativeOffset { get; } + + /// + /// Offset of the resource within the .mresources section of the ECMA module + /// + public int EcmaOffset { get; } + + /// + /// Module the resource is defined in + /// + public EcmaModule EcmaModule { get; } + + /// + /// Length of the resource + /// + public int Length { get; } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ResourceIndexNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ResourceIndexNode.cs new file mode 100644 index 00000000000000..e1e7b2d3af90c4 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ResourceIndexNode.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.NativeFormat; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a hash table of resources within the resource blob in the image. + /// + internal class ResourceIndexNode : ObjectNode, ISymbolDefinitionNode + { + private ResourceDataNode _resourceDataNode; + + public ResourceIndexNode(ResourceDataNode resourceDataNode) + { + _resourceDataNode = resourceDataNode; + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__embedded_resourceindex_End", true); + } + + private ObjectAndOffsetSymbolNode _endSymbol; + + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public override bool IsShareable => false; + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__embedded_resourceindex"); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node has no relocations. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + byte[] blob = GenerateIndexBlob(factory); + return new ObjectData( + blob, + Array.Empty(), + 1, + new ISymbolDefinitionNode[] + { + this, + EndSymbol + }); + } + + /// + /// Builds a native hashtable containing data about each manifest resource + /// + /// + private byte[] GenerateIndexBlob(NodeFactory factory) + { + NativeWriter nativeWriter = new NativeWriter(); + Section indexHashtableSection = nativeWriter.NewSection(); + VertexHashtable indexHashtable = new VertexHashtable(); + indexHashtableSection.Place(indexHashtable); + + // Build a table with a tuple of Assembly Full Name, Resource Name, Offset within the resource data blob, Length + // for each resource. + // This generates a hashtable for the convenience of managed code since there's + // a reader for VertexHashtable, but not for VertexSequence. + + foreach (ResourceIndexData indexData in _resourceDataNode.GetOrCreateIndexData(factory)) + { + Vertex asmName = nativeWriter.GetStringConstant(indexData.AssemblyName); + Vertex resourceName = nativeWriter.GetStringConstant(indexData.ResourceName); + Vertex offsetVertex = nativeWriter.GetUnsignedConstant((uint)indexData.NativeOffset); + Vertex lengthVertex = nativeWriter.GetUnsignedConstant((uint)indexData.Length); + + Vertex indexVertex = nativeWriter.GetTuple(asmName, resourceName); + indexVertex = nativeWriter.GetTuple(indexVertex, offsetVertex); + indexVertex = nativeWriter.GetTuple(indexVertex, lengthVertex); + + int hashCode = TypeHashingAlgorithms.ComputeNameHashCode(indexData.AssemblyName); + indexHashtable.Append((uint)hashCode, indexHashtableSection.Place(indexVertex)); + } + + byte[] blob = nativeWriter.Save(); + _endSymbol.SetSymbolOffset(blob.Length); + return blob; + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.ResourceIndexNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeFieldHandleNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeFieldHandleNode.cs new file mode 100644 index 00000000000000..b681431bcae1ba --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeFieldHandleNode.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class RuntimeFieldHandleNode : ObjectNode, ISymbolDefinitionNode + { + private FieldDesc _targetField; + + public RuntimeFieldHandleNode(FieldDesc targetField) + { + Debug.Assert(!targetField.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)); + Debug.Assert(!targetField.OwningType.IsRuntimeDeterminedSubtype); + _targetField = targetField; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix) + .Append("__RuntimeFieldHandle_") + .Append(nameMangler.GetMangledFieldName(_targetField)); + } + public int Offset => 0; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + public override bool IsShareable => false; + public override bool StaticDependenciesAreComputed => true; + + public override ObjectNodeSection Section + { + get + { + if (_targetField.Context.Target.IsWindows) + return ObjectNodeSection.ReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + + private static Utf8String s_NativeLayoutSignaturePrefix = new Utf8String("__RFHSignature_"); + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList result = null; + factory.MetadataManager.GetDependenciesDueToLdToken(ref result, factory, _targetField); + return result; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + + objData.RequireInitialPointerAlignment(); + objData.AddSymbol(this); + + NativeLayoutFieldLdTokenVertexNode ldtokenSigNode = factory.NativeLayout.FieldLdTokenVertex(_targetField); + objData.EmitPointerReloc(factory.NativeLayout.NativeLayoutSignature(ldtokenSigNode, s_NativeLayoutSignaturePrefix, _targetField)); + + return objData.ToObjectData(); + } + + public override int ClassCode => -1326215725; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_targetField, ((RuntimeFieldHandleNode)other)._targetField); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeImportMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeImportMethodNode.cs new file mode 100644 index 00000000000000..014e501d18c3de --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeImportMethodNode.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a method that is imported from the runtime library. + /// + public class RuntimeImportMethodNode : ExternSymbolNode, IMethodNode, ISymbolDefinitionNode + { + private MethodDesc _method; + + public RuntimeImportMethodNode(MethodDesc method) + : base(((EcmaMethod)method).GetRuntimeImportName()) + { + _method = method; + } + + public MethodDesc Method + { + get + { + return _method; + } + } + + public override int ClassCode => -1173492615; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_method, ((RuntimeImportMethodNode)other)._method); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs new file mode 100644 index 00000000000000..5a12f283cd9950 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class RuntimeMethodHandleNode : ObjectNode, ISymbolDefinitionNode + { + private MethodDesc _targetMethod; + + public MethodDesc Method => _targetMethod; + + public RuntimeMethodHandleNode(MethodDesc targetMethod) + { + Debug.Assert(!targetMethod.IsSharedByGenericInstantiations); + + // IL is allowed to LDTOKEN an uninstantiated thing. Do not check IsRuntimeDetermined for the nonexact thing. + Debug.Assert((targetMethod.HasInstantiation && targetMethod.IsMethodDefinition) + || targetMethod.OwningType.IsGenericDefinition + || !targetMethod.IsRuntimeDeterminedExactMethod); + _targetMethod = targetMethod; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix) + .Append("__RuntimeMethodHandle_") + .Append(nameMangler.GetMangledMethodName(_targetMethod)); + } + public int Offset => 0; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + public override bool IsShareable => false; + public override bool StaticDependenciesAreComputed => true; + + public override ObjectNodeSection Section + { + get + { + if (_targetMethod.Context.Target.IsWindows) + return ObjectNodeSection.ReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencies = null; + + if (!_targetMethod.IsMethodDefinition && !_targetMethod.OwningType.IsGenericDefinition + && _targetMethod.HasInstantiation && _targetMethod.IsVirtual) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.GVMDependencies(_targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)), "GVM dependencies for runtime method handle"); + } + + factory.MetadataManager.GetDependenciesDueToLdToken(ref dependencies, factory, _targetMethod); + + return dependencies; + } + + private static Utf8String s_NativeLayoutSignaturePrefix = new Utf8String("__RMHSignature_"); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + + objData.RequireInitialPointerAlignment(); + objData.AddSymbol(this); + + NativeLayoutMethodLdTokenVertexNode ldtokenSigNode = factory.NativeLayout.MethodLdTokenVertex(_targetMethod); + objData.EmitPointerReloc(factory.NativeLayout.NativeLayoutSignature(ldtokenSigNode, s_NativeLayoutSignaturePrefix, _targetMethod)); + + return objData.ToObjectData(); + } + + public override int ClassCode => -274400625; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_targetMethod, ((RuntimeMethodHandleNode)other)._targetMethod); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedMethodNode.cs new file mode 100644 index 00000000000000..b9d8d941163d01 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedMethodNode.cs @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.Text; +using Internal.TypeSystem; + +using CombinedDependencyList = System.Collections.Generic.List.CombinedDependencyListEntry>; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a method that should be scanned by an IL scanner and its dependencies + /// analyzed. + /// + public class ScannedMethodNode : DependencyNodeCore, IMethodBodyNode + { + private readonly MethodDesc _method; + private DependencyList _dependencies; + + // If we failed to scan the method, the dependencies reported by the node will + // be for a throwing method body. This field will store the underlying cause of the failure. + private TypeSystemException _exception; + + public ScannedMethodNode(MethodDesc method) + { + Debug.Assert(!method.IsAbstract); + Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method); + _method = method; + } + + public MethodDesc Method => _method; + + public TypeSystemException Exception => _exception; + + public int Offset => 0; + + public bool RepresentsIndirectionCell => false; + + public override bool StaticDependenciesAreComputed => _dependencies != null; + + public void InitializeDependencies(NodeFactory factory, IEnumerable dependencies, TypeSystemException scanningException = null) + { + _dependencies = new DependencyList(dependencies); + + if (factory.TypeSystemContext.IsSpecialUnboxingThunk(_method)) + { + // Special unboxing thunks reference a MethodAssociatedDataNode that points to the non-unboxing version. + // This dependency is redundant with the dependency list we constructed above, with a notable + // exception of special unboxing thunks for byref-like types. Those don't actually unbox anything + // and their body is a dummy. We capture the dependency here. + MethodDesc nonUnboxingMethod = factory.TypeSystemContext.GetTargetOfSpecialUnboxingThunk(_method); + _dependencies.Add(new DependencyListEntry(factory.MethodEntrypoint(nonUnboxingMethod, false), "Non-unboxing method")); + } + + _exception = scanningException; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.GetMangledMethodName(_method)); + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + Debug.Assert(_dependencies != null); + return _dependencies; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + CombinedDependencyList dependencies = null; + CodeBasedDependencyAlgorithm.AddConditionalDependenciesDueToMethodCodePresence(ref dependencies, factory, _method); + return dependencies ?? (IEnumerable)Array.Empty(); + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => CodeBasedDependencyAlgorithm.HasConditionalDependenciesDueToMethodCodePresence(_method); + + int ISortableNode.ClassCode => -1381809560; + + int ISortableNode.CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(Method, ((ScannedMethodNode)other).Method); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/SealedVTableNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/SealedVTableNode.cs new file mode 100644 index 00000000000000..50c79563c59b97 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/SealedVTableNode.cs @@ -0,0 +1,292 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using ILCompiler.DependencyAnalysisFramework; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class SealedVTableNode : ObjectNode, ISymbolDefinitionNode + { + private readonly TypeDesc _type; + private List _sealedVTableEntries; + + public SealedVTableNode(TypeDesc type) + { + // Multidimensional arrays should not get a sealed vtable or a dispatch map. Runtime should use the + // sealed vtable and dispatch map of the System.Array basetype instead. + // Pointer arrays also follow the same path + Debug.Assert(!type.IsArrayTypeWithoutGenericInterfaces()); + Debug.Assert(!type.IsRuntimeDeterminedSubtype); + + _type = type; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectNodeSection Section => _type.Context.Target.IsWindows ? ObjectNodeSection.FoldableReadOnlyDataSection : ObjectNodeSection.DataSection; + + public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix + "__SealedVTable_" + nameMangler.NodeMangler.MethodTable(_type)); + } + + int ISymbolNode.Offset => 0; + int ISymbolDefinitionNode.Offset => 0; + public override bool IsShareable => EETypeNode.IsTypeNodeShareable(_type); + public override bool StaticDependenciesAreComputed => true; + + /// + /// Returns the number of sealed vtable slots on the type. This API should only be called after successfully + /// building the sealed vtable slots. + /// + public int NumSealedVTableEntries + { + get + { + if (_sealedVTableEntries == null) + throw new NotSupportedException(); + + return _sealedVTableEntries.Count; + } + } + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) + { + BuildSealedVTableSlots(factory, relocsOnly: false); + return NumSealedVTableEntries == 0; + } + + /// + /// Returns the slot of a method in the sealed vtable, or -1 if not found. This API should only be called after + /// successfully building the sealed vtable slots. + /// + public int ComputeSealedVTableSlot(MethodDesc method) + { + if (_sealedVTableEntries == null) + throw new NotSupportedException(); + + for (int i = 0; i < _sealedVTableEntries.Count; i++) + { + if (_sealedVTableEntries[i].Matches(method)) + return i; + } + + return -1; + } + + public int ComputeDefaultInterfaceMethodSlot(MethodDesc method, DefType interfaceOnDefinition) + { + if (_sealedVTableEntries == null) + throw new NotSupportedException(); + + for (int i = 0; i < _sealedVTableEntries.Count; i++) + { + if (_sealedVTableEntries[i].Matches(method, interfaceOnDefinition)) + return i; + } + + return -1; + } + + public bool BuildSealedVTableSlots(NodeFactory factory, bool relocsOnly) + { + // Sealed vtable already built + if (_sealedVTableEntries != null) + return true; + + TypeDesc declType = _type.GetClosestDefType(); + + // It's only okay to touch the actual list of slots if we're in the final emission phase + // or the vtable is not built lazily. + if (relocsOnly && !factory.VTable(declType).HasFixedSlots) + return false; + + _sealedVTableEntries = new List(); + + // Interfaces don't have any virtual slots with the exception of interfaces that provide + // IDynamicInterfaceCastable implementation. + // Normal interface don't need one because the dispatch is done at the class level. + // For IDynamicInterfaceCastable, we don't have an implementing class. + if (_type.IsInterface && !((MetadataType)_type).IsDynamicInterfaceCastableImplementation()) + return true; + + IReadOnlyList virtualSlots = factory.VTable(declType).Slots; + + for (int i = 0; i < virtualSlots.Count; i++) + { + MethodDesc implMethod = declType.FindVirtualFunctionTargetMethodOnObjectType(virtualSlots[i]); + + if (implMethod.CanMethodBeInSealedVTable()) + _sealedVTableEntries.Add(SealedVTableEntry.FromVirtualMethod(implMethod)); + } + + TypeDesc declTypeDefinition = declType.GetTypeDefinition(); + + DefType[] declTypeRuntimeInterfaces = declType.RuntimeInterfaces; + DefType[] declTypeDefinitionRuntimeInterfaces = declTypeDefinition.RuntimeInterfaces; + + // Catch any runtime interface collapsing. We shouldn't have any + Debug.Assert(declTypeRuntimeInterfaces.Length == declTypeDefinitionRuntimeInterfaces.Length); + + for (int interfaceIndex = 0; interfaceIndex < declTypeRuntimeInterfaces.Length; interfaceIndex++) + { + var interfaceType = declTypeRuntimeInterfaces[interfaceIndex]; + var interfaceDefinitionType = declTypeDefinitionRuntimeInterfaces[interfaceIndex]; + + virtualSlots = factory.VTable(interfaceType).Slots; + + for (int interfaceMethodSlot = 0; interfaceMethodSlot < virtualSlots.Count; interfaceMethodSlot++) + { + MethodDesc declMethod = virtualSlots[interfaceMethodSlot]; + if (!interfaceType.IsTypeDefinition) + declMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(declMethod.GetTypicalMethodDefinition(), (InstantiatedType)interfaceDefinitionType); + + var implMethod = declTypeDefinition.ResolveInterfaceMethodToVirtualMethodOnType(declMethod); + + // Interface methods first implemented by a base type in the hierarchy will return null for the implMethod (runtime interface + // dispatch will walk the inheritance chain). + if (implMethod != null) + { + if (implMethod.CanMethodBeInSealedVTable() && !implMethod.OwningType.HasSameTypeDefinition(declType)) + { + TypeDesc implType = declType; + while (!implType.HasSameTypeDefinition(implMethod.OwningType)) + implType = implType.BaseType; + + MethodDesc targetMethod = implMethod; + if (!implType.IsTypeDefinition) + targetMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(implMethod.GetTypicalMethodDefinition(), (InstantiatedType)implType); + + _sealedVTableEntries.Add(SealedVTableEntry.FromVirtualMethod(targetMethod)); + } + } + else + { + // If the interface method is provided by a default implementation, add the default implementation + // to the sealed vtable. + var resolution = declTypeDefinition.ResolveInterfaceMethodToDefaultImplementationOnType(declMethod, out implMethod); + if (resolution == DefaultInterfaceMethodResolution.DefaultImplementation) + { + DefType providingInterfaceDefinitionType = (DefType)implMethod.OwningType; + implMethod = implMethod.InstantiateSignature(declType.Instantiation, Instantiation.Empty); + _sealedVTableEntries.Add(SealedVTableEntry.FromDefaultInterfaceMethod(implMethod, providingInterfaceDefinitionType)); + } + } + } + } + + return true; + } + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + var result = new DependencyList(); + + // When building the sealed vtable, we consult the vtable layout of these types + TypeDesc declType = _type.GetClosestDefType(); + result.Add(factory.VTable(declType), "VTable of the type"); + + foreach (var interfaceType in declType.RuntimeInterfaces) + result.Add(factory.VTable(interfaceType), "VTable of the interface"); + + return result; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialAlignment(4); + objData.AddSymbol(this); + + if (BuildSealedVTableSlots(factory, relocsOnly)) + { + for (int i = 0; i < _sealedVTableEntries.Count; i++) + { + IMethodNode relocTarget = _sealedVTableEntries[i].GetTarget(factory, _type); + + if (factory.Target.SupportsRelativePointers) + objData.EmitReloc(relocTarget, RelocType.IMAGE_REL_BASED_RELPTR32); + else + objData.EmitPointerReloc(relocTarget); + } + } + + return objData.ToObjectData(); + } + + public override int ClassCode => 1632890252; + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((SealedVTableNode)other)._type); + } + + private readonly struct SealedVTableEntry + { + private readonly MethodDesc _method; + private readonly DefType _interfaceDefinition; + + private SealedVTableEntry(MethodDesc method, DefType interfaceDefinition) + { + Debug.Assert(interfaceDefinition == null || method.GetTypicalMethodDefinition().OwningType == interfaceDefinition.GetTypeDefinition()); + (_method, _interfaceDefinition) = (method, interfaceDefinition); + } + + public static SealedVTableEntry FromVirtualMethod(MethodDesc method) + => new SealedVTableEntry(method, null); + + public static SealedVTableEntry FromDefaultInterfaceMethod(MethodDesc method, DefType interfaceOnDefinition) + => new SealedVTableEntry(method, interfaceOnDefinition); + + public IMethodNode GetTarget(NodeFactory factory, TypeDesc implementingClass) + { + MethodDesc implMethod = _method.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (_interfaceDefinition != null && implMethod.IsCanonicalMethod(CanonicalFormKind.Any)) + { + implMethod = factory.TypeSystemContext.GetDefaultInterfaceMethodImplementationThunk(implMethod, implementingClass.ConvertToCanonForm(CanonicalFormKind.Specific), _interfaceDefinition); + } + return factory.MethodEntrypoint(implMethod, _method.OwningType.IsValueType); + } + + public bool Matches(MethodDesc method) + { + if (_method == method) + { + // It is not valid to ask for slots of default implementations of interfaces on canonical version of the type. + // + // Consider: + // + // interface IFoo { void Frob() => Console.WriteLine(typeof(T)) } + // class Base : IFoo { } + // class Derived : Base, IFoo { } + // + // If we ask what's the slot of IFoo<__Canon>.Frob on Derived<__Canon, __Canon>, the answer is actually + // "two slots". We need extra data (the interface implementation on the definition of the type - + // e.g. "IFace") to disambiguate. Use the other overload. + Debug.Assert(_interfaceDefinition == null || !method.IsCanonicalMethod(CanonicalFormKind.Any)); + return true; + } + + return false; + } + + public bool Matches(MethodDesc method, DefType interfaceDefinition) + { + Debug.Assert(method.GetTypicalMethodDefinition().OwningType == interfaceDefinition.GetTypeDefinition()); + Debug.Assert(interfaceDefinition.IsInterface); + + if (_method == method && _interfaceDefinition == interfaceDefinition) + { + return true; + } + + return false; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ShadowConcreteUnboxingThunkNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ShadowConcreteUnboxingThunkNode.cs new file mode 100644 index 00000000000000..40b7a2f450341e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ShadowConcreteUnboxingThunkNode.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a concrete unboxing thunk on a generic type (or a generic method) that doesn't + /// have code emitted in the executable because it's physically backed by a canonical + /// method body. The purpose of this node is to track the dependencies of the concrete + /// thunk, as if it was generated. The node acts as a symbol for the canonical thunk + /// method for convenience. + /// + internal class ShadowConcreteUnboxingThunkNode : DependencyNodeCore, IMethodNode + { + private IMethodNode _canonicalThunk; + + /// + /// Gets the concrete method represented by this node. + /// + public MethodDesc Method { get; } + + // Implementation of ISymbolNode that makes this node act as a symbol for the canonical body + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + _canonicalThunk.AppendMangledName(nameMangler, sb); + } + public int Offset => _canonicalThunk.Offset; + public bool RepresentsIndirectionCell => _canonicalThunk.RepresentsIndirectionCell; + + public override bool StaticDependenciesAreComputed => true; + public ShadowConcreteUnboxingThunkNode(MethodDesc method, IMethodNode canonicalMethod) + { + Debug.Assert(!method.IsSharedByGenericInstantiations); + Debug.Assert(!method.IsRuntimeDeterminedExactMethod); + Debug.Assert(canonicalMethod.Method.IsSharedByGenericInstantiations); + Debug.Assert(canonicalMethod.Method == method.GetCanonMethodTarget(CanonicalFormKind.Specific)); + Method = method; + _canonicalThunk = canonicalMethod; + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + + // Make sure the canonical body gets generated + dependencies.Add(new DependencyListEntry(_canonicalThunk, "Canonical body")); + + // Make sure the target of the thunk gets modeled as a dependency + dependencies.Add(new DependencyListEntry(factory.ShadowConcreteMethod(Method), "Unboxing thunk target")); + + return dependencies; + } + + protected override string GetName(NodeFactory factory) => $"{Method.ToString()} backed by {_canonicalThunk.GetMangledName(factory.NameMangler)}"; + + public sealed override bool HasConditionalStaticDependencies => false; + public sealed override bool HasDynamicDependencies => false; + public sealed override bool InterestingForDynamicDependencyAnalysis => false; + + public sealed override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + public sealed override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + + int ISortableNode.ClassCode => -501699818; + + int ISortableNode.CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + var compare = comparer.Compare(Method, ((ShadowConcreteUnboxingThunkNode)other).Method); + if (compare != 0) + return compare; + + return comparer.Compare(_canonicalThunk, ((ShadowConcreteUnboxingThunkNode)other)._canonicalThunk); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StackTraceMethodMappingNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StackTraceMethodMappingNode.cs new file mode 100644 index 00000000000000..8f3389f584f6ab --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StackTraceMethodMappingNode.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// BlobIdStackTraceMethodRvaToTokenMapping - list of 8-byte pairs (method RVA-method token) + /// + public sealed class StackTraceMethodMappingNode : ObjectNode, ISymbolDefinitionNode + { + public StackTraceMethodMappingNode() + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "_stacktrace_methodRVA_to_token_mapping_End", true); + } + + private ObjectAndOffsetSymbolNode _endSymbol; + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public override bool IsShareable => false; + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.StackTraceMethodMappingNode; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("_stacktrace_methodRVA_to_token_mapping"); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // The dependency tracking of this node currently does nothing because the data emission relies + // the set of compiled methods which has an incomplete state during dependency tracking. + if (relocsOnly) + { + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + } + + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialPointerAlignment(); + objData.AddSymbol(this); + objData.AddSymbol(_endSymbol); + + foreach (var mappingEntry in factory.MetadataManager.GetStackTraceMapping(factory)) + { + objData.EmitReloc(factory.MethodEntrypoint(mappingEntry.Entity), RelocType.IMAGE_REL_BASED_RELPTR32); + objData.EmitInt(mappingEntry.MetadataHandle); + } + + _endSymbol.SetSymbolOffset(objData.CountBytes); + return objData.ToObjectData(); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs new file mode 100644 index 00000000000000..ba32d69a7d8dd7 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs @@ -0,0 +1,130 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a hashtable with information about all statics regions for all compiled generic types. + /// + internal sealed class StaticsInfoHashtableNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + private ExternalReferencesTableNode _nativeStaticsReferences; + + public StaticsInfoHashtableNode(ExternalReferencesTableNode externalReferences, ExternalReferencesTableNode nativeStaticsReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "_StaticsInfoHashtableNode_End", true); + _externalReferences = externalReferences; + _nativeStaticsReferences = nativeStaticsReferences; + } + + public ISymbolNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("_StaticsInfoHashtableNode"); + } + + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + + /// + /// Helper method to compute the dependencies that would be needed by a hashtable entry for statics info lookup. + /// This helper is used by EETypeNode, which is used by the dependency analysis to compute the statics hashtable + /// entries for the compiled types. + /// + public static void AddStaticsInfoDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + if (type is MetadataType && type.HasInstantiation && !type.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + MetadataType metadataType = (MetadataType)type; + + // The StaticsInfoHashtable entries only exist for static fields on generic types. + + if (metadataType.GCStaticFieldSize.AsInt > 0) + { + dependencies.Add(factory.TypeGCStaticsSymbol(metadataType), "GC statics indirection for StaticsInfoHashtable"); + } + + if (metadataType.NonGCStaticFieldSize.AsInt > 0 || factory.PreinitializationManager.HasLazyStaticConstructor(type)) + { + // The entry in the StaticsInfoHashtable points at the beginning of the static fields data, rather than the cctor + // context offset. + dependencies.Add(factory.TypeNonGCStaticsSymbol(metadataType), "Non-GC statics indirection for StaticsInfoHashtable"); + } + + if (metadataType.ThreadGcStaticFieldSize.AsInt > 0) + { + dependencies.Add(factory.TypeThreadStaticIndex(metadataType), "Threadstatics indirection for StaticsInfoHashtable"); + } + } + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + NativeWriter writer = new NativeWriter(); + VertexHashtable hashtable = new VertexHashtable(); + Section section = writer.NewSection(); + + section.Place(hashtable); + + foreach (var type in factory.MetadataManager.GetTypesWithConstructedEETypes()) + { + if (!type.HasInstantiation || type.IsCanonicalSubtype(CanonicalFormKind.Any) || type.IsGenericDefinition) + continue; + + MetadataType metadataType = type as MetadataType; + if (metadataType == null) + continue; + + VertexBag bag = new VertexBag(); + + if (metadataType.GCStaticFieldSize.AsInt > 0) + { + bag.AppendUnsigned(BagElementKind.GcStaticData, _nativeStaticsReferences.GetIndex(factory.TypeGCStaticsSymbol(metadataType))); + } + if (metadataType.NonGCStaticFieldSize.AsInt > 0 || factory.PreinitializationManager.HasLazyStaticConstructor(type)) + { + bag.AppendUnsigned(BagElementKind.NonGcStaticData, _nativeStaticsReferences.GetIndex(factory.TypeNonGCStaticsSymbol(metadataType))); + } + if (metadataType.ThreadGcStaticFieldSize.AsInt > 0) + { + bag.AppendUnsigned(BagElementKind.ThreadStaticIndex, _nativeStaticsReferences.GetIndex(factory.TypeThreadStaticIndex(metadataType))); + } + + if (bag.ElementsCount > 0) + { + uint typeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(type)); + Vertex staticsInfo = writer.GetTuple(writer.GetUnsignedConstant(typeId), bag); + + hashtable.Append((uint)type.GetHashCode(), section.Place(staticsInfo)); + } + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.StaticsInfoHashtableNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StringAllocatorMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StringAllocatorMethodNode.cs new file mode 100644 index 00000000000000..4d45bcf90aadaf --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StringAllocatorMethodNode.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.IL; +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// NEWOBJ operation on String type is actually a call to a static method that returns a String + /// instance (i.e. there's an explicit call to the runtime allocator from the static method body). + /// This node is used to model the behavior. It represents the symbol for the target allocator + /// method and makes sure the String type is marked as constructed. + /// + class StringAllocatorMethodNode : DependencyNodeCore, IMethodNode + { + private readonly MethodDesc _allocationMethod; + private readonly MethodDesc _constructorMethod; + + public MethodDesc Method => _allocationMethod; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.GetMangledMethodName(_allocationMethod)); + } + public int Offset => 0; + public bool RepresentsIndirectionCell => false; + + public StringAllocatorMethodNode(MethodDesc constructorMethod) + { + Debug.Assert(constructorMethod.IsConstructor && constructorMethod.OwningType.IsString); + + // Find the allocator method that matches the constructor signature. + var signatureBuilder = new MethodSignatureBuilder(constructorMethod.Signature); + signatureBuilder.Flags = MethodSignatureFlags.Static; + signatureBuilder.ReturnType = constructorMethod.OwningType; + + _allocationMethod = constructorMethod.OwningType.GetKnownMethod("Ctor", signatureBuilder.ToSignature()); + _constructorMethod = constructorMethod; + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyList result = new DependencyList(); + + result.Add( + factory.ConstructedTypeSymbol(factory.TypeSystemContext.GetWellKnownType(WellKnownType.String)), + "String constructor call"); + result.Add( + factory.MethodEntrypoint(_allocationMethod), + "String constructor call"); + + factory.MetadataManager.GetDependenciesDueToMethodCodePresence(ref result, factory, _constructorMethod, methodIL: null); + + return result; + } + + public override bool HasConditionalStaticDependencies => false; + public override bool HasDynamicDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + int ISortableNode.ClassCode => 1991750873; + + int ISortableNode.CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_allocationMethod, ((StringAllocatorMethodNode)other)._allocationMethod); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StructMarshallingDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StructMarshallingDataNode.cs new file mode 100644 index 00000000000000..ffdb02e24d5d9c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StructMarshallingDataNode.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents an entry in the table. + /// + public class StructMarshallingDataNode : DependencyNodeCore + { + private readonly DefType _type; + + public DefType Type => _type; + + public StructMarshallingDataNode(DefType type) + { + _type = type; + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + InteropStateManager stateManager = ((CompilerGeneratedInteropStubManager)factory.InteropStubManager)._interopStateManager; + + yield return new DependencyListEntry(factory.NecessaryTypeSymbol(_type), "Struct Marshalling Stub"); + + // Not all StructMarshalingDataNodes require marshalling - some are only present because we want to + // generate field offset information for Marshal.OffsetOf. + if (MarshalHelpers.IsStructMarshallingRequired(_type)) + { + yield return new DependencyListEntry(factory.MethodEntrypoint(stateManager.GetStructMarshallingManagedToNativeThunk(_type)), "Struct Marshalling stub"); + yield return new DependencyListEntry(factory.MethodEntrypoint(stateManager.GetStructMarshallingNativeToManagedThunk(_type)), "Struct Marshalling stub"); + yield return new DependencyListEntry(factory.MethodEntrypoint(stateManager.GetStructMarshallingCleanupThunk(_type)), "Struct Marshalling stub"); + } + } + + protected override string GetName(NodeFactory context) + { + return $"Struct marshaling data for {_type}"; + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StructMarshallingStubMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StructMarshallingStubMapNode.cs new file mode 100644 index 00000000000000..dfd92dd9340c3d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StructMarshallingStubMapNode.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.NativeFormat; +using Internal.Runtime; +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a hash table of struct marshalling stub types generated into the image. + /// + internal sealed class StructMarshallingStubMapNode : ObjectNode, ISymbolDefinitionNode + { + private readonly ObjectAndOffsetSymbolNode _endSymbol; + private readonly ExternalReferencesTableNode _externalReferences; + private readonly InteropStateManager _interopStateManager; + + public StructMarshallingStubMapNode(ExternalReferencesTableNode externalReferences, InteropStateManager interopStateManager) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__struct_marshalling_stub_map_End", true); + _externalReferences = externalReferences; + _interopStateManager = interopStateManager; + } + + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__struct_marshalling_stub_map"); + } + public int Offset => 0; + public override bool IsShareable => false; + + public override ObjectNodeSection Section => _externalReferences.Section; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + var writer = new NativeWriter(); + var typeMapHashTable = new VertexHashtable(); + + Section hashTableSection = writer.NewSection(); + hashTableSection.Place(typeMapHashTable); + + foreach (var structType in factory.MetadataManager.GetTypesWithStructMarshalling()) + { + // the order of data written is as follows: + // managed struct type + // NumFields<< 2 | (HasInvalidLayout ? (2:0)) | (MarshallingRequired ? (1:0)) + // If MarshallingRequired: + // size + // struct marshalling thunk + // struct unmarshalling thunk + // struct cleanup thunk + // For each field field: + // name + // offset + + var nativeType = _interopStateManager.GetStructMarshallingNativeType(structType); + + Vertex marshallingData = null; + if (MarshalHelpers.IsStructMarshallingRequired(structType)) + { + Vertex thunks = writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(_interopStateManager.GetStructMarshallingManagedToNativeThunk(structType)))), + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(_interopStateManager.GetStructMarshallingNativeToManagedThunk(structType)))), + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(_interopStateManager.GetStructMarshallingCleanupThunk(structType))))); + + uint size = (uint)nativeType.InstanceFieldSize.AsInt; + marshallingData = writer.GetTuple(writer.GetUnsignedConstant(size), thunks); + } + + Vertex fieldOffsetData = null; + for (int i = 0; i < nativeType.Fields.Length; i++) + { + var row = writer.GetTuple( + writer.GetStringConstant(nativeType.Fields[i].Name), + writer.GetUnsignedConstant((uint)nativeType.Fields[i].Offset.AsInt) + ); + + fieldOffsetData = (fieldOffsetData != null) ? writer.GetTuple(fieldOffsetData, row) : row; + } + + uint mask = (uint)((marshallingData != null) ? InteropDataConstants.HasMarshallers : 0) | + (uint)(nativeType.HasInvalidLayout ? InteropDataConstants.HasInvalidLayout : 0) | + (uint)(nativeType.Fields.Length << InteropDataConstants.FieldCountShift); + + Vertex data = writer.GetUnsignedConstant(mask); + if (marshallingData != null) + data = writer.GetTuple(data, marshallingData); + + if (fieldOffsetData != null) + data = writer.GetTuple(data, fieldOffsetData); + + Vertex vertex = writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.NecessaryTypeSymbol(structType))), + data + ); + + int hashCode = structType.GetHashCode(); + typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + public override int ClassCode => (int)ObjectNodeOrder.StructMarshallingStubMapNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMDebug.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMDebug.cs new file mode 100644 index 00000000000000..d32b6a2e8d5270 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMDebug.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using ILCompiler.DependencyAnalysis.ARM; + +namespace ILCompiler.DependencyAnalysis +{ + public static class ARMDebug + { + [System.Diagnostics.Conditional("DEBUG")] + public static void EmitNYIAssert(NodeFactory factory, ref ARMEmitter encoder, string message, + [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = null, + [System.Runtime.CompilerServices.CallerMemberName] string memberName = null, + [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) + { + ISymbolNode NYI_Assert = factory.ExternSymbol("NYI_Assert"); + String CallInfoPrefix = " " + sourceFilePath + "(" + sourceLineNumber.ToString() + "): method " + memberName + ": "; + ISymbolNode messageSymbol = factory.ConstantUtf8String(CallInfoPrefix + message); + encoder.EmitMOV(encoder.TargetRegister.Arg0, messageSymbol); + encoder.EmitJMP(NYI_Assert); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void EmitHelperNYIAssert(NodeFactory factory, ref ARMEmitter encoder, ReadyToRunHelperId hId, + [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = null, + [System.Runtime.CompilerServices.CallerMemberName] string memberName = null, + [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) + { + EmitNYIAssert(factory, ref encoder, hId.ToString() + " is not implemented", sourceFilePath, memberName, sourceLineNumber); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs new file mode 100644 index 00000000000000..7096525ff00132 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Text; +using System; + +using ILCompiler.DependencyAnalysis.ARM; +using ILCompiler.DependencyAnalysis.X64; +using ILCompiler.DependencyAnalysis.X86; +using ILCompiler.DependencyAnalysis.ARM64; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// On ARM, we use R12 to store the interface dispatch cell. However, the jump through the import address + /// table to call the runtime interface dispatch helper trashes R12. This stub pushes R12 before making + /// the runtime call. The ARM runtime interface dispatch code expects this and pops R12 to get the dispatch + /// cell. + /// + public partial class InitialInterfaceDispatchStubNode : AssemblyStubNode + { + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("_InitialInterfaceDispatchStub"); + } + + public override bool IsShareable => false; + + protected override void EmitCode(NodeFactory factory, ref ARMEmitter instructionEncoder, bool relocsOnly) + { + instructionEncoder.EmitPUSH(ARM.Register.R12); + instructionEncoder.EmitMOV(ARM.Register.R12, factory.ExternSymbol("RhpInitialInterfaceDispatch")); + instructionEncoder.EmitMOV(ARM.Register.R15, ARM.Register.R12); + } + + // Only ARM requires a stub + protected override void EmitCode(NodeFactory factory, ref X86Emitter instructionEncoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + + protected override void EmitCode(NodeFactory factory, ref X64Emitter instructionEncoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + + protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructionEncoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + + public override int ClassCode => 588185132; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMJumpStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMJumpStubNode.cs new file mode 100644 index 00000000000000..3890dc5498eaaf --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMJumpStubNode.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.ARM; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class JumpStubNode + { + protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + if (!_target.RepresentsIndirectionCell) + { + encoder.EmitJMP(_target); // b methodEntryPoint + } + else + { + encoder.EmitMOV(encoder.TargetRegister.InterproceduralScratch, _target); + encoder.EmitJMP(encoder.TargetRegister.InterproceduralScratch); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMNodeFactory.cs new file mode 100644 index 00000000000000..3e1bf496dcf37a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMNodeFactory.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class NodeFactory + { + private InitialInterfaceDispatchStubNode _initialInterfaceDispatchStubNode; + + public InitialInterfaceDispatchStubNode InitialInterfaceDispatchStub + { + get + { + if (_initialInterfaceDispatchStubNode == null) + { + _initialInterfaceDispatchStubNode = new InitialInterfaceDispatchStubNode(); + } + + return _initialInterfaceDispatchStubNode; + } + } + + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs new file mode 100644 index 00000000000000..bb12fb16963713 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs @@ -0,0 +1,238 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using ILCompiler.DependencyAnalysis.ARM; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + partial class ReadyToRunGenericHelperNode + { + protected Register GetContextRegister(ref /* readonly */ ARMEmitter encoder) + { + if (_id == ReadyToRunHelperId.DelegateCtor) + return encoder.TargetRegister.Arg2; + else + return encoder.TargetRegister.Arg0; + } + + protected void EmitDictionaryLookup(NodeFactory factory, ref ARMEmitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly) + { + // INVARIANT: must not trash context register + + // Find the generic dictionary slot + int dictionarySlot = 0; + if (!relocsOnly) + { + // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. + dictionarySlot = factory.GenericDictionaryLayout(_dictionaryOwner).GetSlotForEntry(lookup); + } + + // Load the generic dictionary cell + encoder.EmitLDR(result, context, dictionarySlot * factory.Target.PointerSize); + + switch (lookup.LookupResultReferenceType(factory)) + { + case GenericLookupResultReferenceType.Indirect: + // Do another indirection + encoder.EmitLDR(result, result); + break; + + case GenericLookupResultReferenceType.ConditionalIndirect: + // Test result, 0x1 + // JEQ L1 + // mov result, [result-1] + // L1: + throw new NotImplementedException(); + + default: + break; + } + } + + protected sealed override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + // First load the generic context into the context register. + EmitLoadGenericContext(factory, ref encoder, relocsOnly); + + Register contextRegister = GetContextRegister(ref encoder); + + switch (_id) + { + case ReadyToRunHelperId.GetNonGCStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + + MetadataType target = (MetadataType)_target; + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextSize(factory.Target); + encoder.EmitLDR(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0, ((short)(factory.Target.PointerSize - cctorContextSize))); + encoder.EmitCMP(encoder.TargetRegister.Arg1, ((byte)1)); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitSUB(encoder.TargetRegister.Arg0, ((byte)(cctorContextSize))); + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetGCStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result); + + MetadataType target = (MetadataType)_target; + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); + + int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextSize(factory.Target); + encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, ((short)(factory.Target.PointerSize - cctorContextSize))); + encoder.EmitCMP(encoder.TargetRegister.Arg3, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2); + encoder.EmitSUB(encoder.TargetRegister.Arg0, cctorContextSize); + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetThreadStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + MetadataType target = (MetadataType)_target; + + // Look up the index cell + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly); + + ISymbolNode helperEntrypoint; + if (factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + // There is a lazy class constructor. We need the non-GC static base because that's where the + // class constructor context lives. + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); + int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextSize(factory.Target); + encoder.EmitSUB(encoder.TargetRegister.Arg2, cctorContextSize); + + helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase); + } + else + { + helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType); + } + + // First arg: address of the TypeManager slot that provides the helper with + // information about module index and the type manager instance (which is used + // for initialization on first access). + encoder.EmitLDR(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1); + + // Second arg: index of the type in the ThreadStatic section of the modules + encoder.EmitLDR(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg1, factory.Target.PointerSize); + + encoder.EmitJMP(helperEntrypoint); + } + break; + + case ReadyToRunHelperId.DelegateCtor: + { + // This is a weird helper. Codegen populated Arg0 and Arg1 with the values that the constructor + // method expects. Codegen also passed us the generic context in Arg2. + // We now need to load the delegate target method into Arg2 (using a dictionary lookup) + // and the optional 4th parameter, and call the ctor. + + Debug.Assert(contextRegister == encoder.TargetRegister.Arg2); + + var target = (DelegateCreationInfo)_target; + + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, _lookupSignature, relocsOnly); + + if (target.Thunk != null) + { + Debug.Assert(target.Constructor.Method.Signature.Length == 3); + encoder.EmitMOV(encoder.TargetRegister.Arg3, target.Thunk); + } + else + { + Debug.Assert(target.Constructor.Method.Signature.Length == 2); + } + + encoder.EmitJMP(target.Constructor); + } + break; + + // These are all simple: just get the thing from the dictionary and we're done + case ReadyToRunHelperId.TypeHandle: + case ReadyToRunHelperId.MethodHandle: + case ReadyToRunHelperId.FieldHandle: + case ReadyToRunHelperId.MethodDictionary: + case ReadyToRunHelperId.MethodEntry: + case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DefaultConstructor: + case ReadyToRunHelperId.ObjectAllocator: + case ReadyToRunHelperId.TypeHandleForCasting: + { + EmitDictionaryLookup(factory, ref encoder, contextRegister, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + encoder.EmitRET(); + } + break; + default: + throw new NotImplementedException(); + } + } + + protected virtual void EmitLoadGenericContext(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + // Assume generic context is already loaded in the context register. + } + } + + partial class ReadyToRunGenericLookupFromTypeNode + { + protected override void EmitLoadGenericContext(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + // We start with context register pointing to the MethodTable + Register contextRegister = GetContextRegister(ref encoder); + + // Locate the VTable slot that points to the dictionary + int vtableSlot = 0; + if (!relocsOnly) + { + // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. + vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)_dictionaryOwner); + } + + int pointerSize = factory.Target.PointerSize; + int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize); + + // Load the dictionary pointer from the VTable + encoder.EmitLDR(contextRegister, contextRegister, slotOffset); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs new file mode 100644 index 00000000000000..1f462113d8e0c4 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs @@ -0,0 +1,200 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using Internal.TypeSystem; + +using ILCompiler; +using ILCompiler.DependencyAnalysis.ARM; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// ARM specific portions of ReadyToRunHelperNode + /// + partial class ReadyToRunHelperNode + { + protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + switch (Id) + { + case ReadyToRunHelperId.VirtualCall: + { + MethodDesc targetMethod = (MethodDesc)Target; + + Debug.Assert(!targetMethod.OwningType.IsInterface); + Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); + + int pointerSize = factory.Target.PointerSize; + + int slot = 0; + if (!relocsOnly) + { + slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); + Debug.Assert(slot != -1); + } + + encoder.EmitLDR(encoder.TargetRegister.InterproceduralScratch, encoder.TargetRegister.Arg0, 0); + encoder.EmitLDR(encoder.TargetRegister.InterproceduralScratch, encoder.TargetRegister.InterproceduralScratch, + EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize)); + encoder.EmitJMP(encoder.TargetRegister.InterproceduralScratch); + } + break; + + case ReadyToRunHelperId.GetNonGCStaticBase: + { + MetadataType target = (MetadataType)Target; + bool hasLazyStaticConstructor = factory.PreinitializationManager.HasLazyStaticConstructor(target); + encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); + + if (!hasLazyStaticConstructor) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); + encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, + ((short)(factory.Target.PointerSize - NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)))); + encoder.EmitCMP(encoder.TargetRegister.Arg3, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0/*Result*/, encoder.TargetRegister.Arg2); + encoder.EmitSUB(encoder.TargetRegister.Arg0, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetThreadStaticBase: + { + MetadataType target = (MetadataType)Target; + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); + + // First arg: address of the TypeManager slot that provides the helper with + // information about module index and the type manager instance (which is used + // for initialization on first access). + encoder.EmitLDR(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2); + + // Second arg: index of the type in the ThreadStatic section of the modules + encoder.EmitLDR(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, factory.Target.PointerSize); + + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType)); + } + else + { + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); + encoder.EmitSUB(encoder.TargetRegister.Arg2, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + // TODO: performance optimization - inline the check verifying whether we need to trigger the cctor + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetGCStaticBase: + { + MetadataType target = (MetadataType)Target; + encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result); + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); + encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, + ((short)(factory.Target.PointerSize - NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)))); + encoder.EmitCMP(encoder.TargetRegister.Arg3, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0/*Result*/, encoder.TargetRegister.Arg2); + encoder.EmitSUB(encoder.TargetRegister.Arg0, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.DelegateCtor: + { + DelegateCreationInfo target = (DelegateCreationInfo)Target; + + if (target.TargetNeedsVTableLookup) + { + Debug.Assert(!target.TargetMethod.CanMethodBeInSealedVTable()); + + encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg1); + + int slot = 0; + if (!relocsOnly) + slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod, target.TargetMethod.OwningType); + + Debug.Assert(slot != -1); + encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, + EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)); + } + else + { + ISymbolNode targetMethodNode = target.GetTargetNode(factory); + encoder.EmitMOV(encoder.TargetRegister.Arg2, target.GetTargetNode(factory)); + } + + if (target.Thunk != null) + { + Debug.Assert(target.Constructor.Method.Signature.Length == 3); + encoder.EmitMOV(encoder.TargetRegister.Arg3, target.Thunk); + } + else + { + Debug.Assert(target.Constructor.Method.Signature.Length == 2); + } + + encoder.EmitJMP(target.Constructor); + } + break; + + case ReadyToRunHelperId.ResolveVirtualFunction: + { + ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.ResolveVirtualFunction); + /* + *** + NOT TESTED!!! + *** + MethodDesc targetMethod = (MethodDesc)Target; + if (targetMethod.OwningType.IsInterface) + { + encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); + encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); + } + else + { + if (relocsOnly) + break; + + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0); + + Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); + + int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); + Debug.Assert(slot != -1); + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result, + ((short)(EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)))); + encoder.EmitRET(); + } + */ + } + break; + + default: + throw new NotImplementedException(); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMTentativeMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMTentativeMethodNode.cs new file mode 100644 index 00000000000000..982a444c46a123 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMTentativeMethodNode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.ARM; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class TentativeMethodNode + { + protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + encoder.EmitJMP(GetTarget(factory)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMUnboxingStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMUnboxingStubNode.cs new file mode 100644 index 00000000000000..3968eb92af79b1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMUnboxingStubNode.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.ARM; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class UnboxingStubNode + { + protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + encoder.EmitADD(encoder.TargetRegister.Arg0, (byte)factory.Target.PointerSize); // add r0, sizeof(void*); + encoder.EmitJMP(GetUnderlyingMethodEntrypoint(factory)); // b methodEntryPoint + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64JumpStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64JumpStubNode.cs new file mode 100644 index 00000000000000..c1444e6bbc5c5a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64JumpStubNode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.ARM64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class JumpStubNode + { + protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(_target); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunGenericHelperNode.cs new file mode 100644 index 00000000000000..8451b672dffcea --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunGenericHelperNode.cs @@ -0,0 +1,240 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using ILCompiler.DependencyAnalysis.ARM64; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + partial class ReadyToRunGenericHelperNode + { + protected Register GetContextRegister(ref /* readonly */ ARM64Emitter encoder) + { + if (_id == ReadyToRunHelperId.DelegateCtor) + return encoder.TargetRegister.Arg2; + else + return encoder.TargetRegister.Arg0; + } + + protected void EmitDictionaryLookup(NodeFactory factory, ref ARM64Emitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly) + { + // INVARIANT: must not trash context register + + // Find the generic dictionary slot + int dictionarySlot = 0; + if (!relocsOnly) + { + // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. + dictionarySlot = factory.GenericDictionaryLayout(_dictionaryOwner).GetSlotForEntry(lookup); + } + + // Load the generic dictionary cell + encoder.EmitLDR(result, context, dictionarySlot * factory.Target.PointerSize); + + switch (lookup.LookupResultReferenceType(factory)) + { + case GenericLookupResultReferenceType.Indirect: + // Do another indirection + encoder.EmitLDR(result, result); + break; + + case GenericLookupResultReferenceType.ConditionalIndirect: + // Test result, 0x1 + // JEQ L1 + // mov result, [result-1] + // L1: + throw new NotImplementedException(); + + default: + break; + } + } + + protected sealed override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + // First load the generic context into the context register. + EmitLoadGenericContext(factory, ref encoder, relocsOnly); + + Register contextRegister = GetContextRegister(ref encoder); + + switch (_id) + { + case ReadyToRunHelperId.GetNonGCStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + + MetadataType target = (MetadataType)_target; + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitSUB(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg0, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg3, (short)factory.Target.PointerSize); + encoder.EmitCMP(encoder.TargetRegister.Arg2, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg3); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetGCStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result); + + MetadataType target = (MetadataType)_target; + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); + + encoder.EmitSUB(encoder.TargetRegister.Arg2, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, (short)factory.Target.PointerSize); + encoder.EmitCMP(encoder.TargetRegister.Arg3, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetThreadStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + MetadataType target = (MetadataType)_target; + + // Look up the index cell + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly); + + ISymbolNode helperEntrypoint; + if (factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + // There is a lazy class constructor. We need the non-GC static base because that's where the + // class constructor context lives. + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); + int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextSize(factory.Target); + encoder.EmitSUB(encoder.TargetRegister.Arg2, cctorContextSize); + + helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase); + } + else + { + helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType); + } + + // First arg: address of the TypeManager slot that provides the helper with + // information about module index and the type manager instance (which is used + // for initialization on first access). + encoder.EmitLDR(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1); + + // Second arg: index of the type in the ThreadStatic section of the modules + encoder.EmitLDR(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg1, factory.Target.PointerSize); + + encoder.EmitJMP(helperEntrypoint); + } + break; + + case ReadyToRunHelperId.DelegateCtor: + { + // This is a weird helper. Codegen populated Arg0 and Arg1 with the values that the constructor + // method expects. Codegen also passed us the generic context in Arg2. + // We now need to load the delegate target method into Arg2 (using a dictionary lookup) + // and the optional 4th parameter, and call the ctor. + + Debug.Assert(contextRegister == encoder.TargetRegister.Arg2); + + var target = (DelegateCreationInfo)_target; + + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, _lookupSignature, relocsOnly); + + if (target.Thunk != null) + { + Debug.Assert(target.Constructor.Method.Signature.Length == 3); + encoder.EmitMOV(encoder.TargetRegister.Arg3, target.Thunk); + } + else + { + Debug.Assert(target.Constructor.Method.Signature.Length == 2); + } + + encoder.EmitJMP(target.Constructor); + } + break; + + // These are all simple: just get the thing from the dictionary and we're done + case ReadyToRunHelperId.TypeHandle: + case ReadyToRunHelperId.MethodHandle: + case ReadyToRunHelperId.FieldHandle: + case ReadyToRunHelperId.MethodDictionary: + case ReadyToRunHelperId.MethodEntry: + case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DefaultConstructor: + case ReadyToRunHelperId.ObjectAllocator: + case ReadyToRunHelperId.TypeHandleForCasting: + { + EmitDictionaryLookup(factory, ref encoder, contextRegister, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + encoder.EmitRET(); + } + break; + + default: + throw new NotImplementedException(); + } + } + + protected virtual void EmitLoadGenericContext(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + // Assume generic context is already loaded in the context register. + } + } + + partial class ReadyToRunGenericLookupFromTypeNode + { + protected override void EmitLoadGenericContext(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + // We start with context register pointing to the MethodTable + Register contextRegister = GetContextRegister(ref encoder); + + // Locate the VTable slot that points to the dictionary + int vtableSlot = 0; + if (!relocsOnly) + { + // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. + vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)_dictionaryOwner); + } + + int pointerSize = factory.Target.PointerSize; + int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize); + + // Load the dictionary pointer from the VTable + encoder.EmitLDR(contextRegister, contextRegister, slotOffset); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs new file mode 100644 index 00000000000000..0b9068e3a13f24 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs @@ -0,0 +1,203 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using ILCompiler.DependencyAnalysis.ARM64; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// ARM64 specific portions of ReadyToRunHelperNode + /// + public partial class ReadyToRunHelperNode + { + protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + switch (Id) + { + case ReadyToRunHelperId.VirtualCall: + { + MethodDesc targetMethod = (MethodDesc)Target; + + Debug.Assert(!targetMethod.OwningType.IsInterface); + Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); + + int pointerSize = factory.Target.PointerSize; + + int slot = 0; + if (!relocsOnly) + { + slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); + Debug.Assert(slot != -1); + } + + encoder.EmitLDR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg0, 0); + encoder.EmitLDR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.IntraProcedureCallScratch1, + EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize)); + encoder.EmitJMP(encoder.TargetRegister.IntraProcedureCallScratch1); + } + break; + + case ReadyToRunHelperId.GetNonGCStaticBase: + { + MetadataType target = (MetadataType)Target; + + bool hasLazyStaticConstructor = factory.PreinitializationManager.HasLazyStaticConstructor(target); + encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); + + if (!hasLazyStaticConstructor) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitSUB(encoder.TargetRegister.Arg3, encoder.TargetRegister.Result, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg3, (short)factory.Target.PointerSize); + encoder.EmitCMP(encoder.TargetRegister.Arg2, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg3); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetThreadStaticBase: + { + MetadataType target = (MetadataType)Target; + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); + + // First arg: address of the TypeManager slot that provides the helper with + // information about module index and the type manager instance (which is used + // for initialization on first access). + encoder.EmitLDR(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2); + + // Second arg: index of the type in the ThreadStatic section of the modules + encoder.EmitLDR(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, factory.Target.PointerSize); + + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType)); + } + else + { + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); + encoder.EmitSUB(encoder.TargetRegister.Arg2, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + + encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, (short)factory.Target.PointerSize); + encoder.EmitCMP(encoder.TargetRegister.Arg3, 1); + encoder.EmitJE(factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType)); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetGCStaticBase: + { + MetadataType target = (MetadataType)Target; + + encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result); + + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); + encoder.EmitSUB(encoder.TargetRegister.Arg2, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, (short)factory.Target.PointerSize); + encoder.EmitCMP(encoder.TargetRegister.Arg3, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.DelegateCtor: + { + DelegateCreationInfo target = (DelegateCreationInfo)Target; + + if (target.TargetNeedsVTableLookup) + { + Debug.Assert(!target.TargetMethod.CanMethodBeInSealedVTable()); + + encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg1); + + int slot = 0; + if (!relocsOnly) + slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod, target.TargetMethod.OwningType); + + Debug.Assert(slot != -1); + encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, + EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)); + } + else + { + ISymbolNode targetMethodNode = target.GetTargetNode(factory); + encoder.EmitMOV(encoder.TargetRegister.Arg2, target.GetTargetNode(factory)); + } + + if (target.Thunk != null) + { + Debug.Assert(target.Constructor.Method.Signature.Length == 3); + encoder.EmitMOV(encoder.TargetRegister.Arg3, target.Thunk); + } + else + { + Debug.Assert(target.Constructor.Method.Signature.Length == 2); + } + + encoder.EmitJMP(target.Constructor); + } + break; + + case ReadyToRunHelperId.ResolveVirtualFunction: + { + // Not tested + encoder.EmitINT3(); + + MethodDesc targetMethod = (MethodDesc)Target; + if (targetMethod.OwningType.IsInterface) + { + encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); + encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); + } + else + { + if (relocsOnly) + break; + + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0); + + Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); + + int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); + Debug.Assert(slot != -1); + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result, + ((short)(EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)))); + encoder.EmitRET(); + } + } + break; + + + default: + throw new NotImplementedException(); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64TentativeMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64TentativeMethodNode.cs new file mode 100644 index 00000000000000..2081d828327fa8 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64TentativeMethodNode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.ARM64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class TentativeMethodNode + { + protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(GetTarget(factory)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64UnboxingStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64UnboxingStubNode.cs new file mode 100644 index 00000000000000..40596f8338c3d0 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64UnboxingStubNode.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using ILCompiler.DependencyAnalysis.ARM64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class UnboxingStubNode + { + protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + encoder.EmitADD(encoder.TargetRegister.Arg0, (byte)factory.Target.PointerSize); // add r0, sizeof(void*); + encoder.EmitJMP(GetUnderlyingMethodEntrypoint(factory)); // b methodEntryPoint + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64JumpStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64JumpStubNode.cs new file mode 100644 index 00000000000000..a575559f326ee6 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64JumpStubNode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.X64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class JumpStubNode + { + protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(_target); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs new file mode 100644 index 00000000000000..2ec2b998afe768 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs @@ -0,0 +1,263 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using ILCompiler.DependencyAnalysis.X64; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + partial class ReadyToRunGenericHelperNode + { + protected Register GetContextRegister(ref /* readonly */ X64Emitter encoder) + { + if (_id == ReadyToRunHelperId.DelegateCtor) + return encoder.TargetRegister.Arg2; + else + return encoder.TargetRegister.Arg0; + } + + protected void EmitDictionaryLookup(NodeFactory factory, ref X64Emitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly) + { + // INVARIANT: must not trash context register + + // Find the generic dictionary slot + int dictionarySlot = 0; + if (!relocsOnly) + { + // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. + dictionarySlot = factory.GenericDictionaryLayout(_dictionaryOwner).GetSlotForEntry(lookup); + } + + // Load the generic dictionary cell + AddrMode loadEntry = new AddrMode( + context, null, dictionarySlot * factory.Target.PointerSize, 0, AddrModeSize.Int64); + encoder.EmitMOV(result, ref loadEntry); + + // If there's any invalid entries, we need to test for them + // + // Only do this in relocsOnly to make it easier to weed out bugs - the _hasInvalidEntries + // flag can change over the course of compilation and the bad slot helper dependency + // should be reported by someone else - the system should not rely on it coming from here. + if (!relocsOnly && _hasInvalidEntries) + { + encoder.EmitCompareToZero(result); + encoder.EmitJE(GetBadSlotHelper(factory)); + } + + switch (lookup.LookupResultReferenceType(factory)) + { + case GenericLookupResultReferenceType.Indirect: + // Do another indirection + loadEntry = new AddrMode(result, null, 0, 0, AddrModeSize.Int64); + encoder.EmitMOV(result, ref loadEntry); + break; + + case GenericLookupResultReferenceType.ConditionalIndirect: + // Test result, 0x1 + // JEQ L1 + // mov result, [result-1] + // L1: + throw new NotImplementedException(); + + default: + break; + } + } + + protected sealed override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) + { + // First load the generic context into the context register. + EmitLoadGenericContext(factory, ref encoder, relocsOnly); + + Register contextRegister = GetContextRegister(ref encoder); + + switch (_id) + { + case ReadyToRunHelperId.GetNonGCStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + MetadataType target = (MetadataType)_target; + + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + encoder.EmitRET(); + } + else + { + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, _lookupSignature, relocsOnly); + encoder.EmitMOV(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0); + + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextSize(factory.Target); + AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize - cctorContextSize, 0, AddrModeSize.Int32); + encoder.EmitCMP(ref initialized, 1); + encoder.EmitRETIfEqual(); + + AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg0, null, -cctorContextSize, 0, AddrModeSize.Int64); + encoder.EmitLEA(encoder.TargetRegister.Arg0, ref loadCctor); + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetGCStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + MetadataType target = (MetadataType)_target; + + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + + AddrMode loadFromResult = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult); + + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, nonGcRegionLookup, relocsOnly); + + int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextSize(factory.Target); + AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize - cctorContextSize, 0, AddrModeSize.Int32); + encoder.EmitCMP(ref initialized, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg0, null, -cctorContextSize, 0, AddrModeSize.Int64); + encoder.EmitLEA(encoder.TargetRegister.Arg0, ref loadCctor); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetThreadStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + MetadataType target = (MetadataType)_target; + + // Look up the index cell + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly); + + ISymbolNode helperEntrypoint; + if (factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + // There is a lazy class constructor. We need the non-GC static base because that's where the + // class constructor context lives. + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); + int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextSize(factory.Target); + AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg2, null, -cctorContextSize, 0, AddrModeSize.Int64); + encoder.EmitLEA(encoder.TargetRegister.Arg2, ref loadCctor); + + helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase); + } + else + { + helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType); + } + + // First arg: address of the TypeManager slot that provides the helper with + // information about module index and the type manager instance (which is used + // for initialization on first access). + AddrMode loadFromArg1 = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg1); + + // Second arg: index of the type in the ThreadStatic section of the modules + AddrMode loadFromArg1AndDelta = new AddrMode(encoder.TargetRegister.Arg1, null, factory.Target.PointerSize, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg1AndDelta); + + encoder.EmitJMP(helperEntrypoint); + } + break; + + case ReadyToRunHelperId.DelegateCtor: + { + // This is a weird helper. Codegen populated Arg0 and Arg1 with the values that the constructor + // method expects. Codegen also passed us the generic context in Arg2. + // We now need to load the delegate target method into Arg2 (using a dictionary lookup) + // and the optional 4th parameter, and call the ctor. + + Debug.Assert(contextRegister == encoder.TargetRegister.Arg2); + + var target = (DelegateCreationInfo)_target; + + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, _lookupSignature, relocsOnly); + + if (target.Thunk != null) + { + Debug.Assert(target.Constructor.Method.Signature.Length == 3); + encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk); + } + else + { + Debug.Assert(target.Constructor.Method.Signature.Length == 2); + } + + encoder.EmitJMP(target.Constructor); + } + break; + + // These are all simple: just get the thing from the dictionary and we're done + case ReadyToRunHelperId.TypeHandle: + case ReadyToRunHelperId.MethodHandle: + case ReadyToRunHelperId.FieldHandle: + case ReadyToRunHelperId.MethodDictionary: + case ReadyToRunHelperId.MethodEntry: + case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DefaultConstructor: + case ReadyToRunHelperId.ObjectAllocator: + case ReadyToRunHelperId.TypeHandleForCasting: + { + EmitDictionaryLookup(factory, ref encoder, contextRegister, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + encoder.EmitRET(); + } + break; + default: + throw new NotImplementedException(); + } + } + + protected virtual void EmitLoadGenericContext(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) + { + // Assume generic context is already loaded in the context register. + } + } + + partial class ReadyToRunGenericLookupFromTypeNode + { + protected override void EmitLoadGenericContext(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) + { + // We start with context register pointing to the MethodTable + Register contextRegister = GetContextRegister(ref encoder); + + // Locate the VTable slot that points to the dictionary + int vtableSlot = 0; + if (!relocsOnly) + { + // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. + vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)_dictionaryOwner); + } + + int pointerSize = factory.Target.PointerSize; + int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize); + + // Load the dictionary pointer from the VTable + AddrMode loadDictionary = new AddrMode(contextRegister, null, slotOffset, 0, AddrModeSize.Int64); + encoder.EmitMOV(contextRegister, ref loadDictionary); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs new file mode 100644 index 00000000000000..07ab13b13d2c70 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs @@ -0,0 +1,203 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +using ILCompiler.DependencyAnalysis.X64; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// X64 specific portions of ReadyToRunHelperNode + /// + public partial class ReadyToRunHelperNode + { + protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) + { + switch (Id) + { + case ReadyToRunHelperId.VirtualCall: + { + MethodDesc targetMethod = (MethodDesc)Target; + + Debug.Assert(!targetMethod.OwningType.IsInterface); + Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); + + AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); + + int pointerSize = factory.Target.PointerSize; + + int slot = 0; + if (!relocsOnly) + { + slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); + Debug.Assert(slot != -1); + } + Debug.Assert(((NativeSequencePoint[])((INodeWithDebugInfo)this).GetNativeSequencePoints())[1].NativeOffset == encoder.Builder.CountBytes); + + AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64); + encoder.EmitJmpToAddrMode(ref jmpAddrMode); + } + break; + + case ReadyToRunHelperId.GetNonGCStaticBase: + { + MetadataType target = (MetadataType)Target; + bool hasLazyStaticConstructor = factory.PreinitializationManager.HasLazyStaticConstructor(target); + encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); + + if (!hasLazyStaticConstructor) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + + AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); + encoder.EmitCMP(ref initialized, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetThreadStaticBase: + { + MetadataType target = (MetadataType)Target; + + encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); + + // First arg: address of the TypeManager slot that provides the helper with + // information about module index and the type manager instance (which is used + // for initialization on first access). + AddrMode loadFromArg2 = new AddrMode(encoder.TargetRegister.Arg2, null, 0, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg2); + + // Second arg: index of the type in the ThreadStatic section of the modules + AddrMode loadFromArg2AndDelta = new AddrMode(encoder.TargetRegister.Arg2, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); + encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg2AndDelta); + + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitJMP(factory.ExternSymbol("RhpGetThreadStaticBaseForType")); + } + else + { + encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target), - NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + + AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg2, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); + encoder.EmitCMP(ref initialized, 1); + encoder.EmitJE(factory.ExternSymbol("RhpGetThreadStaticBaseForType")); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetGCStaticBase: + { + MetadataType target = (MetadataType)Target; + + encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); + AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); + + if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + + AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); + encoder.EmitCMP(ref initialized, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.DelegateCtor: + { + DelegateCreationInfo target = (DelegateCreationInfo)Target; + + if (target.TargetNeedsVTableLookup) + { + Debug.Assert(!target.TargetMethod.CanMethodBeInSealedVTable()); + + AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromThisPtr); + + int slot = 0; + if (!relocsOnly) + slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod, target.TargetMethod.OwningType); + + Debug.Assert(slot != -1); + AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Arg2, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromSlot); + } + else + { + ISymbolNode targetMethodNode = target.GetTargetNode(factory); + encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.GetTargetNode(factory)); + } + + if (target.Thunk != null) + { + Debug.Assert(target.Constructor.Method.Signature.Length == 3); + encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk); + } + else + { + Debug.Assert(target.Constructor.Method.Signature.Length == 2); + } + + encoder.EmitJMP(target.Constructor); + } + break; + + case ReadyToRunHelperId.ResolveVirtualFunction: + { + MethodDesc targetMethod = (MethodDesc)Target; + if (targetMethod.OwningType.IsInterface) + { + encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); + encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); + } + else + { + if (relocsOnly) + break; + + AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); + + Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); + + int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); + Debug.Assert(slot != -1); + AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot); + encoder.EmitRET(); + } + } + break; + + default: + throw new NotImplementedException(); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64TentativeMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64TentativeMethodNode.cs new file mode 100644 index 00000000000000..ed95dbc106f16f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64TentativeMethodNode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.X64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class TentativeMethodNode + { + protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(GetTarget(factory)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64UnboxingStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64UnboxingStubNode.cs new file mode 100644 index 00000000000000..c8c894b15dedf1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64UnboxingStubNode.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.X64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class UnboxingStubNode + { + protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) + { + AddrMode thisPtr = new AddrMode( + Register.RegDirect | encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); + encoder.EmitADD(ref thisPtr, (sbyte)factory.Target.PointerSize); + encoder.EmitJMP(GetUnderlyingMethodEntrypoint(factory)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86JumpStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86JumpStubNode.cs new file mode 100644 index 00000000000000..2767f552ece525 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86JumpStubNode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.X86; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class JumpStubNode + { + protected override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(_target); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunGenericHelperNode.cs new file mode 100644 index 00000000000000..0367ef8a24a4eb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunGenericHelperNode.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using ILCompiler.DependencyAnalysis.X86; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + partial class ReadyToRunGenericHelperNode + { + protected sealed override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bool relocsOnly) + { + encoder.EmitINT3(); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs new file mode 100644 index 00000000000000..5d4c1502030184 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using ILCompiler.DependencyAnalysis.X86; + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// X86 specific portions of ReadyToRunHelperNode + /// + public partial class ReadyToRunHelperNode + { + protected override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bool relocsOnly) + { + switch (Id) + { + case ReadyToRunHelperId.VirtualCall: + { + encoder.EmitINT3(); + } + break; + + case ReadyToRunHelperId.GetNonGCStaticBase: + { + MetadataType target = (MetadataType)Target; + bool hasLazyStaticConstructor = factory.PreinitializationManager.HasLazyStaticConstructor(target); + encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); + + if (!hasLazyStaticConstructor) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitMOV(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); + + AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); + encoder.EmitCMP(ref initialized, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetThreadStaticBase: + { + encoder.EmitINT3(); + } + break; + + case ReadyToRunHelperId.GetGCStaticBase: + { + encoder.EmitINT3(); + } + break; + + case ReadyToRunHelperId.DelegateCtor: + { + encoder.EmitINT3(); + } + break; + + case ReadyToRunHelperId.ResolveVirtualFunction: + { + encoder.EmitINT3(); + } + break; + + default: + throw new NotImplementedException(); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86TentativeMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86TentativeMethodNode.cs new file mode 100644 index 00000000000000..319f520f5f73b3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86TentativeMethodNode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.X86; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class TentativeMethodNode + { + protected override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(GetTarget(factory)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86UnboxingStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86UnboxingStubNode.cs new file mode 100644 index 00000000000000..6a218ecbc2e216 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86UnboxingStubNode.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis.X86; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class UnboxingStubNode + { + protected override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bool relocsOnly) + { + AddrMode thisPtr = new AddrMode( + Register.RegDirect | encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int32); + encoder.EmitADD(ref thisPtr, (sbyte)factory.Target.PointerSize); + encoder.EmitJMP(GetUnderlyingMethodEntrypoint(factory)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TentativeInstanceMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TentativeInstanceMethodNode.cs new file mode 100644 index 00000000000000..59a45be86dc597 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TentativeInstanceMethodNode.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.IL; +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a stand-in for a real method body that can turn into the real method + /// body at object emission phase if the real method body was marked. + /// It the real method body wasn't marked, this stub will tail-call into a throw helper. + /// This node conditionally depends on the real method body - the real method body + /// will be brough into compilation if the owning type was marked. + /// This operates under the assumption that instance methods don't get called with + /// a null `this`. While it's possible to call instance methods with a null `this`, + /// there are many reasons why it's a bad idea (C# language assumes `this` can never be null; + /// generically shared native code generated by this compiler assumes `this` is not null and + /// we can get the generic dictionary out of it, etc.). + public class TentativeInstanceMethodNode : TentativeMethodNode + { + public TentativeInstanceMethodNode(IMethodBodyNode methodNode) + : base(methodNode) + { + Debug.Assert(!methodNode.Method.Signature.IsStatic); + Debug.Assert(!methodNode.Method.OwningType.IsValueType); + } + + public override bool HasConditionalStaticDependencies => true; + + protected override ISymbolNode GetTarget(NodeFactory factory) + { + // If the class library doesn't provide this helper, the optimization is disabled. + MethodDesc helper = factory.InstanceMethodRemovedHelper; + return helper == null ? RealBody: factory.MethodEntrypoint(helper); + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + // Convert methods on Array into T[] + TypeDesc owningType = Method.OwningType; + if (owningType.HasSameTypeDefinition(factory.ArrayOfTClass)) + { + owningType = owningType.Instantiation[0].MakeArrayType(); + } + + // If a constructed symbol for the owning type was included in the compilation, + // include the real method body. + return new CombinedDependencyListEntry[] + { + new CombinedDependencyListEntry( + RealBody, + factory.ConstructedTypeSymbol(owningType), + "Instance method on a constructed type"), + }; + } + + protected override string GetName(NodeFactory factory) + { + return "Tentative instance method: " + RealBody.GetMangledName(factory.NameMangler); + } + + public override int ClassCode => 0x8905207; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TentativeMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TentativeMethodNode.cs new file mode 100644 index 00000000000000..23d9201244526b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TentativeMethodNode.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.IL; +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a stand-in for a real method body that can turn into the real method + /// body at object emission phase if the real method body was marked. + /// It the real method body wasn't marked, this stub will tail-call into a throw helper. + /// + partial class TentativeMethodNode : AssemblyStubNode, IMethodNode, ISymbolNodeWithLinkage + { + private readonly IMethodBodyNode _methodNode; + + public IMethodBodyNode RealBody => _methodNode; + + public TentativeMethodNode(IMethodBodyNode methodNode) + { + _methodNode = methodNode; + } + + protected virtual ISymbolNode GetTarget(NodeFactory factory) + { + // If the class library doesn't provide this helper, the optimization is disabled. + MethodDesc helper = factory.TypeSystemContext.GetOptionalHelperEntryPoint("ThrowHelpers", "ThrowBodyRemoved"); + return helper == null ? RealBody : factory.MethodEntrypoint(helper); + } + + public MethodDesc Method => _methodNode.Method; + + protected override string GetName(NodeFactory factory) + { + return "Tentative method: " + _methodNode.GetMangledName(factory.NameMangler); + } + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) + { + // If the real body was marked, don't emit this assembly stub. + return _methodNode.Marked; + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + // We use the same mangled name as the underlying real method body. + // This is okay since this node will go out of the way if the real body is marked + // and part of the graph. + _methodNode.AppendMangledName(nameMangler, sb); + } + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return _methodNode.CompareToImpl(((TentativeMethodNode)other)._methodNode, comparer); + } + + public ISymbolNode NodeForLinkage(NodeFactory factory) + { + // If someone refers to this node but the real method was marked, emit relocs to this + // as relocs to the real method. + return _methodNode.Marked ? _methodNode : (ISymbolNode)this; + } + + public override bool RepresentsIndirectionCell + { + get + { + Debug.Assert(!_methodNode.RepresentsIndirectionCell); + return false; + } + } + + public override int ClassCode => 0x562912; + + public override bool IsShareable => ((ObjectNode)_methodNode).IsShareable; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ThreadStaticsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ThreadStaticsNode.cs new file mode 100644 index 00000000000000..c52395f690e2a1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ThreadStaticsNode.cs @@ -0,0 +1,90 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents the thread static region of a given type. This is very similar to , + /// since the actual storage will be allocated on the GC heap at runtime and is allowed to contain GC pointers. + /// + public class ThreadStaticsNode : EmbeddedObjectNode, ISymbolDefinitionNode + { + private MetadataType _type; + + public ThreadStaticsNode(MetadataType type, NodeFactory factory) + { + Debug.Assert(factory.Target.Abi == TargetAbi.CoreRT || factory.Target.Abi == TargetAbi.CppCodegen); + _type = type; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + protected override void OnMarked(NodeFactory factory) + { + factory.ThreadStaticsRegion.AddEmbeddedObject(this); + } + + public static string GetMangledName(TypeDesc type, NameMangler nameMangler) + { + return nameMangler.NodeMangler.ThreadStatics(type); + } + + int ISymbolNode.Offset => 0; + + int ISymbolDefinitionNode.Offset => OffsetFromBeginningOfArray; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetMangledName(_type, nameMangler)); + } + + private ISymbolNode GetGCStaticEETypeNode(NodeFactory factory) + { + GCPointerMap map = GCPointerMap.FromThreadStaticLayout(_type); + return factory.GCStaticEEType(map); + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyList result = new DependencyList(); + + result.Add(new DependencyListEntry(GetGCStaticEETypeNode(factory), "ThreadStatic MethodTable")); + + if (factory.PreinitializationManager.HasEagerStaticConstructor(_type)) + { + result.Add(new DependencyListEntry(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor")); + } + + if (_type.Module.GetGlobalModuleType().GetStaticConstructor() is MethodDesc moduleCctor) + { + result.Add(factory.MethodEntrypoint(moduleCctor), "Static base in a module with initializer"); + } + + EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref result); + return result; + } + + public override bool StaticDependenciesAreComputed => true; + + public override void EncodeData(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly) + { + // At runtime, an instance of the GCStaticEEType will be created and a GCHandle to it + // will be written in this location. + builder.RequireInitialPointerAlignment(); + builder.EmitPointerReloc(GetGCStaticEETypeNode(factory)); + } + + public override int ClassCode => 2091208431; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((ThreadStaticsNode)other)._type); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs new file mode 100644 index 00000000000000..fe9d0cd8b89272 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// This node is used for GVM dependency analysis and GVM tables building. Given an input + /// type, this node can scan the type for a list of GVM table entries, and compute their dependencies. + /// + internal sealed class TypeGVMEntriesNode : DependencyNodeCore + { + internal class TypeGVMEntryInfo + { + public TypeGVMEntryInfo(MethodDesc callingMethod, MethodDesc implementationMethod) + { + CallingMethod = callingMethod; + ImplementationMethod = implementationMethod; + } + public MethodDesc CallingMethod { get; } + public MethodDesc ImplementationMethod { get; } + } + + internal class InterfaceGVMEntryInfo : TypeGVMEntryInfo + { + public InterfaceGVMEntryInfo(MethodDesc callingMethod, MethodDesc implementationMethod, + TypeDesc implementationType, DefaultInterfaceMethodResolution defaultResolution) + : base(callingMethod, implementationMethod) + { + ImplementationType = implementationType; + DefaultResolution = defaultResolution; + } + + public TypeDesc ImplementationType { get; } + public DefaultInterfaceMethodResolution DefaultResolution { get; } + } + + private readonly TypeDesc _associatedType; + private DependencyList _staticDependencies; + + public TypeGVMEntriesNode(TypeDesc associatedType) + { + Debug.Assert(associatedType.IsTypeDefinition); + Debug.Assert(TypeNeedsGVMTableEntries(associatedType)); + _associatedType = associatedType; + } + + public override bool HasConditionalStaticDependencies => false; + public override bool HasDynamicDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => "__TypeGVMEntriesNode_" + factory.NameMangler.GetMangledTypeName(_associatedType); + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + if (_staticDependencies == null) + { + _staticDependencies = new DependencyList(); + + foreach(var entry in ScanForGenericVirtualMethodEntries()) + GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationMethod); + + foreach (var entry in ScanForInterfaceGenericVirtualMethodEntries()) + InterfaceGenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationType, entry.ImplementationMethod); + } + + return _staticDependencies; + } + + public static bool TypeNeedsGVMTableEntries(TypeDesc type) + { + // Only non-interface deftypes can have entries for their GVMs in the GVM hashtables. + // Interface GVM entries are computed for types that implemenent the interface (not for the interface on its own) + if (!type.IsDefType || type.IsInterface) + return false; + + // Type declares GVMs + if (type.HasGenericVirtualMethods()) + return true; + + // + // Check if the type implements any interface with GVM methods, where the method implementations could be on + // base types. + // Example: + // interface IFace { + // void IFaceGVMethod(); + // } + // class BaseClass { + // public virtual void IFaceGVMethod() { ... } + // } + // public class DerivedClass : BaseClass, IFace { } + // + foreach (var iface in type.RuntimeInterfaces) + { + foreach (var method in iface.GetVirtualMethods()) + { + if (!method.HasInstantiation || method.Signature.IsStatic) + continue; + + MethodDesc slotDecl = type.ResolveInterfaceMethodTarget(method); + if (slotDecl == null) + { + var resolution = type.ResolveInterfaceMethodToDefaultImplementationOnType(method, out slotDecl); + if (resolution == DefaultInterfaceMethodResolution.None) + slotDecl = null; + } + + if (slotDecl != null) + return true; + } + } + + return false; + } + + public IEnumerable ScanForGenericVirtualMethodEntries() + { + foreach (MethodDesc decl in _associatedType.EnumAllVirtualSlots()) + { + // Non-Generic virtual methods are tracked by an orthogonal mechanism. + if (!decl.HasInstantiation) + continue; + + MethodDesc impl = _associatedType.FindVirtualFunctionTargetMethodOnObjectType(decl); + + if (impl.OwningType == _associatedType) + yield return new TypeGVMEntryInfo(decl, impl); + } + } + + public IEnumerable ScanForInterfaceGenericVirtualMethodEntries() + { + foreach (var iface in _associatedType.RuntimeInterfaces) + { + foreach (var method in iface.GetVirtualMethods()) + { + Debug.Assert(!method.Signature.IsStatic); + + if (!method.HasInstantiation) + continue; + + DefaultInterfaceMethodResolution resolution = DefaultInterfaceMethodResolution.None; + MethodDesc slotDecl = _associatedType.ResolveInterfaceMethodTarget(method); + if (slotDecl == null) + { + resolution = _associatedType.ResolveInterfaceMethodToDefaultImplementationOnType(method, out slotDecl); + if (resolution != DefaultInterfaceMethodResolution.DefaultImplementation) + slotDecl = null; + } + + if (slotDecl != null + || resolution == DefaultInterfaceMethodResolution.Diamond + || resolution == DefaultInterfaceMethodResolution.Reabstraction) + { + yield return new InterfaceGVMEntryInfo(method, slotDecl, _associatedType, resolution); + } + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeManagerIndirectionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeManagerIndirectionNode.cs new file mode 100644 index 00000000000000..8e737fde4fb91c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeManagerIndirectionNode.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class TypeManagerIndirectionNode : ObjectNode, ISymbolDefinitionNode + { + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__typemanager_indirection"); + } + public int Offset => 0; + public override bool IsShareable => false; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + + public override bool StaticDependenciesAreComputed => true; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.AddSymbol(this); + objData.RequireInitialPointerAlignment(); + objData.EmitZeroPointer(); + objData.EmitZeroPointer(); + return objData.ToObjectData(); + } + + public override int ClassCode => -2028598574; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataMapNode.cs new file mode 100644 index 00000000000000..ab69129df3f278 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataMapNode.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.NativeFormat; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a map between EETypes and metadata records within the . + /// + public sealed class TypeMetadataMapNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public TypeMetadataMapNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__type_to_metadata_map_End", true); + _externalReferences = externalReferences; + } + + public ISymbolNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__type_to_metadata_map"); + } + public int Offset => 0; + public override bool IsShareable => false; + + public override ObjectNodeSection Section => _externalReferences.Section; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + var writer = new NativeWriter(); + var typeMapHashTable = new VertexHashtable(); + + Section hashTableSection = writer.NewSection(); + hashTableSection.Place(typeMapHashTable); + + foreach (var mappingEntry in factory.MetadataManager.GetTypeDefinitionMapping(factory)) + { + // Types that don't have EETypes don't need mapping table entries because there's no risk of them + // not unifying to the same System.Type at runtime. + if (!factory.MetadataManager.TypeGeneratesEEType(mappingEntry.Entity) && !factory.CompilationModuleGroup.ShouldReferenceThroughImportTable(mappingEntry.Entity)) + continue; + + // Go with a necessary type symbol. It will be upgraded to a constructed one if a constructed was emitted. + IEETypeNode typeSymbol = factory.NecessaryTypeSymbol(mappingEntry.Entity); + + Vertex vertex = writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(typeSymbol)), + writer.GetUnsignedConstant((uint)mappingEntry.MetadataHandle) + ); + + int hashCode = typeSymbol.Type.GetHashCode(); + typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + public override int ClassCode => (int)ObjectNodeOrder.TypeMetadataMapNode; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs new file mode 100644 index 00000000000000..e2bff8eb335f23 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; +using EcmaType = Internal.TypeSystem.Ecma.EcmaType; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a type that has metadata generated in the current compilation. + /// + /// + /// Only expected to be used during ILScanning when scanning for reflection. + /// + internal class TypeMetadataNode : DependencyNodeCore + { + private readonly MetadataType _type; + + public TypeMetadataNode(MetadataType type) + { + Debug.Assert(type.IsTypeDefinition); + _type = type; + } + + public MetadataType Type => _type; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + + CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, ((EcmaType)_type)); + + DefType containingType = _type.ContainingType; + if (containingType != null) + dependencies.Add(factory.TypeMetadata((MetadataType)containingType), "Containing type of a reflectable type"); + else + dependencies.Add(factory.ModuleMetadata(_type.Module), "Containing module of a reflectable type"); + + var mdManager = (UsageBasedMetadataManager)factory.MetadataManager; + if (_type.IsDelegate) + { + // A delegate type metadata is rather useless without the Invoke method. + // If someone reflects on a delegate, chances are they're going to look at the signature. + var invokeMethod = _type.GetMethod("Invoke", null); + if (!mdManager.IsReflectionBlocked(invokeMethod)) + dependencies.Add(factory.MethodMetadata(invokeMethod), "Delegate invoke method metadata"); + } + + if (_type.IsEnum) + { + // A lot of the enum reflection actually happens on top of the respective MethodTable (e.g. getting the underlying type), + // so for enums also include their MethodTable. + dependencies.Add(factory.MaximallyConstructableType(_type), "Reflectable enum"); + } + + // If the user asked for complete metadata to be generated for all types that are getting metadata, ensure that. + if ((mdManager._generationOptions & UsageBasedMetadataGenerationOptions.CompleteTypesOnly) != 0) + { + foreach (MethodDesc method in _type.GetMethods()) + { + if (!mdManager.IsReflectionBlocked(method)) + { + try + { + // Make sure we're not adding a method to the dependency graph that is going to + // cause trouble down the line. This entire type would not actually load on CoreCLR anyway. + LibraryRootProvider.CheckCanGenerateMethod(method); + } + catch (TypeSystemException) + { + continue; + } + + dependencies.Add(factory.MethodMetadata(method), "Complete metadata for type"); + } + } + + foreach (FieldDesc field in _type.GetFields()) + { + if (!mdManager.IsReflectionBlocked(field)) + dependencies.Add(factory.FieldMetadata(field), "Complete metadata for type"); + } + } + + return dependencies; + } + + /// + /// Decomposes a constructed type into individual units that will be needed to + /// express the constructed type in metadata. + /// + public static void GetMetadataDependencies(ref DependencyList dependencies, NodeFactory nodeFactory, TypeDesc type, string reason) + { + MetadataManager mdManager = nodeFactory.MetadataManager; + + switch (type.Category) + { + case TypeFlags.Array: + case TypeFlags.SzArray: + case TypeFlags.ByRef: + case TypeFlags.Pointer: + GetMetadataDependencies(ref dependencies, nodeFactory, ((ParameterizedType)type).ParameterType, reason); + break; + case TypeFlags.FunctionPointer: + var pointerType = (FunctionPointerType)type; + GetMetadataDependencies(ref dependencies, nodeFactory, pointerType.Signature.ReturnType, reason); + foreach (TypeDesc paramType in pointerType.Signature) + GetMetadataDependencies(ref dependencies, nodeFactory, paramType, reason); + break; + + case TypeFlags.SignatureMethodVariable: + case TypeFlags.SignatureTypeVariable: + break; + + default: + Debug.Assert(type.IsDefType); + + TypeDesc typeDefinition = type.GetTypeDefinition(); + if (typeDefinition != type) + { + if (mdManager.CanGenerateMetadata((MetadataType)typeDefinition)) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(nodeFactory.TypeMetadata((MetadataType)typeDefinition), reason); + } + + foreach (TypeDesc typeArg in type.Instantiation) + { + GetMetadataDependencies(ref dependencies, nodeFactory, typeArg, reason); + } + } + else + { + if (mdManager.CanGenerateMetadata((MetadataType)type)) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(nodeFactory.TypeMetadata((MetadataType)type), reason); + } + } + break; + } + } + + protected override string GetName(NodeFactory factory) + { + return "Reflectable type: " + _type.ToString(); + } + + protected override void OnMarked(NodeFactory factory) + { + Debug.Assert(!factory.MetadataManager.IsReflectionBlocked(_type)); + Debug.Assert(factory.MetadataManager.CanGenerateMetadata(_type)); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs new file mode 100644 index 00000000000000..d8b65d2a3453f4 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a node containing information necessary at runtime to locate type's thread static base. + /// + public class TypeThreadStaticIndexNode : ObjectNode, ISymbolDefinitionNode, ISortableSymbolNode + { + private MetadataType _type; + + public TypeThreadStaticIndexNode(MetadataType type) + { + _type = type; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.NodeMangler.ThreadStaticsIndex(_type)); + } + public int Offset => 0; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + public override ObjectNodeSection Section + { + get + { + if (_type.Context.Target.IsWindows) + return ObjectNodeSection.ReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } + } + public override bool IsShareable => true; + public override bool StaticDependenciesAreComputed => true; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + return new DependencyList + { + new DependencyListEntry(factory.TypeThreadStaticsSymbol(_type), "Thread static storage") + }; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + + objData.RequireInitialPointerAlignment(); + objData.AddSymbol(this); + + int typeTlsIndex = 0; + if (!relocsOnly) + { + var node = factory.TypeThreadStaticsSymbol(_type); + typeTlsIndex = ((ThreadStaticsNode)node).IndexFromBeginningOfArray; + } + + objData.EmitPointerReloc(factory.TypeManagerIndirection); + objData.EmitNaturalInt(typeTlsIndex); + + return objData.ToObjectData(); + } + + public MetadataType Type => _type; + + public override int ClassCode => -149601250; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((TypeThreadStaticIndexNode)other)._type); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/UnboxingStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/UnboxingStubNode.cs new file mode 100644 index 00000000000000..29f9222b3011b5 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/UnboxingStubNode.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Text; +using Internal.TypeSystem; + +using ILCompiler.DependencyAnalysisFramework; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents an unboxing stub that supports calling instance methods on boxed valuetypes. + /// + public partial class UnboxingStubNode : AssemblyStubNode, IMethodNode, ISymbolDefinitionNode + { + // Section name on Windows has to be alphabetically less than the ending WindowsUnboxingStubsRegionNode node, and larger than + // the begining WindowsUnboxingStubsRegionNode node, in order to have proper delimiters to the begining/ending of the + // stubs region, in order for the runtime to know where the region starts and ends. + internal static readonly string WindowsSectionName = ".unbox$M"; + internal static readonly string UnixSectionName = "__unbox"; + + private readonly TargetDetails _targetDetails; + + public MethodDesc Method { get; } + + public override ObjectNodeSection Section + { + get + { + string sectionName = _targetDetails.IsWindows ? WindowsSectionName : UnixSectionName; + return new ObjectNodeSection(sectionName, SectionType.Executable); + } + } + public override bool IsShareable => true; + + public UnboxingStubNode(MethodDesc target, TargetDetails targetDetails) + { + Debug.Assert(target.GetCanonMethodTarget(CanonicalFormKind.Specific) == target); + Debug.Assert(target.OwningType.IsValueType); + Method = target; + _targetDetails = targetDetails; + } + + private ISymbolNode GetUnderlyingMethodEntrypoint(NodeFactory factory) + { + ISymbolNode node = factory.MethodEntrypoint(Method); + return node; + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("unbox_").Append(nameMangler.GetMangledMethodName(Method)); + } + + public static string GetMangledName(NameMangler nameMangler, MethodDesc method) + { + return "unbox_" + nameMangler.GetMangledMethodName(method); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override int ClassCode => -1846923013; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(Method, ((UnboxingStubNode)other).Method); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VTableSliceNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VTableSliceNode.cs new file mode 100644 index 00000000000000..54c1fb215ddc82 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VTableSliceNode.cs @@ -0,0 +1,246 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysisFramework; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents the VTable for a type's slice. For example, System.String's VTableSliceNode includes virtual + /// slots added by System.String itself, System.Object's VTableSliceNode contains the virtuals it defines. + /// + public abstract class VTableSliceNode : DependencyNodeCore + { + protected TypeDesc _type; + + public VTableSliceNode(TypeDesc type) + { + Debug.Assert(!type.IsArray, "Wanted to call GetClosestDefType?"); + _type = type; + } + + public abstract IReadOnlyList Slots + { + get; + } + + public TypeDesc Type => _type; + + /// + /// Gets a value indicating whether the slots are assigned at the beginning of the compilation. + /// + public abstract bool HasFixedSlots + { + get; + } + + protected override string GetName(NodeFactory factory) => $"__vtable_{factory.NameMangler.GetMangledTypeName(_type).ToString()}"; + + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + if (_type.HasBaseType) + { + return new[] { new DependencyListEntry(factory.VTable(_type.BaseType), "Base type VTable") }; + } + + return null; + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + } + + /// + /// Represents a VTable slice with fixed slots whose assignment was determined at the time the slice was allocated. + /// + internal class PrecomputedVTableSliceNode : VTableSliceNode + { + private readonly IReadOnlyList _slots; + + public PrecomputedVTableSliceNode(TypeDesc type, IReadOnlyList slots) + : base(type) + { + _slots = slots; + } + + public override IReadOnlyList Slots + { + get + { + return _slots; + } + } + + public override bool HasFixedSlots + { + get + { + return true; + } + } + } + + /// + /// Represents a VTable slice for a complete type - a type with all virtual method slots generated, + /// irrespective of whether they are used. + /// + internal sealed class EagerlyBuiltVTableSliceNode : PrecomputedVTableSliceNode + { + public EagerlyBuiltVTableSliceNode(TypeDesc type) + : base(type, ComputeSlots(type)) + { + } + + private static IReadOnlyList ComputeSlots(TypeDesc type) + { + var slots = new ArrayBuilder(); + + bool isObjectType = type.IsObject; + DefType defType = type.GetClosestDefType(); + + IEnumerable allSlots = type.IsInterface ? + type.GetAllVirtualMethods() : defType.EnumAllVirtualSlots(); + + foreach (var method in allSlots) + { + // Static virtual methods don't go in vtables + if (method.Signature.IsStatic) + continue; + + // GVMs are not emitted in the type's vtable. + if (method.HasInstantiation) + continue; + + // Finalizers are called via a field on the MethodTable, not through the VTable + if (isObjectType && method.Name == "Finalize") + continue; + + // Current type doesn't define this slot. + if (method.OwningType != defType) + continue; + + slots.Add(method); + } + + return slots.ToArray(); + } + } + + /// + /// Represents a VTable slice where slots are built on demand. Only the slots that are actually used + /// will be generated. + /// + internal sealed class LazilyBuiltVTableSliceNode : VTableSliceNode + { + private HashSet _usedMethods = new HashSet(); + private MethodDesc[] _slots; + + public LazilyBuiltVTableSliceNode(TypeDesc type) + : base(type) + { + } + + public override IReadOnlyList Slots + { + get + { + if (_slots == null) + { + // Sort the lazily populated slots in metadata order (the order in which they show up + // in GetAllMethods()). + // This ensures that Foo and Foo will end up with the same vtable + // no matter the order in which VirtualMethodUse nodes populated it. + ArrayBuilder slotsBuilder = new ArrayBuilder(); + DefType defType = _type.GetClosestDefType(); + foreach (var method in defType.GetAllMethods()) + { + if (_usedMethods.Contains(method)) + slotsBuilder.Add(method); + } + Debug.Assert(_usedMethods.Count == slotsBuilder.Count); + _slots = slotsBuilder.ToArray(); + + // Null out used methods so that we AV if someone tries to add now. + _usedMethods = null; + } + + return _slots; + } + } + + public override bool HasFixedSlots + { + get + { + return false; + } + } + + public void AddEntry(NodeFactory factory, MethodDesc virtualMethod) + { + // GVMs are not emitted in the type's vtable. + Debug.Assert(!virtualMethod.HasInstantiation); + Debug.Assert(virtualMethod.IsVirtual); + Debug.Assert(_slots == null && _usedMethods != null); + Debug.Assert(virtualMethod.OwningType == _type); + + // Finalizers are called via a field on the MethodTable, not through the VTable + if (_type.IsObject && virtualMethod.Name == "Finalize") + return; + + _usedMethods.Add(virtualMethod); + } + + public override bool HasConditionalStaticDependencies + { + get + { + return _type.ConvertToCanonForm(CanonicalFormKind.Specific) != _type; + } + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + // VirtualMethodUse of Foo.Method will bring in VirtualMethodUse + // of Foo<__Canon>.Method. This in turn should bring in Foo.Method. + DefType defType = _type.GetClosestDefType(); + + IEnumerable allSlots = _type.IsInterface ? + _type.GetAllVirtualMethods() : defType.EnumAllVirtualSlots(); + + foreach (var method in allSlots) + { + // Generic virtual methods are tracked by an orthogonal mechanism. + if (method.HasInstantiation) + continue; + + // Current type doesn't define this slot. Another VTableSlice will take care of this. + if (method.OwningType != defType) + continue; + + if (defType.Context.SupportsCanon) + yield return new CombinedDependencyListEntry( + factory.VirtualMethodUse(method), + factory.VirtualMethodUse(method.GetCanonMethodTarget(CanonicalFormKind.Specific)), + "Canonically equivalent virtual method use"); + + if (defType.Context.SupportsUniversalCanon) + yield return new CombinedDependencyListEntry( + factory.VirtualMethodUse(method), + factory.VirtualMethodUse(method.GetCanonMethodTarget(CanonicalFormKind.Universal)), + "Universal Canonically equivalent virtual method use"); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VariantInterfaceMethodUseNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VariantInterfaceMethodUseNode.cs new file mode 100644 index 00000000000000..c5e1d27a53c134 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VariantInterfaceMethodUseNode.cs @@ -0,0 +1,155 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + // This node represents the concept of a variant interface method being used. + // It has no direct depedencies, but may be referred to by conditional static + // dependencies, or static dependencies from elsewhere. + // + // We only track the generic definition of the interface method being used. + // There's a potential optimization opportunity because e.g. the fact that + // IEnumerable.GetEnumerator() is used doesn't mean that + // IEnumerable.GetEnumerator() is used, but this complicates the tracking. + internal class VariantInterfaceMethodUseNode : DependencyNodeCore + { + private readonly MethodDesc _decl; + + public VariantInterfaceMethodUseNode(MethodDesc decl) + { + Debug.Assert(!decl.IsRuntimeDeterminedExactMethod); + Debug.Assert(decl.IsVirtual); + Debug.Assert(decl.OwningType.IsInterface); + + // Virtual method use always represents the slot defining method of the virtual. + // Places that might see virtual methods being used through an override need to normalize + // to the slot defining method. + Debug.Assert(MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(decl) == decl); + + // Generic virtual methods are tracked by an orthogonal mechanism. + Debug.Assert(!decl.HasInstantiation); + + // We currently track this for definitions only + Debug.Assert(decl.IsTypicalMethodDefinition); + + _decl = decl; + } + + public static bool IsVariantInterfaceImplementation(NodeFactory factory, TypeDesc providingType, DefType implementedInterface) + { + Debug.Assert(implementedInterface.IsInterface); + Debug.Assert(!implementedInterface.IsGenericDefinition); + + // If any of the implemented interfaces have variance, calls against compatible interface methods + // could result in interface methods of this type being used (e.g. IEnumerable.GetEnumerator() + // can dispatch to an implementation of IEnumerable.GetEnumerator()). + bool result = false; + if (implementedInterface.HasVariance) + { + TypeDesc interfaceDefinition = implementedInterface.GetTypeDefinition(); + for (int i = 0; i < interfaceDefinition.Instantiation.Length; i++) + { + var variantParameter = (GenericParameterDesc)interfaceDefinition.Instantiation[i]; + if (variantParameter.Variance != 0) + { + // Variant interface parameters that are instantiated over valuetypes are + // not actually variant. Neither are contravariant parameters instantiated + // over sealed types (there won't be another interface castable to it + // through variance on that parameter). + TypeDesc variantArgument = implementedInterface.Instantiation[i]; + if (!variantArgument.IsValueType + && (!variantArgument.IsSealed() || variantParameter.IsCovariant)) + { + result = true; + break; + } + } + } + } + + if (!result && + (providingType.IsArray || providingType.GetTypeDefinition() == factory.ArrayOfTEnumeratorType) && + implementedInterface.HasInstantiation) + { + // We need to also do this for generic interfaces on arrays because they have a weird casting rule + // that doesn't require the implemented interface to be variant to consider it castable. + // For value types, we only need this when the array is castable by size (int[] and ICollection), + // or it's a reference type (Derived[] and ICollection). + TypeDesc elementType = providingType.IsArray ? ((ArrayType)providingType).ElementType : providingType.Instantiation[0]; + result = + CastingHelper.IsArrayElementTypeCastableBySize(elementType) || + (elementType.IsDefType && !elementType.IsValueType); + } + + return result; + } + + public static bool IsVariantMethodCall(NodeFactory factory, MethodDesc calledMethod) + { + Debug.Assert(calledMethod.IsVirtual); + + TypeDesc owningType = calledMethod.OwningType; + + if (!owningType.IsInterface) + return false; + + bool result = false; + if (owningType.HasVariance) + { + TypeDesc owningTypeDefinition = owningType.GetTypeDefinition(); + for (int i = 0; i < owningTypeDefinition.Instantiation.Length; i++) + { + var variantParameter = (GenericParameterDesc)owningTypeDefinition.Instantiation[i]; + if (variantParameter.Variance != 0) + { + // Variant interface parameters that are instantiated over valuetypes are + // not actually variant. Neither are contravariant parameters instantiated + // over sealed types (there won't be another interface castable to it + // through variance on that parameter). + TypeDesc variantArgument = owningType.Instantiation[i]; + if (!variantArgument.IsValueType + && (!variantArgument.IsSealed() || variantParameter.IsCovariant)) + { + result = true; + break; + } + } + } + } + + if (!result && factory.TypeSystemContext.IsGenericArrayInterfaceType(owningType)) + { + // We need to also do this for generic interfaces on arrays because they have a weird casting rule + // that doesn't require the implemented interface to be variant to consider it castable. + // For value types, we only need this when the array is castable by size (int[] and ICollection), + // or it's a reference type (Derived[] and ICollection). + TypeDesc elementType = owningType.Instantiation[0]; + result = + CastingHelper.IsArrayElementTypeCastableBySize(elementType) || + (elementType.IsDefType && !elementType.IsValueType); + } + + return result; + } + + protected override string GetName(NodeFactory factory) => $"VariantInterfaceMethodUse {_decl.ToString()}"; + + public override bool HasConditionalStaticDependencies => false; + public override bool HasDynamicDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) => null; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs new file mode 100644 index 00000000000000..1240dc82df38cf --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + // This node represents the concept of a virtual method being used. + // It has no direct depedencies, but may be referred to by conditional static + // dependencies, or static dependencies from elsewhere. + // + // It is used to keep track of uses of virtual methods to ensure that the + // vtables are properly constructed + internal class VirtualMethodUseNode : DependencyNodeCore + { + private readonly MethodDesc _decl; + + public VirtualMethodUseNode(MethodDesc decl) + { + Debug.Assert(!decl.IsRuntimeDeterminedExactMethod); + Debug.Assert(decl.IsVirtual); + Debug.Assert(!decl.Signature.IsStatic); + + // Virtual method use always represents the slot defining method of the virtual. + // Places that might see virtual methods being used through an override need to normalize + // to the slot defining method. + Debug.Assert(MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(decl) == decl); + + // Generic virtual methods are tracked by an orthogonal mechanism. + Debug.Assert(!decl.HasInstantiation); + + _decl = decl; + } + + protected override string GetName(NodeFactory factory) => $"VirtualMethodUse {_decl.ToString()}"; + + protected override void OnMarked(NodeFactory factory) + { + // If the VTable slice is getting built on demand, the fact that the virtual method is used means + // that the slot is used. + var lazyVTableSlice = factory.VTable(_decl.OwningType) as LazilyBuiltVTableSliceNode; + if (lazyVTableSlice != null) + lazyVTableSlice.AddEntry(factory, _decl); + } + + public override bool HasConditionalStaticDependencies => _decl.Context.SupportsUniversalCanon && _decl.OwningType.HasInstantiation && !_decl.OwningType.IsInterface; + public override bool HasDynamicDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + + MethodDesc canonDecl = _decl.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (canonDecl != _decl) + dependencies.Add(factory.VirtualMethodUse(canonDecl), "Canonical method"); + + dependencies.Add(factory.VTable(_decl.OwningType), "VTable of a VirtualMethodUse"); + + // Do not report things like Foo.Frob(). + if (!_decl.IsCanonicalMethod(CanonicalFormKind.Any) || canonDecl == _decl) + factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref dependencies, factory, _decl); + + if (VariantInterfaceMethodUseNode.IsVariantMethodCall(factory, _decl)) + dependencies.Add(factory.VariantInterfaceMethodUse(_decl.GetTypicalMethodDefinition()), "Variant interface call"); + + return dependencies; + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + Debug.Assert(_decl.OwningType.HasInstantiation); + Debug.Assert(!_decl.OwningType.IsInterface); + Debug.Assert(factory.TypeSystemContext.SupportsUniversalCanon); + + DefType universalCanonicalOwningType = (DefType)_decl.OwningType.ConvertToCanonForm(CanonicalFormKind.Universal); + Debug.Assert(universalCanonicalOwningType.IsCanonicalSubtype(CanonicalFormKind.Universal)); + + if (!factory.VTable(universalCanonicalOwningType).HasFixedSlots) + { + // This code ensures that in cases where we don't structurally force all universal canonical instantiations + // to have full vtables, that we ensure that all vtables are equivalently shaped between universal and non-universal types + return new CombinedDependencyListEntry[] { + new CombinedDependencyListEntry( + factory.VirtualMethodUse(_decl.GetCanonMethodTarget(CanonicalFormKind.Universal)), + factory.NativeLayout.TemplateTypeLayout(universalCanonicalOwningType), + "If universal canon instantiation of method exists, ensure that the universal canonical type has the right set of dependencies") + }; + } + else + { + return Array.Empty(); + } + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DictionaryLayoutProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DictionaryLayoutProvider.cs new file mode 100644 index 00000000000000..1291e4b9d15bfe --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DictionaryLayoutProvider.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler +{ + /// + /// Provides generic dictionary layout information for a specific type or method. + /// + public abstract class DictionaryLayoutProvider + { + public abstract DictionaryLayoutNode GetLayout(TypeSystemEntity methodOrType); + } + + /// + /// Provides dictionary layout information that collects data during the compilation to build a dictionary layout + /// for a type or method on demand. + /// + public sealed class LazyDictionaryLayoutProvider : DictionaryLayoutProvider + { + public override DictionaryLayoutNode GetLayout(TypeSystemEntity methodOrType) + { + return new LazilyBuiltDictionaryLayoutNode(methodOrType); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DirectPInvokePolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DirectPInvokePolicy.cs new file mode 100644 index 00000000000000..cc062cd5172f57 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DirectPInvokePolicy.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.IL; +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// P/Invoke policy that generates direct calls for all methods and avoids lazy resolution + /// of P/invokes at runtime. + /// + public sealed class DirectPInvokePolicy : PInvokeILEmitterConfiguration + { + public override bool GenerateDirectCall(MethodDesc method, out string externName) + { + externName = method.GetPInvokeMethodMetadata().Name ?? method.Name; + return true; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Disassembler.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Disassembler.cs new file mode 100644 index 00000000000000..bacc6b9fff9acd --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Disassembler.cs @@ -0,0 +1,142 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +using ILCompiler.DependencyAnalysis; +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Disassembler based on CoreDisTools. Only available in Debug builds. + /// + public class Disassembler + { + public static string Disassemble(TargetArchitecture arch, byte[] bytes, Relocation[] relocs) + { + var sb = new StringBuilder(); + + // The coredistools library is not available in release builds because + // we don't want to ship yet another huge LLVM-based DLL. +#if DEBUG + SortedList sortedRelocs = new SortedList(relocs.Length); + foreach (Relocation reloc in relocs) + sortedRelocs.Add(reloc.Offset, reloc); + + int relocIndex = 0; + + using (var disasm = new CoreDisassembler(arch)) + { + int offset = 0; + while (offset < bytes.Length) + { + int size = disasm.Disassemble(bytes, offset, out string dis); + if (size == 0) + break; + offset += size; + + // Drop the annoying `\n` + dis = dis.Substring(0, dis.Length - 1); + + sb.Append(dis); + + if (relocIndex < sortedRelocs.Count) + { + Relocation currentReloc = sortedRelocs.Values[relocIndex]; + if (currentReloc.Offset < offset) + { + sb.Append(" // "); + sb.Append(currentReloc.Target.ToString()); + relocIndex++; + } + } + + sb.AppendLine(); + } + } +#else + sb.AppendLine("// CoreDisTools not available in release builds"); +#endif + return sb.ToString(); + } + + private class CoreDisassembler : IDisposable + { + private IntPtr _handle; + + private const string Library = "coredistools"; + + private enum TargetArch + { + Target_X86 = 1, + Target_X64, + Target_Thumb, + Target_Arm64 + }; + + [DllImport(Library)] + private static extern IntPtr InitBufferedDisasm(TargetArch Target); + + public CoreDisassembler(TargetArchitecture arch) + { + _handle = InitBufferedDisasm(arch switch + { + TargetArchitecture.X86 => TargetArch.Target_X86, + TargetArchitecture.X64 => TargetArch.Target_X64, + TargetArchitecture.ARM => TargetArch.Target_Thumb, + TargetArchitecture.ARM64 => TargetArch.Target_Arm64, + _ => throw new NotSupportedException() + }); + + if (_handle == IntPtr.Zero) + throw new OutOfMemoryException(); + } + + [DllImport(Library)] + private static extern int DumpInstruction(IntPtr handle, ulong address, IntPtr bytes, int size); + + public unsafe int Disassemble(byte[] bytes, int offset, out string instruction) + { + int size; + fixed (byte* pByte = &bytes[offset]) + { + size = DumpInstruction(_handle, (ulong)offset, (IntPtr)pByte, bytes.Length - offset); + } + + instruction = Marshal.PtrToStringAnsi(GetOutputBuffer()); + ClearOutputBuffer(); + return size; + } + + [DllImport(Library)] + private static extern IntPtr GetOutputBuffer(); + + [DllImport(Library)] + private static extern void ClearOutputBuffer(); + + [DllImport(Library)] + private static extern void FinishDisasm(IntPtr handle); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + FinishDisasm(_handle); + _handle = IntPtr.Zero; + } + + ~CoreDisassembler() + { + Dispose(false); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DynamicInvokeThunkGenerationPolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DynamicInvokeThunkGenerationPolicy.cs new file mode 100644 index 00000000000000..0a543c837cf755 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DynamicInvokeThunkGenerationPolicy.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Controls the way calling convention conversion is performed in + /// + /// scenarios. + /// + public abstract class DynamicInvokeThunkGenerationPolicy + { + /// + /// Gets a value indicating whether reflection-invokable method '' + /// should get a static calling convention conversion thunk. Static calling convention + /// conversion thunks speed up reflection invoke of the method at the cost of extra code generation. + /// + public abstract bool HasStaticInvokeThunk(MethodDesc targetMethod); + } + + /// + /// A thunk generation policy that generates no static invocation thunks. + /// + public sealed class NoDynamicInvokeThunkGenerationPolicy : DynamicInvokeThunkGenerationPolicy + { + public override bool HasStaticInvokeThunk(MethodDesc targetMethod) => false; + } + + /// + /// A thunk generation policy that uses static invocation thunks whenever possible. + /// + public sealed class DefaultDynamicInvokeThunkGenerationPolicy : DynamicInvokeThunkGenerationPolicy + { + public override bool HasStaticInvokeThunk(MethodDesc targetMethod) + { + // Place an upper limit on how many parameters a method can have to still get a static stub. + // From the past experience, methods taking 8000+ parameters get a stub that can hit various limitations + // in the codegen. On Project N, we were limited to 256 parameters because of MDIL limitations. + // We don't have such limitations here, but it's a nice round number. + // Reflection invoke will still work, but will go through the calling convention converter. + + return targetMethod.Signature.Length <= 256; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyInteropStubManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyInteropStubManager.cs new file mode 100644 index 00000000000000..1261a63c1117c6 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyInteropStubManager.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysis; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using Internal.IL; + +namespace ILCompiler +{ + /// + /// This class is responsible for managing stub methods for interop + /// + public sealed class EmptyInteropStubManager : InteropStubManager + { + public override PInvokeILProvider CreatePInvokeILProvider() + { + return null; + } + + public override void AddDependeciesDueToPInvoke(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + } + + public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + } + + public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EventPseudoDesc.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EventPseudoDesc.cs new file mode 100644 index 00000000000000..0f30adaf0e3ea6 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EventPseudoDesc.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection.Metadata; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + /// + /// An "EventDesc" to describe events. Represents an event within the compiler. + /// This is not a real type system entity. In particular, these are not interned. + /// + public class EventPseudoDesc : TypeSystemEntity + { + private readonly EcmaType _type; + private readonly EventDefinitionHandle _handle; + + private EventDefinition Definition => _type.MetadataReader.GetEventDefinition(_handle); + + public MethodDesc AddMethod + { + get + { + MethodDefinitionHandle adder = Definition.GetAccessors().Adder; + return adder.IsNil ? null : _type.EcmaModule.GetMethod(adder); + } + } + + public MethodDesc RemoveMethod + { + get + { + MethodDefinitionHandle setter = Definition.GetAccessors().Remover; + return setter.IsNil ? null : _type.EcmaModule.GetMethod(setter); + } + } + + public MetadataType OwningType + { + get + { + return _type; + } + } + + public string Name + { + get + { + return _type.MetadataReader.GetString(Definition.Name); + } + } + + public EventPseudoDesc(EcmaType type, EventDefinitionHandle handle) + { + _type = type; + _handle = handle; + } + + public override TypeSystemContext Context => _type.Context; + + #region Do not use these + public override bool Equals(object obj) => throw new NotImplementedException(); + public override int GetHashCode() => throw new NotImplementedException(); + public static bool operator ==(EventPseudoDesc a, EventPseudoDesc b) => throw new NotImplementedException(); + public static bool operator !=(EventPseudoDesc a, EventPseudoDesc b) => throw new NotImplementedException(); + #endregion + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExpectedIsaFeaturesRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExpectedIsaFeaturesRootProvider.cs new file mode 100644 index 00000000000000..90f7b77a4f76fb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExpectedIsaFeaturesRootProvider.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; + +namespace ILCompiler +{ + public class ExpectedIsaFeaturesRootProvider : ICompilationRootProvider + { + private readonly InstructionSetSupport _isaSupport; + + public ExpectedIsaFeaturesRootProvider(InstructionSetSupport isaSupport) + { + _isaSupport = isaSupport; + } + + void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider) + { + if (_isaSupport.Architecture == TargetArchitecture.X64 + || _isaSupport.Architecture == TargetArchitecture.X86 + || _isaSupport.Architecture == TargetArchitecture.ARM64) + { + int isaFlags = HardwareIntrinsicHelpers.GetRuntimeRequiredIsaFlags(_isaSupport); + byte[] bytes = BitConverter.GetBytes(isaFlags); + rootProvider.RootReadOnlyDataBlob(bytes, 4, "ISA support flags", "g_requiredCpuFeatures"); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportedMethodsRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportedMethodsRootProvider.cs new file mode 100644 index 00000000000000..eb2878fb0a6379 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportedMethodsRootProvider.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + /// + /// Computes a set of roots based on managed and unmanaged methods exported from a module. + /// + public class ExportedMethodsRootProvider : ICompilationRootProvider + { + private EcmaModule _module; + + public ExportedMethodsRootProvider(EcmaModule module) + { + _module = module; + } + + public IEnumerable ExportedMethods + { + get + { + foreach (var type in _module.GetAllTypes()) + { + foreach (var method in type.GetMethods()) + { + EcmaMethod ecmaMethod = (EcmaMethod)method; + if (ecmaMethod.IsRuntimeExport || ecmaMethod.IsUnmanagedCallersOnly) + yield return ecmaMethod; + } + } + } + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + foreach (var ecmaMethod in ExportedMethods) + { + if (ecmaMethod.IsRuntimeExport) + { + string runtimeExportName = ecmaMethod.GetRuntimeExportName(); + if (runtimeExportName != null) + rootProvider.AddCompilationRoot((MethodDesc)ecmaMethod, "Runtime export", runtimeExportName); + } + else if (ecmaMethod.IsUnmanagedCallersOnly) + { + string unmanagedCallersOnlyExportName = ecmaMethod.GetUnmanagedCallersOnlyExportName(); + if (unmanagedCallersOnlyExportName != null) + rootProvider.AddCompilationRoot((MethodDesc)ecmaMethod, "Native callable", unmanagedCallersOnlyExportName); + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportsFileWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportsFileWriter.cs new file mode 100644 index 00000000000000..4a0929729b47f6 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportsFileWriter.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + public class ExportsFileWriter + { + private string _exportsFile; + private List _methods; + private TypeSystemContext _context; + + public ExportsFileWriter(TypeSystemContext context, string exportsFile) + { + _exportsFile = exportsFile; + _context = context; + _methods = new List(); + } + + public void AddExportedMethods(IEnumerable methods) + => _methods.AddRange(methods.Where(m => m.Module != _context.SystemModule)); + + public void EmitExportedMethods() + { + FileStream fileStream = new FileStream(_exportsFile, FileMode.Create); + using (StreamWriter streamWriter = new StreamWriter(fileStream)) + { + if (_context.Target.IsWindows) + { + streamWriter.WriteLine("EXPORTS"); + foreach (var method in _methods) + streamWriter.WriteLine($" {method.GetUnmanagedCallersOnlyExportName()}"); + } + else if(_context.Target.IsOSX) + { + foreach (var method in _methods) + streamWriter.WriteLine($"_{method.GetUnmanagedCallersOnlyExportName()}"); + } + else + { + streamWriter.WriteLine("V1.0 {"); + streamWriter.WriteLine(" global: _init; _fini;"); + foreach (var method in _methods) + streamWriter.WriteLine($" {method.GetUnmanagedCallersOnlyExportName()};"); + streamWriter.WriteLine(" local: *;"); + streamWriter.WriteLine("};"); + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExternSymbolMappedField.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExternSymbolMappedField.cs new file mode 100644 index 00000000000000..2ee25f4dc7c3bb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExternSymbolMappedField.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Represents a field whose address is mapped to an external symbol. + /// + public class ExternSymbolMappedField : FieldDesc + { + private readonly TypeDesc _fieldType; + private readonly string _symbolName; + + public ExternSymbolMappedField(TypeDesc fieldType, string symbolName) + { + _fieldType = fieldType; + _symbolName = symbolName; + } + + public override string Name => _symbolName; + + public string SymbolName => _symbolName; + + public override DefType OwningType => _fieldType.Context.SystemModule.GetGlobalModuleType(); + + public override TypeDesc FieldType => _fieldType; + + public override bool IsStatic => true; + + public override bool IsInitOnly => true; + + public override bool IsThreadStatic => false; + + public override bool HasRva => true; + + public override bool IsLiteral => false; + + public override TypeSystemContext Context => _fieldType.Context; + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) + { + return false; + } + +#if !SUPPORT_JIT + protected override int CompareToImpl(FieldDesc other, TypeSystemComparer comparer) + { + return _symbolName.CompareTo(((ExternSymbolMappedField)other)._symbolName); + } + + protected override int ClassCode => 7462882; +#endif + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs new file mode 100644 index 00000000000000..5f06913b97d37b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs @@ -0,0 +1,911 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using System.Xml; + +using Internal.IL; +using Internal.IL.Stubs; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Debug = System.Diagnostics.Debug; +using MethodDebugInformation = Internal.IL.MethodDebugInformation; + +namespace ILCompiler +{ + public class FeatureSwitchManager : ILProvider + { + private readonly FeatureSwitchHashtable _hashtable; + private readonly ILProvider _nestedILProvider; + + public FeatureSwitchManager(ILProvider nestedILProvider, IEnumerable> switchValues) + { + _nestedILProvider = nestedILProvider; + _hashtable = new FeatureSwitchHashtable(new Dictionary(switchValues)); + } + + private BodySubstitution GetSubstitution(MethodDesc method) + { + if (method.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod) + { + AssemblyFeatureInfo info = _hashtable.GetOrCreateValue(ecmaMethod.Module); + if (info.BodySubstitutions != null && info.BodySubstitutions.TryGetValue(ecmaMethod, out BodySubstitution result)) + return result; + } + + return null; + } + + private object GetSubstitution(FieldDesc field) + { + if (field.GetTypicalFieldDefinition() is EcmaField ecmaField) + { + AssemblyFeatureInfo info = _hashtable.GetOrCreateValue(ecmaField.Module); + if (info.BodySubstitutions != null && info.FieldSubstitutions.TryGetValue(ecmaField, out object result)) + return result; + } + + return null; + } + + public bool HasSubstitutedBody(MethodDesc method) + { + return GetSubstitution(method) != null; + } + + public bool HasSubstitutedValue(FieldDesc field) + { + return GetSubstitution(field) != null; + } + + public override MethodIL GetMethodIL(MethodDesc method) + { + BodySubstitution substitution = GetSubstitution(method); + if (substitution != null) + { + return substitution.EmitIL(method); + } + + // BEGIN TEMPORARY WORKAROUND + // + // The following lines should just be: + // return _nestedILProvider.GetMethodIL(method); + // But we want to allow this to be used as a general-purpose IL provider. + // + // Rewriting all IL has compilation throughput hit we don't want. + MethodIL result = _nestedILProvider.GetMethodIL(method); + if (result != null) + { + var resultDef = result.GetMethodILDefinition(); + if (resultDef != result) + { + MethodIL newBodyDef = GetMethodILWithInlinedSubstitutions(resultDef); + + // If we didn't rewrite the body, we can keep the existing result. + if (newBodyDef != resultDef) + result = new InstantiatedMethodIL(method, newBodyDef); + } + else + { + result = GetMethodILWithInlinedSubstitutions(result); + } + } + return result; + // END TEMPORARY WORKAROUND + } + + // Flags that we track for each byte of the IL instruction stream. + [Flags] + enum OpcodeFlags : byte + { + // This offset is an instruction boundary. + InstructionStart = 0x1, + + // Beginning of a basic block in the original IL stream + BasicBlockStart = 0x2, + + // End of a basic block in the original IL stream + EndBasicBlock = 0x4, + + // Beginning of a basic block in the rewritten stream + // (Lets us avoid seeing lots of small basic blocks within eliminated chunks.) + VisibleBasicBlockStart = 0x10, + + // The instruction at this offset is reachable + Mark = 0x80, + } + + public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) + { + // This attempts to find all basic blocks that are unreachable after applying the substitutions. + // + // On a high level, we first find all the basic blocks and instruction boundaries in the IL stream. + // This is tracked in a sidecar `flags` array that has flags for each byte of the IL stream. + // + // Once we have all the basic blocks and instruction boundaries, we do a marking phase to mark + // the reachable blocks. We use substitutions to tell us what's unreachable. We consider conditional + // branches "interesting" and whenever we see one, we seek backwards in the IL instruction stream + // to find the instruction that feeds it. We make sure we don't cross the basic block boundary while + // doing that. If the conditional instruction is fed by known values (either through the substitutions + // or because it's an IL constant), we simulate the result of the comparison and only mark + // the taken branch. We also mark any associated EH regions. + // + // The "seek backwards to find what feeds the comparison" only works for a couple known instructions + // (load constant, call). It can't e.g. skip over arguments to the call. + // + // Last step is a sweep - we replace the tail of all unreachable blocks with "br $-2" + // and nop out the rest. If the basic block is smaller than 2 bytes, we don't touch it. + // We also eliminate any EH records that correspond to the stubbed out basic block. + + Debug.Assert(method.GetMethodILDefinition() == method); + + ILExceptionRegion[] ehRegions = method.GetExceptionRegions(); + byte[] methodBytes = method.GetILBytes(); + OpcodeFlags[] flags = new OpcodeFlags[methodBytes.Length]; + + // Offset 0 is the first basic block + Stack offsetsToVisit = new Stack(); + offsetsToVisit.Push(0); + + // Basic blocks also start around EH regions + foreach (ILExceptionRegion ehRegion in ehRegions) + { + if (ehRegion.Kind == ILExceptionRegionKind.Filter) + offsetsToVisit.Push(ehRegion.FilterOffset); + + offsetsToVisit.Push(ehRegion.HandlerOffset); + } + + // Identify basic blocks and instruction boundaries + while (offsetsToVisit.TryPop(out int offset)) + { + // If this was already visited, we're done + if (flags[offset] != 0) + { + // Also mark as basic block start in case this was a target of a backwards branch. + flags[offset] |= OpcodeFlags.BasicBlockStart; + continue; + } + + flags[offset] |= OpcodeFlags.BasicBlockStart; + + // Read until we reach the end of the basic block + ILReader reader = new ILReader(methodBytes, offset); + while (reader.HasNext) + { + offset = reader.Offset; + flags[offset] |= OpcodeFlags.InstructionStart; + ILOpcode opcode = reader.ReadILOpcode(); + if (opcode >= ILOpcode.br_s && opcode <= ILOpcode.blt_un + || opcode == ILOpcode.leave || opcode == ILOpcode.leave_s) + { + int destination = reader.ReadBranchDestination(opcode); + offsetsToVisit.Push(destination); + + if (opcode != ILOpcode.leave && opcode != ILOpcode.leave_s + && opcode != ILOpcode.br && opcode != ILOpcode.br_s) + { + // Branches not tested for above are conditional and the flow falls through. + offsetsToVisit.Push(reader.Offset); + } + + flags[offset] |= OpcodeFlags.EndBasicBlock; + } + else if (opcode == ILOpcode.ret + || opcode == ILOpcode.endfilter + || opcode == ILOpcode.endfinally + || opcode == ILOpcode.throw_ + || opcode == ILOpcode.rethrow + || opcode == ILOpcode.jmp) + { + // Ends basic block. + flags[offset] |= OpcodeFlags.EndBasicBlock; + + reader.Skip(opcode); + } + else if (opcode == ILOpcode.switch_) + { + uint count = reader.ReadILUInt32(); + int jmpBase = reader.Offset + (int)(4 * count); + for (uint i = 0; i < count; i++) + { + int destination = (int)reader.ReadILUInt32() + jmpBase; + offsetsToVisit.Push(destination); + } + // We fall through to the next basic block. + offsetsToVisit.Push(reader.Offset); + flags[offset] |= OpcodeFlags.EndBasicBlock; + } + else + { + reader.Skip(opcode); + } + + if ((flags[offset] & OpcodeFlags.EndBasicBlock) != 0) + { + if (reader.HasNext) + { + // If the bytes following this basic block are not reachable from anywhere, + // the sweeping step would consider them to be part of the last instruction + // of the current basic block because of how instruction boundaries are identified. + // We wouldn't NOP them out if the current basic block is reachable. + // + // That's a problem for RyuJIT because RyuJIT looks at these bytes for... reasons. + // + // We can just do the same thing as RyuJIT and consider those a basic block. + offsetsToVisit.Push(reader.Offset); + } + break; + } + } + } + + // Mark all reachable basic blocks + // + // We also do another round of basic block marking to mark beginning of visible basic blocks + // after dead branch elimination. This allows us to limit the number of potential small basic blocks + // that are not interesting (because no code jumps to them anymore), but could prevent us from + // finishing the process. Unreachable basic blocks smaller than 2 bytes abort the substitution + // inlining process because we can't neutralize them (turn them into an infinite loop). + offsetsToVisit.Push(0); + while (offsetsToVisit.TryPop(out int offset)) + { + // Mark as a basic block visible after constant propagation. + flags[offset] |= OpcodeFlags.VisibleBasicBlockStart; + + // If this was already marked, we're done. + if ((flags[offset] & OpcodeFlags.Mark) != 0) + continue; + + ILReader reader = new ILReader(methodBytes, offset); + while (reader.HasNext) + { + offset = reader.Offset; + flags[offset] |= OpcodeFlags.Mark; + ILOpcode opcode = reader.ReadILOpcode(); + + // Mark any applicable EH blocks + foreach (ILExceptionRegion ehRegion in ehRegions) + { + int delta = offset - ehRegion.TryOffset; + if (delta >= 0 && delta < ehRegion.TryLength) + { + if (ehRegion.Kind == ILExceptionRegionKind.Filter) + offsetsToVisit.Push(ehRegion.FilterOffset); + + offsetsToVisit.Push(ehRegion.HandlerOffset); + + // RyuJIT is going to look at this basic block even though it's unreachable. + // Consider it visible so that we replace the tail with an endless loop. + int handlerEnd = ehRegion.HandlerOffset + ehRegion.HandlerLength; + if (handlerEnd < flags.Length) + flags[handlerEnd] |= OpcodeFlags.VisibleBasicBlockStart; + } + } + + // All branches are relevant to basic block tracking + if (opcode == ILOpcode.brfalse || opcode == ILOpcode.brfalse_s + || opcode == ILOpcode.brtrue || opcode == ILOpcode.brtrue_s) + { + int destination = reader.ReadBranchDestination(opcode); + if (!TryGetConstantArgument(method, methodBytes, flags, offset, 0, out int constant)) + { + // Can't get the constant - both branches are live. + offsetsToVisit.Push(destination); + offsetsToVisit.Push(reader.Offset); + } + else if ((constant == 0 && (opcode == ILOpcode.brfalse || opcode == ILOpcode.brfalse_s)) + || (constant != 0 && (opcode == ILOpcode.brtrue || opcode == ILOpcode.brtrue_s))) + { + // Only the "branch taken" is live. + // The fallthrough marks the beginning of a visible (but not live) basic block. + offsetsToVisit.Push(destination); + flags[reader.Offset] |= OpcodeFlags.VisibleBasicBlockStart; + } + else + { + // Only fallthrough is live. + // The "brach taken" marks the beginning of a visible (but not live) basic block. + flags[destination] |= OpcodeFlags.VisibleBasicBlockStart; + offsetsToVisit.Push(reader.Offset); + } + } + else if (opcode == ILOpcode.beq || opcode == ILOpcode.beq_s + || opcode == ILOpcode.bne_un || opcode == ILOpcode.bne_un_s) + { + int destination = reader.ReadBranchDestination(opcode); + if (!TryGetConstantArgument(method, methodBytes, flags, offset, 0, out int left) + || !TryGetConstantArgument(method, methodBytes, flags, offset, 1, out int right)) + { + // Can't get the constant - both branches are live. + offsetsToVisit.Push(destination); + offsetsToVisit.Push(reader.Offset); + } + else if ((left == right && (opcode == ILOpcode.beq || opcode == ILOpcode.beq_s) + || (left != right) && (opcode == ILOpcode.bne_un || opcode == ILOpcode.bne_un_s))) + { + // Only the "branch taken" is live. + // The fallthrough marks the beginning of a visible (but not live) basic block. + offsetsToVisit.Push(destination); + flags[reader.Offset] |= OpcodeFlags.VisibleBasicBlockStart; + } + else + { + // Only fallthrough is live. + // The "brach taken" marks the beginning of a visible (but not live) basic block. + flags[destination] |= OpcodeFlags.VisibleBasicBlockStart; + offsetsToVisit.Push(reader.Offset); + } + } + else if (opcode >= ILOpcode.br_s && opcode <= ILOpcode.blt_un + || opcode == ILOpcode.leave || opcode == ILOpcode.leave_s) + { + int destination = reader.ReadBranchDestination(opcode); + offsetsToVisit.Push(destination); + if (opcode != ILOpcode.leave && opcode != ILOpcode.leave_s + && opcode != ILOpcode.br && opcode != ILOpcode.br_s) + { + // Branches not tested for above are conditional and the flow falls through. + offsetsToVisit.Push(reader.Offset); + } + else + { + // RyuJIT is going to look at this basic block even though it's unreachable. + // Consider it visible so that we replace the tail with an endless loop. + if (reader.HasNext) + flags[reader.Offset] |= OpcodeFlags.VisibleBasicBlockStart; + } + } + else if (opcode == ILOpcode.switch_) + { + uint count = reader.ReadILUInt32(); + int jmpBase = reader.Offset + (int)(4 * count); + for (uint i = 0; i < count; i++) + { + int destination = (int)reader.ReadILUInt32() + jmpBase; + offsetsToVisit.Push(destination); + } + offsetsToVisit.Push(reader.Offset); + } + else if (opcode == ILOpcode.ret + || opcode == ILOpcode.endfilter + || opcode == ILOpcode.endfinally + || opcode == ILOpcode.throw_ + || opcode == ILOpcode.rethrow + || opcode == ILOpcode.jmp) + { + reader.Skip(opcode); + + // RyuJIT is going to look at this basic block even though it's unreachable. + // Consider it visible so that we replace the tail with an endless loop. + if (reader.HasNext) + flags[reader.Offset] |= OpcodeFlags.VisibleBasicBlockStart; + } + else + { + reader.Skip(opcode); + } + + if ((flags[offset] & OpcodeFlags.EndBasicBlock) != 0) + break; + } + } + + // Now sweep unreachable basic blocks by replacing them with nops + bool hasUnmarkedIntructions = false; + foreach (var flag in flags) + { + if ((flag & OpcodeFlags.InstructionStart) != 0 && + (flag & OpcodeFlags.Mark) == 0) + { + hasUnmarkedIntructions = true; + } + } + + if (!hasUnmarkedIntructions) + return method; + + byte[] newBody = (byte[])methodBytes.Clone(); + int position = 0; + while (position < newBody.Length) + { + Debug.Assert((flags[position] & OpcodeFlags.InstructionStart) != 0); + Debug.Assert((flags[position] & OpcodeFlags.VisibleBasicBlockStart) != 0); + + bool erase = (flags[position] & OpcodeFlags.Mark) == 0; + + int basicBlockStart = position; + do + { + if (erase) + newBody[position] = (byte)ILOpCode.Nop; + position++; + } while (position < newBody.Length && (flags[position] & OpcodeFlags.VisibleBasicBlockStart) == 0); + + // If we had to nop out this basic block, we need to neutralize it by appending + // an infinite loop ("br $-2"). + // We append instead of prepend because RyuJIT's importer has trouble with junk unreachable bytes. + if (erase) + { + if (position - basicBlockStart < 2) + { + // We cannot neutralize the basic block, so better leave the method alone. + // The control would fall through to the next basic block. + return method; + } + + newBody[position - 2] = (byte)ILOpCode.Br_s; + newBody[position - 1] = unchecked((byte)-2); + } + } + + // EH regions with unmarked handlers belong to unmarked basic blocks + // Need to eliminate them because they're not usable. + ArrayBuilder newEHRegions = new ArrayBuilder(); + foreach (ILExceptionRegion ehRegion in ehRegions) + { + if ((flags[ehRegion.HandlerOffset] & OpcodeFlags.Mark) != 0) + { + newEHRegions.Add(ehRegion); + } + } + + // Existing debug information might not match new instruction boundaries (plus there's little point + // in generating debug information for NOPs) - generate new debug information by filtering + // out the sequence points associated with nopped out instructions. + MethodDebugInformation debugInfo = method.GetDebugInfo(); + IEnumerable oldSequencePoints = debugInfo?.GetSequencePoints(); + if (oldSequencePoints != null) + { + ArrayBuilder sequencePoints = new ArrayBuilder(); + foreach (var sequencePoint in oldSequencePoints) + { + if (sequencePoint.Offset < flags.Length && (flags[sequencePoint.Offset] & OpcodeFlags.Mark) != 0) + { + sequencePoints.Add(sequencePoint); + } + } + + debugInfo = new SubstitutedDebugInformation(debugInfo, sequencePoints.ToArray()); + } + + return new SubstitutedMethodIL(method, newBody, newEHRegions.ToArray(), debugInfo); + } + + private bool TryGetConstantArgument(MethodIL methodIL, byte[] body, OpcodeFlags[] flags, int offset, int argIndex, out int constant) + { + if ((flags[offset] & OpcodeFlags.BasicBlockStart) != 0) + { + constant = 0; + return false; + } + + for (int currentOffset = offset - 1; currentOffset >= 0; currentOffset--) + { + if ((flags[currentOffset] & OpcodeFlags.InstructionStart) == 0) + continue; + + ILReader reader = new ILReader(body, currentOffset); + ILOpcode opcode = reader.ReadILOpcode(); + if (opcode == ILOpcode.call || opcode == ILOpcode.callvirt) + { + MethodDesc method = (MethodDesc)methodIL.GetObject(reader.ReadILToken()); + if (argIndex == 0) + { + BodySubstitution substitution = GetSubstitution(method); + if (substitution != null && substitution.Value is int + && (opcode != ILOpcode.callvirt || !method.IsVirtual)) + { + constant = (int)substitution.Value; + return true; + } + else + { + constant = 0; + return false; + } + } + + argIndex--; + + if (method.Signature.Length > 0 || !method.Signature.IsStatic) + { + // We don't know how to skip over the parameters + break; + } + } + else if (opcode == ILOpcode.ldsfld) + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + if (argIndex == 0) + { + object substitution = GetSubstitution(field); + if (substitution is int) + { + constant = (int)substitution; + return true; + } + else + { + constant = 0; + return false; + } + } + + argIndex--; + } + else if (opcode >= ILOpcode.ldc_i4_0 && opcode <= ILOpcode.ldc_i4_8) + { + if (argIndex == 0) + { + constant = opcode - ILOpcode.ldc_i4_0; + return true; + } + + argIndex--; + } + else if (opcode == ILOpcode.ldc_i4) + { + if (argIndex == 0) + { + constant = (int)reader.ReadILUInt32(); + return true; + } + + argIndex--; + } + else if (opcode == ILOpcode.ldc_i4_s) + { + if (argIndex == 0) + { + constant = (int)(sbyte)reader.ReadILByte(); + return true; + } + + argIndex--; + } + else if ((opcode == ILOpcode.ldloc || opcode == ILOpcode.ldloc_s || + (opcode >= ILOpcode.ldloc_0 && opcode <= ILOpcode.ldloc_3)) && + ((flags[currentOffset] & OpcodeFlags.BasicBlockStart) == 0)) + { + // Paired stloc/ldloc that the C# compiler generates in debug code? + int locIndex = opcode switch + { + ILOpcode.ldloc => reader.ReadILUInt16(), + ILOpcode.ldloc_s => reader.ReadILByte(), + _ => opcode - ILOpcode.ldloc_0, + }; + + for (int potentialStlocOffset = currentOffset - 1; potentialStlocOffset >= 0; potentialStlocOffset--) + { + if ((flags[potentialStlocOffset] & OpcodeFlags.InstructionStart) == 0) + continue; + + ILReader nestedReader = new ILReader(body, potentialStlocOffset); + ILOpcode otherOpcode = nestedReader.ReadILOpcode(); + if ((otherOpcode == ILOpcode.stloc || otherOpcode == ILOpcode.stloc_s || + (otherOpcode >= ILOpcode.stloc_0 && otherOpcode <= ILOpcode.stloc_3)) + && otherOpcode switch + { + ILOpcode.stloc => nestedReader.ReadILUInt16(), + ILOpcode.stloc_s => nestedReader.ReadILByte(), + _ => otherOpcode - ILOpcode.stloc_0, + } == locIndex) + { + // Move all the way to the stloc and resume looking for previous instruction. + currentOffset = potentialStlocOffset; + break; + } + else + { + constant = 0; + return false; + } + } + } + else if (opcode == ILOpcode.ceq) + { + if (argIndex == 0) + { + if (!TryGetConstantArgument(methodIL, body, flags, currentOffset, 0, out int left) + || !TryGetConstantArgument(methodIL, body, flags, currentOffset, 1, out int right)) + { + constant = 0; + return false; + } + + constant = left == right ? 1 : 0; + return true; + } + + // If we knew where the arguments to this end, we could resume looking from there. + // Punting for now. + Debug.Assert(argIndex != 0); + constant = 0; + return false; + } + else + { + constant = 0; + return false; + } + + if ((flags[currentOffset] & OpcodeFlags.BasicBlockStart) != 0) + break; + } + + constant = 0; + return false; + } + + private class SubstitutedMethodIL : MethodIL + { + private readonly byte[] _body; + private readonly ILExceptionRegion[] _ehRegions; + private readonly MethodIL _wrappedMethodIL; + private readonly MethodDebugInformation _debugInfo; + + public SubstitutedMethodIL(MethodIL wrapped, byte[] body, ILExceptionRegion[] ehRegions, MethodDebugInformation debugInfo) + { + _wrappedMethodIL = wrapped; + _body = body; + _ehRegions = ehRegions; + _debugInfo = debugInfo; + } + + public override MethodDesc OwningMethod => _wrappedMethodIL.OwningMethod; + public override int MaxStack => _wrappedMethodIL.MaxStack; + public override bool IsInitLocals => _wrappedMethodIL.IsInitLocals; + public override ILExceptionRegion[] GetExceptionRegions() => _ehRegions; + public override byte[] GetILBytes() => _body; + public override LocalVariableDefinition[] GetLocals() => _wrappedMethodIL.GetLocals(); + public override object GetObject(int token, NotFoundBehavior notFoundBehavior) => _wrappedMethodIL.GetObject(token, notFoundBehavior); + public override MethodDebugInformation GetDebugInfo() => _debugInfo; + } + + private class SubstitutedDebugInformation : MethodDebugInformation + { + private readonly MethodDebugInformation _originalDebugInformation; + private readonly ILSequencePoint[] _sequencePoints; + + public SubstitutedDebugInformation(MethodDebugInformation originalDebugInformation, ILSequencePoint[] newSequencePoints) + { + _originalDebugInformation = originalDebugInformation; + _sequencePoints = newSequencePoints; + } + + public override IEnumerable GetLocalVariables() => _originalDebugInformation.GetLocalVariables(); + public override IEnumerable GetParameterNames() => _originalDebugInformation.GetParameterNames(); + public override IEnumerable GetSequencePoints() => _sequencePoints; + } + + private class FeatureSwitchHashtable : LockFreeReaderHashtable + { + private readonly Dictionary _switchValues; + + public FeatureSwitchHashtable(Dictionary switchValues) + { + _switchValues = switchValues; + } + + protected override bool CompareKeyToValue(EcmaModule key, AssemblyFeatureInfo value) => key == value.Module; + protected override bool CompareValueToValue(AssemblyFeatureInfo value1, AssemblyFeatureInfo value2) => value1.Module == value2.Module; + protected override int GetKeyHashCode(EcmaModule key) => key.GetHashCode(); + protected override int GetValueHashCode(AssemblyFeatureInfo value) => value.Module.GetHashCode(); + + protected override AssemblyFeatureInfo CreateValueFromKey(EcmaModule key) + { + return new AssemblyFeatureInfo(key, _switchValues); + } + } + + private class AssemblyFeatureInfo + { + public EcmaModule Module { get; } + + public Dictionary BodySubstitutions { get; } + public Dictionary FieldSubstitutions { get; } + + public AssemblyFeatureInfo(EcmaModule module, IReadOnlyDictionary featureSwitchValues) + { + Module = module; + + PEMemoryBlock resourceDirectory = module.PEReader.GetSectionData(module.PEReader.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress); + + foreach (var resourceHandle in module.MetadataReader.ManifestResources) + { + ManifestResource resource = module.MetadataReader.GetManifestResource(resourceHandle); + + // Don't try to process linked resources or resources in other assemblies + if (!resource.Implementation.IsNil) + { + continue; + } + + string resourceName = module.MetadataReader.GetString(resource.Name); + if (resourceName == "ILLink.Substitutions.xml") + { + BlobReader reader = resourceDirectory.GetReader((int)resource.Offset, resourceDirectory.Length - (int)resource.Offset); + int length = (int)reader.ReadUInt32(); + + UnmanagedMemoryStream ms; + unsafe + { + ms = new UnmanagedMemoryStream(reader.CurrentPointer, length); + } + + (BodySubstitutions, FieldSubstitutions) = SubstitutionsReader.GetSubstitutions(module.Context, XmlReader.Create(ms), module, featureSwitchValues); + } + } + } + } + + private class BodySubstitution + { + private object _value; + + private readonly static object Throw = new object(); + + public readonly static BodySubstitution ThrowingBody = new BodySubstitution(Throw); + public readonly static BodySubstitution EmptyBody = new BodySubstitution(null); + + public object Value + { + get + { + Debug.Assert(_value != Throw); + return _value; + } + } + + private BodySubstitution(object value) => _value = value; + + public static BodySubstitution Create(object value) => new BodySubstitution(value); + public MethodIL EmitIL(MethodDesc method) + { + ILEmitter emit = new ILEmitter(); + ILCodeStream codestream = emit.NewCodeStream(); + + if (_value == Throw) + { + codestream.EmitCallThrowHelper(emit, method.Context.GetHelperEntryPoint("ThrowHelpers", "ThrowFeatureBodyRemoved")); + } + else if (_value == null) + { + Debug.Assert(method.Signature.ReturnType.IsVoid); + codestream.Emit(ILOpcode.ret); + } + else + { + Debug.Assert(_value is int); + codestream.EmitLdc((int)_value); + codestream.Emit(ILOpcode.ret); + } + + return emit.Link(method); + } + } + + private class SubstitutionsReader : ProcessLinkerXmlBase + { + private readonly Dictionary _methodSubstitutions; + private readonly Dictionary _fieldSubstitutions; + + private SubstitutionsReader(TypeSystemContext context, XmlReader reader, ModuleDesc module, IReadOnlyDictionary featureSwitchValues) + : base(context, reader, module, featureSwitchValues) + { + _methodSubstitutions = new Dictionary(); + _fieldSubstitutions = new Dictionary(); + } + + protected override void ProcessMethod(MethodDesc method) + { + string action = GetAttribute("body"); + if (!String.IsNullOrEmpty(action)) + { + switch (action) + { + case "remove": + _methodSubstitutions.Add(method, BodySubstitution.ThrowingBody); + break; + case "stub": + BodySubstitution stubBody; + if (method.Signature.ReturnType.IsVoid) + stubBody = BodySubstitution.EmptyBody; + else + stubBody = BodySubstitution.Create(TryCreateSubstitution(method.Signature.ReturnType, GetAttribute("value"))); + + if (stubBody != null) + { + _methodSubstitutions[method] = stubBody; + } + else + { + // Context.LogWarning ($"Invalid value for '{method.GetDisplayName ()}' stub", 2010, _xmlDocumentLocation); + } + + break; + default: + //Context.LogWarning($"Unknown body modification '{action}' for '{method.GetDisplayName()}'", 2011, _xmlDocumentLocation); + break; + } + } + } + + protected override void ProcessField(FieldDesc field) + { + if (!field.IsStatic || field.IsLiteral) + { + // Context.LogWarning ($"Substituted field '{field.GetDisplayName ()}' needs to be static field.", 2013, _xmlDocumentLocation); + return; + } + + string value = GetAttribute("value"); + if (string.IsNullOrEmpty(value)) + { + //Context.LogWarning($"Missing 'value' attribute for field '{field.GetDisplayName()}'.", 2014, _xmlDocumentLocation); + return; + } + + object substitution = TryCreateSubstitution(field.FieldType, value); + if (substitution == null) + { + //Context.LogWarning($"Invalid value '{value}' for '{field.GetDisplayName()}'.", 2015, _xmlDocumentLocation); + return; + } + + if (String.Equals(GetAttribute("initialize"), "true", StringComparison.InvariantCultureIgnoreCase)) + { + // We would need to also mess with the cctor of the type to set the field to this value: + // + // * Linker will remove all stsfld instructions referencing this field from the cctor + // * It will place an explicit stsfld in front of the last "ret" instruction in the cctor + // + // This approach... has issues. + throw new NotSupportedException(); + } + + _fieldSubstitutions[field] = substitution; + } + + private object TryCreateSubstitution(TypeDesc type, string value) + { + switch (type.UnderlyingType.Category) + { + case TypeFlags.Int32: + if (string.IsNullOrEmpty(value)) + return 0; + else if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int iresult)) + return iresult; + break; + + case TypeFlags.Boolean: + if (String.IsNullOrEmpty(value)) + return 0; + else if (bool.TryParse(value, out bool bvalue)) + return bvalue ? 1 : 0; + else + goto case TypeFlags.Int32; + + default: + throw new NotSupportedException(type.ToString()); + } + + return null; + } + + public static (Dictionary, Dictionary) GetSubstitutions(TypeSystemContext context, XmlReader reader, ModuleDesc module, IReadOnlyDictionary featureSwitchValues) + { + var rdr = new SubstitutionsReader(context, reader, module, featureSwitchValues); + rdr.ProcessXml(); + return (rdr._methodSubstitutions, rdr._fieldSubstitutions); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs new file mode 100644 index 00000000000000..65aa1384d39e2a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs @@ -0,0 +1,251 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Collections.Generic; +using System.Text; + +using Internal.IL; +using Internal.IL.Stubs; +using Internal.TypeSystem; +using Internal.Metadata.NativeFormat.Writer; + +using ILCompiler.Metadata; +using ILCompiler.DependencyAnalysis; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; + +namespace ILCompiler +{ + /// + /// Base class for metadata managers that generate metadata blobs. + /// + public abstract class GeneratingMetadataManager : MetadataManager + { + protected readonly string _metadataLogFile; + protected readonly StackTraceEmissionPolicy _stackTraceEmissionPolicy; + private readonly ModuleDesc _generatedAssembly; + + public GeneratingMetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, + ManifestResourceBlockingPolicy resourceBlockingPolicy, string logFile, StackTraceEmissionPolicy stackTracePolicy, + DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy) + : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, invokeThunkGenerationPolicy) + { + _metadataLogFile = logFile; + _stackTraceEmissionPolicy = stackTracePolicy; + _generatedAssembly = typeSystemContext.GeneratedAssembly; + } + + public sealed override bool WillUseMetadataTokenToReferenceMethod(MethodDesc method) + { + return (GetMetadataCategory(method) & MetadataCategory.Description) != 0; + } + + public sealed override bool WillUseMetadataTokenToReferenceField(FieldDesc field) + { + return (GetMetadataCategory(field) & MetadataCategory.Description) != 0; + } + + protected void ComputeMetadata( + TPolicy policy, + NodeFactory factory, + out byte[] metadataBlob, + out List> typeMappings, + out List> methodMappings, + out List> fieldMappings, + out List> stackTraceMapping) where TPolicy : struct, IMetadataPolicy + { + var transformed = MetadataTransform.Run(policy, GetCompilationModulesWithMetadata()); + MetadataTransform transform = transformed.Transform; + + // TODO: DeveloperExperienceMode: Use transformed.Transform.HandleType() to generate + // TypeReference records for _typeDefinitionsGenerated that don't have metadata. + // (To be used in MissingMetadataException messages) + + // Generate metadata blob + var writer = new MetadataWriter(); + writer.ScopeDefinitions.AddRange(transformed.Scopes); + + // Generate entries in the blob for methods that will be necessary for stack trace purposes. + var stackTraceRecords = new List>(); + foreach (var methodBody in GetCompiledMethodBodies()) + { + MethodDesc method = methodBody.Method; + + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + + // Methods that will end up in the reflection invoke table should not have an entry in stack trace table + // We'll try looking them up in reflection data at runtime. + if (transformed.GetTransformedMethodDefinition(typicalMethod) != null && + ShouldMethodBeInInvokeMap(method) && + (GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) != 0) + continue; + + if (!_stackTraceEmissionPolicy.ShouldIncludeMethod(method)) + continue; + + MetadataRecord record = CreateStackTraceRecord(transform, method); + + stackTraceRecords.Add(new KeyValuePair( + method, + record)); + + writer.AdditionalRootRecords.Add(record); + } + + var ms = new MemoryStream(); + + // .NET metadata is UTF-16 and UTF-16 contains code points that don't translate to UTF-8. + var noThrowUtf8Encoding = new UTF8Encoding(false, false); + + using (var logWriter = _metadataLogFile != null ? new StreamWriter(File.Open(_metadataLogFile, FileMode.Create, FileAccess.Write, FileShare.Read), noThrowUtf8Encoding) : null) + { + writer.LogWriter = logWriter; + writer.Write(ms); + } + + metadataBlob = ms.ToArray(); + + const int MaxAllowedMetadataOffset = 0xFFFFFF; + if (metadataBlob.Length > MaxAllowedMetadataOffset) + { + // Offset portion of metadata handles is limited to 16 MB. + throw new InvalidOperationException($"Metadata blob exceeded the addressing range (allowed: {MaxAllowedMetadataOffset}, actual: {metadataBlob.Length})"); + } + + typeMappings = new List>(); + methodMappings = new List>(); + fieldMappings = new List>(); + stackTraceMapping = new List>(); + + // Generate type definition mappings + foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) + { + MetadataType definition = type.IsTypeDefinition ? type as MetadataType : null; + if (definition == null) + continue; + + MetadataRecord record = transformed.GetTransformedTypeDefinition(definition); + + // Reflection requires that we maintain type identity. Even if we only generated a TypeReference record, + // if there is an MethodTable for it, we also need a mapping table entry for it. + if (record == null) + record = transformed.GetTransformedTypeReference(definition); + + if (record != null) + typeMappings.Add(new MetadataMapping(definition, writer.GetRecordHandle(record))); + } + + HashSet canonicalGenericMethods = new HashSet(); + foreach (var method in GetReflectableMethods()) + { + if (method.IsGenericMethodDefinition || method.OwningType.IsGenericDefinition) + { + // Generic definitions don't have runtime artifacts we would need to map to. + continue; + } + + if ((method.HasInstantiation && method.IsCanonicalMethod(CanonicalFormKind.Specific)) + || (!method.HasInstantiation && method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method)) + { + // Methods that are not in their canonical form are not interesting with the exception + // of generic methods: their dictionaries convey their identity. + continue; + } + + if (IsReflectionBlocked(method.Instantiation) || IsReflectionBlocked(method.OwningType.Instantiation)) + continue; + + if ((GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) == 0) + continue; + + // If we already added a canonically equivalent generic method, skip this one. + if (method.HasInstantiation && !canonicalGenericMethods.Add(method.GetCanonMethodTarget(CanonicalFormKind.Specific))) + continue; + + MetadataRecord record = transformed.GetTransformedMethodDefinition(method.GetTypicalMethodDefinition()); + + if (record != null) + methodMappings.Add(new MetadataMapping(method, writer.GetRecordHandle(record))); + } + + HashSet canonicalFields = new HashSet(); + foreach (var field in GetFieldsWithRuntimeMapping()) + { + FieldDesc fieldToAdd = field; + if (!field.IsStatic) + { + TypeDesc canonOwningType = field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); + if (canonOwningType != field.OwningType) + { + FieldDesc canonField = _typeSystemContext.GetFieldForInstantiatedType(field.GetTypicalFieldDefinition(), (InstantiatedType)canonOwningType); + + // If we already added a canonically equivalent field, skip this one. + if (!canonicalFields.Add(canonField)) + continue; + + fieldToAdd = canonField; + } + } + + Field record = transformed.GetTransformedFieldDefinition(fieldToAdd.GetTypicalFieldDefinition()); + if (record != null) + fieldMappings.Add(new MetadataMapping(fieldToAdd, writer.GetRecordHandle(record))); + } + + // Generate stack trace metadata mapping + foreach (var stackTraceRecord in stackTraceRecords) + { + stackTraceMapping.Add(new MetadataMapping(stackTraceRecord.Key, writer.GetRecordHandle(stackTraceRecord.Value))); + } + } + + /// + /// Gets a list of fields that got "compiled" and are eligible for a runtime mapping. + /// + /// + protected abstract IEnumerable GetFieldsWithRuntimeMapping(); + + /// + /// Gets a stub that can be used to reflection-invoke a method with a given signature. + /// + public sealed override MethodDesc GetCanonicalReflectionInvokeStub(MethodDesc method) + { + // Get a generic method that can be used to invoke method with this shape. + var lookupSig = new DynamicInvokeMethodSignature(method.Signature); + MethodDesc thunk = _typeSystemContext.GetDynamicInvokeThunk(lookupSig); + + return InstantiateCanonicalDynamicInvokeMethodForMethod(thunk, method); + } + + protected override void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + if (!ConstructedEETypeNode.CreationAllowed(type)) + { + // Both EETypeNode and ConstructedEETypeNode call into this logic. EETypeNode will only call for unconstructable + // EETypes. We don't have templates for those. + return; + } + + DefType closestDefType = type.GetClosestDefType(); + + // TODO-SIZE: this is overly generous in the templates we create + if (_blockingPolicy is FullyBlockedMetadataBlockingPolicy) + return; + + if (closestDefType.HasInstantiation) + { + TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific); + TypeDesc canonClosestDefType = closestDefType.ConvertToCanonForm(CanonicalFormKind.Specific); + + // Add a dependency on the template for this type, if the canonical type should be generated into this binary. + // If the type is an array type, the check should be on its underlying Array type. This is because a copy of + // an array type gets placed into each module but the Array type only exists in the defining module and only + // one template is needed for the Array type by the dynamic type loader. + if (canonType.IsCanonicalSubtype(CanonicalFormKind.Any) && !factory.NecessaryTypeSymbol(canonClosestDefType).RepresentsIndirectionCell) + dependencies.Add(factory.NativeLayout.TemplateTypeLayout(canonType), "Template Type Layout"); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GenericDictionaryLookup.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GenericDictionaryLookup.cs new file mode 100644 index 00000000000000..49005719c94cdb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GenericDictionaryLookup.cs @@ -0,0 +1,150 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysis; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// Structure that specifies how a generic dictionary lookup should be performed. + /// + public struct GenericDictionaryLookup + { + private const short UseHelperOffset = -1; + + private readonly object _helperObject; + + private readonly short _offset1; + private readonly short _offset2; + private readonly bool _indirectLastOffset; + + /// + /// Gets the information about the source of the generic context for shared code. + /// + public readonly GenericContextSource ContextSource; + + /// + /// Gets the target object of the lookup. Only valid when is true. + /// This is typically a whose + /// is true, a on a runtime determined type, a , or + /// a . + /// + public object HelperObject + { + get + { + Debug.Assert(_offset1 == UseHelperOffset); + return _helperObject; + } + } + + /// + /// Gets the ID of the helper to use if is true. + /// + public ReadyToRunHelperId HelperId + { + get + { + Debug.Assert(_offset1 == UseHelperOffset); + return (ReadyToRunHelperId)_offset2; + } + } + + /// + /// Gets a value indicating whether the lookup needs to be performed by calling a helper method. + /// + public bool UseHelper + { + get + { + return _offset1 == UseHelperOffset; + } + } + + /// + /// Gets the number of indirections to follow. Only valid if is false. + /// + public int NumberOfIndirections + { + get + { + Debug.Assert(!UseHelper); + return ContextSource == GenericContextSource.MethodParameter ? 1 : 2; + } + } + + public int this[int index] + { + get + { + Debug.Assert(!UseHelper); + Debug.Assert(index < NumberOfIndirections); + switch (index) + { + case 0: + return _offset1; + case 1: + return _offset2; + } + + // Should be unreachable. + throw new NotSupportedException(); + } + } + + public bool IndirectLastOffset + { + get + { + Debug.Assert(!UseHelper); + return _indirectLastOffset; + } + } + + private GenericDictionaryLookup(GenericContextSource contextSource, int offset1, int offset2, object helperObject, bool indirectLastOffset) + { + ContextSource = contextSource; + _offset1 = checked((short)offset1); + _offset2 = checked((short)offset2); + _helperObject = helperObject; + _indirectLastOffset = indirectLastOffset; + } + + public static GenericDictionaryLookup CreateFixedLookup(GenericContextSource contextSource, int offset1, int offset2 = UseHelperOffset, bool indirectLastOffset = false) + { + Debug.Assert(offset1 != UseHelperOffset); + return new GenericDictionaryLookup(contextSource, offset1, offset2, null, indirectLastOffset); + } + + public static GenericDictionaryLookup CreateHelperLookup(GenericContextSource contextSource, ReadyToRunHelperId helperId, object helperObject) + { + return new GenericDictionaryLookup(contextSource, UseHelperOffset, checked((short)helperId), helperObject, indirectLastOffset: false); + } + } + + /// + /// Specifies to source of the generic context. + /// + public enum GenericContextSource + { + /// + /// Generic context is specified by a hidden parameter that has a method dictionary. + /// + MethodParameter, + + /// + /// Generic context is specified by a hidden parameter that has a type. + /// + TypeParameter, + + /// + /// Generic context is specified implicitly by the `this` object. + /// + ThisObject, + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GenericRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GenericRootProvider.cs new file mode 100644 index 00000000000000..22a2029b3a189c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GenericRootProvider.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem.Ecma; +using Internal.TypeSystem; +using System; + +namespace ILCompiler +{ + public class GenericRootProvider : ICompilationRootProvider + { + private readonly TState _state; + + private readonly Action _addRoots; + + public GenericRootProvider(TState state, Action addRootsMethod) => + (_state, _addRoots) = (state, addRootsMethod); + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) => _addRoots(_state, rootProvider); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/HardwareIntrinsicHelpers.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/HardwareIntrinsicHelpers.Aot.cs new file mode 100644 index 00000000000000..7c12cf6c8a3da5 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/HardwareIntrinsicHelpers.Aot.cs @@ -0,0 +1,238 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; +using Internal.IL; +using Internal.IL.Stubs; +using Internal.JitInterface; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + public static partial class HardwareIntrinsicHelpers + { + public static bool IsIsSupportedMethod(MethodDesc method) + { + return method.Name == "get_IsSupported"; + } + + /// + /// Generates IL for the IsSupported property that reads this information from a field initialized by the runtime + /// at startup. Only works for intrinsics that the code generator can generate detection code for. + /// + public static MethodIL EmitIsSupportedIL(MethodDesc method, FieldDesc isSupportedField) + { + Debug.Assert(IsIsSupportedMethod(method)); + Debug.Assert(isSupportedField.IsStatic && isSupportedField.FieldType.IsWellKnownType(WellKnownType.Int32)); + + string id = InstructionSetSupport.GetHardwareIntrinsicId(method.Context.Target.Architecture, method.OwningType); + + int flag = 0; + + switch (method.Context.Target.Architecture) + { + case TargetArchitecture.X86: + case TargetArchitecture.X64: + flag = XArchIntrinsicConstants.FromHardwareIntrinsicId(id); + break; + + case TargetArchitecture.ARM64: + flag = Arm64IntrinsicConstants.FromHardwareIntrinsicId(id); + break; + + default: + Debug.Fail("Unsupported Architecture"); + break; + } + + var emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + + codeStream.Emit(ILOpcode.ldsfld, emit.NewToken(isSupportedField)); + codeStream.EmitLdc(flag); + codeStream.Emit(ILOpcode.and); + codeStream.EmitLdc(0); + codeStream.Emit(ILOpcode.cgt_un); + codeStream.Emit(ILOpcode.ret); + + return emit.Link(method); + } + + public static int GetRuntimeRequiredIsaFlags(InstructionSetSupport instructionSetSupport) + { + switch (instructionSetSupport.Architecture) + { + case TargetArchitecture.X86: + case TargetArchitecture.X64: + return XArchIntrinsicConstants.FromInstructionSetFlags(instructionSetSupport.SupportedFlags); + + case TargetArchitecture.ARM64: + return Arm64IntrinsicConstants.FromInstructionSetFlags(instructionSetSupport.SupportedFlags); + + default: + Debug.Fail("Unsupported Architecture"); + return 0; + } + } + + // Keep these enumerations in sync with startup.cpp in the native runtime. + private static class XArchIntrinsicConstants + { + // SSE and SSE2 are baseline ISAs - they're always available + public const int Aes = 0x0001; + public const int Pclmulqdq = 0x0002; + public const int Sse3 = 0x0004; + public const int Ssse3 = 0x0008; + public const int Sse41 = 0x0010; + public const int Sse42 = 0x0020; + public const int Popcnt = 0x0040; + public const int Avx = 0x0080; + public const int Fma = 0x0100; + public const int Avx2 = 0x0200; + public const int Bmi1 = 0x0400; + public const int Bmi2 = 0x0800; + public const int Lzcnt = 0x1000; + + public static int FromHardwareIntrinsicId(string id) + { + return id switch + { + "Aes" => Aes, + "Pclmulqdq" => Pclmulqdq, + "Sse3" => Sse3, + "Ssse3" => Ssse3, + "Sse41" => Sse41, + "Sse42" => Sse42, + "Popcnt" => Popcnt, + "Avx" => Avx, + "Fma" => Fma, + "Avx2" => Avx2, + "Bmi1" => Bmi1, + "Bmi2" => Bmi2, + "Lzcnt" => Lzcnt, + _ => throw new NotSupportedException(), + }; + } + + public static int FromInstructionSetFlags(InstructionSetFlags instructionSets) + { + int result = 0; + + Debug.Assert(InstructionSet.X64_AES == InstructionSet.X86_AES); + Debug.Assert(InstructionSet.X64_SSE41 == InstructionSet.X86_SSE41); + Debug.Assert(InstructionSet.X64_LZCNT == InstructionSet.X86_LZCNT); + + foreach (InstructionSet instructionSet in instructionSets) + { + result |= instructionSet switch + { + InstructionSet.X64_AES => Aes, + InstructionSet.X64_AES_X64 => Aes, + InstructionSet.X64_PCLMULQDQ => Pclmulqdq, + InstructionSet.X64_PCLMULQDQ_X64 => Pclmulqdq, + InstructionSet.X64_SSE3 => Sse3, + InstructionSet.X64_SSE3_X64 => Sse3, + InstructionSet.X64_SSSE3 => Ssse3, + InstructionSet.X64_SSSE3_X64 => Ssse3, + InstructionSet.X64_SSE41 => Sse41, + InstructionSet.X64_SSE41_X64 => Sse41, + InstructionSet.X64_SSE42 => Sse42, + InstructionSet.X64_SSE42_X64 => Sse42, + InstructionSet.X64_POPCNT => Popcnt, + InstructionSet.X64_POPCNT_X64 => Popcnt, + InstructionSet.X64_AVX => Avx, + InstructionSet.X64_AVX_X64 => Avx, + InstructionSet.X64_FMA => Fma, + InstructionSet.X64_FMA_X64 => Fma, + InstructionSet.X64_AVX2 => Avx2, + InstructionSet.X64_AVX2_X64 => Avx2, + InstructionSet.X64_BMI1 => Bmi1, + InstructionSet.X64_BMI1_X64 => Bmi1, + InstructionSet.X64_BMI2 => Bmi2, + InstructionSet.X64_BMI2_X64 => Bmi2, + InstructionSet.X64_LZCNT => Lzcnt, + InstructionSet.X64_LZCNT_X64 => Popcnt, + + // SSE and SSE2 are baseline ISAs - they're always available + InstructionSet.X64_SSE => 0, + InstructionSet.X64_SSE_X64 => 0, + InstructionSet.X64_SSE2 => 0, + InstructionSet.X64_SSE2_X64 => 0, + InstructionSet.X64_X86Base => 0, + InstructionSet.X64_X86Base_X64 => 0, + + _ => throw new NotSupportedException(instructionSet.ToString()) + }; + } + + return result; + } + } + + private static class Arm64IntrinsicConstants + { + public const int ArmBase = 0x0001; + public const int ArmBase_Arm64 = 0x0002; + public const int AdvSimd = 0x0004; + public const int AdvSimd_Arm64 = 0x0008; + public const int Aes = 0x0010; + public const int Crc32 = 0x0020; + public const int Crc32_Arm64 = 0x0040; + public const int Sha1 = 0x0080; + public const int Sha256 = 0x0100; + public const int Atomics = 0x0200; + public const int Vector64 = 0x0400; + public const int Vector128 = 0x0800; + + public static int FromHardwareIntrinsicId(string id) + { + return id switch + { + "ArmBase" => ArmBase, + "ArmBase_Arm64" => ArmBase_Arm64, + "AdvSimd" => AdvSimd, + "AdvSimd_Arm64" => AdvSimd_Arm64, + "Aes" => Aes, + "Crc32" => Crc32, + "Crc32_Arm64" => Crc32_Arm64, + "Sha1" => Sha1, + "Sha256" => Sha256, + "Atomics" => Atomics, + "Vector64" => Vector64, + "Vector128" => Vector128, + _ => throw new NotSupportedException(), + }; + } + + public static int FromInstructionSetFlags(InstructionSetFlags instructionSets) + { + int result = 0; + + foreach (InstructionSet instructionSet in instructionSets) + { + result |= instructionSet switch + { + InstructionSet.ARM64_ArmBase => ArmBase, + InstructionSet.ARM64_ArmBase_Arm64 => ArmBase_Arm64, + InstructionSet.ARM64_AdvSimd => AdvSimd, + InstructionSet.ARM64_AdvSimd_Arm64 => AdvSimd_Arm64, + InstructionSet.ARM64_Aes => Aes, + InstructionSet.ARM64_Crc32 => Crc32, + InstructionSet.ARM64_Crc32_Arm64 => Crc32_Arm64, + InstructionSet.ARM64_Sha1 => Sha1, + InstructionSet.ARM64_Sha256 => Sha256, + InstructionSet.ARM64_Atomics => Atomics, + InstructionSet.ARM64_Vector64 => Vector64, + InstructionSet.ARM64_Vector128 => Vector128, + _ => throw new NotSupportedException() + }; + } + + return result; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IInliningPolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IInliningPolicy.cs new file mode 100644 index 00000000000000..9515f8e206c25e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IInliningPolicy.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler +{ + public interface IInliningPolicy + { + bool CanInline(MethodDesc caller, MethodDesc callee); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILAssemblyGeneratingMethodDebugInfoProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILAssemblyGeneratingMethodDebugInfoProvider.cs new file mode 100644 index 00000000000000..91ef798d5b26d1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILAssemblyGeneratingMethodDebugInfoProvider.cs @@ -0,0 +1,164 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; + +using Internal.IL; +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Debug information provider that wraps another provider and generates IL assembly listing + /// (and maps debug information to it) for all methods the wrapped provider doesn't provide + /// debug information for. + /// + public class ILAssemblyGeneratingMethodDebugInfoProvider : DebugInformationProvider, IDisposable + { + private readonly DebugInformationProvider _wrappedProvider; + private readonly string _fileName; + private readonly TextWriter _tw; + private readonly Dictionary _generatedInfos = new Dictionary(); + + private int _currentLine; + + public ILAssemblyGeneratingMethodDebugInfoProvider(string fileName, DebugInformationProvider wrappedProvider = null) + { + _wrappedProvider = wrappedProvider; + _fileName = fileName; + _tw = new StreamWriter(File.OpenWrite(fileName)); + } + + public override MethodDebugInformation GetDebugInfo(MethodIL methodIL) + { + MethodDebugInformation debugInfo = _wrappedProvider?.GetDebugInfo(methodIL); + if (debugInfo != null && debugInfo != MethodDebugInformation.None) + return debugInfo; + + MethodIL definitionIL = methodIL.GetMethodILDefinition(); + + // One file per compilation model has obvious stability problems in multithreaded + // compilation. This would be fixable by e.g. generating multiple files, or preparing + // the file after the IL scanning phase. Since this is a non-shipping feature, + // we're going to ignore the stability concern here. + lock (_generatedInfos) + { + if (!_generatedInfos.TryGetValue(definitionIL.OwningMethod, out debugInfo)) + { + debugInfo = GetDebugInformation(definitionIL); + _generatedInfos.Add(definitionIL.OwningMethod, debugInfo); + } + + return debugInfo; + } + } + + private MethodDebugInformation GetDebugInformation(MethodIL methodIL) + { + MethodDesc owningMethod = methodIL.OwningMethod; + var disasm = new ILDisassembler(methodIL); + var fmt = new ILDisassembler.ILTypeNameFormatter(null); + + ArrayBuilder sequencePoints = new ArrayBuilder(); + + _tw.Write(".method "); + // TODO: accessibility, specialname, calling conventions etc. + if (!owningMethod.Signature.IsStatic) + _tw.Write("instance "); + _tw.Write(fmt.FormatName(owningMethod.Signature.ReturnType)); + _tw.Write(" "); + _tw.Write(owningMethod.Name); + if (owningMethod.HasInstantiation) + { + _tw.Write("<"); + for (int i = 0; i < owningMethod.Instantiation.Length; i++) + { + if (i != 0) + _tw.Write(", "); + _tw.Write(fmt.FormatName(owningMethod.Instantiation[i])); + } + _tw.Write(">"); + } + _tw.Write("("); + for (int i = 0; i < owningMethod.Signature.Length; i++) + { + if (i != 0) + _tw.Write(", "); + _tw.Write(fmt.FormatName(owningMethod.Signature[i])); + } + _tw.WriteLine(") cil managed"); _currentLine++; + + _tw.WriteLine("{"); _currentLine++; + + _tw.Write(" // Code size: "); + _tw.Write(disasm.CodeSize); + _tw.WriteLine(); _currentLine++; + _tw.Write(" .maxstack "); + _tw.Write(methodIL.MaxStack); + _tw.WriteLine(); _currentLine++; + + LocalVariableDefinition[] locals = methodIL.GetLocals(); + if (locals != null && locals.Length > 0) + { + _tw.Write(" .locals "); + if (methodIL.IsInitLocals) + _tw.Write("init "); + + _tw.Write("("); + + for (int i = 0; i < locals.Length; i++) + { + if (i != 0) + { + _tw.WriteLine(","); _currentLine++; + _tw.Write(" "); + } + _tw.Write(fmt.FormatName(locals[i].Type)); + _tw.Write(" "); + if (locals[i].IsPinned) + _tw.Write("pinned "); + _tw.Write("V_"); + _tw.Write(i); + } + _tw.WriteLine(")"); _currentLine++; + } + _tw.WriteLine(); _currentLine++; + + while (disasm.HasNextInstruction) + { + _currentLine++; + + int offset = disasm.Offset; + _tw.WriteLine(disasm.GetNextInstruction()); + sequencePoints.Add(new ILSequencePoint(offset, _fileName, _currentLine)); + } + + _tw.WriteLine("}"); _currentLine++; + _tw.WriteLine(); _currentLine++; + + return new SyntheticMethodDebugInformation(sequencePoints.ToArray()); + } + + public void Dispose() + { + _tw.Dispose(); + } + + private class SyntheticMethodDebugInformation : MethodDebugInformation + { + private readonly ILSequencePoint[] _sequencePoints; + + public SyntheticMethodDebugInformation(ILSequencePoint[] sequencePoints) + { + _sequencePoints = sequencePoints; + } + + public override IEnumerable GetSequencePoints() + { + return _sequencePoints; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs new file mode 100644 index 00000000000000..e10bbe95f07971 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -0,0 +1,540 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +using Internal.IL; +using Internal.IL.Stubs; +using Internal.TypeSystem; +using Internal.ReadyToRunConstants; + +using Debug = System.Diagnostics.Debug; +using Internal.JitInterface; + +namespace ILCompiler +{ + /// + /// IL scan analyzer of programs - this class analyzes what methods, types and other runtime artifact + /// will need to be generated during a compilation. The result of analysis is a conservative superset of + /// what methods will be compiled by the actual codegen backend. + /// + internal sealed class ILScanner : Compilation, IILScanner + { + private CountdownEvent _compilationCountdown; + private readonly bool _singleThreaded; + + internal ILScanner( + DependencyAnalyzerBase dependencyGraph, + ILScanNodeFactory nodeFactory, + IEnumerable roots, + ILProvider ilProvider, + DebugInformationProvider debugInformationProvider, + Logger logger, + bool singleThreaded) + : base(dependencyGraph, nodeFactory, roots, ilProvider, debugInformationProvider, null, nodeFactory.CompilationModuleGroup, logger) + { + _helperCache = new HelperCache(this); + _singleThreaded = singleThreaded; + } + + protected override void CompileInternal(string outputFile, ObjectDumper dumper) + { + // TODO: We should have a base class for compilation that doesn't implement ICompilation so that + // we don't need this. + throw new NotSupportedException(); + } + + protected override void ComputeDependencyNodeDependencies(List> obj) + { + // Determine the list of method we actually need to scan + var methodsToCompile = new List(); + var canonicalMethodsToCompile = new HashSet(); + + foreach (DependencyNodeCore dependency in obj) + { + var methodCodeNodeNeedingCode = dependency as ScannedMethodNode; + if (methodCodeNodeNeedingCode == null) + { + // To compute dependencies of the shadow method that tracks dictionary + // dependencies we need to ensure there is code for the canonical method body. + var dependencyMethod = (ShadowConcreteMethodNode)dependency; + methodCodeNodeNeedingCode = (ScannedMethodNode)dependencyMethod.CanonicalMethodNode; + } + + // We might have already queued this method for compilation + MethodDesc method = methodCodeNodeNeedingCode.Method; + if (method.IsCanonicalMethod(CanonicalFormKind.Any) + && !canonicalMethodsToCompile.Add(method)) + { + continue; + } + + methodsToCompile.Add(methodCodeNodeNeedingCode); + } + + if (_singleThreaded) + { + CompileSingleThreaded(methodsToCompile); + } + else + { + CompileMultiThreaded(methodsToCompile); + } + } + + private void CompileMultiThreaded(List methodsToCompile) + { + if (Logger.IsVerbose) + { + Logger.Writer.WriteLine($"Scanning {methodsToCompile.Count} methods..."); + } + + WaitCallback compileSingleMethodDelegate = m => CompileSingleMethod((ScannedMethodNode)m); + + using (_compilationCountdown = new CountdownEvent(methodsToCompile.Count)) + { + foreach (ScannedMethodNode methodCodeNodeNeedingCode in methodsToCompile) + { + ThreadPool.QueueUserWorkItem(compileSingleMethodDelegate, methodCodeNodeNeedingCode); + } + + _compilationCountdown.Wait(); + _compilationCountdown = null; + } + } + + private void CompileSingleThreaded(List methodsToCompile) + { + foreach (ScannedMethodNode methodCodeNodeNeedingCode in methodsToCompile) + { + if (Logger.IsVerbose) + { + Logger.Writer.WriteLine($"Compiling {methodCodeNodeNeedingCode.Method}..."); + } + + CompileSingleMethod(methodCodeNodeNeedingCode); + } + } + + private void CompileSingleMethod(ScannedMethodNode methodCodeNodeNeedingCode) + { + MethodDesc method = methodCodeNodeNeedingCode.Method; + + try + { + var importer = new ILImporter(this, method); + methodCodeNodeNeedingCode.InitializeDependencies(_nodeFactory, importer.Import()); + } + catch (TypeSystemException ex) + { + // Try to compile the method again, but with a throwing method body this time. + MethodIL throwingIL = TypeSystemThrowingILEmitter.EmitIL(method, ex); + var importer = new ILImporter(this, method, throwingIL); + methodCodeNodeNeedingCode.InitializeDependencies(_nodeFactory, importer.Import(), ex); + } + catch (Exception ex) + { + throw new CodeGenerationFailedException(method, ex); + } + finally + { + if (_compilationCountdown != null) + _compilationCountdown.Signal(); + } + } + + ILScanResults IILScanner.Scan() + { + _dependencyGraph.ComputeMarkedNodes(); + + _nodeFactory.SetMarkingComplete(); + + return new ILScanResults(_dependencyGraph, _nodeFactory); + } + + public ISymbolNode GetHelperEntrypoint(ReadyToRunHelper helper) + { + return _helperCache.GetOrCreateValue(helper).Symbol; + } + + class Helper + { + public ReadyToRunHelper HelperID { get; } + public ISymbolNode Symbol { get; } + + public Helper(ReadyToRunHelper id, ISymbolNode symbol) + { + HelperID = id; + Symbol = symbol; + } + } + + private HelperCache _helperCache; + class HelperCache : LockFreeReaderHashtable + { + private Compilation _compilation; + + public HelperCache(Compilation compilation) + { + _compilation = compilation; + } + + protected override bool CompareKeyToValue(ReadyToRunHelper key, Helper value) => key == value.HelperID; + protected override bool CompareValueToValue(Helper value1, Helper value2) => value1.HelperID == value2.HelperID; + protected override int GetKeyHashCode(ReadyToRunHelper key) => (int)key; + protected override int GetValueHashCode(Helper value) => (int)value.HelperID; + protected override Helper CreateValueFromKey(ReadyToRunHelper key) + { + string mangledName; + MethodDesc methodDesc; + JitHelper.GetEntryPoint(_compilation.TypeSystemContext, key, out mangledName, out methodDesc); + Debug.Assert(mangledName != null || methodDesc != null); + + ISymbolNode entryPoint; + if (mangledName != null) + entryPoint = _compilation.NodeFactory.ExternSymbol(mangledName); + else + entryPoint = _compilation.NodeFactory.MethodEntrypoint(methodDesc); + + return new Helper(key, entryPoint); + } + + } + } + + public interface IILScanner + { + ILScanResults Scan(); + } + + internal class ScannerFailedException : InternalCompilerErrorException + { + public ScannerFailedException(string message) + : base(message + " " + "You can work around by running the compilation with scanner disabled.") + { + } + } + + public class ILScanResults : CompilationResults + { + internal ILScanResults(DependencyAnalyzerBase graph, NodeFactory factory) + : base(graph, factory) + { + } + + public AnalysisBasedInteropStubManager GetInteropStubManager(InteropStateManager stateManager, PInvokeILEmitterConfiguration pinvokePolicy) + { + return new AnalysisBasedInteropStubManager(stateManager, pinvokePolicy, + _factory.MetadataManager.GetTypesWithStructMarshalling(), + _factory.MetadataManager.GetTypesWithDelegateMarshalling()); + } + + public VTableSliceProvider GetVTableLayoutInfo() + { + return new ScannedVTableProvider(MarkedNodes); + } + + public DictionaryLayoutProvider GetDictionaryLayoutInfo() + { + return new ScannedDictionaryLayoutProvider(_factory, MarkedNodes); + } + + public DevirtualizationManager GetDevirtualizationManager() + { + return new ScannedDevirtualizationManager(MarkedNodes); + } + + public IInliningPolicy GetInliningPolicy() + { + return new ScannedInliningPolicy(_factory.CompilationModuleGroup, MarkedNodes); + } + + public MethodImportationErrorProvider GetMethodImportationErrorProvider() + { + return new ScannedMethodImportationErrorProvider(MarkedNodes); + } + + private class ScannedVTableProvider : VTableSliceProvider + { + private Dictionary> _vtableSlices = new Dictionary>(); + + public ScannedVTableProvider(ImmutableArray> markedNodes) + { + foreach (var node in markedNodes) + { + var vtableSliceNode = node as VTableSliceNode; + if (vtableSliceNode != null) + { + _vtableSlices.Add(vtableSliceNode.Type, vtableSliceNode.Slots); + } + } + } + + internal override VTableSliceNode GetSlice(TypeDesc type) + { + // TODO: move ownership of compiler-generated entities to CompilerTypeSystemContext. + // https://github.com/dotnet/corert/issues/3873 + if (type.GetTypeDefinition() is Internal.TypeSystem.Ecma.EcmaType) + { + if (!_vtableSlices.TryGetValue(type, out IReadOnlyList slots)) + { + // If we couln't find the vtable slice information for this type, it's because the scanner + // didn't correctly predict what will be needed. + // To troubleshoot, compare the dependency graph of the scanner and the compiler. + // Follow the path from the node that requested this node to the root. + // On the path, you'll find a node that exists in both graphs, but it's predecessor + // only exists in the compiler's graph. That's the place to focus the investigation on. + // Use the ILCompiler-DependencyGraph-Viewer tool to investigate. + Debug.Assert(false); + string typeName = ExceptionTypeNameFormatter.Instance.FormatName(type); + throw new ScannerFailedException($"VTable of type '{typeName}' not computed by the IL scanner."); + } + return new PrecomputedVTableSliceNode(type, slots); + } + else + return new LazilyBuiltVTableSliceNode(type); + } + } + + private class ScannedDictionaryLayoutProvider : DictionaryLayoutProvider + { + private Dictionary> _layouts = new Dictionary>(); + private HashSet _entitiesWithForcedLazyLookups = new HashSet(); + + public ScannedDictionaryLayoutProvider(NodeFactory factory, ImmutableArray> markedNodes) + { + foreach (var node in markedNodes) + { + if (node is DictionaryLayoutNode layoutNode) + { + TypeSystemEntity owningMethodOrType = layoutNode.OwningMethodOrType; + _layouts.Add(owningMethodOrType, layoutNode.Entries); + } + else if (node is ReadyToRunGenericHelperNode genericLookup + && genericLookup.HandlesInvalidEntries(factory)) + { + // If a dictionary layout has an associated lookup helper that contains handling of broken slots + // (because one of our precomputed dictionaries contained an uncompilable entry) + // we won't hand out a precomputed dictionary and keep using the lookup helpers. + // The inlined lookups using the precomputed dictionary wouldn't handle the broken slots. + _entitiesWithForcedLazyLookups.Add(genericLookup.DictionaryOwner); + } + } + } + + private DictionaryLayoutNode GetPrecomputedLayout(TypeSystemEntity methodOrType) + { + if (!_layouts.TryGetValue(methodOrType, out IEnumerable layout)) + { + // If we couln't find the dictionary layout information for this, it's because the scanner + // didn't correctly predict what will be needed. + // To troubleshoot, compare the dependency graph of the scanner and the compiler. + // Follow the path from the node that requested this node to the root. + // On the path, you'll find a node that exists in both graphs, but it's predecessor + // only exists in the compiler's graph. That's the place to focus the investigation on. + // Use the ILCompiler-DependencyGraph-Viewer tool to investigate. + Debug.Assert(false); + throw new ScannerFailedException($"A dictionary layout was not computed by the IL scanner."); + } + return new PrecomputedDictionaryLayoutNode(methodOrType, layout); + } + + public override DictionaryLayoutNode GetLayout(TypeSystemEntity methodOrType) + { + if (_entitiesWithForcedLazyLookups.Contains(methodOrType)) + { + return new LazilyBuiltDictionaryLayoutNode(methodOrType); + } + + if (methodOrType is TypeDesc type) + { + // TODO: move ownership of compiler-generated entities to CompilerTypeSystemContext. + // https://github.com/dotnet/corert/issues/3873 + if (type.GetTypeDefinition() is Internal.TypeSystem.Ecma.EcmaType) + return GetPrecomputedLayout(type); + else + return new LazilyBuiltDictionaryLayoutNode(type); + } + else + { + Debug.Assert(methodOrType is MethodDesc); + MethodDesc method = (MethodDesc)methodOrType; + + // TODO: move ownership of compiler-generated entities to CompilerTypeSystemContext. + // https://github.com/dotnet/corert/issues/3873 + if (method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod) + return GetPrecomputedLayout(method); + else + return new LazilyBuiltDictionaryLayoutNode(method); + } + } + } + + private class ScannedDevirtualizationManager : DevirtualizationManager + { + private HashSet _constructedTypes = new HashSet(); + private HashSet _unsealedTypes = new HashSet(); + private HashSet _abstractButNonabstractlyOverridenTypes = new HashSet(); + + public ScannedDevirtualizationManager(ImmutableArray> markedNodes) + { + foreach (var node in markedNodes) + { + if (node is ConstructedEETypeNode eetypeNode) + { + TypeDesc type = eetypeNode.Type; + + if (!type.IsInterface) + { + // + // We collect this information: + // + // 1. What types got allocated + // 2. What types are the base types of other types + // This is needed for optimizations. We use this information to effectively + // seal types that are not base types for any other type. + // 3. What abstract types got derived by non-abstract types. + // This is needed for correctness. Abstract types that were never derived + // by non-abstract types should never be devirtualized into - we probably + // didn't scan the virtual methods on them. + // + + _constructedTypes.Add(type); + + TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific); + + bool hasNonAbstractTypeInHierarchy = canonType is not MetadataType mdType || !mdType.IsAbstract; + TypeDesc baseType = canonType.BaseType; + bool added = true; + while (baseType != null && added) + { + baseType = baseType.ConvertToCanonForm(CanonicalFormKind.Specific); + added = _unsealedTypes.Add(baseType); + + bool currentTypeIsAbstract = ((MetadataType)baseType).IsAbstract; + if (currentTypeIsAbstract && hasNonAbstractTypeInHierarchy) + added |= _abstractButNonabstractlyOverridenTypes.Add(baseType); + hasNonAbstractTypeInHierarchy |= !currentTypeIsAbstract; + + baseType = baseType.BaseType; + } + } + } + } + } + + public override bool IsEffectivelySealed(TypeDesc type) + { + // If we know we scanned a type that derives from this one, this for sure can't be reported as sealed. + TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific); + if (_unsealedTypes.Contains(canonType)) + return false; + + // Don't report __Canon as sealed or it can cause trouble + // (E.g. RyuJIT might think it's okay to omit array element type checks for __Canon[].) + if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) + return false; + + if (type is MetadataType metadataType) + { + // Due to how the compiler is structured, we might see "constructed" EETypes for things + // that never got allocated (doing a typeof() on a class that is otherwise never used is + // a good example of when that happens). This can put us into a position where we could + // report `sealed` on an `abstract` class, but that doesn't lead to anything good. + return !metadataType.IsAbstract; + } + + // Everything else can be considered sealed. + return true; + } + + protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType, out CORINFO_DEVIRTUALIZATION_DETAIL devirtualizationDetail) + { + MethodDesc result = base.ResolveVirtualMethod(declMethod, implType, out devirtualizationDetail); + if (result != null && result.IsFinal && result.OwningType is MetadataType mdType && mdType.IsAbstract) + { + // If this type is abstract check that we saw a non-abstract type deriving from it. + // We don't look at virtual methods introduced by abstract classes unless there's a non-abstract + // class that needs them (i.e. the non-abstract class doesn't immediately override them). + // This lets us optimize out some unused virtual method implementations. + // Allowing this to devirtualize would cause trouble because we didn't scan the method + // and expected it would be optimized out. + if (!_abstractButNonabstractlyOverridenTypes.Contains(mdType.ConvertToCanonForm(CanonicalFormKind.Specific))) + { + // FAILED_BUBBLE_IMPL_NOT_REFERENCEABLE is close enough... + devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_BUBBLE_IMPL_NOT_REFERENCEABLE; + return null; + } + } + + return result; + } + + public override bool CanConstructType(TypeDesc type) => _constructedTypes.Contains(type); + } + + private class ScannedInliningPolicy : IInliningPolicy + { + private readonly HashSet _constructedTypes = new HashSet(); + private readonly CompilationModuleGroup _baseGroup; + + public ScannedInliningPolicy(CompilationModuleGroup baseGroup, ImmutableArray> markedNodes) + { + _baseGroup = baseGroup; + + foreach (var node in markedNodes) + { + if (node is ConstructedEETypeNode eetypeNode) + { + TypeDesc type = eetypeNode.Type; + _constructedTypes.Add(type); + } + } + } + + public bool CanInline(MethodDesc caller, MethodDesc callee) + { + if (_baseGroup.CanInline(caller, callee)) + { + // Since the scanner doesn't look at instance methods whose owning type + // wasn't allocated (done through TentativeInstanceMethodNode), + // we need to disallow inlining these methods. They could + // bring in dependencies that we didn't look at. + if (callee.NotCallableWithoutOwningEEType()) + { + return _constructedTypes.Contains(callee.OwningType); + } + return true; + } + + return false; + } + } + + private sealed class ScannedMethodImportationErrorProvider : MethodImportationErrorProvider + { + private readonly Dictionary _importationErrors = new Dictionary(); + + public ScannedMethodImportationErrorProvider(ImmutableArray> markedNodes) + { + foreach (var markedNode in markedNodes) + { + if (markedNode is ScannedMethodNode scannedMethod + && scannedMethod.Exception != null) + { + _importationErrors.Add(scannedMethod.Method, scannedMethod.Exception); + } + } + } + + public override TypeSystemException GetCompilationError(MethodDesc method) + => _importationErrors.TryGetValue(method, out var exception) ? exception : null; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs new file mode 100644 index 00000000000000..27e7e64b6ce193 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; +using Internal.IL; + +namespace ILCompiler +{ + public sealed class ILScannerBuilder + { + private readonly CompilerTypeSystemContext _context; + private readonly CompilationModuleGroup _compilationGroup; + private readonly NameMangler _nameMangler; + private readonly ILProvider _ilProvider; + private readonly PreinitializationManager _preinitializationManager; + + // These need to provide reasonable defaults so that the user can optionally skip + // calling the Use/Configure methods and still get something reasonable back. + private Logger _logger = Logger.Null; + private DependencyTrackingLevel _dependencyTrackingLevel = DependencyTrackingLevel.None; + private IEnumerable _compilationRoots = Array.Empty(); + private MetadataManager _metadataManager; + private InteropStubManager _interopStubManager = new EmptyInteropStubManager(); + private bool _singleThreaded; + + internal ILScannerBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler mangler, ILProvider ilProvider, PreinitializationManager preinitializationManager) + { + _context = context; + _compilationGroup = compilationGroup; + _nameMangler = mangler; + _metadataManager = new AnalysisBasedMetadataManager(context); + _ilProvider = ilProvider; + _preinitializationManager = preinitializationManager; + } + + public ILScannerBuilder UseDependencyTracking(DependencyTrackingLevel trackingLevel) + { + _dependencyTrackingLevel = trackingLevel; + return this; + } + + public ILScannerBuilder UseCompilationRoots(IEnumerable compilationRoots) + { + _compilationRoots = compilationRoots; + return this; + } + + public ILScannerBuilder UseMetadataManager(MetadataManager metadataManager) + { + _metadataManager = metadataManager; + return this; + } + + public ILScannerBuilder UseInteropStubManager(InteropStubManager interopStubManager) + { + _interopStubManager = interopStubManager; + return this; + } + + public ILScannerBuilder UseSingleThread(bool enable) + { + _singleThreaded = enable; + return this; + } + + public IILScanner ToILScanner() + { + var nodeFactory = new ILScanNodeFactory(_context, _compilationGroup, _metadataManager, _interopStubManager, _nameMangler, _preinitializationManager); + DependencyAnalyzerBase graph = _dependencyTrackingLevel.CreateDependencyGraph(nodeFactory); + + return new ILScanner(graph, nodeFactory, _compilationRoots, _ilProvider, new NullDebugInformationProvider(), _logger, _singleThreaded); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILStreamReader.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILStreamReader.cs new file mode 100644 index 00000000000000..1f1310d3d0653c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILStreamReader.cs @@ -0,0 +1,181 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; +using Internal.IL; + +namespace Internal.Compiler +{ + /// + /// IL Opcode reader in external reader style where the reading is done by trying to read + /// various opcodes, and the reader can indicate success or failure of reading a particular opcode + /// + /// Used by logic which is designed to encode information in il structure, but not used + /// to support general compilation of IL. + /// + public struct ILStreamReader + { + private ILReader _reader; + private readonly MethodIL _methodIL; + + public ILStreamReader(MethodIL methodIL) + { + _methodIL = methodIL; + _reader = new ILReader(methodIL.GetILBytes()); + } + + public bool HasNextInstruction + { + get + { + return _reader.HasNext; + } + } + + public bool TryReadLdtoken(out int token) + { + if (_reader.PeekILOpcode() != ILOpcode.ldtoken) + { + token = 0; + return false; + } + + _reader.ReadILOpcode(); + token = _reader.ReadILToken(); + return true; + } + + public int ReadLdtoken() + { + int result; + if (!TryReadLdtoken(out result)) + throw new BadImageFormatException(); + + return result; + } + + public bool TryReadLdtokenAsTypeSystemEntity(out TypeSystemEntity entity) + { + int token; + bool tokenResolved; + try + { + tokenResolved = TryReadLdtoken(out token); + entity = tokenResolved ? (TypeSystemEntity)_methodIL.GetObject(token) : null; + } + catch (TypeSystemException) + { + tokenResolved = false; + entity = null; + } + + return tokenResolved; + } + + public TypeSystemEntity ReadLdtokenAsTypeSystemEntity() + { + TypeSystemEntity result; + if (!TryReadLdtokenAsTypeSystemEntity(out result)) + throw new BadImageFormatException(); + + return result; + } + + public bool TryReadLdcI4(out int value) + { + ILOpcode opcode = _reader.PeekILOpcode(); + + if (opcode == ILOpcode.ldc_i4) // ldc.i4 + { + _reader.ReadILOpcode(); + value = unchecked((int)_reader.ReadILUInt32()); + return true; + } + + if ((opcode >= ILOpcode.ldc_i4_m1) && (opcode <= ILOpcode.ldc_i4_8)) // ldc.m1 to ldc.i4.8 + { + _reader.ReadILOpcode(); + value = -1 + ((int)opcode) - 0x15; + return true; + } + + if (opcode == ILOpcode.ldc_i4_s) // ldc.i4.s + { + _reader.ReadILOpcode(); + + value = (int)unchecked((sbyte)_reader.ReadILByte()); + return true; + } + value = 0; + return false; + } + + public int ReadLdcI4() + { + int result; + if (!TryReadLdcI4(out result)) + throw new BadImageFormatException(); + + return result; + } + + public bool TryReadRet() + { + ILOpcode opcode = _reader.PeekILOpcode(); + if (opcode == ILOpcode.ret) + { + _reader.ReadILOpcode(); + return true; + } + return false; + } + + public void ReadRet() + { + if (!TryReadRet()) + throw new BadImageFormatException(); + } + + public bool TryReadPop() + { + ILOpcode opcode = _reader.PeekILOpcode(); + if (opcode == ILOpcode.pop) + { + _reader.ReadILOpcode(); + return true; + } + return false; + } + + public void ReadPop() + { + if (!TryReadPop()) + throw new BadImageFormatException(); + } + + public bool TryReadLdstr(out string ldstrString) + { + if (_reader.PeekILOpcode() != ILOpcode.ldstr) + { + ldstrString = null; + return false; + } + + _reader.ReadILOpcode(); + int token = _reader.ReadILToken(); + ldstrString = (string)_methodIL.GetObject(token); + return true; + } + + public string ReadLdstr() + { + string result; + if (!TryReadLdstr(out result)) + throw new BadImageFormatException(); + + return result; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs new file mode 100644 index 00000000000000..adff5b36116e1b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Provides a means to root types / methods at the compiler driver layer + /// + public interface IRootingServiceProvider + { + void AddCompilationRoot(MethodDesc method, string reason, string exportName = null); + void AddCompilationRoot(TypeDesc type, string reason); + void AddReflectionRoot(MethodDesc method, string reason); + void RootThreadStaticBaseForType(TypeDesc type, string reason); + void RootGCStaticBaseForType(TypeDesc type, string reason); + void RootNonGCStaticBaseForType(TypeDesc type, string reason); + void RootModuleMetadata(ModuleDesc module, string reason); + void RootReadOnlyDataBlob(byte[] data, int alignment, string reason, string exportName); + void RootDelegateMarshallingData(DefType type, string reason); + void RootStructMarshallingData(DefType type, string reason); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/InteropStubManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/InteropStubManager.cs new file mode 100644 index 00000000000000..87a7a35bb8b1ba --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/InteropStubManager.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.IL; +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysis; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; + +namespace ILCompiler +{ + /// + /// This class is responsible for managing stub methods for interop + /// + public abstract class InteropStubManager : ICompilationRootProvider + { + public abstract void AddDependeciesDueToPInvoke(ref DependencyList dependencies, NodeFactory factory, MethodDesc method); + + public abstract void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type); + + public abstract PInvokeILProvider CreatePInvokeILProvider(); + + /// + /// For Marshal generic APIs(eg. Marshal.StructureToPtr, GetFunctionPointerForDelegate) we add + /// the generic parameter as dependencies so that we can generate runtime data for them + /// + public abstract void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method); + + public virtual void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) + { + } + + public virtual void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs new file mode 100644 index 00000000000000..8259de25d6c8fd --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs @@ -0,0 +1,351 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; +using Internal.IL; +using Internal.ReadyToRunConstants; + +namespace ILCompiler +{ + internal class JitHelper + { + /// + /// Returns JIT helper entrypoint. JIT helpers can be either implemented by entrypoint with given mangled name or + /// by a method in class library. + /// + public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id, out string mangledName, out MethodDesc methodDesc) + { + mangledName = null; + methodDesc = null; + + switch (id) + { + case ReadyToRunHelper.Throw: + mangledName = "RhpThrowEx"; + break; + case ReadyToRunHelper.Rethrow: + mangledName = "RhpRethrow"; + break; + + case ReadyToRunHelper.Overflow: + methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowOverflowException"); + break; + case ReadyToRunHelper.RngChkFail: + methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowIndexOutOfRangeException"); + break; + case ReadyToRunHelper.FailFast: + mangledName = "RhpFallbackFailFast"; // TODO: Report stack buffer overrun + break; + case ReadyToRunHelper.ThrowNullRef: + methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowNullReferenceException"); + break; + case ReadyToRunHelper.ThrowDivZero: + methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowDivideByZeroException"); + break; + case ReadyToRunHelper.ThrowArgumentOutOfRange: + methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowArgumentOutOfRangeException"); + break; + case ReadyToRunHelper.ThrowArgument: + methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowArgumentException"); + break; + case ReadyToRunHelper.ThrowPlatformNotSupported: + methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowPlatformNotSupportedException"); + break; + case ReadyToRunHelper.ThrowNotImplemented: + methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowNotImplementedException"); + break; + + case ReadyToRunHelper.DebugBreak: + mangledName = "RhDebugBreak"; + break; + + case ReadyToRunHelper.WriteBarrier: + mangledName = context.Target.Architecture == TargetArchitecture.ARM64 ? "RhpAssignRefArm64" : "RhpAssignRef"; + break; + case ReadyToRunHelper.CheckedWriteBarrier: + mangledName = context.Target.Architecture == TargetArchitecture.ARM64 ? "RhpCheckedAssignRefArm64" : "RhpCheckedAssignRef"; + break; + case ReadyToRunHelper.ByRefWriteBarrier: + mangledName = context.Target.Architecture == TargetArchitecture.ARM64 ? "RhpByRefAssignRefArm64" : "RhpByRefAssignRef"; + break; + case ReadyToRunHelper.WriteBarrier_EAX: + mangledName = "RhpAssignRefEAX"; + break; + case ReadyToRunHelper.WriteBarrier_ECX: + mangledName = "RhpAssignRefECX"; + break; + case ReadyToRunHelper.CheckedWriteBarrier_EAX: + mangledName = "RhpCheckedAssignRefEAX"; + break; + case ReadyToRunHelper.CheckedWriteBarrier_ECX: + mangledName = "RhpCheckedAssignRefECX"; + break; + + case ReadyToRunHelper.Box: + case ReadyToRunHelper.Box_Nullable: + mangledName = "RhBox"; + break; + case ReadyToRunHelper.Unbox: + mangledName = "RhUnbox2"; + break; + case ReadyToRunHelper.Unbox_Nullable: + mangledName = "RhUnboxNullable"; + break; + + case ReadyToRunHelper.NewMultiDimArr_NonVarArg: + methodDesc = context.GetHelperEntryPoint("ArrayHelpers", "NewObjArray"); + break; + + case ReadyToRunHelper.NewArray: + mangledName = "RhNewArray"; + break; + case ReadyToRunHelper.NewObject: + mangledName = "RhNewObject"; + break; + + case ReadyToRunHelper.Stelem_Ref: + mangledName = "RhpStelemRef"; + break; + case ReadyToRunHelper.Ldelema_Ref: + mangledName = "RhpLdelemaRef"; + break; + + case ReadyToRunHelper.MemCpy: + mangledName = "memcpy"; // TODO: Null reference handling + break; + case ReadyToRunHelper.MemSet: + mangledName = "memset"; // TODO: Null reference handling + break; + + case ReadyToRunHelper.GetRuntimeTypeHandle: + methodDesc = context.GetHelperEntryPoint("LdTokenHelpers", "GetRuntimeTypeHandle"); + break; + case ReadyToRunHelper.GetRuntimeType: + methodDesc = context.GetHelperEntryPoint("LdTokenHelpers", "GetRuntimeType"); + break; + case ReadyToRunHelper.GetRuntimeMethodHandle: + methodDesc = context.GetHelperEntryPoint("LdTokenHelpers", "GetRuntimeMethodHandle"); + break; + case ReadyToRunHelper.GetRuntimeFieldHandle: + methodDesc = context.GetHelperEntryPoint("LdTokenHelpers", "GetRuntimeFieldHandle"); + break; + + case ReadyToRunHelper.AreTypesEquivalent: + mangledName = "RhTypeCast_AreTypesEquivalent"; + break; + + case ReadyToRunHelper.Lng2Dbl: + mangledName = "RhpLng2Dbl"; + break; + case ReadyToRunHelper.ULng2Dbl: + mangledName = "RhpULng2Dbl"; + break; + + case ReadyToRunHelper.Dbl2Lng: + mangledName = "RhpDbl2Lng"; + break; + case ReadyToRunHelper.Dbl2ULng: + mangledName = "RhpDbl2ULng"; + break; + case ReadyToRunHelper.Dbl2Int: + mangledName = "RhpDbl2Int"; + break; + case ReadyToRunHelper.Dbl2UInt: + mangledName = "RhpDbl2UInt"; + break; + + case ReadyToRunHelper.Dbl2IntOvf: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2IntOvf"); + break; + case ReadyToRunHelper.Dbl2UIntOvf: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2UIntOvf"); + break; + case ReadyToRunHelper.Dbl2LngOvf: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2LngOvf"); + break; + case ReadyToRunHelper.Dbl2ULngOvf: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2ULngOvf"); + break; + + case ReadyToRunHelper.DblRem: + mangledName = "RhpDblRem"; + break; + case ReadyToRunHelper.FltRem: + mangledName = "RhpFltRem"; + break; + case ReadyToRunHelper.DblRound: + mangledName = "RhpDblRound"; + break; + case ReadyToRunHelper.FltRound: + mangledName = "RhpFltRound"; + break; + + case ReadyToRunHelper.LMul: + mangledName = "RhpLMul"; + break; + case ReadyToRunHelper.LMulOfv: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "LMulOvf"); + break; + case ReadyToRunHelper.ULMulOvf: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "ULMulOvf"); + break; + + case ReadyToRunHelper.Mod: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "IMod"); + break; + case ReadyToRunHelper.UMod: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "UMod"); + break; + case ReadyToRunHelper.ULMod: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "ULMod"); + break; + case ReadyToRunHelper.LMod: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "LMod"); + break; + + case ReadyToRunHelper.Div: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "IDiv"); + break; + case ReadyToRunHelper.UDiv: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "UDiv"); + break; + case ReadyToRunHelper.ULDiv: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "ULDiv"); + break; + case ReadyToRunHelper.LDiv: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "LDiv"); + break; + + case ReadyToRunHelper.LRsz: + mangledName = "RhpLRsz"; + break; + case ReadyToRunHelper.LRsh: + mangledName = "RhpLRsh"; + break; + case ReadyToRunHelper.LLsh: + mangledName = "RhpLLsh"; + break; + + case ReadyToRunHelper.PInvokeBegin: + mangledName = "RhpPInvoke"; + break; + case ReadyToRunHelper.PInvokeEnd: + mangledName = "RhpPInvokeReturn"; + break; + + case ReadyToRunHelper.ReversePInvokeEnter: + mangledName = "RhpReversePInvoke"; + break; + case ReadyToRunHelper.ReversePInvokeExit: + mangledName = "RhpReversePInvokeReturn"; + break; + + case ReadyToRunHelper.CheckCastAny: + mangledName = "RhTypeCast_CheckCast"; + break; + case ReadyToRunHelper.CheckInstanceAny: + mangledName = "RhTypeCast_IsInstanceOf"; + break; + case ReadyToRunHelper.CheckCastInterface: + mangledName = "RhTypeCast_CheckCastInterface"; + break; + case ReadyToRunHelper.CheckInstanceInterface: + mangledName = "RhTypeCast_IsInstanceOfInterface"; + break; + case ReadyToRunHelper.CheckCastClass: + mangledName = "RhTypeCast_CheckCastClass"; + break; + case ReadyToRunHelper.CheckInstanceClass: + mangledName = "RhTypeCast_IsInstanceOfClass"; + break; + case ReadyToRunHelper.CheckCastArray: + mangledName = "RhTypeCast_CheckCastArray"; + break; + case ReadyToRunHelper.CheckInstanceArray: + mangledName = "RhTypeCast_IsInstanceOfArray"; + break; + + case ReadyToRunHelper.MonitorEnter: + methodDesc = context.GetHelperEntryPoint("SynchronizedMethodHelpers", "MonitorEnter"); + break; + case ReadyToRunHelper.MonitorExit: + methodDesc = context.GetHelperEntryPoint("SynchronizedMethodHelpers", "MonitorExit"); + break; + case ReadyToRunHelper.MonitorEnterStatic: + methodDesc = context.GetHelperEntryPoint("SynchronizedMethodHelpers", "MonitorEnterStatic"); + break; + case ReadyToRunHelper.MonitorExitStatic: + methodDesc = context.GetHelperEntryPoint("SynchronizedMethodHelpers", "MonitorExitStatic"); + break; + + case ReadyToRunHelper.GVMLookupForSlot: + methodDesc = context.SystemModule.GetKnownType("System.Runtime", "TypeLoaderExports").GetKnownMethod("GVMLookupForSlot", null); + break; + + case ReadyToRunHelper.TypeHandleToRuntimeType: + methodDesc = context.GetHelperEntryPoint("TypedReferenceHelpers", "TypeHandleToRuntimeTypeMaybeNull"); + break; + case ReadyToRunHelper.GetRefAny: + methodDesc = context.GetHelperEntryPoint("TypedReferenceHelpers", "GetRefAny"); + break; + case ReadyToRunHelper.TypeHandleToRuntimeTypeHandle: + methodDesc = context.GetHelperEntryPoint("TypedReferenceHelpers", "TypeHandleToRuntimeTypeHandleMaybeNull"); + break; + + case ReadyToRunHelper.GetCurrentManagedThreadId: + methodDesc = context.SystemModule.GetKnownType("System", "Environment").GetKnownMethod("get_CurrentManagedThreadId", null); + break; + + default: + throw new NotImplementedException(id.ToString()); + } + } + + // + // These methods are static compiler equivalent of RhGetRuntimeHelperForType + // + public static string GetNewObjectHelperForType(TypeDesc type) + { + if (type.RequiresAlign8()) + { + if (type.HasFinalizer) + return "RhpNewFinalizableAlign8"; + + if (type.IsValueType) + return "RhpNewFastMisalign"; + + return "RhpNewFastAlign8"; + } + + if (type.HasFinalizer) + return "RhpNewFinalizable"; + + return "RhpNewFast"; + } + + public static string GetNewArrayHelperForType(TypeDesc type) + { + if (type.RequiresAlign8()) + return "RhpNewArrayAlign8"; + + return "RhpNewArray"; + } + + public static string GetCastingHelperNameForType(TypeDesc type, bool throwing) + { + if (type.IsArray) + return throwing ? "RhTypeCast_CheckCastArray" : "RhTypeCast_IsInstanceOfArray"; + + if (type.IsInterface) + return throwing ? "RhTypeCast_CheckCastInterface" : "RhTypeCast_IsInstanceOfInterface"; + + if (type.IsDefType) + return throwing ? "RhTypeCast_CheckCastClass" : "RhTypeCast_IsInstanceOfClass"; + + // No specialized helper for the rest of the types because they don't make much sense anyway. + return throwing ? "RhTypeCast_CheckCast" : "RhTypeCast_IsInstanceOf"; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/Graph.Cycles.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/Graph.Cycles.cs new file mode 100644 index 00000000000000..9819cd855c5e7a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/Graph.Cycles.cs @@ -0,0 +1,390 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace ILCompiler +{ + internal static partial class LazyGenericsSupport + { + private const long s_previousAlgorithmTimeout = 10000; + + private sealed partial class Graph

+ { + class TarjanWorkerClass + { + Stack _inProgress = new Stack(); + int _currentComponentIndex = 0; + Vertex[] _vertices; + List> _result = new List>(); + + public TarjanWorkerClass(Vertex[] vertices, bool iterative) + { + this._vertices = vertices; + + foreach (Vertex vertex in vertices) + { + vertex.Index = -1; + vertex.LowLink = -1; + vertex.OnStack = false; + } + + foreach (Vertex vertex in vertices) + { + if (vertex.Index == -1) + { + if (iterative) + { + StrongConnectIterative(vertex); + } + else + { + StrongConnectRecursive(vertex); + } + } + } + } + + void StrongConnectRecursive(Vertex vertex) + { + vertex.Index = _currentComponentIndex; + vertex.LowLink = _currentComponentIndex; + + _currentComponentIndex++; + + _inProgress.Push(vertex); + vertex.OnStack = true; + + foreach (Edge edge in vertex.Edges) + { + if (edge.Destination.Index == -1) + { + // Recurse if the destination has not begun processing yet + StrongConnectRecursive(edge.Destination); + vertex.LowLink = Math.Min(vertex.LowLink, edge.Destination.LowLink); + } + else if (edge.Destination.OnStack) + { + vertex.LowLink = Math.Min(vertex.LowLink, edge.Destination.Index); + } + } + + // If v is a root node, pop the stack and generate an SCC + if (vertex.LowLink == vertex.Index) + { + List newStronglyConnectedComponent = new List(); + Vertex poppedVertex = null; + while (poppedVertex != vertex) + { + poppedVertex = _inProgress.Pop(); + poppedVertex.OnStack = false; + + newStronglyConnectedComponent.Add(poppedVertex); + } + + _result.Add(newStronglyConnectedComponent); + } + } + + struct StrongConnectStackElement + { + public Vertex Vertex; + public IEnumerator EdgeEnumeratorPosition; + } + + private Stack IterativeStrongConnectStack = new Stack(); + + void StrongConnectIterative(Vertex vertex) + { + IEnumerator currentEdgeEnumerator = null; + +StartOfFunctionWithLogicalRecursion: + vertex.Index = _currentComponentIndex; + vertex.LowLink = _currentComponentIndex; + + _currentComponentIndex++; + + _inProgress.Push(vertex); + vertex.OnStack = true; + + currentEdgeEnumerator = vertex.Edges.GetEnumerator(); + +ReturnFromEndOfRecursiveFunction: + if (currentEdgeEnumerator == null) + { + // Return from logically recursive call + StrongConnectStackElement iterativeStackElementOnReturn = IterativeStrongConnectStack.Pop(); + vertex = iterativeStackElementOnReturn.Vertex; + currentEdgeEnumerator = iterativeStackElementOnReturn.EdgeEnumeratorPosition; + + vertex.LowLink = Math.Min(vertex.LowLink, currentEdgeEnumerator.Current.Destination.LowLink); + } + + while (currentEdgeEnumerator.MoveNext()) + { + Edge edge = currentEdgeEnumerator.Current; + + if (edge.Destination.Index == -1) + { + // Recurse if the destination has not begun processing yet + StrongConnectStackElement iterativeStackElement = new StrongConnectStackElement(); + iterativeStackElement.Vertex = vertex; + iterativeStackElement.EdgeEnumeratorPosition = currentEdgeEnumerator; + + IterativeStrongConnectStack.Push(iterativeStackElement); + + currentEdgeEnumerator = null; + vertex = edge.Destination; + goto StartOfFunctionWithLogicalRecursion; + } + else if (edge.Destination.OnStack) + { + vertex.LowLink = Math.Min(vertex.LowLink, edge.Destination.Index); + } + } + + // If v is a root node, pop the stack and generate an SCC + if (vertex.LowLink == vertex.Index) + { + List newStronglyConnectedComponent = new List(); + Vertex poppedVertex = null; + while (poppedVertex != vertex) + { + poppedVertex = _inProgress.Pop(); + poppedVertex.OnStack = false; + + newStronglyConnectedComponent.Add(poppedVertex); + } + + _result.Add(newStronglyConnectedComponent); + } + + if (IterativeStrongConnectStack.Count > 0) + { + currentEdgeEnumerator = null; + vertex = null; + goto ReturnFromEndOfRecursiveFunction; + } + } + + public IEnumerable> Result + { + get + { + return _result; + } + } + } + + private IEnumerable> TarjansAlgorithm() + { + Vertex[] vertices = _vertexMap.Values.ToArray(); + + TarjanWorkerClass tarjansResultsIterative = new TarjanWorkerClass(vertices, true); + +#if DEBUG + TarjanWorkerClass tarjansResultsRecursive = new TarjanWorkerClass(vertices, false); + + // assert the result of the iterative and recursive versions of the algorithm are EXACTLY the same + Debug.Assert(tarjansResultsIterative.Result.SelectMany(x => x).SequenceEqual(tarjansResultsRecursive.Result.SelectMany(x => x))); +#endif + return tarjansResultsIterative.Result; + } + + ///

+ /// Returns the set of vertices that are part of at least one cycle where one of the edges are flagged. + /// + public IEnumerable

ComputeVerticesInvolvedInAFlaggedCycle() + { + // New Algorithm. + IEnumerable> stronglyConnectedComponents = this.TarjansAlgorithm(); + foreach (List stronglyConnectedComponent in stronglyConnectedComponents) + { + HashSet strongConnectedComponentVertexContainsChecker = new HashSet(stronglyConnectedComponent); + // Detect flags between elements of cycle. + // Walk all edges of the strongly connected component. + // - If an edge is not flagged, it can't affect behavior + // - If an edge is flagged, if it refers to another Vertex in the strongly connected component, then the cycle should be flagged. + bool flagDetected = false; + foreach (Vertex vertex in stronglyConnectedComponent) + { + foreach (Edge edge in vertex.Edges) + { + if (edge.Flagged) + { + if (strongConnectedComponentVertexContainsChecker.Contains(edge.Destination)) + { + flagDetected = true; + break; + } + } + } + + if (flagDetected) + { + break; + } + } + + // Flag was detected, therefore each vertex in the strongly connected component is part of a flagged cycle + if (flagDetected) + { + foreach (Vertex vertex in stronglyConnectedComponent) + { + vertex.ProvedToBeInvolvedInAFlaggedCycle = true; + } + } + } + + IEnumerable verticesInAFlaggedCycleTarjanStyle = _vertexMap.Values.Where(v => v.ProvedToBeInvolvedInAFlaggedCycle); + +#if DEBUG + Vertex[] vertices = _vertexMap.Values.ToArray(); + foreach (Vertex vertex in vertices) + { + vertex.ProvedToBeNotPartOfAnyCycle = false; + vertex.ProvedToBeInvolvedInAFlaggedCycle = false; + } + + Stopwatch previousAlgorithmTimeoutWatch = new Stopwatch(); + previousAlgorithmTimeoutWatch.Start(); + bool abortedDueToTimeout = false; + int operationCount = 0; + + for (; ; ) + { + // + // Each pass visits every vertex and attempts to detect whether it is part of a flagged cycle. The first pass only finds simple + // cycles (i.e. it finds T==>U==>T but not T-->U==>V==>U-->T where "-->" denotes a non-flagged edge and "==>" denotes a flagged edge.). The second + // pass detects the "T-->U==>V==>U-->T" cycle (as it now benefits from the knowledge that any cycle including "U" is itself a flagged cycle.) + // More complex graphs require more than two passes, with the worst case being the number of vertices. + // + // Rather than count the passes, however, we will iterate the passes until we find no new cycles. + // + + int count = vertices.Count(v => v.ProvedToBeInvolvedInAFlaggedCycle); + foreach (Vertex vertex in vertices) + { + if (!vertex.ProvedToBeNotPartOfAnyCycle) + { + if (!vertex.ProvedToBeInvolvedInAFlaggedCycle) + { + // FindCyclesWorker recurses on edges rather than vertices, so we have to invent a fictitious edge leading to the root vertex + // to kick it off. + Edge startingEdge = new Edge(vertex, false); + FindCyclesWorker(startingEdge, new List(), ref operationCount, previousAlgorithmTimeoutWatch); + } + } + } + + if (previousAlgorithmTimeoutWatch.ElapsedMilliseconds > s_previousAlgorithmTimeout) + { + abortedDueToTimeout = true; + break; + } + + int newCount = vertices.Count(v => v.ProvedToBeInvolvedInAFlaggedCycle); + if (count == newCount) + break; + + } + previousAlgorithmTimeoutWatch.Stop(); + + if (!abortedDueToTimeout) + { + Vertex[] verticesInAFlaggedCyclePreviousAlgorithmStyle = vertices.Where(v => v.ProvedToBeInvolvedInAFlaggedCycle).ToArray(); + + // Generate hashset of preview algorithm style result + Debug.Assert(verticesInAFlaggedCyclePreviousAlgorithmStyle.Length == verticesInAFlaggedCycleTarjanStyle.Count()); + HashSet verticesInFlaggedCyclePreviousAlgorithmStyleHashset = new HashSet(verticesInAFlaggedCyclePreviousAlgorithmStyle); + foreach (Vertex v in verticesInAFlaggedCycleTarjanStyle) + { + Debug.Assert(verticesInFlaggedCyclePreviousAlgorithmStyleHashset.Contains(v)); + } + } +#endif + + return verticesInAFlaggedCycleTarjanStyle.Select(v => v.Payload).ToArray(); + } + + ///

+ /// Depth-first walk every path from edge.Destination to every other reachable vertex. If one of those vertices is edge.Destination itself + /// and the cycle includes a flagged edge or a vertex that itself is involved in a flagged cycle, then mark edge.Destination as belonging to a flagged cycle. + /// + /// + /// "alreadySeen" is actually a stack but we use a List<> because Stack<> doesn't support indexing. + /// + private void FindCyclesWorker(Edge edge, List alreadySeen, ref int operationCount, Stopwatch previousAlgorithmTimeoutWatch) + { + Vertex vertex = edge.Destination; + + if (vertex.ProvedToBeNotPartOfAnyCycle) + return; + + if ((operationCount % 10000) == 0) + { + if (previousAlgorithmTimeoutWatch.ElapsedMilliseconds > s_previousAlgorithmTimeout) + { + return; + } + } + operationCount++; + + // If this a vertex we've visited already on this path? + bool flagged = edge.Flagged || vertex.ProvedToBeInvolvedInAFlaggedCycle; + + int idx = alreadySeen.Count - 1; + while (idx != -1 && !(alreadySeen[idx].Destination == vertex)) + { + if (alreadySeen[idx].Flagged || alreadySeen[idx].Destination.ProvedToBeInvolvedInAFlaggedCycle) + flagged = true; + idx--; + } + + if (idx != -1) + { + Debug.Assert(alreadySeen[idx].Destination == vertex); + + // We've seen this vertex already in our path. We now know that this vertex is involved in a simple cycle. + // + // At minimum, we need to mark the root vertex (alreadySeen[0].Destination) if the cycle includes the root and + // includes a flagged edge or a vertex that is itself known to be part of a flagged cycle. + // That's the primary answer our caller seeks. + // + // At minimum, we also need to stop recursing so that our caller gets an answer at all. + // + // Having said that, we are in a position to do more than the minimum since we may have + // discovered a flagged cycle involving vertices other than the root and are in a position to mark them. + // So we'll be nice and do that too as this will save work overall. + if (flagged) + { + while (idx < alreadySeen.Count) + { + alreadySeen[idx++].Destination.ProvedToBeInvolvedInAFlaggedCycle = true; + } + } + + return; + } + + bool allChildrenProvenNotPartOfCycle = true; + alreadySeen.Add(edge); // Push + foreach (Edge newEdge in vertex.Edges) + { + FindCyclesWorker(newEdge, alreadySeen, ref operationCount, previousAlgorithmTimeoutWatch); + allChildrenProvenNotPartOfCycle = allChildrenProvenNotPartOfCycle && newEdge.Destination.ProvedToBeNotPartOfAnyCycle; + } + alreadySeen.RemoveAt(alreadySeen.Count - 1); // Pop + + if (allChildrenProvenNotPartOfCycle) + vertex.ProvedToBeNotPartOfAnyCycle = true; + + return; + } + } + } +} + diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/Graph.Vertex.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/Graph.Vertex.cs new file mode 100644 index 00000000000000..1aaeaa191b6639 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/Graph.Vertex.cs @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +namespace ILCompiler +{ + internal static partial class LazyGenericsSupport + { + private sealed partial class Graph

+ { + ///

+ /// Gets or creates the canonical Vertex object for this payload value. Payload equality is defined by its Object.Equals() override. Vertex equality + /// is determined by reference equality. + /// + private Vertex GetVertex(P payload) + { + Vertex vertex; + if (_vertexMap.TryGetValue(payload, out vertex)) + return vertex; + vertex = new Vertex(payload); + _vertexMap.Add(payload, vertex); + return vertex; + } + + private sealed class Vertex + { + public Vertex(P payload) + { + this.Payload = payload; + this._edges = new List(); + return; + } + + public P Payload { get; private set; } + + public IEnumerable Edges { get { return _edges; } } + + public void AddEdge(Vertex toVertex, bool flagged) + { + for (int i = 0; i < _edges.Count; i++) + { + if (_edges[i].Destination.Equals(toVertex)) + { + if (flagged) + { + // Don't shorten to "_edge[i].Flagged = true" - that falls into the "update a compiler temp" gotcha. + Edge e = _edges[i]; + e.Flagged = true; + _edges[i] = e; + } + return; + } + } + Edge newEdge = new Edge(toVertex, flagged); + _edges.Add(newEdge); + return; + } + + + /// + /// If true, we have established that this vertex is part of a cycle in which at least one edge is flagged (abbreviated as "flagged cycle" + /// in the interests of brevity.) + /// If false, we have not yet established (but may yet establish) this fact. + /// + public bool ProvedToBeInvolvedInAFlaggedCycle; + + /// + /// If true, we have established that this vertex is not part of any cycle. + /// If false, we have not yet established (but may yet establish) this fact. + /// + public bool ProvedToBeNotPartOfAnyCycle; + + /// + /// Flag used during Tarjan's algorithm + /// + public bool OnStack = false; + + /// + /// Index used in Tarjan's algorithm + /// + public int Index = -1; + + /// + /// LowLink field for Tarjan's algorithm + /// + public int LowLink = -1; + + public sealed override String ToString() + { + return this.Payload.ToString(); + } + + private List _edges = new List(); + } + + private struct Edge + { + public Edge(Vertex destination, bool flagged) + { + this.Destination = destination; + this.Flagged = flagged; + return; + } + + public readonly Vertex Destination; + public bool Flagged; + + public override String ToString() + { + return "[" + (Flagged ? "==>" : "-->") + Destination + "]"; + } + } + + private Dictionary _vertexMap = new Dictionary(); + } + } +} + diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/Graph.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/Graph.cs new file mode 100644 index 00000000000000..12fcc14766cd4d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/Graph.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; + +namespace ILCompiler +{ + internal static partial class LazyGenericsSupport + { + /// + /// A weighted directed graph abstraction. For our purposes, we only use two weights, so our "weight" is a boolean: "Flagged" or "Not Flagged". + /// + /// The generic type "P" denotes the type that holds the payload data of graph vertices. Its overload of Object.Equals() is used + /// to determine whether two "P"'s represent the same vertex. + /// + private sealed partial class Graph

+ { + ///

+ /// Adds an edge from "from" to "to". If an edge already exists, the "flagged" value is merged (using boolean OR) into + /// the existing edge. + /// + public void AddEdge(P from, P to, bool flagged) + { + Vertex fromVertex = GetVertex(from); + Vertex toVertex = GetVertex(to); + fromVertex.AddEdge(toVertex, flagged); + return; + } + + public IEnumerable

Vertices + { + get + { + return this._vertexMap.Keys.ToArray(); + } + } + } + } +} + diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/GraphBuilder.ForEach.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/GraphBuilder.ForEach.cs new file mode 100644 index 00000000000000..5fa18aea5886ba --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/GraphBuilder.ForEach.cs @@ -0,0 +1,95 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + internal static partial class LazyGenericsSupport + { + private sealed partial class GraphBuilder + { + ///

+ /// Walk through the type expression and find any embedded generic parameter references. For each one found, + /// invoke the collector delegate with that generic parameter and a boolean indicate whether this is + /// a proper embedding (i.e. there is something actually nesting this.) + /// + /// Typically, the type expression is something that a generic type formal is being bound to, and we're + /// looking to see if another other generic type formals are referenced within that type expression. + /// + /// This method also records bindings for any generic instances it finds inside the tree expression. + /// Sometimes, this side-effect is all that's wanted - in such cases, invoke this method with a null collector. + /// + private void ForEachEmbeddedGenericFormal(TypeDesc typeExpression, Instantiation typeContext, Instantiation methodContext, System.Action collector = null) + { + System.Action wrappedCollector = + delegate(EcmaGenericParameter embedded, int depth) + { + bool isProperEmbedding = (depth > 0); + if (collector != null) + collector(embedded, isProperEmbedding); + return; + }; + ForEachEmbeddedGenericFormalWorker(typeExpression, typeContext, methodContext, wrappedCollector, depth: 0); + } + + private void ForEachEmbeddedGenericFormalWorker(TypeDesc type, Instantiation typeContext, Instantiation methodContext, System.Action collector, int depth) + { + switch (type.Category) + { + case TypeFlags.Array: + case TypeFlags.SzArray: + case TypeFlags.ByRef: + case TypeFlags.Pointer: + ForEachEmbeddedGenericFormalWorker(((ParameterizedType)type).ParameterType, typeContext, methodContext, collector, depth + 1); + return; + case TypeFlags.FunctionPointer: + return; + case TypeFlags.SignatureMethodVariable: + var methodParam = (EcmaGenericParameter)methodContext[((SignatureMethodVariable)type).Index]; + collector(methodParam, depth); + return; + case TypeFlags.SignatureTypeVariable: + var typeParam = (EcmaGenericParameter)typeContext[((SignatureTypeVariable)type).Index]; + collector(typeParam, depth); + return; + default: + Debug.Assert(type.IsDefType); + + // Non-constructed type. End of recursion. + if (!type.HasInstantiation || type.IsGenericDefinition) + return; + + TypeDesc genericTypeDefinition = type.GetTypeDefinition(); + Instantiation genericTypeParameters = genericTypeDefinition.Instantiation; + Instantiation genericTypeArguments = type.Instantiation; + for (int i = 0; i < genericTypeArguments.Length; i++) + { + var genericTypeParameter = (EcmaGenericParameter)genericTypeParameters[i]; + TypeDesc genericTypeArgument = genericTypeArguments[i]; + + int newDepth = depth + 1; + ForEachEmbeddedGenericFormalWorker( + genericTypeArgument, + typeContext, + methodContext, + delegate (EcmaGenericParameter embedded, int depth2) + { + collector(embedded, depth2); + bool isProperEmbedding = (depth2 > newDepth); + RecordBinding(genericTypeParameter, embedded, isProperEmbedding); + }, + newDepth + ); + } + return; + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/GraphBuilder.MethodCall.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/GraphBuilder.MethodCall.cs new file mode 100644 index 00000000000000..5ddd13772827a1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/GraphBuilder.MethodCall.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + internal static partial class LazyGenericsSupport + { + private sealed partial class GraphBuilder + { + /// + /// Found a method call inside a method body. Method calls may bind generic parameters of the target method. If so, + /// we have to record that fact. + /// + private void ProcessMethodCall(MethodDesc target, Instantiation typeContext, Instantiation methodContext) + { + if (!target.HasInstantiation) + return; + + // + // Collect all the generic parameters that have to be bound. There are two (non-mutually-exclusive) cases to consider: + // + // - The target method is declared on a generic type. + // - The target method is itself generic. + // + // We already took care of any generic type parameters in ProcessTypeReference. So we only need to handle + // any generic method parameters here. + // + + Instantiation genericTypeParameters = target.GetTypicalMethodDefinition().Instantiation; + Instantiation genericTypeArguments = target.Instantiation; + + Debug.Assert(genericTypeParameters.Length == genericTypeArguments.Length); + + // We've now collected all the generic parameters for the target and the generic arguments that the caller is binding them to. + // Recursively walk each generic argument to see if we're passing along one of the caller's generic parameters, and if so, + // are we embedding it into a more complex type. + for (int i = 0; i < genericTypeParameters.Length; i++) + { + ForEachEmbeddedGenericFormal( + genericTypeArguments[i], + typeContext, + methodContext, + delegate(EcmaGenericParameter embedded, bool isProperEmbedding) + { + // If we got here, we found a method with generic arity (either from itself or its declaring type or both) + // that invokes a generic method. The caller is binding one of the target's generic formals to a type expression + // involving one of the caller's own formals. + // + // e.g. + // + // void Caller() + // { + // Target>(); + // return; + // } + // + RecordBinding((EcmaGenericParameter)genericTypeParameters[i], embedded, isProperEmbedding); + } + ); + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/GraphBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/GraphBuilder.cs new file mode 100644 index 00000000000000..5cdf10665fa280 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/GraphBuilder.cs @@ -0,0 +1,274 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; + +using Internal.IL; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + internal static partial class LazyGenericsSupport + { + private sealed partial class GraphBuilder + { + public GraphBuilder(EcmaModule assembly) + { + _graph = new Graph(); + _metadataReader = assembly.MetadataReader; + + foreach (TypeDefinitionHandle typeHandle in _metadataReader.TypeDefinitions) + { + TypeDefinition typeDefinition = _metadataReader.GetTypeDefinition(typeHandle); + + // Only things that deal with some sort of genericness could form cycles. + // Do not look at any types/member where genericness is not involved. + // Do not even bother getting type system entities for those that are not generic. + bool isGenericType = typeDefinition.GetGenericParameters().Count > 0; + if (isGenericType) + { + try + { + var ecmaType = (EcmaType)assembly.GetObject(typeHandle); + WalkAncestorTypes(ecmaType); + } + catch (TypeSystemException) + { + } + } + + foreach (MethodDefinitionHandle methodHandle in typeDefinition.GetMethods()) + { + // We need to look at methods on generic types, or generic methods. + bool needsScanning = isGenericType; + + if (!needsScanning) + { + MethodDefinition methodDefinition = _metadataReader.GetMethodDefinition(methodHandle); + BlobReader sigBlob = _metadataReader.GetBlobReader(methodDefinition.Signature); + needsScanning = sigBlob.ReadSignatureHeader().IsGeneric; + } + + if (needsScanning) + { + try + { + var ecmaMethod = (EcmaMethod)assembly.GetObject(methodHandle); + WalkMethod(ecmaMethod); + } + catch (TypeSystemException) + { + } + } + } + } + return; + } + + public Graph Graph { get { return _graph; } } + + // Base types and interfaces. + private void WalkAncestorTypes(EcmaType declaringType) + { + TypeDesc baseType = declaringType.BaseType; + Instantiation typeContext = declaringType.Instantiation; + if (baseType != null) + { + ProcessAncestorType(baseType, typeContext); + } + foreach (DefType ifcType in declaringType.RuntimeInterfaces) + { + ProcessAncestorType(ifcType, typeContext); + } + } + + private void ProcessAncestorType(TypeDesc ancestorType, Instantiation typeContext) + { + ForEachEmbeddedGenericFormal(ancestorType, typeContext, Instantiation.Empty); + } + + private void WalkMethod(EcmaMethod method) + { + Instantiation typeContext = method.OwningType.Instantiation; + Instantiation methodContext = method.Instantiation; + + MethodSignature methodSig = method.Signature; + ProcessTypeReference(methodSig.ReturnType, typeContext, methodContext); + foreach (TypeDesc parameterType in methodSig) + { + ProcessTypeReference(parameterType, typeContext, methodContext); + } + + if (method.IsAbstract) + { + return; + } + + var methodIL = EcmaMethodIL.Create(method); + if (methodIL == null) + { + return; + } + + // If this is a generic virtual method, add an edge from each of the generic parameters + // of the implementation to the generic parameters of the declaration - any call to the + // declaration will be modeled as if the declaration was calling into the implementation. + if (method.IsVirtual && method.HasInstantiation) + { + var decl = (EcmaMethod)MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method).GetTypicalMethodDefinition(); + if (decl != method) + { + Instantiation declInstantiation = decl.Instantiation; + Instantiation implInstantiation = method.Instantiation; + for (int i = 0; i < declInstantiation.Length; i++) + { + RecordBinding( + (EcmaGenericParameter)implInstantiation[i], + (EcmaGenericParameter)declInstantiation[i], + isProperEmbedding: false); + } + } + } + + // Walk the method body looking at referenced things that have some genericness. + // Nongeneric things cannot be forming cycles. + // In particular, we don't care about MemberRefs to non-generic things, TypeDefs/MethodDefs/FieldDefs. + // Avoid the work to even materialize type system entities for those. + + ILReader reader = new ILReader(methodIL.GetILBytes()); + + while (reader.HasNext) + { + ILOpcode opcode = reader.ReadILOpcode(); + switch (opcode) + { + case ILOpcode.sizeof_: + case ILOpcode.newarr: + case ILOpcode.initobj: + case ILOpcode.stelem: + case ILOpcode.ldelem: + case ILOpcode.ldelema: + case ILOpcode.box: + case ILOpcode.unbox: + case ILOpcode.unbox_any: + case ILOpcode.cpobj: + case ILOpcode.ldobj: + case ILOpcode.castclass: + case ILOpcode.isinst: + case ILOpcode.stobj: + case ILOpcode.refanyval: + case ILOpcode.mkrefany: + case ILOpcode.constrained: + EntityHandle accessedType = MetadataTokens.EntityHandle(reader.ReadILToken()); + typeCase: + if (accessedType.Kind == HandleKind.TypeSpecification) + { + var t = methodIL.GetObject(MetadataTokens.GetToken(accessedType), NotFoundBehavior.ReturnNull) as TypeDesc; + if (t != null) + { + ProcessTypeReference(t, typeContext, methodContext); + } + } + break; + + case ILOpcode.stsfld: + case ILOpcode.ldsfld: + case ILOpcode.ldsflda: + case ILOpcode.stfld: + case ILOpcode.ldfld: + case ILOpcode.ldflda: + EntityHandle accessedField = MetadataTokens.EntityHandle(reader.ReadILToken()); + fieldCase: + if (accessedField.Kind == HandleKind.MemberReference) + { + accessedType = _metadataReader.GetMemberReference((MemberReferenceHandle)accessedField).Parent; + goto typeCase; + } + break; + + case ILOpcode.call: + case ILOpcode.callvirt: + case ILOpcode.newobj: + case ILOpcode.ldftn: + case ILOpcode.ldvirtftn: + case ILOpcode.jmp: + EntityHandle accessedMethod = MetadataTokens.EntityHandle(reader.ReadILToken()); + methodCase: + if (accessedMethod.Kind == HandleKind.MethodSpecification + || (accessedMethod.Kind == HandleKind.MemberReference + && _metadataReader.GetMemberReference((MemberReferenceHandle)accessedMethod).Parent.Kind == HandleKind.TypeSpecification)) + { + var m = methodIL.GetObject(MetadataTokens.GetToken(accessedMethod), NotFoundBehavior.ReturnNull) as MethodDesc; + ProcessTypeReference(m.OwningType, typeContext, methodContext); + ProcessMethodCall(m, typeContext, methodContext); + } + break; + + case ILOpcode.ldtoken: + EntityHandle accessedEntity = MetadataTokens.EntityHandle(reader.ReadILToken()); + if (accessedEntity.Kind == HandleKind.MethodSpecification + || (accessedEntity.Kind == HandleKind.MemberReference && _metadataReader.GetMemberReference((MemberReferenceHandle)accessedEntity).GetKind() == MemberReferenceKind.Method)) + { + accessedMethod = accessedEntity; + goto methodCase; + } + else if (accessedEntity.Kind == HandleKind.MemberReference) + { + accessedField = accessedEntity; + goto fieldCase; + } + else if (accessedEntity.Kind == HandleKind.TypeSpecification) + { + accessedType = accessedEntity; + goto typeCase; + } + break; + + default: + reader.Skip(opcode); + break; + } + } + } + + /// + /// Inside a method body, we found a reference to another type (e.g. ldtoken, or a member access.) + /// If the type is a generic instance, record any bindings between its formals and the referencer's + /// formals. + /// + private void ProcessTypeReference(TypeDesc typeReference, Instantiation typeContext, Instantiation methodContext) + { + ForEachEmbeddedGenericFormal(typeReference, typeContext, methodContext); + } + + /// + /// Records the fact that the type formal "receiver" is being bound to a type expression that references + /// "embedded." + /// + private void RecordBinding(EcmaGenericParameter receiver, EcmaGenericParameter embedded, bool isProperEmbedding) + { + bool flagged; + if (isProperEmbedding) + { + // If we got here, we have a potential codepath that binds "receiver" to a type expression involving "embedded" + // (and is not simply "embedded" itself.) + flagged = true; + } + else + { + // If we got here, we have a potential codepath that binds "receiver" to a type expression that is simply "embedded" + flagged = false; + } + + _graph.AddEdge(embedded, receiver, flagged); + + return; + } + + private Graph _graph; + private MetadataReader _metadataReader; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/ModuleCycleInfo.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/ModuleCycleInfo.cs new file mode 100644 index 00000000000000..47b1ce7e1d05c0 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/ModuleCycleInfo.cs @@ -0,0 +1,261 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + internal static partial class LazyGenericsSupport + { + private class ModuleCycleInfo + { + private readonly HashSet _entitiesInCycles; + + public IEnumerable EntitiesInCycles => _entitiesInCycles; + + public EcmaModule Module { get; } + + public ModuleCycleInfo(EcmaModule module, HashSet entitiesInCycles) + { + Module = module; + _entitiesInCycles = entitiesInCycles; + } + + public bool FormsCycle(TypeSystemEntity owner) + { + Debug.Assert(owner is EcmaMethod || owner is EcmaType); + TypeDesc ownerType = (owner as EcmaMethod)?.OwningType; + return _entitiesInCycles.Contains(owner) || (ownerType != null && _entitiesInCycles.Contains(ownerType)); + } + + // Chosen rather arbitrarily. For the app that I was looking at, cutoff point of 7 compiled + // more than 10 minutes on a release build of the compiler, and I lost patience. + // Cutoff point of 5 produced an 1.7 GB object file. + // Cutoff point of 4 produced an 830 MB object file. + // Cutoff point of 3 produced an 470 MB object file. + // We want this to be high enough so that it doesn't cut off too early. But also not too + // high because things that are recursive often end up expanding laterally as well + // through various other generic code the deep code calls into. + private const int CutoffPoint = 4; + + public bool IsDeepPossiblyCyclicInstantiation(TypeSystemEntity entity) + { + if (entity is TypeDesc type) + { + return IsDeepPossiblyCyclicInstantiation(type); + } + else + { + return IsDeepPossiblyCyclicInstantiation((MethodDesc)entity); + } + } + + public bool IsDeepPossiblyCyclicInstantiation(TypeDesc type, List seenTypes = null) + { + switch (type.Category) + { + case TypeFlags.Array: + case TypeFlags.SzArray: + return IsDeepPossiblyCyclicInstantiation(((ParameterizedType)type).ParameterType, seenTypes); + default: + TypeDesc typeDef = type.GetTypeDefinition(); + if (type != typeDef) + { + (seenTypes ??= new List()).Add(typeDef); + for (int i = 0; i < seenTypes.Count; i++) + { + TypeDesc typeToFind = seenTypes[i]; + int count = 1; + for (int j = i + 1; j < seenTypes.Count; j++) + { + if (seenTypes[j] == typeToFind) + { + count++; + } + + if (count > CutoffPoint) + { + return true; + } + } + } + + bool result = IsDeepPossiblyCyclicInstantiation(type.Instantiation, seenTypes); + seenTypes.RemoveAt(seenTypes.Count - 1); + return result; + } + return false; + } + } + + private bool IsDeepPossiblyCyclicInstantiation(Instantiation instantiation, List seenTypes = null) + { + foreach (TypeDesc arg in instantiation) + { + if (IsDeepPossiblyCyclicInstantiation(arg, seenTypes)) + { + return true; + } + } + + return false; + } + + public bool IsDeepPossiblyCyclicInstantiation(MethodDesc method) + { + return IsDeepPossiblyCyclicInstantiation(method.Instantiation) || IsDeepPossiblyCyclicInstantiation(method.OwningType); + } + } + + private class CycleInfoHashtable : LockFreeReaderHashtable + { + protected override bool CompareKeyToValue(EcmaModule key, ModuleCycleInfo value) => key == value.Module; + protected override bool CompareValueToValue(ModuleCycleInfo value1, ModuleCycleInfo value2) => value1.Module == value2.Module; + protected override int GetKeyHashCode(EcmaModule key) => key.GetHashCode(); + protected override int GetValueHashCode(ModuleCycleInfo value) => value.Module.GetHashCode(); + + protected override ModuleCycleInfo CreateValueFromKey(EcmaModule key) + { + GraphBuilder gb = new GraphBuilder(key); + Graph graph = gb.Graph; + + var formalsNeedingLazyGenerics = graph.ComputeVerticesInvolvedInAFlaggedCycle(); + var entitiesNeedingLazyGenerics = new HashSet(); + + foreach (EcmaGenericParameter formal in formalsNeedingLazyGenerics) + { + var formalDefinition = key.MetadataReader.GetGenericParameter(formal.Handle); + if (formal.Kind == GenericParameterKind.Type) + { + entitiesNeedingLazyGenerics.Add(key.GetType(formalDefinition.Parent)); + } + else + { + entitiesNeedingLazyGenerics.Add(key.GetMethod(formalDefinition.Parent)); + } + } + + return new ModuleCycleInfo(key, entitiesNeedingLazyGenerics); + } + } + + internal class GenericCycleDetector + { + private readonly CycleInfoHashtable _hashtable = new CycleInfoHashtable(); + + private readonly struct EntityPair : IEquatable + { + public readonly TypeSystemEntity Owner; + public readonly TypeSystemEntity Referent; + public EntityPair(TypeSystemEntity owner, TypeSystemEntity referent) + => (Owner, Referent) = (owner, referent); + public bool Equals(EntityPair other) => Owner == other.Owner && Referent == other.Referent; + public override bool Equals(object obj) => obj is EntityPair p && Equals(p); + public override int GetHashCode() => HashCode.Combine(Owner.GetHashCode(), Referent.GetHashCode()); + } + + // This is a set of entities that had actual problems that caused us to abort compilation + // somewhere. + // Would prefer this to be a ConcurrentHashSet but there isn't any. ModuleCycleInfo can be looked up + // from the key, but since this is a key/value pair, might as well use the value too... + private readonly ConcurrentDictionary _actualProblems = new ConcurrentDictionary(); + + public void DetectCycle(TypeSystemEntity owner, TypeSystemEntity referent) + { + // Not clear if generic recursion through fields is a thing + if (referent is FieldDesc) + { + return; + } + + var ownerType = owner as TypeDesc; + var ownerMethod = owner as MethodDesc; + var ownerDefinition = ownerType?.GetTypeDefinition() ?? (TypeSystemEntity)ownerMethod.GetTypicalMethodDefinition(); + var referentType = referent as TypeDesc; + var referentMethod = referent as MethodDesc; + var referentDefinition = referentType?.GetTypeDefinition() ?? (TypeSystemEntity)referentMethod.GetTypicalMethodDefinition(); + + // We don't track cycles in non-ecma entities. + if ((referentDefinition is not EcmaMethod && referentDefinition is not EcmaType) + || (ownerDefinition is not EcmaMethod && ownerDefinition is not EcmaType)) + { + return; + } + + EcmaModule ownerModule = (ownerDefinition as EcmaType)?.EcmaModule ?? ((EcmaMethod)ownerDefinition).Module; + + ModuleCycleInfo cycleInfo = _hashtable.GetOrCreateValue(ownerModule); + if (cycleInfo.FormsCycle(ownerDefinition)) + { + // Just the presence of a cycle is not a problem, but once we start getting too deep, + // we need to cut our losses. + if (cycleInfo.IsDeepPossiblyCyclicInstantiation(referent)) + { + _actualProblems.TryAdd(new EntityPair(owner, referent), cycleInfo); + + if (referentType != null) + { + // TODO: better exception string ID? + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, referentType); + } + else + { + // TODO: better exception string ID? + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, referentMethod); + } + } + } + } + + public void LogWarnings(Logger logger) + { + var problems = new List>(_actualProblems); + + // Might need to sort these if we care about warning determinism, but we probably don't. + + var reportedProblems = new HashSet(); + + foreach (var actualProblem in _actualProblems) + { + TypeSystemEntity referent = actualProblem.Key.Referent; + TypeSystemEntity owner = actualProblem.Key.Owner; + + TypeSystemEntity referentDefinition = referent is TypeDesc referentType ? referentType.GetTypeDefinition() + : ((MethodDesc)referent).GetTypicalMethodDefinition(); + TypeSystemEntity ownerDefinition = owner is TypeDesc ownerType ? ownerType.GetTypeDefinition() + : ((MethodDesc)owner).GetTypicalMethodDefinition(); + + if (!reportedProblems.Add(new EntityPair(ownerDefinition, referentDefinition))) + continue; + + string message = $"Generic expansion to '{actualProblem.Key.Referent.GetDisplayName()}' was aborted " + + "due to generic recursion. An exception will be thrown at runtime if this codepath is ever reached. " + + "Generic recursion also negatively affects compilation speed and the size of the compilation output. " + + "It is advisable to remove the source of the generic recursion by restructuring the program around " + + "the source of recursion. The source of generic recursion might include: "; + + ModuleCycleInfo cycleInfo = actualProblem.Value; + bool first = true; + foreach (TypeSystemEntity cycleEntity in cycleInfo.EntitiesInCycles) + { + if (!first) + message += ", "; + + first = false; + + message += $"'{cycleEntity.GetDisplayName()}'"; + } + + logger.LogWarning(message, 3054, actualProblem.Key.Owner, MessageSubCategory.AotAnalysis); + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenericsPolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenericsPolicy.cs new file mode 100644 index 00000000000000..73cd33dddee081 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenericsPolicy.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler +{ + public abstract class LazyGenericsPolicy + { + public abstract bool UsesLazyGenerics(MethodDesc method); + public abstract bool UsesLazyGenerics(TypeDesc type); + public abstract bool UsesLazyGenerics(MetadataType type); + } + + public sealed class AttributeDrivenLazyGenericsPolicy : LazyGenericsPolicy + { + public sealed override bool UsesLazyGenerics(MethodDesc method) + { + if (UsesLazyGenerics(method.OwningType)) + return true; + + if (method.HasInstantiation) + return method.IsVirtual || method.HasCustomAttribute("System.Runtime.CompilerServices", "ForceLazyDictionaryAttribute"); + + return false; + } + + public sealed override bool UsesLazyGenerics(TypeDesc type) + { + if (type is MetadataType) + return UsesLazyGenerics(type as MetadataType); + else + return false; + } + + public sealed override bool UsesLazyGenerics(MetadataType type) + { + return type.HasCustomAttribute("System.Runtime.CompilerServices", "ForceLazyDictionaryAttribute"); + } + } + + public sealed class LazyGenericsDisabledPolicy : LazyGenericsPolicy + { + public sealed override bool UsesLazyGenerics(MethodDesc method) => false; + public sealed override bool UsesLazyGenerics(TypeDesc type) => false; + public sealed override bool UsesLazyGenerics(MetadataType type) => false; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LibraryInitializers.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LibraryInitializers.cs new file mode 100644 index 00000000000000..2cb61913079760 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LibraryInitializers.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// Encapsulates a list of class constructors that must be run in a prescribed order during start-up + /// + public sealed class LibraryInitializers + { + private const string LibraryInitializerContainerNamespaceName = "Internal.Runtime.CompilerHelpers"; + private const string LibraryInitializerContainerTypeName = "LibraryInitializer"; + private const string LibraryInitializerMethodName = "InitializeLibrary"; + + private List _libraryInitializerMethods; + + private readonly TypeSystemContext _context; + private IReadOnlyCollection _librariesWithInitializers; + + public LibraryInitializers(TypeSystemContext context, IEnumerable librariesWithInitalizers) + { + _context = context; + _librariesWithInitializers = new List(librariesWithInitalizers); + } + + public IReadOnlyCollection LibraryInitializerMethods + { + get + { + if (_libraryInitializerMethods == null) + InitLibraryInitializers(); + + return _libraryInitializerMethods; + } + } + + private void InitLibraryInitializers() + { + Debug.Assert(_libraryInitializerMethods == null); + + _libraryInitializerMethods = new List(); + + foreach (var assembly in _librariesWithInitializers) + { + TypeDesc containingType = assembly.GetType(LibraryInitializerContainerNamespaceName, LibraryInitializerContainerTypeName, throwIfNotFound: false); + if (containingType == null) + continue; + + MethodDesc initializerMethod = containingType.GetMethod(LibraryInitializerMethodName, null); + if (initializerMethod == null) + continue; + + _libraryInitializerMethods.Add(initializerMethod); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LibraryRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LibraryRootProvider.cs new file mode 100644 index 00000000000000..35ef0b337ce89c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LibraryRootProvider.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem.Ecma; +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Provides compilation group for a library that compiles everything in the input IL module. + /// + public class LibraryRootProvider : ICompilationRootProvider + { + private EcmaModule _module; + + public LibraryRootProvider(EcmaModule module) + { + _module = module; + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + foreach (TypeDesc type in _module.GetAllTypes()) + { + try + { + rootProvider.AddCompilationRoot(type, "Library module type"); + } + catch (TypeSystemException) + { + // TODO: fail compilation if a switch was passed + + // Swallow type load exceptions while rooting + continue; + + // TODO: Log as a warning + } + + // If this is not a generic definition, root all methods + if (!type.HasInstantiation) + { + RootMethods(type, "Library module method", rootProvider); + rootProvider.RootThreadStaticBaseForType(type, "Library module type statics"); + rootProvider.RootGCStaticBaseForType(type, "Library module type statics"); + rootProvider.RootNonGCStaticBaseForType(type, "Library module type statics"); + } + } + } + + private void RootMethods(TypeDesc type, string reason, IRootingServiceProvider rootProvider) + { + foreach (MethodDesc method in type.GetAllMethods()) + { + // Skip methods with no IL and uninstantiated generic methods + if (method.IsAbstract || method.HasInstantiation) + continue; + + if (method.IsInternalCall) + continue; + + try + { + CheckCanGenerateMethod(method); + rootProvider.AddCompilationRoot(method, reason); + } + catch (TypeSystemException) + { + // TODO: fail compilation if a switch was passed + + // Individual methods can fail to load types referenced in their signatures. + // Skip them in library mode since they're not going to be callable. + continue; + + // TODO: Log as a warning + } + } + } + + /// + /// Validates that it will be possible to generate '' based on the types + /// in its signature. Unresolvable types in a method's signature prevent RyuJIT from generating + /// even a stubbed out throwing implementation. + /// + public static void CheckCanGenerateMethod(MethodDesc method) + { + MethodSignature signature = method.Signature; + + // Vararg methods are not supported in .NET Core + if ((signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) == MethodSignatureFlags.CallingConventionVarargs) + ThrowHelper.ThrowBadImageFormatException(); + + CheckTypeCanBeUsedInSignature(signature.ReturnType); + + for (int i = 0; i < signature.Length; i++) + { + CheckTypeCanBeUsedInSignature(signature[i]); + } + } + + private static void CheckTypeCanBeUsedInSignature(TypeDesc type) + { + MetadataType defType = type as MetadataType; + + if (defType != null) + { + defType.ComputeTypeContainsGCPointers(); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MainMethodRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MainMethodRootProvider.cs new file mode 100644 index 00000000000000..2d77ba865e6a25 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MainMethodRootProvider.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; +using Internal.IL.Stubs.StartupCode; + +namespace ILCompiler +{ + /// + /// Computes a compilation root based on the entrypoint of the assembly. + /// + public class MainMethodRootProvider : ICompilationRootProvider + { + /// + /// Symbolic name under which the managed entrypoint is exported. + /// + public const string ManagedEntryPointMethodName = "__managed__Main"; + + private EcmaModule _module; + private IReadOnlyCollection _libraryInitializers; + + public MainMethodRootProvider(EcmaModule module, IReadOnlyCollection libraryInitializers) + { + _module = module; + _libraryInitializers = libraryInitializers; + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + MethodDesc mainMethod = _module.EntryPoint; + if (mainMethod == null) + throw new Exception("No managed entrypoint defined for executable module"); + + TypeDesc owningType = _module.GetGlobalModuleType(); + var startupCodeMain = new StartupCodeMainMethod(owningType, mainMethod, _libraryInitializers); + + rootProvider.AddCompilationRoot(startupCodeMain, "Startup Code Main Method", ManagedEntryPointMethodName); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManagedBinaryEmitter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManagedBinaryEmitter.cs new file mode 100644 index 00000000000000..a705a68e903dd6 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManagedBinaryEmitter.cs @@ -0,0 +1,386 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Linq; +using System.Reflection; +using System.Collections.Generic; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.Metadata; +using System.Collections.Immutable; +using System.Reflection.PortableExecutable; +using System.IO; +using System.Diagnostics; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler +{ + public class ManagedBinaryEmitter + { + public class EmittedTypeDefinition + { + private readonly ManagedBinaryEmitter _managedEmitter; + private List _methods = new List(); + + public string Name { get; private set; } + public bool IsValueType { get; private set; } + public IReadOnlyList Methods => _methods; + + internal EmittedTypeDefinition(string name, bool isValueType, ManagedBinaryEmitter managedEmitter) + { + Name = name; + IsValueType = isValueType; + _managedEmitter = managedEmitter; + } + + public EmittedMethodDefinition EmitMethodDefinition(string name, MethodSignature signature) + { + var newMethodDef = new EmittedMethodDefinition(name, signature, _managedEmitter); + _methods.Add(newMethodDef); + return newMethodDef; + } + } + public class EmittedMethodDefinition + { + private readonly ManagedBinaryEmitter _managedEmitter; + + public string Name { get; private set; } + public MethodSignature Signature { get; private set; } + public InstructionEncoder Code { get; private set; } + + internal EmittedMethodDefinition(string name, MethodSignature signature, ManagedBinaryEmitter managedEmitter) + { + Name = name; + Signature = signature; + _managedEmitter = managedEmitter; + Code = new InstructionEncoder(new BlobBuilder()); + } + } + + private readonly TypeSystemContext _typeSystemContext; + + private MetadataBuilder _metadataBuilder; + private MethodBodyStreamEncoder _methodBodyStream; + private List _emittedTypes; + + protected MetadataBuilder Builder => _metadataBuilder; + + public ManagedBinaryEmitter(TypeSystemContext typeSystemContext, string assemblyName) + { + _typeSystemContext = typeSystemContext; + + _metadataBuilder = new MetadataBuilder(); + _methodBodyStream = new MethodBodyStreamEncoder(new BlobBuilder()); + _emittedTypes = new List(); + + _metadataBuilder.AddAssembly( + _metadataBuilder.GetOrAddString(assemblyName), + new Version(0, 0, 0, 0), + culture: default(StringHandle), + publicKey: default(BlobHandle), + flags: default(AssemblyFlags), + hashAlgorithm: AssemblyHashAlgorithm.None); + + _metadataBuilder.AddModule( + 0, + _metadataBuilder.GetOrAddString(assemblyName), + default(GuidHandle), default(GuidHandle), default(GuidHandle)); + + // Module type + _metadataBuilder.AddTypeDefinition( + default(TypeAttributes), + default(StringHandle), + _metadataBuilder.GetOrAddString(""), + baseType: default(EntityHandle), + fieldList: MetadataTokens.FieldDefinitionHandle(1), + methodList: MetadataTokens.MethodDefinitionHandle(1)); + + _signatureEmitter = new EcmaSignatureEncoder(new EntityProviderForEcmaSignature(this)); + } + + public EmittedTypeDefinition EmitTypeDefinition(string typeName, bool isValueType) + { + EmittedTypeDefinition newTypeDef = new EmittedTypeDefinition(typeName, isValueType, this); + _emittedTypes.Add(newTypeDef); + return newTypeDef; + } + + public EntityHandle EmitMetadataHandleForTypeSystemEntity(TypeSystemEntity entity) + { + switch (entity) + { + case FieldDesc field: return MakeMemberReferenceHandle(field); + case MethodDesc method: return MakeMemberReferenceHandle(method); + case TypeDesc type: return MakeTypeRefOrSpecHandle(type); + + default: + throw new NotSupportedException(); + } + } + + public BlobHandle EmitSignatureBlobForMethodSignature(MethodSignature signature) + { + return MakeSignatureHandle(signature); + } + + /// + /// Encode a type signature into a specified blob. + /// + /// Blob to encode type signature into. Must not be null + public void EncodeSignatureForType(TypeDesc type, BlobBuilder blobBuilder) + { + SignatureTypeEncoder sigEncoder = new SignatureTypeEncoder(blobBuilder); + _signatureEmitter.EncodeTypeSignature(sigEncoder, type); + } + + public void EmitOutputFile(string outputPath) + { + using (FileStream sw = new FileStream(outputPath, FileMode.Create, FileAccess.ReadWrite)) + { + EmitToStream(sw); + } + } + + public void EmitToStream(Stream stream) + { + foreach (var typeDef in _emittedTypes) + { + MethodDefinitionHandle? firstMethodHandle = null; + + foreach (var methodDef in typeDef.Methods) + { + int bodyOffset = _methodBodyStream.AddMethodBody(methodDef.Code); + + BlobHandle signature = MakeSignatureHandle(methodDef.Signature); + + MethodDefinitionHandle methodHandle = _metadataBuilder.AddMethodDefinition( + MethodAttributes.PrivateScope | MethodAttributes.Static, + MethodImplAttributes.IL | MethodImplAttributes.Managed, + _metadataBuilder.GetOrAddString(methodDef.Name), + signature, + bodyOffset, + parameterList: default(ParameterHandle)); + + if (firstMethodHandle == null) + firstMethodHandle = methodHandle; + } + + _metadataBuilder.AddTypeDefinition( + default(TypeAttributes), + default(StringHandle), + _metadataBuilder.GetOrAddString(typeDef.Name), + typeDef.IsValueType ? + MakeTypeRefHandle(_typeSystemContext.GetWellKnownType(WellKnownType.ValueType)) : + MakeTypeRefHandle(_typeSystemContext.GetWellKnownType(WellKnownType.Object)), + fieldList: MetadataTokens.FieldDefinitionHandle(1), + methodList: firstMethodHandle.Value); + } + + BlobBuilder peBlob = new BlobBuilder(); + new ManagedPEBuilder(PEHeaderBuilder.CreateLibraryHeader(), new MetadataRootBuilder(_metadataBuilder), _methodBodyStream.Builder).Serialize(peBlob); + + peBlob.WriteContentTo(stream); + + // Clear some variables to catch any caller trying to emit data after writing the output file + _emittedTypes = null; + _metadataBuilder = null; + _methodBodyStream = default(MethodBodyStreamEncoder); + } + + #region TypeSystem Entities To Handle Encoders + private Dictionary _assemblyRefHandles = new Dictionary(); + private Dictionary _typeRefOrSpecHandles = new Dictionary(); + private Dictionary _memberRefOrSpecHandles = new Dictionary(); + private Dictionary _methodSignatureHandles = new Dictionary(); + private Dictionary _fieldSignatureHandles = new Dictionary(); + + private struct EntityProviderForEcmaSignature : IEntityHandleProvider + { + private ManagedBinaryEmitter _emitter; + + public EntityProviderForEcmaSignature(ManagedBinaryEmitter emitter) + { + _emitter = emitter; + } + + public EntityHandle GetTypeDefOrRefHandleForTypeDesc(TypeDesc type) + { + return _emitter.MakeTypeRefHandle(type); + } + } + + private EcmaSignatureEncoder _signatureEmitter; + + private BlobHandle MakeSignatureHandle(MethodSignature signature) + { + BlobHandle handle; + + if (!_methodSignatureHandles.TryGetValue(signature, out handle)) + { + BlobBuilder metadataSignature = new BlobBuilder(); + + _signatureEmitter.EncodeMethodSignature(metadataSignature, signature); + + _methodSignatureHandles[signature] = handle = _metadataBuilder.GetOrAddBlob(metadataSignature); + } + + return handle; + } + + private BlobHandle MakeSignatureHandle(TypeSystemEntity methodOrField) + { + if (methodOrField is MethodDesc) + { + return MakeSignatureHandle(((MethodDesc)methodOrField).Signature); + } + else + { + BlobHandle handle; + FieldDesc field = (FieldDesc)methodOrField; + + if (!_fieldSignatureHandles.TryGetValue(field, out handle)) + { + BlobBuilder metadataSignature = new BlobBuilder(); + + SignatureTypeEncoder fieldSigEncoder = new BlobEncoder(metadataSignature).FieldSignature(); + _signatureEmitter.EncodeTypeSignature(fieldSigEncoder, field.FieldType); + + _fieldSignatureHandles[field] = handle = _metadataBuilder.GetOrAddBlob(metadataSignature); + } + + return handle; + } + } + + private AssemblyReferenceHandle MakeAssemblyReferenceHandle(IAssemblyDesc assemblyRef) + { + AssemblyReferenceHandle handle; + + if (!_assemblyRefHandles.TryGetValue(assemblyRef, out handle)) + { + AssemblyName assemblyName = assemblyRef.GetName(); + + handle = _metadataBuilder.AddAssemblyReference( + _metadataBuilder.GetOrAddString(assemblyName.Name), + assemblyName.Version, + default(StringHandle), + _metadataBuilder.GetOrAddBlob(ImmutableArray.Create(assemblyName.GetPublicKeyToken())), + default(AssemblyFlags), + default(BlobHandle)); + + _assemblyRefHandles[assemblyRef] = handle; + } + + return handle; + } + + private EntityHandle MakeTypeRefHandle(TypeDesc type) + { + Debug.Assert(type.IsTypeDefinition); + Debug.Assert(type is MetadataType); + + EntityHandle handle; + + if (!_typeRefOrSpecHandles.TryGetValue(type, out handle)) + { + EntityHandle scope; + MetadataType typeAsMetadataType = (MetadataType)type; + + if (typeAsMetadataType.ContainingType != null) + scope = MakeTypeRefHandle(typeAsMetadataType.ContainingType); + else + scope = MakeAssemblyReferenceHandle((IAssemblyDesc)typeAsMetadataType.Module); + + handle = _metadataBuilder.AddTypeReference( + scope, + _metadataBuilder.GetOrAddString(typeAsMetadataType.Namespace), + _metadataBuilder.GetOrAddString(typeAsMetadataType.Name)); + + _typeRefOrSpecHandles[type] = handle; + } + + return handle; + } + + private EntityHandle MakeTypeRefOrSpecHandle(TypeDesc type) + { + EntityHandle handle; + + if (!_typeRefOrSpecHandles.TryGetValue(type, out handle)) + { + if(!type.IsDefType || !type.IsTypeDefinition || type is RuntimeDeterminedType) + { + SignatureTypeEncoder sigEncoder = new SignatureTypeEncoder(new BlobBuilder()); + _signatureEmitter.EncodeTypeSignature(sigEncoder, type); + handle = _metadataBuilder.AddTypeSpecification(_metadataBuilder.GetOrAddBlob(sigEncoder.Builder)); + } + else + { + handle = MakeTypeRefHandle(type); + } + + _typeRefOrSpecHandles[type] = handle; + } + + return handle; + } + + private EntityHandle MakeMemberReferenceHandle(TypeSystemEntity methodOrField) + { + EntityHandle handle; + + if (!_memberRefOrSpecHandles.TryGetValue(methodOrField, out handle)) + { + MethodDesc method = methodOrField as MethodDesc; + FieldDesc field = methodOrField as FieldDesc; + TypeDesc owningType = (method != null ? method.OwningType : field.OwningType); + string name = (method != null ? method.Name : field.Name); + + BlobHandle signature = method != null ? + MakeSignatureHandle(method.GetTypicalMethodDefinition()) : + MakeSignatureHandle(field); + + handle = _metadataBuilder.AddMemberReference( + MakeTypeRefOrSpecHandle(owningType), + _metadataBuilder.GetOrAddString(name), + signature); + + if (method != null && method.HasInstantiation && !method.IsTypicalMethodDefinition) + { + BlobEncoder methodSpecEncoder = new BlobEncoder(new BlobBuilder()); + + GenericTypeArgumentsEncoder argEncoder = methodSpecEncoder.MethodSpecificationSignature(method.Instantiation.Length); + for (int i = 0; i < method.Instantiation.Length; i++) + { + SignatureTypeEncoder argTypeEncoder = argEncoder.AddArgument(); + _signatureEmitter.EncodeTypeSignature(argTypeEncoder, method.Instantiation[i]); + } + + handle = _metadataBuilder.AddMethodSpecification(handle, _metadataBuilder.GetOrAddBlob(methodSpecEncoder.Builder)); + } + + _memberRefOrSpecHandles[methodOrField] = handle; + } + + return handle; + } + #endregion + } + + public static class InstructionEncoderExtensions + { + public static void EmitLdToken(this InstructionEncoder code, TypeSystemEntity typeSystemEntity, ManagedBinaryEmitter emitter) + { + code.OpCode(ILOpCode.Ldtoken); + code.Token(emitter.EmitMetadataHandleForTypeSystemEntity(typeSystemEntity)); + } + public static void EmitI4Constant(this InstructionEncoder code, int value) + { + code.OpCode(ILOpCode.Ldc_i4); + code.CodeBuilder.WriteInt32(value); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManifestResourceBlockingPolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManifestResourceBlockingPolicy.cs new file mode 100644 index 00000000000000..f9df59bfa816fb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManifestResourceBlockingPolicy.cs @@ -0,0 +1,136 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using System.Xml; + +using Internal.TypeSystem; + +using EcmaModule = Internal.TypeSystem.Ecma.EcmaModule; + +namespace ILCompiler +{ + /// + /// Represents a manifest resource blocking policy. The policy dictates whether manifest resources should + /// be generated into the executable. + /// + public class ManifestResourceBlockingPolicy + { + private readonly FeatureSwitchHashtable _hashtable; + + protected ManifestResourceBlockingPolicy() { } + + public ManifestResourceBlockingPolicy(IEnumerable> switchValues) + { + _hashtable = new FeatureSwitchHashtable(new Dictionary(switchValues)); + } + + /// + /// Returns true if manifest resource with name '' in module '' + /// is reflection blocked. + /// + public virtual bool IsManifestResourceBlocked(ModuleDesc module, string resourceName) + { + return module is EcmaModule ecmaModule && + (_hashtable.GetOrCreateValue(ecmaModule).BlockedResources.Contains(resourceName) + || (resourceName.StartsWith("ILLink.") && resourceName.EndsWith(".xml"))); + } + + private class FeatureSwitchHashtable : LockFreeReaderHashtable + { + private readonly Dictionary _switchValues; + + public FeatureSwitchHashtable(Dictionary switchValues) + { + _switchValues = switchValues; + } + + protected override bool CompareKeyToValue(EcmaModule key, AssemblyFeatureInfo value) => key == value.Module; + protected override bool CompareValueToValue(AssemblyFeatureInfo value1, AssemblyFeatureInfo value2) => value1.Module == value2.Module; + protected override int GetKeyHashCode(EcmaModule key) => key.GetHashCode(); + protected override int GetValueHashCode(AssemblyFeatureInfo value) => value.Module.GetHashCode(); + + protected override AssemblyFeatureInfo CreateValueFromKey(EcmaModule key) + { + return new AssemblyFeatureInfo(key, _switchValues); + } + } + + private class AssemblyFeatureInfo + { + public EcmaModule Module { get; } + + public HashSet BlockedResources { get; } + + public AssemblyFeatureInfo(EcmaModule module, IReadOnlyDictionary featureSwitchValues) + { + Module = module; + BlockedResources = new HashSet(); + + PEMemoryBlock resourceDirectory = module.PEReader.GetSectionData(module.PEReader.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress); + + foreach (var resourceHandle in module.MetadataReader.ManifestResources) + { + ManifestResource resource = module.MetadataReader.GetManifestResource(resourceHandle); + + // Don't try to process linked resources or resources in other assemblies + if (!resource.Implementation.IsNil) + { + continue; + } + + string resourceName = module.MetadataReader.GetString(resource.Name); + if (resourceName == "ILLink.Substitutions.xml") + { + BlobReader reader = resourceDirectory.GetReader((int)resource.Offset, resourceDirectory.Length - (int)resource.Offset); + int length = (int)reader.ReadUInt32(); + + UnmanagedMemoryStream ms; + unsafe + { + ms = new UnmanagedMemoryStream(reader.CurrentPointer, length); + } + + BlockedResources = SubstitutionsReader.GetSubstitutions(module.Context, XmlReader.Create(ms), module, featureSwitchValues); + } + } + } + } + + private class SubstitutionsReader : ProcessLinkerXmlBase + { + private readonly HashSet _substitutions = new HashSet(); + + private SubstitutionsReader(TypeSystemContext context, XmlReader reader, ModuleDesc module, IReadOnlyDictionary featureSwitchValues) + : base(context, reader, module, featureSwitchValues) + { + } + + public static HashSet GetSubstitutions(TypeSystemContext context, XmlReader reader, ModuleDesc module, IReadOnlyDictionary featureSwitchValues) + { + var rdr = new SubstitutionsReader(context, reader, module, featureSwitchValues); + rdr.ProcessXml(); + return rdr._substitutions; + } + + protected override void ProcessResource(ModuleDesc module) + { + if (GetAttribute("action") == "remove") + { + _substitutions.Add(GetAttribute("name")); + } + } + } + } + + public class FullyBlockedManifestResourceBlockingPolicy : ManifestResourceBlockingPolicy + { + public override bool IsManifestResourceBlocked(ModuleDesc module, string resourceName) + { + return true; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBlockingPolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBlockingPolicy.cs new file mode 100644 index 00000000000000..e1663faea99013 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBlockingPolicy.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// Represents a metadata blocking policy. A metadata blocking policy decides what types or members are never + /// eligible to have their metadata generated into the executable. + /// + public abstract class MetadataBlockingPolicy + { + /// + /// Returns true if type definition '' is reflection blocked. + /// + public abstract bool IsBlocked(MetadataType type); + + /// + /// Returns true if method definition '' is reflection blocked. + /// + public abstract bool IsBlocked(MethodDesc method); + + /// + /// Returns true if field definition '' is reflection blocked. + /// + public abstract bool IsBlocked(FieldDesc field); + } + + public class FullyBlockedMetadataBlockingPolicy : MetadataBlockingPolicy + { + public override bool IsBlocked(MetadataType type) + { + Debug.Assert(type.IsTypeDefinition); + return true; + } + + public override bool IsBlocked(MethodDesc method) + { + Debug.Assert(method.IsTypicalMethodDefinition); + return true; + } + + public override bool IsBlocked(FieldDesc field) + { + Debug.Assert(field.IsTypicalFieldDefinition); + return true; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs new file mode 100644 index 00000000000000..837171d9059c1c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -0,0 +1,876 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +using Debug = System.Diagnostics.Debug; +using ReadyToRunSectionType = Internal.Runtime.ReadyToRunSectionType; +using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using CombinedDependencyList = System.Collections.Generic.List.CombinedDependencyListEntry>; +using MethodIL = Internal.IL.MethodIL; +using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue; + +using MetadataRecord = Internal.Metadata.NativeFormat.Writer.MetadataRecord; +using MemberReference = Internal.Metadata.NativeFormat.Writer.MemberReference; +using TypeReference = Internal.Metadata.NativeFormat.Writer.TypeReference; +using TypeSpecification = Internal.Metadata.NativeFormat.Writer.TypeSpecification; +using ConstantStringValue = Internal.Metadata.NativeFormat.Writer.ConstantStringValue; +using TypeInstantiationSignature = Internal.Metadata.NativeFormat.Writer.TypeInstantiationSignature; +using MethodInstantiation = Internal.Metadata.NativeFormat.Writer.MethodInstantiation; + +namespace ILCompiler +{ + /// + /// This class is responsible for managing native metadata to be emitted into the compiled + /// module. It also helps facilitate mappings between generated runtime structures or code, + /// and the native metadata. + /// + public abstract class MetadataManager : ICompilationRootProvider + { + internal const int MetadataOffsetMask = 0xFFFFFF; + + private byte[] _metadataBlob; + private List> _typeMappings; + private List> _fieldMappings; + private List> _methodMappings; + private List> _stackTraceMappings; + + protected readonly CompilerTypeSystemContext _typeSystemContext; + protected readonly MetadataBlockingPolicy _blockingPolicy; + protected readonly ManifestResourceBlockingPolicy _resourceBlockingPolicy; + protected readonly DynamicInvokeThunkGenerationPolicy _dynamicInvokeThunkGenerationPolicy; + + private List _cctorContextsGenerated = new List(); + private readonly HashSet _typesWithEETypesGenerated = new HashSet(); + private readonly HashSet _typesWithConstructedEETypesGenerated = new HashSet(); + private HashSet _methodsGenerated = new HashSet(); + private HashSet _reflectableMethods = new HashSet(); + private HashSet _genericDictionariesGenerated = new HashSet(); + private HashSet _methodBodiesGenerated = new HashSet(); + private List _typeGVMEntries = new List(); + private HashSet _typesWithDelegateMarshalling = new HashSet(); + private HashSet _typesWithStructMarshalling = new HashSet(); + private HashSet _dynamicInvokeTemplates = new HashSet(); + private HashSet _templateMethodEntries = new HashSet(); + + internal NativeLayoutInfoNode NativeLayoutInfo { get; private set; } + internal DynamicInvokeTemplateDataNode DynamicInvokeTemplateData { get; private set; } + + public MetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, + ManifestResourceBlockingPolicy resourceBlockingPolicy, DynamicInvokeThunkGenerationPolicy dynamicInvokeThunkGenerationPolicy) + { + _typeSystemContext = typeSystemContext; + _blockingPolicy = blockingPolicy; + _resourceBlockingPolicy = resourceBlockingPolicy; + _dynamicInvokeThunkGenerationPolicy = dynamicInvokeThunkGenerationPolicy; + } + + public void AttachToDependencyGraph(DependencyAnalyzerBase graph) + { + graph.NewMarkedNode += Graph_NewMarkedNode; + } + + internal static ReadyToRunSectionType BlobIdToReadyToRunSection(ReflectionMapBlob blobId) + { + var result = (ReadyToRunSectionType)((int)blobId + (int)ReadyToRunSectionType.ReadonlyBlobRegionStart); + Debug.Assert(result <= ReadyToRunSectionType.ReadonlyBlobRegionEnd); + return result; + } + + public virtual void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) + { + var metadataNode = new MetadataNode(); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.EmbeddedMetadata), metadataNode, metadataNode, metadataNode.EndSymbol); + + var nativeReferencesTableNode = new ExternalReferencesTableNode("NativeReferences", nodeFactory); + var nativeStaticsTableNode = new ExternalReferencesTableNode("NativeStatics", nodeFactory); + + var resourceDataNode = new ResourceDataNode(); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdResourceData), resourceDataNode, resourceDataNode, resourceDataNode.EndSymbol); + + var resourceIndexNode = new ResourceIndexNode(resourceDataNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdResourceIndex), resourceIndexNode, resourceIndexNode, resourceIndexNode.EndSymbol); + + var typeMapNode = new TypeMetadataMapNode(commonFixupsTableNode); + + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.TypeMap), typeMapNode, typeMapNode, typeMapNode.EndSymbol); + + var cctorContextMapNode = new ClassConstructorContextMap(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CCtorContextMap), cctorContextMapNode, cctorContextMapNode, cctorContextMapNode.EndSymbol); + + DynamicInvokeTemplateData = new DynamicInvokeTemplateDataNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.DynamicInvokeTemplateData), DynamicInvokeTemplateData, DynamicInvokeTemplateData, DynamicInvokeTemplateData.EndSymbol); + + var invokeMapNode = new ReflectionInvokeMapNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.InvokeMap), invokeMapNode, invokeMapNode, invokeMapNode.EndSymbol); + + var arrayMapNode = new ArrayMapNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ArrayMap), arrayMapNode, arrayMapNode, arrayMapNode.EndSymbol); + + var fieldMapNode = new ReflectionFieldMapNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.FieldAccessMap), fieldMapNode, fieldMapNode, fieldMapNode.EndSymbol); + + NativeLayoutInfo = new NativeLayoutInfoNode(nativeReferencesTableNode, nativeStaticsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeLayoutInfo), NativeLayoutInfo, NativeLayoutInfo, NativeLayoutInfo.EndSymbol); + + var exactMethodInstantiations = new ExactMethodInstantiationsNode(nativeReferencesTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ExactMethodInstantiationsHashtable), exactMethodInstantiations, exactMethodInstantiations, exactMethodInstantiations.EndSymbol); + + var genericsTypesHashtableNode = new GenericTypesHashtableNode(nativeReferencesTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericsHashtable), genericsTypesHashtableNode, genericsTypesHashtableNode, genericsTypesHashtableNode.EndSymbol); + + var genericMethodsHashtableNode = new GenericMethodsHashtableNode(nativeReferencesTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericMethodsHashtable), genericMethodsHashtableNode, genericMethodsHashtableNode, genericMethodsHashtableNode.EndSymbol); + + var genericVirtualMethodTableNode = new GenericVirtualMethodTableNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericVirtualMethodTable), genericVirtualMethodTableNode, genericVirtualMethodTableNode, genericVirtualMethodTableNode.EndSymbol); + + var interfaceGenericVirtualMethodTableNode = new InterfaceGenericVirtualMethodTableNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.InterfaceGenericVirtualMethodTable), interfaceGenericVirtualMethodTableNode, interfaceGenericVirtualMethodTableNode, interfaceGenericVirtualMethodTableNode.EndSymbol); + + var genericMethodsTemplatesMapNode = new GenericMethodsTemplateMap(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericMethodsTemplateMap), genericMethodsTemplatesMapNode, genericMethodsTemplatesMapNode, genericMethodsTemplatesMapNode.EndSymbol); + + var genericTypesTemplatesMapNode = new GenericTypesTemplateMap(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.TypeTemplateMap), genericTypesTemplatesMapNode, genericTypesTemplatesMapNode, genericTypesTemplatesMapNode.EndSymbol); + + var blockReflectionTypeMapNode = new BlockReflectionTypeMapNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlockReflectionTypeMap), blockReflectionTypeMapNode, blockReflectionTypeMapNode, blockReflectionTypeMapNode.EndSymbol); + + var staticsInfoHashtableNode = new StaticsInfoHashtableNode(nativeReferencesTableNode, nativeStaticsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.StaticsInfoHashtable), staticsInfoHashtableNode, staticsInfoHashtableNode, staticsInfoHashtableNode.EndSymbol); + + var virtualInvokeMapNode = new ReflectionVirtualInvokeMapNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.VirtualInvokeMap), virtualInvokeMapNode, virtualInvokeMapNode, virtualInvokeMapNode.EndSymbol); + + var defaultConstructorMapNode = new DefaultConstructorMapNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.DefaultConstructorMap), defaultConstructorMapNode, defaultConstructorMapNode, defaultConstructorMapNode.EndSymbol); + + var stackTraceMethodMappingNode = new StackTraceMethodMappingNode(); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdStackTraceMethodRvaToTokenMapping), stackTraceMethodMappingNode, stackTraceMethodMappingNode, stackTraceMethodMappingNode.EndSymbol); + + // The external references tables should go last + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), nativeReferencesTableNode, nativeReferencesTableNode, nativeReferencesTableNode.EndSymbol); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeStatics), nativeStaticsTableNode, nativeStaticsTableNode, nativeStaticsTableNode.EndSymbol); + } + + protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) + { + var eetypeNode = obj as EETypeNode; + if (eetypeNode != null) + { + _typesWithEETypesGenerated.Add(eetypeNode.Type); + + if (eetypeNode is ConstructedEETypeNode || eetypeNode is CanonicalEETypeNode) + { + _typesWithConstructedEETypesGenerated.Add(eetypeNode.Type); + } + + return; + } + + IMethodBodyNode methodBodyNode = obj as IMethodBodyNode; + if (methodBodyNode != null) + { + _methodBodiesGenerated.Add(methodBodyNode); + } + + IMethodNode methodNode = methodBodyNode; + if (methodNode == null) + methodNode = obj as ShadowConcreteMethodNode; + + if (methodNode != null) + { + _methodsGenerated.Add(methodNode.Method); + + if (AllMethodsCanBeReflectable) + _reflectableMethods.Add(methodNode.Method); + + return; + } + + var reflectableMethodNode = obj as ReflectableMethodNode; + if (reflectableMethodNode != null) + { + _reflectableMethods.Add(reflectableMethodNode.Method); + } + + var nonGcStaticSectionNode = obj as NonGCStaticsNode; + if (nonGcStaticSectionNode != null && nonGcStaticSectionNode.HasCCtorContext) + { + _cctorContextsGenerated.Add(nonGcStaticSectionNode); + } + + var gvmEntryNode = obj as TypeGVMEntriesNode; + if (gvmEntryNode != null) + { + _typeGVMEntries.Add(gvmEntryNode); + } + + var dictionaryNode = obj as GenericDictionaryNode; + if (dictionaryNode != null) + { + _genericDictionariesGenerated.Add(dictionaryNode); + } + + if (obj is StructMarshallingDataNode structMarshallingDataNode) + { + _typesWithStructMarshalling.Add(structMarshallingDataNode.Type); + } + + if (obj is DelegateMarshallingDataNode delegateMarshallingDataNode) + { + _typesWithDelegateMarshalling.Add(delegateMarshallingDataNode.Type); + } + + if (obj is DynamicInvokeTemplateNode dynamicInvokeTemplate) + { + _dynamicInvokeTemplates.Add(dynamicInvokeTemplate.Method); + } + + if (obj is NativeLayoutTemplateMethodSignatureVertexNode templateMethodEntry) + { + _templateMethodEntries.Add(templateMethodEntry); + } + } + + protected virtual bool AllMethodsCanBeReflectable => false; + + /// + /// Is a method that is reflectable a method which should be placed into the invoke map as invokable? + /// + public virtual bool IsReflectionInvokable(MethodDesc method) + { + return Internal.IL.Stubs.DynamicInvokeMethodThunk.SupportsSignature(method.Signature) + && IsMethodSupportedInReflectionInvoke(method); + } + + public static bool IsMethodSupportedInReflectionInvoke(MethodDesc method) + { + TypeDesc owningType = method.OwningType; + + // Methods on nullable are special cased in the runtime reflection + if (owningType.IsNullable) + return false; + + // Methods on arrays are special cased in the runtime reflection + if (owningType.IsArray) + return false; + + // Finalizers are not reflection invokable + if (method.IsFinalizer) + return false; + + // Static constructors are not reflection invokable + if (method.IsStaticConstructor) + return false; + + if (method.IsConstructor) + { + // Delegate construction is only allowed through specific IL sequences + if (owningType.IsDelegate) + return false; + + // String constructors are intrinsic and special cased in runtime reflection + if (owningType.IsString) + return false; + } + + // Everything else can go in the mapping table. + return true; + } + + /// + /// Is there a reflection invoke stub for a method that is invokable? + /// + public bool HasReflectionInvokeStub(MethodDesc method) + { + if (!IsReflectionInvokable(method)) + return false; + + return HasReflectionInvokeStubForInvokableMethod(method); + } + + /// + /// Is there a reflection invoke stub for a method that is invokable? + /// + public bool ShouldMethodBeInInvokeMap(MethodDesc method) + { + // The current format requires us to have an MethodTable for the owning type. We might want to lift this. + if (!TypeGeneratesEEType(method.OwningType)) + return false; + + // We have a method body, we have a metadata token, but we can't get an invoke stub. Bail. + if (!IsReflectionInvokable(method)) + return false; + + return true; + } + + /// + /// This method is an extension point that can provide additional metadata-based dependencies to compiled method bodies. + /// + public void GetDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + MetadataCategory category = GetMetadataCategory(method); + + if ((category & MetadataCategory.Description) != 0) + { + GetMetadataDependenciesDueToReflectability(ref dependencies, factory, method); + } + + if ((category & MetadataCategory.RuntimeMapping) != 0) + { + if (IsReflectionInvokable(method)) + { + // We're going to generate a mapping table entry for this. Collect dependencies. + ReflectionInvokeMapNode.AddDependenciesDueToReflectability(ref dependencies, factory, method); + } + } + } + + /// + /// This method is an extension point that can provide additional metadata-based dependencies on a virtual method. + /// + public virtual void GetDependenciesDueToVirtualMethodReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + } + + protected virtual void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + // MetadataManagers can override this to provide additional dependencies caused by the emission of metadata + // (E.g. dependencies caused by the method having custom attributes applied to it: making sure we compile the attribute constructor + // and property setters) + } + + /// + /// This method is an extension point that can provide additional metadata-based dependencies to generated EETypes. + /// + public void GetDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + MetadataCategory category = GetMetadataCategory(type); + + if ((category & MetadataCategory.Description) != 0) + { + GetMetadataDependenciesDueToReflectability(ref dependencies, factory, type); + } + + if ((category & MetadataCategory.RuntimeMapping) != 0) + { + // We're going to generate a mapping table entry for this. Collect dependencies. + + // Nothing special is needed for the mapping table (we only emit the MethodTable and we already + // have one, since we got this callback). But check if a child wants to do something extra. + GetRuntimeMappingDependenciesDueToReflectability(ref dependencies, factory, type); + } + + GetDependenciesDueToEETypePresence(ref dependencies, factory, type); + } + + protected virtual void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + // MetadataManagers can override this to provide additional dependencies caused by the emission of metadata + // (E.g. dependencies caused by the type having custom attributes applied to it: making sure we compile the attribute constructor + // and property setters) + } + + protected virtual void GetRuntimeMappingDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + // MetadataManagers can override this to provide additional dependencies caused by the emission of a runtime + // mapping for a type. + } + + protected virtual void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + // MetadataManagers can override this to provide additional dependencies caused by the emission of an MethodTable. + } + + public virtual void GetConditionalDependenciesDueToEETypePresence(ref CombinedDependencyList dependencies, NodeFactory factory, TypeDesc type) + { + // MetadataManagers can override this to provide additional dependencies caused by the presence of + // an MethodTable. + } + + public virtual bool HasConditionalDependenciesDueToEETypePresence(TypeDesc type) + { + return false; + } + + /// + /// This method is an extension point that can provide additional metadata-based dependencies to generated RuntimeMethodHandles. + /// + public virtual void GetDependenciesDueToLdToken(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + // MetadataManagers can override this to provide additional dependencies caused by the presence of a + // RuntimeMethodHandle data structure. + } + + /// + /// This method is an extension point that can provide additional metadata-based dependencies to generated RuntimeFieldHandles. + /// + public virtual void GetDependenciesDueToLdToken(ref DependencyList dependencies, NodeFactory factory, FieldDesc field) + { + // MetadataManagers can override this to provide additional dependencies caused by the presence of a + // RuntimeFieldHandle data structure. + } + + /// + /// This method is an extension point that can provide additional metadata-based dependencies to generated method bodies. + /// + public void GetDependenciesDueToMethodCodePresence(ref DependencyList dependencies, NodeFactory factory, MethodDesc method, MethodIL methodIL) + { + if (method.HasInstantiation) + { + ExactMethodInstantiationsNode.GetExactMethodInstantiationDependenciesForMethod(ref dependencies, factory, method); + } + + GetDependenciesDueToTemplateTypeLoader(ref dependencies, factory, method); + GetDependenciesDueToMethodCodePresenceInternal(ref dependencies, factory, method, methodIL); + } + + public virtual void GetConditionalDependenciesDueToMethodCodePresence(ref CombinedDependencyList dependencies, NodeFactory factory, MethodDesc method) + { + // MetadataManagers can override this to provide additional dependencies caused by the presence of + // method code. + } + + private void GetDependenciesDueToTemplateTypeLoader(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + // TODO-SIZE: this is overly generous in the templates we create + if (_blockingPolicy is FullyBlockedMetadataBlockingPolicy) + return; + + if (method.HasInstantiation) + { + GenericMethodsTemplateMap.GetTemplateMethodDependencies(ref dependencies, factory, method); + } + else + { + TypeDesc owningTemplateType = method.OwningType; + + // Unboxing and Instantiating stubs use a different type as their template + if (factory.TypeSystemContext.IsSpecialUnboxingThunk(method)) + owningTemplateType = factory.TypeSystemContext.GetTargetOfSpecialUnboxingThunk(method).OwningType; + + GenericTypesTemplateMap.GetTemplateTypeDependencies(ref dependencies, factory, owningTemplateType); + } + } + + protected virtual void GetDependenciesDueToMethodCodePresenceInternal(ref DependencyList dependencies, NodeFactory factory, MethodDesc method, MethodIL methodIL) + { + // MetadataManagers can override this to provide additional dependencies caused by the presence of a + // compiled method body. + } + + /// + /// Given that a method is invokable, does there exist a reflection invoke stub? + /// + public bool HasReflectionInvokeStubForInvokableMethod(MethodDesc method) + { + Debug.Assert(IsReflectionInvokable(method)); + return _dynamicInvokeThunkGenerationPolicy.HasStaticInvokeThunk(method); + } + + /// + /// Given that a method is invokable, if it is inserted into the reflection invoke table + /// will it use a method token to be referenced, or not? + /// + public abstract bool WillUseMetadataTokenToReferenceMethod(MethodDesc method); + + /// + /// Given that a method is invokable, if it is inserted into the reflection invoke table + /// will it use a field token to be referenced, or not? + /// + public abstract bool WillUseMetadataTokenToReferenceField(FieldDesc field); + + /// + /// Gets a stub that can be used to reflection-invoke a method with a given signature. + /// + public abstract MethodDesc GetCanonicalReflectionInvokeStub(MethodDesc method); + + /// + /// Compute the canonical instantiation of a dynamic invoke thunk needed to invoke a method + /// This algorithm is shared with the runtime, so if a thunk requires generic instantiation + /// to be used, it must match this algorithm, and cannot be different with different MetadataManagers + /// NOTE: This function may return null in cases where an exact instantiation does not exist. (Universal Generics) + /// + protected MethodDesc InstantiateCanonicalDynamicInvokeMethodForMethod(MethodDesc thunk, MethodDesc method) + { + if (thunk.Instantiation.Length == 0) + { + // nothing to instantiate + return thunk; + } + + MethodSignature sig = method.Signature; + TypeSystemContext context = method.Context; + + // + // Instantiate the generic thunk over the parameters and the return type of the target method + // + + TypeDesc[] instantiation = Internal.IL.Stubs.DynamicInvokeMethodThunk.GetThunkInstantiationForMethod(method); + Debug.Assert(thunk.Instantiation.Length == instantiation.Length); + + // Check if at least one of the instantiation arguments is a universal canonical type, and if so, we + // won't create a dynamic invoker instantiation. The arguments will be interpreted at runtime by the + // calling convention converter during the dynamic invocation + foreach (TypeDesc type in instantiation) + { + if (type.IsCanonicalSubtype(CanonicalFormKind.Universal)) + return null; + } + + // If the thunk ends up being shared code, return the canonical method body. + // The concrete dictionary for the thunk will be built at runtime and is not interesting for the compiler. + Instantiation canonInstantiation = context.ConvertInstantiationToCanonForm(new Instantiation(instantiation), CanonicalFormKind.Specific); + + MethodDesc instantiatedDynamicInvokeMethod = thunk.Context.GetInstantiatedMethod(thunk, canonInstantiation); + return instantiatedDynamicInvokeMethod; + } + + protected void EnsureMetadataGenerated(NodeFactory factory) + { + if (_metadataBlob != null) + return; + + ComputeMetadata(factory, out _metadataBlob, out _typeMappings, out _methodMappings, out _fieldMappings, out _stackTraceMappings); + } + + void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider) + { + // MetadataManagers can override this to provide metadata compilation roots that need to be added to the graph ahead of time. + // (E.g. reflection roots computed by IL analyzers, or non-compilation-based roots) + } + + protected abstract void ComputeMetadata(NodeFactory factory, + out byte[] metadataBlob, + out List> typeMappings, + out List> methodMappings, + out List> fieldMappings, + out List> stackTraceMapping); + + protected MetadataRecord CreateStackTraceRecord(Metadata.MetadataTransform transform, MethodDesc method) + { + // In the metadata, we only represent the generic definition + MethodDesc methodToGenerateMetadataFor = method.GetTypicalMethodDefinition(); + MetadataRecord record = transform.HandleQualifiedMethod(methodToGenerateMetadataFor); + + // If we're generating a MemberReference to a method on a generic type, the owning type + // should appear as if instantiated over its formals + TypeDesc owningTypeToGenerateMetadataFor = methodToGenerateMetadataFor.OwningType; + if (owningTypeToGenerateMetadataFor.HasInstantiation + && record is MemberReference memberRefRecord + && memberRefRecord.Parent is TypeReference) + { + List genericArgs = new List(); + foreach (Internal.TypeSystem.Ecma.EcmaGenericParameter genericParam in owningTypeToGenerateMetadataFor.Instantiation) + { + genericArgs.Add(new TypeReference + { + TypeName = (ConstantStringValue)genericParam.Name, + }); + } + + memberRefRecord.Parent = new TypeSpecification + { + Signature = new TypeInstantiationSignature + { + GenericType = memberRefRecord.Parent, + GenericTypeArguments = genericArgs, + } + }; + } + + // As a twist, instantiated generic methods appear as if instantiated over their formals. + if (methodToGenerateMetadataFor.HasInstantiation) + { + var methodInst = new MethodInstantiation + { + Method = record, + }; + methodInst.GenericTypeArguments.Capacity = methodToGenerateMetadataFor.Instantiation.Length; + foreach (Internal.TypeSystem.Ecma.EcmaGenericParameter typeArgument in methodToGenerateMetadataFor.Instantiation) + { + var genericParam = new TypeReference + { + TypeName = (ConstantStringValue)typeArgument.Name, + }; + methodInst.GenericTypeArguments.Add(genericParam); + } + record = methodInst; + } + + return record; + } + + /// + /// Returns a set of modules that will get some metadata emitted into the output module + /// + public abstract IEnumerable GetCompilationModulesWithMetadata(); + + public byte[] GetMetadataBlob(NodeFactory factory) + { + EnsureMetadataGenerated(factory); + return _metadataBlob; + } + + public IEnumerable> GetTypeDefinitionMapping(NodeFactory factory) + { + EnsureMetadataGenerated(factory); + return _typeMappings; + } + + public IEnumerable> GetMethodMapping(NodeFactory factory) + { + EnsureMetadataGenerated(factory); + return _methodMappings; + } + + public IEnumerable> GetFieldMapping(NodeFactory factory) + { + EnsureMetadataGenerated(factory); + return _fieldMappings; + } + + public IEnumerable> GetStackTraceMapping(NodeFactory factory) + { + EnsureMetadataGenerated(factory); + return _stackTraceMappings; + } + + internal IEnumerable GetCctorContextMapping() + { + return _cctorContextsGenerated; + } + + internal IEnumerable GetTypeGVMEntries() + { + return _typeGVMEntries; + } + + internal IReadOnlyCollection GetCompiledGenericDictionaries() + { + return _genericDictionariesGenerated; + } + + internal IEnumerable GetTypesWithStructMarshalling() + { + return _typesWithStructMarshalling; + } + + internal IEnumerable GetTypesWithDelegateMarshalling() + { + return _typesWithDelegateMarshalling; + } + + public IEnumerable GetCompiledMethods() + { + return _methodsGenerated; + } + + public IEnumerable GetReflectableMethods() + { + return _reflectableMethods; + } + + internal IEnumerable GetCompiledMethodBodies() + { + return _methodBodiesGenerated; + } + + internal bool TypeGeneratesEEType(TypeDesc type) + { + return _typesWithEETypesGenerated.Contains(type); + } + + internal IEnumerable GetTypesWithEETypes() + { + return _typesWithEETypesGenerated; + } + + internal IEnumerable GetTypesWithConstructedEETypes() + { + return _typesWithConstructedEETypesGenerated; + } + + internal IEnumerable GetDynamicInvokeTemplateMethods() + { + return _dynamicInvokeTemplates; + } + + internal IEnumerable GetTemplateMethodEntries() + { + return _templateMethodEntries; + } + + public bool IsReflectionBlocked(TypeDesc type) + { + switch (type.Category) + { + case TypeFlags.SzArray: + case TypeFlags.Array: + case TypeFlags.Pointer: + case TypeFlags.ByRef: + return IsReflectionBlocked(((ParameterizedType)type).ParameterType); + + case TypeFlags.FunctionPointer: + MethodSignature pointerSignature = ((FunctionPointerType)type).Signature; + + for (int i = 0; i < pointerSignature.Length; i++) + if (IsReflectionBlocked(pointerSignature[i])) + return true; + + return IsReflectionBlocked(pointerSignature.ReturnType); + + default: + Debug.Assert(type.IsDefType); + + TypeDesc typeDefinition = type.GetTypeDefinition(); + if (type != typeDefinition) + { + if (_blockingPolicy.IsBlocked((MetadataType)typeDefinition)) + return true; + + if (IsReflectionBlocked(type.Instantiation)) + return true; + + return false; + } + + return _blockingPolicy.IsBlocked((MetadataType)type); + } + } + + protected bool IsReflectionBlocked(Instantiation instantiation) + { + foreach (TypeDesc type in instantiation) + { + if (IsReflectionBlocked(type) && !type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) + return true; + } + return false; + } + + public bool IsReflectionBlocked(FieldDesc field) + { + FieldDesc typicalFieldDefinition = field.GetTypicalFieldDefinition(); + if (typicalFieldDefinition != field && IsReflectionBlocked(field.OwningType.Instantiation)) + { + return true; + } + + return _blockingPolicy.IsBlocked(typicalFieldDefinition); + } + + public bool IsReflectionBlocked(MethodDesc method) + { + MethodDesc methodDefinition = method.GetMethodDefinition(); + if (method != methodDefinition && IsReflectionBlocked(method.Instantiation)) + { + return true; + } + + MethodDesc typicalMethodDefinition = methodDefinition.GetTypicalMethodDefinition(); + if (typicalMethodDefinition != methodDefinition && IsReflectionBlocked(method.OwningType.Instantiation)) + { + return true; + } + + return _blockingPolicy.IsBlocked(typicalMethodDefinition); + } + + public bool IsManifestResourceBlocked(ModuleDesc module, string resourceName) + { + return _resourceBlockingPolicy.IsManifestResourceBlocked(module, resourceName); + } + + public bool CanGenerateMetadata(MetadataType type) + { + return (GetMetadataCategory(type) & MetadataCategory.Description) != 0; + } + + public bool CanGenerateMetadata(MethodDesc method) + { + Debug.Assert(method.IsTypicalMethodDefinition); + return (GetMetadataCategory(method) & MetadataCategory.Description) != 0; + } + + public bool CanGenerateMetadata(FieldDesc field) + { + Debug.Assert(field.IsTypicalFieldDefinition); + return (GetMetadataCategory(field) & MetadataCategory.Description) != 0; + } + + /// + /// Gets the metadata category for a compiled method body in the current compilation. + /// The method will only get called with '' that has a compiled method body + /// in this compilation. + /// Note that if this method doesn't return , it doesn't mean + /// that the method never has metadata. The metadata might just be generated in a different compilation. + /// + protected abstract MetadataCategory GetMetadataCategory(MethodDesc method); + + /// + /// Gets the metadata category for a generated type in the current compilation. + /// The method can assume it will only get called with '' that has an MethodTable generated + /// in the current compilation. + /// Note that if this method doesn't return , it doesn't mean + /// that the method never has metadata. The metadata might just be generated in a different compilation. + /// + protected abstract MetadataCategory GetMetadataCategory(TypeDesc type); + protected abstract MetadataCategory GetMetadataCategory(FieldDesc field); + + public virtual void GetDependenciesDueToAccess(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, MethodDesc calledMethod) + { + } + + public virtual void GetDependenciesDueToAccess(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, FieldDesc writtenField) + { + } + + public virtual DependencyList GetDependenciesForCustomAttribute(NodeFactory factory, MethodDesc attributeCtor, CustomAttributeValue decodedValue) + { + return null; + } + + public virtual void GetDependenciesForGenericDictionary(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + } + + public virtual void GetDependenciesForGenericDictionary(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + } + + public virtual void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc overridingMethod) + { + } + } + + public struct MetadataMapping + { + public readonly TEntity Entity; + public readonly int MetadataHandle; + + public MetadataMapping(TEntity entity, int metadataHandle) + { + Entity = entity; + MetadataHandle = metadataHandle; + } + } + + [Flags] + public enum MetadataCategory + { + None = 0x00, + Description = 0x01, + RuntimeMapping = 0x02, + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MethodExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MethodExtensions.cs new file mode 100644 index 00000000000000..b7aeee454ef021 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MethodExtensions.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + public static class MethodExtensions + { + public static string GetRuntimeImportName(this EcmaMethod This) + { + var decoded = This.GetDecodedCustomAttribute("System.Runtime", "RuntimeImportAttribute"); + if (decoded == null) + return null; + + var decodedValue = decoded.Value; + + if (decodedValue.FixedArguments.Length != 0) + return (string)decodedValue.FixedArguments[decodedValue.FixedArguments.Length - 1].Value; + + return null; + } + + public static string GetRuntimeImportDllName(this EcmaMethod This) + { + var decoded = This.GetDecodedCustomAttribute("System.Runtime", "RuntimeImportAttribute"); + if (decoded == null) + return null; + + var decodedValue = decoded.Value; + + if (decodedValue.FixedArguments.Length == 2) + return (string)decodedValue.FixedArguments[0].Value; + + return null; + } + + public static string GetRuntimeExportName(this EcmaMethod This) + { + var decoded = This.GetDecodedCustomAttribute("System.Runtime", "RuntimeExportAttribute"); + if (decoded == null) + return null; + + var decodedValue = decoded.Value; + + if (decodedValue.FixedArguments.Length != 0) + return (string)decodedValue.FixedArguments[0].Value; + + foreach (var argument in decodedValue.NamedArguments) + { + if (argument.Name == "EntryPoint") + return (string)argument.Value; + } + + return null; + } + + public static string GetUnmanagedCallersOnlyExportName(this EcmaMethod This) + { + var decoded = This.GetDecodedCustomAttribute("System.Runtime.InteropServices", "UnmanagedCallersOnlyAttribute"); + if (decoded == null) + return null; + + var decodedValue = decoded.Value; + + foreach (var argument in decodedValue.NamedArguments) + { + if (argument.Name == "EntryPoint") + return (string)argument.Value; + } + + return null; + } + + /// + /// Determine whether a method can go into the sealed vtable of a type. Such method must be a sealed virtual + /// method that is not overriding any method on a base type. + /// Given that such methods can never be overridden in any derived type, we can + /// save space in the vtable of a type, and all of its derived types by not emitting these methods in their vtables, + /// and storing them in a separate table on the side. This is especially beneficial for all array types, + /// since all of their collection interface methods are sealed and implemented on the System.Array and + /// System.Array<T> base types, and therefore we can minimize the vtable sizes of all derived array types. + /// + public static bool CanMethodBeInSealedVTable(this MethodDesc method) + { + // Methods on interfaces never go into sealed vtable + // We would hit this code path for default implementations of interface methods (they are newslot+final). + // Inteface types don't get physical slots, but they have logical slot numbers and that logic shouldn't + // attempt to place final+newslot methods differently. + return method.IsFinal && method.IsNewSlot && !method.OwningType.IsInterface; + } + + public static bool NotCallableWithoutOwningEEType(this MethodDesc method) + { + TypeDesc owningType = method.OwningType; + return !method.Signature.IsStatic && /* Static methods don't have this */ + !owningType.IsValueType && /* Value type instance methods take a ref to data */ + !owningType.IsArrayTypeWithoutGenericInterfaces() && /* Type loader can make these at runtime */ + (owningType is not MetadataType mdType || !mdType.IsModuleType) && /* Compiler parks some instance methods on the type */ + !method.IsSharedByGenericInstantiations; /* Current impl limitation; can be lifted */ + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MethodImportationErrorProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MethodImportationErrorProvider.cs new file mode 100644 index 00000000000000..2a5fae99b76265 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MethodImportationErrorProvider.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Class that provides cached information about method body importation errors that occured in previous phases. + /// + public class MethodImportationErrorProvider + { + /// + /// Returns the error discovered while processing the method body for the given method in the past. + /// + /// Relevant or null if no error was seen. + public virtual TypeSystemException GetCompilationError(MethodDesc method) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MultiFileCompilationModuleGroup.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MultiFileCompilationModuleGroup.cs new file mode 100644 index 00000000000000..9f93ae3ea87a13 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MultiFileCompilationModuleGroup.cs @@ -0,0 +1,136 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using ILCompiler.DependencyAnalysis; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + public abstract class MultiFileCompilationModuleGroup : CompilationModuleGroup + { + private HashSet _compilationModuleSet; + + public MultiFileCompilationModuleGroup(TypeSystemContext context, IEnumerable compilationModuleSet) + { + _compilationModuleSet = new HashSet(compilationModuleSet); + + // The fake assembly that holds compiler generated types is part of the compilation. + _compilationModuleSet.Add(context.GeneratedAssembly); + } + + public sealed override bool ContainsType(TypeDesc type) + { + EcmaType ecmaType = type as EcmaType; + + if (ecmaType == null) + return true; + + if (!IsModuleInCompilationGroup(ecmaType.EcmaModule)) + { + return false; + } + + return true; + } + + public sealed override bool ContainsTypeDictionary(TypeDesc type) + { + return ContainsType(type); + } + + public sealed override bool ContainsMethodBody(MethodDesc method, bool unboxingStub) + { + if (method.HasInstantiation) + return true; + + return ContainsType(method.OwningType); + } + + public sealed override bool ContainsMethodDictionary(MethodDesc method) + { + Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method); + return ContainsMethodBody(method, false); + } + + public sealed override bool ImportsMethod(MethodDesc method, bool unboxingStub) + { + return false; + } + + private bool IsModuleInCompilationGroup(EcmaModule module) + { + return _compilationModuleSet.Contains(module); + } + + public sealed override bool IsSingleFileCompilation + { + get + { + return false; + } + } + + public sealed override bool ShouldReferenceThroughImportTable(TypeDesc type) + { + return false; + } + + public override bool CanHaveReferenceThroughImportTable + { + get + { + return false; + } + } + } + + /// + /// Represents a non-leaf multifile compilation group where types contained in the group are always fully expanded. + /// + public class MultiFileSharedCompilationModuleGroup : MultiFileCompilationModuleGroup + { + public MultiFileSharedCompilationModuleGroup(TypeSystemContext context, IEnumerable compilationModuleSet) + : base(context, compilationModuleSet) + { + } + + public override bool ShouldProduceFullVTable(TypeDesc type) + { + return ConstructedEETypeNode.CreationAllowed(type); + } + + public override bool ShouldPromoteToFullType(TypeDesc type) + { + return ShouldProduceFullVTable(type); + } + + public override bool PresenceOfEETypeImpliesAllMethodsOnType(TypeDesc type) + { + return (type.HasInstantiation || type.IsArray) && ShouldProduceFullVTable(type) && + type.ConvertToCanonForm(CanonicalFormKind.Specific).IsCanonicalSubtype(CanonicalFormKind.Any); + } + + public override bool AllowInstanceMethodOptimization(MethodDesc method) + { + // Both the instance methods and the owning type are homed in a single compilation group + // so if we're able to generate the body, we would also generate the owning type here + // and nowhere else. + Debug.Assert(ContainsMethodBody(method, unboxingStub: false)); + TypeDesc owningType = method.OwningType; + return owningType.IsDefType && !owningType.HasInstantiation && !method.HasInstantiation; + } + + public override bool AllowVirtualMethodOnAbstractTypeOptimization(MethodDesc method) + { + // Not really safe to do this since we need to assume IgnoreAccessChecks + // and we wouldn't know all derived types when compiling methods on the type + // that introduces this method. + return false; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/NativeLibraryInitializerRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/NativeLibraryInitializerRootProvider.cs new file mode 100644 index 00000000000000..4b7c639400f647 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/NativeLibraryInitializerRootProvider.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.TypeSystem.Ecma; +using Internal.TypeSystem; +using Internal.IL.Stubs.StartupCode; + +namespace ILCompiler +{ + /// + /// Provides compilation group for a native library that compiles the initialize method. + /// + public class NativeLibraryInitializerRootProvider : ICompilationRootProvider + { + /// + /// Symbolic name under which the managed initializer is exported. + /// + public const string ManagedEntryPointMethodName = "__managed__Startup"; + + private ModuleDesc _module; + private IReadOnlyCollection _libraryInitializers; + + public NativeLibraryInitializerRootProvider(ModuleDesc module, IReadOnlyCollection libraryInitializers) + { + _module = module; + _libraryInitializers = libraryInitializers; + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + TypeDesc owningType = _module.GetGlobalModuleType(); + NativeLibraryStartupMethod nativeLibStartupCode = new NativeLibraryStartupMethod(owningType, _libraryInitializers); + rootProvider.AddCompilationRoot(nativeLibStartupCode, "Startup Code Main Method", ManagedEntryPointMethodName); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/NoMetadataBlockingPolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/NoMetadataBlockingPolicy.cs new file mode 100644 index 00000000000000..d27ba4ab79c9ad --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/NoMetadataBlockingPolicy.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + /// + /// Represents a metadata blocking policy that doesn't block any metadata. + /// + public sealed class NoMetadataBlockingPolicy : MetadataBlockingPolicy + { + public override bool IsBlocked(MetadataType type) => !(type is EcmaType); + + public override bool IsBlocked(FieldDesc field) => !(field is EcmaField); + + private MetadataType _arrayOfTType; + private MetadataType InitializeArrayOfTType(TypeSystemEntity contextEntity) + { + _arrayOfTType = contextEntity.Context.SystemModule.GetType("System", "Array`1"); + return _arrayOfTType; + } + private MetadataType GetArrayOfTType(TypeSystemEntity contextEntity) + { + if (_arrayOfTType != null) + { + return _arrayOfTType; + } + return InitializeArrayOfTType(contextEntity); + } + + public override bool IsBlocked(MethodDesc method) + { + if (method is EcmaMethod ecmaMethod) + { + // Methods on Array`1 are implementation details that implement the generic interfaces on + // arrays. They should not generate metadata or be reflection invokable. + // We can get rid of this special casing if we make these methods stop being regular EcmaMethods + // with Array as their owning type + if (ecmaMethod.OwningType == GetArrayOfTType(ecmaMethod)) + return true; + + return false; + } + + return true; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/NodeMangler.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/NodeMangler.cs new file mode 100644 index 00000000000000..ea9586de22de21 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/NodeMangler.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler +{ + // + // NodeMangler is responsible for producing mangled names for specific nodes + // and for node-related purposes, where the name needs to be in a special format + // on some platform + // + public abstract class NodeMangler + { + public NameMangler NameMangler; + + protected const string GenericDictionaryNamePrefix = "__GenericDict_"; + + // Mangled name of boxed version of a type + public abstract string MangledBoxedTypeName(TypeDesc type); + + public abstract string MethodTable(TypeDesc type); + public abstract string GCStatics(TypeDesc type); + public abstract string NonGCStatics(TypeDesc type); + public abstract string ThreadStatics(TypeDesc type); + public abstract string ThreadStaticsIndex(TypeDesc type); + public abstract string TypeGenericDictionary(TypeDesc type); + public abstract string MethodGenericDictionary(MethodDesc method); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectDumper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectDumper.cs new file mode 100644 index 00000000000000..8d56104eb2502b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectDumper.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Security.Cryptography; +using System.Xml; + +using Internal.Text; + +using ILCompiler.DependencyAnalysis; + +using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; + +namespace ILCompiler +{ + public class ObjectDumper : IObjectDumper + { + private readonly string _fileName; + private SHA256 _sha256; + private XmlWriter _writer; + + public ObjectDumper(string fileName) + { + _fileName = fileName; + } + + internal void Begin() + { + var settings = new XmlWriterSettings + { + CloseOutput = true, + Indent = true, + }; + + _sha256 = SHA256.Create(); + _writer = XmlWriter.Create(File.CreateText(_fileName), settings); + _writer.WriteStartElement("ObjectNodes"); + } + + private static string GetObjectNodeName(ObjectNode node) + { + string name = node.GetType().Name; + + // Some nodes are generic and their type name contains "`". Strip it. + int indexOfAccent = name.LastIndexOf('`'); + if (indexOfAccent > 0) + name = name.Substring(0, indexOfAccent); + + // Node type names generally end with "Node", but that's redundant. + if (name.EndsWith("Node")) + name = name.Substring(0, name.Length - 4); + + return name; + } + + void IObjectDumper.DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData) + { + string name = null; + + _writer.WriteStartElement(GetObjectNodeName(node)); + + var symbolNode = node as ISymbolNode; + if (symbolNode != null) + { + Utf8StringBuilder sb = new Utf8StringBuilder(); + symbolNode.AppendMangledName(mangler, sb); + name = sb.ToString(); + _writer.WriteAttributeString("Name", name); + } + + _writer.WriteAttributeString("Length", objectData.Data.Length.ToStringInvariant()); + _writer.WriteAttributeString("Hash", HashData(objectData.Data)); + _writer.WriteEndElement(); + + var nodeWithCodeInfo = node as INodeWithCodeInfo; + if (nodeWithCodeInfo != null) + { + _writer.WriteStartElement("GCInfo"); + _writer.WriteAttributeString("Name", name); + _writer.WriteAttributeString("Length", nodeWithCodeInfo.GCInfo.Length.ToStringInvariant()); + _writer.WriteAttributeString("Hash", HashData(nodeWithCodeInfo.GCInfo)); + _writer.WriteEndElement(); + } + } + + private string HashData(byte[] data) + { + return BitConverter.ToString(_sha256.ComputeHash(data)).Replace("-", "").ToLower(); + } + + internal void End() + { + _writer.WriteEndElement(); + _writer.Dispose(); + _writer = null; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PreinitializationManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PreinitializationManager.cs new file mode 100644 index 00000000000000..031c247bf24e2f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PreinitializationManager.cs @@ -0,0 +1,159 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using Internal.IL; +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Manages policies around static constructors (.cctors) and static data initialization. + /// + public class PreinitializationManager + { + private readonly bool _supportsLazyCctors; + private readonly bool _enableInterpreter; + + public PreinitializationManager(TypeSystemContext context, CompilationModuleGroup compilationGroup, ILProvider ilprovider, bool enableInterpreter) + { + _supportsLazyCctors = context.SystemModule.GetType("System.Runtime.CompilerServices", "ClassConstructorRunner", throwIfNotFound: false) != null; + _preinitHashTable = new PreinitializationInfoHashtable(compilationGroup, ilprovider); + _enableInterpreter = enableInterpreter; + } + + /// + /// Returns true if '' has a lazily executed static constructor. + /// A lazy static constructor gets executed on first access to type's members. + /// + public bool HasLazyStaticConstructor(TypeDesc type) + { + if (!type.HasStaticConstructor) + return false; + + // If the cctor runs eagerly at startup, it's not lazy + if (HasEagerConstructorAttribute(type)) + return false; + + // If the class library doesn't support lazy cctors, everything is preinitialized before Main + // either by interpretting the cctor at compile time, or by running the cctor eagerly at startup. + if (!_supportsLazyCctors) + return false; + + // Would be odd to see a type with a cctor that is not MetadataType + Debug.Assert(type is MetadataType); + var mdType = (MetadataType)type; + + // The cctor on the Module type is the module constructor. That's special. + if (mdType.IsModuleType) + return false; + + // If we can't interpret the cctor at compile time, the cctor is lazy. + return !IsPreinitialized(mdType); + } + + /// + /// Returns true if '' has a static constructor that is eagerly + /// executed at process startup time. + /// + public bool HasEagerStaticConstructor(TypeDesc type) + { + if (!type.HasStaticConstructor) + return false; + + // Would be odd to see a type with a cctor that is not MetadataType + Debug.Assert(type is MetadataType); + var mdType = (MetadataType)type; + + // If the type is preinitialized at compile time, that's not eager. + if (IsPreinitialized(mdType)) + return false; + + // If the type is marked as eager or classlib doesn't have a cctor runner, it's eager. + return HasEagerConstructorAttribute(type) || !_supportsLazyCctors; + } + + private static bool HasEagerConstructorAttribute(TypeDesc type) + { + MetadataType mdType = type as MetadataType; + return mdType != null && + mdType.HasCustomAttribute("System.Runtime.CompilerServices", "EagerStaticClassConstructionAttribute"); + } + + public bool IsPreinitialized(MetadataType type) + { + // If the cctor interpreter is not enabled, no type is preinitialized. + if (!_enableInterpreter) + return false; + + if (!type.HasStaticConstructor) + return false; + + // The cctor on the Module type is the module constructor. That's special. + if (type.IsModuleType) + return false; + + // Generic definitions cannot be preinitialized + if (type.IsGenericDefinition) + return false; + + return GetPreinitializationInfo(type).IsPreinitialized; + } + + public void LogStatistics(Logger logger) + { + if (!_enableInterpreter) + return; + + int totalEligibleTypes = 0; + int totalPreinitializedTypes = 0; + + if (logger.IsVerbose) + { + foreach (var item in LockFreeReaderHashtable.Enumerator.Get(_preinitHashTable)) + { + totalEligibleTypes++; + if (item.IsPreinitialized) + { + logger.Writer.WriteLine($"Preinitialized type '{item.Type}'"); + totalPreinitializedTypes++; + } + else + { + logger.Writer.WriteLine($"Could not preinitialize '{item.Type}': {item.FailureReason}"); + } + } + + logger.Writer.WriteLine($"Preinitialized {totalPreinitializedTypes} types out of {totalEligibleTypes}."); + } + } + + public TypePreinit.PreinitializationInfo GetPreinitializationInfo(MetadataType type) + { + return _preinitHashTable.GetOrCreateValue(type); + } + + class PreinitializationInfoHashtable : LockFreeReaderHashtable + { + private readonly CompilationModuleGroup _compilationGroup; + private readonly ILProvider _ilProvider; + + public PreinitializationInfoHashtable(CompilationModuleGroup compilationGroup, ILProvider ilProvider) + { + _compilationGroup = compilationGroup; + _ilProvider = ilProvider; + } + + protected override bool CompareKeyToValue(MetadataType key, TypePreinit.PreinitializationInfo value) => key == value.Type; + protected override bool CompareValueToValue(TypePreinit.PreinitializationInfo value1, TypePreinit.PreinitializationInfo value2) => value1.Type == value2.Type; + protected override int GetKeyHashCode(MetadataType key) => key.GetHashCode(); + protected override int GetValueHashCode(TypePreinit.PreinitializationInfo value) => value.Type.GetHashCode(); + + protected override TypePreinit.PreinitializationInfo CreateValueFromKey(MetadataType key) + { + return TypePreinit.ScanType(_compilationGroup, _ilProvider, key); + } + } + private PreinitializationInfoHashtable _preinitHashTable; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ProcessLinkerXmlBase.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ProcessLinkerXmlBase.cs new file mode 100644 index 00000000000000..c5101bde818fa3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ProcessLinkerXmlBase.cs @@ -0,0 +1,404 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Xml; + +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Base class for readers of IL Linker XML file format. + /// + internal abstract class ProcessLinkerXmlBase + { + private readonly XmlReader _reader; + private readonly ModuleDesc _owningModule; + private readonly IReadOnlyDictionary _featureSwitchValues; + protected readonly TypeSystemContext _context; + + public ProcessLinkerXmlBase(TypeSystemContext context, XmlReader reader, ModuleDesc owningModule, IReadOnlyDictionary featureSwitchValues) + { + _reader = reader; + _owningModule = owningModule; + _featureSwitchValues = featureSwitchValues; + _context = context; + } + + public void ProcessXml() + { + if (_reader.IsStartElement() && _reader.Name == "linker") + { + if (!ShouldProcessElement()) + return; + + _reader.Read(); + + ProcessAssemblies(); + } + } + + protected string GetAttribute(string attribute) + { + return _reader.GetAttribute(attribute); + } + + protected bool IsEmpty() + { + return _reader.IsEmptyElement; + } + + private void ProcessAssemblies() + { + while (_reader.IsStartElement()) + { + if (_reader.Name == "assembly") + { + string assemblyName = _reader.GetAttribute("fullname"); + + if (assemblyName == "*") + { + // https://github.com/dotnet/runtimelab/issues/1381 + _reader.Skip(); + continue; + } + + // Errors for invalid assembly names should show up even if this element will be + // skipped due to feature conditions. + var name = new AssemblyName(assemblyName); + + if (!ShouldProcessElement()) + { + _reader.Skip(); + continue; + } + + ModuleDesc assembly = GetAssembly(name); + + if (assembly == null) + { + //Context.LogWarning($"Could not resolve assembly '{name.Name}'", 2007, _xmlDocumentLocation); + _reader.Skip(); + continue; + } + + _reader.Read(); + + ProcessAssembly(assembly); + } + else if (_reader.Name == "type") + { + ProcessType(_owningModule); + } + else if (_reader.Name == "resource") + { + ProcessResource(_owningModule); + } + else + { + _reader.Skip(); + } + } + } + + protected ModuleDesc GetAssembly(AssemblyName name) + { + return _context.ResolveAssembly(name); + } + + private void ProcessAssembly(ModuleDesc assembly) + { + while (_reader.IsStartElement()) + { + if (_reader.Name == "type") + { + ProcessType(assembly); + } + else if (_reader.Name == "resource") + { + ProcessResource(assembly); + } + + _reader.Skip(); + } + + _reader.ReadEndElement(); + } + + private void ProcessType(ModuleDesc assembly) + { + if (ShouldProcessElement()) + { + string typeName = _reader.GetAttribute("fullname"); + + if (typeName.Contains('*')) + throw new NotSupportedException(); + + TypeDesc type = CustomAttributeTypeNameParser.GetTypeByCustomAttributeTypeName(assembly, typeName, throwIfNotFound: false); + if (type == null) + { + //Context.LogWarning ($"Could not resolve type '{fullname}'", 2008, _xmlDocumentLocation); + _reader.Skip(); + return; + } + + _reader.Read(); + + while (_reader.IsStartElement()) + { + if (_reader.Name == "method") + { + ProcessMethod(type); + } + else if (_reader.Name == "field") + { + ProcessField(type); + } + else if (_reader.Name == "attribute") + { + ProcessAttribute(type); + } + + _reader.Skip(); + } + } + + _reader.Skip(); + } + + private void ProcessMethod(TypeDesc type) + { + if (ShouldProcessElement()) + { + string signature = _reader.GetAttribute("signature"); + if (!String.IsNullOrEmpty(signature)) + { + MethodDesc method = GetMethod(type, signature); + if (method == null) + { + //Context.LogWarning($"Could not find method '{signature}' on type '{type.GetDisplayName()}'", 2009, _xmlDocumentLocation); + return; + } + + ProcessMethod(method); + } + + string methodName = _reader.GetAttribute("name"); + if (!String.IsNullOrEmpty(methodName)) + { + bool foundMatch = false; + foreach (MethodDesc method in type.GetMethods()) + { + if (method.Name == methodName) + { + foundMatch = true; + ProcessMethod(method); + } + } + + if (!foundMatch) + { + //Context.LogWarning($"Could not find method '{name}' on type '{type.GetDisplayName()}'", 2009, _xmlDocumentLocation); + } + } + } + } + + protected virtual void ProcessMethod(MethodDesc method) + { + } + + private void ProcessField(TypeDesc type) + { + if (ShouldProcessElement()) + { + string fieldName = _reader.GetAttribute("name"); + if (!String.IsNullOrEmpty(fieldName)) + { + FieldDesc field = type.GetField(fieldName); + + if (field == null) + { + //Context.LogWarning($"Could not find field '{name}' on type '{type.GetDisplayName()}'", 2012, _xmlDocumentLocation); + } + else + { + ProcessField(field); + } + } + } + } + + protected virtual void ProcessField(FieldDesc field) + { + } + + protected virtual void ProcessAttribute(TypeDesc type) + { + } + + protected virtual void ProcessResource(ModuleDesc module) + { + } + + protected MethodDesc GetMethod(TypeDesc type, string signature) + { + foreach (MethodDesc meth in type.GetMethods()) + if (signature == GetMethodSignature(meth, false)) + return meth; + + return null; + } + + public static string GetMethodSignature(MethodDesc meth, bool includeGenericParameters) + { + StringBuilder sb = new StringBuilder(); + CecilTypeNameFormatter.Instance.AppendName(sb, meth.Signature.ReturnType); + sb.Append(' '); + sb.Append(meth.Name); + if (includeGenericParameters && meth.HasInstantiation) + { + sb.Append('`'); + sb.Append(meth.Instantiation.Length); + } + + sb.Append('('); + for (int i = 0; i < meth.Signature.Length; i++) + { + if (i > 0) + sb.Append(','); + + CecilTypeNameFormatter.Instance.AppendName(sb, meth.Signature[i]); + } + + sb.Append(')'); + return sb.ToString(); + } + + private bool ShouldProcessElement() + { + string feature = _reader.GetAttribute("feature"); + if (string.IsNullOrEmpty(feature)) + return true; + + string value = _reader.GetAttribute("featurevalue"); + if (string.IsNullOrEmpty(value)) + { + //context.LogError($"Failed to process '{documentLocation}'. Feature '{feature}' does not specify a 'featurevalue' attribute", 1001); + return false; + } + + if (!bool.TryParse(value, out bool bValue)) + { + //context.LogError($"Failed to process '{documentLocation}'. Unsupported non-boolean feature definition '{feature}'", 1002); + return false; + } + + var isDefault = _reader.GetAttribute("featuredefault"); + bool bIsDefault = false; + if (!string.IsNullOrEmpty(isDefault) && (!bool.TryParse(isDefault, out bIsDefault) || !bIsDefault)) + { + //context.LogError($"Failed to process '{documentLocation}'. Unsupported value for featuredefault attribute", 1014); + return false; + } + + if (!_featureSwitchValues.TryGetValue(feature, out bool featureSetting)) + return bIsDefault; + + return bValue == featureSetting; + } + + class CecilTypeNameFormatter : TypeNameFormatter + { + public static readonly CecilTypeNameFormatter Instance = new CecilTypeNameFormatter(); + + public override void AppendName(StringBuilder sb, ArrayType type) + { + AppendName(sb, type.ElementType); + sb.Append('['); + if (type.Rank > 1) + sb.Append(new string(',', type.Rank - 1)); + sb.Append(']'); + } + public override void AppendName(StringBuilder sb, ByRefType type) + { + AppendName(sb, type.ParameterType); + sb.Append('&'); + } + + public override void AppendName(StringBuilder sb, PointerType type) + { + AppendName(sb, type.ParameterType); + sb.Append('*'); + } + + public override void AppendName(StringBuilder sb, FunctionPointerType type) + { + sb.Append(" "); + AppendName(sb, type.Signature.ReturnType); + sb.Append(" *"); + + sb.Append("("); + + for (int i = 0; i < type.Signature.Length; i++) + { + var parameter = type.Signature[i]; + if (i > 0) + sb.Append(","); + + AppendName(sb, parameter); + } + + sb.Append(")"); + } + + public override void AppendName(StringBuilder sb, GenericParameterDesc type) + { + sb.Append(type.Name); + } + public override void AppendName(StringBuilder sb, SignatureMethodVariable type) + { + } + public override void AppendName(StringBuilder sb, SignatureTypeVariable type) + { + } + protected override void AppendNameForInstantiatedType(StringBuilder sb, DefType type) + { + AppendName(sb, type.GetTypeDefinition()); + + sb.Append('<'); + + for (int i = 0; i < type.Instantiation.Length; i++) + { + if (i != 0) + sb.Append(','); + + AppendName(sb, type.Instantiation[i]); + } + + sb.Append('>'); + } + protected override void AppendNameForNamespaceType(StringBuilder sb, DefType type) + { + if (!String.IsNullOrEmpty(type.Namespace)) + { + sb.Append(type.Namespace); + sb.Append('.'); + } + + sb.Append(type.Name); + } + + protected override void AppendNameForNestedType(StringBuilder sb, DefType nestedType, DefType containingType) + { + AppendName(sb, containingType); + sb.Append('/'); + sb.Append(nestedType.Name); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PropertyPseudoDesc.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PropertyPseudoDesc.cs new file mode 100644 index 00000000000000..3113c629926c1c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PropertyPseudoDesc.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection.Metadata; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + /// + /// A "PropertyDesc" to describe properties. Represents a property within the compiler. + /// This is not a real type system entity. In particular, these are not interned. + /// + public class PropertyPseudoDesc : TypeSystemEntity + { + private readonly EcmaType _type; + private readonly PropertyDefinitionHandle _handle; + + private PropertyDefinition Definition => _type.MetadataReader.GetPropertyDefinition(_handle); + + public PropertySignature Signature => + new EcmaSignatureParser(_type.EcmaModule, _type.MetadataReader.GetBlobReader(Definition.Signature), NotFoundBehavior.Throw) + .ParsePropertySignature(); + + public MethodDesc GetMethod + { + get + { + MethodDefinitionHandle getter = Definition.GetAccessors().Getter; + return getter.IsNil ? null : _type.EcmaModule.GetMethod(getter); + } + } + + public MethodDesc SetMethod + { + get + { + MethodDefinitionHandle setter = Definition.GetAccessors().Setter; + return setter.IsNil ? null : _type.EcmaModule.GetMethod(setter); + } + } + + public MetadataType OwningType + { + get + { + return _type; + } + } + + public string Name + { + get + { + return _type.MetadataReader.GetString(Definition.Name); + } + } + + public PropertyPseudoDesc(EcmaType type, PropertyDefinitionHandle handle) + { + _type = type; + _handle = handle; + } + + public override TypeSystemContext Context => _type.Context; + + #region Do not use these + public override bool Equals(object obj) => throw new NotImplementedException(); + public override int GetHashCode() => throw new NotImplementedException(); + public static bool operator ==(PropertyPseudoDesc a, PropertyPseudoDesc b) => throw new NotImplementedException(); + public static bool operator !=(PropertyPseudoDesc a, PropertyPseudoDesc b) => throw new NotImplementedException(); + #endregion + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingHelpers.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingHelpers.cs new file mode 100644 index 00000000000000..a23fdf1e658485 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingHelpers.cs @@ -0,0 +1,269 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.TypeSystem; + +using ILCompiler.DependencyAnalysis; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + public class RootingHelpers + { + public static bool TryRootType(IRootingServiceProvider rootProvider, TypeDesc type, string reason) + { + try + { + RootType(rootProvider, type, reason); + return true; + } + catch (TypeSystemException) + { + return false; + } + } + + public static void RootType(IRootingServiceProvider rootProvider, TypeDesc type, string reason) + { + rootProvider.AddCompilationRoot(type, reason); + + // Instantiate generic types over something that will be useful at runtime + if (type.IsGenericDefinition) + { + Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(type.Instantiation, allowCanon: true); + if (inst.IsNull) + return; + + type = ((MetadataType)type).MakeInstantiatedType(inst); + + rootProvider.AddCompilationRoot(type, reason); + } + + // Also root base types. This is so that we make methods on the base types callable. + // This helps in cases like "class Foo : Bar { }" where we discover new + // generic instantiations. + TypeDesc baseType = type.BaseType; + if (baseType != null) + { + RootType(rootProvider, baseType.NormalizeInstantiation(), reason); + } + + if (type.IsDefType) + { + foreach (var method in type.GetMethods()) + { + if (method.HasInstantiation) + { + // Generic methods on generic types could end up as Foo.Bar<__Canon>(), + // so for simplicity, we just don't handle them right now to make this more + // predictable. + if (!method.OwningType.HasInstantiation) + { + Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(method.Instantiation, allowCanon: false); + if (!inst.IsNull) + { + TryRootMethod(rootProvider, method.MakeInstantiatedMethod(inst), reason); + } + } + } + else + { + TryRootMethod(rootProvider, method, reason); + } + } + } + } + + public static bool TryRootMethod(IRootingServiceProvider rootProvider, MethodDesc method, string reason) + { + try + { + RootMethod(rootProvider, method, reason); + return true; + } + catch (TypeSystemException) + { + return false; + } + } + + public static void RootMethod(IRootingServiceProvider rootProvider, MethodDesc method, string reason) + { + // Make sure we're not putting something into the graph that will crash later. + LibraryRootProvider.CheckCanGenerateMethod(method); + + rootProvider.AddReflectionRoot(method, reason); + } + + public static bool TryGetDependenciesForReflectedMethod(ref DependencyList dependencies, NodeFactory factory, MethodDesc method, string reason) + { + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + if (factory.MetadataManager.IsReflectionBlocked(typicalMethod)) + { + return false; + } + + // If this is a generic method, make sure we at minimum have the metadata + // for it. This hedges against the risk that we fail to figure out a code body + // for it below. + if (typicalMethod.IsGenericMethodDefinition || typicalMethod.OwningType.IsGenericDefinition) + { + dependencies ??= new DependencyList(); + dependencies.Add(factory.ReflectableMethod(typicalMethod), reason); + } + + // If there's any genericness involved, try to create a fitting instantiation that would be usable at runtime. + // This is not a complete solution to the problem. + // If we ever decide that MakeGenericType/MakeGenericMethod should simply be considered unsafe, this code can be deleted + // and instantiations that are not fully closed can be ignored. + if (method.OwningType.IsGenericDefinition || method.OwningType.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) + { + TypeDesc owningType = method.OwningType.GetTypeDefinition(); + Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(owningType.Instantiation, allowCanon: false); + if (inst.IsNull) + { + return false; + } + + method = method.Context.GetMethodForInstantiatedType( + method.GetTypicalMethodDefinition(), + ((MetadataType)owningType).MakeInstantiatedType(inst)); + } + + if (method.IsGenericMethodDefinition || method.Instantiation.ContainsSignatureVariables()) + { + method = method.GetMethodDefinition(); + + Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(method.Instantiation, allowCanon: false); + if (inst.IsNull) + { + return false; + } + + method = method.MakeInstantiatedMethod(inst); + } + + try + { + // Make sure we're not putting something into the graph that will crash later. + LibraryRootProvider.CheckCanGenerateMethod(method); + } + catch (TypeSystemException) + { + return false; + } + + dependencies ??= new DependencyList(); + dependencies.Add(factory.ReflectableMethod(method), reason); + + return true; + } + + public static bool TryGetDependenciesForReflectedField(ref DependencyList dependencies, NodeFactory factory, FieldDesc field, string reason) + { + // If there's any genericness involved, try to create a fitting instantiation that would be usable at runtime. + // This is not a complete solution to the problem. + // If we ever decide that MakeGenericType/MakeGenericMethod should simply be considered unsafe, this code can be deleted + // and instantiations that are not fully closed can be ignored. + if (field.OwningType.IsGenericDefinition || field.OwningType.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) + { + TypeDesc owningType = field.OwningType.GetTypeDefinition(); + Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(owningType.Instantiation, allowCanon: true); + if (inst.IsNull) + { + return false; + } + + field = field.Context.GetFieldForInstantiatedType( + field.GetTypicalFieldDefinition(), + ((MetadataType)owningType).MakeInstantiatedType(inst)); + } + + if (factory.MetadataManager.IsReflectionBlocked(field)) + { + return false; + } + + if (!TryGetDependenciesForReflectedType(ref dependencies, factory, field.OwningType, reason)) + { + return false; + } + + // Currently generating the base of the type is enough to make the field reflectable. + + if (field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + return true; + } + + if (field.IsStatic && !field.IsLiteral && !field.HasRva) + { + bool cctorContextAdded = false; + if (field.IsThreadStatic) + { + dependencies.Add(factory.TypeThreadStaticIndex((MetadataType)field.OwningType), reason); + } + else if (field.HasGCStaticBase) + { + dependencies.Add(factory.TypeGCStaticsSymbol((MetadataType)field.OwningType), reason); + } + else + { + dependencies.Add(factory.TypeNonGCStaticsSymbol((MetadataType)field.OwningType), reason); + cctorContextAdded = true; + } + + if (!cctorContextAdded && factory.PreinitializationManager.HasLazyStaticConstructor(field.OwningType)) + { + dependencies.Add(factory.TypeNonGCStaticsSymbol((MetadataType)field.OwningType), reason); + } + } + + return true; + } + + public static bool TryGetDependenciesForReflectedType(ref DependencyList dependencies, NodeFactory factory, TypeDesc type, string reason) + { + try + { + // Instantiations with signature variables are not helpful - just use the definition. + if (type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) + { + type = type.GetTypeDefinition(); + } + + if (factory.MetadataManager.IsReflectionBlocked(type)) + { + return false; + } + + dependencies ??= new DependencyList(); + + dependencies.Add(factory.MaximallyConstructableType(type), reason); + + // If there's any unknown genericness involved, try to create a fitting instantiation that would be usable at runtime. + // This is not a complete solution to the problem. + // If we ever decide that MakeGenericType/MakeGenericMethod should simply be considered unsafe, this code can be deleted + // and instantiations that are not fully closed can be ignored. + if (type.IsGenericDefinition) + { + Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(type.Instantiation, allowCanon: true); + if (!inst.IsNull) + { + dependencies.Add(factory.MaximallyConstructableType(((MetadataType)type).MakeInstantiatedType(inst)), reason); + } + } + } + catch (TypeSystemException) + { + return false; + } + + return true; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs new file mode 100644 index 00000000000000..ce5dd8f931f919 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + internal delegate void RootAdder(object o, string reason); + + internal class RootingServiceProvider : IRootingServiceProvider + { + private readonly NodeFactory _factory; + private readonly RootAdder _rootAdder; + + public RootingServiceProvider(NodeFactory factory, RootAdder rootAdder) + { + _factory = factory; + _rootAdder = rootAdder; + } + + public void AddCompilationRoot(MethodDesc method, string reason, string exportName = null) + { + MethodDesc canonMethod = method.GetCanonMethodTarget(CanonicalFormKind.Specific); + IMethodNode methodEntryPoint = _factory.MethodEntrypoint(canonMethod); + _rootAdder(methodEntryPoint, reason); + + if (exportName != null) + _factory.NodeAliases.Add(methodEntryPoint, exportName); + + if (canonMethod != method && method.HasInstantiation) + _rootAdder(_factory.MethodGenericDictionary(method), reason); + } + + public void AddCompilationRoot(TypeDesc type, string reason) + { + _rootAdder(_factory.MaximallyConstructableType(type), reason); + } + + public void AddReflectionRoot(MethodDesc method, string reason) + { + if (!_factory.MetadataManager.IsReflectionBlocked(method)) + _rootAdder(_factory.ReflectableMethod(method), reason); + } + + public void RootThreadStaticBaseForType(TypeDesc type, string reason) + { + Debug.Assert(!type.IsGenericDefinition); + + MetadataType metadataType = type as MetadataType; + if (metadataType != null && metadataType.ThreadGcStaticFieldSize.AsInt > 0) + { + _rootAdder(_factory.TypeThreadStaticIndex(metadataType), reason); + + // Also explicitly root the non-gc base if we have a lazy cctor + if (_factory.PreinitializationManager.HasLazyStaticConstructor(type)) + _rootAdder(_factory.TypeNonGCStaticsSymbol(metadataType), reason); + } + } + + public void RootGCStaticBaseForType(TypeDesc type, string reason) + { + Debug.Assert(!type.IsGenericDefinition); + + MetadataType metadataType = type as MetadataType; + if (metadataType != null && metadataType.GCStaticFieldSize.AsInt > 0) + { + _rootAdder(_factory.TypeGCStaticsSymbol(metadataType), reason); + + // Also explicitly root the non-gc base if we have a lazy cctor + if (_factory.PreinitializationManager.HasLazyStaticConstructor(type)) + _rootAdder(_factory.TypeNonGCStaticsSymbol(metadataType), reason); + } + } + + public void RootNonGCStaticBaseForType(TypeDesc type, string reason) + { + Debug.Assert(!type.IsGenericDefinition); + + MetadataType metadataType = type as MetadataType; + if (metadataType != null && (metadataType.NonGCStaticFieldSize.AsInt > 0 || _factory.PreinitializationManager.HasLazyStaticConstructor(type))) + { + _rootAdder(_factory.TypeNonGCStaticsSymbol(metadataType), reason); + } + } + + public void RootModuleMetadata(ModuleDesc module, string reason) + { + // RootModuleMetadata is kind of a hack - this is pretty much only used to force include + // type forwarders from assemblies metadata generator would normally not look at. + // This will go away when the temporary RD.XML parser goes away. + if (_factory.MetadataManager is UsageBasedMetadataManager mdManager) + { + // If we wouldn't generate metadata for the global module type, don't root the metadata at all. + // Global module type always gets metadata and if we're not generating it, this is not the right + // compilation unit (we're likely doing multifile). + if (mdManager.CanGenerateMetadata(module.GetGlobalModuleType())) + { + _rootAdder(_factory.ModuleMetadata(module), reason); + } + } + } + + public void RootReadOnlyDataBlob(byte[] data, int alignment, string reason, string exportName) + { + var blob = _factory.ReadOnlyDataBlob("__readonlydata_" + exportName, data, alignment); + _rootAdder(blob, reason); + _factory.NodeAliases.Add(blob, exportName); + } + + public void RootDelegateMarshallingData(DefType type, string reason) + { + _rootAdder(_factory.DelegateMarshallingData(type), reason); + } + + public void RootStructMarshallingData(DefType type, string reason) + { + _rootAdder(_factory.StructMarshallingData(type), reason); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RuntimeConfigurationRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RuntimeConfigurationRootProvider.cs new file mode 100644 index 00000000000000..9a3facb6eeee17 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RuntimeConfigurationRootProvider.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace ILCompiler +{ + /// + /// A root provider that provides a runtime configuration blob that influences runtime behaviors. + /// See RhConfigValues.h for allowed values. + /// + public class RuntimeConfigurationRootProvider : ICompilationRootProvider + { + private readonly IEnumerable _runtimeOptions; + + public RuntimeConfigurationRootProvider(IEnumerable runtimeOptions) + { + _runtimeOptions = runtimeOptions; + } + + void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider) + { + rootProvider.RootReadOnlyDataBlob(GetRuntimeOptionsBlob(), 4, "Runtime configuration information", "g_compilerEmbeddedSettingsBlob"); + } + + protected byte[] GetRuntimeOptionsBlob() + { + const int HeaderSize = 4; + + ArrayBuilder options = new ArrayBuilder(); + + // Reserve space for the header + options.ZeroExtend(HeaderSize); + + foreach (string option in _runtimeOptions) + { + byte[] optionBytes = System.Text.Encoding.ASCII.GetBytes(option); + options.Append(optionBytes); + + // Emit a null to separate the next option + options.Add(0); + } + + byte[] result = options.ToArray(); + + int length = options.Count - HeaderSize; + + // Encode the size of the blob into the header + result[0] = (byte)length; + result[1] = (byte)(length >> 8); + result[2] = (byte)(length >> 0x10); + result[3] = (byte)(length >> 0x18); + + return result; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SingleFileCompilationModuleGroup.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SingleFileCompilationModuleGroup.cs new file mode 100644 index 00000000000000..09e08d12ab3b60 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SingleFileCompilationModuleGroup.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +using Internal.TypeSystem; + +namespace ILCompiler +{ + public class SingleFileCompilationModuleGroup : CompilationModuleGroup + { + public override bool ContainsType(TypeDesc type) + { + return true; + } + + public override bool ContainsTypeDictionary(TypeDesc type) + { + return true; + } + + public override bool ContainsMethodBody(MethodDesc method, bool unboxingStub) + { + return true; + } + + public override bool ContainsMethodDictionary(MethodDesc method) + { + Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method); + return ContainsMethodBody(method, false); + } + + public override bool ImportsMethod(MethodDesc method, bool unboxingStub) + { + return false; + } + + public override bool IsSingleFileCompilation + { + get + { + return true; + } + } + + public override bool ShouldProduceFullVTable(TypeDesc type) + { + return false; + } + + public override bool ShouldPromoteToFullType(TypeDesc type) + { + return false; + } + + public override bool PresenceOfEETypeImpliesAllMethodsOnType(TypeDesc type) + { + return false; + } + + public override bool ShouldReferenceThroughImportTable(TypeDesc type) + { + return false; + } + + public override bool CanHaveReferenceThroughImportTable + { + get + { + return false; + } + } + + public override bool AllowInstanceMethodOptimization(MethodDesc method) + { + return true; + } + + public override bool AllowVirtualMethodOnAbstractTypeOptimization(MethodDesc method) + { + return true; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SingleMethodCompilationModuleGroup.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SingleMethodCompilationModuleGroup.cs new file mode 100644 index 00000000000000..3d8738cf8cd688 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SingleMethodCompilationModuleGroup.cs @@ -0,0 +1,95 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// A compilation group that only contains a single method. Useful for development purposes when investigating + /// code generation issues. + /// + public class SingleMethodCompilationModuleGroup : CompilationModuleGroup + { + private MethodDesc _method; + + public SingleMethodCompilationModuleGroup(MethodDesc method) + { + _method = method; + } + + public override bool IsSingleFileCompilation + { + get + { + return false; + } + } + + public override bool ContainsMethodBody(MethodDesc method, bool unboxingStub) + { + return method == _method; + } + + public sealed override bool ContainsMethodDictionary(MethodDesc method) + { + Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method); + return true; + } + + public override bool ContainsType(TypeDesc type) + { + return true; + } + + public override bool ContainsTypeDictionary(TypeDesc type) + { + return true; + } + + public override bool ImportsMethod(MethodDesc method, bool unboxingStub) + { + return false; + } + + public override bool ShouldProduceFullVTable(TypeDesc type) + { + return false; + } + + public override bool ShouldPromoteToFullType(TypeDesc type) + { + return false; + } + + public override bool PresenceOfEETypeImpliesAllMethodsOnType(TypeDesc type) + { + return false; + } + + public override bool ShouldReferenceThroughImportTable(TypeDesc type) + { + return false; + } + + public override bool CanHaveReferenceThroughImportTable + { + get + { + return false; + } + } + + public override bool AllowInstanceMethodOptimization(MethodDesc method) + { + return false; + } + + public override bool AllowVirtualMethodOnAbstractTypeOptimization(MethodDesc method) + { + return false; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/StackTraceEmissionPolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/StackTraceEmissionPolicy.cs new file mode 100644 index 00000000000000..08c891f95d5352 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/StackTraceEmissionPolicy.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Represents a stack trace emission policy. + /// + public abstract class StackTraceEmissionPolicy + { + public abstract bool ShouldIncludeMethod(MethodDesc method); + } + + public class NoStackTraceEmissionPolicy : StackTraceEmissionPolicy + { + public override bool ShouldIncludeMethod(MethodDesc method) + { + return false; + } + } + + /// + /// Stack trace emission policy that ensures presence of stack trace metadata for all + /// -based methods. + /// + public class EcmaMethodStackTraceEmissionPolicy : StackTraceEmissionPolicy + { + public override bool ShouldIncludeMethod(MethodDesc method) + { + return method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs new file mode 100644 index 00000000000000..1c139739b298c2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs @@ -0,0 +1,2374 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using ILCompiler.DependencyAnalysis; + +using Internal.IL; +using Internal.TypeSystem; + +namespace ILCompiler +{ + // Class that computes the initial state of static fields on a type by interpreting the static constructor. + // + // Values are represented by instances of an abstract Value class. Several specialized descendants of + // the Value class exist, representing value types (including e.g. a specialized class representing + // RuntimeFieldHandle), or reference types (including e.g. specialized class representing an array). + // + // For simplicity, non-reference values are represented as byte arrays. This requires many short lived array + // allocations, but makes a lot of things simpler (e.g. byrefs to values are essentially free because they + // only carry a reference to the original array and an optional index). + // + // When dealing with non-reference types (valuetypes and unmanaged pointers) we need to be careful + // about assignment semantics. Some operations need to make a copy of the valuetype bytes while others + // are fine to reuse the original byte array. Whenever storing a value into a location, we need to assign + // a new value to the existing Value instance to keep byrefs working. + public class TypePreinit + { + private readonly MetadataType _type; + private readonly CompilationModuleGroup _compilationGroup; + private readonly ILProvider _ilProvider; + private readonly Dictionary _fieldValues = new Dictionary(); + private readonly Dictionary _internedStrings = new Dictionary(); + + private TypePreinit(MetadataType owningType, CompilationModuleGroup compilationGroup, ILProvider ilProvider) + { + _type = owningType; + _compilationGroup = compilationGroup; + _ilProvider = ilProvider; + + // Zero initialize all fields we model. + foreach (var field in owningType.GetFields()) + { + if (!field.IsStatic || field.IsLiteral || field.IsThreadStatic || field.HasRva) + continue; + + _fieldValues.Add(field, NewUninitializedLocationValue(field.FieldType)); + } + } + + // Could potentially expose this as a policy class. When type loader is not present or + // the given type can't be constructed by the type loader, preinitialization could still + // happen. + private static bool CanPreinitializeByPolicy(TypeDesc type) + { + // If the type has a canonical form the runtime type loader could create + // a new type sharing code with this one. They need to agree on how + // initialization happens. We can't preinitialize runtime-created + // generic types at compile time. + if (type.ConvertToCanonForm(CanonicalFormKind.Specific) + .IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; + + return true; + } + + public static PreinitializationInfo ScanType(CompilationModuleGroup compilationGroup, ILProvider ilProvider, MetadataType type) + { + Debug.Assert(type.HasStaticConstructor); + Debug.Assert(!type.IsGenericDefinition); + + if (!CanPreinitializeByPolicy(type)) + return new PreinitializationInfo(type, "Disallowed by policy"); + + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any)); + + TypePreinit preinit = null; + + Status status; + try + { + preinit = new TypePreinit(type, compilationGroup, ilProvider); + int instructions = 0; + status = preinit.TryScanMethod(type.GetStaticConstructor(), null, null, ref instructions, out _); + } + catch (TypeSystemException ex) + { + status = Status.Fail(type.GetStaticConstructor(), ex.Message); + } + + if (status.IsSuccessful) + { + var values = new List>(); + foreach (var kvp in preinit._fieldValues) + values.Add(new KeyValuePair(kvp.Key, kvp.Value)); + + return new PreinitializationInfo(type, values); + } + + return new PreinitializationInfo(type, status.FailureReason); + } + + private Status TryScanMethod(MethodDesc method, Value[] parameters, Stack recursionProtect, ref int instructionCounter, out Value returnValue) + { + MethodIL methodIL = _ilProvider.GetMethodIL(method); + if (methodIL == null) + { + returnValue = null; + return Status.Fail(method, "Extern method"); + } + + return TryScanMethod(methodIL, parameters, recursionProtect, ref instructionCounter, out returnValue); + } + + private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack recursionProtect, ref int instructionCounter, out Value returnValue) + { + returnValue = default; + + if (recursionProtect != null && recursionProtect.Contains(methodIL.OwningMethod)) + return Status.Fail(methodIL.OwningMethod, "Recursion"); + + ILExceptionRegion[] ehRegions = methodIL.GetExceptionRegions(); + if (ehRegions != null && ehRegions.Length > 0) + { + // We don't care about catch/filter/fault because those only run when an exception happens + // (exceptions will never happen here). But finally needs to run in non-exceptional paths + // and we don't model that yet. + foreach (ILExceptionRegion ehRegion in ehRegions) + { + if (ehRegion.Kind == ILExceptionRegionKind.Finally) + return Status.Fail(methodIL.OwningMethod, "Finally regions"); + } + } + + var reader = new ILReader(methodIL.GetILBytes()); + + TypeSystemContext context = methodIL.OwningMethod.Context; + + var stack = new Stack(methodIL.MaxStack, context.Target); + + LocalVariableDefinition[] localTypes = methodIL.GetLocals(); + Value[] locals = new Value[localTypes.Length]; + for (int i = 0; i < localTypes.Length; i++) + { + locals[i] = NewUninitializedLocationValue(localTypes[i].Type); + } + + // Read IL opcodes and interpret their semantics. + // + // This is not a full interpreter and we're allowed to not interpret everything. If a semantic is + // not implemented by the interpreter, we simply fail. + // + // We also need to do basic sanity checking for invalid IL to protect us from crashing. These + // all throw the TypeSystem's InvalidProgramException. The exception doesn't need to exactly match + // the runtime exception. We just need something reasonably catchable to abort interpreting. + // + // We throw instead of returning false to aid debuggability of the interpreter (we shouldn't see + // exceptions in normal code so an exception is usually a bug). + + while (reader.HasNext) + { + if (instructionCounter == 100000) + return Status.Fail(methodIL.OwningMethod, "Instruction limit"); + + instructionCounter++; + + ILOpcode opcode = reader.ReadILOpcode(); + switch (opcode) + { + case ILOpcode.ldc_i4_m1: + case ILOpcode.ldc_i4_s: + case ILOpcode.ldc_i4: + case ILOpcode.ldc_i4_0: + case ILOpcode.ldc_i4_1: + case ILOpcode.ldc_i4_2: + case ILOpcode.ldc_i4_3: + case ILOpcode.ldc_i4_4: + case ILOpcode.ldc_i4_5: + case ILOpcode.ldc_i4_6: + case ILOpcode.ldc_i4_7: + case ILOpcode.ldc_i4_8: + { + int value = opcode switch + { + ILOpcode.ldc_i4_m1 => -1, + ILOpcode.ldc_i4_s => (sbyte)reader.ReadILByte(), + ILOpcode.ldc_i4 => (int)reader.ReadILUInt32(), + _ => opcode - ILOpcode.ldc_i4_0, + }; + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(value)); + } + break; + + case ILOpcode.ldc_i8: + stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((long)reader.ReadILUInt64())); + break; + + case ILOpcode.ldc_r4: + case ILOpcode.ldc_r8: + stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble( + opcode == ILOpcode.ldc_r4 ? reader.ReadILFloat() : reader.ReadILDouble())); + break; + + case ILOpcode.sizeof_: + { + TypeDesc type = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(type.GetElementSize().AsInt)); + } + break; + + case ILOpcode.ldnull: + stack.Push((ReferenceTypeValue)null); + break; + + case ILOpcode.newarr: + { + if (!stack.TryPopIntValue(out int elementCount)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + const int MaximumInterpretedArraySize = 8192; + + TypeDesc elementType = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); + if (elementCount > 0 + && (elementType.IsGCPointer + || (elementType.IsValueType && ((DefType)elementType).ContainsGCPointers))) + { + return Status.Fail(methodIL.OwningMethod, opcode, "GC pointers"); + } + + if (elementCount < 0 + || elementCount > MaximumInterpretedArraySize) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Array out of bounds"); + } + + stack.Push(new ArrayInstance(elementType.MakeArrayType(), elementCount)); + } + break; + + case ILOpcode.dup: + if (stack.Count == 0) + { + ThrowHelper.ThrowInvalidProgramException(); + } + stack.Push(stack.Peek()); + break; + + case ILOpcode.ldstr: + { + string s = (string)methodIL.GetObject(reader.ReadILToken()); + if (!_internedStrings.TryGetValue(s, out StringInstance instance)) + { + instance = new StringInstance(context.GetWellKnownType(WellKnownType.String), s); + _internedStrings.Add(s, instance); + } + stack.Push(instance); + } + break; + + case ILOpcode.ret: + { + bool returnsVoid = methodIL.OwningMethod.Signature.ReturnType.IsVoid; + if ((returnsVoid && stack.Count > 0) + || (!returnsVoid && stack.Count != 1)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + if (!returnsVoid) + { + returnValue = stack.PopIntoLocation(methodIL.OwningMethod.Signature.ReturnType); + } + return Status.Success; + } + + case ILOpcode.nop: + case ILOpcode.volatile_: + break; + + case ILOpcode.stsfld: + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + if (!field.IsStatic || field.IsLiteral) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + if (field.OwningType != _type) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Store into other static"); + } + + if (field.IsThreadStatic || field.HasRva) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported static"); + } + + if (_fieldValues[field] is IAssignableValue assignableField) + { + if (!assignableField.TryAssign(stack.PopIntoLocation(field.FieldType))) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported store"); + } + } + else + { + Value value = stack.PopIntoLocation(field.FieldType); + if (value is IInternalModelingOnlyValue) + return Status.Fail(methodIL.OwningMethod, opcode, "Value with no external representation"); + _fieldValues[field] = value; + } + } + break; + + case ILOpcode.ldsfld: + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + if (!field.IsStatic || field.IsLiteral) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + if (field.IsThreadStatic || field.HasRva) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported static"); + } + + if (field.OwningType == _type) + { + stack.PushFromLocation(field.FieldType, _fieldValues[field]); + } + else if (field.IsInitOnly + && field.OwningType.HasStaticConstructor + && CanPreinitializeByPolicy(field.OwningType)) + { + TypePreinit nestedPreinit = new TypePreinit((MetadataType)field.OwningType, _compilationGroup, _ilProvider); + recursionProtect ??= new Stack(); + recursionProtect.Push(methodIL.OwningMethod); + Status status = nestedPreinit.TryScanMethod(field.OwningType.GetStaticConstructor(), null, recursionProtect, ref instructionCounter, out Value _); + if (!status.IsSuccessful) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Nested cctor failed to preinit"); + } + recursionProtect.Pop(); + Value value = nestedPreinit._fieldValues[field]; + if (value is ValueTypeValue) + stack.PushFromLocation(field.FieldType, value); + else if (value is ReferenceTypeValue referenceType) + stack.PushFromLocation(field.FieldType, new ForeignTypeInstance(referenceType.Type, field, referenceType)); + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + else + { + return Status.Fail(methodIL.OwningMethod, opcode, "Load from other non-initonly static"); + } + } + break; + + case ILOpcode.ldsflda: + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + if (!field.IsStatic || field.IsLiteral) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + if (field.OwningType != _type) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Address of other static"); + } + + if (field.IsThreadStatic || field.HasRva) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported static"); + } + + Value fieldValue = _fieldValues[field]; + if (fieldValue == null || !fieldValue.TryCreateByRef(out Value byRefValue)) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported byref"); + } + + stack.Push(StackValueKind.ByRef, byRefValue); + } + break; + + case ILOpcode.call: + { + MethodDesc method = (MethodDesc)methodIL.GetObject(reader.ReadILToken()); + MethodSignature methodSig = method.Signature; + int paramOffset = methodSig.IsStatic ? 0 : 1; + int numParams = methodSig.Length + paramOffset; + + TypeDesc owningType = method.OwningType; + if (!_compilationGroup.CanInline(methodIL.OwningMethod, method)) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Cannot inline"); + } + + if (owningType.HasStaticConstructor + && owningType != methodIL.OwningMethod.OwningType + && !((MetadataType)owningType).IsBeforeFieldInit) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Static constructor"); + } + + Value[] methodParams = new Value[numParams]; + for (int i = numParams - 1; i >= 0; i--) + { + methodParams[i] = stack.PopIntoLocation(GetArgType(method, i)); + } + + Value retVal; + if (!method.IsIntrinsic || !TryHandleIntrinsicCall(method, methodParams, out retVal)) + { + recursionProtect ??= new Stack(); + recursionProtect.Push(methodIL.OwningMethod); + Status callResult = TryScanMethod(method, methodParams, recursionProtect, ref instructionCounter, out retVal); + if (!callResult.IsSuccessful) + { + recursionProtect.Pop(); + return callResult; + } + recursionProtect.Pop(); + } + + if (!methodSig.ReturnType.IsVoid) + stack.PushFromLocation(methodSig.ReturnType, retVal); + } + break; + + case ILOpcode.newobj: + { + MethodDesc ctor = (MethodDesc)methodIL.GetObject(reader.ReadILToken()); + MethodSignature ctorSig = ctor.Signature; + + TypeDesc owningType = ctor.OwningType; + if (!_compilationGroup.CanInline(methodIL.OwningMethod, ctor) + || !_compilationGroup.ContainsType(owningType)) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Cannot inline"); + } + + if (owningType.HasStaticConstructor + && owningType != methodIL.OwningMethod.OwningType + && !((MetadataType)owningType).IsBeforeFieldInit) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Static constructor"); + } + + if (!owningType.IsDefType) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Not a class or struct"); + } + + if (owningType.HasFinalizer) + { + // Finalizer might have observable side effects + return Status.Fail(methodIL.OwningMethod, opcode, "Finalizable class"); + } + + Value[] ctorParameters = new Value[ctorSig.Length + 1]; + for (int i = ctorSig.Length - 1; i >= 0; i--) + { + ctorParameters[i + 1] = stack.PopIntoLocation(GetArgType(ctor, i + 1)); + } + + Value instance; + if (owningType.IsDelegate) + { + if (!(ctorParameters[2] is MethodPointerValue methodPointer)) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Unverifiable delegate creation"); + } + + ForeignTypeInstance firstParameter = null; + if (ctorParameters[1] != null) + { + // We only have a way to refer to an allocated object if it's referenced from a static + // field of a different type. This conveniently matches delegates that the C# compiler creates + // for lambdas: this is more common than it sounds. + firstParameter = ctorParameters[1] as ForeignTypeInstance; + if (firstParameter == null) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Delegate with an unsupported first parameter"); + } + } + + MethodDesc pointedMethod = methodPointer.PointedToMethod; + if ((firstParameter == null) != pointedMethod.Signature.IsStatic) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Open/closed static/instance delegate mismatch"); + } + + if (firstParameter != null && pointedMethod.HasInstantiation) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Delegate with fat pointer"); + } + + instance = new DelegateInstance(owningType, pointedMethod, firstParameter); + } + else + { + if (owningType.IsValueType) + { + instance = new ValueTypeValue(owningType); + bool byrefCreated = instance.TryCreateByRef(out ctorParameters[0]); + Debug.Assert(byrefCreated); + } + else + { + instance = new ObjectInstance((DefType)owningType); + ctorParameters[0] = instance; + } + + if (((DefType)owningType).ContainsGCPointers) + { + // We don't want to end up with GC pointers in the frozen region + // because write barriers can't handle that. + + // We can make an exception for readonly fields. + bool allGcPointersAreReadonly = true; + TypeDesc currentType = owningType; + do + { + foreach (FieldDesc field in currentType.GetFields()) + { + if (field.IsStatic) + continue; + + TypeDesc fieldType = field.FieldType; + if (fieldType.IsGCPointer) + { + if (!field.IsInitOnly) + { + allGcPointersAreReadonly = false; + break; + } + } + else if (fieldType.IsValueType && ((DefType)fieldType).ContainsGCPointers) + { + allGcPointersAreReadonly = false; + break; + } + } + } while (allGcPointersAreReadonly && (currentType = currentType.BaseType) != null && !currentType.IsValueType); + + if (!allGcPointersAreReadonly) + return Status.Fail(methodIL.OwningMethod, opcode, "GC pointers"); + } + + recursionProtect ??= new Stack(); + recursionProtect.Push(methodIL.OwningMethod); + Status ctorCallResult = TryScanMethod(ctor, ctorParameters, recursionProtect, ref instructionCounter, out _); + if (!ctorCallResult.IsSuccessful) + { + recursionProtect.Pop(); + return ctorCallResult; + } + + recursionProtect.Pop(); + } + + stack.PushFromLocation(owningType, instance); + } + break; + + case ILOpcode.stfld: + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + + if (field.IsStatic) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Static field with stfld"); + } + + Value value = stack.PopIntoLocation(field.FieldType); + StackEntry instance = stack.Pop(); + + if (field.FieldType.IsGCPointer && value != null) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Reference field"); + } + + var settableInstance = instance.Value as IHasInstanceFields; + if (settableInstance == null) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + settableInstance.SetField(field, value); + } + break; + + case ILOpcode.ldfld: + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + + if (field.FieldType.IsGCPointer + || field.IsStatic) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + StackEntry instance = stack.Pop(); + + var loadableInstance = instance.Value as IHasInstanceFields; + if (loadableInstance == null) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + Value fieldValue = loadableInstance.GetField(field); + + stack.PushFromLocation(field.FieldType, fieldValue); + } + break; + + case ILOpcode.ldflda: + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + if (field.FieldType.IsGCPointer + || field.IsStatic) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + StackEntry instance = stack.Pop(); + + var loadableInstance = instance.Value as IHasInstanceFields; + if (loadableInstance == null) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + stack.Push(StackValueKind.ByRef, loadableInstance.GetFieldAddress(field)); + } + break; + + case ILOpcode.conv_i: + case ILOpcode.conv_u: + case ILOpcode.conv_i2: + case ILOpcode.conv_i4: + case ILOpcode.conv_i8: + case ILOpcode.conv_u2: + case ILOpcode.conv_u8: + { + StackEntry popped = stack.Pop(); + if (popped.ValueKind == StackValueKind.Int32) + { + int val = popped.Value.AsInt32(); + switch (opcode) + { + case ILOpcode.conv_i: + stack.Push(StackValueKind.NativeInt, + context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64(val) : ValueTypeValue.FromInt32(val)); + break; + case ILOpcode.conv_u: + stack.Push(StackValueKind.NativeInt, + context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64((uint)val) : ValueTypeValue.FromInt32(val)); + break; + case ILOpcode.conv_i2: + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((short)val)); + break; + case ILOpcode.conv_i8: + stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(val)); + break; + case ILOpcode.conv_u2: + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((ushort)val)); + break; + case ILOpcode.conv_u8: + stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((uint)val)); + break; + default: + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + else if (popped.ValueKind == StackValueKind.NativeInt) + { + long val = context.Target.PointerSize == 8 ? popped.Value.AsInt64() : popped.Value.AsInt32(); + switch (opcode) + { + case ILOpcode.conv_i4: + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((int)val)); + break; + default: + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + else if (popped.ValueKind == StackValueKind.Int64) + { + long val = popped.Value.AsInt64(); + switch (opcode) + { + case ILOpcode.conv_u: + stack.Push(StackValueKind.NativeInt, + context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64(val) : ValueTypeValue.FromInt32((int)val)); + break; + } + } + else if (popped.ValueKind == StackValueKind.Float) + { + double val = popped.Value.AsDouble(); + switch (opcode) + { + case ILOpcode.conv_i8: + stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((long)val)); + break; + default: + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + else + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + break; + + case ILOpcode.ldarg_0: + case ILOpcode.ldarg_1: + case ILOpcode.ldarg_2: + case ILOpcode.ldarg_3: + case ILOpcode.ldarg_s: + case ILOpcode.ldarg: + { + int index = opcode switch + { + ILOpcode.ldarg_s => reader.ReadILByte(), + ILOpcode.ldarg => reader.ReadILUInt16(), + _ => opcode - ILOpcode.ldarg_0, + }; + stack.PushFromLocation(GetArgType(methodIL.OwningMethod, index), parameters[index]); + } + break; + + case ILOpcode.starg_s: + case ILOpcode.starg: + { + int index = opcode == ILOpcode.starg ? reader.ReadILUInt16() : reader.ReadILByte(); + TypeDesc argType = GetArgType(methodIL.OwningMethod, index); + if (parameters[index] is IAssignableValue assignableParam) + { + if (!assignableParam.TryAssign(stack.PopIntoLocation(argType))) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Unsuported store"); + } + } + else + parameters[index] = stack.PopIntoLocation(argType); + } + break; + + case ILOpcode.ldtoken: + { + var token = methodIL.GetObject(reader.ReadILToken()); + if (!(token is FieldDesc field)) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + stack.Push(new StackEntry(StackValueKind.ValueType, new RuntimeFieldHandleValue(field))); + } + break; + + case ILOpcode.ldftn: + { + var method = methodIL.GetObject(reader.ReadILToken()) as MethodDesc; + if (method != null) + stack.Push(StackValueKind.NativeInt, new MethodPointerValue(method)); + else + ThrowHelper.ThrowInvalidProgramException(); + } + break; + + case ILOpcode.ldloc_0: + case ILOpcode.ldloc_1: + case ILOpcode.ldloc_2: + case ILOpcode.ldloc_3: + case ILOpcode.ldloc_s: + case ILOpcode.ldloc: + { + int index = opcode switch + { + ILOpcode.ldloc_s => reader.ReadILByte(), + ILOpcode.ldloc => reader.ReadILUInt16(), + _ => opcode - ILOpcode.ldloc_0, + }; + + if (index >= locals.Length) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + stack.PushFromLocation(localTypes[index].Type, locals[index]); + } + break; + + case ILOpcode.stloc_0: + case ILOpcode.stloc_1: + case ILOpcode.stloc_2: + case ILOpcode.stloc_3: + case ILOpcode.stloc_s: + case ILOpcode.stloc: + { + int index = opcode switch + { + ILOpcode.stloc_s => reader.ReadILByte(), + ILOpcode.stloc => reader.ReadILUInt16(), + _ => opcode - ILOpcode.stloc_0, + }; + + if (index >= locals.Length) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + TypeDesc localType = localTypes[index].Type; + if (locals[index] is IAssignableValue assignableLocal) + { + if (!assignableLocal.TryAssign(stack.PopIntoLocation(localType))) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported store"); + } + } + else + locals[index] = stack.PopIntoLocation(localType); + + } + break; + + case ILOpcode.ldloca_s: + case ILOpcode.ldloca: + { + int index = opcode switch + { + ILOpcode.ldloca_s => reader.ReadILByte(), + ILOpcode.ldloca => reader.ReadILUInt16(), + _ => throw new NotImplementedException(), // Unreachable + }; + + if (index >= locals.Length) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + Value localValue = locals[index]; + if (localValue == null || !localValue.TryCreateByRef(out Value byrefValue)) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + else + { + stack.Push(StackValueKind.ByRef, byrefValue); + } + } + break; + + case ILOpcode.initobj: + { + StackEntry popped = stack.Pop(); + if (popped.ValueKind != StackValueKind.ByRef) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + TypeDesc token = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); + if (token.IsGCPointer) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + ((ByRefValue)popped.Value).Initialize(token.GetElementSize().AsInt); + } + break; + + case ILOpcode.br: + case ILOpcode.brfalse: + case ILOpcode.brtrue: + case ILOpcode.blt: + case ILOpcode.blt_un: + case ILOpcode.bgt: + case ILOpcode.bgt_un: + case ILOpcode.beq: + case ILOpcode.bne_un: + case ILOpcode.bge: + case ILOpcode.bge_un: + case ILOpcode.ble: + case ILOpcode.ble_un: + case ILOpcode.br_s: + case ILOpcode.brfalse_s: + case ILOpcode.brtrue_s: + case ILOpcode.blt_s: + case ILOpcode.blt_un_s: + case ILOpcode.bgt_s: + case ILOpcode.bgt_un_s: + case ILOpcode.beq_s: + case ILOpcode.bne_un_s: + case ILOpcode.bge_s: + case ILOpcode.bge_un_s: + case ILOpcode.ble_s: + case ILOpcode.ble_un_s: + { + int delta = opcode >= ILOpcode.br ? + (int)reader.ReadILUInt32() : + (sbyte)reader.ReadILByte(); + int target = reader.Offset + delta; + if (target < 0 + || target > reader.Size) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + ILOpcode normalizedOpcode = opcode >= ILOpcode.br ? + opcode - ILOpcode.br + ILOpcode.br_s: + opcode; + + bool branchTaken; + if (normalizedOpcode == ILOpcode.brtrue_s || normalizedOpcode == ILOpcode.brfalse_s) + { + StackEntry condition = stack.Pop(); + if (condition.ValueKind == StackValueKind.Int32 || (condition.ValueKind == StackValueKind.NativeInt && context.Target.PointerSize == 4)) + branchTaken = normalizedOpcode == ILOpcode.brfalse_s + ? condition.Value.AsInt32() == 0 : condition.Value.AsInt32() != 0; + else if (condition.ValueKind == StackValueKind.Int64 || (condition.ValueKind == StackValueKind.NativeInt && context.Target.PointerSize == 8)) + branchTaken = normalizedOpcode == ILOpcode.brfalse_s + ? condition.Value.AsInt64() == 0 : condition.Value.AsInt64() != 0; + else if (condition.ValueKind == StackValueKind.ObjRef) + branchTaken = normalizedOpcode == ILOpcode.brfalse_s + ? condition.Value == null : condition.Value != null; + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + else if (normalizedOpcode == ILOpcode.blt_s || normalizedOpcode == ILOpcode.bgt_s + || normalizedOpcode == ILOpcode.bge_s || normalizedOpcode == ILOpcode.beq_s + || normalizedOpcode == ILOpcode.ble_s || normalizedOpcode == ILOpcode.blt_un_s + || normalizedOpcode == ILOpcode.ble_un_s || normalizedOpcode == ILOpcode.bge_un_s + || normalizedOpcode == ILOpcode.bgt_un_s || normalizedOpcode == ILOpcode.bne_un_s) + { + StackEntry value2 = stack.Pop(); + StackEntry value1 = stack.Pop(); + + if (value1.ValueKind == StackValueKind.Int32 && value2.ValueKind == StackValueKind.Int32) + { + branchTaken = normalizedOpcode switch + { + ILOpcode.blt_s => value1.Value.AsInt32() < value2.Value.AsInt32(), + ILOpcode.blt_un_s => (uint)value1.Value.AsInt32() < (uint)value2.Value.AsInt32(), + ILOpcode.bgt_s => value1.Value.AsInt32() > value2.Value.AsInt32(), + ILOpcode.bgt_un_s => (uint)value1.Value.AsInt32() > (uint)value2.Value.AsInt32(), + ILOpcode.bge_s => value1.Value.AsInt32() >= value2.Value.AsInt32(), + ILOpcode.bge_un_s => (uint)value1.Value.AsInt32() >= (uint)value2.Value.AsInt32(), + ILOpcode.beq_s => value1.Value.AsInt32() == value2.Value.AsInt32(), + ILOpcode.bne_un_s => value1.Value.AsInt32() != value2.Value.AsInt32(), + ILOpcode.ble_s => value1.Value.AsInt32() <= value2.Value.AsInt32(), + ILOpcode.ble_un_s => (uint)value1.Value.AsInt32() <= (uint)value2.Value.AsInt32(), + _ => throw new NotImplementedException() // unreachable + }; + } + else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int64) + { + branchTaken = normalizedOpcode switch + { + ILOpcode.blt_s => value1.Value.AsInt64() < value2.Value.AsInt64(), + ILOpcode.blt_un_s => (ulong)value1.Value.AsInt64() < (ulong)value2.Value.AsInt64(), + ILOpcode.bgt_s => value1.Value.AsInt64() > value2.Value.AsInt64(), + ILOpcode.bgt_un_s => (ulong)value1.Value.AsInt64() > (ulong)value2.Value.AsInt64(), + ILOpcode.bge_s => value1.Value.AsInt64() >= value2.Value.AsInt64(), + ILOpcode.bge_un_s => (ulong)value1.Value.AsInt64() >= (ulong)value2.Value.AsInt64(), + ILOpcode.beq_s => value1.Value.AsInt64() == value2.Value.AsInt64(), + ILOpcode.bne_un_s => value1.Value.AsInt64() != value2.Value.AsInt64(), + ILOpcode.ble_s => value1.Value.AsInt64() <= value2.Value.AsInt64(), + ILOpcode.ble_un_s => (ulong)value1.Value.AsInt64() <= (ulong)value2.Value.AsInt64(), + _ => throw new NotImplementedException() // unreachable + }; + } + else if (value1.ValueKind == StackValueKind.Float && value2.ValueKind == StackValueKind.Float) + { + branchTaken = normalizedOpcode switch + { + ILOpcode.blt_s => value1.Value.AsDouble() < value2.Value.AsDouble(), + ILOpcode.blt_un_s => !(value1.Value.AsDouble() >= value2.Value.AsDouble()), + ILOpcode.bgt_s => value1.Value.AsDouble() > value2.Value.AsDouble(), + ILOpcode.bgt_un_s => !(value1.Value.AsDouble() <= value2.Value.AsDouble()), + ILOpcode.bge_s => value1.Value.AsDouble() >= value2.Value.AsDouble(), + ILOpcode.bge_un_s => !(value1.Value.AsDouble() < value2.Value.AsDouble()), + ILOpcode.beq_s => value1.Value.AsDouble() == value2.Value.AsDouble(), + ILOpcode.bne_un_s => value1.Value.AsDouble() != value2.Value.AsDouble(), + ILOpcode.ble_s => value1.Value.AsDouble() <= value2.Value.AsDouble(), + ILOpcode.ble_un_s => !(value1.Value.AsDouble() > value2.Value.AsDouble()), + _ => throw new NotImplementedException() // unreachable + }; + } + else + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + else + { + Debug.Assert(normalizedOpcode == ILOpcode.br_s); + branchTaken = true; + } + + if (branchTaken) + { + reader.Seek(target); + } + } + break; + + case ILOpcode.leave: + case ILOpcode.leave_s: + { + stack.Clear(); + + // We assume no finally regions (would have to run them here) + // This is validated before, but we're being paranoid. + foreach (ILExceptionRegion ehRegion in ehRegions) + { + Debug.Assert(ehRegion.Kind != ILExceptionRegionKind.Finally); + } + + int delta = opcode == ILOpcode.leave ? + (int)reader.ReadILUInt32() : + (sbyte)reader.ReadILByte(); + int target = reader.Offset + delta; + if (target < 0 + || target > reader.Size) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + reader.Seek(target); + } + break; + + case ILOpcode.clt: + case ILOpcode.clt_un: + case ILOpcode.cgt: + case ILOpcode.cgt_un: + { + StackEntry value1 = stack.Pop(); + StackEntry value2 = stack.Pop(); + + bool condition; + if (value1.ValueKind == StackValueKind.Int32 && value2.ValueKind == StackValueKind.Int32) + { + if (opcode == ILOpcode.cgt) + condition = value1.Value.AsInt32() < value2.Value.AsInt32(); + else if (opcode == ILOpcode.cgt_un) + condition = (uint)value1.Value.AsInt32() < (uint)value2.Value.AsInt32(); + else if (opcode == ILOpcode.clt) + condition = value1.Value.AsInt32() > value2.Value.AsInt32(); + else if (opcode == ILOpcode.clt_un) + condition = (uint)value1.Value.AsInt32() > (uint)value2.Value.AsInt32(); + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int64) + { + if (opcode == ILOpcode.cgt) + condition = value1.Value.AsInt64() < value2.Value.AsInt64(); + else if (opcode == ILOpcode.cgt_un) + condition = (ulong)value1.Value.AsInt64() < (ulong)value2.Value.AsInt64(); + else if (opcode == ILOpcode.clt) + condition = value1.Value.AsInt64() > value2.Value.AsInt64(); + else if (opcode == ILOpcode.clt_un) + condition = (ulong)value1.Value.AsInt64() > (ulong)value2.Value.AsInt64(); + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + else if (value1.ValueKind == StackValueKind.Float && value2.ValueKind == StackValueKind.Float) + { + if (opcode == ILOpcode.cgt) + condition = value1.Value.AsDouble() < value2.Value.AsDouble(); + else if (opcode == ILOpcode.cgt_un) + condition = !(value1.Value.AsDouble() >= value2.Value.AsDouble()); + else if (opcode == ILOpcode.clt) + condition = value1.Value.AsDouble() > value2.Value.AsDouble(); + else if (opcode == ILOpcode.clt_un) + condition = !(value1.Value.AsDouble() <= value2.Value.AsDouble()); + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + else if (value1.ValueKind == StackValueKind.ObjRef && value2.ValueKind == StackValueKind.ObjRef) + { + if (opcode == ILOpcode.cgt_un) + condition = value1.Value == null && value2.Value != null; + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + else + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + stack.Push(StackValueKind.Int32, condition + ? ValueTypeValue.FromInt32(1) + : ValueTypeValue.FromInt32(0)); + } + break; + + case ILOpcode.ceq: + { + StackEntry value1 = stack.Pop(); + StackEntry value2 = stack.Pop(); + + if (value1.ValueKind == value2.ValueKind) + { + stack.Push(StackValueKind.Int32, + Value.Equals(value1.Value, value2.Value) + ? ValueTypeValue.FromInt32(1) + : ValueTypeValue.FromInt32(0)); + } + else + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + break; + + case ILOpcode.neg: + { + StackEntry value = stack.Pop(); + if (value.ValueKind == StackValueKind.Int32) + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(-value.Value.AsInt32())); + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + break; + + case ILOpcode.or: + case ILOpcode.shl: + case ILOpcode.add: + case ILOpcode.sub: + case ILOpcode.mul: + case ILOpcode.and: + case ILOpcode.div: + case ILOpcode.div_un: + case ILOpcode.rem: + case ILOpcode.rem_un: + { + bool isDivRem = opcode == ILOpcode.div || opcode == ILOpcode.div_un + || opcode == ILOpcode.rem || opcode == ILOpcode.rem_un; + + StackEntry value2 = stack.Pop(); + StackEntry value1 = stack.Pop(); + if (value1.ValueKind == StackValueKind.Int32 && value2.ValueKind == StackValueKind.Int32) + { + if (isDivRem && value2.Value.AsInt32() == 0) + return Status.Fail(methodIL.OwningMethod, opcode, "Division by zero"); + + int result = opcode switch + { + ILOpcode.or => value1.Value.AsInt32() | value2.Value.AsInt32(), + ILOpcode.shl => value1.Value.AsInt32() << value2.Value.AsInt32(), + ILOpcode.add => value1.Value.AsInt32() + value2.Value.AsInt32(), + ILOpcode.sub => value1.Value.AsInt32() - value2.Value.AsInt32(), + ILOpcode.and => value1.Value.AsInt32() & value2.Value.AsInt32(), + ILOpcode.mul => value1.Value.AsInt32() * value2.Value.AsInt32(), + ILOpcode.div => value1.Value.AsInt32() / value2.Value.AsInt32(), + ILOpcode.div_un => (int)((uint)value1.Value.AsInt32() / (uint)value2.Value.AsInt32()), + ILOpcode.rem => value1.Value.AsInt32() % value2.Value.AsInt32(), + ILOpcode.rem_un => (int)((uint)value1.Value.AsInt32() % (uint)value2.Value.AsInt32()), + _ => throw new NotImplementedException(), // unreachable + }; + + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(result)); + } + else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int64) + { + if (isDivRem && value2.Value.AsInt64() == 0) + return Status.Fail(methodIL.OwningMethod, opcode, "Division by zero"); + + long result = opcode switch + { + ILOpcode.or => value1.Value.AsInt64() | value2.Value.AsInt64(), + ILOpcode.add => value1.Value.AsInt64() + value2.Value.AsInt64(), + ILOpcode.sub => value1.Value.AsInt64() - value2.Value.AsInt64(), + ILOpcode.and => value1.Value.AsInt64() & value2.Value.AsInt64(), + ILOpcode.mul => value1.Value.AsInt64() * value2.Value.AsInt64(), + ILOpcode.div => value1.Value.AsInt64() / value2.Value.AsInt64(), + ILOpcode.div_un => (long)((ulong)value1.Value.AsInt64() / (ulong)value2.Value.AsInt64()), + ILOpcode.rem => value1.Value.AsInt64() % value2.Value.AsInt64(), + ILOpcode.rem_un => (long)((ulong)value1.Value.AsInt64() % (ulong)value2.Value.AsInt64()), + _ => throw new NotImplementedException(), // unreachable + }; + + stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(result)); + } + else if (value1.ValueKind == StackValueKind.Float && value2.ValueKind == StackValueKind.Float) + { + if (isDivRem && value2.Value.AsDouble() == 0) + return Status.Fail(methodIL.OwningMethod, opcode, "Division by zero"); + + if (opcode == ILOpcode.or || opcode == ILOpcode.shl || opcode == ILOpcode.and || opcode == ILOpcode.div_un || opcode == ILOpcode.rem_un) + ThrowHelper.ThrowInvalidProgramException(); + + double result = opcode switch + { + ILOpcode.add => value1.Value.AsDouble() + value2.Value.AsDouble(), + ILOpcode.sub => value1.Value.AsDouble() - value2.Value.AsDouble(), + ILOpcode.mul => value1.Value.AsDouble() * value2.Value.AsDouble(), + ILOpcode.div => value1.Value.AsDouble() / value2.Value.AsDouble(), + ILOpcode.rem => value1.Value.AsDouble() % value2.Value.AsDouble(), + _ => throw new NotImplementedException(), // unreachable + }; + + stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble(result)); + } + else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int32 + && opcode == ILOpcode.shl) + { + long result = value1.Value.AsInt64() << value2.Value.AsInt32(); + stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(result)); + } + else + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + break; + + case ILOpcode.ldlen: + { + StackEntry popped = stack.Pop(); + if (popped.Value is ArrayInstance arrayInstance) + { + stack.Push(StackValueKind.NativeInt, context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64(arrayInstance.Length) : ValueTypeValue.FromInt32(arrayInstance.Length)); + } + else if (popped.Value == null) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Null array"); + } + else + { + ThrowHelper.ThrowInvalidProgramException(); + } + } + break; + + case ILOpcode.stelem: + case ILOpcode.stelem_i: + case ILOpcode.stelem_i1: + case ILOpcode.stelem_i2: + case ILOpcode.stelem_i4: + case ILOpcode.stelem_i8: + case ILOpcode.stelem_r4: + case ILOpcode.stelem_r8: + { + TypeDesc elementType = opcode switch + { + ILOpcode.stelem_i => context.GetWellKnownType(WellKnownType.IntPtr), + ILOpcode.stelem_i1 => context.GetWellKnownType(WellKnownType.SByte), + ILOpcode.stelem_i2 => context.GetWellKnownType(WellKnownType.Int16), + ILOpcode.stelem_i4 => context.GetWellKnownType(WellKnownType.Int32), + ILOpcode.stelem_i8 => context.GetWellKnownType(WellKnownType.Int64), + ILOpcode.stelem_r4 => context.GetWellKnownType(WellKnownType.Single), + ILOpcode.stelem_r8 => context.GetWellKnownType(WellKnownType.Double), + _ => (TypeDesc)methodIL.GetObject(reader.ReadILToken()), + }; + + if (elementType.IsGCPointer) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + Value value = stack.PopIntoLocation(elementType); + if (!stack.TryPopIntValue(out int index)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + StackEntry array = stack.Pop(); + if (array.Value is ArrayInstance arrayInstance) + { + if (!arrayInstance.TryStoreElement(index, value)) + return Status.Fail(methodIL.OwningMethod, opcode, "Out of range access"); + } + else if (array.Value == null) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Null array"); + } + else + { + ThrowHelper.ThrowInvalidProgramException(); + } + } + break; + + case ILOpcode.ldelem: + case ILOpcode.ldelem_i: + case ILOpcode.ldelem_i1: + case ILOpcode.ldelem_u1: + case ILOpcode.ldelem_i2: + case ILOpcode.ldelem_u2: + case ILOpcode.ldelem_i4: + case ILOpcode.ldelem_u4: + case ILOpcode.ldelem_i8: + case ILOpcode.ldelem_r4: + case ILOpcode.ldelem_r8: + { + TypeDesc elementType = opcode switch + { + ILOpcode.ldelem_i => context.GetWellKnownType(WellKnownType.IntPtr), + ILOpcode.ldelem_i1 => context.GetWellKnownType(WellKnownType.SByte), + ILOpcode.ldelem_u1 => context.GetWellKnownType(WellKnownType.Byte), + ILOpcode.ldelem_i2 => context.GetWellKnownType(WellKnownType.Int16), + ILOpcode.ldelem_u2 => context.GetWellKnownType(WellKnownType.UInt16), + ILOpcode.ldelem_i4 => context.GetWellKnownType(WellKnownType.Int32), + ILOpcode.ldelem_u4 => context.GetWellKnownType(WellKnownType.UInt32), + ILOpcode.ldelem_i8 => context.GetWellKnownType(WellKnownType.Int64), + ILOpcode.ldelem_r4 => context.GetWellKnownType(WellKnownType.Single), + ILOpcode.ldelem_r8 => context.GetWellKnownType(WellKnownType.Double), + _ => (TypeDesc)methodIL.GetObject(reader.ReadILToken()), + }; + + if (elementType.IsGCPointer) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + if (!stack.TryPopIntValue(out int index)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + StackEntry array = stack.Pop(); + if (array.Value is ArrayInstance arrayInstance) + { + if (!arrayInstance.TryLoadElement(index, out Value value)) + return Status.Fail(methodIL.OwningMethod, opcode, "Out of range access"); + + stack.PushFromLocation(elementType, value); + } + else if (array.Value == null) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Null array"); + } + else if (array.Value is ForeignTypeInstance) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Foreign array"); + } + else + { + ThrowHelper.ThrowInvalidProgramException(); + } + + } + break; + + case ILOpcode.box: + { + TypeDesc type = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); + if (type.IsValueType) + { + if (type.IsNullable) + return Status.Fail(methodIL.OwningMethod, opcode); + + Value value = stack.PopIntoLocation(type); + if (!ObjectInstance.TryBox((DefType)type, value, out ObjectInstance boxedResult)) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + + stack.Push(boxedResult); + } + } + break; + + case ILOpcode.unbox_any: + { + TypeDesc type = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); + StackEntry entry = stack.Pop(); + if (entry.Value is ObjectInstance objInst + && objInst.TryUnboxAny(type, out Value unboxed)) + { + stack.PushFromLocation(type, unboxed); + } + else + { + ThrowHelper.ThrowInvalidProgramException(); + } + } + break; + + default: + return Status.Fail(methodIL.OwningMethod, opcode); + } + + } + + return Status.Fail(methodIL.OwningMethod, "Control fell through"); + } + + private static Value NewUninitializedLocationValue(TypeDesc locationType) + { + if (locationType.IsGCPointer || locationType.IsByRef) + { + return null; + } + else + { + Debug.Assert(locationType.IsValueType || locationType.IsPointer || locationType.IsFunctionPointer); + return new ValueTypeValue(locationType); + } + } + + private bool TryHandleIntrinsicCall(MethodDesc method, Value[] parameters, out Value retVal) + { + retVal = default; + + switch (method.Name) + { + case "InitializeArray": + if (method.OwningType is MetadataType mdType + && mdType.Name == "RuntimeHelpers" && mdType.Namespace == "System.Runtime.CompilerServices" + && mdType.Module == mdType.Context.SystemModule + && parameters[0] is ArrayInstance array + && parameters[1] is RuntimeFieldHandleValue fieldHandle + && fieldHandle.Field.IsStatic && fieldHandle.Field.HasRva + && fieldHandle.Field is Internal.TypeSystem.Ecma.EcmaField ecmaField) + { + byte[] rvaData = Internal.TypeSystem.Ecma.EcmaFieldExtensions.GetFieldRvaData(ecmaField); + return array.TryInitialize(rvaData); + } + return false; + } + + return false; + } + + private TypeDesc GetArgType(MethodDesc method, int index) + { + var sig = method.Signature; + int offset = 0; + if (!sig.IsStatic) + { + if (index == 0) + return method.OwningType.IsValueType ? method.OwningType.MakeByRefType() : method.OwningType; + offset = 1; + } + + if ((uint)(index - offset) >= (uint)sig.Length) + ThrowHelper.ThrowInvalidProgramException(); + + return sig[index - offset]; + } + + class Stack : Stack + { + private readonly TargetDetails _target; + + public Stack(int capacity, TargetDetails target) : base(capacity) + { + _target = target; + } + + public new StackEntry Pop() + { + if (Count < 1) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + return base.Pop(); + } + + public bool TryPopIntValue(out int value) + { + if (Count == 0) + { + value = 0; + return false; + } + + StackEntry entry = Pop(); + if (entry.ValueKind == StackValueKind.Int32) + { + value = entry.Value.AsInt32(); + return true; + } + else if (entry.ValueKind == StackValueKind.NativeInt) + { + if (_target.PointerSize == 8) + { + long longValue = entry.Value.AsInt64(); + if (longValue < int.MinValue || longValue > int.MaxValue) + { + value = 0; + return false; + } + value = (int)longValue; + return true; + } + + value = entry.Value.AsInt32(); + return true; + } + + value = 0; + return false; + } + + public void Push(StackValueKind kind, Value val) + { + Push(new StackEntry(kind, val)); + } + + public void Push(ReferenceTypeValue value) + { + Push(StackValueKind.ObjRef, value); + } + + public void PushFromLocation(TypeDesc locationType, Value value) + { + switch (locationType.UnderlyingType.Category) + { + case TypeFlags.Boolean: + case TypeFlags.Byte: + Push(StackValueKind.Int32, ValueTypeValue.FromInt32((byte)value.AsSByte())); break; + case TypeFlags.Char: + case TypeFlags.UInt16: + Push(StackValueKind.Int32, ValueTypeValue.FromInt32((ushort)value.AsInt16())); break; + case TypeFlags.SByte: + Push(StackValueKind.Int32, ValueTypeValue.FromInt32(value.AsSByte())); break; + case TypeFlags.Int16: + Push(StackValueKind.Int32, ValueTypeValue.FromInt32(value.AsInt16())); break; + case TypeFlags.Int32: + case TypeFlags.UInt32: + Push(StackValueKind.Int32, value.Clone()); break; + case TypeFlags.Int64: + case TypeFlags.UInt64: + Push(StackValueKind.Int64, value.Clone()); break; + case TypeFlags.IntPtr: + case TypeFlags.UIntPtr: + case TypeFlags.Pointer: + case TypeFlags.FunctionPointer: + Push(StackValueKind.NativeInt, value.Clone()); break; + case TypeFlags.Single: + Push(StackValueKind.Float, ValueTypeValue.FromDouble(value.AsSingle())); break; + case TypeFlags.Double: + Push(StackValueKind.Float, value.Clone()); break; + case TypeFlags.ValueType: + case TypeFlags.Nullable: + Push(StackValueKind.ValueType, value.Clone()); break; + case TypeFlags.Class: + case TypeFlags.Interface: + case TypeFlags.Array: + case TypeFlags.SzArray: + Push(StackValueKind.ObjRef, value); break; + case TypeFlags.ByRef: + Push(StackValueKind.ByRef, value); break; + default: + throw new NotImplementedException(); + } + } + + public Value PopIntoLocation(TypeDesc locationType) + { + if (Count == 0) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + locationType = locationType.UnderlyingType; + + StackEntry popped = Pop(); + + switch (popped.ValueKind) + { + case StackValueKind.Int64: + if (!locationType.IsWellKnownType(WellKnownType.Int64) + && !locationType.IsWellKnownType(WellKnownType.UInt64)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return popped.Value; + + case StackValueKind.Int32: + if (!locationType.IsWellKnownType(WellKnownType.Int32) + && !locationType.IsWellKnownType(WellKnownType.UInt32)) + { + int value = popped.Value.AsInt32(); + switch (locationType.Category) + { + case TypeFlags.SByte: + case TypeFlags.Byte: + case TypeFlags.Boolean: + return ValueTypeValue.FromSByte((sbyte)value); + case TypeFlags.Int16: + case TypeFlags.UInt16: + case TypeFlags.Char: + return ValueTypeValue.FromInt16((short)value); + // case TypeFlags.IntPtr: sign extend + // case TypeFlags.UIntPtr: zero extend + } + ThrowHelper.ThrowInvalidProgramException(); + } + return popped.Value; + + case StackValueKind.NativeInt: + // If it's none of the natural pointer types, we might need to truncate. + if (!locationType.IsPointer + && !locationType.IsFunctionPointer + && !locationType.IsWellKnownType(WellKnownType.IntPtr) + && !locationType.IsWellKnownType(WellKnownType.UIntPtr)) + { + long value = _target.PointerSize == 8 ? popped.Value.AsInt64() : popped.Value.AsInt32(); + switch (locationType.Category) + { + case TypeFlags.SByte: + case TypeFlags.Byte: + case TypeFlags.Boolean: + return ValueTypeValue.FromSByte((sbyte)value); + case TypeFlags.Int16: + case TypeFlags.UInt16: + case TypeFlags.Char: + return ValueTypeValue.FromInt16((short)value); + case TypeFlags.Int32: + case TypeFlags.UInt32: + return ValueTypeValue.FromInt32((int)value); + // case TypeFlags.ByRef: start GC tracking + } + + ThrowHelper.ThrowInvalidProgramException(); + } + return popped.Value; + + case StackValueKind.Float: + if (locationType.IsWellKnownType(WellKnownType.Double)) + { + return popped.Value; + } + else if (!locationType.IsWellKnownType(WellKnownType.Single)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return ValueTypeValue.FromSingle((float)popped.Value.AsDouble()); + + case StackValueKind.ByRef: + if (!locationType.IsByRef) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return popped.Value; + + case StackValueKind.ObjRef: + if (!locationType.IsGCPointer) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return popped.Value; + + case StackValueKind.ValueType: + if (!locationType.IsValueType + || ((BaseValueTypeValue)popped.Value).Size != ((DefType)locationType).InstanceFieldSize.AsInt) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return popped.Value; + + default: + throw new NotImplementedException(); + } + } + } + + private enum StackValueKind + { + Unknown, + Int32, + Int64, + NativeInt, + Float, + ByRef, + ObjRef, + ValueType, + } + + /// + /// Represents a field value that can be serialized into a preinitialized blob. + /// + public interface ISerializableValue + { + void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory); + } + + /// + /// Represents a frozen object whose contents can be serialized into the executable. + /// + public interface ISerializableReference : ISerializableValue + { + TypeDesc Type { get; } + void WriteContent(ref ObjectDataBuilder builder, ISymbolNode thisNode, NodeFactory factory); + } + + /// + /// Represents a value with instance fields. This is either a reference type, or a byref to + /// a valuetype. + /// + private interface IHasInstanceFields + { + void SetField(FieldDesc field, Value value); + Value GetField(FieldDesc field); + ByRefValue GetFieldAddress(FieldDesc field); + } + + /// + /// Represents a special value that is used internally to model known constructs, but cannot + /// be represented externally and that's why we don't allow field stores with it. + /// + private interface IInternalModelingOnlyValue + { + } + + /// + /// Represents a value that can be assigned into. + /// + private interface IAssignableValue + { + bool TryAssign(Value value); + } + + private abstract class Value : ISerializableValue + { + public abstract bool Equals(Value value); + + public static bool Equals(Value value1, Value value2) + { + if (value1 == value2) + { + return true; + } + if (value1 == null || value2 == null) + { + return false; + } + return value1.Equals(value2); + } + + public virtual bool TryCreateByRef(out Value value) + { + value = null; + return false; + } + + public abstract void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory); + + private T ThrowInvalidProgram() + { + ThrowHelper.ThrowInvalidProgramException(); + return default; + } + + public virtual sbyte AsSByte() => ThrowInvalidProgram(); + public virtual short AsInt16() => ThrowInvalidProgram(); + public virtual int AsInt32() => ThrowInvalidProgram(); + public virtual long AsInt64() => ThrowInvalidProgram(); + public virtual float AsSingle() => ThrowInvalidProgram(); + public virtual double AsDouble() => ThrowInvalidProgram(); + public virtual Value Clone() => ThrowInvalidProgram(); + } + + private abstract class BaseValueTypeValue : Value + { + public abstract int Size { get; } + } + + // Also represents pointers and function pointer. + private class ValueTypeValue : BaseValueTypeValue, IAssignableValue + { + public readonly byte[] InstanceBytes; + + public override int Size => InstanceBytes.Length; + + public ValueTypeValue(TypeDesc type) + { + Debug.Assert(type.IsValueType || type.IsPointer || type.IsFunctionPointer); + InstanceBytes = new byte[type.GetElementSize().AsInt]; + } + + private ValueTypeValue(byte[] bytes) + { + InstanceBytes = bytes; + } + + public override Value Clone() + { + return new ValueTypeValue((byte[])InstanceBytes.Clone()); + } + + public override bool TryCreateByRef(out Value value) + { + value = new ByRefValue(InstanceBytes, 0); + return true; + } + + bool IAssignableValue.TryAssign(Value value) + { + if (!(value is BaseValueTypeValue other) + || other.Size != Size) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + if (!(value is ValueTypeValue vtvalue)) + { + return false; + } + + Array.Copy(vtvalue.InstanceBytes, InstanceBytes, InstanceBytes.Length); + return true; + } + + public override bool Equals(Value value) + { + if (!(value is ValueTypeValue vtvalue) + || vtvalue.InstanceBytes.Length != InstanceBytes.Length) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + for (int i = 0; i < InstanceBytes.Length; i++) + { + if (InstanceBytes[i] != ((ValueTypeValue)value).InstanceBytes[i]) + return false; + } + + return true; + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + Debug.Assert(field.FieldType.GetElementSize().AsInt == InstanceBytes.Length); + builder.EmitBytes(InstanceBytes); + } + + private byte[] AsExactByteCount(int size) + { + if (InstanceBytes.Length != size) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return InstanceBytes; + } + + public override sbyte AsSByte() => (sbyte)AsExactByteCount(1)[0]; + public override short AsInt16() => BitConverter.ToInt16(AsExactByteCount(2), 0); + public override int AsInt32() => BitConverter.ToInt32(AsExactByteCount(4), 0); + public override long AsInt64() => BitConverter.ToInt64(AsExactByteCount(8), 0); + public override float AsSingle() => BitConverter.ToSingle(AsExactByteCount(4), 0); + public override double AsDouble() => BitConverter.ToDouble(AsExactByteCount(8), 0); + public static ValueTypeValue FromSByte(sbyte value) => new ValueTypeValue(new byte[1] { (byte)value }); + public static ValueTypeValue FromInt16(short value) => new ValueTypeValue(BitConverter.GetBytes(value)); + public static ValueTypeValue FromInt32(int value) => new ValueTypeValue(BitConverter.GetBytes(value)); + public static ValueTypeValue FromInt64(long value) => new ValueTypeValue(BitConverter.GetBytes(value)); + public static ValueTypeValue FromSingle(float value) => new ValueTypeValue(BitConverter.GetBytes(value)); + public static ValueTypeValue FromDouble(double value) => new ValueTypeValue(BitConverter.GetBytes(value)); + } + + private class RuntimeFieldHandleValue : BaseValueTypeValue, IInternalModelingOnlyValue + { + public FieldDesc Field { get; private set; } + + public RuntimeFieldHandleValue(FieldDesc field) + { + Field = field; + } + + public override int Size => Field.Context.Target.PointerSize; + + public override bool Equals(Value value) + { + if (!(value is RuntimeFieldHandleValue)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + return Field == ((RuntimeFieldHandleValue)value).Field; + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + throw new NotSupportedException(); + } + } + + private class MethodPointerValue : BaseValueTypeValue, IInternalModelingOnlyValue + { + public MethodDesc PointedToMethod { get; } + + public MethodPointerValue(MethodDesc pointedToMethod) + { + PointedToMethod = pointedToMethod; + } + + public override int Size => PointedToMethod.Context.Target.PointerSize; + + public override bool Equals(Value value) + { + if (!(value is MethodPointerValue)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + return PointedToMethod == ((MethodPointerValue)value).PointedToMethod; + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + throw new NotSupportedException(); + } + } + + private class ByRefValue : Value, IHasInstanceFields + { + public readonly byte[] PointedToBytes; + public readonly int PointedToOffset; + + public ByRefValue(byte[] pointedToBytes, int pointedToOffset) + { + PointedToBytes = pointedToBytes; + PointedToOffset = pointedToOffset; + } + + public override bool Equals(Value value) + { + if (!(value is ByRefValue)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + return PointedToBytes == ((ByRefValue)value).PointedToBytes + && PointedToOffset == ((ByRefValue)value).PointedToOffset; + } + + Value IHasInstanceFields.GetField(FieldDesc field) => new FieldAccessor(PointedToBytes, PointedToOffset).GetField(field); + void IHasInstanceFields.SetField(FieldDesc field, Value value) => new FieldAccessor(PointedToBytes, PointedToOffset).SetField(field, value); + ByRefValue IHasInstanceFields.GetFieldAddress(FieldDesc field) => new FieldAccessor(PointedToBytes, PointedToOffset).GetFieldAddress(field); + + public void Initialize(int size) + { + if ((uint)size > (uint)(PointedToBytes.Length - PointedToOffset)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + for (int i = PointedToOffset; i < PointedToOffset + size; i++) + { + PointedToBytes[i] = 0; + } + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + // This would imply we have a byref-typed static field. The layout algorithm should have blocked this. + throw new NotImplementedException(); + } + } + + private abstract class ReferenceTypeValue : Value + { + public TypeDesc Type { get; } + + protected ReferenceTypeValue(TypeDesc type) { Type = type; } + + public override bool Equals(Value value) + { + return this == value; + } + } + + private class DelegateInstance : ReferenceTypeValue, ISerializableReference + { + private readonly MethodDesc _methodPointed; + private readonly ForeignTypeInstance _firstParameter; + + public DelegateInstance(TypeDesc delegateType, MethodDesc methodPointed, ForeignTypeInstance firstParameter) + : base(delegateType) + { + _methodPointed = methodPointed; + _firstParameter = firstParameter; + } + + public virtual void WriteContent(ref ObjectDataBuilder builder, ISymbolNode thisNode, NodeFactory factory) + { + Debug.Assert(_methodPointed.Signature.IsStatic == (_firstParameter == null)); + + var creationInfo = DelegateCreationInfo.Create(Type.ConvertToCanonForm(CanonicalFormKind.Specific), _methodPointed, factory, followVirtualDispatch: false); + + Debug.Assert(!creationInfo.TargetNeedsVTableLookup); + + // MethodTable + var node = factory.ConstructedTypeSymbol(Type); + Debug.Assert(!node.RepresentsIndirectionCell); // Shouldn't have allowed this + builder.EmitPointerReloc(node); + + if (_methodPointed.Signature.IsStatic) + { + Debug.Assert(creationInfo.Constructor.Method.Name == "InitializeOpenStaticThunk"); + + // m_firstParameter + builder.EmitPointerReloc(thisNode); + + // m_helperObject + builder.EmitZeroPointer(); + + // m_extraFunctionPointerOrData + builder.EmitPointerReloc(creationInfo.GetTargetNode(factory)); + + // m_functionPointer + Debug.Assert(creationInfo.Thunk != null); + builder.EmitPointerReloc(creationInfo.Thunk); + } + else + { + Debug.Assert(creationInfo.Constructor.Method.Name == "InitializeClosedInstance"); + + // m_firstParameter + _firstParameter.WriteFieldData(ref builder, _firstParameter.ForeignField, factory); + + // m_helperObject + builder.EmitZeroPointer(); + + // m_extraFunctionPointerOrData + builder.EmitZeroPointer(); + + // m_functionPointer + builder.EmitPointerReloc(factory.CanonicalEntrypoint(_methodPointed)); + } + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + builder.EmitPointerReloc(factory.SerializedFrozenObject(field, this)); + } + } + + private class ArrayInstance : ReferenceTypeValue, ISerializableReference + { + private readonly int _elementCount; + private readonly int _elementSize; + private readonly byte[] _data; + + public ArrayInstance(ArrayType type, int elementCount) + : base(type) + { + _elementCount = elementCount; + _elementSize = type.ElementType.GetElementSize().AsInt; + _data = new byte[elementCount * _elementSize]; + } + + public bool TryInitialize(byte[] bytes) + { + if (bytes.Length != _data.Length) + return false; + + Array.Copy(bytes, _data, bytes.Length); + return true; + } + + public int Length + { + get + { + return _elementCount; + } + } + + public bool TryStoreElement(int index, Value value) + { + if (!(value is ValueTypeValue valueToStore)) + return false; + + if ((uint)index >= (uint)Length) + return false; + + Debug.Assert(valueToStore.InstanceBytes.Length == _elementSize); + Array.Copy(valueToStore.InstanceBytes, 0, _data, index * _elementSize, valueToStore.InstanceBytes.Length); + return true; + } + + public bool TryLoadElement(int index, out Value value) + { + if ((uint)index > (uint)Length) + { + value = null; + return false; + } + + ValueTypeValue result = new ValueTypeValue(((ArrayType)Type).ElementType); + Array.Copy(_data, index * _elementSize, result.InstanceBytes, 0, _elementSize); + value = result; + return true; + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + builder.EmitPointerReloc(factory.SerializedFrozenObject(field, this)); + } + + public virtual void WriteContent(ref ObjectDataBuilder builder, ISymbolNode thisNode, NodeFactory factory) + { + // MethodTable + var node = factory.ConstructedTypeSymbol(Type); + Debug.Assert(!node.RepresentsIndirectionCell); // Arrays are always local + builder.EmitPointerReloc(node); + + // numComponents + builder.EmitInt(_elementCount); + + int pointerSize = Type.Context.Target.PointerSize; + Debug.Assert(pointerSize == 8 || pointerSize == 4); + + if (pointerSize == 8) + { + // padding numComponents in 64-bit + builder.EmitInt(0); + } + + builder.EmitBytes(_data); + } + } + + private class ForeignTypeInstance : ReferenceTypeValue + { + public FieldDesc ForeignField { get; } + public ReferenceTypeValue Data { get; } + + public ForeignTypeInstance(TypeDesc type, FieldDesc foreignField, ReferenceTypeValue data) + : base(type) + { + ForeignField = foreignField; + Data = data; + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + if (Data is ISerializableReference serializableReference) + { + builder.EmitPointerReloc(factory.SerializedFrozenObject(ForeignField, serializableReference)); + } + else + { + Data.WriteFieldData(ref builder, field, factory); + } + } + } + + private class StringInstance : ReferenceTypeValue + { + private readonly string _value; + + public StringInstance(TypeDesc stringType, string value) + : base(stringType) + { + _value = value; + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + builder.EmitPointerReloc(factory.SerializedStringObject(_value)); + } + } + + private class ObjectInstance : ReferenceTypeValue, IHasInstanceFields, ISerializableReference + { + private readonly byte[] _data; + + public ObjectInstance(DefType type) + : base(type) + { + int size = type.InstanceByteCount.AsInt; + if (type.IsValueType) + size += type.Context.Target.PointerSize; + _data = new byte[size]; + } + + public static bool TryBox(DefType type, Value value, out ObjectInstance result) + { + if (!(value is BaseValueTypeValue)) + ThrowHelper.ThrowInvalidProgramException(); + + if (!(value is ValueTypeValue valuetype)) + { + result = null; + return false; + } + + result = new ObjectInstance(type); + Array.Copy(valuetype.InstanceBytes, 0, result._data, type.Context.Target.PointerSize, valuetype.InstanceBytes.Length); + return true; + } + + public bool TryUnboxAny(TypeDesc type, out Value value) + { + value = null; + + if (!type.IsValueType || type.IsNullable) + return false; + + if (Type.UnderlyingType != type.UnderlyingType) + return false; + + var result = new ValueTypeValue(type); + Array.Copy(_data, type.Context.Target.PointerSize, result.InstanceBytes, 0, result.InstanceBytes.Length); + value = result; + return true; + } + + Value IHasInstanceFields.GetField(FieldDesc field) => new FieldAccessor(_data).GetField(field); + void IHasInstanceFields.SetField(FieldDesc field, Value value) => new FieldAccessor(_data).SetField(field, value); + ByRefValue IHasInstanceFields.GetFieldAddress(FieldDesc field) => new FieldAccessor(_data).GetFieldAddress(field); + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + builder.EmitPointerReloc(factory.SerializedFrozenObject(field, this)); + } + + public virtual void WriteContent(ref ObjectDataBuilder builder, ISymbolNode thisNode, NodeFactory factory) + { + // MethodTable + var node = factory.ConstructedTypeSymbol(Type); + Debug.Assert(!node.RepresentsIndirectionCell); // Shouldn't have allowed preinitializing this + builder.EmitPointerReloc(node); + + // We skip the first pointer because that's the MethodTable pointer + // we just initialized above. + int pointerSize = factory.Target.PointerSize; + builder.EmitBytes(_data, pointerSize, _data.Length - pointerSize); + } + } + + private struct FieldAccessor + { + private readonly byte[] _instanceBytes; + private readonly int _offset; + + public FieldAccessor(byte[] bytes, int offset = 0) + { + _instanceBytes = bytes; + _offset = offset; + } + + public Value GetField(FieldDesc field) + { + Debug.Assert(!field.IsStatic); + Debug.Assert(!field.FieldType.IsGCPointer); + int fieldOffset = field.Offset.AsInt; + int fieldSize = field.FieldType.GetElementSize().AsInt; + if (fieldOffset + fieldSize > _instanceBytes.Length - _offset) + ThrowHelper.ThrowInvalidProgramException(); + + var result = new ValueTypeValue(field.FieldType); + Array.Copy(_instanceBytes, _offset + fieldOffset, result.InstanceBytes, 0, fieldSize); + return result; + } + + public void SetField(FieldDesc field, Value value) + { + Debug.Assert(!field.IsStatic); + + if (field.FieldType.IsGCPointer) + { + // Allow setting reference type fields to null. Since this is the only value we can + // write, this is a no-op since reference type fields are always null + Debug.Assert(value == null); + return; + } + + int fieldOffset = field.Offset.AsInt; + int fieldSize = field.FieldType.GetElementSize().AsInt; + if (fieldOffset + fieldSize > _instanceBytes.Length - _offset) + ThrowHelper.ThrowInvalidProgramException(); + + if (value is not ValueTypeValue vtValue) + { + // This is either invalid IL, or value is one of our modeling-only values + // that don't have a bit representation (e.g. function pointer). + ThrowHelper.ThrowInvalidProgramException(); + return; // unreached + } + + Array.Copy(vtValue.InstanceBytes, 0, _instanceBytes, _offset + fieldOffset, fieldSize); + } + + public ByRefValue GetFieldAddress(FieldDesc field) + { + Debug.Assert(!field.IsStatic); + Debug.Assert(!field.FieldType.IsGCPointer); + int fieldOffset = field.Offset.AsInt; + int fieldSize = field.FieldType.GetElementSize().AsInt; + if (fieldOffset + fieldSize > _instanceBytes.Length - _offset) + ThrowHelper.ThrowInvalidProgramException(); + + return new ByRefValue(_instanceBytes, _offset + fieldOffset); + } + } + + private struct StackEntry + { + public readonly StackValueKind ValueKind; + public readonly Value Value; + + public StackEntry(StackValueKind valueKind, Value value) + { + ValueKind = valueKind; + Value = value; + } + } + + private struct Status + { + public string FailureReason { get; } + + public static Status Success => default; + + public bool IsSuccessful => FailureReason == null; + + private Status(string message) + { + FailureReason = message; + } + + public static Status Fail(MethodDesc method, ILOpcode opcode, string detail = null) + { + return new Status($"Method '{method}', opcode '{opcode}' {detail ?? ""}"); + } + + public static Status Fail(MethodDesc method, string detail) + { + return new Status($"Method '{method}': {detail}"); + } + } + + public class PreinitializationInfo + { + private readonly Dictionary _fieldValues; + + public MetadataType Type { get; } + + public string FailureReason { get; } + + public bool IsPreinitialized => _fieldValues != null; + + public PreinitializationInfo(MetadataType type, IEnumerable> fieldValues) + { + Type = type; + _fieldValues = new Dictionary(); + foreach (var field in fieldValues) + _fieldValues.Add(field.Key, field.Value); + } + + public PreinitializationInfo(MetadataType type, string failureReason) + { + Type = type; + FailureReason = failureReason; + } + + public ISerializableValue GetFieldValue(FieldDesc field) + { + Debug.Assert(IsPreinitialized); + Debug.Assert(field.OwningType == Type); + Debug.Assert(field.IsStatic && !field.HasRva && !field.IsThreadStatic && !field.IsLiteral); + return _fieldValues[field]; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UniversalGenericsRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UniversalGenericsRootProvider.cs new file mode 100644 index 00000000000000..8788f375b65e5d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UniversalGenericsRootProvider.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Compilation roots necessary to enable universal shared generics thats + /// are not encompassed in other root providers + /// + public class UniversalGenericsRootProvider : ICompilationRootProvider + { + TypeSystemContext _context; + + public UniversalGenericsRootProvider(TypeSystemContext context) + { + _context = context; + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + if (_context.SupportsUniversalCanon) + rootProvider.AddCompilationRoot(_context.UniversalCanonType.MakeArrayType(), "Universal generic array support"); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UnixNodeMangler.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UnixNodeMangler.cs new file mode 100644 index 00000000000000..877f8da545d08e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UnixNodeMangler.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Text; +using Internal.TypeSystem; +using System.Diagnostics; +using System.Globalization; + +namespace ILCompiler +{ + public sealed class UnixNodeMangler : NodeMangler + { + // Mangled name of boxed version of a type + public sealed override string MangledBoxedTypeName(TypeDesc type) + { + Debug.Assert(type.IsValueType); + return "Boxed_" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string MethodTable(TypeDesc type) + { + string mangledJustTypeName; + + if (type.IsValueType) + mangledJustTypeName = MangledBoxedTypeName(type); + else + mangledJustTypeName = NameMangler.GetMangledTypeName(type); + + return "_ZTV" + mangledJustTypeName.Length.ToString(CultureInfo.InvariantCulture) + mangledJustTypeName; + } + + public sealed override string GCStatics(TypeDesc type) + { + return "__GCSTATICS" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string NonGCStatics(TypeDesc type) + { + return "__NONGCSTATICS" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string ThreadStatics(TypeDesc type) + { + return NameMangler.CompilationUnitPrefix + "__THREADSTATICS" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string ThreadStaticsIndex(TypeDesc type) + { + return "__TypeThreadStaticIndex" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string TypeGenericDictionary(TypeDesc type) + { + return GenericDictionaryNamePrefix + NameMangler.GetMangledTypeName(type); + } + + public sealed override string MethodGenericDictionary(MethodDesc method) + { + return GenericDictionaryNamePrefix + NameMangler.GetMangledMethodName(method); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedInteropStubManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedInteropStubManager.cs new file mode 100644 index 00000000000000..9aa675ca738fc0 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedInteropStubManager.cs @@ -0,0 +1,123 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.IL; +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; + +using ILCompiler.DependencyAnalysis; + +using Debug = System.Diagnostics.Debug; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using System.Runtime.InteropServices; + +namespace ILCompiler +{ + /// + /// Represents an interop stub manager whose list of stubs is determined by statical usage seen in the compiled program. + /// + public class UsageBasedInteropStubManager : CompilerGeneratedInteropStubManager + { + public UsageBasedInteropStubManager(InteropStateManager interopStateManager, PInvokeILEmitterConfiguration pInvokeILEmitterConfiguration) + : base(interopStateManager, pInvokeILEmitterConfiguration) + { + } + + public override void AddDependeciesDueToPInvoke(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + if (method.IsPInvoke) + { + dependencies = dependencies ?? new DependencyList(); + + MethodSignature methodSig = method.Signature; + AddParameterMarshallingDependencies(ref dependencies, factory, methodSig.ReturnType); + + for (int i = 0; i < methodSig.Length; i++) + { + AddParameterMarshallingDependencies(ref dependencies, factory, methodSig[i]); + } + } + + if (method.HasInstantiation) + { + dependencies = dependencies ?? new DependencyList(); + AddMarshalAPIsGenericDependencies(ref dependencies, factory, method); + } + } + + private static void AddParameterMarshallingDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + if (type.IsDelegate) + { + dependencies.Add(factory.DelegateMarshallingData((DefType)type), "Delegate marshaling"); + } + + // struct may contain delegate fields, hence we need to add dependencies for it + if (type.IsByRef) + type = ((ParameterizedType)type).ParameterType; + + if (MarshalHelpers.IsStructMarshallingRequired(type)) + { + foreach (FieldDesc field in type.GetFields()) + { + if (field.IsStatic) + continue; + + AddParameterMarshallingDependencies(ref dependencies, factory, field.FieldType); + } + } + } + + public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + if (type.IsDelegate) + { + var delegateType = (MetadataType)type; + if (delegateType.HasCustomAttribute("System.Runtime.InteropServices", "UnmanagedFunctionPointerAttribute")) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.DelegateMarshallingData(delegateType), "Delegate marshalling"); + } + } + } + + /// + /// For Marshal generic APIs(eg. Marshal.StructureToPtr, GetFunctionPointerForDelegate) we add + /// the generic parameter as dependencies so that we can generate runtime data for them + /// + public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + Debug.Assert(method.HasInstantiation); + + TypeDesc owningType = method.OwningType; + MetadataType metadataType = owningType as MetadataType; + if (metadataType != null && metadataType.Module == factory.TypeSystemContext.SystemModule) + { + if (metadataType.Name == "Marshal" && metadataType.Namespace == "System.Runtime.InteropServices") + { + string methodName = method.Name; + if (methodName == "GetFunctionPointerForDelegate" || + methodName == "GetDelegateForFunctionPointer" || + methodName == "PtrToStructure" || + methodName == "StructureToPtr" || + methodName == "SizeOf" || + methodName == "OffsetOf") + { + foreach (TypeDesc type in method.Instantiation) + { + dependencies = dependencies ?? new DependencyList(); + if (type.IsDelegate) + { + dependencies.Add(factory.DelegateMarshallingData((DefType)type), "Delegate marshlling"); + } + else if (MarshalHelpers.IsStructMarshallingRequired(type) || (methodName == "OffsetOf" && type is DefType)) + { + dependencies.Add(factory.StructMarshallingData((DefType)type), "Struct marshalling"); + } + } + } + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs new file mode 100644 index 00000000000000..45591657c1c3b3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -0,0 +1,1018 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection.PortableExecutable; +using System.Reflection.Metadata; +using System.IO; +using System.Xml; + +using Internal.IL; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using ILCompiler.Metadata; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +using FlowAnnotations = ILCompiler.Dataflow.FlowAnnotations; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using CombinedDependencyList = System.Collections.Generic.List.CombinedDependencyListEntry>; +using Debug = System.Diagnostics.Debug; +using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue; +using EcmaModule = Internal.TypeSystem.Ecma.EcmaModule; +using EcmaType = Internal.TypeSystem.Ecma.EcmaType; +using CustomAttributeHandle = System.Reflection.Metadata.CustomAttributeHandle; +using CustomAttributeTypeProvider = Internal.TypeSystem.Ecma.CustomAttributeTypeProvider; +using MetadataExtensions = Internal.TypeSystem.Ecma.MetadataExtensions; + +namespace ILCompiler +{ + /// + /// This class is responsible for managing native metadata to be emitted into the compiled + /// module. It applies a policy that every type/method that is statically used shall be reflectable. + /// + public sealed class UsageBasedMetadataManager : GeneratingMetadataManager + { + private readonly CompilationModuleGroup _compilationModuleGroup; + + internal readonly UsageBasedMetadataGenerationOptions _generationOptions; + private readonly bool _hasPreciseFieldUsageInformation; + + private readonly FeatureSwitchHashtable _featureSwitchHashtable; + + private readonly List _modulesWithMetadata = new List(); + private readonly List _fieldsWithMetadata = new List(); + private readonly List _methodsWithMetadata = new List(); + private readonly List _typesWithMetadata = new List(); + private readonly List _customAttributesWithMetadata = new List(); + + private readonly HashSet _rootEntireAssembliesExaminedModules = new HashSet(); + + private readonly HashSet _rootEntireAssembliesModules; + private readonly HashSet _trimmedAssemblies; + + internal FlowAnnotations FlowAnnotations { get; } + + internal Logger Logger { get; } + + public UsageBasedMetadataManager( + CompilationModuleGroup group, + CompilerTypeSystemContext typeSystemContext, + MetadataBlockingPolicy blockingPolicy, + ManifestResourceBlockingPolicy resourceBlockingPolicy, + string logFile, + StackTraceEmissionPolicy stackTracePolicy, + DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy, + FlowAnnotations flowAnnotations, + UsageBasedMetadataGenerationOptions generationOptions, + Logger logger, + IEnumerable> featureSwitchValues, + IEnumerable rootEntireAssembliesModules, + IEnumerable trimmedAssemblies) + : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy) + { + // We use this to mark places that would behave differently if we tracked exact fields used. + _hasPreciseFieldUsageInformation = false; + _compilationModuleGroup = group; + _generationOptions = generationOptions; + + FlowAnnotations = flowAnnotations; + Logger = logger; + + _featureSwitchHashtable = new FeatureSwitchHashtable(new Dictionary(featureSwitchValues)); + + _rootEntireAssembliesModules = new HashSet(rootEntireAssembliesModules); + _trimmedAssemblies = new HashSet(trimmedAssemblies); + } + + protected override void Graph_NewMarkedNode(DependencyNodeCore obj) + { + base.Graph_NewMarkedNode(obj); + + var moduleMetadataNode = obj as ModuleMetadataNode; + if (moduleMetadataNode != null) + { + _modulesWithMetadata.Add(moduleMetadataNode.Module); + } + + var fieldMetadataNode = obj as FieldMetadataNode; + if (fieldMetadataNode != null) + { + _fieldsWithMetadata.Add(fieldMetadataNode.Field); + } + + var methodMetadataNode = obj as MethodMetadataNode; + if (methodMetadataNode != null) + { + _methodsWithMetadata.Add(methodMetadataNode.Method); + } + + var typeMetadataNode = obj as TypeMetadataNode; + if (typeMetadataNode != null) + { + _typesWithMetadata.Add(typeMetadataNode.Type); + } + + var customAttributeMetadataNode = obj as CustomAttributeMetadataNode; + if (customAttributeMetadataNode != null) + { + _customAttributesWithMetadata.Add(customAttributeMetadataNode.CustomAttribute); + } + } + + protected override MetadataCategory GetMetadataCategory(FieldDesc field) + { + MetadataCategory category = 0; + + if (!IsReflectionBlocked(field)) + { + // Can't do mapping for uninstantiated things + if (!field.OwningType.IsGenericDefinition) + category = MetadataCategory.RuntimeMapping; + + if (_compilationModuleGroup.ContainsType(field.GetTypicalFieldDefinition().OwningType)) + category |= MetadataCategory.Description; + } + + return category; + } + + protected override MetadataCategory GetMetadataCategory(MethodDesc method) + { + MetadataCategory category = 0; + + if (!IsReflectionBlocked(method)) + { + // Can't do mapping for uninstantiated things + if (!method.IsGenericMethodDefinition && !method.OwningType.IsGenericDefinition) + category = MetadataCategory.RuntimeMapping; + + if (_compilationModuleGroup.ContainsType(method.GetTypicalMethodDefinition().OwningType)) + category |= MetadataCategory.Description; + } + + return category; + } + + protected override MetadataCategory GetMetadataCategory(TypeDesc type) + { + MetadataCategory category = 0; + + if (!IsReflectionBlocked(type)) + { + category = MetadataCategory.RuntimeMapping; + + if (_compilationModuleGroup.ContainsType(type.GetTypeDefinition())) + category |= MetadataCategory.Description; + } + + return category; + } + + protected override bool AllMethodsCanBeReflectable => (_generationOptions & UsageBasedMetadataGenerationOptions.ReflectedMembersOnly) == 0; + + protected override void ComputeMetadata(NodeFactory factory, + out byte[] metadataBlob, + out List> typeMappings, + out List> methodMappings, + out List> fieldMappings, + out List> stackTraceMapping) + { + ComputeMetadata(new GeneratedTypesAndCodeMetadataPolicy(_blockingPolicy, factory), + factory, out metadataBlob, out typeMappings, out methodMappings, out fieldMappings, out stackTraceMapping); + } + + protected override void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.MethodMetadata(method.GetTypicalMethodDefinition()), "Reflectable method"); + } + + protected override void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + TypeMetadataNode.GetMetadataDependencies(ref dependencies, factory, type, "Reflectable type"); + + // If we don't have precise field usage information, apply policy that all fields that + // are eligible to have metadata get metadata. + if (!_hasPreciseFieldUsageInformation) + { + TypeDesc typeDefinition = type.GetTypeDefinition(); + + foreach (FieldDesc field in typeDefinition.GetFields()) + { + if ((GetMetadataCategory(field) & MetadataCategory.Description) != 0) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.FieldMetadata(field), "Field of a reflectable type"); + } + } + } + + MetadataType mdType = type as MetadataType; + + // If anonymous type heuristic is turned on and this is an anonymous type, make sure we have + // method bodies for all properties. It's common to have anonymous types used with reflection + // and it's hard to specify them in RD.XML. + if ((_generationOptions & UsageBasedMetadataGenerationOptions.AnonymousTypeHeuristic) != 0) + { + if (mdType != null && + mdType.HasInstantiation && + !mdType.IsGenericDefinition && + mdType.HasCustomAttribute("System.Runtime.CompilerServices", "CompilerGeneratedAttribute") && + mdType.Name.Contains("AnonymousType")) + { + foreach (MethodDesc method in type.GetMethods()) + { + if (!method.Signature.IsStatic && method.IsSpecialName) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.CanonicalEntrypoint(method), "Anonymous type accessor"); + } + } + } + } + + ModuleDesc module = mdType?.Module; + if (module != null && !_rootEntireAssembliesExaminedModules.Contains(module)) + { + // If the owning assembly needs to be fully compiled, do that. + _rootEntireAssembliesExaminedModules.Add(module); + + string assemblyName = module.Assembly.GetName().Name; + + bool fullyRoot; + string reason; + + if (_rootEntireAssembliesModules.Contains(assemblyName)) + { + // If the assembly was specified as a root on the command line, root it + fullyRoot = true; + reason = "Rooted from command line"; + } + else if (_trimmedAssemblies.Contains(assemblyName) || IsTrimmableAssembly(module)) + { + // If the assembly was specified as trimmed on the command line, do not root + // If the assembly is marked trimmable via an attribute, do not root + fullyRoot = false; + reason = null; + } + else + { + // If rooting default assemblies was requested, root + fullyRoot = (_generationOptions & UsageBasedMetadataGenerationOptions.RootDefaultAssemblies) != 0; + reason = "Assemblies rooted from command line"; + } + + if (fullyRoot) + { + dependencies = dependencies ?? new DependencyList(); + var rootProvider = new RootingServiceProvider(factory, dependencies.Add); + foreach (TypeDesc t in mdType.Module.GetAllTypes()) + { + RootingHelpers.TryRootType(rootProvider, t, reason); + } + } + } + + // Event sources need their special nested types + if (mdType != null && mdType.HasCustomAttribute("System.Diagnostics.Tracing", "EventSourceAttribute")) + { + AddEventSourceSpecialTypeDependencies(ref dependencies, factory, mdType.GetNestedType("Keywords")); + AddEventSourceSpecialTypeDependencies(ref dependencies, factory, mdType.GetNestedType("Tasks")); + AddEventSourceSpecialTypeDependencies(ref dependencies, factory, mdType.GetNestedType("Opcodes")); + + void AddEventSourceSpecialTypeDependencies(ref DependencyList dependencies, NodeFactory factory, MetadataType type) + { + if (type != null) + { + const string reason = "Event source"; + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.TypeMetadata(type), reason); + foreach (FieldDesc field in type.GetFields()) + { + if (field.IsLiteral) + dependencies.Add(factory.FieldMetadata(field), reason); + } + } + } + } + } + + private static bool IsTrimmableAssembly(ModuleDesc assembly) + { + if (assembly is EcmaAssembly ecmaAssembly) + { + foreach (var attribute in ecmaAssembly.GetDecodedCustomAttributes("System.Reflection", "AssemblyMetadataAttribute")) + { + if (attribute.FixedArguments.Length != 2) + continue; + + if (!attribute.FixedArguments[0].Type.IsString + || ((string)(attribute.FixedArguments[0].Value)).Equals("IsTrimmable", StringComparison.Ordinal)) + continue; + + if (!attribute.FixedArguments[1].Type.IsString) + continue; + + string value = (string)attribute.FixedArguments[1].Value; + + if (value.Equals("True", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + } + + return false; + } + + protected override void GetRuntimeMappingDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + // If we precisely track field usage, we don't need the logic below. + if (_hasPreciseFieldUsageInformation) + return; + + const string reason = "Reflection"; + + // This logic is applying policy: if a type is reflectable (has a runtime mapping), all of it's fields + // are reflectable (with a runtime mapping) as well. + // This is potentially overly broad (we don't know if any of the fields will actually be eligile + // for metadata - e.g. they could all be reflection blocked). This is fine since lack of + // precise field usage information is already not ideal from a size on disk perspective. + // The more precise way to do this would be to go over each field, check that it's eligible for RuntimeMapping + // according to the policy (e.g. it's not blocked), and only then root the base of the field. + if (type is MetadataType metadataType && !type.IsGenericDefinition) + { + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any)); + + if (metadataType.GCStaticFieldSize.AsInt > 0) + { + dependencies.Add(factory.TypeGCStaticsSymbol(metadataType), reason); + } + + if (metadataType.NonGCStaticFieldSize.AsInt > 0 || factory.PreinitializationManager.HasLazyStaticConstructor(metadataType)) + { + dependencies.Add(factory.TypeNonGCStaticsSymbol(metadataType), reason); + } + + if (metadataType.ThreadGcStaticFieldSize.AsInt > 0) + { + dependencies.Add(factory.TypeThreadStaticIndex(metadataType), reason); + } + + Debug.Assert(metadataType.ThreadNonGcStaticFieldSize.AsInt == 0); + } + } + + public override bool HasConditionalDependenciesDueToEETypePresence(TypeDesc type) + { + // Note: duplicated with the check in GetConditionalDependenciesDueToEETypePresence + return type.IsDefType && !type.IsInterface && FlowAnnotations.GetTypeAnnotation(type) != default; + } + + public override void GetConditionalDependenciesDueToEETypePresence(ref CombinedDependencyList dependencies, NodeFactory factory, TypeDesc type) + { + // Check to see if we have any dataflow annotations on the type. + // The check below also covers flow annotations inherited through base classes and implemented interfaces. + if (type.IsDefType + && !type.IsInterface /* "IFoo x; x.GetType();" -> this doesn't actually return an interface type */ + && FlowAnnotations.GetTypeAnnotation(type) != default) + { + // We have some flow annotations on this type. + // + // The flow annotations are supposed to ensure that should we call object.GetType on a location + // typed as one of the annotated subclasses of this type, this type is going to have the specified + // members kept. We don't keep them right away, but condition them on the object.GetType being called. + // + // Now we figure out where the annotations are coming from: + + DefType baseType = type.BaseType; + if (baseType != null && FlowAnnotations.GetTypeAnnotation(baseType) != default) + { + // There's an annotation on the base type. If object.GetType was called on something + // statically typed as the base type, we might actually be calling it on this type. + // Ensure we have the flow dependencies. + dependencies ??= new CombinedDependencyList(); + dependencies.Add(new DependencyNodeCore.CombinedDependencyListEntry( + factory.ObjectGetTypeFlowDependencies((MetadataType)type), + factory.ObjectGetTypeFlowDependencies((MetadataType)baseType), + "GetType called on the base type")); + + // We don't have to follow all the bases since the base MethodTable will bubble this up + } + + foreach (DefType interfaceType in type.RuntimeInterfaces) + { + if (FlowAnnotations.GetTypeAnnotation(interfaceType) != default) + { + // There's an annotation on the interface type. If object.GetType was called on something + // statically typed as the interface type, we might actually be calling it on this type. + // Ensure we have the flow dependencies. + dependencies ??= new CombinedDependencyList(); + dependencies.Add(new DependencyNodeCore.CombinedDependencyListEntry( + factory.ObjectGetTypeFlowDependencies((MetadataType)type), + factory.ObjectGetTypeFlowDependencies((MetadataType)interfaceType), + "GetType called on the interface")); + } + + // We don't have to recurse into the interface because we're inspecting runtime interfaces + // and this list is already flattened. + } + + // Note we don't add any conditional dependencies if this type itself was annotated and none + // of the bases/interfaces are annotated. + // ObjectGetTypeFlowDependencies don't need to be conditional in that case. They'll be added as needed. + } + } + + public override void GetDependenciesDueToLdToken(ref DependencyList dependencies, NodeFactory factory, FieldDesc field) + { + // In order for the RuntimeFieldHandle data structure to be usable at runtime, ensure the field + // is generating metadata. + if ((GetMetadataCategory(field) & MetadataCategory.Description) == MetadataCategory.Description) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.FieldMetadata(field.GetTypicalFieldDefinition()), "LDTOKEN field"); + } + } + + public override void GetDependenciesDueToLdToken(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + dependencies = dependencies ?? new DependencyList(); + + if (!IsReflectionBlocked(method)) + dependencies.Add(factory.ReflectableMethod(method), "LDTOKEN method"); + } + + protected override void GetDependenciesDueToMethodCodePresenceInternal(ref DependencyList dependencies, NodeFactory factory, MethodDesc method, MethodIL methodIL) + { + bool scanReflection = (_generationOptions & UsageBasedMetadataGenerationOptions.ReflectionILScanning) != 0; + + Debug.Assert(methodIL != null || method.IsAbstract || method.IsPInvoke || method.IsInternalCall); + + if (methodIL != null && scanReflection) + { + if (FlowAnnotations.RequiresDataflowAnalysis(method)) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.DataflowAnalyzedMethod(methodIL.GetMethodILDefinition()), "Method has annotated parameters"); + } + + if ((method.HasInstantiation && !method.IsCanonicalMethod(CanonicalFormKind.Any))) + { + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + Debug.Assert(typicalMethod != method); + + GetFlowDependenciesForInstantiation(ref dependencies, factory, method.Instantiation, typicalMethod.Instantiation, method); + } + + TypeDesc owningType = method.OwningType; + if (owningType.HasInstantiation && !owningType.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + TypeDesc owningTypeDefinition = owningType.GetTypeDefinition(); + Debug.Assert(owningType != owningTypeDefinition); + + GetFlowDependenciesForInstantiation(ref dependencies, factory, owningType.Instantiation, owningTypeDefinition.Instantiation, owningType); + } + } + + if (method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod ecmaMethod) + { + DynamicDependencyAttributeAlgorithm.AddDependenciesDueToDynamicDependencyAttribute(ref dependencies, factory, ecmaMethod); + } + + // Presence of code might trigger the reflectability dependencies. + if ((_generationOptions & UsageBasedMetadataGenerationOptions.ReflectedMembersOnly) == 0) + { + GetDependenciesDueToReflectability(ref dependencies, factory, method); + } + } + + public override void GetConditionalDependenciesDueToMethodCodePresence(ref CombinedDependencyList dependencies, NodeFactory factory, MethodDesc method) + { + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + + // Ensure methods with genericness have the same reflectability by injecting a conditional dependency. + if ((_generationOptions & UsageBasedMetadataGenerationOptions.ReflectedMembersOnly) != 0 + && method != typicalMethod) + { + dependencies ??= new CombinedDependencyList(); + dependencies.Add(new DependencyNodeCore.CombinedDependencyListEntry( + factory.ReflectableMethod(method), factory.ReflectableMethod(typicalMethod), "Reflectability of methods is same across genericness")); + } + } + + public override void GetDependenciesDueToVirtualMethodReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + if ((_generationOptions & UsageBasedMetadataGenerationOptions.ReflectedMembersOnly) == 0) + { + // If we have a use of an abstract method, GetDependenciesDueToReflectability is not going to see the method + // as being used since there's no body. We inject a dependency on a new node that serves as a logical method body + // for the metadata manager. Metadata manager treats that node the same as a body. + if (method.IsAbstract && GetMetadataCategory(method) != 0) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.ReflectableMethod(method), "Abstract reflectable method"); + } + } + } + + protected override IEnumerable GetFieldsWithRuntimeMapping() + { + if (_hasPreciseFieldUsageInformation) + { + // TODO + } + else + { + // This applies a policy that fields inherit runtime mapping from their owning type, + // unless they are blocked. + foreach (var type in GetTypesWithRuntimeMapping()) + { + if (type.IsGenericDefinition) + continue; + + foreach (var field in type.GetFields()) + { + if ((GetMetadataCategory(field) & MetadataCategory.RuntimeMapping) != 0) + yield return field; + } + } + } + } + + public override IEnumerable GetCompilationModulesWithMetadata() + { + return _modulesWithMetadata; + } + + private IEnumerable GetTypesWithRuntimeMapping() + { + // All constructed types that are not blocked get runtime mapping + foreach (var constructedType in GetTypesWithConstructedEETypes()) + { + if (!IsReflectionBlocked(constructedType)) + yield return constructedType; + } + + // All necessary types for which this is the highest load level that are not blocked + // get runtime mapping. + foreach (var necessaryType in GetTypesWithEETypes()) + { + if (!ConstructedEETypeNode.CreationAllowed(necessaryType) && + !IsReflectionBlocked(necessaryType)) + yield return necessaryType; + } + } + + public override void GetDependenciesDueToAccess(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, FieldDesc writtenField) + { + bool scanReflection = (_generationOptions & UsageBasedMetadataGenerationOptions.ReflectionILScanning) != 0; + if (scanReflection && Dataflow.ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForAccess(FlowAnnotations, writtenField)) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.DataflowAnalyzedMethod(methodIL.GetMethodILDefinition()), "Access to interesting field"); + } + } + + public override void GetDependenciesDueToAccess(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, MethodDesc calledMethod) + { + bool scanReflection = (_generationOptions & UsageBasedMetadataGenerationOptions.ReflectionILScanning) != 0; + if (scanReflection && Dataflow.ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForCallSite(FlowAnnotations, calledMethod)) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.DataflowAnalyzedMethod(methodIL.GetMethodILDefinition()), "Call to interesting method"); + } + } + + public override DependencyList GetDependenciesForCustomAttribute(NodeFactory factory, MethodDesc attributeCtor, CustomAttributeValue decodedValue) + { + bool scanReflection = (_generationOptions & UsageBasedMetadataGenerationOptions.ReflectionILScanning) != 0; + if (scanReflection) + { + return Dataflow.ReflectionMethodBodyScanner.ProcessAttributeDataflow(factory, FlowAnnotations, Logger, attributeCtor, decodedValue); + } + + return null; + } + + private void GetFlowDependenciesForInstantiation(ref DependencyList dependencies, NodeFactory factory, Instantiation instantiation, Instantiation typicalInstantiation, TypeSystemEntity source) + { + for (int i = 0; i < instantiation.Length; i++) + { + var genericParameter = (GenericParameterDesc)typicalInstantiation[i]; + if (FlowAnnotations.GetGenericParameterAnnotation(genericParameter) != default) + { + var deps = ILCompiler.Dataflow.ReflectionMethodBodyScanner.ProcessGenericArgumentDataFlow(factory, FlowAnnotations, Logger, genericParameter, instantiation[i], source); + if (deps.Count > 0) + { + if (dependencies == null) + dependencies = deps; + else + dependencies.AddRange(deps); + } + } + } + } + + public override void GetDependenciesForGenericDictionary(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + TypeDesc owningType = method.OwningType; + + if (FlowAnnotations.HasAnyAnnotations(owningType)) + { + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + Debug.Assert(typicalMethod != method); + + GetFlowDependenciesForInstantiation(ref dependencies, factory, method.Instantiation, typicalMethod.Instantiation, method); + + if (owningType.HasInstantiation) + { + // Since this also introduces a new type instantiation into the system, collect the dependencies for that too. + // We might not see the instantiated type elsewhere. + GetFlowDependenciesForInstantiation(ref dependencies, factory, owningType.Instantiation, owningType.GetTypeDefinition().Instantiation, method); + } + } + } + + public override void GetDependenciesForGenericDictionary(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + if (FlowAnnotations.HasAnyAnnotations(type)) + { + TypeDesc typeDefinition = type.GetTypeDefinition(); + Debug.Assert(type != typeDefinition); + GetFlowDependenciesForInstantiation(ref dependencies, factory, type.Instantiation, typeDefinition.Instantiation, type); + } + } + + public bool GeneratesAttributeMetadata(TypeDesc attributeType) + { + var ecmaType = attributeType.GetTypeDefinition() as EcmaType; + if (ecmaType != null) + { + var moduleInfo = _featureSwitchHashtable.GetOrCreateValue(ecmaType.EcmaModule); + return !moduleInfo.RemovedAttributes.Contains(ecmaType); + } + + return true; + } + + public override void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc overridingMethod) + { + // We validate that the various dataflow/Requires* annotations are consistent across virtual method overrides + + bool baseMethodRequiresUnreferencedCode = baseMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresUnreferencedCodeAttribute"); + bool overridingMethodRequiresUnreferencedCode = overridingMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresUnreferencedCodeAttribute"); + + bool baseMethodRequiresDynamicCode = baseMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresDynamicCodeAttribute"); + bool overridingMethodRequiresDynamicCode = overridingMethod.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "RequiresDynamicCodeAttribute"); + + bool baseMethodRequiresDataflow = FlowAnnotations.RequiresDataflowAnalysis(baseMethod); + bool overridingMethodRequiresDataflow = FlowAnnotations.RequiresDataflowAnalysis(overridingMethod); + + if (baseMethodRequiresUnreferencedCode != overridingMethodRequiresUnreferencedCode) + { + Logger.LogWarning( + $"Presence of 'RequiresUnreferencedCodeAttribute' on method '{overridingMethod.GetDisplayName()}' doesn't match overridden method '{baseMethod.GetDisplayName()}'. " + + $"All overridden methods must have 'RequiresUnreferencedCodeAttribute'.", 2046, overridingMethod, MessageSubCategory.TrimAnalysis); + } + + if (baseMethodRequiresDynamicCode != overridingMethodRequiresDynamicCode) + { + Logger.LogWarning( + $"Presence of 'RequiresDynamicCodeAttribute' on method '{overridingMethod.GetDisplayName()}' doesn't match overridden method '{baseMethod.GetDisplayName()}'. " + + $"All overridden methods must have 'RequiresDynamicCodeAttribute'.", 2046, overridingMethod, MessageSubCategory.AotAnalysis); + } + + if (baseMethodRequiresDataflow || overridingMethodRequiresDataflow) + { + FlowAnnotations.ValidateMethodAnnotationsAreSame(overridingMethod, baseMethod); + } + } + + public MetadataManager ToAnalysisBasedMetadataManager() + { + var reflectableTypes = ReflectableEntityBuilder.Create(); + + // Collect the list of types that are generating reflection metadata + foreach (var typeWithMetadata in _typesWithMetadata) + { + reflectableTypes[typeWithMetadata] = MetadataCategory.Description; + } + + foreach (var constructedType in GetTypesWithRuntimeMapping()) + { + reflectableTypes[constructedType] |= MetadataCategory.RuntimeMapping; + + // Also set the description bit if the definition is getting metadata. + TypeDesc constructedTypeDefinition = constructedType.GetTypeDefinition(); + if (constructedType != constructedTypeDefinition && + (reflectableTypes[constructedTypeDefinition] & MetadataCategory.Description) != 0) + { + reflectableTypes[constructedType] |= MetadataCategory.Description; + } + } + + var reflectableMethods = ReflectableEntityBuilder.Create(); + foreach (var methodWithMetadata in _methodsWithMetadata) + { + reflectableMethods[methodWithMetadata] = MetadataCategory.Description; + } + + foreach (var method in GetReflectableMethods()) + { + if (method.IsGenericMethodDefinition || method.OwningType.IsGenericDefinition) + continue; + + if (!IsReflectionBlocked(method)) + { + if ((reflectableTypes[method.OwningType] & MetadataCategory.RuntimeMapping) != 0) + reflectableMethods[method] |= MetadataCategory.RuntimeMapping; + + // Also set the description bit if the definition is getting metadata. + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + if (method != typicalMethod && + (reflectableMethods[typicalMethod] & MetadataCategory.Description) != 0) + { + reflectableMethods[method] |= MetadataCategory.Description; + reflectableTypes[method.OwningType] |= MetadataCategory.Description; + } + } + } + + var reflectableFields = ReflectableEntityBuilder.Create(); + foreach (var fieldWithMetadata in _fieldsWithMetadata) + { + reflectableFields[fieldWithMetadata] = MetadataCategory.Description; + } + + if (_hasPreciseFieldUsageInformation) + { + // TODO + } + else + { + // If we don't have precise field usage information we apply a policy that + // says the fields inherit the setting from the type, potentially restricted by blocking. + // (I.e. if a type has RuntimeMapping metadata, the field has RuntimeMapping too, unless blocked.) + foreach (var reflectableType in reflectableTypes.ToEnumerable()) + { + if (reflectableType.Entity.IsGenericDefinition) + continue; + + if (reflectableType.Entity.IsCanonicalSubtype(CanonicalFormKind.Specific)) + continue; + + if ((reflectableType.Category & MetadataCategory.RuntimeMapping) == 0) + continue; + + foreach (var field in reflectableType.Entity.GetFields()) + { + if (!IsReflectionBlocked(field)) + { + reflectableFields[field] |= MetadataCategory.RuntimeMapping; + + // Also set the description bit if the definition is getting metadata. + FieldDesc typicalField = field.GetTypicalFieldDefinition(); + if (field != typicalField && + (reflectableFields[typicalField] & MetadataCategory.Description) != 0) + { + reflectableFields[field] |= MetadataCategory.Description; + } + } + } + } + } + + return new AnalysisBasedMetadataManager( + _typeSystemContext, _blockingPolicy, _resourceBlockingPolicy, _metadataLogFile, _stackTraceEmissionPolicy, _dynamicInvokeThunkGenerationPolicy, + _modulesWithMetadata, reflectableTypes.ToEnumerable(), reflectableMethods.ToEnumerable(), + reflectableFields.ToEnumerable(), _customAttributesWithMetadata); + } + + private struct ReflectableEntityBuilder + { + private Dictionary _dictionary; + + public static ReflectableEntityBuilder Create() + { + return new ReflectableEntityBuilder + { + _dictionary = new Dictionary(), + }; + } + + public MetadataCategory this[T key] + { + get + { + if (_dictionary.TryGetValue(key, out MetadataCategory category)) + return category; + return 0; + } + set + { + _dictionary[key] = value; + } + } + + public IEnumerable> ToEnumerable() + { + foreach (var entry in _dictionary) + { + yield return new ReflectableEntity(entry.Key, entry.Value); + } + } + } + + private struct GeneratedTypesAndCodeMetadataPolicy : IMetadataPolicy + { + private readonly MetadataBlockingPolicy _blockingPolicy; + private readonly NodeFactory _factory; + + public GeneratedTypesAndCodeMetadataPolicy(MetadataBlockingPolicy blockingPolicy, NodeFactory factory) + { + _blockingPolicy = blockingPolicy; + _factory = factory; + } + + public bool GeneratesMetadata(FieldDesc fieldDef) + { + return _factory.FieldMetadata(fieldDef).Marked; + } + + public bool GeneratesMetadata(MethodDesc methodDef) + { + return _factory.MethodMetadata(methodDef).Marked; + } + + public bool GeneratesMetadata(MetadataType typeDef) + { + return _factory.TypeMetadata(typeDef).Marked; + } + + public bool GeneratesMetadata(EcmaModule module, CustomAttributeHandle caHandle) + { + return _factory.CustomAttributeMetadata(new ReflectableCustomAttribute(module, caHandle)).Marked; + } + + public bool GeneratesMetadata(EcmaModule module, ExportedTypeHandle exportedTypeHandle) + { + try + { + // Generate the forwarder only if we generated the target type. + // If the target type is in a different compilation group, assume we generated it there. + var targetType = (MetadataType)module.GetObject(exportedTypeHandle); + return GeneratesMetadata(targetType) || !_factory.CompilationModuleGroup.ContainsType(targetType); + } + catch (TypeSystemException) + { + // No harm in generating a forwarder that didn't resolve. + // We'll get matching behavior at runtime. + return true; + } + } + + public bool IsBlocked(MetadataType typeDef) + { + return _blockingPolicy.IsBlocked(typeDef); + } + + public bool IsBlocked(MethodDesc methodDef) + { + return _blockingPolicy.IsBlocked(methodDef); + } + } + + private class FeatureSwitchHashtable : LockFreeReaderHashtable + { + private readonly Dictionary _switchValues; + + public FeatureSwitchHashtable(Dictionary switchValues) + { + _switchValues = switchValues; + } + + protected override bool CompareKeyToValue(EcmaModule key, AssemblyFeatureInfo value) => key == value.Module; + protected override bool CompareValueToValue(AssemblyFeatureInfo value1, AssemblyFeatureInfo value2) => value1.Module == value2.Module; + protected override int GetKeyHashCode(EcmaModule key) => key.GetHashCode(); + protected override int GetValueHashCode(AssemblyFeatureInfo value) => value.Module.GetHashCode(); + + protected override AssemblyFeatureInfo CreateValueFromKey(EcmaModule key) + { + return new AssemblyFeatureInfo(key, _switchValues); + } + } + + private class AssemblyFeatureInfo + { + public EcmaModule Module { get; } + + public HashSet RemovedAttributes { get; } + + public AssemblyFeatureInfo(EcmaModule module, IReadOnlyDictionary featureSwitchValues) + { + Module = module; + RemovedAttributes = new HashSet(); + + PEMemoryBlock resourceDirectory = module.PEReader.GetSectionData(module.PEReader.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress); + + foreach (var resourceHandle in module.MetadataReader.ManifestResources) + { + ManifestResource resource = module.MetadataReader.GetManifestResource(resourceHandle); + + // Don't try to process linked resources or resources in other assemblies + if (!resource.Implementation.IsNil) + { + continue; + } + + string resourceName = module.MetadataReader.GetString(resource.Name); + if (resourceName == "ILLink.LinkAttributes.xml") + { + BlobReader reader = resourceDirectory.GetReader((int)resource.Offset, resourceDirectory.Length - (int)resource.Offset); + int length = (int)reader.ReadUInt32(); + + UnmanagedMemoryStream ms; + unsafe + { + ms = new UnmanagedMemoryStream(reader.CurrentPointer, length); + } + + RemovedAttributes = LinkAttributesReader.GetRemovedAttributes(module.Context, XmlReader.Create(ms), module, featureSwitchValues); + } + } + } + } + + private class LinkAttributesReader : ProcessLinkerXmlBase + { + private readonly HashSet _removedAttributes; + + private LinkAttributesReader(TypeSystemContext context, XmlReader reader, ModuleDesc module, IReadOnlyDictionary featureSwitchValues) + : base(context, reader, module, featureSwitchValues) + { + _removedAttributes = new HashSet(); + } + + protected override void ProcessAttribute(TypeDesc type) + { + string internalValue = GetAttribute("internal"); + if (internalValue == "RemoveAttributeInstances" && IsEmpty()) + { + _removedAttributes.Add(type); + } + } + + public static HashSet GetRemovedAttributes(TypeSystemContext context, XmlReader reader, ModuleDesc module, IReadOnlyDictionary featureSwitchValues) + { + var rdr = new LinkAttributesReader(context, reader, module, featureSwitchValues); + rdr.ProcessXml(); + return rdr._removedAttributes; + } + } + } + + [Flags] + public enum UsageBasedMetadataGenerationOptions + { + None = 0, + + /// + /// Specifies that complete metadata should be generated for types. + /// + /// + /// If this option is set, generated metadata will no longer be pay for play, + /// and a certain class of bugs will disappear (APIs returning "member doesn't + /// exist" at runtime, even though the member exists and we just didn't generate the metadata). + /// Reflection blocking still applies. + /// + CompleteTypesOnly = 1, + + /// + /// Specifies that heuristic that makes anonymous types work should be applied. + /// + /// + /// Generates method bodies for properties on anonymous types even if they're not + /// statically used. + /// + AnonymousTypeHeuristic = 2, + + /// + /// Scan IL for common reflection patterns to find additional compilation roots. + /// + ReflectionILScanning = 4, + + /// + /// Only members that were seen as reflected on will be reflectable. + /// + ReflectedMembersOnly = 8, + + /// + /// Fully root used assemblies that are not marked IsTrimmable in metadata. + /// + RootDefaultAssemblies = 16, + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UserDefinedTypeDescriptor.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UserDefinedTypeDescriptor.cs new file mode 100644 index 00000000000000..e582ca5d074664 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UserDefinedTypeDescriptor.cs @@ -0,0 +1,848 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; +using Internal.TypeSystem.TypesDebugInfo; + +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler +{ + public class UserDefinedTypeDescriptor + { + object _lock = new object(); + NodeFactory _nodeFactory; + + NodeFactory NodeFactory => _nodeFactory; + + bool Is64Bit => NodeFactory.Target.PointerSize == 8; + + TargetAbi Abi => NodeFactory.Target.Abi; + + public UserDefinedTypeDescriptor(ITypesDebugInfoWriter objectWriter, NodeFactory nodeFactory) + { + _objectWriter = objectWriter; + _nodeFactory = nodeFactory; + } + + // Get type index for use as a variable/parameter + public uint GetVariableTypeIndex(TypeDesc type) + { + lock (_lock) + { + return GetVariableTypeIndex(type, true); + } + } + + public uint GetStateMachineThisVariableTypeIndex(TypeDesc type) + { + // If the state machine is a valuetype, the this parameter will be a byref + if (type.IsByRef) + { + type = type.GetParameterType(); + Debug.Assert(type.IsValueType); + } + + type = DebuggerCanonicalize(type); + + lock (_lock) + { + if (!_knownStateMachineThisTypes.TryGetValue(type, out uint typeIndex)) + { + Debug.Assert(type.IsDefType); + + MetadataType defType = (MetadataType)type; + ClassTypeDescriptor classTypeDescriptor = new ClassTypeDescriptor + { + IsStruct = 1, + Name = $"StateMachineLocals_{System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(((EcmaType)defType.GetTypeDefinition()).Handle):X}", + InstanceSize = defType.InstanceByteCount.IsIndeterminate ? 0 : (ulong)defType.InstanceByteCount.AsInt, + }; + + var fieldsDescs = new ArrayBuilder(); + + foreach (var fieldDesc in defType.GetFields()) + { + if (fieldDesc.IsStatic) + continue; + + // We're going to parse the Roslyn-generated backing fields and unmangle them back to usable names. + // We'll also skip some infrastructural fields that are not interesting (like the field that + // holds the initial value of parameters, the current state, the current thread ID, etc.). + // The set of fields we're going to touch is tagged as "parsed by the expression evaluator" + // in the Roslyn codebase, so it's somewhat safe to do this. + // https://github.com/dotnet/roslyn/blob/afd10305a37c0ffb2cfb2c2d8446154c68cfa87a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs#L15-L22 + string fieldNameEmit = fieldDesc.Name; + if (fieldNameEmit.Length > 0 && fieldNameEmit[0] == '<') + { + if (TryGetGeneratedNameKind(fieldNameEmit, out char kind)) + { + if (kind == '4' /* ThisProxy */) + { + fieldNameEmit = "this"; + } + else if (kind == '5' /* HoistedLocalField */) + { + fieldNameEmit = fieldNameEmit.Substring(1, fieldNameEmit.IndexOf('>') - 1); + } + else + { + // The rest of fields support the state machine infrastructure + continue; + } + } + + static bool TryGetGeneratedNameKind(string name, out char kind) + { + int endIndex = name.IndexOf('>'); + if (endIndex > 0 && endIndex + 1 < name.Length) + { + kind = name[endIndex + 1]; + return true; + } + kind = default; + return false; + } + } + + LayoutInt fieldOffset = fieldDesc.Offset; + int fieldOffsetEmit = fieldOffset.IsIndeterminate ? 0xBAAD : fieldOffset.AsInt; + + TypeDesc fieldType = GetFieldDebugType(fieldDesc); + + uint fieldTypeIndex = GetVariableTypeIndex(fieldType, false); + + DataFieldDescriptor field = new DataFieldDescriptor + { + FieldTypeIndex = fieldTypeIndex, + Offset = (ulong)fieldOffsetEmit, + Name = fieldNameEmit + }; + + fieldsDescs.Add(field); + } + + LayoutInt elementSize = defType.GetElementSize(); + int elementSizeEmit = elementSize.IsIndeterminate ? 0xBAAD : elementSize.AsInt; + ClassFieldsTypeDescriptor fieldsDescriptor = new ClassFieldsTypeDescriptor + { + Size = (ulong)elementSizeEmit, + FieldsCount = fieldsDescs.Count, + }; + + uint completeTypeIndex = _objectWriter.GetCompleteClassTypeIndex(classTypeDescriptor, fieldsDescriptor, fieldsDescs.ToArray(), Array.Empty()); + + PointerTypeDescriptor descriptor = new PointerTypeDescriptor + { + ElementType = completeTypeIndex, + Is64Bit = Is64Bit ? 1 : 0, + IsConst = 0, + IsReference = 1, + }; + + typeIndex = _objectWriter.GetPointerTypeIndex(descriptor); + + _knownStateMachineThisTypes.Add(type, typeIndex); + } + + return typeIndex; + } + } + + // Get Type index for this pointer of specified type + public uint GetThisTypeIndex(TypeDesc type) + { + lock (_lock) + { + uint typeIndex; + + if (_thisTypes.TryGetValue(type, out typeIndex)) + return typeIndex; + + PointerTypeDescriptor descriptor = new PointerTypeDescriptor(); + // Note the use of GetTypeIndex here instead of GetVariableTypeIndex (We need the type exactly, not a reference to the type (as would happen for arrays/classes), and not a primitive value (as would happen for primitives)) + descriptor.ElementType = GetTypeIndex(type, true); + descriptor.Is64Bit = Is64Bit ? 1 : 0; + descriptor.IsConst = 1; + descriptor.IsReference = 0; + + typeIndex = _objectWriter.GetPointerTypeIndex(descriptor); + _thisTypes.Add(type, typeIndex); + return typeIndex; + } + } + + // Get type index for method + public uint GetMethodTypeIndex(MethodDesc method) + { + lock (_lock) + { + uint typeIndex; + + if (_methodIndices.TryGetValue(method, out typeIndex)) + return typeIndex; + + MemberFunctionTypeDescriptor descriptor = new MemberFunctionTypeDescriptor(); + MethodSignature signature = method.Signature; + + descriptor.ReturnType = GetVariableTypeIndex(DebuggerCanonicalize(signature.ReturnType)); + descriptor.ThisAdjust = 0; + descriptor.CallingConvention = 0x4; // Near fastcall + descriptor.TypeIndexOfThisPointer = signature.IsStatic ? + GetPrimitiveTypeIndex(method.OwningType.Context.GetWellKnownType(WellKnownType.Void)) : + GetThisTypeIndex(method.OwningType); + descriptor.ContainingClass = GetTypeIndex(method.OwningType, true); + + try + { + descriptor.NumberOfArguments = checked((ushort)signature.Length); + } + catch (OverflowException) + { + return 0; + } + + uint[] args = new uint[signature.Length]; + for (int i = 0; i < args.Length; i++) + args[i] = GetVariableTypeIndex(DebuggerCanonicalize(signature[i])); + + + typeIndex = _objectWriter.GetMemberFunctionTypeIndex(descriptor, args); + _methodIndices.Add(method, typeIndex); + return typeIndex; + } + } + + // Get type index for specific method by name + public uint GetMethodFunctionIdTypeIndex(MethodDesc method) + { + lock (_lock) + { + uint typeIndex; + + if (_methodIdIndices.TryGetValue(method, out typeIndex)) + return typeIndex; + + MemberFunctionIdTypeDescriptor descriptor = new MemberFunctionIdTypeDescriptor(); + + descriptor.MemberFunction = GetMethodTypeIndex(method); + descriptor.ParentClass = GetTypeIndex(method.OwningType, true); + descriptor.Name = method.Name; + + typeIndex = _objectWriter.GetMemberFunctionId(descriptor); + _methodIdIndices.Add(method, typeIndex); + return typeIndex; + } + } + + private TypeDesc DebuggerCanonicalize(TypeDesc type) + { + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return type.ConvertToCanonForm(CanonicalFormKind.Specific); + + return type; + } + + private uint GetVariableTypeIndex(TypeDesc type, bool needsCompleteIndex) + { + uint variableTypeIndex = 0; + if (type.IsPrimitive) + { + variableTypeIndex = GetPrimitiveTypeIndex(type); + } + else + { + type = DebuggerCanonicalize(type); + + if ((type.IsDefType && !type.IsValueType) || type.IsArray) + { + // The type index of a variable/field of a reference type is wrapped + // in a pointer, as these fields are really pointer fields, and the data is on the heap + variableTypeIndex = 0; + if (_knownReferenceWrappedTypes.TryGetValue(type, out variableTypeIndex)) + { + return variableTypeIndex; + } + else + { + uint typeindex = GetTypeIndex(type, false); + + PointerTypeDescriptor descriptor = new PointerTypeDescriptor(); + descriptor.ElementType = typeindex; + descriptor.Is64Bit = Is64Bit ? 1 : 0; + descriptor.IsConst = 0; + descriptor.IsReference = 1; + + variableTypeIndex = _objectWriter.GetPointerTypeIndex(descriptor); + _knownReferenceWrappedTypes[type] = variableTypeIndex; + + return variableTypeIndex; + } + } + else if (type.IsEnum) + { + // Enum's use the LF_ENUM record as the variable type index, but it is required to also emit a regular structure record for them. + + if (_enumTypes.TryGetValue(type, out variableTypeIndex)) + return variableTypeIndex; + + variableTypeIndex = GetEnumTypeIndex(type); + + GetTypeIndex(type, false); // Ensure regular structure record created + + _enumTypes[type] = variableTypeIndex; + + return variableTypeIndex; + } + + variableTypeIndex = GetTypeIndex(type, needsCompleteIndex); + } + return variableTypeIndex; + } + + /// + /// Get type index for type without the type being wrapped as a reference (as a variable or field must be) + /// + /// + /// + /// + public uint GetTypeIndex(TypeDesc type, bool needsCompleteType) + { + uint typeIndex = 0; + if (needsCompleteType ? + _completeKnownTypes.TryGetValue(type, out typeIndex) + : _knownTypes.TryGetValue(type, out typeIndex)) + { + return typeIndex; + } + else + { + return GetNewTypeIndex(type, needsCompleteType); + } + } + + private uint GetNewTypeIndex(TypeDesc type, bool needsCompleteType) + { + if (type.IsArray) + { + return GetArrayTypeIndex(type); + } + else if (type.IsDefType) + { + return GetClassTypeIndex(type, needsCompleteType); + } + else if (type.IsPointer) + { + return GetPointerTypeIndex(((ParameterizedType)type).ParameterType); + } + else if (type.IsByRef) + { + return GetByRefTypeIndex(((ParameterizedType)type).ParameterType); + } + else if (type.IsFunctionPointer) + { + return GetPointerTypeIndex(type.Context.GetWellKnownType(WellKnownType.Void)); + } + + Debug.Fail("Unhandled UserDefinedTypeDescriptor type: {type}"); + return 0; + } + + private uint GetPointerTypeIndex(TypeDesc pointeeType) + { + uint typeIndex; + + if (_pointerTypes.TryGetValue(pointeeType, out typeIndex)) + return typeIndex; + + PointerTypeDescriptor descriptor = new PointerTypeDescriptor(); + descriptor.ElementType = GetVariableTypeIndex(pointeeType, false); + descriptor.Is64Bit = Is64Bit ? 1 : 0; + descriptor.IsConst = 0; + descriptor.IsReference = 0; + + // Calling GetVariableTypeIndex may have filled in _pointerTypes + if (_pointerTypes.TryGetValue(pointeeType, out typeIndex)) + return typeIndex; + + typeIndex = _objectWriter.GetPointerTypeIndex(descriptor); + _pointerTypes.Add(pointeeType, typeIndex); + return typeIndex; + } + + private uint GetByRefTypeIndex(TypeDesc pointeeType) + { + uint typeIndex; + + if (_byRefTypes.TryGetValue(pointeeType, out typeIndex)) + return typeIndex; + + PointerTypeDescriptor descriptor = new PointerTypeDescriptor(); + descriptor.ElementType = GetVariableTypeIndex(pointeeType, false); + descriptor.Is64Bit = Is64Bit ? 1 : 0; + descriptor.IsConst = 0; + descriptor.IsReference = 1; + + // Calling GetVariableTypeIndex may have filled in _byRefTypes + if (_byRefTypes.TryGetValue(pointeeType, out typeIndex)) + return typeIndex; + + typeIndex = _objectWriter.GetPointerTypeIndex(descriptor); + _byRefTypes.Add(pointeeType, typeIndex); + return typeIndex; + } + + private uint GetEnumTypeIndex(TypeDesc type) + { + System.Diagnostics.Debug.Assert(type.IsEnum, "GetEnumTypeIndex was called with wrong type"); + DefType defType = type as DefType; + System.Diagnostics.Debug.Assert(defType != null, "GetEnumTypeIndex was called with non def type"); + List fieldsDescriptors = new List(); + foreach (var field in defType.GetFields()) + { + if (field.IsLiteral) + { + fieldsDescriptors.Add(field); + } + } + EnumTypeDescriptor enumTypeDescriptor = new EnumTypeDescriptor + { + ElementCount = (ulong)fieldsDescriptors.Count, + ElementType = GetPrimitiveTypeIndex(defType.UnderlyingType), + Name = _objectWriter.GetMangledName(type), + }; + EnumRecordTypeDescriptor[] typeRecords = new EnumRecordTypeDescriptor[enumTypeDescriptor.ElementCount]; + for (int i = 0; i < fieldsDescriptors.Count; ++i) + { + FieldDesc field = fieldsDescriptors[i]; + EnumRecordTypeDescriptor recordTypeDescriptor; + recordTypeDescriptor.Value = GetEnumRecordValue(field); + recordTypeDescriptor.Name = field.Name; + typeRecords[i] = recordTypeDescriptor; + } + uint typeIndex = _objectWriter.GetEnumTypeIndex(enumTypeDescriptor, typeRecords); + return typeIndex; + } + + private uint GetArrayTypeIndex(TypeDesc type) + { + System.Diagnostics.Debug.Assert(type.IsArray, "GetArrayTypeIndex was called with wrong type"); + ArrayType arrayType = (ArrayType)type; + + uint elementSize = (uint)type.Context.Target.PointerSize; + LayoutInt layoutElementSize = arrayType.GetElementSize(); + if (!layoutElementSize.IsIndeterminate) + elementSize = (uint)layoutElementSize.AsInt; + + ArrayTypeDescriptor arrayTypeDescriptor = new ArrayTypeDescriptor + { + Rank = (uint)arrayType.Rank, + ElementType = GetVariableTypeIndex(arrayType.ElementType, false), + Size = elementSize, + IsMultiDimensional = arrayType.IsMdArray ? 1 : 0 + }; + + ClassTypeDescriptor classDescriptor = new ClassTypeDescriptor + { + IsStruct = 0, + Name = _objectWriter.GetMangledName(type), + BaseClassId = GetTypeIndex(arrayType.BaseType, false) + }; + + uint typeIndex = _objectWriter.GetArrayTypeIndex(classDescriptor, arrayTypeDescriptor); + _knownTypes[type] = typeIndex; + _completeKnownTypes[type] = typeIndex; + return typeIndex; + } + + private ulong GetEnumRecordValue(FieldDesc field) + { + var ecmaField = field as EcmaField; + if (ecmaField != null) + { + MetadataReader reader = ecmaField.MetadataReader; + FieldDefinition fieldDef = reader.GetFieldDefinition(ecmaField.Handle); + ConstantHandle defaultValueHandle = fieldDef.GetDefaultValue(); + if (!defaultValueHandle.IsNil) + { + return HandleConstant(ecmaField.Module, defaultValueHandle); + } + } + return 0; + } + + private ulong HandleConstant(EcmaModule module, ConstantHandle constantHandle) + { + MetadataReader reader = module.MetadataReader; + Constant constant = reader.GetConstant(constantHandle); + BlobReader blob = reader.GetBlobReader(constant.Value); + switch (constant.TypeCode) + { + case ConstantTypeCode.Byte: + return (ulong)blob.ReadByte(); + case ConstantTypeCode.Int16: + return (ulong)blob.ReadInt16(); + case ConstantTypeCode.Int32: + return (ulong)blob.ReadInt32(); + case ConstantTypeCode.Int64: + return (ulong)blob.ReadInt64(); + case ConstantTypeCode.SByte: + return (ulong)blob.ReadSByte(); + case ConstantTypeCode.UInt16: + return (ulong)blob.ReadUInt16(); + case ConstantTypeCode.UInt32: + return (ulong)blob.ReadUInt32(); + case ConstantTypeCode.UInt64: + return (ulong)blob.ReadUInt64(); + } + System.Diagnostics.Debug.Assert(false); + return 0; + } + + bool ShouldUseCanonicalTypeRecord(TypeDesc type) + { + // TODO: check the type's generic complexity + return type.GetGenericDepth() > NodeFactory.TypeSystemContext.GenericsConfig.MaxGenericDepthOfDebugRecord; + } + + TypeDesc GetDebugType(TypeDesc type) + { + TypeDesc typeGenericComplexityInfo = type; + + // Strip off pointer, array, and byref details. + while (typeGenericComplexityInfo is ParameterizedType paramType) { + typeGenericComplexityInfo = paramType.ParameterType; + } + + // Types that have some canonical subtypes types should always be represented in normalized canonical form to the binder. + // Also, to avoid infinite generic recursion issues, attempt to use canonical form for fields with high generic complexity. + if (type.IsCanonicalSubtype(CanonicalFormKind.Specific) || (typeGenericComplexityInfo is DefType defType) && ShouldUseCanonicalTypeRecord(defType)) + { + type = type.ConvertToCanonForm(CanonicalFormKind.Specific); + + // Re-check if the canonical subtype has acceptable generic complexity + typeGenericComplexityInfo = type; + + while (typeGenericComplexityInfo is ParameterizedType paramType) { + typeGenericComplexityInfo = paramType.ParameterType; + } + + if ((typeGenericComplexityInfo is DefType canonDefType) && ShouldUseCanonicalTypeRecord(canonDefType)) + { + type = type.ConvertToCanonForm(CanonicalFormKind.Universal); + } + } + + return type; + } + + TypeDesc GetFieldDebugType(FieldDesc field) + { + return GetDebugType(field.FieldType); + } + + private uint GetClassTypeIndex(TypeDesc type, bool needsCompleteType) + { + TypeDesc debugType = GetDebugType(type); + DefType defType = debugType as DefType; + System.Diagnostics.Debug.Assert(defType != null, "GetClassTypeIndex was called with non def type"); + ClassTypeDescriptor classTypeDescriptor = new ClassTypeDescriptor + { + IsStruct = type.IsValueType ? 1 : 0, + Name = _objectWriter.GetMangledName(defType), + BaseClassId = 0, + InstanceSize = 0 + }; + + uint typeIndex = _objectWriter.GetClassTypeIndex(classTypeDescriptor); + _knownTypes[type] = typeIndex; + + if (!defType.InstanceByteCount.IsIndeterminate) + { + classTypeDescriptor.InstanceSize = (ulong)defType.InstanceByteCount.AsInt; + } + + if (type.HasBaseType && !type.IsValueType) + { + classTypeDescriptor.BaseClassId = GetTypeIndex(defType.BaseType, true); + } + else if (type.IsInterface) + { + // Allows debuggers to vtcast the types and see the real instance types. + classTypeDescriptor.BaseClassId = GetTypeIndex(type.Context.GetWellKnownType(WellKnownType.Object), true); + } + + List fieldsDescs = new List(); + List nonGcStaticFields = new List(); + List gcStaticFields = new List(); + List threadStaticFields = new List(); + List staticsDescs = new List(); + + string nonGcStaticDataName = NodeFactory.NameMangler.NodeMangler.NonGCStatics(type); + string gcStaticDataName = NodeFactory.NameMangler.NodeMangler.GCStatics(type); + string threadStaticDataName = NodeFactory.NameMangler.NodeMangler.ThreadStatics(type); + bool IsCoreRTAbi = Abi == TargetAbi.CoreRT; + + bool isCanonical = defType.IsCanonicalSubtype(CanonicalFormKind.Any); + + foreach (var fieldDesc in defType.GetFields()) + { + if (fieldDesc.HasRva || fieldDesc.IsLiteral) + continue; + + if (isCanonical && fieldDesc.IsStatic) + continue; + + LayoutInt fieldOffset = fieldDesc.Offset; + int fieldOffsetEmit = fieldOffset.IsIndeterminate ? 0xBAAD : fieldOffset.AsInt; + + TypeDesc fieldType = GetFieldDebugType(fieldDesc); + + // We're going to drill into this type more deeply and it might be more than what the + // compiler already looked at. e.g. if this is a reference type instance field that + // has fields that don't resolve we might hit resolution exceptions in the process: + // + // class NeverInstantiated + // { + // private UnresolvableType Foo; + // } + // + // class GeneratingDebugInfoForThis + // { + // // Going to throw here because instance layout of UnresolvableType cannot be computed. + // private NeverInstantiated Bar; + // } + // + // If this happens, just treat the field as Object or IntPtr. Better than crashing here. + // + // We are limiting this try/catch to when the type is not a valuetype. If it's a valuetype, + // we would generate a very invalid debug info. This should have been prevented elsewhere. + uint fieldTypeIndex; + try + { + fieldTypeIndex = GetVariableTypeIndex(fieldType, false); + } + catch (TypeSystemException) when (!fieldType.IsValueType) + { + fieldTypeIndex = fieldType.IsGCPointer ? + GetVariableTypeIndex(fieldType.Context.GetWellKnownType(WellKnownType.Object)) + : GetVariableTypeIndex(fieldType.Context.GetWellKnownType(WellKnownType.IntPtr)); + } + + DataFieldDescriptor field = new DataFieldDescriptor + { + FieldTypeIndex = fieldTypeIndex, + Offset = (ulong)fieldOffsetEmit, + Name = fieldDesc.Name + }; + + if (fieldDesc.IsStatic) + { + if (NodeFactory.Target.OperatingSystem != TargetOS.Windows) + { + StaticDataFieldDescriptor staticDesc = new StaticDataFieldDescriptor + { + StaticOffset = (ulong)fieldOffsetEmit + }; + + // Mark field as static + field.Offset = 0xFFFFFFFF; + + if (fieldDesc.IsThreadStatic) { + staticDesc.StaticDataName = threadStaticDataName; + staticDesc.IsStaticDataInObject = IsCoreRTAbi ? 1 : 0; + } else if (fieldDesc.HasGCStaticBase) { + staticDesc.StaticDataName = gcStaticDataName; + staticDesc.IsStaticDataInObject = IsCoreRTAbi ? 1 : 0; + } else { + staticDesc.StaticDataName = nonGcStaticDataName; + staticDesc.IsStaticDataInObject = 0; + } + + staticsDescs.Add(staticDesc); + } + + if (fieldDesc.IsThreadStatic) + threadStaticFields.Add(field); + else if (fieldDesc.HasGCStaticBase) + gcStaticFields.Add(field); + else + nonGcStaticFields.Add(field); + } + else + { + fieldsDescs.Add(field); + } + } + + if (NodeFactory.Target.OperatingSystem == TargetOS.Windows) + { + InsertStaticFieldRegionMember(fieldsDescs, defType, nonGcStaticFields, WindowsNodeMangler.NonGCStaticMemberName, false, false); + InsertStaticFieldRegionMember(fieldsDescs, defType, gcStaticFields, WindowsNodeMangler.GCStaticMemberName, IsCoreRTAbi, false); + InsertStaticFieldRegionMember(fieldsDescs, defType, threadStaticFields, WindowsNodeMangler.ThreadStaticMemberName, IsCoreRTAbi, true); + } + else + { + fieldsDescs.AddRange(nonGcStaticFields); + fieldsDescs.AddRange(gcStaticFields); + fieldsDescs.AddRange(threadStaticFields); + } + + DataFieldDescriptor[] fields = new DataFieldDescriptor[fieldsDescs.Count]; + for (int i = 0; i < fieldsDescs.Count; ++i) + { + fields[i] = fieldsDescs[i]; + } + + StaticDataFieldDescriptor[] statics = new StaticDataFieldDescriptor[staticsDescs.Count]; + for (int i = 0; i < staticsDescs.Count; ++i) + { + statics[i] = staticsDescs[i]; + } + + LayoutInt elementSize = defType.GetElementSize(); + int elementSizeEmit = elementSize.IsIndeterminate ? 0xBAAD : elementSize.AsInt; + ClassFieldsTypeDescriptor fieldsDescriptor = new ClassFieldsTypeDescriptor + { + Size = (ulong)elementSizeEmit, + FieldsCount = fieldsDescs.Count, + }; + + uint completeTypeIndex = _objectWriter.GetCompleteClassTypeIndex(classTypeDescriptor, fieldsDescriptor, fields, statics); + _completeKnownTypes[type] = completeTypeIndex; + + if (needsCompleteType) + return completeTypeIndex; + else + return typeIndex; + } + + private void InsertStaticFieldRegionMember(List fieldDescs, DefType defType, List staticFields, string staticFieldForm, + bool staticDataInObject, bool isThreadStatic) + { + if (staticFields != null && (staticFields.Count > 0)) + { + // Generate struct symbol for type describing individual fields of the statics region + ClassFieldsTypeDescriptor fieldsDescriptor = new ClassFieldsTypeDescriptor + { + Size = (ulong)0, + FieldsCount = staticFields.Count + }; + + ClassTypeDescriptor classTypeDescriptor = new ClassTypeDescriptor + { + IsStruct = !staticDataInObject ? 1 : 0, + Name = $"__type{staticFieldForm}{_objectWriter.GetMangledName(defType)}", + BaseClassId = 0 + }; + + if (staticDataInObject) + { + classTypeDescriptor.BaseClassId = GetTypeIndex(defType.Context.GetWellKnownType(WellKnownType.Object), true); + } + + uint staticFieldRegionTypeIndex = _objectWriter.GetCompleteClassTypeIndex(classTypeDescriptor, fieldsDescriptor, staticFields.ToArray(), null); + uint staticFieldRegionSymbolTypeIndex = staticFieldRegionTypeIndex; + + if (isThreadStatic) + { + // Generate helper struct used by natvis to get to the actual thread static data + ClassFieldsTypeDescriptor helperFieldsDescriptor = new ClassFieldsTypeDescriptor + { + Size = (ulong)NodeFactory.Target.PointerSize * 2ul, + FieldsCount = 2 + }; + + ClassTypeDescriptor helperClassTypeDescriptor = new ClassTypeDescriptor + { + IsStruct = 1, + Name = $"__ThreadStaticHelper<{classTypeDescriptor.Name}>", + BaseClassId = 0 + }; + var pointerTypeDescriptor = new PointerTypeDescriptor + { + Is64Bit = Is64Bit ? 1 : 0, + IsConst = 0, + IsReference = 0, + ElementType = GetTypeIndex(defType.Context.SystemModule.GetType("Internal.Runtime.CompilerHelpers", "TypeManagerSlot"), true) + }; + + var helperFields = new DataFieldDescriptor[] { + new DataFieldDescriptor + { + FieldTypeIndex = _objectWriter.GetPointerTypeIndex(pointerTypeDescriptor), + Offset = 0, + Name = "TypeManagerSlot" + }, + new DataFieldDescriptor + { + FieldTypeIndex = GetVariableTypeIndex(defType.Context.GetWellKnownType(Is64Bit? WellKnownType.Int64 : WellKnownType.Int32), true), + Offset = (ulong)NodeFactory.Target.PointerSize, + Name = "ClassIndex" + } + }; + + staticFieldRegionTypeIndex = _objectWriter.GetCompleteClassTypeIndex(helperClassTypeDescriptor, helperFieldsDescriptor, helperFields, null); + staticFieldRegionSymbolTypeIndex = staticFieldRegionTypeIndex; + staticFieldForm = WindowsNodeMangler.ThreadStaticIndexName; + } + else if (staticDataInObject)// This means that access to this static region is done via indirection + { + PointerTypeDescriptor pointerTypeDescriptor = new PointerTypeDescriptor(); + pointerTypeDescriptor.Is64Bit = Is64Bit ? 1 : 0; + pointerTypeDescriptor.IsConst = 0; + pointerTypeDescriptor.IsReference = 0; + pointerTypeDescriptor.ElementType = staticFieldRegionTypeIndex; + + staticFieldRegionSymbolTypeIndex = _objectWriter.GetPointerTypeIndex(pointerTypeDescriptor); + } + + DataFieldDescriptor staticRegionField = new DataFieldDescriptor + { + FieldTypeIndex = staticFieldRegionSymbolTypeIndex, + Offset = 0xFFFFFFFF, + Name = staticFieldForm + }; + + fieldDescs.Add(staticRegionField); + } + } + + private uint GetPrimitiveTypeIndex(TypeDesc type) + { + Debug.Assert(type.IsPrimitive, "it is not a primitive type"); + + uint typeIndex; + + if (_primitiveTypes.TryGetValue(type, out typeIndex)) + return typeIndex; + + typeIndex = _objectWriter.GetPrimitiveTypeIndex(type); + _primitiveTypes[type] = typeIndex; + + return typeIndex; + } + + private ITypesDebugInfoWriter _objectWriter; + private Dictionary _knownTypes = new Dictionary(); + private Dictionary _completeKnownTypes = new Dictionary(); + private Dictionary _knownReferenceWrappedTypes = new Dictionary(); + private Dictionary _knownStateMachineThisTypes = new Dictionary(); + private Dictionary _pointerTypes = new Dictionary(); + private Dictionary _enumTypes = new Dictionary(); + private Dictionary _byRefTypes = new Dictionary(); + private Dictionary _thisTypes = new Dictionary(); + private Dictionary _primitiveTypes = new Dictionary(); + private Dictionary _methodIndices = new Dictionary(); + private Dictionary _methodIdIndices = new Dictionary(); + + public ICollection> CompleteKnownTypes => _completeKnownTypes; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VTableSliceProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VTableSliceProvider.cs new file mode 100644 index 00000000000000..9f5c98eeccd769 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VTableSliceProvider.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler +{ + /// + /// Provides VTable information for a specific type. + /// + public abstract class VTableSliceProvider + { + internal abstract VTableSliceNode GetSlice(TypeDesc type); + } + + /// + /// Provides VTable information that collects data during the compilation to build a VTable for a type. + /// + public sealed class LazyVTableSliceProvider : VTableSliceProvider + { + internal override VTableSliceNode GetSlice(TypeDesc type) + { + return new LazilyBuiltVTableSliceNode(type); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VectorOfTFieldLayoutAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VectorOfTFieldLayoutAlgorithm.cs new file mode 100644 index 00000000000000..9b608cab136ddb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VectorOfTFieldLayoutAlgorithm.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// Represents an algorithm that computes field layout for the SIMD Vector<T> type + /// depending on the target details. + /// + public class VectorOfTFieldLayoutAlgorithm : FieldLayoutAlgorithm + { + private readonly FieldLayoutAlgorithm _fallbackAlgorithm; + + public VectorOfTFieldLayoutAlgorithm(FieldLayoutAlgorithm fallbackAlgorithm) + { + _fallbackAlgorithm = fallbackAlgorithm; + } + + public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defType, InstanceLayoutKind layoutKind) + { + TargetDetails targetDetails = defType.Context.Target; + + ComputedInstanceFieldLayout layoutFromMetadata = _fallbackAlgorithm.ComputeInstanceLayout(defType, layoutKind); + + LayoutInt instanceFieldSize; + + if (targetDetails.MaximumSimdVectorLength == SimdVectorLength.Vector128Bit) + { + instanceFieldSize = new LayoutInt(16); + } + else if (targetDetails.MaximumSimdVectorLength == SimdVectorLength.Vector256Bit) + { + instanceFieldSize = new LayoutInt(32); + } + else + { + Debug.Assert(targetDetails.MaximumSimdVectorLength == SimdVectorLength.None); + return layoutFromMetadata; + } + + return new ComputedInstanceFieldLayout + { + ByteCountUnaligned = instanceFieldSize, + ByteCountAlignment = layoutFromMetadata.ByteCountAlignment, + FieldAlignment = layoutFromMetadata.FieldAlignment, + FieldSize = instanceFieldSize, + Offsets = layoutFromMetadata.Offsets, + }; + } + + public unsafe override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefType defType, StaticLayoutKind layoutKind) + { + return _fallbackAlgorithm.ComputeStaticFieldLayout(defType, layoutKind); + } + + public override bool ComputeContainsGCPointers(DefType type) + { + return false; + } + + public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type) + { + if (type.Context.Target.Architecture == TargetArchitecture.ARM64 && + type.Instantiation[0].IsPrimitiveNumeric) + { + return type.InstanceFieldSize.AsInt switch + { + 8 => ValueTypeShapeCharacteristics.Vector64Aggregate, + 16 => ValueTypeShapeCharacteristics.Vector128Aggregate, + _ => ValueTypeShapeCharacteristics.None + }; + } + + return _fallbackAlgorithm.ComputeValueTypeShapeCharacteristics(type); + } + + public override bool ComputeIsUnsafeValueType(DefType type) + { + return false; + } + + public static bool IsVectorOfTType(DefType type) + { + return type.IsIntrinsic && type.Namespace == "System.Numerics" && type.Name == "Vector`1"; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VirtualMethodCallHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VirtualMethodCallHelper.cs new file mode 100644 index 00000000000000..2261d59239d0fc --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VirtualMethodCallHelper.cs @@ -0,0 +1,205 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler +{ + public static class VirtualMethodSlotHelper + { + public static int GetDefaultInterfaceMethodSlot(NodeFactory factory, MethodDesc method, TypeDesc implType, DefType interfaceOnDefinition, bool countDictionarySlots = true) + { + Debug.Assert(method.GetTypicalMethodDefinition().OwningType == interfaceOnDefinition.GetTypeDefinition()); + + SealedVTableNode sealedVTable = factory.SealedVTable(implType); + + // Ensure the sealed vtable is built before computing the slot + sealedVTable.BuildSealedVTableSlots(factory, relocsOnly: false /* GetVirtualMethodSlot is called in the final emission phase */); + + int sealedVTableSlot = sealedVTable.ComputeDefaultInterfaceMethodSlot(method, interfaceOnDefinition); + if (sealedVTableSlot == -1) + return -1; + + int numVTableSlots = GetNumberOfSlotsInCurrentType(factory, implType, countDictionarySlots); + + return numVTableSlots + sealedVTableSlot; + } + + /// + /// Given a virtual method decl, return its VTable slot if the method is used on its containing type. + /// Return -1 if the virtual method is not used. + /// + public static int GetVirtualMethodSlot(NodeFactory factory, MethodDesc method, TypeDesc implType, bool countDictionarySlots = true) + { + if (method.CanMethodBeInSealedVTable()) + { + // If the method is a sealed newslot method, it will be put in the sealed vtable instead of the type's vtable. In this + // case, the slot index return should be the index in the sealed vtable, plus the total number of vtable slots. + + // First, make sure we are not attempting to resolve the slot of a sealed vtable method on a special array type, which + // does not get any sealed vtable entries + Debug.Assert(!implType.IsArrayTypeWithoutGenericInterfaces()); + + SealedVTableNode sealedVTable = factory.SealedVTable(implType); + + // Ensure the sealed vtable is built before computing the slot + sealedVTable.BuildSealedVTableSlots(factory, relocsOnly: false /* GetVirtualMethodSlot is called in the final emission phase */); + + int sealedVTableSlot = sealedVTable.ComputeSealedVTableSlot(method); + if (sealedVTableSlot == -1) + return -1; + + int numVTableSlots = GetNumberOfSlotsInCurrentType(factory, implType, countDictionarySlots); + + return numVTableSlots + sealedVTableSlot; + } + else + { + // TODO: More efficient lookup of the slot + TypeDesc owningType = method.OwningType; + int baseSlots = GetNumberOfBaseSlots(factory, owningType, countDictionarySlots); + + // For types that have a generic dictionary, the introduced virtual method slots are + // prefixed with a pointer to the generic dictionary. + if (owningType.HasGenericDictionarySlot() && countDictionarySlots) + baseSlots++; + + IReadOnlyList virtualSlots = factory.VTable(owningType).Slots; + int methodSlot = -1; + int numSealedVTableEntries = 0; + for (int slot = 0; slot < virtualSlots.Count; slot++) + { + if (virtualSlots[slot].CanMethodBeInSealedVTable()) + { + numSealedVTableEntries++; + continue; + } + + if (virtualSlots[slot] == method) + { + methodSlot = slot; + break; + } + } + + return methodSlot == -1 ? -1 : baseSlots + methodSlot - numSealedVTableEntries; + } + } + + private static int GetNumberOfSlotsInCurrentType(NodeFactory factory, TypeDesc implType, bool countDictionarySlots) + { + if (implType.IsInterface) + { + // We normally don't need to ask about vtable slots of interfaces. It's not wrong to ask + // that question, but we currently only ask it for IDynamicInterfaceCastable implementations. + Debug.Assert(((MetadataType)implType).IsDynamicInterfaceCastableImplementation()); + return (implType.HasGenericDictionarySlot() && countDictionarySlots) ? 1 : 0; + } + + // Now compute the total number of vtable slots that would exist on the type + int baseSlots = GetNumberOfBaseSlots(factory, implType, countDictionarySlots); + + // For types that have a generic dictionary, the introduced virtual method slots are + // prefixed with a pointer to the generic dictionary. + if (implType.HasGenericDictionarySlot() && countDictionarySlots) + baseSlots++; + + int numVTableSlots = baseSlots; + IReadOnlyList virtualSlots = factory.VTable(implType).Slots; + for (int slot = 0; slot < virtualSlots.Count; slot++) + { + if (virtualSlots[slot].CanMethodBeInSealedVTable()) + continue; + numVTableSlots++; + } + + return numVTableSlots; + } + + private static int GetNumberOfBaseSlots(NodeFactory factory, TypeDesc owningType, bool countDictionarySlots) + { + int baseSlots = 0; + + TypeDesc baseType = owningType.BaseType; + TypeDesc templateBaseType = owningType.ConvertToCanonForm(CanonicalFormKind.Specific).BaseType; + + while (baseType != null) + { + // Normalize the base type. Necessary to make this work with the lazy vtable slot + // concept - if we start with a canonical type, the base type could end up being + // something like Base<__Canon, string>. We would get "0 slots used" for weird + // base types like this. + baseType = baseType.ConvertToCanonForm(CanonicalFormKind.Specific); + templateBaseType = templateBaseType.ConvertToCanonForm(CanonicalFormKind.Specific); + + // + // In the universal canonical types case, we could have base types in the hierarchy that are partial universal canonical types. + // The presence of these types could cause incorrect vtable layouts, so we need to fully canonicalize them and walk the + // hierarchy of the template type of the original input type to detect these cases. + // + // Exmaple: we begin with Derived<__UniversalCanon> and walk the template hierarchy: + // + // class Derived : Middle { } // -> Template is Derived<__UniversalCanon> and needs a dictionary slot + // // -> Basetype tempalte is Middle<__UniversalCanon, MyStruct>. It's a partial + // Universal canonical type, so we need to fully canonicalize it. + // + // class Middle : Base { } // -> Template is Middle<__UniversalCanon, __UniversalCanon> and needs a dictionary slot + // // -> Basetype template is Base<__UniversalCanon> + // + // class Base { } // -> Template is Base<__UniversalCanon> and needs a dictionary slot. + // + // If we had not fully canonicalized the Middle class template, we would have ended up with Base, which does not need + // a dictionary slot, meaning we would have created a vtable layout that the runtime does not expect. + // + + // For types that have a generic dictionary, the introduced virtual method slots are + // prefixed with a pointer to the generic dictionary. + if ((baseType.HasGenericDictionarySlot() || templateBaseType.HasGenericDictionarySlot()) && countDictionarySlots) + baseSlots++; + + IReadOnlyList baseVirtualSlots = factory.VTable(baseType).Slots; + foreach (var vtableMethod in baseVirtualSlots) + { + // Methods in the sealed vtable should be excluded from the count + if (vtableMethod.CanMethodBeInSealedVTable()) + continue; + baseSlots++; + } + + baseType = baseType.BaseType; + templateBaseType = templateBaseType.BaseType; + } + + return baseSlots; + } + + /// + /// Gets the vtable slot that holds the generic dictionary of this type. + /// + public static int GetGenericDictionarySlot(NodeFactory factory, TypeDesc type) + { + Debug.Assert(type.HasGenericDictionarySlot()); + return GetNumberOfBaseSlots(factory, type, countDictionarySlots: true); + } + + /// + /// Gets a value indicating whether the virtual method slots introduced by this type are prefixed + /// by a pointer to the generic dictionary of the type. + /// + public static bool HasGenericDictionarySlot(this TypeDesc type) + { + // Dictionary slots on generic interfaces are necessary to support static methods on interfaces + // The reason behind making this unconditional is simplicity, and keeping method slot indices for methods on IFoo + // and IFoo identical. That won't change. + if (type.IsInterface) + return type.HasInstantiation; + + return type.HasInstantiation && + (type.ConvertToCanonForm(CanonicalFormKind.Specific) != type || type.IsCanonicalSubtype(CanonicalFormKind.Any)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/WindowsNodeMangler.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/WindowsNodeMangler.cs new file mode 100644 index 00000000000000..3cbcd19ca39888 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/WindowsNodeMangler.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Text; +using Internal.TypeSystem; +using System.Diagnostics; + +namespace ILCompiler +{ + // + // The naming format of these names is known to the debugger + // + public class WindowsNodeMangler : NodeMangler + { + public const string NonGCStaticMemberName = "__NONGCSTATICS"; + public const string GCStaticMemberName = "__GCSTATICS"; + public const string ThreadStaticMemberName = "__THREADSTATICS"; + public const string ThreadStaticIndexName = "__THREADSTATICINDEX"; + + // Mangled name of boxed version of a type + public sealed override string MangledBoxedTypeName(TypeDesc type) + { + Debug.Assert(type.IsValueType); + return "Boxed_" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string MethodTable(TypeDesc type) + { + string mangledJustTypeName; + + if (type.IsValueType) + mangledJustTypeName = MangledBoxedTypeName(type); + else + mangledJustTypeName = NameMangler.GetMangledTypeName(type); + + // "??_7TypeName@@6B@" is the C++ mangling for "const TypeName::`vftable'" + // This, along with LF_VTSHAPE debug records added by the object writer + // is the debugger magic that allows debuggers to vcast types to their bases. + return "??_7" + mangledJustTypeName + "@@6B@"; + } + + private string CreateStaticFieldName(TypeDesc type, string fieldName) + { + return @$"?{fieldName}@{NameMangler.GetMangledTypeName(type)}@@"; + } + + public sealed override string GCStatics(TypeDesc type) + { + return CreateStaticFieldName(type, GCStaticMemberName); + } + + public sealed override string NonGCStatics(TypeDesc type) + { + return CreateStaticFieldName(type, NonGCStaticMemberName); + } + + public sealed override string ThreadStatics(TypeDesc type) + { + return CreateStaticFieldName(type, NameMangler.CompilationUnitPrefix + ThreadStaticMemberName); + } + + public sealed override string ThreadStaticsIndex(TypeDesc type) + { + return CreateStaticFieldName(type, ThreadStaticIndexName); + } + + public sealed override string TypeGenericDictionary(TypeDesc type) + { + return GenericDictionaryNamePrefix + NameMangler.GetMangledTypeName(type); + } + + public override string MethodGenericDictionary(MethodDesc method) + { + return GenericDictionaryNamePrefix + NameMangler.GetMangledMethodName(method); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs new file mode 100644 index 00000000000000..32131808adece0 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -0,0 +1,1319 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; +using Internal.ReadyToRunConstants; + +using ILCompiler; +using ILCompiler.DependencyAnalysis; + +using Debug = System.Diagnostics.Debug; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; + +namespace Internal.IL +{ + // Implements an IL scanner that scans method bodies to be compiled by the code generation + // backend before the actual compilation happens to gain insights into the code. + partial class ILImporter + { + private readonly MethodIL _methodIL; + private readonly MethodIL _canonMethodIL; + private readonly ILScanner _compilation; + private readonly ILScanNodeFactory _factory; + + // True if we're scanning a throwing method body because scanning the real body failed. + private readonly bool _isFallbackBodyCompilation; + + private readonly MethodDesc _canonMethod; + + private DependencyList _dependencies = new DependencyList(); + + private readonly byte[] _ilBytes; + + private class BasicBlock + { + // Common fields + public enum ImportState : byte + { + Unmarked, + IsPending + } + + public BasicBlock Next; + + public int StartOffset; + public ImportState State = ImportState.Unmarked; + + public bool TryStart; + public bool FilterStart; + public bool HandlerStart; + } + + private bool _isReadOnly; + private TypeDesc _constrained; + + private int _currentInstructionOffset; + private int _previousInstructionOffset; + + private class ExceptionRegion + { + public ILExceptionRegion ILRegion; + } + private ExceptionRegion[] _exceptionRegions; + + public ILImporter(ILScanner compilation, MethodDesc method, MethodIL methodIL = null) + { + if (methodIL == null) + { + methodIL = compilation.GetMethodIL(method); + } + else + { + _isFallbackBodyCompilation = true; + } + + // This is e.g. an "extern" method in C# without a DllImport or InternalCall. + if (methodIL == null) + { + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramSpecific, method); + } + + _compilation = compilation; + _factory = (ILScanNodeFactory)compilation.NodeFactory; + + _ilBytes = methodIL.GetILBytes(); + + _canonMethodIL = methodIL; + + // Get the runtime determined method IL so that this works right in shared code + // and tokens in shared code resolve to runtime determined types. + MethodIL uninstantiatiedMethodIL = methodIL.GetMethodILDefinition(); + if (methodIL != uninstantiatiedMethodIL) + { + MethodDesc sharedMethod = method.GetSharedRuntimeFormMethodTarget(); + _methodIL = new InstantiatedMethodIL(sharedMethod, uninstantiatiedMethodIL); + } + else + { + _methodIL = methodIL; + } + + _canonMethod = method; + + var ilExceptionRegions = methodIL.GetExceptionRegions(); + _exceptionRegions = new ExceptionRegion[ilExceptionRegions.Length]; + for (int i = 0; i < ilExceptionRegions.Length; i++) + { + _exceptionRegions[i] = new ExceptionRegion() { ILRegion = ilExceptionRegions[i] }; + } + } + + public DependencyList Import() + { + TypeDesc owningType = _canonMethod.OwningType; + if (_compilation.HasLazyStaticConstructor(owningType)) + { + // Don't trigger cctor if this is a fallback compilation (bad cctor could have been the reason for fallback). + // Otherwise follow the rules from ECMA-335 I.8.9.5. + if (!_isFallbackBodyCompilation && + (_canonMethod.Signature.IsStatic || _canonMethod.IsConstructor || owningType.IsValueType)) + { + // For beforefieldinit, we can wait for field access. + if (!((MetadataType)owningType).IsBeforeFieldInit) + { + MethodDesc method = _methodIL.OwningMethod; + if (method.OwningType.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.GetNonGCStaticBase, method.OwningType), "Owning type cctor"); + } + else + { + _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.GetNonGCStaticBase, method.OwningType), "Owning type cctor"); + } + } + } + } + + if (_canonMethod.IsSynchronized) + { + const string reason = "Synchronized method"; + if (_canonMethod.Signature.IsStatic) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.MonitorEnterStatic), reason); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.MonitorExitStatic), reason); + } + else + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.MonitorEnter), reason); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.MonitorExit), reason); + } + + } + + FindBasicBlocks(); + ImportBasicBlocks(); + + CodeBasedDependencyAlgorithm.AddDependenciesDueToMethodCodePresence(ref _dependencies, _factory, _canonMethod, _canonMethodIL); + + return _dependencies; + } + + private ISymbolNode GetGenericLookupHelper(ReadyToRunHelperId helperId, object helperArgument) + { + GenericDictionaryLookup lookup = _compilation.ComputeGenericLookup(_canonMethod, helperId, helperArgument); + Debug.Assert(lookup.UseHelper); + + if (_canonMethod.RequiresInstMethodDescArg()) + { + return _compilation.NodeFactory.ReadyToRunHelperFromDictionaryLookup(lookup.HelperId, lookup.HelperObject, _canonMethod); + } + else + { + Debug.Assert(_canonMethod.RequiresInstArg() || _canonMethod.AcquiresInstMethodTableFromThis()); + return _compilation.NodeFactory.ReadyToRunHelperFromTypeLookup(lookup.HelperId, lookup.HelperObject, _canonMethod.OwningType); + } + } + + private ISymbolNode GetHelperEntrypoint(ReadyToRunHelper helper) + { + return _compilation.GetHelperEntrypoint(helper); + } + + private void MarkInstructionBoundary() { } + private void EndImportingBasicBlock(BasicBlock basicBlock) { } + + private void StartImportingBasicBlock(BasicBlock basicBlock) + { + // Import all associated EH regions + foreach (ExceptionRegion ehRegion in _exceptionRegions) + { + ILExceptionRegion region = ehRegion.ILRegion; + if (region.TryOffset == basicBlock.StartOffset) + { + MarkBasicBlock(_basicBlocks[region.HandlerOffset]); + if (region.Kind == ILExceptionRegionKind.Filter) + MarkBasicBlock(_basicBlocks[region.FilterOffset]); + + // Once https://github.com/dotnet/corert/issues/3460 is done, this should be deleted. + // Throwing InvalidProgram is not great, but we want to do *something* if this happens + // because doing nothing means problems at runtime. This is not worth piping a + // a new exception with a fancy message for. + if (region.Kind == ILExceptionRegionKind.Catch) + { + TypeDesc catchType = (TypeDesc)_methodIL.GetObject(region.ClassToken); + if (catchType.IsRuntimeDeterminedSubtype) + ThrowHelper.ThrowInvalidProgramException(); + } + } + } + + _currentInstructionOffset = -1; + _previousInstructionOffset = -1; + } + + private void StartImportingInstruction() + { + _previousInstructionOffset = _currentInstructionOffset; + _currentInstructionOffset = _currentOffset; + } + + private void EndImportingInstruction() + { + // The instruction should have consumed any prefixes. + _constrained = null; + _isReadOnly = false; + } + + private void ImportJmp(int token) + { + // JMP is kind of like a tail call (with no arguments pushed on the stack). + ImportCall(ILOpcode.call, token); + } + + private void ImportCasting(ILOpcode opcode, int token) + { + TypeDesc type = (TypeDesc)_methodIL.GetObject(token); + + if (type.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandleForCasting, type), "IsInst/CastClass"); + } + else + { + _dependencies.Add(_compilation.ComputeConstantLookup(ReadyToRunHelperId.TypeHandleForCasting, type), "IsInst/CastClass"); + } + } + + private IMethodNode GetMethodEntrypoint(MethodDesc method) + { + if (method.HasInstantiation || method.OwningType.HasInstantiation) + { + _compilation.DetectGenericCycles(_canonMethod, method); + } + + return _factory.MethodEntrypoint(method); + } + + private void ImportCall(ILOpcode opcode, int token) + { + // We get both the canonical and runtime determined form - JitInterface mostly operates + // on the canonical form. + var runtimeDeterminedMethod = (MethodDesc)_methodIL.GetObject(token); + var method = (MethodDesc)_canonMethodIL.GetObject(token); + + _compilation.NodeFactory.MetadataManager.GetDependenciesDueToAccess(ref _dependencies, _compilation.NodeFactory, _canonMethodIL, method); + + if (method.IsRawPInvoke()) + { + // Raw P/invokes don't have any dependencies. + return; + } + + string reason = null; + switch (opcode) + { + case ILOpcode.newobj: + reason = "newobj"; break; + case ILOpcode.call: + reason = "call"; break; + case ILOpcode.callvirt: + reason = "callvirt"; break; + case ILOpcode.ldftn: + reason = "ldftn"; break; + case ILOpcode.ldvirtftn: + reason = "ldvirtftn"; break; + default: + Debug.Assert(false); break; + } + + if (opcode == ILOpcode.newobj) + { + TypeDesc owningType = runtimeDeterminedMethod.OwningType; + if (owningType.IsString) + { + // String .ctor handled specially below + } + else if (owningType.IsGCPointer) + { + if (owningType.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, owningType), reason); + } + else + { + _dependencies.Add(_factory.ConstructedTypeSymbol(owningType), reason); + } + + if (owningType.IsArray) + { + // RyuJIT is going to call the "MdArray" creation helper even if this is an SzArray, + // hence the IsArray check above. Note that the MdArray helper can handle SzArrays. + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewMultiDimArr_NonVarArg), reason); + return; + } + else + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewObject), reason); + } + } + + if (owningType.IsDelegate) + { + // If this is a verifiable delegate construction sequence, the previous instruction is a ldftn/ldvirtftn + if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.prefix1) + { + // TODO: for ldvirtftn we need to also check for the `dup` instruction, otherwise this is a normal newobj. + + ILOpcode previousOpcode = (ILOpcode)(0x100 + _ilBytes[_previousInstructionOffset + 1]); + if (previousOpcode == ILOpcode.ldvirtftn || previousOpcode == ILOpcode.ldftn) + { + int delTargetToken = ReadILTokenAt(_previousInstructionOffset + 2); + var delTargetMethod = (MethodDesc)_methodIL.GetObject(delTargetToken); + TypeDesc canonDelegateType = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); + DelegateCreationInfo info = _compilation.GetDelegateCtor(canonDelegateType, delTargetMethod, previousOpcode == ILOpcode.ldvirtftn); + + if (info.NeedsRuntimeLookup) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DelegateCtor, info), reason); + } + else + { + _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.DelegateCtor, info), reason); + } + + return; + } + } + } + } + + if (method.OwningType.IsDelegate && method.Name == "Invoke" && + opcode != ILOpcode.ldftn && opcode != ILOpcode.ldvirtftn) + { + // This call is expanded as an intrinsic; it's not an actual function call. + // Before codegen realizes this is an intrinsic, it might still ask questions about + // the vtable of this virtual method, so let's make sure it's marked in the scanner's + // dependency graph. + _dependencies.Add(_factory.VTable(method.OwningType), reason); + return; + } + + if (method.IsIntrinsic) + { + if (IsRuntimeHelpersInitializeArrayOrCreateSpan(method)) + { + if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) + return; + } + + if (IsActivatorDefaultConstructorOf(method)) + { + if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DefaultConstructor, runtimeDeterminedMethod.Instantiation[0]), reason); + } + else + { + MethodDesc ctor = Compilation.GetConstructorForCreateInstanceIntrinsic(method.Instantiation[0]); + _dependencies.Add(_factory.CanonicalEntrypoint(ctor), reason); + } + + return; + } + + if (IsActivatorAllocatorOf(method)) + { + if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.ObjectAllocator, runtimeDeterminedMethod.Instantiation[0]), reason); + } + else + { + _dependencies.Add(_compilation.ComputeConstantLookup(ReadyToRunHelperId.ObjectAllocator, method.Instantiation[0]), reason); + } + + return; + } + + if (method.OwningType.IsByReferenceOfT && (method.IsConstructor || method.Name == "get_Value")) + { + return; + } + + if (IsEETypePtrOf(method)) + { + if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.Instantiation[0]), reason); + } + else + { + _dependencies.Add(_factory.ConstructedTypeSymbol(method.Instantiation[0]), reason); + } + return; + } + } + + TypeDesc exactType = method.OwningType; + + if (method.IsUnmanagedCallersOnly && (opcode != ILOpcode.ldftn && opcode != ILOpcode.ldvirtftn)) + { + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramUnmanagedCallersOnly, method); + } + + bool resolvedConstraint = false; + bool forceUseRuntimeLookup = false; + + MethodDesc methodAfterConstraintResolution = method; + if (_constrained != null) + { + // We have a "constrained." call. Try a partial resolve of the constraint call. Note that this + // will not necessarily resolve the call exactly, since we might be compiling + // shared generic code - it may just resolve it to a candidate suitable for + // JIT compilation, and require a runtime lookup for the actual code pointer + // to call. + + TypeDesc constrained = _constrained; + if (constrained.IsRuntimeDeterminedSubtype) + constrained = constrained.ConvertToCanonForm(CanonicalFormKind.Specific); + + MethodDesc directMethod = constrained.GetClosestDefType().TryResolveConstraintMethodApprox(method.OwningType, method, out forceUseRuntimeLookup); + if (directMethod == null && constrained.IsEnum) + { + // Constrained calls to methods on enum methods resolve to System.Enum's methods. System.Enum is a reference + // type though, so we would fail to resolve and box. We have a special path for those to avoid boxing. + directMethod = _compilation.TypeSystemContext.TryResolveConstrainedEnumMethod(constrained, method); + } + + if (directMethod != null) + { + // Either + // 1. no constraint resolution at compile time (!directMethod) + // OR 2. no code sharing lookup in call + // OR 3. we have have resolved to an instantiating stub + + methodAfterConstraintResolution = directMethod; + + Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface); + resolvedConstraint = true; + + exactType = constrained; + } + else if (constrained.IsValueType) + { + // We'll need to box `this`. Note we use _constrained here, because the other one is canonical. + AddBoxingDependencies(_constrained, reason); + } + } + + MethodDesc targetMethod = methodAfterConstraintResolution; + + bool exactContextNeedsRuntimeLookup; + if (targetMethod.HasInstantiation) + { + exactContextNeedsRuntimeLookup = targetMethod.IsSharedByGenericInstantiations; + } + else + { + exactContextNeedsRuntimeLookup = exactType.IsCanonicalSubtype(CanonicalFormKind.Any); + } + + // + // Determine whether to perform direct call + // + + bool directCall = false; + + if (targetMethod.Signature.IsStatic) + { + // Static methods are always direct calls + directCall = true; + } + else if ((opcode != ILOpcode.callvirt && opcode != ILOpcode.ldvirtftn) || resolvedConstraint) + { + directCall = true; + } + else + { + if (!targetMethod.IsVirtual || + // Final/sealed has no meaning for interfaces, but lets us devirtualize otherwise + (!targetMethod.OwningType.IsInterface && (targetMethod.IsFinal || targetMethod.OwningType.IsSealed()))) + { + directCall = true; + } + } + + if (directCall && targetMethod.IsAbstract) + { + ThrowHelper.ThrowBadImageFormatException(); + } + + bool allowInstParam = opcode != ILOpcode.ldvirtftn && opcode != ILOpcode.ldftn; + + if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg()) + { + // Needs a single address to call this method but the method needs a hidden argument. + // We need a fat function pointer for this that captures both things. + + if (exactContextNeedsRuntimeLookup) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, runtimeDeterminedMethod), reason); + } + else + { + _dependencies.Add(_factory.FatFunctionPointer(runtimeDeterminedMethod), reason); + } + } + else if (directCall && resolvedConstraint && exactContextNeedsRuntimeLookup) + { + // We want to do a direct call to a shared method on a valuetype. We need to provide + // a generic context, but the JitInterface doesn't provide a way for us to do it from here. + // So we do the next best thing and ask RyuJIT to look up a fat pointer. + // + // We have the canonical version of the method - find the runtime determined version. + // This is simplified because we know the method is on a valuetype. + Debug.Assert(targetMethod.OwningType.IsValueType); + MethodDesc targetOfLookup; + if (_constrained.IsRuntimeDeterminedType) + targetOfLookup = _compilation.TypeSystemContext.GetMethodForRuntimeDeterminedType(targetMethod.GetTypicalMethodDefinition(), (RuntimeDeterminedType)_constrained); + else + targetOfLookup = _compilation.TypeSystemContext.GetMethodForInstantiatedType(targetMethod.GetTypicalMethodDefinition(), (InstantiatedType)_constrained); + if (targetOfLookup.HasInstantiation) + { + targetOfLookup = targetOfLookup.MakeInstantiatedMethod(runtimeDeterminedMethod.Instantiation); + } + Debug.Assert(targetOfLookup.GetCanonMethodTarget(CanonicalFormKind.Specific) == targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)); + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, targetOfLookup), reason); + } + else if (directCall) + { + bool referencingArrayAddressMethod = false; + + if (targetMethod.IsIntrinsic) + { + // If this is an intrinsic method with a callsite-specific expansion, this will replace + // the method with a method the intrinsic expands into. If it's not the special intrinsic, + // method stays unchanged. + targetMethod = _compilation.ExpandIntrinsicForCallsite(targetMethod, _canonMethod); + + // Array address method requires special dependency tracking. + referencingArrayAddressMethod = targetMethod.IsArrayAddressMethod(); + } + + MethodDesc concreteMethod = targetMethod; + targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + + if (targetMethod.IsConstructor && targetMethod.OwningType.IsString) + { + _dependencies.Add(_factory.StringAllocator(targetMethod), reason); + } + else if (exactContextNeedsRuntimeLookup) + { + if (targetMethod.IsSharedByGenericInstantiations && !resolvedConstraint && !referencingArrayAddressMethod) + { + ISymbolNode instParam = null; + + if (targetMethod.RequiresInstMethodDescArg()) + { + instParam = GetGenericLookupHelper(ReadyToRunHelperId.MethodDictionary, runtimeDeterminedMethod); + } + else if (targetMethod.RequiresInstMethodTableArg()) + { + bool hasHiddenParameter = true; + + if (targetMethod.IsIntrinsic) + { + if (_factory.TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(targetMethod)) + hasHiddenParameter = false; + } + + if (hasHiddenParameter) + instParam = GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType); + } + + if (instParam != null) + { + _dependencies.Add(instParam, reason); + } + + if (instParam == null + && !targetMethod.OwningType.IsValueType + && !_factory.TypeSystemContext.IsSpecialUnboxingThunk(_canonMethod)) + { + // We have a call to a shared instance method and we're already in a shared context. + // e.g. this is a call to Foo.Method() and we're about to add Foo<__Canon>.Method() + // to the dependency graph). + // + // We will pretend the runtime determined owning type (Foo) got allocated as well. + // This is because RyuJIT might end up inlining the shared method body, making it concrete again, + // without actually having to go through a dictionary. + // (This would require inlining across two generic contexts, but RyuJIT does that.) + // + // If we didn't have a constructed type for this at the scanning time, we wouldn't + // know the dictionary dependencies at the inlined site, leading to a compile failure. + // (Remember that dictionary dependencies of instance methods on generic reference types + // are tied to the owning type.) + // + // This is not ideal, because if e.g. Foo never got allocated otherwise, this code is + // unreachable and we're making the scanner scan more of it. + // + // Technically, we could get away with injecting a RuntimeDeterminedMethodNode here + // but that introduces more complexities and doesn't seem worth it at this time. + Debug.Assert(targetMethod.AcquiresInstMethodTableFromThis()); + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType), reason + " - inlining protection"); + } + + _dependencies.Add(_factory.CanonicalEntrypoint(targetMethod), reason); + } + else + { + Debug.Assert(!forceUseRuntimeLookup); + _dependencies.Add(GetMethodEntrypoint(targetMethod), reason); + + if (targetMethod.RequiresInstMethodTableArg() && resolvedConstraint) + { + if (_constrained.IsRuntimeDeterminedSubtype) + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, _constrained), reason); + else + _dependencies.Add(_factory.ConstructedTypeSymbol(_constrained), reason); + } + + if (referencingArrayAddressMethod && !_isReadOnly) + { + // Address method is special - it expects an instantiation argument, unless a readonly prefix was applied. + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType), reason); + } + } + } + else + { + ISymbolNode instParam = null; + + if (targetMethod.RequiresInstMethodDescArg()) + { + instParam = _compilation.NodeFactory.MethodGenericDictionary(concreteMethod); + } + else if (targetMethod.RequiresInstMethodTableArg() || (referencingArrayAddressMethod && !_isReadOnly)) + { + // Ask for a constructed type symbol because we need the vtable to get to the dictionary + instParam = _compilation.NodeFactory.ConstructedTypeSymbol(concreteMethod.OwningType); + } + + if (instParam != null) + { + _dependencies.Add(instParam, reason); + } + + if (instParam == null + && concreteMethod != targetMethod + && targetMethod.OwningType.NormalizeInstantiation() == targetMethod.OwningType + && !targetMethod.OwningType.IsValueType) + { + // We have a call to a shared instance method and we still know the concrete + // type of the generic instance (e.g. this is a call to Foo.Method() + // and we're about to add Foo<__Canon>.Method() to the dependency graph). + // + // We will pretend the concrete type got allocated as well. This is because RyuJIT might + // end up inlining the shared method body, making it concrete again. + // + // If we didn't have a constructed type for this at the scanning time, we wouldn't + // know the dictionary dependencies at the inlined site, leading to a compile failure. + // (Remember that dictionary dependencies of instance methods on generic reference types + // are tied to the owning type.) + // + // This is not ideal, because if Foo never got allocated otherwise, this code is + // unreachable and we're making the scanner scan more of it. + // + // Technically, we could get away with injecting a ShadowConcreteMethod for the concrete + // method, but that's more complex and doesn't seem worth it at this time. + Debug.Assert(targetMethod.AcquiresInstMethodTableFromThis()); + _dependencies.Add(_compilation.NodeFactory.MaximallyConstructableType(concreteMethod.OwningType), reason + " - inlining protection"); + } + + _dependencies.Add(GetMethodEntrypoint(targetMethod), reason); + } + } + else if (method.HasInstantiation) + { + // Generic virtual method call + + MethodDesc methodToLookup = _compilation.GetTargetOfGenericVirtualMethodCall(runtimeDeterminedMethod); + + _compilation.DetectGenericCycles( + _canonMethod, + methodToLookup.GetCanonMethodTarget(CanonicalFormKind.Specific)); + + if (exactContextNeedsRuntimeLookup) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodHandle, methodToLookup), reason); + } + else + { + _dependencies.Add(_factory.RuntimeMethodHandle(methodToLookup), reason); + } + + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GVMLookupForSlot), reason); + } + else if (method.OwningType.IsInterface) + { + if (exactContextNeedsRuntimeLookup) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.VirtualDispatchCell, runtimeDeterminedMethod), reason); + } + else + { + _dependencies.Add(_factory.InterfaceDispatchCell(method), reason); + } + } + else if (_compilation.HasFixedSlotVTable(method.OwningType)) + { + // No dependencies: virtual call through the vtable + } + else + { + MethodDesc slotDefiningMethod = targetMethod.IsNewSlot ? + targetMethod : MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod); + _dependencies.Add(_factory.VirtualMethodUse(slotDefiningMethod), reason); + } + } + + private void ImportLdFtn(int token, ILOpcode opCode) + { + // Is this a verifiable delegate creation? If so, we will handle it when we reach the newobj + if (_ilBytes[_currentOffset] == (byte)ILOpcode.newobj) + { + int delegateToken = ReadILTokenAt(_currentOffset + 1); + var delegateType = ((MethodDesc)_methodIL.GetObject(delegateToken)).OwningType; + if (delegateType.IsDelegate) + return; + } + + ImportCall(opCode, token); + } + + private void ImportBranch(ILOpcode opcode, BasicBlock target, BasicBlock fallthrough) + { + ImportFallthrough(target); + + if (fallthrough != null) + ImportFallthrough(fallthrough); + } + + private void ImportSwitchJump(int jmpBase, int[] jmpDelta, BasicBlock fallthrough) + { + for (int i = 0; i < jmpDelta.Length; i++) + { + BasicBlock target = _basicBlocks[jmpBase + jmpDelta[i]]; + ImportFallthrough(target); + } + + if (fallthrough != null) + ImportFallthrough(fallthrough); + } + + private void ImportUnbox(int token, ILOpcode opCode) + { + TypeDesc type = (TypeDesc)_methodIL.GetObject(token); + + if (!type.IsValueType) + { + if (opCode == ILOpcode.unbox_any) + { + // When applied to a reference type, unbox_any has the same effect as castclass. + ImportCasting(ILOpcode.castclass, token); + } + return; + } + + if (type.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type), "Unbox"); + } + else + { + _dependencies.Add(_factory.NecessaryTypeSymbol(type), "Unbox"); + } + + ReadyToRunHelper helper; + if (opCode == ILOpcode.unbox) + { + helper = ReadyToRunHelper.Unbox; + } + else + { + Debug.Assert(opCode == ILOpcode.unbox_any); + helper = ReadyToRunHelper.Unbox_Nullable; + } + + _dependencies.Add(GetHelperEntrypoint(helper), "Unbox"); + } + + private void ImportRefAnyVal(int token) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GetRefAny), "refanyval"); + } + + private void ImportMkRefAny(int token) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.TypeHandleToRuntimeType), "mkrefany"); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.TypeHandleToRuntimeTypeHandle), "mkrefany"); + } + + private void ImportLdToken(int token) + { + object obj = _methodIL.GetObject(token); + + if (obj is TypeDesc) + { + // If this is a ldtoken Type / Type.GetTypeFromHandle sequence, we need one more helper. + // We might also be able to optimize this a little if this is a ldtoken/GetTypeFromHandle/Equals sequence. + bool isTypeEquals = false; + BasicBlock nextBasicBlock = _basicBlocks[_currentOffset]; + if (nextBasicBlock == null) + { + if ((ILOpcode)_ilBytes[_currentOffset] == ILOpcode.call) + { + int methodToken = ReadILTokenAt(_currentOffset + 1); + var method = (MethodDesc)_methodIL.GetObject(methodToken); + if (IsTypeGetTypeFromHandle(method)) + { + // Codegen will swap this one for GetRuntimeTypeHandle when optimizing + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GetRuntimeType), "ldtoken"); + + // Is the next instruction a call to Type::Equals? + nextBasicBlock = _basicBlocks[_currentOffset + 5]; + if (nextBasicBlock == null) + { + if ((ILOpcode)_ilBytes[_currentOffset + 5] == ILOpcode.call) + { + methodToken = ReadILTokenAt(_currentOffset + 6); + method = (MethodDesc)_methodIL.GetObject(methodToken); + isTypeEquals = IsTypeEquals(method); + } + } + } + } + } + + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GetRuntimeTypeHandle), "ldtoken"); + + var type = (TypeDesc)obj; + + ISymbolNode reference; + if (type.IsRuntimeDeterminedSubtype) + { + reference = GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type); + } + else + { + reference = _compilation.ComputeConstantLookup( + isTypeEquals ? ReadyToRunHelperId.NecessaryTypeHandle : _compilation.GetLdTokenHelperForType(type), type); + } + _dependencies.Add(reference, "ldtoken"); + } + else if (obj is MethodDesc) + { + var method = (MethodDesc)obj; + if (method.IsRuntimeDeterminedExactMethod) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodHandle, method), "ldtoken"); + } + else + { + _dependencies.Add(_factory.RuntimeMethodHandle(method), "ldtoken"); + } + + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GetRuntimeMethodHandle), "ldtoken"); + } + else + { + Debug.Assert(obj is FieldDesc); + + // First check if this is a ldtoken Field followed by InitializeArray or CreateSpan. + BasicBlock nextBasicBlock = _basicBlocks[_currentOffset]; + if (nextBasicBlock == null) + { + if ((ILOpcode)_ilBytes[_currentOffset] == ILOpcode.call) + { + int methodToken = ReadILTokenAt(_currentOffset + 1); + var method = (MethodDesc)_methodIL.GetObject(methodToken); + if (IsRuntimeHelpersInitializeArrayOrCreateSpan(method)) + { + // Codegen expands this and doesn't do the normal ldtoken. + return; + } + } + } + + var field = (FieldDesc)obj; + if (field.OwningType.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.FieldHandle, field), "ldtoken"); + } + else + { + _dependencies.Add(_factory.RuntimeFieldHandle(field), "ldtoken"); + } + + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GetRuntimeFieldHandle), "ldtoken"); + } + } + + private void ImportRefAnyType() + { + // TODO + } + + private void ImportArgList() + { + } + + private void ImportConstrainedPrefix(int token) + { + _constrained = (TypeDesc)_methodIL.GetObject(token); + } + + private void ImportReadOnlyPrefix() + { + _isReadOnly = true; + } + + private void ImportFieldAccess(int token, bool isStatic, string reason) + { + var field = (FieldDesc)_methodIL.GetObject(token); + + _compilation.NodeFactory.MetadataManager.GetDependenciesDueToAccess(ref _dependencies, _compilation.NodeFactory, _canonMethodIL, field); + + // Covers both ldsfld/ldsflda and ldfld/ldflda with a static field + if (isStatic || field.IsStatic) + { + // ldsfld/ldsflda with an instance field is invalid IL + if (isStatic && !field.IsStatic) + ThrowHelper.ThrowInvalidProgramException(); + + // References to literal fields from IL body should never resolve. + // The CLR would throw a MissingFieldException while jitting and so should we. + if (field.IsLiteral) + ThrowHelper.ThrowMissingFieldException(field.OwningType, field.Name); + + if (field.HasRva) + { + // We don't care about field RVA data for the usual cases, but if this is one of the + // magic fields the compiler synthetized, the data blob might bring more dependencies + // and we need to scan those. + _dependencies.Add(_compilation.GetFieldRvaData(field), reason); + // TODO: lazy cctor dependency + return; + } + + ReadyToRunHelperId helperId; + if (field.IsThreadStatic) + { + helperId = ReadyToRunHelperId.GetThreadStaticBase; + } + else if (field.HasGCStaticBase) + { + helperId = ReadyToRunHelperId.GetGCStaticBase; + } + else + { + helperId = ReadyToRunHelperId.GetNonGCStaticBase; + } + + TypeDesc owningType = field.OwningType; + if (owningType.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(helperId, owningType), reason); + } + else + { + _dependencies.Add(_factory.ReadyToRunHelper(helperId, owningType), reason); + } + } + } + + private void ImportLoadField(int token, bool isStatic) + { + ImportFieldAccess(token, isStatic, isStatic ? "ldsfld" : "ldfld"); + } + + private void ImportAddressOfField(int token, bool isStatic) + { + ImportFieldAccess(token, isStatic, isStatic ? "ldsflda" : "ldflda"); + } + + private void ImportStoreField(int token, bool isStatic) + { + ImportFieldAccess(token, isStatic, isStatic ? "stsfld" : "stfld"); + } + + private void ImportLoadString(int token) + { + // If we care, this can include allocating the frozen string node. + _dependencies.Add(_factory.SerializedStringObject(""), "ldstr"); + } + + private void ImportBox(int token) + { + AddBoxingDependencies((TypeDesc)_methodIL.GetObject(token), "Box"); + } + + private void AddBoxingDependencies(TypeDesc type, string reason) + { + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any)); + + // Generic code will have BOX instructions when referring to T - the instruction is a no-op + // if the substitution wasn't a value type. + if (!type.IsValueType) + return; + + if (type.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type), reason); + } + else + { + _dependencies.Add(_factory.ConstructedTypeSymbol(type), reason); + } + + if (type.IsNullable) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Box), reason); + } + else + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Box_Nullable), reason); + } + } + + private void ImportLeave(BasicBlock target) + { + ImportFallthrough(target); + } + + private void ImportNewArray(int token) + { + var type = ((TypeDesc)_methodIL.GetObject(token)).MakeArrayType(); + if (type.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type), "newarr"); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewArray), "newarr"); + } + else + { + _dependencies.Add(_factory.ConstructedTypeSymbol(type), "newarr"); + } + } + + private void ImportLoadElement(int token) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.RngChkFail), "ldelem"); + } + + private void ImportLoadElement(TypeDesc elementType) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.RngChkFail), "ldelem"); + } + + private void ImportStoreElement(int token) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.RngChkFail), "stelem"); + } + + private void ImportStoreElement(TypeDesc elementType) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.RngChkFail), "stelem"); + } + + private void ImportAddressOfElement(int token) + { + TypeDesc elementType = (TypeDesc)_methodIL.GetObject(token); + if (elementType.IsGCPointer && !_isReadOnly) + { + if (elementType.IsRuntimeDeterminedSubtype) + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, elementType), "ldelema"); + else + _dependencies.Add(_factory.NecessaryTypeSymbol(elementType), "ldelema"); + } + + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.RngChkFail), "ldelema"); + } + + private void ImportBinaryOperation(ILOpcode opcode) + { + switch (opcode) + { + case ILOpcode.add_ovf: + case ILOpcode.add_ovf_un: + case ILOpcode.sub_ovf: + case ILOpcode.sub_ovf_un: + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Overflow), "_ovf"); + break; + case ILOpcode.mul_ovf: + case ILOpcode.mul_ovf_un: + if (_compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.ARM) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.LMulOfv), "_lmulovf"); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.ULMulOvf), "_ulmulovf"); + } + + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Overflow), "_ovf"); + break; + case ILOpcode.div: + case ILOpcode.div_un: + if (_compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.ARM) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.ULDiv), "_uldiv"); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.LDiv), "_ldiv"); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.UDiv), "_udiv"); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Div), "_div"); + } + else if (_compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.ARM64) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.ThrowDivZero), "_divbyzero"); + } + break; + case ILOpcode.rem: + case ILOpcode.rem_un: + if (_compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.ARM) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.ULMod), "_ulmod"); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.LMod), "_lmod"); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.UMod), "_umod"); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Mod), "_mod"); + } + else if (_compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.ARM64) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.ThrowDivZero), "_divbyzero"); + } + break; + } + } + + private void ImportFallthrough(BasicBlock next) + { + MarkBasicBlock(next); + } + + private int ReadILTokenAt(int ilOffset) + { + return (int)(_ilBytes[ilOffset] + + (_ilBytes[ilOffset + 1] << 8) + + (_ilBytes[ilOffset + 2] << 16) + + (_ilBytes[ilOffset + 3] << 24)); + } + + private void ReportInvalidBranchTarget(int targetOffset) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportFallthroughAtEndOfMethod() + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportMethodEndInsideInstruction() + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportInvalidInstruction(ILOpcode opcode) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private bool IsRuntimeHelpersInitializeArrayOrCreateSpan(MethodDesc method) + { + if (method.IsIntrinsic) + { + string name = method.Name; + if (name == "InitializeArray" || name == "CreateSpan") + { + MetadataType owningType = method.OwningType as MetadataType; + if (owningType != null) + { + return owningType.Name == "RuntimeHelpers" && owningType.Namespace == "System.Runtime.CompilerServices"; + } + } + } + + return false; + } + + private bool IsTypeGetTypeFromHandle(MethodDesc method) + { + if (method.IsIntrinsic && method.Name == "GetTypeFromHandle") + { + MetadataType owningType = method.OwningType as MetadataType; + if (owningType != null) + { + return owningType.Name == "Type" && owningType.Namespace == "System"; + } + } + + return false; + } + + private bool IsTypeEquals(MethodDesc method) + { + if (method.IsIntrinsic && method.Name == "op_Equality") + { + MetadataType owningType = method.OwningType as MetadataType; + if (owningType != null) + { + return owningType.Name == "Type" && owningType.Namespace == "System"; + } + } + + return false; + } + + private bool IsActivatorDefaultConstructorOf(MethodDesc method) + { + if (method.IsIntrinsic && method.Name == "DefaultConstructorOf" && method.Instantiation.Length == 1) + { + MetadataType owningType = method.OwningType as MetadataType; + if (owningType != null) + { + return owningType.Name == "Activator" && owningType.Namespace == "System"; + } + } + + return false; + } + + private bool IsActivatorAllocatorOf(MethodDesc method) + { + if (method.IsIntrinsic && method.Name == "AllocatorOf" && method.Instantiation.Length == 1) + { + MetadataType owningType = method.OwningType as MetadataType; + if (owningType != null) + { + return owningType.Name == "Activator" && owningType.Namespace == "System"; + } + } + + return false; + } + + private bool IsEETypePtrOf(MethodDesc method) + { + if (method.IsIntrinsic && (method.Name == "EETypePtrOf" || method.Name == "MethodTableOf") && method.Instantiation.Length == 1) + { + MetadataType owningType = method.OwningType as MetadataType; + if (owningType != null) + { + return (owningType.Name == "EETypePtr" && owningType.Namespace == "System") + || (owningType.Name == "Object" && owningType.Namespace == "System"); + } + } + + return false; + } + + private TypeDesc GetWellKnownType(WellKnownType wellKnownType) + { + return _compilation.TypeSystemContext.GetWellKnownType(wellKnownType); + } + + private void ImportNop() { } + private void ImportBreak() { } + private void ImportLoadVar(int index, bool argument) { } + private void ImportStoreVar(int index, bool argument) { } + private void ImportAddressOfVar(int index, bool argument) { } + private void ImportDup() { } + private void ImportPop() { } + private void ImportCalli(int token) { } + private void ImportLoadNull() { } + private void ImportReturn() { } + private void ImportLoadInt(long value, StackValueKind kind) { } + private void ImportLoadFloat(double value) { } + private void ImportLoadIndirect(int token) { } + private void ImportLoadIndirect(TypeDesc type) { } + private void ImportStoreIndirect(int token) { } + private void ImportStoreIndirect(TypeDesc type) { } + private void ImportShiftOperation(ILOpcode opcode) { } + private void ImportCompareOperation(ILOpcode opcode) { } + private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned) { } + private void ImportUnaryOperation(ILOpcode opCode) { } + private void ImportCpOpj(int token) { } + private void ImportCkFinite() { } + private void ImportLocalAlloc() { } + private void ImportEndFilter() { } + private void ImportCpBlk() { } + private void ImportInitBlk() { } + private void ImportRethrow() { } + private void ImportSizeOf(int token) { } + private void ImportUnalignedPrefix(byte alignment) { } + private void ImportVolatilePrefix() { } + private void ImportTailPrefix() { } + private void ImportNoPrefix(byte mask) { } + private void ImportThrow() { } + private void ImportInitObj(int token) { } + private void ImportLoadLength() { } + private void ImportEndFinally() { } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/PInvokeILProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/PInvokeILProvider.cs new file mode 100644 index 00000000000000..830fb11d1ef21a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/PInvokeILProvider.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; +using Internal.IL.Stubs; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL +{ + /// + /// Wraps the API and configuration for a particular PInvoke IL emitter. Eventually this will + /// allow ILProvider to switch out its PInvoke IL generator with another, such as MCG. + /// + public class PInvokeILProvider : ILProvider + { + private readonly PInvokeILEmitterConfiguration _pInvokeILEmitterConfiguration; + private readonly InteropStateManager _interopStateManager; + + public PInvokeILProvider(PInvokeILEmitterConfiguration pInvokeILEmitterConfiguration, InteropStateManager interopStateManager) + { + _pInvokeILEmitterConfiguration = pInvokeILEmitterConfiguration; + _interopStateManager = interopStateManager; + } + + public override MethodIL GetMethodIL(MethodDesc method) + { + return PInvokeILEmitter.EmitIL(method, _pInvokeILEmitterConfiguration, _interopStateManager); + } + + public MethodDesc GetCalliStub(MethodSignature signature) + { + return _interopStateManager.GetPInvokeCalliStub(signature); + } + + public string GetDirectCallExternName(MethodDesc method) + { + bool directCall = _pInvokeILEmitterConfiguration.GenerateDirectCall(method, out string externName); + Debug.Assert(directCall); + Debug.Assert(externName != null); + return externName; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/AppContextInitializerMethod.Sorting.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/AppContextInitializerMethod.Sorting.cs new file mode 100644 index 00000000000000..8222352a9159c3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/AppContextInitializerMethod.Sorting.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs.StartupCode +{ + partial class AppContextInitializerMethod + { + protected override int ClassCode => 15749517; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + // Should be a singleton + Debug.Assert(this == other); + return 0; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/AppContextInitializerMethod.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/AppContextInitializerMethod.cs new file mode 100644 index 00000000000000..d4b53340b55301 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/AppContextInitializerMethod.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs.StartupCode +{ + public sealed partial class AppContextInitializerMethod : ILStubMethod + { + private TypeDesc _owningType; + private MethodSignature _signature; + private IReadOnlyCollection> _switches; + + public AppContextInitializerMethod(TypeDesc owningType, IEnumerable args) + { + _owningType = owningType; + var switches = new List>(); + + foreach (string s in args) + { + int index = s.IndexOf('='); + if (index <= 0) + throw new ArgumentException($"String '{s}' in unexpected format. Expected 'Key=Value'"); + switches.Add(KeyValuePair.Create(s.Substring(0, index), s.Substring(index + 1))); + } + + _switches = switches; + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + public override string Name + { + get + { + return "SetAppContextSwitches"; + } + } + + public override MethodIL EmitIL() + { + ILEmitter emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + MetadataType appContextType = Context.SystemModule.GetKnownType("System", "AppContext"); + MethodDesc setDataMethod = appContextType.GetKnownMethod("SetData", null); + ILToken setDataToken = emitter.NewToken(setDataMethod); + + foreach (KeyValuePair keyValue in _switches) + { + codeStream.Emit(ILOpcode.ldstr, emitter.NewToken(keyValue.Key)); + codeStream.Emit(ILOpcode.ldstr, emitter.NewToken(keyValue.Value)); + codeStream.Emit(ILOpcode.call, setDataToken); + } + + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + _signature = new MethodSignature(MethodSignatureFlags.Static, 0, + Context.GetWellKnownType(WellKnownType.Void), + TypeDesc.EmptyTypes); + } + + return _signature; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/NativeLibraryStartupMethod.Sorting.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/NativeLibraryStartupMethod.Sorting.cs new file mode 100644 index 00000000000000..be6be0bb39eb7b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/NativeLibraryStartupMethod.Sorting.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs.StartupCode +{ + partial class NativeLibraryStartupMethod + { + protected override int ClassCode => -304225482; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + // Should be a singleton + Debug.Assert(this == other); + return 0; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/NativeLibraryStartupMethod.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/NativeLibraryStartupMethod.cs new file mode 100644 index 00000000000000..7138804bb23138 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/NativeLibraryStartupMethod.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs.StartupCode +{ + /// + /// Startup code that does initialization, Main invocation + /// and shutdown of the runtime. + /// + public sealed partial class NativeLibraryStartupMethod : ILStubMethod + { + private TypeDesc _owningType; + private MethodSignature _signature; + private IReadOnlyCollection _libraryInitializers; + + public NativeLibraryStartupMethod(TypeDesc owningType, IReadOnlyCollection libraryInitializers) + { + _owningType = owningType; + _libraryInitializers = libraryInitializers; + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + public override string Name + { + get + { + return "NativeLibraryStartup"; + } + } + + public override MethodIL EmitIL() + { + ILEmitter emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + // Allow the class library to run explicitly ordered class constructors first thing in start-up. + if (_libraryInitializers != null) + { + foreach (MethodDesc method in _libraryInitializers) + { + codeStream.Emit(ILOpcode.call, emitter.NewToken(method)); + } + } + + MetadataType startup = Context.GetOptionalHelperType("StartupCodeHelpers"); + + // Run module initializers + MethodDesc runModuleInitializers = startup?.GetMethod("RunModuleInitializers", null); + if (runModuleInitializers != null) + { + codeStream.Emit(ILOpcode.call, emitter.NewToken(runModuleInitializers)); + } + + codeStream.Emit(ILOpcode.ret); + return emitter.Link(this); + } + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + _signature = new MethodSignature(MethodSignatureFlags.Static | MethodSignatureFlags.UnmanagedCallingConvention, 0, + Context.GetWellKnownType(WellKnownType.Void), + new TypeDesc[0]); + } + + return _signature; + } + } + + public override bool IsUnmanagedCallersOnly + { + get + { + return true; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.Sorting.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.Sorting.cs new file mode 100644 index 00000000000000..d33a2de6a007f9 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.Sorting.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +using AssemblyName = System.Reflection.AssemblyName; +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs.StartupCode +{ + partial class StartupCodeMainMethod + { + protected override int ClassCode => -304225481; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + // Should be a singleton + Debug.Assert(this == other); + return 0; + } + + partial class MainMethodWrapper + { + protected override int ClassCode => -215672259; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + // Should be a singleton + Debug.Assert(this == other); + return 0; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs new file mode 100644 index 00000000000000..b18b9435491343 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs @@ -0,0 +1,262 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +using AssemblyName = System.Reflection.AssemblyName; +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs.StartupCode +{ + /// + /// Startup code that does initialization, Main invocation + /// and shutdown of the runtime. + /// + public sealed partial class StartupCodeMainMethod : ILStubMethod + { + private TypeDesc _owningType; + private MainMethodWrapper _mainMethod; + private MethodSignature _signature; + private IReadOnlyCollection _libraryInitializers; + + public StartupCodeMainMethod(TypeDesc owningType, MethodDesc mainMethod, IReadOnlyCollection libraryInitializers) + { + _owningType = owningType; + _mainMethod = new MainMethodWrapper(owningType, mainMethod); + _libraryInitializers = libraryInitializers; + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + public override string Name + { + get + { + return "StartupCodeMain"; + } + } + + public override MethodIL EmitIL() + { + ILEmitter emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + if (Context.Target.IsWindows) + codeStream.MarkDebuggerStepThroughPoint(); + + // Allow the class library to run explicitly ordered class constructors first thing in start-up. + if (_libraryInitializers != null) + { + foreach (MethodDesc method in _libraryInitializers) + { + codeStream.Emit(ILOpcode.call, emitter.NewToken(method)); + } + } + + MetadataType startup = Context.GetOptionalHelperType("StartupCodeHelpers"); + + // Initialize command line args if the class library supports this + string initArgsName = (Context.Target.OperatingSystem == TargetOS.Windows) + ? "InitializeCommandLineArgsW" + : "InitializeCommandLineArgs"; + MethodDesc initArgs = startup?.GetMethod(initArgsName, null); + if (initArgs != null) + { + codeStream.Emit(ILOpcode.ldarg_0); // argc + codeStream.Emit(ILOpcode.ldarg_1); // argv + codeStream.Emit(ILOpcode.call, emitter.NewToken(initArgs)); + } + + // Initialize the entrypoint assembly if the class library supports this + MethodDesc initEntryAssembly = startup?.GetMethod("InitializeEntryAssembly", null); + if (initEntryAssembly != null) + { + ModuleDesc entrypointModule = ((MetadataType)_mainMethod.WrappedMethod.OwningType).Module; + codeStream.Emit(ILOpcode.ldtoken, emitter.NewToken(entrypointModule.GetGlobalModuleType())); + codeStream.Emit(ILOpcode.call, emitter.NewToken(initEntryAssembly)); + } + + // Initialize COM apartment + MethodDesc initApartmentState = startup?.GetMethod("InitializeApartmentState", null); + if (initApartmentState != null) + { + if (_mainMethod.HasCustomAttribute("System", "STAThreadAttribute")) + { + codeStream.EmitLdc((int)System.Threading.ApartmentState.STA); + codeStream.Emit(ILOpcode.call, emitter.NewToken(initApartmentState)); + } + if (_mainMethod.HasCustomAttribute("System", "MTAThreadAttribute")) + { + codeStream.EmitLdc((int)System.Threading.ApartmentState.MTA); + codeStream.Emit(ILOpcode.call, emitter.NewToken(initApartmentState)); + } + } + + // Run module initializers + MethodDesc runModuleInitializers = startup?.GetMethod("RunModuleInitializers", null); + if (runModuleInitializers != null) + { + codeStream.Emit(ILOpcode.call, emitter.NewToken(runModuleInitializers)); + } + + // Call program Main + if (_mainMethod.Signature.Length > 0) + { + // TODO: better exception + if (initArgs == null) + throw new Exception("Main() has parameters, but the class library doesn't support them"); + + codeStream.Emit(ILOpcode.call, emitter.NewToken(startup.GetKnownMethod("GetMainMethodArguments", null))); + } + + if (Context.Target.IsWindows) + codeStream.MarkDebuggerStepInPoint(); + codeStream.Emit(ILOpcode.call, emitter.NewToken(_mainMethod)); + + MethodDesc setLatchedExitCode = startup?.GetMethod("SetLatchedExitCode", null); + MethodDesc shutdown = startup?.GetMethod("Shutdown", null); + + // The class library either supports "advanced shutdown", or doesn't. No half-implementations allowed. + Debug.Assert((setLatchedExitCode != null) == (shutdown != null)); + + if (setLatchedExitCode != null) + { + // If the main method has a return value, save it + if (!_mainMethod.Signature.ReturnType.IsVoid) + { + codeStream.Emit(ILOpcode.call, emitter.NewToken(setLatchedExitCode)); + } + + // Ask the class library to shut down and return exit code. + codeStream.Emit(ILOpcode.call, emitter.NewToken(shutdown)); + } + else + { + // This is a class library that doesn't have SetLatchedExitCode/Shutdown. + // If the main method returns void, we simply use 0 exit code. + if (_mainMethod.Signature.ReturnType.IsVoid) + { + codeStream.EmitLdc(0); + } + } + + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(this); + } + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + _signature = new MethodSignature(MethodSignatureFlags.Static | MethodSignatureFlags.UnmanagedCallingConvention, 0, + Context.GetWellKnownType(WellKnownType.Int32), + new TypeDesc[2] { + Context.GetWellKnownType(WellKnownType.Int32), + Context.GetWellKnownType(WellKnownType.IntPtr) }); + } + + return _signature; + } + } + + public override bool IsUnmanagedCallersOnly + { + get + { + return true; + } + } + + /// + /// Wraps the main method in a layer of indirection. This is necessary to protect the startup code + /// infrastructure from situations when the owning type of the main method cannot be loaded, and codegen + /// is instructed to generate a throwing body. Without wrapping, this behavior would result in + /// replacing the entire startup code sequence with a throwing body, causing us to enter the "rich" managed + /// environment without it being fully initialized. (In particular, the unhandled exception experience + /// won't be initialized, making this difficult to diagnose.) + /// + private partial class MainMethodWrapper : ILStubMethod + { + public MainMethodWrapper(TypeDesc owningType, MethodDesc mainMethod) + { + WrappedMethod = mainMethod; + OwningType = owningType; + } + + public MethodDesc WrappedMethod + { + get; + } + + public override TypeSystemContext Context + { + get + { + return OwningType.Context; + } + } + + public override TypeDesc OwningType + { + get; + } + + public override string Name + { + get + { + return "MainMethodWrapper"; + } + } + + public override MethodSignature Signature + { + get + { + return WrappedMethod.Signature; + } + } + + public override MethodIL EmitIL() + { + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + + if (Context.Target.IsWindows) + codeStream.MarkDebuggerStepThroughPoint(); + + for (int i = 0; i < Signature.Length; i++) + codeStream.EmitLdArg(i); + + if (Context.Target.IsWindows) + codeStream.MarkDebuggerStepInPoint(); + + codeStream.Emit(ILOpcode.call, emit.NewToken(WrappedMethod)); + + codeStream.Emit(ILOpcode.ret); + + return emit.Link(this); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj new file mode 100644 index 00000000000000..af1e7859356933 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -0,0 +1,480 @@ + + + Library + ILCompiler.Compiler + net5.0 + true + false + x64;x86 + AnyCPU + false + + + false + Debug;Release;Checked + + + + + + + + + + + Compiler\Logging\CompilerGeneratedState.cs + + + Compiler\Logging\MessageContainer.cs + + + Compiler\Logging\MessageOrigin.cs + + + Compiler\Logging\DocumentationSignatureParser.cs + + + Compiler\Logging\DocumentationSignatureGenerator.PartVisitor.cs + + + Common\GCDescEncoder.cs + + + Common\ReadyToRunConstants.cs + + + Common\InteropConstants.cs + + + Common\ITargetBinaryWriter.cs + + + Common\MappingTableFlags.cs + + + Common\MetadataBlob.cs + + + Common\RuntimeConstants.cs + + + Common\UniversalGenericParameterLayout.cs + + + Common\Utf8String.cs + + + Common\Utf8StringBuilder.cs + + + Ecma\EcmaSignatureEncoder.cs + + + IL\CoreRTILProvider.cs + + + IL\ILImporter.cs + + + IL\ILReader.cs + + + IL\StackValueKind.cs + + + IL\Stubs\AssemblyGetExecutingAssemblyMethodThunk.cs + + + IL\Stubs\AssemblyGetExecutingAssemblyMethodThunk.Sorting.cs + + + IL\Stubs\ComparerIntrinsics.cs + + + IL\Stubs\GetCanonTypeIntrinsic.cs + + + + IL\Stubs\MethodBaseGetCurrentMethodThunk.cs + + + IL\Stubs\MethodBaseGetCurrentMethodThunk.Mangling.cs + + + IL\Stubs\DelegateMethodILEmitter.cs + + + IL\Stubs\MethodBaseGetCurrentMethodThunk.Sorting.cs + + + + IL\Stubs\TypeGetTypeMethodThunk.cs + + + IL\Stubs\TypeGetTypeMethodThunk.Sorting.cs + + + IL\Stubs\TypeSystemThrowingILEmitter.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Common\FormattingHelpers.cs + + + Common\ArrayBuilder.cs + + + Common\CanonTypeKind.cs + + + Common\MethodTable.Constants.cs + + + Common\EETypeOptionalFieldsBuilder.cs + + + Common\EETypeBuilderHelpers.cs + + + Common\InterfaceDispatchCellCachePointerFlags.cs + + + Common\ModuleHeaders.cs + + + Common\NativeFormat\NativeFormatWriter.Primitives.cs + + + + + IL\HelperExtensions.cs + + + IL\ILProvider.cs + + + IL\Stubs\ArrayMethodILEmitter.cs + + + IL\Stubs\UnsafeIntrinsics.cs + + + IL\Stubs\MemoryMarshalIntrinsics.cs + + + IL\Stubs\VolatileIntrinsics.cs + + + JitInterface\CorInfoInstructionSet.cs + + + JitInterface\CorInfoHelpFunc.cs + + + JitInterface\CorInfoTypes.cs + + + JitInterface\CorInfoTypes.VarInfo.cs + + + JitInterface\MemoryHelper.cs + + + TypeSystem\TypesDebugInfoWriter\TypesDebugInfoWriter.cs + + + Pgo\PgoFormat.cs + + + diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler.MetadataTransform.csproj b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler.MetadataTransform.csproj new file mode 100644 index 00000000000000..ca6174b66dab13 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler.MetadataTransform.csproj @@ -0,0 +1,79 @@ + + + Library + ILCompiler.MetadataTransform + netstandard2.0 + true + $(DefineConstants);NATIVEFORMAT_PUBLICWRITER;NETFX_45 + false + x64;x86 + AnyCPU + false + + + false + Debug;Release;Checked + + $(MSBuildThisFileDirectory)..\..\Common\ + $(CommonSourcePath)Internal\NativeFormat + $(CommonSourcePath)\Internal\Metadata\NativeFormat + $(MSBuildThisFileDirectory)Internal\Metadata\NativeFormat\Writer + + + + + + + + $(SystemReflectionMetadataVersion) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/EntityMap.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/EntityMap.cs new file mode 100644 index 00000000000000..cd07acd36dc32f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/EntityMap.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +namespace ILCompiler.Metadata +{ + internal struct EntityMap + { + private Dictionary _map; + + public IReadOnlyCollection Records + { + get { return _map.Values; } + } + + public EntityMap(IEqualityComparer comparer) + { + _map = new Dictionary(comparer); + } + + public TRecord GetOrCreate(TConcreteEntity entity, Action initializer) + where TConcreteEntity : TEntity + where TConcreteRecord : TRecord, new() + { + TRecord record; + if (!_map.TryGetValue(entity, out record)) + { + // We are externalizing the allocation instead of having a 'creator' delegate + // because initializer might end up recursing into GetOrCreate for the same entity. + // EntityMap needs to be ready to return a pointer to the currently initialized record. + + // The transform doesn't care that the record is not fully initialized yet + // since we're not reading it at this stage. + + // Example: + // + // class FooAttribute : Attribute + // { + // [FooAttribute] + // public FooAttribute() + // { + // } + // } + // + // In here, while we're emitting the record for FooAttribute..ctor, we need + // a pointer to the record for FooAttribute..ctor because that's what the + // constructor of the custom attribute applied to the constructor. + + TConcreteRecord concreteRecord = new TConcreteRecord(); + _map.Add(entity, concreteRecord); + + initializer(entity, concreteRecord); + + return concreteRecord; + } + + return record; + } + + public bool TryGet(TEntity entity, out TRecord record) + { + return _map.TryGetValue(entity, out record); + } + + public TConcreteRecord Create(TConcreteEntity entity, Action initializer) + where TConcreteEntity : TEntity + where TConcreteRecord : TRecord, new() + { + TConcreteRecord concreteRecord = new TConcreteRecord(); + + // Important: add to the map before calling the initializer. + // For reasoning see GetOrCreate. + _map.Add(entity, concreteRecord); + + initializer(entity, concreteRecord); + + return concreteRecord; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/IMetadataPolicy.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/IMetadataPolicy.cs new file mode 100644 index 00000000000000..3aa61454e57f9b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/IMetadataPolicy.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; +using Ecma = System.Reflection.Metadata; + +namespace ILCompiler.Metadata +{ + /// + /// Controls metadata generation policy. Decides what types and members will get metadata. + /// + /// + /// Thread safety: the implementers are required to be thread safe. + /// + public interface IMetadataPolicy + { + /// + /// Returns true if the type should generate metadata. If false, + /// the type will generate a if required within the object graph. + /// + /// Uninstantiated type definition to check. + bool GeneratesMetadata(Cts.MetadataType typeDef); + + /// + /// Returns true if the method should generate metadata. If false, + /// the method should generate a when needed. + /// + /// Uninstantiated method definition to check. + bool GeneratesMetadata(Cts.MethodDesc methodDef); + + /// + /// Returns true if the field should generate metadata. If false, + /// the field should generate a when needed. + /// + /// Uninstantiated field definition to check. + bool GeneratesMetadata(Cts.FieldDesc fieldDef); + + /// + /// Returns true if the custom attribute should generate metadata. + /// If false, the custom attribute is not generated. + /// + bool GeneratesMetadata(Cts.Ecma.EcmaModule module, Ecma.CustomAttributeHandle customAttribute); + + /// + /// Returns true if an exported type entry should generate metadata. + /// + bool GeneratesMetadata(Cts.Ecma.EcmaModule module, Ecma.ExportedTypeHandle exportedType); + + /// + /// Returns true if a type should be blocked from generating any metadata. + /// Blocked interfaces are skipped from interface lists, and custom attributes referring to + /// blocked types are dropped from metadata. + /// + bool IsBlocked(Cts.MetadataType typeDef); + + bool IsBlocked(Cts.MethodDesc methodDef); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/MetadataTransform.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/MetadataTransform.cs new file mode 100644 index 00000000000000..4d020cf0dd9a2d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/MetadataTransform.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; + +namespace ILCompiler.Metadata +{ + /// + /// Transforms type system object model into metadata writer object model that is suitable + /// for binary serialization into a metadata blob using the class. + /// + public abstract class MetadataTransform + { + /// + /// Transforms the specified modules using the specified metadata generation + /// policy. Policy is required to be a struct for performance reasons. + /// + /// + /// The list of is required to be transitively complete with respect to + /// : whenever there's a reference from the object graph to a type or member defined in a + /// module that was not included in the enumeration, the + /// + /// and are required to return false. + /// + public static MetadataTransformResult Run(TPolicy policy, IEnumerable modules) + where TPolicy : struct, IMetadataPolicy + { + // TODO: Make this multithreaded. The high level plan is: + // - make EntityMap thread safe + // - change the way TypeDefs are hooked up into namespaces and scopes + // - queue up calls to the various Initialize* methods on a threadpool + + var transform = new Transform(policy); + + foreach (var module in modules) + { + foreach (var type in module.GetAllTypes()) + { + if (policy.GeneratesMetadata(type) && !policy.IsBlocked(type)) + { + transform.HandleType(type); + } + } + } + + return new MetadataTransformResult(transform); + } + + /// + /// Retrieves an existing , , + /// or record representing specified type in the metadata writer object + /// model, or creates a new one. + /// + public abstract MetadataRecord HandleType(Cts.TypeDesc type); + + /// + /// Retrieves an existing , , or + /// record representing specified method in the metadata writer object model, or creates a new one. + /// + public abstract MetadataRecord HandleQualifiedMethod(Cts.MethodDesc method); + + /// + /// Retrieves an existing or a record + /// representing specified field in the metadata writer object model, or creates a new one. + /// + public abstract MetadataRecord HandleQualifiedField(Cts.FieldDesc field); + + /// + /// Retrieves an existing record representing the specified signature + /// in the metadata writer object model, or creates a new one. + /// + public abstract MethodSignature HandleMethodSignature(Cts.MethodSignature signature); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/MetadataTransformResult.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/MetadataTransformResult.cs new file mode 100644 index 00000000000000..d64c6fd67dadb3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/MetadataTransformResult.cs @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.Metadata +{ + public struct MetadataTransformResult + where T : struct, IMetadataPolicy + { + private Transform _transform; + + /// + /// Gets a collection of records representing the top level transformed metadata scopes. + /// + public IReadOnlyCollection Scopes + { + get + { + return _transform._scopeDefs.Records; + } + } + + /// + /// Gets an instance of that allows generation of additional + /// (non-definition) metadata records. This can be used to retrieve or create metadata records + /// to be referenced from e.g. code. + /// Note that the records may not be reachable from any of the top level . + /// To make sure the records get emitted into the metadata blob by the , + /// add them to . + /// + public MetadataTransform Transform + { + get + { + return _transform; + } + } + + internal MetadataTransformResult(Transform transform) + { + _transform = transform; + } + + /// + /// Attempts to retrieve a record corresponding to the specified + /// . Returns null if not found. + /// + public TypeDefinition GetTransformedTypeDefinition(Cts.MetadataType type) + { + Debug.Assert(type.IsTypeDefinition); + + MetadataRecord rec; + if (!_transform._types.TryGet(type, out rec)) + { + return null; + } + + return rec as TypeDefinition; + } + + /// + /// Attempts to retrieve a record corresponding to the specified + /// . Returns null if not found. + /// + public TypeReference GetTransformedTypeReference(Cts.MetadataType type) + { + Debug.Assert(type.IsTypeDefinition); + + MetadataRecord rec; + if (!_transform._types.TryGet(type, out rec)) + { + return null; + } + + return rec as TypeReference; + } + + /// + /// Attempts to retrieve a record corresponding to the specified + /// . Returns null if not found. + /// + public Method GetTransformedMethodDefinition(Cts.MethodDesc method) + { + Debug.Assert(method.IsTypicalMethodDefinition); + + MetadataRecord rec; + if (!_transform._methods.TryGet(method, out rec)) + { + return null; + } + + return rec as Method; + } + + /// + /// Attempts to retrieve a record corresponding to the specified + /// . Returns null if not found. + /// + public Field GetTransformedFieldDefinition(Cts.FieldDesc field) + { + Debug.Assert(field.OwningType.IsTypeDefinition); + + MetadataRecord rec; + if (!_transform._fields.TryGet(field, out rec)) + { + return null; + } + + return rec as Field; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Constant.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Constant.cs new file mode 100644 index 00000000000000..d343a4ae42c5ab --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Constant.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; +using Ecma = System.Reflection.Metadata; +using ConstantTypeCode = System.Reflection.Metadata.ConstantTypeCode; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + private MetadataRecord HandleConstant(Cts.Ecma.EcmaModule module, Ecma.ConstantHandle constantHandle) + { + Ecma.MetadataReader reader = module.MetadataReader; + Ecma.Constant constant = reader.GetConstant(constantHandle); + + Ecma.BlobReader blob = reader.GetBlobReader(constant.Value); + + switch (constant.TypeCode) + { + case ConstantTypeCode.Boolean: + return new ConstantBooleanValue { Value = blob.ReadBoolean() }; + case ConstantTypeCode.Byte: + return new ConstantByteValue { Value = blob.ReadByte() }; + case ConstantTypeCode.Char: + return new ConstantCharValue { Value = blob.ReadChar() }; + case ConstantTypeCode.Double: + return new ConstantDoubleValue { Value = blob.ReadDouble() }; + case ConstantTypeCode.Int16: + return new ConstantInt16Value { Value = blob.ReadInt16() }; + case ConstantTypeCode.Int32: + return new ConstantInt32Value { Value = blob.ReadInt32() }; + case ConstantTypeCode.Int64: + return new ConstantInt64Value { Value = blob.ReadInt64() }; + case ConstantTypeCode.SByte: + return new ConstantSByteValue { Value = blob.ReadSByte() }; + case ConstantTypeCode.Single: + return new ConstantSingleValue { Value = blob.ReadSingle() }; + case ConstantTypeCode.String: + return HandleString(blob.ReadUTF16(blob.Length)); + case ConstantTypeCode.UInt16: + return new ConstantUInt16Value { Value = blob.ReadUInt16() }; + case ConstantTypeCode.UInt32: + return new ConstantUInt32Value { Value = blob.ReadUInt32() }; + case ConstantTypeCode.UInt64: + return new ConstantUInt64Value { Value = blob.ReadUInt64() }; + case ConstantTypeCode.NullReference: + return new ConstantReferenceValue(); + default: + throw new BadImageFormatException(); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.CustomAttribute.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.CustomAttribute.cs new file mode 100644 index 00000000000000..27b1044313d605 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.CustomAttribute.cs @@ -0,0 +1,217 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Collections.Immutable; + +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; +using Ecma = System.Reflection.Metadata; + +using Debug = System.Diagnostics.Debug; +using NamedArgumentMemberKind = Internal.Metadata.NativeFormat.NamedArgumentMemberKind; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + private List HandleCustomAttributes(Cts.Ecma.EcmaModule module, Ecma.CustomAttributeHandleCollection attributes) + { + List customAttributes = new List(attributes.Count); + + var attributeTypeProvider = new Cts.Ecma.CustomAttributeTypeProvider(module); + + Ecma.MetadataReader reader = module.MetadataReader; + + foreach (var attributeHandle in attributes) + { + if (!_policy.GeneratesMetadata(module, attributeHandle)) + continue; + + Ecma.CustomAttribute attribute = reader.GetCustomAttribute(attributeHandle); + + // TODO-NICE: We can intern the attributes based on the CA constructor and blob bytes + + Cts.MethodDesc constructor = module.GetMethod(attribute.Constructor); + var decodedValue = attribute.DecodeValue(attributeTypeProvider); + + customAttributes.Add(HandleCustomAttribute(constructor, decodedValue)); + } + + return customAttributes; + } + + private CustomAttribute HandleCustomAttribute(Cts.MethodDesc constructor, Ecma.CustomAttributeValue decodedValue) + { + CustomAttribute result = new CustomAttribute + { + Constructor = HandleQualifiedMethod(constructor), + }; + + result.FixedArguments.Capacity = decodedValue.FixedArguments.Length; + foreach (var decodedArgument in decodedValue.FixedArguments) + { + var fixedArgument = HandleCustomAttributeConstantValue(decodedArgument.Type, decodedArgument.Value); + result.FixedArguments.Add(fixedArgument); + } + + result.NamedArguments.Capacity = decodedValue.NamedArguments.Length; + foreach (var decodedArgument in decodedValue.NamedArguments) + { + var namedArgument = new NamedArgument + { + Flags = decodedArgument.Kind == Ecma.CustomAttributeNamedArgumentKind.Field ? + NamedArgumentMemberKind.Field : NamedArgumentMemberKind.Property, + Name = HandleString(decodedArgument.Name), + Type = HandleType(decodedArgument.Type), + Value = HandleCustomAttributeConstantValue(decodedArgument.Type, decodedArgument.Value) + }; + result.NamedArguments.Add(namedArgument); + } + + return result; + } + + private MetadataRecord HandleCustomAttributeConstantValue(Cts.TypeDesc type, object value) + { + switch (type.UnderlyingType.Category) + { + case Cts.TypeFlags.Boolean: + return new ConstantBooleanValue { Value = (bool)value }; + case Cts.TypeFlags.Byte: + return new ConstantByteValue { Value = (byte)value }; + case Cts.TypeFlags.Char: + return new ConstantCharValue { Value = (char)value }; + case Cts.TypeFlags.Double: + return new ConstantDoubleValue { Value = (double)value }; + case Cts.TypeFlags.Int16: + return new ConstantInt16Value { Value = (short)value }; + case Cts.TypeFlags.Int32: + return new ConstantInt32Value { Value = (int)value }; + case Cts.TypeFlags.Int64: + return new ConstantInt64Value { Value = (long)value }; + case Cts.TypeFlags.SByte: + return new ConstantSByteValue { Value = (sbyte)value }; + case Cts.TypeFlags.Single: + return new ConstantSingleValue { Value = (float)value }; + case Cts.TypeFlags.UInt16: + return new ConstantUInt16Value { Value = (ushort)value }; + case Cts.TypeFlags.UInt32: + return new ConstantUInt32Value { Value = (uint)value }; + case Cts.TypeFlags.UInt64: + return new ConstantUInt64Value { Value = (ulong)value }; + } + + if (value == null) + { + return new ConstantReferenceValue(); + } + + if (type.IsString) + { + return HandleString((string)value); + } + + if (type.IsSzArray) + { + return HandleCustomAttributeConstantArray( + (Cts.ArrayType)type, + (ImmutableArray>)value); + } + + Debug.Assert(value is Cts.TypeDesc); + Debug.Assert(type is Cts.MetadataType + && ((Cts.MetadataType)type).Name == "Type" + && ((Cts.MetadataType)type).Namespace == "System"); + + return HandleType((Cts.TypeDesc)value); + } + + private MetadataRecord HandleCustomAttributeConstantArray( + Cts.ArrayType type, ImmutableArray> value) + { + Cts.TypeDesc elementType = type.ElementType; + + if (elementType.IsEnum) + { + Cts.TypeSystemContext context = type.Context; + return new ConstantEnumArray + { + ElementType = HandleType(elementType), + Value = HandleCustomAttributeConstantArray(context.GetArrayType(elementType.UnderlyingType), value), + }; + } + + switch (elementType.Category) + { + case Cts.TypeFlags.Boolean: + return new ConstantBooleanArray { Value = GetCustomAttributeConstantArrayElements(value) }; + case Cts.TypeFlags.Byte: + return new ConstantByteArray { Value = GetCustomAttributeConstantArrayElements(value) }; + case Cts.TypeFlags.Char: + return new ConstantCharArray { Value = GetCustomAttributeConstantArrayElements(value) }; + case Cts.TypeFlags.Double: + return new ConstantDoubleArray { Value = GetCustomAttributeConstantArrayElements(value) }; + case Cts.TypeFlags.Int16: + return new ConstantInt16Array { Value = GetCustomAttributeConstantArrayElements(value) }; + case Cts.TypeFlags.Int32: + return new ConstantInt32Array { Value = GetCustomAttributeConstantArrayElements(value) }; + case Cts.TypeFlags.Int64: + return new ConstantInt64Array { Value = GetCustomAttributeConstantArrayElements(value) }; + case Cts.TypeFlags.SByte: + return new ConstantSByteArray { Value = GetCustomAttributeConstantArrayElements(value) }; + case Cts.TypeFlags.Single: + return new ConstantSingleArray { Value = GetCustomAttributeConstantArrayElements(value) }; + case Cts.TypeFlags.UInt16: + return new ConstantUInt16Array { Value = GetCustomAttributeConstantArrayElements(value) }; + case Cts.TypeFlags.UInt32: + return new ConstantUInt32Array { Value = GetCustomAttributeConstantArrayElements(value) }; + case Cts.TypeFlags.UInt64: + return new ConstantUInt64Array { Value = GetCustomAttributeConstantArrayElements(value) }; + } + + if (elementType.IsString) + { + var record = new ConstantStringArray(); + record.Value.Capacity = value.Length; + foreach (var element in value) + { + MetadataRecord elementRecord = element.Value == null ? + (MetadataRecord)new ConstantReferenceValue() : HandleString((string)element.Value); + record.Value.Add(elementRecord); + } + return record; + } + + var result = new ConstantHandleArray(); + result.Value.Capacity = value.Length; + for (int i = 0; i < value.Length; i++) + { + MetadataRecord elementRecord = HandleCustomAttributeConstantValue(value[i].Type, value[i].Value); + if (value[i].Type.IsEnum) + { + elementRecord = new ConstantBoxedEnumValue + { + Value = elementRecord, + Type = HandleType(value[i].Type) + }; + } + result.Value.Add(elementRecord); + } + + return result; + } + + private static TValue[] GetCustomAttributeConstantArrayElements(ImmutableArray> value) + { + TValue[] result = new TValue[value.Length]; + for (int i = 0; i < value.Length; i++) + { + result[i] = (TValue)value[i].Value; + } + return result; + } + } + +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Event.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Event.cs new file mode 100644 index 00000000000000..422f70e99927a0 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Event.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; +using Ecma = System.Reflection.Metadata; + +using MethodSemanticsAttributes = Internal.Metadata.NativeFormat.MethodSemanticsAttributes; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + private Event HandleEvent(Cts.Ecma.EcmaModule module, Ecma.EventDefinitionHandle eventHandle) + { + Ecma.MetadataReader reader = module.MetadataReader; + + Ecma.EventDefinition eventDef = reader.GetEventDefinition(eventHandle); + + Ecma.EventAccessors acc = eventDef.GetAccessors(); + Cts.MethodDesc adderMethod = acc.Adder.IsNil ? null : module.GetMethod(acc.Adder); + Cts.MethodDesc raiserMethod = acc.Raiser.IsNil ? null : module.GetMethod(acc.Raiser); + Cts.MethodDesc removerMethod = acc.Remover.IsNil ? null : module.GetMethod(acc.Remover); + + bool adderHasMetadata = adderMethod != null && _policy.GeneratesMetadata(adderMethod); + bool raiserHasMetadata = raiserMethod != null && _policy.GeneratesMetadata(raiserMethod); + bool removerHasMetadata = removerMethod != null && _policy.GeneratesMetadata(removerMethod); + + // Policy: If none of the accessors has metadata, event doesn't have metadata + if (!adderHasMetadata && !raiserHasMetadata && !removerHasMetadata) + return null; + + Event result = new Event + { + Name = HandleString(reader.GetString(eventDef.Name)), + Flags = eventDef.Attributes, + Type = HandleType(module.GetType(eventDef.Type)), + + }; + + if (adderHasMetadata) + { + result.MethodSemantics.Add(new MethodSemantics + { + Attributes = MethodSemanticsAttributes.AddOn, + Method = HandleMethodDefinition(adderMethod), + }); + } + + if (raiserHasMetadata) + { + result.MethodSemantics.Add(new MethodSemantics + { + Attributes = MethodSemanticsAttributes.Fire, + Method = HandleMethodDefinition(raiserMethod), + }); + } + + if (removerHasMetadata) + { + result.MethodSemantics.Add(new MethodSemantics + { + Attributes = MethodSemanticsAttributes.RemoveOn, + Method = HandleMethodDefinition(removerMethod), + }); + } + + Ecma.CustomAttributeHandleCollection customAttributes = eventDef.GetCustomAttributes(); + if (customAttributes.Count > 0) + { + result.CustomAttributes = HandleCustomAttributes(module, customAttributes); + } + + return result; + } + + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Field.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Field.cs new file mode 100644 index 00000000000000..adc5e48cf39632 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Field.cs @@ -0,0 +1,125 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; +using Ecma = System.Reflection.Metadata; + +using Debug = System.Diagnostics.Debug; +using FieldAttributes = System.Reflection.FieldAttributes; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + internal EntityMap _fields = + new EntityMap(EqualityComparer.Default); + + private Action _initFieldDef; + private Action _initFieldRef; + + public override MetadataRecord HandleQualifiedField(Cts.FieldDesc field) + { + if (_policy.GeneratesMetadata(field) && field.GetTypicalFieldDefinition() == field) + { + QualifiedField record = new QualifiedField(); + record.Field = HandleFieldDefinition(field); + record.EnclosingType = (TypeDefinition)HandleType(field.OwningType); + return record; + } + else + { + return HandleFieldReference(field); + } + } + + private Field HandleFieldDefinition(Cts.FieldDesc field) + { + Debug.Assert(field.GetTypicalFieldDefinition() == field); + Debug.Assert(_policy.GeneratesMetadata(field)); + return (Field)_fields.GetOrCreate(field, _initFieldDef ?? (_initFieldDef = InitializeFieldDefinition)); + } + + private void InitializeFieldDefinition(Cts.FieldDesc entity, Field record) + { + record.Name = HandleString(entity.Name); + record.Signature = new FieldSignature + { + Type = HandleType(entity.FieldType), + // TODO: CustomModifiers + }; + record.Flags = GetFieldAttributes(entity); + + var ecmaField = entity as Cts.Ecma.EcmaField; + if (ecmaField != null) + { + Ecma.MetadataReader reader = ecmaField.MetadataReader; + Ecma.FieldDefinition fieldDef = reader.GetFieldDefinition(ecmaField.Handle); + Ecma.ConstantHandle defaultValueHandle = fieldDef.GetDefaultValue(); + if (!defaultValueHandle.IsNil) + { + record.DefaultValue = HandleConstant(ecmaField.Module, defaultValueHandle); + } + + Ecma.CustomAttributeHandleCollection customAttributes = fieldDef.GetCustomAttributes(); + if (customAttributes.Count > 0) + { + record.CustomAttributes = HandleCustomAttributes(ecmaField.Module, customAttributes); + } + + int offset = fieldDef.GetOffset(); + if (offset >= 0) + record.Offset = (uint)offset; + } + } + + private MemberReference HandleFieldReference(Cts.FieldDesc field) + { + return (MemberReference)_fields.GetOrCreate(field, _initFieldRef ?? (_initFieldRef = InitializeFieldReference)); + } + + private void InitializeFieldReference(Cts.FieldDesc entity, MemberReference record) + { + record.Name = HandleString(entity.Name); + record.Parent = HandleType(entity.OwningType); + record.Signature = new FieldSignature + { + Type = HandleType(entity.GetTypicalFieldDefinition().FieldType), + // TODO: CustomModifiers + }; + } + + private FieldAttributes GetFieldAttributes(Cts.FieldDesc field) + { + FieldAttributes result; + + var ecmaField = field as Cts.Ecma.EcmaField; + if (ecmaField != null) + { + var fieldDefinition = ecmaField.MetadataReader.GetFieldDefinition(ecmaField.Handle); + result = fieldDefinition.Attributes; + } + else + { + result = 0; + + if (field.IsStatic) + result |= FieldAttributes.Static; + if (field.IsInitOnly) + result |= FieldAttributes.InitOnly; + if (field.IsLiteral) + result |= FieldAttributes.Literal; + if (field.HasRva) + result |= FieldAttributes.HasFieldRVA; + + // Not set: Visibility, NotSerialized, SpecialName, RTSpecialName, HasFieldMarshal, HasDefault + } + + return result; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Method.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Method.cs new file mode 100644 index 00000000000000..ae6946d0b5af8d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Method.cs @@ -0,0 +1,210 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; +using Ecma = System.Reflection.Metadata; + +using CallingConventions = System.Reflection.CallingConventions; +using Debug = System.Diagnostics.Debug; +using MethodAttributes = System.Reflection.MethodAttributes; +using MethodImplAttributes = System.Reflection.MethodImplAttributes; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + internal EntityMap _methods + = new EntityMap(EqualityComparer.Default); + + private Action _initMethodDef; + private Action _initMethodRef; + private Action _initMethodInst; + + public override MetadataRecord HandleQualifiedMethod(Cts.MethodDesc method) + { + MetadataRecord rec; + + if (method is Cts.InstantiatedMethod) + { + rec = HandleMethodInstantiation(method); + } + else if (method.IsTypicalMethodDefinition && _policy.GeneratesMetadata(method)) + { + rec = new QualifiedMethod + { + EnclosingType = (TypeDefinition)HandleType(method.OwningType), + Method = HandleMethodDefinition(method), + }; + } + else + { + rec = HandleMethodReference(method); + } + + Debug.Assert(rec is QualifiedMethod || rec is MemberReference || rec is MethodInstantiation); + + return rec; + } + + private Method HandleMethodDefinition(Cts.MethodDesc method) + { + Debug.Assert(method.IsTypicalMethodDefinition); + Debug.Assert(_policy.GeneratesMetadata(method)); + return (Method)_methods.GetOrCreate(method, _initMethodDef ?? (_initMethodDef = InitializeMethodDefinition)); + } + + private void InitializeMethodDefinition(Cts.MethodDesc entity, Method record) + { + record.Name = HandleString(entity.Name); + record.Signature = HandleMethodSignature(entity.Signature); + + if (entity.HasInstantiation) + { + record.GenericParameters.Capacity = entity.Instantiation.Length; + foreach (var p in entity.Instantiation) + record.GenericParameters.Add(HandleGenericParameter((Cts.GenericParameterDesc)p)); + } + + var ecmaEntity = entity as Cts.Ecma.EcmaMethod; + if (ecmaEntity != null) + { + Ecma.MetadataReader reader = ecmaEntity.MetadataReader; + Ecma.MethodDefinition methodDef = reader.GetMethodDefinition(ecmaEntity.Handle); + Ecma.ParameterHandleCollection paramHandles = methodDef.GetParameters(); + + record.Parameters.Capacity = paramHandles.Count; + foreach (var paramHandle in paramHandles) + { + Ecma.Parameter param = reader.GetParameter(paramHandle); + Parameter paramRecord = new Parameter + { + Flags = param.Attributes, + Name = HandleString(reader.GetString(param.Name)), + Sequence = checked((ushort)param.SequenceNumber) + }; + + Ecma.ConstantHandle defaultValue = param.GetDefaultValue(); + if (!defaultValue.IsNil) + { + paramRecord.DefaultValue = HandleConstant(ecmaEntity.Module, defaultValue); + } + + Ecma.CustomAttributeHandleCollection paramAttributes = param.GetCustomAttributes(); + if (paramAttributes.Count > 0) + { + paramRecord.CustomAttributes = HandleCustomAttributes(ecmaEntity.Module, paramAttributes); + } + + record.Parameters.Add(paramRecord); + } + + Ecma.CustomAttributeHandleCollection attributes = methodDef.GetCustomAttributes(); + if (attributes.Count > 0) + { + record.CustomAttributes = HandleCustomAttributes(ecmaEntity.Module, attributes); + } + } + else + { + throw new NotImplementedException(); + } + + record.Flags = GetMethodAttributes(entity); + record.ImplFlags = GetMethodImplAttributes(entity); + + //TODO: RVA + } + + private MemberReference HandleMethodReference(Cts.MethodDesc method) + { + Debug.Assert(method.IsMethodDefinition); + return (MemberReference)_methods.GetOrCreate(method, _initMethodRef ?? (_initMethodRef = InitializeMethodReference)); + } + + private void InitializeMethodReference(Cts.MethodDesc entity, MemberReference record) + { + record.Name = HandleString(entity.Name); + record.Parent = HandleType(entity.OwningType); + record.Signature = HandleMethodSignature(entity.GetTypicalMethodDefinition().Signature); + } + + private MethodInstantiation HandleMethodInstantiation(Cts.MethodDesc method) + { + return (MethodInstantiation)_methods.GetOrCreate(method, _initMethodInst ?? (_initMethodInst = InitializeMethodInstantiation)); + } + + private void InitializeMethodInstantiation(Cts.MethodDesc entity, MethodInstantiation record) + { + Cts.InstantiatedMethod instantiation = (Cts.InstantiatedMethod)entity; + record.Method = HandleQualifiedMethod(instantiation.GetMethodDefinition()); + record.GenericTypeArguments.Capacity = instantiation.Instantiation.Length; + foreach (Cts.TypeDesc typeArgument in instantiation.Instantiation) + { + record.GenericTypeArguments.Add(HandleType(typeArgument)); + } + } + + public override MethodSignature HandleMethodSignature(Cts.MethodSignature signature) + { + // TODO: if Cts.MethodSignature implements Equals/GetHashCode, we could enable pooling here. + + var result = new MethodSignature + { + CallingConvention = GetSignatureCallingConvention(signature), + GenericParameterCount = signature.GenericParameterCount, + ReturnType = HandleType(signature.ReturnType), + // TODO-NICE: VarArgParameters + }; + + result.Parameters.Capacity = signature.Length; + for (int i = 0; i < signature.Length; i++) + { + result.Parameters.Add(HandleType(signature[i])); + } + + return result; + } + + private MethodAttributes GetMethodAttributes(Cts.MethodDesc method) + { + var ecmaMethod = method as Cts.Ecma.EcmaMethod; + if (ecmaMethod != null) + { + Ecma.MetadataReader reader = ecmaMethod.MetadataReader; + Ecma.MethodDefinition methodDef = reader.GetMethodDefinition(ecmaMethod.Handle); + return methodDef.Attributes; + } + else + throw new NotImplementedException(); + } + + private MethodImplAttributes GetMethodImplAttributes(Cts.MethodDesc method) + { + var ecmaMethod = method as Cts.Ecma.EcmaMethod; + if (ecmaMethod != null) + { + Ecma.MetadataReader reader = ecmaMethod.MetadataReader; + Ecma.MethodDefinition methodDef = reader.GetMethodDefinition(ecmaMethod.Handle); + return methodDef.ImplAttributes; + } + else + throw new NotImplementedException(); + } + + private CallingConventions GetSignatureCallingConvention(Cts.MethodSignature signature) + { + CallingConventions callingConvention = CallingConventions.Standard; + if ((signature.Flags & Cts.MethodSignatureFlags.Static) == 0) + { + callingConvention = CallingConventions.HasThis; + } + // TODO: additional calling convention flags like stdcall / cdecl etc. + return callingConvention; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Namespace.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Namespace.cs new file mode 100644 index 00000000000000..ebd9c45b8c7eae --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Namespace.cs @@ -0,0 +1,157 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + private Dictionary _namespaceDefs = new Dictionary(); + + private NamespaceDefinition HandleNamespaceDefinition(Cts.ModuleDesc parentScope, string namespaceString) + { + Debug.Assert(namespaceString != null); + + NamespaceDefinition result; + NamespaceKey key = new NamespaceKey(parentScope, namespaceString); + if (_namespaceDefs.TryGetValue(key, out result)) + { + return result; + } + + if (namespaceString.Length == 0) + { + var rootNamespace = new NamespaceDefinition + { + Name = null, + }; + _namespaceDefs.Add(key, rootNamespace); + ScopeDefinition rootScope = HandleScopeDefinition(parentScope); + rootScope.RootNamespaceDefinition = rootNamespace; + rootNamespace.ParentScopeOrNamespace = rootScope; + return rootNamespace; + } + + string currentNamespaceName = String.Empty; + NamespaceDefinition currentNamespace = HandleNamespaceDefinition(parentScope, currentNamespaceName); + foreach (var segment in namespaceString.Split('.')) + { + string nextNamespaceName = currentNamespaceName; + if (nextNamespaceName.Length > 0) + nextNamespaceName = nextNamespaceName + '.'; + nextNamespaceName += segment; + NamespaceDefinition nextNamespace; + key = new NamespaceKey(parentScope, nextNamespaceName); + if (!_namespaceDefs.TryGetValue(key, out nextNamespace)) + { + nextNamespace = new NamespaceDefinition + { + Name = HandleString(segment.Length == 0 ? null : segment), + ParentScopeOrNamespace = currentNamespace + }; + + _namespaceDefs.Add(key, nextNamespace); + currentNamespace.NamespaceDefinitions.Add(nextNamespace); + } + currentNamespace = nextNamespace; + currentNamespaceName = nextNamespaceName; + } + + return currentNamespace; + } + + private Dictionary _namespaceRefs = new Dictionary(); + + private NamespaceReference HandleNamespaceReference(Cts.ModuleDesc parentScope, string namespaceString) + { + // The format represents root namespace as a namespace with null name, in contrast with ECMA-335 + if (namespaceString.Length == 0) + namespaceString = null; + + NamespaceReference result; + NamespaceKey key = new NamespaceKey(parentScope, namespaceString); + if (_namespaceRefs.TryGetValue(key, out result)) + { + return result; + } + + ScopeReference scope = HandleScopeReference(parentScope); + NamespaceReference rootNamespace; + key = new NamespaceKey(parentScope, null); + if (!_namespaceRefs.TryGetValue(key, out rootNamespace)) + { + rootNamespace = new NamespaceReference + { + Name = null, + ParentScopeOrNamespace = scope, + }; + _namespaceRefs.Add(key, rootNamespace); + } + + if (namespaceString == null) + return rootNamespace; + + NamespaceReference currentNamespace = rootNamespace; + string currentNamespaceName = String.Empty; + foreach (var segment in namespaceString.Split('.')) + { + string nextNamespaceName = currentNamespaceName; + if (nextNamespaceName.Length > 0) + nextNamespaceName = nextNamespaceName + '.'; + nextNamespaceName += segment; + NamespaceReference nextNamespace; + key = new NamespaceKey(parentScope, nextNamespaceName); + if (!_namespaceRefs.TryGetValue(key, out nextNamespace)) + { + nextNamespace = new NamespaceReference + { + Name = HandleString(segment.Length == 0 ? null : segment), + ParentScopeOrNamespace = currentNamespace + }; + + _namespaceRefs.Add(key, nextNamespace); + } + currentNamespace = nextNamespace; + currentNamespaceName = nextNamespaceName; + } + + return currentNamespace; + } + } + + internal struct NamespaceKey : IEquatable + { + public readonly Cts.ModuleDesc Module; + public readonly string Namespace; + + public NamespaceKey(Cts.ModuleDesc module, string namespaceName) + { + Module = module; + Namespace = namespaceName; + } + + public bool Equals(NamespaceKey other) + { + return Module == other.Module && Namespace == other.Namespace; + } + + public override bool Equals(object obj) + { + if (obj is NamespaceKey) + return Equals((NamespaceKey)obj); + return false; + } + + public override int GetHashCode() + { + return Namespace != null ? Namespace.GetHashCode() : 0; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Parameter.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Parameter.cs new file mode 100644 index 00000000000000..89062c11462488 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Parameter.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; +using Ecma = System.Reflection.Metadata; + +using GenericParameterKind = Internal.Metadata.NativeFormat.GenericParameterKind; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + private GenericParameter HandleGenericParameter(Cts.GenericParameterDesc genParam) + { + var result = new GenericParameter + { + Kind = genParam.Kind == Cts.GenericParameterKind.Type ? + GenericParameterKind.GenericTypeParameter : GenericParameterKind.GenericMethodParameter, + Number = checked((ushort)genParam.Index), + }; + + foreach (Cts.TypeDesc constraint in genParam.TypeConstraints) + { + result.Constraints.Add(HandleType(constraint)); + } + + var ecmaGenParam = genParam as Cts.Ecma.EcmaGenericParameter; + if (ecmaGenParam != null) + { + Ecma.MetadataReader reader = ecmaGenParam.MetadataReader; + Ecma.GenericParameter genParamDef = reader.GetGenericParameter(ecmaGenParam.Handle); + + result.Flags = genParamDef.Attributes; + result.Name = HandleString(reader.GetString(genParamDef.Name)); + + Ecma.CustomAttributeHandleCollection customAttributes = genParamDef.GetCustomAttributes(); + if (customAttributes.Count > 0) + { + result.CustomAttributes = HandleCustomAttributes(ecmaGenParam.Module, customAttributes); + } + } + else + throw new NotImplementedException(); + + return result; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Property.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Property.cs new file mode 100644 index 00000000000000..b0c694e4a25a2f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Property.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; +using Ecma = System.Reflection.Metadata; + +using MethodSemanticsAttributes = Internal.Metadata.NativeFormat.MethodSemanticsAttributes; +using CallingConventions = System.Reflection.CallingConventions; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + private Property HandleProperty(Cts.Ecma.EcmaModule module, Ecma.PropertyDefinitionHandle property) + { + Ecma.MetadataReader reader = module.MetadataReader; + + Ecma.PropertyDefinition propDef = reader.GetPropertyDefinition(property); + + Ecma.PropertyAccessors acc = propDef.GetAccessors(); + Cts.MethodDesc getterMethod = acc.Getter.IsNil ? null : module.GetMethod(acc.Getter); + Cts.MethodDesc setterMethod = acc.Setter.IsNil ? null : module.GetMethod(acc.Setter); + + bool getterHasMetadata = getterMethod != null && _policy.GeneratesMetadata(getterMethod); + bool setterHasMetadata = setterMethod != null && _policy.GeneratesMetadata(setterMethod); + + // Policy: If neither the getter nor setter have metadata, property doesn't have metadata + if (!getterHasMetadata && !setterHasMetadata) + return null; + + Ecma.BlobReader sigBlobReader = reader.GetBlobReader(propDef.Signature); + Cts.PropertySignature sig = new Cts.Ecma.EcmaSignatureParser(module, sigBlobReader, Cts.NotFoundBehavior.Throw).ParsePropertySignature(); + + Property result = new Property + { + Name = HandleString(reader.GetString(propDef.Name)), + Flags = propDef.Attributes, + Signature = new PropertySignature + { + CallingConvention = sig.IsStatic ? CallingConventions.Standard : CallingConventions.HasThis, + Type = HandleType(sig.ReturnType) + }, + }; + + result.Signature.Parameters.Capacity = sig.Length; + for (int i = 0; i < sig.Length; i++) + result.Signature.Parameters.Add(HandleType(sig[i])); + + if (getterHasMetadata) + { + result.MethodSemantics.Add(new MethodSemantics + { + Attributes = MethodSemanticsAttributes.Getter, + Method = HandleMethodDefinition(getterMethod), + }); + } + + if (setterHasMetadata) + { + result.MethodSemantics.Add(new MethodSemantics + { + Attributes = MethodSemanticsAttributes.Setter, + Method = HandleMethodDefinition(setterMethod), + }); + } + + Ecma.ConstantHandle defaultValue = propDef.GetDefaultValue(); + if (!defaultValue.IsNil) + { + result.DefaultValue = HandleConstant(module, defaultValue); + } + + Ecma.CustomAttributeHandleCollection customAttributes = propDef.GetCustomAttributes(); + if (customAttributes.Count > 0) + { + result.CustomAttributes = HandleCustomAttributes(module, customAttributes); + } + + return result; + } + + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Scope.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Scope.cs new file mode 100644 index 00000000000000..8b769ca8ff6bcf --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Scope.cs @@ -0,0 +1,164 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; +using Ecma = System.Reflection.Metadata; + +using Debug = System.Diagnostics.Debug; +using AssemblyFlags = Internal.Metadata.NativeFormat.AssemblyFlags; +using AssemblyNameFlags = System.Reflection.AssemblyNameFlags; +using AssemblyContentType = System.Reflection.AssemblyContentType; +using AssemblyName = System.Reflection.AssemblyName; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + internal EntityMap _scopeDefs + = new EntityMap(EqualityComparer.Default); + private Action _initScopeDef; + + private ScopeDefinition HandleScopeDefinition(Cts.ModuleDesc module) + { + return _scopeDefs.GetOrCreate(module, _initScopeDef ?? (_initScopeDef = InitializeScopeDefinition)); + } + + private void InitializeScopeDefinition(Cts.ModuleDesc module, ScopeDefinition scopeDefinition) + { + var assemblyDesc = module as Cts.IAssemblyDesc; + if (assemblyDesc != null) + { + var assemblyName = assemblyDesc.GetName(); + + scopeDefinition.Name = HandleString(assemblyName.Name); +#if NETFX_45 + // With NET 4.5 contract System.Reflection 4.0.0.0 EcmaModule has no way + // to set Culture in its AssemblyName. + scopeDefinition.Culture = HandleString(assemblyName.CultureName ?? ""); +#else + scopeDefinition.Culture = HandleString(assemblyName.CultureName); +#endif + scopeDefinition.MajorVersion = checked((ushort)assemblyName.Version.Major); + scopeDefinition.MinorVersion = checked((ushort)assemblyName.Version.Minor); + scopeDefinition.BuildNumber = checked((ushort)assemblyName.Version.Build); + scopeDefinition.RevisionNumber = checked((ushort)assemblyName.Version.Revision); + + Debug.Assert((int)AssemblyFlags.PublicKey == (int)AssemblyNameFlags.PublicKey); + Debug.Assert((int)AssemblyFlags.Retargetable == (int)AssemblyNameFlags.Retargetable); + scopeDefinition.Flags = (AssemblyFlags)assemblyName.Flags; + + if (assemblyName.ContentType == AssemblyContentType.WindowsRuntime) + { + scopeDefinition.Flags |= (AssemblyFlags)((int)AssemblyContentType.WindowsRuntime << 9); + } + + if ((scopeDefinition.Flags & AssemblyFlags.PublicKey) != 0) + { + scopeDefinition.PublicKey = assemblyName.GetPublicKey(); + } + else + { + scopeDefinition.PublicKey = assemblyName.GetPublicKeyToken(); + } + + Cts.MetadataType moduleType = module.GetGlobalModuleType(); + if (moduleType != null && _policy.GeneratesMetadata(moduleType)) + { + scopeDefinition.GlobalModuleType = (TypeDefinition)HandleType(moduleType); + } + + Cts.Ecma.EcmaAssembly ecmaAssembly = module as Cts.Ecma.EcmaAssembly; + if (ecmaAssembly != null) + { + Ecma.CustomAttributeHandleCollection customAttributes = ecmaAssembly.AssemblyDefinition.GetCustomAttributes(); + if (customAttributes.Count > 0) + { + scopeDefinition.CustomAttributes = HandleCustomAttributes(ecmaAssembly, customAttributes); + } + + Cts.MethodDesc entryPoint = ecmaAssembly.EntryPoint; + if (entryPoint != null && _policy.GeneratesMetadata(entryPoint)) + { + scopeDefinition.EntryPoint = (QualifiedMethod)HandleQualifiedMethod(entryPoint); + } + + Ecma.MetadataReader reader = ecmaAssembly.MetadataReader; + Ecma.ModuleDefinition moduleDefinition = reader.GetModuleDefinition(); + scopeDefinition.ModuleName = HandleString(reader.GetString(moduleDefinition.Name)); + scopeDefinition.Mvid = reader.GetGuid(moduleDefinition.Mvid).ToByteArray(); + + // This is rather awkward because ModuleDefinition doesn't offer means to get to the custom attributes + Ecma.CustomAttributeHandleCollection moduleAttributes = reader.GetCustomAttributes(Ecma.Ecma335.MetadataTokens.EntityHandle(0x1)); + if (moduleAttributes.Count > 0) + { + scopeDefinition.ModuleCustomAttributes = HandleCustomAttributes(ecmaAssembly, moduleAttributes); + } + + HandleTypeForwarders(ecmaAssembly); + } + } + else + { + throw new NotSupportedException("Multi-module assemblies"); + } + } + + private EntityMap _scopeRefs + = new EntityMap(new SimpleAssemblyNameComparer()); + private Action _initScopeRef; + + private ScopeReference HandleScopeReference(Cts.ModuleDesc module) + { + var assembly = module as Cts.IAssemblyDesc; + if (assembly != null) + return HandleScopeReference(assembly.GetName()); + else + throw new NotSupportedException("Multi-module assemblies"); + } + + private ScopeReference HandleScopeReference(AssemblyName assemblyName) + { + return _scopeRefs.GetOrCreate(assemblyName, _initScopeRef ?? (_initScopeRef = InitializeScopeReference)); + } + + private void InitializeScopeReference(AssemblyName assemblyName, ScopeReference scopeReference) + { + scopeReference.Name = HandleString(assemblyName.Name); + scopeReference.Culture = HandleString(assemblyName.CultureName); + scopeReference.MajorVersion = checked((ushort)assemblyName.Version.Major); + scopeReference.MinorVersion = checked((ushort)assemblyName.Version.Minor); + scopeReference.BuildNumber = checked((ushort)assemblyName.Version.Build); + scopeReference.RevisionNumber = checked((ushort)assemblyName.Version.Revision); + + Debug.Assert((int)AssemblyFlags.PublicKey == (int)AssemblyNameFlags.PublicKey); + Debug.Assert((int)AssemblyFlags.Retargetable == (int)AssemblyNameFlags.Retargetable); + + // References use a public key token instead of full public key. + scopeReference.Flags = (AssemblyFlags)(assemblyName.Flags & ~AssemblyNameFlags.PublicKey); + + if (assemblyName.ContentType == AssemblyContentType.WindowsRuntime) + { + scopeReference.Flags |= (AssemblyFlags)((int)AssemblyContentType.WindowsRuntime << 9); + } + + scopeReference.PublicKeyOrToken = assemblyName.GetPublicKeyToken(); + } + + private class SimpleAssemblyNameComparer : IEqualityComparer + { + public bool Equals(AssemblyName x, AssemblyName y) + { + return Object.Equals(x.Name, y.Name); + } + + public int GetHashCode(AssemblyName obj) + { + return obj.Name?.GetHashCode() ?? 0; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.String.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.String.cs new file mode 100644 index 00000000000000..41d7e6249f777a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.String.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using Internal.Metadata.NativeFormat.Writer; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + private Dictionary _strings = new Dictionary(StringComparer.Ordinal); + + private ConstantStringValue HandleString(string s) + { + if (s == null) + return null; + + ConstantStringValue result; + if (!_strings.TryGetValue(s, out result)) + { + result = (ConstantStringValue)s; + _strings.Add(s, result); + } + + return result; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Type.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Type.cs new file mode 100644 index 00000000000000..ca81bd42d8e4c2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Type.cs @@ -0,0 +1,383 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using Internal.Metadata.NativeFormat.Writer; + +using Ecma = System.Reflection.Metadata; +using Cts = Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; +using TypeAttributes = System.Reflection.TypeAttributes; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + internal EntityMap _types = + new EntityMap(EqualityComparer.Default); + + private Action _initTypeDef; + private Action _initTypeRef; + private Action _initSzArray; + private Action _initArray; + private Action _initByRef; + private Action _initPointer; + private Action _initFunctionPointer; + private Action _initTypeInst; + private Action _initTypeVar; + private Action _initMethodVar; + + public override MetadataRecord HandleType(Cts.TypeDesc type) + { + MetadataRecord rec; + if (_types.TryGet(type, out rec)) + { + return rec; + } + + switch (type.Category) + { + case Cts.TypeFlags.SzArray: + rec = _types.Create((Cts.ArrayType)type, _initSzArray ?? (_initSzArray = InitializeSzArray)); + break; + case Cts.TypeFlags.Array: + rec = _types.Create((Cts.ArrayType)type, _initArray ?? (_initArray = InitializeArray)); + break; + case Cts.TypeFlags.ByRef: + rec = _types.Create((Cts.ByRefType)type, _initByRef ?? (_initByRef = InitializeByRef)); + break; + case Cts.TypeFlags.Pointer: + rec = _types.Create((Cts.PointerType)type, _initPointer ?? (_initPointer = InitializePointer)); + break; + case Cts.TypeFlags.FunctionPointer: + rec = _types.Create((Cts.FunctionPointerType)type, _initFunctionPointer ?? (_initFunctionPointer = InitializeFunctionPointer)); + break; + case Cts.TypeFlags.SignatureTypeVariable: + rec = _types.Create((Cts.SignatureTypeVariable)type, _initTypeVar ?? (_initTypeVar = InitializeTypeVariable)); + break; + case Cts.TypeFlags.SignatureMethodVariable: + rec = _types.Create((Cts.SignatureMethodVariable)type, _initMethodVar ?? (_initMethodVar = InitializeMethodVariable)); + break; + default: + { + Debug.Assert(type.IsDefType); + + if (!type.IsTypeDefinition) + { + // Instantiated generic type + rec = _types.Create(type, _initTypeInst ?? (_initTypeInst = InitializeTypeInstance)); + } + else + { + // Type definition + var metadataType = (Cts.MetadataType)type; + if (_policy.GeneratesMetadata(metadataType)) + { + Debug.Assert(!_policy.IsBlocked(metadataType)); + rec = _types.Create(metadataType, _initTypeDef ?? (_initTypeDef = InitializeTypeDef)); + } + else + { + rec = _types.Create(metadataType, _initTypeRef ?? (_initTypeRef = InitializeTypeRef)); + } + } + } + break; + } + + + Debug.Assert(rec is TypeDefinition || rec is TypeReference || rec is TypeSpecification); + + return rec; + } + + private void InitializeSzArray(Cts.ArrayType entity, TypeSpecification record) + { + record.Signature = new SZArraySignature + { + ElementType = HandleType(entity.ElementType), + }; + } + + private void InitializeArray(Cts.ArrayType entity, TypeSpecification record) + { + record.Signature = new ArraySignature + { + ElementType = HandleType(entity.ElementType), + Rank = entity.Rank, + // TODO: LowerBounds + // TODO: Sizes + }; + } + + private void InitializeByRef(Cts.ByRefType entity, TypeSpecification record) + { + record.Signature = new ByReferenceSignature + { + Type = HandleType(entity.ParameterType) + }; + } + + private void InitializePointer(Cts.PointerType entity, TypeSpecification record) + { + record.Signature = new PointerSignature + { + Type = HandleType(entity.ParameterType) + }; + } + + private void InitializeFunctionPointer(Cts.FunctionPointerType entity, TypeSpecification record) + { + record.Signature = new FunctionPointerSignature + { + Signature = HandleMethodSignature(entity.Signature) + }; + } + + private void InitializeTypeVariable(Cts.SignatureTypeVariable entity, TypeSpecification record) + { + record.Signature = new TypeVariableSignature + { + Number = entity.Index + }; + } + + private void InitializeMethodVariable(Cts.SignatureMethodVariable entity, TypeSpecification record) + { + record.Signature = new MethodTypeVariableSignature + { + Number = entity.Index + }; + } + + private void InitializeTypeInstance(Cts.TypeDesc entity, TypeSpecification record) + { + var sig = new TypeInstantiationSignature + { + GenericType = HandleType(entity.GetTypeDefinition()), + }; + + for (int i = 0; i < entity.Instantiation.Length; i++) + { + sig.GenericTypeArguments.Add(HandleType(entity.Instantiation[i])); + } + + record.Signature = sig; + } + + private TypeReference GetNestedReferenceParent(Cts.MetadataType entity) + { + // This special code deals with the metadata format requirement saying that + // nested type *references* need to have a type *reference* as their containing type. + // This is potentially in conflict with our other rule that says to always resolve + // references to their definition records (we are avoiding emitting references + // to things that have a definition within the same blob to save space). + + Cts.MetadataType containingType = (Cts.MetadataType)entity.ContainingType; + MetadataRecord parentRecord = HandleType(containingType); + TypeReference parentReferenceRecord = parentRecord as TypeReference; + + if (parentReferenceRecord != null) + { + // Easy case - parent type doesn't have a definition record. + return parentReferenceRecord; + } + + // Parent has a type definition record. We need to make a new record that's a reference. + // We don't bother with interning these because this will be rare and metadata writer + // will do the interning anyway. + Debug.Assert(parentRecord is TypeDefinition); + + parentReferenceRecord = new TypeReference + { + TypeName = HandleString(containingType.Name), + }; + + if (containingType.ContainingType != null) + { + parentReferenceRecord.ParentNamespaceOrType = GetNestedReferenceParent(containingType); + } + else + { + parentReferenceRecord.ParentNamespaceOrType = HandleNamespaceReference(containingType.Module, containingType.Namespace); + } + + return parentReferenceRecord; + } + + private void InitializeTypeRef(Cts.MetadataType entity, TypeReference record) + { + Debug.Assert(entity.IsTypeDefinition); + + if (entity.ContainingType != null) + { + record.ParentNamespaceOrType = GetNestedReferenceParent(entity); + } + else + { + record.ParentNamespaceOrType = HandleNamespaceReference(entity.Module, entity.Namespace); + } + + record.TypeName = HandleString(entity.Name); + } + + private void InitializeTypeDef(Cts.MetadataType entity, TypeDefinition record) + { + Debug.Assert(entity.IsTypeDefinition); + + Cts.MetadataType containingType = (Cts.MetadataType)entity.ContainingType; + if (containingType != null) + { + var enclosingType = (TypeDefinition)HandleType(containingType); + record.EnclosingType = enclosingType; + enclosingType.NestedTypes.Add(record); + + var namespaceDefinition = + HandleNamespaceDefinition(containingType.Module, entity.ContainingType.Namespace); + record.NamespaceDefinition = namespaceDefinition; + } + else + { + var namespaceDefinition = HandleNamespaceDefinition(entity.Module, entity.Namespace); + record.NamespaceDefinition = namespaceDefinition; + + if (entity.IsModuleType) + { + // These don't get added to the global namespace. + // Instead, they have a dedicated field on the scope record. + } + else + { + namespaceDefinition.TypeDefinitions.Add(record); + } + } + + record.Name = HandleString(entity.Name); + + Cts.ClassLayoutMetadata layoutMetadata = entity.GetClassLayout(); + record.Size = checked((uint)layoutMetadata.Size); + record.PackingSize = checked((ushort)layoutMetadata.PackingSize); + record.Flags = GetTypeAttributes(entity); + + if (entity.HasBaseType) + { + record.BaseType = HandleType(entity.BaseType); + } + + record.Interfaces.Capacity = entity.ExplicitlyImplementedInterfaces.Length; + foreach (var interfaceType in entity.ExplicitlyImplementedInterfaces) + { + if (IsBlocked(interfaceType)) + continue; + record.Interfaces.Add(HandleType(interfaceType)); + } + + if (entity.HasInstantiation) + { + record.GenericParameters.Capacity = entity.Instantiation.Length; + foreach (var p in entity.Instantiation) + record.GenericParameters.Add(HandleGenericParameter((Cts.GenericParameterDesc)p)); + } + + foreach (var field in entity.GetFields()) + { + if (_policy.GeneratesMetadata(field)) + { + record.Fields.Add(HandleFieldDefinition(field)); + } + } + + foreach (var method in entity.GetMethods()) + { + if (_policy.GeneratesMetadata(method)) + { + record.Methods.Add(HandleMethodDefinition(method)); + } + } + + var ecmaEntity = entity as Cts.Ecma.EcmaType; + if (ecmaEntity != null) + { + Ecma.TypeDefinition ecmaRecord = ecmaEntity.MetadataReader.GetTypeDefinition(ecmaEntity.Handle); + + foreach (var e in ecmaRecord.GetEvents()) + { + Event evt = HandleEvent(ecmaEntity.EcmaModule, e); + if (evt != null) + record.Events.Add(evt); + } + + foreach (var property in ecmaRecord.GetProperties()) + { + Property prop = HandleProperty(ecmaEntity.EcmaModule, property); + if (prop != null) + record.Properties.Add(prop); + } + + Ecma.CustomAttributeHandleCollection customAttributes = ecmaRecord.GetCustomAttributes(); + if (customAttributes.Count > 0) + { + record.CustomAttributes = HandleCustomAttributes(ecmaEntity.EcmaModule, customAttributes); + } + + /* COMPLETENESS + foreach (var miHandle in ecmaRecord.GetMethodImplementations()) + { + Ecma.MetadataReader reader = ecmaEntity.EcmaModule.MetadataReader; + + Ecma.MethodImplementation miDef = reader.GetMethodImplementation(miHandle); + + Cts.MethodDesc methodBody = (Cts.MethodDesc)ecmaEntity.EcmaModule.GetObject(miDef.MethodBody); + if (_policy.IsBlocked(methodBody)) + continue; + + Cts.MethodDesc methodDecl = (Cts.MethodDesc)ecmaEntity.EcmaModule.GetObject(miDef.MethodDeclaration); + if (_policy.IsBlocked(methodDecl.GetTypicalMethodDefinition())) + continue; + + MethodImpl methodImplRecord = new MethodImpl + { + MethodBody = HandleQualifiedMethod(methodBody), + MethodDeclaration = HandleQualifiedMethod(methodDecl) + }; + + record.MethodImpls.Add(methodImplRecord); + }*/ + } + } + + private TypeAttributes GetTypeAttributes(Cts.MetadataType type) + { + TypeAttributes result; + + var ecmaType = type as Cts.Ecma.EcmaType; + if (ecmaType != null) + { + Ecma.TypeDefinition ecmaRecord = ecmaType.MetadataReader.GetTypeDefinition(ecmaType.Handle); + result = ecmaRecord.Attributes; + } + else + { + result = 0; + + if (type.IsExplicitLayout) + result |= TypeAttributes.ExplicitLayout; + if (type.IsSequentialLayout) + result |= TypeAttributes.SequentialLayout; + if (type.IsInterface) + result |= TypeAttributes.Interface; + if (type.IsSealed) + result |= TypeAttributes.Sealed; + if (type.IsBeforeFieldInit) + result |= TypeAttributes.BeforeFieldInit; + + // Not set: Abstract, Ansi/Unicode/Auto, HasSecurity, Import, visibility, Serializable, + // WindowsRuntime, HasSecurity, SpecialName, RTSpecialName + } + + return result; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.TypeForwarders.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.TypeForwarders.cs new file mode 100644 index 00000000000000..104a2223da0dc6 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.TypeForwarders.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; +using Ecma = System.Reflection.Metadata; + +using Debug = System.Diagnostics.Debug; +using AssemblyName = System.Reflection.AssemblyName; +using AssemblyContentType = System.Reflection.AssemblyContentType; +using AssemblyNameFlags = System.Reflection.AssemblyNameFlags; +using AssemblyFlags = System.Reflection.AssemblyFlags; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + private void HandleTypeForwarders(Cts.Ecma.EcmaModule module) + { + foreach (var exportedTypeHandle in module.MetadataReader.ExportedTypes) + { + if (!_policy.GeneratesMetadata(module, exportedTypeHandle)) + { + continue; + } + + Ecma.ExportedType exportedType = module.MetadataReader.GetExportedType(exportedTypeHandle); + if (exportedType.IsForwarder || exportedType.Implementation.Kind == Ecma.HandleKind.ExportedType) + { + HandleTypeForwarder(module, exportedType); + } + else + { + Debug.Assert(false, "Multi-module assemblies"); + } + } + } + + private TypeForwarder HandleTypeForwarder(Cts.Ecma.EcmaModule module, Ecma.ExportedType exportedType) + { + Ecma.MetadataReader reader = module.MetadataReader; + string name = reader.GetString(exportedType.Name); + TypeForwarder result; + + switch (exportedType.Implementation.Kind) + { + case Ecma.HandleKind.AssemblyReference: + { + string ns = reader.GetString(exportedType.Namespace); + NamespaceDefinition namespaceDefinition = HandleNamespaceDefinition(module, ns); + + Ecma.AssemblyReference assemblyRef = reader.GetAssemblyReference((Ecma.AssemblyReferenceHandle)exportedType.Implementation); + AssemblyName refName = new AssemblyName + { + ContentType = (AssemblyContentType)((int)(assemblyRef.Flags & AssemblyFlags.ContentTypeMask) >> 9), + Flags = (AssemblyNameFlags)(assemblyRef.Flags & ~AssemblyFlags.ContentTypeMask), + CultureName = reader.GetString(assemblyRef.Culture), + Name = reader.GetString(assemblyRef.Name), + Version = assemblyRef.Version, + }; + + if ((assemblyRef.Flags & AssemblyFlags.PublicKey) != 0) + refName.SetPublicKey(reader.GetBlobBytes(assemblyRef.PublicKeyOrToken)); + else + refName.SetPublicKeyToken(reader.GetBlobBytes(assemblyRef.PublicKeyOrToken)); + + result = new TypeForwarder + { + Name = HandleString(name), + Scope = HandleScopeReference(refName), + }; + + namespaceDefinition.TypeForwarders.Add(result); + } + break; + + case Ecma.HandleKind.ExportedType: + { + TypeForwarder scope = HandleTypeForwarder(module, reader.GetExportedType((Ecma.ExportedTypeHandle)exportedType.Implementation)); + + result = new TypeForwarder + { + Name = HandleString(name), + Scope = scope.Scope, + }; + + scope.NestedTypes.Add(result); + } + break; + + default: + throw new BadImageFormatException(); + } + + return result; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.cs new file mode 100644 index 00000000000000..f570c836d121ab --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Cts = Internal.TypeSystem; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.Metadata +{ + /// + /// Provides implementation of the contract. + /// This class is generic over the policy to make policy lookups cheap (policy being + /// a struct means all the interface calls end up being constrained over the type + /// and therefore fully inlineable). + /// + internal sealed partial class Transform : MetadataTransform + where TPolicy : struct, IMetadataPolicy + { + private TPolicy _policy; + + public Transform(TPolicy policy) + { + _policy = policy; + } + + private bool IsBlocked(Cts.TypeDesc type) + { + switch (type.Category) + { + case Cts.TypeFlags.SzArray: + case Cts.TypeFlags.Array: + case Cts.TypeFlags.Pointer: + case Cts.TypeFlags.ByRef: + return IsBlocked(((Cts.ParameterizedType)type).ParameterType); + + case Cts.TypeFlags.SignatureMethodVariable: + case Cts.TypeFlags.SignatureTypeVariable: + return false; + + case Cts.TypeFlags.FunctionPointer: + { + Cts.MethodSignature pointerSignature = ((Cts.FunctionPointerType)type).Signature; + + for (int i = 0; i < pointerSignature.Length; i++) + if (IsBlocked(pointerSignature[i])) + return true; + + return IsBlocked(pointerSignature.ReturnType); + } + default: + Debug.Assert(type.IsDefType); + + if (!type.IsTypeDefinition) + { + if (IsBlocked(type.GetTypeDefinition())) + return true; + + foreach (var arg in type.Instantiation) + if (IsBlocked(arg)) + return true; + + return false; + } + + return _policy.IsBlocked((Cts.MetadataType)type); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/ConstantValues.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/ConstantValues.cs new file mode 100644 index 00000000000000..4013dfbab145d9 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/ConstantValues.cs @@ -0,0 +1,247 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Text; +using System.Diagnostics; +using System.Threading.Tasks; +using System.Collections.Generic; + +using System.Reflection; + +using Internal.LowLevelLinq; + +namespace Internal.Metadata.NativeFormat.Writer +{ + public partial class ConstantBooleanValue + { + public override string ToString() + { + //return String.Format("ConstantBooleanValue : {0}", this.Value); + return String.Format("(Boolean){0}", this.Value); + } + } + + public partial class ConstantBooleanArray + { + public override string ToString() + { + //return "ConstantBooleanArray : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(Boolean[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantCharValue + { + public override string ToString() + { + //return String.Format("ConstantCharValue : {0}", this.Value); + + return String.Format("'{0}'", this.Value); + } + } + + public partial class ConstantCharArray + { + public override string ToString() + { + //return "ConstantCharArray : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(Char[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantStringValue + { + public override string ToString() + { + //return String.Format("ConstantStringValue : {0}", this.Value); + if (this.Value == null) return "null"; + else return String.Format("\"{0}\"", this.Value); + } + } + + public partial class ConstantStringArray + { + public override string ToString() + { + //return "ConstantStringArray : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(String[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantByteValue + { + public override string ToString() + { + //return String.Format("ConstantByteValue : {0}", this.Value); + return String.Format("(Byte){0}", this.Value); + } + } + + public partial class ConstantByteArray + { + public override string ToString() + { + //return "ConstantByteArray : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(Byte[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantSByteValue + { + public override string ToString() + { + //return String.Format("ConstantSByteValue : {0}", this.Value); + return String.Format("(SByte){0}", this.Value); + } + } + + public partial class ConstantSByteArray + { + public override string ToString() + { + //return "ConstantSByteArray : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(SByte[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantInt16Value + { + public override string ToString() + { + //return String.Format("ConstantInt16Value : {0}", this.Value); + return String.Format("(Int16){0}", this.Value); + } + } + + public partial class ConstantInt16Array + { + public override string ToString() + { + //return "ConstantInt16Array : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(Int16[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantUInt16Value + { + public override string ToString() + { + //return String.Format("ConstantUInt16Value : {0}", this.Value); + return String.Format("(UInt16){0}", this.Value); + } + } + + public partial class ConstantUInt16Array + { + public override string ToString() + { + //return "ConstantUInt16Array : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(UInt16[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantInt32Value + { + public override string ToString() + { + //return String.Format("ConstantInt32Value : {0}", this.Value); + return String.Format("(Int32){0}", this.Value); + } + } + + public partial class ConstantInt32Array + { + public override string ToString() + { + //return "ConstantInt32Array : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(Int32[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantUInt32Value + { + public override string ToString() + { + //return String.Format("ConstantUInt32Value : {0}", this.Value); + return String.Format("(UInt32){0}", this.Value); + } + } + + public partial class ConstantUInt32Array + { + public override string ToString() + { + //return "ConstantUInt32Array : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(UInt32[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantInt64Value + { + public override string ToString() + { + //return String.Format("ConstantInt64Value : {0}", this.Value); + return String.Format("(Int64){0}", this.Value); + } + } + + public partial class ConstantInt64Array + { + public override string ToString() + { + //return "ConstantInt64Array : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(Int64[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantUInt64Value + { + public override string ToString() + { + //return String.Format("ConstantUInt64Value : {0}", this.Value); + return String.Format("(UInt64){0}", this.Value); + } + } + + public partial class ConstantUInt64Array + { + public override string ToString() + { + //return "ConstantUInt64Array : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(UInt64[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantSingleValue + { + public override string ToString() + { + //return String.Format("ConstantSingleValue : {0}", this.Value); + return String.Format("(Single){0}", this.Value); + } + } + + public partial class ConstantSingleArray + { + public override string ToString() + { + //return "ConstantSingleArray : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(Single[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantDoubleValue + { + public override string ToString() + { + //return String.Format("ConstantDoubleValue : {0}", this.Value); + return String.Format("(Double){0}", this.Value); + } + } + + public partial class ConstantDoubleArray + { + public override string ToString() + { + //return "ConstantDoubleArray : {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + return "(Double[]) {" + String.Join(", ", this.Value.Select(v => v.ToString())) + "}"; + } + } + public partial class ConstantReferenceValue + { + public override string ToString() + { + return "null"; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/MdBinaryWriter.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/MdBinaryWriter.cs new file mode 100644 index 00000000000000..b8ca4bc55bbfd0 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/MdBinaryWriter.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using Internal.LowLevelLinq; +using Internal.NativeFormat; +using Debug = System.Diagnostics.Debug; + +namespace Internal.Metadata.NativeFormat.Writer +{ + internal static partial class MdBinaryWriter + { + public static void Write(this NativeWriter writer, bool value) + { + writer.WriteUInt8((byte)(value ? 1 : 0)); + } + + public static void Write(this NativeWriter writer, byte value) + { + writer.WriteUInt8(value); + } + + public static void Write(this NativeWriter writer, sbyte value) + { + writer.WriteUInt8((byte)value); + } + + public static void Write(this NativeWriter writer, short value) + { + writer.WriteSigned(value); + } + + public static void Write(this NativeWriter writer, ushort value) + { + writer.WriteUnsigned(value); + } + + public static void Write(this NativeWriter writer, int value) + { + writer.WriteSigned(value); + } + + public static void Write(this NativeWriter writer, uint value) + { + writer.WriteUnsigned(value); + } + + public static void Write(this NativeWriter writer, ulong value) + { + writer.WriteUnsignedLong(value); + } + + public static void Write(this NativeWriter writer, long value) + { + writer.WriteSignedLong(value); + } + + public static void Write(this NativeWriter writer, string value) + { + Debug.Assert(value != null); + writer.WriteString(value); + } + + public static void Write(this NativeWriter writer, char value) + { + writer.WriteUnsigned((uint)value); + } + + public static void Write(this NativeWriter writer, float value) + { + writer.WriteFloat(value); + } + + public static void Write(this NativeWriter writer, double value) + { + writer.WriteDouble(value); + } + + public static void Write(this NativeWriter writer, MetadataRecord record) + { + if (record != null) + writer.WriteUnsigned((uint)record.HandleType | (uint)(record.HandleOffset << 8)); + else + writer.WriteUnsigned(0); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/MdBinaryWriterGen.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/MdBinaryWriterGen.cs new file mode 100644 index 00000000000000..22eaa62a2978d0 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/MdBinaryWriterGen.cs @@ -0,0 +1,1663 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// NOTE: This is a generated file - do not manually edit! + +#pragma warning disable 649 + +using System; +using System.IO; +using System.Collections.Generic; +using System.Reflection; +using Internal.LowLevelLinq; +using Internal.NativeFormat; +using Debug = System.Diagnostics.Debug; + +namespace Internal.Metadata.NativeFormat.Writer +{ + internal static partial class MdBinaryWriter + { + public static void Write(this NativeWriter writer, bool[] values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Length); + foreach (bool value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, char[] values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Length); + foreach (char value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, byte[] values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Length); + foreach (byte value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, sbyte[] values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Length); + foreach (sbyte value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, short[] values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Length); + foreach (short value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ushort[] values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Length); + foreach (ushort value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, int[] values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Length); + foreach (int value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, uint[] values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Length); + foreach (uint value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, long[] values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Length); + foreach (long value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ulong[] values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Length); + foreach (ulong value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, float[] values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Length); + foreach (float value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, double[] values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Length); + foreach (double value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, AssemblyFlags value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, AssemblyHashAlgorithm value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, CallingConventions value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, EventAttributes value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, FieldAttributes value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, GenericParameterAttributes value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, GenericParameterKind value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, MethodAttributes value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, MethodImplAttributes value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, MethodSemanticsAttributes value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, NamedArgumentMemberKind value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, ParameterAttributes value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, PInvokeAttributes value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, PropertyAttributes value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, TypeAttributes value) + { + writer.WriteUnsigned((uint)value); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (MetadataRecord value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ArraySignature record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ArraySignature value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ByReferenceSignature record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ByReferenceSignature value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantBooleanArray record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantBooleanArray value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantBooleanValue record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantBooleanValue value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantBoxedEnumValue record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantBoxedEnumValue value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantByteArray record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantByteArray value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantByteValue record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantByteValue value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantCharArray record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantCharArray value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantCharValue record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantCharValue value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantDoubleArray record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantDoubleArray value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantDoubleValue record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantDoubleValue value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantEnumArray record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantEnumArray value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantHandleArray record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantHandleArray value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantInt16Array record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantInt16Array value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantInt16Value record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantInt16Value value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantInt32Array record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantInt32Array value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantInt32Value record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantInt32Value value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantInt64Array record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantInt64Array value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantInt64Value record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantInt64Value value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantReferenceValue record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantReferenceValue value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantSByteArray record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantSByteArray value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantSByteValue record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantSByteValue value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantSingleArray record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantSingleArray value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantSingleValue record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantSingleValue value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantStringArray record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantStringArray value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantStringValue record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantStringValue value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantUInt16Array record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantUInt16Array value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantUInt16Value record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantUInt16Value value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantUInt32Array record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantUInt32Array value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantUInt32Value record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantUInt32Value value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantUInt64Array record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantUInt64Array value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ConstantUInt64Value record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ConstantUInt64Value value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, CustomAttribute record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (CustomAttribute value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, Event record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (Event value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, Field record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (Field value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, FieldSignature record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (FieldSignature value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, FunctionPointerSignature record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (FunctionPointerSignature value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, GenericParameter record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (GenericParameter value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, MemberReference record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (MemberReference value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, Method record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (Method value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, MethodInstantiation record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (MethodInstantiation value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, MethodSemantics record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (MethodSemantics value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, MethodSignature record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (MethodSignature value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, MethodTypeVariableSignature record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (MethodTypeVariableSignature value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ModifiedType record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ModifiedType value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, NamedArgument record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (NamedArgument value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, NamespaceDefinition record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (NamespaceDefinition value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, NamespaceReference record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (NamespaceReference value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, Parameter record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (Parameter value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, PointerSignature record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (PointerSignature value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, Property record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (Property value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, PropertySignature record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (PropertySignature value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, QualifiedField record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (QualifiedField value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, QualifiedMethod record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (QualifiedMethod value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, SZArraySignature record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (SZArraySignature value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ScopeDefinition record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ScopeDefinition value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, ScopeReference record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (ScopeReference value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, TypeDefinition record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (TypeDefinition value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, TypeForwarder record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (TypeForwarder value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, TypeInstantiationSignature record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (TypeInstantiationSignature value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, TypeReference record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (TypeReference value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, TypeSpecification record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (TypeSpecification value in values) + { + writer.Write(value); + } + } // Write + + public static void Write(this NativeWriter writer, TypeVariableSignature record) + { + if (record != null) + writer.WriteUnsigned((uint)record.Handle.Offset); + else + writer.WriteUnsigned(0); + } // Write + + public static void Write(this NativeWriter writer, List values) + { + if (values == null) + { + writer.WriteUnsigned(0); + return; + } + writer.WriteUnsigned((uint)values.Count); + foreach (TypeVariableSignature value in values) + { + writer.Write(value); + } + } // Write + } // MdBinaryWriter +} // Internal.Metadata.NativeFormat.Writer diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/NativeFormatWriterGen.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/NativeFormatWriterGen.cs new file mode 100644 index 00000000000000..b8a008b716c182 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/NativeFormatWriterGen.cs @@ -0,0 +1,4932 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// NOTE: This is a generated file - do not manually edit! + +#pragma warning disable 649 + +using System; +using System.IO; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using Internal.LowLevelLinq; +using Internal.Metadata.NativeFormat.Writer; +using Internal.NativeFormat; +using HandleType = Internal.Metadata.NativeFormat.HandleType; +using Debug = System.Diagnostics.Debug; + +namespace Internal.Metadata.NativeFormat.Writer +{ + public partial class ArraySignature : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ArraySignature; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + ElementType = visitor.Visit(this, ElementType); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ArraySignature; + if (other == null) return false; + if (!Object.Equals(ElementType, other.ElementType)) return false; + if (Rank != other.Rank) return false; + if (!Sizes.SequenceEqual(other.Sizes)) return false; + if (!LowerBounds.SequenceEqual(other.LowerBounds)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1450734452; + hash = ((hash << 13) - (hash >> 19)) ^ (ElementType == null ? 0 : ElementType.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ Rank.GetHashCode(); + if (Sizes != null) + { + for (int i = 0; i < Sizes.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Sizes[i].GetHashCode(); + } + } + if (LowerBounds != null) + { + for (int i = 0; i < LowerBounds.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ LowerBounds[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(ElementType == null || + ElementType.HandleType == HandleType.TypeDefinition || + ElementType.HandleType == HandleType.TypeReference || + ElementType.HandleType == HandleType.TypeSpecification); + writer.Write(ElementType); + writer.Write(Rank); + writer.Write(Sizes); + writer.Write(LowerBounds); + } // Save + + internal static ArraySignatureHandle AsHandle(ArraySignature record) + { + if (record == null) + { + return new ArraySignatureHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ArraySignatureHandle Handle + { + get + { + return new ArraySignatureHandle(HandleOffset); + } + } // Handle + + public MetadataRecord ElementType; + public int Rank; + public Int32[] Sizes; + public Int32[] LowerBounds; + } // ArraySignature + + public partial class ByReferenceSignature : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ByReferenceSignature; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Type = visitor.Visit(this, Type); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ByReferenceSignature; + if (other == null) return false; + if (!Object.Equals(Type, other.Type)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -2085375797; + hash = ((hash << 13) - (hash >> 19)) ^ (Type == null ? 0 : Type.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(Type == null || + Type.HandleType == HandleType.TypeDefinition || + Type.HandleType == HandleType.TypeReference || + Type.HandleType == HandleType.TypeSpecification); + writer.Write(Type); + } // Save + + internal static ByReferenceSignatureHandle AsHandle(ByReferenceSignature record) + { + if (record == null) + { + return new ByReferenceSignatureHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ByReferenceSignatureHandle Handle + { + get + { + return new ByReferenceSignatureHandle(HandleOffset); + } + } // Handle + + public MetadataRecord Type; + } // ByReferenceSignature + + public partial class ConstantBooleanArray : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantBooleanArray; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantBooleanArray; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 1817079014; + if (Value != null) + { + for (int i = 0; i < Value.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Value[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantBooleanArrayHandle AsHandle(ConstantBooleanArray record) + { + if (record == null) + { + return new ConstantBooleanArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantBooleanArrayHandle Handle + { + get + { + return new ConstantBooleanArrayHandle(HandleOffset); + } + } // Handle + + public Boolean[] Value; + } // ConstantBooleanArray + + public partial class ConstantBooleanValue : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantBooleanValue; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantBooleanValue; + if (other == null) return false; + if (Value != other.Value) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 555429541; + hash = ((hash << 13) - (hash >> 19)) ^ Value.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantBooleanValueHandle AsHandle(ConstantBooleanValue record) + { + if (record == null) + { + return new ConstantBooleanValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantBooleanValueHandle Handle + { + get + { + return new ConstantBooleanValueHandle(HandleOffset); + } + } // Handle + + public bool Value; + } // ConstantBooleanValue + + public partial class ConstantBoxedEnumValue : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantBoxedEnumValue; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Value = visitor.Visit(this, Value); + Type = visitor.Visit(this, Type); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantBoxedEnumValue; + if (other == null) return false; + if (!Object.Equals(Value, other.Value)) return false; + if (!Object.Equals(Type, other.Type)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 879057725; + hash = ((hash << 13) - (hash >> 19)) ^ (Value == null ? 0 : Value.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Type == null ? 0 : Type.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(Value == null || + Value.HandleType == HandleType.ConstantByteValue || + Value.HandleType == HandleType.ConstantSByteValue || + Value.HandleType == HandleType.ConstantInt16Value || + Value.HandleType == HandleType.ConstantUInt16Value || + Value.HandleType == HandleType.ConstantInt32Value || + Value.HandleType == HandleType.ConstantUInt32Value || + Value.HandleType == HandleType.ConstantInt64Value || + Value.HandleType == HandleType.ConstantUInt64Value); + writer.Write(Value); + Debug.Assert(Type == null || + Type.HandleType == HandleType.TypeDefinition || + Type.HandleType == HandleType.TypeReference || + Type.HandleType == HandleType.TypeSpecification); + writer.Write(Type); + } // Save + + internal static ConstantBoxedEnumValueHandle AsHandle(ConstantBoxedEnumValue record) + { + if (record == null) + { + return new ConstantBoxedEnumValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantBoxedEnumValueHandle Handle + { + get + { + return new ConstantBoxedEnumValueHandle(HandleOffset); + } + } // Handle + + public MetadataRecord Value; + public MetadataRecord Type; + } // ConstantBoxedEnumValue + + public partial class ConstantByteArray : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantByteArray; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantByteArray; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 2080036690; + if (Value != null) + { + for (int i = 0; i < Value.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Value[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantByteArrayHandle AsHandle(ConstantByteArray record) + { + if (record == null) + { + return new ConstantByteArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantByteArrayHandle Handle + { + get + { + return new ConstantByteArrayHandle(HandleOffset); + } + } // Handle + + public Byte[] Value; + } // ConstantByteArray + + public partial class ConstantByteValue : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantByteValue; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantByteValue; + if (other == null) return false; + if (Value != other.Value) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -452758418; + hash = ((hash << 13) - (hash >> 19)) ^ Value.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantByteValueHandle AsHandle(ConstantByteValue record) + { + if (record == null) + { + return new ConstantByteValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantByteValueHandle Handle + { + get + { + return new ConstantByteValueHandle(HandleOffset); + } + } // Handle + + public byte Value; + } // ConstantByteValue + + public partial class ConstantCharArray : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantCharArray; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantCharArray; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -210173789; + if (Value != null) + { + for (int i = 0; i < Value.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Value[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantCharArrayHandle AsHandle(ConstantCharArray record) + { + if (record == null) + { + return new ConstantCharArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantCharArrayHandle Handle + { + get + { + return new ConstantCharArrayHandle(HandleOffset); + } + } // Handle + + public Char[] Value; + } // ConstantCharArray + + public partial class ConstantCharValue : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantCharValue; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantCharValue; + if (other == null) return false; + if (Value != other.Value) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 2135306273; + hash = ((hash << 13) - (hash >> 19)) ^ Value.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantCharValueHandle AsHandle(ConstantCharValue record) + { + if (record == null) + { + return new ConstantCharValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantCharValueHandle Handle + { + get + { + return new ConstantCharValueHandle(HandleOffset); + } + } // Handle + + public char Value; + } // ConstantCharValue + + public partial class ConstantDoubleArray : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantDoubleArray; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantDoubleArray; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value, DoubleComparer.Instance)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 1195490519; + if (Value != null) + { + for (int i = 0; i < Value.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Value[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantDoubleArrayHandle AsHandle(ConstantDoubleArray record) + { + if (record == null) + { + return new ConstantDoubleArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantDoubleArrayHandle Handle + { + get + { + return new ConstantDoubleArrayHandle(HandleOffset); + } + } // Handle + + public Double[] Value; + } // ConstantDoubleArray + + public partial class ConstantDoubleValue : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantDoubleValue; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantDoubleValue; + if (other == null) return false; + if (!CustomComparer.Equals(Value, other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -621001209; + hash = ((hash << 13) - (hash >> 19)) ^ Value.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantDoubleValueHandle AsHandle(ConstantDoubleValue record) + { + if (record == null) + { + return new ConstantDoubleValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantDoubleValueHandle Handle + { + get + { + return new ConstantDoubleValueHandle(HandleOffset); + } + } // Handle + + public double Value; + } // ConstantDoubleValue + + public partial class ConstantEnumArray : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantEnumArray; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + ElementType = visitor.Visit(this, ElementType); + Value = visitor.Visit(this, Value); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantEnumArray; + if (other == null) return false; + if (!Object.Equals(ElementType, other.ElementType)) return false; + if (!Object.Equals(Value, other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 1812865730; + hash = ((hash << 13) - (hash >> 19)) ^ (ElementType == null ? 0 : ElementType.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Value == null ? 0 : Value.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(ElementType); + writer.Write(Value); + } // Save + + internal static ConstantEnumArrayHandle AsHandle(ConstantEnumArray record) + { + if (record == null) + { + return new ConstantEnumArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantEnumArrayHandle Handle + { + get + { + return new ConstantEnumArrayHandle(HandleOffset); + } + } // Handle + + public MetadataRecord ElementType; + public MetadataRecord Value; + } // ConstantEnumArray + + public partial class ConstantHandleArray : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantHandleArray; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Value = visitor.Visit(this, Value); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantHandleArray; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1728604822; + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantHandleArrayHandle AsHandle(ConstantHandleArray record) + { + if (record == null) + { + return new ConstantHandleArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantHandleArrayHandle Handle + { + get + { + return new ConstantHandleArrayHandle(HandleOffset); + } + } // Handle + + public List Value = new List(); + } // ConstantHandleArray + + public partial class ConstantInt16Array : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantInt16Array; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantInt16Array; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1341795012; + if (Value != null) + { + for (int i = 0; i < Value.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Value[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantInt16ArrayHandle AsHandle(ConstantInt16Array record) + { + if (record == null) + { + return new ConstantInt16ArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantInt16ArrayHandle Handle + { + get + { + return new ConstantInt16ArrayHandle(HandleOffset); + } + } // Handle + + public Int16[] Value; + } // ConstantInt16Array + + public partial class ConstantInt16Value : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantInt16Value; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantInt16Value; + if (other == null) return false; + if (Value != other.Value) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 813581618; + hash = ((hash << 13) - (hash >> 19)) ^ Value.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantInt16ValueHandle AsHandle(ConstantInt16Value record) + { + if (record == null) + { + return new ConstantInt16ValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantInt16ValueHandle Handle + { + get + { + return new ConstantInt16ValueHandle(HandleOffset); + } + } // Handle + + public short Value; + } // ConstantInt16Value + + public partial class ConstantInt32Array : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantInt32Array; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantInt32Array; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -889690268; + if (Value != null) + { + for (int i = 0; i < Value.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Value[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantInt32ArrayHandle AsHandle(ConstantInt32Array record) + { + if (record == null) + { + return new ConstantInt32ArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantInt32ArrayHandle Handle + { + get + { + return new ConstantInt32ArrayHandle(HandleOffset); + } + } // Handle + + public Int32[] Value; + } // ConstantInt32Array + + public partial class ConstantInt32Value : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantInt32Value; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantInt32Value; + if (other == null) return false; + if (Value != other.Value) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1381815736; + hash = ((hash << 13) - (hash >> 19)) ^ Value.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantInt32ValueHandle AsHandle(ConstantInt32Value record) + { + if (record == null) + { + return new ConstantInt32ValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantInt32ValueHandle Handle + { + get + { + return new ConstantInt32ValueHandle(HandleOffset); + } + } // Handle + + public int Value; + } // ConstantInt32Value + + public partial class ConstantInt64Array : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantInt64Array; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantInt64Array; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1304463479; + if (Value != null) + { + for (int i = 0; i < Value.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Value[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantInt64ArrayHandle AsHandle(ConstantInt64Array record) + { + if (record == null) + { + return new ConstantInt64ArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantInt64ArrayHandle Handle + { + get + { + return new ConstantInt64ArrayHandle(HandleOffset); + } + } // Handle + + public Int64[] Value; + } // ConstantInt64Array + + public partial class ConstantInt64Value : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantInt64Value; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantInt64Value; + if (other == null) return false; + if (Value != other.Value) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 826277577; + hash = ((hash << 13) - (hash >> 19)) ^ Value.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantInt64ValueHandle AsHandle(ConstantInt64Value record) + { + if (record == null) + { + return new ConstantInt64ValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantInt64ValueHandle Handle + { + get + { + return new ConstantInt64ValueHandle(HandleOffset); + } + } // Handle + + public long Value; + } // ConstantInt64Value + + public partial class ConstantReferenceValue : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantReferenceValue; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantReferenceValue; + if (other == null) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -2104982909; + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + } // Save + + internal static ConstantReferenceValueHandle AsHandle(ConstantReferenceValue record) + { + if (record == null) + { + return new ConstantReferenceValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantReferenceValueHandle Handle + { + get + { + return new ConstantReferenceValueHandle(HandleOffset); + } + } // Handle + + } // ConstantReferenceValue + + public partial class ConstantSByteArray : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantSByteArray; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantSByteArray; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 234859551; + if (Value != null) + { + for (int i = 0; i < Value.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Value[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantSByteArrayHandle AsHandle(ConstantSByteArray record) + { + if (record == null) + { + return new ConstantSByteArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantSByteArrayHandle Handle + { + get + { + return new ConstantSByteArrayHandle(HandleOffset); + } + } // Handle + + public SByte[] Value; + } // ConstantSByteArray + + public partial class ConstantSByteValue : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantSByteValue; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantSByteValue; + if (other == null) return false; + if (Value != other.Value) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -222060848; + hash = ((hash << 13) - (hash >> 19)) ^ Value.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantSByteValueHandle AsHandle(ConstantSByteValue record) + { + if (record == null) + { + return new ConstantSByteValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantSByteValueHandle Handle + { + get + { + return new ConstantSByteValueHandle(HandleOffset); + } + } // Handle + + public sbyte Value; + } // ConstantSByteValue + + public partial class ConstantSingleArray : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantSingleArray; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantSingleArray; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value, SingleComparer.Instance)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -2043917844; + if (Value != null) + { + for (int i = 0; i < Value.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Value[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantSingleArrayHandle AsHandle(ConstantSingleArray record) + { + if (record == null) + { + return new ConstantSingleArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantSingleArrayHandle Handle + { + get + { + return new ConstantSingleArrayHandle(HandleOffset); + } + } // Handle + + public Single[] Value; + } // ConstantSingleArray + + public partial class ConstantSingleValue : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantSingleValue; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantSingleValue; + if (other == null) return false; + if (!CustomComparer.Equals(Value, other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 1809397893; + hash = ((hash << 13) - (hash >> 19)) ^ Value.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantSingleValueHandle AsHandle(ConstantSingleValue record) + { + if (record == null) + { + return new ConstantSingleValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantSingleValueHandle Handle + { + get + { + return new ConstantSingleValueHandle(HandleOffset); + } + } // Handle + + public float Value; + } // ConstantSingleValue + + public partial class ConstantStringArray : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantStringArray; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Value = visitor.Visit(this, Value); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantStringArray; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -106723178; + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(Value.TrueForAll(handle => handle == null || + handle.HandleType == HandleType.ConstantStringValue || + handle.HandleType == HandleType.ConstantReferenceValue)); + writer.Write(Value); + } // Save + + internal static ConstantStringArrayHandle AsHandle(ConstantStringArray record) + { + if (record == null) + { + return new ConstantStringArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantStringArrayHandle Handle + { + get + { + return new ConstantStringArrayHandle(HandleOffset); + } + } // Handle + + public List Value = new List(); + } // ConstantStringArray + + public partial class ConstantStringValue : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantStringValue; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantStringValue; + if (other == null) return false; + if (Value != other.Value) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 697695316; + hash = ((hash << 13) - (hash >> 19)) ^ (Value == null ? 0 : Value.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + if (Value == null) + return; + + writer.Write(Value); + } // Save + + internal static ConstantStringValueHandle AsHandle(ConstantStringValue record) + { + if (record == null) + { + return new ConstantStringValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantStringValueHandle Handle + { + get + { + if (Value == null) + return new ConstantStringValueHandle(0); + else + return new ConstantStringValueHandle(HandleOffset); + } + } // Handle + + public string Value; + } // ConstantStringValue + + public partial class ConstantUInt16Array : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantUInt16Array; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantUInt16Array; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -89281077; + if (Value != null) + { + for (int i = 0; i < Value.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Value[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantUInt16ArrayHandle AsHandle(ConstantUInt16Array record) + { + if (record == null) + { + return new ConstantUInt16ArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantUInt16ArrayHandle Handle + { + get + { + return new ConstantUInt16ArrayHandle(HandleOffset); + } + } // Handle + + public UInt16[] Value; + } // ConstantUInt16Array + + public partial class ConstantUInt16Value : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantUInt16Value; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantUInt16Value; + if (other == null) return false; + if (Value != other.Value) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1363963764; + hash = ((hash << 13) - (hash >> 19)) ^ Value.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantUInt16ValueHandle AsHandle(ConstantUInt16Value record) + { + if (record == null) + { + return new ConstantUInt16ValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantUInt16ValueHandle Handle + { + get + { + return new ConstantUInt16ValueHandle(HandleOffset); + } + } // Handle + + public ushort Value; + } // ConstantUInt16Value + + public partial class ConstantUInt32Array : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantUInt32Array; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantUInt32Array; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1294553100; + if (Value != null) + { + for (int i = 0; i < Value.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Value[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantUInt32ArrayHandle AsHandle(ConstantUInt32Array record) + { + if (record == null) + { + return new ConstantUInt32ArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantUInt32ArrayHandle Handle + { + get + { + return new ConstantUInt32ArrayHandle(HandleOffset); + } + } // Handle + + public UInt32[] Value; + } // ConstantUInt32Array + + public partial class ConstantUInt32Value : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantUInt32Value; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantUInt32Value; + if (other == null) return false; + if (Value != other.Value) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1681229940; + hash = ((hash << 13) - (hash >> 19)) ^ Value.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantUInt32ValueHandle AsHandle(ConstantUInt32Value record) + { + if (record == null) + { + return new ConstantUInt32ValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantUInt32ValueHandle Handle + { + get + { + return new ConstantUInt32ValueHandle(HandleOffset); + } + } // Handle + + public uint Value; + } // ConstantUInt32Value + + public partial class ConstantUInt64Array : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantUInt64Array; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantUInt64Array; + if (other == null) return false; + if (!Value.SequenceEqual(other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 47301549; + if (Value != null) + { + for (int i = 0; i < Value.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Value[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantUInt64ArrayHandle AsHandle(ConstantUInt64Array record) + { + if (record == null) + { + return new ConstantUInt64ArrayHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantUInt64ArrayHandle Handle + { + get + { + return new ConstantUInt64ArrayHandle(HandleOffset); + } + } // Handle + + public UInt64[] Value; + } // ConstantUInt64Array + + public partial class ConstantUInt64Value : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ConstantUInt64Value; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ConstantUInt64Value; + if (other == null) return false; + if (Value != other.Value) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1162192418; + hash = ((hash << 13) - (hash >> 19)) ^ Value.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Value); + } // Save + + internal static ConstantUInt64ValueHandle AsHandle(ConstantUInt64Value record) + { + if (record == null) + { + return new ConstantUInt64ValueHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ConstantUInt64ValueHandle Handle + { + get + { + return new ConstantUInt64ValueHandle(HandleOffset); + } + } // Handle + + public ulong Value; + } // ConstantUInt64Value + + public partial class CustomAttribute : MetadataRecord + { + public CustomAttribute() + { + _equalsReentrancyGuard = new ThreadLocal(() => new ReentrancyGuardStack()); + } + + public override HandleType HandleType + { + get + { + return HandleType.CustomAttribute; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Constructor = visitor.Visit(this, Constructor); + FixedArguments = visitor.Visit(this, FixedArguments); + NamedArguments = visitor.Visit(this, NamedArguments); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as CustomAttribute; + if (other == null) return false; + if (_equalsReentrancyGuard.Value.Contains(other)) + return true; + _equalsReentrancyGuard.Value.Push(other); + try + { + if (!Object.Equals(Constructor, other.Constructor)) return false; + if (!FixedArguments.SequenceEqual(other.FixedArguments)) return false; + if (!NamedArguments.SequenceEqual(other.NamedArguments)) return false; + } + finally + { + var popped = _equalsReentrancyGuard.Value.Pop(); + Debug.Assert(Object.ReferenceEquals(other, popped)); + } + return true; + } // Equals + private ThreadLocal _equalsReentrancyGuard; + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 478371161; + hash = ((hash << 13) - (hash >> 19)) ^ (Constructor == null ? 0 : Constructor.GetHashCode()); + if (FixedArguments != null) + { + for (int i = 0; i < FixedArguments.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (FixedArguments[i] == null ? 0 : FixedArguments[i].GetHashCode()); + } + } + if (NamedArguments != null) + { + for (int i = 0; i < NamedArguments.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (NamedArguments[i] == null ? 0 : NamedArguments[i].GetHashCode()); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(Constructor == null || + Constructor.HandleType == HandleType.QualifiedMethod || + Constructor.HandleType == HandleType.MemberReference); + writer.Write(Constructor); + Debug.Assert(FixedArguments.TrueForAll(handle => handle == null || + handle.HandleType == HandleType.TypeDefinition || + handle.HandleType == HandleType.TypeReference || + handle.HandleType == HandleType.TypeSpecification || + handle.HandleType == HandleType.ConstantBooleanArray || + handle.HandleType == HandleType.ConstantBooleanValue || + handle.HandleType == HandleType.ConstantByteArray || + handle.HandleType == HandleType.ConstantByteValue || + handle.HandleType == HandleType.ConstantCharArray || + handle.HandleType == HandleType.ConstantCharValue || + handle.HandleType == HandleType.ConstantDoubleArray || + handle.HandleType == HandleType.ConstantDoubleValue || + handle.HandleType == HandleType.ConstantEnumArray || + handle.HandleType == HandleType.ConstantHandleArray || + handle.HandleType == HandleType.ConstantInt16Array || + handle.HandleType == HandleType.ConstantInt16Value || + handle.HandleType == HandleType.ConstantInt32Array || + handle.HandleType == HandleType.ConstantInt32Value || + handle.HandleType == HandleType.ConstantInt64Array || + handle.HandleType == HandleType.ConstantInt64Value || + handle.HandleType == HandleType.ConstantReferenceValue || + handle.HandleType == HandleType.ConstantSByteArray || + handle.HandleType == HandleType.ConstantSByteValue || + handle.HandleType == HandleType.ConstantSingleArray || + handle.HandleType == HandleType.ConstantSingleValue || + handle.HandleType == HandleType.ConstantStringArray || + handle.HandleType == HandleType.ConstantStringValue || + handle.HandleType == HandleType.ConstantUInt16Array || + handle.HandleType == HandleType.ConstantUInt16Value || + handle.HandleType == HandleType.ConstantUInt32Array || + handle.HandleType == HandleType.ConstantUInt32Value || + handle.HandleType == HandleType.ConstantUInt64Array || + handle.HandleType == HandleType.ConstantUInt64Value)); + writer.Write(FixedArguments); + writer.Write(NamedArguments); + } // Save + + internal static CustomAttributeHandle AsHandle(CustomAttribute record) + { + if (record == null) + { + return new CustomAttributeHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new CustomAttributeHandle Handle + { + get + { + return new CustomAttributeHandle(HandleOffset); + } + } // Handle + + public MetadataRecord Constructor; + public List FixedArguments = new List(); + public List NamedArguments = new List(); + } // CustomAttribute + + public partial class Event : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.Event; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Name = visitor.Visit(this, Name); + Type = visitor.Visit(this, Type); + MethodSemantics = visitor.Visit(this, MethodSemantics); + CustomAttributes = visitor.Visit(this, CustomAttributes); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as Event; + if (other == null) return false; + if (Flags != other.Flags) return false; + if (!Object.Equals(Name, other.Name)) return false; + if (!Object.Equals(Type, other.Type)) return false; + if (!MethodSemantics.SequenceEqual(other.MethodSemantics)) return false; + if (!CustomAttributes.SequenceEqual(other.CustomAttributes)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1454825650; + hash = ((hash << 13) - (hash >> 19)) ^ Flags.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Type == null ? 0 : Type.GetHashCode()); + if (MethodSemantics != null) + { + for (int i = 0; i < MethodSemantics.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (MethodSemantics[i] == null ? 0 : MethodSemantics[i].GetHashCode()); + } + } + if (CustomAttributes != null) + { + for (int i = 0; i < CustomAttributes.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (CustomAttributes[i] == null ? 0 : CustomAttributes[i].GetHashCode()); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Flags); + writer.Write(Name); + Debug.Assert(Type == null || + Type.HandleType == HandleType.TypeDefinition || + Type.HandleType == HandleType.TypeReference || + Type.HandleType == HandleType.TypeSpecification); + writer.Write(Type); + writer.Write(MethodSemantics); + writer.Write(CustomAttributes); + } // Save + + internal static EventHandle AsHandle(Event record) + { + if (record == null) + { + return new EventHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new EventHandle Handle + { + get + { + return new EventHandle(HandleOffset); + } + } // Handle + + public EventAttributes Flags; + public ConstantStringValue Name; + public MetadataRecord Type; + public List MethodSemantics = new List(); + public List CustomAttributes = new List(); + } // Event + + public partial class Field : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.Field; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Name = visitor.Visit(this, Name); + Signature = visitor.Visit(this, Signature); + DefaultValue = visitor.Visit(this, DefaultValue); + CustomAttributes = visitor.Visit(this, CustomAttributes); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as Field; + if (other == null) return false; + if (Flags != other.Flags) return false; + if (!Object.Equals(Name, other.Name)) return false; + if (!Object.Equals(Signature, other.Signature)) return false; + if (!Object.Equals(DefaultValue, other.DefaultValue)) return false; + if (Offset != other.Offset) return false; + if (!CustomAttributes.SequenceEqual(other.CustomAttributes)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -540975116; + hash = ((hash << 13) - (hash >> 19)) ^ Flags.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Signature == null ? 0 : Signature.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (DefaultValue == null ? 0 : DefaultValue.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ Offset.GetHashCode(); + if (CustomAttributes != null) + { + for (int i = 0; i < CustomAttributes.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (CustomAttributes[i] == null ? 0 : CustomAttributes[i].GetHashCode()); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Flags); + writer.Write(Name); + writer.Write(Signature); + Debug.Assert(DefaultValue == null || + DefaultValue.HandleType == HandleType.TypeDefinition || + DefaultValue.HandleType == HandleType.TypeReference || + DefaultValue.HandleType == HandleType.TypeSpecification || + DefaultValue.HandleType == HandleType.ConstantBooleanArray || + DefaultValue.HandleType == HandleType.ConstantBooleanValue || + DefaultValue.HandleType == HandleType.ConstantByteArray || + DefaultValue.HandleType == HandleType.ConstantByteValue || + DefaultValue.HandleType == HandleType.ConstantCharArray || + DefaultValue.HandleType == HandleType.ConstantCharValue || + DefaultValue.HandleType == HandleType.ConstantDoubleArray || + DefaultValue.HandleType == HandleType.ConstantDoubleValue || + DefaultValue.HandleType == HandleType.ConstantEnumArray || + DefaultValue.HandleType == HandleType.ConstantHandleArray || + DefaultValue.HandleType == HandleType.ConstantInt16Array || + DefaultValue.HandleType == HandleType.ConstantInt16Value || + DefaultValue.HandleType == HandleType.ConstantInt32Array || + DefaultValue.HandleType == HandleType.ConstantInt32Value || + DefaultValue.HandleType == HandleType.ConstantInt64Array || + DefaultValue.HandleType == HandleType.ConstantInt64Value || + DefaultValue.HandleType == HandleType.ConstantReferenceValue || + DefaultValue.HandleType == HandleType.ConstantSByteArray || + DefaultValue.HandleType == HandleType.ConstantSByteValue || + DefaultValue.HandleType == HandleType.ConstantSingleArray || + DefaultValue.HandleType == HandleType.ConstantSingleValue || + DefaultValue.HandleType == HandleType.ConstantStringArray || + DefaultValue.HandleType == HandleType.ConstantStringValue || + DefaultValue.HandleType == HandleType.ConstantUInt16Array || + DefaultValue.HandleType == HandleType.ConstantUInt16Value || + DefaultValue.HandleType == HandleType.ConstantUInt32Array || + DefaultValue.HandleType == HandleType.ConstantUInt32Value || + DefaultValue.HandleType == HandleType.ConstantUInt64Array || + DefaultValue.HandleType == HandleType.ConstantUInt64Value); + writer.Write(DefaultValue); + writer.Write(Offset); + writer.Write(CustomAttributes); + } // Save + + internal static FieldHandle AsHandle(Field record) + { + if (record == null) + { + return new FieldHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new FieldHandle Handle + { + get + { + return new FieldHandle(HandleOffset); + } + } // Handle + + public FieldAttributes Flags; + public ConstantStringValue Name; + public FieldSignature Signature; + public MetadataRecord DefaultValue; + public uint Offset; + public List CustomAttributes = new List(); + } // Field + + public partial class FieldSignature : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.FieldSignature; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Type = visitor.Visit(this, Type); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as FieldSignature; + if (other == null) return false; + if (!Object.Equals(Type, other.Type)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 1865977400; + hash = ((hash << 13) - (hash >> 19)) ^ (Type == null ? 0 : Type.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(Type == null || + Type.HandleType == HandleType.TypeDefinition || + Type.HandleType == HandleType.TypeReference || + Type.HandleType == HandleType.TypeSpecification || + Type.HandleType == HandleType.ModifiedType); + writer.Write(Type); + } // Save + + internal static FieldSignatureHandle AsHandle(FieldSignature record) + { + if (record == null) + { + return new FieldSignatureHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new FieldSignatureHandle Handle + { + get + { + return new FieldSignatureHandle(HandleOffset); + } + } // Handle + + public MetadataRecord Type; + } // FieldSignature + + public partial class FunctionPointerSignature : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.FunctionPointerSignature; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Signature = visitor.Visit(this, Signature); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as FunctionPointerSignature; + if (other == null) return false; + if (!Object.Equals(Signature, other.Signature)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 1400760676; + hash = ((hash << 13) - (hash >> 19)) ^ (Signature == null ? 0 : Signature.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Signature); + } // Save + + internal static FunctionPointerSignatureHandle AsHandle(FunctionPointerSignature record) + { + if (record == null) + { + return new FunctionPointerSignatureHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new FunctionPointerSignatureHandle Handle + { + get + { + return new FunctionPointerSignatureHandle(HandleOffset); + } + } // Handle + + public MethodSignature Signature; + } // FunctionPointerSignature + + public partial class GenericParameter : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.GenericParameter; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Name = visitor.Visit(this, Name); + Constraints = visitor.Visit(this, Constraints); + CustomAttributes = visitor.Visit(this, CustomAttributes); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as GenericParameter; + if (other == null) return false; + if (Number != other.Number) return false; + if (Flags != other.Flags) return false; + if (Kind != other.Kind) return false; + if (!Object.Equals(Name, other.Name)) return false; + if (!Constraints.SequenceEqual(other.Constraints)) return false; + if (!CustomAttributes.SequenceEqual(other.CustomAttributes)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 1389630306; + hash = ((hash << 13) - (hash >> 19)) ^ Number.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ Flags.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ Kind.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + if (Constraints != null) + { + for (int i = 0; i < Constraints.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (Constraints[i] == null ? 0 : Constraints[i].GetHashCode()); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Number); + writer.Write(Flags); + writer.Write(Kind); + writer.Write(Name); + Debug.Assert(Constraints.TrueForAll(handle => handle == null || + handle.HandleType == HandleType.TypeDefinition || + handle.HandleType == HandleType.TypeReference || + handle.HandleType == HandleType.TypeSpecification || + handle.HandleType == HandleType.ModifiedType)); + writer.Write(Constraints); + writer.Write(CustomAttributes); + } // Save + + internal static GenericParameterHandle AsHandle(GenericParameter record) + { + if (record == null) + { + return new GenericParameterHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new GenericParameterHandle Handle + { + get + { + return new GenericParameterHandle(HandleOffset); + } + } // Handle + + public ushort Number; + public GenericParameterAttributes Flags; + public GenericParameterKind Kind; + public ConstantStringValue Name; + public List Constraints = new List(); + public List CustomAttributes = new List(); + } // GenericParameter + + public partial class MemberReference : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.MemberReference; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Parent = visitor.Visit(this, Parent); + Name = visitor.Visit(this, Name); + Signature = visitor.Visit(this, Signature); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as MemberReference; + if (other == null) return false; + if (!Object.Equals(Parent, other.Parent)) return false; + if (!Object.Equals(Name, other.Name)) return false; + if (!Object.Equals(Signature, other.Signature)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -875402938; + hash = ((hash << 13) - (hash >> 19)) ^ (Parent == null ? 0 : Parent.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Signature == null ? 0 : Signature.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(Parent == null || + Parent.HandleType == HandleType.TypeDefinition || + Parent.HandleType == HandleType.TypeReference || + Parent.HandleType == HandleType.TypeSpecification); + writer.Write(Parent); + writer.Write(Name); + Debug.Assert(Signature == null || + Signature.HandleType == HandleType.MethodSignature || + Signature.HandleType == HandleType.FieldSignature); + writer.Write(Signature); + } // Save + + internal static MemberReferenceHandle AsHandle(MemberReference record) + { + if (record == null) + { + return new MemberReferenceHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new MemberReferenceHandle Handle + { + get + { + return new MemberReferenceHandle(HandleOffset); + } + } // Handle + + public MetadataRecord Parent; + public ConstantStringValue Name; + public MetadataRecord Signature; + } // MemberReference + + public partial class Method : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.Method; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Name = visitor.Visit(this, Name); + Signature = visitor.Visit(this, Signature); + Parameters = visitor.Visit(this, Parameters); + GenericParameters = visitor.Visit(this, GenericParameters); + CustomAttributes = visitor.Visit(this, CustomAttributes); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as Method; + if (other == null) return false; + if (Flags != other.Flags) return false; + if (ImplFlags != other.ImplFlags) return false; + if (!Object.Equals(Name, other.Name)) return false; + if (!Object.Equals(Signature, other.Signature)) return false; + if (!Parameters.SequenceEqual(other.Parameters)) return false; + if (!GenericParameters.SequenceEqual(other.GenericParameters)) return false; + if (!CustomAttributes.SequenceEqual(other.CustomAttributes)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1225154478; + hash = ((hash << 13) - (hash >> 19)) ^ Flags.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ ImplFlags.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Signature == null ? 0 : Signature.GetHashCode()); + if (Parameters != null) + { + for (int i = 0; i < Parameters.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (Parameters[i] == null ? 0 : Parameters[i].GetHashCode()); + } + } + if (GenericParameters != null) + { + for (int i = 0; i < GenericParameters.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (GenericParameters[i] == null ? 0 : GenericParameters[i].GetHashCode()); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Flags); + writer.Write(ImplFlags); + writer.Write(Name); + writer.Write(Signature); + writer.Write(Parameters); + writer.Write(GenericParameters); + writer.Write(CustomAttributes); + } // Save + + internal static MethodHandle AsHandle(Method record) + { + if (record == null) + { + return new MethodHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new MethodHandle Handle + { + get + { + return new MethodHandle(HandleOffset); + } + } // Handle + + public MethodAttributes Flags; + public MethodImplAttributes ImplFlags; + public ConstantStringValue Name; + public MethodSignature Signature; + public List Parameters = new List(); + public List GenericParameters = new List(); + public List CustomAttributes = new List(); + } // Method + + public partial class MethodInstantiation : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.MethodInstantiation; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Method = visitor.Visit(this, Method); + GenericTypeArguments = visitor.Visit(this, GenericTypeArguments); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as MethodInstantiation; + if (other == null) return false; + if (!Object.Equals(Method, other.Method)) return false; + if (!GenericTypeArguments.SequenceEqual(other.GenericTypeArguments)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1777408040; + hash = ((hash << 13) - (hash >> 19)) ^ (Method == null ? 0 : Method.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(Method == null || + Method.HandleType == HandleType.QualifiedMethod || + Method.HandleType == HandleType.MemberReference); + writer.Write(Method); + Debug.Assert(GenericTypeArguments.TrueForAll(handle => handle == null || + handle.HandleType == HandleType.TypeDefinition || + handle.HandleType == HandleType.TypeReference || + handle.HandleType == HandleType.TypeSpecification)); + writer.Write(GenericTypeArguments); + } // Save + + internal static MethodInstantiationHandle AsHandle(MethodInstantiation record) + { + if (record == null) + { + return new MethodInstantiationHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new MethodInstantiationHandle Handle + { + get + { + return new MethodInstantiationHandle(HandleOffset); + } + } // Handle + + public MetadataRecord Method; + public List GenericTypeArguments = new List(); + } // MethodInstantiation + + public partial class MethodSemantics : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.MethodSemantics; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Method = visitor.Visit(this, Method); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as MethodSemantics; + if (other == null) return false; + if (Attributes != other.Attributes) return false; + if (!Object.Equals(Method, other.Method)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 1967828724; + hash = ((hash << 13) - (hash >> 19)) ^ Attributes.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (Method == null ? 0 : Method.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Attributes); + writer.Write(Method); + } // Save + + internal static MethodSemanticsHandle AsHandle(MethodSemantics record) + { + if (record == null) + { + return new MethodSemanticsHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new MethodSemanticsHandle Handle + { + get + { + return new MethodSemanticsHandle(HandleOffset); + } + } // Handle + + public MethodSemanticsAttributes Attributes; + public Method Method; + } // MethodSemantics + + public partial class MethodSignature : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.MethodSignature; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + ReturnType = visitor.Visit(this, ReturnType); + Parameters = visitor.Visit(this, Parameters); + VarArgParameters = visitor.Visit(this, VarArgParameters); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as MethodSignature; + if (other == null) return false; + if (CallingConvention != other.CallingConvention) return false; + if (GenericParameterCount != other.GenericParameterCount) return false; + if (!Object.Equals(ReturnType, other.ReturnType)) return false; + if (!Parameters.SequenceEqual(other.Parameters)) return false; + if (!VarArgParameters.SequenceEqual(other.VarArgParameters)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1362083279; + hash = ((hash << 13) - (hash >> 19)) ^ CallingConvention.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ GenericParameterCount.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (ReturnType == null ? 0 : ReturnType.GetHashCode()); + if (Parameters != null) + { + for (int i = 0; i < Parameters.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (Parameters[i] == null ? 0 : Parameters[i].GetHashCode()); + } + } + if (VarArgParameters != null) + { + for (int i = 0; i < VarArgParameters.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (VarArgParameters[i] == null ? 0 : VarArgParameters[i].GetHashCode()); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(CallingConvention); + writer.Write(GenericParameterCount); + Debug.Assert(ReturnType == null || + ReturnType.HandleType == HandleType.TypeDefinition || + ReturnType.HandleType == HandleType.TypeReference || + ReturnType.HandleType == HandleType.TypeSpecification || + ReturnType.HandleType == HandleType.ModifiedType); + writer.Write(ReturnType); + Debug.Assert(Parameters.TrueForAll(handle => handle == null || + handle.HandleType == HandleType.TypeDefinition || + handle.HandleType == HandleType.TypeReference || + handle.HandleType == HandleType.TypeSpecification || + handle.HandleType == HandleType.ModifiedType)); + writer.Write(Parameters); + Debug.Assert(VarArgParameters.TrueForAll(handle => handle == null || + handle.HandleType == HandleType.TypeDefinition || + handle.HandleType == HandleType.TypeReference || + handle.HandleType == HandleType.TypeSpecification || + handle.HandleType == HandleType.ModifiedType)); + writer.Write(VarArgParameters); + } // Save + + internal static MethodSignatureHandle AsHandle(MethodSignature record) + { + if (record == null) + { + return new MethodSignatureHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new MethodSignatureHandle Handle + { + get + { + return new MethodSignatureHandle(HandleOffset); + } + } // Handle + + public CallingConventions CallingConvention; + public int GenericParameterCount; + public MetadataRecord ReturnType; + public List Parameters = new List(); + public List VarArgParameters = new List(); + } // MethodSignature + + public partial class MethodTypeVariableSignature : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.MethodTypeVariableSignature; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as MethodTypeVariableSignature; + if (other == null) return false; + if (Number != other.Number) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 542622499; + hash = ((hash << 13) - (hash >> 19)) ^ Number.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Number); + } // Save + + internal static MethodTypeVariableSignatureHandle AsHandle(MethodTypeVariableSignature record) + { + if (record == null) + { + return new MethodTypeVariableSignatureHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new MethodTypeVariableSignatureHandle Handle + { + get + { + return new MethodTypeVariableSignatureHandle(HandleOffset); + } + } // Handle + + public int Number; + } // MethodTypeVariableSignature + + public partial class ModifiedType : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ModifiedType; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + ModifierType = visitor.Visit(this, ModifierType); + Type = visitor.Visit(this, Type); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ModifiedType; + if (other == null) return false; + if (IsOptional != other.IsOptional) return false; + if (!Object.Equals(ModifierType, other.ModifierType)) return false; + if (!Object.Equals(Type, other.Type)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -707476144; + hash = ((hash << 13) - (hash >> 19)) ^ IsOptional.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (ModifierType == null ? 0 : ModifierType.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Type == null ? 0 : Type.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(IsOptional); + Debug.Assert(ModifierType == null || + ModifierType.HandleType == HandleType.TypeDefinition || + ModifierType.HandleType == HandleType.TypeReference || + ModifierType.HandleType == HandleType.TypeSpecification); + writer.Write(ModifierType); + Debug.Assert(Type == null || + Type.HandleType == HandleType.TypeDefinition || + Type.HandleType == HandleType.TypeReference || + Type.HandleType == HandleType.TypeSpecification || + Type.HandleType == HandleType.ModifiedType); + writer.Write(Type); + } // Save + + internal static ModifiedTypeHandle AsHandle(ModifiedType record) + { + if (record == null) + { + return new ModifiedTypeHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ModifiedTypeHandle Handle + { + get + { + return new ModifiedTypeHandle(HandleOffset); + } + } // Handle + + public bool IsOptional; + public MetadataRecord ModifierType; + public MetadataRecord Type; + } // ModifiedType + + public partial class NamedArgument : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.NamedArgument; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Name = visitor.Visit(this, Name); + Type = visitor.Visit(this, Type); + Value = visitor.Visit(this, Value); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as NamedArgument; + if (other == null) return false; + if (Flags != other.Flags) return false; + if (!Object.Equals(Name, other.Name)) return false; + if (!Object.Equals(Type, other.Type)) return false; + if (!Object.Equals(Value, other.Value)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -469180039; + hash = ((hash << 13) - (hash >> 19)) ^ Flags.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Type == null ? 0 : Type.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Value == null ? 0 : Value.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Flags); + writer.Write(Name); + Debug.Assert(Type == null || + Type.HandleType == HandleType.TypeDefinition || + Type.HandleType == HandleType.TypeReference || + Type.HandleType == HandleType.TypeSpecification); + writer.Write(Type); + Debug.Assert(Value == null || + Value.HandleType == HandleType.TypeDefinition || + Value.HandleType == HandleType.TypeReference || + Value.HandleType == HandleType.TypeSpecification || + Value.HandleType == HandleType.ConstantBooleanArray || + Value.HandleType == HandleType.ConstantBooleanValue || + Value.HandleType == HandleType.ConstantByteArray || + Value.HandleType == HandleType.ConstantByteValue || + Value.HandleType == HandleType.ConstantCharArray || + Value.HandleType == HandleType.ConstantCharValue || + Value.HandleType == HandleType.ConstantDoubleArray || + Value.HandleType == HandleType.ConstantDoubleValue || + Value.HandleType == HandleType.ConstantEnumArray || + Value.HandleType == HandleType.ConstantHandleArray || + Value.HandleType == HandleType.ConstantInt16Array || + Value.HandleType == HandleType.ConstantInt16Value || + Value.HandleType == HandleType.ConstantInt32Array || + Value.HandleType == HandleType.ConstantInt32Value || + Value.HandleType == HandleType.ConstantInt64Array || + Value.HandleType == HandleType.ConstantInt64Value || + Value.HandleType == HandleType.ConstantReferenceValue || + Value.HandleType == HandleType.ConstantSByteArray || + Value.HandleType == HandleType.ConstantSByteValue || + Value.HandleType == HandleType.ConstantSingleArray || + Value.HandleType == HandleType.ConstantSingleValue || + Value.HandleType == HandleType.ConstantStringArray || + Value.HandleType == HandleType.ConstantStringValue || + Value.HandleType == HandleType.ConstantUInt16Array || + Value.HandleType == HandleType.ConstantUInt16Value || + Value.HandleType == HandleType.ConstantUInt32Array || + Value.HandleType == HandleType.ConstantUInt32Value || + Value.HandleType == HandleType.ConstantUInt64Array || + Value.HandleType == HandleType.ConstantUInt64Value); + writer.Write(Value); + } // Save + + internal static NamedArgumentHandle AsHandle(NamedArgument record) + { + if (record == null) + { + return new NamedArgumentHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new NamedArgumentHandle Handle + { + get + { + return new NamedArgumentHandle(HandleOffset); + } + } // Handle + + public NamedArgumentMemberKind Flags; + public ConstantStringValue Name; + public MetadataRecord Type; + public MetadataRecord Value; + } // NamedArgument + + public partial class NamespaceDefinition : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.NamespaceDefinition; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + ParentScopeOrNamespace = visitor.Visit(this, ParentScopeOrNamespace); + Name = visitor.Visit(this, Name); + TypeDefinitions = visitor.Visit(this, TypeDefinitions); + TypeForwarders = visitor.Visit(this, TypeForwarders); + NamespaceDefinitions = visitor.Visit(this, NamespaceDefinitions); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as NamespaceDefinition; + if (other == null) return false; + if (!Object.Equals(ParentScopeOrNamespace, other.ParentScopeOrNamespace)) return false; + if (!Object.Equals(Name, other.Name)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 633688634; + hash = ((hash << 13) - (hash >> 19)) ^ (ParentScopeOrNamespace == null ? 0 : ParentScopeOrNamespace.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(ParentScopeOrNamespace == null || + ParentScopeOrNamespace.HandleType == HandleType.NamespaceDefinition || + ParentScopeOrNamespace.HandleType == HandleType.ScopeDefinition); + writer.Write(ParentScopeOrNamespace); + writer.Write(Name); + writer.Write(TypeDefinitions); + writer.Write(TypeForwarders); + writer.Write(NamespaceDefinitions); + } // Save + + internal static NamespaceDefinitionHandle AsHandle(NamespaceDefinition record) + { + if (record == null) + { + return new NamespaceDefinitionHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new NamespaceDefinitionHandle Handle + { + get + { + return new NamespaceDefinitionHandle(HandleOffset); + } + } // Handle + + public MetadataRecord ParentScopeOrNamespace; + public ConstantStringValue Name; + public List TypeDefinitions = new List(); + public List TypeForwarders = new List(); + public List NamespaceDefinitions = new List(); + } // NamespaceDefinition + + public partial class NamespaceReference : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.NamespaceReference; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + ParentScopeOrNamespace = visitor.Visit(this, ParentScopeOrNamespace); + Name = visitor.Visit(this, Name); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as NamespaceReference; + if (other == null) return false; + if (!Object.Equals(ParentScopeOrNamespace, other.ParentScopeOrNamespace)) return false; + if (!Object.Equals(Name, other.Name)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 1563382231; + hash = ((hash << 13) - (hash >> 19)) ^ (ParentScopeOrNamespace == null ? 0 : ParentScopeOrNamespace.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(ParentScopeOrNamespace == null || + ParentScopeOrNamespace.HandleType == HandleType.NamespaceReference || + ParentScopeOrNamespace.HandleType == HandleType.ScopeReference); + writer.Write(ParentScopeOrNamespace); + writer.Write(Name); + } // Save + + internal static NamespaceReferenceHandle AsHandle(NamespaceReference record) + { + if (record == null) + { + return new NamespaceReferenceHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new NamespaceReferenceHandle Handle + { + get + { + return new NamespaceReferenceHandle(HandleOffset); + } + } // Handle + + public MetadataRecord ParentScopeOrNamespace; + public ConstantStringValue Name; + } // NamespaceReference + + public partial class Parameter : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.Parameter; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Name = visitor.Visit(this, Name); + DefaultValue = visitor.Visit(this, DefaultValue); + CustomAttributes = visitor.Visit(this, CustomAttributes); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as Parameter; + if (other == null) return false; + if (Flags != other.Flags) return false; + if (Sequence != other.Sequence) return false; + if (!Object.Equals(Name, other.Name)) return false; + if (!Object.Equals(DefaultValue, other.DefaultValue)) return false; + if (!CustomAttributes.SequenceEqual(other.CustomAttributes)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1049753891; + hash = ((hash << 13) - (hash >> 19)) ^ Flags.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ Sequence.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (DefaultValue == null ? 0 : DefaultValue.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Flags); + writer.Write(Sequence); + writer.Write(Name); + Debug.Assert(DefaultValue == null || + DefaultValue.HandleType == HandleType.TypeDefinition || + DefaultValue.HandleType == HandleType.TypeReference || + DefaultValue.HandleType == HandleType.TypeSpecification || + DefaultValue.HandleType == HandleType.ConstantBooleanArray || + DefaultValue.HandleType == HandleType.ConstantBooleanValue || + DefaultValue.HandleType == HandleType.ConstantByteArray || + DefaultValue.HandleType == HandleType.ConstantByteValue || + DefaultValue.HandleType == HandleType.ConstantCharArray || + DefaultValue.HandleType == HandleType.ConstantCharValue || + DefaultValue.HandleType == HandleType.ConstantDoubleArray || + DefaultValue.HandleType == HandleType.ConstantDoubleValue || + DefaultValue.HandleType == HandleType.ConstantEnumArray || + DefaultValue.HandleType == HandleType.ConstantHandleArray || + DefaultValue.HandleType == HandleType.ConstantInt16Array || + DefaultValue.HandleType == HandleType.ConstantInt16Value || + DefaultValue.HandleType == HandleType.ConstantInt32Array || + DefaultValue.HandleType == HandleType.ConstantInt32Value || + DefaultValue.HandleType == HandleType.ConstantInt64Array || + DefaultValue.HandleType == HandleType.ConstantInt64Value || + DefaultValue.HandleType == HandleType.ConstantReferenceValue || + DefaultValue.HandleType == HandleType.ConstantSByteArray || + DefaultValue.HandleType == HandleType.ConstantSByteValue || + DefaultValue.HandleType == HandleType.ConstantSingleArray || + DefaultValue.HandleType == HandleType.ConstantSingleValue || + DefaultValue.HandleType == HandleType.ConstantStringArray || + DefaultValue.HandleType == HandleType.ConstantStringValue || + DefaultValue.HandleType == HandleType.ConstantUInt16Array || + DefaultValue.HandleType == HandleType.ConstantUInt16Value || + DefaultValue.HandleType == HandleType.ConstantUInt32Array || + DefaultValue.HandleType == HandleType.ConstantUInt32Value || + DefaultValue.HandleType == HandleType.ConstantUInt64Array || + DefaultValue.HandleType == HandleType.ConstantUInt64Value); + writer.Write(DefaultValue); + writer.Write(CustomAttributes); + } // Save + + internal static ParameterHandle AsHandle(Parameter record) + { + if (record == null) + { + return new ParameterHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ParameterHandle Handle + { + get + { + return new ParameterHandle(HandleOffset); + } + } // Handle + + public ParameterAttributes Flags; + public ushort Sequence; + public ConstantStringValue Name; + public MetadataRecord DefaultValue; + public List CustomAttributes = new List(); + } // Parameter + + public partial class PointerSignature : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.PointerSignature; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Type = visitor.Visit(this, Type); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as PointerSignature; + if (other == null) return false; + if (!Object.Equals(Type, other.Type)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 747249584; + hash = ((hash << 13) - (hash >> 19)) ^ (Type == null ? 0 : Type.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(Type == null || + Type.HandleType == HandleType.TypeDefinition || + Type.HandleType == HandleType.TypeReference || + Type.HandleType == HandleType.TypeSpecification || + Type.HandleType == HandleType.ModifiedType); + writer.Write(Type); + } // Save + + internal static PointerSignatureHandle AsHandle(PointerSignature record) + { + if (record == null) + { + return new PointerSignatureHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new PointerSignatureHandle Handle + { + get + { + return new PointerSignatureHandle(HandleOffset); + } + } // Handle + + public MetadataRecord Type; + } // PointerSignature + + public partial class Property : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.Property; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Name = visitor.Visit(this, Name); + Signature = visitor.Visit(this, Signature); + MethodSemantics = visitor.Visit(this, MethodSemantics); + DefaultValue = visitor.Visit(this, DefaultValue); + CustomAttributes = visitor.Visit(this, CustomAttributes); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as Property; + if (other == null) return false; + if (Flags != other.Flags) return false; + if (!Object.Equals(Name, other.Name)) return false; + if (!Object.Equals(Signature, other.Signature)) return false; + if (!MethodSemantics.SequenceEqual(other.MethodSemantics)) return false; + if (!Object.Equals(DefaultValue, other.DefaultValue)) return false; + if (!CustomAttributes.SequenceEqual(other.CustomAttributes)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1324612544; + hash = ((hash << 13) - (hash >> 19)) ^ Flags.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Signature == null ? 0 : Signature.GetHashCode()); + if (MethodSemantics != null) + { + for (int i = 0; i < MethodSemantics.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (MethodSemantics[i] == null ? 0 : MethodSemantics[i].GetHashCode()); + } + } + hash = ((hash << 13) - (hash >> 19)) ^ (DefaultValue == null ? 0 : DefaultValue.GetHashCode()); + if (CustomAttributes != null) + { + for (int i = 0; i < CustomAttributes.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (CustomAttributes[i] == null ? 0 : CustomAttributes[i].GetHashCode()); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Flags); + writer.Write(Name); + writer.Write(Signature); + writer.Write(MethodSemantics); + Debug.Assert(DefaultValue == null || + DefaultValue.HandleType == HandleType.TypeDefinition || + DefaultValue.HandleType == HandleType.TypeReference || + DefaultValue.HandleType == HandleType.TypeSpecification || + DefaultValue.HandleType == HandleType.ConstantBooleanArray || + DefaultValue.HandleType == HandleType.ConstantBooleanValue || + DefaultValue.HandleType == HandleType.ConstantByteArray || + DefaultValue.HandleType == HandleType.ConstantByteValue || + DefaultValue.HandleType == HandleType.ConstantCharArray || + DefaultValue.HandleType == HandleType.ConstantCharValue || + DefaultValue.HandleType == HandleType.ConstantDoubleArray || + DefaultValue.HandleType == HandleType.ConstantDoubleValue || + DefaultValue.HandleType == HandleType.ConstantEnumArray || + DefaultValue.HandleType == HandleType.ConstantHandleArray || + DefaultValue.HandleType == HandleType.ConstantInt16Array || + DefaultValue.HandleType == HandleType.ConstantInt16Value || + DefaultValue.HandleType == HandleType.ConstantInt32Array || + DefaultValue.HandleType == HandleType.ConstantInt32Value || + DefaultValue.HandleType == HandleType.ConstantInt64Array || + DefaultValue.HandleType == HandleType.ConstantInt64Value || + DefaultValue.HandleType == HandleType.ConstantReferenceValue || + DefaultValue.HandleType == HandleType.ConstantSByteArray || + DefaultValue.HandleType == HandleType.ConstantSByteValue || + DefaultValue.HandleType == HandleType.ConstantSingleArray || + DefaultValue.HandleType == HandleType.ConstantSingleValue || + DefaultValue.HandleType == HandleType.ConstantStringArray || + DefaultValue.HandleType == HandleType.ConstantStringValue || + DefaultValue.HandleType == HandleType.ConstantUInt16Array || + DefaultValue.HandleType == HandleType.ConstantUInt16Value || + DefaultValue.HandleType == HandleType.ConstantUInt32Array || + DefaultValue.HandleType == HandleType.ConstantUInt32Value || + DefaultValue.HandleType == HandleType.ConstantUInt64Array || + DefaultValue.HandleType == HandleType.ConstantUInt64Value); + writer.Write(DefaultValue); + writer.Write(CustomAttributes); + } // Save + + internal static PropertyHandle AsHandle(Property record) + { + if (record == null) + { + return new PropertyHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new PropertyHandle Handle + { + get + { + return new PropertyHandle(HandleOffset); + } + } // Handle + + public PropertyAttributes Flags; + public ConstantStringValue Name; + public PropertySignature Signature; + public List MethodSemantics = new List(); + public MetadataRecord DefaultValue; + public List CustomAttributes = new List(); + } // Property + + public partial class PropertySignature : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.PropertySignature; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Type = visitor.Visit(this, Type); + Parameters = visitor.Visit(this, Parameters); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as PropertySignature; + if (other == null) return false; + if (CallingConvention != other.CallingConvention) return false; + if (!Object.Equals(Type, other.Type)) return false; + if (!Parameters.SequenceEqual(other.Parameters)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1535652143; + hash = ((hash << 13) - (hash >> 19)) ^ CallingConvention.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (Type == null ? 0 : Type.GetHashCode()); + if (Parameters != null) + { + for (int i = 0; i < Parameters.Count; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ (Parameters[i] == null ? 0 : Parameters[i].GetHashCode()); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(CallingConvention); + Debug.Assert(Type == null || + Type.HandleType == HandleType.TypeDefinition || + Type.HandleType == HandleType.TypeReference || + Type.HandleType == HandleType.TypeSpecification || + Type.HandleType == HandleType.ModifiedType); + writer.Write(Type); + Debug.Assert(Parameters.TrueForAll(handle => handle == null || + handle.HandleType == HandleType.TypeDefinition || + handle.HandleType == HandleType.TypeReference || + handle.HandleType == HandleType.TypeSpecification || + handle.HandleType == HandleType.ModifiedType)); + writer.Write(Parameters); + } // Save + + internal static PropertySignatureHandle AsHandle(PropertySignature record) + { + if (record == null) + { + return new PropertySignatureHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new PropertySignatureHandle Handle + { + get + { + return new PropertySignatureHandle(HandleOffset); + } + } // Handle + + public CallingConventions CallingConvention; + public MetadataRecord Type; + public List Parameters = new List(); + } // PropertySignature + + public partial class QualifiedField : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.QualifiedField; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Field = visitor.Visit(this, Field); + EnclosingType = visitor.Visit(this, EnclosingType); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as QualifiedField; + if (other == null) return false; + if (!Object.Equals(Field, other.Field)) return false; + if (!Object.Equals(EnclosingType, other.EnclosingType)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 1470437688; + hash = ((hash << 13) - (hash >> 19)) ^ (Field == null ? 0 : Field.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (EnclosingType == null ? 0 : EnclosingType.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Field); + writer.Write(EnclosingType); + } // Save + + internal static QualifiedFieldHandle AsHandle(QualifiedField record) + { + if (record == null) + { + return new QualifiedFieldHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new QualifiedFieldHandle Handle + { + get + { + return new QualifiedFieldHandle(HandleOffset); + } + } // Handle + + public Field Field; + public TypeDefinition EnclosingType; + } // QualifiedField + + public partial class QualifiedMethod : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.QualifiedMethod; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Method = visitor.Visit(this, Method); + EnclosingType = visitor.Visit(this, EnclosingType); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as QualifiedMethod; + if (other == null) return false; + if (!Object.Equals(Method, other.Method)) return false; + if (!Object.Equals(EnclosingType, other.EnclosingType)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -620024567; + hash = ((hash << 13) - (hash >> 19)) ^ (Method == null ? 0 : Method.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (EnclosingType == null ? 0 : EnclosingType.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Method); + writer.Write(EnclosingType); + } // Save + + internal static QualifiedMethodHandle AsHandle(QualifiedMethod record) + { + if (record == null) + { + return new QualifiedMethodHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new QualifiedMethodHandle Handle + { + get + { + return new QualifiedMethodHandle(HandleOffset); + } + } // Handle + + public Method Method; + public TypeDefinition EnclosingType; + } // QualifiedMethod + + public partial class SZArraySignature : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.SZArraySignature; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + ElementType = visitor.Visit(this, ElementType); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as SZArraySignature; + if (other == null) return false; + if (!Object.Equals(ElementType, other.ElementType)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -179535243; + hash = ((hash << 13) - (hash >> 19)) ^ (ElementType == null ? 0 : ElementType.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(ElementType == null || + ElementType.HandleType == HandleType.TypeDefinition || + ElementType.HandleType == HandleType.TypeReference || + ElementType.HandleType == HandleType.TypeSpecification || + ElementType.HandleType == HandleType.ModifiedType); + writer.Write(ElementType); + } // Save + + internal static SZArraySignatureHandle AsHandle(SZArraySignature record) + { + if (record == null) + { + return new SZArraySignatureHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new SZArraySignatureHandle Handle + { + get + { + return new SZArraySignatureHandle(HandleOffset); + } + } // Handle + + public MetadataRecord ElementType; + } // SZArraySignature + + public partial class ScopeDefinition : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ScopeDefinition; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Name = visitor.Visit(this, Name); + Culture = visitor.Visit(this, Culture); + RootNamespaceDefinition = visitor.Visit(this, RootNamespaceDefinition); + EntryPoint = visitor.Visit(this, EntryPoint); + GlobalModuleType = visitor.Visit(this, GlobalModuleType); + CustomAttributes = visitor.Visit(this, CustomAttributes); + ModuleName = visitor.Visit(this, ModuleName); + ModuleCustomAttributes = visitor.Visit(this, ModuleCustomAttributes); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ScopeDefinition; + if (other == null) return false; + if (Flags != other.Flags) return false; + if (!Object.Equals(Name, other.Name)) return false; + if (HashAlgorithm != other.HashAlgorithm) return false; + if (MajorVersion != other.MajorVersion) return false; + if (MinorVersion != other.MinorVersion) return false; + if (BuildNumber != other.BuildNumber) return false; + if (RevisionNumber != other.RevisionNumber) return false; + if (!PublicKey.SequenceEqual(other.PublicKey)) return false; + if (!Object.Equals(Culture, other.Culture)) return false; + if (!Object.Equals(ModuleName, other.ModuleName)) return false; + if (!Mvid.SequenceEqual(other.Mvid)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 1490364984; + hash = ((hash << 13) - (hash >> 19)) ^ Flags.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ HashAlgorithm.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ MajorVersion.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ MinorVersion.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ BuildNumber.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ RevisionNumber.GetHashCode(); + if (PublicKey != null) + { + for (int i = 0; i < PublicKey.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ PublicKey[i].GetHashCode(); + } + } + hash = ((hash << 13) - (hash >> 19)) ^ (Culture == null ? 0 : Culture.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (ModuleName == null ? 0 : ModuleName.GetHashCode()); + if (Mvid != null) + { + for (int i = 0; i < Mvid.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ Mvid[i].GetHashCode(); + } + } + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Flags); + writer.Write(Name); + writer.Write(HashAlgorithm); + writer.Write(MajorVersion); + writer.Write(MinorVersion); + writer.Write(BuildNumber); + writer.Write(RevisionNumber); + writer.Write(PublicKey); + writer.Write(Culture); + writer.Write(RootNamespaceDefinition); + writer.Write(EntryPoint); + writer.Write(GlobalModuleType); + writer.Write(CustomAttributes); + writer.Write(ModuleName); + writer.Write(Mvid); + writer.Write(ModuleCustomAttributes); + } // Save + + internal static ScopeDefinitionHandle AsHandle(ScopeDefinition record) + { + if (record == null) + { + return new ScopeDefinitionHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ScopeDefinitionHandle Handle + { + get + { + return new ScopeDefinitionHandle(HandleOffset); + } + } // Handle + + public AssemblyFlags Flags; + public ConstantStringValue Name; + public AssemblyHashAlgorithm HashAlgorithm; + public ushort MajorVersion; + public ushort MinorVersion; + public ushort BuildNumber; + public ushort RevisionNumber; + public Byte[] PublicKey; + public ConstantStringValue Culture; + public NamespaceDefinition RootNamespaceDefinition; + public QualifiedMethod EntryPoint; + public TypeDefinition GlobalModuleType; + public List CustomAttributes = new List(); + public ConstantStringValue ModuleName; + public Byte[] Mvid; + public List ModuleCustomAttributes = new List(); + } // ScopeDefinition + + public partial class ScopeReference : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.ScopeReference; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Name = visitor.Visit(this, Name); + Culture = visitor.Visit(this, Culture); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as ScopeReference; + if (other == null) return false; + if (Flags != other.Flags) return false; + if (!Object.Equals(Name, other.Name)) return false; + if (MajorVersion != other.MajorVersion) return false; + if (MinorVersion != other.MinorVersion) return false; + if (BuildNumber != other.BuildNumber) return false; + if (RevisionNumber != other.RevisionNumber) return false; + if (!PublicKeyOrToken.SequenceEqual(other.PublicKeyOrToken)) return false; + if (!Object.Equals(Culture, other.Culture)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 2056651797; + hash = ((hash << 13) - (hash >> 19)) ^ Flags.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ MajorVersion.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ MinorVersion.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ BuildNumber.GetHashCode(); + hash = ((hash << 13) - (hash >> 19)) ^ RevisionNumber.GetHashCode(); + if (PublicKeyOrToken != null) + { + for (int i = 0; i < PublicKeyOrToken.Length; i++) + { + hash = ((hash << 13) - (hash >> 19)) ^ PublicKeyOrToken[i].GetHashCode(); + } + } + hash = ((hash << 13) - (hash >> 19)) ^ (Culture == null ? 0 : Culture.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Flags); + writer.Write(Name); + writer.Write(MajorVersion); + writer.Write(MinorVersion); + writer.Write(BuildNumber); + writer.Write(RevisionNumber); + writer.Write(PublicKeyOrToken); + writer.Write(Culture); + } // Save + + internal static ScopeReferenceHandle AsHandle(ScopeReference record) + { + if (record == null) + { + return new ScopeReferenceHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new ScopeReferenceHandle Handle + { + get + { + return new ScopeReferenceHandle(HandleOffset); + } + } // Handle + + public AssemblyFlags Flags; + public ConstantStringValue Name; + public ushort MajorVersion; + public ushort MinorVersion; + public ushort BuildNumber; + public ushort RevisionNumber; + public Byte[] PublicKeyOrToken; + public ConstantStringValue Culture; + } // ScopeReference + + public partial class TypeDefinition : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.TypeDefinition; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + BaseType = visitor.Visit(this, BaseType); + NamespaceDefinition = visitor.Visit(this, NamespaceDefinition); + Name = visitor.Visit(this, Name); + EnclosingType = visitor.Visit(this, EnclosingType); + NestedTypes = visitor.Visit(this, NestedTypes); + Methods = visitor.Visit(this, Methods); + Fields = visitor.Visit(this, Fields); + Properties = visitor.Visit(this, Properties); + Events = visitor.Visit(this, Events); + GenericParameters = visitor.Visit(this, GenericParameters); + Interfaces = visitor.Visit(this, Interfaces); + CustomAttributes = visitor.Visit(this, CustomAttributes); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as TypeDefinition; + if (other == null) return false; + if (!Object.Equals(NamespaceDefinition, other.NamespaceDefinition)) return false; + if (!Object.Equals(Name, other.Name)) return false; + if (!Object.Equals(EnclosingType, other.EnclosingType)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -1095947977; + hash = ((hash << 13) - (hash >> 19)) ^ (NamespaceDefinition == null ? 0 : NamespaceDefinition.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (EnclosingType == null ? 0 : EnclosingType.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Flags); + Debug.Assert(BaseType == null || + BaseType.HandleType == HandleType.TypeDefinition || + BaseType.HandleType == HandleType.TypeReference || + BaseType.HandleType == HandleType.TypeSpecification); + writer.Write(BaseType); + writer.Write(NamespaceDefinition); + writer.Write(Name); + writer.Write(Size); + writer.Write(PackingSize); + writer.Write(EnclosingType); + writer.Write(NestedTypes); + writer.Write(Methods); + writer.Write(Fields); + writer.Write(Properties); + writer.Write(Events); + writer.Write(GenericParameters); + Debug.Assert(Interfaces.TrueForAll(handle => handle == null || + handle.HandleType == HandleType.TypeDefinition || + handle.HandleType == HandleType.TypeReference || + handle.HandleType == HandleType.TypeSpecification)); + writer.Write(Interfaces); + writer.Write(CustomAttributes); + } // Save + + internal static TypeDefinitionHandle AsHandle(TypeDefinition record) + { + if (record == null) + { + return new TypeDefinitionHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new TypeDefinitionHandle Handle + { + get + { + return new TypeDefinitionHandle(HandleOffset); + } + } // Handle + + public TypeAttributes Flags; + public MetadataRecord BaseType; + public NamespaceDefinition NamespaceDefinition; + public ConstantStringValue Name; + public uint Size; + public ushort PackingSize; + public TypeDefinition EnclosingType; + public List NestedTypes = new List(); + public List Methods = new List(); + public List Fields = new List(); + public List Properties = new List(); + public List Events = new List(); + public List GenericParameters = new List(); + public List Interfaces = new List(); + public List CustomAttributes = new List(); + } // TypeDefinition + + public partial class TypeForwarder : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.TypeForwarder; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Scope = visitor.Visit(this, Scope); + Name = visitor.Visit(this, Name); + NestedTypes = visitor.Visit(this, NestedTypes); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as TypeForwarder; + if (other == null) return false; + if (!Object.Equals(Scope, other.Scope)) return false; + if (!Object.Equals(Name, other.Name)) return false; + if (!NestedTypes.SequenceEqual(other.NestedTypes)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 467167184; + hash = ((hash << 13) - (hash >> 19)) ^ (Scope == null ? 0 : Scope.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (Name == null ? 0 : Name.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Scope); + writer.Write(Name); + writer.Write(NestedTypes); + } // Save + + internal static TypeForwarderHandle AsHandle(TypeForwarder record) + { + if (record == null) + { + return new TypeForwarderHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new TypeForwarderHandle Handle + { + get + { + return new TypeForwarderHandle(HandleOffset); + } + } // Handle + + public ScopeReference Scope; + public ConstantStringValue Name; + public List NestedTypes = new List(); + } // TypeForwarder + + public partial class TypeInstantiationSignature : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.TypeInstantiationSignature; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + GenericType = visitor.Visit(this, GenericType); + GenericTypeArguments = visitor.Visit(this, GenericTypeArguments); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as TypeInstantiationSignature; + if (other == null) return false; + if (!Object.Equals(GenericType, other.GenericType)) return false; + if (!GenericTypeArguments.SequenceEqual(other.GenericTypeArguments)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 770132338; + hash = ((hash << 13) - (hash >> 19)) ^ (GenericType == null ? 0 : GenericType.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(GenericType == null || + GenericType.HandleType == HandleType.TypeDefinition || + GenericType.HandleType == HandleType.TypeReference || + GenericType.HandleType == HandleType.TypeSpecification); + writer.Write(GenericType); + Debug.Assert(GenericTypeArguments.TrueForAll(handle => handle == null || + handle.HandleType == HandleType.TypeDefinition || + handle.HandleType == HandleType.TypeReference || + handle.HandleType == HandleType.TypeSpecification)); + writer.Write(GenericTypeArguments); + } // Save + + internal static TypeInstantiationSignatureHandle AsHandle(TypeInstantiationSignature record) + { + if (record == null) + { + return new TypeInstantiationSignatureHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new TypeInstantiationSignatureHandle Handle + { + get + { + return new TypeInstantiationSignatureHandle(HandleOffset); + } + } // Handle + + public MetadataRecord GenericType; + public List GenericTypeArguments = new List(); + } // TypeInstantiationSignature + + public partial class TypeReference : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.TypeReference; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + ParentNamespaceOrType = visitor.Visit(this, ParentNamespaceOrType); + TypeName = visitor.Visit(this, TypeName); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as TypeReference; + if (other == null) return false; + if (!Object.Equals(ParentNamespaceOrType, other.ParentNamespaceOrType)) return false; + if (!Object.Equals(TypeName, other.TypeName)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -540108450; + hash = ((hash << 13) - (hash >> 19)) ^ (ParentNamespaceOrType == null ? 0 : ParentNamespaceOrType.GetHashCode()); + hash = ((hash << 13) - (hash >> 19)) ^ (TypeName == null ? 0 : TypeName.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(ParentNamespaceOrType == null || + ParentNamespaceOrType.HandleType == HandleType.NamespaceReference || + ParentNamespaceOrType.HandleType == HandleType.TypeReference); + writer.Write(ParentNamespaceOrType); + writer.Write(TypeName); + } // Save + + internal static TypeReferenceHandle AsHandle(TypeReference record) + { + if (record == null) + { + return new TypeReferenceHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new TypeReferenceHandle Handle + { + get + { + return new TypeReferenceHandle(HandleOffset); + } + } // Handle + + public MetadataRecord ParentNamespaceOrType; + public ConstantStringValue TypeName; + } // TypeReference + + public partial class TypeSpecification : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.TypeSpecification; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + Signature = visitor.Visit(this, Signature); + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as TypeSpecification; + if (other == null) return false; + if (!Object.Equals(Signature, other.Signature)) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = -902636182; + hash = ((hash << 13) - (hash >> 19)) ^ (Signature == null ? 0 : Signature.GetHashCode()); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + Debug.Assert(Signature == null || + Signature.HandleType == HandleType.TypeDefinition || + Signature.HandleType == HandleType.TypeReference || + Signature.HandleType == HandleType.TypeInstantiationSignature || + Signature.HandleType == HandleType.SZArraySignature || + Signature.HandleType == HandleType.ArraySignature || + Signature.HandleType == HandleType.PointerSignature || + Signature.HandleType == HandleType.FunctionPointerSignature || + Signature.HandleType == HandleType.ByReferenceSignature || + Signature.HandleType == HandleType.TypeVariableSignature || + Signature.HandleType == HandleType.MethodTypeVariableSignature); + writer.Write(Signature); + } // Save + + internal static TypeSpecificationHandle AsHandle(TypeSpecification record) + { + if (record == null) + { + return new TypeSpecificationHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new TypeSpecificationHandle Handle + { + get + { + return new TypeSpecificationHandle(HandleOffset); + } + } // Handle + + public MetadataRecord Signature; + } // TypeSpecification + + public partial class TypeVariableSignature : MetadataRecord + { + public override HandleType HandleType + { + get + { + return HandleType.TypeVariableSignature; + } + } // HandleType + + internal override void Visit(IRecordVisitor visitor) + { + } // Visit + + public override sealed bool Equals(Object obj) + { + if (Object.ReferenceEquals(this, obj)) return true; + var other = obj as TypeVariableSignature; + if (other == null) return false; + if (Number != other.Number) return false; + return true; + } // Equals + + public override sealed int GetHashCode() + { + if (_hash != 0) + return _hash; + EnterGetHashCode(); + int hash = 711693641; + hash = ((hash << 13) - (hash >> 19)) ^ Number.GetHashCode(); + LeaveGetHashCode(); + _hash = hash; + return _hash; + } // GetHashCode + + internal override void Save(NativeWriter writer) + { + writer.Write(Number); + } // Save + + internal static TypeVariableSignatureHandle AsHandle(TypeVariableSignature record) + { + if (record == null) + { + return new TypeVariableSignatureHandle(0); + } + else + { + return record.Handle; + } + } // AsHandle + + internal new TypeVariableSignatureHandle Handle + { + get + { + return new TypeVariableSignatureHandle(HandleOffset); + } + } // Handle + + public int Number; + } // TypeVariableSignature +} // Internal.Metadata.NativeFormat.Writer diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/NativeMetadataWriter.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/NativeMetadataWriter.cs new file mode 100644 index 00000000000000..01bef848d2b36b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/Internal/Metadata/NativeFormat/Writer/NativeMetadataWriter.cs @@ -0,0 +1,1119 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Collections.Generic; +using System.Reflection; +using Debug = System.Diagnostics.Debug; +using ConditionalAttribute = System.Diagnostics.ConditionalAttribute; +using HandleType = Internal.Metadata.NativeFormat.HandleType; +using Internal.LowLevelLinq; +using Internal.NativeFormat; + +namespace Internal.Metadata.NativeFormat.Writer +{ + using Graph = AdjacencyGraph; + internal class Edge + { + public MetadataRecord Source; + public MetadataRecord Target; + public Edge(MetadataRecord source, MetadataRecord target) + { + Source = source; + Target = target; + } + }; + internal class AdjacencyGraph + { + private HashSet _vertices = new HashSet(); + // private Dictionary> _edges = new Dictionary>(); + + public void AddVertex(MetadataRecord v) + { + _vertices.Add(v); + } + +#if false + public void AddEdge(Edge e) + { + HashSet vedges; + if (!_edges.TryGetValue(e.Source, out vedges)) + { + vedges = new HashSet(); + _edges.Add(e.Source, vedges); + } + vedges.Add(e); + } +#endif + + public bool ContainsVertex(MetadataRecord v) + { + return _vertices.Contains(v); + } + + public IEnumerable Vertices + { get { return _vertices; } } + } + + internal partial interface IRecordVisitor + { + // Adds edge + DstT Visit(SrcT src, DstT dst) + where SrcT : MetadataRecord + where DstT : MetadataRecord; + + // Adds grouped edges + Dictionary Visit(SrcT src, IEnumerable> dst) + where SrcT : MetadataRecord + where DstT : MetadataRecord; + + // Adds grouped edges + List Visit(SrcT src, List dst) + where SrcT : MetadataRecord + where DstT : MetadataRecord; + } + + internal class SourceVertex : MetadataRecord + { + public override HandleType HandleType + { + get { throw new NotImplementedException(); } + } + + internal override void Save(NativeWriter writer) + { + throw new NotImplementedException(); + } + + internal override void Visit(IRecordVisitor visitor) + { + throw new NotImplementedException(); + } + } + + internal abstract class RecordVisitorBase : IRecordVisitor + { +#if false + private class SequenceComparer + : IEqualityComparer> + { + public bool Equals(IEnumerable x, IEnumerable y) + { + return + Object.ReferenceEquals(x, y) || + (x != null && + y != null && + x.GetType() == y.GetType() && + x.SequenceEqual(y)); + } + + public static int GetHashCode(T o) + { + if (o == null) + return 0; + return o.GetHashCode(); + } + + public int GetHashCode(IEnumerable obj) + { + if (obj == null) + return 0; + return obj.Aggregate(0, (h, o) => h ^ GetHashCode(o)); + } + } + + Dictionary, object> _listPool = new Dictionary, object>(new SequenceComparer()); + + private List GetPooledArray(List rec) where T : MetadataRecord + { + if (rec == null || rec.Count() == 0) + return rec; + + object pooledRecord; + if (_listPool.TryGetValue(rec, out pooledRecord) && pooledRecord != rec) + { + if (rec.GetType().GetElementType() == typeof(MetadataRecord)) + _stats.ArraySizeSavings += rec.Count() * sizeof(int) - 3; + else + _stats.ArraySizeSavings += rec.Count() * 3 - 3; + Debug.Assert(rec.GetType() == pooledRecord.GetType()); + rec = (List)pooledRecord; + } + else + { + _listPool[rec] = rec; + } + return rec; + } + + private struct Stats + { + public int ArraySizeSavings; + } + private Stats _stats = new Stats(); +#endif + + private Dictionary _recordPool = new Dictionary(); + + public RecordVisitorBase() + { + _graph.AddVertex(MetaSourceVertex); + } + + internal T MapToPooledRecord(T rec) where T : MetadataRecord + { + return (T)_recordPool[rec]; + } + + private T GetPooledRecord(T rec) where T : MetadataRecord + { + if (rec == null) + return rec; + + MetadataRecord pooledRecord; + if (_recordPool.TryGetValue(rec, out pooledRecord) && pooledRecord != rec) + { + Debug.Assert(rec.GetType() == pooledRecord.GetType()); + rec = (T)pooledRecord; + } + else + { + _recordPool[rec] = rec; + } + return rec; + } + + // Adds a Vertex + public T Visit(T rec) + where T : MetadataRecord + { + rec = GetPooledRecord(rec); + + if (rec == null) + return rec; + + if (_graph.ContainsVertex(rec)) + return rec; + + _graph.AddVertex(rec); + _queue.Enqueue(rec); + + return rec; + } + + // Adds Edges + public Dictionary Visit(SrcT src, IEnumerable> dst) + where SrcT : MetadataRecord + where DstT : MetadataRecord + { + var res = new Dictionary(); + + foreach (var kv in dst) + { + res.Add(kv.Key, Visit(src, kv.Value, true)); + } + + return res; + } + + public void Run(IEnumerable records) + { + foreach (var rec in records) + { + Visit((MetadataRecord)null, rec); + } + + while (_queue.Count != 0) + { + _queue.Dequeue().Visit(this); + } + } + + // Adds Edges + public List Visit(SrcT src, List dst) + where SrcT : MetadataRecord + where DstT : MetadataRecord + { +#if false + return GetPooledArray(dst.Select(d => Visit(src, d, true)).ToList()); +#else + var result = new List(dst.Count); + foreach (var destNode in dst) + result.Add(Visit(src, destNode, true)); + + return result; +#endif + } + + // Adds Edge + public DstT Visit(SrcT src, DstT dst) + where SrcT : MetadataRecord + where DstT : MetadataRecord + { + return Visit(src, dst, src == null); + } + + // Adds Edge + internal DstT Visit(SrcT src, DstT dst, bool isChild) + where SrcT : MetadataRecord + where DstT : MetadataRecord + { + var res = Visit(dst); + +#if false + if (res != null) + { + _graph.AddEdge(new Edge(src ?? MetaSourceVertex, res)); + } +#endif + + return res; + } + + protected Queue _queue = new Queue(); + protected Graph _graph = new Graph(); + + public Graph Graph { get { return _graph; } } + public readonly MetadataRecord MetaSourceVertex = new SourceVertex(); + } + + internal class RecordVisitor : RecordVisitorBase + { + } + + + internal partial class MetadataHeader : MetadataRecord + { + public const uint Signature = 0xDEADDFFD; + public List ScopeDefinitions = new List(); + + internal override void Save(NativeWriter writer) + { + writer.WriteUInt32(Signature); + writer.Write(ScopeDefinitions); + } + + public override HandleType HandleType + { + get { throw new NotImplementedException(); } + } + + internal override void Visit(IRecordVisitor visitor) + { + ScopeDefinitions = visitor.Visit(this, ScopeDefinitions); + } + } + + public partial class MetadataWriter + { + internal MetadataHeader _metadataHeader = new MetadataHeader(); + + public List AdditionalRootRecords { get; private set; } + + public List ScopeDefinitions + { + get { return _metadataHeader.ScopeDefinitions; } + } + + private RecordVisitor _visitor = null; + + public MetadataWriter() + { + AdditionalRootRecords = new List(); + } + + public int GetRecordHandle(MetadataRecord rec) + { + var realRec = _visitor.MapToPooledRecord(rec); + + Debug.Assert(realRec.Handle.Offset != 0); + + return realRec.Handle._value; + } + + public void Write(Stream stream) + { + _visitor = new RecordVisitor(); + + _visitor.Run(ScopeDefinitions.AsEnumerable()); + _visitor.Run(AdditionalRootRecords.AsEnumerable()); + + IEnumerable records = _visitor.Graph.Vertices.Where(v => v != _visitor.MetaSourceVertex); + + var writer = new NativeWriter(); + + var section = writer.NewSection(); + + _metadataHeader.ScopeDefinitions = ScopeDefinitions; + section.Place(_metadataHeader); + + foreach (var rec in records) + { + section.Place(rec); + } + + writer.Save(stream); + + if (LogWriter != null) + { + // Create a CSV file, one line per meta-data record. + LogWriter.WriteLine("Handle, Kind, Name, Children"); + // needed to enumerate children of a meta-data record + var childVisitor = new WriteChildrenVisitor(LogWriter); + + foreach (var rec in records) + { + // First the metadata handle + LogWriter.Write(rec.Handle._value.ToString("x8")); + LogWriter.Write(", "); + + // Next the handle type + LogWriter.Write(rec.HandleType.ToString()); + LogWriter.Write(", "); + + // 3rd, the name, Quote the string if not already quoted + string asString = rec.ToString(false); + bool alreadyQuoted = asString.StartsWith("\"") && asString.EndsWith("\""); + if (!alreadyQuoted) + { + LogWriter.Write("\""); + asString = asString.Replace("\\", "\\\\").Replace("\"", "\\\""); // Quote " and \ + } + // TODO we assume that a quoted string is escaped properly + LogWriter.Write(asString); + + if (!alreadyQuoted) + LogWriter.Write("\""); + LogWriter.Write(", "); + + // Finally write out the handle IDs for my children + LogWriter.Write("\""); + childVisitor.Reset(); + rec.Visit(childVisitor); + LogWriter.Write("\""); + LogWriter.WriteLine(); + } + LogWriter.Flush(); + } + } + + // WriteChildrenVisitor is a helper class needed to write out the list of the + // handles (as space separated hex numbers) of all children of a given node + // to the 'logWriter' text stream. It simply implementes the IRecordVisitor + // interface to hook the callbacks needed for the MetadataRecord.Visit API. + // It is only used in the Write() method above. + private class WriteChildrenVisitor : IRecordVisitor + { + public WriteChildrenVisitor(TextWriter logWriter) + { + _logWriter = logWriter; + } + + // Resets the state back to what is was just after the constructor is called. + public void Reset() { _notFirst = false; } + + // All visits come to here for every child. Here we simply print the handle as hex. + public void Log(MetadataRecord rec) + { + if (rec == null) + return; + if (_notFirst) + _logWriter.Write(" "); + else + _notFirst = true; + _logWriter.Write(rec.Handle._value.ToString("x")); + } + + public DstT Visit(SrcT src, DstT dst) where SrcT : MetadataRecord where DstT : MetadataRecord + { + Log(dst); + return dst; + } + public Dictionary Visit(SrcT src, IEnumerable> dst) where SrcT : MetadataRecord where DstT : MetadataRecord + { + foreach (var keyValue in dst) + Log(keyValue.Value); + return dst as Dictionary; + } + public List Visit(SrcT src, List dst) where SrcT : MetadataRecord where DstT : MetadataRecord + { + foreach (var elem in dst) + Log(elem); + return dst.ToList(); + } + + private bool _notFirst; // The first child should not have a space before it. This tracks this + private TextWriter _logWriter; // Where we write output to + } + + public TextWriter LogWriter = null; + } + + internal class ReentrancyGuardStack + { + private MetadataRecord[] _array; + private int _size; + + public ReentrancyGuardStack() + { + // Start with a non-zero initial size. With a bit of luck this will prevent memory allocations + // when Push() is used. + _array = new MetadataRecord[8]; + _size = 0; + } + + public bool Contains(MetadataRecord item) + { + int count = _size; + while (count-- > 0) + { + // Important: we use ReferenceEquals because this method will be called from Equals() + // on 'record'. This is also why we can't use System.Collections.Generic.Stack. + if (Object.ReferenceEquals(item, _array[count])) + return true; + } + return false; + } + + public MetadataRecord Pop() + { + if (_size == 0) + throw new InvalidOperationException(); + MetadataRecord record = _array[--_size]; + _array[_size] = null; + return record; + } + + public void Push(MetadataRecord item) + { + if (_size == _array.Length) + Array.Resize(ref _array, 2 * _array.Length); + _array[_size++] = item; + } + } + + public abstract class MetadataRecord : Vertex + { + protected int _hash = 0; + + // debug-only guard against reentrancy in GetHashCode() + private bool _gettingHashCode = false; + + [Conditional("DEBUG")] + protected void EnterGetHashCode() + { + Debug.Assert(!_gettingHashCode); + _gettingHashCode = true; + } + + [Conditional("DEBUG")] + protected void LeaveGetHashCode() + { + Debug.Assert(_gettingHashCode); + _gettingHashCode = false; + } + + public abstract HandleType HandleType { get; } + + internal int HandleOffset + { + get + { + return _offset & 0x00FFFFFF; + } + } + + internal Handle Handle + { + get + { + return new Handle(HandleType, HandleOffset); + } + } + + internal abstract void Visit(IRecordVisitor visitor); + + public override string ToString() + { + return "[@TODO:" + this.GetType().ToString() + "]"; + } + + public virtual string ToString(bool includeHandleValue) + { + return ToString(); + } + + protected static string ToString(IEnumerable arr, string sep = ", ", bool includeHandleValue = false) where T : MetadataRecord + { + return String.Join(sep, arr.Select(v => v.ToString(includeHandleValue))); + } + } + + public interface ICustomAttributeMetadataRecord + { + IList GetCustomAttributes(); + } + + public abstract partial class Blob : MetadataRecord + { + } + + /// + /// Supplements generated class with convenient coversion operators + /// + public partial class ConstantStringValue + { + public static explicit operator string(ConstantStringValue value) + { + if (value == null) + return null; + else + return value.Value; + } + + public static explicit operator ConstantStringValue(string value) + { + return new ConstantStringValue() { Value = value }; + } + } + + public partial class ScopeDefinition + { + public override string ToString() + { + return ToString(true); + } + public override string ToString(bool includeHandleValue) + { + return Name.ToString() + (includeHandleValue ? String.Format(" ({0:x})", Handle._value) : ""); + } + } + + public partial class ScopeReference + { + public override string ToString() + { + return ToString(true); + } + public override string ToString(bool includeHandleValue) + { + return Name.ToString() + (includeHandleValue ? String.Format(" ({0:x})", Handle._value) : ""); + } + } + + public partial class NamespaceDefinition + { + public override string ToString() + { + return ToString(true); + } + + public override string ToString(bool includeHandleValue) + { + string str; + + if (Name != null && !String.IsNullOrEmpty(Name.Value)) + { + str = Name.Value; + } + else + { + str = string.Empty; + } + + if (includeHandleValue) + str += String.Format("({0})", Handle.ToString()); + + if (this.ParentScopeOrNamespace != null) + { + var pns = this.ParentScopeOrNamespace as NamespaceDefinition; + if (pns != null) + { + if (!String.IsNullOrEmpty(pns.ToString(false))) + str = pns.ToString(false) + '.' + str; + } + } + return str; + } + } + + public partial class NamespaceReference + { + public override string ToString() + { + return ToString(true); + } + public override string ToString(bool includeHandleValue) + { + string str; + + if (Name != null && !String.IsNullOrEmpty(Name.Value)) + { + str = Name.Value; + } + else + { + str = string.Empty; + } + + if (includeHandleValue) + str += String.Format("({0})", Handle.ToString()); + + if (this.ParentScopeOrNamespace != null) + { + var pns = this.ParentScopeOrNamespace as NamespaceReference; + if (pns != null) + { + if (!String.IsNullOrEmpty(pns.ToString(false))) + str = pns.ToString(false) + '.' + str; + } + else + { + //str = ParentScopeOrNamespace.ToString() + " : " + str; + } + } + return str; + } + } + + public partial class TypeDefinition + { + public override string ToString() + { + return ToString(false); + } + public override string ToString(bool includeHandleValue) + { + string str = null; + if (this.EnclosingType != null) + { + str = this.EnclosingType.ToString(false) + "+" + Name.Value; + if (includeHandleValue) + str += String.Format(" ({0:x})", Handle._value); + return str; + } + else if (this.NamespaceDefinition != null && this.NamespaceDefinition.Name != null) + { + str = this.NamespaceDefinition.ToString(false) + "." + Name.Value; + if (includeHandleValue) + str += String.Format(" ({0:x})", Handle._value); + return str; + } + str = Name.Value + String.Format(" ({0:x})", Handle._value); + if (includeHandleValue) + str += String.Format(" ({0:x})", Handle._value); + return str; + } + } + + public partial class TypeReference + { + public override string ToString() + { + return ToString(false); + } + public override string ToString(bool includeHandleValue) + { + String s = ""; + if (ParentNamespaceOrType is NamespaceReference) + s += ParentNamespaceOrType.ToString(false) + "."; + if (ParentNamespaceOrType is TypeReference) + s += ParentNamespaceOrType.ToString(false) + "+"; + s += TypeName.Value; + if (includeHandleValue) + s += String.Format(" ({0:x})", Handle._value); + return s; + } + } + + public partial class TypeForwarder + { + public override string ToString() + { + return this.Name.Value + " -> " + this.Scope.Name.Value; + } + } + + public partial class GenericParameter + { + public override string ToString() + { + return Kind.FlagsToString() + " " + Name.Value + "(" + Number.ToString() + ")"; + } + } + + public partial class Field + { + public override string ToString() + { + return Name.Value; + } + } + + public partial class Method + { + public override string ToString() + { + return Signature.ToString(Name.Value); + } + } + + public partial class QualifiedMethod + { + public override string ToString() + { + return EnclosingType.ToString(false) + "." + Method.ToString(); + } + } + + public partial class Property + { + public override string ToString() + { + return Name.Value; + } + } + + public partial class Event + { + public override string ToString() + { + return Name.Value; + } + } + + public partial class SZArraySignature + { + public override string ToString() + { + return ElementType.ToString() + "[]"; + } + } + + public partial class ArraySignature + { + public override string ToString() + { + return ElementType.ToString() + "[" + new String(',', Rank - 1) + "]"; + } + } + + public partial class TypeSpecification + { + public override string ToString() + { + return Signature.ToString(); + } + } + + public partial class TypeInstantiationSignature + { + public override string ToString() + { + return this.GenericType.ToString() + "<" + String.Join(", ", this.GenericTypeArguments.Select(ga => ga.ToString())) + ">"; + } + } + + /* COMPLETENESS + public partial class MethodImpl + { + public override string ToString() + { + return this.MethodDeclaration.ToString(); + } + }*/ + + public partial class MethodInstantiation + { + public override string ToString() + { + return Method.ToString() + + "(Arguments: " + + "<" + + String.Join(", ", this.GenericTypeArguments.Select(ga => ga.ToString())) + + ">"; + } + } + + public partial class ByReferenceSignature + { + public override string ToString() + { + return "ref " + Type.ToString(); + } + } + + public partial class CustomAttribute + { + public override string ToString() + { + string str = Constructor.ToString(); + str += "(" + String.Join(", ", FixedArguments.Select(fa => fa.ToString())) + + String.Join(", ", NamedArguments.Select(na => na.ToString())) + ")"; + str += "(ctor: " + Constructor.Handle.ToString(); + return str; + } + } + + public partial class NamedArgument + { + public override string ToString() + { + return Name + " = " + Value.ToString(); + } + } + + public partial class MemberReference + { + public override string ToString() + { + return Parent.ToString() + "." + Name.Value + " (Signature: " + Signature.ToString() + ")"; + } + } + + public partial class MethodSemantics + { + public override string ToString() + { + string str = Enum.GetName(typeof(MethodSemanticsAttributes), Attributes); + return str + " : " + Method.ToString(); + } + } + + public partial class MethodSignature + { + public override string ToString() + { + return ToString(" "); + } + + public string ToString(string name) + { + return String.Join(" ", new string[] { + CallingConvention.FlagsToString(), + ReturnType.ToString(false), + name + + (GenericParameterCount == 0 ? "" : "`" + GenericParameterCount.ToString()) + + "(" + String.Join(", ", Parameters.Select(p => p.ToString(false))) + + String.Join(", ", VarArgParameters.Select(p => p.ToString(false))) + ")"}.Where(e => !String.IsNullOrWhiteSpace(e))); + } + } + + public partial class PropertySignature + { + public override string ToString() + { + return String.Join(" ", Enum.GetName(typeof(CallingConventions), CallingConvention), + Type.ToString()) + "(" + ToString(Parameters) + ")"; + } + } + + public partial class FieldSignature + { + public override string ToString() + { + return Type.ToString(); + } + } + + public partial class ModifiedType + { + public override string ToString() + { + return "[" + (IsOptional ? "opt : " : "req : ") + ModifierType.ToString() + "] " + + Type.ToString(); + } + } + + public partial class TypeVariableSignature + { + public override string ToString() + { + return "!" + Number; + } + } + + public partial class MethodTypeVariableSignature + { + public override string ToString() + { + return "!!" + Number; + } + } + + public partial class Parameter + { + public override string ToString() + { + string flags = Flags.FlagsToString(); + return String.Format("{0}{1} (Seq:{2}) {3}", + flags, + Name.ToString(), + Sequence, + (DefaultValue == null ? "" : " = " + DefaultValue.ToString())); + } + } + + public partial class PointerSignature + { + public override string ToString() + { + return Type.ToString() + "*"; + } + } + + public static class EnumHelpers + { + public static string FlagsToString(this T value) where T : IConvertible + { + if (!(value is Enum)) + throw new ArgumentException(); + + var eType = value.GetType(); + var flags = ((T[])Enum.GetValues(eType)).Where( + eVal => (((IConvertible)eVal).ToInt32(null) != 0) && ((((IConvertible)value).ToInt32(null) & ((IConvertible)eVal).ToInt32(null)) == ((IConvertible)eVal).ToInt32(null))); + if (flags.Count() == 0) + return ""; + else + return "[" + String.Join(" | ", flags.Select(eVal => Enum.GetName(eType, eVal))) + "] "; + } + } + + public static class ListExtensions + { + public static T FirstOrDefault(this List list) + { + if (list.Count != 0) + return list[0]; + return default(T); + } + public static T First(this List list) where T : class + { + if (list.Count != 0) + return list[0]; + return null; + } + } + + public static partial class DictionaryExtensions + { + internal static T FirstOrDefault(this Dictionary dict) + { + if (dict.Count != 0) + foreach (var value in dict.Values) + return value; + return default(T); + } + internal static T First(this Dictionary dict) where T : class + { + if (dict.Count != 0) + foreach (var value in dict.Values) + return value; + return null; + } + + internal static IEnumerable AsSingleEnumerable(this T value) + { + yield return value; + } + } + + public static partial class SignatureHelpers + { + public static SZArraySignature AsSZArray(this MetadataRecord record) + { + return new SZArraySignature() { ElementType = record }; + } + } + + // SequenceEquals on IEnumerable is painfully slow and allocates memory. + public static class SequenceExtensions + { + public static bool SequenceEqual(this List first, List second) + { + return first.SequenceEqual(second, null); + } + + public static bool SequenceEqual(this List first, List second, IEqualityComparer comparer) + { + if (first.Count != second.Count) + { + return false; + } + + if (comparer == null) + { + comparer = EqualityComparer.Default; + } + + for (int i = 0; i < first.Count; i++) + { + if (!comparer.Equals(first[i], second[i])) + { + return false; + } + } + + return true; + } + + public static bool SequenceEqual(this T[] first, T[] second) + { + return first.SequenceEqual(second, null); + } + + public static bool SequenceEqual(this T[] first, T[] second, IEqualityComparer comparer) + { + if (first.Length != second.Length) + { + return false; + } + + if (comparer == null) + { + comparer = EqualityComparer.Default; + } + + for (int i = 0; i < first.Length; i++) + { + if (!comparer.Equals(first[i], second[i])) + { + return false; + } + } + + return true; + } + } + + // Distinguishes positive and negative zeros for float and double values + public static class CustomComparer + { + public static unsafe bool Equals(float x, float y) + { + return *(int*)&x == *(int*)&y; + } + + public static bool Equals(double x, double y) + { + return BitConverter.DoubleToInt64Bits(x) == BitConverter.DoubleToInt64Bits(y); + } + } + + public sealed class SingleComparer : IEqualityComparer + { + public static readonly SingleComparer Instance = new SingleComparer(); + + public bool Equals(float x, float y) => CustomComparer.Equals(x, y); + public int GetHashCode(float obj) => obj.GetHashCode(); + } + + public sealed class DoubleComparer : IEqualityComparer + { + public static readonly DoubleComparer Instance = new DoubleComparer(); + + public bool Equals(double x, double y) => CustomComparer.Equals(x, y); + public int GetHashCode(double obj) => obj.GetHashCode(); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index bce5efe4e0905e..8a1dc4eca0d174 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -102,6 +102,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 542565a5128c94..da50f5bbf458ed 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -970,8 +970,8 @@ private MethodWithToken ComputeMethodWithToken(MethodDesc method, ref CORINFO_RE private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken, MethodDesc methodDesc, out object context, ref TypeDesc constrainedType) { - if (methodDesc != null && (_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(methodDesc) - || (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_DevirtualizedMethod) + if (methodDesc != null && (_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(methodDesc) + || (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_DevirtualizedMethod) || methodDesc.IsPInvoke)) { if ((CorTokenType)(unchecked((uint)pResolvedToken.token) & 0xFF000000u) == CorTokenType.mdtMethodDef && @@ -1129,7 +1129,7 @@ private void setVars(CORINFO_METHOD_STRUCT_* ftn, uint cVars, NativeVarInfo* var { _debugVarInfos[i] = vars[i]; } - + // JIT gave the ownership of this to us, so need to free this. freeArray(vars); } @@ -1146,7 +1146,7 @@ private void setBoundaries(CORINFO_METHOD_STRUCT_* ftn, uint cMap, OffsetMapping { _debugLocInfos[i] = pMap[i]; } - + // JIT gave the ownership of this to us, so need to free this. freeArray(pMap); } @@ -1599,6 +1599,7 @@ private void ceeInfoGetCallInfo( if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0) { + PrepareForUseAsAFunctionPointer(targetMethod); directCall = true; } else @@ -1644,6 +1645,7 @@ private void ceeInfoGetCallInfo( // 3) JIT intrinsics - since they have pre-defined behavior devirt = targetMethod.OwningType.IsValueType || (targetMethod.OwningType.IsDelegate && targetMethod.Name == "Invoke") || + (targetMethod.OwningType.IsObject && targetMethod.Name == "GetType") || (targetMethod.IsIntrinsic && getIntrinsicID(targetMethod, null) != CorInfoIntrinsics.CORINFO_INTRINSIC_Illegal); callVirtCrossingVersionBubble = true; @@ -1823,6 +1825,17 @@ private uint getMethodAttribs(CORINFO_METHOD_STRUCT_* ftn) // OK, if the EE said we're not doing a stub dispatch then just return the kind to } + private void PrepareForUseAsAFunctionPointer(MethodDesc method) + { + foreach (TypeDesc type in method.Signature) + { + if (type.IsValueType) + { + classMustBeLoadedBeforeCodeIsRun(type); + } + } + } + private void classMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_STRUCT_* cls) { TypeDesc type = HandleToObject(cls); diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEReaderExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEReaderExtensions.cs index fd0ce63bb6be6f..97fce016e68bda 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEReaderExtensions.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEReaderExtensions.cs @@ -124,5 +124,16 @@ public static PEExportTable GetExportTable(this PEReader reader) { return PEExportTable.Parse(reader); } + + /// + /// Check whether the file is a ReadyToRun image and returns the RVA of its ReadyToRun header if positive. + /// + /// PEReader representing the executable to check for the presence of ReadyToRun header + /// RVA of the ReadyToRun header if available, 0 when not + /// true when the PEReader represents a ReadyToRun image, false otherwise + public static bool TryGetReadyToRunHeader(this PEReader reader, out int rva) + { + return reader.GetExportTable().TryGetValue("RTR_HEADER", out rva); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index a8962fa2e3c1d1..4eab54b43b8628 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -447,7 +447,7 @@ public static bool IsReadyToRunImage(PEReader peReader) if ((peReader.PEHeaders.CorHeader.Flags & CorFlags.ILLibrary) == 0) { - return TryLocateNativeReadyToRunHeader(peReader, out _); + return peReader.TryGetReadyToRunHeader(out _); } else { @@ -565,16 +565,9 @@ public IReadOnlyDictionary GetCustomMethodToRuntimeFu return customMethods; } - private static bool TryLocateNativeReadyToRunHeader(PEReader reader, out int readyToRunHeaderRVA) - { - PEExportTable exportTable = reader.GetExportTable(); - - return exportTable.TryGetValue("RTR_HEADER", out readyToRunHeaderRVA); - } - private bool TryLocateNativeReadyToRunHeader() { - _composite = TryLocateNativeReadyToRunHeader(CompositeReader, out _readyToRunHeaderRVA); + _composite = CompositeReader.TryGetReadyToRunHeader(out _readyToRunHeaderRVA); return _composite; } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs new file mode 100644 index 00000000000000..a979b8fd01871f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs @@ -0,0 +1,353 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +using Internal.IL; +using Internal.Text; +using Internal.TypeSystem; + +using CombinedDependencyList = System.Collections.Generic.List.CombinedDependencyListEntry>; + +namespace ILCompiler.DependencyAnalysis +{ + [DebuggerTypeProxy(typeof(MethodCodeNodeDebugView))] + public class MethodCodeNode : ObjectNode, IMethodBodyNode, INodeWithCodeInfo, INodeWithDebugInfo, ISymbolDefinitionNode, ISpecialUnboxThunkNode + { + private MethodDesc _method; + private ObjectData _methodCode; + private FrameInfo[] _frameInfos; + private byte[] _gcInfo; + private MethodExceptionHandlingInfoNode _ehInfo; + private DebugLocInfo[] _debugLocInfos; + private DebugVarInfo[] _debugVarInfos; + private DebugEHClauseInfo[] _debugEHClauseInfos; + private DependencyList _nonRelocationDependencies; + private bool _isFoldable; + private MethodDebugInformation _debugInfo; + private TypeDesc[] _localTypes; + + public MethodCodeNode(MethodDesc method) + { + Debug.Assert(!method.IsAbstract); + Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method); + _method = method; + } + + public void SetCode(ObjectData data, bool isFoldable) + { + Debug.Assert(_methodCode == null); + _methodCode = data; + _isFoldable = isFoldable; + } + + public MethodDesc Method => _method; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectNodeSection Section + { + get + { + return _method.Context.Target.IsWindows ? + (_isFoldable ? ObjectNodeSection.FoldableManagedCodeWindowsContentSection : ObjectNodeSection.ManagedCodeWindowsContentSection) : + (_isFoldable ? ObjectNodeSection.FoldableManagedCodeUnixContentSection : ObjectNodeSection.ManagedCodeUnixContentSection); + } + } + + public override bool StaticDependenciesAreComputed => _methodCode != null; + + public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.GetMangledMethodName(_method)); + } + public int Offset => 0; + public override bool IsShareable => _method is InstantiatedMethod || EETypeNode.IsTypeNodeShareable(_method.OwningType); + + public override bool HasConditionalStaticDependencies => CodeBasedDependencyAlgorithm.HasConditionalDependenciesDueToMethodCodePresence(_method); + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + CombinedDependencyList dependencies = null; + CodeBasedDependencyAlgorithm.AddConditionalDependenciesDueToMethodCodePresence(ref dependencies, factory, _method); + return dependencies ?? (IEnumerable)Array.Empty(); + } + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencies = _nonRelocationDependencies != null ? new DependencyList(_nonRelocationDependencies) : null; + + TypeDesc owningType = _method.OwningType; + if (factory.PreinitializationManager.HasEagerStaticConstructor(owningType)) + { + if (dependencies == null) + dependencies = new DependencyList(); + dependencies.Add(factory.EagerCctorIndirection(owningType.GetStaticConstructor()), "Eager .cctor"); + } + + if (_ehInfo != null) + { + if (dependencies == null) + dependencies = new DependencyList(); + dependencies.Add(_ehInfo, "Exception handling information"); + } + + if (MethodAssociatedDataNode.MethodHasAssociatedData(factory, this)) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(new DependencyListEntry(factory.MethodAssociatedData(this), "Method associated data")); + } + + return dependencies; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + return _methodCode; + } + + public bool IsSpecialUnboxingThunk => ((CompilerTypeSystemContext)Method.Context).IsSpecialUnboxingThunk(_method); + + public ISymbolNode GetUnboxingThunkTarget(NodeFactory factory) + { + Debug.Assert(IsSpecialUnboxingThunk); + + MethodDesc nonUnboxingMethod = ((CompilerTypeSystemContext)Method.Context).GetTargetOfSpecialUnboxingThunk(_method); + return factory.MethodEntrypoint(nonUnboxingMethod, false); + } + + public FrameInfo[] FrameInfos => _frameInfos; + public byte[] GCInfo => _gcInfo; + public MethodExceptionHandlingInfoNode EHInfo => _ehInfo; + + public ISymbolNode GetAssociatedDataNode(NodeFactory factory) + { + if (MethodAssociatedDataNode.MethodHasAssociatedData(factory, this)) + return factory.MethodAssociatedData(this); + + return null; + } + + public void InitializeFrameInfos(FrameInfo[] frameInfos) + { + Debug.Assert(_frameInfos == null); + _frameInfos = frameInfos; + } + + public void InitializeGCInfo(byte[] gcInfo) + { + Debug.Assert(_gcInfo == null); + _gcInfo = gcInfo; + } + + public void InitializeEHInfo(ObjectData ehInfo) + { + Debug.Assert(_ehInfo == null); + if (ehInfo != null) + _ehInfo = new MethodExceptionHandlingInfoNode(_method, ehInfo); + } + + public DebugLocInfo[] DebugLocInfos => _debugLocInfos; + public DebugVarInfo[] DebugVarInfos => _debugVarInfos; + public DebugEHClauseInfo[] DebugEHClauseInfos => _debugEHClauseInfos; + + public bool IsStateMachineMoveNextMethod => _debugInfo.IsStateMachineMoveNextMethod; + + public void InitializeDebugLocInfos(DebugLocInfo[] debugLocInfos) + { + Debug.Assert(_debugLocInfos == null); + _debugLocInfos = debugLocInfos; + } + + public void InitializeDebugVarInfos(DebugVarInfo[] debugVarInfos) + { + Debug.Assert(_debugVarInfos == null); + _debugVarInfos = debugVarInfos; + } + + public void InitializeDebugInfo(MethodDebugInformation debugInfo) + { + Debug.Assert(_debugInfo == null); + _debugInfo = debugInfo; + } + + public void InitializeLocalTypes(TypeDesc[] localTypes) + { + Debug.Assert(_localTypes == null); + _localTypes = localTypes; + } + + public void InitializeDebugEHClauseInfos(DebugEHClauseInfo[] debugEHClauseInfos) + { + Debug.Assert(_debugEHClauseInfos == null); + _debugEHClauseInfos = debugEHClauseInfos; + } + + public IEnumerable GetDebugVars() + { + MethodSignature sig = _method.Signature; + int offset = sig.IsStatic ? 0 : 1; + + var parameterNames = new string[sig.Length + offset]; + int i = 0; + foreach (var paramName in _debugInfo.GetParameterNames()) + { + parameterNames[i] = paramName; + i++; + } + + var localNames = new string[_localTypes.Length]; + + foreach (var local in _debugInfo.GetLocalVariables()) + { + if (!local.CompilerGenerated && local.Slot < localNames.Length) + localNames[local.Slot] = local.Name; + } + + foreach (var varInfo in _debugVarInfos) + { + if (varInfo.VarNumber < parameterNames.Length) + { + // This is a parameter + TypeDesc varType; + if (!sig.IsStatic && varInfo.VarNumber == 0) + { + varType = _method.OwningType.IsValueType ? + _method.OwningType.MakeByRefType() : + _method.OwningType; + } + else + { + varType = _method.Signature[(int)varInfo.VarNumber - offset]; + } + + string name = parameterNames[varInfo.VarNumber]; + if (name == null) + continue; + + yield return new DebugVarInfoMetadata(name, varType, isParameter: true, varInfo); + } + else + { + // This is a local + int localNumber = (int)varInfo.VarNumber - sig.Length - offset; + string name = localNames[localNumber]; + if (name == null) + continue; + + yield return new DebugVarInfoMetadata(name, _localTypes[localNumber], isParameter: false, varInfo); + } + } + } + + public void InitializeNonRelocationDependencies(DependencyList dependencies) + { + _nonRelocationDependencies = dependencies; + } + + public IEnumerable GetNativeSequencePoints() + { + var sequencePoints = new (string Document, int LineNumber)[_debugLocInfos.Length * 4 /* chosen empirically */]; + try + { + foreach (var sequencePoint in _debugInfo.GetSequencePoints()) + { + int offset = sequencePoint.Offset; + if (offset >= sequencePoints.Length) + { + int newLength = Math.Max(2 * sequencePoints.Length, sequencePoint.Offset + 1); + Array.Resize(ref sequencePoints, newLength); + } + sequencePoints[offset] = (sequencePoint.Document, sequencePoint.LineNumber); + } + } + catch (BadImageFormatException) + { + // Roslyn had a bug where it was generating bad sequence points: + // https://github.com/dotnet/roslyn/issues/20118 + // Do not crash the compiler. + yield break; + } + + int previousNativeOffset = -1; + foreach (var nativeMapping in _debugLocInfos) + { + if (nativeMapping.NativeOffset == previousNativeOffset) + continue; + + if (nativeMapping.ILOffset < sequencePoints.Length) + { + var sequencePoint = sequencePoints[nativeMapping.ILOffset]; + if (sequencePoint.Document != null) + { + yield return new NativeSequencePoint( + nativeMapping.NativeOffset, + sequencePoint.Document, + sequencePoint.LineNumber); + previousNativeOffset = nativeMapping.NativeOffset; + } + } + } + } + + public override int ClassCode => 788492407; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_method, ((MethodCodeNode)other)._method); + } + + public override string ToString() + { + return _method.ToString(); + } + + internal class MethodCodeNodeDebugView + { + private readonly MethodCodeNode _node; + + public MethodCodeNodeDebugView(MethodCodeNode node) + { + _node = node; + } + + public MethodDesc Method => _node.Method; + + public string Disassembly + { + get + { + var sb = new StringBuilder(); + sb.Append("// "); + sb.AppendLine(_node.Method.ToString()); + if (_node.StaticDependenciesAreComputed) + { + var d = Disassembler.Disassemble( + _node.Method.Context.Target.Architecture, + _node._methodCode.Data, + _node._methodCode.Relocs); + sb.Append(d); + } + else + { + sb.Append("// Not compiled yet."); + } + + return sb.ToString(); + } + } + } + } + + public readonly struct DebugLocInfo + { + public readonly int NativeOffset; + public readonly int ILOffset; + + public DebugLocInfo(int nativeOffset, int ilOffset) + => (NativeOffset, ILOffset) = (nativeOffset, ilOffset); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs new file mode 100644 index 00000000000000..50952cab62d034 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + public sealed class RyuJitNodeFactory : NodeFactory + { + public RyuJitNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, + InteropStubManager interopStubManager, NameMangler nameMangler, VTableSliceProvider vtableSliceProvider, DictionaryLayoutProvider dictionaryLayoutProvider, PreinitializationManager preinitializationManager) + : base(context, compilationModuleGroup, metadataManager, interopStubManager, nameMangler, new LazyGenericsDisabledPolicy(), vtableSliceProvider, dictionaryLayoutProvider, new ExternSymbolsImportedNodeProvider(), preinitializationManager) + { + } + + protected override IMethodNode CreateMethodEntrypointNode(MethodDesc method) + { + if (method.IsInternalCall) + { + if (TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(method)) + { + return MethodEntrypoint(TypeSystemContext.GetRealSpecialUnboxingThunkTargetMethod(method)); + } + else if (TypeSystemContext.IsDefaultInterfaceMethodImplementationThunkTargetMethod(method)) + { + return MethodEntrypoint(TypeSystemContext.GetRealDefaultInterfaceMethodImplementationThunkTargetMethod(method)); + } + else if (method.IsArrayAddressMethod()) + { + return MethodEntrypoint(((ArrayType)method.OwningType).GetArrayMethod(ArrayMethodKind.AddressWithHiddenArg)); + } + else if (method.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute")) + { + return new RuntimeImportMethodNode(method); + } + } + + // MethodDesc that represents an unboxing thunk is a thing that is internal to the JitInterface. + // It should not leak out of JitInterface. + Debug.Assert(!Internal.JitInterface.UnboxingMethodDescExtensions.IsUnboxingThunk(method)); + + if (CompilationModuleGroup.ContainsMethodBody(method, false)) + { + // We might be able to optimize the method body away if the owning type was never seen as allocated. + if (method.NotCallableWithoutOwningEEType() && CompilationModuleGroup.AllowInstanceMethodOptimization(method)) + return new TentativeInstanceMethodNode(new MethodCodeNode(method)); + + return new MethodCodeNode(method); + } + else + { + return _importedNodeProvider.ImportedMethodCodeNode(this, method, false); + } + } + + protected override IMethodNode CreateUnboxingStubNode(MethodDesc method) + { + Debug.Assert(!method.Signature.IsStatic); + + if (method.IsCanonicalMethod(CanonicalFormKind.Specific) && !method.HasInstantiation) + { + // Unboxing stubs to canonical instance methods need a special unboxing stub that unboxes + // 'this' and also provides an instantiation argument (we do a calling convention conversion). + // We don't do this for generic instance methods though because they don't use the MethodTable + // for the generic context anyway. + return new MethodCodeNode(TypeSystemContext.GetSpecialUnboxingThunk(method, TypeSystemContext.GeneratedAssembly)); + } + else + { + // Otherwise we just unbox 'this' and don't touch anything else. + return new UnboxingStubNode(method, Target); + } + } + + protected override ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall) + { + return new ReadyToRunHelperNode(helperCall.HelperId, helperCall.Target); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/ProfileDataManager.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/ProfileDataManager.cs new file mode 100644 index 00000000000000..c75a1a8c4f024f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/ProfileDataManager.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Reflection.PortableExecutable; + +using ILCompiler.IBC; +using Internal.TypeSystem; + +namespace ILCompiler +{ + public class ProfileDataManager + { + private readonly Dictionary _mergedProfileData = new Dictionary(); + + public ProfileDataManager(IEnumerable mibcFiles, + CompilerTypeSystemContext context) + { + List _inputData = new List(); + + foreach (string file in mibcFiles) + { + using (PEReader peReader = MIbcProfileParser.OpenMibcAsPEReader(file)) + { + _inputData.Add(MIbcProfileParser.ParseMIbcFile(context, peReader, null, null)); + } + } + + bool dummy = false; + + // Merge all data together + foreach (ProfileData profileData in _inputData) + { + ProfileData.MergeProfileData(ref dummy, _mergedProfileData, profileData); + } + } + + public MethodProfileData this[MethodDesc method] + { + get + { + _mergedProfileData.TryGetValue(method, out var profileData); + return profileData; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs new file mode 100644 index 00000000000000..e794bdd1a94790 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs @@ -0,0 +1,258 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +using Internal.IL; +using Internal.IL.Stubs; +using Internal.TypeSystem; +using Internal.JitInterface; + +namespace ILCompiler +{ + public sealed class RyuJitCompilation : Compilation + { + private readonly ConditionalWeakTable _corinfos = new ConditionalWeakTable(); + internal readonly RyuJitCompilationOptions _compilationOptions; + private readonly ExternSymbolMappedField _hardwareIntrinsicFlags; + private CountdownEvent _compilationCountdown; + private readonly Dictionary _instructionSetMap; + private readonly ProfileDataManager _profileDataManager; + private readonly MethodImportationErrorProvider _methodImportationErrorProvider; + + public InstructionSetSupport InstructionSetSupport { get; } + + internal RyuJitCompilation( + DependencyAnalyzerBase dependencyGraph, + NodeFactory nodeFactory, + IEnumerable roots, + ILProvider ilProvider, + DebugInformationProvider debugInformationProvider, + Logger logger, + DevirtualizationManager devirtualizationManager, + IInliningPolicy inliningPolicy, + InstructionSetSupport instructionSetSupport, + ProfileDataManager profileDataManager, + MethodImportationErrorProvider errorProvider, + RyuJitCompilationOptions options) + : base(dependencyGraph, nodeFactory, roots, ilProvider, debugInformationProvider, devirtualizationManager, inliningPolicy, logger) + { + _compilationOptions = options; + _hardwareIntrinsicFlags = new ExternSymbolMappedField(nodeFactory.TypeSystemContext.GetWellKnownType(WellKnownType.Int32), "g_cpuFeatures"); + InstructionSetSupport = instructionSetSupport; + + _instructionSetMap = new Dictionary(); + foreach (var instructionSetInfo in InstructionSetFlags.ArchitectureToValidInstructionSets(TypeSystemContext.Target.Architecture)) + { + if (!instructionSetInfo.Specifiable) + continue; + + _instructionSetMap.Add(instructionSetInfo.ManagedName, instructionSetInfo.InstructionSet); + } + + _profileDataManager = profileDataManager; + + _methodImportationErrorProvider = errorProvider; + } + + public ProfileDataManager ProfileData => _profileDataManager; + + public override IEETypeNode NecessaryTypeSymbolIfPossible(TypeDesc type) + { + // RyuJIT makes assumptions around the value of these symbols - in particular, it assumes + // that type handles and type symbols have a 1:1 relationship. We therefore need to + // make sure RyuJIT never sees a constructed and unconstructed type symbol for the + // same type. If the type is constructable and we don't have whole progam view + // information proving that it isn't, give RyuJIT the constructed symbol even + // though we just need the unconstructed one. + // https://github.com/dotnet/runtimelab/issues/1128 + bool canPotentiallyConstruct = _devirtualizationManager == null + ? true : _devirtualizationManager.CanConstructType(type); + if (canPotentiallyConstruct) + return _nodeFactory.MaximallyConstructableType(type); + + return _nodeFactory.NecessaryTypeSymbol(type); + } + + protected override void CompileInternal(string outputFile, ObjectDumper dumper) + { + _dependencyGraph.ComputeMarkedNodes(); + var nodes = _dependencyGraph.MarkedNodeList; + + NodeFactory.SetMarkingComplete(); + + ObjectWritingOptions options = default; + if (_debugInformationProvider is not NullDebugInformationProvider) + options |= ObjectWritingOptions.GenerateDebugInfo; + + if ((_compilationOptions & RyuJitCompilationOptions.ControlFlowGuardAnnotations) != 0) + options |= ObjectWritingOptions.ControlFlowGuard; + + ObjectWriter.EmitObject(outputFile, nodes, NodeFactory, options, dumper, _logger); + } + + protected override void ComputeDependencyNodeDependencies(List> obj) + { + // Determine the list of method we actually need to compile + var methodsToCompile = new List(); + var canonicalMethodsToCompile = new HashSet(); + + foreach (DependencyNodeCore dependency in obj) + { + var methodCodeNodeNeedingCode = dependency as MethodCodeNode; + if (methodCodeNodeNeedingCode == null) + { + // To compute dependencies of the shadow method that tracks dictionary + // dependencies we need to ensure there is code for the canonical method body. + var dependencyMethod = (ShadowConcreteMethodNode)dependency; + methodCodeNodeNeedingCode = (MethodCodeNode)dependencyMethod.CanonicalMethodNode; + } + + // We might have already queued this method for compilation + MethodDesc method = methodCodeNodeNeedingCode.Method; + if (method.IsCanonicalMethod(CanonicalFormKind.Any) + && !canonicalMethodsToCompile.Add(method)) + { + continue; + } + + methodsToCompile.Add(methodCodeNodeNeedingCode); + } + + if ((_compilationOptions & RyuJitCompilationOptions.SingleThreadedCompilation) != 0) + { + CompileSingleThreaded(methodsToCompile); + } + else + { + CompileMultiThreaded(methodsToCompile); + } + } + private void CompileMultiThreaded(List methodsToCompile) + { + if (Logger.IsVerbose) + { + Logger.Writer.WriteLine($"Compiling {methodsToCompile.Count} methods..."); + } + + WaitCallback compileSingleMethodDelegate = m => + { + CorInfoImpl corInfo = _corinfos.GetValue(Thread.CurrentThread, thread => new CorInfoImpl(this)); + CompileSingleMethod(corInfo, (MethodCodeNode)m); + }; + + using (_compilationCountdown = new CountdownEvent(methodsToCompile.Count)) + { + + foreach (MethodCodeNode methodCodeNodeNeedingCode in methodsToCompile) + { + ThreadPool.QueueUserWorkItem(compileSingleMethodDelegate, methodCodeNodeNeedingCode); + } + + _compilationCountdown.Wait(); + _compilationCountdown = null; + } + } + + + private void CompileSingleThreaded(List methodsToCompile) + { + CorInfoImpl corInfo = _corinfos.GetValue(Thread.CurrentThread, thread => new CorInfoImpl(this)); + + foreach (MethodCodeNode methodCodeNodeNeedingCode in methodsToCompile) + { + if (Logger.IsVerbose) + { + Logger.Writer.WriteLine($"Compiling {methodCodeNodeNeedingCode.Method}..."); + } + + CompileSingleMethod(corInfo, methodCodeNodeNeedingCode); + } + } + + private void CompileSingleMethod(CorInfoImpl corInfo, MethodCodeNode methodCodeNodeNeedingCode) + { + try + { + MethodDesc method = methodCodeNodeNeedingCode.Method; + + TypeSystemException exception = _methodImportationErrorProvider.GetCompilationError(method); + + // If we previously failed to import the method, do not try to import it again and go + // directly to the error path. + if (exception == null) + { + try + { + corInfo.CompileMethod(methodCodeNodeNeedingCode); + } + catch (TypeSystemException ex) + { + exception = ex; + } + } + + if (exception != null) + { + // TODO: fail compilation if a switch was passed + + // Try to compile the method again, but with a throwing method body this time. + MethodIL throwingIL = TypeSystemThrowingILEmitter.EmitIL(method, exception); + corInfo.CompileMethod(methodCodeNodeNeedingCode, throwingIL); + + if (exception is TypeSystemException.InvalidProgramException + && method.OwningType is MetadataType mdOwningType + && mdOwningType.HasCustomAttribute("System.Runtime.InteropServices", "ClassInterfaceAttribute")) + { + Logger.LogWarning("COM interop is not supported with full ahead of time compilation", 3052, method, MessageSubCategory.AotAnalysis); + } + else + { + Logger.LogWarning($"Method will always throw because: {exception.Message}", 1005, method, MessageSubCategory.AotAnalysis); + } + } + } + finally + { + if (_compilationCountdown != null) + _compilationCountdown.Signal(); + } + } + + public override MethodIL GetMethodIL(MethodDesc method) + { + TypeDesc owningType = method.OwningType; + string intrinsicId = InstructionSetSupport.GetHardwareIntrinsicId(TypeSystemContext.Target.Architecture, owningType); + if (!string.IsNullOrEmpty(intrinsicId) + && HardwareIntrinsicHelpers.IsIsSupportedMethod(method)) + { + InstructionSet instructionSet = _instructionSetMap[intrinsicId]; + + // If this is an instruction set that is optimistically supported, but is not one of the + // intrinsics that are known to be always available, emit IL that checks the support level + // at runtime. + if (!InstructionSetSupport.IsInstructionSetSupported(instructionSet) + && InstructionSetSupport.OptimisticFlags.HasInstructionSet(instructionSet)) + { + return HardwareIntrinsicHelpers.EmitIsSupportedIL(method, _hardwareIntrinsicFlags); + } + } + + return base.GetMethodIL(method); + } + } + + [Flags] + public enum RyuJitCompilationOptions + { + MethodBodyFolding = 0x1, + SingleThreadedCompilation = 0x2, + ControlFlowGuardAnnotations = 0x4, + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs new file mode 100644 index 00000000000000..162f1a39e86bbc --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs @@ -0,0 +1,123 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +using Internal.IL; +using Internal.JitInterface; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + public sealed class RyuJitCompilationBuilder : CompilationBuilder + { + // These need to provide reasonable defaults so that the user can optionally skip + // calling the Use/Configure methods and still get something reasonable back. + private KeyValuePair[] _ryujitOptions = Array.Empty>(); + private ILProvider _ilProvider = new CoreRTILProvider(); + private ProfileDataManager _profileDataManager; + + public RyuJitCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group) + : base(context, group, + new CoreRTNameMangler(context.Target.IsWindows ? (NodeMangler)new WindowsNodeMangler() : (NodeMangler)new UnixNodeMangler(), false)) + { + } + + public RyuJitCompilationBuilder UseProfileData(IEnumerable mibcFiles) + { + _profileDataManager = new ProfileDataManager(mibcFiles, _context); + return this; + } + + public override CompilationBuilder UseBackendOptions(IEnumerable options) + { + var builder = new ArrayBuilder>(); + + foreach (string param in options) + { + int indexOfEquals = param.IndexOf('='); + + // We're skipping bad parameters without reporting. + // This is not a mainstream feature that would need to be friendly. + // Besides, to really validate this, we would also need to check that the config name is known. + if (indexOfEquals < 1) + continue; + + string name = param.Substring(0, indexOfEquals); + string value = param.Substring(indexOfEquals + 1); + + builder.Add(new KeyValuePair(name, value)); + } + + _ryujitOptions = builder.ToArray(); + + return this; + } + + public override CompilationBuilder UseILProvider(ILProvider ilProvider) + { + _ilProvider = ilProvider; + return this; + } + + protected override ILProvider GetILProvider() + { + return _ilProvider; + } + + public override ICompilation ToCompilation() + { + ArrayBuilder jitFlagBuilder = new ArrayBuilder(); + + switch (_optimizationMode) + { + case OptimizationMode.None: + jitFlagBuilder.Add(CorJitFlag.CORJIT_FLAG_DEBUG_CODE); + break; + + case OptimizationMode.PreferSize: + jitFlagBuilder.Add(CorJitFlag.CORJIT_FLAG_SIZE_OPT); + break; + + case OptimizationMode.PreferSpeed: + jitFlagBuilder.Add(CorJitFlag.CORJIT_FLAG_SPEED_OPT); + break; + + default: + // Not setting a flag results in BLENDED_CODE. + break; + } + + if (_optimizationMode != OptimizationMode.None && _profileDataManager != null) + { + jitFlagBuilder.Add(CorJitFlag.CORJIT_FLAG_BBOPT); + } + + // Do not bother with debug information if the debug info provider never gives anything. + if (!(_debugInformationProvider is NullDebugInformationProvider)) + jitFlagBuilder.Add(CorJitFlag.CORJIT_FLAG_DEBUG_INFO); + + RyuJitCompilationOptions options = 0; + if (_methodBodyFolding) + options |= RyuJitCompilationOptions.MethodBodyFolding; + + if (_singleThreaded) + options |= RyuJitCompilationOptions.SingleThreadedCompilation; + + if ((_mitigationOptions & SecurityMitigationOptions.ControlFlowGuardAnnotations) != 0) + options |= RyuJitCompilationOptions.ControlFlowGuardAnnotations; + + var factory = new RyuJitNodeFactory(_context, _compilationGroup, _metadataManager, _interopStubManager, _nameMangler, _vtableSliceProvider, _dictionaryLayoutProvider, GetPreinitializationManager()); + + JitConfigProvider.Initialize(_context.Target, jitFlagBuilder.ToArray(), _ryujitOptions); + DependencyAnalyzerBase graph = CreateDependencyGraph(factory, new ObjectNode.ObjectNodeComparer(new CompilerComparer())); + return new RyuJitCompilation(graph, factory, _compilationRoots, _ilProvider, _debugInformationProvider, _logger, _devirtualizationManager, _inliningPolicy ?? _compilationGroup, _instructionSetSupport, _profileDataManager, _methodImportationErrorProvider, options); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj new file mode 100644 index 00000000000000..b1325a2a7a339a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj @@ -0,0 +1,103 @@ + + + Library + ILCompiler + ILCompiler.RyuJit + net5.0 + true + false + x64;x86 + AnyCPU + false + + + false + Debug;Release;Checked + + + + + + + + + + + + + + + + + + + + + Common\ArrayBuilder.cs + + + Common\MethodTable.Constants.cs + + + Common\CorConstants.cs + + + Common\FormattingHelpers.cs + + + Compiler\JitHelper.cs + + + IL\HelperExtensions.cs + + + IL\ILReader.cs + + + IL\Stubs\TypeSystemThrowingILEmitter.cs + + + Interop\CallConvHelpers.cs + + + JitInterface\JitConfigProvider.cs + + + JitInterface\TypeString.cs + + + JitInterface\CorInfoBase.cs + + + JitInterface\CorInfoImpl.cs + + + JitInterface\CorInfoImpl.Intrinsics.cs + + + JitInterface\MemoryHelper.cs + + + JitInterface\UnboxingMethodDesc.cs + + + JitInterface\UnboxingMethodDescFactory.cs + + + JitInterface\SystemVStructClassificator.cs + + + Pgo\TypeSystemEntityOrUnknown.cs + + + + + + + + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs new file mode 100644 index 00000000000000..2ecd15ab009959 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -0,0 +1,2052 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; + +using Internal.IL; +using Internal.TypeSystem; +using Internal.ReadyToRunConstants; + +using ILCompiler; +using ILCompiler.DependencyAnalysis; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; + +#if SUPPORT_JIT +using MethodCodeNode = Internal.Runtime.JitSupport.JitMethodCodeNode; +using RyuJitCompilation = ILCompiler.Compilation; +#endif + +namespace Internal.JitInterface +{ + unsafe partial class CorInfoImpl + { + private const CORINFO_RUNTIME_ABI TargetABI = CORINFO_RUNTIME_ABI.CORINFO_CORERT_ABI; + + private uint OffsetOfDelegateFirstTarget => (uint)(4 * PointerSize); // Delegate::m_functionPointer + private int SizeOfReversePInvokeTransitionFrame => 2 * PointerSize; + + private RyuJitCompilation _compilation; + private MethodDebugInformation _debugInfo; + private MethodCodeNode _methodCodeNode; + private DebugLocInfo[] _debugLocInfos; + private DebugVarInfo[] _debugVarInfos; + private readonly UnboxingMethodDescFactory _unboxingThunkFactory = new UnboxingMethodDescFactory(); + private bool _isFallbackBodyCompilation; + private DependencyList _additionalDependencies; + + public CorInfoImpl(RyuJitCompilation compilation) + : this() + { + _compilation = compilation; + } + + private MethodDesc getUnboxingThunk(MethodDesc method) + { + return _unboxingThunkFactory.GetUnboxingMethod(method); + } + + public void CompileMethod(MethodCodeNode methodCodeNodeNeedingCode, MethodIL methodIL = null) + { + _methodCodeNode = methodCodeNodeNeedingCode; + _isFallbackBodyCompilation = methodIL != null; + + if (methodIL == null) + methodIL = _compilation.GetMethodIL(MethodBeingCompiled); + + try + { + CompileMethodInternal(methodCodeNodeNeedingCode, methodIL); + } + finally + { +#if DEBUG + // RyuJIT makes assumptions around the value of type symbols - in particular, it assumes + // that type handles and type symbols have a 1:1 relationship. We therefore need to + // make sure RyuJIT never sees a constructed and unconstructed type symbol for the + // same type. This check makes sure we didn't accidentally hand out a necessary type symbol + // that the compilation class didn't agree to handing out. + // https://github.com/dotnet/runtimelab/issues/1128 + for (int i = 0; i < _codeRelocs.Count; i++) + { + Debug.Assert(_codeRelocs[i].Target.GetType() != typeof(EETypeNode) + || _compilation.NecessaryTypeSymbolIfPossible(((EETypeNode)_codeRelocs[i].Target).Type) == _codeRelocs[i].Target); + } +#endif + + CompileMethodCleanup(); + } + } + + private enum CFI_OPCODE + { + CFI_ADJUST_CFA_OFFSET, // Offset is adjusted relative to the current one. + CFI_DEF_CFA_REGISTER, // New register is used to compute CFA + CFI_REL_OFFSET, // Register is saved at offset from the current CFA + CFI_DEF_CFA // Take address from register and add offset to it. + } + + // Get the CFI data in the same shape as clang/LLVM generated one. This improves the compatibility with libunwind and other unwind solutions + // - Combine in one single block for the whole prolog instead of one CFI block per assembler instruction + // - Store CFA definition first + // - Store all used registers in ascending order + private byte[] CompressARM64CFI(byte[] blobData) + { + if (blobData == null || blobData.Length == 0) + { + return blobData; + } + + Debug.Assert(blobData.Length % 8 == 0); + + short spReg = -1; + + int codeOffset = 0; + short cfaRegister = spReg; + int cfaOffset = 0; + int spOffset = 0; + + int[] registerOffset = new int[96]; + + for (int i = 0; i < registerOffset.Length; i++) + { + registerOffset[i] = int.MinValue; + } + + int offset = 0; + while (offset < blobData.Length) + { + codeOffset = Math.Max(codeOffset, blobData[offset++]); + CFI_OPCODE opcode = (CFI_OPCODE)blobData[offset++]; + short dwarfReg = BitConverter.ToInt16(blobData, offset); + offset += sizeof(short); + int cfiOffset = BitConverter.ToInt32(blobData, offset); + offset += sizeof(int); + + switch (opcode) + { + case CFI_OPCODE.CFI_DEF_CFA_REGISTER: + cfaRegister = dwarfReg; + + if (spOffset != 0) + { + for (int i = 0; i < registerOffset.Length; i++) + { + if (registerOffset[i] != int.MinValue) + { + registerOffset[i] -= spOffset; + } + } + + cfaOffset += spOffset; + spOffset = 0; + } + + break; + + case CFI_OPCODE.CFI_REL_OFFSET: + Debug.Assert(cfaRegister == spReg); + registerOffset[dwarfReg] = cfiOffset; + break; + + case CFI_OPCODE.CFI_ADJUST_CFA_OFFSET: + if (cfaRegister != spReg) + { + cfaOffset += cfiOffset; + } + else + { + spOffset += cfiOffset; + + for (int i = 0; i < registerOffset.Length; i++) + { + if (registerOffset[i] != int.MinValue) + { + registerOffset[i] += cfiOffset; + } + } + } + break; + } + } + + using (MemoryStream cfiStream = new MemoryStream()) + { + int storeOffset = 0; + + using (BinaryWriter cfiWriter = new BinaryWriter(cfiStream)) + { + if (cfaRegister != -1) + { + cfiWriter.Write((byte)codeOffset); + cfiWriter.Write(cfaOffset != 0 ? (byte)CFI_OPCODE.CFI_DEF_CFA : (byte)CFI_OPCODE.CFI_DEF_CFA_REGISTER); + cfiWriter.Write(cfaRegister); + cfiWriter.Write(cfaOffset); + storeOffset = cfaOffset; + } + else + { + if (cfaOffset != 0) + { + cfiWriter.Write((byte)codeOffset); + cfiWriter.Write((byte)CFI_OPCODE.CFI_ADJUST_CFA_OFFSET); + cfiWriter.Write((short)-1); + cfiWriter.Write(cfaOffset); + } + + if (spOffset != 0) + { + cfiWriter.Write((byte)codeOffset); + cfiWriter.Write((byte)CFI_OPCODE.CFI_DEF_CFA); + cfiWriter.Write((short)31); + cfiWriter.Write(spOffset); + } + } + + for (int i = registerOffset.Length - 1; i >= 0; i--) + { + if (registerOffset[i] != int.MinValue) + { + cfiWriter.Write((byte)codeOffset); + cfiWriter.Write((byte)CFI_OPCODE.CFI_REL_OFFSET); + cfiWriter.Write((short)i); + cfiWriter.Write(registerOffset[i] + storeOffset); + } + } + } + + return cfiStream.ToArray(); + } + } + + private CORINFO_RUNTIME_LOOKUP_KIND GetLookupKindFromContextSource(GenericContextSource contextSource) + { + switch (contextSource) + { + case GenericContextSource.MethodParameter: + return CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_METHODPARAM; + case GenericContextSource.TypeParameter: + return CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_CLASSPARAM; + default: + Debug.Assert(contextSource == GenericContextSource.ThisObject); + return CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ; + } + } + + private void ComputeLookup(ref CORINFO_RESOLVED_TOKEN pResolvedToken, object entity, ReadyToRunHelperId helperId, ref CORINFO_LOOKUP lookup) + { + if (_compilation.NeedsRuntimeLookup(helperId, entity)) + { + lookup.lookupKind.needsRuntimeLookup = true; + lookup.runtimeLookup.signature = null; + + // Do not bother computing the runtime lookup if we are inlining. The JIT is going + // to abort the inlining attempt anyway. + if (pResolvedToken.tokenContext != contextFromMethodBeingCompiled()) + { + lookup.lookupKind.runtimeLookupKind = CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_NOT_SUPPORTED; + return; + } + + MethodDesc contextMethod = methodFromContext(pResolvedToken.tokenContext); + + GenericDictionaryLookup genericLookup = _compilation.ComputeGenericLookup(contextMethod, helperId, entity); + + if (genericLookup.UseHelper) + { + lookup.runtimeLookup.indirections = CORINFO.USEHELPER; + lookup.lookupKind.runtimeLookupFlags = (ushort)genericLookup.HelperId; + lookup.lookupKind.runtimeLookupArgs = (void*)ObjectToHandle(genericLookup.HelperObject); + } + else + { + if (genericLookup.ContextSource == GenericContextSource.MethodParameter) + { + lookup.runtimeLookup.helper = CorInfoHelpFunc.CORINFO_HELP_RUNTIMEHANDLE_METHOD; + } + else + { + lookup.runtimeLookup.helper = CorInfoHelpFunc.CORINFO_HELP_RUNTIMEHANDLE_CLASS; + } + + lookup.runtimeLookup.indirections = (ushort)(genericLookup.NumberOfIndirections + (genericLookup.IndirectLastOffset ? 1 : 0)); + lookup.runtimeLookup.offset0 = (IntPtr)genericLookup[0]; + if (genericLookup.NumberOfIndirections > 1) + { + lookup.runtimeLookup.offset1 = (IntPtr)genericLookup[1]; + if (genericLookup.IndirectLastOffset) + lookup.runtimeLookup.offset2 = IntPtr.Zero; + } + else if (genericLookup.IndirectLastOffset) + { + lookup.runtimeLookup.offset1 = IntPtr.Zero; + } + lookup.runtimeLookup.sizeOffset = CORINFO.CORINFO_NO_SIZE_CHECK; + lookup.runtimeLookup.testForFixup = false; // TODO: this will be needed in true multifile + lookup.runtimeLookup.testForNull = false; + lookup.runtimeLookup.indirectFirstOffset = false; + lookup.runtimeLookup.indirectSecondOffset = false; + lookup.lookupKind.runtimeLookupFlags = 0; + lookup.lookupKind.runtimeLookupArgs = null; + } + + lookup.lookupKind.runtimeLookupKind = GetLookupKindFromContextSource(genericLookup.ContextSource); + } + else + { + lookup.lookupKind.needsRuntimeLookup = false; + ISymbolNode constLookup = _compilation.ComputeConstantLookup(helperId, entity); + lookup.constLookup = CreateConstLookupToSymbol(constLookup); + } + } + + private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref CORINFO_LOOKUP_KIND pGenericLookupKind, CorInfoHelpFunc id, ref CORINFO_CONST_LOOKUP pLookup) + { + switch (id) + { + case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NEW: + case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NEWARR_1: + case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_ISINSTANCEOF: + case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_CHKCAST: + return false; + case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_STATIC_BASE: + { + var type = HandleToObject(pResolvedToken.hClass); + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; + + pLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ReadyToRunHelper(ReadyToRunHelperId.GetNonGCStaticBase, type)); + } + break; + case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE: + { + // Token == 0 means "initialize this class". We only expect RyuJIT to call it for this case. + Debug.Assert(pResolvedToken.token == 0 && pResolvedToken.tokenScope == null); + Debug.Assert(pGenericLookupKind.needsRuntimeLookup); + + DefType typeToInitialize = (DefType)MethodBeingCompiled.OwningType; + Debug.Assert(typeToInitialize.IsCanonicalSubtype(CanonicalFormKind.Any)); + + DefType helperArg = typeToInitialize.ConvertToSharedRuntimeDeterminedForm(); + ISymbolNode helper = GetGenericLookupHelper(pGenericLookupKind.runtimeLookupKind, ReadyToRunHelperId.GetNonGCStaticBase, helperArg); + pLookup = CreateConstLookupToSymbol(helper); + } + break; + case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GENERIC_HANDLE: + { + Debug.Assert(pGenericLookupKind.needsRuntimeLookup); + + ReadyToRunHelperId helperId = (ReadyToRunHelperId)pGenericLookupKind.runtimeLookupFlags; + object helperArg = HandleToObject((IntPtr)pGenericLookupKind.runtimeLookupArgs); + ISymbolNode helper = GetGenericLookupHelper(pGenericLookupKind.runtimeLookupKind, helperId, helperArg); + pLookup = CreateConstLookupToSymbol(helper); + } + break; + default: + throw new NotImplementedException("ReadyToRun: " + id.ToString()); + } + return true; + } + + private void getReadyToRunDelegateCtorHelper(ref CORINFO_RESOLVED_TOKEN pTargetMethod, CORINFO_CLASS_STRUCT_* delegateType, ref CORINFO_LOOKUP pLookup) + { +#if DEBUG + // In debug, write some bogus data to the struct to ensure we have filled everything + // properly. + fixed (CORINFO_LOOKUP* tmp = &pLookup) + MemoryHelper.FillMemory((byte*)tmp, 0xcc, sizeof(CORINFO_LOOKUP)); +#endif + + MethodDesc targetMethod = HandleToObject(pTargetMethod.hMethod); + TypeDesc delegateTypeDesc = HandleToObject(delegateType); + + if (targetMethod.IsSharedByGenericInstantiations) + { + // If the method is not exact, fetch it as a runtime determined method. + targetMethod = (MethodDesc)GetRuntimeDeterminedObjectForToken(ref pTargetMethod); + } + + bool isLdvirtftn = pTargetMethod.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Ldvirtftn; + DelegateCreationInfo delegateInfo = _compilation.GetDelegateCtor(delegateTypeDesc, targetMethod, isLdvirtftn); + + if (delegateInfo.NeedsRuntimeLookup) + { + pLookup.lookupKind.needsRuntimeLookup = true; + + MethodDesc contextMethod = methodFromContext(pTargetMethod.tokenContext); + + // We should not be inlining these. RyuJIT should have aborted inlining already. + Debug.Assert(contextMethod == MethodBeingCompiled); + + pLookup.lookupKind.runtimeLookupKind = GetGenericRuntimeLookupKind(contextMethod); + pLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.DelegateCtor; + pLookup.lookupKind.runtimeLookupArgs = (void*)ObjectToHandle(delegateInfo); + } + else + { + pLookup.lookupKind.needsRuntimeLookup = false; + pLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ReadyToRunHelper(ReadyToRunHelperId.DelegateCtor, delegateInfo)); + } + } + + private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) + { + ReadyToRunHelper id; + + switch (ftnNum) + { + case CorInfoHelpFunc.CORINFO_HELP_THROW: + id = ReadyToRunHelper.Throw; + break; + case CorInfoHelpFunc.CORINFO_HELP_RETHROW: + id = ReadyToRunHelper.Rethrow; + break; + case CorInfoHelpFunc.CORINFO_HELP_USER_BREAKPOINT: + id = ReadyToRunHelper.DebugBreak; + break; + case CorInfoHelpFunc.CORINFO_HELP_OVERFLOW: + id = ReadyToRunHelper.Overflow; + break; + case CorInfoHelpFunc.CORINFO_HELP_RNGCHKFAIL: + id = ReadyToRunHelper.RngChkFail; + break; + case CorInfoHelpFunc.CORINFO_HELP_FAIL_FAST: + id = ReadyToRunHelper.FailFast; + break; + case CorInfoHelpFunc.CORINFO_HELP_THROWNULLREF: + id = ReadyToRunHelper.ThrowNullRef; + break; + case CorInfoHelpFunc.CORINFO_HELP_THROWDIVZERO: + id = ReadyToRunHelper.ThrowDivZero; + break; + case CorInfoHelpFunc.CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION: + id = ReadyToRunHelper.ThrowArgumentOutOfRange; + break; + case CorInfoHelpFunc.CORINFO_HELP_THROW_ARGUMENTEXCEPTION: + id = ReadyToRunHelper.ThrowArgument; + break; + case CorInfoHelpFunc.CORINFO_HELP_THROW_NOT_IMPLEMENTED: + id = ReadyToRunHelper.ThrowNotImplemented; + break; + case CorInfoHelpFunc.CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED: + id = ReadyToRunHelper.ThrowPlatformNotSupported; + break; + + case CorInfoHelpFunc.CORINFO_HELP_ASSIGN_REF: + id = ReadyToRunHelper.WriteBarrier; + break; + case CorInfoHelpFunc.CORINFO_HELP_CHECKED_ASSIGN_REF: + id = ReadyToRunHelper.CheckedWriteBarrier; + break; + case CorInfoHelpFunc.CORINFO_HELP_ASSIGN_BYREF: + id = ReadyToRunHelper.ByRefWriteBarrier; + break; + case CorInfoHelpFunc.CORINFO_HELP_ASSIGN_REF_EAX: + id = ReadyToRunHelper.WriteBarrier_EAX; + break; + case CorInfoHelpFunc.CORINFO_HELP_ASSIGN_REF_ECX: + id = ReadyToRunHelper.WriteBarrier_ECX; + break; + case CorInfoHelpFunc.CORINFO_HELP_CHECKED_ASSIGN_REF_EAX: + id = ReadyToRunHelper.CheckedWriteBarrier_EAX; + break; + case CorInfoHelpFunc.CORINFO_HELP_CHECKED_ASSIGN_REF_ECX: + id = ReadyToRunHelper.CheckedWriteBarrier_ECX; + break; + + case CorInfoHelpFunc.CORINFO_HELP_ARRADDR_ST: + id = ReadyToRunHelper.Stelem_Ref; + break; + case CorInfoHelpFunc.CORINFO_HELP_LDELEMA_REF: + id = ReadyToRunHelper.Ldelema_Ref; + break; + + case CorInfoHelpFunc.CORINFO_HELP_MEMSET: + id = ReadyToRunHelper.MemSet; + break; + case CorInfoHelpFunc.CORINFO_HELP_MEMCPY: + id = ReadyToRunHelper.MemCpy; + break; + + case CorInfoHelpFunc.CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE: + id = ReadyToRunHelper.GetRuntimeType; + break; + case CorInfoHelpFunc.CORINFO_HELP_METHODDESC_TO_STUBRUNTIMEMETHOD: + id = ReadyToRunHelper.GetRuntimeMethodHandle; + break; + case CorInfoHelpFunc.CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD: + id = ReadyToRunHelper.GetRuntimeFieldHandle; + break; + case CorInfoHelpFunc.CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE: + id = ReadyToRunHelper.GetRuntimeTypeHandle; + break; + + case CorInfoHelpFunc.CORINFO_HELP_ARE_TYPES_EQUIVALENT: + id = ReadyToRunHelper.AreTypesEquivalent; + break; + + case CorInfoHelpFunc.CORINFO_HELP_BOX: + id = ReadyToRunHelper.Box; + break; + case CorInfoHelpFunc.CORINFO_HELP_BOX_NULLABLE: + id = ReadyToRunHelper.Box_Nullable; + break; + case CorInfoHelpFunc.CORINFO_HELP_UNBOX: + id = ReadyToRunHelper.Unbox; + break; + case CorInfoHelpFunc.CORINFO_HELP_UNBOX_NULLABLE: + id = ReadyToRunHelper.Unbox_Nullable; + break; + case CorInfoHelpFunc.CORINFO_HELP_NEW_MDARR_NONVARARG: + id = ReadyToRunHelper.NewMultiDimArr_NonVarArg; + break; + case CorInfoHelpFunc.CORINFO_HELP_NEWFAST: + id = ReadyToRunHelper.NewObject; + break; + case CorInfoHelpFunc.CORINFO_HELP_NEWSFAST: + return _compilation.NodeFactory.ExternSymbol("RhpNewFast"); + case CorInfoHelpFunc.CORINFO_HELP_NEWSFAST_FINALIZE: + return _compilation.NodeFactory.ExternSymbol("RhpNewFinalizable"); + case CorInfoHelpFunc.CORINFO_HELP_NEWSFAST_ALIGN8: + return _compilation.NodeFactory.ExternSymbol("RhpNewFastAlign8"); + case CorInfoHelpFunc.CORINFO_HELP_NEWSFAST_ALIGN8_FINALIZE: + return _compilation.NodeFactory.ExternSymbol("RhpNewFinalizableAlign8"); + case CorInfoHelpFunc.CORINFO_HELP_NEWSFAST_ALIGN8_VC: + return _compilation.NodeFactory.ExternSymbol("RhpNewFastMisalign"); + case CorInfoHelpFunc.CORINFO_HELP_NEWARR_1_DIRECT: + id = ReadyToRunHelper.NewArray; + break; + case CorInfoHelpFunc.CORINFO_HELP_NEWARR_1_ALIGN8: + return _compilation.NodeFactory.ExternSymbol("RhpNewArrayAlign8"); + case CorInfoHelpFunc.CORINFO_HELP_NEWARR_1_VC: + return _compilation.NodeFactory.ExternSymbol("RhpNewArray"); + + case CorInfoHelpFunc.CORINFO_HELP_STACK_PROBE: + return _compilation.NodeFactory.ExternSymbol("RhpStackProbe"); + + case CorInfoHelpFunc.CORINFO_HELP_POLL_GC: + return _compilation.NodeFactory.ExternSymbol("RhpGcPoll"); + + case CorInfoHelpFunc.CORINFO_HELP_LMUL: + id = ReadyToRunHelper.LMul; + break; + case CorInfoHelpFunc.CORINFO_HELP_LMUL_OVF: + id = ReadyToRunHelper.LMulOfv; + break; + case CorInfoHelpFunc.CORINFO_HELP_ULMUL_OVF: + id = ReadyToRunHelper.ULMulOvf; + break; + case CorInfoHelpFunc.CORINFO_HELP_LDIV: + id = ReadyToRunHelper.LDiv; + break; + case CorInfoHelpFunc.CORINFO_HELP_LMOD: + id = ReadyToRunHelper.LMod; + break; + case CorInfoHelpFunc.CORINFO_HELP_ULDIV: + id = ReadyToRunHelper.ULDiv; + break; + case CorInfoHelpFunc.CORINFO_HELP_ULMOD: + id = ReadyToRunHelper.ULMod; + break; + case CorInfoHelpFunc.CORINFO_HELP_LLSH: + id = ReadyToRunHelper.LLsh; + break; + case CorInfoHelpFunc.CORINFO_HELP_LRSH: + id = ReadyToRunHelper.LRsh; + break; + case CorInfoHelpFunc.CORINFO_HELP_LRSZ: + id = ReadyToRunHelper.LRsz; + break; + case CorInfoHelpFunc.CORINFO_HELP_LNG2DBL: + id = ReadyToRunHelper.Lng2Dbl; + break; + case CorInfoHelpFunc.CORINFO_HELP_ULNG2DBL: + id = ReadyToRunHelper.ULng2Dbl; + break; + + case CorInfoHelpFunc.CORINFO_HELP_DIV: + id = ReadyToRunHelper.Div; + break; + case CorInfoHelpFunc.CORINFO_HELP_MOD: + id = ReadyToRunHelper.Mod; + break; + case CorInfoHelpFunc.CORINFO_HELP_UDIV: + id = ReadyToRunHelper.UDiv; + break; + case CorInfoHelpFunc.CORINFO_HELP_UMOD: + id = ReadyToRunHelper.UMod; + break; + + case CorInfoHelpFunc.CORINFO_HELP_DBL2INT: + id = ReadyToRunHelper.Dbl2Int; + break; + case CorInfoHelpFunc.CORINFO_HELP_DBL2INT_OVF: + id = ReadyToRunHelper.Dbl2IntOvf; + break; + case CorInfoHelpFunc.CORINFO_HELP_DBL2LNG: + id = ReadyToRunHelper.Dbl2Lng; + break; + case CorInfoHelpFunc.CORINFO_HELP_DBL2LNG_OVF: + id = ReadyToRunHelper.Dbl2LngOvf; + break; + case CorInfoHelpFunc.CORINFO_HELP_DBL2UINT: + id = ReadyToRunHelper.Dbl2UInt; + break; + case CorInfoHelpFunc.CORINFO_HELP_DBL2UINT_OVF: + id = ReadyToRunHelper.Dbl2UIntOvf; + break; + case CorInfoHelpFunc.CORINFO_HELP_DBL2ULNG: + id = ReadyToRunHelper.Dbl2ULng; + break; + case CorInfoHelpFunc.CORINFO_HELP_DBL2ULNG_OVF: + id = ReadyToRunHelper.Dbl2ULngOvf; + break; + + case CorInfoHelpFunc.CORINFO_HELP_FLTREM: + id = ReadyToRunHelper.FltRem; + break; + case CorInfoHelpFunc.CORINFO_HELP_DBLREM: + id = ReadyToRunHelper.DblRem; + break; + case CorInfoHelpFunc.CORINFO_HELP_FLTROUND: + id = ReadyToRunHelper.FltRound; + break; + case CorInfoHelpFunc.CORINFO_HELP_DBLROUND: + id = ReadyToRunHelper.DblRound; + break; + + case CorInfoHelpFunc.CORINFO_HELP_JIT_PINVOKE_BEGIN: + id = ReadyToRunHelper.PInvokeBegin; + break; + case CorInfoHelpFunc.CORINFO_HELP_JIT_PINVOKE_END: + id = ReadyToRunHelper.PInvokeEnd; + break; + + case CorInfoHelpFunc.CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER: + id = ReadyToRunHelper.ReversePInvokeEnter; + break; + case CorInfoHelpFunc.CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT: + id = ReadyToRunHelper.ReversePInvokeExit; + break; + + case CorInfoHelpFunc.CORINFO_HELP_CHKCASTANY: + id = ReadyToRunHelper.CheckCastAny; + break; + case CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFANY: + id = ReadyToRunHelper.CheckInstanceAny; + break; + case CorInfoHelpFunc.CORINFO_HELP_CHKCASTCLASS: + case CorInfoHelpFunc.CORINFO_HELP_CHKCASTCLASS_SPECIAL: + // TODO: separate helper for the _SPECIAL case + id = ReadyToRunHelper.CheckCastClass; + break; + case CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFCLASS: + id = ReadyToRunHelper.CheckInstanceClass; + break; + case CorInfoHelpFunc.CORINFO_HELP_CHKCASTARRAY: + id = ReadyToRunHelper.CheckCastArray; + break; + case CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFARRAY: + id = ReadyToRunHelper.CheckInstanceArray; + break; + case CorInfoHelpFunc.CORINFO_HELP_CHKCASTINTERFACE: + id = ReadyToRunHelper.CheckCastInterface; + break; + case CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFINTERFACE: + id = ReadyToRunHelper.CheckInstanceInterface; + break; + + case CorInfoHelpFunc.CORINFO_HELP_MON_ENTER: + id = ReadyToRunHelper.MonitorEnter; + break; + case CorInfoHelpFunc.CORINFO_HELP_MON_EXIT: + id = ReadyToRunHelper.MonitorExit; + break; + + case CorInfoHelpFunc.CORINFO_HELP_MON_ENTER_STATIC: + id = ReadyToRunHelper.MonitorEnterStatic; + break; + case CorInfoHelpFunc.CORINFO_HELP_MON_EXIT_STATIC: + id = ReadyToRunHelper.MonitorExitStatic; + break; + + case CorInfoHelpFunc.CORINFO_HELP_GVMLOOKUP_FOR_SLOT: + id = ReadyToRunHelper.GVMLookupForSlot; + break; + + case CorInfoHelpFunc.CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL: + id = ReadyToRunHelper.TypeHandleToRuntimeType; + break; + case CorInfoHelpFunc.CORINFO_HELP_GETREFANY: + id = ReadyToRunHelper.GetRefAny; + break; + case CorInfoHelpFunc.CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL: + id = ReadyToRunHelper.TypeHandleToRuntimeTypeHandle; + break; + + case CorInfoHelpFunc.CORINFO_HELP_GETCURRENTMANAGEDTHREADID: + id = ReadyToRunHelper.GetCurrentManagedThreadId; + break; + + default: + throw new NotImplementedException(ftnNum.ToString()); + } + + string mangledName; + MethodDesc methodDesc; + JitHelper.GetEntryPoint(_compilation.TypeSystemContext, id, out mangledName, out methodDesc); + Debug.Assert(mangledName != null || methodDesc != null); + + ISymbolNode entryPoint; + if (mangledName != null) + entryPoint = _compilation.NodeFactory.ExternSymbol(mangledName); + else + entryPoint = _compilation.NodeFactory.MethodEntrypoint(methodDesc); + + return entryPoint; + } + + private void getFunctionEntryPoint(CORINFO_METHOD_STRUCT_* ftn, ref CORINFO_CONST_LOOKUP pResult, CORINFO_ACCESS_FLAGS accessFlags) + { + MethodDesc method = HandleToObject(ftn); + + // TODO: Implement MapMethodDeclToMethodImpl from CoreCLR + if (method.IsVirtual && + method.OwningType is MetadataType mdType && + mdType.VirtualMethodImplsForType.Length > 0) + { + throw new NotImplementedException("getFunctionEntryPoint"); + } + + pResult = CreateConstLookupToSymbol(_compilation.NodeFactory.MethodEntrypoint(method)); + } + + private bool canTailCall(CORINFO_METHOD_STRUCT_* callerHnd, CORINFO_METHOD_STRUCT_* declaredCalleeHnd, CORINFO_METHOD_STRUCT_* exactCalleeHnd, bool fIsTailPrefix) + { + // Assume we can tail call unless proved otherwise + bool result = true; + + if (!fIsTailPrefix) + { + MethodDesc caller = HandleToObject(callerHnd); + + if (caller.IsNoInlining) + { + // Do not tailcall from methods that are marked as noinline (people often use no-inline + // to mean "I want to always see this method in stacktrace") + result = false; + } + } + + return result; + } + + private InfoAccessType constructStringLiteral(CORINFO_MODULE_STRUCT_* module, mdToken metaTok, ref void* ppValue) + { + MethodIL methodIL = (MethodIL)HandleToObject((IntPtr)module); + object literal = methodIL.GetObject((int)metaTok); + ISymbolNode stringObject = _compilation.NodeFactory.SerializedStringObject((string)literal); + ppValue = (void*)ObjectToHandle(stringObject); + return stringObject.RepresentsIndirectionCell ? InfoAccessType.IAT_PVALUE : InfoAccessType.IAT_VALUE; + } + + enum RhEHClauseKind + { + RH_EH_CLAUSE_TYPED = 0, + RH_EH_CLAUSE_FAULT = 1, + RH_EH_CLAUSE_FILTER = 2 + } + + private ObjectNode.ObjectData EncodeEHInfo() + { + var builder = new ObjectDataBuilder(); + builder.RequireInitialAlignment(1); + + int totalClauses = _ehClauses.Length; + + // Count the number of special markers that will be needed + for (int i = 1; i < _ehClauses.Length; i++) + { + ref CORINFO_EH_CLAUSE clause = ref _ehClauses[i]; + ref CORINFO_EH_CLAUSE previousClause = ref _ehClauses[i - 1]; + + if ((previousClause.TryOffset == clause.TryOffset) && + (previousClause.TryLength == clause.TryLength) && + ((clause.Flags & CORINFO_EH_CLAUSE_FLAGS.CORINFO_EH_CLAUSE_SAMETRY) == 0)) + { + totalClauses++; + } + } + + builder.EmitCompressedUInt((uint)totalClauses); + + for (int i = 0; i < _ehClauses.Length; i++) + { + ref CORINFO_EH_CLAUSE clause = ref _ehClauses[i]; + + if (i > 0) + { + ref CORINFO_EH_CLAUSE previousClause = ref _ehClauses[i - 1]; + + // If the previous clause has same try offset and length as the current clause, + // but belongs to a different try block (CORINFO_EH_CLAUSE_SAMETRY is not set), + // emit a special marker to allow runtime distinguish this case. + if ((previousClause.TryOffset == clause.TryOffset) && + (previousClause.TryLength == clause.TryLength) && + ((clause.Flags & CORINFO_EH_CLAUSE_FLAGS.CORINFO_EH_CLAUSE_SAMETRY) == 0)) + { + builder.EmitCompressedUInt(0); + builder.EmitCompressedUInt((uint)RhEHClauseKind.RH_EH_CLAUSE_FAULT); + builder.EmitCompressedUInt(0); + } + } + + RhEHClauseKind clauseKind; + + if (((clause.Flags & CORINFO_EH_CLAUSE_FLAGS.CORINFO_EH_CLAUSE_FAULT) != 0) || + ((clause.Flags & CORINFO_EH_CLAUSE_FLAGS.CORINFO_EH_CLAUSE_FINALLY) != 0)) + { + clauseKind = RhEHClauseKind.RH_EH_CLAUSE_FAULT; + } + else + if ((clause.Flags & CORINFO_EH_CLAUSE_FLAGS.CORINFO_EH_CLAUSE_FILTER) != 0) + { + clauseKind = RhEHClauseKind.RH_EH_CLAUSE_FILTER; + } + else + { + clauseKind = RhEHClauseKind.RH_EH_CLAUSE_TYPED; + } + + builder.EmitCompressedUInt((uint)clause.TryOffset); + + // clause.TryLength returned by the JIT is actually end offset... + // https://github.com/dotnet/runtime/issues/5282 + int tryLength = (int)clause.TryLength - (int)clause.TryOffset; + builder.EmitCompressedUInt((uint)((tryLength << 2) | (int)clauseKind)); + + switch (clauseKind) + { + case RhEHClauseKind.RH_EH_CLAUSE_TYPED: + { + builder.EmitCompressedUInt(clause.HandlerOffset); + + var methodIL = (MethodIL)HandleToObject((IntPtr)_methodScope); + var type = (TypeDesc)methodIL.GetObject((int)clause.ClassTokenOrOffset); + + // Once https://github.com/dotnet/corert/issues/3460 is done, this should be an assert. + // Throwing InvalidProgram is not great, but we want to do *something* if this happens + // because doing nothing means problems at runtime. This is not worth piping a + // a new exception with a fancy message for. + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + ThrowHelper.ThrowInvalidProgramException(); + + var typeSymbol = _compilation.NecessaryTypeSymbolIfPossible(type); + + RelocType rel = (_compilation.NodeFactory.Target.IsWindows) ? + RelocType.IMAGE_REL_BASED_ABSOLUTE : + RelocType.IMAGE_REL_BASED_RELPTR32; + + if (_compilation.NodeFactory.Target.Abi == TargetAbi.Jit) + rel = RelocType.IMAGE_REL_BASED_REL32; + + builder.EmitReloc(typeSymbol, rel); + } + break; + case RhEHClauseKind.RH_EH_CLAUSE_FAULT: + builder.EmitCompressedUInt(clause.HandlerOffset); + break; + case RhEHClauseKind.RH_EH_CLAUSE_FILTER: + builder.EmitCompressedUInt(clause.HandlerOffset); + builder.EmitCompressedUInt(clause.ClassTokenOrOffset); + break; + } + } + + return builder.ToObjectData(); + } + + private void setVars(CORINFO_METHOD_STRUCT_* ftn, uint cVars, NativeVarInfo* vars) + { + var methodIL = (MethodIL)HandleToObject((IntPtr)_methodScope); + + MethodSignature sig = methodIL.OwningMethod.Signature; + int numLocals = methodIL.GetLocals().Length; + + ArrayBuilder[] debugVarInfoBuilders = new ArrayBuilder[(sig.IsStatic ? 0 : 1) + sig.Length + numLocals]; + + for (uint i = 0; i < cVars; i++) + { + uint varNumber = vars[i].varNumber; + if (varNumber < debugVarInfoBuilders.Length) + debugVarInfoBuilders[varNumber].Add(new DebugVarRangeInfo(vars[i].startOffset, vars[i].endOffset, vars[i].varLoc)); + } + + var debugVarInfos = new ArrayBuilder(); + for (uint i = 0; i < debugVarInfoBuilders.Length; i++) + { + if (debugVarInfoBuilders[i].Count > 0) + { + debugVarInfos.Add(new DebugVarInfo(i, debugVarInfoBuilders[i].ToArray())); + } + } + + _debugVarInfos = debugVarInfos.ToArray(); + + // JIT gave the ownership of this to us, so need to free this. + freeArray(vars); + } + + /// + /// Create a DebugLocInfo which is a table from native offset to source line. + /// using native to il offset (pMap) and il to source line (_sequencePoints). + /// + private void setBoundaries(CORINFO_METHOD_STRUCT_* ftn, uint cMap, OffsetMapping* pMap) + { + Debug.Assert(_debugLocInfos == null); + + int largestILOffset = 0; // All epiloges point to the largest IL offset. + for (int i = 0; i < cMap; i++) + { + OffsetMapping nativeToILInfo = pMap[i]; + int currectILOffset = (int)nativeToILInfo.ilOffset; + if (currectILOffset > largestILOffset) // Special offsets are negative. + { + largestILOffset = currectILOffset; + } + } + + ArrayBuilder debugLocInfos = new ArrayBuilder(); + for (int i = 0; i < cMap; i++) + { + OffsetMapping* nativeToILInfo = &pMap[i]; + int ilOffset = (int)nativeToILInfo->ilOffset; + switch (ilOffset) + { + case (int)MappingTypes.PROLOG: + ilOffset = 0; + break; + case (int)MappingTypes.EPILOG: + ilOffset = largestILOffset; + break; + case (int)MappingTypes.NO_MAPPING: + continue; + } + + debugLocInfos.Add(new DebugLocInfo((int)nativeToILInfo->nativeOffset, ilOffset)); + } + + if (debugLocInfos.Count > 0) + { + _debugLocInfos = debugLocInfos.ToArray(); + } + + freeArray(pMap); + } + + private void SetDebugInformation(IMethodNode methodCodeNodeNeedingCode, MethodIL methodIL) + { + _debugInfo = _compilation.GetDebugInfo(methodIL); + } + + private ISymbolNode GetGenericLookupHelper(CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, ReadyToRunHelperId helperId, object helperArgument) + { + if (runtimeLookupKind == CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ + || runtimeLookupKind == CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_CLASSPARAM) + { + return _compilation.NodeFactory.ReadyToRunHelperFromTypeLookup(helperId, helperArgument, MethodBeingCompiled.OwningType); + } + + Debug.Assert(runtimeLookupKind == CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_METHODPARAM); + return _compilation.NodeFactory.ReadyToRunHelperFromDictionaryLookup(helperId, helperArgument, MethodBeingCompiled); + } + + private CorInfoHelpFunc getCastingHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, bool fThrowing) + { + TypeDesc type = HandleToObject(pResolvedToken.hClass); + + CorInfoHelpFunc helper; + + if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) + { + // In shared code just use the catch-all helper for type variables, as the same + // code may be used for interface/array/class instantiations + // + // We may be able to take advantage of constraints to select a specialized helper. + // This optimizations does not seem to be warranted at the moment. + helper = CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFANY; + } + else if (type.IsInterface) + { + // If it is an interface, use the fast interface helper + helper = CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFINTERFACE; + } + else if (type.IsArray) + { + // If it is an array, use the fast array helper + helper = CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFARRAY; + } + else if (type.IsDefType) + { + helper = CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFCLASS; +#if !SUPPORT_JIT + // When this assert is hit, we'll have to do something with the class checks in RyuJIT + // Frozen strings might end up failing inlined checks generated by RyuJIT for sealed classes. + Debug.Assert(!_compilation.NodeFactory.CompilationModuleGroup.CanHaveReferenceThroughImportTable); +#endif + } + else + { + // Otherwise, use the slow helper + helper = CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFANY; + } + + if (fThrowing) + { + int delta = CorInfoHelpFunc.CORINFO_HELP_CHKCASTANY - CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFANY; + + Debug.Assert(CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFINTERFACE + delta + == CorInfoHelpFunc.CORINFO_HELP_CHKCASTINTERFACE); + Debug.Assert(CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFARRAY + delta + == CorInfoHelpFunc.CORINFO_HELP_CHKCASTARRAY); + Debug.Assert(CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFCLASS + delta + == CorInfoHelpFunc.CORINFO_HELP_CHKCASTCLASS); + + helper += delta; + } + + return helper; + } + + private CorInfoHelpFunc getNewHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_METHOD_STRUCT_* callerHandle, ref bool pHasSideEffects) + { + TypeDesc type = HandleToObject(pResolvedToken.hClass); + + Debug.Assert(!type.IsString && !type.IsArray && !type.IsCanonicalDefinitionType(CanonicalFormKind.Any)); + + pHasSideEffects = type.HasFinalizer; + + if (type.RequiresAlign8()) + { + if (type.HasFinalizer) + return CorInfoHelpFunc.CORINFO_HELP_NEWSFAST_ALIGN8_FINALIZE; + + if (type.IsValueType) + return CorInfoHelpFunc.CORINFO_HELP_NEWSFAST_ALIGN8_VC; + + return CorInfoHelpFunc.CORINFO_HELP_NEWSFAST_ALIGN8; + } + + if (type.HasFinalizer) + return CorInfoHelpFunc.CORINFO_HELP_NEWSFAST_FINALIZE; + + return CorInfoHelpFunc.CORINFO_HELP_NEWSFAST; + } + + private CorInfoHelpFunc getNewArrHelper(CORINFO_CLASS_STRUCT_* arrayCls) + { + TypeDesc type = HandleToObject(arrayCls); + + Debug.Assert(type.IsArray); + + if (type.RequiresAlign8()) + return CorInfoHelpFunc.CORINFO_HELP_NEWARR_1_ALIGN8; + + return CorInfoHelpFunc.CORINFO_HELP_NEWARR_1_VC; + } + + private IMethodNode GetMethodEntrypoint(CORINFO_MODULE_STRUCT_* pScope, MethodDesc method) + { + bool isUnboxingThunk = method.IsUnboxingThunk(); + if (isUnboxingThunk) + { + method = method.GetUnboxedMethod(); + } + + if (method.HasInstantiation || method.OwningType.HasInstantiation) + { + MethodIL methodIL = (MethodIL)HandleToObject((IntPtr)pScope); + _compilation.DetectGenericCycles(methodIL.OwningMethod, method); + } + + return _compilation.NodeFactory.MethodEntrypoint(method, isUnboxingThunk); + } + + private static bool IsTypeSpecForTypicalInstantiation(TypeDesc t) + { + Instantiation inst = t.Instantiation; + for (int i = 0; i < inst.Length; i++) + { + var arg = inst[i] as SignatureTypeVariable; + if (arg == null || arg.Index != i) + return false; + } + return true; + } + + private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_METHOD_STRUCT_* callerHandle, CORINFO_CALLINFO_FLAGS flags, CORINFO_CALL_INFO* pResult) + { +#if DEBUG + // In debug, write some bogus data to the struct to ensure we have filled everything + // properly. + MemoryHelper.FillMemory((byte*)pResult, 0xcc, Marshal.SizeOf()); +#endif + MethodDesc method = HandleToObject(pResolvedToken.hMethod); + + // Spec says that a callvirt lookup ignores static methods. Since static methods + // can't have the exact same signature as instance methods, a lookup that found + // a static method would have never found an instance method. + if (method.Signature.IsStatic && (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) != 0) + { + throw new BadImageFormatException(); + } + + // This block enforces the rule that methods with [UnmanagedCallersOnly] attribute + // can only be called from unmanaged code. The call from managed code is replaced + // with a stub that throws an InvalidProgramException + if (method.IsUnmanagedCallersOnly && (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) == 0) + { + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramUnmanagedCallersOnly, method); + } + + TypeDesc exactType = HandleToObject(pResolvedToken.hClass); + + TypeDesc constrainedType = null; + if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) != 0 && pConstrainedResolvedToken != null) + { + constrainedType = HandleToObject(pConstrainedResolvedToken->hClass); + } + + bool resolvedConstraint = false; + bool forceUseRuntimeLookup = false; + bool targetIsFatFunctionPointer = false; + bool useFatCallTransform = false; + + MethodDesc methodAfterConstraintResolution = method; + if (constrainedType == null) + { + pResult->thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM; + } + else + { + // We have a "constrained." call. Try a partial resolve of the constraint call. Note that this + // will not necessarily resolve the call exactly, since we might be compiling + // shared generic code - it may just resolve it to a candidate suitable for + // JIT compilation, and require a runtime lookup for the actual code pointer + // to call. + + MethodDesc directMethod = constrainedType.GetClosestDefType().TryResolveConstraintMethodApprox(exactType, method, out forceUseRuntimeLookup); + if (directMethod == null && constrainedType.IsEnum) + { + // Constrained calls to methods on enum methods resolve to System.Enum's methods. System.Enum is a reference + // type though, so we would fail to resolve and box. We have a special path for those to avoid boxing. + directMethod = _compilation.TypeSystemContext.TryResolveConstrainedEnumMethod(constrainedType, method); + } + + if (directMethod != null) + { + // Either + // 1. no constraint resolution at compile time (!directMethod) + // OR 2. no code sharing lookup in call + // OR 3. we have have resolved to an instantiating stub + + methodAfterConstraintResolution = directMethod; + + Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface); + resolvedConstraint = true; + pResult->thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM; + + exactType = constrainedType; + } + else if (constrainedType.IsValueType) + { + pResult->thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_BOX_THIS; + } + else + { + pResult->thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_DEREF_THIS; + } + } + + MethodDesc targetMethod = methodAfterConstraintResolution; + + // + // Initialize callee context used for inlining and instantiation arguments + // + + + if (targetMethod.HasInstantiation) + { + pResult->contextHandle = contextFromMethod(targetMethod); + pResult->exactContextNeedsRuntimeLookup = targetMethod.IsSharedByGenericInstantiations; + } + else + { + pResult->contextHandle = contextFromType(exactType); + pResult->exactContextNeedsRuntimeLookup = exactType.IsCanonicalSubtype(CanonicalFormKind.Any); + + // Use main method as the context as long as the methods are called on the same type + if (pResult->exactContextNeedsRuntimeLookup && + pResolvedToken.tokenContext == contextFromMethodBeingCompiled() && + constrainedType == null && + exactType == MethodBeingCompiled.OwningType && + // But don't allow inlining into generic methods since the generic context won't be the same. + // The scanner won't be able to predict such inlinig. See https://github.com/dotnet/runtimelab/pull/489 + !MethodBeingCompiled.HasInstantiation) + { + var methodIL = (MethodIL)HandleToObject((IntPtr)pResolvedToken.tokenScope); + var rawMethod = (MethodDesc)methodIL.GetMethodILDefinition().GetObject((int)pResolvedToken.token); + if (IsTypeSpecForTypicalInstantiation(rawMethod.OwningType)) + { + pResult->contextHandle = contextFromMethodBeingCompiled(); + } + } + } + + // + // Determine whether to perform direct call + // + + bool directCall = false; + bool resolvedCallVirt = false; + + if (targetMethod.Signature.IsStatic) + { + // Static methods are always direct calls + directCall = true; + } + else if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) == 0 || resolvedConstraint) + { + directCall = true; + } + else + { + if (!targetMethod.IsVirtual || + // Final/sealed has no meaning for interfaces, but lets us devirtualize otherwise + !targetMethod.OwningType.IsInterface && (targetMethod.IsFinal || targetMethod.OwningType.IsSealed())) + { + resolvedCallVirt = true; + directCall = true; + } + } + + pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup = false; + + bool allowInstParam = (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_ALLOWINSTPARAM) != 0; + + if (directCall && targetMethod.IsAbstract) + { + ThrowHelper.ThrowBadImageFormatException(); + } + + if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg()) + { + // JIT needs a single address to call this method but the method needs a hidden argument. + // We need a fat function pointer for this that captures both things. + targetIsFatFunctionPointer = true; + + // JIT won't expect fat function pointers unless this is e.g. delegate creation + Debug.Assert((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0); + + pResult->kind = CORINFO_CALL_KIND.CORINFO_CALL_CODE_POINTER; + + if (pResult->exactContextNeedsRuntimeLookup) + { + pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup = true; + pResult->codePointerOrStubLookup.lookupKind.runtimeLookupFlags = 0; + pResult->codePointerOrStubLookup.runtimeLookup.indirections = CORINFO.USEHELPER; + + // Do not bother computing the runtime lookup if we are inlining. The JIT is going + // to abort the inlining attempt anyway. + if (pResolvedToken.tokenContext == contextFromMethodBeingCompiled()) + { + MethodDesc contextMethod = methodFromContext(pResolvedToken.tokenContext); + pResult->codePointerOrStubLookup.lookupKind.runtimeLookupKind = GetGenericRuntimeLookupKind(contextMethod); + pResult->codePointerOrStubLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.MethodEntry; + pResult->codePointerOrStubLookup.lookupKind.runtimeLookupArgs = (void*)ObjectToHandle(GetRuntimeDeterminedObjectForToken(ref pResolvedToken)); + } + else + { + pResult->codePointerOrStubLookup.lookupKind.runtimeLookupKind = CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_NOT_SUPPORTED; + } + } + else + { + pResult->codePointerOrStubLookup.constLookup = + CreateConstLookupToSymbol(_compilation.NodeFactory.FatFunctionPointer(targetMethod)); + } + } + else if (directCall && resolvedConstraint && pResult->exactContextNeedsRuntimeLookup) + { + // We want to do a direct call to a shared method on a valuetype. We need to provide + // a generic context, but the JitInterface doesn't provide a way for us to do it from here. + // So we do the next best thing and ask RyuJIT to look up a fat pointer. + + pResult->kind = CORINFO_CALL_KIND.CORINFO_CALL_CODE_POINTER; + pResult->codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; + pResult->nullInstanceCheck = true; + + // We have the canonical version of the method - find the runtime determined version. + // This is simplified because we know the method is on a valuetype. + Debug.Assert(targetMethod.OwningType.IsValueType); + TypeDesc runtimeDeterminedConstrainedType = (TypeDesc)GetRuntimeDeterminedObjectForToken(ref *pConstrainedResolvedToken); + + if (forceUseRuntimeLookup) + { + // The below logic would incorrectly resolve the lookup into the first match we found, + // but there was a compile-time ambiguity due to shared code. The correct fix should + // use the ConstrainedMethodUseLookupResult dictionary entry so that the exact + // dispatch can be computed with the help of the generic dictionary. + // We fail the compilation here to avoid bad codegen. This is not actually an invalid program. + // https://github.com/dotnet/runtimelab/issues/1431 + ThrowHelper.ThrowInvalidProgramException(); + } + + MethodDesc targetOfLookup; + if (runtimeDeterminedConstrainedType.IsRuntimeDeterminedType) + targetOfLookup = _compilation.TypeSystemContext.GetMethodForRuntimeDeterminedType(targetMethod.GetTypicalMethodDefinition(), (RuntimeDeterminedType)runtimeDeterminedConstrainedType); + else + targetOfLookup = _compilation.TypeSystemContext.GetMethodForInstantiatedType(targetMethod.GetTypicalMethodDefinition(), (InstantiatedType)runtimeDeterminedConstrainedType); + if (targetOfLookup.HasInstantiation) + { + var methodToGetInstantiation = (MethodDesc)GetRuntimeDeterminedObjectForToken(ref pResolvedToken); + targetOfLookup = targetOfLookup.MakeInstantiatedMethod(methodToGetInstantiation.Instantiation); + } + Debug.Assert(targetOfLookup.GetCanonMethodTarget(CanonicalFormKind.Specific) == targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)); + + ComputeLookup(ref pResolvedToken, + targetOfLookup, + ReadyToRunHelperId.MethodEntry, + ref pResult->codePointerOrStubLookup); + + targetIsFatFunctionPointer = true; + useFatCallTransform = true; + } + else if (directCall) + { + bool referencingArrayAddressMethod = false; + + if (targetMethod.IsIntrinsic) + { + // If this is an intrinsic method with a callsite-specific expansion, this will replace + // the method with a method the intrinsic expands into. If it's not the special intrinsic, + // method stays unchanged. + var methodIL = (MethodIL)HandleToObject((IntPtr)pResolvedToken.tokenScope); + targetMethod = _compilation.ExpandIntrinsicForCallsite(targetMethod, methodIL.OwningMethod); + + // For multidim array Address method, we pretend the method requires a hidden instantiation argument + // (even though it doesn't need one). We'll actually swap the method out for a differnt one with + // a matching calling convention later. See ArrayMethod for a description. + referencingArrayAddressMethod = targetMethod.IsArrayAddressMethod(); + } + + pResult->kind = CORINFO_CALL_KIND.CORINFO_CALL; + + TypeDesc owningType = targetMethod.OwningType; + if (owningType.IsString && targetMethod.IsConstructor) + { + // Calling a string constructor doesn't call the actual constructor. + pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( + _compilation.NodeFactory.StringAllocator(targetMethod) + ); + } + else if (owningType.IsArray && targetMethod.IsConstructor) + { + // Constructors on arrays are special and don't actually have entrypoints. + // That would be fine by itself and wouldn't need special casing. But + // constructors on SzArray have a weird property that causes them not to have canonical forms. + // int[][] has a .ctor(int32,int32) to construct the jagged array in one go, but its canonical + // form of __Canon[] doesn't have the two-parameter constructor. The canonical form would need + // to have an unlimited number of constructors to cover stuff like "int[][][][][][]..." + pResult->codePointerOrStubLookup.constLookup = default; + } + else if (pResult->exactContextNeedsRuntimeLookup) + { + // Nothing to do... The generic handle lookup gets embedded in to the codegen + // during the jitting of the call. + // (Note: The generic lookup in R2R is performed by a call to a helper at runtime, not by + // codegen emitted at crossgen time) + + targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + + Debug.Assert(!forceUseRuntimeLookup); + pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( + GetMethodEntrypoint(pResolvedToken.tokenScope, targetMethod) + ); + } + else + { + MethodDesc concreteMethod = targetMethod; + targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + + ISymbolNode instParam = null; + + if (targetMethod.RequiresInstMethodDescArg()) + { + instParam = _compilation.NodeFactory.MethodGenericDictionary(concreteMethod); + } + else if (targetMethod.RequiresInstMethodTableArg() || referencingArrayAddressMethod) + { + // Ask for a constructed type symbol because we need the vtable to get to the dictionary + instParam = _compilation.NodeFactory.ConstructedTypeSymbol(concreteMethod.OwningType); + } + + if (instParam != null) + { + pResult->instParamLookup = CreateConstLookupToSymbol(instParam); + } + + pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( + GetMethodEntrypoint(pResolvedToken.tokenScope, targetMethod) + ); + } + + pResult->nullInstanceCheck = resolvedCallVirt; + } + else if (targetMethod.HasInstantiation) + { + // Generic virtual method call support + pResult->kind = CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_LDVIRTFTN; + pResult->codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; + pResult->nullInstanceCheck = true; + + MethodDesc targetOfLookup = _compilation.GetTargetOfGenericVirtualMethodCall((MethodDesc)GetRuntimeDeterminedObjectForToken(ref pResolvedToken)); + + _compilation.DetectGenericCycles( + ((MethodILScope)HandleToObject((IntPtr)pResolvedToken.tokenScope)).OwningMethod, + targetOfLookup.GetCanonMethodTarget(CanonicalFormKind.Specific)); + + ComputeLookup(ref pResolvedToken, + targetOfLookup, + ReadyToRunHelperId.MethodHandle, + ref pResult->codePointerOrStubLookup); + + // RyuJIT will assert if we report CORINFO_CALLCONV_PARAMTYPE for a result of a ldvirtftn + // We don't need an instantiation parameter, so let's just not report it. Might be nice to + // move that assert to some place later though. + targetIsFatFunctionPointer = true; + } + else if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) == 0 + && targetMethod.OwningType.IsInterface) + { + pResult->kind = CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_STUB; + + if (pResult->exactContextNeedsRuntimeLookup) + { + ComputeLookup(ref pResolvedToken, + GetRuntimeDeterminedObjectForToken(ref pResolvedToken), + ReadyToRunHelperId.VirtualDispatchCell, + ref pResult->codePointerOrStubLookup); + Debug.Assert(pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup); + } + else + { + pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup = false; + pResult->codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_PVALUE; + pResult->codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle( + _compilation.NodeFactory.InterfaceDispatchCell(targetMethod +#if !SUPPORT_JIT + , _compilation.NameMangler.GetMangledMethodName(MethodBeingCompiled).ToString() +#endif + )); + } + + pResult->nullInstanceCheck = false; + } + else if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) == 0 + // Canonically-equivalent types have the same vtable layout. Check the canonical form. + // We don't want to accidentally ask about Foo that may or may not + // be available to ask vtable questions about. + // This can happen in inlining that the scanner didn't expect. + && _compilation.HasFixedSlotVTable(targetMethod.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific))) + { + pResult->kind = CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_VTABLE; + pResult->nullInstanceCheck = true; + } + else + { + ReadyToRunHelperId helperId; + if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0) + { + pResult->kind = CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_LDVIRTFTN; + helperId = ReadyToRunHelperId.ResolveVirtualFunction; + } + else + { + // CORINFO_CALL_CODE_POINTER tells the JIT that this is indirect + // call that should not be inlined. + pResult->kind = CORINFO_CALL_KIND.CORINFO_CALL_CODE_POINTER; + helperId = ReadyToRunHelperId.VirtualCall; + } + + // If this is a non-interface call, we actually don't need a runtime lookup to find the target. + // We don't even need to keep track of the runtime-determined method being called because the system ensures + // that if e.g. Foo<__Canon>.GetHashCode is needed and we're generating a dictionary for Foo, + // Foo.GetHashCode is needed too. + if (pResult->exactContextNeedsRuntimeLookup && targetMethod.OwningType.IsInterface) + { + // We need JitInterface changes to fully support this. + // If this is LDVIRTFTN of an interface method that is part of a verifiable delegate creation sequence, + // RyuJIT is not going to use this value. + Debug.Assert(helperId == ReadyToRunHelperId.ResolveVirtualFunction); + pResult->exactContextNeedsRuntimeLookup = false; + pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ExternSymbol("NYI_LDVIRTFTN")); + } + else + { + pResult->exactContextNeedsRuntimeLookup = false; + targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + + // Get the slot defining method to make sure our virtual method use tracking gets this right. + // For normal C# code the targetMethod will always be newslot. + MethodDesc slotDefiningMethod = targetMethod.IsNewSlot ? + targetMethod : MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod); + + pResult->codePointerOrStubLookup.constLookup = + CreateConstLookupToSymbol( + _compilation.NodeFactory.ReadyToRunHelper(helperId, slotDefiningMethod)); + } + + // The current CoreRT ReadyToRun helpers do not handle null thisptr - ask the JIT to emit explicit null checks + // TODO: Optimize this + pResult->nullInstanceCheck = true; + } + + pResult->hMethod = ObjectToHandle(targetMethod); + + pResult->accessAllowed = CorInfoIsAccessAllowedResult.CORINFO_ACCESS_ALLOWED; + + // We're pretty much done at this point. Let's grab the rest of the information that the jit is going to + // need. + pResult->classFlags = getClassAttribsInternal(targetMethod.OwningType); + + pResult->methodFlags = getMethodAttribsInternal(targetMethod); + + targetIsFatFunctionPointer |= (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT) != 0 && !(pResult->kind == CORINFO_CALL_KIND.CORINFO_CALL); + + Get_CORINFO_SIG_INFO(targetMethod, &pResult->sig, targetIsFatFunctionPointer); + if (useFatCallTransform) + { + pResult->sig.flags |= CorInfoSigInfoFlags.CORINFO_SIGFLAG_FAT_CALL; + } + + if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_VERIFICATION) != 0) + { + if (pResult->hMethod != pResolvedToken.hMethod) + { + pResult->verMethodFlags = getMethodAttribsInternal(targetMethod); + Get_CORINFO_SIG_INFO(targetMethod, &pResult->verSig); + } + else + { + pResult->verMethodFlags = pResult->methodFlags; + pResult->verSig = pResult->sig; + } + } + + pResult->_wrapperDelegateInvoke = 0; + } + + private CORINFO_CLASS_STRUCT_* embedClassHandle(CORINFO_CLASS_STRUCT_* handle, ref void* ppIndirection) + { + TypeDesc type = HandleToObject(handle); + ISymbolNode typeHandleSymbol = _compilation.NecessaryTypeSymbolIfPossible(type); + CORINFO_CLASS_STRUCT_* result = (CORINFO_CLASS_STRUCT_*)ObjectToHandle(typeHandleSymbol); + + if (typeHandleSymbol.RepresentsIndirectionCell) + { + ppIndirection = result; + return null; + } + else + { + ppIndirection = null; + return result; + } + } + + private void embedGenericHandle(ref CORINFO_RESOLVED_TOKEN pResolvedToken, bool fEmbedParent, ref CORINFO_GENERICHANDLE_RESULT pResult) + { +#if DEBUG + // In debug, write some bogus data to the struct to ensure we have filled everything + // properly. + fixed (CORINFO_GENERICHANDLE_RESULT* tmp = &pResult) + MemoryHelper.FillMemory((byte*)tmp, 0xcc, Marshal.SizeOf()); +#endif + ReadyToRunHelperId helperId = ReadyToRunHelperId.Invalid; + object target = null; + + if (!fEmbedParent && pResolvedToken.hMethod != null) + { + MethodDesc md = HandleToObject(pResolvedToken.hMethod); + TypeDesc td = HandleToObject(pResolvedToken.hClass); + + pResult.handleType = CorInfoGenericHandleType.CORINFO_HANDLETYPE_METHOD; + + Debug.Assert(md.OwningType == td); + + pResult.compileTimeHandle = (CORINFO_GENERIC_STRUCT_*)ObjectToHandle(md); + + if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Ldtoken) + helperId = ReadyToRunHelperId.MethodHandle; + else + { + Debug.Assert(pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Method); + helperId = ReadyToRunHelperId.MethodDictionary; + } + + target = GetRuntimeDeterminedObjectForToken(ref pResolvedToken); + } + else if (!fEmbedParent && pResolvedToken.hField != null) + { + FieldDesc fd = HandleToObject(pResolvedToken.hField); + TypeDesc td = HandleToObject(pResolvedToken.hClass); + + pResult.handleType = CorInfoGenericHandleType.CORINFO_HANDLETYPE_FIELD; + pResult.compileTimeHandle = (CORINFO_GENERIC_STRUCT_*)pResolvedToken.hField; + + Debug.Assert(pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Ldtoken); + helperId = ReadyToRunHelperId.FieldHandle; + target = GetRuntimeDeterminedObjectForToken(ref pResolvedToken); + } + else + { + TypeDesc td = HandleToObject(pResolvedToken.hClass); + + pResult.handleType = CorInfoGenericHandleType.CORINFO_HANDLETYPE_CLASS; + pResult.compileTimeHandle = (CORINFO_GENERIC_STRUCT_*)pResolvedToken.hClass; + + object obj = GetRuntimeDeterminedObjectForToken(ref pResolvedToken); + target = obj as TypeDesc; + if (target == null) + { + Debug.Assert(fEmbedParent); + + if (obj is MethodDesc objAsMethod) + { + target = objAsMethod.OwningType; + } + else + { + Debug.Assert(obj is FieldDesc); + target = ((FieldDesc)obj).OwningType; + } + } + + if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_NewObj + || pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Newarr + || pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Box + || pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Constrained) + { + helperId = ReadyToRunHelperId.TypeHandle; + } + else if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Casting) + { + helperId = ReadyToRunHelperId.TypeHandleForCasting; + } + else if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Ldtoken) + { + helperId = _compilation.GetLdTokenHelperForType(td); + } + else + { + helperId = ReadyToRunHelperId.NecessaryTypeHandle; + } + } + + Debug.Assert(pResult.compileTimeHandle != null); + + ComputeLookup(ref pResolvedToken, target, helperId, ref pResult.lookup); + } + + private CORINFO_METHOD_STRUCT_* embedMethodHandle(CORINFO_METHOD_STRUCT_* handle, ref void* ppIndirection) + { + MethodDesc method = HandleToObject(handle); + ISymbolNode methodHandleSymbol = _compilation.NodeFactory.RuntimeMethodHandle(method); + CORINFO_METHOD_STRUCT_* result = (CORINFO_METHOD_STRUCT_*)ObjectToHandle(methodHandleSymbol); + + if (methodHandleSymbol.RepresentsIndirectionCell) + { + ppIndirection = result; + return null; + } + else + { + ppIndirection = null; + return result; + } + } + + private void getMethodVTableOffset(CORINFO_METHOD_STRUCT_* method, ref uint offsetOfIndirection, ref uint offsetAfterIndirection, ref bool isRelative) + { + MethodDesc methodDesc = HandleToObject(method); + int pointerSize = _compilation.TypeSystemContext.Target.PointerSize; + offsetOfIndirection = (uint)CORINFO_VIRTUALCALL_NO_CHUNK.Value; + isRelative = false; + + // Normalize to the slot defining method. We don't have slot information for the overrides. + methodDesc = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(methodDesc); + Debug.Assert(!methodDesc.CanMethodBeInSealedVTable()); + + // Avoid asking about slots on types like Foo. We might not have that information. + // Canonically-equivalent types have the same slots, so ask for Foo<__Canon, __Canon>. + methodDesc = methodDesc.GetCanonMethodTarget(CanonicalFormKind.Specific); + + int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(_compilation.NodeFactory, methodDesc, methodDesc.OwningType); + if (slot == -1) + { + throw new InvalidOperationException(methodDesc.ToString()); + } + + offsetAfterIndirection = (uint)(EETypeNode.GetVTableOffset(pointerSize) + slot * pointerSize); + } + + private void expandRawHandleIntrinsic(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref CORINFO_GENERICHANDLE_RESULT pResult) + { + // Resolved token as a potentially RuntimeDetermined object. + MethodDesc method = (MethodDesc)GetRuntimeDeterminedObjectForToken(ref pResolvedToken); + + switch (method.Name) + { + case "EETypePtrOf": + case "MethodTableOf": + ComputeLookup(ref pResolvedToken, method.Instantiation[0], ReadyToRunHelperId.TypeHandle, ref pResult.lookup); + break; + case "DefaultConstructorOf": + ComputeLookup(ref pResolvedToken, method.Instantiation[0], ReadyToRunHelperId.DefaultConstructor, ref pResult.lookup); + break; + case "AllocatorOf": + ComputeLookup(ref pResolvedToken, method.Instantiation[0], ReadyToRunHelperId.ObjectAllocator, ref pResult.lookup); + break; + } + } + + private uint getMethodAttribs(CORINFO_METHOD_STRUCT_* ftn) + { + return getMethodAttribsInternal(HandleToObject(ftn)); + } + + private void* getMethodSync(CORINFO_METHOD_STRUCT_* ftn, ref void* ppIndirection) + { + MethodDesc method = HandleToObject(ftn); + TypeDesc type = method.OwningType; + ISymbolNode methodSync = _compilation.NecessaryTypeSymbolIfPossible(type); + + void* result = (void*)ObjectToHandle(methodSync); + + if (methodSync.RepresentsIndirectionCell) + { + ppIndirection = result; + return null; + } + else + { + ppIndirection = null; + return result; + } + } + + private unsafe HRESULT allocPgoInstrumentationBySchema(CORINFO_METHOD_STRUCT_* ftnHnd, PgoInstrumentationSchema* pSchema, uint countSchemaItems, byte** pInstrumentationData) + { + throw new NotImplementedException("allocPgoInstrumentationBySchema"); + } + + private CORINFO_CLASS_STRUCT_* getLikelyClass(CORINFO_METHOD_STRUCT_* ftnHnd, CORINFO_CLASS_STRUCT_* baseHnd, uint IlOffset, ref uint pLikelihood, ref uint pNumberOfClasses) + { + return null; + } + + private void getAddressOfPInvokeTarget(CORINFO_METHOD_STRUCT_* method, ref CORINFO_CONST_LOOKUP pLookup) + { + MethodDesc md = HandleToObject(method); + + string externName = _compilation.PInvokeILProvider.GetDirectCallExternName(md); + + pLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ExternSymbol(externName)); + } + + private void getGSCookie(IntPtr* pCookieVal, IntPtr** ppCookieVal) + { + // TODO: fully implement GS cookies + + if (pCookieVal != null) + { + if (PointerSize == 4) + { + *pCookieVal = (IntPtr)0x3F796857; + } + else + { + *pCookieVal = (IntPtr)0x216D6F6D202C6948; + } + *ppCookieVal = null; + } + else + { + throw new NotImplementedException("getGSCookie"); + } + } + + private bool pInvokeMarshalingRequired(CORINFO_METHOD_STRUCT_* handle, CORINFO_SIG_INFO* callSiteSig) + { + // calli is covered by convertPInvokeCalliToCall + if (handle == null) + { +#if DEBUG + MethodSignature methodSignature = (MethodSignature)HandleToObject((IntPtr)callSiteSig->pSig); + + MethodDesc stub = _compilation.PInvokeILProvider.GetCalliStub(methodSignature); + Debug.Assert(!IsPInvokeStubRequired(stub)); +#endif + + return false; + } + + MethodDesc method = HandleToObject(handle); + + if (method.IsRawPInvoke()) + return false; + + // Stub is required to trigger precise static constructor + TypeDesc owningType = method.OwningType; + if (_compilation.HasLazyStaticConstructor(owningType) && !((MetadataType)owningType).IsBeforeFieldInit) + return true; + + // We could have given back the PInvoke stub IL to the JIT and let it inline it, without + // checking whether there is any stub required. Save the JIT from doing the inlining by checking upfront. + return IsPInvokeStubRequired(method); + } + + private bool convertPInvokeCalliToCall(ref CORINFO_RESOLVED_TOKEN pResolvedToken, bool mustConvert) + { + var methodIL = (MethodIL)HandleToObject((IntPtr)pResolvedToken.tokenScope); + + // Suppress recursive expansion of calli in marshaling stubs + if (methodIL is Internal.IL.Stubs.PInvokeILStubMethodIL) + return false; + + MethodSignature signature = (MethodSignature)methodIL.GetObject((int)pResolvedToken.token); + if ((signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) == 0) + return false; + + MethodDesc stub = _compilation.PInvokeILProvider.GetCalliStub(signature); + if (!mustConvert && !IsPInvokeStubRequired(stub)) + return false; + + pResolvedToken.hMethod = ObjectToHandle(stub); + pResolvedToken.hClass = ObjectToHandle(stub.OwningType); + return true; + } + + private bool IsPInvokeStubRequired(MethodDesc method) + { + if (_compilation.GetMethodIL(method) is Internal.IL.Stubs.PInvokeILStubMethodIL stub) + return stub.IsStubRequired; + + // This path is taken for PInvokes replaced by RemovingILProvider + return true; + } + + private int SizeOfPInvokeTransitionFrame + { + get + { + // struct PInvokeTransitionFrame: + // #ifdef _TARGET_ARM_ + // m_ChainPointer + // #endif + // m_RIP + // m_FramePointer + // m_pThread + // m_Flags + align (no align for ARM64 that has 64 bit m_Flags) + // m_PreserverRegs - RSP + // No need to save other preserved regs because of the JIT ensures that there are + // no live GC references in callee saved registers around the PInvoke callsite. + int size = 5 * this.PointerSize; + + if (_compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.ARM) + size += this.PointerSize; // m_ChainPointer + + return size; + } + } + + private bool canGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig) + { throw new NotImplementedException("canGetCookieForPInvokeCalliSig"); } + + private void classMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_STRUCT_* cls) + { + } + + private void setEHcount(uint cEH) + { + _ehClauses = new CORINFO_EH_CLAUSE[cEH]; + } + + private void setEHinfo(uint EHnumber, ref CORINFO_EH_CLAUSE clause) + { + _ehClauses[EHnumber] = clause; + } + + private void reportInliningDecision(CORINFO_METHOD_STRUCT_* inlinerHnd, CORINFO_METHOD_STRUCT_* inlineeHnd, CorInfoInline inlineResult, byte* reason) + { + } + + private void updateEntryPointForTailCall(ref CORINFO_CONST_LOOKUP entryPoint) + { + } + + private int* getAddrOfCaptureThreadGlobal(ref void* ppIndirection) + { + ppIndirection = null; + return (int*)ObjectToHandle(_compilation.NodeFactory.ExternSymbol("RhpTrapThreads")); + } + + private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_METHOD_STRUCT_* callerHandle, CORINFO_ACCESS_FLAGS flags, CORINFO_FIELD_INFO* pResult) + { +#if DEBUG + // In debug, write some bogus data to the struct to ensure we have filled everything + // properly. + MemoryHelper.FillMemory((byte*)pResult, 0xcc, Marshal.SizeOf()); +#endif + + Debug.Assert(((int)flags & ((int)CORINFO_ACCESS_FLAGS.CORINFO_ACCESS_GET | + (int)CORINFO_ACCESS_FLAGS.CORINFO_ACCESS_SET | + (int)CORINFO_ACCESS_FLAGS.CORINFO_ACCESS_ADDRESS | + (int)CORINFO_ACCESS_FLAGS.CORINFO_ACCESS_INIT_ARRAY)) != 0); + + var field = HandleToObject(pResolvedToken.hField); + + CORINFO_FIELD_ACCESSOR fieldAccessor; + CORINFO_FIELD_FLAGS fieldFlags = (CORINFO_FIELD_FLAGS)0; + uint fieldOffset = (field.IsStatic && field.HasRva ? 0xBAADF00D : (uint)field.Offset.AsInt); + + if (field.IsStatic) + { + fieldFlags |= CORINFO_FIELD_FLAGS.CORINFO_FLG_FIELD_STATIC; + + if (field.HasRva) + { + fieldFlags |= CORINFO_FIELD_FLAGS.CORINFO_FLG_FIELD_UNMANAGED; + + // TODO: Handle the case when the RVA is in the TLS range + fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_STATIC_RVA_ADDRESS; + + // We are not going through a helper. The constructor has to be triggered explicitly. + if (_compilation.HasLazyStaticConstructor(field.OwningType)) + { + fieldFlags |= CORINFO_FIELD_FLAGS.CORINFO_FLG_FIELD_INITCLASS; + } + } + else if (field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + // The JIT wants to know how to access a static field on a generic type. We need a runtime lookup. + fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_STATIC_READYTORUN_HELPER; + pResult->helper = CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE; + + // Don't try to compute the runtime lookup if we're inlining. The JIT is going to abort the inlining + // attempt anyway. + if (pResolvedToken.tokenContext == contextFromMethodBeingCompiled()) + { + MethodDesc contextMethod = methodFromContext(pResolvedToken.tokenContext); + + FieldDesc runtimeDeterminedField = (FieldDesc)GetRuntimeDeterminedObjectForToken(ref pResolvedToken); + + ReadyToRunHelperId helperId; + + // Find out what kind of base do we need to look up. + if (field.IsThreadStatic) + { + helperId = ReadyToRunHelperId.GetThreadStaticBase; + } + else if (field.HasGCStaticBase) + { + helperId = ReadyToRunHelperId.GetGCStaticBase; + } + else + { + helperId = ReadyToRunHelperId.GetNonGCStaticBase; + } + + // What generic context do we look up the base from. + ISymbolNode helper; + if (contextMethod.AcquiresInstMethodTableFromThis() || contextMethod.RequiresInstMethodTableArg()) + { + helper = _compilation.NodeFactory.ReadyToRunHelperFromTypeLookup( + helperId, runtimeDeterminedField.OwningType, contextMethod.OwningType); + } + else + { + Debug.Assert(contextMethod.RequiresInstMethodDescArg()); + helper = _compilation.NodeFactory.ReadyToRunHelperFromDictionaryLookup( + helperId, runtimeDeterminedField.OwningType, contextMethod); + } + + pResult->fieldLookup = CreateConstLookupToSymbol(helper); + } + } + else + { + fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_STATIC_SHARED_STATIC_HELPER; + pResult->helper = CorInfoHelpFunc.CORINFO_HELP_READYTORUN_STATIC_BASE; + + ReadyToRunHelperId helperId = ReadyToRunHelperId.Invalid; + CORINFO_FIELD_ACCESSOR intrinsicAccessor; + if (field.IsIntrinsic && + (flags & CORINFO_ACCESS_FLAGS.CORINFO_ACCESS_GET) != 0 && + (intrinsicAccessor = getFieldIntrinsic(field)) != (CORINFO_FIELD_ACCESSOR)(-1)) + { + fieldAccessor = intrinsicAccessor; + } + else if (field.IsThreadStatic) + { + helperId = ReadyToRunHelperId.GetThreadStaticBase; + } + else + { + helperId = field.HasGCStaticBase ? + ReadyToRunHelperId.GetGCStaticBase : + ReadyToRunHelperId.GetNonGCStaticBase; + + // + // Currently, we only do this optimization for regular statics, but it + // looks like it may be permissible to do this optimization for + // thread statics as well. + // + if ((flags & CORINFO_ACCESS_FLAGS.CORINFO_ACCESS_ADDRESS) != 0 && + (fieldAccessor != CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_STATIC_TLS)) + { + fieldFlags |= CORINFO_FIELD_FLAGS.CORINFO_FLG_FIELD_SAFESTATIC_BYREF_RETURN; + } + } + + if (helperId != ReadyToRunHelperId.Invalid) + { + pResult->fieldLookup = CreateConstLookupToSymbol( + _compilation.NodeFactory.ReadyToRunHelper(helperId, field.OwningType)); + } + } + } + else + { + fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_INSTANCE; + } + + if (field.IsInitOnly) + fieldFlags |= CORINFO_FIELD_FLAGS.CORINFO_FLG_FIELD_FINAL; + + pResult->fieldAccessor = fieldAccessor; + pResult->fieldFlags = fieldFlags; + pResult->fieldType = getFieldType(pResolvedToken.hField, &pResult->structType, pResolvedToken.hClass); + pResult->accessAllowed = CorInfoIsAccessAllowedResult.CORINFO_ACCESS_ALLOWED; + pResult->offset = fieldOffset; + + // TODO: We need to implement access checks for fields and methods. See JitInterface.cpp in mrtjit + // and STS::AccessCheck::CanAccess. + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj new file mode 100644 index 00000000000000..4aa53fd19c5e11 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj @@ -0,0 +1,776 @@ + + + Library + Internal.TypeSystem + ILCompiler.TypeSystem + true + netstandard2.0 + false + x64;x86 + AnyCPU + false + + + false + Debug;Release;Checked + + + + + Internal.TypeSystem.Strings.resources + + + + + + 4.3.0 + + + $(SystemReflectionMetadataVersion) + + + 1.3.0 + + + + + + TypeSystem\Canon\ArrayType.Canon.cs + + + TypeSystem\Canon\ByRefType.Canon.cs + + + TypeSystem\Canon\CanonTypes.cs + + + TypeSystem\Canon\CanonTypes.Interop.cs + + + TypeSystem\Canon\CanonTypes.Sorting.cs + + + TypeSystem\Canon\FunctionPointerType.Canon.cs + + + TypeSystem\Canon\GenericParameterDesc.Canon.cs + + + TypeSystem\Canon\StandardCanonicalizationAlgorithm.cs + + + TypeSystem\Canon\DefType.Canon.cs + + + TypeSystem\Canon\InstantiatedMethod.Canon.cs + + + TypeSystem\Canon\InstantiatedType.Canon.cs + + + TypeSystem\Canon\MetadataType.Canon.cs + + + TypeSystem\Canon\MethodDelegator.Canon.cs + + + TypeSystem\Canon\MethodDesc.Canon.cs + + + TypeSystem\Canon\MethodForInstantiatedType.Canon.cs + + + TypeSystem\Canon\ParameterizedType.Canon.cs + + + TypeSystem\Canon\PointerType.Canon.cs + + + TypeSystem\Canon\SignatureVariable.Canon.cs + + + TypeSystem\Canon\TypeDesc.Canon.cs + + + TypeSystem\Canon\TypeSystemContext.Canon.cs + + + TypeSystem\Aot\TargetDetails.Aot.cs + + + TypeSystem\CodeGen\FieldDesc.CodeGen.cs + + + TypeSystem\CodeGen\MethodDelegator.CodeGen.cs + + + TypeSystem\CodeGen\MethodDesc.CodeGen.cs + + + TypeSystem\CodeGen\TargetDetails.CodeGen.cs + + + TypeSystem\CodeGen\TypeDesc.CodeGen.cs + + + Utilities\AlignmentHelper.cs + + + TypeSystem\Common\CastingHelper.cs + + + TypeSystem\Common\ConstructedTypeRewritingHelpers.cs + + + TypeSystem\Common\FunctionPointerType.cs + + + TypeSystem\Common\IAssemblyDesc.cs + + + TypeSystem\Common\Instantiation.cs + + + TypeSystem\Common\ModuleDesc.cs + + + TypeSystem\Common\NotFoundBehavior.cs + + + TypeSystem\Common\ResolutionFailure.cs + + + TypeSystem\Common\TypeSystemEntity.cs + + + TypeSystem\Common\TypeSystemException.cs + + + TypeSystem\Common\TypeSystemException.Resources.cs + + + TypeSystem\Common\ThrowHelper.cs + + + TypeSystem\Common\ThrowHelper.Common.cs + + + Utilities\CustomAttributeTypeNameFormatter.cs + + + Utilities\CustomAttributeTypeNameParser.cs + + + Utilities\DebugNameFormatter.cs + + + Utilities\GCPointerMap.Algorithm.cs + + + Utilities\GCPointerMap.Aot.cs + + + Utilities\GCPointerMap.cs + + + Utilities\LockFreeReaderHashtable.cs + + + TypeSystem\Common\ArrayType.cs + + + TypeSystem\Common\ArrayOfTRuntimeInterfacesAlgorithm.cs + + + TypeSystem\Common\BaseTypeRuntimeInterfacesAlgorithm.cs + + + TypeSystem\Common\ByRefType.cs + + + TypeSystem\Common\GenericParameterDesc.cs + + + TypeSystem\Common\GenericParameterDesc.Dummy.Diagnostic.cs + + + TypeSystem\Common\ExceptionStringID.cs + + + TypeSystem\Common\FieldForInstantiatedType.cs + + + TypeSystem\Common\FieldDesc.cs + + + TypeSystem\Common\FieldDesc.ToString.cs + + + TypeSystem\Common\FieldDesc.FieldLayout.cs + + + TypeSystem\Common\FieldLayoutAlgorithm.cs + + + TypeSystem\Common\ExplicitLayoutValidator.cs + + + TypeSystem\Common\IModuleResolver.cs + + + TypeSystem\Common\InstantiatedMethod.cs + + + TypeSystem\Common\InstantiatedType.cs + + + TypeSystem\Common\InstantiatedType.Interfaces.cs + + + TypeSystem\Common\InstantiatedType.MethodImpls.cs + + + TypeSystem\Common\LayoutInt.cs + + + TypeSystem\Common\MetadataType.cs + + + TypeSystem\Common\MetadataType.Interfaces.cs + + + TypeSystem\Common\MetadataType.MethodImpls.cs + + + TypeSystem\Common\MetadataFieldLayoutAlgorithm.cs + + + TypeSystem\Common\MetadataRuntimeInterfacesAlgorithm.cs + + + TypeSystem\Common\MetadataTypeSystemContext.cs + + + TypeSystem\Common\MethodForInstantiatedType.cs + + + TypeSystem\Common\ParameterizedType.cs + + + TypeSystem\Common\PointerType.cs + + + TypeSystem\Common\PropertySignature.cs + + + TypeSystem\Common\SignatureVariable.cs + + + TypeSystem\Common\TargetArchitecture.cs + + + TypeSystem\Common\TargetDetails.cs + + + TypeSystem\Common\TargetDetails.ToString.cs + + + TypeSystem\Common\ThreadSafeFlags.cs + + + TypeSystem\Common\TypeFlags.cs + + + TypeSystem\Common\TypeHashingAlgorithms.cs + + + TypeSystem\Common\TypeSystemContext.cs + + + TypeSystem\Common\TypeSystemHelpers.cs + + + TypeSystem\Common\TypeSystemConstraintsHelpers.cs + + + TypeSystem\Common\UniversalCanonLayoutAlgorithm.cs + + + Utilities\ExceptionTypeNameFormatter.cs + + + Utilities\ExceptionTypeNameFormatter.Metadata.cs + + + Utilities\TypeNameFormatter.cs + + + TypeSystem\Common\WellKnownType.cs + + + TypeSystem\Common\VirtualMethodAlgorithm.cs + + + TypeSystem\Common\MethodDelegator.cs + + + TypeSystem\Common\MethodDesc.cs + + + TypeSystem\Common\MethodDesc.Dummy.Diagnostic.cs + + + TypeSystem\Common\MethodDesc.ToString.cs + + + TypeSystem\Common\StandardVirtualMethodAlgorithm.cs + + + TypeSystem\Common\TypeDesc.cs + + + TypeSystem\Common\TypeDesc.ToString.cs + + + TypeSystem\Common\TypeDesc.Interfaces.cs + + + TypeSystem\Common\DefType.cs + + + TypeSystem\Common\DefType.Dummy.Diagnostic.cs + + + TypeSystem\Common\DefType.FieldLayout.cs + + + TypeSystem\Common\RuntimeInterfacesAlgorithm.cs + + + Ecma\CustomAttributeTypeProvider.cs + + + Ecma\EcmaAssembly.cs + + + Ecma\EcmaAssembly.Symbols.cs + + + Ecma\EcmaField.Sorting.cs + + + Ecma\EcmaField.CodeGen.cs + + + Ecma\EcmaField.Serialization.cs + + + Ecma\EcmaGenericParameter.Sorting.cs + + + Ecma\EcmaMethod.Sorting.cs + + + Ecma\EcmaModule.Sorting.cs + + + Ecma\EcmaType.Serialization.cs + + + Ecma\EcmaType.Sorting.cs + + + Ecma\PrimitiveTypeProvider.cs + + + Ecma\EcmaModule.Symbols.cs + + + Ecma\SymbolReader\PdbSymbolReader.cs + + + Ecma\SymbolReader\PortablePdbSymbolReader.cs + + + Ecma\SymbolReader\UnmanagedPdbSymbolReader.cs + + + Ecma\EcmaField.cs + + + Ecma\EcmaGenericParameter.cs + + + Ecma\EcmaMethod.cs + + + Ecma\EcmaModule.cs + + + Ecma\EcmaSignatureParser.cs + + + Ecma\EcmaType.cs + + + Ecma\EcmaType.MethodImpls.cs + + + Ecma\EcmaType.Interfaces.cs + + + Ecma\MetadataExtensions.cs + + + Ecma\IMetadataStringDecoderProvider.cs + + + Ecma\CachingMetadataStringDecoder.cs + + + IL\DelegateInfo.cs + + + IL\Stubs\DelegateThunks.Sorting.cs + + + IL\Stubs\DynamicInvokeMethodThunk.cs + + + IL\Stubs\DynamicInvokeMethodThunk.Sorting.cs + + + IL\Stubs\EnumThunks.cs + + + IL\Stubs\EnumThunks.Sorting.cs + + + IL\Stubs\PInvokeLazyFixupField.cs + + + IL\Stubs\PInvokeLazyFixupField.Sorting.cs + + + IL\Stubs\PInvokeTargetNativeMethod.Sorting.cs + + + IL\Stubs\StructMarshallingThunk.Sorting.cs + + + IL\Stubs\ValueTypeGetFieldHelperMethodOverride.cs + + + IL\Stubs\ValueTypeGetFieldHelperMethodOverride.Sorting.cs + + + IL\TypeSystemContext.DelegateInfo.cs + + + IL\TypeSystemContext.DynamicInvoke.cs + + + IL\TypeSystemContext.GeneratedAssembly.cs + + + IL\TypeSystemContext.GeneratedAssembly.Sorting.cs + + + IL\EcmaMethodIL.cs + + + IL\EcmaMethodIL.Symbols.cs + + + IL\HelperExtensions.cs + + + IL\MethodIL.cs + + + IL\MethodIL.Symbols.cs + + + IL\MethodILDebugView.cs + + + IL\ILDisassembler.cs + + + IL\InstantiatedMethodIL.cs + + + IL\ILStackHelper.cs + + + IL\ILOpcode.cs + + + IL\ILOpcodeHelper.cs + + + IL\TypeSystemContext.EnumMethods.cs + + + IL\TypeSystemContext.ValueTypeMethods.cs + + + TypeSystem\Interop\IL\InlineArrayType.Sorting.cs + + + Interop\IL\MarshalHelpers.cs + + + Interop\IL\MarshalHelpers.Aot.cs + + + Interop\IL\MarshalUtils.cs + + + Interop\IL\Marshaller.cs + + + Interop\IL\Marshaller.Aot.cs + + + TypeSystem\Interop\IL\NativeStructType.Sorting.cs + + + Interop\IL\PInvokeILEmitterConfiguration.cs + + + IL\Stubs\PInvokeILEmitter.cs + + + IL\Stubs\PInvokeILCodeStreams.cs + + + IL\Stubs\PInvokeTargetNativeMethod.cs + + + IL\Stubs\PInvokeTargetNativeMethod.Mangling.cs + + + IL\Stubs\ILEmitter.cs + + + IL\Stubs\DebuggerSteppingHelpers.cs + + + IL\Stubs\CalliMarshallingMethodThunk.cs + + + IL\Stubs\CalliMarshallingMethodThunk.Mangling.cs + + + IL\Stubs\CalliMarshallingMethodThunk.Sorting.cs + + + IL\Stubs\DelegateMarshallingMethodThunk.cs + + + IL\Stubs\DelegateMarshallingMethodThunk.Mangling.cs + + + IL\Stubs\DelegateMarshallingMethodThunk.Sorting.cs + + + IL\Stubs\ForwardDelegateCreationThunk.cs + + + IL\Stubs\ForwardDelegateCreationThunk.Mangling.cs + + + IL\Stubs\ForwardDelegateCreationThunk.Sorting.cs + + + IL\Stubs\DelegateThunks.cs + + + IL\Stubs\StructMarshallingThunk.cs + + + IL\Stubs\StructMarshallingThunk.Mangling.cs + + + TypeSystem\Interop\IL\NativeStructType.cs + + + TypeSystem\CodeGen\FieldDesc.Serialization.cs + + + TypeSystem\CodeGen\TypeDesc.Serialization.cs + + + TypeSystem\Sorting\FieldDesc.Sorting.cs + + + TypeSystem\Sorting\FieldForInstantiatedType.Sorting.cs + + + TypeSystem\Sorting\InstantiatedMethod.Sorting.cs + + + TypeSystem\Sorting\MethodDesc.Sorting.cs + + + TypeSystem\Sorting\MethodForInstantiatedType.Sorting.cs + + + TypeSystem\Interop\IL\NativeStructType.Mangling.cs + + + TypeSystem\Interop\IL\PInvokeDelegateWrapper.cs + + + TypeSystem\Interop\IL\PInvokeDelegateWrapper.Mangling.cs + + + TypeSystem\Interop\IL\PInvokeDelegateWrapper.Sorting.cs + + + TypeSystem\Interop\IL\PInvokeDelegateWrapperConstructor.cs + + + TypeSystem\Interop\IL\PInvokeDelegateWrapperConstructor.Sorting.cs + + + TypeSystem\Interop\IL\InlineArrayType.cs + + + TypeSystem\Interop\InteropTypes.cs + + + TypeSystem\Interop\InteropStateManager.cs + + + TypeSystem\Interop\FieldDesc.Interop.cs + + + TypeSystem\Interop\InstantiatedType.Interop.cs + + + TypeSystem\Interop\MetadataType.Interop.cs + + + TypeSystem\Interop\MethodDelegator.Interop.cs + + + TypeSystem\Interop\MethodDesc.Interop.cs + + + TypeSystem\Interop\MarshalAsDescriptor.cs + + + TypeSystem\Mangling\IPrefixMangledMethod.cs + + + TypeSystem\Mangling\IPrefixMangledType.cs + + + TypeSystem\Mangling\IPrefixMangledSignature.cs + + + Utilities\ArrayBuilder.cs + + + TypeSystem\Common\LocalVariableDefinition.cs + + + TypeSystem\CodeGen\INonEmittableType.cs + + + TypeSystem\CodeGen\NativeStructType.CodeGen.cs + + + TypeSystem\RuntimeDetermined\ArrayType.RuntimeDetermined.cs + + + TypeSystem\RuntimeDetermined\ByRefType.RuntimeDetermined.cs + + + TypeSystem\RuntimeDetermined\FieldDesc.RuntimeDetermined.cs + + + TypeSystem\RuntimeDetermined\FunctionPointerType.RuntimeDetermined.cs + + + TypeSystem\RuntimeDetermined\MethodDesc.RuntimeDetermined.cs + + + TypeSystem\RuntimeDetermined\PointerType.RuntimeDetermined.cs + + + TypeSystem\RuntimeDetermined\MethodForRuntimeDeterminedType.cs + + + TypeSystem\RuntimeDetermined\MethodForRuntimeDeterminedType.Sorting.cs + + + TypeSystem\RuntimeDetermined\RuntimeDeterminedCanonicalizationAlgorithm.cs + + + TypeSystem\RuntimeDetermined\RuntimeDeterminedFieldLayoutAlgorithm.cs + + + TypeSystem\RuntimeDetermined\DefType.RuntimeDetermined.cs + + + TypeSystem\RuntimeDetermined\GenericParameterDesc.RuntimeDetermined.cs + + + TypeSystem\RuntimeDetermined\ParameterizedType.RuntimeDetermined.cs + + + TypeSystem\RuntimeDetermined\RuntimeDeterminedType.cs + + + TypeSystem\RuntimeDetermined\RuntimeDeterminedType.Sorting.cs + + + TypeSystem\RuntimeDetermined\RuntimeDeterminedTypeUtilities.cs + + + TypeSystem\RuntimeDetermined\SignatureVariable.RuntimeDetermined.cs + + + TypeSystem\RuntimeDetermined\TypeDesc.RuntimeDetermined.cs + + + TypeSystem\RuntimeDetermined\TypeSystemContext.RuntimeDetermined.cs + + + Common\System\FormattingHelpers.cs + + + TypeSystem\Sorting\ArrayType.Sorting.cs + + + TypeSystem\Sorting\ByRefType.Sorting.cs + + + TypeSystem\Sorting\FunctionPointerType.Sorting.cs + + + TypeSystem\Sorting\InstantiatedType.Sorting.cs + + + TypeSystem\Sorting\MethodSignature.Sorting.cs + + + TypeSystem\Sorting\PointerType.Sorting.cs + + + TypeSystem\Sorting\SignatureVariable.Sorting.cs + + + TypeSystem\Sorting\TypeDesc.Sorting.cs + + + TypeSystem\Sorting\TypeSystemComparer.cs + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem/Utilities/UniqueTypeNameFormatter.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem/Utilities/UniqueTypeNameFormatter.cs new file mode 100644 index 00000000000000..980c0c8050aacb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem/Utilities/UniqueTypeNameFormatter.cs @@ -0,0 +1,164 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Text; + +namespace Internal.TypeSystem +{ + /// + /// Provides a name formatter attempts to produce unique names given a type. + /// The exact format of this type name provider is not considered stable or suited for parsing at this time. + /// + public class UniqueTypeNameFormatter : TypeNameFormatter + { + public static UniqueTypeNameFormatter Instance { get; } = new UniqueTypeNameFormatter(); + + public override void AppendName(StringBuilder sb, PointerType type) + { + AppendName(sb, type.ParameterType); + sb.Append('*'); + } + + public override void AppendName(StringBuilder sb, GenericParameterDesc type) + { + string prefix = type.Kind == GenericParameterKind.Type ? "!" : "!!"; + sb.Append(prefix); + sb.Append(type.Name); + } + + public override void AppendName(StringBuilder sb, SignatureTypeVariable type) + { + sb.Append("!"); + sb.Append(type.Index.ToStringInvariant()); + } + + public override void AppendName(StringBuilder sb, SignatureMethodVariable type) + { + sb.Append("!!"); + sb.Append(type.Index.ToStringInvariant()); + } + + public override void AppendName(StringBuilder sb, FunctionPointerType type) + { + MethodSignature signature = type.Signature; + + AppendName(sb, signature.ReturnType); + + sb.Append(" ("); + for (int i = 0; i < signature.Length; i++) + { + if (i > 0) + sb.Append(", "); + AppendName(sb, signature[i]); + } + + // TODO: Append '...' for vararg methods + + sb.Append(')'); + } + + public override void AppendName(StringBuilder sb, ByRefType type) + { + AppendName(sb, type.ParameterType); + sb.Append(" ByRef"); + } + + public override void AppendName(StringBuilder sb, ArrayType type) + { + AppendName(sb, type.ElementType); + sb.Append('['); + + if (type.Rank == 1 && type.IsMdArray) + sb.Append('*'); + sb.Append(',', type.Rank - 1); + + sb.Append(']'); + } + + protected override void AppendNameForInstantiatedType(StringBuilder sb, DefType type) + { + AppendName(sb, type.GetTypeDefinition()); + sb.Append('<'); + + for (int i = 0; i < type.Instantiation.Length; i++) + { + if (i > 0) + sb.Append(", "); + AppendName(sb, type.Instantiation[i]); + } + + sb.Append('>'); + } + + protected override void AppendNameForNamespaceType(StringBuilder sb, DefType type) + { + string ns = GetTypeNamespace(type); + if (ns.Length > 0) + { + AppendEscapedIdentifier(sb, ns); + sb.Append('.'); + } + AppendEscapedIdentifier(sb, GetTypeName(type)); + + if (type is MetadataType) + { + IAssemblyDesc homeAssembly = ((MetadataType)type).Module as IAssemblyDesc; + AppendAssemblyName(sb, homeAssembly); + } + } + + private void AppendAssemblyName(StringBuilder sb, IAssemblyDesc assembly) + { + if (assembly == null) + return; + + sb.Append(','); + AppendEscapedIdentifier(sb, assembly.GetName().Name); + } + + protected override void AppendNameForNestedType(StringBuilder sb, DefType nestedType, DefType containingType) + { + AppendName(sb, containingType); + + sb.Append('+'); + + string ns = GetTypeNamespace(nestedType); + if (ns.Length > 0) + { + AppendEscapedIdentifier(sb, ns); + sb.Append('.'); + } + AppendEscapedIdentifier(sb, GetTypeName(nestedType)); + } + + private string GetTypeName(DefType type) + { + return type.Name; + } + + private string GetTypeNamespace(DefType type) + { + return type.Namespace; + } + + private static char[] s_escapedChars = new char[] { ',', '=', '"', ']', '[', '*', '&', '+', '\\' }; + private void AppendEscapedIdentifier(StringBuilder sb, string identifier) + { + if (identifier.IndexOfAny(s_escapedChars) < 0) + { + string escapedIdentifier = identifier; + foreach (char escapedChar in s_escapedChars) + { + string escapedCharString = new string(escapedChar, 1); + escapedIdentifier = escapedIdentifier.Replace(escapedCharString, "\\" + escapedCharString); + } + sb.Append(escapedIdentifier); + } + else + { + sb.Append(identifier); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler/ConfigurablePInvokePolicy.cs b/src/coreclr/tools/aot/ILCompiler/ConfigurablePInvokePolicy.cs new file mode 100644 index 00000000000000..93fd389484b607 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler/ConfigurablePInvokePolicy.cs @@ -0,0 +1,166 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; + +using Internal.IL; +using Internal.TypeSystem; + +namespace ILCompiler +{ + class ConfigurablePInvokePolicy : PInvokeILEmitterConfiguration + { + private readonly TargetDetails _target; + private readonly Dictionary> _directPInvokes; + + public ConfigurablePInvokePolicy(TargetDetails target, IReadOnlyList directPInvokes, IReadOnlyList directPInvokeLists) + { + _directPInvokes = new Dictionary>(target.IsWindows ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal); + + // * is always a direct call + _directPInvokes.Add("*", null); + + foreach (var file in directPInvokeLists) + { + foreach (var entry in File.ReadLines(file)) + { + AddDirectPInvoke(entry); + } + } + + foreach (var entry in directPInvokes) + { + AddDirectPInvoke(entry); + } + + _target = target; + } + + private void AddDirectPInvoke(string entry) + { + // Ignore comments + if (entry.StartsWith('#')) + return; + + entry = entry.Trim(); + + // Ignore empty entries + if (string.IsNullOrEmpty(entry)) + return; + + int separator = entry.IndexOf('!'); + + if (separator != -1) + { + string libraryName = entry.Substring(0, separator); + string entrypointName = entry.Substring(separator + 1); + + if (_directPInvokes.TryGetValue(libraryName, out HashSet entrypointSet)) + { + // All entrypoints from the library are direct + if (entrypointSet == null) + return; + } + else + { + _directPInvokes.Add(libraryName, entrypointSet = new HashSet()); + } + + entrypointSet.Add(entrypointName); + } + else + { + // All entrypoints from the library are direct + _directPInvokes[entry] = null; + } + } + + private IEnumerable ModuleNameVariations(string name) + { + yield return name; + + if (_target.IsWindows) + { + string suffix = ".dll"; + + if (name.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) + yield return name.Substring(0, name.Length - suffix.Length); + } + else + { + string suffix = _target.IsOSX ? ".dylib" : ".so"; + + if (name.EndsWith(suffix, StringComparison.Ordinal)) + yield return name.Substring(0, name.Length - suffix.Length); + } + } + + private IEnumerable EntryPointNameVariations(string name, PInvokeFlags flags) + { + if (_target.IsWindows && !flags.ExactSpelling) + { + // Mirror CharSet normalization from Marshaller.CreateMarshaller + bool isAnsi = flags.CharSet switch + { + CharSet.Ansi => true, + CharSet.Unicode => false, + CharSet.Auto => false, + _ => true + }; + + if (isAnsi) + { + // For ANSI, look for the user-provided entry point name first. + // If that does not exist, try the charset suffix. + yield return name; + yield return name + "A"; + } + else + { + // For Unicode, look for the entry point name with the charset suffix first. + // The 'W' API takes precedence over the undecorated one. + yield return name + "W"; + yield return name; + } + } + else + { + yield return name; + } + } + + public override bool GenerateDirectCall(MethodDesc method, out string externName) + { + var pInvokeMetadata = method.GetPInvokeMethodMetadata(); + + foreach (var moduleName in ModuleNameVariations(pInvokeMetadata.Module)) + { + if (_directPInvokes.TryGetValue(moduleName, out HashSet entrypoints)) + { + string entryPointMetadataName = pInvokeMetadata.Name ?? method.Name; + + if (entrypoints == null) + { + externName = entryPointMetadataName; + return true; + } + + foreach (var entryPointName in EntryPointNameVariations(entryPointMetadataName, pInvokeMetadata.Flags)) + { + if (entrypoints.Contains(entryPointName)) + { + externName = entryPointName; + return true; + } + } + } + } + + externName = null; + return false; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler/EcmaOnlyDebugInformationProvider.cs b/src/coreclr/tools/aot/ILCompiler/EcmaOnlyDebugInformationProvider.cs new file mode 100644 index 00000000000000..39949befdaa9cb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler/EcmaOnlyDebugInformationProvider.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.IL; + +namespace ILCompiler +{ + /// + /// Provides debug information for ECMA-based only. + /// + public class EcmaOnlyDebugInformationProvider : DebugInformationProvider + { + public override MethodDebugInformation GetDebugInfo(MethodIL methodIL) + { + MethodIL definitionIL = methodIL.GetMethodILDefinition(); + if (definitionIL is EcmaMethodIL) + return methodIL.GetDebugInfo(); + + return MethodDebugInformation.None; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj b/src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj new file mode 100644 index 00000000000000..f505b0851ecc22 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj @@ -0,0 +1,139 @@ + + + ilc + true + Exe + $(NetCoreAppToolCurrent) + 8002,NU1701 + x64;x86 + AnyCPU + false + true + $(RuntimeBinDir)ilc + true + false + Debug;Release;Checked + true + true + false + + + + true + false + $(__DistroRid) + + + win-$(TargetArchitecture) + linux-$(TargetArchitecture) + osx-$(TargetArchitecture) + + + + 7.0.0-alpha.1.21455.2 + + $(Configuration) + Debug + + $(ArtifactsDir)llvm-project\llvm\build\$(TargetArchitecture)\$(ObjWriterBuildType)\bin\$(LibPrefix)objwriter$(LibSuffix) + $(ArtifactsDir)llvm-project\llvm\build\$(TargetArchitecture)\lib\$(LibPrefix)objwriter$(LibSuffix) + + + true + + + + + + + PreserveNewest + false + false + + + + + + $(ILCompilerVersion) + + + + PreserveNewest + false + false + + + + + + PreserveNewest + false + false + + + + PreserveNewest + false + false + + + + PreserveNewest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + Internal.CommandLine.Strings + + + + + + + + + unix + win + $(TargetOSComponent)_$(TargetArchitecture)_$(TargetArchitecture) + + + + + + + + diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs new file mode 100644 index 00000000000000..007c3787df15f5 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -0,0 +1,996 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; + +using Internal.IL; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Internal.CommandLine; + +using Debug = System.Diagnostics.Debug; +using InstructionSet = Internal.JitInterface.InstructionSet; + +namespace ILCompiler +{ + internal class Program + { + private const string DefaultSystemModule = "System.Private.CoreLib"; + + private Dictionary _inputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + private Dictionary _referenceFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + + private string _outputFilePath; + private bool _isVerbose; + + private string _dgmlLogFileName; + private bool _generateFullDgmlLog; + private string _scanDgmlLogFileName; + private bool _generateFullScanDgmlLog; + + private TargetArchitecture _targetArchitecture; + private string _targetArchitectureStr; + private TargetOS _targetOS; + private string _targetOSStr; + private OptimizationMode _optimizationMode; + private bool _enableDebugInfo; + private string _ilDump; + private string _systemModuleName = DefaultSystemModule; + private bool _multiFile; + private bool _nativeLib; + private string _exportsFile; + private bool _useScanner; + private bool _noScanner; + private bool _preinitStatics; + private bool _noPreinitStatics; + private bool _emitStackTraceData; + private string _mapFileName; + private string _metadataLogFileName; + private bool _noMetadataBlocking; + private bool _disableReflection; + private bool _completeTypesMetadata; + private bool _reflectedOnly; + private bool _scanReflection; + private bool _methodBodyFolding; + private bool _singleThreaded; + private string _instructionSet; + private string _guard; + + private string _singleMethodTypeName; + private string _singleMethodName; + private IReadOnlyList _singleMethodGenericArgs; + + private IReadOnlyList _codegenOptions = Array.Empty(); + + private IReadOnlyList _rdXmlFilePaths = Array.Empty(); + + private IReadOnlyList _initAssemblies = Array.Empty(); + + private IReadOnlyList _appContextSwitches = Array.Empty(); + + private IReadOnlyList _runtimeOptions = Array.Empty(); + + private IReadOnlyList _featureSwitches = Array.Empty(); + + private IReadOnlyList _suppressedWarnings = Array.Empty(); + + private IReadOnlyList _directPInvokes = Array.Empty(); + + private IReadOnlyList _directPInvokeLists = Array.Empty(); + + private IReadOnlyList _rootedAssemblies = Array.Empty(); + private IReadOnlyList _conditionallyRootedAssemblies = Array.Empty(); + private IReadOnlyList _trimmedAssemblies = Array.Empty(); + private bool _rootDefaultAssemblies; + + public IReadOnlyList _mibcFilePaths = Array.Empty(); + + private IReadOnlyList _singleWarnEnabledAssemblies = Array.Empty(); + private IReadOnlyList _singleWarnDisabledAssemblies = Array.Empty(); + private bool _singleWarn; + + private string _makeReproPath; + + private bool _help; + + private Program() + { + } + + private void Help(string helpText) + { + Console.WriteLine(); + Console.Write("Microsoft (R) .NET Native IL Compiler"); + Console.Write(" "); + Console.Write(typeof(Program).GetTypeInfo().Assembly.GetName().Version); + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine(helpText); + } + + private void InitializeDefaultOptions() + { + // We could offer this as a command line option, but then we also need to + // load a different RyuJIT, so this is a future nice to have... + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + _targetOS = TargetOS.Windows; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + _targetOS = TargetOS.Linux; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + _targetOS = TargetOS.OSX; + else + throw new NotImplementedException(); + + switch (RuntimeInformation.ProcessArchitecture) + { + case Architecture.X86: + _targetArchitecture = TargetArchitecture.X86; + break; + case Architecture.X64: + _targetArchitecture = TargetArchitecture.X64; + break; + case Architecture.Arm: + _targetArchitecture = TargetArchitecture.ARM; + break; + case Architecture.Arm64: + _targetArchitecture = TargetArchitecture.ARM64; + break; + default: + throw new NotImplementedException(); + } + + // Workaround for https://github.com/dotnet/corefx/issues/25267 + // If pointer size is 8, we're obviously not an X86 process... + if (_targetArchitecture == TargetArchitecture.X86 && IntPtr.Size == 8) + _targetArchitecture = TargetArchitecture.X64; + } + + private ArgumentSyntax ParseCommandLine(string[] args) + { + IReadOnlyList inputFiles = Array.Empty(); + IReadOnlyList referenceFiles = Array.Empty(); + + bool optimize = false; + bool optimizeSpace = false; + bool optimizeTime = false; + + bool waitForDebugger = false; + AssemblyName name = typeof(Program).GetTypeInfo().Assembly.GetName(); + ArgumentSyntax argSyntax = ArgumentSyntax.Parse(args, syntax => + { + syntax.ApplicationName = name.Name.ToString(); + + // HandleHelp writes to error, fails fast with crash dialog and lacks custom formatting. + syntax.HandleHelp = false; + syntax.HandleErrors = true; + + syntax.DefineOption("h|help", ref _help, "Help message for ILC"); + syntax.DefineOptionList("r|reference", ref referenceFiles, "Reference file(s) for compilation"); + syntax.DefineOption("o|out", ref _outputFilePath, "Output file path"); + syntax.DefineOption("O", ref optimize, "Enable optimizations"); + syntax.DefineOption("Os", ref optimizeSpace, "Enable optimizations, favor code space"); + syntax.DefineOption("Ot", ref optimizeTime, "Enable optimizations, favor code speed"); + syntax.DefineOptionList("m|mibc", ref _mibcFilePaths, "Mibc file(s) for profile guided optimization"); ; + syntax.DefineOption("g", ref _enableDebugInfo, "Emit debugging information"); + syntax.DefineOption("nativelib", ref _nativeLib, "Compile as static or shared library"); + syntax.DefineOption("exportsfile", ref _exportsFile, "File to write exported method definitions"); + syntax.DefineOption("dgmllog", ref _dgmlLogFileName, "Save result of dependency analysis as DGML"); + syntax.DefineOption("fulllog", ref _generateFullDgmlLog, "Save detailed log of dependency analysis"); + syntax.DefineOption("scandgmllog", ref _scanDgmlLogFileName, "Save result of scanner dependency analysis as DGML"); + syntax.DefineOption("scanfulllog", ref _generateFullScanDgmlLog, "Save detailed log of scanner dependency analysis"); + syntax.DefineOption("verbose", ref _isVerbose, "Enable verbose logging"); + syntax.DefineOption("systemmodule", ref _systemModuleName, "System module name (default: System.Private.CoreLib)"); + syntax.DefineOption("multifile", ref _multiFile, "Compile only input files (do not compile referenced assemblies)"); + syntax.DefineOption("waitfordebugger", ref waitForDebugger, "Pause to give opportunity to attach debugger"); + syntax.DefineOptionList("codegenopt", ref _codegenOptions, "Define a codegen option"); + syntax.DefineOptionList("rdxml", ref _rdXmlFilePaths, "RD.XML file(s) for compilation"); + syntax.DefineOption("map", ref _mapFileName, "Generate a map file"); + syntax.DefineOption("metadatalog", ref _metadataLogFileName, "Generate a metadata log file"); + syntax.DefineOption("nometadatablocking", ref _noMetadataBlocking, "Ignore metadata blocking for internal implementation details"); + syntax.DefineOption("disablereflection", ref _disableReflection, "Disable generation of reflection metadata"); + syntax.DefineOption("completetypemetadata", ref _completeTypesMetadata, "Generate complete metadata for types"); + syntax.DefineOption("reflectedonly", ref _reflectedOnly, "Generate metadata only for reflected members"); + syntax.DefineOption("scanreflection", ref _scanReflection, "Scan IL for reflection patterns"); + syntax.DefineOption("scan", ref _useScanner, "Use IL scanner to generate optimized code (implied by -O)"); + syntax.DefineOption("noscan", ref _noScanner, "Do not use IL scanner to generate optimized code"); + syntax.DefineOption("ildump", ref _ilDump, "Dump IL assembly listing for compiler-generated IL"); + syntax.DefineOption("stacktracedata", ref _emitStackTraceData, "Emit data to support generating stack trace strings at runtime"); + syntax.DefineOption("methodbodyfolding", ref _methodBodyFolding, "Fold identical method bodies"); + syntax.DefineOptionList("initassembly", ref _initAssemblies, "Assembly(ies) with a library initializer"); + syntax.DefineOptionList("appcontextswitch", ref _appContextSwitches, "System.AppContext switches to set (format: 'Key=Value')"); + syntax.DefineOptionList("feature", ref _featureSwitches, "Feature switches to apply (format: 'Namespace.Name=[true|false]'"); + syntax.DefineOptionList("runtimeopt", ref _runtimeOptions, "Runtime options to set"); + syntax.DefineOption("singlethreaded", ref _singleThreaded, "Run compilation on a single thread"); + syntax.DefineOption("instructionset", ref _instructionSet, "Instruction set to allow or disallow"); + syntax.DefineOption("guard", ref _guard, "Enable mitigations. Options: 'cf': CFG (Control Flow Guard, Windows only)"); + syntax.DefineOption("preinitstatics", ref _preinitStatics, "Interpret static constructors at compile time if possible (implied by -O)"); + syntax.DefineOption("nopreinitstatics", ref _noPreinitStatics, "Do not interpret static constructors at compile time"); + syntax.DefineOptionList("nowarn", ref _suppressedWarnings, "Disable specific warning messages"); + syntax.DefineOption("singlewarn", ref _singleWarn, "Generate single AOT/trimming warning per assembly"); + syntax.DefineOptionList("singlewarnassembly", ref _singleWarnEnabledAssemblies, "Generate single AOT/trimming warning for given assembly"); + syntax.DefineOptionList("nosinglewarnassembly", ref _singleWarnDisabledAssemblies, "Expand AOT/trimming warnings for given assembly"); + syntax.DefineOptionList("directpinvoke", ref _directPInvokes, "PInvoke to call directly"); + syntax.DefineOptionList("directpinvokelist", ref _directPInvokeLists, "File with list of PInvokes to call directly"); + + syntax.DefineOptionList("root", ref _rootedAssemblies, "Fully generate given assembly"); + syntax.DefineOptionList("conditionalroot", ref _conditionallyRootedAssemblies, "Fully generate given assembly if it's used"); + syntax.DefineOptionList("trim", ref _trimmedAssemblies, "Trim the specified assembly"); + syntax.DefineOption("defaultrooting", ref _rootDefaultAssemblies, "Root assemblies that are not marked [IsTrimmable]"); + + syntax.DefineOption("targetarch", ref _targetArchitectureStr, "Target architecture for cross compilation"); + syntax.DefineOption("targetos", ref _targetOSStr, "Target OS for cross compilation"); + + syntax.DefineOption("singlemethodtypename", ref _singleMethodTypeName, "Single method compilation: assembly-qualified name of the owning type"); + syntax.DefineOption("singlemethodname", ref _singleMethodName, "Single method compilation: name of the method"); + syntax.DefineOptionList("singlemethodgenericarg", ref _singleMethodGenericArgs, "Single method compilation: generic arguments to the method"); + + syntax.DefineOption("make-repro-path", ref _makeReproPath, "Path where to place a repro package"); + + syntax.DefineParameterList("in", ref inputFiles, "Input file(s) to compile"); + }); + if (waitForDebugger) + { + Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); + Console.ReadLine(); + } + + _optimizationMode = OptimizationMode.None; + if (optimizeSpace) + { + if (optimizeTime) + Console.WriteLine("Warning: overriding -Ot with -Os"); + _optimizationMode = OptimizationMode.PreferSize; + } + else if (optimizeTime) + _optimizationMode = OptimizationMode.PreferSpeed; + else if (optimize) + _optimizationMode = OptimizationMode.Blended; + + foreach (var input in inputFiles) + Helpers.AppendExpandedPaths(_inputFilePaths, input, true); + + foreach (var reference in referenceFiles) + Helpers.AppendExpandedPaths(_referenceFilePaths, reference, false); + + if (_makeReproPath != null) + { + // Create a repro package in the specified path + // This package will have the set of input files needed for compilation + // + the original command line arguments + // + a rsp file that should work to directly run out of the zip file + + Helpers.MakeReproPackage(_makeReproPath, _outputFilePath, args, argSyntax, new[] { "-r", "-m", "--rdxml", "--directpinvokelist" }); + } + + return argSyntax; + } + + private IReadOnlyCollection CreateInitializerList(TypeSystemContext context) + { + List assembliesWithInitalizers = new List(); + + // Build a list of assemblies that have an initializer that needs to run before + // any user code runs. + foreach (string initAssemblyName in _initAssemblies) + { + ModuleDesc assembly = context.ResolveAssembly(new AssemblyName(initAssemblyName)); + assembliesWithInitalizers.Add(assembly); + } + + var libraryInitializers = new LibraryInitializers(context, assembliesWithInitalizers); + + List initializerList = new List(libraryInitializers.LibraryInitializerMethods); + + // If there are any AppContext switches the user wishes to enable, generate code that sets them. + if (_appContextSwitches.Count > 0) + { + MethodDesc appContextInitMethod = new Internal.IL.Stubs.StartupCode.AppContextInitializerMethod( + context.GeneratedAssembly.GetGlobalModuleType(), _appContextSwitches); + initializerList.Add(appContextInitMethod); + } + + return initializerList; + } + + private int Run(string[] args) + { + InitializeDefaultOptions(); + + ArgumentSyntax syntax = ParseCommandLine(args); + if (_help) + { + Help(syntax.GetHelpText()); + return 1; + } + + if (_outputFilePath == null) + throw new CommandLineException("Output filename must be specified (/out )"); + + // + // Set target Architecture and OS + // + if (_targetArchitectureStr != null) + { + if (_targetArchitectureStr.Equals("x86", StringComparison.OrdinalIgnoreCase)) + _targetArchitecture = TargetArchitecture.X86; + else if (_targetArchitectureStr.Equals("x64", StringComparison.OrdinalIgnoreCase)) + _targetArchitecture = TargetArchitecture.X64; + else if (_targetArchitectureStr.Equals("arm", StringComparison.OrdinalIgnoreCase)) + _targetArchitecture = TargetArchitecture.ARM; + else if (_targetArchitectureStr.Equals("armel", StringComparison.OrdinalIgnoreCase)) + _targetArchitecture = TargetArchitecture.ARM; + else if (_targetArchitectureStr.Equals("arm64", StringComparison.OrdinalIgnoreCase)) + _targetArchitecture = TargetArchitecture.ARM64; + else + throw new CommandLineException("Target architecture is not supported"); + } + if (_targetOSStr != null) + { + if (_targetOSStr.Equals("windows", StringComparison.OrdinalIgnoreCase)) + _targetOS = TargetOS.Windows; + else if (_targetOSStr.Equals("linux", StringComparison.OrdinalIgnoreCase)) + _targetOS = TargetOS.Linux; + else if (_targetOSStr.Equals("osx", StringComparison.OrdinalIgnoreCase)) + _targetOS = TargetOS.OSX; + else + throw new CommandLineException("Target OS is not supported"); + } + + InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); + + // The runtime expects certain baselines that the codegen can assume as well. + if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) + { + instructionSetSupportBuilder.AddSupportedInstructionSet("sse"); + instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); + } + else if (_targetArchitecture == TargetArchitecture.ARM64) + { + instructionSetSupportBuilder.AddSupportedInstructionSet("base"); + instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); + } + + if (_instructionSet != null) + { + List instructionSetParams = new List(); + + // Normalize instruction set format to include implied +. + string[] instructionSetParamsInput = _instructionSet.Split(','); + for (int i = 0; i < instructionSetParamsInput.Length; i++) + { + string instructionSet = instructionSetParamsInput[i]; + + if (String.IsNullOrEmpty(instructionSet)) + throw new CommandLineException("Instruction set must not be empty"); + + char firstChar = instructionSet[0]; + if ((firstChar != '+') && (firstChar != '-')) + { + instructionSet = "+" + instructionSet; + } + instructionSetParams.Add(instructionSet); + } + + Dictionary instructionSetSpecification = new Dictionary(); + foreach (string instructionSetSpecifier in instructionSetParams) + { + string instructionSet = instructionSetSpecifier.Substring(1, instructionSetSpecifier.Length - 1); + + bool enabled = instructionSetSpecifier[0] == '+' ? true : false; + if (enabled) + { + if (!instructionSetSupportBuilder.AddSupportedInstructionSet(instructionSet)) + throw new CommandLineException($"Unrecognized instruction set '{instructionSet}'"); + } + else + { + if (!instructionSetSupportBuilder.RemoveInstructionSetSupport(instructionSet)) + throw new CommandLineException($"Unrecognized instruction set '{instructionSet}'"); + } + } + } + + instructionSetSupportBuilder.ComputeInstructionSetFlags(out var supportedInstructionSet, out var unsupportedInstructionSet, + (string specifiedInstructionSet, string impliedInstructionSet) => + throw new CommandLineException(String.Format("Unsupported combination of instruction sets: {0}/{1}", specifiedInstructionSet, impliedInstructionSet))); + + InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); + + // Optimistically assume some instruction sets are present. + if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) + { + // We set these hardware features as enabled always, as most + // of hardware in the wild supports them. Note that we do not indicate support for AVX, or any other + // instruction set which uses the VEX encodings as the presence of those makes otherwise acceptable + // code be unusable on hardware which does not support VEX encodings, as well as emulators that do not + // support AVX instructions. + // + // The compiler is able to generate runtime IsSupported checks for the following instruction sets. + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.1"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("ssse3"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt"); + + // If AVX was enabled, we can opportunistically enable FMA/BMI + Debug.Assert(InstructionSet.X64_AVX == InstructionSet.X86_AVX); + if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX)) + { + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("fma"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi2"); + } + } + else if (_targetArchitecture == TargetArchitecture.ARM64) + { + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("crc"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha1"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha2"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lse"); + } + + optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(out var optimisticInstructionSet, out _, + (string specifiedInstructionSet, string impliedInstructionSet) => throw new NotSupportedException()); + optimisticInstructionSet.Remove(unsupportedInstructionSet); + optimisticInstructionSet.Add(supportedInstructionSet); + + var instructionSetSupport = new InstructionSetSupport(supportedInstructionSet, + unsupportedInstructionSet, + optimisticInstructionSet, + InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture), + _targetArchitecture); + + bool supportsReflection = !_disableReflection && _systemModuleName == DefaultSystemModule; + + // + // Initialize type system context + // + + SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; + + var simdVectorLength = instructionSetSupport.GetVectorTSimdVector(); + var targetAbi = TargetAbi.CoreRT; + var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, targetAbi, simdVectorLength); + CompilerTypeSystemContext typeSystemContext = + new CompilerTypeSystemContext(targetDetails, genericsMode, supportsReflection ? DelegateFeature.All : 0); + + // + // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since + // some tests contain a mixture of both managed and native binaries. + // + // See: https://github.com/dotnet/corert/issues/2785 + // + // When we undo this this hack, replace this foreach with + // typeSystemContext.InputFilePaths = _inputFilePaths; + // + Dictionary inputFilePaths = new Dictionary(); + foreach (var inputFile in _inputFilePaths) + { + try + { + var module = typeSystemContext.GetModuleFromPath(inputFile.Value); + inputFilePaths.Add(inputFile.Key, inputFile.Value); + } + catch (TypeSystemException.BadImageFormatException) + { + // Keep calm and carry on. + } + } + + typeSystemContext.InputFilePaths = inputFilePaths; + typeSystemContext.ReferenceFilePaths = _referenceFilePaths; + if (!typeSystemContext.InputFilePaths.ContainsKey(_systemModuleName) + && !typeSystemContext.ReferenceFilePaths.ContainsKey(_systemModuleName)) + throw new CommandLineException($"System module {_systemModuleName} does not exists. Make sure that you specify --systemmodule"); + + typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(_systemModuleName)); + + if (typeSystemContext.InputFilePaths.Count == 0) + throw new CommandLineException("No input files specified"); + + SecurityMitigationOptions securityMitigationOptions = 0; + if (StringComparer.OrdinalIgnoreCase.Equals(_guard, "cf")) + { + if (_targetOS != TargetOS.Windows) + { + throw new CommandLineException($"Control flow guard only available on Windows"); + } + + securityMitigationOptions = SecurityMitigationOptions.ControlFlowGuardAnnotations; + } + else if (!String.IsNullOrEmpty(_guard)) + { + throw new CommandLineException($"Unrecognized mitigation option '{_guard}'"); + } + + // + // Initialize compilation group and compilation roots + // + + // Single method mode? + MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext); + + CompilationModuleGroup compilationGroup; + List compilationRoots = new List(); + if (singleMethod != null) + { + // Compiling just a single method + compilationGroup = new SingleMethodCompilationModuleGroup(singleMethod); + compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); + } + else + { + // Either single file, or multifile library, or multifile consumption. + EcmaModule entrypointModule = null; + bool systemModuleIsInputModule = false; + foreach (var inputFile in typeSystemContext.InputFilePaths) + { + EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); + + if (module.PEReader.PEHeaders.IsExe) + { + if (entrypointModule != null) + throw new Exception("Multiple EXE modules"); + entrypointModule = module; + } + + if (module == typeSystemContext.SystemModule) + systemModuleIsInputModule = true; + + compilationRoots.Add(new ExportedMethodsRootProvider(module)); + } + + if (entrypointModule != null) + { + compilationRoots.Add(new MainMethodRootProvider(entrypointModule, CreateInitializerList(typeSystemContext))); + compilationRoots.Add(new RuntimeConfigurationRootProvider(_runtimeOptions)); + compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); + } + + if (_multiFile) + { + List inputModules = new List(); + + foreach (var inputFile in typeSystemContext.InputFilePaths) + { + EcmaModule module = typeSystemContext.GetModuleFromPath(inputFile.Value); + + if (entrypointModule == null) + { + // This is a multifile production build - we need to root all methods + compilationRoots.Add(new LibraryRootProvider(module)); + } + inputModules.Add(module); + } + + compilationGroup = new MultiFileSharedCompilationModuleGroup(typeSystemContext, inputModules); + } + else + { + if (entrypointModule == null && !_nativeLib) + throw new Exception("No entrypoint module"); + + if (!systemModuleIsInputModule) + compilationRoots.Add(new ExportedMethodsRootProvider((EcmaModule)typeSystemContext.SystemModule)); + compilationGroup = new SingleFileCompilationModuleGroup(); + } + + if (_nativeLib) + { + // Set owning module of generated native library startup method to compiler generated module, + // to ensure the startup method is included in the object file during multimodule mode build + compilationRoots.Add(new NativeLibraryInitializerRootProvider(typeSystemContext.GeneratedAssembly, CreateInitializerList(typeSystemContext))); + compilationRoots.Add(new RuntimeConfigurationRootProvider(_runtimeOptions)); + compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); + } + + foreach (var rdXmlFilePath in _rdXmlFilePaths) + { + compilationRoots.Add(new RdXmlRootProvider(typeSystemContext, rdXmlFilePath)); + } + } + + _rootedAssemblies = new List(_rootedAssemblies.Select(a => ILLinkify(a))); + _conditionallyRootedAssemblies = new List(_conditionallyRootedAssemblies.Select(a => ILLinkify(a))); + _trimmedAssemblies = new List(_trimmedAssemblies.Select(a => ILLinkify(a))); + + static string ILLinkify(string rootedAssembly) + { + // For compatibility with IL Linker, the parameter could be a file name or an assembly name. + // This is the logic IL Linker uses to decide how to interpret the string. Really. + string simpleName; + if (File.Exists(rootedAssembly)) + simpleName = Path.GetFileNameWithoutExtension(rootedAssembly); + else + simpleName = rootedAssembly; + return simpleName; + } + + // Root whatever assemblies were specified on the command line + foreach (var rootedAssembly in _rootedAssemblies) + { + // We only root the module type. The rest will fall out because we treat _rootedAssemblies + // same as conditionally rooted ones and here we're fulfilling the condition ("something is used"). + compilationRoots.Add( + new GenericRootProvider(typeSystemContext.GetModuleForSimpleName(rootedAssembly), + (ModuleDesc module, IRootingServiceProvider rooter) => rooter.AddCompilationRoot(module.GetGlobalModuleType(), "Command line root"))); + } + + // + // Compile + // + + CompilationBuilder builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup); + + string compilationUnitPrefix = _multiFile ? System.IO.Path.GetFileNameWithoutExtension(_outputFilePath) : ""; + builder.UseCompilationUnitPrefix(compilationUnitPrefix); + + if (_mibcFilePaths.Count > 0) + ((RyuJitCompilationBuilder)builder).UseProfileData(_mibcFilePaths); + + PInvokeILEmitterConfiguration pinvokePolicy = new ConfigurablePInvokePolicy(typeSystemContext.Target, _directPInvokes, _directPInvokeLists); + + ILProvider ilProvider = new CoreRTILProvider(); + + List> featureSwitches = new List>(); + foreach (var switchPair in _featureSwitches) + { + string[] switchAndValue = switchPair.Split('='); + if (switchAndValue.Length != 2 + || !bool.TryParse(switchAndValue[1], out bool switchValue)) + throw new CommandLineException($"Unexpected feature switch pair '{switchPair}'"); + featureSwitches.Add(new KeyValuePair(switchAndValue[0], switchValue)); + } + ilProvider = new FeatureSwitchManager(ilProvider, featureSwitches); + + var logger = new Logger(Console.Out, _isVerbose, ProcessWarningCodes(_suppressedWarnings), _singleWarn, _singleWarnEnabledAssemblies, _singleWarnDisabledAssemblies); + + var stackTracePolicy = _emitStackTraceData ? + (StackTraceEmissionPolicy)new EcmaMethodStackTraceEmissionPolicy() : new NoStackTraceEmissionPolicy(); + + MetadataBlockingPolicy mdBlockingPolicy; + ManifestResourceBlockingPolicy resBlockingPolicy; + UsageBasedMetadataGenerationOptions metadataGenerationOptions = default; + if (supportsReflection) + { + mdBlockingPolicy = _noMetadataBlocking + ? (MetadataBlockingPolicy)new NoMetadataBlockingPolicy() + : new BlockedInternalsBlockingPolicy(typeSystemContext); + + resBlockingPolicy = new ManifestResourceBlockingPolicy(featureSwitches); + + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.AnonymousTypeHeuristic; + if (_completeTypesMetadata) + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CompleteTypesOnly; + if (_scanReflection) + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.ReflectionILScanning; + if (_reflectedOnly) + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.ReflectedMembersOnly; + if (_rootDefaultAssemblies) + metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.RootDefaultAssemblies; + } + else + { + mdBlockingPolicy = new FullyBlockedMetadataBlockingPolicy(); + resBlockingPolicy = new FullyBlockedManifestResourceBlockingPolicy(); + } + + DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy = new DefaultDynamicInvokeThunkGenerationPolicy(); + + var flowAnnotations = new Dataflow.FlowAnnotations(logger, ilProvider); + + MetadataManager metadataManager = new UsageBasedMetadataManager( + compilationGroup, + typeSystemContext, + mdBlockingPolicy, + resBlockingPolicy, + _metadataLogFileName, + stackTracePolicy, + invokeThunkGenerationPolicy, + flowAnnotations, + metadataGenerationOptions, + logger, + featureSwitches, + _conditionallyRootedAssemblies.Concat(_rootedAssemblies), + _trimmedAssemblies); + + InteropStateManager interopStateManager = new InteropStateManager(typeSystemContext.GeneratedAssembly); + InteropStubManager interopStubManager = new UsageBasedInteropStubManager(interopStateManager, pinvokePolicy); + + // Unless explicitly opted in at the command line, we enable scanner for retail builds by default. + // We also don't do this for multifile because scanner doesn't simulate inlining (this would be + // fixable by using a CompilationGroup for the scanner that has a bigger worldview, but + // let's cross that bridge when we get there). + bool useScanner = _useScanner || + (_optimizationMode != OptimizationMode.None && !_multiFile); + + useScanner &= !_noScanner; + + // Enable static data preinitialization in optimized builds. + bool preinitStatics = _preinitStatics || + (_optimizationMode != OptimizationMode.None && !_multiFile); + preinitStatics &= !_noPreinitStatics; + + var preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, preinitStatics); + builder + .UseILProvider(ilProvider) + .UsePreinitializationManager(preinitManager); + + ILScanResults scanResults = null; + if (useScanner) + { + ILScannerBuilder scannerBuilder = builder.GetILScannerBuilder() + .UseCompilationRoots(compilationRoots) + .UseMetadataManager(metadataManager) + .UseSingleThread(enable: _singleThreaded) + .UseInteropStubManager(interopStubManager); + + if (_scanDgmlLogFileName != null) + scannerBuilder.UseDependencyTracking(_generateFullScanDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); + + IILScanner scanner = scannerBuilder.ToILScanner(); + + scanResults = scanner.Scan(); + + metadataManager = ((UsageBasedMetadataManager)metadataManager).ToAnalysisBasedMetadataManager(); + + interopStubManager = scanResults.GetInteropStubManager(interopStateManager, pinvokePolicy); + } + + DebugInformationProvider debugInfoProvider = _enableDebugInfo ? + (_ilDump == null ? new DebugInformationProvider() : new ILAssemblyGeneratingMethodDebugInfoProvider(_ilDump, new EcmaOnlyDebugInformationProvider())) : + new NullDebugInformationProvider(); + + DependencyTrackingLevel trackingLevel = _dgmlLogFileName == null ? + DependencyTrackingLevel.None : (_generateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); + + compilationRoots.Add(metadataManager); + compilationRoots.Add(interopStubManager); + + builder + .UseInstructionSetSupport(instructionSetSupport) + .UseBackendOptions(_codegenOptions) + .UseMethodBodyFolding(enable: _methodBodyFolding) + .UseSingleThread(enable: _singleThreaded) + .UseMetadataManager(metadataManager) + .UseInteropStubManager(interopStubManager) + .UseLogger(logger) + .UseDependencyTracking(trackingLevel) + .UseCompilationRoots(compilationRoots) + .UseOptimizationMode(_optimizationMode) + .UseSecurityMitigationOptions(securityMitigationOptions) + .UseDebugInfoProvider(debugInfoProvider); + + if (scanResults != null) + { + // If we have a scanner, feed the vtable analysis results to the compilation. + // This could be a command line switch if we really wanted to. + builder.UseVTableSliceProvider(scanResults.GetVTableLayoutInfo()); + + // If we have a scanner, feed the generic dictionary results to the compilation. + // This could be a command line switch if we really wanted to. + builder.UseGenericDictionaryLayoutProvider(scanResults.GetDictionaryLayoutInfo()); + + // If we have a scanner, we can drive devirtualization using the information + // we collected at scanning time (effectively sealing unsealed types if possible). + // This could be a command line switch if we really wanted to. + builder.UseDevirtualizationManager(scanResults.GetDevirtualizationManager()); + + // If we use the scanner's result, we need to consult it to drive inlining. + // This prevents e.g. devirtualizing and inlining methods on types that were + // never actually allocated. + builder.UseInliningPolicy(scanResults.GetInliningPolicy()); + + // Use an error provider that prevents us from re-importing methods that failed + // to import with an exception during scanning phase. We would see the same failure during + // compilation, but before RyuJIT gets there, it might ask questions that we don't + // have answers for because we didn't scan the entire method. + builder.UseMethodImportationErrorProvider(scanResults.GetMethodImportationErrorProvider()); + } + + ICompilation compilation = builder.ToCompilation(); + + ObjectDumper dumper = _mapFileName != null ? new ObjectDumper(_mapFileName) : null; + + CompilationResults compilationResults = compilation.Compile(_outputFilePath, dumper); + if (_exportsFile != null) + { + ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, _exportsFile); + foreach (var compilationRoot in compilationRoots) + { + if (compilationRoot is ExportedMethodsRootProvider provider) + defFileWriter.AddExportedMethods(provider.ExportedMethods); + } + + defFileWriter.EmitExportedMethods(); + } + + typeSystemContext.LogWarnings(logger); + + if (_dgmlLogFileName != null) + compilationResults.WriteDependencyLog(_dgmlLogFileName); + + if (scanResults != null) + { + if (_scanDgmlLogFileName != null) + scanResults.WriteDependencyLog(_scanDgmlLogFileName); + + // If the scanner and compiler don't agree on what to compile, the outputs of the scanner might not actually be usable. + // We are going to check this two ways: + // 1. The methods and types generated during compilation are a subset of method and types scanned + // 2. The methods and types scanned are a subset of methods and types compiled (this has a chance to hold for unoptimized builds only). + + // Check that methods and types generated during compilation are a subset of method and types scanned + bool scanningFail = false; + DiffCompilationResults(ref scanningFail, compilationResults.CompiledMethodBodies, scanResults.CompiledMethodBodies, + "Methods", "compiled", "scanned", method => !(method.GetTypicalMethodDefinition() is EcmaMethod) || method.Name == "ThrowPlatformNotSupportedException" || method.Name == "ThrowArgumentOutOfRangeException"); + DiffCompilationResults(ref scanningFail, compilationResults.ConstructedEETypes, scanResults.ConstructedEETypes, + "EETypes", "compiled", "scanned", type => !(type.GetTypeDefinition() is EcmaType)); + + // If optimizations are enabled, the results will for sure not match in the other direction due to inlining, etc. + // But there's at least some value in checking the scanner doesn't expand the universe too much in debug. + if (_optimizationMode == OptimizationMode.None) + { + // Check that methods and types scanned are a subset of methods and types compiled + + // If we find diffs here, they're not critical, but still might be causing a Size on Disk regression. + bool dummy = false; + + // We additionally skip methods in SIMD module because there's just too many intrisics to handle and IL scanner + // doesn't expand them. They would show up as noisy diffs. + DiffCompilationResults(ref dummy, scanResults.CompiledMethodBodies, compilationResults.CompiledMethodBodies, + "Methods", "scanned", "compiled", method => !(method.GetTypicalMethodDefinition() is EcmaMethod) || method.OwningType.IsIntrinsic); + DiffCompilationResults(ref dummy, scanResults.ConstructedEETypes, compilationResults.ConstructedEETypes, + "EETypes", "scanned", "compiled", type => !(type.GetTypeDefinition() is EcmaType)); + } + + if (scanningFail) + throw new Exception("Scanning failure"); + } + + if (debugInfoProvider is IDisposable) + ((IDisposable)debugInfoProvider).Dispose(); + + preinitManager.LogStatistics(logger); + + return 0; + } + + [System.Diagnostics.Conditional("DEBUG")] + private void DiffCompilationResults(ref bool result, IEnumerable set1, IEnumerable set2, string prefix, + string set1name, string set2name, Predicate filter) + { + HashSet diff = new HashSet(set1); + diff.ExceptWith(set2); + + // TODO: move ownership of compiler-generated entities to CompilerTypeSystemContext. + // https://github.com/dotnet/corert/issues/3873 + diff.RemoveWhere(filter); + + if (diff.Count > 0) + { + result = true; + + Console.WriteLine($"*** {prefix} {set1name} but not {set2name}:"); + + foreach (var d in diff) + { + Console.WriteLine(d.ToString()); + } + } + } + + private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) + { + ModuleDesc systemModule = context.SystemModule; + + TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName, false, (typeDefName, module, throwIfNotFound) => + { + return (MetadataType)context.GetCanonType(typeDefName) + ?? CustomAttributeTypeNameParser.ResolveCustomAttributeTypeDefinitionName(typeDefName, module, throwIfNotFound); + }); + if (foundType == null) + throw new CommandLineException($"Type '{typeName}' not found"); + + return foundType; + } + + private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) + { + if (_singleMethodName == null && _singleMethodTypeName == null && _singleMethodGenericArgs == null) + return null; + + if (_singleMethodName == null || _singleMethodTypeName == null) + throw new CommandLineException("Both method name and type name are required parameters for single method mode"); + + TypeDesc owningType = FindType(context, _singleMethodTypeName); + + // TODO: allow specifying signature to distinguish overloads + MethodDesc method = owningType.GetMethod(_singleMethodName, null); + if (method == null) + throw new CommandLineException($"Method '{_singleMethodName}' not found in '{_singleMethodTypeName}'"); + + if (method.HasInstantiation != (_singleMethodGenericArgs != null) || + (method.HasInstantiation && (method.Instantiation.Length != _singleMethodGenericArgs.Count))) + { + throw new CommandLineException( + $"Expected {method.Instantiation.Length} generic arguments for method '{_singleMethodName}' on type '{_singleMethodTypeName}'"); + } + + if (method.HasInstantiation) + { + List genericArguments = new List(); + foreach (var argString in _singleMethodGenericArgs) + genericArguments.Add(FindType(context, argString)); + method = method.MakeInstantiatedMethod(genericArguments.ToArray()); + } + + return method; + } + + private static bool DumpReproArguments(CodeGenerationFailedException ex) + { + Console.WriteLine("To repro, add following arguments to the command line:"); + + MethodDesc failingMethod = ex.Method; + + var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)failingMethod.Context.SystemModule); + + Console.Write($"--singlemethodtypename \"{formatter.FormatName(failingMethod.OwningType, true)}\""); + Console.Write($" --singlemethodname {failingMethod.Name}"); + + for (int i = 0; i < failingMethod.Instantiation.Length; i++) + Console.Write($" --singlemethodgenericarg \"{formatter.FormatName(failingMethod.Instantiation[i], true)}\""); + + return false; + } + + private static IEnumerable ProcessWarningCodes(IEnumerable warningCodes) + { + foreach (string value in warningCodes) + { + string[] values = value.Split(new char[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string id in values) + { + if (!id.StartsWith("IL", StringComparison.Ordinal) || !ushort.TryParse(id.Substring(2), out ushort code)) + continue; + + yield return code; + } + } + } + + private static int Main(string[] args) + { +#if DEBUG + try + { + return new Program().Run(args); + } + catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) + { + throw new NotSupportedException(); // Unreachable + } +#else + try + { + return new Program().Run(args); + } + catch (Exception e) + { + Console.Error.WriteLine("Error: " + e.Message); + Console.Error.WriteLine(e.ToString()); + return 1; + } +#endif + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler/RdXmlRootProvider.cs b/src/coreclr/tools/aot/ILCompiler/RdXmlRootProvider.cs new file mode 100644 index 00000000000000..e1d976eb3118e9 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler/RdXmlRootProvider.cs @@ -0,0 +1,211 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using AssemblyName = System.Reflection.AssemblyName; + +namespace ILCompiler +{ + /// + /// Compilation root provider that provides roots based on the RD.XML file format. + /// Only supports a subset of the Runtime Directives configuration file format. + /// + /// https://msdn.microsoft.com/en-us/library/dn600639(v=vs.110).aspx + internal class RdXmlRootProvider : ICompilationRootProvider + { + private XElement _documentRoot; + private TypeSystemContext _context; + + public RdXmlRootProvider(TypeSystemContext context, string rdXmlFileName) + { + _context = context; + _documentRoot = XElement.Load(rdXmlFileName); + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + var libraryOrApplication = _documentRoot.Elements().Single(); + + if (libraryOrApplication.Name.LocalName != "Library" && libraryOrApplication.Name.LocalName != "Application") + throw new NotSupportedException($"{libraryOrApplication.Name.LocalName} is not a supported top level Runtime Directive. Supported top level Runtime Directives are \"Library\" and \"Application\"."); + + if (libraryOrApplication.Attributes().Any()) + throw new NotSupportedException($"The {libraryOrApplication.Name.LocalName} Runtime Directive does not support any attributes"); + + foreach (var element in libraryOrApplication.Elements()) + { + switch (element.Name.LocalName) + { + case "Assembly": + ProcessAssemblyDirective(rootProvider, element); + break; + + default: + throw new NotSupportedException($"\"{element.Name.LocalName}\" is not a supported Runtime Directive."); + } + } + } + + private void ProcessAssemblyDirective(IRootingServiceProvider rootProvider, XElement assemblyElement) + { + var assemblyNameAttribute = assemblyElement.Attribute("Name"); + if (assemblyNameAttribute == null) + throw new Exception("The \"Name\" attribute is required on the \"Assembly\" Runtime Directive."); + + ModuleDesc assembly = _context.ResolveAssembly(new AssemblyName(assemblyNameAttribute.Value)); + + rootProvider.RootModuleMetadata(assembly, "RD.XML root"); + + var dynamicDegreeAttribute = assemblyElement.Attribute("Dynamic"); + if (dynamicDegreeAttribute != null) + { + if (dynamicDegreeAttribute.Value != "Required All") + throw new NotSupportedException($"\"{dynamicDegreeAttribute.Value}\" is not a supported value for the \"Dynamic\" attribute of the \"Assembly\" Runtime Directive. Supported values are \"Required All\"."); + + foreach (TypeDesc type in ((EcmaModule)assembly).GetAllTypes()) + { + RootingHelpers.TryRootType(rootProvider, type, "RD.XML root"); + } + } + + foreach (var element in assemblyElement.Elements()) + { + switch (element.Name.LocalName) + { + case "Type": + ProcessTypeDirective(rootProvider, assembly, element); + break; + default: + throw new NotSupportedException($"\"{element.Name.LocalName}\" is not a supported Runtime Directive."); + } + } + } + + private void ProcessTypeDirective(IRootingServiceProvider rootProvider, ModuleDesc containingModule, XElement typeElement) + { + var typeNameAttribute = typeElement.Attribute("Name"); + if (typeNameAttribute == null) + throw new Exception("The \"Name\" attribute is required on the \"Type\" Runtime Directive."); + + string typeName = typeNameAttribute.Value; + TypeDesc type = containingModule.GetTypeByCustomAttributeTypeName(typeName); + + var dynamicDegreeAttribute = typeElement.Attribute("Dynamic"); + if (dynamicDegreeAttribute != null) + { + if (dynamicDegreeAttribute.Value != "Required All") + throw new NotSupportedException($"\"{dynamicDegreeAttribute.Value}\" is not a supported value for the \"Dynamic\" attribute of the \"Type\" Runtime Directive. Supported values are \"Required All\"."); + + RootingHelpers.RootType(rootProvider, type, "RD.XML root"); + } + + var marshalStructureDegreeAttribute = typeElement.Attribute("MarshalStructure"); + if (marshalStructureDegreeAttribute != null && type is DefType defType) + { + if (marshalStructureDegreeAttribute.Value != "Required All") + throw new NotSupportedException($"\"{marshalStructureDegreeAttribute.Value}\" is not a supported value for the \"MarshalStructure\" attribute of the \"Type\" Runtime Directive. Supported values are \"Required All\"."); + + rootProvider.RootStructMarshallingData(defType, "RD.XML root"); + } + + var marshalDelegateDegreeAttribute = typeElement.Attribute("MarshalDelegate"); + if (marshalDelegateDegreeAttribute != null && type.IsDelegate) + { + if (marshalDelegateDegreeAttribute.Value != "Required All") + throw new NotSupportedException($"\"{marshalDelegateDegreeAttribute.Value}\" is not a supported value for the \"MarshalDelegate\" attribute of the \"Type\" Runtime Directive. Supported values are \"Required All\"."); + + rootProvider.RootDelegateMarshallingData((DefType)type, "RD.XML root"); + } + + foreach (var element in typeElement.Elements()) + { + switch (element.Name.LocalName) + { + case "Method": + ProcessMethodDirective(rootProvider, containingModule, type, element); + break; + default: + throw new NotSupportedException($"\"{element.Name.LocalName}\" is not a supported Runtime Directive."); + } + } + } + + private void ProcessMethodDirective(IRootingServiceProvider rootProvider, ModuleDesc containingModule, TypeDesc containingType, XElement methodElement) + { + var methodNameAttribute = methodElement.Attribute("Name"); + if (methodNameAttribute == null) + throw new Exception("The \"Name\" attribute is required on the \"Method\" Runtime Directive."); + + var parameter = new List(); + var instArgs = new List(); + foreach (var element in methodElement.Elements()) + { + switch (element.Name.LocalName) + { + case "Parameter": + string paramName = element.Attribute("Name").Value; + parameter.Add(containingModule.GetTypeByCustomAttributeTypeName(paramName)); + break; + case "GenericArgument": + string instArgName = element.Attribute("Name").Value; + instArgs.Add(containingModule.GetTypeByCustomAttributeTypeName(instArgName)); + break; + default: + throw new NotSupportedException($"\"{element.Name.LocalName}\" is not a supported Runtime Directive."); + } + } + + static bool SignatureMatches(MethodDesc method, List parameter) + { + if (parameter.Count != method.Signature.Length) + return false; + + for (int i = 0; i < method.Signature.Length; i++) + { + if (method.Signature[i] != parameter[i]) + return false; + } + + return true; + } + + bool rootedAnyMethod = false; + string methodName = methodNameAttribute.Value; + foreach (var m in containingType.GetMethods()) + { + var method = m; + if (method.Name != methodName) + continue; + + if (instArgs.Count != method.Instantiation.Length) + continue; + + if (instArgs.Count > 0) + { + var methodInst = new Instantiation(instArgs.ToArray()); + method = method.MakeInstantiatedMethod(methodInst); + } + + if (parameter.Count > 0 && !SignatureMatches(method, parameter)) + continue; + + RootingHelpers.RootMethod(rootProvider, method, "RD.XML root"); + rootedAnyMethod = true; + } + + if (!rootedAnyMethod) + { + string parameterString = parameter.Count > 0 ? "(" + string.Join(", ", parameter) + ")" : null; + string instantiationString = instArgs.Count > 0 ? "<" + string.Join(", ", instArgs) + ">" : null; + throw new Exception($"Could not find Method(s) {containingType}.{methodName}{instantiationString}{parameterString} specified by a Runtime Directive."); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler/repro/Program.cs b/src/coreclr/tools/aot/ILCompiler/repro/Program.cs new file mode 100644 index 00000000000000..0a97c922c939ba --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler/repro/Program.cs @@ -0,0 +1,3 @@ +using System; + +Console.WriteLine("Hello world"); diff --git a/src/coreclr/tools/aot/ILCompiler/repro/repro.csproj b/src/coreclr/tools/aot/ILCompiler/repro/repro.csproj new file mode 100644 index 00000000000000..0194a7f6e43201 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler/repro/repro.csproj @@ -0,0 +1,45 @@ + + + net6.0 + Exe + x64;x86 + AnyCPU + false + false + linux-x64;win-x64;osx-x64 + Debug;Release;Checked + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreclr/tools/aot/ILCompiler/reproNative/.gitignore b/src/coreclr/tools/aot/ILCompiler/reproNative/.gitignore new file mode 100644 index 00000000000000..4b5f2495866ef2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler/reproNative/.gitignore @@ -0,0 +1,3 @@ +syntax: glob + +x64 diff --git a/src/coreclr/tools/aot/ILCompiler/reproNative/Directory.Build.props b/src/coreclr/tools/aot/ILCompiler/reproNative/Directory.Build.props new file mode 100644 index 00000000000000..8c119d5413b585 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler/reproNative/Directory.Build.props @@ -0,0 +1,2 @@ + + diff --git a/src/coreclr/tools/aot/ILCompiler/reproNative/Directory.Build.targets b/src/coreclr/tools/aot/ILCompiler/reproNative/Directory.Build.targets new file mode 100644 index 00000000000000..8c119d5413b585 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler/reproNative/Directory.Build.targets @@ -0,0 +1,2 @@ + + diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.vcxproj b/src/coreclr/tools/aot/ILCompiler/reproNative/reproNative.vcxproj similarity index 58% rename from src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.vcxproj rename to src/coreclr/tools/aot/ILCompiler/reproNative/reproNative.vcxproj index a193e2998e9e8d..c369a6e279ed44 100644 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogAnalyzer.vcxproj +++ b/src/coreclr/tools/aot/ILCompiler/reproNative/reproNative.vcxproj @@ -1,51 +1,42 @@ - - - Debug - Win32 - - - Release - Win32 - Debug x64 + + Checked + x64 + Release x64 - 16.0 + {ECB5D162-A31B-45FF-87C7-2E92BD445F5A} Win32Proj - {c6955b43-4176-4259-8831-9a60596e373d} - StressLog + reproNative 10.0 - StressLogAnalyzer + ..\..\..\..\ + $(CoreClrSourceRoot)nativeaot\ + $(CoreClrSourceRoot)..\..\artifacts\ + advapi32.lib;bcrypt.lib;crypt32.lib;iphlpapi.lib;kernel32.lib;mswsock.lib;ncrypt.lib;normaliz.lib;ntdll.lib;ole32.lib;oleaut32.lib;secur32.lib;user32.lib;version.lib;ws2_32.lib - + Application true v142 Unicode - + Application false v142 true Unicode - - Application - true - v142 - Unicode - Application false @@ -56,94 +47,91 @@ - - - - - - + - + - - true - - + false - - true + + false false - true - + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;HOST_AMD64;%(PreprocessorDefinitions) true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true + $(CoreClrSourceRoot)gc;$(CoreClrSourceRoot)gc\env + 4477 + MultiThreadedDebug + false + ProgramDatabase Console true + $(ArtifactsRoot)bin\repro\x64\Debug\repro.obj;$(Win32SDKLibs);%(AdditionalDependencies);$(ArtifactsRoot)bin\coreclr\windows.x64.Debug\aotsdk\Runtime.WorkstationGC.lib - + Level3 + MaxSpeed true true + WIN32;_DEBUG;_CONSOLE;_LIB;HOST_AMD64;%(PreprocessorDefinitions) true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true + $(CoreClrSourceRoot)gc;$(CoreClrSourceRoot)gc\env + 4477 + MultiThreadedDebug Console + true true true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true + $(ArtifactsRoot)bin\repro\x64\Checked\repro.obj;$(Win32SDKLibs);%(AdditionalDependencies);$(ArtifactsRoot)bin\coreclr\windows.x64.Checked\aotsdk\Runtime.WorkstationGC.lib Level3 + MaxSpeed true true + WIN32;NDEBUG;_CONSOLE;_LIB;HOST_AMD64;%(PreprocessorDefinitions) true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true + $(CoreClrSourceRoot)gc;$(CoreClrSourceRoot)gc\env + 4477 + MultiThreaded Console + true true true - true + $(ArtifactsRoot)bin\repro\x64\Release\repro.obj;$(Win32SDKLibs);%(AdditionalDependencies);$(ArtifactsRoot)bin\coreclr\windows.x64.Release\aotsdk\Runtime.WorkstationGC.lib - + + + + + - \ No newline at end of file + diff --git a/src/coreclr/tools/aot/ObjWriter/.editorconfig b/src/coreclr/tools/aot/ObjWriter/.editorconfig new file mode 100644 index 00000000000000..0c59ada6fa2231 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/.editorconfig @@ -0,0 +1,3 @@ +[*.{cpp,h}] + +indent_size = 2 diff --git a/src/coreclr/tools/aot/ObjWriter/CMakeLists.txt b/src/coreclr/tools/aot/ObjWriter/CMakeLists.txt new file mode 100644 index 00000000000000..2c55045803f73a --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/CMakeLists.txt @@ -0,0 +1,26 @@ +set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/objwriter.exports) + +set(LLVM_LINK_COMPONENTS + AllTargetsDescs + AllTargetsInfos + MC + Support + ) + +message(STATUS "ObjWriter configuring with (${CMAKE_BUILD_TYPE}) build type and (${LLVM_DEFAULT_TARGET_TRIPLE}) default target triple") + +include_directories(${CORECLR_INCLUDE_DIR}) + +add_llvm_library(objwriter SHARED + objwriter.cpp + debugInfo/codeView/codeViewTypeBuilder.cpp + debugInfo/codeView/codeViewTypeBuilder.h + debugInfo/dwarf/dwarfTypeBuilder.cpp + debugInfo/dwarf/dwarfTypeBuilder.h + debugInfo/dwarf/dwarfGen.cpp + debugInfo/dwarf/dwarfGen.h + debugInfo/dwarf/dwarfAbbrev.cpp + debugInfo/dwarf/dwarfAbbrev.h + debugInfo/typeBuilder.h + objwriter.h # Visual Studio generator doesn't include necessary header files into the project automatically +) diff --git a/src/coreclr/tools/aot/ObjWriter/README.md b/src/coreclr/tools/aot/ObjWriter/README.md new file mode 100644 index 00000000000000..af1d3efcd7e3c6 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/README.md @@ -0,0 +1,7 @@ +# Native Object Writer library + +This directory contains a native object file writer library based on LLVM. This library exposes C APIs to emit ELF/Mach-O/PE object files. + +The implementation is based on LLVM's assembler APIs - these are APIs intended to be consumed by assembly language compilers and are therefore close to the underlying object file formats. When in doubt, look at how the assemblers (e.g. llvm-ml in the tools directory of LLVM) use these APIs. + +The build script clones the appropriate version of the LLVM source tree, applies a patch that adds a couple of things we need, and builds the repo with this directory added as an out-of-tree tool. diff --git a/src/coreclr/tools/aot/ObjWriter/build.cmd b/src/coreclr/tools/aot/ObjWriter/build.cmd new file mode 100644 index 00000000000000..77cbc45d2818d3 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/build.cmd @@ -0,0 +1,113 @@ +@echo off +setlocal + +set "ScriptDir=%~dp0" +set "ArtifactsDir=%~1" +set "RepoRoot=%~2" +set "BuildArch=%~3" +set "TargetArch=%~4" +set "BuildType=%~5" +if "%BuildType%"=="" set "BuildType=Release" + +set LLVMBranch=release/12.x + +:: Check that we have enough arguments +if "%TargetArch%"=="" ( + echo Usage: %~nx0 ArtifactsDir RepoRoot BuildArch TargetArch [BuildType] + goto Error +) + +cd "%ArtifactsDir%" || goto Error + +if not exist llvm-project ( + rem Clone the LLVM repo + git clone --no-checkout --depth 1 -b %LLVMBranch% https://github.com/llvm/llvm-project.git || goto Error + cd llvm-project || goto Error + :: Purposefuly ignoring exit codes of sparse-checkout so that this works with git lower than 2.26 + git sparse-checkout init + git sparse-checkout set /llvm/* !/llvm/test/* + git checkout %LLVMBranch% || goto Error + cd llvm || goto Error + goto ApplyPatch +) + +:: Check whether the current diff is the same as the patch +cd llvm-project\llvm || goto Error +if not exist build mkdir build +set "DiffFile=build\llvm_%RANDOM%.patch" +git diff --full-index >"%DiffFile%" || goto Error +fc "%DiffFile%" "%ScriptDir%llvm.patch" +if %ErrorLevel% == 0 ( + rem The current diff is the same as the patch + del "%DiffFile%" + goto PatchApplied +) else ( + echo LLVM changes are saved to %CD%/%DiffFile% and overwritten with %ScriptDir%llvm.patch +) + +:ApplyPatch + +:: Clean the tree and apply the patch +git restore . || goto Error +git apply "%ScriptDir%llvm.patch" || goto Error + +:PatchApplied + +:: Init VS environment +call "%RepoRoot%eng\native\init-vs-env.cmd" || goto Error + +:: Set CMakePath by evaluating the output from set-cmake-path.ps1 +for /f "delims=" %%a in ('powershell -NoProfile -ExecutionPolicy ByPass "& ""%RepoRoot%eng\native\set-cmake-path.ps1"""') do %%a +echo Using CMake at "%CMakePath%" + +:: Configure and build objwriter +if /i "%BuildArch%"=="%TargetArch%" ( + call :BuildLlvmTarget objwriter "%TargetArch%" || goto Error + exit /b 0 +) + +rem Cross-build: first build llvm-tblgen.exe for the build architecture +set "TableGen=%CD%\build\%BuildArch%\%BuildType%\bin\llvm-tblgen.exe" + +if not exist "%TableGen%" ( + echo Building llvm-tablegen.exe + call :BuildLlvmTarget llvm-tblgen "%BuildArch%" || goto Error + if not exist "%TableGen%" goto Error +) + +rem Now use llvm-tblgen.exe to build objwriter for the target architecture +set "CMakeArch=%TargetArch%" +if /i "%TargetArch%"=="x86" set "CMakeArch=Win32" + +call :BuildLlvmTarget objwriter "%TargetArch%" "-A %CMakeArch% -DLLVM_TABLEGEN=%TableGen%" || goto Error +exit /b 0 + +:BuildLlvmTarget +set "Target=%~1" +set "Arch=%~2" +set "ExtraCMakeArgs=%~3" + +"%CMakePath%" -S . -B "build\%Arch%" ^ + %ExtraCMakeArgs% ^ + -DCMAKE_TOOLCHAIN_FILE="%ScriptDir%toolchain.cmake" ^ + -DCMAKE_BUILD_TYPE=%BuildType% ^ + -DCMAKE_INSTALL_PREFIX=install ^ + -DLLVM_BUILD_TOOLS=0 ^ + -DLLVM_ENABLE_TERMINFO=0 ^ + -DLLVM_INCLUDE_UTILS=0 ^ + -DLLVM_INCLUDE_RUNTIMES=0 ^ + -DLLVM_INCLUDE_EXAMPLES=0 ^ + -DLLVM_INCLUDE_TESTS=0 ^ + -DLLVM_INCLUDE_DOCS=0 ^ + -DLLVM_TARGETS_TO_BUILD="AArch64;ARM;X86" ^ + -DLLVM_EXTERNAL_OBJWRITER_SOURCE_DIR="%ScriptDir%\" ^ + -DCORECLR_INCLUDE_DIR="%RepoRoot%src\coreclr\inc" ^ + || goto Error + +echo Executing "%CMakePath%" --build "build\%Arch%" --config %BuildType% --target %Target% -- -m +"%CMakePath%" --build "build\%Arch%" --config %BuildType% --target %Target% -- -m || goto Error +echo Done building target %Target% +exit /b 0 + +:Error +exit /b 1 diff --git a/src/coreclr/tools/aot/ObjWriter/build.sh b/src/coreclr/tools/aot/ObjWriter/build.sh new file mode 100644 index 00000000000000..2f7acbd622c987 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/build.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash + +ScriptDir="$(cd "$(dirname "$0")"; pwd -P)" +ArtifactsDir="$1" +RepoRoot="$2" +BuildArch="$3" +TargetArch="$4" +BuildType="${5:-Release}" +CompilerId="$(echo "$6" | tr "[:upper:]" "[:lower:]")" + +LLVMBranch="release/12.x" + +# Check that we have enough arguments +if [ $# -lt 4 ]; then + echo "Usage: $(basename $0) ArtifactsDir RepoRoot BuildArch TargetArch [BuildType [CompilerId]]" + exit 1 +fi + +if [ -z "$CompilerId" ]; then + Compiler=clang + CompilerMajorVer= + CompilerMinorVer= +# Expecting a compiler id similar to -clang9 or -gcc10.2. See also eng/native/build-commons.sh. +elif [[ "$CompilerId" =~ ^-?([a-z]+)(-?([0-9]+)(\.([0-9]+))?)?$ ]]; then + Compiler=${BASH_REMATCH[1]} + CompilerMajorVer=${BASH_REMATCH[3]} + CompilerMinorVer=${BASH_REMATCH[5]} + if [[ "$Compiler" == "clang" && -n "$CompilerMajorVer" && "$CompilerMajorVer" -le 6 + && -z "$CompilerMinorVer" ]]; then + CompilerMinorVer=0 + fi +else + echo "Unexpected compiler identifier '$6'" + exit 1 +fi + +cd "$ArtifactsDir" || exit 1 +PatchApplied=0 + +if [ ! -d llvm-project ]; then + # Clone the LLVM repo + git clone --no-checkout --depth 1 -b $LLVMBranch https://github.com/llvm/llvm-project.git || exit 1 + cd llvm-project || exit 1 + # Purposefuly ignoring exit codes of sparse-checkout so that this works with git lower than 2.26 + git sparse-checkout init + git sparse-checkout set /llvm/\* !/llvm/test/\* + git checkout $LLVMBranch || exit 1 + cd llvm || exit 1 +else + # Check whether the current diff is the same as the patch + cd llvm-project/llvm || exit 1 + mkdir -p build + DiffFile="build/llvm_$RANDOM.patch" + git diff --full-index >"$DiffFile" || exit 1 + cmp -s "$DiffFile" "$ScriptDir/llvm.patch" + if [ $? -eq 0 ]; then + # The current diff is the same as the patch + rm "$DiffFile" + PatchApplied=1 + else + echo "LLVM changes are saved to $PWD/$DiffFile and overwritten with $ScriptDir/llvm.patch" + fi +fi + +if [ "$PatchApplied" -ne 1 ]; then + # Clean the tree and apply the patch + git checkout -- . || exit 1 + git apply "$ScriptDir/llvm.patch" || exit 1 +fi + +# Configure and build objwriter +mkdir -p "build/$TargetArch" || exit 1 + +if [ "$TargetArch" != "$BuildArch" ]; then + export CROSSCOMPILE=1 +fi + +# Script arguments: +# +# +# [build flavor] [ninja] [scan-build] [cmakeargs] +"${RepoRoot}eng/native/gen-buildsys.sh" \ + "$PWD" "$PWD/build/$TargetArch" "$TargetArch" \ + "$Compiler" "$CompilerMajorVer" "$CompilerMinorVer" "$BuildType" \ + -DCMAKE_BUILD_TYPE="$BuildType" \ + -DCMAKE_INSTALL_PREFIX=install \ + -DLLVM_BUILD_TOOLS=0 \ + -DLLVM_ENABLE_TERMINFO=0 \ + -DLLVM_INCLUDE_UTILS=0 \ + -DLLVM_INCLUDE_RUNTIMES=0 \ + -DLLVM_INCLUDE_EXAMPLES=0 \ + -DLLVM_INCLUDE_TESTS=0 \ + -DLLVM_INCLUDE_DOCS=0 \ + -DLLVM_TARGETS_TO_BUILD="AArch64;ARM;X86" \ + -DLLVM_EXTERNAL_OBJWRITER_SOURCE_DIR="$ScriptDir" \ + -DCORECLR_INCLUDE_DIR="${RepoRoot}src/coreclr/inc" \ + || exit 1 + +# Get the number of available processors +Platform="$(uname)" +if [[ "$Platform" == "FreeBSD" ]]; then + NumProc=$(sysctl -n hw.ncpu) +elif [[ "$Platform" == "NetBSD" || "$Platform" == "SunOS" ]]; then + NumProc=$(getconf NPROCESSORS_ONLN) +elif [[ "$Platform" == "Darwin" ]]; then + NumProc=$(getconf _NPROCESSORS_ONLN) +else + NumProc=$(nproc --all) +fi + +MaxJobs=$((NumProc+1)) + +echo "Executing cmake --build \"build/$TargetArch\" --config \"$BuildType\" --target objwriter -j \"$MaxJobs\"" +cmake --build "build/$TargetArch" --config "$BuildType" --target objwriter -j "$MaxJobs" || exit 1 +echo "Done building target objwriter" diff --git a/src/coreclr/tools/aot/ObjWriter/debugInfo/codeView/codeViewTypeBuilder.cpp b/src/coreclr/tools/aot/ObjWriter/debugInfo/codeView/codeViewTypeBuilder.cpp new file mode 100644 index 00000000000000..de90c5319008d6 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/debugInfo/codeView/codeViewTypeBuilder.cpp @@ -0,0 +1,344 @@ +//===---- codeViewTypeBuilder.cpp -------------------------------*- C++ -*-===// +// +// type builder implementation using codeview::TypeTableBuilder +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +//===----------------------------------------------------------------------===// + +#include "codeViewTypeBuilder.h" +#include "llvm/BinaryFormat/COFF.h" +#include +#include + +UserDefinedCodeViewTypesBuilder::UserDefinedCodeViewTypesBuilder() + : Allocator(), TypeTable(Allocator) +{ + // We pretend that the MethodTable pointer in System.Object is VTable shape. + // We use the same "Vtable" for all types because the vtable shape debug + // record is not expressive enough to capture our vtable shape (where the + // vtable slots don't start at the beginning of the vtable). + VFTableShapeRecord vfTableShape(TypeRecordKind::VFTableShape); + ClassVTableTypeIndex = TypeTable.writeLeafType(vfTableShape); + + PointerRecord ptrToVfTableShape(ClassVTableTypeIndex, + TargetPointerSize == 8 ? PointerKind::Near64 : PointerKind::Near32, + PointerMode::LValueReference, + PointerOptions::None, + 0); + + VFuncTabTypeIndex = TypeTable.writeLeafType(ptrToVfTableShape); +} + +void UserDefinedCodeViewTypesBuilder::EmitCodeViewMagicVersion() { + Streamer->emitValueToAlignment(4); + Streamer->AddComment("Debug section magic"); + Streamer->emitIntValue(COFF::DEBUG_SECTION_MAGIC, 4); +} + +ClassOptions UserDefinedCodeViewTypesBuilder::GetCommonClassOptions() { + return ClassOptions(); +} + +void UserDefinedCodeViewTypesBuilder::EmitTypeInformation( + MCSection *TypeSection, + MCSection *StrSection) { + + if (TypeTable.empty()) + return; + + Streamer->SwitchSection(TypeSection); + EmitCodeViewMagicVersion(); + + TypeTable.ForEachRecord([&](TypeIndex FieldTypeIndex, + CVRecord Record) { + StringRef S(reinterpret_cast(Record.data().data()), Record.data().size()); + Streamer->emitBinaryData(S); + }); +} + +unsigned UserDefinedCodeViewTypesBuilder::GetEnumFieldListType( + uint64 Count, const EnumRecordTypeDescriptor *TypeRecords) { + ContinuationRecordBuilder CRB; + CRB.begin(ContinuationRecordKind::FieldList); +#ifndef NDEBUG + uint64 MaxInt = (unsigned int)-1; + assert(Count <= MaxInt && "There are too many fields inside enum"); +#endif + for (int i = 0; i < (int)Count; ++i) { + EnumRecordTypeDescriptor record = TypeRecords[i]; + EnumeratorRecord ER(MemberAccess::Public, APSInt::getUnsigned(record.Value), + record.Name); + CRB.writeMemberType(ER); + } + return TypeTable.insertRecord(CRB).getIndex(); +} + +unsigned UserDefinedCodeViewTypesBuilder::GetEnumTypeIndex( + const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords) { + + ClassOptions CO = GetCommonClassOptions(); + unsigned FieldListIndex = + GetEnumFieldListType(TypeDescriptor.ElementCount, TypeRecords); + TypeIndex FieldListIndexType = TypeIndex(FieldListIndex); + TypeIndex ElementTypeIndex = TypeIndex(TypeDescriptor.ElementType); + + EnumRecord EnumRecord(TypeDescriptor.ElementCount, CO, FieldListIndexType, + TypeDescriptor.Name, StringRef(), + ElementTypeIndex); + + TypeIndex Type = TypeTable.writeLeafType(EnumRecord); + UserDefinedTypes.push_back(std::make_pair(TypeDescriptor.Name, Type.getIndex())); + return Type.getIndex(); +} + +unsigned UserDefinedCodeViewTypesBuilder::GetClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor) { + TypeRecordKind Kind = + ClassDescriptor.IsStruct ? TypeRecordKind::Struct : TypeRecordKind::Class; + ClassOptions CO = ClassOptions::ForwardReference | GetCommonClassOptions(); + + ClassRecord CR(Kind, 0, CO, TypeIndex(), TypeIndex(), TypeIndex(), 0, + ClassDescriptor.Name, StringRef()); + TypeIndex FwdDeclTI = TypeTable.writeLeafType(CR); + return FwdDeclTI.getIndex(); +} + +unsigned UserDefinedCodeViewTypesBuilder::GetCompleteClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors, + const StaticDataFieldDescriptor *StaticsDescriptors) { + + ContinuationRecordBuilder CRB; + CRB.begin(ContinuationRecordKind::FieldList); + + uint16_t memberCount = 0; + if (ClassDescriptor.BaseClassId != 0) { + memberCount++; + AddBaseClass(CRB, ClassDescriptor.BaseClassId); + } + else if (!ClassDescriptor.IsStruct) { + memberCount++; + AddClassVTShape(CRB); + } + + for (int i = 0; i < ClassFieldsDescriptor.FieldsCount; ++i) { + DataFieldDescriptor desc = FieldsDescriptors[i]; + MemberAccess Access = MemberAccess::Public; + TypeIndex MemberBaseType(desc.FieldTypeIndex); + if (desc.Offset == 0xFFFFFFFF) + { + StaticDataMemberRecord SDMR(Access, MemberBaseType, desc.Name); + CRB.writeMemberType(SDMR); + } + else + { + int MemberOffsetInBytes = desc.Offset; + DataMemberRecord DMR(Access, MemberBaseType, MemberOffsetInBytes, + desc.Name); + CRB.writeMemberType(DMR); + } + memberCount++; + } + TypeIndex FieldListIndex = TypeTable.insertRecord(CRB); + TypeRecordKind Kind = + ClassDescriptor.IsStruct ? TypeRecordKind::Struct : TypeRecordKind::Class; + ClassOptions CO = GetCommonClassOptions(); + ClassRecord CR(Kind, memberCount, CO, FieldListIndex, + TypeIndex(), TypeIndex(), ClassFieldsDescriptor.Size, + ClassDescriptor.Name, StringRef()); + TypeIndex ClassIndex = TypeTable.writeLeafType(CR); + + UserDefinedTypes.push_back(std::make_pair(ClassDescriptor.Name, ClassIndex.getIndex())); + + return ClassIndex.getIndex(); +} + +unsigned UserDefinedCodeViewTypesBuilder::GetArrayTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ArrayTypeDescriptor &ArrayDescriptor) { + ContinuationRecordBuilder CRB; + CRB.begin(ContinuationRecordKind::FieldList); + + unsigned Offset = 0; + unsigned FieldsCount = 0; + + assert(ClassDescriptor.BaseClassId != 0); + + AddBaseClass(CRB, ClassDescriptor.BaseClassId); + FieldsCount++; + Offset += TargetPointerSize; + + MemberAccess Access = MemberAccess::Public; + TypeIndex IndexType = TypeIndex(SimpleTypeKind::Int32); + DataMemberRecord CountDMR(Access, IndexType, Offset, "count"); + CRB.writeMemberType(CountDMR); + FieldsCount++; + Offset += TargetPointerSize; + + if (ArrayDescriptor.IsMultiDimensional == 1) { + for (unsigned i = 0; i < ArrayDescriptor.Rank; ++i) { + DataMemberRecord LengthDMR(Access, TypeIndex(SimpleTypeKind::Int32), + Offset, ArrayDimentions.GetLengthName(i)); + CRB.writeMemberType(LengthDMR); + FieldsCount++; + Offset += 4; + } + + for (unsigned i = 0; i < ArrayDescriptor.Rank; ++i) { + DataMemberRecord BoundsDMR(Access, TypeIndex(SimpleTypeKind::Int32), + Offset, ArrayDimentions.GetBoundsName(i)); + CRB.writeMemberType(BoundsDMR); + FieldsCount++; + Offset += 4; + } + } + + TypeIndex ElementTypeIndex = TypeIndex(ArrayDescriptor.ElementType); + ArrayRecord AR(ElementTypeIndex, IndexType, ArrayDescriptor.Size, ""); + TypeIndex ArrayIndex = TypeTable.writeLeafType(AR); + DataMemberRecord ArrayDMR(Access, ArrayIndex, Offset, "values"); + CRB.writeMemberType(ArrayDMR); + FieldsCount++; + + TypeIndex FieldListIndex = TypeTable.insertRecord(CRB); + + assert(ClassDescriptor.IsStruct == false); + TypeRecordKind Kind = TypeRecordKind::Class; + ClassOptions CO = GetCommonClassOptions(); + ClassRecord CR(Kind, FieldsCount, CO, FieldListIndex, TypeIndex(), + TypeIndex(), ArrayDescriptor.Size, ClassDescriptor.Name, + StringRef()); + TypeIndex ClassIndex = TypeTable.writeLeafType(CR); + + UserDefinedTypes.push_back(std::make_pair(ClassDescriptor.Name, ClassIndex.getIndex())); + + return ClassIndex.getIndex(); +} + +unsigned UserDefinedCodeViewTypesBuilder::GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor) +{ + uint32_t elementType = PointerDescriptor.ElementType; + PointerKind pointerKind = PointerDescriptor.Is64Bit ? PointerKind::Near64 : PointerKind::Near32; + PointerMode pointerMode = PointerDescriptor.IsReference ? PointerMode::LValueReference : PointerMode::Pointer; + PointerOptions pointerOptions = PointerDescriptor.IsConst ? PointerOptions::Const : PointerOptions::None; + + PointerRecord PointerToClass(TypeIndex(elementType), pointerKind, pointerMode, pointerOptions, 0); + TypeIndex PointerIndex = TypeTable.writeLeafType(PointerToClass); + return PointerIndex.getIndex(); +} + +unsigned UserDefinedCodeViewTypesBuilder::GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes) +{ + std::vector argumentTypes; + argumentTypes.reserve(MemberDescriptor.NumberOfArguments); + for (uint16_t iArgument = 0; iArgument < MemberDescriptor.NumberOfArguments; iArgument++) + { + argumentTypes.emplace_back(ArgumentTypes[iArgument]); + } + + ArgListRecord ArgList(TypeRecordKind::ArgList, argumentTypes); + TypeIndex ArgumentList = TypeTable.writeLeafType(ArgList); + + MemberFunctionRecord MemberFunction(TypeIndex(MemberDescriptor.ReturnType), + TypeIndex(MemberDescriptor.ContainingClass), + TypeIndex(MemberDescriptor.TypeIndexOfThisPointer), + CallingConvention(MemberDescriptor.CallingConvention), + FunctionOptions::None, MemberDescriptor.NumberOfArguments, + ArgumentList, + MemberDescriptor.ThisAdjust); + + TypeIndex MemberFunctionIndex = TypeTable.writeLeafType(MemberFunction); + return MemberFunctionIndex.getIndex(); +} + +unsigned UserDefinedCodeViewTypesBuilder::GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor) +{ + MemberFuncIdRecord MemberFuncId(TypeIndex(MemberIdDescriptor.MemberFunction), TypeIndex(MemberIdDescriptor.ParentClass), MemberIdDescriptor.Name); + TypeIndex MemberFuncIdIndex = TypeTable.writeLeafType(MemberFuncId); + return MemberFuncIdIndex.getIndex(); +} + +unsigned UserDefinedCodeViewTypesBuilder::GetPrimitiveTypeIndex(PrimitiveTypeFlags Type) { + switch (Type) { + case PrimitiveTypeFlags::Void: + return TypeIndex::Void().getIndex(); + case PrimitiveTypeFlags::Boolean: + return TypeIndex(SimpleTypeKind::Boolean8).getIndex(); + case PrimitiveTypeFlags::Char: + return TypeIndex::WideCharacter().getIndex(); + case PrimitiveTypeFlags::SByte: + return TypeIndex(SimpleTypeKind::SByte).getIndex(); + case PrimitiveTypeFlags::Byte: + return TypeIndex(SimpleTypeKind::Byte).getIndex(); + case PrimitiveTypeFlags::Int16: + return TypeIndex(SimpleTypeKind::Int16).getIndex(); + case PrimitiveTypeFlags::UInt16: + return TypeIndex(SimpleTypeKind::UInt16).getIndex(); + case PrimitiveTypeFlags::Int32: + return TypeIndex::Int32().getIndex(); + case PrimitiveTypeFlags::UInt32: + return TypeIndex::UInt32().getIndex(); + case PrimitiveTypeFlags::Int64: + return TypeIndex::Int64().getIndex(); + case PrimitiveTypeFlags::UInt64: + return TypeIndex::UInt64().getIndex(); + case PrimitiveTypeFlags::Single: + return TypeIndex::Float32().getIndex(); + case PrimitiveTypeFlags::Double: + return TypeIndex::Float64().getIndex(); + case PrimitiveTypeFlags::IntPtr: + case PrimitiveTypeFlags::UIntPtr: + return TargetPointerSize == 4 ? TypeIndex::VoidPointer32().getIndex() : + TypeIndex::VoidPointer64().getIndex(); + default: + assert(false && "Unexpected type"); + return 0; + } +} + +void UserDefinedCodeViewTypesBuilder::AddBaseClass(ContinuationRecordBuilder &CRB, + unsigned BaseClassId) { + MemberAttributes def; + TypeIndex BaseTypeIndex(BaseClassId); + BaseClassRecord BCR(def, BaseTypeIndex, 0); + CRB.writeMemberType(BCR); +} + +void UserDefinedCodeViewTypesBuilder::AddClassVTShape(ContinuationRecordBuilder &CRB) { + VFPtrRecord VfPtr(VFuncTabTypeIndex); + CRB.writeMemberType(VfPtr); +} + +const char *ArrayDimensionsDescriptor::GetLengthName(unsigned index) { + if (Lengths.size() <= index) { + Resize(index + 1); + } + return Lengths[index].c_str(); +} + +const char *ArrayDimensionsDescriptor::GetBoundsName(unsigned index) { + if (Bounds.size() <= index) { + Resize(index); + } + return Bounds[index].c_str(); +} + +void ArrayDimensionsDescriptor::Resize(unsigned NewSize) { + unsigned OldSize = Lengths.size(); + assert(OldSize == Bounds.size()); + Lengths.resize(NewSize); + Bounds.resize(NewSize); + for (unsigned i = OldSize; i < NewSize; ++i) { + std::stringstream ss; + ss << "length" << i; + ss >> Lengths[i]; + ss.clear(); + ss << "bounds" << i; + ss >> Bounds[i]; + } +} diff --git a/src/coreclr/tools/aot/ObjWriter/debugInfo/codeView/codeViewTypeBuilder.h b/src/coreclr/tools/aot/ObjWriter/debugInfo/codeView/codeViewTypeBuilder.h new file mode 100644 index 00000000000000..14980fa9963743 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/debugInfo/codeView/codeViewTypeBuilder.h @@ -0,0 +1,74 @@ +//===---- codeViewTypeBuilder.h ---------------------------------*- C++ -*-===// +// +// type builder is used to convert .Net types into CodeView descriptors. +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "debugInfo/typeBuilder.h" +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" + +#include + +using namespace llvm::codeview; + +class ArrayDimensionsDescriptor { +public: + const char *GetLengthName(unsigned index); + const char *GetBoundsName(unsigned index); + +private: + void Resize(unsigned NewSize); + + std::vector Lengths; + std::vector Bounds; +}; + +class UserDefinedCodeViewTypesBuilder : public UserDefinedTypesBuilder { +public: + UserDefinedCodeViewTypesBuilder(); + void EmitTypeInformation(MCSection *TypeSection, MCSection *StrSection = nullptr) override; + + unsigned GetEnumTypeIndex(const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords) override; + unsigned GetClassTypeIndex(const ClassTypeDescriptor &ClassDescriptor) override; + unsigned GetCompleteClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors, + const StaticDataFieldDescriptor *StaticsDescriptors) override; + + unsigned GetArrayTypeIndex(const ClassTypeDescriptor &ClassDescriptor, + const ArrayTypeDescriptor &ArrayDescriptor) override; + + unsigned GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor) override; + + unsigned GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes) override; + + unsigned GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor) override; + + unsigned GetPrimitiveTypeIndex(PrimitiveTypeFlags Type) override; + +private: + void EmitCodeViewMagicVersion(); + ClassOptions GetCommonClassOptions(); + + unsigned GetEnumFieldListType(uint64 Count, + const EnumRecordTypeDescriptor *TypeRecords); + + void AddBaseClass(ContinuationRecordBuilder &CRB, unsigned BaseClassId); + void AddClassVTShape(ContinuationRecordBuilder &CRB); + + BumpPtrAllocator Allocator; + GlobalTypeTableBuilder TypeTable; + + ArrayDimensionsDescriptor ArrayDimentions; + TypeIndex ClassVTableTypeIndex; + TypeIndex VFuncTabTypeIndex; +}; diff --git a/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfAbbrev.cpp b/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfAbbrev.cpp new file mode 100644 index 00000000000000..362d5df76017bd --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfAbbrev.cpp @@ -0,0 +1,292 @@ +//===---- dwarfAbbrev.cpp ---------------------------------------*- C++ -*-===// +// +// dwarf abbreviations implementation +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +//===----------------------------------------------------------------------===// + +#include "dwarfAbbrev.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCObjectFileInfo.h" + +namespace DwarfAbbrev { + +void Dump(MCObjectStreamer *Streamer, uint16_t DwarfVersion, unsigned TargetPointerSize) { + uint16_t DW_FORM_size; + switch (TargetPointerSize) { + case 1: + DW_FORM_size = dwarf::DW_FORM_data1; + break; + case 2: + DW_FORM_size = dwarf::DW_FORM_data2; + break; + case 4: + DW_FORM_size = dwarf::DW_FORM_data4; + break; + case 8: + DW_FORM_size = dwarf::DW_FORM_data8; + break; + default: + assert(false && "Unexpected TargerPointerSize"); + return; + } + + const uint16_t AbbrevTable[] = { + CompileUnit, + dwarf::DW_TAG_compile_unit, dwarf::DW_CHILDREN_yes, + dwarf::DW_AT_producer, dwarf::DW_FORM_string, + dwarf::DW_AT_language, dwarf::DW_FORM_data2, + dwarf::DW_AT_stmt_list, (DwarfVersion >= 4 ? dwarf::DW_FORM_sec_offset : dwarf::DW_FORM_data4), + 0, 0, + + BaseType, + dwarf::DW_TAG_base_type, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_encoding, dwarf::DW_FORM_data1, + dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, + 0, 0, + + EnumerationType, + dwarf::DW_TAG_enumeration_type, dwarf::DW_CHILDREN_yes, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, + 0, 0, + + Enumerator1, + dwarf::DW_TAG_enumerator, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_const_value, dwarf::DW_FORM_data1, + 0, 0, + + Enumerator2, + dwarf::DW_TAG_enumerator, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_const_value, dwarf::DW_FORM_data2, + 0, 0, + + Enumerator4, + dwarf::DW_TAG_enumerator, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_const_value, dwarf::DW_FORM_data4, + 0, 0, + + Enumerator8, + dwarf::DW_TAG_enumerator, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_const_value, dwarf::DW_FORM_data8, + 0, 0, + + TypeDef, + dwarf::DW_TAG_typedef, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + 0, 0, + + Subprogram, + dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_yes, + dwarf::DW_AT_specification, dwarf::DW_FORM_ref4, + dwarf::DW_AT_low_pc, dwarf::DW_FORM_addr, + dwarf::DW_AT_high_pc, DW_FORM_size, + dwarf::DW_AT_frame_base, dwarf::DW_FORM_exprloc, + dwarf::DW_AT_object_pointer, dwarf::DW_FORM_ref4, + 0, 0, + + SubprogramStatic, + dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_yes, + dwarf::DW_AT_specification, dwarf::DW_FORM_ref4, + dwarf::DW_AT_low_pc, dwarf::DW_FORM_addr, + dwarf::DW_AT_high_pc, DW_FORM_size, + dwarf::DW_AT_frame_base, dwarf::DW_FORM_exprloc, + 0, 0, + + SubprogramSpec, + dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_yes, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_linkage_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, + dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_external, dwarf::DW_FORM_flag_present, + dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present, + dwarf::DW_AT_object_pointer, dwarf::DW_FORM_ref4, + 0, 0, + + SubprogramStaticSpec, + dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_yes, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_linkage_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, + dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_external, dwarf::DW_FORM_flag_present, + dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present, + 0, 0, + + Variable, + dwarf::DW_TAG_variable, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, + dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_location, dwarf::DW_FORM_exprloc, + 0, 0, + + VariableLoc, + dwarf::DW_TAG_variable, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, + dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_location, dwarf::DW_FORM_sec_offset, + 0, 0, + + VariableStatic, + dwarf::DW_TAG_variable, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_specification, dwarf::DW_FORM_ref4, + dwarf::DW_AT_location, dwarf::DW_FORM_exprloc, + 0, 0, + + FormalParameter, + dwarf::DW_TAG_formal_parameter, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, + dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_location, dwarf::DW_FORM_exprloc, + 0, 0, + + FormalParameterThis, + dwarf::DW_TAG_formal_parameter, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, + dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_location, dwarf::DW_FORM_exprloc, + dwarf::DW_AT_artificial, dwarf::DW_FORM_flag_present, + 0, 0, + + FormalParameterLoc, + dwarf::DW_TAG_formal_parameter, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, + dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_location, dwarf::DW_FORM_sec_offset, + 0, 0, + + FormalParameterThisLoc, + dwarf::DW_TAG_formal_parameter, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_decl_file, dwarf::DW_FORM_data1, + dwarf::DW_AT_decl_line, dwarf::DW_FORM_data1, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_location, dwarf::DW_FORM_sec_offset, + dwarf::DW_AT_artificial, dwarf::DW_FORM_flag_present, + 0, 0, + + FormalParameterSpec, + dwarf::DW_TAG_formal_parameter, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + 0, 0, + + FormalParameterThisSpec, + dwarf::DW_TAG_formal_parameter, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_artificial, dwarf::DW_FORM_flag_present, + 0, 0, + + ClassType, + dwarf::DW_TAG_class_type, dwarf::DW_CHILDREN_yes, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_byte_size, dwarf::DW_FORM_data4, + 0, 0, + + ClassTypeDecl, + dwarf::DW_TAG_class_type, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present, + 0, 0, + + ClassMember, + dwarf::DW_TAG_member, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data4, + 0, 0, + + ClassMemberStatic, + dwarf::DW_TAG_member, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_external, dwarf::DW_FORM_flag_present, + dwarf::DW_AT_declaration, dwarf::DW_FORM_flag_present, + 0, 0, + + PointerType, + dwarf::DW_TAG_pointer_type, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, + 0, 0, + + ReferenceType, + dwarf::DW_TAG_reference_type, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_byte_size, dwarf::DW_FORM_data1, + 0, 0, + + ArrayType, + dwarf::DW_TAG_array_type, dwarf::DW_CHILDREN_yes, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + 0, 0, + + SubrangeType, + dwarf::DW_TAG_subrange_type, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_upper_bound, dwarf::DW_FORM_udata, + 0, 0, + + ClassInheritance, + dwarf::DW_TAG_inheritance, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_type, dwarf::DW_FORM_ref4, + dwarf::DW_AT_data_member_location, dwarf::DW_FORM_data1, + 0, 0, + + LexicalBlock, + dwarf::DW_TAG_lexical_block, dwarf::DW_CHILDREN_yes, + dwarf::DW_AT_low_pc, dwarf::DW_FORM_addr, + dwarf::DW_AT_high_pc, DW_FORM_size, + 0, 0, + + TryBlock, + dwarf::DW_TAG_try_block, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_low_pc, dwarf::DW_FORM_addr, + dwarf::DW_AT_high_pc, DW_FORM_size, + 0, 0, + + CatchBlock, + dwarf::DW_TAG_catch_block, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_low_pc, dwarf::DW_FORM_addr, + dwarf::DW_AT_high_pc, DW_FORM_size, + 0, 0, + + VoidType, + dwarf::DW_TAG_unspecified_type, dwarf::DW_CHILDREN_no, + dwarf::DW_AT_name, dwarf::DW_FORM_strp, + 0, 0, + + VoidPointerType, + dwarf::DW_TAG_pointer_type, dwarf::DW_CHILDREN_no, + 0, 0, + }; + + MCContext &context = Streamer->getContext(); + Streamer->SwitchSection(context.getObjectFileInfo()->getDwarfAbbrevSection()); + + for (uint16_t e : AbbrevTable) { + Streamer->emitULEB128IntValue(e); + } +} + +} diff --git a/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfAbbrev.h b/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfAbbrev.h new file mode 100644 index 00000000000000..3cd8cea23772cc --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfAbbrev.h @@ -0,0 +1,60 @@ +//===---- dwarfAbbrev.h -----------------------------------------*- C++ -*-===// +// +// dwarf abbreviations +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/MC/MCObjectStreamer.h" + +using namespace llvm; + +namespace DwarfAbbrev { + +enum DwarfAbbrev : uint16_t +{ + CompileUnit = 0x1, + BaseType, + EnumerationType, + Enumerator1, + Enumerator2, + Enumerator4, + Enumerator8, + TypeDef, + Subprogram, + SubprogramStatic, + SubprogramSpec, + SubprogramStaticSpec, + Variable, + VariableLoc, + VariableStatic, + FormalParameter, + FormalParameterThis, + FormalParameterLoc, + FormalParameterThisLoc, + FormalParameterSpec, + FormalParameterThisSpec, + ClassType, + ClassTypeDecl, + ClassMember, + ClassMemberStatic, + PointerType, + ReferenceType, + ArrayType, + SubrangeType, + ClassInheritance, + LexicalBlock, + TryBlock, + CatchBlock, + VoidType, + VoidPointerType, +}; + +void Dump(MCObjectStreamer *Streamer, uint16_t DwarfVersion, unsigned TargetPointerSize); + +} diff --git a/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfGen.cpp b/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfGen.cpp new file mode 100644 index 00000000000000..b2ad3f4bd3d565 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfGen.cpp @@ -0,0 +1,1109 @@ +//===---- dwarfGen.cpp ------------------------------------------*- C++ -*-===// +// +// dwarf generator implementation +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +//===----------------------------------------------------------------------===// + +#include "dwarfGen.h" +#include "dwarfAbbrev.h" + +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/Support/LEB128.h" + +#ifdef FEATURE_LANGID_CS +#define DW_LANG_MICROSOFT_CSHARP 0x9e57 +#endif + +// Keep sync with ICorDebugInfo::RegNum (cordebuginfo.h) + +enum class RegNumX86 +{ + REGNUM_EAX, + REGNUM_ECX, + REGNUM_EDX, + REGNUM_EBX, + REGNUM_ESP, + REGNUM_EBP, + REGNUM_ESI, + REGNUM_EDI, + REGNUM_COUNT, + REGNUM_FP = REGNUM_EBP, + REGNUM_SP = REGNUM_ESP +}; + +enum class RegNumArm +{ + REGNUM_R0, + REGNUM_R1, + REGNUM_R2, + REGNUM_R3, + REGNUM_R4, + REGNUM_R5, + REGNUM_R6, + REGNUM_R7, + REGNUM_R8, + REGNUM_R9, + REGNUM_R10, + REGNUM_R11, + REGNUM_R12, + REGNUM_SP, + REGNUM_LR, + REGNUM_PC, + REGNUM_COUNT, + REGNUM_FP = REGNUM_R7 +}; + +enum class RegNumArm64 +{ + REGNUM_X0, + REGNUM_X1, + REGNUM_X2, + REGNUM_X3, + REGNUM_X4, + REGNUM_X5, + REGNUM_X6, + REGNUM_X7, + REGNUM_X8, + REGNUM_X9, + REGNUM_X10, + REGNUM_X11, + REGNUM_X12, + REGNUM_X13, + REGNUM_X14, + REGNUM_X15, + REGNUM_X16, + REGNUM_X17, + REGNUM_X18, + REGNUM_X19, + REGNUM_X20, + REGNUM_X21, + REGNUM_X22, + REGNUM_X23, + REGNUM_X24, + REGNUM_X25, + REGNUM_X26, + REGNUM_X27, + REGNUM_X28, + REGNUM_FP, + REGNUM_LR, + REGNUM_SP, + REGNUM_PC, + REGNUM_COUNT +}; + +enum class RegNumAmd64 +{ + REGNUM_RAX, + REGNUM_RCX, + REGNUM_RDX, + REGNUM_RBX, + REGNUM_RSP, + REGNUM_RBP, + REGNUM_RSI, + REGNUM_RDI, + REGNUM_R8, + REGNUM_R9, + REGNUM_R10, + REGNUM_R11, + REGNUM_R12, + REGNUM_R13, + REGNUM_R14, + REGNUM_R15, + REGNUM_COUNT, + REGNUM_SP = REGNUM_RSP, + REGNUM_FP = REGNUM_RBP +}; + +// Helper routines from lib/MC/MCDwarf.cpp +static const MCExpr *forceExpAbs(MCStreamer &OS, const MCExpr* Expr) { + MCContext &Context = OS.getContext(); + assert(!isa(Expr)); + if (Context.getAsmInfo()->hasAggressiveSymbolFolding()) + return Expr; + + MCSymbol *ABS = Context.createTempSymbol(); + OS.emitAssignment(ABS, Expr); + return MCSymbolRefExpr::create(ABS, Context); +} + +static void emitAbsValue(MCStreamer &OS, const MCExpr *Value, unsigned Size) { + const MCExpr *ABS = forceExpAbs(OS, Value); + OS.emitValue(ABS, Size); +} + +static const MCExpr *MakeStartMinusEndExpr(const MCStreamer &MCOS, + const MCSymbol &Start, + const MCSymbol &End, + int IntVal) { + MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None; + const MCExpr *Res = + MCSymbolRefExpr::create(&End, Variant, MCOS.getContext()); + const MCExpr *RHS = + MCSymbolRefExpr::create(&Start, Variant, MCOS.getContext()); + const MCExpr *Res1 = + MCBinaryExpr::create(MCBinaryExpr::Sub, Res, RHS, MCOS.getContext()); + const MCExpr *Res2 = + MCConstantExpr::create(IntVal, MCOS.getContext()); + const MCExpr *Res3 = + MCBinaryExpr::create(MCBinaryExpr::Sub, Res1, Res2, MCOS.getContext()); + return Res3; +} + +static int GetDwarfRegNum(Triple::ArchType ArchType, int RegNum) { + switch (ArchType) { + case Triple::x86: + switch (static_cast(RegNum)) { + case RegNumX86::REGNUM_EAX: return 0; + case RegNumX86::REGNUM_ECX: return 1; + case RegNumX86::REGNUM_EDX: return 2; + case RegNumX86::REGNUM_EBX: return 3; + case RegNumX86::REGNUM_ESP: return 4; + case RegNumX86::REGNUM_EBP: return 5; + case RegNumX86::REGNUM_ESI: return 6; + case RegNumX86::REGNUM_EDI: return 7; + // fp registers + default: + return RegNum - static_cast(RegNumX86::REGNUM_COUNT) + 32; + } + case Triple::arm: // fall through + case Triple::armeb: // fall through + case Triple::thumb: // fall through + case Triple::thumbeb: + switch (static_cast(RegNum)) { + case RegNumArm::REGNUM_R0: return 0; + case RegNumArm::REGNUM_R1: return 1; + case RegNumArm::REGNUM_R2: return 2; + case RegNumArm::REGNUM_R3: return 3; + case RegNumArm::REGNUM_R4: return 4; + case RegNumArm::REGNUM_R5: return 5; + case RegNumArm::REGNUM_R6: return 6; + case RegNumArm::REGNUM_R7: return 7; + case RegNumArm::REGNUM_R8: return 8; + case RegNumArm::REGNUM_R9: return 9; + case RegNumArm::REGNUM_R10: return 10; + case RegNumArm::REGNUM_R11: return 11; + case RegNumArm::REGNUM_R12: return 12; + case RegNumArm::REGNUM_SP: return 13; + case RegNumArm::REGNUM_LR: return 14; + case RegNumArm::REGNUM_PC: return 15; + // fp registers + default: + return (RegNum - static_cast(RegNumArm::REGNUM_COUNT)) / 2 + 256; + } + case Triple::aarch64: // fall through + case Triple::aarch64_be: + switch (static_cast(RegNum)) { + case RegNumArm64::REGNUM_X0: return 0; + case RegNumArm64::REGNUM_X1: return 1; + case RegNumArm64::REGNUM_X2: return 2; + case RegNumArm64::REGNUM_X3: return 3; + case RegNumArm64::REGNUM_X4: return 4; + case RegNumArm64::REGNUM_X5: return 5; + case RegNumArm64::REGNUM_X6: return 6; + case RegNumArm64::REGNUM_X7: return 7; + case RegNumArm64::REGNUM_X8: return 8; + case RegNumArm64::REGNUM_X9: return 9; + case RegNumArm64::REGNUM_X10: return 10; + case RegNumArm64::REGNUM_X11: return 11; + case RegNumArm64::REGNUM_X12: return 12; + case RegNumArm64::REGNUM_X13: return 13; + case RegNumArm64::REGNUM_X14: return 14; + case RegNumArm64::REGNUM_X15: return 15; + case RegNumArm64::REGNUM_X16: return 16; + case RegNumArm64::REGNUM_X17: return 17; + case RegNumArm64::REGNUM_X18: return 18; + case RegNumArm64::REGNUM_X19: return 19; + case RegNumArm64::REGNUM_X20: return 20; + case RegNumArm64::REGNUM_X21: return 21; + case RegNumArm64::REGNUM_X22: return 22; + case RegNumArm64::REGNUM_X23: return 23; + case RegNumArm64::REGNUM_X24: return 24; + case RegNumArm64::REGNUM_X25: return 25; + case RegNumArm64::REGNUM_X26: return 26; + case RegNumArm64::REGNUM_X27: return 27; + case RegNumArm64::REGNUM_X28: return 28; + case RegNumArm64::REGNUM_FP: return 29; + case RegNumArm64::REGNUM_LR: return 30; + case RegNumArm64::REGNUM_SP: return 31; + case RegNumArm64::REGNUM_PC: return 32; + // fp registers + default: + return RegNum - static_cast(RegNumArm64::REGNUM_COUNT) + 64; + } + case Triple::x86_64: + switch (static_cast(RegNum)) { + case RegNumAmd64::REGNUM_RAX: return 0; + case RegNumAmd64::REGNUM_RDX: return 1; + case RegNumAmd64::REGNUM_RCX: return 2; + case RegNumAmd64::REGNUM_RBX: return 3; + case RegNumAmd64::REGNUM_RSI: return 4; + case RegNumAmd64::REGNUM_RDI: return 5; + case RegNumAmd64::REGNUM_RBP: return 6; + case RegNumAmd64::REGNUM_RSP: return 7; + case RegNumAmd64::REGNUM_R8: return 8; + case RegNumAmd64::REGNUM_R9: return 9; + case RegNumAmd64::REGNUM_R10: return 10; + case RegNumAmd64::REGNUM_R11: return 11; + case RegNumAmd64::REGNUM_R12: return 12; + case RegNumAmd64::REGNUM_R13: return 13; + case RegNumAmd64::REGNUM_R14: return 14; + case RegNumAmd64::REGNUM_R15: return 15; + // fp registers + default: + return RegNum - static_cast(RegNumAmd64::REGNUM_COUNT) + 17; + } + default: + assert(false && "Unexpected architecture"); + return 0; + } +} + +static int GetDwarfFpRegNum(Triple::ArchType ArchType) +{ + switch (ArchType) { + case Triple::x86: + return GetDwarfRegNum(ArchType, static_cast(RegNumX86::REGNUM_FP)); + case Triple::arm: // fall through + case Triple::armeb: // fall through + case Triple::thumb: // fall through + case Triple::thumbeb: + return GetDwarfRegNum(ArchType, static_cast(RegNumArm::REGNUM_FP)); + case Triple::aarch64: // fall through + case Triple::aarch64_be: + return GetDwarfRegNum(ArchType, static_cast(RegNumArm64::REGNUM_FP)); + case Triple::x86_64: + return GetDwarfRegNum(ArchType, static_cast(RegNumAmd64::REGNUM_FP)); + default: + assert(false && "Unexpected architecture"); + return 0; + } +} + +static int GetRegOpSize(int DwarfRegNum) { + if (DwarfRegNum <= 31) { + return 1; + } + else if (DwarfRegNum < 128) { + return 2; + } + else if (DwarfRegNum < 16384) { + return 3; + } + else { + assert(false && "Too big register number"); + return 0; + } +} + +static void EmitBreg(MCObjectStreamer* Streamer, int DwarfRegNum, StringRef bytes) { + if (DwarfRegNum <= 31) { + Streamer->emitIntValue(DwarfRegNum + dwarf::DW_OP_breg0, 1); + } + else { + Streamer->emitIntValue(dwarf::DW_OP_bregx, 1); + Streamer->emitULEB128IntValue(DwarfRegNum); + } + Streamer->emitBytes(bytes); +} + +static void EmitBreg(MCObjectStreamer* Streamer, int DwarfRegNum, int value) { + if (DwarfRegNum <= 31) { + Streamer->emitIntValue(DwarfRegNum + dwarf::DW_OP_breg0, 1); + } + else { + Streamer->emitIntValue(dwarf::DW_OP_bregx, 1); + Streamer->emitULEB128IntValue(DwarfRegNum); + } + Streamer->emitSLEB128IntValue(value); +} + +static void EmitReg(MCObjectStreamer* Streamer, int DwarfRegNum) { + if (DwarfRegNum <= 31) { + Streamer->emitIntValue(DwarfRegNum + dwarf::DW_OP_reg0, 1); + } + else { + Streamer->emitIntValue(dwarf::DW_OP_regx, 1); + Streamer->emitULEB128IntValue(DwarfRegNum); + } +} + +static void EmitVarLocation(MCObjectStreamer *Streamer, + const ICorDebugInfo::NativeVarInfo &VarInfo, + bool IsLocList = false) { + MCContext &context = Streamer->getContext(); + unsigned TargetPointerSize = context.getAsmInfo()->getCodePointerSize(); + Triple::ArchType ArchType = context.getObjectFileInfo()->getTargetTriple().getArch(); + + int DwarfRegNum; + int DwarfRegNum2; + int DwarfBaseRegNum; + unsigned Len; + + bool IsByRef = false; + bool IsStk2 = false; + bool IsRegStk = false; + + switch (VarInfo.loc.vlType) { + case ICorDebugInfo::VLT_REG_BYREF: // fall through + IsByRef = true; + case ICorDebugInfo::VLT_REG_FP: // fall through + case ICorDebugInfo::VLT_REG: { + DwarfRegNum = GetDwarfRegNum(ArchType, VarInfo.loc.vlReg.vlrReg); + if (IsByRef) { + Len = 1 + GetRegOpSize(DwarfRegNum); + if (IsLocList) { + Streamer->emitIntValue(Len, 2); + } else { + Streamer->emitULEB128IntValue(Len); + } + EmitBreg(Streamer, DwarfRegNum, 0); + } else { + Len = GetRegOpSize(DwarfRegNum); + if (IsLocList) { + Streamer->emitIntValue(Len, 2); + } else { + Streamer->emitULEB128IntValue(Len); + } + EmitReg(Streamer, DwarfRegNum); + } + + break; + } + case ICorDebugInfo::VLT_STK_BYREF: // fall through + IsByRef = true; + case ICorDebugInfo::VLT_STK2: + IsStk2 = true; + case ICorDebugInfo::VLT_FPSTK: + case ICorDebugInfo::VLT_STK: { + DwarfBaseRegNum = GetDwarfRegNum(ArchType, IsStk2 ? VarInfo.loc.vlStk2.vls2BaseReg : + VarInfo.loc.vlStk.vlsBaseReg); + + SmallString<128> Tmp; + raw_svector_ostream OSE(Tmp); + encodeSLEB128(IsStk2 ? VarInfo.loc.vlStk2.vls2Offset : + VarInfo.loc.vlStk.vlsOffset, OSE); + StringRef OffsetRepr = OSE.str(); + + if (IsByRef) { + Len = OffsetRepr.size() + 1 + GetRegOpSize(DwarfBaseRegNum); + if (IsLocList) { + Streamer->emitIntValue(Len, 2); + } else { + Streamer->emitULEB128IntValue(Len); + } + EmitBreg(Streamer, DwarfBaseRegNum, OffsetRepr); + Streamer->emitIntValue(dwarf::DW_OP_deref, 1); + } else { + Len = OffsetRepr.size() + GetRegOpSize(DwarfBaseRegNum); + if (IsLocList) { + Streamer->emitIntValue(Len, 2); + } else { + Streamer->emitULEB128IntValue(Len); + } + EmitBreg(Streamer, DwarfBaseRegNum, OffsetRepr); + } + + break; + } + case ICorDebugInfo::VLT_REG_REG: { + DwarfRegNum = GetDwarfRegNum(ArchType, VarInfo.loc.vlRegReg.vlrrReg1); + DwarfRegNum2 = GetDwarfRegNum(ArchType, VarInfo.loc.vlRegReg.vlrrReg2); + + Len = (GetRegOpSize(DwarfRegNum2) /* DW_OP_reg */ + 1 /* DW_OP_piece */ + 1 /* Reg size */) + + (GetRegOpSize(DwarfRegNum) /* DW_OP_reg */ + 1 /* DW_OP_piece */ + 1 /* Reg size */); + if (IsLocList) { + Streamer->emitIntValue(Len, 2); + } else { + Streamer->emitULEB128IntValue(Len + 1); + } + + EmitReg(Streamer, DwarfRegNum2); + Streamer->emitIntValue(dwarf::DW_OP_piece, 1); + Streamer->emitULEB128IntValue(TargetPointerSize); + + EmitReg(Streamer, DwarfRegNum); + Streamer->emitIntValue(dwarf::DW_OP_piece, 1); + Streamer->emitULEB128IntValue(TargetPointerSize); + + break; + } + case ICorDebugInfo::VLT_REG_STK: // fall through + IsRegStk = true; + case ICorDebugInfo::VLT_STK_REG: { + DwarfRegNum = GetDwarfRegNum(ArchType, IsRegStk ? VarInfo.loc.vlRegStk.vlrsReg : + VarInfo.loc.vlStkReg.vlsrReg); + DwarfBaseRegNum = GetDwarfRegNum(ArchType, IsRegStk ? VarInfo.loc.vlRegStk.vlrsStk.vlrssBaseReg : + VarInfo.loc.vlStkReg.vlsrStk.vlsrsBaseReg); + + SmallString<128> Tmp; + raw_svector_ostream OSE(Tmp); + encodeSLEB128(IsRegStk ? VarInfo.loc.vlRegStk.vlrsStk.vlrssOffset : + VarInfo.loc.vlStkReg.vlsrStk.vlsrsOffset, OSE); + StringRef OffsetRepr = OSE.str(); + + Len = (GetRegOpSize(DwarfRegNum) /* DW_OP_reg */ + 1 /* DW_OP_piece */ + 1 /* Reg size */) + + (GetRegOpSize(DwarfBaseRegNum) /*DW_OP_breg */ + OffsetRepr.size() + 1 /* DW_OP_piece */ + 1 /* Reg size */); + + if (IsLocList) { + Streamer->emitIntValue(Len, 2); + } else { + Streamer->emitULEB128IntValue(Len + 1); + } + + if (IsRegStk) { + EmitReg(Streamer, DwarfRegNum); + Streamer->emitIntValue(dwarf::DW_OP_piece, 1); + Streamer->emitULEB128IntValue(TargetPointerSize); + + EmitBreg(Streamer, DwarfBaseRegNum, OffsetRepr); + Streamer->emitIntValue(dwarf::DW_OP_piece, 1); + Streamer->emitULEB128IntValue(TargetPointerSize); + } else { + EmitBreg(Streamer, DwarfBaseRegNum, OffsetRepr); + Streamer->emitIntValue(dwarf::DW_OP_piece, 1); + Streamer->emitULEB128IntValue(TargetPointerSize); + + EmitReg(Streamer, DwarfRegNum); + Streamer->emitIntValue(dwarf::DW_OP_piece, 1); + Streamer->emitULEB128IntValue(TargetPointerSize); + } + + break; + } + case ICorDebugInfo::VLT_FIXED_VA: + assert(false && "Unsupported varloc type!"); + default: + assert(false && "Unknown varloc type!"); + if (IsLocList) { + Streamer->emitIntValue(0, 2); + } else { + Streamer->emitULEB128IntValue(0); + } + } +} + +// Lexical scope + +class LexicalScope +{ +public: + LexicalScope(uint64_t Start, uint64_t End, bool IsFuncScope = false) : + Start(Start), + End(End), + IsFuncScope(IsFuncScope) {} + + LexicalScope(VarInfo *Info) : + Start(Info->GetStartOffset()), + End(Info->GetEndOffset()), + IsFuncScope(false) { Vars.push_back(Info); } + + bool IsContains(const VarInfo *Info) const { + return Start <= Info->GetStartOffset() && End >= Info->GetEndOffset(); + } + + void AddVar(VarInfo *Info); + + void Dump(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection, const MCExpr *SymExpr); + +private: + uint64_t Start; + uint64_t End; + bool IsFuncScope; + std::vector Vars; + std::vector InnerScopes; +}; + +void LexicalScope::AddVar(VarInfo *Info) { + if (Info->IsParam() && IsFuncScope) { + Vars.push_back(Info); + return; + } + + if (!IsContains(Info)) + return; + + uint64_t VarStart = Info->GetStartOffset(); + uint64_t VarEnd = Info->GetEndOffset(); + + // Var belongs to inner scope + if (VarStart != Start || VarEnd != End) { + // Try to add variable to one the inner scopes + for (auto &Scope : InnerScopes) { + if (Scope.IsContains(Info)) { + Scope.AddVar(Info); + return; + } + } + // We need to create new inner scope for this var + InnerScopes.emplace_back(Info); + } else { + Vars.push_back(Info); + } +} + +void LexicalScope::Dump(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection, const MCExpr *SymExpr) { + Streamer->SwitchSection(TypeSection); + + if (!IsFuncScope) + { + // Dump lexical block DIE + MCContext &context = Streamer->getContext(); + unsigned TargetPointerSize = context.getAsmInfo()->getCodePointerSize(); + + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::LexicalBlock); + + // DW_AT_low_pc + const MCExpr *StartExpr = MCConstantExpr::create(Start, context); + const MCExpr *LowPcExpr = MCBinaryExpr::create(MCBinaryExpr::Add, SymExpr, + StartExpr, context); + Streamer->emitValue(LowPcExpr, TargetPointerSize); + + // DW_AT_high_pc + Streamer->emitIntValue(End - Start, TargetPointerSize); + } + + for (auto *Var : Vars) { + Var->Dump(TypeBuilder, Streamer, TypeSection, StrSection); + } + + for (auto &Scope : InnerScopes) { + Scope.Dump(TypeBuilder, Streamer, TypeSection, StrSection, SymExpr); + } + + if (!IsFuncScope) { + // Terminate block + Streamer->emitIntValue(0, 1); + } +} + +// StaticVarInfo + +class StaticVarInfo : public DwarfInfo +{ +public: + StaticVarInfo(const DwarfStaticDataField *StaticField) : StaticField(StaticField) {} + + void Dump(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) override; + +protected: + void DumpStrings(MCObjectStreamer *Streamer) override {}; + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + +private: + const DwarfStaticDataField *StaticField; + + void EmitLocation(MCObjectStreamer *Streamer); +}; + +void StaticVarInfo::Dump(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) { + MCContext &context = Streamer->getContext(); + MCSymbol *Sym = context.getOrCreateSymbol(Twine(StaticField->GetStaticDataName())); + if (Sym->isUndefined()) + return; + + DwarfInfo::Dump(TypeBuilder, Streamer, TypeSection, StrSection); +} + +void StaticVarInfo::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::VariableStatic); + + // DW_AT_specification + EmitInfoOffset(Streamer, StaticField, 4); + + // DW_AT_location + EmitLocation(Streamer); +} + +void StaticVarInfo::EmitLocation(MCObjectStreamer *Streamer) { + MCContext &context = Streamer->getContext(); + unsigned TargetPointerSize = context.getAsmInfo()->getCodePointerSize(); + + MCSymbol *Sym = context.getOrCreateSymbol(Twine(StaticField->GetStaticDataName())); + + SmallString<128> Tmp; + raw_svector_ostream OSE(Tmp); + encodeULEB128(StaticField->GetStaticOffset(), OSE); + StringRef OffsetRepr = OSE.str(); + + unsigned Len = 1 /* DW_OP_addr */ + TargetPointerSize; + + // Need double deref + if (StaticField->IsStaticDataInObject()) { + Len += (1 /* DW_OP_deref */) * 2; + } + + if (StaticField->GetStaticOffset() != 0) { + Len += 1 /* DW_OP_plus_uconst */ + OffsetRepr.size(); + } + + Streamer->emitULEB128IntValue(Len); + Streamer->emitIntValue(dwarf::DW_OP_addr, 1); + Streamer->emitSymbolValue(Sym, TargetPointerSize); + + if (StaticField->IsStaticDataInObject()) { + Streamer->emitIntValue(dwarf::DW_OP_deref, 1); + Streamer->emitIntValue(dwarf::DW_OP_deref, 1); + } + + if (StaticField->GetStaticOffset() != 0) { + Streamer->emitIntValue(dwarf::DW_OP_plus_uconst, 1); + Streamer->emitBytes(OffsetRepr); + } +} + +// VarInfo + +VarInfo::VarInfo(const DebugVarInfo &Info, bool IsThis) : + DebugInfo(Info), + LocSymbol(nullptr), + IsThis(IsThis) { + if (!Info.IsParam) { + assert(!Info.Ranges.empty()); + StartOffset = Info.Ranges.front().startOffset; + EndOffset = Info.Ranges.back().endOffset; + } else { + // Params belong to func scope + StartOffset = 0xFFFFFFFF; + EndOffset = 0xFFFFFFFF; + } +} + +void VarInfo::DumpLocsIfNeeded(MCObjectStreamer *Streamer, + MCSection *LocSection, + const MCExpr *SymExpr) { + if (!IsDebugLocNeeded()) + return; + + Streamer->SwitchSection(LocSection); + + MCContext &context = Streamer->getContext(); + unsigned TargetPointerSize = context.getAsmInfo()->getCodePointerSize(); + + LocSymbol = context.createTempSymbol(); + Streamer->emitLabel(LocSymbol); + + for (const auto &NativeInfo : DebugInfo.Ranges) { + const MCExpr *StartOffsetExpr = MCConstantExpr::create(NativeInfo.startOffset, context); + const MCExpr *EndOffsetExpr = MCConstantExpr::create(NativeInfo.endOffset, context); + + // Begin address + const MCExpr *BeginAddrExpr = MCBinaryExpr::create(MCBinaryExpr::Add, SymExpr, + StartOffsetExpr, context); + Streamer->emitValue(BeginAddrExpr, TargetPointerSize); + + // End address + const MCExpr *EndAddrExpr = MCBinaryExpr::create(MCBinaryExpr::Add, SymExpr, + EndOffsetExpr, context); + Streamer->emitValue(EndAddrExpr, TargetPointerSize); + + // Expression + EmitVarLocation(Streamer, NativeInfo, true); + } + + // Terminate list entry + Streamer->emitIntValue(0, TargetPointerSize); + Streamer->emitIntValue(0, TargetPointerSize); +} + +void VarInfo::DumpStrings(MCObjectStreamer *Streamer) { + if (IsThis) { + Streamer->emitBytes(StringRef("this")); + } else { + Streamer->emitBytes(StringRef(DebugInfo.Name)); + } + Streamer->emitIntValue(0, 1); +} + +void VarInfo::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + bool IsDebugLocUsed = IsDebugLocNeeded(); + + // Abbrev Number + if (DebugInfo.IsParam) { + if (IsThis) { + Streamer->emitULEB128IntValue(IsDebugLocUsed ? DwarfAbbrev::FormalParameterThisLoc : + DwarfAbbrev::FormalParameterThis); + } else { + Streamer->emitULEB128IntValue(IsDebugLocUsed ? DwarfAbbrev::FormalParameterLoc : + DwarfAbbrev::FormalParameter); + } + } else { + Streamer->emitULEB128IntValue(IsDebugLocUsed ? DwarfAbbrev::VariableLoc : + DwarfAbbrev::Variable); + } + + // DW_AT_name + EmitSectionOffset(Streamer, StrSymbol, 4); + + // DW_AT_decl_file + Streamer->emitIntValue(1, 1); + + // DW_AT_decl_line + Streamer->emitIntValue(1, 1); + + // DW_AT_type + DwarfInfo *Info = TypeBuilder->GetTypeInfoByIndex(DebugInfo.TypeIndex); + assert(Info != nullptr); + + EmitInfoOffset(Streamer, Info, 4); + + // DW_AT_location + if (IsDebugLocUsed) { + EmitSectionOffset(Streamer, LocSymbol, 4); + } else { + assert(DebugInfo.Ranges.size() == 1); + EmitVarLocation(Streamer, DebugInfo.Ranges[0]); + } +} + +// SubprogramInfo + +SubprogramInfo::SubprogramInfo(const char *Name, + int Size, + DwarfMemberFunctionIdTypeInfo *MethodTypeInfo, + const std::vector &DebugVarInfos, + const std::vector &DebugEHClauseInfos) : + Name(Name), + Size(Size), + MethodTypeInfo(MethodTypeInfo), + DebugEHClauseInfos(DebugEHClauseInfos) { + bool IsStatic = MethodTypeInfo->IsStatic(); + for (unsigned i = 0; i < DebugVarInfos.size(); i++) { + VarInfos.emplace_back(DebugVarInfos[i], i == 0 && !IsStatic); + } +} + +void SubprogramInfo::Dump(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection, MCSection *LocSection) { + DumpDebugLoc(Streamer, LocSection); + + DwarfInfo::Dump(TypeBuilder, Streamer, TypeSection, StrSection); + + // Dump vars + DumpVars(TypeBuilder, Streamer, TypeSection, StrSection); + + // Dump try-catch blocks + Streamer->SwitchSection(TypeSection); + DumpEHClauses(Streamer, TypeSection); + + // Terminate subprogram DIE + Streamer->emitIntValue(0, 1); +} + +void SubprogramInfo::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + MCContext &context = Streamer->getContext(); + bool IsStatic = MethodTypeInfo->IsStatic(); + unsigned TargetPointerSize = context.getAsmInfo()->getCodePointerSize(); + Triple::ArchType ArchType = context.getObjectFileInfo()->getTargetTriple().getArch(); + + // Subprogram DIE + + // Abbrev Number + Streamer->emitULEB128IntValue(IsStatic ? DwarfAbbrev::SubprogramStatic : + DwarfAbbrev::Subprogram); + + // DW_AT_specification + EmitInfoOffset(Streamer, MethodTypeInfo, 4); + + // DW_AT_low_pc + MCSymbol *Sym = context.getOrCreateSymbol(Twine(Name)); + const MCExpr *SymExpr = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, context); + Streamer->emitValue(SymExpr, TargetPointerSize); + + // DW_AT_high_pc + Streamer->emitIntValue(Size, TargetPointerSize); + + // DW_AT_frame_base + Streamer->emitULEB128IntValue(1); + Streamer->emitIntValue(GetDwarfFpRegNum(ArchType) + dwarf::DW_OP_reg0, 1); + + if (!IsStatic) { + // DW_AT_object_pointer + uint32_t Offset = Streamer->getOrCreateDataFragment()->getContents().size(); + + Streamer->emitIntValue(Offset + 4, 4); + } +} + +void SubprogramInfo::DumpDebugLoc(MCObjectStreamer *Streamer, MCSection *LocSection) { + MCContext &context = Streamer->getContext(); + MCSymbol *Sym = context.getOrCreateSymbol(Twine(Name)); + const MCExpr *SymExpr = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, context); + + for (auto &VarInfo : VarInfos) { + VarInfo.DumpLocsIfNeeded(Streamer, LocSection, SymExpr); + } +} + +void SubprogramInfo::DumpVars(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) { + MCContext &context = Streamer->getContext(); + MCSymbol *Sym = context.getOrCreateSymbol(Twine(Name)); + const MCExpr *SymExpr = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, context); + + LexicalScope FuncScope(0, Size, true); + + for (unsigned i = 0; i < VarInfos.size(); i++) { + FuncScope.AddVar(&VarInfos[i]); + } + + FuncScope.Dump(TypeBuilder, Streamer, TypeSection, StrSection, SymExpr); +} + +static void DumpEHClause(MCObjectStreamer *Streamer, MCSection *TypeSection, int Abbrev, + const MCExpr *SymExpr, unsigned Offset, unsigned Length) { + MCContext &context = Streamer->getContext(); + unsigned TargetPointerSize = context.getAsmInfo()->getCodePointerSize(); + + // Abbrev Number + Streamer->emitULEB128IntValue(Abbrev); + + // DW_AT_low_pc + const MCExpr *OffsetExpr = MCConstantExpr::create(Offset, context); + const MCExpr *AddrExpr = MCBinaryExpr::create(MCBinaryExpr::Add, SymExpr, + OffsetExpr, context); + + Streamer->emitValue(AddrExpr, TargetPointerSize); + + // DW_AT_high_pc + Streamer->emitIntValue(Length, TargetPointerSize); +} + +void SubprogramInfo::DumpEHClauses(MCObjectStreamer *Streamer, MCSection *TypeSection) { + MCContext &context = Streamer->getContext(); + + MCSymbol *Sym = context.getOrCreateSymbol(Twine(Name)); + const MCExpr *SymExpr = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, context); + + for (const auto &EHClause: DebugEHClauseInfos) { + // Try block DIE + DumpEHClause(Streamer, TypeSection, DwarfAbbrev::TryBlock, + SymExpr, EHClause.TryOffset, EHClause.TryLength); + + // Catch block DIE + DumpEHClause(Streamer, TypeSection, DwarfAbbrev::CatchBlock, + SymExpr, EHClause.HandlerOffset, EHClause.HandlerLength); + } +} + +// DwarfGen + +void DwarfGen::SetTypeBuilder(UserDefinedDwarfTypesBuilder *TypeBuilder) { + assert(this->TypeBuilder == nullptr); + assert(TypeBuilder != nullptr); + this->TypeBuilder = TypeBuilder; + this->Streamer = TypeBuilder->GetStreamer(); +} + +void DwarfGen::EmitCompileUnit() { + MCContext &context = Streamer->getContext(); + + MCSymbol *LineSectionSymbol = nullptr; + MCSymbol *AbbrevSectionSymbol = nullptr; + if (context.getAsmInfo()->doesDwarfUseRelocationsAcrossSections()) { + LineSectionSymbol = Streamer->getDwarfLineTableSymbol(0); + + Streamer->SwitchSection(context.getObjectFileInfo()->getDwarfAbbrevSection()); + AbbrevSectionSymbol = context.createTempSymbol(); + Streamer->emitLabel(AbbrevSectionSymbol); + } + + MCSection *debugSection = context.getObjectFileInfo()->getDwarfInfoSection(); + Streamer->SwitchSection(debugSection); + + InfoStart = debugSection->getBeginSymbol(); + InfoEnd = context.createTempSymbol(); + + // Length + const MCExpr *Length = MakeStartMinusEndExpr(*Streamer, *InfoStart, *InfoEnd, 4); + emitAbsValue(*Streamer, Length, 4); + + // Version + Streamer->emitIntValue(context.getDwarfVersion(), 2); + + // Unit type, Addr Size and Abbrev offset - DWARF >= 5 + // Abbrev offset, Addr Size - DWARF <= 4 + unsigned addrSize = context.getAsmInfo()->getCodePointerSize(); + if (context.getDwarfVersion() >= 5) { + Streamer->emitIntValue(dwarf::DW_UT_compile, 1); + Streamer->emitIntValue(addrSize, 1); + } + + // Abbrev Offset + if (AbbrevSectionSymbol == nullptr) { + Streamer->emitIntValue(0, 4); + } else { + Streamer->emitSymbolValue(AbbrevSectionSymbol, 4, + context.getAsmInfo()->needsDwarfSectionOffsetDirective()); + } + + if (context.getDwarfVersion() <= 4) + Streamer->emitIntValue(addrSize, 1); + + // CompileUnit DIE + + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::CompileUnit); + + // DW_AT_producer: CoreRT + Streamer->emitBytes(StringRef("CoreRT")); + Streamer->emitIntValue(0, 1); + + // DW_AT_language +#ifdef FEATURE_LANGID_CS + Streamer->EmitIntValue(DW_LANG_MICROSOFT_CSHARP, 2); +#else + Streamer->emitIntValue(dwarf::DW_LANG_C_plus_plus, 2); +#endif + + // DW_AT_stmt_list + if (LineSectionSymbol == nullptr) { + Streamer->emitIntValue(0, 4); + } else { + Streamer->emitSymbolValue(LineSectionSymbol, 4, + context.getAsmInfo()->needsDwarfSectionOffsetDirective()); + } +} + +void DwarfGen::EmitSubprogramInfo(const char *FunctionName, + int FunctionSize, + unsigned MethodTypeIndex, + const std::vector &VarInfos, + const std::vector &DebugEHClauseInfos) { + // return if CU isn't emitted + if (InfoStart == nullptr) + return; + + if (MethodTypeIndex == 0) + return; + + DwarfMemberFunctionIdTypeInfo *MethodTypeInfo = static_cast( + TypeBuilder->GetTypeInfoByIndex(MethodTypeIndex)); + assert(MethodTypeInfo != nullptr); + + MethodTypeInfo->SetLinkageName(FunctionName); + + Subprograms.emplace_back(FunctionName, FunctionSize, MethodTypeInfo, VarInfos, DebugEHClauseInfos); +} + +void DwarfGen::EmitAbbrev() { + // return if CU isn't emitted + if (InfoStart == nullptr) + return; + + MCContext &context = Streamer->getContext(); + + DwarfAbbrev::Dump(Streamer, context.getDwarfVersion(), + context.getAsmInfo()->getCodePointerSize()); +} + +void DwarfGen::EmitAranges() { + // return if CU isn't emitted + if (InfoStart == nullptr) + return; + + MCContext &context = Streamer->getContext(); + Streamer->SwitchSection(context.getObjectFileInfo()->getDwarfARangesSection()); + + auto &Sections = context.getGenDwarfSectionSyms(); + + int Length = 4 + 2 + 4 + 1 + 1; + int AddrSize = context.getAsmInfo()->getCodePointerSize(); + int Pad = 2 * AddrSize - (Length & (2 * AddrSize - 1)); + if (Pad == 2 * AddrSize) + Pad = 0; + Length += Pad; + + Length += 2 * AddrSize * Sections.size(); + Length += 2 * AddrSize; + + // Emit the header for this section. + // The 4 byte length not including the 4 byte value for the length. + Streamer->emitIntValue(Length - 4, 4); + + // The 2 byte version, which is 2. + Streamer->emitIntValue(2, 2); + + // The 4 byte offset to the compile unit in the .debug_info from the start + // of the .debug_info. + Streamer->emitSymbolValue(InfoStart, 4, + context.getAsmInfo()->needsDwarfSectionOffsetDirective()); + + Streamer->emitIntValue(AddrSize, 1); + + Streamer->emitIntValue(0, 1); + + for(int i = 0; i < Pad; i++) + Streamer->emitIntValue(0, 1); + + for (MCSection *Sec : Sections) { + const MCSymbol *StartSymbol = Sec->getBeginSymbol(); + MCSymbol *EndSymbol = Sec->getEndSymbol(context); + assert(StartSymbol && "StartSymbol must not be NULL"); + assert(EndSymbol && "EndSymbol must not be NULL"); + + const MCExpr *Addr = MCSymbolRefExpr::create( + StartSymbol, MCSymbolRefExpr::VK_None, context); + const MCExpr *Size = MakeStartMinusEndExpr(*Streamer, + *StartSymbol, *EndSymbol, 0); + Streamer->emitValue(Addr, AddrSize); + emitAbsValue(*Streamer, Size, AddrSize); + } + + // Terminating zeros. + Streamer->emitIntValue(0, AddrSize); + Streamer->emitIntValue(0, AddrSize); +} + +void DwarfGen::Finish() { + // return if CU isn't emitted + if (InfoStart == nullptr) + return; + + MCContext &context = Streamer->getContext(); + + // Dump type info + + MCSection *InfoSection = context.getObjectFileInfo()->getDwarfInfoSection(); + MCSection *StrSection = context.getObjectFileInfo()->getDwarfStrSection(); + MCSection *LocSection = context.getObjectFileInfo()->getDwarfLocSection(); + + TypeBuilder->EmitTypeInformation(InfoSection, StrSection); + + // Dump subprograms + + for (auto &Subprogram : Subprograms) { + Subprogram.Dump(TypeBuilder, Streamer, InfoSection, StrSection, LocSection); + } + + // Dump static vars + + for (const auto *ClassTypeInfo : TypeBuilder->GetClassesWithStaticFields()) { + for (const auto &StaticField : ClassTypeInfo->GetStaticFields()) { + StaticVarInfo Info(&StaticField); + Info.Dump(TypeBuilder, Streamer, InfoSection, StrSection); + } + } + + // Add the NULL terminating the Compile Unit DIE's. + Streamer->SwitchSection(context.getObjectFileInfo()->getDwarfInfoSection()); + + Streamer->emitIntValue(0, 1); + + Streamer->emitLabel(InfoEnd); + + Streamer->SwitchSection(context.getObjectFileInfo()->getDwarfAbbrevSection()); + + // Terminate the abbreviations for this compilation unit + Streamer->emitIntValue(0, 1); +} diff --git a/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfGen.h b/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfGen.h new file mode 100644 index 00000000000000..40f3d28f5fd44e --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfGen.h @@ -0,0 +1,105 @@ +//===---- dwarfGen.h --------------------------------------------*- C++ -*-===// +// +// dwarf generator is used to generate dwarf debuginfo. +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "llvm/MC/MCObjectStreamer.h" + +#include "dwarfTypeBuilder.h" +#include "jitDebugInfo.h" + +#include + +class VarInfo : public DwarfInfo +{ +public: + VarInfo(const DebugVarInfo &Info, bool IsThis); + + bool IsDebugLocNeeded() const { return DebugInfo.Ranges.size() > 1; } + + void DumpLocsIfNeeded(MCObjectStreamer *Streamer, MCSection *LocSection, const MCExpr *SymExpr); + + uint64_t GetStartOffset() const { return StartOffset; } + + uint64_t GetEndOffset() const { return EndOffset; } + + bool IsParam() const { return DebugInfo.IsParam; } + +protected: + void DumpStrings(MCObjectStreamer *Streamer) override; + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + +private: + DebugVarInfo DebugInfo; + MCSymbol *LocSymbol; + bool IsThis; + uint64_t StartOffset; + uint64_t EndOffset; +}; + +class SubprogramInfo : public DwarfInfo +{ +public: + using DwarfInfo::Dump; + + SubprogramInfo(const char *Name, + int Size, + DwarfMemberFunctionIdTypeInfo *MethodTypeInfo, + const std::vector &DebugVarInfos, + const std::vector &DebugEHClauseInfos); + + void Dump(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection, MCSection *LocSection); + +protected: + void DumpStrings(MCObjectStreamer *Streamer) override {} + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + +private: + void DumpDebugLoc(MCObjectStreamer *Streamer, MCSection *LocSection); + void DumpVars(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection); + void DumpEHClauses(MCObjectStreamer *Streamer, MCSection *TypeSection); + + std::string Name; + int Size; + DwarfMemberFunctionIdTypeInfo *MethodTypeInfo; + std::vector DebugEHClauseInfos; + std::vector VarInfos; +}; + +class DwarfGen +{ +public: + DwarfGen() : Streamer(nullptr), + TypeBuilder(nullptr), + InfoStart(nullptr), + InfoEnd(nullptr) {} + + void SetTypeBuilder(UserDefinedDwarfTypesBuilder *TypeBuilder); + void EmitCompileUnit(); + void EmitSubprogramInfo(const char *FunctionName, + int FunctionSize, + unsigned MethodTypeIndex, + const std::vector &VarsInfo, + const std::vector &DebugEHClauseInfos); + + void EmitAbbrev(); + void EmitAranges(); + void Finish(); + +private: + MCObjectStreamer *Streamer; + UserDefinedDwarfTypesBuilder *TypeBuilder; + + MCSymbol *InfoStart; + MCSymbol *InfoEnd; + + std::vector Subprograms; +}; diff --git a/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfTypeBuilder.cpp b/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfTypeBuilder.cpp new file mode 100644 index 00000000000000..6d663e05128844 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfTypeBuilder.cpp @@ -0,0 +1,823 @@ +//===---- dwarfTypeBuilder.cpp ----------------------------------*- C++ -*-===// +// +// dwarf type builder implementation +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +//===----------------------------------------------------------------------===// + +#include "dwarfTypeBuilder.h" +#include "dwarfAbbrev.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" + +#include +#include + +// DwarfInfo + +void DwarfInfo::Dump(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) { + if (IsDumped) + return; + + IsDumped = true; + + MCContext &context = Streamer->getContext(); + + InfoSymbol = context.createTempSymbol(); + InfoExpr = CreateOffsetExpr(context, TypeSection->getBeginSymbol(), InfoSymbol); + + DumpTypes(TypeBuilder, Streamer, TypeSection, StrSection); + + Streamer->SwitchSection(StrSection); + StrSymbol = context.createTempSymbol(); + Streamer->emitLabel(StrSymbol); + DumpStrings(Streamer); + + Streamer->SwitchSection(TypeSection); + Streamer->emitLabel(InfoSymbol); + DumpTypeInfo(Streamer, TypeBuilder); +} + +void DwarfInfo::DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) { + if (IsDumpedTypes) + return; + + IsDumpedTypes = true; +} + +void DwarfInfo::EmitSectionOffset(MCObjectStreamer *Streamer, + MCSymbol *Symbol, + unsigned Size, + uint32_t Offset) { + MCContext &context = Streamer->getContext(); + + if (context.getAsmInfo()->doesDwarfUseRelocationsAcrossSections()) { + if (Offset == 0) { + Streamer->emitSymbolValue(Symbol, Size); + } else { + const MCSymbolRefExpr *SymbolExpr = MCSymbolRefExpr::create(Symbol, + MCSymbolRefExpr::VK_None, context); + const MCExpr *OffsetExpr = MCConstantExpr::create(Offset, context); + const MCExpr *Expr = MCBinaryExpr::createAdd(SymbolExpr, OffsetExpr, context); + Streamer->emitValue(Expr, Size); + } + } else { + Streamer->emitIntValue(Symbol->getOffset() + Offset, Size); + } +} + +const MCExpr *DwarfInfo::CreateOffsetExpr(MCContext &Context, + MCSymbol *BeginSymbol, + MCSymbol *Symbol) { + MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None; + const MCExpr *StartExpr = + MCSymbolRefExpr::create(BeginSymbol, Variant, Context); + const MCExpr *EndExpr = + MCSymbolRefExpr::create(Symbol, Variant, Context); + return MCBinaryExpr::createSub(EndExpr, StartExpr, Context); +} + +void DwarfInfo::EmitOffset(MCObjectStreamer *Streamer, + const MCExpr *OffsetExpr, + unsigned Size) { + MCContext &context = Streamer->getContext(); + + if (!context.getAsmInfo()->hasAggressiveSymbolFolding()) { + MCSymbol *Temp = context.createTempSymbol(); + Streamer->emitAssignment(Temp, OffsetExpr); + OffsetExpr = MCSymbolRefExpr::create(Temp, context); + } + + Streamer->emitValue(OffsetExpr, Size); +} + +void DwarfInfo::EmitInfoOffset(MCObjectStreamer *Streamer, const DwarfInfo *Info, unsigned Size) { + uint64_t Offset = Info->InfoSymbol->getOffset(); + if (Offset != 0) { + Streamer->emitIntValue(Offset, Size); + } else { + EmitOffset(Streamer, Info->InfoExpr, Size); + } +} + +// DwarfPrimitiveTypeInfo + +struct PrimitiveTypeDesc { + const char *Name; + int Encoding; + unsigned ByteSize; +}; + +static PrimitiveTypeDesc GetPrimitiveTypeDesc(PrimitiveTypeFlags Type, unsigned TargetPointerSize) { + switch (Type) { + case PrimitiveTypeFlags::Boolean: return {"bool", dwarf::DW_ATE_boolean, 1}; + case PrimitiveTypeFlags::Char: return {"char16_t", dwarf::DW_ATE_UTF, 2}; + case PrimitiveTypeFlags::SByte: return {"sbyte", dwarf::DW_ATE_signed, 1}; + case PrimitiveTypeFlags::Byte: return {"byte", dwarf::DW_ATE_unsigned, 1}; + case PrimitiveTypeFlags::Int16: return {"short", dwarf::DW_ATE_signed, 2}; + case PrimitiveTypeFlags::UInt16: return {"ushort", dwarf::DW_ATE_unsigned, 2}; + case PrimitiveTypeFlags::Int32: return {"int", dwarf::DW_ATE_signed, 4}; + case PrimitiveTypeFlags::UInt32: return {"uint", dwarf::DW_ATE_unsigned, 4}; + case PrimitiveTypeFlags::Int64: return {"long", dwarf::DW_ATE_signed, 8}; + case PrimitiveTypeFlags::UInt64: return {"ulong", dwarf::DW_ATE_unsigned, 8}; + case PrimitiveTypeFlags::IntPtr: return {"System.IntPtr", dwarf::DW_ATE_signed, TargetPointerSize}; + case PrimitiveTypeFlags::UIntPtr: return {"System.UIntPtr", dwarf::DW_ATE_unsigned, TargetPointerSize}; + case PrimitiveTypeFlags::Single: return {"float", dwarf::DW_ATE_float, 4}; + case PrimitiveTypeFlags::Double: return {"double", dwarf::DW_ATE_float, 8}; + default: + assert(false && "Unexpected type"); + return {nullptr, 0, 0}; + } +} + +void DwarfPrimitiveTypeInfo::DumpStrings(MCObjectStreamer *Streamer) { + MCContext &context = Streamer->getContext(); + unsigned TargetPointerSize = context.getAsmInfo()->getCodePointerSize(); + + PrimitiveTypeDesc TD = GetPrimitiveTypeDesc(Type, TargetPointerSize); + if (TD.Name == nullptr) + return; + + Streamer->emitBytes(StringRef(TD.Name)); + Streamer->emitIntValue(0, 1); +} + +void DwarfPrimitiveTypeInfo::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + MCContext &context = Streamer->getContext(); + unsigned TargetPointerSize = context.getAsmInfo()->getCodePointerSize(); + + PrimitiveTypeDesc TD = GetPrimitiveTypeDesc(Type, TargetPointerSize); + if (TD.Name == nullptr) + return; + + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::BaseType); + + // DW_AT_name + EmitSectionOffset(Streamer, StrSymbol, 4); + + // DW_AT_encoding + Streamer->emitIntValue(TD.Encoding, 1); + + // DW_AT_byte_size + Streamer->emitIntValue(TD.ByteSize, 1); +} + +// DwarfVoidTypeInfo + +void DwarfVoidTypeInfo::DumpStrings(MCObjectStreamer* Streamer) { + Streamer->emitBytes(StringRef("void")); + Streamer->emitIntValue(0, 1); +} + +void DwarfVoidTypeInfo::DumpTypeInfo(MCObjectStreamer* Streamer, UserDefinedDwarfTypesBuilder* TypeBuilder) { + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::VoidType); + + // DW_AT_name + EmitSectionOffset(Streamer, StrSymbol, 4); +} + +// DwarfEnumerator + +void DwarfEnumerator::DumpStrings(MCObjectStreamer *Streamer) { + Streamer->emitBytes(Name); + Streamer->emitIntValue(0, 1); +} + +void DwarfEnumerator::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + uint8_t Size = EnumTypeInfo->GetByteSize(); + + // Abbrev Number + switch (Size) { + case 1: + Streamer->emitULEB128IntValue(DwarfAbbrev::Enumerator1); + break; + case 2: + Streamer->emitULEB128IntValue(DwarfAbbrev::Enumerator2); + break; + case 4: + Streamer->emitULEB128IntValue(DwarfAbbrev::Enumerator4); + break; + case 8: + Streamer->emitULEB128IntValue(DwarfAbbrev::Enumerator8); + break; + default: + assert(false && "Unexpected byte size value"); + } + + // DW_AT_name + EmitSectionOffset(Streamer, StrSymbol, 4); + + // DW_AT_const_value + Streamer->emitIntValue(Value, Size); +} + +// DwarfEnumTypeInfo + +DwarfEnumTypeInfo::DwarfEnumTypeInfo(const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords) : + Name(TypeDescriptor.Name), + ElementType(TypeDescriptor.ElementType) { + for (uint64 i = 0; i < TypeDescriptor.ElementCount; i++) { + Records.emplace_back(TypeRecords[i], this); + } +} + +void DwarfEnumTypeInfo::DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) { + if (IsDumpedTypes) + return; + + DwarfInfo::DumpTypes(TypeBuilder, Streamer, TypeSection, StrSection); + + DwarfInfo *Info = TypeBuilder->GetTypeInfoByIndex(ElementType); + assert(Info != nullptr); + + Info->Dump(TypeBuilder, Streamer, TypeSection, StrSection); +} + +void DwarfEnumTypeInfo::Dump(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) { + if (IsDumped) + return; + + MCContext &context = Streamer->getContext(); + unsigned TargetPointerSize = context.getAsmInfo()->getCodePointerSize(); + + DwarfPrimitiveTypeInfo *ElementTypeInfo = static_cast( + TypeBuilder->GetTypeInfoByIndex(ElementType)); + assert(ElementTypeInfo != nullptr); + + PrimitiveTypeDesc TD = GetPrimitiveTypeDesc(ElementTypeInfo->GetType(), TargetPointerSize); + ByteSize = TD.ByteSize; + + DwarfInfo::Dump(TypeBuilder, Streamer, TypeSection, StrSection); + + for (auto &Enumerator : Records) { + Enumerator.Dump(TypeBuilder, Streamer, TypeSection, StrSection); + } + + // Terminate DIE + Streamer->SwitchSection(TypeSection); + Streamer->emitIntValue(0, 1); +} + +void DwarfEnumTypeInfo::DumpStrings(MCObjectStreamer *Streamer) { + Streamer->emitBytes(Name); + Streamer->emitIntValue(0, 1); +} + +void DwarfEnumTypeInfo::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::EnumerationType); + + // DW_AT_name + EmitSectionOffset(Streamer, StrSymbol, 4); + + // DW_AT_type + DwarfInfo *Info = TypeBuilder->GetTypeInfoByIndex(ElementType); + assert(Info != nullptr); + + EmitInfoOffset(Streamer, Info, 4); + + // DW_AT_byte_size + Streamer->emitIntValue(ByteSize, 1); +} + +// DwarfDataField + +void DwarfDataField::DumpStrings(MCObjectStreamer *Streamer) { + Streamer->emitBytes(StringRef(Name)); + Streamer->emitIntValue(0, 1); +} + +void DwarfDataField::DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) { + if (IsDumpedTypes) + return; + + DwarfInfo::DumpTypes(TypeBuilder, Streamer, TypeSection, StrSection); + + DwarfInfo *MemberTypeInfo = TypeBuilder->GetTypeInfoByIndex(TypeIndex); + assert(MemberTypeInfo != nullptr); + + MemberTypeInfo->Dump(TypeBuilder, Streamer, TypeSection, StrSection); +} + +void DwarfDataField::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::ClassMember); + + // DW_AT_name + EmitSectionOffset(Streamer, StrSymbol, 4); + + // DW_AT_type + DwarfInfo *MemberTypeInfo = TypeBuilder->GetTypeInfoByIndex(TypeIndex); + assert(MemberTypeInfo != nullptr); + EmitInfoOffset(Streamer, MemberTypeInfo, 4); + + // DW_AT_data_member_location + Streamer->emitIntValue(Offset, 4); +} + +// DwarfStaticDataField + +void DwarfStaticDataField::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::ClassMemberStatic); + + // DW_AT_name + EmitSectionOffset(Streamer, StrSymbol, 4); + + // DW_AT_type + DwarfInfo *MemberTypeInfo = TypeBuilder->GetTypeInfoByIndex(TypeIndex); + assert(MemberTypeInfo != nullptr); + EmitInfoOffset(Streamer, MemberTypeInfo, 4); +} + +// DwarfClassTypeInfo + +DwarfClassTypeInfo::DwarfClassTypeInfo(const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors, + const StaticDataFieldDescriptor *StaticsDescriptors) : + Name(ClassDescriptor.Name), + IsStruct(ClassDescriptor.IsStruct), + BaseClassId(ClassDescriptor.BaseClassId), + Size(ClassDescriptor.InstanceSize), + IsForwardDecl(false) { + int32_t staticIdx = 0; + for (int32_t i = 0; i < ClassFieldsDescriptor.FieldsCount; i++) { + if (FieldsDescriptors[i].Offset == 0xFFFFFFFF) { + StaticFields.emplace_back(FieldsDescriptors[i], StaticsDescriptors[staticIdx++]); + } else { + Fields.emplace_back(FieldsDescriptors[i]); + } + } +} + +void DwarfClassTypeInfo::DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) { + if (IsDumpedTypes) + return; + + DwarfInfo::DumpTypes(TypeBuilder, Streamer, TypeSection, StrSection); + + if (BaseClassId != 0) { + DwarfInfo *BaseClassInfo = TypeBuilder->GetTypeInfoByIndex(BaseClassId); + assert(BaseClassInfo != nullptr); + + BaseClassInfo->Dump(TypeBuilder, Streamer, TypeSection, StrSection); + } + + for (auto &Field : Fields) { + Field.DumpTypes(TypeBuilder, Streamer, TypeSection, StrSection); + } + + for (auto &StaticField : StaticFields) { + StaticField.DumpTypes(TypeBuilder, Streamer, TypeSection, StrSection); + } + + for (auto *Function : MemberFunctions) { + Function->DumpTypes(TypeBuilder, Streamer, TypeSection, StrSection); + } +} + +void DwarfClassTypeInfo::Dump(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) { + if (IsDumped) + return; + + DwarfInfo::Dump(TypeBuilder, Streamer, TypeSection, StrSection); + + if (IsForwardDecl) + return; + + for (auto &Field : Fields) { + Field.Dump(TypeBuilder, Streamer, TypeSection, StrSection); + } + + for (auto &StaticField : StaticFields) { + StaticField.Dump(TypeBuilder, Streamer, TypeSection, StrSection); + } + + for (auto *Function : MemberFunctions) { + Function->Dump(TypeBuilder, Streamer, TypeSection, StrSection); + } + + // Terminate DIE + Streamer->SwitchSection(TypeSection); + Streamer->emitIntValue(0, 1); +} + +void DwarfClassTypeInfo::DumpStrings(MCObjectStreamer *Streamer) { + Streamer->emitBytes(StringRef(Name)); + Streamer->emitIntValue(0, 1); +} + +void DwarfClassTypeInfo::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + // Abbrev Number + Streamer->emitULEB128IntValue(IsForwardDecl ? DwarfAbbrev::ClassTypeDecl : DwarfAbbrev::ClassType); + + // DW_AT_name + EmitSectionOffset(Streamer, StrSymbol, 4); + + if (!IsForwardDecl) { + // DW_AT_byte_size + Streamer->emitIntValue(Size, 4); + } + + if (BaseClassId != 0) { + DwarfInfo *BaseClassInfo = TypeBuilder->GetTypeInfoByIndex(BaseClassId); + assert(BaseClassInfo != nullptr); + + // DW_TAG_inheritance DIE + + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::ClassInheritance); + + // DW_AT_type + EmitInfoOffset(Streamer, BaseClassInfo, 4); + + // DW_AT_data_member_location = 0 + Streamer->emitIntValue(0, 1); + } +} + +// DwarfSimpleArrayTypeInfo + +void DwarfSimpleArrayTypeInfo::DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) { + if (IsDumpedTypes) + return; + + DwarfInfo::DumpTypes(TypeBuilder, Streamer, TypeSection, StrSection); + + DwarfInfo *ElementInfo = TypeBuilder->GetTypeInfoByIndex(ElementType); + assert(ElementInfo != nullptr); + + ElementInfo->Dump(TypeBuilder, Streamer, TypeSection, StrSection); +} + +void DwarfSimpleArrayTypeInfo::DumpStrings(MCObjectStreamer *Streamer) { + // nothing to dump +} + +void DwarfSimpleArrayTypeInfo::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::ArrayType); + + DwarfInfo *Info = TypeBuilder->GetTypeInfoByIndex(ElementType); + assert(Info != nullptr); + + // DW_AT_type + EmitInfoOffset(Streamer, Info, 4); + + // DW_TAG_subrange_type DIE + + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::SubrangeType); + + // DW_AT_upper_bound + Streamer->emitULEB128IntValue(Size - 1); + + // Terminate DIE + Streamer->emitIntValue(0, 1); +} + +// DwarfPointerTypeInfo + +void DwarfPointerTypeInfo::DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) { + if (IsDumpedTypes) + return; + + DwarfInfo::DumpTypes(TypeBuilder, Streamer, TypeSection, StrSection); + + DwarfInfo *Info = TypeBuilder->GetTypeInfoByIndex(TypeDesc.ElementType); + assert(Info != nullptr); + + Info->Dump(TypeBuilder, Streamer, TypeSection, StrSection); +} + +void DwarfPointerTypeInfo::DumpStrings(MCObjectStreamer *Streamer) { + // nothing to dump +} + +void DwarfPointerTypeInfo::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + // Abbrev Number + Streamer->emitULEB128IntValue(TypeDesc.IsReference ? DwarfAbbrev::ReferenceType : DwarfAbbrev::PointerType); + + DwarfInfo *Info = TypeBuilder->GetTypeInfoByIndex(TypeDesc.ElementType); + assert(Info != nullptr); + + // DW_AT_type + EmitInfoOffset(Streamer, Info, 4); + + // DW_AT_byte_size + Streamer->emitIntValue(TypeDesc.Is64Bit ? 8 : 4, 1); +} + +// DwarfVoidPtrTypeInfo + +void DwarfVoidPtrTypeInfo::DumpStrings(MCObjectStreamer* Streamer) { + // nothing to dump +} + +void DwarfVoidPtrTypeInfo::DumpTypeInfo(MCObjectStreamer* Streamer, UserDefinedDwarfTypesBuilder* TypeBuilder) { + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::VoidPointerType); +} + +// DwarfMemberFunctionTypeInfo + +DwarfMemberFunctionTypeInfo::DwarfMemberFunctionTypeInfo( + const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes, + bool IsStaticMethod) : + TypeDesc(MemberDescriptor), + IsStaticMethod(IsStaticMethod) { + for (uint16_t i = 0; i < MemberDescriptor.NumberOfArguments; i++) { + this->ArgumentTypes.push_back(ArgumentTypes[i]); + } +} + +void DwarfMemberFunctionTypeInfo::DumpStrings(MCObjectStreamer *Streamer) { + // nothing to dump +} + +void DwarfMemberFunctionTypeInfo::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + // nothing to dump +} + +// DwarfMemberFunctionIdTypeInfo + +void DwarfMemberFunctionIdTypeInfo::DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) { + if (IsDumpedTypes) + return; + + DwarfInfo::DumpTypes(TypeBuilder, Streamer, TypeSection, StrSection); + + // Dump return type + DwarfInfo *ReturnTypeInfo = TypeBuilder->GetTypeInfoByIndex(MemberFunctionTypeInfo->GetReturnTypeIndex()); + assert(ReturnTypeInfo != nullptr); + + ReturnTypeInfo->Dump(TypeBuilder, Streamer, TypeSection, StrSection); + + // Dump this pointer type + if (!MemberFunctionTypeInfo->IsStatic()) { + DwarfInfo *ThisPtrTypeInfo = TypeBuilder->GetTypeInfoByIndex(MemberFunctionTypeInfo->GetThisPtrTypeIndex()); + assert(ThisPtrTypeInfo != nullptr); + + ThisPtrTypeInfo->Dump(TypeBuilder, Streamer, TypeSection, StrSection); + } + + // Dump argument types + for (uint32_t ArgTypeIndex : MemberFunctionTypeInfo->GetArgTypes()) { + DwarfInfo *ArgTypeInfo = TypeBuilder->GetTypeInfoByIndex(ArgTypeIndex); + assert(ArgTypeInfo != nullptr); + ArgTypeInfo->Dump(TypeBuilder, Streamer, TypeSection, StrSection); + } +} + +void DwarfMemberFunctionIdTypeInfo::DumpStrings(MCObjectStreamer *Streamer) { + Streamer->emitBytes(StringRef(Name)); + Streamer->emitIntValue(0, 1); + + MCContext &context = Streamer->getContext(); + LinkageNameSymbol = context.createTempSymbol(); + Streamer->emitLabel(LinkageNameSymbol); + Streamer->emitBytes(StringRef(LinkageName)); + Streamer->emitIntValue(0, 1); +} + +void DwarfMemberFunctionIdTypeInfo::DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) { + // Abbrev Number + bool IsStatic = MemberFunctionTypeInfo->IsStatic(); + + Streamer->emitULEB128IntValue(IsStatic ? DwarfAbbrev::SubprogramStaticSpec : DwarfAbbrev::SubprogramSpec); + + // DW_AT_name + EmitSectionOffset(Streamer, StrSymbol, 4); + + // DW_AT_linkage_name + EmitSectionOffset(Streamer, LinkageNameSymbol, 4); + + // DW_AT_decl_file + Streamer->emitIntValue(1, 1); + + // DW_AT_decl_line + Streamer->emitIntValue(1, 1); + + // DW_AT_type + DwarfInfo *ReturnTypeInfo = TypeBuilder->GetTypeInfoByIndex(MemberFunctionTypeInfo->GetReturnTypeIndex()); + assert(ReturnTypeInfo != nullptr); + + EmitInfoOffset(Streamer, ReturnTypeInfo, 4); + + if (!IsStatic) { + // DW_AT_object_pointer + uint32_t Offset = Streamer->getOrCreateDataFragment()->getContents().size(); + + Streamer->emitIntValue(Offset + 4, 4); + + // This formal parameter DIE + DwarfInfo *ThisTypeInfo = TypeBuilder->GetTypeInfoByIndex(MemberFunctionTypeInfo->GetThisPtrTypeIndex()); + assert(ThisTypeInfo != nullptr); + + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::FormalParameterThisSpec); + + // DW_AT_type + EmitInfoOffset(Streamer, ThisTypeInfo, 4); + } + + for (uint32_t ArgTypeIndex : MemberFunctionTypeInfo->GetArgTypes()) { + DwarfInfo *ArgTypeInfo = TypeBuilder->GetTypeInfoByIndex(ArgTypeIndex); + assert(ArgTypeInfo != nullptr); + + // Formal parameter DIE + + // Abbrev Number + Streamer->emitULEB128IntValue(DwarfAbbrev::FormalParameterSpec); + + // DW_AT_type + EmitInfoOffset(Streamer, ArgTypeInfo, 4); + } + + // Ternimate DIE + Streamer->emitIntValue(0, 1); +} + +// DwarfTypesBuilder + +void UserDefinedDwarfTypesBuilder::EmitTypeInformation( + MCSection *TypeSection, + MCSection *StrSection) { + for (auto &Info : DwarfTypes) { + Info->Dump(this, Streamer, TypeSection, StrSection); + } +} + +unsigned UserDefinedDwarfTypesBuilder::GetEnumTypeIndex( + const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords) { + unsigned TypeIndex = ArrayIndexToTypeIndex(DwarfTypes.size()); + UserDefinedTypes.push_back(std::make_pair(TypeDescriptor.Name, TypeIndex)); + DwarfTypes.push_back(std::make_unique(TypeDescriptor, TypeRecords)); + return TypeIndex; +} + +unsigned UserDefinedDwarfTypesBuilder::GetClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor) { + unsigned TypeIndex = ArrayIndexToTypeIndex(DwarfTypes.size()); + DwarfTypes.push_back(std::make_unique(ClassDescriptor)); + return TypeIndex; +} + +unsigned UserDefinedDwarfTypesBuilder::GetCompleteClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors, + const StaticDataFieldDescriptor *StaticsDescriptors) { + unsigned TypeIndex = ArrayIndexToTypeIndex(DwarfTypes.size()); + UserDefinedTypes.push_back(std::make_pair(ClassDescriptor.Name, TypeIndex)); + + DwarfClassTypeInfo *ClassTypeInfo = new DwarfClassTypeInfo(ClassDescriptor, ClassFieldsDescriptor, + FieldsDescriptors, StaticsDescriptors); + + DwarfTypes.push_back(std::unique_ptr(ClassTypeInfo)); + + if (ClassTypeInfo->GetStaticFields().size() > 0) { + ClassesWithStaticFields.push_back(ClassTypeInfo); + } + + return TypeIndex; +} + +unsigned UserDefinedDwarfTypesBuilder::GetArrayTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ArrayTypeDescriptor &ArrayDescriptor) { + // Create corresponding class info + ClassTypeDescriptor ArrayClassDescriptor = ClassDescriptor; + + std::vector FieldDescs; + unsigned FieldOffset = TargetPointerSize; + + FieldDescs.push_back({GetPrimitiveTypeIndex(PrimitiveTypeFlags::Int32), FieldOffset, "m_NumComponents"}); + FieldOffset += TargetPointerSize; + + if (ArrayDescriptor.IsMultiDimensional == 1) { + unsigned BoundsTypeIndex = GetSimpleArrayTypeIndex(GetPrimitiveTypeIndex(PrimitiveTypeFlags::Int32), ArrayDescriptor.Rank); + FieldDescs.push_back({BoundsTypeIndex, FieldOffset, "m_Bounds"}); + FieldOffset += 2 * 4 * ArrayDescriptor.Rank; + } + + unsigned DataTypeIndex = GetSimpleArrayTypeIndex(ArrayDescriptor.ElementType, 0); + FieldDescs.push_back({DataTypeIndex, FieldOffset, "m_Data"}); + + ClassFieldsTypeDescriptior FieldsTypeDesc = + {TargetPointerSize, ArrayDescriptor.IsMultiDimensional ? 3 : 2}; + + ArrayClassDescriptor.InstanceSize = FieldOffset; + + unsigned TypeIndex = ArrayIndexToTypeIndex(DwarfTypes.size()); + UserDefinedTypes.push_back(std::make_pair(ArrayClassDescriptor.Name, TypeIndex)); + DwarfTypes.push_back(std::make_unique(ArrayClassDescriptor, FieldsTypeDesc, FieldDescs.data(), nullptr)); + + return TypeIndex; +} + +unsigned UserDefinedDwarfTypesBuilder::GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor) +{ + unsigned VoidTypeIndex = GetPrimitiveTypeIndex(PrimitiveTypeFlags::Void); + + unsigned TypeIndex = ArrayIndexToTypeIndex(DwarfTypes.size()); + + // Creating a pointer to what DWARF considers Void type (DW_TAG_unspecified_type - + // per http://eagercon.com/dwarf/issues/minutes-001017.htm) leads to unhappines + // since debuggers don't really know how to handle that. The Clang symbol parser + // in LLDB only handles DW_TAG_unspecified_type if it's named + // "nullptr_t" or "decltype(nullptr)". + // + // We resort to this kludge to generate the exact same debug info for void* that + // clang would generate (pointer type with no element type specified). + if (PointerDescriptor.ElementType == VoidTypeIndex) + DwarfTypes.push_back(std::make_unique()); + else + DwarfTypes.push_back(std::make_unique(PointerDescriptor)); + + return TypeIndex; +} + +unsigned UserDefinedDwarfTypesBuilder::GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes) +{ + bool IsStatic = MemberDescriptor.TypeIndexOfThisPointer == GetPrimitiveTypeIndex(PrimitiveTypeFlags::Void); + unsigned TypeIndex = ArrayIndexToTypeIndex(DwarfTypes.size()); + DwarfTypes.push_back(std::make_unique(MemberDescriptor, ArgumentTypes, IsStatic)); + return TypeIndex; +} + +unsigned UserDefinedDwarfTypesBuilder::GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor) +{ + unsigned TypeIndex = ArrayIndexToTypeIndex(DwarfTypes.size()); + + DwarfMemberFunctionTypeInfo *MemberFunctionTypeInfo = static_cast( + GetTypeInfoByIndex(MemberIdDescriptor.MemberFunction)); + assert(MemberFunctionTypeInfo != nullptr); + + DwarfMemberFunctionIdTypeInfo *MemberFunctionIdTypeInfo = + new DwarfMemberFunctionIdTypeInfo(MemberIdDescriptor, MemberFunctionTypeInfo); + + DwarfTypes.push_back(std::unique_ptr(MemberFunctionIdTypeInfo)); + + DwarfClassTypeInfo *ParentClassInfo = static_cast( + GetTypeInfoByIndex(MemberIdDescriptor.ParentClass)); + assert(ParentClassInfo != nullptr); + + ParentClassInfo->AddMemberFunction(MemberFunctionIdTypeInfo); + + return TypeIndex; +} + +unsigned UserDefinedDwarfTypesBuilder::GetPrimitiveTypeIndex(PrimitiveTypeFlags Type) { + auto Iter = PrimitiveDwarfTypes.find(Type); + if (Iter != PrimitiveDwarfTypes.end()) + return Iter->second; + + unsigned TypeIndex = ArrayIndexToTypeIndex(DwarfTypes.size()); + + if (Type == PrimitiveTypeFlags::Void) + DwarfTypes.push_back(std::make_unique()); + else + DwarfTypes.push_back(std::make_unique(Type)); + + PrimitiveDwarfTypes.insert(std::make_pair(Type, TypeIndex)); + + return TypeIndex; +} + +unsigned UserDefinedDwarfTypesBuilder::GetSimpleArrayTypeIndex(unsigned ElemIndex, unsigned Size) { + auto Iter = SimpleArrayDwarfTypes.find(ElemIndex); + if (Iter != SimpleArrayDwarfTypes.end()) { + auto CountMap = Iter->second; + auto CountIter = CountMap.find(Size); + if (CountIter != CountMap.end()) + return CountIter->second; + } + + unsigned TypeIndex = ArrayIndexToTypeIndex(DwarfTypes.size()); + DwarfTypes.push_back(std::make_unique(ElemIndex, Size)); + + SimpleArrayDwarfTypes[ElemIndex][Size] = TypeIndex; + + return TypeIndex; +} diff --git a/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfTypeBuilder.h b/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfTypeBuilder.h new file mode 100644 index 00000000000000..82942450419616 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/debugInfo/dwarf/dwarfTypeBuilder.h @@ -0,0 +1,385 @@ +//===---- dwarfTypeBuilder.h ------------------------------------*- C++ -*-===// +// +// type builder is used to convert .Net types into Dwarf debuginfo. +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "debugInfo/typeBuilder.h" + +#include +#include + +class UserDefinedDwarfTypesBuilder; + +class DwarfInfo +{ +public: + DwarfInfo() : + StrSymbol(nullptr), + InfoSymbol(nullptr), + IsDumped(false), + IsDumpedTypes(false) {} + + virtual ~DwarfInfo() {} + + virtual void Dump(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection); + + virtual void DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection); + + MCSymbol *GetInfoSymbol() { return InfoSymbol; } + + const MCExpr *GetInfoExpr() { return InfoExpr; } + +protected: + virtual void DumpStrings(MCObjectStreamer *Streamer) = 0; + virtual void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) = 0; + + static void EmitSectionOffset(MCObjectStreamer *Streamer, + MCSymbol *Symbol, + unsigned Size, + uint32_t Offset = 0); + + static const MCExpr *CreateOffsetExpr(MCContext &Context, + MCSymbol *BeginSymbol, + MCSymbol *Symbol); + + static void EmitOffset(MCObjectStreamer *Streamer, + const MCExpr *OffsetExpr, + unsigned Size); + + static void EmitInfoOffset(MCObjectStreamer *Streamer, const DwarfInfo *Info, unsigned Size); + + MCSymbol *StrSymbol; + MCSymbol *InfoSymbol; + const MCExpr *InfoExpr; + + bool IsDumped; + bool IsDumpedTypes; +}; + +class DwarfPrimitiveTypeInfo : public DwarfInfo +{ +public: + DwarfPrimitiveTypeInfo(PrimitiveTypeFlags PrimitiveType) : Type(PrimitiveType) {} + + PrimitiveTypeFlags GetType() { return Type; } + +protected: + void DumpStrings(MCObjectStreamer *Streamer) override; + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + +private: + PrimitiveTypeFlags Type; +}; + +class DwarfVoidTypeInfo : public DwarfPrimitiveTypeInfo +{ +public: + DwarfVoidTypeInfo() : DwarfPrimitiveTypeInfo(PrimitiveTypeFlags::Void) {} + +protected: + void DumpStrings(MCObjectStreamer* Streamer) override; + void DumpTypeInfo(MCObjectStreamer* Streamer, UserDefinedDwarfTypesBuilder* TypeBuilder) override; +}; + +class DwarfEnumTypeInfo; + +class DwarfEnumerator : public DwarfInfo +{ +public: + DwarfEnumerator(const EnumRecordTypeDescriptor &Descriptor, DwarfEnumTypeInfo *TypeInfo) : + Name(Descriptor.Name), Value(Descriptor.Value), EnumTypeInfo(TypeInfo) {} + +protected: + void DumpStrings(MCObjectStreamer *Streamer) override; + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + +private: + std::string Name; + uint64 Value; + DwarfEnumTypeInfo *EnumTypeInfo; +}; + +class DwarfEnumTypeInfo : public DwarfInfo +{ +public: + DwarfEnumTypeInfo(const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords); + + void Dump(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) override; + + void DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) override; + + uint8_t GetByteSize() const { return ByteSize; } + +protected: + void DumpStrings(MCObjectStreamer *Streamer) override; + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + +private: + std::string Name; + uint32_t ElementType; + std::vector Records; + uint8_t ByteSize; +}; + +class DwarfDataField : public DwarfInfo +{ +public: + DwarfDataField(const DataFieldDescriptor &Descriptor) : + Name(Descriptor.Name), + TypeIndex(Descriptor.FieldTypeIndex), + Offset(Descriptor.Offset) {} + + void DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) override; + + uint32_t GetTypeIndex() const { return TypeIndex; } + + const std::string &GetName() const { return Name; } + +protected: + void DumpStrings(MCObjectStreamer *Streamer) override; + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + + std::string Name; + uint32_t TypeIndex; + uint64 Offset; +}; + +class DwarfStaticDataField : public DwarfDataField +{ +public: + DwarfStaticDataField(const DataFieldDescriptor &Descriptor, + const StaticDataFieldDescriptor &StaticDescriptor) : + DwarfDataField(Descriptor), + StaticDataName(StaticDescriptor.StaticDataName), + StaticOffset(StaticDescriptor.StaticOffset), + StaticDataInObject(StaticDescriptor.IsStaticDataInObject) {} + + const std::string &GetStaticDataName() const { return StaticDataName; } + uint64 GetStaticOffset() const { return StaticOffset; } + bool IsStaticDataInObject() const { return StaticDataInObject; } + +protected: + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + +private: + std::string StaticDataName; + uint64 StaticOffset; + bool StaticDataInObject; +}; + +class DwarfMemberFunctionIdTypeInfo; + +class DwarfClassTypeInfo : public DwarfInfo +{ +public: + DwarfClassTypeInfo(const ClassTypeDescriptor &ClassDescriptor) : + Name(ClassDescriptor.Name), + IsStruct(ClassDescriptor.IsStruct), + BaseClassId(ClassDescriptor.BaseClassId), + Size(ClassDescriptor.InstanceSize), + IsForwardDecl(true) {} + + DwarfClassTypeInfo(const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors, + const StaticDataFieldDescriptor *StaticsDescriptors); + + void Dump(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) override; + + void DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) override; + + void AddMemberFunction(DwarfMemberFunctionIdTypeInfo* TypeInfo) { + MemberFunctions.push_back(TypeInfo); + } + + const std::vector &GetStaticFields() const { return StaticFields; } + + const std::string &GetName() const { return Name; } + +protected: + void DumpStrings(MCObjectStreamer *Streamer) override; + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + +private: + std::string Name; + bool IsStruct; + uint32_t BaseClassId; + uint64 Size; + bool IsForwardDecl; + std::vector Fields; + std::vector StaticFields; + std::vector MemberFunctions; +}; + +class DwarfSimpleArrayTypeInfo : public DwarfInfo +{ +public: + DwarfSimpleArrayTypeInfo(uint32_t ArrayElementType, uint64_t Size) : + ElementType(ArrayElementType), + Size(Size) {} + + void DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) override; + +protected: + void DumpStrings(MCObjectStreamer *Streamer) override; + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + +private: + uint32_t ElementType; + uint64_t Size; +}; + +class DwarfPointerTypeInfo : public DwarfInfo +{ +public: + DwarfPointerTypeInfo(const PointerTypeDescriptor& PointerDescriptor) : + TypeDesc(PointerDescriptor) {} + + void DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) override; + +protected: + void DumpStrings(MCObjectStreamer *Streamer) override; + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + +private: + PointerTypeDescriptor TypeDesc; +}; + +class DwarfVoidPtrTypeInfo : public DwarfInfo +{ +public: + DwarfVoidPtrTypeInfo() {} + +protected: + void DumpStrings(MCObjectStreamer* Streamer) override; + void DumpTypeInfo(MCObjectStreamer* Streamer, UserDefinedDwarfTypesBuilder* TypeBuilder) override; +}; + +class DwarfMemberFunctionTypeInfo : public DwarfInfo +{ +public: + DwarfMemberFunctionTypeInfo(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes, + bool IsStaticMethod); + + const std::vector &GetArgTypes() const { return ArgumentTypes; } + + bool IsStatic() const { return IsStaticMethod; } + + uint32_t GetReturnTypeIndex() const { return TypeDesc.ReturnType; } + + uint32_t GetThisPtrTypeIndex() const { return TypeDesc.TypeIndexOfThisPointer; } + +protected: + void DumpStrings(MCObjectStreamer *Streamer) override; + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + +private: + MemberFunctionTypeDescriptor TypeDesc; + std::vector ArgumentTypes; + bool IsStaticMethod; +}; + +class DwarfMemberFunctionIdTypeInfo : public DwarfInfo +{ +public: + DwarfMemberFunctionIdTypeInfo(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor, + DwarfMemberFunctionTypeInfo *TypeInfo) : + LinkageName(MemberIdDescriptor.Name), + Name(MemberIdDescriptor.Name), + ParentClass(MemberIdDescriptor.ParentClass), + MemberFunctionTypeInfo(TypeInfo), + LinkageNameSymbol(nullptr) {} + + const std::vector &GetArgTypes() const { return MemberFunctionTypeInfo->GetArgTypes(); } + + void SetLinkageName(const char *Name) { LinkageName = Name; } + + void DumpTypes(UserDefinedDwarfTypesBuilder *TypeBuilder, MCObjectStreamer *Streamer, + MCSection *TypeSection, MCSection *StrSection) override; + + bool IsStatic() const { return MemberFunctionTypeInfo->IsStatic(); } + +protected: + void DumpStrings(MCObjectStreamer *Streamer) override; + void DumpTypeInfo(MCObjectStreamer *Streamer, UserDefinedDwarfTypesBuilder *TypeBuilder) override; + +private: + std::string LinkageName; + std::string Name; + uint32_t ParentClass; + DwarfMemberFunctionTypeInfo *MemberFunctionTypeInfo; + MCSymbol *LinkageNameSymbol; +}; + +template +class EnumHash +{ + typedef typename std::underlying_type::type enumType; +public: + size_t operator()(const T& elem) const { + return std::hash()(static_cast(elem)); + } +}; + +class UserDefinedDwarfTypesBuilder : public UserDefinedTypesBuilder +{ +public: + UserDefinedDwarfTypesBuilder() {} + void EmitTypeInformation(MCSection *TypeSection, MCSection *StrSection = nullptr) override; + + unsigned GetEnumTypeIndex(const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords) override; + unsigned GetClassTypeIndex(const ClassTypeDescriptor &ClassDescriptor) override; + unsigned GetCompleteClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors, + const StaticDataFieldDescriptor *StaticsDescriptors) override; + + unsigned GetArrayTypeIndex(const ClassTypeDescriptor &ClassDescriptor, + const ArrayTypeDescriptor &ArrayDescriptor) override; + + unsigned GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor) override; + + unsigned GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes) override; + + unsigned GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor) override; + + unsigned GetPrimitiveTypeIndex(PrimitiveTypeFlags Type) override; + + DwarfInfo *GetTypeInfoByIndex(unsigned Index) const { return DwarfTypes[TypeIndexToArrayIndex(Index)].get(); } + + unsigned GetSimpleArrayTypeIndex(unsigned ElemIndex, unsigned Size); + + const std::vector &GetClassesWithStaticFields() const { return ClassesWithStaticFields; } + +private: + static const unsigned StartTypeIndex = 1; // Make TypeIndex 0 - Invalid + static unsigned TypeIndexToArrayIndex(unsigned TypeIndex) { return TypeIndex - StartTypeIndex; } + static unsigned ArrayIndexToTypeIndex(unsigned ArrayIndex) { return ArrayIndex + StartTypeIndex; } + + std::vector> DwarfTypes; + std::unordered_map> PrimitiveDwarfTypes; + // map[ElemTypeIndex][Size] -> ArrTypeIndex + std::unordered_map> SimpleArrayDwarfTypes; + + std::vector ClassesWithStaticFields; +}; diff --git a/src/coreclr/tools/aot/ObjWriter/debugInfo/typeBuilder.h b/src/coreclr/tools/aot/ObjWriter/debugInfo/typeBuilder.h new file mode 100644 index 00000000000000..67a5004d248b9a --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/debugInfo/typeBuilder.h @@ -0,0 +1,157 @@ +//===---- typeBuilder.h -----------------------------------------*- C++ -*-===// +// +// type builder is used to convert .Net types into debuginfo. +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "llvm/MC/MCObjectStreamer.h" + +#include +#include + +using namespace llvm; + +// Keep in sync with Internal.TypeSystem.TypeFlags (Common/src/TypeSystem/Common/TypeFlags.cs) +enum class PrimitiveTypeFlags { + Unknown = 0x00, + Void = 0x01, + Boolean = 0x02, + Char = 0x03, + SByte = 0x04, + Byte = 0x05, + Int16 = 0x06, + UInt16 = 0x07, + Int32 = 0x08, + UInt32 = 0x09, + Int64 = 0x0A, + UInt64 = 0x0B, + IntPtr = 0x0C, + UIntPtr = 0x0D, + Single = 0x0E, + Double = 0x0F +}; + +typedef unsigned long long uint64; + +#pragma pack(push, 8) + +extern "C" struct EnumRecordTypeDescriptor { + uint64 Value; + const char *Name; +}; + +extern "C" struct EnumTypeDescriptor { + uint32_t ElementType; + uint64 ElementCount; + const char *Name; +}; + +extern "C" struct ClassTypeDescriptor { + int32_t IsStruct; + const char *Name; + uint32_t BaseClassId; + uint64 InstanceSize; +}; + +extern "C" struct DataFieldDescriptor { + uint32_t FieldTypeIndex; + uint64 Offset; + const char *Name; +}; + +extern "C" struct StaticDataFieldDescriptor { + const char *StaticDataName; + uint64 StaticOffset; + int IsStaticDataInObject; +}; + +extern "C" struct ClassFieldsTypeDescriptior { + uint64 Size; + int32_t FieldsCount; +}; + +extern "C" struct ArrayTypeDescriptor { + uint32_t Rank; + uint32_t ElementType; + uint32_t Size; // ElementSize + int32_t IsMultiDimensional; +}; + +extern "C" struct PointerTypeDescriptor { + uint32_t ElementType; + int32_t IsReference; + int32_t IsConst; + int32_t Is64Bit; +}; + +extern "C" struct MemberFunctionTypeDescriptor { + uint32_t ReturnType; + uint32_t ContainingClass; + uint32_t TypeIndexOfThisPointer; + int32_t ThisAdjust; + uint32_t CallingConvention; + uint16_t NumberOfArguments; +}; + +extern "C" struct MemberFunctionIdTypeDescriptor { + uint32_t MemberFunction; + uint32_t ParentClass; + const char *Name; +}; + +#pragma pack(pop) +class UserDefinedTypesBuilder { +public: + UserDefinedTypesBuilder() : Streamer(nullptr), TargetPointerSize(0) {} + virtual ~UserDefinedTypesBuilder() {} + void SetStreamer(MCObjectStreamer *Streamer) { + assert(this->Streamer == nullptr); + assert(Streamer != nullptr); + this->Streamer = Streamer; + } + MCObjectStreamer *GetStreamer() const { + return Streamer; + } + void SetTargetPointerSize(unsigned TargetPointerSize) { + assert(this->TargetPointerSize == 0); + assert(TargetPointerSize != 0); + this->TargetPointerSize = TargetPointerSize; + } + virtual void EmitTypeInformation(MCSection *TypeSection, MCSection *StrSection = nullptr) = 0; + + virtual unsigned GetEnumTypeIndex(const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords) = 0; + virtual unsigned GetClassTypeIndex(const ClassTypeDescriptor &ClassDescriptor) = 0; + virtual unsigned GetCompleteClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors, + const StaticDataFieldDescriptor *StaticsDescriptors) = 0; + + virtual unsigned GetArrayTypeIndex(const ClassTypeDescriptor &ClassDescriptor, + const ArrayTypeDescriptor &ArrayDescriptor) = 0; + + virtual unsigned GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor) = 0; + + virtual unsigned GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes) = 0; + + virtual unsigned GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor) = 0; + + virtual unsigned GetPrimitiveTypeIndex(PrimitiveTypeFlags Type) = 0; + + virtual const std::vector> &GetUDTs() { + return UserDefinedTypes; + } + +protected: + MCObjectStreamer *Streamer; + unsigned TargetPointerSize; + + std::vector> UserDefinedTypes; +}; diff --git a/src/coreclr/tools/aot/ObjWriter/jitDebugInfo.h b/src/coreclr/tools/aot/ObjWriter/jitDebugInfo.h new file mode 100644 index 00000000000000..88a96f76732878 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/jitDebugInfo.h @@ -0,0 +1,66 @@ +#ifndef JIT_DEBUG_INFO_H +#define JIT_DEBUG_INFO_H + +typedef unsigned int DWORD; + +#define PORTABILITY_WARNING(msg) +#include "cordebuginfo.h" + +// RegNum enumeration is architecture-specific and we need it for all +// architectures we support. + +namespace X86 { +#define TARGET_X86 1 +#include "cordebuginfo.h" +#undef TARGET_X86 +} + +namespace Amd64 { +#define TARGET_AMD64 1 +#include "cordebuginfo.h" +#undef TARGET_AMD64 +} + +namespace Arm { +#define TARGET_ARM 1 +#include "cordebuginfo.h" +#undef TARGET_ARM +} + +namespace Arm64 { +#define TARGET_ARM64 1 +#include "cordebuginfo.h" +#undef TARGET_ARM64 +} + +struct DebugLocInfo { + int NativeOffset; + int FileId; + int LineNumber; + int ColNumber; +}; + +struct DebugVarInfo { + std::string Name; + int TypeIndex; + bool IsParam; + std::vector Ranges; + + DebugVarInfo() {} + DebugVarInfo(char *ArgName, int ArgTypeIndex, bool ArgIsParam) + : Name(ArgName), TypeIndex(ArgTypeIndex), IsParam(ArgIsParam) {} +}; + +struct DebugEHClauseInfo { + unsigned TryOffset; + unsigned TryLength; + unsigned HandlerOffset; + unsigned HandlerLength; + + DebugEHClauseInfo(unsigned TryOffset, unsigned TryLength, + unsigned HandlerOffset, unsigned HandlerLength) : + TryOffset(TryOffset), TryLength(TryLength), + HandlerOffset(HandlerOffset), HandlerLength(HandlerLength) {} +}; + +#endif // JIT_DEBUG_INFO_H diff --git a/src/coreclr/tools/aot/ObjWriter/llvm.patch b/src/coreclr/tools/aot/ObjWriter/llvm.patch new file mode 100644 index 00000000000000..58fa44c9b924eb --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/llvm.patch @@ -0,0 +1,333 @@ +diff --git a/llvm/include/llvm/MC/MCObjectStreamer.h b/llvm/include/llvm/MC/MCObjectStreamer.h +index a00000bc11b60cae8567ee49da9dd7a4967aac1e..810fe68fc5bf3501c7a3543696485990ea558cb9 100644 +--- a/llvm/include/llvm/MC/MCObjectStreamer.h ++++ b/llvm/include/llvm/MC/MCObjectStreamer.h +@@ -120,6 +120,11 @@ public: + void emitAssignment(MCSymbol *Symbol, const MCExpr *Value) override; + void emitValueImpl(const MCExpr *Value, unsigned Size, + SMLoc Loc = SMLoc()) override; ++ /// \brief EmitValueImpl with additional param, that allows to emit PCRelative ++ /// MCFixup. ++ void emitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc, ++ bool isPCRelative); ++ + void emitULEB128Value(const MCExpr *Value) override; + void emitSLEB128Value(const MCExpr *Value) override; + void emitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol) override; +diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h +index cdc728f7377207f809781e53831fe4e8cc32d82e..8457b465e807bf5b8ee889548074de00a199a81f 100644 +--- a/llvm/include/llvm/MC/MCStreamer.h ++++ b/llvm/include/llvm/MC/MCStreamer.h +@@ -146,6 +146,7 @@ public: + virtual void emitPad(int64_t Offset); + virtual void emitRegSave(const SmallVectorImpl &RegList, + bool isVector); ++ virtual void emitLsda(const MCSymbol *Symbol); + virtual void emitUnwindRaw(int64_t StackOffset, + const SmallVectorImpl &Opcodes); + +@@ -669,6 +670,9 @@ public: + /// etc. + virtual void emitBytes(StringRef Data); + ++ /// \brief Emit the given \p Instruction data into the current section. ++ virtual void emitInstructionBytes(StringRef Data); ++ + /// Functionally identical to EmitBytes. When emitting textual assembly, this + /// method uses .byte directives instead of .ascii or .asciz for readability. + virtual void emitBinaryData(StringRef Data); +diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp +index 1c23d31f8744a6f842fa96279daf7ab7770157b1..1c7012bd9b77360d20c860fc6fa198c4cc06e6e5 100644 +--- a/llvm/lib/MC/MCObjectStreamer.cpp ++++ b/llvm/lib/MC/MCObjectStreamer.cpp +@@ -223,7 +223,7 @@ void MCObjectStreamer::emitCFISections(bool EH, bool Debug) { + } + + void MCObjectStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, +- SMLoc Loc) { ++ SMLoc Loc, bool isPCRelative) { + MCStreamer::emitValueImpl(Value, Size, Loc); + MCDataFragment *DF = getOrCreateDataFragment(); + flushPendingLabels(DF, DF->getContents().size()); +@@ -243,10 +243,15 @@ void MCObjectStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, + } + DF->getFixups().push_back( + MCFixup::create(DF->getContents().size(), Value, +- MCFixup::getKindForSize(Size, false), Loc)); ++ MCFixup::getKindForSize(Size, isPCRelative), Loc)); + DF->getContents().resize(DF->getContents().size() + Size, 0); + } + ++void MCObjectStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, ++ SMLoc Loc) { ++ emitValueImpl(Value, Size, Loc, false); ++} ++ + MCSymbol *MCObjectStreamer::emitCFILabel() { + MCSymbol *Label = getContext().createTempSymbol("cfi"); + emitLabel(Label); +diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp +index 4b5ae3cc202de943e93ea7f3d84cfe9b47c40baf..623e06fa6ba889c6d0c7cc203821f4411678e39e 100644 +--- a/llvm/lib/MC/MCStreamer.cpp ++++ b/llvm/lib/MC/MCStreamer.cpp +@@ -1136,6 +1136,7 @@ void MCStreamer::emitTBSSSymbol(MCSection *Section, MCSymbol *Symbol, + void MCStreamer::changeSection(MCSection *, const MCExpr *) {} + void MCStreamer::emitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol) {} + void MCStreamer::emitBytes(StringRef Data) {} ++void MCStreamer::emitInstructionBytes(StringRef Data) { emitBytes(Data); } + void MCStreamer::emitBinaryData(StringRef Data) { emitBytes(Data); } + void MCStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc) { + visitUsedExpr(*Value); +diff --git a/llvm/lib/MC/WinCOFFObjectWriter.cpp b/llvm/lib/MC/WinCOFFObjectWriter.cpp +index 901d2c06e716f0ab05ea18385662ab548fcba9b4..932f487f7dcd612627f6aa205edea86c969d1740 100644 +--- a/llvm/lib/MC/WinCOFFObjectWriter.cpp ++++ b/llvm/lib/MC/WinCOFFObjectWriter.cpp +@@ -794,7 +794,9 @@ void WinCOFFObjectWriter::recordRelocation(MCAssembler &Asm, + if ((Header.Machine == COFF::IMAGE_FILE_MACHINE_AMD64 && + Reloc.Data.Type == COFF::IMAGE_REL_AMD64_REL32) || + (Header.Machine == COFF::IMAGE_FILE_MACHINE_I386 && +- Reloc.Data.Type == COFF::IMAGE_REL_I386_REL32)) ++ Reloc.Data.Type == COFF::IMAGE_REL_I386_REL32) || ++ (Header.Machine == COFF::IMAGE_FILE_MACHINE_ARM64 && ++ Reloc.Data.Type == COFF::IMAGE_REL_ARM64_REL32)) + FixedValue += 4; + + if (Header.Machine == COFF::IMAGE_FILE_MACHINE_ARMNT) { +diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp +index 75a9f2f5c80e3b6c81258084f52237a54f6aad43..73a2d7b2c5bf966bddc0d0435ca6c008bd8bc6c9 100644 +--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp ++++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp +@@ -141,6 +141,7 @@ static unsigned getFixupKindNumBytes(unsigned Kind) { + case AArch64::fixup_aarch64_pcrel_call26: + case FK_Data_4: + case FK_SecRel_4: ++ case FK_PCRel_4: + return 4; + + case FK_Data_8: +@@ -330,13 +331,19 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, const MCValue &Target, + case FK_Data_8: + case FK_SecRel_2: + case FK_SecRel_4: ++ case FK_PCRel_4: + return Value; + } + } + + Optional AArch64AsmBackend::getFixupKind(StringRef Name) const { +- if (!TheTriple.isOSBinFormatELF()) +- return None; ++ if (!TheTriple.isOSBinFormatELF()) { ++ return StringSwitch>(Name) ++ .Case("R_AARCH64_CALL26", (MCFixupKind)AArch64::fixup_aarch64_pcrel_call26) ++ .Case("R_AARCH64_ADR_PREL_PG_HI21", (MCFixupKind)AArch64::fixup_aarch64_pcrel_adrp_imm21) ++ .Case("R_AARCH64_ADD_ABS_LO12_NC", (MCFixupKind)AArch64::fixup_aarch64_add_imm12) ++ .Default(MCAsmBackend::getFixupKind(Name)); ++ } + + unsigned Type = llvm::StringSwitch(Name) + #define ELF_RELOC(X, Y) .Case(#X, Y) +diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp +index fcf67bd2f740f5f01454029d0d33c5f1172737f8..c6bcf963f6f89fe6eb7962c3fc0edd08cad94816 100644 +--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp ++++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp +@@ -130,6 +130,7 @@ unsigned AArch64ELFObjectWriter::getRelocType(MCContext &Ctx, + return ELF::R_AARCH64_NONE; + case FK_Data_2: + return R_CLS(PREL16); ++ case FK_PCRel_4: + case FK_Data_4: { + return Target.getAccessVariant() == MCSymbolRefExpr::VK_PLT + ? R_CLS(PLT32) +diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFObjectWriter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFObjectWriter.cpp +index aaadc8dc1b6001ace9268be68eadb7bd38035304..48ed1e63f6d9bd73a4816e5816b6e34992c5c0a1 100644 +--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFObjectWriter.cpp ++++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFObjectWriter.cpp +@@ -91,6 +91,9 @@ unsigned AArch64WinCOFFObjectWriter::getRelocType( + case FK_Data_8: + return COFF::IMAGE_REL_ARM64_ADDR64; + ++ case FK_PCRel_4: ++ return COFF::IMAGE_REL_ARM64_REL32; ++ + case FK_SecRel_2: + return COFF::IMAGE_REL_ARM64_SECTION; + +diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp +index b02aef3c338b8fcc4f7d3f110a6a9c5417518c88..f9636aed5068c8fdd20d1b5b2c03e97c9f1b2f20 100644 +--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp ++++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp +@@ -448,6 +448,8 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm, + case FK_Data_2: + case FK_Data_4: + return Value; ++ case FK_PCRel_4: ++ return Value; + case FK_SecRel_2: + return Value; + case FK_SecRel_4: +@@ -967,6 +969,9 @@ static unsigned getFixupKindNumBytes(unsigned Kind) { + case ARM::fixup_le: + return 4; + ++ case FK_PCRel_4: ++ return 4; ++ + case FK_SecRel_2: + return 2; + case FK_SecRel_4: +diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp +index 37d81e4b0af13dd0c450849d2a24ab33a5b0c6d1..00a543cc08a9665c42dafa8a863aa41e85705ee9 100644 +--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp ++++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp +@@ -111,6 +111,8 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target, + case MCSymbolRefExpr::VK_ARM_PREL31: + return ELF::R_ARM_PREL31; + } ++ case FK_PCRel_4: ++ return ELF::R_ARM_REL32; + case ARM::fixup_arm_blx: + case ARM::fixup_arm_uncondbl: + switch (Modifier) { +diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp +index 07ca5c29f0ec995c9f0213a78d3f0f6e386289e0..fa9f498182403136765f29136a1318260c14deb2 100644 +--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp ++++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp +@@ -84,6 +84,7 @@ class ARMTargetAsmStreamer : public ARMTargetStreamer { + void emitPad(int64_t Offset) override; + void emitRegSave(const SmallVectorImpl &RegList, + bool isVector) override; ++ void emitLsda(const MCSymbol* Symbol) override; + void emitUnwindRaw(int64_t Offset, + const SmallVectorImpl &Opcodes) override; + +@@ -272,6 +273,8 @@ void ARMTargetAsmStreamer::emitUnwindRaw(int64_t Offset, + OS << '\n'; + } + ++void ARMTargetAsmStreamer::emitLsda(const MCSymbol* Symbol) {} ++ + class ARMTargetELFStreamer : public ARMTargetStreamer { + private: + // This structure holds all attributes, accounting for +@@ -388,6 +391,7 @@ private: + void emitPad(int64_t Offset) override; + void emitRegSave(const SmallVectorImpl &RegList, + bool isVector) override; ++ void emitLsda(const MCSymbol *Symbol) override; + void emitUnwindRaw(int64_t Offset, + const SmallVectorImpl &Opcodes) override; + +@@ -457,6 +461,7 @@ public: + void emitMovSP(unsigned Reg, int64_t Offset = 0); + void emitPad(int64_t Offset); + void emitRegSave(const SmallVectorImpl &RegList, bool isVector); ++ void emitLsda(const MCSymbol* Symbol); + void emitUnwindRaw(int64_t Offset, const SmallVectorImpl &Opcodes); + void emitFill(const MCExpr &NumBytes, uint64_t FillValue, + SMLoc Loc) override { +@@ -536,6 +541,18 @@ public: + MCELFStreamer::emitBytes(Data); + } + ++ /// This function is the one used to emit instruction data into the ELF ++ /// streamer. We override it to add the appropriate mapping symbol if ++ /// necessary. ++ void emitInstructionBytes(StringRef Data) override { ++ if (IsThumb) ++ EmitThumbMappingSymbol(); ++ else ++ EmitARMMappingSymbol(); ++ ++ MCELFStreamer::emitBytes(Data); ++ } ++ + void FlushPendingMappingSymbol() { + if (!LastEMSInfo->hasInfo()) + return; +@@ -699,6 +716,7 @@ private: + bool CantUnwind; + SmallVector Opcodes; + UnwindOpcodeAssembler UnwindOpAsm; ++ const MCSymbol *Lsda; + }; + + } // end anonymous namespace +@@ -741,6 +759,10 @@ void ARMTargetELFStreamer::emitRegSave(const SmallVectorImpl &RegList, + getStreamer().emitRegSave(RegList, isVector); + } + ++void ARMTargetELFStreamer::emitLsda(const MCSymbol *Symbol) { ++ getStreamer().emitLsda(Symbol); ++} ++ + void ARMTargetELFStreamer::emitUnwindRaw(int64_t Offset, + const SmallVectorImpl &Opcodes) { + getStreamer().emitUnwindRaw(Offset, Opcodes); +@@ -1241,6 +1263,7 @@ void ARMELFStreamer::EHReset() { + PendingOffset = 0; + UsedFP = false; + CantUnwind = false; ++ Lsda = nullptr; + + Opcodes.clear(); + UnwindOpAsm.Reset(); +@@ -1343,6 +1366,8 @@ void ARMELFStreamer::FlushUnwindOpcodes(bool NoHandlerData) { + } + + // Finalize the unwind opcode sequence ++ if (Lsda != nullptr && Opcodes.size() <= 4u) ++ PersonalityIndex = ARM::EHABI::AEABI_UNWIND_CPP_PR1; + UnwindOpAsm.Finalize(PersonalityIndex, Opcodes); + + // For compact model 0, we have to emit the unwind opcodes in the .ARM.exidx +@@ -1387,7 +1412,13 @@ void ARMELFStreamer::FlushUnwindOpcodes(bool NoHandlerData) { + // + // In case that the .handlerdata directive is not specified by the + // programmer, we should emit zero to terminate the handler data. +- if (NoHandlerData && !Personality) ++ if (Lsda != nullptr) { ++ const MCSymbolRefExpr *LsdaRef = ++ MCSymbolRefExpr::create(Lsda, ++ MCSymbolRefExpr::VK_None, ++ getContext()); ++ emitValue(LsdaRef, 4); ++ } else if (NoHandlerData && !Personality) + emitInt32(0); + } + +@@ -1470,6 +1501,10 @@ void ARMELFStreamer::emitRegSave(const SmallVectorImpl &RegList, + UnwindOpAsm.EmitRegSave(Mask); + } + ++void ARMELFStreamer::emitLsda(const MCSymbol *Symbol) { ++ Lsda = Symbol; ++} ++ + void ARMELFStreamer::emitUnwindRaw(int64_t Offset, + const SmallVectorImpl &Opcodes) { + FlushPendingOffset(); +diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp +index 1fee354cad93266060222bec3ed6ceef3d310e54..7d2eb8ef5e367372466c0bf1fb7d98494a00cfcd 100644 +--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp ++++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp +@@ -97,6 +97,7 @@ void ARMTargetStreamer::emitMovSP(unsigned Reg, int64_t Offset) {} + void ARMTargetStreamer::emitPad(int64_t Offset) {} + void ARMTargetStreamer::emitRegSave(const SmallVectorImpl &RegList, + bool isVector) {} ++void ARMTargetStreamer::emitLsda(const MCSymbol *Symbol) {} + void ARMTargetStreamer::emitUnwindRaw(int64_t StackOffset, + const SmallVectorImpl &Opcodes) { + } +diff --git a/llvm/tools/CMakeLists.txt b/llvm/tools/CMakeLists.txt +index b6d3b2c2fcbeb1f3c9ec041766ae60604ec2db64..491715166e96527b09d3299f35615fd9f8ad5fd8 100644 +--- a/llvm/tools/CMakeLists.txt ++++ b/llvm/tools/CMakeLists.txt +@@ -39,6 +39,7 @@ add_llvm_external_project(lldb) + add_llvm_external_project(mlir) + # Flang depends on mlir, so place it afterward + add_llvm_external_project(flang) ++add_llvm_external_project(ObjWriter) + + # Automatically add remaining sub-directories containing a 'CMakeLists.txt' + # file as external projects. diff --git a/src/coreclr/tools/aot/ObjWriter/objwriter.cpp b/src/coreclr/tools/aot/ObjWriter/objwriter.cpp new file mode 100644 index 00000000000000..3e44a8b66ed817 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/objwriter.cpp @@ -0,0 +1,1130 @@ +//===---- objwriter.cpp -----------------------------------------*- C++ -*-===// +// +// object writer +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Implementation of object writer API for JIT/AOT +/// +//===----------------------------------------------------------------------===// + +#include "objwriter.h" +#include "debugInfo/dwarf/dwarfTypeBuilder.h" +#include "debugInfo/codeView/codeViewTypeBuilder.h" +#include "cvconst.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCParser/AsmLexer.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSectionCOFF.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compression.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/Win64EH.h" +#include "llvm/Target/TargetMachine.h" +#include "../../../lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h" + +using namespace llvm; +using namespace llvm::codeview; + +bool error(const Twine &Error) { + errs() << Twine("error: ") + Error + "\n"; + return false; +} + +void ObjectWriter::InitTripleName(const char* tripleName) { + TripleName = tripleName != nullptr ? tripleName : sys::getDefaultTargetTriple(); +} + +Triple ObjectWriter::GetTriple() { + Triple TheTriple(TripleName); + + if (TheTriple.getOS() == Triple::OSType::Darwin) { + TheTriple = Triple( + TheTriple.getArchName(), TheTriple.getVendorName(), "darwin", + TheTriple + .getEnvironmentName()); // it is workaround for llvm bug + // https://bugs.llvm.org//show_bug.cgi?id=24927. + } + return TheTriple; +} + +bool ObjectWriter::Init(llvm::StringRef ObjectFilePath, const char* tripleName) { + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + // Initialize targets + InitializeAllTargetInfos(); + InitializeAllTargetMCs(); + + InitTripleName(tripleName); + Triple TheTriple = GetTriple(); + + // Get the target specific parser. + std::string TargetError; + const Target *TheTarget = + TargetRegistry::lookupTarget(TripleName, TargetError); + if (!TheTarget) { + return error("Unable to create target for " + ObjectFilePath + ": " + + TargetError); + } + + std::error_code EC; + OS.reset(new raw_fd_ostream(ObjectFilePath, EC, sys::fs::F_None)); + if (EC) + return error("Unable to create file for " + ObjectFilePath + ": " + + EC.message()); + + RegisterInfo.reset(TheTarget->createMCRegInfo(TripleName)); + if (!RegisterInfo) + return error("Unable to create target register info!"); + + AsmInfo.reset(TheTarget->createMCAsmInfo(*RegisterInfo, TripleName, TargetMOptions)); + if (!AsmInfo) + return error("Unable to create target asm info!"); + + ObjFileInfo.reset(new MCObjectFileInfo); + OutContext.reset( + new MCContext(AsmInfo.get(), RegisterInfo.get(), ObjFileInfo.get())); + ObjFileInfo->InitMCObjectFileInfo(TheTriple, false, + *OutContext); + + InstrInfo.reset(TheTarget->createMCInstrInfo()); + if (!InstrInfo) + return error("no instr info info for target " + TripleName); + + std::string FeaturesStr; + std::string MCPU; + SubtargetInfo.reset( + TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); + if (!SubtargetInfo) + return error("no subtarget info for target " + TripleName); + + CodeEmitter = + TheTarget->createMCCodeEmitter(*InstrInfo, *RegisterInfo, *OutContext); + if (!CodeEmitter) + return error("no code emitter for target " + TripleName); + + AsmBackend = TheTarget->createMCAsmBackend(*SubtargetInfo, *RegisterInfo, TargetMOptions); + if (!AsmBackend) + return error("no asm backend for target " + TripleName); + + Streamer = (MCObjectStreamer *)TheTarget->createMCObjectStreamer( + TheTriple, *OutContext, std::unique_ptr(AsmBackend), AsmBackend->createObjectWriter(*OS), + std::unique_ptr(CodeEmitter), *SubtargetInfo, + /*RelaxAll*/ true, + /*IncrementalLinkerCompatible*/ false, + /*DWARFMustBeAtTheEnd*/ false); + if (!Streamer) + return error("no object streamer for target " + TripleName); + Assembler = &Streamer->getAssembler(); + + FrameOpened = false; + FuncId = 1; + + SetCodeSectionAttribute("text", CustomSectionAttributes_Executable, nullptr); + + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + TypeBuilder.reset(new UserDefinedCodeViewTypesBuilder()); + } else { + TypeBuilder.reset(new UserDefinedDwarfTypesBuilder()); + } + + TypeBuilder->SetStreamer(Streamer); + unsigned TargetPointerSize = Streamer->getContext().getAsmInfo()->getCodePointerSize(); + TypeBuilder->SetTargetPointerSize(TargetPointerSize); + + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsELF) { + DwarfGenerator.reset(new DwarfGen()); + DwarfGenerator->SetTypeBuilder(static_cast(TypeBuilder.get())); + } + + CFIsPerOffset.set_size(0); + + return true; +} + +void ObjectWriter::Finish() { + + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF + && AddressTakenFunctions.size() > 0) { + + // Emit all address-taken functions into the GFIDs section + // to support control flow guard. + Streamer->SwitchSection(ObjFileInfo->getGFIDsSection()); + for (const MCSymbol* S : AddressTakenFunctions) { + Streamer->EmitCOFFSymbolIndex(S); + } + + // Emit the feat.00 symbol that controls various linker behaviors + MCSymbol* S = OutContext->getOrCreateSymbol(StringRef("@feat.00")); + Streamer->BeginCOFFSymbolDef(S); + Streamer->EmitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_STATIC); + Streamer->EmitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_NULL); + Streamer->EndCOFFSymbolDef(); + int64_t Feat00Flags = 0; + + Feat00Flags |= 0x800; // cfGuardCF flags this object as control flow guard aware + + Streamer->emitSymbolAttribute(S, MCSA_Global); + Streamer->emitAssignment( + S, MCConstantExpr::create(Feat00Flags, *OutContext)); + } + + Streamer->Finish(); +} + +void ObjectWriter::SwitchSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + MCSection *Section = GetSection(SectionName, attributes, ComdatName); + Streamer->SwitchSection(Section); + if (Sections.count(Section) == 0) { + Sections.insert(Section); + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsMachO) { + assert(!Section->getBeginSymbol()); + // Output a DWARF linker-local symbol. + // This symbol is used as a base for other symbols in a section. + MCSymbol *SectionStartSym = OutContext->createTempSymbol(); + Streamer->emitLabel(SectionStartSym); + Section->setBeginSymbol(SectionStartSym); + } + } +} + +MCSection *ObjectWriter::GetSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + MCSection *Section = nullptr; + + if (strcmp(SectionName, "text") == 0) { + Section = ObjFileInfo->getTextSection(); + } else if (strcmp(SectionName, "data") == 0) { + Section = ObjFileInfo->getDataSection(); + } else if (strcmp(SectionName, "rdata") == 0) { + Section = ObjFileInfo->getReadOnlySection(); + } else if (strcmp(SectionName, "xdata") == 0) { + Section = ObjFileInfo->getXDataSection(); + } else if (strcmp(SectionName, "bss") == 0) { + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsMachO) { + Section = ObjFileInfo->getDataBSSSection(); + } else { + Section = ObjFileInfo->getBSSSection(); + } + } else { + Section = GetSpecificSection(SectionName, attributes, ComdatName); + } + assert(Section); + return Section; +} + +MCSection *ObjectWriter::GetSpecificSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + Triple TheTriple(TripleName); + MCSection *Section = nullptr; + SectionKind Kind = (attributes & CustomSectionAttributes_Executable) + ? SectionKind::getText() + : (attributes & CustomSectionAttributes_Writeable) + ? SectionKind::getData() + : SectionKind::getReadOnly(); + switch (TheTriple.getObjectFormat()) { + case Triple::MachO: { + unsigned typeAndAttributes = 0; + if (attributes & CustomSectionAttributes_MachO_Init_Func_Pointers) { + typeAndAttributes |= MachO::SectionType::S_MOD_INIT_FUNC_POINTERS; + } + Section = OutContext->getMachOSection( + (attributes & CustomSectionAttributes_Executable) ? "__TEXT" : "__DATA", + SectionName, typeAndAttributes, Kind); + break; + } + case Triple::COFF: { + unsigned Characteristics = COFF::IMAGE_SCN_MEM_READ; + + if (attributes & CustomSectionAttributes_Executable) { + Characteristics |= COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE; + } else if (attributes & CustomSectionAttributes_Writeable) { + Characteristics |= + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_WRITE; + } else { + Characteristics |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; + } + + if (ComdatName != nullptr) { + Section = OutContext->getCOFFSection( + SectionName, Characteristics | COFF::IMAGE_SCN_LNK_COMDAT, Kind, + ComdatName, COFF::COMDATType::IMAGE_COMDAT_SELECT_ANY); + } else { + Section = OutContext->getCOFFSection(SectionName, Characteristics, Kind); + } + break; + } + case Triple::ELF: { + unsigned Flags = ELF::SHF_ALLOC; + if (ComdatName != nullptr) { + MCSymbolELF *GroupSym = + cast(OutContext->getOrCreateSymbol(ComdatName)); + OutContext->createELFGroupSection(GroupSym); + Flags |= ELF::SHF_GROUP; + } + if (attributes & CustomSectionAttributes_Executable) { + Flags |= ELF::SHF_EXECINSTR; + } else if (attributes & CustomSectionAttributes_Writeable) { + Flags |= ELF::SHF_WRITE; + } + Section = + OutContext->getELFSection(SectionName, ELF::SHT_PROGBITS, Flags, 0, + ComdatName != nullptr ? ComdatName : ""); + break; + } + default: + error("Unknown output format for target " + TripleName); + break; + } + return Section; +} + +void ObjectWriter::SetCodeSectionAttribute(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + MCSection *Section = GetSection(SectionName, attributes, ComdatName); + + assert(!Section->hasInstructions()); + Section->setHasInstructions(true); + if (ObjFileInfo->getObjectFileType() != ObjFileInfo->IsCOFF) { + OutContext->addGenDwarfSection(Section); + } +} + +void ObjectWriter::EmitAlignment(int ByteAlignment) { + int64_t fillValue = 0; + + if (Streamer->getCurrentSectionOnly()->getKind().isText()) { + if (ObjFileInfo->getTargetTriple().getArch() == llvm::Triple::ArchType::x86 || + ObjFileInfo->getTargetTriple().getArch() == llvm::Triple::ArchType::x86_64) { + fillValue = 0x90; // x86 nop + } + } + + Streamer->emitValueToAlignment(ByteAlignment, fillValue); +} + +void ObjectWriter::EmitBlob(int BlobSize, const char *Blob) { + if (Streamer->getCurrentSectionOnly()->getKind().isText()) { + Streamer->emitInstructionBytes(StringRef(Blob, BlobSize)); + } else { + Streamer->emitBytes(StringRef(Blob, BlobSize)); + } +} + +void ObjectWriter::EmitIntValue(uint64_t Value, unsigned Size) { + Streamer->emitIntValue(Value, Size); +} + +void ObjectWriter::EmitSymbolDef(const char *SymbolName, bool global) { + MCSymbol *Sym = OutContext->getOrCreateSymbol(Twine(SymbolName)); + + Streamer->emitSymbolAttribute(Sym, MCSA_Global); + + Triple TheTriple = ObjFileInfo->getTargetTriple(); + + if (TheTriple.getObjectFormat() == Triple::ELF) { + // An ARM function symbol should be marked with an appropriate ELF attribute + // to make later computation of a relocation address value correct + if (Streamer->getCurrentSectionOnly()->getKind().isText()) { + switch (TheTriple.getArch()) { + case Triple::arm: + case Triple::armeb: + case Triple::thumb: + case Triple::thumbeb: + case Triple::aarch64: + case Triple::aarch64_be: + Streamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeFunction); + break; + default: + break; + } + } + + // Mark the symbol hidden if requested + if (!global) { + Streamer->emitSymbolAttribute(Sym, MCSA_Hidden); + } + } + + Streamer->emitLabel(Sym); +} + +const MCSymbolRefExpr * +ObjectWriter::GetSymbolRefExpr(const char *SymbolName, + MCSymbolRefExpr::VariantKind Kind) { + // Create symbol reference + MCSymbol *T = OutContext->getOrCreateSymbol(SymbolName); + Assembler->registerSymbol(*T); + return MCSymbolRefExpr::create(T, Kind, *OutContext); +} + +unsigned ObjectWriter::GetDFSize() { + return Streamer->getOrCreateDataFragment()->getContents().size(); +} + +void ObjectWriter::EmitRelocDirective(const int Offset, StringRef Name, const MCExpr *Expr) { + const MCExpr *OffsetExpr = MCConstantExpr::create(Offset, *OutContext); + Optional> result = Streamer->emitRelocDirective(*OffsetExpr, Name, Expr, SMLoc(), *SubtargetInfo); + assert(!result.hasValue()); +} + +const MCExpr *ObjectWriter::GenTargetExpr(const MCSymbol* Symbol, MCSymbolRefExpr::VariantKind Kind, + int Delta, bool IsPCRel, int Size) { + const MCExpr *TargetExpr = MCSymbolRefExpr::create(Symbol, Kind, *OutContext); + if (IsPCRel && Size != 0) { + // If the fixup is pc-relative, we need to bias the value to be relative to + // the start of the field, not the end of the field + TargetExpr = MCBinaryExpr::createSub( + TargetExpr, MCConstantExpr::create(Size, *OutContext), *OutContext); + } + if (Delta != 0) { + TargetExpr = MCBinaryExpr::createAdd( + TargetExpr, MCConstantExpr::create(Delta, *OutContext), *OutContext); + } + return TargetExpr; +} + +int ObjectWriter::EmitSymbolRef(const char *SymbolName, + RelocType RelocationType, int Delta, SymbolRefFlags Flags) { + bool IsPCRel = false; + int Size = 0; + MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None; + + MCSymbol* Symbol = OutContext->getOrCreateSymbol(SymbolName); + Assembler->registerSymbol(*Symbol); + + if ((int)Flags & (int)SymbolRefFlags::SymbolRefFlags_AddressTakenFunction) { + AddressTakenFunctions.insert(Symbol); + } + + // Convert RelocationType to MCSymbolRefExpr + switch (RelocationType) { + case RelocType::IMAGE_REL_BASED_ABSOLUTE: + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF); + Kind = MCSymbolRefExpr::VK_COFF_IMGREL32; + Size = 4; + break; + case RelocType::IMAGE_REL_BASED_HIGHLOW: + Size = 4; + break; + case RelocType::IMAGE_REL_BASED_DIR64: + Size = 8; + break; + case RelocType::IMAGE_REL_BASED_REL32: + Size = 4; + IsPCRel = true; + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsELF) { + // PLT is valid only for code symbols, + // but there shouldn't be references to global data symbols + Kind = MCSymbolRefExpr::VK_PLT; + } + break; + case RelocType::IMAGE_REL_BASED_RELPTR32: + Size = 4; + IsPCRel = true; + Delta += 4; + break; + case RelocType::IMAGE_REL_BASED_THUMB_MOV32: { + const unsigned Offset = GetDFSize(); + const MCExpr *TargetExpr = GenTargetExpr(Symbol, Kind, Delta); + EmitRelocDirective(Offset, "R_ARM_THM_MOVW_ABS_NC", TargetExpr); + EmitRelocDirective(Offset + 4, "R_ARM_THM_MOVT_ABS", TargetExpr); + return 8; + } + case RelocType::IMAGE_REL_BASED_THUMB_BRANCH24: { + const MCExpr *TargetExpr = GenTargetExpr(Symbol, Kind, Delta); + EmitRelocDirective(GetDFSize(), "R_ARM_THM_CALL", TargetExpr); + return 4; + } + case RelocType::IMAGE_REL_BASED_ARM64_BRANCH26: { + const MCExpr *TargetExpr = GenTargetExpr(Symbol, Kind, Delta); + EmitRelocDirective(GetDFSize(), "R_AARCH64_CALL26", TargetExpr); + return 4; + } + case RelocType::IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: { + const MCExpr *TargetExpr = GenTargetExpr(Symbol, Kind, Delta); + TargetExpr = + AArch64MCExpr::create(TargetExpr, AArch64MCExpr::VK_CALL, *OutContext); + EmitRelocDirective(GetDFSize(), "R_AARCH64_ADR_PREL_PG_HI21", TargetExpr); + return 4; + } + case RelocType::IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: { + const MCExpr *TargetExpr = GenTargetExpr(Symbol, Kind, Delta); + TargetExpr = + AArch64MCExpr::create(TargetExpr, AArch64MCExpr::VK_LO12, *OutContext); + EmitRelocDirective(GetDFSize(), "R_AARCH64_ADD_ABS_LO12_NC", TargetExpr); + return 4; + } + } + + const MCExpr *TargetExpr = GenTargetExpr(Symbol, Kind, Delta, IsPCRel, Size); + Streamer->emitValueImpl(TargetExpr, Size, SMLoc(), IsPCRel); + return Size; +} + +void ObjectWriter::EmitWinFrameInfo(const char *FunctionName, int StartOffset, + int EndOffset, const char *BlobSymbolName) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF); + + // .pdata emission + MCSection *Section = ObjFileInfo->getPDataSection(); + + // If the function was emitted to a Comdat section, create an associative + // section to place the frame info in. This is due to the Windows linker + // requirement that a function and its unwind info come from the same + // object file. + MCSymbol *Fn = OutContext->getOrCreateSymbol(Twine(FunctionName)); + const MCSectionCOFF *FunctionSection = cast(&Fn->getSection()); + if (FunctionSection->getCharacteristics() & COFF::IMAGE_SCN_LNK_COMDAT) { + Section = OutContext->getAssociativeCOFFSection( + cast(Section), FunctionSection->getCOMDATSymbol()); + } + + Streamer->SwitchSection(Section); + Streamer->emitValueToAlignment(4); + + const MCExpr *BaseRefRel = + GetSymbolRefExpr(FunctionName, MCSymbolRefExpr::VK_COFF_IMGREL32); + + Triple::ArchType Arch = ObjFileInfo->getTargetTriple().getArch(); + + if (Arch == Triple::thumb || Arch == Triple::thumbeb) { + StartOffset |= 1; + } + + // start Offset + const MCExpr *StartOfs = MCConstantExpr::create(StartOffset, *OutContext); + Streamer->emitValue( + MCBinaryExpr::createAdd(BaseRefRel, StartOfs, *OutContext), 4); + + if (Arch == Triple::x86 || Arch == Triple::x86_64) { + // end Offset + const MCExpr *EndOfs = MCConstantExpr::create(EndOffset, *OutContext); + Streamer->emitValue( + MCBinaryExpr::createAdd(BaseRefRel, EndOfs, *OutContext), 4); + } + + // frame symbol reference + Streamer->emitValue( + GetSymbolRefExpr(BlobSymbolName, MCSymbolRefExpr::VK_COFF_IMGREL32), 4); +} + +void ObjectWriter::EmitCFIStart(int Offset) { + assert(!FrameOpened && "frame should be closed before CFIStart"); + Streamer->emitCFIStartProc(false); + FrameOpened = true; +} + +void ObjectWriter::EmitCFIEnd(int Offset) { + assert(FrameOpened && "frame should be opened before CFIEnd"); + Streamer->emitCFIEndProc(); + FrameOpened = false; +} + +void ObjectWriter::EmitCFILsda(const char *LsdaBlobSymbolName) { + assert(FrameOpened && "frame should be opened before CFILsda"); + + // Create symbol reference + MCSymbol *T = OutContext->getOrCreateSymbol(LsdaBlobSymbolName); + Assembler->registerSymbol(*T); + Streamer->emitCFILsda(T, llvm::dwarf::Constants::DW_EH_PE_pcrel | + llvm::dwarf::Constants::DW_EH_PE_sdata4); +} + +void ObjectWriter::EmitCFICode(int Offset, const char *Blob) { + assert(FrameOpened && "frame should be opened before CFICode"); + + const CFI_CODE *CfiCode = (const CFI_CODE *)Blob; + switch (CfiCode->CfiOpCode) { + case CFI_ADJUST_CFA_OFFSET: + assert(CfiCode->DwarfReg == DWARF_REG_ILLEGAL && + "Unexpected Register Value for OpAdjustCfaOffset"); + Streamer->emitCFIAdjustCfaOffset(CfiCode->Offset); + break; + case CFI_REL_OFFSET: + Streamer->emitCFIRelOffset(CfiCode->DwarfReg, CfiCode->Offset); + break; + case CFI_DEF_CFA_REGISTER: + assert(CfiCode->Offset == 0 && + "Unexpected Offset Value for OpDefCfaRegister"); + Streamer->emitCFIDefCfaRegister(CfiCode->DwarfReg); + break; + case CFI_DEF_CFA: + assert(CfiCode->Offset != 0 && + "Unexpected Offset Value for OpDefCfa"); + Streamer->emitCFIDefCfa(CfiCode->DwarfReg, CfiCode->Offset); + break; + default: + assert(false && "Unrecognized CFI"); + break; + } +} + +void ObjectWriter::EmitLabelDiff(const MCSymbol *From, const MCSymbol *To, + unsigned int Size) { + MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None; + const MCExpr *FromRef = MCSymbolRefExpr::create(From, Variant, *OutContext), + *ToRef = MCSymbolRefExpr::create(To, Variant, *OutContext); + const MCExpr *AddrDelta = + MCBinaryExpr::create(MCBinaryExpr::Sub, ToRef, FromRef, *OutContext); + Streamer->emitValue(AddrDelta, Size); +} + +void ObjectWriter::EmitSymRecord(int Size, SymbolRecordKind SymbolKind) { + RecordPrefix Rec; + Rec.RecordLen = ulittle16_t(Size + sizeof(ulittle16_t)); + Rec.RecordKind = ulittle16_t((uint16_t)SymbolKind); + Streamer->emitBytes(StringRef((char *)&Rec, sizeof(Rec))); +} + +void ObjectWriter::EmitCOFFSecRel32Value(MCExpr const *Value) { + MCDataFragment *DF = Streamer->getOrCreateDataFragment(); + MCFixup Fixup = MCFixup::create(DF->getContents().size(), Value, FK_SecRel_4); + DF->getFixups().push_back(Fixup); + DF->getContents().resize(DF->getContents().size() + 4, 0); +} + +void ObjectWriter::EmitVarDefRange(const MCSymbol *Fn, + const LocalVariableAddrRange &Range) { + const MCSymbolRefExpr *BaseSym = MCSymbolRefExpr::create(Fn, *OutContext); + const MCExpr *Offset = MCConstantExpr::create(Range.OffsetStart, *OutContext); + const MCExpr *Expr = MCBinaryExpr::createAdd(BaseSym, Offset, *OutContext); + EmitCOFFSecRel32Value(Expr); + Streamer->EmitCOFFSectionIndex(Fn); + Streamer->emitIntValue(Range.Range, 2); +} + +// Maps an ICorDebugInfo register number to the corresponding CodeView +// register number +CVRegNum ObjectWriter::GetCVRegNum(unsigned RegNum) { + static const CVRegNum CVRegMapAmd64[] = { + CV_AMD64_RAX, CV_AMD64_RCX, CV_AMD64_RDX, CV_AMD64_RBX, + CV_AMD64_RSP, CV_AMD64_RBP, CV_AMD64_RSI, CV_AMD64_RDI, + CV_AMD64_R8, CV_AMD64_R9, CV_AMD64_R10, CV_AMD64_R11, + CV_AMD64_R12, CV_AMD64_R13, CV_AMD64_R14, CV_AMD64_R15, + }; + + switch (ObjFileInfo->getTargetTriple().getArch()) { + case Triple::x86: + if (X86::ICorDebugInfo::REGNUM_EAX <= RegNum && + RegNum <= X86::ICorDebugInfo::REGNUM_EDI) { + return RegNum - X86::ICorDebugInfo::REGNUM_EAX + CV_REG_EAX; + } + break; + case Triple::x86_64: + if (RegNum < sizeof(CVRegMapAmd64) / sizeof(CVRegMapAmd64[0])) { + return CVRegMapAmd64[RegNum]; + } + break; + case Triple::arm: + case Triple::armeb: + case Triple::thumb: + case Triple::thumbeb: + if (Arm::ICorDebugInfo::REGNUM_R0 <= RegNum && + RegNum <= Arm::ICorDebugInfo::REGNUM_PC) { + return RegNum - Arm::ICorDebugInfo::REGNUM_R0 + CV_ARM_R0; + } + break; + case Triple::aarch64: + case Triple::aarch64_be: + if (Arm64::ICorDebugInfo::REGNUM_X0 <= RegNum && + RegNum < Arm64::ICorDebugInfo::REGNUM_PC) { + return RegNum - Arm64::ICorDebugInfo::REGNUM_X0 + CV_ARM64_X0; + } + // Special registers are ordered FP, LR, SP, PC in ICorDebugInfo's + // enumeration and FP, LR, SP, *ZR*, PC in CodeView's enumeration. + // For that reason handle the PC register separately. + if (RegNum == Arm64::ICorDebugInfo::REGNUM_PC) { + return CV_ARM64_PC; + } + break; + default: + assert(false && "Unexpected architecture"); + break; + } + return CV_REG_NONE; +} + +void ObjectWriter::EmitCVDebugVarInfo(const MCSymbol *Fn, + const DebugVarInfo LocInfos[], + int NumVarInfos) { + for (int I = 0; I < NumVarInfos; I++) { + // Emit an S_LOCAL record + DebugVarInfo Var = LocInfos[I]; + TypeIndex Type = TypeIndex(Var.TypeIndex); + LocalSymFlags Flags = LocalSymFlags::None; + unsigned SizeofSym = sizeof(Type) + sizeof(Flags); + unsigned NameLength = Var.Name.length() + 1; + EmitSymRecord(SizeofSym + NameLength, SymbolRecordKind::LocalSym); + if (Var.IsParam) { + Flags |= LocalSymFlags::IsParameter; + } + Streamer->emitBytes(StringRef((char *)&Type, sizeof(Type))); + Streamer->emitIntValue(static_cast(Flags), sizeof(Flags)); + Streamer->emitBytes(StringRef(Var.Name.c_str(), NameLength)); + + for (const auto &Range : Var.Ranges) { + // Emit a range record + switch (Range.loc.vlType) { + case ICorDebugInfo::VLT_REG: + case ICorDebugInfo::VLT_REG_FP: { + + // Currently only support integer registers. + // TODO: support xmm registers + CVRegNum CVReg = GetCVRegNum(Range.loc.vlReg.vlrReg); + if (CVReg == CV_REG_NONE) { + break; + } + SymbolRecordKind SymbolKind = SymbolRecordKind::DefRangeRegisterSym; + unsigned SizeofDefRangeRegisterSym = sizeof(DefRangeRegisterSym::Hdr) + + sizeof(DefRangeRegisterSym::Range); + EmitSymRecord(SizeofDefRangeRegisterSym, SymbolKind); + + DefRangeRegisterSym DefRangeRegisterSymbol(SymbolKind); + DefRangeRegisterSymbol.Range.OffsetStart = Range.startOffset; + DefRangeRegisterSymbol.Range.Range = + Range.endOffset - Range.startOffset; + DefRangeRegisterSymbol.Range.ISectStart = 0; + DefRangeRegisterSymbol.Hdr.Register = CVReg; + DefRangeRegisterSymbol.Hdr.MayHaveNoName = 0; + + unsigned Length = sizeof(DefRangeRegisterSymbol.Hdr); + Streamer->emitBytes( + StringRef((char *)&DefRangeRegisterSymbol.Hdr, Length)); + EmitVarDefRange(Fn, DefRangeRegisterSymbol.Range); + break; + } + + case ICorDebugInfo::VLT_STK: { + + // TODO: support REGNUM_AMBIENT_SP + CVRegNum CVReg = GetCVRegNum(Range.loc.vlStk.vlsBaseReg); + if (CVReg == CV_REG_NONE) { + break; + } + + SymbolRecordKind SymbolKind = SymbolRecordKind::DefRangeRegisterRelSym; + unsigned SizeofDefRangeRegisterRelSym = + sizeof(DefRangeRegisterRelSym::Hdr) + + sizeof(DefRangeRegisterRelSym::Range); + EmitSymRecord(SizeofDefRangeRegisterRelSym, SymbolKind); + + DefRangeRegisterRelSym DefRangeRegisterRelSymbol(SymbolKind); + DefRangeRegisterRelSymbol.Range.OffsetStart = Range.startOffset; + DefRangeRegisterRelSymbol.Range.Range = + Range.endOffset - Range.startOffset; + DefRangeRegisterRelSymbol.Range.ISectStart = 0; + DefRangeRegisterRelSymbol.Hdr.Register = CVReg; + DefRangeRegisterRelSymbol.Hdr.Flags = 0; + DefRangeRegisterRelSymbol.Hdr.BasePointerOffset = + Range.loc.vlStk.vlsOffset; + + unsigned Length = sizeof(DefRangeRegisterRelSymbol.Hdr); + Streamer->emitBytes( + StringRef((char *)&DefRangeRegisterRelSymbol.Hdr, Length)); + EmitVarDefRange(Fn, DefRangeRegisterRelSymbol.Range); + break; + } + + case ICorDebugInfo::VLT_REG_BYREF: + case ICorDebugInfo::VLT_STK_BYREF: + case ICorDebugInfo::VLT_REG_REG: + case ICorDebugInfo::VLT_REG_STK: + case ICorDebugInfo::VLT_STK_REG: + case ICorDebugInfo::VLT_STK2: + case ICorDebugInfo::VLT_FPSTK: + case ICorDebugInfo::VLT_FIXED_VA: + // TODO: for optimized debugging + break; + + default: + assert(false && "Unknown varloc type!"); + break; + } + } + } +} + +void ObjectWriter::EmitCVDebugFunctionInfo(const char *FunctionName, + int FunctionSize) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF); + + // Mark the end of function. + MCSymbol *FnEnd = OutContext->createTempSymbol(); + Streamer->emitLabel(FnEnd); + + MCSection *Section = ObjFileInfo->getCOFFDebugSymbolsSection(); + Streamer->SwitchSection(Section); + // Emit debug section magic before the first entry. + if (FuncId == 1) { + Streamer->emitIntValue(COFF::DEBUG_SECTION_MAGIC, 4); + } + MCSymbol *Fn = OutContext->getOrCreateSymbol(Twine(FunctionName)); + + // Emit a symbol subsection, required by VS2012+ to find function boundaries. + MCSymbol *SymbolsBegin = OutContext->createTempSymbol(), + *SymbolsEnd = OutContext->createTempSymbol(); + Streamer->emitIntValue(unsigned(DebugSubsectionKind::Symbols), 4); + EmitLabelDiff(SymbolsBegin, SymbolsEnd); + Streamer->emitLabel(SymbolsBegin); + { + ProcSym ProcSymbol(SymbolRecordKind::GlobalProcIdSym); + ProcSymbol.CodeSize = FunctionSize; + ProcSymbol.DbgEnd = FunctionSize; + + unsigned FunctionNameLength = strlen(FunctionName) + 1; + unsigned HeaderSize = + sizeof(ProcSymbol.Parent) + sizeof(ProcSymbol.End) + + sizeof(ProcSymbol.Next) + sizeof(ProcSymbol.CodeSize) + + sizeof(ProcSymbol.DbgStart) + sizeof(ProcSymbol.DbgEnd) + + sizeof(ProcSymbol.FunctionType); + unsigned SymbolSize = HeaderSize + 4 + 2 + 1 + FunctionNameLength; + EmitSymRecord(SymbolSize, SymbolRecordKind::GlobalProcIdSym); + + Streamer->emitBytes(StringRef((char *)&ProcSymbol.Parent, HeaderSize)); + // Emit relocation + Streamer->EmitCOFFSecRel32(Fn, 0); + Streamer->EmitCOFFSectionIndex(Fn); + + // Emit flags + Streamer->emitIntValue(0, 1); + + // Emit the function display name as a null-terminated string. + + Streamer->emitBytes(StringRef(FunctionName, FunctionNameLength)); + + // Emit local var info + int NumVarInfos = DebugVarInfos.size(); + if (NumVarInfos > 0) { + EmitCVDebugVarInfo(Fn, &DebugVarInfos[0], NumVarInfos); + DebugVarInfos.clear(); + } + + // We're done with this function. + EmitSymRecord(0, SymbolRecordKind::ProcEnd); + } + + Streamer->emitLabel(SymbolsEnd); + + // Every subsection must be aligned to a 4-byte boundary. + Streamer->emitValueToAlignment(4); + + // We have an assembler directive that takes care of the whole line table. + // We also increase function id for the next function. + Streamer->emitCVLinetableDirective(FuncId++, Fn, FnEnd); +} + +void ObjectWriter::EmitDwarfFunctionInfo(const char *FunctionName, + int FunctionSize, + unsigned MethodTypeIndex) { + if (FuncId == 1) { + DwarfGenerator->EmitCompileUnit(); + } + + DwarfGenerator->EmitSubprogramInfo(FunctionName, FunctionSize, + MethodTypeIndex, DebugVarInfos, DebugEHClauseInfos); + + DebugVarInfos.clear(); + DebugEHClauseInfos.clear(); + + FuncId++; +} + +void ObjectWriter::EmitDebugFileInfo(int FileId, const char *FileName) { + assert(FileId > 0 && "FileId should be greater than 0."); + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + // TODO: we could pipe through the checksum and hash algorithm from the managed PDB + ArrayRef ChecksumAsBytes; + Streamer->EmitCVFileDirective(FileId, FileName, ChecksumAsBytes, 0); + } else { + Streamer->emitDwarfFileDirective(FileId, "", FileName); + } +} + +void ObjectWriter::EmitDebugFunctionInfo(const char *FunctionName, + int FunctionSize, + unsigned MethodTypeIndex) { + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + Streamer->EmitCVFuncIdDirective(FuncId); + EmitCVDebugFunctionInfo(FunctionName, FunctionSize); + } else { + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsELF) { + MCSymbol *Sym = OutContext->getOrCreateSymbol(Twine(FunctionName)); + Streamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeFunction); + Streamer->emitELFSize(Sym, + MCConstantExpr::create(FunctionSize, *OutContext)); + EmitDwarfFunctionInfo(FunctionName, FunctionSize, MethodTypeIndex); + } + // TODO: Should test it for Macho. + } +} + +void ObjectWriter::EmitDebugVar(char *Name, int TypeIndex, bool IsParm, + int RangeCount, + const ICorDebugInfo::NativeVarInfo *Ranges) { + assert(RangeCount != 0); + DebugVarInfo NewVar(Name, TypeIndex, IsParm); + + for (int I = 0; I < RangeCount; I++) { + assert(Ranges[0].varNumber == Ranges[I].varNumber); + NewVar.Ranges.push_back(Ranges[I]); + } + + DebugVarInfos.push_back(NewVar); +} + +void ObjectWriter::EmitDebugEHClause(unsigned TryOffset, unsigned TryLength, + unsigned HandlerOffset, unsigned HandlerLength) { + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsELF) { + DebugEHClauseInfos.emplace_back(TryOffset, TryLength, HandlerOffset, HandlerLength); + } +} + +void ObjectWriter::EmitDebugLoc(int NativeOffset, int FileId, int LineNumber, + int ColNumber) { + assert(FileId > 0 && "FileId should be greater than 0."); + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + Streamer->EmitCVFuncIdDirective(FuncId); + Streamer->emitCVLocDirective(FuncId, FileId, LineNumber, ColNumber, false, + true, "", SMLoc()); + } else { + Streamer->emitDwarfLocDirective(FileId, LineNumber, ColNumber, 1, 0, 0, ""); + } +} + +void ObjectWriter::EmitCVUserDefinedTypesSymbols() { + const auto &UDTs = TypeBuilder->GetUDTs(); + if (UDTs.empty()) { + return; + } + MCSection *Section = ObjFileInfo->getCOFFDebugSymbolsSection(); + Streamer->SwitchSection(Section); + + MCSymbol *SymbolsBegin = OutContext->createTempSymbol(), + *SymbolsEnd = OutContext->createTempSymbol(); + Streamer->emitIntValue(unsigned(DebugSubsectionKind::Symbols), 4); + EmitLabelDiff(SymbolsBegin, SymbolsEnd); + Streamer->emitLabel(SymbolsBegin); + + for (const std::pair &UDT : UDTs) { + unsigned NameLength = UDT.first.length() + 1; + unsigned RecordLength = 2 + 4 + NameLength; + Streamer->emitIntValue(RecordLength, 2); + Streamer->emitIntValue(unsigned(SymbolKind::S_UDT), 2); + Streamer->emitIntValue(UDT.second, 4); + Streamer->emitBytes(StringRef(UDT.first.c_str(), NameLength)); + } + Streamer->emitLabel(SymbolsEnd); + Streamer->emitValueToAlignment(4); +} + +void ObjectWriter::EmitDebugModuleInfo() { + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + TypeBuilder->EmitTypeInformation(ObjFileInfo->getCOFFDebugTypesSection()); + EmitCVUserDefinedTypesSymbols(); + } + + // Ensure ending all sections. + for (auto Section : Sections) { + Streamer->endSection(Section); + } + + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + MCSection *Section = ObjFileInfo->getCOFFDebugSymbolsSection(); + Streamer->SwitchSection(Section); + Streamer->emitCVFileChecksumsDirective(); + Streamer->emitCVStringTableDirective(); + } else if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsELF) { + DwarfGenerator->EmitAbbrev(); + DwarfGenerator->EmitAranges(); + DwarfGenerator->Finish(); + } else { + OutContext->setGenDwarfForAssembly(true); + } +} + +unsigned +ObjectWriter::GetEnumTypeIndex(const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords) { + return TypeBuilder->GetEnumTypeIndex(TypeDescriptor, TypeRecords); +} + +unsigned +ObjectWriter::GetClassTypeIndex(const ClassTypeDescriptor &ClassDescriptor) { + unsigned res = TypeBuilder->GetClassTypeIndex(ClassDescriptor); + return res; +} + +unsigned ObjectWriter::GetCompleteClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors, + const StaticDataFieldDescriptor *StaticsDescriptors) { + unsigned res = TypeBuilder->GetCompleteClassTypeIndex(ClassDescriptor, + ClassFieldsDescriptor, FieldsDescriptors, StaticsDescriptors); + return res; +} + +unsigned +ObjectWriter::GetArrayTypeIndex(const ClassTypeDescriptor &ClassDescriptor, + const ArrayTypeDescriptor &ArrayDescriptor) { + return TypeBuilder->GetArrayTypeIndex(ClassDescriptor, ArrayDescriptor); +} + +unsigned +ObjectWriter::GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor) { + return TypeBuilder->GetPointerTypeIndex(PointerDescriptor); +} + +unsigned +ObjectWriter::GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes) { + return TypeBuilder->GetMemberFunctionTypeIndex(MemberDescriptor, ArgumentTypes); +} + +unsigned +ObjectWriter::GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor) { + return TypeBuilder->GetMemberFunctionId(MemberIdDescriptor); +} + +unsigned +ObjectWriter::GetPrimitiveTypeIndex(int Type) { + return TypeBuilder->GetPrimitiveTypeIndex(static_cast(Type)); +} + +void +ObjectWriter::EmitARMFnStart() { + MCTargetStreamer &TS = *(Streamer->getTargetStreamer()); + ARMTargetStreamer &ATS = static_cast(TS); + + ATS.emitFnStart(); +} + +void ObjectWriter::EmitARMFnEnd() { + + if (!CFIsPerOffset.empty()) + { + EmitARMExIdxPerOffset(); + } + + MCTargetStreamer &TS = *(Streamer->getTargetStreamer()); + ARMTargetStreamer &ATS = static_cast(TS); + + ATS.emitFnEnd(); +} + +void ObjectWriter::EmitARMExIdxLsda(const char *LsdaBlobSymbolName) +{ + MCTargetStreamer &TS = *(Streamer->getTargetStreamer()); + ARMTargetStreamer &ATS = static_cast(TS); + + MCSymbol *T = OutContext->getOrCreateSymbol(LsdaBlobSymbolName); + Assembler->registerSymbol(*T); + + ATS.emitLsda(T); +} + +void ObjectWriter::EmitARMExIdxPerOffset() +{ + MCTargetStreamer &TS = *(Streamer->getTargetStreamer()); + ARMTargetStreamer &ATS = static_cast(TS); + const MCRegisterInfo *MRI = OutContext->getRegisterInfo(); + + SmallVector RegSet; + bool IsVector = false; + + // LLVM reverses opcodes that are fed to ARMTargetStreamer, so we do the same, + // but per code offset. Opcodes with different code offsets are already given in + // the correct order. + for (int i = CFIsPerOffset.size() - 1; i >= 0; --i) + { + unsigned char opCode = CFIsPerOffset[i].CfiOpCode; + short Reg = CFIsPerOffset[i].DwarfReg; + + if (RegSet.empty() && opCode == CFI_REL_OFFSET) + { + IsVector = Reg >= 16; + } + else if (!RegSet.empty() && opCode != CFI_REL_OFFSET) + { + ATS.emitRegSave(RegSet, IsVector); + RegSet.clear(); + } + + switch (opCode) + { + case CFI_REL_OFFSET: + assert(IsVector == (Reg >= 16) && "Unexpected Register Type"); + RegSet.push_back(MRI->getLLVMRegNum(Reg, true).getValue()); + break; + case CFI_ADJUST_CFA_OFFSET: + assert(Reg == DWARF_REG_ILLEGAL && + "Unexpected Register Value for OpAdjustCfaOffset"); + ATS.emitPad(CFIsPerOffset[i].Offset); + break; + case CFI_DEF_CFA_REGISTER: + ATS.emitMovSP(MRI->getLLVMRegNum(Reg, true).getValue()); + break; + default: + assert(false && "Unrecognized CFI"); + break; + } + } + + // if we have some registers left over, emit them + if (!RegSet.empty()) + { + ATS.emitRegSave(RegSet, IsVector); + } + + CFIsPerOffset.clear(); +} + +void ObjectWriter::EmitARMExIdxCode(int Offset, const char *Blob) +{ + const CFI_CODE *CfiCode = (const CFI_CODE *)Blob; + + if (!CFIsPerOffset.empty() && CFIsPerOffset[0].CodeOffset != CfiCode->CodeOffset) + { + EmitARMExIdxPerOffset(); + } + + CFIsPerOffset.push_back(*CfiCode); +} diff --git a/src/coreclr/tools/aot/ObjWriter/objwriter.exports b/src/coreclr/tools/aot/ObjWriter/objwriter.exports new file mode 100644 index 00000000000000..40655101ba8d63 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/objwriter.exports @@ -0,0 +1,32 @@ +InitObjWriter +FinishObjWriter +SwitchSection +SetCodeSectionAttribute +EmitAlignment +EmitBlob +EmitIntValue +EmitSymbolDef +EmitSymbolRef +EmitWinFrameInfo +EmitCFIStart +EmitCFIEnd +EmitCFILsda +EmitCFICode +EmitDebugFileInfo +EmitDebugFunctionInfo +EmitDebugVar +EmitDebugEHClause +EmitDebugLoc +EmitDebugModuleInfo +GetEnumTypeIndex +GetClassTypeIndex +GetCompleteClassTypeIndex +GetArrayTypeIndex +GetPointerTypeIndex +GetMemberFunctionTypeIndex +GetMemberFunctionIdTypeIndex +GetPrimitiveTypeIndex +EmitARMFnStart +EmitARMFnEnd +EmitARMExIdxLsda +EmitARMExIdxCode diff --git a/src/coreclr/tools/aot/ObjWriter/objwriter.h b/src/coreclr/tools/aot/ObjWriter/objwriter.h new file mode 100644 index 00000000000000..4840cf7859895a --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/objwriter.h @@ -0,0 +1,397 @@ +//===---- objwriter.h ------------------------------------------*- C++ -*-===// +// +// object writer +// +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" + +#include "cfi.h" +#include "jitDebugInfo.h" +#include "debugInfo/dwarf/dwarfGen.h" + +#include +#include + +using namespace llvm; +using namespace llvm::codeview; + +#ifdef _WIN32 +#define DLL_EXPORT extern "C" __declspec(dllexport) +#else +#define DLL_EXPORT extern "C" __attribute((visibility("default"))) +#endif + +// *** +// Define default call conventions +// *** +#if defined(HOST_X86) && !defined(HOST_UNIX) +#define STDMETHODCALLTYPE __stdcall +#else +#define STDMETHODCALLTYPE +#endif // defined(HOST_X86) && !defined(HOST_UNIX) + +typedef uint16_t CVRegNum; + +enum CustomSectionAttributes : int32_t { + CustomSectionAttributes_ReadOnly = 0x0000, + CustomSectionAttributes_Writeable = 0x0001, + CustomSectionAttributes_Executable = 0x0002, + CustomSectionAttributes_MachO_Init_Func_Pointers = 0x0100, +}; + +enum class RelocType { + IMAGE_REL_BASED_ABSOLUTE = 0x00, + IMAGE_REL_BASED_HIGHLOW = 0x03, + IMAGE_REL_BASED_THUMB_MOV32 = 0x07, + IMAGE_REL_BASED_DIR64 = 0x0A, + IMAGE_REL_BASED_REL32 = 0x10, + IMAGE_REL_BASED_THUMB_BRANCH24 = 0x13, + IMAGE_REL_BASED_ARM64_BRANCH26 = 0x15, + IMAGE_REL_BASED_RELPTR32 = 0x7C, + IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 = 0x81, + IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A = 0x82, +}; + +enum class SymbolRefFlags +{ + SymbolRefFlags_AddressTakenFunction = 0x0001, +}; + +class ObjectWriter { +public: + bool Init(StringRef FunctionName, const char* tripleName = nullptr); + void Finish(); + + void SwitchSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName); + void SetCodeSectionAttribute(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName); + + void EmitAlignment(int ByteAlignment); + void EmitBlob(int BlobSize, const char *Blob); + void EmitIntValue(uint64_t Value, unsigned Size); + void EmitSymbolDef(const char *SymbolName, bool global); + void EmitWinFrameInfo(const char *FunctionName, int StartOffset, + int EndOffset, const char *BlobSymbolName); + int EmitSymbolRef(const char *SymbolName, RelocType RelocType, int Delta, SymbolRefFlags Flags); + + void EmitDebugFileInfo(int FileId, const char *FileName); + void EmitDebugFunctionInfo(const char *FunctionName, int FunctionSize, unsigned MethodTypeIndex); + void EmitDebugVar(char *Name, int TypeIndex, bool IsParm, int RangeCount, + const ICorDebugInfo::NativeVarInfo *Ranges); + void EmitDebugLoc(int NativeOffset, int FileId, int LineNumber, + int ColNumber); + void EmitDebugEHClause(unsigned TryOffset, unsigned TryLength, + unsigned HandlerOffset, unsigned HandlerLength); + void EmitDebugModuleInfo(); + + void EmitCFIStart(int Offset); + void EmitCFIEnd(int Offset); + void EmitCFILsda(const char *LsdaBlobSymbolName); + void EmitCFICode(int Offset, const char *Blob); + + unsigned GetEnumTypeIndex(const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords); + unsigned GetClassTypeIndex(const ClassTypeDescriptor &ClassDescriptor); + unsigned GetCompleteClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors, + const StaticDataFieldDescriptor *StaticsDescriptors); + + unsigned GetArrayTypeIndex(const ClassTypeDescriptor &ClassDescriptor, + const ArrayTypeDescriptor &ArrayDescriptor); + + unsigned GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor); + + unsigned GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes); + + unsigned GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor); + + unsigned GetPrimitiveTypeIndex(int Type); + + void EmitARMFnStart(); + void EmitARMFnEnd(); + void EmitARMExIdxCode(int Offset, const char *Blob); + void EmitARMExIdxLsda(const char *Blob); + +private: + void EmitLabelDiff(const MCSymbol *From, const MCSymbol *To, + unsigned int Size = 4); + void EmitSymRecord(int Size, SymbolRecordKind SymbolKind); + void EmitCOFFSecRel32Value(MCExpr const *Value); + + void EmitVarDefRange(const MCSymbol *Fn, const LocalVariableAddrRange &Range); + + CVRegNum GetCVRegNum(unsigned RegNum); + void EmitCVDebugVarInfo(const MCSymbol *Fn, const DebugVarInfo LocInfos[], + int NumVarInfos); + void EmitCVDebugFunctionInfo(const char *FunctionName, int FunctionSize); + + void EmitDwarfFunctionInfo(const char *FunctionName, int FunctionSize, unsigned MethodTypeIndex); + + const MCSymbolRefExpr *GetSymbolRefExpr( + const char *SymbolName, + MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None); + + MCSection *GetSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName); + + MCSection *GetSpecificSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName); + + void EmitCVUserDefinedTypesSymbols(); + + void InitTripleName(const char* tripleName = nullptr); + Triple GetTriple(); + unsigned GetDFSize(); + void EmitRelocDirective(const int Offset, StringRef Name, const MCExpr *Expr); + const MCExpr *GenTargetExpr(const MCSymbol* Symbol, + MCSymbolRefExpr::VariantKind Kind, int Delta, + bool IsPCRel = false, int Size = 0); + void EmitARMExIdxPerOffset(); + + +private: + std::unique_ptr RegisterInfo; + std::unique_ptr AsmInfo; + std::unique_ptr ObjFileInfo; + std::unique_ptr OutContext; + MCAsmBackend *AsmBackend; // Owned by MCStreamer + std::unique_ptr InstrInfo; + std::unique_ptr SubtargetInfo; + MCCodeEmitter *CodeEmitter; // Owned by MCStreamer + MCAssembler *Assembler; // Owned by MCStreamer + std::unique_ptr DwarfGenerator; + + std::unique_ptr OS; + MCTargetOptions TargetMOptions; + bool FrameOpened; + std::vector DebugVarInfos; + std::vector DebugEHClauseInfos; + DenseSet AddressTakenFunctions; + + std::set Sections; + int FuncId; + + std::unique_ptr TypeBuilder; + + std::string TripleName; + + MCObjectStreamer *Streamer; // Owned by AsmPrinter + + SmallVector CFIsPerOffset; +}; + +// When object writer is created/initialized successfully, it is returned. +// Or null object is returned. Client should check this. +DLL_EXPORT STDMETHODCALLTYPE ObjectWriter *InitObjWriter(const char *ObjectFilePath, const char* TripleName = nullptr) { + ObjectWriter *OW = new ObjectWriter(); + if (OW->Init(ObjectFilePath, TripleName)) { + return OW; + } + delete OW; + return nullptr; +} + +DLL_EXPORT STDMETHODCALLTYPE void FinishObjWriter(ObjectWriter *OW) { + assert(OW && "ObjWriter is null"); + OW->Finish(); + delete OW; +} + +DLL_EXPORT STDMETHODCALLTYPE void SwitchSection(ObjectWriter *OW, const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + assert(OW && "ObjWriter is null"); + OW->SwitchSection(SectionName, attributes, ComdatName); +} + +DLL_EXPORT STDMETHODCALLTYPE void SetCodeSectionAttribute(ObjectWriter *OW, + const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + assert(OW && "ObjWriter is null"); + OW->SetCodeSectionAttribute(SectionName, attributes, ComdatName); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitAlignment(ObjectWriter *OW, int ByteAlignment) { + assert(OW && "ObjWriter is null"); + OW->EmitAlignment(ByteAlignment); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitBlob(ObjectWriter *OW, int BlobSize, const char *Blob) { + assert(OW && "ObjWriter null"); + OW->EmitBlob(BlobSize, Blob); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitIntValue(ObjectWriter *OW, uint64_t Value, unsigned Size) { + assert(OW && "ObjWriter is null"); + OW->EmitIntValue(Value, Size); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitSymbolDef(ObjectWriter *OW, const char *SymbolName, bool global) { + assert(OW && "ObjWriter is null"); + OW->EmitSymbolDef(SymbolName, global); +} + +DLL_EXPORT STDMETHODCALLTYPE int EmitSymbolRef(ObjectWriter *OW, const char *SymbolName, + RelocType RelocType, int Delta, SymbolRefFlags Flags) { + assert(OW && "ObjWriter is null"); + return OW->EmitSymbolRef(SymbolName, RelocType, Delta, Flags); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitWinFrameInfo(ObjectWriter *OW, const char *FunctionName, + int StartOffset, int EndOffset, + const char *BlobSymbolName) { + assert(OW && "ObjWriter is null"); + OW->EmitWinFrameInfo(FunctionName, StartOffset, EndOffset, BlobSymbolName); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitCFIStart(ObjectWriter *OW, int Offset) { + assert(OW && "ObjWriter is null"); + OW->EmitCFIStart(Offset); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitCFIEnd(ObjectWriter *OW, int Offset) { + assert(OW && "ObjWriter is null"); + OW->EmitCFIEnd(Offset); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitCFILsda(ObjectWriter *OW, const char *LsdaBlobSymbolName) { + assert(OW && "ObjWriter is null"); + OW->EmitCFILsda(LsdaBlobSymbolName); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitCFICode(ObjectWriter *OW, int Offset, const char *Blob) { + assert(OW && "ObjWriter is null"); + OW->EmitCFICode(Offset, Blob); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitDebugFileInfo(ObjectWriter *OW, int FileId, + const char *FileName) { + assert(OW && "ObjWriter is null"); + OW->EmitDebugFileInfo(FileId, FileName); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitDebugFunctionInfo(ObjectWriter *OW, + const char *FunctionName, + int FunctionSize, + unsigned MethodTypeIndex) { + assert(OW && "ObjWriter is null"); + OW->EmitDebugFunctionInfo(FunctionName, FunctionSize, MethodTypeIndex); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitDebugVar(ObjectWriter *OW, char *Name, int TypeIndex, + bool IsParam, int RangeCount, + ICorDebugInfo::NativeVarInfo *Ranges) { + assert(OW && "ObjWriter is null"); + OW->EmitDebugVar(Name, TypeIndex, IsParam, RangeCount, Ranges); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitDebugEHClause(ObjectWriter *OW, unsigned TryOffset, + unsigned TryLength, unsigned HandlerOffset, + unsigned HandlerLength) { + assert(OW && "ObjWriter is null"); + OW->EmitDebugEHClause(TryOffset, TryLength, HandlerOffset, HandlerLength); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitDebugLoc(ObjectWriter *OW, int NativeOffset, int FileId, + int LineNumber, int ColNumber) { + assert(OW && "ObjWriter is null"); + OW->EmitDebugLoc(NativeOffset, FileId, LineNumber, ColNumber); +} + +// This should be invoked at the end of module emission to finalize +// debug module info. +DLL_EXPORT STDMETHODCALLTYPE void EmitDebugModuleInfo(ObjectWriter *OW) { + assert(OW && "ObjWriter is null"); + OW->EmitDebugModuleInfo(); +} + +DLL_EXPORT STDMETHODCALLTYPE unsigned GetEnumTypeIndex(ObjectWriter *OW, + EnumTypeDescriptor TypeDescriptor, + EnumRecordTypeDescriptor *TypeRecords) { + assert(OW && "ObjWriter is null"); + return OW->GetEnumTypeIndex(TypeDescriptor, TypeRecords); +} + +DLL_EXPORT STDMETHODCALLTYPE unsigned GetClassTypeIndex(ObjectWriter *OW, + ClassTypeDescriptor ClassDescriptor) { + assert(OW && "ObjWriter is null"); + return OW->GetClassTypeIndex(ClassDescriptor); +} + +DLL_EXPORT STDMETHODCALLTYPE unsigned +GetCompleteClassTypeIndex(ObjectWriter *OW, ClassTypeDescriptor ClassDescriptor, + ClassFieldsTypeDescriptior ClassFieldsDescriptor, + DataFieldDescriptor *FieldsDescriptors, + StaticDataFieldDescriptor *StaticsDescriptors) { + assert(OW && "ObjWriter is null"); + return OW->GetCompleteClassTypeIndex(ClassDescriptor, ClassFieldsDescriptor, + FieldsDescriptors, StaticsDescriptors); +} + +DLL_EXPORT STDMETHODCALLTYPE unsigned GetArrayTypeIndex(ObjectWriter *OW, + ClassTypeDescriptor ClassDescriptor, + ArrayTypeDescriptor ArrayDescriptor) { + assert(OW && "ObjWriter is null"); + return OW->GetArrayTypeIndex(ClassDescriptor, ArrayDescriptor); +} + +DLL_EXPORT STDMETHODCALLTYPE unsigned GetPointerTypeIndex(ObjectWriter *OW, + PointerTypeDescriptor PointerDescriptor) { + assert(OW && "ObjWriter is null"); + return OW->GetPointerTypeIndex(PointerDescriptor); +} + +DLL_EXPORT STDMETHODCALLTYPE unsigned GetMemberFunctionTypeIndex(ObjectWriter *OW, + MemberFunctionTypeDescriptor MemberDescriptor, + uint32_t *ArgumentTypes) { + assert(OW && "ObjWriter is null"); + return OW->GetMemberFunctionTypeIndex(MemberDescriptor, ArgumentTypes); +} + +DLL_EXPORT STDMETHODCALLTYPE unsigned GetMemberFunctionIdTypeIndex(ObjectWriter *OW, + MemberFunctionIdTypeDescriptor MemberIdDescriptor) { + assert(OW && "ObjWriter is null"); + return OW->GetMemberFunctionId(MemberIdDescriptor); +} + +DLL_EXPORT STDMETHODCALLTYPE unsigned GetPrimitiveTypeIndex(ObjectWriter *OW, int Type) { + assert(OW && "ObjWriter is null"); + return OW->GetPrimitiveTypeIndex(Type); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitARMFnStart(ObjectWriter *OW) { + assert(OW && "ObjWriter is null"); + return OW->EmitARMFnStart(); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitARMFnEnd(ObjectWriter *OW) { + assert(OW && "ObjWriter is null"); + return OW->EmitARMFnEnd(); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitARMExIdxLsda(ObjectWriter *OW, const char *Blob) { + assert(OW && "ObjWriter is null"); + return OW->EmitARMExIdxLsda(Blob); +} + +DLL_EXPORT STDMETHODCALLTYPE void EmitARMExIdxCode(ObjectWriter *OW, int Offset, const char *Blob) { + assert(OW && "ObjWriter is null"); + return OW->EmitARMExIdxCode(Offset, Blob); +} diff --git a/src/coreclr/tools/aot/ObjWriter/objwriter.proj b/src/coreclr/tools/aot/ObjWriter/objwriter.proj new file mode 100644 index 00000000000000..0cf0d566fb786b --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/objwriter.proj @@ -0,0 +1,21 @@ + + + + + build.cmd + build.sh + + $(Configuration) + Debug + + "$(ArtifactsDir)" "$(RepoRoot)" + $(BuildArguments) $(BuildArchitecture) $(TargetArchitecture) + $(BuildArguments) $(ObjWriterBuildType) $(Compiler) + + + + + + + diff --git a/src/coreclr/tools/aot/ObjWriter/toolchain.cmake b/src/coreclr/tools/aot/ObjWriter/toolchain.cmake new file mode 100644 index 00000000000000..83f04843018a30 --- /dev/null +++ b/src/coreclr/tools/aot/ObjWriter/toolchain.cmake @@ -0,0 +1,12 @@ +if (WIN32) + # Use static CRT + set(LLVM_USE_CRT_DEBUG MTd) + set(LLVM_USE_CRT_RELEASE MT) + set(LLVM_USE_CRT_MINSIZEREL MT) + set(LLVM_USE_CRT_RELWITHDEBINFO MT) + + # Enable debug information for Release builds + set(CMAKE_CXX_FLAGS_RELEASE_INIT /Zi) + set(CMAKE_C_FLAGS_RELEASE_INIT /Zi) + set(CMAKE_SHARED_LINKER_FLAGS_RELEASE_INIT "/debug /OPT:REF /OPT:ICF") +endif() diff --git a/src/coreclr/tools/aot/crossgen2.sln b/src/coreclr/tools/aot/crossgen2.sln index ac541c0a78d16a..4727feaef44e4f 100644 --- a/src/coreclr/tools/aot/crossgen2.sln +++ b/src/coreclr/tools/aot/crossgen2.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29123.88 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31612.314 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "crossgen2", "crossgen2\crossgen2.csproj", "{9B928D3E-06AB-45E5-BF79-F374F0AE3B98}" EndProject @@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.TypeSystem.Ready EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Diagnostics", "ILCompiler.Diagnostics\ILCompiler.Diagnostics.csproj", "{3EACD929-4725-4173-A845-734936BBDF87}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Reflection.ReadyToRun", "ILCompiler.Reflection.ReadyToRun\ILCompiler.Reflection.ReadyToRun.csproj", "{0BB34BA1-1B3A-445C-9C04-0D710D1983F0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Checked|Any CPU = Checked|Any CPU @@ -122,6 +124,24 @@ Global {3EACD929-4725-4173-A845-734936BBDF87}.Release|x64.Build.0 = Release|Any CPU {3EACD929-4725-4173-A845-734936BBDF87}.Release|x86.ActiveCfg = Release|Any CPU {3EACD929-4725-4173-A845-734936BBDF87}.Release|x86.Build.0 = Release|Any CPU + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Checked|Any CPU.Build.0 = Debug|Any CPU + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Checked|x64.ActiveCfg = Debug|x64 + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Checked|x64.Build.0 = Debug|x64 + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Checked|x86.ActiveCfg = Debug|Any CPU + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Checked|x86.Build.0 = Debug|Any CPU + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Debug|x64.ActiveCfg = Debug|x64 + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Debug|x64.Build.0 = Debug|x64 + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Debug|x86.ActiveCfg = Debug|Any CPU + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Debug|x86.Build.0 = Debug|Any CPU + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Release|Any CPU.Build.0 = Release|Any CPU + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Release|x64.ActiveCfg = Release|x64 + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Release|x64.Build.0 = Release|x64 + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Release|x86.ActiveCfg = Release|Any CPU + {0BB34BA1-1B3A-445C-9C04-0D710D1983F0}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 4df45e3065e0d3..d4c693805f5e20 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -16,6 +16,8 @@ using Internal.TypeSystem; using Internal.TypeSystem.Ecma; +using ILCompiler.Reflection.ReadyToRun; + namespace ILCompiler { internal class Program @@ -401,6 +403,12 @@ private int Run(string[] args) try { var module = _typeSystemContext.GetModuleFromPath(inputFile.Value); + if ((module.PEReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == (CorFlags)0 + && module.PEReader.TryGetReadyToRunHeader(out int _)) + { + Console.WriteLine(SR.IgnoringCompositeImage, inputFile.Value); + continue; + } _allInputFilePaths.Add(inputFile.Key, inputFile.Value); inputFilePaths.Add(inputFile.Key, inputFile.Value); _referenceableModules.Add(module); diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index 7a272037fd14f7..903ae55086e320 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -321,6 +321,9 @@ Error: managed C++ is not supported: '{0}' + + Ignoring composite native image: '{0}' + Verify that struct type layout and field offsets match between compile time and runtime. Use only for diagnostic purposes. diff --git a/src/coreclr/tools/aot/ilc.sln b/src/coreclr/tools/aot/ilc.sln new file mode 100644 index 00000000000000..25fc1745ba8fc1 --- /dev/null +++ b/src/coreclr/tools/aot/ilc.sln @@ -0,0 +1,144 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29123.88 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler", "ILCompiler\ILCompiler.csproj", "{6856F5F6-E568-493F-AF8A-7F624B5A02A5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.DependencyAnalysisFramework", "ILCompiler.DependencyAnalysisFramework\ILCompiler.DependencyAnalysisFramework.csproj", "{FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.MetadataTransform", "ILCompiler.MetadataTransform\ILCompiler.MetadataTransform.csproj", "{B68D5B9E-405B-4E44-B2CC-418EC78AE070}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.RyuJit", "ILCompiler.RyuJit\ILCompiler.RyuJit.csproj", "{05E020F4-6FA1-4DEE-8B9D-4F4B79840231}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.TypeSystem", "ILCompiler.TypeSystem\ILCompiler.TypeSystem.csproj", "{07221944-6A68-4B82-8461-82C7754B1B1F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Compiler", "ILCompiler.Compiler\ILCompiler.Compiler.csproj", "{FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "repro", "ILCompiler\repro\repro.csproj", "{CBDE0470-E0C9-4693-9A11-ACC117522F3F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Checked|Any CPU = Checked|Any CPU + Checked|x64 = Checked|x64 + Checked|x86 = Checked|x86 + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Checked|Any CPU.ActiveCfg = Checked|x86 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Checked|x64.ActiveCfg = Checked|x64 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Checked|x64.Build.0 = Checked|x64 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Checked|x86.ActiveCfg = Checked|x86 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Checked|x86.Build.0 = Checked|x86 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Debug|Any CPU.ActiveCfg = Debug|x86 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Debug|x64.ActiveCfg = Debug|x64 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Debug|x64.Build.0 = Debug|x64 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Debug|x86.ActiveCfg = Debug|x86 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Debug|x86.Build.0 = Debug|x86 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Release|Any CPU.ActiveCfg = Release|x86 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Release|x64.ActiveCfg = Release|x64 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Release|x64.Build.0 = Release|x64 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Release|x86.ActiveCfg = Release|x86 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Release|x86.Build.0 = Release|x86 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Checked|Any CPU.ActiveCfg = Checked|x86 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Checked|x64.ActiveCfg = Checked|x64 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Checked|x64.Build.0 = Checked|x64 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Checked|x86.ActiveCfg = Checked|x86 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Checked|x86.Build.0 = Checked|x86 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Debug|Any CPU.ActiveCfg = Debug|x86 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Debug|x64.ActiveCfg = Debug|x64 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Debug|x64.Build.0 = Debug|x64 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Debug|x86.ActiveCfg = Debug|x86 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Debug|x86.Build.0 = Debug|x86 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Release|Any CPU.ActiveCfg = Release|x86 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Release|x64.ActiveCfg = Release|x64 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Release|x64.Build.0 = Release|x64 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Release|x86.ActiveCfg = Release|x86 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Release|x86.Build.0 = Release|x86 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Checked|Any CPU.ActiveCfg = Checked|x86 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Checked|x64.ActiveCfg = Checked|x64 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Checked|x64.Build.0 = Checked|x64 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Checked|x86.ActiveCfg = Checked|x86 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Checked|x86.Build.0 = Checked|x86 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Debug|Any CPU.ActiveCfg = Debug|x86 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Debug|x64.ActiveCfg = Debug|x64 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Debug|x64.Build.0 = Debug|x64 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Debug|x86.ActiveCfg = Debug|x86 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Debug|x86.Build.0 = Debug|x86 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Release|Any CPU.ActiveCfg = Release|x86 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Release|x64.ActiveCfg = Release|x64 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Release|x64.Build.0 = Release|x64 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Release|x86.ActiveCfg = Release|x86 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Release|x86.Build.0 = Release|x86 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Checked|Any CPU.ActiveCfg = Checked|x86 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Checked|x64.ActiveCfg = Checked|x64 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Checked|x64.Build.0 = Checked|x64 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Checked|x86.ActiveCfg = Checked|x86 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Checked|x86.Build.0 = Checked|x86 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Debug|Any CPU.ActiveCfg = Debug|x86 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Debug|x64.ActiveCfg = Debug|x64 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Debug|x64.Build.0 = Debug|x64 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Debug|x86.ActiveCfg = Debug|x86 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Debug|x86.Build.0 = Debug|x86 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Release|Any CPU.ActiveCfg = Release|x86 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Release|x64.ActiveCfg = Release|x64 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Release|x64.Build.0 = Release|x64 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Release|x86.ActiveCfg = Release|x86 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Release|x86.Build.0 = Release|x86 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Checked|Any CPU.ActiveCfg = Checked|x86 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Checked|x64.ActiveCfg = Checked|x64 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Checked|x64.Build.0 = Checked|x64 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Checked|x86.ActiveCfg = Checked|x86 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Checked|x86.Build.0 = Checked|x86 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Debug|Any CPU.ActiveCfg = Debug|x86 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Debug|x64.ActiveCfg = Debug|x64 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Debug|x64.Build.0 = Debug|x64 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Debug|x86.ActiveCfg = Debug|x86 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Debug|x86.Build.0 = Debug|x86 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Release|Any CPU.ActiveCfg = Release|x86 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Release|x64.ActiveCfg = Release|x64 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Release|x64.Build.0 = Release|x64 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Release|x86.ActiveCfg = Release|x86 + {07221944-6A68-4B82-8461-82C7754B1B1F}.Release|x86.Build.0 = Release|x86 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Checked|Any CPU.ActiveCfg = Checked|x86 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Checked|x64.ActiveCfg = Checked|x64 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Checked|x64.Build.0 = Checked|x64 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Checked|x86.ActiveCfg = Checked|x86 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Checked|x86.Build.0 = Checked|x86 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Debug|Any CPU.ActiveCfg = Debug|x86 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Debug|x64.ActiveCfg = Debug|x64 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Debug|x64.Build.0 = Debug|x64 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Debug|x86.ActiveCfg = Debug|x86 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Debug|x86.Build.0 = Debug|x86 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Release|Any CPU.ActiveCfg = Release|x86 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Release|x64.ActiveCfg = Release|x64 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Release|x64.Build.0 = Release|x64 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Release|x86.ActiveCfg = Release|x86 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Release|x86.Build.0 = Release|x86 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Checked|Any CPU.ActiveCfg = Checked|x86 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Checked|x64.ActiveCfg = Checked|x64 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Checked|x64.Build.0 = Checked|x64 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Checked|x86.ActiveCfg = Checked|x86 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Checked|x86.Build.0 = Checked|x86 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Debug|Any CPU.ActiveCfg = Debug|x86 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Debug|x64.ActiveCfg = Debug|x64 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Debug|x64.Build.0 = Debug|x64 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Debug|x86.ActiveCfg = Debug|x86 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Debug|x86.Build.0 = Debug|x86 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Release|Any CPU.ActiveCfg = Release|x86 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Release|x64.ActiveCfg = Release|x64 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Release|x64.Build.0 = Release|x64 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Release|x86.ActiveCfg = Release|x86 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A484CF9D-B203-427F-9D15-A5BBC6013421} + EndGlobalSection +EndGlobal diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface.h b/src/coreclr/tools/aot/jitinterface/jitinterface.h index ee00bce0a69b2c..c620b9b29f9484 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface.h @@ -134,7 +134,7 @@ struct JitInterfaceCallbacks int32_t* (* getAddrOfCaptureThreadGlobal)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); void* (* getHelperFtn)(void * thisHandle, CorInfoExceptionClass** ppException, CorInfoHelpFunc ftnNum, void** ppIndirection); void (* getFunctionEntryPoint)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP* pResult, CORINFO_ACCESS_FLAGS accessFlags); - void (* getFunctionFixedEntryPoint)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP* pResult); + void (* getFunctionFixedEntryPoint)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, bool isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP* pResult); void* (* getMethodSync)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, void** ppIndirection); CorInfoHelpFunc (* getLazyStringLiteralHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_MODULE_HANDLE handle); CORINFO_MODULE_HANDLE (* embedModuleHandle)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_MODULE_HANDLE handle, void** ppIndirection); @@ -1374,10 +1374,11 @@ class JitInterfaceWrapper : public ICorJitInfo virtual void getFunctionFixedEntryPoint( CORINFO_METHOD_HANDLE ftn, + bool isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP* pResult) { CorInfoExceptionClass* pException = nullptr; - _callbacks->getFunctionFixedEntryPoint(_thisHandle, &pException, ftn, pResult); + _callbacks->getFunctionFixedEntryPoint(_thisHandle, &pException, ftn, isUnsafeFunctionPointer, pResult); if (pException != nullptr) throw pException; } diff --git a/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs b/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs index f460a5f49817f3..1481f647090ff4 100644 --- a/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs +++ b/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs @@ -33,7 +33,7 @@ internal class CommandLineOptions public bool IncludeFullGraphs; public int SpgoMinSamples = 50; public bool VerboseWarnings; - public jittraceoptions JitTraceOptions; + public JitTraceOptions JitTraceOptions; public double ExcludeEventsBefore; public double ExcludeEventsAfter; public bool Warnings; @@ -211,7 +211,7 @@ void HelpOption() HelpOption(); } - JitTraceOptions = jittraceoptions.none; + JitTraceOptions = JitTraceOptions.none; #if DEBUG // Usage of the jittrace format requires using logic embedded in the runtime repository and isn't suitable for general consumer use at this time // Build it in debug and check builds to ensure that it doesn't bitrot, and remains available for use by developers willing to build the repo @@ -230,14 +230,14 @@ void HelpOption() syntax.DefineOption(name: "sorted", value: ref sorted, help: "Generate sorted output.", requireValue: false); if (sorted) { - JitTraceOptions |= jittraceoptions.sorted; + JitTraceOptions |= JitTraceOptions.sorted; } bool showtimestamp = false; syntax.DefineOption(name: "showtimestamp", value: ref showtimestamp, help: "Show timestamps in output.", requireValue: false); if (showtimestamp) { - JitTraceOptions |= jittraceoptions.showtimestamp; + JitTraceOptions |= JitTraceOptions.showtimestamp; } syntax.DefineOption(name: "includeReadyToRun", value: ref ProcessR2REvents, help: "Include ReadyToRun methods in the trace file.", requireValue: false); diff --git a/src/coreclr/tools/dotnet-pgo/Program.cs b/src/coreclr/tools/dotnet-pgo/Program.cs index c3a13789686abe..c210adca92148a 100644 --- a/src/coreclr/tools/dotnet-pgo/Program.cs +++ b/src/coreclr/tools/dotnet-pgo/Program.cs @@ -41,7 +41,7 @@ public enum PgoFileType } [Flags] - public enum jittraceoptions + public enum JitTraceOptions { none = 0, sorted = 1, @@ -1650,7 +1650,7 @@ void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodF return 0; } - static void GenerateJittraceFile(FileInfo outputFileName, IEnumerable methodsToAttemptToPrepare, jittraceoptions jittraceOptions) + static void GenerateJittraceFile(FileInfo outputFileName, IEnumerable methodsToAttemptToPrepare, JitTraceOptions jittraceOptions) { PrintMessage($"JitTrace options {jittraceOptions}"); @@ -1677,7 +1677,7 @@ static void GenerateJittraceFile(FileInfo outputFileName, IEnumerableheaderSize = sizeof(StressLogHeader); - hdr->magic = 'STRL'; + hdr->magic = *(uint32_t*)"LRTS"; hdr->version = 0x00010001; hdr->memoryBase = (uint8_t*)hdr; hdr->memoryCur = hdr->memoryBase + sizeof(StressLogHeader); @@ -330,10 +335,16 @@ void StressLog::AddModule(uint8_t* moduleBase) #endif //MEMORY_MAPPED_STRESSLOG } #else //HOST_WINDOWS - // as it is not easy to obtain module size on Linux or OSX, - // just guess and hope for the best - size_t remainingSize = StressMsg::maxOffset - cumSize; - theLog.modules[moduleIndex].size = remainingSize / 2; + uint8_t* destination = nullptr; + uint8_t* destination_end = nullptr; +#ifdef MEMORY_MAPPED_STRESSLOG + if (hdr != nullptr) + { + destination = &hdr->moduleImage[cumSize]; + destination_end = &hdr->moduleImage[64*1024*1024]; + } +#endif //MEMORY_MAPPED_STRESSLOG + theLog.modules[moduleIndex].size = PAL_CopyModuleData(moduleBase, destination, destination_end); #endif //HOST_WINDOWS } @@ -932,15 +943,14 @@ void* StressLog::AllocMemoryMapped(size_t n) void* __cdecl ThreadStressLog::operator new(size_t n, const NoThrow&) NOEXCEPT { - if (StressLogChunk::s_LogChunkHeap != NULL) - { - //no need to zero memory because we could handle garbage contents - return HeapAlloc(StressLogChunk::s_LogChunkHeap, 0, n); - } - else - { + if (StressLogChunk::s_memoryMapped) return StressLog::AllocMemoryMapped(n); - } +#ifdef HOST_WINDOWS + _ASSERTE(StressLogChunk::s_LogChunkHeap); + return HeapAlloc(StressLogChunk::s_LogChunkHeap, 0, n); +#else + return malloc(n); +#endif //HOST_WINDOWS } #endif //MEMORY_MAPPED_STRESSLOG diff --git a/src/coreclr/vm/corelib.cpp b/src/coreclr/vm/corelib.cpp index 4189a6371b25c7..707cd24274148b 100644 --- a/src/coreclr/vm/corelib.cpp +++ b/src/coreclr/vm/corelib.cpp @@ -114,7 +114,6 @@ enum _gsigc { #include "metasig.h" }; - // // The actual array with the hardcoded metasig: // @@ -176,8 +175,6 @@ enum _gsigc { #undef _IM #undef _Fld - - #ifdef _DEBUG // @@ -213,10 +210,6 @@ enum _gsigc { #endif - - - - /////////////////////////////////////////////////////////////////////////////// // // CoreLib binder @@ -271,36 +264,28 @@ const USHORT c_nCoreLibFieldDescriptions = ARRAY_SIZE(c_rgCoreLibFieldDescriptio // When compiling crossgen, we only need the target version of the ecall tables -#define FCFuncFlags(intrinsicID, dynamicID) \ - (BYTE*)( (((BYTE)intrinsicID) << 16) + (((BYTE)dynamicID) << 24) ) - +#define FCFuncFlags(dynamicID) \ + (BYTE*)( (((BYTE)dynamicID) << 24) ) -#define FCFuncElement(name, impl) FCFuncFlags(CORINFO_INTRINSIC_Illegal, ECall::InvalidDynamicFCallId), \ +#define FCFuncElement(name, impl) FCFuncFlags(ECall::InvalidDynamicFCallId), \ (LPVOID)GetEEFuncEntryPoint(impl), (LPVOID)name, #define FCFuncElementSig(name,sig,impl) \ FCFuncFlag_HasSignature + FCFuncElement(name, impl) (LPVOID)sig, -#define FCIntrinsic(name,impl,intrinsicID) FCFuncFlags(intrinsicID, ECall::InvalidDynamicFCallId), \ - (LPVOID)GetEEFuncEntryPoint(impl), (LPVOID)name, - -#define FCIntrinsicSig(name,sig,impl,intrinsicID) \ - FCFuncFlag_HasSignature + FCIntrinsic(name,impl,intrinsicID) (LPVOID)sig, - -#define FCDynamic(name,intrinsicID,dynamicID) FCFuncFlags(intrinsicID, dynamicID), \ +#define FCDynamic(name,dynamicID) FCFuncFlags(dynamicID), \ NULL, (LPVOID)name, -#define FCDynamicSig(name,sig,intrinsicID,dynamicID) \ - FCFuncFlag_HasSignature + FCDynamic(name,intrinsicID,dynamicID) (LPVOID)sig, +#define FCDynamicSig(name,sig,dynamicID) \ + FCFuncFlag_HasSignature + FCDynamic(name,dynamicID) (LPVOID)sig, #define FCUnreferenced FCFuncFlag_Unreferenced + #define FCFuncStart(name) static const LPVOID name[] = { -#define FCFuncEnd() FCFuncFlag_EndOfArray + FCFuncFlags(CORINFO_INTRINSIC_Illegal, ECall::InvalidDynamicFCallId) }; +#define FCFuncEnd() FCFuncFlag_EndOfArray + FCFuncFlags(ECall::InvalidDynamicFCallId) }; #include "ecalllist.h" - // Extern definitions so that ecall.cpp can see these tables extern const ECClass c_rgECClasses[]; extern const int c_nECClasses; diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index b46562ddb13b75..6fb9d0e3e3f210 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -998,7 +998,7 @@ DEFINE_METHOD(BUFFER, MEMCPY, Memcpy, DEFINE_CLASS(STUBHELPERS, StubHelpers, StubHelpers) DEFINE_METHOD(STUBHELPERS, GET_NDIRECT_TARGET, GetNDirectTarget, SM_IntPtr_RetIntPtr) -DEFINE_METHOD(STUBHELPERS, GET_DELEGATE_TARGET, GetDelegateTarget, SM_Delegate_RefIntPtr_RetIntPtr) +DEFINE_METHOD(STUBHELPERS, GET_DELEGATE_TARGET, GetDelegateTarget, SM_Delegate_RetIntPtr) #ifdef FEATURE_COMINTEROP DEFINE_METHOD(STUBHELPERS, GET_COM_HR_EXCEPTION_OBJECT, GetCOMHRExceptionObject, SM_Int_IntPtr_Obj_RetException) DEFINE_METHOD(STUBHELPERS, GET_COM_IP_FROM_RCW, GetCOMIPFromRCW, SM_Obj_IntPtr_RefIntPtr_RefBool_RetIntPtr) @@ -1027,9 +1027,6 @@ DEFINE_METHOD(STUBHELPERS, VALIDATE_OBJECT, Validate DEFINE_METHOD(STUBHELPERS, VALIDATE_BYREF, ValidateByref, SM_IntPtr_IntPtr_Obj_RetVoid) DEFINE_METHOD(STUBHELPERS, GET_STUB_CONTEXT, GetStubContext, SM_RetIntPtr) DEFINE_METHOD(STUBHELPERS, LOG_PINNED_ARGUMENT, LogPinnedArgument, SM_IntPtr_IntPtr_RetVoid) -#ifdef TARGET_64BIT -DEFINE_METHOD(STUBHELPERS, GET_STUB_CONTEXT_ADDR, GetStubContextAddr, SM_RetIntPtr) -#endif // TARGET_64BIT DEFINE_METHOD(STUBHELPERS, NEXT_CALL_RETURN_ADDRESS, NextCallReturnAddress, SM_RetIntPtr) DEFINE_METHOD(STUBHELPERS, SAFE_HANDLE_ADD_REF, SafeHandleAddRef, SM_SafeHandle_RefBool_RetIntPtr) DEFINE_METHOD(STUBHELPERS, SAFE_HANDLE_RELEASE, SafeHandleRelease, SM_SafeHandle_RetVoid) diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 7888c8e377f4bc..6c4ce7608660ee 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -784,14 +784,11 @@ class ILStubState : public StubState { // Struct marshal stubs don't actually call anything so they do not need the secrect parameter. } -#ifndef HOST_64BIT else if (SF_IsForwardDelegateStub(m_dwStubFlags)) { // Forward delegate stubs get all the context they need in 'this' so they - // don't use the secret parameter. Except for AMD64 where we use the secret - // argument to pass the real target to the stub-for-host. + // don't use the secret parameter. } -#endif // !HOST_64BIT else { // All other IL stubs will need to use the secret parameter. @@ -2076,22 +2073,12 @@ void NDirectStubLinker::DoNDirect(ILCodeStream *pcsEmit, DWORD dwStubFlags, Meth if (SF_IsForwardStub(dwStubFlags)) // managed-to-native { - if (SF_IsDelegateStub(dwStubFlags)) // delegate invocation { // get the delegate unmanaged target - we call a helper instead of just grabbing // the _methodPtrAux field because we may need to intercept the call for host, etc. pcsEmit->EmitLoadThis(); -#ifdef TARGET_64BIT - // on AMD64 GetDelegateTarget will return address of the generic stub for host when we are hosted - // and update the secret argument with real target - the secret arg will be embedded in the - // InlinedCallFrame by the JIT and fetched via TLS->Thread->Frame->Datum by the stub for host - pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT_ADDR, 0, 1); -#else // !TARGET_64BIT - // we don't need to do this on x86 because stub for host is generated dynamically per target - pcsEmit->EmitLDNULL(); -#endif // !TARGET_64BIT - pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_DELEGATE_TARGET, 2, 1); + pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_DELEGATE_TARGET, 1, 1); } else // direct invocation { diff --git a/src/coreclr/vm/ecall.cpp b/src/coreclr/vm/ecall.cpp index 439e9d9beced25..ad93743a9cbfac 100644 --- a/src/coreclr/vm/ecall.cpp +++ b/src/coreclr/vm/ecall.cpp @@ -641,56 +641,6 @@ MethodDesc* ECall::MapTargetBackToMethod(PCODE pTarg, PCODE * ppAdjustedEntryPoi #ifndef DACCESS_COMPILE -/* static */ -CorInfoIntrinsics ECall::GetIntrinsicID(MethodDesc* pMD) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(pMD->IsFCall()); - } - CONTRACTL_END; - - MethodTable * pMT = pMD->GetMethodTable(); - -#ifdef FEATURE_COMINTEROP - // COM imported classes have special constructors - if (pMT->IsComObjectType()) - { - // This has to be tlbimp constructor - return(CORINFO_INTRINSIC_Illegal); - } -#endif // FEATURE_COMINTEROP - - // - // Delegate constructors are FCalls for which the entrypoint points to the target of the delegate - // We have to intercept these and set the call target to the helper COMDelegate::DelegateConstruct - // - if (pMT->IsDelegate()) - { - // COMDelegate::DelegateConstruct is the only fcall used by user delegates. - // All the other gDelegateFuncs are only used by System.Delegate - _ASSERTE(pMD->IsCtor()); - - return(CORINFO_INTRINSIC_Illegal); - } - - // All intrinsic live in CoreLib (FindECFuncForMethod does not work for non-CoreLib intrinsics) - if (!pMD->GetModule()->IsSystem()) - { - return(CORINFO_INTRINSIC_Illegal); - } - - ECFunc* info = FindECFuncForMethod(pMD); - - if (info == NULL) - return(CORINFO_INTRINSIC_Illegal); - - return info->IntrinsicID(); -} - #ifdef _DEBUG void FCallAssert(void*& cache, void* target) diff --git a/src/coreclr/vm/ecall.h b/src/coreclr/vm/ecall.h index f5c8ac646b4406..1a538db3c67e58 100644 --- a/src/coreclr/vm/ecall.h +++ b/src/coreclr/vm/ecall.h @@ -83,7 +83,6 @@ class ECall static PCODE GetFCallImpl(MethodDesc* pMD, BOOL * pfSharedOrDynamicFCallImpl = NULL); static MethodDesc* MapTargetBackToMethod(PCODE pTarg, PCODE * ppAdjustedEntryPoint = NULL); static DWORD GetIDForMethod(MethodDesc *pMD); - static CorInfoIntrinsics GetIntrinsicID(MethodDesc *pMD); // Some fcalls (delegate ctors and tlbimpl ctors) shared one implementation. // We should never patch vtable for these since they have 1:N mapping between diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index afdbf678d1183c..be7a6826854f0e 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -1,13 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. + // ECallList.H // // This file contains definitions of FCall entrypoints // - - - #ifndef FCFuncElement #define FCFuncElement(name, impl) #endif @@ -16,20 +14,12 @@ #define FCFuncElementSig(name,sig,impl) #endif -#ifndef FCIntrinsic -#define FCIntrinsic(name,impl,intrinsicID) -#endif - -#ifndef FCIntrinsicSig -#define FCIntrinsicSig(name,sig,impl,intrinsicID) -#endif - #ifndef FCDynamic -#define FCDynamic(name,intrinsicID,dynamicID) +#define FCDynamic(name,dynamicID) #endif #ifndef FCDynamicSig -#define FCDynamicSig(name,sig,intrinsicID,dynamicID) +#define FCDynamicSig(name,sig,dynamicID) #endif #ifndef FCUnreferenced @@ -54,8 +44,6 @@ // // - - FCFuncStart(gDependentHandleFuncs) FCFuncElement("InternalInitialize", DependentHandle::InternalInitialize) FCFuncElement("InternalGetTarget", DependentHandle::InternalGetTarget) @@ -66,10 +54,6 @@ FCFuncStart(gDependentHandleFuncs) FCFuncElement("InternalFree", DependentHandle::InternalFree) FCFuncEnd() - - - - FCFuncStart(gEnumFuncs) FCFuncElement("InternalGetUnderlyingType", ReflectionEnum::InternalGetEnumUnderlyingType) FCFuncElement("InternalGetCorElementType", ReflectionEnum::InternalGetCorElementType) @@ -77,20 +61,20 @@ FCFuncStart(gEnumFuncs) FCFuncEnd() FCFuncStart(gObjectFuncs) - FCIntrinsic("GetType", ObjectNative::GetClass, CORINFO_INTRINSIC_Object_GetType) + FCFuncElement("GetType", ObjectNative::GetClass) FCFuncEnd() FCFuncStart(gStringFuncs) - FCDynamic("FastAllocateString", CORINFO_INTRINSIC_Illegal, ECall::FastAllocateString) - FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharArrayManaged) - FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrChar_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharArrayStartLengthManaged) - FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharPtrManaged) - FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrChar_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharPtrStartLengthManaged) - FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_Char_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharCountManaged) - FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ReadOnlySpanOfChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorReadOnlySpanOfCharManaged) - FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorSBytePtrManaged) - FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorSBytePtrStartLengthManaged) - FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_Int_Int_Encoding_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorSBytePtrStartLengthEncodingManaged) + FCDynamic("FastAllocateString", ECall::FastAllocateString) + FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrChar_RetVoid, ECall::CtorCharArrayManaged) + FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrChar_Int_Int_RetVoid, ECall::CtorCharArrayStartLengthManaged) + FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrChar_RetVoid, ECall::CtorCharPtrManaged) + FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrChar_Int_Int_RetVoid, ECall::CtorCharPtrStartLengthManaged) + FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_Char_Int_RetVoid, ECall::CtorCharCountManaged) + FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ReadOnlySpanOfChar_RetVoid, ECall::CtorReadOnlySpanOfCharManaged) + FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_RetVoid, ECall::CtorSBytePtrManaged) + FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_Int_Int_RetVoid, ECall::CtorSBytePtrStartLengthManaged) + FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_Int_Int_Encoding_RetVoid, ECall::CtorSBytePtrStartLengthEncodingManaged) FCFuncElement("SetTrailByte", COMString::FCSetTrailByte) FCFuncElement("TryGetTrailByte", COMString::FCTryGetTrailByte) FCFuncElement("IsInterned", AppDomainNative::IsStringInterned) @@ -192,7 +176,7 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("AllocateComObject", RuntimeTypeHandle::AllocateComObject) #endif // FEATURE_COMINTEROP FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) - FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) + FCFuncElement("GetValueInternal", RuntimeTypeHandle::GetValueInternal) FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo) FCFuncEnd() @@ -254,7 +238,6 @@ FCFuncStart(gRuntimeMethodHandle) FCFuncElement("GetLoaderAllocator", RuntimeMethodHandle::GetLoaderAllocator) FCFuncEnd() - FCFuncStart(gCOMFieldHandleNewFuncs) FCFuncElement("GetValue", RuntimeFieldHandle::GetValue) FCFuncElement("SetValue", RuntimeFieldHandle::SetValue) @@ -270,7 +253,6 @@ FCFuncStart(gCOMFieldHandleNewFuncs) FCFuncElement("AcquiresContextFromThis", RuntimeFieldHandle::AcquiresContextFromThis) FCFuncEnd() - FCFuncStart(gCOMModuleFuncs) FCFuncElement("GetTypes", COMModule::GetTypes) FCFuncEnd() @@ -713,17 +695,14 @@ FCFuncStart(gStubHelperFuncs) FCFuncElement("ValidateObject", StubHelpers::ValidateObject) FCFuncElement("ValidateByref", StubHelpers::ValidateByref) FCFuncElement("LogPinnedArgument", StubHelpers::LogPinnedArgument) - FCIntrinsic("GetStubContext", StubHelpers::GetStubContext, CORINFO_INTRINSIC_StubHelpers_GetStubContext) -#ifdef TARGET_64BIT - FCIntrinsic("GetStubContextAddr", StubHelpers::GetStubContextAddr, CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr) -#endif // TARGET_64BIT + FCFuncElement("GetStubContext", StubHelpers::GetStubContext) #ifdef FEATURE_ARRAYSTUB_AS_IL FCFuncElement("ArrayTypeCheck", StubHelpers::ArrayTypeCheck) #endif //FEATURE_ARRAYSTUB_AS_IL #ifdef FEATURE_MULTICASTSTUB_AS_IL FCFuncElement("MulticastDebuggerTraceHelper", StubHelpers::MulticastDebuggerTraceHelper) #endif //FEATURE_MULTICASTSTUB_AS_IL - FCIntrinsic("NextCallReturnAddress", StubHelpers::NextCallReturnAddress, CORINFO_INTRINSIC_StubHelpers_NextCallReturnAddress) + FCFuncElement("NextCallReturnAddress", StubHelpers::NextCallReturnAddress) FCFuncEnd() FCFuncStart(gGCHandleFuncs) @@ -780,7 +759,6 @@ FCFuncEnd() #endif // FEATURE_COMINTEROP - // // // Class definitions @@ -839,7 +817,6 @@ FCClassElement("ObjectMarshaler", "System.StubHelpers", gObjectMarshalerFuncs) #endif FCClassElement("OverlappedData", "System.Threading", gOverlappedFuncs) - FCClassElement("RegisteredWaitHandle", "System.Threading", gRegisteredWaitHandleFuncs) FCClassElement("RuntimeAssembly", "System.Reflection", gRuntimeAssemblyFuncs) @@ -869,8 +846,6 @@ FCClassElement("WeakReference`1", "System", gWeakReferenceOfTFuncs) #undef FCFuncElement #undef FCFuncElementSig -#undef FCIntrinsic -#undef FCIntrinsicSig #undef FCDynamic #undef FCDynamicSig #undef FCUnreferenced diff --git a/src/coreclr/vm/interpreter.cpp b/src/coreclr/vm/interpreter.cpp index cbec8481ce4c41..0f3490ade5a056 100644 --- a/src/coreclr/vm/interpreter.cpp +++ b/src/coreclr/vm/interpreter.cpp @@ -9155,16 +9155,14 @@ void Interpreter::DoCallWork(bool virtualCall, void* thisArg, CORINFO_RESOLVED_T didIntrinsic = true; break; #if INTERP_ILSTUBS - case CORINFO_INTRINSIC_StubHelpers_GetStubContext: + // TODO: enable when NamedIntrinsic is available to interpreter + /* + case NI_System_StubHelpers_GetStubContext: OpStackSet(m_curStackHt, GetStubContext()); OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_NATIVEINT)); m_curStackHt++; didIntrinsic = true; break; - case CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr: - OpStackSet(m_curStackHt, GetStubContextAddr()); - OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_NATIVEINT)); - m_curStackHt++; didIntrinsic = true; - break; + */ #endif // INTERP_ILSTUBS default: #if INTERP_TRACING diff --git a/src/coreclr/vm/interpreter.h b/src/coreclr/vm/interpreter.h index 33a83c200922dc..a6346cebc4ccfa 100644 --- a/src/coreclr/vm/interpreter.h +++ b/src/coreclr/vm/interpreter.h @@ -907,7 +907,6 @@ class Interpreter #if INTERP_ILSTUBS void* GetStubContext() { return m_stubContext; } - void* GetStubContextAddr() { return &m_stubContext; } #endif OBJECTREF* GetAddressOfSecurityObject() { return &m_securityObject; } diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 94d7101094891e..1433856f3378ab 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -395,26 +395,6 @@ inline static CorInfoType toJitType(TypeHandle typeHnd, CORINFO_CLASS_HANDLE *cl return CEEInfo::asCorInfoType(typeHnd.GetInternalCorElementType(), typeHnd, clsRet); } -inline static void TypeEquivalenceFixupSpecificationHelper(ICorDynamicInfo * pCorInfo, MethodDesc *pMD) -{ - STANDARD_VM_CONTRACT; - - // A fixup is necessary to ensure that the parameters to the method are loaded before the method - // is called. In these cases we will not perform the appropriate loading when we load parameter - // types because with type equivalence, the parameter types at the call site do not necessarily - // match that those in the actual function. (They must be equivalent, but not necessarily the same.) - // In non-ngen scenarios this code here will force the types to be loaded directly by the call to - // HasTypeEquivalentStructParameters. - if (pMD->IsVirtual()) - { - if (pMD->GetMethodTable()->DependsOnEquivalentOrForwardedStructs()) - { - if (pMD->HasTypeEquivalentStructParameters()) - pCorInfo->classMustBeLoadedBeforeCodeIsRun((CORINFO_CLASS_HANDLE)pMD->GetMethodTable()); - } - } -} - //--------------------------------------------------------------------------------------- // //@GENERICS: @@ -5007,13 +4987,28 @@ void CEEInfo::getCallInfo( bool directCall = false; bool resolvedCallVirt = false; - bool callVirtCrossingVersionBubble = false; - // Delegate targets are always treated as direct calls here. (It would be nice to clean it up...). if (flags & CORINFO_CALLINFO_LDFTN) { - if (m_pOverride != NULL) - TypeEquivalenceFixupSpecificationHelper(m_pOverride, pTargetMD); + // Since the ldvirtftn instruction resolves types + // at run-time we do this earlier than ldftn. The + // ldftn scenario is handled later when the fixed + // address is requested by in the JIT. + // See getFunctionFixedEntryPoint(). + // + // Using ldftn or ldvirtftn on a Generic method + // requires early type loading since instantiation + // occurs at run-time as opposed to JIT time. The + // GC special cases Generic types and relaxes the + // loaded type constraint to permit Generic types + // that are loaded with Canon as opposed to being + // instantiated with an actual type. + if ((flags & CORINFO_CALLINFO_CALLVIRT) + || pTargetMD->HasMethodInstantiation()) + { + pTargetMD->PrepareForUseAsAFunctionPointer(); + } + directCall = true; } else @@ -8370,11 +8365,6 @@ CorInfoIntrinsics CEEInfo::getIntrinsicID(CORINFO_METHOD_HANDLE methodHnd, result = arrMethod->GetIntrinsicID(); } else - if (method->IsFCall()) - { - result = ECall::GetIntrinsicID(method); - } - else { MethodTable * pMT = method->GetMethodTable(); if (pMT->GetModule()->IsSystem() && pMT->IsByRefLike()) @@ -8964,6 +8954,7 @@ void CEEInfo::getFunctionEntryPoint(CORINFO_METHOD_HANDLE ftnHnd, /*********************************************************************/ void CEEInfo::getFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, + bool isUnsafeFunctionPointer, CORINFO_CONST_LOOKUP * pResult) { CONTRACTL { @@ -8976,6 +8967,9 @@ void CEEInfo::getFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, MethodDesc * pMD = GetMethod(ftn); + if (isUnsafeFunctionPointer) + pMD->PrepareForUseAsAFunctionPointer(); + pResult->accessType = IAT_VALUE; pResult->addr = (void*)pMD->GetMultiCallableAddrOfCode(); diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index e2b53f78fcd348..a684163030d566 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -509,7 +509,6 @@ DEFINE_METASIG_T(IM(Str_BindingFlags_Binder_Obj_ArrObj_ArrParameterModifier_Cult s g(BINDING_FLAGS) C(BINDER) j a(j) a(g(PARAMETER_MODIFIER)) C(CULTURE_INFO) a(s), j)) DEFINE_METASIG_T(IM(Str_Type_Str_RetVoid, s C(TYPE) s, v)) DEFINE_METASIG_T(SM(Delegate_RetIntPtr, C(DELEGATE), I)) -DEFINE_METASIG_T(SM(Delegate_RefIntPtr_RetIntPtr, C(DELEGATE) r(I), I)) DEFINE_METASIG_T(SM(RuntimeTypeHandle_RetType, g(RT_TYPE_HANDLE), C(TYPE))) DEFINE_METASIG_T(SM(RuntimeTypeHandle_RetIntPtr, g(RT_TYPE_HANDLE), I)) DEFINE_METASIG_T(SM(RuntimeMethodHandle_RetIntPtr, g(METHOD_HANDLE), I)) diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 9185f484d4de41..0461aa6b50283e 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -4002,58 +4002,6 @@ void MethodDesc::WalkValueTypeParameters(MethodTable *pMT, WalkValueTypeParamete } } - -#ifdef FEATURE_TYPEEQUIVALENCE -static void CheckForEquivalenceAndLoadType(Module *pModule, mdToken token, Module *pDefModule, mdToken defToken, const SigParser *ptr, SigTypeContext *pTypeContext, void *pData) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - } - CONTRACTL_END; - - BOOL *pHasEquivalentParam = (BOOL *)pData; - - if (IsTypeDefEquivalent(defToken, pDefModule)) - { - *pHasEquivalentParam = TRUE; - SigPointer sigPtr(*ptr); - TypeHandle th = sigPtr.GetTypeHandleThrowing(pModule, pTypeContext); - } -} -#endif // FEATURE_TYPEEQUIVALENCE - -BOOL MethodDesc::HasTypeEquivalentStructParameters() -{ -#ifdef FEATURE_TYPEEQUIVALENCE - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - BOOL fHasTypeEquivalentStructParameters = FALSE; - if (DoesNotHaveEquivalentValuetypeParameters()) - return FALSE; - - WalkValueTypeParameters(this->GetMethodTable(), CheckForEquivalenceAndLoadType, &fHasTypeEquivalentStructParameters); - - if (!fHasTypeEquivalentStructParameters) - SetDoesNotHaveEquivalentValuetypeParameters(); - - return fHasTypeEquivalentStructParameters; - -#else - LIMITED_METHOD_CONTRACT; - return FALSE; - -#endif // FEATURE_TYPEEQUIVALENCE -} - - PrecodeType MethodDesc::GetPrecodeType() { LIMITED_METHOD_CONTRACT; @@ -4124,6 +4072,54 @@ void MethodDesc::PrepareForUseAsADependencyOfANativeImageWorker() EX_END_CATCH(RethrowTerminalExceptions); _ASSERTE(HaveValueTypeParametersBeenWalked()); } + +static void CheckForEquivalenceAndLoadType(Module *pModule, mdToken token, Module *pDefModule, mdToken defToken, const SigParser *ptr, SigTypeContext *pTypeContext, void *pData) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + } + CONTRACTL_END; + + BOOL *pHasEquivalentParam = (BOOL *)pData; + +#ifdef FEATURE_TYPEEQUIVALENCE + *pHasEquivalentParam = IsTypeDefEquivalent(defToken, pDefModule); +#else + _ASSERTE(*pHasEquivalentParam == FALSE); // Assert this is always false. +#endif // FEATURE_TYPEEQUIVALENCE + + SigPointer sigPtr(*ptr); + TypeHandle th = sigPtr.GetTypeHandleThrowing(pModule, pTypeContext); + _ASSERTE(!th.IsNull()); +} + +void MethodDesc::PrepareForUseAsAFunctionPointer() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + // Since function pointers are unsafe and can enable type punning, all + // value type parameters must be loaded prior to providing a function pointer. + if (HaveValueTypeParametersBeenLoaded()) + return; + + BOOL fHasTypeEquivalentStructParameters = FALSE; + WalkValueTypeParameters(this->GetMethodTable(), CheckForEquivalenceAndLoadType, &fHasTypeEquivalentStructParameters); + +#ifdef FEATURE_TYPEEQUIVALENCE + if (!fHasTypeEquivalentStructParameters) + SetDoesNotHaveEquivalentValuetypeParameters(); +#endif // FEATURE_TYPEEQUIVALENCE + + SetValueTypeParametersLoaded(); +} #endif //!DACCESS_COMPILE #ifdef _MSC_VER diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 44818d14da1eed..fc23cd0506dea7 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -51,7 +51,7 @@ GVAL_DECL(TADDR, g_MiniMetaDataBuffAddress); EXTERN_C VOID STDCALL NDirectImportThunk(); -#define METHOD_TOKEN_REMAINDER_BIT_COUNT 14 +#define METHOD_TOKEN_REMAINDER_BIT_COUNT 13 #define METHOD_TOKEN_REMAINDER_MASK ((1 << METHOD_TOKEN_REMAINDER_BIT_COUNT) - 1) #define METHOD_TOKEN_RANGE_BIT_COUNT (24 - METHOD_TOKEN_REMAINDER_BIT_COUNT) #define METHOD_TOKEN_RANGE_MASK ((1 << METHOD_TOKEN_RANGE_BIT_COUNT) - 1) @@ -1652,8 +1652,6 @@ class MethodDesc VOID GetMethodInfoNoSig(SString &namespaceOrClassName, SString &methodName); VOID GetFullMethodInfo(SString& fullMethodSigName); - BOOL HasTypeEquivalentStructParameters(); - typedef void (*WalkValueTypeParameterFnPtr)(Module *pModule, mdToken token, Module *pDefModule, mdToken tkDefToken, const SigParser *ptr, SigTypeContext *pTypeContext, void *pData); void WalkValueTypeParameters(MethodTable *pMT, WalkValueTypeParameterFnPtr function, void *pData); @@ -1665,6 +1663,9 @@ class MethodDesc PrepareForUseAsADependencyOfANativeImageWorker(); } + void PrepareForUseAsAFunctionPointer(); + +private: void PrepareForUseAsADependencyOfANativeImageWorker(); //================================================================ @@ -1674,13 +1675,12 @@ class MethodDesc enum { // There are flags available for use here (currently 5 flags bits are available); however, new bits are hard to come by, so any new flags bits should // have a fairly strong justification for existence. - enum_flag3_TokenRemainderMask = 0x3FFF, // This must equal METHOD_TOKEN_REMAINDER_MASK calculated higher in this file - // These are seperate to allow the flags space available and used to be obvious here - // and for the logic that splits the token to be algorithmically generated based on the - // #define - // unused = 0x4000, - enum_flag3_ValueTypeParametersWalked = 0x4000, // Indicates that all typeref's in the signature of the method have been resolved to typedefs (or that process failed) (this flag is only valid for non-ngenned methods) - enum_flag3_DoesNotHaveEquivalentValuetypeParameters = 0x8000, // Indicates that we have verified that there are no equivalent valuetype parameters for this method + enum_flag3_TokenRemainderMask = 0x1FFF, // This must equal METHOD_TOKEN_REMAINDER_MASK calculated higher in this file. + enum_flag3_ValueTypeParametersWalked = 0x2000, // Indicates that all typeref's in the signature of the method have been resolved + // to typedefs (or that process failed). + enum_flag3_ValueTypeParametersLoaded = 0x4000, // Indicates if the valuetype parameter types have been loaded. + enum_flag3_DoesNotHaveEquivalentValuetypeParameters = 0x8000, // Indicates that we have verified that there are no equivalent valuetype parameters + // for this method. }; UINT16 m_wFlags3AndTokenRemainder; @@ -1794,32 +1794,44 @@ class MethodDesc WORD InterlockedUpdateFlags3(WORD wMask, BOOL fSet); -#ifdef FEATURE_TYPEEQUIVALENCE - inline BOOL DoesNotHaveEquivalentValuetypeParameters() + inline BOOL HaveValueTypeParametersBeenWalked() { LIMITED_METHOD_DAC_CONTRACT; - return (m_wFlags3AndTokenRemainder & enum_flag3_DoesNotHaveEquivalentValuetypeParameters) != 0; + return (m_wFlags3AndTokenRemainder & enum_flag3_ValueTypeParametersWalked) != 0; } - inline void SetDoesNotHaveEquivalentValuetypeParameters() + inline void SetValueTypeParametersWalked() { LIMITED_METHOD_CONTRACT; - InterlockedUpdateFlags3(enum_flag3_DoesNotHaveEquivalentValuetypeParameters, TRUE); + InterlockedUpdateFlags3(enum_flag3_ValueTypeParametersWalked, TRUE); } -#endif // FEATURE_TYPEEQUIVALENCE - inline BOOL HaveValueTypeParametersBeenWalked() + inline BOOL HaveValueTypeParametersBeenLoaded() { LIMITED_METHOD_DAC_CONTRACT; - return (m_wFlags3AndTokenRemainder & enum_flag3_ValueTypeParametersWalked) != 0; + return (m_wFlags3AndTokenRemainder & enum_flag3_ValueTypeParametersLoaded) != 0; } - inline void SetValueTypeParametersWalked() + inline void SetValueTypeParametersLoaded() { LIMITED_METHOD_CONTRACT; - InterlockedUpdateFlags3(enum_flag3_ValueTypeParametersWalked, TRUE); + InterlockedUpdateFlags3(enum_flag3_ValueTypeParametersLoaded, TRUE); + } + +#ifdef FEATURE_TYPEEQUIVALENCE + inline BOOL DoesNotHaveEquivalentValuetypeParameters() + { + LIMITED_METHOD_DAC_CONTRACT; + return (m_wFlags3AndTokenRemainder & enum_flag3_DoesNotHaveEquivalentValuetypeParameters) != 0; } + inline void SetDoesNotHaveEquivalentValuetypeParameters() + { + LIMITED_METHOD_CONTRACT; + InterlockedUpdateFlags3(enum_flag3_DoesNotHaveEquivalentValuetypeParameters, TRUE); + } +#endif // FEATURE_TYPEEQUIVALENCE + // // Optional MethodDesc slots appear after the end of base MethodDesc in this order: // @@ -2159,7 +2171,7 @@ class MethodDescChunk friend class CheckAsmOffsets; enum { - enum_flag_TokenRangeMask = 0x03FF, // This must equal METHOD_TOKEN_RANGE_MASK calculated higher in this file + enum_flag_TokenRangeMask = 0x07FF, // This must equal METHOD_TOKEN_RANGE_MASK calculated higher in this file // These are seperate to allow the flags space available and used to be obvious here // and for the logic that splits the token to be algorithmically generated based on the // #define @@ -2594,7 +2606,7 @@ class DynamicMethodDesc : public StoredSigMethodDesc bool HasMDContextArg() { LIMITED_METHOD_CONTRACT; - return IsCLRToCOMStub() || IsPInvokeStub(); + return IsCLRToCOMStub() || (IsPInvokeStub() && !IsDelegateStub()); } // diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index a0b269b38d8e1f..41057e01620d38 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -1642,9 +1642,9 @@ extern "C" void * QCALLTYPE RuntimeMethodHandle_GetFunctionPointer(MethodDesc * BEGIN_QCALL; - // Ensure the method is active so - // the function pointer can be used. + // Ensure the method is active and all types have been loaded so the function pointer can be used. pMethod->EnsureActive(); + pMethod->PrepareForUseAsAFunctionPointer(); funcPtr = (void*)pMethod->GetMultiCallableAddrOfCode(); END_QCALL; diff --git a/src/coreclr/vm/stubhelpers.cpp b/src/coreclr/vm/stubhelpers.cpp index 8e15d3aa54a834..4fb4e9ff8c656d 100644 --- a/src/coreclr/vm/stubhelpers.cpp +++ b/src/coreclr/vm/stubhelpers.cpp @@ -4,9 +4,6 @@ // File: stubhelpers.cpp // -// - - #include "common.h" #include "mlinfo.h" @@ -504,7 +501,7 @@ FCIMPL1(void*, StubHelpers::GetNDirectTarget, NDirectMethodDesc* pNMD) } FCIMPLEND -FCIMPL2(void*, StubHelpers::GetDelegateTarget, DelegateObject *pThisUNSAFE, UINT_PTR *ppStubArg) +FCIMPL1(void*, StubHelpers::GetDelegateTarget, DelegateObject *pThisUNSAFE) { PCODE pEntryPoint = NULL; @@ -527,19 +524,9 @@ FCIMPL2(void*, StubHelpers::GetDelegateTarget, DelegateObject *pThisUNSAFE, UINT // See code:GenericPInvokeCalliHelper // The lowest bit is used to distinguish between MD and target on 64-bit. target = (target << 1) | 1; +#endif // HOST_64BIT - // On 64-bit we pass the real target to the stub-for-host through this out argument, - // see IL code gen in NDirectStubLinker::DoNDirect for details. - *ppStubArg = target; - -#elif defined(TARGET_ARM) - // @ARMTODO: Nothing to do for ARM yet since we don't support the hosted path. -#endif // HOST_64BIT, TARGET_ARM - - if (pEntryPoint == NULL) - { - pEntryPoint = orefThis->GetMethodPtrAux(); - } + pEntryPoint = orefThis->GetMethodPtrAux(); #ifdef _DEBUG END_PRESERVE_LAST_ERROR; @@ -980,17 +967,6 @@ FCIMPL2(void, StubHelpers::LogPinnedArgument, MethodDesc *target, Object *pinned } FCIMPLEND -#ifdef TARGET_64BIT -FCIMPL0(void*, StubHelpers::GetStubContextAddr) -{ - FCALL_CONTRACT; - - FCUnique(0xa1); - UNREACHABLE_MSG("This is a JIT intrinsic!"); -} -FCIMPLEND -#endif // TARGET_64BIT - FCIMPL1(DWORD, StubHelpers::CalcVaListSize, VARARGS *varargs) { FCALL_CONTRACT; diff --git a/src/coreclr/vm/stubhelpers.h b/src/coreclr/vm/stubhelpers.h index 308af895ccadc7..6beba1f163955f 100644 --- a/src/coreclr/vm/stubhelpers.h +++ b/src/coreclr/vm/stubhelpers.h @@ -4,9 +4,6 @@ // File: stubhelpers.h // -// - - #ifndef __STUBHELPERS_h__ #define __STUBHELPERS_h__ @@ -60,8 +57,7 @@ class StubHelpers static FCDECL0(void, SetLastError ); static FCDECL0(void, ClearLastError ); static FCDECL1(void*, GetNDirectTarget, NDirectMethodDesc* pNMD); - static FCDECL2(void*, GetDelegateTarget, DelegateObject *pThisUNSAFE, UINT_PTR *ppStubArg); - + static FCDECL1(void*, GetDelegateTarget, DelegateObject *pThisUNSAFE); static FCDECL2(void, ThrowInteropParamException, UINT resID, UINT paramIdx); static FCDECL1(Object*, GetHRExceptionObject, HRESULT hr); @@ -80,9 +76,6 @@ class StubHelpers static FCDECL2(void, MarshalToManagedVaListInternal, va_list va, VARARGS* pArgIterator); static FCDECL0(void*, GetStubContext); static FCDECL2(void, LogPinnedArgument, MethodDesc *localDesc, Object *nativeArg); -#ifdef TARGET_64BIT - static FCDECL0(void*, GetStubContextAddr); -#endif // TARGET_64BIT static FCDECL1(DWORD, CalcVaListSize, VARARGS *varargs); static FCDECL3(void, ValidateObject, Object *pObjUNSAFE, MethodDesc *pMD, Object *pThisUNSAFE); static FCDECL3(void, ValidateByref, void *pByref, MethodDesc *pMD, Object *pThisUNSAFE); diff --git a/src/installer/pkg/sfx/installers/dotnet-host.proj b/src/installer/pkg/sfx/installers/dotnet-host.proj index 7a7d6a57f21c47..edf3f5bf753fe5 100644 --- a/src/installer/pkg/sfx/installers/dotnet-host.proj +++ b/src/installer/pkg/sfx/installers/dotnet-host.proj @@ -4,6 +4,7 @@ true dotnet-host dotnet-host-internal + / Host NetCore.SharedHost true @@ -19,9 +20,6 @@ false osx_scripts/host The .NET Shared Host. - $(MSBuildThisFileDirectory)rpm_scripts/host - $(RpmScriptsDirectory)/after_install.sh - $(RpmScriptsDirectory)/after_remove.sh dotnet-host $(MajorVersion).$(MinorVersion) $(Platform) @@ -41,21 +39,37 @@ + + <_DestinationPath>$(OutputPath) + <_DestinationPath Condition="'$(BuildRpmPackage)' == 'true'">$(OutputPath)usr/share/dotnet/ + <_UsrBinPath>$(OutputPath)usr/bin/ + + + + + + Destination="$(_DestinationPath)dotnet$(ExeSuffix)" /> + Destination="$(_DestinationPath)ThirdPartyNotices.txt" /> + + + + @@ -77,8 +91,6 @@ - - diff --git a/src/installer/pkg/sfx/installers/rpm_scripts/host/after_install.sh b/src/installer/pkg/sfx/installers/rpm_scripts/host/after_install.sh deleted file mode 100644 index 2a588366faa952..00000000000000 --- a/src/installer/pkg/sfx/installers/rpm_scripts/host/after_install.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -if [[ -L /usr/local/bin/dotnet ]]; then - rm /usr/local/bin/dotnet -fi -ln -s /usr/share/dotnet/dotnet /usr/local/bin/dotnet - diff --git a/src/installer/pkg/sfx/installers/rpm_scripts/host/after_remove.sh b/src/installer/pkg/sfx/installers/rpm_scripts/host/after_remove.sh deleted file mode 100644 index 35fb8359a54b28..00000000000000 --- a/src/installer/pkg/sfx/installers/rpm_scripts/host/after_remove.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -if [[ -L /usr/local/bin/dotnet ]]; then - rm /usr/local/bin/dotnet -fi - diff --git a/src/libraries/Common/src/Internal/Cryptography/HashProviderCng.cs b/src/libraries/Common/src/Internal/Cryptography/HashProviderCng.cs index 3b864790b25252..c905ca1bd6996e 100644 --- a/src/libraries/Common/src/Internal/Cryptography/HashProviderCng.cs +++ b/src/libraries/Common/src/Internal/Cryptography/HashProviderCng.cs @@ -127,10 +127,15 @@ public override void Reset() { if (_reusable && !_running) return; + DestroyHash(); + BCryptCreateHashFlags flags = _reusable ? + BCryptCreateHashFlags.BCRYPT_HASH_REUSABLE_FLAG : + BCryptCreateHashFlags.None; + SafeBCryptHashHandle hHash; - NTSTATUS ntStatus = Interop.BCrypt.BCryptCreateHash(_hAlgorithm, out hHash, IntPtr.Zero, 0, _key, _key == null ? 0 : _key.Length, BCryptCreateHashFlags.None); + NTSTATUS ntStatus = Interop.BCrypt.BCryptCreateHash(_hAlgorithm, out hHash, IntPtr.Zero, 0, _key, _key == null ? 0 : _key.Length, flags); if (ntStatus != NTSTATUS.STATUS_SUCCESS) throw Interop.BCrypt.CreateCryptographicException(ntStatus); diff --git a/src/libraries/Common/src/Interop/Android/Interop.Logcat.cs b/src/libraries/Common/src/Interop/Android/Interop.Logcat.cs index fe35fd18da6376..690555d824665d 100644 --- a/src/libraries/Common/src/Interop/Android/Interop.Logcat.cs +++ b/src/libraries/Common/src/Interop/Android/Interop.Logcat.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class Logcat { - [DllImport(Libraries.Liblog)] - private static extern void __android_log_print(LogLevel level, string? tag, string format, string args, IntPtr ptr); + [GeneratedDllImport(Libraries.Liblog, CharSet = CharSet.Ansi)] + private static partial void __android_log_print(LogLevel level, string? tag, string format, string args, IntPtr ptr); internal static void AndroidLogPrint(LogLevel level, string? tag, string message) => __android_log_print(level, tag, "%s", message, IntPtr.Zero); diff --git a/src/libraries/Common/src/Interop/Interop.ICU.iOS.cs b/src/libraries/Common/src/Interop/Interop.ICU.iOS.cs index 8eb0ee177ab8d4..96c64d7aaafb30 100644 --- a/src/libraries/Common/src/Interop/Interop.ICU.iOS.cs +++ b/src/libraries/Common/src/Interop/Interop.ICU.iOS.cs @@ -8,7 +8,7 @@ internal static partial class Interop { internal static partial class Globalization { - [DllImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_LoadICUData")] - internal static extern int LoadICUData(string path); + [GeneratedDllImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_LoadICUData", CharSet = CharSet.Ansi)] + internal static partial int LoadICUData(string path); } } diff --git a/src/libraries/Common/src/Interop/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Interop.Ldap.cs index 84d78f6002e510..c62e99f4b4f285 100644 --- a/src/libraries/Common/src/Interop/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Interop.Ldap.cs @@ -120,19 +120,19 @@ internal sealed class LDAP_TIMEVAL } [StructLayout(LayoutKind.Sequential)] - internal sealed class berval + internal sealed class BerVal { public int bv_len; public IntPtr bv_val = IntPtr.Zero; - public berval() { } + public BerVal() { } } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal sealed class LdapControl { public IntPtr ldctl_oid = IntPtr.Zero; - public berval ldctl_value; + public BerVal ldctl_value; public bool ldctl_iscritical; public LdapControl() { } diff --git a/src/libraries/Common/src/Interop/Interop.zlib.cs b/src/libraries/Common/src/Interop/Interop.zlib.cs index e0e2785cef4f74..d764bf865310f8 100644 --- a/src/libraries/Common/src/Interop/Interop.zlib.cs +++ b/src/libraries/Common/src/Interop/Interop.zlib.cs @@ -6,7 +6,7 @@ internal static partial class Interop { - internal static partial class zlib + internal static partial class ZLib { [GeneratedDllImport(Libraries.CompressionNative, EntryPoint = "CompressionNative_DeflateInit2_")] internal static unsafe partial ZLibNative.ErrorCode DeflateInit2_( diff --git a/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ber.cs b/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ber.cs index 4ee4bc8bab9432..468b73fe4962c7 100644 --- a/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ber.cs +++ b/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ber.cs @@ -12,14 +12,17 @@ internal static partial class Ldap { public const int ber_default_successful_return_code = 0; - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_alloc_t", CharSet = CharSet.Ansi)] - public static extern IntPtr ber_alloc(int option); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_alloc_t", CharSet = CharSet.Ansi)] + public static partial IntPtr ber_alloc(int option); +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. [DllImport(Libraries.OpenLdap, EntryPoint = "ber_init", CharSet = CharSet.Ansi)] - public static extern IntPtr ber_init(berval value); + public static extern IntPtr ber_init(BerVal value); +#pragma warning restore DLLIMPORTGENANALYZER015 - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_free", CharSet = CharSet.Ansi)] - public static extern IntPtr ber_free([In] IntPtr berelement, int option); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_free", CharSet = CharSet.Ansi)] + public static partial IntPtr ber_free(IntPtr berelement, int option); public static int ber_printf_emptyarg(SafeBerHandle berElement, string format, nuint tag) { @@ -46,20 +49,20 @@ public static int ber_printf_emptyarg(SafeBerHandle berElement, string format, n } } - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_start_seq", CharSet = CharSet.Ansi)] - public static extern int ber_start_seq(SafeBerHandle berElement, nuint tag); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_start_seq", CharSet = CharSet.Ansi)] + public static partial int ber_start_seq(SafeBerHandle berElement, nuint tag); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_start_set", CharSet = CharSet.Ansi)] - public static extern int ber_start_set(SafeBerHandle berElement, nuint tag); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_start_set", CharSet = CharSet.Ansi)] + public static partial int ber_start_set(SafeBerHandle berElement, nuint tag); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_put_seq", CharSet = CharSet.Ansi)] - public static extern int ber_put_seq(SafeBerHandle berElement, nuint tag); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_put_seq", CharSet = CharSet.Ansi)] + public static partial int ber_put_seq(SafeBerHandle berElement, nuint tag); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_put_set", CharSet = CharSet.Ansi)] - public static extern int ber_put_set(SafeBerHandle berElement, nuint tag); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_put_set", CharSet = CharSet.Ansi)] + public static partial int ber_put_set(SafeBerHandle berElement, nuint tag); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_put_null", CharSet = CharSet.Ansi)] - public static extern int ber_put_null(SafeBerHandle berElement, nuint tag); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_put_null", CharSet = CharSet.Ansi)] + public static partial int ber_put_null(SafeBerHandle berElement, nuint tag); public static int ber_printf_int(SafeBerHandle berElement, string format, int value, nuint tag) { @@ -78,14 +81,14 @@ public static int ber_printf_int(SafeBerHandle berElement, string format, int va } } - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_put_int", CharSet = CharSet.Ansi)] - public static extern int ber_put_int(SafeBerHandle berElement, int value, nuint tag); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_put_int", CharSet = CharSet.Ansi)] + public static partial int ber_put_int(SafeBerHandle berElement, int value, nuint tag); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_put_enum", CharSet = CharSet.Ansi)] - public static extern int ber_put_enum(SafeBerHandle berElement, int value, nuint tag); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_put_enum", CharSet = CharSet.Ansi)] + public static partial int ber_put_enum(SafeBerHandle berElement, int value, nuint tag); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_put_boolean", CharSet = CharSet.Ansi)] - public static extern int ber_put_boolean(SafeBerHandle berElement, int value, nuint tag); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_put_boolean", CharSet = CharSet.Ansi)] + public static partial int ber_put_boolean(SafeBerHandle berElement, int value, nuint tag); public static int ber_printf_bytearray(SafeBerHandle berElement, string format, HGlobalMemHandle value, nuint length, nuint tag) { @@ -104,23 +107,23 @@ public static int ber_printf_bytearray(SafeBerHandle berElement, string format, } } - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_put_ostring", CharSet = CharSet.Ansi)] - private static extern int ber_put_ostring(SafeBerHandle berElement, HGlobalMemHandle value, nuint length, nuint tag); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_put_ostring", CharSet = CharSet.Ansi)] + private static partial int ber_put_ostring(SafeBerHandle berElement, HGlobalMemHandle value, nuint length, nuint tag); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_put_string", CharSet = CharSet.Ansi)] - private static extern int ber_put_string(SafeBerHandle berElement, HGlobalMemHandle value, nuint tag); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_put_string", CharSet = CharSet.Ansi)] + private static partial int ber_put_string(SafeBerHandle berElement, HGlobalMemHandle value, nuint tag); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_put_bitstring", CharSet = CharSet.Ansi)] - private static extern int ber_put_bitstring(SafeBerHandle berElement, HGlobalMemHandle value, nuint length, nuint tag); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_put_bitstring", CharSet = CharSet.Ansi)] + private static partial int ber_put_bitstring(SafeBerHandle berElement, HGlobalMemHandle value, nuint length, nuint tag); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_flatten", CharSet = CharSet.Ansi)] - public static extern int ber_flatten(SafeBerHandle berElement, ref IntPtr value); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_flatten", CharSet = CharSet.Ansi)] + public static partial int ber_flatten(SafeBerHandle berElement, ref IntPtr value); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_bvfree", CharSet = CharSet.Ansi)] - public static extern int ber_bvfree(IntPtr value); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_bvfree", CharSet = CharSet.Ansi)] + public static partial int ber_bvfree(IntPtr value); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_bvecfree", CharSet = CharSet.Ansi)] - public static extern int ber_bvecfree(IntPtr value); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_bvecfree", CharSet = CharSet.Ansi)] + public static partial int ber_bvecfree(IntPtr value); public static int ber_scanf_emptyarg(SafeBerHandle berElement, string format) { @@ -141,11 +144,11 @@ public static int ber_scanf_emptyarg(SafeBerHandle berElement, string format) } } - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_skip_tag", CharSet = CharSet.Ansi)] - private static extern int ber_skip_tag(SafeBerHandle berElement, ref nuint len); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_skip_tag", CharSet = CharSet.Ansi)] + private static partial int ber_skip_tag(SafeBerHandle berElement, ref nuint len); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_get_null", CharSet = CharSet.Ansi)] - private static extern int ber_get_null(SafeBerHandle berElement); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_get_null", CharSet = CharSet.Ansi)] + private static partial int ber_get_null(SafeBerHandle berElement); public static int ber_scanf_int(SafeBerHandle berElement, string format, ref int value) { @@ -164,14 +167,14 @@ public static int ber_scanf_int(SafeBerHandle berElement, string format, ref int } } - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_get_int", CharSet = CharSet.Ansi)] - private static extern int ber_get_int(SafeBerHandle berElement, ref int value); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_get_int", CharSet = CharSet.Ansi)] + private static partial int ber_get_int(SafeBerHandle berElement, ref int value); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_get_enum", CharSet = CharSet.Ansi)] - private static extern int ber_get_enum(SafeBerHandle berElement, ref int value); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_get_enum", CharSet = CharSet.Ansi)] + private static partial int ber_get_enum(SafeBerHandle berElement, ref int value); - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_get_boolean", CharSet = CharSet.Ansi)] - private static extern int ber_get_boolean(SafeBerHandle berElement, ref int value); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_get_boolean", CharSet = CharSet.Ansi)] + private static partial int ber_get_boolean(SafeBerHandle berElement, ref int value); public static int ber_scanf_bitstring(SafeBerHandle berElement, string format, ref IntPtr value, ref uint bitLength) { @@ -182,8 +185,8 @@ public static int ber_scanf_bitstring(SafeBerHandle berElement, string format, r return res; } - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_get_stringb", CharSet = CharSet.Ansi)] - private static extern int ber_get_stringb(SafeBerHandle berElement, ref IntPtr value, ref nuint bitLength); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_get_stringb", CharSet = CharSet.Ansi)] + private static partial int ber_get_stringb(SafeBerHandle berElement, ref IntPtr value, ref nuint bitLength); public static int ber_scanf_ptr(SafeBerHandle berElement, string format, ref IntPtr value) { @@ -191,8 +194,8 @@ public static int ber_scanf_ptr(SafeBerHandle berElement, string format, ref Int return ber_get_stringal(berElement, ref value); } - [DllImport(Libraries.OpenLdap, EntryPoint = "ber_get_stringal", CharSet = CharSet.Ansi)] - private static extern int ber_get_stringal(SafeBerHandle berElement, ref IntPtr value); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ber_get_stringal", CharSet = CharSet.Ansi)] + private static partial int ber_get_stringal(SafeBerHandle berElement, ref IntPtr value); public static int ber_printf_berarray(SafeBerHandle berElement, string format, IntPtr value, nuint tag) { diff --git a/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs index 7fbaf9cc32eab8..8a8d3b01b0bdf8 100644 --- a/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs @@ -76,149 +76,167 @@ static Ldap() ldap_get_option_int(IntPtr.Zero, LdapOption.LDAP_OPT_DEBUG_LEVEL, ref unused); } - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_initialize", CharSet = CharSet.Ansi, SetLastError = true)] - public static extern int ldap_initialize(out IntPtr ld, string uri); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_initialize", CharSet = CharSet.Ansi, SetLastError = true)] + public static partial int ldap_initialize(out IntPtr ld, string uri); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_unbind_ext_s", CharSet = CharSet.Ansi)] - public static extern int ldap_unbind_ext_s(IntPtr ld, ref IntPtr serverctrls, ref IntPtr clientctrls); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_unbind_ext_s", CharSet = CharSet.Ansi)] + public static partial int ldap_unbind_ext_s(IntPtr ld, ref IntPtr serverctrls, ref IntPtr clientctrls); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_dn", CharSet = CharSet.Ansi)] - public static extern IntPtr ldap_get_dn([In] ConnectionHandle ldapHandle, [In] IntPtr result); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_dn", CharSet = CharSet.Ansi)] + public static partial IntPtr ldap_get_dn(ConnectionHandle ldapHandle, IntPtr result); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_option", CharSet = CharSet.Ansi)] - public static extern int ldap_get_option_bool([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref bool outValue); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_option", CharSet = CharSet.Ansi)] + public static partial int ldap_get_option_bool(ConnectionHandle ldapHandle, LdapOption option, ref bool outValue); +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] We need to manually convert SecurityPackageContextConnectionInformation to marshal differently as layout classes are not supported in generated interop. [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_option", CharSet = CharSet.Ansi)] - public static extern int ldap_get_option_secInfo([In] ConnectionHandle ldapHandle, [In] LdapOption option, [In, Out] SecurityPackageContextConnectionInformation outValue); + public static extern int ldap_get_option_secInfo(ConnectionHandle ldapHandle, LdapOption option, [In, Out] SecurityPackageContextConnectionInformation outValue); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_option", CharSet = CharSet.Ansi)] - public static extern int ldap_get_option_sechandle([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref SecurityHandle outValue); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_option", CharSet = CharSet.Ansi)] + public static partial int ldap_get_option_sechandle(ConnectionHandle ldapHandle, LdapOption option, ref SecurityHandle outValue); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_option", CharSet = CharSet.Ansi)] - private static extern int ldap_get_option_int([In] IntPtr ldapHandle, [In] LdapOption option, ref int outValue); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_option", CharSet = CharSet.Ansi)] + private static partial int ldap_get_option_int(IntPtr ldapHandle, LdapOption option, ref int outValue); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_option", CharSet = CharSet.Ansi)] - public static extern int ldap_get_option_int([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref int outValue); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_option", CharSet = CharSet.Ansi)] + public static partial int ldap_get_option_int(ConnectionHandle ldapHandle, LdapOption option, ref int outValue); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_option", CharSet = CharSet.Ansi)] - public static extern int ldap_get_option_ptr([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref IntPtr outValue); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_option", CharSet = CharSet.Ansi)] + public static partial int ldap_get_option_ptr(ConnectionHandle ldapHandle, LdapOption option, ref IntPtr outValue); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_values_len", CharSet = CharSet.Ansi)] - public static extern IntPtr ldap_get_values_len([In] ConnectionHandle ldapHandle, [In] IntPtr result, string name); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_get_values_len", CharSet = CharSet.Ansi)] + public static partial IntPtr ldap_get_values_len(ConnectionHandle ldapHandle, IntPtr result, string name); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_result", SetLastError = true, CharSet = CharSet.Ansi)] - public static extern int ldap_result([In] ConnectionHandle ldapHandle, int messageId, int all, LDAP_TIMEVAL timeout, ref IntPtr Mesage); +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. + [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_result", CharSet = CharSet.Ansi, SetLastError = true)] + public static extern int ldap_result(ConnectionHandle ldapHandle, int messageId, int all, LDAP_TIMEVAL timeout, ref IntPtr Mesage); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_result2error", CharSet = CharSet.Ansi)] - public static extern int ldap_result2error([In] ConnectionHandle ldapHandle, [In] IntPtr result, int freeIt); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_result2error", CharSet = CharSet.Ansi)] + public static partial int ldap_result2error(ConnectionHandle ldapHandle, IntPtr result, int freeIt); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_search_ext", CharSet = CharSet.Ansi)] - public static extern int ldap_search([In] ConnectionHandle ldapHandle, string dn, int scope, string filter, IntPtr attributes, bool attributeOnly, IntPtr servercontrol, IntPtr clientcontrol, int timelimit, int sizelimit, ref int messageNumber); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_search_ext", CharSet = CharSet.Ansi)] + public static partial int ldap_search(ConnectionHandle ldapHandle, string dn, int scope, string filter, IntPtr attributes, bool attributeOnly, IntPtr servercontrol, IntPtr clientcontrol, int timelimit, int sizelimit, ref int messageNumber); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi, SetLastError = true)] - public static extern int ldap_set_option_bool([In] ConnectionHandle ld, [In] LdapOption option, bool value); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi, SetLastError = true)] + public static partial int ldap_set_option_bool(ConnectionHandle ld, LdapOption option, bool value); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi)] - public static extern int ldap_set_option_clientcert([In] ConnectionHandle ldapHandle, [In] LdapOption option, QUERYCLIENTCERT outValue); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi)] + public static partial int ldap_set_option_clientcert(ConnectionHandle ldapHandle, LdapOption option, QUERYCLIENTCERT outValue); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi)] - public static extern int ldap_set_option_servercert([In] ConnectionHandle ldapHandle, [In] LdapOption option, VERIFYSERVERCERT outValue); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi)] + public static partial int ldap_set_option_servercert(ConnectionHandle ldapHandle, LdapOption option, VERIFYSERVERCERT outValue); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi, SetLastError = true)] - public static extern int ldap_set_option_int([In] ConnectionHandle ld, [In] LdapOption option, ref int inValue); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi, SetLastError = true)] + public static partial int ldap_set_option_int(ConnectionHandle ld, LdapOption option, ref int inValue); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi)] - public static extern int ldap_set_option_ptr([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref IntPtr inValue); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi)] + public static partial int ldap_set_option_ptr(ConnectionHandle ldapHandle, LdapOption option, ref IntPtr inValue); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi)] - public static extern int ldap_set_option_string([In] ConnectionHandle ldapHandle, [In] LdapOption option, string inValue); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi)] + public static partial int ldap_set_option_string(ConnectionHandle ldapHandle, LdapOption option, string inValue); +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_set_option", CharSet = CharSet.Ansi)] - public static extern int ldap_set_option_referral([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref LdapReferralCallback outValue); + public static extern int ldap_set_option_referral(ConnectionHandle ldapHandle, LdapOption option, ref LdapReferralCallback outValue); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time // Note that ldap_start_tls_s has a different signature across Windows LDAP and OpenLDAP - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_start_tls_s", CharSet = CharSet.Ansi)] - public static extern int ldap_start_tls(ConnectionHandle ldapHandle, IntPtr serverControls, IntPtr clientControls); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_start_tls_s", CharSet = CharSet.Ansi)] + public static partial int ldap_start_tls(ConnectionHandle ldapHandle, IntPtr serverControls, IntPtr clientControls); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_parse_result", CharSet = CharSet.Ansi)] - public static extern int ldap_parse_result([In] ConnectionHandle ldapHandle, [In] IntPtr result, ref int serverError, ref IntPtr dn, ref IntPtr message, ref IntPtr referral, ref IntPtr control, byte freeIt); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_parse_result", CharSet = CharSet.Ansi)] + public static partial int ldap_parse_result(ConnectionHandle ldapHandle, IntPtr result, ref int serverError, ref IntPtr dn, ref IntPtr message, ref IntPtr referral, ref IntPtr control, byte freeIt); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_parse_result", CharSet = CharSet.Ansi)] - public static extern int ldap_parse_result_referral([In] ConnectionHandle ldapHandle, [In] IntPtr result, IntPtr serverError, IntPtr dn, IntPtr message, ref IntPtr referral, IntPtr control, byte freeIt); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_parse_result", CharSet = CharSet.Ansi)] + public static partial int ldap_parse_result_referral(ConnectionHandle ldapHandle, IntPtr result, IntPtr serverError, IntPtr dn, IntPtr message, ref IntPtr referral, IntPtr control, byte freeIt); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_parse_extended_result", CharSet = CharSet.Ansi)] - public static extern int ldap_parse_extended_result([In] ConnectionHandle ldapHandle, [In] IntPtr result, ref IntPtr oid, ref IntPtr data, byte freeIt); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_parse_extended_result", CharSet = CharSet.Ansi)] + public static partial int ldap_parse_extended_result(ConnectionHandle ldapHandle, IntPtr result, ref IntPtr oid, ref IntPtr data, byte freeIt); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_parse_reference", CharSet = CharSet.Ansi)] - public static extern int ldap_parse_reference([In] ConnectionHandle ldapHandle, [In] IntPtr result, ref IntPtr referrals, IntPtr ServerControls, byte freeIt); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_parse_reference", CharSet = CharSet.Ansi)] + public static partial int ldap_parse_reference(ConnectionHandle ldapHandle, IntPtr result, ref IntPtr referrals, IntPtr ServerControls, byte freeIt); +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_sasl_bind_s", CharSet = CharSet.Ansi)] - internal static extern int ldap_sasl_bind([In] ConnectionHandle ld, string dn, string mechanism, berval cred, IntPtr serverctrls, IntPtr clientctrls, IntPtr servercredp); + internal static extern int ldap_sasl_bind(ConnectionHandle ld, string dn, string mechanism, BerVal cred, IntPtr serverctrls, IntPtr clientctrls, IntPtr servercredp); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_sasl_interactive_bind_s", CharSet = CharSet.Ansi)] - internal static extern int ldap_sasl_interactive_bind([In] ConnectionHandle ld, string dn, string mechanism, IntPtr serverctrls, IntPtr clientctrls, uint flags, [MarshalAs(UnmanagedType.FunctionPtr)] LDAP_SASL_INTERACT_PROC proc, IntPtr defaults); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_sasl_interactive_bind_s", CharSet = CharSet.Ansi)] + internal static partial int ldap_sasl_interactive_bind(ConnectionHandle ld, string dn, string mechanism, IntPtr serverctrls, IntPtr clientctrls, uint flags, LDAP_SASL_INTERACT_PROC proc, IntPtr defaults); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_err2string", CharSet = CharSet.Ansi)] - public static extern IntPtr ldap_err2string(int err); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_err2string", CharSet = CharSet.Ansi)] + public static partial IntPtr ldap_err2string(int err); +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_extended_operation", CharSet = CharSet.Ansi)] - public static extern int ldap_extended_operation([In] ConnectionHandle ldapHandle, string oid, berval data, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); + public static extern int ldap_extended_operation(ConnectionHandle ldapHandle, string oid, BerVal data, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_first_attribute", CharSet = CharSet.Ansi)] - public static extern IntPtr ldap_first_attribute([In] ConnectionHandle ldapHandle, [In] IntPtr result, ref IntPtr address); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_first_attribute", CharSet = CharSet.Ansi)] + public static partial IntPtr ldap_first_attribute(ConnectionHandle ldapHandle, IntPtr result, ref IntPtr address); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_first_entry", CharSet = CharSet.Ansi)] - public static extern IntPtr ldap_first_entry([In] ConnectionHandle ldapHandle, [In] IntPtr result); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_first_entry", CharSet = CharSet.Ansi)] + public static partial IntPtr ldap_first_entry(ConnectionHandle ldapHandle, IntPtr result); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_first_reference", CharSet = CharSet.Ansi)] - public static extern IntPtr ldap_first_reference([In] ConnectionHandle ldapHandle, [In] IntPtr result); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_first_reference", CharSet = CharSet.Ansi)] + public static partial IntPtr ldap_first_reference(ConnectionHandle ldapHandle, IntPtr result); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_create_sort_control", CharSet = CharSet.Ansi)] - public static extern int ldap_create_sort_control(ConnectionHandle handle, IntPtr keys, byte critical, ref IntPtr control); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_create_sort_control", CharSet = CharSet.Ansi)] + public static partial int ldap_create_sort_control(ConnectionHandle handle, IntPtr keys, byte critical, ref IntPtr control); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_control_free", CharSet = CharSet.Ansi)] - public static extern int ldap_control_free(IntPtr control); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_control_free", CharSet = CharSet.Ansi)] + public static partial int ldap_control_free(IntPtr control); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_controls_free", CharSet = CharSet.Ansi)] - public static extern int ldap_controls_free([In] IntPtr value); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_controls_free", CharSet = CharSet.Ansi)] + public static partial int ldap_controls_free(IntPtr value); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_value_free", CharSet = CharSet.Ansi)] - public static extern int ldap_value_free([In] IntPtr value); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_value_free", CharSet = CharSet.Ansi)] + public static partial int ldap_value_free(IntPtr value); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_value_free_len", CharSet = CharSet.Ansi)] - public static extern IntPtr ldap_value_free_len([In] IntPtr berelement); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_value_free_len", CharSet = CharSet.Ansi)] + public static partial IntPtr ldap_value_free_len(IntPtr berelement); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_memfree", CharSet = CharSet.Ansi)] - public static extern void ldap_memfree([In] IntPtr value); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_memfree", CharSet = CharSet.Ansi)] + public static partial void ldap_memfree(IntPtr value); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_msgfree", CharSet = CharSet.Ansi)] - public static extern void ldap_msgfree([In] IntPtr value); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_msgfree", CharSet = CharSet.Ansi)] + public static partial void ldap_msgfree(IntPtr value); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_modify_ext", CharSet = CharSet.Ansi)] - public static extern int ldap_modify([In] ConnectionHandle ldapHandle, string dn, IntPtr attrs, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_modify_ext", CharSet = CharSet.Ansi)] + public static partial int ldap_modify(ConnectionHandle ldapHandle, string dn, IntPtr attrs, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_next_attribute", CharSet = CharSet.Ansi)] - public static extern IntPtr ldap_next_attribute([In] ConnectionHandle ldapHandle, [In] IntPtr result, [In, Out] IntPtr address); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_next_attribute", CharSet = CharSet.Ansi)] + public static partial IntPtr ldap_next_attribute(ConnectionHandle ldapHandle, IntPtr result, IntPtr address); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_next_entry", CharSet = CharSet.Ansi)] - public static extern IntPtr ldap_next_entry([In] ConnectionHandle ldapHandle, [In] IntPtr result); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_next_entry", CharSet = CharSet.Ansi)] + public static partial IntPtr ldap_next_entry(ConnectionHandle ldapHandle, IntPtr result); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_next_reference", CharSet = CharSet.Ansi)] - public static extern IntPtr ldap_next_reference([In] ConnectionHandle ldapHandle, [In] IntPtr result); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_next_reference", CharSet = CharSet.Ansi)] + public static partial IntPtr ldap_next_reference(ConnectionHandle ldapHandle, IntPtr result); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_abandon", CharSet = CharSet.Ansi)] - public static extern int ldap_abandon([In] ConnectionHandle ldapHandle, [In] int messagId); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_abandon", CharSet = CharSet.Ansi)] + public static partial int ldap_abandon(ConnectionHandle ldapHandle, int messagId); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_add_ext", CharSet = CharSet.Ansi)] - public static extern int ldap_add([In] ConnectionHandle ldapHandle, string dn, IntPtr attrs, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_add_ext", CharSet = CharSet.Ansi)] + public static partial int ldap_add(ConnectionHandle ldapHandle, string dn, IntPtr attrs, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_delete_ext", CharSet = CharSet.Ansi)] - public static extern int ldap_delete_ext([In] ConnectionHandle ldapHandle, string dn, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_delete_ext", CharSet = CharSet.Ansi)] + public static partial int ldap_delete_ext(ConnectionHandle ldapHandle, string dn, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); - [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_rename", CharSet = CharSet.Ansi)] - public static extern int ldap_rename([In] ConnectionHandle ldapHandle, string dn, string newRdn, string newParentDn, int deleteOldRdn, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); + [GeneratedDllImport(Libraries.OpenLdap, EntryPoint = "ldap_rename", CharSet = CharSet.Ansi)] + public static partial int ldap_rename(ConnectionHandle ldapHandle, string dn, string newRdn, string newParentDn, int deleteOldRdn, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. [DllImport(Libraries.OpenLdap, EntryPoint = "ldap_compare_ext", CharSet = CharSet.Ansi)] - public static extern int ldap_compare([In] ConnectionHandle ldapHandle, string dn, string attributeName, berval binaryValue, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); + public static extern int ldap_compare(ConnectionHandle ldapHandle, string dn, string attributeName, BerVal binaryValue, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time } } diff --git a/src/libraries/Common/src/Interop/Linux/cgroups/Interop.cgroups.cs b/src/libraries/Common/src/Interop/Linux/cgroups/Interop.cgroups.cs index 29a8a05cdcb3d4..79745aac2abfaf 100644 --- a/src/libraries/Common/src/Interop/Linux/cgroups/Interop.cgroups.cs +++ b/src/libraries/Common/src/Interop/Linux/cgroups/Interop.cgroups.cs @@ -10,7 +10,7 @@ internal static partial class Interop { /// Provides access to some cgroup (v1 and v2) features - internal static partial class cgroups + internal static partial class @cgroups { // For cgroup v1, see https://www.kernel.org/doc/Documentation/cgroup-v1/ // For cgroup v2, see https://www.kernel.org/doc/Documentation/cgroup-v2.txt diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs index 22b06128926fae..fcc687de2ba1ce 100644 --- a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs +++ b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs @@ -12,7 +12,7 @@ internal static partial class Interop { - internal static partial class procfs + internal static partial class @procfs { internal const string RootPath = "/proc/"; private const string StatusFileName = "/status"; diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs index a85ec0a5f4ffbc..8dd29a54221d03 100644 --- a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs +++ b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs @@ -11,7 +11,7 @@ internal static partial class Interop { - internal static partial class procfs + internal static partial class @procfs { private const string ExeFileName = "/exe"; private const string CmdLineFileName = "/cmdline"; diff --git a/src/libraries/Common/src/Interop/OSX/Interop.libproc.cs b/src/libraries/Common/src/Interop/OSX/Interop.libproc.cs index fefac9aac1fa8f..a51adbb79c0835 100644 --- a/src/libraries/Common/src/Interop/OSX/Interop.libproc.cs +++ b/src/libraries/Common/src/Interop/OSX/Interop.libproc.cs @@ -25,6 +25,9 @@ internal static partial class libproc // Constants from sys\resource.h private const int RUSAGE_INFO_V3 = 3; + // Constants from sys/errno.h + private const int EPERM = 1; + // Defines from proc_info.h internal enum ThreadRunState { @@ -120,7 +123,14 @@ internal static unsafe int[] proc_listallpids() { // Get the number of processes currently running to know how much data to allocate int numProcesses = proc_listallpids(null, 0); - if (numProcesses <= 0) + if (numProcesses == 0 && Marshal.GetLastPInvokeError() == EPERM) + { + // An app running in App Sandbox does not have permissions to list other running processes + // and so the `proc_listallpids` function returns 0 and sets errno to 1. As a fallback + // we return at least an array with the PID of the current process which we always know. + return new[] { Environment.ProcessId }; + } + else if (numProcesses <= 0) { throw new Win32Exception(SR.CantGetAllPids); } diff --git a/src/libraries/Common/src/Interop/OSX/System.Native/Interop.SearchPath.cs b/src/libraries/Common/src/Interop/OSX/System.Native/Interop.SearchPath.cs index fc68cf1d37ff85..2a41f989f77937 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Native/Interop.SearchPath.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Native/Interop.SearchPath.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class Sys { - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SearchPath")] - internal static extern string? SearchPath(NSSearchPathDirectory folderId); + [GeneratedDllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SearchPath", CharSet = CharSet.Ansi)] + internal static partial string? SearchPath(NSSearchPathDirectory folderId); internal enum NSSearchPathDirectory { diff --git a/src/libraries/Common/src/Interop/OSX/System.Native/Interop.iOSSupportVersion.cs b/src/libraries/Common/src/Interop/OSX/System.Native/Interop.iOSSupportVersion.cs index adffe6b4600791..5a7af296163e40 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Native/Interop.iOSSupportVersion.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Native/Interop.iOSSupportVersion.cs @@ -8,7 +8,7 @@ internal static partial class Interop { internal static partial class Sys { - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_iOSSupportVersion")] - internal static extern string iOSSupportVersion(); + [GeneratedDllImport(Libraries.SystemNative, EntryPoint = "SystemNative_iOSSupportVersion", CharSet = CharSet.Ansi)] + internal static partial string iOSSupportVersion(); } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Abort.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Abort.cs new file mode 100644 index 00000000000000..3d040c31545840 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Abort.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal unsafe partial class Sys + { + [DoesNotReturn] + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_Abort")] + internal static extern void Abort(); + } +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.DynamicLoad.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.DynamicLoad.cs new file mode 100644 index 00000000000000..6fd3ab2bba6aed --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.DynamicLoad.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal unsafe partial class Sys + { + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_LoadLibrary")] + internal static extern IntPtr LoadLibrary(string filename); + + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetProcAddress")] + internal static extern IntPtr GetProcAddress(IntPtr handle, byte* symbol); + + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetProcAddress")] + internal static extern IntPtr GetProcAddress(IntPtr handle, string symbol); + + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_FreeLibrary")] + internal static extern void FreeLibrary(IntPtr handle); + } +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Exit.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Exit.cs new file mode 100644 index 00000000000000..a1e4d94149b6ff --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Exit.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal unsafe partial class Sys + { + [DoesNotReturn] + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_Exit")] + internal static extern void Exit(int exitCode); + } +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetNodeName.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetNodeName.cs index be1801280ca333..0c2f1ee7078b1f 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetNodeName.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetNodeName.cs @@ -10,16 +10,16 @@ internal static partial class Interop { internal static partial class Sys { - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetNodeName", SetLastError = true)] - private static extern unsafe int GetNodeName(char* name, out int len); + [GeneratedDllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetNodeName", SetLastError = true)] + private static unsafe partial int GetNodeName(byte* name, ref int len); internal static unsafe string GetNodeName() { // max value of _UTSNAME_LENGTH on known Unix platforms is 1024. const int _UTSNAME_LENGTH = 1024; int len = _UTSNAME_LENGTH; - char* name = stackalloc char[_UTSNAME_LENGTH]; - int err = GetNodeName(name, out len); + byte* name = stackalloc byte[_UTSNAME_LENGTH]; + int err = GetNodeName(name, ref len); if (err != 0) { // max domain name can be 255 chars. diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemSet.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemSet.cs index 46449d375648c0..a71c0149d5a5c4 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemSet.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemSet.cs @@ -8,7 +8,7 @@ internal static partial class Interop { internal static partial class Sys { - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MemSet")] + [GeneratedDllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MemSet")] internal static extern unsafe void* MemSet(void *s, int c, UIntPtr n); } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SchedGetCpu.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SchedGetCpu.cs new file mode 100644 index 00000000000000..afc91ab8a733cb --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SchedGetCpu.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal unsafe partial class Sys + { + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_SchedGetCpu")] + internal static extern int SchedGetCpu(); + } +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Threading.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Threading.cs new file mode 100644 index 00000000000000..cecbaca6de6619 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Threading.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal unsafe partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_CreateThread")] + internal static extern unsafe bool CreateThread(IntPtr stackSize, delegate* unmanaged startAddress, IntPtr parameter); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ChangeServiceConfig2.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ChangeServiceConfig2.cs index 753a310ea20847..ad28538739e913 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ChangeServiceConfig2.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ChangeServiceConfig2.cs @@ -9,10 +9,13 @@ internal static partial class Interop { internal static partial class Advapi32 { - [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] - public static extern bool ChangeServiceConfig2(SafeServiceHandle serviceHandle, uint infoLevel, ref SERVICE_DESCRIPTION serviceDesc); + [GeneratedDllImport(Libraries.Advapi32, EntryPoint = "ChangeServiceConfig2W", ExactSpelling = true, SetLastError = true)] + public static partial bool ChangeServiceConfig2(SafeServiceHandle serviceHandle, uint infoLevel, ref SERVICE_DESCRIPTION serviceDesc); - [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable types. + [DllImport(Libraries.Advapi32, EntryPoint = "ChangeServiceConfig2W", ExactSpelling = true, SetLastError = true)] public static extern bool ChangeServiceConfig2(SafeServiceHandle serviceHandle, uint infoLevel, ref SERVICE_DELAYED_AUTOSTART_INFO serviceDesc); +#pragma warning restore DLLIMPORTGENANALYZER015 } } diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CreateService.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CreateService.cs index 593b0bd2a62c8e..3c05b2d080b1c7 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CreateService.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CreateService.cs @@ -9,7 +9,7 @@ internal static partial class Interop { internal static partial class Advapi32 { - [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] + [GeneratedDllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern IntPtr CreateService(SafeServiceHandle databaseHandle, string serviceName, string displayName, int access, int serviceType, int startType, int errorControl, string binaryPath, string loadOrderGroup, IntPtr pTagId, string dependencies, string servicesStartName, string password); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.DeleteService.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.DeleteService.cs index b770ff4febbc8b..5804314638a60b 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.DeleteService.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.DeleteService.cs @@ -9,7 +9,7 @@ internal static partial class Interop { internal static partial class Advapi32 { - [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] - public static extern bool DeleteService(SafeServiceHandle serviceHandle); + [GeneratedDllImport(Libraries.Advapi32, SetLastError = true)] + public static partial bool DeleteService(SafeServiceHandle serviceHandle); } } diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.BitBlt.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.BitBlt.cs index 2632da9a2b09de..6ead12e254100c 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.BitBlt.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.BitBlt.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true, SetLastError = true)] - public static extern int BitBlt(IntPtr hdc, int x, int y, int cx, int cy, + [GeneratedDllImport(Libraries.Gdi32, SetLastError = true)] + public static partial int BitBlt(IntPtr hdc, int x, int y, int cx, int cy, IntPtr hdcSrc, int x1, int y1, RasterOp rop); public static int BitBlt(HandleRef hdc, int x, int y, int cx, int cy, diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CombineRgn.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CombineRgn.cs index e417aa029bb542..1f253821e28f46 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CombineRgn.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CombineRgn.cs @@ -16,8 +16,8 @@ public enum CombineMode : int RGN_DIFF = 4, } - [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true)] - public static extern RegionType CombineRgn(IntPtr hrgnDst, IntPtr hrgnSrc1, IntPtr hrgnSrc2, CombineMode iMode); + [GeneratedDllImport(Libraries.Gdi32, SetLastError = true)] + public static partial RegionType CombineRgn(IntPtr hrgnDst, IntPtr hrgnSrc1, IntPtr hrgnSrc2, CombineMode iMode); public static RegionType CombineRgn(HandleRef hrgnDst, HandleRef hrgnSrc1, HandleRef hrgnSrc2, CombineMode iMode) { diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateCompatibleDC.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateCompatibleDC.cs index 634773c518b162..11107c5a197b15 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateCompatibleDC.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateCompatibleDC.cs @@ -8,7 +8,7 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true)] - public static extern IntPtr CreateCompatibleDC(IntPtr hdc); + [GeneratedDllImport(Libraries.Gdi32)] + public static partial IntPtr CreateCompatibleDC(IntPtr hdc); } } diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateDC.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateDC.cs index 08f4a5e70921c2..9f9d16dfa247b4 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateDC.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateDC.cs @@ -8,7 +8,7 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true, CharSet = CharSet.Unicode)] - public static extern IntPtr CreateDCW(string pwszDriver, string pwszDevice, string? pszPort, IntPtr pdm); + [GeneratedDllImport(Libraries.Gdi32, CharSet = CharSet.Unicode, ExactSpelling = true)] + public static partial IntPtr CreateDCW(string pwszDriver, string pwszDevice, string? pszPort, IntPtr pdm); } } diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateFontIndirect.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateFontIndirect.cs index cd46252f7c2f41..e291a9ef80dd24 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateFontIndirect.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateFontIndirect.cs @@ -8,7 +8,7 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true, CharSet = CharSet.Unicode)] - public static extern IntPtr CreateFontIndirectW(ref User32.LOGFONT lplf); + [GeneratedDllImport(Libraries.Gdi32, CharSet = CharSet.Unicode, ExactSpelling = true)] + public static partial IntPtr CreateFontIndirectW(ref User32.LOGFONT lplf); } } diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateIC.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateIC.cs index ad7e381c1f775a..9602db7443bd8f 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateIC.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateIC.cs @@ -8,7 +8,7 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true, CharSet = CharSet.Unicode)] - public static extern IntPtr CreateICW(string pszDriver, string pszDevice, string? pszPort, IntPtr pdm); + [GeneratedDllImport(Libraries.Gdi32, CharSet = CharSet.Unicode, ExactSpelling = true)] + public static partial IntPtr CreateICW(string pszDriver, string pszDevice, string? pszPort, IntPtr pdm); } } diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateRectRgn.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateRectRgn.cs index da77bde404de0b..f54cf7eecd37fe 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateRectRgn.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.CreateRectRgn.cs @@ -8,7 +8,7 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true)] - public static extern IntPtr CreateRectRgn(int x1, int y1, int x2, int y2); + [GeneratedDllImport(Libraries.Gdi32)] + public static partial IntPtr CreateRectRgn(int x1, int y1, int x2, int y2); } } diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.DeleteDC.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.DeleteDC.cs index cda012b9169357..9a93d724b58e45 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.DeleteDC.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.DeleteDC.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true)] - public static extern bool DeleteDC(IntPtr hdc); + [GeneratedDllImport(Libraries.Gdi32)] + public static partial bool DeleteDC(IntPtr hdc); public static bool DeleteDC(HandleRef hdc) { diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.DeleteObject.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.DeleteObject.cs index 504442c780873d..cd048a74c7931b 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.DeleteObject.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.DeleteObject.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true)] - public static extern bool DeleteObject(IntPtr ho); + [GeneratedDllImport(Libraries.Gdi32)] + public static partial bool DeleteObject(IntPtr ho); public static bool DeleteObject(HandleRef ho) { diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetClipRgn.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetClipRgn.cs index 0a71afab637950..8d5d4dd19b2338 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetClipRgn.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetClipRgn.cs @@ -9,8 +9,8 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true)] - public static extern int GetClipRgn(IntPtr hdc, IntPtr hrgn); + [GeneratedDllImport(Libraries.Gdi32)] + public static partial int GetClipRgn(IntPtr hdc, IntPtr hrgn); public static int GetClipRgn(HandleRef hdc, IntPtr hrgn) { diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetCurrentObject.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetCurrentObject.cs index 8a011badecb903..036cefd3bf2970 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetCurrentObject.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetCurrentObject.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true)] - public static extern IntPtr GetCurrentObject(IntPtr hdc, ObjectType type); + [GeneratedDllImport(Libraries.Gdi32)] + public static partial IntPtr GetCurrentObject(IntPtr hdc, ObjectType type); public static IntPtr GetCurrentObject(HandleRef hdc, ObjectType type) { diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetDeviceCaps.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetDeviceCaps.cs index a9f29a1b8fb559..55cec2b89d0f28 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetDeviceCaps.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetDeviceCaps.cs @@ -30,8 +30,8 @@ public static class DeviceTechnology public const int DT_RASPRINTER = 2; } - [DllImport(Libraries.Gdi32, ExactSpelling = true)] - public static extern int GetDeviceCaps(IntPtr hdc, DeviceCapability index); + [GeneratedDllImport(Libraries.Gdi32)] + public static partial int GetDeviceCaps(IntPtr hdc, DeviceCapability index); public static int GetDeviceCaps(HandleRef hdc, DeviceCapability index) { diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetObjectType.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetObjectType.cs index dcf21c3045a2c4..5355c10e67bf65 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetObjectType.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetObjectType.cs @@ -8,7 +8,7 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true)] - public static extern ObjectType GetObjectType(IntPtr h); + [GeneratedDllImport(Libraries.Gdi32)] + public static partial ObjectType GetObjectType(IntPtr h); } } diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetRgnBox.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetRgnBox.cs index 8bd9b20a6dbd0f..30e68bbe212809 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetRgnBox.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetRgnBox.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true)] - public static extern RegionType GetRgnBox(IntPtr hrgn, ref RECT lprc); + [GeneratedDllImport(Libraries.Gdi32)] + public static partial RegionType GetRgnBox(IntPtr hrgn, ref RECT lprc); public static RegionType GetRgnBox(HandleRef hrgn, ref RECT lprc) { diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetStockObject.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetStockObject.cs index 9d65eeca04039c..a17c3de2ae3412 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetStockObject.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.GetStockObject.cs @@ -13,7 +13,7 @@ public enum StockObject : int DEFAULT_GUI_FONT = 17 } - [DllImport(Libraries.Gdi32, ExactSpelling = true)] - public static extern IntPtr GetStockObject(StockObject i); + [GeneratedDllImport(Libraries.Gdi32)] + public static partial IntPtr GetStockObject(StockObject i); } } diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.OffsetViewportOrgEx.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.OffsetViewportOrgEx.cs index 724992c9ba4e6d..8768fd8493be71 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.OffsetViewportOrgEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.OffsetViewportOrgEx.cs @@ -9,8 +9,11 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(Libraries.Gdi32)] public static extern bool OffsetViewportOrgEx(IntPtr hdc, int x, int y, ref Point lppt); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time public static bool OffsetViewportOrgEx(HandleRef hdc, int x, int y, ref Point lppt) { diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.RestoreDC.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.RestoreDC.cs index b37ffdbdeba0af..808bd48a91fec0 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.RestoreDC.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.RestoreDC.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true)] - public static extern bool RestoreDC(IntPtr hdc, int nSavedDC); + [GeneratedDllImport(Libraries.Gdi32)] + public static partial bool RestoreDC(IntPtr hdc, int nSavedDC); public static bool RestoreDC(HandleRef hdc, int nSavedDC) { diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.SaveDC.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.SaveDC.cs index 3c598c1793c440..52aa47a6ca375e 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.SaveDC.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.SaveDC.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, ExactSpelling = true)] - public static extern int SaveDC(IntPtr hdc); + [GeneratedDllImport(Libraries.Gdi32)] + public static partial int SaveDC(IntPtr hdc); public static int SaveDC(HandleRef hdc) { diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.SelectClipRgn.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.SelectClipRgn.cs index adf12cde9407aa..7aa96a8a0ae731 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.SelectClipRgn.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.SelectClipRgn.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class Gdi32 { - [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true)] - public static extern RegionType SelectClipRgn(IntPtr hdc, IntPtr hrgn); + [GeneratedDllImport(Libraries.Gdi32, SetLastError = true)] + public static partial RegionType SelectClipRgn(IntPtr hdc, IntPtr hrgn); public static RegionType SelectClipRgn(HandleRef hdc, HandleRef hrgn) { diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CheckTokenMembershipEx.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CheckTokenMembershipEx.cs index 590d8651fae4b3..dad6a63281115f 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CheckTokenMembershipEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CheckTokenMembershipEx.cs @@ -11,7 +11,7 @@ internal static partial class Kernel32 { internal const uint CTMF_INCLUDE_APPCONTAINER = 0x00000001; - [DllImport(Interop.Libraries.Kernel32, SetLastError = true)] - internal static extern bool CheckTokenMembershipEx(SafeAccessTokenHandle TokenHandle, byte[] SidToCheck, uint Flags, ref bool IsMember); + [GeneratedDllImport(Interop.Libraries.Kernel32, SetLastError = true)] + internal static partial bool CheckTokenMembershipEx(SafeAccessTokenHandle TokenHandle, byte[] SidToCheck, uint Flags, ref bool IsMember); } } diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DynamicLoad.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DynamicLoad.cs new file mode 100644 index 00000000000000..c0a0a2aeb5fbec --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DynamicLoad.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static unsafe partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static extern IntPtr GetProcAddress(IntPtr hModule, byte* lpProcName); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ExitProcess.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ExitProcess.cs new file mode 100644 index 00000000000000..af50326e9771de --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ExitProcess.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static unsafe partial class Kernel32 + { + [DoesNotReturn] + [DllImport(Libraries.Kernel32, EntryPoint = "ExitProcess")] + internal static extern void ExitProcess(int exitCode); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs index cc4896c1c52e48..c43b4fd19d21d2 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs @@ -23,7 +23,8 @@ internal static partial class FileOperations internal const int FILE_FLAG_OVERLAPPED = 0x40000000; internal const int FILE_LIST_DIRECTORY = 0x0001; - } + internal const int FILE_WRITE_ATTRIBUTES = 0x100; + } } } diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCurrentProcessorNumber.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCurrentProcessorNumber.cs new file mode 100644 index 00000000000000..4ebdc93faf9e62 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCurrentProcessorNumber.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static extern uint GetCurrentProcessorNumber(); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessName.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessName.cs new file mode 100644 index 00000000000000..293a9c0ee49743 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessName.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, EntryPoint = "QueryFullProcessImageNameW", ExactSpelling = true, SetLastError = true)] + private static unsafe extern bool QueryFullProcessImageName( + SafeHandle hProcess, + uint dwFlags, + char* lpBuffer, + ref uint lpdwSize); + + internal static unsafe string? GetProcessName(uint processId) + { + using (SafeProcessHandle handle = OpenProcess(Advapi32.ProcessOptions.PROCESS_QUERY_LIMITED_INFORMATION, false, (int)processId)) + { + if (handle.IsInvalid) // OpenProcess can fail + { + return null; + } + + const int StartLength = +#if DEBUG + 1; // in debug, validate ArrayPool growth +#else + Interop.Kernel32.MAX_PATH; +#endif + + Span buffer = stackalloc char[StartLength + 1]; + char[]? rentedArray = null; + + try + { + while (true) + { + uint length = (uint)buffer.Length; + fixed (char* pinnedBuffer = &MemoryMarshal.GetReference(buffer)) + { + if (QueryFullProcessImageName(handle, 0, pinnedBuffer, ref length)) + { + return buffer.Slice(0, (int)length).ToString(); + } + else if (Marshal.GetLastWin32Error() != Errors.ERROR_INSUFFICIENT_BUFFER) + { + return null; + } + } + + char[]? toReturn = rentedArray; + buffer = rentedArray = ArrayPool.Shared.Rent(buffer.Length * 2); + if (toReturn is not null) + { + ArrayPool.Shared.Return(toReturn); + } + } + } + finally + { + if (rentedArray is not null) + { + ArrayPool.Shared.Return(rentedArray); + } + } + } + } + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetTickCount64.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetTickCount64.cs new file mode 100644 index 00000000000000..97e3349aef8784 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetTickCount64.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static unsafe partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + [SuppressGCTransition] + internal static extern ulong GetTickCount64(); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalFree.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalFree.cs index 6241ed4849182d..f62db377183f7c 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalFree.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalFree.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class Kernel32 { - [DllImport(Libraries.Kernel32, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr GlobalFree(IntPtr handle); + [GeneratedDllImport(Libraries.Kernel32, SetLastError = true)] + public static partial IntPtr GlobalFree(IntPtr handle); public static IntPtr GlobalFree(HandleRef handle) { diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IsDebuggerPresent.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IsDebuggerPresent.cs new file mode 100644 index 00000000000000..232ac6b8f678fc --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IsDebuggerPresent.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static extern bool IsDebuggerPresent(); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.RaiseFailFastException.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.RaiseFailFastException.cs new file mode 100644 index 00000000000000..1e80b3733688e7 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.RaiseFailFastException.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +internal partial class Interop +{ +#pragma warning disable 649 + internal unsafe struct EXCEPTION_RECORD + { + internal uint ExceptionCode; + internal uint ExceptionFlags; + internal IntPtr ExceptionRecord; + internal IntPtr ExceptionAddress; + internal uint NumberParameters; +#if TARGET_64BIT + internal fixed ulong ExceptionInformation[15]; +#else + internal fixed uint ExceptionInformation[15]; +#endif + } +#pragma warning restore 649 + + internal partial class Kernel32 + { + internal const uint EXCEPTION_NONCONTINUABLE = 0x1; + + internal const uint FAIL_FAST_GENERATE_EXCEPTION_ADDRESS = 0x1; + + // + // Wrapper for calling RaiseFailFastException + // + [DoesNotReturn] + internal static unsafe void RaiseFailFastException(uint faultCode, IntPtr pExAddress, IntPtr pExContext) + { + EXCEPTION_RECORD exceptionRecord; + exceptionRecord.ExceptionCode = faultCode; + exceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + exceptionRecord.ExceptionRecord = IntPtr.Zero; + exceptionRecord.ExceptionAddress = pExAddress; + exceptionRecord.NumberParameters = 0; + // don't care about exceptionRecord.ExceptionInformation as we set exceptionRecord.NumberParameters to zero + + RaiseFailFastException( + &exceptionRecord, + pExContext, + pExAddress == IntPtr.Zero ? FAIL_FAST_GENERATE_EXCEPTION_ADDRESS : 0); + } + + [DllImport(Libraries.Kernel32, EntryPoint = "RaiseFailFastException")] + [DoesNotReturn] + private static extern unsafe void RaiseFailFastException( + EXCEPTION_RECORD* pExceptionRecord, + IntPtr pContextRecord, + uint dwFlags); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ThreadPool.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ThreadPool.cs new file mode 100644 index 00000000000000..a7d9abdaf47b2c --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ThreadPool.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static extern unsafe IntPtr CreateThreadpoolWork(delegate* unmanaged pfnwk, IntPtr pv, IntPtr pcbe); + + [DllImport(Libraries.Kernel32)] + internal static extern void SubmitThreadpoolWork(IntPtr pwk); + + [DllImport(Libraries.Kernel32)] + internal static extern void CloseThreadpoolWork(IntPtr pwk); + + [DllImport(Libraries.Kernel32)] + internal static extern unsafe IntPtr CreateThreadpoolWait(delegate* unmanaged pfnwa, IntPtr pv, IntPtr pcbe); + + [DllImport(Libraries.Kernel32)] + internal static extern void SetThreadpoolWait(IntPtr pwa, IntPtr h, IntPtr pftTimeout); + + [DllImport(Libraries.Kernel32)] + internal static extern void WaitForThreadpoolWaitCallbacks(IntPtr pwa, bool fCancelPendingCallbacks); + + [DllImport(Libraries.Kernel32)] + internal static extern void CloseThreadpoolWait(IntPtr pwa); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ThreadPoolIO.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ThreadPoolIO.cs new file mode 100644 index 00000000000000..7dd7435c6a826b --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ThreadPoolIO.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static unsafe extern SafeThreadPoolIOHandle CreateThreadpoolIo(SafeHandle fl, delegate* unmanaged pfnio, IntPtr context, IntPtr pcbe); + + [DllImport(Libraries.Kernel32)] + internal static unsafe extern void CloseThreadpoolIo(IntPtr pio); + + [DllImport(Libraries.Kernel32)] + internal static unsafe extern void StartThreadpoolIo(SafeThreadPoolIOHandle pio); + + [DllImport(Libraries.Kernel32)] + internal static unsafe extern void CancelThreadpoolIo(SafeThreadPoolIOHandle pio); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs index d4919d725a52cd..87884a18610051 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs @@ -11,23 +11,23 @@ internal static partial class Kernel32 { internal const int WAIT_FAILED = unchecked((int)0xFFFFFFFF); - [DllImport(Libraries.Kernel32)] - internal static extern uint WaitForMultipleObjectsEx(uint nCount, IntPtr lpHandles, BOOL bWaitAll, uint dwMilliseconds, BOOL bAlertable); + [GeneratedDllImport(Libraries.Kernel32)] + internal static partial uint WaitForMultipleObjectsEx(uint nCount, IntPtr lpHandles, BOOL bWaitAll, uint dwMilliseconds, BOOL bAlertable); - [DllImport(Libraries.Kernel32)] - internal static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); + [GeneratedDllImport(Libraries.Kernel32)] + internal static partial uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); - [DllImport(Libraries.Kernel32)] - internal static extern uint SignalObjectAndWait(IntPtr hObjectToSignal, IntPtr hObjectToWaitOn, uint dwMilliseconds, BOOL bAlertable); + [GeneratedDllImport(Libraries.Kernel32)] + internal static partial uint SignalObjectAndWait(IntPtr hObjectToSignal, IntPtr hObjectToWaitOn, uint dwMilliseconds, BOOL bAlertable); - [DllImport(Libraries.Kernel32)] - internal static extern void Sleep(uint milliseconds); + [GeneratedDllImport(Libraries.Kernel32)] + internal static partial void Sleep(uint milliseconds); internal const uint CREATE_SUSPENDED = 0x00000004; internal const uint STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000; - [DllImport(Libraries.Kernel32)] - internal static extern unsafe SafeWaitHandle CreateThread( + [GeneratedDllImport(Libraries.Kernel32)] + internal static unsafe partial SafeWaitHandle CreateThread( IntPtr lpThreadAttributes, IntPtr dwStackSize, delegate* unmanaged lpStartAddress, @@ -35,16 +35,16 @@ internal static extern unsafe SafeWaitHandle CreateThread( uint dwCreationFlags, out uint lpThreadId); - [DllImport(Libraries.Kernel32)] - internal static extern uint ResumeThread(SafeWaitHandle hThread); + [GeneratedDllImport(Libraries.Kernel32)] + internal static partial uint ResumeThread(SafeWaitHandle hThread); - [DllImport(Libraries.Kernel32)] - internal static extern IntPtr GetCurrentThread(); + [GeneratedDllImport(Libraries.Kernel32)] + internal static partial IntPtr GetCurrentThread(); internal const int DUPLICATE_SAME_ACCESS = 2; - [DllImport(Libraries.Kernel32, SetLastError = true)] - internal static extern bool DuplicateHandle( + [GeneratedDllImport(Libraries.Kernel32, SetLastError = true)] + internal static partial bool DuplicateHandle( IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, @@ -66,10 +66,10 @@ internal enum ThreadPriority : int ErrorReturn = 0x7FFFFFFF } - [DllImport(Libraries.Kernel32)] - internal static extern ThreadPriority GetThreadPriority(SafeWaitHandle hThread); + [GeneratedDllImport(Libraries.Kernel32)] + internal static partial ThreadPriority GetThreadPriority(SafeWaitHandle hThread); - [DllImport(Libraries.Kernel32)] - internal static extern bool SetThreadPriority(SafeWaitHandle hThread, int nPriority); + [GeneratedDllImport(Libraries.Kernel32)] + internal static partial bool SetThreadPriority(SafeWaitHandle hThread, int nPriority); } } diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Timer.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Timer.cs new file mode 100644 index 00000000000000..19f4987d14f6f9 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Timer.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static extern unsafe IntPtr CreateThreadpoolTimer(delegate* unmanaged pfnti, IntPtr pv, IntPtr pcbe); + + [DllImport(Libraries.Kernel32)] + internal static extern unsafe IntPtr SetThreadpoolTimer(IntPtr pti, long* pftDueTime, uint msPeriod, uint msWindowLength); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoGetApartmentType.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoGetApartmentType.cs new file mode 100644 index 00000000000000..01abd6333c8940 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoGetApartmentType.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal enum APTTYPE : uint + { + APTTYPE_STA = 0x0u, + APTTYPE_MTA = 0x1u, + APTTYPE_NA = 0x2u, + APTTYPE_MAINSTA = 0x3u, + APTTYPE_CURRENT = 0xFFFFFFFFu, + } + + internal enum APTTYPEQUALIFIER : uint + { + APTTYPEQUALIFIER_NONE = 0x0u, + APTTYPEQUALIFIER_IMPLICIT_MTA = 0x1u, + APTTYPEQUALIFIER_NA_ON_MTA = 0x2u, + APTTYPEQUALIFIER_NA_ON_STA = 0x3u, + APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA = 0x4u, + APTTYPEQUALIFIER_NA_ON_MAINSTA = 0x5u, + APTTYPEQUALIFIER_APPLICATION_STA = 0x6u, + } + + internal static partial class Ole32 + { + [DllImport(Interop.Libraries.Ole32, ExactSpelling = true)] + internal static extern int CoGetApartmentType(out APTTYPE pAptType, out APTTYPEQUALIFIER pAptQualifier); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoInitializeEx.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoInitializeEx.cs new file mode 100644 index 00000000000000..7be311659cab48 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoInitializeEx.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Ole32 + { + internal const uint COINIT_APARTMENTTHREADED = 2; + internal const uint COINIT_MULTITHREADED = 0; + + [DllImport(Interop.Libraries.Ole32, ExactSpelling = true)] + internal static extern int CoInitializeEx(IntPtr reserved, uint dwCoInit); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoUninitialize.cs b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoUninitialize.cs new file mode 100644 index 00000000000000..2e92ce2a113f37 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Ole32/Interop.CoUninitialize.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Ole32 + { + [DllImport(Interop.Libraries.Ole32, ExactSpelling = true)] + internal static extern int CoUninitialize(); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetDC.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetDC.cs index fdcff52f711c39..597ad6b654ba72 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetDC.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetDC.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class User32 { - [DllImport(Libraries.User32, ExactSpelling = true)] - public static extern IntPtr GetDC(IntPtr hWnd); + [GeneratedDllImport(Libraries.User32)] + public static partial IntPtr GetDC(IntPtr hWnd); public static IntPtr GetDC(HandleRef hWnd) { diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.ReleaseDC.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.ReleaseDC.cs index eebf7f1f536a9d..0f596aa2997484 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.ReleaseDC.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.ReleaseDC.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class User32 { - [DllImport(Libraries.User32, ExactSpelling = true)] - public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); + [GeneratedDllImport(Libraries.User32)] + public static partial int ReleaseDC(IntPtr hWnd, IntPtr hDC); public static int ReleaseDC(HandleRef hWnd, IntPtr hDC) { diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.SystemParametersInfo.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.SystemParametersInfo.cs index 566ac5b212dc9f..78b42390c61278 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.SystemParametersInfo.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.SystemParametersInfo.cs @@ -14,7 +14,7 @@ public enum SystemParametersAction : uint SPI_GETNONCLIENTMETRICS = 0x29 } - [DllImport(Libraries.User32, ExactSpelling = true, CharSet = CharSet.Unicode)] - public static extern unsafe bool SystemParametersInfoW(SystemParametersAction uiAction, uint uiParam, void* pvParam, uint fWinIni); + [GeneratedDllImport(Libraries.User32)] + public static unsafe partial bool SystemParametersInfoW(SystemParametersAction uiAction, uint uiParam, void* pvParam, uint fWinIni); } } diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.WindowFromDC.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.WindowFromDC.cs index 75dc7497768e46..b92572564b8c5a 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.WindowFromDC.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.WindowFromDC.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class User32 { - [DllImport(Libraries.User32, ExactSpelling = true)] - public static extern IntPtr WindowFromDC(IntPtr hDC); + [GeneratedDllImport(Libraries.User32)] + public static partial IntPtr WindowFromDC(IntPtr hDC); public static IntPtr WindowFromDC(HandleRef hDC) { diff --git a/src/libraries/Common/src/Interop/Windows/WinSock/hostent.cs b/src/libraries/Common/src/Interop/Windows/WinSock/hostent.cs deleted file mode 100644 index 0a9c13d5e4ca23..00000000000000 --- a/src/libraries/Common/src/Interop/Windows/WinSock/hostent.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; - -namespace System.Net.Sockets -{ - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal struct hostent - { - public IntPtr h_name; - public IntPtr h_aliases; - public short h_addrtype; - public short h_length; - public IntPtr h_addr_list; - } -} // namespace System.Net.Sockets diff --git a/src/libraries/Common/src/Interop/Windows/Wldap32/Interop.Ber.cs b/src/libraries/Common/src/Interop/Windows/Wldap32/Interop.Ber.cs index 0ba2c96ecf7575..5ca47f48b28613 100644 --- a/src/libraries/Common/src/Interop/Windows/Wldap32/Interop.Ber.cs +++ b/src/libraries/Common/src/Interop/Windows/Wldap32/Interop.Ber.cs @@ -9,28 +9,43 @@ internal static partial class Interop { internal static partial class Ldap { - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ber_free", CharSet = CharSet.Unicode)] - public static extern IntPtr ber_free([In] IntPtr berelement, int option); + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ber_free", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr ber_free(IntPtr berelement, int option); - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ber_alloc_t", CharSet = CharSet.Unicode)] - public static extern IntPtr ber_alloc(int option); + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ber_alloc_t", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr ber_alloc(int option); - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ber_printf", CharSet = CharSet.Unicode)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Requires some varargs support mechanism in generated interop + [DllImport(Libraries.Wldap32, EntryPoint = "ber_printf", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] public static extern int ber_printf(SafeBerHandle berElement, string format, __arglist); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ber_flatten", CharSet = CharSet.Unicode)] - public static extern int ber_flatten(SafeBerHandle berElement, ref IntPtr value); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ber_init", CharSet = CharSet.Unicode)] - public static extern IntPtr ber_init(berval value); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ber_scanf", CharSet = CharSet.Unicode)] +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ber_flatten", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ber_flatten(SafeBerHandle berElement, ref IntPtr value); + +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. + [DllImport(Libraries.Wldap32, EntryPoint = "ber_init", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static extern IntPtr ber_init(BerVal value); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Requires some varargs support mechanism in generated interop + [DllImport(Libraries.Wldap32, EntryPoint = "ber_scanf", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] public static extern int ber_scanf(SafeBerHandle berElement, string format, __arglist); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ber_bvfree", CharSet = CharSet.Unicode)] - public static extern int ber_bvfree(IntPtr value); + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ber_bvfree", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ber_bvfree(IntPtr value); - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ber_bvecfree", CharSet = CharSet.Unicode)] - public static extern int ber_bvecfree(IntPtr value); + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ber_bvecfree", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ber_bvecfree(IntPtr value); } } diff --git a/src/libraries/Common/src/Interop/Windows/Wldap32/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Windows/Wldap32/Interop.Ldap.cs index a5635502d30a40..4a6469602e908e 100644 --- a/src/libraries/Common/src/Interop/Windows/Wldap32/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Windows/Wldap32/Interop.Ldap.cs @@ -9,148 +9,213 @@ internal static partial class Interop { internal static partial class Ldap { - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_bind_sW", CharSet = CharSet.Unicode)] - public static extern int ldap_bind_s([In] ConnectionHandle ldapHandle, string dn, SEC_WINNT_AUTH_IDENTITY_EX credentials, BindMethod method); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_initW", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern IntPtr ldap_init(string hostName, int portNumber); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, EntryPoint = "ldap_connect", CharSet = CharSet.Unicode)] - public static extern int ldap_connect([In] ConnectionHandle ldapHandle, LDAP_TIMEVAL timeout); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, EntryPoint = "ldap_unbind", CharSet = CharSet.Unicode)] - public static extern int ldap_unbind([In] IntPtr ldapHandle); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_get_optionW", CharSet = CharSet.Unicode)] - public static extern int ldap_get_option_int([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref int outValue); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_set_optionW", CharSet = CharSet.Unicode)] - public static extern int ldap_set_option_int([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref int inValue); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_get_optionW", CharSet = CharSet.Unicode)] - public static extern int ldap_get_option_ptr([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref IntPtr outValue); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_set_optionW", CharSet = CharSet.Unicode)] - public static extern int ldap_set_option_ptr([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref IntPtr inValue); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_get_optionW", CharSet = CharSet.Unicode)] - public static extern int ldap_get_option_sechandle([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref SecurityHandle outValue); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_get_optionW", CharSet = CharSet.Unicode)] - public static extern int ldap_get_option_secInfo([In] ConnectionHandle ldapHandle, [In] LdapOption option, [In, Out] SecurityPackageContextConnectionInformation outValue); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_set_optionW", CharSet = CharSet.Unicode)] - public static extern int ldap_set_option_referral([In] ConnectionHandle ldapHandle, [In] LdapOption option, ref LdapReferralCallback outValue); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_set_optionW", CharSet = CharSet.Unicode)] - public static extern int ldap_set_option_clientcert([In] ConnectionHandle ldapHandle, [In] LdapOption option, QUERYCLIENTCERT outValue); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_set_optionW", CharSet = CharSet.Unicode)] - public static extern int ldap_set_option_servercert([In] ConnectionHandle ldapHandle, [In] LdapOption option, VERIFYSERVERCERT outValue); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "LdapGetLastError")] - public static extern int LdapGetLastError(); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "cldap_openW", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern IntPtr cldap_open(string hostName, int portNumber); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_simple_bind_sW", CharSet = CharSet.Unicode)] - public static extern int ldap_simple_bind_s([In] ConnectionHandle ldapHandle, string distinguishedName, string password); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_delete_extW", CharSet = CharSet.Unicode)] - public static extern int ldap_delete_ext([In] ConnectionHandle ldapHandle, string dn, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_result", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern int ldap_result([In] ConnectionHandle ldapHandle, int messageId, int all, LDAP_TIMEVAL timeout, ref IntPtr Mesage); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_parse_resultW", CharSet = CharSet.Unicode)] - public static extern int ldap_parse_result([In] ConnectionHandle ldapHandle, [In] IntPtr result, ref int serverError, ref IntPtr dn, ref IntPtr message, ref IntPtr referral, ref IntPtr control, byte freeIt); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_parse_resultW", CharSet = CharSet.Unicode)] - public static extern int ldap_parse_result_referral([In] ConnectionHandle ldapHandle, [In] IntPtr result, IntPtr serverError, IntPtr dn, IntPtr message, ref IntPtr referral, IntPtr control, byte freeIt); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_memfreeW", CharSet = CharSet.Unicode)] - public static extern void ldap_memfree([In] IntPtr value); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_value_freeW", CharSet = CharSet.Unicode)] - public static extern int ldap_value_free([In] IntPtr value); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_controls_freeW", CharSet = CharSet.Unicode)] - public static extern int ldap_controls_free([In] IntPtr value); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_abandon", CharSet = CharSet.Unicode)] - public static extern int ldap_abandon([In] ConnectionHandle ldapHandle, [In] int messagId); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_start_tls_sW", CharSet = CharSet.Unicode)] - public static extern int ldap_start_tls(ConnectionHandle ldapHandle, ref int ServerReturnValue, ref IntPtr Message, IntPtr ServerControls, IntPtr ClientControls); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_stop_tls_s", CharSet = CharSet.Unicode)] - public static extern byte ldap_stop_tls(ConnectionHandle ldapHandle); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_rename_extW", CharSet = CharSet.Unicode)] - public static extern int ldap_rename([In] ConnectionHandle ldapHandle, string dn, string newRdn, string newParentDn, int deleteOldRdn, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_compare_extW", CharSet = CharSet.Unicode)] - public static extern int ldap_compare([In] ConnectionHandle ldapHandle, string dn, string attributeName, string strValue, berval binaryValue, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_add_extW", CharSet = CharSet.Unicode)] - public static extern int ldap_add([In] ConnectionHandle ldapHandle, string dn, IntPtr attrs, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_modify_extW", CharSet = CharSet.Unicode)] - public static extern int ldap_modify([In] ConnectionHandle ldapHandle, string dn, IntPtr attrs, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_extended_operationW", CharSet = CharSet.Unicode)] - public static extern int ldap_extended_operation([In] ConnectionHandle ldapHandle, string oid, berval data, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_parse_extended_resultW", CharSet = CharSet.Unicode)] - public static extern int ldap_parse_extended_result([In] ConnectionHandle ldapHandle, [In] IntPtr result, ref IntPtr oid, ref IntPtr data, byte freeIt); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_msgfree", CharSet = CharSet.Unicode)] - public static extern int ldap_msgfree([In] IntPtr result); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_search_extW", CharSet = CharSet.Unicode)] - public static extern int ldap_search([In] ConnectionHandle ldapHandle, string dn, int scope, string filter, IntPtr attributes, bool attributeOnly, IntPtr servercontrol, IntPtr clientcontrol, int timelimit, int sizelimit, ref int messageNumber); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_first_entry", CharSet = CharSet.Unicode)] - public static extern IntPtr ldap_first_entry([In] ConnectionHandle ldapHandle, [In] IntPtr result); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_next_entry", CharSet = CharSet.Unicode)] - public static extern IntPtr ldap_next_entry([In] ConnectionHandle ldapHandle, [In] IntPtr result); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_first_reference", CharSet = CharSet.Unicode)] - public static extern IntPtr ldap_first_reference([In] ConnectionHandle ldapHandle, [In] IntPtr result); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_next_reference", CharSet = CharSet.Unicode)] - public static extern IntPtr ldap_next_reference([In] ConnectionHandle ldapHandle, [In] IntPtr result); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_get_dnW", CharSet = CharSet.Unicode)] - public static extern IntPtr ldap_get_dn([In] ConnectionHandle ldapHandle, [In] IntPtr result); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_first_attributeW", CharSet = CharSet.Unicode)] - public static extern IntPtr ldap_first_attribute([In] ConnectionHandle ldapHandle, [In] IntPtr result, ref IntPtr address); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_next_attributeW", CharSet = CharSet.Unicode)] - public static extern IntPtr ldap_next_attribute([In] ConnectionHandle ldapHandle, [In] IntPtr result, [In, Out] IntPtr address); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_get_values_lenW", CharSet = CharSet.Unicode)] - public static extern IntPtr ldap_get_values_len([In] ConnectionHandle ldapHandle, [In] IntPtr result, string name); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_value_free_len", CharSet = CharSet.Unicode)] - public static extern IntPtr ldap_value_free_len([In] IntPtr berelement); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_parse_referenceW", CharSet = CharSet.Unicode)] - public static extern int ldap_parse_reference([In] ConnectionHandle ldapHandle, [In] IntPtr result, ref IntPtr referrals); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_create_sort_controlW", CharSet = CharSet.Unicode)] - public static extern int ldap_create_sort_control(ConnectionHandle handle, IntPtr keys, byte critical, ref IntPtr control); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_control_freeW", CharSet = CharSet.Unicode)] - public static extern int ldap_control_free(IntPtr control); - - [DllImport("Crypt32.dll", EntryPoint = "CertFreeCRLContext", CharSet = CharSet.Unicode)] - public static extern int CertFreeCRLContext(IntPtr certContext); - - [DllImport(Libraries.Wldap32, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_result2error", CharSet = CharSet.Unicode)] - public static extern int ldap_result2error([In] ConnectionHandle ldapHandle, [In] IntPtr result, int freeIt); +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. + [DllImport(Libraries.Wldap32, EntryPoint = "ldap_bind_sW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static extern int ldap_bind_s(ConnectionHandle ldapHandle, string dn, SEC_WINNT_AUTH_IDENTITY_EX credentials, BindMethod method); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_initW", CharSet = CharSet.Unicode, SetLastError = true)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr ldap_init(string hostName, int portNumber); + +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. + [DllImport(Libraries.Wldap32, EntryPoint = "ldap_connect", CharSet = CharSet.Unicode, ExactSpelling = true)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static extern int ldap_connect(ConnectionHandle ldapHandle, LDAP_TIMEVAL timeout); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_unbind", CharSet = CharSet.Unicode, ExactSpelling = true)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_unbind(IntPtr ldapHandle); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_get_optionW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_get_option_int(ConnectionHandle ldapHandle, LdapOption option, ref int outValue); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_set_optionW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_set_option_int(ConnectionHandle ldapHandle, LdapOption option, ref int inValue); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_get_optionW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_get_option_ptr(ConnectionHandle ldapHandle, LdapOption option, ref IntPtr outValue); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_set_optionW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_set_option_ptr(ConnectionHandle ldapHandle, LdapOption option, ref IntPtr inValue); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_get_optionW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_get_option_sechandle(ConnectionHandle ldapHandle, LdapOption option, ref SecurityHandle outValue); + +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. + [DllImport(Libraries.Wldap32, EntryPoint = "ldap_get_optionW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static extern int ldap_get_option_secInfo(ConnectionHandle ldapHandle, LdapOption option, [In, Out] SecurityPackageContextConnectionInformation outValue); + + [DllImport(Libraries.Wldap32, EntryPoint = "ldap_set_optionW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static extern int ldap_set_option_referral(ConnectionHandle ldapHandle, LdapOption option, ref LdapReferralCallback outValue); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_set_optionW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_set_option_clientcert(ConnectionHandle ldapHandle, LdapOption option, QUERYCLIENTCERT outValue); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_set_optionW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_set_option_servercert(ConnectionHandle ldapHandle, LdapOption option, VERIFYSERVERCERT outValue); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "LdapGetLastError")] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int LdapGetLastError(); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "cldap_openW", CharSet = CharSet.Unicode, SetLastError = true)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr cldap_open(string hostName, int portNumber); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_simple_bind_sW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_simple_bind_s(ConnectionHandle ldapHandle, string distinguishedName, string password); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_delete_extW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_delete_ext(ConnectionHandle ldapHandle, string dn, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); + +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. + [DllImport(Libraries.Wldap32, EntryPoint = "ldap_result", CharSet = CharSet.Unicode, SetLastError = true)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static extern int ldap_result(ConnectionHandle ldapHandle, int messageId, int all, LDAP_TIMEVAL timeout, ref IntPtr Mesage); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_parse_resultW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_parse_result(ConnectionHandle ldapHandle, IntPtr result, ref int serverError, ref IntPtr dn, ref IntPtr message, ref IntPtr referral, ref IntPtr control, byte freeIt); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_parse_resultW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_parse_result_referral(ConnectionHandle ldapHandle, IntPtr result, IntPtr serverError, IntPtr dn, IntPtr message, ref IntPtr referral, IntPtr control, byte freeIt); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_memfreeW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial void ldap_memfree(IntPtr value); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_value_freeW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_value_free(IntPtr value); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_controls_freeW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_controls_free(IntPtr value); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_abandon", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_abandon(ConnectionHandle ldapHandle, int messagId); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_start_tls_sW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_start_tls(ConnectionHandle ldapHandle, ref int ServerReturnValue, ref IntPtr Message, IntPtr ServerControls, IntPtr ClientControls); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_stop_tls_s", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial byte ldap_stop_tls(ConnectionHandle ldapHandle); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_rename_extW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_rename(ConnectionHandle ldapHandle, string dn, string newRdn, string newParentDn, int deleteOldRdn, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); + +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. + [DllImport(Libraries.Wldap32, EntryPoint = "ldap_compare_extW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static extern int ldap_compare(ConnectionHandle ldapHandle, string dn, string attributeName, string strValue, BerVal binaryValue, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_add_extW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_add(ConnectionHandle ldapHandle, string dn, IntPtr attrs, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_modify_extW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_modify(ConnectionHandle ldapHandle, string dn, IntPtr attrs, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); + +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. + [DllImport(Libraries.Wldap32, EntryPoint = "ldap_extended_operationW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static extern int ldap_extended_operation(ConnectionHandle ldapHandle, string oid, BerVal data, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_parse_extended_resultW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_parse_extended_result(ConnectionHandle ldapHandle, IntPtr result, ref IntPtr oid, ref IntPtr data, byte freeIt); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_msgfree", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_msgfree(IntPtr result); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_search_extW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_search(ConnectionHandle ldapHandle, string dn, int scope, string filter, IntPtr attributes, bool attributeOnly, IntPtr servercontrol, IntPtr clientcontrol, int timelimit, int sizelimit, ref int messageNumber); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_first_entry", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr ldap_first_entry(ConnectionHandle ldapHandle, IntPtr result); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_next_entry", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr ldap_next_entry(ConnectionHandle ldapHandle, IntPtr result); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_first_reference", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr ldap_first_reference(ConnectionHandle ldapHandle, IntPtr result); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_next_reference", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr ldap_next_reference(ConnectionHandle ldapHandle, IntPtr result); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_get_dnW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr ldap_get_dn(ConnectionHandle ldapHandle, IntPtr result); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_first_attributeW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr ldap_first_attribute(ConnectionHandle ldapHandle, IntPtr result, ref IntPtr address); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_next_attributeW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr ldap_next_attribute(ConnectionHandle ldapHandle, IntPtr result, IntPtr address); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_get_values_lenW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr ldap_get_values_len(ConnectionHandle ldapHandle, IntPtr result, string name); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_value_free_len", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial IntPtr ldap_value_free_len(IntPtr berelement); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_parse_referenceW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_parse_reference(ConnectionHandle ldapHandle, IntPtr result, ref IntPtr referrals); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_create_sort_controlW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_create_sort_control(ConnectionHandle handle, IntPtr keys, byte critical, ref IntPtr control); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_control_freeW", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_control_free(IntPtr control); + + [GeneratedDllImport("Crypt32.dll", EntryPoint = "CertFreeCRLContext", CharSet = CharSet.Unicode)] + public static partial int CertFreeCRLContext(IntPtr certContext); + + [GeneratedDllImport(Libraries.Wldap32, EntryPoint = "ldap_result2error", CharSet = CharSet.Unicode)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + public static partial int ldap_result2error(ConnectionHandle ldapHandle, IntPtr result, int freeIt); } } diff --git a/src/libraries/Common/src/System/IO/Compression/ZLibNative.cs b/src/libraries/Common/src/System/IO/Compression/ZLibNative.cs index 7457e97ad653db..843c28a879bba2 100644 --- a/src/libraries/Common/src/System/IO/Compression/ZLibNative.cs +++ b/src/libraries/Common/src/System/IO/Compression/ZLibNative.cs @@ -269,7 +269,7 @@ public unsafe ErrorCode DeflateInit2_(CompressionLevel level, int windowBits, in fixed (ZStream* stream = &_zStream) { - ErrorCode errC = Interop.zlib.DeflateInit2_(stream, level, CompressionMethod.Deflated, windowBits, memLevel, strategy); + ErrorCode errC = Interop.ZLib.DeflateInit2_(stream, level, CompressionMethod.Deflated, windowBits, memLevel, strategy); _initializationState = State.InitializedForDeflate; return errC; @@ -284,7 +284,7 @@ public unsafe ErrorCode Deflate(FlushCode flush) fixed (ZStream* stream = &_zStream) { - return Interop.zlib.Deflate(stream, flush); + return Interop.ZLib.Deflate(stream, flush); } } @@ -296,7 +296,7 @@ public unsafe ErrorCode DeflateReset() fixed (ZStream* stream = &_zStream) { - return Interop.zlib.DeflateReset(stream); + return Interop.ZLib.DeflateReset(stream); } } @@ -307,7 +307,7 @@ public unsafe ErrorCode DeflateEnd() fixed (ZStream* stream = &_zStream) { - ErrorCode errC = Interop.zlib.DeflateEnd(stream); + ErrorCode errC = Interop.ZLib.DeflateEnd(stream); _initializationState = State.Disposed; return errC; @@ -322,7 +322,7 @@ public unsafe ErrorCode InflateInit2_(int windowBits) fixed (ZStream* stream = &_zStream) { - ErrorCode errC = Interop.zlib.InflateInit2_(stream, windowBits); + ErrorCode errC = Interop.ZLib.InflateInit2_(stream, windowBits); _initializationState = State.InitializedForInflate; return errC; @@ -337,7 +337,7 @@ public unsafe ErrorCode Inflate(FlushCode flush) fixed (ZStream* stream = &_zStream) { - return Interop.zlib.Inflate(stream, flush); + return Interop.ZLib.Inflate(stream, flush); } } @@ -349,7 +349,7 @@ public unsafe ErrorCode InflateReset() fixed (ZStream* stream = &_zStream) { - return Interop.zlib.InflateReset(stream); + return Interop.ZLib.InflateReset(stream); } } @@ -360,7 +360,7 @@ public unsafe ErrorCode InflateEnd() fixed (ZStream* stream = &_zStream) { - ErrorCode errC = Interop.zlib.InflateEnd(stream); + ErrorCode errC = Interop.ZLib.InflateEnd(stream); _initializationState = State.Disposed; return errC; diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/DynamicTable.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/DynamicTable.cs index 7b496baf83ee57..d57242b0521d73 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/DynamicTable.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/DynamicTable.cs @@ -46,6 +46,11 @@ public ref readonly HeaderField this[int index] } public void Insert(ReadOnlySpan name, ReadOnlySpan value) + { + Insert(staticTableIndex: null, name, value); + } + + public void Insert(int? staticTableIndex, ReadOnlySpan name, ReadOnlySpan value) { int entryLength = HeaderField.GetLength(name.Length, value.Length); EnsureAvailable(entryLength); @@ -59,7 +64,7 @@ public void Insert(ReadOnlySpan name, ReadOnlySpan value) return; } - var entry = new HeaderField(name, value); + var entry = new HeaderField(staticTableIndex, name, value); _buffer[_insertIndex] = entry; _insertIndex = (_insertIndex + 1) % _buffer.Length; _size += entry.Length; diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/H2StaticTable.Http2.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/H2StaticTable.Http2.cs index a4db64ab586221..29ee89f034e2f9 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/H2StaticTable.Http2.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/H2StaticTable.Http2.cs @@ -30,74 +30,75 @@ public static bool TryGetStatusIndex(int status, out int index) private static readonly HeaderField[] s_staticDecoderTable = new HeaderField[] { - CreateHeaderField(":authority", ""), - CreateHeaderField(":method", "GET"), - CreateHeaderField(":method", "POST"), - CreateHeaderField(":path", "/"), - CreateHeaderField(":path", "/index.html"), - CreateHeaderField(":scheme", "http"), - CreateHeaderField(":scheme", "https"), - CreateHeaderField(":status", "200"), - CreateHeaderField(":status", "204"), - CreateHeaderField(":status", "206"), - CreateHeaderField(":status", "304"), - CreateHeaderField(":status", "400"), - CreateHeaderField(":status", "404"), - CreateHeaderField(":status", "500"), - CreateHeaderField("accept-charset", ""), - CreateHeaderField("accept-encoding", "gzip, deflate"), - CreateHeaderField("accept-language", ""), - CreateHeaderField("accept-ranges", ""), - CreateHeaderField("accept", ""), - CreateHeaderField("access-control-allow-origin", ""), - CreateHeaderField("age", ""), - CreateHeaderField("allow", ""), - CreateHeaderField("authorization", ""), - CreateHeaderField("cache-control", ""), - CreateHeaderField("content-disposition", ""), - CreateHeaderField("content-encoding", ""), - CreateHeaderField("content-language", ""), - CreateHeaderField("content-length", ""), - CreateHeaderField("content-location", ""), - CreateHeaderField("content-range", ""), - CreateHeaderField("content-type", ""), - CreateHeaderField("cookie", ""), - CreateHeaderField("date", ""), - CreateHeaderField("etag", ""), - CreateHeaderField("expect", ""), - CreateHeaderField("expires", ""), - CreateHeaderField("from", ""), - CreateHeaderField("host", ""), - CreateHeaderField("if-match", ""), - CreateHeaderField("if-modified-since", ""), - CreateHeaderField("if-none-match", ""), - CreateHeaderField("if-range", ""), - CreateHeaderField("if-unmodified-since", ""), - CreateHeaderField("last-modified", ""), - CreateHeaderField("link", ""), - CreateHeaderField("location", ""), - CreateHeaderField("max-forwards", ""), - CreateHeaderField("proxy-authenticate", ""), - CreateHeaderField("proxy-authorization", ""), - CreateHeaderField("range", ""), - CreateHeaderField("referer", ""), - CreateHeaderField("refresh", ""), - CreateHeaderField("retry-after", ""), - CreateHeaderField("server", ""), - CreateHeaderField("set-cookie", ""), - CreateHeaderField("strict-transport-security", ""), - CreateHeaderField("transfer-encoding", ""), - CreateHeaderField("user-agent", ""), - CreateHeaderField("vary", ""), - CreateHeaderField("via", ""), - CreateHeaderField("www-authenticate", "") + CreateHeaderField(1, ":authority", ""), + CreateHeaderField(2, ":method", "GET"), + CreateHeaderField(3, ":method", "POST"), + CreateHeaderField(4, ":path", "/"), + CreateHeaderField(5, ":path", "/index.html"), + CreateHeaderField(6, ":scheme", "http"), + CreateHeaderField(7, ":scheme", "https"), + CreateHeaderField(8, ":status", "200"), + CreateHeaderField(9, ":status", "204"), + CreateHeaderField(10, ":status", "206"), + CreateHeaderField(11, ":status", "304"), + CreateHeaderField(12, ":status", "400"), + CreateHeaderField(13, ":status", "404"), + CreateHeaderField(14, ":status", "500"), + CreateHeaderField(15, "accept-charset", ""), + CreateHeaderField(16, "accept-encoding", "gzip, deflate"), + CreateHeaderField(17, "accept-language", ""), + CreateHeaderField(18, "accept-ranges", ""), + CreateHeaderField(19, "accept", ""), + CreateHeaderField(20, "access-control-allow-origin", ""), + CreateHeaderField(21, "age", ""), + CreateHeaderField(22, "allow", ""), + CreateHeaderField(23, "authorization", ""), + CreateHeaderField(24, "cache-control", ""), + CreateHeaderField(25, "content-disposition", ""), + CreateHeaderField(26, "content-encoding", ""), + CreateHeaderField(27, "content-language", ""), + CreateHeaderField(28, "content-length", ""), + CreateHeaderField(29, "content-location", ""), + CreateHeaderField(30, "content-range", ""), + CreateHeaderField(31, "content-type", ""), + CreateHeaderField(32, "cookie", ""), + CreateHeaderField(33, "date", ""), + CreateHeaderField(34, "etag", ""), + CreateHeaderField(35, "expect", ""), + CreateHeaderField(36, "expires", ""), + CreateHeaderField(37, "from", ""), + CreateHeaderField(38, "host", ""), + CreateHeaderField(39, "if-match", ""), + CreateHeaderField(40, "if-modified-since", ""), + CreateHeaderField(41, "if-none-match", ""), + CreateHeaderField(42, "if-range", ""), + CreateHeaderField(43, "if-unmodified-since", ""), + CreateHeaderField(44, "last-modified", ""), + CreateHeaderField(45, "link", ""), + CreateHeaderField(46, "location", ""), + CreateHeaderField(47, "max-forwards", ""), + CreateHeaderField(48, "proxy-authenticate", ""), + CreateHeaderField(49, "proxy-authorization", ""), + CreateHeaderField(50, "range", ""), + CreateHeaderField(51, "referer", ""), + CreateHeaderField(52, "refresh", ""), + CreateHeaderField(53, "retry-after", ""), + CreateHeaderField(54, "server", ""), + CreateHeaderField(55, "set-cookie", ""), + CreateHeaderField(56, "strict-transport-security", ""), + CreateHeaderField(57, "transfer-encoding", ""), + CreateHeaderField(58, "user-agent", ""), + CreateHeaderField(59, "vary", ""), + CreateHeaderField(60, "via", ""), + CreateHeaderField(61, "www-authenticate", "") }; // TODO: The HeaderField constructor will allocate and copy again. We should avoid this. // Tackle as part of header table allocation strategy in general (see note in HeaderField constructor). - private static HeaderField CreateHeaderField(string name, string value) => + private static HeaderField CreateHeaderField(int staticTableIndex, string name, string value) => new HeaderField( + staticTableIndex, Encoding.ASCII.GetBytes(name), value.Length != 0 ? Encoding.ASCII.GetBytes(value) : Array.Empty()); } diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackDecoder.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackDecoder.cs index 22e2f8bd1987aa..c287478bbee7ab 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackDecoder.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackDecoder.cs @@ -430,6 +430,11 @@ private void ParseHeaderName(ReadOnlySpan data, ref int currentIndex, IHtt currentIndex += count; _state = State.HeaderValueLength; + ParseHeaderValueLength(data, ref currentIndex, handler); + } + else if (count == 0) + { + // no-op } else { @@ -503,7 +508,7 @@ private void ProcessHeaderValue(ReadOnlySpan data, IHttpHeadersHandler han if (_index) { - _dynamicTable.Insert(H2StaticTable.Get(_headerStaticIndex - 1).Name, headerValueSpan); + _dynamicTable.Insert(_headerStaticIndex, H2StaticTable.Get(_headerStaticIndex - 1).Name, headerValueSpan); } } else @@ -543,7 +548,7 @@ private void OnIndexedHeaderField(int index, IHttpHeadersHandler handler) else { ref readonly HeaderField header = ref GetDynamicHeader(index); - handler.OnHeader(header.Name, header.Value); + handler.OnDynamicIndexedHeader(header.StaticTableIndex, header.Name, header.Value); } _state = State.Ready; diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HeaderField.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HeaderField.cs index aff43658c3abf7..957574c77c1bac 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HeaderField.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HeaderField.cs @@ -11,8 +11,12 @@ internal readonly struct HeaderField // http://httpwg.org/specs/rfc7541.html#rfc.section.4.1 public const int RfcOverhead = 32; - public HeaderField(ReadOnlySpan name, ReadOnlySpan value) + public HeaderField(int? staticTableIndex, ReadOnlySpan name, ReadOnlySpan value) { + // Store the static table index (if there is one) for the header field. + // ASP.NET Core has a fast path that sets a header value using the static table index instead of the name. + StaticTableIndex = staticTableIndex; + Debug.Assert(name.Length > 0); // TODO: We're allocating here on every new table entry. @@ -24,6 +28,8 @@ public HeaderField(ReadOnlySpan name, ReadOnlySpan value) Value = value.ToArray(); } + public int? StaticTableIndex { get; } + public byte[] Name { get; } public byte[] Value { get; } diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackDecoder.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackDecoder.cs index edd361871ca01f..9b7e6a2716ec9f 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackDecoder.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackDecoder.cs @@ -4,6 +4,7 @@ #nullable enable using System.Buffers; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Net.Http.HPack; using System.Numerics; @@ -25,14 +26,11 @@ private enum State HeaderFieldIndex, HeaderNameIndex, HeaderNameLength, - HeaderNameLengthContinue, HeaderName, HeaderValueLength, HeaderValueLengthContinue, HeaderValue, - DynamicTableSizeUpdate, PostBaseIndex, - LiteralHeaderFieldWithNameReference, HeaderNameIndexPostBase } @@ -105,64 +103,53 @@ private enum State private const int StringLengthPrefix = 7; private const byte HuffmanMask = 0x80; - private const int DefaultStringBufferSize = 64; + private const int HeaderStaticIndexUnset = -1; // Static index starts at 0 private readonly int _maxHeadersLength; private State _state = State.RequiredInsertCount; - private byte[] _stringOctets; - private byte[] _headerNameOctets; - private byte[] _headerValueOctets; - // s is used for whatever s is in each field. This has multiple definition private bool _huffman; - private int? _index; private byte[]? _headerName; + private int _headerStaticIndex; private int _headerNameLength; private int _headerValueLength; private int _stringLength; private int _stringIndex; + private IntegerDecoder _integerDecoder; + private byte[]? _stringOctets; + private byte[]? _headerNameOctets; + private byte[]? _headerValueOctets; + private (int start, int length)? _headerNameRange; + private (int start, int length)? _headerValueRange; private static ArrayPool Pool => ArrayPool.Shared; - private static void ReturnAndGetNewPooledArray(ref byte[] buffer, int newSize) - { - byte[] old = buffer; - buffer = null!; - - Pool.Return(old, clearArray: true); - buffer = Pool.Rent(newSize); - } - public QPackDecoder(int maxHeadersLength) { _maxHeadersLength = maxHeadersLength; - - // TODO: make allocation lazy? with static entries it's possible no buffers will be needed. - _stringOctets = Pool.Rent(DefaultStringBufferSize); - _headerNameOctets = Pool.Rent(DefaultStringBufferSize); - _headerValueOctets = Pool.Rent(DefaultStringBufferSize); + _headerStaticIndex = HeaderStaticIndexUnset; } public void Dispose() { if (_stringOctets != null) { - Pool.Return(_stringOctets, true); + Pool.Return(_stringOctets); _stringOctets = null!; } if (_headerNameOctets != null) { - Pool.Return(_headerNameOctets, true); + Pool.Return(_headerNameOctets); _headerNameOctets = null!; } if (_headerValueOctets != null) { - Pool.Return(_headerValueOctets, true); + Pool.Return(_headerValueOctets); _headerValueOctets = null!; } } @@ -176,303 +163,488 @@ public void Reset() _state = State.RequiredInsertCount; } - public void Decode(in ReadOnlySequence headerBlock, bool endHeaders, IHttpHeadersHandler handler) + public void Decode(in ReadOnlySequence data, bool endHeaders, IHttpHeadersHandler handler) { - foreach (ReadOnlyMemory segment in headerBlock) + foreach (ReadOnlyMemory segment in data) { - DecodeCore(segment.Span, handler); + DecodeInternal(segment.Span, handler); } CheckIncompleteHeaderBlock(endHeaders); } - public void Decode(ReadOnlySpan headerBlock, bool endHeaders, IHttpHeadersHandler handler) + public void Decode(ReadOnlySpan data, bool endHeaders, IHttpHeadersHandler handler) { - DecodeCore(headerBlock, handler); + DecodeInternal(data, handler); CheckIncompleteHeaderBlock(endHeaders); } - private void DecodeCore(ReadOnlySpan headerBlock, IHttpHeadersHandler handler) + private void DecodeInternal(ReadOnlySpan data, IHttpHeadersHandler handler) { - foreach (byte b in headerBlock) + int currentIndex = 0; + + do { - OnByte(b, handler); + switch (_state) + { + case State.RequiredInsertCount: + ParseRequiredInsertCount(data, ref currentIndex, handler); + break; + case State.RequiredInsertCountContinue: + ParseRequiredInsertCountContinue(data, ref currentIndex, handler); + break; + case State.Base: + ParseBase(data, ref currentIndex, handler); + break; + case State.BaseContinue: + ParseBaseContinue(data, ref currentIndex, handler); + break; + case State.CompressedHeaders: + ParseCompressedHeaders(data, ref currentIndex, handler); + break; + case State.HeaderFieldIndex: + ParseHeaderFieldIndex(data, ref currentIndex, handler); + break; + case State.HeaderNameIndex: + ParseHeaderNameIndex(data, ref currentIndex, handler); + break; + case State.HeaderNameLength: + ParseHeaderNameLength(data, ref currentIndex, handler); + break; + case State.HeaderName: + ParseHeaderName(data, ref currentIndex, handler); + break; + case State.HeaderValueLength: + ParseHeaderValueLength(data, ref currentIndex, handler); + break; + case State.HeaderValueLengthContinue: + ParseHeaderValueLengthContinue(data, ref currentIndex, handler); + break; + case State.HeaderValue: + ParseHeaderValue(data, ref currentIndex, handler); + break; + case State.PostBaseIndex: + ParsePostBaseIndex(data, ref currentIndex, handler); + break; + case State.HeaderNameIndexPostBase: + ParseHeaderNameIndexPostBase(data, ref currentIndex, handler); + break; + default: + // Can't happen + Debug.Fail("QPACK decoder reach an invalid state"); + throw new NotImplementedException(_state.ToString()); + } + } + // Parse methods each check the length. This check is to see whether there is still data available + // and to continue parsing. + while (currentIndex < data.Length); + + // If a header range was set, but the value was not in the data, then copy the range + // to the name buffer. Must copy because because the data will be replaced and the range + // will no longer be valid. + if (_headerNameRange != null) + { + EnsureStringCapacity(ref _headerNameOctets, _stringLength, existingLength: 0); + _headerName = _headerNameOctets; + + ReadOnlySpan headerBytes = data.Slice(_headerNameRange.GetValueOrDefault().start, _headerNameRange.GetValueOrDefault().length); + headerBytes.CopyTo(_headerName); + _headerNameLength = headerBytes.Length; + _headerNameRange = null; } } - private void CheckIncompleteHeaderBlock(bool endHeaders) + private void ParseHeaderNameIndexPostBase(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) { - if (endHeaders) + if (TryDecodeInteger(data, ref currentIndex, out int intResult)) { - if (_state != State.CompressedHeaders) + OnIndexedHeaderNamePostBase(intResult); + } + } + + private void ParsePostBaseIndex(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) + { + if (TryDecodeInteger(data, ref currentIndex, out int intResult)) + { + OnPostBaseIndex(intResult, handler); + } + } + + private void ParseHeaderNameLength(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) + { + if (TryDecodeInteger(data, ref currentIndex, out int intResult)) + { + if (intResult == 0) { - throw new QPackDecodingException(SR.net_http_hpack_incomplete_header_block); + throw new QPackDecodingException(SR.Format(SR.net_http_invalid_header_name, "")); } + OnStringLength(intResult, nextState: State.HeaderName); + ParseHeaderName(data, ref currentIndex, handler); } } - private void OnByte(byte b, IHttpHeadersHandler handler) + private void ParseHeaderName(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) { - int intResult; - int prefixInt; - switch (_state) + // Read remaining chars, up to the length of the current data + int count = Math.Min(_stringLength - _stringIndex, data.Length - currentIndex); + + // Check whether the whole string is available in the data and no decompression required. + // If string is good then mark its range. + // NOTE: it may need to be copied to buffer later the if value is not current data. + if (count == _stringLength && !_huffman) { - case State.RequiredInsertCount: - if (_integerDecoder.BeginTryDecode(b, RequiredInsertCountPrefix, out intResult)) - { - OnRequiredInsertCount(intResult); - } - else - { - _state = State.RequiredInsertCountContinue; - } - break; - case State.RequiredInsertCountContinue: - if (_integerDecoder.TryDecode(b, out intResult)) - { - OnRequiredInsertCount(intResult); - } - break; - case State.Base: - prefixInt = ~BaseMask & b; + // Fast path. Store the range rather than copying. + _headerNameRange = (start: currentIndex, count); + currentIndex += count; + + _state = State.HeaderValueLength; + ParseHeaderValueLength(data, ref currentIndex, handler); + } + else if (count == 0) + { + // no-op + } + else + { + // Copy string to temporary buffer. + EnsureStringCapacity(ref _stringOctets, _stringIndex + count, existingLength: _stringIndex); + data.Slice(currentIndex, count).CopyTo(_stringOctets.AsSpan(_stringIndex)); + + _stringIndex += count; + currentIndex += count; + + if (_stringIndex == _stringLength) + { + OnString(nextState: State.HeaderValueLength); + ParseHeaderValueLength(data, ref currentIndex, handler); + } + } + } + + private void ParseHeaderFieldIndex(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) + { + if (TryDecodeInteger(data, ref currentIndex, out int intResult)) + { + OnIndexedHeaderField(intResult, handler); + } + } + + private void ParseHeaderNameIndex(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) + { + if (TryDecodeInteger(data, ref currentIndex, out int intResult)) + { + OnIndexedHeaderName(intResult); + ParseHeaderValueLength(data, ref currentIndex, handler); + } + } + + private void ParseHeaderValueLength(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) + { + if (currentIndex < data.Length) + { + byte b = data[currentIndex++]; + + _huffman = IsHuffmanEncoded(b); - if (_integerDecoder.BeginTryDecode(b, BasePrefix, out intResult)) + if (_integerDecoder.BeginTryDecode((byte)(b & ~HuffmanMask), StringLengthPrefix, out int intResult)) + { + OnStringLength(intResult, nextState: State.HeaderValue); + + if (intResult == 0) { - OnBase(intResult); + _state = State.CompressedHeaders; + ProcessHeaderValue(data, handler); } else { - _state = State.BaseContinue; - } - break; - case State.BaseContinue: - if (_integerDecoder.TryDecode(b, out intResult)) - { - OnBase(intResult); + ParseHeaderValue(data, ref currentIndex, handler); } - break; - case State.CompressedHeaders: - switch (BitOperations.LeadingZeroCount(b) - 24) // byte 'b' is extended to uint, so will have 24 extra 0s. - { - case 0: // Indexed Header Field - prefixInt = IndexedHeaderFieldPrefixMask & b; + } + else + { + _state = State.HeaderValueLengthContinue; + ParseHeaderValueLengthContinue(data, ref currentIndex, handler); + } + } + } - bool useStaticTable = (b & IndexedHeaderStaticMask) == IndexedHeaderStaticRepresentation; + private void ParseHeaderValue(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) + { + // Read remaining chars, up to the length of the current data + int count = Math.Min(_stringLength - _stringIndex, data.Length - currentIndex); - if (!useStaticTable) - { - ThrowDynamicTableNotSupported(); - } + // Check whether the whole string is available in the data and no decompressed required. + // If string is good then mark its range. + if (count == _stringLength && !_huffman) + { + // Fast path. Store the range rather than copying. + _headerValueRange = (start: currentIndex, count); + currentIndex += count; - if (_integerDecoder.BeginTryDecode((byte)prefixInt, IndexedHeaderFieldPrefix, out intResult)) - { - OnIndexedHeaderField(intResult, handler); - } - else - { - _state = State.HeaderFieldIndex; - } - break; - case 1: // Literal Header Field With Name Reference - useStaticTable = (LiteralHeaderFieldStaticMask & b) == LiteralHeaderFieldStaticMask; + _state = State.CompressedHeaders; + ProcessHeaderValue(data, handler); + } + else if (count == 0) + { + // no-op + } + else + { + // Copy string to temporary buffer. + EnsureStringCapacity(ref _stringOctets, _stringIndex + count, existingLength: _stringIndex); + data.Slice(currentIndex, count).CopyTo(_stringOctets.AsSpan(_stringIndex)); - if (!useStaticTable) - { - ThrowDynamicTableNotSupported(); - } + _stringIndex += count; + currentIndex += count; - prefixInt = b & LiteralHeaderFieldPrefixMask; - if (_integerDecoder.BeginTryDecode((byte)prefixInt, LiteralHeaderFieldPrefix, out intResult)) - { - OnIndexedHeaderName(intResult); - } - else - { - _state = State.HeaderNameIndex; - } - break; - case 2: // Literal Header Field Without Name Reference - _huffman = (b & LiteralHeaderFieldWithoutNameReferenceHuffmanMask) != 0; - prefixInt = b & LiteralHeaderFieldWithoutNameReferencePrefixMask; + if (_stringIndex == _stringLength) + { + OnString(nextState: State.CompressedHeaders); + ProcessHeaderValue(data, handler); + } + } + } - if (_integerDecoder.BeginTryDecode((byte)prefixInt, LiteralHeaderFieldWithoutNameReferencePrefix, out intResult)) - { - if (intResult == 0) - { - throw new QPackDecodingException(SR.Format(SR.net_http_invalid_header_name, "")); - } - OnStringLength(intResult, State.HeaderName); - } - else - { - _state = State.HeaderNameLength; - } - break; - case 3: // Indexed Header Field With Post-Base Index - prefixInt = ~PostBaseIndexMask & b; - if (_integerDecoder.BeginTryDecode((byte)prefixInt, PostBaseIndexPrefix, out intResult)) - { - OnPostBaseIndex(intResult, handler); - } - else - { - _state = State.PostBaseIndex; - } - break; - default: // Literal Header Field With Post-Base Name Reference (at least 4 zeroes, maybe more) - prefixInt = b & LiteralHeaderFieldPostBasePrefixMask; - if (_integerDecoder.BeginTryDecode((byte)prefixInt, LiteralHeaderFieldPostBasePrefix, out intResult)) - { - OnIndexedHeaderNamePostBase(intResult); - } - else - { - _state = State.HeaderNameIndexPostBase; - } - break; - } - break; - case State.HeaderNameLength: - if (_integerDecoder.TryDecode(b, out intResult)) - { - if (intResult == 0) + private void ParseHeaderValueLengthContinue(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) + { + if (TryDecodeInteger(data, ref currentIndex, out int intResult)) + { + if (intResult == 0) + { + _state = State.CompressedHeaders; + ProcessHeaderValue(data, handler); + } + else + { + OnStringLength(intResult, nextState: State.HeaderValue); + ParseHeaderValue(data, ref currentIndex, handler); + } + } + } + + private void ParseCompressedHeaders(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) + { + if (currentIndex < data.Length) + { + Debug.Assert(_state == State.CompressedHeaders, "Should be ready to parse a new header."); + + byte b = data[currentIndex++]; + int prefixInt; + int intResult; + + switch (BitOperations.LeadingZeroCount(b) - 24) // byte 'b' is extended to uint, so will have 24 extra 0s. + { + case 0: // Indexed Header Field + prefixInt = IndexedHeaderFieldPrefixMask & b; + + bool useStaticTable = (b & IndexedHeaderStaticMask) == IndexedHeaderStaticRepresentation; + + if (!useStaticTable) { - throw new QPackDecodingException(SR.Format(SR.net_http_invalid_header_name, "")); + ThrowDynamicTableNotSupported(); } - OnStringLength(intResult, nextState: State.HeaderName); - } - break; - case State.HeaderName: - _stringOctets[_stringIndex++] = b; - if (_stringIndex == _stringLength) - { - OnString(nextState: State.HeaderValueLength); - } + if (_integerDecoder.BeginTryDecode((byte)prefixInt, IndexedHeaderFieldPrefix, out intResult)) + { + OnIndexedHeaderField(intResult, handler); + } + else + { + _state = State.HeaderFieldIndex; + ParseHeaderFieldIndex(data, ref currentIndex, handler); + } + break; + case 1: // Literal Header Field With Name Reference + useStaticTable = (LiteralHeaderFieldStaticMask & b) == LiteralHeaderFieldStaticMask; - break; - case State.HeaderNameIndex: - if (_integerDecoder.TryDecode(b, out intResult)) - { - OnIndexedHeaderName(intResult); - } - break; - case State.HeaderNameIndexPostBase: - if (_integerDecoder.TryDecode(b, out intResult)) - { - OnIndexedHeaderNamePostBase(intResult); - } - break; - case State.HeaderValueLength: - _huffman = (b & HuffmanMask) != 0; + if (!useStaticTable) + { + ThrowDynamicTableNotSupported(); + } - // TODO confirm this. - if (_integerDecoder.BeginTryDecode((byte)(b & ~HuffmanMask), StringLengthPrefix, out intResult)) - { - OnStringLength(intResult, nextState: State.HeaderValue); - if (intResult == 0) + prefixInt = b & LiteralHeaderFieldPrefixMask; + if (_integerDecoder.BeginTryDecode((byte)prefixInt, LiteralHeaderFieldPrefix, out intResult)) { - ProcessHeaderValue(handler); + OnIndexedHeaderName(intResult); + ParseHeaderValueLength(data, ref currentIndex, handler); } - } - else - { - _state = State.HeaderValueLengthContinue; - } - break; - case State.HeaderValueLengthContinue: - if (_integerDecoder.TryDecode(b, out intResult)) - { - OnStringLength(intResult, nextState: State.HeaderValue); - if (intResult == 0) + else { - ProcessHeaderValue(handler); + _state = State.HeaderNameIndex; + ParseHeaderNameIndex(data, ref currentIndex, handler); } - } - break; - case State.HeaderValue: - _stringOctets[_stringIndex++] = b; - if (_stringIndex == _stringLength) - { - ProcessHeaderValue(handler); - } - break; - case State.HeaderFieldIndex: - if (_integerDecoder.TryDecode(b, out intResult)) - { - OnIndexedHeaderField(intResult, handler); - } - break; - case State.PostBaseIndex: - if (_integerDecoder.TryDecode(b, out intResult)) - { - OnPostBaseIndex(intResult, handler); - } - break; - case State.LiteralHeaderFieldWithNameReference: - break; + break; + case 2: // Literal Header Field Without Name Reference + _huffman = (b & LiteralHeaderFieldWithoutNameReferenceHuffmanMask) != 0; + prefixInt = b & LiteralHeaderFieldWithoutNameReferencePrefixMask; + + if (_integerDecoder.BeginTryDecode((byte)prefixInt, LiteralHeaderFieldWithoutNameReferencePrefix, out intResult)) + { + if (intResult == 0) + { + throw new QPackDecodingException(SR.Format(SR.net_http_invalid_header_name, "")); + } + OnStringLength(intResult, State.HeaderName); + ParseHeaderName(data, ref currentIndex, handler); + } + else + { + _state = State.HeaderNameLength; + ParseHeaderNameLength(data, ref currentIndex, handler); + } + break; + case 3: // Indexed Header Field With Post-Base Index + prefixInt = ~PostBaseIndexMask & b; + if (_integerDecoder.BeginTryDecode((byte)prefixInt, PostBaseIndexPrefix, out intResult)) + { + OnPostBaseIndex(intResult, handler); + } + else + { + _state = State.PostBaseIndex; + ParsePostBaseIndex(data, ref currentIndex, handler); + } + break; + default: // Literal Header Field With Post-Base Name Reference (at least 4 zeroes, maybe more) + prefixInt = b & LiteralHeaderFieldPostBasePrefixMask; + if (_integerDecoder.BeginTryDecode((byte)prefixInt, LiteralHeaderFieldPostBasePrefix, out intResult)) + { + OnIndexedHeaderNamePostBase(intResult); + } + else + { + _state = State.HeaderNameIndexPostBase; + ParseHeaderNameIndexPostBase(data, ref currentIndex, handler); + } + break; + } } } - private void OnStringLength(int length, State nextState) + private void ParseRequiredInsertCountContinue(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) + { + if (TryDecodeInteger(data, ref currentIndex, out int intResult)) + { + OnRequiredInsertCount(intResult); + ParseBase(data, ref currentIndex, handler); + } + } + + private void ParseBase(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) { - if (length > _stringOctets.Length) + if (currentIndex < data.Length) { - if (length > _maxHeadersLength) + byte b = data[currentIndex++]; + int prefixInt = ~BaseMask & b; + + if (_integerDecoder.BeginTryDecode((byte)prefixInt, BasePrefix, out int intResult)) { - throw new QPackDecodingException(SR.Format(SR.net_http_headers_exceeded_length, _maxHeadersLength)); + OnBase(intResult); + ParseCompressedHeaders(data, ref currentIndex, handler); + } + else + { + _state = State.BaseContinue; + ParseBaseContinue(data, ref currentIndex, handler); } - - ReturnAndGetNewPooledArray(ref _stringOctets, length); } + } - _stringLength = length; - _stringIndex = 0; - _state = nextState; + private void ParseBaseContinue(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) + { + if (TryDecodeInteger(data, ref currentIndex, out int intResult)) + { + OnBase(intResult); + ParseCompressedHeaders(data, ref currentIndex, handler); + } } - private void ProcessHeaderValue(IHttpHeadersHandler handler) + private void ParseRequiredInsertCount(ReadOnlySpan data, ref int currentIndex, IHttpHeadersHandler handler) { - OnString(nextState: State.CompressedHeaders); + if (currentIndex < data.Length) + { + byte b = data[currentIndex++]; - Span headerNameSpan; - Span headerValueSpan = _headerValueOctets.AsSpan(0, _headerValueLength); + if (_integerDecoder.BeginTryDecode(b, RequiredInsertCountPrefix, out int intResult)) + { + OnRequiredInsertCount(intResult); + ParseBase(data, ref currentIndex, handler); + } + else + { + _state = State.RequiredInsertCountContinue; + ParseRequiredInsertCountContinue(data, ref currentIndex, handler); + } + } + } - if (_index is int index) + private void CheckIncompleteHeaderBlock(bool endHeaders) + { + if (endHeaders) { - Debug.Assert(index >= 0 && index <= H3StaticTable.Count, $"The index should be a valid static index here. {nameof(QPackDecoder)} should have previously thrown if it read a dynamic index."); - handler.OnStaticIndexedHeader(index, headerValueSpan); - _index = null; + if (_state != State.CompressedHeaders) + { + throw new QPackDecodingException(SR.net_http_hpack_incomplete_header_block); + } + } + } + + private void ProcessHeaderValue(ReadOnlySpan data, IHttpHeadersHandler handler) + { + ReadOnlySpan headerValueSpan = _headerValueRange == null + ? _headerValueOctets.AsSpan(0, _headerValueLength) + : data.Slice(_headerValueRange.GetValueOrDefault().start, _headerValueRange.GetValueOrDefault().length); - return; + if (_headerStaticIndex != HeaderStaticIndexUnset) + { + handler.OnStaticIndexedHeader(_headerStaticIndex, headerValueSpan); } else { - headerNameSpan = _headerNameOctets.AsSpan(0, _headerNameLength); + ReadOnlySpan headerNameSpan = _headerNameRange == null + ? _headerName.AsSpan(0, _headerNameLength) + : data.Slice(_headerNameRange.GetValueOrDefault().start, _headerNameRange.GetValueOrDefault().length); + + handler.OnHeader(headerNameSpan, headerValueSpan); + } + + _headerStaticIndex = HeaderStaticIndexUnset; + _headerNameRange = null; + _headerNameLength = 0; + _headerValueRange = null; + _headerValueLength = 0; + } + + private void OnStringLength(int length, State nextState) + { + if (length > _maxHeadersLength) + { + throw new QPackDecodingException(SR.Format(SR.net_http_headers_exceeded_length, _maxHeadersLength)); } - handler.OnHeader(headerNameSpan, headerValueSpan); + _stringLength = length; + _stringIndex = 0; + _state = nextState; } private void OnString(State nextState) { - int Decode(ref byte[] dst) + int Decode(ref byte[]? dst) { + EnsureStringCapacity(ref dst, _stringLength, existingLength: 0); + if (_huffman) { return Huffman.Decode(new ReadOnlySpan(_stringOctets, 0, _stringLength), ref dst); } else { - if (dst.Length < _stringLength) - { - ReturnAndGetNewPooledArray(ref dst, _stringLength); - } - Buffer.BlockCopy(_stringOctets, 0, dst, 0, _stringLength); return _stringLength; } } + Debug.Assert(_stringOctets != null, "String buffer should have a value."); + try { if (_state == State.HeaderName) @@ -493,10 +665,48 @@ int Decode(ref byte[] dst) _state = nextState; } + private static void EnsureStringCapacity([NotNull] ref byte[]? buffer, int requiredLength, int existingLength) + { + if (buffer == null) + { + buffer = Pool.Rent(requiredLength); + } + else if (buffer.Length < requiredLength) + { + byte[] newBuffer = Pool.Rent(requiredLength); + if (existingLength > 0) + { + buffer.AsMemory(0, existingLength).CopyTo(newBuffer); + } + + Pool.Return(buffer); + buffer = newBuffer; + } + } + + private bool TryDecodeInteger(ReadOnlySpan data, ref int currentIndex, out int result) + { + for (; currentIndex < data.Length; currentIndex++) + { + if (_integerDecoder.TryDecode(data[currentIndex], out result)) + { + currentIndex++; + return true; + } + } + + result = default; + return false; + } + + private static bool IsHuffmanEncoded(byte b) + { + return (b & HuffmanMask) != 0; + } private void OnIndexedHeaderName(int index) { - _index = index; + _headerStaticIndex = index; _state = State.HeaderValueLength; } diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/IHttpHeadersHandler.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/IHttpHeadersHandler.cs index 824889cf79da3d..96dfae364252cd 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/IHttpHeadersHandler.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/IHttpHeadersHandler.cs @@ -21,5 +21,11 @@ interface IHttpHeadersHandler void OnStaticIndexedHeader(int index, ReadOnlySpan value); void OnHeader(ReadOnlySpan name, ReadOnlySpan value); void OnHeadersComplete(bool endStream); + + // DIM to avoid breaking change on public interface (for Kestrel). + public void OnDynamicIndexedHeader(int? index, ReadOnlySpan name, ReadOnlySpan value) + { + OnHeader(name, value); + } } } diff --git a/src/libraries/Common/src/System/SR.cs b/src/libraries/Common/src/System/SR.cs index 5d8d88b0d6d8ca..015c3c398e6b18 100644 --- a/src/libraries/Common/src/System/SR.cs +++ b/src/libraries/Common/src/System/SR.cs @@ -27,7 +27,7 @@ internal static string GetResourceString(string resourceKey) try { resourceString = -#if SYSTEM_PRIVATE_CORELIB +#if SYSTEM_PRIVATE_CORELIB || CORERT InternalGetResourceString(resourceKey); #else ResourceManager.GetString(resourceKey); diff --git a/src/libraries/System.IO.FileSystem/tests/PortedCommon/ReparsePointUtilities.cs b/src/libraries/Common/tests/System/IO/ReparsePointUtilities.cs similarity index 70% rename from src/libraries/System.IO.FileSystem/tests/PortedCommon/ReparsePointUtilities.cs rename to src/libraries/Common/tests/System/IO/ReparsePointUtilities.cs index 87c96cfed7916d..bbc3ce8a55b18b 100644 --- a/src/libraries/System.IO.FileSystem/tests/PortedCommon/ReparsePointUtilities.cs +++ b/src/libraries/Common/tests/System/IO/ReparsePointUtilities.cs @@ -17,6 +17,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; +using Xunit; public static class MountHelper { @@ -28,11 +29,62 @@ public static class MountHelper [DllImport("kernel32.dll", EntryPoint = "DeleteVolumeMountPointW", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)] private static extern bool DeleteVolumeMountPoint(string mountPoint); + // Helper for ConditionalClass attributes + internal static bool IsSubstAvailable => PlatformDetection.IsSubstAvailable; + + /// + /// In some cases (such as when running without elevated privileges), + /// the symbolic link may fail to create. Only run this test if it creates + /// links successfully. + /// + internal static bool CanCreateSymbolicLinks => s_canCreateSymbolicLinks.Value; + + private static readonly Lazy s_canCreateSymbolicLinks = new Lazy(() => + { + bool success = true; + + // Verify file symlink creation + string path = Path.GetTempFileName(); + string linkPath = path + ".link"; + success = CreateSymbolicLink(linkPath: linkPath, targetPath: path, isDirectory: false); + try { File.Delete(path); } catch { } + try { File.Delete(linkPath); } catch { } + + // Verify directory symlink creation + path = Path.GetTempFileName(); + linkPath = path + ".link"; + success = success && CreateSymbolicLink(linkPath: linkPath, targetPath: path, isDirectory: true); + try { Directory.Delete(path); } catch { } + try { Directory.Delete(linkPath); } catch { } + + // Reduce the risk we accidentally stop running these altogether + // on Windows, due to a bug in CreateSymbolicLink + if (!success && PlatformDetection.IsWindows) + Assert.True(!PlatformDetection.IsWindowsAndElevated); + + return success; + }); + /// Creates a symbolic link using command line tools. public static bool CreateSymbolicLink(string linkPath, string targetPath, bool isDirectory) { + // It's easy to get the parameters backwards. + Assert.EndsWith(".link", linkPath); + if (linkPath != targetPath) // testing loop + Assert.False(targetPath.EndsWith(".link"), $"{targetPath} should not end with .link"); + +#if NETFRAMEWORK + bool isWindows = true; +#else + if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsMacCatalyst() || OperatingSystem.IsBrowser()) // OSes that don't support Process.Start() + { + return false; + } + + bool isWindows = OperatingSystem.IsWindows(); +#endif Process symLinkProcess = new Process(); - if (OperatingSystem.IsWindows()) + if (isWindows) { symLinkProcess.StartInfo.FileName = "cmd"; symLinkProcess.StartInfo.Arguments = string.Format("/c mklink{0} \"{1}\" \"{2}\"", isDirectory ? " /D" : "", linkPath, targetPath); @@ -44,10 +96,12 @@ public static bool CreateSymbolicLink(string linkPath, string targetPath, bool i } symLinkProcess.StartInfo.UseShellExecute = false; symLinkProcess.StartInfo.RedirectStandardOutput = true; + symLinkProcess.Start(); symLinkProcess.WaitForExit(); - return symLinkProcess.ExitCode == 0; + + return (symLinkProcess.ExitCode == 0); } /// On Windows, creates a junction using command line tools. diff --git a/src/libraries/Common/tests/System/Xml/XmlCoreTest/AsyncUtil.cs b/src/libraries/Common/tests/System/Xml/XmlCoreTest/AsyncUtil.cs index 80b751518501b5..d60e4c35d54eef 100644 --- a/src/libraries/Common/tests/System/Xml/XmlCoreTest/AsyncUtil.cs +++ b/src/libraries/Common/tests/System/Xml/XmlCoreTest/AsyncUtil.cs @@ -15,8 +15,6 @@ /// namespace XmlCoreTest.Common { - public class aaaa { }; - public class AsyncUtil { public static bool IsAsyncEnabled diff --git a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs index 04b27885d4d385..4f9b32add6f0d2 100644 --- a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs +++ b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using Xunit; +using Microsoft.Win32.SafeHandles; +using System.Buffers; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; @@ -9,7 +11,7 @@ namespace System.IO { /// Base class for test classes the use temporary files that need to be cleaned up. - public abstract class FileCleanupTestBase : IDisposable + public abstract partial class FileCleanupTestBase : IDisposable { private static readonly Lazy s_isElevated = new Lazy(() => AdminHelpers.IsProcessElevated()); @@ -77,6 +79,16 @@ protected virtual void Dispose(bool disposing) /// protected string TestDirectory { get; } + protected string GetRandomFileName() => GetTestFileName() + ".txt"; + protected string GetRandomLinkName() => GetTestFileName() + ".link"; + protected string GetRandomDirName() => GetTestFileName() + "_dir"; + + protected string GetRandomFilePath() => Path.Combine(ActualTestDirectory.Value, GetRandomFileName()); + protected string GetRandomLinkPath() => Path.Combine(ActualTestDirectory.Value, GetRandomLinkName()); + protected string GetRandomDirPath() => Path.Combine(ActualTestDirectory.Value, GetRandomDirName()); + + private Lazy ActualTestDirectory => new Lazy(() => GetTestDirectoryActualCasing()); + /// Gets a test file full path that is associated with the call site. /// An optional index value to use as a suffix on the file name. Typically a loop index. /// The member name of the function calling this method. @@ -130,62 +142,52 @@ private string GenerateTestFileName(int? index, string memberName, int lineNumbe index.GetValueOrDefault(), Guid.NewGuid().ToString("N").Substring(0, 8)); // randomness to avoid collisions between derived test classes using same base method concurrently - /// - /// In some cases (such as when running without elevated privileges), - /// the symbolic link may fail to create. Only run this test if it creates - /// links successfully. - /// - protected static bool CanCreateSymbolicLinks => s_canCreateSymbolicLinks.Value; - - private static readonly Lazy s_canCreateSymbolicLinks = new Lazy(() => - { - bool success = true; - - // Verify file symlink creation - string path = Path.GetTempFileName(); - string linkPath = path + ".link"; - success = CreateSymLink(path, linkPath, isDirectory: false); - try { File.Delete(path); } catch { } - try { File.Delete(linkPath); } catch { } - - // Verify directory symlink creation - path = Path.GetTempFileName(); - linkPath = path + ".link"; - success = success && CreateSymLink(path, linkPath, isDirectory: true); - try { Directory.Delete(path); } catch { } - try { Directory.Delete(linkPath); } catch { } - - return success; - }); - - protected static bool CreateSymLink(string targetPath, string linkPath, bool isDirectory) + // Some Windows versions like Windows Nano Server have the %TEMP% environment variable set to "C:\TEMP" but the + // actual folder name is "C:\Temp", which prevents asserting path values using Assert.Equal due to case sensitiveness. + // So instead of using TestDirectory directly, we retrieve the real path with proper casing of the initial folder path. + private unsafe string GetTestDirectoryActualCasing() { -#if NETFRAMEWORK - bool isWindows = true; -#else - if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsMacCatalyst() || OperatingSystem.IsBrowser()) // OSes that don't support Process.Start() - { - return false; - } - bool isWindows = OperatingSystem.IsWindows(); -#endif - Process symLinkProcess = new Process(); - if (isWindows) - { - symLinkProcess.StartInfo.FileName = "cmd"; - symLinkProcess.StartInfo.Arguments = string.Format("/c mklink{0} \"{1}\" \"{2}\"", isDirectory ? " /D" : "", Path.GetFullPath(linkPath), Path.GetFullPath(targetPath)); - } - else + if (!PlatformDetection.IsWindows) + return TestDirectory; + + try { - symLinkProcess.StartInfo.FileName = "/bin/ln"; - symLinkProcess.StartInfo.Arguments = string.Format("-s \"{0}\" \"{1}\"", Path.GetFullPath(targetPath), Path.GetFullPath(linkPath)); + using SafeFileHandle handle = Interop.Kernel32.CreateFile( + TestDirectory, + dwDesiredAccess: 0, + dwShareMode: FileShare.ReadWrite | FileShare.Delete, + dwCreationDisposition: FileMode.Open, + dwFlagsAndAttributes: + Interop.Kernel32.FileOperations.OPEN_EXISTING | + Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS // Necessary to obtain a handle to a directory + ); + + if (!handle.IsInvalid) + { + const int InitialBufferSize = 4096; + char[]? buffer = ArrayPool.Shared.Rent(InitialBufferSize); + uint result = GetFinalPathNameByHandle(handle, buffer); + + // Remove extended prefix + int skip = PathInternal.IsExtended(buffer) ? 4 : 0; + + return new string( + buffer, + skip, + (int)result - skip); + } } - symLinkProcess.StartInfo.RedirectStandardOutput = true; - symLinkProcess.Start(); + catch { } - symLinkProcess.WaitForExit(); - return (0 == symLinkProcess.ExitCode); + return TestDirectory; } + private unsafe uint GetFinalPathNameByHandle(SafeFileHandle handle, char[] buffer) + { + fixed (char* bufPtr = buffer) + { + return Interop.Kernel32.GetFinalPathNameByHandle(handle, bufPtr, (uint)buffer.Length, Interop.Kernel32.FILE_NAME_NORMALIZED); + } + } } } diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs index b75c0fee68d954..ac167ec7cecaf4 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs @@ -41,6 +41,8 @@ public static partial class PlatformDetection public static bool IsMacOsCatalinaOrHigher => IsOSX && Environment.OSVersion.Version >= new Version(10, 15); public static bool IsMacOsAppleSilicon => IsOSX && IsArm64Process; public static bool IsNotMacOsAppleSilicon => !IsMacOsAppleSilicon; + public static bool IsAppSandbox => Environment.GetEnvironmentVariable("APP_SANDBOX_CONTAINER_ID") != null; + public static bool IsNotAppSandbox => !IsAppSandbox; // RedHat family covers RedHat and CentOS public static bool IsRedHatFamily => IsRedHatFamilyAndVersion(); @@ -49,7 +51,7 @@ public static partial class PlatformDetection public static bool IsNotFedoraOrRedHatFamily => !IsFedora && !IsRedHatFamily; public static bool IsNotDebian10 => !IsDebian10; - public static bool IsSuperUser => IsBrowser || IsWindows ? false : libc.geteuid() == 0; + public static bool IsSuperUser => IsBrowser || IsWindows ? false : Libc.geteuid() == 0; public static Version OpenSslVersion => !IsOSXLike && !IsWindows && !IsAndroid ? GetOpenSslVersion() : @@ -70,7 +72,7 @@ public static string LibcRelease try { - return Marshal.PtrToStringAnsi(libc.gnu_get_libc_release()); + return Marshal.PtrToStringAnsi(Libc.gnu_get_libc_release()); } catch (Exception e) when (e is DllNotFoundException || e is EntryPointNotFoundException) { @@ -94,7 +96,7 @@ public static string LibcVersion try { - return Marshal.PtrToStringAnsi(libc.gnu_get_libc_version()); + return Marshal.PtrToStringAnsi(Libc.gnu_get_libc_version()); } catch (Exception e) when (e is DllNotFoundException || e is EntryPointNotFoundException) { @@ -317,7 +319,7 @@ private struct DistroInfo public Version VersionId { get; set; } } - private static partial class libc + private static partial class Libc { [GeneratedDllImport("libc", SetLastError = true)] public static unsafe partial uint geteuid(); diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Windows.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Windows.cs index c1a2c8f3710e3a..4bb55c0efd29b8 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Windows.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Windows.cs @@ -235,6 +235,8 @@ public static bool IsInAppContainer } } + public static bool CanRunImpersonatedTests => PlatformDetection.IsNotWindowsNanoServer && PlatformDetection.IsWindowsAndElevated; + private static int s_isWindowsElevated = -1; public static bool IsWindowsAndElevated { diff --git a/src/libraries/Common/tests/TestUtilities/System/WindowsIdentityFixture.cs b/src/libraries/Common/tests/TestUtilities/System/WindowsIdentityFixture.cs index 18df2183f2358f..1adb9b3020b48e 100644 --- a/src/libraries/Common/tests/TestUtilities/System/WindowsIdentityFixture.cs +++ b/src/libraries/Common/tests/TestUtilities/System/WindowsIdentityFixture.cs @@ -10,6 +10,7 @@ using System.Security.Principal; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; +using Xunit; namespace System { @@ -37,6 +38,8 @@ public sealed partial class WindowsTestAccount : IDisposable public WindowsTestAccount(string userName) { + Assert.True(PlatformDetection.IsWindowsAndElevated); + _userName = userName; CreateUser(); } @@ -77,6 +80,10 @@ private void CreateUser() throw new Win32Exception((int)result); } } + else if (result != 0) + { + throw new Win32Exception((int)result); + } const int LOGON32_PROVIDER_DEFAULT = 0; const int LOGON32_LOGON_INTERACTIVE = 2; diff --git a/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj b/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj index ecb7f715559ff9..0a4b6219baed75 100644 --- a/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj +++ b/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj @@ -35,6 +35,9 @@ + + + + + + + + diff --git a/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http2/DynamicTableTest.cs b/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http2/DynamicTableTest.cs index eaa9c6cb8c2e8b..9576a9e67b5f95 100644 --- a/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http2/DynamicTableTest.cs +++ b/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http2/DynamicTableTest.cs @@ -13,8 +13,8 @@ namespace System.Net.Http.Unit.Tests.HPack { public class DynamicTableTest { - private readonly HeaderField _header1 = new HeaderField(Encoding.ASCII.GetBytes("header-1"), Encoding.ASCII.GetBytes("value1")); - private readonly HeaderField _header2 = new HeaderField(Encoding.ASCII.GetBytes("header-02"), Encoding.ASCII.GetBytes("value_2")); + private readonly HeaderField _header1 = new HeaderField(staticTableIndex: null, Encoding.ASCII.GetBytes("header-1"), Encoding.ASCII.GetBytes("value1")); + private readonly HeaderField _header2 = new HeaderField(staticTableIndex: null, Encoding.ASCII.GetBytes("header-02"), Encoding.ASCII.GetBytes("value_2")); [Fact] public void DynamicTable_IsInitiallyEmpty() diff --git a/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http3/QPackDecoderTest.cs b/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http3/QPackDecoderTest.cs index 57810746c77862..d9074fa06b05e2 100644 --- a/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http3/QPackDecoderTest.cs +++ b/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http3/QPackDecoderTest.cs @@ -100,6 +100,39 @@ public void DecodesLiteralFieldLineWithLiteralName_Value() TestDecodeWithoutIndexing(encoded, _translateString, _headerValueString); } + [Fact] + public void DecodesAuthority_Value() + { + byte[] encoded = Convert.FromBase64String("AADR11AOMTI3LjAuMC4xOjUwMDHBNwFhbHQtdXNlZA4xMjcuMC4wLjE6NTAwMQ=="); + + KeyValuePair[] expectedValues = new[] + { + new KeyValuePair(":method", "GET"), + new KeyValuePair(":scheme", "https"), + new KeyValuePair(":authority", "127.0.0.1:5001"), + new KeyValuePair(":path", "/"), + new KeyValuePair("alt-used", "127.0.0.1:5001"), + }; + + TestDecodeWithoutIndexing(encoded[2..], expectedValues); + } + + [Fact] + public void DecodesAuthority_Empty() + { + byte[] encoded = Convert.FromBase64String("AAA3ADptZXRob2QDR0VUNTpwYXRoAS83ADpzY2hlbWUEaHR0cDcDOmF1dGhvcml0eQA="); + + KeyValuePair[] expectedValues = new[] + { + new KeyValuePair(":method", "GET"), + new KeyValuePair(":scheme", "http"), + new KeyValuePair(":authority", ""), + new KeyValuePair(":path", "/"), + }; + + TestDecodeWithoutIndexing(encoded[2..], expectedValues); + } + [Fact] public void DecodesLiteralFieldLineWithLiteralName_HuffmanEncodedValue() { @@ -120,6 +153,26 @@ public void DecodesLiteralFieldLineWithLiteralName_HuffmanEncodedName() TestDecodeWithoutIndexing(encoded, _headerNameString, _headerValueString); } + [Fact] + public void DecodesLiteralFieldLineWithLiteralName_LargeValues() + { + int length = 0; + Span buffer = new byte[1024 * 1024]; + QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReference(":method", new string('A', 8192 / 2), buffer.Slice(length), out int bytesWritten); + length += bytesWritten; + QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReference(":path", new string('A', 8192 / 2), buffer.Slice(length), out bytesWritten); + length += bytesWritten; + QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReference(":scheme", "http", buffer.Slice(length), out bytesWritten); + length += bytesWritten; + + TestDecodeWithoutIndexing(buffer.Slice(0, length).ToArray(), new[] + { + new KeyValuePair(":method", new string('A', 8192 / 2)), + new KeyValuePair(":path", new string('A', 8192 / 2)), + new KeyValuePair(":scheme", "http") + }); + } + public static readonly TheoryData _incompleteHeaderBlockData = new TheoryData { // Incomplete header @@ -146,35 +199,68 @@ public void DecodesIncompleteHeaderBlock_Error(byte[] encoded) private static void TestDecodeWithoutIndexing(byte[] encoded, string expectedHeaderName, string expectedHeaderValue) { - TestDecode(encoded, expectedHeaderName, expectedHeaderValue, expectDynamicTableEntry: false, byteAtATime: false); - TestDecode(encoded, expectedHeaderName, expectedHeaderValue, expectDynamicTableEntry: false, byteAtATime: true); + KeyValuePair[] expectedValues = new[] { new KeyValuePair(expectedHeaderName, expectedHeaderValue) }; + + TestDecodeWithoutIndexing(encoded, expectedValues); } - private static void TestDecode(byte[] encoded, string expectedHeaderName, string expectedHeaderValue, bool expectDynamicTableEntry, bool byteAtATime) + private static void TestDecodeWithoutIndexing(byte[] encoded, KeyValuePair[] expectedValues) { - var decoder = new QPackDecoder(MaxHeaderFieldSize); - var handler = new TestHttpHeadersHandler(); + TestDecode(encoded, expectedValues, expectDynamicTableEntry: false, bytesAtATime: null); + + for (int i = 1; i <= 20; i++) + { + try + { + TestDecode(encoded, expectedValues, expectDynamicTableEntry: false, bytesAtATime: i); + } + catch (Exception ex) + { + throw new Exception($"Error when decoding with chunk size {i}.", ex); + } + } + } + + private static void TestDecode(byte[] encoded, KeyValuePair[] expectedValues, bool expectDynamicTableEntry, int? bytesAtATime) + { + QPackDecoder decoder = new QPackDecoder(MaxHeaderFieldSize); + TestHttpHeadersHandler handler = new TestHttpHeadersHandler(); // Read past header decoder.Decode(new byte[] { 0x00, 0x00 }, endHeaders: false, handler: handler); - if (!byteAtATime) + if (bytesAtATime == null) { decoder.Decode(encoded, endHeaders: true, handler: handler); } else { - // Parse data in 1 byte chunks, separated by empty chunks - for (int i = 0; i < encoded.Length; i++) + int chunkSize = bytesAtATime.Value; + + // Parse data in chunks, separated by empty chunks + for (int i = 0; i < encoded.Length; i += chunkSize) { - bool end = i + 1 == encoded.Length; + int resolvedSize = Math.Min(encoded.Length - i, chunkSize); + bool end = i + resolvedSize == encoded.Length; + + Span chunk = encoded.AsSpan(i, resolvedSize); decoder.Decode(Array.Empty(), endHeaders: false, handler: handler); - decoder.Decode(new byte[] { encoded[i] }, endHeaders: end, handler: handler); + decoder.Decode(chunk, endHeaders: end, handler: handler); } } - Assert.Equal(expectedHeaderValue, handler.DecodedHeaders[expectedHeaderName]); + foreach (KeyValuePair expectedValue in expectedValues) + { + try + { + Assert.Equal(expectedValue.Value, handler.DecodedHeaders[expectedValue.Key]); + } + catch (Exception ex) + { + throw new InvalidOperationException($"Error when checking header '{expectedValue.Key}'.", ex); + } + } } } @@ -185,6 +271,11 @@ public class TestHttpHeadersHandler : IHttpHeadersHandler void IHttpHeadersHandler.OnHeader(ReadOnlySpan name, ReadOnlySpan value) { + if (name.Length == 0) + { + throw new InvalidOperationException("Header with length zero."); + } + string headerName = Encoding.ASCII.GetString(name); string headerValue = Encoding.ASCII.GetString(value); diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index 2412b8d4ed861c..49c6d278ee1d2f 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -74,9 +74,7 @@ Open - MicrosoftAspNetCore - true false false diff --git a/src/libraries/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Microsoft/Diagnostics/Tracing/StubEnvironment.cs b/src/libraries/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Microsoft/Diagnostics/Tracing/StubEnvironment.cs index 0cffd88038be26..3d569d79f5b70a 100644 --- a/src/libraries/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Microsoft/Diagnostics/Tracing/StubEnvironment.cs +++ b/src/libraries/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Microsoft/Diagnostics/Tracing/StubEnvironment.cs @@ -65,10 +65,10 @@ internal static partial class Interop [SuppressUnmanagedCodeSecurityAttribute] internal static partial class Kernel32 { - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - internal static extern int GetCurrentThreadId(); + [GeneratedDllImport(nameof(Kernel32))] + internal static partial int GetCurrentThreadId(); - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - internal static extern uint GetCurrentProcessId(); + [GeneratedDllImport(nameof(Kernel32))] + internal static partial uint GetCurrentProcessId(); } } diff --git a/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/Microsoft.Extensions.Caching.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/Microsoft.Extensions.Caching.Abstractions.csproj index fb4932e84012b4..f2f695e68eff7d 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/Microsoft.Extensions.Caching.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/Microsoft.Extensions.Caching.Abstractions.csproj @@ -5,6 +5,7 @@ true false + true Caching abstractions for in-memory cache and distributed cache. Commonly Used Types: diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/src/Microsoft.Extensions.Caching.Memory.csproj b/src/libraries/Microsoft.Extensions.Caching.Memory/src/Microsoft.Extensions.Caching.Memory.csproj index a75f35e5dc64cc..ab39a8dae5b852 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/src/Microsoft.Extensions.Caching.Memory.csproj +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/src/Microsoft.Extensions.Caching.Memory.csproj @@ -5,6 +5,7 @@ true false + true In-memory cache implementation of Microsoft.Extensions.Caching.Memory.IMemoryCache. diff --git a/src/libraries/Microsoft.Extensions.Configuration.Abstractions/src/Microsoft.Extensions.Configuration.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Configuration.Abstractions/src/Microsoft.Extensions.Configuration.Abstractions.csproj index 03f61f772b639d..f167e3fa276b5a 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Abstractions/src/Microsoft.Extensions.Configuration.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.Abstractions/src/Microsoft.Extensions.Configuration.Abstractions.csproj @@ -3,6 +3,7 @@ $(NetCoreAppCurrent);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) enable + true true false diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj index a04a112aee18a9..e6bbd1039b3dd3 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj @@ -6,6 +6,7 @@ true false + true Functionality to bind an object to data in configuration providers for Microsoft.Extensions.Configuration. diff --git a/src/libraries/Microsoft.Extensions.Configuration.CommandLine/src/Microsoft.Extensions.Configuration.CommandLine.csproj b/src/libraries/Microsoft.Extensions.Configuration.CommandLine/src/Microsoft.Extensions.Configuration.CommandLine.csproj index bf8e1cce4abb2f..a7b12e2099e6f5 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.CommandLine/src/Microsoft.Extensions.Configuration.CommandLine.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.CommandLine/src/Microsoft.Extensions.Configuration.CommandLine.csproj @@ -6,6 +6,7 @@ true false + true Command line configuration provider implementation for Microsoft.Extensions.Configuration. diff --git a/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/src/Microsoft.Extensions.Configuration.EnvironmentVariables.csproj b/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/src/Microsoft.Extensions.Configuration.EnvironmentVariables.csproj index a9563d05f3346c..df60db22e069f5 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/src/Microsoft.Extensions.Configuration.EnvironmentVariables.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.EnvironmentVariables/src/Microsoft.Extensions.Configuration.EnvironmentVariables.csproj @@ -6,6 +6,7 @@ true false + true Environment variables configuration provider implementation for Microsoft.Extensions.Configuration. diff --git a/src/libraries/Microsoft.Extensions.Configuration.FileExtensions/src/Microsoft.Extensions.Configuration.FileExtensions.csproj b/src/libraries/Microsoft.Extensions.Configuration.FileExtensions/src/Microsoft.Extensions.Configuration.FileExtensions.csproj index d2fa2fef4f528d..c256903f01cc2b 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.FileExtensions/src/Microsoft.Extensions.Configuration.FileExtensions.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.FileExtensions/src/Microsoft.Extensions.Configuration.FileExtensions.csproj @@ -5,6 +5,7 @@ true false + true Extension methods for configuring file-based configuration providers for Microsoft.Extensions.Configuration. diff --git a/src/libraries/Microsoft.Extensions.Configuration.Ini/src/Microsoft.Extensions.Configuration.Ini.csproj b/src/libraries/Microsoft.Extensions.Configuration.Ini/src/Microsoft.Extensions.Configuration.Ini.csproj index f300479c5ba496..7b0a14abec59c9 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Ini/src/Microsoft.Extensions.Configuration.Ini.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.Ini/src/Microsoft.Extensions.Configuration.Ini.csproj @@ -5,6 +5,7 @@ true false + true INI configuration provider implementation for Microsoft.Extensions.Configuration. diff --git a/src/libraries/Microsoft.Extensions.Configuration.Json/src/Microsoft.Extensions.Configuration.Json.csproj b/src/libraries/Microsoft.Extensions.Configuration.Json/src/Microsoft.Extensions.Configuration.Json.csproj index f18a470746b1f8..ad2b4ea3c1f827 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Json/src/Microsoft.Extensions.Configuration.Json.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.Json/src/Microsoft.Extensions.Configuration.Json.csproj @@ -6,6 +6,7 @@ true false + true JSON configuration provider implementation for Microsoft.Extensions.Configuration. diff --git a/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/src/Microsoft.Extensions.Configuration.UserSecrets.csproj b/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/src/Microsoft.Extensions.Configuration.UserSecrets.csproj index ee370a166d7b87..99b72902330c8b 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/src/Microsoft.Extensions.Configuration.UserSecrets.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/src/Microsoft.Extensions.Configuration.UserSecrets.csproj @@ -5,6 +5,7 @@ true false + true User secrets configuration provider implementation for Microsoft.Extensions.Configuration. diff --git a/src/libraries/Microsoft.Extensions.Configuration.Xml/src/Microsoft.Extensions.Configuration.Xml.csproj b/src/libraries/Microsoft.Extensions.Configuration.Xml/src/Microsoft.Extensions.Configuration.Xml.csproj index cc974bc18ea12a..b07237dd68d229 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Xml/src/Microsoft.Extensions.Configuration.Xml.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.Xml/src/Microsoft.Extensions.Configuration.Xml.csproj @@ -5,6 +5,7 @@ true false + true XML configuration provider implementation for Microsoft.Extensions.Configuration. diff --git a/src/libraries/Microsoft.Extensions.Configuration/src/Microsoft.Extensions.Configuration.csproj b/src/libraries/Microsoft.Extensions.Configuration/src/Microsoft.Extensions.Configuration.csproj index f114bd7c04c31b..0f2a92e5a9a39d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/src/Microsoft.Extensions.Configuration.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration/src/Microsoft.Extensions.Configuration.csproj @@ -6,6 +6,7 @@ true false + true Implementation of key-value pair based configuration for Microsoft.Extensions.Configuration. Includes the memory configuration provider. diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/Microsoft.Extensions.DependencyInjection.Abstractions.csproj b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/Microsoft.Extensions.DependencyInjection.Abstractions.csproj index f4ad2addae1707..a2485e4cf64551 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/Microsoft.Extensions.DependencyInjection.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/Microsoft.Extensions.DependencyInjection.Abstractions.csproj @@ -4,6 +4,7 @@ $(NetCoreAppCurrent);$(NetCoreAppMinimum);netstandard2.1;netstandard2.0;$(NetFrameworkMinimum) true enable + true Abstractions for dependency injection. Commonly Used Types: diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/Microsoft.Extensions.DependencyInjection.Specification.Tests.csproj b/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/Microsoft.Extensions.DependencyInjection.Specification.Tests.csproj index c8cbe339b43ec1..b53c4a6b663b6c 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/Microsoft.Extensions.DependencyInjection.Specification.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/Microsoft.Extensions.DependencyInjection.Specification.Tests.csproj @@ -7,8 +7,7 @@ false false false - - 3.1.15 + true Suite of xUnit.net tests to check for container compatibility with Microsoft.Extensions.DependencyInjection. diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/Microsoft.Extensions.DependencyInjection.csproj b/src/libraries/Microsoft.Extensions.DependencyInjection/src/Microsoft.Extensions.DependencyInjection.csproj index f1efaee3f0dbfb..38174f12577d0a 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/Microsoft.Extensions.DependencyInjection.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/Microsoft.Extensions.DependencyInjection.csproj @@ -7,6 +7,7 @@ Annotations $(NoWarn);CP0001 + true Default implementation of dependency injection for Microsoft.Extensions.DependencyInjection. false diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/CompatibilitySuppressions.xml b/src/libraries/Microsoft.Extensions.DependencyModel/src/CompatibilitySuppressions.xml deleted file mode 100644 index d088c3cafe5541..00000000000000 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - PKV006 - .NETFramework,Version=v4.5.1 - - - PKV006 - .NETStandard,Version=v1.3 - - - PKV006 - .NETStandard,Version=v1.6 - - \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj b/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj index 027674771e5969..8fa0032fc79c88 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj @@ -5,6 +5,7 @@ true false + true Abstractions for reading `.deps` files. Commonly Used Types: diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/Microsoft.Extensions.FileProviders.Abstractions.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/Microsoft.Extensions.FileProviders.Abstractions.csproj index 3e83ae70c800b7..a0145045e26028 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/Microsoft.Extensions.FileProviders.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/Microsoft.Extensions.FileProviders.Abstractions.csproj @@ -5,6 +5,7 @@ $(NetCoreAppCurrent);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) enable true + true Abstractions of files and directories. Commonly Used Types: diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/Microsoft.Extensions.FileProviders.Composite.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/Microsoft.Extensions.FileProviders.Composite.csproj index 98d26fe1fdb075..b1c24793a0b2d9 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/Microsoft.Extensions.FileProviders.Composite.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/Microsoft.Extensions.FileProviders.Composite.csproj @@ -6,6 +6,7 @@ true false + true Composite file and directory providers for Microsoft.Extensions.FileProviders. diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj index fde292d45cd616..18ccacdf36cd42 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj @@ -6,6 +6,7 @@ enable true true + true File provider for physical files for Microsoft.Extensions.FileProviders. diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Microsoft.Extensions.FileSystemGlobbing.csproj b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Microsoft.Extensions.FileSystemGlobbing.csproj index d6cf9c374979d2..7e4f494e6daa36 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Microsoft.Extensions.FileSystemGlobbing.csproj +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Microsoft.Extensions.FileSystemGlobbing.csproj @@ -6,6 +6,7 @@ true false + true File system globbing to find files matching a specified pattern. diff --git a/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/Microsoft.Extensions.HostFactoryResolver.Sources.csproj b/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/Microsoft.Extensions.HostFactoryResolver.Sources.csproj index 31cd86025ee019..3bcf5ca81fd406 100644 --- a/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/Microsoft.Extensions.HostFactoryResolver.Sources.csproj +++ b/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/Microsoft.Extensions.HostFactoryResolver.Sources.csproj @@ -9,6 +9,7 @@ true true + true Internal package for sharing Microsoft.Extensions.Hosting.HostFactoryResolver type. diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj index c30c91368bb988..aae3d0bc2c4c4d 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj @@ -6,6 +6,7 @@ true false + true Hosting and startup abstractions for applications. diff --git a/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/Microsoft.Extensions.Hosting.Systemd.csproj b/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/Microsoft.Extensions.Hosting.Systemd.csproj index cc158d880d2bf8..5d375e67feab7c 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/Microsoft.Extensions.Hosting.Systemd.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/Microsoft.Extensions.Hosting.Systemd.csproj @@ -6,6 +6,8 @@ .NET hosting infrastructure for Systemd Services. false + true + true @@ -20,4 +22,7 @@ + + + diff --git a/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.cs b/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.cs index 9241a1ba8834c0..244e41c69e179d 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.cs @@ -12,7 +12,7 @@ namespace Microsoft.Extensions.Hosting.Systemd /// /// Helper methods for systemd Services. /// - public static class SystemdHelpers + public static partial class SystemdHelpers { private static bool? _isSystemdService; @@ -46,7 +46,7 @@ private static bool CheckParentIsSystemd() return false; } - [DllImport("libc", EntryPoint = "getppid")] - private static extern int GetParentPid(); + [GeneratedDllImport("libc", EntryPoint = "getppid")] + private static partial int GetParentPid(); } } diff --git a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/Microsoft.Extensions.Hosting.WindowsServices.csproj b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/Microsoft.Extensions.Hosting.WindowsServices.csproj index f58e2771dc5df2..e68a5c2e805338 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/Microsoft.Extensions.Hosting.WindowsServices.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/Microsoft.Extensions.Hosting.WindowsServices.csproj @@ -5,6 +5,7 @@ true false + true .NET hosting infrastructure for Windows Services. true true diff --git a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/WindowsServiceHelpers.cs b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/WindowsServiceHelpers.cs index 30dfb7c0135e38..7569a921263495 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/WindowsServiceHelpers.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/WindowsServiceHelpers.cs @@ -29,7 +29,7 @@ public static bool IsWindowsService() { return false; } - return parent.SessionId == 0 && string.Equals("services", parent.ProcessName, StringComparison.OrdinalIgnoreCase); + return string.Equals("services", parent.ProcessName, StringComparison.OrdinalIgnoreCase); } } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj index a9dcd34765f88b..902c5ffc70f63f 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj @@ -6,6 +6,7 @@ Hosting and startup infrastructures for applications. false + true diff --git a/src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj b/src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj index b9c5dddb945537..ca465451efc098 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj +++ b/src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj @@ -5,6 +5,7 @@ true false + true Annotations The HttpClient factory is a pattern for configuring and retrieving named HttpClients in a composable way. The HttpClient factory provides extensibility to plug in DelegatingHandlers that address cross-cutting concerns such as service location, load balancing, and reliability. The default HttpClient factory provides built-in diagnostics and logging and manages the lifetimes of connections in a performant way. diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj index c1785b80c287c5..8ca2bab854730e 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj @@ -7,6 +7,7 @@ false enable + true Logging abstractions for Microsoft.Extensions.Logging. Commonly Used Types: diff --git a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/Microsoft.Extensions.Logging.Configuration.csproj b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/Microsoft.Extensions.Logging.Configuration.csproj index 348eba95ca6003..fd40fd717a4bb6 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/Microsoft.Extensions.Logging.Configuration.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/Microsoft.Extensions.Logging.Configuration.csproj @@ -5,6 +5,7 @@ true false + true Configuration support for Microsoft.Extensions.Logging. diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj index 932e729c0b9ada..9256474085a479 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj @@ -10,6 +10,7 @@ false true true + true Console logger provider implementation for Microsoft.Extensions.Logging. diff --git a/src/libraries/Microsoft.Extensions.Logging.Debug/src/Microsoft.Extensions.Logging.Debug.csproj b/src/libraries/Microsoft.Extensions.Logging.Debug/src/Microsoft.Extensions.Logging.Debug.csproj index 9e386497759ddc..5448c0f8026862 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Debug/src/Microsoft.Extensions.Logging.Debug.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Debug/src/Microsoft.Extensions.Logging.Debug.csproj @@ -5,6 +5,7 @@ true false + true Debug output logger provider implementation for Microsoft.Extensions.Logging. This logger logs messages to a debugger monitor by writing messages with System.Diagnostics.Debug.WriteLine(). diff --git a/src/libraries/Microsoft.Extensions.Logging.EventLog/src/Microsoft.Extensions.Logging.EventLog.csproj b/src/libraries/Microsoft.Extensions.Logging.EventLog/src/Microsoft.Extensions.Logging.EventLog.csproj index fec4d3c3cc3a90..c6579c84fa30bf 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventLog/src/Microsoft.Extensions.Logging.EventLog.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.EventLog/src/Microsoft.Extensions.Logging.EventLog.csproj @@ -5,6 +5,7 @@ true false + true Windows Event Log logger provider implementation for Microsoft.Extensions.Logging. diff --git a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/Microsoft.Extensions.Logging.EventSource.csproj b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/Microsoft.Extensions.Logging.EventSource.csproj index d01ff9dc851192..aaba3a89e379ad 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/Microsoft.Extensions.Logging.EventSource.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/Microsoft.Extensions.Logging.EventSource.csproj @@ -6,6 +6,7 @@ true false + true EventSource/EventListener logger provider implementation for Microsoft.Extensions.Logging. diff --git a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/Microsoft.Extensions.Logging.TraceSource.csproj b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/Microsoft.Extensions.Logging.TraceSource.csproj index b261effa318a4b..6283620d5458a3 100644 --- a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/Microsoft.Extensions.Logging.TraceSource.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/Microsoft.Extensions.Logging.TraceSource.csproj @@ -5,6 +5,7 @@ true false + true TraceSource logger provider implementation for Microsoft.Extensions.Logging. This logger logs messages to a trace listener by writing messages with System.Diagnostics.TraceSource.TraceEvent(). diff --git a/src/libraries/Microsoft.Extensions.Logging/src/Microsoft.Extensions.Logging.csproj b/src/libraries/Microsoft.Extensions.Logging/src/Microsoft.Extensions.Logging.csproj index 1b8cb73f5845f9..aa51d0b03d995c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/Microsoft.Extensions.Logging.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/src/Microsoft.Extensions.Logging.csproj @@ -5,6 +5,7 @@ true false + true Logging infrastructure default implementation for Microsoft.Extensions.Logging. diff --git a/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/src/Microsoft.Extensions.Options.ConfigurationExtensions.csproj b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/src/Microsoft.Extensions.Options.ConfigurationExtensions.csproj index f63c831073ed90..709e5b90ae84ea 100644 --- a/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/src/Microsoft.Extensions.Options.ConfigurationExtensions.csproj +++ b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/src/Microsoft.Extensions.Options.ConfigurationExtensions.csproj @@ -5,6 +5,7 @@ true false + true Provides additional configuration specific functionality related to Options. diff --git a/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/Microsoft.Extensions.Options.DataAnnotations.csproj b/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/Microsoft.Extensions.Options.DataAnnotations.csproj index 530a0567139bc9..43c6bc5b40e100 100644 --- a/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/Microsoft.Extensions.Options.DataAnnotations.csproj +++ b/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/Microsoft.Extensions.Options.DataAnnotations.csproj @@ -5,6 +5,7 @@ true false + true Provides additional DataAnnotations specific functionality related to Options. diff --git a/src/libraries/Microsoft.Extensions.Options/src/Microsoft.Extensions.Options.csproj b/src/libraries/Microsoft.Extensions.Options/src/Microsoft.Extensions.Options.csproj index 9eea25e29ab1c5..9ed0d4776f0584 100644 --- a/src/libraries/Microsoft.Extensions.Options/src/Microsoft.Extensions.Options.csproj +++ b/src/libraries/Microsoft.Extensions.Options/src/Microsoft.Extensions.Options.csproj @@ -5,6 +5,7 @@ true false + true Provides a strongly typed way of specifying and accessing settings using dependency injection. diff --git a/src/libraries/Microsoft.Extensions.Primitives/src/Microsoft.Extensions.Primitives.csproj b/src/libraries/Microsoft.Extensions.Primitives/src/Microsoft.Extensions.Primitives.csproj index a8f50fa5b880c4..53c7b1e16be1a1 100644 --- a/src/libraries/Microsoft.Extensions.Primitives/src/Microsoft.Extensions.Primitives.csproj +++ b/src/libraries/Microsoft.Extensions.Primitives/src/Microsoft.Extensions.Primitives.csproj @@ -6,6 +6,7 @@ true false + true Primitives shared by framework extensions. Commonly used types include: Commonly Used Types: diff --git a/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs b/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs index f141c084097e32..b247e1e8b6c21c 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs +++ b/src/libraries/Microsoft.VisualBasic.Core/tests/NewLateBindingTests.cs @@ -94,7 +94,7 @@ public static IEnumerable LateCall_OptionalValues_Data() static object[] CreateData(string memberName, object[] arguments, Type[] typeArguments, string expectedValue) => new object[] { memberName, arguments, typeArguments, expectedValue }; } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/51834", typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltWithAggressiveTrimming), nameof(PlatformDetection.IsBrowser))] [MemberData(nameof(LateCall_OptionalValues_Data))] public void LateCall_OptionalValues(string memberName, object[] arguments, Type[] typeArguments, string expectedValue) diff --git a/src/libraries/Microsoft.Win32.Registry.AccessControl/src/CompatibilitySuppressions.xml b/src/libraries/Microsoft.Win32.Registry.AccessControl/src/CompatibilitySuppressions.xml deleted file mode 100644 index cc1f8cf96806e3..00000000000000 --- a/src/libraries/Microsoft.Win32.Registry.AccessControl/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - PKV006 - .NETFramework,Version=v4.6 - - - PKV006 - .NETStandard,Version=v1.3 - - - PKV007 - .NETFramework,Version=v4.6-win - - - PKV007 - .NETStandard,Version=v1.3-win - - \ No newline at end of file diff --git a/src/libraries/System.Collections.Immutable/src/CompatibilitySuppressions.xml b/src/libraries/System.Collections.Immutable/src/CompatibilitySuppressions.xml deleted file mode 100644 index 17167f23750304..00000000000000 --- a/src/libraries/System.Collections.Immutable/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - PKV006 - .NETStandard,Version=v1.0 - - - PKV006 - .NETStandard,Version=v1.3 - - - PKV006 - .NETPortable,Version=v0.0,Profile=Profile259 - - \ No newline at end of file diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/StandardCommands.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/StandardCommands.cs index f224b0449d3468..773d659771debc 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/StandardCommands.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/StandardCommands.cs @@ -662,9 +662,6 @@ private static class VSStandardCommands internal const int cmdidPanePrevTab = 287; internal const int cmdidPaneCloseToolWindow = 288; internal const int cmdidPaneActivateDocWindow = 289; -#if DCR27419 - internal const int cmdidDockingViewMDI = 290; -#endif internal const int cmdidDockingViewFloater = 291; internal const int cmdidAutoHideWindow = 292; internal const int cmdidMoveToDropdownBar = 293; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs index 2e7dc3253411b3..48709a88c24675 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -79,10 +79,8 @@ private static Type[] InitializeSkipInterfaceAttributeList() { return new Type[] { -#if FEATURE_SKIP_INTERFACE typeof(System.Runtime.InteropServices.GuidAttribute), - typeof(System.Runtime.InteropServices.InterfaceTypeAttribute) -#endif + typeof(System.Runtime.InteropServices.InterfaceTypeAttribute), typeof(System.Runtime.InteropServices.ComVisibleAttribute), }; } @@ -649,16 +647,14 @@ protected internal override IExtenderProvider[] GetExtenderProviders(object inst { return GetExtenders(extenderList.GetExtenderProviders(), instance, cache); } -#if FEATURE_COMPONENT_COLLECTION else { - IContainer cont = component.Site.Container; + IContainer? cont = component.Site.Container; if (cont != null) { return GetExtenders(cont.Components, instance, cache); } } -#endif } return Array.Empty(); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/ComponentResourceManagerTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/ComponentResourceManagerTests.cs index fdae3994444284..12b72967c4089f 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/ComponentResourceManagerTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/ComponentResourceManagerTests.cs @@ -167,7 +167,7 @@ private class TestSite : ISite public bool DesignMode { get; set; } public IComponent Component => throw new NotImplementedException(); - public IContainer Container => throw new NotImplementedException(); + public IContainer Container => null; public string Name { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public object GetService(Type serviceType) => null; } diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/ReflectTypeDescriptionProviderTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/ReflectTypeDescriptionProviderTests.cs new file mode 100644 index 00000000000000..b65c506b63c167 --- /dev/null +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/ReflectTypeDescriptionProviderTests.cs @@ -0,0 +1,176 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Linq; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.ComponentModel.Tests +{ + public class ReflectTypeDescriptionProviderTests + { + [Fact] + public void GetAttributes_Skips_ComVisibleAttribute_And_GuidAttribute_And_InterfaceTypeAttribute() + { + AttributeCollection attributeCollection = TypeDescriptor.GetAttributes(typeof(TestClass1)); + Assert.NotEmpty(attributeCollection); + IEnumerable attributes = attributeCollection.Cast(); + Attribute attribute = Assert.Single(attributes); + Assert.IsType(attribute); + Assert.DoesNotContain(attributes, attr => attr.GetType() == typeof(ComVisibleAttribute)); + Assert.DoesNotContain(attributes, attr => attr.GetType() == typeof(GuidAttribute)); + Assert.DoesNotContain(attributes, attr => attr.GetType() == typeof(InterfaceTypeAttribute)); + } + + [Fact] + public void GetExtenderProviders_ReturnResultFromContainerComponents_WhenComponentSiteServiceNull() + { + using ComponentExtendedProvider testComponent = new ComponentExtendedProvider(); + testComponent.Site = new TestSiteWithoutService(); + testComponent.Disposed += (object obj, EventArgs args) => { }; + PropertyDescriptorCollection propertyDescriptorCollection = TypeDescriptor.GetProperties(testComponent); + PropertyDescriptor testPropDescriptor = propertyDescriptorCollection["TestProp"]; + Assert.NotNull(testPropDescriptor); + ExtenderProvidedPropertyAttribute extenderProvidedPropertyAttribute = testPropDescriptor.Attributes[typeof(ExtenderProvidedPropertyAttribute)] as ExtenderProvidedPropertyAttribute; + Assert.NotNull(extenderProvidedPropertyAttribute); + Assert.IsType(extenderProvidedPropertyAttribute.Provider); + } + + [Fact] + public void GetExtenderProviders_ReturnResultFromComponentSiteService_WhenComponentSiteServiceNotNull() + { + using TestComponent testComponent = new TestComponent(); + testComponent.Site = new TestSiteWithService(); + testComponent.Disposed += (object obj, EventArgs args) => { }; + PropertyDescriptorCollection propertyDescriptorCollection = TypeDescriptor.GetProperties(testComponent); + PropertyDescriptor testPropDescriptor = propertyDescriptorCollection["TestProp"]; + Assert.NotNull(testPropDescriptor); + ExtenderProvidedPropertyAttribute extenderProvidedPropertyAttribute = testPropDescriptor.Attributes[typeof(ExtenderProvidedPropertyAttribute)] as ExtenderProvidedPropertyAttribute; + Assert.NotNull(extenderProvidedPropertyAttribute); + Assert.IsType(extenderProvidedPropertyAttribute.Provider); + } + + [ComVisible(true), Guid("4a223ebb-fe95-4649-94e5-2e5cc8f5f4e9"), InterfaceType(1)] + internal interface TestInterface + { + void Action(); + } + + [Description] + internal class TestClass1 : TestInterface + { + public void Action() { } + } + + internal class TestComponent : IComponent + { + public ISite? Site { get; set; } + + public event EventHandler? Disposed; + + public void Dispose() + { + if (Disposed != null) + { + Disposed(this, new EventArgs()); + } + } + } + + [ProvideProperty("TestProp", "System.Object")] + internal class ComponentExtendedProvider : IComponent, IExtenderProvider + { + private int _testProp; + + public ISite? Site { get; set; } + + public event EventHandler? Disposed; + + public bool CanExtend(object extendee) => true; + + public int GetTestProp(object extendee) => _testProp; + + public void SetTestProp(object extendee, int value) + { + _testProp = value; + } + + public void Dispose() + { + if (Disposed != null) + { + Disposed(this, new EventArgs()); + } + } + } + + internal class TestSiteWithoutService : ISite + { + private TestComponent _testComponent = new TestComponent(); + private TestContainer _testContainer = new TestContainer(); + + public IComponent Component => _testComponent; + + public IContainer? Container => _testContainer; + + public bool DesignMode => true; + + public string? Name { get; set; } + + public object? GetService(Type serviceType) => null; + } + + internal class TestSiteWithService : ISite + { + private TestComponent _testComponent = new TestComponent(); + private TestContainer _testContainer = new TestContainer(); + private TestExtenderListService _testExtenderListService = new TestExtenderListService(); + + public IComponent Component => _testComponent; + + public IContainer? Container => _testContainer; + + public bool DesignMode => true; + + public string? Name { get; set; } + + public object? GetService(Type serviceType) => _testExtenderListService; + } + + internal class TestContainer : IContainer + { + private List _components = new List { new ComponentExtendedProvider() }; + + public ComponentCollection Components => new ComponentCollection(_components.ToArray()); + + public void Add(IComponent? component) => _components.Add(component); + public void Add(IComponent? component, string? name) => _components.Add(component); + public void Dispose() => _components.Clear(); + public void Remove(IComponent? component) => _components.Remove(component); + } + + [ProvideProperty("TestProp", "System.Object")] + internal class TestExtenderProvider : IExtenderProvider + { + private int _testProp; + + public bool CanExtend(object extendee) => true; + + public int GetTestProp(object extendee) => _testProp; + + public void SetTestProp(object extendee, int value) + { + _testProp = value; + } + } + + internal class TestExtenderListService : IExtenderListService + { + private TestExtenderProvider _testExtenderProvider = new TestExtenderProvider(); + + public IExtenderProvider[] GetExtenderProviders() => new[] { _testExtenderProvider }; + } + } +} diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj b/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj index 61850c94c0414b..8caeb1d48b8ef2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj @@ -21,6 +21,7 @@ + diff --git a/src/libraries/System.Composition.AttributedModel/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.AttributedModel/src/CompatibilitySuppressions.xml deleted file mode 100644 index e80b49dae2deb8..00000000000000 --- a/src/libraries/System.Composition.AttributedModel/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - PKV006 - .NETStandard,Version=v1.0 - - - PKV006 - .NETPortable,Version=v0.0,Profile=Profile259 - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Convention/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.Convention/src/CompatibilitySuppressions.xml deleted file mode 100644 index e80b49dae2deb8..00000000000000 --- a/src/libraries/System.Composition.Convention/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - PKV006 - .NETStandard,Version=v1.0 - - - PKV006 - .NETPortable,Version=v0.0,Profile=Profile259 - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Hosting/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.Hosting/src/CompatibilitySuppressions.xml deleted file mode 100644 index e80b49dae2deb8..00000000000000 --- a/src/libraries/System.Composition.Hosting/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - PKV006 - .NETStandard,Version=v1.0 - - - PKV006 - .NETPortable,Version=v0.0,Profile=Profile259 - - \ No newline at end of file diff --git a/src/libraries/System.Composition.Runtime/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.Runtime/src/CompatibilitySuppressions.xml deleted file mode 100644 index e80b49dae2deb8..00000000000000 --- a/src/libraries/System.Composition.Runtime/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - PKV006 - .NETStandard,Version=v1.0 - - - PKV006 - .NETPortable,Version=v0.0,Profile=Profile259 - - \ No newline at end of file diff --git a/src/libraries/System.Composition.TypedParts/src/CompatibilitySuppressions.xml b/src/libraries/System.Composition.TypedParts/src/CompatibilitySuppressions.xml deleted file mode 100644 index e80b49dae2deb8..00000000000000 --- a/src/libraries/System.Composition.TypedParts/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - PKV006 - .NETStandard,Version=v1.0 - - - PKV006 - .NETPortable,Version=v0.0,Profile=Profile259 - - \ No newline at end of file diff --git a/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs index 78ac56e9f751a2..9869027d730dfb 100644 --- a/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs +++ b/src/libraries/System.Data.Common/tests/TrimmingTests/DbConnectionStringBuilder.cs @@ -136,7 +136,7 @@ class TestSite : INestedSite { public string FullName => null; public IComponent Component => throw new NotImplementedException(); - public IContainer Container => throw new NotImplementedException(); + public IContainer Container => null; public bool DesignMode => throw new NotImplementedException(); public string Name { get => "Test Component Name"; set => throw new NotImplementedException(); } public object GetService(Type serviceType) => null; diff --git a/src/libraries/System.Data.Odbc/src/CompatibilitySuppressions.xml b/src/libraries/System.Data.Odbc/src/CompatibilitySuppressions.xml index 5215a2294e4e87..5971367836429e 100644 --- a/src/libraries/System.Data.Odbc/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Data.Odbc/src/CompatibilitySuppressions.xml @@ -147,84 +147,203 @@ CP0001 T:System.Data.Odbc.ODBC32 - runtimes/freebsd/lib/netcoreapp2.0/System.Data.Odbc.dll + lib/netcoreapp3.1/System.Data.Odbc.dll lib/netstandard2.0/System.Data.Odbc.dll true CP0002 M:System.Data.Odbc.OdbcParameter.get_Offset - runtimes/freebsd/lib/netcoreapp2.0/System.Data.Odbc.dll + runtimes/freebsd/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.set_Offset(System.Int32) + runtimes/freebsd/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.get_Offset + runtimes/illumos/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.set_Offset(System.Int32) + runtimes/illumos/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.get_Offset + runtimes/ios/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.set_Offset(System.Int32) + runtimes/ios/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.get_Offset + runtimes/linux/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.set_Offset(System.Int32) + runtimes/linux/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.get_Offset + runtimes/osx/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.set_Offset(System.Int32) + runtimes/osx/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.get_Offset + runtimes/solaris/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.set_Offset(System.Int32) + runtimes/solaris/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.get_Offset + runtimes/tvos/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.set_Offset(System.Int32) + runtimes/tvos/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.get_Offset + runtimes/win/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.set_Offset(System.Int32) + runtimes/win/lib/net6.0/System.Data.Odbc.dll + lib/net6.0/System.Data.Odbc.dll + true + + + CP0001 + T:System.Data.Odbc.ODBC32 + runtimes/freebsd/lib/netcoreapp3.1/System.Data.Odbc.dll + lib/netstandard2.0/System.Data.Odbc.dll + true + + + CP0002 + M:System.Data.Odbc.OdbcParameter.get_Offset + runtimes/freebsd/lib/netcoreapp3.1/System.Data.Odbc.dll lib/netstandard2.0/System.Data.Odbc.dll true CP0002 M:System.Data.Odbc.OdbcParameter.set_Offset(System.Int32) - runtimes/freebsd/lib/netcoreapp2.0/System.Data.Odbc.dll + runtimes/freebsd/lib/netcoreapp3.1/System.Data.Odbc.dll lib/netstandard2.0/System.Data.Odbc.dll true CP0001 T:System.Data.Odbc.ODBC32 - runtimes/linux/lib/netcoreapp2.0/System.Data.Odbc.dll + runtimes/linux/lib/netcoreapp3.1/System.Data.Odbc.dll lib/netstandard2.0/System.Data.Odbc.dll true CP0002 M:System.Data.Odbc.OdbcParameter.get_Offset - runtimes/linux/lib/netcoreapp2.0/System.Data.Odbc.dll + runtimes/linux/lib/netcoreapp3.1/System.Data.Odbc.dll lib/netstandard2.0/System.Data.Odbc.dll true CP0002 M:System.Data.Odbc.OdbcParameter.set_Offset(System.Int32) - runtimes/linux/lib/netcoreapp2.0/System.Data.Odbc.dll + runtimes/linux/lib/netcoreapp3.1/System.Data.Odbc.dll lib/netstandard2.0/System.Data.Odbc.dll true CP0001 T:System.Data.Odbc.ODBC32 - runtimes/osx/lib/netcoreapp2.0/System.Data.Odbc.dll + runtimes/osx/lib/netcoreapp3.1/System.Data.Odbc.dll lib/netstandard2.0/System.Data.Odbc.dll true CP0002 M:System.Data.Odbc.OdbcParameter.get_Offset - runtimes/osx/lib/netcoreapp2.0/System.Data.Odbc.dll + runtimes/osx/lib/netcoreapp3.1/System.Data.Odbc.dll lib/netstandard2.0/System.Data.Odbc.dll true CP0002 M:System.Data.Odbc.OdbcParameter.set_Offset(System.Int32) - runtimes/osx/lib/netcoreapp2.0/System.Data.Odbc.dll + runtimes/osx/lib/netcoreapp3.1/System.Data.Odbc.dll lib/netstandard2.0/System.Data.Odbc.dll true CP0001 T:System.Data.Odbc.ODBC32 - runtimes/win/lib/netcoreapp2.0/System.Data.Odbc.dll + runtimes/win/lib/netcoreapp3.1/System.Data.Odbc.dll lib/netstandard2.0/System.Data.Odbc.dll true CP0002 M:System.Data.Odbc.OdbcParameter.get_Offset - runtimes/win/lib/netcoreapp2.0/System.Data.Odbc.dll + runtimes/win/lib/netcoreapp3.1/System.Data.Odbc.dll lib/netstandard2.0/System.Data.Odbc.dll true CP0002 M:System.Data.Odbc.OdbcParameter.set_Offset(System.Int32) - runtimes/win/lib/netcoreapp2.0/System.Data.Odbc.dll + runtimes/win/lib/netcoreapp3.1/System.Data.Odbc.dll lib/netstandard2.0/System.Data.Odbc.dll true diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/CompatibilitySuppressions.xml b/src/libraries/System.Diagnostics.DiagnosticSource/src/CompatibilitySuppressions.xml deleted file mode 100644 index fd952328f044bf..00000000000000 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - PKV006 - .NETFramework,Version=v4.5 - - - PKV006 - .NETFramework,Version=v4.6 - - - PKV006 - .NETStandard,Version=v1.1 - - - PKV006 - .NETStandard,Version=v1.3 - - - PKV006 - .NETPortable,Version=v0.0,Profile=Profile111 - - \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/FileVersionInfoTest.Unix.cs b/src/libraries/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/FileVersionInfoTest.Unix.cs index ef0d008871e987..861373456cae90 100644 --- a/src/libraries/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/FileVersionInfoTest.Unix.cs +++ b/src/libraries/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/FileVersionInfoTest.Unix.cs @@ -23,7 +23,7 @@ public void NonRegularFile_Throws() public void Symlink_ValidFile_Succeeds() { string filePath = Path.Combine(Directory.GetCurrentDirectory(), TestAssemblyFileName); - string linkPath = GetTestFilePath(); + string linkPath = GetRandomLinkPath(); Assert.Equal(0, symlink(filePath, linkPath)); @@ -68,7 +68,7 @@ public void Symlink_InvalidFile_Throws() string sourcePath = Path.Combine(Directory.GetCurrentDirectory(), TestAssemblyFileName); string filePath = GetTestFilePath(); File.Copy(sourcePath, filePath); - string linkPath = GetTestFilePath(); + string linkPath = GetRandomLinkPath(); Assert.Equal(0, symlink(filePath, linkPath)); File.Delete(filePath); Assert.Throws(() => FileVersionInfo.GetVersionInfo(linkPath)); diff --git a/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs b/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs index 53a49f897c1a2e..7e1f6d5d77adec 100644 --- a/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs +++ b/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs @@ -43,8 +43,8 @@ public Process() { } public System.Diagnostics.ProcessModule? MainModule { get { throw null; } } public System.IntPtr MainWindowHandle { get { throw null; } } public string MainWindowTitle { get { throw null; } } - public System.IntPtr MaxWorkingSet { [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios"), System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("freebsd"), System.Runtime.Versioning.SupportedOSPlatformAttribute("macos"), System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } } - public System.IntPtr MinWorkingSet { [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios"), System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("freebsd"), System.Runtime.Versioning.SupportedOSPlatformAttribute("macos"), System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } } + public System.IntPtr MaxWorkingSet { [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios"), System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos"), System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("freebsd"), System.Runtime.Versioning.SupportedOSPlatformAttribute("macos"), System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst"), System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } } + public System.IntPtr MinWorkingSet { [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios"), System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos"), System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("freebsd"), System.Runtime.Versioning.SupportedOSPlatformAttribute("macos"), System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst"), System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } } public System.Diagnostics.ProcessModuleCollection Modules { get { throw null; } } [System.ObsoleteAttribute("Process.NonpagedSystemMemorySize has been deprecated because the type of the property can't represent all valid results. Use System.Diagnostics.Process.NonpagedSystemMemorySize64 instead.")] public int NonpagedSystemMemorySize { get { throw null; } } @@ -86,9 +86,11 @@ public Process() { } public System.Diagnostics.ProcessThreadCollection Threads { get { throw null; } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public System.TimeSpan TotalProcessorTime { get { throw null; } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public System.TimeSpan UserProcessorTime { get { throw null; } } [System.ObsoleteAttribute("Process.VirtualMemorySize has been deprecated because the type of the property can't represent all valid results. Use System.Diagnostics.Process.VirtualMemorySize64 instead.")] public int VirtualMemorySize { get { throw null; } } @@ -112,39 +114,50 @@ public static void EnterDebugMode() { } public static System.Diagnostics.Process GetProcessById(int processId, string machineName) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public static System.Diagnostics.Process[] GetProcesses() { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public static System.Diagnostics.Process[] GetProcesses(string machineName) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public static System.Diagnostics.Process[] GetProcessesByName(string? processName) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public static System.Diagnostics.Process[] GetProcessesByName(string? processName, string machineName) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public void Kill() { } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public void Kill(bool entireProcessTree) { } public static void LeaveDebugMode() { } protected void OnExited() { } public void Refresh() { } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public bool Start() { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public static System.Diagnostics.Process? Start(System.Diagnostics.ProcessStartInfo startInfo) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public static System.Diagnostics.Process Start(string fileName) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public static System.Diagnostics.Process Start(string fileName, string arguments) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public static System.Diagnostics.Process Start(string fileName, System.Collections.Generic.IEnumerable arguments) { throw null; } [System.CLSCompliantAttribute(false)] [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] @@ -247,6 +260,7 @@ public int IdealProcessor { set { } } public System.Diagnostics.ThreadPriorityLevel PriorityLevel { [System.Runtime.Versioning.SupportedOSPlatform("windows")] [System.Runtime.Versioning.SupportedOSPlatform("linux")] [System.Runtime.Versioning.SupportedOSPlatform("freebsd")] get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public System.TimeSpan PrivilegedProcessorTime { get { throw null; } } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public System.IntPtr ProcessorAffinity { set { } } @@ -257,9 +271,11 @@ public System.IntPtr ProcessorAffinity { set { } } public System.Diagnostics.ThreadState ThreadState { get { throw null; } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public System.TimeSpan TotalProcessorTime { get { throw null; } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] + [System.Runtime.Versioning.SupportedOSPlatformAttribute("maccatalyst")] public System.TimeSpan UserProcessorTime { get { throw null; } } public System.Diagnostics.ThreadWaitReason WaitReason { get { throw null; } } public void ResetIdealProcessor() { } diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index 41548252d1f5e5..2279a7ca2325ed 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -2,7 +2,7 @@ $(DefineConstants);FEATURE_REGISTRY true - $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-FreeBSD;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent);$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-FreeBSD;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent);$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-MacCatalyst;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS enable @@ -159,6 +159,8 @@ Link="Common\Interop\Windows\Advapi32\Interop.AdjustTokenPrivileges.cs" /> + - + diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.BSD.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.BSD.cs index 3380481ca5c8e3..70c408cea290bb 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.BSD.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.BSD.cs @@ -15,6 +15,7 @@ public partial class Process /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static Process[] GetProcessesByName(string? processName, string machineName) { if (processName == null) diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs index 016327ad61ab57..e4b149b84a6d23 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs @@ -20,6 +20,7 @@ public partial class Process : IDisposable /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static Process[] GetProcessesByName(string? processName, string machineName) { ProcessManager.ThrowIfRemoteMachine(machineName); diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.NonUap.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.NonUap.cs index 0dff87802214e9..2bad9c22c9b6d9 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.NonUap.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.NonUap.cs @@ -11,6 +11,7 @@ public partial class Process : IDisposable { [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public void Kill(bool entireProcessTree) { if (!entireProcessTree) diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs index f869eb7aa4b2c0..d7644199391746 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs @@ -55,9 +55,10 @@ public static Process Start(string fileName, string arguments, string userName, /// Terminates the associated process immediately. [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public void Kill() { - if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS()) + if (PlatformDoesNotSupportProcessStartAndKill) { throw new PlatformNotSupportedException(); } @@ -369,7 +370,7 @@ private SafeProcessHandle GetProcessHandle() /// The start info with which to start the process. private bool StartCore(ProcessStartInfo startInfo) { - if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS()) + if (PlatformDoesNotSupportProcessStartAndKill) { throw new PlatformNotSupportedException(); } @@ -1113,5 +1114,18 @@ private static int OnSigChild(int reapAll, int configureConsole) s_processStartLock.ExitWriteLock(); } } + + /// Gets the friendly name of the process. + public string ProcessName + { + get + { + EnsureState(State.HaveProcessInfo); + return _processInfo!.ProcessName; + } + } + + private static bool PlatformDoesNotSupportProcessStartAndKill + => (OperatingSystem.IsIOS() && !OperatingSystem.IsMacCatalyst()) || OperatingSystem.IsTvOS(); } } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.UnknownUnix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.UnknownUnix.cs index 897fde77e9a75d..384ef4497db612 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.UnknownUnix.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.UnknownUnix.cs @@ -9,6 +9,9 @@ public partial class Process : IDisposable /// Creates an array of components that are associated with process resources on a /// remote computer. These process resources share the specified process name. /// + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static Process[] GetProcessesByName(string? processName, string machineName) { throw new PlatformNotSupportedException(); @@ -83,5 +86,15 @@ private string GetPathToOpenFile() } private int ParentProcessId => throw new PlatformNotSupportedException(); + + /// Gets the friendly name of the process. + public string ProcessName + { + get + { + EnsureState(State.HaveProcessInfo); + return _processInfo!.ProcessName; + } + } } } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs index ad7209a3f198d2..05046528fa6f59 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs @@ -17,12 +17,15 @@ public partial class Process : IDisposable { private static readonly object s_createProcessLock = new object(); + private string? _processName; + /// /// Creates an array of components that are associated with process resources on a /// remote computer. These process resources share the specified process name. /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static Process[] GetProcessesByName(string? processName, string machineName) { if (processName == null) @@ -93,6 +96,7 @@ public static void LeaveDebugMode() /// Terminates the associated process immediately. [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public void Kill() { using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_TERMINATE | Interop.Advapi32.ProcessOptions.PROCESS_QUERY_LIMITED_INFORMATION, throwIfExited: false)) @@ -125,6 +129,7 @@ private void RefreshCore() _haveMainWindow = false; _mainWindowTitle = null; _haveResponding = false; + _processName = null; } /// Additional logic invoked when the Process is closed. @@ -887,5 +892,35 @@ private static string GetEnvironmentVariablesBlock(IDictionary s } private static string GetErrorMessage(int error) => Interop.Kernel32.GetMessage(error); + + /// Gets the friendly name of the process. + public string ProcessName + { + get + { + if (_processName == null) + { + EnsureState(State.HaveNonExitedId); + // If we already have the name via a populated ProcessInfo + // then use that one. + if (_processInfo?.ProcessName != null) + { + _processName = _processInfo!.ProcessName; + } + else + { + // If we don't have a populated ProcessInfo, then get and cache the process name. + _processName = ProcessManager.GetProcessName(_processId, _machineName); + + if (_processName == null) + { + throw new InvalidOperationException(SR.NoProcessInfo); + } + } + } + + return _processName; + } + } } } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs index dab24936576a4b..e75dd7961866f8 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs @@ -195,6 +195,7 @@ public bool HasExited /// Gets the time the associated process was started. [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public DateTime StartTime { get @@ -264,14 +265,16 @@ public IntPtr MaxWorkingSet { [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] get { EnsureWorkingSetLimits(); return _maxWorkingSet; } - [SupportedOSPlatform("windows")] - [SupportedOSPlatform("macos")] [SupportedOSPlatform("freebsd")] + [SupportedOSPlatform("macos")] + [SupportedOSPlatform("maccatalyst")] + [SupportedOSPlatform("windows")] set { SetWorkingSetLimits(null, value); @@ -286,14 +289,16 @@ public IntPtr MinWorkingSet { [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] get { EnsureWorkingSetLimits(); return _minWorkingSet; } - [SupportedOSPlatform("windows")] - [SupportedOSPlatform("macos")] [SupportedOSPlatform("freebsd")] + [SupportedOSPlatform("macos")] + [SupportedOSPlatform("maccatalyst")] + [SupportedOSPlatform("windows")] set { SetWorkingSetLimits(value, null); @@ -505,21 +510,6 @@ public int PrivateMemorySize } } - /// - /// - /// Gets - /// the friendly name of the process. - /// - /// - public string ProcessName - { - get - { - EnsureState(State.HaveProcessInfo); - return _processInfo!.ProcessName; - } - } - /// /// /// Gets @@ -1043,6 +1033,7 @@ public static Process GetProcessById(int processId) /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static Process[] GetProcessesByName(string? processName) { return GetProcessesByName(processName, "."); @@ -1056,6 +1047,7 @@ public static Process[] GetProcessesByName(string? processName) /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static Process[] GetProcesses() { return GetProcesses("."); @@ -1070,6 +1062,7 @@ public static Process[] GetProcesses() /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static Process[] GetProcesses(string machineName) { bool isRemoteMachine = ProcessManager.IsRemoteMachine(machineName); @@ -1213,6 +1206,7 @@ private void SetProcessId(int processId) /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public bool Start() { Close(); @@ -1267,6 +1261,7 @@ public bool Start() /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static Process Start(string fileName) { // the underlying Start method can only return null on Windows platforms, @@ -1285,6 +1280,7 @@ public static Process Start(string fileName) /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static Process Start(string fileName, string arguments) { // the underlying Start method can only return null on Windows platforms, @@ -1298,6 +1294,7 @@ public static Process Start(string fileName, string arguments) /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static Process Start(string fileName, IEnumerable arguments) { if (fileName == null) @@ -1324,6 +1321,7 @@ public static Process Start(string fileName, IEnumerable arguments) /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static Process? Start(ProcessStartInfo startInfo) { Process process = new Process(); diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.iOS.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.iOS.cs index e3fa89f34ccd1c..abbb712d4006ac 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.iOS.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.iOS.cs @@ -11,6 +11,7 @@ public partial class Process : IDisposable { [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public void Kill(bool entireProcessTree) { throw new PlatformNotSupportedException(); @@ -22,6 +23,7 @@ public void Kill(bool entireProcessTree) /// [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static Process[] GetProcessesByName(string? processName, string machineName) { throw new PlatformNotSupportedException(); diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Unix.cs index d9e27a8c542769..1ef579803c622f 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Unix.cs @@ -47,6 +47,7 @@ public static bool IsProcessRunning(int processId) /// An array of process IDs from the specified machine. [UnsupportedOSPlatform("ios")] [UnsupportedOSPlatform("tvos")] + [SupportedOSPlatform("maccatalyst")] public static int[] GetProcessIds(string machineName) { ThrowIfRemoteMachine(machineName); diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs index 103443d7f23251..ad791263cbf078 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs @@ -85,6 +85,39 @@ public static ProcessInfo[] GetProcessInfos(string machineName) return null; } + /// Gets the process name for the specified process ID on the specified machine. + /// The process ID. + /// The machine name. + /// The process name for the process if it could be found; otherwise, null. + public static string? GetProcessName(int processId, string machineName) + { + if (IsRemoteMachine(machineName)) + { + // remote case: we take the hit of looping through all results + ProcessInfo[] processInfos = NtProcessManager.GetProcessInfos(machineName, isRemoteMachine: true); + foreach (ProcessInfo processInfo in processInfos) + { + if (processInfo.ProcessId == processId) + { + return processInfo.ProcessName; + } + } + } + else + { + // local case: do not use performance counter and also attempt to get the matching (by pid) process only + + string? processName = Interop.Kernel32.GetProcessName((uint)processId); + if (processName is not null) + { + return NtProcessInfoHelper.GetProcessShortName(processName); + } + } + + return null; + } + + /// Gets the IDs of all processes on the specified machine. /// The machine to examine. /// An array of process IDs from the specified machine. diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs index 38062773cb3671..6f304b08989f55 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs @@ -1318,7 +1318,7 @@ private void VerifyNotepadMainWindowTitle(Process process, string filename) string expected = Path.GetFileNameWithoutExtension(filename); process.WaitForInputIdle(); // Give the file a chance to load - Assert.Equal("notepad", process.ProcessName); + Assert.Equal("notepad", process.ProcessName.ToLower()); // Notepad calls CreateWindowEx with pWindowName of empty string, then calls SetWindowTextW // with "Untitled - Notepad" then finally if you're opening a file, calls SetWindowTextW diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs index 63d7f3d6d02b3c..e27e143f75b642 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.Unix.cs @@ -78,6 +78,7 @@ public void ProcessStart_UseShellExecute_OnLinux_ThrowsIfNoProgramInstalled() [Fact] [OuterLoop("Opens program")] + [SkipOnPlatform(TestPlatforms.MacCatalyst, "In App Sandbox mode, the process doesn't have read access to the binary.")] public void ProcessStart_DirectoryNameInCurDirectorySameAsFileNameInExecDirectory_Success() { string fileToOpen = "dotnet"; @@ -157,8 +158,8 @@ public void ProcessStart_UseShellExecute_OnUnix_SuccessWhenProgramInstalled(bool } [Fact] - [SkipOnPlatform(TestPlatforms.OSX, "On OSX, ProcessName returns the script interpreter.")] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on iOS, MacCatalyst, or tvOS.")] + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.MacCatalyst, "On OSX, ProcessName returns the script interpreter.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS or tvOS.")] public void ProcessNameMatchesScriptName() { string scriptName = GetTestFileName(); @@ -379,7 +380,7 @@ public void ProcessStart_OnLinux_UsesSpecifiedProgramUsingArgumentList() } [Theory, InlineData("/usr/bin/open"), InlineData("/usr/bin/nano")] - [PlatformSpecific(TestPlatforms.OSX)] + [PlatformSpecific(TestPlatforms.OSX | TestPlatforms.MacCatalyst)] [OuterLoop("Opens program")] public void ProcessStart_OpenFileOnOsx_UsesSpecifiedProgram(string programToOpenWith) { @@ -396,7 +397,7 @@ public void ProcessStart_OpenFileOnOsx_UsesSpecifiedProgram(string programToOpen } [Theory, InlineData("Safari"), InlineData("\"Google Chrome\"")] - [PlatformSpecific(TestPlatforms.OSX)] + [PlatformSpecific(TestPlatforms.OSX | TestPlatforms.MacCatalyst)] [OuterLoop("Opens browser")] public void ProcessStart_OpenUrl_UsesSpecifiedApplication(string applicationToOpenWith) { @@ -410,7 +411,7 @@ public void ProcessStart_OpenUrl_UsesSpecifiedApplication(string applicationToOp } [Theory, InlineData("-a Safari"), InlineData("-a \"Google Chrome\"")] - [PlatformSpecific(TestPlatforms.OSX)] + [PlatformSpecific(TestPlatforms.OSX | TestPlatforms.MacCatalyst)] [OuterLoop("Opens browser")] public void ProcessStart_UseShellExecuteTrue_OpenUrl_SuccessfullyReadsArgument(string arguments) { @@ -432,7 +433,7 @@ public void ProcessStart_UseShellExecuteTrue_OpenUrl_SuccessfullyReadsArgument(s [Theory, MemberData(nameof(StartOSXProcessWithArgumentList))] - [PlatformSpecific(TestPlatforms.OSX)] + [PlatformSpecific(TestPlatforms.OSX | TestPlatforms.MacCatalyst)] [OuterLoop("Opens browser")] public void ProcessStart_UseShellExecuteTrue_OpenUrl_SuccessfullyReadsArgumentArray(string[] argumentList) { @@ -502,7 +503,7 @@ public void TestBasePriorityOnUnix() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on iOS, MacCatalyst, or tvOS.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS or tvOS.")] public void TestStartOnUnixWithBadPermissions() { string path = GetTestFilePath(); @@ -514,7 +515,7 @@ public void TestStartOnUnixWithBadPermissions() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on iOS, MacCatalyst, or tvOS.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS or tvOS.")] public void TestStartOnUnixWithBadFormat() { string path = GetTestFilePath(); diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs index b3086da60d7572..18406af45353f4 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs @@ -171,28 +171,28 @@ public void TestExited_SynchronizingObject(bool invokeRequired) } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Not supported on iOS, tvOS, or MacCatalyst.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS and tvOS.")] public void ProcessStart_TryExitCommandAsFileName_ThrowsWin32Exception() { Assert.Throws(() => Process.Start(new ProcessStartInfo { UseShellExecute = false, FileName = "exit", Arguments = "42" })); } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Not supported on iOS, tvOS, or MacCatalyst.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS and tvOS.")] public void ProcessStart_UseShellExecuteFalse_FilenameIsUrl_ThrowsWin32Exception() { Assert.Throws(() => Process.Start(new ProcessStartInfo { UseShellExecute = false, FileName = "https://www.github.com/corefx" })); } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Not supported on iOS, tvOS, or MacCatalyst.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS and tvOS.")] public void ProcessStart_TryOpenFolder_UseShellExecuteIsFalse_ThrowsWin32Exception() { Assert.Throws(() => Process.Start(new ProcessStartInfo { UseShellExecute = false, FileName = Path.GetTempPath() })); } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Not supported on iOS, tvOS, or MacCatalyst.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS and tvOS.")] public void TestStartWithBadWorkingDirectory() { string program; @@ -230,7 +230,7 @@ public void TestStartWithBadWorkingDirectory() [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.HasWindowsShell))] [OuterLoop("Launches File Explorer")] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Not supported on iOS, tvOS, or MacCatalyst.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS and tvOS.")] public void ProcessStart_UseShellExecute_OnWindows_OpenMissingFile_Throws() { string fileToOpen = Path.Combine(Environment.CurrentDirectory, "_no_such_file.TXT"); @@ -243,7 +243,7 @@ public void ProcessStart_UseShellExecute_OnWindows_OpenMissingFile_Throws() [InlineData(true)] [InlineData(false)] [OuterLoop("Launches File Explorer")] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Not supported on iOS, tvOS, or MacCatalyst.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS and tvOS.")] public void ProcessStart_UseShellExecute_OnWindows_DoesNotThrow(bool isFolder) { string fileToOpen; @@ -267,21 +267,27 @@ public void ProcessStart_UseShellExecute_OnWindows_DoesNotThrow(bool isFolder) { if (px != null) // sometimes process is null { - Assert.Equal("notepad", px.ProcessName); - - px.Kill(); - Assert.True(px.WaitForExit(WaitInMS)); - px.WaitForExit(); // wait for event handlers to complete + try + { + Assert.Equal("notepad", px.ProcessName.ToLower()); + } + finally + { + px.Kill(); + Assert.True(px.WaitForExit(WaitInMS)); + px.WaitForExit(); // wait for event handlers to complete + } } } } } [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsServerCore), - nameof(PlatformDetection.IsNotWindowsNanoServer), nameof(PlatformDetection.IsNotWindowsIoTCore))] + nameof(PlatformDetection.IsNotWindowsNanoServer), nameof(PlatformDetection.IsNotWindowsIoTCore), + nameof(PlatformDetection.IsNotAppSandbox))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34685", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [InlineData(true), InlineData(false)] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on iOS, MacCatalyst, or tvOS.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS and tvOS.")] public void ProcessStart_UseShellExecute_Executes(bool filenameAsUrl) { string filename = WriteScriptFile(TestDirectory, GetTestFileName(), returnValue: 42); @@ -304,7 +310,9 @@ public void ProcessStart_UseShellExecute_Executes(bool filenameAsUrl) PlatformDetection.IsNotWindowsIoTCore && RemoteExecutor.IsSupported; - [ConditionalFact(nameof(UseShellExecuteExecuteOrderIsRunnablePlatform))] + private static bool IsNotAppSandbox => PlatformDetection.IsNotAppSandbox; + + [ConditionalFact(nameof(UseShellExecuteExecuteOrderIsRunnablePlatform), nameof(IsNotAppSandbox))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34685", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void ProcessStart_UseShellExecute_ExecuteOrder() { @@ -348,9 +356,10 @@ public void ProcessStart_UseShellExecute_ExecuteOrder() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsServerCore), - nameof(PlatformDetection.IsNotWindowsNanoServer), nameof(PlatformDetection.IsNotWindowsIoTCore))] + nameof(PlatformDetection.IsNotWindowsNanoServer), nameof(PlatformDetection.IsNotWindowsIoTCore), + nameof(PlatformDetection.IsNotAppSandbox))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34685", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on iOS, MacCatalyst, or tvOS.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS and tvOS.")] public void ProcessStart_UseShellExecute_WorkingDirectory() { // Create a directory that will ProcessStartInfo.WorkingDirectory @@ -502,7 +511,7 @@ public void HasExited_GetNotStarted_ThrowsInvalidOperationException() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Not supported on iOS, tvOS, or MacCatalyst.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS and tvOS.")] public void Kill_NotStarted_ThrowsInvalidOperationException() { var process = new Process(); @@ -1419,7 +1428,7 @@ public void CanBeFinalized() [Theory] [InlineData(false)] [InlineData(true)] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on iOS, MacCatalyst, or tvOS.")] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS and tvOS.")] public void TestStartWithMissingFile(bool fullPath) { string path = Guid.NewGuid().ToString("N"); diff --git a/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/Trace.cs b/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/Trace.cs index 31af00ab0accc5..c4fd3b72d2eb7f 100644 --- a/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/Trace.cs +++ b/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/Trace.cs @@ -4,6 +4,7 @@ #define TRACE using System; using System.Collections; +using System.Threading; namespace System.Diagnostics { @@ -19,18 +20,11 @@ private Trace() } private static CorrelationManager? s_correlationManager; - public static CorrelationManager CorrelationManager - { - get - { - if (s_correlationManager == null) - { - s_correlationManager = new CorrelationManager(); - } - return s_correlationManager; - } - } + public static CorrelationManager CorrelationManager => + Volatile.Read(ref s_correlationManager) ?? + Interlocked.CompareExchange(ref s_correlationManager, new CorrelationManager(), null) ?? + s_correlationManager; /// /// Gets the collection of listeners that is monitoring the trace output. diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj b/src/libraries/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj index 102c67d81410e3..915b7d71b0a9df 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj +++ b/src/libraries/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj @@ -1,4 +1,4 @@ - + true true @@ -8,6 +8,8 @@ true true Provides the methods defined in the Lightweight Directory Access Protocol (LDAP) version 3 (V3) and Directory Services Markup Language (DSML) version 2.0 (V2) standards. + + $(NoWarn);CS3016 diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Linux.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Linux.cs index d67782f40ad36b..4197abb9c1fd07 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Linux.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Linux.cs @@ -12,7 +12,7 @@ internal static class LdapPal internal static int AddDirectoryEntry(ConnectionHandle ldapHandle, string dn, IntPtr attrs, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => Interop.Ldap.ldap_add(ldapHandle, dn, attrs, servercontrol, clientcontrol, ref messageNumber); - internal static int CompareDirectoryEntries(ConnectionHandle ldapHandle, string dn, string attributeName, string strValue, berval binaryValue, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => + internal static int CompareDirectoryEntries(ConnectionHandle ldapHandle, string dn, string attributeName, string strValue, BerVal binaryValue, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => Interop.Ldap.ldap_compare(ldapHandle, dn, attributeName, binaryValue, servercontrol, clientcontrol, ref messageNumber); internal static void FreeDirectoryControl(IntPtr control) => Interop.Ldap.ldap_control_free(control); @@ -23,7 +23,7 @@ internal static int CompareDirectoryEntries(ConnectionHandle ldapHandle, string internal static int DeleteDirectoryEntry(ConnectionHandle ldapHandle, string dn, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => Interop.Ldap.ldap_delete_ext(ldapHandle, dn, servercontrol, clientcontrol, ref messageNumber); - internal static int ExtendedDirectoryOperation(ConnectionHandle ldapHandle, string oid, berval data, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => + internal static int ExtendedDirectoryOperation(ConnectionHandle ldapHandle, string oid, BerVal data, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => Interop.Ldap.ldap_extended_operation(ldapHandle, oid, data, servercontrol, clientcontrol, ref messageNumber); internal static IntPtr GetFirstAttributeFromEntry(ConnectionHandle ldapHandle, IntPtr result, ref IntPtr address) => Interop.Ldap.ldap_first_attribute(ldapHandle, result, ref address); @@ -109,7 +109,7 @@ internal static int BindToDirectory(ConnectionHandle ld, string who, string pass try { passwordPtr = LdapPal.StringToPtr(passwd); - berval passwordBerval = new berval + BerVal passwordBerval = new BerVal { bv_len = passwd.Length, bv_val = passwordPtr, diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Windows.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Windows.cs index 12cd9f7e8fd9fe..ddceb9b39f9783 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Windows.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Windows.cs @@ -12,7 +12,7 @@ internal static class LdapPal internal static int AddDirectoryEntry(ConnectionHandle ldapHandle, string dn, IntPtr attrs, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => Interop.Ldap.ldap_add(ldapHandle, dn, attrs, servercontrol, clientcontrol, ref messageNumber); - internal static int CompareDirectoryEntries(ConnectionHandle ldapHandle, string dn, string attributeName, string strValue, berval binaryValue, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => + internal static int CompareDirectoryEntries(ConnectionHandle ldapHandle, string dn, string attributeName, string strValue, BerVal binaryValue, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => Interop.Ldap.ldap_compare(ldapHandle, dn, attributeName, strValue, binaryValue, servercontrol, clientcontrol, ref messageNumber); internal static void FreeDirectoryControl(IntPtr control) => Interop.Ldap.ldap_control_free(control); @@ -23,7 +23,7 @@ internal static int CompareDirectoryEntries(ConnectionHandle ldapHandle, string internal static int DeleteDirectoryEntry(ConnectionHandle ldapHandle, string dn, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => Interop.Ldap.ldap_delete_ext(ldapHandle, dn, servercontrol, clientcontrol, ref messageNumber); - internal static int ExtendedDirectoryOperation(ConnectionHandle ldapHandle, string oid, berval data, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => + internal static int ExtendedDirectoryOperation(ConnectionHandle ldapHandle, string oid, BerVal data, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => Interop.Ldap.ldap_extended_operation(ldapHandle, oid, data, servercontrol, clientcontrol, ref messageNumber); internal static IntPtr GetFirstAttributeFromEntry(ConnectionHandle ldapHandle, IntPtr result, ref IntPtr address) => Interop.Ldap.ldap_first_attribute(ldapHandle, result, ref address); diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.Linux.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.Linux.cs index a7e43947e20314..5444766451c26a 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.Linux.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.Linux.cs @@ -60,7 +60,7 @@ public SafeBerHandle() : base(true) } } - internal SafeBerHandle(berval value) : base(true) + internal SafeBerHandle(BerVal value) : base(true) { // In Linux if bv_val is null ber_init will segFault instead of returning IntPtr.Zero. // In Linux if bv_len is 0 ber_init returns a valid pointer which will then fail when trying to use it, diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.Windows.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.Windows.cs index b86922c0466b40..81b65310bb57af 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.Windows.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SafeHandles.Windows.cs @@ -16,7 +16,7 @@ public SafeBerHandle() : base(true) } } - internal SafeBerHandle(berval value) : base(true) + internal SafeBerHandle(BerVal value) : base(true) { SetHandle(Interop.Ldap.ber_init(value)); if (handle == IntPtr.Zero) diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/BerConverter.Linux.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/BerConverter.Linux.cs index 393ebf330c2b54..1cbda9c7cd4c05 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/BerConverter.Linux.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/BerConverter.Linux.cs @@ -13,7 +13,7 @@ private static int DecodeBitStringHelper(ArrayList resultList, SafeBerHandle ber // In order to match behavior, in Linux we will interpret 'B' as 'O' when passing the call to libldap. int error = 0; - // return berval + // return BerVal byte[] byteArray = DecodingByteArrayHelper(berElement, 'O', ref error); if (!BerPal.IsBerDecodeError(error)) { diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/BerConverter.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/BerConverter.cs index 8833ae73e31dc3..b1ca5e02eb6b4f 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/BerConverter.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/BerConverter.cs @@ -251,7 +251,7 @@ public static byte[] Encode(string format, params object[] value) } // get the binary value back - berval binaryValue = new berval(); + BerVal binaryValue = new BerVal(); IntPtr flattenptr = IntPtr.Zero; try @@ -309,7 +309,7 @@ internal static object[] TryDecode(string format, byte[] value, out bool decodeS Debug.WriteLine("Begin decoding"); UTF8Encoding utf8Encoder = new UTF8Encoding(false, true); - berval berValue = new berval(); + BerVal berValue = new BerVal(); ArrayList resultList = new ArrayList(); SafeBerHandle berElement = null; @@ -390,7 +390,7 @@ internal static object[] TryDecode(string format, byte[] value, out bool decodeS } else if (fmt == 'O') { - // return berval + // return BerVal byte[] byteArray = DecodingByteArrayHelper(berElement, fmt, ref error); if (!BerPal.IsBerDecodeError(error)) { @@ -488,7 +488,7 @@ private static byte[] DecodingByteArrayHelper(SafeBerHandle berElement, char fmt { error = 0; IntPtr result = IntPtr.Zero; - berval binaryValue = new berval(); + BerVal binaryValue = new BerVal(); byte[] byteArray = null; // can't use SafeBerval here as CLR creates a SafeBerval which points to a different memory location, but when doing memory @@ -523,7 +523,7 @@ private static int EncodingMultiByteArrayHelper(SafeBerHandle berElement, byte[] { IntPtr berValArray = IntPtr.Zero; IntPtr tempPtr = IntPtr.Zero; - berval[] managedBervalArray = null; + BerVal[] managedBervalArray = null; int error = 0; try @@ -532,15 +532,15 @@ private static int EncodingMultiByteArrayHelper(SafeBerHandle berElement, byte[] { int i = 0; berValArray = Utility.AllocHGlobalIntPtrArray(tempValue.Length + 1); - int structSize = Marshal.SizeOf(typeof(berval)); - managedBervalArray = new berval[tempValue.Length]; + int structSize = Marshal.SizeOf(typeof(BerVal)); + managedBervalArray = new BerVal[tempValue.Length]; for (i = 0; i < tempValue.Length; i++) { byte[] byteArray = tempValue[i]; - // construct the managed berval - managedBervalArray[i] = new berval(); + // construct the managed BerVal + managedBervalArray[i] = new BerVal(); if (byteArray != null) { @@ -577,7 +577,7 @@ private static int EncodingMultiByteArrayHelper(SafeBerHandle berElement, byte[] } if (managedBervalArray != null) { - foreach (berval managedBerval in managedBervalArray) + foreach (BerVal managedBerval in managedBervalArray) { if (managedBerval.bv_val != IntPtr.Zero) { @@ -593,7 +593,7 @@ private static int EncodingMultiByteArrayHelper(SafeBerHandle berElement, byte[] private static byte[][] DecodingMultiByteArrayHelper(SafeBerHandle berElement, char fmt, ref int error) { error = 0; - // several berval + // several BerVal IntPtr ptrResult = IntPtr.Zero; int i = 0; ArrayList binaryList = new ArrayList(); @@ -611,7 +611,7 @@ private static byte[][] DecodingMultiByteArrayHelper(SafeBerHandle berElement, c tempPtr = Marshal.ReadIntPtr(ptrResult); while (tempPtr != IntPtr.Zero) { - berval ber = new berval(); + BerVal ber = new BerVal(); Marshal.PtrToStructure(tempPtr, ber); byte[] berArray = new byte[ber.bv_len]; diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryControl.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryControl.cs index ba46374c53c944..51b09c7d6d73bd 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryControl.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryControl.cs @@ -746,7 +746,7 @@ public override byte[] GetValue() LdapControl managedControl = new LdapControl(); Marshal.PtrToStructure(control, managedControl); - berval value = managedControl.ldctl_value; + BerVal value = managedControl.ldctl_value; // reinitialize the value _directoryControlValue = null; if (value != null) diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs index 508cb1020f06f2..7e8c81dc4afb04 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs @@ -541,7 +541,7 @@ private int SendRequestHelper(DirectoryRequest request, ref int messageID) IntPtr modArray = IntPtr.Zero; int addModCount = 0; - berval berValuePtr = null; + BerVal berValuePtr = null; IntPtr searchAttributes = IntPtr.Zero; int attributeCount = 0; @@ -639,7 +639,7 @@ private int SendRequestHelper(DirectoryRequest request, ref int messageID) { if (byteArray != null && byteArray.Length != 0) { - berValuePtr = new berval + berValuePtr = new BerVal { bv_len = byteArray.Length, bv_val = Marshal.AllocHGlobal(byteArray.Length) @@ -712,7 +712,7 @@ private int SendRequestHelper(DirectoryRequest request, ref int messageID) // process the requestvalue if (val != null && val.Length != 0) { - berValuePtr = new berval() + berValuePtr = new BerVal() { bv_len = val.Length, bv_val = Marshal.AllocHGlobal(val.Length) @@ -1252,7 +1252,7 @@ internal LdapControl[] BuildControlArray(DirectoryControlCollection controls, bo if (byteControlValue == null || byteControlValue.Length == 0) { // Treat the control value as null. - managedControls[i].ldctl_value = new berval + managedControls[i].ldctl_value = new BerVal { bv_len = 0, bv_val = IntPtr.Zero @@ -1260,7 +1260,7 @@ internal LdapControl[] BuildControlArray(DirectoryControlCollection controls, bo } else { - managedControls[i].ldctl_value = new berval + managedControls[i].ldctl_value = new BerVal { bv_len = byteControlValue.Length, bv_val = Marshal.AllocHGlobal(sizeof(byte) * byteControlValue.Length) @@ -1327,11 +1327,11 @@ internal LdapMod[] BuildAttributes(CollectionBase directoryAttributes, ArrayList // Write the values. int valuesCount = 0; - berval[] berValues = null; + BerVal[] berValues = null; if (modAttribute.Count > 0) { valuesCount = modAttribute.Count; - berValues = new berval[valuesCount]; + berValues = new BerVal[valuesCount]; for (int j = 0; j < valuesCount; j++) { byte[] byteArray = null; @@ -1348,7 +1348,7 @@ internal LdapMod[] BuildAttributes(CollectionBase directoryAttributes, ArrayList byteArray = (byte[])modAttribute[j]; } - berValues[j] = new berval() + berValues[j] = new BerVal() { bv_len = byteArray.Length, bv_val = Marshal.AllocHGlobal(byteArray.Length) @@ -1361,7 +1361,7 @@ internal LdapMod[] BuildAttributes(CollectionBase directoryAttributes, ArrayList } attributes[i].values = Utility.AllocHGlobalIntPtrArray(valuesCount + 1); - int structSize = Marshal.SizeOf(typeof(berval)); + int structSize = Marshal.SizeOf(typeof(BerVal)); IntPtr controlPtr = IntPtr.Zero; IntPtr tempPtr = IntPtr.Zero; @@ -1501,11 +1501,11 @@ internal async ValueTask ConstructResponseAsync(int messageId name = LdapPal.PtrToString(requestName); } - berval val = null; + BerVal val = null; byte[] requestValueArray = null; if (requestValue != IntPtr.Zero) { - val = new berval(); + val = new BerVal(); Marshal.PtrToStructure(requestValue, val); if (val.bv_len != 0 && val.bv_val != IntPtr.Zero) { @@ -1825,7 +1825,7 @@ internal DirectoryAttribute ConstructAttribute(IntPtr entryMessage, IntPtr attri IntPtr tempPtr = Marshal.ReadIntPtr(valuesArray, IntPtr.Size * count); while (tempPtr != IntPtr.Zero) { - berval bervalue = new berval(); + BerVal bervalue = new BerVal(); Marshal.PtrToStructure(tempPtr, bervalue); byte[] byteArray = null; if (bervalue.bv_len > 0 && bervalue.bv_val != IntPtr.Zero) diff --git a/src/libraries/System.DirectoryServices/Directory.Build.props b/src/libraries/System.DirectoryServices/Directory.Build.props index bfa544ca6f649e..0740890146a71c 100644 --- a/src/libraries/System.DirectoryServices/Directory.Build.props +++ b/src/libraries/System.DirectoryServices/Directory.Build.props @@ -5,6 +5,9 @@ plan on shipping a new desktop version out of band. Instead add API to a different assembly. --> 4.0.0.0 + + true Microsoft windows diff --git a/src/libraries/System.Drawing.Common/src/CompatibilitySuppressions.xml b/src/libraries/System.Drawing.Common/src/CompatibilitySuppressions.xml index 2fb526dc172a6f..4870097bdac776 100644 --- a/src/libraries/System.Drawing.Common/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Drawing.Common/src/CompatibilitySuppressions.xml @@ -54,4 +54,151 @@ lib/netstandard2.0/System.Drawing.Common.dll lib/net462/System.Drawing.Common.dll + + CP0002 + M:System.Drawing.Graphics.GetContextInfo(System.Drawing.PointF@) + lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.GetContextInfo(System.Drawing.PointF@,System.Drawing.Region@) + lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.get_TransformElements + lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.set_TransformElements(System.Numerics.Matrix3x2) + lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Drawing2D.Matrix.#ctor(System.Numerics.Matrix3x2) + lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Drawing2D.Matrix.get_MatrixElements + lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Drawing2D.Matrix.set_MatrixElements(System.Numerics.Matrix3x2) + lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.get_TransformElements + runtimes/unix/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.set_TransformElements(System.Numerics.Matrix3x2) + runtimes/unix/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.GetContextInfo(System.Drawing.PointF@) + runtimes/unix/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.GetContextInfo(System.Drawing.PointF@,System.Drawing.Region@) + runtimes/unix/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Drawing2D.Matrix.#ctor(System.Numerics.Matrix3x2) + runtimes/unix/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Drawing2D.Matrix.get_MatrixElements + runtimes/unix/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Drawing2D.Matrix.set_MatrixElements(System.Numerics.Matrix3x2) + runtimes/unix/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.get_TransformElements + runtimes/win/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.set_TransformElements(System.Numerics.Matrix3x2) + runtimes/win/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.GetContextInfo(System.Drawing.PointF@) + runtimes/win/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.GetContextInfo(System.Drawing.PointF@,System.Drawing.Region@) + runtimes/win/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Drawing2D.Matrix.#ctor(System.Numerics.Matrix3x2) + runtimes/win/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Drawing2D.Matrix.get_MatrixElements + runtimes/win/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Drawing2D.Matrix.set_MatrixElements(System.Numerics.Matrix3x2) + runtimes/win/lib/netcoreapp3.1/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + \ No newline at end of file diff --git a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Comdlg32.cs b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Comdlg32.cs index fc4c1d957d18b3..b6ed3098be47e1 100644 --- a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Comdlg32.cs +++ b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Comdlg32.cs @@ -8,11 +8,14 @@ internal static partial class Interop { internal static partial class Comdlg32 { - [DllImport(Libraries.Comdlg32, SetLastError = true, CharSet = CharSet.Auto)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. + [DllImport(Libraries.Comdlg32, CharSet = CharSet.Auto, SetLastError = true)] internal static extern bool PrintDlg([In, Out] PRINTDLG lppd); - [DllImport(Libraries.Comdlg32, SetLastError = true, CharSet = CharSet.Auto)] + [DllImport(Libraries.Comdlg32, CharSet = CharSet.Auto, SetLastError = true)] internal static extern bool PrintDlg([In, Out] PRINTDLGX86 lppd); +#pragma warning restore DLLIMPORTGENANALYZER015 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal sealed class PRINTDLG @@ -62,4 +65,4 @@ internal sealed class PRINTDLGX86 internal IntPtr hSetupTemplate; } } -} \ No newline at end of file +} diff --git a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs index 6c3e188d9a7fc6..7ea26b42835385 100644 --- a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs +++ b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs @@ -42,8 +42,8 @@ internal static partial class Gdi32 [DllImport(Libraries.Gdi32, SetLastError = true, CharSet = CharSet.Auto)] internal static extern IntPtr /*HDC*/ ResetDC(HandleRef hDC, HandleRef /*DEVMODE*/ lpDevMode); - [DllImport(Libraries.Gdi32, SetLastError = true, CharSet = CharSet.Auto)] - internal static extern int AddFontResourceEx(string lpszFilename, int fl, IntPtr pdv); + [GeneratedDllImport(Libraries.Gdi32, CharSet = CharSet.Auto, SetLastError = true)] + internal static partial int AddFontResourceEx(string lpszFilename, int fl, IntPtr pdv); internal static int AddFontFile(string fileName) { diff --git a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Kernel32.cs b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Kernel32.cs index d61946a8d1d49a..57d2b9171783ba 100644 --- a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Kernel32.cs +++ b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Kernel32.cs @@ -8,11 +8,11 @@ internal static partial class Interop { internal static partial class Kernel32 { - [DllImport(Libraries.Kernel32, SetLastError = true)] - public static extern int GetSystemDefaultLCID(); + [GeneratedDllImport(Libraries.Kernel32, SetLastError = true)] + public static partial int GetSystemDefaultLCID(); - [DllImport(Libraries.Kernel32, SetLastError = true, ExactSpelling = true, EntryPoint = "GlobalAlloc", CharSet = CharSet.Auto)] - internal static extern IntPtr IntGlobalAlloc(int uFlags, UIntPtr dwBytes); // size should be 32/64bits compatible + [GeneratedDllImport(Libraries.Kernel32, EntryPoint = "GlobalAlloc", SetLastError = true)] + internal static partial IntPtr IntGlobalAlloc(int uFlags, UIntPtr dwBytes); // size should be 32/64bits compatible internal static IntPtr GlobalAlloc(int uFlags, uint dwBytes) { diff --git a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.User32.cs b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.User32.cs index e00bfa9e1a9b02..dc485e0bab512f 100644 --- a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.User32.cs +++ b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.User32.cs @@ -20,14 +20,14 @@ internal static partial class User32 [DllImport(Libraries.User32, SetLastError = true, ExactSpelling = true)] internal static extern bool GetIconInfo(HandleRef hIcon, ref ICONINFO info); - [DllImport(Libraries.User32, SetLastError = true, ExactSpelling = true)] - public static extern int GetSystemMetrics(int nIndex); + [GeneratedDllImport(Libraries.User32, SetLastError = true)] + public static partial int GetSystemMetrics(int nIndex); [DllImport(Libraries.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] internal static extern bool DrawIconEx(HandleRef hDC, int x, int y, HandleRef hIcon, int width, int height, int iStepIfAniCursor, HandleRef hBrushFlickerFree, int diFlags); - [DllImport(Libraries.User32, ExactSpelling = true, SetLastError = true)] - internal static extern unsafe IntPtr CreateIconFromResourceEx(byte* pbIconBits, uint cbIconBits, bool fIcon, int dwVersion, int csDesired, int cyDesired, int flags); + [GeneratedDllImport(Libraries.User32, SetLastError = true)] + internal static unsafe partial IntPtr CreateIconFromResourceEx(byte* pbIconBits, uint cbIconBits, bool fIcon, int dwVersion, int csDesired, int cyDesired, int flags); [StructLayout(LayoutKind.Sequential)] internal struct ICONINFO diff --git a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Winspool.cs b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Winspool.cs index 05f50b02a5af2a..1b11a3fa45a8b9 100644 --- a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Winspool.cs +++ b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Winspool.cs @@ -8,8 +8,8 @@ internal static partial class Interop { internal static partial class Winspool { - [DllImport(Libraries.Winspool, SetLastError = true, CharSet = CharSet.Auto)] - internal static extern int DeviceCapabilities(string pDevice, string pPort, short fwCapabilities, IntPtr pOutput, IntPtr /*DEVMODE*/ pDevMode); + [GeneratedDllImport(Libraries.Winspool, CharSet = CharSet.Auto, SetLastError = true)] + internal static partial int DeviceCapabilities(string pDevice, string pPort, short fwCapabilities, IntPtr pOutput, IntPtr /*DEVMODE*/ pDevMode); [DllImport(Libraries.Winspool, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)] internal static extern int DocumentProperties(HandleRef hwnd, HandleRef hPrinter, string pDeviceName, IntPtr /*DEVMODE*/ pDevModeOutput, HandleRef /*DEVMODE*/ pDevModeInput, int fMode); @@ -17,7 +17,7 @@ internal static partial class Winspool [DllImport(Libraries.Winspool, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)] internal static extern int DocumentProperties(HandleRef hwnd, HandleRef hPrinter, string pDeviceName, IntPtr /*DEVMODE*/ pDevModeOutput, IntPtr /*DEVMODE*/ pDevModeInput, int fMode); - [DllImport(Libraries.Winspool, SetLastError = true, CharSet = CharSet.Auto)] - internal static extern int EnumPrinters(int flags, string? name, int level, IntPtr pPrinterEnum/*buffer*/, int cbBuf, out int pcbNeeded, out int pcReturned); + [GeneratedDllImport(Libraries.Winspool, CharSet = CharSet.Auto, SetLastError = true)] + internal static partial int EnumPrinters(int flags, string? name, int level, IntPtr pPrinterEnum/*buffer*/, int cbBuf, out int pcbNeeded, out int pcReturned); } } \ No newline at end of file diff --git a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj index 07dd32177629af..82058146952669 100644 --- a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj +++ b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -407,9 +407,11 @@ Unix support is disabled by default. See https://aka.ms/systemdrawingnonwindows + + diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs index f2f0594472a678..1a85c2a79bd0df 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs @@ -67,334 +67,389 @@ private static void PlatformInitialize() // Imported functions - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs + [DllImport(LibraryName)] internal static extern int GdiplusStartup(out IntPtr token, ref StartupInput input, out StartupOutput output); +#pragma warning restore DLLIMPORTGENANALYZER015 - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern void GdiplusShutdown(ref ulong token); + [GeneratedDllImport(LibraryName)] + internal static partial void GdiplusShutdown(ref ulong token); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern IntPtr GdipAlloc(int size); + [GeneratedDllImport(LibraryName)] + internal static partial IntPtr GdipAlloc(int size); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern void GdipFree(IntPtr ptr); + [GeneratedDllImport(LibraryName)] + internal static partial void GdipFree(IntPtr ptr); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeleteBrush(HandleRef brush); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetBrushType(IntPtr brush, out BrushType type); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetBrushType(IntPtr brush, out BrushType type); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeleteGraphics(HandleRef graphics); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipRestoreGraphics(IntPtr graphics, uint graphicsState); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipRestoreGraphics(IntPtr graphics, uint graphicsState); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipReleaseDC(HandleRef graphics, HandleRef hdc); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipFillPath(IntPtr graphics, IntPtr brush, IntPtr path); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipFillPath(IntPtr graphics, IntPtr brush, IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetNearestColor(IntPtr graphics, out int argb); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetNearestColor(IntPtr graphics, out int argb); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int GdipAddPathString(IntPtr path, string s, int lenght, IntPtr family, int style, float emSize, ref RectangleF layoutRect, IntPtr format); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int GdipAddPathStringI(IntPtr path, string s, int lenght, IntPtr family, int style, float emSize, ref Rectangle layoutRect, IntPtr format); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateFromHWND(IntPtr hwnd, out IntPtr graphics); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateFromHWND(IntPtr hwnd, out IntPtr graphics); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCloneImage(IntPtr image, out IntPtr imageclone); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCloneImage(IntPtr image, out IntPtr imageclone); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetImagePaletteSize(IntPtr image, out int size); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetImagePaletteSize(IntPtr image, out int size); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetImagePalette(IntPtr image, IntPtr palette, int size); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetImagePalette(IntPtr image, IntPtr palette, int size); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipSetImagePalette(IntPtr image, IntPtr palette); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipSetImagePalette(IntPtr image, IntPtr palette); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipGetImageBounds(IntPtr image, out RectangleF source, ref GraphicsUnit unit); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetImageThumbnail(IntPtr image, uint width, uint height, out IntPtr thumbImage, IntPtr callback, IntPtr callBackData); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetImageThumbnail(IntPtr image, uint width, uint height, out IntPtr thumbImage, IntPtr callback, IntPtr callBackData); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in CoreLib (like Guid). + [DllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int GdipSaveImageToFile(IntPtr image, string filename, ref Guid encoderClsID, IntPtr encoderParameters); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipSaveAdd(IntPtr image, IntPtr encoderParameters); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipSaveAdd(IntPtr image, IntPtr encoderParameters); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipSaveAddImage(IntPtr image, IntPtr imagenew, IntPtr encoderParameters); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipSaveAddImage(IntPtr image, IntPtr imagenew, IntPtr encoderParameters); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetImageGraphicsContext(IntPtr image, out IntPtr graphics); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetImageGraphicsContext(IntPtr image, out IntPtr graphics); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreatePath(FillMode brushMode, out IntPtr path); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreatePath(FillMode brushMode, out IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipCreatePath2(PointF[] points, byte[] types, int count, FillMode brushMode, out IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreatePath2I(Point[] points, byte[] types, int count, FillMode brushMode, out IntPtr path); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipClonePath(IntPtr path, out IntPtr clonePath); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipClonePath(IntPtr path, out IntPtr clonePath); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeletePath(HandleRef path); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipResetPath(IntPtr path); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipResetPath(IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPointCount(IntPtr path, out int count); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetPointCount(IntPtr path, out int count); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPathTypes(IntPtr path, [Out] byte[] types, int count); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetPathTypes(IntPtr path, byte[] types, int count); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipGetPathPoints(IntPtr path, [Out] PointF[] points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathPointsI(IntPtr path, [Out] Point[] points, int count); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPathFillMode(IntPtr path, out FillMode fillMode); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetPathFillMode(IntPtr path, out FillMode fillMode); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipSetPathFillMode(IntPtr path, FillMode fillMode); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipSetPathFillMode(IntPtr path, FillMode fillMode); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipStartPathFigure(IntPtr path); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipStartPathFigure(IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipClosePathFigure(IntPtr path); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipClosePathFigure(IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipClosePathFigures(IntPtr path); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipClosePathFigures(IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipSetPathMarker(IntPtr path); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipSetPathMarker(IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipClearPathMarkers(IntPtr path); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipClearPathMarkers(IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipReversePath(IntPtr path); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipReversePath(IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipGetPathLastPoint(IntPtr path, out PointF lastPoint); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathLine(IntPtr path, float x1, float y1, float x2, float y2); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathLine(IntPtr path, float x1, float y1, float x2, float y2); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipAddPathLine2(IntPtr path, PointF[] points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathLine2I(IntPtr path, Point[] points, int count); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathArc(IntPtr path, float x, float y, float width, float height, float startAngle, float sweepAngle); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathArc(IntPtr path, float x, float y, float width, float height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathBezier(IntPtr path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathBezier(IntPtr path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipAddPathBeziers(IntPtr path, PointF[] points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathCurve(IntPtr path, PointF[] points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathCurveI(IntPtr path, Point[] points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathCurve2(IntPtr path, PointF[] points, int count, float tension); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathCurve2I(IntPtr path, Point[] points, int count, float tension); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathCurve3(IntPtr path, PointF[] points, int count, int offset, int numberOfSegments, float tension); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathCurve3I(IntPtr path, Point[] points, int count, int offset, int numberOfSegments, float tension); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathClosedCurve(IntPtr path, PointF[] points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathClosedCurveI(IntPtr path, Point[] points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathClosedCurve2(IntPtr path, PointF[] points, int count, float tension); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathClosedCurve2I(IntPtr path, Point[] points, int count, float tension); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathRectangle(IntPtr path, float x, float y, float width, float height); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathRectangle(IntPtr path, float x, float y, float width, float height); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipAddPathRectangles(IntPtr path, RectangleF[] rects, int count); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathEllipse(IntPtr path, float x, float y, float width, float height); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathEllipse(IntPtr path, float x, float y, float width, float height); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathEllipseI(IntPtr path, int x, int y, int width, int height); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathEllipseI(IntPtr path, int x, int y, int width, int height); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathPie(IntPtr path, float x, float y, float width, float height, float startAngle, float sweepAngle); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathPie(IntPtr path, float x, float y, float width, float height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathPieI(IntPtr path, int x, int y, int width, int height, float startAngle, float sweepAngle); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathPieI(IntPtr path, int x, int y, int width, int height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipAddPathPolygon(IntPtr path, PointF[] points, int count); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathPath(IntPtr path, IntPtr addingPath, bool connect); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathPath(IntPtr path, IntPtr addingPath, bool connect); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathLineI(IntPtr path, int x1, int y1, int x2, int y2); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathLineI(IntPtr path, int x1, int y1, int x2, int y2); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathArcI(IntPtr path, int x, int y, int width, int height, float startAngle, float sweepAngle); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathArcI(IntPtr path, int x, int y, int width, int height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathBezierI(IntPtr path, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathBezierI(IntPtr path, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipAddPathBeziersI(IntPtr path, Point[] points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathPolygonI(IntPtr path, Point[] points, int count); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipAddPathRectangleI(IntPtr path, int x, int y, int width, int height); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipAddPathRectangleI(IntPtr path, int x, int y, int width, int height); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipAddPathRectanglesI(IntPtr path, Rectangle[] rects, int count); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipFlattenPath(IntPtr path, IntPtr matrix, float floatness); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipFlattenPath(IntPtr path, IntPtr matrix, float floatness); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipTransformPath(IntPtr path, IntPtr matrix); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipTransformPath(IntPtr path, IntPtr matrix); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipWarpPath(IntPtr path, IntPtr matrix, PointF[] points, int count, float srcx, float srcy, float srcwidth, float srcheight, WarpMode mode, float flatness); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipWidenPath(IntPtr path, IntPtr pen, IntPtr matrix, float flatness); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipWidenPath(IntPtr path, IntPtr pen, IntPtr matrix, float flatness); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipGetPathWorldBounds(IntPtr path, out RectangleF bounds, IntPtr matrix, IntPtr pen); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathWorldBoundsI(IntPtr path, out Rectangle bounds, IntPtr matrix, IntPtr pen); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipIsVisiblePathPoint(IntPtr path, float x, float y, IntPtr graphics, out bool result); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipIsVisiblePathPoint(IntPtr path, float x, float y, IntPtr graphics, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipIsVisiblePathPointI(IntPtr path, int x, int y, IntPtr graphics, out bool result); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipIsVisiblePathPointI(IntPtr path, int x, int y, IntPtr graphics, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipIsOutlineVisiblePathPoint(IntPtr path, float x, float y, IntPtr pen, IntPtr graphics, out bool result); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipIsOutlineVisiblePathPoint(IntPtr path, float x, float y, IntPtr pen, IntPtr graphics, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipIsOutlineVisiblePathPointI(IntPtr path, int x, int y, IntPtr pen, IntPtr graphics, out bool result); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipIsOutlineVisiblePathPointI(IntPtr path, int x, int y, IntPtr pen, IntPtr graphics, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateFontFromLogfont(IntPtr hdc, ref Interop.User32.LOGFONT lf, out IntPtr ptr); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateFontFromLogfont(IntPtr hdc, ref Interop.User32.LOGFONT lf, out IntPtr ptr); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateFontFromHfont(IntPtr hdc, out IntPtr font, ref Interop.User32.LOGFONT lf); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateFontFromHfont(IntPtr hdc, out IntPtr font, ref Interop.User32.LOGFONT lf); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipGetMetafileHeaderFromFile(string filename, IntPtr header); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipGetMetafileHeaderFromFile(string filename, IntPtr header); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetMetafileHeaderFromMetafile(IntPtr metafile, IntPtr header); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetMetafileHeaderFromMetafile(IntPtr metafile, IntPtr header); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetMetafileHeaderFromEmf(IntPtr hEmf, IntPtr header); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetMetafileHeaderFromEmf(IntPtr hEmf, IntPtr header); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetMetafileHeaderFromWmf(IntPtr hWmf, IntPtr wmfPlaceableFileHeader, IntPtr header); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetMetafileHeaderFromWmf(IntPtr hWmf, IntPtr wmfPlaceableFileHeader, IntPtr header); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetHemfFromMetafile(IntPtr metafile, out IntPtr hEmf); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetHemfFromMetafile(IntPtr metafile, out IntPtr hEmf); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetMetafileDownLevelRasterizationLimit(IntPtr metafile, ref uint metafileRasterizationLimitDpi); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetMetafileDownLevelRasterizationLimit(IntPtr metafile, ref uint metafileRasterizationLimitDpi); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipSetMetafileDownLevelRasterizationLimit(IntPtr metafile, uint metafileRasterizationLimitDpi); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipSetMetafileDownLevelRasterizationLimit(IntPtr metafile, uint metafileRasterizationLimitDpi); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateFromContext_macosx(IntPtr cgref, int width, int height, out IntPtr graphics); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateFromContext_macosx(IntPtr cgref, int width, int height, out IntPtr graphics); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipSetVisibleClip_linux(IntPtr graphics, ref Rectangle rect); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateFromXDrawable_linux(IntPtr drawable, IntPtr display, out IntPtr graphics); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateFromXDrawable_linux(IntPtr drawable, IntPtr display, out IntPtr graphics); // Stream functions for non-Win32 (libgdiplus specific) - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipLoadImageFromDelegate_linux(StreamGetHeaderDelegate getHeader, + [GeneratedDllImport(LibraryName)] + internal static partial int GdipLoadImageFromDelegate_linux(StreamGetHeaderDelegate getHeader, StreamGetBytesDelegate getBytes, StreamPutBytesDelegate putBytes, StreamSeekDelegate doSeek, StreamCloseDelegate close, StreamSizeDelegate size, out IntPtr image); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in CoreLib (like Guid). + [DllImport(LibraryName)] internal static extern int GdipSaveImageToDelegate_linux(IntPtr image, StreamGetBytesDelegate getBytes, StreamPutBytesDelegate putBytes, StreamSeekDelegate doSeek, StreamCloseDelegate close, StreamSizeDelegate size, ref Guid encoderClsID, IntPtr encoderParameters); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateMetafileFromDelegate_linux(StreamGetHeaderDelegate getHeader, + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateMetafileFromDelegate_linux(StreamGetHeaderDelegate getHeader, StreamGetBytesDelegate getBytes, StreamPutBytesDelegate putBytes, StreamSeekDelegate doSeek, StreamCloseDelegate close, StreamSizeDelegate size, out IntPtr metafile); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetMetafileHeaderFromDelegate_linux(StreamGetHeaderDelegate getHeader, + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetMetafileHeaderFromDelegate_linux(StreamGetHeaderDelegate getHeader, StreamGetBytesDelegate getBytes, StreamPutBytesDelegate putBytes, StreamSeekDelegate doSeek, StreamCloseDelegate close, StreamSizeDelegate size, IntPtr header); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int GdipRecordMetafileFromDelegate_linux(StreamGetHeaderDelegate getHeader, StreamGetBytesDelegate getBytes, StreamPutBytesDelegate putBytes, StreamSeekDelegate doSeek, StreamCloseDelegate close, StreamSizeDelegate size, IntPtr hdc, EmfType type, ref RectangleF frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int GdipRecordMetafileFromDelegateI_linux(StreamGetHeaderDelegate getHeader, StreamGetBytesDelegate getBytes, StreamPutBytesDelegate putBytes, StreamSeekDelegate doSeek, StreamCloseDelegate close, StreamSizeDelegate size, IntPtr hdc, EmfType type, ref Rectangle frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPostScriptGraphicsContext( + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetPostScriptGraphicsContext( [MarshalAs(UnmanagedType.LPStr)] string filename, int width, int height, double dpix, double dpiy, ref IntPtr graphics); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPostScriptSavePage(IntPtr graphics); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetPostScriptSavePage(IntPtr graphics); } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs index dbe2888361346e..129416828600aa 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs @@ -20,344 +20,350 @@ private static void PlatformInitialize() // Imported functions - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. + [DllImport(LibraryName)] private static extern int GdiplusStartup(out IntPtr token, ref StartupInput input, out StartupOutput output); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreatePath(int brushMode, out IntPtr path); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreatePath(int brushMode, out IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreatePath2(PointF* points, byte* types, int count, int brushMode, out IntPtr path); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreatePath2(PointF* points, byte* types, int count, int brushMode, out IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreatePath2I(Point* points, byte* types, int count, int brushMode, out IntPtr path); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreatePath2I(Point* points, byte* types, int count, int brushMode, out IntPtr path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipClonePath(HandleRef path, out IntPtr clonepath); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeletePath(HandleRef path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipResetPath(HandleRef path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPointCount(HandleRef path, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathTypes(HandleRef path, byte[] types, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathPoints(HandleRef path, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathFillMode(HandleRef path, out FillMode fillmode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPathFillMode(HandleRef path, FillMode fillmode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathData(HandleRef path, GpPathData* pathData); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipStartPathFigure(HandleRef path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipClosePathFigure(HandleRef path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipClosePathFigures(HandleRef path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPathMarker(HandleRef path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipClearPathMarkers(HandleRef path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipReversePath(HandleRef path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathLastPoint(HandleRef path, out PointF lastPoint); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathLine(HandleRef path, float x1, float y1, float x2, float y2); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathLine2(HandleRef path, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathArc(HandleRef path, float x, float y, float width, float height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathBezier(HandleRef path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathBeziers(HandleRef path, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathCurve(HandleRef path, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathCurve2(HandleRef path, PointF* points, int count, float tension); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathCurve3(HandleRef path, PointF* points, int count, int offset, int numberOfSegments, float tension); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathClosedCurve(HandleRef path, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathClosedCurve2(HandleRef path, PointF* points, int count, float tension); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathRectangle(HandleRef path, float x, float y, float width, float height); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathRectangles(HandleRef path, RectangleF* rects, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathEllipse(HandleRef path, float x, float y, float width, float height); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathPie(HandleRef path, float x, float y, float width, float height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathPolygon(HandleRef path, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathPath(HandleRef path, HandleRef addingPath, bool connect); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode)] internal static extern int GdipAddPathString(HandleRef path, string s, int length, HandleRef fontFamily, int style, float emSize, ref RectangleF layoutRect, HandleRef format); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode)] internal static extern int GdipAddPathStringI(HandleRef path, string s, int length, HandleRef fontFamily, int style, float emSize, ref Rectangle layoutRect, HandleRef format); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathLineI(HandleRef path, int x1, int y1, int x2, int y2); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathLine2I(HandleRef path, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathArcI(HandleRef path, int x, int y, int width, int height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathBezierI(HandleRef path, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathBeziersI(HandleRef path, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathCurveI(HandleRef path, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathCurve2I(HandleRef path, Point* points, int count, float tension); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathCurve3I(HandleRef path, Point* points, int count, int offset, int numberOfSegments, float tension); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathClosedCurveI(HandleRef path, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathClosedCurve2I(HandleRef path, Point* points, int count, float tension); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathRectangleI(HandleRef path, int x, int y, int width, int height); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathRectanglesI(HandleRef path, Rectangle* rects, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathEllipseI(HandleRef path, int x, int y, int width, int height); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathPieI(HandleRef path, int x, int y, int width, int height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipAddPathPolygonI(HandleRef path, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipFlattenPath(HandleRef path, HandleRef matrixfloat, float flatness); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipWidenPath(HandleRef path, HandleRef pen, HandleRef matrix, float flatness); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipWarpPath(HandleRef path, HandleRef matrix, PointF* points, int count, float srcX, float srcY, float srcWidth, float srcHeight, WarpMode warpMode, float flatness); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTransformPath(HandleRef path, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathWorldBounds(HandleRef path, out RectangleF gprectf, HandleRef matrix, HandleRef pen); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsVisiblePathPoint(HandleRef path, float x, float y, HandleRef graphics, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsVisiblePathPointI(HandleRef path, int x, int y, HandleRef graphics, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsOutlineVisiblePathPoint(HandleRef path, float x, float y, HandleRef pen, HandleRef graphics, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsOutlineVisiblePathPointI(HandleRef path, int x, int y, HandleRef pen, HandleRef graphics, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeleteBrush(HandleRef brush); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipLoadImageFromStream(IntPtr stream, IntPtr* image); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipLoadImageFromStream(IntPtr stream, IntPtr* image); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipLoadImageFromStreamICM(IntPtr stream, IntPtr* image); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipLoadImageFromStreamICM(IntPtr stream, IntPtr* image); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCloneImage(HandleRef image, out IntPtr cloneimage); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode)] internal static extern int GdipSaveImageToFile(HandleRef image, string filename, ref Guid classId, HandleRef encoderParams); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSaveImageToStream(HandleRef image, IntPtr stream, Guid* classId, HandleRef encoderParams); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSaveAdd(HandleRef image, HandleRef encoderParams); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSaveAddImage(HandleRef image, HandleRef newImage, HandleRef encoderParams); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImageGraphicsContext(HandleRef image, out IntPtr graphics); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImageBounds(HandleRef image, out RectangleF gprectf, out GraphicsUnit unit); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImageThumbnail(HandleRef image, int thumbWidth, int thumbHeight, out IntPtr thumbImage, Image.GetThumbnailImageAbort? callback, IntPtr callbackdata); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImagePalette(HandleRef image, IntPtr palette, int size); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetImagePalette(HandleRef image, IntPtr palette); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImagePaletteSize(HandleRef image, out int size); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipImageForceValidation(IntPtr image); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipImageForceValidation(IntPtr image); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateFromHDC2(IntPtr hdc, IntPtr hdevice, out IntPtr graphics); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateFromHDC2(IntPtr hdc, IntPtr hdevice, out IntPtr graphics); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateFromHWND(IntPtr hwnd, out IntPtr graphics); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateFromHWND(IntPtr hwnd, out IntPtr graphics); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeleteGraphics(HandleRef graphics); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipReleaseDC(HandleRef graphics, IntPtr hdc); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetNearestColor(HandleRef graphics, ref int color); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern IntPtr GdipCreateHalftonePalette(); + [GeneratedDllImport(LibraryName)] + internal static partial IntPtr GdipCreateHalftonePalette(); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawBeziers(HandleRef graphics, HandleRef pen, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawBeziersI(HandleRef graphics, HandleRef pen, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillPath(HandleRef graphics, HandleRef brush, HandleRef path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEnumerateMetafileDestPoint(HandleRef graphics, HandleRef metafile, ref PointF destPoint, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEnumerateMetafileDestPointI(HandleRef graphics, HandleRef metafile, ref Point destPoint, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEnumerateMetafileDestRect(HandleRef graphics, HandleRef metafile, ref RectangleF destRect, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEnumerateMetafileDestRectI(HandleRef graphics, HandleRef metafile, ref Rectangle destRect, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEnumerateMetafileDestPoints(HandleRef graphics, HandleRef metafile, PointF* destPoints, int count, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEnumerateMetafileDestPointsI(HandleRef graphics, HandleRef metafile, Point* destPoints, int count, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEnumerateMetafileSrcRectDestPoint(HandleRef graphics, HandleRef metafile, ref PointF destPoint, ref RectangleF srcRect, GraphicsUnit pageUnit, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEnumerateMetafileSrcRectDestPointI(HandleRef graphics, HandleRef metafile, ref Point destPoint, ref Rectangle srcRect, GraphicsUnit pageUnit, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEnumerateMetafileSrcRectDestRect(HandleRef graphics, HandleRef metafile, ref RectangleF destRect, ref RectangleF srcRect, GraphicsUnit pageUnit, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEnumerateMetafileSrcRectDestRectI(HandleRef graphics, HandleRef metafile, ref Rectangle destRect, ref Rectangle srcRect, GraphicsUnit pageUnit, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEnumerateMetafileSrcRectDestPoints(HandleRef graphics, HandleRef metafile, PointF* destPoints, int count, ref RectangleF srcRect, GraphicsUnit pageUnit, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEnumerateMetafileSrcRectDestPointsI(HandleRef graphics, HandleRef metafile, Point* destPoints, int count, ref Rectangle srcRect, GraphicsUnit pageUnit, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipRestoreGraphics(HandleRef graphics, int state); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. + [DllImport(LibraryName)] internal static extern int GdipGetMetafileHeaderFromWmf(IntPtr hMetafile, WmfPlaceableFileHeader wmfplaceable, [In] [Out] MetafileHeaderWmf metafileHeaderWmf); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetMetafileHeaderFromEmf(IntPtr hEnhMetafile, [In] [Out] MetafileHeaderEmf metafileHeaderEmf); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipGetMetafileHeaderFromFile(string filename, IntPtr header); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipGetMetafileHeaderFromFile(string filename, IntPtr header); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetMetafileHeaderFromStream(IntPtr stream, IntPtr header); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetMetafileHeaderFromStream(IntPtr stream, IntPtr header); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetMetafileHeaderFromMetafile(HandleRef metafile, IntPtr header); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetHemfFromMetafile(HandleRef metafile, out IntPtr hEnhMetafile); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateMetafileFromStream(IntPtr stream, IntPtr* metafile); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateMetafileFromStream(IntPtr stream, IntPtr* metafile); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, RectangleF* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, RectangleF* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileStreamI(IntPtr stream, IntPtr referenceHdc, EmfType emfType, Rectangle* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipRecordMetafileStreamI(IntPtr stream, IntPtr referenceHdc, EmfType emfType, Rectangle* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipComment(HandleRef graphics, int sizeData, byte[] data); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipCreateFontFromLogfontW(IntPtr hdc, ref Interop.User32.LOGFONT lf, out IntPtr font); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipCreateFontFromLogfontW(IntPtr hdc, ref Interop.User32.LOGFONT lf, out IntPtr font); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateBitmapFromStream(IntPtr stream, IntPtr* bitmap); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateBitmapFromStream(IntPtr stream, IntPtr* bitmap); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateBitmapFromStreamICM(IntPtr stream, IntPtr* bitmap); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateBitmapFromStreamICM(IntPtr stream, IntPtr* bitmap); } } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs index c9539e24a38761..a435640a795c19 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs @@ -15,1364 +15,1382 @@ internal static partial class SafeNativeMethods internal static unsafe partial class Gdip { // Shared function imports (all platforms) - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipBeginContainer(HandleRef graphics, ref RectangleF dstRect, ref RectangleF srcRect, GraphicsUnit unit, out int state); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipBeginContainer2(HandleRef graphics, out int state); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipBeginContainerI(HandleRef graphics, ref Rectangle dstRect, ref Rectangle srcRect, GraphicsUnit unit, out int state); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipEndContainer(HandleRef graphics, int state); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateAdjustableArrowCap(float height, float width, bool isFilled, out IntPtr adjustableArrowCap); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateAdjustableArrowCap(float height, float width, bool isFilled, out IntPtr adjustableArrowCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetAdjustableArrowCapHeight(HandleRef adjustableArrowCap, out float height); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetAdjustableArrowCapHeight(HandleRef adjustableArrowCap, float height); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetAdjustableArrowCapWidth(HandleRef adjustableArrowCap, float width); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetAdjustableArrowCapWidth(HandleRef adjustableArrowCap, out float width); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetAdjustableArrowCapMiddleInset(HandleRef adjustableArrowCap, float middleInset); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetAdjustableArrowCapMiddleInset(HandleRef adjustableArrowCap, out float middleInset); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetAdjustableArrowCapFillState(HandleRef adjustableArrowCap, bool fillState); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetAdjustableArrowCapFillState(HandleRef adjustableArrowCap, out bool fillState); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetCustomLineCapType(IntPtr customCap, out CustomLineCapType capType); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetCustomLineCapType(IntPtr customCap, out CustomLineCapType capType); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateCustomLineCap(HandleRef fillpath, HandleRef strokepath, LineCap baseCap, float baseInset, out IntPtr customCap); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipDeleteCustomLineCap(IntPtr customCap); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipDeleteCustomLineCap(IntPtr customCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeleteCustomLineCap(HandleRef customCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCloneCustomLineCap(HandleRef customCap, out IntPtr clonedCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetCustomLineCapStrokeCaps(HandleRef customCap, LineCap startCap, LineCap endCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetCustomLineCapStrokeCaps(HandleRef customCap, out LineCap startCap, out LineCap endCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetCustomLineCapStrokeJoin(HandleRef customCap, LineJoin lineJoin); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetCustomLineCapStrokeJoin(HandleRef customCap, out LineJoin lineJoin); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetCustomLineCapBaseCap(HandleRef customCap, LineCap baseCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetCustomLineCapBaseCap(HandleRef customCap, out LineCap baseCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetCustomLineCapBaseInset(HandleRef customCap, float inset); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetCustomLineCapBaseInset(HandleRef customCap, out float inset); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetCustomLineCapWidthScale(HandleRef customCap, float widthScale); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetCustomLineCapWidthScale(HandleRef customCap, out float widthScale); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreatePathIter(out IntPtr pathIter, HandleRef path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeletePathIter(HandleRef pathIter); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPathIterNextSubpath(HandleRef pathIter, out int resultCount, out int startIndex, out int endIndex, out bool isClosed); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPathIterNextSubpathPath(HandleRef pathIter, out int resultCount, HandleRef path, out bool isClosed); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPathIterNextPathType(HandleRef pathIter, out int resultCount, out byte pathType, out int startIndex, out int endIndex); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPathIterNextMarker(HandleRef pathIter, out int resultCount, out int startIndex, out int endIndex); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPathIterNextMarkerPath(HandleRef pathIter, out int resultCount, HandleRef path); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPathIterGetCount(HandleRef pathIter, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPathIterGetSubpathCount(HandleRef pathIter, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPathIterHasCurve(HandleRef pathIter, out bool hasCurve); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPathIterRewind(HandleRef pathIter); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPathIterEnumerate(HandleRef pathIter, out int resultCount, PointF* points, byte* types, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPathIterCopyData(HandleRef pathIter, out int resultCount, PointF* points, byte* types, int startIndex, int endIndex); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateHatchBrush(int hatchstyle, int forecol, int backcol, out IntPtr brush); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateHatchBrush(int hatchstyle, int forecol, int backcol, out IntPtr brush); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetHatchStyle(HandleRef brush, out int hatchstyle); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetHatchForegroundColor(HandleRef brush, out int forecol); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetHatchBackgroundColor(HandleRef brush, out int backcol); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCloneBrush(HandleRef brush, out IntPtr clonebrush); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipCreateLineBrush(ref PointF point1, ref PointF point2, int color1, int color2, WrapMode wrapMode, out IntPtr lineGradient); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateLineBrushI(ref Point point1, ref Point point2, int color1, int color2, WrapMode wrapMode, out IntPtr lineGradient); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateLineBrushFromRect(ref RectangleF rect, int color1, int color2, LinearGradientMode lineGradientMode, WrapMode wrapMode, out IntPtr lineGradient); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateLineBrushFromRectI(ref Rectangle rect, int color1, int color2, LinearGradientMode lineGradientMode, WrapMode wrapMode, out IntPtr lineGradient); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateLineBrushFromRectWithAngle(ref RectangleF rect, int color1, int color2, float angle, bool isAngleScaleable, WrapMode wrapMode, out IntPtr lineGradient); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateLineBrushFromRectWithAngleI(ref Rectangle rect, int color1, int color2, float angle, bool isAngleScaleable, WrapMode wrapMode, out IntPtr lineGradient); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetLineColors(HandleRef brush, int color1, int color2); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetLineColors(HandleRef brush, int[] colors); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetLineRect(HandleRef brush, out RectangleF gprectf); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetLineGammaCorrection(HandleRef brush, out bool useGammaCorrection); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetLineGammaCorrection(HandleRef brush, bool useGammaCorrection); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetLineSigmaBlend(HandleRef brush, float focus, float scale); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetLineLinearBlend(HandleRef brush, float focus, float scale); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetLineBlendCount(HandleRef brush, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetLineBlend(HandleRef brush, IntPtr blend, IntPtr positions, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetLineBlend(HandleRef brush, IntPtr blend, IntPtr positions, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetLinePresetBlendCount(HandleRef brush, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetLinePresetBlend(HandleRef brush, IntPtr blend, IntPtr positions, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetLinePresetBlend(HandleRef brush, IntPtr blend, IntPtr positions, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetLineWrapMode(HandleRef brush, int wrapMode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetLineWrapMode(HandleRef brush, out int wrapMode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipResetLineTransform(HandleRef brush); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipMultiplyLineTransform(HandleRef brush, HandleRef matrix, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetLineTransform(HandleRef brush, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetLineTransform(HandleRef brush, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTranslateLineTransform(HandleRef brush, float dx, float dy, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipScaleLineTransform(HandleRef brush, float sx, float sy, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipRotateLineTransform(HandleRef brush, float angle, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreatePathGradient(PointF* points, int count, WrapMode wrapMode, out IntPtr brush); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreatePathGradient(PointF* points, int count, WrapMode wrapMode, out IntPtr brush); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreatePathGradientI(Point* points, int count, WrapMode wrapMode, out IntPtr brush); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreatePathGradientI(Point* points, int count, WrapMode wrapMode, out IntPtr brush); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreatePathGradientFromPath(HandleRef path, out IntPtr brush); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientCenterColor(HandleRef brush, out int color); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPathGradientCenterColor(HandleRef brush, int color); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientSurroundColorsWithCount(HandleRef brush, int[] color, ref int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPathGradientSurroundColorsWithCount(HandleRef brush, int[] argb, ref int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientCenterPoint(HandleRef brush, out PointF point); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPathGradientCenterPoint(HandleRef brush, ref PointF point); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientRect(HandleRef brush, out RectangleF gprectf); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientPointCount(HandleRef brush, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientSurroundColorCount(HandleRef brush, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientBlendCount(HandleRef brush, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientBlend(HandleRef brush, float[] blend, float[] positions, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPathGradientBlend(HandleRef brush, IntPtr blend, IntPtr positions, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientPresetBlendCount(HandleRef brush, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientPresetBlend(HandleRef brush, int[] blend, float[] positions, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPathGradientPresetBlend(HandleRef brush, int[] blend, float[] positions, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPathGradientSigmaBlend(HandleRef brush, float focus, float scale); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPathGradientLinearBlend(HandleRef brush, float focus, float scale); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPathGradientWrapMode(HandleRef brush, int wrapmode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientWrapMode(HandleRef brush, out int wrapmode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPathGradientTransform(HandleRef brush, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientTransform(HandleRef brush, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipResetPathGradientTransform(HandleRef brush); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipMultiplyPathGradientTransform(HandleRef brush, HandleRef matrix, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTranslatePathGradientTransform(HandleRef brush, float dx, float dy, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipScalePathGradientTransform(HandleRef brush, float sx, float sy, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipRotatePathGradientTransform(HandleRef brush, float angle, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPathGradientFocusScales(HandleRef brush, float[] xScale, float[] yScale); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPathGradientFocusScales(HandleRef brush, float xScale, float yScale); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateImageAttributes(out IntPtr imageattr); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateImageAttributes(out IntPtr imageattr); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCloneImageAttributes(HandleRef imageattr, out IntPtr cloneImageattr); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDisposeImageAttributes(HandleRef imageattr); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetImageAttributesColorMatrix(HandleRef imageattr, ColorAdjustType type, bool enableFlag, ColorMatrix? colorMatrix, ColorMatrix? grayMatrix, ColorMatrixFlag flags); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetImageAttributesThreshold(HandleRef imageattr, ColorAdjustType type, bool enableFlag, float threshold); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetImageAttributesGamma(HandleRef imageattr, ColorAdjustType type, bool enableFlag, float gamma); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetImageAttributesNoOp(HandleRef imageattr, ColorAdjustType type, bool enableFlag); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetImageAttributesColorKeys(HandleRef imageattr, ColorAdjustType type, bool enableFlag, int colorLow, int colorHigh); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetImageAttributesOutputChannel(HandleRef imageattr, ColorAdjustType type, bool enableFlag, ColorChannelFlag flags); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode)] internal static extern int GdipSetImageAttributesOutputChannelColorProfile(HandleRef imageattr, ColorAdjustType type, bool enableFlag, string colorProfileFilename); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetImageAttributesRemapTable(HandleRef imageattr, ColorAdjustType type, bool enableFlag, int mapSize, IntPtr map); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetImageAttributesWrapMode(HandleRef imageattr, int wrapmode, int argb, bool clamp); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImageAttributesAdjustedPalette(HandleRef imageattr, IntPtr palette, ColorAdjustType type); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetImageDecodersSize(out int numDecoders, out int size); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetImageDecodersSize(out int numDecoders, out int size); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetImageDecoders(int numDecoders, int size, IntPtr decoders); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetImageDecoders(int numDecoders, int size, IntPtr decoders); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetImageEncodersSize(out int numEncoders, out int size); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetImageEncodersSize(out int numEncoders, out int size); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetImageEncoders(int numEncoders, int size, IntPtr encoders); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetImageEncoders(int numEncoders, int size, IntPtr encoders); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateSolidFill(int color, out IntPtr brush); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateSolidFill(int color, out IntPtr brush); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetSolidFillColor(HandleRef brush, int color); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetSolidFillColor(HandleRef brush, out int color); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateTexture(HandleRef bitmap, int wrapmode, out IntPtr texture); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateTexture2(HandleRef bitmap, int wrapmode, float x, float y, float width, float height, out IntPtr texture); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateTextureIA(HandleRef bitmap, HandleRef imageAttrib, float x, float y, float width, float height, out IntPtr texture); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateTexture2I(HandleRef bitmap, int wrapmode, int x, int y, int width, int height, out IntPtr texture); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateTextureIAI(HandleRef bitmap, HandleRef imageAttrib, int x, int y, int width, int height, out IntPtr texture); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetTextureTransform(HandleRef brush, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetTextureTransform(HandleRef brush, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipResetTextureTransform(HandleRef brush); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipMultiplyTextureTransform(HandleRef brush, HandleRef matrix, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTranslateTextureTransform(HandleRef brush, float dx, float dy, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipScaleTextureTransform(HandleRef brush, float sx, float sy, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipRotateTextureTransform(HandleRef brush, float angle, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetTextureWrapMode(HandleRef brush, int wrapMode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetTextureWrapMode(HandleRef brush, out int wrapMode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetTextureImage(HandleRef brush, out IntPtr image); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetFontCollectionFamilyCount(HandleRef fontCollection, out int numFound); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetFontCollectionFamilyList(HandleRef fontCollection, int numSought, IntPtr[] gpfamilies, out int numFound); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCloneFontFamily(IntPtr fontfamily, out IntPtr clonefontfamily); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCloneFontFamily(IntPtr fontfamily, out IntPtr clonefontfamily); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode)] internal static extern int GdipCreateFontFamilyFromName(string name, HandleRef fontCollection, out IntPtr FontFamily); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetGenericFontFamilySansSerif(out IntPtr fontfamily); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetGenericFontFamilySansSerif(out IntPtr fontfamily); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetGenericFontFamilySerif(out IntPtr fontfamily); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetGenericFontFamilySerif(out IntPtr fontfamily); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetGenericFontFamilyMonospace(out IntPtr fontfamily); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetGenericFontFamilyMonospace(out IntPtr fontfamily); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeleteFontFamily(HandleRef fontFamily); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode)] internal static extern int GdipGetFamilyName(HandleRef family, char* name, int language); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsStyleAvailable(HandleRef family, FontStyle style, out int isStyleAvailable); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetEmHeight(HandleRef family, FontStyle style, out int EmHeight); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetCellAscent(HandleRef family, FontStyle style, out int CellAscent); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetCellDescent(HandleRef family, FontStyle style, out int CellDescent); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetLineSpacing(HandleRef family, FontStyle style, out int LineSpaceing); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipNewInstalledFontCollection(out IntPtr fontCollection); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipNewInstalledFontCollection(out IntPtr fontCollection); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipNewPrivateFontCollection(out IntPtr fontCollection); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipNewPrivateFontCollection(out IntPtr fontCollection); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipDeletePrivateFontCollection(ref IntPtr fontCollection); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipDeletePrivateFontCollection(ref IntPtr fontCollection); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode)] internal static extern int GdipPrivateAddFontFile(HandleRef fontCollection, string filename); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPrivateAddMemoryFont(HandleRef fontCollection, IntPtr memory, int length); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateFont(HandleRef fontFamily, float emSize, FontStyle style, GraphicsUnit unit, out IntPtr font); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateFontFromDC(IntPtr hdc, ref IntPtr font); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateFontFromDC(IntPtr hdc, ref IntPtr font); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCloneFont(HandleRef font, out IntPtr cloneFont); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeleteFont(HandleRef font); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetFamily(HandleRef font, out IntPtr family); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetFontStyle(HandleRef font, out FontStyle style); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetFontSize(HandleRef font, out float size); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetFontHeight(HandleRef font, HandleRef graphics, out float size); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetFontHeightGivenDPI(HandleRef font, float dpi, out float size); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetFontUnit(HandleRef font, out GraphicsUnit unit); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetLogFontW(HandleRef font, HandleRef graphics, ref Interop.User32.LOGFONT lf); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreatePen1(int argb, float width, int unit, out IntPtr pen); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreatePen1(int argb, float width, int unit, out IntPtr pen); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreatePen2(HandleRef brush, float width, int unit, out IntPtr pen); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipClonePen(HandleRef pen, out IntPtr clonepen); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeletePen(HandleRef Pen); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenMode(HandleRef pen, PenAlignment penAlign); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenMode(HandleRef pen, out PenAlignment penAlign); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenWidth(HandleRef pen, float width); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenWidth(HandleRef pen, float[] width); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenLineCap197819(HandleRef pen, int startCap, int endCap, int dashCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenStartCap(HandleRef pen, int startCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenEndCap(HandleRef pen, int endCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenStartCap(HandleRef pen, out int startCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenEndCap(HandleRef pen, out int endCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenDashCap197819(HandleRef pen, out int dashCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenDashCap197819(HandleRef pen, int dashCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenLineJoin(HandleRef pen, int lineJoin); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenLineJoin(HandleRef pen, out int lineJoin); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenCustomStartCap(HandleRef pen, HandleRef customCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenCustomStartCap(HandleRef pen, out IntPtr customCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenCustomEndCap(HandleRef pen, HandleRef customCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenCustomEndCap(HandleRef pen, out IntPtr customCap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenMiterLimit(HandleRef pen, float miterLimit); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenMiterLimit(HandleRef pen, float[] miterLimit); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenTransform(HandleRef pen, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenTransform(HandleRef pen, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipResetPenTransform(HandleRef brush); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipMultiplyPenTransform(HandleRef brush, HandleRef matrix, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTranslatePenTransform(HandleRef brush, float dx, float dy, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipScalePenTransform(HandleRef brush, float sx, float sy, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipRotatePenTransform(HandleRef brush, float angle, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenColor(HandleRef pen, int argb); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenColor(HandleRef pen, out int argb); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenBrushFill(HandleRef pen, HandleRef brush); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenBrushFill(HandleRef pen, out IntPtr brush); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenFillType(HandleRef pen, out int pentype); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenDashStyle(HandleRef pen, out int dashstyle); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenDashStyle(HandleRef pen, int dashstyle); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenDashArray(HandleRef pen, HandleRef memorydash, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenDashOffset(HandleRef pen, float[] dashoffset); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenDashOffset(HandleRef pen, float dashoffset); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenDashCount(HandleRef pen, out int dashcount); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenDashArray(HandleRef pen, float[] memorydash, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenCompoundCount(HandleRef pen, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPenCompoundArray(HandleRef pen, float[] array, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPenCompoundArray(HandleRef pen, float[] array, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetWorldTransform(HandleRef graphics, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipResetWorldTransform(HandleRef graphics); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipMultiplyWorldTransform(HandleRef graphics, HandleRef matrix, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTranslateWorldTransform(HandleRef graphics, float dx, float dy, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipScaleWorldTransform(HandleRef graphics, float sx, float sy, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipRotateWorldTransform(HandleRef graphics, float angle, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetWorldTransform(HandleRef graphics, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetCompositingMode(HandleRef graphics, CompositingMode compositingMode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetTextRenderingHint(HandleRef graphics, TextRenderingHint textRenderingHint); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetTextContrast(HandleRef graphics, int textContrast); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetInterpolationMode(HandleRef graphics, InterpolationMode interpolationMode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetCompositingMode(HandleRef graphics, out CompositingMode compositingMode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetRenderingOrigin(HandleRef graphics, int x, int y); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetRenderingOrigin(HandleRef graphics, out int x, out int y); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetCompositingQuality(HandleRef graphics, CompositingQuality quality); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetCompositingQuality(HandleRef graphics, out CompositingQuality quality); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetSmoothingMode(HandleRef graphics, SmoothingMode smoothingMode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetSmoothingMode(HandleRef graphics, out SmoothingMode smoothingMode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPixelOffsetMode(HandleRef graphics, PixelOffsetMode pixelOffsetMode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPixelOffsetMode(HandleRef graphics, out PixelOffsetMode pixelOffsetMode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetTextRenderingHint(HandleRef graphics, out TextRenderingHint textRenderingHint); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetTextContrast(HandleRef graphics, out int textContrast); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetInterpolationMode(HandleRef graphics, out InterpolationMode interpolationMode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPageUnit(HandleRef graphics, out GraphicsUnit unit); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPageScale(HandleRef graphics, out float scale); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPageUnit(HandleRef graphics, GraphicsUnit unit); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPageScale(HandleRef graphics, float scale); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetDpiX(HandleRef graphics, out float dpi); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetDpiY(HandleRef graphics, out float dpi); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateMatrix(out IntPtr matrix); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateMatrix(out IntPtr matrix); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateMatrix2(float m11, float m12, float m21, float m22, float dx, float dy, out IntPtr matrix); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateMatrix2(float m11, float m12, float m21, float m22, float dx, float dy, out IntPtr matrix); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipCreateMatrix3(ref RectangleF rect, PointF* dstplg, out IntPtr matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateMatrix3I(ref Rectangle rect, Point* dstplg, out IntPtr matrix); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCloneMatrix(HandleRef matrix, out IntPtr cloneMatrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeleteMatrix(HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetMatrixElements(HandleRef matrix, float m11, float m12, float m21, float m22, float dx, float dy); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipMultiplyMatrix(HandleRef matrix, HandleRef matrix2, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTranslateMatrix(HandleRef matrix, float offsetX, float offsetY, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipScaleMatrix(HandleRef matrix, float scaleX, float scaleY, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipRotateMatrix(HandleRef matrix, float angle, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipShearMatrix(HandleRef matrix, float shearX, float shearY, MatrixOrder order); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipInvertMatrix(HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTransformMatrixPoints(HandleRef matrix, PointF* pts, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTransformMatrixPointsI(HandleRef matrix, Point* pts, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipVectorTransformMatrixPoints(HandleRef matrix, PointF* pts, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipVectorTransformMatrixPointsI(HandleRef matrix, Point* pts, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern unsafe int GdipGetMatrixElements(HandleRef matrix, float* m); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsMatrixInvertible(HandleRef matrix, out int boolean); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsMatrixIdentity(HandleRef matrix, out int boolean); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsMatrixEqual(HandleRef matrix, HandleRef matrix2, out int boolean); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateRegion(out IntPtr region); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateRegion(out IntPtr region); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName)] internal static extern int GdipCreateRegionRect(ref RectangleF gprectf, out IntPtr region); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateRegionRectI(ref Rectangle gprect, out IntPtr region); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateRegionPath(HandleRef path, out IntPtr region); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateRegionRgnData(byte[] rgndata, int size, out IntPtr region); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateRegionRgnData(byte[] rgndata, int size, out IntPtr region); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateRegionHrgn(IntPtr hRgn, out IntPtr region); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateRegionHrgn(IntPtr hRgn, out IntPtr region); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCloneRegion(HandleRef region, out IntPtr cloneregion); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeleteRegion(HandleRef region); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillRegion(HandleRef graphics, HandleRef brush, HandleRef region); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetInfinite(HandleRef region); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetEmpty(HandleRef region); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCombineRegionRect(HandleRef region, ref RectangleF gprectf, CombineMode mode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCombineRegionRectI(HandleRef region, ref Rectangle gprect, CombineMode mode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCombineRegionPath(HandleRef region, HandleRef path, CombineMode mode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCombineRegionRegion(HandleRef region, HandleRef region2, CombineMode mode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTranslateRegion(HandleRef region, float dx, float dy); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTranslateRegionI(HandleRef region, int dx, int dy); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTransformRegion(HandleRef region, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetRegionBounds(HandleRef region, HandleRef graphics, out RectangleF gprectf); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetRegionHRgn(HandleRef region, HandleRef graphics, out IntPtr hrgn); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsEmptyRegion(HandleRef region, HandleRef graphics, out int boolean); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsInfiniteRegion(HandleRef region, HandleRef graphics, out int boolean); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsEqualRegion(HandleRef region, HandleRef region2, HandleRef graphics, out int boolean); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetRegionDataSize(HandleRef region, out int bufferSize); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetRegionData(HandleRef region, byte[] regionData, int bufferSize, out int sizeFilled); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsVisibleRegionPoint(HandleRef region, float X, float Y, HandleRef graphics, out int boolean); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsVisibleRegionPointI(HandleRef region, int X, int Y, HandleRef graphics, out int boolean); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsVisibleRegionRect(HandleRef region, float X, float Y, float width, float height, HandleRef graphics, out int boolean); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsVisibleRegionRectI(HandleRef region, int X, int Y, int width, int height, HandleRef graphics, out int boolean); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetRegionScansCount(HandleRef region, out int count, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetRegionScans(HandleRef region, RectangleF* rects, out int count, HandleRef matrix); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateFromHDC(IntPtr hdc, out IntPtr graphics); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateFromHDC(IntPtr hdc, out IntPtr graphics); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetClipGraphics(HandleRef graphics, HandleRef srcgraphics, CombineMode mode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetClipRect(HandleRef graphics, float x, float y, float width, float height, CombineMode mode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetClipRectI(HandleRef graphics, int x, int y, int width, int height, CombineMode mode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetClipPath(HandleRef graphics, HandleRef path, CombineMode mode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetClipRegion(HandleRef graphics, HandleRef region, CombineMode mode); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipResetClip(HandleRef graphics); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTranslateClip(HandleRef graphics, float dx, float dy); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetClip(HandleRef graphics, HandleRef region); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetClipBounds(HandleRef graphics, out RectangleF rect); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsClipEmpty(HandleRef graphics, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetVisibleClipBounds(HandleRef graphics, out RectangleF rect); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsVisibleClipEmpty(HandleRef graphics, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsVisiblePoint(HandleRef graphics, float x, float y, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsVisiblePointI(HandleRef graphics, int x, int y, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsVisibleRect(HandleRef graphics, float x, float y, float width, float height, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipIsVisibleRectI(HandleRef graphics, int x, int y, int width, int height, out bool result); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipFlush(HandleRef graphics, FlushIntention intention); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetDC(HandleRef graphics, out IntPtr hdc); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetStringFormatMeasurableCharacterRanges(HandleRef format, int rangeCount, [In] [Out] CharacterRange[] range); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateStringFormat(StringFormatFlags options, int language, out IntPtr format); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateStringFormat(StringFormatFlags options, int language, out IntPtr format); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipStringFormatGetGenericDefault(out IntPtr format); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipStringFormatGetGenericDefault(out IntPtr format); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipStringFormatGetGenericTypographic(out IntPtr format); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipStringFormatGetGenericTypographic(out IntPtr format); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDeleteStringFormat(HandleRef format); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCloneStringFormat(HandleRef format, out IntPtr newFormat); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetStringFormatFlags(HandleRef format, StringFormatFlags options); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetStringFormatFlags(HandleRef format, out StringFormatFlags result); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetStringFormatAlign(HandleRef format, StringAlignment align); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetStringFormatAlign(HandleRef format, out StringAlignment align); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetStringFormatLineAlign(HandleRef format, StringAlignment align); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetStringFormatLineAlign(HandleRef format, out StringAlignment align); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetStringFormatHotkeyPrefix(HandleRef format, HotkeyPrefix hotkeyPrefix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetStringFormatHotkeyPrefix(HandleRef format, out HotkeyPrefix hotkeyPrefix); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetStringFormatTabStops(HandleRef format, float firstTabOffset, int count, float[] tabStops); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetStringFormatTabStops(HandleRef format, int count, out float firstTabOffset, [In] [Out] float[] tabStops); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetStringFormatTabStopCount(HandleRef format, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetStringFormatMeasurableCharacterRangeCount(HandleRef format, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetStringFormatTrimming(HandleRef format, StringTrimming trimming); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetStringFormatTrimming(HandleRef format, out StringTrimming trimming); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetStringFormatDigitSubstitution(HandleRef format, int langID, StringDigitSubstitute sds); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetStringFormatDigitSubstitution(HandleRef format, out int langID, out StringDigitSubstitute sds); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImageDimension(HandleRef image, out float width, out float height); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImageWidth(HandleRef image, out int width); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImageHeight(HandleRef image, out int height); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImageHorizontalResolution(HandleRef image, out float horzRes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImageVerticalResolution(HandleRef image, out float vertRes); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImageFlags(HandleRef image, out int flags); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImageRawFormat(HandleRef image, ref Guid format); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImagePixelFormat(HandleRef image, out PixelFormat format); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipImageGetFrameCount(HandleRef image, ref Guid dimensionID, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipImageSelectActiveFrame(HandleRef image, ref Guid dimensionID, int frameIndex); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipImageRotateFlip(HandleRef image, int rotateFlipType); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetAllPropertyItems(HandleRef image, uint totalBufferSize, uint numProperties, PropertyItemInternal* allItems); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPropertyCount(HandleRef image, out uint numOfProperty); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPropertyIdList(HandleRef image, uint numOfProperty, int* list); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPropertyItem(HandleRef image, int propid, uint propSize, PropertyItemInternal* buffer); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPropertyItemSize(HandleRef image, int propid, out uint size); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetPropertySize(HandleRef image, out uint totalBufferSize, out uint numProperties); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipRemovePropertyItem(HandleRef image, int propid); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSetPropertyItem(HandleRef image, PropertyItemInternal* item); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetImageType(HandleRef image, out int type); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetImageType(IntPtr image, out int type); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipGetImageType(IntPtr image, out int type); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipDisposeImage(HandleRef image); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipDisposeImage(IntPtr image); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipDisposeImage(IntPtr image); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipCreateBitmapFromFile(string filename, out IntPtr bitmap); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipCreateBitmapFromFile(string filename, out IntPtr bitmap); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipCreateBitmapFromFileICM(string filename, out IntPtr bitmap); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipCreateBitmapFromFileICM(string filename, out IntPtr bitmap); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateBitmapFromScan0(int width, int height, int stride, int format, IntPtr scan0, out IntPtr bitmap); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateBitmapFromScan0(int width, int height, int stride, int format, IntPtr scan0, out IntPtr bitmap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateBitmapFromGraphics(int width, int height, HandleRef graphics, out IntPtr bitmap); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateBitmapFromHBITMAP(IntPtr hbitmap, IntPtr hpalette, out IntPtr bitmap); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateBitmapFromHBITMAP(IntPtr hbitmap, IntPtr hpalette, out IntPtr bitmap); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateBitmapFromHICON(IntPtr hicon, out IntPtr bitmap); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateBitmapFromHICON(IntPtr hicon, out IntPtr bitmap); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateBitmapFromResource(IntPtr hresource, IntPtr name, out IntPtr bitmap); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateBitmapFromResource(IntPtr hresource, IntPtr name, out IntPtr bitmap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateHBITMAPFromBitmap(HandleRef nativeBitmap, out IntPtr hbitmap, int argbBackground); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCreateHICONFromBitmap(HandleRef nativeBitmap, out IntPtr hicon); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCloneBitmapArea(float x, float y, float width, float height, int format, HandleRef srcbitmap, out IntPtr dstbitmap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipCloneBitmapAreaI(int x, int y, int width, int height, int format, HandleRef srcbitmap, out IntPtr dstbitmap); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipBitmapLockBits(HandleRef bitmap, ref Rectangle rect, ImageLockMode flags, PixelFormat format, [In] [Out] BitmapData lockedBitmapData); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipBitmapUnlockBits(HandleRef bitmap, BitmapData lockedBitmapData); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipBitmapGetPixel(HandleRef bitmap, int x, int y, out int argb); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipBitmapSetPixel(HandleRef bitmap, int x, int y, int argb); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipBitmapSetResolution(HandleRef bitmap, float dpix, float dpiy); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipImageGetFrameDimensionsCount(HandleRef image, out int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipImageGetFrameDimensionsList(HandleRef image, Guid* dimensionIDs, int count); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipCreateMetafileFromEmf(IntPtr hEnhMetafile, bool deleteEmf, out IntPtr metafile); + [GeneratedDllImport(LibraryName)] + internal static partial int GdipCreateMetafileFromEmf(IntPtr hEnhMetafile, bool deleteEmf, out IntPtr metafile); - [DllImport(LibraryName, ExactSpelling = true)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support non-blittable structs. + [DllImport(LibraryName)] internal static extern int GdipCreateMetafileFromWmf(IntPtr hMetafile, bool deleteWmf, WmfPlaceableFileHeader wmfplacealbeHeader, out IntPtr metafile); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipCreateMetafileFromFile(string file, out IntPtr metafile); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipCreateMetafileFromFile(string file, out IntPtr metafile); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipRecordMetafile(IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipRecordMetafile(IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int GdipRecordMetafile(IntPtr referenceHdc, EmfType emfType, ref RectangleF frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int GdipRecordMetafileI(IntPtr referenceHdc, EmfType emfType, ref Rectangle frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int GdipRecordMetafileFileName(string fileName, IntPtr referenceHdc, EmfType emfType, ref RectangleF frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipRecordMetafileFileName(string fileName, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipRecordMetafileFileName(string fileName, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] +#pragma warning disable DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time + // TODO: [DllImportGenerator] Switch to use GeneratedDllImport once we support blittable structs defined in other assemblies. + [DllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int GdipRecordMetafileFileNameI(string fileName, IntPtr referenceHdc, EmfType emfType, ref Rectangle frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); +#pragma warning restore DLLIMPORTGENANALYZER015 // Use 'GeneratedDllImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipPlayMetafileRecord(HandleRef metafile, EmfPlusRecordType recordType, int flags, int dataSize, byte[] data); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipSaveGraphics(HandleRef graphics, out int state); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawArc(HandleRef graphics, HandleRef pen, float x, float y, float width, float height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawArcI(HandleRef graphics, HandleRef pen, int x, int y, int width, int height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawLinesI(HandleRef graphics, HandleRef pen, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawBezier(HandleRef graphics, HandleRef pen, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawEllipse(HandleRef graphics, HandleRef pen, float x, float y, float width, float height); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawEllipseI(HandleRef graphics, HandleRef pen, int x, int y, int width, int height); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawLine(HandleRef graphics, HandleRef pen, float x1, float y1, float x2, float y2); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawLineI(HandleRef graphics, HandleRef pen, int x1, int y1, int x2, int y2); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawLines(HandleRef graphics, HandleRef pen, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawPath(HandleRef graphics, HandleRef pen, HandleRef path); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawPie(HandleRef graphics, HandleRef pen, float x, float y, float width, float height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawPieI(HandleRef graphics, HandleRef pen, int x, int y, int width, int height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawPolygon(HandleRef graphics, HandleRef pen, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawPolygonI(HandleRef graphics, HandleRef pen, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillEllipse(HandleRef graphics, HandleRef brush, float x, float y, float width, float height); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillEllipseI(HandleRef graphics, HandleRef brush, int x, int y, int width, int height); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillPolygon(HandleRef graphics, HandleRef brush, PointF* points, int count, FillMode brushMode); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillPolygonI(HandleRef graphics, HandleRef brush, Point* points, int count, FillMode brushMode); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillRectangle(HandleRef graphics, HandleRef brush, float x, float y, float width, float height); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillRectangleI(HandleRef graphics, HandleRef brush, int x, int y, int width, int height); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillRectangles(HandleRef graphics, HandleRef brush, RectangleF* rects, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillRectanglesI(HandleRef graphics, HandleRef brush, Rectangle* rects, int count); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] + [DllImport(LibraryName, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern int GdipDrawString(HandleRef graphics, string textString, int length, HandleRef font, ref RectangleF layoutRect, HandleRef stringFormat, HandleRef brush); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawImageRectI(HandleRef graphics, HandleRef image, int x, int y, int width, int height); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGraphicsClear(HandleRef graphics, int argb); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawClosedCurve(HandleRef graphics, HandleRef pen, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawClosedCurveI(HandleRef graphics, HandleRef pen, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawClosedCurve2(HandleRef graphics, HandleRef pen, PointF* points, int count, float tension); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawClosedCurve2I(HandleRef graphics, HandleRef pen, Point* points, int count, float tension); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawCurve(HandleRef graphics, HandleRef pen, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawCurveI(HandleRef graphics, HandleRef pen, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawCurve2(HandleRef graphics, HandleRef pen, PointF* points, int count, float tension); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawCurve2I(HandleRef graphics, HandleRef pen, Point* points, int count, float tension); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawCurve3(HandleRef graphics, HandleRef pen, PointF* points, int count, int offset, int numberOfSegments, float tension); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawCurve3I(HandleRef graphics, HandleRef pen, Point* points, int count, int offset, int numberOfSegments, float tension); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillClosedCurve(HandleRef graphics, HandleRef brush, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillClosedCurveI(HandleRef graphics, HandleRef brush, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillClosedCurve2(HandleRef graphics, HandleRef brush, PointF* points, int count, float tension, FillMode mode); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillClosedCurve2I(HandleRef graphics, HandleRef brush, Point* points, int count, float tension, FillMode mode); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillPie(HandleRef graphics, HandleRef brush, float x, float y, float width, float height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipFillPieI(HandleRef graphics, HandleRef brush, int x, int y, int width, int height, float startAngle, float sweepAngle); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode)] internal static extern int GdipMeasureString(HandleRef graphics, string textString, int length, HandleRef font, ref RectangleF layoutRect, HandleRef stringFormat, ref RectangleF boundingBox, out int codepointsFitted, out int linesFilled); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + [DllImport(LibraryName, CharSet = CharSet.Unicode)] internal static extern int GdipMeasureCharacterRanges(HandleRef graphics, string textString, int length, HandleRef font, ref RectangleF layoutRect, HandleRef stringFormat, int characterCount, [In] [Out] IntPtr[] region); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawImageI(HandleRef graphics, HandleRef image, int x, int y); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawImage(HandleRef graphics, HandleRef image, float x, float y); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawImagePoints(HandleRef graphics, HandleRef image, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawImagePointsI(HandleRef graphics, HandleRef image, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawImageRectRectI(HandleRef graphics, HandleRef image, int dstx, int dsty, int dstwidth, int dstheight, int srcx, int srcy, int srcwidth, int srcheight, GraphicsUnit srcunit, HandleRef imageAttributes, Graphics.DrawImageAbort? callback, HandleRef callbackdata); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawImagePointsRect(HandleRef graphics, HandleRef image, PointF* points, int count, float srcx, float srcy, float srcwidth, float srcheight, GraphicsUnit srcunit, HandleRef imageAttributes, Graphics.DrawImageAbort? callback, HandleRef callbackdata); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawImageRectRect(HandleRef graphics, HandleRef image, float dstx, float dsty, float dstwidth, float dstheight, float srcx, float srcy, float srcwidth, float srcheight, GraphicsUnit srcunit, HandleRef imageAttributes, Graphics.DrawImageAbort? callback, HandleRef callbackdata); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawImagePointsRectI(HandleRef graphics, HandleRef image, Point* points, int count, int srcx, int srcy, int srcwidth, int srcheight, GraphicsUnit srcunit, HandleRef imageAttributes, Graphics.DrawImageAbort? callback, HandleRef callbackdata); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawImageRect(HandleRef graphics, HandleRef image, float x, float y, float width, float height); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawImagePointRect(HandleRef graphics, HandleRef image, float x, float y, float srcx, float srcy, float srcwidth, float srcheight, int srcunit); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawImagePointRectI(HandleRef graphics, HandleRef image, int x, int y, int srcx, int srcy, int srcwidth, int srcheight, int srcunit); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawRectangle(HandleRef graphics, HandleRef pen, float x, float y, float width, float height); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawRectangleI(HandleRef graphics, HandleRef pen, int x, int y, int width, int height); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawRectangles(HandleRef graphics, HandleRef pen, RectangleF* rects, int count); - [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + [DllImport(LibraryName, SetLastError = true)] internal static extern int GdipDrawRectanglesI(HandleRef graphics, HandleRef pen, Rectangle* rects, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTransformPoints(HandleRef graphics, int destSpace, int srcSpace, PointF* points, int count); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipTransformPointsI(HandleRef graphics, int destSpace, int srcSpace, Point* points, int count); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipLoadImageFromFileICM(string filename, out IntPtr image); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipLoadImageFromFileICM(string filename, out IntPtr image); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] - internal static extern int GdipLoadImageFromFile(string filename, out IntPtr image); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static partial int GdipLoadImageFromFile(string filename, out IntPtr image); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetEncoderParameterListSize(HandleRef image, ref Guid encoder, out int size); - [DllImport(LibraryName, ExactSpelling = true)] + [DllImport(LibraryName)] internal static extern int GdipGetEncoderParameterList(HandleRef image, ref Guid encoder, int size, IntPtr buffer); } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs index 3d9ad1a3566765..0f77c8bdb55ba7 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.COMWrappers.cs @@ -61,8 +61,8 @@ public unsafe void Save(Stream outputStream) } } - [DllImport(Interop.Libraries.Oleaut32)] - private static unsafe extern int OleCreatePictureIndirect(PICTDESC* pictdesc, Guid* refiid, int fOwn, IntPtr* lplpvObj); + [GeneratedDllImport(Interop.Libraries.Oleaut32)] + private static unsafe partial int OleCreatePictureIndirect(PICTDESC* pictdesc, Guid* refiid, int fOwn, IntPtr* lplpvObj); [StructLayout(LayoutKind.Sequential)] private readonly struct PICTDESC diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/LibX11Functions.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/LibX11Functions.cs index daafc9a7f6806c..0727247b27c55c 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/LibX11Functions.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/LibX11Functions.cs @@ -7,44 +7,44 @@ namespace System.Drawing { - internal static class LibX11Functions + internal static partial class LibX11Functions { // Some special X11 stuff - [DllImport("libX11", EntryPoint = "XOpenDisplay")] - internal static extern IntPtr XOpenDisplay(IntPtr display); + [GeneratedDllImport("libX11", EntryPoint = "XOpenDisplay")] + internal static partial IntPtr XOpenDisplay(IntPtr display); - [DllImport("libX11", EntryPoint = "XCloseDisplay")] - internal static extern int XCloseDisplay(IntPtr display); + [GeneratedDllImport("libX11", EntryPoint = "XCloseDisplay")] + internal static partial int XCloseDisplay(IntPtr display); - [DllImport("libX11", EntryPoint = "XRootWindow")] - internal static extern IntPtr XRootWindow(IntPtr display, int screen); + [GeneratedDllImport("libX11", EntryPoint = "XRootWindow")] + internal static partial IntPtr XRootWindow(IntPtr display, int screen); - [DllImport("libX11", EntryPoint = "XDefaultScreen")] - internal static extern int XDefaultScreen(IntPtr display); + [GeneratedDllImport("libX11", EntryPoint = "XDefaultScreen")] + internal static partial int XDefaultScreen(IntPtr display); - [DllImport("libX11", EntryPoint = "XDefaultDepth")] - internal static extern uint XDefaultDepth(IntPtr display, int screen); + [GeneratedDllImport("libX11", EntryPoint = "XDefaultDepth")] + internal static partial uint XDefaultDepth(IntPtr display, int screen); - [DllImport("libX11", EntryPoint = "XGetImage")] - internal static extern IntPtr XGetImage(IntPtr display, IntPtr drawable, int src_x, int src_y, int width, int height, int pane, int format); + [GeneratedDllImport("libX11", EntryPoint = "XGetImage")] + internal static partial IntPtr XGetImage(IntPtr display, IntPtr drawable, int src_x, int src_y, int width, int height, int pane, int format); - [DllImport("libX11", EntryPoint = "XGetPixel")] - internal static extern int XGetPixel(IntPtr image, int x, int y); + [GeneratedDllImport("libX11", EntryPoint = "XGetPixel")] + internal static partial int XGetPixel(IntPtr image, int x, int y); - [DllImport("libX11", EntryPoint = "XDestroyImage")] - internal static extern int XDestroyImage(IntPtr image); + [GeneratedDllImport("libX11", EntryPoint = "XDestroyImage")] + internal static partial int XDestroyImage(IntPtr image); - [DllImport("libX11", EntryPoint = "XDefaultVisual")] - internal static extern IntPtr XDefaultVisual(IntPtr display, int screen); + [GeneratedDllImport("libX11", EntryPoint = "XDefaultVisual")] + internal static partial IntPtr XDefaultVisual(IntPtr display, int screen); - [DllImport("libX11", EntryPoint = "XGetVisualInfo")] - internal static extern IntPtr XGetVisualInfo(IntPtr display, int vinfo_mask, ref XVisualInfo vinfo_template, ref int nitems); + [GeneratedDllImport("libX11", EntryPoint = "XGetVisualInfo")] + internal static partial IntPtr XGetVisualInfo(IntPtr display, int vinfo_mask, ref XVisualInfo vinfo_template, ref int nitems); - [DllImport("libX11", EntryPoint = "XVisualIDFromVisual")] - internal static extern IntPtr XVisualIDFromVisual(IntPtr visual); + [GeneratedDllImport("libX11", EntryPoint = "XVisualIDFromVisual")] + internal static partial IntPtr XVisualIDFromVisual(IntPtr visual); - [DllImport("libX11", EntryPoint = "XFree")] - internal static extern void XFree(IntPtr data); + [GeneratedDllImport("libX11", EntryPoint = "XFree")] + internal static partial void XFree(IntPtr data); } [StructLayout(LayoutKind.Sequential)] diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/LibcupsNative.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/LibcupsNative.cs index cc3e39f8a73092..3a8501cd0e1865 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/LibcupsNative.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/LibcupsNative.cs @@ -6,7 +6,7 @@ namespace System.Drawing.Printing { - internal static class LibcupsNative + internal static partial class LibcupsNative { internal const string LibraryName = "libcups"; @@ -26,39 +26,39 @@ internal static IntPtr LoadLibcups() return lib; } - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int cupsGetDests(ref IntPtr dests); + [GeneratedDllImport(LibraryName)] + internal static partial int cupsGetDests(ref IntPtr dests); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern void cupsFreeDests(int num_dests, IntPtr dests); + [GeneratedDllImport(LibraryName)] + internal static partial void cupsFreeDests(int num_dests, IntPtr dests); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Ansi)] + [DllImport(LibraryName, CharSet = CharSet.Ansi)] #pragma warning disable CA1838 // not hot-path enough to worry about the overheads of StringBuilder marshaling internal static extern IntPtr cupsTempFd([Out] StringBuilder sb, int len); #pragma warning restore CA1838 - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern IntPtr cupsGetDefault(); + [GeneratedDllImport(LibraryName)] + internal static partial IntPtr cupsGetDefault(); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Ansi)] - internal static extern int cupsPrintFile(string printer, string filename, string title, int num_options, IntPtr options); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Ansi)] + internal static partial int cupsPrintFile(string printer, string filename, string title, int num_options, IntPtr options); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Ansi)] - internal static extern IntPtr cupsGetPPD(string printer); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Ansi)] + internal static partial IntPtr cupsGetPPD(string printer); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Ansi)] - internal static extern IntPtr ppdOpenFile(string filename); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Ansi)] + internal static partial IntPtr ppdOpenFile(string filename); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Ansi)] - internal static extern IntPtr ppdFindOption(IntPtr ppd_file, string keyword); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Ansi)] + internal static partial IntPtr ppdFindOption(IntPtr ppd_file, string keyword); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Ansi)] - internal static extern void ppdClose(IntPtr ppd); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Ansi)] + internal static partial void ppdClose(IntPtr ppd); - [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Ansi)] - internal static extern int cupsParseOptions(string arg, int number_of_options, ref IntPtr options); + [GeneratedDllImport(LibraryName, CharSet = CharSet.Ansi)] + internal static partial int cupsParseOptions(string arg, int number_of_options, ref IntPtr options); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern void cupsFreeOptions(int number_options, IntPtr options); + [GeneratedDllImport(LibraryName)] + internal static partial void cupsFreeOptions(int number_options, IntPtr options); } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/macFunctions.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/macFunctions.cs index ddc7fef94d976a..b8483c9fa53ef8 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/macFunctions.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/macFunctions.cs @@ -34,7 +34,7 @@ namespace System.Drawing { - internal static class MacSupport + internal static partial class MacSupport { internal static readonly Hashtable contextReference = new Hashtable(); internal static readonly object lockobj = new object(); @@ -151,84 +151,84 @@ internal static void ReleaseContext(IntPtr port, IntPtr context) } #region Cocoa Methods - [DllImport("libobjc.dylib")] - public static extern IntPtr objc_getClass(string className); - [DllImport("libobjc.dylib", EntryPoint = "objc_msgSend")] - public static extern IntPtr intptr_objc_msgSend(IntPtr basePtr, IntPtr selector); - [DllImport("libobjc.dylib", EntryPoint = "objc_msgSend_stret")] - public static extern void Rect_objc_msgSend_stret(out Rect arect, IntPtr basePtr, IntPtr selector); - [DllImport("libobjc.dylib", EntryPoint = "objc_msgSend")] + [GeneratedDllImport("libobjc.dylib", CharSet = CharSet.Ansi)] + public static partial IntPtr objc_getClass(string className); + [GeneratedDllImport("libobjc.dylib", EntryPoint = "objc_msgSend")] + public static partial IntPtr intptr_objc_msgSend(IntPtr basePtr, IntPtr selector); + [GeneratedDllImport("libobjc.dylib", EntryPoint = "objc_msgSend_stret")] + public static partial void Rect_objc_msgSend_stret(out Rect arect, IntPtr basePtr, IntPtr selector); + [GeneratedDllImport("libobjc.dylib", EntryPoint = "objc_msgSend")] [return:MarshalAs(UnmanagedType.U1)] - public static extern bool bool_objc_msgSend(IntPtr handle, IntPtr selector); - [DllImport("libobjc.dylib")] - public static extern IntPtr sel_registerName(string selectorName); + public static partial bool bool_objc_msgSend(IntPtr handle, IntPtr selector); + [GeneratedDllImport("libobjc.dylib", CharSet = CharSet.Ansi)] + public static partial IntPtr sel_registerName(string selectorName); #endregion - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern IntPtr CGMainDisplayID(); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern Rect CGDisplayBounds(IntPtr display); - - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern int HIViewGetBounds(IntPtr vHnd, ref Rect r); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern int HIViewConvertRect(ref Rect r, IntPtr a, IntPtr b); - - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern IntPtr GetControlOwner(IntPtr aView); - - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern int GetWindowBounds(IntPtr wHnd, uint reg, ref QDRect rect); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern IntPtr GetWindowPort(IntPtr hWnd); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern IntPtr GetQDGlobalsThePort(); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CreateCGContextForPort(IntPtr port, ref IntPtr context); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CFRelease(IntPtr context); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void QDBeginCGContext(IntPtr port, ref IntPtr context); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void QDEndCGContext(IntPtr port, ref IntPtr context); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern int CGContextClipToRect(IntPtr context, Rect clip); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern int CGContextClipToRects(IntPtr context, Rect[] clip_rects, int count); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextTranslateCTM(IntPtr context, float tx, float ty); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextScaleCTM(IntPtr context, float x, float y); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextFlush(IntPtr context); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextSynchronize(IntPtr context); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern IntPtr CGPathCreateMutable(); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGPathAddRects(IntPtr path, IntPtr _void, Rect[] rects, int count); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGPathAddRect(IntPtr path, IntPtr _void, Rect rect); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextAddRects(IntPtr context, Rect[] rects, int count); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextAddRect(IntPtr context, Rect rect); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextBeginPath(IntPtr context); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextClosePath(IntPtr context); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextAddPath(IntPtr context, IntPtr path); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextClip(IntPtr context); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextEOClip(IntPtr context); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextEOFillPath(IntPtr context); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextSaveGState(IntPtr context); - [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] - internal static extern void CGContextRestoreGState(IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial IntPtr CGMainDisplayID(); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial Rect CGDisplayBounds(IntPtr display); + + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial int HIViewGetBounds(IntPtr vHnd, ref Rect r); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial int HIViewConvertRect(ref Rect r, IntPtr a, IntPtr b); + + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial IntPtr GetControlOwner(IntPtr aView); + + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial int GetWindowBounds(IntPtr wHnd, uint reg, ref QDRect rect); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial IntPtr GetWindowPort(IntPtr hWnd); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial IntPtr GetQDGlobalsThePort(); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CreateCGContextForPort(IntPtr port, ref IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CFRelease(IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void QDBeginCGContext(IntPtr port, ref IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void QDEndCGContext(IntPtr port, ref IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial int CGContextClipToRect(IntPtr context, Rect clip); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial int CGContextClipToRects(IntPtr context, Rect[] clip_rects, int count); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextTranslateCTM(IntPtr context, float tx, float ty); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextScaleCTM(IntPtr context, float x, float y); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextFlush(IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextSynchronize(IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial IntPtr CGPathCreateMutable(); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGPathAddRects(IntPtr path, IntPtr _void, Rect[] rects, int count); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGPathAddRect(IntPtr path, IntPtr _void, Rect rect); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextAddRects(IntPtr context, Rect[] rects, int count); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextAddRect(IntPtr context, Rect rect); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextBeginPath(IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextClosePath(IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextAddPath(IntPtr context, IntPtr path); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextClip(IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextEOClip(IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextEOFillPath(IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextSaveGState(IntPtr context); + [GeneratedDllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static partial void CGContextRestoreGState(IntPtr context); } internal struct CGSize diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.delegateEvent.delegate.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.delegateEvent.delegate.cs index 2ef4efef62c4cb..ca36e304db3ce3 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.delegateEvent.delegate.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.delegateEvent.delegate.cs @@ -130,7 +130,7 @@ public void M0D(ref int n, object[] v1, params object[] v2) } } - public class start + public class Start { private static int s_retval = (int)((1 + 0x0D) * 0x0D) / 2; // field @@ -364,7 +364,7 @@ public string M04(ManagedTests.DynamicCSharp.Conformance.dynamic.context.dlgateE } } - public class start + public class Start { // field private static ManagedTests.DynamicCSharp.Conformance.dynamic.context.dlgateEvent.dlgate.dlgatedeclarelib02.dlgatedeclarelib02.DynNamespace01.DynClass.D001[] s_d001 = new ManagedTests.DynamicCSharp.Conformance.dynamic.context.dlgateEvent.dlgate.dlgatedeclarelib02.dlgatedeclarelib02.DynNamespace01.DynClass.D001[3]; @@ -582,7 +582,7 @@ internal int M24(object v1, decimal v2) } } - public struct start + public struct Start { // field private static DynClassDrived.InternalDel s_interDel; @@ -803,11 +803,11 @@ public class TestClass [Fact] public void RunTest() { - start.DynamicCSharpRunTest(); + Start.DynamicCSharpRunTest(); } } - public struct start + public struct Start { // field public static void DynamicCSharpRunTest() @@ -960,7 +960,7 @@ internal static void M04(dynamic v1, out dynamic v2) } } - public class start + public class Start { public static void DynamicCSharpRunTest() @@ -1040,7 +1040,7 @@ public static void SMinClass(dynamic v1) } } - public class start + public class Start { [Fact] public static void DynamicCSharpRunTest() @@ -1111,11 +1111,11 @@ public class TestClass [Fact] public void RunTest() { - start.DynamicCSharpRunTest(); + Start.DynamicCSharpRunTest(); } } - public struct start + public struct Start { public static void DynamicCSharpRunTest() { diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs index 79d677b3c2abf3..75c76c34c69615 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs @@ -774,7 +774,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.context.indexer.genclas public class Test { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55118", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55118", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void TryCatchFinally() { dynamic dy = new MemberClass(); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.operator.compound.basic.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.operator.compound.basic.cs index 381b89b819864a..bed0c85fa2cf30 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.operator.compound.basic.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.operator.compound.basic.cs @@ -239,12 +239,11 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.context.operate.compoun // // // - using System; public class Test { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.backwardscompatible.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.backwardscompatible.cs index 9c0d75b4aa91d1..7cc2b6b8658366 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.backwardscompatible.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.backwardscompatible.cs @@ -11,7 +11,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { } @@ -104,7 +104,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { public int i = 0; } @@ -147,7 +147,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { public dynamic(int i) { @@ -240,7 +240,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { public dynamic(int i) { @@ -315,7 +315,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { } @@ -353,7 +353,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public struct dynamic + public struct @dynamic { } @@ -388,7 +388,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { } @@ -463,7 +463,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { public T GetT() { @@ -471,7 +471,7 @@ public T GetT() } } - public class dynamic + public class @dynamic { public T GetT() { @@ -526,7 +526,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { } @@ -561,7 +561,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { } @@ -603,7 +603,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { } @@ -640,7 +640,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { } @@ -690,7 +690,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class G + public class G<@dynamic> where dynamic : new() { public dynamic V = new dynamic(); @@ -698,7 +698,7 @@ public class G public class C { - public dynamic M() where dynamic : new() + public dynamic M<@dynamic>() where dynamic : new() { return new dynamic(); } @@ -744,11 +744,11 @@ public static int MainMethod() namespace NS { - public class dynamic + public class @dynamic { } - public class G2 + public class G2<@dynamic> where dynamic : new() { public dynamic V = new dynamic(); @@ -756,7 +756,7 @@ public class G2 public class C2 { - public dynamic M() where dynamic : new() + public dynamic M<@dynamic>() where dynamic : new() { return new dynamic(); } @@ -774,11 +774,11 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { } - public class G + public class G<@dynamic> where dynamic : new() { public dynamic V = new dynamic(); @@ -786,7 +786,7 @@ public class G public class C { - public dynamic M() where dynamic : new() + public dynamic M<@dynamic>() where dynamic : new() { return new dynamic(); } @@ -829,7 +829,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { } @@ -902,7 +902,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - internal enum dynamic + internal enum @dynamic { foo, bar @@ -939,7 +939,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // // // - public class dynamic + public class @dynamic { } @@ -1003,7 +1003,7 @@ public static int MainMethod() } private event dynamic MyEvent; - private delegate void dynamic(int i); + private delegate void @dynamic(int i); } } @@ -1013,7 +1013,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc { public class Test { - public class dynamic + public class @dynamic { public int Foo() { @@ -1048,7 +1048,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc // public class App { - public class dynamic + public class @dynamic { public int Foo() { @@ -1086,7 +1086,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.backwardsc { public class Test { - public class dynamic + public class @dynamic { public int Foo() { diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.formalParameter.Methods.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.formalParameter.Methods.cs index 6306863d44ea3b..53af8ca88f35b9 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.formalParameter.Methods.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.formalParameter.Methods.cs @@ -98,8 +98,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.extensionmethod006.extensionmethod006 { - using System; - static // Extension method that extends dynamic // // @@ -117,7 +115,7 @@ public static string Foo(this object x, dynamic d) public class Test { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -192,8 +190,6 @@ public static int Method(this Test t, int value) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method001.method001 { - using System; - public class Test { private static bool s_ok = false; @@ -208,7 +204,7 @@ public void Foo(dynamic d) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -266,8 +262,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method003.method003 { - using System; - public class Test { private static bool s_ok = false; @@ -281,7 +275,7 @@ public void Foo(dynamic d) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -303,8 +297,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method004.method004 { - using System; - public class Test { private static bool s_ok = false; @@ -317,7 +309,7 @@ public void Foo(ref dynamic d) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -342,8 +334,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method005.method005 { - using System; - public class Test { private static bool s_ok = false; @@ -359,7 +349,7 @@ public void Foo(params dynamic[] d) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -382,8 +372,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method007.method007 { - using System; - public class Test { private static bool s_ok = false; @@ -397,7 +385,7 @@ public void Foo(dynamic d, dynamic d2) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -421,8 +409,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method008.method008 { - using System; - public class Test { private static bool s_ok = false; @@ -436,7 +422,7 @@ public void Foo(dynamic d, int d2) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -459,8 +445,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method009.method009 { - using System; - public class Test { private static bool s_ok = false; @@ -474,7 +458,7 @@ public void Foo(dynamic d, dynamic d2, dynamic d3) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -497,8 +481,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method010.method010 { - using System; - public class Test { private static bool s_ok = false; @@ -514,7 +496,7 @@ public MyClass(params dynamic[] d) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -536,8 +518,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method011.method011 { - using System; - public class Test { private static bool s_ok = false; @@ -552,7 +532,7 @@ public static void Foo(dynamic d) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -573,8 +553,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method012.method012 { - using System; - public class Test { private static bool s_ok = false; @@ -587,7 +565,7 @@ public void Foo(ref dynamic d) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -612,8 +590,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.formalParameter.Methods.method013.method013 { - using System; - public class Test { private static bool s_ok = false; @@ -626,7 +602,7 @@ public void Foo(T d) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); @@ -1130,7 +1106,7 @@ public MyClass1() } } - public class MyClass2 : I1, I3 where U : class + public class MyClass2 : I1, I3 where U : class { } diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.localVariable.blockVariable.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.localVariable.blockVariable.cs index 46c8372b14a318..515c804132a09e 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.localVariable.blockVariable.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Declarations/Conformance.dynamic.declarations.localVariable.blockVariable.cs @@ -1189,12 +1189,10 @@ from d in numbers namespace ManagedTests.DynamicCSharp.Conformance.dynamic.declarations.localVariable.blockVariable.trycatch002.trycatch002 { - using System; - public class Test { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs index cb63fac7995399..204dffb0b73f91 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs @@ -4356,8 +4356,6 @@ public static int MainMethod() namespace ManagedTests.DynamicCSharp.Conformance.dynamic.dynamicType.conversions.dlgate003.dlgate003 { - using System; - // Delegate conversions // // Tests to figure out if the right conversion from method groups to delegates are applied @@ -4377,7 +4375,7 @@ public static object Foo() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod()); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.NamedAndOptional/Conformance.dynamic.namedandoptional.usage.basic.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.NamedAndOptional/Conformance.dynamic.namedandoptional.usage.basic.cs index a76ac8737bdf9a..dbb20a8dd06ed0 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.NamedAndOptional/Conformance.dynamic.namedandoptional.usage.basic.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.NamedAndOptional/Conformance.dynamic.namedandoptional.usage.basic.cs @@ -3708,7 +3708,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.namedandoptional.usage. // // // - public class test + public class Test { [Fact] public static void DynamicCSharpRunTest() @@ -3719,8 +3719,8 @@ public static void DynamicCSharpRunTest() public static int MainMethod() { int tests = 0, success = 0; - var s = new test(); - dynamic d = new test(); + var s = new Test(); + dynamic d = new Test(); tests++; if (s.Foo(x: 1, y: "") == 2) success++; //this should compile @@ -3760,7 +3760,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.namedandoptional.usage. // // // - public class test + public class Test { [Fact] public static void DynamicCSharpRunTest() @@ -3771,8 +3771,8 @@ public static void DynamicCSharpRunTest() public static int MainMethod() { int tests = 0, success = 0; - var s = new test(); - dynamic d = new test(); + var s = new Test(); + dynamic d = new Test(); tests++; if (s.Foo(x: 1, y: "") == 2) success++; //this should compile @@ -3812,7 +3812,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.namedandoptional.usage. // // // - public class test + public class Test { [Fact] public static void DynamicCSharpRunTest() @@ -3823,8 +3823,8 @@ public static void DynamicCSharpRunTest() public static int MainMethod() { int tests = 0, success = 0; - var s = new test(); - dynamic d = new test(); + var s = new Test(); + dynamic d = new Test(); tests++; if (s.Foo(x: 1, y: "") == 1) success++; //this should compile @@ -3864,7 +3864,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.namedandoptional.usage. // // // - public class test + public class Test { [Fact] public static void DynamicCSharpRunTest() @@ -3875,8 +3875,8 @@ public static void DynamicCSharpRunTest() public static int MainMethod() { int tests = 0, success = 0; - var s = new test(); - dynamic d = new test(); + var s = new Test(); + dynamic d = new Test(); tests++; if (s.Foo(x: 1, y: null) == 2) success++; //this should compile diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.NamedAndOptional/Conformance.dynamic.namedandoptional.usage.other.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.NamedAndOptional/Conformance.dynamic.namedandoptional.usage.other.cs index ebde9aa08fc35a..983c44015b4df5 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.NamedAndOptional/Conformance.dynamic.namedandoptional.usage.other.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.NamedAndOptional/Conformance.dynamic.namedandoptional.usage.other.cs @@ -2902,7 +2902,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.namedandoptional.usage. // // // - public class test + public class Test { [Fact] public static void DynamicCSharpRunTest() @@ -2913,8 +2913,8 @@ public static void DynamicCSharpRunTest() public static int MainMethod() { int tests = 0, success = 0; - var s = new test(); - dynamic d = new test(); + var s = new Test(); + dynamic d = new Test(); //converting null to string tests++; if (s.Foo(x: 1, y: null) == 2) diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Indexers.1class2indexers.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Indexers.1class2indexers.cs index 5b08d4e447ed67..ad756222006959 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Indexers.1class2indexers.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Indexers.1class2indexers.cs @@ -665,8 +665,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.overloadResolution.Indexers.Oneclass2indexers.onedynamicparam004.onedynamicparam004 { - using System; - // Tests overload resolution for 1 class and 2 methods // // @@ -722,7 +720,7 @@ public object this[object x] public class Test { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs index 72f5998f65690c..4e35225bb317bf 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs @@ -501,8 +501,6 @@ public static int MainMethod(string[] args) namespace ManagedTests.DynamicCSharp.Conformance.dynamic.overloadResolution.Methods.Oneclass2methods.onedynamicparam004.onedynamicparam004 { - using System; - // Tests overload resolution for 1 class and 2 methods // // @@ -532,7 +530,7 @@ public void Method(object x) public class Test { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55117", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod(null)); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Statements/Conformance.dynamic.statements.foreach.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Statements/Conformance.dynamic.statements.foreach.cs index df9b9265680c42..91698ad38b0145 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Statements/Conformance.dynamic.statements.foreach.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Statements/Conformance.dynamic.statements.foreach.cs @@ -716,14 +716,14 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.statements.freach.freac // // - public enum color + public enum Color { Red, Blue, Green } - public enum cars + public enum Cars { Toyota, Lexus, @@ -814,42 +814,42 @@ public static int MainMethod() } ; - color[] x13 = new color[] + Color[] x13 = new Color[] { - color.Red, color.Green + Color.Red, Color.Green } ; - cars[] x14 = new cars[] + Cars[] x14 = new Cars[] { - cars.Toyota, cars.BMW + Cars.Toyota, Cars.BMW } ; // From sybte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal to enum-type - foreach (color y in (dynamic)x1) + foreach (Color y in (dynamic)x1) i++; - foreach (color y in (dynamic)x2) + foreach (Color y in (dynamic)x2) i++; - foreach (color y in (dynamic)x3) + foreach (Color y in (dynamic)x3) i++; - foreach (color y in (dynamic)x4) + foreach (Color y in (dynamic)x4) i++; - foreach (color y in (dynamic)x5) + foreach (Color y in (dynamic)x5) i++; - foreach (color y in (dynamic)x6) + foreach (Color y in (dynamic)x6) i++; - foreach (color y in (dynamic)x7) + foreach (Color y in (dynamic)x7) i++; - foreach (color y in (dynamic)x8) + foreach (Color y in (dynamic)x8) i++; - foreach (color y in (dynamic)x9) + foreach (Color y in (dynamic)x9) i++; - foreach (color y in (dynamic)x10) + foreach (Color y in (dynamic)x10) i++; - foreach (color y in (dynamic)x11) + foreach (Color y in (dynamic)x11) i++; - foreach (color y in (dynamic)x12) + foreach (Color y in (dynamic)x12) i++; // From enum type to sybte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal foreach (sbyte y in (dynamic)x13) @@ -877,7 +877,7 @@ public static int MainMethod() foreach (decimal y in (dynamic)x13) i++; // From one enum type to another enum type - foreach (color y in (dynamic)x14) + foreach (Color y in (dynamic)x14) i++; return 0; } diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Statements/Conformance.dynamic.statements.unaryOperators.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Statements/Conformance.dynamic.statements.unaryOperators.cs index 37e7fd9ef28517..6b32d5794de2d3 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Statements/Conformance.dynamic.statements.unaryOperators.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Statements/Conformance.dynamic.statements.unaryOperators.cs @@ -251,7 +251,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.statements.unaryOperato // // - public class temp + public class Temp { } @@ -265,7 +265,7 @@ public static void DynamicCSharpRunTest() public static int MainMethod(string[] args) { - temp t = new temp(); + Temp t = new Temp(); dynamic d = t as dynamic; try { @@ -399,7 +399,7 @@ namespace ManagedTests.DynamicCSharp.Conformance.dynamic.statements.unaryOperato // //\(26,18\).*CS1981 - public class temp + public class Temp { public dynamic MyTest() { @@ -418,7 +418,7 @@ public static void DynamicCSharpRunTest() public static int MainMethod(string[] args) { - dynamic d = new temp(); + dynamic d = new Temp(); bool b = d.MyTest() is dynamic; if (b != true) return 1; diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.assign.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.assign.cs index d9dd1adb35618c..6df91ff1e74dd5 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.assign.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.assign.cs @@ -476,8 +476,6 @@ public static int MainMethod() namespace ManagedTests.DynamicCSharp.Conformance.dynamic.Variance.assign.assignment07.assignment07 { - using System; - // variance // assignment Contravariant delegates // contravariance on delegates assigned to arrays @@ -501,7 +499,7 @@ public class C private static dynamic s_array3; [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55119", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55119", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod()); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.complex.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.complex.cs index 4dd83d81692946..ecb8922eca3673 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.complex.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Variance/Conformance.dynamic.Variance.complex.cs @@ -19,12 +19,12 @@ public class Tiger : Animal { } - public interface imeta + public interface IMeta { int foo(IAction a); } - public class Meta : imeta where T : new() + public class Meta : IMeta where T : new() { public int foo(IAction a) { @@ -59,7 +59,7 @@ public static int MainMethod() dynamic v2 = (IAction)v1; var x = v2.boo(new Tiger()); dynamic m1 = new Meta(); - dynamic m2 = (imeta)m1; + dynamic m2 = (IMeta)m1; var y = m2.foo(v1); return 0; } diff --git a/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Unix.cs b/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Unix.cs index 2cc10837f5ffc6..b743ddd77efc9b 100644 --- a/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Unix.cs +++ b/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Unix.cs @@ -140,7 +140,7 @@ public void UnixExtractFilePermissionsCompat(string zipName, string expectedPerm } [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)] + [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser & ~TestPlatforms.tvOS & ~TestPlatforms.iOS)] public async Task CanZipNamedPipe() { string destPath = Path.Combine(TestDirectory, "dest.zip"); @@ -155,7 +155,7 @@ public async Task CanZipNamedPipe() await Task.WhenAll( Task.Run(() => { - using FileStream fs = new (fifoPath, FileMode.Open, FileAccess.Write, FileShare.Read); + using FileStream fs = new (fifoPath, FileMode.Open, FileAccess.Write, FileShare.Read, bufferSize: 0); foreach (byte content in contentBytes) { fs.WriteByte(content); diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/Crc32Helper.ZLib.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/Crc32Helper.ZLib.cs index 40cd3a07e0a2f1..6b0344688157f9 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/Crc32Helper.ZLib.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/Crc32Helper.ZLib.cs @@ -14,7 +14,7 @@ public static unsafe uint UpdateCrc32(uint crc32, byte[] buffer, int offset, int Debug.Assert((buffer != null) && (offset >= 0) && (length >= 0) && (offset <= buffer.Length - length)); fixed (byte* bufferPtr = &buffer[offset]) { - return Interop.zlib.crc32(crc32, bufferPtr, length); + return Interop.ZLib.crc32(crc32, bufferPtr, length); } } @@ -22,7 +22,7 @@ public static unsafe uint UpdateCrc32(uint crc32, ReadOnlySpan buffer) { fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) { - return Interop.zlib.crc32(crc32, bufferPtr, buffer.Length); + return Interop.ZLib.crc32(crc32, bufferPtr, buffer.Length); } } } diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs index 6f40b8f4d364f9..e662526ba64909 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs @@ -58,7 +58,7 @@ public void FileSystemWatcher_Directory_Changed_Nested(bool includeSubdirectorie } } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void FileSystemWatcher_Directory_Changed_SymLink() { using (var testDirectory = new TempDirectory(GetTestFilePath())) @@ -70,7 +70,7 @@ public void FileSystemWatcher_Directory_Changed_SymLink() // Setup the watcher watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size; watcher.IncludeSubdirectories = true; - Assert.True(CreateSymLink(tempDir.Path, Path.Combine(dir.Path, "link"), true)); + Assert.True(MountHelper.CreateSymbolicLink(Path.Combine(dir.Path, GetRandomLinkName()), tempDir.Path, true)); Action action = () => File.AppendAllText(file.Path, "longtext"); Action cleanup = () => File.AppendAllText(file.Path, "short"); diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Create.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Create.cs index c375f3f038c32d..f5e84517f3e4c3 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Create.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Create.cs @@ -86,7 +86,7 @@ public void FileSystemWatcher_Directory_Create_DeepDirectoryStructure() } } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void FileSystemWatcher_Directory_Create_SymLink() { using (var testDirectory = new TempDirectory(GetTestFilePath())) @@ -95,8 +95,8 @@ public void FileSystemWatcher_Directory_Create_SymLink() using (var watcher = new FileSystemWatcher(Path.GetFullPath(dir.Path), "*")) { // Make the symlink in our path (to the temp folder) and make sure an event is raised - string symLinkPath = Path.Combine(dir.Path, Path.GetFileName(temp.Path)); - Action action = () => Assert.True(CreateSymLink(temp.Path, symLinkPath, true)); + string symLinkPath = Path.Combine(dir.Path, GetRandomLinkName()); + Action action = () => Assert.True(MountHelper.CreateSymbolicLink(symLinkPath, temp.Path, true)); Action cleanup = () => Directory.Delete(symLinkPath); ExpectEvent(watcher, WatcherChangeTypes.Created, action, cleanup, symLinkPath); diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Delete.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Delete.cs index 5e5f5ed4a0e64b..0e42e9968b4fed 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Delete.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Delete.cs @@ -68,7 +68,7 @@ public void FileSystemWatcher_Directory_Delete_DeepDirectoryStructure() } } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void FileSystemWatcher_Directory_Delete_SymLink() { using (var testDirectory = new TempDirectory(GetTestFilePath())) @@ -77,9 +77,9 @@ public void FileSystemWatcher_Directory_Delete_SymLink() using (var watcher = new FileSystemWatcher(Path.GetFullPath(dir.Path), "*")) { // Make the symlink in our path (to the temp folder) and make sure an event is raised - string symLinkPath = Path.Combine(dir.Path, "link"); + string symLinkPath = Path.Combine(dir.Path, GetRandomLinkName()); Action action = () => Directory.Delete(symLinkPath); - Action cleanup = () => Assert.True(CreateSymLink(tempDir.Path, symLinkPath, true)); + Action cleanup = () => Assert.True(MountHelper.CreateSymbolicLink(symLinkPath, tempDir.Path, true)); cleanup(); ExpectEvent(watcher, WatcherChangeTypes.Deleted, action, cleanup, expectedPath: symLinkPath); diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Changed.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Changed.cs index da7b508c836da5..ad3ada7a923ae9 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Changed.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Changed.cs @@ -65,7 +65,7 @@ public void FileSystemWatcher_File_Changed_DataModification() } } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void FileSystemWatcher_File_Changed_SymLink() { using (var testDirectory = new TempDirectory(GetTestFilePath())) @@ -74,7 +74,7 @@ public void FileSystemWatcher_File_Changed_SymLink() using (var watcher = new FileSystemWatcher(dir.Path, "*")) { watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size; - Assert.True(CreateSymLink(file.Path, Path.Combine(dir.Path, "link"), false)); + Assert.True(MountHelper.CreateSymbolicLink(Path.Combine(dir.Path, GetRandomLinkName()), file.Path, false)); Action action = () => File.AppendAllText(file.Path, "longtext"); Action cleanup = () => File.AppendAllText(file.Path, "short"); diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs index 3a190a6899895c..ff9dc01e8c73a7 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs @@ -125,7 +125,7 @@ public void FileSystemWatcher_File_Create_DeepDirectoryStructure() } } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void FileSystemWatcher_File_Create_SymLink() { using (var testDirectory = new TempDirectory(GetTestFilePath())) @@ -134,8 +134,8 @@ public void FileSystemWatcher_File_Create_SymLink() using (var watcher = new FileSystemWatcher(dir.Path, "*")) { // Make the symlink in our path (to the temp file) and make sure an event is raised - string symLinkPath = Path.Combine(dir.Path, Path.GetFileName(temp.Path)); - Action action = () => Assert.True(CreateSymLink(temp.Path, symLinkPath, false)); + string symLinkPath = Path.Combine(dir.Path, GetRandomLinkName()); + Action action = () => Assert.True(MountHelper.CreateSymbolicLink(symLinkPath, temp.Path, false)); Action cleanup = () => File.Delete(symLinkPath); ExpectEvent(watcher, WatcherChangeTypes.Created, action, cleanup, symLinkPath); diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs index bbb0a95069d8a3..04cd6635fc5b66 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs @@ -89,7 +89,7 @@ public void FileSystemWatcher_File_Delete_DeepDirectoryStructure() } } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void FileSystemWatcher_File_Delete_SymLink() { using (var testDirectory = new TempDirectory(GetTestFilePath())) @@ -98,9 +98,9 @@ public void FileSystemWatcher_File_Delete_SymLink() using (var watcher = new FileSystemWatcher(dir.Path, "*")) { // Make the symlink in our path (to the temp file) and make sure an event is raised - string symLinkPath = Path.Combine(dir.Path, Path.GetFileName(temp.Path)); + string symLinkPath = Path.Combine(dir.Path, GetRandomLinkName()); Action action = () => File.Delete(symLinkPath); - Action cleanup = () => Assert.True(CreateSymLink(temp.Path, symLinkPath, false)); + Action cleanup = () => Assert.True(MountHelper.CreateSymbolicLink(symLinkPath, temp.Path, false)); cleanup(); ExpectEvent(watcher, WatcherChangeTypes.Deleted, action, cleanup, symLinkPath); diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs index 1846e688accab6..64b088ff86ea61 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs @@ -6,13 +6,13 @@ namespace System.IO.Tests { [ActiveIssue("https://github.com/dotnet/runtime/issues/34583", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ConditionalClass(typeof(SymbolicLink_Changed_Tests), nameof(CanCreateSymbolicLinks))] + [ConditionalClass(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public class SymbolicLink_Changed_Tests : FileSystemWatcherTest { private string CreateSymbolicLinkToTarget(string targetPath, bool isDirectory, string linkPath = null) { - linkPath ??= GetTestFilePath(); - Assert.True(CreateSymLink(targetPath, linkPath, isDirectory)); + linkPath ??= GetRandomLinkPath(); + Assert.True(MountHelper.CreateSymbolicLink(linkPath, targetPath, isDirectory)); return linkPath; } @@ -48,7 +48,7 @@ public void FileSystemWatcher_DirectorySymbolicLink_TargetsFile_Fails() public void FileSystemWatcher_DirectorySymbolicLink_TargetsSelf_Fails() { // Arrange - string linkPath = GetTestFilePath(); + string linkPath = GetRandomLinkPath(); CreateSymbolicLinkToTarget(targetPath: linkPath, isDirectory: true, linkPath: linkPath); using var watcher = new FileSystemWatcher(linkPath); @@ -112,7 +112,7 @@ public void FileSystemWatcher_SymbolicLink_IncludeSubdirectories_DoNotDereferenc using var dirA = new TempDirectory(GetTestFilePath()); using var dirB = new TempDirectory(GetTestFilePath()); - string linkPath = Path.Combine(dirA.Path, "linkToDirB"); + string linkPath = Path.Combine(dirA.Path, GetRandomDirName() + ".link"); CreateSymbolicLinkToTarget(dirB.Path, isDirectory: true, linkPath); using var watcher = new FileSystemWatcher(dirA.Path); diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/System.IO.FileSystem.Watcher.Tests.csproj b/src/libraries/System.IO.FileSystem.Watcher/tests/System.IO.FileSystem.Watcher.Tests.csproj index 453021d1f73769..f7185093a21c14 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/System.IO.FileSystem.Watcher.Tests.csproj +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/System.IO.FileSystem.Watcher.Tests.csproj @@ -35,6 +35,8 @@ Link="Common\System\IO\TempFile.cs" /> + diff --git a/src/libraries/System.IO.FileSystem/tests/Base/AllGetSetAttributes.cs b/src/libraries/System.IO.FileSystem/tests/Base/AllGetSetAttributes.cs index dd72459d281a13..ab667d4ebe7e6c 100644 --- a/src/libraries/System.IO.FileSystem/tests/Base/AllGetSetAttributes.cs +++ b/src/libraries/System.IO.FileSystem/tests/Base/AllGetSetAttributes.cs @@ -35,11 +35,11 @@ public void SetAttributes_MissingDirectory(char trailingChar) } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void SymLinksAreReparsePoints() { string path = CreateItem(); - string linkPath = GetTestFilePath(); + string linkPath = GetRandomLinkPath(); Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: IsDirectory)); @@ -47,11 +47,11 @@ public void SymLinksAreReparsePoints() Assert.Equal(FileAttributes.ReparsePoint, FileAttributes.ReparsePoint & GetAttributes(linkPath)); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void SymLinksReflectSymLinkAttributes() { string path = CreateItem(); - string linkPath = GetTestFilePath(); + string linkPath = GetRandomLinkPath(); Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: IsDirectory)); diff --git a/src/libraries/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs b/src/libraries/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs index 57a63f1c1cca28..c694a53161981f 100644 --- a/src/libraries/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs +++ b/src/libraries/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs @@ -21,7 +21,9 @@ public abstract class BaseGetSetTimes : FileSystemTest protected static bool LowTemporalResolution => PlatformDetection.IsBrowser || isHFS; protected static bool HighTemporalResolution => !LowTemporalResolution; - protected abstract T GetExistingItem(); + protected abstract bool CanBeReadOnly { get; } + + protected abstract T GetExistingItem(bool readOnly = false); protected abstract T GetMissingItem(); protected abstract T CreateSymlink(string path, string pathToTarget); @@ -84,7 +86,19 @@ public void SettingUpdatesProperties() SettingUpdatesPropertiesCore(item); } - [Theory] + [Fact] + public void SettingUpdatesPropertiesWhenReadOnly() + { + if (!CanBeReadOnly) + { + return; // directories can't be read only, so automatic pass + } + + T item = GetExistingItem(readOnly: true); + SettingUpdatesPropertiesCore(item); + } + + [ConditionalTheory(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] [PlatformSpecific(~TestPlatforms.Browser)] // Browser is excluded as it doesn't support symlinks [InlineData(false)] [InlineData(true)] @@ -164,7 +178,7 @@ public void SettingUpdatesPropertiesAfterAnother() TimeFunction function1 = functions.x; TimeFunction function2 = functions.y; bool reverse = functions.reverse; - + // Checking that milliseconds are not dropped after setter. DateTime dt1 = new DateTime(2002, 12, 1, 12, 3, 3, LowTemporalResolution ? 0 : 321, DateTimeKind.Utc); DateTime dt2 = new DateTime(2001, 12, 1, 12, 3, 3, LowTemporalResolution ? 0 : 321, DateTimeKind.Utc); diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Unix.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Unix.cs deleted file mode 100644 index 8acc040a8cb695..00000000000000 --- a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Unix.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Buffers; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Win32.SafeHandles; -using Xunit; - -namespace System.IO.Tests -{ - public abstract partial class BaseSymbolicLinks : FileSystemTest - { - private string GetTestDirectoryActualCasing() => TestDirectory; - } -} diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Windows.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Windows.cs deleted file mode 100644 index 4572d48544fcf2..00000000000000 --- a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.Windows.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Buffers; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Win32.SafeHandles; -using Xunit; - -namespace System.IO.Tests -{ - public abstract partial class BaseSymbolicLinks : FileSystemTest - { - private const int OPEN_EXISTING = 3; - private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; - - // Some Windows versions like Windows Nano Server have the %TEMP% environment variable set to "C:\TEMP" but the - // actual folder name is "C:\Temp", which prevents asserting path values using Assert.Equal due to case sensitiveness. - // So instead of using TestDirectory directly, we retrieve the real path with proper casing of the initial folder path. - private unsafe string GetTestDirectoryActualCasing() - { - try - { - using SafeFileHandle handle = Interop.Kernel32.CreateFile( - TestDirectory, - dwDesiredAccess: 0, - dwShareMode: FileShare.ReadWrite | FileShare.Delete, - dwCreationDisposition: FileMode.Open, - dwFlagsAndAttributes: - OPEN_EXISTING | - FILE_FLAG_BACKUP_SEMANTICS // Necessary to obtain a handle to a directory - ); - - if (!handle.IsInvalid) - { - const int InitialBufferSize = 4096; - char[]? buffer = ArrayPool.Shared.Rent(InitialBufferSize); - uint result = GetFinalPathNameByHandle(handle, buffer); - - // Remove extended prefix - int skip = PathInternal.IsExtended(buffer) ? 4 : 0; - - return new string( - buffer, - skip, - (int)result - skip); - } - } - catch { } - - return TestDirectory; - } - - private unsafe uint GetFinalPathNameByHandle(SafeFileHandle handle, char[] buffer) - { - fixed (char* bufPtr = buffer) - { - return Interop.Kernel32.GetFinalPathNameByHandle(handle, bufPtr, (uint)buffer.Length, Interop.Kernel32.FILE_NAME_NORMALIZED); - } - } - } -} diff --git a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.cs index d506e7e5e9b2b2..adaec2b9094f00 100644 --- a/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.cs +++ b/src/libraries/System.IO.FileSystem/tests/Base/SymbolicLinks/BaseSymbolicLinks.cs @@ -7,14 +7,18 @@ namespace System.IO.Tests { - [ConditionalClass(typeof(BaseSymbolicLinks), nameof(CanCreateSymbolicLinks))] // Contains helper methods that are shared by all symbolic link test classes. public abstract partial class BaseSymbolicLinks : FileSystemTest { + public BaseSymbolicLinks() + { + Assert.True(MountHelper.CanCreateSymbolicLinks); + } + protected DirectoryInfo CreateDirectoryContainingSelfReferencingSymbolicLink() { DirectoryInfo testDirectory = Directory.CreateDirectory(GetRandomDirPath()); - string pathToLink = Path.Join(testDirectory.FullName, GetRandomDirName()); + string pathToLink = Path.Join(testDirectory.FullName, GetRandomDirName() + ".link"); Assert.True(MountHelper.CreateSymbolicLink(pathToLink, pathToLink, isDirectory: true)); // Create a symlink cycle return testDirectory; } @@ -25,16 +29,6 @@ protected DirectoryInfo CreateSelfReferencingSymbolicLink() return (DirectoryInfo)Directory.CreateSymbolicLink(path, path); } - protected string GetRandomFileName() => GetTestFileName() + ".txt"; - protected string GetRandomLinkName() => GetTestFileName() + ".link"; - protected string GetRandomDirName() => GetTestFileName() + "_dir"; - - protected string GetRandomFilePath() => Path.Join(ActualTestDirectory.Value, GetRandomFileName()); - protected string GetRandomLinkPath() => Path.Join(ActualTestDirectory.Value, GetRandomLinkName()); - protected string GetRandomDirPath() => Path.Join(ActualTestDirectory.Value, GetRandomDirName()); - - private Lazy ActualTestDirectory => new Lazy(() => GetTestDirectoryActualCasing()); - /// /// Changes the current working directory path to a new temporary directory. /// Important: Make sure to call this inside a remote executor to avoid changing the cwd for all tests in same process. diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/Delete.cs b/src/libraries/System.IO.FileSystem/tests/Directory/Delete.cs index 1e12662b971ca0..312105af65379c 100644 --- a/src/libraries/System.IO.FileSystem/tests/Directory/Delete.cs +++ b/src/libraries/System.IO.FileSystem/tests/Directory/Delete.cs @@ -98,11 +98,11 @@ public void ShouldThrowIOExceptionDeletingCurrentDirectory() Assert.Throws(() => Delete(Directory.GetCurrentDirectory())); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void DeletingSymLinkDoesntDeleteTarget() { var path = GetTestFilePath(); - var linkPath = GetTestFilePath(); + var linkPath = GetRandomLinkPath(); Directory.CreateDirectory(path); Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: true)); @@ -291,7 +291,7 @@ public void RecursiveDelete_ShouldThrowIOExceptionIfContainedFileInUse() Assert.True(testDir.Exists); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void RecursiveDeletingDoesntFollowLinks() { var target = GetTestFilePath(); diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/Delete_MountVolume.cs b/src/libraries/System.IO.FileSystem/tests/Directory/Delete_MountVolume.cs index d789066ed9ecee..9703a8299bf304 100644 --- a/src/libraries/System.IO.FileSystem/tests/Directory/Delete_MountVolume.cs +++ b/src/libraries/System.IO.FileSystem/tests/Directory/Delete_MountVolume.cs @@ -15,448 +15,452 @@ This testcase attempts to delete some directories in a mounted volume using System.Threading; using System.Threading.Tasks; using Xunit; -public class Directory_Delete_MountVolume -{ - private delegate void ExceptionCode(); - private static bool s_pass = true; - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/14378")] - [PlatformSpecific(TestPlatforms.Windows)] // testing volumes / mounts / drive letters - public static void RunTest() +namespace System.IO.Tests +{ + public class Directory_Delete_MountVolume { - try - { - const string MountPrefixName = "LaksMount"; + private delegate void ExceptionCode(); + private static bool s_pass = true; - string mountedDirName; - string dirName; - string dirNameWithoutRoot; - string dirNameReferedFromMountedDrive; - - //Adding debug info since this test hangs sometime in RTS runs - string debugFileName = "Co7604Delete_MountVolume_Debug.txt"; - DeleteFile(debugFileName); - string scenarioDescription; - - scenarioDescription = "Scenario 1: Vanilla - Different drive is mounted on the current drive"; + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/14378")] + [PlatformSpecific(TestPlatforms.Windows)] // testing volumes / mounts / drive letters + public static void RunTest() + { try { - File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); + const string MountPrefixName = "LaksMount"; + + string mountedDirName; + string dirName; + string dirNameWithoutRoot; + string dirNameReferedFromMountedDrive; + + //Adding debug info since this test hangs sometime in RTS runs + string debugFileName = "Co7604Delete_MountVolume_Debug.txt"; + DeleteFile(debugFileName); + string scenarioDescription; - string otherDriveInMachine = IOServices.GetNtfsDriveOtherThanCurrent(); - //out labs use UIP tools in one drive and don't expect this drive to be used by others. We avoid this problem by not testing if the other drive is not NTFS - if (FileSystemDebugInfo.IsCurrentDriveNTFS() && otherDriveInMachine != null) + scenarioDescription = "Scenario 1: Vanilla - Different drive is mounted on the current drive"; + try { - Console.WriteLine(scenarioDescription); - mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Path.DirectorySeparatorChar.ToString(), MountPrefixName)); + File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); - try + string otherDriveInMachine = IOServices.GetNtfsDriveOtherThanCurrent(); + //out labs use UIP tools in one drive and don't expect this drive to be used by others. We avoid this problem by not testing if the other drive is not NTFS + if (FileSystemDebugInfo.IsCurrentDriveNTFS() && otherDriveInMachine != null) { - Directory.CreateDirectory(mountedDirName); + Console.WriteLine(scenarioDescription); + mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Path.DirectorySeparatorChar.ToString(), MountPrefixName)); - File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", otherDriveInMachine.Substring(0, 2), mountedDirName, Environment.NewLine)); - MountHelper.Mount(otherDriveInMachine.Substring(0, 2), mountedDirName); + try + { + Directory.CreateDirectory(mountedDirName); + + File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", otherDriveInMachine.Substring(0, 2), mountedDirName, Environment.NewLine)); + MountHelper.Mount(otherDriveInMachine.Substring(0, 2), mountedDirName); - dirName = ManageFileSystem.GetNonExistingDir(otherDriveInMachine, ManageFileSystem.DirPrefixName); - File.AppendAllText(debugFileName, string.Format("Creating a sub tree at: {0}{1}", dirName, Environment.NewLine)); - using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) + dirName = ManageFileSystem.GetNonExistingDir(otherDriveInMachine, ManageFileSystem.DirPrefixName); + File.AppendAllText(debugFileName, string.Format("Creating a sub tree at: {0}{1}", dirName, Environment.NewLine)); + using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) + { + Eval(Directory.Exists(dirName), "Err_3974g! Directory {0} doesn't exist: {1}", dirName, Directory.Exists(dirName)); + //Lets refer to these via mounted drive and check + dirNameWithoutRoot = dirName.Substring(3); + dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); + Directory.Delete(dirNameReferedFromMountedDrive, true); + Task.Delay(300).Wait(); + Eval(!Directory.Exists(dirName), "Err_20387g! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); + } + } + finally { - Eval(Directory.Exists(dirName), "Err_3974g! Directory {0} doesn't exist: {1}", dirName, Directory.Exists(dirName)); - //Lets refer to these via mounted drive and check - dirNameWithoutRoot = dirName.Substring(3); - dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); - Directory.Delete(dirNameReferedFromMountedDrive, true); - Task.Delay(300).Wait(); - Eval(!Directory.Exists(dirName), "Err_20387g! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); } + File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } - finally - { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); - } - File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); + else + File.AppendAllText(debugFileName, string.Format("Scenario 1 - Vanilla - NOT RUN: Different drive is mounted on the current drive {0}", Environment.NewLine)); + } + catch (Exception ex) + { + s_pass = false; + Console.WriteLine("Err_768lme! Exception caught in scenario: {0}", ex); } - else - File.AppendAllText(debugFileName, string.Format("Scenario 1 - Vanilla - NOT RUN: Different drive is mounted on the current drive {0}", Environment.NewLine)); - } - catch (Exception ex) - { - s_pass = false; - Console.WriteLine("Err_768lme! Exception caught in scenario: {0}", ex); - } - scenarioDescription = "Scenario 2: Current drive is mounted on a different drive"; - Console.WriteLine(scenarioDescription); - File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); - try - { - string otherDriveInMachine = IOServices.GetNtfsDriveOtherThanCurrent(); - if (otherDriveInMachine != null) + scenarioDescription = "Scenario 2: Current drive is mounted on a different drive"; + Console.WriteLine(scenarioDescription); + File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); + try { - mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(otherDriveInMachine.Substring(0, 3), MountPrefixName)); - try + string otherDriveInMachine = IOServices.GetNtfsDriveOtherThanCurrent(); + if (otherDriveInMachine != null) { - Directory.CreateDirectory(mountedDirName); + mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(otherDriveInMachine.Substring(0, 3), MountPrefixName)); + try + { + Directory.CreateDirectory(mountedDirName); - File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName, Environment.NewLine)); - MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); + File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName, Environment.NewLine)); + MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); - dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); - File.AppendAllText(debugFileName, string.Format("Creating a sub tree at: {0}{1}", dirName, Environment.NewLine)); - using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) + dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); + File.AppendAllText(debugFileName, string.Format("Creating a sub tree at: {0}{1}", dirName, Environment.NewLine)); + using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) + { + Eval(Directory.Exists(dirName), "Err_239ufz! Directory {0} doesn't exist: {1}", dirName, Directory.Exists(dirName)); + //Lets refer to these via mounted drive and check + dirNameWithoutRoot = dirName.Substring(3); + dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); + Directory.Delete(dirNameReferedFromMountedDrive, true); + Task.Delay(300).Wait(); + Eval(!Directory.Exists(dirName), "Err_794aiu! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); + } + } + finally { - Eval(Directory.Exists(dirName), "Err_239ufz! Directory {0} doesn't exist: {1}", dirName, Directory.Exists(dirName)); - //Lets refer to these via mounted drive and check - dirNameWithoutRoot = dirName.Substring(3); - dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); - Directory.Delete(dirNameReferedFromMountedDrive, true); - Task.Delay(300).Wait(); - Eval(!Directory.Exists(dirName), "Err_794aiu! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); } + File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } - finally - { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); - } - File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } - } - catch (Exception ex) - { - s_pass = false; - Console.WriteLine("Err_231vwf! Exception caught in scenario: {0}", ex); - } + catch (Exception ex) + { + s_pass = false; + Console.WriteLine("Err_231vwf! Exception caught in scenario: {0}", ex); + } - //scenario 3.1: Current drive is mounted on current drive - scenarioDescription = "Scenario 3.1 - Current drive is mounted on current drive"; - try - { - if (FileSystemDebugInfo.IsCurrentDriveNTFS()) + //scenario 3.1: Current drive is mounted on current drive + scenarioDescription = "Scenario 3.1 - Current drive is mounted on current drive"; + try { - File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); - mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Path.DirectorySeparatorChar.ToString(), MountPrefixName)); - try + if (FileSystemDebugInfo.IsCurrentDriveNTFS()) { - Directory.CreateDirectory(mountedDirName); + File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); + mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Path.DirectorySeparatorChar.ToString(), MountPrefixName)); + try + { + Directory.CreateDirectory(mountedDirName); - File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName, Environment.NewLine)); - MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); + File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName, Environment.NewLine)); + MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); - dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); - File.AppendAllText(debugFileName, string.Format("Creating a sub tree at: {0}{1}", dirName, Environment.NewLine)); - using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) + dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); + File.AppendAllText(debugFileName, string.Format("Creating a sub tree at: {0}{1}", dirName, Environment.NewLine)); + using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) + { + Eval(Directory.Exists(dirName), "Err_324eez! Directory {0} doesn't exist: {1}", dirName, Directory.Exists(dirName)); + //Lets refer to these via mounted drive and check + dirNameWithoutRoot = dirName.Substring(3); + dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); + Directory.Delete(dirNameReferedFromMountedDrive, true); + Task.Delay(300).Wait(); + Eval(!Directory.Exists(dirName), "Err_195whv! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); + } + } + finally { - Eval(Directory.Exists(dirName), "Err_324eez! Directory {0} doesn't exist: {1}", dirName, Directory.Exists(dirName)); - //Lets refer to these via mounted drive and check - dirNameWithoutRoot = dirName.Substring(3); - dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); - Directory.Delete(dirNameReferedFromMountedDrive, true); - Task.Delay(300).Wait(); - Eval(!Directory.Exists(dirName), "Err_195whv! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); } + File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } - finally - { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); - } - File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } - } - catch (Exception ex) - { - s_pass = false; - Console.WriteLine("Err_493ojg! Exception caught in scenario: {0}", ex); - } + catch (Exception ex) + { + s_pass = false; + Console.WriteLine("Err_493ojg! Exception caught in scenario: {0}", ex); + } - //scenario 3.2: Current drive is mounted on current directory - scenarioDescription = "Scenario 3.2 - Current drive is mounted on current directory"; - try - { - if (FileSystemDebugInfo.IsCurrentDriveNTFS()) + //scenario 3.2: Current drive is mounted on current directory + scenarioDescription = "Scenario 3.2 - Current drive is mounted on current directory"; + try { - File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); - mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), MountPrefixName)); - try + if (FileSystemDebugInfo.IsCurrentDriveNTFS()) { - Directory.CreateDirectory(mountedDirName); + File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); + mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), MountPrefixName)); + try + { + Directory.CreateDirectory(mountedDirName); - File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName, Environment.NewLine)); - MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); + File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName, Environment.NewLine)); + MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); - dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); - File.AppendAllText(debugFileName, string.Format("Creating a sub tree at: {0}{1}", dirName, Environment.NewLine)); - using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) + dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); + File.AppendAllText(debugFileName, string.Format("Creating a sub tree at: {0}{1}", dirName, Environment.NewLine)); + using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) + { + Eval(Directory.Exists(dirName), "Err_951ipb! Directory {0} doesn't exist: {1}", dirName, Directory.Exists(dirName)); + //Lets refer to these via mounted drive and check + dirNameWithoutRoot = dirName.Substring(3); + dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); + Directory.Delete(dirNameReferedFromMountedDrive, true); + Task.Delay(300).Wait(); + Eval(!Directory.Exists(dirName), "Err_493yin! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); + } + } + finally { - Eval(Directory.Exists(dirName), "Err_951ipb! Directory {0} doesn't exist: {1}", dirName, Directory.Exists(dirName)); - //Lets refer to these via mounted drive and check - dirNameWithoutRoot = dirName.Substring(3); - dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); - Directory.Delete(dirNameReferedFromMountedDrive, true); - Task.Delay(300).Wait(); - Eval(!Directory.Exists(dirName), "Err_493yin! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); } + File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } - finally - { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); - } - File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } - } - catch (Exception ex) - { - s_pass = false; - Console.WriteLine("Err_432qcp! Exception caught in scenario: {0}", ex); - } + catch (Exception ex) + { + s_pass = false; + Console.WriteLine("Err_432qcp! Exception caught in scenario: {0}", ex); + } - //@WATCH - potentially dangerous code - can delete the whole drive!! - //scenario 3.3: we call delete on the mounted volume - this should only delete the mounted drive? - //Current drive is mounted on current directory - scenarioDescription = "Scenario 3.3 - we call delete on the mounted volume"; - try - { - if (FileSystemDebugInfo.IsCurrentDriveNTFS()) + //@WATCH - potentially dangerous code - can delete the whole drive!! + //scenario 3.3: we call delete on the mounted volume - this should only delete the mounted drive? + //Current drive is mounted on current directory + scenarioDescription = "Scenario 3.3 - we call delete on the mounted volume"; + try { - File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); - mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), MountPrefixName)); - try + if (FileSystemDebugInfo.IsCurrentDriveNTFS()) { - Directory.CreateDirectory(mountedDirName); + File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); + mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), MountPrefixName)); + try + { + Directory.CreateDirectory(mountedDirName); - File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName, Environment.NewLine)); - MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); + File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName, Environment.NewLine)); + MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); - Directory.Delete(mountedDirName, true); - Task.Delay(300).Wait(); - } - finally - { - if (!Eval(!Directory.Exists(mountedDirName), "Err_001yph! Directory {0} still exist: {1}", mountedDirName, Directory.Exists(mountedDirName))) + Directory.Delete(mountedDirName, true); + Task.Delay(300).Wait(); + } + finally { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); + if (!Eval(!Directory.Exists(mountedDirName), "Err_001yph! Directory {0} still exist: {1}", mountedDirName, Directory.Exists(mountedDirName))) + { + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); + } } + File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } - File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } - } - catch (Exception ex) - { - s_pass = false; - Console.WriteLine("Err_386rpj! Exception caught in scenario: {0}", ex); - } + catch (Exception ex) + { + s_pass = false; + Console.WriteLine("Err_386rpj! Exception caught in scenario: {0}", ex); + } - //@WATCH - potentially dangerous code - can delete the whole drive!! - //scenario 3.4: we call delete on parent directory of the mounted volume, the parent directory will have some other directories and files - //Current drive is mounted on current directory - scenarioDescription = "Scenario 3.4 - we call delete on parent directory of the mounted volume, the parent directory will have some other directories and files"; - try - { - if (FileSystemDebugInfo.IsCurrentDriveNTFS()) + //@WATCH - potentially dangerous code - can delete the whole drive!! + //scenario 3.4: we call delete on parent directory of the mounted volume, the parent directory will have some other directories and files + //Current drive is mounted on current directory + scenarioDescription = "Scenario 3.4 - we call delete on parent directory of the mounted volume, the parent directory will have some other directories and files"; + try { - File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); - mountedDirName = null; - try + if (FileSystemDebugInfo.IsCurrentDriveNTFS()) { - dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); - File.AppendAllText(debugFileName, string.Format("Creating a sub tree at: {0}{1}", dirName, Environment.NewLine)); - using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 2, 20)) + File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); + mountedDirName = null; + try { - Eval(Directory.Exists(dirName), "Err_469yvh! Directory {0} doesn't exist: {1}", dirName, Directory.Exists(dirName)); - string[] dirs = fileManager.GetDirectories(1); - mountedDirName = Path.GetFullPath(dirs[0]); - if (Eval(Directory.GetDirectories(mountedDirName).Length == 0, "Err_974tsg! the sub directory has directories: {0}", mountedDirName)) + dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); + File.AppendAllText(debugFileName, string.Format("Creating a sub tree at: {0}{1}", dirName, Environment.NewLine)); + using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 2, 20)) { - foreach (string file in Directory.GetFiles(mountedDirName)) - File.Delete(file); - if (Eval(Directory.GetFiles(mountedDirName).Length == 0, "Err_13ref! the mounted directory has files: {0}", mountedDirName)) + Eval(Directory.Exists(dirName), "Err_469yvh! Directory {0} doesn't exist: {1}", dirName, Directory.Exists(dirName)); + string[] dirs = fileManager.GetDirectories(1); + mountedDirName = Path.GetFullPath(dirs[0]); + if (Eval(Directory.GetDirectories(mountedDirName).Length == 0, "Err_974tsg! the sub directory has directories: {0}", mountedDirName)) { - File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName, Environment.NewLine)); - MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); - //now lets call delete on the parent directory - Directory.Delete(dirName, true); - Task.Delay(300).Wait(); - Eval(!Directory.Exists(dirName), "Err_006jsf! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); - Console.WriteLine("Completed Scenario 3.4"); + foreach (string file in Directory.GetFiles(mountedDirName)) + File.Delete(file); + if (Eval(Directory.GetFiles(mountedDirName).Length == 0, "Err_13ref! the mounted directory has files: {0}", mountedDirName)) + { + File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName, Environment.NewLine)); + MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); + //now lets call delete on the parent directory + Directory.Delete(dirName, true); + Task.Delay(300).Wait(); + Eval(!Directory.Exists(dirName), "Err_006jsf! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); + Console.WriteLine("Completed Scenario 3.4"); + } } } } - } - finally - { - if (!Eval(!Directory.Exists(mountedDirName), "Err_625ckx! Directory {0} still exist: {1}", mountedDirName, Directory.Exists(mountedDirName))) + finally { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); + if (!Eval(!Directory.Exists(mountedDirName), "Err_625ckx! Directory {0} still exist: {1}", mountedDirName, Directory.Exists(mountedDirName))) + { + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); + } } + File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } - File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } - } - catch (Exception ex) - { - s_pass = false; - Console.WriteLine("Err_578tni! Exception caught in scenario: {0}", ex); - } + catch (Exception ex) + { + s_pass = false; + Console.WriteLine("Err_578tni! Exception caught in scenario: {0}", ex); + } - //@WATCH - potentially dangerous code - can delete the whole drive!! - //scenario 3.5: we call delete on parent directory of the mounted volume, the parent directory will have some other directories and files - //we call a different directory than the first - //Current drive is mounted on current directory - scenarioDescription = "Scenario 3.5 - we call delete on parent directory of the mounted volume, the parent directory will have some other directories and files"; - try - { - if (FileSystemDebugInfo.IsCurrentDriveNTFS()) + //@WATCH - potentially dangerous code - can delete the whole drive!! + //scenario 3.5: we call delete on parent directory of the mounted volume, the parent directory will have some other directories and files + //we call a different directory than the first + //Current drive is mounted on current directory + scenarioDescription = "Scenario 3.5 - we call delete on parent directory of the mounted volume, the parent directory will have some other directories and files"; + try { - File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); - mountedDirName = null; - try + if (FileSystemDebugInfo.IsCurrentDriveNTFS()) { - dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); - File.AppendAllText(debugFileName, string.Format("Creating a sub tree at: {0}{1}", dirName, Environment.NewLine)); - using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 2, 30)) + File.AppendAllText(debugFileName, string.Format("{0}{1}", scenarioDescription, Environment.NewLine)); + mountedDirName = null; + try { - Eval(Directory.Exists(dirName), "Err_715tdq! Directory {0} doesn't exist: {1}", dirName, Directory.Exists(dirName)); - string[] dirs = fileManager.GetDirectories(1); - mountedDirName = Path.GetFullPath(dirs[0]); - if (dirs.Length > 1) - mountedDirName = Path.GetFullPath(dirs[1]); - if (Eval(Directory.GetDirectories(mountedDirName).Length == 0, "Err_492qwl! the sub directory has directories: {0}", mountedDirName)) + dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); + File.AppendAllText(debugFileName, string.Format("Creating a sub tree at: {0}{1}", dirName, Environment.NewLine)); + using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 2, 30)) { - foreach (string file in Directory.GetFiles(mountedDirName)) - File.Delete(file); - if (Eval(Directory.GetFiles(mountedDirName).Length == 0, "Err_904kij! the mounted directory has files: {0}", mountedDirName)) + Eval(Directory.Exists(dirName), "Err_715tdq! Directory {0} doesn't exist: {1}", dirName, Directory.Exists(dirName)); + string[] dirs = fileManager.GetDirectories(1); + mountedDirName = Path.GetFullPath(dirs[0]); + if (dirs.Length > 1) + mountedDirName = Path.GetFullPath(dirs[1]); + if (Eval(Directory.GetDirectories(mountedDirName).Length == 0, "Err_492qwl! the sub directory has directories: {0}", mountedDirName)) { - File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName, Environment.NewLine)); - MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); - //now lets call delete on the parent directory - Directory.Delete(dirName, true); - Task.Delay(300).Wait(); - Eval(!Directory.Exists(dirName), "Err_900edl! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); - Console.WriteLine("Completed Scenario 3.5: {0}", mountedDirName); + foreach (string file in Directory.GetFiles(mountedDirName)) + File.Delete(file); + if (Eval(Directory.GetFiles(mountedDirName).Length == 0, "Err_904kij! the mounted directory has files: {0}", mountedDirName)) + { + File.AppendAllText(debugFileName, string.Format("Mounting on {0}{1}{2}", Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName, Environment.NewLine)); + MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); + //now lets call delete on the parent directory + Directory.Delete(dirName, true); + Task.Delay(300).Wait(); + Eval(!Directory.Exists(dirName), "Err_900edl! Directory {0} still exist: {1}", dirName, Directory.Exists(dirName)); + Console.WriteLine("Completed Scenario 3.5: {0}", mountedDirName); + } } } } - } - finally - { - if (!Eval(!Directory.Exists(mountedDirName), "Err_462xtc! Directory {0} still exist: {1}", mountedDirName, Directory.Exists(mountedDirName))) + finally { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); + if (!Eval(!Directory.Exists(mountedDirName), "Err_462xtc! Directory {0} still exist: {1}", mountedDirName, Directory.Exists(mountedDirName))) + { + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); + } } + File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } - File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); + } + catch (Exception ex) + { + s_pass = false; + Console.WriteLine("Err_471jli! Exception caught in scenario: {0}", ex); } } catch (Exception ex) { s_pass = false; - Console.WriteLine("Err_471jli! Exception caught in scenario: {0}", ex); + Console.WriteLine("Err_234rsgf! Uncaught exception in RunTest: {0}", ex); + } + finally + { + Assert.True(s_pass); } } - catch (Exception ex) - { - s_pass = false; - Console.WriteLine("Err_234rsgf! Uncaught exception in RunTest: {0}", ex); - } - finally + + private static void DeleteFile(string debugFileName) { - Assert.True(s_pass); + if (File.Exists(debugFileName)) + File.Delete(debugFileName); } - } - - private static void DeleteFile(string debugFileName) - { - if (File.Exists(debugFileName)) - File.Delete(debugFileName); - } - private static void DeleteDir(string debugFileName, bool sub) - { - bool deleted = false; int maxAttempts = 5; - while (!deleted && maxAttempts > 0) + private static void DeleteDir(string debugFileName, bool sub) { - if (Directory.Exists(debugFileName)) + bool deleted = false; int maxAttempts = 5; + while (!deleted && maxAttempts > 0) { - try - { - Directory.Delete(debugFileName, sub); - deleted = true; - } - catch (Exception) + if (Directory.Exists(debugFileName)) { - if (--maxAttempts == 0) - throw; - else - Task.Delay(300).Wait(); + try + { + Directory.Delete(debugFileName, sub); + deleted = true; + } + catch (Exception) + { + if (--maxAttempts == 0) + throw; + else + Task.Delay(300).Wait(); + } } } } - } - //Checks for error - private static bool Eval(bool expression, string msg, params object[] values) - { - return Eval(expression, string.Format(msg, values)); - } + //Checks for error + private static bool Eval(bool expression, string msg, params object[] values) + { + return Eval(expression, string.Format(msg, values)); + } - private static bool Eval(T actual, T expected, string errorMsg) - { - bool retValue = expected == null ? actual == null : expected.Equals(actual); + private static bool Eval(T actual, T expected, string errorMsg) + { + bool retValue = expected == null ? actual == null : expected.Equals(actual); - if (!retValue) - Eval(retValue, errorMsg + - " Expected:" + (null == expected ? "" : expected.ToString()) + - " Actual:" + (null == actual ? "" : actual.ToString())); + if (!retValue) + Eval(retValue, errorMsg + + " Expected:" + (null == expected ? "" : expected.ToString()) + + " Actual:" + (null == actual ? "" : actual.ToString())); - return retValue; - } + return retValue; + } - private static bool Eval(bool expression, string msg) - { - if (!expression) + private static bool Eval(bool expression, string msg) { - s_pass = false; - Console.WriteLine(msg); + if (!expression) + { + s_pass = false; + Console.WriteLine(msg); + } + return expression; } - return expression; - } - //Checks for a particular type of exception - private static void CheckException(ExceptionCode test, string error) - { - CheckException(test, error, null); - } - - //Checks for a particular type of exception and an Exception msg in the English locale - private static void CheckException(ExceptionCode test, string error, string msgExpected) - { - bool exception = false; - try + //Checks for a particular type of exception + private static void CheckException(ExceptionCode test, string error) { - test(); - error = string.Format("{0} Exception NOT thrown ", error); + CheckException(test, error, null); } - catch (Exception e) + + //Checks for a particular type of exception and an Exception msg in the English locale + private static void CheckException(ExceptionCode test, string error, string msgExpected) { - if (e.GetType() == typeof(E)) + bool exception = false; + try { - exception = true; - if (System.Globalization.CultureInfo.CurrentUICulture.Name == "en-US" && msgExpected != null && e.Message != msgExpected) + test(); + error = string.Format("{0} Exception NOT thrown ", error); + } + catch (Exception e) + { + if (e.GetType() == typeof(E)) { - exception = false; - error = string.Format("{0} Message Different: <{1}>", error, e.Message); + exception = true; + if (System.Globalization.CultureInfo.CurrentUICulture.Name == "en-US" && msgExpected != null && e.Message != msgExpected) + { + exception = false; + error = string.Format("{0} Message Different: <{1}>", error, e.Message); + } } + else + error = string.Format("{0} Exception type: {1}", error, e.GetType().Name); } - else - error = string.Format("{0} Exception type: {1}", error, e.GetType().Name); + Eval(exception, error); } - Eval(exception, error); } } diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs b/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs index 107472c795059b..b5cff81546e151 100644 --- a/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs +++ b/src/libraries/System.IO.FileSystem/tests/Directory/Exists.cs @@ -106,11 +106,11 @@ public void DirectoryLongerThanMaxLongPath_DoesntThrow() }); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void SymLinksMayExistIndependentlyOfTarget() { var path = GetTestFilePath(); - var linkPath = GetTestFilePath(); + var linkPath = GetRandomLinkPath(); Directory.CreateDirectory(path); Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: true)); @@ -153,13 +153,13 @@ public void SymLinksMayExistIndependentlyOfTarget() Assert.False(File.Exists(linkPath), "linkPath should no longer exist as a file"); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void SymlinkToNewDirectory() { string path = GetTestFilePath(); Directory.CreateDirectory(path); - string linkPath = GetTestFilePath(); + string linkPath = GetRandomLinkPath(); Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: true)); Assert.True(Directory.Exists(path)); diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/GetDirectories.cs b/src/libraries/System.IO.FileSystem/tests/Directory/GetDirectories.cs index 3e4c0aaf9f3284..ef370ff5bd6f6e 100644 --- a/src/libraries/System.IO.FileSystem/tests/Directory/GetDirectories.cs +++ b/src/libraries/System.IO.FileSystem/tests/Directory/GetDirectories.cs @@ -16,7 +16,7 @@ public override string[] GetEntries(string path) return Directory.GetDirectories(path); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void EnumerateWithSymLinkToDirectory() { DirectoryInfo containingFolder = Directory.CreateDirectory(GetTestFilePath()); @@ -25,7 +25,7 @@ public void EnumerateWithSymLinkToDirectory() DirectoryInfo targetDir = Directory.CreateDirectory(GetTestFilePath()); { // Create a symlink to a folder that exists - string linkPath = Path.Combine(containingFolder.FullName, Path.GetRandomFileName()); + string linkPath = Path.Combine(containingFolder.FullName, GetRandomLinkName()); Assert.True(MountHelper.CreateSymbolicLink(linkPath, targetDir.FullName, isDirectory: true)); Assert.True(Directory.Exists(linkPath)); diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/GetFiles.cs b/src/libraries/System.IO.FileSystem/tests/Directory/GetFiles.cs index bec3ff78cf546f..e3b7a529a774f8 100644 --- a/src/libraries/System.IO.FileSystem/tests/Directory/GetFiles.cs +++ b/src/libraries/System.IO.FileSystem/tests/Directory/GetFiles.cs @@ -16,7 +16,7 @@ public override string[] GetEntries(string path) return Directory.GetFiles(path); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void EnumerateWithSymLinkToFile() { DirectoryInfo containingFolder = Directory.CreateDirectory(GetTestFilePath()); @@ -25,7 +25,7 @@ public void EnumerateWithSymLinkToFile() FileInfo targetFile = new FileInfo(GetTestFilePath()); targetFile.Create().Dispose(); - string linkPath = Path.Combine(containingFolder.FullName, Path.GetRandomFileName()); + string linkPath = Path.Combine(containingFolder.FullName, GetRandomLinkName()); Assert.True(MountHelper.CreateSymbolicLink(linkPath, targetFile.FullName, isDirectory: false)); Assert.True(File.Exists(linkPath)); diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/GetSetTimes.cs b/src/libraries/System.IO.FileSystem/tests/Directory/GetSetTimes.cs index 698fbd1d67ce08..c71832b8f76eb9 100644 --- a/src/libraries/System.IO.FileSystem/tests/Directory/GetSetTimes.cs +++ b/src/libraries/System.IO.FileSystem/tests/Directory/GetSetTimes.cs @@ -7,7 +7,9 @@ namespace System.IO.Tests { public class Directory_GetSetTimes : StaticGetSetTimes { - protected override string GetExistingItem() => Directory.CreateDirectory(GetTestFilePath()).FullName; + protected override bool CanBeReadOnly => false; + + protected override string GetExistingItem(bool _) => Directory.CreateDirectory(GetTestFilePath()).FullName; protected override string CreateSymlink(string path, string pathToTarget) => Directory.CreateSymbolicLink(path, pathToTarget).FullName; diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/ReparsePoints_MountVolume.cs b/src/libraries/System.IO.FileSystem/tests/Directory/ReparsePoints_MountVolume.cs index a50b9292e3968c..b09183f113070a 100644 --- a/src/libraries/System.IO.FileSystem/tests/Directory/ReparsePoints_MountVolume.cs +++ b/src/libraries/System.IO.FileSystem/tests/Directory/ReparsePoints_MountVolume.cs @@ -12,420 +12,423 @@ This testcase attempts to checks GetDirectories/GetFiles with the following Repa using System.Diagnostics; using Xunit; -public class Directory_ReparsePoints_MountVolume +namespace System.IO.Tests { - private delegate void ExceptionCode(); - private static bool s_pass = true; - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/14378")] - [PlatformSpecific(TestPlatforms.Windows)] // testing mounting volumes and reparse points - public static void runTest() + public class Directory_ReparsePoints_MountVolume { - try - { - Stopwatch watch; - - const string MountPrefixName = "LaksMount"; - - string mountedDirName; - string dirNameWithoutRoot; - string dirNameReferedFromMountedDrive; - string dirName; - string[] expectedFiles; - string[] files; - string[] expectedDirs; - string[] dirs; - List list; + private delegate void ExceptionCode(); + private static bool s_pass = true; - watch = new Stopwatch(); - watch.Start(); + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/14378")] + [PlatformSpecific(TestPlatforms.Windows)] // testing mounting volumes and reparse points + public static void runTest() + { try { - //Scenario 1: Vanilla - Different drive is mounted on the current drive - Console.WriteLine("Scenario 1 - Vanilla: Different drive is mounted on the current drive: {0}", watch.Elapsed); + Stopwatch watch; + + const string MountPrefixName = "LaksMount"; + + string mountedDirName; + string dirNameWithoutRoot; + string dirNameReferedFromMountedDrive; + string dirName; + string[] expectedFiles; + string[] files; + string[] expectedDirs; + string[] dirs; + List list; - string otherDriveInMachine = IOServices.GetNtfsDriveOtherThanCurrent(); - if (FileSystemDebugInfo.IsCurrentDriveNTFS() && otherDriveInMachine != null) + watch = new Stopwatch(); + watch.Start(); + try { - mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Path.DirectorySeparatorChar.ToString(), MountPrefixName)); - try - { - Console.WriteLine("Creating directory " + mountedDirName); - Directory.CreateDirectory(mountedDirName); - MountHelper.Mount(otherDriveInMachine.Substring(0, 2), mountedDirName); + //Scenario 1: Vanilla - Different drive is mounted on the current drive + Console.WriteLine("Scenario 1 - Vanilla: Different drive is mounted on the current drive: {0}", watch.Elapsed); - dirName = ManageFileSystem.GetNonExistingDir(otherDriveInMachine, ManageFileSystem.DirPrefixName); - using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) + string otherDriveInMachine = IOServices.GetNtfsDriveOtherThanCurrent(); + if (FileSystemDebugInfo.IsCurrentDriveNTFS() && otherDriveInMachine != null) + { + mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Path.DirectorySeparatorChar.ToString(), MountPrefixName)); + try { - dirNameWithoutRoot = dirName.Substring(3); - dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); + Console.WriteLine("Creating directory " + mountedDirName); + Directory.CreateDirectory(mountedDirName); + MountHelper.Mount(otherDriveInMachine.Substring(0, 2), mountedDirName); - //Files - expectedFiles = fileManager.GetAllFiles(); - list = new List(); - //We will only test the filenames since they are unique - foreach (string file in expectedFiles) - list.Add(Path.GetFileName(file)); - files = Directory.GetFiles(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); - Eval(files.Length == list.Count, "Err_3947g! wrong count"); - for (int i = 0; i < expectedFiles.Length; i++) - { - if (Eval(list.Contains(Path.GetFileName(files[i])), "Err_582bmw! No file found: {0}", files[i])) - list.Remove(Path.GetFileName(files[i])); - } - if (!Eval(list.Count == 0, "Err_891vut! wrong count: {0}", list.Count)) + dirName = ManageFileSystem.GetNonExistingDir(otherDriveInMachine, ManageFileSystem.DirPrefixName); + using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) { - Console.WriteLine(); - foreach (string fileName in list) - Console.WriteLine(fileName); - } + dirNameWithoutRoot = dirName.Substring(3); + dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); - //Directories - expectedDirs = fileManager.GetAllDirectories(); - list = new List(); - foreach (string dir in expectedDirs) - list.Add(dir.Substring(dirName.Length)); - dirs = Directory.GetDirectories(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); - Eval(dirs.Length == list.Count, "Err_813weq! wrong count"); - for (int i = 0; i < dirs.Length; i++) - { - string exDir = dirs[i].Substring(dirNameReferedFromMountedDrive.Length); - if (Eval(list.Contains(exDir), "Err_287kkm! No file found: {0}", exDir)) - list.Remove(exDir); - } - if (!Eval(list.Count == 0, "Err_921mhs! wrong count: {0}", list.Count)) - { - Console.WriteLine(); - foreach (string value in list) - Console.WriteLine(value); + //Files + expectedFiles = fileManager.GetAllFiles(); + list = new List(); + //We will only test the filenames since they are unique + foreach (string file in expectedFiles) + list.Add(Path.GetFileName(file)); + files = Directory.GetFiles(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); + Eval(files.Length == list.Count, "Err_3947g! wrong count"); + for (int i = 0; i < expectedFiles.Length; i++) + { + if (Eval(list.Contains(Path.GetFileName(files[i])), "Err_582bmw! No file found: {0}", files[i])) + list.Remove(Path.GetFileName(files[i])); + } + if (!Eval(list.Count == 0, "Err_891vut! wrong count: {0}", list.Count)) + { + Console.WriteLine(); + foreach (string fileName in list) + Console.WriteLine(fileName); + } + + //Directories + expectedDirs = fileManager.GetAllDirectories(); + list = new List(); + foreach (string dir in expectedDirs) + list.Add(dir.Substring(dirName.Length)); + dirs = Directory.GetDirectories(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); + Eval(dirs.Length == list.Count, "Err_813weq! wrong count"); + for (int i = 0; i < dirs.Length; i++) + { + string exDir = dirs[i].Substring(dirNameReferedFromMountedDrive.Length); + if (Eval(list.Contains(exDir), "Err_287kkm! No file found: {0}", exDir)) + list.Remove(exDir); + } + if (!Eval(list.Count == 0, "Err_921mhs! wrong count: {0}", list.Count)) + { + Console.WriteLine(); + foreach (string value in list) + Console.WriteLine(value); + } } } + finally + { + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); + } } - finally + else { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); + Console.WriteLine("Skipping since drive is not NTFS and there is no other drive on the machine"); } } - else + catch (Exception ex) { - Console.WriteLine("Skipping since drive is not NTFS and there is no other drive on the machine"); + s_pass = false; + Console.WriteLine("Err_768lme! Exception caught in scenario: {0}", ex); } - } - catch (Exception ex) - { - s_pass = false; - Console.WriteLine("Err_768lme! Exception caught in scenario: {0}", ex); - } - //Scenario 2: Current drive is mounted on a different drive - Console.WriteLine(Environment.NewLine + "Scenario 2 - Current drive is mounted on a different drive: {0}", watch.Elapsed); - try - { - string otherDriveInMachine = IOServices.GetNtfsDriveOtherThanCurrent(); - if (otherDriveInMachine != null) + //Scenario 2: Current drive is mounted on a different drive + Console.WriteLine(Environment.NewLine + "Scenario 2 - Current drive is mounted on a different drive: {0}", watch.Elapsed); + try { - mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(otherDriveInMachine.Substring(0, 3), MountPrefixName)); - try + string otherDriveInMachine = IOServices.GetNtfsDriveOtherThanCurrent(); + if (otherDriveInMachine != null) { - Directory.CreateDirectory(mountedDirName); + mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(otherDriveInMachine.Substring(0, 3), MountPrefixName)); + try + { + Directory.CreateDirectory(mountedDirName); - MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); + MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); - dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); - using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) - { - dirNameWithoutRoot = dirName.Substring(3); - dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); - //Files - expectedFiles = fileManager.GetAllFiles(); - list = new List(); - //We will only test the filenames since they are unique - foreach (string file in expectedFiles) - list.Add(Path.GetFileName(file)); - files = Directory.GetFiles(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); - Eval(files.Length == list.Count, "Err_689myg! wrong count"); - for (int i = 0; i < expectedFiles.Length; i++) - { - if (Eval(list.Contains(Path.GetFileName(files[i])), "Err_894vhm! No file found: {0}", files[i])) - list.Remove(Path.GetFileName(files[i])); - } - if (!Eval(list.Count == 0, "Err_952qkj! wrong count: {0}", list.Count)) + dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); + using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) { - Console.WriteLine(); - foreach (string fileName in list) - Console.WriteLine(fileName); - } + dirNameWithoutRoot = dirName.Substring(3); + dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); + //Files + expectedFiles = fileManager.GetAllFiles(); + list = new List(); + //We will only test the filenames since they are unique + foreach (string file in expectedFiles) + list.Add(Path.GetFileName(file)); + files = Directory.GetFiles(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); + Eval(files.Length == list.Count, "Err_689myg! wrong count"); + for (int i = 0; i < expectedFiles.Length; i++) + { + if (Eval(list.Contains(Path.GetFileName(files[i])), "Err_894vhm! No file found: {0}", files[i])) + list.Remove(Path.GetFileName(files[i])); + } + if (!Eval(list.Count == 0, "Err_952qkj! wrong count: {0}", list.Count)) + { + Console.WriteLine(); + foreach (string fileName in list) + Console.WriteLine(fileName); + } - //Directories - expectedDirs = fileManager.GetAllDirectories(); - list = new List(); - foreach (string dir in expectedDirs) - list.Add(dir.Substring(dirName.Length)); - dirs = Directory.GetDirectories(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); - Eval(dirs.Length == list.Count, "Err_154vrz! wrong count"); - for (int i = 0; i < dirs.Length; i++) - { - string exDir = dirs[i].Substring(dirNameReferedFromMountedDrive.Length); - if (Eval(list.Contains(exDir), "Err_301sao! No file found: {0}", exDir)) - list.Remove(exDir); - } - if (!Eval(list.Count == 0, "Err_630gjj! wrong count: {0}", list.Count)) - { - Console.WriteLine(); - foreach (string value in list) - Console.WriteLine(value); + //Directories + expectedDirs = fileManager.GetAllDirectories(); + list = new List(); + foreach (string dir in expectedDirs) + list.Add(dir.Substring(dirName.Length)); + dirs = Directory.GetDirectories(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); + Eval(dirs.Length == list.Count, "Err_154vrz! wrong count"); + for (int i = 0; i < dirs.Length; i++) + { + string exDir = dirs[i].Substring(dirNameReferedFromMountedDrive.Length); + if (Eval(list.Contains(exDir), "Err_301sao! No file found: {0}", exDir)) + list.Remove(exDir); + } + if (!Eval(list.Count == 0, "Err_630gjj! wrong count: {0}", list.Count)) + { + Console.WriteLine(); + foreach (string value in list) + Console.WriteLine(value); + } } } + finally + { + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); + } } - finally + else { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); + Console.WriteLine("Skipping since drive is not NTFS and there is no other drive on the machine"); } } - else + catch (Exception ex) { - Console.WriteLine("Skipping since drive is not NTFS and there is no other drive on the machine"); + s_pass = false; + Console.WriteLine("Err_231vwf! Exception caught in scenario: {0}", ex); } - } - catch (Exception ex) - { - s_pass = false; - Console.WriteLine("Err_231vwf! Exception caught in scenario: {0}", ex); - } - //scenario 3.1: Current drive is mounted on current drive - Console.WriteLine(Environment.NewLine + "Scenario 3.1 - Current drive is mounted on current drive: {0}", watch.Elapsed); - try - { - if (FileSystemDebugInfo.IsCurrentDriveNTFS()) + //scenario 3.1: Current drive is mounted on current drive + Console.WriteLine(Environment.NewLine + "Scenario 3.1 - Current drive is mounted on current drive: {0}", watch.Elapsed); + try { - mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Path.DirectorySeparatorChar.ToString(), MountPrefixName)); - try + if (FileSystemDebugInfo.IsCurrentDriveNTFS()) { - Directory.CreateDirectory(mountedDirName); - MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); - - dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); - using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) + mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Path.DirectorySeparatorChar.ToString(), MountPrefixName)); + try { - dirNameWithoutRoot = dirName.Substring(3); - dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); - //Files - expectedFiles = fileManager.GetAllFiles(); - list = new List(); - //We will only test the filenames since they are unique - foreach (string file in expectedFiles) - list.Add(Path.GetFileName(file)); - files = Directory.GetFiles(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); - Eval(files.Length == list.Count, "Err_213fuo! wrong count"); - for (int i = 0; i < expectedFiles.Length; i++) - { - if (Eval(list.Contains(Path.GetFileName(files[i])), "Err_499oxz! No file found: {0}", files[i])) - list.Remove(Path.GetFileName(files[i])); - } - if (!Eval(list.Count == 0, "Err_301gtz! wrong count: {0}", list.Count)) - { - Console.WriteLine(); - foreach (string fileName in list) - Console.WriteLine(fileName); - } + Directory.CreateDirectory(mountedDirName); + MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); - //Directories - expectedDirs = fileManager.GetAllDirectories(); - list = new List(); - foreach (string dir in expectedDirs) - list.Add(dir.Substring(dirName.Length)); - dirs = Directory.GetDirectories(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); - Eval(dirs.Length == list.Count, "Err_771dxv! wrong count"); - for (int i = 0; i < dirs.Length; i++) - { - string exDir = dirs[i].Substring(dirNameReferedFromMountedDrive.Length); - if (Eval(list.Contains(exDir), "Err_315jey! No file found: {0}", exDir)) - list.Remove(exDir); - } - if (!Eval(list.Count == 0, "Err_424opm! wrong count: {0}", list.Count)) + dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); + using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) { - Console.WriteLine(); - foreach (string value in list) - Console.WriteLine(value); + dirNameWithoutRoot = dirName.Substring(3); + dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); + //Files + expectedFiles = fileManager.GetAllFiles(); + list = new List(); + //We will only test the filenames since they are unique + foreach (string file in expectedFiles) + list.Add(Path.GetFileName(file)); + files = Directory.GetFiles(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); + Eval(files.Length == list.Count, "Err_213fuo! wrong count"); + for (int i = 0; i < expectedFiles.Length; i++) + { + if (Eval(list.Contains(Path.GetFileName(files[i])), "Err_499oxz! No file found: {0}", files[i])) + list.Remove(Path.GetFileName(files[i])); + } + if (!Eval(list.Count == 0, "Err_301gtz! wrong count: {0}", list.Count)) + { + Console.WriteLine(); + foreach (string fileName in list) + Console.WriteLine(fileName); + } + + //Directories + expectedDirs = fileManager.GetAllDirectories(); + list = new List(); + foreach (string dir in expectedDirs) + list.Add(dir.Substring(dirName.Length)); + dirs = Directory.GetDirectories(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); + Eval(dirs.Length == list.Count, "Err_771dxv! wrong count"); + for (int i = 0; i < dirs.Length; i++) + { + string exDir = dirs[i].Substring(dirNameReferedFromMountedDrive.Length); + if (Eval(list.Contains(exDir), "Err_315jey! No file found: {0}", exDir)) + list.Remove(exDir); + } + if (!Eval(list.Count == 0, "Err_424opm! wrong count: {0}", list.Count)) + { + Console.WriteLine(); + foreach (string value in list) + Console.WriteLine(value); + } } } + finally + { + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); + } } - finally + else { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); + Console.WriteLine("Drive is not NTFS. Skipping scenario"); } } - else + catch (Exception ex) { - Console.WriteLine("Drive is not NTFS. Skipping scenario"); + s_pass = false; + Console.WriteLine("Err_493ojg! Exception caught in scenario: {0}", ex); } - } - catch (Exception ex) - { - s_pass = false; - Console.WriteLine("Err_493ojg! Exception caught in scenario: {0}", ex); - } - //scenario 3.2: Current drive is mounted on current directory - Console.WriteLine(Environment.NewLine + "Scenario 3.2 - Current drive is mounted on current directory: {0}", watch.Elapsed); - try - { - if (FileSystemDebugInfo.IsCurrentDriveNTFS()) + //scenario 3.2: Current drive is mounted on current directory + Console.WriteLine(Environment.NewLine + "Scenario 3.2 - Current drive is mounted on current directory: {0}", watch.Elapsed); + try { - mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), MountPrefixName)); - try + if (FileSystemDebugInfo.IsCurrentDriveNTFS()) { - Directory.CreateDirectory(mountedDirName); - MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); - - dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); - using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) + mountedDirName = Path.GetFullPath(ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), MountPrefixName)); + try { - dirNameWithoutRoot = dirName.Substring(3); - dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); - //Files - expectedFiles = fileManager.GetAllFiles(); - list = new List(); - //We will only test the filenames since they are unique - foreach (string file in expectedFiles) - list.Add(Path.GetFileName(file)); - files = Directory.GetFiles(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); - Eval(files.Length == list.Count, "Err_253yit! wrong count"); - for (int i = 0; i < expectedFiles.Length; i++) - { - if (Eval(list.Contains(Path.GetFileName(files[i])), "Err_798mjs! No file found: {0}", files[i])) - list.Remove(Path.GetFileName(files[i])); - } - if (!Eval(list.Count == 0, "Err_141lgl! wrong count: {0}", list.Count)) - { - Console.WriteLine(); - foreach (string fileName in list) - Console.WriteLine(fileName); - } + Directory.CreateDirectory(mountedDirName); + MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountedDirName); - //Directories - expectedDirs = fileManager.GetAllDirectories(); - list = new List(); - foreach (string dir in expectedDirs) - list.Add(dir.Substring(dirName.Length)); - dirs = Directory.GetDirectories(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); - Eval(dirs.Length == list.Count, "Err_512oxq! wrong count"); - for (int i = 0; i < dirs.Length; i++) + dirName = ManageFileSystem.GetNonExistingDir(Directory.GetCurrentDirectory(), ManageFileSystem.DirPrefixName); + using (ManageFileSystem fileManager = new ManageFileSystem(dirName, 3, 100)) { - string exDir = dirs[i].Substring(dirNameReferedFromMountedDrive.Length); - if (Eval(list.Contains(exDir), "Err_907zbr! No file found: {0}", exDir)) - list.Remove(exDir); - } - if (!Eval(list.Count == 0, "Err_574raf! wrong count: {0}", list.Count)) - { - Console.WriteLine(); - foreach (string value in list) - Console.WriteLine(value); + dirNameWithoutRoot = dirName.Substring(3); + dirNameReferedFromMountedDrive = Path.Combine(mountedDirName, dirNameWithoutRoot); + //Files + expectedFiles = fileManager.GetAllFiles(); + list = new List(); + //We will only test the filenames since they are unique + foreach (string file in expectedFiles) + list.Add(Path.GetFileName(file)); + files = Directory.GetFiles(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); + Eval(files.Length == list.Count, "Err_253yit! wrong count"); + for (int i = 0; i < expectedFiles.Length; i++) + { + if (Eval(list.Contains(Path.GetFileName(files[i])), "Err_798mjs! No file found: {0}", files[i])) + list.Remove(Path.GetFileName(files[i])); + } + if (!Eval(list.Count == 0, "Err_141lgl! wrong count: {0}", list.Count)) + { + Console.WriteLine(); + foreach (string fileName in list) + Console.WriteLine(fileName); + } + + //Directories + expectedDirs = fileManager.GetAllDirectories(); + list = new List(); + foreach (string dir in expectedDirs) + list.Add(dir.Substring(dirName.Length)); + dirs = Directory.GetDirectories(dirNameReferedFromMountedDrive, "*.*", SearchOption.AllDirectories); + Eval(dirs.Length == list.Count, "Err_512oxq! wrong count"); + for (int i = 0; i < dirs.Length; i++) + { + string exDir = dirs[i].Substring(dirNameReferedFromMountedDrive.Length); + if (Eval(list.Contains(exDir), "Err_907zbr! No file found: {0}", exDir)) + list.Remove(exDir); + } + if (!Eval(list.Count == 0, "Err_574raf! wrong count: {0}", list.Count)) + { + Console.WriteLine(); + foreach (string value in list) + Console.WriteLine(value); + } } } + finally + { + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); + } } - finally + else { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); + Console.WriteLine("Drive is not NTFS. Skipping scenario"); } } - else + catch (Exception ex) { - Console.WriteLine("Drive is not NTFS. Skipping scenario"); + s_pass = false; + Console.WriteLine("Err_432qcp! Exception caught in scenario: {0}", ex); } + + Console.WriteLine("Completed {0}", watch.Elapsed); } catch (Exception ex) { s_pass = false; - Console.WriteLine("Err_432qcp! Exception caught in scenario: {0}", ex); + Console.WriteLine("Err_234rsgf! Uncaught exception in RunTest: {0}", ex); } - Console.WriteLine("Completed {0}", watch.Elapsed); + Assert.True(s_pass); } - catch (Exception ex) + + private static void DeleteFile(string fileName) { - s_pass = false; - Console.WriteLine("Err_234rsgf! Uncaught exception in RunTest: {0}", ex); + if (File.Exists(fileName)) + File.Delete(fileName); } - Assert.True(s_pass); - } - - private static void DeleteFile(string fileName) - { - if (File.Exists(fileName)) - File.Delete(fileName); - } - - private static void DeleteDir(string fileName, bool sub) - { - if (Directory.Exists(fileName)) - Directory.Delete(fileName, sub); - } + private static void DeleteDir(string fileName, bool sub) + { + if (Directory.Exists(fileName)) + Directory.Delete(fileName, sub); + } - //Checks for error - private static bool Eval(bool expression, string msg, params object[] values) - { - return Eval(expression, string.Format(msg, values)); - } + //Checks for error + private static bool Eval(bool expression, string msg, params object[] values) + { + return Eval(expression, string.Format(msg, values)); + } - private static bool Eval(T actual, T expected, string errorMsg) - { - bool retValue = expected == null ? actual == null : expected.Equals(actual); + private static bool Eval(T actual, T expected, string errorMsg) + { + bool retValue = expected == null ? actual == null : expected.Equals(actual); - if (!retValue) - Eval(retValue, errorMsg + - " Expected:" + (null == expected ? "" : expected.ToString()) + - " Actual:" + (null == actual ? "" : actual.ToString())); + if (!retValue) + Eval(retValue, errorMsg + + " Expected:" + (null == expected ? "" : expected.ToString()) + + " Actual:" + (null == actual ? "" : actual.ToString())); - return retValue; - } + return retValue; + } - private static bool Eval(bool expression, string msg) - { - if (!expression) + private static bool Eval(bool expression, string msg) { - s_pass = false; - Console.WriteLine(msg); + if (!expression) + { + s_pass = false; + Console.WriteLine(msg); + } + return expression; } - return expression; - } - //Checks for a particular type of exception - private static void CheckException(ExceptionCode test, string error) - { - CheckException(test, error, null); - } - - //Checks for a particular type of exception and an Exception msg in the English locale - private static void CheckException(ExceptionCode test, string error, string msgExpected) - { - bool exception = false; - try + //Checks for a particular type of exception + private static void CheckException(ExceptionCode test, string error) { - test(); - error = string.Format("{0} Exception NOT thrown ", error); + CheckException(test, error, null); } - catch (Exception e) + + //Checks for a particular type of exception and an Exception msg in the English locale + private static void CheckException(ExceptionCode test, string error, string msgExpected) { - if (e.GetType() == typeof(E)) + bool exception = false; + try + { + test(); + error = string.Format("{0} Exception NOT thrown ", error); + } + catch (Exception e) { - exception = true; - if (System.Globalization.CultureInfo.CurrentUICulture.Name == "en-US" && msgExpected != null && e.Message != msgExpected) + if (e.GetType() == typeof(E)) { - exception = false; - error = string.Format("{0} Message Different: <{1}>", error, e.Message); + exception = true; + if (System.Globalization.CultureInfo.CurrentUICulture.Name == "en-US" && msgExpected != null && e.Message != msgExpected) + { + exception = false; + error = string.Format("{0} Message Different: <{1}>", error, e.Message); + } } + else + error = string.Format("{0} Exception type: {1}", error, e.GetType().Name); } - else - error = string.Format("{0} Exception type: {1}", error, e.GetType().Name); + Eval(exception, error); } - Eval(exception, error); } } diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/SetCurrentDirectory.cs b/src/libraries/System.IO.FileSystem/tests/Directory/SetCurrentDirectory.cs index 60544b0fd09eba..17efa3935788c7 100644 --- a/src/libraries/System.IO.FileSystem/tests/Directory/SetCurrentDirectory.cs +++ b/src/libraries/System.IO.FileSystem/tests/Directory/SetCurrentDirectory.cs @@ -45,7 +45,7 @@ public void SetToValidOtherDirectory() public sealed class Directory_SetCurrentDirectory_SymLink : FileSystemTest { - private static bool CanCreateSymbolicLinksAndRemoteExecutorSupported => CanCreateSymbolicLinks && RemoteExecutor.IsSupported; + private static bool CanCreateSymbolicLinksAndRemoteExecutorSupported => MountHelper.CanCreateSymbolicLinks && RemoteExecutor.IsSupported; [ConditionalFact(nameof(CanCreateSymbolicLinksAndRemoteExecutorSupported))] public void SetToPathContainingSymLink() @@ -53,7 +53,7 @@ public void SetToPathContainingSymLink() RemoteExecutor.Invoke(() => { var path = GetTestFilePath(); - var linkPath = GetTestFilePath(); + var linkPath = GetRandomLinkPath(); Directory.CreateDirectory(path); Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: true)); diff --git a/src/libraries/System.IO.FileSystem/tests/Directory/SymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/Directory/SymbolicLinks.cs index d3d04ca2e54888..43106e3c2a6b79 100644 --- a/src/libraries/System.IO.FileSystem/tests/Directory/SymbolicLinks.cs +++ b/src/libraries/System.IO.FileSystem/tests/Directory/SymbolicLinks.cs @@ -7,6 +7,7 @@ namespace System.IO.Tests { + [ConditionalClass(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public class Directory_SymbolicLinks : BaseSymbolicLinks_FileSystem { protected override bool IsDirectoryTest => true; diff --git a/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs b/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs index 1be25240b0d6a3..8a007e8df90248 100644 --- a/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs +++ b/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs @@ -119,24 +119,24 @@ public void FalseForNonRegularFile() Assert.False(di.Exists); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void SymlinkToNewDirectoryInfo() { string path = GetTestFilePath(); new DirectoryInfo(path).Create(); - string linkPath = GetTestFilePath(); + string linkPath = GetRandomLinkPath(); Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: true)); Assert.True(new DirectoryInfo(path).Exists); Assert.True(new DirectoryInfo(linkPath).Exists); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void SymLinksMayExistIndependentlyOfTarget() { var path = GetTestFilePath(); - var linkPath = GetTestFilePath(); + var linkPath = GetRandomLinkPath(); var pathFI = new DirectoryInfo(path); var linkPathFI = new DirectoryInfo(linkPath); diff --git a/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/GetSetTimes.cs b/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/GetSetTimes.cs index 795a8750e49bf2..7a52964f4c5af2 100644 --- a/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/GetSetTimes.cs +++ b/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/GetSetTimes.cs @@ -7,7 +7,9 @@ namespace System.IO.Tests { public class DirectoryInfo_GetSetTimes : InfoGetSetTimes { - protected override DirectoryInfo GetExistingItem() => Directory.CreateDirectory(GetTestFilePath()); + protected override bool CanBeReadOnly => false; + + protected override DirectoryInfo GetExistingItem(bool _) => Directory.CreateDirectory(GetTestFilePath()); protected override DirectoryInfo GetMissingItem() => new DirectoryInfo(GetTestFilePath()); diff --git a/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/SymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/SymbolicLinks.cs index 430a1da6e4f845..84658b6f275b96 100644 --- a/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/SymbolicLinks.cs +++ b/src/libraries/System.IO.FileSystem/tests/DirectoryInfo/SymbolicLinks.cs @@ -7,6 +7,7 @@ namespace System.IO.Tests { + [ConditionalClass(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public class DirectoryInfo_SymbolicLinks : BaseSymbolicLinks_FileSystemInfo { protected override bool IsDirectoryTest => true; diff --git a/src/libraries/System.IO.FileSystem/tests/Enumeration/AttributeTests.cs b/src/libraries/System.IO.FileSystem/tests/Enumeration/AttributeTests.cs index ea9d91345ff902..20b6cdf22a31de 100644 --- a/src/libraries/System.IO.FileSystem/tests/Enumeration/AttributeTests.cs +++ b/src/libraries/System.IO.FileSystem/tests/Enumeration/AttributeTests.cs @@ -55,18 +55,30 @@ protected override FileSystemEntryProperties TransformEntry(ref FileSystemEntry } } + public static IEnumerable PropertiesWhenItemNoLongerExists_Data + { + get + { + yield return new object[] { "file1", "dir2" }; + yield return new object[] { "dir1", "dir2" }; + yield return new object[] { "dir1", "file2" }; + yield return new object[] { "file1", "file2" }; + + if (MountHelper.CanCreateSymbolicLinks) + { + yield return new object[] { "dir1", "link2" }; + yield return new object[] { "file1", "link2" }; + yield return new object[] { "link1", "file2" }; + yield return new object[] { "link1", "dir2" }; + yield return new object[] { "link1", "link2" }; + } + } + } + // The test is performed using two items with different properties (file/dir, file length) // to check cached values from the previous entry don't leak into the non-existing entry. - [InlineData("dir1", "dir2")] - [InlineData("dir1", "file2")] - [InlineData("dir1", "link2")] - [InlineData("file1", "file2")] - [InlineData("file1", "dir2")] - [InlineData("file1", "link2")] - [InlineData("link1", "file2")] - [InlineData("link1", "dir2")] - [InlineData("link1", "link2")] [Theory] + [MemberData(nameof(PropertiesWhenItemNoLongerExists_Data))] public void PropertiesWhenItemNoLongerExists(string item1, string item2) { DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath()); diff --git a/src/libraries/System.IO.FileSystem/tests/Enumeration/SymbolicLinksTests.cs b/src/libraries/System.IO.FileSystem/tests/Enumeration/SymbolicLinksTests.cs index 8365b85027ce6f..c6370e77ff39df 100644 --- a/src/libraries/System.IO.FileSystem/tests/Enumeration/SymbolicLinksTests.cs +++ b/src/libraries/System.IO.FileSystem/tests/Enumeration/SymbolicLinksTests.cs @@ -8,9 +8,10 @@ namespace System.IO.Tests.Enumeration { + [ConditionalClass(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public class Enumeration_SymbolicLinksTests : BaseSymbolicLinks { - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [Fact] public void EnumerateDirectories_LinksWithCycles_ShouldNotThrow() { DirectoryInfo testDirectory = CreateDirectoryContainingSelfReferencingSymbolicLink(); @@ -29,7 +30,7 @@ public void EnumerateDirectories_LinksWithCycles_ShouldNotThrow() Assert.Equal(expected, enumerable.Count()); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [Fact] public void EnumerateFiles_LinksWithCycles_ShouldNotThrow() { DirectoryInfo testDirectory = CreateDirectoryContainingSelfReferencingSymbolicLink(); @@ -48,7 +49,7 @@ public void EnumerateFiles_LinksWithCycles_ShouldNotThrow() Assert.Equal(expected, enumerable.Count()); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [Fact] public void EnumerateFileSystemEntries_LinksWithCycles_ShouldNotThrow() { DirectoryInfo testDirectory = CreateDirectoryContainingSelfReferencingSymbolicLink(); @@ -62,7 +63,7 @@ public void EnumerateFileSystemEntries_LinksWithCycles_ShouldNotThrow() Assert.Single(enumerable); } - [ConditionalTheory(nameof(CanCreateSymbolicLinks))] + [Theory] [InlineData(false, false)] // OK [InlineData(false, true)] // throw [InlineData(true, false)] // throw, OK on Unix @@ -100,7 +101,7 @@ public void EnumerateGet_SelfReferencingLink_Instance(bool recurse, bool linkAsR } } - [ConditionalTheory(nameof(CanCreateSymbolicLinks))] + [Theory] [InlineData(false, false)] // OK [InlineData(false, true)] // throw [InlineData(true, false)] // throw, OK on Unix diff --git a/src/libraries/System.IO.FileSystem/tests/File/Delete.cs b/src/libraries/System.IO.FileSystem/tests/File/Delete.cs index b696838619b0da..04588ae495ce31 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/Delete.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/Delete.cs @@ -81,11 +81,11 @@ public void ShouldThrowIOExceptionDeletingDirectory() Assert.Throws(() => Delete(TestDirectory)); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void DeletingSymLinkDoesntDeleteTarget() { var path = GetTestFilePath(); - var linkPath = GetTestFilePath(); + var linkPath = GetRandomLinkPath(); File.Create(path).Dispose(); Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: false)); diff --git a/src/libraries/System.IO.FileSystem/tests/File/Exists.cs b/src/libraries/System.IO.FileSystem/tests/File/Exists.cs index 5e9d658602ed8b..ec3e0336a41cec 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/Exists.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/Exists.cs @@ -130,11 +130,11 @@ public void DirectoryLongerThanMaxPathAsPath_DoesntThrow() }); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void SymLinksMayExistIndependentlyOfTarget() { var path = GetTestFilePath(); - var linkPath = GetTestFilePath(); + var linkPath = GetRandomLinkPath(); File.Create(path).Dispose(); Assert.True(MountHelper.CreateSymbolicLink(linkPath, path, isDirectory: false)); diff --git a/src/libraries/System.IO.FileSystem/tests/File/GetSetTimes.cs b/src/libraries/System.IO.FileSystem/tests/File/GetSetTimes.cs index 50dcf0990a0eb6..936b7021ac2794 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/GetSetTimes.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/GetSetTimes.cs @@ -11,14 +11,22 @@ namespace System.IO.Tests { public class File_GetSetTimes : StaticGetSetTimes { + protected override bool CanBeReadOnly => true; + // OSX has the limitation of setting upto 2262-04-11T23:47:16 (long.Max) date. // 32bit Unix has time_t up to ~ 2038. private static bool SupportsLongMaxDateTime => PlatformDetection.IsWindows || (!PlatformDetection.Is32BitProcess && !PlatformDetection.IsOSXLike); - protected override string GetExistingItem() + protected override string GetExistingItem(bool readOnly = false) { string path = GetTestFilePath(); File.Create(path).Dispose(); + + if (readOnly) + { + File.SetAttributes(path, FileAttributes.ReadOnly); + } + return path; } diff --git a/src/libraries/System.IO.FileSystem/tests/File/SymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/File/SymbolicLinks.cs index e879cb353af73d..56b79f940f57e3 100644 --- a/src/libraries/System.IO.FileSystem/tests/File/SymbolicLinks.cs +++ b/src/libraries/System.IO.FileSystem/tests/File/SymbolicLinks.cs @@ -7,6 +7,7 @@ namespace System.IO.Tests { + [ConditionalClass(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public class File_SymbolicLinks : BaseSymbolicLinks_FileSystem { protected override bool IsDirectoryTest => false; diff --git a/src/libraries/System.IO.FileSystem/tests/FileInfo/Exists.cs b/src/libraries/System.IO.FileSystem/tests/FileInfo/Exists.cs index 72b8ba48ba3ab2..be6f5e90fdf0d2 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileInfo/Exists.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileInfo/Exists.cs @@ -100,11 +100,11 @@ public void TrueForNonRegularFile() Assert.True(fi.Exists); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void SymLinksMayExistIndependentlyOfTarget() { var path = GetTestFilePath(); - var linkPath = GetTestFilePath(); + var linkPath = GetRandomLinkPath(); var pathFI = new FileInfo(path); var linkPathFI = new FileInfo(linkPath); diff --git a/src/libraries/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs b/src/libraries/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs index 0559edc669e5d0..78c2781eac6613 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs @@ -10,10 +10,18 @@ namespace System.IO.Tests { public class FileInfo_GetSetTimes : InfoGetSetTimes { - protected override FileInfo GetExistingItem() + protected override bool CanBeReadOnly => true; + + protected override FileInfo GetExistingItem(bool readOnly = false) { string path = GetTestFilePath(); File.Create(path).Dispose(); + + if (readOnly) + { + File.SetAttributes(path, FileAttributes.ReadOnly); + } + return new FileInfo(path); } diff --git a/src/libraries/System.IO.FileSystem/tests/FileInfo/Length.cs b/src/libraries/System.IO.FileSystem/tests/FileInfo/Length.cs index b7c3bbe81a2afe..e77cc47ab97d2b 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileInfo/Length.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileInfo/Length.cs @@ -63,11 +63,11 @@ public void Length_MissingDirectory_ThrowsFileNotFound(char trailingChar) Assert.Throws(() => info.Length); } - [ConditionalFact(nameof(CanCreateSymbolicLinks))] + [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void SymLinkLength() { string path = GetTestFilePath(); - string linkPath = GetTestFilePath(); + string linkPath = GetRandomLinkPath(); const int FileSize = 2000; using (var tempFile = new TempFile(path, FileSize)) diff --git a/src/libraries/System.IO.FileSystem/tests/FileInfo/SymbolicLinks.cs b/src/libraries/System.IO.FileSystem/tests/FileInfo/SymbolicLinks.cs index 914edd06a1ba65..13c41150ffd5c6 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileInfo/SymbolicLinks.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileInfo/SymbolicLinks.cs @@ -6,6 +6,7 @@ namespace System.IO.Tests { + [ConditionalClass(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public class FileInfo_SymbolicLinks : BaseSymbolicLinks_FileSystemInfo { protected override bool IsDirectoryTest => false; diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/DeleteOnClose.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/DeleteOnClose.cs index ea32adc5352adc..cec39c94e64e43 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/DeleteOnClose.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/DeleteOnClose.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Xunit; @@ -63,6 +64,7 @@ public async Task OpenOrCreate_DeleteOnClose_UsableAsMutex() } }; + var sw = Stopwatch.StartNew(); var tasks = new Task[50]; for (int i = 0; i < tasks.Length; i++) { @@ -70,12 +72,13 @@ public async Task OpenOrCreate_DeleteOnClose_UsableAsMutex() } // Wait for 1000 locks. - cts.CancelAfter(TimeSpan.FromSeconds(120)); // Test timeout (reason for outerloop) + cts.CancelAfter(TimeSpan.FromSeconds(240)); // Test timeout (reason for outerloop) Volatile.Write(ref locksRemaining, 500); await Task.WhenAll(tasks); + sw.Stop(); Assert.True(exclusive, "Exclusive"); - Assert.False(cts.IsCancellationRequested, "Test cancelled"); + Assert.False(cts.IsCancellationRequested, $"Test cancelled with {locksRemaining}/500 locks remaining after {sw.Elapsed.TotalSeconds} seconds"); Assert.False(File.Exists(path), "File exists"); } } diff --git a/src/libraries/System.IO.FileSystem/tests/Junctions.Windows.cs b/src/libraries/System.IO.FileSystem/tests/Junctions.Windows.cs index c280008148977a..da5299946f44b2 100644 --- a/src/libraries/System.IO.FileSystem/tests/Junctions.Windows.cs +++ b/src/libraries/System.IO.FileSystem/tests/Junctions.Windows.cs @@ -6,6 +6,7 @@ namespace System.IO.Tests { [PlatformSpecific(TestPlatforms.Windows)] + [ConditionalClass(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public class Junctions : BaseSymbolicLinks { private DirectoryInfo CreateJunction(string junctionPath, string targetPath) diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index 476bf05913ffcd..579196928e45fc 100644 --- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -69,13 +69,11 @@ - - @@ -102,7 +100,6 @@ - @@ -167,7 +164,6 @@ - @@ -217,6 +213,7 @@ + diff --git a/src/libraries/System.IO.FileSystem/tests/VirtualDriveSymbolicLinks.Windows.cs b/src/libraries/System.IO.FileSystem/tests/VirtualDriveSymbolicLinks.Windows.cs index fb9997e6ab17ea..00242d1089f73d 100644 --- a/src/libraries/System.IO.FileSystem/tests/VirtualDriveSymbolicLinks.Windows.cs +++ b/src/libraries/System.IO.FileSystem/tests/VirtualDriveSymbolicLinks.Windows.cs @@ -8,7 +8,7 @@ namespace System.IO.Tests // Need to reuse the same virtual drive for all the test methods. // Creating and disposing one virtual drive per class achieves this. [PlatformSpecific(TestPlatforms.Windows)] - [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsSubstAvailable))] + [ConditionalClass(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks), nameof(MountHelper.IsSubstAvailable))] public class VirtualDrive_SymbolicLinks : BaseSymbolicLinks { private VirtualDriveHelper VirtualDrive { get; } = new VirtualDriveHelper(); diff --git a/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj b/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj index 20eb7e5484fd3b..f05f45948ebd70 100644 --- a/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj +++ b/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj @@ -4,8 +4,6 @@ enable $(NetCoreAppCurrent);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) true - - true Provides non-cryptographic hash algorithms, such as CRC-32. Commonly Used Types: diff --git a/src/libraries/System.IO.Packaging/src/CompatibilitySuppressions.xml b/src/libraries/System.IO.Packaging/src/CompatibilitySuppressions.xml deleted file mode 100644 index c4a6f07ed08c6a..00000000000000 --- a/src/libraries/System.IO.Packaging/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - PKV006 - .NETFramework,Version=v4.6 - - - PKV006 - .NETStandard,Version=v1.3 - - \ No newline at end of file diff --git a/src/libraries/System.IO.Pipelines/src/CompatibilitySuppressions.xml b/src/libraries/System.IO.Pipelines/src/CompatibilitySuppressions.xml deleted file mode 100644 index e322081124afec..00000000000000 --- a/src/libraries/System.IO.Pipelines/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - .NETStandard,Version=v1.3 - - \ No newline at end of file diff --git a/src/libraries/System.IO.Pipelines/tests/PipeReaderWriterFacts.cs b/src/libraries/System.IO.Pipelines/tests/PipeReaderWriterFacts.cs index f4b41418a6afc4..331216e30f322c 100644 --- a/src/libraries/System.IO.Pipelines/tests/PipeReaderWriterFacts.cs +++ b/src/libraries/System.IO.Pipelines/tests/PipeReaderWriterFacts.cs @@ -228,8 +228,6 @@ public async Task ReadAsync_ThrowsIfWriterCompletedWithException() invalidOperationException = await Assert.ThrowsAsync(async () => await _pipe.Reader.ReadAsync()); Assert.Equal("Writer exception", invalidOperationException.Message); Assert.Contains(nameof(ThrowTestException), invalidOperationException.StackTrace); - - Assert.Single(Regex.Matches(invalidOperationException.StackTrace, "Pipe.GetReadResult")); } [Fact] diff --git a/src/libraries/System.IO.Ports/pkg/runtime.osx-arm64.runtime.native.System.IO.Ports.proj b/src/libraries/System.IO.Ports/pkg/runtime.osx-arm64.runtime.native.System.IO.Ports.proj index e6410bd635cd22..800eff44ee6744 100644 --- a/src/libraries/System.IO.Ports/pkg/runtime.osx-arm64.runtime.native.System.IO.Ports.proj +++ b/src/libraries/System.IO.Ports/pkg/runtime.osx-arm64.runtime.native.System.IO.Ports.proj @@ -1,7 +1,7 @@ - + true diff --git a/src/libraries/System.IO.Ports/tests/SerialPort/DosDevices.cs b/src/libraries/System.IO.Ports/tests/SerialPort/DosDevices.cs index 3fe9493c0a30e5..2d74327f97eb0d 100644 --- a/src/libraries/System.IO.Ports/tests/SerialPort/DosDevices.cs +++ b/src/libraries/System.IO.Ports/tests/SerialPort/DosDevices.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Runtime.InteropServices; namespace System.IO.Ports.Tests @@ -127,14 +128,20 @@ private static char[] CallQueryDosDevice(string name, out int dataSize) buffer = new char[buffer.Length * 2]; dataSize = QueryDosDevice(null, buffer, buffer.Length); } + else if (lastError == ERROR_ACCESS_DENIED) // Access denied eg for "MSSECFLTSYS" - just skip + { + dataSize = 0; + break; + } else { - throw new Exception("Unknown Win32 Error: " + lastError); + throw new Exception($"Error {lastError} calling QueryDosDevice for '{name}' with buffer size {buffer.Length}. {new Win32Exception((int)lastError).Message}"); } } return buffer; } + public const int ERROR_ACCESS_DENIED = 5; public const int ERROR_INSUFFICIENT_BUFFER = 122; public const int ERROR_MORE_DATA = 234; diff --git a/src/libraries/System.IO.Ports/tests/SerialPort/ctor.cs b/src/libraries/System.IO.Ports/tests/SerialPort/ctor.cs index f710b6808a96ba..a099506b8035d0 100644 --- a/src/libraries/System.IO.Ports/tests/SerialPort/ctor.cs +++ b/src/libraries/System.IO.Ports/tests/SerialPort/ctor.cs @@ -8,7 +8,7 @@ namespace System.IO.Ports.Tests { - public class ctor : PortsTest + public class Ctor : PortsTest { [Fact] public void Verify() diff --git a/src/libraries/System.IO/tests/StringReader/StringReader.CtorTests.cs b/src/libraries/System.IO/tests/StringReader/StringReader.CtorTests.cs index 8ff84e80c06950..9e500923164f38 100644 --- a/src/libraries/System.IO/tests/StringReader/StringReader.CtorTests.cs +++ b/src/libraries/System.IO/tests/StringReader/StringReader.CtorTests.cs @@ -59,7 +59,7 @@ public static void ReadLine() using (StringReader sr = new StringReader(str1)) { - Assert.Equal(str1, sr.ReadLine()); + Assert.Same(str1, sr.ReadLine()); } using (StringReader sr = new StringReader(str2)) { diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.cs index 35dae2e3a1102c..ced4a207e16496 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.cs @@ -109,7 +109,7 @@ private static bool IsByRef(DynamicMetaObject mo) private static Type MakeNewCustomDelegate(Type[] types) { - if (LambdaExpression.CanCompileToIL) + if (RuntimeFeature.IsDynamicCodeSupported) { Type returnType = types[types.Length - 1]; Type[] parameters = types.RemoveLast(); diff --git a/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Logical/BinaryLogicalTests.cs b/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Logical/BinaryLogicalTests.cs index c1db2afa8e1462..45ffdbde7c730c 100644 --- a/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Logical/BinaryLogicalTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/BinaryOperators/Logical/BinaryLogicalTests.cs @@ -415,7 +415,7 @@ public static void MethodDeclaringTypeHasNoTrueFalseOperator_ThrowsArgumentExcep AssertExtensions.Throws(null, () => Expression.OrElse(Expression.Constant(5), Expression.Constant(5), method)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void AndAlso_NoMethod_NotStatic_ThrowsInvalidOperationException() { TypeBuilder type = GetTypeBuilder(); @@ -428,7 +428,7 @@ public static void AndAlso_NoMethod_NotStatic_ThrowsInvalidOperationException() Assert.Throws(() => Expression.AndAlso(Expression.Constant(obj), Expression.Constant(obj))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void OrElse_NoMethod_NotStatic_ThrowsInvalidOperationException() { TypeBuilder type = GetTypeBuilder(); @@ -441,7 +441,7 @@ public static void OrElse_NoMethod_NotStatic_ThrowsInvalidOperationException() Assert.Throws(() => Expression.OrElse(Expression.Constant(obj), Expression.Constant(obj))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void AndAlso_NoMethod_VoidReturnType_ThrowsArgumentException() { TypeBuilder type = GetTypeBuilder(); @@ -454,7 +454,7 @@ public static void AndAlso_NoMethod_VoidReturnType_ThrowsArgumentException() AssertExtensions.Throws("method", () => Expression.AndAlso(Expression.Constant(obj), Expression.Constant(obj))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void OrElse_NoMethod_VoidReturnType_ThrowsArgumentException() { TypeBuilder type = GetTypeBuilder(); @@ -467,7 +467,7 @@ public static void OrElse_NoMethod_VoidReturnType_ThrowsArgumentException() AssertExtensions.Throws("method", () => Expression.OrElse(Expression.Constant(obj), Expression.Constant(obj))); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [InlineData(0)] [InlineData(1)] [InlineData(3)] @@ -483,7 +483,7 @@ public static void AndAlso_NoMethod_DoesntHaveTwoParameters_ThrowsInvalidOperati Assert.Throws(() => Expression.AndAlso(Expression.Constant(obj), Expression.Constant(obj))); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [InlineData(0)] [InlineData(1)] [InlineData(3)] @@ -499,7 +499,7 @@ public static void OrElse_NoMethod_DoesntHaveTwoParameters_ThrowsInvalidOperatio Assert.Throws(() => Expression.OrElse(Expression.Constant(obj), Expression.Constant(obj))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void AndAlso_NoMethod_ExpressionDoesntMatchMethodParameters_ThrowsInvalidOperationException() { TypeBuilder type = GetTypeBuilder(); @@ -512,7 +512,7 @@ public static void AndAlso_NoMethod_ExpressionDoesntMatchMethodParameters_Throws Assert.Throws(() => Expression.AndAlso(Expression.Constant(obj), Expression.Constant(obj))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void OrElse_NoMethod_ExpressionDoesntMatchMethodParameters_ThrowsInvalidOperationException() { TypeBuilder type = GetTypeBuilder(); @@ -526,7 +526,7 @@ public static void OrElse_NoMethod_ExpressionDoesntMatchMethodParameters_ThrowsI } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void AndAlso_NoMethod_ReturnTypeNotEqualToParameterTypes_ThrowsArgumentException() { TypeBuilder type = GetTypeBuilder(); @@ -539,7 +539,7 @@ public static void AndAlso_NoMethod_ReturnTypeNotEqualToParameterTypes_ThrowsArg AssertExtensions.Throws(null, () => Expression.AndAlso(Expression.Constant(obj), Expression.Constant(obj))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void OrElse_NoMethod_ReturnTypeNotEqualToParameterTypes_ThrowsArgumentException() { TypeBuilder type = GetTypeBuilder(); @@ -569,7 +569,7 @@ public static IEnumerable Operator_IncorrectMethod_TestData() yield return new object[] { GetTypeBuilder(), typeof(bool), new Type[0] }; } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [MemberData(nameof(Operator_IncorrectMethod_TestData))] public static void Method_TrueOperatorIncorrectMethod_ThrowsArgumentException(TypeBuilder builder, Type returnType, Type[] parameterTypes) { @@ -590,7 +590,7 @@ public static void Method_TrueOperatorIncorrectMethod_ThrowsArgumentException(Ty AssertExtensions.Throws(null, () => Expression.OrElse(Expression.Constant(obj), Expression.Constant(obj), createdMethod)); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [MemberData(nameof(Operator_IncorrectMethod_TestData))] public static void Method_FalseOperatorIncorrectMethod_ThrowsArgumentException(TypeBuilder builder, Type returnType, Type[]parameterTypes) { @@ -611,7 +611,7 @@ public static void Method_FalseOperatorIncorrectMethod_ThrowsArgumentException(T AssertExtensions.Throws(null, () => Expression.OrElse(Expression.Constant(obj), Expression.Constant(obj), createdMethod)); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [MemberData(nameof(Operator_IncorrectMethod_TestData))] public static void AndAlso_NoMethod_TrueOperatorIncorrectMethod_ThrowsArgumentException(TypeBuilder builder, Type returnType, Type[] parameterTypes) { @@ -630,7 +630,7 @@ public static void AndAlso_NoMethod_TrueOperatorIncorrectMethod_ThrowsArgumentEx AssertExtensions.Throws(null, () => Expression.AndAlso(Expression.Constant(obj), Expression.Constant(obj))); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [MemberData(nameof(Operator_IncorrectMethod_TestData))] public static void OrElse_NoMethod_TrueOperatorIncorrectMethod_ThrowsArgumentException(TypeBuilder builder, Type returnType, Type[] parameterTypes) { @@ -649,7 +649,7 @@ public static void OrElse_NoMethod_TrueOperatorIncorrectMethod_ThrowsArgumentExc AssertExtensions.Throws(null, () => Expression.OrElse(Expression.Constant(obj), Expression.Constant(obj))); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [InlineData("op_True")] [InlineData("op_False")] public static void Method_NoTrueFalseOperator_ThrowsArgumentException(string name) @@ -672,7 +672,7 @@ public static void Method_NoTrueFalseOperator_ThrowsArgumentException(string nam AssertExtensions.Throws(null, () => Expression.OrElse(Expression.Constant(obj), Expression.Constant(obj), createdMethod)); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [InlineData("op_True")] [InlineData("op_False")] public static void AndAlso_NoMethod_NoTrueFalseOperator_ThrowsArgumentException(string name) @@ -692,7 +692,7 @@ public static void AndAlso_NoMethod_NoTrueFalseOperator_ThrowsArgumentException( AssertExtensions.Throws(null, () => Expression.AndAlso(Expression.Constant(obj), Expression.Constant(obj))); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [InlineData("op_True")] [InlineData("op_False")] public static void OrElse_NoMethod_NoTrueFalseOperator_ThrowsArgumentException(string name) @@ -712,7 +712,7 @@ public static void OrElse_NoMethod_NoTrueFalseOperator_ThrowsArgumentException(s AssertExtensions.Throws(null, () => Expression.OrElse(Expression.Constant(obj), Expression.Constant(obj))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void Method_ParamsDontMatchOperator_ThrowsInvalidOperationException() { TypeBuilder builder = GetTypeBuilder(); @@ -733,7 +733,7 @@ public static void Method_ParamsDontMatchOperator_ThrowsInvalidOperationExceptio Assert.Throws(() => Expression.OrElse(Expression.Constant(5), Expression.Constant(5), createdMethod)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void AndAlso_NoMethod_ParamsDontMatchOperator_ThrowsInvalidOperationException() { TypeBuilder builder = GetTypeBuilder(); @@ -751,7 +751,7 @@ public static void AndAlso_NoMethod_ParamsDontMatchOperator_ThrowsInvalidOperati Assert.Throws(() => Expression.OrElse(Expression.Constant(5), Expression.Constant(5))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void OrElse_NoMethod_ParamsDontMatchOperator_ThrowsInvalidOperationException() { TypeBuilder builder = GetTypeBuilder(); @@ -817,14 +817,14 @@ public static void ToStringTest() Assert.Equal("(a OrElse b)", e2.ToString()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void AndAlsoGlobalMethod() { MethodInfo method = GlobalMethod(typeof(int), new[] { typeof(int), typeof(int) }); AssertExtensions.Throws(null, () => Expression.AndAlso(Expression.Constant(1), Expression.Constant(2), method)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void OrElseGlobalMethod() { MethodInfo method = GlobalMethod(typeof(int), new [] { typeof(int), typeof(int) }); diff --git a/src/libraries/System.Linq.Expressions/tests/Cast/CastTests.cs b/src/libraries/System.Linq.Expressions/tests/Cast/CastTests.cs index 64e20adf5cfddc..0f0294a58465c4 100644 --- a/src/libraries/System.Linq.Expressions/tests/Cast/CastTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Cast/CastTests.cs @@ -2373,7 +2373,7 @@ public static IEnumerable EnumerableTypes() yield return typeof(Int64Enum); yield return typeof(UInt64Enum); - if (PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly) + if (PlatformDetection.IsReflectionEmitSupported) { yield return NonCSharpTypes.CharEnumType; yield return NonCSharpTypes.BoolEnumType; diff --git a/src/libraries/System.Linq.Expressions/tests/Constant/ConstantTests.cs b/src/libraries/System.Linq.Expressions/tests/Constant/ConstantTests.cs index 40ff43d0d5ff0e..2483f1756e52d4 100644 --- a/src/libraries/System.Linq.Expressions/tests/Constant/ConstantTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Constant/ConstantTests.cs @@ -302,7 +302,7 @@ public static void CheckTypeConstantTest(bool useInterpreter) VerifyTypeConstant(value, useInterpreter); } - if (PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly) + if (PlatformDetection.IsReflectionEmitSupported) { VerifyTypeConstant(GetTypeBuilder(), useInterpreter); } @@ -328,14 +328,24 @@ public static void CheckMethodInfoConstantTest(bool useInterpreter) typeof(SomePublicMethodsForLdToken).GetMethod(nameof(SomePublicMethodsForLdToken.Qux), BindingFlags.Public | BindingFlags.Static), typeof(SomePublicMethodsForLdToken).GetMethod(nameof(SomePublicMethodsForLdToken.Qux), BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(typeof(int)), typeof(List<>).GetMethod(nameof(List.Add)), - typeof(List).GetMethod(nameof(List.Add)), - GlobalMethod(Type.EmptyTypes), - GlobalMethod(typeof(PrivateGenericClass)), - GlobalMethod(typeof(PrivateGenericClass<>)) + typeof(List).GetMethod(nameof(List.Add)) }) { VerifyMethodInfoConstant(value, useInterpreter); } + + if (PlatformDetection.IsReflectionEmitSupported) + { + foreach (MethodInfo value in new MethodInfo[] + { + GlobalMethod(Type.EmptyTypes), + GlobalMethod(typeof(PrivateGenericClass)), + GlobalMethod(typeof(PrivateGenericClass<>)) + }) + { + VerifyMethodInfoConstant(value, useInterpreter); + } + } } [Theory, ClassData(typeof(CompilationTypes))] diff --git a/src/libraries/System.Linq.Expressions/tests/DelegateType/GetDelegateTypeTests.cs b/src/libraries/System.Linq.Expressions/tests/DelegateType/GetDelegateTypeTests.cs index 9d0852a8479164..a627772d7e6804 100644 --- a/src/libraries/System.Linq.Expressions/tests/DelegateType/GetDelegateTypeTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/DelegateType/GetDelegateTypeTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Xunit; @@ -57,7 +58,7 @@ public void GetNullaryAction() [MemberData(nameof(ManagedPointerTypeArgs))] public void CantBeFunc(Type[] typeArgs) { - if (PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly) + if (!RuntimeFeature.IsDynamicCodeSupported) { Assert.Throws(() => Expression.GetDelegateType(typeArgs)); } @@ -83,7 +84,7 @@ public void CantBeFunc(Type[] typeArgs) public void CantBeAction(Type[] typeArgs) { Type[] delegateArgs = typeArgs.Append(typeof(void)).ToArray(); - if (PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly) + if (!RuntimeFeature.IsDynamicCodeSupported) { Assert.Throws(() => Expression.GetDelegateType(delegateArgs)); } diff --git a/src/libraries/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs b/src/libraries/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs index 616f2234524765..934478e72d1177 100644 --- a/src/libraries/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs @@ -54,7 +54,7 @@ public static IEnumerable ObjectArguments() yield return new[] {new object()}; } - [ActiveIssue("https://github.com/dotnet/runtime/issues/55070", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55070", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] [Theory, MemberData(nameof(ObjectArguments))] public void InvokeVirtualMethod(object value) { @@ -231,8 +231,7 @@ private static dynamic GetObjectWithNonIndexerParameterProperty(bool hasGetter, return Activator.CreateInstance(typeBuild.CreateType()); } - // We're not testing compilation, but we do need Reflection.Emit for the test - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void NonIndexerParameterizedDirectAccess() { // If a parameterized property isn't the type's indexer, we should be allowed to use the @@ -243,8 +242,7 @@ public void NonIndexerParameterizedDirectAccess() Assert.Equal(19, value); } - // We're not testing compilation, but we do need Reflection.Emit for the test - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void NonIndexerParameterizedGetterAndSetterIndexAccess() { dynamic d = GetObjectWithNonIndexerParameterProperty(true, true); @@ -254,8 +252,7 @@ public void NonIndexerParameterizedGetterAndSetterIndexAccess() Assert.Contains("set_ItemProp", ex.Message); } - // We're not testing compilation, but we do need Reflection.Emit for the test - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void NonIndexerParameterizedGetterOnlyIndexAccess() { dynamic d = GetObjectWithNonIndexerParameterProperty(true, false); @@ -265,8 +262,7 @@ public void NonIndexerParameterizedGetterOnlyIndexAccess() Assert.Contains("get_ItemProp", ex.Message); } - // We're not testing compilation, but we do need Reflection.Emit for the test - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void NonIndexerParameterizedSetterOnlyIndexAccess() { dynamic d = GetObjectWithNonIndexerParameterProperty(false, true); @@ -335,8 +331,7 @@ public int GetValue2( int arg10) => 11; } - // We're not testing compilation, but we do need Reflection.Emit for the test - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [Fact] public void ManyArities() { dynamic d = new ManyOverloads(); @@ -380,8 +375,7 @@ public static IEnumerable SameNameObjectPairs() return testObjects.SelectMany(i => testObjects.Select(j => new[] { i, j })); } - // We're not testing compilation, but we do need Reflection.Emit for the test - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [Theory] [MemberData(nameof(SameNameObjectPairs))] public void OperationOnTwoObjectsDifferentTypesOfSameName(object x, object y) { diff --git a/src/libraries/System.Linq.Expressions/tests/ExceptionHandling/ExceptionHandlingExpressions.cs b/src/libraries/System.Linq.Expressions/tests/ExceptionHandling/ExceptionHandlingExpressions.cs index 26ca0e2ea5119e..40e794a5f69357 100644 --- a/src/libraries/System.Linq.Expressions/tests/ExceptionHandling/ExceptionHandlingExpressions.cs +++ b/src/libraries/System.Linq.Expressions/tests/ExceptionHandling/ExceptionHandlingExpressions.cs @@ -240,7 +240,7 @@ public void ExpressionsUnwrapeExternallyThrownRuntimeWrappedException(bool useIn Assert.Equal(4, func()); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [ClassData(typeof(CompilationTypes))] public void CatchFromExternallyThrownString(bool useInterpreter) { diff --git a/src/libraries/System.Linq.Expressions/tests/IndexExpression/IndexExpressionTests.cs b/src/libraries/System.Linq.Expressions/tests/IndexExpression/IndexExpressionTests.cs index 98a436c9f4a17f..8fb36788766d74 100644 --- a/src/libraries/System.Linq.Expressions/tests/IndexExpression/IndexExpressionTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/IndexExpression/IndexExpressionTests.cs @@ -97,7 +97,7 @@ private static TypeBuilder GetTestTypeBuilder() => .DefineDynamicModule("TestModule") .DefineType("TestType"); - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [ActiveIssue("https://github.com/mono/mono/issues/14920", TestRuntimes.Mono)] public void NoAccessorIndexedProperty() { @@ -113,7 +113,7 @@ public void NoAccessorIndexedProperty() AssertExtensions.Throws("propertyName", () => Expression.Property(instance, "Item", Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void ByRefIndexedProperty() { TypeBuilder typeBuild = GetTestTypeBuilder(); @@ -144,7 +144,7 @@ public void ByRefIndexedProperty() AssertExtensions.Throws("propertyName", () => Expression.Property(instance, "Item", Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void VoidIndexedProperty() { TypeBuilder typeBuild = GetTestTypeBuilder(); @@ -172,7 +172,7 @@ public void VoidIndexedProperty() AssertExtensions.Throws("propertyName", () => Expression.Property(instance, "Item", Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [ActiveIssue("https://github.com/mono/mono/issues/14927", TestRuntimes.Mono)] public void IndexedPropertyGetReturnsWrongType() { @@ -201,7 +201,7 @@ public void IndexedPropertyGetReturnsWrongType() AssertExtensions.Throws("propertyName", () => Expression.Property(instance, "Item", Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void IndexedPropertySetterNoParams() { TypeBuilder typeBuild = GetTestTypeBuilder(); @@ -229,7 +229,7 @@ public void IndexedPropertySetterNoParams() AssertExtensions.Throws("propertyName", () => Expression.Property(instance, "Item", Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void IndexedPropertySetterByrefValueType() { TypeBuilder typeBuild = GetTestTypeBuilder(); @@ -257,7 +257,7 @@ public void IndexedPropertySetterByrefValueType() AssertExtensions.Throws("propertyName", () => Expression.Property(instance, "Item", Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void IndexedPropertySetterNotReturnVoid() { TypeBuilder typeBuild = GetTestTypeBuilder(); @@ -285,7 +285,7 @@ public void IndexedPropertySetterNotReturnVoid() AssertExtensions.Throws("propertyName", () => Expression.Property(instance, "Item", Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void IndexedPropertyGetterInstanceSetterStatic() { TypeBuilder typeBuild = GetTestTypeBuilder(); @@ -325,7 +325,7 @@ public void IndexedPropertyGetterInstanceSetterStatic() AssertExtensions.Throws("propertyName", () => Expression.Property(instance, "Item", Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [ActiveIssue("https://github.com/mono/mono/issues/14927", TestRuntimes.Mono)] public void IndexedPropertySetterValueTypeNotMatchPropertyType() { @@ -354,7 +354,7 @@ public void IndexedPropertySetterValueTypeNotMatchPropertyType() AssertExtensions.Throws("propertyName", () => Expression.Property(instance, "Item", Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void IndexedPropertyGetterSetterArgCountMismatch() { TypeBuilder typeBuild = GetTestTypeBuilder(); @@ -394,7 +394,7 @@ public void IndexedPropertyGetterSetterArgCountMismatch() AssertExtensions.Throws("propertyName", () => Expression.Property(instance, "Item", Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void IndexedPropertyGetterSetterArgumentTypeMismatch() { TypeBuilder typeBuild = GetTestTypeBuilder(); @@ -434,7 +434,7 @@ public void IndexedPropertyGetterSetterArgumentTypeMismatch() AssertExtensions.Throws("propertyName", () => Expression.Property(instance, "Item", Expression.Constant(0), Expression.Constant(0), Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void IndexedPropertyVarArgs() { TypeBuilder typeBuild = GetTestTypeBuilder(); @@ -464,7 +464,7 @@ public void IndexedPropertyVarArgs() AssertExtensions.Throws("propertyName", () => Expression.Property(instance, "Item", Expression.Constant(0), Expression.Constant(0), Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [Fact] public void NullInstanceInstanceProperty() { PropertyInfo prop = typeof(Dictionary).GetProperty("Item"); @@ -472,7 +472,7 @@ public void NullInstanceInstanceProperty() AssertExtensions.Throws("instance", () => Expression.Property(null, prop, index)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void InstanceToStaticProperty() { TypeBuilder typeBuild = GetTestTypeBuilder(); @@ -500,7 +500,7 @@ public void InstanceToStaticProperty() AssertExtensions.Throws("instance", () => Expression.Property(instance, prop, Expression.Constant(0))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void ByRefIndexer() { TypeBuilder typeBuild = GetTestTypeBuilder(); diff --git a/src/libraries/System.Linq.Expressions/tests/InterpreterTests.cs b/src/libraries/System.Linq.Expressions/tests/InterpreterTests.cs index 4298013f5f59c5..9a71ecf4d35ffd 100644 --- a/src/libraries/System.Linq.Expressions/tests/InterpreterTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/InterpreterTests.cs @@ -101,9 +101,7 @@ .maxcontinuation 1 }"); } - // IsNotLinqExpressionsBuiltWithIsInterpretingOnly is not directly required, - // but this functionality relies on private reflection and that would not work with AOT - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [Fact] [ActiveIssue ("https://github.com/dotnet/runtime/issues/53599", platforms: TestPlatforms.MacCatalyst, runtimes: TestRuntimes.Mono)] public static void ConstructorThrows_StackTrace() { @@ -112,9 +110,7 @@ public static void ConstructorThrows_StackTrace() AssertStackTrace(() => f(), "Thrower..ctor"); } - // IsNotLinqExpressionsBuiltWithIsInterpretingOnly is not directly required, - // but this functionality relies on private reflection and that would not work with AOT - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [Fact] public static void PropertyGetterThrows_StackTrace() { Expression> e = t => t.Bar; @@ -122,9 +118,7 @@ public static void PropertyGetterThrows_StackTrace() AssertStackTrace(() => f(new Thrower(error: false)), "Thrower.get_Bar"); } - // IsNotLinqExpressionsBuiltWithIsInterpretingOnly is not directly required, - // but this functionality relies on private reflection and that would not work with AOT - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [Fact] public static void PropertySetterThrows_StackTrace() { ParameterExpression t = Expression.Parameter(typeof(Thrower), "t"); @@ -133,9 +127,7 @@ public static void PropertySetterThrows_StackTrace() AssertStackTrace(() => f(new Thrower(error: false)), "Thrower.set_Bar"); } - // IsNotLinqExpressionsBuiltWithIsInterpretingOnly is not directly required, - // but this functionality relies on private reflection and that would not work with AOT - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [Fact] public static void IndexerGetterThrows_StackTrace() { ParameterExpression t = Expression.Parameter(typeof(Thrower), "t"); @@ -144,9 +136,7 @@ public static void IndexerGetterThrows_StackTrace() AssertStackTrace(() => f(new Thrower(error: false)), "Thrower.get_Item"); } - // IsNotLinqExpressionsBuiltWithIsInterpretingOnly is not directly required, - // but this functionality relies on private reflection and that would not work with AOT - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [Fact] public static void IndexerSetterThrows_StackTrace() { ParameterExpression t = Expression.Parameter(typeof(Thrower), "t"); @@ -155,9 +145,7 @@ public static void IndexerSetterThrows_StackTrace() AssertStackTrace(() => f(new Thrower(error: false)), "Thrower.set_Item"); } - // IsNotLinqExpressionsBuiltWithIsInterpretingOnly is not directly required, - // but this functionality relies on private reflection and that would not work with AOT - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [Fact] public static void MethodThrows_StackTrace() { Expression> e = t => t.Foo(); diff --git a/src/libraries/System.Linq.Expressions/tests/Invoke/InvocationTests.cs b/src/libraries/System.Linq.Expressions/tests/Invoke/InvocationTests.cs index 829e7e435ba331..14fa4ca58fae18 100644 --- a/src/libraries/System.Linq.Expressions/tests/Invoke/InvocationTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Invoke/InvocationTests.cs @@ -244,8 +244,7 @@ public static void LambdaAndArgChangeVisit(InvocationExpression invoke) Assert.NotSame(invoke, new ParameterAndConstantChangingVisitor().Visit(invoke)); } - // When we don't have IsNotLinqExpressionsBuiltWithIsInterpretingOnly we don't have the Reflection.Emit used in the tests. - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [ClassData(typeof(CompilationTypes))] public static void InvokePrivateDelegate(bool useInterpreter) { @@ -263,8 +262,7 @@ public static void InvokePrivateDelegate(bool useInterpreter) Assert.Equal(42, invFunc()); } - // When we don't have IsNotLinqExpressionsBuiltWithIsInterpretingOnly we don't have the Reflection.Emit used in the tests. - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [ClassData(typeof(CompilationTypes))] public static void InvokePrivateDelegateTypeLambda(bool useInterpreter) { diff --git a/src/libraries/System.Linq.Expressions/tests/Lambda/LambdaTests.cs b/src/libraries/System.Linq.Expressions/tests/Lambda/LambdaTests.cs index c62a6ed60273e2..a72d32875128ee 100644 --- a/src/libraries/System.Linq.Expressions/tests/Lambda/LambdaTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Lambda/LambdaTests.cs @@ -6,6 +6,7 @@ using System.Collections.ObjectModel; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.CompilerServices; using Xunit; namespace System.Linq.Expressions.Tests @@ -268,9 +269,9 @@ public void ImplicitlyTyped() double, double, double, double, bool>), exp.Type); - // From this point on, the tests require IsLinqExpressionsBuiltWithIsInterpretingOnly (RefEmit) support as SLE needs to create delegate types on the fly. + // From this point on, the tests require Ref.Emit support as SLE needs to create delegate types on the fly. // You can't instantiate Func<> over 20 arguments or over byrefs. - if (PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly) + if (!RuntimeFeature.IsDynamicCodeSupported) return; ParameterExpression[] paramList = Enumerable.Range(0, 20).Select(_ => Expression.Variable(typeof(int))).ToArray(); @@ -924,8 +925,7 @@ public void OpenGenericDelegate() AssertExtensions.Throws("delegateType", () => Expression.Lambda(typeof(Action<>), Expression.Empty(), false, Enumerable.Empty())); } - // When we don't have IsLinqExpressionsBuiltWithIsInterpretingOnly we don't have the Reflection.Emit used in the tests. - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [ClassData(typeof(CompilationTypes))] public void PrivateDelegate(bool useInterpreter) { diff --git a/src/libraries/System.Linq.Expressions/tests/Member/MemberAccessTests.cs b/src/libraries/System.Linq.Expressions/tests/Member/MemberAccessTests.cs index 78466bcd842eee..ae9913e9753d2a 100644 --- a/src/libraries/System.Linq.Expressions/tests/Member/MemberAccessTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/Member/MemberAccessTests.cs @@ -552,7 +552,7 @@ public static void MakeMemberAccess_MemberNotFieldOrProperty_ThrowsArgumentExcep AssertExtensions.Throws("member", () => Expression.MakeMemberAccess(Expression.Constant(new PC()), member)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [ActiveIssue("https://github.com/mono/mono/issues/14920", TestRuntimes.Mono)] public static void Property_NoGetOrSetAccessors_ThrowsArgumentException() { diff --git a/src/libraries/System.Linq.Expressions/tests/MemberInit/BindTests.cs b/src/libraries/System.Linq.Expressions/tests/MemberInit/BindTests.cs index 8777d108110b09..cf0e4676f71c09 100644 --- a/src/libraries/System.Linq.Expressions/tests/MemberInit/BindTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/MemberInit/BindTests.cs @@ -284,7 +284,7 @@ public void BogusBindingType(MemberBinding binding) AssertExtensions.Throws("bindings[0]", () => Expression.MemberInit(Expression.New(typeof(PropertyAndFields)), binding)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void GlobalMethod() { ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); @@ -295,7 +295,7 @@ public void GlobalMethod() AssertExtensions.Throws("propertyAccessor", () => Expression.Bind(globalMethodInfo, Expression.Constant(2))); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void GlobalField() { ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); diff --git a/src/libraries/System.Linq.Expressions/tests/MemberInit/ListBindTests.cs b/src/libraries/System.Linq.Expressions/tests/MemberInit/ListBindTests.cs index 3b25685e8368fe..2c607f433a47e7 100644 --- a/src/libraries/System.Linq.Expressions/tests/MemberInit/ListBindTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/MemberInit/ListBindTests.cs @@ -290,7 +290,7 @@ public void OpenGenericTypesMembers() AssertExtensions.Throws(null, () => Expression.ListBind(method)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void GlobalMethod() { ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); diff --git a/src/libraries/System.Linq.Expressions/tests/MemberInit/MemberBindTests.cs b/src/libraries/System.Linq.Expressions/tests/MemberInit/MemberBindTests.cs index 8a72d0e39849e2..d3122945ab0b8d 100644 --- a/src/libraries/System.Linq.Expressions/tests/MemberInit/MemberBindTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/MemberInit/MemberBindTests.cs @@ -253,7 +253,7 @@ public void StaticReadonlyInnerField(bool useInterpreter) Assert.Throws(() => exp.Compile(useInterpreter)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void GlobalMethod() { ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); diff --git a/src/libraries/System.Linq.Expressions/tests/New/NewTests.cs b/src/libraries/System.Linq.Expressions/tests/New/NewTests.cs index f056ed3df36c1c..e6493c5a6d3003 100644 --- a/src/libraries/System.Linq.Expressions/tests/New/NewTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/New/NewTests.cs @@ -555,7 +555,7 @@ public static void OpenGenericConstructorsInvalid(ConstructorInfo ctor, Expressi } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void GlobalMethodInMembers() { ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); @@ -569,7 +569,7 @@ public static void GlobalMethodInMembers() AssertExtensions.Throws("members[0]", () => Expression.New(constructor, arguments, members)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static void GlobalFieldInMembers() { ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); diff --git a/src/libraries/System.Linq.Expressions/tests/New/NewWithByRefParameterTests.cs b/src/libraries/System.Linq.Expressions/tests/New/NewWithByRefParameterTests.cs index 04804eef82f9b4..e20bba67732241 100644 --- a/src/libraries/System.Linq.Expressions/tests/New/NewWithByRefParameterTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/New/NewWithByRefParameterTests.cs @@ -70,7 +70,8 @@ public void CreateByRefAliasingInterpreted() CreateByRefAliasing(useInterpreter: true); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotLinqExpressionsBuiltWithIsInterpretingOnly))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/19286", typeof(PlatformDetection), nameof(PlatformDetection.IsLinqExpressionsBuiltWithIsInterpretingOnly))] + [Fact] public void CreateByRefAliasingCompiled() { CreateByRefAliasing(useInterpreter: false); diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/CompatibilitySuppressions.xml b/src/libraries/System.Net.Http.WinHttpHandler/src/CompatibilitySuppressions.xml deleted file mode 100644 index cc1f8cf96806e3..00000000000000 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - PKV006 - .NETFramework,Version=v4.6 - - - PKV006 - .NETStandard,Version=v1.3 - - - PKV007 - .NETFramework,Version=v4.6-win - - - PKV007 - .NETStandard,Version=v1.3-win - - \ No newline at end of file diff --git a/src/libraries/System.Net.Http/src/Resources/Strings.resx b/src/libraries/System.Net.Http/src/Resources/Strings.resx index a6972e36a6be5b..7464b6aa14086a 100644 --- a/src/libraries/System.Net.Http/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Http/src/Resources/Strings.resx @@ -309,6 +309,9 @@ The server returned an invalid or unrecognized response. + + Sent {0} request content bytes, but Content-Length promised {1}. + The response ended prematurely. diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthWriteStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthWriteStream.cs index 5b7b6551661972..33add1358ab51a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthWriteStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthWriteStream.cs @@ -11,14 +11,23 @@ internal sealed partial class HttpConnection : IDisposable { private sealed class ContentLengthWriteStream : HttpContentWriteStream { - public ContentLengthWriteStream(HttpConnection connection) : base(connection) + private readonly long _contentLength; + + public ContentLengthWriteStream(HttpConnection connection, long contentLength) + : base(connection) { + _contentLength = contentLength; } public override void Write(ReadOnlySpan buffer) { BytesWritten += buffer.Length; + if (BytesWritten > _contentLength) + { + throw new HttpRequestException(SR.net_http_content_write_larger_than_content_length); + } + // Have the connection write the data, skipping the buffer. Importantly, this will // force a flush of anything already in the buffer, i.e. any remaining request headers // that are still buffered. @@ -31,6 +40,11 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo { BytesWritten += buffer.Length; + if (BytesWritten > _contentLength) + { + return ValueTask.FromException(new HttpRequestException(SR.net_http_content_write_larger_than_content_length)); + } + // Have the connection write the data, skipping the buffer. Importantly, this will // force a flush of anything already in the buffer, i.e. any remaining request headers // that are still buffered. @@ -41,6 +55,11 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo public override Task FinishAsync(bool async) { + if (BytesWritten != _contentLength) + { + return Task.FromException(new HttpRequestException(SR.Format(SR.net_http_request_content_length_mismatch, BytesWritten, _contentLength))); + } + _connection = null; return Task.CompletedTask; } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs index b5d5ffa8b84698..3f77cd7f88d61e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs @@ -195,7 +195,7 @@ public async Task SendRequestBodyAsync(CancellationToken cancellationToken) if (sendRequestContent) { - using var writeStream = new Http2WriteStream(this); + using var writeStream = new Http2WriteStream(this, _request.Content.Headers.ContentLength.GetValueOrDefault(-1)); if (HttpTelemetry.Log.IsEnabled()) HttpTelemetry.Log.RequestContentStart(); @@ -214,6 +214,12 @@ public async Task SendRequestBodyAsync(CancellationToken cancellationToken) await vt.ConfigureAwait(false); } + if (writeStream.BytesWritten < writeStream.ContentLength) + { + // The number of bytes we actually sent doesn't match the advertised Content-Length + throw new HttpRequestException(SR.Format(SR.net_http_request_content_length_mismatch, writeStream.BytesWritten, writeStream.ContentLength)); + } + if (HttpTelemetry.Log.IsEnabled()) HttpTelemetry.Log.RequestContentStop(writeStream.BytesWritten); } @@ -1506,10 +1512,14 @@ private sealed class Http2WriteStream : HttpBaseStream public long BytesWritten { get; private set; } - public Http2WriteStream(Http2Stream http2Stream) + public long ContentLength { get; private set; } + + public Http2WriteStream(Http2Stream http2Stream, long contentLength) { Debug.Assert(http2Stream != null); + Debug.Assert(contentLength >= -1); _http2Stream = http2Stream; + ContentLength = contentLength; } protected override void Dispose(bool disposing) @@ -1534,6 +1544,11 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo { BytesWritten += buffer.Length; + if ((ulong)BytesWritten > (ulong)ContentLength) // If ContentLength == -1, this will always be false + { + return ValueTask.FromException(new HttpRequestException(SR.net_http_content_write_larger_than_content_length)); + } + Http2Stream? http2Stream = _http2Stream; if (http2Stream == null) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs index e14302ef51f216..357e768e80319e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs @@ -384,6 +384,14 @@ private async Task SendContentAsync(HttpContent content, CancellationToken cance await content.CopyToAsync(writeStream, null, cancellationToken).ConfigureAwait(false); } + if (_requestContentLengthRemaining > 0) + { + // The number of bytes we actually sent doesn't match the advertised Content-Length + long contentLength = content.Headers.ContentLength.GetValueOrDefault(); + long sent = contentLength - _requestContentLengthRemaining; + throw new HttpRequestException(SR.Format(SR.net_http_request_content_length_mismatch, sent, contentLength)); + } + // Set to 0 to recognize that the whole request body has been sent and therefore there's no need to abort write side in case of a premature disposal. _requestContentLengthRemaining = 0; @@ -414,8 +422,7 @@ private async ValueTask WriteRequestContentAsync(ReadOnlyMemory buffer, Ca if (buffer.Length > _requestContentLengthRemaining) { - string msg = SR.net_http_content_write_larger_than_content_length; - throw new IOException(msg, new HttpRequestException(msg)); + throw new HttpRequestException(SR.net_http_content_write_larger_than_content_length); } _requestContentLengthRemaining -= buffer.Length; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs index d75ed2779f0428..12b6b4bf3c55a0 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs @@ -860,10 +860,11 @@ private bool MapSendException(Exception exception, CancellationToken cancellatio private HttpContentWriteStream CreateRequestContentStream(HttpRequestMessage request) { + Debug.Assert(request.Content is not null); bool requestTransferEncodingChunked = request.HasHeaders && request.Headers.TransferEncodingChunked == true; HttpContentWriteStream requestContentStream = requestTransferEncodingChunked ? (HttpContentWriteStream) new ChunkedEncodingWriteStream(this) : - new ContentLengthWriteStream(this); + new ContentLengthWriteStream(this, request.Content.Headers.ContentLength.GetValueOrDefault()); return requestContentStream; } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/ImpersonatedAuthTests.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/ImpersonatedAuthTests.cs index 6b56d3590d9ad0..32f44d5e00f705 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/ImpersonatedAuthTests.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/ImpersonatedAuthTests.cs @@ -14,7 +14,6 @@ namespace System.Net.Http.Functional.Tests { public class ImpersonatedAuthTests: IClassFixture { - public static bool CanRunImpersonatedTests = PlatformDetection.IsWindows && PlatformDetection.IsNotWindowsNanoServer; private readonly WindowsIdentityFixture _fixture; private readonly ITestOutputHelper _output; @@ -28,11 +27,11 @@ public ImpersonatedAuthTests(WindowsIdentityFixture windowsIdentityFixture, ITe } [OuterLoop] - [ConditionalTheory(nameof(CanRunImpersonatedTests))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.CanRunImpersonatedTests))] [InlineData(true)] [InlineData(false)] [PlatformSpecific(TestPlatforms.Windows)] - public async Task DefaultHandler_ImpersonificatedUser_Success(bool useNtlm) + public async Task DefaultHandler_ImpersonatedUser_Success(bool useNtlm) { await LoopbackServer.CreateClientAndServerAsync( async uri => diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index e7ce1967052703..e2c8c2d68891a2 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -3361,4 +3361,70 @@ public sealed class SocketsHttpHandler_RequestValidationTest_Sync : SocketsHttpH { protected override bool TestAsync => false; } + + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public abstract class SocketsHttpHandler_RequestContentLengthMismatchTest : HttpClientHandlerTestBase + { + public SocketsHttpHandler_RequestContentLengthMismatchTest(ITestOutputHelper output) : base(output) { } + + [Theory] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(1, 2)] + [InlineData(2, 1)] + public async Task ContentLength_DoesNotMatchRequestContentLength_Throws(int contentLength, int bytesSent) + { + await LoopbackServerFactory.CreateClientAndServerAsync(async uri => + { + using var client = CreateHttpClient(); + + var content = new ByteArrayContent(new byte[bytesSent]); + content.Headers.ContentLength = contentLength; + + Exception ex = await Assert.ThrowsAsync(() => client.PostAsync(uri, content)); + Assert.Contains("Content-Length", ex.Message); + + if (UseVersion.Major == 1) + { + await client.GetStringAsync(uri).WaitAsync(TestHelper.PassingTestTimeout); + } + }, + async server => + { + try + { + await server.HandleRequestAsync(); + } + catch { } + + // On HTTP/1.x, an exception being thrown while sending the request content will result in the connection being closed. + // This test is ensuring that a subsequent request can succeed on a new connection. + if (UseVersion.Major == 1) + { + await server.HandleRequestAsync().WaitAsync(TestHelper.PassingTestTimeout); + } + }); + } + } + + public sealed class SocketsHttpHandler_RequestContentLengthMismatchTest_Http11 : SocketsHttpHandler_RequestContentLengthMismatchTest + { + public SocketsHttpHandler_RequestContentLengthMismatchTest_Http11(ITestOutputHelper output) : base(output) { } + protected override Version UseVersion => HttpVersion.Version11; + } + + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.SupportsAlpn))] + public sealed class SocketsHttpHandler_RequestContentLengthMismatchTest_Http2 : SocketsHttpHandler_RequestContentLengthMismatchTest + { + public SocketsHttpHandler_RequestContentLengthMismatchTest_Http2(ITestOutputHelper output) : base(output) { } + protected override Version UseVersion => HttpVersion.Version20; + } + + [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsMsQuicSupported))] + public sealed class SocketsHttpHandler_RequestContentLengthMismatchTest_Http3 : SocketsHttpHandler_RequestContentLengthMismatchTest + { + public SocketsHttpHandler_RequestContentLengthMismatchTest_Http3(ITestOutputHelper output) : base(output) { } + protected override Version UseVersion => HttpVersion.Version30; + protected override QuicImplementationProvider UseQuicImplementationProvider => QuicImplementationProviders.MsQuic; + } } diff --git a/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj b/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj index 7626f207fa6fe5..743f3750a6d8b9 100644 --- a/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj +++ b/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj @@ -55,8 +55,6 @@ Link="Common\Interop\Windows\Kernel32\Interop.LoadLibraryEx_IntPtr.cs" /> - - $(NetCoreAppCurrent);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) enable true - + true Tensor class which represents and extends multi-dimensional arrays. diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index dfaf5c53e9eabe..ae32ed0e29bf4d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -68,6 +68,7 @@ ref MemoryMarshal.GetArrayDataReference(larray), Debug.Assert(array != null); } + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] public static Array CreateInstance(Type elementType, params long[] lengths) { if (lengths == null) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ArrayList.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ArrayList.cs index cfacf96b1f1436..67365bc719f7e6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ArrayList.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ArrayList.cs @@ -12,6 +12,7 @@ ===========================================================*/ using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Collections { @@ -716,6 +717,7 @@ public static ArrayList Synchronized(ArrayList list) // downcasting all elements. This copy may fail and is an O(n) operation. // Internally, this implementation calls Array.Copy. // + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] public virtual Array ToArray(Type type) { if (type == null) @@ -1099,6 +1101,7 @@ public override void Sort(int index, int count, IComparer? comparer) return array; } + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] public override Array ToArray(Type type) { if (type == null) @@ -1492,6 +1495,7 @@ public override void Sort(int index, int count, IComparer? comparer) } } + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] public override Array ToArray(Type type) { lock (_root) @@ -1874,6 +1878,7 @@ public override void Sort(int index, int count, IComparer? comparer) return _list.ToArray(); } + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] public override Array ToArray(Type type) { return _list.ToArray(type); @@ -2125,6 +2130,7 @@ public override void Sort(int index, int count, IComparer? comparer) return _list.ToArray(); } + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] public override Array ToArray(Type type) { return _list.ToArray(type); @@ -2578,6 +2584,7 @@ public override object? this[int index] return array; } + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] public override Array ToArray(Type type) { if (type == null) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Hashtable.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Hashtable.cs index cc92c922f44cbe..01b9cdb3c737ad 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Hashtable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Hashtable.cs @@ -123,14 +123,14 @@ the hash table. // The hash table data. // This cannot be serialized - private struct bucket + private struct Bucket { public object? key; public object? val; public int hash_coll; // Store hash code; sign bit means there was a collision. } - private bucket[] _buckets = null!; + private Bucket[] _buckets = null!; // The total number of entries in the hash table. private int _count; @@ -271,7 +271,7 @@ public Hashtable(int capacity, float loadFactor) // Avoid awfully small sizes int hashsize = (rawsize > InitialSize) ? HashHelpers.GetPrime((int)rawsize) : InitialSize; - _buckets = new bucket[hashsize]; + _buckets = new Bucket[hashsize]; _loadsize = (int)(_loadFactor * hashsize); _isWriterInProgress = false; @@ -443,7 +443,7 @@ public virtual void Clear() // to those Objects. public virtual object Clone() { - bucket[] lbuckets = _buckets; + Bucket[] lbuckets = _buckets; Hashtable ht = new Hashtable(_count, _keycomparer); ht._version = _version; ht._loadFactor = _loadFactor; @@ -480,11 +480,11 @@ public virtual bool ContainsKey(object key) } // Take a snapshot of buckets, in case another thread resizes table - bucket[] lbuckets = _buckets; + Bucket[] lbuckets = _buckets; uint hashcode = InitHash(key, lbuckets.Length, out uint seed, out uint incr); int ntry = 0; - bucket b; + Bucket b; int bucketNumber = (int)(seed % (uint)lbuckets.Length); do { @@ -537,7 +537,7 @@ private void CopyKeys(Array array, int arrayIndex) Debug.Assert(array != null); Debug.Assert(array.Rank == 1); - bucket[] lbuckets = _buckets; + Bucket[] lbuckets = _buckets; for (int i = lbuckets.Length; --i >= 0;) { object? keyv = lbuckets[i].key; @@ -556,7 +556,7 @@ private void CopyEntries(Array array, int arrayIndex) Debug.Assert(array != null); Debug.Assert(array.Rank == 1); - bucket[] lbuckets = _buckets; + Bucket[] lbuckets = _buckets; for (int i = lbuckets.Length; --i >= 0;) { object? keyv = lbuckets[i].key; @@ -592,7 +592,7 @@ internal virtual KeyValuePairs[] ToKeyValuePairsArray() { KeyValuePairs[] array = new KeyValuePairs[_count]; int index = 0; - bucket[] lbuckets = _buckets; + Bucket[] lbuckets = _buckets; for (int i = lbuckets.Length; --i >= 0;) { object? keyv = lbuckets[i].key; @@ -613,7 +613,7 @@ private void CopyValues(Array array, int arrayIndex) Debug.Assert(array != null); Debug.Assert(array.Rank == 1); - bucket[] lbuckets = _buckets; + Bucket[] lbuckets = _buckets; for (int i = lbuckets.Length; --i >= 0;) { object? keyv = lbuckets[i].key; @@ -638,11 +638,11 @@ public virtual object? this[object key] // Take a snapshot of buckets, in case another thread does a resize - bucket[] lbuckets = _buckets; + Bucket[] lbuckets = _buckets; uint hashcode = InitHash(key, lbuckets.Length, out uint seed, out uint incr); int ntry = 0; - bucket b; + Bucket b; int bucketNumber = (int)(seed % (uint)lbuckets.Length); do { @@ -724,18 +724,18 @@ private void rehash(int newsize) _occupancy = 0; // Don't replace any internal state until we've finished adding to the - // new bucket[]. This serves two purposes: + // new Bucket[]. This serves two purposes: // 1) Allow concurrent readers to see valid hashtable contents // at all times // 2) Protect against an OutOfMemoryException while allocating this - // new bucket[]. - bucket[] newBuckets = new bucket[newsize]; + // new Bucket[]. + Bucket[] newBuckets = new Bucket[newsize]; // rehash table into new buckets int nb; for (nb = 0; nb < _buckets.Length; nb++) { - bucket oldb = _buckets[nb]; + Bucket oldb = _buckets[nb]; if ((oldb.key != null) && (oldb.key != _buckets)) { int hashcode = oldb.hash_coll & 0x7FFFFFFF; @@ -743,7 +743,7 @@ private void rehash(int newsize) } } - // New bucket[] is good to go - replace buckets and other internal state. + // New Bucket[] is good to go - replace buckets and other internal state. _isWriterInProgress = true; _buckets = newBuckets; _loadsize = (int)(_loadFactor * newsize); @@ -948,7 +948,7 @@ private void Insert(object key, object? nvalue, bool add) throw new InvalidOperationException(SR.InvalidOperation_HashInsertFailed); } - private void putEntry(bucket[] newBuckets, object key, object? nvalue, int hashcode) + private void putEntry(Bucket[] newBuckets, object key, object? nvalue, int hashcode) { Debug.Assert(hashcode >= 0, "hashcode >= 0"); // make sure collision bit (sign bit) wasn't set. @@ -991,7 +991,7 @@ public virtual void Remove(object key) uint hashcode = InitHash(key, _buckets.Length, out uint seed, out uint incr); int ntry = 0; - bucket b; + Bucket b; int bn = (int)(seed % (uint)_buckets.Length); // bucketNumber do { @@ -1164,7 +1164,7 @@ public virtual void OnDeserialization(object? sender) _keycomparer = new CompatibleComparer(hcp, c); } - _buckets = new bucket[hashsize]; + _buckets = new Bucket[hashsize]; if (serKeys == null) { diff --git a/src/libraries/System.Private.CoreLib/src/System/DefaultBinder.cs b/src/libraries/System.Private.CoreLib/src/System/DefaultBinder.cs index 47028f9b8d408e..9cd5f01a951e3c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DefaultBinder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DefaultBinder.cs @@ -3,6 +3,7 @@ using System.Reflection; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using CultureInfo = System.Globalization.CultureInfo; @@ -28,6 +29,8 @@ partial class DefaultBinder : Binder // // The most specific match will be selected. // + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "AOT compiler ensures params arrays are created for reflection-invokable methods")] public sealed override MethodBase BindToMethod( BindingFlags bindingAttr, MethodBase[] match, ref object?[] args, ParameterModifier[]? modifiers, CultureInfo? cultureInfo, string[]? names, out object? state) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs index d156cdf1a412a6..557b7953f0ca56 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; #endif +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; @@ -20,7 +21,13 @@ namespace System.Diagnostics.Tracing /// /// To get the value of a property quickly, use a delegate produced by . /// - internal readonly unsafe struct PropertyValue +#if CORERT + [CLSCompliant(false)] + public // On CoreRT, this must be public to prevent it from getting reflection blocked. +#else + internal +#endif + readonly unsafe struct PropertyValue { /// /// Union of well-known value types, to avoid boxing those types. @@ -195,23 +202,41 @@ private static Func GetBoxedValueTypePropertyGette /// /// /// +#if !ES_BUILD_STANDALONE + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", + Justification = "Instantiation over a reference type. See comments above.")] +#endif private static Func GetReferenceTypePropertyGetter(PropertyInfo property) { var helper = (TypeHelper)Activator.CreateInstance(typeof(ReferenceTypeHelper<>).MakeGenericType(property.DeclaringType!))!; return helper.GetPropertyGetter(property); } - private abstract class TypeHelper +#if CORERT + public // On CoreRT, this must be public to prevent it from getting reflection blocked. +#else + private +#endif + abstract class TypeHelper { public abstract Func GetPropertyGetter(PropertyInfo property); +#if !ES_BUILD_STANDALONE + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", + Justification = "Instantiation over a reference type. See comments above.")] +#endif protected static Delegate GetGetMethod(PropertyInfo property, Type propertyType) { return property.GetMethod!.CreateDelegate(typeof(Func<,>).MakeGenericType(property.DeclaringType!, propertyType)); } } - private sealed class ReferenceTypeHelper : TypeHelper where TContainer : class +#if CORERT + public // On CoreRT, this must be public to prevent it from getting reflection blocked. +#else + private +#endif + sealed class ReferenceTypeHelper : TypeHelper where TContainer : class { private static Func GetGetMethod(PropertyInfo property) where TProperty : struct => #if ES_BUILD_STANDALONE diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.cs index 52288de923b47a..3af9ab2a2bcfa0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.cs @@ -319,9 +319,12 @@ public static Type GetUnderlyingType(Type enumType) return enumType.GetEnumUnderlyingType(); } +#if !CORERT public static TEnum[] GetValues() where TEnum : struct, Enum => (TEnum[])GetValues(typeof(TEnum)); +#endif + [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues overload instead.")] public static Array GetValues(Type enumType) { if (enumType is null) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs index b4796fd92e63d9..a7ebe4c496f641 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs @@ -341,10 +341,10 @@ internal enum DS new DS[] { DS.BEGIN, DS.ERROR, DS.TX_N, DS.N, DS.D_Nd, DS.T_Nt, DS.ERROR, DS.D_M, DS.D_M, DS.D_S, DS.T_S, DS.BEGIN, DS.D_Y, DS.D_Y, DS.ERROR, DS.BEGIN, DS.BEGIN, DS.ERROR }, // DS.N // DS.N -new DS[] { DS.ERROR, DS.DX_NN, DS.ERROR, DS.NN, DS.D_NNd, DS.ERROR, DS.DX_NM, DS.D_NM, DS.D_MNd, DS.D_NDS, DS.ERROR, DS.N, DS.D_YN, DS.D_YNd, DS.DX_YN, DS.N, DS.N, DS.ERROR }, +new DS[] { DS.ERROR, DS.DX_NN, DS.TX_NN, DS.NN, DS.D_NNd, DS.ERROR, DS.DX_NM, DS.D_NM, DS.D_MNd, DS.D_NDS, DS.ERROR, DS.N, DS.D_YN, DS.D_YNd, DS.DX_YN, DS.N, DS.N, DS.ERROR }, // DS.NN // DS.NN -new DS[] { DS.DX_NN, DS.DX_NNN, DS.TX_N, DS.DX_NNN, DS.ERROR, DS.T_Nt, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.ERROR, DS.T_S, DS.NN, DS.DX_NNY, DS.ERROR, DS.DX_NNY, DS.NN, DS.NN, DS.ERROR }, +new DS[] { DS.DX_NN, DS.DX_NNN, DS.TX_NNN, DS.DX_NNN, DS.ERROR, DS.T_Nt, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.ERROR, DS.T_S, DS.NN, DS.DX_NNY, DS.ERROR, DS.DX_NNY, DS.NN, DS.NN, DS.ERROR }, // DS.D_Nd // DS.D_Nd new DS[] { DS.ERROR, DS.DX_NN, DS.ERROR, DS.D_NN, DS.D_NNd, DS.ERROR, DS.DX_NM, DS.D_MN, DS.D_MNd, DS.ERROR, DS.ERROR, DS.D_Nd, DS.D_YN, DS.D_YNd, DS.DX_YN, DS.ERROR, DS.D_Nd, DS.ERROR }, @@ -3192,8 +3192,8 @@ private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, ref /*=================================ParseSign================================== **Action: Parse a positive or a negative sign. - **Returns: true if postive sign. flase if negative sign. - **Arguments: str: a __DTString. The parsing will start from the + **Returns: true if positive sign. false if negative sign. + **Arguments: str: a __DTString. The parsing will start from the ** next character after str.Index. **Exceptions: FormatException if end of string is encountered or a sign ** symbol is not found. @@ -5244,6 +5244,8 @@ private static void Trace(string s) // internal ref struct __DTString { + internal const char RightToLeftMark = '\u200F'; + // // Value property: stores the real string to be parsed. // @@ -5418,7 +5420,7 @@ internal TokenType GetSeparatorToken(DateTimeFormatInfo dtfi, out int indexBefor indexBeforeSeparator = Index; charBeforeSeparator = m_current; TokenType tokenType; - if (!SkipWhiteSpaceCurrent()) + if (!SkipWhiteSpaceAndRtlMarkCurrent()) { // Reach the end of the string. return TokenType.SEP_End; @@ -5672,18 +5674,20 @@ internal void SkipWhiteSpaces() } // - // Skip white spaces from the current position + // Skip white spaces and right-to-left Mark from the current position // + // U+200F is the Unicode right-to-left mark. In some Bidi cultures, this mark gets inserted inside the formatted date or time to have the output displayed in the correct layout. + // This mark does not affect the date or the time component values. Ignoring this mark during parsing wouldn't affect the result but will avoid having the parsing fail. // Return false if end of string is encountered. // - internal bool SkipWhiteSpaceCurrent() + internal bool SkipWhiteSpaceAndRtlMarkCurrent() { if (Index >= Length) { return false; } - if (!char.IsWhiteSpace(m_current)) + if (!char.IsWhiteSpace(m_current) && m_current != RightToLeftMark) { return true; } @@ -5691,7 +5695,7 @@ internal bool SkipWhiteSpaceCurrent() while (++Index < Length) { m_current = Value[Index]; - if (!char.IsWhiteSpace(m_current)) + if (!char.IsWhiteSpace(m_current) && m_current != RightToLeftMark) { return true; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs index e81ef6234e7b9e..81214ade56e202 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs @@ -5,9 +5,14 @@ namespace System.Globalization { internal static partial class GlobalizationMode { +#if CORERT + // CORERT-TODO: Enable Icu on Windows + internal static bool UseNls { get; } = !Invariant; +#else internal static bool UseNls { get; } = !Invariant && (AppContextConfigHelper.GetBooleanConfig("System.Globalization.UseNls", "DOTNET_SYSTEM_GLOBALIZATION_USENLS") || !LoadIcu()); +#endif private static bool LoadIcu() { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs index 4e90b98a03fd74..3436a7f1428f60 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs @@ -182,10 +182,9 @@ public static void MoveFile(string sourceFullPath, string destFullPath, bool ove } } - private static SafeFileHandle OpenHandle(string fullPath, bool asDirectory) + private static SafeFileHandle OpenHandleToWriteAttributes(string fullPath, bool asDirectory) { - string root = fullPath.Substring(0, PathInternal.GetRootLength(fullPath.AsSpan())); - if (root == fullPath && root[1] == Path.VolumeSeparatorChar) + if (fullPath.Length == PathInternal.GetRootLength(fullPath) && fullPath[1] == Path.VolumeSeparatorChar) { // intentionally not fullpath, most upstack public APIs expose this as path. throw new ArgumentException(SR.Arg_PathIsVolume, "path"); @@ -199,7 +198,7 @@ private static SafeFileHandle OpenHandle(string fullPath, bool asDirectory) SafeFileHandle handle = Interop.Kernel32.CreateFile( fullPath, - Interop.Kernel32.GenericOperations.GENERIC_WRITE, + Interop.Kernel32.FileOperations.FILE_WRITE_ATTRIBUTES, FileShare.ReadWrite | FileShare.Delete, FileMode.Open, dwFlagsAndAttributes); @@ -425,7 +424,7 @@ private static unsafe void SetFileTime( long changeTime = -1, uint fileAttributes = 0) { - using (SafeFileHandle handle = OpenHandle(fullPath, asDirectory)) + using (SafeFileHandle handle = OpenHandleToWriteAttributes(fullPath, asDirectory)) { var basicInfo = new Interop.Kernel32.FILE_BASIC_INFO() { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StringReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StringReader.cs index 35618daa455104..3d50897ec7f386 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StringReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StringReader.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; @@ -11,7 +12,6 @@ public class StringReader : TextReader { private string? _s; private int _pos; - private int _length; public StringReader(string s) { @@ -21,7 +21,6 @@ public StringReader(string s) } _s = s; - _length = s.Length; } public override void Close() @@ -33,7 +32,6 @@ protected override void Dispose(bool disposing) { _s = null; _pos = 0; - _length = 0; base.Dispose(disposing); } @@ -44,16 +42,19 @@ protected override void Dispose(bool disposing) // public override int Peek() { - if (_s == null) + string? s = _s; + if (s == null) { - throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed); + ThrowObjectDisposedException_ReaderClosed(); } - if (_pos == _length) + + int pos = _pos; + if ((uint)pos < (uint)s.Length) { - return -1; + return s[pos]; } - return _s[_pos]; + return -1; } // Reads the next character from the underlying string. The returned value @@ -61,16 +62,20 @@ public override int Peek() // public override int Read() { - if (_s == null) + string? s = _s; + if (s == null) { - throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed); + ThrowObjectDisposedException_ReaderClosed(); } - if (_pos == _length) + + int pos = _pos; + if ((uint)pos < (uint)s.Length) { - return -1; + _pos++; + return s[pos]; } - return _s[_pos++]; + return -1; } // Reads a block of characters. This method will read up to count @@ -98,10 +103,10 @@ public override int Read(char[] buffer, int index, int count) } if (_s == null) { - throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed); + ThrowObjectDisposedException_ReaderClosed(); } - int n = _length - _pos; + int n = _s.Length - _pos; if (n > 0) { if (n > count) @@ -124,12 +129,13 @@ public override int Read(Span buffer) return base.Read(buffer); } - if (_s == null) + string? s = _s; + if (s == null) { - throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed); + ThrowObjectDisposedException_ReaderClosed(); } - int n = _length - _pos; + int n = s.Length - _pos; if (n > 0) { if (n > buffer.Length) @@ -137,7 +143,7 @@ public override int Read(Span buffer) n = buffer.Length; } - _s.AsSpan(_pos, n).CopyTo(buffer); + s.AsSpan(_pos, n).CopyTo(buffer); _pos += n; } @@ -148,22 +154,20 @@ public override int Read(Span buffer) public override string ReadToEnd() { - if (_s == null) + string? s = _s; + if (s == null) { - throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed); + ThrowObjectDisposedException_ReaderClosed(); } - string s; - if (_pos == 0) - { - s = _s; - } - else + int pos = _pos; + _pos = s.Length; + + if (pos != 0) { - s = _s.Substring(_pos, _length - _pos); + s = s.Substring(pos); } - _pos = _length; return s; } @@ -175,38 +179,43 @@ public override string ReadToEnd() // public override string? ReadLine() { - if (_s == null) + string? s = _s; + if (s == null) { - throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed); + ThrowObjectDisposedException_ReaderClosed(); } - int i = _pos; - while (i < _length) + int pos = _pos; + if ((uint)pos >= (uint)s.Length) { - char ch = _s[i]; - if (ch == '\r' || ch == '\n') + return null; + } + + ReadOnlySpan remaining = s.AsSpan(pos); + int foundLineLength = remaining.IndexOfAny('\r', '\n'); + if (foundLineLength >= 0) + { + string result = s.Substring(pos, foundLineLength); + + char ch = remaining[foundLineLength]; + pos += foundLineLength + 1; + if (ch == '\r') { - string result = _s.Substring(_pos, i - _pos); - _pos = i + 1; - if (ch == '\r' && _pos < _length && _s[_pos] == '\n') + if ((uint)pos < (uint)s.Length && s[pos] == '\n') { - _pos++; + pos++; } - - return result; } + _pos = pos; - i++; + return result; } - - if (i > _pos) + else { - string result = _s.Substring(_pos, i - _pos); - _pos = i; + string result = s.Substring(pos); + _pos = s.Length; return result; } - - return null; } #region Task based Async APIs @@ -264,5 +273,11 @@ public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken.IsCancellationRequested ? ValueTask.FromCanceled(cancellationToken) : new ValueTask(Read(buffer.Span)); #endregion + + [DoesNotReturn] + private static void ThrowObjectDisposedException_ReaderClosed() + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs index d14d3339b05e11..a9d065ffbc3455 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs @@ -123,6 +123,7 @@ internal static void AppendParameters(ref ValueStringBuilder sbParamList, Type[] } } +#if !CORERT private protected void ValidateInvokeTarget(object? target) { // Confirm member invocation has an instance and is of the correct type @@ -191,5 +192,6 @@ private protected struct StackAllocedArguments private object? _arg3; #pragma warning restore CA1823, CS0169, IDE0051 } +#endif } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInfo.cs index 8a0cbc11f5163a..7e16e554c0807b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInfo.cs @@ -18,6 +18,7 @@ protected MethodInfo() { } public override Type[] GetGenericArguments() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } public virtual MethodInfo GetGenericMethodDefinition() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] public virtual MethodInfo MakeGenericMethod(params Type[] typeArguments) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureType.cs index e592c24bbb5847..8bebe753e08a11 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureType.cs @@ -37,7 +37,9 @@ internal abstract class SignatureType : Type public sealed override MemberTypes MemberType => MemberTypes.TypeInfo; // Compositors + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] public sealed override Type MakeArrayType() => new SignatureArrayType(this, rank: 1, isMultiDim: false); + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] public sealed override Type MakeArrayType(int rank) { if (rank <= 0) @@ -47,6 +49,7 @@ public sealed override Type MakeArrayType(int rank) public sealed override Type MakeByRefType() => new SignatureByRefType(this); public sealed override Type MakePointerType() => new SignaturePointerType(this); + [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] public sealed override Type MakeGenericType(params Type[] typeArguments) => throw new NotSupportedException(SR.NotSupported_SignatureType); // There is no SignatureType for type definition types so it would never be legal to call this. @@ -96,6 +99,7 @@ public sealed override Type MakeArrayType(int rank) public sealed override string GetEnumName(object value) => throw new NotSupportedException(SR.NotSupported_SignatureType); public sealed override string[] GetEnumNames() => throw new NotSupportedException(SR.NotSupported_SignatureType); public sealed override Type GetEnumUnderlyingType() => throw new NotSupportedException(SR.NotSupported_SignatureType); + [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use Enum.GetValues instead.")] public sealed override Array GetEnumValues() => throw new NotSupportedException(SR.NotSupported_SignatureType); public sealed override Guid GUID => throw new NotSupportedException(SR.NotSupported_SignatureType); protected sealed override TypeCode GetTypeCodeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureTypeExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureTypeExtensions.cs index 9e15a6d0a209f9..266eabd6850daa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureTypeExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureTypeExtensions.cs @@ -112,6 +112,8 @@ internal static bool MatchesExactly(this SignatureType pattern, Type actual) [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Used to find matching method overloads. Only used for assignability checks.")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", + Justification = "Used to find matching method overloads. Only used for assignability checks.")] private static Type? TryResolve(this SignatureType signatureType, Type[] genericMethodParameters) { if (signatureType.IsSZArray) @@ -164,6 +166,8 @@ internal static bool MatchesExactly(this SignatureType pattern, Type actual) } } + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", + Justification = "Used to find matching method overloads. Only used for assignability checks.")] private static Type? TryMakeArrayType(this Type type) { try @@ -176,6 +180,8 @@ internal static bool MatchesExactly(this SignatureType pattern, Type actual) } } + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", + Justification = "Used to find matching method overloads. Only used for assignability checks.")] private static Type? TryMakeArrayType(this Type type, int rank) { try @@ -213,6 +219,7 @@ internal static bool MatchesExactly(this SignatureType pattern, Type actual) } [RequiresUnreferencedCode("Wrapper around MakeGenericType which itself has RequiresUnreferencedCode")] + [RequiresDynamicCode("Wrapper around MakeGenericType which itself has RequiresDynamicCode")] private static Type? TryMakeGenericType(this Type type, Type[] instantiation) { try diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs index 9f3b203bdb0390..b2e9302a8dd78f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -104,6 +104,7 @@ public static unsafe string PtrToStringUTF8(IntPtr ptr, int byteLen) return string.CreateStringFromEncoding((byte*)ptr, byteLen, Encoding.UTF8); } + [RequiresDynamicCode("Marshalling code for the object might not be available. Use the SizeOf overload instead.")] public static int SizeOf(object structure) { if (structure is null) @@ -124,6 +125,7 @@ public static int SizeOf(T structure) return SizeOfHelper(structure.GetType(), throwIfNotMarshalable: true); } + [RequiresDynamicCode("Marshalling code for the object might not be available. Use the SizeOf overload instead.")] public static int SizeOf(Type t) { if (t is null) @@ -378,6 +380,7 @@ public static unsafe int ReadInt32(IntPtr ptr, int ofs) public static int ReadInt32(IntPtr ptr) => ReadInt32(ptr, 0); + [RequiresDynamicCode("Marshalling code for the object might not be available")] public static IntPtr ReadIntPtr(object ptr, int ofs) { #if TARGET_64BIT @@ -464,6 +467,7 @@ public static unsafe void WriteInt16(IntPtr ptr, int ofs, short val) public static void WriteInt16(IntPtr ptr, int ofs, char val) => WriteInt16(ptr, ofs, (short)val); + [RequiresDynamicCode("Marshalling code for the object might not be available")] public static void WriteInt16([In, Out]object ptr, int ofs, char val) => WriteInt16(ptr, ofs, (short)val); public static void WriteInt16(IntPtr ptr, char val) => WriteInt16(ptr, 0, (short)val); @@ -501,6 +505,7 @@ public static void WriteIntPtr(IntPtr ptr, int ofs, IntPtr val) #endif } + [RequiresDynamicCode("Marshalling code for the object might not be available")] public static void WriteIntPtr(object ptr, int ofs, IntPtr val) { #if TARGET_64BIT @@ -563,6 +568,8 @@ public static void PrelinkAll(Type c) } } + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", + Justification = "AOT compilers can see the T.")] public static void StructureToPtr([DisallowNull] T structure, IntPtr ptr, bool fDeleteOld) { StructureToPtr((object)structure!, ptr, fDeleteOld); @@ -572,6 +579,7 @@ public static void StructureToPtr([DisallowNull] T structure, IntPtr ptr, boo /// Creates a new instance of "structuretype" and marshals data from a /// native memory block to it. /// + [RequiresDynamicCode("Marshalling code for the object might not be available")] public static object? PtrToStructure(IntPtr ptr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type structureType) @@ -602,6 +610,7 @@ public static void StructureToPtr([DisallowNull] T structure, IntPtr ptr, boo /// /// Marshals data from a native memory block to a preallocated structure class. /// + [RequiresDynamicCode("Marshalling code for the object might not be available")] public static void PtrToStructure(IntPtr ptr, object structure) { PtrToStructureHelper(ptr, structure, allowValueClasses: false); @@ -633,6 +642,8 @@ public static void PtrToStructure(IntPtr ptr, [DisallowNull] T structure) return (T)structure; } + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", + Justification = "AOT compilers can see the T.")] public static void DestroyStructure(IntPtr ptr) => DestroyStructure(ptr, typeof(T)); // CoreCLR has a different implementation for Windows only @@ -1151,6 +1162,7 @@ public static Guid GenerateGuidForType(Type type) return type.FullName; } + [RequiresDynamicCode("Marshalling code for the delegate might not be available. Use the GetDelegateForFunctionPointer overload instead.")] public static Delegate GetDelegateForFunctionPointer(IntPtr ptr, Type t) { if (ptr == IntPtr.Zero) @@ -1205,6 +1217,7 @@ public static TDelegate GetDelegateForFunctionPointer(IntPtr ptr) return (TDelegate)(object)GetDelegateForFunctionPointerInternal(ptr, t); } + [RequiresDynamicCode("Marshalling code for the delegate might not be available. Use the GetFunctionPointerForDelegate overload instead.")] public static IntPtr GetFunctionPointerForDelegate(Delegate d) { if (d is null) @@ -1215,6 +1228,8 @@ public static IntPtr GetFunctionPointerForDelegate(Delegate d) return GetFunctionPointerForDelegateInternal(d); } + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", + Justification = "AOT compilers can see the T.")] public static IntPtr GetFunctionPointerForDelegate(TDelegate d) where TDelegate : notnull { return GetFunctionPointerForDelegate((Delegate)(object)d); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs index 2eddfe5ce9bc75..43c30ecbfcf810 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs @@ -356,8 +356,10 @@ public static void Sleep(int millisecondsTimeout) SleepInternal(millisecondsTimeout); } +#if !CORERT /// Returns the operating system identifier for the current thread. internal static ulong CurrentOSThreadId => GetCurrentOSThreadId(); +#endif public ExecutionContext? ExecutionContext => ExecutionContext.Capture(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Type.cs b/src/libraries/System.Private.CoreLib/src/System/Type.cs index 40f119f0e4892d..bd3c449d74cd58 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Type.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Type.cs @@ -522,6 +522,8 @@ public virtual Type GetEnumUnderlyingType() return fields[0].FieldType; } + + [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use Enum.GetValues instead.")] public virtual Array GetEnumValues() { if (!IsEnum) @@ -532,10 +534,13 @@ public virtual Array GetEnumValues() throw NotImplemented.ByDesign; } + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] public virtual Type MakeArrayType() => throw new NotSupportedException(); + [RequiresDynamicCode("The native code for the array might not be available at runtime.")] public virtual Type MakeArrayType(int rank) => throw new NotSupportedException(); public virtual Type MakeByRefType() => throw new NotSupportedException(); + [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] public virtual Type MakeGenericType(params Type[] typeArguments) => throw new NotSupportedException(SR.NotSupported_SubclassOverride); diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System.Private.Runtime.InteropServices.JavaScript.Tests.csproj b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System.Private.Runtime.InteropServices.JavaScript.Tests.csproj index d7b18fc65e1505..f1afdecab341e2 100644 --- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System.Private.Runtime.InteropServices.JavaScript.Tests.csproj +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System.Private.Runtime.InteropServices.JavaScript.Tests.csproj @@ -17,6 +17,11 @@ + + + + + diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/TimerTests.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/TimerTests.cs new file mode 100644 index 00000000000000..2bf8dee0b82d9d --- /dev/null +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/TimerTests.cs @@ -0,0 +1,118 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.Runtime.InteropServices.JavaScript.Tests +{ + // V8's implementation of setTimer ignores delay parameter and always run immediately. So it could not be used to test this. + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsBrowserDomSupported))] + public class TimerTests : IAsyncLifetime + { + static JSObject _timersHelper; + static Function _installWrapper; + static Function _getRegisterCount; + static Function _getHitCount; + static Function _cleanupWrapper; + static Function _log; + + public static IEnumerable TestCases() + { + yield return new object[] { new int[0], 0, null, null }; + yield return new object[] { new[] { 10 }, 1, null, null }; + yield return new object[] { new[] { 10, 5 }, 2, null, null }; + yield return new object[] { new[] { 10, 20 }, 1, null, null }; + yield return new object[] { new[] { 800, 600, 400, 200, 100 }, 5, 13, 9 }; + } + + [Theory] + [MemberData(nameof(TestCases))] + public async Task TestTimers(int[] timeouts, int? expectedSetCounter, int? expectedSetCounterAfterCleanUp, int? expectedHitCount) + { + int wasCalled = 0; + Timer[] timers = new Timer[timeouts.Length]; + try + { + _log.Call(_timersHelper, $"Waiting for runtime to settle"); + // the test is quite sensitive to timing and order of execution. Here we are giving time to timers of XHarness and previous tests to finish. + await Task.Delay(2000); + _installWrapper.Call(_timersHelper); + _log.Call(_timersHelper, $"Ready!"); + + for (int i = 0; i < timeouts.Length; i++) + { + int index = i; + _log.Call(_timersHelper, $"Registering {index} delay {timeouts[i]}"); + timers[i] = new Timer((_) => + { + _log.Call(_timersHelper, $"In timer{index}"); + wasCalled++; + }, null, timeouts[i], 0); + } + + var setCounter = (int)_getRegisterCount.Call(_timersHelper); + Assert.True(0 == wasCalled, $"wasCalled: {wasCalled}"); + Assert.True((expectedSetCounter ?? timeouts.Length) == setCounter, $"setCounter: actual {setCounter} expected {expectedSetCounter}"); + + } + finally + { + // the test is quite sensitive to timing and order of execution. + // Here we are giving time to our timers to finish. + var afterLastTimer = timeouts.Length == 0 ? 500 : 500 + timeouts.Max(); + + _log.Call(_timersHelper, "wait for timers to run"); + // this delay is also implemented as timer, so it counts to asserts + await Task.Delay(afterLastTimer); + _log.Call(_timersHelper, "cleanup"); + _cleanupWrapper.Call(_timersHelper); + + Assert.True(timeouts.Length == wasCalled, $"wasCalled: actual {wasCalled} expected {timeouts.Length}"); + + if (expectedSetCounterAfterCleanUp != null) + { + var setCounter = (int)_getRegisterCount.Call(_timersHelper); + Assert.True(expectedSetCounterAfterCleanUp.Value == setCounter, $"setCounter: actual {setCounter} expected {expectedSetCounterAfterCleanUp.Value}"); + } + + if (expectedHitCount != null) + { + var hitCounter = (int)_getHitCount.Call(_timersHelper); + Assert.True(expectedHitCount == hitCounter, $"hitCounter: actual {hitCounter} expected {expectedHitCount}"); + } + + for (int i = 0; i < timeouts.Length; i++) + { + timers[i].Dispose(); + } + } + } + + public async Task InitializeAsync() + { + if (_timersHelper == null) + { + Function helper = new Function(@" + const loadTimersJs = async () => { + await import('./timers.js'); + }; + return loadTimersJs(); + "); + await (Task)helper.Call(_timersHelper); + + _timersHelper = (JSObject)Runtime.GetGlobalObject("timersHelper"); + _installWrapper = (Function)_timersHelper.GetObjectProperty("install"); + _getRegisterCount = (Function)_timersHelper.GetObjectProperty("getRegisterCount"); + _getHitCount = (Function)_timersHelper.GetObjectProperty("getHitCount"); + _cleanupWrapper = (Function)_timersHelper.GetObjectProperty("cleanup"); + _log = (Function)_timersHelper.GetObjectProperty("log"); + } + } + + public Task DisposeAsync() => Task.CompletedTask; + } +} diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/timers.js b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/timers.js new file mode 100644 index 00000000000000..ca2b7f31d52954 --- /dev/null +++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/timers.js @@ -0,0 +1,47 @@ +class TimersHelper { + log(message) { + // uncomment for debugging + // console.log(message); + } + install() { + const measuredCallbackName = "mono_wasm_set_timeout_exec"; + globalThis.registerCount = 0; + globalThis.hitCount = 0; + this.log("install") + if (!globalThis.originalSetTimeout) { + globalThis.originalSetTimeout = globalThis.setTimeout; + } + globalThis.setTimeout = (cb, time) => { + var start = Date.now().valueOf(); + if (cb.name === measuredCallbackName) { + globalThis.registerCount++; + this.log(`registerCount: ${globalThis.registerCount} now:${start} delay:${time}`) + } + return globalThis.originalSetTimeout(() => { + if (cb.name === measuredCallbackName) { + var hit = Date.now().valueOf(); + globalThis.hitCount++; + this.log(`hitCount: ${globalThis.hitCount} now:${hit} delay:${time} delta:${hit - start}`) + } + cb(); + }, time); + }; + } + + getRegisterCount() { + this.log(`registerCount: ${globalThis.registerCount} `) + return globalThis.registerCount; + } + + getHitCount() { + this.log(`hitCount: ${globalThis.hitCount} `) + return globalThis.hitCount; + } + + cleanup() { + this.log(`cleanup registerCount: ${globalThis.registerCount} hitCount: ${globalThis.hitCount} `) + globalThis.setTimeout = globalThis.originalSetTimeout; + } +} + +globalThis.timersHelper = new TimersHelper(); diff --git a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj index ad19c548e32025..613e07f835e94e 100644 --- a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj +++ b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj @@ -140,7 +140,6 @@ - diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlEncodedRawTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlEncodedRawTextWriter.cs index b4210f187a4116..a824c569749824 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlEncodedRawTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlEncodedRawTextWriter.cs @@ -5,13 +5,8 @@ // Instead, modify HtmlRawTextWriterGenerator.ttinclude #nullable disable -using System; using System.IO; -using System.Text; -using System.Xml; -using System.Xml.Schema; using System.Diagnostics; -using MS.Internal.Xml; namespace System.Xml { @@ -27,9 +22,6 @@ internal class HtmlEncodedRawTextWriter : XmlEncodedRawTextWriter private string _mediaType; private bool _doNotEscapeUriAttributes; - protected static TernaryTreeReadOnly _elementPropertySearch; - protected static TernaryTreeReadOnly _attributePropertySearch; - private const int StackIncrement = 10; public HtmlEncodedRawTextWriter(TextWriter writer, XmlWriterSettings settings) : base(writer, settings) @@ -112,7 +104,7 @@ public override void WriteStartElement(string prefix, string localName, string n if (_trackTextContent && _inTextContent != false) { ChangeTextContentMark(false); } - _currentElementProperties = (ElementProperties)_elementPropertySearch.FindCaseInsensitiveString(localName); + _currentElementProperties = TernaryTreeReadOnly.FindElementProperty(localName); base._bufChars[_bufPos++] = (char)'<'; base.RawText(localName); base._attrEndPos = _bufPos; @@ -309,7 +301,7 @@ public override void WriteStartAttribute(string prefix, string localName, string if ((_currentElementProperties & (ElementProperties.BOOL_PARENT | ElementProperties.URI_PARENT | ElementProperties.NAME_PARENT)) != 0) { - _currentAttributeProperties = (AttributeProperties)_attributePropertySearch.FindCaseInsensitiveString(localName) & + _currentAttributeProperties = TernaryTreeReadOnly.FindAttributeProperty(localName) & (AttributeProperties)_currentElementProperties; if ((_currentAttributeProperties & AttributeProperties.BOOLEAN) != 0) @@ -447,13 +439,6 @@ private void Init(XmlWriterSettings settings) Debug.Assert((int)ElementProperties.BOOL_PARENT == (int)AttributeProperties.BOOLEAN); Debug.Assert((int)ElementProperties.NAME_PARENT == (int)AttributeProperties.NAME); - if (_elementPropertySearch == null) - { - // _elementPropertySearch should be init last for the mutli thread safe situation. - _attributePropertySearch = new TernaryTreeReadOnly(HtmlTernaryTree.htmlAttributes); - _elementPropertySearch = new TernaryTreeReadOnly(HtmlTernaryTree.htmlElements); - } - _elementScope = new ByteStack(StackIncrement); _uriEscapingBuffer = new byte[5]; _currentElementProperties = ElementProperties.DEFAULT; @@ -660,7 +645,7 @@ private unsafe void WriteUriAttributeText(char* pSrc, char* pSrcEnd) pDstEnd = pDstBegin + _bufLen; } - while (pDst < pDstEnd && (XmlCharType.IsAttributeValueChar((char)(ch = *pSrc)) && ch < 0x80)) + while (pDst < pDstEnd && XmlCharType.IsAttributeValueChar((char)(ch = *pSrc)) && ch < 0x80) { *pDst++ = (char)ch; pSrc++; @@ -839,7 +824,7 @@ public override void WriteStartElement(string prefix, string localName, string n { Debug.Assert(prefix.Length == 0); - base._currentElementProperties = (ElementProperties)_elementPropertySearch.FindCaseInsensitiveString(localName); + base._currentElementProperties = TernaryTreeReadOnly.FindElementProperty(localName); if (_endBlockPos == base._bufPos && (base._currentElementProperties & ElementProperties.BLOCK_WS) != 0) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlRawTextWriterGenerator.ttinclude b/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlRawTextWriterGenerator.ttinclude index 3af8316ae748f4..a5b08afd0b976f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlRawTextWriterGenerator.ttinclude +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlRawTextWriterGenerator.ttinclude @@ -6,13 +6,8 @@ // Instead, modify HtmlRawTextWriterGenerator.ttinclude #nullable disable -using System; using System.IO; -using System.Text; -using System.Xml; -using System.Xml.Schema; using System.Diagnostics; -using MS.Internal.Xml; namespace System.Xml { @@ -28,9 +23,6 @@ namespace System.Xml private string _mediaType; private bool _doNotEscapeUriAttributes; - protected static TernaryTreeReadOnly _elementPropertySearch; - protected static TernaryTreeReadOnly _attributePropertySearch; - private const int StackIncrement = 10; <# if (WriterType == RawTextWriterType.Encoded) { @@ -115,7 +107,7 @@ namespace System.Xml #><#= SetTextContentMark(4, false) #> - _currentElementProperties = (ElementProperties)_elementPropertySearch.FindCaseInsensitiveString(localName); + _currentElementProperties = TernaryTreeReadOnly.FindElementProperty(localName); base.<#= BufferName #>[_bufPos++] = (<#= BufferType #>)'<'; base.RawText(localName); base._attrEndPos = _bufPos; @@ -312,7 +304,7 @@ namespace System.Xml if ((_currentElementProperties & (ElementProperties.BOOL_PARENT | ElementProperties.URI_PARENT | ElementProperties.NAME_PARENT)) != 0) { - _currentAttributeProperties = (AttributeProperties)_attributePropertySearch.FindCaseInsensitiveString(localName) & + _currentAttributeProperties = TernaryTreeReadOnly.FindAttributeProperty(localName) & (AttributeProperties)_currentElementProperties; if ((_currentAttributeProperties & AttributeProperties.BOOLEAN) != 0) @@ -450,13 +442,6 @@ namespace System.Xml Debug.Assert((int)ElementProperties.BOOL_PARENT == (int)AttributeProperties.BOOLEAN); Debug.Assert((int)ElementProperties.NAME_PARENT == (int)AttributeProperties.NAME); - if (_elementPropertySearch == null) - { - // _elementPropertySearch should be init last for the mutli thread safe situation. - _attributePropertySearch = new TernaryTreeReadOnly(HtmlTernaryTree.htmlAttributes); - _elementPropertySearch = new TernaryTreeReadOnly(HtmlTernaryTree.htmlElements); - } - _elementScope = new ByteStack(StackIncrement); _uriEscapingBuffer = new byte[5]; _currentElementProperties = ElementProperties.DEFAULT; @@ -576,9 +561,9 @@ namespace System.Xml } <# if (WriterType == RawTextWriterType.Utf8) {#> - while (pDst < pDstEnd && (_xmlCharType.IsAttributeValueChar((char)(ch = *pSrc)) && ch <= 0x7F)) + while (pDst < pDstEnd && XmlCharType.IsAttributeValueChar((char)(ch = *pSrc)) && ch <= 0x7F) <# } else { #> - while (pDst < pDstEnd && _xmlCharType.IsAttributeValueChar((char)(ch = *pSrc))) + while (pDst < pDstEnd && XmlCharType.IsAttributeValueChar((char)(ch = *pSrc))) <# } #> { *pDst++ = (<#= BufferType #>)ch; @@ -667,7 +652,7 @@ namespace System.Xml pDstEnd = pDstBegin + _bufLen; } - while (pDst < pDstEnd && (_xmlCharType.IsAttributeValueChar((char)(ch = *pSrc)) && ch < 0x80)) + while (pDst < pDstEnd && XmlCharType.IsAttributeValueChar((char)(ch = *pSrc)) && ch < 0x80) { *pDst++ = (<#= BufferType #>)ch; pSrc++; @@ -793,7 +778,7 @@ namespace System.Xml // // 4). SE SC same as above EE a). check stored blockPro // b). true: indentLevel no change - internal class <#= ClassNameIndent #> : <#= ClassName #> + internal sealed class <#= ClassNameIndent #> : <#= ClassName #> { // // Fields @@ -848,7 +833,7 @@ namespace System.Xml { Debug.Assert(prefix.Length == 0); - base._currentElementProperties = (ElementProperties)_elementPropertySearch.FindCaseInsensitiveString(localName); + base._currentElementProperties = TernaryTreeReadOnly.FindElementProperty(localName); if (_endBlockPos == base._bufPos && (base._currentElementProperties & ElementProperties.BLOCK_WS) != 0) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlTernaryTree.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlTernaryTree.cs deleted file mode 100644 index 36450f80e4e651..00000000000000 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlTernaryTree.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// This file is generated by TernaryTreeGenerator.cs, -// and is used by the TernaryTreeRO class. - -namespace System.Xml -{ - // - // It contains the state for a ternary tree used to map HTML - // keywords to XSL output properties. - // - // Do not modify this file directly! (as if you could) - // - internal abstract class HtmlTernaryTree - { - internal static byte[] htmlElements = { - 73, 4, 147, 0, 77, 140, 162, 0, 71, 0, 0, 0, 0, 0, 0, 11, 68, 4, 85, 0, 73, 71, 92, 0, 86, 81, 0, 0, - 0, 0, 0, 64, 66, 3, 45, 0, 82, 21, 55, 0, 0, 0, 0, 8, 65, 0, 0, 0, 82, 4, 0, 0, 69, 0, 0, 0, - 65, 0, 0, 0, 0, 0, 0, 75, 68, 7, 8, 0, 68, 0, 0, 0, 82, 0, 0, 0, 69, 0, 0, 0, 83, 0, 0, 0, - 83, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 1, 80, 0, 0, 0, 80, 0, 0, 0, 76, 0, 0, 0, 69, 0, 0, 0, - 84, 0, 0, 0, 0, 0, 0, 64, 65, 0, 9, 0, 83, 0, 0, 0, 69, 0, 0, 0, 70, 5, 0, 0, 79, 0, 0, 0, - 78, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 72, 0, 0, 0, 73, 76, 0, 10, 0, 79, 0, 0, 0, 67, 0, 0, 0, - 75, 0, 0, 0, 81, 0, 0, 0, 85, 0, 0, 0, 79, 0, 0, 0, 84, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 65, - 79, 0, 0, 0, 68, 0, 0, 0, 89, 0, 0, 0, 0, 0, 0, 64, 67, 0, 0, 0, 79, 3, 0, 0, 76, 0, 0, 0, - 0, 0, 22, 72, 65, 0, 13, 0, 80, 0, 0, 0, 84, 0, 0, 0, 73, 0, 0, 0, 79, 0, 0, 0, 78, 0, 0, 0, - 0, 0, 0, 64, 85, 0, 0, 0, 84, 0, 0, 0, 84, 0, 0, 0, 79, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 2, - 69, 0, 0, 0, 78, 0, 0, 0, 84, 0, 0, 0, 69, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 64, 68, 0, 8, 0, - 0, 0, 0, 64, 71, 0, 0, 0, 82, 0, 0, 0, 79, 0, 0, 0, 85, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 64, - 69, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 65, 82, 0, 0, 0, 0, 0, 0, 66, 72, 3, 0, 0, 50, 31, 33, 0, - 0, 0, 0, 64, 70, 0, 0, 0, 79, 8, 16, 0, 78, 0, 20, 0, 84, 0, 0, 0, 0, 0, 0, 64, 84, 2, 0, 0, - 0, 0, 0, 64, 76, 0, 0, 0, 0, 0, 0, 66, 73, 0, 0, 0, 69, 0, 0, 0, 76, 0, 0, 0, 68, 0, 0, 0, - 83, 0, 0, 0, 69, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 64, 82, 0, 0, 0, 65, 0, 0, 0, 77, 0, 0, 0, - 69, 0, 0, 0, 0, 0, 4, 74, 82, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 65, 83, 0, 0, 0, 69, 0, 0, 0, - 84, 0, 0, 0, 0, 0, 0, 64, 49, 0, 0, 0, 0, 0, 0, 64, 54, 2, 8, 0, 0, 0, 0, 64, 52, 2, 4, 0, - 0, 0, 0, 64, 51, 0, 0, 0, 0, 0, 0, 64, 53, 0, 0, 0, 0, 0, 0, 64, 82, 2, 6, 0, 0, 0, 0, 74, - 69, 0, 0, 0, 65, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 97, 84, 0, 0, 0, 77, 0, 0, 0, 76, 0, 0, 0, - 0, 0, 0, 64, 70, 0, 0, 0, 82, 0, 0, 0, 65, 0, 0, 0, 77, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 64, - 80, 4, 84, 0, 82, 77, 0, 0, 69, 0, 0, 0, 0, 0, 0, 64, 77, 5, 37, 0, 69, 30, 0, 0, 84, 32, 0, 0, - 65, 0, 0, 0, 0, 0, 0, 72, 76, 0, 0, 0, 69, 0, 20, 0, 71, 0, 0, 0, 69, 0, 0, 0, 78, 0, 0, 0, - 68, 0, 0, 0, 0, 0, 0, 64, 78, 0, 7, 0, 83, 2, 0, 0, 0, 0, 0, 65, 80, 0, 0, 0, 85, 0, 0, 0, - 84, 0, 0, 0, 0, 0, 0, 11, 83, 0, 0, 0, 73, 0, 0, 0, 78, 0, 0, 0, 68, 0, 0, 0, 69, 0, 0, 0, - 88, 0, 0, 0, 0, 0, 0, 72, 73, 0, 0, 0, 78, 3, 0, 0, 75, 0, 0, 0, 0, 0, 0, 73, 0, 0, 0, 64, - 65, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 64, 78, 0, 0, 0, 85, 0, 0, 0, 0, 0, 0, 66, 79, 3, 0, 0, - 76, 18, 24, 0, 0, 0, 0, 66, 78, 0, 0, 0, 79, 0, 0, 0, 83, 7, 0, 0, 67, 0, 0, 0, 82, 0, 0, 0, - 73, 0, 0, 0, 80, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 64, 70, 0, 0, 0, 82, 0, 0, 0, 65, 0, 0, 0, - 77, 0, 0, 0, 69, 0, 0, 0, 83, 0, 0, 0, 0, 0, 0, 64, 66, 0, 0, 0, 74, 0, 0, 0, 69, 0, 0, 0, - 67, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 3, 80, 0, 0, 0, 84, 0, 0, 0, 73, 4, 0, 0, 79, 0, 0, 0, - 78, 0, 0, 0, 0, 0, 0, 66, 71, 0, 0, 0, 82, 0, 0, 0, 79, 0, 0, 0, 85, 0, 0, 0, 80, 0, 0, 0, - 0, 0, 0, 66, 0, 0, 1, 64, 65, 0, 0, 0, 82, 0, 0, 0, 65, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 72, - 84, 3, 65, 0, 68, 28, 38, 0, 0, 0, 0, 66, 83, 8, 0, 0, 69, 6, 15, 0, 76, 0, 0, 0, 69, 0, 0, 0, - 67, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 2, 0, 0, 3, 64, 81, 0, 0, 0, 0, 0, 0, 1, 67, 0, 0, 0, - 82, 0, 0, 0, 73, 0, 0, 0, 80, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 19, 84, 0, 0, 0, 89, 4, 0, 0, - 76, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 80, 82, 0, 0, 0, 73, 0, 0, 0, 75, 0, 0, 0, 69, 0, 0, 0, - 0, 0, 0, 64, 65, 0, 5, 0, 66, 0, 0, 0, 76, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 65, 66, 0, 0, 0, - 79, 0, 0, 0, 68, 0, 0, 0, 89, 0, 0, 0, 0, 0, 0, 64, 72, 5, 19, 0, 69, 17, 0, 0, 65, 0, 0, 0, - 68, 0, 0, 0, 0, 0, 0, 64, 70, 5, 0, 0, 79, 0, 0, 0, 79, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 64, - 69, 0, 0, 0, 88, 0, 0, 0, 84, 0, 0, 0, 65, 0, 0, 0, 82, 0, 0, 0, 69, 0, 0, 0, 65, 0, 0, 0, - 0, 0, 0, 2, 0, 0, 0, 66, 82, 2, 0, 0, 0, 0, 0, 64, 73, 0, 0, 0, 84, 0, 0, 0, 76, 0, 0, 0, - 69, 0, 0, 0, 0, 0, 0, 64, 85, 0, 3, 0, 76, 0, 0, 0, 0, 0, 0, 66, 88, 0, 0, 0, 77, 0, 0, 0, - 80, 0, 0, 0, 0, 0, 0, 64, - }; - internal static byte[] htmlAttributes = { - 72, 5, 77, 0, 82, 0, 0, 0, 69, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 1, 67, 12, 40, 0, 79, 7, 0, 0, - 77, 31, 0, 0, 80, 0, 0, 0, 65, 0, 0, 0, 67, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 2, 73, 11, 18, 0, - 84, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 1, 65, 0, 0, 0, 67, 0, 0, 0, 84, 0, 0, 0, 73, 0, 0, 0, - 79, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 1, 72, 0, 0, 0, 69, 0, 0, 0, 67, 0, 0, 0, 75, 0, 0, 0, - 69, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 2, 76, 0, 0, 0, 65, 0, 0, 0, 83, 0, 0, 0, 83, 0, 0, 0, - 73, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 1, 68, 0, 0, 0, 69, 0, 0, 0, 66, 0, 0, 0, 65, 0, 0, 0, - 83, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 1, 68, 0, 28, 0, 69, 7, 15, 0, 67, 0, 22, 0, 76, 0, 0, 0, - 65, 0, 0, 0, 82, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 2, 65, 0, 0, 0, 84, 0, 0, 0, 65, 0, 0, 0, - 0, 0, 1, 1, 83, 0, 0, 0, 82, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 1, 73, 0, 0, 0, 83, 0, 0, 0, - 65, 0, 0, 0, 66, 0, 0, 0, 76, 0, 0, 0, 69, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 2, 70, 0, 0, 0, - 69, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 2, 70, 0, 0, 0, 79, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 1, - 78, 8, 48, 0, 79, 36, 0, 0, 83, 30, 55, 0, 72, 0, 0, 0, 65, 0, 0, 0, 68, 0, 0, 0, 69, 0, 0, 0, - 0, 0, 0, 2, 77, 9, 0, 0, 85, 0, 0, 0, 76, 0, 0, 0, 84, 0, 0, 0, 73, 0, 0, 0, 80, 0, 0, 0, - 76, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 2, 73, 0, 6, 0, 83, 0, 0, 0, 77, 0, 0, 0, 65, 0, 0, 0, - 80, 0, 0, 0, 0, 0, 0, 2, 76, 0, 0, 0, 79, 0, 0, 0, 78, 0, 0, 0, 71, 0, 0, 0, 68, 0, 0, 0, - 69, 0, 0, 0, 83, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 1, 72, 0, 9, 0, 82, 0, 0, 0, 69, 0, 0, 0, - 70, 0, 0, 0, 0, 0, 0, 2, 65, 0, 0, 0, 77, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 1, 82, 0, 0, 0, - 69, 0, 0, 0, 83, 0, 0, 0, 73, 0, 0, 0, 90, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 2, 82, 14, 22, 0, - 69, 0, 0, 0, 65, 0, 0, 0, 68, 0, 0, 0, 79, 0, 0, 0, 78, 0, 0, 0, 76, 0, 0, 0, 89, 0, 0, 0, - 0, 0, 0, 2, 87, 0, 0, 0, 82, 0, 0, 0, 65, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 2, 80, 0, 0, 0, - 82, 0, 0, 0, 79, 0, 0, 0, 70, 0, 0, 0, 73, 0, 0, 0, 76, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 1, - 83, 0, 12, 0, 82, 3, 0, 0, 67, 0, 0, 0, 0, 0, 0, 1, 69, 0, 0, 0, 76, 0, 0, 0, 69, 0, 0, 0, - 67, 0, 0, 0, 84, 0, 0, 0, 69, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 2, 85, 0, 0, 0, 83, 0, 0, 0, - 69, 0, 0, 0, 77, 0, 0, 0, 65, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 1, - }; - } -} diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlUtf8RawTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlUtf8RawTextWriter.cs index 2d3597534cb58d..5bf76a9ad7e84c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlUtf8RawTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlUtf8RawTextWriter.cs @@ -5,13 +5,8 @@ // Instead, modify HtmlRawTextWriterGenerator.ttinclude #nullable disable -using System; using System.IO; -using System.Text; -using System.Xml; -using System.Xml.Schema; using System.Diagnostics; -using MS.Internal.Xml; namespace System.Xml { @@ -27,9 +22,6 @@ internal class HtmlUtf8RawTextWriter : XmlUtf8RawTextWriter private string _mediaType; private bool _doNotEscapeUriAttributes; - protected static TernaryTreeReadOnly _elementPropertySearch; - protected static TernaryTreeReadOnly _attributePropertySearch; - private const int StackIncrement = 10; public HtmlUtf8RawTextWriter(Stream stream, XmlWriterSettings settings) : base(stream, settings) @@ -103,7 +95,7 @@ public override void WriteStartElement(string prefix, string localName, string n { Debug.Assert(prefix.Length == 0); - _currentElementProperties = (ElementProperties)_elementPropertySearch.FindCaseInsensitiveString(localName); + _currentElementProperties = TernaryTreeReadOnly.FindElementProperty(localName); base._bufBytes[_bufPos++] = (byte)'<'; base.RawText(localName); base._attrEndPos = _bufPos; @@ -294,7 +286,7 @@ public override void WriteStartAttribute(string prefix, string localName, string if ((_currentElementProperties & (ElementProperties.BOOL_PARENT | ElementProperties.URI_PARENT | ElementProperties.NAME_PARENT)) != 0) { - _currentAttributeProperties = (AttributeProperties)_attributePropertySearch.FindCaseInsensitiveString(localName) & + _currentAttributeProperties = TernaryTreeReadOnly.FindAttributeProperty(localName) & (AttributeProperties)_currentElementProperties; if ((_currentAttributeProperties & AttributeProperties.BOOLEAN) != 0) @@ -424,13 +416,6 @@ private void Init(XmlWriterSettings settings) Debug.Assert((int)ElementProperties.BOOL_PARENT == (int)AttributeProperties.BOOLEAN); Debug.Assert((int)ElementProperties.NAME_PARENT == (int)AttributeProperties.NAME); - if (_elementPropertySearch == null) - { - // _elementPropertySearch should be init last for the mutli thread safe situation. - _attributePropertySearch = new TernaryTreeReadOnly(HtmlTernaryTree.htmlAttributes); - _elementPropertySearch = new TernaryTreeReadOnly(HtmlTernaryTree.htmlElements); - } - _elementScope = new ByteStack(StackIncrement); _uriEscapingBuffer = new byte[5]; _currentElementProperties = ElementProperties.DEFAULT; @@ -809,7 +794,7 @@ public override void WriteStartElement(string prefix, string localName, string n { Debug.Assert(prefix.Length == 0); - base._currentElementProperties = (ElementProperties)_elementPropertySearch.FindCaseInsensitiveString(localName); + base._currentElementProperties = TernaryTreeReadOnly.FindElementProperty(localName); if (_endBlockPos == base._bufPos && (base._currentElementProperties & ElementProperties.BLOCK_WS) != 0) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadOnlyTernaryTree.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadOnlyTernaryTree.cs index f345e151b61cb2..05a5ffc3a5ecfb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadOnlyTernaryTree.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadOnlyTernaryTree.cs @@ -1,21 +1,32 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.IO; -using System.Diagnostics; - namespace System.Xml { - // Array index to indicate the meaning of the each byte. - internal enum TernaryTreeByte { characterByte = 0, leftTree = 1, rightTree = 2, data = 3 }; - // // XSL HTML output method properties // // Keep the first four bits in sync, so that the element and attribute mask operation can be combined. - internal enum ElementProperties : uint { DEFAULT = 0, URI_PARENT = 1, BOOL_PARENT = 2, NAME_PARENT = 4, EMPTY = 8, NO_ENTITIES = 16, HEAD = 32, BLOCK_WS = 64, HAS_NS = 128 } - internal enum AttributeProperties : uint { DEFAULT = 0, URI = 1, BOOLEAN = 2, NAME = 4 } + internal enum ElementProperties : uint + { + DEFAULT = 0, + URI_PARENT = 1, + BOOL_PARENT = 2, + NAME_PARENT = 4, + EMPTY = 8, + NO_ENTITIES = 16, + HEAD = 32, + BLOCK_WS = 64, + HAS_NS = 128 + } + + internal enum AttributeProperties : uint + { + DEFAULT = 0, + URI = 1, + BOOLEAN = 2, + NAME = 4 + } /** @@ -27,16 +38,97 @@ internal enum AttributeProperties : uint { DEFAULT = 0, URI = 1, BOOLEAN = 2, NA * * Note: Only strings composed of ASCII characters can exist in the tree. */ - internal sealed class TernaryTreeReadOnly + internal static class TernaryTreeReadOnly { - private readonly byte[] _nodeBuffer; - - //define the array positions - - - public TernaryTreeReadOnly(byte[] nodeBuffer) + // Array index to indicate the meaning of the each byte. + private enum TernaryTreeByte + { + CharacterByte = 0, + LeftTree = 1, + RightTree = 2, + Data = 3 + } + private static ReadOnlySpan HtmlElements => new byte[] + { + 73, 4, 147, 0, 77, 140, 162, 0, 71, 0, 0, 0, 0, 0, 0, 11, 68, 4, 85, 0, 73, 71, 92, 0, 86, 81, 0, 0, + 0, 0, 0, 64, 66, 3, 45, 0, 82, 21, 55, 0, 0, 0, 0, 8, 65, 0, 0, 0, 82, 4, 0, 0, 69, 0, 0, 0, + 65, 0, 0, 0, 0, 0, 0, 75, 68, 7, 8, 0, 68, 0, 0, 0, 82, 0, 0, 0, 69, 0, 0, 0, 83, 0, 0, 0, + 83, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 1, 80, 0, 0, 0, 80, 0, 0, 0, 76, 0, 0, 0, 69, 0, 0, 0, + 84, 0, 0, 0, 0, 0, 0, 64, 65, 0, 9, 0, 83, 0, 0, 0, 69, 0, 0, 0, 70, 5, 0, 0, 79, 0, 0, 0, + 78, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 72, 0, 0, 0, 73, 76, 0, 10, 0, 79, 0, 0, 0, 67, 0, 0, 0, + 75, 0, 0, 0, 81, 0, 0, 0, 85, 0, 0, 0, 79, 0, 0, 0, 84, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 65, + 79, 0, 0, 0, 68, 0, 0, 0, 89, 0, 0, 0, 0, 0, 0, 64, 67, 0, 0, 0, 79, 3, 0, 0, 76, 0, 0, 0, + 0, 0, 22, 72, 65, 0, 13, 0, 80, 0, 0, 0, 84, 0, 0, 0, 73, 0, 0, 0, 79, 0, 0, 0, 78, 0, 0, 0, + 0, 0, 0, 64, 85, 0, 0, 0, 84, 0, 0, 0, 84, 0, 0, 0, 79, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 2, + 69, 0, 0, 0, 78, 0, 0, 0, 84, 0, 0, 0, 69, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 64, 68, 0, 8, 0, + 0, 0, 0, 64, 71, 0, 0, 0, 82, 0, 0, 0, 79, 0, 0, 0, 85, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 64, + 69, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 65, 82, 0, 0, 0, 0, 0, 0, 66, 72, 3, 0, 0, 50, 31, 33, 0, + 0, 0, 0, 64, 70, 0, 0, 0, 79, 8, 16, 0, 78, 0, 20, 0, 84, 0, 0, 0, 0, 0, 0, 64, 84, 2, 0, 0, + 0, 0, 0, 64, 76, 0, 0, 0, 0, 0, 0, 66, 73, 0, 0, 0, 69, 0, 0, 0, 76, 0, 0, 0, 68, 0, 0, 0, + 83, 0, 0, 0, 69, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 64, 82, 0, 0, 0, 65, 0, 0, 0, 77, 0, 0, 0, + 69, 0, 0, 0, 0, 0, 4, 74, 82, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 65, 83, 0, 0, 0, 69, 0, 0, 0, + 84, 0, 0, 0, 0, 0, 0, 64, 49, 0, 0, 0, 0, 0, 0, 64, 54, 2, 8, 0, 0, 0, 0, 64, 52, 2, 4, 0, + 0, 0, 0, 64, 51, 0, 0, 0, 0, 0, 0, 64, 53, 0, 0, 0, 0, 0, 0, 64, 82, 2, 6, 0, 0, 0, 0, 74, + 69, 0, 0, 0, 65, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 97, 84, 0, 0, 0, 77, 0, 0, 0, 76, 0, 0, 0, + 0, 0, 0, 64, 70, 0, 0, 0, 82, 0, 0, 0, 65, 0, 0, 0, 77, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 64, + 80, 4, 84, 0, 82, 77, 0, 0, 69, 0, 0, 0, 0, 0, 0, 64, 77, 5, 37, 0, 69, 30, 0, 0, 84, 32, 0, 0, + 65, 0, 0, 0, 0, 0, 0, 72, 76, 0, 0, 0, 69, 0, 20, 0, 71, 0, 0, 0, 69, 0, 0, 0, 78, 0, 0, 0, + 68, 0, 0, 0, 0, 0, 0, 64, 78, 0, 7, 0, 83, 2, 0, 0, 0, 0, 0, 65, 80, 0, 0, 0, 85, 0, 0, 0, + 84, 0, 0, 0, 0, 0, 0, 11, 83, 0, 0, 0, 73, 0, 0, 0, 78, 0, 0, 0, 68, 0, 0, 0, 69, 0, 0, 0, + 88, 0, 0, 0, 0, 0, 0, 72, 73, 0, 0, 0, 78, 3, 0, 0, 75, 0, 0, 0, 0, 0, 0, 73, 0, 0, 0, 64, + 65, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 64, 78, 0, 0, 0, 85, 0, 0, 0, 0, 0, 0, 66, 79, 3, 0, 0, + 76, 18, 24, 0, 0, 0, 0, 66, 78, 0, 0, 0, 79, 0, 0, 0, 83, 7, 0, 0, 67, 0, 0, 0, 82, 0, 0, 0, + 73, 0, 0, 0, 80, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 64, 70, 0, 0, 0, 82, 0, 0, 0, 65, 0, 0, 0, + 77, 0, 0, 0, 69, 0, 0, 0, 83, 0, 0, 0, 0, 0, 0, 64, 66, 0, 0, 0, 74, 0, 0, 0, 69, 0, 0, 0, + 67, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 3, 80, 0, 0, 0, 84, 0, 0, 0, 73, 4, 0, 0, 79, 0, 0, 0, + 78, 0, 0, 0, 0, 0, 0, 66, 71, 0, 0, 0, 82, 0, 0, 0, 79, 0, 0, 0, 85, 0, 0, 0, 80, 0, 0, 0, + 0, 0, 0, 66, 0, 0, 1, 64, 65, 0, 0, 0, 82, 0, 0, 0, 65, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 72, + 84, 3, 65, 0, 68, 28, 38, 0, 0, 0, 0, 66, 83, 8, 0, 0, 69, 6, 15, 0, 76, 0, 0, 0, 69, 0, 0, 0, + 67, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 2, 0, 0, 3, 64, 81, 0, 0, 0, 0, 0, 0, 1, 67, 0, 0, 0, + 82, 0, 0, 0, 73, 0, 0, 0, 80, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 19, 84, 0, 0, 0, 89, 4, 0, 0, + 76, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 80, 82, 0, 0, 0, 73, 0, 0, 0, 75, 0, 0, 0, 69, 0, 0, 0, + 0, 0, 0, 64, 65, 0, 5, 0, 66, 0, 0, 0, 76, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 65, 66, 0, 0, 0, + 79, 0, 0, 0, 68, 0, 0, 0, 89, 0, 0, 0, 0, 0, 0, 64, 72, 5, 19, 0, 69, 17, 0, 0, 65, 0, 0, 0, + 68, 0, 0, 0, 0, 0, 0, 64, 70, 5, 0, 0, 79, 0, 0, 0, 79, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 64, + 69, 0, 0, 0, 88, 0, 0, 0, 84, 0, 0, 0, 65, 0, 0, 0, 82, 0, 0, 0, 69, 0, 0, 0, 65, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 66, 82, 2, 0, 0, 0, 0, 0, 64, 73, 0, 0, 0, 84, 0, 0, 0, 76, 0, 0, 0, + 69, 0, 0, 0, 0, 0, 0, 64, 85, 0, 3, 0, 76, 0, 0, 0, 0, 0, 0, 66, 88, 0, 0, 0, 77, 0, 0, 0, + 80, 0, 0, 0, 0, 0, 0, 64, + }; + private static ReadOnlySpan HtmlAttributes => new byte[] + { + 72, 5, 77, 0, 82, 0, 0, 0, 69, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 1, 67, 12, 40, 0, 79, 7, 0, 0, + 77, 31, 0, 0, 80, 0, 0, 0, 65, 0, 0, 0, 67, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 2, 73, 11, 18, 0, + 84, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 1, 65, 0, 0, 0, 67, 0, 0, 0, 84, 0, 0, 0, 73, 0, 0, 0, + 79, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 1, 72, 0, 0, 0, 69, 0, 0, 0, 67, 0, 0, 0, 75, 0, 0, 0, + 69, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 2, 76, 0, 0, 0, 65, 0, 0, 0, 83, 0, 0, 0, 83, 0, 0, 0, + 73, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 1, 68, 0, 0, 0, 69, 0, 0, 0, 66, 0, 0, 0, 65, 0, 0, 0, + 83, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 1, 68, 0, 28, 0, 69, 7, 15, 0, 67, 0, 22, 0, 76, 0, 0, 0, + 65, 0, 0, 0, 82, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 2, 65, 0, 0, 0, 84, 0, 0, 0, 65, 0, 0, 0, + 0, 0, 1, 1, 83, 0, 0, 0, 82, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 1, 73, 0, 0, 0, 83, 0, 0, 0, + 65, 0, 0, 0, 66, 0, 0, 0, 76, 0, 0, 0, 69, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 2, 70, 0, 0, 0, + 69, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 2, 70, 0, 0, 0, 79, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 1, + 78, 8, 48, 0, 79, 36, 0, 0, 83, 30, 55, 0, 72, 0, 0, 0, 65, 0, 0, 0, 68, 0, 0, 0, 69, 0, 0, 0, + 0, 0, 0, 2, 77, 9, 0, 0, 85, 0, 0, 0, 76, 0, 0, 0, 84, 0, 0, 0, 73, 0, 0, 0, 80, 0, 0, 0, + 76, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 2, 73, 0, 6, 0, 83, 0, 0, 0, 77, 0, 0, 0, 65, 0, 0, 0, + 80, 0, 0, 0, 0, 0, 0, 2, 76, 0, 0, 0, 79, 0, 0, 0, 78, 0, 0, 0, 71, 0, 0, 0, 68, 0, 0, 0, + 69, 0, 0, 0, 83, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 1, 72, 0, 9, 0, 82, 0, 0, 0, 69, 0, 0, 0, + 70, 0, 0, 0, 0, 0, 0, 2, 65, 0, 0, 0, 77, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 1, 82, 0, 0, 0, + 69, 0, 0, 0, 83, 0, 0, 0, 73, 0, 0, 0, 90, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 2, 82, 14, 22, 0, + 69, 0, 0, 0, 65, 0, 0, 0, 68, 0, 0, 0, 79, 0, 0, 0, 78, 0, 0, 0, 76, 0, 0, 0, 89, 0, 0, 0, + 0, 0, 0, 2, 87, 0, 0, 0, 82, 0, 0, 0, 65, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 2, 80, 0, 0, 0, + 82, 0, 0, 0, 79, 0, 0, 0, 70, 0, 0, 0, 73, 0, 0, 0, 76, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 1, + 83, 0, 12, 0, 82, 3, 0, 0, 67, 0, 0, 0, 0, 0, 0, 1, 69, 0, 0, 0, 76, 0, 0, 0, 69, 0, 0, 0, + 67, 0, 0, 0, 84, 0, 0, 0, 69, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 2, 85, 0, 0, 0, 83, 0, 0, 0, + 69, 0, 0, 0, 77, 0, 0, 0, 65, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 1, + }; + public static ElementProperties FindElementProperty(ReadOnlySpan stringToFind) + { + return (ElementProperties)FindCaseInsensitiveString(stringToFind, HtmlElements); + } + public static AttributeProperties FindAttributeProperty(ReadOnlySpan stringToFind) { - _nodeBuffer = nodeBuffer; + return (AttributeProperties)FindCaseInsensitiveString(stringToFind, HtmlAttributes); } /* ---------------------------------------------------------------------------- @@ -45,53 +137,61 @@ public TernaryTreeReadOnly(byte[] nodeBuffer) Find a Unicode string in the ternary tree and return the data byte it's mapped to. Find is case-insensitive. */ - public byte FindCaseInsensitiveString(string stringToFind) + private static byte FindCaseInsensitiveString(ReadOnlySpan stringToFind, ReadOnlySpan nodeBuffer) { - //Debug.Assert(wszFind != null && wszFind.Length != 0); - int stringPos = 0, nodePos = 0; - int charToFind, charInTheTree; - byte[] node = _nodeBuffer; + int charToFind = stringToFind[stringPos]; - charToFind = stringToFind[stringPos]; + if (charToFind > 'z') + { + return 0; // Ternary tree only stores ASCII strings + } - if (charToFind > 'z') return 0; // Ternary tree only stores ASCII strings - if (charToFind >= 'a') charToFind -= ('a' - 'A'); // Normalize to upper case + if (charToFind >= 'a') + { + charToFind -= 'a' - 'A'; // Normalize to upper case + } while (true) { int pos = nodePos * 4; - - charInTheTree = node[pos + (int)TernaryTreeByte.characterByte]; + int charInTheTree = nodeBuffer[pos + (int)TernaryTreeByte.CharacterByte]; //Console.WriteLine("charToFind: {0},charInTheTree: {1}, nodePos: {2}", charToFind, charInTheTree, nodePos); if (charToFind < charInTheTree) { // If input character is less than the tree character, take the left branch - if (node[pos + (int)TernaryTreeByte.leftTree] == 0x0) + if (nodeBuffer[pos + (int)TernaryTreeByte.LeftTree] == 0x0) { break; } - nodePos = nodePos + node[pos + (int)TernaryTreeByte.leftTree]; + + nodePos += nodeBuffer[pos + (int)TernaryTreeByte.LeftTree]; } else if (charToFind > charInTheTree) { // If input character is greater than the tree character, take the right branch - if (node[pos + (int)TernaryTreeByte.rightTree] == 0x0) + if (nodeBuffer[pos + (int)TernaryTreeByte.RightTree] == 0x0) + { break; - nodePos = nodePos + node[pos + (int)TernaryTreeByte.rightTree]; + } + + nodePos += nodeBuffer[pos + (int)TernaryTreeByte.RightTree]; } else { // If input character is equal to the tree character, take the equal branch if (charToFind == 0) - return node[pos + (int)TernaryTreeByte.data]; + { + return nodeBuffer[pos + (int)TernaryTreeByte.Data]; + } // The offset for the equal branch is always one ++nodePos; // Move to the next input character ++stringPos; + if (stringPos == stringToFind.Length) { charToFind = 0; @@ -99,13 +199,20 @@ public byte FindCaseInsensitiveString(string stringToFind) else { charToFind = stringToFind[stringPos]; - if (charToFind > 'z') return 0; // Ternary tree only stores ASCII strings - if (charToFind >= 'a') charToFind -= ('a' - 'A'); // Normalize to upper case + + if (charToFind > 'z') + { + return 0; // Ternary tree only stores ASCII strings + } + + if (charToFind >= 'a') + { + charToFind -= 'a' - 'A'; // Normalize to upper case + } } } } - // Return default return 0; } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextEncoder.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextEncoder.cs index e03320af38639c..d2fdefe60d0dc7 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextEncoder.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextEncoder.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.IO; using System.Text; using System.Diagnostics; @@ -250,9 +249,9 @@ internal void WriteSurrogateCharEntity(char lowChar, char highChar) _textWriter.Write(';'); } - internal void Write(string text) + internal void Write(ReadOnlySpan text) { - if (text == null) + if (text.IsEmpty) { return; } @@ -301,13 +300,13 @@ internal void Write(string text) break; } - char[] helperBuffer = new char[256]; while (true) { if (startPos < i) { - WriteStringFragment(text, startPos, i - startPos, helperBuffer); + _textWriter.Write(text.Slice(startPos, i - startPos)); } + if (i == len) { break; @@ -516,27 +515,6 @@ internal void WriteEntityRef(string name) // // Private implementation methods // - // This is a helper method to workaround the fact that TextWriter does not have a Write method - // for fragment of a string such as Write( string, offset, count). - // The string fragment will be written out by copying into a small helper buffer and then - // calling textWriter to write out the buffer. - private void WriteStringFragment(string str, int offset, int count, char[] helperBuffer) - { - int bufferSize = helperBuffer.Length; - while (count > 0) - { - int copyCount = count; - if (copyCount > bufferSize) - { - copyCount = bufferSize; - } - - str.CopyTo(offset, helperBuffer, 0, copyCount); - _textWriter.Write(helperBuffer, 0, copyCount); - offset += copyCount; - count -= copyCount; - } - } private void WriteCharEntityImpl(char ch) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltLoader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltLoader.cs index 6272150fdd650c..77a16b8600fd15 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltLoader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltLoader.cs @@ -16,7 +16,7 @@ namespace System.Xml.Xsl.Xslt { using ContextInfo = XsltInput.ContextInfo; - using f = AstFactory; + using F = AstFactory; using TypeFactory = XmlQueryTypeFactory; using QName = XsltInput.DelayedQName; using XsltAttribute = XsltInput.XsltAttribute; @@ -31,7 +31,7 @@ internal sealed class XsltLoader : IErrorHelper private Stylesheet? _curStylesheet; // Current stylesheet private Template? _curTemplate; // Current template - internal static QilName nullMode = f.QName(string.Empty); + internal static QilName nullMode = F.QName(string.Empty); // Flags which control attribute versioning public static int V1Opt = 1; @@ -90,7 +90,7 @@ public void Load(Compiler compiler, object stylesheet, XmlResolver? xmlResolver) } } Debug.Assert(compiler.Root != null); - compiler.StartApplyTemplates = f.ApplyTemplates(nullMode); + compiler.StartApplyTemplates = F.ApplyTemplates(nullMode); ProcessOutputSettings(); foreach (AttributeSet attSet in compiler.AttributeSets.Values) { @@ -314,7 +314,7 @@ private void LoadSimplifiedStylesheet() Debug.Assert(_curTemplate == null); // Prefix will be fixed later in LoadLiteralResultElement() - _curTemplate = f.Template(/*name:*/null, /*match:*/"/", /*mode:*/nullMode, /*priority:*/double.NaN, _input.XslVersion); + _curTemplate = F.Template(/*name:*/null, /*match:*/"/", /*mode:*/nullMode, /*priority:*/double.NaN, _input.XslVersion); // This template has mode=null match="/" and no imports _input.CanHaveApplyImports = true; @@ -879,7 +879,7 @@ private void LoadKey(NsDecl? stylesheetNsList) } } - Key key = (Key)SetInfo(f.Key(keyName, match, use, _input.XslVersion), null, ctxInfo); + Key key = (Key)SetInfo(F.Key(keyName, match, use, _input.XslVersion), null, ctxInfo); if (_compiler.Keys.Contains(keyName)) { @@ -1059,7 +1059,7 @@ private void LoadAttributeSet(NsDecl? stylesheetNsList) AttributeSet? set; if (!_curStylesheet!.AttributeSets!.TryGetValue(setName, out set)) { - set = f.AttributeSet(setName); + set = F.AttributeSet(setName); // First definition for setName within this stylesheet _curStylesheet.AttributeSets[setName] = set; if (!_compiler.AttributeSets.ContainsKey(setName)) @@ -1105,7 +1105,7 @@ private void LoadAttributeSet(NsDecl? stylesheetNsList) } } while (_input.MoveToNextSibling()); } - set.AddContent(SetInfo(f.List(), LoadEndTag(content), ctxInfo)); + set.AddContent(SetInfo(F.List(), LoadEndTag(content), ctxInfo)); } private void LoadGlobalVariableOrParameter(NsDecl? stylesheetNsList, XslNodeType nodeType) @@ -1168,7 +1168,7 @@ private void LoadTemplate(NsDecl? stylesheetNsList) ReportNYI("xsl:template/@as"); } - _curTemplate = f.Template(name, match, mode, priority, _input.XslVersion); + _curTemplate = F.Template(name, match, mode, priority, _input.XslVersion); // Template without match considered to not have mode and can't call xsl:apply-imports _input.CanHaveApplyImports = (match != null); @@ -1444,7 +1444,7 @@ private List LoadInstructions(List content, InstructionFlags f } break; case XmlNodeType.SignificantWhitespace: - result = SetLineInfo(f.Text(_input.Value), _input.BuildLineInfo()); + result = SetLineInfo(F.Text(_input.Value), _input.BuildLineInfo()); break; case XmlNodeType.Whitespace: continue; @@ -1532,7 +1532,7 @@ private List LoadWithParams(InstructionFlags flags) } else { - return SetInfo(f.Error(XslLoadException.CreateMessage(contentInfo, /*[XT0260]*/SR.Xslt_NotEmptyContents, _atoms.ApplyImports)), null, ctxInfo); + return SetInfo(F.Error(XslLoadException.CreateMessage(contentInfo, /*[XT0260]*/SR.Xslt_NotEmptyContents, _atoms.ApplyImports)), null, ctxInfo); } } content = null; @@ -1543,7 +1543,7 @@ private List LoadWithParams(InstructionFlags flags) content = null; } - return SetInfo(f.ApplyImports(/*Mode:*/_curTemplate!.Mode, _curStylesheet, _input.XslVersion), content, ctxInfo); + return SetInfo(F.ApplyImports(/*Mode:*/_curTemplate!.Mode, _curStylesheet, _input.XslVersion), content, ctxInfo); } // http://www.w3.org/TR/xslt#section-Applying-Template-Rules @@ -1564,7 +1564,7 @@ private XslNode XslApplyTemplates() List content = LoadWithParams(InstructionFlags.AllowSort); ctxInfo.SaveExtendedLineInfo(_input); - return SetInfo(f.ApplyTemplates(mode, select, ctxInfo, _input.XslVersion), + return SetInfo(F.ApplyTemplates(mode, select, ctxInfo, _input.XslVersion), content, ctxInfo ); } @@ -1581,7 +1581,7 @@ private XslNode XslCallTemplate() List content = LoadWithParams(InstructionFlags.None); ctxInfo.SaveExtendedLineInfo(_input); - return SetInfo(f.CallTemplate(name, ctxInfo), content, ctxInfo); + return SetInfo(F.CallTemplate(name, ctxInfo), content, ctxInfo); } // http://www.w3.org/TR/xslt#copying @@ -1611,7 +1611,7 @@ private XslNode XslCopy() ParseTypeAttribute(3); ParseValidationAttribute(4, /*defVal:*/false); - return SetInfo(f.Copy(), LoadEndTag(LoadInstructions(content)), ctxInfo); + return SetInfo(F.Copy(), LoadEndTag(LoadInstructions(content)), ctxInfo); } private readonly XsltAttribute[] _copyOfAttributes = { @@ -1631,7 +1631,7 @@ private XslNode XslCopyOf() ParseValidationAttribute(3, /*defVal:*/false); CheckNoContent(); - return SetInfo(f.CopyOf(select, _input.XslVersion), null, ctxInfo); + return SetInfo(F.CopyOf(select, _input.XslVersion), null, ctxInfo); } // http://www.w3.org/TR/xslt#fallback @@ -1651,7 +1651,7 @@ private XslNode XslIf() ContextInfo ctxInfo = _input.GetAttributes(_ifAttributes); string? test = ParseStringAttribute(0, "test"); - return SetInfo(f.If(test, _input.XslVersion), LoadInstructions(), ctxInfo); + return SetInfo(F.If(test, _input.XslVersion), LoadInstructions(), ctxInfo); } private XslNode XslChoose() @@ -1721,13 +1721,13 @@ private XslNode XslChoose() } while (_input.MoveToNextSibling()); } CheckError(!when, /*[XT_021]*/SR.Xslt_NoWhen); - return SetInfo(f.Choose(), content, ctxInfo); + return SetInfo(F.Choose(), content, ctxInfo); } private XslNode XslOtherwise() { ContextInfo ctxInfo = _input.GetAttributes(); - return SetInfo(f.Otherwise(), LoadInstructions(), ctxInfo); + return SetInfo(F.Otherwise(), LoadInstructions(), ctxInfo); } private readonly XsltAttribute[] _forEachAttributes = { @@ -1743,7 +1743,7 @@ private XslNode XslForEach() List content = LoadInstructions(InstructionFlags.AllowSort); ctxInfo.SaveExtendedLineInfo(_input); - return SetInfo(f.ForEach(select, ctxInfo, _input.XslVersion), + return SetInfo(F.ForEach(select, ctxInfo, _input.XslVersion), content, ctxInfo ); } @@ -1768,10 +1768,10 @@ private XslNode XslMessage() } if (select != null) { - content.Insert(0, f.CopyOf(select, _input.XslVersion)); + content.Insert(0, F.CopyOf(select, _input.XslVersion)); } - return SetInfo(f.Message(terminate), content, ctxInfo); + return SetInfo(F.Message(terminate), content, ctxInfo); } // http://www.w3.org/TR/xslt#number @@ -1830,7 +1830,7 @@ private XslNode XslNumber() CheckNoContent(); return SetInfo( - f.Number(level, count, from, value, + F.Number(level, count, from, value, format, lang, letterValue, groupingSeparator, groupingSize, _input.XslVersion ), @@ -1871,7 +1871,7 @@ private XslNode XslValueOf() if (select == null) { _input.SkipNode(); - return SetInfo(f.Error(XslLoadException.CreateMessage(ctxInfo.lineInfo, SR.Xslt_MissingAttribute, "select")), null, ctxInfo); + return SetInfo(F.Error(XslLoadException.CreateMessage(ctxInfo.lineInfo, SR.Xslt_MissingAttribute, "select")), null, ctxInfo); } CheckNoContent(); } @@ -1886,7 +1886,7 @@ private XslNode XslValueOf() } } - return SetInfo(f.XslNode(doe ? XslNodeType.ValueOfDoe : XslNodeType.ValueOf, null, select, _input.XslVersion), + return SetInfo(F.XslNode(doe ? XslNodeType.ValueOfDoe : XslNodeType.ValueOf, null, select, _input.XslVersion), null, ctxInfo ); } @@ -1967,7 +1967,7 @@ private VarPar XslVarPar() List content = LoadContent(select != null); CheckError((required == TriState.True) && (select != null || content.Count != 0), /*[???]*/SR.Xslt_RequiredAndSelect, name!.ToString()); - VarPar result = f.VarPar(nodeType, name, select, _input.XslVersion); + VarPar result = F.VarPar(nodeType, name, select, _input.XslVersion); SetInfo(result, content, ctxInfo); return result; } @@ -1983,7 +1983,7 @@ private XslNode XslComment() string? select = ParseStringAttribute(0, "select"); if (select != null) ReportNYI("xsl:comment/@select"); - return SetInfo(f.Comment(), LoadContent(select != null), ctxInfo); + return SetInfo(F.Comment(), LoadContent(select != null), ctxInfo); } private List LoadContent(bool hasSelect) @@ -2012,7 +2012,7 @@ private XslNode XslProcessingInstruction() string? select = ParseStringAttribute(1, "select"); if (select != null) ReportNYI("xsl:processing-instruction/@select"); - return SetInfo(f.PI(name, _input.XslVersion), LoadContent(select != null), ctxInfo); + return SetInfo(F.PI(name, _input.XslVersion), LoadContent(select != null), ctxInfo); } // http://www.w3.org/TR/xslt#section-Creating-Text @@ -2040,7 +2040,7 @@ private XslNode XslText() case XmlNodeType.Whitespace: case XmlNodeType.SignificantWhitespace: // xsl:text may contain multiple child text nodes separated by comments and PIs, which are ignored by XsltInput - content.Add(f.Text(_input.Value, hints)); + content.Add(F.Text(_input.Value, hints)); break; default: Debug.Assert(_input.NodeType == XmlNodeType.Element); @@ -2052,7 +2052,7 @@ private XslNode XslText() } // Empty xsl:text elements will be ignored - return SetInfo(f.List(), content, ctxInfo); + return SetInfo(F.List(), content, ctxInfo); } // http://www.w3.org/TR/xslt#section-Creating-Elements-with-xsl:element @@ -2084,7 +2084,7 @@ private XslNode XslElement() { AddUseAttributeSets(content); } - return SetInfo(f.Element(name, ns, _input.XslVersion), + return SetInfo(F.Element(name, ns, _input.XslVersion), LoadEndTag(LoadInstructions(content)), ctxInfo ); } @@ -2116,7 +2116,7 @@ private XslNode XslAttribute() ParseTypeAttribute(4); ParseValidationAttribute(5, /*defVal:*/false); - return SetInfo(f.Attribute(name, ns, _input.XslVersion), LoadContent(select != null), ctxInfo); + return SetInfo(F.Attribute(name, ns, _input.XslVersion), LoadContent(select != null), ctxInfo); } // http://www.w3.org/TR/xslt#sorting @@ -2167,7 +2167,7 @@ private XslNode XslSort(int sortNumber) select = "."; } - return SetInfo(f.Sort(select, lang, dataType, order, caseOrder, _input.XslVersion), + return SetInfo(F.Sort(select, lang, dataType, order, caseOrder, _input.XslVersion), null, ctxInfo ); } @@ -2222,12 +2222,12 @@ private XslNode XslAnalyzeString() { CheckError(matching != null, /*[???]*/SR.Xslt_AnalyzeStringDupChild, atoms.MatchingSubstring); // The current template rule becomes null, so we must not allow xsl:apply-import's within this element input.CanHaveApplyImports = false; - matching = SetInfo(f.List(), LoadInstructions(), ctxInfoChld); + matching = SetInfo(F.List(), LoadInstructions(), ctxInfoChld); } else if (input.IsXsltKeyword(atoms.NonMatchingSubstring)) { ContextInfo ctxInfoChld = input.GetAttributes(); CheckError(nonMatching != null, /*[???]*/SR.Xslt_AnalyzeStringDupChild, atoms.NonMatchingSubstring); input.CanHaveApplyImports = false; - nonMatching = SetInfo(f.List(), LoadInstructions(), ctxInfoChld); + nonMatching = SetInfo(F.List(), LoadInstructions(), ctxInfoChld); } else if (input.IsXsltKeyword(atoms.Fallback)) { XslFallback(); } else { @@ -2509,7 +2509,7 @@ private XslNode LoadLiteralResultElement(bool asStylesheet) if (_input.IsExtensionNamespace(nsUri)) { // This is not a literal result element, so drop all attributes we have collected - return SetInfo(f.List(), LoadFallbacks(name), ctxInfo); + return SetInfo(F.List(), LoadFallbacks(name), ctxInfo); } List content = new List(); @@ -2526,7 +2526,7 @@ private XslNode LoadLiteralResultElement(bool asStylesheet) { if (!_input.IsXsltNamespace()) { - XslNode att = f.LiteralAttribute(f.QName(_input.LocalName, _input.NamespaceUri, _input.Prefix), _input.Value, _input.XslVersion); + XslNode att = F.LiteralAttribute(F.QName(_input.LocalName, _input.NamespaceUri, _input.Prefix), _input.Value, _input.XslVersion); // QilGenerator takes care of AVTs, and needs line info AddInstruction(content, SetLineInfo(att, ctxInfo.lineInfo)); } @@ -2537,7 +2537,7 @@ private XslNode LoadLiteralResultElement(bool asStylesheet) } content = LoadEndTag(LoadInstructions(content)); - return SetInfo(f.LiteralElement(f.QName(name, nsUri, prefix)), content, ctxInfo); + return SetInfo(F.LiteralElement(F.QName(name, nsUri, prefix)), content, ctxInfo); } private void CheckWithParam(List content, XslNode withParam) @@ -2568,7 +2568,7 @@ private List LoadEndTag(List content) Debug.Assert(content != null); if (_compiler.IsDebug && !_input.IsEmptyElement) { - AddInstruction(content, SetLineInfo(f.Nop(), _input.BuildLineInfo())); + AddInstruction(content, SetLineInfo(F.Nop(), _input.BuildLineInfo())); } return content; } @@ -2586,7 +2586,7 @@ private List LoadEndTag(List content) { ContextInfo ctxInfo = _input.GetAttributes(); List fallbacks = LoadFallbacks(_input.LocalName); - return SetInfo(f.List(), fallbacks, ctxInfo); + return SetInfo(F.List(), fallbacks, ctxInfo); } } @@ -2603,7 +2603,7 @@ private List LoadFallbacks(string instrName) if (_input.IsXsltKeyword(_atoms.Fallback)) { ContextInfo ctxInfo = _input.GetAttributes(); - fallbacksArray.Add(SetInfo(f.List(), LoadInstructions(), ctxInfo)); + fallbacksArray.Add(SetInfo(F.List(), LoadInstructions(), ctxInfo)); } else { @@ -2616,7 +2616,7 @@ private List LoadFallbacks(string instrName) if (fallbacksArray.Count == 0) { fallbacksArray.Add( - f.Error(XslLoadException.CreateMessage(extElmLineInfo, SR.Xslt_UnknownExtensionElement, instrName)) + F.Error(XslLoadException.CreateMessage(extElmLineInfo, SR.Xslt_UnknownExtensionElement, instrName)) ); } return fallbacksArray; @@ -2803,7 +2803,7 @@ private bool ResolveQName(bool ignoreDefaultNs, string qname, out string localNa string prefix, localName, namespaceName; if (ResolveQName(/*ignoreDefaultNs:*/true, _input.Value, out localName, out namespaceName, out prefix)) { - result = f.QName(localName, namespaceName, prefix); + result = F.QName(localName, namespaceName, prefix); } } if (!required) @@ -2812,7 +2812,7 @@ private bool ResolveQName(bool ignoreDefaultNs, string qname, out string localNa } if (result == null && required) { - result = f.QName(_compiler.PhantomNCName, _compiler.CreatePhantomNamespace(), _compiler.PhantomNCName); + result = F.QName(_compiler.PhantomNCName, _compiler.CreatePhantomNamespace(), _compiler.PhantomNCName); } return result; } @@ -2832,7 +2832,7 @@ private QilName CreateXPathQName(string qname) { string prefix, localName, namespaceName; ResolveQName(/*ignoreDefaultNs:*/true, qname, out localName, out namespaceName, out prefix); - return f.QName(localName, namespaceName, prefix); + return F.QName(localName, namespaceName, prefix); } // Does not suppress errors @@ -2919,7 +2919,7 @@ private void AddUseAttributeSets(List list) _compiler.EnterForwardsCompatible(); foreach (string qname in XmlConvert.SplitString(_input.Value)) { - AddInstruction(list, SetLineInfo(f.UseAttributeSet(CreateXPathQName(qname)), _input.BuildLineInfo())); + AddInstruction(list, SetLineInfo(F.UseAttributeSet(CreateXPathQName(qname)), _input.BuildLineInfo())); } if (!_compiler.ExitForwardsCompatible(_input.ForwardCompatibility)) { diff --git a/src/libraries/System.Reflection.Context/src/CompatibilitySuppressions.xml b/src/libraries/System.Reflection.Context/src/CompatibilitySuppressions.xml deleted file mode 100644 index e18c500fc752c9..00000000000000 --- a/src/libraries/System.Reflection.Context/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - PKV006 - .NETCore,Version=v5.0 - - - PKV006 - .NETStandard,Version=v1.1 - - \ No newline at end of file diff --git a/src/libraries/System.Reflection.DispatchProxy/tests/DispatchProxyTests.cs b/src/libraries/System.Reflection.DispatchProxy/tests/DispatchProxyTests.cs index 9118e69a2a586a..3be55f8c0912cf 100644 --- a/src/libraries/System.Reflection.DispatchProxy/tests/DispatchProxyTests.cs +++ b/src/libraries/System.Reflection.DispatchProxy/tests/DispatchProxyTests.cs @@ -628,6 +628,7 @@ private static void testRefOutInInvocation(Action invocation, private static TestType_IHelloService CreateTestHelloProxy() => DispatchProxy.Create(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/62503", TestRuntimes.Mono)] [Fact] public static void Test_Unloadability() { diff --git a/src/libraries/System.Reflection.Metadata/src/CompatibilitySuppressions.xml b/src/libraries/System.Reflection.Metadata/src/CompatibilitySuppressions.xml deleted file mode 100644 index 64c3772cd60a42..00000000000000 --- a/src/libraries/System.Reflection.Metadata/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - PKV006 - .NETStandard,Version=v1.1 - - - PKV006 - .NETPortable,Version=v0.0,Profile=Profile7 - - \ No newline at end of file diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.Get.CornerCases.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.Get.CornerCases.cs index 72f4196dd19207..14ecdecd23c9af 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.Get.CornerCases.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.Get.CornerCases.cs @@ -413,7 +413,7 @@ private class Derived : Base { public event Action MyEvent; - public class myinner { } + public class @myinner { } public class MyInner { } public int MyProp { get; } diff --git a/src/libraries/System.Reflection/tests/GetTypeTests.cs b/src/libraries/System.Reflection/tests/GetTypeTests.cs index 09c8f272d35ef4..73ff198d9eab9a 100644 --- a/src/libraries/System.Reflection/tests/GetTypeTests.cs +++ b/src/libraries/System.Reflection/tests/GetTypeTests.cs @@ -279,7 +279,7 @@ public class MyClass2 { } public class MyClass3 { public class Inner { } - public class inner { } + public class @inner { } public class iNner { } public class inNer { } } diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/CompatibilitySuppressions.xml b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/CompatibilitySuppressions.xml deleted file mode 100644 index df781eabea842f..00000000000000 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - PKV006 - .NETStandard,Version=v1.0 - - - PKV006 - .NETFramework,Version=v4.5 - - \ No newline at end of file diff --git a/src/libraries/System.Runtime.Experimental/ref/System.Runtime.Experimental.csproj b/src/libraries/System.Runtime.Experimental/ref/System.Runtime.Experimental.csproj index 0ec6e1e5a1e891..4c6ae1a740fe26 100644 --- a/src/libraries/System.Runtime.Experimental/ref/System.Runtime.Experimental.csproj +++ b/src/libraries/System.Runtime.Experimental/ref/System.Runtime.Experimental.csproj @@ -19,8 +19,6 @@ true Exposes new experimental APIs from System.Runtime $(MSBuildProjectName) - - true diff --git a/src/libraries/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj b/src/libraries/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj index 10d77e4c9ec050..54240fecb8f7e5 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj +++ b/src/libraries/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj @@ -4,6 +4,7 @@ $(DefineConstants);Unix true true + true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Diagnostics/Stopwatch.cs b/src/libraries/System.Runtime.Extensions/tests/System/Diagnostics/Stopwatch.cs index 672e6af2f32acf..02226d4abe9ef2 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Diagnostics/Stopwatch.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Diagnostics/Stopwatch.cs @@ -11,19 +11,13 @@ public static class StopwatchTests { private static readonly ManualResetEvent s_sleepEvent = new ManualResetEvent(false); + private static readonly int s_defaultSleepTimeMs = PlatformDetection.IsBrowser ? 5 : 1; + [Fact] public static void GetTimestamp() { long ts1 = Stopwatch.GetTimestamp(); - if (PlatformDetection.IsBrowser) - { - // workaround for issue: https://github.com/dotnet/runtime/issues/62021 - Sleep(5); - } - else - { - Sleep(); - } + Sleep(s_defaultSleepTimeMs); long ts2 = Stopwatch.GetTimestamp(); Assert.NotEqual(ts1, ts2); } @@ -38,20 +32,20 @@ public static void ConstructStartAndStop() Assert.Equal(0, watch.ElapsedMilliseconds); watch.Start(); Assert.True(watch.IsRunning); - Sleep(); + Sleep(s_defaultSleepTimeMs); Assert.True(watch.Elapsed > TimeSpan.Zero); watch.Stop(); Assert.False(watch.IsRunning); var e1 = watch.Elapsed; - Sleep(); + Sleep(s_defaultSleepTimeMs); var e2 = watch.Elapsed; Assert.Equal(e1, e2); Assert.Equal((long)e1.TotalMilliseconds, watch.ElapsedMilliseconds); var t1 = watch.ElapsedTicks; - Sleep(); + Sleep(s_defaultSleepTimeMs); var t2 = watch.ElapsedTicks; Assert.Equal(t1, t2); } @@ -63,7 +57,7 @@ public static void StartNewAndReset() Assert.True(watch.IsRunning); watch.Start(); // should be no-op Assert.True(watch.IsRunning); - Sleep(); + Sleep(s_defaultSleepTimeMs); Assert.True(watch.Elapsed > TimeSpan.Zero); watch.Reset(); @@ -130,7 +124,7 @@ public static void ElapsedMilliseconds_WithinExpectedWindow() Assert.True(false, $"All {AllowedTries} fell outside of {WindowFactor} window of {SleepTime} sleep time: {string.Join(", ", results)}"); } - private static void Sleep(int milliseconds = 1) + private static void Sleep(int milliseconds) { s_sleepEvent.WaitOne(milliseconds); } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/Error.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/Error.cs index add53a9f9f4ba7..a71a29696a0073 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/Error.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/Error.cs @@ -17,12 +17,12 @@ private class Kernel32 public static extern int GetLastError(); } - private class libc + private class Libc { - [DllImport(nameof(libc))] + [DllImport("libc")] internal static unsafe extern int* __errno_location(); - [DllImport(nameof(libc))] + [DllImport("libc")] internal static unsafe extern int* __error(); } @@ -54,11 +54,11 @@ private static void SetLastError(int error) } else if (OperatingSystem.IsMacOS()) { - *libc.__error() = error; + *Libc.__error() = error; } else if (OperatingSystem.IsLinux()) { - *libc.__errno_location() = error; + *Libc.__errno_location() = error; } else { diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs index 5899cd61ed1ada..d841d36fb244f4 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs @@ -3806,7 +3806,7 @@ public static void DCS_BasicPerSerializerRoundTripAndCompare_EnumStruct() TestObjectInObjectContainerWithSimpleResolver(new SerializationTestTypes.Derived(), "<_data i:type=\"a:SerializationTestTypes.Derived***\" xmlns:a=\"http://schemas.datacontract.org/2004/07/SerializationTestTypes.Derived***\">0<_data2 i:type=\"a:SerializationTestTypes.Derived***\" xmlns:a=\"http://schemas.datacontract.org/2004/07/SerializationTestTypes.Derived***\">0"); - TestObjectInObjectContainerWithSimpleResolver(new SerializationTestTypes.list(), "<_data i:type=\"a:SerializationTestTypes.list***\" xmlns:a=\"http://schemas.datacontract.org/2004/07/SerializationTestTypes.list***\">0<_data2 i:type=\"a:SerializationTestTypes.list***\" xmlns:a=\"http://schemas.datacontract.org/2004/07/SerializationTestTypes.list***\">0"); + TestObjectInObjectContainerWithSimpleResolver(new SerializationTestTypes.List(), "<_data i:type=\"a:SerializationTestTypes.List***\" xmlns:a=\"http://schemas.datacontract.org/2004/07/SerializationTestTypes.List***\">0<_data2 i:type=\"a:SerializationTestTypes.List***\" xmlns:a=\"http://schemas.datacontract.org/2004/07/SerializationTestTypes.List***\">0"); TestObjectInObjectContainerWithSimpleResolver(new SerializationTestTypes.Arrays(), "<_data i:type=\"a:SerializationTestTypes.Arrays***\" xmlns:a=\"http://schemas.datacontract.org/2004/07/SerializationTestTypes.Arrays***\">112340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000<_data2 i:type=\"a:SerializationTestTypes.Arrays***\" xmlns:a=\"http://schemas.datacontract.org/2004/07/SerializationTestTypes.Arrays***\">112340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRTypeLibrary.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRTypeLibrary.cs index e34da8cf2b2f3b..13d4f69832e474 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRTypeLibrary.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRTypeLibrary.cs @@ -446,7 +446,7 @@ public List EnumsStructList _primitiveTypeList.Add(typeof(EmptyDC)); _primitiveTypeList.Add(typeof(Base)); _primitiveTypeList.Add(typeof(Derived)); - _primitiveTypeList.Add(typeof(list)); + _primitiveTypeList.Add(typeof(List)); _primitiveTypeList.Add(typeof(Arrays)); _primitiveTypeList.Add(typeof(Array3)); _primitiveTypeList.Add(typeof(Properties)); diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/Primitives.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/Primitives.cs index 89cda1233e3c29..10e74b683f4169 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/Primitives.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/Primitives.cs @@ -925,13 +925,13 @@ public enum Seasons3 : short } [DataContract] - public class list + public class List { [DataMember] public int value; [DataMember] - public list next; + public List next; } [DataContract] diff --git a/src/libraries/System.Runtime/tests/System/DateTimeTests.cs b/src/libraries/System.Runtime/tests/System/DateTimeTests.cs index 1a9a487ecd4849..0cbe26a005ede3 100644 --- a/src/libraries/System.Runtime/tests/System/DateTimeTests.cs +++ b/src/libraries/System.Runtime/tests/System/DateTimeTests.cs @@ -1024,6 +1024,43 @@ public static void Parse_Japanese() Assert.Equal(expected, DateTime.Parse(expectedString, cultureInfo)); } + private static bool IsNotOSXOrBrowser => !PlatformDetection.IsOSXLike && !PlatformDetection.IsBrowser; + + [ConditionalTheory(nameof(IsNotOSXOrBrowser))] + [InlineData("ar")] + [InlineData("ar-EG")] + [InlineData("ar-IQ")] + [InlineData("ar-SA")] + [InlineData("ar-YE")] + public static void DateTimeParsingWithBiDiCultureTest(string cultureName) + { + DateTime dt = new DateTime(2021, 11, 30, 14, 30, 40); + CultureInfo ci = CultureInfo.GetCultureInfo(cultureName); + string formatted = dt.ToString("d", ci); + Assert.Equal(dt.Date, DateTime.Parse(formatted, ci)); + formatted = dt.ToString("g", ci); + DateTime parsed = DateTime.Parse(formatted, ci); + Assert.Equal(dt.Date, parsed.Date); + Assert.Equal(dt.Hour, parsed.Hour); + Assert.Equal(dt.Minute, parsed.Minute); + } + + [Fact] + public static void DateTimeParsingWithSpaceTimeSeparators() + { + DateTime dt = new DateTime(2021, 11, 30, 14, 30, 40); + CultureInfo ci = CultureInfo.GetCultureInfo("en-US"); + // It is possible we find some cultures use such formats. dz-BT is example of that + string formatted = dt.ToString("yyyy/MM/dd hh mm tt", ci); + DateTime parsed = DateTime.Parse(formatted, ci); + Assert.Equal(dt.Hour, parsed.Hour); + Assert.Equal(dt.Minute, parsed.Minute); + + formatted = dt.ToString("yyyy/MM/dd hh mm ss tt", ci); + parsed = DateTime.Parse(formatted, ci); + Assert.Equal(dt, parsed); + } + [Fact] public static void Parse_InvalidArguments_Throws() { diff --git a/src/libraries/System.Runtime/tests/System/Reflection/TypeTests.Get.CornerCases.cs b/src/libraries/System.Runtime/tests/System/Reflection/TypeTests.Get.CornerCases.cs index a60d0b884ac410..2f6ff264c029e1 100644 --- a/src/libraries/System.Runtime/tests/System/Reflection/TypeTests.Get.CornerCases.cs +++ b/src/libraries/System.Runtime/tests/System/Reflection/TypeTests.Get.CornerCases.cs @@ -418,7 +418,7 @@ private class Derived : Base { public event Action MyEvent; - public class myinner { } + public class @myinner { } public class MyInner { } public int MyProp { get; } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/CompatibilitySuppressions.xml b/src/libraries/System.Security.Cryptography.Pkcs/src/CompatibilitySuppressions.xml index 459caaa3038b2f..101c1df6149a27 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/CompatibilitySuppressions.xml @@ -2,37 +2,37 @@ CP0002 - M:System.Security.Cryptography.Pkcs.CmsSigner.get_SignaturePadding + M:System.Security.Cryptography.Pkcs.CmsSigner.get_PrivateKey lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll lib/net462/System.Security.Cryptography.Pkcs.dll CP0002 - M:System.Security.Cryptography.Pkcs.CmsSigner.set_SignaturePadding(System.Security.Cryptography.RSASignaturePadding) + M:System.Security.Cryptography.Pkcs.CmsSigner.set_PrivateKey(System.Security.Cryptography.AsymmetricAlgorithm) lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll lib/net462/System.Security.Cryptography.Pkcs.dll CP0002 - M:System.Security.Cryptography.Pkcs.CmsSigner.#ctor(System.Security.Cryptography.Pkcs.SubjectIdentifierType,System.Security.Cryptography.X509Certificates.X509Certificate2,System.Security.Cryptography.RSA,System.Security.Cryptography.RSASignaturePadding) + M:System.Security.Cryptography.Pkcs.CmsSigner.get_SignaturePadding lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll lib/net462/System.Security.Cryptography.Pkcs.dll CP0002 - M:System.Security.Cryptography.Pkcs.CmsSigner.get_PrivateKey + M:System.Security.Cryptography.Pkcs.CmsSigner.set_SignaturePadding(System.Security.Cryptography.RSASignaturePadding) lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll lib/net462/System.Security.Cryptography.Pkcs.dll CP0002 - M:System.Security.Cryptography.Pkcs.CmsSigner.set_PrivateKey(System.Security.Cryptography.AsymmetricAlgorithm) + M:System.Security.Cryptography.Pkcs.CmsSigner.#ctor(System.Security.Cryptography.Pkcs.SubjectIdentifierType,System.Security.Cryptography.X509Certificates.X509Certificate2,System.Security.Cryptography.AsymmetricAlgorithm) lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll lib/net462/System.Security.Cryptography.Pkcs.dll CP0002 - M:System.Security.Cryptography.Pkcs.CmsSigner.#ctor(System.Security.Cryptography.Pkcs.SubjectIdentifierType,System.Security.Cryptography.X509Certificates.X509Certificate2,System.Security.Cryptography.AsymmetricAlgorithm) + M:System.Security.Cryptography.Pkcs.CmsSigner.#ctor(System.Security.Cryptography.Pkcs.SubjectIdentifierType,System.Security.Cryptography.X509Certificates.X509Certificate2,System.Security.Cryptography.RSA,System.Security.Cryptography.RSASignaturePadding) lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll lib/net462/System.Security.Cryptography.Pkcs.dll @@ -102,104 +102,4 @@ lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll lib/net462/System.Security.Cryptography.Pkcs.dll - - PKV006 - .NETFramework,Version=v4.6 - - - PKV006 - .NETStandard,Version=v1.3 - - - PKV007 - .NETFramework,Version=v4.6-win - - - PKV007 - .NETStandard,Version=v1.3-win - - - CP0001 - T:System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest - ref/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll - lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll - true - - - CP0001 - T:System.Security.Cryptography.Pkcs.Rfc3161TimestampToken - ref/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll - lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll - true - - - CP0001 - T:System.Security.Cryptography.Pkcs.Rfc3161TimestampTokenInfo - ref/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll - lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll - true - - - CP0001 - T:System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest - lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll - lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll - true - - - CP0001 - T:System.Security.Cryptography.Pkcs.Rfc3161TimestampToken - lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll - lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll - true - - - CP0001 - T:System.Security.Cryptography.Pkcs.Rfc3161TimestampTokenInfo - lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll - lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll - true - - - CP0001 - T:System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest - runtimes/win/lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll - lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll - true - - - CP0001 - T:System.Security.Cryptography.Pkcs.Rfc3161TimestampToken - runtimes/win/lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll - lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll - true - - - CP0001 - T:System.Security.Cryptography.Pkcs.Rfc3161TimestampTokenInfo - runtimes/win/lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll - lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll - true - - - CP0001 - T:System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest - runtimes/win/lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll - runtimes/win/lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll - true - - - CP0001 - T:System.Security.Cryptography.Pkcs.Rfc3161TimestampToken - runtimes/win/lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll - runtimes/win/lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll - true - - - CP0001 - T:System.Security.Cryptography.Pkcs.Rfc3161TimestampTokenInfo - runtimes/win/lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll - runtimes/win/lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll - true - - + \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.ProtectedData/src/CompatibilitySuppressions.xml b/src/libraries/System.Security.Cryptography.ProtectedData/src/CompatibilitySuppressions.xml deleted file mode 100644 index 1fabb2d07af7b1..00000000000000 --- a/src/libraries/System.Security.Cryptography.ProtectedData/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - PKV006 - .NETFramework,Version=v4.6 - - - PKV006 - .NETStandard,Version=v1.3 - - - PKV007 - .NETFramework,Version=v4.6-win - - - PKV007 - .NETStandard,Version=v1.3-win - - \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTestDriver.cs b/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTestDriver.cs index 07f72debe15862..9f940b3753a63e 100644 --- a/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTestDriver.cs +++ b/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTestDriver.cs @@ -488,6 +488,30 @@ public void Initialize_TransformBlock_Unused() } } + [Fact] + public void Initialize_DoubleInitialize_Works() + { + byte[] hashInput = new byte[] { 1, 2, 3, 4, 5 }; + byte[] expectedDigest; + + using (HashAlgorithm hash = Create()) + { + expectedDigest = hash.ComputeHash(hashInput); + } + + using (HashAlgorithm hash = Create()) + { + byte[] buffer = new byte[1024]; + hash.TransformBlock(buffer, 0, buffer.Length, buffer, 0); + hash.Initialize(); + hash.TransformFinalBlock(Array.Empty(), 0, 0); + hash.Initialize(); + hash.TransformFinalBlock(hashInput, 0, hashInput.Length); + + Assert.Equal(expectedDigest, hash.Hash); + } + } + protected class DataRepeatingStream : Stream { private int _remaining; diff --git a/src/libraries/System.Security.Permissions/src/CompatibilitySuppressions.xml b/src/libraries/System.Security.Permissions/src/CompatibilitySuppressions.xml index 16ccd1eac146cb..1f11b7c644c503 100644 --- a/src/libraries/System.Security.Permissions/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Security.Permissions/src/CompatibilitySuppressions.xml @@ -21,76 +21,6 @@ CP0001 T:System.Xaml.Permissions.XamlLoadPermission - ref/net5.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Security.Policy.Evidence - ref/net5.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Security.Policy.EvidenceBase - ref/net5.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Xaml.Permissions.XamlLoadPermission - ref/netcoreapp3.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Security.Policy.Evidence - ref/netcoreapp3.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Security.Policy.EvidenceBase - ref/netcoreapp3.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Security.Policy.Evidence - ref/netstandard2.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Security.Policy.EvidenceBase - ref/netstandard2.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Xaml.Permissions.XamlLoadPermission - lib/net5.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Security.Policy.Evidence - lib/net5.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Security.Policy.EvidenceBase lib/net5.0/System.Security.Permissions.dll lib/netstandard2.0/System.Security.Permissions.dll true @@ -98,35 +28,7 @@ CP0001 T:System.Xaml.Permissions.XamlLoadPermission - lib/netcoreapp3.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Security.Policy.Evidence - lib/netcoreapp3.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Security.Policy.EvidenceBase - lib/netcoreapp3.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Security.Policy.Evidence - lib/netstandard2.0/System.Security.Permissions.dll - lib/netstandard2.0/System.Security.Permissions.dll - true - - - CP0001 - T:System.Security.Policy.EvidenceBase - lib/netstandard2.0/System.Security.Permissions.dll + lib/netcoreapp3.1/System.Security.Permissions.dll lib/netstandard2.0/System.Security.Permissions.dll true diff --git a/src/libraries/System.Security.Principal.Windows/tests/WindowsIdentityImpersonatedTests.netcoreapp.cs b/src/libraries/System.Security.Principal.Windows/tests/WindowsIdentityImpersonatedTests.netcoreapp.cs index 4bfb059d708c8c..0a7c1745ed9f42 100644 --- a/src/libraries/System.Security.Principal.Windows/tests/WindowsIdentityImpersonatedTests.netcoreapp.cs +++ b/src/libraries/System.Security.Principal.Windows/tests/WindowsIdentityImpersonatedTests.netcoreapp.cs @@ -26,7 +26,7 @@ public WindowsIdentityImpersonatedTests(WindowsIdentityFixture windowsIdentityFi Assert.False(string.IsNullOrEmpty(_fixture.TestAccount.AccountName)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.CanRunImpersonatedTests))] [OuterLoop] public async Task RunImpersonatedAsync_TaskAndTaskOfT() { @@ -60,7 +60,7 @@ void Asserts(WindowsIdentity currentWindowsIdentity) } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.CanRunImpersonatedTests))] [OuterLoop] public void RunImpersonated_NameResolution() { @@ -78,7 +78,7 @@ public void RunImpersonated_NameResolution() }); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.CanRunImpersonatedTests))] [OuterLoop] public async Task RunImpersonatedAsync_NameResolution() { diff --git a/src/libraries/System.ServiceProcess.ServiceController/src/CompatibilitySuppressions.xml b/src/libraries/System.ServiceProcess.ServiceController/src/CompatibilitySuppressions.xml index b8d2693f9cf6c9..c8e86cf869dbe7 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.ServiceProcess.ServiceController/src/CompatibilitySuppressions.xml @@ -1,15 +1,24 @@  - PKV006 - .NETStandard,Version=v1.4 + CP0002 + M:System.ServiceProcess.ServiceController.Stop(System.Boolean) + lib/netcoreapp3.1/System.ServiceProcess.ServiceController.dll + lib/netstandard2.0/System.ServiceProcess.ServiceController.dll + true - PKV006 - .NETStandard,Version=v1.5 + CP0002 + M:System.ServiceProcess.ServiceController.Stop(System.Boolean) + runtimes/win/lib/netcoreapp3.1/System.ServiceProcess.ServiceController.dll + lib/netstandard2.0/System.ServiceProcess.ServiceController.dll + true - PKV007 - .NETStandard,Version=v1.5-win + CP0002 + M:System.ServiceProcess.ServiceController.Stop(System.Boolean) + runtimes/win/lib/netcoreapp3.1/System.ServiceProcess.ServiceController.dll + runtimes/win/lib/netstandard2.0/System.ServiceProcess.ServiceController.dll + true \ No newline at end of file diff --git a/src/libraries/System.Text.Encoding.CodePages/src/CompatibilitySuppressions.xml b/src/libraries/System.Text.Encoding.CodePages/src/CompatibilitySuppressions.xml deleted file mode 100644 index e74741f98cce90..00000000000000 --- a/src/libraries/System.Text.Encoding.CodePages/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - PKV006 - .NETFramework,Version=v4.6 - - - PKV006 - .NETStandard,Version=v1.3 - - - PKV007 - .NETStandard,Version=v1.3-win - - \ No newline at end of file diff --git a/src/libraries/System.Text.Encodings.Web/src/CompatibilitySuppressions.xml b/src/libraries/System.Text.Encodings.Web/src/CompatibilitySuppressions.xml deleted file mode 100644 index 55a8d58e928206..00000000000000 --- a/src/libraries/System.Text.Encodings.Web/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - .NETStandard,Version=v1.0 - - \ No newline at end of file diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index 087f3e2dc4095c..2a8e805499fd67 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -96,6 +96,9 @@ private sealed partial class Emitter private readonly HashSet _emittedPropertyFileNames = new(); + private bool _generateGetConverterMethodForTypes = false; + private bool _generateGetConverterMethodForProperties = false; + public Emitter(in JsonSourceGenerationContext sourceGenerationContext, SourceGenerationSpec generationSpec) { _sourceGenerationContext = sourceGenerationContext; @@ -107,16 +110,12 @@ public void Emit() foreach (ContextGenerationSpec contextGenerationSpec in _generationSpec.ContextGenerationSpecList) { _currentContext = contextGenerationSpec; - - bool generateGetConverterMethodForTypes = false; - bool generateGetConverterMethodForProperties = false; + _generateGetConverterMethodForTypes = false; + _generateGetConverterMethodForProperties = false; foreach (TypeGenerationSpec typeGenerationSpec in _currentContext.RootSerializableTypes) { GenerateTypeInfo(typeGenerationSpec); - - generateGetConverterMethodForTypes |= typeGenerationSpec.HasTypeFactoryConverter; - generateGetConverterMethodForProperties |= typeGenerationSpec.HasPropertyFactoryConverters; } string contextName = _currentContext.ContextType.Name; @@ -124,7 +123,7 @@ public void Emit() // Add root context implementation. AddSource( $"{contextName}.g.cs", - GetRootJsonContextImplementation(generateGetConverterMethodForTypes, generateGetConverterMethodForProperties), + GetRootJsonContextImplementation(), isRootContextDef: true); // Add GetJsonTypeInfo override implementation. @@ -297,6 +296,9 @@ private void GenerateTypeInfo(TypeGenerationSpec typeGenerationSpec) Location location = typeGenerationSpec.AttributeLocation ?? _currentContext.Location; _sourceGenerationContext.ReportDiagnostic(Diagnostic.Create(DuplicateTypeName, location, new string[] { typeGenerationSpec.TypeInfoPropertyName })); } + + _generateGetConverterMethodForTypes |= typeGenerationSpec.HasTypeFactoryConverter; + _generateGetConverterMethodForProperties |= typeGenerationSpec.HasPropertyFactoryConverters; } private string GenerateForTypeWithKnownConverter(TypeGenerationSpec typeMetadata) @@ -1140,9 +1142,7 @@ private string WrapWithCheckForCustomConverter(string source, string typeCompila {IndentSource(source, numIndentations: 1)} }}"; - private string GetRootJsonContextImplementation( - bool generateGetConverterMethodForTypes, - bool generateGetConverterMethodForProperties) + private string GetRootJsonContextImplementation() { string contextTypeRef = _currentContext.ContextTypeRef; string contextTypeName = _currentContext.ContextType.Name; @@ -1166,12 +1166,12 @@ private string GetRootJsonContextImplementation( {GetFetchLogicForRuntimeSpecifiedCustomConverter()}"); - if (generateGetConverterMethodForProperties) + if (_generateGetConverterMethodForProperties) { sb.Append(GetFetchLogicForGetCustomConverter_PropertiesWithFactories()); } - if (generateGetConverterMethodForProperties || generateGetConverterMethodForTypes) + if (_generateGetConverterMethodForProperties || _generateGetConverterMethodForTypes) { sb.Append(GetFetchLogicForGetCustomConverter_TypesWithFactories()); } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs index fcb3dc91a886c3..c01499f7fef3e3 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs @@ -42,8 +42,8 @@ public interface ITestContext public JsonTypeInfo StructWithCustomConverterFactory { get; } public JsonTypeInfo ClassWithCustomConverterProperty { get; } public JsonTypeInfo StructWithCustomConverterProperty { get; } - public JsonTypeInfo ClassWithCustomConverterPropertyFactory { get; } - public JsonTypeInfo StructWithCustomConverterPropertyFactory { get; } + public JsonTypeInfo ClassWithCustomConverterFactoryProperty { get; } + public JsonTypeInfo StructWithCustomConverterFactoryProperty { get; } public JsonTypeInfo ClassWithBadCustomConverter { get; } public JsonTypeInfo StructWithBadCustomConverter { get; } public JsonTypeInfo NullablePersonStruct { get; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs index 49deae9acadd78..7bc91507d9ad53 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Reflection; using System.Text.Json.Serialization; using Microsoft.DotNet.RemoteExecutor; @@ -89,5 +90,44 @@ internal record Person(string FirstName, string LastName); internal partial class PersonJsonContext : JsonSerializerContext { } + + // Regression test for https://github.com/dotnet/runtime/issues/62079 + [Fact] + public static void SupportsPropertiesWithCustomConverterFactory() + { + var value = new ClassWithCustomConverterFactoryProperty { MyEnum = Serialization.Tests.SampleEnum.MinZero }; + string json = JsonSerializer.Serialize(value, SingleClassWithCustomConverterFactoryPropertyContext.Default.ClassWithCustomConverterFactoryProperty); + Assert.Equal(@"{""MyEnum"":""MinZero""}", json); + } + + public class ParentClass + { + public ClassWithCustomConverterFactoryProperty? Child { get; set; } + } + + [JsonSerializable(typeof(ParentClass))] + internal partial class SingleClassWithCustomConverterFactoryPropertyContext : JsonSerializerContext + { + } + + // Regression test for https://github.com/dotnet/runtime/issues/61860 + [Fact] + public static void SupportsGenericParameterWithCustomConverterFactory() + { + var value = new List { TestEnum.Cee }; + string json = JsonSerializer.Serialize(value, GenericParameterWithCustomConverterFactoryContext.Default.ListTestEnum); + Assert.Equal(@"[""Cee""]", json); + } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum TestEnum + { + Aye, Bee, Cee + } + + [JsonSerializable(typeof(List))] + internal partial class GenericParameterWithCustomConverterFactoryContext : JsonSerializerContext + { + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs index 99eecd14cd7ee1..57abdf04808a0c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs @@ -36,8 +36,8 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(StructWithCustomConverterFactory))] [JsonSerializable(typeof(ClassWithCustomConverterProperty))] [JsonSerializable(typeof(StructWithCustomConverterProperty))] - [JsonSerializable(typeof(ClassWithCustomConverterPropertyFactory))] - [JsonSerializable(typeof(StructWithCustomConverterPropertyFactory))] + [JsonSerializable(typeof(ClassWithCustomConverterFactoryProperty))] + [JsonSerializable(typeof(StructWithCustomConverterFactoryProperty))] [JsonSerializable(typeof(ClassWithBadCustomConverter))] [JsonSerializable(typeof(StructWithBadCustomConverter))] [JsonSerializable(typeof(PersonStruct?))] @@ -82,8 +82,8 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.NotNull(MetadataAndSerializationContext.Default.StructWithCustomConverterFactory); Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithCustomConverterProperty); Assert.NotNull(MetadataAndSerializationContext.Default.StructWithCustomConverterProperty); - Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithCustomConverterPropertyFactory); - Assert.NotNull(MetadataAndSerializationContext.Default.StructWithCustomConverterPropertyFactory); + Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithCustomConverterFactoryProperty); + Assert.NotNull(MetadataAndSerializationContext.Default.StructWithCustomConverterFactoryProperty); Assert.Throws(() => MetadataAndSerializationContext.Default.ClassWithBadCustomConverter); Assert.Throws(() => MetadataAndSerializationContext.Default.StructWithBadCustomConverter); Assert.Null(MetadataAndSerializationContext.Default.NullablePersonStruct.SerializeHandler); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs index 6aaaf82d48ef6d..d0c3023b41ae9f 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs @@ -35,8 +35,8 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(StructWithCustomConverterFactory), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(ClassWithCustomConverterProperty), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(StructWithCustomConverterProperty), GenerationMode = JsonSourceGenerationMode.Metadata)] - [JsonSerializable(typeof(ClassWithCustomConverterPropertyFactory), GenerationMode = JsonSourceGenerationMode.Metadata)] - [JsonSerializable(typeof(StructWithCustomConverterPropertyFactory), GenerationMode = JsonSourceGenerationMode.Metadata)] + [JsonSerializable(typeof(ClassWithCustomConverterFactoryProperty), GenerationMode = JsonSourceGenerationMode.Metadata)] + [JsonSerializable(typeof(StructWithCustomConverterFactoryProperty), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(ClassWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(StructWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(PersonStruct?), GenerationMode = JsonSourceGenerationMode.Metadata)] @@ -80,8 +80,8 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterFactory.SerializeHandler); Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverterProperty.SerializeHandler); Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterProperty.SerializeHandler); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverterPropertyFactory.SerializeHandler); - Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterPropertyFactory.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverterFactoryProperty.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterFactoryProperty.SerializeHandler); Assert.Throws(() => MetadataWithPerTypeAttributeContext.Default.ClassWithBadCustomConverter.SerializeHandler); Assert.Throws(() => MetadataWithPerTypeAttributeContext.Default.StructWithBadCustomConverter.SerializeHandler); Assert.Null(MetadataWithPerTypeAttributeContext.Default.NullablePersonStruct.SerializeHandler); @@ -119,8 +119,8 @@ public override void EnsureFastPathGeneratedAsExpected() [JsonSerializable(typeof(StructWithCustomConverterFactory))] [JsonSerializable(typeof(ClassWithCustomConverterProperty))] [JsonSerializable(typeof(StructWithCustomConverterProperty))] - [JsonSerializable(typeof(ClassWithCustomConverterPropertyFactory))] - [JsonSerializable(typeof(StructWithCustomConverterPropertyFactory))] + [JsonSerializable(typeof(ClassWithCustomConverterFactoryProperty))] + [JsonSerializable(typeof(StructWithCustomConverterFactoryProperty))] [JsonSerializable(typeof(ClassWithBadCustomConverter))] [JsonSerializable(typeof(StructWithBadCustomConverter))] [JsonSerializable(typeof(PersonStruct?))] @@ -187,8 +187,8 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.Null(MetadataContext.Default.StructWithCustomConverterFactory.SerializeHandler); Assert.Null(MetadataContext.Default.ClassWithCustomConverterProperty.SerializeHandler); Assert.Null(MetadataContext.Default.StructWithCustomConverterProperty.SerializeHandler); - Assert.Null(MetadataContext.Default.ClassWithCustomConverterPropertyFactory.SerializeHandler); - Assert.Null(MetadataContext.Default.StructWithCustomConverterPropertyFactory.SerializeHandler); + Assert.Null(MetadataContext.Default.ClassWithCustomConverterFactoryProperty.SerializeHandler); + Assert.Null(MetadataContext.Default.StructWithCustomConverterFactoryProperty.SerializeHandler); Assert.Throws(() => MetadataContext.Default.ClassWithBadCustomConverter.SerializeHandler); Assert.Throws(() => MetadataContext.Default.StructWithBadCustomConverter.SerializeHandler); Assert.Null(MetadataContext.Default.NullablePersonStruct.SerializeHandler); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs index 988ab86027b576..d5475515699359 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs @@ -36,8 +36,8 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(StructWithCustomConverterFactory), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(ClassWithCustomConverterProperty), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(StructWithCustomConverterProperty), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] - [JsonSerializable(typeof(ClassWithCustomConverterPropertyFactory), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] - [JsonSerializable(typeof(StructWithCustomConverterPropertyFactory), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(ClassWithCustomConverterFactoryProperty), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(StructWithCustomConverterFactoryProperty), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(ClassWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(StructWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(PersonStruct?), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] @@ -82,8 +82,8 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.Null(MixedModeContext.Default.StructWithCustomConverterFactory.SerializeHandler); Assert.Null(MixedModeContext.Default.ClassWithCustomConverterProperty.SerializeHandler); Assert.Null(MixedModeContext.Default.StructWithCustomConverterProperty.SerializeHandler); - Assert.Null(MixedModeContext.Default.ClassWithCustomConverterPropertyFactory.SerializeHandler); - Assert.Null(MixedModeContext.Default.StructWithCustomConverterPropertyFactory.SerializeHandler); + Assert.Null(MixedModeContext.Default.ClassWithCustomConverterFactoryProperty.SerializeHandler); + Assert.Null(MixedModeContext.Default.StructWithCustomConverterFactoryProperty.SerializeHandler); Assert.Throws(() => MixedModeContext.Default.ClassWithBadCustomConverter.SerializeHandler); Assert.Throws(() => MixedModeContext.Default.StructWithBadCustomConverter.SerializeHandler); Assert.Null(MixedModeContext.Default.NullablePersonStruct.SerializeHandler); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs index 9df6dd24533336..1cb9cb56bbf9fa 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs @@ -246,28 +246,28 @@ public virtual void RoundTripWithCustomPropertyConverterFactory_Class() { const string Json = "{\"MyEnum\":\"One\"}"; - ClassWithCustomConverterPropertyFactory obj = new() + ClassWithCustomConverterFactoryProperty obj = new() { MyEnum = SampleEnum.One }; if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization) { - Assert.Throws(() => JsonSerializer.Serialize(obj, DefaultContext.ClassWithCustomConverterPropertyFactory)); + Assert.Throws(() => JsonSerializer.Serialize(obj, DefaultContext.ClassWithCustomConverterFactoryProperty)); } else { - string json = JsonSerializer.Serialize(obj, DefaultContext.ClassWithCustomConverterPropertyFactory); + string json = JsonSerializer.Serialize(obj, DefaultContext.ClassWithCustomConverterFactoryProperty); Assert.Equal(Json, json); } if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization) { - Assert.Throws(() => JsonSerializer.Serialize(obj, DefaultContext.ClassWithCustomConverterPropertyFactory)); + Assert.Throws(() => JsonSerializer.Serialize(obj, DefaultContext.ClassWithCustomConverterFactoryProperty)); } else { - obj = JsonSerializer.Deserialize(Json, DefaultContext.ClassWithCustomConverterPropertyFactory); + obj = JsonSerializer.Deserialize(Json, DefaultContext.ClassWithCustomConverterFactoryProperty); Assert.Equal(SampleEnum.One, obj.MyEnum); } } @@ -277,28 +277,28 @@ public virtual void RoundTripWithCustomPropertyConverterFactory_Struct() { const string Json = "{\"MyEnum\":\"One\"}"; - StructWithCustomConverterPropertyFactory obj = new() + StructWithCustomConverterFactoryProperty obj = new() { MyEnum = SampleEnum.One }; if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization) { - Assert.Throws(() => JsonSerializer.Serialize(obj, DefaultContext.StructWithCustomConverterPropertyFactory)); + Assert.Throws(() => JsonSerializer.Serialize(obj, DefaultContext.StructWithCustomConverterFactoryProperty)); } else { - string json = JsonSerializer.Serialize(obj, DefaultContext.StructWithCustomConverterPropertyFactory); + string json = JsonSerializer.Serialize(obj, DefaultContext.StructWithCustomConverterFactoryProperty); Assert.Equal(Json, json); } if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization) { - Assert.Throws(() => JsonSerializer.Serialize(obj, DefaultContext.StructWithCustomConverterPropertyFactory)); + Assert.Throws(() => JsonSerializer.Serialize(obj, DefaultContext.StructWithCustomConverterFactoryProperty)); } else { - obj = JsonSerializer.Deserialize(Json, DefaultContext.StructWithCustomConverterPropertyFactory); + obj = JsonSerializer.Deserialize(Json, DefaultContext.StructWithCustomConverterFactoryProperty); Assert.Equal(SampleEnum.One, obj.MyEnum); } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs index 147cae5e46dc7d..b51ba070be1885 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs @@ -36,8 +36,8 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(StructWithCustomConverterFactory))] [JsonSerializable(typeof(ClassWithCustomConverterProperty))] [JsonSerializable(typeof(StructWithCustomConverterProperty))] - [JsonSerializable(typeof(ClassWithCustomConverterPropertyFactory))] - [JsonSerializable(typeof(StructWithCustomConverterPropertyFactory))] + [JsonSerializable(typeof(ClassWithCustomConverterFactoryProperty))] + [JsonSerializable(typeof(StructWithCustomConverterFactoryProperty))] [JsonSerializable(typeof(ClassWithBadCustomConverter))] [JsonSerializable(typeof(StructWithBadCustomConverter))] [JsonSerializable(typeof(PersonStruct?))] @@ -75,8 +75,8 @@ internal partial class SerializationContext : JsonSerializerContext, ITestContex [JsonSerializable(typeof(StructWithCustomConverterFactory), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(ClassWithCustomConverterProperty), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(StructWithCustomConverterProperty), GenerationMode = JsonSourceGenerationMode.Serialization)] - [JsonSerializable(typeof(ClassWithCustomConverterPropertyFactory), GenerationMode = JsonSourceGenerationMode.Serialization)] - [JsonSerializable(typeof(StructWithCustomConverterPropertyFactory), GenerationMode = JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(ClassWithCustomConverterFactoryProperty), GenerationMode = JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(StructWithCustomConverterFactoryProperty), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(ClassWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(StructWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(PersonStruct?), GenerationMode = JsonSourceGenerationMode.Serialization)] @@ -115,8 +115,8 @@ internal partial class SerializationWithPerTypeAttributeContext : JsonSerializer [JsonSerializable(typeof(StructWithCustomConverterFactory), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(ClassWithCustomConverterProperty), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(StructWithCustomConverterProperty), GenerationMode = JsonSourceGenerationMode.Serialization)] - [JsonSerializable(typeof(ClassWithCustomConverterPropertyFactory), GenerationMode = JsonSourceGenerationMode.Serialization)] - [JsonSerializable(typeof(StructWithCustomConverterPropertyFactory), GenerationMode = JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(ClassWithCustomConverterFactoryProperty), GenerationMode = JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(StructWithCustomConverterFactoryProperty), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(ClassWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(StructWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(PersonStruct?), GenerationMode = JsonSourceGenerationMode.Serialization)] @@ -486,8 +486,8 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverterFactory.SerializeHandler); Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverterProperty.SerializeHandler); Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverterProperty.SerializeHandler); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverterPropertyFactory.SerializeHandler); - Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverterPropertyFactory.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverterFactoryProperty.SerializeHandler); + Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverterFactoryProperty.SerializeHandler); Assert.Throws(() => SerializationWithPerTypeAttributeContext.Default.ClassWithBadCustomConverter.SerializeHandler); Assert.Throws(() => SerializationWithPerTypeAttributeContext.Default.StructWithBadCustomConverter.SerializeHandler); Assert.Null(SerializationWithPerTypeAttributeContext.Default.NullablePersonStruct.SerializeHandler); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.CustomConverters.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.CustomConverters.cs index f2be0926a0bc89..1fff23f8a6c55c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.CustomConverters.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.CustomConverters.cs @@ -245,13 +245,13 @@ public struct StructWithCustomConverterProperty public ClassWithCustomConverterProperty.NestedPoco Property { get; set; } } - public struct ClassWithCustomConverterPropertyFactory + public class ClassWithCustomConverterFactoryProperty { [JsonConverter(typeof(JsonStringEnumConverter))] // This converter is a JsonConverterFactory public Serialization.Tests.SampleEnum MyEnum { get; set; } } - public struct StructWithCustomConverterPropertyFactory + public struct StructWithCustomConverterFactoryProperty { [JsonConverter(typeof(JsonStringEnumConverter))] // This converter is a JsonConverterFactory public Serialization.Tests.SampleEnum MyEnum { get; set; } diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs index 30ec664562819e..cbe77998186a2d 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs @@ -10,6 +10,7 @@ using System.Diagnostics; using System.Globalization; using System.IO; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Cryptography; @@ -35,7 +36,6 @@ public partial class RegexGenerator "#nullable enable", "#pragma warning disable CS0162 // Unreachable code", "#pragma warning disable CS0164 // Unreferenced label", - "#pragma warning disable CS0168 // Variable declared but never used", "#pragma warning disable CS0219 // Variable assigned but never used", "", }; @@ -152,7 +152,7 @@ static bool ExceedsMaxDepthForSimpleCodeGeneration(RegexNode node, int allowedDe private static ImmutableArray EmitRegexMethod(IndentedTextWriter writer, RegexMethod rm, string id) { string patternExpression = Literal(rm.Pattern); - string optionsExpression = $"(global::System.Text.RegularExpressions.RegexOptions)({(int)rm.Options})"; + string optionsExpression = Literal(rm.Options); string timeoutExpression = rm.MatchTimeout == Timeout.Infinite ? "global::System.Threading.Timeout.InfiniteTimeSpan" : $"global::System.TimeSpan.FromMilliseconds({rm.MatchTimeout.ToString(CultureInfo.InvariantCulture)})"; @@ -207,7 +207,6 @@ private static ImmutableArray EmitRegexMethod(IndentedTextWriter wri writer.WriteLine(" };"); } writer.WriteLine($" base.capsize = {rm.Code.CapSize};"); - writer.WriteLine($" base.InitializeReferences();"); writer.WriteLine($" }}"); writer.WriteLine(" "); writer.WriteLine($" private sealed class RunnerFactory : global::System.Text.RegularExpressions.RegexRunnerFactory"); @@ -220,29 +219,56 @@ private static ImmutableArray EmitRegexMethod(IndentedTextWriter wri // Main implementation methods writer.WriteLine($" protected override void InitTrackCount() => base.runtrackcount = {rm.Code.TrackCount};"); writer.WriteLine(); -#if DEBUG - writer.WriteLine(" // Node tree:"); - var treeLineReader = new StringReader(rm.Code.Tree.ToString()); - string? treeLine = null; - while ((treeLine = treeLineReader.ReadLine()) != null) - { - writer.WriteLine($" // {treeLine}"); - } + + writer.WriteLine(" // Description:"); + DescribeExpression(writer, rm.Code.Tree.Root.Child(0), " // "); // skip implicit root capture writer.WriteLine(); -#endif + writer.WriteLine($" protected override bool FindFirstChar()"); writer.WriteLine($" {{"); writer.Indent += 4; - EmitFindFirstChar(writer, rm, id); + RequiredHelperFunctions requiredHelpers = EmitFindFirstChar(writer, rm, id); writer.Indent -= 4; writer.WriteLine($" }}"); writer.WriteLine(); writer.WriteLine($" protected override void Go()"); writer.WriteLine($" {{"); writer.Indent += 4; - EmitGo(writer, rm, id); + requiredHelpers |= EmitGo(writer, rm, id); writer.Indent -= 4; writer.WriteLine($" }}"); + + if ((requiredHelpers & RequiredHelperFunctions.IsWordChar) != 0) + { + writer.WriteLine(); + writer.WriteLine($" /// Determines whether the character is part of the [\\w] set."); + writer.WriteLine($" [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine($" private static bool IsWordChar(char ch)"); + writer.WriteLine($" {{"); + writer.WriteLine($" global::System.ReadOnlySpan ascii = new byte[]"); + writer.WriteLine($" {{"); + writer.WriteLine($" 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03,"); + writer.WriteLine($" 0xFE, 0xFF, 0xFF, 0x87, 0xFE, 0xFF, 0xFF, 0x07"); + writer.WriteLine($" }};"); + writer.WriteLine(); + writer.WriteLine($" int chDiv8 = ch >> 3;"); + writer.WriteLine($" return (uint)chDiv8 < (uint)ascii.Length ?"); + writer.WriteLine($" (ascii[chDiv8] & (1 << (ch & 0x7))) != 0 :"); + writer.WriteLine($" global::System.Globalization.CharUnicodeInfo.GetUnicodeCategory(ch) switch"); + writer.WriteLine($" {{"); + writer.WriteLine($" global::System.Globalization.UnicodeCategory.UppercaseLetter or"); + writer.WriteLine($" global::System.Globalization.UnicodeCategory.LowercaseLetter or"); + writer.WriteLine($" global::System.Globalization.UnicodeCategory.TitlecaseLetter or"); + writer.WriteLine($" global::System.Globalization.UnicodeCategory.ModifierLetter or"); + writer.WriteLine($" global::System.Globalization.UnicodeCategory.OtherLetter or"); + writer.WriteLine($" global::System.Globalization.UnicodeCategory.NonSpacingMark or"); + writer.WriteLine($" global::System.Globalization.UnicodeCategory.DecimalDigitNumber or"); + writer.WriteLine($" global::System.Globalization.UnicodeCategory.ConnectorPunctuation => true,"); + writer.WriteLine($" _ => false,"); + writer.WriteLine($" }};"); + writer.WriteLine($" }}"); + } + writer.WriteLine($" }}"); writer.WriteLine($" }}"); writer.WriteLine("}"); @@ -272,22 +298,20 @@ static void AppendHashtableContents(IndentedTextWriter writer, Hashtable ht) } /// Emits the body of the FindFirstChar override. - private static void EmitFindFirstChar(IndentedTextWriter writer, RegexMethod rm, string id) + private static RequiredHelperFunctions EmitFindFirstChar(IndentedTextWriter writer, RegexMethod rm, string id) { RegexOptions options = (RegexOptions)rm.Options; RegexCode code = rm.Code; bool hasTextInfo = false; + RequiredHelperFunctions requiredHelpers = RequiredHelperFunctions.None; // In some cases, we need to emit declarations at the beginning of the method, but we only discover we need them later. - // To handle that, we emit a placeholder value that's not valid C#, and then at the end of the code generation we either - // delete it if no additional declarations are required, or we replace it with the list of additional declarations - // built up while generating code. + // To handle that, we build up a collection of all the declarations to include, track where they should be inserted, + // and then insert them at that position once everything else has been output. var additionalDeclarations = new HashSet(); // Emit locals initialization - writer.WriteLine("global::System.ReadOnlySpan runtextSpan = base.runtext;"); - writer.WriteLine("int runtextpos = base.runtextpos;"); - writer.Write($"int runtextend = base.runtextend;"); + writer.WriteLine("int pos = base.runtextpos, end = base.runtextend;"); writer.Flush(); int additionalDeclarationsPosition = ((StringWriter)writer.InnerWriter).GetStringBuilder().Length; int additionalDeclarationsIndent = writer.Indent; @@ -300,9 +324,9 @@ private static void EmitFindFirstChar(IndentedTextWriter writer, RegexMethod rm, Debug.Assert(minRequiredLength >= 0); string clause = minRequiredLength switch { - 0 => "if (runtextpos <= runtextend)", - 1 => "if (runtextpos < runtextend)", - _ => $"if (runtextpos < runtextend - {minRequiredLength - 1})" + 0 => "if (pos <= end)", + 1 => "if (pos < end)", + _ => $"if (pos < end - {minRequiredLength - 1})" }; using (EmitBlock(writer, clause)) { @@ -321,7 +345,8 @@ private static void EmitFindFirstChar(IndentedTextWriter writer, RegexMethod rm, { case FindNextStartingPositionMode.LeadingPrefix_LeftToRight_CaseSensitive: Debug.Assert(!string.IsNullOrEmpty(code.FindOptimizations.LeadingCaseSensitivePrefix)); - EmitIndexOf_LeftToRight(code.FindOptimizations.LeadingCaseSensitivePrefix); + additionalDeclarations.Add("global::System.ReadOnlySpan inputSpan = base.runtext;"); + EmitIndexOf(code.FindOptimizations.LeadingCaseSensitivePrefix); break; case FindNextStartingPositionMode.FixedSets_LeftToRight_CaseSensitive: @@ -329,7 +354,8 @@ private static void EmitFindFirstChar(IndentedTextWriter writer, RegexMethod rm, case FindNextStartingPositionMode.LeadingSet_LeftToRight_CaseSensitive: case FindNextStartingPositionMode.LeadingSet_LeftToRight_CaseInsensitive: Debug.Assert(code.FindOptimizations.FixedDistanceSets is { Count: > 0 }); - EmitFixedSet_LeftToRight(); + additionalDeclarations.Add("global::System.ReadOnlySpan inputSpan = base.runtext;"); + EmitFixedSet(); break; default: @@ -344,14 +370,14 @@ private static void EmitFindFirstChar(IndentedTextWriter writer, RegexMethod rm, } writer.WriteLine(); - writer.WriteLine("// No match"); + writer.WriteLine("// No starting position found"); writer.WriteLine("ReturnFalse:"); - writer.WriteLine("base.runtextpos = runtextend;"); + writer.WriteLine("base.runtextpos = end;"); writer.WriteLine("return false;"); // We're done. Patch up any additional declarations. ReplaceAdditionalDeclarations(writer, additionalDeclarations, additionalDeclarationsPosition, additionalDeclarationsIndent); - return; + return requiredHelpers; // Emits any anchors. Returns true if the anchor roots any match to a specific location and thus no further // searching is required; otherwise, false. @@ -364,7 +390,8 @@ bool EmitAnchors() { case RegexPrefixAnalyzer.Beginning: writer.WriteLine("// Beginning \\A anchor"); - using (EmitBlock(writer, "if (runtextpos > runtextbeg)")) + additionalDeclarations.Add("int beginning = base.runtextbeg;"); + using (EmitBlock(writer, "if (pos > beginning)")) { writer.WriteLine("goto ReturnFalse;"); } @@ -373,7 +400,7 @@ bool EmitAnchors() case RegexPrefixAnalyzer.Start: writer.WriteLine("// Start \\G anchor"); - using (EmitBlock(writer, "if (runtextpos > runtextstart)")) + using (EmitBlock(writer, "if (pos > base.runtextstart)")) { writer.WriteLine("goto ReturnFalse;"); } @@ -382,18 +409,18 @@ bool EmitAnchors() case RegexPrefixAnalyzer.EndZ: writer.WriteLine("// End \\Z anchor"); - using (EmitBlock(writer, "if (runtextpos < runtextend - 1)")) + using (EmitBlock(writer, "if (pos < end - 1)")) { - writer.WriteLine("base.runtextpos = runtextend - 1;"); + writer.WriteLine("base.runtextpos = end - 1;"); } writer.WriteLine("return true;"); return true; case RegexPrefixAnalyzer.End: writer.WriteLine("// End \\z anchor"); - using (EmitBlock(writer, "if (runtextpos < runtextend)")) + using (EmitBlock(writer, "if (pos < end)")) { - writer.WriteLine("base.runtextpos = runtextend;"); + writer.WriteLine("base.runtextpos = end;"); } writer.WriteLine("return true;"); return true; @@ -404,14 +431,16 @@ bool EmitAnchors() // the other anchors, which all skip all subsequent processing if found, with BOL we just use it // to boost our position to the next line, and then continue normally with any searches. writer.WriteLine("// Beginning-of-line anchor"); - using (EmitBlock(writer, "if (runtextpos > runtextbeg && runtextSpan[runtextpos - 1] != '\\n')")) + additionalDeclarations.Add("global::System.ReadOnlySpan inputSpan = base.runtext;"); + additionalDeclarations.Add("int beginning = base.runtextbeg;"); + using (EmitBlock(writer, "if (pos > beginning && inputSpan[pos - 1] != '\\n')")) { - writer.WriteLine("int newlinePos = global::System.MemoryExtensions.IndexOf(runtextSpan.Slice(runtextpos), '\\n');"); - using (EmitBlock(writer, "if (newlinePos == -1 || newlinePos + runtextpos + 1 > runtextend)")) + writer.WriteLine("int newlinePos = global::System.MemoryExtensions.IndexOf(inputSpan.Slice(pos), '\\n');"); + using (EmitBlock(writer, "if (newlinePos < 0 || newlinePos + pos + 1 > end)")) { writer.WriteLine("goto ReturnFalse;"); } - writer.WriteLine("runtextpos = newlinePos + runtextpos + 1;"); + writer.WriteLine("pos = newlinePos + pos + 1;"); } writer.WriteLine(); break; @@ -421,20 +450,20 @@ bool EmitAnchors() return false; } - // Emits a case-sensitive left-to-right prefix search for a string at the beginning of the pattern. - void EmitIndexOf_LeftToRight(string prefix) + // Emits a case-sensitive prefix search for a string at the beginning of the pattern. + void EmitIndexOf(string prefix) { - writer.WriteLine($"int i = global::System.MemoryExtensions.IndexOf(runtextSpan.Slice(runtextpos, runtextend - runtextpos), {Literal(prefix)});"); + writer.WriteLine($"int i = global::System.MemoryExtensions.IndexOf(inputSpan.Slice(pos, end - pos), {Literal(prefix)});"); writer.WriteLine("if (i >= 0)"); writer.WriteLine("{"); - writer.WriteLine(" base.runtextpos = runtextpos + i;"); + writer.WriteLine(" base.runtextpos = pos + i;"); writer.WriteLine(" return true;"); writer.WriteLine("}"); } - // Emits a left-to-right search for a set at a fixed position from the start of the pattern, + // Emits a search for a set at a fixed position from the start of the pattern, // and potentially other sets at other fixed positions in the pattern. - void EmitFixedSet_LeftToRight() + void EmitFixedSet() { List<(char[]? Chars, string Set, int Distance, bool CaseInsensitive)>? sets = code.FindOptimizations.FixedDistanceSets; (char[]? Chars, string Set, int Distance, bool CaseInsensitive) primarySet = sets![0]; @@ -450,7 +479,7 @@ void EmitFixedSet_LeftToRight() FinishEmitScope loopBlock = default; if (needLoop) { - writer.WriteLine("global::System.ReadOnlySpan span = runtextSpan.Slice(runtextpos, runtextend - runtextpos);"); + writer.WriteLine("global::System.ReadOnlySpan span = inputSpan.Slice(pos, end - pos);"); string upperBound = "span.Length" + (setsToUse > 1 || primarySet.Distance != 0 ? $" - {minRequiredLength - 1}" : ""); loopBlock = EmitBlock(writer, $"for (int i = 0; i < {upperBound}; i++)"); } @@ -459,7 +488,7 @@ void EmitFixedSet_LeftToRight() { string span = needLoop ? "span" : - "runtextSpan.Slice(runtextpos, runtextend - runtextpos)"; + "inputSpan.Slice(pos, end - pos)"; span = (needLoop, primarySet.Distance) switch { @@ -501,7 +530,7 @@ void EmitFixedSet_LeftToRight() writer.WriteLine($"int i = {indexOf};"); using (EmitBlock(writer, "if (i >= 0)")) { - writer.WriteLine("base.runtextpos = runtextpos + i;"); + writer.WriteLine("base.runtextpos = pos + i;"); writer.WriteLine("return true;"); } } @@ -522,7 +551,7 @@ void EmitFixedSet_LeftToRight() for (; setIndex < setsToUse; setIndex++) { string spanIndex = $"span[i{(sets[setIndex].Distance > 0 ? $" + {sets[setIndex].Distance}" : "")}]"; - string charInClassExpr = MatchCharacterClass(hasTextInfo, options, spanIndex, sets[setIndex].Set, sets[setIndex].CaseInsensitive, additionalDeclarations); + string charInClassExpr = MatchCharacterClass(hasTextInfo, options, spanIndex, sets[setIndex].Set, sets[setIndex].CaseInsensitive, negate: false, additionalDeclarations, ref requiredHelpers); if (setIndex == start) { @@ -540,7 +569,7 @@ void EmitFixedSet_LeftToRight() using (hasCharClassConditions ? EmitBlock(writer, null) : default) { - writer.WriteLine("base.runtextpos = runtextpos + i;"); + writer.WriteLine("base.runtextpos = pos + i;"); writer.WriteLine("return true;"); } } @@ -575,7 +604,7 @@ FindNextStartingPositionMode.FixedSets_LeftToRight_CaseInsensitive or } /// Emits the body of the Go override. - private static void EmitGo(IndentedTextWriter writer, RegexMethod rm, string id) + private static RequiredHelperFunctions EmitGo(IndentedTextWriter writer, RegexMethod rm, string id) { // In .NET Framework and up through .NET Core 3.1, the code generated for RegexOptions.Compiled was effectively an unrolled // version of what RegexInterpreter would process. The RegexNode tree would be turned into a series of opcodes via @@ -603,123 +632,157 @@ private static void EmitGo(IndentedTextWriter writer, RegexMethod rm, string id) RegexOptions options = (RegexOptions)rm.Options; RegexCode code = rm.Code; - bool hasTimeout = false; + RequiredHelperFunctions requiredHelpers = RequiredHelperFunctions.None; // Helper to define names. Names start unadorned, but as soon as there's repetition, // they begin to have a numbered suffix. var usedNames = new Dictionary(); - string ReserveName(string prefix) - { - usedNames.TryGetValue(prefix, out int count); - usedNames[prefix] = count + 1; - return count == 0 ? prefix : $"{prefix}{count}"; - } - void MarkLabel(string label, bool emitSemicolon = true) => writer.WriteLine($"{label}:{(emitSemicolon ? ";" : "")}"); - - RegexNode node = rm.Code.Tree.Root; + // Every RegexTree is rooted in the implicit Capture for the whole expression. + // Skip the Capture node. We handle the implicit root capture specially. + RegexNode node = code.Tree.Root; Debug.Assert(node.Type == RegexNode.Capture, "Every generated tree should begin with a capture node"); Debug.Assert(node.ChildCount() == 1, "Capture nodes should have one child"); - - // Skip the Capture node. We handle the implicit root capture specially. node = node.Child(0); - // In some limited cases, FindFirstChar will only return true if it successfully matched the whole thing. - // This is the case, in particular, for strings. We can special case these to do essentially nothing - // in Go other than emit the capture. - if (!IsCaseInsensitive(node)) // FindFirstChar may not be 100% accurate on casing in all cultures + // In some limited cases, FindFirstChar will only return true if it successfully matched the whole expression. + // We can special case these to do essentially nothing in Go other than emit the capture. + switch (node.Type) { - switch (node.Type) - { - case RegexNode.Multi: - case RegexNode.Notone: - case RegexNode.One: - case RegexNode.Set: - writer.WriteLine($"int start = base.runtextpos;"); - writer.WriteLine($"int end = start + {(node.Type == RegexNode.Multi ? node.Str!.Length : 1)};"); - writer.WriteLine("base.Capture(0, start, end);"); - writer.WriteLine("base.runtextpos = end;"); - return; - } + case RegexNode.Multi or RegexNode.Notone or RegexNode.One or RegexNode.Set when !IsCaseInsensitive(node): + // This is the case for single and multiple characters, though the whole thing is only guaranteed + // to have been validated in FindFirstChar when doing case-sensitive comparison. + writer.WriteLine($"int start = base.runtextpos;"); + writer.WriteLine($"int end = start + {(node.Type == RegexNode.Multi ? node.Str!.Length : 1)};"); + writer.WriteLine("base.Capture(0, start, end);"); + writer.WriteLine("base.runtextpos = end;"); + return requiredHelpers; + + case RegexNode.Empty: + // This case isn't common in production, but it's very common when first getting started with the + // source generator and seeing what happens as you add more to expressions. When approaching + // it from a learning perspective, this is very common, as it's the empty string you start with. + writer.WriteLine("base.Capture(0, base.runtextpos, base.runtextpos);"); + return requiredHelpers; } // In some cases, we need to emit declarations at the beginning of the method, but we only discover we need them later. - // To handle that, we emit a placeholder value that's not valid C#, and then at the end of the code generation we either - // delete it if no additional declarations are required, or we replace it with the list of additional declarations - // built up while generating code. + // To handle that, we build up a collection of all the declarations to include, track where they should be inserted, + // and then insert them at that position once everything else has been output. var additionalDeclarations = new HashSet(); + var additionalLocalFunctions = new Dictionary(); // Declare some locals. string sliceSpan = "slice"; - writer.WriteLine("global::System.ReadOnlySpan runtextSpan = base.runtext;"); - writer.WriteLine("int runtextpos = base.runtextpos;"); - writer.WriteLine("int runtextend = base.runtextend;"); - writer.WriteLine($"int original_runtextpos = runtextpos;"); - hasTimeout = EmitLoopTimeoutCounterIfNeeded(writer, rm); - writer.Write("int runstackpos = 0;"); + writer.WriteLine("global::System.ReadOnlySpan inputSpan = base.runtext;"); + writer.WriteLine("int pos = base.runtextpos, end = base.runtextend;"); + writer.WriteLine($"int original_pos = pos;"); + bool hasTimeout = EmitLoopTimeoutCounterIfNeeded(writer, rm); + bool hasTextInfo = EmitInitializeCultureForGoIfNecessary(writer, rm); writer.Flush(); int additionalDeclarationsPosition = ((StringWriter)writer.InnerWriter).GetStringBuilder().Length; int additionalDeclarationsIndent = writer.Indent; - writer.WriteLine(); - - // TextInfo textInfo = CultureInfo.CurrentCulture.TextInfo; // only if the whole expression or any subportion is ignoring case, and we're not using invariant - bool hasTextInfo = EmitInitializeCultureForGoIfNecessary(writer, rm); // The implementation tries to use const indexes into the span wherever possible, which we can do - // in all places except for variable-length loops. For everything else, we know at any point in - // the regex exactly how far into it we are, and we can use that to index into the span created - // at the beginning of the routine to begin at exactly where we're starting in the input. For - // variable-length loops, we index at this textSpanPos + i, and then after the loop we slice the input - // by i so that this position is still accurate for everything after it. - int textSpanPos = 0; - LoadTextSpanLocal(writer, defineLocal: true); + // for all fixed-length constructs. In such cases (e.g. single chars, repeaters, strings, etc.) + // we know at any point in the regex exactly how far into it we are, and we can use that to index + // into the span created at the beginning of the routine to begin at exactly where we're starting + // in the input. When we encounter a variable-length construct, we transfer the static value to + // pos, slicing the inputSpan appropriately, and then zero out the static position. + int sliceStaticPos = 0; + SliceInputSpan(writer, defineLocal: true); writer.WriteLine(); + // doneLabel starts out as the top-level label for the whole expression failing to match. However, + // it may be changed by the processing of a node to point to whereever subsequent match failures + // should jump to, in support of backtracking or other constructs. For example, before emitting + // the code for a branch N, an alternation will set the the doneLabel to point to the label for + // processing the next branch N+1: that way, any failures in the branch N's processing will + // implicitly end up jumping to the right location without needing to know in what context it's used. string doneLabel = ReserveName("NoMatch"); string originalDoneLabel = doneLabel; - // Emit the code for all nodes in the tree. + // Check whether there are captures anywhere in the expression. If there isn't, we can skip all + // the boilerplate logic around uncapturing, as there won't be anything to uncapture. This can + // be improved as well to be more fine-grained: https://github.com/dotnet/runtime/issues/62451. bool expressionHasCaptures = (node.Options & RegexNode.HasCapturesFlag) != 0; + + // Emit the code for all nodes in the tree. EmitNode(node); - // Emit success - writer.WriteLine("// Match"); - if (textSpanPos > 0) + // If we fall through to this place in the code, we've successfully matched the expression. + writer.WriteLine(); + writer.WriteLine("// The input matched."); + if (sliceStaticPos > 0) { - EmitAdd(writer, "runtextpos", textSpanPos); + EmitAdd(writer, "pos", sliceStaticPos); // TransferSliceStaticPosToPos would also slice, which isn't needed here } - writer.WriteLine("base.runtextpos = runtextpos;"); - writer.WriteLine("base.Capture(0, original_runtextpos, runtextpos);"); + writer.WriteLine("base.runtextpos = pos;"); + writer.WriteLine("base.Capture(0, original_pos, pos);"); writer.WriteLine("return;"); writer.WriteLine(); - // Emit failure - writer.WriteLine("// No match"); + // We only get here in the code if the whole expression fails to match and jumps to + // the original value of doneLabel. + writer.WriteLine("// The input didn't match."); MarkLabel(originalDoneLabel, emitSemicolon: !expressionHasCaptures); if (expressionHasCaptures) { EmitUncaptureUntil("0"); } - // We're done. Patch up any additional declarations. + // We're done with the match. + + // Patch up any additional declarations. ReplaceAdditionalDeclarations(writer, additionalDeclarations, additionalDeclarationsPosition, additionalDeclarationsIndent); - return; + // And emit any required helpers. + if (additionalLocalFunctions.Count != 0) + { + writer.WriteLine("return;"); // not strictly necessary, just for readability + + foreach (KeyValuePair localFunctions in additionalLocalFunctions.OrderBy(k => k.Key)) + { + writer.WriteLine(); + foreach (string line in localFunctions.Value) + { + writer.WriteLine(line); + } + } + } + + return requiredHelpers; + + // Helper to create a name guaranteed to be unique within the function. + string ReserveName(string prefix) + { + usedNames.TryGetValue(prefix, out int count); + usedNames[prefix] = count + 1; + return count == 0 ? prefix : $"{prefix}{count}"; + } + + // Helper to emit a label. As of C# 10, labels aren't statements of their own and need to adorn a following statement; + // if a label appears just before a closing brace, then, it's a compilation error. To avoid issues there, this by + // default implements a blank statement (a semicolon) after each label, but individual uses can opt-out of the semicolon + // when it's known the label will always be followed by a statement. + void MarkLabel(string label, bool emitSemicolon = true) => writer.WriteLine($"{label}:{(emitSemicolon ? ";" : "")}"); + + // Whether the node has RegexOptions.IgnoreCase set. static bool IsCaseInsensitive(RegexNode node) => (node.Options & RegexOptions.IgnoreCase) != 0; - // Creates a local span for runtextSpan starting at runtextpos until base.runtextend. - void LoadTextSpanLocal(IndentedTextWriter writer, bool defineLocal = false) + // Slices the inputSpan starting at pos until end and stores it into slice. + void SliceInputSpan(IndentedTextWriter writer, bool defineLocal = false) { if (defineLocal) { writer.Write("global::System.ReadOnlySpan "); } - writer.WriteLine($"{sliceSpan} = runtextSpan.Slice(runtextpos, runtextend - runtextpos);"); + writer.WriteLine($"{sliceSpan} = inputSpan.Slice(pos, end - pos);"); } // Emits the sum of a constant and a value from a local. string Sum(int constant, string? local = null) => - local is null ? constant.ToString() : + local is null ? constant.ToString(CultureInfo.InvariantCulture) : constant == 0 ? local : $"{constant} + {local}"; @@ -733,26 +796,29 @@ void EmitSpanLengthCheck(int requiredLength, string? dynamicRequiredLength = nul } } + // Returns a length check for the current span slice. The check returns true if + // the span isn't long enough for the specified length. string SpanLengthCheck(int requiredLength, string? dynamicRequiredLength = null) => - $"(uint){sliceSpan}.Length < {Sum(textSpanPos + requiredLength, dynamicRequiredLength)}"; + dynamicRequiredLength is null && sliceStaticPos + requiredLength == 1 ? $"{sliceSpan}.IsEmpty" : + $"(uint){sliceSpan}.Length < {Sum(sliceStaticPos + requiredLength, dynamicRequiredLength)}"; - // Adds the value of textSpanPos into the runtextpos local, slices slice by the corresponding amount, - // and zeros out textSpanPos. - void TransferTextSpanPosToRunTextPos() + // Adds the value of sliceStaticPos into the pos local, slices slice by the corresponding amount, + // and zeros out sliceStaticPos. + void TransferSliceStaticPosToPos() { - if (textSpanPos > 0) + if (sliceStaticPos > 0) { - EmitAdd(writer, "runtextpos", textSpanPos); - writer.WriteLine($"{sliceSpan} = {sliceSpan}.Slice({textSpanPos});"); - textSpanPos = 0; + EmitAdd(writer, "pos", sliceStaticPos); + writer.WriteLine($"{sliceSpan} = {sliceSpan}.Slice({sliceStaticPos});"); + sliceStaticPos = 0; } } - string AddTextSpanPos() => textSpanPos > 0 ? $" + {textSpanPos}" : ""; - // Emits the code for an alternation. void EmitAlternation(RegexNode node) { + Debug.Assert(node.Type is RegexNode.Alternate, $"Unexpected type: {node.Type}"); + int childCount = node.ChildCount(); Debug.Assert(childCount >= 2); @@ -809,25 +875,31 @@ void EmitAlternation(RegexNode node) { EmitAllBranches(); } + return; + // Emits the code for a switch-based alternation of non-overlapping branches. void EmitSwitchedBranches() { + // We need at least 1 remaining character in the span, for the char to switch on. EmitSpanLengthCheck(1); writer.WriteLine(); - using (EmitBlock(writer, $"switch ({ToLowerIfNeeded(hasTextInfo, options, $"{sliceSpan}[{textSpanPos++}]", IsCaseInsensitive(node))})")) + // Emit a switch statement on the first char of each branch. + using (EmitBlock(writer, $"switch ({ToLowerIfNeeded(hasTextInfo, options, $"{sliceSpan}[{sliceStaticPos++}]", IsCaseInsensitive(node))})")) { - int startingTextSpanPos = textSpanPos; + int startingSliceStaticPos = sliceStaticPos; + + // Emit a case for each branch. for (int i = 0; i < childCount; i++) { - textSpanPos = startingTextSpanPos; + sliceStaticPos = startingSliceStaticPos; RegexNode child = node.Child(i); - Debug.Assert(child.Type is RegexNode.One or RegexNode.Multi or RegexNode.Concatenate, child.Description()); + Debug.Assert(child.Type is RegexNode.One or RegexNode.Multi or RegexNode.Concatenate, DescribeNode(child)); Debug.Assert(child.Type is not RegexNode.Concatenate || (child.ChildCount() >= 2 && child.Child(0).Type is RegexNode.One or RegexNode.Multi)); RegexNode? childStart = child.FindBranchOneOrMultiStart(); - Debug.Assert(childStart is not null, child.Description()); + Debug.Assert(childStart is not null, DescribeNode(child)); writer.WriteLine($"case {Literal(childStart.FirstCharOfOneOrMulti())}:"); writer.Indent++; @@ -869,10 +941,11 @@ static RegexNode CloneMultiWithoutFirstChar(RegexNode node) doneLabel = originalDoneLabel; // If we get here in the generated code, the branch completed successfully. - // Before jumping to the end, we need to zero out textSpanPos, so that no + // Before jumping to the end, we need to zero out sliceStaticPos, so that no // matter what the value is after the branch, whatever follows the alternate - // will see the same textSpanPos. - TransferTextSpanPosToRunTextPos(); + // will see the same sliceStaticPos. + writer.WriteLine(); + TransferSliceStaticPosToPos(); writer.WriteLine($"break;"); writer.WriteLine(); @@ -880,10 +953,7 @@ static RegexNode CloneMultiWithoutFirstChar(RegexNode node) } // Default branch if the character didn't match the start of any branches. - writer.WriteLine("default:"); - writer.Indent++; - writer.WriteLine($"goto {doneLabel};"); - writer.Indent--; + writer.WriteLine($"default: goto {doneLabel};"); } } @@ -892,11 +962,10 @@ void EmitAllBranches() // Label to jump to when any branch completes successfully. string matchLabel = ReserveName("AlternationMatch"); - // Save off runtextpos. We'll need to reset this each time a branch fails. - string startingRunTextPos = ReserveName("alternation_starting_runtextpos"); - additionalDeclarations.Add($"int {startingRunTextPos} = 0;"); - writer.WriteLine($"{startingRunTextPos} = runtextpos;"); - int startingTextSpanPos = textSpanPos; + // Save off pos. We'll need to reset this each time a branch fails. + string startingPos = ReserveName("alternation_starting_pos"); + writer.WriteLine($"int {startingPos} = pos;"); + int startingSliceStaticPos = sliceStaticPos; // We need to be able to undo captures in two situations: // - If a branch of the alternation itself contains captures, then if that branch @@ -918,12 +987,11 @@ void EmitAllBranches() // of the alternation, which will again dutifully unwind the remaining captures until // what they were at the start of the alternation. Of course, if there are no captures // anywhere in the regex, we don't have to do any of that. - string? startingCrawlPos = null; + string? startingCapturePos = null; if (expressionHasCaptures && ((node.Options & RegexNode.HasCapturesFlag) != 0 || !isAtomic)) { - startingCrawlPos = ReserveName("alternation_starting_crawlpos"); - additionalDeclarations.Add($"int {startingCrawlPos} = 0;"); - writer.WriteLine($"{startingCrawlPos} = base.Crawlpos();"); + startingCapturePos = ReserveName("alternation_starting_capturepos"); + writer.WriteLine($"int {startingCapturePos} = base.Crawlpos();"); } writer.WriteLine(); @@ -939,67 +1007,77 @@ void EmitAllBranches() for (int i = 0; i < childCount; i++) { - using var __ = EmitScope(writer, $"Branch {i}", faux: !isAtomic); - bool isLastBranch = i == childCount - 1; - - string? nextBranch = null; - if (!isLastBranch) - { - // Failure to match any branch other than the last one should result - // in jumping to process the next branch. - nextBranch = ReserveName("AlternationBranch"); - doneLabel = nextBranch; - } - else + using (EmitScope(writer, $"Branch {i}", faux: !isAtomic)) { - // Failure to match the last branch is equivalent to failing to match - // the whole alternation, which means those failures should jump to - // what "doneLabel" was defined as when starting the alternation. - doneLabel = originalDoneLabel; - } + bool isLastBranch = i == childCount - 1; - // Emit the code for each branch. - EmitNode(node.Child(i)); + string? nextBranch = null; + if (!isLastBranch) + { + // Failure to match any branch other than the last one should result + // in jumping to process the next branch. + nextBranch = ReserveName("AlternationBranch"); + doneLabel = nextBranch; + } + else + { + // Failure to match the last branch is equivalent to failing to match + // the whole alternation, which means those failures should jump to + // what "doneLabel" was defined as when starting the alternation. + doneLabel = originalDoneLabel; + } - // Add this branch to the backtracking table. At this point, either the child - // had backtracking constructs, in which case doneLabel points to the last one - // and that's where we'll want to jump to, or it doesn't, in which case doneLabel - // still points to the nextBranch, which similarly is where we'll want to jump to. - if (!isAtomic) - { - EmitRunstackResizeIfNeeded(2); - writer.WriteLine($"{RunstackPush()} = {i};"); - if (startingCrawlPos is not null) + // Emit the code for each branch. + EmitNode(node.Child(i)); + writer.WriteLine(); + + // Add this branch to the backtracking table. At this point, either the child + // had backtracking constructs, in which case doneLabel points to the last one + // and that's where we'll want to jump to, or it doesn't, in which case doneLabel + // still points to the nextBranch, which similarly is where we'll want to jump to. + if (!isAtomic) { - writer.WriteLine($"{RunstackPush()} = {startingCrawlPos};"); + EmitStackPush(startingCapturePos is not null ? + new[] { i.ToString(), startingPos, startingCapturePos } : + new[] { i.ToString(), startingPos }); } - writer.WriteLine($"{RunstackPush()} = {startingRunTextPos};"); - } - labelMap[i] = doneLabel; - - // If we get here in the generated code, the branch completed successfully. - // Before jumping to the end, we need to zero out textSpanPos, so that no - // matter what the value is after the branch, whatever follows the alternate - // will see the same textSpanPos. - TransferTextSpanPosToRunTextPos(); - writer.WriteLine($"goto {matchLabel};"); - writer.WriteLine(); + labelMap[i] = doneLabel; - // Reset state for next branch and loop around to generate it. This includes - // setting runtextpos back to what it was at the beginning of the alternation, - // updating slice to be the full length it was, and if there's a capture that - // needs to be reset, uncapturing it. - if (!isLastBranch) - { - MarkLabel(nextBranch!, emitSemicolon: false); - writer.WriteLine($"runtextpos = {startingRunTextPos};"); - LoadTextSpanLocal(writer); - textSpanPos = startingTextSpanPos; - if (startingCrawlPos is not null) + // If we get here in the generated code, the branch completed successfully. + // Before jumping to the end, we need to zero out sliceStaticPos, so that no + // matter what the value is after the branch, whatever follows the alternate + // will see the same sliceStaticPos. + TransferSliceStaticPosToPos(); + if (!isLastBranch || !isAtomic) { - EmitUncaptureUntil(startingCrawlPos); + // If this isn't the last branch, we're about to output a reset section, + // and if this isn't atomic, there will be a backtracking section before + // the end of the method. In both of those cases, we've successfully + // matched and need to skip over that code. If, however, this is the + // last branch and this is an atomic alternation, we can just fall + // through to the successfully matched location. + writer.WriteLine($"goto {matchLabel};"); + } + + // Reset state for next branch and loop around to generate it. This includes + // setting pos back to what it was at the beginning of the alternation, + // updating slice to be the full length it was, and if there's a capture that + // needs to be reset, uncapturing it. + if (!isLastBranch) + { + writer.WriteLine(); + MarkLabel(nextBranch!, emitSemicolon: false); + writer.WriteLine($"pos = {startingPos};"); + SliceInputSpan(writer); + sliceStaticPos = startingSliceStaticPos; + if (startingCapturePos is not null) + { + EmitUncaptureUntil(startingCapturePos); + } } } + + writer.WriteLine(); } // We should never fall through to this location in the generated code. Either @@ -1018,92 +1096,148 @@ void EmitAllBranches() doneLabel = backtrackLabel; MarkLabel(backtrackLabel, emitSemicolon: false); - writer.WriteLine($"{startingRunTextPos} = {RunstackPop()};"); - if (startingCrawlPos is not null) - { - writer.WriteLine($"{startingCrawlPos} = {RunstackPop()};"); - } - using (EmitBlock(writer, $"switch ({RunstackPop()})")) + EmitStackPop(startingCapturePos is not null ? + new[] { startingCapturePos, startingPos } : + new[] { startingPos}); + using (EmitBlock(writer, $"switch ({StackPop()})")) { for (int i = 0; i < labelMap.Length; i++) { writer.WriteLine($"case {i}: goto {labelMap[i]};"); } - writer.WriteLine("default: global::System.Diagnostics.Debug.Fail(\"Unknown backtracking location\"); break;"); } writer.WriteLine(); } // Successfully completed the alternate. MarkLabel(matchLabel); - Debug.Assert(textSpanPos == 0); + Debug.Assert(sliceStaticPos == 0); } } // Emits the code to handle a backreference. void EmitBackreference(RegexNode node) { + Debug.Assert(node.Type is RegexNode.Ref, $"Unexpected type: {node.Type}"); + int capnum = RegexParser.MapCaptureNumber(node.M, rm.Code.Caps); - TransferTextSpanPosToRunTextPos(); + TransferSliceStaticPosToPos(); - using (EmitBlock(writer, $"if (base.IsMatched({capnum}))")) + // If the specified capture hasn't yet captured anything, fail to match... except when using RegexOptions.ECMAScript, + // in which case per ECMA 262 section 21.2.2.9 the backreference should succeed. + if ((node.Options & RegexOptions.ECMAScript) != 0) + { + writer.WriteLine($"// If the {DescribeNonNegative(node.M)} capture hasn't matched, the backreference matches with RegexOptions.ECMAScript rules."); + using (EmitBlock(writer, $"if (base.IsMatched({capnum}))")) + { + EmitWhenHasCapture(); + } + } + else { - string matchLength = ReserveName("backreference_matchLength"); - writer.WriteLine($"int {matchLength} = base.MatchLength({capnum});"); - using (EmitBlock(writer, $"if ({sliceSpan}.Length < {matchLength})")) + writer.WriteLine($"// If the {DescribeNonNegative(node.M)} capture hasn't matched, the backreference doesn't match."); + using (EmitBlock(writer, $"if (!base.IsMatched({capnum}))")) { writer.WriteLine($"goto {doneLabel};"); } writer.WriteLine(); + EmitWhenHasCapture(); + } + + void EmitWhenHasCapture() + { + writer.WriteLine("// Get the captured text. If it doesn't match at the current position, the backreference doesn't match."); - string matchIndex = ReserveName("backreference_matchIndex"); - writer.WriteLine($"int {matchIndex} = base.MatchIndex({capnum});"); + additionalDeclarations.Add("int matchLength = 0;"); + writer.WriteLine($"matchLength = base.MatchLength({capnum});"); - string i = ReserveName("backreference_iteration"); - using (EmitBlock(writer, $"for (int {i} = 0; {i} < {matchLength}; {i}++)")) + if (!IsCaseInsensitive(node)) { - using (EmitBlock(writer, $"if ({ToLowerIfNeeded(hasTextInfo, options, $"runtextSpan[{matchIndex} + {i}]", IsCaseInsensitive(node))} != {ToLowerIfNeeded(hasTextInfo, options, $"{sliceSpan}[{i}]", IsCaseInsensitive(node))})")) + // If we're case-sensitive, we can simply validate that the remaining length of the slice is sufficient + // to possibly match, and then do a SequenceEqual against the matched text. + writer.WriteLine($"if ({sliceSpan}.Length < matchLength || "); + using (EmitBlock(writer, $" !global::System.MemoryExtensions.SequenceEqual(inputSpan.Slice(base.MatchIndex({capnum}), matchLength), {sliceSpan}.Slice(0, matchLength)))")) { writer.WriteLine($"goto {doneLabel};"); } } - writer.WriteLine(); - - writer.WriteLine($"runtextpos += {matchLength};"); - LoadTextSpanLocal(writer); - } - - if ((node.Options & RegexOptions.ECMAScript) == 0) - { - using (EmitBlock(writer, "else")) + else { - writer.WriteLine($"goto {doneLabel};"); + // For case-insensitive, we have to walk each character individually. + using (EmitBlock(writer, $"if ({sliceSpan}.Length < matchLength)")) + { + writer.WriteLine($"goto {doneLabel};"); + } + writer.WriteLine(); + + additionalDeclarations.Add("int matchIndex = 0;"); + writer.WriteLine($"matchIndex = base.MatchIndex({capnum});"); + using (EmitBlock(writer, $"for (int i = 0; i < matchLength; i++)")) + { + using (EmitBlock(writer, $"if ({ToLower(hasTextInfo, options, $"inputSpan[matchIndex + i]")} != {ToLower(hasTextInfo, options, $"{sliceSpan}[i]")})")) + { + writer.WriteLine($"goto {doneLabel};"); + } + } } + writer.WriteLine(); + + writer.WriteLine($"pos += matchLength;"); + SliceInputSpan(writer); } } // Emits the code for an if(backreference)-then-else conditional. void EmitBackreferenceConditional(RegexNode node) { - bool isAtomic = node.IsAtomicByParent(); + Debug.Assert(node.Type is RegexNode.Testref, $"Unexpected type: {node.Type}"); - // We're branching in a complicated fashion. Make sure textSpanPos is 0. - TransferTextSpanPosToRunTextPos(); + // We're branching in a complicated fashion. Make sure sliceStaticPos is 0. + TransferSliceStaticPosToPos(); // Get the capture number to test. int capnum = RegexParser.MapCaptureNumber(node.M, rm.Code.Caps); + // Get the "yes" branch and the optional "no" branch, if it exists. + RegexNode yesBranch = node.Child(0); + RegexNode? noBranch = node.ChildCount() > 1 && node.Child(1) is { Type: not RegexNode.Empty } childNo ? childNo : null; string originalDoneLabel = doneLabel; - string endRef = ReserveName("ConditionalBackreferenceEnd"); - bool hasNo = node.ChildCount() > 1 && node.Child(1).Type != RegexNode.Empty; + + // If the child branches might backtrack, we can't emit the branches inside constructs that + // require braces, e.g. if/else, even though that would yield more idiomatic output. + // But if we know for certain they won't backtrack, we can output the nicer code. + if (node.IsAtomicByParent() || (!PossiblyBacktracks(yesBranch) && (noBranch is null || !PossiblyBacktracks(noBranch)))) + { + using (EmitBlock(writer, $"if (base.IsMatched({capnum}))")) + { + writer.WriteLine($"// The {DescribeNonNegative(node.M)} capture group captured a value. Match the first branch."); + EmitNode(yesBranch); + writer.WriteLine(); + TransferSliceStaticPosToPos(); // make sure sliceStaticPos is 0 after each branch + } + + if (noBranch is not null) + { + using (EmitBlock(writer, $"else")) + { + writer.WriteLine($"// Otherwise, match the second branch."); + EmitNode(noBranch); + writer.WriteLine(); + TransferSliceStaticPosToPos(); // make sure sliceStaticPos is 0 after each branch + } + } + + doneLabel = originalDoneLabel; + return; + } // As with alternations, we have potentially multiple branches, each of which may contain // backtracking constructs, but the expression after the conditional needs a single target // to backtrack to. So, we expose a single Backtrack label and track which branch was // followed in this resumeAt local. string resumeAt = ReserveName("conditionalbackreference_branch"); - additionalDeclarations.Add($"int {resumeAt} = 0;"); + writer.WriteLine($"int {resumeAt} = 0;"); // While it would be nicely readable to use an if/else block, if the branches contain // anything that triggers backtracking, labels will end up being defined, and if they're @@ -1119,30 +1253,33 @@ void EmitBackreferenceConditional(RegexNode node) // The specified capture was captured. Run the "yes" branch. // If it successfully matches, jump to the end. - EmitNode(node.Child(0)); - TransferTextSpanPosToRunTextPos(); // make sure textSpanPos is 0 after each branch - string postIfDoneLabel = doneLabel; - if (postIfDoneLabel != originalDoneLabel) + EmitNode(yesBranch); + writer.WriteLine(); + TransferSliceStaticPosToPos(); // make sure sliceStaticPos is 0 after each branch + string postYesDoneLabel = doneLabel; + if (postYesDoneLabel != originalDoneLabel) { writer.WriteLine($"{resumeAt} = 0;"); } - if (postIfDoneLabel != originalDoneLabel || hasNo) + string endRef = ReserveName("ConditionalBackreferenceEnd"); + if (postYesDoneLabel != originalDoneLabel || noBranch is not null) { writer.WriteLine($"goto {endRef};"); writer.WriteLine(); } MarkLabel(refNotMatched); - string postElseDoneLabel = originalDoneLabel; - if (hasNo) + string postNoDoneLabel = originalDoneLabel; + if (noBranch is not null) { // The earlier base.IsMatched returning false will jump to here. // Output the no branch. doneLabel = originalDoneLabel; - EmitNode(node.Child(1)); - TransferTextSpanPosToRunTextPos(); // make sure textSpanPos is 0 after each branch - postElseDoneLabel = doneLabel; - if (postElseDoneLabel != originalDoneLabel) + EmitNode(noBranch); + writer.WriteLine(); + TransferSliceStaticPosToPos(); // make sure sliceStaticPos is 0 after each branch + postNoDoneLabel = doneLabel; + if (postNoDoneLabel != originalDoneLabel) { writer.WriteLine($"{resumeAt} = 1;"); } @@ -1152,59 +1289,47 @@ void EmitBackreferenceConditional(RegexNode node) // There's only a yes branch. If it's going to cause us to output a backtracking // label but code may not end up taking the yes branch path, we need to emit a resumeAt // that will cause the backtracking to immediately pass through this node. - if (postIfDoneLabel != originalDoneLabel) + if (postYesDoneLabel != originalDoneLabel) { writer.WriteLine($"{resumeAt} = 2;"); } } - if (isAtomic) - { - doneLabel = originalDoneLabel; - } - else + // If either the yes branch or the no branch contained backtracking, subsequent expressions + // might try to backtrack to here, so output a backtracking map based on resumeAt. + if (postYesDoneLabel != originalDoneLabel || postNoDoneLabel != originalDoneLabel) { - // If either the yes branch or the no branch contained backtracking, subsequent expressions - // might try to backtrack to here, so output a backtracking map based on resumeAt. - if (postIfDoneLabel != originalDoneLabel || postElseDoneLabel != originalDoneLabel) - { - // Skip the backtracking section. - writer.WriteLine($"goto {endRef};"); - writer.WriteLine(); - - string backtrack = ReserveName("ConditionalBackreferenceBacktrack"); - doneLabel = backtrack; - MarkLabel(backtrack); + // Skip the backtracking section. + writer.WriteLine($"goto {endRef};"); + writer.WriteLine(); - writer.WriteLine($"{resumeAt} = {RunstackPop()};"); + string backtrack = ReserveName("ConditionalBackreferenceBacktrack"); + doneLabel = backtrack; + MarkLabel(backtrack); - using (EmitBlock(writer, $"switch ({resumeAt})")) + EmitStackPop(resumeAt); + using (EmitBlock(writer, $"switch ({resumeAt})")) + { + if (postYesDoneLabel != originalDoneLabel) { - if (postIfDoneLabel != originalDoneLabel) - { - writer.WriteLine($"case 0: goto {postIfDoneLabel};"); - } - - if (postElseDoneLabel != originalDoneLabel) - { - writer.WriteLine($"case 1: goto {postElseDoneLabel};"); - } + writer.WriteLine($"case 0: goto {postYesDoneLabel};"); + } - writer.WriteLine($"default: goto {originalDoneLabel};"); + if (postNoDoneLabel != originalDoneLabel) + { + writer.WriteLine($"case 1: goto {postNoDoneLabel};"); } + + writer.WriteLine($"default: goto {originalDoneLabel};"); } } - if (postIfDoneLabel != originalDoneLabel || hasNo) + if (postYesDoneLabel != originalDoneLabel || noBranch is not null) { MarkLabel(endRef); - if (!isAtomic) + if (postYesDoneLabel != originalDoneLabel || postNoDoneLabel != originalDoneLabel) { - if (postIfDoneLabel != originalDoneLabel || postElseDoneLabel != originalDoneLabel) - { - EmitRunstackResizeIfNeeded(1); - writer.WriteLine($"{RunstackPush()} = {resumeAt};"); - } + EmitStackPush(resumeAt); } } } @@ -1212,21 +1337,20 @@ void EmitBackreferenceConditional(RegexNode node) // Emits the code for an if(expression)-then-else conditional. void EmitExpressionConditional(RegexNode node) { + Debug.Assert(node.Type is RegexNode.Testgroup, $"Unexpected type: {node.Type}"); + bool isAtomic = node.IsAtomicByParent(); - // We're branching in a complicated fashion. Make sure textSpanPos is 0. - TransferTextSpanPosToRunTextPos(); + // We're branching in a complicated fashion. Make sure sliceStaticPos is 0. + TransferSliceStaticPosToPos(); // The first child node is the conditional expression. If this matches, then we branch to the "yes" branch. // If it doesn't match, then we branch to the optional "no" branch if it exists, or simply skip the "yes" - // branch, otherwise. The conditional is treated as a positive lookahead. If it's not already - // such a node, wrap it in one. + // branch, otherwise. The conditional is treated as a positive lookahead if it isn't already one. RegexNode conditional = node.Child(0); - if (conditional is not { Type: RegexNode.Require }) + if (conditional is { Type: RegexNode.Require }) { - var newConditional = new RegexNode(RegexNode.Require, conditional.Options); - newConditional.AddChild(conditional); - conditional = newConditional; + conditional = conditional.Child(0); } // Get the "yes" branch and the optional "no" branch, if it exists. @@ -1237,12 +1361,17 @@ void EmitExpressionConditional(RegexNode node) string? no = noBranch is not null ? ReserveName("ConditionalExpressionNoBranch") : null; // If the conditional expression has captures, we'll need to uncapture them in the case of no match. - string? startingCrawlPos = null; + string? startingCapturePos = null; if ((conditional.Options & RegexNode.HasCapturesFlag) != 0) { - startingCrawlPos = ReserveName("conditionalexpression_starting_crawlpos"); - writer.WriteLine($"int {startingCrawlPos} = base.Crawlpos();"); - writer.WriteLine(); + startingCapturePos = ReserveName("conditionalexpression_starting_capturepos"); + writer.WriteLine($"int {startingCapturePos} = base.Crawlpos();"); + } + + string resumeAt = ReserveName("conditionalexpression_resumeAt"); + if (!isAtomic) + { + writer.WriteLine($"int {resumeAt} = 0;"); } // Emit the conditional expression. We need to reroute any match failures to either the "no" branch @@ -1250,31 +1379,22 @@ void EmitExpressionConditional(RegexNode node) string originalDoneLabel = doneLabel; string tmpDoneLabel = no ?? end; doneLabel = tmpDoneLabel; - EmitPositiveLookaheadAssertion(conditional); + EmitPositiveLookaheadAssertionChild(conditional); if (doneLabel == tmpDoneLabel) { doneLabel = originalDoneLabel; } - string postConditionalDoneLabel = doneLabel; - string resumeAt = ReserveName("conditionalexpression_resumeAt"); - if (!isAtomic) - { - additionalDeclarations.Add($"int {resumeAt} = 0;"); - } // If we get to this point of the code, the conditional successfully matched, so run the "yes" branch. // Since the "yes" branch may have a different execution path than the "no" branch or the lack of - // any branch, we need to store the current textSpanPosition and reset it prior to emitting the code + // any branch, we need to store the current sliceStaticPos and reset it prior to emitting the code // for what comes after the "yes" branch, so that everyone is on equal footing. - int startingTextSpanPos = textSpanPos; + int startingSliceStaticPos = sliceStaticPos; EmitNode(yesBranch); - TransferTextSpanPosToRunTextPos(); // ensure all subsequent code sees the same textSpanPos value by setting it to 0 + writer.WriteLine(); + TransferSliceStaticPosToPos(); // ensure all subsequent code sees the same sliceStaticPos value by setting it to 0 string postYesDoneLabel = doneLabel; - if (!isAtomic && postYesDoneLabel != originalDoneLabel) - { - writer.WriteLine($"{resumeAt} = 0;"); - } if (postYesDoneLabel != originalDoneLabel || noBranch is not null) { writer.WriteLine($"goto {end};"); @@ -1288,16 +1408,17 @@ void EmitExpressionConditional(RegexNode node) // Emit the no branch, first uncapturing any captures from the expression condition that failed // to match and emit the branch. - MarkLabel(no, emitSemicolon: startingCrawlPos is null); - if (startingCrawlPos is not null) + MarkLabel(no, emitSemicolon: startingCapturePos is null); + if (startingCapturePos is not null) { - EmitUncaptureUntil(startingCrawlPos); + EmitUncaptureUntil(startingCapturePos); } doneLabel = postConditionalDoneLabel; - textSpanPos = startingTextSpanPos; + sliceStaticPos = startingSliceStaticPos; EmitNode(noBranch); - TransferTextSpanPosToRunTextPos(); // ensure all subsequent code sees the same textSpanPos value by setting it to 0 + writer.WriteLine(); + TransferSliceStaticPosToPos(); // ensure all subsequent code sees the same sliceStaticPos value by setting it to 0 postNoDoneLabel = doneLabel; if (!isAtomic && postNoDoneLabel != originalDoneLabel) { @@ -1331,7 +1452,7 @@ void EmitExpressionConditional(RegexNode node) doneLabel = backtrack; MarkLabel(backtrack); - using (EmitBlock(writer, $"switch ({RunstackPop()})")) + using (EmitBlock(writer, $"switch ({StackPop()})")) { if (postYesDoneLabel != postConditionalDoneLabel) { @@ -1349,8 +1470,7 @@ void EmitExpressionConditional(RegexNode node) if (postYesDoneLabel != originalDoneLabel || postNoDoneLabel != originalDoneLabel) { - EmitRunstackResizeIfNeeded(1); - writer.WriteLine($"{RunstackPush()} = {resumeAt};"); + EmitStackPush(resumeAt); } } @@ -1360,15 +1480,15 @@ void EmitExpressionConditional(RegexNode node) // Emits the code for a Capture node. void EmitCapture(RegexNode node, RegexNode? subsequent = null) { - Debug.Assert(node.Type == RegexNode.Capture); + Debug.Assert(node.Type is RegexNode.Capture, $"Unexpected type: {node.Type}"); + int capnum = RegexParser.MapCaptureNumber(node.M, rm.Code.Caps); int uncapnum = RegexParser.MapCaptureNumber(node.N, rm.Code.Caps); bool isAtomic = node.IsAtomicByParent(); - TransferTextSpanPosToRunTextPos(); - string startingRunTextPos = ReserveName("capture_starting_runtextpos"); - additionalDeclarations.Add($"int {startingRunTextPos} = 0;"); - writer.WriteLine($"{startingRunTextPos} = runtextpos;"); + TransferSliceStaticPosToPos(); + string startingPos = ReserveName("capture_starting_pos"); + writer.WriteLine($"int {startingPos} = pos;"); writer.WriteLine(); RegexNode child = node.Child(0); @@ -1387,22 +1507,22 @@ void EmitCapture(RegexNode node, RegexNode? subsequent = null) EmitNode(child, subsequent); bool childBacktracks = doneLabel != originalDoneLabel; - TransferTextSpanPosToRunTextPos(); + writer.WriteLine(); + TransferSliceStaticPosToPos(); if (uncapnum == -1) { - writer.WriteLine($"base.Capture({capnum}, {startingRunTextPos}, runtextpos);"); + writer.WriteLine($"base.Capture({capnum}, {startingPos}, pos);"); } else { - writer.WriteLine($"base.TransferCapture({capnum}, {uncapnum}, {startingRunTextPos}, runtextpos);"); + writer.WriteLine($"base.TransferCapture({capnum}, {uncapnum}, {startingPos}, pos);"); } if (!isAtomic && (childBacktracks || node.IsInLoop())) { writer.WriteLine(); - EmitRunstackResizeIfNeeded(1); - writer.WriteLine($"{RunstackPush()} = {startingRunTextPos};"); + EmitStackPush(startingPos); // Skip past the backtracking section string end = ReserveName("SkipBacktrack"); @@ -1412,11 +1532,11 @@ void EmitCapture(RegexNode node, RegexNode? subsequent = null) // Emit a backtracking section that restores the capture's state and then jumps to the previous done label string backtrack = ReserveName($"CaptureBacktrack"); MarkLabel(backtrack); - writer.WriteLine($"{startingRunTextPos} = {RunstackPop()};"); + EmitStackPop(startingPos); if (!childBacktracks) { - writer.WriteLine($"runtextpos = {startingRunTextPos};"); - LoadTextSpanLocal(writer); + writer.WriteLine($"pos = {startingPos};"); + SliceInputSpan(writer); } writer.WriteLine($"goto {doneLabel};"); writer.WriteLine(); @@ -1430,35 +1550,34 @@ void EmitCapture(RegexNode node, RegexNode? subsequent = null) } } - // Emits code to unwind the capture stack until the crawl position specified in the provided local. - void EmitUncaptureUntil(string crawlpos) + // Emits the code to handle a positive lookahead assertion. + void EmitPositiveLookaheadAssertion(RegexNode node) { - using (EmitBlock(writer, $"while (base.Crawlpos() > {crawlpos})")) - { - writer.WriteLine("base.Uncapture();"); - } + Debug.Assert(node.Type is RegexNode.Require, $"Unexpected type: {node.Type}"); + EmitPositiveLookaheadAssertionChild(node.Child(0)); } - // Emits the code to handle a positive lookahead assertion. - void EmitPositiveLookaheadAssertion(RegexNode node) + // Emits the code to handle a node as if it's wrapped in a positive lookahead assertion. + void EmitPositiveLookaheadAssertionChild(RegexNode child) { // Lookarounds are implicitly atomic. Store the original done label to reset at the end. string originalDoneLabel = doneLabel; - // Save off runtextpos. We'll need to reset this upon successful completion of the lookahead. - string startingRunTextPos = ReserveName("positivelookahead_starting_runtextpos"); - writer.WriteLine($"int {startingRunTextPos} = runtextpos;"); + // Save off pos. We'll need to reset this upon successful completion of the lookahead. + string startingPos = ReserveName("positivelookahead_starting_pos"); + writer.WriteLine($"int {startingPos} = pos;"); writer.WriteLine(); - int startingTextSpanPos = textSpanPos; + int startingSliceStaticPos = sliceStaticPos; // Emit the child. - EmitNode(node.Child(0)); + EmitNode(child); // After the child completes successfully, reset the text positions. // Do not reset captures, which persist beyond the lookahead. - writer.WriteLine($"runtextpos = {startingRunTextPos};"); - LoadTextSpanLocal(writer); - textSpanPos = startingTextSpanPos; + writer.WriteLine(); + writer.WriteLine($"pos = {startingPos};"); + SliceInputSpan(writer); + sliceStaticPos = startingSliceStaticPos; doneLabel = originalDoneLabel; } @@ -1466,13 +1585,15 @@ void EmitPositiveLookaheadAssertion(RegexNode node) // Emits the code to handle a negative lookahead assertion. void EmitNegativeLookaheadAssertion(RegexNode node) { + Debug.Assert(node.Type is RegexNode.Prevent, $"Unexpected type: {node.Type}"); + // Lookarounds are implicitly atomic. Store the original done label to reset at the end. string originalDoneLabel = doneLabel; - // Save off runtextpos. We'll need to reset this upon successful completion of the lookahead. - string startingRunTextPos = ReserveName("negativelookahead_starting_runtextpos"); - writer.WriteLine($"int {startingRunTextPos} = runtextpos;"); - int startingTextSpanPos = textSpanPos; + // Save off pos. We'll need to reset this upon successful completion of the lookahead. + string startingPos = ReserveName("negativelookahead_starting_pos"); + writer.WriteLine($"int {startingPos} = pos;"); + int startingSliceStaticPos = sliceStaticPos; string negativeLookaheadDoneLabel = ReserveName("NegativeLookaheadMatch"); doneLabel = negativeLookaheadDoneLabel; @@ -1482,6 +1603,7 @@ void EmitNegativeLookaheadAssertion(RegexNode node) // If the generated code ends up here, it matched the lookahead, which actually // means failure for a _negative_ lookahead, so we need to jump to the original done. + writer.WriteLine(); writer.WriteLine($"goto {originalDoneLabel};"); writer.WriteLine(); @@ -1489,22 +1611,21 @@ void EmitNegativeLookaheadAssertion(RegexNode node) MarkLabel(negativeLookaheadDoneLabel, emitSemicolon: false); // After the child completes in failure (success for negative lookahead), reset the text positions. - writer.WriteLine($"runtextpos = {startingRunTextPos};"); - LoadTextSpanLocal(writer); - textSpanPos = startingTextSpanPos; + writer.WriteLine($"pos = {startingPos};"); + SliceInputSpan(writer); + sliceStaticPos = startingSliceStaticPos; doneLabel = originalDoneLabel; } - static string DescribeNode(RegexNode node) => SymbolDisplay.FormatLiteral(node.Description(), quote: false); - static bool PossiblyBacktracks(RegexNode node) => !( // Certain nodes will never backtrack out of them node.Type is RegexNode.Atomic or // atomic nodes by definition don't give up anything RegexNode.Oneloopatomic or RegexNode.Notoneloopatomic or RegexNode.Setloopatomic or // same for atomic loops RegexNode.One or RegexNode.Notone or RegexNode.Set or // individual characters don't backtrack RegexNode.Multi or // multiple characters don't backtrack - RegexNode.Beginning or RegexNode.Start or RegexNode.End or RegexNode.EndZ or RegexNode.Boundary or RegexNode.NonBoundary or RegexNode.ECMABoundary or RegexNode.NonECMABoundary or // anchors don't backtrack + RegexNode.Ref or // backreferences don't backtrack + RegexNode.Beginning or RegexNode.Bol or RegexNode.Start or RegexNode.End or RegexNode.EndZ or RegexNode.Eol or RegexNode.Boundary or RegexNode.NonBoundary or RegexNode.ECMABoundary or RegexNode.NonECMABoundary or // anchors don't backtrack RegexNode.Nothing or RegexNode.Empty or RegexNode.UpdateBumpalong // empty/nothing don't do anything // Fixed-size repeaters of single characters or atomic don't backtrack || node.Type is RegexNode.Oneloop or RegexNode.Notoneloop or RegexNode.Setloop or RegexNode.Onelazy or RegexNode.Notonelazy or RegexNode.Setlazy && node.M == node.N @@ -1526,6 +1647,11 @@ void EmitNode(RegexNode node, RegexNode? subsequent = null, bool emitLengthCheck case RegexNode.Empty: return; + // A match failure doesn't need a scope. + case RegexNode.Nothing: + writer.WriteLine($"goto {doneLabel};"); + return; + // Atomic is invisible in the generated source, other than its impact on the targets of jumps case RegexNode.Atomic: EmitAtomic(node, subsequent); @@ -1540,107 +1666,106 @@ void EmitNode(RegexNode node, RegexNode? subsequent = null, bool emitLengthCheck // Put the node's code into its own scope. If the node contains labels that may need to // be visible outside of its scope, the scope is still emitted for clarity but is commented out. - using var _ = EmitScope(writer, DescribeNode(node), faux: PossiblyBacktracks(node) && !node.IsAtomicByParent()); - - switch (node.Type) + using (EmitScope(writer, DescribeNode(node), faux: PossiblyBacktracks(node) && !node.IsAtomicByParent())) { - case RegexNode.Beginning: - case RegexNode.Start: - case RegexNode.Bol: - case RegexNode.Eol: - case RegexNode.End: - case RegexNode.EndZ: - EmitAnchors(node); - break; - - case RegexNode.Boundary: - case RegexNode.NonBoundary: - case RegexNode.ECMABoundary: - case RegexNode.NonECMABoundary: - EmitBoundary(node); - break; + switch (node.Type) + { + case RegexNode.Beginning: + case RegexNode.Start: + case RegexNode.Bol: + case RegexNode.Eol: + case RegexNode.End: + case RegexNode.EndZ: + EmitAnchors(node); + break; - case RegexNode.Multi: - EmitMultiChar(node, emitLengthChecksIfRequired); - break; + case RegexNode.Boundary: + case RegexNode.NonBoundary: + case RegexNode.ECMABoundary: + case RegexNode.NonECMABoundary: + EmitBoundary(node); + break; - case RegexNode.One: - case RegexNode.Notone: - case RegexNode.Set: - EmitSingleChar(node, emitLengthChecksIfRequired); - break; + case RegexNode.Multi: + EmitMultiChar(node, emitLengthChecksIfRequired); + break; - case RegexNode.Oneloop: - case RegexNode.Notoneloop: - case RegexNode.Setloop: - EmitSingleCharLoop(node, subsequent, emitLengthChecksIfRequired); - break; + case RegexNode.One: + case RegexNode.Notone: + case RegexNode.Set: + EmitSingleChar(node, emitLengthChecksIfRequired); + break; - case RegexNode.Onelazy: - case RegexNode.Notonelazy: - case RegexNode.Setlazy: - EmitSingleCharLazy(node, emitLengthChecksIfRequired); - break; + case RegexNode.Oneloop: + case RegexNode.Notoneloop: + case RegexNode.Setloop: + EmitSingleCharLoop(node, subsequent, emitLengthChecksIfRequired); + break; - case RegexNode.Oneloopatomic: - case RegexNode.Notoneloopatomic: - case RegexNode.Setloopatomic: - EmitSingleCharAtomicLoop(node, emitLengthChecksIfRequired); - break; + case RegexNode.Onelazy: + case RegexNode.Notonelazy: + case RegexNode.Setlazy: + EmitSingleCharLazy(node, emitLengthChecksIfRequired); + break; - case RegexNode.Loop: - EmitLoop(node); - break; + case RegexNode.Oneloopatomic: + case RegexNode.Notoneloopatomic: + case RegexNode.Setloopatomic: + EmitSingleCharAtomicLoop(node, emitLengthChecksIfRequired); + break; - case RegexNode.Lazyloop: - EmitLazy(node); - break; + case RegexNode.Loop: + EmitLoop(node); + break; - case RegexNode.Alternate: - EmitAlternation(node); - break; + case RegexNode.Lazyloop: + EmitLazy(node); + break; - case RegexNode.Ref: - EmitBackreference(node); - break; + case RegexNode.Alternate: + EmitAlternation(node); + break; - case RegexNode.Testref: - EmitBackreferenceConditional(node); - break; + case RegexNode.Ref: + EmitBackreference(node); + break; - case RegexNode.Testgroup: - EmitExpressionConditional(node); - break; + case RegexNode.Testref: + EmitBackreferenceConditional(node); + break; - case RegexNode.Capture: - EmitCapture(node, subsequent); - break; + case RegexNode.Testgroup: + EmitExpressionConditional(node); + break; - case RegexNode.Require: - EmitPositiveLookaheadAssertion(node); - break; + case RegexNode.Capture: + EmitCapture(node, subsequent); + break; - case RegexNode.Prevent: - EmitNegativeLookaheadAssertion(node); - break; + case RegexNode.Require: + EmitPositiveLookaheadAssertion(node); + break; - case RegexNode.Nothing: - writer.WriteLine($"goto {doneLabel};"); - break; + case RegexNode.Prevent: + EmitNegativeLookaheadAssertion(node); + break; - case RegexNode.UpdateBumpalong: - EmitUpdateBumpalong(); - break; + case RegexNode.UpdateBumpalong: + EmitUpdateBumpalong(node); + break; - default: - Debug.Fail($"Unexpected node type: {node.Type}"); - break; + default: + Debug.Fail($"Unexpected node type: {node.Type}"); + break; + } } } // Emits the node for an atomic. void EmitAtomic(RegexNode node, RegexNode? subsequent) { + Debug.Assert(node.Type is RegexNode.Atomic, $"Unexpected type: {node.Type}"); + // Atomic simply outputs the code for the child, but it ensures that any done label left // set by the child is reset to what it was prior to the node's processing. That way, // anything later that tries to jump back won't see labels set inside the atomic. @@ -1649,19 +1774,24 @@ void EmitAtomic(RegexNode node, RegexNode? subsequent) doneLabel = originalDoneLabel; } - // Emits the code to handle updating base.runtextpos to runtextpos in response to + // Emits the code to handle updating base.runtextpos to pos in response to // an UpdateBumpalong node. This is used when we want to inform the scan loop that // it should bump from this location rather than from the original location. - void EmitUpdateBumpalong() + void EmitUpdateBumpalong(RegexNode node) { - TransferTextSpanPosToRunTextPos(); - writer.WriteLine("base.runtextpos = runtextpos;"); + Debug.Assert(node.Type is RegexNode.UpdateBumpalong, $"Unexpected type: {node.Type}"); + + TransferSliceStaticPosToPos(); + writer.WriteLine("base.runtextpos = pos;"); } // Emits code for a concatenation void EmitConcatenation(RegexNode node, RegexNode? subsequent, bool emitLengthChecksIfRequired) { + Debug.Assert(node.Type is RegexNode.Concatenate, $"Unexpected type: {node.Type}"); + // Emit the code for each child one after the other. + string? prevDescription = null; int childCount = node.ChildCount(); for (int i = 0; i < childCount; i++) { @@ -1677,11 +1807,11 @@ void EmitConcatenation(RegexNode node, RegexNode? subsequent, bool emitLengthChe { for (; i < exclusiveEnd; i++) { - void WriteSingleCharChild(RegexNode child) + void WriteSingleCharChild(RegexNode child, bool includeDescription = true) { if (wroteClauses) { - writer.WriteLine(" ||"); + writer.WriteLine(prevDescription is not null ? $" || // {prevDescription}" : " ||"); writer.Write(" "); } else @@ -1689,6 +1819,7 @@ void WriteSingleCharChild(RegexNode child) writer.Write("if ("); } EmitSingleChar(child, emitLengthCheck: false, clauseOnly: true); + prevDescription = includeDescription ? DescribeNode(child) : null; wroteClauses = true; } @@ -1705,7 +1836,7 @@ RegexNode.Notoneloop or RegexNode.Notonelazy or RegexNode.Notoneloopatomic && { for (int c = 0; c < child.M; c++) { - WriteSingleCharChild(child); + WriteSingleCharChild(child, includeDescription: c == 0); } } else @@ -1716,18 +1847,28 @@ RegexNode.Notoneloop or RegexNode.Notonelazy or RegexNode.Notoneloopatomic && if (wroteClauses) { - writer.WriteLine(")"); + writer.WriteLine(prevDescription is not null ? $") // {prevDescription}" : ")"); using (EmitBlock(writer, null)) { writer.WriteLine($"goto {doneLabel};"); } + if (i < childCount) + { + writer.WriteLine(); + } + wroteClauses = false; + prevDescription = null; } if (i < exclusiveEnd) { - writer.WriteLine(); EmitNode(node.Child(i), i + 1 < childCount ? node.Child(i + 1) : subsequent, emitLengthChecksIfRequired: false); + if (i < childCount - 1) + { + writer.WriteLine(); + } + i++; } } @@ -1737,21 +1878,27 @@ RegexNode.Notoneloop or RegexNode.Notonelazy or RegexNode.Notoneloopatomic && } EmitNode(node.Child(i), i + 1 < childCount ? node.Child(i + 1) : subsequent, emitLengthChecksIfRequired: emitLengthChecksIfRequired); + if (i < childCount - 1) + { + writer.WriteLine(); + } } } // Emits the code to handle a single-character match. void EmitSingleChar(RegexNode node, bool emitLengthCheck = true, string? offset = null, bool clauseOnly = false) { + Debug.Assert(node.IsOneFamily || node.IsNotoneFamily || node.IsSetFamily, $"Unexpected type: {node.Type}"); + // This only emits a single check, but it's called from the looping constructs in a loop // to generate the code for a single check, so we map those looping constructs to the // appropriate single check. - string expr = $"{sliceSpan}[{Sum(textSpanPos, offset)}]"; + string expr = $"{sliceSpan}[{Sum(sliceStaticPos, offset)}]"; if (node.IsSetFamily) { - expr = $"!{MatchCharacterClass(hasTextInfo, options, expr, node.Str!, IsCaseInsensitive(node), additionalDeclarations)}"; + expr = $"{MatchCharacterClass(hasTextInfo, options, expr, node.Str!, IsCaseInsensitive(node), negate: true, additionalDeclarations, ref requiredHelpers)}"; } else { @@ -1771,34 +1918,23 @@ void EmitSingleChar(RegexNode node, bool emitLengthCheck = true, string? offset } } - textSpanPos++; + sliceStaticPos++; } // Emits the code to handle a boundary check on a character. void EmitBoundary(RegexNode node) { - string call; - switch (node.Type) - { - case RegexNode.Boundary: - call = "!base.IsBoundary"; - break; + Debug.Assert(node.Type is RegexNode.Boundary or RegexNode.NonBoundary or RegexNode.ECMABoundary or RegexNode.NonECMABoundary, $"Unexpected type: {node.Type}"); - case RegexNode.NonBoundary: - call = "base.IsBoundary"; - break; - - case RegexNode.ECMABoundary: - call = "!base.IsECMABoundary"; - break; - - default: - Debug.Assert(node.Type == RegexNode.NonECMABoundary); - call = "base.IsECMABoundary"; - break; - } + string call = node.Type switch + { + RegexNode.Boundary => "!base.IsBoundary", + RegexNode.NonBoundary => "base.IsBoundary", + RegexNode.ECMABoundary => "!base.IsECMABoundary", + _ => "base.IsECMABoundary", + }; - using (EmitBlock(writer, $"if ({call}(runtextpos{AddTextSpanPos()}, base.runtextbeg, runtextend))")) + using (EmitBlock(writer, $"if ({call}(pos{(sliceStaticPos > 0 ? $" + {sliceStaticPos}" : "")}, base.runtextbeg, end))")) { writer.WriteLine($"goto {doneLabel};"); } @@ -1807,12 +1943,14 @@ void EmitBoundary(RegexNode node) // Emits the code to handle various anchors. void EmitAnchors(RegexNode node) { - Debug.Assert(textSpanPos >= 0); + Debug.Assert(node.Type is RegexNode.Beginning or RegexNode.Start or RegexNode.Bol or RegexNode.End or RegexNode.EndZ or RegexNode.Eol, $"Unexpected type: {node.Type}"); + + Debug.Assert(sliceStaticPos >= 0); switch (node.Type) { case RegexNode.Beginning: case RegexNode.Start: - if (textSpanPos > 0) + if (sliceStaticPos > 0) { // If we statically know we've already matched part of the regex, there's no way we're at the // beginning or start, as we've already progressed past it. @@ -1820,7 +1958,8 @@ void EmitAnchors(RegexNode node) } else { - using (EmitBlock(writer, node.Type == RegexNode.Beginning ? "if (runtextpos != runtextbeg)" : "if (runtextpos != runtextstart)")) + additionalDeclarations.Add(node.Type == RegexNode.Beginning ? "int beginning = base.runtextbeg;" : "int start = base.runtextstart;"); + using (EmitBlock(writer, node.Type == RegexNode.Beginning ? "if (pos != beginning)" : "if (pos != start)")) { writer.WriteLine($"goto {doneLabel};"); } @@ -1828,17 +1967,18 @@ void EmitAnchors(RegexNode node) break; case RegexNode.Bol: - if (textSpanPos > 0) + if (sliceStaticPos > 0) { - using (EmitBlock(writer, $"if ({sliceSpan}[{textSpanPos - 1}] != '\\n')")) + using (EmitBlock(writer, $"if ({sliceSpan}[{sliceStaticPos - 1}] != '\\n')")) { writer.WriteLine($"goto {doneLabel};"); } } else { - // We can't use our slice in this case, because we'd need to access slice[-1], so we access the runtextSpan field directly: - using (EmitBlock(writer, $"if (runtextpos > runtextbeg && runtextSpan[runtextpos - 1] != '\\n')")) + // We can't use our slice in this case, because we'd need to access slice[-1], so we access the inputSpan field directly: + additionalDeclarations.Add("int beginning = base.runtextbeg;"); + using (EmitBlock(writer, $"if (pos > beginning && inputSpan[pos - 1] != '\\n')")) { writer.WriteLine($"goto {doneLabel};"); } @@ -1846,14 +1986,14 @@ void EmitAnchors(RegexNode node) break; case RegexNode.End: - using (EmitBlock(writer, $"if ({sliceSpan}.Length > {textSpanPos})")) + using (EmitBlock(writer, $"if ({IsSliceLengthGreaterThanSliceStaticPos()})")) { writer.WriteLine($"goto {doneLabel};"); } break; case RegexNode.EndZ: - writer.WriteLine($"if ({textSpanPos} < {sliceSpan}.Length - 1 || ({textSpanPos} < {sliceSpan}.Length && {sliceSpan}[{textSpanPos}] != '\\n'))"); + writer.WriteLine($"if ({sliceSpan}.Length - 1 > {sliceStaticPos} || ({IsSliceLengthGreaterThanSliceStaticPos()} && {sliceSpan}[{sliceStaticPos}] != '\\n'))"); using (EmitBlock(writer, null)) { writer.WriteLine($"goto {doneLabel};"); @@ -1861,17 +2001,23 @@ void EmitAnchors(RegexNode node) break; case RegexNode.Eol: - using (EmitBlock(writer, $"if ({textSpanPos} < {sliceSpan}.Length && {sliceSpan}[{textSpanPos}] != '\\n')")) + using (EmitBlock(writer, $"if ({IsSliceLengthGreaterThanSliceStaticPos()} && {sliceSpan}[{sliceStaticPos}] != '\\n')")) { writer.WriteLine($"goto {doneLabel};"); } break; + + string IsSliceLengthGreaterThanSliceStaticPos() => + sliceStaticPos == 0 ? $"!{sliceSpan}.IsEmpty" : + $"{sliceSpan}.Length > {sliceStaticPos}"; } } // Emits the code to handle a multiple-character match. void EmitMultiChar(RegexNode node, bool emitLengthCheck = true) { + Debug.Assert(node.Type is RegexNode.Multi, $"Unexpected type: {node.Type}"); + bool caseInsensitive = IsCaseInsensitive(node); string str = node.Str!; @@ -1898,7 +2044,7 @@ void EmitMultiChar(RegexNode node, bool emitLengthCheck = true) bool emittedFirstCheck = false; if (emitLengthCheck) { - writer.Write($"(uint){sliceSpan}.Length < {textSpanPos + str.Length}"); + writer.Write($"(uint){sliceSpan}.Length < {sliceStaticPos + str.Length}"); emittedFirstCheck = true; } @@ -1917,18 +2063,18 @@ void EmitOr() while (byteStr.Length >= sizeof(ulong)) { EmitOr(); - string byteSpan = textSpanPos > 0 ? $"byteSpan.Slice({textSpanPos * sizeof(char)})" : "byteSpan"; + string byteSpan = sliceStaticPos > 0 ? $"byteSpan.Slice({sliceStaticPos * sizeof(char)})" : "byteSpan"; writer.Write($"global::System.Buffers.Binary.BinaryPrimitives.ReadUInt64LittleEndian({byteSpan}) != 0x{BinaryPrimitives.ReadUInt64LittleEndian(byteStr):X}ul"); - textSpanPos += sizeof(ulong) / sizeof(char); + sliceStaticPos += sizeof(ulong) / sizeof(char); byteStr = byteStr.Slice(sizeof(ulong)); } while (byteStr.Length >= sizeof(uint)) { EmitOr(); - string byteSpan = textSpanPos > 0 ? $"byteSpan.Slice({textSpanPos * sizeof(char)})" : "byteSpan"; + string byteSpan = sliceStaticPos > 0 ? $"byteSpan.Slice({sliceStaticPos * sizeof(char)})" : "byteSpan"; writer.Write($"global::System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian({byteSpan}) != 0x{BinaryPrimitives.ReadUInt32LittleEndian(byteStr):X}u"); - textSpanPos += sizeof(uint) / sizeof(char); + sliceStaticPos += sizeof(uint) / sizeof(char); byteStr = byteStr.Slice(sizeof(uint)); } } @@ -1937,8 +2083,8 @@ void EmitOr() for (int i = (str.Length * sizeof(char) - byteStr.Length) / sizeof(char); i < str.Length; i++) { EmitOr(); - writer.Write($"{ToLowerIfNeeded(hasTextInfo, options, $"{sliceSpan}[{textSpanPos}]", caseInsensitive)} != {Literal(str[i])}"); - textSpanPos++; + writer.Write($"{ToLowerIfNeeded(hasTextInfo, options, $"{sliceSpan}[{sliceStaticPos}]", caseInsensitive)} != {Literal(str[i])}"); + sliceStaticPos++; } writer.WriteLine(")"); @@ -1955,31 +2101,33 @@ void EmitOr() // character-by-character while respecting the culture. if (!caseInsensitive) { - string sourceSpan = textSpanPos > 0 ? $"{sliceSpan}.Slice({textSpanPos})" : sliceSpan; + string sourceSpan = sliceStaticPos > 0 ? $"{sliceSpan}.Slice({sliceStaticPos})" : sliceSpan; using (EmitBlock(writer, $"if (!global::System.MemoryExtensions.StartsWith({sourceSpan}, {Literal(node.Str)}))")) { writer.WriteLine($"goto {doneLabel};"); } - textSpanPos += node.Str.Length; + sliceStaticPos += node.Str.Length; } else { EmitSpanLengthCheck(str.Length); using (EmitBlock(writer, $"for (int i = 0; i < {Literal(node.Str)}.Length; i++)")) { - string textSpanIndex = textSpanPos > 0 ? $"i + {textSpanPos}" : "i"; + string textSpanIndex = sliceStaticPos > 0 ? $"i + {sliceStaticPos}" : "i"; using (EmitBlock(writer, $"if ({ToLower(hasTextInfo, options, $"{sliceSpan}[{textSpanIndex}]")} != {Literal(str)}[i])")) { writer.WriteLine($"goto {doneLabel};"); } } - textSpanPos += node.Str.Length; + sliceStaticPos += node.Str.Length; } } } void EmitSingleCharLoop(RegexNode node, RegexNode? subsequent = null, bool emitLengthChecksIfRequired = true) { + Debug.Assert(node.Type is RegexNode.Oneloop or RegexNode.Notoneloop or RegexNode.Setloop, $"Unexpected type: {node.Type}"); + // If this is actually a repeater, emit that instead; no backtracking necessary. if (node.M == node.N) { @@ -1994,40 +2142,27 @@ void EmitSingleCharLoop(RegexNode node, RegexNode? subsequent = null, bool emitL Debug.Assert(node.M < node.N); string backtrackingLabel = ReserveName("CharLoopBacktrack"); string endLoop = ReserveName("CharLoopEnd"); - string startingPos = ReserveName("charloop_starting_runtextpos"); - string endingPos = ReserveName("charloop_ending_runtextpos"); + string startingPos = ReserveName("charloop_starting_pos"); + string endingPos = ReserveName("charloop_ending_pos"); additionalDeclarations.Add($"int {startingPos} = 0, {endingPos} = 0;"); // We're about to enter a loop, so ensure our text position is 0. - TransferTextSpanPosToRunTextPos(); + TransferSliceStaticPosToPos(); // Grab the current position, then emit the loop as atomic, and then // grab the current position again. Even though we emit the loop without // knowledge of backtracking, we can layer it on top by just walking back // through the individual characters (a benefit of the loop matching exactly // one character per iteration, no possible captures within the loop, etc.) - writer.WriteLine($"{startingPos} = runtextpos;"); + writer.WriteLine($"{startingPos} = pos;"); writer.WriteLine(); EmitSingleCharAtomicLoop(node); writer.WriteLine(); - TransferTextSpanPosToRunTextPos(); - writer.WriteLine($"{endingPos} = runtextpos;"); - - string? crawlPos = null; - if (expressionHasCaptures) - { - crawlPos = ReserveName("charloop_crawlpos"); - additionalDeclarations.Add($"int {crawlPos} = 0;"); - writer.WriteLine($"{crawlPos} = base.Crawlpos();"); - } - - if (node.M > 0) - { - writer.WriteLine($"{startingPos} += {node.M};"); - } - + TransferSliceStaticPosToPos(); + writer.WriteLine($"{endingPos} = pos;"); + EmitAdd(writer, startingPos, node.M); writer.WriteLine($"goto {endLoop};"); writer.WriteLine(); @@ -2035,13 +2170,15 @@ void EmitSingleCharLoop(RegexNode node, RegexNode? subsequent = null, bool emitL // point we decrement the matched count as long as it's above the minimum // required, and try again by flowing to everything that comes after this. MarkLabel(backtrackingLabel, emitSemicolon: false); - if (crawlPos is not null) + if (expressionHasCaptures) + { + EmitUncaptureUntil(StackPop()); + EmitStackPop(endingPos, startingPos); + } + else { - writer.WriteLine($"{crawlPos} = {RunstackPop()};"); - EmitUncaptureUntil(crawlPos); + EmitStackPop(endingPos, startingPos); } - writer.WriteLine($"{endingPos} = {RunstackPop()};"); - writer.WriteLine($"{startingPos} = {RunstackPop()};"); string originalDoneLabel = doneLabel; using (EmitBlock(writer, $"if ({startingPos} >= {endingPos})")) @@ -2053,34 +2190,32 @@ void EmitSingleCharLoop(RegexNode node, RegexNode? subsequent = null, bool emitL if (subsequent?.FindStartingCharacter() is char subsequentCharacter) { writer.WriteLine(); - writer.WriteLine($"{endingPos} = global::System.MemoryExtensions.LastIndexOf(runtextSpan.Slice({startingPos}, {endingPos} - {startingPos}), {Literal(subsequentCharacter)});"); + writer.WriteLine($"{endingPos} = global::System.MemoryExtensions.LastIndexOf(inputSpan.Slice({startingPos}, {endingPos} - {startingPos}), {Literal(subsequentCharacter)});"); using (EmitBlock(writer, $"if ({endingPos} < 0)")) { writer.WriteLine($"goto {originalDoneLabel};"); } writer.WriteLine($"{endingPos} += {startingPos};"); - writer.WriteLine($"runtextpos = {endingPos};"); + writer.WriteLine($"pos = {endingPos};"); } else { - writer.WriteLine($"runtextpos = --{endingPos};"); + writer.WriteLine($"pos = --{endingPos};"); } - LoadTextSpanLocal(writer); + SliceInputSpan(writer); writer.WriteLine(); MarkLabel(endLoop); - EmitRunstackResizeIfNeeded(expressionHasCaptures ? 3 : 2); - writer.WriteLine($"{RunstackPush()} = {startingPos};"); - writer.WriteLine($"{RunstackPush()} = {endingPos};"); - if (crawlPos is not null) - { - writer.WriteLine($"{RunstackPush()} = {crawlPos};"); - } + EmitStackPush(expressionHasCaptures ? + new[] { startingPos, endingPos, "base.Crawlpos()" } : + new[] { startingPos, endingPos }); } void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) { + Debug.Assert(node.Type is RegexNode.Onelazy or RegexNode.Notonelazy or RegexNode.Setlazy, $"Unexpected type: {node.Type}"); + // Emit the min iterations as a repeater. Any failures here don't necessitate backtracking, // as the lazy itself failed to match, and there's no backtracking possible by the individual // characters/iterations themselves. @@ -2102,7 +2237,7 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) // to try to match, and only matching another character if the subsequent expression fails to match. // We're about to enter a loop, so ensure our text position is 0. - TransferTextSpanPosToRunTextPos(); + TransferSliceStaticPosToPos(); // If the loop isn't unbounded, track the number of iterations and the max number to allow. string? iterationCount = null; @@ -2112,23 +2247,22 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) maxIterations = $"{node.N - node.M}"; iterationCount = ReserveName("lazyloop_iteration"); - additionalDeclarations.Add($"int {iterationCount} = 0;"); - writer.WriteLine($"{iterationCount} = 0;"); + writer.WriteLine($"int {iterationCount} = 0;"); } // Track the current crawl position. Upon backtracking, we'll unwind any captures beyond this point. - string? crawlPos = null; + string? capturePos = null; if (expressionHasCaptures) { - crawlPos = ReserveName("lazyloop_crawlpos"); - additionalDeclarations.Add($"int {crawlPos} = 0;"); + capturePos = ReserveName("lazyloop_capturepos"); + additionalDeclarations.Add($"int {capturePos} = 0;"); } - // Track the current runtextpos. Each time we backtrack, we'll reset to the stored position, which + // Track the current pos. Each time we backtrack, we'll reset to the stored position, which // is also incremented each time we match another character in the loop. - string startingRunTextPos = ReserveName("lazyloop_runtextpos"); - additionalDeclarations.Add($"int {startingRunTextPos} = 0;"); - writer.WriteLine($"{startingRunTextPos} = runtextpos;"); + string startingPos = ReserveName("lazyloop_pos"); + additionalDeclarations.Add($"int {startingPos} = 0;"); + writer.WriteLine($"{startingPos} = pos;"); // Skip the backtracking section for the initial subsequent matching. We've already matched the // minimum number of iterations, which means we can successfully match with zero additional iterations. @@ -2142,9 +2276,9 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) // Uncapture any captures if the expression has any. It's possible the captures it has // are before this node, in which case this is wasted effort, but still functionally correct. - if (crawlPos is not null) + if (capturePos is not null) { - EmitUncaptureUntil(crawlPos); + EmitUncaptureUntil(capturePos); } // If there's a max number of iterations, see if we've exceeded the maximum number of characters @@ -2158,14 +2292,14 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) writer.WriteLine($"{iterationCount}++;"); } - // Now match the next item in the lazy loop. We need to reset the runtextpos to the position + // Now match the next item in the lazy loop. We need to reset the pos to the position // just after the last character in this loop was matched, and we need to store the resulting position // for the next time we backtrack. - writer.WriteLine($"runtextpos = {startingRunTextPos};"); - LoadTextSpanLocal(writer); + writer.WriteLine($"pos = {startingPos};"); + SliceInputSpan(writer); EmitSingleChar(node); - TransferTextSpanPosToRunTextPos(); - writer.WriteLine($"{startingRunTextPos} = runtextpos;"); + TransferSliceStaticPosToPos(); + writer.WriteLine($"{startingPos} = pos;"); // Update the done label for everything that comes after this node. This is done after we emit the single char // matching, as that failing indicates the loop itself has failed to match. @@ -2174,9 +2308,9 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) writer.WriteLine(); MarkLabel(endLoopLabel); - if (crawlPos is not null) + if (capturePos is not null) { - writer.WriteLine($"{crawlPos} = base.Crawlpos();"); + writer.WriteLine($"{capturePos} = base.Crawlpos();"); } if (node.IsInLoop()) @@ -2184,16 +2318,17 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) writer.WriteLine(); // Store the capture's state - EmitRunstackResizeIfNeeded(3); - writer.WriteLine($"{RunstackPush()} = {startingRunTextPos};"); - if (crawlPos is not null) + var toPushPop = new List(3) { startingPos }; + if (capturePos is not null) { - writer.WriteLine($"{RunstackPush()} = {crawlPos};"); + toPushPop.Add(capturePos); } if (iterationCount is not null) { - writer.WriteLine($"{RunstackPush()} = {iterationCount};"); + toPushPop.Add(iterationCount); } + string[] toPushPopArray = toPushPop.ToArray(); + EmitStackPush(toPushPopArray); // Skip past the backtracking section string end = ReserveName("SkipBacktrack"); @@ -2203,15 +2338,9 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) // Emit a backtracking section that restores the capture's state and then jumps to the previous done label string backtrack = ReserveName("CharLazyBacktrack"); MarkLabel(backtrack); - if (iterationCount is not null) - { - writer.WriteLine($"{iterationCount} = {RunstackPop()};"); - } - if (crawlPos is not null) - { - writer.WriteLine($"{crawlPos} = {RunstackPop()};"); - } - writer.WriteLine($"{startingRunTextPos} = {RunstackPop()};"); + + Array.Reverse(toPushPopArray); + EmitStackPop(toPushPopArray); writer.WriteLine($"goto {doneLabel};"); writer.WriteLine(); @@ -2246,24 +2375,22 @@ void EmitLazy(RegexNode node) EmitNode(node.Child(0)); return; } + + writer.WriteLine(); } - // We might loop any number of times. In order to ensure this loop and subsequent code sees textSpanPos + // We might loop any number of times. In order to ensure this loop and subsequent code sees sliceStaticPos // the same regardless, we always need it to contain the same value, and the easiest such value is 0. - // So, we transfer textSpanPos to runtextpos, and ensure that any path out of here has textSpanPos as 0. - TransferTextSpanPosToRunTextPos(); + // So, we transfer sliceStaticPos to pos, and ensure that any path out of here has sliceStaticPos as 0. + TransferSliceStaticPosToPos(); - string startingRunTextPos = ReserveName("lazyloop_starting_runtextpos"); + string startingPos = ReserveName("lazyloop_starting_pos"); string iterationCount = ReserveName("lazyloop_iteration"); string sawEmpty = ReserveName("lazyLoopEmptySeen"); string body = ReserveName("LazyLoopBody"); string endLoop = ReserveName("LazyLoopEnd"); - additionalDeclarations.Add($"int {iterationCount} = 0, {startingRunTextPos} = 0, {sawEmpty} = 0;"); - writer.WriteLine($"{iterationCount} = 0;"); - writer.WriteLine($"{startingRunTextPos} = runtextpos;"); - writer.WriteLine($"{sawEmpty} = 0;"); - writer.WriteLine(); + writer.WriteLine($"int {iterationCount} = 0, {startingPos} = pos, {sawEmpty} = 0;"); // If the min count is 0, start out by jumping right to what's after the loop. Backtracking // will then bring us back in to do further iterations. @@ -2271,29 +2398,26 @@ void EmitLazy(RegexNode node) { writer.WriteLine($"goto {endLoop};"); } + writer.WriteLine(); // Iteration body MarkLabel(body, emitSemicolon: false); EmitTimeoutCheck(writer, hasTimeout); - // We need to store the starting runtextpos and crawl position so that it may + // We need to store the starting pos and crawl position so that it may // be backtracked through later. This needs to be the starting position from - // the iteration we're leaving, so it's pushed before updating it to runtextpos. - EmitRunstackResizeIfNeeded(3); - if (expressionHasCaptures) - { - writer.WriteLine($"{RunstackPush()} = base.Crawlpos();"); - } - writer.WriteLine($"{RunstackPush()} = {startingRunTextPos};"); - writer.WriteLine($"{RunstackPush()} = runtextpos;"); - writer.WriteLine($"{RunstackPush()} = {sawEmpty};"); + // the iteration we're leaving, so it's pushed before updating it to pos. + EmitStackPush(expressionHasCaptures ? + new[] { "base.Crawlpos()", startingPos, "pos", sawEmpty } : + new[] { startingPos, "pos", sawEmpty }); + writer.WriteLine(); - // Save off some state. We need to store the current runtextpos so we can compare it against - // runtextpos after the iteration, in order to determine whether the iteration was empty. Empty + // Save off some state. We need to store the current pos so we can compare it against + // pos after the iteration, in order to determine whether the iteration was empty. Empty // iterations are allowed as part of min matches, but once we've met the min quote, empty matches // are considered match failures. - writer.WriteLine($"{startingRunTextPos} = runtextpos;"); + writer.WriteLine($"{startingPos} = pos;"); // Proactively increase the number of iterations. We do this prior to the match rather than once // we know it's successful, because we need to decrement it as part of a failed match when @@ -2309,9 +2433,10 @@ void EmitLazy(RegexNode node) doneLabel = iterationFailedLabel; // Finally, emit the child. - Debug.Assert(textSpanPos == 0); + Debug.Assert(sliceStaticPos == 0); EmitNode(node.Child(0)); - TransferTextSpanPosToRunTextPos(); // ensure textSpanPos remains 0 + writer.WriteLine(); + TransferSliceStaticPosToPos(); // ensure sliceStaticPos remains 0 if (doneLabel == iterationFailedLabel) { doneLabel = originalDoneLabel; @@ -2320,7 +2445,7 @@ void EmitLazy(RegexNode node) // Loop condition. Continue iterating if we've not yet reached the minimum. if (minIterations > 0) { - using (EmitBlock(writer, $"if ({iterationCount} < {minIterations})")) + using (EmitBlock(writer, $"if ({CountIsLessThan(iterationCount, minIterations)})")) { writer.WriteLine($"goto {body};"); } @@ -2329,7 +2454,7 @@ void EmitLazy(RegexNode node) // If the last iteration was empty, we need to prevent further iteration from this point // unless we backtrack out of this iteration. We can do that easily just by pretending // we reached the max iteration count. - using (EmitBlock(writer, $"if (runtextpos == {startingRunTextPos})")) + using (EmitBlock(writer, $"if (pos == {startingPos})")) { writer.WriteLine($"{sawEmpty} = 1;"); } @@ -2339,23 +2464,19 @@ void EmitLazy(RegexNode node) writer.WriteLine(); // Now handle what happens when an iteration fails. We need to reset state to what it was before just that iteration - // started. That includes resetting runtextpos and clearing out any captures from that iteration. + // started. That includes resetting pos and clearing out any captures from that iteration. MarkLabel(iterationFailedLabel, emitSemicolon: false); writer.WriteLine($"{iterationCount}--;"); using (EmitBlock(writer, $"if ({iterationCount} < 0)")) { writer.WriteLine($"goto {originalDoneLabel};"); } - writer.WriteLine($"{sawEmpty} = {RunstackPop()};"); - writer.WriteLine($"runtextpos = {RunstackPop()};"); - writer.WriteLine($"{startingRunTextPos} = {RunstackPop()};"); + EmitStackPop(sawEmpty, "pos", startingPos); if (expressionHasCaptures) { - string poppedCrawlPos = ReserveName("lazyloop_crawlpos"); - writer.WriteLine($"int {poppedCrawlPos} = {RunstackPop()};"); - EmitUncaptureUntil(poppedCrawlPos); + EmitUncaptureUntil(StackPop()); } - LoadTextSpanLocal(writer); + SliceInputSpan(writer); if (doneLabel == originalDoneLabel) { writer.WriteLine($"goto {originalDoneLabel};"); @@ -2375,10 +2496,8 @@ void EmitLazy(RegexNode node) if (!isAtomic) { // Store the capture's state and skip the backtracking section - EmitRunstackResizeIfNeeded(3); - writer.WriteLine($"{RunstackPush()} = {startingRunTextPos};"); - writer.WriteLine($"{RunstackPush()} = {iterationCount};"); - writer.WriteLine($"{RunstackPush()} = {sawEmpty};"); + EmitStackPush(startingPos, iterationCount, sawEmpty); + string skipBacktrack = ReserveName("SkipBacktrack"); writer.WriteLine($"goto {skipBacktrack};"); writer.WriteLine(); @@ -2387,9 +2506,7 @@ void EmitLazy(RegexNode node) string backtrack = ReserveName($"LazyLoopBacktrack"); MarkLabel(backtrack); - writer.WriteLine($"{sawEmpty} = {RunstackPop()};"); - writer.WriteLine($"{iterationCount} = {RunstackPop()};"); - writer.WriteLine($"{startingRunTextPos} = {RunstackPop()};"); + EmitStackPop(sawEmpty, iterationCount, startingPos); if (maxIterations == int.MaxValue) { @@ -2400,7 +2517,7 @@ void EmitLazy(RegexNode node) } else { - using (EmitBlock(writer, $"if ({iterationCount} < {maxIterations} && {sawEmpty} == 0)")) + using (EmitBlock(writer, $"if ({CountIsLessThan(iterationCount, maxIterations)} && {sawEmpty} == 0)")) { writer.WriteLine($"goto {body};"); } @@ -2418,6 +2535,8 @@ void EmitLazy(RegexNode node) // RegexNode.M is used for the number of iterations; RegexNode.N is ignored. void EmitSingleCharFixedRepeater(RegexNode node, bool emitLengthCheck = true) { + Debug.Assert(node.IsOneFamily || node.IsNotoneFamily || node.IsSetFamily, $"Unexpected type: {node.Type}"); + int iterations = node.M; if (iterations == 0) { @@ -2427,9 +2546,9 @@ void EmitSingleCharFixedRepeater(RegexNode node, bool emitLengthCheck = true) if (iterations <= MaxUnrollSize) { - // if ((uint)(textSpanPos + iterations - 1) >= (uint)slice.Length || - // slice[textSpanPos] != c1 || - // slice[textSpanPos + 1] != c2 || + // if ((uint)(sliceStaticPos + iterations - 1) >= (uint)slice.Length || + // slice[sliceStaticPos] != c1 || + // slice[sliceStaticPos + 1] != c2 || // ...) // { // goto doneLabel; @@ -2455,34 +2574,35 @@ void EmitSingleCharFixedRepeater(RegexNode node, bool emitLengthCheck = true) } else { - // if ((uint)(textSpanPos + iterations - 1) >= (uint)slice.Length) goto doneLabel; + // if ((uint)(sliceStaticPos + iterations - 1) >= (uint)slice.Length) goto doneLabel; if (emitLengthCheck) { EmitSpanLengthCheck(iterations); } string repeaterSpan = "repeaterSlice"; // As this repeater doesn't wrap arbitrary node emits, this shouldn't conflict with anything - writer.WriteLine($"global::System.ReadOnlySpan {repeaterSpan} = {sliceSpan}.Slice({textSpanPos}, {iterations});"); - string i = ReserveName("charrepeater_iteration"); - using (EmitBlock(writer, $"for (int {i} = 0; {i} < {repeaterSpan}.Length; {i}++)")) + writer.WriteLine($"global::System.ReadOnlySpan {repeaterSpan} = {sliceSpan}.Slice({sliceStaticPos}, {iterations});"); + using (EmitBlock(writer, $"for (int i = 0; i < {repeaterSpan}.Length; i++)")) { EmitTimeoutCheck(writer, hasTimeout); string tmpTextSpanLocal = sliceSpan; // we want EmitSingleChar to refer to this temporary - int tmpTextSpanPos = textSpanPos; + int tmpSliceStaticPos = sliceStaticPos; sliceSpan = repeaterSpan; - textSpanPos = 0; - EmitSingleChar(node, emitLengthCheck: false, offset: i); + sliceStaticPos = 0; + EmitSingleChar(node, emitLengthCheck: false, offset: "i"); sliceSpan = tmpTextSpanLocal; - textSpanPos = tmpTextSpanPos; + sliceStaticPos = tmpSliceStaticPos; } - textSpanPos += iterations; + sliceStaticPos += iterations; } } // Emits the code to handle a non-backtracking, variable-length loop around a single character comparison. void EmitSingleCharAtomicLoop(RegexNode node, bool emitLengthChecksIfRequired = true) { + Debug.Assert(node.Type is RegexNode.Oneloop or RegexNode.Oneloopatomic or RegexNode.Notoneloop or RegexNode.Notoneloopatomic or RegexNode.Setloop or RegexNode.Setloopatomic, $"Unexpected type: {node.Type}"); + // If this is actually a repeater, emit that instead. if (node.M == node.N) { @@ -2516,18 +2636,19 @@ void EmitSingleCharAtomicLoop(RegexNode node, bool emitLengthChecksIfRequired = // handle the unbounded case. writer.Write($"int {iterationLocal} = global::System.MemoryExtensions.IndexOf({sliceSpan}"); - if (textSpanPos > 0) + if (sliceStaticPos > 0) { - writer.Write($".Slice({textSpanPos})"); + writer.Write($".Slice({sliceStaticPos})"); } writer.WriteLine($", {Literal(node.Ch)});"); - using (EmitBlock(writer, $"if ({iterationLocal} == -1)")) + using (EmitBlock(writer, $"if ({iterationLocal} < 0)")) { - writer.WriteLine(textSpanPos > 0 ? - $"{iterationLocal} = {sliceSpan}.Length - {textSpanPos};" : + writer.WriteLine(sliceStaticPos > 0 ? + $"{iterationLocal} = {sliceSpan}.Length - {sliceStaticPos};" : $"{iterationLocal} = {sliceSpan}.Length;"); } + writer.WriteLine(); } else if (node.IsSetFamily && maxIterations == int.MaxValue && @@ -2541,9 +2662,9 @@ void EmitSingleCharAtomicLoop(RegexNode node, bool emitLengthChecksIfRequired = Debug.Assert(numSetChars > 1); writer.Write($"int {iterationLocal} = global::System.MemoryExtensions.IndexOfAny({sliceSpan}"); - if (textSpanPos != 0) + if (sliceStaticPos != 0) { - writer.Write($".Slice({textSpanPos})"); + writer.Write($".Slice({sliceStaticPos})"); } writer.WriteLine(numSetChars switch { @@ -2551,21 +2672,22 @@ void EmitSingleCharAtomicLoop(RegexNode node, bool emitLengthChecksIfRequired = 3 => $", {Literal(setChars[0])}, {Literal(setChars[1])}, {Literal(setChars[2])});", _ => $", {Literal(setChars.Slice(0, numSetChars).ToString())});", }); - using (EmitBlock(writer, $"if ({iterationLocal} == -1)")) + using (EmitBlock(writer, $"if ({iterationLocal} < 0)")) { - writer.WriteLine(textSpanPos > 0 ? - $"{iterationLocal} = {sliceSpan}.Length - {textSpanPos};" : + writer.WriteLine(sliceStaticPos > 0 ? + $"{iterationLocal} = {sliceSpan}.Length - {sliceStaticPos};" : $"{iterationLocal} = {sliceSpan}.Length;"); } + writer.WriteLine(); } else if (node.IsSetFamily && maxIterations == int.MaxValue && node.Str == RegexCharClass.AnyClass) { // .* was used with RegexOptions.Singleline, which means it'll consume everything. Just jump to the end. // The unbounded constraint is the same as in the Notone case above, done purely for simplicity. - // int i = runtextend - runtextpos; - TransferTextSpanPosToRunTextPos(); - writer.WriteLine($"int {iterationLocal} = runtextend - runtextpos;"); + // int i = end - pos; + TransferSliceStaticPosToPos(); + writer.WriteLine($"int {iterationLocal} = end - pos;"); } else { @@ -2574,7 +2696,7 @@ void EmitSingleCharAtomicLoop(RegexNode node, bool emitLengthChecksIfRequired = string expr = $"{sliceSpan}[{iterationLocal}]"; if (node.IsSetFamily) { - expr = MatchCharacterClass(hasTextInfo, options, expr, node.Str!, IsCaseInsensitive(node), additionalDeclarations); + expr = MatchCharacterClass(hasTextInfo, options, expr, node.Str!, IsCaseInsensitive(node), negate: false, additionalDeclarations, ref requiredHelpers); } else { @@ -2584,48 +2706,51 @@ void EmitSingleCharAtomicLoop(RegexNode node, bool emitLengthChecksIfRequired = if (minIterations != 0 || maxIterations != int.MaxValue) { - // For any loops other than * loops, transfer text pos to runtextpos in + // For any loops other than * loops, transfer text pos to pos in // order to zero it out to be able to use the single iteration variable // for both iteration count and indexer. - TransferTextSpanPosToRunTextPos(); + TransferSliceStaticPosToPos(); } - writer.WriteLine($"int {iterationLocal} = {textSpanPos};"); - textSpanPos = 0; + writer.WriteLine($"int {iterationLocal} = {sliceStaticPos};"); + sliceStaticPos = 0; - string maxClause = maxIterations != int.MaxValue ? $"{iterationLocal} < {maxIterations} && " : ""; + string maxClause = maxIterations != int.MaxValue ? $"{CountIsLessThan(iterationLocal, maxIterations)} && " : ""; using (EmitBlock(writer, $"while ({maxClause}(uint){iterationLocal} < (uint){sliceSpan}.Length && {expr})")) { EmitTimeoutCheck(writer, hasTimeout); writer.WriteLine($"{iterationLocal}++;"); } + writer.WriteLine(); } // Check to ensure we've found at least min iterations. if (minIterations > 0) { - using (EmitBlock(writer, $"if ({iterationLocal} < {minIterations})")) + using (EmitBlock(writer, $"if ({CountIsLessThan(iterationLocal, minIterations)})")) { writer.WriteLine($"goto {doneLabel};"); } + writer.WriteLine(); } // Now that we've completed our optional iterations, advance the text span - // and runtextpos by the number of iterations completed. + // and pos by the number of iterations completed. writer.WriteLine($"{sliceSpan} = {sliceSpan}.Slice({iterationLocal});"); - writer.WriteLine($"runtextpos += {iterationLocal};"); + writer.WriteLine($"pos += {iterationLocal};"); } // Emits the code to handle a non-backtracking optional zero-or-one loop. void EmitAtomicSingleCharZeroOrOne(RegexNode node) { + Debug.Assert(node.Type is RegexNode.Oneloop or RegexNode.Oneloopatomic or RegexNode.Notoneloop or RegexNode.Notoneloopatomic or RegexNode.Setloop or RegexNode.Setloopatomic, $"Unexpected type: {node.Type}"); Debug.Assert(node.M == 0 && node.N == 1); - string expr = $"{sliceSpan}[{textSpanPos}]"; + string expr = $"{sliceSpan}[{sliceStaticPos}]"; if (node.IsSetFamily) { - expr = MatchCharacterClass(hasTextInfo, options, expr, node.Str!, IsCaseInsensitive(node), additionalDeclarations); + expr = MatchCharacterClass(hasTextInfo, options, expr, node.Str!, IsCaseInsensitive(node), negate: false, additionalDeclarations, ref requiredHelpers); } else { @@ -2633,10 +2758,11 @@ void EmitAtomicSingleCharZeroOrOne(RegexNode node) expr = $"{expr} {(node.IsOneFamily ? "==" : "!=")} {Literal(node.Ch)}"; } - using (EmitBlock(writer, $"if ((uint){textSpanPos} < (uint){sliceSpan}.Length && {expr})")) + string spaceAvailable = sliceStaticPos != 0 ? $"(uint){sliceSpan}.Length > (uint){sliceStaticPos}" : $"!{sliceSpan}.IsEmpty"; + using (EmitBlock(writer, $"if ({spaceAvailable} && {expr})")) { writer.WriteLine($"{sliceSpan} = {sliceSpan}.Slice(1);"); - writer.WriteLine($"runtextpos++;"); + writer.WriteLine($"pos++;"); } } @@ -2649,44 +2775,40 @@ void EmitLoop(RegexNode node) int maxIterations = node.N; bool isAtomic = node.IsAtomicByParent(); - // We might loop any number of times. In order to ensure this loop and subsequent code sees textSpanPos + // We might loop any number of times. In order to ensure this loop and subsequent code sees sliceStaticPos // the same regardless, we always need it to contain the same value, and the easiest such value is 0. - // So, we transfer textSpanPos to runtextpos, and ensure that any path out of here has textSpanPos as 0. - TransferTextSpanPosToRunTextPos(); + // So, we transfer sliceStaticPos to pos, and ensure that any path out of here has sliceStaticPos as 0. + TransferSliceStaticPosToPos(); string originalDoneLabel = doneLabel; - string startingRunTextPos = ReserveName("loop_starting_runtextpos"); + string startingPos = ReserveName("loop_starting_pos"); string iterationCount = ReserveName("loop_iteration"); string body = ReserveName("LoopBody"); string endLoop = ReserveName("LoopEnd"); - additionalDeclarations.Add($"int {iterationCount} = 0, {startingRunTextPos} = 0;"); + additionalDeclarations.Add($"int {iterationCount} = 0, {startingPos} = 0;"); writer.WriteLine($"{iterationCount} = 0;"); - writer.WriteLine($"{startingRunTextPos} = runtextpos;"); + writer.WriteLine($"{startingPos} = pos;"); writer.WriteLine(); // Iteration body MarkLabel(body, emitSemicolon: false); EmitTimeoutCheck(writer, hasTimeout); - // We need to store the starting runtextpos and crawl position so that it may + // We need to store the starting pos and crawl position so that it may // be backtracked through later. This needs to be the starting position from - // the iteration we're leaving, so it's pushed before updating it to runtextpos. - EmitRunstackResizeIfNeeded(3); - if (expressionHasCaptures) - { - writer.WriteLine($"{RunstackPush()} = base.Crawlpos();"); - } - writer.WriteLine($"{RunstackPush()} = {startingRunTextPos};"); - writer.WriteLine($"{RunstackPush()} = runtextpos;"); + // the iteration we're leaving, so it's pushed before updating it to pos. + EmitStackPush(expressionHasCaptures ? + new[] { "base.Crawlpos()", startingPos, "pos" } : + new[] { startingPos, "pos" }); writer.WriteLine(); - // Save off some state. We need to store the current runtextpos so we can compare it against - // runtextpos after the iteration, in order to determine whether the iteration was empty. Empty + // Save off some state. We need to store the current pos so we can compare it against + // pos after the iteration, in order to determine whether the iteration was empty. Empty // iterations are allowed as part of min matches, but once we've met the min quote, empty matches // are considered match failures. - writer.WriteLine($"{startingRunTextPos} = runtextpos;"); + writer.WriteLine($"{startingPos} = pos;"); // Proactively increase the number of iterations. We do this prior to the match rather than once // we know it's successful, because we need to decrement it as part of a failed match when @@ -2703,19 +2825,20 @@ void EmitLoop(RegexNode node) doneLabel = iterationFailedLabel; // Finally, emit the child. - Debug.Assert(textSpanPos == 0); + Debug.Assert(sliceStaticPos == 0); EmitNode(node.Child(0)); - TransferTextSpanPosToRunTextPos(); // ensure textSpanPos remains 0 + writer.WriteLine(); + TransferSliceStaticPosToPos(); // ensure sliceStaticPos remains 0 bool childBacktracks = doneLabel != iterationFailedLabel; // Loop condition. Continue iterating greedily if we've not yet reached the maximum. We also need to stop // iterating if the iteration matched empty and we already hit the minimum number of iterations. using (EmitBlock(writer, (minIterations > 0, maxIterations == int.MaxValue) switch { - (true, true) => $"if (runtextpos != {startingRunTextPos} || {iterationCount} < {minIterations})", - (true, false) => $"if ((runtextpos != {startingRunTextPos} || {iterationCount} < {minIterations}) && {iterationCount} < {maxIterations})", - (false, true) => $"if (runtextpos != {startingRunTextPos})", - (false, false) => $"if (runtextpos != {startingRunTextPos} && {iterationCount} < {maxIterations})", + (true, true) => $"if (pos != {startingPos} || {CountIsLessThan(iterationCount, minIterations)})", + (true, false) => $"if ((pos != {startingPos} || {CountIsLessThan(iterationCount, minIterations)}) && {CountIsLessThan(iterationCount, maxIterations)})", + (false, true) => $"if (pos != {startingPos})", + (false, false) => $"if (pos != {startingPos} && {CountIsLessThan(iterationCount, maxIterations)})", })) { writer.WriteLine($"goto {body};"); @@ -2727,22 +2850,19 @@ void EmitLoop(RegexNode node) // Now handle what happens when an iteration fails, which could be an initial failure or it // could be while backtracking. We need to reset state to what it was before just that iteration - // started. That includes resetting runtextpos and clearing out any captures from that iteration. + // started. That includes resetting pos and clearing out any captures from that iteration. MarkLabel(iterationFailedLabel, emitSemicolon: false); writer.WriteLine($"{iterationCount}--;"); using (EmitBlock(writer, $"if ({iterationCount} < 0)")) { writer.WriteLine($"goto {originalDoneLabel};"); } - writer.WriteLine($"runtextpos = {RunstackPop()};"); - writer.WriteLine($"{startingRunTextPos} = {RunstackPop()};"); + EmitStackPop("pos", startingPos); if (expressionHasCaptures) { - string poppedCrawlPos = ReserveName("loop_crawlpos"); - writer.WriteLine($"int {poppedCrawlPos} = {RunstackPop()};"); - EmitUncaptureUntil(poppedCrawlPos); + EmitUncaptureUntil(StackPop()); } - LoadTextSpanLocal(writer); + SliceInputSpan(writer); if (minIterations > 0) { @@ -2751,7 +2871,7 @@ void EmitLoop(RegexNode node) writer.WriteLine($"goto {originalDoneLabel};"); } - using (EmitBlock(writer, $"if ({iterationCount} < {minIterations})")) + using (EmitBlock(writer, $"if ({CountIsLessThan(iterationCount, minIterations)})")) { writer.WriteLine($"goto {(childBacktracks ? doneLabel : originalDoneLabel)};"); } @@ -2786,9 +2906,7 @@ void EmitLoop(RegexNode node) writer.WriteLine(); // Store the capture's state - EmitRunstackResizeIfNeeded(3); - writer.WriteLine($"{RunstackPush()} = {startingRunTextPos};"); - writer.WriteLine($"{RunstackPush()} = {iterationCount};"); + EmitStackPush(startingPos, iterationCount); // Skip past the backtracking section string end = ReserveName("SkipBacktrack"); @@ -2798,8 +2916,7 @@ void EmitLoop(RegexNode node) // Emit a backtracking section that restores the capture's state and then jumps to the previous done label string backtrack = ReserveName("LoopBacktrack"); MarkLabel(backtrack); - writer.WriteLine($"{iterationCount} = {RunstackPop()};"); - writer.WriteLine($"{startingRunTextPos} = {RunstackPop()};"); + EmitStackPop(iterationCount, startingPos); writer.WriteLine($"goto {doneLabel};"); writer.WriteLine(); @@ -2810,17 +2927,119 @@ void EmitLoop(RegexNode node) } } - void EmitRunstackResizeIfNeeded(int count) + // Gets a comparison for whether the value is less than the upper bound. + static string CountIsLessThan(string value, int exclusiveUpper) => + exclusiveUpper == 1 ? $"{value} == 0" : $"{value} < {exclusiveUpper}"; + + // Emits code to unwind the capture stack until the crawl position specified in the provided local. + void EmitUncaptureUntil(string capturepos) { - string subCount = count > 1 ? $" - {count - 1}" : ""; - using (EmitBlock(writer, $"if (runstackpos >= base.runstack!.Length{subCount})")) + string name = "UncaptureUntil"; + + if (!additionalLocalFunctions.ContainsKey(name)) + { + var lines = new string[9]; + lines[0] = "// Undo captures until we reach the specified capture position."; + lines[1] = "[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"; + lines[2] = $"void {name}(int capturepos)"; + lines[3] = "{"; + lines[4] = " while (base.Crawlpos() > capturepos)"; + lines[5] = " {"; + lines[6] = " base.Uncapture();"; + lines[7] = " }"; + lines[8] = "}"; + + additionalLocalFunctions.Add(name, lines); + } + + writer.WriteLine($"{name}({capturepos});"); + } + + /// Pushes values on to the backtracking stack. + void EmitStackPush(params string[] args) + { + Debug.Assert(args.Length is >= 1); + string function = $"StackPush{args.Length}"; + + additionalDeclarations.Add("int stackpos = 0;"); + + if (!additionalLocalFunctions.ContainsKey(function)) + { + var lines = new string[24 + args.Length]; + lines[0] = $"// Push {args.Length} value{(args.Length == 1 ? "" : "s")} onto the backtracking stack."; + lines[1] = $"[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"; + lines[2] = $"static void {function}(ref int[] stack, ref int pos{FormatN(", int arg{0}", args.Length)})"; + lines[3] = $"{{"; + lines[4] = $" // If there's space available for {(args.Length > 1 ? $"all {args.Length} values, store them" : "the value, store it")}."; + lines[5] = $" int[] s = stack;"; + lines[6] = $" int p = pos;"; + lines[7] = $" if ((uint){(args.Length > 1 ? $"(p + {args.Length - 1})" : "p")} < (uint)s.Length)"; + lines[8] = $" {{"; + for (int i = 0; i < args.Length; i++) + { + lines[9 + i] = $" s[p{(i == 0 ? "" : $" + {i}")}] = arg{i};"; + } + lines[9 + args.Length] = args.Length > 1 ? $" pos += {args.Length};" : " pos++;"; + lines[10 + args.Length] = $" return;"; + lines[11 + args.Length] = $" }}"; + lines[12 + args.Length] = $""; + lines[13 + args.Length] = $" // Otherwise, resize the stack to make room and try again."; + lines[14 + args.Length] = $" WithResize(ref stack, ref pos{FormatN(", arg{0}", args.Length)});"; + lines[15 + args.Length] = $""; + lines[16 + args.Length] = $" // Resize the backtracking stack array and push {args.Length} value{(args.Length == 1 ? "" : "s")} onto the stack."; + lines[17 + args.Length] = $" [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]"; + lines[18 + args.Length] = $" static void WithResize(ref int[] stack, ref int pos{FormatN(", int arg{0}", args.Length)})"; + lines[19 + args.Length] = $" {{"; + lines[20 + args.Length] = $" global::System.Array.Resize(ref stack, (pos + {args.Length - 1}) * 2);"; + lines[21 + args.Length] = $" {function}(ref stack, ref pos{FormatN(", arg{0}", args.Length)});"; + lines[22 + args.Length] = $" }}"; + lines[23 + args.Length] = $"}}"; + + additionalLocalFunctions.Add(function, lines); + } + + writer.WriteLine($"{function}(ref base.runstack!, ref stackpos, {string.Join(", ", args)});"); + } + + /// Pops values from the backtracking stack into the specified locations. + void EmitStackPop(params string[] args) + { + Debug.Assert(args.Length is >= 1); + + if (args.Length == 1) + { + writer.WriteLine($"{args[0]} = {StackPop()};"); + return; + } + + string function = $"StackPop{args.Length}"; + + if (!additionalLocalFunctions.ContainsKey(function)) { - writer.WriteLine("global::System.Array.Resize(ref base.runstack, base.runstack.Length * 2);"); + var lines = new string[5 + args.Length]; + lines[0] = $"// Pop {args.Length} value{(args.Length == 1 ? "" : "s")} from the backtracking stack."; + lines[1] = $"[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"; + lines[2] = $"static void {function}(int[] stack, ref int pos{FormatN(", out int arg{0}", args.Length)})"; + lines[3] = $"{{"; + for (int i = 0; i < args.Length; i++) + { + lines[4 + i] = $" arg{i} = stack[--pos];"; + } + lines[4 + args.Length] = $"}}"; + + additionalLocalFunctions.Add(function, lines); } + + writer.WriteLine($"{function}(base.runstack, ref stackpos, out {string.Join(", out ", args)});"); } - string RunstackPush() => "base.runstack[runstackpos++]"; - string RunstackPop() => "base.runstack![--runstackpos]"; + /// Expression for popping the next item from the backtracking stack. + string StackPop() => "base.runstack![--stackpos]"; + + /// Concatenates the strings resulting from formatting the format string with the values [0, count). + static string FormatN(string format, int count) => + string.Concat(from i in Enumerable.Range(0, count) + select string.Format(format, i)); } private static bool EmitLoopTimeoutCounterIfNeeded(IndentedTextWriter writer, RegexMethod rm) @@ -2885,7 +3104,7 @@ private static bool EmitInitializeCultureForGoIfNecessary(IndentedTextWriter wri private static string ToLowerIfNeeded(bool hasTextInfo, RegexOptions options, string expression, bool toLower) => toLower ? ToLower(hasTextInfo, options, expression) : expression; - private static string MatchCharacterClass(bool hasTextInfo, RegexOptions options, string chExpr, string charClass, bool caseInsensitive, HashSet? additionalDeclarations) + private static string MatchCharacterClass(bool hasTextInfo, RegexOptions options, string chExpr, string charClass, bool caseInsensitive, bool negate, HashSet additionalDeclarations, ref RequiredHelperFunctions requiredHelpers) { // We need to perform the equivalent of calling RegexRunner.CharInClass(ch, charClass), // but that call is relatively expensive. Before we fall back to it, we try to optimize @@ -2899,19 +3118,23 @@ private static string MatchCharacterClass(bool hasTextInfo, RegexOptions options { case RegexCharClass.AnyClass: // ideally this could just be "return true;", but we need to evaluate the expression for its side effects - return $"({chExpr} != -1)"; // a char is unsigned and thus won't ever be equal to -1, so this is equivalent to true + return $"({chExpr} {(negate ? "<" : ">=")} 0)"; // a char is unsigned and thus won't ever be negative case RegexCharClass.DigitClass: - return $"char.IsDigit({chExpr})"; - case RegexCharClass.NotDigitClass: - return $"!char.IsDigit({chExpr})"; + negate ^= charClass == RegexCharClass.NotDigitClass; + return $"{(negate ? "!" : "")}char.IsDigit({chExpr})"; case RegexCharClass.SpaceClass: - return $"char.IsWhiteSpace({chExpr})"; - case RegexCharClass.NotSpaceClass: - return $"!char.IsWhiteSpace({chExpr})"; + negate ^= charClass == RegexCharClass.NotSpaceClass; + return $"{(negate ? "!" : "")}char.IsWhiteSpace({chExpr})"; + + case RegexCharClass.WordClass: + case RegexCharClass.NotWordClass: + requiredHelpers |= RequiredHelperFunctions.IsWordChar; + negate ^= charClass == RegexCharClass.NotWordClass; + return $"{(negate ? "!" : "")}IsWordChar({chExpr})"; } // If we're meant to be doing a case-insensitive lookup, and if we're not using the invariant culture, @@ -2933,10 +3156,10 @@ private static string MatchCharacterClass(bool hasTextInfo, RegexOptions options // Next, handle simple sets of one range, e.g. [A-Z], [0-9], etc. This includes some built-in classes, like ECMADigitClass. if (!invariant && RegexCharClass.TryGetSingleRange(charClass, out char lowInclusive, out char highInclusive)) { - bool invert = RegexCharClass.IsNegated(charClass); + negate ^= RegexCharClass.IsNegated(charClass); return lowInclusive == highInclusive ? - $"({chExpr} {(invert ? "!=" : "==")} {Literal(lowInclusive)})" : - $"(((uint){chExpr}) - {Literal(lowInclusive)} {(invert ? ">" : "<=")} (uint)({Literal(highInclusive)} - {Literal(lowInclusive)}))"; + $"({chExpr} {(negate ? "!=" : "==")} {Literal(lowInclusive)})" : + $"(((uint){chExpr}) - {Literal(lowInclusive)} {(negate ? ">" : "<=")} (uint)({Literal(highInclusive)} - {Literal(lowInclusive)}))"; } // Next if the character class contains nothing but a single Unicode category, we can calle char.GetUnicodeCategory and @@ -2944,7 +3167,8 @@ private static string MatchCharacterClass(bool hasTextInfo, RegexOptions options // we get smaller code), and it's what we'd do for the fallback (which we get to avoid generating) as part of CharInClass. if (!invariant && RegexCharClass.TryGetSingleUnicodeCategory(charClass, out UnicodeCategory category, out bool negated)) { - return $"(char.GetUnicodeCategory({chExpr}) {(negated ? "!=" : "==")} global::System.Globalization.UnicodeCategory.{category})"; + negate ^= negated; + return $"(char.GetUnicodeCategory({chExpr}) {(negate ? "!=" : "==")} global::System.Globalization.UnicodeCategory.{category})"; } // Next, if there's only 2, 3, or 4 chars in the set (fairly common due to the sets we create for prefixes), @@ -2959,30 +3183,38 @@ private static string MatchCharacterClass(bool hasTextInfo, RegexOptions options case 2: if ((setChars[0] | 0x20) == setChars[1]) { - return $"(({chExpr} | 0x20) == {Literal(setChars[1])})"; + return $"(({chExpr} | 0x20) {(negate ? "!=" : "==")} {Literal(setChars[1])})"; } - additionalDeclarations?.Add("char ch;"); - return $"(((ch = {chExpr}) == {Literal(setChars[0])}) | (ch == {Literal(setChars[1])}))"; + additionalDeclarations.Add("char ch;"); + return negate ? + $"(((ch = {chExpr}) != {Literal(setChars[0])}) & (ch != {Literal(setChars[1])}))" : + $"(((ch = {chExpr}) == {Literal(setChars[0])}) | (ch == {Literal(setChars[1])}))"; case 3: - additionalDeclarations?.Add("char ch;"); - return (setChars[0] | 0x20) == setChars[1] ? - $"((((ch = {chExpr}) | 0x20) == {Literal(setChars[1])}) | (ch == {Literal(setChars[2])}))" : - $"(((ch = {chExpr}) == {Literal(setChars[0])}) | (ch == {Literal(setChars[1])}) | (ch == {Literal(setChars[2])}))"; + additionalDeclarations.Add("char ch;"); + return (negate, (setChars[0] | 0x20) == setChars[1]) switch + { + (false, false) => $"(((ch = {chExpr}) == {Literal(setChars[0])}) | (ch == {Literal(setChars[1])}) | (ch == {Literal(setChars[2])}))", + (true, false) => $"(((ch = {chExpr}) != {Literal(setChars[0])}) & (ch != {Literal(setChars[1])}) & (ch != {Literal(setChars[2])}))", + (false, true) => $"((((ch = {chExpr}) | 0x20) == {Literal(setChars[1])}) | (ch == {Literal(setChars[2])}))", + (true, true) => $"((((ch = {chExpr}) | 0x20) != {Literal(setChars[1])}) & (ch != {Literal(setChars[2])}))", + }; case 4: if (((setChars[0] | 0x20) == setChars[1]) && ((setChars[2] | 0x20) == setChars[3])) { - additionalDeclarations?.Add("char ch;"); - return $"(((ch = ({chExpr} | 0x20)) == {Literal(setChars[1])}) | (ch == {Literal(setChars[3])}))"; + additionalDeclarations.Add("char ch;"); + return negate ? + $"(((ch = ({chExpr} | 0x20)) != {Literal(setChars[1])}) & (ch != {Literal(setChars[3])}))" : + $"(((ch = ({chExpr} | 0x20)) == {Literal(setChars[1])}) | (ch == {Literal(setChars[3])}))"; } break; } } // All options after this point require a ch local. - additionalDeclarations?.Add("char ch;"); + additionalDeclarations.Add("char ch;"); // Analyze the character set more to determine what code to generate. RegexCharClass.CharClassAnalysisResults analysis = RegexCharClass.Analyze(charClass); @@ -2996,8 +3228,8 @@ private static string MatchCharacterClass(bool hasTextInfo, RegexOptions options // the same as [\u0370-\u03FF\u1F00-1FFF]. (In the future, we could possibly // extend the analysis to produce a known lower-bound and compare against // that rather than always using 128 as the pivot point.) - return invariant ? - $"((ch = {chExpr}) >= 128 && global::System.Text.RegularExpressions.RegexRunner.CharInClass(char.ToLowerInvariant((char)ch), {Literal(charClass)}))" : + return negate ? + $"((ch = {chExpr}) < 128 || !global::System.Text.RegularExpressions.RegexRunner.CharInClass((char)ch, {Literal(charClass)}))" : $"((ch = {chExpr}) >= 128 && global::System.Text.RegularExpressions.RegexRunner.CharInClass((char)ch, {Literal(charClass)}))"; } @@ -3006,8 +3238,8 @@ private static string MatchCharacterClass(bool hasTextInfo, RegexOptions options // We determined that every ASCII character is in the class, for example // if the class were the negated example from case 1 above: // [^\p{IsGreek}\p{IsGreekExtended}]. - return invariant ? - $"((ch = {chExpr}) < 128 || global::System.Text.RegularExpressions.RegexRunner.CharInClass(char.ToLowerInvariant((char)ch), {Literal(charClass)}))" : + return negate ? + $"((ch = {chExpr}) >= 128 && !global::System.Text.RegularExpressions.RegexRunner.CharInClass((char)ch, {Literal(charClass)}))" : $"((ch = {chExpr}) < 128 || global::System.Text.RegularExpressions.RegexRunner.CharInClass((char)ch, {Literal(charClass)}))"; } } @@ -3050,7 +3282,9 @@ private static string MatchCharacterClass(bool hasTextInfo, RegexOptions options // We know that all inputs that could match are ASCII, for example if the // character class were [A-Za-z0-9], so since the ch is now known to be >= 128, we // can just fail the comparison. - return $"((ch = {chExpr}) < 128 && ({Literal(bitVectorString)}[ch >> 4] & (1 << (ch & 0xF))) != 0)"; + return negate ? + $"((ch = {chExpr}) >= 128 || ({Literal(bitVectorString)}[ch >> 4] & (1 << (ch & 0xF))) == 0)" : + $"((ch = {chExpr}) < 128 && ({Literal(bitVectorString)}[ch >> 4] & (1 << (ch & 0xF))) != 0)"; } if (analysis.AllNonAsciiContained) @@ -3058,15 +3292,21 @@ private static string MatchCharacterClass(bool hasTextInfo, RegexOptions options // We know that all non-ASCII inputs match, for example if the character // class were [^\r\n], so since we just determined the ch to be >= 128, we can just // give back success. - return $"((ch = {chExpr}) >= 128 || ({Literal(bitVectorString)}[ch >> 4] & (1 << (ch & 0xF))) != 0)"; + return negate ? + $"((ch = {chExpr}) < 128 && ({Literal(bitVectorString)}[ch >> 4] & (1 << (ch & 0xF))) == 0)" : + $"((ch = {chExpr}) >= 128 || ({Literal(bitVectorString)}[ch >> 4] & (1 << (ch & 0xF))) != 0)"; } // We know that the whole class wasn't ASCII, and we don't know anything about the non-ASCII // characters other than that some might be included, for example if the character class // were [\w\d], so since ch >= 128, we need to fall back to calling CharInClass. - return invariant ? - $"((ch = {chExpr}) < 128 ? ({Literal(bitVectorString)}[ch >> 4] & (1 << (ch & 0xF))) != 0 : global::System.Text.RegularExpressions.RegexRunner.CharInClass(char.ToLowerInvariant((char)ch), {Literal(charClass)}))" : - $"((ch = {chExpr}) < 128 ? ({Literal(bitVectorString)}[ch >> 4] & (1 << (ch & 0xF))) != 0 : global::System.Text.RegularExpressions.RegexRunner.CharInClass((char)ch, {Literal(charClass)}))"; + return (negate, invariant) switch + { + (false, false) => $"((ch = {chExpr}) < 128 ? ({Literal(bitVectorString)}[ch >> 4] & (1 << (ch & 0xF))) != 0 : global::System.Text.RegularExpressions.RegexRunner.CharInClass((char)ch, {Literal(charClass)}))", + (true, false) => $"((ch = {chExpr}) < 128 ? ({Literal(bitVectorString)}[ch >> 4] & (1 << (ch & 0xF))) == 0 : !global::System.Text.RegularExpressions.RegexRunner.CharInClass((char)ch, {Literal(charClass)}))", + (false, true) => $"((ch = {chExpr}) < 128 ? ({Literal(bitVectorString)}[ch >> 4] & (1 << (ch & 0xF))) != 0 : global::System.Text.RegularExpressions.RegexRunner.CharInClass(char.ToLowerInvariant((char)ch), {Literal(charClass)}))", + (true, true) => $"((ch = {chExpr}) < 128 ? ({Literal(bitVectorString)}[ch >> 4] & (1 << (ch & 0xF))) == 0 : !global::System.Text.RegularExpressions.RegexRunner.CharInClass(char.ToLowerInvariant((char)ch), {Literal(charClass)}))", + }; } /// @@ -3081,12 +3321,8 @@ private static void ReplaceAdditionalDeclarations(IndentedTextWriter writer, Has { if (declarations.Count != 0) { - var arr = new string[declarations.Count]; - declarations.CopyTo(arr); - Array.Sort(arr); - - StringBuilder tmp = new StringBuilder().AppendLine(); - foreach (string decl in arr) + var tmp = new StringBuilder(); + foreach (string decl in declarations.OrderBy(s => s)) { for (int i = 0; i < indent; i++) { @@ -3100,13 +3336,161 @@ private static void ReplaceAdditionalDeclarations(IndentedTextWriter writer, Has } } + /// Formats the character as valid C#. private static string Literal(char c) => SymbolDisplay.FormatLiteral(c, quote: true); + /// Formats the string as valid C#. private static string Literal(string s) => SymbolDisplay.FormatLiteral(s, quote: true); - private static FinishEmitScope EmitScope(IndentedTextWriter writer, string title, bool faux = false) => EmitBlock(writer, $"// {title}", appendBlankLine: true, faux); + private static string Literal(RegexOptions options) + { + string s = options.ToString(); + if (int.TryParse(s, out _)) + { + // The options were formatted as an int, which means the runtime couldn't + // produce a textual representation. So just output casting the value as an int. + return $"(global::System.Text.RegularExpressions.RegexOptions)({(int)options})"; + } + + // Parse the runtime-generated "Option1, Option2" into each piece and then concat + // them back together. + string[] parts = s.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < parts.Length; i++) + { + parts[i] = "global::System.Text.RegularExpressions.RegexOptions." + parts[i].Trim(); + } + return string.Join(" | ", parts); + } - private static FinishEmitScope EmitBlock(IndentedTextWriter writer, string? clause, bool appendBlankLine = false, bool faux = false) + /// Gets a textual description of the node fit for rendering in a comment in source. + private static string DescribeNode(RegexNode node) => + node.Type switch + { + RegexNode.Alternate => $"Match with {node.ChildCount()} alternative expressions{(node.IsAtomicByParent() ? ", atomically" : "")}.", + RegexNode.Atomic => $"Atomic group.", + RegexNode.Beginning => "Match if at the beginning of the string.", + RegexNode.Bol => "Match if at the beginning of a line.", + RegexNode.Boundary => $"Match if at a word boundary.", + RegexNode.Capture when node.N != -1 => $"{DescribeNonNegative(node.M)} capturing group. Uncaptures the {DescribeNonNegative(node.N)} capturing group.", + RegexNode.Capture when node.N == -1 => $"{DescribeNonNegative(node.M)} capturing group.", + RegexNode.Concatenate => "Match a sequence of expressions.", + RegexNode.ECMABoundary => $"Match if at a word boundary (according to ECMAScript rules).", + RegexNode.Empty => $"Match an empty string.", + RegexNode.End => "Match if at the end of the string.", + RegexNode.EndZ => "Match if at the end of the string or if before an ending newline.", + RegexNode.Eol => "Match if at the end of a line.", + RegexNode.Loop or RegexNode.Lazyloop => $"Loop {DescribeLoop(node)}.", + RegexNode.Multi => $"Match the string {Literal(node.Str!)}.", + RegexNode.NonBoundary => $"Match if at anything other than a word boundary.", + RegexNode.NonECMABoundary => $"Match if at anything other than a word boundary (according to ECMAScript rules).", + RegexNode.Nothing => $"Fail to match.", + RegexNode.Notone => $"Match any character other than {Literal(node.Ch)}.", + RegexNode.Notoneloop or RegexNode.Notoneloopatomic or RegexNode.Notonelazy => $"Match a character other than {Literal(node.Ch)} {DescribeLoop(node)}.", + RegexNode.One => $"Match {Literal(node.Ch)}.", + RegexNode.Oneloop or RegexNode.Oneloopatomic or RegexNode.Onelazy => $"Match {Literal(node.Ch)} {DescribeLoop(node)}.", + RegexNode.Prevent => $"Zero-width negative lookahead assertion.", + RegexNode.Ref => $"Match the same text as matched by the {DescribeNonNegative(node.M)} capture group.", + RegexNode.Require => $"Zero-width positive lookahead assertion.", + RegexNode.Set => $"Match a character in the set {RegexCharClass.SetDescription(node.Str!)}.", + RegexNode.Setloop or RegexNode.Setloopatomic or RegexNode.Setlazy => $"Match a character in the set {RegexCharClass.SetDescription(node.Str!)} {DescribeLoop(node)}.", + RegexNode.Start => "Match if at the start position.", + RegexNode.Testgroup => $"Conditionally match {(node.ChildCount() == 2 ? "an expression" : "one of two expressions")} depending on whether an initial expression matches.", + RegexNode.Testref => $"Conditionally match {(node.ChildCount() == 1 ? "an expression" : "one of two expressions")} depending on whether the {DescribeNonNegative(node.M)} capture group matched.", + RegexNode.UpdateBumpalong => $"Advance the next matching position.", + _ => $"Unknown node type {node.Type}", + + // Concatenation + }; + + /// Writes a textual description of the node tree fit for rending in source. + /// The writer to which the description should be written. + /// The node being written. + /// The prefix to write at the beginning of every line, including a "//" for a comment. + /// The depth of the current node. + private static void DescribeExpression(TextWriter writer, RegexNode node, string prefix, int depth = 0) + { + bool skip = node.Type switch + { + // For concatenations, flatten the contents into the parent, but only if the parent isn't a form of alternation, + // where each branch is considered to be independent rather than a concatenation. + RegexNode.Concatenate when node.Next is not { Type: RegexNode.Alternate or RegexNode.Testref or RegexNode.Testgroup } => true, + + // For atomic, skip the node if we'll instead render the atomic label as part of rendering the child. + RegexNode.Atomic when node.Child(0).Type is RegexNode.Loop or RegexNode.Lazyloop or RegexNode.Alternate => true, + + // Don't skip anything else. + _ => false, + }; + + if (!skip) + { + // Write out the line for the node. + const char BulletPoint = '\u25CB'; + writer.WriteLine($"{prefix}{new string(' ', depth * 4)}{BulletPoint} {DescribeNode(node)}"); + } + + // Recur into each of its children. + int childCount = node.ChildCount(); + if (childCount > 0) + { + for (int i = 0; i < childCount; i++) + { + int childDepth = skip ? depth : depth + 1; + DescribeExpression(writer, node.Child(i), prefix, childDepth); + } + } + } + + /// Gets a textual description of a number, e.g. 3 => "3rd". + private static string DescribeNonNegative(int n) + { + if (n < 0) + { + return n.ToString(CultureInfo.InvariantCulture); + } + + int tens = n % 10; + return tens is >= 1 and <= 3 && n % 100 is < 10 or > 20 ? // Ends in 1, 2, 3 but not 11, 12, or 13 + tens switch + { + 1 => $"{n}st", + 2 => $"{n}nd", + _ => $"{n}rd", + } : + $"{n}th"; + } + + /// Gets a textual description of a loop's style and bounds. + private static string DescribeLoop(RegexNode node) + { + string style = node.Type switch + { + _ when node.M == node.N => "exactly", + RegexNode.Oneloopatomic or RegexNode.Notoneloopatomic or RegexNode.Setloopatomic => "atomically", + RegexNode.Oneloop or RegexNode.Notoneloop or RegexNode.Setloop => "greedily", + RegexNode.Onelazy or RegexNode.Notonelazy or RegexNode.Setlazy => "lazily", + RegexNode.Loop => node.IsAtomicByParent() ? "greedily and atomically" : "greedily", + _ /* RegexNode.Lazy */ => node.IsAtomicByParent() ? "lazily and atomically" : "lazily", + }; + + string bounds = + node.M == node.N ? $" {node.M} times" : + (node.M, node.N) switch + { + (0, int.MaxValue) => " any number of times", + (1, int.MaxValue) => " at least once", + (2, int.MaxValue) => " at least twice", + (_, int.MaxValue) => $" at least {node.M} times", + (0, 1) => ", optionally", + _ => $" at least {node.M} and at most {node.N} times" + }; + + return style + bounds; + } + + private static FinishEmitScope EmitScope(IndentedTextWriter writer, string title, bool faux = false) => EmitBlock(writer, $"// {title}", faux: faux); + + private static FinishEmitScope EmitBlock(IndentedTextWriter writer, string? clause, bool faux = false) { if (clause is not null) { @@ -3114,7 +3498,7 @@ private static FinishEmitScope EmitBlock(IndentedTextWriter writer, string? clau } writer.WriteLine(faux ? "//{" : "{"); writer.Indent++; - return new FinishEmitScope(writer, appendBlankLine, faux); + return new FinishEmitScope(writer, faux); } private static void EmitAdd(IndentedTextWriter writer, string variable, int value) @@ -3135,13 +3519,11 @@ private static void EmitAdd(IndentedTextWriter writer, string variable, int valu private readonly struct FinishEmitScope : IDisposable { private readonly IndentedTextWriter _writer; - private readonly bool _appendBlankLine; private readonly bool _faux; - public FinishEmitScope(IndentedTextWriter writer, bool appendBlankLine, bool faux) + public FinishEmitScope(IndentedTextWriter writer, bool faux) { _writer = writer; - _appendBlankLine = appendBlankLine; _faux = faux; } @@ -3151,12 +3533,18 @@ public void Dispose() { _writer.Indent--; _writer.WriteLine(_faux ? "//}" : "}"); - if (_appendBlankLine) - { - _writer.WriteLine(); - } } } } + + /// Bit flags indicating which additional helpers should be emitted into the regex class. + [Flags] + private enum RequiredHelperFunctions + { + /// No additional functions are required. + None, + /// The IsWordChar helper is required. + IsWordChar + } } } diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs index e78f23c1c5032f..e00d03064a97a8 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs @@ -41,9 +41,6 @@ internal sealed partial class RegexCharClass private const short SpaceConst = 100; private const short NotSpaceConst = -100; - private const char ZeroWidthJoiner = '\u200D'; - private const char ZeroWidthNonJoiner = '\u200C'; - private const string InternalRegexIgnoreCase = "__InternalRegexIgnoreCase__"; private const string Space = "\x64"; private const string NotSpace = "\uFF9C"; @@ -975,25 +972,59 @@ public static bool IsECMAWordChar(char ch) => ch == '_' || // underscore ch == '\u0130'; // latin capital letter I with dot above + /// 16 bytes, representing the chars 0 through 127, with a 1 for a bit where that char is a word char. + private static ReadOnlySpan WordCharAsciiLookup => new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, + 0xFE, 0xFF, 0xFF, 0x87, 0xFE, 0xFF, 0xFF, 0x07 + }; + + /// Determines whether a character is considered a word character for the purposes of testing the \w set. public static bool IsWordChar(char ch) + { + // This is the same as IsBoundaryWordChar, except that IsBoundaryWordChar also + // returns true for \u200c and \u200d. + + // Fast lookup in our lookup table for ASCII characters. This is purely an optimization, and has the + // behavior as if we fell through to the switch below (which was actually used to produce the lookup table). + ReadOnlySpan asciiLookup = WordCharAsciiLookup; + int chDiv8 = ch >> 3; + if ((uint)chDiv8 < (uint)asciiLookup.Length) + { + return (asciiLookup[chDiv8] & (1 << (ch & 0x7))) != 0; + } + + // For non-ASCII, fall back to checking the Unicode category. + switch (CharUnicodeInfo.GetUnicodeCategory(ch)) + { + case UnicodeCategory.UppercaseLetter: + case UnicodeCategory.LowercaseLetter: + case UnicodeCategory.TitlecaseLetter: + case UnicodeCategory.ModifierLetter: + case UnicodeCategory.OtherLetter: + case UnicodeCategory.NonSpacingMark: + case UnicodeCategory.DecimalDigitNumber: + case UnicodeCategory.ConnectorPunctuation: + return true; + + default: + return false; + } + } + + /// Determines whether a character is considered a word character for the purposes of testing a word character boundary. + public static bool IsBoundaryWordChar(char ch) { // According to UTS#18 Unicode Regular Expressions (http://www.unicode.org/reports/tr18/) // RL 1.4 Simple Word Boundaries The class of includes all Alphabetic // values from the Unicode character database, from UnicodeData.txt [UData], plus the U+200C // ZERO WIDTH NON-JOINER and U+200D ZERO WIDTH JOINER. - // 16 bytes, representing the chars 0 through 127, with a 1 for a bit where that char is a word char - static ReadOnlySpan AsciiLookup() => new byte[] - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, - 0xFE, 0xFF, 0xFF, 0x87, 0xFE, 0xFF, 0xFF, 0x07 - }; - // Fast lookup in our lookup table for ASCII characters. This is purely an optimization, and has the // behavior as if we fell through to the switch below (which was actually used to produce the lookup table). - ReadOnlySpan asciiLookup = AsciiLookup(); + ReadOnlySpan asciiLookup = WordCharAsciiLookup; int chDiv8 = ch >> 3; - if ((uint)chDiv8 < asciiLookup.Length) + if ((uint)chDiv8 < (uint)asciiLookup.Length) { return (asciiLookup[chDiv8] & (1 << (ch & 0x7))) != 0; } @@ -1012,7 +1043,8 @@ public static bool IsWordChar(char ch) return true; default: - return ch == ZeroWidthJoiner || ch == ZeroWidthNonJoiner; + const char ZeroWidthNonJoiner = '\u200C', ZeroWidthJoiner = '\u200D'; + return ch == ZeroWidthJoiner | ch == ZeroWidthNonJoiner; } } @@ -1892,25 +1924,16 @@ public static string CharDescription(char ch) => }; [ExcludeFromCodeCoverage] - private static string CategoryDescription(char ch) - { - if (ch == SpaceConst) - { - return "\\s"; - } - - if ((short)ch == NotSpaceConst) - { - return "\\S"; - } - - if ((short)ch < 0) - { - return "\\P{" + CategoryIdToName[(-((short)ch) - 1)] + "}"; - } - - return "\\p{" + CategoryIdToName[(ch - 1)] + "}"; - } + private static string CategoryDescription(char ch) => + (short)ch switch + { + SpaceConst => @"\s", + NotSpaceConst => @"\S", + (short)(UnicodeCategory.DecimalDigitNumber + 1) => @"\d", + -(short)(UnicodeCategory.DecimalDigitNumber + 1) => @"\D", + < 0 => $"\\P{{{CategoryIdToName[-(short)ch - 1]}}}", + _ => $"\\p{{{CategoryIdToName[ch - 1]}}}", + }; /// /// A first/last pair representing a single range of characters. diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs index 815e552e334066..fc16e44f774548 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs @@ -30,6 +30,7 @@ internal abstract class RegexCompiler private static readonly MethodInfo s_matchLengthMethod = RegexRunnerMethod("MatchLength"); private static readonly MethodInfo s_matchIndexMethod = RegexRunnerMethod("MatchIndex"); private static readonly MethodInfo s_isBoundaryMethod = RegexRunnerMethod("IsBoundary"); + private static readonly MethodInfo s_isWordCharMethod = RegexRunnerMethod("IsWordChar"); private static readonly MethodInfo s_isECMABoundaryMethod = RegexRunnerMethod("IsECMABoundary"); private static readonly MethodInfo s_crawlposMethod = RegexRunnerMethod("Crawlpos"); private static readonly MethodInfo s_charInClassMethod = RegexRunnerMethod("CharInClass"); @@ -354,9 +355,9 @@ protected void EmitFindFirstChar() _int32LocalsPool?.Clear(); _readOnlySpanCharLocalsPool?.Clear(); - LocalBuilder runtextSpan = DeclareReadOnlySpanChar(); - LocalBuilder runtextpos = DeclareInt32(); - LocalBuilder runtextend = DeclareInt32(); + LocalBuilder inputSpan = DeclareReadOnlySpanChar(); + LocalBuilder pos = DeclareInt32(); + LocalBuilder end = DeclareInt32(); _textInfo = null; if ((_options & RegexOptions.CultureInvariant) == 0) @@ -380,14 +381,14 @@ FindNextStartingPositionMode.FixedSets_LeftToRight_CaseInsensitive or } // Load necessary locals - // int runtextpos = this.runtextpos; - // int runtextend = this.runtextend; - // ReadOnlySpan runtextSpan = this.runtext.AsSpan(); - Mvfldloc(s_runtextposField, runtextpos); - Mvfldloc(s_runtextendField, runtextend); + // int pos = base.runtextpos; + // int end = base.runtextend; + // ReadOnlySpan inputSpan = base.runtext.AsSpan(); + Mvfldloc(s_runtextposField, pos); + Mvfldloc(s_runtextendField, end); Ldthisfld(s_runtextField); Call(s_stringAsSpanMethod); - Stloc(runtextSpan); + Stloc(inputSpan); // Generate length check. If the input isn't long enough to possibly match, fail quickly. // It's rare for min required length to be 0, so we don't bother special-casing the check, @@ -397,13 +398,13 @@ FindNextStartingPositionMode.FixedSets_LeftToRight_CaseInsensitive or Label returnFalse = DefineLabel(); Label finishedLengthCheck = DefineLabel(); - // if (runtextpos > runtextend - _code.Tree.MinRequiredLength) + // if (pos > end - _code.Tree.MinRequiredLength) // { - // this.runtextpos = runtextend; + // base.runtextpos = end; // return false; // } - Ldloc(runtextpos); - Ldloc(runtextend); + Ldloc(pos); + Ldloc(end); if (minRequiredLength > 0) { Ldc(minRequiredLength); @@ -413,7 +414,7 @@ FindNextStartingPositionMode.FixedSets_LeftToRight_CaseInsensitive or MarkLabel(returnFalse); Ldthis(); - Ldloc(runtextend); + Ldloc(end); Stfld(s_runtextposField); Ldc(0); @@ -465,7 +466,7 @@ bool GenerateAnchors() case RegexPrefixAnalyzer.Beginning: { Label l1 = DefineLabel(); - Ldloc(runtextpos); + Ldloc(pos); Ldthisfld(s_runtextbegField); Ble(l1); Br(returnFalse); @@ -478,7 +479,7 @@ bool GenerateAnchors() case RegexPrefixAnalyzer.Start: { Label l1 = DefineLabel(); - Ldloc(runtextpos); + Ldloc(pos); Ldthisfld(s_runtextstartField); Ble(l1); Br(returnFalse); @@ -491,13 +492,13 @@ bool GenerateAnchors() case RegexPrefixAnalyzer.EndZ: { Label l1 = DefineLabel(); - Ldloc(runtextpos); - Ldloc(runtextend); + Ldloc(pos); + Ldloc(end); Ldc(1); Sub(); Bge(l1); Ldthis(); - Ldloc(runtextend); + Ldloc(end); Ldc(1); Sub(); Stfld(s_runtextposField); @@ -510,11 +511,11 @@ bool GenerateAnchors() case RegexPrefixAnalyzer.End: { Label l1 = DefineLabel(); - Ldloc(runtextpos); - Ldloc(runtextend); + Ldloc(pos); + Ldloc(end); Bge(l1); Ldthis(); - Ldloc(runtextend); + Ldloc(end); Stfld(s_runtextposField); MarkLabel(l1); } @@ -531,14 +532,14 @@ bool GenerateAnchors() Label atBeginningOfLine = DefineLabel(); - // if (runtextpos > runtextbeg... - Ldloc(runtextpos!); + // if (pos > runtextbeg... + Ldloc(pos!); Ldthisfld(s_runtextbegField); Ble(atBeginningOfLine); - // ... && runtextSpan[runtextpos - 1] != '\n') { ... } - Ldloca(runtextSpan); - Ldloc(runtextpos); + // ... && inputSpan[pos - 1] != '\n') { ... } + Ldloca(inputSpan); + Ldloc(pos); Ldc(1); Sub(); Call(s_spanGetItemMethod); @@ -546,9 +547,9 @@ bool GenerateAnchors() Ldc('\n'); Beq(atBeginningOfLine); - // int tmp = runtextSpan.Slice(runtextpos).IndexOf('\n'); - Ldloca(runtextSpan); - Ldloc(runtextpos); + // int tmp = inputSpan.Slice(pos).IndexOf('\n'); + Ldloca(inputSpan); + Ldloc(pos); Call(s_spanSliceIntMethod); Ldc('\n'); Call(s_spanIndexOfChar); @@ -556,29 +557,29 @@ bool GenerateAnchors() { Stloc(newlinePos); - // if (newlinePos == -1 || newlinePos + runtextpos + 1 > runtextend) + // if (newlinePos < 0 || newlinePos + pos + 1 > end) // { - // runtextpos = runtextend; + // base.runtextpos = end; // return false; // } Ldloc(newlinePos); - Ldc(-1); - Beq(returnFalse); + Ldc(0); + Blt(returnFalse); Ldloc(newlinePos); - Ldloc(runtextpos); + Ldloc(pos); Add(); Ldc(1); Add(); - Ldloc(runtextend); + Ldloc(end); Bgt(returnFalse); - // runtextpos = newlinePos + runtextpos + 1; + // pos += newlinePos + 1; + Ldloc(pos); Ldloc(newlinePos); - Ldloc(runtextpos); Add(); Ldc(1); Add(); - Stloc(runtextpos); + Stloc(pos); } MarkLabel(atBeginningOfLine); @@ -594,11 +595,11 @@ void EmitIndexOf_LeftToRight(string prefix) { using RentedLocalBuilder i = RentInt32Local(); - // int i = runtextSpan.Slice(runtextpos, runtextend - runtextpos).IndexOf(prefix); - Ldloca(runtextSpan); - Ldloc(runtextpos); - Ldloc(runtextend); - Ldloc(runtextpos); + // int i = inputSpan.Slice(pos, end - pos).IndexOf(prefix); + Ldloca(inputSpan); + Ldloc(pos); + Ldloc(end); + Ldloc(pos); Sub(); Call(s_spanSliceIntIntMethod); Ldstr(prefix); @@ -611,10 +612,10 @@ void EmitIndexOf_LeftToRight(string prefix) Ldc(0); BltFar(returnFalse); - // base.runtextpos = runtextpos + i; + // base.runtextpos = pos + i; // return true; Ldthis(); - Ldloc(runtextpos); + Ldloc(pos); Ldloc(i); Add(); Stfld(s_runtextposField); @@ -632,11 +633,11 @@ void EmitFixedSet_LeftToRight() using RentedLocalBuilder iLocal = RentInt32Local(); using RentedLocalBuilder textSpanLocal = RentReadOnlySpanCharLocal(); - // ReadOnlySpan span = runtextSpan.Slice(runtextpos, runtextend - runtextpos); - Ldloca(runtextSpan); - Ldloc(runtextpos); - Ldloc(runtextend); - Ldloc(runtextpos); + // ReadOnlySpan span = inputSpan.Slice(pos, end - pos); + Ldloca(inputSpan); + Ldloc(pos); + Ldloc(end); + Ldloc(pos); Sub(); Call(s_spanSliceIntIntMethod); Stloc(textSpanLocal); @@ -669,7 +670,7 @@ void EmitFixedSet_LeftToRight() if (needLoop) { - // textSpan.Slice(iLocal + primarySet.Distance); + // slice.Slice(iLocal + primarySet.Distance); Ldloca(textSpanLocal); Ldloc(iLocal); if (primarySet.Distance != 0) @@ -681,14 +682,14 @@ void EmitFixedSet_LeftToRight() } else if (primarySet.Distance != 0) { - // textSpan.Slice(primarySet.Distance) + // slice.Slice(primarySet.Distance) Ldloca(textSpanLocal); Ldc(primarySet.Distance); Call(s_spanSliceIntMethod); } else { - // textSpan + // slice Ldloc(textSpanLocal); } @@ -748,7 +749,7 @@ void EmitFixedSet_LeftToRight() BltFar(returnFalse); } - // if (i >= textSpan.Length - (minRequiredLength - 1)) goto returnFalse; + // if (i >= slice.Length - (minRequiredLength - 1)) goto returnFalse; if (sets.Count > 1) { Debug.Assert(needLoop); @@ -761,9 +762,9 @@ void EmitFixedSet_LeftToRight() } } - // if (!CharInClass(textSpan[i], prefix[0], "...")) continue; - // if (!CharInClass(textSpan[i + 1], prefix[1], "...")) continue; - // if (!CharInClass(textSpan[i + 2], prefix[2], "...")) continue; + // if (!CharInClass(slice[i], prefix[0], "...")) continue; + // if (!CharInClass(slice[i + 1], prefix[1], "...")) continue; + // if (!CharInClass(slice[i + 2], prefix[2], "...")) continue; // ... Debug.Assert(setIndex == 0 || setIndex == 1); for ( ; setIndex < sets.Count; setIndex++) @@ -782,10 +783,10 @@ void EmitFixedSet_LeftToRight() BrfalseFar(charNotInClassLabel); } - // this.runtextpos = runtextpos + i; + // base.runtextpos = pos + i; // return true; Ldthis(); - Ldloc(runtextpos); + Ldloc(pos); Ldloc(iLocal); Add(); Stfld(s_runtextposField); @@ -814,7 +815,7 @@ void EmitFixedSet_LeftToRight() } BltFar(loopBody); - // runtextpos = runtextend; + // base.runtextpos = end; // return false; BrFar(returnFalse); } @@ -856,43 +857,42 @@ protected void EmitGo() // Skip the Capture node. We handle the implicit root capture specially. node = node.Child(0); - // In some limited cases, FindFirstChar will only return true if it successfully matched the whole thing. - // This is the case, in particular, for strings. We can special case these to do essentially nothing - // in Go other than emit the capture. - if (!IsCaseInsensitive(node)) // FindFirstChar may yield false positives on these in some cultures when case-insensitive + + // In some limited cases, FindFirstChar will only return true if it successfully matched the whole expression. + // We can special case these to do essentially nothing in Go other than emit the capture. + switch (node.Type) { - switch (node.Type) - { - case RegexNode.Multi: - case RegexNode.Notone: - case RegexNode.One: - case RegexNode.Set: - // base.Capture(0, base.runtextpos, base.runtextpos + node.Str.Length); - // base.runtextpos = base.runtextpos + node.Str.Length; - // return; - Ldthis(); - Dup(); - Ldc(0); - Ldthisfld(s_runtextposField); - Dup(); - Ldc(node.Type == RegexNode.Multi ? node.Str!.Length : 1); - Add(); - Call(s_captureMethod); - Ldthisfld(s_runtextposField); - Ldc(node.Type == RegexNode.Multi ? node.Str!.Length : 1); - Add(); - Stfld(s_runtextposField); - Ret(); - return; - } + case RegexNode.Multi or RegexNode.Notone or RegexNode.One or RegexNode.Set when !IsCaseInsensitive(node): + // This is the case for single and multiple characters, though the whole thing is only guaranteed + // to have been validated in FindFirstChar when doing case-sensitive comparison. + // base.Capture(0, base.runtextpos, base.runtextpos + node.Str.Length); + // base.runtextpos = base.runtextpos + node.Str.Length; + // return; + Ldthis(); + Dup(); + Ldc(0); + Ldthisfld(s_runtextposField); + Dup(); + Ldc(node.Type == RegexNode.Multi ? node.Str!.Length : 1); + Add(); + Call(s_captureMethod); + Ldthisfld(s_runtextposField); + Ldc(node.Type == RegexNode.Multi ? node.Str!.Length : 1); + Add(); + Stfld(s_runtextposField); + Ret(); + return; + + // The source generator special-cases RegexNode.Empty, for purposes of code learning rather than + // performance. Since that's not applicable to RegexCompiler, that code isn't mirrored here. } // Initialize the main locals used throughout the implementation. - LocalBuilder runtextSpanLocal = DeclareReadOnlySpanChar(); - LocalBuilder originalruntextposLocal = DeclareInt32(); - LocalBuilder runtextposLocal = DeclareInt32(); - LocalBuilder textSpanLocal = DeclareReadOnlySpanChar(); - LocalBuilder runtextendLocal = DeclareInt32(); + LocalBuilder inputSpan = DeclareReadOnlySpanChar(); + LocalBuilder originalPos = DeclareInt32(); + LocalBuilder pos = DeclareInt32(); + LocalBuilder slice = DeclareReadOnlySpanChar(); + LocalBuilder end = DeclareInt32(); Label stopSuccessLabel = DefineLabel(); Label doneLabel = DefineLabel(); Label originalDoneLabel = doneLabel; @@ -904,63 +904,63 @@ protected void EmitGo() // CultureInfo culture = CultureInfo.CurrentCulture; // only if the whole expression or any subportion is ignoring case, and we're not using invariant InitializeCultureForGoIfNecessary(); - // ReadOnlySpan runtextSpan = this.runtext.AsSpan(); - // int runtextend = this.runtextend; + // ReadOnlySpan inputSpan = base.runtext.AsSpan(); + // int end = base.runtextend; Ldthisfld(s_runtextField); Call(s_stringAsSpanMethod); - Stloc(runtextSpanLocal); - Mvfldloc(s_runtextendField, runtextendLocal); + Stloc(inputSpan); + Mvfldloc(s_runtextendField, end); - // int runtextpos = this.runtextpos; - // int originalruntextpos = this.runtextpos; + // int pos = base.runtextpos; + // int originalpos = pos; Ldthisfld(s_runtextposField); - Stloc(runtextposLocal); - Ldloc(runtextposLocal); - Stloc(originalruntextposLocal); + Stloc(pos); + Ldloc(pos); + Stloc(originalPos); - // int runstackpos = 0; - LocalBuilder runstackpos = DeclareInt32(); + // int stackpos = 0; + LocalBuilder stackpos = DeclareInt32(); Ldc(0); - Stloc(runstackpos); + Stloc(stackpos); // The implementation tries to use const indexes into the span wherever possible, which we can do - // in all places except for variable-length loops. For everything else, we know at any point in - // the regex exactly how far into it we are, and we can use that to index into the span created - // at the beginning of the routine to begin at exactly where we're starting in the input. For - // variable-length loops, we index at this textSpanPos + i, and then after the loop we slice the input - // by i so that this position is still accurate for everything after it. - int textSpanPos = 0; - LoadTextSpanLocal(); + // for all fixed-length constructs. In such cases (e.g. single chars, repeaters, strings, etc.) + // we know at any point in the regex exactly how far into it we are, and we can use that to index + // into the span created at the beginning of the routine to begin at exactly where we're starting + // in the input. When we encounter a variable-length construct, we transfer the static value to + // pos, slicing the inputSpan appropriately, and then zero out the static position. + int sliceStaticPos = 0; + SliceInputSpan(); // Emit the code for all nodes in the tree. bool expressionHasCaptures = (node.Options & RegexNode.HasCapturesFlag) != 0; EmitNode(node); // Success: - // runtextpos += textSpanPos; - // this.runtextpos = runtextpos; - // Capture(0, originalruntextpos, runtextpos); + // pos += sliceStaticPos; + // base.runtextpos = pos; + // Capture(0, originalpos, pos); MarkLabel(stopSuccessLabel); Ldthis(); - Ldloc(runtextposLocal); - if (textSpanPos > 0) + Ldloc(pos); + if (sliceStaticPos > 0) { - Ldc(textSpanPos); + Ldc(sliceStaticPos); Add(); - Stloc(runtextposLocal); - Ldloc(runtextposLocal); + Stloc(pos); + Ldloc(pos); } Stfld(s_runtextposField); Ldthis(); Ldc(0); - Ldloc(originalruntextposLocal); - Ldloc(runtextposLocal); + Ldloc(originalPos); + Ldloc(pos); Call(s_captureMethod); // If the graph contained captures, undo any remaining to handle failed matches. if (expressionHasCaptures) { - // while (Crawlpos() != 0) Uncapture(); + // while (base.Crawlpos() != 0) base.Uncapture(); Label finalReturnLabel = DefineLabel(); Br(finalReturnLabel); @@ -994,17 +994,17 @@ protected void EmitGo() static bool IsCaseInsensitive(RegexNode node) => (node.Options & RegexOptions.IgnoreCase) != 0; - // Creates a span for runtext starting at runtextpos until this.runtextend. - void LoadTextSpanLocal() + // Slices the inputSpan starting at pos until end and stores it into slice. + void SliceInputSpan() { - // textSpan = runtextSpan.Slice(runtextpos, this.runtextend - runtextpos); - Ldloca(runtextSpanLocal); - Ldloc(runtextposLocal); - Ldloc(runtextendLocal); - Ldloc(runtextposLocal); + // slice = inputSpan.Slice(pos, end - pos); + Ldloca(inputSpan); + Ldloc(pos); + Ldloc(end); + Ldloc(pos); Sub(); Call(s_spanSliceIntIntMethod); - Stloc(textSpanLocal); + Stloc(slice); } // Emits the sum of a constant and a value from a local. @@ -1029,46 +1029,46 @@ void EmitSum(int constant, LocalBuilder? local) // Emits a check that the span is large enough at the currently known static position to handle the required additional length. void EmitSpanLengthCheck(int requiredLength, LocalBuilder? dynamicRequiredLength = null) { - // if ((uint)(textSpanPos + requiredLength + dynamicRequiredLength - 1) >= (uint)textSpan.Length) goto Done; + // if ((uint)(sliceStaticPos + requiredLength + dynamicRequiredLength - 1) >= (uint)slice.Length) goto Done; Debug.Assert(requiredLength > 0); - EmitSum(textSpanPos + requiredLength - 1, dynamicRequiredLength); - Ldloca(textSpanLocal); + EmitSum(sliceStaticPos + requiredLength - 1, dynamicRequiredLength); + Ldloca(slice); Call(s_spanGetLengthMethod); BgeUnFar(doneLabel); } - // Emits code to get ref textSpan[textSpanPos] + // Emits code to get ref slice[sliceStaticPos] void EmitTextSpanOffset() { - Ldloc(textSpanLocal); + Ldloc(slice); Call(s_memoryMarshalGetReference); - if (textSpanPos > 0) + if (sliceStaticPos > 0) { - Ldc(textSpanPos * sizeof(char)); + Ldc(sliceStaticPos * sizeof(char)); Add(); } } - // Adds the value of textSpanPos into the runtextpos local, slices textspan by the corresponding amount, - // and zeros out textSpanPos. - void TransferTextSpanPosToRunTextPos() + // Adds the value of sliceStaticPos into the pos local, slices textspan by the corresponding amount, + // and zeros out sliceStaticPos. + void TransferSliceStaticPosToPos() { - if (textSpanPos > 0) + if (sliceStaticPos > 0) { - // runtextpos += textSpanPos; - Ldloc(runtextposLocal); - Ldc(textSpanPos); + // pos += sliceStaticPos; + Ldloc(pos); + Ldc(sliceStaticPos); Add(); - Stloc(runtextposLocal); + Stloc(pos); - // textSpan = textSpan.Slice(textSpanPos); - Ldloca(textSpanLocal); - Ldc(textSpanPos); + // slice = slice.Slice(sliceStaticPos); + Ldloca(slice); + Ldc(sliceStaticPos); Call(s_spanSliceIntMethod); - Stloc(textSpanLocal); + Stloc(slice); - // textSpanPos = 0; - textSpanPos = 0; + // sliceStaticPos = 0; + sliceStaticPos = 0; } } @@ -1089,12 +1089,12 @@ void EmitAlternation(RegexNode node) // Label to jump to when any branch completes successfully. Label matchLabel = DefineLabel(); - // Save off runtextpos. We'll need to reset this each time a branch fails. - // startingRunTextPos = runtextpos; - LocalBuilder startingRunTextPos = DeclareInt32(); - Ldloc(runtextposLocal); - Stloc(startingRunTextPos); - int startingTextSpanPos = textSpanPos; + // Save off pos. We'll need to reset this each time a branch fails. + // startingPos = pos; + LocalBuilder startingPos = DeclareInt32(); + Ldloc(pos); + Stloc(startingPos); + int startingTextSpanPos = sliceStaticPos; // We need to be able to undo captures in two situations: // - If a branch of the alternation itself contains captures, then if that branch @@ -1116,14 +1116,14 @@ void EmitAlternation(RegexNode node) // of the alternation, which will again dutifully unwind the remaining captures until // what they were at the start of the alternation. Of course, if there are no captures // anywhere in the regex, we don't have to do any of that. - LocalBuilder? startingCrawlpos = null; + LocalBuilder? startingCapturePos = null; if (expressionHasCaptures && ((node.Options & RegexNode.HasCapturesFlag) != 0 || !isAtomic)) { - // startingCrawlpos = base.Crawlpos(); - startingCrawlpos = DeclareInt32(); + // startingCapturePos = base.Crawlpos(); + startingCapturePos = DeclareInt32(); Ldthis(); Call(s_crawlposMethod); - Stloc(startingCrawlpos); + Stloc(startingCapturePos); } // After executing the alternation, subsequent matching may fail, at which point execution @@ -1165,48 +1165,48 @@ void EmitAlternation(RegexNode node) // still points to the nextBranch, which similarly is where we'll want to jump to. if (!isAtomic) { - // if (runstackpos + 3 >= base.runstack.Length) Array.Resize(ref base.runstack, base.runstack.Length * 2); - // base.runstack[runstackpos++] = i; - // base.runstack[runstackpos++] = startingCrawlpos; - // base.runstack[runstackpos++] = startingRunTextPos; + // if (stackpos + 3 >= base.runstack.Length) Array.Resize(ref base.runstack, base.runstack.Length * 2); + // base.runstack[stackpos++] = i; + // base.runstack[stackpos++] = startingCapturePos; + // base.runstack[stackpos++] = startingPos; EmitRunstackResizeIfNeeded(3); EmitRunstackPush(() => Ldc(i)); - if (startingCrawlpos is not null) + if (startingCapturePos is not null) { - EmitRunstackPush(() => Ldloc(startingCrawlpos)); + EmitRunstackPush(() => Ldloc(startingCapturePos)); } - EmitRunstackPush(() => Ldloc(startingRunTextPos)); + EmitRunstackPush(() => Ldloc(startingPos)); } labelMap[i] = doneLabel; // If we get here in the generated code, the branch completed successfully. - // Before jumping to the end, we need to zero out textSpanPos, so that no + // Before jumping to the end, we need to zero out sliceStaticPos, so that no // matter what the value is after the branch, whatever follows the alternate - // will see the same textSpanPos. - // runtextpos += textSpanPos; - // textSpanPos = 0; + // will see the same sliceStaticPos. + // pos += sliceStaticPos; + // sliceStaticPos = 0; // goto matchLabel; - TransferTextSpanPosToRunTextPos(); + TransferSliceStaticPosToPos(); BrFar(matchLabel); // Reset state for next branch and loop around to generate it. This includes - // setting runtextpos back to what it was at the beginning of the alternation, - // updating textSpan to be the full length it was, and if there's a capture that + // setting pos back to what it was at the beginning of the alternation, + // updating slice to be the full length it was, and if there's a capture that // needs to be reset, uncapturing it. if (!isLastBranch) { // NextBranch: - // runtextpos = startingRunTextPos; - // textSpan = runtext.AsSpan(runtextpos, runtextend - runtextpos); - // while (base.Crawlpos() > startingCrawlpos) base.Uncapture(); + // pos = startingPos; + // slice = inputSpan.Slice(pos, end - pos); + // while (base.Crawlpos() > startingCapturePos) base.Uncapture(); MarkLabel(nextBranch); - Ldloc(startingRunTextPos); - Stloc(runtextposLocal); - LoadTextSpanLocal(); - textSpanPos = startingTextSpanPos; - if (startingCrawlpos is not null) + Ldloc(startingPos); + Stloc(pos); + SliceInputSpan(); + sliceStaticPos = startingTextSpanPos; + if (startingCapturePos is not null) { - EmitUncaptureUntil(startingCrawlpos); + EmitUncaptureUntil(startingCapturePos); } } } @@ -1227,15 +1227,15 @@ void EmitAlternation(RegexNode node) doneLabel = backtrackLabel; MarkLabel(backtrackLabel); - // startingRuntextPos = base.runstack[--runstackpos]; - // startingCrawlPos = base.runstack[--runstackpos]; - // switch (base.runstack[--runstackpos]) { ... } // branch number + // startingPos = base.runstack[--stackpos]; + // startingCapturePos = base.runstack[--stackpos]; + // switch (base.runstack[--stackpos]) { ... } // branch number EmitRunstackPop(); - Stloc(startingRunTextPos); - if (startingCrawlpos is not null) + Stloc(startingPos); + if (startingCapturePos is not null) { EmitRunstackPop(); - Stloc(startingCrawlpos); + Stloc(startingCapturePos); } EmitRunstackPop(); Switch(labelMap); @@ -1243,7 +1243,7 @@ void EmitAlternation(RegexNode node) // Successfully completed the alternate. MarkLabel(matchLabel); - Debug.Assert(textSpanPos == 0); + Debug.Assert(sliceStaticPos == 0); } // Emits the code to handle a backreference. @@ -1251,15 +1251,15 @@ void EmitBackreference(RegexNode node) { int capnum = RegexParser.MapCaptureNumber(node.M, _code!.Caps); - TransferTextSpanPosToRunTextPos(); + TransferSliceStaticPosToPos(); - Label end = DefineLabel(); + Label backreferenceEnd = DefineLabel(); - // if (!base.IsMatched(capnum)) goto (!ecmascript ? doneLabel : end); + // if (!base.IsMatched(capnum)) goto (ecmascript ? end : doneLabel); Ldthis(); Ldc(capnum); Call(s_isMatchedMethod); - BrfalseFar((node.Options & RegexOptions.ECMAScript) == 0 ? doneLabel : end); + BrfalseFar((node.Options & RegexOptions.ECMAScript) == 0 ? doneLabel : backreferenceEnd); using RentedLocalBuilder matchLength = RentInt32Local(); using RentedLocalBuilder matchIndex = RentInt32Local(); @@ -1271,8 +1271,8 @@ void EmitBackreference(RegexNode node) Call(s_matchLengthMethod); Stloc(matchLength); - // if (textSpan.Length < matchLength) goto doneLabel; - Ldloca(textSpanLocal); + // if (slice.Length < matchLength) goto doneLabel; + Ldloca(slice); Call(s_spanGetLengthMethod); Ldloc(matchLength); BltFar(doneLabel); @@ -1293,8 +1293,8 @@ void EmitBackreference(RegexNode node) MarkLabel(body); - // if (runtextSpan[matchIndex + i] != textSpan[i]) goto doneLabel; - Ldloca(runtextSpanLocal); + // if (inputSpan[matchIndex + i] != slice[i]) goto doneLabel; + Ldloca(inputSpan); Ldloc(matchIndex); Ldloc(i); Add(); @@ -1304,7 +1304,7 @@ void EmitBackreference(RegexNode node) { CallToLower(); } - Ldloca(textSpanLocal); + Ldloca(slice); Ldloc(i); Call(s_spanGetItemMethod); LdindU2(); @@ -1326,14 +1326,14 @@ void EmitBackreference(RegexNode node) Ldloc(matchLength); Blt(body); - // runtextpos += matchLength; - Ldloc(runtextposLocal); + // pos += matchLength; + Ldloc(pos); Ldloc(matchLength); Add(); - Stloc(runtextposLocal); - LoadTextSpanLocal(); + Stloc(pos); + SliceInputSpan(); - MarkLabel(end); + MarkLabel(backreferenceEnd); } // Emits the code for an if(backreference)-then-else conditional. @@ -1341,14 +1341,14 @@ void EmitBackreferenceConditional(RegexNode node) { bool isAtomic = node.IsAtomicByParent(); - // We're branching in a complicated fashion. Make sure textSpanPos is 0. - TransferTextSpanPosToRunTextPos(); + // We're branching in a complicated fashion. Make sure sliceStaticPos is 0. + TransferSliceStaticPosToPos(); // Get the capture number to test. int capnum = RegexParser.MapCaptureNumber(node.M, _code!.Caps); Label originalDoneLabel = doneLabel; - Label endRef = DefineLabel(); + Label backreferenceConditionalEnd = DefineLabel(); bool hasNo = node.ChildCount() > 1 && node.Child(1).Type != RegexNode.Empty; // As with alternations, we have potentially multiple branches, each of which may contain @@ -1367,7 +1367,7 @@ void EmitBackreferenceConditional(RegexNode node) // The specified capture was captured. Run the "yes" branch. // If it successfully matches, jump to the end. EmitNode(node.Child(0)); - TransferTextSpanPosToRunTextPos(); + TransferSliceStaticPosToPos(); Label postIfDoneLabel = doneLabel; if (postIfDoneLabel != originalDoneLabel) { @@ -1378,7 +1378,7 @@ void EmitBackreferenceConditional(RegexNode node) if (postIfDoneLabel != originalDoneLabel || hasNo) { // goto endRef; - BrFar(endRef); + BrFar(backreferenceConditionalEnd); } MarkLabel(refNotMatched); @@ -1389,7 +1389,7 @@ void EmitBackreferenceConditional(RegexNode node) // Output the no branch. doneLabel = originalDoneLabel; EmitNode(node.Child(1)); - TransferTextSpanPosToRunTextPos(); // make sure textSpanPos is 0 after each branch + TransferSliceStaticPosToPos(); // make sure sliceStaticPos is 0 after each branch postElseDoneLabel = doneLabel; if (postElseDoneLabel != originalDoneLabel) { @@ -1423,13 +1423,13 @@ void EmitBackreferenceConditional(RegexNode node) { // Skip the backtracking section // goto endRef; - Br(endRef); + Br(backreferenceConditionalEnd); Label backtrack = DefineLabel(); doneLabel = backtrack; MarkLabel(backtrack); - // resumeAt = base.runstack[--runstackpos]; + // resumeAt = base.runstack[--stackpos]; EmitRunstackPop(); Stloc(resumeAt); @@ -1456,11 +1456,11 @@ void EmitBackreferenceConditional(RegexNode node) if (postIfDoneLabel != originalDoneLabel || hasNo) { - MarkLabel(endRef); + MarkLabel(backreferenceConditionalEnd); if (!isAtomic && (postIfDoneLabel != originalDoneLabel || postElseDoneLabel != originalDoneLabel)) { - // if (runstackpos + 1 >= base.runstack.Length) Array.Resize(ref base.runstack, base.runstack.Length * 2); - // base.runstack[runstackpos++] = resumeAt; + // if (stackpos + 1 >= base.runstack.Length) Array.Resize(ref base.runstack, base.runstack.Length * 2); + // base.runstack[stackpos++] = resumeAt; EmitRunstackResizeIfNeeded(1); EmitRunstackPush(() => Ldloc(resumeAt)); } @@ -1472,8 +1472,8 @@ void EmitExpressionConditional(RegexNode node) { bool isAtomic = node.IsAtomicByParent(); - // We're branching in a complicated fashion. Make sure textSpanPos is 0. - TransferTextSpanPosToRunTextPos(); + // We're branching in a complicated fashion. Make sure sliceStaticPos is 0. + TransferSliceStaticPosToPos(); // The first child node is the conditional expression. If this matches, then we branch to the "yes" branch. // If it doesn't match, then we branch to the optional "no" branch if it exists, or simply skip the "yes" @@ -1491,24 +1491,24 @@ void EmitExpressionConditional(RegexNode node) RegexNode yesBranch = node.Child(1); RegexNode? noBranch = node.ChildCount() > 2 && node.Child(2) is { Type: not RegexNode.Empty } childNo ? childNo : null; - Label end = DefineLabel(); + Label expressionConditionalEnd = DefineLabel(); Label no = DefineLabel(); // If the conditional expression has captures, we'll need to uncapture them in the case of no match. - LocalBuilder? startingCrawlPos = null; + LocalBuilder? startingCapturePos = null; if ((conditional.Options & RegexNode.HasCapturesFlag) != 0) { - // int startingCrawlPos = base.Crawlpos(); - startingCrawlPos = DeclareInt32(); + // int startingCapturePos = base.Crawlpos(); + startingCapturePos = DeclareInt32(); Ldthis(); Call(s_crawlposMethod); - Stloc(startingCrawlPos); + Stloc(startingCapturePos); } // Emit the conditional expression. We need to reroute any match failures to either the "no" branch // if it exists, or to the end of the node (skipping the "yes" branch) if it doesn't. Label originalDoneLabel = doneLabel; - Label tmpDoneLabel = noBranch is not null ? no : end; + Label tmpDoneLabel = noBranch is not null ? no : expressionConditionalEnd; doneLabel = tmpDoneLabel; EmitPositiveLookaheadAssertion(conditional); if (doneLabel == tmpDoneLabel) @@ -1521,11 +1521,11 @@ void EmitExpressionConditional(RegexNode node) // If we get to this point of the code, the conditional successfully matched, so run the "yes" branch. // Since the "yes" branch may have a different execution path than the "no" branch or the lack of - // any branch, we need to store the current textSpanPosition and reset it prior to emitting the code + // any branch, we need to store the current sliceStaticPos and reset it prior to emitting the code // for what comes after the "yes" branch, so that everyone is on equal footing. - int startingTextSpanPos = textSpanPos; + int startingTextSpanPos = sliceStaticPos; EmitNode(yesBranch); - TransferTextSpanPosToRunTextPos(); // ensure all subsequent code sees the same textSpanPos value by setting it to 0 + TransferSliceStaticPosToPos(); // ensure all subsequent code sees the same sliceStaticPos value by setting it to 0 Label postYesDoneLabel = doneLabel; if (resumeAt is not null && postYesDoneLabel != originalDoneLabel) { @@ -1536,7 +1536,7 @@ void EmitExpressionConditional(RegexNode node) if (postYesDoneLabel != originalDoneLabel || noBranch is not null) { // goto end; - BrFar(end); + BrFar(expressionConditionalEnd); } // If there's a no branch, we need to emit it, but skipping it from a successful "yes" branch match. @@ -1546,21 +1546,21 @@ void EmitExpressionConditional(RegexNode node) // Emit the no branch, first uncapturing any captures from the expression condition that failed // to match and emit the branch. MarkLabel(no); - if (startingCrawlPos is not null) + if (startingCapturePos is not null) { - // while (base.Crawlpos() > startingCrawlPos) base.Uncapture(); - EmitUncaptureUntil(startingCrawlPos); + // while (base.Crawlpos() > startingCapturePos) base.Uncapture(); + EmitUncaptureUntil(startingCapturePos); } doneLabel = postConditionalDoneLabel; - textSpanPos = startingTextSpanPos; + sliceStaticPos = startingTextSpanPos; EmitNode(noBranch); - TransferTextSpanPosToRunTextPos(); // ensure all subsequent code sees the same textSpanPos value by setting it to 0 + TransferSliceStaticPosToPos(); // ensure all subsequent code sees the same sliceStaticPos value by setting it to 0 postNoDoneLabel = doneLabel; if (postNoDoneLabel != originalDoneLabel) { // goto end; - BrFar(end); + BrFar(expressionConditionalEnd); } } else @@ -1586,7 +1586,7 @@ void EmitExpressionConditional(RegexNode node) if (postYesDoneLabel != postConditionalDoneLabel || postNoDoneLabel != postConditionalDoneLabel) { // Skip the backtracking section. - BrFar(end); + BrFar(expressionConditionalEnd); Label backtrack = DefineLabel(); doneLabel = backtrack; @@ -1614,14 +1614,14 @@ void EmitExpressionConditional(RegexNode node) if (postYesDoneLabel != originalDoneLabel || postNoDoneLabel != originalDoneLabel) { - // if (runstackpos + 1 >= base.runstack.Length) Array.Resize(ref base.runstack, base.runstack.Length * 2); - // base.runstack[runstackpos++] = resumeAt; + // if (stackpos + 1 >= base.runstack.Length) Array.Resize(ref base.runstack, base.runstack.Length * 2); + // base.runstack[stackpos++] = resumeAt; EmitRunstackResizeIfNeeded(1); EmitRunstackPush(() => Ldloc(resumeAt)); } } - MarkLabel(end); + MarkLabel(expressionConditionalEnd); } // Emits the code for a Capture node. @@ -1632,13 +1632,13 @@ void EmitCapture(RegexNode node, RegexNode? subsequent = null) int uncapnum = RegexParser.MapCaptureNumber(node.N, _code.Caps); bool isAtomic = node.IsAtomicByParent(); - // runtextpos += textSpanPos; - // textSpan = textSpan.Slice(textSpanPos); - // startingRunTextPos = runtextpos; - TransferTextSpanPosToRunTextPos(); - LocalBuilder startingRunTextPos = DeclareInt32(); - Ldloc(runtextposLocal); - Stloc(startingRunTextPos); + // pos += sliceStaticPos; + // slice = slice.Slice(sliceStaticPos); + // startingPos = pos; + TransferSliceStaticPosToPos(); + LocalBuilder startingPos = DeclareInt32(); + Ldloc(pos); + Stloc(startingPos); RegexNode child = node.Child(0); @@ -1657,60 +1657,60 @@ void EmitCapture(RegexNode node, RegexNode? subsequent = null) EmitNode(child, subsequent); bool childBacktracks = doneLabel != originalDoneLabel; - // runtextpos += textSpanPos; - // textSpan = textSpan.Slice(textSpanPos); - TransferTextSpanPosToRunTextPos(); + // pos += sliceStaticPos; + // slice = slice.Slice(sliceStaticPos); + TransferSliceStaticPosToPos(); if (uncapnum == -1) { - // Capture(capnum, startingRunTextPos, runtextpos); + // Capture(capnum, startingPos, pos); Ldthis(); Ldc(capnum); - Ldloc(startingRunTextPos); - Ldloc(runtextposLocal); + Ldloc(startingPos); + Ldloc(pos); Call(s_captureMethod); } else { - // TransferCapture(capnum, uncapnum, startingRunTextPos, runtextpos); + // TransferCapture(capnum, uncapnum, startingPos, pos); Ldthis(); Ldc(capnum); Ldc(uncapnum); - Ldloc(startingRunTextPos); - Ldloc(runtextposLocal); + Ldloc(startingPos); + Ldloc(pos); Call(s_transferCaptureMethod); } if (!isAtomic && (childBacktracks || node.IsInLoop())) { - // if (runstackpos + 1 >= base.runstack.Length) Array.Resize(ref base.runstack, base.runstack.Length * 2); - // base.runstack[runstackpos++] = startingRunTextPos; + // if (stackpos + 1 >= base.runstack.Length) Array.Resize(ref base.runstack, base.runstack.Length * 2); + // base.runstack[stackpos++] = startingPos; EmitRunstackResizeIfNeeded(1); - EmitRunstackPush(() => Ldloc(startingRunTextPos)); + EmitRunstackPush(() => Ldloc(startingPos)); // Skip past the backtracking section - // goto end; - Label end = DefineLabel(); - Br(end); + // goto backtrackingEnd; + Label backtrackingEnd = DefineLabel(); + Br(backtrackingEnd); // Emit a backtracking section that restores the capture's state and then jumps to the previous done label Label backtrack = DefineLabel(); MarkLabel(backtrack); EmitRunstackPop(); - Stloc(startingRunTextPos); + Stloc(startingPos); if (!childBacktracks) { - // runtextpos = startingRunTextPos - Ldloc(startingRunTextPos); - Stloc(runtextposLocal); - LoadTextSpanLocal(); + // pos = startingPos + Ldloc(startingPos); + Stloc(pos); + SliceInputSpan(); } // goto doneLabel; BrFar(doneLabel); doneLabel = backtrack; - MarkLabel(end); + MarkLabel(backtrackingEnd); } else { @@ -1719,11 +1719,11 @@ void EmitCapture(RegexNode node, RegexNode? subsequent = null) } // Emits code to unwind the capture stack until the crawl position specified in the provided local. - void EmitUncaptureUntil(LocalBuilder startingCrawlpos) + void EmitUncaptureUntil(LocalBuilder startingCapturePos) { - Debug.Assert(startingCrawlpos != null); + Debug.Assert(startingCapturePos != null); - // while (Crawlpos() > startingCrawlpos) Uncapture(); + // while (base.Crawlpos() > startingCapturePos) base.Uncapture(); Label condition = DefineLabel(); Label body = DefineLabel(); Br(condition); @@ -1735,7 +1735,7 @@ void EmitUncaptureUntil(LocalBuilder startingCrawlpos) MarkLabel(condition); Ldthis(); Call(s_crawlposMethod); - Ldloc(startingCrawlpos); + Ldloc(startingCapturePos); Bgt(body); } @@ -1745,24 +1745,24 @@ void EmitPositiveLookaheadAssertion(RegexNode node) // Lookarounds are implicitly atomic. Store the original done label to reset at the end. Label originalDoneLabel = doneLabel; - // Save off runtextpos. We'll need to reset this upon successful completion of the lookahead. - // startingRunTextPos = runtextpos; - LocalBuilder startingRunTextPos = DeclareInt32(); - Ldloc(runtextposLocal); - Stloc(startingRunTextPos); - int startingTextSpanPos = textSpanPos; + // Save off pos. We'll need to reset this upon successful completion of the lookahead. + // startingPos = pos; + LocalBuilder startingPos = DeclareInt32(); + Ldloc(pos); + Stloc(startingPos); + int startingTextSpanPos = sliceStaticPos; // Emit the child. EmitNode(node.Child(0)); // After the child completes successfully, reset the text positions. // Do not reset captures, which persist beyond the lookahead. - // runtextpos = startingRunTextPos; - // textSpan = runtext.AsSpan(runtextpos, runtextend - runtextpos); - Ldloc(startingRunTextPos); - Stloc(runtextposLocal); - LoadTextSpanLocal(); - textSpanPos = startingTextSpanPos; + // pos = startingPos; + // slice = inputSpan.Slice(pos, end - pos); + Ldloc(startingPos); + Stloc(pos); + SliceInputSpan(); + sliceStaticPos = startingTextSpanPos; doneLabel = originalDoneLabel; } @@ -1773,12 +1773,12 @@ void EmitNegativeLookaheadAssertion(RegexNode node) // Lookarounds are implicitly atomic. Store the original done label to reset at the end. Label originalDoneLabel = doneLabel; - // Save off runtextpos. We'll need to reset this upon successful completion of the lookahead. - // startingRunTextPos = runtextpos; - LocalBuilder startingRunTextPos = DeclareInt32(); - Ldloc(runtextposLocal); - Stloc(startingRunTextPos); - int startingTextSpanPos = textSpanPos; + // Save off pos. We'll need to reset this upon successful completion of the lookahead. + // startingPos = pos; + LocalBuilder startingPos = DeclareInt32(); + Ldloc(pos); + Stloc(startingPos); + int startingTextSpanPos = sliceStaticPos; Label negativeLookaheadDoneLabel = DefineLabel(); doneLabel = negativeLookaheadDoneLabel; @@ -1799,11 +1799,11 @@ void EmitNegativeLookaheadAssertion(RegexNode node) } // After the child completes in failure (success for negative lookahead), reset the text positions. - // runtextpos = startingRunTextPos; - Ldloc(startingRunTextPos); - Stloc(runtextposLocal); - LoadTextSpanLocal(); - textSpanPos = startingTextSpanPos; + // pos = startingPos; + Ldloc(startingPos); + Stloc(pos); + SliceInputSpan(); + sliceStaticPos = startingTextSpanPos; doneLabel = originalDoneLabel; } @@ -1936,15 +1936,15 @@ void EmitAtomic(RegexNode node, RegexNode? subsequent) doneLabel = originalDoneLabel; } - // Emits the code to handle updating base.runtextpos to runtextpos in response to + // Emits the code to handle updating base.runtextpos to pos in response to // an UpdateBumpalong node. This is used when we want to inform the scan loop that // it should bump from this location rather than from the original location. void EmitUpdateBumpalong() { - // base.runtextpos = runtextpos; - TransferTextSpanPosToRunTextPos(); + // base.runtextpos = pos; + TransferSliceStaticPosToPos(); Ldthis(); - Ldloc(runtextposLocal); + Ldloc(pos); Stfld(s_runtextposField); } @@ -1980,13 +1980,13 @@ void EmitSingleChar(RegexNode node, bool emitLengthCheck = true, LocalBuilder? o // to generate the code for a single check, so we check for each "family" (one, notone, set) // rather than only for the specific single character nodes. - // if ((uint)(textSpanPos + offset) >= textSpan.Length || textSpan[textSpanPos + offset] != ch) goto Done; + // if ((uint)(sliceStaticPos + offset) >= slice.Length || slice[sliceStaticPos + offset] != ch) goto Done; if (emitLengthCheck) { EmitSpanLengthCheck(1, offset); } - Ldloca(textSpanLocal); - EmitSum(textSpanPos, offset); + Ldloca(slice); + EmitSum(sliceStaticPos, offset); Call(s_spanGetItemMethod); LdindU2(); if (node.IsSetFamily) @@ -2011,22 +2011,22 @@ void EmitSingleChar(RegexNode node, bool emitLengthCheck = true, LocalBuilder? o } } - textSpanPos++; + sliceStaticPos++; } // Emits the code to handle a boundary check on a character. void EmitBoundary(RegexNode node) { - // if (!IsBoundary(runtextpos + textSpanPos, this.runtextbeg, this.runtextend)) goto doneLabel; + // if (!IsBoundary(pos + sliceStaticPos, base.runtextbeg, end)) goto doneLabel; Ldthis(); - Ldloc(runtextposLocal); - if (textSpanPos > 0) + Ldloc(pos); + if (sliceStaticPos > 0) { - Ldc(textSpanPos); + Ldc(sliceStaticPos); Add(); } Ldthisfld(s_runtextbegField); - Ldloc(runtextendLocal); + Ldloc(end); switch (node.Type) { case RegexNode.Boundary: @@ -2055,12 +2055,12 @@ void EmitBoundary(RegexNode node) // Emits the code to handle various anchors. void EmitAnchors(RegexNode node) { - Debug.Assert(textSpanPos >= 0); + Debug.Assert(sliceStaticPos >= 0); switch (node.Type) { case RegexNode.Beginning: case RegexNode.Start: - if (textSpanPos > 0) + if (sliceStaticPos > 0) { // If we statically know we've already matched part of the regex, there's no way we're at the // beginning or start, as we've already progressed past it. @@ -2068,19 +2068,19 @@ void EmitAnchors(RegexNode node) } else { - // if (runtextpos > this.runtextbeg/start) goto doneLabel; - Ldloc(runtextposLocal); + // if (pos > base.runtextbeg/start) goto doneLabel; + Ldloc(pos); Ldthisfld(node.Type == RegexNode.Beginning ? s_runtextbegField : s_runtextstartField); BneFar(doneLabel); } break; case RegexNode.Bol: - if (textSpanPos > 0) + if (sliceStaticPos > 0) { - // if (textSpan[textSpanPos - 1] != '\n') goto doneLabel; - Ldloca(textSpanLocal); - Ldc(textSpanPos - 1); + // if (slice[sliceStaticPos - 1] != '\n') goto doneLabel; + Ldloca(slice); + Ldc(sliceStaticPos - 1); Call(s_spanGetItemMethod); LdindU2(); Ldc('\n'); @@ -2088,14 +2088,14 @@ void EmitAnchors(RegexNode node) } else { - // We can't use our textSpan in this case, because we'd need to access textSpan[-1], so we access the runtext field directly: - // if (runtextpos > this.runtextbeg && this.runtext[runtextpos - 1] != '\n') goto doneLabel; + // We can't use our slice in this case, because we'd need to access slice[-1], so we access the runtext field directly: + // if (pos > base.runtextbeg && base.runtext[pos - 1] != '\n') goto doneLabel; Label success = DefineLabel(); - Ldloc(runtextposLocal); + Ldloc(pos); Ldthisfld(s_runtextbegField); Ble(success); - Ldloca(runtextSpanLocal); - Ldloc(runtextposLocal); + Ldloca(inputSpan); + Ldloc(pos); Ldc(1); Sub(); Call(s_spanGetItemMethod); @@ -2107,17 +2107,17 @@ void EmitAnchors(RegexNode node) break; case RegexNode.End: - // if (textSpanPos < textSpan.Length) goto doneLabel; - Ldc(textSpanPos); - Ldloca(textSpanLocal); + // if (sliceStaticPos < slice.Length) goto doneLabel; + Ldc(sliceStaticPos); + Ldloca(slice); Call(s_spanGetLengthMethod); BltUnFar(doneLabel); break; case RegexNode.EndZ: - // if (textSpanPos < textSpan.Length - 1) goto doneLabel; - Ldc(textSpanPos); - Ldloca(textSpanLocal); + // if (sliceStaticPos < slice.Length - 1) goto doneLabel; + Ldc(sliceStaticPos); + Ldloca(slice); Call(s_spanGetLengthMethod); Ldc(1); Sub(); @@ -2125,15 +2125,15 @@ void EmitAnchors(RegexNode node) goto case RegexNode.Eol; case RegexNode.Eol: - // if (textSpanPos < textSpan.Length && textSpan[textSpanPos] != '\n') goto doneLabel; + // if (sliceStaticPos < slice.Length && slice[sliceStaticPos] != '\n') goto doneLabel; { Label success = DefineLabel(); - Ldc(textSpanPos); - Ldloca(textSpanLocal); + Ldc(sliceStaticPos); + Ldloca(slice); Call(s_spanGetLengthMethod); BgeUn(success); - Ldloca(textSpanLocal); - Ldc(textSpanPos); + Ldloca(slice); + Ldc(sliceStaticPos); Call(s_spanGetItemMethod); LdindU2(); Ldc('\n'); @@ -2162,20 +2162,20 @@ void EmitMultiChar(RegexNode node, bool emitLengthCheck = true) if (!caseInsensitive && // StartsWith(..., XxIgnoreCase) won't necessarily be the same as char-by-char comparison node.Str!.Length > MaxUnrollLength) { - // if (!textSpan.Slice(textSpanPos).StartsWith("...") goto doneLabel; - Ldloca(textSpanLocal); - Ldc(textSpanPos); + // if (!slice.Slice(sliceStaticPos).StartsWith("...") goto doneLabel; + Ldloca(slice); + Ldc(sliceStaticPos); Call(s_spanSliceIntMethod); Ldstr(node.Str); Call(s_stringAsSpanMethod); Call(s_spanStartsWith); BrfalseFar(doneLabel); - textSpanPos += node.Str.Length; + sliceStaticPos += node.Str.Length; return; } // Emit the length check for the whole string. If the generated code gets past this point, - // we know the span is at least textSpanPos + s.Length long. + // we know the span is at least sliceStaticPos + s.Length long. ReadOnlySpan s = node.Str; if (emitLengthCheck) { @@ -2197,13 +2197,13 @@ void EmitMultiChar(RegexNode node, bool emitLengthCheck = true) const int CharsPerInt64 = 4; while (s.Length >= CharsPerInt64) { - // if (Unsafe.ReadUnaligned(ref Unsafe.Add(ref MemoryMarshal.GetReference(textSpan), textSpanPos)) != value) goto doneLabel; + // if (Unsafe.ReadUnaligned(ref Unsafe.Add(ref MemoryMarshal.GetReference(slice), sliceStaticPos)) != value) goto doneLabel; EmitTextSpanOffset(); Unaligned(1); LdindI8(); LdcI8(MemoryMarshal.Read(MemoryMarshal.AsBytes(s))); BneFar(doneLabel); - textSpanPos += CharsPerInt64; + sliceStaticPos += CharsPerInt64; s = s.Slice(CharsPerInt64); } } @@ -2212,13 +2212,13 @@ void EmitMultiChar(RegexNode node, bool emitLengthCheck = true) const int CharsPerInt32 = 2; while (s.Length >= CharsPerInt32) { - // if (Unsafe.ReadUnaligned(ref Unsafe.Add(ref MemoryMarshal.GetReference(textSpan), textSpanPos)) != value) goto doneLabel; + // if (Unsafe.ReadUnaligned(ref Unsafe.Add(ref MemoryMarshal.GetReference(slice), sliceStaticPos)) != value) goto doneLabel; EmitTextSpanOffset(); Unaligned(1); LdindI4(); Ldc(MemoryMarshal.Read(MemoryMarshal.AsBytes(s))); BneFar(doneLabel); - textSpanPos += CharsPerInt32; + sliceStaticPos += CharsPerInt32; s = s.Slice(CharsPerInt32); } } @@ -2226,9 +2226,9 @@ void EmitMultiChar(RegexNode node, bool emitLengthCheck = true) // Finally, process all of the remaining characters one by one. for (int i = 0; i < s.Length; i++) { - // if (s[i] != textSpan[textSpanPos++]) goto doneLabel; + // if (s[i] != slice[sliceStaticPos++]) goto doneLabel; EmitTextSpanOffset(); - textSpanPos++; + sliceStaticPos++; LdindU2(); if (caseInsensitive) { @@ -2258,10 +2258,10 @@ void EmitSingleCharLoop(RegexNode node, RegexNode? subsequent = null, bool emitL Label endLoop = DefineLabel(); LocalBuilder startingPos = DeclareInt32(); LocalBuilder endingPos = DeclareInt32(); - LocalBuilder? crawlPos = expressionHasCaptures ? DeclareInt32() : null; + LocalBuilder? capturepos = expressionHasCaptures ? DeclareInt32() : null; // We're about to enter a loop, so ensure our text position is 0. - TransferTextSpanPosToRunTextPos(); + TransferSliceStaticPosToPos(); // Grab the current position, then emit the loop as atomic, and then // grab the current position again. Even though we emit the loop without @@ -2269,24 +2269,24 @@ void EmitSingleCharLoop(RegexNode node, RegexNode? subsequent = null, bool emitL // through the individual characters (a benefit of the loop matching exactly // one character per iteration, no possible captures within the loop, etc.) - // int startingPos = runtextpos; - Ldloc(runtextposLocal); + // int startingPos = pos; + Ldloc(pos); Stloc(startingPos); EmitSingleCharAtomicLoop(node); - // runtextpos += textSpanPos; - // int endingPos = runtextpos; - TransferTextSpanPosToRunTextPos(); - Ldloc(runtextposLocal); + // pos += sliceStaticPos; + // int endingPos = pos; + TransferSliceStaticPosToPos(); + Ldloc(pos); Stloc(endingPos); - // int crawlPos = base.Crawlpos(); - if (crawlPos is not null) + // int capturepos = base.Crawlpos(); + if (capturepos is not null) { Ldthis(); Call(s_crawlposMethod); - Stloc(crawlPos!); + Stloc(capturepos); } // startingPos += node.M; @@ -2306,17 +2306,17 @@ void EmitSingleCharLoop(RegexNode node, RegexNode? subsequent = null, bool emitL // required, and try again by flowing to everything that comes after this. MarkLabel(backtrackingLabel); - if (crawlPos is not null) + if (capturepos is not null) { - // crawlPos = base.runstack[--runstackpos]; - // while (base.Crawlpos() > crawlpos) base.Uncapture(); + // capturepos = base.runstack[--stackpos]; + // while (base.Crawlpos() > capturepos) base.Uncapture(); EmitRunstackPop(); - Stloc(crawlPos); - EmitUncaptureUntil(crawlPos); + Stloc(capturepos); + EmitUncaptureUntil(capturepos); } - // endingPos = base.runstack[--runstackpos]; - // startingPos = base.runstack[--runstackpos]; + // endingPos = base.runstack[--stackpos]; + // startingPos = base.runstack[--stackpos]; EmitRunstackPop(); Stloc(endingPos); EmitRunstackPop(); @@ -2331,12 +2331,12 @@ void EmitSingleCharLoop(RegexNode node, RegexNode? subsequent = null, bool emitL if (subsequent?.FindStartingCharacter() is char subsequentCharacter) { - // endingPos = runtextSpan.Slice(startingPos, endingPos - startingPos).LastIndexOf(subsequentCharacter); + // endingPos = inputSpan.Slice(startingPos, endingPos - startingPos).LastIndexOf(subsequentCharacter); // if (endingPos < 0) // { // goto originalDoneLabel; // } - Ldloca(runtextSpanLocal); + Ldloca(inputSpan); Ldloc(startingPos); Ldloc(endingPos); Ldloc(startingPos); @@ -2364,20 +2364,20 @@ void EmitSingleCharLoop(RegexNode node, RegexNode? subsequent = null, bool emitL Stloc(endingPos); } - // runtextpos = endingPos; + // pos = endingPos; Ldloc(endingPos); - Stloc(runtextposLocal); + Stloc(pos); - // textspan = runtext.AsSpan(runtextpos, runtextend - runtextpos); - LoadTextSpanLocal(); + // slice = inputSpan.Slice(pos, end - pos); + SliceInputSpan(); MarkLabel(endLoop); EmitRunstackResizeIfNeeded(expressionHasCaptures ? 3 : 2); EmitRunstackPush(() => Ldloc(startingPos)); EmitRunstackPush(() => Ldloc(endingPos)); - if (crawlPos is not null) + if (capturepos is not null) { - EmitRunstackPush(() => Ldloc(crawlPos!)); + EmitRunstackPush(() => Ldloc(capturepos!)); } } @@ -2404,7 +2404,7 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) // to try to match, and only matching another character if the subsequent expression fails to match. // We're about to enter a loop, so ensure our text position is 0. - TransferTextSpanPosToRunTextPos(); + TransferSliceStaticPosToPos(); // If the loop isn't unbounded, track the number of iterations and the max number to allow. LocalBuilder? iterationCount = null; @@ -2420,14 +2420,14 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) } // Track the current crawl position. Upon backtracking, we'll unwind any captures beyond this point. - LocalBuilder? crawlPos = expressionHasCaptures ? DeclareInt32() : null; + LocalBuilder? capturepos = expressionHasCaptures ? DeclareInt32() : null; - // Track the current runtextpos. Each time we backtrack, we'll reset to the stored position, which + // Track the current pos. Each time we backtrack, we'll reset to the stored position, which // is also incremented each time we match another character in the loop. - // int startingRunTextPos = runtextpos; - LocalBuilder startingRunTextPos = DeclareInt32(); - Ldloc(runtextposLocal); - Stloc(startingRunTextPos); + // int startingPos = pos; + LocalBuilder startingPos = DeclareInt32(); + Ldloc(pos); + Stloc(startingPos); // Skip the backtracking section for the initial subsequent matching. We've already matched the // minimum number of iterations, which means we can successfully match with zero additional iterations. @@ -2441,10 +2441,10 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) // Uncapture any captures if the expression has any. It's possible the captures it has // are before this node, in which case this is wasted effort, but still functionally correct. - if (crawlPos is not null) + if (capturepos is not null) { - // while (base.Crawlpos() > crawlPos) base.Uncapture(); - EmitUncaptureUntil(crawlPos); + // while (base.Crawlpos() > capturepos) base.Uncapture(); + EmitUncaptureUntil(capturepos); } // If there's a max number of iterations, see if we've exceeded the maximum number of characters @@ -2463,22 +2463,22 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) Stloc(iterationCount!); } - // Now match the next item in the lazy loop. We need to reset the runtextpos to the position + // Now match the next item in the lazy loop. We need to reset the pos to the position // just after the last character in this loop was matched, and we need to store the resulting position // for the next time we backtrack. - // runtextpos = startingRunTextPos; - Ldloc(startingRunTextPos); - Stloc(runtextposLocal); - LoadTextSpanLocal(); + // pos = startingPos; + Ldloc(startingPos); + Stloc(pos); + SliceInputSpan(); // Match single character EmitSingleChar(node); - TransferTextSpanPosToRunTextPos(); + TransferSliceStaticPosToPos(); - // startingRunTextPos = runtextpos; - Ldloc(runtextposLocal); - Stloc(startingRunTextPos); + // startingPos = pos; + Ldloc(pos); + Stloc(startingPos); // Update the done label for everything that comes after this node. This is done after we emit the single char // matching, as that failing indicates the loop itself has failed to match. @@ -2486,25 +2486,25 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) doneLabel = backtrackingLabel; // leave set to the backtracking label for all subsequent nodes MarkLabel(endLoopLabel); - if (crawlPos is not null) + if (capturepos is not null) { - // crawlPos = base.CrawlPos(); + // capturepos = base.CrawlPos(); Ldthis(); Call(s_crawlposMethod); - Stloc(crawlPos); + Stloc(capturepos); } if (node.IsInLoop()) { // Store the capture's state - // base.runstack[runstackpos++] = startingRunTextPos; - // base.runstack[runstackpos++] = crawlPos; - // base.runstack[runstackpos++] = iterationCount; + // base.runstack[stackpos++] = startingPos; + // base.runstack[stackpos++] = capturepos; + // base.runstack[stackpos++] = iterationCount; EmitRunstackResizeIfNeeded(3); - EmitRunstackPush(() => Ldloc(startingRunTextPos)); - if (crawlPos is not null) + EmitRunstackPush(() => Ldloc(startingPos)); + if (capturepos is not null) { - EmitRunstackPush(() => Ldloc(crawlPos)); + EmitRunstackPush(() => Ldloc(capturepos)); } if (iterationCount is not null) { @@ -2512,34 +2512,34 @@ void EmitSingleCharLazy(RegexNode node, bool emitLengthChecksIfRequired = true) } // Skip past the backtracking section - Label end = DefineLabel(); - BrFar(end); + Label backtrackingEnd = DefineLabel(); + BrFar(backtrackingEnd); // Emit a backtracking section that restores the capture's state and then jumps to the previous done label Label backtrack = DefineLabel(); MarkLabel(backtrack); - // iterationCount = base.runstack[--runstackpos]; - // crawlPos = base.runstack[--runstackpos]; - // startingRunTextPos = base.runstack[--runstackpos]; + // iterationCount = base.runstack[--stackpos]; + // capturepos = base.runstack[--stackpos]; + // startingPos = base.runstack[--stackpos]; if (iterationCount is not null) { EmitRunstackPop(); Stloc(iterationCount); } - if (crawlPos is not null) + if (capturepos is not null) { EmitRunstackPop(); - Stloc(crawlPos); + Stloc(capturepos); } EmitRunstackPop(); - Stloc(startingRunTextPos); + Stloc(startingPos); // goto doneLabel; BrFar(doneLabel); doneLabel = backtrack; - MarkLabel(end); + MarkLabel(backtrackingEnd); } } @@ -2570,24 +2570,24 @@ void EmitLazy(RegexNode node) } } - // We might loop any number of times. In order to ensure this loop and subsequent code sees textSpanPos + // We might loop any number of times. In order to ensure this loop and subsequent code sees sliceStaticPos // the same regardless, we always need it to contain the same value, and the easiest such value is 0. - // So, we transfer textSpanPos to runtextpos, and ensure that any path out of here has textSpanPos as 0. - TransferTextSpanPosToRunTextPos(); + // So, we transfer sliceStaticPos to pos, and ensure that any path out of here has sliceStaticPos as 0. + TransferSliceStaticPosToPos(); - LocalBuilder startingRunTextPos = DeclareInt32(); + LocalBuilder startingPos = DeclareInt32(); LocalBuilder iterationCount = DeclareInt32(); LocalBuilder sawEmpty = DeclareInt32(); Label body = DefineLabel(); Label endLoop = DefineLabel(); // iterationCount = 0; - // startingRunTextPos = runtextpos; + // startingPos = pos; // sawEmpty = 0; // false Ldc(0); Stloc(iterationCount); - Ldloc(runtextposLocal); - Stloc(startingRunTextPos); + Ldloc(pos); + Stloc(startingPos); Ldc(0); Stloc(sawEmpty); @@ -2603,13 +2603,13 @@ void EmitLazy(RegexNode node) MarkLabel(body); EmitTimeoutCheck(); - // We need to store the starting runtextpos and crawl position so that it may + // We need to store the starting pos and crawl position so that it may // be backtracked through later. This needs to be the starting position from - // the iteration we're leaving, so it's pushed before updating it to runtextpos. - // base.runstack[runstackpos++] = base.Crawlpos(); - // base.runstack[runstackpos++] = startingRunTextPos; - // base.runstack[runstackpos++] = runtextpos; - // base.runstack[runstackpos++] = sawEmpty; + // the iteration we're leaving, so it's pushed before updating it to pos. + // base.runstack[stackpos++] = base.Crawlpos(); + // base.runstack[stackpos++] = startingPos; + // base.runstack[stackpos++] = pos; + // base.runstack[stackpos++] = sawEmpty; EmitRunstackResizeIfNeeded(3); if (expressionHasCaptures) { @@ -2619,17 +2619,17 @@ void EmitLazy(RegexNode node) Call(s_crawlposMethod); }); } - EmitRunstackPush(() => Ldloc(startingRunTextPos)); - EmitRunstackPush(() => Ldloc(runtextposLocal)); + EmitRunstackPush(() => Ldloc(startingPos)); + EmitRunstackPush(() => Ldloc(pos)); EmitRunstackPush(() => Ldloc(sawEmpty)); - // Save off some state. We need to store the current runtextpos so we can compare it against - // runtextpos after the iteration, in order to determine whether the iteration was empty. Empty + // Save off some state. We need to store the current pos so we can compare it against + // pos after the iteration, in order to determine whether the iteration was empty. Empty // iterations are allowed as part of min matches, but once we've met the min quote, empty matches // are considered match failures. - // startingRunTextPos = runtextpos; - Ldloc(runtextposLocal); - Stloc(startingRunTextPos); + // startingPos = pos; + Ldloc(pos); + Stloc(startingPos); // Proactively increase the number of iterations. We do this prior to the match rather than once // we know it's successful, because we need to decrement it as part of a failed match when @@ -2649,9 +2649,9 @@ void EmitLazy(RegexNode node) doneLabel = iterationFailedLabel; // Finally, emit the child. - Debug.Assert(textSpanPos == 0); + Debug.Assert(sliceStaticPos == 0); EmitNode(node.Child(0)); - TransferTextSpanPosToRunTextPos(); // ensure textSpanPos remains 0 + TransferSliceStaticPosToPos(); // ensure sliceStaticPos remains 0 if (doneLabel == iterationFailedLabel) { doneLabel = originalDoneLabel; @@ -2669,10 +2669,10 @@ void EmitLazy(RegexNode node) // If the last iteration was empty, we need to prevent further iteration from this point // unless we backtrack out of this iteration. We can do that easily just by pretending // we reached the max iteration count. - // if (runtextpos == startingRunTextPos) sawEmpty = 1; // true + // if (pos == startingPos) sawEmpty = 1; // true Label skipSawEmptySet = DefineLabel(); - Ldloc(runtextposLocal); - Ldloc(startingRunTextPos); + Ldloc(pos); + Ldloc(startingPos); Bne(skipSawEmptySet); Ldc(1); Stloc(sawEmpty); @@ -2683,7 +2683,7 @@ void EmitLazy(RegexNode node) BrFar(endLoop); // Now handle what happens when an iteration fails. We need to reset state to what it was before just that iteration - // started. That includes resetting runtextpos and clearing out any captures from that iteration. + // started. That includes resetting pos and clearing out any captures from that iteration. MarkLabel(iterationFailedLabel); // iterationCount--; @@ -2697,17 +2697,17 @@ void EmitLazy(RegexNode node) Ldc(0); BltFar(originalDoneLabel); - // sawEmpty = base.runstack[--runstackpos]; - // runtextpos = base.runstack[--runstackpos]; - // startingRunTextPos = base.runstack[--runstackpos]; - // crawlpos = base.runstack[--runstackpos]; - // while (base.Crawlpos() > crawlpos) base.Uncapture(); + // sawEmpty = base.runstack[--stackpos]; + // pos = base.runstack[--stackpos]; + // startingPos = base.runstack[--stackpos]; + // capturepos = base.runstack[--stackpos]; + // while (base.Crawlpos() > capturepos) base.Uncapture(); EmitRunstackPop(); Stloc(sawEmpty); EmitRunstackPop(); - Stloc(runtextposLocal); + Stloc(pos); EmitRunstackPop(); - Stloc(startingRunTextPos); + Stloc(startingPos); if (expressionHasCaptures) { using RentedLocalBuilder poppedCrawlPos = RentInt32Local(); @@ -2715,7 +2715,7 @@ void EmitLazy(RegexNode node) Stloc(poppedCrawlPos); EmitUncaptureUntil(poppedCrawlPos); } - LoadTextSpanLocal(); + SliceInputSpan(); if (doneLabel == originalDoneLabel) { @@ -2738,7 +2738,7 @@ void EmitLazy(RegexNode node) { // Store the capture's state and skip the backtracking section EmitRunstackResizeIfNeeded(3); - EmitRunstackPush(() => Ldloc(startingRunTextPos)); + EmitRunstackPush(() => Ldloc(startingPos)); EmitRunstackPush(() => Ldloc(iterationCount)); EmitRunstackPush(() => Ldloc(sawEmpty)); Label skipBacktrack = DefineLabel(); @@ -2748,15 +2748,15 @@ void EmitLazy(RegexNode node) Label backtrack = DefineLabel(); MarkLabel(backtrack); - // sawEmpty = base.runstack[--runstackpos]; - // iterationCount = base.runstack[--runstackpos]; - // startingRunTextPos = base.runstack[--runstackpos]; + // sawEmpty = base.runstack[--stackpos]; + // iterationCount = base.runstack[--stackpos]; + // startingPos = base.runstack[--stackpos]; EmitRunstackPop(); Stloc(sawEmpty); EmitRunstackPop(); Stloc(iterationCount); EmitRunstackPop(); - Stloc(startingRunTextPos); + Stloc(startingPos); if (maxIterations == int.MaxValue) { @@ -2796,7 +2796,7 @@ void EmitSingleCharFixedRepeater(RegexNode node, bool emitLengthChecksIfRequired return; } - // if ((uint)(textSpanPos + iterations - 1) >= (uint)textSpan.Length) goto doneLabel; + // if ((uint)(sliceStaticPos + iterations - 1) >= (uint)slice.Length) goto doneLabel; if (emitLengthChecksIfRequired) { EmitSpanLengthCheck(iterations); @@ -2808,8 +2808,8 @@ void EmitSingleCharFixedRepeater(RegexNode node, bool emitLengthChecksIfRequired if (iterations <= MaxUnrollSize) { - // if (textSpan[textSpanPos] != c1 || - // textSpan[textSpanPos + 1] != c2 || + // if (slice[sliceStaticPos] != c1 || + // slice[sliceStaticPos + 1] != c2 || // ...) // goto doneLabel; for (int i = 0; i < iterations; i++) @@ -2819,20 +2819,20 @@ void EmitSingleCharFixedRepeater(RegexNode node, bool emitLengthChecksIfRequired } else { - // ReadOnlySpan tmp = textSpan.Slice(textSpanPos, iterations); + // ReadOnlySpan tmp = slice.Slice(sliceStaticPos, iterations); // for (int i = 0; i < tmp.Length; i++) // { // TimeoutCheck(); // if (tmp[i] != ch) goto Done; // } - // textSpanPos += iterations; + // sliceStaticPos += iterations; Label conditionLabel = DefineLabel(); Label bodyLabel = DefineLabel(); using RentedLocalBuilder spanLocal = RentReadOnlySpanCharLocal(); - Ldloca(textSpanLocal); - Ldc(textSpanPos); + Ldloca(slice); + Ldc(sliceStaticPos); Ldc(iterations); Call(s_spanSliceIntIntMethod); Stloc(spanLocal); @@ -2845,13 +2845,13 @@ void EmitSingleCharFixedRepeater(RegexNode node, bool emitLengthChecksIfRequired MarkLabel(bodyLabel); EmitTimeoutCheck(); - LocalBuilder tmpTextSpanLocal = textSpanLocal; // we want EmitSingleChar to refer to this temporary - int tmpTextSpanPos = textSpanPos; - textSpanLocal = spanLocal; - textSpanPos = 0; + LocalBuilder tmpTextSpanLocal = slice; // we want EmitSingleChar to refer to this temporary + int tmpTextSpanPos = sliceStaticPos; + slice = spanLocal; + sliceStaticPos = 0; EmitSingleChar(node, emitLengthCheck: false, offset: iterationLocal); - textSpanLocal = tmpTextSpanLocal; - textSpanPos = tmpTextSpanPos; + slice = tmpTextSpanLocal; + sliceStaticPos = tmpTextSpanPos; Ldloc(iterationLocal); Ldc(1); @@ -2864,7 +2864,7 @@ void EmitSingleCharFixedRepeater(RegexNode node, bool emitLengthChecksIfRequired Call(s_spanGetLengthMethod); BltFar(bodyLabel); - textSpanPos += iterations; + sliceStaticPos += iterations; } } @@ -2906,16 +2906,16 @@ void EmitSingleCharAtomicLoop(RegexNode node) // restriction is purely for simplicity; it could be removed in the future with additional code to // handle the unbounded case. - // int i = textSpan.Slice(textSpanPos).IndexOf(char); - if (textSpanPos > 0) + // int i = slice.Slice(sliceStaticPos).IndexOf(char); + if (sliceStaticPos > 0) { - Ldloca(textSpanLocal); - Ldc(textSpanPos); + Ldloca(slice); + Ldc(sliceStaticPos); Call(s_spanSliceIntMethod); } else { - Ldloc(textSpanLocal); + Ldloc(slice); } Ldc(node.Ch); Call(s_spanIndexOfChar); @@ -2926,12 +2926,12 @@ void EmitSingleCharAtomicLoop(RegexNode node) Ldc(0); BgeFar(atomicLoopDoneLabel); - // i = textSpan.Length - textSpanPos; - Ldloca(textSpanLocal); + // i = slice.Length - sliceStaticPos; + Ldloca(slice); Call(s_spanGetLengthMethod); - if (textSpanPos > 0) + if (sliceStaticPos > 0) { - Ldc(textSpanPos); + Ldc(sliceStaticPos); Sub(); } Stloc(iterationLocal); @@ -2947,16 +2947,16 @@ void EmitSingleCharAtomicLoop(RegexNode node) // As with the notoneloopatomic above, the unbounded constraint is purely for simplicity. Debug.Assert(numSetChars > 1); - // int i = textSpan.Slice(textSpanPos).IndexOfAny(ch1, ch2, ...); - if (textSpanPos > 0) + // int i = slice.Slice(sliceStaticPos).IndexOfAny(ch1, ch2, ...); + if (sliceStaticPos > 0) { - Ldloca(textSpanLocal); - Ldc(textSpanPos); + Ldloca(slice); + Ldc(sliceStaticPos); Call(s_spanSliceIntMethod); } else { - Ldloc(textSpanLocal); + Ldloc(slice); } switch (numSetChars) { @@ -2986,12 +2986,12 @@ void EmitSingleCharAtomicLoop(RegexNode node) Ldc(0); BgeFar(atomicLoopDoneLabel); - // i = textSpan.Length - textSpanPos; - Ldloca(textSpanLocal); + // i = slice.Length - sliceStaticPos; + Ldloca(slice); Call(s_spanGetLengthMethod); - if (textSpanPos > 0) + if (sliceStaticPos > 0) { - Ldc(textSpanPos); + Ldc(sliceStaticPos); Sub(); } Stloc(iterationLocal); @@ -3001,10 +3001,10 @@ void EmitSingleCharAtomicLoop(RegexNode node) // .* was used with RegexOptions.Singleline, which means it'll consume everything. Just jump to the end. // The unbounded constraint is the same as in the Notone case above, done purely for simplicity. - // int i = runtextend - runtextpos; - TransferTextSpanPosToRunTextPos(); - Ldloc(runtextendLocal); - Ldloc(runtextposLocal); + // int i = end - pos; + TransferSliceStaticPosToPos(); + Ldloc(end); + Ldloc(pos); Sub(); Stloc(iterationLocal); } @@ -3012,8 +3012,8 @@ void EmitSingleCharAtomicLoop(RegexNode node) { // For everything else, do a normal loop. - // Transfer text pos to runtextpos to help with bounds check elimination on the loop. - TransferTextSpanPosToRunTextPos(); + // Transfer sliceStaticPos to pos to help with bounds check elimination on the loop. + TransferSliceStaticPosToPos(); Label conditionLabel = DefineLabel(); Label bodyLabel = DefineLabel(); @@ -3028,14 +3028,14 @@ void EmitSingleCharAtomicLoop(RegexNode node) MarkLabel(bodyLabel); EmitTimeoutCheck(); - // if ((uint)i >= (uint)textSpan.Length) goto atomicLoopDoneLabel; + // if ((uint)i >= (uint)slice.Length) goto atomicLoopDoneLabel; Ldloc(iterationLocal); - Ldloca(textSpanLocal); + Ldloca(slice); Call(s_spanGetLengthMethod); BgeUnFar(atomicLoopDoneLabel); - // if (textSpan[i] != ch) goto atomicLoopDoneLabel; - Ldloca(textSpanLocal); + // if (slice[i] != ch) goto atomicLoopDoneLabel; + Ldloca(slice); Ldloc(iterationLocal); Call(s_spanGetItemMethod); LdindU2(); @@ -3093,19 +3093,19 @@ void EmitSingleCharAtomicLoop(RegexNode node) } // Now that we've completed our optional iterations, advance the text span - // and runtextpos by the number of iterations completed. + // and pos by the number of iterations completed. - // textSpan = textSpan.Slice(i); - Ldloca(textSpanLocal); + // slice = slice.Slice(i); + Ldloca(slice); Ldloc(iterationLocal); Call(s_spanSliceIntMethod); - Stloc(textSpanLocal); + Stloc(slice); - // runtextpos += i; - Ldloc(runtextposLocal); + // pos += i; + Ldloc(pos); Ldloc(iterationLocal); Add(); - Stloc(runtextposLocal); + Stloc(pos); } // Emits the code to handle a non-backtracking optional zero-or-one loop. @@ -3115,15 +3115,15 @@ void EmitAtomicSingleCharZeroOrOne(RegexNode node) Label skipUpdatesLabel = DefineLabel(); - // if ((uint)textSpanPos >= (uint)textSpan.Length) goto skipUpdatesLabel; - Ldc(textSpanPos); - Ldloca(textSpanLocal); + // if ((uint)sliceStaticPos >= (uint)slice.Length) goto skipUpdatesLabel; + Ldc(sliceStaticPos); + Ldloca(slice); Call(s_spanGetLengthMethod); BgeUnFar(skipUpdatesLabel); - // if (textSpan[textSpanPos] != ch) goto skipUpdatesLabel; - Ldloca(textSpanLocal); - Ldc(textSpanPos); + // if (slice[sliceStaticPos] != ch) goto skipUpdatesLabel; + Ldloca(slice); + Ldc(sliceStaticPos); Call(s_spanGetItemMethod); LdindU2(); if (node.IsSetFamily) @@ -3148,17 +3148,17 @@ void EmitAtomicSingleCharZeroOrOne(RegexNode node) } } - // textSpan = textSpan.Slice(1); - Ldloca(textSpanLocal); + // slice = slice.Slice(1); + Ldloca(slice); Ldc(1); Call(s_spanSliceIntMethod); - Stloc(textSpanLocal); + Stloc(slice); - // runtextpos++; - Ldloc(runtextposLocal); + // pos++; + Ldloc(pos); Ldc(1); Add(); - Stloc(runtextposLocal); + Stloc(pos); MarkLabel(skipUpdatesLabel); } @@ -3172,48 +3172,48 @@ void EmitLoop(RegexNode node) int maxIterations = node.N; bool isAtomic = node.IsAtomicByParent(); - // We might loop any number of times. In order to ensure this loop and subsequent code sees textSpanPos + // We might loop any number of times. In order to ensure this loop and subsequent code sees sliceStaticPos // the same regardless, we always need it to contain the same value, and the easiest such value is 0. - // So, we transfer textSpanPos to runtextpos, and ensure that any path out of here has textSpanPos as 0. - TransferTextSpanPosToRunTextPos(); + // So, we transfer sliceStaticPos to pos, and ensure that any path out of here has sliceStaticPos as 0. + TransferSliceStaticPosToPos(); Label originalDoneLabel = doneLabel; - LocalBuilder startingRunTextPos = DeclareInt32(); + LocalBuilder startingPos = DeclareInt32(); LocalBuilder iterationCount = DeclareInt32(); Label body = DefineLabel(); Label endLoop = DefineLabel(); // iterationCount = 0; - // startingRunTextPos = 0; + // startingPos = 0; Ldc(0); Stloc(iterationCount); Ldc(0); - Stloc(startingRunTextPos); + Stloc(startingPos); // Iteration body MarkLabel(body); EmitTimeoutCheck(); - // We need to store the starting runtextpos and crawl position so that it may + // We need to store the starting pos and crawl position so that it may // be backtracked through later. This needs to be the starting position from - // the iteration we're leaving, so it's pushed before updating it to runtextpos. + // the iteration we're leaving, so it's pushed before updating it to pos. EmitRunstackResizeIfNeeded(3); if (expressionHasCaptures) { - // base.runstack[runstackpos++] = base.Crawlpos(); + // base.runstack[stackpos++] = base.Crawlpos(); EmitRunstackPush(() => { Ldthis(); Call(s_crawlposMethod); }); } - EmitRunstackPush(() => Ldloc(startingRunTextPos)); - EmitRunstackPush(() => Ldloc(runtextposLocal)); + EmitRunstackPush(() => Ldloc(startingPos)); + EmitRunstackPush(() => Ldloc(pos)); - // Save off some state. We need to store the current runtextpos so we can compare it against - // runtextpos after the iteration, in order to determine whether the iteration was empty. Empty + // Save off some state. We need to store the current pos so we can compare it against + // pos after the iteration, in order to determine whether the iteration was empty. Empty // iterations are allowed as part of min matches, but once we've met the min quote, empty matches // are considered match failures. - // startingRunTextPos = runtextpos; - Ldloc(runtextposLocal); - Stloc(startingRunTextPos); + // startingPos = pos; + Ldloc(pos); + Stloc(startingPos); // Proactively increase the number of iterations. We do this prior to the match rather than once // we know it's successful, because we need to decrement it as part of a failed match when @@ -3233,9 +3233,9 @@ void EmitLoop(RegexNode node) doneLabel = iterationFailedLabel; // Finally, emit the child. - Debug.Assert(textSpanPos == 0); + Debug.Assert(sliceStaticPos == 0); EmitNode(node.Child(0)); - TransferTextSpanPosToRunTextPos(); // ensure textSpanPos remains 0 + TransferSliceStaticPosToPos(); // ensure sliceStaticPos remains 0 bool childBacktracks = doneLabel != iterationFailedLabel; // Loop condition. Continue iterating greedily if we've not yet reached the maximum. We also need to stop @@ -3244,10 +3244,10 @@ void EmitLoop(RegexNode node) switch ((minIterations > 0, maxIterations == int.MaxValue)) { case (true, true): - // if (runtextpos != startingRunTextPos || iterationCount < minIterations) goto body; + // if (pos != startingPos || iterationCount < minIterations) goto body; // goto endLoop; - Ldloc(runtextposLocal); - Ldloc(startingRunTextPos); + Ldloc(pos); + Ldloc(startingPos); BneFar(body); Ldloc(iterationCount); Ldc(minIterations); @@ -3256,13 +3256,13 @@ void EmitLoop(RegexNode node) break; case (true, false): - // if ((runtextpos != startingRunTextPos || iterationCount < minIterations) && iterationCount < maxIterations) goto body; + // if ((pos != startingPos || iterationCount < minIterations) && iterationCount < maxIterations) goto body; // goto endLoop; Ldloc(iterationCount); Ldc(maxIterations); BgeFar(endLoop); - Ldloc(runtextposLocal); - Ldloc(startingRunTextPos); + Ldloc(pos); + Ldloc(startingPos); BneFar(body); Ldloc(iterationCount); Ldc(minIterations); @@ -3271,19 +3271,19 @@ void EmitLoop(RegexNode node) break; case (false, true): - // if (runtextpos != startingRunTextPos) goto body; + // if (pos != startingPos) goto body; // goto endLoop; - Ldloc(runtextposLocal); - Ldloc(startingRunTextPos); + Ldloc(pos); + Ldloc(startingPos); BneFar(body); BrFar(endLoop); break; case (false, false): - // if (runtextpos == startingRunTextPos || iterationCount >= maxIterations) goto endLoop; + // if (pos == startingPos || iterationCount >= maxIterations) goto endLoop; // goto body; - Ldloc(runtextposLocal); - Ldloc(startingRunTextPos); + Ldloc(pos); + Ldloc(startingPos); BeqFar(endLoop); Ldloc(iterationCount); Ldc(maxIterations); @@ -3294,7 +3294,7 @@ void EmitLoop(RegexNode node) // Now handle what happens when an iteration fails, which could be an initial failure or it // could be while backtracking. We need to reset state to what it was before just that iteration - // started. That includes resetting runtextpos and clearing out any captures from that iteration. + // started. That includes resetting pos and clearing out any captures from that iteration. MarkLabel(iterationFailedLabel); // iterationCount--; @@ -3308,22 +3308,22 @@ void EmitLoop(RegexNode node) Ldc(0); BltFar(originalDoneLabel); - // runtextpos = base.runstack[--runstackpos]; - // startingRunTextPos = base.runstack[--runstackpos]; + // pos = base.runstack[--stackpos]; + // startingPos = base.runstack[--stackpos]; EmitRunstackPop(); - Stloc(runtextposLocal); + Stloc(pos); EmitRunstackPop(); - Stloc(startingRunTextPos); + Stloc(startingPos); if (expressionHasCaptures) { - // int poppedCrawlPos = base.runstack[--runstackpos]; + // int poppedCrawlPos = base.runstack[--stackpos]; // while (base.Crawlpos() > poppedCrawlPos) base.Uncapture(); using RentedLocalBuilder poppedCrawlPos = RentInt32Local(); EmitRunstackPop(); Stloc(poppedCrawlPos); EmitUncaptureUntil(poppedCrawlPos); } - LoadTextSpanLocal(); + SliceInputSpan(); if (minIterations > 0) { @@ -3371,30 +3371,30 @@ void EmitLoop(RegexNode node) { // Store the capture's state EmitRunstackResizeIfNeeded(3); - EmitRunstackPush(() => Ldloc(startingRunTextPos)); + EmitRunstackPush(() => Ldloc(startingPos)); EmitRunstackPush(() => Ldloc(iterationCount)); // Skip past the backtracking section - // goto end; - Label end = DefineLabel(); - BrFar(end); + // goto backtrackingEnd; + Label backtrackingEnd = DefineLabel(); + BrFar(backtrackingEnd); // Emit a backtracking section that restores the capture's state and then jumps to the previous done label Label backtrack = DefineLabel(); MarkLabel(backtrack); // iterationCount = base.runstack[--runstack]; - // startingRunTextPos = base.runstack[--runstack]; + // startingPos = base.runstack[--runstack]; EmitRunstackPop(); Stloc(iterationCount); EmitRunstackPop(); - Stloc(startingRunTextPos); + Stloc(startingPos); // goto doneLabel; BrFar(doneLabel); doneLabel = backtrack; - MarkLabel(end); + MarkLabel(backtrackingEnd); } } } @@ -3403,14 +3403,14 @@ void EmitRunstackResizeIfNeeded(int count) { Debug.Assert(count >= 1); - // if (runstackpos >= base.runstack!.Length - (count - 1)) + // if (stackpos >= base.runstack!.Length - (count - 1)) // { // Array.Resize(ref base.runstack, base.runstack.Length * 2); // } Label skipResize = DefineLabel(); - Ldloc(runstackpos); + Ldloc(stackpos); Ldthisfld(s_runstackField); Ldlen(); if (count > 1) @@ -3433,28 +3433,28 @@ void EmitRunstackResizeIfNeeded(int count) void EmitRunstackPush(Action load) { - // base.runstack[runstackpos] = load(); + // base.runstack[stackpos] = load(); Ldthisfld(s_runstackField); - Ldloc(runstackpos); + Ldloc(stackpos); load(); StelemI4(); - // runstackpos++; - Ldloc(runstackpos); + // stackpos++; + Ldloc(stackpos); Ldc(1); Add(); - Stloc(runstackpos); + Stloc(stackpos); } void EmitRunstackPop() { - // ... = base.runstack[--runstackpos]; + // ... = base.runstack[--stackpos]; Ldthisfld(s_runstackField); - Ldloc(runstackpos); + Ldloc(stackpos); Ldc(1); Sub(); - Stloc(runstackpos); - Ldloc(runstackpos); + Stloc(stackpos); + Ldloc(stackpos); LdelemI4(); } } @@ -3530,6 +3530,18 @@ private void EmitMatchCharacterClass(string charClass, bool caseInsensitive) Ldc(0); Ceq(); return; + + case RegexCharClass.WordClass: + // RegexRunner.IsWordChar(ch) + Call(s_isWordCharMethod); + return; + + case RegexCharClass.NotWordClass: + // !RegexRunner.IsWordChar(ch) + Call(s_isWordCharMethod); + Ldc(0); + Ceq(); + return; } // If we're meant to be doing a case-insensitive lookup, and if we're not using the invariant culture, diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs index 5c28f58a168a25..e915634b695755 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs @@ -207,14 +207,14 @@ private void StackPush(int i1, int i2) private int Forwardchars() => _rightToLeft ? runtextpos - runtextbeg : runtextend - runtextpos; - private char Forwardcharnext(ReadOnlySpan runtextSpan) + private char Forwardcharnext(ReadOnlySpan inputSpan) { int i = _rightToLeft ? --runtextpos : runtextpos++; - char ch = runtextSpan[i]; + char ch = inputSpan[i]; return _caseInsensitive ? _textInfo.ToLower(ch) : ch; } - private bool MatchString(string str, ReadOnlySpan runtextSpan) + private bool MatchString(string str, ReadOnlySpan inputSpan) { int c = str.Length; int pos; @@ -242,7 +242,7 @@ private bool MatchString(string str, ReadOnlySpan runtextSpan) { while (c != 0) { - if (str[--c] != runtextSpan[--pos]) + if (str[--c] != inputSpan[--pos]) { return false; } @@ -253,7 +253,7 @@ private bool MatchString(string str, ReadOnlySpan runtextSpan) TextInfo ti = _textInfo; while (c != 0) { - if (str[--c] != ti.ToLower(runtextSpan[--pos])) + if (str[--c] != ti.ToLower(inputSpan[--pos])) { return false; } @@ -270,7 +270,7 @@ private bool MatchString(string str, ReadOnlySpan runtextSpan) return true; } - private bool MatchRef(int index, int length, ReadOnlySpan runtextSpan) + private bool MatchRef(int index, int length, ReadOnlySpan inputSpan) { int pos; if (!_rightToLeft) @@ -299,7 +299,7 @@ private bool MatchRef(int index, int length, ReadOnlySpan runtextSpan) { while (c-- != 0) { - if (runtextSpan[--cmpos] != runtextSpan[--pos]) + if (inputSpan[--cmpos] != inputSpan[--pos]) { return false; } @@ -310,7 +310,7 @@ private bool MatchRef(int index, int length, ReadOnlySpan runtextSpan) TextInfo ti = _textInfo; while (c-- != 0) { - if (ti.ToLower(runtextSpan[--cmpos]) != ti.ToLower(runtextSpan[--pos])) + if (ti.ToLower(inputSpan[--cmpos]) != ti.ToLower(inputSpan[--pos])) { return false; } @@ -337,7 +337,7 @@ protected override void Go() SetOperator(_code.Codes[0]); _codepos = 0; int advance = -1; - ReadOnlySpan runtextSpan = runtext; + ReadOnlySpan inputSpan = runtext; while (true) { @@ -700,7 +700,7 @@ protected override void Go() break; case RegexCode.Bol: - if (Leftchars() > 0 && runtextSpan[runtextpos - 1] != '\n') + if (Leftchars() > 0 && inputSpan[runtextpos - 1] != '\n') { break; } @@ -708,7 +708,7 @@ protected override void Go() continue; case RegexCode.Eol: - if (Rightchars() > 0 && runtextSpan[runtextpos] != '\n') + if (Rightchars() > 0 && inputSpan[runtextpos] != '\n') { break; } @@ -764,7 +764,7 @@ protected override void Go() continue; case RegexCode.EndZ: - if (Rightchars() > 1 || Rightchars() == 1 && runtextSpan[runtextpos] != '\n') + if (Rightchars() > 1 || Rightchars() == 1 && inputSpan[runtextpos] != '\n') { break; } @@ -780,7 +780,7 @@ protected override void Go() continue; case RegexCode.One: - if (Forwardchars() < 1 || Forwardcharnext(runtextSpan) != (char)Operand(0)) + if (Forwardchars() < 1 || Forwardcharnext(inputSpan) != (char)Operand(0)) { break; } @@ -788,7 +788,7 @@ protected override void Go() continue; case RegexCode.Notone: - if (Forwardchars() < 1 || Forwardcharnext(runtextSpan) == (char)Operand(0)) + if (Forwardchars() < 1 || Forwardcharnext(inputSpan) == (char)Operand(0)) { break; } @@ -803,7 +803,7 @@ protected override void Go() else { int operand = Operand(0); - if (!RegexCharClass.CharInClass(Forwardcharnext(runtextSpan), _code.Strings[operand], ref _code.StringsAsciiLookup[operand])) + if (!RegexCharClass.CharInClass(Forwardcharnext(inputSpan), _code.Strings[operand], ref _code.StringsAsciiLookup[operand])) { break; } @@ -812,7 +812,7 @@ protected override void Go() continue; case RegexCode.Multi: - if (!MatchString(_code.Strings[Operand(0)], runtextSpan)) + if (!MatchString(_code.Strings[Operand(0)], inputSpan)) { break; } @@ -824,7 +824,7 @@ protected override void Go() int capnum = Operand(0); if (IsMatched(capnum)) { - if (!MatchRef(MatchIndex(capnum), MatchLength(capnum), runtextSpan)) + if (!MatchRef(MatchIndex(capnum), MatchLength(capnum), inputSpan)) { break; } @@ -851,7 +851,7 @@ protected override void Go() char ch = (char)Operand(0); while (c-- > 0) { - if (Forwardcharnext(runtextSpan) != ch) + if (Forwardcharnext(inputSpan) != ch) { goto BreakBackward; } @@ -871,7 +871,7 @@ protected override void Go() char ch = (char)Operand(0); while (c-- > 0) { - if (Forwardcharnext(runtextSpan) == ch) + if (Forwardcharnext(inputSpan) == ch) { goto BreakBackward; } @@ -900,7 +900,7 @@ protected override void Go() CheckTimeout(); } - if (!RegexCharClass.CharInClass(Forwardcharnext(runtextSpan), set, ref setLookup)) + if (!RegexCharClass.CharInClass(Forwardcharnext(inputSpan), set, ref setLookup)) { goto BreakBackward; } @@ -918,7 +918,7 @@ protected override void Go() for (i = len; i > 0; i--) { - if (Forwardcharnext(runtextSpan) != ch) + if (Forwardcharnext(inputSpan) != ch) { Backwardnext(); break; @@ -944,7 +944,7 @@ protected override void Go() { // We're left-to-right and case-sensitive, so we can employ the vectorized IndexOf // to search for the character. - i = runtextSpan.Slice(runtextpos, len).IndexOf(ch); + i = inputSpan.Slice(runtextpos, len).IndexOf(ch); if (i == -1) { runtextpos += len; @@ -960,7 +960,7 @@ protected override void Go() { for (i = len; i > 0; i--) { - if (Forwardcharnext(runtextSpan) == ch) + if (Forwardcharnext(inputSpan) == ch) { Backwardnext(); break; @@ -993,7 +993,7 @@ protected override void Go() CheckTimeout(); } - if (!RegexCharClass.CharInClass(Forwardcharnext(runtextSpan), set, ref setLookup)) + if (!RegexCharClass.CharInClass(Forwardcharnext(inputSpan), set, ref setLookup)) { Backwardnext(); break; @@ -1043,7 +1043,7 @@ protected override void Go() int pos = TrackPeek(1); runtextpos = pos; - if (Forwardcharnext(runtextSpan) != (char)Operand(0)) + if (Forwardcharnext(inputSpan) != (char)Operand(0)) { break; } @@ -1063,7 +1063,7 @@ protected override void Go() int pos = TrackPeek(1); runtextpos = pos; - if (Forwardcharnext(runtextSpan) == (char)Operand(0)) + if (Forwardcharnext(inputSpan) == (char)Operand(0)) { break; } @@ -1084,7 +1084,7 @@ protected override void Go() runtextpos = pos; int operand0 = Operand(0); - if (!RegexCharClass.CharInClass(Forwardcharnext(runtextSpan), _code.Strings[operand0], ref _code.StringsAsciiLookup[operand0])) + if (!RegexCharClass.CharInClass(Forwardcharnext(inputSpan), _code.Strings[operand0], ref _code.StringsAsciiLookup[operand0])) { break; } diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs index 582cb1130be7ab..49c0696776d40b 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs @@ -593,6 +593,9 @@ internal RegexNode Reduce() case Setlazy: return ReduceSet(); + case Prevent: + return ReducePrevent(); + default: return this; } @@ -1817,6 +1820,24 @@ static void ProcessNode(RegexNode node, RegexNode subsequent) return null; } + /// Optimizations for negative lookaheads/behinds. + private RegexNode ReducePrevent() + { + Debug.Assert(Type == Prevent); + Debug.Assert(ChildCount() == 1); + + // A negative lookahead/lookbehind wrapped around an empty child, i.e. (?!), is + // sometimes used as a way to insert a guaranteed no-match into the expression. + // We can reduce it to simply Nothing. + if (Child(0).Type == Empty) + { + Type = Nothing; + Children = null; + } + + return this; + } + /// /// Determines whether node can be switched to an atomic loop. Subsequent is the node /// immediately after 'node'. @@ -1894,8 +1915,8 @@ private static bool CanBeMadeAtomic(RegexNode node, RegexNode subsequent) case End: case EndZ when node.Ch != '\n': case Eol when node.Ch != '\n': - case Boundary when RegexCharClass.IsWordChar(node.Ch): - case NonBoundary when !RegexCharClass.IsWordChar(node.Ch): + case Boundary when RegexCharClass.IsBoundaryWordChar(node.Ch): + case NonBoundary when !RegexCharClass.IsBoundaryWordChar(node.Ch): case ECMABoundary when RegexCharClass.IsECMAWordChar(node.Ch): case NonECMABoundary when !RegexCharClass.IsECMAWordChar(node.Ch): return true; @@ -2273,6 +2294,7 @@ public bool IsInLoop() return false; } +#if DEBUG private string TypeName => Type switch { @@ -2390,7 +2412,6 @@ public string Description() return sb.ToString(); } -#if DEBUG [ExcludeFromCodeCoverage] public void Dump() => Debug.WriteLine(ToString()); diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs index e327952a2560ec..070acc36abcc4c 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs @@ -894,7 +894,7 @@ node.M is not (0 or RegexReplacement.LeftPortion or RegexReplacement.RightPortio throw MakeException(RegexParseError.CaptureGroupOfZero, SR.CaptureGroupOfZero); } } - else if (RegexCharClass.IsWordChar(ch)) + else if (RegexCharClass.IsBoundaryWordChar(ch)) { string capname = ScanCapname(); @@ -941,7 +941,7 @@ node.M is not (0 or RegexReplacement.LeftPortion or RegexReplacement.RightPortio throw MakeException(RegexParseError.CaptureGroupNameInvalid, SR.CaptureGroupNameInvalid); } } - else if (RegexCharClass.IsWordChar(ch)) + else if (RegexCharClass.IsBoundaryWordChar(ch)) { string uncapname = ScanCapname(); @@ -1001,7 +1001,7 @@ node.M is not (0 or RegexReplacement.LeftPortion or RegexReplacement.RightPortio throw MakeException(RegexParseError.AlternationHasMalformedReference, SR.Format(SR.AlternationHasMalformedReference, capnum.ToString())); } - else if (RegexCharClass.IsWordChar(ch)) + else if (RegexCharClass.IsBoundaryWordChar(ch)) { string capname = ScanCapname(); @@ -1352,7 +1352,7 @@ static RegexOptions RemoveIgnoreCaseIfNotEcma(RegexOptions options) // Try to parse backreference: \ - else if (angled && RegexCharClass.IsWordChar(ch)) + else if (angled && RegexCharClass.IsBoundaryWordChar(ch)) { string capname = ScanCapname(); @@ -1456,7 +1456,7 @@ private RegexNode ScanDollar() } } } - else if (angled && RegexCharClass.IsWordChar(ch)) + else if (angled && RegexCharClass.IsBoundaryWordChar(ch)) { string capname = ScanCapname(); if (CharsRight() > 0 && RightCharMoveRight() == '}') @@ -1537,7 +1537,7 @@ private string ScanCapname() while (CharsRight() > 0) { - if (!RegexCharClass.IsWordChar(RightCharMoveRight())) + if (!RegexCharClass.IsBoundaryWordChar(RightCharMoveRight())) { MoveLeft(); break; @@ -1743,7 +1743,7 @@ private char ScanCharEscape() case 'c': return ScanControl(); default: - if (!UseOptionE() && RegexCharClass.IsWordChar(ch)) + if (!UseOptionE() && RegexCharClass.IsBoundaryWordChar(ch)) { throw MakeException(RegexParseError.UnrecognizedEscape, SR.Format(SR.UnrecognizedEscape, ch)); } @@ -1769,7 +1769,7 @@ private string ParseProperty() while (CharsRight() > 0) { ch = RightCharMoveRight(); - if (!(RegexCharClass.IsWordChar(ch) || ch == '-')) + if (!(RegexCharClass.IsBoundaryWordChar(ch) || ch == '-')) { MoveLeft(); break; @@ -1885,7 +1885,7 @@ private void CountCaptures() MoveRight(); ch = RightChar(); - if (ch != '0' && RegexCharClass.IsWordChar(ch)) + if (ch != '0' && RegexCharClass.IsBoundaryWordChar(ch)) { if ((uint)(ch - '1') <= '9' - '1') { diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunner.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunner.cs index dbab82e7cfd4a2..15f33145b79838 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunner.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunner.cs @@ -521,10 +521,13 @@ protected void EnsureStorage() /// protected bool IsBoundary(int index, int startpos, int endpos) { - return (index > startpos && RegexCharClass.IsWordChar(runtext![index - 1])) != - (index < endpos && RegexCharClass.IsWordChar(runtext![index])); + return (index > startpos && RegexCharClass.IsBoundaryWordChar(runtext![index - 1])) != + (index < endpos && RegexCharClass.IsBoundaryWordChar(runtext![index])); } + /// Called to determine a char's inclusion in the \w set. + internal static bool IsWordChar(char ch) => RegexCharClass.IsWordChar(ch); + protected bool IsECMABoundary(int index, int startpos, int endpos) { return (index > startpos && RegexCharClass.IsECMAWordChar(runtext![index - 1])) != diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexMatcher.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexMatcher.cs index 21c023345c7315..c941c6840c458a 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexMatcher.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexMatcher.cs @@ -243,11 +243,11 @@ private interface ITransition } /// Compute the target state for the source state and input[i] character. - /// input string + /// input span /// The index into at which the target character lives. /// The source state [MethodImpl(MethodImplOptions.AggressiveInlining)] - private DfaMatchingState Delta(string input, int i, DfaMatchingState sourceState) where TTransition : struct, ITransition + private DfaMatchingState Delta(ReadOnlySpan input, int i, DfaMatchingState sourceState) where TTransition : struct, ITransition { TSetType[]? minterms = _builder._minterms; Debug.Assert(minterms is not null); @@ -356,10 +356,10 @@ private void DoCheckTimeout(int timeoutOccursAt) /// Find a match. /// Whether to return once we know there's a match without determining where exactly it matched. - /// The input string - /// The position to start search in the input string. - /// The non-inclusive position to end the search in the input string. - public SymbolicMatch FindMatch(bool isMatch, string input, int startat, int end) + /// The input span + /// The position to start search in the input span. + /// The non-inclusive position to end the search in the input span. + public SymbolicMatch FindMatch(bool isMatch, ReadOnlySpan input, int startat, int end) { int timeoutOccursAt = 0; if (_checkTimeout) @@ -419,11 +419,11 @@ public SymbolicMatch FindMatch(bool isMatch, string input, int startat, int end) } /// Find match end position using the original pattern, end position is known to exist. - /// input array + /// input span /// inclusive start position /// exclusive end position /// - private int FindEndPosition(string input, int exclusiveEnd, int i) + private int FindEndPosition(ReadOnlySpan input, int exclusiveEnd, int i) { int i_end = exclusiveEnd; @@ -462,7 +462,7 @@ private int FindEndPosition(string input, int exclusiveEnd, int i) /// Inner loop for FindEndPosition parameterized by an ITransition type. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool FindEndPositionDeltas(string input, ref int i, int j, ref DfaMatchingState q, ref int i_end) where TTransition : struct, ITransition + private bool FindEndPositionDeltas(ReadOnlySpan input, ref int i, int j, ref DfaMatchingState q, ref int i_end) where TTransition : struct, ITransition { do { @@ -494,11 +494,11 @@ private bool FindEndPositionDeltas(string input, ref int i, int j, } /// Walk back in reverse using the reverse pattern to find the start position of match, start position is known to exist. - /// the input string + /// the input span /// position to start walking back from, i points at the last character of the match /// do not pass this boundary when walking back /// - private int FindStartPosition(string input, int i, int match_start_boundary) + private int FindStartPosition(ReadOnlySpan input, int i, int match_start_boundary) { // Fetch the correct start state for the reverse pattern. // This depends on previous character --- which, because going backwards, is character number i+1. @@ -539,7 +539,7 @@ private int FindStartPosition(string input, int i, int match_start_boundary) // Inner loop for FindStartPosition parameterized by an ITransition type. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool FindStartPositionDeltas(string input, ref int i, int j, ref DfaMatchingState q, ref int last_start) where TTransition : struct, ITransition + private bool FindStartPositionDeltas(ReadOnlySpan input, ref int i, int j, ref DfaMatchingState q, ref int last_start) where TTransition : struct, ITransition { do { @@ -566,13 +566,13 @@ private bool FindStartPositionDeltas(string input, ref int i, int j } /// Returns NoMatchExists if no match exists. Returns -1 when i=0 and the initial state is nullable. - /// given input string + /// given input span /// input length or bounded input length /// start position /// The time at which timeout occurs, if timeouts are being checked. /// last position the initial state of was visited /// length of match when positive - private int FindFinalStatePosition(string input, int k, int i, int timeoutOccursAt, out int initialStateIndex, out int watchdog) + private int FindFinalStatePosition(ReadOnlySpan input, int k, int i, int timeoutOccursAt, out int initialStateIndex, out int watchdog) { // Get the correct start state of the dot-star pattern, which in general depends on the previous character kind in the input. uint prevCharKindId = GetCharKind(input, i - 1); @@ -651,7 +651,7 @@ private int FindFinalStatePosition(string input, int k, int i, int timeoutOccurs /// Inner loop for FindFinalStatePosition parameterized by an ITransition type. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool FindFinalStatePositionDeltas(string input, int j, ref int i, ref DfaMatchingState q, ref int watchdog, out int result) where TTransition : struct, ITransition + private bool FindFinalStatePositionDeltas(ReadOnlySpan input, int j, ref int i, ref DfaMatchingState q, ref int watchdog, out int result) where TTransition : struct, ITransition { do { @@ -682,13 +682,13 @@ private bool FindFinalStatePositionDeltas(string input, int j, ref } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private uint GetCharKind(string input, int i) + private uint GetCharKind(ReadOnlySpan input, int i) { return !_pattern._info.ContainsSomeAnchor ? CharKind.General : // The previous character kind is irrelevant when anchors are not used. GetCharKindWithAnchor(input, i); - uint GetCharKindWithAnchor(string input, int i) + uint GetCharKindWithAnchor(ReadOnlySpan input, int i) { Debug.Assert(_asciiCharKinds is not null); diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexRunnerFactory.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexRunnerFactory.cs index b0d9de74668a02..097a67ccd00326 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexRunnerFactory.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexRunnerFactory.cs @@ -89,8 +89,10 @@ protected override void InitTrackCount() { } // nop, no backtracking protected override void Go() { + ReadOnlySpan inputSpan = runtext; + // Perform the match. - SymbolicMatch pos = _matcher.FindMatch(quick, runtext!, runtextpos, runtextend); + SymbolicMatch pos = _matcher.FindMatch(quick, inputSpan, runtextpos, runtextend); if (pos.Success) { // If we successfully matched, capture the match, and then jump the current position to the end of the match. diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Threading/StackHelper.cs b/src/libraries/System.Text.RegularExpressions/src/System/Threading/StackHelper.cs index e15d49c78ff4c2..18265762dda5da 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Threading/StackHelper.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Threading/StackHelper.cs @@ -31,13 +31,6 @@ public static bool TryEnsureSufficientExecutionStack() // It does so in a way that prevents task inlining (which would defeat the purpose) but that // also plays nicely with the thread pool's sync-over-async aggressive thread injection policies. - /// Calls the provided action on the stack of a different thread pool thread. - /// The action to invoke. - public static void CallOnEmptyStack(Action action) => - Task.Run(() => action()) - .ContinueWith(t => t.GetAwaiter().GetResult(), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default) - .GetAwaiter().GetResult(); - /// Calls the provided action on the stack of a different thread pool thread. /// The type of the first argument to pass to the function. /// The action to invoke. @@ -114,21 +107,5 @@ public static TResult CallOnEmptyStack(Func func(arg1, arg2, arg3)) .ContinueWith(t => t.GetAwaiter().GetResult(), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default) .GetAwaiter().GetResult(); - - /// Calls the provided function on the stack of a different thread pool thread. - /// The type of the first argument to pass to the function. - /// The type of the second argument to pass to the function. - /// The type of the third argument to pass to the function. - /// The type of the fourth argument to pass to the function. - /// The return type of the function. - /// The function to invoke. - /// The first argument to pass to the function. - /// The second argument to pass to the function. - /// The third argument to pass to the function. - /// The fourth argument to pass to the function. - public static TResult CallOnEmptyStack(Func func, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4) => - Task.Run(() => func(arg1, arg2, arg3, arg4)) - .ContinueWith(t => t.GetAwaiter().GetResult(), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default) - .GetAwaiter().GetResult(); } } diff --git a/src/libraries/System.Text.RegularExpressions/tests/Regex.KnownPattern.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/Regex.KnownPattern.Tests.cs index b53526645705f9..7c50f4c5bf6a3f 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/Regex.KnownPattern.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/Regex.KnownPattern.Tests.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; +using System.Text.Json; using System.Threading.Tasks; using Xunit; @@ -1405,5 +1407,66 @@ public void TerminationInNonBacktrackingVsBackTracking(RegexOptions options, int // NonBacktracking needs way less than 1s Assert.False(re.Match(input).Success); } + + // + // dotnet/runtime-assets contains a set a regular expressions sourced from + // permissively-licensed packages. Validate Regex behavior with those expressions. + // + + [Theory] + [InlineData(RegexEngine.Interpreter)] + [InlineData(RegexEngine.Compiled)] + public async Task PatternsDataSet_ConstructRegexForAll(RegexEngine engine) + { + foreach (DataSetExpression exp in s_patternsDataSet.Value) + { + await RegexHelpers.GetRegexAsync(engine, exp.Pattern, exp.Options); + } + } + + private static Lazy s_patternsDataSet = new Lazy(() => + { + using Stream json = File.OpenRead("Regex_RealWorldPatterns.json"); + return JsonSerializer.Deserialize(json, new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip }).Distinct().ToArray(); + }); + + private sealed class DataSetExpression : IEquatable + { + public int Count { get; set; } + public RegexOptions Options { get; set; } + public string Pattern { get; set; } + + public bool Equals(DataSetExpression? other) => + other is not null && + other.Pattern == Pattern && + (Options & ~RegexOptions.Compiled) == (other.Options & ~RegexOptions.Compiled); // Compiled doesn't affect semantics, so remove it from equality for our purposes + } + +#if NETCOREAPP + [OuterLoop("Takes many seconds")] + [Fact] + public async Task PatternsDataSet_ConstructRegexForAll_NonBacktracking() + { + foreach (DataSetExpression exp in s_patternsDataSet.Value) + { + try + { + await RegexHelpers.GetRegexAsync(RegexEngine.NonBacktracking, exp.Pattern, exp.Options); + } + catch (Exception e) when (e.Message.Contains(nameof(RegexOptions.NonBacktracking))) { } + } + } + + [OuterLoop("Takes minutes to generate and compile thousands of expressions")] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] // consumes a lot of memory + public void PatternsDataSet_ConstructRegexForAll_SourceGenerated() + { + Parallel.ForEach(s_patternsDataSet.Value.Chunk(50), chunk => + { + RegexHelpers.GetRegexesAsync(RegexEngine.SourceGenerated, + chunk.Select(r => (r.Pattern, (RegexOptions?)r.Options, (TimeSpan?)null)).ToArray()).GetAwaiter().GetResult(); + }); + } +#endif } } diff --git a/src/libraries/System.Text.RegularExpressions/tests/RegexCultureTests.cs b/src/libraries/System.Text.RegularExpressions/tests/RegexCultureTests.cs index 4251de2c1504b9..65dc2a847456b0 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/RegexCultureTests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/RegexCultureTests.cs @@ -342,6 +342,7 @@ public void Match_InvariantCulture_None_vs_Compiled() /// This test is to make sure that the generated IgnoreCaseRelation table for NonBacktracking does not need to be updated. /// It would need to be updated/regenerated if this test fails. /// + [ActiveIssue("https://github.com/dotnet/runtime/issues/60753")] [OuterLoop("May take several seconds due to large number of cultures tested")] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] [Fact] diff --git a/src/libraries/System.Text.RegularExpressions/tests/RegexGeneratorHelper.netcoreapp.cs b/src/libraries/System.Text.RegularExpressions/tests/RegexGeneratorHelper.netcoreapp.cs index 294cbfe3fde06a..f4904dc9c73247 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/RegexGeneratorHelper.netcoreapp.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/RegexGeneratorHelper.netcoreapp.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; @@ -109,9 +110,13 @@ internal static async Task SourceGenRegexAsync( .AddSolution(SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create())) .AddProject("Test", "test.dll", "C#") .WithMetadataReferences(s_refs) - .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) - .WithNullableContextOptions(NullableContextOptions.Enable)) - .WithParseOptions(new CSharpParseOptions(LanguageVersion.Preview)) + .WithCompilationOptions( + new CSharpCompilationOptions( + OutputKind.DynamicallyLinkedLibrary, + warningLevel: 9999, // docs recommend using "9999" to catch all warnings now and in the future + specificDiagnosticOptions: ImmutableDictionary.Empty.Add("SYSLIB1045", ReportDiagnostic.Hidden)) // regex with limited support + .WithNullableContextOptions(NullableContextOptions.Enable)) + .WithParseOptions(new CSharpParseOptions(LanguageVersion.Preview, DocumentationMode.Diagnose)) .AddDocument("RegexGenerator.g.cs", SourceText.From("// Empty", Encoding.UTF8)).Project; Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution)); @@ -123,7 +128,7 @@ internal static async Task SourceGenRegexAsync( // Run the generator GeneratorDriverRunResult generatorResults = s_generatorDriver.RunGenerators(comp!, cancellationToken).GetRunResult(); - ImmutableArray generatorDiagnostics = generatorResults.Diagnostics.RemoveAll(d => d.Severity <= DiagnosticSeverity.Info); + ImmutableArray generatorDiagnostics = generatorResults.Diagnostics.RemoveAll(d => d.Severity <= DiagnosticSeverity.Hidden); if (generatorDiagnostics.Length != 0) { throw new ArgumentException( diff --git a/src/libraries/System.Text.RegularExpressions/tests/System.Text.RegularExpressions.Tests.csproj b/src/libraries/System.Text.RegularExpressions/tests/System.Text.RegularExpressions.Tests.csproj index b9e4b5e2426fb1..9641f9665a175c 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/System.Text.RegularExpressions.Tests.csproj +++ b/src/libraries/System.Text.RegularExpressions/tests/System.Text.RegularExpressions.Tests.csproj @@ -38,6 +38,7 @@ + @@ -57,4 +58,7 @@ + + + diff --git a/src/libraries/System.Threading.AccessControl/src/CompatibilitySuppressions.xml b/src/libraries/System.Threading.AccessControl/src/CompatibilitySuppressions.xml index 399552ac360a06..fa8650e58d9337 100644 --- a/src/libraries/System.Threading.AccessControl/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Threading.AccessControl/src/CompatibilitySuppressions.xml @@ -72,20 +72,4 @@ lib/netstandard2.0/System.Threading.AccessControl.dll lib/net462/System.Threading.AccessControl.dll - - PKV006 - .NETFramework,Version=v4.6 - - - PKV006 - .NETStandard,Version=v1.3 - - - PKV007 - .NETFramework,Version=v4.6-win - - - PKV007 - .NETStandard,Version=v1.3-win - \ No newline at end of file diff --git a/src/libraries/System.Threading.Channels/src/CompatibilitySuppressions.xml b/src/libraries/System.Threading.Channels/src/CompatibilitySuppressions.xml deleted file mode 100644 index e322081124afec..00000000000000 --- a/src/libraries/System.Threading.Channels/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - .NETStandard,Version=v1.3 - - \ No newline at end of file diff --git a/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj b/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj index 9e0d1804cedf13..d7642f10340275 100644 --- a/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj +++ b/src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csproj @@ -2,6 +2,9 @@ $(NetCoreAppCurrent);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) enable + true + + true APIs to help manage rate limiting. Commonly Used Types: diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/CompatibilitySuppressions.xml b/src/libraries/System.Threading.Tasks.Dataflow/src/CompatibilitySuppressions.xml deleted file mode 100644 index 685e515a8ac11e..00000000000000 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/CompatibilitySuppressions.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - PKV006 - .NETStandard,Version=v1.0 - - - PKV006 - .NETStandard,Version=v1.1 - - - PKV006 - .NETPortable,Version=v0.0,Profile=Profile111 - - \ No newline at end of file diff --git a/src/libraries/System.Windows.Extensions/src/CompatibilitySuppressions.xml b/src/libraries/System.Windows.Extensions/src/CompatibilitySuppressions.xml index c11ac9d327ad33..d05c6dd5c6fbc4 100644 --- a/src/libraries/System.Windows.Extensions/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Windows.Extensions/src/CompatibilitySuppressions.xml @@ -1,12 +1,12 @@ - + - + PKV006 - .NETCoreApp,Version=v3.0 + .NETCoreApp,Version=v3.1 PKV007 - .NETCoreApp,Version=v3.0-win + .NETCoreApp,Version=v3.1-win - + \ No newline at end of file diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index c3f4fc1549607b..9051d411c55028 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -499,7 +499,7 @@ %(Identity) - $(ExecXHarnessCmd) wasm $(XHarnessCommand) --app=. --engine=V8 --engine-arg=--stack-trace-limit=1000 --js-file=main.js --output-directory=$(XHarnessOutput) -- --run %(FileName).dll + $(ExecXHarnessCmd) wasm $(XHarnessCommand) --app=. --engine=V8 --engine-arg=--stack-trace-limit=1000 --js-file=test-main.js --output-directory=$(XHarnessOutput) -- --run %(FileName).dll diff --git a/src/libraries/shims/ApiCompatBaseline.PreviousNetCoreApp.txt b/src/libraries/shims/ApiCompatBaseline.PreviousNetCoreApp.txt index 0fdeb001ef9d87..f408c36427e63e 100644 --- a/src/libraries/shims/ApiCompatBaseline.PreviousNetCoreApp.txt +++ b/src/libraries/shims/ApiCompatBaseline.PreviousNetCoreApp.txt @@ -1,57 +1,22 @@ Compat issues with assembly mscorlib: -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.Beep()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.BufferHeight.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.BufferWidth.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.CursorSize.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.CursorVisible.set(System.Boolean)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.GetCursorPosition()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.OpenStandardInput()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.OpenStandardInput(System.Int32)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.Read()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.ReadKey()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.ReadKey(System.Boolean)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.ReadLine()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.ResetColor()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.SetCursorPosition(System.Int32, System.Int32)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.SetIn(System.IO.TextReader)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.Title.set(System.String)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.WindowHeight.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.WindowWidth.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute' exists on 'System.Delegate.CreateDelegate(System.Type, System.Type, System.String)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute' exists on 'System.Delegate.CreateDelegate(System.Type, System.Type, System.String, System.Boolean)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute' exists on 'System.Delegate.CreateDelegate(System.Type, System.Type, System.String, System.Boolean, System.Boolean)' in the contract but not the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Type.GetMember(System.String, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Type.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Type.GetMembers()' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Type.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.IReflect.GetMember(System.String, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.IReflect.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.TypeDelegator.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.TypeDelegator.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.EnumBuilder.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.EnumBuilder.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.GenericTypeParameterBuilder.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.GenericTypeParameterBuilder.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.TypeBuilder.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.TypeBuilder.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' exists on 'System.Runtime.InteropServices.Marshal.AddRef(System.IntPtr)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' exists on 'System.Runtime.InteropServices.Marshal.QueryInterface(System.IntPtr, System.Guid, System.IntPtr)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' exists on 'System.Runtime.InteropServices.Marshal.Release(System.IntPtr)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.Aes' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.AsymmetricKeyExchangeDeformatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.AsymmetricKeyExchangeFormatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.AsymmetricSignatureDeformatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.AsymmetricSignatureFormatter' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.CryptoConfig' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DeriveBytes' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DES' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DSA' in the contract but not the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.DSA.Create()' changed from '[UnsupportedOSPlatformAttribute("ios")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.DSA.Create(System.Int32)' changed from '[UnsupportedOSPlatformAttribute("ios")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.DSA.Create(System.Security.Cryptography.DSAParameters)' changed from '[UnsupportedOSPlatformAttribute("ios")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DSASignatureDeformatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DSASignatureFormatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.MaskGenerationMethod' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.PKCS1MaskGenerationMethod' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RandomNumberGenerator.Create(System.String)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RC2' in the contract but not the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.RC2.Create()' changed from '[UnsupportedOSPlatformAttribute("android")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.Rijndael' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RSA' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RSAEncryptionPadding' in the contract but not the implementation. @@ -63,42 +28,8 @@ CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatfo CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.SignatureDescription' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.TripleDES' in the contract but not the implementation. Compat issues with assembly netstandard: -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.Beep()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.BufferHeight.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.BufferWidth.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.CursorSize.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.CursorVisible.set(System.Boolean)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.GetCursorPosition()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.OpenStandardInput()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.OpenStandardInput(System.Int32)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.Read()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.ReadKey()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.ReadKey(System.Boolean)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.ReadLine()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.ResetColor()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.SetCursorPosition(System.Int32, System.Int32)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.SetIn(System.IO.TextReader)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.Title.set(System.String)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.WindowHeight.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.WindowWidth.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute' exists on 'System.Delegate.CreateDelegate(System.Type, System.Type, System.String)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute' exists on 'System.Delegate.CreateDelegate(System.Type, System.Type, System.String, System.Boolean)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute' exists on 'System.Delegate.CreateDelegate(System.Type, System.Type, System.String, System.Boolean, System.Boolean)' in the contract but not the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Type.GetMember(System.String, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Type.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Type.GetMembers()' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Type.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.ComponentModel.DesignerAttribute' on 'System.ComponentModel.IComponent' changed from '[DesignerAttribute("System.ComponentModel.Design.ComponentDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]' in the contract to '[DesignerAttribute("System.ComponentModel.Design.ComponentDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]' in the implementation. -CannotRemoveAttribute : Attribute 'System.ComponentModel.DesignerAttribute' exists on 'System.ComponentModel.MarshalByValueComponent' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.ComponentModel.TypeDescriptionProvider.CreateInstance(System.IServiceProvider, System.Type, System.Type[], System.Object[])' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.ComponentModel.TypeDescriptor.CreateInstance(System.IServiceProvider, System.Type, System.Type[], System.Object[])' in the contract but not the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' on 'System.Diagnostics.Process.MaxWorkingSet.set(System.IntPtr)' changed from '[SupportedOSPlatformAttribute("windows")]' in the contract to '[SupportedOSPlatformAttribute("freebsd")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' on 'System.Diagnostics.Process.MinWorkingSet.set(System.IntPtr)' changed from '[SupportedOSPlatformAttribute("windows")]' in the contract to '[SupportedOSPlatformAttribute("freebsd")]' in the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.AllowNullAttribute' exists on parameter 'value' on member 'System.Net.Cookie.Name.set(System.String)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Net.HttpListener.ExtendedProtectionPolicy.set(System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Net.HttpListenerResponse.Abort()' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Net.HttpListenerResponse.Close()' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Net.HttpListenerResponse.Close(System.Byte[], System.Boolean)' in the contract but not the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' on 'System.Diagnostics.Process.MaxWorkingSet.set(System.IntPtr)' changed from '[SupportedOSPlatformAttribute("freebsd")]' in the contract to '[SupportedOSPlatformAttribute("freebsd")]' in the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' on 'System.Diagnostics.Process.MinWorkingSet.set(System.IntPtr)' changed from '[SupportedOSPlatformAttribute("freebsd")]' in the contract to '[SupportedOSPlatformAttribute("freebsd")]' in the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'System.Numerics.Vector.CopyTo(System.Span)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'System.Numerics.Vector.CopyTo(System.Span)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'System.Numerics.Vector.CopyTo(T[])' in the contract but not the implementation. @@ -112,31 +43,17 @@ CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAtt CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'System.Numerics.Vector.ToString(System.String, System.IFormatProvider)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'System.Numerics.Vector.TryCopyTo(System.Span)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'System.Numerics.Vector.TryCopyTo(System.Span)' in the contract but not the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.IReflect.GetMember(System.String, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.IReflect.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.TypeDelegator.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.TypeDelegator.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.EnumBuilder.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.EnumBuilder.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.GenericTypeParameterBuilder.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.GenericTypeParameterBuilder.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.TypeBuilder.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.TypeBuilder.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Runtime.CompilerServices.AsyncMethodBuilderAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' exists on 'System.Runtime.InteropServices.Marshal.AddRef(System.IntPtr)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' exists on 'System.Runtime.InteropServices.Marshal.QueryInterface(System.IntPtr, System.Guid, System.IntPtr)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' exists on 'System.Runtime.InteropServices.Marshal.Release(System.IntPtr)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.Aes' in the contract but not the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.AesCcm' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.AesGcm' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.AsymmetricKeyExchangeDeformatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.AsymmetricKeyExchangeFormatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.AsymmetricSignatureDeformatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.AsymmetricSignatureFormatter' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.CryptoConfig' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DeriveBytes' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DES' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DSA' in the contract but not the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.DSA.Create()' changed from '[UnsupportedOSPlatformAttribute("ios")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.DSA.Create(System.Int32)' changed from '[UnsupportedOSPlatformAttribute("ios")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.DSA.Create(System.Security.Cryptography.DSAParameters)' changed from '[UnsupportedOSPlatformAttribute("ios")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DSASignatureDeformatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DSASignatureFormatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.ECCurve' in the contract but not the implementation. @@ -147,6 +64,7 @@ CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatfo CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.PKCS1MaskGenerationMethod' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RandomNumberGenerator.Create(System.String)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RC2' in the contract but not the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.RC2.Create()' changed from '[UnsupportedOSPlatformAttribute("android")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.Rijndael' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RSA' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RSAEncryptionPadding' in the contract but not the implementation. @@ -158,42 +76,8 @@ CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatfo CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.SignatureDescription' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.TripleDES' in the contract but not the implementation. Compat issues with assembly System: -CannotChangeAttribute : Attribute 'System.ComponentModel.DesignerAttribute' on 'System.ComponentModel.IComponent' changed from '[DesignerAttribute("System.ComponentModel.Design.ComponentDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]' in the contract to '[DesignerAttribute("System.ComponentModel.Design.ComponentDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]' in the implementation. -CannotRemoveAttribute : Attribute 'System.ComponentModel.DesignerAttribute' exists on 'System.ComponentModel.MarshalByValueComponent' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.ComponentModel.TypeDescriptionProvider.CreateInstance(System.IServiceProvider, System.Type, System.Type[], System.Object[])' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.ComponentModel.TypeDescriptor.CreateInstance(System.IServiceProvider, System.Type, System.Type[], System.Object[])' in the contract but not the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' on 'System.Diagnostics.Process.MaxWorkingSet.set(System.IntPtr)' changed from '[SupportedOSPlatformAttribute("windows")]' in the contract to '[SupportedOSPlatformAttribute("freebsd")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' on 'System.Diagnostics.Process.MinWorkingSet.set(System.IntPtr)' changed from '[SupportedOSPlatformAttribute("windows")]' in the contract to '[SupportedOSPlatformAttribute("freebsd")]' in the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.AllowNullAttribute' exists on parameter 'value' on member 'System.Net.Cookie.Name.set(System.String)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Net.HttpListener.ExtendedProtectionPolicy.set(System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Net.HttpListenerResponse.Abort()' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Net.HttpListenerResponse.Close()' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Net.HttpListenerResponse.Close(System.Byte[], System.Boolean)' in the contract but not the implementation. -Compat issues with assembly System.ComponentModel.Primitives: -CannotChangeAttribute : Attribute 'System.ComponentModel.DesignerAttribute' on 'System.ComponentModel.IComponent' changed from '[DesignerAttribute("System.ComponentModel.Design.ComponentDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]' in the contract to '[DesignerAttribute("System.ComponentModel.Design.ComponentDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]' in the implementation. -Compat issues with assembly System.ComponentModel.TypeConverter: -CannotRemoveAttribute : Attribute 'System.ComponentModel.DesignerAttribute' exists on 'System.ComponentModel.MarshalByValueComponent' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.ComponentModel.TypeDescriptionProvider.CreateInstance(System.IServiceProvider, System.Type, System.Type[], System.Object[])' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.ComponentModel.TypeDescriptor.CreateInstance(System.IServiceProvider, System.Type, System.Type[], System.Object[])' in the contract but not the implementation. -Compat issues with assembly System.Console: -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.Beep()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.BufferHeight.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.BufferWidth.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.CursorSize.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.CursorVisible.set(System.Boolean)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.GetCursorPosition()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.OpenStandardInput()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.OpenStandardInput(System.Int32)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.Read()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.ReadKey()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.ReadKey(System.Boolean)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.ReadLine()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.ResetColor()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.SetCursorPosition(System.Int32, System.Int32)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.SetIn(System.IO.TextReader)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.Title.set(System.String)' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.WindowHeight.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Console.WindowWidth.get()' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' on 'System.Diagnostics.Process.MaxWorkingSet.set(System.IntPtr)' changed from '[SupportedOSPlatformAttribute("freebsd")]' in the contract to '[SupportedOSPlatformAttribute("freebsd")]' in the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' on 'System.Diagnostics.Process.MinWorkingSet.set(System.IntPtr)' changed from '[SupportedOSPlatformAttribute("freebsd")]' in the contract to '[SupportedOSPlatformAttribute("freebsd")]' in the implementation. Compat issues with assembly System.Core: CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.Aes' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.ECCurve' in the contract but not the implementation. @@ -201,17 +85,8 @@ CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatfo CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.ECDsa' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.ECParameters' in the contract but not the implementation. Compat issues with assembly System.Diagnostics.Process: -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' on 'System.Diagnostics.Process.MaxWorkingSet.set(System.IntPtr)' changed from '[SupportedOSPlatformAttribute("windows")]' in the contract to '[SupportedOSPlatformAttribute("freebsd")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' on 'System.Diagnostics.Process.MinWorkingSet.set(System.IntPtr)' changed from '[SupportedOSPlatformAttribute("windows")]' in the contract to '[SupportedOSPlatformAttribute("freebsd")]' in the implementation. -Compat issues with assembly System.Net: -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.AllowNullAttribute' exists on parameter 'value' on member 'System.Net.Cookie.Name.set(System.String)' in the contract but not the implementation. -Compat issues with assembly System.Net.HttpListener: -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Net.HttpListener.ExtendedProtectionPolicy.set(System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Net.HttpListenerResponse.Abort()' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Net.HttpListenerResponse.Close()' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Net.HttpListenerResponse.Close(System.Byte[], System.Boolean)' in the contract but not the implementation. -Compat issues with assembly System.Net.Primitives: -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.AllowNullAttribute' exists on parameter 'value' on member 'System.Net.Cookie.Name.set(System.String)' in the contract but not the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' on 'System.Diagnostics.Process.MaxWorkingSet.set(System.IntPtr)' changed from '[SupportedOSPlatformAttribute("freebsd")]' in the contract to '[SupportedOSPlatformAttribute("freebsd")]' in the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' on 'System.Diagnostics.Process.MinWorkingSet.set(System.IntPtr)' changed from '[SupportedOSPlatformAttribute("freebsd")]' in the contract to '[SupportedOSPlatformAttribute("freebsd")]' in the implementation. Compat issues with assembly System.Numerics.Vectors: CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'System.Numerics.Vector.CopyTo(System.Span)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'System.Numerics.Vector.CopyTo(System.Span)' in the contract but not the implementation. @@ -226,53 +101,22 @@ CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAtt CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'System.Numerics.Vector.ToString(System.String, System.IFormatProvider)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'System.Numerics.Vector.TryCopyTo(System.Span)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'System.Numerics.Vector.TryCopyTo(System.Span)' in the contract but not the implementation. -Compat issues with assembly System.ObjectModel: -TypesMustExist : Type 'System.Collections.ObjectModel.ReadOnlyDictionary' does not exist in the implementation but it does exist in the contract. -Compat issues with assembly System.Reflection.Emit: -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.EnumBuilder.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.EnumBuilder.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.GenericTypeParameterBuilder.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.GenericTypeParameterBuilder.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.TypeBuilder.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.Emit.TypeBuilder.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -Compat issues with assembly System.Runtime: -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute' exists on 'System.Delegate.CreateDelegate(System.Type, System.Type, System.String)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute' exists on 'System.Delegate.CreateDelegate(System.Type, System.Type, System.String, System.Boolean)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute' exists on 'System.Delegate.CreateDelegate(System.Type, System.Type, System.String, System.Boolean, System.Boolean)' in the contract but not the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Type.GetMember(System.String, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Type.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Type.GetMembers()' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Type.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.GenericParameter | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.GenericParameter | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Struct, Inherited=false)]' in the implementation. -CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Constructor | AttributeTargets.Method, Inherited=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Method, Inherited=false)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.IReflect.GetMember(System.String, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.IReflect.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.TypeDelegator.GetMember(System.String, System.Reflection.MemberTypes, System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on 'System.Reflection.TypeDelegator.GetMembers(System.Reflection.BindingFlags)' changed from '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.All)]' in the contract to '[DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties)]' in the implementation. -CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Runtime.CompilerServices.AsyncMethodBuilderAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the implementation. -CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Runtime.Versioning.SupportedOSPlatformAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Enum | AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Module | AttributeTargets.Property | AttributeTargets.Struct, AllowMultiple=true, Inherited=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Enum | AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Module | AttributeTargets.Property | AttributeTargets.Struct, AllowMultiple=true, Inherited=false)]' in the implementation. -CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Enum | AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Module | AttributeTargets.Property | AttributeTargets.Struct, AllowMultiple=true, Inherited=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Enum | AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Module | AttributeTargets.Property | AttributeTargets.Struct, AllowMultiple=true, Inherited=false)]' in the implementation. -Compat issues with assembly System.Runtime.InteropServices: -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' exists on 'System.Runtime.InteropServices.ComWrappers' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' exists on 'System.Runtime.InteropServices.Marshal.AddRef(System.IntPtr)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' exists on 'System.Runtime.InteropServices.Marshal.QueryInterface(System.IntPtr, System.Guid, System.IntPtr)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.SupportedOSPlatformAttribute' exists on 'System.Runtime.InteropServices.Marshal.Release(System.IntPtr)' in the contract but not the implementation. Compat issues with assembly System.Runtime.Intrinsics: MembersMustExist : Member 'public System.Runtime.Intrinsics.Vector128 System.Runtime.Intrinsics.Vector128.As(System.Runtime.Intrinsics.Vector128)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Runtime.Intrinsics.Vector256 System.Runtime.Intrinsics.Vector256.As(System.Runtime.Intrinsics.Vector256)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Runtime.Intrinsics.Vector64 System.Runtime.Intrinsics.Vector64.As(System.Runtime.Intrinsics.Vector64)' does not exist in the implementation but it does exist in the contract. Compat issues with assembly System.Security.Cryptography.Algorithms: CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.Aes' in the contract but not the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.AesCcm' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. -CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.AesGcm' changed from '[UnsupportedOSPlatformAttribute("browser")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.AsymmetricKeyExchangeDeformatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.AsymmetricKeyExchangeFormatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.AsymmetricSignatureDeformatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.AsymmetricSignatureFormatter' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.CryptoConfig' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DeriveBytes' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DES' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DSA' in the contract but not the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.DSA.Create()' changed from '[UnsupportedOSPlatformAttribute("ios")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.DSA.Create(System.Int32)' changed from '[UnsupportedOSPlatformAttribute("ios")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.DSA.Create(System.Security.Cryptography.DSAParameters)' changed from '[UnsupportedOSPlatformAttribute("ios")]' in the contract to '[UnsupportedOSPlatformAttribute("browser")]' in the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DSASignatureDeformatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.DSASignatureFormatter' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.ECCurve' in the contract but not the implementation. @@ -283,6 +127,7 @@ CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatfo CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.PKCS1MaskGenerationMethod' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RandomNumberGenerator.Create(System.String)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RC2' in the contract but not the implementation. +CannotChangeAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' on 'System.Security.Cryptography.RC2.Create()' changed from '[UnsupportedOSPlatformAttribute("android")]' in the contract to '[UnsupportedOSPlatformAttribute("android")]' in the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.Rijndael' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RSA' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RSAEncryptionPadding' in the contract but not the implementation. @@ -293,16 +138,4 @@ CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatfo CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.RSASignaturePadding' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.SignatureDescription' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.TripleDES' in the contract but not the implementation. -Compat issues with assembly System.Text.Json: -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' exists on parameter 'returnType' on member 'System.Text.Json.JsonSerializer.Deserialize(System.ReadOnlySpan, System.Type, System.Text.Json.JsonSerializerOptions)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' exists on parameter 'returnType' on member 'System.Text.Json.JsonSerializer.Deserialize(System.String, System.Type, System.Text.Json.JsonSerializerOptions)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' exists on parameter 'returnType' on member 'System.Text.Json.JsonSerializer.Deserialize(System.Text.Json.Utf8JsonReader, System.Type, System.Text.Json.JsonSerializerOptions)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' exists on parameter 'returnType' on member 'System.Text.Json.JsonSerializer.DeserializeAsync(System.IO.Stream, System.Type, System.Text.Json.JsonSerializerOptions, System.Threading.CancellationToken)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' exists on parameter 'inputType' on member 'System.Text.Json.JsonSerializer.Serialize(System.Object, System.Type, System.Text.Json.JsonSerializerOptions)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' exists on parameter 'inputType' on member 'System.Text.Json.JsonSerializer.Serialize(System.Text.Json.Utf8JsonWriter, System.Object, System.Type, System.Text.Json.JsonSerializerOptions)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' exists on parameter 'inputType' on member 'System.Text.Json.JsonSerializer.SerializeAsync(System.IO.Stream, System.Object, System.Type, System.Text.Json.JsonSerializerOptions, System.Threading.CancellationToken)' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' exists on parameter 'inputType' on member 'System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(System.Object, System.Type, System.Text.Json.JsonSerializerOptions)' in the contract but not the implementation. -CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Text.Json.Serialization.JsonConverterAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Struct, AllowMultiple=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Property | AttributeTargets.Struct, AllowMultiple=false)]' in the implementation. -Compat issues with assembly System.Threading.Tasks.Extensions: -CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Runtime.CompilerServices.AsyncMethodBuilderAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the implementation. -Total Issues: 287 +Total Issues: 132 diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 592a83a1a93149..cc334885232b74 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -13,7 +13,8 @@ false - + + @@ -21,6 +22,12 @@ + + + + + + @@ -39,6 +46,11 @@ + + + + + @@ -223,6 +235,15 @@ + + + + + + + + + diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs index 5ad4ea2e23cead..4187cfcb829089 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs @@ -17,7 +17,9 @@ internal partial class TimerQueue { private static List? s_scheduledTimers; private static List? s_scheduledTimersToFire; + private static long s_shortestDueTimeMs = long.MaxValue; + // this means that it's in the s_scheduledTimers collection, not that it's the one which would run on the next TimeoutCallback private bool _isScheduled; private long _scheduledDueTimeMs; @@ -27,24 +29,25 @@ private TimerQueue(int id) [DynamicDependency("TimeoutCallback")] // The id argument is unused in netcore + // This replaces the current pending setTimeout with shorter one [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern void SetTimeout(int timeout, int id); // Called by mini-wasm.c:mono_set_timeout_exec private static void TimeoutCallback() { - int shortestWaitDurationMs = PumpTimerQueue(); + // always only have one scheduled at a time + s_shortestDueTimeMs = long.MaxValue; - if (shortestWaitDurationMs != int.MaxValue) - { - SetTimeout((int)shortestWaitDurationMs, 0); - } + long currentTimeMs = TickCount64; + ReplaceNextSetTimeout(PumpTimerQueue(currentTimeMs), currentTimeMs); } + // this is called with shortest of timers scheduled on the particular TimerQueue private bool SetTimer(uint actualDuration) { Debug.Assert((int)actualDuration >= 0); - long dueTimeMs = TickCount64 + (int)actualDuration; + long currentTimeMs = TickCount64; if (!_isScheduled) { s_scheduledTimers ??= new List(Instances.Length); @@ -52,24 +55,65 @@ private bool SetTimer(uint actualDuration) s_scheduledTimers.Add(this); _isScheduled = true; } - _scheduledDueTimeMs = dueTimeMs; - SetTimeout((int)actualDuration, 0); + + _scheduledDueTimeMs = currentTimeMs + (int)actualDuration; + + ReplaceNextSetTimeout(ShortestDueTime(), currentTimeMs); return true; } - private static int PumpTimerQueue() + // shortest time of all TimerQueues + private static void ReplaceNextSetTimeout(long shortestDueTimeMs, long currentTimeMs) { - if (s_scheduledTimersToFire == null) + if (shortestDueTimeMs == int.MaxValue) + { + return; + } + + // this also covers s_shortestDueTimeMs = long.MaxValue when none is scheduled + if (s_shortestDueTimeMs > shortestDueTimeMs) + { + s_shortestDueTimeMs = shortestDueTimeMs; + int shortestWait = Math.Max((int)(shortestDueTimeMs - currentTimeMs), 0); + // this would cancel the previous schedule and create shorter one + // it is expensive call + SetTimeout(shortestWait, 0); + } + } + + private static long ShortestDueTime() + { + if (s_scheduledTimers == null) { return int.MaxValue; } + long shortestDueTimeMs = long.MaxValue; + var timers = s_scheduledTimers!; + for (int i = timers.Count - 1; i >= 0; --i) + { + TimerQueue timer = timers[i]; + if (timer._scheduledDueTimeMs < shortestDueTimeMs) + { + shortestDueTimeMs = timer._scheduledDueTimeMs; + } + } + + return shortestDueTimeMs; + } + + private static long PumpTimerQueue(long currentTimeMs) + { + if (s_scheduledTimersToFire == null) + { + return ShortestDueTime(); + } + List timersToFire = s_scheduledTimersToFire!; List timers; timers = s_scheduledTimers!; - long currentTimeMs = TickCount64; - int shortestWaitDurationMs = int.MaxValue; + long shortestDueTimeMs = int.MaxValue; for (int i = timers.Count - 1; i >= 0; --i) { TimerQueue timer = timers[i]; @@ -88,9 +132,9 @@ private static int PumpTimerQueue() continue; } - if (waitDurationMs < shortestWaitDurationMs) + if (timer._scheduledDueTimeMs < shortestDueTimeMs) { - shortestWaitDurationMs = (int)waitDurationMs; + shortestDueTimeMs = timer._scheduledDueTimeMs; } } @@ -103,7 +147,7 @@ private static int PumpTimerQueue() timersToFire.Clear(); } - return shortestWaitDurationMs; + return shortestDueTimeMs; } } } diff --git a/src/mono/cmake/config.h.in b/src/mono/cmake/config.h.in index 648ad60dd0494a..30f429a7893f0c 100644 --- a/src/mono/cmake/config.h.in +++ b/src/mono/cmake/config.h.in @@ -577,15 +577,6 @@ /* Define to 1 if you have the `pthread_attr_setstacksize' function. */ #cmakedefine HAVE_PTHREAD_ATTR_SETSTACKSIZE 1 -/* Define to 1 if you have the `pthread_attr_getstack' function. */ -#cmakedefine HAVE_PTHREAD_ATTR_GETSTACK 1 - -/* Define to 1 if you have the `pthread_attr_getstacksize' function. */ -#cmakedefine HAVE_PTHREAD_ATTR_GETSTACKSIZE 1 - -/* Define to 1 if you have the `pthread_get_stacksize_np' function. */ -#cmakedefine HAVE_PTHREAD_GET_STACKSIZE_NP 1 - /* Define to 1 if you have the `pthread_get_stackaddr_np' function. */ #cmakedefine HAVE_PTHREAD_GET_STACKADDR_NP 1 diff --git a/src/mono/mono/metadata/class-internals.h b/src/mono/mono/metadata/class-internals.h index 72234b97d4542a..541166813fbe85 100644 --- a/src/mono/mono/metadata/class-internals.h +++ b/src/mono/mono/metadata/class-internals.h @@ -351,8 +351,8 @@ struct MonoVTable { guint32 imt_collisions_bitmap; MonoRuntimeGenericContext *runtime_generic_context; - /* interp virtual method table */ - gpointer *interp_vtable; + /* Maintained by the Execution Engine */ + gpointer ee_data; /* do not add any fields after vtable, the structure is dynamically extended */ /* vtable contains function pointers to methods or their trampolines, at the end there may be a slot containing the pointer to the static fields */ diff --git a/src/mono/mono/metadata/icall-signatures.h b/src/mono/mono/metadata/icall-signatures.h index 40d2f9b8392376..080d4724d89459 100644 --- a/src/mono/mono/metadata/icall-signatures.h +++ b/src/mono/mono/metadata/icall-signatures.h @@ -202,6 +202,7 @@ ICALL_SIG (3, (object, ptr, ptr)) \ ICALL_SIG (3, (object, ptr, sizet)) \ ICALL_SIG (3, (ptr, int32, ptrref)) \ ICALL_SIG (3, (ptr, object, ptr)) \ +ICALL_SIG (3, (ptr, object, int)) \ ICALL_SIG (3, (ptr, ptr, int)) \ ICALL_SIG (3, (ptr, ptr, int32)) \ ICALL_SIG (3, (ptr, ptr, ptr)) \ diff --git a/src/mono/mono/metadata/jit-icall-reg.h b/src/mono/mono/metadata/jit-icall-reg.h index c7ed70424a7e7c..577a14eb656191 100644 --- a/src/mono/mono/metadata/jit-icall-reg.h +++ b/src/mono/mono/metadata/jit-icall-reg.h @@ -135,10 +135,11 @@ MONO_JIT_ICALL (mini_llvmonly_resolve_generic_virtual_call) \ MONO_JIT_ICALL (mini_llvmonly_resolve_generic_virtual_iface_call) \ MONO_JIT_ICALL (mini_llvmonly_resolve_iface_call_gsharedvt) \ MONO_JIT_ICALL (mini_llvmonly_resolve_vcall_gsharedvt) \ +MONO_JIT_ICALL (mini_llvmonly_resolve_vcall_gsharedvt_fast) \ MONO_JIT_ICALL (mini_llvmonly_throw_nullref_exception) \ MONO_JIT_ICALL (mini_llvmonly_throw_aot_failed_exception) \ MONO_JIT_ICALL (mini_llvmonly_pop_lmf) \ -MONO_JIT_ICALL (mini_llvmonly_get_interp_entry) \ +MONO_JIT_ICALL (mini_llvmonly_interp_entry_gsharedvt) \ MONO_JIT_ICALL (mono_amd64_resume_unwind) \ MONO_JIT_ICALL (mono_amd64_start_gsharedvt_call) \ MONO_JIT_ICALL (mono_amd64_throw_corlib_exception) \ diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index 0df1f3cf791030..c8aac849ad17ec 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -4291,6 +4291,14 @@ add_extra_method_with_depth (MonoAotCompile *acfg, MonoMethod *method, int depth { ERROR_DECL (error); + if (method->is_generic && acfg->aot_opts.profile_only) { + // Add the fully shared version to its home image + // This has already been added just need to add it to profile_methods so its not skipped + method = mini_get_shared_method_full (method, SHARE_MODE_NONE, error); + g_hash_table_insert (acfg->profile_methods, method, method); + return; + } + if (mono_method_is_generic_sharable_full (method, TRUE, TRUE, FALSE)) { MonoMethod *orig = method; @@ -6792,6 +6800,7 @@ encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info, guint8 *buf, guint case MONO_PATCH_INFO_METHOD: case MONO_PATCH_INFO_METHOD_JUMP: case MONO_PATCH_INFO_METHOD_FTNDESC: + case MONO_PATCH_INFO_LLVMONLY_INTERP_ENTRY: case MONO_PATCH_INFO_ICALL_ADDR: case MONO_PATCH_INFO_ICALL_ADDR_CALL: case MONO_PATCH_INFO_METHOD_RGCTX: @@ -8679,7 +8688,8 @@ can_encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info) case MONO_PATCH_INFO_METHOD_FTNDESC: case MONO_PATCH_INFO_METHODCONST: case MONO_PATCH_INFO_METHOD_CODE_SLOT: - case MONO_PATCH_INFO_METHOD_PINVOKE_ADDR_CACHE: { + case MONO_PATCH_INFO_METHOD_PINVOKE_ADDR_CACHE: + case MONO_PATCH_INFO_LLVMONLY_INTERP_ENTRY: { MonoMethod *method = patch_info->data.method; return can_encode_method (acfg, method); @@ -13015,20 +13025,24 @@ resolve_profile_data (MonoAotCompile *acfg, ProfileData *data, MonoAssembly* cur continue; if (mdata->inst) { resolve_ginst (mdata->inst); - if (!mdata->inst->inst) - continue; - MonoGenericContext ctx; + if (mdata->inst->inst) { + MonoGenericContext ctx; - memset (&ctx, 0, sizeof (ctx)); - ctx.method_inst = mdata->inst->inst; + memset (&ctx, 0, sizeof (ctx)); + ctx.method_inst = mdata->inst->inst; - m = mono_class_inflate_generic_method_checked (m, &ctx, error); - if (!m) - continue; - sig = mono_method_signature_checked (m, error); - if (!is_ok (error)) { - mono_error_cleanup (error); - continue; + m = mono_class_inflate_generic_method_checked (m, &ctx, error); + if (!m) + continue; + sig = mono_method_signature_checked (m, error); + if (!is_ok (error)) { + mono_error_cleanup (error); + continue; + } + } else { + /* Use the generic definition */ + mdata->method = m; + break; } } char *sig_str = mono_signature_full_name (sig); diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index dbb35bd6209203..c4b0b821a25dcc 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -3666,7 +3666,8 @@ decode_patch (MonoAotModule *aot_module, MonoMemPool *mp, MonoJumpInfo *ji, guin case MONO_PATCH_INFO_ICALL_ADDR_CALL: case MONO_PATCH_INFO_METHOD_RGCTX: case MONO_PATCH_INFO_METHOD_CODE_SLOT: - case MONO_PATCH_INFO_METHOD_PINVOKE_ADDR_CACHE: { + case MONO_PATCH_INFO_METHOD_PINVOKE_ADDR_CACHE: + case MONO_PATCH_INFO_LLVMONLY_INTERP_ENTRY: { MethodRef ref; gboolean res; diff --git a/src/mono/mono/mini/calls.c b/src/mono/mono/mini/calls.c index 52ae664a18fe63..dc3007b36bfb74 100644 --- a/src/mono/mono/mini/calls.c +++ b/src/mono/mono/mini/calls.c @@ -717,7 +717,7 @@ mini_emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMeth return mini_emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target); } - if (!fsig->generic_param_count && !is_iface) { + if (!is_gsharedvt && !fsig->generic_param_count && !is_iface) { /* * The simplest case, a normal virtual call. */ @@ -751,22 +751,13 @@ mini_emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMeth /* Fastpath */ MONO_START_BB (cfg, non_null_bb); - if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) { - MonoInst *wrapper_ins = mini_emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT); - int arg_reg = alloc_preg (cfg); - EMIT_NEW_UNALU (cfg, ins, OP_MOVE, arg_reg, slot_reg); - int addr_reg = alloc_preg (cfg); - EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, wrapper_ins->dreg, 0); - return mini_emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target); - } else { - /* Load the address + arg from the vtable slot */ - EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, slot_reg, TARGET_SIZEOF_VOID_P); - return mini_emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target); - } + /* Load the address + arg from the vtable slot */ + EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0); + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, slot_reg, TARGET_SIZEOF_VOID_P); + return mini_emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target); } - if (!fsig->generic_param_count && is_iface && !variant_iface && !special_array_interface) { + if (!is_gsharedvt && !fsig->generic_param_count && is_iface && !variant_iface && !special_array_interface) { /* * A simple interface call * @@ -801,18 +792,10 @@ mini_emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMeth icall_args [1] = mini_emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD); ftndesc_ins = mini_emit_calli (cfg, helper_sig_llvmonly_imt_trampoline, icall_args, thunk_addr_ins, NULL, NULL); - - if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) { - MonoInst *wrapper_ins = mini_emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT); - int addr_reg = alloc_preg (cfg); - EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, wrapper_ins->dreg, 0); - return mini_emit_extra_arg_calli (cfg, fsig, sp, ftndesc_ins->dreg, call_target); - } else { - return mini_emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins); - } + return mini_emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins); } - if (fsig->generic_param_count || variant_iface || special_array_interface) { + if (!is_gsharedvt && (fsig->generic_param_count || variant_iface || special_array_interface)) { /* * This is similar to the interface case, the vtable slot points to an imt thunk which is * dynamically extended as more instantiations are discovered. @@ -876,15 +859,19 @@ mini_emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMeth /* Common case */ MONO_START_BB (cfg, end_bb); + return mini_emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins); + } - if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) { - MonoInst *wrapper_ins = mini_emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT); - int addr_reg = alloc_preg (cfg); - EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, wrapper_ins->dreg, 0); - return mini_emit_extra_arg_calli (cfg, fsig, sp, ftndesc_ins->dreg, call_target); - } else { - return mini_emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins); - } + if (is_gsharedvt && !(is_iface || fsig->generic_param_count || variant_iface || special_array_interface)) { + MonoInst *ftndesc_ins; + + /* Normal virtual call using a gsharedvt calling conv */ + icall_args [0] = sp [0]; + EMIT_NEW_ICONST (cfg, icall_args [1], slot); + + ftndesc_ins = mono_emit_jit_icall (cfg, mini_llvmonly_resolve_vcall_gsharedvt_fast, icall_args); + + return mini_emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins); } /* diff --git a/src/mono/mono/mini/decompose.c b/src/mono/mono/mini/decompose.c index fabddcb3cb0d27..46de11de21c927 100644 --- a/src/mono/mono/mini/decompose.c +++ b/src/mono/mono/mini/decompose.c @@ -1499,6 +1499,9 @@ mono_decompose_array_access_opts (MonoCompile *cfg) cfg->cbb = (MonoBasicBlock *)mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock)); first_bb = cfg->cbb; + // This code can't handle the creation of new basic blocks + cfg->disable_inline_rgctx_fetch = TRUE; + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { MonoInst *ins; MonoInst *prev = NULL; @@ -1592,6 +1595,8 @@ mono_decompose_array_access_opts (MonoCompile *cfg) if (cfg->verbose_level > 3) mono_print_bb (bb, "AFTER DECOMPOSE-ARRAY-ACCESS-OPTS "); } + + cfg->disable_inline_rgctx_fetch = FALSE; } typedef union { diff --git a/src/mono/mono/mini/ee.h b/src/mono/mono/mini/ee.h index efa77ec5d3b8be..33a06a3820b07e 100644 --- a/src/mono/mono/mini/ee.h +++ b/src/mono/mono/mini/ee.h @@ -14,7 +14,7 @@ #ifndef __MONO_EE_H__ #define __MONO_EE_H__ -#define MONO_EE_API_VERSION 0x13 +#define MONO_EE_API_VERSION 0x14 typedef struct _MonoInterpStackIter MonoInterpStackIter; @@ -61,6 +61,8 @@ typedef gpointer MonoInterpFrameHandle; MONO_EE_CALLBACK (void, mark_stack, (gpointer thread_info, GcScanFunc func, gpointer gc_data, gboolean precise)) \ MONO_EE_CALLBACK (void, jit_info_foreach, (InterpJitInfoFunc func, gpointer user_data)) \ MONO_EE_CALLBACK (gboolean, sufficient_stack, (gsize size)) \ + MONO_EE_CALLBACK (void, entry_llvmonly, (gpointer res, gpointer *args, gpointer imethod)) \ + MONO_EE_CALLBACK (gpointer, get_interp_method, (MonoMethod *method, MonoError *error)) \ typedef struct _MonoEECallbacks { diff --git a/src/mono/mono/mini/interp-stubs.c b/src/mono/mono/mini/interp-stubs.c index 84c4f0795b0461..5d4e3b5c14ed61 100644 --- a/src/mono/mono/mini/interp-stubs.c +++ b/src/mono/mono/mini/interp-stubs.c @@ -226,6 +226,19 @@ stub_sufficient_stack (gsize size) g_assert_not_reached (); } +static void +stub_entry_llvmonly (gpointer res, gpointer *args, gpointer imethod) +{ + g_assert_not_reached (); +} + +static gpointer +stub_get_interp_method (MonoMethod *method, MonoError *error) +{ + g_assert_not_reached (); + return NULL; +} + #undef MONO_EE_CALLBACK #define MONO_EE_CALLBACK(ret, name, sig) stub_ ## name, diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index f5ee46419ac4d3..56d4134d91a700 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -666,11 +666,24 @@ get_target_imethod (GSList *list, InterpMethod *imethod) return NULL; } +static inline MonoVTableEEData* +get_vtable_ee_data (MonoVTable *vtable) +{ + MonoVTableEEData *ee_data = (MonoVTableEEData*)vtable->ee_data; + + if (G_UNLIKELY (!ee_data)) { + ee_data = m_class_alloc0 (vtable->klass, sizeof (MonoVTableEEData)); + mono_memory_barrier (); + vtable->ee_data = ee_data; + } + return ee_data; +} + static gpointer* get_method_table (MonoVTable *vtable, int offset) { if (offset >= 0) - return vtable->interp_vtable; + return get_vtable_ee_data (vtable)->interp_vtable; else return (gpointer*)vtable; } @@ -682,7 +695,7 @@ alloc_method_table (MonoVTable *vtable, int offset) if (offset >= 0) { table = (gpointer*)m_class_alloc0 (vtable->klass, m_class_get_vtable_size (vtable->klass) * sizeof (gpointer)); - vtable->interp_vtable = table; + get_vtable_ee_data (vtable)->interp_vtable = table; } else { table = (gpointer*)vtable; } @@ -2234,6 +2247,7 @@ typedef struct { int pindex; gpointer jit_wrapper; gpointer *args; + gpointer extra_arg; MonoFtnDesc ftndesc; } JitCallCbData; @@ -2245,7 +2259,7 @@ jit_call_cb (gpointer arg) gpointer jit_wrapper = cb_data->jit_wrapper; int pindex = cb_data->pindex; gpointer *args = cb_data->args; - MonoFtnDesc *ftndesc = &cb_data->ftndesc; + gpointer ftndesc = cb_data->extra_arg; switch (pindex) { case 0: { @@ -2339,6 +2353,7 @@ struct _JitCallInfo { guint8 *arginfo; gint32 res_size; int ret_mt; + gboolean no_wrapper; }; static MONO_NEVER_INLINE void @@ -2357,20 +2372,38 @@ init_jit_call_info (InterpMethod *rmethod, MonoError *error) sig = mono_method_signature_internal (method); g_assert (sig); - MonoMethod *wrapper = mini_get_gsharedvt_out_sig_wrapper (sig); - //printf ("J: %s %s\n", mono_method_full_name (method, 1), mono_method_full_name (wrapper, 1)); - - gpointer jit_wrapper = mono_jit_compile_method_jit_only (wrapper, error); - mono_error_assert_ok (error); - gpointer addr = mono_jit_compile_method_jit_only (method, error); return_if_nok (error); g_assert (addr); - if (mono_llvm_only) - cinfo->addr = mini_llvmonly_add_method_wrappers (method, addr, FALSE, FALSE, &cinfo->extra_arg); - else + gboolean need_wrapper = TRUE; + if (mono_llvm_only) { + MonoAotMethodFlags flags = mono_aot_get_method_flags (addr); + + if (flags & MONO_AOT_METHOD_FLAG_GSHAREDVT_VARIABLE) { + /* + * The callee already has a gsharedvt signature, we can call it directly + * instead of through a gsharedvt out wrapper. + */ + need_wrapper = FALSE; + cinfo->no_wrapper = TRUE; + } + } + + gpointer jit_wrapper = NULL; + if (need_wrapper) { + MonoMethod *wrapper = mini_get_gsharedvt_out_sig_wrapper (sig); + jit_wrapper = mono_jit_compile_method_jit_only (wrapper, error); + mono_error_assert_ok (error); + } + + if (mono_llvm_only) { + gboolean caller_gsharedvt = !need_wrapper; + cinfo->addr = mini_llvmonly_add_method_wrappers (method, addr, caller_gsharedvt, FALSE, &cinfo->extra_arg); + } else { cinfo->addr = addr; + } + cinfo->sig = sig; cinfo->wrapper = jit_wrapper; @@ -2456,11 +2489,17 @@ do_jit_call (ThreadContext *context, stackval *ret_sp, stackval *sp, InterpFrame JitCallCbData cb_data; memset (&cb_data, 0, sizeof (cb_data)); - cb_data.jit_wrapper = cinfo->wrapper; cb_data.pindex = pindex; cb_data.args = args; - cb_data.ftndesc.addr = cinfo->addr; - cb_data.ftndesc.arg = cinfo->extra_arg; + if (cinfo->no_wrapper) { + cb_data.jit_wrapper = cinfo->addr; + cb_data.extra_arg = cinfo->extra_arg; + } else { + cb_data.ftndesc.addr = cinfo->addr; + cb_data.ftndesc.arg = cinfo->extra_arg; + cb_data.jit_wrapper = cinfo->wrapper; + cb_data.extra_arg = &cb_data.ftndesc; + } interp_push_lmf (&ext, frame); gboolean thrown = FALSE; @@ -2803,6 +2842,23 @@ interp_entry_from_trampoline (gpointer ccontext_untyped, gpointer rmethod_untype #endif /* MONO_ARCH_HAVE_INTERP_ENTRY_TRAMPOLINE */ +static void +interp_entry_llvmonly (gpointer res, gpointer *args, gpointer imethod_untyped) +{ + InterpMethod *imethod = (InterpMethod*)imethod_untyped; + + if (imethod->hasthis) + interp_entry_general (*(gpointer*)(args [0]), res, args + 1, imethod); + else + interp_entry_general (NULL, res, args, imethod); +} + +static gpointer +interp_get_interp_method (MonoMethod *method, MonoError *error) +{ + return mono_interp_get_imethod (method, error); +} + static InterpMethod* lookup_method_pointer (gpointer addr) { diff --git a/src/mono/mono/mini/llvmonly-runtime.c b/src/mono/mono/mini/llvmonly-runtime.c index 7847285a947196..8da3e60cce81bc 100644 --- a/src/mono/mono/mini/llvmonly-runtime.c +++ b/src/mono/mono/mini/llvmonly-runtime.c @@ -203,7 +203,7 @@ mini_llvmonly_add_method_wrappers (MonoMethod *m, gpointer compiled_method, gboo if (caller_gsharedvt && !callee_gsharedvt) { /* - * The callee uses the gsharedvt calling convention, have to add an out wrapper. + * The caller uses the gsharedvt calling convention, have to add an out wrapper. */ gpointer out_wrapper = mini_get_gsharedvt_wrapper (FALSE, NULL, mono_method_signature_internal (m), NULL, -1, FALSE); MonoFtnDesc *out_wrapper_arg = mini_llvmonly_create_ftndesc (m, addr, *out_arg); @@ -215,6 +215,29 @@ mini_llvmonly_add_method_wrappers (MonoMethod *m, gpointer compiled_method, gboo return addr; } +static inline MonoVTableEEData* +get_vtable_ee_data (MonoVTable *vtable) +{ + MonoVTableEEData *ee_data = (MonoVTableEEData*)vtable->ee_data; + + if (G_UNLIKELY (!ee_data)) { + ee_data = m_class_alloc0 (vtable->klass, sizeof (MonoVTableEEData)); + mono_memory_barrier (); + vtable->ee_data = ee_data; + } + return ee_data; +} + +static void +alloc_gsharedvt_vtable (MonoVTable *vtable) +{ + MonoVTableEEData *ee_data = get_vtable_ee_data (vtable); + + if (!ee_data->gsharedvt_vtable) { + gpointer *vt = (gpointer*)m_class_alloc0 (vtable->klass, (m_class_get_vtable_size (vtable->klass) + MONO_IMT_SIZE) * sizeof (gpointer)); + ee_data->gsharedvt_vtable = (MonoFtnDesc**)(vt + MONO_IMT_SIZE); + } +} typedef struct { MonoVTable *vtable; @@ -296,6 +319,26 @@ llvmonly_imt_tramp_3 (gpointer *arg, MonoMethod *imt_method) return arg [5]; } +static gpointer +llvmonly_fallback_imt_tramp_1 (gpointer *arg, MonoMethod *imt_method) +{ + if (G_LIKELY (arg [0] == imt_method)) + return arg [1]; + else + return NULL; +} + +static gpointer +llvmonly_fallback_imt_tramp_2 (gpointer *arg, MonoMethod *imt_method) +{ + if (arg [0] == imt_method) + return arg [1]; + else if (arg [2] == imt_method) + return arg [3]; + else + return NULL; +} + /* * A version of the imt trampoline used for generic virtual/variant iface methods. * Unlikely a normal imt trampoline, its possible that IMT_METHOD is not found @@ -392,8 +435,19 @@ mini_llvmonly_get_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_ent res [0] = (gpointer)llvmonly_imt_tramp; break; } - if (virtual_generic || fail_tramp) - res [0] = (gpointer)llvmonly_fallback_imt_tramp; + if (virtual_generic || fail_tramp) { + switch (real_count) { + case 1: + res [0] = (gpointer)llvmonly_fallback_imt_tramp_1; + break; + case 2: + res [0] = (gpointer)llvmonly_fallback_imt_tramp_2; + break; + default: + res [0] = (gpointer)llvmonly_fallback_imt_tramp; + break; + } + } res [1] = buf; return res; @@ -523,6 +577,53 @@ mini_llvmonly_resolve_vcall_gsharedvt (MonoObject *this_obj, int slot, MonoMetho return result; } +MonoFtnDesc* +mini_llvmonly_resolve_vcall_gsharedvt_fast (MonoObject *this_obj, int slot) +{ + g_assert (this_obj); + + /* + * Virtual calls with gsharedvt signatures are complicated to handle: + * - gsharedvt methods cannot be stored in vtable slots since most callers used the normal + * calling convention and we don't want to do a runtime check on every virtual call. + * - adding a gsharedvt in wrapper around them and storing that to the vtable wouldn't work either, + * because the wrapper might not exist if all the possible callers are also gsharedvt, since in that + * case, the concrete signature might not exist in the program. + * To solve this, allocate a separate vtable which contains entries with gsharedvt calling convs. + * either gsharedvt methods, or normal methods with gsharedvt out wrappers. + */ + + /* Fastpath */ + MonoVTable *vtable = this_obj->vtable; + MonoVTableEEData *ee_data = get_vtable_ee_data (vtable); + MonoFtnDesc *ftndesc; + + if (G_LIKELY (ee_data->gsharedvt_vtable)) { + ftndesc = ee_data->gsharedvt_vtable [slot]; + if (G_LIKELY (ftndesc)) + return ftndesc; + } + + /* Slowpath */ + alloc_gsharedvt_vtable (vtable); + + ERROR_DECL (error); + gpointer arg; + gpointer addr = resolve_vcall (vtable, slot, NULL, &arg, TRUE, error); + if (!is_ok (error)) { + MonoException *ex = mono_error_convert_to_exception (error); + mono_llvm_throw_exception ((MonoObject*)ex); + } + + ftndesc = m_class_alloc0 (vtable->klass, sizeof (MonoFtnDesc)); + ftndesc->addr = addr; + ftndesc->arg = arg; + mono_memory_barrier (); + ee_data->gsharedvt_vtable [slot] = ftndesc; + + return ftndesc; +} + /* * mini_llvmonly_resolve_generic_virtual_call: * @@ -673,6 +774,8 @@ mini_llvmonly_init_delegate (MonoDelegate *del, MonoDelegateTrampInfo *info) info = mono_create_delegate_trampoline_info (del->object.vtable->klass, del->method); } + del->method = info->method; + /* Cache the target method address in MonoDelegateTrampInfo */ ftndesc = (MonoFtnDesc*)info->method_ptr; @@ -705,7 +808,7 @@ mini_llvmonly_init_delegate (MonoDelegate *del, MonoDelegateTrampInfo *info) ERROR_DECL (error); MonoMethod *invoke_impl = mono_marshal_get_delegate_invoke (info->invoke, del); gpointer arg = NULL; - gpointer addr = mini_llvmonly_load_method_delegate (invoke_impl, FALSE, FALSE, &arg, error); + gpointer addr = mini_llvmonly_load_method (invoke_impl, FALSE, FALSE, &arg, error); mono_error_assert_ok (error); ftndesc = mini_llvmonly_create_ftndesc (invoke_impl, addr, arg); if (subtype == WRAPPER_SUBTYPE_NONE) { @@ -786,12 +889,46 @@ mini_llvmonly_resolve_iface_call_gsharedvt (MonoObject *this_obj, int imt_slot, { ERROR_DECL (error); - gpointer res = resolve_iface_call (this_obj, imt_slot, imt_method, out_arg, TRUE, error); + // Fastpath + MonoVTable *vtable = this_obj->vtable; + MonoVTableEEData* ee_data = get_vtable_ee_data (vtable); + gpointer *gsharedvt_imt = NULL; + MonoFtnDesc *ftndesc; + + if (G_LIKELY (ee_data && ee_data->gsharedvt_vtable)) { + gsharedvt_imt = ((gpointer*)ee_data->gsharedvt_vtable) - MONO_IMT_SIZE; + + // Use a 1 element imt entry for now, i.e. cache one method per IMT slot + gpointer *entries = gsharedvt_imt [imt_slot]; + if (G_LIKELY (entries && entries [0] == imt_method)) { + ftndesc = entries [1]; + *out_arg = ftndesc->arg; + return ftndesc->addr; + } + } + + alloc_gsharedvt_vtable (vtable); + gsharedvt_imt = ((gpointer*)ee_data->gsharedvt_vtable) - MONO_IMT_SIZE; + + gpointer addr = resolve_iface_call (this_obj, imt_slot, imt_method, out_arg, TRUE, error); if (!is_ok (error)) { MonoException *ex = mono_error_convert_to_exception (error); mono_llvm_throw_exception ((MonoObject*)ex); } - return res; + + if (!gsharedvt_imt [imt_slot]) { + ftndesc = m_class_alloc0 (vtable->klass, sizeof (MonoFtnDesc)); + ftndesc->addr = addr; + ftndesc->arg = *out_arg; + + gpointer *arr = m_class_alloc0 (vtable->klass, sizeof (gpointer) * 2); + arr [0] = imt_method; + arr [1] = ftndesc; + mono_memory_barrier (); + gsharedvt_imt [imt_slot] = arr; + } + + return addr; } /* Called from LLVM code to initialize a method */ @@ -850,12 +987,8 @@ mini_llvmonly_pop_lmf (MonoLMF *lmf) mono_set_lmf ((MonoLMF*)lmf->previous_lmf); } -gpointer -mini_llvmonly_get_interp_entry (MonoMethod *method) +void +mini_llvmonly_interp_entry_gsharedvt (gpointer imethod, gpointer res, gpointer *args) { - ERROR_DECL (error); - - MonoFtnDesc *desc = mini_get_interp_callbacks ()->create_method_pointer_llvmonly (method, FALSE, error); - mono_error_assert_ok (error); - return desc; + mini_get_interp_callbacks ()->entry_llvmonly (res, args, imethod); } diff --git a/src/mono/mono/mini/llvmonly-runtime.h b/src/mono/mono/mini/llvmonly-runtime.h index 7fbc196fd163bd..5b6c7ed392bb3f 100644 --- a/src/mono/mono/mini/llvmonly-runtime.h +++ b/src/mono/mono/mini/llvmonly-runtime.h @@ -25,6 +25,7 @@ G_EXTERN_C gpointer mini_llvmonly_resolve_vcall_gsharedvt (MonoObject *this_obj, G_EXTERN_C gpointer mini_llvmonly_resolve_iface_call_gsharedvt (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg); G_EXTERN_C MonoFtnDesc* mini_llvmonly_resolve_generic_virtual_call (MonoVTable *vt, int slot, MonoMethod *imt_method); G_EXTERN_C MonoFtnDesc* mini_llvmonly_resolve_generic_virtual_iface_call (MonoVTable *vt, int imt_slot, MonoMethod *imt_method); +G_EXTERN_C MonoFtnDesc* mini_llvmonly_resolve_vcall_gsharedvt_fast (MonoObject *this_obj, int slot); G_EXTERN_C void mini_llvmonly_init_delegate (MonoDelegate *del, MonoDelegateTrampInfo *info); G_EXTERN_C void mini_llvmonly_init_delegate_virtual (MonoDelegate *del, MonoObject *target, MonoMethod *method); @@ -37,6 +38,6 @@ G_EXTERN_C void mini_llvmonly_throw_aot_failed_exception (const char *name); G_EXTERN_C void mini_llvmonly_pop_lmf (MonoLMF *lmf); -G_EXTERN_C gpointer mini_llvmonly_get_interp_entry (MonoMethod *method); +G_EXTERN_C void mini_llvmonly_interp_entry_gsharedvt (gpointer imethod, gpointer res, gpointer *args); #endif diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 5835e33e0db5ee..d1da9506106686 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -2586,8 +2586,8 @@ emit_rgctx_fetch_inline (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEnt MonoInst *slot_ins; EMIT_NEW_AOTCONST (cfg, slot_ins, MONO_PATCH_INFO_RGCTX_SLOT_INDEX, entry); - // Can't add basic blocks during decompose/interp entry mode etc. - if (cfg->after_method_to_ir || cfg->interp_entry_only) { + // Can't add basic blocks during interp entry mode + if (cfg->disable_inline_rgctx_fetch || cfg->interp_entry_only) { MonoInst *args [2] = { rgctx, slot_ins }; if (entry->in_mrgctx) call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args); @@ -2800,6 +2800,9 @@ emit_get_rgctx_method (MonoCompile *cfg, int context_used, case MONO_RGCTX_INFO_METHOD_FTNDESC: EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_METHOD_FTNDESC, cmethod); return ins; + case MONO_RGCTX_INFO_LLVMONLY_INTERP_ENTRY: + EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_LLVMONLY_INTERP_ENTRY, cmethod); + return ins; default: g_assert_not_reached (); } @@ -3579,7 +3582,7 @@ handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, Mono } /* Set method field */ - if (!(target_method_context_used || invoke_context_used) || cfg->llvm_only) { + if (!(target_method_context_used || invoke_context_used) && !cfg->llvm_only) { //If compiling with gsharing enabled, it's faster to load method the delegate trampoline info than to use a rgctx slot MonoInst *method_ins = emit_get_rgctx_method (cfg, target_method_context_used, method, MONO_RGCTX_INFO_METHOD); MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method), method_ins->dreg); @@ -3631,8 +3634,10 @@ handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, Mono //This is emited as a contant store for the non-shared case. //We copy from the delegate trampoline info as it's faster than a rgctx fetch dreg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, method)); - MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method), dreg); + if (!cfg->llvm_only) { + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, method)); + MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method), dreg); + } } else if (cfg->compile_aot) { MonoDelegateClassMethodPair *del_tramp; @@ -5580,6 +5585,12 @@ handle_ctor_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fs { MonoInst *vtable_arg = NULL, *callvirt_this_arg = NULL, *ins; + if (cmethod && (ins = mini_emit_inst_for_ctor (cfg, cmethod, fsig, sp))) { + g_assert (MONO_TYPE_IS_VOID (fsig->ret)); + CHECK_CFG_EXCEPTION; + return; + } + if (mono_class_generic_sharing_enabled (cmethod->klass) && mono_method_is_generic_sharable (cmethod, TRUE)) { MonoRgctxAccess access = mini_get_rgctx_access_for_method (cmethod); @@ -5601,11 +5612,7 @@ handle_ctor_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fs } /* Avoid virtual calls to ctors if possible */ - - if (cmethod && (ins = mini_emit_inst_for_ctor (cfg, cmethod, fsig, sp))) { - g_assert (MONO_TYPE_IS_VOID (fsig->ret)); - CHECK_CFG_EXCEPTION; - } else if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg && + if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg && mono_method_check_inlining (cfg, cmethod) && !mono_class_is_subclass_of_internal (cmethod->klass, mono_defaults.exception_class, FALSE)) { int costs; @@ -5901,8 +5908,6 @@ emit_llvmonly_interp_entry (MonoCompile *cfg, MonoMethodHeader *header) MonoInst **iargs; MonoMethodSignature *sig = mono_method_signature_internal (cfg->method); - iargs = g_newa (MonoInst*, sig->param_count + 1); - iargs [0] = emit_get_rgctx_method (cfg, -1, cfg->method, MONO_RGCTX_INFO_METHOD); MonoInst *ftndesc; cfg->interp_in_signatures = g_slist_prepend_mempool (cfg->mempool, cfg->interp_in_signatures, sig); @@ -5913,13 +5918,58 @@ emit_llvmonly_interp_entry (MonoCompile *cfg, MonoMethodHeader *header) * entry/exit bblocks. */ g_assert (cfg->cbb == cfg->bb_init); - /* Obtain the interp entry function */ - ftndesc = mono_emit_jit_icall_id (cfg, MONO_JIT_ICALL_mini_llvmonly_get_interp_entry, iargs); - /* Call it */ - for (int i = 0; i < sig->param_count + sig->hasthis; ++i) - EMIT_NEW_ARGLOAD (cfg, iargs [i], i); - ins = mini_emit_llvmonly_calli (cfg, sig, iargs, ftndesc); + if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (sig)) { + /* + * Would have to generate a gsharedvt out wrapper which calls the interp entry wrapper, but + * the gsharedvt out wrapper might not exist if the caller is also a gsharedvt method since + * the concrete signature of the call might not exist in the program. + * So transition directly to the interpreter without the wrappers. + */ + MonoInst *args_ins; + MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM); + ins->dreg = alloc_preg (cfg); + ins->inst_imm = sig->param_count * sizeof (target_mgreg_t); + MONO_ADD_INS (cfg->cbb, ins); + args_ins = ins; + + for (int i = 0; i < sig->hasthis + sig->param_count; ++i) { + MonoInst *arg_addr_ins; + EMIT_NEW_VARLOADA ((cfg), arg_addr_ins, cfg->args [i], cfg->arg_types [i]); + EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args_ins->dreg, i * sizeof (target_mgreg_t), arg_addr_ins->dreg); + } + + MonoInst *ret_var = NULL; + MonoInst *ret_arg_ins; + if (!MONO_TYPE_IS_VOID (sig->ret)) { + ret_var = mono_compile_create_var (cfg, sig->ret, OP_LOCAL); + EMIT_NEW_VARLOADA (cfg, ret_arg_ins, ret_var, sig->ret); + } else { + EMIT_NEW_PCONST (cfg, ret_arg_ins, NULL); + } + + iargs = g_newa (MonoInst*, 3); + iargs [0] = emit_get_rgctx_method (cfg, -1, cfg->method, MONO_RGCTX_INFO_INTERP_METHOD); + iargs [1] = ret_arg_ins; + iargs [2] = args_ins; + mono_emit_jit_icall_id (cfg, MONO_JIT_ICALL_mini_llvmonly_interp_entry_gsharedvt, iargs); + + if (!MONO_TYPE_IS_VOID (sig->ret)) + EMIT_NEW_VARLOAD (cfg, ins, ret_var, sig->ret); + else + ins = NULL; + } else { + /* Obtain the interp entry function */ + ftndesc = emit_get_rgctx_method (cfg, -1, cfg->method, MONO_RGCTX_INFO_LLVMONLY_INTERP_ENTRY); + + /* Call it */ + iargs = g_newa (MonoInst*, sig->param_count + 1); + for (int i = 0; i < sig->param_count + sig->hasthis; ++i) + EMIT_NEW_ARGLOAD (cfg, iargs [i], i); + + ins = mini_emit_llvmonly_calli (cfg, sig, iargs, ftndesc); + } + /* Do a normal return */ if (cfg->ret) { emit_setret (cfg, ins); diff --git a/src/mono/mono/mini/mini-generic-sharing.c b/src/mono/mono/mini/mini-generic-sharing.c index e69da90cfb055f..93534fedf547e7 100644 --- a/src/mono/mono/mini/mini-generic-sharing.c +++ b/src/mono/mono/mini/mini-generic-sharing.c @@ -563,11 +563,12 @@ inflate_info (MonoMemoryManager *mem_manager, MonoRuntimeGenericContextInfoTempl case MONO_RGCTX_INFO_METHOD_FTNDESC: case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: - case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT: case MONO_RGCTX_INFO_METHOD_RGCTX: case MONO_RGCTX_INFO_METHOD_CONTEXT: case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: - case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: { + case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: + case MONO_RGCTX_INFO_INTERP_METHOD: + case MONO_RGCTX_INFO_LLVMONLY_INTERP_ENTRY: { MonoMethod *method = (MonoMethod *)data; MonoMethod *inflated_method; MonoType *inflated_type = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (method->klass), context, error); @@ -2231,16 +2232,15 @@ instantiate_info (MonoMemoryManager *mem_manager, MonoRuntimeGenericContextInfoT return mini_llvmonly_create_ftndesc (m, addr, arg); } } - case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT: { + case MONO_RGCTX_INFO_INTERP_METHOD: { MonoMethod *m = (MonoMethod*)data; - gpointer addr; - /* A gsharedvt out wrapper for the signature of M */ - g_assert (mono_llvm_only); - addr = mini_get_gsharedvt_wrapper (FALSE, NULL, mono_method_signature_internal (m), NULL, -1, FALSE); + return mini_get_interp_callbacks ()->get_interp_method (m, error); + } + case MONO_RGCTX_INFO_LLVMONLY_INTERP_ENTRY: { + MonoMethod *m = (MonoMethod*)data; - /* Returns an ftndesc */ - return mini_llvmonly_create_ftndesc (m, addr, NULL); + return mini_get_interp_callbacks ()->create_method_pointer_llvmonly (m, FALSE, error); } case MONO_RGCTX_INFO_VIRT_METHOD_CODE: { MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data; @@ -2625,7 +2625,8 @@ mono_rgctx_info_type_to_str (MonoRgctxInfoType type) case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: return "GSHAREDVT_INFO"; case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: return "GENERIC_METHOD_CODE"; case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: return "GSHAREDVT_OUT_WRAPPER"; - case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT: return "GSHAREDVT_OUT_WRAPPER_VIRT"; + case MONO_RGCTX_INFO_INTERP_METHOD: return "INTERP_METHOD"; + case MONO_RGCTX_INFO_LLVMONLY_INTERP_ENTRY: return "LLVMONLY_INTERP_ENTRY"; case MONO_RGCTX_INFO_CLASS_FIELD: return "CLASS_FIELD"; case MONO_RGCTX_INFO_METHOD_RGCTX: return "METHOD_RGCTX"; case MONO_RGCTX_INFO_METHOD_CONTEXT: return "METHOD_CONTEXT"; @@ -2738,7 +2739,8 @@ info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type) case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: - case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT: + case MONO_RGCTX_INFO_INTERP_METHOD: + case MONO_RGCTX_INFO_LLVMONLY_INTERP_ENTRY: case MONO_RGCTX_INFO_CLASS_FIELD: case MONO_RGCTX_INFO_FIELD_OFFSET: case MONO_RGCTX_INFO_METHOD_RGCTX: @@ -2804,7 +2806,8 @@ mini_rgctx_info_type_to_patch_info_type (MonoRgctxInfoType info_type) case MONO_RGCTX_INFO_METHOD_RGCTX: case MONO_RGCTX_INFO_METHOD_FTNDESC: case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: - case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT: + case MONO_RGCTX_INFO_INTERP_METHOD: + case MONO_RGCTX_INFO_LLVMONLY_INTERP_ENTRY: return MONO_PATCH_INFO_METHOD; default: printf ("%d\n", info_type); diff --git a/src/mono/mono/mini/mini-llvm-cpp.cpp b/src/mono/mono/mini/mini-llvm-cpp.cpp index ece236a5350d1a..d2bf4eb7c77421 100644 --- a/src/mono/mono/mini/mini-llvm-cpp.cpp +++ b/src/mono/mono/mini/mini-llvm-cpp.cpp @@ -755,3 +755,26 @@ unsigned int mono_llvm_get_prim_size_bits (LLVMTypeRef type) { return unwrap (type)->getPrimitiveSizeInBits (); } + +/* + * Inserts a call to a fragment of inline assembly. + * + * Return values correspond to output constraints. Parameter values correspond + * to input constraints. Example of use: + * mono_llvm_inline_asm (builder, void_func_t, "int $$0x3", "", LLVM_ASM_SIDE_EFFECT, NULL, 0, ""); + */ +LLVMValueRef +mono_llvm_inline_asm (LLVMBuilderRef builder, LLVMTypeRef type, + const char *asmstr, const char *constraints, + MonoLLVMAsmFlags flags, LLVMValueRef *args, unsigned num_args, + const char *name) +{ + const auto asmstr_len = strlen (asmstr); + const auto constraints_len = strlen (constraints); + const auto asmval = LLVMGetInlineAsm (type, + const_cast(asmstr), asmstr_len, + const_cast(constraints), constraints_len, + (flags & LLVM_ASM_SIDE_EFFECT) != 0, (flags & LLVM_ASM_ALIGN_STACK) != 0, + LLVMInlineAsmDialectATT); + return LLVMBuildCall2 (builder, type, asmval, args, num_args, name); +} diff --git a/src/mono/mono/mini/mini-llvm-cpp.h b/src/mono/mono/mini/mini-llvm-cpp.h index 27f561505d5111..aaf2ef031f30eb 100644 --- a/src/mono/mono/mini/mini-llvm-cpp.h +++ b/src/mono/mono/mini/mini-llvm-cpp.h @@ -220,6 +220,17 @@ mono_llvm_register_overloaded_intrinsic (LLVMModuleRef module, IntrinsicId id, L unsigned int mono_llvm_get_prim_size_bits (LLVMTypeRef type); +typedef enum { + LLVM_ASM_SIDE_EFFECT = 1 << 0, + LLVM_ASM_ALIGN_STACK = 1 << 1, +} MonoLLVMAsmFlags; + +LLVMValueRef +mono_llvm_inline_asm (LLVMBuilderRef builder, LLVMTypeRef type, + const char *asmstr, const char *constraints, + MonoLLVMAsmFlags flags, LLVMValueRef *args, unsigned num_args, + const char *name); + G_END_DECLS #endif /* __MONO_MINI_LLVM_CPP_H__ */ diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 5830c336da380a..4193900b324c53 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -318,6 +318,8 @@ static LLVMTypeRef sse_i1_t, sse_i2_t, sse_i4_t, sse_i8_t, sse_r4_t, sse_r8_t; static LLVMTypeRef v64_i1_t, v64_i2_t, v64_i4_t, v64_i8_t, v64_r4_t, v64_r8_t; static LLVMTypeRef v128_i1_t, v128_i2_t, v128_i4_t, v128_i8_t, v128_r4_t, v128_r8_t; +static LLVMTypeRef void_func_t; + static MonoLLVMModule *init_jit_module (void); static void emit_dbg_loc (EmitContext *ctx, LLVMBuilderRef builder, const unsigned char *cil_code); @@ -12404,6 +12406,8 @@ mono_llvm_init (gboolean enable_jit) intrins_id_to_intrins = g_hash_table_new (NULL, NULL); + void_func_t = LLVMFunctionType0 (LLVMVoidType (), FALSE); + if (enable_jit) mono_llvm_jit_init (); } diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index c48380c08748e9..ac65672001415d 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -1076,7 +1076,8 @@ mono_print_ji (const MonoJumpInfo *ji) } case MONO_PATCH_INFO_METHOD: case MONO_PATCH_INFO_METHODCONST: - case MONO_PATCH_INFO_METHOD_FTNDESC: { + case MONO_PATCH_INFO_METHOD_FTNDESC: + case MONO_PATCH_INFO_LLVMONLY_INTERP_ENTRY: { char *s = mono_method_get_full_name (ji->data.method); printf ("[%s %s]", type, s); g_free (s); @@ -1216,6 +1217,7 @@ mono_patch_info_hash (gconstpointer data) case MONO_PATCH_INFO_METHOD: case MONO_PATCH_INFO_METHOD_JUMP: case MONO_PATCH_INFO_METHOD_FTNDESC: + case MONO_PATCH_INFO_LLVMONLY_INTERP_ENTRY: case MONO_PATCH_INFO_IMAGE: case MONO_PATCH_INFO_ICALL_ADDR: case MONO_PATCH_INFO_ICALL_ADDR_CALL: @@ -1417,6 +1419,11 @@ mono_resolve_patch_target_ext (MonoMemoryManager *mem_manager, MonoMethod *metho return_val_if_nok (error, NULL); break; } + case MONO_PATCH_INFO_LLVMONLY_INTERP_ENTRY: { + target = mini_get_interp_callbacks ()->create_method_pointer_llvmonly (patch_info->data.method, FALSE, error); + mono_error_assert_ok (error); + break; + } case MONO_PATCH_INFO_METHOD_CODE_SLOT: { gpointer code_slot; @@ -4892,6 +4899,7 @@ register_icalls (void) register_icall (mini_llvm_init_method, mono_icall_sig_void_ptr_ptr_ptr_ptr, TRUE); register_icall_no_wrapper (mini_llvmonly_resolve_iface_call_gsharedvt, mono_icall_sig_ptr_object_int_ptr_ptr); register_icall_no_wrapper (mini_llvmonly_resolve_vcall_gsharedvt, mono_icall_sig_ptr_object_int_ptr_ptr); + register_icall_no_wrapper (mini_llvmonly_resolve_vcall_gsharedvt_fast, mono_icall_sig_ptr_object_int); register_icall_no_wrapper (mini_llvmonly_resolve_generic_virtual_call, mono_icall_sig_ptr_ptr_int_ptr); register_icall_no_wrapper (mini_llvmonly_resolve_generic_virtual_iface_call, mono_icall_sig_ptr_ptr_int_ptr); /* This needs a wrapper so it can have a preserveall cconv */ @@ -4901,7 +4909,7 @@ register_icalls (void) register_icall (mini_llvmonly_throw_nullref_exception, mono_icall_sig_void, TRUE); register_icall (mini_llvmonly_throw_aot_failed_exception, mono_icall_sig_void_ptr, TRUE); register_icall (mini_llvmonly_pop_lmf, mono_icall_sig_void_ptr, TRUE); - register_icall (mini_llvmonly_get_interp_entry, mono_icall_sig_ptr_ptr, TRUE); + register_icall (mini_llvmonly_interp_entry_gsharedvt, mono_icall_sig_void_ptr_ptr_ptr, TRUE); register_icall (mono_get_assembly_object, mono_icall_sig_object_ptr, TRUE); register_icall (mono_get_method_object, mono_icall_sig_object_ptr, TRUE); diff --git a/src/mono/mono/mini/mini-runtime.h b/src/mono/mono/mini/mini-runtime.h index abf2f68b826e15..3a2a2382247b75 100644 --- a/src/mono/mono/mini/mini-runtime.h +++ b/src/mono/mono/mini/mini-runtime.h @@ -111,6 +111,13 @@ jit_code_hash_unlock (MonoJitMemoryManager *jit_mm) mono_locks_os_release(&(jit_mm)->jit_code_hash_lock, DomainJitCodeHashLock); } +/* Per vtable data maintained by the EE */ +typedef struct { + /* interp virtual method table */ + gpointer *interp_vtable; + MonoFtnDesc **gsharedvt_vtable; +} MonoVTableEEData; + /* * Stores state need to resume exception handling when using LLVM */ diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c index 7e2c7bb1047dcd..2b5e7bc22c6640 100644 --- a/src/mono/mono/mini/mini-wasm.c +++ b/src/mono/mono/mini/mini-wasm.c @@ -66,10 +66,11 @@ is_scalar_vtype (MonoType *type) nfields ++; if (nfields > 1) return FALSE; - if (MONO_TYPE_ISSTRUCT (field->type)) { - if (!is_scalar_vtype (field->type)) + MonoType *t = mini_get_underlying_type (field->type); + if (MONO_TYPE_ISSTRUCT (t)) { + if (!is_scalar_vtype (t)) return FALSE; - } else if (!((MONO_TYPE_IS_PRIMITIVE (field->type) || MONO_TYPE_IS_REFERENCE (field->type)))) { + } else if (!((MONO_TYPE_IS_PRIMITIVE (t) || MONO_TYPE_IS_REFERENCE (t)))) { return FALSE; } } diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index 943df2b71b2295..b693081d6311f8 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -1070,8 +1070,10 @@ typedef enum { MONO_RGCTX_INFO_METHOD_FTNDESC = 33, /* mono_type_size () for a class */ MONO_RGCTX_INFO_CLASS_SIZEOF = 34, - /* A gsharedvt_out wrapper for a method */ - MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT = 35 + /* The InterpMethod for a method */ + MONO_RGCTX_INFO_INTERP_METHOD = 35, + /* The llvmonly interp entry for a method */ + MONO_RGCTX_INFO_LLVMONLY_INTERP_ENTRY = 36 } MonoRgctxInfoType; /* How an rgctx is passed to a method */ @@ -1486,6 +1488,7 @@ typedef struct { guint code_exec_only : 1; guint interp_entry_only : 1; guint after_method_to_ir : 1; + guint disable_inline_rgctx_fetch : 1; guint8 uses_simd_intrinsics; int r4_stack_type; gpointer debug_info; diff --git a/src/mono/mono/mini/patch-info.h b/src/mono/mono/mini/patch-info.h index 6807f92b5c645a..19c1ae29c199c0 100644 --- a/src/mono/mono/mini/patch-info.h +++ b/src/mono/mono/mini/patch-info.h @@ -85,3 +85,6 @@ PATCH_INFO(R4_GOT, "r4_got") /* MonoMethod* -> the address of a memory slot which is used to cache the pinvoke address */ PATCH_INFO(METHOD_PINVOKE_ADDR_CACHE, "pinvoke_addr_cache") + +/* MonoMethod* -> llvmonly interp entry */ +PATCH_INFO(LLVMONLY_INTERP_ENTRY, "llvmonly_interp_entry") diff --git a/src/mono/msbuild/apple/build/AppleApp.targets b/src/mono/msbuild/apple/build/AppleApp.targets index cb388d01b750bd..d8ebc730cd7451 100644 --- a/src/mono/msbuild/apple/build/AppleApp.targets +++ b/src/mono/msbuild/apple/build/AppleApp.targets @@ -134,6 +134,7 @@ MainLibraryFileName="$(MainLibraryFileName)" ForceAOT="$(RunAOTCompilation)" ForceInterpreter="$(MonoForceInterpreter)" + EnableAppSandbox="$(EnableAppSandbox)" InvariantGlobalization="$(InvariantGlobalization)" UseConsoleUITemplate="True" GenerateXcodeProject="$(GenerateXcodeProject)" diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj index a5129bdfe82c68..dabf1700dce5f1 100644 --- a/src/mono/sample/Android/AndroidSampleApp.csproj +++ b/src/mono/sample/Android/AndroidSampleApp.csproj @@ -2,7 +2,7 @@ Exe false - $(NetCoreAppToolCurrent) + $(NetCoreAppCurrent) android-$(TargetArchitecture) true Link diff --git a/src/mono/sample/HelloWorld/HelloWorld.csproj b/src/mono/sample/HelloWorld/HelloWorld.csproj index db05235fce86c0..f727ffe343f4d1 100644 --- a/src/mono/sample/HelloWorld/HelloWorld.csproj +++ b/src/mono/sample/HelloWorld/HelloWorld.csproj @@ -1,6 +1,6 @@ Exe - $(NetCoreAppToolCurrent) + $(NetCoreAppCurrent) diff --git a/src/mono/sample/iOS/Makefile b/src/mono/sample/iOS/Makefile index 8148d6eeb6cd75..bdfc6c8f854b2b 100644 --- a/src/mono/sample/iOS/Makefile +++ b/src/mono/sample/iOS/Makefile @@ -5,6 +5,7 @@ USE_LLVM=true AOT?=false TARGET?=iOSSimulator DEPLOY_AND_RUN?=true +APP_SANDBOX?=false #If DIAGNOSTIC_PORTS is enabled, RUNTIME_COMPONENTS must also be enabled. #If RUNTIME_COMPONENTS is enabled, DIAGNOSTIC_PORTS is optional. @@ -58,7 +59,8 @@ run-catalyst: /p:TargetArchitecture=$(MONO_ARCH) \ '/p:DeployAndRun="$(DEPLOY_AND_RUN)"' \ /p:UseLLVM=False \ - /p:ForceAOT=True + /p:ForceAOT=True \ + /p:EnableAppSandbox=$(APP_SANDBOX) run-sim-interp: clean appbuilder $(DOTNET) publish \ @@ -80,6 +82,7 @@ run-catalyst-interp: /p:UseLLVM=False \ '/p:DeployAndRun="$(DEPLOY_AND_RUN)"' \ /p:ForceAOT=True \ + /p:EnableAppSandbox=$(APP_SANDBOX) \ /p:MonoForceInterpreter=true clean: diff --git a/src/mono/sample/iOS/Program.csproj b/src/mono/sample/iOS/Program.csproj index fc2f27d7a613b2..ec1f84c354d9cc 100644 --- a/src/mono/sample/iOS/Program.csproj +++ b/src/mono/sample/iOS/Program.csproj @@ -2,7 +2,7 @@ Exe bin - $(NetCoreAppToolCurrent) + $(NetCoreAppCurrent) iOS iOSSimulator true @@ -18,6 +18,7 @@ adhoc + false @@ -81,6 +82,7 @@ ForceAOT="$(RunAOTCompilation)" ForceInterpreter="$(MonoForceInterpreter)" RuntimeComponents="$(RuntimeComponents)" + EnableAppSandbox="$(EnableAppSandbox)" DiagnosticPorts="$(DiagnosticPorts)" AppDir="$(MSBuildThisFileDirectory)$(PublishDir)"> diff --git a/src/mono/sample/mbr/DeltaHelper/DeltaHelper.csproj b/src/mono/sample/mbr/DeltaHelper/DeltaHelper.csproj index 6a27072a4b5fed..6168dec054654d 100644 --- a/src/mono/sample/mbr/DeltaHelper/DeltaHelper.csproj +++ b/src/mono/sample/mbr/DeltaHelper/DeltaHelper.csproj @@ -1,5 +1,5 @@ - $(NetCoreAppToolCurrent) + $(NetCoreAppCurrent) diff --git a/src/mono/sample/mbr/apple/AppleDelta.csproj b/src/mono/sample/mbr/apple/AppleDelta.csproj index af4ec2b324a10a..145dfb80552c71 100644 --- a/src/mono/sample/mbr/apple/AppleDelta.csproj +++ b/src/mono/sample/mbr/apple/AppleDelta.csproj @@ -2,7 +2,7 @@ Exe bin - $(NetCoreAppToolCurrent) + $(NetCoreAppCurrent) $(TargetOS.ToLower())-$(TargetArchitecture) $(DefineConstants);CI_TEST true diff --git a/src/mono/sample/mbr/console/ConsoleDelta.csproj b/src/mono/sample/mbr/console/ConsoleDelta.csproj index eb70ee9df2469d..6741c2eba1d8b2 100644 --- a/src/mono/sample/mbr/console/ConsoleDelta.csproj +++ b/src/mono/sample/mbr/console/ConsoleDelta.csproj @@ -1,7 +1,7 @@ Exe - $(NetCoreAppToolCurrent) + $(NetCoreAppCurrent) false diff --git a/src/mono/sample/wasm/Directory.Build.targets b/src/mono/sample/wasm/Directory.Build.targets index f3ee714290d2ab..cebfca1893bba3 100644 --- a/src/mono/sample/wasm/Directory.Build.targets +++ b/src/mono/sample/wasm/Directory.Build.targets @@ -5,19 +5,21 @@ + <_ScriptExt Condition="'$(OS)' == 'Windows_NT'">.cmd <_ScriptExt Condition="'$(OS)' != 'Windows_NT'">.sh <_Dotnet>$(RepoRoot)dotnet$(_ScriptExt) <_AOTFlag Condition="'$(RunAOTCompilation)' != ''">/p:RunAOTCompilation=$(RunAOTCompilation) + <_WasmMainJSFileName>$([System.IO.Path]::GetFileName('$(WasmMainJSPath)')) - + - + diff --git a/src/mono/sample/wasm/browser-bench/AppStart.cs b/src/mono/sample/wasm/browser-bench/AppStart.cs index 98fe46c229bcda..09db124fe9c032 100644 --- a/src/mono/sample/wasm/browser-bench/AppStart.cs +++ b/src/mono/sample/wasm/browser-bench/AppStart.cs @@ -47,19 +47,11 @@ class PageShow : BenchTask.Measurement public override int InitialSamples => 3; - async Task RunAsyncStep() - { - var function = Activator.CreateInstance(jsFunctionType, new object[] { new object[] { @"return App.StartAppUI();" } }); - var task = (Task)functionCall.Invoke(function, new object[] { }); - - await task; - } - public override bool HasRunStepAsync => true; public override async Task RunStepAsync() { - var function = Activator.CreateInstance(jsFunctionType, new object[] { new object[] { @"return App.PageShow();" } }); + var function = Activator.CreateInstance(jsFunctionType, new object[] { new object[] { @"return mainApp.PageShow();" } }); await (Task)functionCall.Invoke(function, null); } } @@ -70,8 +62,8 @@ class ReachManaged : BenchTask.Measurement public override int InitialSamples => 3; public override bool HasRunStepAsync => true; - static object jsUIReachedManagedFunction = Activator.CreateInstance(jsFunctionType, new object[] { new object[] { @"return App.ReachedManaged();" } }); - static object jsReached = Activator.CreateInstance(jsFunctionType, new object[] { new object[] { @"return App.reached();" } }); + static object jsUIReachedManagedFunction = Activator.CreateInstance(jsFunctionType, new object[] { new object[] { @"return mainApp.ReachedManaged();" } }); + static object jsReached = Activator.CreateInstance(jsFunctionType, new object[] { new object[] { @"return frameApp.reached();" } }); [MethodImpl(MethodImplOptions.NoInlining)] public static void Reached() diff --git a/src/mono/sample/wasm/browser-bench/Wasm.Browser.Bench.Sample.csproj b/src/mono/sample/wasm/browser-bench/Wasm.Browser.Bench.Sample.csproj index 89c7feed07ef75..3ad96f7aea7009 100644 --- a/src/mono/sample/wasm/browser-bench/Wasm.Browser.Bench.Sample.csproj +++ b/src/mono/sample/wasm/browser-bench/Wasm.Browser.Bench.Sample.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/mono/sample/wasm/browser-bench/appstart-frame.html b/src/mono/sample/wasm/browser-bench/appstart-frame.html index 84f0832330583b..97469a19a50309 100644 --- a/src/mono/sample/wasm/browser-bench/appstart-frame.html +++ b/src/mono/sample/wasm/browser-bench/appstart-frame.html @@ -2,40 +2,18 @@ - - App task - - - - - - - + + - var App = { - init: function () { - INTERNAL.call_static_method("[Wasm.Browser.Bench.Sample] Sample.AppStartTask/ReachManaged:Reached"); - }, - - reached: function() { - window.parent.resolveAppStartEvent("reached"); - } - }; - - - - - - + \ No newline at end of file diff --git a/src/mono/sample/wasm/browser-bench/appstart.js b/src/mono/sample/wasm/browser-bench/appstart.js deleted file mode 100644 index 5aa11c1e2b66b6..00000000000000 --- a/src/mono/sample/wasm/browser-bench/appstart.js +++ /dev/null @@ -1,31 +0,0 @@ -var AppStart = { - Construct: function() { - this._frame = document.createElement('iframe'); - document.body.appendChild(this._frame); - }, - - WaitForPageShow: async function() { - let promise; - let promiseResolve; - this._frame.src = 'appstart-frame.html'; - promise = new Promise(resolve => { promiseResolve = resolve; }) - window.resolveAppStartEvent = function(event) { promiseResolve(); } - await promise; - }, - - WaitForReached: async function() { - let promise; - let promiseResolve; - this._frame.src = 'appstart-frame.html'; - promise = new Promise(resolve => { promiseResolve = resolve; }) - window.resolveAppStartEvent = function(event) { - if (event == "reached") - promiseResolve(); - } - await promise; - }, - - RemoveFrame: function () { - document.body.removeChild(this._frame); - } -}; diff --git a/src/mono/sample/wasm/browser-bench/frame-main.js b/src/mono/sample/wasm/browser-bench/frame-main.js new file mode 100644 index 00000000000000..6ddbf2f5b50338 --- /dev/null +++ b/src/mono/sample/wasm/browser-bench/frame-main.js @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +"use strict"; +class FrameApp { + async init({ BINDING }) { + const reachManagedReached = BINDING.bind_static_method("[Wasm.Browser.Bench.Sample] Sample.AppStartTask/ReachManaged:Reached"); + await reachManagedReached(); + } + + reached() { + window.parent.resolveAppStartEvent("reached"); + } +} + +globalThis.frameApp = new FrameApp(); + +let mute = false; +createDotnetRuntime(({ BINDING }) => { + return { + disableDotnet6Compatibility: true, + configSrc: "./mono-config.json", + printErr: function () { + if (!mute) { + console.error(...arguments); + } + }, + onConfigLoaded: () => { + window.parent.resolveAppStartEvent("onConfigLoaded"); + // Module.config.diagnostic_tracing = true; + }, + onDotnetReady: async () => { + window.parent.resolveAppStartEvent("onDotnetReady"); + try { + await frameApp.init({ BINDING }); + } catch (error) { + set_exit_code(1, error); + throw (error); + } + }, + onAbort: (error) => { + set_exit_code(1, error); + }, + } +}).catch(err => { + if (!mute) { + console.error(`WASM ERROR ${err}`); + } +}) + +window.addEventListener("pageshow", event => { window.parent.resolveAppStartEvent("pageshow"); }) + +window.muteErrors = () => { + mute = true; +} + +function set_exit_code(exit_code, reason) { + /* Set result in a tests_done element, to be read by xharness */ + var tests_done_elem = document.createElement("label"); + tests_done_elem.id = "tests_done"; + tests_done_elem.innerHTML = exit_code.toString(); + document.body.appendChild(tests_done_elem); + + console.log(`WASM EXIT ${exit_code}`); +}; \ No newline at end of file diff --git a/src/mono/sample/wasm/browser-bench/index.html b/src/mono/sample/wasm/browser-bench/index.html index bec077fe5324c2..850537b605fb10 100644 --- a/src/mono/sample/wasm/browser-bench/index.html +++ b/src/mono/sample/wasm/browser-bench/index.html @@ -10,70 +10,11 @@ - + Output:

- + - - \ No newline at end of file diff --git a/src/mono/sample/wasm/browser-bench/main.js b/src/mono/sample/wasm/browser-bench/main.js index c62fb564e218b4..da003783cf1507 100644 --- a/src/mono/sample/wasm/browser-bench/main.js +++ b/src/mono/sample/wasm/browser-bench/main.js @@ -2,13 +2,89 @@ // The .NET Foundation licenses this file to you under the MIT license. "use strict"; -var Module = { + +let runBenchmark; +let setTasks; + +class MainApp { + init({ BINDING }) { + runBenchmark = BINDING.bind_static_method("[Wasm.Browser.Bench.Sample] Sample.Test:RunBenchmark"); + setTasks = BINDING.bind_static_method("[Wasm.Browser.Bench.Sample] Sample.Test:SetTasks"); + + var url = new URL(decodeURI(window.location)); + let tasks = url.searchParams.getAll('task'); + if (tasks != '') { + setTasks(tasks.join(',')); + } + + this.yieldBench(); + } + + + yieldBench() { + let promise = runBenchmark(); + promise.then(ret => { + document.getElementById("out").innerHTML += ret; + if (ret.length > 0) { + setTimeout(() => { this.yieldBench(); }, 0); + } else { + document.getElementById("out").innerHTML += "Finished"; + } + }); + } + + async PageShow() { + try { + await this.waitFor('pageshow'); + } finally { + this.removeFrame(); + } + } + + async ReachedManaged() { + try { + await this.waitFor('reached'); + } finally { + this.removeFrame(); + } + } + + async waitFor(eventName) { + try { + let promise; + let promiseResolve; + this._frame = document.createElement('iframe'); + this._frame.src = 'appstart-frame.html'; + + promise = new Promise(resolve => { promiseResolve = resolve; }) + window.resolveAppStartEvent = function (event) { + if (!eventName || event == eventName) + promiseResolve(); + } + + document.body.appendChild(this._frame); + await promise; + } catch (err) { + console.log(err); + throw err; + } + } + + removeFrame() { + this._frame.contentWindow.muteErrors(); + document.body.removeChild(this._frame); + } +} + +globalThis.mainApp = new MainApp(); + +createDotnetRuntime(({ BINDING }) => ({ + disableDotnet6Compatibility: true, configSrc: "./mono-config.json", onDotnetReady: () => { try { - App.init(); + mainApp.init({ BINDING }); } catch (error) { - console.log("exception: " + error); set_exit_code(1, error); throw (error); } @@ -16,4 +92,14 @@ var Module = { onAbort: (error) => { set_exit_code(1, error); }, +})); + +function set_exit_code(exit_code, reason) { + /* Set result in a tests_done element, to be read by xharness */ + const tests_done_elem = document.createElement("label"); + tests_done_elem.id = "tests_done"; + tests_done_elem.innerHTML = exit_code.toString(); + document.body.appendChild(tests_done_elem); + + console.log(`WASM EXIT ${exit_code}`); }; diff --git a/src/mono/sample/wasm/console-v8-cjs/Program.cs b/src/mono/sample/wasm/console-v8-cjs/Program.cs new file mode 100644 index 00000000000000..d5ea012a33c792 --- /dev/null +++ b/src/mono/sample/wasm/console-v8-cjs/Program.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading.Tasks; + +public class Test +{ + public static async Task Main(string[] args) + { + await Task.Delay(1); + Console.WriteLine("Hello World!"); + for (int i = 0; i < args.Length; i++) { + Console.WriteLine($"args[{i}] = {args[i]}"); + } + return args.Length; + } +} diff --git a/src/mono/sample/wasm/console-v8-cjs/Wasm.Console.V8.CJS.Sample.csproj b/src/mono/sample/wasm/console-v8-cjs/Wasm.Console.V8.CJS.Sample.csproj new file mode 100644 index 00000000000000..0b28f10d49c3a9 --- /dev/null +++ b/src/mono/sample/wasm/console-v8-cjs/Wasm.Console.V8.CJS.Sample.csproj @@ -0,0 +1,15 @@ + + + true + main.js + true + false + + + + <_SampleProject>Wasm.Console.V8.CJS.Sample.csproj + <_SampleAssembly>Wasm.Console.V8.CJS.Sample.dll + + + + diff --git a/src/mono/sample/wasm/console-v8-cjs/main.js b/src/mono/sample/wasm/console-v8-cjs/main.js new file mode 100644 index 00000000000000..5cca1845b8a259 --- /dev/null +++ b/src/mono/sample/wasm/console-v8-cjs/main.js @@ -0,0 +1,11 @@ +load("./dotnet.js") + +const dllName = "Wasm.Console.V8.CJS.Sample.dll"; +const app_args = Array.from(arguments); + +async function main() { + const { MONO } = await createDotnetRuntime(); + await MONO.mono_run_main_and_exit(dllName, app_args); +} + +main(); \ No newline at end of file diff --git a/src/mono/sample/wasm/wasm.mk b/src/mono/sample/wasm/wasm.mk index 5a1c70133489f8..624722c2169454 100644 --- a/src/mono/sample/wasm/wasm.mk +++ b/src/mono/sample/wasm/wasm.mk @@ -30,4 +30,4 @@ run-browser: fi run-console: - cd bin/$(CONFIG)/AppBundle && ~/.jsvu/v8 --stack-trace-limit=1000 --single-threaded --expose_wasm main.js -- $(DOTNET_MONO_LOG_LEVEL) --run $(CONSOLE_DLL) $(ARGS) + cd bin/$(CONFIG)/AppBundle && ~/.jsvu/v8 --stack-trace-limit=1000 --single-threaded --expose_wasm test-main.js -- $(DOTNET_MONO_LOG_LEVEL) --run $(CONSOLE_DLL) $(ARGS) diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 9fdf3c38aa40c1..ea282f9638cf1e 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -26,6 +26,10 @@ true + + + + <_MonoComponent Include="hot_reload;debugger" /> diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 4bcaac94f329d8..1185fc11a562e0 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -159,7 +159,7 @@ + Text="%24(MicrosoftNetCoreAppRuntimePackDir)='', and cannot find %25(ResolvedRuntimePack.PackageDirectory)=%(ResolvedRuntimePack.PackageDirectory). One of these need to be set to a valid path" /> @@ -295,12 +295,13 @@ $(WasmAppDir)run-v8.sh + <_WasmMainJSFileName>$([System.IO.Path]::GetFileName('$(WasmMainJSPath)')) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 148fd03d37c84e..fc0dbf7aaf96a9 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -20,6 +20,7 @@ using Microsoft.CodeAnalysis.Debugging; using System.IO.Compression; using System.Reflection; +using System.Collections.Immutable; namespace Microsoft.WebAssembly.Diagnostics { @@ -524,39 +525,57 @@ internal class AssemblyInfo private Dictionary sourceLinkMappings = new Dictionary(); private readonly List sources = new List(); internal string Url { get; } + //The caller must keep the PEReader alive and undisposed throughout the lifetime of the metadata reader + internal PEReader peReader; internal MetadataReader asmMetadataReader { get; } internal MetadataReader pdbMetadataReader { get; set; } - internal List enCMemoryStream = new List(); internal List enCMetadataReader = new List(); - internal PEReader peReader; - internal MemoryStream asmStream; - internal MemoryStream pdbStream; public int DebugId { get; set; } - + internal int PdbAge { get; } + internal System.Guid PdbGuid { get; } + internal string PdbName { get; } + internal bool PdbInformationAvailable { get; } public bool TriedToLoadSymbolsOnDemand { get; set; } - public unsafe AssemblyInfo(string url, byte[] assembly, byte[] pdb) + public unsafe AssemblyInfo(MonoProxy monoProxy, SessionId sessionId, string url, byte[] assembly, byte[] pdb, CancellationToken token) { this.id = Interlocked.Increment(ref next_id); - asmStream = new MemoryStream(assembly); + using var asmStream = new MemoryStream(assembly); peReader = new PEReader(asmStream); + var entries = peReader.ReadDebugDirectory(); + if (entries.Length > 0) + { + var codeView = entries[0]; + CodeViewDebugDirectoryData codeViewData = peReader.ReadCodeViewDebugDirectoryData(codeView); + PdbAge = codeViewData.Age; + PdbGuid = codeViewData.Guid; + PdbName = codeViewData.Path; + PdbInformationAvailable = true; + } asmMetadataReader = PEReaderExtensions.GetMetadataReader(peReader); + var asmDef = asmMetadataReader.GetAssemblyDefinition(); + Name = asmDef.GetAssemblyName().Name + ".dll"; if (pdb != null) { - pdbStream = new MemoryStream(pdb); - pdbMetadataReader = MetadataReaderProvider.FromPortablePdbStream(pdbStream).GetMetadataReader(); + var pdbStream = new MemoryStream(pdb); + try + { + // MetadataReaderProvider.FromPortablePdbStream takes ownership of the stream + pdbMetadataReader = MetadataReaderProvider.FromPortablePdbStream(pdbStream).GetMetadataReader(); + } + catch (BadImageFormatException) + { + monoProxy.SendLog(sessionId, $"Warning: Unable to read debug information of: {Name} (use DebugType=Portable/Embedded)", token); + } } else { - var entries = peReader.ReadDebugDirectory(); var embeddedPdbEntry = entries.FirstOrDefault(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb); if (embeddedPdbEntry.DataSize != 0) { pdbMetadataReader = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry).GetMetadataReader(); } } - Name = asmMetadataReader.GetAssemblyDefinition().GetAssemblyName().Name + ".dll"; - AssemblyNameUnqualified = asmMetadataReader.GetAssemblyDefinition().GetAssemblyName().Name + ".dll"; Populate(); } @@ -566,8 +585,6 @@ public bool EnC(byte[] meta, byte[] pdb) MetadataReader asmMetadataReader = MetadataReaderProvider.FromMetadataStream(asmStream).GetMetadataReader(); var pdbStream = new MemoryStream(pdb); MetadataReader pdbMetadataReader = MetadataReaderProvider.FromPortablePdbStream(pdbStream).GetMetadataReader(); - enCMemoryStream.Add(asmStream); - enCMemoryStream.Add(pdbStream); enCMetadataReader.Add(asmMetadataReader); enCMetadataReader.Add(pdbMetadataReader); PopulateEnC(asmMetadataReader, pdbMetadataReader); @@ -728,7 +745,7 @@ public TypeInfo GetTypeByName(string name) internal void UpdatePdbInformation(Stream streamToReadFrom) { - pdbStream = new MemoryStream(); + var pdbStream = new MemoryStream(); streamToReadFrom.CopyTo(pdbStream); pdbMetadataReader = MetadataReaderProvider.FromPortablePdbStream(pdbStream).GetMetadataReader(); } @@ -907,14 +924,16 @@ internal class DebugStore internal List assemblies = new List(); private readonly HttpClient client; private readonly ILogger logger; + private readonly MonoProxy monoProxy; - public DebugStore(ILogger logger, HttpClient client) + public DebugStore(MonoProxy monoProxy, ILogger logger, HttpClient client) { this.client = client; this.logger = logger; + this.monoProxy = monoProxy; } - public DebugStore(ILogger logger) : this(logger, new HttpClient()) + public DebugStore(MonoProxy monoProxy, ILogger logger) : this(monoProxy, logger, new HttpClient()) { } private class DebugItem @@ -933,12 +952,12 @@ public IEnumerable EnC(AssemblyInfo asm, byte[] meta_data, byte[] pd } } - public IEnumerable Add(string name, byte[] assembly_data, byte[] pdb_data) + public IEnumerable Add(SessionId id, string name, byte[] assembly_data, byte[] pdb_data, CancellationToken token) { AssemblyInfo assembly = null; try { - assembly = new AssemblyInfo(name, assembly_data, pdb_data); + assembly = new AssemblyInfo(monoProxy, id, name, assembly_data, pdb_data, token); } catch (Exception e) { @@ -949,7 +968,7 @@ public IEnumerable Add(string name, byte[] assembly_data, byte[] pdb if (assembly == null) yield break; - if (GetAssemblyByUnqualifiedName(assembly.AssemblyNameUnqualified) != null) + if (GetAssemblyByName(assembly.Name) != null) { logger.LogDebug($"Skipping adding {assembly.Name} into the debug store, as it already exists"); yield break; @@ -962,7 +981,7 @@ public IEnumerable Add(string name, byte[] assembly_data, byte[] pdb } } - public async IAsyncEnumerable Load(string[] loaded_files, [EnumeratorCancellation] CancellationToken token) + public async IAsyncEnumerable Load(SessionId id, string[] loaded_files, [EnumeratorCancellation] CancellationToken token) { var asm_files = new List(); var pdb_files = new List(); @@ -1001,7 +1020,7 @@ public async IAsyncEnumerable Load(string[] loaded_files, [Enumerato try { byte[][] bytes = await step.Data.ConfigureAwait(false); - assembly = new AssemblyInfo(step.Url, bytes[0], bytes[1]); + assembly = new AssemblyInfo(monoProxy, id, step.Url, bytes[0], bytes[1], token); } catch (Exception e) { @@ -1010,7 +1029,7 @@ public async IAsyncEnumerable Load(string[] loaded_files, [Enumerato if (assembly == null) continue; - if (GetAssemblyByUnqualifiedName(assembly.AssemblyNameUnqualified) != null) + if (GetAssemblyByName(assembly.Name) != null) { logger.LogDebug($"Skipping loading {assembly.Name} into the debug store, as it already exists"); continue; @@ -1028,7 +1047,6 @@ public async IAsyncEnumerable Load(string[] loaded_files, [Enumerato public AssemblyInfo GetAssemblyByName(string name) => assemblies.FirstOrDefault(a => a.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)); - public AssemblyInfo GetAssemblyByUnqualifiedName(string name) => assemblies.FirstOrDefault(a => a.AssemblyNameUnqualified.Equals(name, StringComparison.InvariantCultureIgnoreCase)); /* V8 uses zero based indexing for both line and column. diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index f6689614aafd71..e0f796e09c5ad2 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -50,6 +50,34 @@ private bool UpdateContext(SessionId sessionId, ExecutionContext executionContex internal Task SendMonoCommand(SessionId id, MonoCommands cmd, CancellationToken token) => SendCommand(id, "Runtime.evaluate", JObject.FromObject(cmd), token); + internal void SendLog(SessionId sessionId, string message, CancellationToken token) + { + if (!contexts.TryGetValue(sessionId, out ExecutionContext context)) + return; + /*var o = JObject.FromObject(new + { + entry = JObject.FromObject(new + { + source = "recommendation", + level = "warning", + text = message + }) + }); + SendEvent(id, "Log.enabled", null, token); + SendEvent(id, "Log.entryAdded", o, token);*/ + var o = JObject.FromObject(new + { + type = "warning", + args = new JArray(JObject.FromObject(new + { + type = "string", + value = message, + })), + executionContextId = context.Id + }); + SendEvent(sessionId, "Runtime.consoleAPICalled", o, token); + } + protected override async Task AcceptEvent(SessionId sessionId, string method, JObject args, CancellationToken token) { switch (method) @@ -936,21 +964,14 @@ internal async Task LoadSymbolsOnDemand(AssemblyInfo asm, int method ExecutionContext context = GetContext(sessionId); if (urlSymbolServerList.Count == 0) return null; - if (asm.TriedToLoadSymbolsOnDemand) + if (asm.TriedToLoadSymbolsOnDemand || !asm.PdbInformationAvailable) return null; asm.TriedToLoadSymbolsOnDemand = true; - var peReader = asm.peReader; - var entries = peReader.ReadDebugDirectory(); - var codeView = entries[0]; - var codeViewData = peReader.ReadCodeViewDebugDirectoryData(codeView); - int pdbAge = codeViewData.Age; - var pdbGuid = codeViewData.Guid; - string pdbName = codeViewData.Path; - pdbName = Path.GetFileName(pdbName); + var pdbName = Path.GetFileName(asm.PdbName); foreach (string urlSymbolServer in urlSymbolServerList) { - string downloadURL = $"{urlSymbolServer}/{pdbName}/{pdbGuid.ToString("N").ToUpper() + pdbAge}/{pdbName}"; + string downloadURL = $"{urlSymbolServer}/{pdbName}/{asm.PdbGuid.ToString("N").ToUpper() + asm.PdbAge}/{pdbName}"; try { @@ -1064,7 +1085,7 @@ private async Task OnAssemblyLoadedJSEvent(SessionId sessionId, JObject ev var store = await LoadStore(sessionId, token); var assembly_name = eventArgs?["assembly_name"]?.Value(); - if (store.GetAssemblyByUnqualifiedName(assembly_name) != null) + if (store.GetAssemblyByName(assembly_name) != null) { Log("debug", $"Got AssemblyLoaded event for {assembly_name}, but skipping it as it has already been loaded."); return true; @@ -1083,7 +1104,7 @@ private async Task OnAssemblyLoadedJSEvent(SessionId sessionId, JObject ev var pdb_data = string.IsNullOrEmpty(pdb_b64) ? null : Convert.FromBase64String(pdb_b64); var context = GetContext(sessionId); - foreach (var source in store.Add(assembly_name, assembly_data, pdb_data)) + foreach (var source in store.Add(sessionId, assembly_name, assembly_data, pdb_data, token)) { await OnSourceFileAdded(sessionId, source, context, token); } @@ -1211,7 +1232,7 @@ internal async Task LoadStore(SessionId sessionId, CancellationToken { ExecutionContext context = GetContext(sessionId); - if (Interlocked.CompareExchange(ref context.store, new DebugStore(logger), null) != null) + if (Interlocked.CompareExchange(ref context.store, new DebugStore(this, logger), null) != null) return await context.Source.Task; try @@ -1225,7 +1246,7 @@ internal async Task LoadStore(SessionId sessionId, CancellationToken } await - foreach (SourceFile source in context.store.Load(loaded_files, token).WithCancellation(token)) + foreach (SourceFile source in context.store.Load(sessionId, loaded_files, token).WithCancellation(token)) { await OnSourceFileAdded(sessionId, source, context, token); } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs index d6b260375c1f0e..6025d684659359 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs @@ -901,6 +901,35 @@ await EvaluateAndCheck( } ); } + + [Fact] + public async Task InspectLocalsUsingClassFromLibraryUsingDebugTypeFull() + { + byte[] bytes = File.ReadAllBytes(Path.Combine(DebuggerTestAppPath, "debugger-test-with-full-debug-type.dll")); + string asm_base64 = Convert.ToBase64String(bytes); + + string pdb_base64 = null; + bytes = File.ReadAllBytes(Path.Combine(DebuggerTestAppPath, "debugger-test-with-full-debug-type.pdb")); + pdb_base64 = Convert.ToBase64String(bytes); + + var expression = $"{{ let asm_b64 = '{asm_base64}'; let pdb_b64 = '{pdb_base64}'; invoke_static_method('[debugger-test] DebugTypeFull:CallToEvaluateLocal', asm_b64, pdb_b64); }}"; + + await EvaluateAndCheck( + "window.setTimeout(function() {" + expression + "; }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 860, 8, + "CallToEvaluateLocal", + wait_for_event_fn: async (pause_location) => + { + var a_props = await GetObjectOnFrame(pause_location["callFrames"][0], "a"); + await CheckProps(a_props, new + { + a = TNumber(10), + b = TNumber(20), + c = TNumber(30) + }, "a"); + } + ); + } //TODO add tests covering basic stepping behavior as step in/out/over } } diff --git a/src/mono/wasm/debugger/tests/debugger-test-with-full-debug-type/debugger-test-with-full-debug-type.csproj b/src/mono/wasm/debugger/tests/debugger-test-with-full-debug-type/debugger-test-with-full-debug-type.csproj new file mode 100644 index 00000000000000..7746f400741700 --- /dev/null +++ b/src/mono/wasm/debugger/tests/debugger-test-with-full-debug-type/debugger-test-with-full-debug-type.csproj @@ -0,0 +1,5 @@ + + + full + + diff --git a/src/mono/wasm/debugger/tests/debugger-test-with-full-debug-type/test.cs b/src/mono/wasm/debugger/tests/debugger-test-with-full-debug-type/test.cs new file mode 100644 index 00000000000000..3b9ab8aecbbd1a --- /dev/null +++ b/src/mono/wasm/debugger/tests/debugger-test-with-full-debug-type/test.cs @@ -0,0 +1,20 @@ +using System; + +namespace DebuggerTests +{ + public class ClassToInspectWithDebugTypeFull + { + int a; + int b; + int c; + public ClassToInspectWithDebugTypeFull() + { + a = 10; + b = 20; + c = 30; + Console.WriteLine(a); + Console.WriteLine(b); + Console.WriteLine(c); + } + } +} diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs index 813102bbbc047d..3d3502a83d4ade 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs @@ -849,3 +849,15 @@ public static void RunDebuggerBreak() VisibleMethodDebuggerBreak(); } } + +public class DebugTypeFull +{ + public static void CallToEvaluateLocal(string asm_base64, string pdb_base64) + { + var asm = System.Reflection.Assembly.LoadFrom("debugger-test-with-full-debug-type.dll"); + var myType = asm.GetType("DebuggerTests.ClassToInspectWithDebugTypeFull"); + var myMethod = myType.GetConstructor(new Type[] { }); + var a = myMethod.Invoke(new object[]{}); + System.Diagnostics.Debugger.Break(); + } +} diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj index c3cc1800aad87e..a55b5a513656b6 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj @@ -12,7 +12,6 @@ - @@ -22,13 +21,14 @@ + false $(AppDir) - $(MonoProjectRoot)wasm\test-main.js + debugger-main.js -1 @@ -38,6 +38,7 @@ + diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.extpost.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.extpost.js index 7e1523b6d38be5..a1867520b68579 100644 --- a/src/mono/wasm/runtime/cjs/dotnet.cjs.extpost.js +++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.extpost.js @@ -1,4 +1,5 @@ - -if (typeof globalThis.Module === "object") { +// if loaded into global namespace and configured with global Module, we will self start in compatibility mode +let ENVIRONMENT_IS_GLOBAL = typeof globalThis.Module === "object" && globalThis.__dotnet_runtime === __dotnet_runtime; +if (ENVIRONMENT_IS_GLOBAL) { createDotnetRuntime(() => { return globalThis.Module; }).then((exports) => exports); } \ No newline at end of file diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js index 0a76f5f5ca4ba5..4dfe69ccac8553 100644 --- a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js +++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js @@ -15,9 +15,13 @@ const DotnetSupportLib = { __dotnet_replacements); // here we replace things which are not exposed in another way - __dirname = scriptDirectory = __dotnet_replacements.scriptDirectory; + scriptDirectory = __dotnet_replacements.scriptDirectory; readAsync = __dotnet_replacements.readAsync; - var fetch = __dotnet_replacements.fetch;`, + var fetch = __dotnet_replacements.fetch; + if (ENVIRONMENT_IS_NODE) { + __dirname = __dotnet_replacements.scriptDirectory; + } + `, }; // the methods would be visible to EMCC linker diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.pre.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.pre.js index b98da867a3cb01..6b55ff1360bee7 100644 --- a/src/mono/wasm/runtime/cjs/dotnet.cjs.pre.js +++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.pre.js @@ -1,5 +1,4 @@ const MONO = {}, BINDING = {}, INTERNAL = {}; -let ENVIRONMENT_IS_GLOBAL = typeof globalThis.Module === "object"; if (ENVIRONMENT_IS_GLOBAL) { if (globalThis.Module.ready) { throw new Error("MONO_WASM: Module.ready couldn't be redefined.") @@ -7,8 +6,12 @@ if (ENVIRONMENT_IS_GLOBAL) { globalThis.Module.ready = Module.ready; Module = createDotnetRuntime = globalThis.Module; } +else if (typeof createDotnetRuntime === "object") { + Module = { ready: Module.ready, __undefinedConfig: Object.keys(createDotnetRuntime).length === 1 }; + Object.assign(Module, createDotnetRuntime); + createDotnetRuntime = Module; +} else if (typeof createDotnetRuntime === "function") { - ENVIRONMENT_IS_GLOBAL = false; Module = { ready: Module.ready }; const extension = createDotnetRuntime({ MONO, BINDING, INTERNAL, Module }) if (extension.ready) { diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts index cc4341a54dd3cd..0ee7f4c80e16d8 100644 --- a/src/mono/wasm/runtime/dotnet.d.ts +++ b/src/mono/wasm/runtime/dotnet.d.ts @@ -47,6 +47,9 @@ declare interface EmscriptenModule { preInit?: (() => any)[]; preRun?: (() => any)[]; postRun?: (() => any)[]; + onAbort?: { + (error: any): void; + }; onRuntimeInitialized?: () => any; instantiateWasm: (imports: any, successCallback: Function) => any; } @@ -258,7 +261,7 @@ declare function unbox_mono_obj(mono_obj: MonoObject): any; declare function mono_array_to_js_array(mono_array: MonoArray): any[] | null; declare function mono_bind_static_method(fqn: string, signature?: ArgsMarshalString): Function; -declare function mono_call_assembly_entry_point(assembly: string, args: any[], signature: ArgsMarshalString): any; +declare function mono_call_assembly_entry_point(assembly: string, args?: any[], signature?: ArgsMarshalString): number; declare function mono_wasm_load_bytes_into_heap(bytes: Uint8Array): VoidPtr; @@ -282,6 +285,9 @@ declare function getI64(offset: _MemOffset): number; declare function getF32(offset: _MemOffset): number; declare function getF64(offset: _MemOffset): number; +declare function mono_run_main_and_exit(main_assembly_name: string, args: string[]): Promise; +declare function mono_run_main(main_assembly_name: string, args: string[]): Promise; + declare const MONO: { mono_wasm_setenv: typeof mono_wasm_setenv; mono_wasm_load_bytes_into_heap: typeof mono_wasm_load_bytes_into_heap; @@ -293,6 +299,8 @@ declare const MONO: { mono_wasm_new_root_buffer: typeof mono_wasm_new_root_buffer; mono_wasm_new_root: typeof mono_wasm_new_root; mono_wasm_release_roots: typeof mono_wasm_release_roots; + mono_run_main: typeof mono_run_main; + mono_run_main_and_exit: typeof mono_run_main_and_exit; mono_wasm_add_assembly: (name: string, data: VoidPtr, size: number) => number; mono_wasm_load_runtime: (unused: string, debug_level: number) => void; config: MonoConfig | MonoConfigError; @@ -342,7 +350,7 @@ interface DotnetPublicAPI { }; } -declare function createDotnetRuntime(moduleFactory: (api: DotnetPublicAPI) => DotnetModuleConfig): Promise; +declare function createDotnetRuntime(moduleFactory: DotnetModuleConfig | ((api: DotnetPublicAPI) => DotnetModuleConfig)): Promise; declare type CreateDotnetRuntimeType = typeof createDotnetRuntime; declare global { function getDotnetRuntime(runtimeId: number): DotnetPublicAPI | undefined; diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js index 341fa01b9078e7..2f94047ddd0ea1 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js @@ -10,17 +10,18 @@ const DotnetSupportLib = { $DOTNET__postset: ` let __dotnet_replacements = {scriptDirectory, readAsync, fetch: globalThis.fetch}; let __dotnet_exportedAPI = __dotnet_runtime.__initializeImportsAndExports( - { isGlobal:ENVIRONMENT_IS_GLOBAL, isNode:ENVIRONMENT_IS_NODE, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, locateFile }, + { isGlobal:false, isNode:ENVIRONMENT_IS_NODE, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, locateFile }, { mono:MONO, binding:BINDING, internal:INTERNAL, module:Module }, __dotnet_replacements); // here we replace things which are not exposed in another way - __dirname = scriptDirectory = __dotnet_replacements.scriptDirectory; + scriptDirectory = __dotnet_replacements.scriptDirectory; readAsync = __dotnet_replacements.readAsync; var fetch = __dotnet_replacements.fetch; // here we replace things which are broken on NodeJS for ES6 if (ENVIRONMENT_IS_NODE) { + __dirname = __dotnet_replacements.scriptDirectory; getBinaryPromise = async () => { if (!wasmBinary) { try { diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.pre.js b/src/mono/wasm/runtime/es6/dotnet.es6.pre.js index 02d8cffb481854..b9b456f88e4e54 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.pre.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.pre.js @@ -9,6 +9,11 @@ if (typeof createDotnetRuntime === "function") { Object.assign(Module, extension); createDotnetRuntime = Module; } +else if (typeof createDotnetRuntime === "object") { + Module = { ready: Module.ready, __undefinedConfig: Object.keys(createDotnetRuntime).length === 1 }; + Object.assign(Module, createDotnetRuntime); + createDotnetRuntime = Module; +} else { throw new Error("MONO_WASM: Can't use moduleFactory callback of createDotnetRuntime function.") } diff --git a/src/mono/wasm/runtime/export-types.ts b/src/mono/wasm/runtime/export-types.ts index 77bca1f53b70db..3cb8c0df090773 100644 --- a/src/mono/wasm/runtime/export-types.ts +++ b/src/mono/wasm/runtime/export-types.ts @@ -9,7 +9,7 @@ import { EmscriptenModule, VoidPtr } from "./types/emscripten"; // this files has all public exports from the dotnet.js module // ----------------------------------------------------------- -declare function createDotnetRuntime(moduleFactory: (api: DotnetPublicAPI) => DotnetModuleConfig): Promise; +declare function createDotnetRuntime(moduleFactory: DotnetModuleConfig | ((api: DotnetPublicAPI) => DotnetModuleConfig)): Promise; declare type CreateDotnetRuntimeType = typeof createDotnetRuntime; // Here, declare things that go in the global namespace, or augment existing declarations in the global namespace diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts index f4b44fc9385d8d..911d0382afa049 100644 --- a/src/mono/wasm/runtime/exports.ts +++ b/src/mono/wasm/runtime/exports.ts @@ -31,7 +31,6 @@ import { mono_load_runtime_and_bcl_args, mono_wasm_load_config, mono_wasm_setenv, mono_wasm_set_runtime_options, mono_wasm_load_data_archive, mono_wasm_asm_loaded, - mono_wasm_set_main_args, mono_wasm_pre_init, mono_wasm_runtime_is_initialized, mono_wasm_on_runtime_initialized @@ -68,6 +67,7 @@ import { import { create_weak_ref } from "./weak-ref"; import { fetch_like, readAsync_like } from "./polyfills"; import { EmscriptenModule } from "./types/emscripten"; +import { mono_on_abort, mono_run_main, mono_run_main_and_exit } from "./run"; const MONO = { // current "public" MONO API @@ -81,6 +81,8 @@ const MONO = { mono_wasm_new_root_buffer, mono_wasm_new_root, mono_wasm_release_roots, + mono_run_main, + mono_run_main_and_exit, // for Blazor's future! mono_wasm_add_assembly: cwraps.mono_wasm_add_assembly, @@ -157,6 +159,10 @@ function initializeImportsAndExports( Configuration } }; + if (exports.module.__undefinedConfig) { + module.disableDotnet6Compatibility = true; + module.configSrc = "./mono-config.json"; + } // these could be overriden on DotnetModuleConfig if (!module.preInit) { @@ -171,10 +177,10 @@ function initializeImportsAndExports( } if (!module.print) { - module.print = console.log; + module.print = console.log.bind(console); } if (!module.printErr) { - module.printErr = console.error; + module.printErr = console.error.bind(console); } module.imports = module.imports || {}; if (!module.imports.require) { @@ -271,6 +277,10 @@ function initializeImportsAndExports( }); } + if (!module.onAbort) { + module.onAbort = () => mono_on_abort; + } + // this code makes it possible to find dotnet runtime on a page via global namespace, even when there are multiple runtimes at the same time let list: RuntimeList; if (!globalThisAny.getDotnetRuntime) { @@ -345,7 +355,6 @@ const INTERNAL: any = { mono_wasm_enable_on_demand_gc: cwraps.mono_wasm_enable_on_demand_gc, mono_profiler_init_aot: cwraps.mono_profiler_init_aot, mono_wasm_set_runtime_options, - mono_wasm_set_main_args: mono_wasm_set_main_args, mono_wasm_exec_regression: cwraps.mono_wasm_exec_regression, mono_method_resolve,//MarshalTests.cs mono_bind_static_method,// MarshalTests.cs diff --git a/src/mono/wasm/runtime/icu.ts b/src/mono/wasm/runtime/icu.ts index 58ad13628cee9f..da5d03bd8cb8a4 100644 --- a/src/mono/wasm/runtime/icu.ts +++ b/src/mono/wasm/runtime/icu.ts @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import cwraps from "./cwraps"; +import { Module } from "./imports"; import { GlobalizationMode } from "./types"; import { VoidPtr } from "./types/emscripten"; @@ -46,7 +47,7 @@ export function mono_wasm_globalization_init(globalization_mode: GlobalizationMo invariantMode = true; } else { const msg = "invariant globalization mode is inactive and no ICU data archives were loaded"; - console.error(`MONO_WASM: ERROR: ${msg}`); + Module.printErr(`MONO_WASM: ERROR: ${msg}`); throw new Error(msg); } } diff --git a/src/mono/wasm/runtime/method-calls.ts b/src/mono/wasm/runtime/method-calls.ts index 64d4d06eb8dcb5..698da9121555c5 100644 --- a/src/mono/wasm/runtime/method-calls.ts +++ b/src/mono/wasm/runtime/method-calls.ts @@ -261,7 +261,7 @@ export function mono_bind_static_method(fqn: string, signature?: ArgsMarshalStri return mono_bind_method(method, null, signature, fqn); } -export function mono_bind_assembly_entry_point(assembly: string, signature: ArgsMarshalString): Function { +export function mono_bind_assembly_entry_point(assembly: string, signature?: ArgsMarshalString): Function { bindings_lazy_init();// TODO remove this once Blazor does better startup const asm = cwraps.mono_wasm_assembly_load(assembly); @@ -272,23 +272,20 @@ export function mono_bind_assembly_entry_point(assembly: string, signature: Args if (!method) throw new Error("Could not find entry point for assembly: " + assembly); - if (typeof signature === "undefined") + if (!signature) signature = mono_method_get_call_signature(method); - return function (...args: any[]) { - try { - if (args.length > 0 && Array.isArray(args[0])) - args[0] = js_array_to_mono_array(args[0], true, false); - - const result = call_method(method, undefined, signature, args); - return Promise.resolve(result); - } catch (error) { - return Promise.reject(error); - } + return async function (...args: any[]) { + if (args.length > 0 && Array.isArray(args[0])) + args[0] = js_array_to_mono_array(args[0], true, false); + return call_method(method, undefined, signature!, args); }; } -export function mono_call_assembly_entry_point(assembly: string, args: any[], signature: ArgsMarshalString): any { +export function mono_call_assembly_entry_point(assembly: string, args?: any[], signature?: ArgsMarshalString): number { + if (!args) { + args = [[]]; + } return mono_bind_assembly_entry_point(assembly, signature)(...args); } diff --git a/src/mono/wasm/runtime/rollup.config.js b/src/mono/wasm/runtime/rollup.config.js index b9266366507e94..bb9a984721d874 100644 --- a/src/mono/wasm/runtime/rollup.config.js +++ b/src/mono/wasm/runtime/rollup.config.js @@ -35,7 +35,8 @@ const terserConfig = { }, mangle: { // because of stack walk at src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs - keep_fnames: /(mono_wasm_runtime_ready|mono_wasm_fire_debugger_agent_message)/, + // and unit test at src\libraries\System.Private.Runtime.InteropServices.JavaScript\tests\timers.js + keep_fnames: /(mono_wasm_runtime_ready|mono_wasm_fire_debugger_agent_message|mono_wasm_set_timeout_exec)/, }, }; const plugins = isDebug ? [writeOnChangePlugin()] : [terser(terserConfig), writeOnChangePlugin()]; @@ -64,6 +65,13 @@ const iffeConfig = { plugins, } ], + onwarn: (warning, handler) => { + if (warning.code === "EVAL" && warning.loc.file.indexOf("method-calls.ts") != -1) { + return; + } + + handler(warning); + }, plugins: [consts({ productVersion, configuration }), typescript()] }; const typesConfig = { @@ -86,7 +94,7 @@ if (isDebug) { format: "es", file: "./dotnet.d.ts", banner: banner_generated, - plugins: [writeOnChangePlugin()], + plugins: [alwaysLF(), writeOnChangePlugin()], }); } @@ -103,6 +111,19 @@ function writeOnChangePlugin() { }; } +// force always unix line ending +function alwaysLF() { + return { + name: "writeOnChange", + generateBundle: (options, bundle) => { + const name = Object.keys(bundle)[0]; + const asset = bundle[name]; + const code = asset.code; + asset.code = code.replace(/\r/g, ""); + } + }; +} + async function writeWhenChanged(options, bundle) { try { const name = Object.keys(bundle)[0]; diff --git a/src/mono/wasm/runtime/run.ts b/src/mono/wasm/runtime/run.ts new file mode 100644 index 00000000000000..738873e360ffff --- /dev/null +++ b/src/mono/wasm/runtime/run.ts @@ -0,0 +1,40 @@ +import { Module } from "./imports"; +import { mono_call_assembly_entry_point } from "./method-calls"; +import { mono_wasm_set_main_args, runtime_is_initialized_reject } from "./startup"; + + +export async function mono_run_main_and_exit(main_assembly_name: string, args: string[]): Promise { + try { + const result = await mono_run_main(main_assembly_name, args); + set_exit_code(result); + } catch (error) { + set_exit_code(1, error); + } +} + +export async function mono_run_main(main_assembly_name: string, args: string[]): Promise { + mono_wasm_set_main_args(main_assembly_name, args); + return mono_call_assembly_entry_point(main_assembly_name, [args], "m"); +} + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export function mono_on_abort(error: any): void { + runtime_is_initialized_reject(error); + set_exit_code(1, error); +} + +function set_exit_code(exit_code: number, reason?: any) { + if (reason) { + Module.printErr(reason.toString()); + if (reason.stack) { + Module.printErr(reason.stack); + } + } + const globalThisAny: any = globalThis; + if (typeof globalThisAny.exit === "function") { + globalThisAny.exit(exit_code); + } + else if (typeof globalThisAny.quit === "function") { + globalThisAny.quit(exit_code); + } +} diff --git a/src/mono/wasm/runtime/scheduling.ts b/src/mono/wasm/runtime/scheduling.ts index 8ab55fd045a9fa..b2b5616738e78f 100644 --- a/src/mono/wasm/runtime/scheduling.ts +++ b/src/mono/wasm/runtime/scheduling.ts @@ -3,7 +3,6 @@ import cwraps from "./cwraps"; -const timeout_queue: Function[] = []; let spread_timers_maximum = 0; export let isChromium = false; let pump_count = 0; @@ -19,23 +18,14 @@ if (globalThis.navigator) { } function pump_message() { - while (timeout_queue.length > 0) { - --pump_count; - const cb: Function = timeout_queue.shift()!; - cb(); - } while (pump_count > 0) { --pump_count; cwraps.mono_background_exec(); } } -function mono_wasm_set_timeout_exec(id: number) { - cwraps.mono_set_timeout_exec(id); -} - export function prevent_timer_throttling(): void { - if (isChromium) { + if (!isChromium) { return; } @@ -48,7 +38,7 @@ export function prevent_timer_throttling(): void { for (let schedule = next_reach_time; schedule < desired_reach_time; schedule += light_throttling_frequency) { const delay = schedule - now; setTimeout(() => { - mono_wasm_set_timeout_exec(0); + cwraps.mono_set_timeout_exec(0); pump_count++; pump_message(); }, delay); @@ -58,21 +48,17 @@ export function prevent_timer_throttling(): void { export function schedule_background_exec(): void { ++pump_count; - if (typeof globalThis.setTimeout === "function") { - globalThis.setTimeout(pump_message, 0); - } + setTimeout(pump_message, 0); } +let lastScheduledTimeoutId: any = undefined; export function mono_set_timeout(timeout: number, id: number): void { - - if (typeof globalThis.setTimeout === "function") { - globalThis.setTimeout(function () { - mono_wasm_set_timeout_exec(id); - }, timeout); - } else { - ++pump_count; - timeout_queue.push(function () { - mono_wasm_set_timeout_exec(id); - }); + function mono_wasm_set_timeout_exec() { + cwraps.mono_set_timeout_exec(id); } -} \ No newline at end of file + if (lastScheduledTimeoutId) { + clearTimeout(lastScheduledTimeoutId); + lastScheduledTimeoutId = undefined; + } + lastScheduledTimeoutId = setTimeout(mono_wasm_set_timeout_exec, timeout); +} diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 6ab3aaa943df64..cc115366db6db1 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -142,7 +142,7 @@ function _handle_fetched_asset(ctx: MonoInitContext, asset: AssetEntry, url: str } else if (asset.behavior === "icu") { if (!mono_wasm_load_icu_data(offset!)) - console.error(`MONO_WASM: Error loading ICU asset ${asset.name}`); + Module.printErr(`MONO_WASM: Error loading ICU asset ${asset.name}`); } else if (asset.behavior === "resource") { cwraps.mono_wasm_add_satellite_assembly(virtualName, asset.culture!, offset!, bytes.length); @@ -232,7 +232,7 @@ function finalize_startup(config: MonoConfig | MonoConfigError | undefined): voi runtime_is_initialized_resolve(); } catch (err: any) { - console.error("MONO_WASM: Error in finalize_startup:", err); + Module.printErr("MONO_WASM: Error in finalize_startup: " + err); runtime_is_initialized_reject(err); throw err; } @@ -399,7 +399,7 @@ export async function mono_load_runtime_and_bcl_args(config: MonoConfig | MonoCo console.log("MONO_WASM: loaded_files: " + JSON.stringify(ctx.loaded_files)); } } catch (err: any) { - console.error("MONO_WASM: Error in mono_load_runtime_and_bcl_args:", err); + Module.printErr("MONO_WASM: Error in mono_load_runtime_and_bcl_args: " + err); runtime_is_initialized_reject(err); throw err; } @@ -468,9 +468,9 @@ export function mono_wasm_load_data_archive(data: Uint8Array, prefix: string): b */ export async function mono_wasm_load_config(configFilePath: string): Promise { const module = Module; - module.addRunDependency(configFilePath); try { - // NOTE: when we add nodejs make sure to include the nodejs fetch package + module.addRunDependency(configFilePath); + const configRaw = await runtimeHelpers.fetch(configFilePath); const config = await configRaw.json(); @@ -479,15 +479,13 @@ export async function mono_wasm_load_config(configFilePath: string): Promise any)[]; preRun?: (() => any)[]; postRun?: (() => any)[]; + onAbort?: { (error: any): void }; onRuntimeInitialized?: () => any; instantiateWasm: (imports: any, successCallback: Function) => any; } diff --git a/src/mono/wasm/sln/WasmBuild.sln b/src/mono/wasm/sln/WasmBuild.sln new file mode 100755 index 00000000000000..10256d28e62648 --- /dev/null +++ b/src/mono/wasm/sln/WasmBuild.sln @@ -0,0 +1,73 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31722.452 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WasmBuildTasks", "..\..\..\tasks\WasmBuildTasks\WasmBuildTasks.csproj", "{D5BD9C0C-8A05-493E-BE45-13AF8286CD92}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WasmAppBuilder", "..\..\..\tasks\WasmAppBuilder\WasmAppBuilder.csproj", "{8DEBFDE2-B127-46D7-92CF-EEA6D1DA2554}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoAOTCompiler", "..\..\..\tasks\AotCompilerTask\MonoAOTCompiler.csproj", "{A9C02284-0387-42E7-BF78-47DF13656D5E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Build.Tests", "..\..\..\tests\BuildWasmApps\Wasm.Build.Tests\Wasm.Build.Tests.csproj", "{94E18644-B0E5-4DBB-9CE4-EA1515ACE4C2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DebuggerTestSuite", "..\debugger\DebuggerTestSuite\DebuggerTestSuite.csproj", "{4C0EE027-FC30-4167-B2CF-A6D18F00E08F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BrowserDebugHost", "..\debugger\BrowserDebugHost\BrowserDebugHost.csproj", "{292A88FD-795F-467A-8801-B5B791CEF96E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BrowserDebugProxy", "..\debugger\BrowserDebugProxy\BrowserDebugProxy.csproj", "{F5AE2AF5-3C30-45E3-A0C6-D962C51FE5E7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WasmAppHost", "..\host\WasmAppHost.csproj", "{C7099764-EC2E-4FAF-9057-0321893DE4F8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplyUpdateReferencedAssembly", "..\debugger\tests\ApplyUpdateReferencedAssembly\ApplyUpdateReferencedAssembly.csproj", "{75477B6F-DC8E-4002-88B8-017C992C568E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D5BD9C0C-8A05-493E-BE45-13AF8286CD92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5BD9C0C-8A05-493E-BE45-13AF8286CD92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5BD9C0C-8A05-493E-BE45-13AF8286CD92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5BD9C0C-8A05-493E-BE45-13AF8286CD92}.Release|Any CPU.Build.0 = Release|Any CPU + {8DEBFDE2-B127-46D7-92CF-EEA6D1DA2554}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8DEBFDE2-B127-46D7-92CF-EEA6D1DA2554}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DEBFDE2-B127-46D7-92CF-EEA6D1DA2554}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8DEBFDE2-B127-46D7-92CF-EEA6D1DA2554}.Release|Any CPU.Build.0 = Release|Any CPU + {A9C02284-0387-42E7-BF78-47DF13656D5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9C02284-0387-42E7-BF78-47DF13656D5E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9C02284-0387-42E7-BF78-47DF13656D5E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9C02284-0387-42E7-BF78-47DF13656D5E}.Release|Any CPU.Build.0 = Release|Any CPU + {94E18644-B0E5-4DBB-9CE4-EA1515ACE4C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94E18644-B0E5-4DBB-9CE4-EA1515ACE4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94E18644-B0E5-4DBB-9CE4-EA1515ACE4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94E18644-B0E5-4DBB-9CE4-EA1515ACE4C2}.Release|Any CPU.Build.0 = Release|Any CPU + {4C0EE027-FC30-4167-B2CF-A6D18F00E08F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C0EE027-FC30-4167-B2CF-A6D18F00E08F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C0EE027-FC30-4167-B2CF-A6D18F00E08F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C0EE027-FC30-4167-B2CF-A6D18F00E08F}.Release|Any CPU.Build.0 = Release|Any CPU + {292A88FD-795F-467A-8801-B5B791CEF96E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {292A88FD-795F-467A-8801-B5B791CEF96E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {292A88FD-795F-467A-8801-B5B791CEF96E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {292A88FD-795F-467A-8801-B5B791CEF96E}.Release|Any CPU.Build.0 = Release|Any CPU + {F5AE2AF5-3C30-45E3-A0C6-D962C51FE5E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5AE2AF5-3C30-45E3-A0C6-D962C51FE5E7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5AE2AF5-3C30-45E3-A0C6-D962C51FE5E7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5AE2AF5-3C30-45E3-A0C6-D962C51FE5E7}.Release|Any CPU.Build.0 = Release|Any CPU + {75477B6F-DC8E-4002-88B8-017C992C568E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75477B6F-DC8E-4002-88B8-017C992C568E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75477B6F-DC8E-4002-88B8-017C992C568E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75477B6F-DC8E-4002-88B8-017C992C568E}.Release|Any CPU.Build.0 = Release|Any CPU + {C7099764-EC2E-4FAF-9057-0321893DE4F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7099764-EC2E-4FAF-9057-0321893DE4F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7099764-EC2E-4FAF-9057-0321893DE4F8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7099764-EC2E-4FAF-9057-0321893DE4F8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2BDE8FDE-4261-4B4D-8B54-ACC88B06C8D1} + EndGlobalSection +EndGlobal diff --git a/src/mono/wasm/test-main.js b/src/mono/wasm/test-main.js index 84b26ec78bac86..58f4a4d1f22453 100644 --- a/src/mono/wasm/test-main.js +++ b/src/mono/wasm/test-main.js @@ -22,40 +22,37 @@ const originalConsole = { error: console.error }; -let isXUnitDoneCheck = false; -let isXmlDoneCheck = false; - -function proxyMethod(prefix, func, asJson) { +function proxyConsoleMethod(prefix, func, asJson) { return function () { - const args = [...arguments]; - let payload = args[0]; - if (payload === undefined) payload = 'undefined'; - else if (payload === null) payload = 'null'; - else if (typeof payload === 'function') payload = payload.toString(); - else if (typeof payload !== 'string') { - try { - payload = JSON.stringify(payload); - } catch (e) { - payload = payload.toString(); + try { + const args = [...arguments]; + let payload = args[0]; + if (payload === undefined) payload = 'undefined'; + else if (payload === null) payload = 'null'; + else if (typeof payload === 'function') payload = payload.toString(); + else if (typeof payload !== 'string') { + try { + payload = JSON.stringify(payload); + } catch (e) { + payload = payload.toString(); + } } - } - if (payload.indexOf("=== TEST EXECUTION SUMMARY ===") != -1) { - isXUnitDoneCheck = true; - } - if (payload.startsWith("STARTRESULTXML")) { - originalConsole.log('Sending RESULTXML') - isXmlDoneCheck = true; - func(payload); - } - else if (asJson) { - func(JSON.stringify({ - method: prefix, - payload: payload, - arguments: args - })); - } else { - func([prefix + payload, ...args.slice(1)]); + if (payload.startsWith("STARTRESULTXML")) { + originalConsole.log('Sending RESULTXML') + func(payload); + } + else if (asJson) { + func(JSON.stringify({ + method: prefix, + payload: payload, + arguments: args + })); + } else { + func([prefix + payload, ...args.slice(1)]); + } + } catch (err) { + originalConsole.error(`proxyConsole failed: ${err}`) } }; }; @@ -63,7 +60,7 @@ function proxyMethod(prefix, func, asJson) { const methods = ["debug", "trace", "warn", "info", "error"]; for (let m of methods) { if (typeof (console[m]) !== "function") { - console[m] = proxyMethod(`console.${m}: `, console.log, false); + console[m] = proxyConsoleMethod(`console.${m}: `, console.log, false); } } @@ -94,7 +91,7 @@ if (is_browser) { // redirect output early, so that when emscripten starts it's already redirected for (let m of ["log", ...methods]) - console[m] = proxyMethod(`console.${m}`, send, true); + console[m] = proxyConsoleMethod(`console.${m}`, send, true); } if (typeof globalThis.crypto === 'undefined') { @@ -211,13 +208,9 @@ const App = { return; } try { - const main_assembly_name = processedArguments.applicationArgs[1]; const app_args = processedArguments.applicationArgs.slice(2); - INTERNAL.mono_wasm_set_main_args(main_assembly_name, app_args); - - // Automatic signature isn't working correctly - const result = await BINDING.call_assembly_entry_point(main_assembly_name, [app_args], "m"); + const result = await MONO.mono_run_main(main_assembly_name, app_args); set_exit_code(result); } catch (error) { set_exit_code(1, error); @@ -369,20 +362,9 @@ async function loadDotnet(file) { }; } else if (is_browser) { // vanila JS in browser loadScript = async function (file) { - const script = document.createElement("script"); - script.src = file; - document.head.appendChild(script); - let timeout = 100; - // bysy spin waiting for script to load into global namespace - while (timeout > 0) { - if (globalThis.createDotnetRuntime) { - return globalThis.createDotnetRuntime; - } - // delay 10ms - await new Promise(resolve => setTimeout(resolve, 10)); - timeout--; - } - throw new Error("Can't load " + file); + globalThis.exports = {}; // if we are loading cjs file + const createDotnetRuntime = await import(file); + return typeof createDotnetRuntime === "function" ? createDotnetRuntime : globalThis.exports.createDotnetRuntime; } } else if (typeof globalThis.load !== 'undefined') { diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 0a8e59dd439363..5340cefff55a5d 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -12,6 +12,7 @@ emcc $(ArtifactsObjDir)wasm <_EmccDefaultsRspPath>$(NativeBinDir)src\emcc-default.rsp + false diff --git a/src/native/libs/Common/pal_config.h.in b/src/native/libs/Common/pal_config.h.in index 00b774b87341b4..431071204dfb8e 100644 --- a/src/native/libs/Common/pal_config.h.in +++ b/src/native/libs/Common/pal_config.h.in @@ -45,7 +45,9 @@ #cmakedefine01 HAVE_TIOCGWINSZ #cmakedefine01 HAVE_SCHED_GETAFFINITY #cmakedefine01 HAVE_SCHED_SETAFFINITY +#cmakedefine01 HAVE_SCHED_GETCPU #cmakedefine01 HAVE_PTHREAD_SETCANCELSTATE +#cmakedefine01 HAVE_GNU_LIBNAMES_H #cmakedefine01 HAVE_ARC4RANDOM_BUF #cmakedefine01 KEVENT_HAS_VOID_UDATA #cmakedefine01 HAVE_FDS_BITS diff --git a/src/native/libs/System.Native/CMakeLists.txt b/src/native/libs/System.Native/CMakeLists.txt index 8fea908f798014..60780bf85c7089 100644 --- a/src/native/libs/System.Native/CMakeLists.txt +++ b/src/native/libs/System.Native/CMakeLists.txt @@ -4,11 +4,18 @@ if (NOT CLR_CMAKE_TARGET_MACCATALYST AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CM add_definitions(-DHAS_CONSOLE_SIGNALS) endif () +if (CLR_CMAKE_HOST_ALPINE_LINUX) + # Fix up the thread stack size for MUSL to more reasonable size. + # TODO: https://github.com/dotnet/runtimelab/issues/791 + add_definitions(-DENSURE_PRIMARY_STACK_SIZE) +endif () + if (CLR_CMAKE_TARGET_OSX) add_definitions(-D_DARWIN_C_SOURCE) endif () set(NATIVE_SOURCES + pal_dynamicload.c pal_errno.c pal_interfaceaddresses.c pal_io.c diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c index ab227685b5a898..8b251d93e3f1d9 100644 --- a/src/native/libs/System.Native/entrypoints.c +++ b/src/native/libs/System.Native/entrypoints.c @@ -7,6 +7,7 @@ #include "pal_autoreleasepool.h" #include "pal_console.h" #include "pal_datetime.h" +#include "pal_dynamicload.h" #include "pal_environment.h" #include "pal_errno.h" #include "pal_interfaceaddresses.h" @@ -235,6 +236,12 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_LowLevelMonitor_Wait) DllImportEntry(SystemNative_LowLevelMonitor_TimedWait) DllImportEntry(SystemNative_LowLevelMonitor_Signal_Release) + DllImportEntry(SystemNative_LoadLibrary) + DllImportEntry(SystemNative_GetProcAddress) + DllImportEntry(SystemNative_FreeLibrary) + DllImportEntry(SystemNative_SchedGetCpu) + DllImportEntry(SystemNative_Exit) + DllImportEntry(SystemNative_Abort) DllImportEntry(SystemNative_UTimensat) DllImportEntry(SystemNative_GetTimestamp) DllImportEntry(SystemNative_GetCpuUtilization) @@ -253,6 +260,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_PWrite) DllImportEntry(SystemNative_PReadV) DllImportEntry(SystemNative_PWriteV) + DllImportEntry(SystemNative_CreateThread) DllImportEntry(SystemNative_EnablePosixSignalHandling) DllImportEntry(SystemNative_DisablePosixSignalHandling) DllImportEntry(SystemNative_HandleNonCanceledPosixSignal) diff --git a/src/native/libs/System.Native/pal_dynamicload.c b/src/native/libs/System.Native/pal_dynamicload.c new file mode 100644 index 00000000000000..20ac55859eb1ad --- /dev/null +++ b/src/native/libs/System.Native/pal_dynamicload.c @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "pal_config.h" +#include "pal_dynamicload.h" + +#include +#include + +#if HAVE_GNU_LIBNAMES_H +#include +#endif + +void* SystemNative_LoadLibrary(const char* filename) +{ + // Check whether we have been requested to load 'libc'. If that's the case, then: + // * For Linux, use the full name of the library that is defined in by the + // LIBC_SO constant. The problem is that calling dlopen("libc.so") will fail for libc even + // though it works for other libraries. The reason is that libc.so is just linker script + // (i.e. a test file). + // As a result, we have to use the full name (i.e. lib.so.6) that is defined by LIBC_SO. + // * For macOS, use constant value absolute path "/usr/lib/libc.dylib". + // * For FreeBSD, use constant value "libc.so.7". + // * For rest of Unices, use constant value "libc.so". + if (strcmp(filename, "libc") == 0) + { +#if defined(__APPLE__) + filename = "/usr/lib/libc.dylib"; +#elif defined(__FreeBSD__) + filename = "libc.so.7"; +#elif defined(LIBC_SO) + filename = LIBC_SO; +#else + filename = "libc.so"; +#endif + } + + return dlopen(filename, RTLD_LAZY); +} + +void* SystemNative_GetProcAddress(void* handle, const char* symbol) +{ + // We're not trying to disambiguate between "symbol was not found" and "symbol found, but + // the value is null". .NET does not define a behavior for DllImports of null entrypoints, + // so we might as well take the "not found" path on the managed side. + return dlsym(handle, symbol); +} + +void SystemNative_FreeLibrary(void* handle) +{ + dlclose(handle); +} diff --git a/src/native/libs/System.Native/pal_dynamicload.h b/src/native/libs/System.Native/pal_dynamicload.h new file mode 100644 index 00000000000000..857f45def88eb3 --- /dev/null +++ b/src/native/libs/System.Native/pal_dynamicload.h @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "pal_compiler.h" +#include "pal_types.h" + +PALEXPORT void* SystemNative_LoadLibrary(const char* filename); + +PALEXPORT void* SystemNative_GetProcAddress(void* handle, const char* symbol); + +PALEXPORT void SystemNative_FreeLibrary(void* handle); diff --git a/src/native/libs/System.Native/pal_threading.c b/src/native/libs/System.Native/pal_threading.c index 6f69d1056b8f1f..30b021dcbd698b 100644 --- a/src/native/libs/System.Native/pal_threading.c +++ b/src/native/libs/System.Native/pal_threading.c @@ -12,6 +12,9 @@ #include #include #include +#if HAVE_SCHED_GETCPU +#include +#endif #if defined(TARGET_OSX) // So we can use the declaration of pthread_cond_timedwait_relative_np @@ -214,3 +217,71 @@ void SystemNative_LowLevelMonitor_Signal_Release(LowLevelMonitor* monitor) (void)error; // unused in release build } + +int32_t SystemNative_CreateThread(uintptr_t stackSize, void *(*startAddress)(void*), void *parameter) +{ + bool result = false; + pthread_attr_t attrs; + + int error = pthread_attr_init(&attrs); + if (error != 0) + { + // Do not call pthread_attr_destroy + return false; + } + + error = pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED); + assert(error == 0); + +#ifdef ENSURE_PRIMARY_STACK_SIZE + // TODO: https://github.com/dotnet/runtimelab/issues/791 + if (stackSize == 0) + { + stackSize = 1536 * 1024; + } +#endif + + if (stackSize > 0) + { + if (stackSize < PTHREAD_STACK_MIN) + { + stackSize = PTHREAD_STACK_MIN; + } + + error = pthread_attr_setstacksize(&attrs, stackSize); + if (error != 0) goto CreateThreadExit; + } + + pthread_t threadId; + error = pthread_create(&threadId, &attrs, startAddress, parameter); + if (error != 0) goto CreateThreadExit; + + result = true; + +CreateThreadExit: + error = pthread_attr_destroy(&attrs); + assert(error == 0); + + return result; +} + +int32_t SystemNative_SchedGetCpu() +{ +#if HAVE_SCHED_GETCPU + return sched_getcpu(); +#else + return -1; +#endif +} + +__attribute__((noreturn)) +void SystemNative_Exit(int32_t exitCode) +{ + exit(exitCode); +} + +__attribute__((noreturn)) +void SystemNative_Abort() +{ + abort(); +} diff --git a/src/native/libs/System.Native/pal_threading.h b/src/native/libs/System.Native/pal_threading.h index 599d678fc98666..a3b336acd0461b 100644 --- a/src/native/libs/System.Native/pal_threading.h +++ b/src/native/libs/System.Native/pal_threading.h @@ -21,3 +21,11 @@ PALEXPORT void SystemNative_LowLevelMonitor_Wait(LowLevelMonitor* monitor); PALEXPORT int32_t SystemNative_LowLevelMonitor_TimedWait(LowLevelMonitor *monitor, int32_t timeoutMilliseconds); PALEXPORT void SystemNative_LowLevelMonitor_Signal_Release(LowLevelMonitor* monitor); + +PALEXPORT int32_t SystemNative_CreateThread(uintptr_t stackSize, void *(*startAddress)(void*), void *parameter); + +PALEXPORT int32_t SystemNative_SchedGetCpu(void); + +PALEXPORT __attribute__((noreturn)) void SystemNative_Exit(int32_t exitCode); + +PALEXPORT __attribute__((noreturn)) void SystemNative_Abort(void); diff --git a/src/native/libs/configure.cmake b/src/native/libs/configure.cmake index 57708aff84b651..8eadf95602021f 100644 --- a/src/native/libs/configure.cmake +++ b/src/native/libs/configure.cmake @@ -261,11 +261,20 @@ check_symbol_exists( "sched.h" HAVE_SCHED_SETAFFINITY) +check_symbol_exists( + sched_getcpu + "sched.h" + HAVE_SCHED_GETCPU) + check_symbol_exists( pthread_setcancelstate "pthread.h" HAVE_PTHREAD_SETCANCELSTATE) +check_include_files( + gnu/lib-names.h + HAVE_GNU_LIBNAMES_H) + check_symbol_exists( arc4random_buf "stdlib.h" diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index dee6b537b7bd59..701edb6063d0ca 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -744,13 +744,13 @@ private bool PrecompileLibrary(PrecompileArguments args) Log.LogMessage(importance, $"{msgPrefix}Exec (with response file contents expanded) in {args.WorkingDir}: {envStr}{CompilerBinaryPath} {File.ReadAllText(args.ResponseFilePath)}"); } - Log.LogMessage(importance, output); - if (exitCode != 0) { - Log.LogError($"Precompiling failed for {assembly}"); + Log.LogError($"Precompiling failed for {assembly}.{Environment.NewLine}{output}"); return false; } + + Log.LogMessage(importance, output); } catch (Exception ex) { diff --git a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs index e1f735efdffa43..bd0a775ce55109 100644 --- a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs +++ b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs @@ -158,6 +158,11 @@ public string TargetOS /// public bool EnableRuntimeLogging { get; set; } + /// + /// Enables App Sandbox for Mac Catalyst apps + /// + public bool EnableAppSandbox { get; set; } + public override bool Execute() { bool isDevice = (TargetOS == TargetNames.iOS || TargetOS == TargetNames.tvOS); @@ -229,12 +234,17 @@ public override bool Execute() throw new ArgumentException("Using DiagnosticPorts require diagnostics_tracing runtime component."); } + if (EnableAppSandbox && (string.IsNullOrEmpty(DevTeamProvisioning) || DevTeamProvisioning == "-")) + { + throw new ArgumentException("DevTeamProvisioning must be set to a valid value when App Sandbox is enabled, using '-' is not supported."); + } + var generator = new Xcode(Log, TargetOS, Arch); if (GenerateXcodeProject) { XcodeProjectPath = generator.GenerateXCode(ProjectName, MainLibraryFileName, assemblerFiles, assemblerFilesToLink, - AppDir, binDir, MonoRuntimeHeaders, !isDevice, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, Optimized, EnableRuntimeLogging, DiagnosticPorts, RuntimeComponents, NativeMainSource); + AppDir, binDir, MonoRuntimeHeaders, !isDevice, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, Optimized, EnableRuntimeLogging, EnableAppSandbox, DiagnosticPorts, RuntimeComponents, NativeMainSource); if (BuildAppBundle) { @@ -252,7 +262,7 @@ public override bool Execute() else if (GenerateCMakeProject) { generator.GenerateCMake(ProjectName, MainLibraryFileName, assemblerFiles, assemblerFilesToLink, - AppDir, binDir, MonoRuntimeHeaders, !isDevice, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, Optimized, EnableRuntimeLogging, DiagnosticPorts, RuntimeComponents, NativeMainSource); + AppDir, binDir, MonoRuntimeHeaders, !isDevice, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, Optimized, EnableRuntimeLogging, EnableAppSandbox, DiagnosticPorts, RuntimeComponents, NativeMainSource); } return true; diff --git a/src/tasks/AppleAppBuilder/Xcode.cs b/src/tasks/AppleAppBuilder/Xcode.cs index 0ff335efadd747..8fba3ba2b50ba0 100644 --- a/src/tasks/AppleAppBuilder/Xcode.cs +++ b/src/tasks/AppleAppBuilder/Xcode.cs @@ -145,11 +145,12 @@ public string GenerateXCode( bool invariantGlobalization, bool optimized, bool enableRuntimeLogging, + bool enableAppSandbox, string? diagnosticPorts, string? runtimeComponents=null, string? nativeMainSource = null) { - var cmakeDirectoryPath = GenerateCMake(projectName, entryPointLib, asmFiles, asmLinkFiles, workspace, binDir, monoInclude, preferDylibs, useConsoleUiTemplate, forceAOT, forceInterpreter, invariantGlobalization, optimized, enableRuntimeLogging, diagnosticPorts, runtimeComponents, nativeMainSource); + var cmakeDirectoryPath = GenerateCMake(projectName, entryPointLib, asmFiles, asmLinkFiles, workspace, binDir, monoInclude, preferDylibs, useConsoleUiTemplate, forceAOT, forceInterpreter, invariantGlobalization, optimized, enableRuntimeLogging, enableAppSandbox, diagnosticPorts, runtimeComponents, nativeMainSource); CreateXcodeProject(projectName, cmakeDirectoryPath); return Path.Combine(binDir, projectName, projectName + ".xcodeproj"); } @@ -201,6 +202,7 @@ public string GenerateCMake( bool invariantGlobalization, bool optimized, bool enableRuntimeLogging, + bool enableAppSandbox, string? diagnosticPorts, string? runtimeComponents=null, string? nativeMainSource = null) @@ -236,7 +238,8 @@ public string GenerateCMake( var entitlements = new List>(); bool hardenedRuntime = false; - if (Target == TargetNames.MacCatalyst && !forceAOT) { + if (Target == TargetNames.MacCatalyst && !forceAOT) + { hardenedRuntime = true; /* for mmmap MAP_JIT */ @@ -245,6 +248,15 @@ public string GenerateCMake( entitlements.Add (KeyValuePair.Create ("com.apple.security.cs.disable-library-validation", "")); } + if (enableAppSandbox) + { + hardenedRuntime = true; + entitlements.Add (KeyValuePair.Create ("com.apple.security.app-sandbox", "")); + + // the networking entitlement is necessary to enable communication between the test app and xharness + entitlements.Add (KeyValuePair.Create ("com.apple.security.network.client", "")); + } + string cmakeLists = Utils.GetEmbeddedResource("CMakeLists.txt.template") .Replace("%ProjectName%", projectName) .Replace("%AppResources%", string.Join(Environment.NewLine, resources.Where(r => !r.EndsWith("-llvm.o")).Select(r => " " + Path.GetRelativePath(binDir, r)))) diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index 8170c0f04c483a..9526a602876f28 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -24,7 +24,7 @@ public class PInvokeTableGenerator : Task [Output] public string FileWrites { get; private set; } = string.Empty; - private static char[] s_charsToReplace = new[] { '.', '-', }; + private static char[] s_charsToReplace = new[] { '.', '-', '+' }; public override bool Execute() { @@ -88,7 +88,21 @@ public void GenPInvokeTable(string[] pinvokeModules, string[] assemblies) private void CollectPInvokes(List pinvokes, List callbacks, Type type) { - foreach (var method in type.GetMethods(BindingFlags.DeclaredOnly|BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance)) { + foreach (var method in type.GetMethods(BindingFlags.DeclaredOnly|BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance)) + { + try + { + CollectPInvokesForMethod(method); + } + catch (Exception ex) + { + Log.LogMessage(MessageImportance.Low, $"Could not get pinvoke, or callbacks for method {method.Name}: {ex}"); + continue; + } + } + + void CollectPInvokesForMethod(MethodInfo method) + { if ((method.Attributes & MethodAttributes.PinvokeImpl) != 0) { var dllimport = method.CustomAttributes.First(attr => attr.AttributeType.Name == "DllImportAttribute"); @@ -164,7 +178,8 @@ private void EmitPInvokeTable(StreamWriter w, Dictionary modules Where(l => l.Module == module && !l.Skip). OrderBy(l => l.EntryPoint). GroupBy(d => d.EntryPoint). - Select (l => "{\"" + l.Key + "\", " + l.Key + "}, // " + string.Join (", ", l.Select(c => c.Method.DeclaringType!.Module!.Assembly!.GetName ()!.Name!).Distinct().OrderBy(n => n))); + Select (l => "{\"" + FixupSymbolName(l.Key) + "\", " + FixupSymbolName(l.Key) + "}, " + + "// " + string.Join (", ", l.Select(c => c.Method.DeclaringType!.Module!.Assembly!.GetName ()!.Name!).Distinct().OrderBy(n => n))); foreach (var pinvoke in assemblies_pinvokes) { w.WriteLine (pinvoke); @@ -216,6 +231,45 @@ static bool ShouldTreatAsVariadic(PInvoke[] candidates) } } + private static string FixupSymbolName(string name) + { + UTF8Encoding utf8 = new(); + byte[] bytes = utf8.GetBytes(name); + StringBuilder sb = new(); + + foreach (byte b in bytes) + { + if ((b >= (byte)'0' && b <= (byte)'9') || + (b >= (byte)'a' && b <= (byte)'z') || + (b >= (byte)'A' && b <= (byte)'Z') || + (b == (byte)'_')) + { + sb.Append((char) b); + } + else if (s_charsToReplace.Contains((char) b)) + { + sb.Append('_'); + } + else + { + sb.Append($"_{b:X}_"); + } + } + + return sb.ToString(); + } + + private static string SymbolNameForMethod(MethodInfo method) + { + StringBuilder sb = new(); + Type? type = method.DeclaringType; + sb.Append($"{type!.Module!.Assembly!.GetName()!.Name!}_"); + sb.Append($"{(type!.IsNested ? type!.FullName : type!.Name)}_"); + sb.Append(method.Name); + + return FixupSymbolName(sb.ToString()); + } + private string MapType (Type t) { string name = t.Name; @@ -262,7 +316,7 @@ private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotN if (method.Name == "EnumCalendarInfo") { // FIXME: System.Reflection.MetadataLoadContext can't decode function pointer types // https://github.com/dotnet/runtime/issues/43791 - sb.Append($"int {pinvoke.EntryPoint} (int, int, int, int, int);"); + sb.Append($"int {FixupSymbolName(pinvoke.EntryPoint)} (int, int, int, int, int);"); return sb.ToString(); } @@ -274,7 +328,7 @@ private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotN } sb.Append(MapType(method.ReturnType)); - sb.Append($" {pinvoke.EntryPoint} ("); + sb.Append($" {FixupSymbolName(pinvoke.EntryPoint)} ("); int pindex = 0; var pars = method.GetParameters(); foreach (var p in pars) { diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index f592f488327cf6..47e56293a52e63 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -174,12 +174,13 @@ private bool ExecuteInternal () if (!FileCopyChecked(item.ItemSpec, dest, "NativeAssets")) return false; } - FileCopyChecked(MainJS!, Path.Combine(AppDir, "main.js"), string.Empty); + var mainFileName=Path.GetFileName(MainJS); + FileCopyChecked(MainJS!, Path.Combine(AppDir, mainFileName), string.Empty); string indexHtmlPath = Path.Combine(AppDir, "index.html"); if (!File.Exists(indexHtmlPath)) { - var html = @""; + var html = @""; File.WriteAllText(indexHtmlPath, html); } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildPublishTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildPublishTests.cs index 42eb96633f9b2a..1e25eb02a3f863 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildPublishTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildPublishTests.cs @@ -33,11 +33,14 @@ public void BuildThenPublishNoAOT(BuildArgs buildArgs, RunHost host, string id) // no relinking for build bool relinked = false; BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), - dotnetWasmFromRuntimePack: !relinked, id: id, - createProject: true, - publish: false); + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + DotnetWasmFromRuntimePack: !relinked, + CreateProject: true, + Publish: false + )); + Run(); @@ -53,10 +56,11 @@ public void BuildThenPublishNoAOT(BuildArgs buildArgs, RunHost host, string id) relinked = buildArgs.Config == "Release"; BuildProject(buildArgs, id: id, - dotnetWasmFromRuntimePack: !relinked, - createProject: false, - publish: true, - useCache: false); + new BuildProjectOptions( + DotnetWasmFromRuntimePack: !relinked, + CreateProject: false, + Publish: true, + UseCache: false)); Run(); @@ -79,12 +83,13 @@ public void BuildThenPublishWithAOT(BuildArgs buildArgs, RunHost host, string id // no relinking for build bool relinked = false; (_, string output) = BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), - dotnetWasmFromRuntimePack: !relinked, - id: id, - createProject: true, - publish: false, - label: "first_build"); + id, + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + DotnetWasmFromRuntimePack: !relinked, + CreateProject: true, + Publish: false, + Label: "first_build")); BuildPaths paths = GetBuildPaths(buildArgs); var pathsDict = GetFilesTable(buildArgs, paths, unchanged: false); @@ -109,11 +114,12 @@ public void BuildThenPublishWithAOT(BuildArgs buildArgs, RunHost host, string id // relink by default for Release+publish (_, output) = BuildProject(buildArgs, id: id, - dotnetWasmFromRuntimePack: false, - createProject: false, - publish: true, - useCache: false, - label: "first_publish"); + new BuildProjectOptions( + DotnetWasmFromRuntimePack: false, + CreateProject: false, + Publish: true, + UseCache: false, + Label: "first_publish")); var publishStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath)); Assert.True(publishStat["pinvoke.o"].Exists); @@ -125,12 +131,13 @@ public void BuildThenPublishWithAOT(BuildArgs buildArgs, RunHost host, string id // second build (_, output) = BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), - dotnetWasmFromRuntimePack: !relinked, - id: id, - createProject: true, - publish: false, - label: "second_build"); + id: id, + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + DotnetWasmFromRuntimePack: !relinked, + CreateProject: true, + Publish: false, + Label: "second_build")); var secondBuildStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath)); // no relinking, or AOT diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index cc4aa85a2c39d6..aebb02920310af 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs @@ -147,7 +147,7 @@ protected string RunAndTestWasmApp(BuildArgs buildArgs, string bundleDir = Path.Combine(GetBinDir(baseDir: buildDir, config: buildArgs.Config), "AppBundle"); (string testCommand, string extraXHarnessArgs) = host switch { - RunHost.V8 => ("wasm test", "--js-file=main.js --engine=V8 -v trace"), + RunHost.V8 => ("wasm test", "--js-file=test-main.js --engine=V8 -v trace"), _ => ("wasm test-browser", $"-v trace -b {host}") }; @@ -280,18 +280,10 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp public (string projectDir, string buildOutput) BuildProject(BuildArgs buildArgs, string id, - Action? initProject = null, - bool? dotnetWasmFromRuntimePack = null, - bool hasIcudt = true, - bool useCache = true, - bool expectSuccess = true, - bool createProject = true, - bool publish = true, - string? verbosity=null, - string? label=null) - { - string msgPrefix = label != null ? $"[{label}] " : string.Empty; - if (useCache && _buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product)) + BuildProjectOptions options) + { + string msgPrefix = options.Label != null ? $"[{options.Label}] " : string.Empty; + if (options.UseCache && _buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product)) { Console.WriteLine ($"Using existing build found at {product.ProjectDir}, with build log at {product.LogFile}"); @@ -303,33 +295,35 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp return (_projectDir, "FIXME"); } - if (createProject) + if (options.CreateProject) { InitPaths(id); InitProjectDir(_projectDir); - initProject?.Invoke(); + options.InitProject?.Invoke(); File.WriteAllText(Path.Combine(_projectDir, $"{buildArgs.ProjectName}.csproj"), buildArgs.ProjectFileContents); File.Copy(Path.Combine(AppContext.BaseDirectory, "test-main.js"), Path.Combine(_projectDir, "test-main.js")); } else if (_projectDir is null) { - throw new Exception("_projectDir should be set, to use createProject=false"); + throw new Exception("_projectDir should be set, to use options.createProject=false"); } StringBuilder sb = new(); - sb.Append(publish ? "publish" : "build"); + sb.Append(options.Publish ? "publish" : "build"); + if (options.Publish && options.BuildOnlyAfterPublish) + sb.Append(" -p:WasmBuildOnlyAfterPublish=true"); sb.Append($" {s_buildEnv.DefaultBuildArgs}"); sb.Append($" /p:Configuration={buildArgs.Config}"); - string logFileSuffix = label == null ? string.Empty : label.Replace(' ', '_'); + string logFileSuffix = options.Label == null ? string.Empty : options.Label.Replace(' ', '_'); string logFilePath = Path.Combine(_logPath, $"{buildArgs.ProjectName}{logFileSuffix}.binlog"); _testOutput.WriteLine($"-------- Building ---------"); _testOutput.WriteLine($"Binlog path: {logFilePath}"); Console.WriteLine($"Binlog path: {logFilePath}"); sb.Append($" /bl:\"{logFilePath}\" /nologo"); - sb.Append($" /fl /flp:\"v:diag,LogFile={logFilePath}.log\" /v:{verbosity ?? "minimal"}"); + sb.Append($" /fl /flp:\"v:diag,LogFile={logFilePath}.log\" /v:{options.Verbosity ?? "minimal"}"); if (buildArgs.ExtraBuildArgs != null) sb.Append($" {buildArgs.ExtraBuildArgs} "); @@ -338,26 +332,26 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp (int exitCode, string buildOutput) result; try { - result = AssertBuild(sb.ToString(), id, expectSuccess: expectSuccess, envVars: s_buildEnv.EnvVars); + result = AssertBuild(sb.ToString(), id, expectSuccess: options.ExpectSuccess, envVars: s_buildEnv.EnvVars); //AssertRuntimePackPath(result.buildOutput); // check that we are using the correct runtime pack! - if (expectSuccess) + if (options.ExpectSuccess) { string bundleDir = Path.Combine(GetBinDir(config: buildArgs.Config), "AppBundle"); - AssertBasicAppBundle(bundleDir, buildArgs.ProjectName, buildArgs.Config, hasIcudt, dotnetWasmFromRuntimePack ?? !buildArgs.AOT); + AssertBasicAppBundle(bundleDir, buildArgs.ProjectName, buildArgs.Config, options.HasIcudt, options.DotnetWasmFromRuntimePack ?? !buildArgs.AOT); } - if (useCache) + if (options.UseCache) _buildContext.CacheBuild(buildArgs, new BuildProduct(_projectDir, logFilePath, true)); return (_projectDir, result.buildOutput); } catch { - if (useCache) + if (options.UseCache) _buildContext.CacheBuild(buildArgs, new BuildProduct(_projectDir, logFilePath, false)); throw; } @@ -486,7 +480,7 @@ protected static void AssertBasicAppBundle(string bundleDir, string projectName, AssertFilesExist(bundleDir, new [] { "index.html", - "main.js", + "test-main.js", "dotnet.timezones.blat", "dotnet.wasm", "mono-config.json", @@ -860,4 +854,18 @@ public record BuildArgs(string ProjectName, public record BuildProduct(string ProjectDir, string LogFile, bool Result); internal record FileStat (bool Exists, DateTime LastWriteTimeUtc, long Length, string FullPath); internal record BuildPaths(string ObjWasmDir, string ObjDir, string BinDir, string BundleDir); - } + + public record BuildProjectOptions + ( + Action? InitProject = null, + bool? DotnetWasmFromRuntimePack = null, + bool HasIcudt = true, + bool UseCache = true, + bool ExpectSuccess = true, + bool CreateProject = true, + bool Publish = true, + bool BuildOnlyAfterPublish = true, + string? Verbosity = null, + string? Label = null + ); +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs index 7d66bc32a49a11..f34cb9cde7d655 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs @@ -74,10 +74,11 @@ private void TestInvariantGlobalization(BuildArgs buildArgs, bool? invariantGlob "; BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), - id: id, - dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, - hasIcudt: invariantGlobalization == null || invariantGlobalization.Value == false); + id: id, + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), + DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, + HasIcudt: invariantGlobalization == null || invariantGlobalization.Value == false)); if (invariantGlobalization == true) { diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/LocalEMSDKTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/LocalEMSDKTests.cs index 30ed330bf17910..5a85ae935bb916 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/LocalEMSDKTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/LocalEMSDKTests.cs @@ -30,9 +30,10 @@ public void AOT_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPath, str buildArgs = ExpandBuildArgs(buildArgs); (_, string buildOutput) = BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), id: id, - expectSuccess: false); + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + ExpectSuccess: false)); Assert.Matches(errorPattern, buildOutput); } @@ -52,9 +53,10 @@ public void Relinking_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPat buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); (_, string buildOutput) = BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), id: id, - expectSuccess: false); + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + ExpectSuccess: false)); Assert.Matches(errorPattern, buildOutput); } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs index 73598d8b079b13..2e453ee33845aa 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs @@ -86,9 +86,10 @@ void TestMainWithArgs(string projectNamePrefix, Console.WriteLine ($"-- args: {buildArgs}, name: {projectName}"); BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), - id: id, - dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack); + id: id, + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), + DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack)); RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42 + args.Length, args: string.Join(' ', args), test: output => diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs index 3453f9922dbbad..304161ef086d1f 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs @@ -32,9 +32,10 @@ public void SimpleNativeBuild(BuildArgs buildArgs, RunHost host, string id) buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), - dotnetWasmFromRuntimePack: false, - id: id); + id: id, + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + DotnetWasmFromRuntimePack: false)); RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, test: output => {}, @@ -57,10 +58,11 @@ public void MonoAOTCross_WorksWithNoTrimming(BuildArgs buildArgs, string id) (_, string output) = BuildProject( buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), - dotnetWasmFromRuntimePack: false, id: id, - expectSuccess: false); + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + DotnetWasmFromRuntimePack: false, + ExpectSuccess: false)); Assert.Contains("Stopping after AOT", output); } @@ -89,9 +91,10 @@ public void IntermediateBitcodeToObjectFilesAreNotLLVMIR(BuildArgs buildArgs, st buildArgs = ExpandBuildArgs(buildArgs, insertAtEnd: printFileTypeTarget); (_, string output) = BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), - dotnetWasmFromRuntimePack: false, - id: id); + id: id, + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + DotnetWasmFromRuntimePack: false)); if (!output.Contains("** wasm-dis exit code: 0")) throw new XunitException($"Expected to successfully run wasm-dis on System.Private.CoreLib.dll.o ." diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs index eb306286ba5e0d..e8892a9575d017 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs @@ -37,8 +37,8 @@ public void ProjectWithNativeReference(BuildArgs buildArgs, RunHost host, string } BuildProject(buildArgs, - dotnetWasmFromRuntimePack: false, - id: id); + id: id, + new BuildProjectOptions(DotnetWasmFromRuntimePack: false)); string output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 0, test: output => {}, @@ -81,9 +81,10 @@ public static int Main() }"; BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), - dotnetWasmFromRuntimePack: false, - id: id); + id: id, + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), + DotnetWasmFromRuntimePack: false)); string output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 0, test: output => {}, diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs index cd0d3ac4396349..da32a9f9e2ea1b 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs @@ -48,11 +48,12 @@ public NativeRebuildTestsBase(ITestOutputHelper output, SharedBuildPerTestClassF { buildArgs = GenerateProjectContents(buildArgs, nativeRelink, invariant, extraProperties); BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), - dotnetWasmFromRuntimePack: false, - hasIcudt: !invariant, - id: id, - createProject: true); + id: id, + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), + DotnetWasmFromRuntimePack: false, + HasIcudt: !invariant, + CreateProject: true)); RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: RunHost.V8, id: id); return (buildArgs, GetBuildPaths(buildArgs)); @@ -80,11 +81,12 @@ protected string Rebuild(bool nativeRelink, bool invariant, BuildArgs buildArgs, Console.WriteLine($"{Environment.NewLine}Rebuilding with no changes ..{Environment.NewLine}"); (_, string output) = BuildProject(buildArgs, id: id, - dotnetWasmFromRuntimePack: false, - hasIcudt: !invariant, - createProject: false, - useCache: false, - verbosity: verbosity); + new BuildProjectOptions( + DotnetWasmFromRuntimePack: false, + HasIcudt: !invariant, + CreateProject: false, + UseCache: false, + Verbosity: verbosity)); return output; } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs index 27783ae85c95ad..feada6bed9b99c 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs @@ -123,15 +123,16 @@ public static int Main() extraProperties: "true<_WasmDevel>true"); (_, string output) = BuildProject(buildArgs, - initProject: () => - { - File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText); - File.Copy(Path.Combine(BuildEnvironment.TestAssetsPath, "native-libs", filename), - Path.Combine(_projectDir!, filename)); - }, - publish: buildArgs.AOT, id: id, - dotnetWasmFromRuntimePack: false); + new BuildProjectOptions( + InitProject: () => + { + File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText); + File.Copy(Path.Combine(BuildEnvironment.TestAssetsPath, "native-libs", filename), + Path.Combine(_projectDir!, filename)); + }, + Publish: buildArgs.AOT, + DotnetWasmFromRuntimePack: false)); return (buildArgs, output); } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs index 961c141348275d..ba65b2445aabf9 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs @@ -35,10 +35,11 @@ public void NoOpRebuild(BuildArgs buildArgs, RunHost host, string id) buildArgs = ExpandBuildArgs(buildArgs); BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), - dotnetWasmFromRuntimePack: true, - id: id, - createProject: true); + id: id, + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + DotnetWasmFromRuntimePack: true, + CreateProject: true)); Run(); @@ -52,9 +53,10 @@ public void NoOpRebuild(BuildArgs buildArgs, RunHost host, string id) // no-op Rebuild BuildProject(buildArgs, id: id, - dotnetWasmFromRuntimePack: true, - createProject: false, - useCache: false); + new BuildProjectOptions( + DotnetWasmFromRuntimePack: true, + CreateProject: false, + UseCache: false)); Run(); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs index b65c274ae62ed4..7b6c4c71c042c3 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs @@ -50,13 +50,14 @@ public void ResourcesFromMainAssembly(BuildArgs buildArgs, extraProperties: nativeRelink ? $"true" : string.Empty); BuildProject(buildArgs, - initProject: () => - { - Utils.DirectoryCopy(Path.Combine(BuildEnvironment.TestAssetsPath, "resx"), Path.Combine(_projectDir!, "resx")); - CreateProgramForCultureTest(_projectDir!, $"{projectName}.resx.words", "TestClass"); - }, - dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, - id: id); + id: id, + new BuildProjectOptions( + InitProject: () => + { + Utils.DirectoryCopy(Path.Combine(BuildEnvironment.TestAssetsPath, "resx"), Path.Combine(_projectDir!, "resx")); + CreateProgramForCultureTest(_projectDir!, $"{projectName}.resx.words", "TestClass"); + }, + DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack)); string output = RunAndTestWasmApp( buildArgs, expectedExitCode: 42, @@ -88,17 +89,18 @@ public void ResourcesFromProjectReference(BuildArgs buildArgs, extraItems: $""); BuildProject(buildArgs, - dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, - id: id, - initProject: () => - { - string rootDir = _projectDir!; - _projectDir = Path.Combine(rootDir, projectName); - - Directory.CreateDirectory(_projectDir); - Utils.DirectoryCopy(Path.Combine(BuildEnvironment.TestAssetsPath, "SatelliteAssemblyFromProjectRef"), rootDir); - CreateProgramForCultureTest(_projectDir, "LibraryWithResources.resx.words", "LibraryWithResources.Class1"); - }); + id: id, + new BuildProjectOptions( + DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, + InitProject: () => + { + string rootDir = _projectDir!; + _projectDir = Path.Combine(rootDir, projectName); + + Directory.CreateDirectory(_projectDir); + Utils.DirectoryCopy(Path.Combine(BuildEnvironment.TestAssetsPath, "SatelliteAssemblyFromProjectRef"), rootDir); + CreateProgramForCultureTest(_projectDir, "LibraryWithResources.resx.words", "LibraryWithResources.Class1"); + })); string output = RunAndTestWasmApp(buildArgs, expectedExitCode: 42, @@ -123,9 +125,10 @@ public void CheckThatSatelliteAssembliesAreNotAOTed(BuildArgs buildArgs, string extraItems: $""); BuildProject(buildArgs, - initProject: () => CreateProgramForCultureTest(_projectDir!, $"{projectName}.words", "TestClass"), - dotnetWasmFromRuntimePack: false, - id: id); + id: id, + new BuildProjectOptions( + InitProject: () => CreateProgramForCultureTest(_projectDir!, $"{projectName}.words", "TestClass"), + DotnetWasmFromRuntimePack: false)); var bitCodeFileNames = Directory.GetFileSystemEntries(Path.Combine(_projectDir!, "obj"), "*.dll.bc", SearchOption.AllDirectories) .Select(path => Path.GetFileName(path)) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs index 68bc578754c04b..d6ae5ed870cfcd 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs @@ -112,13 +112,14 @@ public void PropertiesFromRuntimeConfigJson(BuildArgs buildArgs, RunHost host, s }"; BuildProject(buildArgs, - initProject: () => - { - File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText); - File.WriteAllText(Path.Combine(_projectDir!, "runtimeconfig.template.json"), runtimeConfigTemplateJson); - }, - id: id, - dotnetWasmFromRuntimePack: !(buildArgs.AOT || buildArgs.Config == "Release")); + id: id, + new BuildProjectOptions( + InitProject: () => + { + File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText); + File.WriteAllText(Path.Combine(_projectDir!, "runtimeconfig.template.json"), runtimeConfigTemplateJson); + }, + DotnetWasmFromRuntimePack: !(buildArgs.AOT || buildArgs.Config == "Release"))); RunAndTestWasmApp(buildArgs, expectedExitCode: 42, test: output => Assert.Contains("test_runtimeconfig_json: 25", output), host: host, id: id); @@ -141,12 +142,13 @@ public void PropertiesFromCsproj(BuildArgs buildArgs, RunHost host, string id) "; BuildProject(buildArgs, - initProject: () => - { - File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText); - }, - id: id, - dotnetWasmFromRuntimePack: !(buildArgs.AOT || buildArgs.Config == "Release")); + id: id, + new BuildProjectOptions( + InitProject: () => + { + File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText); + }, + DotnetWasmFromRuntimePack: !(buildArgs.AOT || buildArgs.Config == "Release"))); RunAndTestWasmApp(buildArgs, expectedExitCode: 42, test: output => Assert.Contains("System.Threading.ThreadPool.MaxThreads: 20", output), host: host, id: id); @@ -167,9 +169,10 @@ void TestMain(string projectName, dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), - id: id, - dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack); + id: id, + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), + DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack)); RunAndTestWasmApp(buildArgs, expectedExitCode: 42, test: output => Assert.Contains("Hello, World!", output), host: host, id: id); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmNativeDefaultsTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmNativeDefaultsTests.cs index 64d008549e8ec1..00674bf4630dcc 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmNativeDefaultsTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmNativeDefaultsTests.cs @@ -89,11 +89,13 @@ private string CheckWasmNativeDefaultValue(string projectName, insertAtEnd: printValueTarget); (_, string output) = BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), - dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, - id: Path.GetRandomFileName(), - expectSuccess: false, - useCache: false); + id: Path.GetRandomFileName(), + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, + ExpectSuccess: false, + UseCache: false, + BuildOnlyAfterPublish: false)); return output; } diff --git a/src/tests/Common/CLRTest.CrossGen.targets b/src/tests/Common/CLRTest.CrossGen.targets index 4d3e8ea07d3ac3..84ec7a004c6f1e 100644 --- a/src/tests/Common/CLRTest.CrossGen.targets +++ b/src/tests/Common/CLRTest.CrossGen.targets @@ -193,8 +193,8 @@ if defined RunCrossGen2 ( call :CompileOneFileCrossgen2 IF NOT !CrossGen2Status!==0 goto :DoneCrossgen2Operations ) else ( + set ExtraCrossGen2Args=!ExtraCrossGen2Args! -r:!scriptPath!IL-CG2\*.dll for %%I in (!scriptPath!\*.dll) do ( - set ExtraCrossGen2Args=!ExtraCrossGen2Args! -r:!scriptPath!IL-CG2\*.dll set __OutputFile=!scriptPath!\%%~nI.dll set __InputFile=!scriptPath!IL-CG2\%%~nI.dll call :CompileOneFileCrossgen2 diff --git a/src/tests/Common/Directory.Build.targets b/src/tests/Common/Directory.Build.targets index e003e2dc6ec860..cdf13215416491 100644 --- a/src/tests/Common/Directory.Build.targets +++ b/src/tests/Common/Directory.Build.targets @@ -16,6 +16,8 @@ true false true + + true @@ -103,6 +105,8 @@ Include="$(CoreCLRArtifactsPath)%(RunTimeArtifactsIncludeFolders.Identity)**/*" Exclude="@(RunTimeArtifactsExcludeFiles -> '$(CoreCLRArtifactsPath)%(Identity)')" TargetDir="%(RunTimeArtifactsIncludeFolders.Identity)" /> + + @@ -167,6 +171,12 @@ + + + + + + testReporterWrapper.WrapTestExecutionWithReporting(ExecutionStatement, this); + + public override bool Equals(object obj) + { + return obj is OutOfProcessTest other + && DisplayNameForFiltering == other.DisplayNameForFiltering + && ExecutionStatement == other.ExecutionStatement; + } +} + +sealed class TestWithCustomDisplayName : ITestInfo +{ + private ITestInfo _inner; + + public TestWithCustomDisplayName(ITestInfo inner, string displayName) + { + _inner = inner; + DisplayNameForFiltering = displayName; + } + + public string TestNameExpression => $@"""{DisplayNameForFiltering.Replace(@"\", @"\\")}"""; + + public string DisplayNameForFiltering { get; } + + public string Method => _inner.Method; + + public string ContainingType => _inner.ContainingType; + + public string GenerateTestExecution(ITestReporterWrapper testReporterWrapper) + { + ITestReporterWrapper dummyInnerWrapper = new NoTestReporting(); + string innerExecution = _inner.GenerateTestExecution(dummyInnerWrapper); + return testReporterWrapper.WrapTestExecutionWithReporting(innerExecution, this); + } + + public override bool Equals(object obj) + { + return obj is TestWithCustomDisplayName other + && _inner.Equals(other._inner) + && DisplayNameForFiltering == other.DisplayNameForFiltering; + } } sealed class NoTestReporting : ITestReporterWrapper @@ -289,7 +329,7 @@ public string WrapTestExecutionWithReporting(string testExecutionExpression, ITe builder.AppendLine($"if ({_filterLocalIdentifier} is null || {_filterLocalIdentifier}.ShouldRunTest(@\"{test.ContainingType}.{test.Method}\", {test.TestNameExpression}))"); builder.AppendLine("{"); - builder.AppendLine($"TimeSpan testStart = stopwatch.Elapsed;"); + builder.AppendLine($"System.TimeSpan testStart = stopwatch.Elapsed;"); builder.AppendLine("try {"); builder.AppendLine(testExecutionExpression); builder.AppendLine($"{_summaryLocalIdentifier}.ReportPassedTest({test.TestNameExpression}, \"{test.ContainingType}\", @\"{test.Method}\", stopwatch.Elapsed - testStart);"); @@ -304,6 +344,6 @@ public string WrapTestExecutionWithReporting(string testExecutionExpression, ITe public string GenerateSkippedTestReporting(ITestInfo skippedTest) { - return $"{_summaryLocalIdentifier}.ReportSkippedTest({skippedTest.TestNameExpression}, \"{skippedTest.ContainingType}\", \"{skippedTest.Method}\", TimeSpan.Zero, string.Empty);"; + return $"{_summaryLocalIdentifier}.ReportSkippedTest({skippedTest.TestNameExpression}, \"{skippedTest.ContainingType}\", \"{skippedTest.Method}\", System.TimeSpan.Zero, string.Empty);"; } } diff --git a/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs b/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs index 39a766bd03f4b4..9d27d24c46080d 100644 --- a/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs +++ b/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs @@ -13,6 +13,7 @@ public static class OptionsHelper private const string TestFilterOption = "build_metadata.AdditionalFiles.TestFilter"; private const string TestAssemblyRelativePathOption = "build_metadata.AdditionalFiles.TestAssemblyRelativePath"; private const string TestDisplayNameOption = "build_metadata.AdditionalFiles.TestDisplayName"; + private const string SingleTestDisplayNameOption = "build_metadata.AdditionalFiles.SingleTestDisplayName"; private static bool GetBoolOption(this AnalyzerConfigOptions options, string key) { @@ -38,7 +39,9 @@ private static bool GetBoolOption(this AnalyzerConfigOptions options, string key internal static string? TestFilter(this AnalyzerConfigOptions options) => options.TryGetValue(TestFilterOption, out string? filter) ? filter : null; - internal static string? TestAssemblyRelativePath(this AnalyzerConfigOptions options) => options.TryGetValue(TestAssemblyRelativePathOption, out string? flavor) ? flavor : null; + internal static string? TestAssemblyRelativePath(this AnalyzerConfigOptions options) => options.TryGetValue(TestAssemblyRelativePathOption, out string? relativePath) ? relativePath : null; - internal static string? TestDisplayName(this AnalyzerConfigOptions options) => options.TryGetValue(TestDisplayNameOption, out string? flavor) ? flavor : null; + internal static string? TestDisplayName(this AnalyzerConfigOptions options) => options.TryGetValue(TestDisplayNameOption, out string? displayName) ? displayName : null; + + internal static string? SingleTestDisplayName(this AnalyzerConfigOptions options) => options.TryGetValue(SingleTestDisplayNameOption, out string? displayName) ? displayName : null; } diff --git a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs index 88fb05d7801abc..5804b2017ad5f6 100644 --- a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs +++ b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.IO; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; @@ -26,12 +27,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) && method.AttributeLists.Count > 0, static (context, ct) => (IMethodSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!); - var methodsInReferencedAssemblies = context.MetadataReferencesProvider.Combine(context.CompilationProvider).SelectMany((data, ct) => - { - ExternallyReferencedTestMethodsVisitor visitor = new(); - return visitor.Visit(data.Right.GetAssemblyOrModuleSymbol(data.Left))!; - }); - var outOfProcessTests = context.AdditionalTextsProvider.Combine(context.AnalyzerConfigOptionsProvider).SelectMany((data, ct) => { var (file, options) = data; @@ -51,8 +46,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return ImmutableArray.Empty; }); - var allMethods = methodsInSource.Collect().Combine(methodsInReferencedAssemblies.Collect()).SelectMany((methods, ct) => methods.Left.AddRange(methods.Right)); - var aliasMap = context.CompilationProvider.Select((comp, ct) => { var aliasMap = ImmutableDictionary.CreateBuilder(); @@ -69,21 +62,55 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var alwaysWriteEntryPoint = context.CompilationProvider.Select((comp, ct) => comp.Options.OutputKind == OutputKind.ConsoleApplication && comp.GetEntryPoint(ct) is null); - context.RegisterImplementationSourceOutput( - allMethods + var testsInSource = + methodsInSource .Combine(context.AnalyzerConfigOptionsProvider) .Combine(aliasMap) - .SelectMany((data, ct) => ImmutableArray.CreateRange(GetTestMethodInfosForMethod(data.Left.Left, data.Left.Right, data.Right))) + .SelectMany((data, ct) => ImmutableArray.CreateRange(GetTestMethodInfosForMethod(data.Left.Left, data.Left.Right, data.Right))); + + var pathsForReferences = context + .AdditionalTextsProvider + .Combine(context.AnalyzerConfigOptionsProvider) + .Select((data, ct) => new KeyValuePair(data.Left.Path, data.Right.GetOptions(data.Left).SingleTestDisplayName())) + .Where(data => data.Value is not null) .Collect() - .Combine(outOfProcessTests.Collect()) - .Select((tests, ct) => tests.Left.AddRange(tests.Right)) + .Select((paths, ct) => ImmutableDictionary.CreateRange(paths)) + .WithComparer(new ImmutableDictionaryValueComparer(EqualityComparer.Default)); + + var testsInReferencedAssemblies = context + .MetadataReferencesProvider + .Combine(context.CompilationProvider) + .Combine(context.AnalyzerConfigOptionsProvider) + .Combine(pathsForReferences) + .Combine(aliasMap) + .SelectMany((data, ct) => + { + var ((((reference, compilation), configOptions), paths), aliasMap) = data; + ExternallyReferencedTestMethodsVisitor visitor = new(); + IEnumerable testMethods = visitor.Visit(compilation.GetAssemblyOrModuleSymbol(reference))!; + ImmutableArray tests = ImmutableArray.CreateRange(testMethods.SelectMany(method => GetTestMethodInfosForMethod(method, configOptions, aliasMap))); + if (tests.Length == 1 && reference is PortableExecutableReference { FilePath: string pathOnDisk } && paths.TryGetValue(pathOnDisk, out string? referencePath)) + { + // If we only have one test in the module and we have a display name for the module the test comes from, then rename it to the module name to make on disk discovery easier. + return ImmutableArray.Create((ITestInfo)new TestWithCustomDisplayName(tests[0], referencePath!)); + } + return tests; + }); + + var allTests = testsInSource.Collect().Combine(testsInReferencedAssemblies.Collect()).Combine(outOfProcessTests.Collect()).SelectMany((tests, ct) => tests.Left.Left.AddRange(tests.Left.Right).AddRange(tests.Right)); + + context.RegisterImplementationSourceOutput( + allTests .Combine(context.AnalyzerConfigOptionsProvider) - .Select((data, ct) => + .Where(data => { - var (tests, options) = data; + var (test, options) = data; var filter = new XUnitWrapperLibrary.TestFilter(options.GlobalOptions.TestFilter() ?? ""); - return (ImmutableArray.CreateRange(tests.Where(test => filter.ShouldRunTest($"{test.ContainingType}.{test.Method}", test.DisplayNameForFiltering, Array.Empty()))), options); + return filter.ShouldRunTest($"{test.ContainingType}.{test.Method}", test.DisplayNameForFiltering, Array.Empty()); }) + .Select((data, ct) => data.Left) + .Collect() + .Combine(context.AnalyzerConfigOptionsProvider) .Combine(aliasMap) .Combine(assemblyName) .Combine(alwaysWriteEntryPoint), @@ -129,7 +156,8 @@ private static string GenerateFullTestRunner(ImmutableArray testInfos builder.AppendLine(test.GenerateTestExecution(reporter)); } - builder.AppendLine($@"System.IO.File.WriteAllText(""{assemblyName}.testResults.xml"", summary.GetTestResultOutput());"); + builder.AppendLine($@"System.IO.File.WriteAllText(""{assemblyName}.testResults.xml"", summary.GetTestResultOutput(""{assemblyName}""));"); + builder.AppendLine("return 100;"); return builder.ToString(); } diff --git a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props index b05866e9812294..651a40c62aba46 100644 --- a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props +++ b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props @@ -11,13 +11,17 @@ + - + + <_ResolvedProjectReferenceFiles Include="@(ReferencePath->ClearMetadata())" Condition="'%(ReferencePath.ReferenceSourceTarget)' == 'ProjectReference'" /> + <_ResolvedProjectReferenceFiles SingleTestDisplayName="$([MSBuild]::MakeRelative('$(TestBinDir)', '%(Identity)'))" /> + diff --git a/src/tests/Common/XUnitWrapperLibrary/TestSummary.cs b/src/tests/Common/XUnitWrapperLibrary/TestSummary.cs index f7e47ebdeb619b..32170cf2158fd7 100644 --- a/src/tests/Common/XUnitWrapperLibrary/TestSummary.cs +++ b/src/tests/Common/XUnitWrapperLibrary/TestSummary.cs @@ -39,7 +39,7 @@ public void ReportSkippedTest(string name, string containingTypeName, string met _testResults.Add(new TestResult(name, containingTypeName, methodName, duration, null, reason)); } - public string GetTestResultOutput() + public string GetTestResultOutput(string assemblyName) { double totalRunSeconds = (DateTime.Now - _testRunStart).TotalSeconds; // using StringBuilder here for simplicity of loaded IL. @@ -47,7 +47,7 @@ public string GetTestResultOutput() resultsFile.AppendLine(""); resultsFile.AppendLine($@" "); diff --git a/src/tests/Common/helixpublishwitharcade.proj b/src/tests/Common/helixpublishwitharcade.proj index 2b711ba5b1a394..5b8dfcceb8709c 100644 --- a/src/tests/Common/helixpublishwitharcade.proj +++ b/src/tests/Common/helixpublishwitharcade.proj @@ -7,6 +7,14 @@ + + $(TestBinDir)Tests\Core_Root\ + $(TestBinDir)LegacyPayloads\ + $([MSBuild]::NormalizeDirectory($(LegacyPayloadsRootDirectory))) + $(TestBinDir)MergedPayloads\ + $([MSBuild]::NormalizeDirectory($(MergedPayloadsRootDirectory))) + + @@ -40,7 +48,6 @@ PALTestsDir=$(_PALTestsDir) - <_PropertiesToPass Condition="'$(TargetOS)' == 'Browser' Or '$(TargetOS)' == 'Android'"> $(_PropertiesToPass); IncludeDotNetCli=$(IncludeDotNetCli); @@ -54,8 +61,15 @@ + + + + + + + - + @@ -88,9 +102,6 @@ - $(TestBinDir)Tests\Core_Root\ - $(TestBinDir)Payloads\ - $([MSBuild]::NormalizeDirectory($(PayloadsRootDirectory))) SetStressModes_$(Scenario).cmd SetStressModes_$(Scenario).sh @@ -124,7 +135,7 @@ - + <_XUnitWrapperDll Include="%(TestGrouping.XUnitWrapperDll)" Condition="Exists('%(XUnitWrapperDll)')"> %(TestGroup) @@ -132,9 +143,9 @@ <_XUnitWrapperDll Include="@(XUnitWrapperGrouping)" /> - <_XUnitWrapperDll Include="$(TestBinDir)**\*.XUnitWrapper.dll" Exclude="$(PayloadsRootDirectory)**\*.XUnitWrapper.dll;@(XUnitWrapperGrouping->Metadata('FullPath'))"> + <_XUnitWrapperDll Include="$(TestBinDir)**\*.XUnitWrapper.dll" Exclude="$(LegacyPayloadsRootDirectory)**\*.XUnitWrapper.dll;@(XUnitWrapperGrouping->Metadata('FullPath'))"> @@ -146,7 +157,9 @@ - + <_FileDirectory>%(_XUnitWrapperDll.RootDir)%(Directory) <_PayloadGroup>%(_XUnitWrapperDll.PayloadGroup) @@ -161,32 +174,33 @@ <_TestGroupingExists>@(_TestGroupingRelevant->AnyHaveMetadataValue('TestGroup','$(_PayloadGroup)')) - + - <_PayloadFiles Include="$(_FileDirectory)**" Exclude="@(_TestGroupingRelevant)" Condition="'$(_TestGroupingExists)' != 'true'" /> + <_LegacyPayloadFiles Include="$(_FileDirectory)**" Exclude="@(_TestGroupingRelevant)" Condition="'$(_TestGroupingExists)' != 'true'" /> - <_PayloadFiles Include="@(_TestGroupingRelevant->WithMetadataValue('TestGroup','$(_PayloadGroup)')->DistinctWithCase())" Condition="'$(_TestGroupingExists)' == 'true'" /> - <_PayloadFiles Include="$(_FileDirectory)*" Condition="'$(_TestGroupingExists)' == 'true'" /> - <_PayloadFiles Include="$(_FileDirectory)/*.app" Condition="'$(_TestGroupingExists)' == 'true'" /> - <_PayloadFiles Include="$(_FileDirectory)/*.app/**" Condition="'$(_TestGroupingExists)' == 'true'" /> + <_LegacyPayloadFiles Include="@(_TestGroupingRelevant->WithMetadataValue('TestGroup','$(_PayloadGroup)')->DistinctWithCase())" Condition="'$(_TestGroupingExists)' == 'true'" /> + <_LegacyPayloadFiles Include="$(_FileDirectory)*" Condition="'$(_TestGroupingExists)' == 'true'" /> + <_LegacyPayloadFiles Include="$(_FileDirectory)/*.app" Condition="'$(_TestGroupingExists)' == 'true'" /> + <_LegacyPayloadFiles Include="$(_FileDirectory)/*.app/**" Condition="'$(_TestGroupingExists)' == 'true'" /> - <_PayloadFiles Update="@(_PayloadFiles)"> + <_LegacyPayloadFiles Update="@(_LegacyPayloadFiles)"> $(_PayloadGroup)\$([System.IO.Path]::GetRelativePath($(TestBinDir), %(FullPath))) - + - + - - + + + - + @@ -203,13 +217,16 @@ - + <_PayloadGroups Include="$(PayloadGroups)" /> - <_ProjectsToBuild Include="testenvironment.proj"> - Scenario=$(Scenario);TestEnvFileName=$(PayloadsRootDirectory)%(_PayloadGroups.Identity)\$(TestEnvFileName);TargetsWindows=$(TestWrapperTargetsWindows);RuntimeVariant=$(_RuntimeVariant) + <_ProjectsToBuild Include="testenvironment.proj" Condition="!$(IsMergedTestWrapper)"> + Scenario=$(Scenario);TestEnvFileName=$(LegacyPayloadsRootDirectory)%(_PayloadGroups.Identity)\$(TestEnvFileName);TargetsWindows=$(TestWrapperTargetsWindows);RuntimeVariant=$(_RuntimeVariant) + + <_ProjectsToBuild Include="testenvironment.proj" Condition="$(IsMergedTestWrapper)"> + Scenario=$(Scenario);TestEnvFileName=$(MergedPayloadsRootDirectory)%(_PayloadGroups.Identity)\$(TestEnvFileName);TargetsWindows=$(TestWrapperTargetsWindows);RuntimeVariant=$(_RuntimeVariant) @@ -219,39 +236,97 @@ + Command="tar -czvf $(LegacyPayloadsRootDirectory)paltests.tar.gz `ls -A`"/> - - - <_PayloadGroups>@(_XUnitWrapperDll->Metadata('PayloadGroup')->DistinctWithCase()) - + + + <_LegacyPayloadGroups Include="@(_XUnitWrapperDll->Metadata('PayloadGroup')->DistinctWithCase())" /> <_Scenario Include="$(_Scenarios.Split(','))" /> <_ProjectsToBuild Include="$(MSBuildProjectFile)"> - Scenario=%(_Scenario.Identity);PayloadGroups=$(_PayloadGroups) + Scenario=%(_Scenario.Identity);PayloadGroups=@(_LegacyPayloadGroups);IsMergedTestWrapper=false + + + <_Scenario Include="$(_Scenarios.Split(','))" /> + <_ProjectsToBuild Include="$(MSBuildProjectFile)"> + Scenario=%(_Scenario.Identity);PayloadGroups=@(_MergedPayloadGroups);IsMergedTestWrapper=true + + + + + + + + + <_MergedWrapperMarker Include="$(TestBinDir)**\*.MergedTestAssembly" /> + <_MergedWrapperRunScript Include="$([System.IO.Path]::ChangeExtension('%(_MergedWrapperMarker.Identity)', '.$(TestScriptExtension)'))" /> + + + + + + <_MergedWrapperDirectory>%(_MergedWrapperRunScript.RootDir)%(Directory) + <_MergedWrapperName>%(_MergedWrapperRunScript.FileName) + <_MergedWrapperRunScriptRelative Condition="'%(_MergedWrapperRunScript.Identity)' != ''">$([System.IO.Path]::GetRelativePath($(TestBinDir), %(_MergedWrapperRunScript.FullPath))) + + + + <_MergedPayloadGroups Include="$(_MergedWrapperName)" /> + <_MergedPayloadFiles Include="$(_MergedWrapperDirectory)**" /> + <_MergedPayloadFiles Update="@(_MergedPayloadFiles)"> + + $([System.IO.Path]::GetRelativePath($(TestBinDir), %(FullPath))) + + + + + + + + + + + + false true - + - - - $([MSBuild]::MakeRelative($(PayloadsRootDirectory), %(FullPath))) + + + $([MSBuild]::MakeRelative($(LegacyPayloadsRootDirectory), %(FullPath))) %(FullPath) - + - + + + + + + + + $([MSBuild]::MakeRelative($(MergedPayloadsRootDirectory), %(FullPath))) + %(FullPath) + + + + @@ -342,34 +417,61 @@ - - - $([MSBuild]::MakeRelative($(PayloadsRootDirectory), %(FullPath))) + + + $([MSBuild]::MakeRelative($(LegacyPayloadsRootDirectory), %(FullPath))) %(FullPath) $([System.String]::Join(' ', $([System.IO.Directory]::GetFiles(%(FullPath), '*.XUnitWrapper.dll', SearchOption.AllDirectories))).Replace($([MSBuild]::EnsureTrailingSlash(%(FullPath))),'')) - - - + + %(PayloadGroup) - $(PayloadsRootDirectory)\%(PayloadGroup).zip - + $(LegacyPayloadsRootDirectory)\%(PayloadGroup).zip + + + - + + %(FullPath) + $(MergedPayloadsRootDirectory)%(FileName)\HelixCommand.txt + + + + $([System.IO.File]::ReadAllText('%(PayloadHelixCommandFile)')) + $([System.IO.Path]::GetDirectoryName('%(MergedTestHelixCommand)').Replace('/', '-').Replace('\', '-')) + $(MergedPayloadsRootDirectory)%(PayloadGroup).zip + + + + chmod +x %(MergedPayloads.MergedTestHelixCommand)%0A%(MergedPayloads.MergedTestHelixCommand) + + + %(PayloadDirectory) dotnet $(XUnitRunnerDll) %(XUnitWrapperDlls) $(XUnitRunnerArgs) dotnet $(XUnitRunnerDll) %(XUnitWrapperDlls) $(XUnitRunnerArgs) -trait TestGroup=%(TestGroup) $([System.TimeSpan]::FromMinutes($(TimeoutPerTestCollectionInMinutes))) - + + %(PayloadDirectory) + %(MergedTestHelixCommand) + $([System.TimeSpan]::FromMinutes($(TimeoutPerTestCollectionInMinutes))) + + + $([System.TimeSpan]::FromMinutes($(TimeoutPerTestCollectionInMinutes))) dotnet $(XUnitRunnerDll) %(XUnitWrapperDlls) $(XUnitRunnerArgs) dotnet $(XUnitRunnerDll) %(XUnitWrapperDlls) $(XUnitRunnerArgs) -trait TestGroup=%(TestGroup) - + + $([System.TimeSpan]::FromMinutes($(TimeoutPerTestCollectionInMinutes))) + %(MergedTestHelixCommand) + + + ios-simulator-64 ios-simulator-64 $([System.TimeSpan]::FromMinutes($(TimeoutPerTestCollectionInMinutes))) @@ -377,8 +479,15 @@ dotnet $(XUnitRunnerDll) %(XUnitWrapperDlls) $(XUnitRunnerArgs) -trait TestGroup=%(TestGroup) + + ios-simulator-64 + ios-simulator-64 + $([System.TimeSpan]::FromMinutes($(TimeoutPerTestCollectionInMinutes))) + %(MergedTestHelixCommand) + + - $(PayloadsRootDirectory)paltests.tar.gz + $(LegacyPayloadsRootDirectory)paltests.tar.gz ./runpaltestshelix.sh $([System.TimeSpan]::FromMinutes($(TimeoutPerTestCollectionInMinutes))) diff --git a/src/tests/Common/tests.targets b/src/tests/Common/tests.targets index fa4e20e7db3e65..fbab315d4faf02 100644 --- a/src/tests/Common/tests.targets +++ b/src/tests/Common/tests.targets @@ -10,24 +10,38 @@ <__TestRunHtmlLog Condition="'$(__TestRunHtmlLog)' == ''">$(__LogsDir)\TestRun.html <__TestRunXmlLog Condition="'$(__TestRunXmlLog)' == ''">$(__LogsDir)\TestRun.xml - + + - - + + - - - + - + + + <_MergedTestAssemblyMarkers Include="$(BaseOutputPathWithConfig)\**\*.MergedTestAssembly" /> + + + $([System.IO.Path]::ChangeExtension('%(MergedTestWrapperScripts.Identity)', '.log')) + $([System.IO.Path]::ChangeExtension('%(MergedTestWrapperScripts.Identity)', '.testResults.xml')) + $([System.IO.Path]::GetRelativePath('$(TestBinDir)', $([System.IO.Path]::GetDirectoryName('%(MergedTestWrapperScripts.Identity)')))) + + + $(__LogsDir)\$([MSBuild]::ValueOrDefault('%(MergedTestWrapperScripts.RelativeResultsPath)', '').Replace('/', '.').Replace('\', '.')).testRun.xml + + + + + + category=outerloop;category=failing @@ -66,14 +80,35 @@ + + + + + + + + + + + <_ProjectsToBuild Include="$(MSBuildThisFileFullPath)"> + TestWrapperScript=%(MergedTestWrapperScripts.Identity);RedirectOutputToFile=%(MergedTestWrapperScripts.RedirectOutputToFile);TestResultsXmlFile=%(MergedTestWrapperScripts.TestResultsXmlFile);TestResultsCopyTo=%(MergedTestWrapperScripts.TestResultsCopyTo) + + + + + + $(CorerunExecutable) $(XunitConsoleRunner) @(TestAssemblies->'%(Identity)', ' ') $(XunitArgs) - + WorkingDirectory="$(BaseOutputPathWithConfig)" + Condition="'@(TestAssemblies)' != ''" /> + + + diff --git a/src/tests/Directory.Build.props b/src/tests/Directory.Build.props index 12dbe872646f6c..2fe5ea5f75d6f8 100644 --- a/src/tests/Directory.Build.props +++ b/src/tests/Directory.Build.props @@ -141,7 +141,7 @@ - true + true C# F# IL @@ -167,6 +167,8 @@ false true + sh + cmd @@ -182,6 +184,6 @@ true - Exe + Exe diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index a3aca72d338e58..fb65f6c096860a 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -11,7 +11,10 @@ - BuildAndRun + true + Exe + BuildAndRun + BuildOnly 0 @@ -245,6 +248,11 @@ + + @@ -417,7 +425,7 @@ - + true @@ -425,6 +433,10 @@ + + + + diff --git a/src/tests/Interop/PInvoke/Generics/GenericsNative.Vector256B.cpp b/src/tests/Interop/PInvoke/Generics/GenericsNative.Vector256B.cpp index 405aa08949b29a..985c4116bfe28f 100644 --- a/src/tests/Interop/PInvoke/Generics/GenericsNative.Vector256B.cpp +++ b/src/tests/Interop/PInvoke/Generics/GenericsNative.Vector256B.cpp @@ -55,7 +55,7 @@ static Vector256B Vector256BValue = { }; -extern "C" DLL_EXPORT Vector256B STDMETHODCALLTYPE GetVector256B(bool e00, bool e01, bool e02, bool e03, bool e04, bool e05, bool e06, bool e07, bool e08, bool e09, bool e10, bool e11, bool e12, bool e13, bool e14, bool e15, bool e16, bool e17, bool e18, bool e19, bool e20, bool e21, bool e22, bool e23, bool e24, bool e25, bool e26, bool e27, bool e28, bool e29, bool e30, bool e31) +extern "C" DLL_EXPORT Vector256B STDMETHODCALLTYPE ENABLE_AVX GetVector256B(bool e00, bool e01, bool e02, bool e03, bool e04, bool e05, bool e06, bool e07, bool e08, bool e09, bool e10, bool e11, bool e12, bool e13, bool e14, bool e15, bool e16, bool e17, bool e18, bool e19, bool e20, bool e21, bool e22, bool e23, bool e24, bool e25, bool e26, bool e27, bool e28, bool e29, bool e30, bool e31) { union { bool value[32]; @@ -98,7 +98,7 @@ extern "C" DLL_EXPORT Vector256B STDMETHODCALLTYPE GetVector256B(bool e00, bool return result; } -extern "C" DLL_EXPORT void STDMETHODCALLTYPE GetVector256BOut(bool e00, bool e01, bool e02, bool e03, bool e04, bool e05, bool e06, bool e07, bool e08, bool e09, bool e10, bool e11, bool e12, bool e13, bool e14, bool e15, bool e16, bool e17, bool e18, bool e19, bool e20, bool e21, bool e22, bool e23, bool e24, bool e25, bool e26, bool e27, bool e28, bool e29, bool e30, bool e31, Vector256B* pValue) +extern "C" DLL_EXPORT void STDMETHODCALLTYPE ENABLE_AVX GetVector256BOut(bool e00, bool e01, bool e02, bool e03, bool e04, bool e05, bool e06, bool e07, bool e08, bool e09, bool e10, bool e11, bool e12, bool e13, bool e14, bool e15, bool e16, bool e17, bool e18, bool e19, bool e20, bool e21, bool e22, bool e23, bool e24, bool e25, bool e26, bool e27, bool e28, bool e29, bool e30, bool e31, Vector256B* pValue) { Vector256B value = GetVector256B(e00, e01, e02, e03, e04, e05, e06, e07, e08, e09, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31); @@ -110,18 +110,18 @@ extern "C" DLL_EXPORT void STDMETHODCALLTYPE GetVector256BOut(bool e00, bool e01 #endif } -extern "C" DLL_EXPORT const Vector256B* STDMETHODCALLTYPE GetVector256BPtr(bool e00, bool e01, bool e02, bool e03, bool e04, bool e05, bool e06, bool e07, bool e08, bool e09, bool e10, bool e11, bool e12, bool e13, bool e14, bool e15, bool e16, bool e17, bool e18, bool e19, bool e20, bool e21, bool e22, bool e23, bool e24, bool e25, bool e26, bool e27, bool e28, bool e29, bool e30, bool e31) +extern "C" DLL_EXPORT const Vector256B* STDMETHODCALLTYPE ENABLE_AVX GetVector256BPtr(bool e00, bool e01, bool e02, bool e03, bool e04, bool e05, bool e06, bool e07, bool e08, bool e09, bool e10, bool e11, bool e12, bool e13, bool e14, bool e15, bool e16, bool e17, bool e18, bool e19, bool e20, bool e21, bool e22, bool e23, bool e24, bool e25, bool e26, bool e27, bool e28, bool e29, bool e30, bool e31) { GetVector256BOut(e00, e01, e02, e03, e04, e05, e06, e07, e08, e09, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, &Vector256BValue); return &Vector256BValue; } -extern "C" DLL_EXPORT Vector256B STDMETHODCALLTYPE AddVector256B(Vector256B lhs, Vector256B rhs) +extern "C" DLL_EXPORT Vector256B STDMETHODCALLTYPE ENABLE_AVX AddVector256B(Vector256B lhs, Vector256B rhs) { throw "P/Invoke for Vector256 should be unsupported."; } -extern "C" DLL_EXPORT Vector256B STDMETHODCALLTYPE AddVector256Bs(const Vector256B* pValues, uint32_t count) +extern "C" DLL_EXPORT Vector256B STDMETHODCALLTYPE ENABLE_AVX AddVector256Bs(const Vector256B* pValues, uint32_t count) { throw "P/Invoke for Vector256 should be unsupported."; } diff --git a/src/tests/Interop/PInvoke/Generics/GenericsNative.Vector256C.cpp b/src/tests/Interop/PInvoke/Generics/GenericsNative.Vector256C.cpp index 6f4d4d3315e7b3..e567af1e013253 100644 --- a/src/tests/Interop/PInvoke/Generics/GenericsNative.Vector256C.cpp +++ b/src/tests/Interop/PInvoke/Generics/GenericsNative.Vector256C.cpp @@ -39,7 +39,7 @@ static Vector256C Vector256CValue = { }; -extern "C" DLL_EXPORT Vector256C STDMETHODCALLTYPE GetVector256C(char16_t e00, char16_t e01, char16_t e02, char16_t e03, char16_t e04, char16_t e05, char16_t e06, char16_t e07, char16_t e08, char16_t e09, char16_t e10, char16_t e11, char16_t e12, char16_t e13, char16_t e14, char16_t e15) +extern "C" DLL_EXPORT Vector256C STDMETHODCALLTYPE ENABLE_AVX GetVector256C(char16_t e00, char16_t e01, char16_t e02, char16_t e03, char16_t e04, char16_t e05, char16_t e06, char16_t e07, char16_t e08, char16_t e09, char16_t e10, char16_t e11, char16_t e12, char16_t e13, char16_t e14, char16_t e15) { union { char16_t value[16]; @@ -66,7 +66,7 @@ extern "C" DLL_EXPORT Vector256C STDMETHODCALLTYPE GetVector256C(char16_t e00, c return result; } -extern "C" DLL_EXPORT void STDMETHODCALLTYPE GetVector256COut(char16_t e00, char16_t e01, char16_t e02, char16_t e03, char16_t e04, char16_t e05, char16_t e06, char16_t e07, char16_t e08, char16_t e09, char16_t e10, char16_t e11, char16_t e12, char16_t e13, char16_t e14, char16_t e15, Vector256C* pValue) +extern "C" DLL_EXPORT void STDMETHODCALLTYPE ENABLE_AVX GetVector256COut(char16_t e00, char16_t e01, char16_t e02, char16_t e03, char16_t e04, char16_t e05, char16_t e06, char16_t e07, char16_t e08, char16_t e09, char16_t e10, char16_t e11, char16_t e12, char16_t e13, char16_t e14, char16_t e15, Vector256C* pValue) { Vector256C value = GetVector256C(e00, e01, e02, e03, e04, e05, e06, e07, e08, e09, e10, e11, e12, e13, e14, e15); @@ -78,18 +78,18 @@ extern "C" DLL_EXPORT void STDMETHODCALLTYPE GetVector256COut(char16_t e00, char #endif } -extern "C" DLL_EXPORT const Vector256C* STDMETHODCALLTYPE GetVector256CPtr(char16_t e00, char16_t e01, char16_t e02, char16_t e03, char16_t e04, char16_t e05, char16_t e06, char16_t e07, char16_t e08, char16_t e09, char16_t e10, char16_t e11, char16_t e12, char16_t e13, char16_t e14, char16_t e15) +extern "C" DLL_EXPORT const Vector256C* STDMETHODCALLTYPE ENABLE_AVX GetVector256CPtr(char16_t e00, char16_t e01, char16_t e02, char16_t e03, char16_t e04, char16_t e05, char16_t e06, char16_t e07, char16_t e08, char16_t e09, char16_t e10, char16_t e11, char16_t e12, char16_t e13, char16_t e14, char16_t e15) { GetVector256COut(e00, e01, e02, e03, e04, e05, e06, e07, e08, e09, e10, e11, e12, e13, e14, e15, &Vector256CValue); return &Vector256CValue; } -extern "C" DLL_EXPORT Vector256C STDMETHODCALLTYPE AddVector256C(Vector256C lhs, Vector256C rhs) +extern "C" DLL_EXPORT Vector256C STDMETHODCALLTYPE ENABLE_AVX AddVector256C(Vector256C lhs, Vector256C rhs) { throw "P/Invoke for Vector256 should be unsupported."; } -extern "C" DLL_EXPORT Vector256C STDMETHODCALLTYPE AddVector256Cs(const Vector256C* pValues, uint32_t count) +extern "C" DLL_EXPORT Vector256C STDMETHODCALLTYPE ENABLE_AVX AddVector256Cs(const Vector256C* pValues, uint32_t count) { throw "P/Invoke for Vector256 should be unsupported."; } diff --git a/src/tests/Interop/PInvoke/Generics/GenericsNative.Vector256L.cpp b/src/tests/Interop/PInvoke/Generics/GenericsNative.Vector256L.cpp index ffc2aff692648c..744c8ed6527bd3 100644 --- a/src/tests/Interop/PInvoke/Generics/GenericsNative.Vector256L.cpp +++ b/src/tests/Interop/PInvoke/Generics/GenericsNative.Vector256L.cpp @@ -27,7 +27,7 @@ static Vector256L Vector256LValue = { }; -extern "C" DLL_EXPORT Vector256L STDMETHODCALLTYPE GetVector256L(int64_t e00, int64_t e01, int64_t e02, int64_t e03) +extern "C" DLL_EXPORT Vector256L STDMETHODCALLTYPE ENABLE_AVX GetVector256L(int64_t e00, int64_t e01, int64_t e02, int64_t e03) { union { int64_t value[4]; @@ -42,7 +42,7 @@ extern "C" DLL_EXPORT Vector256L STDMETHODCALLTYPE GetVector256L(int64_t e00, in return result; } -extern "C" DLL_EXPORT void STDMETHODCALLTYPE GetVector256LOut(int64_t e00, int64_t e01, int64_t e02, int64_t e03, Vector256L* pValue) +extern "C" DLL_EXPORT void STDMETHODCALLTYPE ENABLE_AVX GetVector256LOut(int64_t e00, int64_t e01, int64_t e02, int64_t e03, Vector256L* pValue) { Vector256L value = GetVector256L(e00, e01, e02, e03); @@ -54,18 +54,18 @@ extern "C" DLL_EXPORT void STDMETHODCALLTYPE GetVector256LOut(int64_t e00, int64 #endif } -extern "C" DLL_EXPORT const Vector256L* STDMETHODCALLTYPE GetVector256LPtr(int64_t e00, int64_t e01, int64_t e02, int64_t e03) +extern "C" DLL_EXPORT const Vector256L* STDMETHODCALLTYPE ENABLE_AVX GetVector256LPtr(int64_t e00, int64_t e01, int64_t e02, int64_t e03) { GetVector256LOut(e00, e01, e02, e03, &Vector256LValue); return &Vector256LValue; } -extern "C" DLL_EXPORT Vector256L STDMETHODCALLTYPE AddVector256L(Vector256L lhs, Vector256L rhs) +extern "C" DLL_EXPORT Vector256L STDMETHODCALLTYPE ENABLE_AVX AddVector256L(Vector256L lhs, Vector256L rhs) { throw "P/Invoke for Vector256 should be unsupported."; } -extern "C" DLL_EXPORT Vector256L STDMETHODCALLTYPE AddVector256Ls(const Vector256L* pValues, uint32_t count) +extern "C" DLL_EXPORT Vector256L STDMETHODCALLTYPE ENABLE_AVX AddVector256Ls(const Vector256L* pValues, uint32_t count) { throw "P/Invoke for Vector256 should be unsupported."; } diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_62249/Runtime_62249.cs b/src/tests/JIT/Regression/JitBlue/Runtime_62249/Runtime_62249.cs new file mode 100644 index 00000000000000..2bdaf33de4dbfc --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_62249/Runtime_62249.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +class Runtime_62249 +{ + public struct CanBeReinterpretedAsDouble + { + public double _0; + } + + // Note that all VFP registers are occupied by d0-d7 arguments, hence the last argument is passed on the stack. + [MethodImpl(MethodImplOptions.NoInlining)] + public static int Callee(double d0, double d1, double d2, double d3, double d4, double d5, double d6, double d7, CanBeReinterpretedAsDouble onStack) + { + return onStack._0 == 62249 ? 100 : 0; + } + + public static int Caller(ref CanBeReinterpretedAsDouble byRef) + { + // Since the last parameter + // 1. Is passed by value + // 2. Has size of power of 2 + // 3. Has a single field + // morph transforms OBJ(struct, byRef) to IND(double, byRef). + // However, lower does not expect such transformation and asserts. + return Callee(0, 0, 0, 6, 2, 2, 4, 9, byRef); + } + + public static int Main(string[] args) + { + var val = new CanBeReinterpretedAsDouble(); + val._0 = 62249; + + return Caller(ref val); + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_62249/Runtime_62249.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_62249/Runtime_62249.csproj new file mode 100644 index 00000000000000..6946bed81bfd5b --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_62249/Runtime_62249.csproj @@ -0,0 +1,9 @@ + + + Exe + True + + + + + diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_62524/Runtime_62524.cs b/src/tests/JIT/Regression/JitBlue/Runtime_62524/Runtime_62524.cs new file mode 100644 index 00000000000000..7fc6c30b000b97 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_62524/Runtime_62524.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +public class Runtime_62524 +{ + public static int Main() + { + return Problem(new() { Value = 1 }) == 1 ? 100 : 101; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int Problem(StructWithIndex a) + { + bool k = a.Value == 1; + a = default; + if (k) + { + return 1; + } + + return a.Index; + } +} + +public struct StructWithIndex +{ + public int Index; + public int Value; +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_62524/Runtime_62524.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_62524/Runtime_62524.csproj new file mode 100644 index 00000000000000..fbb8cd21671d81 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_62524/Runtime_62524.csproj @@ -0,0 +1,19 @@ + + + Exe + True + + + + + + + \ No newline at end of file diff --git a/src/tests/JIT/opt/AssertionPropagation/ArrBoundElim.cs b/src/tests/JIT/opt/AssertionPropagation/ArrBoundElim.cs new file mode 100644 index 00000000000000..8fd88e2e7715d0 --- /dev/null +++ b/src/tests/JIT/opt/AssertionPropagation/ArrBoundElim.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +class Program +{ + private static int returnCode = 100; + + public static int Main(string[] args) + { + RunTestThrows(Tests.MulOutsideRange); + RunTestThrows(Tests.MulOverflow); + RunTestThrows(Tests.LshOutsideRange); + + RunTestNoThrow(Tests.MulInsideRange); + RunTestNoThrow(Tests.LshInsideRange); + + return returnCode; + } + + private static void RunTestThrows(Action action) + { + try + { + action(); + Console.WriteLine("failed " + action.Method.Name); + returnCode--; + } + catch (Exception) + { + + } + } + + private static void RunTestNoThrow(Action action) + { + try + { + action(); + } + catch (Exception) + { + Console.WriteLine("failed " + action.Method.Name); + returnCode--; + } + } +} + +public static class Tests +{ + private static byte[] smallArr => new byte[10]; + + // RangeCheck analysis should eliminate the bounds check on + // smallArr. + public static void MulInsideRange() + { + for (int i = 0; i < 3; i++) + { + smallArr[i*3] = 17; + } + } + + // RangeCheck analysis should keep the bounds check on + // smallArr. + public static void MulOutsideRange() + { + for (int i = 0; i < 3; i++) + { + smallArr[i*5] = 17; + } + } + + private static byte[] bigArr => new byte[268435460]; + + // RangeCheck analysis should detect that the multiplcation + // overflows and keep all range checks for bigArr. bigArr + // size, and the bounds on the loop were carefully chosen to to + // potentially spoof the RangeCheck analysis to eliminate a bound + // check IF overflow detection on GT_MUL for RangeCheck is implemented + // incorrectly. + public static void MulOverflow() + { + for (int i = 0; i < 39768215; i++) + { + bigArr[i*402653184] = 17; + } + } + + // RangeCheck analysis should eliminate the bounds check on + // smallArr. + public static void LshInsideRange() + { + for (int i = 0; i < 3; i++) + { + smallArr[i<<1] = 17; + } + } + + // RangeCheck analysis should keep the bounds check on + // smallArr. + public static void LshOutsideRange() + { + for (int i = 0; i < 3; i++) + { + smallArr[i<<3] = 17; + } + } + +} diff --git a/src/tests/JIT/opt/AssertionPropagation/ArrBoundElim.csproj b/src/tests/JIT/opt/AssertionPropagation/ArrBoundElim.csproj new file mode 100644 index 00000000000000..e5ad660f3d5007 --- /dev/null +++ b/src/tests/JIT/opt/AssertionPropagation/ArrBoundElim.csproj @@ -0,0 +1,13 @@ + + + Exe + + + None + True + + + + + + diff --git a/src/tests/JIT/superpmi/superpmicollect.cs b/src/tests/JIT/superpmi/superpmicollect.cs index d00272b441ca74..c588d69cd30b63 100644 --- a/src/tests/JIT/superpmi/superpmicollect.cs +++ b/src/tests/JIT/superpmi/superpmicollect.cs @@ -369,7 +369,7 @@ private static void CreateCleanMCHFile() if (File.Exists(s_baseFailMclFile) && !String.IsNullOrEmpty(File.ReadAllText(s_baseFailMclFile))) { - RunProgram(Global.McsPath, "-strip " + s_baseMchFile + " " + s_finalMchFile); + RunProgram(Global.McsPath, "-strip " + s_baseFailMclFile + " " + s_baseMchFile + " " + s_finalMchFile); } else { diff --git a/src/tests/Loader/classloader/Casting/punning.cs b/src/tests/Loader/classloader/Casting/punning.cs new file mode 100644 index 00000000000000..3971a781578b73 --- /dev/null +++ b/src/tests/Loader/classloader/Casting/punning.cs @@ -0,0 +1,165 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +using Xunit; + +partial class Program +{ + [Fact] + static void Via_GetFunctionPointer() + { + Console.WriteLine($"Running {nameof(Via_GetFunctionPointer)}..."); + + IntPtr fptr = typeof(A.Class).GetMethod("GetField").MethodHandle.GetFunctionPointer(); + Assert.NotEqual(IntPtr.Zero, fptr); + var b = new Caller.Struct() + { + Field = 0x55 + }; + int fieldValue = Caller.Class.CallGetField(b, fptr, null); + Assert.Equal(b.Field, fieldValue); + } + + [Fact] + static void Via_GetFunctionPointer_Generics() + { + Console.WriteLine($"Running {nameof(Via_GetFunctionPointer_Generics)}..."); + + IntPtr fptr = typeof(A.Class).GetMethod("GetFieldGeneric").MakeGenericMethod(typeof(object)).MethodHandle.GetFunctionPointer(); + Assert.NotEqual(IntPtr.Zero, fptr); + var b = new Caller.Struct() + { + Field = 0x55 + }; + int fieldValue = Caller.Class.CallGetField(b, fptr, null); + Assert.Equal(b.Field, fieldValue); + } + + [Fact] + static void Via_Ldftn() + { + Console.WriteLine($"Running {nameof(Via_Ldftn)}..."); + + IntPtr fptr = B.Class.GetFunctionPointer(); + Assert.NotEqual(IntPtr.Zero, fptr); + var b = new Caller.Struct() + { + Field = 0x55 + }; + int fieldValue = Caller.Class.CallGetField(b, fptr, null); + Assert.Equal(b.Field, fieldValue); + } + + [Fact] + static void Via_Ldftn_Generics() + { + Console.WriteLine($"Running {nameof(Via_Ldftn_Generics)}..."); + + IntPtr fptr = B.Class.GetFunctionPointerGeneric(); + Assert.NotEqual(IntPtr.Zero, fptr); + var b = new Caller.Struct() + { + Field = 0x55 + }; + int fieldValue = Caller.Class.CallGetField(b, fptr, null); + Assert.Equal(b.Field, fieldValue); + } + + [Fact] + static void Via_Ldftn_Generics_Virtual() + { + Console.WriteLine($"Running {nameof(Via_Ldftn_Generics_Virtual)}..."); + + object inst = new B.Derived(); + IntPtr fptr = B.Class.GetFunctionPointerGeneric(inst); + Assert.NotEqual(IntPtr.Zero, fptr); + var b = new Caller.Struct() + { + Field = 0x55 + }; + int fieldValue = Caller.Class.CallGetField(b, fptr, inst); + Assert.Equal(b.Field, fieldValue); + } + + [Fact] + static void Via_Ldftn_Generics_EarlyLoad() + { + Console.WriteLine($"Running {nameof(Via_Ldftn_Generics_EarlyLoad)}..."); + + IntPtr fptr = B.Class.GetFunctionPointer(); + Assert.NotEqual(IntPtr.Zero, fptr); + var b = new Caller.Struct() + { + Field = 0x55 + }; + int fieldValue = Caller.Class.CallGetField(b, fptr, null); + Assert.Equal(b.Field, fieldValue); + } + + [Fact] + static void Via_Ldftn_Generics_Virtual_EarlyLoad() + { + Console.WriteLine($"Running {nameof(Via_Ldftn_Generics_Virtual_EarlyLoad)}..."); + + object inst = new B.Derived(); + IntPtr fptr = B.Class.GetFunctionPointer(inst); + Assert.NotEqual(IntPtr.Zero, fptr); + var b = new Caller.Struct() + { + Field = 0x55 + }; + int fieldValue = Caller.Class.CallGetField(b, fptr, inst); + Assert.Equal(b.Field, fieldValue); + } + + [Fact] + static void Via_Ldvirtftn() + { + Console.WriteLine($"Running {nameof(Via_Ldvirtftn)}..."); + + object inst = new C.Derived(); + IntPtr fptr = C.Class.GetFunctionPointer(inst); + Assert.NotEqual(IntPtr.Zero, fptr); + var b = new Caller.Struct() + { + Field = 0x55 + }; + int fieldValue = Caller.Class.CallGetField(b, fptr, inst); + Assert.Equal(b.Field, fieldValue); + } + + [Fact] + static void Via_Ldvirtftn_Generics() + { + Console.WriteLine($"Running {nameof(Via_Ldvirtftn_Generics)}..."); + + object inst = new C.Derived(); + IntPtr fptr = C.Class.GetFunctionPointerGeneric(inst); + Assert.NotEqual(IntPtr.Zero, fptr); + var b = new Caller.Struct() + { + Field = 0x55 + }; + int fieldValue = Caller.Class.CallGetField(b, fptr, inst); + Assert.Equal(b.Field, fieldValue); + } + + [Fact] + static void Via_Ldvirtftn_Generics_EarlyLoad() + { + Console.WriteLine($"Running {nameof(Via_Ldvirtftn_Generics_EarlyLoad)}..."); + + object inst = new C.Derived(); + IntPtr fptr = C.Class.GetFunctionPointer(inst); + Assert.NotEqual(IntPtr.Zero, fptr); + var b = new Caller.Struct() + { + Field = 0x55 + }; + int fieldValue = Caller.Class.CallGetField(b, fptr, inst); + Assert.Equal(b.Field, fieldValue); + } +} \ No newline at end of file diff --git a/src/tests/Loader/classloader/Casting/punning.csproj b/src/tests/Loader/classloader/Casting/punning.csproj new file mode 100644 index 00000000000000..765f941ba7d22e --- /dev/null +++ b/src/tests/Loader/classloader/Casting/punning.csproj @@ -0,0 +1,11 @@ + + + Exe + + + + + + + + diff --git a/src/tests/Loader/classloader/Casting/punninglib.il b/src/tests/Loader/classloader/Casting/punninglib.il new file mode 100644 index 00000000000000..d5194cecadb315 --- /dev/null +++ b/src/tests/Loader/classloader/Casting/punninglib.il @@ -0,0 +1,327 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) } + +.assembly punninglib { } + +.class public sequential ansi sealed beforefieldinit Caller.Struct extends [System.Runtime]System.ValueType +{ + .field public int32 Field +} +.class public sequential ansi sealed beforefieldinit Caller.Struct`1 extends [System.Runtime]System.ValueType +{ + .field public int32 Field +} +.class public auto ansi abstract sealed beforefieldinit Caller.Class + extends [System.Runtime]System.Object +{ + .method public hidebysig static + int32 CallGetField ( + valuetype Caller.Struct s, + native int callbackRaw, + object inst + ) cil managed + { + ldarg.2 + ldnull + ceq + brfalse.s FALSE + + TRUE: nop + ldarg.0 + ldarg.1 + calli int32(valuetype Caller.Struct) + br.s DONE + + FALSE: nop + ldarg.2 + ldarg.0 + ldarg.1 + calli instance int32(valuetype Caller.Struct) + br.s DONE + + DONE: ret + } + .method public hidebysig static + int32 CallGetField ( + valuetype Caller.Struct`1 s, + native int callbackRaw, + object inst + ) cil managed + { + ldarg.2 + ldnull + ceq + brfalse.s FALSE + + TRUE: nop + ldarg.0 + ldarg.1 + calli int32(valuetype Caller.Struct`1) + br.s DONE + + FALSE: nop + ldarg.2 + ldarg.0 + ldarg.1 + calli instance int32(valuetype Caller.Struct`1) + br.s DONE + + DONE: ret + } +} + +// +// Used for GetFunctionPointer() +// +.class public sequential ansi sealed beforefieldinit A.Struct extends [System.Runtime]System.ValueType +{ + .field public int32 Field +} +.class public sequential ansi sealed beforefieldinit A.Struct`1 extends [System.Runtime]System.ValueType +{ + .field public int32 Field +} +.class public auto ansi beforefieldinit A.Class + extends [System.Runtime]System.Object +{ + .method public hidebysig static + int32 GetField (valuetype A.Struct s) cil managed + { + ldarg.0 + ldfld int32 A.Struct::Field + ret + } + .method public hidebysig static + int32 GetFieldGeneric (valuetype A.Struct`1 s) cil managed + { + ldarg.0 + ldfld int32 valuetype A.Struct`1::Field + ret + } + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} + +// +// Used for ldftn +// +.class public sequential ansi sealed beforefieldinit B.Struct extends [System.Runtime]System.ValueType +{ + .field public int32 Field +} +.class public sequential ansi sealed beforefieldinit B.Struct`1 extends [System.Runtime]System.ValueType +{ + .field public int32 Field +} +.class public sequential ansi sealed beforefieldinit B.Struct`2 extends [System.Runtime]System.ValueType +{ + .field public int32 Field +} +.class public sequential ansi sealed beforefieldinit B.Struct`3 extends [System.Runtime]System.ValueType +{ + .field public int32 Field +} +.class public sequential ansi sealed beforefieldinit B.Struct`4 extends [System.Runtime]System.ValueType +{ + .field public int32 Field +} +.class public auto ansi beforefieldinit B.Class + extends [System.Runtime]System.Object +{ + .method public hidebysig static + native int GetFunctionPointer () cil managed + { + ldftn int32 B.Class::GetField(valuetype B.Struct) + call native int [System.Runtime]System.IntPtr::op_Explicit(void*) + ret + } + .method public hidebysig static + int32 GetField (valuetype B.Struct s) cil managed + { + ldarg.0 + ldfld int32 B.Struct::Field + ret + } + .method public hidebysig static + native int GetFunctionPointerGeneric () cil managed + { + ldftn int32 B.Class::GetFieldGeneric(valuetype B.Struct`1) + call native int [System.Runtime]System.IntPtr::op_Explicit(void*) + ret + } + .method public hidebysig static + int32 GetFieldGeneric (valuetype B.Struct`1 s) cil managed + { + ldarg.0 + ldfld int32 valuetype B.Struct`1::Field + ret + } + .method public hidebysig static + native int GetFunctionPointer () cil managed + { + ldftn int32 B.Class::GetFieldGeneric(valuetype B.Struct`2) + call native int [System.Runtime]System.IntPtr::op_Explicit(void*) + ret + } + .method public hidebysig static + int32 GetFieldGeneric (valuetype B.Struct`2 s) cil managed + { + ldarg.0 + ldfld int32 valuetype B.Struct`2::Field + ret + } + .method public hidebysig static + native int GetFunctionPointerGeneric ( object inst ) cil managed + { + ldftn instance int32 B.Class::GetFieldGeneric(valuetype B.Struct`3) + call native int [System.Runtime]System.IntPtr::op_Explicit(void*) + ret + } + .method public hidebysig newslot virtual + instance int32 GetFieldGeneric (valuetype B.Struct`3 s) cil managed + { + ldarg.1 + ldfld int32 valuetype B.Struct`3::Field + ret + } + .method public hidebysig static + native int GetFunctionPointer ( object inst ) cil managed + { + ldftn instance int32 B.Class::GetFieldGeneric(valuetype B.Struct`4) + call native int [System.Runtime]System.IntPtr::op_Explicit(void*) + ret + } + .method public hidebysig newslot virtual + instance int32 GetFieldGeneric (valuetype B.Struct`4 s) cil managed + { + ldarg.1 + ldfld int32 valuetype B.Struct`4::Field + ret + } + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} +.class public auto ansi beforefieldinit B.Derived + extends B.Class +{ + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void B.Class::.ctor() + ret + } +} + +// +// Used for ldvirtftn +// +.class public sequential ansi sealed beforefieldinit C.Struct extends [System.Runtime]System.ValueType +{ + .field public int32 Field +} +.class public sequential ansi sealed beforefieldinit C.Struct`1 extends [System.Runtime]System.ValueType +{ + .field public int32 Field +} +.class public sequential ansi sealed beforefieldinit C.Struct`2 extends [System.Runtime]System.ValueType +{ + .field public int32 Field +} +.class public auto ansi beforefieldinit C.Class + extends [System.Runtime]System.Object +{ + .method public hidebysig static + native int GetFunctionPointer ( object inst ) cil managed + { + ldarg.0 + ldvirtftn instance int32 C.Class::GetField(valuetype C.Struct) + call native int [System.Runtime]System.IntPtr::op_Explicit(void*) + ret + } + .method public hidebysig newslot virtual + instance int32 GetField (valuetype C.Struct s) cil managed + { + newobj instance void [System.Runtime]System.NotImplementedException::.ctor() + throw + } + .method public hidebysig static + native int GetFunctionPointerGeneric ( object inst ) cil managed + { + ldarg.0 + ldvirtftn instance int32 C.Class::GetFieldGeneric(valuetype C.Struct`1) + call native int [System.Runtime]System.IntPtr::op_Explicit(void*) + ret + } + .method public hidebysig newslot virtual + instance int32 GetFieldGeneric (valuetype C.Struct`1 s) cil managed + { + newobj instance void [System.Runtime]System.NotImplementedException::.ctor() + throw + } + .method public hidebysig static + native int GetFunctionPointer ( object inst ) cil managed + { + ldarg.0 + ldvirtftn instance int32 C.Class::GetFieldGeneric(valuetype C.Struct`2) + call native int [System.Runtime]System.IntPtr::op_Explicit(void*) + ret + } + .method public hidebysig newslot virtual + instance int32 GetFieldGeneric (valuetype C.Struct`2 s) cil managed + { + newobj instance void [System.Runtime]System.NotImplementedException::.ctor() + throw + } + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} +.class public auto ansi beforefieldinit C.Derived + extends C.Class +{ + .method public hidebysig virtual + instance int32 GetField (valuetype C.Struct s) cil managed + { + ldarg.1 + ldfld int32 C.Struct::Field + ret + } + .method public hidebysig virtual + instance int32 GetFieldGeneric (valuetype C.Struct`1 s) cil managed + { + ldarg.1 + ldfld int32 valuetype C.Struct`1::Field + ret + } + .method public hidebysig virtual + instance int32 GetFieldGeneric (valuetype C.Struct`2 s) cil managed + { + ldarg.1 + ldfld int32 valuetype C.Struct`2::Field + ret + } + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void C.Class::.ctor() + ret + } +} diff --git a/src/tests/Loader/classloader/Casting/punninglib.ilproj b/src/tests/Loader/classloader/Casting/punninglib.ilproj new file mode 100644 index 00000000000000..bb04932e3bdaaf --- /dev/null +++ b/src/tests/Loader/classloader/Casting/punninglib.ilproj @@ -0,0 +1,9 @@ + + + Library + SharedLibrary + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/Directory.Build.props b/src/tests/Loader/classloader/TypeGeneratorTests/Directory.Build.props new file mode 100644 index 00000000000000..33c5ba495f035c --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/Directory.Build.props @@ -0,0 +1,9 @@ + + + + + false + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest0/Generated0.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest0/Generated0.ilproj index 2414dfdb478173..6d514e917d8019 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest0/Generated0.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest0/Generated0.ilproj @@ -1,7 +1,6 @@ - - Exe - 1 + + 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1/Generated1.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1/Generated1.ilproj index c08ce9ccafe234..29c4f1febc00f5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1/Generated1.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1/Generated1.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest10/Generated10.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest10/Generated10.ilproj index a2c00d227ba63a..5fa076cf71d0a6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest10/Generated10.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest10/Generated10.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest100/Generated100.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest100/Generated100.ilproj index ae926814b8d1c3..bdb7b0d6694561 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest100/Generated100.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest100/Generated100.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1000/Generated1000.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1000/Generated1000.ilproj index 3c20a475cc4786..9206cb834a2aa9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1000/Generated1000.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1000/Generated1000.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1001/Generated1001.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1001/Generated1001.ilproj index af39ad52cd4728..c08882fe9d6e7a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1001/Generated1001.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1001/Generated1001.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1002/Generated1002.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1002/Generated1002.ilproj index a3f5f692203112..42fe9e72c78557 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1002/Generated1002.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1002/Generated1002.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1003/Generated1003.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1003/Generated1003.ilproj index 8ac52a98ef471f..3b0fa322787574 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1003/Generated1003.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1003/Generated1003.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1004/Generated1004.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1004/Generated1004.ilproj index 18b84203be54ce..59af02db5d7522 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1004/Generated1004.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1004/Generated1004.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1005/Generated1005.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1005/Generated1005.ilproj index 2922b721a8832a..b2393b4323ccdf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1005/Generated1005.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1005/Generated1005.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1006/Generated1006.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1006/Generated1006.ilproj index 8c3ea6f8a7c545..71d86b179c5b5a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1006/Generated1006.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1006/Generated1006.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1007/Generated1007.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1007/Generated1007.ilproj index d7755711cd9a46..cbf4195aa3ad5d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1007/Generated1007.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1007/Generated1007.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1008/Generated1008.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1008/Generated1008.ilproj index 6014de26a37ab3..10a9cee88142e4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1008/Generated1008.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1008/Generated1008.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1009/Generated1009.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1009/Generated1009.ilproj index 2bf1a1c8193125..95e6fb67761938 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1009/Generated1009.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1009/Generated1009.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest101/Generated101.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest101/Generated101.ilproj index 8d002b6faa72d6..519dec52850175 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest101/Generated101.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest101/Generated101.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1010/Generated1010.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1010/Generated1010.ilproj index f1d69e6d8f8726..36ee5e9407c7ec 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1010/Generated1010.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1010/Generated1010.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1011/Generated1011.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1011/Generated1011.ilproj index 9dcad3f28fdb86..b1b5b1b8ff38a9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1011/Generated1011.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1011/Generated1011.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1012/Generated1012.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1012/Generated1012.ilproj index 4a5b12da7ab1ef..6f0e84e9a35639 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1012/Generated1012.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1012/Generated1012.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1013/Generated1013.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1013/Generated1013.ilproj index 69535017221207..d2d7d5224d3926 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1013/Generated1013.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1013/Generated1013.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1014/Generated1014.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1014/Generated1014.ilproj index 81eccc1d58218e..10c29aedf8fecb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1014/Generated1014.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1014/Generated1014.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1015/Generated1015.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1015/Generated1015.ilproj index 0dca935ad08dec..c1a23737bacf4d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1015/Generated1015.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1015/Generated1015.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1016/Generated1016.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1016/Generated1016.ilproj index 8b55fadca597ca..d076c147696e77 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1016/Generated1016.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1016/Generated1016.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1017/Generated1017.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1017/Generated1017.ilproj index df849f62949984..f15542b97c466f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1017/Generated1017.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1017/Generated1017.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1018/Generated1018.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1018/Generated1018.ilproj index 45a141cf677056..98174d7c6f3496 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1018/Generated1018.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1018/Generated1018.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1019/Generated1019.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1019/Generated1019.ilproj index f74ceb9b033a0f..f84113f3ab0c12 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1019/Generated1019.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1019/Generated1019.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest102/Generated102.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest102/Generated102.ilproj index 1b7375524621d1..603c5c2097346b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest102/Generated102.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest102/Generated102.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1020/Generated1020.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1020/Generated1020.ilproj index 6f994e828914cc..862beb008ac251 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1020/Generated1020.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1020/Generated1020.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1021/Generated1021.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1021/Generated1021.ilproj index 956ea3d7ea8999..660bcca1dec824 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1021/Generated1021.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1021/Generated1021.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1022/Generated1022.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1022/Generated1022.ilproj index fe6642180e5f93..d693d2f03bd7ef 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1022/Generated1022.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1022/Generated1022.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1023/Generated1023.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1023/Generated1023.ilproj index 318403cb4cbeec..2e6e5ae4aa03ac 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1023/Generated1023.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1023/Generated1023.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1024/Generated1024.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1024/Generated1024.ilproj index 699713282865c7..628cc2f7f5c723 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1024/Generated1024.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1024/Generated1024.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1025/Generated1025.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1025/Generated1025.ilproj index fb6c8882928825..6e3e820180e21c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1025/Generated1025.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1025/Generated1025.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1026/Generated1026.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1026/Generated1026.ilproj index 8a2385d46fe3b0..f4a0458d1e0d14 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1026/Generated1026.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1026/Generated1026.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1027/Generated1027.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1027/Generated1027.ilproj index f370dfff44e849..c3404d534605e7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1027/Generated1027.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1027/Generated1027.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1028/Generated1028.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1028/Generated1028.ilproj index f0b9fcdfb55602..849c1a7636e7b5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1028/Generated1028.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1028/Generated1028.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1029/Generated1029.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1029/Generated1029.ilproj index 50ffb2925f05e6..676b22b36ecdbf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1029/Generated1029.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1029/Generated1029.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest103/Generated103.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest103/Generated103.ilproj index 3a5bcdd9762cf5..93622e254c6cf4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest103/Generated103.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest103/Generated103.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1030/Generated1030.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1030/Generated1030.ilproj index 5a3f6fde2dfdf6..396ac087d67081 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1030/Generated1030.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1030/Generated1030.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1031/Generated1031.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1031/Generated1031.ilproj index 6831b599d7c71b..0905931c979d21 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1031/Generated1031.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1031/Generated1031.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1032/Generated1032.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1032/Generated1032.ilproj index f0db2d423e0c96..7554a7243e5a03 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1032/Generated1032.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1032/Generated1032.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1033/Generated1033.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1033/Generated1033.ilproj index 4cab1cb19f03f3..032b8c6805125d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1033/Generated1033.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1033/Generated1033.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1034/Generated1034.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1034/Generated1034.ilproj index ea3615cd04386f..b9e1ba88feda22 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1034/Generated1034.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1034/Generated1034.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1035/Generated1035.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1035/Generated1035.ilproj index 7f7d1f8f7a1eff..1fdbc44281106e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1035/Generated1035.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1035/Generated1035.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1036/Generated1036.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1036/Generated1036.ilproj index 26d84a5f01ec3f..63182763e70afe 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1036/Generated1036.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1036/Generated1036.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1037/Generated1037.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1037/Generated1037.ilproj index 49e17fd1018dc1..5b95ad04c0a774 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1037/Generated1037.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1037/Generated1037.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1038/Generated1038.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1038/Generated1038.ilproj index b6b5dda150c559..19fc8643b91f96 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1038/Generated1038.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1038/Generated1038.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1039/Generated1039.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1039/Generated1039.ilproj index 115c6eaff29750..3a86879eff755c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1039/Generated1039.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1039/Generated1039.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest104/Generated104.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest104/Generated104.ilproj index f420a434c9c75a..9edf0436bcd80a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest104/Generated104.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest104/Generated104.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1040/Generated1040.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1040/Generated1040.ilproj index 887a3eb4ea85bd..11a5b759096cf9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1040/Generated1040.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1040/Generated1040.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1041/Generated1041.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1041/Generated1041.ilproj index a588c16466a5c5..c036f3dcad8fc0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1041/Generated1041.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1041/Generated1041.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1042/Generated1042.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1042/Generated1042.ilproj index 9088d484aa5986..4585d86c9c289d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1042/Generated1042.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1042/Generated1042.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1043/Generated1043.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1043/Generated1043.ilproj index ab7ff60b6d372a..324fdbe3b49e8f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1043/Generated1043.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1043/Generated1043.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1044/Generated1044.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1044/Generated1044.ilproj index cc92234120018f..b2334e5494ca83 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1044/Generated1044.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1044/Generated1044.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1045/Generated1045.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1045/Generated1045.ilproj index f0e21481493710..b3aa72738f4e9d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1045/Generated1045.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1045/Generated1045.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1046/Generated1046.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1046/Generated1046.ilproj index cbf2bd2736ccc9..dd3743cee83e2b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1046/Generated1046.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1046/Generated1046.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1047/Generated1047.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1047/Generated1047.ilproj index 43eef1fe200593..e5c5f94b68aef3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1047/Generated1047.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1047/Generated1047.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1048/Generated1048.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1048/Generated1048.ilproj index d1f3a2761a551e..9dab05df5ca451 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1048/Generated1048.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1048/Generated1048.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1049/Generated1049.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1049/Generated1049.ilproj index 736babb0ef3a1a..e2803a7e26e813 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1049/Generated1049.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1049/Generated1049.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest105/Generated105.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest105/Generated105.ilproj index fea3394800659f..c44c806539e83c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest105/Generated105.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest105/Generated105.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1050/Generated1050.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1050/Generated1050.ilproj index 7478267d3b274f..d1f6453408c0a1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1050/Generated1050.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1050/Generated1050.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1051/Generated1051.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1051/Generated1051.ilproj index 1112cc5a8512d3..0fb92f07c4517f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1051/Generated1051.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1051/Generated1051.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1052/Generated1052.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1052/Generated1052.ilproj index 5f858db926f9ef..fec73d5be4896a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1052/Generated1052.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1052/Generated1052.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1053/Generated1053.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1053/Generated1053.ilproj index 1c02a585b7044b..93a6ae24567fd1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1053/Generated1053.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1053/Generated1053.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1054/Generated1054.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1054/Generated1054.ilproj index 5677eca80f5032..c3f73de5737079 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1054/Generated1054.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1054/Generated1054.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1055/Generated1055.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1055/Generated1055.ilproj index 087f197462b6d5..7d3f70dc96ad16 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1055/Generated1055.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1055/Generated1055.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1056/Generated1056.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1056/Generated1056.ilproj index 2d4944b48928ca..973193bed73b40 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1056/Generated1056.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1056/Generated1056.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1057/Generated1057.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1057/Generated1057.ilproj index 54d0dc6036ef31..642bed02db7a4f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1057/Generated1057.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1057/Generated1057.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1058/Generated1058.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1058/Generated1058.ilproj index 71a91acc1f1d5a..fb07e2dd34241a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1058/Generated1058.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1058/Generated1058.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1059/Generated1059.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1059/Generated1059.ilproj index 53c4893b4def72..7dd80664a51895 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1059/Generated1059.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1059/Generated1059.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest106/Generated106.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest106/Generated106.ilproj index f4bc64d704cfee..518d1e8a5c0aad 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest106/Generated106.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest106/Generated106.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1060/Generated1060.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1060/Generated1060.ilproj index 2a3760b4593463..baa839eec2109e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1060/Generated1060.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1060/Generated1060.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1061/Generated1061.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1061/Generated1061.ilproj index 582cdff7d7e1e3..c24678086629ea 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1061/Generated1061.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1061/Generated1061.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1062/Generated1062.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1062/Generated1062.ilproj index e1117dc7385df7..a6b2db3e25d62b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1062/Generated1062.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1062/Generated1062.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1063/Generated1063.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1063/Generated1063.ilproj index a5d4c63fdde420..0d9ea577783c12 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1063/Generated1063.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1063/Generated1063.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1064/Generated1064.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1064/Generated1064.ilproj index ab0b3f902285bc..e27330a74a7edd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1064/Generated1064.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1064/Generated1064.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1065/Generated1065.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1065/Generated1065.ilproj index 2b6a704d24ab15..d349845d2b408a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1065/Generated1065.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1065/Generated1065.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1066/Generated1066.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1066/Generated1066.ilproj index 7c6c402158b0c4..275a314892db9e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1066/Generated1066.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1066/Generated1066.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1067/Generated1067.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1067/Generated1067.ilproj index eda589d595f017..ca2da91ac1f0b3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1067/Generated1067.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1067/Generated1067.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1068/Generated1068.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1068/Generated1068.ilproj index 7cd0c19f1e791f..24f284a2e69f08 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1068/Generated1068.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1068/Generated1068.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1069/Generated1069.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1069/Generated1069.ilproj index 974d3b222f8b32..f70cc9f8e3df56 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1069/Generated1069.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1069/Generated1069.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest107/Generated107.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest107/Generated107.ilproj index 11fcebf32fdabb..d52a79b97b086d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest107/Generated107.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest107/Generated107.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1070/Generated1070.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1070/Generated1070.ilproj index 73dcf8da61505e..bbd56ed3933165 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1070/Generated1070.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1070/Generated1070.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1071/Generated1071.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1071/Generated1071.ilproj index 907ab90b47efa4..911ea7c2abeda8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1071/Generated1071.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1071/Generated1071.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1072/Generated1072.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1072/Generated1072.ilproj index 58ee6f5fd35d9b..3cc66d8b9e6aac 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1072/Generated1072.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1072/Generated1072.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1073/Generated1073.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1073/Generated1073.ilproj index b1a1d5883f2290..b73ccbbadd637d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1073/Generated1073.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1073/Generated1073.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1074/Generated1074.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1074/Generated1074.ilproj index 32465a7d812ff4..c2c5eeff693490 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1074/Generated1074.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1074/Generated1074.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1075/Generated1075.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1075/Generated1075.ilproj index c9f4a46c62f5d5..04e0c3c2ea39e6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1075/Generated1075.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1075/Generated1075.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1076/Generated1076.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1076/Generated1076.ilproj index e9317def11f367..8917b9d5e2b21d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1076/Generated1076.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1076/Generated1076.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1077/Generated1077.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1077/Generated1077.ilproj index cda912213993ce..c8c78141f06cdd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1077/Generated1077.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1077/Generated1077.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1078/Generated1078.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1078/Generated1078.ilproj index 5f2e6dd6042be9..6166edca34fc3c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1078/Generated1078.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1078/Generated1078.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1079/Generated1079.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1079/Generated1079.ilproj index 305231e293b9fb..91f56b5d212c04 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1079/Generated1079.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1079/Generated1079.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest108/Generated108.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest108/Generated108.ilproj index 6a0e8becec104c..989a84f9025494 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest108/Generated108.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest108/Generated108.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1080/Generated1080.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1080/Generated1080.ilproj index 3da490a31c4a2c..98713192e4b695 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1080/Generated1080.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1080/Generated1080.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1081/Generated1081.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1081/Generated1081.ilproj index 94ef1cf47a6a50..53ec9edd2bd5dd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1081/Generated1081.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1081/Generated1081.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1082/Generated1082.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1082/Generated1082.ilproj index 9dbff9da6881e1..c332388715cea0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1082/Generated1082.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1082/Generated1082.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1083/Generated1083.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1083/Generated1083.ilproj index 25a0bc66d8ab43..23749d3602e503 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1083/Generated1083.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1083/Generated1083.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1084/Generated1084.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1084/Generated1084.ilproj index e2f1f7dafe8c45..8aba21eaf414a2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1084/Generated1084.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1084/Generated1084.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1085/Generated1085.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1085/Generated1085.ilproj index f64fb50049ecab..bb9aa10ef9c970 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1085/Generated1085.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1085/Generated1085.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1086/Generated1086.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1086/Generated1086.ilproj index f9b8d924d6572a..ab6a0c741b0f29 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1086/Generated1086.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1086/Generated1086.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1087/Generated1087.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1087/Generated1087.ilproj index 784617972f343b..e3f3886cf353de 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1087/Generated1087.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1087/Generated1087.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1088/Generated1088.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1088/Generated1088.ilproj index 00c2fc3ed7dc42..00900777a84a06 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1088/Generated1088.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1088/Generated1088.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1089/Generated1089.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1089/Generated1089.ilproj index bc31121aef1f66..568d47abfe9339 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1089/Generated1089.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1089/Generated1089.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest109/Generated109.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest109/Generated109.ilproj index 4a84cdefb44978..fd35e0aaf9acbc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest109/Generated109.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest109/Generated109.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1090/Generated1090.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1090/Generated1090.ilproj index d9a76bef16294e..e2575265a31690 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1090/Generated1090.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1090/Generated1090.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1091/Generated1091.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1091/Generated1091.ilproj index 64878a24722d06..fdb942b4aaaaeb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1091/Generated1091.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1091/Generated1091.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1092/Generated1092.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1092/Generated1092.ilproj index bf0a193958264e..fd7abb6ce5f540 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1092/Generated1092.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1092/Generated1092.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1093/Generated1093.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1093/Generated1093.ilproj index 549d024cc82535..28c759fe932a61 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1093/Generated1093.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1093/Generated1093.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1094/Generated1094.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1094/Generated1094.ilproj index 386d3bb148ea76..e0e87f9e570728 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1094/Generated1094.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1094/Generated1094.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1095/Generated1095.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1095/Generated1095.ilproj index f21d8b1b89d832..699c3fd034baad 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1095/Generated1095.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1095/Generated1095.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1096/Generated1096.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1096/Generated1096.ilproj index dbd4d6486cdb5b..d63c661ece63cf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1096/Generated1096.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1096/Generated1096.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1097/Generated1097.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1097/Generated1097.ilproj index 1b2375c6a9d06b..59ffee1947cb41 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1097/Generated1097.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1097/Generated1097.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1098/Generated1098.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1098/Generated1098.ilproj index 37e3044c5aeea9..f49f35b0510606 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1098/Generated1098.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1098/Generated1098.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1099/Generated1099.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1099/Generated1099.ilproj index 0ccb233fe9b73a..ab725adf89b889 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1099/Generated1099.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1099/Generated1099.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest11/Generated11.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest11/Generated11.ilproj index 443b9fe9a65f2e..a54593022ce015 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest11/Generated11.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest11/Generated11.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest110/Generated110.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest110/Generated110.ilproj index 975e3de2222634..37e11793ae0c7e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest110/Generated110.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest110/Generated110.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1100/Generated1100.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1100/Generated1100.ilproj index 7bbd2169c9f9df..1d388795c5379f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1100/Generated1100.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1100/Generated1100.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1101/Generated1101.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1101/Generated1101.ilproj index 0ab41994b98162..9be40a1c17a9a4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1101/Generated1101.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1101/Generated1101.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1102/Generated1102.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1102/Generated1102.ilproj index 727b1d29a5206d..256b87430d370a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1102/Generated1102.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1102/Generated1102.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1103/Generated1103.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1103/Generated1103.ilproj index 49094ececf78ed..f1cdb89905bd74 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1103/Generated1103.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1103/Generated1103.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1104/Generated1104.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1104/Generated1104.ilproj index bfdcf2e2e3c1be..fc1665b4bd084d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1104/Generated1104.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1104/Generated1104.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1105/Generated1105.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1105/Generated1105.ilproj index d4003aab51ce03..802868eabbd0a1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1105/Generated1105.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1105/Generated1105.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1106/Generated1106.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1106/Generated1106.ilproj index d148ce836bde2f..d09a2046f69466 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1106/Generated1106.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1106/Generated1106.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1107/Generated1107.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1107/Generated1107.ilproj index 49481ccad28034..5be867b7ebcb62 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1107/Generated1107.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1107/Generated1107.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1108/Generated1108.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1108/Generated1108.ilproj index 3071fd8a4fe4f0..bf0b7e2d171f7d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1108/Generated1108.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1108/Generated1108.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1109/Generated1109.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1109/Generated1109.ilproj index 512c43b015ccf0..d5b1509c6029f2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1109/Generated1109.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1109/Generated1109.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest111/Generated111.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest111/Generated111.ilproj index 1d691d7340b74b..3fc6ae10423755 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest111/Generated111.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest111/Generated111.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1110/Generated1110.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1110/Generated1110.ilproj index 0f5179be0bade1..d107c282e60a4b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1110/Generated1110.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1110/Generated1110.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1111/Generated1111.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1111/Generated1111.ilproj index ef5b4c73a6ab6d..b89dcd56bd4b58 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1111/Generated1111.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1111/Generated1111.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1112/Generated1112.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1112/Generated1112.ilproj index 6034abe4380b88..3d7932eb209923 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1112/Generated1112.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1112/Generated1112.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1113/Generated1113.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1113/Generated1113.ilproj index 12fead55995d43..17ae71edefa1c3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1113/Generated1113.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1113/Generated1113.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1114/Generated1114.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1114/Generated1114.ilproj index 5841c9b3c85250..c4f02675fd9258 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1114/Generated1114.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1114/Generated1114.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1115/Generated1115.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1115/Generated1115.ilproj index 93dc2a147db8b2..c868791ca9a4e6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1115/Generated1115.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1115/Generated1115.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1116/Generated1116.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1116/Generated1116.ilproj index 3f5a197c6e4943..a7b0c5441c0131 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1116/Generated1116.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1116/Generated1116.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1117/Generated1117.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1117/Generated1117.ilproj index 67135b8f48ef48..12777d2e31fa70 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1117/Generated1117.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1117/Generated1117.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1118/Generated1118.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1118/Generated1118.ilproj index 08edb950b3d271..43236535c59707 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1118/Generated1118.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1118/Generated1118.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1119/Generated1119.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1119/Generated1119.ilproj index 9192f87f7671a0..34809640f5b829 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1119/Generated1119.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1119/Generated1119.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest112/Generated112.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest112/Generated112.ilproj index 297690e70fd316..1db2713ab93455 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest112/Generated112.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest112/Generated112.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1120/Generated1120.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1120/Generated1120.ilproj index 95782e02d70e4a..37bf2ef8f06f39 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1120/Generated1120.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1120/Generated1120.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1121/Generated1121.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1121/Generated1121.ilproj index 39c9d609afb932..2c2111b2d121dc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1121/Generated1121.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1121/Generated1121.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1122/Generated1122.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1122/Generated1122.ilproj index 56b89b0ea6d6f7..dbf89e237271ac 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1122/Generated1122.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1122/Generated1122.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1123/Generated1123.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1123/Generated1123.ilproj index 9b792557c68def..a4dd1d501da8c3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1123/Generated1123.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1123/Generated1123.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1124/Generated1124.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1124/Generated1124.ilproj index b120babe073314..1196330e07d2ad 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1124/Generated1124.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1124/Generated1124.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1125/Generated1125.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1125/Generated1125.ilproj index c0ced85709f2cf..555441870af436 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1125/Generated1125.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1125/Generated1125.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1126/Generated1126.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1126/Generated1126.ilproj index 2a28bc4cfb4739..b9d67b22c5c9ff 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1126/Generated1126.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1126/Generated1126.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1127/Generated1127.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1127/Generated1127.ilproj index ac2e84fe4e5cdd..7879a4096dc2d9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1127/Generated1127.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1127/Generated1127.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1128/Generated1128.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1128/Generated1128.ilproj index 359cc5270eab05..4d941dd5ec0159 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1128/Generated1128.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1128/Generated1128.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1129/Generated1129.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1129/Generated1129.ilproj index 82852cb2e565cc..b2c2d493404730 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1129/Generated1129.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1129/Generated1129.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest113/Generated113.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest113/Generated113.ilproj index 72106d098d8093..6f110f1d169cfb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest113/Generated113.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest113/Generated113.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1130/Generated1130.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1130/Generated1130.ilproj index ecdf8e324c4f7b..00ac7010091177 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1130/Generated1130.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1130/Generated1130.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1131/Generated1131.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1131/Generated1131.ilproj index f3a0385a53c1ab..4306ac5b73e732 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1131/Generated1131.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1131/Generated1131.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1132/Generated1132.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1132/Generated1132.ilproj index 215aad88d27ceb..11f7d5b7cc321a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1132/Generated1132.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1132/Generated1132.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1133/Generated1133.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1133/Generated1133.ilproj index c1e18ea4ac2ffa..d9306836fc69eb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1133/Generated1133.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1133/Generated1133.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1134/Generated1134.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1134/Generated1134.ilproj index 728a0acffeb2ca..19b2c63e328092 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1134/Generated1134.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1134/Generated1134.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1135/Generated1135.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1135/Generated1135.ilproj index 3c23d7bf7f5530..0ac31c3e2e90fd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1135/Generated1135.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1135/Generated1135.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1136/Generated1136.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1136/Generated1136.ilproj index 67281835f727fb..4ad14cc3aa2cbe 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1136/Generated1136.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1136/Generated1136.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1137/Generated1137.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1137/Generated1137.ilproj index e7b9e64f405c1b..ba673ff7c25a2e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1137/Generated1137.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1137/Generated1137.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1138/Generated1138.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1138/Generated1138.ilproj index 7b64973bc549dc..e9dd67c51027ec 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1138/Generated1138.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1138/Generated1138.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1139/Generated1139.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1139/Generated1139.ilproj index f0541c3d356f07..39fbe8ea53d497 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1139/Generated1139.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1139/Generated1139.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest114/Generated114.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest114/Generated114.ilproj index 075e71788ab422..d37d76c5747e8b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest114/Generated114.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest114/Generated114.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1140/Generated1140.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1140/Generated1140.ilproj index 9fd1807ad49372..f1167298a324f4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1140/Generated1140.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1140/Generated1140.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1141/Generated1141.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1141/Generated1141.ilproj index e4caa67618f39c..9b5f1f65b95da3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1141/Generated1141.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1141/Generated1141.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1142/Generated1142.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1142/Generated1142.ilproj index 666d2726b98bf8..f4eb6206e25117 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1142/Generated1142.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1142/Generated1142.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1143/Generated1143.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1143/Generated1143.ilproj index cd4eb90bc6fa08..4c18c266798263 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1143/Generated1143.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1143/Generated1143.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1144/Generated1144.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1144/Generated1144.ilproj index f204f99fca8f57..6d31779da6ebb9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1144/Generated1144.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1144/Generated1144.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1145/Generated1145.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1145/Generated1145.ilproj index d9299fb101f0c4..787b91cc380d62 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1145/Generated1145.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1145/Generated1145.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1146/Generated1146.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1146/Generated1146.ilproj index 3f1650095ac24e..359cae3079236b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1146/Generated1146.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1146/Generated1146.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1147/Generated1147.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1147/Generated1147.ilproj index 832da61296be57..58fca43e848f86 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1147/Generated1147.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1147/Generated1147.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1148/Generated1148.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1148/Generated1148.ilproj index 512d9a16bdd288..6f8d786f2bd3d9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1148/Generated1148.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1148/Generated1148.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1149/Generated1149.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1149/Generated1149.ilproj index 487210fc721eee..973c3d2c1ad3bc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1149/Generated1149.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1149/Generated1149.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest115/Generated115.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest115/Generated115.ilproj index e06880b988e81b..5edd40b6299bc8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest115/Generated115.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest115/Generated115.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1150/Generated1150.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1150/Generated1150.ilproj index ada245f02cc4b1..38a945cf39cac8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1150/Generated1150.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1150/Generated1150.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1151/Generated1151.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1151/Generated1151.ilproj index b5c83aa51740f5..adc6dd230f86d8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1151/Generated1151.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1151/Generated1151.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1152/Generated1152.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1152/Generated1152.ilproj index 9e2352e7ad7481..21dd4c43f9928a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1152/Generated1152.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1152/Generated1152.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1153/Generated1153.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1153/Generated1153.ilproj index 9f5e1a220a2f5d..1bbe4e55ed43dd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1153/Generated1153.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1153/Generated1153.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1154/Generated1154.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1154/Generated1154.ilproj index 098a1c157c8d26..4e0a4bac77f4f9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1154/Generated1154.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1154/Generated1154.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1155/Generated1155.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1155/Generated1155.ilproj index b7a65d475b7f82..09a9c6a22a582c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1155/Generated1155.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1155/Generated1155.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1156/Generated1156.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1156/Generated1156.ilproj index 5287c1e61a06d3..b380b106f38080 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1156/Generated1156.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1156/Generated1156.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1157/Generated1157.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1157/Generated1157.ilproj index 9e4fd0014040a1..9e26a4c4f82608 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1157/Generated1157.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1157/Generated1157.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1158/Generated1158.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1158/Generated1158.ilproj index f7a1fb80811720..61e52910a4274a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1158/Generated1158.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1158/Generated1158.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1159/Generated1159.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1159/Generated1159.ilproj index a2a41931515975..4567b3cc82d44e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1159/Generated1159.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1159/Generated1159.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest116/Generated116.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest116/Generated116.ilproj index c2b4e4d9d439c2..475e10de2382c0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest116/Generated116.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest116/Generated116.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1160/Generated1160.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1160/Generated1160.ilproj index c78c8b0f2119c0..a52e9de261ab2e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1160/Generated1160.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1160/Generated1160.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1161/Generated1161.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1161/Generated1161.ilproj index c92dc30bc2a0ee..b9c39ae5f54265 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1161/Generated1161.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1161/Generated1161.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1162/Generated1162.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1162/Generated1162.ilproj index 2d8ca46b24e813..77bc22e5353cea 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1162/Generated1162.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1162/Generated1162.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1163/Generated1163.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1163/Generated1163.ilproj index 2cc0765a9334ed..46dfb783e11c6f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1163/Generated1163.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1163/Generated1163.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1164/Generated1164.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1164/Generated1164.ilproj index b511a008e1697f..c73a41bf92d730 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1164/Generated1164.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1164/Generated1164.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1165/Generated1165.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1165/Generated1165.ilproj index ec5b10b9abae67..39e79e82f76c80 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1165/Generated1165.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1165/Generated1165.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1166/Generated1166.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1166/Generated1166.ilproj index 0f174d59643681..5e54adcbe593d6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1166/Generated1166.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1166/Generated1166.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1167/Generated1167.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1167/Generated1167.ilproj index 01b30c88b46f5d..3002284e1c833d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1167/Generated1167.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1167/Generated1167.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1168/Generated1168.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1168/Generated1168.ilproj index 8ae4619c6c7159..3ed29c0f49b6c2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1168/Generated1168.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1168/Generated1168.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1169/Generated1169.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1169/Generated1169.ilproj index a02c8199e95ed8..f552ef4588b22d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1169/Generated1169.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1169/Generated1169.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest117/Generated117.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest117/Generated117.ilproj index cda81b10f44b85..e549b3442d745e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest117/Generated117.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest117/Generated117.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1170/Generated1170.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1170/Generated1170.ilproj index a7e87bcb6edab6..f173e32a393e40 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1170/Generated1170.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1170/Generated1170.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1171/Generated1171.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1171/Generated1171.ilproj index 4f396af1008903..dcd16a1500ff2d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1171/Generated1171.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1171/Generated1171.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1172/Generated1172.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1172/Generated1172.ilproj index 6f2499d8bec0da..876f70fe564aa5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1172/Generated1172.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1172/Generated1172.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1173/Generated1173.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1173/Generated1173.ilproj index df27db9dd71cc0..6921c2e9c6e3ec 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1173/Generated1173.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1173/Generated1173.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1174/Generated1174.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1174/Generated1174.ilproj index 118b68a3cc9921..387e2deb3dc79f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1174/Generated1174.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1174/Generated1174.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1175/Generated1175.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1175/Generated1175.ilproj index ba243cddc39565..07a0643fd2e615 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1175/Generated1175.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1175/Generated1175.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1176/Generated1176.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1176/Generated1176.ilproj index afaeb4c24efee9..dd8fbf42f32e50 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1176/Generated1176.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1176/Generated1176.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1177/Generated1177.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1177/Generated1177.ilproj index a70ab7eb81b5dc..1a53a2433afebf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1177/Generated1177.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1177/Generated1177.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1178/Generated1178.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1178/Generated1178.ilproj index 82910855fca272..e0728c63a40255 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1178/Generated1178.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1178/Generated1178.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1179/Generated1179.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1179/Generated1179.ilproj index 3efcd8eed7c958..a416473dcb6842 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1179/Generated1179.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1179/Generated1179.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest118/Generated118.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest118/Generated118.ilproj index 8346c306339198..a1b552636a5fdc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest118/Generated118.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest118/Generated118.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1180/Generated1180.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1180/Generated1180.ilproj index d62580b50dc46e..7a9386ef6a5d5d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1180/Generated1180.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1180/Generated1180.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1181/Generated1181.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1181/Generated1181.ilproj index 52deee1fc57521..f71887c4840ee3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1181/Generated1181.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1181/Generated1181.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1182/Generated1182.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1182/Generated1182.ilproj index 712e09aef00d64..03ddb238d007a6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1182/Generated1182.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1182/Generated1182.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1183/Generated1183.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1183/Generated1183.ilproj index 642a4e7d4399b6..99b8bd54b3d4b5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1183/Generated1183.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1183/Generated1183.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1184/Generated1184.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1184/Generated1184.ilproj index bd35ee19750c20..8e9c17aa4b65f8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1184/Generated1184.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1184/Generated1184.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1185/Generated1185.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1185/Generated1185.ilproj index 3e2d3581d25308..48cc49cc6cbd91 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1185/Generated1185.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1185/Generated1185.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1186/Generated1186.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1186/Generated1186.ilproj index d77cf26aaeeca5..f7c9f5a521ef4a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1186/Generated1186.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1186/Generated1186.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1187/Generated1187.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1187/Generated1187.ilproj index ae0c17acfdec04..727c13b5af2bb5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1187/Generated1187.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1187/Generated1187.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1188/Generated1188.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1188/Generated1188.ilproj index b88867bafb32b9..d33cbc4e456d4b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1188/Generated1188.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1188/Generated1188.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1189/Generated1189.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1189/Generated1189.ilproj index 696df162651a4f..6e499cb265d672 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1189/Generated1189.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1189/Generated1189.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest119/Generated119.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest119/Generated119.ilproj index 60b5a2072a8c54..69741973c2242d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest119/Generated119.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest119/Generated119.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1190/Generated1190.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1190/Generated1190.ilproj index a432f7e4b342f7..5b8011b5379cc8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1190/Generated1190.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1190/Generated1190.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1191/Generated1191.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1191/Generated1191.ilproj index 294da11fb8b65c..6f30ae03144917 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1191/Generated1191.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1191/Generated1191.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1192/Generated1192.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1192/Generated1192.ilproj index 2a9596a048f028..379b8540f749e1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1192/Generated1192.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1192/Generated1192.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1193/Generated1193.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1193/Generated1193.ilproj index 41aa6b71e6c9f9..a05a1ab899f898 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1193/Generated1193.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1193/Generated1193.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1194/Generated1194.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1194/Generated1194.ilproj index 1697b9bcd57337..4b539c2c05291e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1194/Generated1194.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1194/Generated1194.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1195/Generated1195.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1195/Generated1195.ilproj index a454ecbb6097a3..db98f1f0395b69 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1195/Generated1195.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1195/Generated1195.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1196/Generated1196.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1196/Generated1196.ilproj index c962ec6c8fc715..efb3a394fb9764 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1196/Generated1196.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1196/Generated1196.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1197/Generated1197.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1197/Generated1197.ilproj index 7ec722ad6eeeef..b88b521203778b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1197/Generated1197.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1197/Generated1197.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1198/Generated1198.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1198/Generated1198.ilproj index c8989e76a62be7..80db03a610f5b3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1198/Generated1198.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1198/Generated1198.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1199/Generated1199.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1199/Generated1199.ilproj index e5f447cda56bf5..d8bed7ee19b0f9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1199/Generated1199.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1199/Generated1199.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest12/Generated12.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest12/Generated12.ilproj index b4bba13661f29d..4091a29665a642 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest12/Generated12.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest12/Generated12.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest120/Generated120.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest120/Generated120.ilproj index 4ec81a4412d726..616ab3c3ddd0fb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest120/Generated120.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest120/Generated120.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1200/Generated1200.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1200/Generated1200.ilproj index 927f9aa788ca8f..e7b4ee0c94c656 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1200/Generated1200.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1200/Generated1200.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1201/Generated1201.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1201/Generated1201.ilproj index 556bc2c904661b..0e7607edd79dfe 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1201/Generated1201.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1201/Generated1201.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1202/Generated1202.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1202/Generated1202.ilproj index 84b2800e9bec1f..e79dd2ec5676a5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1202/Generated1202.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1202/Generated1202.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1203/Generated1203.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1203/Generated1203.ilproj index 8beb1f5eb6eb7d..651671c6ae6ea0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1203/Generated1203.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1203/Generated1203.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1204/Generated1204.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1204/Generated1204.ilproj index 047c821b647721..2eef81fc95462e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1204/Generated1204.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1204/Generated1204.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1205/Generated1205.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1205/Generated1205.ilproj index ab0bebe07e067a..3bc493d71ca919 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1205/Generated1205.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1205/Generated1205.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1206/Generated1206.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1206/Generated1206.ilproj index 1d4f618a5ef848..cc4b9402f990c4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1206/Generated1206.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1206/Generated1206.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1207/Generated1207.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1207/Generated1207.ilproj index 13bd85f4923a34..217f4d95f48893 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1207/Generated1207.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1207/Generated1207.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1208/Generated1208.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1208/Generated1208.ilproj index a2d4fc961c669d..d5669663266523 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1208/Generated1208.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1208/Generated1208.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1209/Generated1209.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1209/Generated1209.ilproj index f4e4979ee33434..a927b8eff36e36 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1209/Generated1209.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1209/Generated1209.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest121/Generated121.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest121/Generated121.ilproj index a11e1764ec5331..1bcc72cde32cd9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest121/Generated121.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest121/Generated121.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1210/Generated1210.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1210/Generated1210.ilproj index b9d409a2dc7d36..575f32a99c058a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1210/Generated1210.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1210/Generated1210.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1211/Generated1211.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1211/Generated1211.ilproj index a30f34e4e44fdb..1cc61de0e3e120 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1211/Generated1211.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1211/Generated1211.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1212/Generated1212.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1212/Generated1212.ilproj index 0240840ed3de78..690fd629c79505 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1212/Generated1212.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1212/Generated1212.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1213/Generated1213.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1213/Generated1213.ilproj index af69ed65cb675b..d39ca8293dd33a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1213/Generated1213.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1213/Generated1213.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1214/Generated1214.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1214/Generated1214.ilproj index 19521dd801bb64..88c09019a1ff93 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1214/Generated1214.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1214/Generated1214.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1215/Generated1215.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1215/Generated1215.ilproj index dbe2bf52539293..a9c24d93d7aa93 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1215/Generated1215.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1215/Generated1215.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1216/Generated1216.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1216/Generated1216.ilproj index 8d4a0e8a5d885f..6f38bd6b061a0e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1216/Generated1216.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1216/Generated1216.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1217/Generated1217.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1217/Generated1217.ilproj index 851ca351ac6091..7a59b7123fdac0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1217/Generated1217.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1217/Generated1217.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1218/Generated1218.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1218/Generated1218.ilproj index b39ea0b2e842bd..997abe5bd6f8a6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1218/Generated1218.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1218/Generated1218.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1219/Generated1219.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1219/Generated1219.ilproj index 885c7e8ef8bff1..ab872bf06331d3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1219/Generated1219.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1219/Generated1219.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest122/Generated122.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest122/Generated122.ilproj index f33db27c526343..9ce785e94d5bff 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest122/Generated122.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest122/Generated122.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1220/Generated1220.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1220/Generated1220.ilproj index 7cf871a1631a5c..88804c40f04402 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1220/Generated1220.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1220/Generated1220.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1221/Generated1221.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1221/Generated1221.ilproj index ac4a5c065884cf..2afda44a5bea74 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1221/Generated1221.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1221/Generated1221.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1222/Generated1222.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1222/Generated1222.ilproj index a781fea834d50b..e963e36118418b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1222/Generated1222.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1222/Generated1222.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1223/Generated1223.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1223/Generated1223.ilproj index 8e3c45f560a0fc..b233df1ee84fe5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1223/Generated1223.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1223/Generated1223.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1224/Generated1224.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1224/Generated1224.ilproj index ea5ce662222feb..a318278be44335 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1224/Generated1224.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1224/Generated1224.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1225/Generated1225.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1225/Generated1225.ilproj index 9d30339f997f87..3fabed9014186e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1225/Generated1225.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1225/Generated1225.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1226/Generated1226.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1226/Generated1226.ilproj index f72195703c57bf..e7a5b74e018b7a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1226/Generated1226.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1226/Generated1226.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1227/Generated1227.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1227/Generated1227.ilproj index 7373df6709c398..e74fc374ab277e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1227/Generated1227.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1227/Generated1227.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1228/Generated1228.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1228/Generated1228.ilproj index df15c23468c6de..9153f3631c0711 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1228/Generated1228.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1228/Generated1228.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1229/Generated1229.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1229/Generated1229.ilproj index cbd931140cc4c2..a22692cfe86fdf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1229/Generated1229.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1229/Generated1229.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest123/Generated123.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest123/Generated123.ilproj index e9357ca500adaa..4e7bb43b9222d5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest123/Generated123.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest123/Generated123.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1230/Generated1230.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1230/Generated1230.ilproj index 25ef478f3ffef9..097e1d77f40e1e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1230/Generated1230.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1230/Generated1230.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1231/Generated1231.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1231/Generated1231.ilproj index 62a5ded1d9d942..a79d7fc54dde1f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1231/Generated1231.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1231/Generated1231.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1232/Generated1232.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1232/Generated1232.ilproj index c9c6a07160600d..e8bca9f80a94bb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1232/Generated1232.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1232/Generated1232.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1233/Generated1233.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1233/Generated1233.ilproj index 7fadee23c2879d..b4dc04a24cad16 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1233/Generated1233.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1233/Generated1233.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1234/Generated1234.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1234/Generated1234.ilproj index aeb45426e59ba8..5a4873c6027865 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1234/Generated1234.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1234/Generated1234.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1235/Generated1235.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1235/Generated1235.ilproj index 92c29992fbc5da..01439183370253 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1235/Generated1235.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1235/Generated1235.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1236/Generated1236.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1236/Generated1236.ilproj index b636bef9064183..988cc482a952c6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1236/Generated1236.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1236/Generated1236.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1237/Generated1237.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1237/Generated1237.ilproj index d74a7bc6d9dc35..6f8173e44f91fe 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1237/Generated1237.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1237/Generated1237.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1238/Generated1238.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1238/Generated1238.ilproj index dad0680fc113b0..9a04a9a409c033 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1238/Generated1238.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1238/Generated1238.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1239/Generated1239.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1239/Generated1239.ilproj index 68ff2261d803c6..d0108835cf62bf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1239/Generated1239.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1239/Generated1239.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest124/Generated124.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest124/Generated124.ilproj index b23653ac9242de..2129e0b1ace9d3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest124/Generated124.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest124/Generated124.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1240/Generated1240.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1240/Generated1240.ilproj index 85602a4764abab..a2961a49ce994a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1240/Generated1240.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1240/Generated1240.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1241/Generated1241.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1241/Generated1241.ilproj index 6f8dd9e85fa7a2..92769e4bb28f57 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1241/Generated1241.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1241/Generated1241.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1242/Generated1242.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1242/Generated1242.ilproj index 8e8cc5cba5aa1d..15cdc516d3ebc7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1242/Generated1242.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1242/Generated1242.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1243/Generated1243.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1243/Generated1243.ilproj index 40926742b90236..2607dcad5256f4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1243/Generated1243.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1243/Generated1243.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1244/Generated1244.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1244/Generated1244.ilproj index c43b114b3d6cad..2f04cdfc36421a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1244/Generated1244.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1244/Generated1244.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1245/Generated1245.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1245/Generated1245.ilproj index 7b80da038c3743..77002b27708373 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1245/Generated1245.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1245/Generated1245.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1246/Generated1246.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1246/Generated1246.ilproj index 1cdd562c140fee..8daa785126e906 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1246/Generated1246.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1246/Generated1246.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1247/Generated1247.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1247/Generated1247.ilproj index d32378da539e92..9431ee8a3dccdb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1247/Generated1247.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1247/Generated1247.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1248/Generated1248.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1248/Generated1248.ilproj index fd7236213eec71..0368c1275c3755 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1248/Generated1248.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1248/Generated1248.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1249/Generated1249.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1249/Generated1249.ilproj index 16ee3fa04a38c6..70ea735522bc04 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1249/Generated1249.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1249/Generated1249.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest125/Generated125.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest125/Generated125.ilproj index 3907a0df908670..69b5f17032b8c8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest125/Generated125.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest125/Generated125.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1250/Generated1250.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1250/Generated1250.ilproj index e1d42322048076..e1bb1b3a609912 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1250/Generated1250.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1250/Generated1250.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1251/Generated1251.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1251/Generated1251.ilproj index 401586aca9d49e..631fb62bfd0de1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1251/Generated1251.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1251/Generated1251.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1252/Generated1252.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1252/Generated1252.ilproj index ecd465638c5497..f5607b403a1e0c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1252/Generated1252.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1252/Generated1252.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1253/Generated1253.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1253/Generated1253.ilproj index 793098dcf687f7..2d08a7e4f8f886 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1253/Generated1253.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1253/Generated1253.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1254/Generated1254.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1254/Generated1254.ilproj index 0fb9fc394128a7..578b39f3870a2b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1254/Generated1254.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1254/Generated1254.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1255/Generated1255.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1255/Generated1255.ilproj index c85d89b120deec..0769fa1f707da6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1255/Generated1255.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1255/Generated1255.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1256/Generated1256.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1256/Generated1256.ilproj index 6c37b4654d1771..130aff6f14b11b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1256/Generated1256.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1256/Generated1256.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1257/Generated1257.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1257/Generated1257.ilproj index cdc5ce5ce79038..5be53dc58cb6de 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1257/Generated1257.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1257/Generated1257.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1258/Generated1258.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1258/Generated1258.ilproj index da088bf3da67df..9521c3b77cb551 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1258/Generated1258.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1258/Generated1258.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1259/Generated1259.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1259/Generated1259.ilproj index 649f49f7c47f70..b818636b8d6cf7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1259/Generated1259.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1259/Generated1259.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest126/Generated126.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest126/Generated126.ilproj index 6ee80cdac21d0d..4509888b0f3d54 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest126/Generated126.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest126/Generated126.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1260/Generated1260.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1260/Generated1260.ilproj index a2a9c71c76f071..38fab6a65714d3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1260/Generated1260.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1260/Generated1260.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1261/Generated1261.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1261/Generated1261.ilproj index 413b3bfc885ec8..5c1d339828f5e1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1261/Generated1261.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1261/Generated1261.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1262/Generated1262.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1262/Generated1262.ilproj index 13f11409a55c1b..fad1e8c1fec058 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1262/Generated1262.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1262/Generated1262.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1263/Generated1263.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1263/Generated1263.ilproj index deb47a20769d85..e2499d66e0a953 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1263/Generated1263.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1263/Generated1263.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1264/Generated1264.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1264/Generated1264.ilproj index 1f82d3b912eea9..d50c6698d6c370 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1264/Generated1264.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1264/Generated1264.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1265/Generated1265.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1265/Generated1265.ilproj index 52a8230b648e0d..ff872fbd4d58ba 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1265/Generated1265.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1265/Generated1265.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1266/Generated1266.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1266/Generated1266.ilproj index 88985654c9a0b3..93af1d2a4330ee 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1266/Generated1266.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1266/Generated1266.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1267/Generated1267.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1267/Generated1267.ilproj index e69b8ee8577854..c2bbacb4ed6a85 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1267/Generated1267.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1267/Generated1267.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1268/Generated1268.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1268/Generated1268.ilproj index 355a3b52c5007f..882afd5ab8e5f1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1268/Generated1268.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1268/Generated1268.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1269/Generated1269.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1269/Generated1269.ilproj index 7a4fd5c9893126..f78e5979c41d25 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1269/Generated1269.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1269/Generated1269.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest127/Generated127.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest127/Generated127.ilproj index 4598c791853ad4..57ae9077b62b3a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest127/Generated127.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest127/Generated127.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1270/Generated1270.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1270/Generated1270.ilproj index 7c29c4b609de28..ce51fc90c02e1f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1270/Generated1270.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1270/Generated1270.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1271/Generated1271.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1271/Generated1271.ilproj index a027a178812029..e6d250c1b7351f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1271/Generated1271.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1271/Generated1271.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1272/Generated1272.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1272/Generated1272.ilproj index 32e344c2b143d2..20c0af0e29f75c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1272/Generated1272.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1272/Generated1272.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1273/Generated1273.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1273/Generated1273.ilproj index 70e9ac4d6523c5..5b7b3901d6171c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1273/Generated1273.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1273/Generated1273.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1274/Generated1274.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1274/Generated1274.ilproj index 0198d8ef637f37..9650de081e65ad 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1274/Generated1274.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1274/Generated1274.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1275/Generated1275.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1275/Generated1275.ilproj index 8b603d0512a8f5..64f304c2f631eb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1275/Generated1275.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1275/Generated1275.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1276/Generated1276.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1276/Generated1276.ilproj index 0c7d69ae43504d..f1f1be8da34473 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1276/Generated1276.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1276/Generated1276.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1277/Generated1277.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1277/Generated1277.ilproj index 41dc375e8f27e5..6ba3149275d78e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1277/Generated1277.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1277/Generated1277.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1278/Generated1278.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1278/Generated1278.ilproj index e036ceae65df77..505bfc335b003a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1278/Generated1278.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1278/Generated1278.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1279/Generated1279.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1279/Generated1279.ilproj index 76d75e888c89c2..313cdd1cd614e9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1279/Generated1279.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1279/Generated1279.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest128/Generated128.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest128/Generated128.ilproj index 1b31cc69b527fb..ee37269bf941e9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest128/Generated128.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest128/Generated128.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1280/Generated1280.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1280/Generated1280.ilproj index 1be641db7857d3..f0114375e71a1a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1280/Generated1280.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1280/Generated1280.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1281/Generated1281.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1281/Generated1281.ilproj index a19a33e76c85ae..bc6519d96aea00 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1281/Generated1281.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1281/Generated1281.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1282/Generated1282.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1282/Generated1282.ilproj index ec9fee8c78f26d..0041401cf7606d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1282/Generated1282.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1282/Generated1282.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1283/Generated1283.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1283/Generated1283.ilproj index 9079104d875250..fce11c2f8365a5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1283/Generated1283.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1283/Generated1283.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1284/Generated1284.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1284/Generated1284.ilproj index 2565c1d736d163..f94f4b8143d3f7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1284/Generated1284.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1284/Generated1284.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1285/Generated1285.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1285/Generated1285.ilproj index 29d1cd74ad944d..3dd3333927de70 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1285/Generated1285.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1285/Generated1285.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1286/Generated1286.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1286/Generated1286.ilproj index aa47e08339cb52..860d95f4bf1eb6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1286/Generated1286.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1286/Generated1286.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1287/Generated1287.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1287/Generated1287.ilproj index c76ff38268bf60..99371e799fa8d8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1287/Generated1287.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1287/Generated1287.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1288/Generated1288.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1288/Generated1288.ilproj index 5adf752d1b8fa0..70dd7ee4a90e34 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1288/Generated1288.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1288/Generated1288.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1289/Generated1289.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1289/Generated1289.ilproj index ebebb6e2b3ad24..06a942c47a4b73 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1289/Generated1289.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1289/Generated1289.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest129/Generated129.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest129/Generated129.ilproj index 4db3479517b98b..fb137617b59607 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest129/Generated129.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest129/Generated129.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1290/Generated1290.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1290/Generated1290.ilproj index 9b4d15eb589284..6d62a10c3f2100 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1290/Generated1290.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1290/Generated1290.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1291/Generated1291.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1291/Generated1291.ilproj index d613fbfcdf3c3b..718be46c143c64 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1291/Generated1291.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1291/Generated1291.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1292/Generated1292.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1292/Generated1292.ilproj index d5772aacf30358..c60432569c72ba 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1292/Generated1292.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1292/Generated1292.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1293/Generated1293.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1293/Generated1293.ilproj index 9a22141047a1fc..51f18e1b9a80c3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1293/Generated1293.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1293/Generated1293.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1294/Generated1294.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1294/Generated1294.ilproj index f3c58f5ce072b1..2edbd695933efe 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1294/Generated1294.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1294/Generated1294.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1295/Generated1295.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1295/Generated1295.ilproj index 40d4ca9822e0c8..248b4cb6d1838a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1295/Generated1295.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1295/Generated1295.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1296/Generated1296.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1296/Generated1296.ilproj index 561d7db9681547..bbb81d0bafdd5d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1296/Generated1296.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1296/Generated1296.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1297/Generated1297.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1297/Generated1297.ilproj index ca6dd1167c05ef..28e23ae4e50e9d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1297/Generated1297.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1297/Generated1297.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1298/Generated1298.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1298/Generated1298.ilproj index edbf887feb961e..52c2f9258d66ee 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1298/Generated1298.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1298/Generated1298.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1299/Generated1299.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1299/Generated1299.ilproj index 76a202acc21260..cae52647d90a0c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1299/Generated1299.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1299/Generated1299.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest13/Generated13.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest13/Generated13.ilproj index 029300bb6a1cc3..63e5c265ff1d94 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest13/Generated13.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest13/Generated13.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest130/Generated130.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest130/Generated130.ilproj index 0a4a35154decc7..0382c1a610330e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest130/Generated130.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest130/Generated130.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1300/Generated1300.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1300/Generated1300.ilproj index 98d0ec385a970c..f1e54c2974a27e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1300/Generated1300.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1300/Generated1300.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1301/Generated1301.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1301/Generated1301.ilproj index a9c11be2b581ad..edbb3c9ed8881f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1301/Generated1301.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1301/Generated1301.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1302/Generated1302.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1302/Generated1302.ilproj index d328b5d70735ab..22f2b4c4e049be 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1302/Generated1302.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1302/Generated1302.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1303/Generated1303.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1303/Generated1303.ilproj index a251c0801b5a94..cfe1c5b231a5c8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1303/Generated1303.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1303/Generated1303.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1304/Generated1304.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1304/Generated1304.ilproj index 62cd6f7b2bfe7d..2d3e8b3ceef072 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1304/Generated1304.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1304/Generated1304.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1305/Generated1305.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1305/Generated1305.ilproj index d00b52884fc9a1..cb43db382bebca 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1305/Generated1305.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1305/Generated1305.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1306/Generated1306.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1306/Generated1306.ilproj index 993258255cc9f2..46e345d14fbfe3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1306/Generated1306.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1306/Generated1306.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1307/Generated1307.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1307/Generated1307.ilproj index 6241d35c05ced6..66f992addb209e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1307/Generated1307.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1307/Generated1307.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1308/Generated1308.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1308/Generated1308.ilproj index 066766706ad8ea..57db016659bf71 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1308/Generated1308.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1308/Generated1308.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1309/Generated1309.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1309/Generated1309.ilproj index 8cb9977cb2cbd8..c11089c82fb313 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1309/Generated1309.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1309/Generated1309.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest131/Generated131.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest131/Generated131.ilproj index 3e66ff4a171234..c75e5c7bfdd0c7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest131/Generated131.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest131/Generated131.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1310/Generated1310.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1310/Generated1310.ilproj index 46f26fa596672c..69d7bafe48e09d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1310/Generated1310.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1310/Generated1310.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1311/Generated1311.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1311/Generated1311.ilproj index 65d7a4a2aca5c0..da6b59cce8f302 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1311/Generated1311.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1311/Generated1311.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1312/Generated1312.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1312/Generated1312.ilproj index 4892b25c0f5183..ae71e8bfd7f044 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1312/Generated1312.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1312/Generated1312.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1313/Generated1313.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1313/Generated1313.ilproj index d1128ca9b89f1c..ddf72af8d570bc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1313/Generated1313.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1313/Generated1313.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1314/Generated1314.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1314/Generated1314.ilproj index 96bd3a52236e67..a2be23a957c9a6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1314/Generated1314.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1314/Generated1314.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1315/Generated1315.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1315/Generated1315.ilproj index 9e85eaccc882ad..214d66934f4894 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1315/Generated1315.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1315/Generated1315.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1316/Generated1316.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1316/Generated1316.ilproj index 1ed8889dec822f..4edc7372b2081c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1316/Generated1316.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1316/Generated1316.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1317/Generated1317.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1317/Generated1317.ilproj index 12b9fd20ffd25d..ff0bda500c3c08 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1317/Generated1317.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1317/Generated1317.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1318/Generated1318.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1318/Generated1318.ilproj index 7f635b62107f08..92024c0edf546d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1318/Generated1318.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1318/Generated1318.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1319/Generated1319.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1319/Generated1319.ilproj index 170d638be7031f..485e1567cf0e92 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1319/Generated1319.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1319/Generated1319.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest132/Generated132.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest132/Generated132.ilproj index dddac02f25a5cc..31c1d09cb5f89b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest132/Generated132.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest132/Generated132.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1320/Generated1320.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1320/Generated1320.ilproj index 40cfc38c496594..a9cb55b76ca43b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1320/Generated1320.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1320/Generated1320.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1321/Generated1321.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1321/Generated1321.ilproj index 5f9ffe3c6dc188..e9b30274c8bcce 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1321/Generated1321.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1321/Generated1321.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1322/Generated1322.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1322/Generated1322.ilproj index 2e8de8b7aec30a..9c5f6cbc36bafc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1322/Generated1322.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1322/Generated1322.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1323/Generated1323.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1323/Generated1323.ilproj index a8ef97c0695612..a60147f95e1a13 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1323/Generated1323.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1323/Generated1323.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1324/Generated1324.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1324/Generated1324.ilproj index 3e4fae017c0337..007c788aa2cbd2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1324/Generated1324.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1324/Generated1324.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1325/Generated1325.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1325/Generated1325.ilproj index b705d8e14bc022..84efafebafbbcf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1325/Generated1325.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1325/Generated1325.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1326/Generated1326.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1326/Generated1326.ilproj index fa2543fe491c51..462a22a1aac12e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1326/Generated1326.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1326/Generated1326.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1327/Generated1327.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1327/Generated1327.ilproj index 5a99bb42412448..1b9a101f5c7fa0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1327/Generated1327.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1327/Generated1327.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1328/Generated1328.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1328/Generated1328.ilproj index de21a1ab202a70..39b0dc0fb065ac 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1328/Generated1328.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1328/Generated1328.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1329/Generated1329.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1329/Generated1329.ilproj index 39d10a93437460..d1832240846be7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1329/Generated1329.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1329/Generated1329.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest133/Generated133.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest133/Generated133.ilproj index 3cd35d81dfc8bc..13e3627b33b5ad 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest133/Generated133.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest133/Generated133.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1330/Generated1330.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1330/Generated1330.ilproj index 45b67fdee00e90..ebb1c647a3f97a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1330/Generated1330.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1330/Generated1330.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1331/Generated1331.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1331/Generated1331.ilproj index ab888c9e96070e..07c286f7b2fb14 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1331/Generated1331.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1331/Generated1331.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1332/Generated1332.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1332/Generated1332.ilproj index dc003dbcd5b8dd..a9ffadbd6e6784 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1332/Generated1332.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1332/Generated1332.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1333/Generated1333.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1333/Generated1333.ilproj index 38d7d7c546386b..5bcffdb134220d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1333/Generated1333.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1333/Generated1333.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1334/Generated1334.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1334/Generated1334.ilproj index b497cfaa721795..9ea5b2cac199bd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1334/Generated1334.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1334/Generated1334.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1335/Generated1335.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1335/Generated1335.ilproj index 6658ea66a8858e..2112facc697e54 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1335/Generated1335.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1335/Generated1335.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1336/Generated1336.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1336/Generated1336.ilproj index fa7b75568a316a..01c3ed816f77f8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1336/Generated1336.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1336/Generated1336.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1337/Generated1337.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1337/Generated1337.ilproj index 76e123d1e9d8f3..3cdd0b6d04933b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1337/Generated1337.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1337/Generated1337.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1338/Generated1338.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1338/Generated1338.ilproj index b65eaa1fb9394e..5b98c144d686aa 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1338/Generated1338.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1338/Generated1338.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1339/Generated1339.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1339/Generated1339.ilproj index 59a09e0f8ade77..4f85de6769bb07 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1339/Generated1339.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1339/Generated1339.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest134/Generated134.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest134/Generated134.ilproj index 4bdb3f86a1d20b..e5e634b4c8e07d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest134/Generated134.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest134/Generated134.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1340/Generated1340.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1340/Generated1340.ilproj index 0c35f633d95032..103ff516e9d356 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1340/Generated1340.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1340/Generated1340.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1341/Generated1341.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1341/Generated1341.ilproj index 4e4c307363e1f9..be0f4e0ffaf84d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1341/Generated1341.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1341/Generated1341.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1342/Generated1342.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1342/Generated1342.ilproj index bbdcd347b5483c..7f18cd69f3cf2e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1342/Generated1342.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1342/Generated1342.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1343/Generated1343.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1343/Generated1343.ilproj index 52a217e0fb6300..d038d06f56a138 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1343/Generated1343.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1343/Generated1343.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1344/Generated1344.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1344/Generated1344.ilproj index 22154ee82a5b97..1e24b8b5283309 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1344/Generated1344.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1344/Generated1344.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1345/Generated1345.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1345/Generated1345.ilproj index 7fe2d3d74744e6..a5e29497362760 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1345/Generated1345.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1345/Generated1345.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1346/Generated1346.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1346/Generated1346.ilproj index fa14fc72846c3e..282b77889522e1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1346/Generated1346.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1346/Generated1346.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1347/Generated1347.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1347/Generated1347.ilproj index 59b8c2b8441919..7c92f305a34e87 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1347/Generated1347.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1347/Generated1347.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1348/Generated1348.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1348/Generated1348.ilproj index 5ce11eb3b81705..ea44da922c5f0a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1348/Generated1348.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1348/Generated1348.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1349/Generated1349.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1349/Generated1349.ilproj index 8dc7f4f43bca7f..7a4a9e22c21523 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1349/Generated1349.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1349/Generated1349.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest135/Generated135.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest135/Generated135.ilproj index 5a6488cdf8932c..17d17d5aa7f766 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest135/Generated135.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest135/Generated135.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1350/Generated1350.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1350/Generated1350.ilproj index adfe658071d094..4ec396b11d7886 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1350/Generated1350.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1350/Generated1350.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1351/Generated1351.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1351/Generated1351.ilproj index 296f4c2d9f8383..8b6d2e60827733 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1351/Generated1351.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1351/Generated1351.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1352/Generated1352.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1352/Generated1352.ilproj index 08ef7ea02d57e2..9b9926b4549f10 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1352/Generated1352.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1352/Generated1352.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1353/Generated1353.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1353/Generated1353.ilproj index dfa22b34253e28..de46d49be35d9f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1353/Generated1353.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1353/Generated1353.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1354/Generated1354.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1354/Generated1354.ilproj index e352f1145c7d15..b8f149ec576e1c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1354/Generated1354.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1354/Generated1354.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1355/Generated1355.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1355/Generated1355.ilproj index 248a958e99c728..970c1b09870d4a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1355/Generated1355.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1355/Generated1355.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1356/Generated1356.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1356/Generated1356.ilproj index 7dcfecc118debe..f9f58885cf0c59 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1356/Generated1356.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1356/Generated1356.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1357/Generated1357.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1357/Generated1357.ilproj index b0059a32283b83..1bda1f520a5d71 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1357/Generated1357.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1357/Generated1357.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1358/Generated1358.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1358/Generated1358.ilproj index 44fea38580377c..4320a00e81e131 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1358/Generated1358.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1358/Generated1358.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1359/Generated1359.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1359/Generated1359.ilproj index 89308f743631dd..01a8f841377bdd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1359/Generated1359.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1359/Generated1359.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest136/Generated136.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest136/Generated136.ilproj index 811d8fb6e23d31..7f383a8e1e5df3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest136/Generated136.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest136/Generated136.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1360/Generated1360.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1360/Generated1360.ilproj index 4897f18ed13130..dc49f40ab18da8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1360/Generated1360.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1360/Generated1360.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1361/Generated1361.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1361/Generated1361.ilproj index 161847262385c6..18a94b82d597f0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1361/Generated1361.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1361/Generated1361.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1362/Generated1362.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1362/Generated1362.ilproj index d4e36d57e3fe7c..304d2b14c56479 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1362/Generated1362.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1362/Generated1362.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1363/Generated1363.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1363/Generated1363.ilproj index 089b41c0e9876f..108cd27eba458e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1363/Generated1363.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1363/Generated1363.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1364/Generated1364.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1364/Generated1364.ilproj index d877df61ee64c6..4bc02fc526998f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1364/Generated1364.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1364/Generated1364.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1365/Generated1365.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1365/Generated1365.ilproj index 9d1f40002baa91..54db88191b1175 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1365/Generated1365.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1365/Generated1365.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1366/Generated1366.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1366/Generated1366.ilproj index d42d26609bc667..237e726fec7cfe 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1366/Generated1366.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1366/Generated1366.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1367/Generated1367.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1367/Generated1367.ilproj index 454213c5921b51..bdb7a09c6df553 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1367/Generated1367.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1367/Generated1367.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1368/Generated1368.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1368/Generated1368.ilproj index 6a04ea34c4c123..6b940886b48a80 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1368/Generated1368.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1368/Generated1368.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1369/Generated1369.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1369/Generated1369.ilproj index bb0c4526c15a29..6f190bbf47c6b3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1369/Generated1369.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1369/Generated1369.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest137/Generated137.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest137/Generated137.ilproj index 5fe4f6ab63522e..12d7e6b90c0128 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest137/Generated137.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest137/Generated137.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1370/Generated1370.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1370/Generated1370.ilproj index 46e60f81d0488c..513e0363fdefed 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1370/Generated1370.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1370/Generated1370.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1371/Generated1371.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1371/Generated1371.ilproj index f0509599242b33..463b09c6b5912d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1371/Generated1371.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1371/Generated1371.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1372/Generated1372.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1372/Generated1372.ilproj index c2ebc782df4abc..350ccc27582457 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1372/Generated1372.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1372/Generated1372.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1373/Generated1373.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1373/Generated1373.ilproj index de0c64e548f4e9..d8dcfb592c2b03 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1373/Generated1373.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1373/Generated1373.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1374/Generated1374.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1374/Generated1374.ilproj index e5589765ed5f27..131fbed094a51b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1374/Generated1374.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1374/Generated1374.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1375/Generated1375.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1375/Generated1375.ilproj index d766f564bbb260..98027775772e14 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1375/Generated1375.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1375/Generated1375.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1376/Generated1376.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1376/Generated1376.ilproj index dddfe8ac320a31..156b6ca983c499 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1376/Generated1376.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1376/Generated1376.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1377/Generated1377.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1377/Generated1377.ilproj index 377567fd550f9f..c7146d9817dc2f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1377/Generated1377.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1377/Generated1377.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1378/Generated1378.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1378/Generated1378.ilproj index e47cf80f22cbb5..7a4c15c9426a04 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1378/Generated1378.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1378/Generated1378.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1379/Generated1379.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1379/Generated1379.ilproj index 3d4c22889df5b5..68224db4765a5d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1379/Generated1379.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1379/Generated1379.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest138/Generated138.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest138/Generated138.ilproj index 743bb9f0fdadca..304e1a7316f6fd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest138/Generated138.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest138/Generated138.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1380/Generated1380.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1380/Generated1380.ilproj index 611123f1683f97..c0658abf4cbde1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1380/Generated1380.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1380/Generated1380.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1381/Generated1381.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1381/Generated1381.ilproj index 6b9cc7f71788f5..cb5309682432ed 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1381/Generated1381.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1381/Generated1381.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1382/Generated1382.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1382/Generated1382.ilproj index 18830e14da77d3..9272728c4a5524 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1382/Generated1382.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1382/Generated1382.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1383/Generated1383.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1383/Generated1383.ilproj index ca97ce6fc8996c..e49fbcecfe28e9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1383/Generated1383.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1383/Generated1383.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1384/Generated1384.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1384/Generated1384.ilproj index 8b6d62c1e668b0..d1545057f9cf2d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1384/Generated1384.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1384/Generated1384.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1385/Generated1385.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1385/Generated1385.ilproj index 01590fb20bb846..d529dea74bd1a3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1385/Generated1385.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1385/Generated1385.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1386/Generated1386.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1386/Generated1386.ilproj index b2c4f2df533c5b..248e07e736de04 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1386/Generated1386.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1386/Generated1386.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1387/Generated1387.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1387/Generated1387.ilproj index 1857ab252513b4..dad60baa5451a6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1387/Generated1387.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1387/Generated1387.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1388/Generated1388.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1388/Generated1388.ilproj index 78c452896dca88..c61394d447cb20 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1388/Generated1388.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1388/Generated1388.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1389/Generated1389.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1389/Generated1389.ilproj index 8a099ffe4d49a8..416d36ba3b0540 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1389/Generated1389.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1389/Generated1389.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest139/Generated139.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest139/Generated139.ilproj index 56d8b2ff50f97d..0faab0287e7d81 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest139/Generated139.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest139/Generated139.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1390/Generated1390.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1390/Generated1390.ilproj index df63fff0fb9a26..92fe8404645e43 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1390/Generated1390.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1390/Generated1390.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1391/Generated1391.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1391/Generated1391.ilproj index c2bdd7597585e4..0be06fa160f083 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1391/Generated1391.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1391/Generated1391.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1392/Generated1392.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1392/Generated1392.ilproj index 8a9134ca75594a..85dc3acb758297 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1392/Generated1392.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1392/Generated1392.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1393/Generated1393.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1393/Generated1393.ilproj index 91fb170dd1e876..391d5aa7feb67d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1393/Generated1393.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1393/Generated1393.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1394/Generated1394.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1394/Generated1394.ilproj index e66d29d01f076f..4e56212b0ea342 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1394/Generated1394.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1394/Generated1394.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1395/Generated1395.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1395/Generated1395.ilproj index 897d42ab2680a3..755464e122b982 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1395/Generated1395.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1395/Generated1395.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1396/Generated1396.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1396/Generated1396.ilproj index e82f4fd778c545..14759cf2708bd4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1396/Generated1396.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1396/Generated1396.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1397/Generated1397.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1397/Generated1397.ilproj index a31a0e21165b75..02e7136ee37fdc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1397/Generated1397.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1397/Generated1397.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1398/Generated1398.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1398/Generated1398.ilproj index 0f50c667b44837..5da79f6d540a61 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1398/Generated1398.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1398/Generated1398.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1399/Generated1399.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1399/Generated1399.ilproj index bf4516f6e41592..a2651697a51af0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1399/Generated1399.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1399/Generated1399.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest14/Generated14.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest14/Generated14.ilproj index b585339d14bcb7..515551fb332aab 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest14/Generated14.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest14/Generated14.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest140/Generated140.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest140/Generated140.ilproj index 26886b93053082..07ef045a2c8f5c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest140/Generated140.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest140/Generated140.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1400/Generated1400.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1400/Generated1400.ilproj index 8e376895d58ac0..350d307a43da1b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1400/Generated1400.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1400/Generated1400.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1401/Generated1401.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1401/Generated1401.ilproj index 0c9fa721ce743a..f026675d343050 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1401/Generated1401.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1401/Generated1401.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1402/Generated1402.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1402/Generated1402.ilproj index 0122a92cda8377..9272a629deb462 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1402/Generated1402.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1402/Generated1402.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1403/Generated1403.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1403/Generated1403.ilproj index 719d2c83240ed7..845dd20ce31a84 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1403/Generated1403.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1403/Generated1403.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1404/Generated1404.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1404/Generated1404.ilproj index 3426c06746bac6..12257ce0e619d4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1404/Generated1404.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1404/Generated1404.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1405/Generated1405.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1405/Generated1405.ilproj index 3e6593b7fc1524..867fad9c99bddc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1405/Generated1405.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1405/Generated1405.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1406/Generated1406.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1406/Generated1406.ilproj index 494c4a8731dee6..38cc67510876e4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1406/Generated1406.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1406/Generated1406.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1407/Generated1407.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1407/Generated1407.ilproj index 226b8be1eaab9f..34372efdad377d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1407/Generated1407.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1407/Generated1407.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1408/Generated1408.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1408/Generated1408.ilproj index 8d483ef8c613d6..18d97c7333ae5d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1408/Generated1408.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1408/Generated1408.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1409/Generated1409.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1409/Generated1409.ilproj index 6d07b04b1f4712..40108a450b97d9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1409/Generated1409.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1409/Generated1409.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest141/Generated141.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest141/Generated141.ilproj index 607c1453a49659..2104c9d2b6f05e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest141/Generated141.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest141/Generated141.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1410/Generated1410.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1410/Generated1410.ilproj index f70c3e0c0f0e4f..19388e2d64a71a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1410/Generated1410.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1410/Generated1410.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1411/Generated1411.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1411/Generated1411.ilproj index 8a94305d60dc01..27d425a57af924 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1411/Generated1411.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1411/Generated1411.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1412/Generated1412.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1412/Generated1412.ilproj index 9d1bb33ebb0c2b..d9b3cae0fcc197 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1412/Generated1412.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1412/Generated1412.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1413/Generated1413.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1413/Generated1413.ilproj index 1d95b350ff514b..44f0bd80451e68 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1413/Generated1413.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1413/Generated1413.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1414/Generated1414.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1414/Generated1414.ilproj index 1d4b99bd8d5780..ee30461ba5fde9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1414/Generated1414.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1414/Generated1414.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1415/Generated1415.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1415/Generated1415.ilproj index fb8ad324251fb4..7e560ed540e15d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1415/Generated1415.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1415/Generated1415.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1416/Generated1416.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1416/Generated1416.ilproj index f7801e11acd89f..a1ef5de14b5535 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1416/Generated1416.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1416/Generated1416.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1417/Generated1417.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1417/Generated1417.ilproj index bef78328da6d93..329c62d6dcf63f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1417/Generated1417.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1417/Generated1417.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1418/Generated1418.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1418/Generated1418.ilproj index df827770d7666a..4a1588348bf516 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1418/Generated1418.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1418/Generated1418.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1419/Generated1419.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1419/Generated1419.ilproj index 1207df0f24ccf0..58039751c6dc49 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1419/Generated1419.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1419/Generated1419.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest142/Generated142.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest142/Generated142.ilproj index 76bf99adf6ca5d..8da5a3c7c31a21 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest142/Generated142.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest142/Generated142.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1420/Generated1420.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1420/Generated1420.ilproj index 06e43825eeb15d..9cce900e86ba2d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1420/Generated1420.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1420/Generated1420.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1421/Generated1421.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1421/Generated1421.ilproj index d8cbce66bbf09e..4e460a4f1c194d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1421/Generated1421.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1421/Generated1421.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1422/Generated1422.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1422/Generated1422.ilproj index fffb6437af46d8..f20675d58295d5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1422/Generated1422.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1422/Generated1422.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1423/Generated1423.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1423/Generated1423.ilproj index 94c4e66ade9323..03efcf6f0e41fb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1423/Generated1423.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1423/Generated1423.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1424/Generated1424.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1424/Generated1424.ilproj index 8459ccf9b56172..1d7c99628da486 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1424/Generated1424.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1424/Generated1424.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1425/Generated1425.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1425/Generated1425.ilproj index bb3a14bc02ece3..bb0e993e1d9380 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1425/Generated1425.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1425/Generated1425.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1426/Generated1426.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1426/Generated1426.ilproj index fc5bfcd33bd48d..d8cb450e3c6988 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1426/Generated1426.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1426/Generated1426.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1427/Generated1427.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1427/Generated1427.ilproj index 6bc7ea21ea8f76..ec1e9f9e4c406a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1427/Generated1427.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1427/Generated1427.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1428/Generated1428.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1428/Generated1428.ilproj index 515fcc2266f994..49c091836d5203 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1428/Generated1428.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1428/Generated1428.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1429/Generated1429.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1429/Generated1429.ilproj index e617af096bd0db..d5a4a1c1e3ce90 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1429/Generated1429.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1429/Generated1429.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest143/Generated143.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest143/Generated143.ilproj index efb0be95130dbb..abbe4c1845d826 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest143/Generated143.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest143/Generated143.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1430/Generated1430.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1430/Generated1430.ilproj index 329d1d0a0b1a74..de7db8ef7f52ab 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1430/Generated1430.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1430/Generated1430.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1431/Generated1431.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1431/Generated1431.ilproj index 5ea61fd0581886..884f917e3d4286 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1431/Generated1431.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1431/Generated1431.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1432/Generated1432.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1432/Generated1432.ilproj index 1fe3fb1a522f1c..006a79783e47db 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1432/Generated1432.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1432/Generated1432.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1433/Generated1433.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1433/Generated1433.ilproj index c35c7c05b18bd0..1edea2ffb85f9e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1433/Generated1433.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1433/Generated1433.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1434/Generated1434.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1434/Generated1434.ilproj index abbf77a0baadb5..1af0834b8a78a8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1434/Generated1434.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1434/Generated1434.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1435/Generated1435.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1435/Generated1435.ilproj index a4da9c6c13ace6..2027093be2f121 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1435/Generated1435.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1435/Generated1435.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1436/Generated1436.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1436/Generated1436.ilproj index efd56487db6376..cbe5702548152d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1436/Generated1436.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1436/Generated1436.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1437/Generated1437.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1437/Generated1437.ilproj index 8fd7160ce97e19..826bae91ae03d7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1437/Generated1437.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1437/Generated1437.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1438/Generated1438.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1438/Generated1438.ilproj index c0e6ceceb99e1e..ce92fa013cf898 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1438/Generated1438.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1438/Generated1438.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1439/Generated1439.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1439/Generated1439.ilproj index 96f2690545438e..f16e1598cdd02b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1439/Generated1439.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1439/Generated1439.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest144/Generated144.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest144/Generated144.ilproj index d12685694e41fc..6b2a263cb70714 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest144/Generated144.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest144/Generated144.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1440/Generated1440.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1440/Generated1440.ilproj index 9677fff3f9ecaf..b60dffc993bd3f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1440/Generated1440.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1440/Generated1440.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1441/Generated1441.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1441/Generated1441.ilproj index b792bc6cda3c1d..55e3e023794ee3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1441/Generated1441.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1441/Generated1441.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1442/Generated1442.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1442/Generated1442.ilproj index d9413f75a78026..50c90d732371e0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1442/Generated1442.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1442/Generated1442.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1443/Generated1443.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1443/Generated1443.ilproj index e1a9911d9cd7d8..e04c3d6609967b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1443/Generated1443.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1443/Generated1443.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1444/Generated1444.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1444/Generated1444.ilproj index 4e4e1df970da99..3ce0955bdf17a6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1444/Generated1444.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1444/Generated1444.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1445/Generated1445.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1445/Generated1445.ilproj index fbcd63748ad826..c3a3a12f0b2287 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1445/Generated1445.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1445/Generated1445.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1446/Generated1446.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1446/Generated1446.ilproj index 57106cb2f5f0bd..15a3ce9de34945 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1446/Generated1446.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1446/Generated1446.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1447/Generated1447.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1447/Generated1447.ilproj index af51566042da6b..147614cb694dcf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1447/Generated1447.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1447/Generated1447.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1448/Generated1448.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1448/Generated1448.ilproj index 9f06f91a0f6eab..37821fbe16a62a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1448/Generated1448.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1448/Generated1448.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1449/Generated1449.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1449/Generated1449.ilproj index 1a5ea0b867d08e..7ba0fc1e3059d1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1449/Generated1449.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1449/Generated1449.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest145/Generated145.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest145/Generated145.ilproj index ff6e8fd7d0e25a..763184ad5d8e90 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest145/Generated145.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest145/Generated145.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1450/Generated1450.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1450/Generated1450.ilproj index 662d7b606f0206..5672b0f932ff30 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1450/Generated1450.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1450/Generated1450.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1451/Generated1451.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1451/Generated1451.ilproj index c31d8880ba385d..f9f315e2f5e1c7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1451/Generated1451.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1451/Generated1451.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1452/Generated1452.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1452/Generated1452.ilproj index 13591b950412ce..df3b0f20c4e363 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1452/Generated1452.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1452/Generated1452.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1453/Generated1453.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1453/Generated1453.ilproj index 22b4a2c93000b3..65a51ab097b81d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1453/Generated1453.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1453/Generated1453.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1454/Generated1454.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1454/Generated1454.ilproj index 24d536dedc7ead..9b9aa717ad0792 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1454/Generated1454.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1454/Generated1454.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1455/Generated1455.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1455/Generated1455.ilproj index 00b5e8f2bbdfa5..47660093e6a37f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1455/Generated1455.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1455/Generated1455.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1456/Generated1456.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1456/Generated1456.ilproj index 03829d28597055..1966162a9affbe 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1456/Generated1456.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1456/Generated1456.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1457/Generated1457.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1457/Generated1457.ilproj index c850c493a6a1f3..2372fc18aab221 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1457/Generated1457.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1457/Generated1457.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1458/Generated1458.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1458/Generated1458.ilproj index dbd5153d58f8da..8d18a2a63b6cc1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1458/Generated1458.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1458/Generated1458.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1459/Generated1459.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1459/Generated1459.ilproj index 69df94b093d96e..262433a7a9d09e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1459/Generated1459.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1459/Generated1459.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest146/Generated146.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest146/Generated146.ilproj index 45a6dd3046353e..9625a93b434459 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest146/Generated146.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest146/Generated146.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1460/Generated1460.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1460/Generated1460.ilproj index 307343e4dfadb4..1cd63799eeac0f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1460/Generated1460.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1460/Generated1460.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1461/Generated1461.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1461/Generated1461.ilproj index 7609ec89b1f13a..a97b643d458147 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1461/Generated1461.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1461/Generated1461.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1462/Generated1462.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1462/Generated1462.ilproj index 67cd2a7a79c98b..9239234fc6f35f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1462/Generated1462.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1462/Generated1462.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1463/Generated1463.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1463/Generated1463.ilproj index cc6eb4ed7ef02a..c8772593c6c91f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1463/Generated1463.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1463/Generated1463.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1464/Generated1464.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1464/Generated1464.ilproj index cb0f1136f710d5..d78d79fa384b0b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1464/Generated1464.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1464/Generated1464.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1465/Generated1465.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1465/Generated1465.ilproj index 8b6c4207af565d..43e31890a78216 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1465/Generated1465.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1465/Generated1465.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1466/Generated1466.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1466/Generated1466.ilproj index f27c83e5aab0b9..79eb578bb12ae6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1466/Generated1466.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1466/Generated1466.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1467/Generated1467.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1467/Generated1467.ilproj index 478721a34cbf23..678ad60fbe1427 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1467/Generated1467.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1467/Generated1467.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1468/Generated1468.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1468/Generated1468.ilproj index ab6c950b367c4d..0f923cf72a9fad 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1468/Generated1468.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1468/Generated1468.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1469/Generated1469.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1469/Generated1469.ilproj index 972ebb749d8188..040a2118c0c2a4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1469/Generated1469.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1469/Generated1469.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest147/Generated147.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest147/Generated147.ilproj index fb6c64ed94826c..e6388af96a5245 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest147/Generated147.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest147/Generated147.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1470/Generated1470.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1470/Generated1470.ilproj index c5cfccdbb876e7..bb16da14ec035f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1470/Generated1470.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1470/Generated1470.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1471/Generated1471.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1471/Generated1471.ilproj index 4ceee0d537695a..3c5324b98413f0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1471/Generated1471.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1471/Generated1471.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1472/Generated1472.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1472/Generated1472.ilproj index 4d5fff5013b3eb..0ad50739cb2486 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1472/Generated1472.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1472/Generated1472.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1473/Generated1473.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1473/Generated1473.ilproj index 1a6afa89de054b..3bb695471aacad 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1473/Generated1473.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1473/Generated1473.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1474/Generated1474.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1474/Generated1474.ilproj index 0b715b519de270..e771dd78d2f489 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1474/Generated1474.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1474/Generated1474.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1475/Generated1475.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1475/Generated1475.ilproj index 7d9d6d550c6c83..29f0ed452f81b7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1475/Generated1475.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1475/Generated1475.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1476/Generated1476.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1476/Generated1476.ilproj index 0380d33565c57c..2726d7b2ed36ec 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1476/Generated1476.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1476/Generated1476.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1477/Generated1477.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1477/Generated1477.ilproj index 7d1fb1d40646ba..5e0a5c2f5165be 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1477/Generated1477.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1477/Generated1477.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1478/Generated1478.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1478/Generated1478.ilproj index 17bab0de215b5e..3e0447666a2cc2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1478/Generated1478.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1478/Generated1478.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1479/Generated1479.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1479/Generated1479.ilproj index 37ec36ca739726..c546b6812ce0ec 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1479/Generated1479.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1479/Generated1479.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest148/Generated148.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest148/Generated148.ilproj index de2febb68444dc..9605aa8c19ccd6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest148/Generated148.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest148/Generated148.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1480/Generated1480.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1480/Generated1480.ilproj index 89790e516f8343..7d4a9d77b4c23e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1480/Generated1480.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1480/Generated1480.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1481/Generated1481.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1481/Generated1481.ilproj index 7ac7a1a9891f52..cea5c0716198cc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1481/Generated1481.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1481/Generated1481.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1482/Generated1482.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1482/Generated1482.ilproj index 8f6d4db3edc07a..566048052514f4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1482/Generated1482.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1482/Generated1482.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1483/Generated1483.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1483/Generated1483.ilproj index babef79fc2c41d..f7143eac12164c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1483/Generated1483.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1483/Generated1483.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1484/Generated1484.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1484/Generated1484.ilproj index 5af467773e4771..a18239bd2f1880 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1484/Generated1484.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1484/Generated1484.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1485/Generated1485.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1485/Generated1485.ilproj index 0afd92f6d23a4a..5b38bf72c46dbe 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1485/Generated1485.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1485/Generated1485.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1486/Generated1486.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1486/Generated1486.ilproj index d4e4fd6f6f94ad..d175f5a356f02d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1486/Generated1486.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1486/Generated1486.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1487/Generated1487.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1487/Generated1487.ilproj index 616eb459dce1f6..b18b759cc048d9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1487/Generated1487.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1487/Generated1487.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1488/Generated1488.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1488/Generated1488.ilproj index 0b7ba8f894041c..74568e4b04ceec 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1488/Generated1488.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1488/Generated1488.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1489/Generated1489.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1489/Generated1489.ilproj index f65566303b792e..04e2feb909a214 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1489/Generated1489.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1489/Generated1489.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest149/Generated149.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest149/Generated149.ilproj index 822ad5cd74437e..ef67a0faffe374 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest149/Generated149.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest149/Generated149.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1490/Generated1490.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1490/Generated1490.ilproj index 5a76a777dfc6a4..dc7e54f92d4d8b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1490/Generated1490.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1490/Generated1490.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1491/Generated1491.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1491/Generated1491.ilproj index 2bc66a461334cc..43bcb731b8f139 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1491/Generated1491.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1491/Generated1491.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1492/Generated1492.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1492/Generated1492.ilproj index 3a38313fa82dd7..40b31e9f695e93 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1492/Generated1492.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1492/Generated1492.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1493/Generated1493.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1493/Generated1493.ilproj index 19c3b326833c45..ab01862eb422b4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1493/Generated1493.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1493/Generated1493.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1494/Generated1494.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1494/Generated1494.ilproj index bb9bd59fe3e110..dcc543859bb047 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1494/Generated1494.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1494/Generated1494.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1495/Generated1495.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1495/Generated1495.ilproj index bdc7046920f340..18d9a8f5ba9be4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1495/Generated1495.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1495/Generated1495.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1496/Generated1496.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1496/Generated1496.ilproj index 12e8aa118430e1..e319ebc93f9728 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1496/Generated1496.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1496/Generated1496.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1497/Generated1497.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1497/Generated1497.ilproj index fe8175a1b3b661..ae35ebed14001c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1497/Generated1497.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1497/Generated1497.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1498/Generated1498.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1498/Generated1498.ilproj index 79d40237f0c02e..067966f8408c94 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1498/Generated1498.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1498/Generated1498.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1499/Generated1499.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1499/Generated1499.ilproj index 778fc0dbe26092..085db31c7b2d92 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1499/Generated1499.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1499/Generated1499.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest15/Generated15.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest15/Generated15.ilproj index b76d10edf3bf1b..e7ec8eb4d7cb8f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest15/Generated15.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest15/Generated15.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest150/Generated150.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest150/Generated150.ilproj index a57ae250a02917..024f05f7340d2f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest150/Generated150.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest150/Generated150.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1500/Generated1500.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1500/Generated1500.ilproj index 309d0d80eceb9a..e05b575629a1d3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1500/Generated1500.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest1500/Generated1500.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest151/Generated151.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest151/Generated151.ilproj index 25c18e46135853..a3319b36575eda 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest151/Generated151.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest151/Generated151.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest152/Generated152.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest152/Generated152.ilproj index be26bc8356a5ea..3960314deaa003 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest152/Generated152.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest152/Generated152.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest153/Generated153.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest153/Generated153.ilproj index 5df8f3d2e45e73..725ca799e1004c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest153/Generated153.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest153/Generated153.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest154/Generated154.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest154/Generated154.ilproj index 31fa9fb11adcec..5d064238b56625 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest154/Generated154.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest154/Generated154.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest155/Generated155.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest155/Generated155.ilproj index 65868eebae4ec8..28ac11da69658f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest155/Generated155.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest155/Generated155.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest156/Generated156.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest156/Generated156.ilproj index f95708b7d44449..b786eff3a9a9b3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest156/Generated156.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest156/Generated156.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest157/Generated157.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest157/Generated157.ilproj index 0359ef2fa97e6b..b2a00482a99023 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest157/Generated157.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest157/Generated157.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest158/Generated158.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest158/Generated158.ilproj index f0b01f187f61f5..8a0b7ec422f465 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest158/Generated158.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest158/Generated158.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest159/Generated159.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest159/Generated159.ilproj index b0e79fd03bcae1..a2f53c27653bc3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest159/Generated159.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest159/Generated159.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest16/Generated16.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest16/Generated16.ilproj index 731fc311418d93..ba64dac08c0e51 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest16/Generated16.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest16/Generated16.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest160/Generated160.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest160/Generated160.ilproj index 9eeb4180425079..c21d7fcdb8f0ca 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest160/Generated160.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest160/Generated160.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest161/Generated161.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest161/Generated161.ilproj index dbfcaebd09cb67..2edab1e517d82c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest161/Generated161.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest161/Generated161.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest162/Generated162.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest162/Generated162.ilproj index c38c757b156c28..06c2830e02c088 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest162/Generated162.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest162/Generated162.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest163/Generated163.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest163/Generated163.ilproj index c6407393591001..f4572f0890872b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest163/Generated163.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest163/Generated163.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest164/Generated164.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest164/Generated164.ilproj index 76679438140821..ad499fc6ae4b3b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest164/Generated164.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest164/Generated164.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest165/Generated165.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest165/Generated165.ilproj index aaf4e953715bcc..5a5c69c40c8b01 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest165/Generated165.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest165/Generated165.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest166/Generated166.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest166/Generated166.ilproj index 1bb8a12182f88e..aae5512cc4aa19 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest166/Generated166.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest166/Generated166.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest167/Generated167.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest167/Generated167.ilproj index ec407afe8f5d1d..1c302f2f9bf246 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest167/Generated167.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest167/Generated167.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest168/Generated168.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest168/Generated168.ilproj index ed77ca7ace9ae8..ca413649d135ad 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest168/Generated168.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest168/Generated168.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest169/Generated169.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest169/Generated169.ilproj index 968a22fe72f50d..a8cc3358239ae7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest169/Generated169.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest169/Generated169.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest17/Generated17.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest17/Generated17.ilproj index cd9776659ef587..81d663618c8e02 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest17/Generated17.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest17/Generated17.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest170/Generated170.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest170/Generated170.ilproj index 39e899a82a3ed0..27eab65f438c9d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest170/Generated170.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest170/Generated170.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest171/Generated171.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest171/Generated171.ilproj index cd11cc96e9ecee..2aa5b8366834ea 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest171/Generated171.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest171/Generated171.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest172/Generated172.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest172/Generated172.ilproj index d1ad0ccb48299d..423cf65bf90f68 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest172/Generated172.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest172/Generated172.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest173/Generated173.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest173/Generated173.ilproj index 25599c74ebb5df..090eced28cf733 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest173/Generated173.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest173/Generated173.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest174/Generated174.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest174/Generated174.ilproj index fbf2d5c3aef9c5..5ce224e8f6f4f1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest174/Generated174.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest174/Generated174.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest175/Generated175.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest175/Generated175.ilproj index 6dfef7b4fd3bc0..00871019b819c4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest175/Generated175.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest175/Generated175.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest176/Generated176.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest176/Generated176.ilproj index bd8c8164d621ab..d82eb6ed242706 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest176/Generated176.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest176/Generated176.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest177/Generated177.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest177/Generated177.ilproj index 115d797b5085b8..4a4f7aac15248d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest177/Generated177.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest177/Generated177.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest178/Generated178.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest178/Generated178.ilproj index efcf03024c8f65..7e372efa893b4f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest178/Generated178.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest178/Generated178.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest179/Generated179.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest179/Generated179.ilproj index 98954af39367b1..966a0d2464908a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest179/Generated179.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest179/Generated179.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest18/Generated18.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest18/Generated18.ilproj index becba85e26018d..35f83dc7d33ec0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest18/Generated18.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest18/Generated18.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest180/Generated180.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest180/Generated180.ilproj index 3698159bee1f2d..9aeb4a439f9ae6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest180/Generated180.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest180/Generated180.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest181/Generated181.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest181/Generated181.ilproj index e507dc034e9108..2df4c507b9de69 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest181/Generated181.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest181/Generated181.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest182/Generated182.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest182/Generated182.ilproj index 9b74ab21a06daa..ce5f30fbd15c54 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest182/Generated182.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest182/Generated182.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest183/Generated183.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest183/Generated183.ilproj index d5beba5b865482..36ba9f6850a61a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest183/Generated183.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest183/Generated183.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest184/Generated184.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest184/Generated184.ilproj index 1488a3d0f7ccbd..6b8235b4e14bc3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest184/Generated184.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest184/Generated184.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest185/Generated185.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest185/Generated185.ilproj index d521fc84d4916c..ac06f787359cc9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest185/Generated185.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest185/Generated185.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest186/Generated186.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest186/Generated186.ilproj index bb969a2208f71d..75a17d384d08d2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest186/Generated186.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest186/Generated186.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest187/Generated187.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest187/Generated187.ilproj index 90ec4e9f78dbeb..c6bf94792bb309 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest187/Generated187.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest187/Generated187.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest188/Generated188.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest188/Generated188.ilproj index 4e4f93cd9d227e..406015779cdf61 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest188/Generated188.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest188/Generated188.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest189/Generated189.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest189/Generated189.ilproj index b1888335b7b586..c67aff98934641 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest189/Generated189.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest189/Generated189.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest19/Generated19.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest19/Generated19.ilproj index 2d90438d2a704d..e0fcb0e7d87ebe 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest19/Generated19.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest19/Generated19.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest190/Generated190.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest190/Generated190.ilproj index 543f90a6c4e2f9..746c1e0189d8bb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest190/Generated190.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest190/Generated190.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest191/Generated191.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest191/Generated191.ilproj index 637889eaf9c372..865e0d50b3d5b5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest191/Generated191.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest191/Generated191.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest192/Generated192.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest192/Generated192.ilproj index 4fa2898f35b2ba..23ccff1a26baf2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest192/Generated192.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest192/Generated192.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest193/Generated193.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest193/Generated193.ilproj index 4c482285da6fd6..1eaa5330d691c0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest193/Generated193.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest193/Generated193.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest194/Generated194.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest194/Generated194.ilproj index 8531ce8fad1900..db343dc15ef25e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest194/Generated194.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest194/Generated194.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest195/Generated195.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest195/Generated195.ilproj index 2b7a1ebf750e75..6b34ebc3321d37 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest195/Generated195.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest195/Generated195.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest196/Generated196.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest196/Generated196.ilproj index a5788602becdb9..ea766c936893f2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest196/Generated196.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest196/Generated196.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest197/Generated197.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest197/Generated197.ilproj index 4261678951ed84..9288745391bd2b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest197/Generated197.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest197/Generated197.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest198/Generated198.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest198/Generated198.ilproj index ce2a9154b2af88..96de101c391608 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest198/Generated198.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest198/Generated198.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest199/Generated199.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest199/Generated199.ilproj index f26471fa632bd0..3ad9e928ec87b5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest199/Generated199.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest199/Generated199.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest2/Generated2.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest2/Generated2.ilproj index fa7e8c093af97e..9bcaa548a670dd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest2/Generated2.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest2/Generated2.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest20/Generated20.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest20/Generated20.ilproj index ff68d09d553380..20d53fc2877a3f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest20/Generated20.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest20/Generated20.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest200/Generated200.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest200/Generated200.ilproj index ea63b6eb9a0320..bebd24162718f4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest200/Generated200.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest200/Generated200.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest201/Generated201.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest201/Generated201.ilproj index c883e48b285c8e..26eeece4676847 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest201/Generated201.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest201/Generated201.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest202/Generated202.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest202/Generated202.ilproj index 36ae4b78bc41d2..c766a6a11d57d6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest202/Generated202.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest202/Generated202.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest203/Generated203.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest203/Generated203.ilproj index 2f7a3921935550..2a68f618f03c6e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest203/Generated203.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest203/Generated203.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest204/Generated204.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest204/Generated204.ilproj index bed23624f1be7b..056f6ff39a4e32 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest204/Generated204.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest204/Generated204.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest205/Generated205.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest205/Generated205.ilproj index 9f403ea32202e7..a8f9daf8200f40 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest205/Generated205.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest205/Generated205.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest206/Generated206.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest206/Generated206.ilproj index a4912c91bcc4eb..0bfb78abf9dca2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest206/Generated206.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest206/Generated206.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest207/Generated207.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest207/Generated207.ilproj index feecbf2b397203..ddb4e370e2adbc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest207/Generated207.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest207/Generated207.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest208/Generated208.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest208/Generated208.ilproj index 752039e2868300..8a8e0d57e50ee7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest208/Generated208.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest208/Generated208.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest209/Generated209.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest209/Generated209.ilproj index 446172a37e603f..180a2e259956b1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest209/Generated209.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest209/Generated209.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest21/Generated21.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest21/Generated21.ilproj index ef8a9b8f6fa5a0..7580c52a0692fa 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest21/Generated21.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest21/Generated21.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest210/Generated210.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest210/Generated210.ilproj index 504bcbf2a330d5..ccd6b28bf2fc79 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest210/Generated210.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest210/Generated210.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest211/Generated211.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest211/Generated211.ilproj index 41efa4a168e95d..6ac7ceaf7d2f97 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest211/Generated211.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest211/Generated211.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest212/Generated212.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest212/Generated212.ilproj index 0038e45de94221..5137bfdd04f567 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest212/Generated212.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest212/Generated212.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest213/Generated213.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest213/Generated213.ilproj index 1f7a8d4ef7486d..c5eb8945a0735b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest213/Generated213.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest213/Generated213.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest214/Generated214.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest214/Generated214.ilproj index 08cc768dcefd5f..b95be10c76ae17 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest214/Generated214.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest214/Generated214.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest215/Generated215.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest215/Generated215.ilproj index c49fc0a5b0e70c..4bfc5aa7e70c0e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest215/Generated215.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest215/Generated215.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest216/Generated216.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest216/Generated216.ilproj index 65958ab65d9767..002172b8365b55 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest216/Generated216.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest216/Generated216.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest217/Generated217.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest217/Generated217.ilproj index 332c816a2b302a..29bea49692d707 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest217/Generated217.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest217/Generated217.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest218/Generated218.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest218/Generated218.ilproj index e3c16d831d1b4f..fe13b677b4abf0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest218/Generated218.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest218/Generated218.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest219/Generated219.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest219/Generated219.ilproj index 2fa03fb3f9bc79..b2654730fa0008 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest219/Generated219.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest219/Generated219.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest22/Generated22.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest22/Generated22.ilproj index 92bd6292db8154..00a756480d2359 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest22/Generated22.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest22/Generated22.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest220/Generated220.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest220/Generated220.ilproj index 36d7ea490a5239..07c20b07ce60c6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest220/Generated220.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest220/Generated220.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest221/Generated221.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest221/Generated221.ilproj index e79251678d59dc..0daa26eff4acba 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest221/Generated221.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest221/Generated221.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest222/Generated222.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest222/Generated222.ilproj index ff314ca226b998..55515d77467b85 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest222/Generated222.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest222/Generated222.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest223/Generated223.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest223/Generated223.ilproj index 4a3d843c53d8fc..3ba9d74f113eb6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest223/Generated223.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest223/Generated223.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest224/Generated224.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest224/Generated224.ilproj index f3063d80da648c..f1d69c5915bbab 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest224/Generated224.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest224/Generated224.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest225/Generated225.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest225/Generated225.ilproj index 13688c7d056c04..50eab85c8a180f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest225/Generated225.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest225/Generated225.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest226/Generated226.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest226/Generated226.ilproj index f4228a97c04635..9a19fe5f33b19f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest226/Generated226.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest226/Generated226.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest227/Generated227.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest227/Generated227.ilproj index bb94ccde23e736..c59b4b4fd3971e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest227/Generated227.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest227/Generated227.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest228/Generated228.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest228/Generated228.ilproj index f0bff2668bd75b..057abd9c1a4734 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest228/Generated228.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest228/Generated228.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest229/Generated229.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest229/Generated229.ilproj index 57b7ace41b98bc..f598304c50cfaf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest229/Generated229.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest229/Generated229.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest23/Generated23.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest23/Generated23.ilproj index 09a3bd13285822..3e4e46235bb839 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest23/Generated23.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest23/Generated23.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest230/Generated230.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest230/Generated230.ilproj index 6b082150e1c39f..b80e724fee8581 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest230/Generated230.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest230/Generated230.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest231/Generated231.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest231/Generated231.ilproj index a5e0431ba7dc81..f77e42eeaee7b4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest231/Generated231.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest231/Generated231.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest232/Generated232.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest232/Generated232.ilproj index 068351ba543333..eb49bf488fd523 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest232/Generated232.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest232/Generated232.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest233/Generated233.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest233/Generated233.ilproj index 5bacb3b94d88f0..11f3a976c94472 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest233/Generated233.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest233/Generated233.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest234/Generated234.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest234/Generated234.ilproj index 44d105168f6c9f..0b01a5f2e90358 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest234/Generated234.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest234/Generated234.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest235/Generated235.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest235/Generated235.ilproj index 4a3c35c0890ab5..530227e218fe2d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest235/Generated235.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest235/Generated235.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest236/Generated236.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest236/Generated236.ilproj index ee59296c3392a5..bcceee7265e383 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest236/Generated236.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest236/Generated236.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest237/Generated237.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest237/Generated237.ilproj index ff70d5dcae8f84..40420ec781fdf0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest237/Generated237.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest237/Generated237.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest238/Generated238.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest238/Generated238.ilproj index 4af53e27fdfd76..015a0c486ffda6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest238/Generated238.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest238/Generated238.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest239/Generated239.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest239/Generated239.ilproj index 7a33f78677dd65..a6647d55facdf9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest239/Generated239.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest239/Generated239.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest24/Generated24.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest24/Generated24.ilproj index 4e9e7d5ef730be..ff030c8d8fe484 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest24/Generated24.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest24/Generated24.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest240/Generated240.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest240/Generated240.ilproj index 37a6dcd233556f..de2709bf17bfed 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest240/Generated240.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest240/Generated240.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest241/Generated241.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest241/Generated241.ilproj index 4cdd711fd743c6..6424b30fddeecc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest241/Generated241.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest241/Generated241.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest242/Generated242.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest242/Generated242.ilproj index 3a1c75fb5d5c64..cf4952fc85e8af 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest242/Generated242.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest242/Generated242.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest243/Generated243.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest243/Generated243.ilproj index 27a06cb0b9cd68..2591c6a266f829 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest243/Generated243.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest243/Generated243.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest244/Generated244.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest244/Generated244.ilproj index 1c6917ae560d4a..cdc397bcb3e331 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest244/Generated244.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest244/Generated244.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest245/Generated245.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest245/Generated245.ilproj index 4c46e84a5ac580..1a2897ce0867f5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest245/Generated245.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest245/Generated245.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest246/Generated246.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest246/Generated246.ilproj index fe2fe9dd43043c..58af5d6e85a0cc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest246/Generated246.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest246/Generated246.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest247/Generated247.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest247/Generated247.ilproj index 0671cea51b2095..1a3924a8d04fc9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest247/Generated247.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest247/Generated247.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest248/Generated248.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest248/Generated248.ilproj index 41790955044a13..4a95568263719f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest248/Generated248.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest248/Generated248.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest249/Generated249.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest249/Generated249.ilproj index 3e881d05ae6222..8998b2a8a393b2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest249/Generated249.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest249/Generated249.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest25/Generated25.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest25/Generated25.ilproj index dbe094d0ba5846..6fab3cb6c83e0d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest25/Generated25.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest25/Generated25.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest250/Generated250.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest250/Generated250.ilproj index 54ac0032365cca..3d8b0593d88c52 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest250/Generated250.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest250/Generated250.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest251/Generated251.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest251/Generated251.ilproj index c5acea77b1a73e..912e817ccbe60e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest251/Generated251.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest251/Generated251.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest252/Generated252.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest252/Generated252.ilproj index 1c202de305b860..27cb59b615822f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest252/Generated252.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest252/Generated252.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest253/Generated253.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest253/Generated253.ilproj index 2f4de09012d7e8..f490d8990781ca 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest253/Generated253.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest253/Generated253.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest254/Generated254.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest254/Generated254.ilproj index 9aa5743929064f..511aebc3bd2a44 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest254/Generated254.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest254/Generated254.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest255/Generated255.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest255/Generated255.ilproj index f24831ce07ab8a..158a454fb65729 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest255/Generated255.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest255/Generated255.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest256/Generated256.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest256/Generated256.ilproj index efe0e4f9a62f1c..41d4c8c0f9115e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest256/Generated256.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest256/Generated256.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest257/Generated257.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest257/Generated257.ilproj index 81aff875987886..12ae3c9e445fe4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest257/Generated257.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest257/Generated257.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest258/Generated258.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest258/Generated258.ilproj index 05cb0b3d9ab088..2f0171ddccf611 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest258/Generated258.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest258/Generated258.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest259/Generated259.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest259/Generated259.ilproj index 693fb2fd16f19d..7fe927a0210a52 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest259/Generated259.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest259/Generated259.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest26/Generated26.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest26/Generated26.ilproj index c4f15bc94cb7c4..e76dc4b16d44d5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest26/Generated26.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest26/Generated26.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest260/Generated260.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest260/Generated260.ilproj index 1725cd9a25a6f3..7a4f34f90ecab2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest260/Generated260.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest260/Generated260.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest261/Generated261.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest261/Generated261.ilproj index e1931949692a4b..4366a9a017206b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest261/Generated261.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest261/Generated261.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest262/Generated262.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest262/Generated262.ilproj index 35fc9050c589c4..881ebb81458904 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest262/Generated262.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest262/Generated262.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest263/Generated263.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest263/Generated263.ilproj index 3d50396cb55eeb..f91d81bb2d5e78 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest263/Generated263.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest263/Generated263.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest264/Generated264.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest264/Generated264.ilproj index 92c0fb25b45b8c..325d8cf00234a8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest264/Generated264.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest264/Generated264.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest265/Generated265.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest265/Generated265.ilproj index e54e5d64afbb02..dac0251342c3b6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest265/Generated265.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest265/Generated265.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest266/Generated266.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest266/Generated266.ilproj index b6862f1d129151..3c4f00cbbba90c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest266/Generated266.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest266/Generated266.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest267/Generated267.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest267/Generated267.ilproj index ca12892ea3fe9c..c51e440c22bc35 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest267/Generated267.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest267/Generated267.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest268/Generated268.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest268/Generated268.ilproj index 900bb1ba1b96a0..41d281fed07666 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest268/Generated268.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest268/Generated268.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest269/Generated269.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest269/Generated269.ilproj index 8629b24119e580..97484c401a4b89 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest269/Generated269.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest269/Generated269.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest27/Generated27.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest27/Generated27.ilproj index dd6c33e07d80c1..7681e88fd498db 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest27/Generated27.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest27/Generated27.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest270/Generated270.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest270/Generated270.ilproj index 560df620c01485..0754462e2f48f1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest270/Generated270.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest270/Generated270.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest271/Generated271.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest271/Generated271.ilproj index 1a7b8668585059..59ebcc350457b5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest271/Generated271.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest271/Generated271.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest272/Generated272.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest272/Generated272.ilproj index e70e90ee9ca580..44f9de75631eb5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest272/Generated272.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest272/Generated272.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest273/Generated273.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest273/Generated273.ilproj index 43a18a1d4fc1fb..e1702f95d95819 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest273/Generated273.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest273/Generated273.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest274/Generated274.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest274/Generated274.ilproj index 821aa7eebd605e..15167f226fdbca 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest274/Generated274.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest274/Generated274.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest275/Generated275.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest275/Generated275.ilproj index 2d6a8acb92cbef..7deef790273c34 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest275/Generated275.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest275/Generated275.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest276/Generated276.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest276/Generated276.ilproj index 174e67c829387a..16c82c851ace25 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest276/Generated276.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest276/Generated276.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest277/Generated277.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest277/Generated277.ilproj index ca21d580830daa..3db4ff1bc4257c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest277/Generated277.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest277/Generated277.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest278/Generated278.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest278/Generated278.ilproj index 9fc092b569dd3a..eb5a116d45d532 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest278/Generated278.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest278/Generated278.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest279/Generated279.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest279/Generated279.ilproj index 75e5d0681875bf..c873192ea9515b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest279/Generated279.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest279/Generated279.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest28/Generated28.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest28/Generated28.ilproj index 7b06c1d951c6df..86aadb001128be 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest28/Generated28.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest28/Generated28.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest280/Generated280.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest280/Generated280.ilproj index 8aadf98d3b638b..ac25be237f9edf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest280/Generated280.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest280/Generated280.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest281/Generated281.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest281/Generated281.ilproj index 604f8744e6e250..b8faf1f7b827ce 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest281/Generated281.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest281/Generated281.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest282/Generated282.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest282/Generated282.ilproj index 0fc3a4f8b2ae07..01999b06f0429f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest282/Generated282.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest282/Generated282.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest283/Generated283.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest283/Generated283.ilproj index 12596c9bfbc4da..76b7e61d4af220 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest283/Generated283.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest283/Generated283.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest284/Generated284.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest284/Generated284.ilproj index 705f1dbacce576..c3d6e3139e67b0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest284/Generated284.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest284/Generated284.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest285/Generated285.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest285/Generated285.ilproj index 6d484e857e3ef2..7d75dcd3c89525 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest285/Generated285.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest285/Generated285.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest286/Generated286.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest286/Generated286.ilproj index 76172aec0ce253..3df6bef4a832bc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest286/Generated286.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest286/Generated286.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest287/Generated287.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest287/Generated287.ilproj index 78b84a7de1c842..15588dde90b9e3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest287/Generated287.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest287/Generated287.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest288/Generated288.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest288/Generated288.ilproj index 525c5c6f2d53b0..0afb29270b92af 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest288/Generated288.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest288/Generated288.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest289/Generated289.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest289/Generated289.ilproj index 183d2d5138cb88..c48c1bd4cd7d54 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest289/Generated289.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest289/Generated289.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest29/Generated29.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest29/Generated29.ilproj index 6ec1bcce4a1917..cffa2ead146b67 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest29/Generated29.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest29/Generated29.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest290/Generated290.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest290/Generated290.ilproj index 32a05e954e90f4..9b8168aa1a7e4c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest290/Generated290.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest290/Generated290.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest291/Generated291.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest291/Generated291.ilproj index 58488c13174030..fe0a3cd3075924 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest291/Generated291.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest291/Generated291.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest292/Generated292.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest292/Generated292.ilproj index 4a088ff6068df1..53e044bad478e8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest292/Generated292.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest292/Generated292.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest293/Generated293.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest293/Generated293.ilproj index 155458d90b9780..07b7a2a65df693 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest293/Generated293.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest293/Generated293.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest294/Generated294.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest294/Generated294.ilproj index fa641f9bab4ed5..16ac18b0a32731 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest294/Generated294.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest294/Generated294.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest295/Generated295.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest295/Generated295.ilproj index f42c0df15ef473..a247f7b3fc5351 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest295/Generated295.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest295/Generated295.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest296/Generated296.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest296/Generated296.ilproj index ac964d7f065b46..b7712b7fa84a52 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest296/Generated296.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest296/Generated296.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest297/Generated297.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest297/Generated297.ilproj index bc3d0fabb4f87f..ba559c1598f30e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest297/Generated297.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest297/Generated297.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest298/Generated298.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest298/Generated298.ilproj index c0d81338f6d273..c0211ccd17e8e2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest298/Generated298.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest298/Generated298.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest299/Generated299.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest299/Generated299.ilproj index 82cebb9e76e33f..118056a237e807 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest299/Generated299.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest299/Generated299.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest3/Generated3.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest3/Generated3.ilproj index 84b86237ccfe4a..1b414f5b34510f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest3/Generated3.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest3/Generated3.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest30/Generated30.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest30/Generated30.ilproj index dc044505b7dc22..15ddf23703dd4a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest30/Generated30.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest30/Generated30.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest300/Generated300.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest300/Generated300.ilproj index 8693e8743ee087..e96e1fca145b7e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest300/Generated300.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest300/Generated300.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest301/Generated301.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest301/Generated301.ilproj index 02337850c485d5..d79467ecd89b95 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest301/Generated301.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest301/Generated301.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest302/Generated302.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest302/Generated302.ilproj index f24630489d3ddd..e1a261474c1e87 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest302/Generated302.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest302/Generated302.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest303/Generated303.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest303/Generated303.ilproj index 19996141e0d6bf..e174fade066992 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest303/Generated303.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest303/Generated303.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest304/Generated304.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest304/Generated304.ilproj index b4d8d94a621d04..5d3c43651a8432 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest304/Generated304.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest304/Generated304.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest305/Generated305.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest305/Generated305.ilproj index 2860b369cfcd34..82706dce5b0d23 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest305/Generated305.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest305/Generated305.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest306/Generated306.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest306/Generated306.ilproj index 581bd4ad43ee94..36af8b52be8c97 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest306/Generated306.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest306/Generated306.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest307/Generated307.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest307/Generated307.ilproj index 31ce9d7eef1252..2b03eb892f091d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest307/Generated307.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest307/Generated307.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest308/Generated308.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest308/Generated308.ilproj index 28db8c582830d8..0f96aee168bc9a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest308/Generated308.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest308/Generated308.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest309/Generated309.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest309/Generated309.ilproj index 83282a3a8169ec..b6388bc7c99a16 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest309/Generated309.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest309/Generated309.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest31/Generated31.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest31/Generated31.ilproj index 1a11fb0ca80685..e62c3e150c0e16 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest31/Generated31.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest31/Generated31.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest310/Generated310.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest310/Generated310.ilproj index e8ea3e6590df1a..9f0aa45fa235e5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest310/Generated310.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest310/Generated310.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest311/Generated311.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest311/Generated311.ilproj index d95a5bb6e3ccec..0bdeefccfb956f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest311/Generated311.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest311/Generated311.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest312/Generated312.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest312/Generated312.ilproj index cf24fa7f882161..b63e4a812ea1f1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest312/Generated312.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest312/Generated312.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest313/Generated313.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest313/Generated313.ilproj index 806b10ce09d83b..da5f14c9cac234 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest313/Generated313.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest313/Generated313.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest314/Generated314.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest314/Generated314.ilproj index e4b78bd8c4f3c6..446ea24c20088d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest314/Generated314.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest314/Generated314.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest315/Generated315.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest315/Generated315.ilproj index 8a07025b4af3b5..382048a55a6fd6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest315/Generated315.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest315/Generated315.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest316/Generated316.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest316/Generated316.ilproj index 3c48b973eb3799..39642aa66f37d0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest316/Generated316.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest316/Generated316.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest317/Generated317.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest317/Generated317.ilproj index 79d0f1ddcb6856..a46f96e94ba985 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest317/Generated317.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest317/Generated317.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest318/Generated318.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest318/Generated318.ilproj index ff1cdd2f5e500e..778973fedd59f9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest318/Generated318.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest318/Generated318.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest319/Generated319.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest319/Generated319.ilproj index c5d280934713c6..dbbc55bd21d4c8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest319/Generated319.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest319/Generated319.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest32/Generated32.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest32/Generated32.ilproj index 7785bbe936c2b3..a72a12f1590611 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest32/Generated32.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest32/Generated32.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest320/Generated320.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest320/Generated320.ilproj index 7c2ac17db935e3..ca822cd2921859 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest320/Generated320.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest320/Generated320.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest321/Generated321.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest321/Generated321.ilproj index fb462ec5f5cafe..332c7c49453e81 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest321/Generated321.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest321/Generated321.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest322/Generated322.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest322/Generated322.ilproj index 7375defcaa32aa..cae20cb9165bfb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest322/Generated322.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest322/Generated322.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest323/Generated323.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest323/Generated323.ilproj index 49a7c02b3cb08f..4a2c77b19a6949 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest323/Generated323.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest323/Generated323.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest324/Generated324.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest324/Generated324.ilproj index dcb362fd16508f..81cb855d96fd30 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest324/Generated324.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest324/Generated324.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest325/Generated325.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest325/Generated325.ilproj index 5372b1ffe67aff..c4828f343e0518 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest325/Generated325.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest325/Generated325.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest326/Generated326.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest326/Generated326.ilproj index aec822357feb45..d562589a637da5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest326/Generated326.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest326/Generated326.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest327/Generated327.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest327/Generated327.ilproj index 256b33e25e0173..202c7cf1c463f6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest327/Generated327.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest327/Generated327.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest328/Generated328.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest328/Generated328.ilproj index f645b21c3f796a..41ccc16d7211a9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest328/Generated328.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest328/Generated328.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest329/Generated329.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest329/Generated329.ilproj index 1aa23ac2a9538e..55f5b202d2b66c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest329/Generated329.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest329/Generated329.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest33/Generated33.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest33/Generated33.ilproj index 6afe66028aa502..04cade4ba4b5c8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest33/Generated33.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest33/Generated33.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest330/Generated330.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest330/Generated330.ilproj index 07bcdef13f6abd..16e294299997b9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest330/Generated330.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest330/Generated330.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest331/Generated331.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest331/Generated331.ilproj index 143a8b5315e539..a0f246b9265ecd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest331/Generated331.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest331/Generated331.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest332/Generated332.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest332/Generated332.ilproj index 496cc5b35ff0ce..6c25f5a547b0ab 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest332/Generated332.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest332/Generated332.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest333/Generated333.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest333/Generated333.ilproj index fd0bfca7bcec32..04b3127267840c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest333/Generated333.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest333/Generated333.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest334/Generated334.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest334/Generated334.ilproj index de2fe61109704a..07eaa1bf295315 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest334/Generated334.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest334/Generated334.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest335/Generated335.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest335/Generated335.ilproj index 31394deab05a51..4da099050d4f7c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest335/Generated335.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest335/Generated335.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest336/Generated336.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest336/Generated336.ilproj index c7e68fdeb503d4..46f6e3ca1ad52b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest336/Generated336.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest336/Generated336.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest337/Generated337.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest337/Generated337.ilproj index a2313942a257c3..8c3c1f25517462 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest337/Generated337.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest337/Generated337.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest338/Generated338.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest338/Generated338.ilproj index a614a1e7925fd7..48d3a50a9fbb72 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest338/Generated338.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest338/Generated338.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest339/Generated339.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest339/Generated339.ilproj index fb9909d7cc725d..669da0844158d0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest339/Generated339.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest339/Generated339.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest34/Generated34.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest34/Generated34.ilproj index a4207bc1cd29ea..a373c1e870d493 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest34/Generated34.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest34/Generated34.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest340/Generated340.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest340/Generated340.ilproj index d1a6f601e4cd75..eb5f7f017f3cd3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest340/Generated340.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest340/Generated340.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest341/Generated341.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest341/Generated341.ilproj index 013b32ce1b6d04..39509834235e43 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest341/Generated341.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest341/Generated341.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest342/Generated342.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest342/Generated342.ilproj index d34220182905aa..0ae6563ad0246d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest342/Generated342.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest342/Generated342.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest343/Generated343.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest343/Generated343.ilproj index 36f4a35595c673..a843dff3e8fc95 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest343/Generated343.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest343/Generated343.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest344/Generated344.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest344/Generated344.ilproj index 85aac0d2d64f3c..0f4b3df63c0dc2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest344/Generated344.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest344/Generated344.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest345/Generated345.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest345/Generated345.ilproj index 29f76130a9ff9b..3b97ebe507db81 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest345/Generated345.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest345/Generated345.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest346/Generated346.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest346/Generated346.ilproj index a313498ddc6bdb..4204c1eb70aed7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest346/Generated346.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest346/Generated346.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest347/Generated347.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest347/Generated347.ilproj index 96a40bde1e8120..e79806b0cebe8b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest347/Generated347.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest347/Generated347.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest348/Generated348.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest348/Generated348.ilproj index 81d5e7bd138be2..e02334065712fd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest348/Generated348.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest348/Generated348.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest349/Generated349.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest349/Generated349.ilproj index 6f83a3ddd1d138..d76fb1872dc7b9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest349/Generated349.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest349/Generated349.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest35/Generated35.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest35/Generated35.ilproj index d90eabd43373f1..ba09d3b0b548fb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest35/Generated35.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest35/Generated35.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest350/Generated350.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest350/Generated350.ilproj index 285f9177118f73..851d2eb62dce63 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest350/Generated350.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest350/Generated350.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest351/Generated351.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest351/Generated351.ilproj index a5028dcbace074..3c12fb900645b7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest351/Generated351.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest351/Generated351.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest352/Generated352.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest352/Generated352.ilproj index b6f0f60819bc34..2faa7d6e395254 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest352/Generated352.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest352/Generated352.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest353/Generated353.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest353/Generated353.ilproj index 0595985fd22d46..89286b106892b2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest353/Generated353.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest353/Generated353.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest354/Generated354.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest354/Generated354.ilproj index 81dd5aa9f08c0d..66f0d3990e53f8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest354/Generated354.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest354/Generated354.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest355/Generated355.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest355/Generated355.ilproj index 366d8565b49d41..d68f038ddd51d3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest355/Generated355.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest355/Generated355.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest356/Generated356.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest356/Generated356.ilproj index 1159fb48834a7d..00c1a2526f1610 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest356/Generated356.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest356/Generated356.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest357/Generated357.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest357/Generated357.ilproj index 1f5b309ba20a87..f97b7b771058a0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest357/Generated357.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest357/Generated357.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest358/Generated358.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest358/Generated358.ilproj index a4e0c54226e011..76a2f4e2888ff1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest358/Generated358.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest358/Generated358.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest359/Generated359.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest359/Generated359.ilproj index 1a2aca50731a61..c623d3c99cc47a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest359/Generated359.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest359/Generated359.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest36/Generated36.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest36/Generated36.ilproj index bb02da31d8dfca..7520d02392885a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest36/Generated36.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest36/Generated36.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest360/Generated360.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest360/Generated360.ilproj index 28ee06a74e67f1..d9b387bc93c6d4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest360/Generated360.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest360/Generated360.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest361/Generated361.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest361/Generated361.ilproj index 0994ea469d44ec..c1f109f1117355 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest361/Generated361.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest361/Generated361.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest362/Generated362.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest362/Generated362.ilproj index 1ae201b6ea9fdc..21e3c5323c1670 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest362/Generated362.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest362/Generated362.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest363/Generated363.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest363/Generated363.ilproj index 33d436d3d23693..a060a7f2bb130b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest363/Generated363.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest363/Generated363.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest364/Generated364.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest364/Generated364.ilproj index ffc246d11e9714..626e55831a56d0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest364/Generated364.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest364/Generated364.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest365/Generated365.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest365/Generated365.ilproj index 276c11ad21dbd2..78633ae4938c21 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest365/Generated365.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest365/Generated365.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest366/Generated366.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest366/Generated366.ilproj index a87a9be511c3fd..4a3c17102b6a7c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest366/Generated366.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest366/Generated366.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest367/Generated367.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest367/Generated367.ilproj index 78c8602ce1a9c3..0599a5ba3672e1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest367/Generated367.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest367/Generated367.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest368/Generated368.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest368/Generated368.ilproj index 5e8c4ffba5174d..a2351453baba18 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest368/Generated368.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest368/Generated368.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest369/Generated369.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest369/Generated369.ilproj index 0940849c55e80f..574a097630690d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest369/Generated369.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest369/Generated369.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest37/Generated37.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest37/Generated37.ilproj index f6416bc6a54c29..1fa7a6f3233cbd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest37/Generated37.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest37/Generated37.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest370/Generated370.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest370/Generated370.ilproj index a145edd38c7f59..0cdc9dc5e9228d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest370/Generated370.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest370/Generated370.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest371/Generated371.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest371/Generated371.ilproj index 47d2190a53c3c5..015d1ae4a78ce8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest371/Generated371.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest371/Generated371.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest372/Generated372.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest372/Generated372.ilproj index 52de3334f4239c..3f1e332fd2cdea 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest372/Generated372.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest372/Generated372.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest373/Generated373.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest373/Generated373.ilproj index 29159f67f8a21a..27f9395d28be80 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest373/Generated373.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest373/Generated373.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest374/Generated374.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest374/Generated374.ilproj index f47b1e9efcdd27..e5616fc8319e0f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest374/Generated374.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest374/Generated374.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest375/Generated375.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest375/Generated375.ilproj index 2515616392298c..44b0cfe21095f3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest375/Generated375.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest375/Generated375.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest376/Generated376.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest376/Generated376.ilproj index 97735ba42c1d6a..c8813ed351907c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest376/Generated376.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest376/Generated376.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest377/Generated377.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest377/Generated377.ilproj index d95ecc87829e90..d85613e57e88b1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest377/Generated377.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest377/Generated377.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest378/Generated378.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest378/Generated378.ilproj index 5b3af1caba8c38..dc6650cb9f752b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest378/Generated378.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest378/Generated378.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest379/Generated379.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest379/Generated379.ilproj index 0464cfc77ae1e4..bdf87ae07d11f7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest379/Generated379.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest379/Generated379.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest38/Generated38.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest38/Generated38.ilproj index 0bce535fbdcb28..e4a4ea5d8d13e0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest38/Generated38.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest38/Generated38.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest380/Generated380.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest380/Generated380.ilproj index e4b6a7529d01cf..365c17b2c6a2f6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest380/Generated380.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest380/Generated380.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest381/Generated381.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest381/Generated381.ilproj index a2400517e0c6b1..d56fb39429d33b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest381/Generated381.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest381/Generated381.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest382/Generated382.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest382/Generated382.ilproj index 5996c694bd09c1..972de2e7beabb1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest382/Generated382.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest382/Generated382.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest383/Generated383.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest383/Generated383.ilproj index 39debc4b58d098..01605475e5443b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest383/Generated383.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest383/Generated383.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest384/Generated384.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest384/Generated384.ilproj index 3eea30b52f03ba..f470807d35ae5a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest384/Generated384.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest384/Generated384.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest385/Generated385.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest385/Generated385.ilproj index 500f78d21c5d5e..a0c25cfc30814e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest385/Generated385.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest385/Generated385.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest386/Generated386.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest386/Generated386.ilproj index 136ed3936e0481..e624f6877b5ae7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest386/Generated386.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest386/Generated386.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest387/Generated387.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest387/Generated387.ilproj index be81076c915536..3b7f47dcf1e9f4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest387/Generated387.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest387/Generated387.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest388/Generated388.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest388/Generated388.ilproj index 1c58a62ee410a0..433d197e997aa3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest388/Generated388.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest388/Generated388.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest389/Generated389.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest389/Generated389.ilproj index 6508b8d156ad6a..a883f53b255674 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest389/Generated389.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest389/Generated389.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest39/Generated39.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest39/Generated39.ilproj index 0e21c554b95874..16d9ff5804b510 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest39/Generated39.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest39/Generated39.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest390/Generated390.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest390/Generated390.ilproj index 7d4034aa155497..743294d43b7f98 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest390/Generated390.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest390/Generated390.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest391/Generated391.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest391/Generated391.ilproj index 095b3bd020589a..cf5b772c6fbcc4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest391/Generated391.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest391/Generated391.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest392/Generated392.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest392/Generated392.ilproj index dabb500a805146..55234a5e1596d5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest392/Generated392.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest392/Generated392.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest393/Generated393.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest393/Generated393.ilproj index b1980486b1ce23..f246661fbba162 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest393/Generated393.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest393/Generated393.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest394/Generated394.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest394/Generated394.ilproj index 3941d9f3f213c6..e3b5a76fa64d54 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest394/Generated394.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest394/Generated394.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest395/Generated395.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest395/Generated395.ilproj index a0376ee62a0ffd..e081e5e34f82db 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest395/Generated395.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest395/Generated395.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest396/Generated396.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest396/Generated396.ilproj index 2b1b762d3eacdc..23108db282f27c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest396/Generated396.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest396/Generated396.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest397/Generated397.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest397/Generated397.ilproj index ae90551bd0d483..cee17e7514edcc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest397/Generated397.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest397/Generated397.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest398/Generated398.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest398/Generated398.ilproj index 06d0170f063875..374e05f6d41b46 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest398/Generated398.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest398/Generated398.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest399/Generated399.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest399/Generated399.ilproj index 4214ff0e06bd02..a79c3287ee2cf8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest399/Generated399.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest399/Generated399.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest4/Generated4.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest4/Generated4.ilproj index 3f1f7f718b3375..1d3d7d8101256f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest4/Generated4.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest4/Generated4.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest40/Generated40.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest40/Generated40.ilproj index 0823980865f83a..cc16ff2623ac02 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest40/Generated40.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest40/Generated40.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest400/Generated400.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest400/Generated400.ilproj index 6311f7d1d91f90..039787e4f4d986 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest400/Generated400.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest400/Generated400.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest401/Generated401.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest401/Generated401.ilproj index d74fbf45276dce..f66b84cb33d408 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest401/Generated401.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest401/Generated401.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest402/Generated402.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest402/Generated402.ilproj index a17517899dd22f..2a86f1af4e7846 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest402/Generated402.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest402/Generated402.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest403/Generated403.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest403/Generated403.ilproj index 2ac373823431b1..66bbb6bb4a30d7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest403/Generated403.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest403/Generated403.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest404/Generated404.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest404/Generated404.ilproj index ae8b346837bc0a..7cab2c3ea46b2c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest404/Generated404.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest404/Generated404.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest405/Generated405.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest405/Generated405.ilproj index da9a345eb57f54..dbacf389207f8d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest405/Generated405.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest405/Generated405.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest406/Generated406.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest406/Generated406.ilproj index 1375b6831c242b..91ebfe61103361 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest406/Generated406.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest406/Generated406.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest407/Generated407.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest407/Generated407.ilproj index 938d5fd04b0585..eef86baaa38884 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest407/Generated407.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest407/Generated407.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest408/Generated408.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest408/Generated408.ilproj index 0a2f491dfce270..3cdca412dd2dd4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest408/Generated408.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest408/Generated408.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest409/Generated409.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest409/Generated409.ilproj index 2ed0eec33ce312..55888f79156595 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest409/Generated409.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest409/Generated409.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest41/Generated41.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest41/Generated41.ilproj index 616c979da70dc5..771ec2aff17905 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest41/Generated41.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest41/Generated41.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest410/Generated410.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest410/Generated410.ilproj index 7fbe7b02a18675..51b84a0be58fda 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest410/Generated410.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest410/Generated410.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest411/Generated411.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest411/Generated411.ilproj index fda7d3b349eb66..a73842956bba62 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest411/Generated411.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest411/Generated411.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest412/Generated412.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest412/Generated412.ilproj index 38b6c71dbf37c0..1e122b05fa6c3a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest412/Generated412.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest412/Generated412.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest413/Generated413.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest413/Generated413.ilproj index 36851072bfc175..8a161d417ebf81 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest413/Generated413.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest413/Generated413.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest414/Generated414.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest414/Generated414.ilproj index 013a613bb89e6f..b6572f2031a248 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest414/Generated414.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest414/Generated414.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest415/Generated415.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest415/Generated415.ilproj index 6b06a11d869997..7d1f74525ecb54 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest415/Generated415.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest415/Generated415.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest416/Generated416.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest416/Generated416.ilproj index bd0cdfce962669..d2cb8b349f0186 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest416/Generated416.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest416/Generated416.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest417/Generated417.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest417/Generated417.ilproj index 89fb2d0382e76d..54c2b82387c467 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest417/Generated417.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest417/Generated417.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest418/Generated418.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest418/Generated418.ilproj index 1b15549b09d97b..2c564a12a8ac52 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest418/Generated418.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest418/Generated418.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest419/Generated419.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest419/Generated419.ilproj index 934adfe857e9eb..b3a67bc75fa0c0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest419/Generated419.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest419/Generated419.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest42/Generated42.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest42/Generated42.ilproj index a4600abb928983..a3c227efca1966 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest42/Generated42.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest42/Generated42.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest420/Generated420.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest420/Generated420.ilproj index 74312db29e2cc4..f4da16d4ce755c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest420/Generated420.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest420/Generated420.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest421/Generated421.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest421/Generated421.ilproj index 05a270b5d42b01..c215b1444d2353 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest421/Generated421.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest421/Generated421.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest422/Generated422.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest422/Generated422.ilproj index ad4dcb4e2b1c11..d0099e5fd7dc93 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest422/Generated422.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest422/Generated422.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest423/Generated423.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest423/Generated423.ilproj index de5a850a342046..fb176893357b59 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest423/Generated423.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest423/Generated423.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest424/Generated424.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest424/Generated424.ilproj index 7240714fa37f53..495a4428ad5d4c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest424/Generated424.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest424/Generated424.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest425/Generated425.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest425/Generated425.ilproj index 9408a8a9ba39b5..3000368018d859 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest425/Generated425.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest425/Generated425.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest426/Generated426.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest426/Generated426.ilproj index 5118ef7b65b6be..eecb37d824564c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest426/Generated426.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest426/Generated426.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest427/Generated427.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest427/Generated427.ilproj index 9b3688d61e2c53..af171bf6128b80 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest427/Generated427.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest427/Generated427.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest428/Generated428.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest428/Generated428.ilproj index 55dc45345449d3..3adad5424b256e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest428/Generated428.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest428/Generated428.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest429/Generated429.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest429/Generated429.ilproj index a395292a791ae4..2987b3e5c57d95 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest429/Generated429.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest429/Generated429.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest43/Generated43.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest43/Generated43.ilproj index b094b419f52d2a..39ca74eb9f7599 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest43/Generated43.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest43/Generated43.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest430/Generated430.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest430/Generated430.ilproj index 79a2621f08bce5..9c46d1514473d5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest430/Generated430.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest430/Generated430.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest431/Generated431.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest431/Generated431.ilproj index 1a2b43a12df1f5..8113fa9fd9da64 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest431/Generated431.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest431/Generated431.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest432/Generated432.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest432/Generated432.ilproj index c6556bd24d67a3..2c0f446dd86a43 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest432/Generated432.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest432/Generated432.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest433/Generated433.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest433/Generated433.ilproj index c39f0d64aaad68..ba1973ee94dc0a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest433/Generated433.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest433/Generated433.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest434/Generated434.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest434/Generated434.ilproj index 2082fb7d2bca86..c97f8f330fc454 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest434/Generated434.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest434/Generated434.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest435/Generated435.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest435/Generated435.ilproj index 69e4866cdd2239..e6ca65146d535b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest435/Generated435.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest435/Generated435.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest436/Generated436.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest436/Generated436.ilproj index 9adaba97a1a225..c0dc361b7ab07b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest436/Generated436.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest436/Generated436.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest437/Generated437.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest437/Generated437.ilproj index fa180372302d6c..b490455db875be 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest437/Generated437.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest437/Generated437.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest438/Generated438.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest438/Generated438.ilproj index f3e983499a979b..201279e7f3f538 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest438/Generated438.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest438/Generated438.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest439/Generated439.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest439/Generated439.ilproj index c0e1858bb2be71..00ca19043260bf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest439/Generated439.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest439/Generated439.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest44/Generated44.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest44/Generated44.ilproj index e7080670f82ee9..7d94646e8b11e3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest44/Generated44.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest44/Generated44.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest440/Generated440.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest440/Generated440.ilproj index 6e31869ccb3e31..32043f4072ebbb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest440/Generated440.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest440/Generated440.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest441/Generated441.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest441/Generated441.ilproj index 84e46e69905b97..04e83cdb65f7ce 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest441/Generated441.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest441/Generated441.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest442/Generated442.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest442/Generated442.ilproj index bfc4a7c1cf2320..1ade2aa6bd9889 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest442/Generated442.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest442/Generated442.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest443/Generated443.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest443/Generated443.ilproj index 44741f4ca540f9..ff29044b90ff08 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest443/Generated443.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest443/Generated443.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest444/Generated444.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest444/Generated444.ilproj index e7a771288240a7..5f6b74bed3cdd7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest444/Generated444.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest444/Generated444.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest445/Generated445.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest445/Generated445.ilproj index 847c1cdf336403..1c8dbf5278b84e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest445/Generated445.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest445/Generated445.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest446/Generated446.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest446/Generated446.ilproj index 036d107cf414b4..f253cfd1715d06 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest446/Generated446.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest446/Generated446.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest447/Generated447.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest447/Generated447.ilproj index 19b0c89f0ab783..906bfaeb0ef0db 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest447/Generated447.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest447/Generated447.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest448/Generated448.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest448/Generated448.ilproj index 83b6ec6253c34a..e4a3f1d35acf9f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest448/Generated448.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest448/Generated448.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest449/Generated449.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest449/Generated449.ilproj index 7b05b3cb333235..4c23be41cbbc28 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest449/Generated449.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest449/Generated449.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest45/Generated45.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest45/Generated45.ilproj index a6d29f52ecff8c..0b3a88ee05b785 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest45/Generated45.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest45/Generated45.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest450/Generated450.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest450/Generated450.ilproj index e5187461169445..7a055505d45fb4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest450/Generated450.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest450/Generated450.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest451/Generated451.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest451/Generated451.ilproj index 65d70512c66174..0f5b92de583888 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest451/Generated451.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest451/Generated451.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest452/Generated452.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest452/Generated452.ilproj index c159391782e2ea..9a65dac60ddc25 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest452/Generated452.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest452/Generated452.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest453/Generated453.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest453/Generated453.ilproj index 43add662f533d7..754a9879a22051 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest453/Generated453.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest453/Generated453.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest454/Generated454.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest454/Generated454.ilproj index a0dea2c4c20895..24b8c7bdc56a3d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest454/Generated454.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest454/Generated454.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest455/Generated455.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest455/Generated455.ilproj index 05bfee31c87251..c32987889fb315 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest455/Generated455.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest455/Generated455.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest456/Generated456.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest456/Generated456.ilproj index eae159433d91b6..118ff015ebe9bc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest456/Generated456.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest456/Generated456.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest457/Generated457.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest457/Generated457.ilproj index 38990ec097bab6..479bc1b9e35c8e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest457/Generated457.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest457/Generated457.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest458/Generated458.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest458/Generated458.ilproj index 6ab23e5a84f4a8..b9f4bdbc6f0c72 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest458/Generated458.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest458/Generated458.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest459/Generated459.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest459/Generated459.ilproj index 9e286d501e3f01..1554c1bcda730a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest459/Generated459.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest459/Generated459.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest46/Generated46.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest46/Generated46.ilproj index 8c66c61d7a37e4..ffef52afa1073a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest46/Generated46.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest46/Generated46.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest460/Generated460.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest460/Generated460.ilproj index 4ada4fcdfbaad4..a6fa06adc345c9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest460/Generated460.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest460/Generated460.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest461/Generated461.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest461/Generated461.ilproj index 7a9c9c987cb263..588ffa34489b7d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest461/Generated461.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest461/Generated461.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest462/Generated462.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest462/Generated462.ilproj index 001f7107390626..77f806be84a51f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest462/Generated462.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest462/Generated462.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest463/Generated463.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest463/Generated463.ilproj index f5f83952ad5633..b0c24882981ede 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest463/Generated463.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest463/Generated463.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest464/Generated464.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest464/Generated464.ilproj index 9050a1b79af449..c123b22e6350e3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest464/Generated464.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest464/Generated464.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest465/Generated465.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest465/Generated465.ilproj index 7521b8630999b9..3d18b42942a7d6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest465/Generated465.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest465/Generated465.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest466/Generated466.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest466/Generated466.ilproj index 5189200b38eabb..90f8324e694a85 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest466/Generated466.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest466/Generated466.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest467/Generated467.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest467/Generated467.ilproj index 380a060041e94c..6d66f6c5df59ed 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest467/Generated467.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest467/Generated467.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest468/Generated468.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest468/Generated468.ilproj index b7be7300389d95..2830201959643d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest468/Generated468.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest468/Generated468.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest469/Generated469.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest469/Generated469.ilproj index 6afa7964701bfc..09d3f2ae274df7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest469/Generated469.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest469/Generated469.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest47/Generated47.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest47/Generated47.ilproj index 2a66d5af4116f3..3815dac5d1123f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest47/Generated47.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest47/Generated47.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest470/Generated470.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest470/Generated470.ilproj index 84b940cdb70a8b..c476a8abfd359a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest470/Generated470.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest470/Generated470.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest471/Generated471.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest471/Generated471.ilproj index ee092d5cd94a17..982a757b16e142 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest471/Generated471.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest471/Generated471.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest472/Generated472.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest472/Generated472.ilproj index cdc27fd20a041b..da7e7c78006b45 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest472/Generated472.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest472/Generated472.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest473/Generated473.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest473/Generated473.ilproj index 117d35dc896852..ec23190f3d6c80 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest473/Generated473.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest473/Generated473.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest474/Generated474.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest474/Generated474.ilproj index 0df1621bad65bd..0ed801ce59e9d5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest474/Generated474.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest474/Generated474.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest475/Generated475.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest475/Generated475.ilproj index fd7f29e37aa37d..b03186652e4c48 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest475/Generated475.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest475/Generated475.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest476/Generated476.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest476/Generated476.ilproj index 92462f81bd8bdd..8e9290abe8c15f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest476/Generated476.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest476/Generated476.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest477/Generated477.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest477/Generated477.ilproj index 91b9396a79d9ab..8cad0455d94350 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest477/Generated477.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest477/Generated477.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest478/Generated478.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest478/Generated478.ilproj index da4ac2b418b9f9..af1ca8a8c8da4a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest478/Generated478.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest478/Generated478.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest479/Generated479.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest479/Generated479.ilproj index bcf24e31b8517d..d405502ceb6396 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest479/Generated479.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest479/Generated479.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest48/Generated48.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest48/Generated48.ilproj index 24a88be58c0ae8..c1f6b773ac36ea 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest48/Generated48.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest48/Generated48.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest480/Generated480.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest480/Generated480.ilproj index cdad2884d6d24c..751934baef201c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest480/Generated480.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest480/Generated480.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest481/Generated481.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest481/Generated481.ilproj index 9c4ae5447dc762..63f32b1e5f6748 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest481/Generated481.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest481/Generated481.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest482/Generated482.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest482/Generated482.ilproj index ba91e19be3823a..af952e0cdab91a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest482/Generated482.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest482/Generated482.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest483/Generated483.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest483/Generated483.ilproj index 050c08514a81d0..b3252e32b33b07 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest483/Generated483.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest483/Generated483.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest484/Generated484.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest484/Generated484.ilproj index b0eb728370145e..57962fcfd2d481 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest484/Generated484.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest484/Generated484.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest485/Generated485.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest485/Generated485.ilproj index 314557ed9c0b57..2ebb9612be838c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest485/Generated485.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest485/Generated485.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest486/Generated486.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest486/Generated486.ilproj index db50536fb0f6df..7118d46a84c4ea 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest486/Generated486.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest486/Generated486.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest487/Generated487.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest487/Generated487.ilproj index dba27408956333..8df1c9ddfb0b54 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest487/Generated487.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest487/Generated487.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest488/Generated488.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest488/Generated488.ilproj index 60a37148652dd1..e3c84b1adb9341 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest488/Generated488.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest488/Generated488.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest489/Generated489.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest489/Generated489.ilproj index f465c764e7f284..a986cd5d883744 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest489/Generated489.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest489/Generated489.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest49/Generated49.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest49/Generated49.ilproj index 377d40f082011c..9434d1d4973d84 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest49/Generated49.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest49/Generated49.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest490/Generated490.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest490/Generated490.ilproj index a2b50f75ea0c34..09ee5cbd7d8a58 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest490/Generated490.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest490/Generated490.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest491/Generated491.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest491/Generated491.ilproj index 5b0b0138721cc9..1a16868e4dfdf8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest491/Generated491.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest491/Generated491.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest492/Generated492.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest492/Generated492.ilproj index 08892b479ab630..9bdb653ec09e78 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest492/Generated492.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest492/Generated492.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest493/Generated493.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest493/Generated493.ilproj index 877a22ce99259f..f59deb80c6a683 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest493/Generated493.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest493/Generated493.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest494/Generated494.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest494/Generated494.ilproj index ea4a111da83ce0..1fd152a06fa5ff 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest494/Generated494.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest494/Generated494.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest495/Generated495.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest495/Generated495.ilproj index 370b539b68e471..0b14e340a7dfc5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest495/Generated495.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest495/Generated495.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest496/Generated496.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest496/Generated496.ilproj index afbbfabc0af7ea..c87f77d0b9d314 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest496/Generated496.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest496/Generated496.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest497/Generated497.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest497/Generated497.ilproj index 83a12cd5b4e6c5..f5ef5c64698132 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest497/Generated497.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest497/Generated497.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest498/Generated498.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest498/Generated498.ilproj index 4f7304a9f71289..41ca9fae439402 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest498/Generated498.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest498/Generated498.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest499/Generated499.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest499/Generated499.ilproj index cd0a75b1f42cf0..d24233e316f13b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest499/Generated499.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest499/Generated499.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest5/Generated5.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest5/Generated5.ilproj index 6817dfcf694474..ee5a83239f8292 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest5/Generated5.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest5/Generated5.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest50/Generated50.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest50/Generated50.ilproj index d4123089b47670..fe1405e810f7a4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest50/Generated50.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest50/Generated50.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest500/Generated500.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest500/Generated500.ilproj index 5c3536704dd9f6..1948e404e5e9a4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest500/Generated500.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest500/Generated500.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest501/Generated501.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest501/Generated501.ilproj index 4513a4a87a5206..8b7b96aa984e99 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest501/Generated501.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest501/Generated501.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest502/Generated502.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest502/Generated502.ilproj index 931f3652eeb32c..096c06e6e17a41 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest502/Generated502.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest502/Generated502.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest503/Generated503.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest503/Generated503.ilproj index 275203d38f7e98..0c636a17def242 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest503/Generated503.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest503/Generated503.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest504/Generated504.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest504/Generated504.ilproj index 1b8e0957e9c127..4c8c91446d223a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest504/Generated504.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest504/Generated504.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest505/Generated505.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest505/Generated505.ilproj index 3327db0b6351ad..5a82ff19f24c14 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest505/Generated505.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest505/Generated505.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest506/Generated506.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest506/Generated506.ilproj index edb194cc084aad..325ed78c71ed28 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest506/Generated506.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest506/Generated506.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest507/Generated507.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest507/Generated507.ilproj index 53451a99746eac..8e3a63745627cb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest507/Generated507.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest507/Generated507.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest508/Generated508.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest508/Generated508.ilproj index 5e552f134262f8..7837f33aa016a1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest508/Generated508.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest508/Generated508.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest509/Generated509.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest509/Generated509.ilproj index 02fc81bc6dcc65..5790fe604e27a6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest509/Generated509.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest509/Generated509.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest51/Generated51.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest51/Generated51.ilproj index 9960674866fd0a..00cf045629c19c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest51/Generated51.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest51/Generated51.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest510/Generated510.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest510/Generated510.ilproj index e09766e4617708..84f5b2dbb95248 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest510/Generated510.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest510/Generated510.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest511/Generated511.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest511/Generated511.ilproj index f42e3865c1db9e..2ac38598266bdf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest511/Generated511.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest511/Generated511.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest512/Generated512.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest512/Generated512.ilproj index 03f46bc854eadd..ba341cb878da0a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest512/Generated512.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest512/Generated512.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest513/Generated513.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest513/Generated513.ilproj index ed239edbc61d8c..fb669912b43eec 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest513/Generated513.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest513/Generated513.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest514/Generated514.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest514/Generated514.ilproj index b362ec9a6ad76c..80d88a442b5a2d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest514/Generated514.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest514/Generated514.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest515/Generated515.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest515/Generated515.ilproj index 89cb0345ceb832..d806e94aedf590 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest515/Generated515.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest515/Generated515.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest516/Generated516.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest516/Generated516.ilproj index 79a168d4bee2cd..9d3c791dbdc72c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest516/Generated516.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest516/Generated516.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest517/Generated517.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest517/Generated517.ilproj index f7067986dc0f7f..7ece21d97be742 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest517/Generated517.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest517/Generated517.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest518/Generated518.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest518/Generated518.ilproj index 7c2242dfa16727..faba506a474551 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest518/Generated518.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest518/Generated518.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest519/Generated519.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest519/Generated519.ilproj index a77ffd9a45e3d8..03bac2ee8a62cd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest519/Generated519.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest519/Generated519.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest52/Generated52.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest52/Generated52.ilproj index c05eef78f34dab..6dcc5eb5970e8e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest52/Generated52.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest52/Generated52.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest520/Generated520.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest520/Generated520.ilproj index d2df567614703a..2043ec968ba762 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest520/Generated520.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest520/Generated520.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest521/Generated521.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest521/Generated521.ilproj index 41d37e20920484..a10bb5dcab6bb8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest521/Generated521.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest521/Generated521.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest522/Generated522.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest522/Generated522.ilproj index 6818d09f8f0a17..203443bdf0d55c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest522/Generated522.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest522/Generated522.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest523/Generated523.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest523/Generated523.ilproj index 25b86b8109fa7e..3d73da7d4a955b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest523/Generated523.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest523/Generated523.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest524/Generated524.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest524/Generated524.ilproj index b2266c0a9a8bc2..d68f990cc0f9c0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest524/Generated524.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest524/Generated524.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest525/Generated525.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest525/Generated525.ilproj index 40e3481ba178da..bfc0f31e3e2ee8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest525/Generated525.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest525/Generated525.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest526/Generated526.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest526/Generated526.ilproj index 03f7f617ca00dd..96627019e1f644 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest526/Generated526.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest526/Generated526.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest527/Generated527.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest527/Generated527.ilproj index e6d666fabda35d..4a17bbb532f85a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest527/Generated527.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest527/Generated527.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest528/Generated528.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest528/Generated528.ilproj index b79bd6708ac47c..718000f1e4599b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest528/Generated528.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest528/Generated528.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest529/Generated529.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest529/Generated529.ilproj index 387cb99df1d176..9fb93bb6a93fc6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest529/Generated529.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest529/Generated529.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest53/Generated53.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest53/Generated53.ilproj index dac78f1a6ac1e4..05eb3aa71fea6f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest53/Generated53.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest53/Generated53.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest530/Generated530.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest530/Generated530.ilproj index abc144e8aa7161..7b60eb2ccb1aa0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest530/Generated530.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest530/Generated530.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest531/Generated531.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest531/Generated531.ilproj index 7289ae9f61d7d5..f8f3ede6f107a5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest531/Generated531.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest531/Generated531.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest532/Generated532.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest532/Generated532.ilproj index 1bde2981e5df2d..e9ca4e088c9dfa 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest532/Generated532.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest532/Generated532.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest533/Generated533.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest533/Generated533.ilproj index 4931e5a2659def..1ac9a48421f807 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest533/Generated533.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest533/Generated533.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest534/Generated534.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest534/Generated534.ilproj index b2910a171c48ec..84a2f031c0e0d1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest534/Generated534.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest534/Generated534.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest535/Generated535.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest535/Generated535.ilproj index 2e9ad1f5c489ee..801f8d99518b1c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest535/Generated535.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest535/Generated535.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest536/Generated536.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest536/Generated536.ilproj index 0b1bca4cc371c2..c84196814677a2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest536/Generated536.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest536/Generated536.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest537/Generated537.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest537/Generated537.ilproj index 2055960de73881..10cbaf9632deda 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest537/Generated537.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest537/Generated537.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest538/Generated538.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest538/Generated538.ilproj index 03191501016216..77dbef0266d52e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest538/Generated538.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest538/Generated538.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest539/Generated539.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest539/Generated539.ilproj index 9f453143e9e183..c87c0600024d8a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest539/Generated539.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest539/Generated539.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest54/Generated54.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest54/Generated54.ilproj index ad57711a5dd1e2..8258531aeb1668 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest54/Generated54.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest54/Generated54.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest540/Generated540.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest540/Generated540.ilproj index d916de7b3476e5..e9d9454b80d608 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest540/Generated540.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest540/Generated540.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest541/Generated541.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest541/Generated541.ilproj index 37c4db45c640d0..41772862dc5a3e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest541/Generated541.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest541/Generated541.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest542/Generated542.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest542/Generated542.ilproj index fbb085e557e5e0..9de91b6d535fe3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest542/Generated542.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest542/Generated542.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest543/Generated543.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest543/Generated543.ilproj index 9b48866e5fabed..bbb8c95bacca34 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest543/Generated543.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest543/Generated543.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest544/Generated544.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest544/Generated544.ilproj index f778b6e257f524..e0542cedc3e85a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest544/Generated544.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest544/Generated544.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest545/Generated545.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest545/Generated545.ilproj index 285b8fca21c76f..bc4ad7583d62db 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest545/Generated545.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest545/Generated545.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest546/Generated546.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest546/Generated546.ilproj index a3932dd495d60d..6e9cdb30f247df 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest546/Generated546.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest546/Generated546.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest547/Generated547.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest547/Generated547.ilproj index ba3fce2015a9c3..74991a57b90533 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest547/Generated547.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest547/Generated547.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest548/Generated548.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest548/Generated548.ilproj index 62b9a621cba1ef..cec9d716810bac 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest548/Generated548.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest548/Generated548.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest549/Generated549.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest549/Generated549.ilproj index 8668a19fe5fd23..ac587be3728510 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest549/Generated549.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest549/Generated549.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest55/Generated55.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest55/Generated55.ilproj index e0623c1c72de10..b2c4f4177d83d3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest55/Generated55.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest55/Generated55.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest550/Generated550.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest550/Generated550.ilproj index 247c3ebcb30a2a..986fdce7afff0a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest550/Generated550.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest550/Generated550.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest551/Generated551.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest551/Generated551.ilproj index d761496914d2b9..1fc786c73b2613 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest551/Generated551.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest551/Generated551.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest552/Generated552.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest552/Generated552.ilproj index 362be7fbc7487e..628eed10f7cc4a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest552/Generated552.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest552/Generated552.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest553/Generated553.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest553/Generated553.ilproj index b2954dec7f4d0b..f5a6870069e435 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest553/Generated553.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest553/Generated553.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest554/Generated554.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest554/Generated554.ilproj index d47f68ffc54340..4f7ff69d23bbc2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest554/Generated554.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest554/Generated554.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest555/Generated555.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest555/Generated555.ilproj index f79eb8886b5b41..7e8663263740c1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest555/Generated555.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest555/Generated555.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest556/Generated556.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest556/Generated556.ilproj index 95fc99fcd897c1..c64f69850e56d8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest556/Generated556.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest556/Generated556.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest557/Generated557.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest557/Generated557.ilproj index fbd354df381ee4..01b6aca43bdd58 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest557/Generated557.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest557/Generated557.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest558/Generated558.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest558/Generated558.ilproj index a723cb718578d2..cc19ed8420d332 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest558/Generated558.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest558/Generated558.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest559/Generated559.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest559/Generated559.ilproj index bbcf73fa7c9c4c..d36b041a7122a1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest559/Generated559.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest559/Generated559.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest56/Generated56.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest56/Generated56.ilproj index cadc75497f543b..90b0a92bcd3a90 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest56/Generated56.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest56/Generated56.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest560/Generated560.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest560/Generated560.ilproj index be050b396e05ea..d269d4548e0be8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest560/Generated560.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest560/Generated560.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest561/Generated561.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest561/Generated561.ilproj index c5d3f10222b12c..65286b3e16291a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest561/Generated561.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest561/Generated561.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest562/Generated562.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest562/Generated562.ilproj index 7192c63678532f..58223bf2bbe469 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest562/Generated562.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest562/Generated562.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest563/Generated563.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest563/Generated563.ilproj index db83c62bd2d79a..d38f89fda79a3f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest563/Generated563.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest563/Generated563.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest564/Generated564.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest564/Generated564.ilproj index 58f7c171ff98b3..dd982abae84843 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest564/Generated564.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest564/Generated564.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest565/Generated565.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest565/Generated565.ilproj index 04279c5e3adf7f..249c6f94083e36 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest565/Generated565.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest565/Generated565.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest566/Generated566.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest566/Generated566.ilproj index e5bb5fcf874e2a..5039e09cfeb2da 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest566/Generated566.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest566/Generated566.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest567/Generated567.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest567/Generated567.ilproj index 0d5675f66e6e85..98ebf5d927c976 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest567/Generated567.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest567/Generated567.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest568/Generated568.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest568/Generated568.ilproj index b793f3cefeda48..b64692179ad4a5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest568/Generated568.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest568/Generated568.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest569/Generated569.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest569/Generated569.ilproj index 4b372596abc9da..679d2864c0afff 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest569/Generated569.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest569/Generated569.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest57/Generated57.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest57/Generated57.ilproj index 0c3cdfe2ad7c1f..4818593c492503 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest57/Generated57.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest57/Generated57.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest570/Generated570.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest570/Generated570.ilproj index f346853743d59b..95fe420e5bc742 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest570/Generated570.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest570/Generated570.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest571/Generated571.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest571/Generated571.ilproj index 311bb2d127b1e5..8a2f1c53e9e480 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest571/Generated571.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest571/Generated571.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest572/Generated572.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest572/Generated572.ilproj index 7613bf4627f0a3..a33a225f27e9b8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest572/Generated572.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest572/Generated572.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest573/Generated573.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest573/Generated573.ilproj index 9313c218a61312..b49e9b3faa15c4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest573/Generated573.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest573/Generated573.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest574/Generated574.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest574/Generated574.ilproj index 51cddd138b3f9a..ebc3821152c2a4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest574/Generated574.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest574/Generated574.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest575/Generated575.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest575/Generated575.ilproj index 151749e4aec36a..8831756ffb247f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest575/Generated575.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest575/Generated575.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest576/Generated576.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest576/Generated576.ilproj index 0aae48c1500db3..0d758e4f5b973c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest576/Generated576.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest576/Generated576.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest577/Generated577.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest577/Generated577.ilproj index cb58eb21603d1e..be8db55a489fc4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest577/Generated577.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest577/Generated577.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest578/Generated578.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest578/Generated578.ilproj index c18498fa9107be..e3478f5145ef69 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest578/Generated578.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest578/Generated578.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest579/Generated579.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest579/Generated579.ilproj index 351362f0f37cba..6da0794ddfed96 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest579/Generated579.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest579/Generated579.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest58/Generated58.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest58/Generated58.ilproj index 01c2fbe4b96919..a50310a0cf7a46 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest58/Generated58.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest58/Generated58.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest580/Generated580.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest580/Generated580.ilproj index 058f9d38bd4b56..bb48b17d30b152 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest580/Generated580.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest580/Generated580.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest581/Generated581.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest581/Generated581.ilproj index f86a8bdd20dd1c..2d30d6427d9860 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest581/Generated581.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest581/Generated581.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest582/Generated582.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest582/Generated582.ilproj index 4e3e7b4e06bc2c..7ad265ebb94274 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest582/Generated582.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest582/Generated582.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest583/Generated583.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest583/Generated583.ilproj index 004410145ed513..eb0e1ae797254c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest583/Generated583.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest583/Generated583.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest584/Generated584.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest584/Generated584.ilproj index 890fd18ccf2d5d..04052f1cd80a1f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest584/Generated584.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest584/Generated584.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest585/Generated585.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest585/Generated585.ilproj index 540ec2f51923bd..600d3033562de0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest585/Generated585.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest585/Generated585.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest586/Generated586.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest586/Generated586.ilproj index d78d9c5d554496..2be8366bd54e86 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest586/Generated586.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest586/Generated586.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest587/Generated587.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest587/Generated587.ilproj index 0ce23fa52ef13c..10fef840aed26a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest587/Generated587.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest587/Generated587.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest588/Generated588.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest588/Generated588.ilproj index f1cb12bef81042..96dd19cd89fdb0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest588/Generated588.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest588/Generated588.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest589/Generated589.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest589/Generated589.ilproj index b476014925c6d9..2ff809330349af 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest589/Generated589.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest589/Generated589.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest59/Generated59.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest59/Generated59.ilproj index 7b99d68f10716a..fe87d0b1e83e3f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest59/Generated59.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest59/Generated59.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest590/Generated590.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest590/Generated590.ilproj index fd0f524f120ccb..c5753d005d03eb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest590/Generated590.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest590/Generated590.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest591/Generated591.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest591/Generated591.ilproj index 8830d2350930a5..4819150f91ad2c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest591/Generated591.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest591/Generated591.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest592/Generated592.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest592/Generated592.ilproj index b4cd3b3050c3f1..5be7157766175b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest592/Generated592.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest592/Generated592.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest593/Generated593.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest593/Generated593.ilproj index 25ac5a4b17c320..fa9b56bfb0788d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest593/Generated593.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest593/Generated593.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest594/Generated594.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest594/Generated594.ilproj index 3e21caeec047df..e11f529e37de13 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest594/Generated594.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest594/Generated594.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest595/Generated595.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest595/Generated595.ilproj index ae996ed8a716b6..cfc1615ece36aa 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest595/Generated595.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest595/Generated595.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest596/Generated596.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest596/Generated596.ilproj index 2146659d4bac6f..a5cd1915c2f5b0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest596/Generated596.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest596/Generated596.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest597/Generated597.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest597/Generated597.ilproj index 5789ce41957929..f48b0bd39670ab 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest597/Generated597.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest597/Generated597.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest598/Generated598.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest598/Generated598.ilproj index 380363adb002d8..3de016c2ae06c2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest598/Generated598.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest598/Generated598.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest599/Generated599.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest599/Generated599.ilproj index bf8475003e18d0..e5ac754ef2595d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest599/Generated599.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest599/Generated599.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest6/Generated6.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest6/Generated6.ilproj index 500475523273ba..35bdd9865c9ae2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest6/Generated6.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest6/Generated6.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest60/Generated60.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest60/Generated60.ilproj index f50873706d4bc5..2de0ef486816a1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest60/Generated60.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest60/Generated60.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest600/Generated600.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest600/Generated600.ilproj index 9002114a8701a7..ddc70e4ef3bcbd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest600/Generated600.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest600/Generated600.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest601/Generated601.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest601/Generated601.ilproj index cfe67b507fa79c..71f3b72e62889d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest601/Generated601.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest601/Generated601.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest602/Generated602.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest602/Generated602.ilproj index 616b60dd9f07e2..9ca2391f44dd13 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest602/Generated602.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest602/Generated602.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest603/Generated603.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest603/Generated603.ilproj index f99d0355e5ae2f..f6d006ea2fa4f0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest603/Generated603.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest603/Generated603.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest604/Generated604.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest604/Generated604.ilproj index 73bfe231cfc7f0..233740af009351 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest604/Generated604.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest604/Generated604.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest605/Generated605.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest605/Generated605.ilproj index c9491e4ac6fe19..bcf68291d77303 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest605/Generated605.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest605/Generated605.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest606/Generated606.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest606/Generated606.ilproj index 8a14f5982a0efb..5db5205807ee23 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest606/Generated606.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest606/Generated606.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest607/Generated607.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest607/Generated607.ilproj index fb74f432fac781..4589ac28b36a17 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest607/Generated607.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest607/Generated607.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest608/Generated608.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest608/Generated608.ilproj index 5bab7def738956..e6f069454df7ee 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest608/Generated608.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest608/Generated608.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest609/Generated609.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest609/Generated609.ilproj index cffe74ab72c046..e7e7a8d463c8d8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest609/Generated609.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest609/Generated609.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest61/Generated61.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest61/Generated61.ilproj index a780c58f3feb78..529a5a4b91aaec 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest61/Generated61.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest61/Generated61.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest610/Generated610.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest610/Generated610.ilproj index ee0818a1217f90..67b899137db023 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest610/Generated610.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest610/Generated610.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest611/Generated611.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest611/Generated611.ilproj index f4374e332859fc..cab206514007d9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest611/Generated611.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest611/Generated611.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest612/Generated612.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest612/Generated612.ilproj index 66d7882a9a1cb8..fef4f17d2f9c65 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest612/Generated612.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest612/Generated612.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest613/Generated613.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest613/Generated613.ilproj index 24bd51baf2f455..538ac82f309d37 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest613/Generated613.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest613/Generated613.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest614/Generated614.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest614/Generated614.ilproj index cb339b5b9b4753..251cb5bffcc45e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest614/Generated614.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest614/Generated614.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest615/Generated615.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest615/Generated615.ilproj index 523346d25d031f..a4d47029a54ef8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest615/Generated615.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest615/Generated615.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest616/Generated616.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest616/Generated616.ilproj index 6f4cf865e05acd..3a3b79dd9aada4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest616/Generated616.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest616/Generated616.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest617/Generated617.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest617/Generated617.ilproj index 11e23323fb69a1..6a34e11f8c2024 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest617/Generated617.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest617/Generated617.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest618/Generated618.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest618/Generated618.ilproj index 7378c29c6756e0..3596a3609760ff 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest618/Generated618.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest618/Generated618.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest619/Generated619.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest619/Generated619.ilproj index d2234f69e4cf7b..6940f2beb08607 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest619/Generated619.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest619/Generated619.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest62/Generated62.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest62/Generated62.ilproj index 6c126762de66a0..635b15c66e1754 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest62/Generated62.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest62/Generated62.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest620/Generated620.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest620/Generated620.ilproj index 068887202f6b32..67ae521e1e6e1a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest620/Generated620.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest620/Generated620.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest621/Generated621.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest621/Generated621.ilproj index 7bbb2007a0d669..b565f245b89831 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest621/Generated621.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest621/Generated621.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest622/Generated622.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest622/Generated622.ilproj index 11e63d4b150c2e..730ab3752a37f2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest622/Generated622.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest622/Generated622.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest623/Generated623.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest623/Generated623.ilproj index 844b3c43e4ef75..a5d0317f741f09 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest623/Generated623.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest623/Generated623.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest624/Generated624.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest624/Generated624.ilproj index d3f2c1e70b1cf3..61d7fd54fc8ea5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest624/Generated624.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest624/Generated624.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest625/Generated625.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest625/Generated625.ilproj index 3e2b70ffafda2b..ed80c5de3adb09 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest625/Generated625.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest625/Generated625.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest626/Generated626.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest626/Generated626.ilproj index 2efb9bd88f99a5..c292c294277371 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest626/Generated626.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest626/Generated626.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest627/Generated627.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest627/Generated627.ilproj index a9049ca4b1ffab..c1dccca5b33bdd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest627/Generated627.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest627/Generated627.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest628/Generated628.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest628/Generated628.ilproj index 4dddff4e7627f4..5877bd2f76af29 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest628/Generated628.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest628/Generated628.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest629/Generated629.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest629/Generated629.ilproj index 8870e31ee24aa2..0b04c57e124637 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest629/Generated629.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest629/Generated629.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest63/Generated63.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest63/Generated63.ilproj index 0994c5b4b083c0..4c058d6cc3d3a5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest63/Generated63.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest63/Generated63.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest630/Generated630.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest630/Generated630.ilproj index 06a84a2d851d4c..f997bde6e1599e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest630/Generated630.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest630/Generated630.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest631/Generated631.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest631/Generated631.ilproj index 9ada288c77ca94..d48dd876c24400 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest631/Generated631.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest631/Generated631.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest632/Generated632.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest632/Generated632.ilproj index ce0ca28144185f..37cd60acb47adf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest632/Generated632.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest632/Generated632.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest633/Generated633.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest633/Generated633.ilproj index e4750c9a83c3b0..5b0b64bed1bd77 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest633/Generated633.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest633/Generated633.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest634/Generated634.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest634/Generated634.ilproj index 17ec78a85f5a7b..2c5b14d8e16c68 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest634/Generated634.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest634/Generated634.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest635/Generated635.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest635/Generated635.ilproj index e559e8ac8d35bd..e1eb01e2e85d19 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest635/Generated635.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest635/Generated635.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest636/Generated636.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest636/Generated636.ilproj index e1ed6f375ba4be..56aedd1050eebc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest636/Generated636.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest636/Generated636.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest637/Generated637.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest637/Generated637.ilproj index 8761df6e2a195c..dee1cf2c1c13e6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest637/Generated637.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest637/Generated637.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest638/Generated638.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest638/Generated638.ilproj index c3590c6376c6e2..44ef557e9f8ade 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest638/Generated638.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest638/Generated638.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest639/Generated639.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest639/Generated639.ilproj index 4d8a980e0a52d6..3b52b7294f61f6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest639/Generated639.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest639/Generated639.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest64/Generated64.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest64/Generated64.ilproj index 964ab89b87408f..d403f20d0429e5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest64/Generated64.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest64/Generated64.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest640/Generated640.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest640/Generated640.ilproj index 0b45236dc0f34b..9ae7c01454bd0f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest640/Generated640.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest640/Generated640.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest641/Generated641.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest641/Generated641.ilproj index 6b518d56dc940e..e7eb2fc932c21b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest641/Generated641.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest641/Generated641.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest642/Generated642.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest642/Generated642.ilproj index 8227bb61adaaa3..6b3773c0170a61 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest642/Generated642.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest642/Generated642.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest643/Generated643.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest643/Generated643.ilproj index a751882fc3dd09..02f0048e8e0106 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest643/Generated643.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest643/Generated643.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest644/Generated644.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest644/Generated644.ilproj index 33aeb18fd9b28b..31f2e359064841 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest644/Generated644.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest644/Generated644.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest645/Generated645.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest645/Generated645.ilproj index c29c9db6013e49..8a466c649a703d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest645/Generated645.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest645/Generated645.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest646/Generated646.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest646/Generated646.ilproj index ec811e1b0a324f..7be9ea09380de8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest646/Generated646.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest646/Generated646.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest647/Generated647.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest647/Generated647.ilproj index 9c43073f23792a..2d03f31e284c85 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest647/Generated647.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest647/Generated647.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest648/Generated648.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest648/Generated648.ilproj index d9d7eb0473ec40..f3f69c98473a1d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest648/Generated648.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest648/Generated648.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest649/Generated649.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest649/Generated649.ilproj index 700727f000be16..d186f050238f4d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest649/Generated649.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest649/Generated649.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest65/Generated65.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest65/Generated65.ilproj index 37dfbd82a448a4..0e9d09425e1d3e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest65/Generated65.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest65/Generated65.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest650/Generated650.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest650/Generated650.ilproj index 05b94c69047001..bfe808bf9cd05c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest650/Generated650.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest650/Generated650.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest651/Generated651.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest651/Generated651.ilproj index affe415039c3aa..4f88afbbbb408f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest651/Generated651.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest651/Generated651.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest652/Generated652.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest652/Generated652.ilproj index c88cf9a5ac6192..a8c91cfc8158b0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest652/Generated652.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest652/Generated652.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest653/Generated653.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest653/Generated653.ilproj index d9446f377bd184..a1681ed5ac5160 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest653/Generated653.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest653/Generated653.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest654/Generated654.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest654/Generated654.ilproj index 0b2d9faf2f7427..48384c9f1cc567 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest654/Generated654.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest654/Generated654.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest655/Generated655.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest655/Generated655.ilproj index 6f9caad0c43e47..6a70b30c97e093 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest655/Generated655.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest655/Generated655.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest656/Generated656.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest656/Generated656.ilproj index 0d1004bfe19b09..28525cc69b869e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest656/Generated656.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest656/Generated656.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest657/Generated657.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest657/Generated657.ilproj index a066cf79a8e265..f7ea1a73fb5542 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest657/Generated657.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest657/Generated657.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest658/Generated658.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest658/Generated658.ilproj index 803f9f9cbae2ea..7d0d36a0c4ce68 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest658/Generated658.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest658/Generated658.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest659/Generated659.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest659/Generated659.ilproj index 986ef47606a17b..64dbeb79a9f475 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest659/Generated659.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest659/Generated659.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest66/Generated66.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest66/Generated66.ilproj index 23a8c7d32388ac..c72ad6962e9b46 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest66/Generated66.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest66/Generated66.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest660/Generated660.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest660/Generated660.ilproj index dd659f756950ad..26398fe8dbd41f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest660/Generated660.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest660/Generated660.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest661/Generated661.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest661/Generated661.ilproj index 58ea4754272d6f..84b48caee2f355 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest661/Generated661.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest661/Generated661.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest662/Generated662.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest662/Generated662.ilproj index 530bf7284482a8..964160bc4d7f09 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest662/Generated662.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest662/Generated662.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest663/Generated663.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest663/Generated663.ilproj index e121dc527fb01a..b9b0624b37671b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest663/Generated663.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest663/Generated663.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest664/Generated664.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest664/Generated664.ilproj index cbe8840008dba8..4503a26ee7d7b7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest664/Generated664.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest664/Generated664.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest665/Generated665.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest665/Generated665.ilproj index aa45e8399e7ca4..9ae9c2a5ab4657 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest665/Generated665.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest665/Generated665.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest666/Generated666.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest666/Generated666.ilproj index 9cbc72d3b76b57..432181ce28dc58 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest666/Generated666.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest666/Generated666.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest667/Generated667.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest667/Generated667.ilproj index 9f3554f64dc8be..22591d5c2bd8ed 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest667/Generated667.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest667/Generated667.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest668/Generated668.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest668/Generated668.ilproj index 4bca2b19e9fd0a..59b7716b84189d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest668/Generated668.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest668/Generated668.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest669/Generated669.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest669/Generated669.ilproj index af5e6ebbb50c0c..b093390003cd8f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest669/Generated669.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest669/Generated669.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest67/Generated67.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest67/Generated67.ilproj index 7e3fbc969ed1df..af06abac3dbd11 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest67/Generated67.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest67/Generated67.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest670/Generated670.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest670/Generated670.ilproj index b03a3729b51a11..07056037c72cbf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest670/Generated670.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest670/Generated670.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest671/Generated671.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest671/Generated671.ilproj index 9fef3845e2bcb4..246d8b04796bf4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest671/Generated671.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest671/Generated671.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest672/Generated672.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest672/Generated672.ilproj index 881e1ec962835e..c79d4db045d575 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest672/Generated672.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest672/Generated672.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest673/Generated673.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest673/Generated673.ilproj index 840df62d137a67..16c1523ca06518 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest673/Generated673.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest673/Generated673.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest674/Generated674.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest674/Generated674.ilproj index 32d59ce7c86c76..8e30cb0e88725d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest674/Generated674.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest674/Generated674.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest675/Generated675.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest675/Generated675.ilproj index 9bf210139b4c0c..96c3811885ac74 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest675/Generated675.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest675/Generated675.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest676/Generated676.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest676/Generated676.ilproj index 7806a2018ebb83..51b71ef5d827a5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest676/Generated676.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest676/Generated676.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest677/Generated677.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest677/Generated677.ilproj index af70dbe67da8a0..45331e1659833a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest677/Generated677.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest677/Generated677.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest678/Generated678.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest678/Generated678.ilproj index 21ed5554e747d3..aceb257ae5776e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest678/Generated678.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest678/Generated678.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest679/Generated679.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest679/Generated679.ilproj index 91679d9393a16d..11af0289062148 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest679/Generated679.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest679/Generated679.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest68/Generated68.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest68/Generated68.ilproj index 41f334dab033dc..d2929e7b7d5ab5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest68/Generated68.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest68/Generated68.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest680/Generated680.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest680/Generated680.ilproj index dcc7b9b61732ce..77f2cd2f913372 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest680/Generated680.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest680/Generated680.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest681/Generated681.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest681/Generated681.ilproj index e3fa6bc1caa3dd..5e8c1569eb8cfa 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest681/Generated681.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest681/Generated681.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest682/Generated682.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest682/Generated682.ilproj index d8e621f610c158..072858357a5d52 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest682/Generated682.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest682/Generated682.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest683/Generated683.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest683/Generated683.ilproj index c79d35415e622a..b8b76c14d7927b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest683/Generated683.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest683/Generated683.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest684/Generated684.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest684/Generated684.ilproj index d1c50788feb444..ffb4b85e55b036 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest684/Generated684.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest684/Generated684.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest685/Generated685.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest685/Generated685.ilproj index 76e367ec052743..c642de7e17887f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest685/Generated685.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest685/Generated685.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest686/Generated686.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest686/Generated686.ilproj index 5ed895a997361f..a8409c2d2d96f6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest686/Generated686.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest686/Generated686.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest687/Generated687.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest687/Generated687.ilproj index 6802633e699890..99066adcb8e08e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest687/Generated687.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest687/Generated687.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest688/Generated688.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest688/Generated688.ilproj index 0b551139c8e1e3..8da5b489769ec2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest688/Generated688.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest688/Generated688.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest689/Generated689.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest689/Generated689.ilproj index e101da021af5d2..fa6b906cb8e4c9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest689/Generated689.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest689/Generated689.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest69/Generated69.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest69/Generated69.ilproj index 98f925c8e2810a..a522e7c48779e9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest69/Generated69.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest69/Generated69.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest690/Generated690.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest690/Generated690.ilproj index 54d2ac7f5f23d1..b814b3c74a6f6a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest690/Generated690.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest690/Generated690.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest691/Generated691.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest691/Generated691.ilproj index 1f3948ad1d151c..960539394246f6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest691/Generated691.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest691/Generated691.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest692/Generated692.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest692/Generated692.ilproj index 473d2d49575504..0cf4cc29642ce8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest692/Generated692.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest692/Generated692.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest693/Generated693.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest693/Generated693.ilproj index cacaa7541d2025..ee3dd132636671 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest693/Generated693.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest693/Generated693.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest694/Generated694.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest694/Generated694.ilproj index 5f1ba381eca733..ffd95ebaeeec27 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest694/Generated694.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest694/Generated694.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest695/Generated695.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest695/Generated695.ilproj index 42f55a810dcc3f..67141ac4159149 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest695/Generated695.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest695/Generated695.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest696/Generated696.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest696/Generated696.ilproj index d579507fb76bab..271dd0f79315ee 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest696/Generated696.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest696/Generated696.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest697/Generated697.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest697/Generated697.ilproj index f0e3318e6f83fe..499fe652880934 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest697/Generated697.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest697/Generated697.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest698/Generated698.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest698/Generated698.ilproj index 01a9a3da4d0756..f7af8395a2e3b2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest698/Generated698.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest698/Generated698.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest699/Generated699.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest699/Generated699.ilproj index 8cfef07f741ac0..fe3b780a88bb37 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest699/Generated699.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest699/Generated699.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest7/Generated7.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest7/Generated7.ilproj index db1db2c51d5a78..a76c71ca6b22d0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest7/Generated7.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest7/Generated7.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest70/Generated70.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest70/Generated70.ilproj index 34903845ea344d..e3b2dcd8ebf4e2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest70/Generated70.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest70/Generated70.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest700/Generated700.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest700/Generated700.ilproj index 9f0d745c402336..de4e02b3f2c338 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest700/Generated700.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest700/Generated700.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest701/Generated701.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest701/Generated701.ilproj index 36b1a6d3ffd2aa..5f11d19538409e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest701/Generated701.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest701/Generated701.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest702/Generated702.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest702/Generated702.ilproj index 3ab8613d06af24..94cb44080184e0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest702/Generated702.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest702/Generated702.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest703/Generated703.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest703/Generated703.ilproj index f4ed3d2548a217..da75c0e954ec8b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest703/Generated703.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest703/Generated703.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest704/Generated704.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest704/Generated704.ilproj index 64f697bb7da4f8..7c2f5952008d91 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest704/Generated704.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest704/Generated704.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest705/Generated705.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest705/Generated705.ilproj index 2d27ff446c8f7c..c54b2148b90579 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest705/Generated705.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest705/Generated705.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest706/Generated706.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest706/Generated706.ilproj index 5af3f453bbdc9a..aadd9400a0ab8e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest706/Generated706.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest706/Generated706.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest707/Generated707.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest707/Generated707.ilproj index 10952322c405a8..0fc6c17ca2049c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest707/Generated707.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest707/Generated707.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest708/Generated708.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest708/Generated708.ilproj index ebcb8c87080ffe..90131c12d29385 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest708/Generated708.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest708/Generated708.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest709/Generated709.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest709/Generated709.ilproj index 1b7846a3213fa8..7fb929d24c2e4a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest709/Generated709.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest709/Generated709.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest71/Generated71.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest71/Generated71.ilproj index b4febf796702c3..dd1b4d56f8b5cf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest71/Generated71.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest71/Generated71.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest710/Generated710.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest710/Generated710.ilproj index b8ae743ec27e4c..832d16e8be3a99 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest710/Generated710.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest710/Generated710.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest711/Generated711.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest711/Generated711.ilproj index 6f9c1baf962abf..49acfce01ee2b2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest711/Generated711.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest711/Generated711.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest712/Generated712.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest712/Generated712.ilproj index d6fea8d1702e98..d20598921fa0a6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest712/Generated712.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest712/Generated712.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest713/Generated713.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest713/Generated713.ilproj index e782233ddcaa81..43a3efef875278 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest713/Generated713.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest713/Generated713.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest714/Generated714.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest714/Generated714.ilproj index 3b91a46ce42618..3930ddd53d1ef7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest714/Generated714.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest714/Generated714.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest715/Generated715.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest715/Generated715.ilproj index edfa8b9a6813a5..97356411aec10f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest715/Generated715.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest715/Generated715.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest716/Generated716.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest716/Generated716.ilproj index 19e0a29db9f269..c5c8be174904e7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest716/Generated716.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest716/Generated716.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest717/Generated717.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest717/Generated717.ilproj index 13b64d4a14e280..dd531d5d2f1d60 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest717/Generated717.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest717/Generated717.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest718/Generated718.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest718/Generated718.ilproj index 935b385c8c3630..b79cc077fb25ac 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest718/Generated718.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest718/Generated718.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest719/Generated719.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest719/Generated719.ilproj index b220ffd85c10fe..187743e02c47ab 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest719/Generated719.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest719/Generated719.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest72/Generated72.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest72/Generated72.ilproj index a9b1ee604c2aef..72096ebffef2de 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest72/Generated72.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest72/Generated72.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest720/Generated720.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest720/Generated720.ilproj index 6da6671967136f..9ed6daa7a6f0d2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest720/Generated720.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest720/Generated720.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest721/Generated721.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest721/Generated721.ilproj index 64a475cec01e9c..5a7f4776962841 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest721/Generated721.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest721/Generated721.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest722/Generated722.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest722/Generated722.ilproj index 8ab66bdc91cc06..ec7858d4636bb9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest722/Generated722.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest722/Generated722.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest723/Generated723.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest723/Generated723.ilproj index 6efe1888a5e440..db9514cd1fc60b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest723/Generated723.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest723/Generated723.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest724/Generated724.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest724/Generated724.ilproj index 076491585e21a8..a690c3b8dae030 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest724/Generated724.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest724/Generated724.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest725/Generated725.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest725/Generated725.ilproj index fb0c597f25a709..b97ecf575f6b10 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest725/Generated725.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest725/Generated725.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest726/Generated726.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest726/Generated726.ilproj index 050a72cbf457df..94993c753da79c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest726/Generated726.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest726/Generated726.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest727/Generated727.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest727/Generated727.ilproj index 400c70fc69faa7..e4f021be945134 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest727/Generated727.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest727/Generated727.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest728/Generated728.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest728/Generated728.ilproj index d2623a8039dc4e..4de0a8b5feb992 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest728/Generated728.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest728/Generated728.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest729/Generated729.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest729/Generated729.ilproj index df5e795f5618f5..44f1b585f48bc2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest729/Generated729.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest729/Generated729.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest73/Generated73.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest73/Generated73.ilproj index 6a652e70164f47..fa60f0fddb49ff 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest73/Generated73.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest73/Generated73.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest730/Generated730.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest730/Generated730.ilproj index 22e6a20aa1126f..d290306fff8349 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest730/Generated730.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest730/Generated730.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest731/Generated731.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest731/Generated731.ilproj index ccd846a4b0fb3b..bb9b0c14563e1c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest731/Generated731.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest731/Generated731.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest732/Generated732.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest732/Generated732.ilproj index a1e918352155be..0464642234dadc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest732/Generated732.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest732/Generated732.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest733/Generated733.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest733/Generated733.ilproj index c684211ca74628..f7b621c0c2321f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest733/Generated733.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest733/Generated733.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest734/Generated734.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest734/Generated734.ilproj index 9e572121ac0032..14b506ac72c458 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest734/Generated734.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest734/Generated734.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest735/Generated735.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest735/Generated735.ilproj index 529d54685fc7ba..7788197a8b2c53 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest735/Generated735.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest735/Generated735.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest736/Generated736.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest736/Generated736.ilproj index 786de8233f3226..2ecba109be4ba4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest736/Generated736.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest736/Generated736.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest737/Generated737.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest737/Generated737.ilproj index 3273ebf7f78385..a6e2eddab040dd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest737/Generated737.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest737/Generated737.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest738/Generated738.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest738/Generated738.ilproj index 3b41f6f073adde..f5f9da3ed043a1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest738/Generated738.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest738/Generated738.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest739/Generated739.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest739/Generated739.ilproj index 3db1045fb5a02e..0473e911e9fbaf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest739/Generated739.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest739/Generated739.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest74/Generated74.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest74/Generated74.ilproj index 1e36e0035e4946..2dff40a3c91c42 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest74/Generated74.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest74/Generated74.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest740/Generated740.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest740/Generated740.ilproj index 85f682be7873f5..93249f2a9c9ece 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest740/Generated740.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest740/Generated740.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest741/Generated741.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest741/Generated741.ilproj index 6455fcccb09519..12747b5d910a12 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest741/Generated741.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest741/Generated741.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest742/Generated742.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest742/Generated742.ilproj index 14fa1a50ae3193..58e677a6f2253c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest742/Generated742.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest742/Generated742.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest743/Generated743.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest743/Generated743.ilproj index ee272248057e7b..797e53a6edd238 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest743/Generated743.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest743/Generated743.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest744/Generated744.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest744/Generated744.ilproj index 6575329607d7b3..eb573db2506133 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest744/Generated744.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest744/Generated744.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest745/Generated745.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest745/Generated745.ilproj index 8a8f1429845d96..de33038f454d96 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest745/Generated745.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest745/Generated745.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest746/Generated746.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest746/Generated746.ilproj index fd093de52de22d..11d7bc7e364ebf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest746/Generated746.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest746/Generated746.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest747/Generated747.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest747/Generated747.ilproj index 8631d4b40b0b0f..45678e2e544544 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest747/Generated747.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest747/Generated747.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest748/Generated748.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest748/Generated748.ilproj index 1217ee5927826a..f883efc191c168 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest748/Generated748.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest748/Generated748.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest749/Generated749.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest749/Generated749.ilproj index 8e02f4376578b8..b1d173870e3ba5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest749/Generated749.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest749/Generated749.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest75/Generated75.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest75/Generated75.ilproj index 6b461ebbce32d9..ecdfa5472a6158 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest75/Generated75.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest75/Generated75.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest750/Generated750.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest750/Generated750.ilproj index a788f77d5ed822..acf6b83cca73e9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest750/Generated750.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest750/Generated750.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest751/Generated751.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest751/Generated751.ilproj index a386760600d070..7d9cf50c19eeb8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest751/Generated751.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest751/Generated751.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest752/Generated752.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest752/Generated752.ilproj index 66a5f01190049f..f84cf9e38df3b5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest752/Generated752.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest752/Generated752.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest753/Generated753.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest753/Generated753.ilproj index 85124db3019abc..9b5b841d72fe2b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest753/Generated753.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest753/Generated753.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest754/Generated754.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest754/Generated754.ilproj index 35f4d1b30d7db9..0c3107b575b305 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest754/Generated754.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest754/Generated754.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest755/Generated755.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest755/Generated755.ilproj index 5cd6e6a6c627ce..35e32c5f29be11 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest755/Generated755.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest755/Generated755.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest756/Generated756.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest756/Generated756.ilproj index 0254b5d8528020..1052d5f07bf073 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest756/Generated756.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest756/Generated756.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest757/Generated757.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest757/Generated757.ilproj index 92ebc4d7cf986c..42dc16ed72601b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest757/Generated757.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest757/Generated757.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest758/Generated758.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest758/Generated758.ilproj index 4376a80976d313..2643ae3b92d365 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest758/Generated758.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest758/Generated758.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest759/Generated759.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest759/Generated759.ilproj index c5b2dcd1b4f823..5527a55f4d6f22 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest759/Generated759.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest759/Generated759.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest76/Generated76.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest76/Generated76.ilproj index 2ab1ae37d99a6f..aaac402bacc0f9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest76/Generated76.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest76/Generated76.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest760/Generated760.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest760/Generated760.ilproj index 3bba97bae043b0..f91e01aef3488c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest760/Generated760.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest760/Generated760.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest761/Generated761.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest761/Generated761.ilproj index 63d7e1b43805d6..d7668a9fc44d96 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest761/Generated761.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest761/Generated761.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest762/Generated762.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest762/Generated762.ilproj index c49d9e2c98d9da..86abdbed849754 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest762/Generated762.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest762/Generated762.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest763/Generated763.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest763/Generated763.ilproj index e7006496985343..ed08ada9b35826 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest763/Generated763.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest763/Generated763.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest764/Generated764.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest764/Generated764.ilproj index 90bc1eaf08f009..21bd86a374eb80 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest764/Generated764.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest764/Generated764.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest765/Generated765.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest765/Generated765.ilproj index aacd886cfc98ea..e5b5ecbd25419f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest765/Generated765.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest765/Generated765.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest766/Generated766.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest766/Generated766.ilproj index 453f18881e996a..5fc53a54698285 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest766/Generated766.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest766/Generated766.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest767/Generated767.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest767/Generated767.ilproj index a59f275a1900f1..4c408867d34a84 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest767/Generated767.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest767/Generated767.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest768/Generated768.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest768/Generated768.ilproj index b2fe5f7abd1b79..3f82da85554d6e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest768/Generated768.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest768/Generated768.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest769/Generated769.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest769/Generated769.ilproj index 74c823343da5a6..233a7aa4e66d00 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest769/Generated769.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest769/Generated769.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest77/Generated77.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest77/Generated77.ilproj index 725aac7d8eee31..c9f3bbf653aa59 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest77/Generated77.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest77/Generated77.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest770/Generated770.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest770/Generated770.ilproj index cbe6db34deba44..6749166f18d329 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest770/Generated770.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest770/Generated770.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest771/Generated771.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest771/Generated771.ilproj index ba463a11a2fdc1..f53c3d82980500 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest771/Generated771.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest771/Generated771.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest772/Generated772.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest772/Generated772.ilproj index ffde0384e72831..3138a3072fac39 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest772/Generated772.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest772/Generated772.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest773/Generated773.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest773/Generated773.ilproj index 968fa299f138b2..8e140e6f9ce274 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest773/Generated773.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest773/Generated773.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest774/Generated774.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest774/Generated774.ilproj index 8884c42b5dbf8d..877f30ed4fc6e7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest774/Generated774.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest774/Generated774.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest775/Generated775.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest775/Generated775.ilproj index 2ff6bcf4583710..0b4d31ec41e21b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest775/Generated775.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest775/Generated775.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest776/Generated776.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest776/Generated776.ilproj index e3214eabec39d1..13a04cf518b75a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest776/Generated776.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest776/Generated776.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest777/Generated777.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest777/Generated777.ilproj index 3bce777bd91033..713612ac398e55 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest777/Generated777.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest777/Generated777.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest778/Generated778.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest778/Generated778.ilproj index 0fbd075b3c4b7b..2120d661383963 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest778/Generated778.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest778/Generated778.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest779/Generated779.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest779/Generated779.ilproj index 7e3b5f64f8e45f..c2261ad87e1dda 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest779/Generated779.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest779/Generated779.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest78/Generated78.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest78/Generated78.ilproj index bbb78c67b058fe..4d3db0a1f06661 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest78/Generated78.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest78/Generated78.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest780/Generated780.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest780/Generated780.ilproj index 46bb7d2beca92d..2e5403756854d6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest780/Generated780.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest780/Generated780.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest781/Generated781.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest781/Generated781.ilproj index 4eeb17ac5fb930..44346065db5f65 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest781/Generated781.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest781/Generated781.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest782/Generated782.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest782/Generated782.ilproj index 523b79c5d9b40a..b1b03bc7b5ba96 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest782/Generated782.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest782/Generated782.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest783/Generated783.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest783/Generated783.ilproj index fc5027deaa6a3d..c0c0e07b41e99d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest783/Generated783.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest783/Generated783.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest784/Generated784.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest784/Generated784.ilproj index 94f2dd09ef5d7e..52bdf150ddd3f5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest784/Generated784.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest784/Generated784.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest785/Generated785.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest785/Generated785.ilproj index be841f2504b579..e8ee7108a3bb01 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest785/Generated785.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest785/Generated785.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest786/Generated786.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest786/Generated786.ilproj index f9d8af6b209b22..c4aeec47c265b7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest786/Generated786.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest786/Generated786.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest787/Generated787.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest787/Generated787.ilproj index a0b9e6f5ea9f20..64f853a11da5cb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest787/Generated787.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest787/Generated787.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest788/Generated788.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest788/Generated788.ilproj index 8555bf9080c951..2fec96af12ebd9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest788/Generated788.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest788/Generated788.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest789/Generated789.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest789/Generated789.ilproj index cc90f3cf8f9b7e..d1db4815381b41 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest789/Generated789.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest789/Generated789.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest79/Generated79.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest79/Generated79.ilproj index 81635cad6fa16e..16aa8bbf7f623b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest79/Generated79.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest79/Generated79.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest790/Generated790.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest790/Generated790.ilproj index f0b34a9b2dfdee..a84a11ba0d4ded 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest790/Generated790.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest790/Generated790.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest791/Generated791.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest791/Generated791.ilproj index 281472bb865719..001c37cd25e82a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest791/Generated791.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest791/Generated791.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest792/Generated792.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest792/Generated792.ilproj index b30c8296fb0804..5f1fe2dcfc96f1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest792/Generated792.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest792/Generated792.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest793/Generated793.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest793/Generated793.ilproj index eea79dc422d034..a1cb6ae3772b58 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest793/Generated793.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest793/Generated793.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest794/Generated794.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest794/Generated794.ilproj index 424994de0a1d1d..48ba9c3eda85ae 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest794/Generated794.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest794/Generated794.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest795/Generated795.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest795/Generated795.ilproj index 1c7c50d1f85639..98254bb9eaea61 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest795/Generated795.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest795/Generated795.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest796/Generated796.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest796/Generated796.ilproj index eff6c8d28f4738..acdb82ef935fca 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest796/Generated796.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest796/Generated796.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest797/Generated797.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest797/Generated797.ilproj index b3b5c1e1b63b7c..42d474a70b3f6a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest797/Generated797.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest797/Generated797.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest798/Generated798.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest798/Generated798.ilproj index 0777d4cb8bfcf2..38840f5a13bf90 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest798/Generated798.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest798/Generated798.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest799/Generated799.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest799/Generated799.ilproj index 8c7ce702804f7f..6ab84808f0c104 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest799/Generated799.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest799/Generated799.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest8/Generated8.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest8/Generated8.ilproj index e47e22c2d862bf..b35cea0949e955 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest8/Generated8.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest8/Generated8.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest80/Generated80.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest80/Generated80.ilproj index 7c0875c41e91cc..481ca397d637da 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest80/Generated80.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest80/Generated80.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest800/Generated800.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest800/Generated800.ilproj index c04f5fdefaa32b..f89a5b98012f07 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest800/Generated800.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest800/Generated800.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest801/Generated801.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest801/Generated801.ilproj index ec7dad9efb8ffa..a1afcc6937d3c9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest801/Generated801.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest801/Generated801.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest802/Generated802.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest802/Generated802.ilproj index 8f9a11ba7850a9..4c9e3ba9d441ca 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest802/Generated802.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest802/Generated802.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest803/Generated803.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest803/Generated803.ilproj index b496a8e2589388..357da45123cab4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest803/Generated803.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest803/Generated803.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest804/Generated804.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest804/Generated804.ilproj index a6ba56aa4cc333..2aeb34a5332fe0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest804/Generated804.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest804/Generated804.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest805/Generated805.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest805/Generated805.ilproj index 54385fff103023..cc45fa1bd2920a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest805/Generated805.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest805/Generated805.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest806/Generated806.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest806/Generated806.ilproj index 32dd75b29e149f..397f392cd0ceca 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest806/Generated806.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest806/Generated806.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest807/Generated807.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest807/Generated807.ilproj index 91c266acee6f3b..954972bf8ed0a2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest807/Generated807.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest807/Generated807.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest808/Generated808.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest808/Generated808.ilproj index 7d7765295c8b15..350ae521168759 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest808/Generated808.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest808/Generated808.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest809/Generated809.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest809/Generated809.ilproj index 987bebbaf195f7..e2b5a09ca9ad97 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest809/Generated809.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest809/Generated809.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest81/Generated81.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest81/Generated81.ilproj index 74e69eb72e4e53..25f60e85610de6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest81/Generated81.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest81/Generated81.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest810/Generated810.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest810/Generated810.ilproj index 4896feb56bb55d..25d7552abc3582 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest810/Generated810.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest810/Generated810.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest811/Generated811.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest811/Generated811.ilproj index de7a6caddff4e9..1acd3fce589f4b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest811/Generated811.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest811/Generated811.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest812/Generated812.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest812/Generated812.ilproj index 2d51674d464684..bf468d6b50a86b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest812/Generated812.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest812/Generated812.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest813/Generated813.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest813/Generated813.ilproj index 631ff78ba59469..2e425988d8407a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest813/Generated813.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest813/Generated813.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest814/Generated814.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest814/Generated814.ilproj index 67753e8b891ead..e55699dd23b967 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest814/Generated814.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest814/Generated814.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest815/Generated815.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest815/Generated815.ilproj index bee5b665a84543..15b7eed61ffa0c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest815/Generated815.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest815/Generated815.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest816/Generated816.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest816/Generated816.ilproj index 40b5bc155a1094..52f62c0ef6570a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest816/Generated816.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest816/Generated816.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest817/Generated817.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest817/Generated817.ilproj index 1f005d1a111a16..0d8ab625854d98 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest817/Generated817.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest817/Generated817.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest818/Generated818.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest818/Generated818.ilproj index 4d0748bbb2be18..74f6e0fdaae9a2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest818/Generated818.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest818/Generated818.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest819/Generated819.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest819/Generated819.ilproj index 921c157549d9c3..a890d58e7702b4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest819/Generated819.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest819/Generated819.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest82/Generated82.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest82/Generated82.ilproj index 746087622d9645..dc89eda3bb8aff 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest82/Generated82.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest82/Generated82.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest820/Generated820.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest820/Generated820.ilproj index 20ce5c48478f48..8db20a521943ca 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest820/Generated820.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest820/Generated820.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest821/Generated821.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest821/Generated821.ilproj index 47ea8bd18245ab..97968f9b27a325 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest821/Generated821.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest821/Generated821.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest822/Generated822.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest822/Generated822.ilproj index 97c61722a99780..c5fc390c18ff93 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest822/Generated822.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest822/Generated822.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest823/Generated823.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest823/Generated823.ilproj index d29b6b1b8d7286..0ea8e5f0437d4c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest823/Generated823.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest823/Generated823.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest824/Generated824.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest824/Generated824.ilproj index f3dec96ea23293..fc85ef80a45ab1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest824/Generated824.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest824/Generated824.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest825/Generated825.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest825/Generated825.ilproj index dd95fe35d43dc0..7fc933cc7940c5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest825/Generated825.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest825/Generated825.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest826/Generated826.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest826/Generated826.ilproj index f342aa6cb0ec49..ec57d84c860d28 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest826/Generated826.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest826/Generated826.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest827/Generated827.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest827/Generated827.ilproj index 4113f7ae31b21b..19ab3d3b04105c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest827/Generated827.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest827/Generated827.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest828/Generated828.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest828/Generated828.ilproj index 40d49236029c64..e9871360b52ef6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest828/Generated828.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest828/Generated828.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest829/Generated829.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest829/Generated829.ilproj index ae960a40703070..3a8c0038ad1050 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest829/Generated829.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest829/Generated829.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest83/Generated83.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest83/Generated83.ilproj index 980d114b80f120..a5c3f079218c7b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest83/Generated83.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest83/Generated83.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest830/Generated830.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest830/Generated830.ilproj index b365a16d38d24a..c3422af98578c0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest830/Generated830.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest830/Generated830.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest831/Generated831.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest831/Generated831.ilproj index 3b8b3b44e74c28..4974216b65a840 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest831/Generated831.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest831/Generated831.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest832/Generated832.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest832/Generated832.ilproj index 9602df201e15df..dd12bf828f3e68 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest832/Generated832.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest832/Generated832.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest833/Generated833.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest833/Generated833.ilproj index 6c325c2714f516..0ef2520e5e723c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest833/Generated833.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest833/Generated833.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest834/Generated834.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest834/Generated834.ilproj index 7995be4eb6eb7f..f003eb89b925ee 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest834/Generated834.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest834/Generated834.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest835/Generated835.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest835/Generated835.ilproj index f954d88ea2da2c..e6e95dc841cccc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest835/Generated835.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest835/Generated835.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest836/Generated836.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest836/Generated836.ilproj index 714e4b24893ea1..56f435983c7e83 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest836/Generated836.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest836/Generated836.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest837/Generated837.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest837/Generated837.ilproj index 6c10279da97060..e5264434ab3f59 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest837/Generated837.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest837/Generated837.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest838/Generated838.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest838/Generated838.ilproj index c25adafb5dec6d..073b5f1408425e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest838/Generated838.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest838/Generated838.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest839/Generated839.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest839/Generated839.ilproj index b09345b6177b7f..68ac7f3e47a945 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest839/Generated839.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest839/Generated839.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest84/Generated84.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest84/Generated84.ilproj index e9e7b2e1e2d139..b0de70a33d63d0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest84/Generated84.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest84/Generated84.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest840/Generated840.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest840/Generated840.ilproj index 1fa4d7736934de..3e87ae1f66196b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest840/Generated840.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest840/Generated840.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest841/Generated841.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest841/Generated841.ilproj index e2b163738f6a8c..97672d00b7fe8a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest841/Generated841.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest841/Generated841.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest842/Generated842.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest842/Generated842.ilproj index 19619a9213682c..cc5ff47f250abd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest842/Generated842.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest842/Generated842.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest843/Generated843.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest843/Generated843.ilproj index b2d2baca72b7d8..5dcc30da979cab 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest843/Generated843.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest843/Generated843.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest844/Generated844.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest844/Generated844.ilproj index 62bef64d125478..be3a099142b77f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest844/Generated844.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest844/Generated844.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest845/Generated845.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest845/Generated845.ilproj index ee3777cd3efa76..67fe5859a427d4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest845/Generated845.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest845/Generated845.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest846/Generated846.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest846/Generated846.ilproj index e0d6996e21771e..c4cb3bff5220ac 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest846/Generated846.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest846/Generated846.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest847/Generated847.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest847/Generated847.ilproj index ceb7fb2af395ee..4856b0fc28bcf3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest847/Generated847.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest847/Generated847.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest848/Generated848.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest848/Generated848.ilproj index 9d4279cc5eb7a1..30afc6f2755e50 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest848/Generated848.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest848/Generated848.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest849/Generated849.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest849/Generated849.ilproj index 552abc86cf1ffb..e79e66df50f6e7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest849/Generated849.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest849/Generated849.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest85/Generated85.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest85/Generated85.ilproj index 3d69810eba48e7..9968843edda8b2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest85/Generated85.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest85/Generated85.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest850/Generated850.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest850/Generated850.ilproj index 17bff6690b77dc..6e442c7b97359f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest850/Generated850.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest850/Generated850.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest851/Generated851.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest851/Generated851.ilproj index e6b1fb4659b760..af910872130b58 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest851/Generated851.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest851/Generated851.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest852/Generated852.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest852/Generated852.ilproj index 6accd5ee7f9b48..9c42c6ffc76dc4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest852/Generated852.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest852/Generated852.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest853/Generated853.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest853/Generated853.ilproj index 4e1106e42fc11a..b88fae408b1194 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest853/Generated853.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest853/Generated853.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest854/Generated854.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest854/Generated854.ilproj index ca2e3b747fd817..d4cda16d4bbd7c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest854/Generated854.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest854/Generated854.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest855/Generated855.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest855/Generated855.ilproj index c2fe1dc57f1618..f4d1fef80bf748 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest855/Generated855.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest855/Generated855.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest856/Generated856.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest856/Generated856.ilproj index 214956458d890d..5f1708dc44014c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest856/Generated856.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest856/Generated856.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest857/Generated857.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest857/Generated857.ilproj index bafce5693fe9aa..c66dbfd9450121 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest857/Generated857.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest857/Generated857.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest858/Generated858.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest858/Generated858.ilproj index 578ec8ef3764f6..1dfcc848b567a8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest858/Generated858.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest858/Generated858.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest859/Generated859.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest859/Generated859.ilproj index 85a47cc1fc105e..61a87717492804 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest859/Generated859.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest859/Generated859.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest86/Generated86.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest86/Generated86.ilproj index cce70be59d3415..109460f4b0faba 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest86/Generated86.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest86/Generated86.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest860/Generated860.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest860/Generated860.ilproj index 3e442be4522ab0..9feb83f9991ee3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest860/Generated860.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest860/Generated860.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest861/Generated861.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest861/Generated861.ilproj index 7194e9b3822149..dcf114c4d10bef 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest861/Generated861.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest861/Generated861.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest862/Generated862.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest862/Generated862.ilproj index 4ffea903bad4fd..258208bce1e38a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest862/Generated862.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest862/Generated862.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest863/Generated863.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest863/Generated863.ilproj index 90022044f5ba84..1cd20af88b20e8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest863/Generated863.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest863/Generated863.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest864/Generated864.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest864/Generated864.ilproj index f37f58348b960c..6cc158e1962c2f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest864/Generated864.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest864/Generated864.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest865/Generated865.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest865/Generated865.ilproj index 29ddc74543b678..04636be791076e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest865/Generated865.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest865/Generated865.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest866/Generated866.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest866/Generated866.ilproj index 93ce9a1ecb9543..d729d3725933bc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest866/Generated866.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest866/Generated866.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest867/Generated867.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest867/Generated867.ilproj index bda68303cfdac5..93d8867bae89ea 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest867/Generated867.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest867/Generated867.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest868/Generated868.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest868/Generated868.ilproj index 459ac7ceb97473..d16ae76e65b34f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest868/Generated868.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest868/Generated868.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest869/Generated869.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest869/Generated869.ilproj index 1b09655333715a..2aaf1ebb6f29d7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest869/Generated869.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest869/Generated869.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest87/Generated87.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest87/Generated87.ilproj index a58ddad3c9056b..d3247917b87e01 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest87/Generated87.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest87/Generated87.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest870/Generated870.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest870/Generated870.ilproj index ec386c7aa0bd48..fafe5418c794e7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest870/Generated870.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest870/Generated870.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest871/Generated871.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest871/Generated871.ilproj index 9e06b6f946e7b7..cea23afa656e99 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest871/Generated871.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest871/Generated871.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest872/Generated872.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest872/Generated872.ilproj index 7bde5cdb6de0a6..601e21a0e05f50 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest872/Generated872.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest872/Generated872.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest873/Generated873.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest873/Generated873.ilproj index df8c1607c5cf19..40b3805805bfa5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest873/Generated873.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest873/Generated873.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest874/Generated874.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest874/Generated874.ilproj index 36e9149150d818..4924350dd1162a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest874/Generated874.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest874/Generated874.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest875/Generated875.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest875/Generated875.ilproj index 86e3fcb78aefbc..838a2cc1415019 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest875/Generated875.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest875/Generated875.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest876/Generated876.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest876/Generated876.ilproj index edd3d6e3317faa..f2e5a09c0951db 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest876/Generated876.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest876/Generated876.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest877/Generated877.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest877/Generated877.ilproj index 326ec766af3413..4f6859740d9bd2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest877/Generated877.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest877/Generated877.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest878/Generated878.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest878/Generated878.ilproj index 01205e486592a8..ee04c0f63a28ac 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest878/Generated878.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest878/Generated878.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest879/Generated879.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest879/Generated879.ilproj index c4fd4e90797145..a983e21798641a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest879/Generated879.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest879/Generated879.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest88/Generated88.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest88/Generated88.ilproj index 08d5dfcf81de76..4f827ec5a487a2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest88/Generated88.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest88/Generated88.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest880/Generated880.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest880/Generated880.ilproj index cb9e6969bbbb45..fa85a93c78a1e6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest880/Generated880.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest880/Generated880.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest881/Generated881.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest881/Generated881.ilproj index fb20dcd6751427..9924caa95109dc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest881/Generated881.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest881/Generated881.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest882/Generated882.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest882/Generated882.ilproj index 31c27536fc8b5b..0944a3d939180b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest882/Generated882.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest882/Generated882.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest883/Generated883.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest883/Generated883.ilproj index 30829d97193757..734b2ef7e51fb0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest883/Generated883.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest883/Generated883.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest884/Generated884.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest884/Generated884.ilproj index 45aec30d85d1d1..843bb9465968bc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest884/Generated884.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest884/Generated884.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest885/Generated885.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest885/Generated885.ilproj index e6a9ee4f3ecaa2..ec1ba53f5f5388 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest885/Generated885.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest885/Generated885.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest886/Generated886.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest886/Generated886.ilproj index 151ccd8bf8a860..edf6a2de223d1e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest886/Generated886.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest886/Generated886.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest887/Generated887.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest887/Generated887.ilproj index a500bccd5834b4..c5e2b984a21fc5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest887/Generated887.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest887/Generated887.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest888/Generated888.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest888/Generated888.ilproj index 2127bac56ac360..6e40c1f0dfd169 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest888/Generated888.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest888/Generated888.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest889/Generated889.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest889/Generated889.ilproj index 68d3a76b6aa450..afd4bf80c838ec 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest889/Generated889.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest889/Generated889.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest89/Generated89.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest89/Generated89.ilproj index 266e4f3663ca3a..ac7634a2e837e8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest89/Generated89.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest89/Generated89.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest890/Generated890.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest890/Generated890.ilproj index 4ea60f71474a81..131b7c6cbb67a8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest890/Generated890.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest890/Generated890.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest891/Generated891.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest891/Generated891.ilproj index b317e285148ab3..04a850c02b3659 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest891/Generated891.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest891/Generated891.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest892/Generated892.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest892/Generated892.ilproj index 5b73ec8fe9301f..7bfa8fa48f60d1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest892/Generated892.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest892/Generated892.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest893/Generated893.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest893/Generated893.ilproj index 1c7d8fa0c7760a..e11477644afbb9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest893/Generated893.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest893/Generated893.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest894/Generated894.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest894/Generated894.ilproj index c696ec126ed727..75570be22780fd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest894/Generated894.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest894/Generated894.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest895/Generated895.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest895/Generated895.ilproj index d40a42082a35a5..3730cb42aab4fd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest895/Generated895.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest895/Generated895.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest896/Generated896.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest896/Generated896.ilproj index 7558d9bcc0064d..799a5e01e085a4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest896/Generated896.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest896/Generated896.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest897/Generated897.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest897/Generated897.ilproj index c1a584ca3e6cdf..8063d805876c32 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest897/Generated897.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest897/Generated897.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest898/Generated898.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest898/Generated898.ilproj index 5622b4ad185043..a65823cfed2ad3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest898/Generated898.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest898/Generated898.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest899/Generated899.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest899/Generated899.ilproj index b496ce734ad0be..94f1db1079a94a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest899/Generated899.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest899/Generated899.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest9/Generated9.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest9/Generated9.ilproj index 7fed13b27cdecb..a3b0a764cc4f81 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest9/Generated9.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest9/Generated9.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest90/Generated90.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest90/Generated90.ilproj index 0c9d7a77c3c585..b694ccc8224aa1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest90/Generated90.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest90/Generated90.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest900/Generated900.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest900/Generated900.ilproj index 5a67d4e932ef81..c29c2873b3a77a 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest900/Generated900.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest900/Generated900.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest901/Generated901.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest901/Generated901.ilproj index 714276d734a26b..9d9ffe8323ae34 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest901/Generated901.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest901/Generated901.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest902/Generated902.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest902/Generated902.ilproj index 3db940fd80813c..97311f0398a618 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest902/Generated902.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest902/Generated902.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest903/Generated903.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest903/Generated903.ilproj index da504f9fb21092..3344f6853a20fb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest903/Generated903.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest903/Generated903.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest904/Generated904.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest904/Generated904.ilproj index fa0fa84c26e92b..9b8e971931ebea 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest904/Generated904.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest904/Generated904.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest905/Generated905.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest905/Generated905.ilproj index eb0c505880ed11..647145c924f2f2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest905/Generated905.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest905/Generated905.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest906/Generated906.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest906/Generated906.ilproj index f9c0c6018bb7f7..dc7cb952e859cb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest906/Generated906.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest906/Generated906.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest907/Generated907.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest907/Generated907.ilproj index bf86cef19e5e87..3b5428b5f38cd5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest907/Generated907.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest907/Generated907.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest908/Generated908.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest908/Generated908.ilproj index 147bad3940dd60..0a09d98ee8fd56 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest908/Generated908.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest908/Generated908.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest909/Generated909.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest909/Generated909.ilproj index 264819960eae25..85f2e04d6542a8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest909/Generated909.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest909/Generated909.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest91/Generated91.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest91/Generated91.ilproj index 2ea10353914171..e3c7d865211352 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest91/Generated91.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest91/Generated91.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest910/Generated910.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest910/Generated910.ilproj index 01c3464addadde..76699cc91c1bd5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest910/Generated910.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest910/Generated910.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest911/Generated911.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest911/Generated911.ilproj index 628581a49a7085..138f89a13525c8 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest911/Generated911.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest911/Generated911.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest912/Generated912.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest912/Generated912.ilproj index 3adde06cb0a8ce..762798195d45f7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest912/Generated912.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest912/Generated912.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest913/Generated913.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest913/Generated913.ilproj index 7ad252aeecab11..f6b981151a351c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest913/Generated913.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest913/Generated913.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest914/Generated914.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest914/Generated914.ilproj index 5d58e0f27a5e9b..608db066435d0b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest914/Generated914.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest914/Generated914.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest915/Generated915.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest915/Generated915.ilproj index 143883b8dfbcf0..a179487ab4c2e7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest915/Generated915.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest915/Generated915.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest916/Generated916.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest916/Generated916.ilproj index f0dd7abaad0c4b..b6d3c9298add4b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest916/Generated916.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest916/Generated916.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest917/Generated917.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest917/Generated917.ilproj index c934f6a29edf63..877e1a36b2daeb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest917/Generated917.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest917/Generated917.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest918/Generated918.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest918/Generated918.ilproj index 077882dd1fa424..e1be4272990275 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest918/Generated918.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest918/Generated918.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest919/Generated919.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest919/Generated919.ilproj index 6aa452ccb0907b..5cb9b8f5db1ee3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest919/Generated919.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest919/Generated919.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest92/Generated92.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest92/Generated92.ilproj index a8c97090cf3f9a..a42c651ae6821c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest92/Generated92.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest92/Generated92.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest920/Generated920.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest920/Generated920.ilproj index 522f6fe098f11d..4414b0b59f0c52 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest920/Generated920.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest920/Generated920.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest921/Generated921.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest921/Generated921.ilproj index 71f607dd70c4ee..ef7bde44d7513b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest921/Generated921.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest921/Generated921.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest922/Generated922.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest922/Generated922.ilproj index 2ba1c3ee98b57c..e9005b0ae932c9 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest922/Generated922.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest922/Generated922.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest923/Generated923.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest923/Generated923.ilproj index 313be0c53e87a5..e5ec385bafee97 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest923/Generated923.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest923/Generated923.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest924/Generated924.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest924/Generated924.ilproj index 6b81219e2d7f32..4381cc03a82eae 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest924/Generated924.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest924/Generated924.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest925/Generated925.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest925/Generated925.ilproj index 9f66e1de865bae..83640b49eb3177 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest925/Generated925.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest925/Generated925.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest926/Generated926.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest926/Generated926.ilproj index a7b39ea72a6fed..a4f43ac5e006cc 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest926/Generated926.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest926/Generated926.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest927/Generated927.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest927/Generated927.ilproj index c58feba87b51d5..3377b6464d1589 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest927/Generated927.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest927/Generated927.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest928/Generated928.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest928/Generated928.ilproj index 0c4cae6d95485f..e409a51a2114bb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest928/Generated928.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest928/Generated928.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest929/Generated929.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest929/Generated929.ilproj index 355e01ecc7c896..e043534f5f0168 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest929/Generated929.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest929/Generated929.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest93/Generated93.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest93/Generated93.ilproj index 4e87920044d8ab..ae8b12ee8bf4f1 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest93/Generated93.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest93/Generated93.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest930/Generated930.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest930/Generated930.ilproj index 64b80e007cfbe6..a4befa067d0e58 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest930/Generated930.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest930/Generated930.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest931/Generated931.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest931/Generated931.ilproj index 255149199d6eb3..c8ca2eb9ab6b65 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest931/Generated931.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest931/Generated931.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest932/Generated932.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest932/Generated932.ilproj index 1a51b32c270f8a..f4481b8671fdeb 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest932/Generated932.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest932/Generated932.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest933/Generated933.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest933/Generated933.ilproj index 76cc9d28ab44cf..5d1d7f356d9eaa 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest933/Generated933.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest933/Generated933.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest934/Generated934.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest934/Generated934.ilproj index 848d38f935bedc..a4f900bcf3e5be 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest934/Generated934.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest934/Generated934.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest935/Generated935.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest935/Generated935.ilproj index ce97e8400f2a5e..31e74d0daf91f4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest935/Generated935.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest935/Generated935.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest936/Generated936.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest936/Generated936.ilproj index 512760276b41da..21e6050253a48d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest936/Generated936.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest936/Generated936.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest937/Generated937.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest937/Generated937.ilproj index 8f195ed891d554..93380dc63bfd46 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest937/Generated937.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest937/Generated937.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest938/Generated938.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest938/Generated938.ilproj index ae9c45321cb137..7cf5d6b5bae257 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest938/Generated938.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest938/Generated938.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest939/Generated939.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest939/Generated939.ilproj index 43d19f467a4dc8..41729761065cb4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest939/Generated939.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest939/Generated939.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest94/Generated94.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest94/Generated94.ilproj index 4d7834cdf28302..9e7dbc8ef5cc75 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest94/Generated94.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest94/Generated94.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest940/Generated940.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest940/Generated940.ilproj index c9ec4cf3624618..7b9712711dc37c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest940/Generated940.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest940/Generated940.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest941/Generated941.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest941/Generated941.ilproj index 9037c98e122c24..bba43244df8dd6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest941/Generated941.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest941/Generated941.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest942/Generated942.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest942/Generated942.ilproj index 6f54d8a65b67dd..495c1ea9cdfddd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest942/Generated942.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest942/Generated942.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest943/Generated943.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest943/Generated943.ilproj index 7e253589ca58e6..8a51c77526c49f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest943/Generated943.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest943/Generated943.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest944/Generated944.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest944/Generated944.ilproj index 3fb212f9549ccb..296ef1c1e37057 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest944/Generated944.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest944/Generated944.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest945/Generated945.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest945/Generated945.ilproj index 186b9177b854ec..c9c1a252b61596 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest945/Generated945.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest945/Generated945.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest946/Generated946.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest946/Generated946.ilproj index 20813c79f92412..3fcd98128f20df 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest946/Generated946.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest946/Generated946.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest947/Generated947.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest947/Generated947.ilproj index 0db7ab63b92dd8..d9e21c6ebd7860 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest947/Generated947.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest947/Generated947.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest948/Generated948.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest948/Generated948.ilproj index 656b89a960e3f8..586d0b52c9c37d 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest948/Generated948.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest948/Generated948.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest949/Generated949.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest949/Generated949.ilproj index 207fab40b4bd61..1a3a1b17c718c3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest949/Generated949.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest949/Generated949.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest95/Generated95.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest95/Generated95.ilproj index a89e7b559af75d..dab27636bf0629 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest95/Generated95.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest95/Generated95.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest950/Generated950.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest950/Generated950.ilproj index 1394954337efb8..6220cad9c1ac27 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest950/Generated950.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest950/Generated950.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest951/Generated951.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest951/Generated951.ilproj index 7645b810c3909e..69b33afcf70e4b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest951/Generated951.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest951/Generated951.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest952/Generated952.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest952/Generated952.ilproj index 22dd62b2b76c53..5e1bb3d86d8650 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest952/Generated952.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest952/Generated952.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest953/Generated953.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest953/Generated953.ilproj index 449c4f2f2d05af..ff4eadcfa1c836 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest953/Generated953.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest953/Generated953.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest954/Generated954.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest954/Generated954.ilproj index 5ae0d660a1ea37..4eefb1ac22d2a6 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest954/Generated954.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest954/Generated954.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest955/Generated955.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest955/Generated955.ilproj index 3d1b39db12d557..d0312be29257a5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest955/Generated955.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest955/Generated955.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest956/Generated956.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest956/Generated956.ilproj index 48ea90cb4cb58e..04c737f2033f19 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest956/Generated956.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest956/Generated956.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest957/Generated957.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest957/Generated957.ilproj index cbe17973b08a30..8317433b6ad04f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest957/Generated957.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest957/Generated957.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest958/Generated958.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest958/Generated958.ilproj index a7e9a06339c3ac..92dbb7bc228bfa 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest958/Generated958.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest958/Generated958.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest959/Generated959.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest959/Generated959.ilproj index ae5fd7a6850825..0dbf9c430a0ecd 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest959/Generated959.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest959/Generated959.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest96/Generated96.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest96/Generated96.ilproj index b054ccf4871368..0776b08917b9cf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest96/Generated96.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest96/Generated96.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest960/Generated960.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest960/Generated960.ilproj index 79cdf134242e79..2eb9001df7f3f0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest960/Generated960.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest960/Generated960.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest961/Generated961.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest961/Generated961.ilproj index cb8ed19d11bf76..8c2afd9a0d0312 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest961/Generated961.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest961/Generated961.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest962/Generated962.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest962/Generated962.ilproj index ecea26d2b8e149..0057fc0d1edbdf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest962/Generated962.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest962/Generated962.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest963/Generated963.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest963/Generated963.ilproj index cb5a77dde102fb..54a23742f2cddf 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest963/Generated963.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest963/Generated963.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest964/Generated964.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest964/Generated964.ilproj index 59b4ed1877675d..abc74afc93d0f4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest964/Generated964.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest964/Generated964.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest965/Generated965.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest965/Generated965.ilproj index 2ce0a29b6d1cd5..ed045cdbc89211 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest965/Generated965.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest965/Generated965.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest966/Generated966.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest966/Generated966.ilproj index 2d59992b8f05c1..4900ca569f6c58 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest966/Generated966.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest966/Generated966.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest967/Generated967.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest967/Generated967.ilproj index 64e8587bde4481..6b3da4bd026857 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest967/Generated967.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest967/Generated967.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest968/Generated968.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest968/Generated968.ilproj index 3eb880aa7ad2ca..321c7b4e5bde7b 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest968/Generated968.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest968/Generated968.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest969/Generated969.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest969/Generated969.ilproj index ee891e4a07ad7b..6f5e0f268448e7 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest969/Generated969.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest969/Generated969.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest97/Generated97.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest97/Generated97.ilproj index d04f394b1c0a44..ce3aa1bbc702d4 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest97/Generated97.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest97/Generated97.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest970/Generated970.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest970/Generated970.ilproj index 4f36ab3144fd81..0a51a250c60398 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest970/Generated970.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest970/Generated970.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest971/Generated971.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest971/Generated971.ilproj index a073046c656cfc..c84a66d8621d64 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest971/Generated971.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest971/Generated971.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest972/Generated972.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest972/Generated972.ilproj index 3fa7f9b5bfca3f..65901732be70c2 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest972/Generated972.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest972/Generated972.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest973/Generated973.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest973/Generated973.ilproj index f152ed1cfcddb5..1695eefdf1a156 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest973/Generated973.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest973/Generated973.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest974/Generated974.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest974/Generated974.ilproj index d23c4198e10f64..37ac3d7c846340 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest974/Generated974.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest974/Generated974.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest975/Generated975.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest975/Generated975.ilproj index ab208e129f02e9..570cbd4dd09aea 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest975/Generated975.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest975/Generated975.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest976/Generated976.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest976/Generated976.ilproj index 154e0b5fb471cc..c2b98a59590c3e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest976/Generated976.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest976/Generated976.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest977/Generated977.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest977/Generated977.ilproj index a4fd0161bc32bf..d65327fc379d59 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest977/Generated977.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest977/Generated977.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest978/Generated978.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest978/Generated978.ilproj index 5ec0c2ca2a2924..68a76c3f0ba79e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest978/Generated978.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest978/Generated978.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest979/Generated979.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest979/Generated979.ilproj index 96439a662cb645..41ac7e2289f4a5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest979/Generated979.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest979/Generated979.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest98/Generated98.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest98/Generated98.ilproj index 6c9105f816c0fb..db0847c01ab583 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest98/Generated98.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest98/Generated98.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest980/Generated980.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest980/Generated980.ilproj index c3975cdb348854..48e80393973458 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest980/Generated980.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest980/Generated980.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest981/Generated981.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest981/Generated981.ilproj index 16a19636ef04d5..8772d54dfb21b5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest981/Generated981.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest981/Generated981.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest982/Generated982.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest982/Generated982.ilproj index 23aaa8cdde555f..2aef702d7e99e5 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest982/Generated982.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest982/Generated982.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest983/Generated983.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest983/Generated983.ilproj index 078e86ecd98787..59142d586fe224 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest983/Generated983.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest983/Generated983.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest984/Generated984.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest984/Generated984.ilproj index 7f8f7a63f5bbf2..ccbd255caa6146 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest984/Generated984.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest984/Generated984.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest985/Generated985.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest985/Generated985.ilproj index 382946d84b426e..8c12c1574724ed 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest985/Generated985.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest985/Generated985.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest986/Generated986.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest986/Generated986.ilproj index d53aed3c9e23a5..8ccc17ef475134 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest986/Generated986.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest986/Generated986.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest987/Generated987.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest987/Generated987.ilproj index 006e827bcbcef1..60787096fd6c82 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest987/Generated987.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest987/Generated987.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest988/Generated988.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest988/Generated988.ilproj index 1a03698a0ce1be..815405d609d78e 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest988/Generated988.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest988/Generated988.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest989/Generated989.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest989/Generated989.ilproj index c33d9a2fe722d4..2030f12ee9996f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest989/Generated989.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest989/Generated989.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest99/Generated99.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest99/Generated99.ilproj index 21a97a6913602d..3337fd288157b3 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest99/Generated99.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest99/Generated99.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest990/Generated990.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest990/Generated990.ilproj index d3a43fb4ec7af0..82dcb81e199273 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest990/Generated990.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest990/Generated990.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest991/Generated991.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest991/Generated991.ilproj index 029d3a9f84b262..a8b7b095391a04 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest991/Generated991.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest991/Generated991.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest992/Generated992.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest992/Generated992.ilproj index 8a41e35857b7ba..d706814339ef48 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest992/Generated992.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest992/Generated992.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest993/Generated993.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest993/Generated993.ilproj index 64b69f6281c3c2..a0ae03e80df276 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest993/Generated993.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest993/Generated993.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest994/Generated994.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest994/Generated994.ilproj index 2f82de5a4a9e9e..b91e1ebf759c1c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest994/Generated994.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest994/Generated994.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest995/Generated995.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest995/Generated995.ilproj index 3dd1fd0af77052..141f80c7192d06 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest995/Generated995.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest995/Generated995.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest996/Generated996.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest996/Generated996.ilproj index b237683ebe7301..330c30b8639f30 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest996/Generated996.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest996/Generated996.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest997/Generated997.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest997/Generated997.ilproj index d0136174a60560..7315eaa42304d0 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest997/Generated997.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest997/Generated997.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest998/Generated998.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest998/Generated998.ilproj index 6e44be07a034dd..0f88b88781ba0f 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest998/Generated998.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest998/Generated998.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest999/Generated999.ilproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest999/Generated999.ilproj index e3dc8f6f374043..2fbadd0956603c 100644 --- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest999/Generated999.ilproj +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTest999/Generated999.ilproj @@ -1,6 +1,5 @@ - Exe 1 diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests0-99.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests0-99.csproj new file mode 100644 index 00000000000000..115b67e172e88f --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests0-99.csproj @@ -0,0 +1,11 @@ + + + 1 + true + + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests100-199.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests100-199.csproj new file mode 100644 index 00000000000000..08f6a1a0c49cbd --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests100-199.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1000-1099.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1000-1099.csproj new file mode 100644 index 00000000000000..bddda0d0c5b0f6 --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1000-1099.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1100-1199.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1100-1199.csproj new file mode 100644 index 00000000000000..365bcf7d49c1cb --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1100-1199.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1200-1299.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1200-1299.csproj new file mode 100644 index 00000000000000..2ad89135c72f04 --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1200-1299.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1300-1399.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1300-1399.csproj new file mode 100644 index 00000000000000..0f092654b623ac --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1300-1399.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1400-1599.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1400-1599.csproj new file mode 100644 index 00000000000000..74cb61ba6e05dc --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1400-1599.csproj @@ -0,0 +1,11 @@ + + + 1 + true + + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests200-299.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests200-299.csproj new file mode 100644 index 00000000000000..f1aeb00ddab3ae --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests200-299.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests300-399.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests300-399.csproj new file mode 100644 index 00000000000000..74c158b5975503 --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests300-399.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests400-499.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests400-499.csproj new file mode 100644 index 00000000000000..6931acb1fa3c21 --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests400-499.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests500-599.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests500-599.csproj new file mode 100644 index 00000000000000..b95348752dd449 --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests500-599.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests600-699.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests600-699.csproj new file mode 100644 index 00000000000000..7efba2ca08d651 --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests600-699.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests700-799.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests700-799.csproj new file mode 100644 index 00000000000000..2f82ed1c20e2f8 --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests700-799.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests800-899.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests800-899.csproj new file mode 100644 index 00000000000000..7ad7e17d7f7aca --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests800-899.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests900-999.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests900-999.csproj new file mode 100644 index 00000000000000..6e952ff28dd47d --- /dev/null +++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests900-999.csproj @@ -0,0 +1,10 @@ + + + 1 + true + + + + + + diff --git a/src/tests/baseservices/typeequivalence/contracts/Types.cs b/src/tests/baseservices/typeequivalence/contracts/Types.cs index 44b0e2e4a9b0bd..a9fc0270a1bdd0 100644 --- a/src/tests/baseservices/typeequivalence/contracts/Types.cs +++ b/src/tests/baseservices/typeequivalence/contracts/Types.cs @@ -60,4 +60,23 @@ public interface ISparseType int MultiplyBy19(int a); int MultiplyBy20(int a); } + + // + // Below types are used in type punning tests and shouldn't be used anywhere else. + // + [Guid("98CE8975-E734-43C6-9D58-B8062053C6C5")] + public struct OnlyLoadOnce_1 + { + public int Field; + } + [Guid("D3F2B4C3-1CE5-45BB-AC9E-5036E580477F")] + public struct OnlyLoadOnce_2 + { + public int Field; + } + [Guid("690FCAC9-4CEC-406A-88DE-DA86B7914EA7")] + public struct OnlyLoadOnce_3 + { + public int Field; + } } diff --git a/src/tests/baseservices/typeequivalence/impl/Impls.cs b/src/tests/baseservices/typeequivalence/impl/Impls.cs index 3431ed19d4c429..71d5bedb9749e6 100644 --- a/src/tests/baseservices/typeequivalence/impl/Impls.cs +++ b/src/tests/baseservices/typeequivalence/impl/Impls.cs @@ -108,3 +108,19 @@ public static int GetSparseInterfaceMethodCount() public int MultiplyBy19(int a) { return a * 19; } public int MultiplyBy20(int a) { return a * 20; } } + +public class OnlyLoadOnceCaller +{ + public static int GetField_1(OnlyLoadOnce_1 s) + { + return s.Field; + } + public static int GetField_2(OnlyLoadOnce_2 s) + { + return s.Field; + } + public static int GetField_3(OnlyLoadOnce_3 s) + { + return s.Field; + } +} diff --git a/src/tests/baseservices/typeequivalence/impl/PunningLib.il b/src/tests/baseservices/typeequivalence/impl/PunningLib.il new file mode 100644 index 00000000000000..a54f6b6dd8d0b7 --- /dev/null +++ b/src/tests/baseservices/typeequivalence/impl/PunningLib.il @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) } +.assembly extern TypeImpl { auto } + +.assembly PunningLib { } + +.class public auto ansi abstract sealed beforefieldinit CreateFunctionPointer + extends [System.Runtime]System.Object +{ + .method public hidebysig static + int32 For_1( valuetype [TypeImpl]TypeEquivalenceTypes.OnlyLoadOnce_1 s ) cil managed + { + ldarg.0 + ldfld int32 [TypeImpl]TypeEquivalenceTypes.OnlyLoadOnce_1::Field + ret + } + .method public hidebysig static + native int For_2_Ldftn() cil managed + { + ldftn int32 CreateFunctionPointer::For_2( valuetype [TypeImpl]TypeEquivalenceTypes.OnlyLoadOnce_2 ) + call native int [System.Runtime]System.IntPtr::op_Explicit(void*) + ret + } + .method private hidebysig static + int32 For_2( valuetype [TypeImpl]TypeEquivalenceTypes.OnlyLoadOnce_2 s ) cil managed + { + ldarg.0 + ldfld int32 [TypeImpl]TypeEquivalenceTypes.OnlyLoadOnce_2::Field + ret + } + .method public hidebysig static + native int For_3_Ldvirtftn( [out] object& c ) cil managed + { + .locals ( [0] object ) + newobj instance void Derived::.ctor() + stloc.0 + ldarg.0 + ldloc.0 + stind.ref + ldloc.0 + ldvirtftn instance int32 Class::GetField (valuetype [TypeImpl]TypeEquivalenceTypes.OnlyLoadOnce_3 ) + call native int [System.Runtime]System.IntPtr::op_Explicit(void*) + ret + } +} + +.class private auto ansi beforefieldinit Class + extends [System.Runtime]System.Object +{ + .method public hidebysig newslot virtual + instance int32 GetField ( + valuetype [TypeImpl]TypeEquivalenceTypes.OnlyLoadOnce_3 s + ) cil managed + { + newobj instance void [System.Runtime]System.NotImplementedException::.ctor() + throw + } + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} + +.class private auto ansi beforefieldinit Derived + extends Class +{ + .method public hidebysig virtual + instance int32 GetField ( + valuetype [TypeImpl]TypeEquivalenceTypes.OnlyLoadOnce_3 s + ) cil managed + { + ldarg.1 + ldfld int32 [TypeImpl]TypeEquivalenceTypes.OnlyLoadOnce_3::Field + ret + } + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void Class::.ctor() + ret + } +} \ No newline at end of file diff --git a/src/tests/baseservices/typeequivalence/impl/PunningLib.ilproj b/src/tests/baseservices/typeequivalence/impl/PunningLib.ilproj new file mode 100644 index 00000000000000..aede8ebdfe4d9e --- /dev/null +++ b/src/tests/baseservices/typeequivalence/impl/PunningLib.ilproj @@ -0,0 +1,10 @@ + + + Library + SharedLibrary + + + + + + diff --git a/src/tests/baseservices/typeequivalence/simple/Simple.cs b/src/tests/baseservices/typeequivalence/simple/Simple.cs index 047aa5de783fec..c79f46ce27be2d 100644 --- a/src/tests/baseservices/typeequivalence/simple/Simple.cs +++ b/src/tests/baseservices/typeequivalence/simple/Simple.cs @@ -214,6 +214,45 @@ private static void TestGenericInterfaceEquivalence() Assert.True(typeof(IGeneric<>).MakeGenericType(inAsmInterfaceType).IsEquivalentTo(typeof(IGeneric<>).MakeGenericType(otherAsmInterfaceType))); } + private static unsafe void TestTypeEquivalenceWithTypePunning() + { + Console.WriteLine($"{nameof(TestTypeEquivalenceWithTypePunning)}"); + + { + Console.WriteLine($"-- GetFunctionPointer()"); + IntPtr fptr = typeof(CreateFunctionPointer).GetMethod("For_1").MethodHandle.GetFunctionPointer(); + Assert.NotEqual(IntPtr.Zero, fptr); + var s = new OnlyLoadOnce_1() + { + Field = 0x11 + }; + int res = ((delegate* )fptr)(s); + Assert.Equal(s.Field, res); + } + { + Console.WriteLine($"-- Ldftn"); + IntPtr fptr = CreateFunctionPointer.For_2_Ldftn(); + Assert.NotEqual(IntPtr.Zero, fptr); + var s = new OnlyLoadOnce_2() + { + Field = 0x22 + }; + int res = ((delegate* )fptr)(s); + Assert.Equal(s.Field, res); + } + { + Console.WriteLine($"-- Ldvirtftn"); + IntPtr fptr = CreateFunctionPointer.For_3_Ldvirtftn(out object inst); + Assert.NotEqual(IntPtr.Zero, fptr); + var s = new OnlyLoadOnce_3() + { + Field = 0x33 + }; + int res = ((delegate* )fptr)(inst, s); + Assert.Equal(s.Field, res); + } + } + public static int Main(string[] noArgs) { if (!OperatingSystem.IsWindows()) @@ -230,6 +269,7 @@ public static int Main(string[] noArgs) TestArrayEquivalence(); TestGenericClassNonEquivalence(); TestGenericInterfaceEquivalence(); + TestTypeEquivalenceWithTypePunning(); } catch (Exception e) { diff --git a/src/tests/baseservices/typeequivalence/simple/Simple.csproj b/src/tests/baseservices/typeequivalence/simple/Simple.csproj index 028b3cc152efd3..e8bb27e9d97873 100644 --- a/src/tests/baseservices/typeequivalence/simple/Simple.csproj +++ b/src/tests/baseservices/typeequivalence/simple/Simple.csproj @@ -1,6 +1,7 @@ Exe + True @@ -11,6 +12,7 @@ true + diff --git a/src/tests/build.cmd b/src/tests/build.cmd index 18dd4ee6266e6d..b26cd80341887c 100644 --- a/src/tests/build.cmd +++ b/src/tests/build.cmd @@ -257,21 +257,13 @@ if not exist "%__NativeTestIntermediatesDir%\CMakeCache.txt" ( echo Environment setup -set __BuildLog="%__LogsDir%\!__BuildLogRootName!_%__TargetOS%__%__BuildArch%__%__BuildType%.log" -set __BuildWrn="%__LogsDir%\!__BuildLogRootName!_%__TargetOS%__%__BuildArch%__%__BuildType%.wrn" -set __BuildErr="%__LogsDir%\!__BuildLogRootName!_%__TargetOS%__%__BuildArch%__%__BuildType%.err" -set __MsbuildLog=/flp:Verbosity=normal;LogFile=!__BuildLog! -set __MsbuildWrn=/flp1:WarningsOnly;LogFile=!__BuildWrn! -set __MsbuildErr=/flp2:ErrorsOnly;LogFile=!__BuildErr! -set __Logging=!__MsbuildLog! !__MsbuildWrn! !__MsbuildErr! - set __CmakeBuildToolArgs= if %__Ninja% EQU 1 ( set __CmakeBuildToolArgs= ) else ( REM We pass the /m flag directly to MSBuild so that we can get both MSBuild and CL parallelism, which is fastest for our builds. - set __CmakeBuildToolArgs=/nologo /m !__Logging! + set __CmakeBuildToolArgs=/nologo /m ) "%CMakePath%" --build %__NativeTestIntermediatesDir% --target install --config %__BuildType% -- !__CmakeBuildToolArgs! @@ -289,12 +281,25 @@ REM === Restore packages, build managed tests, generate layout and test wrappers REM === REM ========================================================================================= -powershell -NoProfile -ExecutionPolicy ByPass -NoLogo -Command "%__RepoRootDir%\eng\common\msbuild.ps1" %__ArcadeScriptArgs%^ +set __BuildLog="%__LogsDir%\!__BuildLogRootName!_%__TargetOS%__%__BuildArch%__%__BuildType%.log" +set __BuildWrn="%__LogsDir%\!__BuildLogRootName!_%__TargetOS%__%__BuildArch%__%__BuildType%.wrn" +set __BuildErr="%__LogsDir%\!__BuildLogRootName!_%__TargetOS%__%__BuildArch%__%__BuildType%.err" +set __BuildBinLog="%__LogsDir%\!__BuildLogRootName!_%__TargetOS%__%__BuildArch%__%__BuildType%.binlog" +set __MsbuildLog=/flp:Verbosity=normal;LogFile=!__BuildLog! +set __MsbuildWrn=/flp1:WarningsOnly;LogFile=!__BuildWrn! +set __MsbuildErr=/flp2:ErrorsOnly;LogFile=!__BuildErr! +set __MsbuildBinLog=/bl:!__BuildBinLog! +set __Logging='!__MsbuildLog!' '!__MsbuildWrn!' '!__MsbuildErr!' '!__MsbuildBinLog!' + +set BuildCommand=powershell -NoProfile -ExecutionPolicy ByPass -NoLogo -Command "%__RepoRootDir%\eng\common\msbuild.ps1" %__ArcadeScriptArgs%^ %__RepoRootDir%\src\tests\build.proj -warnAsError:0 /t:TestBuild /nodeReuse:false^ /p:RestoreDefaultOptimizationDataPackage=false /p:PortableBuild=true^ - /p:UsePartialNGENOptimization=false /maxcpucount^ + /p:UsePartialNGENOptimization=false /maxcpucount %__Logging%^ %__msbuildArgs% +echo %BuildCommand% +%BuildCommand% + if errorlevel 1 ( echo %__ErrMsgPrefix%%__MsgPrefix%Error: Test build failed. Refer to the build log files for details: echo %__BuildLog% diff --git a/src/tests/build.proj b/src/tests/build.proj index 0a7d6f2b16d9e9..092c2972657ade 100644 --- a/src/tests/build.proj +++ b/src/tests/build.proj @@ -7,8 +7,6 @@ $(BaseOutputPathWithConfig) $(XunitTestBinBase)\TestWrappers\ 1 - sh - cmd C# $(OutputRid) @@ -51,7 +49,6 @@ Text="$(XunitTestBinBase) does not exist. Please run src\tests\build / src/tests/build.sh from the repo root at least once to get the tests built." /> - @@ -59,13 +56,15 @@ + + + - $([System.IO.Path]::GetFullPath(%(AllRunnableTestPaths.Identity))) + $([System.IO.Path]::GetFullPath(%(LegacyRunnableTestPaths.Identity))) - @@ -88,7 +87,7 @@ Text="$(XunitTestBinBase) does not exist. Please run src\tests\build / src/tests/build.sh from the repo root at least once to get the tests built." /> - @(AllRunnableTestPaths->Count()) + @(LegacyRunnableTestPaths->Count()) @@ -123,7 +122,10 @@ - + @@ -260,7 +262,11 @@ - + @@ -336,6 +342,7 @@ MonoRuntimeHeaders="$(MicrosoftNetCoreAppRuntimePackNativeDir)/include/mono-2.0" Assemblies="@(BundleAssemblies)" ForceInterpreter="$(MonoForceInterpreter)" + EnableAppSandbox="$(EnableAppSandbox)" UseConsoleUITemplate="True" GenerateXcodeProject="True" BuildAppBundle="True" @@ -371,6 +378,7 @@ Projects="@(RunProj)" Targets="BuildiOSApp" BuildInParallel="true" + Condition="'@(TestDirectories)' != ''" /> @@ -378,6 +386,9 @@ + + + @@ -433,18 +444,22 @@ - - + + + Condition="'$(__SkipManaged)' != '1'" > + Build + + CopyAllNativeTestProjectBinaries + $(DotNetCli) msbuild $(GroupBuildCmd) $(MSBuildThisFileFullPath) - $(GroupBuildCmd) /t:Build + $(GroupBuildCmd) /t:$(TargetToBuild) $(GroupBuildCmd) "/p:TargetArchitecture=$(TargetArchitecture)" $(GroupBuildCmd) "/p:Configuration=$(Configuration)" $(GroupBuildCmd) "/p:LibrariesConfiguration=$(LibrariesConfiguration)" @@ -465,15 +480,6 @@ - - - - - - diff --git a/src/tests/ilasm/PortablePdb/IlasmPortablePdbTests.csproj b/src/tests/ilasm/PortablePdb/IlasmPortablePdbTests.csproj index b490324b35c166..b03148c6df7635 100644 --- a/src/tests/ilasm/PortablePdb/IlasmPortablePdbTests.csproj +++ b/src/tests/ilasm/PortablePdb/IlasmPortablePdbTests.csproj @@ -1,7 +1,4 @@  - - Exe - diff --git a/src/tests/ilverify/ILVerificationTests.csproj b/src/tests/ilverify/ILVerificationTests.csproj index 049390bad80f48..55d08e4d15775e 100644 --- a/src/tests/ilverify/ILVerificationTests.csproj +++ b/src/tests/ilverify/ILVerificationTests.csproj @@ -1,6 +1,5 @@ - Exe $(BaseOutputPathWithConfig)ilverify\ 1 diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 09ba488248d6e5..eb4332161d9a2d 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -54,6 +54,9 @@ https://github.com/dotnet/runtime/issues/55966 + + https://github.com/dotnet/runtime/issues/62285 + https://github.com/dotnet/runtime/issues/57786 @@ -2081,6 +2084,15 @@ + + https://github.com/dotnet/runtime/issues/62567 + + + https://github.com/dotnet/runtime/issues/62567 + + + https://github.com/dotnet/runtime/issues/62567 + https://github.com/dotnet/runtime/issues/57361 @@ -2343,7 +2355,7 @@ https://github.com/dotnet/runtime/issues/62334 - + diff --git a/src/tests/readytorun/r2rdump/FrameworkTests/R2RDumpTests.csproj b/src/tests/readytorun/r2rdump/FrameworkTests/R2RDumpTests.csproj index dc513e6d2b63cd..b6a2da881f905b 100644 --- a/src/tests/readytorun/r2rdump/FrameworkTests/R2RDumpTests.csproj +++ b/src/tests/readytorun/r2rdump/FrameworkTests/R2RDumpTests.csproj @@ -1,7 +1,5 @@ - Exe - true true diff --git a/src/tests/run.py b/src/tests/run.py index a5348318d5f3a4..abbfa23fa4a654 100755 --- a/src/tests/run.py +++ b/src/tests/run.py @@ -147,7 +147,7 @@ def __init__(self, test ({}) : The test metadata """ - self.unique_name = "%s_%s_%s_%s" % (test["name"], + self.unique_name = "%s_%s_%s_%s" % (test["test_path"], args.host_os, args.arch, args.build_type) @@ -160,7 +160,7 @@ def __init__(self, self.__create_repro_wrapper__() self.path = None - + if self.args.host_os == "windows": self.path = self.unique_name + ".cmd" else: @@ -1147,14 +1147,13 @@ def dir_has_nested_substrings(test_path, test_item): location = starting_path if not os.path.isfile(location): - print("Warning: couldn't find test: %s" % test_name) return None assert(os.path.isfile(location)) return location -def parse_test_results(args): +def parse_test_results(args, tests, assemblies): """ Parse the test results for test execution information Args: @@ -1163,30 +1162,50 @@ def parse_test_results(args): log_path = os.path.join(args.logs_dir, "TestRunResults_%s_%s_%s" % (args.host_os, args.arch, args.build_type)) print("Parsing test results from (%s)" % log_path) - test_run_location = os.path.join(args.logs_dir, "testRun.xml") - - if not os.path.isfile(test_run_location): - # Check if this is a casing issue + found = False - found = False - for item in os.listdir(args.logs_dir): - item_lower = item.lower() - if item_lower == "testrun.xml": - # Correct the name. - os.rename(os.path.join(args.logs_dir, item), test_run_location) - found = True - break + for item in os.listdir(args.logs_dir): + suffix_index = item.lower().find("testrun.xml") + if suffix_index >= 0: + found = True + item_name = "" + if suffix_index > 0: + item_name = item[:suffix_index - 1] + parse_test_results_xml_file(args, item, item_name, tests, assemblies) - if not found: - print("Unable to find testRun.xml. This normally means the tests did not run.") - print("It could also mean there was a problem logging. Please run the tests again.") - return + if not found: + print("Unable to find testRun.xml. This normally means the tests did not run.") + print("It could also mean there was a problem logging. Please run the tests again.") + return - print("Analyzing {}".format(test_run_location)) - assemblies = xml.etree.ElementTree.parse(test_run_location).getroot() +def parse_test_results_xml_file(args, item, item_name, tests, assemblies): + """ Parse test results from a single xml results file + Args: + xml_result_file : results xml file to parse + args : arguments + tests : dictionary of individual test results + assemblies : dictionary of per-assembly aggregations + """ - tests = defaultdict(lambda: None) - for assembly in assemblies: + xml_result_file = os.path.join(args.logs_dir, item) + print("Analyzing {}".format(xml_result_file)) + xml_assemblies = xml.etree.ElementTree.parse(xml_result_file).getroot() + for assembly in xml_assemblies: + assembly_name = assembly.attrib["name"] + assembly_info = assemblies[assembly_name] + display_name = assembly_name + if len(item_name) > 0: + display_name = item_name + if assembly_info == None: + assembly_info = defaultdict(lambda: None, { + "name": assembly_name, + "display_name": display_name, + "time": 0.0, + "passed": 0, + "failed": 0, + "skipped": 0, + }) + assembly_info["time"] = assembly_info["time"] + float(assembly.attrib["time"]) for collection in assembly: if collection.tag == "errors" and collection.text != None: # Something went wrong during running the tests. @@ -1199,39 +1218,33 @@ def parse_test_results(args): method = test.attrib["method"] type = type.split("._")[0] - test_name = type + method - - assert test_name != None - - failed = collection.attrib["failed"] - skipped = collection.attrib["skipped"] - passed = collection.attrib["passed"] - time = float(collection.attrib["time"]) - - test_output = None - - if failed == "1": - failure_info = collection[0][0] - test_output = failure_info.text - - test_location_on_filesystem = find_test_from_name(args.host_os, args.test_location, test_name) - if test_location_on_filesystem is not None: - assert os.path.isfile(test_location_on_filesystem) - + test_name = type + "::" + method + name = test.attrib["name"] + if len(name) > 0: + test_name += " (" + name + ")" + result = test.attrib["result"] + time = float(collection.attrib["time"]) + test_location_on_filesystem = find_test_from_name(args.host_os, args.test_location, name) + if test_location_on_filesystem is None or not os.path.isfile(test_location_on_filesystem): + test_location_on_filesystem = None + test_output = test.findtext("output") assert tests[test_name] == None tests[test_name] = defaultdict(lambda: None, { "name": test_name, "test_path": test_location_on_filesystem, - "failed": failed, - "skipped": skipped, - "passed": passed, + "result" : result, "time": time, "test_output": test_output }) - - return tests - -def print_summary(tests): + if result == "Pass": + assembly_info["passed"] += 1 + elif result == "Fail": + assembly_info["failed"] += 1 + else: + assembly_info["skipped"] += 1 + assemblies[assembly_name] = assembly_info + +def print_summary(tests, assemblies): """ Print a summary of the test results Args: @@ -1241,122 +1254,64 @@ def print_summary(tests): """ assert tests is not None + assert assemblies is not None failed_tests = [] - passed_tests = [] - skipped_tests = [] for test in tests: test = tests[test] + if test["result"] == "Fail": + print("Failed test: %s" % test["name"]) - if test["failed"] == "1": - failed_tests.append(test) - elif test["passed"] == "1": - passed_tests.append(test) - else: - skipped_tests.append(test) - - failed_tests.sort(key=lambda item: item["time"], reverse=True) - passed_tests.sort(key=lambda item: item["time"], reverse=True) - skipped_tests.sort(key=lambda item: item["time"], reverse=True) - - def print_tests_helper(tests, stop_count): - for index, item in enumerate(tests): - time = item["time"] - unit = "seconds" - time_remainder = "" - second_unit = "" - saved_time = time - remainder_str = "" - - # If it can be expressed in hours - if time > 60**2: - time = saved_time / (60**2) - time_remainder = saved_time % (60**2) - time_remainder /= 60 - time_remainder = math.floor(time_remainder) - unit = "hours" - second_unit = "minutes" - - remainder_str = " %s %s" % (int(time_remainder), second_unit) - - elif time > 60 and time < 60**2: - time = saved_time / 60 - time_remainder = saved_time % 60 - time_remainder = math.floor(time_remainder) - unit = "minutes" - second_unit = "seconds" - - remainder_str = " %s %s" % (int(time_remainder), second_unit) - - print("%s (%d %s%s)" % (item["test_path"], time, unit, remainder_str)) - - if stop_count != None: - if index >= stop_count: - break - - if len(failed_tests) > 0: - print("%d failed tests:" % len(failed_tests)) - print("") - print_tests_helper(failed_tests, None) - - # The following code is currently disabled, as it produces too much verbosity in a normal - # test run. It could be put under a switch, or else just enabled as needed when investigating - # test slowness. - # - # if len(passed_tests) > 50: - # print("") - # print("50 slowest passing tests:") - # print("") - # print_tests_helper(passed_tests, 50) - - if len(failed_tests) > 0: - print("") - print("#################################################################") - print("Output of failing tests:") - print("") - - for item in failed_tests: - print("[%s]: " % item["test_path"]) - print("") - - test_output = item["test_output"] - - # XUnit results are captured as escaped characters. - #test_output = test_output.replace("\\r", "\r") - #test_output = test_output.replace("\\n", "\n") - #test_output = test_output.replace("/r", "\r") - #test_output = test_output.replace("/n", "\n") + test_output = test["test_output"] # Replace CR/LF by just LF; Python "print", below, will map as necessary on the platform. # If we don't do this, then Python on Windows will convert \r\n to \r\r\n on output. - test_output = test_output.replace("\r\n", "\n") + if test_output is not None: + test_output = test_output.replace("\r\n", "\n") + + unicode_output = None + if sys.version_info < (3,0): + # Handle unicode characters in output in python2.* + try: + unicode_output = unicode(test_output, "utf-8") + except: + print("Error: failed to convert Unicode output") + else: + unicode_output = test_output - unicode_output = None - if sys.version_info < (3,0): - # Handle unicode characters in output in python2.* - try: - unicode_output = unicode(test_output, "utf-8") - except: - print("Error: failed to convert Unicode output") + if unicode_output is not None: + print(unicode_output) else: - unicode_output = test_output - - if unicode_output is not None: - print(unicode_output) + print("# Test output recorded in log file.") print("") - print("") - print("#################################################################") - print("End of output of failing tests") - print("#################################################################") - print("") + print("Time [secs] | Total | Passed | Failed | Skipped | Assembly Execution Summary") + print("============================================================================") - print("") - print("Total tests run : %d" % len(tests)) - print("Total passing tests: %d" % len(passed_tests)) - print("Total failed tests : %d" % len(failed_tests)) - print("Total skipped tests: %d" % len(skipped_tests)) + total_time = 0.0 + total_total = 0 + total_passed = 0 + total_failed = 0 + total_skipped = 0 + + for assembly in assemblies: + assembly = assemblies[assembly] + name = assembly["display_name"] + time = assembly["time"] + passed = assembly["passed"] + failed = assembly["failed"] + skipped = assembly["skipped"] + total = passed + failed + skipped + print("%11.3f | %5d | %6d | %6d | %7d | %s" % (time, total, passed, failed, skipped, name)) + total_time += time + total_total += total + total_passed += passed + total_failed += failed + total_skipped += skipped + + print("----------------------------------------------------------------------------") + print("%11.3f | %5d | %6d | %6d | %7d | (total)" % (total_time, total_total, total_passed, total_failed, total_skipped)) print("") def create_repro(args, env, tests): @@ -1371,7 +1326,7 @@ def create_repro(args, env, tests): """ assert tests is not None - failed_tests = [tests[item] for item in tests if tests[item]["failed"] == "1"] + failed_tests = [tests[item] for item in tests if tests[item]["result"] == "Fail" and tests[item]["test_path"] is not None] if len(failed_tests) == 0: return @@ -1415,10 +1370,11 @@ def main(args): print("Test run finished.") if not args.skip_test_run: - tests = parse_test_results(args) - if tests is not None: - print_summary(tests) - create_repro(args, env, tests) + assemblies = defaultdict(lambda: None) + tests = defaultdict(lambda: None) + parse_test_results(args, tests, assemblies) + print_summary(tests, assemblies) + create_repro(args, env, tests) return ret_code diff --git a/src/tests/xunit-wrappers.targets b/src/tests/xunit-wrappers.targets index d0139dc2f0f5ae..53cb58c6b9ed24 100644 --- a/src/tests/xunit-wrappers.targets +++ b/src/tests/xunit-wrappers.targets @@ -1,6 +1,10 @@ - + @@ -91,7 +95,7 @@ $(_XunitEpilog) - + @@ -254,6 +258,10 @@ namespace $([System.String]::Copy($(Category)).Replace(".","_").Replace("\",""). + + + +